From fe0714acc7d890fe0331054065199097076f356e Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 7 Jun 2023 16:02:26 -0400 Subject: [PATCH 0001/1617] Make .test parser friendlier (#15385) --- mypy/test/data.py | 111 +++++++++--------- mypy/test/meta/__init__.py | 0 mypy/test/meta/test_parse_data.py | 66 +++++++++++ .../test_update_data.py} | 7 +- 4 files changed, 126 insertions(+), 58 deletions(-) create mode 100644 mypy/test/meta/__init__.py create mode 100644 mypy/test/meta/test_parse_data.py rename mypy/test/{testupdatedata.py => meta/test_update_data.py} (94%) diff --git a/mypy/test/data.py b/mypy/test/data.py index daf815dbdbdc..86193e303d9c 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -12,7 +12,7 @@ from abc import abstractmethod from dataclasses import dataclass from pathlib import Path -from typing import Any, Iterator, NamedTuple, Pattern, Union +from typing import Any, Iterator, NamedTuple, NoReturn, Pattern, Union from typing_extensions import Final, TypeAlias as _TypeAlias import pytest @@ -77,11 +77,19 @@ def parse_test_case(case: DataDrivenTestCase) -> None: targets: dict[int, list[str]] = {} # Fine-grained targets (per fine-grained update) test_modules: list[str] = [] # Modules which are deemed "test" (vs "fixture") + def _case_fail(msg: str) -> NoReturn: + pytest.fail(f"{case.file}:{case.line}: {msg}", pytrace=False) + # Process the parsed items. Each item has a header of form [id args], # optionally followed by lines of text. item = first_item = test_items[0] test_modules.append("__main__") for item in test_items[1:]: + + def _item_fail(msg: str) -> NoReturn: + item_abs_line = case.line + item.line - 2 + pytest.fail(f"{case.file}:{item_abs_line}: {msg}", pytrace=False) + if item.id in {"file", "fixture", "outfile", "outfile-re"}: # Record an extra file needed for the test case. assert item.arg is not None @@ -132,9 +140,11 @@ def parse_test_case(case: DataDrivenTestCase) -> None: # File/directory to delete during a multi-step test case assert item.arg is not None m = re.match(r"(.*)\.([0-9]+)$", item.arg) - assert m, f"Invalid delete section: {item.arg}" + if m is None: + _item_fail(f"Invalid delete section {item.arg!r}") num = int(m.group(2)) - assert num >= 2, f"Can't delete during step {num}" + if num < 2: + _item_fail(f"Can't delete during step {num}") full = join(base_path, m.group(1)) deleted_paths.setdefault(num, set()).add(full) elif re.match(r"out[0-9]*$", item.id): @@ -150,29 +160,18 @@ def parse_test_case(case: DataDrivenTestCase) -> None: if arg.startswith("version"): compare_op = arg[7:9] if compare_op not in {">=", "=="}: - raise ValueError( - "{}, line {}: Only >= and == version checks are currently supported".format( - case.file, item.line - ) - ) + _item_fail("Only >= and == version checks are currently supported") version_str = arg[9:] try: version = tuple(int(x) for x in version_str.split(".")) except ValueError: - raise ValueError( - '{}, line {}: "{}" is not a valid python version'.format( - case.file, item.line, version_str - ) - ) + _item_fail(f"{version_str!r} is not a valid python version") if compare_op == ">=": version_check = sys.version_info >= version elif compare_op == "==": if not 1 < len(version) < 4: - raise ValueError( - "{}, line {}: Only minor or patch version checks " - 'are currently supported with "==": "{}"'.format( - case.file, item.line, version_str - ) + _item_fail( + f'Only minor or patch version checks are currently supported with "==": {version_str!r}' ) version_check = sys.version_info[: len(version)] == version if version_check: @@ -189,10 +188,11 @@ def parse_test_case(case: DataDrivenTestCase) -> None: elif item.id == "triggered" and item.arg is None: triggered = item.data else: - raise ValueError(f"Invalid section header {item.id} in {case.file}:{item.line}") + section_str = item.id + (f" {item.arg}" if item.arg else "") + _item_fail(f"Invalid section header [{section_str}] in case {case.name!r}") if out_section_missing: - raise ValueError(f"{case.file}, line {first_item.line}: Required output section not found") + _case_fail(f"Required output section not found in case {case.name!r}") for passnum in stale_modules.keys(): if passnum not in rechecked_modules: @@ -204,11 +204,7 @@ def parse_test_case(case: DataDrivenTestCase) -> None: and passnum in rechecked_modules and not stale_modules[passnum].issubset(rechecked_modules[passnum]) ): - raise ValueError( - ( - "Stale modules after pass {} must be a subset of rechecked modules ({}:{})" - ).format(passnum, case.file, first_item.line) - ) + _case_fail(f"Stale modules after pass {passnum} must be a subset of rechecked modules") output_inline_start = len(output) input = first_item.data @@ -219,10 +215,7 @@ def parse_test_case(case: DataDrivenTestCase) -> None: seen_files = set() for file, _ in files: if file in seen_files: - raise ValueError( - f"{case.file}, line {first_item.line}: Duplicated filename {file}. Did you include" - " it multiple times?" - ) + _case_fail(f"Duplicated filename {file}. Did you include it multiple times?") seen_files.add(file) @@ -367,12 +360,13 @@ def setup(self) -> None: self.steps = [steps.get(num, []) for num in range(2, max_step + 1)] def teardown(self) -> None: - assert self.old_cwd is not None and self.tmpdir is not None, "test was not properly set up" - os.chdir(self.old_cwd) - try: - self.tmpdir.cleanup() - except OSError: - pass + if self.old_cwd is not None: + os.chdir(self.old_cwd) + if self.tmpdir is not None: + try: + self.tmpdir.cleanup() + except OSError: + pass self.old_cwd = None self.tmpdir = None @@ -634,6 +628,16 @@ def pytest_pycollect_makeitem(collector: Any, name: str, obj: object) -> Any | N return None +_case_name_pattern = re.compile( + r"(?P[a-zA-Z_0-9]+)" + r"(?P-writescache)?" + r"(?P-only_when_cache|-only_when_nocache)?" + r"(-(?Pposix|windows))?" + r"(?P-skip)?" + r"(?P-xfail)?" +) + + def split_test_cases( parent: DataFileCollector, suite: DataSuite, file: str ) -> Iterator[DataDrivenTestCase]: @@ -644,40 +648,33 @@ def split_test_cases( """ with open(file, encoding="utf-8") as f: data = f.read() - # number of groups in the below regex - NUM_GROUPS = 7 - cases = re.split( - r"^\[case ([a-zA-Z_0-9]+)" - r"(-writescache)?" - r"(-only_when_cache|-only_when_nocache)?" - r"(-posix|-windows)?" - r"(-skip)?" - r"(-xfail)?" - r"\][ \t]*$\n", - data, - flags=re.DOTALL | re.MULTILINE, - ) - line_no = cases[0].count("\n") + 1 + cases = re.split(r"^\[case ([^]+)]+)\][ \t]*$\n", data, flags=re.DOTALL | re.MULTILINE) + cases_iter = iter(cases) + line_no = next(cases_iter).count("\n") + 1 test_names = set() - for i in range(1, len(cases), NUM_GROUPS): - name, writescache, only_when, platform_flag, skip, xfail, data = cases[i : i + NUM_GROUPS] + for case_id in cases_iter: + data = next(cases_iter) + + m = _case_name_pattern.fullmatch(case_id) + if not m: + raise RuntimeError(f"Invalid testcase id {case_id!r}") + name = m.group("name") if name in test_names: raise RuntimeError( 'Found a duplicate test name "{}" in {} on line {}'.format( name, parent.name, line_no ) ) - platform = platform_flag[1:] if platform_flag else None yield DataDrivenTestCase.from_parent( parent=parent, suite=suite, file=file, name=add_test_name_suffix(name, suite.test_name_suffix), - writescache=bool(writescache), - only_when=only_when, - platform=platform, - skip=bool(skip), - xfail=bool(xfail), + writescache=bool(m.group("writescache")), + only_when=m.group("only_when"), + platform=m.group("platform"), + skip=bool(m.group("skip")), + xfail=bool(m.group("xfail")), data=data, line=line_no, ) diff --git a/mypy/test/meta/__init__.py b/mypy/test/meta/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py new file mode 100644 index 000000000000..6d9b32b96c7e --- /dev/null +++ b/mypy/test/meta/test_parse_data.py @@ -0,0 +1,66 @@ +""" +A "meta test" which tests the parsing of .test files. This is not meant to become exhaustive +but to ensure we maintain a basic level of ergonomics for mypy contributors. +""" +import subprocess +import sys +import textwrap +from pathlib import Path + +from mypy.test.config import test_data_prefix +from mypy.test.helpers import Suite + + +class ParseTestDataSuite(Suite): + def _dedent(self, s: str) -> str: + return textwrap.dedent(s).lstrip() + + def _run_pytest(self, data_suite: str) -> str: + p = Path(test_data_prefix) / "check-__fixture__.test" + assert not p.exists() + try: + p.write_text(data_suite) + test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" + args = [sys.executable, "-m", "pytest", "-n", "0", "-s", test_nodeid] + proc = subprocess.run(args, capture_output=True, check=False) + return proc.stdout.decode() + finally: + p.unlink() + + def test_parse_invalid_case(self) -> None: + # Arrange + data = self._dedent( + """ + [case abc] + s: str + [case foo-XFAIL] + s: str + """ + ) + + # Act + actual = self._run_pytest(data) + + # Assert + assert "Invalid testcase id 'foo-XFAIL'" in actual + + def test_parse_invalid_section(self) -> None: + # Arrange + data = self._dedent( + """ + [case abc] + s: str + [unknownsection] + abc + """ + ) + + # Act + actual = self._run_pytest(data) + + # Assert + expected_lineno = data.splitlines().index("[unknownsection]") + 1 + expected = ( + f".test:{expected_lineno}: Invalid section header [unknownsection] in case 'abc'" + ) + assert expected in actual diff --git a/mypy/test/testupdatedata.py b/mypy/test/meta/test_update_data.py similarity index 94% rename from mypy/test/testupdatedata.py rename to mypy/test/meta/test_update_data.py index 54e9622a5e91..1239679072e6 100644 --- a/mypy/test/testupdatedata.py +++ b/mypy/test/meta/test_update_data.py @@ -1,3 +1,8 @@ +""" +A "meta test" which tests the `--update-data` feature for updating .test files. +Updating the expected output, especially when it's in the form of inline (comment) assertions, +can be brittle, which is why we're "meta-testing" here. +""" import shlex import subprocess import sys @@ -16,7 +21,7 @@ def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str: """ p_test_data = Path(test_data_prefix) p_root = p_test_data.parent.parent - p = p_test_data / "check-update-data.test" + p = p_test_data / "check-__fixture__.test" assert not p.exists() try: p.write_text(textwrap.dedent(data_suite).lstrip()) From f54c3096b7d328dc59dd85af3a8ba28739dcfed0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:59:36 -0700 Subject: [PATCH 0002/1617] Fix crash when indexing TypedDict with empty key (#15392) --- mypy/messages.py | 3 ++- test-data/unit/check-typeddict.test | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index a732d612123c..cb95fb1c0bde 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2989,8 +2989,9 @@ def _real_quick_ratio(a: str, b: str) -> float: def best_matches(current: str, options: Collection[str], n: int) -> list[str]: + if not current: + return [] # narrow down options cheaply - assert current options = [o for o in options if _real_quick_ratio(current, o) > 0.75] if len(options) >= 50: options = [o for o in options if abs(len(o) - len(current)) <= 1] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 970dc05b488d..fc487d2d553d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2873,3 +2873,15 @@ foo({"foo": {"e": "foo"}}) # E: Type of TypedDict is ambiguous, none of ("A", " # E: Argument 1 to "foo" has incompatible type "Dict[str, Dict[str, str]]"; expected "Union[A, B]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictMissingEmptyKey] +from typing_extensions import TypedDict + +class A(TypedDict): + my_attr_1: str + my_attr_2: int + +d: A +d[''] # E: TypedDict "A" has no key "" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 8dde298fba2e021d1309a0d796a4a7efccc11e41 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 8 Jun 2023 18:27:55 -0400 Subject: [PATCH 0003/1617] .git-blame-ignore-revs: exclude com2ann refactor (#15400) --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4256387e1209..006809c4a0e1 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -2,3 +2,5 @@ 97c5ee99bc98dc475512e549b252b23a6e7e0997 # Use builtin generics and PEP 604 for type annotations wherever possible (#13427) 23ee1e7aff357e656e3102435ad0fe3b5074571e +# Use variable annotations (#10723) +f98f78216ba9d6ab68c8e69c19e9f3c7926c5efe From e253e7660d48422d3a0ea643bc1b11419fb3cddc Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:31:23 -0700 Subject: [PATCH 0004/1617] Fix stubtest crash in explicit init subclass (#15399) --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 4e038cfd75d1..a55be41d1a5d 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -668,7 +668,7 @@ def _verify_arg_default_value( def maybe_strip_cls(name: str, args: list[nodes.Argument]) -> list[nodes.Argument]: - if name in ("__init_subclass__", "__class_getitem__"): + if args and name in ("__init_subclass__", "__class_getitem__"): # These are implicitly classmethods. If the stub chooses not to have @classmethod, we # should remove the cls argument if args[0].variable.name == "cls": From 27593bc23cf6be4ad9a37d6c1bc13abc5f2190ec Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:31:41 -0700 Subject: [PATCH 0005/1617] Improve stubtest error message (#15398) --- mypy/stubtest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a55be41d1a5d..f06faa962b07 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -209,7 +209,12 @@ def test_module(module_name: str) -> Iterator[Error]: except KeyboardInterrupt: raise except BaseException as e: - yield Error([module_name], f"failed to import, {type(e).__name__}: {e}", stub, MISSING) + note = "" + if isinstance(e, ModuleNotFoundError): + note = " Maybe install the runtime package or alter PYTHONPATH?" + yield Error( + [module_name], f"failed to import.{note} {type(e).__name__}: {e}", stub, MISSING + ) return with warnings.catch_warnings(): From 4baf672a8fe19bb78bcb99ec706e3aa0f7c8605c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:53:45 -0700 Subject: [PATCH 0006/1617] Fix abstract and non-abstract variant error for prop.deleter (#15395) --- mypy/semanal.py | 2 ++ test-data/unit/check-abstract.test | 41 ++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index c5a6989f4f61..c95e1c8741dd 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1337,6 +1337,8 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - first_item.var.is_settable_property = True # Get abstractness from the original definition. item.func.abstract_status = first_item.func.abstract_status + if node.name == "deleter": + item.func.abstract_status = first_item.func.abstract_status else: self.fail( f"Only supported top decorator is @{first_item.func.name}.setter", item diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 8a13e5cb5760..3a0a30a14462 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -735,7 +735,44 @@ class A(metaclass=ABCMeta): def x(self) -> int: pass @x.setter def x(self, x: int) -> None: pass -[out] + +[case testReadWriteDeleteAbstractProperty] +from abc import ABC, abstractmethod +class Abstract(ABC): + @property + @abstractmethod + def prop(self) -> str: ... + + @prop.setter + @abstractmethod + def prop(self, code: str) -> None: ... + + @prop.deleter + @abstractmethod + def prop(self) -> None: ... + +class Good(Abstract): + @property + def prop(self) -> str: ... + @prop.setter + def prop(self, code: str) -> None: ... + @prop.deleter + def prop(self) -> None: ... + +class Bad1(Abstract): + @property # E: Read-only property cannot override read-write property + def prop(self) -> str: ... + +class ThisShouldProbablyError(Abstract): + @property + def prop(self) -> str: ... + @prop.setter + def prop(self, code: str) -> None: ... + +a = Good() +reveal_type(a.prop) # N: Revealed type is "builtins.str" +a.prop = 123 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +[builtins fixtures/property.pyi] [case testInstantiateClassWithReadOnlyAbstractProperty] from abc import abstractproperty, ABCMeta @@ -767,7 +804,7 @@ b = B() b.x() # E: "int" not callable [builtins fixtures/property.pyi] -[case testImplementReradWriteAbstractPropertyViaProperty] +[case testImplementReadWriteAbstractPropertyViaProperty] from abc import abstractproperty, ABCMeta class A(metaclass=ABCMeta): @abstractproperty From 3e982a0bd3b7d4e25edc4046af07e2c2c1011784 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 9 Jun 2023 16:27:34 +0100 Subject: [PATCH 0007/1617] Fix crash on joins with recursive tuples (#15402) Fixes #15351 This may have some perf impact, but I predict it will be minimal (and I didn't notice anything locally). --- mypy/typeops.py | 6 ++++++ test-data/unit/check-recursive-types.test | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/mypy/typeops.py b/mypy/typeops.py index ee544c6740bb..58a641a54ab7 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -76,12 +76,18 @@ def is_recursive_pair(s: Type, t: Type) -> bool: isinstance(get_proper_type(t), (Instance, UnionType)) or isinstance(t, TypeAliasType) and t.is_recursive + # Tuple types are special, they can cause an infinite recursion even if + # the other type is not recursive, because of the tuple fallback that is + # calculated "on the fly". + or isinstance(get_proper_type(s), TupleType) ) if isinstance(t, TypeAliasType) and t.is_recursive: return ( isinstance(get_proper_type(s), (Instance, UnionType)) or isinstance(s, TypeAliasType) and s.is_recursive + # Same as above. + or isinstance(get_proper_type(t), TupleType) ) return False diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 75f2433c6d8c..51f07cdccf8f 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -937,3 +937,12 @@ x: A[int, str] if last is not None: reveal_type(last) # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[Tuple[builtins.int, builtins.str, Union[..., None]], None]]" [builtins fixtures/tuple.pyi] + +[case testRecursiveAliasLiteral] +from typing import Tuple +from typing_extensions import Literal + +NotFilter = Tuple[Literal["not"], "NotFilter"] +n: NotFilter +reveal_type(n[1][1][0]) # N: Revealed type is "Literal['not']" +[builtins fixtures/tuple.pyi] From 2c66cba59fefadd0f3ab71a605d9a458fb14f1f6 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 9 Jun 2023 16:28:20 +0100 Subject: [PATCH 0008/1617] Correctly track loop depth for nested functions/classes (#15403) Fixes #15378 --- mypy/semanal.py | 22 +++++++++++++--------- test-data/unit/check-statements.test | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index c95e1c8741dd..923cb789b454 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -375,7 +375,7 @@ class SemanticAnalyzer( missing_names: list[set[str]] # Callbacks that will be called after semantic analysis to tweak things. patches: list[tuple[int, Callable[[], None]]] - loop_depth = 0 # Depth of breakable loops + loop_depth: list[int] # Depth of breakable loops cur_mod_id = "" # Current module id (or None) (phase 2) _is_stub_file = False # Are we analyzing a stub file? _is_typeshed_stub_file = False # Are we analyzing a typeshed stub file? @@ -428,7 +428,7 @@ def __init__( self.tvar_scope = TypeVarLikeScope() self.function_stack = [] self.block_depth = [0] - self.loop_depth = 0 + self.loop_depth = [0] self.errors = errors self.modules = modules self.msg = MessageBuilder(errors, modules) @@ -1810,12 +1810,14 @@ def enter_class(self, info: TypeInfo) -> None: self.locals.append(None) # Add class scope self.is_comprehension_stack.append(False) self.block_depth.append(-1) # The class body increments this to 0 + self.loop_depth.append(0) self._type = info self.missing_names.append(set()) def leave_class(self) -> None: """Restore analyzer state.""" self.block_depth.pop() + self.loop_depth.pop() self.locals.pop() self.is_comprehension_stack.pop() self._type = self.type_stack.pop() @@ -3221,7 +3223,7 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: if lval.is_new_def: lval.is_inferred_def = s.type is None - if self.loop_depth > 0: + if self.loop_depth[-1] > 0: self.fail("Cannot use Final inside a loop", s) if self.type and self.type.is_protocol: self.msg.protocol_members_cant_be_final(s) @@ -4700,9 +4702,9 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None: def visit_while_stmt(self, s: WhileStmt) -> None: self.statement = s s.expr.accept(self) - self.loop_depth += 1 + self.loop_depth[-1] += 1 s.body.accept(self) - self.loop_depth -= 1 + self.loop_depth[-1] -= 1 self.visit_block_maybe(s.else_body) def visit_for_stmt(self, s: ForStmt) -> None: @@ -4724,20 +4726,20 @@ def visit_for_stmt(self, s: ForStmt) -> None: self.store_declared_types(s.index, analyzed) s.index_type = analyzed - self.loop_depth += 1 + self.loop_depth[-1] += 1 self.visit_block(s.body) - self.loop_depth -= 1 + self.loop_depth[-1] -= 1 self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: self.statement = s - if self.loop_depth == 0: + if self.loop_depth[-1] == 0: self.fail('"break" outside loop', s, serious=True, blocker=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: self.statement = s - if self.loop_depth == 0: + if self.loop_depth[-1] == 0: self.fail('"continue" outside loop', s, serious=True, blocker=True) def visit_if_stmt(self, s: IfStmt) -> None: @@ -6232,6 +6234,7 @@ def enter( self.nonlocal_decls.append(set()) # -1 since entering block will increment this to 0. self.block_depth.append(-1) + self.loop_depth.append(0) self.missing_names.append(set()) try: yield @@ -6241,6 +6244,7 @@ def enter( self.global_decls.pop() self.nonlocal_decls.pop() self.block_depth.pop() + self.loop_depth.pop() self.missing_names.pop() def is_func_scope(self) -> bool: diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 3cb8864f9207..29e2c5ba3266 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2212,3 +2212,17 @@ main:1: error: Module "typing" has no attribute "_FutureFeatureFixture" main:1: note: Use `from typing_extensions import _FutureFeatureFixture` instead main:1: note: See https://mypy.readthedocs.io/en/stable/runtime_troubles.html#using-new-additions-to-the-typing-module [builtins fixtures/tuple.pyi] + +[case testNoCrashOnBreakOutsideLoopFunction] +def foo(): + for x in [1, 2]: + def inner(): + break # E: "break" outside loop +[builtins fixtures/list.pyi] + +[case testNoCrashOnBreakOutsideLoopClass] +class Outer: + for x in [1, 2]: + class Inner: + break # E: "break" outside loop +[builtins fixtures/list.pyi] From 3cedd2725c9e8f6ec3dac5e72bf65bddbfccce4c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 9 Jun 2023 18:55:44 +0100 Subject: [PATCH 0009/1617] Fix crash on NamedTuple as attribute (#15404) Fixes #15380 Note I also update the `NamedTuple` fixture to be much closer to real definition in `typing.pyi` in typeshed. --- mypy/semanal.py | 9 ++++++--- test-data/unit/check-namedtuple.test | 14 ++++++++++++-- test-data/unit/fixtures/typing-namedtuple.pyi | 11 ++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 923cb789b454..073bde661617 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3069,6 +3069,12 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)): return False lvalue = s.lvalues[0] + if isinstance(lvalue, MemberExpr): + if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.callee, RefExpr): + fullname = s.rvalue.callee.fullname + if fullname == "collections.namedtuple" or fullname in TYPED_NAMEDTUPLE_NAMES: + self.fail("NamedTuple type as an attribute is not supported", lvalue) + return False name = lvalue.name namespace = self.qualified_name(name) with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): @@ -3077,9 +3083,6 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: ) if internal_name is None: return False - if isinstance(lvalue, MemberExpr): - self.fail("NamedTuple type as an attribute is not supported", lvalue) - return False if internal_name != name: self.fail( 'First argument to namedtuple() should be "{}", not "{}"'.format( diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 6b9f139f541c..5eed70246e8c 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -938,8 +938,9 @@ class A: def __init__(self) -> None: self.b = NamedTuple('x', [('s', str), ('n', int)]) # E: NamedTuple type as an attribute is not supported -reveal_type(A().b) # N: Revealed type is "Any" +reveal_type(A().b) # N: Revealed type is "typing.NamedTuple" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] [case testNamedTupleWrongfile] from typing import NamedTuple @@ -983,7 +984,7 @@ class Both2(Other, Bar): ... class Both3(Biz, Other): ... def print_namedtuple(obj: NamedTuple) -> None: - reveal_type(obj.name) # N: Revealed type is "builtins.str" + reveal_type(obj._fields) # N: Revealed type is "builtins.tuple[builtins.str, ...]" b1: Bar b2: Baz @@ -1337,3 +1338,12 @@ class SNT(NT[int]): ... reveal_type(SNT("test", 42).meth()) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.SNT]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNoCrashUnsupportedNamedTuple] +from typing import NamedTuple +class Test: + def __init__(self, field) -> None: + self.Item = NamedTuple("x", [(field, str)]) # E: NamedTuple type as an attribute is not supported + self.item: self.Item # E: Name "self.Item" is not defined +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/fixtures/typing-namedtuple.pyi b/test-data/unit/fixtures/typing-namedtuple.pyi index c8658a815a13..f4744575fc09 100644 --- a/test-data/unit/fixtures/typing-namedtuple.pyi +++ b/test-data/unit/fixtures/typing-namedtuple.pyi @@ -6,6 +6,8 @@ Type = 0 Literal = 0 Optional = 0 Self = 0 +Tuple = 0 +ClassVar = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -18,6 +20,9 @@ class Mapping(Iterable[KT], Generic[KT, T_co]): def keys(self) -> Iterable[T]: pass # Approximate return type def __getitem__(self, key: T) -> T_co: pass -class Tuple(Sequence): pass -class NamedTuple(Tuple): - name: str +class NamedTuple(tuple[Any, ...]): + _fields: ClassVar[tuple[str, ...]] + @overload + def __init__(self, typename: str, fields: Iterable[tuple[str, Any]] = ...) -> None: ... + @overload + def __init__(self, typename: str, fields: None = None, **kwargs: Any) -> None: ... From e7b917ec7532206b996542570f4b68a33c3ff771 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 10 Jun 2023 00:48:01 +0100 Subject: [PATCH 0010/1617] Fix crash on follow_imports_for_stubs (#15407) --- mypy/build.py | 9 +++++---- mypy/messages.py | 2 +- test-data/unit/check-flags.test | 10 ++++++++++ test-data/unit/cmdline.test | 8 ++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index c239afb56236..7913eae9c6ed 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -116,7 +116,10 @@ "types", "typing_extensions", "mypy_extensions", - "_importlib_modulespec", + "_typeshed", + "_collections_abc", + "collections", + "collections.abc", "sys", "abc", } @@ -659,8 +662,6 @@ def __init__( for module in CORE_BUILTIN_MODULES: if options.use_builtins_fixtures: continue - if module == "_importlib_modulespec": - continue path = self.find_module_cache.find_module(module) if not isinstance(path, str): raise CompileError( @@ -2637,7 +2638,7 @@ def find_module_and_diagnose( result.endswith(".pyi") # Stubs are always normal and not options.follow_imports_for_stubs # except when they aren't ) - or id in mypy.semanal_main.core_modules # core is always normal + or id in CORE_BUILTIN_MODULES # core is always normal ): follow_imports = "normal" if skip_diagnose: diff --git a/mypy/messages.py b/mypy/messages.py index cb95fb1c0bde..9d703a1a974a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2437,7 +2437,7 @@ def format_literal_value(typ: LiteralType) -> str: if isinstance(typ, Instance): itype = typ # Get the short name of the type. - if itype.type.fullname in ("types.ModuleType", "_importlib_modulespec.ModuleType"): + if itype.type.fullname == "types.ModuleType": # Make some common error messages simpler and tidier. base_str = "Module" if itype.extra_attrs and itype.extra_attrs.mod_name and module_names: diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 39fcef1db673..4a43224260d1 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2174,3 +2174,13 @@ def f(x: bytes, y: bytearray, z: memoryview) -> None: x in y x in z [builtins fixtures/primitives.pyi] + +[case testNoCrashFollowImportsForStubs] +# flags: --config-file tmp/mypy.ini +{**{"x": "y"}} + +[file mypy.ini] +\[mypy] +follow_imports = skip +follow_imports_for_stubs = true +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index c2e98cdb74f9..b87bb7f17eb0 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1484,6 +1484,10 @@ note: A user-defined top-level module with name "typing" is not supported [file dir/stdlib/types.pyi] [file dir/stdlib/typing.pyi] [file dir/stdlib/typing_extensions.pyi] +[file dir/stdlib/_typeshed.pyi] +[file dir/stdlib/_collections_abc.pyi] +[file dir/stdlib/collections/abc.pyi] +[file dir/stdlib/collections/__init__.pyi] [file dir/stdlib/VERSIONS] [out] Failed to find builtin module mypy_extensions, perhaps typeshed is broken? @@ -1523,6 +1527,10 @@ class dict: pass [file dir/stdlib/typing.pyi] [file dir/stdlib/mypy_extensions.pyi] [file dir/stdlib/typing_extensions.pyi] +[file dir/stdlib/_typeshed.pyi] +[file dir/stdlib/_collections_abc.pyi] +[file dir/stdlib/collections/abc.pyi] +[file dir/stdlib/collections/__init__.pyi] [file dir/stdlib/foo.pyi] 1() # Errors are reported if the file was explicitly passed on the command line [file dir/stdlib/VERSIONS] From a108c670c395c719e81cd87674654daf54ec95ee Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 11 Jun 2023 09:41:25 -0400 Subject: [PATCH 0011/1617] Add get_expression_type to CheckerPluginInterface (#15369) Fixes #14845. p.s. In the issue above, I was concerned that adding this method would create an avenue for infinite recursions (if called carelessly), but in fact I haven't managed to induce it, e.g. FunctionSigContext has `args` but not the call expression itself. --- mypy/checker.py | 3 +++ mypy/plugin.py | 5 +++++ mypy/plugins/attrs.py | 19 ++++--------------- test-data/unit/check-custom-plugin.test | 5 ++++- test-data/unit/plugins/function_sig_hook.py | 21 +++++++++------------ 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c1c31538b7de..87bd9915af97 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6793,6 +6793,9 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: ) return not watcher.has_new_errors() + def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: + return self.expr_checker.accept(node, type_context=type_context) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/plugin.py b/mypy/plugin.py index cf124b45d04f..4d62c2bd184b 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -250,6 +250,11 @@ def named_generic_type(self, name: str, args: list[Type]) -> Instance: """Construct an instance of a builtin type with given type arguments.""" raise NotImplementedError + @abstractmethod + def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: + """Checks the type of the given expression.""" + raise NotImplementedError + @trait class SemanticAnalyzerPluginInterface: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index afd9423d6820..e05a8e44b2f8 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -9,7 +9,6 @@ import mypy.plugin # To avoid circular imports. from mypy.applytype import apply_generic_arguments -from mypy.checker import TypeChecker from mypy.errorcodes import LITERAL_REQ from mypy.expandtype import expand_type, expand_type_by_instance from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type @@ -1048,13 +1047,7 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl return ctx.default_signature # leave it to the type checker to complain inst_arg = ctx.args[0][0] - - # - assert isinstance(ctx.api, TypeChecker) - inst_type = ctx.api.expr_checker.accept(inst_arg) - # - - inst_type = get_proper_type(inst_type) + inst_type = get_proper_type(ctx.api.get_expression_type(inst_arg)) inst_type_str = format_type_bare(inst_type, ctx.api.options) attr_types = _get_expanded_attr_types(ctx, inst_type, inst_type, inst_type) @@ -1074,14 +1067,10 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> CallableType: """Provide the signature for `attrs.fields`.""" - if not ctx.args or len(ctx.args) != 1 or not ctx.args[0] or not ctx.args[0][0]: + if len(ctx.args) != 1 or len(ctx.args[0]) != 1: return ctx.default_signature - # - assert isinstance(ctx.api, TypeChecker) - inst_type = ctx.api.expr_checker.accept(ctx.args[0][0]) - # - proper_type = get_proper_type(inst_type) + proper_type = get_proper_type(ctx.api.get_expression_type(ctx.args[0][0])) # fields(Any) -> Any, fields(type[Any]) -> Any if ( @@ -1098,7 +1087,7 @@ def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl inner = get_proper_type(proper_type.upper_bound) if isinstance(inner, Instance): # We need to work arg_types to compensate for the attrs stubs. - arg_types = [inst_type] + arg_types = [proper_type] cls = inner.type elif isinstance(proper_type, CallableType): cls = proper_type.type_object() diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index c81de675d808..ec5bce219dbd 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -887,7 +887,10 @@ plugins=/test-data/unit/plugins/descriptor.py # flags: --config-file tmp/mypy.ini def dynamic_signature(arg1: str) -> str: ... -reveal_type(dynamic_signature(1)) # N: Revealed type is "builtins.int" +a: int = 1 +reveal_type(dynamic_signature(a)) # N: Revealed type is "builtins.int" +b: bytes = b'foo' +reveal_type(dynamic_signature(b)) # N: Revealed type is "builtins.bytes" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/function_sig_hook.py diff --git a/test-data/unit/plugins/function_sig_hook.py b/test-data/unit/plugins/function_sig_hook.py index d83c7df26209..4d901b96716e 100644 --- a/test-data/unit/plugins/function_sig_hook.py +++ b/test-data/unit/plugins/function_sig_hook.py @@ -1,5 +1,5 @@ -from mypy.plugin import CallableType, CheckerPluginInterface, FunctionSigContext, Plugin -from mypy.types import Instance, Type +from mypy.plugin import CallableType, FunctionSigContext, Plugin + class FunctionSigPlugin(Plugin): def get_function_signature_hook(self, fullname): @@ -7,20 +7,17 @@ def get_function_signature_hook(self, fullname): return my_hook return None -def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: - if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) - elif typ.args: - return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) - - return typ def my_hook(ctx: FunctionSigContext) -> CallableType: + arg1_args = ctx.args[0] + if len(arg1_args) != 1: + return ctx.default_signature + arg1_type = ctx.api.get_expression_type(arg1_args[0]) return ctx.default_signature.copy_modified( - arg_types=[_str_to_int(ctx.api, t) for t in ctx.default_signature.arg_types], - ret_type=_str_to_int(ctx.api, ctx.default_signature.ret_type), + arg_types=[arg1_type], + ret_type=arg1_type, ) + def plugin(version): return FunctionSigPlugin From 9e7bc383eb9a5df94e8bf6117f008d97017a1fcf Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 11 Jun 2023 21:08:12 +0100 Subject: [PATCH 0012/1617] Always allow returning Any from lambda (#15413) --- mypy/checker.py | 1 + test-data/unit/check-flags.test | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 87bd9915af97..1026376cce63 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4270,6 +4270,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: isinstance(return_type, Instance) and return_type.type.fullname == "builtins.object" ) + and not is_lambda ): self.msg.incorrectly_returning_any(return_type, s) return diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 4a43224260d1..6ec0849146c0 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2184,3 +2184,14 @@ def f(x: bytes, y: bytearray, z: memoryview) -> None: follow_imports = skip follow_imports_for_stubs = true [builtins fixtures/dict.pyi] + +[case testReturnAnyLambda] +# flags: --warn-return-any +from typing import Any, Callable + +def cb(f: Callable[[int], int]) -> None: ... +a: Any +cb(lambda x: a) # OK + +fn = lambda x: a +cb(fn) From 4aa18ea818bfe55617c3f2423d1020796ceccdfc Mon Sep 17 00:00:00 2001 From: jhance Date: Mon, 12 Jun 2023 03:50:31 -0700 Subject: [PATCH 0013/1617] Increment version for 1.5-dev (#15405) --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 826ba0020100..42cda2fc7794 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.4.0+dev" +__version__ = "1.5.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 9b9272ae758e23653c81c94031ec03892d31622a Mon Sep 17 00:00:00 2001 From: Logan Hunt <39638017+dosisod@users.noreply.github.com> Date: Mon, 12 Jun 2023 03:57:23 -0700 Subject: [PATCH 0014/1617] [mypyc] Don't explicitly assign `NULL` values in setup functions (#15379) While investigating something unrelated I stumbled across the `*_setup` functions, and I noticed that they contain a lot of code that looks like this: ```c self->abc = NULL; self->xyz = NULL; // ... ``` Something told me that assigning `NULL` to a bunch of fields is propably not needed, and when looking at the docs for `tp_alloc()` I found this reference to [`allocfunc`](https://docs.python.org/3/c-api/typeobj.html#c.allocfunc), the typedef for `tp_alloc()`: > It should return a pointer to a block of memory of adequate length for the instance, suitably aligned, ***and initialized to zeros***, ... Emphasis is mine. Basically I added a simple check that removes lines that assigns `NULL` values in setup functions. This removes about ~4100 lines from the C file when self-compiling. --- mypyc/codegen/emitclass.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 6a272d1aee2b..eac7a2207972 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -578,7 +578,12 @@ def generate_setup_for_class( for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): - emitter.emit_line(rf"self->{emitter.attr(attr)} = {emitter.c_undefined_value(rtype)};") + value = emitter.c_undefined_value(rtype) + + # We don't need to set this field to NULL since tp_alloc() already + # zero-initializes `self`. + if value != "NULL": + emitter.emit_line(rf"self->{emitter.attr(attr)} = {value};") # Initialize attributes to default values, if necessary if defaults_fn is not None: From cab8c674ebef944498ae4e9a89cd9c77316656ec Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Mon, 12 Jun 2023 13:05:11 -0400 Subject: [PATCH 0015/1617] test_parse_data: fix cwd (#15417) Fixes https://github.com/python/mypy/pull/15385#issuecomment-1587030261 --- mypy/test/meta/test_parse_data.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index 6d9b32b96c7e..a74b3d71d392 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -16,13 +16,15 @@ def _dedent(self, s: str) -> str: return textwrap.dedent(s).lstrip() def _run_pytest(self, data_suite: str) -> str: - p = Path(test_data_prefix) / "check-__fixture__.test" + p_test_data = Path(test_data_prefix) + p_root = p_test_data.parent.parent + p = p_test_data / "check-__fixture__.test" assert not p.exists() try: p.write_text(data_suite) test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" args = [sys.executable, "-m", "pytest", "-n", "0", "-s", test_nodeid] - proc = subprocess.run(args, capture_output=True, check=False) + proc = subprocess.run(args, cwd=p_root, capture_output=True, check=False) return proc.stdout.decode() finally: p.unlink() From 61a7f3bfa37c433ac34f4f07dc224a1745577c82 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 13 Jun 2023 18:54:33 -0400 Subject: [PATCH 0016/1617] Remove special casing for "cannot" in error messages (#15428) Co-authored-by: hauntsaninja --- mypy/test/helpers.py | 6 - test-data/unit/check-classes.test | 4 +- test-data/unit/check-overloading.test | 149 +++++++++++----------- test-data/unit/check-recursive-types.test | 4 +- test-data/unit/check-tuples.test | 40 +++--- test-data/unit/semanal-errors.test | 63 +++++++-- test-data/unit/semanal-statements.test | 4 +- 7 files changed, 151 insertions(+), 119 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 849ccdc376bd..630ba305f349 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -46,15 +46,9 @@ def run_mypy(args: list[str]) -> None: def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) -> None: """Assert that two string arrays are equal. - We consider "can't" and "cannot" equivalent, by replacing the - former with the latter before comparing. - Display any differences in a human-readable form. """ actual = clean_up(actual) - actual = [line.replace("can't", "cannot") for line in actual] - expected = [line.replace("can't", "cannot") for line in expected] - if actual != expected: num_skip_start = num_skipped_prefix_lines(expected, actual) num_skip_end = num_skipped_suffix_lines(expected, actual) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index c2eddbc597a0..cfd0cc3a4ec6 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2019,7 +2019,7 @@ tmp/foo.pyi:5: note: @overload tmp/foo.pyi:5: note: def __add__(self, int, /) -> int tmp/foo.pyi:5: note: @overload tmp/foo.pyi:5: note: def __add__(self, str, /) -> str -tmp/foo.pyi:5: note: Overloaded operator methods cannot have wider argument types in overrides +tmp/foo.pyi:5: note: Overloaded operator methods can't have wider argument types in overrides [case testOperatorMethodOverrideWideningArgumentType] import typing @@ -2138,7 +2138,7 @@ tmp/foo.pyi:8: note: @overload tmp/foo.pyi:8: note: def __add__(self, str, /) -> A tmp/foo.pyi:8: note: @overload tmp/foo.pyi:8: note: def __add__(self, type, /) -> A -tmp/foo.pyi:8: note: Overloaded operator methods cannot have wider argument types in overrides +tmp/foo.pyi:8: note: Overloaded operator methods can't have wider argument types in overrides [case testOverloadedOperatorMethodOverrideWithSwitchedItemOrder] from foo import * diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4209f4ec9164..96fbaa77514e 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -4745,7 +4745,7 @@ main:12: note: @overload main:12: note: def __add__(self, Other, /) -> B main:12: note: @overload main:12: note: def __add__(self, A, /) -> A -main:12: note: Overloaded operator methods cannot have wider argument types in overrides +main:12: note: Overloaded operator methods can't have wider argument types in overrides main:32: note: Revealed type is "__main__.Other" [case testOverloadErrorMessageManyMatches] @@ -5405,26 +5405,26 @@ if False: def f2(g): ... reveal_type(f2(A())) # N: Revealed type is "__main__.A" reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" \ - # N: Possible overload variants: \ - # N: def f2(g: A) -> A \ - # N: def f2(g: B) -> B \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f2(g: A) -> A \ + # N: def f2(g: B) -> B \ + # N: Revealed type is "Any" @overload def f3(g: A) -> A: ... @overload def f3(g: B) -> B: ... -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f3(g: C) -> C: ... def f3(g): ... reveal_type(f3(A())) # N: Revealed type is "__main__.A" reveal_type(f3(C())) # E: No overload variant of "f3" matches argument type "C" \ - # N: Possible overload variants: \ - # N: def f3(g: A) -> A \ - # N: def f3(g: B) -> B \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f3(g: A) -> A \ + # N: def f3(g: B) -> B \ + # N: Revealed type is "Any" if True: @overload @@ -5731,10 +5731,10 @@ def f1(x): ... reveal_type(f1(A())) # N: Revealed type is "__main__.A" reveal_type(f1(B())) # N: Revealed type is "__main__.B" reveal_type(f1(D())) # E: No overload variant of "f1" matches argument type "D" \ - # N: Possible overload variants: \ - # N: def f1(x: A) -> A \ - # N: def f1(x: B) -> B \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f1(x: A) -> A \ + # N: def f1(x: B) -> B \ + # N: Revealed type is "Any" @overload def f2(x: A) -> A: ... @@ -5751,14 +5751,14 @@ def f2(x): ... reveal_type(f2(A())) # N: Revealed type is "__main__.A" reveal_type(f2(B())) # N: Revealed type is "__main__.B" reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" \ - # N: Possible overload variants: \ - # N: def f2(x: A) -> A \ - # N: def f2(x: B) -> B \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f2(x: A) -> A \ + # N: def f2(x: B) -> B \ + # N: Revealed type is "Any" @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... @@ -5771,13 +5771,13 @@ else: def f3(x): ... reveal_type(f3(A())) # N: Revealed type is "__main__.A" reveal_type(f3(B())) # E: No overload variant of "f3" matches argument type "B" \ - # N: Possible overload variant: \ - # N: def f3(x: A) -> A \ - # N: Revealed type is "Any" + # N: Possible overload variant: \ + # N: def f3(x: A) -> A \ + # N: Revealed type is "Any" @overload # E: Single overload definition, multiple required def f4(x: A) -> A: ... -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f4(x: B) -> B: ... @@ -5787,9 +5787,10 @@ else: def f4(x): ... reveal_type(f4(A())) # N: Revealed type is "__main__.A" reveal_type(f4(B())) # E: No overload variant of "f4" matches argument type "B" \ - # N: Possible overload variant: \ - # N: def f4(x: A) -> A \ - # N: Revealed type is "Any" + # N: Possible overload variant: \ + # N: def f4(x: A) -> A \ + # N: Revealed type is "Any" + [case testOverloadIfElse3] # flags: --always-false False @@ -5817,10 +5818,10 @@ else: def f1(x): ... reveal_type(f1(A())) # N: Revealed type is "__main__.A" reveal_type(f1(B())) # E: No overload variant of "f1" matches argument type "B" \ - # N: Possible overload variants: \ - # N: def f1(x: A) -> A \ - # N: def f1(x: D) -> D \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f1(x: A) -> A \ + # N: def f1(x: D) -> D \ + # N: Revealed type is "Any" reveal_type(f1(D())) # N: Revealed type is "__main__.D" @overload # E: Single overload definition, multiple required @@ -5828,7 +5829,7 @@ def f2(x: A) -> A: ... if False: @overload def f2(x: B) -> B: ... -elif maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +elif maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f2(x: C) -> C: ... @@ -5838,13 +5839,13 @@ else: def f2(x): ... reveal_type(f2(A())) # N: Revealed type is "__main__.A" reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" \ - # N: Possible overload variant: \ - # N: def f2(x: A) -> A \ - # N: Revealed type is "Any" + # N: Possible overload variant: \ + # N: def f2(x: A) -> A \ + # N: Revealed type is "Any" @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... @@ -5857,14 +5858,14 @@ else: def f3(x): ... reveal_type(f3(A())) # N: Revealed type is "__main__.A" reveal_type(f3(B())) # E: No overload variant of "f3" matches argument type "B" \ - # N: Possible overload variant: \ - # N: def f3(x: A) -> A \ - # N: Revealed type is "Any" + # N: Possible overload variant: \ + # N: def f3(x: A) -> A \ + # N: Revealed type is "Any" def g(bool_var: bool) -> None: @overload def f4(x: A) -> A: ... - if bool_var: # E: Condition cannot be inferred, unable to merge overloads + if bool_var: # E: Condition can't be inferred, unable to merge overloads @overload def f4(x: B) -> B: ... elif maybe_true: # E: Name "maybe_true" is not defined @@ -5880,10 +5881,11 @@ def g(bool_var: bool) -> None: def f4(x): ... reveal_type(f4(E())) # N: Revealed type is "__main__.E" reveal_type(f4(B())) # E: No overload variant of "f4" matches argument type "B" \ - # N: Possible overload variants: \ - # N: def f4(x: A) -> A \ - # N: def f4(x: E) -> E \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f4(x: A) -> A \ + # N: def f4(x: E) -> E \ + # N: Revealed type is "Any" + [case testOverloadIfSkipUnknownExecution] # flags: --always-true True @@ -5901,14 +5903,14 @@ class D: ... @overload # E: Single overload definition, multiple required def f1(x: A) -> A: ... -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f1(x: B) -> B: ... def f1(x): ... reveal_type(f1(A())) # N: Revealed type is "__main__.A" -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f2(x: A) -> A: ... @@ -5918,15 +5920,15 @@ def f2(x: B) -> B: ... def f2(x: C) -> C: ... def f2(x): ... reveal_type(f2(A())) # E: No overload variant of "f2" matches argument type "A" \ - # N: Possible overload variants: \ - # N: def f2(x: B) -> B \ - # N: def f2(x: C) -> C \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f2(x: B) -> B \ + # N: def f2(x: C) -> C \ + # N: Revealed type is "Any" if True: @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... - if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... @@ -5934,7 +5936,7 @@ if True: reveal_type(f3(A())) # N: Revealed type is "__main__.A" if True: - if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f4(x: A) -> A: ... @@ -5944,10 +5946,10 @@ if True: def f4(x: C) -> C: ... def f4(x): ... reveal_type(f4(A())) # E: No overload variant of "f4" matches argument type "A" \ - # N: Possible overload variants: \ - # N: def f4(x: B) -> B \ - # N: def f4(x: C) -> C \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f4(x: B) -> B \ + # N: def f4(x: C) -> C \ + # N: Revealed type is "Any" [case testOverloadIfDontSkipUnrelatedOverload] # flags: --always-true True @@ -6187,16 +6189,16 @@ if False: def f8(x): ... reveal_type(f8(A())) # N: Revealed type is "__main__.A" reveal_type(f8(C())) # E: No overload variant of "f8" matches argument type "C" \ - # N: Possible overload variants: \ - # N: def f8(x: A) -> A \ - # N: def f8(x: B) -> B \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f8(x: A) -> A \ + # N: def f8(x: B) -> B \ + # N: Revealed type is "Any" -if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f9(x: A) -> A: ... -if another_maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ +if another_maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "another_maybe_true" is not defined @overload def f9(x: B) -> B: ... @@ -6206,18 +6208,18 @@ def f9(x: C) -> C: ... def f9(x: D) -> D: ... def f9(x): ... reveal_type(f9(A())) # E: No overload variant of "f9" matches argument type "A" \ - # N: Possible overload variants: \ - # N: def f9(x: C) -> C \ - # N: def f9(x: D) -> D \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f9(x: C) -> C \ + # N: def f9(x: D) -> D \ + # N: Revealed type is "Any" reveal_type(f9(C())) # N: Revealed type is "__main__.C" if True: - if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f10(x: A) -> A: ... - if another_maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + if another_maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "another_maybe_true" is not defined @overload def f10(x: B) -> B: ... @@ -6227,10 +6229,10 @@ if True: def f10(x: D) -> D: ... def f10(x): ... reveal_type(f10(A())) # E: No overload variant of "f10" matches argument type "A" \ - # N: Possible overload variants: \ - # N: def f10(x: C) -> C \ - # N: def f10(x: D) -> D \ - # N: Revealed type is "Any" + # N: Possible overload variants: \ + # N: def f10(x: C) -> C \ + # N: def f10(x: D) -> D \ + # N: Revealed type is "Any" reveal_type(f10(C())) # N: Revealed type is "__main__.C" if some_var: # E: Name "some_var" is not defined @@ -6251,6 +6253,7 @@ if True: def f12(x: B) -> B: ... def f12(x): ... reveal_type(f12(A())) # N: Revealed type is "__main__.A" + [typing fixtures/typing-medium.pyi] [case testOverloadIfUnconditionalFuncDef] @@ -6406,7 +6409,7 @@ def f1(g: A) -> A: ... if True: @overload # E: Single overload definition, multiple required def f1(g: B) -> B: ... - if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload def f1(g: C) -> C: ... @@ -6432,7 +6435,7 @@ if True: def f3(g: B) -> B: ... if True: pass # Some other node - @overload # E: Name "f3" already defined on line 32 \ + @overload # E: Name "f3" already defined on line 32 \ # E: An overloaded function outside a stub file must have an implementation def f3(g: C) -> C: ... @overload diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 51f07cdccf8f..dc1ae448c0d1 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -409,8 +409,8 @@ def local() -> None: x: L reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, Any]]" -S = Type[S] # E: Type[...] cannot contain another Type[...] -U = Type[Union[int, U]] # E: Type[...] cannot contain another Type[...] +S = Type[S] # E: Type[...] can't contain another Type[...] +U = Type[Union[int, U]] # E: Type[...] can't contain another Type[...] x: U reveal_type(x) # N: Revealed type is "Type[Any]" diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index e843532a2560..f8d6573a56fe 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1367,42 +1367,36 @@ reveal_type(a + b) # N: Revealed type is "Tuple[builtins.int, builtins.str, bui from typing import Tuple # long initializer assignment with few mismatches -t: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", 11) \ - # E: Incompatible types in assignment (3 tuple items are incompatible) \ - # N: Expression tuple item 8 has type "str"; "int" expected; \ - # N: Expression tuple item 9 has type "str"; "int" expected; \ - # N: Expression tuple item 10 has type "str"; "int" expected; +t: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", 11) # E: Incompatible types in assignment (3 tuple items are incompatible) \ + # N: Expression tuple item 8 has type "str"; "int" expected; \ + # N: Expression tuple item 9 has type "str"; "int" expected; \ + # N: Expression tuple item 10 has type "str"; "int" expected; # long initializer assignment with more mismatches -t1: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") \ - # E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \ - # N: Expression tuple item 8 has type "str"; "int" expected; \ - # N: Expression tuple item 9 has type "str"; "int" expected; \ - # N: Expression tuple item 10 has type "str"; "int" expected; +t1: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") # E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \ + # N: Expression tuple item 8 has type "str"; "int" expected; \ + # N: Expression tuple item 9 has type "str"; "int" expected; \ + # N: Expression tuple item 10 has type "str"; "int" expected; # short tuple initializer assignment -t2: Tuple[int, ...] = (1, 2, "s", 4) \ - # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, ...]") +t2: Tuple[int, ...] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, ...]") # long initializer assignment with few mismatches, no ellipsis -t3: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "str", "str") \ - # E: Incompatible types in assignment (2 tuple items are incompatible) \ - # N: Expression tuple item 10 has type "str"; "int" expected; \ - # N: Expression tuple item 11 has type "str"; "int" expected; +t3: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "str", "str") # E: Incompatible types in assignment (2 tuple items are incompatible) \ + # N: Expression tuple item 10 has type "str"; "int" expected; \ + # N: Expression tuple item 11 has type "str"; "int" expected; # long initializer assignment with more mismatches, no ellipsis -t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") \ - # E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \ - # N: Expression tuple item 8 has type "str"; "int" expected; \ - # N: Expression tuple item 9 has type "str"; "int" expected; \ - # N: Expression tuple item 10 has type "str"; "int" expected; +t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") # E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \ + # N: Expression tuple item 8 has type "str"; "int" expected; \ + # N: Expression tuple item 9 has type "str"; "int" expected; \ + # N: Expression tuple item 10 has type "str"; "int" expected; # short tuple initializer assignment, no ellipsis t5: Tuple[int, int] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, int]") # long initializer assignment with mismatched pairs -t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) \ - # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>]) +t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>]) [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 0c3de312cdfa..9c6ac2833c8b 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -362,41 +362,55 @@ main:2: error: "yield" outside function 1 = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [out version>=3.10] -main:1: error: can't assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues2] (1) = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [out version>=3.10] -main:1: error: can't assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues3] (1, 1) = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [case testInvalidLvalues4] [1, 1] = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [case testInvalidLvalues6] x = y = z = 1 # ok x, (y, 1) = 1 [out] main:2: error: can't assign to literal +[out version>=3.8] +main:2: error: cannot assign to literal [case testInvalidLvalues7] x, [y, 1] = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [case testInvalidLvalues8] x, [y, [z, 1]] = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [case testInvalidLvalues9] x, (y) = 1 # ok @@ -404,41 +418,53 @@ x, (y, (z, z)) = 1 # ok x, (y, (z, 1)) = 1 [out] main:3: error: can't assign to literal +[out version>=3.8] +main:3: error: cannot assign to literal [case testInvalidLvalues10] x + x = 1 [out] main:1: error: can't assign to operator +[out version>=3.8] +main:1: error: cannot assign to operator [out version>=3.10] -main:1: error: can't assign to expression here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? [case testInvalidLvalues11] -x = 1 [out] main:1: error: can't assign to operator +[out version>=3.8] +main:1: error: cannot assign to operator [out version>=3.10] -main:1: error: can't assign to expression here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? [case testInvalidLvalues12] 1.1 = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [out version>=3.10] -main:1: error: can't assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues13] 'x' = 1 [out] main:1: error: can't assign to literal +[out version>=3.8] +main:1: error: cannot assign to literal [out version>=3.10] -main:1: error: can't assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues14] x() = 1 [out] main:1: error: can't assign to function call +[out version>=3.8] +main:1: error: cannot assign to function call [out version>=3.10] -main:1: error: can't assign to function call here. Maybe you meant '==' instead of '='? +main:1: error: cannot assign to function call here. Maybe you meant '==' instead of '='? [case testTwoStarExpressions] a, *b, *c = 1 @@ -490,20 +516,24 @@ main:2: error: Can use starred expression only as assignment target [case testInvalidDel1] x = 1 -del x(1) # E: can't delete function call +del x(1) [out] +main:2: error: can't delete function call +[out version>=3.8] +main:2: error: cannot delete function call [case testInvalidDel2] x = 1 del x + 1 [out] +main:2: error: can't delete operator +[out version>=3.8] main:2: error: cannot delete operator [out version>=3.10] main:2: error: cannot delete expression [case testInvalidDel3] del z # E: Name "z" is not defined -[out] [case testFunctionTvarScope] @@ -898,8 +928,10 @@ def f(): pass f() = 1 # type: int [out] main:3: error: can't assign to function call +[out version>=3.8] +main:3: error: cannot assign to function call [out version>=3.10] -main:3: error: can't assign to function call here. Maybe you meant '==' instead of '='? +main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? [case testIndexedAssignmentWithTypeDeclaration] import typing @@ -975,6 +1007,8 @@ x, y = 1, 2 # type: int # E: Tuple type expected for multiple variables a = 1 a() = None # type: int [out] +main:2: error: can't assign to function call +[out version>=3.8] main:2: error: cannot assign to function call [out version>=3.10] main:2: error: cannot assign to function call here. Maybe you meant '==' instead of '='? @@ -1275,8 +1309,11 @@ main:2: note: Did you forget to import it from "typing"? (Suggestion: "from typi [case testInvalidWithTarget] def f(): pass -with f() as 1: pass # E: can't assign to literal +with f() as 1: pass [out] +main:2: error: can't assign to literal +[out version>=3.8] +main:2: error: cannot assign to literal [case testInvalidTypeAnnotation] import typing @@ -1291,8 +1328,10 @@ def f() -> None: f() = 1 # type: int [out] main:3: error: can't assign to function call +[out version>=3.8] +main:3: error: cannot assign to function call [out version>=3.10] -main:3: error: can't assign to function call here. Maybe you meant '==' instead of '='? +main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? [case testInvalidReferenceToAttributeOfOuterClass] class A: diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index f602c236c949..9b43a5b853c0 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -558,8 +558,10 @@ def f(x, y) -> None: del x, y + 1 [out] main:2: error: can't delete operator +[out version>=3.8] +main:2: error: cannot delete operator [out version>=3.10] -main:2: error: can't delete expression +main:2: error: cannot delete expression [case testTry] class c: pass From 2a89f752dc174b4f0b99291c4a6bf10338dbbd5e Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 13 Jun 2023 18:56:22 -0400 Subject: [PATCH 0017/1617] [out] version checks must be against min version or higher (#15430) --- mypy/test/data.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypy/test/data.py b/mypy/test/data.py index 86193e303d9c..9b3931ee8be6 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -17,6 +17,7 @@ import pytest +from mypy import defaults from mypy.test.config import PREFIX, test_data_prefix, test_temp_dir root_dir = os.path.normpath(PREFIX) @@ -166,6 +167,10 @@ def _item_fail(msg: str) -> NoReturn: version = tuple(int(x) for x in version_str.split(".")) except ValueError: _item_fail(f"{version_str!r} is not a valid python version") + if version < defaults.PYTHON3_VERSION: + _item_fail( + f"Version check against {version}; must be >= {defaults.PYTHON3_VERSION}" + ) if compare_op == ">=": version_check = sys.version_info >= version elif compare_op == "==": From c63e873802fecfc3f817dceb94c729a6965e3489 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 14 Jun 2023 07:11:23 -0400 Subject: [PATCH 0018/1617] docs: ref redirector (#15432) Makes the HTML builder generate a _refs.html file that redirects (global) refs (when provided as a hash i.e. `_refs.html#some-ref`) to the appropriate document. This allows tools (e.g. #15431) to link to well-known refs. --- docs/source/conf.py | 2 +- docs/source/html_builder.py | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 docs/source/html_builder.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 80097ef5b3a8..683b2a6785b3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx.ext.intersphinx"] +extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py new file mode 100644 index 000000000000..49d58dda12ec --- /dev/null +++ b/docs/source/html_builder.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import json +import textwrap +from pathlib import Path +from typing import Any + +from sphinx.addnodes import document +from sphinx.application import Sphinx +from sphinx.builders.html import StandaloneHTMLBuilder + + +class MypyHTMLBuilder(StandaloneHTMLBuilder): + def __init__(self, app: Sphinx) -> None: + super().__init__(app) + self._ref_to_doc = {} + + def write_doc(self, docname: str, doctree: document) -> None: + super().write_doc(docname, doctree) + self._ref_to_doc.update({_id: docname for _id in doctree.ids}) + + def _write_ref_redirector(self) -> None: + p = Path(self.outdir) / "_refs.html" + data = f""" + + + + + + """ + p.write_text(textwrap.dedent(data)) + + def finish(self) -> None: + super().finish() + self._write_ref_redirector() + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_builder(MypyHTMLBuilder, override=True) + + return {"version": "0.1", "parallel_read_safe": True, "parallel_write_safe": True} From cdddc508c3533b960786841253e1aba251802081 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 14 Jun 2023 14:33:58 +0100 Subject: [PATCH 0019/1617] Fix dmypy run on Windows (#15429) Fixes #10709 Two changes here: * Add equality methods to `ErrorCode` because they may appear in options snapshots * Use stable comparison for option snapshots, since `Options.apply_changes()` does some unrelated things with flags Both are only relevant on Windows where options may roundtrip as: `options -> snapshot -> pickle -> base64 -> unpickle -> apply_changes`. --- mypy/dmypy_server.py | 2 +- mypy/errorcodes.py | 8 ++++++++ mypy/options.py | 14 +++++++++++++- test-data/unit/daemon.test | 27 +++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index c742f3116402..631aab4ccbc8 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -330,7 +330,7 @@ def cmd_run( header=argparse.SUPPRESS, ) # Signal that we need to restart if the options have changed - if self.options_snapshot != options.snapshot(): + if not options.compare_stable(self.options_snapshot): return {"restart": "configuration changed"} if __version__ != version: return {"restart": "mypy version changed"} diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 50a82be9816d..e87b04b6f473 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -37,6 +37,14 @@ def __init__( def __str__(self) -> str: return f"" + def __eq__(self, other: object) -> bool: + if not isinstance(other, ErrorCode): + return False + return self.code == other.code + + def __hash__(self) -> int: + return hash((self.code,)) + ATTR_DEFINED: Final = ErrorCode("attr-defined", "Check that attribute exists", "General") NAME_DEFINED: Final = ErrorCode("name-defined", "Check that name is defined", "General") diff --git a/mypy/options.py b/mypy/options.py index 45591597ba69..2785d2034c54 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -373,7 +373,7 @@ def use_or_syntax(self) -> bool: def new_semantic_analyzer(self) -> bool: return True - def snapshot(self) -> object: + def snapshot(self) -> dict[str, object]: """Produce a comparable snapshot of this Option""" # Under mypyc, we don't have a __dict__, so we need to do worse things. d = dict(getattr(self, "__dict__", ())) @@ -388,6 +388,7 @@ def __repr__(self) -> str: return f"Options({pprint.pformat(self.snapshot())})" def apply_changes(self, changes: dict[str, object]) -> Options: + # Note: effects of this method *must* be idempotent. new_options = Options() # Under mypyc, we don't have a __dict__, so we need to do worse things. replace_object_state(new_options, self, copy_dict=True) @@ -413,6 +414,17 @@ def apply_changes(self, changes: dict[str, object]) -> Options: return new_options + def compare_stable(self, other_snapshot: dict[str, object]) -> bool: + """Compare options in a way that is stable for snapshot() -> apply_changes() roundtrip. + + This is needed because apply_changes() has non-trivial effects for some flags, so + Options().apply_changes(options.snapshot()) may result in a (slightly) different object. + """ + return ( + Options().apply_changes(self.snapshot()).snapshot() + == Options().apply_changes(other_snapshot).snapshot() + ) + def build_per_module_cache(self) -> None: self._per_module_cache = {} diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index c60068a44bec..424fc6d2b167 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -28,6 +28,33 @@ Daemon stopped [file foo.py] def f(): pass +[case testDaemonRunIgnoreMissingImports] +$ dmypy run -- foo.py --follow-imports=error --ignore-missing-imports +Daemon started +Success: no issues found in 1 source file +$ dmypy stop +Daemon stopped +[file foo.py] +def f(): pass + +[case testDaemonRunErrorCodes] +$ dmypy run -- foo.py --follow-imports=error --disable-error-code=type-abstract +Daemon started +Success: no issues found in 1 source file +$ dmypy stop +Daemon stopped +[file foo.py] +def f(): pass + +[case testDaemonRunCombinedOptions] +$ dmypy run -- foo.py --follow-imports=error --ignore-missing-imports --disable-error-code=type-abstract +Daemon started +Success: no issues found in 1 source file +$ dmypy stop +Daemon stopped +[file foo.py] +def f(): pass + [case testDaemonIgnoreConfigFiles] $ dmypy start -- --follow-imports=error Daemon started From db97bbcede7ddeaecb3da31ee00f1812b115b8a4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 14 Jun 2023 14:35:28 +0100 Subject: [PATCH 0020/1617] Use consistent anchors for error codes (#15435) Fixes #15431 After this PR one will be able to easily find documentation for given error code using `https://mypy.readthedocs.io/en/stable/_refs.html#code-{code as reported by mypy}`, for example `https://mypy.readthedocs.io/en/stable/_refs.html#code-attr-defined`. --- docs/source/error_code_list.rst | 76 ++++++++++++++++++++++++++++++++ docs/source/error_code_list2.rst | 29 +++++++++++- docs/source/error_codes.rst | 2 +- docs/source/html_builder.py | 19 ++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 54dc31f2cfcb..1654c5910f98 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -8,6 +8,8 @@ with default options. See :ref:`error-codes` for general documentation about error codes. :ref:`error-codes-optional` documents additional error codes that you can enable. +.. _code-attr-defined: + Check that attribute exists [attr-defined] ------------------------------------------ @@ -43,6 +45,8 @@ A reference to a missing attribute is given the ``Any`` type. In the above example, the type of ``non_existent`` will be ``Any``, which can be important if you silence the error. +.. _code-union-attr: + Check that attribute exists in each union item [union-attr] ----------------------------------------------------------- @@ -75,6 +79,8 @@ You can often work around these errors by using ``assert isinstance(obj, ClassNa or ``assert obj is not None`` to tell mypy that you know that the type is more specific than what mypy thinks. +.. _code-name-defined: + Check that name is defined [name-defined] ----------------------------------------- @@ -89,6 +95,7 @@ This example accidentally calls ``sort()`` instead of :py:func:`sorted`: x = sort([3, 2, 4]) # Error: Name "sort" is not defined [name-defined] +.. _code-used-before-def: Check that a variable is not used before it's defined [used-before-def] ----------------------------------------------------------------------- @@ -105,6 +112,7 @@ Example: print(x) # Error: Name "x" is used before definition [used-before-def] x = 123 +.. _code-call-arg: Check arguments in calls [call-arg] ----------------------------------- @@ -124,6 +132,8 @@ Example: greet('jack') # OK greet('jill', 'jack') # Error: Too many arguments for "greet" [call-arg] +.. _code-arg-type: + Check argument types [arg-type] ------------------------------- @@ -144,6 +154,8 @@ Example: # expected "list[int]" [arg-type] print(first(t)) +.. _code-call-overload: + Check calls to overloaded functions [call-overload] --------------------------------------------------- @@ -175,6 +187,8 @@ Example: # Error: No overload variant of "inc_maybe" matches argument type "float" [call-overload] inc_maybe(1.2) +.. _code-valid-type: + Check validity of types [valid-type] ------------------------------------ @@ -207,6 +221,8 @@ You can use :py:data:`~typing.Callable` as the type for callable objects: for x in objs: f(x) +.. _code-var-annotated: + Require annotation if variable type is unclear [var-annotated] -------------------------------------------------------------- @@ -239,6 +255,8 @@ To address this, we add an explicit annotation: reveal_type(Bundle().items) # list[str] +.. _code-override: + Check validity of overrides [override] -------------------------------------- @@ -275,6 +293,8 @@ Example: arg: bool) -> int: ... +.. _code-return: + Check that function returns a value [return] -------------------------------------------- @@ -303,6 +323,8 @@ Example: else: raise ValueError('not defined for zero') +.. _code-return-value: + Check that return value is compatible [return-value] ---------------------------------------------------- @@ -317,6 +339,8 @@ Example: # Error: Incompatible return value type (got "int", expected "str") [return-value] return x + 1 +.. _code-assignment: + Check types in assignment statement [assignment] ------------------------------------------------ @@ -339,6 +363,8 @@ Example: # variable has type "str") [assignment] r.name = 5 +.. _code-method-assign: + Check that assignment target is not a method [method-assign] ------------------------------------------------------------ @@ -368,6 +394,8 @@ so only the second assignment will still generate an error. This error code is a subcode of the more general ``[assignment]`` code. +.. _code-type-var: + Check type variable values [type-var] ------------------------------------- @@ -390,6 +418,8 @@ Example: # Error: Value of type variable "T1" of "add" cannot be "str" [type-var] add('x', 'y') +.. _code-operator: + Check uses of various operators [operator] ------------------------------------------ @@ -404,6 +434,8 @@ Example: # Error: Unsupported operand types for + ("int" and "str") [operator] 1 + 'x' +.. _code-index: + Check indexing operations [index] --------------------------------- @@ -425,6 +457,8 @@ Example: # Error: Invalid index type "bytes" for "dict[str, int]"; expected type "str" [index] a[b'x'] = 4 +.. _code-list-item: + Check list items [list-item] ---------------------------- @@ -439,6 +473,8 @@ Example: # Error: List item 0 has incompatible type "int"; expected "str" [list-item] a: list[str] = [0] +.. _code-dict-item: + Check dict items [dict-item] ---------------------------- @@ -453,6 +489,8 @@ Example: # Error: Dict entry 0 has incompatible type "str": "str"; expected "str": "int" [dict-item] d: dict[str, int] = {'key': 'value'} +.. _code-typeddict-item: + Check TypedDict items [typeddict-item] -------------------------------------- @@ -477,6 +515,8 @@ Example: # TypedDict item "x" has type "int") [typeddict-item] p: Point = {'x': 1.2, 'y': 4} +.. _code-typeddict-unknown-key: + Check TypedDict Keys [typeddict-unknown-key] -------------------------------------------- @@ -533,6 +573,8 @@ runtime: This error code is a subcode of the wider ``[typeddict-item]`` code. +.. _code-has-type: + Check that type of target is known [has-type] --------------------------------------------- @@ -572,6 +614,8 @@ the issue: def set_y(self) -> None: self.y: int = self.x # Added annotation here +.. _code-import: + Check that import target can be found [import] ---------------------------------------------- @@ -587,6 +631,8 @@ Example: See :ref:`ignore-missing-imports` for how to work around these errors. +.. _code-no-redef: + Check that each name is defined once [no-redef] ----------------------------------------------- @@ -613,6 +659,8 @@ Example: # (the first definition wins!) A('x') +.. _code-func-returns-value: + Check that called function returns a value [func-returns-value] --------------------------------------------------------------- @@ -635,6 +683,8 @@ returns ``None``: if f(): print("not false") +.. _code-abstract: + Check instantiation of abstract classes [abstract] -------------------------------------------------- @@ -666,6 +716,8 @@ Example: # Error: Cannot instantiate abstract class "Thing" with abstract attribute "save" [abstract] t = Thing() +.. _code-type-abstract: + Safe handling of abstract type object types [type-abstract] ----------------------------------------------------------- @@ -692,6 +744,8 @@ Example: # Error: Only concrete class can be given where "Type[Config]" is expected [type-abstract] make_many(Config, 5) +.. _code-safe-super: + Check that call to an abstract method via super is valid [safe-super] --------------------------------------------------------------------- @@ -714,6 +768,8 @@ will cause runtime errors, so mypy prevents you from doing so: Mypy considers the following as trivial bodies: a ``pass`` statement, a literal ellipsis ``...``, a docstring, and a ``raise NotImplementedError`` statement. +.. _code-valid-newtype: + Check the target of NewType [valid-newtype] ------------------------------------------- @@ -738,6 +794,8 @@ To work around the issue, you can either give mypy access to the sources for ``acme`` or create a stub file for the module. See :ref:`ignore-missing-imports` for more information. +.. _code-exit-return: + Check the return type of __exit__ [exit-return] ----------------------------------------------- @@ -794,6 +852,8 @@ You can also use ``None``: def __exit__(self, exc, value, tb) -> None: # Also OK print('exit') +.. _code-name-match: + Check that naming is consistent [name-match] -------------------------------------------- @@ -807,6 +867,8 @@ consistently when using the call-based syntax. Example: # Error: First argument to namedtuple() should be "Point2D", not "Point" Point2D = NamedTuple("Point", [("x", int), ("y", int)]) +.. _code-literal-required: + Check that literal is used where expected [literal-required] ------------------------------------------------------------ @@ -836,6 +898,8 @@ or ``Literal`` variables. Example: # expected one of ("x", "y") [literal-required] p[key] +.. _code-no-overload-impl: + Check that overloaded functions have an implementation [no-overload-impl] ------------------------------------------------------------------------- @@ -858,6 +922,8 @@ implementation. def func(value): pass # actual implementation +.. _code-unused-coroutine: + Check that coroutine return value is used [unused-coroutine] ------------------------------------------------------------ @@ -881,6 +947,8 @@ otherwise unused variable: _ = f() # No error +.. _code-assert-type: + Check types in assert_type [assert-type] ---------------------------------------- @@ -895,6 +963,8 @@ the provided type. assert_type([1], list[str]) # Error +.. _code-truthy-function: + Check that function isn't used in boolean context [truthy-function] ------------------------------------------------------------------- @@ -908,6 +978,8 @@ Functions will always evaluate to true in boolean contexts. if f: # Error: Function "Callable[[], Any]" could always be true in boolean context [truthy-function] pass +.. _code-str-bytes-safe: + Check for implicit bytes coercions [str-bytes-safe] ------------------------------------------------------------------- @@ -926,6 +998,8 @@ Warn about cases where a bytes object may be converted to a string in an unexpec print(f"The alphabet starts with {b!r}") # The alphabet starts with b'abc' print(f"The alphabet starts with {b.decode('utf-8')}") # The alphabet starts with abc +.. _code-syntax: + Report syntax errors [syntax] ----------------------------- @@ -933,6 +1007,8 @@ If the code being checked is not syntactically valid, mypy issues a syntax error. Most, but not all, syntax errors are *blocking errors*: they can't be ignored with a ``# type: ignore`` comment. +.. _code-misc: + Miscellaneous checks [misc] --------------------------- diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 8be2ac0b1d73..11f463f93018 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -15,6 +15,8 @@ error codes that are enabled by default. options by using a :ref:`configuration file ` or :ref:`command-line options `. +.. _code-type-arg: + Check that type arguments exist [type-arg] ------------------------------------------ @@ -34,6 +36,8 @@ Example: def remove_dups(items: list) -> list: ... +.. _code-no-untyped-def: + Check that every function has an annotation [no-untyped-def] ------------------------------------------------------------ @@ -62,6 +66,8 @@ Example: def __init__(self) -> None: self.value = 0 +.. _code-redundant-cast: + Check that cast is not redundant [redundant-cast] ------------------------------------------------- @@ -82,6 +88,8 @@ Example: # Error: Redundant cast to "int" [redundant-cast] return cast(int, x) +.. _code-redundant-self: + Check that methods do not have redundant Self annotations [redundant-self] -------------------------------------------------------------------------- @@ -104,6 +112,8 @@ Example: def copy(self: Self) -> Self: return type(self)() +.. _code-comparison-overlap: + Check that comparisons are overlapping [comparison-overlap] ----------------------------------------------------------- @@ -135,6 +145,8 @@ literal: def is_magic(x: bytes) -> bool: return x == b'magic' # OK +.. _code-no-untyped-call: + Check that no untyped functions are called [no-untyped-call] ------------------------------------------------------------ @@ -154,6 +166,7 @@ Example: def bad(): ... +.. _code-no-any-return: Check that function does not return Any value [no-any-return] ------------------------------------------------------------- @@ -175,6 +188,8 @@ Example: # Error: Returning Any from function declared to return "str" [no-any-return] return fields(x)[0] +.. _code-no-any-unimported: + Check that types have no Any components due to missing imports [no-any-unimported] ---------------------------------------------------------------------------------- @@ -195,6 +210,8 @@ that ``Cat`` falls back to ``Any`` in a type annotation: def feed(cat: Cat) -> None: ... +.. _code-unreachable: + Check that statement or expression is unreachable [unreachable] --------------------------------------------------------------- @@ -214,6 +231,8 @@ incorrect control flow or conditional checks that are accidentally always true o # Error: Statement is unreachable [unreachable] print('unreachable') +.. _code-redundant-expr: + Check that expression is redundant [redundant-expr] --------------------------------------------------- @@ -236,6 +255,8 @@ mypy generates an error if it thinks that an expression is redundant. [i for i in range(x) if isinstance(i, int)] +.. _code-truthy-bool: + Check that expression is not implicitly true in boolean context [truthy-bool] ----------------------------------------------------------------------------- @@ -259,6 +280,7 @@ Using an iterable value in a boolean context has a separate error code if foo: ... +.. _code-truthy-iterable: Check that iterable is not implicitly true in boolean context [truthy-iterable] ------------------------------------------------------------------------------- @@ -286,8 +308,7 @@ items`` check is actually valid. If that is the case, it is recommended to annotate ``items`` as ``Collection[int]`` instead of ``Iterable[int]``. - -.. _ignore-without-code: +.. _code-ignore-without-code: Check that ``# type: ignore`` include an error code [ignore-without-code] ------------------------------------------------------------------------- @@ -319,6 +340,8 @@ Example: # Error: "Foo" has no attribute "nme"; maybe "name"? f.nme = 42 # type: ignore[assignment] +.. _code-unused-awaitable: + Check that awaitable return value is used [unused-awaitable] ------------------------------------------------------------ @@ -348,6 +371,8 @@ silence the error: async def g() -> None: _ = asyncio.create_task(f()) # No error +.. _code-unused-ignore: + Check that ``# type: ignore`` comment is used [unused-ignore] ------------------------------------------------------------- diff --git a/docs/source/error_codes.rst b/docs/source/error_codes.rst index c8a2728b5697..65ae0e5816e8 100644 --- a/docs/source/error_codes.rst +++ b/docs/source/error_codes.rst @@ -32,7 +32,7 @@ or config ``hide_error_codes = True`` to hide error codes. Error codes are shown prog.py:1: error: "str" has no attribute "trim" [attr-defined] It's also possible to require error codes for ``type: ignore`` comments. -See :ref:`ignore-without-code` for more information. +See :ref:`ignore-without-code` for more information. .. _silence-error-codes: diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py index 49d58dda12ec..efe1fcaa48fa 100644 --- a/docs/source/html_builder.py +++ b/docs/source/html_builder.py @@ -9,6 +9,8 @@ from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder +from mypy.errorcodes import error_codes + class MypyHTMLBuilder(StandaloneHTMLBuilder): def __init__(self, app: Sphinx) -> None: @@ -20,6 +22,23 @@ def write_doc(self, docname: str, doctree: document) -> None: self._ref_to_doc.update({_id: docname for _id in doctree.ids}) def _write_ref_redirector(self) -> None: + known_missing = { + # TODO: fix these before next release + "annotation-unchecked", + "empty-body", + "possibly-undefined", + "str-format", + "top-level-await", + } + missing_error_codes = { + c + for c in error_codes + if f"code-{c}" not in self._ref_to_doc and c not in known_missing + } + if missing_error_codes: + raise ValueError( + f"Some error codes are not documented: {', '.join(sorted(missing_error_codes))}" + ) p = Path(self.outdir) / "_refs.html" data = f""" From e14cddbe31c9437502acef022dca376a25d0b50d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 14 Jun 2023 16:18:09 +0100 Subject: [PATCH 0021/1617] Fix readthedocs build (#15437) I initially included an error (removed allowlisted error code that is not covered), to verify it will be caught. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 1 + docs/source/html_builder.py | 11 ++++++++--- tox.ini | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8851f7fbb0f3..3e06b5e909f1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,6 +20,7 @@ jobs: env: TOXENV: docs TOX_SKIP_MISSING_INTERPRETERS: False + VERIFY_MYPY_ERROR_CODES: 1 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py index efe1fcaa48fa..405b80ac53d2 100644 --- a/docs/source/html_builder.py +++ b/docs/source/html_builder.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import os import textwrap from pathlib import Path from typing import Any @@ -9,8 +10,6 @@ from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder -from mypy.errorcodes import error_codes - class MypyHTMLBuilder(StandaloneHTMLBuilder): def __init__(self, app: Sphinx) -> None: @@ -21,7 +20,9 @@ def write_doc(self, docname: str, doctree: document) -> None: super().write_doc(docname, doctree) self._ref_to_doc.update({_id: docname for _id in doctree.ids}) - def _write_ref_redirector(self) -> None: + def _verify_error_codes(self) -> None: + from mypy.errorcodes import error_codes + known_missing = { # TODO: fix these before next release "annotation-unchecked", @@ -39,6 +40,10 @@ def _write_ref_redirector(self) -> None: raise ValueError( f"Some error codes are not documented: {', '.join(sorted(missing_error_codes))}" ) + + def _write_ref_redirector(self) -> None: + if os.getenv("VERIFY_MYPY_ERROR_CODES"): + self._verify_error_codes() p = Path(self.outdir) / "_refs.html" data = f""" diff --git a/tox.ini b/tox.ini index 6d64cebaec6d..2ddda8281beb 100644 --- a/tox.ini +++ b/tox.ini @@ -33,6 +33,8 @@ commands = [testenv:docs] description = invoke sphinx-build to build the HTML docs +passenv = + VERIFY_MYPY_ERROR_CODES deps = -rdocs/requirements-docs.txt commands = sphinx-build -d "{toxworkdir}/docs_doctree" docs/source "{toxworkdir}/docs_out" --color -W -bhtml {posargs} From 66b96ed54b255495288ba539e3fd1f54f21d4abe Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:03:14 +0200 Subject: [PATCH 0022/1617] Additional validation for TypeVar defaults (PEP 696) (#15442) Check that `default` type is a subtype of `bound` or one of the constraint types. Add `default` to nodes `__match_args__`. Missed that initially. Ref: #14851 --- mypy/checkexpr.py | 9 +++++++++ mypy/nodes.py | 6 +++--- test-data/unit/check-typevar-defaults.test | 9 +++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index cd0ff1100183..4b204a80c130 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5197,6 +5197,15 @@ def visit_temp_node(self, e: TempNode) -> Type: return e.type def visit_type_var_expr(self, e: TypeVarExpr) -> Type: + p_default = get_proper_type(e.default) + if not ( + isinstance(p_default, AnyType) + and p_default.type_of_any == TypeOfAny.from_omitted_generics + ): + if not is_subtype(p_default, e.upper_bound): + self.chk.fail("TypeVar default must be a subtype of the bound type", e) + if e.values and not any(p_default == value for value in e.values): + self.chk.fail("TypeVar default must be one of the constraint types", e) return AnyType(TypeOfAny.special_form) def visit_paramspec_expr(self, e: ParamSpecExpr) -> Type: diff --git a/mypy/nodes.py b/mypy/nodes.py index 8457e39b6aa1..4e29a434d7fe 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2493,7 +2493,7 @@ class TypeVarExpr(TypeVarLikeExpr): __slots__ = ("values",) - __match_args__ = ("name", "values", "upper_bound") + __match_args__ = ("name", "values", "upper_bound", "default") # Value restriction: only types in the list are valid as values. If the # list is empty, there is no restriction. @@ -2541,7 +2541,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarExpr: class ParamSpecExpr(TypeVarLikeExpr): __slots__ = () - __match_args__ = ("name", "upper_bound") + __match_args__ = ("name", "upper_bound", "default") def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_paramspec_expr(self) @@ -2575,7 +2575,7 @@ class TypeVarTupleExpr(TypeVarLikeExpr): tuple_fallback: mypy.types.Instance - __match_args__ = ("name", "upper_bound") + __match_args__ = ("name", "upper_bound", "default") def __init__( self, diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 7bc2d4089ecd..514186aa7518 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -72,3 +72,12 @@ Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple m Ts2 = TypeVarTuple("Ts2", default=int) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts3 = TypeVarTuple("Ts3", default=Tuple[int]) # E: The default argument to TypeVarTuple must be an Unpacked tuple [builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultsInvalid2] +from typing import TypeVar, List, Union + +T1 = TypeVar("T1", bound=str, default=int) # E: TypeVar default must be a subtype of the bound type +T2 = TypeVar("T2", bound=List[str], default=List[int]) # E: TypeVar default must be a subtype of the bound type +T3 = TypeVar("T3", int, str, default=bytes) # E: TypeVar default must be one of the constraint types +T4 = TypeVar("T4", int, str, default=Union[int, str]) # E: TypeVar default must be one of the constraint types +T5 = TypeVar("T5", float, str, default=int) # E: TypeVar default must be one of the constraint types From 59a6c5e73de6b92faa2c25e39856fb43380f82ed Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Thu, 15 Jun 2023 15:58:14 +0100 Subject: [PATCH 0023/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/c38fc45d9c9340eb11e6dd279f460f8a1b3d5d7b --- mypy/typeshed/stdlib/VERSIONS | 4 +- mypy/typeshed/stdlib/_ast.pyi | 51 +++- mypy/typeshed/stdlib/_csv.pyi | 4 + mypy/typeshed/stdlib/_ctypes.pyi | 9 +- mypy/typeshed/stdlib/_socket.pyi | 25 ++ mypy/typeshed/stdlib/_thread.pyi | 3 + mypy/typeshed/stdlib/_winapi.pyi | 31 +++ mypy/typeshed/stdlib/argparse.pyi | 50 +++- mypy/typeshed/stdlib/ast.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 122 ++++++++- mypy/typeshed/stdlib/calendar.pyi | 75 +++++- mypy/typeshed/stdlib/csv.pyi | 5 + mypy/typeshed/stdlib/ctypes/__init__.pyi | 3 + mypy/typeshed/stdlib/dis.pyi | 5 +- mypy/typeshed/stdlib/distutils/__init__.pyi | 5 + mypy/typeshed/stdlib/email/generator.pyi | 8 +- mypy/typeshed/stdlib/email/policy.pyi | 3 +- mypy/typeshed/stdlib/fcntl.pyi | 5 +- mypy/typeshed/stdlib/ftplib.pyi | 15 +- mypy/typeshed/stdlib/functools.pyi | 73 +++-- mypy/typeshed/stdlib/http/__init__.pyi | 11 + mypy/typeshed/stdlib/locale.pyi | 11 +- .../stdlib/multiprocessing/connection.pyi | 7 +- .../stdlib/multiprocessing/queues.pyi | 2 + mypy/typeshed/stdlib/opcode.pyi | 12 +- mypy/typeshed/stdlib/pathlib.pyi | 28 +- mypy/typeshed/stdlib/pdb.pyi | 5 +- mypy/typeshed/stdlib/pkgutil.pyi | 13 +- mypy/typeshed/stdlib/shutil.pyi | 5 +- mypy/typeshed/stdlib/socket.pyi | 35 +++ mypy/typeshed/stdlib/socketserver.pyi | 5 + mypy/typeshed/stdlib/sys.pyi | 12 +- mypy/typeshed/stdlib/tarfile.pyi | 90 ++++++- mypy/typeshed/stdlib/tempfile.pyi | 255 ++++++++++++++---- mypy/typeshed/stdlib/threading.pyi | 7 + mypy/typeshed/stdlib/token.pyi | 9 + mypy/typeshed/stdlib/tokenize.pyi | 3 + mypy/typeshed/stdlib/types.pyi | 8 +- mypy/typeshed/stdlib/typing.pyi | 5 +- mypy/typeshed/stdlib/uuid.pyi | 3 + mypy/typeshed/stdlib/webbrowser.pyi | 11 +- mypy/typeshed/stdlib/zipimport.pyi | 6 +- mypy/typeshed/stdlib/zoneinfo/__init__.pyi | 6 +- 43 files changed, 899 insertions(+), 150 deletions(-) diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 86e8da78677c..49433e346765 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -112,7 +112,7 @@ dbm: 2.7- decimal: 2.7- difflib: 2.7- dis: 2.7- -distutils: 2.7- +distutils: 2.7-3.11 distutils.command.bdist_msi: 2.7-3.10 distutils.command.bdist_wininst: 2.7-3.9 doctest: 2.7- @@ -147,7 +147,7 @@ html: 3.0- http: 3.0- imaplib: 2.7- imghdr: 2.7- -imp: 2.7- +imp: 2.7-3.11 importlib: 2.7- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 7bc47266d713..05e2a08fdc88 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,13 +1,14 @@ import sys +import typing_extensions from typing import Any, ClassVar -from typing_extensions import Literal, TypeAlias +from typing_extensions import Literal PyCF_ONLY_AST: Literal[1024] if sys.version_info >= (3, 8): PyCF_TYPE_COMMENTS: Literal[4096] PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] -_Identifier: TypeAlias = str +_Identifier: typing_extensions.TypeAlias = str class AST: if sys.version_info >= (3, 10): @@ -59,31 +60,43 @@ class Expression(mod): class stmt(AST): ... class FunctionDef(stmt): - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") name: _Identifier args: arguments body: list[stmt] decorator_list: list[expr] returns: expr | None + if sys.version_info >= (3, 12): + type_params: list[type_param] class AsyncFunctionDef(stmt): - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") name: _Identifier args: arguments body: list[stmt] decorator_list: list[expr] returns: expr | None + if sys.version_info >= (3, 12): + type_params: list[type_param] class ClassDef(stmt): - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") + elif sys.version_info >= (3, 10): __match_args__ = ("name", "bases", "keywords", "body", "decorator_list") name: _Identifier bases: list[expr] keywords: list[keyword] body: list[stmt] decorator_list: list[expr] + if sys.version_info >= (3, 12): + type_params: list[type_param] class Return(stmt): if sys.version_info >= (3, 10): @@ -366,10 +379,10 @@ class Attribute(expr): ctx: expr_context if sys.version_info >= (3, 9): - _Slice: TypeAlias = expr + _Slice: typing_extensions.TypeAlias = expr else: class slice(AST): ... - _Slice: TypeAlias = slice + _Slice: typing_extensions.TypeAlias = slice class Slice(_Slice): if sys.version_info >= (3, 10): @@ -526,7 +539,7 @@ if sys.version_info >= (3, 10): class pattern(AST): ... # Without the alias, Pyright complains variables named pattern are recursively defined - _Pattern: TypeAlias = pattern + _Pattern: typing_extensions.TypeAlias = pattern class match_case(AST): __match_args__ = ("pattern", "guard", "body") @@ -571,3 +584,25 @@ if sys.version_info >= (3, 10): class MatchOr(pattern): __match_args__ = ("patterns",) patterns: list[pattern] + +if sys.version_info >= (3, 12): + class type_param(AST): ... + + class TypeVar(type_param): + __match_args__ = ("name", "bound") + name: _Identifier + bound: expr | None + + class ParamSpec(type_param): + __match_args__ = ("name",) + name: _Identifier + + class TypeVarTuple(type_param): + __match_args__ = ("name",) + name: _Identifier + + class TypeAlias(stmt): + __match_args__ = ("name", "typeparams", "value") + name: Name + type_params: list[type_param] + value: expr diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index c9b9f47e6217..19ea487e1530 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import SupportsWrite from collections.abc import Iterable, Iterator from typing import Any @@ -9,6 +10,9 @@ QUOTE_ALL: Literal[1] QUOTE_MINIMAL: Literal[0] QUOTE_NONE: Literal[3] QUOTE_NONNUMERIC: Literal[2] +if sys.version_info >= (3, 12): + QUOTE_STRINGS: Literal[4] + QUOTE_NOTNULL: Literal[5] # Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC` # However, using literals in situations like these can cause false-positives (see #7258) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 8e8bcbe84bd4..25d604218a00 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -22,6 +22,9 @@ RTLD_LOCAL: int if sys.version_info >= (3, 11): CTYPES_MAX_ARGCOUNT: int +if sys.version_info >= (3, 12): + SIZEOF_TIME_T: int + if sys.platform == "win32": # Description, Source, HelpFile, HelpContext, scode _COMError_Details: TypeAlias = tuple[str | None, str | None, str | None, int | None, int | None] @@ -148,7 +151,11 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index f7b0e6901bf4..7a0ede62838c 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -692,3 +692,28 @@ if sys.platform != "win32" or sys.version_info >= (3, 8): def if_nameindex() -> list[tuple[int, str]]: ... def if_nametoindex(__name: str) -> int: ... def if_indextoname(__index: int) -> str: ... + +if sys.version_info >= (3, 12): + IP_PKTINFO: int + IP_UNBLOCK_SOURCE: int + IP_BLOCK_SOURCE: int + IP_ADD_SOURCE_MEMBERSHIP: int + IP_DROP_SOURCE_MEMBERSHIP: int + if sys.platform == "win32": + AF_HYPERV: int + HV_PROTOCOL_RAW: int + HVSOCKET_CONNECT_TIMEOUT: int + HVSOCKET_CONNECT_TIMEOUT_MAX: int + HVSOCKET_CONNECTED_SUSPEND: int + HVSOCKET_ADDRESS_FLAG_PASSTHRU: int + HV_GUID_ZERO: str + HV_GUID_WILDCARD: str + HV_GUID_BROADCAST: str + HV_GUID_CHILDREN: str + HV_GUID_LOOPBACK: str + HV_GUID_PARENT: str + else: + ETHERTYPE_ARP: int + ETHERTYPE_IP: int + ETHERTYPE_IPV6: int + ETHERTYPE_VLAN: int diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 152362edcaea..dba8664fbf13 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -43,3 +43,6 @@ if sys.version_info >= (3, 8): @property def thread(self) -> Thread | None: ... _excepthook: Callable[[_ExceptHookArgs], Any] + +if sys.version_info >= (3, 12): + def daemon_threads_allowed() -> bool: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index ca1e61f0f19f..b51d844701ac 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -137,6 +137,34 @@ if sys.platform == "win32": LCMAP_TRADITIONAL_CHINESE: int LCMAP_UPPERCASE: int + if sys.version_info >= (3, 12): + COPYFILE2_CALLBACK_CHUNK_STARTED: Literal[1] + COPYFILE2_CALLBACK_CHUNK_FINISHED: Literal[2] + COPYFILE2_CALLBACK_STREAM_STARTED: Literal[3] + COPYFILE2_CALLBACK_STREAM_FINISHED: Literal[4] + COPYFILE2_CALLBACK_POLL_CONTINUE: Literal[5] + COPYFILE2_CALLBACK_ERROR: Literal[6] + + COPYFILE2_PROGRESS_CONTINUE: Literal[0] + COPYFILE2_PROGRESS_CANCEL: Literal[1] + COPYFILE2_PROGRESS_STOP: Literal[2] + COPYFILE2_PROGRESS_QUIET: Literal[3] + COPYFILE2_PROGRESS_PAUSE: Literal[4] + + COPY_FILE_FAIL_IF_EXISTS: Literal[0x1] + COPY_FILE_RESTARTABLE: Literal[0x2] + COPY_FILE_OPEN_SOURCE_FOR_WRITE: Literal[0x4] + COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Literal[0x8] + COPY_FILE_COPY_SYMLINK: Literal[0x800] + COPY_FILE_NO_BUFFERING: Literal[0x1000] + COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Literal[0x2000] + COPY_FILE_RESUME_FROM_PAUSE: Literal[0x4000] + COPY_FILE_NO_OFFLOAD: Literal[0x40000] + COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Literal[0x10000000] + + ERROR_ACCESS_DENIED: Literal[5] + ERROR_PRIVILEGE_NOT_HELD: Literal[1314] + def CloseHandle(__handle: int) -> None: ... @overload def ConnectNamedPipe(handle: int, overlapped: Literal[True]) -> Overlapped: ... @@ -224,3 +252,6 @@ if sys.platform == "win32": def GetOverlappedResult(self, __wait: bool) -> tuple[int, int]: ... def cancel(self) -> None: ... def getbuffer(self) -> bytes | None: ... + + if sys.version_info >= (3, 12): + def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index c986b9cdb082..e41048516dd9 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -97,8 +97,16 @@ class _ActionsContainer: version: str = ..., **kwargs: Any, ) -> Action: ... - def add_argument_group(self, *args: Any, **kwargs: Any) -> _ArgumentGroup: ... - def add_mutually_exclusive_group(self, **kwargs: Any) -> _MutuallyExclusiveGroup: ... + def add_argument_group( + self, + title: str | None = None, + description: str | None = None, + *, + prefix_chars: str = ..., + argument_default: Any = ..., + conflict_handler: str = ..., + ) -> _ArgumentGroup: ... + def add_mutually_exclusive_group(self, *, required: bool = False) -> _MutuallyExclusiveGroup: ... def _add_action(self, action: _ActionT) -> _ActionT: ... def _remove_action(self, action: Action) -> None: ... def _add_container_actions(self, container: _ActionsContainer) -> None: ... @@ -161,9 +169,9 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): add_help: bool = True, allow_abbrev: bool = True, ) -> None: ... - # Ignore errors about overlapping overloads + @overload - def parse_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... # type: ignore[misc] + def parse_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> Namespace: ... # type: ignore[misc] @overload def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... @overload @@ -201,16 +209,27 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def print_help(self, file: IO[str] | None = None) -> None: ... def format_usage(self) -> str: ... def format_help(self) -> str: ... - def parse_known_args( - self, args: Sequence[str] | None = None, namespace: Namespace | None = None - ) -> tuple[Namespace, list[str]]: ... + @overload + def parse_known_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + @overload + def parse_known_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... + @overload + def parse_known_args(self, *, namespace: _N) -> tuple[_N, list[str]]: ... def convert_arg_line_to_args(self, arg_line: str) -> list[str]: ... def exit(self, status: int = 0, message: str | None = None) -> NoReturn: ... def error(self, message: str) -> NoReturn: ... - def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> Namespace: ... - def parse_known_intermixed_args( - self, args: Sequence[str] | None = None, namespace: Namespace | None = None - ) -> tuple[Namespace, list[str]]: ... + @overload + def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> Namespace: ... # type: ignore[misc] + @overload + def parse_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... + @overload + def parse_intermixed_args(self, *, namespace: _N) -> _N: ... + @overload + def parse_known_intermixed_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + @overload + def parse_known_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... + @overload + def parse_known_intermixed_args(self, *, namespace: _N) -> tuple[_N, list[str]]: ... # undocumented def _get_optional_actions(self) -> list[Action]: ... def _get_positional_actions(self) -> list[Action]: ... @@ -350,7 +369,14 @@ class _ArgumentGroup(_ActionsContainer): title: str | None _group_actions: list[Action] def __init__( - self, container: _ActionsContainer, title: str | None = None, description: str | None = None, **kwargs: Any + self, + container: _ActionsContainer, + title: str | None = None, + description: str | None = None, + *, + prefix_chars: str = ..., + argument_default: Any = ..., + conflict_handler: str = ..., ) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index ea899e150f97..377138141340 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -3,7 +3,7 @@ import sys from _ast import * from _typeshed import ReadableBuffer, Unused from collections.abc import Iterator -from typing import Any, TypeVar, overload +from typing import Any, TypeVar as _TypeVar, overload from typing_extensions import Literal if sys.version_info >= (3, 8): @@ -168,7 +168,7 @@ class NodeTransformer(NodeVisitor): # The usual return type is AST | None, but Iterable[AST] # is also allowed in some cases -- this needs to be mapped. -_T = TypeVar("_T", bound=AST) +_T = _TypeVar("_T", bound=AST) if sys.version_info >= (3, 8): @overload diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 0676aba1277e..b3871776128a 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -53,7 +53,18 @@ from typing import ( # noqa: Y022 overload, type_check_only, ) -from typing_extensions import Concatenate, Literal, ParamSpec, Self, SupportsIndex, TypeAlias, TypeGuard, final +from typing_extensions import ( # type: ignore + Concatenate, + Literal, + LiteralString, + ParamSpec, + Self, + SupportsIndex, + TypeAlias, + TypeGuard, + TypeVarTuple, + final, +) if sys.version_info >= (3, 9): from types import GenericAlias @@ -187,6 +198,8 @@ class type: if sys.version_info >= (3, 10): def __or__(self, __value: Any) -> types.UnionType: ... def __ror__(self, __value: Any) -> types.UnionType: ... + if sys.version_info >= (3, 12): + __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] class super: @overload @@ -244,6 +257,9 @@ class int: signed: bool = False, ) -> Self: ... + if sys.version_info >= (3, 12): + def is_integer(self) -> Literal[True]: ... + def __add__(self, __value: int) -> int: ... def __sub__(self, __value: int) -> int: ... def __mul__(self, __value: int) -> int: ... @@ -417,8 +433,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -426,12 +451,21 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: + @overload + def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - def format(self, *args: object, **kwargs: object) -> str: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload + def format(self, *args: object, **kwargs: object) -> str: ... # type: ignore def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def isalnum(self) -> bool: ... @@ -446,32 +480,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -482,6 +575,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -489,13 +585,25 @@ class str(Sequence[str]): def __ge__(self, __value: str) -> bool: ... def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - def __mod__(self, __value: Any) -> str: ... + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload + def __mod__(self, __value: Any) -> str: ... # type: ignore + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -874,6 +982,8 @@ class function: if sys.version_info >= (3, 10): @property def __builtins__(self) -> dict[str, Any]: ... + if sys.version_info >= (3, 12): + __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any. @@ -1648,11 +1758,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... @@ -1826,6 +1936,8 @@ class ImportError(Exception): name: str | None path: str | None msg: str # undocumented + if sys.version_info >= (3, 12): + name_from: str | None # undocumented class LookupError(Exception): ... class MemoryError(Exception): ... diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 255a12d3348a..3f881393e99f 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -1,4 +1,5 @@ import datetime +import enum import sys from _typeshed import Unused from collections.abc import Iterable, Sequence @@ -35,6 +36,23 @@ __all__ = [ if sys.version_info >= (3, 10): __all__ += ["FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"] +if sys.version_info >= (3, 12): + __all__ += [ + "Day", + "Month", + "JANUARY", + "FEBRUARY", + "MARCH", + "APRIL", + "MAY", + "JUNE", + "JULY", + "AUGUST", + "SEPTEMBER", + "OCTOBER", + "NOVEMBER", + "DECEMBER", + ] _LocaleType: TypeAlias = tuple[str | None, str | None] @@ -134,12 +152,55 @@ day_abbr: Sequence[str] month_name: Sequence[str] month_abbr: Sequence[str] -MONDAY: Literal[0] -TUESDAY: Literal[1] -WEDNESDAY: Literal[2] -THURSDAY: Literal[3] -FRIDAY: Literal[4] -SATURDAY: Literal[5] -SUNDAY: Literal[6] +if sys.version_info >= (3, 12): + class Month(enum.IntEnum): + JANUARY: Literal[1] + FEBRUARY: Literal[2] + MARCH: Literal[3] + APRIL: Literal[4] + MAY: Literal[5] + JUNE: Literal[6] + JULY: Literal[7] + AUGUST: Literal[8] + SEPTEMBER: Literal[9] + OCTOBER: Literal[10] + NOVEMBER: Literal[11] + DECEMBER: Literal[12] + JANUARY = Month.JANUARY + FEBRUARY = Month.FEBRUARY + MARCH = Month.MARCH + APRIL = Month.APRIL + MAY = Month.MAY + JUNE = Month.JUNE + JULY = Month.JULY + AUGUST = Month.AUGUST + SEPTEMBER = Month.SEPTEMBER + OCTOBER = Month.OCTOBER + NOVEMBER = Month.NOVEMBER + DECEMBER = Month.DECEMBER + + class Day(enum.IntEnum): + MONDAY: Literal[0] + TUESDAY: Literal[1] + WEDNESDAY: Literal[2] + THURSDAY: Literal[3] + FRIDAY: Literal[4] + SATURDAY: Literal[5] + SUNDAY: Literal[6] + MONDAY = Day.MONDAY + TUESDAY = Day.TUESDAY + WEDNESDAY = Day.WEDNESDAY + THURSDAY = Day.THURSDAY + FRIDAY = Day.FRIDAY + SATURDAY = Day.SATURDAY + SUNDAY = Day.SUNDAY +else: + MONDAY: Literal[0] + TUESDAY: Literal[1] + WEDNESDAY: Literal[2] + THURSDAY: Literal[3] + FRIDAY: Literal[4] + SATURDAY: Literal[5] + SUNDAY: Literal[6] EPOCH: Literal[1970] diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 59f2e7a3c96b..139ba7af2208 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -21,6 +21,9 @@ from _csv import ( unregister_dialect as unregister_dialect, writer as writer, ) + +if sys.version_info >= (3, 12): + from _csv import QUOTE_STRINGS as QUOTE_STRINGS, QUOTE_NOTNULL as QUOTE_NOTNULL from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence from typing import Any, Generic, TypeVar, overload @@ -57,6 +60,8 @@ __all__ = [ "DictWriter", "unix_dialect", ] +if sys.version_info >= (3, 12): + __all__ += ["QUOTE_STRINGS", "QUOTE_NOTNULL"] _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 7a185a5b523e..b14fb93c8163 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -181,6 +181,9 @@ class c_bool(_SimpleCData[bool]): if sys.platform == "win32": class HRESULT(_SimpleCData[int]): ... # TODO undocumented +if sys.version_info >= (3, 12): + c_time_t: type[c_int32 | c_int64] + class py_object(_CanCastTo, _SimpleCData[_T]): ... class BigEndianStructure(Structure): ... class LittleEndianStructure(Structure): ... diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index d153771e676b..ab101a517a6f 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -29,9 +29,12 @@ __all__ = [ "opmap", "HAVE_ARGUMENT", "EXTENDED_ARG", - "hasnargs", "stack_effect", ] +if sys.version_info >= (3, 12): + __all__ += ["hasarg", "hasexc"] +else: + __all__ += ["hasnargs"] # Strictly this should not have to include Callable, but mypy doesn't use FunctionType # for functions (python/mypy#3171) diff --git a/mypy/typeshed/stdlib/distutils/__init__.pyi b/mypy/typeshed/stdlib/distutils/__init__.pyi index e69de29bb2d1..328a5b783441 100644 --- a/mypy/typeshed/stdlib/distutils/__init__.pyi +++ b/mypy/typeshed/stdlib/distutils/__init__.pyi @@ -0,0 +1,5 @@ +# Attempts to improve these stubs are probably not the best use of time: +# - distutils is deleted in Python 3.12 and newer +# - Most users already do not use stdlib distutils, due to setuptools monkeypatching +# - We have very little quality assurance on these stubs, since due to the two above issues +# we allowlist all distutils errors in stubtest. diff --git a/mypy/typeshed/stdlib/email/generator.pyi b/mypy/typeshed/stdlib/email/generator.pyi index 8362dd9c4ff6..faa6551fc925 100644 --- a/mypy/typeshed/stdlib/email/generator.pyi +++ b/mypy/typeshed/stdlib/email/generator.pyi @@ -1,11 +1,12 @@ from _typeshed import SupportsWrite from email.message import Message from email.policy import Policy +from typing_extensions import Self __all__ = ["Generator", "DecodedGenerator", "BytesGenerator"] class Generator: - def clone(self, fp: SupportsWrite[str]) -> Generator: ... + def clone(self, fp: SupportsWrite[str]) -> Self: ... def write(self, s: str) -> None: ... def __init__( self, @@ -17,9 +18,7 @@ class Generator: ) -> None: ... def flatten(self, msg: Message, unixfrom: bool = False, linesep: str | None = None) -> None: ... -class BytesGenerator: - def clone(self, fp: SupportsWrite[bytes]) -> BytesGenerator: ... - def write(self, s: str) -> None: ... +class BytesGenerator(Generator): def __init__( self, outfp: SupportsWrite[bytes], @@ -28,7 +27,6 @@ class BytesGenerator: *, policy: Policy | None = None, ) -> None: ... - def flatten(self, msg: Message, unixfrom: bool = False, linesep: str | None = None) -> None: ... class DecodedGenerator(Generator): def __init__( diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index 4df3c1e48b07..dc7f18489bfa 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -5,6 +5,7 @@ from email.errors import MessageDefect from email.header import Header from email.message import Message from typing import Any +from typing_extensions import Self __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] @@ -25,7 +26,7 @@ class Policy(metaclass=ABCMeta): mangle_from_: bool = ..., message_factory: Callable[[Policy], Message] | None = ..., ) -> None: ... - def clone(self, **kw: Any) -> Policy: ... + def clone(self, **kw: Any) -> Self: ... def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... def header_max_count(self, name: str) -> int | None: ... diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 01443083f48d..6aec7515f330 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -20,6 +20,9 @@ if sys.platform != "win32": F_SETOWN: int F_UNLCK: int F_WRLCK: int + + F_GETLEASE: int + F_SETLEASE: int if sys.platform == "darwin": F_FULLFSYNC: int F_NOCACHE: int @@ -30,11 +33,9 @@ if sys.platform != "win32": F_SETSIG: int F_SHLCK: int F_SETLK64: int - F_SETLEASE: int F_GETSIG: int F_NOTIFY: int F_EXLCK: int - F_GETLEASE: int F_GETLK64: int if sys.version_info >= (3, 8): F_ADD_SEALS: int diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 36a213d48680..f24d14fbf2b6 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -118,7 +118,20 @@ class FTP: def close(self) -> None: ... class FTP_TLS(FTP): - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 12): + def __init__( + self, + host: str = "", + user: str = "", + passwd: str = "", + acct: str = "", + *, + context: SSLContext | None = None, + timeout: float = ..., + source_address: tuple[str, int] | None = None, + encoding: str = "utf-8", + ) -> None: ... + elif sys.version_info >= (3, 9): def __init__( self, host: str = "", diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index d01fd8ce55cb..8adc3d82292e 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,10 +28,12 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -70,22 +72,57 @@ if sys.version_info >= (3, 8): else: def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... -WRAPPER_ASSIGNMENTS: tuple[ - Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"] -] +if sys.version_info >= (3, 12): + WRAPPER_ASSIGNMENTS: tuple[ + Literal["__module__"], + Literal["__name__"], + Literal["__qualname__"], + Literal["__doc__"], + Literal["__annotations__"], + Literal["__type_params__"], + ] +else: + WRAPPER_ASSIGNMENTS: tuple[ + Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"] + ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), -) -> _T: ... -def wraps( - wrapped: _AnyCallable, - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), -) -> IdentityFunction: ... +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + +if sys.version_info >= (3, 12): + def update_wrapper( + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Sequence[str] = ("__dict__",), + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + def wraps( + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Sequence[str] = ("__dict__",), + ) -> _Wrapper[_PWrapped, _RWrapped]: ... + +else: + def update_wrapper( + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Sequence[str] = ("__dict__",), + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + def wraps( + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Sequence[str] = ("__dict__",), + ) -> _Wrapper[_PWrapped, _RWrapped]: ... + def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index d4b44f2eb99b..4310c79b9269 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -79,6 +79,17 @@ class HTTPStatus(IntEnum): EARLY_HINTS: Literal[103] IM_A_TEAPOT: Literal[418] TOO_EARLY: Literal[425] + if sys.version_info >= (3, 12): + @property + def is_informational(self) -> bool: ... + @property + def is_success(self) -> bool: ... + @property + def is_redirection(self) -> bool: ... + @property + def is_client_error(self) -> bool: ... + @property + def is_server_error(self) -> bool: ... if sys.version_info >= (3, 11): class HTTPMethod(StrEnum): diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index c6cc7cacfb1d..3753700ea889 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -15,7 +15,6 @@ __all__ = [ "str", "atof", "atoi", - "format", "format_string", "currency", "normalize", @@ -32,6 +31,9 @@ __all__ = [ if sys.version_info >= (3, 11): __all__ += ["getencoding"] +if sys.version_info < (3, 12): + __all__ += ["format"] + # This module defines a function "str()", which is why "str" can't be used # as a type annotation or type alias. from builtins import str as _str @@ -123,7 +125,12 @@ def normalize(localename: _str) -> _str: ... def resetlocale(category: int = ...) -> None: ... def strcoll(__os1: _str, __os2: _str) -> int: ... def strxfrm(__string: _str) -> _str: ... -def format(percent: _str, value: float | Decimal, grouping: bool = False, monetary: bool = False, *additional: Any) -> _str: ... + +if sys.version_info < (3, 12): + def format( + percent: _str, value: float | Decimal, grouping: bool = False, monetary: bool = False, *additional: Any + ) -> _str: ... + def format_string(f: _str, val: Any, grouping: bool = False, monetary: bool = False) -> _str: ... def currency(val: float | Decimal, symbol: bool = True, grouping: bool = False, international: bool = False) -> _str: ... def delocalize(string: _str) -> _str: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index d034373712e0..28696fe6a3a3 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -52,7 +52,12 @@ class Listener: self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... -def deliver_challenge(connection: Connection, authkey: bytes) -> None: ... +if sys.version_info >= (3, 12): + def deliver_challenge(connection: Connection, authkey: bytes, digest_name: str = "sha256") -> None: ... + +else: + def deliver_challenge(connection: Connection, authkey: bytes) -> None: ... + def answer_challenge(connection: Connection, authkey: bytes) -> None: ... def wait( object_list: Iterable[Connection | socket.socket | int], timeout: float | None = None diff --git a/mypy/typeshed/stdlib/multiprocessing/queues.pyi b/mypy/typeshed/stdlib/multiprocessing/queues.pyi index a26ab7173232..8e72d15f25f6 100644 --- a/mypy/typeshed/stdlib/multiprocessing/queues.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/queues.pyi @@ -22,6 +22,8 @@ class Queue(Generic[_T]): def close(self) -> None: ... def join_thread(self) -> None: ... def cancel_join_thread(self) -> None: ... + if sys.version_info >= (3, 12): + def __class_getitem__(cls, __item: Any) -> GenericAlias: ... class JoinableQueue(Queue[_T]): def task_done(self) -> None: ... diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi index 1232454e71ea..f852489ffacf 100644 --- a/mypy/typeshed/stdlib/opcode.pyi +++ b/mypy/typeshed/stdlib/opcode.pyi @@ -14,9 +14,12 @@ __all__ = [ "opmap", "HAVE_ARGUMENT", "EXTENDED_ARG", - "hasnargs", "stack_effect", ] +if sys.version_info >= (3, 12): + __all__ += ["hasarg", "hasexc"] +else: + __all__ += ["hasnargs"] if sys.version_info >= (3, 9): cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]] @@ -42,6 +45,11 @@ hasjabs: list[int] haslocal: list[int] hascompare: list[int] hasfree: list[int] +if sys.version_info >= (3, 12): + hasarg: list[int] + hasexc: list[int] +else: + hasnargs: list[int] opname: list[str] opmap: dict[str, int] @@ -53,5 +61,3 @@ if sys.version_info >= (3, 8): else: def stack_effect(__opcode: int, __oparg: int | None = None) -> int: ... - -hasnargs: list[int] diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 7aec66b584e3..3c2ae0fe7ab1 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -55,7 +55,11 @@ class PurePath(PathLike[str]): if sys.version_info >= (3, 9): def is_relative_to(self, *other: StrPath) -> bool: ... - def match(self, path_pattern: str) -> bool: ... + if sys.version_info >= (3, 12): + def match(self, path_pattern: str, *, case_sensitive: bool | None = None) -> bool: ... + else: + def match(self, path_pattern: str) -> bool: ... + def relative_to(self, *other: StrPath) -> Self: ... def with_name(self, name: str) -> Self: ... if sys.version_info >= (3, 9): @@ -70,6 +74,9 @@ class PurePath(PathLike[str]): if sys.version_info >= (3, 9) and sys.version_info < (3, 11): def __class_getitem__(cls, type: Any) -> GenericAlias: ... + if sys.version_info >= (3, 12): + def with_segments(self, *args: StrPath) -> Self: ... + class PurePosixPath(PurePath): ... class PureWindowsPath(PurePath): ... @@ -86,8 +93,15 @@ class Path(PurePath): def stat(self) -> stat_result: ... def chmod(self, mode: int) -> None: ... - def exists(self) -> bool: ... - def glob(self, pattern: str) -> Generator[Self, None, None]: ... + if sys.version_info >= (3, 12): + def exists(self, *, follow_symlinks: bool = True) -> bool: ... + def glob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... + def rglob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... + else: + def exists(self) -> bool: ... + def glob(self, pattern: str) -> Generator[Self, None, None]: ... + def rglob(self, pattern: str) -> Generator[Self, None, None]: ... + def is_dir(self) -> bool: ... def is_file(self) -> bool: ... def is_symlink(self) -> bool: ... @@ -95,6 +109,9 @@ class Path(PurePath): def is_fifo(self) -> bool: ... def is_block_device(self) -> bool: ... def is_char_device(self) -> bool: ... + if sys.version_info >= (3, 12): + def is_junction(self) -> bool: ... + def iterdir(self) -> Generator[Self, None, None]: ... def lchmod(self, mode: int) -> None: ... def lstat(self) -> stat_result: ... @@ -159,6 +176,10 @@ class Path(PurePath): # so it's safer to pretend they don't exist def owner(self) -> str: ... def group(self) -> str: ... + + # This method does "exist" on Windows on <3.12, but always raises NotImplementedError + # On py312+, it works properly on Windows, as with all other platforms + if sys.platform != "win32" or sys.version_info >= (3, 12): def is_mount(self) -> bool: ... if sys.version_info >= (3, 9): @@ -171,7 +192,6 @@ class Path(PurePath): def replace(self, target: str | PurePath) -> None: ... def resolve(self, strict: bool = False) -> Self: ... - def rglob(self, pattern: str) -> Generator[Self, None, None]: ... def rmdir(self) -> None: ... def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index 405c45ca01ac..e0d69e7d30fa 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -168,7 +168,10 @@ class Pdb(Bdb, Cmd): def find_function(funcname: str, filename: str) -> tuple[str, str, int] | None: ... def main() -> None: ... def help() -> None: ... -def getsourcelines(obj: _SourceObjectType) -> tuple[list[str], int]: ... + +if sys.version_info < (3, 10): + def getsourcelines(obj: _SourceObjectType) -> tuple[list[str], int]: ... + def lasti2lineno(code: CodeType, lasti: int) -> int: ... class _rstr(str): diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi index f9808c9e5de8..59f1f734cf90 100644 --- a/mypy/typeshed/stdlib/pkgutil.pyi +++ b/mypy/typeshed/stdlib/pkgutil.pyi @@ -12,12 +12,12 @@ __all__ = [ "walk_packages", "iter_modules", "get_data", - "ImpImporter", - "ImpLoader", "read_code", "extend_path", "ModuleInfo", ] +if sys.version_info < (3, 12): + __all__ += ["ImpImporter", "ImpLoader"] _PathT = TypeVar("_PathT", bound=Iterable[str]) @@ -28,11 +28,12 @@ class ModuleInfo(NamedTuple): def extend_path(path: _PathT, name: str) -> _PathT: ... -class ImpImporter: - def __init__(self, path: str | None = None) -> None: ... +if sys.version_info < (3, 12): + class ImpImporter: + def __init__(self, path: str | None = None) -> None: ... -class ImpLoader: - def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ... + class ImpLoader: + def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ... def find_loader(fullname: str) -> Loader | None: ... def get_importer(path_item: str) -> PathEntryFinder | None: ... diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index ef716d4049dd..38c50d51b129 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -2,6 +2,7 @@ import os import sys from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite from collections.abc import Callable, Iterable, Sequence +from tarfile import _TarfileFilter from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload from typing_extensions import TypeAlias @@ -192,9 +193,9 @@ def register_archive_format( ) -> None: ... def unregister_archive_format(name: str) -> None: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 8): def unpack_archive( - filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: str | None = None + filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: _TarfileFilter | None = None ) -> None: ... else: diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index 6c897b919909..e1ffc573b52e 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -438,6 +438,36 @@ if sys.platform == "win32": SIO_LOOPBACK_FAST_PATH as SIO_LOOPBACK_FAST_PATH, SIO_RCVALL as SIO_RCVALL, ) +if sys.version_info >= (3, 12): + from _socket import ( + IP_ADD_SOURCE_MEMBERSHIP as IP_ADD_SOURCE_MEMBERSHIP, + IP_BLOCK_SOURCE as IP_BLOCK_SOURCE, + IP_DROP_SOURCE_MEMBERSHIP as IP_DROP_SOURCE_MEMBERSHIP, + IP_PKTINFO as IP_PKTINFO, + IP_UNBLOCK_SOURCE as IP_UNBLOCK_SOURCE, + ) + + if sys.platform == "win32": + from _socket import ( + HV_GUID_BROADCAST as HV_GUID_BROADCAST, + HV_GUID_CHILDREN as HV_GUID_CHILDREN, + HV_GUID_LOOPBACK as HV_GUID_LOOPBACK, + HV_GUID_PARENT as HV_GUID_PARENT, + HV_GUID_WILDCARD as HV_GUID_WILDCARD, + HV_GUID_ZERO as HV_GUID_ZERO, + HV_PROTOCOL_RAW as HV_PROTOCOL_RAW, + HVSOCKET_ADDRESS_FLAG_PASSTHRU as HVSOCKET_ADDRESS_FLAG_PASSTHRU, + HVSOCKET_CONNECT_TIMEOUT as HVSOCKET_CONNECT_TIMEOUT, + HVSOCKET_CONNECT_TIMEOUT_MAX as HVSOCKET_CONNECT_TIMEOUT_MAX, + HVSOCKET_CONNECTED_SUSPEND as HVSOCKET_CONNECTED_SUSPEND, + ) + else: + from _socket import ( + ETHERTYPE_ARP as ETHERTYPE_ARP, + ETHERTYPE_IP as ETHERTYPE_IP, + ETHERTYPE_IPV6 as ETHERTYPE_IPV6, + ETHERTYPE_VLAN as ETHERTYPE_VLAN, + ) # Re-exported from errno EBADF: int @@ -489,6 +519,8 @@ class AddressFamily(IntEnum): AF_LINK: int if sys.platform != "darwin": AF_BLUETOOTH: int + if sys.platform == "win32" and sys.version_info >= (3, 12): + AF_HYPERV: int AF_INET = AddressFamily.AF_INET AF_INET6 = AddressFamily.AF_INET6 @@ -540,6 +572,9 @@ if sys.platform != "win32" or sys.version_info >= (3, 9): if sys.platform != "darwin": AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH +if sys.platform == "win32" and sys.version_info >= (3, 12): + AF_HYPERV = AddressFamily.AF_HYPERV + class SocketKind(IntEnum): SOCK_STREAM: int SOCK_DGRAM: int diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index 3799d82a0065..6a932f66cd09 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -28,6 +28,8 @@ if sys.platform != "win32": "UnixDatagramServer", "UnixStreamServer", ] + if sys.version_info >= (3, 12): + __all__ += ["ForkingUnixStreamServer", "ForkingUnixDatagramServer"] _RequestType: TypeAlias = _socket | tuple[bytes, _socket] _AfUnixAddress: TypeAlias = str | ReadableBuffer # address acceptable for an AF_UNIX socket @@ -124,6 +126,9 @@ class ThreadingMixIn: if sys.platform != "win32": class ForkingTCPServer(ForkingMixIn, TCPServer): ... class ForkingUDPServer(ForkingMixIn, UDPServer): ... + if sys.version_info >= (3, 12): + class ForkingUnixStreamServer(ForkingMixIn, UnixStreamServer): ... + class ForkingUnixDatagramServer(ForkingMixIn, UnixDatagramServer): ... class ThreadingTCPServer(ThreadingMixIn, TCPServer): ... class ThreadingUDPServer(ThreadingMixIn, UDPServer): ... diff --git a/mypy/typeshed/stdlib/sys.pyi b/mypy/typeshed/stdlib/sys.pyi index c2fdbeccca72..ca049124053a 100644 --- a/mypy/typeshed/stdlib/sys.pyi +++ b/mypy/typeshed/stdlib/sys.pyi @@ -321,7 +321,7 @@ if sys.version_info < (3, 9): if sys.version_info >= (3, 8): # Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily. - class UnraisableHookArgs: + class UnraisableHookArgs(Protocol): exc_type: type[BaseException] exc_value: BaseException | None exc_traceback: TracebackType | None @@ -359,3 +359,13 @@ if sys.version_info < (3, 8): # as part of the response to CVE-2020-10735 def set_int_max_str_digits(maxdigits: int) -> None: ... def get_int_max_str_digits() -> int: ... + +if sys.version_info >= (3, 12): + def getunicodeinternedsize() -> int: ... + def deactivate_stack_trampoline() -> None: ... + def is_stack_trampoline_active() -> bool: ... + # It always exists, but raises on non-linux platforms: + if sys.platform == "linux": + def activate_stack_trampoline(__backend: str) -> None: ... + else: + def activate_stack_trampoline(__backend: str) -> NoReturn: ... diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 5cf1d55cac63..d9d9641ac698 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -7,7 +7,7 @@ from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj from types import TracebackType from typing import IO, ClassVar, Protocol, overload -from typing_extensions import Literal, Self +from typing_extensions import Literal, Self, TypeAlias __all__ = [ "TarFile", @@ -26,6 +26,21 @@ __all__ = [ "DEFAULT_FORMAT", "open", ] +if sys.version_info >= (3, 12): + __all__ += [ + "fully_trusted_filter", + "data_filter", + "tar_filter", + "FilterError", + "AbsoluteLinkError", + "OutsideDestinationError", + "SpecialFileError", + "AbsolutePathError", + "LinkOutsideDestinationError", + ] + +_FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None] +_TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction class _Fileobj(Protocol): def read(self, __size: int) -> bytes: ... @@ -125,6 +140,7 @@ class TarFile: debug: int | None errorlevel: int | None offset: int # undocumented + extraction_filter: _FilterFunction | None def __init__( self, name: StrOrBytesPath | None = None, @@ -275,12 +291,32 @@ class TarFile: def getnames(self) -> _list[str]: ... def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ... def next(self) -> TarInfo | None: ... - def extractall( - self, path: StrOrBytesPath = ".", members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False - ) -> None: ... - def extract( - self, member: str | TarInfo, path: StrOrBytesPath = "", set_attrs: bool = True, *, numeric_owner: bool = False - ) -> None: ... + if sys.version_info >= (3, 8): + def extractall( + self, + path: StrOrBytesPath = ".", + members: Iterable[TarInfo] | None = None, + *, + numeric_owner: bool = False, + filter: _TarfileFilter | None = ..., + ) -> None: ... + def extract( + self, + member: str | TarInfo, + path: StrOrBytesPath = "", + set_attrs: bool = True, + *, + numeric_owner: bool = False, + filter: _TarfileFilter | None = ..., + ) -> None: ... + else: + def extractall( + self, path: StrOrBytesPath = ".", members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False + ) -> None: ... + def extract( + self, member: str | TarInfo, path: StrOrBytesPath = "", set_attrs: bool = True, *, numeric_owner: bool = False + ) -> None: ... + def _extract_member( self, tarinfo: TarInfo, targetpath: str, set_attrs: bool = True, numeric_owner: bool = False ) -> None: ... # undocumented @@ -324,6 +360,31 @@ class StreamError(TarError): ... class ExtractError(TarError): ... class HeaderError(TarError): ... +if sys.version_info >= (3, 8): + class FilterError(TarError): + # This attribute is only set directly on the subclasses, but the documentation guarantees + # that it is always present on FilterError. + tarinfo: TarInfo + + class AbsolutePathError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... + + class OutsideDestinationError(FilterError): + def __init__(self, tarinfo: TarInfo, path: str) -> None: ... + + class SpecialFileError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... + + class AbsoluteLinkError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... + + class LinkOutsideDestinationError(FilterError): + def __init__(self, tarinfo: TarInfo, path: str) -> None: ... + + def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ... + def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ... + def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ... + class TarInfo: name: str path: str @@ -353,6 +414,21 @@ class TarInfo: def linkpath(self) -> str: ... @linkpath.setter def linkpath(self, linkname: str) -> None: ... + if sys.version_info >= (3, 8): + def replace( + self, + *, + name: str = ..., + mtime: int = ..., + mode: int = ..., + linkname: str = ..., + uid: int = ..., + gid: int = ..., + uname: str = ..., + gname: str = ..., + deep: bool = True, + ) -> Self: ... + def get_info(self) -> Mapping[str, str | int | bytes | Mapping[str, str]]: ... if sys.version_info >= (3, 8): def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index cd27e91fbc75..b251f8b9d029 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -1,10 +1,21 @@ import io import sys -from _typeshed import BytesPath, GenericPath, ReadableBuffer, StrPath, WriteableBuffer +from _typeshed import ( + BytesPath, + GenericPath, + OpenBinaryMode, + OpenBinaryModeReading, + OpenBinaryModeUpdating, + OpenBinaryModeWriting, + OpenTextMode, + ReadableBuffer, + StrPath, + WriteableBuffer, +) from collections.abc import Iterable, Iterator from types import TracebackType from typing import IO, Any, AnyStr, Generic, overload -from typing_extensions import Literal, Self, TypeAlias +from typing_extensions import Literal, Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -30,13 +41,54 @@ TMP_MAX: int tempdir: str | None template: str -_StrMode: TypeAlias = Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+", "rt", "wt", "at", "xt", "r+t", "w+t", "a+t", "x+t"] -_BytesMode: TypeAlias = Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"] +if sys.version_info >= (3, 12): + @overload + def NamedTemporaryFile( + mode: OpenTextMode, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> _TemporaryFileWrapper[str]: ... + @overload + def NamedTemporaryFile( + mode: OpenBinaryMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> _TemporaryFileWrapper[bytes]: ... + @overload + def NamedTemporaryFile( + mode: str = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> _TemporaryFileWrapper[Any]: ... -if sys.version_info >= (3, 8): +elif sys.version_info >= (3, 8): @overload def NamedTemporaryFile( - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -49,7 +101,7 @@ if sys.version_info >= (3, 8): ) -> _TemporaryFileWrapper[str]: ... @overload def NamedTemporaryFile( - mode: _BytesMode = "w+b", + mode: OpenBinaryMode = "w+b", buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -77,7 +129,7 @@ if sys.version_info >= (3, 8): else: @overload def NamedTemporaryFile( - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -88,7 +140,7 @@ else: ) -> _TemporaryFileWrapper[str]: ... @overload def NamedTemporaryFile( - mode: _BytesMode = "w+b", + mode: OpenBinaryMode = "w+b", buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -112,10 +164,11 @@ else: if sys.platform == "win32": TemporaryFile = NamedTemporaryFile else: + # See the comments for builtins.open() for an explanation of the overloads. if sys.version_info >= (3, 8): @overload def TemporaryFile( - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -124,11 +177,11 @@ else: dir: GenericPath[AnyStr] | None = None, *, errors: str | None = None, - ) -> IO[str]: ... + ) -> io.TextIOWrapper: ... @overload def TemporaryFile( - mode: _BytesMode = "w+b", - buffering: int = -1, + mode: OpenBinaryMode, + buffering: Literal[0], encoding: str | None = None, newline: str | None = None, suffix: AnyStr | None = None, @@ -136,7 +189,54 @@ else: dir: GenericPath[AnyStr] | None = None, *, errors: str | None = None, - ) -> IO[bytes]: ... + ) -> io.FileIO: ... + @overload + def TemporaryFile( + *, + buffering: Literal[0], + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + errors: str | None = None, + ) -> io.FileIO: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeWriting, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedWriter: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeReading, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedReader: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeUpdating = "w+b", + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedRandom: ... @overload def TemporaryFile( mode: str = "w+b", @@ -152,40 +252,84 @@ else: else: @overload def TemporaryFile( - mode: _StrMode, - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: AnyStr | None = ..., - prefix: AnyStr | None = ..., - dir: GenericPath[AnyStr] | None = ..., - ) -> IO[str]: ... + mode: OpenTextMode, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.TextIOWrapper: ... + @overload + def TemporaryFile( + mode: OpenBinaryMode, + buffering: Literal[0], + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.FileIO: ... + @overload + def TemporaryFile( + *, + buffering: Literal[0], + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.FileIO: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeUpdating = "w+b", + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.BufferedRandom: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeWriting, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.BufferedWriter: ... @overload def TemporaryFile( - mode: _BytesMode = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: AnyStr | None = ..., - prefix: AnyStr | None = ..., - dir: GenericPath[AnyStr] | None = ..., - ) -> IO[bytes]: ... + mode: OpenBinaryModeReading, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + ) -> io.BufferedReader: ... @overload def TemporaryFile( - mode: str = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: AnyStr | None = ..., - prefix: AnyStr | None = ..., - dir: GenericPath[AnyStr] | None = ..., + mode: str = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, ) -> IO[Any]: ... class _TemporaryFileWrapper(Generic[AnyStr], IO[AnyStr]): file: IO[AnyStr] # io.TextIOWrapper, io.BufferedReader or io.BufferedWriter name: str delete: bool - def __init__(self, file: IO[AnyStr], name: str, delete: bool = True) -> None: ... + if sys.version_info >= (3, 12): + def __init__(self, file: IO[AnyStr], name: str, delete: bool = True, delete_on_close: bool = True) -> None: ... + else: + def __init__(self, file: IO[AnyStr], name: str, delete: bool = True) -> None: ... + def __enter__(self) -> Self: ... def __exit__(self, exc: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ... def __getattr__(self, name: str) -> Any: ... @@ -246,7 +390,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def __init__( self: SpooledTemporaryFile[bytes], max_size: int = 0, - mode: _BytesMode = "w+b", + mode: OpenBinaryMode = "w+b", buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -260,7 +404,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def __init__( self: SpooledTemporaryFile[str], max_size: int, - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -275,7 +419,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): self: SpooledTemporaryFile[str], max_size: int = 0, *, - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -319,7 +463,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def __init__( self: SpooledTemporaryFile[bytes], max_size: int = 0, - mode: _BytesMode = "w+b", + mode: OpenBinaryMode = "w+b", buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -331,7 +475,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def __init__( self: SpooledTemporaryFile[str], max_size: int, - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -344,7 +488,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): self: SpooledTemporaryFile[str], max_size: int = 0, *, - mode: _StrMode, + mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, newline: str | None = None, @@ -425,7 +569,28 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): class TemporaryDirectory(Generic[AnyStr]): name: AnyStr - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12): + @overload + def __init__( + self: TemporaryDirectory[str], + suffix: str | None = None, + prefix: str | None = None, + dir: StrPath | None = None, + ignore_cleanup_errors: bool = False, + *, + delete: bool = True, + ) -> None: ... + @overload + def __init__( + self: TemporaryDirectory[bytes], + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: BytesPath | None = None, + ignore_cleanup_errors: bool = False, + *, + delete: bool = True, + ) -> None: ... + elif sys.version_info >= (3, 10): @overload def __init__( self: TemporaryDirectory[str], diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index 6275e4552630..badd09cae051 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -37,6 +37,9 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 10): __all__ += ["getprofile", "gettrace"] +if sys.version_info >= (3, 12): + __all__ += ["setprofile_all_threads", "settrace_all_threads"] + _profile_hook: ProfileFunction | None def active_count() -> int: ... @@ -53,6 +56,10 @@ if sys.version_info >= (3, 8): def settrace(func: TraceFunction) -> None: ... def setprofile(func: ProfileFunction | None) -> None: ... +if sys.version_info >= (3, 12): + def setprofile_all_threads(func: ProfileFunction | None) -> None: ... + def settrace_all_threads(func: TraceFunction) -> None: ... + if sys.version_info >= (3, 10): def gettrace() -> TraceFunction | None: ... def getprofile() -> ProfileFunction | None: ... diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi index fcd6ef87d217..85867a2b9744 100644 --- a/mypy/typeshed/stdlib/token.pyi +++ b/mypy/typeshed/stdlib/token.pyi @@ -73,6 +73,9 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] +if sys.version_info >= (3, 12): + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + ENDMARKER: int NAME: int NUMBER: int @@ -145,6 +148,12 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 10): SOFT_KEYWORD: int +if sys.version_info >= (3, 12): + EXCLAMATION: int + FSTRING_END: int + FSTRING_MIDDLE: int + FSTRING_START: int + def ISTERMINAL(x: int) -> bool: ... def ISNONTERMINAL(x: int) -> bool: ... def ISEOF(x: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index ba57402fb845..0028ed034ae6 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -83,6 +83,9 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] +if sys.version_info >= (3, 12): + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + if sys.version_info >= (3, 8): from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES else: diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 43475d91279d..6909c765cb7d 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -17,7 +17,7 @@ from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping from typing import Any, ClassVar, Generic, Mapping, Protocol, TypeVar, overload # noqa: Y022 -from typing_extensions import Literal, ParamSpec, final +from typing_extensions import Literal, ParamSpec, TypeVarTuple, final __all__ = [ "FunctionType", @@ -94,6 +94,8 @@ class FunctionType: if sys.version_info >= (3, 10): @property def __builtins__(self) -> dict[str, Any]: ... + if sys.version_info >= (3, 12): + __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str def __init__( @@ -374,6 +376,10 @@ class AsyncGeneratorType(AsyncGenerator[_T_co, _T_contra]): def ag_await(self) -> Awaitable[Any] | None: ... __name__: str __qualname__: str + if sys.version_info >= (3, 12): + @property + def ag_suspended(self) -> bool: ... + def __aiter__(self) -> AsyncGeneratorType[_T_co, _T_contra]: ... def __anext__(self) -> Coroutine[Any, Any, _T_co]: ... def asend(self, __val: _T_contra) -> Coroutine[Any, Any, _T_co]: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index db042dc440ae..08d7da5e8622 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -631,6 +631,7 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]): def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... def __contains__(self, __key: object) -> bool: ... + def __eq__(self, __other: object) -> bool: ... class MutableMapping(Mapping[_KT, _VT], Generic[_KT, _VT]): @abstractmethod @@ -853,9 +854,9 @@ class NamedTuple(tuple[Any, ...]): if sys.version_info >= (3, 12): __orig_bases__: ClassVar[tuple[Any, ...]] @overload - def __init__(self, typename: str, fields: Iterable[tuple[str, Any]] = ...) -> None: ... + def __init__(self, __typename: str, __fields: Iterable[tuple[str, Any]]) -> None: ... @overload - def __init__(self, typename: str, fields: None = None, **kwargs: Any) -> None: ... + def __init__(self, __typename: str, __fields: None = None, **kwargs: Any) -> None: ... @classmethod def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ... if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index 935e44e80dfa..74ce4ebd6b47 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -96,3 +96,6 @@ RESERVED_NCS: str RFC_4122: str RESERVED_MICROSOFT: str RESERVED_FUTURE: str + +if sys.version_info >= (3, 12): + def main() -> None: ... diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index 02edd42e7d59..99c7bb5846b6 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -43,8 +43,12 @@ class UnixBrowser(BaseBrowser): class Mozilla(UnixBrowser): ... -class Galeon(UnixBrowser): - raise_opts: list[str] +if sys.version_info < (3, 12): + class Galeon(UnixBrowser): + raise_opts: list[str] + + class Grail(BaseBrowser): + def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... class Chrome(UnixBrowser): ... class Opera(UnixBrowser): ... @@ -53,9 +57,6 @@ class Elinks(UnixBrowser): ... class Konqueror(BaseBrowser): def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... -class Grail(BaseBrowser): - def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... - if sys.platform == "win32": class WindowsDefault(BaseBrowser): def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index ee97faace379..0189bfe712b5 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -17,8 +17,10 @@ class zipimporter: else: def __init__(self, path: StrOrBytesPath) -> None: ... - def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ... # undocumented - def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ... + if sys.version_info < (3, 12): + def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ... # undocumented + def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ... + def get_code(self, fullname: str) -> CodeType: ... def get_data(self, pathname: str) -> bytes: ... def get_filename(self, fullname: str) -> str: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index fe994be3e8ff..a95530ed461a 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -17,9 +17,9 @@ class ZoneInfo(tzinfo): @classmethod def no_cache(cls, key: str) -> Self: ... @classmethod - def from_file(cls, __fobj: _IOBytes, key: str | None = ...) -> Self: ... + def from_file(cls, __fobj: _IOBytes, key: str | None = None) -> Self: ... @classmethod - def clear_cache(cls, *, only_keys: Iterable[str] | None = ...) -> None: ... + def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... def tzname(self, __dt: datetime | None) -> str | None: ... def utcoffset(self, __dt: datetime | None) -> timedelta | None: ... def dst(self, __dt: datetime | None) -> timedelta | None: ... @@ -30,7 +30,7 @@ class ZoneInfo(tzinfo): def reset_tzpath(to: Sequence[StrPath] | None = None) -> None: ... def available_timezones() -> set[str]: ... -TZPATH: Sequence[str] +TZPATH: tuple[str, ...] class ZoneInfoNotFoundError(KeyError): ... class InvalidTZPathWarning(RuntimeWarning): ... From 6f913a148e1c1ec126d2009272a4a26ff61b8195 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0024/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index b3871776128a..3cbf54e2dd3d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,7 +56,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # type: ignore Concatenate, Literal, - LiteralString, ParamSpec, Self, SupportsIndex, @@ -433,17 +432,8 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -451,20 +441,11 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: - @overload - def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... # type: ignore def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -480,91 +461,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -575,9 +497,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -585,25 +504,13 @@ class str(Sequence[str]): def __ge__(self, __value: str) -> bool: ... def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... # type: ignore - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From 475a46a7820e1ff7990a23c6ae745acbb3678c18 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0025/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 3cbf54e2dd3d..4337a44c7c68 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1665,11 +1665,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... From f5e5c117de36a339209c885d44b1b784e17023a4 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0026/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 25d604218a00..756ee86d3342 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -151,11 +151,7 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 9f3bbbeb1f5c8ca70f9d0386d5d1091f23e9a7cc Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0027/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 8adc3d82292e..1b4e59b7c120 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,12 +28,10 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -87,41 +85,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From bf9230934c4ce68c8c01aa0dd6aef57b180cc08b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 15 Jun 2023 21:12:18 +0100 Subject: [PATCH 0028/1617] Update hashes in `misc/sync-typeshed.py` following typeshed sync (#15447) Followup to #15444 --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index fc6cbc1d88e7..a8aca425696a 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "c844270a4", # LiteralString reverts - "9ebe5fd49", # sum reverts - "d1987191f", # ctypes reverts - "b1761f4c9", # ParamSpec for functools.wraps + "6f913a148", # LiteralString reverts + "475a46a78", # sum reverts + "f5e5c117d", # ctypes reverts + "9f3bbbeb1", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: subprocess.run(["git", "cherry-pick", commit], check=True) From ceb4d7fd687ddbb94dc6c62fb6a2c46c383da54f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 15 Jun 2023 21:32:37 +0100 Subject: [PATCH 0029/1617] Fix re-added file with errors in mypy daemon (#15440) Fixes #5343 Fixes #12249 This can potentially slow down runs in situations where multiple unchanged files are re-added to source list. Potentially, we can track whether modules had errors permanently (not just from previous run), and then only re-check those unchanged re-added files that had errors before. I am not sure if this is important. --- mypy/dmypy_server.py | 15 +++++++++++++++ test-data/unit/daemon.test | 22 ++++++++++++++++++++++ test-data/unit/fine-grained.test | 21 +++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 631aab4ccbc8..54544f4c01ce 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -857,6 +857,21 @@ def _find_changed( assert path removed.append((source.module, path)) + # Always add modules that were (re-)added, since they may be detected as not changed by + # fswatcher (if they were actually not changed), but they may still need to be checked + # in case they had errors before they were deleted from sources on previous runs. + previous_modules = {source.module for source in self.previous_sources} + changed_set = set(changed) + changed.extend( + [ + (source.module, source.path) + for source in sources + if source.path + and source.module not in previous_modules + and (source.module, source.path) not in changed_set + ] + ) + # Find anything that has had its module path change because of added or removed __init__s last = {s.path: s.module for s in self.previous_sources} for s in sources: diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 424fc6d2b167..1fae1514111c 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -62,6 +62,28 @@ Daemon started \[mypy] files = ./foo.py +[case testDaemonRunMultipleStrict] +$ dmypy run -- foo.py --strict --follow-imports=error +Daemon started +foo.py:1: error: Function is missing a return type annotation +foo.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run -- bar.py --strict --follow-imports=error +bar.py:1: error: Function is missing a return type annotation +bar.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run -- foo.py --strict --follow-imports=error +foo.py:1: error: Function is missing a return type annotation +foo.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +[file foo.py] +def f(): pass +[file bar.py] +def f(): pass + [case testDaemonRunRestart] $ dmypy run -- foo.py --follow-imports=error Daemon started diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 88a11be31f34..a0d7d302fd08 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10340,3 +10340,24 @@ reveal_type(x) [out] == a.py:3: note: Revealed type is "Union[def (x: builtins.int) -> builtins.int, def (*x: builtins.int) -> builtins.int]" + +[case testErrorInReAddedModule] +# flags: --disallow-untyped-defs --follow-imports=error +# cmd: mypy a.py +# cmd2: mypy b.py +# cmd3: mypy a.py + +[file a.py] +def f(): pass +[file b.py] +def f(): pass +[file unrelated.txt.3] +[out] +a.py:1: error: Function is missing a return type annotation +a.py:1: note: Use "-> None" if function does not return a value +== +b.py:1: error: Function is missing a return type annotation +b.py:1: note: Use "-> None" if function does not return a value +== +a.py:1: error: Function is missing a return type annotation +a.py:1: note: Use "-> None" if function does not return a value From cfec71798175fcbf030d6d114750d6fac454b3c9 Mon Sep 17 00:00:00 2001 From: Adel Atallah <2213999+atallahade@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:43:01 +0100 Subject: [PATCH 0030/1617] Remove confusing instance variable example in cheat sheet (#15441) --- docs/source/cheat_sheet_py3.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index 31242d0ad0bc..297427e72aca 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -178,8 +178,6 @@ Classes class AuditedBankAccount(BankAccount): # You can optionally declare instance variables in the class body audit_log: list[str] - # This is an instance variable with a default value - auditor_name: str = "The Spanish Inquisition" def __init__(self, account_name: str, initial_balance: int = 0) -> None: super().__init__(account_name, initial_balance) From 65a715a650cc058daad93e123ce972d99088f19f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 16 Jun 2023 13:42:15 +0100 Subject: [PATCH 0031/1617] [mypyc] Use C99 compound literals for undefined tuple values (#15453) This simplifies things a bit. All the C compilers we care about should support this. This will make things easier once we support more types represented as C structs. I expect this to have no measurable impact on performance. --- mypyc/codegen/emit.py | 35 +++++++++++++---------------------- mypyc/codegen/emitmodule.py | 8 ++------ mypyc/lib-rt/CPy.h | 3 --- mypyc/test/test_emit.py | 20 +++++++++++++++++++- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index a2e3d9849dca..8114e0517219 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -345,12 +345,6 @@ def tuple_c_declaration(self, rtuple: RTuple) -> list[str]: result.append(f"{self.ctype_spaced(typ)}f{i};") i += 1 result.append(f"}} {rtuple.struct_name};") - values = self.tuple_undefined_value_helper(rtuple) - result.append( - "static {} {} = {{ {} }};".format( - self.ctype(rtuple), self.tuple_undefined_value(rtuple), "".join(values) - ) - ) result.append("#endif") result.append("") @@ -470,23 +464,20 @@ def tuple_undefined_check_cond( return check def tuple_undefined_value(self, rtuple: RTuple) -> str: - return "tuple_undefined_" + rtuple.unique_id + """Undefined tuple value suitable in an expression.""" + return f"({rtuple.struct_name}) {self.c_initializer_undefined_value(rtuple)}" - def tuple_undefined_value_helper(self, rtuple: RTuple) -> list[str]: - res = [] - # see tuple_c_declaration() - if len(rtuple.types) == 0: - return [self.c_undefined_value(int_rprimitive)] - for item in rtuple.types: - if not isinstance(item, RTuple): - res.append(self.c_undefined_value(item)) - else: - sub_list = self.tuple_undefined_value_helper(item) - res.append("{ ") - res.extend(sub_list) - res.append(" }") - res.append(", ") - return res[:-1] + def c_initializer_undefined_value(self, rtype: RType) -> str: + """Undefined value represented in a form suitable for variable initialization.""" + if isinstance(rtype, RTuple): + if not rtype.types: + # Empty tuples contain a flag so that they can still indicate + # error values. + return f"{{ {int_rprimitive.c_undefined} }}" + items = ", ".join([self.c_initializer_undefined_value(t) for t in rtype.types]) + return f"{{ {items} }}" + else: + return self.c_undefined_value(rtype) # Higher-level operations diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 0e80ff6da1f2..f360fabbe8f6 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -51,7 +51,7 @@ from mypyc.ir.func_ir import FuncIR from mypyc.ir.module_ir import ModuleIR, ModuleIRs, deserialize_modules from mypyc.ir.ops import DeserMaps, LoadLiteral -from mypyc.ir.rtypes import RTuple, RType +from mypyc.ir.rtypes import RType from mypyc.irbuild.main import build_ir from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.prepare import load_type_map @@ -1052,11 +1052,7 @@ def declare_finals( def final_definition(self, module: str, name: str, typ: RType, emitter: Emitter) -> str: static_name = emitter.static_name(name, module) # Here we rely on the fact that undefined value and error value are always the same - if isinstance(typ, RTuple): - # We need to inline because initializer must be static - undefined = "{{ {} }}".format("".join(emitter.tuple_undefined_value_helper(typ))) - else: - undefined = emitter.c_undefined_value(typ) + undefined = emitter.c_initializer_undefined_value(typ) return f"{emitter.ctype_spaced(typ)}{static_name} = {undefined};" def declare_static_pyobject(self, identifier: str, emitter: Emitter) -> None: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 7a3e16fe9d65..30d93be60989 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -41,7 +41,6 @@ typedef struct tuple_T3OOO { PyObject *f1; PyObject *f2; } tuple_T3OOO; -static tuple_T3OOO tuple_undefined_T3OOO = { NULL, NULL, NULL }; #endif // Our return tuple wrapper for dictionary iteration helper. @@ -52,7 +51,6 @@ typedef struct tuple_T3CIO { CPyTagged f1; // Last dict offset PyObject *f2; // Next dictionary key or value } tuple_T3CIO; -static tuple_T3CIO tuple_undefined_T3CIO = { 2, CPY_INT_TAG, NULL }; #endif // Same as above but for both key and value. @@ -64,7 +62,6 @@ typedef struct tuple_T4CIOO { PyObject *f2; // Next dictionary key PyObject *f3; // Next dictionary value } tuple_T4CIOO; -static tuple_T4CIOO tuple_undefined_T4CIOO = { 2, CPY_INT_TAG, NULL, NULL }; #endif diff --git a/mypyc/test/test_emit.py b/mypyc/test/test_emit.py index 54bf4eef3c74..e4ace3ec01f0 100644 --- a/mypyc/test/test_emit.py +++ b/mypyc/test/test_emit.py @@ -4,7 +4,7 @@ from mypyc.codegen.emit import Emitter, EmitterContext from mypyc.ir.ops import BasicBlock, Register, Value -from mypyc.ir.rtypes import int_rprimitive +from mypyc.ir.rtypes import RTuple, bool_rprimitive, int_rprimitive, str_rprimitive from mypyc.namegen import NameGenerator @@ -49,3 +49,21 @@ def test_emit_line(self) -> None: CPyStatics[1]; /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] */\n""" ) + + def test_emit_undefined_value_for_simple_type(self) -> None: + emitter = Emitter(self.context, {}) + assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG" + assert emitter.c_undefined_value(str_rprimitive) == "NULL" + assert emitter.c_undefined_value(bool_rprimitive) == "2" + + def test_emit_undefined_value_for_tuple(self) -> None: + emitter = Emitter(self.context, {}) + assert ( + emitter.c_undefined_value(RTuple([str_rprimitive, int_rprimitive, bool_rprimitive])) + == "(tuple_T3OIC) { NULL, CPY_INT_TAG, 2 }" + ) + assert emitter.c_undefined_value(RTuple([str_rprimitive])) == "(tuple_T1O) { NULL }" + assert ( + emitter.c_undefined_value(RTuple([RTuple([str_rprimitive]), bool_rprimitive])) + == "(tuple_T2T1OC) { { NULL }, 2 }" + ) From db5b5af1201fff03465b0684d16b6489a62a3d78 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 16 Jun 2023 06:02:39 -0700 Subject: [PATCH 0032/1617] Switch PEP 561 tests to a different build backend (#15451) Fixes #15446 setuptools has broken how editable installs work. It seems like most other build backends continue to use static pth files, so let's just switch to another one. --- mypy/test/testpep561.py | 32 +++++++++---------- .../packages/typedpkg-stubs/pyproject.toml | 11 +++++++ test-data/packages/typedpkg-stubs/setup.py | 13 -------- test-data/packages/typedpkg/pyproject.toml | 8 +++++ test-data/packages/typedpkg/setup.py | 15 --------- .../packages/typedpkg_ns_a/pyproject.toml | 11 +++++++ test-data/packages/typedpkg_ns_a/setup.py | 10 ------ .../typedpkg_ns_b-stubs/pyproject.toml | 11 +++++++ .../packages/typedpkg_ns_b-stubs/setup.py | 14 -------- .../packages/typedpkg_ns_b/pyproject.toml | 8 +++++ test-data/packages/typedpkg_ns_b/setup.py | 10 ------ test-data/unit/pep561.test | 18 ----------- 12 files changed, 64 insertions(+), 97 deletions(-) create mode 100644 test-data/packages/typedpkg-stubs/pyproject.toml delete mode 100644 test-data/packages/typedpkg-stubs/setup.py create mode 100644 test-data/packages/typedpkg/pyproject.toml delete mode 100644 test-data/packages/typedpkg/setup.py create mode 100644 test-data/packages/typedpkg_ns_a/pyproject.toml delete mode 100644 test-data/packages/typedpkg_ns_a/setup.py create mode 100644 test-data/packages/typedpkg_ns_b-stubs/pyproject.toml delete mode 100644 test-data/packages/typedpkg_ns_b-stubs/setup.py create mode 100644 test-data/packages/typedpkg_ns_b/pyproject.toml delete mode 100644 test-data/packages/typedpkg_ns_b/setup.py diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index ed8674e8d5bb..b8a11d7fc8af 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -47,22 +47,16 @@ def virtualenv(python_executable: str = sys.executable) -> Iterator[tuple[str, s def install_package( - pkg: str, python_executable: str = sys.executable, use_pip: bool = True, editable: bool = False + pkg: str, python_executable: str = sys.executable, editable: bool = False ) -> None: """Install a package from test-data/packages/pkg/""" working_dir = os.path.join(package_path, pkg) with tempfile.TemporaryDirectory() as dir: - if use_pip: - install_cmd = [python_executable, "-m", "pip", "install"] - if editable: - install_cmd.append("-e") - install_cmd.append(".") - else: - install_cmd = [python_executable, "setup.py"] - if editable: - install_cmd.append("develop") - else: - install_cmd.append("install") + install_cmd = [python_executable, "-m", "pip", "install"] + if editable: + install_cmd.append("-e") + install_cmd.append(".") + # Note that newer versions of pip (21.3+) don't # follow this env variable, but this is for compatibility env = {"PIP_BUILD": dir} @@ -82,21 +76,25 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: assert testcase.old_cwd is not None, "test was not properly set up" python = sys.executable + if sys.version_info < (3, 8) and testcase.location[-1] == "testTypedPkgSimpleEditable": + # Python 3.7 doesn't ship with new enough pip to support PEP 660 + # This is a quick hack to skip the test; we'll drop Python 3.7 support soon enough + return + assert python is not None, "Should be impossible" pkgs, pip_args = parse_pkgs(testcase.input[0]) mypy_args = parse_mypy_args(testcase.input[1]) - use_pip = True editable = False for arg in pip_args: - if arg == "no-pip": - use_pip = False - elif arg == "editable": + if arg == "editable": editable = True + else: + raise ValueError(f"Unknown pip argument: {arg}") assert pkgs, "No packages to install for PEP 561 test?" with virtualenv(python) as venv: venv_dir, python_executable = venv for pkg in pkgs: - install_package(pkg, python_executable, use_pip, editable) + install_package(pkg, python_executable, editable) cmd_line = list(mypy_args) has_program = not ("-p" in cmd_line or "--package" in cmd_line) diff --git a/test-data/packages/typedpkg-stubs/pyproject.toml b/test-data/packages/typedpkg-stubs/pyproject.toml new file mode 100644 index 000000000000..125816151ef8 --- /dev/null +++ b/test-data/packages/typedpkg-stubs/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = 'typedpkg-stubs' +version = '0.1' +description = 'test' + +[tool.hatch.build] +include = ["**/*.pyi"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg-stubs/setup.py b/test-data/packages/typedpkg-stubs/setup.py deleted file mode 100644 index 4948dc6a01df..000000000000 --- a/test-data/packages/typedpkg-stubs/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -This setup file installs packages to test mypy's PEP 561 implementation -""" - -from setuptools import setup - -setup( - name='typedpkg-stubs', - author="The mypy team", - version='0.1', - package_data={'typedpkg-stubs': ['sample.pyi', '__init__.pyi', 'py.typed']}, - packages=['typedpkg-stubs'], -) diff --git a/test-data/packages/typedpkg/pyproject.toml b/test-data/packages/typedpkg/pyproject.toml new file mode 100644 index 000000000000..5269c94320e1 --- /dev/null +++ b/test-data/packages/typedpkg/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = 'typedpkg' +version = '0.1' +description = 'test' + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg/setup.py b/test-data/packages/typedpkg/setup.py deleted file mode 100644 index 11bcfb11a104..000000000000 --- a/test-data/packages/typedpkg/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -This setup file installs packages to test mypy's PEP 561 implementation -""" - -from setuptools import setup - -setup( - name='typedpkg', - author="The mypy team", - version='0.1', - package_data={'typedpkg': ['py.typed']}, - packages=['typedpkg', 'typedpkg.pkg'], - include_package_data=True, - zip_safe=False, -) diff --git a/test-data/packages/typedpkg_ns_a/pyproject.toml b/test-data/packages/typedpkg_ns_a/pyproject.toml new file mode 100644 index 000000000000..cc464af75b17 --- /dev/null +++ b/test-data/packages/typedpkg_ns_a/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = 'typedpkg_namespace.alpha' +version = '0.1' +description = 'test' + +[tool.hatch.build] +include = ["**/*.py", "**/*.pyi", "**/py.typed"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_a/setup.py b/test-data/packages/typedpkg_ns_a/setup.py deleted file mode 100644 index 3dab731cada9..000000000000 --- a/test-data/packages/typedpkg_ns_a/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -from setuptools import setup - -setup( - name='typedpkg_namespace.alpha', - version='1.0.0', - namespace_packages=['typedpkg_ns'], - zip_safe=False, - package_data={'typedpkg_ns.a': ['py.typed']}, - packages=['typedpkg_ns.a'], -) diff --git a/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml b/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml new file mode 100644 index 000000000000..d5275d1ed8b3 --- /dev/null +++ b/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = 'typedpkg_ns-stubs' +version = '0.1' +description = 'test' + +[tool.hatch.build] +include = ["**/*.pyi"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_b-stubs/setup.py b/test-data/packages/typedpkg_ns_b-stubs/setup.py deleted file mode 100644 index a5d7df83eeea..000000000000 --- a/test-data/packages/typedpkg_ns_b-stubs/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -This setup file installs packages to test mypy's PEP 561 implementation -""" - -from distutils.core import setup - -setup( - name='typedpkg_ns_b-stubs', - author="The mypy team", - version='0.1', - namespace_packages=['typedpkg_ns-stubs'], - package_data={'typedpkg_ns-stubs.b': ['__init__.pyi', 'bbb.pyi']}, - packages=['typedpkg_ns-stubs.b'], -) diff --git a/test-data/packages/typedpkg_ns_b/pyproject.toml b/test-data/packages/typedpkg_ns_b/pyproject.toml new file mode 100644 index 000000000000..8567af11152e --- /dev/null +++ b/test-data/packages/typedpkg_ns_b/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = 'typedpkg_namespace.beta' +version = '0.1' +description = 'test' + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_b/setup.py b/test-data/packages/typedpkg_ns_b/setup.py deleted file mode 100644 index 4f0d0d954a73..000000000000 --- a/test-data/packages/typedpkg_ns_b/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -from setuptools import setup - -setup( - name='typedpkg_namespace.beta', - version='1.0.0', - namespace_packages=['typedpkg_ns'], - zip_safe=False, - package_data={'typedpkg_ns.b': []}, - packages=['typedpkg_ns.b'], -) diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index 8c401cfc3c51..e8ebbd03dca7 100644 --- a/test-data/unit/pep561.test +++ b/test-data/unit/pep561.test @@ -72,15 +72,6 @@ reveal_type(a) [out] testStubPrecedence.py:5: note: Revealed type is "builtins.list[builtins.str]" -[case testTypedPkgSimpleEgg] -# pkgs: typedpkg; no-pip -from typedpkg.sample import ex -from typedpkg import dne -a = ex(['']) -reveal_type(a) -[out] -testTypedPkgSimpleEgg.py:5: note: Revealed type is "builtins.tuple[builtins.str, ...]" - [case testTypedPkgSimpleEditable] # pkgs: typedpkg; editable from typedpkg.sample import ex @@ -90,15 +81,6 @@ reveal_type(a) [out] testTypedPkgSimpleEditable.py:5: note: Revealed type is "builtins.tuple[builtins.str, ...]" -[case testTypedPkgSimpleEditableEgg] -# pkgs: typedpkg; editable; no-pip -from typedpkg.sample import ex -from typedpkg import dne -a = ex(['']) -reveal_type(a) -[out] -testTypedPkgSimpleEditableEgg.py:5: note: Revealed type is "builtins.tuple[builtins.str, ...]" - [case testTypedPkgNamespaceImportFrom] # pkgs: typedpkg, typedpkg_ns_a from typedpkg.pkg.aaa import af From 21cc1c74b7b531ef6e1024d35b364fe30077117b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 16 Jun 2023 21:24:34 +0100 Subject: [PATCH 0033/1617] Improve handling of attribute access on class objects (#14988) Fixes #14056 #14056 was originally reported as a mypyc issue (because that's how it presented itself to the user), but the underlying bug is really a bug to do with how mypy understands metaclasses. On mypy `master`: ```py class Meta(type): bar: str class Foo(metaclass=Meta): bar: int reveal_type(Foo().bar) # Revealed type is int (correct!) reveal_type(Foo.bar) # Revealed type is int, but should be str ``` This PR fixes that incorrect behaviour. Since this is really a mypy bug rather than a mypyc bug, I haven't added a mypyc test, but I'm happy to if that would be useful. (I'll need some guidance as to exactly where it should go -- I don't know much about mypyc internals!) --- mypy/checkmember.py | 30 +++++++++++++++++--- test-data/unit/check-class-namedtuple.test | 5 +++- test-data/unit/check-classes.test | 33 ++++++++++++++++++++++ test-data/unit/pythoneval.test | 13 +++++++++ 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index c2c6b3555805..7af61a532b7b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -388,7 +388,7 @@ 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, mcs_fallback=typ.fallback ) if result: return result @@ -434,17 +434,21 @@ def analyze_type_type_member_access( if isinstance(typ.item.item, Instance): item = typ.item.item.type.metaclass_type ignore_messages = False + + if item is not None: + fallback = item.type.metaclass_type or fallback + 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, mcs_fallback=fallback, override_info=override_info + ) if result: if not (isinstance(get_proper_type(result), AnyType) and item.type.fallback_to_any): return result else: # We don't want errors on metaclass lookup for classes with Any fallback ignore_messages = True - if item is not None: - 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) @@ -893,6 +897,8 @@ def analyze_class_attribute_access( itype: Instance, name: str, mx: MemberContext, + *, + mcs_fallback: Instance, override_info: TypeInfo | None = None, original_vars: Sequence[TypeVarLikeType] | None = None, ) -> Type | None: @@ -919,6 +925,22 @@ def analyze_class_attribute_access( return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) return None + if ( + isinstance(node.node, Var) + and not node.node.is_classvar + and not hook + and mcs_fallback.type.get(name) + ): + # If the same attribute is declared on the metaclass and the class but with different types, + # and the attribute on the class is not a ClassVar, + # the type of the attribute on the metaclass should take priority + # over the type of the attribute on the class, + # when the attribute is being accessed from the class object itself. + # + # Return `None` here to signify that the name should be looked up + # on the class object itself rather than the instance. + return None + is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncBase) if mx.is_lvalue: diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index 8ae7f6555f9d..ab2f5f3f6b48 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -325,7 +325,10 @@ class X(NamedTuple): reveal_type(X._fields) # N: Revealed type is "Tuple[builtins.str, builtins.str]" reveal_type(X._field_types) # N: Revealed type is "builtins.dict[builtins.str, Any]" reveal_type(X._field_defaults) # N: Revealed type is "builtins.dict[builtins.str, Any]" -reveal_type(X.__annotations__) # N: Revealed type is "builtins.dict[builtins.str, Any]" + +# In typeshed's stub for builtins.pyi, __annotations__ is `dict[str, Any]`, +# but it's inferred as `Mapping[str, object]` here due to the fixture we're using +reveal_type(X.__annotations__) # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index cfd0cc3a4ec6..2c80eb7b49bc 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4540,6 +4540,39 @@ def f(TA: Type[A]): reveal_type(TA) # N: Revealed type is "Type[__main__.A]" reveal_type(TA.x) # N: Revealed type is "builtins.int" +[case testMetaclassConflictingInstanceVars] +from typing import ClassVar + +class Meta(type): + foo: int + bar: int + eggs: ClassVar[int] = 42 + spam: ClassVar[int] = 42 + +class Foo(metaclass=Meta): + foo: str + bar: ClassVar[str] = 'bar' + eggs: str + spam: ClassVar[str] = 'spam' + +reveal_type(Foo.foo) # N: Revealed type is "builtins.int" +reveal_type(Foo.bar) # N: Revealed type is "builtins.str" +reveal_type(Foo.eggs) # N: Revealed type is "builtins.int" +reveal_type(Foo.spam) # N: Revealed type is "builtins.str" + +class MetaSub(Meta): ... + +class Bar(metaclass=MetaSub): + foo: str + bar: ClassVar[str] = 'bar' + eggs: str + spam: ClassVar[str] = 'spam' + +reveal_type(Bar.foo) # N: Revealed type is "builtins.int" +reveal_type(Bar.bar) # N: Revealed type is "builtins.str" +reveal_type(Bar.eggs) # N: Revealed type is "builtins.int" +reveal_type(Bar.spam) # N: Revealed type is "builtins.str" + [case testSubclassMetaclass] class M1(type): x = 0 diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 7108734102d1..93ff2da9f7b9 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1987,6 +1987,19 @@ def good9(foo1: Foo[Concatenate[int, P]], foo2: Foo[[int, str, bytes]], *args: P [out] _testStrictEqualitywithParamSpec.py:11: error: Non-overlapping equality check (left operand type: "Foo[[int]]", right operand type: "Bar[[int]]") +[case testInferenceOfDunderDictOnClassObjects] +class Foo: ... +reveal_type(Foo.__dict__) +reveal_type(Foo().__dict__) +Foo.__dict__ = {} +Foo().__dict__ = {} + +[out] +_testInferenceOfDunderDictOnClassObjects.py:2: note: Revealed type is "types.MappingProxyType[builtins.str, Any]" +_testInferenceOfDunderDictOnClassObjects.py:3: note: Revealed type is "builtins.dict[builtins.str, Any]" +_testInferenceOfDunderDictOnClassObjects.py:4: error: Property "__dict__" defined in "type" is read-only +_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[, ]", variable has type "MappingProxyType[str, Any]") + [case testTypeVarTuple] # flags: --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack --python-version=3.11 from typing import Any, Callable, Unpack, TypeVarTuple From 6f2bfff521e708f015af1eec5118db4600b829be Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sat, 17 Jun 2023 09:02:36 -0400 Subject: [PATCH 0034/1617] Add signature for dataclasses.replace (#14849) Validate `dataclassses.replace` actual arguments to match the fields: - Unlike `__init__`, the arguments are always named. - All arguments are optional except for `InitVar`s without a default value. The tricks: - We're looking up type of the first positional argument ("obj") through private API. See #10216, #14845. - We're preparing the signature of "replace" (for that specific dataclass) during the dataclass transformation and storing it in a "private" class attribute `__mypy-replace` (obviously not part of PEP-557 but contains a hyphen so should not conflict with any future valid identifier). Stashing the signature into the symbol table allows it to be passed across phases and cached across invocations. The stashed signature lacks the first argument, which we prepend at function signature hook time, since it depends on the type that `replace` is called on. Based on #14526 but actually simpler. Partially addresses #5152. # Remaining tasks - [x] handle generic dataclasses - [x] avoid data class transforms - [x] fine-grained mode tests --------- Co-authored-by: Alex Waygood --- mypy/plugins/dataclasses.py | 163 +++++++++++++++++- mypy/plugins/default.py | 4 +- test-data/unit/check-dataclass-transform.test | 19 ++ test-data/unit/check-dataclasses.test | 157 ++++++++++++++++- test-data/unit/deps.test | 2 + test-data/unit/fine-grained-dataclass.test | 25 +++ test-data/unit/lib-stub/dataclasses.pyi | 2 + test-data/unit/pythoneval.test | 20 +++ 8 files changed, 389 insertions(+), 3 deletions(-) create mode 100644 test-data/unit/fine-grained-dataclass.test diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 2fd903f2f8b9..913b1ef23312 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -7,6 +7,8 @@ from mypy import errorcodes, message_registry from mypy.expandtype import expand_type, expand_type_by_instance +from mypy.meet import meet_types +from mypy.messages import format_type_bare from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, @@ -38,7 +40,7 @@ TypeVarExpr, Var, ) -from mypy.plugin import ClassDefContext, SemanticAnalyzerPluginInterface +from mypy.plugin import ClassDefContext, FunctionSigContext, SemanticAnalyzerPluginInterface from mypy.plugins.common import ( _get_callee_type, _get_decorator_bool_argument, @@ -56,10 +58,13 @@ Instance, LiteralType, NoneType, + ProperType, TupleType, Type, TypeOfAny, TypeVarType, + UninhabitedType, + UnionType, get_proper_type, ) from mypy.typevars import fill_typevars @@ -76,6 +81,7 @@ frozen_default=False, field_specifiers=("dataclasses.Field", "dataclasses.field"), ) +_INTERNAL_REPLACE_SYM_NAME = "__mypy-replace" class DataclassAttribute: @@ -344,6 +350,9 @@ def transform(self) -> bool: self._add_dataclass_fields_magic_attribute() + if self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES: + self._add_internal_replace_method(attributes) + info.metadata["dataclass"] = { "attributes": [attr.serialize() for attr in attributes], "frozen": decorator_arguments["frozen"], @@ -351,6 +360,37 @@ def transform(self) -> bool: return True + def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> None: + """ + Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass + to be used later whenever 'dataclasses.replace' is called for this dataclass. + """ + arg_types: list[Type] = [] + arg_kinds = [] + arg_names: list[str | None] = [] + + info = self._cls.info + for attr in attributes: + attr_type = attr.expand_type(info) + assert attr_type is not None + arg_types.append(attr_type) + arg_kinds.append( + ARG_NAMED if attr.is_init_var and not attr.has_default else ARG_NAMED_OPT + ) + arg_names.append(attr.name) + + signature = CallableType( + arg_types=arg_types, + arg_kinds=arg_kinds, + arg_names=arg_names, + ret_type=NoneType(), + fallback=self._api.named_type("builtins.function"), + ) + + self._cls.info.names[_INTERNAL_REPLACE_SYM_NAME] = SymbolTableNode( + kind=MDEF, node=FuncDef(typ=signature), plugin_generated=True + ) + def add_slots( self, info: TypeInfo, attributes: list[DataclassAttribute], *, correct_version: bool ) -> None: @@ -893,3 +933,124 @@ def _has_direct_dataclass_transform_metaclass(info: TypeInfo) -> bool: info.declared_metaclass is not None and info.declared_metaclass.type.dataclass_transform_spec is not None ) + + +def _fail_not_dataclass(ctx: FunctionSigContext, t: Type, parent_t: Type) -> None: + t_name = format_type_bare(t, ctx.api.options) + if parent_t is t: + msg = ( + f'Argument 1 to "replace" has a variable type "{t_name}" not bound to a dataclass' + if isinstance(t, TypeVarType) + else f'Argument 1 to "replace" has incompatible type "{t_name}"; expected a dataclass' + ) + else: + pt_name = format_type_bare(parent_t, ctx.api.options) + msg = ( + f'Argument 1 to "replace" has type "{pt_name}" whose item "{t_name}" is not bound to a dataclass' + if isinstance(t, TypeVarType) + else f'Argument 1 to "replace" has incompatible type "{pt_name}" whose item "{t_name}" is not a dataclass' + ) + + ctx.api.fail(msg, ctx.context) + + +def _get_expanded_dataclasses_fields( + ctx: FunctionSigContext, typ: ProperType, display_typ: ProperType, parent_typ: ProperType +) -> list[CallableType] | None: + """ + For a given type, determine what dataclasses it can be: for each class, return the field types. + For generic classes, the field types are expanded. + If the type contains Any or a non-dataclass, returns None; in the latter case, also reports an error. + """ + if isinstance(typ, AnyType): + return None + elif isinstance(typ, UnionType): + ret: list[CallableType] | None = [] + for item in typ.relevant_items(): + item = get_proper_type(item) + item_types = _get_expanded_dataclasses_fields(ctx, item, item, parent_typ) + if ret is not None and item_types is not None: + ret += item_types + else: + ret = None # but keep iterating to emit all errors + return ret + elif isinstance(typ, TypeVarType): + return _get_expanded_dataclasses_fields( + ctx, get_proper_type(typ.upper_bound), display_typ, parent_typ + ) + elif isinstance(typ, Instance): + replace_sym = typ.type.get_method(_INTERNAL_REPLACE_SYM_NAME) + if replace_sym is None: + _fail_not_dataclass(ctx, display_typ, parent_typ) + return None + replace_sig = replace_sym.type + assert isinstance(replace_sig, ProperType) + assert isinstance(replace_sig, CallableType) + return [expand_type_by_instance(replace_sig, typ)] + else: + _fail_not_dataclass(ctx, display_typ, parent_typ) + return None + + +# TODO: we can potentially get the function signature hook to allow returning a union +# and leave this to the regular machinery of resolving a union of callables +# (https://github.com/python/mypy/issues/15457) +def _meet_replace_sigs(sigs: list[CallableType]) -> CallableType: + """ + Produces the lowest bound of the 'replace' signatures of multiple dataclasses. + """ + args = { + name: (typ, kind) + for name, typ, kind in zip(sigs[0].arg_names, sigs[0].arg_types, sigs[0].arg_kinds) + } + + for sig in sigs[1:]: + sig_args = { + name: (typ, kind) + for name, typ, kind in zip(sig.arg_names, sig.arg_types, sig.arg_kinds) + } + for name in (*args.keys(), *sig_args.keys()): + sig_typ, sig_kind = args.get(name, (UninhabitedType(), ARG_NAMED_OPT)) + sig2_typ, sig2_kind = sig_args.get(name, (UninhabitedType(), ARG_NAMED_OPT)) + args[name] = ( + meet_types(sig_typ, sig2_typ), + ARG_NAMED_OPT if sig_kind == sig2_kind == ARG_NAMED_OPT else ARG_NAMED, + ) + + return sigs[0].copy_modified( + arg_names=list(args.keys()), + arg_types=[typ for typ, _ in args.values()], + arg_kinds=[kind for _, kind in args.values()], + ) + + +def replace_function_sig_callback(ctx: FunctionSigContext) -> CallableType: + """ + Returns a signature for the 'dataclasses.replace' function that's dependent on the type + of the first positional argument. + """ + if len(ctx.args) != 2: + # Ideally the name and context should be callee's, but we don't have it in FunctionSigContext. + ctx.api.fail(f'"{ctx.default_signature.name}" has unexpected type annotation', ctx.context) + return ctx.default_signature + + if len(ctx.args[0]) != 1: + return ctx.default_signature # leave it to the type checker to complain + + obj_arg = ctx.args[0][0] + obj_type = get_proper_type(ctx.api.get_expression_type(obj_arg)) + inst_type_str = format_type_bare(obj_type, ctx.api.options) + + replace_sigs = _get_expanded_dataclasses_fields(ctx, obj_type, obj_type, obj_type) + if replace_sigs is None: + return ctx.default_signature + replace_sig = _meet_replace_sigs(replace_sigs) + + return replace_sig.copy_modified( + arg_names=[None, *replace_sig.arg_names], + arg_kinds=[ARG_POS, *replace_sig.arg_kinds], + arg_types=[obj_type, *replace_sig.arg_types], + ret_type=obj_type, + fallback=ctx.default_signature.fallback, + name=f"{ctx.default_signature.name} of {inst_type_str}", + ) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 500eef76a9d9..b83c0192a14b 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -51,12 +51,14 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] def get_function_signature_hook( self, fullname: str ) -> Callable[[FunctionSigContext], FunctionLike] | None: - from mypy.plugins import attrs + from mypy.plugins import attrs, dataclasses if fullname in ("attr.evolve", "attrs.evolve", "attr.assoc", "attrs.assoc"): return attrs.evolve_function_sig_callback elif fullname in ("attr.fields", "attrs.fields"): return attrs.fields_function_sig_callback + elif fullname == "dataclasses.replace": + return dataclasses.replace_function_sig_callback return None def get_method_signature_hook( diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index be6b46d70846..53a6eea95bfa 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -840,6 +840,24 @@ reveal_type(bar.base) # N: Revealed type is "builtins.int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] +[case testDataclassTransformReplace] +from dataclasses import replace +from typing import dataclass_transform, Type + +@dataclass_transform() +def my_dataclass(cls: Type) -> Type: + return cls + +@my_dataclass +class Person: + name: str + +p = Person('John') +y = replace(p, name='Bob') # E: Argument 1 to "replace" has incompatible type "Person"; expected a dataclass + +[typing fixtures/typing-full.pyi] +[builtins fixtures/dataclasses.pyi] + [case testDataclassTransformSimpleDescriptor] # flags: --python-version 3.11 @@ -1051,5 +1069,6 @@ class Desc2: class C: x: Desc # E: Unsupported signature for "__set__" in "Desc" y: Desc2 # E: Unsupported "__set__" in "Desc2" + [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 914e1c2e0602..1f6c8d143243 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2006,7 +2006,6 @@ e: Element[Bar] reveal_type(e.elements) # N: Revealed type is "typing.Sequence[__main__.Element[__main__.Bar]]" [builtins fixtures/dataclasses.pyi] - [case testIfConditionsInDefinition] # flags: --python-version 3.11 --always-true TRUTH from dataclasses import dataclass @@ -2040,8 +2039,164 @@ Foo( present_4=4, present_5=5, ) + +[builtins fixtures/dataclasses.pyi] + +[case testReplace] +from dataclasses import dataclass, replace, InitVar +from typing import ClassVar + +@dataclass +class A: + x: int + q: InitVar[int] + q2: InitVar[int] = 0 + c: ClassVar[int] + + +a = A(x=42, q=7) +a2 = replace(a) # E: Missing named argument "q" for "replace" of "A" +a2 = replace(a, q=42) +a2 = replace(a, x=42, q=42) +a2 = replace(a, x=42, q=42, c=7) # E: Unexpected keyword argument "c" for "replace" of "A" +a2 = replace(a, x='42', q=42) # E: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int" +a2 = replace(a, q='42') # E: Argument "q" to "replace" of "A" has incompatible type "str"; expected "int" +reveal_type(a2) # N: Revealed type is "__main__.A" + +[case testReplaceUnion] +# flags: --strict-optional +from typing import Generic, Union, TypeVar +from dataclasses import dataclass, replace, InitVar + +T = TypeVar('T') + +@dataclass +class A(Generic[T]): + x: T # exercises meet(T=int, int) = int + y: bool # exercises meet(bool, int) = bool + z: str # exercises meet(str, bytes) = + w: dict # exercises meet(dict, ) = + init_var: InitVar[int] # exercises (non-optional, optional) = non-optional + +@dataclass +class B: + x: int + y: int + z: bytes + init_var: int + + +a_or_b: Union[A[int], B] +_ = replace(a_or_b, x=42, y=True, init_var=42) +_ = replace(a_or_b, x=42, y=True) # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]" +_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected +_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[, ]"; expected +_ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool" + [builtins fixtures/dataclasses.pyi] +[case testReplaceUnionOfTypeVar] +# flags: --strict-optional +from typing import Generic, Union, TypeVar +from dataclasses import dataclass, replace + +@dataclass +class A: + x: int + y: int + z: str + w: dict + +class B: + pass + +TA = TypeVar('TA', bound=A) +TB = TypeVar('TB', bound=B) + +def f(b_or_t: Union[TA, TB, int]) -> None: + a2 = replace(b_or_t) # E: Argument 1 to "replace" has type "Union[TA, TB, int]" whose item "TB" is not bound to a dataclass # E: Argument 1 to "replace" has incompatible type "Union[TA, TB, int]" whose item "int" is not a dataclass + +[case testReplaceTypeVarBoundNotDataclass] +from dataclasses import dataclass, replace +from typing import Union, TypeVar + +TInt = TypeVar('TInt', bound=int) +TAny = TypeVar('TAny') +TNone = TypeVar('TNone', bound=None) +TUnion = TypeVar('TUnion', bound=Union[str, int]) + +def f1(t: TInt) -> None: + _ = replace(t, x=42) # E: Argument 1 to "replace" has a variable type "TInt" not bound to a dataclass + +def f2(t: TAny) -> TAny: + return replace(t, x='spam') # E: Argument 1 to "replace" has a variable type "TAny" not bound to a dataclass + +def f3(t: TNone) -> TNone: + return replace(t, x='spam') # E: Argument 1 to "replace" has a variable type "TNone" not bound to a dataclass + +def f4(t: TUnion) -> TUnion: + return replace(t, x='spam') # E: Argument 1 to "replace" has incompatible type "TUnion" whose item "str" is not a dataclass # E: Argument 1 to "replace" has incompatible type "TUnion" whose item "int" is not a dataclass + +[case testReplaceTypeVarBound] +from dataclasses import dataclass, replace +from typing import TypeVar + +@dataclass +class A: + x: int + +@dataclass +class B(A): + pass + +TA = TypeVar('TA', bound=A) + +def f(t: TA) -> TA: + t2 = replace(t, x=42) + reveal_type(t2) # N: Revealed type is "TA`-1" + _ = replace(t, x='42') # E: Argument "x" to "replace" of "TA" has incompatible type "str"; expected "int" + return t2 + +f(A(x=42)) +f(B(x=42)) + +[case testReplaceAny] +from dataclasses import replace +from typing import Any + +a: Any +a2 = replace(a) +reveal_type(a2) # N: Revealed type is "Any" + +[case testReplaceNotDataclass] +from dataclasses import replace + +replace(5) # E: Argument 1 to "replace" has incompatible type "int"; expected a dataclass + +class C: + pass + +replace(C()) # E: Argument 1 to "replace" has incompatible type "C"; expected a dataclass + +replace(None) # E: Argument 1 to "replace" has incompatible type "None"; expected a dataclass + +[case testReplaceGeneric] +from dataclasses import dataclass, replace, InitVar +from typing import ClassVar, Generic, TypeVar + +T = TypeVar('T') + +@dataclass +class A(Generic[T]): + x: T + +a = A(x=42) +reveal_type(a) # N: Revealed type is "__main__.A[builtins.int]" +a2 = replace(a, x=42) +reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" +a2 = replace(a, x='42') # E: Argument "x" to "replace" of "A[int]" has incompatible type "str"; expected "int" +reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" + [case testProtocolNoCrash] from typing import Protocol, Union, ClassVar from dataclasses import dataclass, field diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 28d51f1a4c30..fe5107b1529d 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1388,6 +1388,7 @@ class B(A): -> , m -> -> , m.B.__init__ + -> -> -> -> @@ -1419,6 +1420,7 @@ class B(A): -> -> , m.B.__init__ -> + -> -> -> -> diff --git a/test-data/unit/fine-grained-dataclass.test b/test-data/unit/fine-grained-dataclass.test new file mode 100644 index 000000000000..036d858ddf69 --- /dev/null +++ b/test-data/unit/fine-grained-dataclass.test @@ -0,0 +1,25 @@ +[case testReplace] +[file model.py] +from dataclasses import dataclass + +@dataclass +class Model: + x: int = 0 +[file replace.py] +from dataclasses import replace +from model import Model + +m = Model() +replace(m, x=42) + +[file model.py.2] +from dataclasses import dataclass + +@dataclass +class Model: + x: str = 'hello' + +[builtins fixtures/dataclasses.pyi] +[out] +== +replace.py:5: error: Argument "x" to "replace" of "Model" has incompatible type "int"; expected "str" diff --git a/test-data/unit/lib-stub/dataclasses.pyi b/test-data/unit/lib-stub/dataclasses.pyi index bd33b459266c..b2b48c2ae486 100644 --- a/test-data/unit/lib-stub/dataclasses.pyi +++ b/test-data/unit/lib-stub/dataclasses.pyi @@ -32,3 +32,5 @@ def field(*, class Field(Generic[_T]): pass + +def replace(__obj: _T, **changes: Any) -> _T: ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 93ff2da9f7b9..8c82b6843a3b 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2024,3 +2024,23 @@ def foo(callback: Callable[[], Any]) -> None: def call(callback: Callable[[Unpack[Ts]], Any], *args: Unpack[Ts]) -> Any: ... + +[case testDataclassReplace] +from dataclasses import dataclass, replace + +@dataclass +class A: + x: int + + +a = A(x=42) +a2 = replace(a, x=42) +reveal_type(a2) +a2 = replace() +a2 = replace(a, x='spam') +a2 = replace(a, x=42, q=42) +[out] +_testDataclassReplace.py:10: note: Revealed type is "_testDataclassReplace.A" +_testDataclassReplace.py:11: error: Too few arguments for "replace" +_testDataclassReplace.py:12: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int" +_testDataclassReplace.py:13: error: Unexpected keyword argument "q" for "replace" of "A" From 91b67405a7cd9da551411ccb4e28493647208c11 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 18 Jun 2023 14:12:50 -0700 Subject: [PATCH 0035/1617] Small improvements to protocol documentation (#15460) --- docs/source/protocols.rst | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 95b870265f73..3336d77cb397 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -3,26 +3,24 @@ Protocols and structural subtyping ================================== -Mypy supports two ways of deciding whether two classes are compatible -as types: nominal subtyping and structural subtyping. - -*Nominal* subtyping is strictly based on the class hierarchy. If class ``D`` -inherits class ``C``, it's also a subtype of ``C``, and instances of -``D`` can be used when ``C`` instances are expected. This form of -subtyping is used by default in mypy, since it's easy to understand -and produces clear and concise error messages, and since it matches -how the native :py:func:`isinstance ` check works -- based on class +The Python type system supports two ways of deciding whether two objects are +compatible as types: nominal subtyping and structural subtyping. + +*Nominal* subtyping is strictly based on the class hierarchy. If class ``Dog`` +inherits class ``Animal``, it's a subtype of ``Animal``. Instances of ``Dog`` +can be used when ``Animal`` instances are expected. This form of subtyping +subtyping is what Python's type system predominantly uses: it's easy to +understand and produces clear and concise error messages, and matches how the +native :py:func:`isinstance ` check works -- based on class hierarchy. -*Structural* subtyping is based on the operations that can be performed with an object. Class ``D`` is -a structural subtype of class ``C`` if the former has all attributes -and methods of the latter, and with compatible types. +*Structural* subtyping is based on the operations that can be performed with an +object. Class ``Dog`` is a structural subtype of class ``Animal`` if the former +has all attributes and methods of the latter, and with compatible types. -Structural subtyping can be seen as a static equivalent of duck -typing, which is well known to Python programmers. Mypy provides -support for structural subtyping via protocol classes described -below. See :pep:`544` for the detailed specification of protocols -and structural subtyping in Python. +Structural subtyping can be seen as a static equivalent of duck typing, which is +well known to Python programmers. See :pep:`544` for the detailed specification +of protocols and structural subtyping in Python. .. _predefined_protocols: @@ -60,8 +58,7 @@ For example, ``IntList`` below is iterable, over ``int`` values: :ref:`predefined_protocols_reference` lists all protocols defined in :py:mod:`typing` and the signatures of the corresponding methods you need to define -to implement each protocol (the signatures can be left out, as always, but mypy -won't type check unannotated methods). +to implement each protocol. Simple user-defined protocols ***************************** @@ -89,18 +86,12 @@ class: for item in items: item.close() - close_all([Resource(), open('some/file')]) # Okay! + close_all([Resource(), open('some/file')]) # OK ``Resource`` is a subtype of the ``SupportsClose`` protocol since it defines a compatible ``close`` method. Regular file objects returned by :py:func:`open` are similarly compatible with the protocol, as they support ``close()``. -.. note:: - - The ``Protocol`` base class is provided in the ``typing_extensions`` - package for Python 3.4-3.7. Starting with Python 3.8, ``Protocol`` - is included in the ``typing`` module. - Defining subprotocols and subclassing protocols *********************************************** @@ -171,6 +162,13 @@ abstract: ExplicitSubclass() # error: Cannot instantiate abstract class 'ExplicitSubclass' # with abstract attributes 'attr' and 'method' +Similarly, explicitly assigning to a protocol instance can be a way to ask the +type checker to verify that your class implements a protocol: + +.. code-block:: python + + _proto: SomeProto = cast(ExplicitSubclass, None) + Invariance of protocol attributes ********************************* From 0873230ee60461110bd7bfde7ca3886878aae389 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 18 Jun 2023 23:19:29 +0100 Subject: [PATCH 0036/1617] Foundations for non-linear solver and polymorphic application (#15287) Fixes #1317 Fixes #5738 Fixes #12919 (also fixes a `FIX` comment that is more than 10 years old according to git blame) Note: although this PR fixes most typical use-cases for type inference against generic functions, it is intentionally incomplete, and it is made in a way to limit implications to small scope. This PR has essentially three components (better infer, better solve, better apply - all three are needed for this MVP to work): * A "tiny" change to `constraints.py`: if the actual function is generic, we unify it with template before inferring constraints. This prevents leaking generic type variables of actual in the solutions (which makes no sense), but also introduces new kind of constraints `T <: F[S]`, where type variables we solve for appear in target type. These are much harder to solve, but also it is a great opportunity to play with them to prepare for single bin inference (if we will switch to it in some form later). Note unifying is not the best solution, but a good first approximation (see below on what is the best solution). * New more sophisticated constraint solver in `solve.py`. The full algorithm is outlined in the docstring for `solve_non_linear()`. It looks like it should be able to solve arbitrary constraints that don't (indirectly) contain "F-bounded" things like `T <: list[T]`. Very short the idea is to compute transitive closure, then organize constraints by topologically sorted SCCs. * Polymorphic type argument application in `checkexpr.py`. In cases where solver identifies there are free variables (e.g. we have just one constraint `S <: list[T]`, so `T` is free, and solution for `S` is `list[T]`) it will apply the solutions while creating new generic functions. For example, if we have a function `def [S, T] (fn: Callable[[S], T]) -> Callable[[S], T]` applied to a function `def [U] (x: U) -> U`, this will result in `def [T] (T) -> T` as the return. I want to put here some thoughts on the last ingredient, since it may be mysterious, but now it seems to me it is actually a very well defined procedure. The key point here is thinking about generic functions as about infinite intersections or infinite overloads. Now reducing these infinite overloads/intersections to finite ones it is easy to understand what is actually going on. For example, imagine we live in a world with just two types `int` and `str`. Now we have two functions: ```python T = TypeVar("T") S = TypeVar("S") U = TypeVar("U") def dec(fn: Callable[[T], S]) -> Callable[[T], S]: ... def id(x: U) -> U: ... ``` the first one can be seen as overload over ``` ((int) -> int) -> ((int) -> int) # 1 ((int) -> str) -> ((int) -> str) # 2 ((str) -> int) -> ((str) -> int) # 3 ((str) -> str) -> ((str) -> str) # 4 ``` and second as an overload over ``` (int) -> int (str) -> str ``` Now what happens when I apply `dec(id)`? We need to choose an overload that matches the argument (this is what we call type inference), but here is a trick, in this case two overloads of `dec` match the argument type. So (and btw I think we are missing this for real overloads) we construct a new overload that returns intersection of matching overloads `# 1` and `# 4`. So if we generalize this intuition to the general case, the inference is selection of an (infinite) parametrized subset among the bigger parameterized set of intersecting types. The only question is whether resulting infinite intersection is representable in our type system. For example `forall T. dict[T, T]` can make sense but is not representable, while `forall T. (T) -> T` is a well defined type. And finally, there is a very easy way to find whether a type is representable or not, we are already doing this during semantic analyzis. I use the same logic (that I used to view as ad-hoc because of lack of good syntax for callables) to bind type variables in the inferred type. OK, so here is the list of missing features, and some comments on them: 1. Instead of unifying the actual with template we should include actual's variables in variable set we solve for, as explained in https://github.com/python/mypy/issues/5738#issuecomment-511242682. Note however, this will work only together with the next item 2. We need to (iteratively) infer secondary constraints after linear propagation, e.g. `Sequence[T] <: S <: Sequence[U] => T <: U` 3. Support `ParamSpec` (and probably `TypeVarTuple`). Current support for applying callables with `ParamSpec` to generics is hacky, and kind of dead-end. Although `(Callable[P, T]) -> Callable[P, List[T]]` works when applied to `id`, even a slight variation like `(Callable[P, List[T]]) -> Callable[P, T]` fails. I think it needs to be re-worked in the framework I propose (the tests I added are just to be sure I don't break existing code) 4. Support actual types that are generic in type variables with upper bounds or values (likely we just need to be careful when propagating constraints and choosing free variable within an SCC). 5. Add backtracking for upper/lower bound choice. In general, in the current "Hanoi Tower" inference scheme it is very hard to backtrack, but in in this specific choice in the new solver, it should be totally possible to switch from lower to upper bound on a previous step, if we found no solution (or ``/`object`). 6. After we polish it, we can use the new solver in more situations, e.g. for return type context, and for unification during callable subtyping. 7. Long term we may want to allow instances to bind type variables, at least for things like `LRUCache[[x: T], T]`. Btw note that I apply force expansion to type aliases and callback protocols. Since I can't transform e.g. `A = Callable[[T], T]` into a generic callable without getting proper type. 8. We need to figure out a solution for scenarios where non-linear targets with free variables and constant targets mix without secondary constraints, like `T <: List[int], T <: List[S]`. I am planning to address at least majority of the above items, but I think we should move slowly, since in my experience type inference is really fragile topic with hard to predict long reaching consequences. Please play with this PR if you want to and have time, and please suggest tests to add. --- mypy/build.py | 106 +---- mypy/checkexpr.py | 145 ++++++- mypy/constraints.py | 25 +- mypy/graph_utils.py | 112 +++++ mypy/infer.py | 3 +- mypy/main.py | 5 + mypy/options.py | 2 + mypy/semanal.py | 2 +- mypy/solve.py | 401 +++++++++++++++--- mypy/test/testgraph.py | 11 +- mypy/typeanal.py | 13 - mypy/types.py | 13 + mypy/typestate.py | 6 +- test-data/unit/check-generics.test | 247 +++++++++++ .../unit/check-parameter-specification.test | 37 ++ test-data/unit/check-plugin-attrs.test | 3 +- test-data/unit/pythoneval.test | 60 ++- 17 files changed, 998 insertions(+), 193 deletions(-) create mode 100644 mypy/graph_utils.py diff --git a/mypy/build.py b/mypy/build.py index 7913eae9c6ed..2f556120d430 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -31,14 +31,12 @@ Callable, ClassVar, Dict, - Iterable, Iterator, Mapping, NamedTuple, NoReturn, Sequence, TextIO, - TypeVar, ) from typing_extensions import Final, TypeAlias as _TypeAlias @@ -47,6 +45,7 @@ import mypy.semanal_main from mypy.checker import TypeChecker from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error +from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.indirection import TypeIndirectionVisitor from mypy.messages import MessageBuilder from mypy.nodes import Import, ImportAll, ImportBase, ImportFrom, MypyFile, SymbolTable, TypeInfo @@ -3466,15 +3465,8 @@ def sorted_components( edges = {id: deps_filtered(graph, vertices, id, pri_max) for id in vertices} sccs = list(strongly_connected_components(vertices, edges)) # Topsort. - sccsmap = {id: frozenset(scc) for scc in sccs for id in scc} - data: dict[AbstractSet[str], set[AbstractSet[str]]] = {} - for scc in sccs: - deps: set[AbstractSet[str]] = set() - for id in scc: - deps.update(sccsmap[x] for x in deps_filtered(graph, vertices, id, pri_max)) - data[frozenset(scc)] = deps res = [] - for ready in topsort(data): + for ready in topsort(prepare_sccs(sccs, edges)): # Sort the sets in ready by reversed smallest State.order. Examples: # # - If ready is [{x}, {y}], x.order == 1, y.order == 2, we get @@ -3499,100 +3491,6 @@ def deps_filtered(graph: Graph, vertices: AbstractSet[str], id: str, pri_max: in ] -def strongly_connected_components( - vertices: AbstractSet[str], edges: dict[str, list[str]] -) -> Iterator[set[str]]: - """Compute Strongly Connected Components of a directed graph. - - Args: - vertices: the labels for the vertices - edges: for each vertex, gives the target vertices of its outgoing edges - - Returns: - An iterator yielding strongly connected components, each - represented as a set of vertices. Each input vertex will occur - exactly once; vertices not part of a SCC are returned as - singleton sets. - - From https://code.activestate.com/recipes/578507/. - """ - identified: set[str] = set() - stack: list[str] = [] - index: dict[str, int] = {} - boundaries: list[int] = [] - - def dfs(v: str) -> Iterator[set[str]]: - index[v] = len(stack) - stack.append(v) - boundaries.append(index[v]) - - for w in edges[v]: - if w not in index: - yield from dfs(w) - elif w not in identified: - while index[w] < boundaries[-1]: - boundaries.pop() - - if boundaries[-1] == index[v]: - boundaries.pop() - scc = set(stack[index[v] :]) - del stack[index[v] :] - identified.update(scc) - yield scc - - for v in vertices: - if v not in index: - yield from dfs(v) - - -T = TypeVar("T") - - -def topsort(data: dict[T, set[T]]) -> Iterable[set[T]]: - """Topological sort. - - Args: - data: A map from vertices to all vertices that it has an edge - connecting it to. NOTE: This data structure - is modified in place -- for normalization purposes, - self-dependencies are removed and entries representing - orphans are added. - - Returns: - An iterator yielding sets of vertices that have an equivalent - ordering. - - Example: - Suppose the input has the following structure: - - {A: {B, C}, B: {D}, C: {D}} - - This is normalized to: - - {A: {B, C}, B: {D}, C: {D}, D: {}} - - The algorithm will yield the following values: - - {D} - {B, C} - {A} - - From https://code.activestate.com/recipes/577413/. - """ - # TODO: Use a faster algorithm? - for k, v in data.items(): - v.discard(k) # Ignore self dependencies. - for item in set.union(*data.values()) - set(data.keys()): - data[item] = set() - while True: - ready = {item for item, dep in data.items() if not dep} - if not ready: - break - yield ready - data = {item: (dep - ready) for item, dep in data.items() if item not in ready} - assert not data, f"A cyclic dependency exists amongst {data!r}" - - def missing_stubs_file(cache_dir: str) -> str: return os.path.join(cache_dir, "missing_stubs") diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4b204a80c130..43896171eadc 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -12,7 +12,7 @@ import mypy.errorcodes as codes from mypy import applytype, erasetype, join, message_registry, nodes, operators, types from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals -from mypy.checkmember import analyze_member_access, type_object_type +from mypy.checkmember import analyze_member_access, freeze_all_type_vars, type_object_type from mypy.checkstrformat import StringFormatterChecker from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars from mypy.errors import ErrorWatcher, report_internal_error @@ -98,8 +98,15 @@ ) from mypy.semanal_enum import ENUM_BASES from mypy.state import state -from mypy.subtypes import is_equivalent, is_same_type, is_subtype, non_method_protocol_members +from mypy.subtypes import ( + find_member, + is_equivalent, + is_same_type, + is_subtype, + non_method_protocol_members, +) from mypy.traverser import has_await_expression +from mypy.type_visitor import TypeTranslator from mypy.typeanal import ( check_for_explicit_any, has_any_from_unimported_type, @@ -114,6 +121,7 @@ false_only, fixup_partial_type, function_type, + get_type_vars, is_literal_type_like, make_simplified_union, simple_literal_type, @@ -146,6 +154,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarLikeType, TypeVarTupleType, TypeVarType, UninhabitedType, @@ -300,6 +309,7 @@ def __init__( # on whether current expression is a callee, to give better error messages # related to type context. self.is_callee = False + type_state.infer_polymorphic = self.chk.options.new_type_inference def reset(self) -> None: self.resolved_type = {} @@ -1791,6 +1801,51 @@ def infer_function_type_arguments( inferred_args[0] = self.named_type("builtins.str") elif not first_arg or not is_subtype(self.named_type("builtins.str"), first_arg): self.chk.fail(message_registry.KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE, context) + + if self.chk.options.new_type_inference and any( + a is None + or isinstance(get_proper_type(a), UninhabitedType) + or set(get_type_vars(a)) & set(callee_type.variables) + for a in inferred_args + ): + # If the regular two-phase inference didn't work, try inferring type + # variables while allowing for polymorphic solutions, i.e. for solutions + # potentially involving free variables. + # TODO: support the similar inference for return type context. + poly_inferred_args = infer_function_type_arguments( + callee_type, + arg_types, + arg_kinds, + formal_to_actual, + context=self.argument_infer_context(), + strict=self.chk.in_checked_function(), + allow_polymorphic=True, + ) + for i, pa in enumerate(get_proper_types(poly_inferred_args)): + if isinstance(pa, (NoneType, UninhabitedType)) or has_erased_component(pa): + # Indicate that free variables should not be applied in the call below. + poly_inferred_args[i] = None + poly_callee_type = self.apply_generic_arguments( + callee_type, poly_inferred_args, context + ) + yes_vars = poly_callee_type.variables + no_vars = {v for v in callee_type.variables if v not in poly_callee_type.variables} + if not set(get_type_vars(poly_callee_type)) & no_vars: + # Try applying inferred polymorphic type if possible, e.g. Callable[[T], T] can + # be interpreted as def [T] (T) -> T, but dict[T, T] cannot be expressed. + applied = apply_poly(poly_callee_type, yes_vars) + if applied is not None and poly_inferred_args != [UninhabitedType()] * len( + poly_inferred_args + ): + freeze_all_type_vars(applied) + return applied + # If it didn't work, erase free variables as , to avoid confusing errors. + inferred_args = [ + expand_type(a, {v.id: UninhabitedType() for v in callee_type.variables}) + if a is not None + else None + for a in inferred_args + ] else: # In dynamically typed functions use implicit 'Any' types for # type variables. @@ -5393,6 +5448,92 @@ def replace_callable_return_type(c: CallableType, new_ret_type: Type) -> Callabl return c.copy_modified(ret_type=new_ret_type) +def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> Optional[CallableType]: + """Make free type variables generic in the type if possible. + + This will translate the type `tp` while trying to create valid bindings for + type variables `poly_tvars` while traversing the type. This follows the same rules + as we do during semantic analysis phase, examples: + * Callable[Callable[[T], T], T] -> def [T] (def (T) -> T) -> T + * Callable[[], Callable[[T], T]] -> def () -> def [T] (T -> T) + * List[T] -> None (not possible) + """ + try: + return tp.copy_modified( + arg_types=[t.accept(PolyTranslator(poly_tvars)) for t in tp.arg_types], + ret_type=tp.ret_type.accept(PolyTranslator(poly_tvars)), + variables=[], + ) + except PolyTranslationError: + return None + + +class PolyTranslationError(Exception): + pass + + +class PolyTranslator(TypeTranslator): + """Make free type variables generic in the type if possible. + + See docstring for apply_poly() for details. + """ + + def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None: + self.poly_tvars = set(poly_tvars) + # This is a simplified version of TypeVarScope used during semantic analysis. + self.bound_tvars: set[TypeVarLikeType] = set() + self.seen_aliases: set[TypeInfo] = set() + + def visit_callable_type(self, t: CallableType) -> Type: + found_vars = set() + for arg in t.arg_types: + found_vars |= set(get_type_vars(arg)) & self.poly_tvars + + found_vars -= self.bound_tvars + self.bound_tvars |= found_vars + result = super().visit_callable_type(t) + self.bound_tvars -= found_vars + + assert isinstance(result, ProperType) and isinstance(result, CallableType) + result.variables = list(result.variables) + list(found_vars) + return result + + def visit_type_var(self, t: TypeVarType) -> Type: + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_type_var(t) + + def visit_param_spec(self, t: ParamSpecType) -> Type: + # TODO: Support polymorphic apply for ParamSpec. + raise PolyTranslationError() + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + # TODO: Support polymorphic apply for TypeVarTuple. + raise PolyTranslationError() + + def visit_type_alias_type(self, t: TypeAliasType) -> Type: + if not t.args: + return t.copy_modified() + if not t.is_recursive: + return get_proper_type(t).accept(self) + # We can't handle polymorphic application for recursive generic aliases + # without risking an infinite recursion, just give up for now. + raise PolyTranslationError() + + def visit_instance(self, t: Instance) -> Type: + # There is the same problem with callback protocols as with aliases + # (callback protocols are essentially more flexible aliases to callables). + # Note: consider supporting bindings in instances, e.g. LRUCache[[x: T], T]. + if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]: + if t.type in self.seen_aliases: + raise PolyTranslationError() + self.seen_aliases.add(t.type) + call = find_member("__call__", t, t, is_operator=True) + assert call is not None + return call.accept(self) + return super().visit_instance(t) + + class ArgInferSecondPassQuery(types.BoolTypeQuery): """Query whether an argument type should be inferred in the second pass. diff --git a/mypy/constraints.py b/mypy/constraints.py index 33230871b505..803b9819be6f 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -886,7 +886,30 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: param_spec = template.param_spec() if param_spec is None: # FIX verify argument counts - # FIX what if one of the functions is generic + # TODO: Erase template variables if it is generic? + if ( + type_state.infer_polymorphic + and cactual.variables + and cactual.param_spec() is None + # Technically, the correct inferred type for application of e.g. + # Callable[..., T] -> Callable[..., T] (with literal ellipsis), to a generic + # like U -> U, should be Callable[..., Any], but if U is a self-type, we can + # allow it to leak, to be later bound to self. A bunch of existing code + # depends on this old behaviour. + and not any(tv.id.raw_id == 0 for tv in cactual.variables) + ): + # If actual is generic, unify it with template. Note: this is + # not an ideal solution (which would be adding the generic variables + # to the constraint inference set), but it's a good first approximation, + # and this will prevent leaking these variables in the solutions. + # Note: this may infer constraints like T <: S or T <: List[S] + # that contain variables in the target. + unified = mypy.subtypes.unify_generic_callable( + cactual, template, ignore_return=True + ) + if unified is not None: + cactual = unified + res.extend(infer_constraints(cactual, template, neg_op(self.direction))) # We can't infer constraints from arguments if the template is Callable[..., T] # (with literal '...'). diff --git a/mypy/graph_utils.py b/mypy/graph_utils.py new file mode 100644 index 000000000000..399301a6b0fd --- /dev/null +++ b/mypy/graph_utils.py @@ -0,0 +1,112 @@ +"""Helpers for manipulations with graphs.""" + +from __future__ import annotations + +from typing import AbstractSet, Iterable, Iterator, TypeVar + +T = TypeVar("T") + + +def strongly_connected_components( + vertices: AbstractSet[T], edges: dict[T, list[T]] +) -> Iterator[set[T]]: + """Compute Strongly Connected Components of a directed graph. + + Args: + vertices: the labels for the vertices + edges: for each vertex, gives the target vertices of its outgoing edges + + Returns: + An iterator yielding strongly connected components, each + represented as a set of vertices. Each input vertex will occur + exactly once; vertices not part of a SCC are returned as + singleton sets. + + From https://code.activestate.com/recipes/578507/. + """ + identified: set[T] = set() + stack: list[T] = [] + index: dict[T, int] = {} + boundaries: list[int] = [] + + def dfs(v: T) -> Iterator[set[T]]: + index[v] = len(stack) + stack.append(v) + boundaries.append(index[v]) + + for w in edges[v]: + if w not in index: + yield from dfs(w) + elif w not in identified: + while index[w] < boundaries[-1]: + boundaries.pop() + + if boundaries[-1] == index[v]: + boundaries.pop() + scc = set(stack[index[v] :]) + del stack[index[v] :] + identified.update(scc) + yield scc + + for v in vertices: + if v not in index: + yield from dfs(v) + + +def prepare_sccs( + sccs: list[set[T]], edges: dict[T, list[T]] +) -> dict[AbstractSet[T], set[AbstractSet[T]]]: + """Use original edges to organize SCCs in a graph by dependencies between them.""" + sccsmap = {v: frozenset(scc) for scc in sccs for v in scc} + data: dict[AbstractSet[T], set[AbstractSet[T]]] = {} + for scc in sccs: + deps: set[AbstractSet[T]] = set() + for v in scc: + deps.update(sccsmap[x] for x in edges[v]) + data[frozenset(scc)] = deps + return data + + +def topsort(data: dict[T, set[T]]) -> Iterable[set[T]]: + """Topological sort. + + Args: + data: A map from vertices to all vertices that it has an edge + connecting it to. NOTE: This data structure + is modified in place -- for normalization purposes, + self-dependencies are removed and entries representing + orphans are added. + + Returns: + An iterator yielding sets of vertices that have an equivalent + ordering. + + Example: + Suppose the input has the following structure: + + {A: {B, C}, B: {D}, C: {D}} + + This is normalized to: + + {A: {B, C}, B: {D}, C: {D}, D: {}} + + The algorithm will yield the following values: + + {D} + {B, C} + {A} + + From https://code.activestate.com/recipes/577413/. + """ + # TODO: Use a faster algorithm? + for k, v in data.items(): + v.discard(k) # Ignore self dependencies. + for item in set.union(*data.values()) - set(data.keys()): + data[item] = set() + while True: + ready = {item for item, dep in data.items() if not dep} + if not ready: + break + yield ready + data = {item: (dep - ready) for item, dep in data.items() if item not in ready} + assert not data, f"A cyclic dependency exists amongst {data!r}" diff --git a/mypy/infer.py b/mypy/infer.py index fbec3d7c4278..66ca4169e2ff 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -36,6 +36,7 @@ def infer_function_type_arguments( formal_to_actual: list[list[int]], context: ArgumentInferContext, strict: bool = True, + allow_polymorphic: bool = False, ) -> list[Type | None]: """Infer the type arguments of a generic function. @@ -57,7 +58,7 @@ def infer_function_type_arguments( # Solve constraints. type_vars = callee_type.type_var_ids() - return solve_constraints(type_vars, constraints, strict) + return solve_constraints(type_vars, constraints, strict, allow_polymorphic) def infer_type_arguments( diff --git a/mypy/main.py b/mypy/main.py index 81a0a045745b..b60c5b2a6bba 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -983,6 +983,11 @@ def add_invertible_flag( dest="custom_typing_module", help="Use a custom typing module", ) + internals_group.add_argument( + "--new-type-inference", + action="store_true", + help="Enable new experimental type inference algorithm", + ) internals_group.add_argument( "--disable-recursive-aliases", action="store_true", diff --git a/mypy/options.py b/mypy/options.py index 2785d2034c54..f75734124eb0 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -344,6 +344,8 @@ def __init__(self) -> None: # skip most errors after this many messages have been reported. # -1 means unlimited. self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD + # Enable new experimental type inference algorithm. + self.new_type_inference = False # Disable recursive type aliases (currently experimental) self.disable_recursive_aliases = False # Deprecated reverse version of the above, do not use. diff --git a/mypy/semanal.py b/mypy/semanal.py index 073bde661617..249a57d550b2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -234,7 +234,6 @@ fix_instance_types, has_any_from_unimported_type, no_subscript_builtin_alias, - remove_dups, type_constructors, ) from mypy.typeops import function_type, get_type_vars, try_getting_str_literals_from_type @@ -277,6 +276,7 @@ get_proper_type, get_proper_types, is_named_instance, + remove_dups, ) from mypy.types_utils import is_invalid_recursive_alias, store_argument_type from mypy.typevars import fill_typevars diff --git a/mypy/solve.py b/mypy/solve.py index b8304d29c1ce..6693d66f3479 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -2,27 +2,35 @@ from __future__ import annotations -from collections import defaultdict +from typing import Iterable -from mypy.constraints import SUPERTYPE_OF, Constraint +from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, neg_op +from mypy.expandtype import expand_type +from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.join import join_types from mypy.meet import meet_types from mypy.subtypes import is_subtype +from mypy.typeops import get_type_vars from mypy.types import ( AnyType, ProperType, Type, TypeOfAny, TypeVarId, + TypeVarType, UninhabitedType, UnionType, get_proper_type, + remove_dups, ) from mypy.typestate import type_state def solve_constraints( - vars: list[TypeVarId], constraints: list[Constraint], strict: bool = True + vars: list[TypeVarId], + constraints: list[Constraint], + strict: bool = True, + allow_polymorphic: bool = False, ) -> list[Type | None]: """Solve type constraints. @@ -33,62 +41,355 @@ def solve_constraints( pick NoneType as the value of the type variable. If strict=False, pick AnyType. """ + if not vars: + return [] + if allow_polymorphic: + # Constraints like T :> S and S <: T are semantically the same, but they are + # represented differently. Normalize the constraint list w.r.t this equivalence. + constraints = normalize_constraints(constraints, vars) + # Collect a list of constraints for each type variable. - cmap: dict[TypeVarId, list[Constraint]] = defaultdict(list) + cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars} for con in constraints: - cmap[con.type_var].append(con) + if con.type_var in vars: + cmap[con.type_var].append(con) + + if allow_polymorphic: + solutions = solve_non_linear(vars, constraints, cmap) + else: + solutions = {} + for tv, cs in cmap.items(): + if not cs: + continue + lowers = [c.target for c in cs if c.op == SUPERTYPE_OF] + uppers = [c.target for c in cs if c.op == SUBTYPE_OF] + solutions[tv] = solve_one(lowers, uppers, []) res: list[Type | None] = [] + for v in vars: + if v in solutions: + res.append(solutions[v]) + else: + # No constraints for type variable -- 'UninhabitedType' is the most specific type. + candidate: Type + if strict: + candidate = UninhabitedType() + candidate.ambiguous = True + else: + candidate = AnyType(TypeOfAny.special_form) + res.append(candidate) + return res + + +def solve_non_linear( + vars: list[TypeVarId], constraints: list[Constraint], cmap: dict[TypeVarId, list[Constraint]] +) -> dict[TypeVarId, Type | None]: + """Solve set of constraints that may include non-linear ones, like T <: List[S]. - # Solve each type variable separately. + The whole algorithm consists of five steps: + * Propagate via linear constraints to get all possible constraints for each variable + * Find dependencies between type variables, group them in SCCs, and sort topologically + * Check all SCC are intrinsically linear, we can't solve (express) T <: List[T] + * Variables in leaf SCCs that don't have constant bounds are free (choose one per SCC) + * Solve constraints iteratively starting from leafs, updating targets after each step. + """ + extra_constraints = [] for tvar in vars: - bottom: Type | None = None - top: Type | None = None - candidate: Type | None = None - - # Process each constraint separately, and calculate the lower and upper - # bounds based on constraints. Note that we assume that the constraint - # targets do not have constraint references. - for c in cmap.get(tvar, []): - if c.op == SUPERTYPE_OF: - if bottom is None: - bottom = c.target - else: - if type_state.infer_unions: - # This deviates from the general mypy semantics because - # recursive types are union-heavy in 95% of cases. - bottom = UnionType.make_union([bottom, c.target]) - else: - bottom = join_types(bottom, c.target) + extra_constraints.extend(propagate_constraints_for(tvar, SUBTYPE_OF, cmap)) + extra_constraints.extend(propagate_constraints_for(tvar, SUPERTYPE_OF, cmap)) + constraints += remove_dups(extra_constraints) + + # Recompute constraint map after propagating. + cmap = {tv: [] for tv in vars} + for con in constraints: + if con.type_var in vars: + cmap[con.type_var].append(con) + + dmap = compute_dependencies(cmap) + sccs = list(strongly_connected_components(set(vars), dmap)) + if all(check_linear(scc, cmap) for scc in sccs): + raw_batches = list(topsort(prepare_sccs(sccs, dmap))) + leafs = raw_batches[0] + free_vars = [] + for scc in leafs: + # If all constrain targets in this SCC are type variables within the + # same SCC then the only meaningful solution we can express, is that + # each variable is equal to a new free variable. For example if we + # have T <: S, S <: U, we deduce: T = S = U = . + if all( + isinstance(c.target, TypeVarType) and c.target.id in vars + for tv in scc + for c in cmap[tv] + ): + # For convenience with current type application machinery, we randomly + # choose one of the existing type variables in SCC and designate it as free + # instead of defining a new type variable as a common solution. + # TODO: be careful about upper bounds (or values) when introducing free vars. + free_vars.append(sorted(scc, key=lambda x: x.raw_id)[0]) + + # Flatten the SCCs that are independent, we can solve them together, + # since we don't need to update any targets in between. + batches = [] + for batch in raw_batches: + next_bc = [] + for scc in batch: + next_bc.extend(list(scc)) + batches.append(next_bc) + + solutions: dict[TypeVarId, Type | None] = {} + for flat_batch in batches: + solutions.update(solve_iteratively(flat_batch, cmap, free_vars)) + # We remove the solutions like T = T for free variables. This will indicate + # to the apply function, that they should not be touched. + # TODO: return list of free type variables explicitly, this logic is fragile + # (but if we do, we need to be careful everything works in incremental modes). + for tv in free_vars: + if tv in solutions: + del solutions[tv] + return solutions + return {} + + +def solve_iteratively( + batch: list[TypeVarId], cmap: dict[TypeVarId, list[Constraint]], free_vars: list[TypeVarId] +) -> dict[TypeVarId, Type | None]: + """Solve constraints sequentially, updating constraint targets after each step. + + We solve for type variables that appear in `batch`. If a constraint target is not constant + (i.e. constraint looks like T :> F[S, ...]), we substitute solutions found so far in + the target F[S, ...]. This way we can gradually solve for all variables in the batch taking + one solvable variable at a time (i.e. such a variable that has at least one constant bound). + + Importantly, variables in free_vars are considered constants, so for example if we have just + one initial constraint T <: List[S], we will have two SCCs {T} and {S}, then we first + designate S as free, and therefore T = List[S] is a valid solution for T. + """ + solutions = {} + relevant_constraints = [] + for tv in batch: + relevant_constraints.extend(cmap.get(tv, [])) + lowers, uppers = transitive_closure(batch, relevant_constraints) + s_batch = set(batch) + not_allowed_vars = [v for v in batch if v not in free_vars] + while s_batch: + for tv in s_batch: + if any(not get_vars(l, not_allowed_vars) for l in lowers[tv]) or any( + not get_vars(u, not_allowed_vars) for u in uppers[tv] + ): + solvable_tv = tv + break + else: + break + # Solve each solvable type variable separately. + s_batch.remove(solvable_tv) + result = solve_one(lowers[solvable_tv], uppers[solvable_tv], not_allowed_vars) + solutions[solvable_tv] = result + if result is None: + # TODO: support backtracking lower/upper bound choices + # (will require switching this function from iterative to recursive). + continue + # Update the (transitive) constraints if there is a solution. + subs = {solvable_tv: result} + lowers = {tv: {expand_type(l, subs) for l in lowers[tv]} for tv in lowers} + uppers = {tv: {expand_type(u, subs) for u in uppers[tv]} for tv in uppers} + for v in cmap: + for c in cmap[v]: + c.target = expand_type(c.target, subs) + return solutions + + +def solve_one( + lowers: Iterable[Type], uppers: Iterable[Type], not_allowed_vars: list[TypeVarId] +) -> Type | None: + """Solve constraints by finding by using meets of upper bounds, and joins of lower bounds.""" + bottom: Type | None = None + top: Type | None = None + candidate: Type | None = None + + # Process each bound separately, and calculate the lower and upper + # bounds based on constraints. Note that we assume that the constraint + # targets do not have constraint references. + for target in lowers: + # There may be multiple steps needed to solve all vars within a + # (linear) SCC. We ignore targets pointing to not yet solved vars. + if get_vars(target, not_allowed_vars): + continue + if bottom is None: + bottom = target + else: + if type_state.infer_unions: + # This deviates from the general mypy semantics because + # recursive types are union-heavy in 95% of cases. + bottom = UnionType.make_union([bottom, target]) else: - if top is None: - top = c.target - else: - top = meet_types(top, c.target) - - p_top = get_proper_type(top) - p_bottom = get_proper_type(bottom) - if isinstance(p_top, AnyType) or isinstance(p_bottom, AnyType): - source_any = top if isinstance(p_top, AnyType) else bottom - assert isinstance(source_any, ProperType) and isinstance(source_any, AnyType) - res.append(AnyType(TypeOfAny.from_another_any, source_any=source_any)) + bottom = join_types(bottom, target) + + for target in uppers: + # Same as above. + if get_vars(target, not_allowed_vars): continue - elif bottom is None: - if top: - candidate = top + if top is None: + top = target + else: + top = meet_types(top, target) + + p_top = get_proper_type(top) + p_bottom = get_proper_type(bottom) + if isinstance(p_top, AnyType) or isinstance(p_bottom, AnyType): + source_any = top if isinstance(p_top, AnyType) else bottom + assert isinstance(source_any, ProperType) and isinstance(source_any, AnyType) + return AnyType(TypeOfAny.from_another_any, source_any=source_any) + elif bottom is None: + if top: + candidate = top + else: + # No constraints for type variable + return None + elif top is None: + candidate = bottom + elif is_subtype(bottom, top): + candidate = bottom + else: + candidate = None + return candidate + + +def normalize_constraints( + constraints: list[Constraint], vars: list[TypeVarId] +) -> list[Constraint]: + """Normalize list of constraints (to simplify life for the non-linear solver). + + This includes two things currently: + * Complement T :> S by S <: T + * Remove strict duplicates + """ + res = constraints.copy() + for c in constraints: + if isinstance(c.target, TypeVarType): + res.append(Constraint(c.target, neg_op(c.op), c.origin_type_var)) + return [c for c in remove_dups(constraints) if c.type_var in vars] + + +def propagate_constraints_for( + var: TypeVarId, direction: int, cmap: dict[TypeVarId, list[Constraint]] +) -> list[Constraint]: + """Propagate via linear constraints to get additional constraints for `var`. + + For example if we have constraints: + [T <: int, S <: T, S :> str] + we can add two more + [S <: int, T :> str] + """ + extra_constraints = [] + seen = set() + front = [var] + if cmap[var]: + var_def = cmap[var][0].origin_type_var + else: + return [] + while front: + tv = front.pop(0) + for c in cmap[tv]: + if ( + isinstance(c.target, TypeVarType) + and c.target.id not in seen + and c.target.id in cmap + and c.op == direction + ): + front.append(c.target.id) + seen.add(c.target.id) + elif c.op == direction: + new_c = Constraint(var_def, direction, c.target) + if new_c not in cmap[var]: + extra_constraints.append(new_c) + return extra_constraints + + +def transitive_closure( + tvars: list[TypeVarId], constraints: list[Constraint] +) -> tuple[dict[TypeVarId, set[Type]], dict[TypeVarId, set[Type]]]: + """Find transitive closure for given constraints on type variables. + + Transitive closure gives maximal set of lower/upper bounds for each type variable, + such that we cannot deduce any further bounds by chaining other existing bounds. + + For example if we have initial constraints [T <: S, S <: U, U <: int], the transitive + closure is given by: + * {} <: T <: {S, U, int} + * {T} <: S <: {U, int} + * {T, S} <: U <: {int} + """ + # TODO: merge propagate_constraints_for() into this function. + # TODO: add secondary constraints here to make the algorithm complete. + uppers: dict[TypeVarId, set[Type]] = {tv: set() for tv in tvars} + lowers: dict[TypeVarId, set[Type]] = {tv: set() for tv in tvars} + graph: set[tuple[TypeVarId, TypeVarId]] = set() + + # Prime the closure with the initial trivial values. + for c in constraints: + if isinstance(c.target, TypeVarType) and c.target.id in tvars: + if c.op == SUBTYPE_OF: + graph.add((c.type_var, c.target.id)) else: - # No constraints for type variable -- 'UninhabitedType' is the most specific type. - if strict: - candidate = UninhabitedType() - candidate.ambiguous = True - else: - candidate = AnyType(TypeOfAny.special_form) - elif top is None: - candidate = bottom - elif is_subtype(bottom, top): - candidate = bottom + graph.add((c.target.id, c.type_var)) + if c.op == SUBTYPE_OF: + uppers[c.type_var].add(c.target) else: - candidate = None - res.append(candidate) + lowers[c.type_var].add(c.target) + + # At this stage we know that constant bounds have been propagated already, so we + # only need to propagate linear constraints. + for c in constraints: + if isinstance(c.target, TypeVarType) and c.target.id in tvars: + if c.op == SUBTYPE_OF: + lower, upper = c.type_var, c.target.id + else: + lower, upper = c.target.id, c.type_var + extras = { + (l, u) for l in tvars for u in tvars if (l, lower) in graph and (upper, u) in graph + } + graph |= extras + for u in tvars: + if (upper, u) in graph: + lowers[u] |= lowers[lower] + for l in tvars: + if (l, lower) in graph: + uppers[l] |= uppers[upper] + return lowers, uppers + +def compute_dependencies( + cmap: dict[TypeVarId, list[Constraint]] +) -> dict[TypeVarId, list[TypeVarId]]: + """Compute dependencies between type variables induced by constraints. + + If we have a constraint like T <: List[S], we say that T depends on S, since + we will need to solve for S first before we can solve for T. + """ + res = {} + vars = list(cmap.keys()) + for tv in cmap: + deps = set() + for c in cmap[tv]: + deps |= get_vars(c.target, vars) + res[tv] = list(deps) return res + + +def check_linear(scc: set[TypeVarId], cmap: dict[TypeVarId, list[Constraint]]) -> bool: + """Check there are only linear constraints between type variables in SCC. + + Linear are constraints like T <: S (while T <: F[S] are non-linear). + """ + for tv in scc: + if any( + get_vars(c.target, list(scc)) and not isinstance(c.target, TypeVarType) + for c in cmap[tv] + ): + return False + return True + + +def get_vars(target: Type, vars: list[TypeVarId]) -> set[TypeVarId]: + """Find type variables for which we are solving in a target type.""" + return {tv.id for tv in get_type_vars(target)} & set(vars) diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index ce7697142ff2..b0d148d5ae9c 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -5,17 +5,10 @@ import sys from typing import AbstractSet -from mypy.build import ( - BuildManager, - BuildSourceSet, - State, - order_ascc, - sorted_components, - strongly_connected_components, - topsort, -) +from mypy.build import BuildManager, BuildSourceSet, State, order_ascc, sorted_components from mypy.errors import Errors from mypy.fscache import FileSystemCache +from mypy.graph_utils import strongly_connected_components, topsort from mypy.modulefinder import SearchPaths from mypy.options import Options from mypy.plugin import Plugin diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d1e6e315b9e3..39a44a289365 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1843,19 +1843,6 @@ def set_any_tvars( return TypeAliasType(node, args, newline, newcolumn) -def remove_dups(tvars: list[T]) -> list[T]: - if len(tvars) <= 1: - return tvars - # Get unique elements in order of appearance - all_tvars: set[T] = set() - new_tvars: list[T] = [] - for t in tvars: - if t not in all_tvars: - new_tvars.append(t) - all_tvars.add(t) - return new_tvars - - def flatten_tvars(lists: list[list[T]]) -> list[T]: result: list[T] = [] for lst in lists: diff --git a/mypy/types.py b/mypy/types.py index 5fbdd385826c..e4c22da12603 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3475,6 +3475,19 @@ def callable_with_ellipsis(any_type: AnyType, ret_type: Type, fallback: Instance ) +def remove_dups(types: list[T]) -> list[T]: + if len(types) <= 1: + return types + # Get unique elements in order of appearance + all_types: set[T] = set() + new_types: list[T] = [] + for t in types: + if t not in all_types: + new_types.append(t) + all_types.add(t) + return new_types + + # This cyclic import is unfortunate, but to avoid it we would need to move away all uses # of get_proper_type() from types.py. Majority of them have been removed, but few remaining # are quite tricky to get rid of, but ultimately we want to do it at some point. diff --git a/mypy/typestate.py b/mypy/typestate.py index 9f65481e5e94..ff5933af5928 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -93,6 +93,9 @@ class TypeState: inferring: Final[list[tuple[Type, Type]]] # Whether to use joins or unions when solving constraints, see checkexpr.py for details. infer_unions: bool + # Whether to use new type inference algorithm that can infer polymorphic types. + # This is temporary and will be removed soon when new algorithm is more polished. + infer_polymorphic: bool # N.B: We do all of the accesses to these properties through # TypeState, instead of making these classmethods and accessing @@ -110,6 +113,7 @@ def __init__(self) -> None: self._assuming_proper = [] self.inferring = [] self.infer_unions = False + self.infer_polymorphic = False def is_assumed_subtype(self, left: Type, right: Type) -> bool: for l, r in reversed(self._assuming): @@ -311,7 +315,7 @@ def add_all_protocol_deps(self, deps: dict[str, set[str]]) -> None: def reset_global_state() -> None: """Reset most existing global state. - Currently most of it is in this module. Few exceptions are strict optional status and + Currently most of it is in this module. Few exceptions are strict optional status and functools.lru_cache. """ type_state.reset_all_subtype_caches() diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 06b80be85096..b78fd21d4817 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2733,3 +2733,250 @@ dict1: Any dict2 = {"a": C1(), **{x: C2() for x in dict1}} reveal_type(dict2) # N: Revealed type is "builtins.dict[Any, __main__.B]" [builtins fixtures/dict.pyi] + +-- Type inference for generic decorators applied to generic callables +-- ------------------------------------------------------------------ + +[case testInferenceAgainstGenericCallable] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +X = TypeVar('X') +T = TypeVar('T') + +def foo(x: Callable[[int], X]) -> List[X]: + ... +def bar(x: Callable[[X], int]) -> List[X]: + ... + +def id(x: T) -> T: + ... +reveal_type(foo(id)) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(bar(id)) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableNoLeak] +# flags: --new-type-inference +from typing import TypeVar, Callable + +T = TypeVar('T') + +def f(x: Callable[..., T]) -> T: + return x() + +def tpl(x: T) -> T: + return x + +# This is valid because of "..." +reveal_type(f(tpl)) # N: Revealed type is "Any" +[out] + +[case testInferenceAgainstGenericCallableChain] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +X = TypeVar('X') +T = TypeVar('T') + +def chain(f: Callable[[X], T], g: Callable[[T], int]) -> Callable[[X], int]: ... +def id(x: T) -> T: + ... +reveal_type(chain(id, id)) # N: Revealed type is "def (builtins.int) -> builtins.int" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGeneric] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[S], T]) -> Callable[[S], List[T]]: + ... +def id(x: U) -> U: + ... +reveal_type(dec(id)) # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1]" + +@dec +def same(x: U) -> U: + ... +reveal_type(same) # N: Revealed type is "def [S] (S`3) -> builtins.list[S`3]" +reveal_type(same(42)) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGenericReverse] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[S], List[T]]) -> Callable[[S], T]: + ... +def id(x: U) -> U: + ... +reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`2]) -> T`2" + +@dec +def same(x: U) -> U: + ... +reveal_type(same) # N: Revealed type is "def [T] (builtins.list[T`4]) -> T`4" +reveal_type(same([42])) # N: Revealed type is "builtins.int" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGenericArg] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[S], T]) -> Callable[[S], T]: + ... +def test(x: U) -> List[U]: + ... +reveal_type(dec(test)) # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1]" + +@dec +def single(x: U) -> List[U]: + ... +reveal_type(single) # N: Revealed type is "def [S] (S`3) -> builtins.list[S`3]" +reveal_type(single(42)) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGenericChain] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def comb(f: Callable[[T], S], g: Callable[[S], U]) -> Callable[[T], U]: ... +def id(x: U) -> U: + ... +reveal_type(comb(id, id)) # N: Revealed type is "def [T] (T`1) -> T`1" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGenericNonLinear] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]: + def inner(x: S) -> List[T]: + return [f(x) for f in fs] + return inner + +# Errors caused by arg *name* mismatch are truly cryptic, but this is a known issue :/ +def id(__x: U) -> U: + ... +fs = [id, id, id] +reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`3) -> builtins.list[S`3]" +reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCurry] +# flags: --new-type-inference +from typing import Callable, List, TypeVar + +S = TypeVar("S") +T = TypeVar("T") +U = TypeVar("U") +V = TypeVar("V") + +def dec1(f: Callable[[T], S]) -> Callable[[], Callable[[T], S]]: ... +def dec2(f: Callable[[T, U], S]) -> Callable[[U], Callable[[T], S]]: ... + +def test1(x: V) -> V: ... +def test2(x: V, y: V) -> V: ... + +reveal_type(dec1(test1)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" +# TODO: support this situation +reveal_type(dec2(test2)) # N: Revealed type is "def (builtins.object) -> def (builtins.object) -> builtins.object" +[builtins fixtures/paramspec.pyi] + +[case testInferenceAgainstGenericCallableGenericAlias] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +A = Callable[[S], T] +B = Callable[[S], List[T]] + +def dec(f: A[S, T]) -> B[S, T]: + ... +def id(x: U) -> U: + ... +reveal_type(dec(id)) # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableGenericProtocol] +# flags: --strict-optional --new-type-inference +from typing import TypeVar, Protocol, Generic, Optional + +T = TypeVar('T') + +class F(Protocol[T]): + def __call__(self, __x: T) -> T: ... + +def lift(f: F[T]) -> F[Optional[T]]: ... +def g(x: T) -> T: + return x + +reveal_type(lift(g)) # N: Revealed type is "def [T] (Union[T`1, None]) -> Union[T`1, None]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericSplitOrder] +# flags: --strict-optional --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[T], S], g: Callable[[T], int]) -> Callable[[T], List[S]]: ... +def id(x: U) -> U: + ... + +reveal_type(dec(id, id)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericSplitOrderGeneric] +# flags: --strict-optional --new-type-inference +from typing import TypeVar, Callable, Tuple + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') +V = TypeVar('V') + +def dec(f: Callable[[T], S], g: Callable[[T], U]) -> Callable[[T], Tuple[S, U]]: ... +def id(x: V) -> V: + ... + +reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]" +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericEllipsisSelfSpecialCase] +# flags: --new-type-inference +from typing import Self, Callable, TypeVar + +T = TypeVar("T") +def dec(f: Callable[..., T]) -> Callable[..., T]: ... + +class C: + @dec + def test(self) -> Self: ... + +c: C +reveal_type(c.test()) # N: Revealed type is "__main__.C" diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 901e73008d56..cafcaca0a14c 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1040,6 +1040,28 @@ reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1)" reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] +[case testGenericsInInferredParamspecReturn] +# flags: --new-type-inference +from typing import Callable, TypeVar, Generic +from typing_extensions import ParamSpec + +_P = ParamSpec("_P") +_T = TypeVar("_T") + +class Job(Generic[_P, _T]): + def __init__(self, target: Callable[_P, _T]) -> None: ... + def into_callable(self) -> Callable[_P, _T]: ... + +def generic_f(x: _T) -> _T: ... + +j = Job(generic_f) +reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1], _T`-1]" + +jf = j.into_callable() +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1) -> _T`-1" +reveal_type(jf(1)) # N: Revealed type is "builtins.int" +[builtins fixtures/paramspec.pyi] + [case testStackedConcatenateIsIllegal] from typing_extensions import Concatenate, ParamSpec from typing import Callable @@ -1520,3 +1542,18 @@ def identity(func: Callable[P, None]) -> Callable[P, None]: ... @identity def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... [builtins fixtures/paramspec.pyi] + +[case testParamSpecDecoratorAppliedToGeneric] +# flags: --new-type-inference +from typing import Callable, List, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") +U = TypeVar("U") + +def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... +def test(x: U) -> U: ... +reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]" +reveal_type(dec(test)) # N: Revealed type is "def [U] (x: U`-1) -> builtins.list[U`-1]" +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 9aa31c1ed10b..5b8c361906a8 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1173,12 +1173,13 @@ class A: [builtins fixtures/bool.pyi] [case testAttrsFactoryBadReturn] +# flags: --new-type-inference import attr def my_factory() -> int: return 7 @attr.s class A: - x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[T]", variable has type "int") + x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[]", variable has type "int") y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/list.pyi] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 8c82b6843a3b..eada8cf6fa85 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -833,6 +833,7 @@ _program.py:3: error: Dict entry 1 has incompatible type "str": "str"; expected _program.py:5: error: "Dict[str, int]" has no attribute "xyz" [case testDefaultDict] +# flags: --new-type-inference import typing as t from collections import defaultdict @@ -858,11 +859,11 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]): MyDDict(dict)['0'] MyDDict(dict)[0] [out] -_program.py:6: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Callable[[], str]" -_program.py:9: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" -_program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str") -_program.py:19: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[]]"; expected "defaultdict[int, List[]]" -_program.py:23: error: Invalid index type "str" for "MyDDict[Dict[_KT, _VT]]"; expected type "int" +_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Callable[[], str]" +_program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" +_program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str") +_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[]]"; expected "defaultdict[int, List[]]" +_program.py:24: error: Invalid index type "str" for "MyDDict[Dict[, ]]"; expected type "int" [case testNoSubcriptionOfStdlibCollections] # flags: --python-version 3.6 @@ -2032,7 +2033,6 @@ from dataclasses import dataclass, replace class A: x: int - a = A(x=42) a2 = replace(a, x=42) reveal_type(a2) @@ -2040,7 +2040,47 @@ a2 = replace() a2 = replace(a, x='spam') a2 = replace(a, x=42, q=42) [out] -_testDataclassReplace.py:10: note: Revealed type is "_testDataclassReplace.A" -_testDataclassReplace.py:11: error: Too few arguments for "replace" -_testDataclassReplace.py:12: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int" -_testDataclassReplace.py:13: error: Unexpected keyword argument "q" for "replace" of "A" +_testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A" +_testDataclassReplace.py:10: error: Too few arguments for "replace" +_testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int" +_testDataclassReplace.py:12: error: Unexpected keyword argument "q" for "replace" of "A" + +[case testGenericInferenceWithTuple] +# flags: --new-type-inference +from typing import TypeVar, Callable, Tuple + +T = TypeVar("T") + +def f(x: Callable[..., T]) -> T: + return x() + +x: Tuple[str, ...] = f(tuple) +[out] + +[case testGenericInferenceWithDataclass] +# flags: --new-type-inference +from typing import Any, Collection, List +from dataclasses import dataclass, field + +class Foo: + pass + +@dataclass +class A: + items: Collection[Foo] = field(default_factory=list) +[out] + +[case testGenericInferenceWithItertools] +# flags: --new-type-inference +from typing import TypeVar, Tuple +from itertools import groupby +K = TypeVar("K") +V = TypeVar("V") + +def fst(kv: Tuple[K, V]) -> K: + k, v = kv + return k + +pairs = [(len(s), s) for s in ["one", "two", "three"]] +grouped = groupby(pairs, key=fst) +[out] From 32d14ed9928f04bdbd70b6381c5706024dfb4e70 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 19 Jun 2023 11:01:12 +0100 Subject: [PATCH 0037/1617] Delete a ProperType join hack (#15461) The tests added with this piece of code now pass even without it. It looks like the crash was properly fixed by https://github.com/python/mypy/pull/13371 --- mypy/join.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index 62d256f4440f..f4af59f4e50b 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -29,7 +29,6 @@ Parameters, ParamSpecType, PartialType, - PlaceholderType, ProperType, TupleType, Type, @@ -246,14 +245,6 @@ def join_types(s: Type, t: Type, instance_joiner: InstanceJoiner | None = None) if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): s, t = t, s - # We shouldn't run into PlaceholderTypes here, but in practice we can encounter them - # here in the presence of undefined names - if isinstance(t, PlaceholderType) and not isinstance(s, PlaceholderType): - # mypyc does not allow switching the values like above. - return s.accept(TypeJoinVisitor(t)) - elif isinstance(t, PlaceholderType): - return AnyType(TypeOfAny.from_error) - # Meets/joins require callable type normalization. s, t = normalize_callables(s, t) From 719e7e77a52d30146de3a95ee2b6baf69a11eb17 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Jun 2023 12:34:07 +0100 Subject: [PATCH 0038/1617] [mypyc] Add Python 3.12 feature macro (#15465) It's currently unused but I have other PRs that will need this. We can later make this more general and support things like "are we running on Python 3.12 beta 2 or later", but this seems good enough for now. --- mypyc/lib-rt/mypyc_util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 13672087fbbc..3bc785ac6526 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -69,4 +69,7 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) { return x << 1; } +// Are we targeting Python 3.12 or newer? +#define CPY_3_12_FEATURES (PY_VERSION_HEX >= 0x030c0000) + #endif From c0e6d2818bcab9a7313cebb54463d752004de844 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Jun 2023 23:42:19 +0100 Subject: [PATCH 0039/1617] [mypyc] Don't use _PyErr_ChainExceptions on 3.12, since it's deprecated (#15468) --- mypyc/lib-rt/exc_ops.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/lib-rt/exc_ops.c b/mypyc/lib-rt/exc_ops.c index 219914bf3470..a7536994d059 100644 --- a/mypyc/lib-rt/exc_ops.c +++ b/mypyc/lib-rt/exc_ops.c @@ -230,7 +230,11 @@ void CPy_AddTraceback(const char *filename, const char *funcname, int line, PyOb return; error: +#if CPY_3_12_FEATURES + _PyErr_ChainExceptions1(exc); +#else _PyErr_ChainExceptions(exc, val, tb); +#endif } CPy_NOINLINE From 6907343e0191162aa03cc21d23489606e0997b4a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Jun 2023 23:43:39 +0100 Subject: [PATCH 0040/1617] [mypyc] Fix gen_is_coroutine on Python 3.12 (#15469) --- mypyc/lib-rt/pythonsupport.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 8a1159a98853..6751f6f4af02 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -13,6 +13,10 @@ #include #include "mypyc_util.h" +#if CPY_3_12_FEATURES +#include "internal/pycore_frame.h" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -389,6 +393,31 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { _PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL) #endif +#if CPY_3_12_FEATURES + +// These are copied from genobject.c in Python 3.12 + +/* Returns a borrowed reference */ +static inline PyCodeObject * +_PyGen_GetCode(PyGenObject *gen) { + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); + return frame->f_code; +} + +static int +gen_is_coroutine(PyObject *o) +{ + if (PyGen_CheckExact(o)) { + PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); + if (code->co_flags & CO_ITERABLE_COROUTINE) { + return 1; + } + } + return 0; +} + +#else + // Copied from genobject.c in Python 3.10 static int gen_is_coroutine(PyObject *o) @@ -402,6 +431,8 @@ gen_is_coroutine(PyObject *o) return 0; } +#endif + /* * This helper function returns an awaitable for `o`: * - `o` if `o` is a coroutine-object; From d1fe7cf849dbdf2b41109db1ce5fc6293ce6ec0a Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:06:05 -0700 Subject: [PATCH 0041/1617] [minor] Use keyword-only args for ErrorInfo (#15473) --- mypy/errors.py | 113 +++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 9d29259e943c..b230b3baa3f6 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -90,6 +90,7 @@ class ErrorInfo: def __init__( self, import_ctx: list[tuple[str, int]], + *, file: str, module: str | None, typ: str | None, @@ -415,21 +416,21 @@ def report( code = code or (codes.MISC if not blocker else None) info = ErrorInfo( - self.import_context(), - file, - self.current_module(), - type, - function, - line, - column, - end_line, - end_column, - severity, - message, - code, - blocker, - only_once, - allow_dups, + import_ctx=self.import_context(), + file=file, + module=self.current_module(), + typ=type, + function_or_member=function, + line=line, + column=column, + end_line=end_line, + end_column=end_column, + severity=severity, + message=message, + code=code, + blocker=blocker, + only_once=only_once, + allow_dups=allow_dups, origin=(self.file, origin_span), target=self.current_target(), ) @@ -511,17 +512,17 @@ def add_error_info(self, info: ErrorInfo) -> None: + "may be out of date" ) note = ErrorInfo( - info.import_ctx, - info.file, - info.module, - info.type, - info.function_or_member, - info.line, - info.column, - info.end_line, - info.end_column, - "note", - msg, + import_ctx=info.import_ctx, + file=info.file, + module=info.module, + typ=info.type, + function_or_member=info.function_or_member, + line=info.line, + column=info.column, + end_line=info.end_line, + end_column=info.end_column, + severity="note", + message=msg, code=None, blocker=False, only_once=False, @@ -653,21 +654,21 @@ def generate_unused_ignore_errors(self, file: str) -> None: message += f", use narrower [{', '.join(narrower)}] instead of [{unused}] code" # Don't use report since add_error_info will ignore the error! info = ErrorInfo( - self.import_context(), - file, - self.current_module(), - None, - None, - line, - -1, - line, - -1, - "error", - message, - codes.UNUSED_IGNORE, - False, - False, - False, + import_ctx=self.import_context(), + file=file, + module=self.current_module(), + typ=None, + function_or_member=None, + line=line, + column=-1, + end_line=line, + end_column=-1, + severity="error", + message=message, + code=codes.UNUSED_IGNORE, + blocker=False, + only_once=False, + allow_dups=False, ) self._add_error_info(file, info) @@ -705,21 +706,21 @@ def generate_ignore_without_code_errors( message = f'"type: ignore" comment without error code{codes_hint}' # Don't use report since add_error_info will ignore the error! info = ErrorInfo( - self.import_context(), - file, - self.current_module(), - None, - None, - line, - -1, - line, - -1, - "error", - message, - codes.IGNORE_WITHOUT_CODE, - False, - False, - False, + import_ctx=self.import_context(), + file=file, + module=self.current_module(), + typ=None, + function_or_member=None, + line=line, + column=-1, + end_line=line, + end_column=-1, + severity="error", + message=message, + code=codes.IGNORE_WITHOUT_CODE, + blocker=False, + only_once=False, + allow_dups=False, ) self._add_error_info(file, info) From 15f210b104c253e866b50607126c7022258bd715 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 20 Jun 2023 07:23:40 +0200 Subject: [PATCH 0042/1617] Enable strict optional for pythoneval tests (#15474) --- mypy/test/testpythoneval.py | 2 - test-data/unit/pythoneval.test | 106 ++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 1fd342452102..17baec96cfbc 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -46,10 +46,8 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None """ assert testcase.old_cwd is not None, "test was not properly set up" # We must enable site packages to get access to installed stubs. - # TODO: Enable strict optional for these tests mypy_cmdline = [ "--show-traceback", - "--no-strict-optional", "--no-silence-site-packages", "--no-error-summary", "--hide-error-codes", diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index eada8cf6fa85..b43dcbd7088f 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -63,8 +63,8 @@ print(abs(A())) 5.5 [case testAbs2] -n = None # type: int -f = None # type: float +n: int +f: float n = abs(1) abs(1) + 'x' # Error f = abs(1.1) @@ -95,7 +95,7 @@ print(list.__add__([1, 2], [3, 4])) import typing class A: x = 1 - def f(self) -> None: print('f') + def f(self: typing.Optional["A"]) -> None: print('f') class B(A): pass B.f(None) @@ -130,7 +130,7 @@ A docstring! [case testFunctionAttributes] import typing ord.__class__ -print(type(ord.__doc__ + '')) +print(type(ord.__doc__ or '' + '')) print(ord.__name__) print(ord.__module__) [out] @@ -333,25 +333,28 @@ _program.py:6: note: Revealed type is "typing.IO[Any]" [case testGenericPatterns] from typing import Pattern import re -p = None # type: Pattern[str] +p: Pattern[str] p = re.compile('foo*') -b = None # type: Pattern[bytes] +b: Pattern[bytes] b = re.compile(b'foo*') -print(p.match('fooo').group(0)) +m = p.match('fooo') +assert m +print(m.group(0)) [out] fooo [case testGenericMatch] -from typing import Match +from typing import Match, Optional import re -def f(m: Match[bytes]) -> None: +def f(m: Optional[Match[bytes]]) -> None: + assert m print(m.group(0)) f(re.match(b'x*', b'xxy')) [out] b'xx' [case testIntFloatDucktyping] -x = None # type: float +x: float x = 2.2 x = 2 def f(x: float) -> None: pass @@ -374,18 +377,17 @@ math.sin(2) math.sin(2.2) [case testAbsReturnType] - -f = None # type: float -n = None # type: int +f: float +n: int n = abs(2) f = abs(2.2) abs(2.2) + 'x' [out] -_program.py:6: error: Unsupported operand types for + ("float" and "str") +_program.py:5: error: Unsupported operand types for + ("float" and "str") [case testROperatorMethods] -b = None # type: bytes -s = None # type: str +b: bytes +s: str if int(): s = b'foo' * 5 # Error if int(): @@ -434,7 +436,6 @@ True False [case testOverlappingOperatorMethods] - class X: pass class A: def __add__(self, x) -> int: @@ -444,11 +445,11 @@ class A: class B: def __radd__(self, x: A) -> str: return 'x' class C(X, B): pass -b = None # type: B +b: B b = C() print(A() + b) [out] -_program.py:9: error: Signatures of "__radd__" of "B" and "__add__" of "A" are unsafely overlapping +_program.py:8: error: Signatures of "__radd__" of "B" and "__add__" of "A" are unsafely overlapping [case testBytesAndBytearrayComparisons] import typing @@ -859,7 +860,7 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]): MyDDict(dict)['0'] MyDDict(dict)[0] [out] -_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Callable[[], str]" +_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Optional[Callable[[], str]]" _program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" _program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str") _program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[]]"; expected "defaultdict[int, List[]]" @@ -966,10 +967,10 @@ print(getattr(B(), 'x')) 7 [case testSortedNoError] -from typing import Iterable, Callable, TypeVar, List, Dict +from typing import Iterable, Callable, TypeVar, List, Dict, Optional T = TypeVar('T') -def sorted(x: Iterable[T], *, key: Callable[[T], object] = None) -> None: ... -a = None # type: List[Dict[str, str]] +def sorted(x: Iterable[T], *, key: Optional[Callable[[T], object]] = None) -> None: ... +a = [] # type: List[Dict[str, str]] sorted(a, key=lambda y: y['']) [case testAbstractProperty] @@ -1002,9 +1003,13 @@ import re bre = b'a+' bpat = re.compile(bre) bpat = re.compile(bpat) -re.search(bre, b'').groups() +s1 = re.search(bre, b'') +assert s1 +s1.groups() re.search(bre, u'') # Error -re.search(bpat, b'').groups() +s2 = re.search(bpat, b'') +assert s2 +s2.groups() re.search(bpat, u'') # Error # match(), split(), findall(), finditer() are much the same, so skip those. # sub(), subn() have more overloads and we are checking these: @@ -1017,11 +1022,11 @@ re.subn(bpat, b'', b'')[0] + b'' re.subn(bre, lambda m: b'', b'')[0] + b'' re.subn(bpat, lambda m: b'', b'')[0] + b'' [out] -_testReModuleBytes.py:7: error: No overload variant of "search" matches argument types "bytes", "str" -_testReModuleBytes.py:7: note: Possible overload variants: -_testReModuleBytes.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] -_testReModuleBytes.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Buffer, flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] -_testReModuleBytes.py:9: error: Argument 1 to "search" has incompatible type "Pattern[bytes]"; expected "Union[str, Pattern[str]]" +_testReModuleBytes.py:9: error: No overload variant of "search" matches argument types "bytes", "str" +_testReModuleBytes.py:9: note: Possible overload variants: +_testReModuleBytes.py:9: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] +_testReModuleBytes.py:9: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Buffer, flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleBytes.py:13: error: Argument 1 to "search" has incompatible type "Pattern[bytes]"; expected "Union[str, Pattern[str]]" [case testReModuleString] # Regression tests for various overloads in the re module -- string version @@ -1029,9 +1034,13 @@ import re sre = 'a+' spat = re.compile(sre) spat = re.compile(spat) -re.search(sre, '').groups() +s1 = re.search(sre, '') +assert s1 +s1.groups() re.search(sre, b'') # Error -re.search(spat, '').groups() +s2 = re.search(spat, '') +assert s2 +s2.groups() re.search(spat, b'') # Error # match(), split(), findall(), finditer() are much the same, so skip those. # sus(), susn() have more overloads and we are checking these: @@ -1044,11 +1053,11 @@ re.subn(spat, '', '')[0] + '' re.subn(sre, lambda m: '', '')[0] + '' re.subn(spat, lambda m: '', '')[0] + '' [out] -_testReModuleString.py:7: error: No overload variant of "search" matches argument types "str", "bytes" -_testReModuleString.py:7: note: Possible overload variants: -_testReModuleString.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] -_testReModuleString.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Buffer, flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] -_testReModuleString.py:9: error: Argument 1 to "search" has incompatible type "Pattern[str]"; expected "Union[bytes, Pattern[bytes]]" +_testReModuleString.py:9: error: No overload variant of "search" matches argument types "str", "bytes" +_testReModuleString.py:9: note: Possible overload variants: +_testReModuleString.py:9: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] +_testReModuleString.py:9: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Buffer, flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleString.py:13: error: Argument 1 to "search" has incompatible type "Pattern[str]"; expected "Union[bytes, Pattern[bytes]]" [case testListSetitemTuple] from typing import List, Tuple @@ -1085,7 +1094,6 @@ _program.py:17: note: Revealed type is "builtins.str" [case testTypedDictGet] # Test that TypedDict get plugin works with typeshed stubs -# TODO: Make it possible to use strict optional here from mypy_extensions import TypedDict class A: pass D = TypedDict('D', {'x': int, 'y': str}) @@ -1097,14 +1105,14 @@ d.get() s = '' reveal_type(d.get(s)) [out] -_testTypedDictGet.py:7: note: Revealed type is "builtins.int" -_testTypedDictGet.py:8: note: Revealed type is "builtins.str" -_testTypedDictGet.py:9: note: Revealed type is "builtins.object" -_testTypedDictGet.py:10: error: All overload variants of "get" of "Mapping" require at least one argument -_testTypedDictGet.py:10: note: Possible overload variants: -_testTypedDictGet.py:10: note: def get(self, str, /) -> object -_testTypedDictGet.py:10: note: def [_T] get(self, str, /, default: object) -> object -_testTypedDictGet.py:12: note: Revealed type is "builtins.object" +_testTypedDictGet.py:6: note: Revealed type is "Union[builtins.int, None]" +_testTypedDictGet.py:7: note: Revealed type is "Union[builtins.str, None]" +_testTypedDictGet.py:8: note: Revealed type is "builtins.object" +_testTypedDictGet.py:9: error: All overload variants of "get" of "Mapping" require at least one argument +_testTypedDictGet.py:9: note: Possible overload variants: +_testTypedDictGet.py:9: note: def get(self, str, /) -> object +_testTypedDictGet.py:9: note: def [_T] get(self, str, /, default: object) -> object +_testTypedDictGet.py:11: note: Revealed type is "builtins.object" [case testTypedDictMappingMethods] from mypy_extensions import TypedDict @@ -1143,10 +1151,10 @@ _testTypedDictMappingMethods.py:16: error: Key "value" of TypedDict "Cell" canno _testTypedDictMappingMethods.py:21: note: Revealed type is "builtins.int" [case testCrashOnComplexCheckWithNamedTupleNext] -from typing import NamedTuple +from typing import NamedTuple, Optional MyNamedTuple = NamedTuple('MyNamedTuple', [('parent', 'MyNamedTuple')]) # type: ignore -def foo(mymap) -> MyNamedTuple: +def foo(mymap) -> Optional[MyNamedTuple]: return next((mymap[key] for key in mymap), None) [out] @@ -1374,7 +1382,7 @@ JsonBlob = Dict[str, Any] Column = Union[List[str], List[int], List[bool], List[float], List[DateTime], List[JsonBlob]] def print_custom_table() -> None: - a = None # type: Column + a: Column for row in simple_map(format_row, a, a, a, a, a, a, a, a): # 8 columns reveal_type(row) From 7d5213d0c3490ab03ed5240f9a02dd95dd002793 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 20 Jun 2023 00:31:20 -0700 Subject: [PATCH 0043/1617] Use setuptools instead of distutils in docs (#15477) --- docs/source/installed_packages.rst | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/source/installed_packages.rst b/docs/source/installed_packages.rst index b9a3b891c99c..fa4fae1a0b3e 100644 --- a/docs/source/installed_packages.rst +++ b/docs/source/installed_packages.rst @@ -119,7 +119,7 @@ The ``setup.py`` file could look like this: .. code-block:: python - from distutils.core import setup + from setuptools import setup setup( name="SuperPackageA", @@ -129,11 +129,6 @@ The ``setup.py`` file could look like this: packages=["package_a"] ) -.. note:: - - If you use :doc:`setuptools `, you must pass the option ``zip_safe=False`` to - ``setup()``, or mypy will not be able to find the installed package. - Some packages have a mix of stub files and runtime files. These packages also require a ``py.typed`` file. An example can be seen below: @@ -150,7 +145,7 @@ The ``setup.py`` file might look like this: .. code-block:: python - from distutils.core import setup + from setuptools import setup setup( name="SuperPackageB", @@ -180,7 +175,7 @@ The ``setup.py`` might look like this: .. code-block:: python - from distutils.core import setup + from setuptools import setup setup( name="SuperPackageC", From 1e5caa0dc349750c3eb6c5f448c7aab524e3ec38 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 20 Jun 2023 01:32:34 -0700 Subject: [PATCH 0044/1617] Remove test failing on 3.12 (#15478) --- test-data/unit/check-newsyntax.test | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index 63284c34bd8b..cfcbfc598c51 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -105,11 +105,6 @@ main:5: error: "str" has no attribute "x" async def f(): results = [i async for i in aiter() if i % 2] # E: Async comprehensions are only supported in Python 3.6 and greater - -[case testNewSyntaxFstringError] -# flags: --python-version 3.5 -f'' # E: Format strings are only supported in Python 3.6 and greater - [case testNewSyntaxFStringBasics] # flags: --python-version 3.6 f'foobar' From e3210d07d945bd7b120dd0d6f20036ed664c4129 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 20 Jun 2023 09:41:26 -0400 Subject: [PATCH 0045/1617] meta tests: fix flake (#15480) Since tests are parallelized, the temporary test name must be unique. --- mypy/test/meta/test_parse_data.py | 3 ++- mypy/test/meta/test_update_data.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index a74b3d71d392..cc1b4ff6eeed 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -5,6 +5,7 @@ import subprocess import sys import textwrap +import uuid from pathlib import Path from mypy.test.config import test_data_prefix @@ -18,7 +19,7 @@ def _dedent(self, s: str) -> str: def _run_pytest(self, data_suite: str) -> str: p_test_data = Path(test_data_prefix) p_root = p_test_data.parent.parent - p = p_test_data / "check-__fixture__.test" + p = p_test_data / f"check-meta-{uuid.uuid4()}.test" assert not p.exists() try: p.write_text(data_suite) diff --git a/mypy/test/meta/test_update_data.py b/mypy/test/meta/test_update_data.py index 1239679072e6..67f9a7f56ebd 100644 --- a/mypy/test/meta/test_update_data.py +++ b/mypy/test/meta/test_update_data.py @@ -7,6 +7,7 @@ import subprocess import sys import textwrap +import uuid from pathlib import Path from mypy.test.config import test_data_prefix @@ -21,7 +22,7 @@ def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str: """ p_test_data = Path(test_data_prefix) p_root = p_test_data.parent.parent - p = p_test_data / "check-__fixture__.test" + p = p_test_data / f"check-meta-{uuid.uuid4()}.test" assert not p.exists() try: p.write_text(textwrap.dedent(data_suite).lstrip()) From 304997bfb85200fb521ac727ee0ce3e6085e5278 Mon Sep 17 00:00:00 2001 From: Logan Hunt <39638017+dosisod@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:25:38 -0700 Subject: [PATCH 0046/1617] Switch from `flake8` to `ruff` (#15362) --- .github/workflows/test.yml | 2 +- .gitignore | 1 - .pre-commit-config.yaml | 13 +++-------- CONTRIBUTING.md | 2 +- mypy/build.py | 4 ++-- mypy/plugins/common.py | 4 +--- mypy/server/objgraph.py | 4 ++-- mypy/typeanal.py | 2 +- mypy/types.py | 2 +- mypyc/irbuild/specialize.py | 2 +- pyproject.toml | 37 +++++++++++++++++++++++++++++++ setup.cfg | 43 ------------------------------------- test-data/.flake8 | 22 ------------------- test-data/unit/README.md | 2 +- test-requirements.txt | 4 +--- 15 files changed, 52 insertions(+), 92 deletions(-) delete mode 100644 test-data/.flake8 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81587f3ca747..6fc8fb11c6f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,7 +96,7 @@ jobs: # We also run these checks with pre-commit in CI, # but it's useful to run them with tox too, # to ensure the tox env works as expected - - name: Formatting with Black + isort and code style with flake8 + - name: Formatting with Black + isort and code style with ruff python: '3.10' arch: x64 os: ubuntu-latest diff --git a/.gitignore b/.gitignore index c6761f0ed736..6c35e3d89342 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,5 @@ test_capi *.o *.a test_capi -/.mypyc-flake8-cache.json /mypyc/lib-rt/build/ /mypyc/lib-rt/*.so diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 92d827fba006..cbb3672bd986 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,14 +13,7 @@ repos: rev: 5.12.0 # must match test-requirements.txt hooks: - id: isort - - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 # must match test-requirements.txt + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.272 # must match test-requirements.txt hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear==23.3.23 # must match test-requirements.txt - - flake8-noqa==1.3.1 # must match test-requirements.txt - -ci: - # We run flake8 as part of our GitHub Actions suite in CI - skip: [flake8] + - id: ruff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d169d7f3159e..99ba2f8c9a76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,7 +89,7 @@ pytest -n0 -k 'test_name' pytest mypy/test/testcheck.py::TypeCheckSuite::check-dataclasses.test # Run the linter -flake8 +ruff . # Run formatters black . && isort . diff --git a/mypy/build.py b/mypy/build.py index 2f556120d430..f388f0909854 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -3076,7 +3076,7 @@ def load_graph( manager.errors.report( -1, -1, - "See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules " # noqa: E501 + "See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules " "for more info", severity="note", ) @@ -3164,7 +3164,7 @@ def load_graph( manager.errors.report( -1, 0, - "See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules " # noqa: E501 + "See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules " "for more info", severity="note", ) diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index f803387cde8b..65d967577bea 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -28,9 +28,7 @@ require_bool_literal_argument, set_callable_name, ) -from mypy.typeops import ( # noqa: F401 # Part of public API - try_getting_str_literals as try_getting_str_literals, -) +from mypy.typeops import try_getting_str_literals as try_getting_str_literals from mypy.types import ( AnyType, CallableType, diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index 89a086b8a0ab..37d0f4d2ca1a 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -46,7 +46,7 @@ def get_edge_candidates(o: object) -> Iterator[tuple[object, object]]: try: if attr not in ATTR_BLACKLIST and hasattr(o, attr) and not isproperty(o, attr): e = getattr(o, attr) - if not type(e) in ATOMIC_TYPE_BLACKLIST: + if type(e) not in ATOMIC_TYPE_BLACKLIST: yield attr, e except AssertionError: pass @@ -70,7 +70,7 @@ def get_edges(o: object) -> Iterator[tuple[object, object]]: if se is not o and se is not type(o) and hasattr(s, "__self__"): yield s.__self__, se else: - if not type(e) in TYPE_BLACKLIST: + if type(e) not in TYPE_BLACKLIST: yield s, e diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 39a44a289365..d577f355dbc8 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1256,7 +1256,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type: code=codes.VALID_TYPE, ) self.note( - "See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas", # noqa: E501 + "See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas", t, ) return AnyType(TypeOfAny.from_error) diff --git a/mypy/types.py b/mypy/types.py index e4c22da12603..25bfdd8b03c4 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2957,7 +2957,7 @@ def get_proper_types( # to make it easier to gradually get modules working with mypyc. # Import them here, after the types are defined. # This is intended as a re-export also. -from mypy.type_visitor import ( # noqa: F811,F401 +from mypy.type_visitor import ( # noqa: F811 ALL_STRATEGY as ALL_STRATEGY, ANY_STRATEGY as ANY_STRATEGY, BoolTypeQuery as BoolTypeQuery, diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index ff9df0cd597b..14a6f8e92df2 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -446,7 +446,7 @@ def translate_sum_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> V # handle 'start' argument, if given if len(expr.args) == 2: # ensure call to sum() was properly constructed - if not expr.arg_kinds[1] in (ARG_POS, ARG_NAMED): + if expr.arg_kinds[1] not in (ARG_POS, ARG_NAMED): return None start_expr = expr.args[1] else: diff --git a/pyproject.toml b/pyproject.toml index 3d100dff5101..b46bbd670e60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,3 +39,40 @@ skip_glob = [ "mypyc/test-data/*", "test-data/*", ] + +[tool.ruff] +line-length = 99 +target-version = "py37" + +select = [ + "E", # pycoderstyle (error) + "F", # pyflakes + "B", # flake8-bugbear + "RUF100", # Unused noqa comments + "PGH004" # blanket noqa comments +] + +ignore = [ + "B006", # use of mutable defaults in function signatures + "B007", # Loop control variable not used within the loop body. + "B011", # Don't use assert False + "B023", # Function definition does not bind loop variable + "E203", # conflicts with black + "E402", # module level import not at top of file + "E501", # conflicts with black + "E731", # Do not assign a `lambda` expression, use a `def` + "E741", # Ambiguous variable name +] + +extend-exclude = [ + "@*", + # Sphinx configuration is irrelevant + "docs/source/conf.py", + "mypyc/doc/conf.py", + # tests have more relaxed styling requirements + # fixtures have their own .pyi-specific configuration + "test-data/*", + "mypyc/test-data/*", + # typeshed has its own .pyi-specific configuration + "mypy/typeshed/*", +] diff --git a/setup.cfg b/setup.cfg index 511f794474e7..04d75ac8d19f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,46 +1,3 @@ -[flake8] -max-line-length = 99 -noqa-require-code = True -# typeshed and unit test fixtures have .pyi-specific flake8 configuration -exclude = - # from .gitignore: directories, and file patterns that intersect with *.py - build, - bin, - lib, - include, - @*, - env, - docs/build, - out, - .venv, - .mypy_cache, - .git, - .cache, - # Sphinx configuration is irrelevant - docs/source/conf.py, - mypyc/doc/conf.py, - # tests have more relaxed styling requirements - # fixtures have their own .pyi-specific configuration - test-data/*, - mypyc/test-data/*, - # typeshed has its own .pyi-specific configuration - mypy/typeshed/*, - .tox - .eggs - .Python - -# Things to ignore: -# E203: conflicts with black -# E501: conflicts with black -# W601: has_key() deprecated (false positives) -# E402: module level import not at top of file -# B006: use of mutable defaults in function signatures -# B007: Loop control variable not used within the loop body. -# B011: Don't use assert False -# B023: Function definition does not bind loop variable -# E741: Ambiguous variable name -extend-ignore = E203,E501,W601,E402,B006,B007,B011,B023,E741 - [coverage:run] branch = true source = mypy diff --git a/test-data/.flake8 b/test-data/.flake8 deleted file mode 100644 index df2f9caf8c94..000000000000 --- a/test-data/.flake8 +++ /dev/null @@ -1,22 +0,0 @@ -# Some PEP8 deviations are considered irrelevant to stub files: -# (error counts as of 2016-12-19) -# 17381 E704 multiple statements on one line (def) -# 11840 E301 expected 1 blank line -# 7467 E302 expected 2 blank lines -# 1772 E501 line too long -# 1487 F401 imported but unused -# 1248 E701 multiple statements on one line (colon) -# 427 F811 redefinition -# 356 E305 expected 2 blank lines - -# Nice-to-haves ignored for now -# 152 E128 continuation line under-indented for visual indent -# 43 E127 continuation line over-indented for visual indent - -[flake8] -ignore = F401, F811, E127, E128, E301, E302, E305, E501, E701, E704, B303 -# We are checking with Python 3 but many of the stubs are Python 2 stubs. -# A nice future improvement would be to provide separate .flake8 -# configurations for Python 2 and Python 3 files. -builtins = StandardError,apply,basestring,buffer,cmp,coerce,execfile,file,intern,long,raw_input,reduce,reload,unichr,unicode,xrange -exclude = .venv*,@* diff --git a/test-data/unit/README.md b/test-data/unit/README.md index 97680c949bef..f2c727b43543 100644 --- a/test-data/unit/README.md +++ b/test-data/unit/README.md @@ -159,7 +159,7 @@ To run mypy on itself: To run the linter: - flake8 + ruff . You can also run all of the above tests using `runtests.py` (this includes type checking mypy and linting): diff --git a/test-requirements.txt b/test-requirements.txt index 42c8a08a2b5d..51cd62d4064c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,9 +3,6 @@ attrs>=18.0 black==23.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 -flake8==6.0.0; python_version >= "3.8" # must match version in .pre-commit-config.yaml -flake8-bugbear==23.3.23; python_version >= "3.8" # must match version in .pre-commit-config.yaml -flake8-noqa==1.3.1; python_version >= "3.8" # must match version in .pre-commit-config.yaml isort[colors]==5.12.0; python_version >= "3.8" # must match version in .pre-commit-config.yaml lxml>=4.9.1; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' pre-commit @@ -17,6 +14,7 @@ pytest-xdist>=1.34.0 pytest-forked>=1.3.0,<2.0.0 pytest-cov>=2.10.0 py>=1.5.2 +ruff==0.0.272 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 six tomli>=1.1.0 From 88845188c8d1a16e528bb8881a02d374a33af370 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:02:21 +0200 Subject: [PATCH 0047/1617] Bump typing_extensions dependency (#15488) Co-authored-by: Alex Waygood --- mypy-requirements.txt | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 9a55446eb05a..7043765f09f4 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -1,5 +1,5 @@ # NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml -typing_extensions>=3.10 +typing_extensions>=4.1.0 mypy_extensions>=1.0.0 typed_ast>=1.4.0,<2; python_version<'3.8' tomli>=1.1.0; python_version<'3.11' diff --git a/pyproject.toml b/pyproject.toml index b46bbd670e60..fc70d4279681 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = [ "setuptools >= 40.6.2", "wheel >= 0.30.0", # the following is from mypy-requirements.txt - "typing_extensions>=3.10", + "typing_extensions>=4.1.0", "mypy_extensions>=1.0.0", "typed_ast>=1.4.0,<2; python_version<'3.8'", "tomli>=1.1.0; python_version<'3.11'", diff --git a/setup.py b/setup.py index 061bb9ddf5b5..81494a5566e8 100644 --- a/setup.py +++ b/setup.py @@ -222,7 +222,7 @@ def run(self): # When changing this, also update mypy-requirements.txt. install_requires=[ "typed_ast >= 1.4.0, < 2; python_version<'3.8'", - "typing_extensions>=3.10", + "typing_extensions>=4.1.0", "mypy_extensions >= 1.0.0", "tomli>=1.1.0; python_version<'3.11'", ], From 7d031beb017450ac990c97e288d064290e3be55f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:08:37 +0200 Subject: [PATCH 0048/1617] Add pip as test requirement for PEP 660 editable installs (#15482) --- test-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test-requirements.txt b/test-requirements.txt index 51cd62d4064c..8aea01f9e996 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,6 +5,7 @@ black==23.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 isort[colors]==5.12.0; python_version >= "3.8" # must match version in .pre-commit-config.yaml lxml>=4.9.1; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' +pip>=21.3.1 pre-commit pre-commit-hooks==4.4.0 psutil>=4.0 From aba35af62078155c9c18f05ae1506fe72dd6121a Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 22 Jun 2023 11:07:43 -0400 Subject: [PATCH 0049/1617] Rename unreachable_lines to skipped_lines (#15483) In #15164 we've made it so that `# type: ignore`s in lines skipped in semantic analysis would not be flagged as unused. In #15164 they were called "unreachable" lines but unreachability during type-checking can result in the "Statement is unreachable" error, while these statements are pruned before type-checking, so for clarity we'll use the term "skipped". --- mypy/build.py | 2 +- mypy/errors.py | 13 +++++++------ mypy/nodes.py | 9 +++++---- mypy/semanal_pass1.py | 8 ++++---- mypy/server/update.py | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index f388f0909854..08e85dd28624 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2238,7 +2238,7 @@ def semantic_analysis_pass1(self) -> None: analyzer = SemanticAnalyzerPreAnalysis() with self.wrap_context(): analyzer.visit_file(self.tree, self.xpath, self.id, options) - self.manager.errors.set_unreachable_lines(self.xpath, self.tree.unreachable_lines) + self.manager.errors.set_skipped_lines(self.xpath, self.tree.skipped_lines) # TODO: Do this while constructing the AST? self.tree.names = SymbolTable() if not self.tree.is_stub: diff --git a/mypy/errors.py b/mypy/errors.py index b230b3baa3f6..0e61f5ecf0cd 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -223,8 +223,9 @@ class Errors: # (path -> line -> error-codes) ignored_lines: dict[str, dict[int, list[str]]] - # Lines that are statically unreachable (e.g. due to platform/version check). - unreachable_lines: dict[str, set[int]] + # Lines that were skipped during semantic analysis e.g. due to ALWAYS_FALSE, MYPY_FALSE, + # or platform/version checks. Those lines would not be type-checked. + skipped_lines: dict[str, set[int]] # Lines on which an error was actually ignored. used_ignored_lines: dict[str, dict[int, list[str]]] @@ -281,7 +282,7 @@ def initialize(self) -> None: self.import_ctx = [] self.function_or_member = [None] self.ignored_lines = {} - self.unreachable_lines = {} + self.skipped_lines = {} self.used_ignored_lines = defaultdict(lambda: defaultdict(list)) self.ignored_files = set() self.only_once_messages = set() @@ -330,8 +331,8 @@ def set_file_ignored_lines( if ignore_all: self.ignored_files.add(file) - def set_unreachable_lines(self, file: str, unreachable_lines: set[int]) -> None: - self.unreachable_lines[file] = unreachable_lines + def set_skipped_lines(self, file: str, skipped_lines: set[int]) -> None: + self.skipped_lines[file] = skipped_lines def current_target(self) -> str | None: """Retrieves the current target from the associated scope. @@ -631,7 +632,7 @@ def generate_unused_ignore_errors(self, file: str) -> None: ignored_lines = self.ignored_lines[file] used_ignored_lines = self.used_ignored_lines[file] for line, ignored_codes in ignored_lines.items(): - if line in self.unreachable_lines[file]: + if line in self.skipped_lines[file]: continue if codes.UNUSED_IGNORE.code in ignored_codes: continue diff --git a/mypy/nodes.py b/mypy/nodes.py index 4e29a434d7fe..2f4f3f788385 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -287,7 +287,7 @@ class MypyFile(SymbolNode): "names", "imports", "ignored_lines", - "unreachable_lines", + "skipped_lines", "is_stub", "is_cache_skeleton", "is_partial_stub_package", @@ -314,8 +314,9 @@ class MypyFile(SymbolNode): # If the value is empty, ignore all errors; otherwise, the list contains all # error codes to ignore. ignored_lines: dict[int, list[str]] - # Lines that are statically unreachable (e.g. due to platform/version check). - unreachable_lines: set[int] + # Lines that were skipped during semantic analysis e.g. due to ALWAYS_FALSE, MYPY_FALSE, + # or platform/version checks. Those lines would not be type-checked. + skipped_lines: set[int] # Is this file represented by a stub file (.pyi)? is_stub: bool # Is this loaded from the cache and thus missing the actual body of the file? @@ -348,7 +349,7 @@ def __init__( self.ignored_lines = ignored_lines else: self.ignored_lines = {} - self.unreachable_lines = set() + self.skipped_lines = set() self.path = "" self.is_stub = False diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 659f33e65ead..2df06feacca8 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -62,7 +62,7 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - self.cur_mod_node = file self.options = options self.is_global_scope = True - self.unreachable_lines: set[int] = set() + self.skipped_lines: set[int] = set() for i, defn in enumerate(file.defs): defn.accept(self) @@ -74,10 +74,10 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - next_def, last = file.defs[i + 1], file.defs[-1] if last.end_line is not None: # We are on a Python version recent enough to support end lines. - self.unreachable_lines |= set(range(next_def.line, last.end_line + 1)) + self.skipped_lines |= set(range(next_def.line, last.end_line + 1)) del file.defs[i + 1 :] break - file.unreachable_lines = self.unreachable_lines + file.skipped_lines = self.skipped_lines def visit_func_def(self, node: FuncDef) -> None: old_global_scope = self.is_global_scope @@ -127,7 +127,7 @@ def visit_block(self, b: Block) -> None: if b.is_unreachable: if b.end_line is not None: # We are on a Python version recent enough to support end lines. - self.unreachable_lines |= set(range(b.line, b.end_line + 1)) + self.skipped_lines |= set(range(b.line, b.end_line + 1)) return super().visit_block(b) diff --git a/mypy/server/update.py b/mypy/server/update.py index 7b439eb0ab9f..dafed6642d1e 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -986,7 +986,7 @@ def key(node: FineGrainedDeferredNode) -> int: manager.errors.set_file_ignored_lines( file_node.path, file_node.ignored_lines, options.ignore_errors or state.ignore_all ) - manager.errors.set_unreachable_lines(file_node.path, file_node.unreachable_lines) + manager.errors.set_skipped_lines(file_node.path, file_node.skipped_lines) targets = set() for node in nodes: From a5e316eafdd98ad73847e41436a3158e976ed769 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 22 Jun 2023 19:47:23 +0300 Subject: [PATCH 0050/1617] Do not import `TypedDict` from `mypy-extensions`, use `typing-extensions` (#15494) All recent versions (declared in `setup.py`) have `TypedDict` there. So, no need to use `mypy-extensions` here. (crossref: found while working on https://github.com/typeddjango/django-stubs/pull/1566) --- mypy/build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 08e85dd28624..5a6f8c00896e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -38,9 +38,7 @@ Sequence, TextIO, ) -from typing_extensions import Final, TypeAlias as _TypeAlias - -from mypy_extensions import TypedDict +from typing_extensions import Final, TypeAlias as _TypeAlias, TypedDict import mypy.semanal_main from mypy.checker import TypeChecker @@ -343,7 +341,9 @@ class CacheMeta(NamedTuple): # Metadata for the fine-grained dependencies file associated with a module. -FgDepMeta = TypedDict("FgDepMeta", {"path": str, "mtime": int}) +class FgDepMeta(TypedDict): + path: str + mtime: int def cache_meta_from_dict(meta: dict[str, Any], data_json: str) -> CacheMeta: From 2e304ca36df9975299769b3b40ec9d73ad4f68b4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 23 Jun 2023 00:01:15 +0200 Subject: [PATCH 0051/1617] Fix PEP 561 editable install test case (#15493) --- mypy/test/testpep561.py | 25 +++++++++++++++++++++++++ test-requirements.txt | 1 - 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index b8a11d7fc8af..da5025faef03 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -46,6 +46,28 @@ def virtualenv(python_executable: str = sys.executable) -> Iterator[tuple[str, s yield venv_dir, os.path.abspath(os.path.join(venv_dir, "bin", "python")) +def upgrade_pip(python_executable: str) -> None: + """Install pip>=21.3.1. Required for editable installs with PEP 660.""" + if ( + sys.version_info >= (3, 11) + or (3, 10, 3) <= sys.version_info < (3, 11) + or (3, 9, 11) <= sys.version_info < (3, 10) + or (3, 8, 13) <= sys.version_info < (3, 9) + ): + # Skip for more recent Python releases which come with pip>=21.3.1 + # out of the box - for performance reasons. + return + + install_cmd = [python_executable, "-m", "pip", "install", "pip>=21.3.1"] + try: + with filelock.FileLock(pip_lock, timeout=pip_timeout): + proc = subprocess.run(install_cmd, capture_output=True, env=os.environ) + except filelock.Timeout as err: + raise Exception(f"Failed to acquire {pip_lock}") from err + if proc.returncode != 0: + raise Exception(proc.stdout.decode("utf-8") + proc.stderr.decode("utf-8")) + + def install_package( pkg: str, python_executable: str = sys.executable, editable: bool = False ) -> None: @@ -93,6 +115,9 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: assert pkgs, "No packages to install for PEP 561 test?" with virtualenv(python) as venv: venv_dir, python_executable = venv + if editable: + # Editable installs with PEP 660 require pip>=21.3 + upgrade_pip(python_executable) for pkg in pkgs: install_package(pkg, python_executable, editable) diff --git a/test-requirements.txt b/test-requirements.txt index 8aea01f9e996..51cd62d4064c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,7 +5,6 @@ black==23.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 isort[colors]==5.12.0; python_version >= "3.8" # must match version in .pre-commit-config.yaml lxml>=4.9.1; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' -pip>=21.3.1 pre-commit pre-commit-hooks==4.4.0 psutil>=4.0 From c918d40b1500d141d14764ef967ca56ce633efb2 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 23 Jun 2023 06:59:31 +0300 Subject: [PATCH 0052/1617] Use `()` for `get_function_sig_hook` docs (#15499) --- docs/source/extending_mypy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index daf863616334..506f548db687 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -159,7 +159,7 @@ This hook will be also called for instantiation of classes. This is a good choice if the return type is too complex to be expressed by regular python typing. -**get_function_signature_hook** is used to adjust the signature of a function. +**get_function_signature_hook()** is used to adjust the signature of a function. **get_method_hook()** is the same as ``get_function_hook()`` but for methods instead of module level functions. From a47279b8626e3885580df1a33c179fc5b7e273ce Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 23 Jun 2023 12:41:03 +0100 Subject: [PATCH 0053/1617] Remove `py` and `pytest-forked` test dependencies (#15501) We don't use either of these in our own code at all. Neither of them should be needed in 2023. py used to be a dependency of pytest but no longer is. It was added to test-requirements.txt 6 years ago in order to pin a specific version to fix an issue where some tests were failing: https://github.com/python/mypy/commit/5168c2580ff81ba114776936bdeaf5d81812c34a and https://github.com/python/mypy/commit/ad6dd4ed6923a5eb6227fc9bfcb8e30a5e0fde53. pytest-forked used to be a dependency of pytest-xdist, but no longer is. It was added to test-requirements.txt` 4 years ago in order to pin a specific version to fix builds on macOS: https://github.com/python/mypy/commit/e006b94e097184451e6baf9b9c55e56176728ab8. --- test-requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 51cd62d4064c..7c4b3b869de5 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,9 +11,7 @@ psutil>=4.0 # pytest 6.2.3 does not support Python 3.10 pytest>=6.2.4 pytest-xdist>=1.34.0 -pytest-forked>=1.3.0,<2.0.0 pytest-cov>=2.10.0 -py>=1.5.2 ruff==0.0.272 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 six From 2f56b3f8ce1aed0cee7e163af2a0d442a3939023 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 23 Jun 2023 13:53:14 +0100 Subject: [PATCH 0054/1617] Unbreak CI (#15505) pytest 7.4.0 was just released, and our CI on `master` is very red as a result. Looks like we're using a private API that we shouldn't be: https://github.com/python/mypy/pull/15501#issuecomment-1604177495. For now let's just pin to pytest <7.4.0. https://pypi.org/project/pytest/7.4.0/ --- test-requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 7c4b3b869de5..f7d37058e544 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,8 @@ pre-commit pre-commit-hooks==4.4.0 psutil>=4.0 # pytest 6.2.3 does not support Python 3.10 -pytest>=6.2.4 +# TODO: fix use of removed private APIs so we can use the latest pytest +pytest>=6.2.4,<7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.0.272 # must match version in .pre-commit-config.yaml From 2d8ad8ef240df262be80d9359e9f5892ea1f9ad3 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 23 Jun 2023 06:05:05 -0700 Subject: [PATCH 0055/1617] Fix async iterator body stripping (#15491) Fixes #15489 --- mypy/fastparse.py | 58 +++++++++++++++------------ test-data/unit/check-async-await.test | 13 ++++-- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 902bde110421..fe59ff48bdfc 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -521,7 +521,14 @@ def translate_stmt_list( return [block] stack = self.class_and_function_stack - if self.strip_function_bodies and len(stack) == 1 and stack[0] == "F": + # Fast case for stripping function bodies + if ( + can_strip + and self.strip_function_bodies + and len(stack) == 1 + and stack[0] == "F" + and not is_coroutine + ): return [] res: list[Statement] = [] @@ -529,32 +536,33 @@ def translate_stmt_list( node = self.visit(stmt) res.append(node) - if ( - self.strip_function_bodies - and can_strip - and stack[-2:] == ["C", "F"] - and not is_possible_trivial_body(res) - ): - # We only strip method bodies if they don't assign to an attribute, as - # this may define an attribute which has an externally visible effect. - visitor = FindAttributeAssign() - for s in res: - s.accept(visitor) - if visitor.found: - break - else: - if is_coroutine: - # Yields inside an async function affect the return type and should not - # be stripped. - yield_visitor = FindYield() + # Slow case for stripping function bodies + if can_strip and self.strip_function_bodies: + if stack[-2:] == ["C", "F"]: + if is_possible_trivial_body(res): + can_strip = False + else: + # We only strip method bodies if they don't assign to an attribute, as + # this may define an attribute which has an externally visible effect. + visitor = FindAttributeAssign() for s in res: - s.accept(yield_visitor) - if yield_visitor.found: + s.accept(visitor) + if visitor.found: + can_strip = False break - else: - return [] - else: - return [] + + if can_strip and stack[-1] == "F" and is_coroutine: + # Yields inside an async function affect the return type and should not + # be stripped. + yield_visitor = FindYield() + for s in res: + s.accept(yield_visitor) + if yield_visitor.found: + can_strip = False + break + + if can_strip: + return [] return res def translate_type_comment( diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 83a66ef4a815..d0df2c9e0104 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -945,17 +945,21 @@ async def bar(x: Union[A, B]) -> None: [typing fixtures/typing-async.pyi] [case testAsyncIteratorWithIgnoredErrors] -from m import L +import m -async def func(l: L) -> None: +async def func(l: m.L) -> None: reveal_type(l.get_iterator) # N: Revealed type is "def () -> typing.AsyncIterator[builtins.str]" reveal_type(l.get_iterator2) # N: Revealed type is "def () -> typing.AsyncIterator[builtins.str]" async for i in l.get_iterator(): reveal_type(i) # N: Revealed type is "builtins.str" + reveal_type(m.get_generator) # N: Revealed type is "def () -> typing.AsyncGenerator[builtins.int, None]" + async for i2 in m.get_generator(): + reveal_type(i2) # N: Revealed type is "builtins.int" + [file m.py] # mypy: ignore-errors=True -from typing import AsyncIterator +from typing import AsyncIterator, AsyncGenerator class L: async def some_func(self, i: int) -> str: @@ -968,6 +972,9 @@ class L: if self: a = (yield 'x') +async def get_generator() -> AsyncGenerator[int, None]: + yield 1 + [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] From c099b20df02778455aaa3b5f59bb4deca096b8b0 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 23 Jun 2023 17:38:37 +0300 Subject: [PATCH 0056/1617] Fix error location for class patterns (#15506) Now it uses the same pattern for `.fail` as similar places: - https://github.com/python/mypy/blob/2d8ad8ef240df262be80d9359e9f5892ea1f9ad3/mypy/checkpattern.py#L459 - https://github.com/python/mypy/blob/2d8ad8ef240df262be80d9359e9f5892ea1f9ad3/mypy/checkpattern.py#L493 - etc Test proves that this problem is now fixed. Closes #15496 --- mypy/checkpattern.py | 2 +- test-data/unit/check-python310.test | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index e132a23ff55f..50d7b8a9826f 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -468,7 +468,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: 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) + self.msg.fail(message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o) return self.early_non_match() new_type, rest_type = self.chk.conditional_types_with_intersection( diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index feedd02d82eb..6416fa02bbce 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1958,3 +1958,23 @@ def redefinition_bad(a: int): ... [builtins fixtures/primitives.pyi] + +[case testPatternMatchingClassPatternLocation] +# See https://github.com/python/mypy/issues/15496 +from some_missing_lib import DataFrame, Series # type: ignore[import] +from typing import TypeVar + +T = TypeVar("T", Series, DataFrame) + +def f(x: T) -> None: + match x: + case Series() | DataFrame(): # type: ignore[misc] + pass + +def f2(x: T) -> None: + match x: + case Series(): # type: ignore[misc] + pass + case DataFrame(): # type: ignore[misc] + pass +[builtins fixtures/primitives.pyi] From c23936931688f69b9b9782e7d79c123698458e66 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 23 Jun 2023 18:20:39 +0100 Subject: [PATCH 0057/1617] [mypyc] Support the i16 native integer type (#15464) The `i16` type behaves the same as `i64` and `i32`, but is obviously smaller. The PR is big, but it's mostly because of test cases, which are adapted from `i32` test cases. Also fix error handling in unboxing of native int and float types (i.e. all types with overlapping error values). --- mypy/types.py | 6 +- mypyc/codegen/emit.py | 35 +- mypyc/doc/float_operations.rst | 6 +- mypyc/doc/int_operations.rst | 25 +- mypyc/doc/using_type_annotations.rst | 16 +- mypyc/ir/rtypes.py | 20 +- mypyc/irbuild/ll_builder.py | 14 +- mypyc/irbuild/mapper.py | 3 + mypyc/irbuild/specialize.py | 27 +- mypyc/lib-rt/CPy.h | 4 + mypyc/lib-rt/int_ops.c | 69 +++ mypyc/primitives/int_ops.py | 29 +- mypyc/test-data/irbuild-i16.test | 504 ++++++++++++++++++++ mypyc/test-data/irbuild-i32.test | 11 +- mypyc/test-data/irbuild-i64.test | 11 +- mypyc/test-data/run-i16.test | 338 +++++++++++++ mypyc/test-data/run-i32.test | 8 +- mypyc/test-data/run-i64.test | 19 +- mypyc/test/test_irbuild.py | 1 + mypyc/test/test_run.py | 1 + mypyc/test/test_typeops.py | 22 +- test-data/unit/lib-stub/mypy_extensions.pyi | 32 +- 22 files changed, 1160 insertions(+), 41 deletions(-) create mode 100644 mypyc/test-data/irbuild-i16.test create mode 100644 mypyc/test-data/run-i16.test diff --git a/mypy/types.py b/mypy/types.py index 25bfdd8b03c4..33673b58f775 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -150,7 +150,11 @@ ) # Mypyc fixed-width native int types (compatible with builtins.int) -MYPYC_NATIVE_INT_NAMES: Final = ("mypy_extensions.i64", "mypy_extensions.i32") +MYPYC_NATIVE_INT_NAMES: Final = ( + "mypy_extensions.i64", + "mypy_extensions.i32", + "mypy_extensions.i16", +) DATACLASS_TRANSFORM_NAMES: Final = ( "typing.dataclass_transform", diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 8114e0517219..8f0e0bc65edc 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -35,6 +35,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -900,28 +901,35 @@ def emit_unbox( self.emit_line(f" {dest} = 1;") elif is_int64_rprimitive(typ): # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int64_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt64({src});") - # TODO: Handle 'optional' - # TODO: Handle 'failure' + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_int32_rprimitive(typ): # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line(f"int32_t {dest};") self.emit_line(f"{dest} = CPyLong_AsInt32({src});") - # TODO: Handle 'optional' - # TODO: Handle 'failure' + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) + elif is_int16_rprimitive(typ): + # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values + if declare_dest: + self.emit_line(f"int16_t {dest};") + self.emit_line(f"{dest} = CPyLong_AsInt16({src});") + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_float_rprimitive(typ): + assert not optional # Not supported for overlapping error values if declare_dest: self.emit_line("double {};".format(dest)) # TODO: Don't use __float__ and __index__ self.emit_line(f"{dest} = PyFloat_AsDouble({src});") - self.emit_lines( - f"if ({dest} == -1.0 && PyErr_Occurred()) {{", f"{dest} = -113.0;", "}" - ) - # TODO: Handle 'optional' - # TODO: Handle 'failure' + self.emit_lines(f"if ({dest} == -1.0 && PyErr_Occurred()) {{", failure, "}") elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) if declare_dest: @@ -1006,7 +1014,7 @@ def emit_box( self.emit_lines(f"{declaration}{dest} = Py_None;") if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) - elif is_int32_rprimitive(typ): + elif is_int32_rprimitive(typ) or is_int16_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLong({src});") elif is_int64_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLongLong({src});") @@ -1137,6 +1145,13 @@ def _emit_traceback( if DEBUG_ERRORS: self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') + def emit_unbox_failure_with_overlapping_error_value( + self, dest: str, typ: RType, failure: str + ) -> None: + self.emit_line(f"if ({dest} == {self.c_error_value(typ)} && PyErr_Occurred()) {{") + self.emit_line(failure) + self.emit_line("}") + def c_array_initializer(components: list[str], *, indented: bool = False) -> str: """Construct an initializer for a C array variable. diff --git a/mypyc/doc/float_operations.rst b/mypyc/doc/float_operations.rst index 915c184ae8e7..1705b7672409 100644 --- a/mypyc/doc/float_operations.rst +++ b/mypyc/doc/float_operations.rst @@ -14,6 +14,7 @@ Construction * ``float(x: int)`` * ``float(x: i64)`` * ``float(x: i32)`` +* ``float(x: i16)`` * ``float(x: str)`` * ``float(x: float)`` (no-op) @@ -28,8 +29,9 @@ Functions --------- * ``int(f)`` -* ``i32(f)`` (convert to ``i32``) -* ``i64(f)`` (convert to ``i64``) +* ``i64(f)`` (convert to 64-bit signed integer) +* ``i32(f)`` (convert to 32-bit signed integer) +* ``i16(f)`` (convert to 16-bit signed integer) * ``abs(f)`` * ``math.sin(f)`` * ``math.cos(f)`` diff --git a/mypyc/doc/int_operations.rst b/mypyc/doc/int_operations.rst index 058fdbd511dd..88a4a9d778a1 100644 --- a/mypyc/doc/int_operations.rst +++ b/mypyc/doc/int_operations.rst @@ -8,14 +8,16 @@ Mypyc supports these integer types: * ``int`` (arbitrary-precision integer) * ``i64`` (64-bit signed integer) * ``i32`` (32-bit signed integer) +* ``i16`` (16-bit signed integer) -``i64`` and ``i32`` are *native integer types* and must be imported +``i64``, ``i32`` and ``i16`` are *native integer types* and must be imported from the ``mypy_extensions`` module. ``int`` corresponds to the Python ``int`` type, but uses a more efficient runtime representation (tagged -pointer). Native integer types are value types. All integer types have -optimized primitive operations, but the native integer types are more -efficient than ``int``, since they don't require range or bounds -checks. +pointer). Native integer types are value types. + +All integer types have optimized primitive operations, but the native +integer types are more efficient than ``int``, since they don't +require range or bounds checks. Operations on integers that are listed here have fast, optimized implementations. Other integer operations use generic implementations @@ -31,6 +33,7 @@ Construction * ``int(x: float)`` * ``int(x: i64)`` * ``int(x: i32)`` +* ``int(x: i16)`` * ``int(x: str)`` * ``int(x: str, base: int)`` * ``int(x: int)`` (no-op) @@ -40,6 +43,7 @@ Construction * ``i64(x: int)`` * ``i64(x: float)`` * ``i64(x: i32)`` +* ``i64(x: i16)`` * ``i64(x: str)`` * ``i64(x: str, base: int)`` * ``i64(x: i64)`` (no-op) @@ -49,10 +53,21 @@ Construction * ``i32(x: int)`` * ``i32(x: float)`` * ``i32(x: i64)`` (truncate) +* ``i32(x: i16)`` * ``i32(x: str)`` * ``i32(x: str, base: int)`` * ``i32(x: i32)`` (no-op) +``i16`` type: + +* ``i16(x: int)`` +* ``i16(x: float)`` +* ``i16(x: i64)`` (truncate) +* ``i16(x: i32)`` (truncate) +* ``i16(x: str)`` +* ``i16(x: str, base: int)`` +* ``i16(x: i16)`` (no-op) + Conversions from ``int`` to a native integer type raise ``OverflowError`` if the value is too large or small. Conversions from a wider native integer type to a narrower one truncate the value and never diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 6c9277786751..f095a6717271 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -32,6 +32,7 @@ implementations: * ``int`` (:ref:`native operations `) * ``i64`` (:ref:`documentation `, :ref:`native operations `) * ``i32`` (:ref:`documentation `, :ref:`native operations `) +* ``i16`` (:ref:`documentation `, :ref:`native operations `) * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) @@ -342,13 +343,14 @@ Examples:: Native integer types -------------------- -You can use the native integer types ``i64`` (64-bit signed integer) -and ``i32`` (32-bit signed integer) if you know that integer values -will always fit within fixed bounds. These types are faster than the -arbitrary-precision ``int`` type, since they don't require overflow -checks on operations. ``i32`` may also use less memory than ``int`` -values. The types are imported from the ``mypy_extensions`` module -(installed via ``pip install mypy_extensions``). +You can use the native integer types ``i64`` (64-bit signed integer), +``i32`` (32-bit signed integer), and ``i16`` (16-bit signed integer) +if you know that integer values will always fit within fixed +bounds. These types are faster than the arbitrary-precision ``int`` +type, since they don't require overflow checks on operations. ``i32`` +and ``i16`` may also use less memory than ``int`` values. The types +are imported from the ``mypy_extensions`` module (installed via ``pip +install mypy_extensions``). Example:: diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 7ff82ac9b297..61f83c9c041e 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -206,7 +206,7 @@ def __init__( self.error_overlap = error_overlap if ctype == "CPyTagged": self.c_undefined = "CPY_INT_TAG" - elif ctype in ("int32_t", "int64_t"): + elif ctype in ("int16_t", "int32_t", "int64_t"): # This is basically an arbitrary value that is pretty # unlikely to overlap with a real value. self.c_undefined = "-113" @@ -290,6 +290,16 @@ def __hash__(self) -> int: # Low level integer types (correspond to C integer types) +int16_rprimitive: Final = RPrimitive( + "int16", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=True, + ctype="int16_t", + size=2, + error_overlap=True, +) int32_rprimitive: Final = RPrimitive( "int32", is_unboxed=True, @@ -432,6 +442,10 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive +def is_int16_rprimitive(rtype: RType) -> bool: + return rtype is int16_rprimitive + + def is_int32_rprimitive(rtype: RType) -> bool: return rtype is int32_rprimitive or ( rtype is c_pyssize_t_rprimitive and rtype._ctype == "int32_t" @@ -445,7 +459,7 @@ def is_int64_rprimitive(rtype: RType) -> bool: def is_fixed_width_rtype(rtype: RType) -> bool: - return is_int32_rprimitive(rtype) or is_int64_rprimitive(rtype) + return is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) def is_uint32_rprimitive(rtype: RType) -> bool: @@ -536,6 +550,8 @@ def visit_rprimitive(self, t: RPrimitive) -> str: return "8" # "8 byte integer" elif t._ctype == "int32_t": return "4" # "4 byte integer" + elif t._ctype == "int16_t": + return "2" # "2 byte integer" elif t._ctype == "double": return "F" assert not t.is_unboxed, f"{t} unexpected unboxed type" diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index aa152d32a144..a37bcc0dbb86 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -104,6 +104,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -146,6 +147,9 @@ py_vectorcall_op, ) from mypyc.primitives.int_ops import ( + int16_divide_op, + int16_mod_op, + int16_overflow, int32_divide_op, int32_mod_op, int32_overflow, @@ -456,6 +460,10 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - # Slow path just always generates an OverflowError self.call_c(int32_overflow, [], line) self.add(Unreachable()) + elif is_int16_rprimitive(target_type): + # Slow path just always generates an OverflowError + self.call_c(int16_overflow, [], line) + self.add(Unreachable()) else: assert False, target_type @@ -469,7 +477,7 @@ def coerce_short_int_to_fixed_width(self, src: Value, target_type: RType, line: assert False, (src.type, target_type) def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: - if is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8: + if (is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) or is_int16_rprimitive(src.type): # Simple case -- just sign extend and shift. extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=True)) return self.int_op( @@ -2038,6 +2046,8 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: prim = int64_divide_op elif is_int32_rprimitive(type): prim = int32_divide_op + elif is_int16_rprimitive(type): + prim = int16_divide_op else: assert False, type return self.call_c(prim, [lhs, rhs], line) @@ -2050,6 +2060,8 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: prim = int64_mod_op elif is_int32_rprimitive(type): prim = int32_mod_op + elif is_int16_rprimitive(type): + prim = int16_mod_op else: assert False, type return self.call_c(prim, [lhs, rhs], line) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index dddb35230fd5..a4712f4af524 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -32,6 +32,7 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -102,6 +103,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return int64_rprimitive elif typ.type.fullname == "mypy_extensions.i32": return int32_rprimitive + elif typ.type.fullname == "mypy_extensions.i16": + return int16_rprimitive else: return object_rprimitive elif isinstance(typ, TupleType): diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 14a6f8e92df2..06af4d6d7426 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -49,6 +49,7 @@ bool_rprimitive, c_int_rprimitive, dict_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -56,6 +57,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, @@ -163,6 +165,7 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va @specialize_function("builtins.complex") @specialize_function("mypy_extensions.i64") @specialize_function("mypy_extensions.i32") +@specialize_function("mypy_extensions.i16") def translate_builtins_with_unary_dunder( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: @@ -171,7 +174,7 @@ def translate_builtins_with_unary_dunder( arg = expr.args[0] arg_typ = builder.node_type(arg) shortname = callee.fullname.split(".")[1] - if shortname in ("i64", "i32"): + if shortname in ("i64", "i32", "i16"): method = "__int__" else: method = f"__{shortname}__" @@ -680,7 +683,7 @@ def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value arg_type = builder.node_type(arg) if is_int64_rprimitive(arg_type): return builder.accept(arg) - elif is_int32_rprimitive(arg_type): + elif is_int32_rprimitive(arg_type) or is_int16_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Extend(val, int64_rprimitive, signed=True, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): @@ -700,12 +703,32 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int64_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Truncate(val, int32_rprimitive, line=expr.line)) + elif is_int16_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int32_rprimitive, signed=True, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) return builder.coerce(val, int32_rprimitive, expr.line) return None +@specialize_function("mypy_extensions.i16") +def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: + return None + arg = expr.args[0] + arg_type = builder.node_type(arg) + if is_int16_rprimitive(arg_type): + return builder.accept(arg) + elif is_int32_rprimitive(arg_type) or is_int64_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Truncate(val, int16_rprimitive, line=expr.line)) + elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): + val = builder.accept(arg) + return builder.coerce(val, int16_rprimitive, expr.line) + return None + + @specialize_function("builtins.int") def translate_int(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 30d93be60989..689526e0e826 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -155,6 +155,10 @@ int32_t CPyLong_AsInt32(PyObject *o); int32_t CPyInt32_Divide(int32_t x, int32_t y); int32_t CPyInt32_Remainder(int32_t x, int32_t y); void CPyInt32_Overflow(void); +int16_t CPyLong_AsInt16(PyObject *o); +int16_t CPyInt16_Divide(int16_t x, int16_t y); +int16_t CPyInt16_Remainder(int16_t x, int16_t y); +void CPyInt16_Overflow(void); double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y); static inline int CPyTagged_CheckLong(CPyTagged x) { diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 843d9b0d2230..56720d1992e8 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -641,6 +641,75 @@ void CPyInt32_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i32"); } +int16_t CPyLong_AsInt16(PyObject *o) { + if (likely(PyLong_Check(o))) { + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + digit x = lobj->ob_digit[0]; + if (x < 0x8000) + return x; + } else if (likely(size == 0)) { + return 0; + } + } + // Slow path + int overflow; + long result = PyLong_AsLongAndOverflow(o, &overflow); + if (result > 0x7fff || result < -0x8000) { + overflow = 1; + result = -1; + } + if (result == -1) { + if (PyErr_Occurred()) { + return CPY_LL_INT_ERROR; + } else if (overflow) { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); + return CPY_LL_INT_ERROR; + } + } + return result; +} + +int16_t CPyInt16_Divide(int16_t x, int16_t y) { + if (y == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); + return CPY_LL_INT_ERROR; + } + if (y == -1 && x == INT16_MIN) { + PyErr_SetString(PyExc_OverflowError, "integer division overflow"); + return CPY_LL_INT_ERROR; + } + int16_t d = x / y; + // Adjust for Python semantics + if (((x < 0) != (y < 0)) && d * y != x) { + d--; + } + return d; +} + +int16_t CPyInt16_Remainder(int16_t x, int16_t y) { + if (y == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); + return CPY_LL_INT_ERROR; + } + // Edge case: avoid core dump + if (y == -1 && x == INT16_MIN) { + return 0; + } + int16_t d = x % y; + // Adjust for Python semantics + if (((x < 0) != (y < 0)) && d != 0) { + d += y; + } + return d; +} + +void CPyInt16_Overflow() { + PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); +} + double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { if (unlikely(y == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index eff4b4ffd8ab..ef79bbc51ce6 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -19,6 +19,7 @@ bool_rprimitive, c_pyssize_t_rprimitive, float_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -37,7 +38,12 @@ # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. -for int_name in ("builtins.int", "mypy_extensions.i64", "mypy_extensions.i32"): +for int_name in ( + "builtins.int", + "mypy_extensions.i64", + "mypy_extensions.i32", + "mypy_extensions.i16", +): # These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? @@ -231,6 +237,20 @@ class IntComparisonOpDescription(NamedTuple): error_kind=ERR_MAGIC_OVERLAPPING, ) +int16_divide_op = custom_op( + arg_types=[int16_rprimitive, int16_rprimitive], + return_type=int16_rprimitive, + c_function_name="CPyInt16_Divide", + error_kind=ERR_MAGIC_OVERLAPPING, +) + +int16_mod_op = custom_op( + arg_types=[int16_rprimitive, int16_rprimitive], + return_type=int16_rprimitive, + c_function_name="CPyInt16_Remainder", + error_kind=ERR_MAGIC_OVERLAPPING, +) + # Convert tagged int (as PyObject *) to i64 int_to_int64_op = custom_op( arg_types=[object_rprimitive], @@ -267,3 +287,10 @@ class IntComparisonOpDescription(NamedTuple): c_function_name="CPyInt32_Overflow", error_kind=ERR_ALWAYS, ) + +int16_overflow = custom_op( + arg_types=[], + return_type=void_rtype, + c_function_name="CPyInt16_Overflow", + error_kind=ERR_ALWAYS, +) diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test new file mode 100644 index 000000000000..0f34ea53818d --- /dev/null +++ b/mypyc/test-data/irbuild-i16.test @@ -0,0 +1,504 @@ +# Test cases for i16 native ints. Focus on things that are different from i64; no need to +# duplicate all i64 test cases here. + +[case testI16BinaryOp] +from mypy_extensions import i16 + +def add_op(x: i16, y: i16) -> i16: + x = y + x + y = x + 5 + y += x + y += 7 + x = 5 + y + return x +def compare(x: i16, y: i16) -> None: + a = x == y + b = x == -5 + c = x < y + d = x < -5 + e = -5 == x + f = -5 < x +[out] +def add_op(x, y): + x, y, r0, r1, r2, r3, r4 :: int16 +L0: + r0 = y + x + x = r0 + r1 = x + 5 + y = r1 + r2 = y + x + y = r2 + r3 = y + 7 + y = r3 + r4 = 5 + y + x = r4 + return x +def compare(x, y): + x, y :: int16 + r0 :: bit + a :: bool + r1 :: bit + b :: bool + r2 :: bit + c :: bool + r3 :: bit + d :: bool + r4 :: bit + e :: bool + r5 :: bit + f :: bool +L0: + r0 = x == y + a = r0 + r1 = x == -5 + b = r1 + r2 = x < y :: signed + c = r2 + r3 = x < -5 :: signed + d = r3 + r4 = -5 == x + e = r4 + r5 = -5 < x :: signed + f = r5 + return 1 + +[case testI16UnaryOp] +from mypy_extensions import i16 + +def unary(x: i16) -> i16: + y = -x + x = ~y + y = +x + return y +[out] +def unary(x): + x, r0, y, r1 :: int16 +L0: + r0 = 0 - x + y = r0 + r1 = y ^ -1 + x = r1 + y = x + return y + +[case testI16DivisionByConstant] +from mypy_extensions import i16 + +def div_by_constant(x: i16) -> i16: + x = x // 5 + x //= 17 + return x +[out] +def div_by_constant(x): + x, r0, r1 :: int16 + r2, r3, r4 :: bit + r5 :: int16 + r6 :: bit + r7, r8, r9 :: int16 + r10, r11, r12 :: bit + r13 :: int16 + r14 :: bit + r15 :: int16 +L0: + r0 = x / 5 + r1 = r0 + r2 = x < 0 :: signed + r3 = 5 < 0 :: signed + r4 = r2 == r3 + if r4 goto L3 else goto L1 :: bool +L1: + r5 = r1 * 5 + r6 = r5 == x + if r6 goto L3 else goto L2 :: bool +L2: + r7 = r1 - 1 + r1 = r7 +L3: + x = r1 + r8 = x / 17 + r9 = r8 + r10 = x < 0 :: signed + r11 = 17 < 0 :: signed + r12 = r10 == r11 + if r12 goto L6 else goto L4 :: bool +L4: + r13 = r9 * 17 + r14 = r13 == x + if r14 goto L6 else goto L5 :: bool +L5: + r15 = r9 - 1 + r9 = r15 +L6: + x = r9 + return x + +[case testI16ModByConstant] +from mypy_extensions import i16 + +def mod_by_constant(x: i16) -> i16: + x = x % 5 + x %= 17 + return x +[out] +def mod_by_constant(x): + x, r0, r1 :: int16 + r2, r3, r4, r5 :: bit + r6, r7, r8 :: int16 + r9, r10, r11, r12 :: bit + r13 :: int16 +L0: + r0 = x % 5 + r1 = r0 + r2 = x < 0 :: signed + r3 = 5 < 0 :: signed + r4 = r2 == r3 + if r4 goto L3 else goto L1 :: bool +L1: + r5 = r1 == 0 + if r5 goto L3 else goto L2 :: bool +L2: + r6 = r1 + 5 + r1 = r6 +L3: + x = r1 + r7 = x % 17 + r8 = r7 + r9 = x < 0 :: signed + r10 = 17 < 0 :: signed + r11 = r9 == r10 + if r11 goto L6 else goto L4 :: bool +L4: + r12 = r8 == 0 + if r12 goto L6 else goto L5 :: bool +L5: + r13 = r8 + 17 + r8 = r13 +L6: + x = r8 + return x + +[case testI16DivModByVariable] +from mypy_extensions import i16 + +def divmod(x: i16, y: i16) -> i16: + a = x // y + return a % y +[out] +def divmod(x, y): + x, y, r0, a, r1 :: int16 +L0: + r0 = CPyInt16_Divide(x, y) + a = r0 + r1 = CPyInt16_Remainder(a, y) + return r1 + +[case testI16BoxAndUnbox] +from typing import Any +from mypy_extensions import i16 + +def f(x: Any) -> Any: + y: i16 = x + return y +[out] +def f(x): + x :: object + r0, y :: int16 + r1 :: object +L0: + r0 = unbox(int16, x) + y = r0 + r1 = box(int16, y) + return r1 + +[case testI16MixedCompare1] +from mypy_extensions import i16 +def f(x: int, y: i16) -> bool: + return x == y +[out] +def f(x, y): + x :: int + y :: int16 + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 + r7 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = r6 == y + return r7 + +[case testI16MixedCompare2] +from mypy_extensions import i16 +def f(x: i16, y: int) -> bool: + return x == y +[out] +def f(x, y): + x :: int16 + y :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 + r7 :: bit +L0: + r0 = y & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = y < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = y >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = y >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = x == r6 + return r7 + +[case testI16ConvertToInt] +from mypy_extensions import i16 + +def i16_to_int(a: i16) -> int: + return a +[out] +def i16_to_int(a): + a :: int16 + r0 :: native_int + r1 :: int +L0: + r0 = extend signed a: int16 to native_int + r1 = r0 << 1 + return r1 + +[case testI16OperatorAssignmentMixed] +from mypy_extensions import i16 + +def f(a: i16) -> None: + x = 0 + x += a +[out] +def f(a): + a :: int16 + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6, r7 :: int16 + r8 :: native_int + r9 :: int +L0: + x = 0 + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + r7 = r6 + a + r8 = extend signed r7: int16 to native_int + r9 = r8 << 1 + x = r9 + return 1 + +[case testI16InitializeFromLiteral] +from mypy_extensions import i16, i64 + +def f() -> None: + x: i16 = 0 + y: i16 = -127 + z: i16 = 5 + 7 +[out] +def f(): + x, y, z :: int16 +L0: + x = 0 + y = -127 + z = 12 + return 1 + +[case testI16ExplicitConversionFromNativeInt] +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i16: + return i16(x) + +def from_i32(x: i32) -> i16: + return i16(x) + +def from_i64(x: i64) -> i16: + return i16(x) +[out] +def from_i16(x): + x :: int16 +L0: + return x +def from_i32(x): + x :: int32 + r0 :: int16 +L0: + r0 = truncate x: int32 to int16 + return r0 +def from_i64(x): + x :: int64 + r0 :: int16 +L0: + r0 = truncate x: int64 to int16 + return r0 + +[case testI16ExplicitConversionFromInt] +from mypy_extensions import i16 + +def f(x: int) -> i16: + return i16(x) +[out] +def f(x): + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: int16 +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 65536 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= -65536 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to int16 + r6 = r5 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + return r6 + +[case testI16ExplicitConversionFromLiteral] +from mypy_extensions import i16 + +def f() -> None: + x = i16(0) + y = i16(11) + z = i16(-3) +[out] +def f(): + x, y, z :: int16 +L0: + x = 0 + y = 11 + z = -3 + return 1 + +[case testI16ExplicitConversionFromVariousTypes] +from mypy_extensions import i16 + +def bool_to_i16(b: bool) -> i16: + return i16(b) + +def str_to_i16(s: str) -> i16: + return i16(s) + +class C: + def __int__(self) -> i16: + return 5 + +def instance_to_i16(c: C) -> i16: + return i16(c) + +def float_to_i16(x: float) -> i16: + return i16(x) +[out] +def bool_to_i16(b): + b :: bool + r0 :: int16 +L0: + r0 = extend b: builtins.bool to int16 + return r0 +def str_to_i16(s): + s :: str + r0 :: object + r1 :: int16 +L0: + r0 = CPyLong_FromStr(s) + r1 = unbox(int16, r0) + return r1 +def C.__int__(self): + self :: __main__.C +L0: + return 5 +def instance_to_i16(c): + c :: __main__.C + r0 :: int16 +L0: + r0 = c.__int__() + return r0 +def float_to_i16(x): + x :: float + r0 :: int + r1 :: native_int + r2, r3, r4 :: bit + r5 :: native_int + r6, r7 :: int16 +L0: + r0 = CPyTagged_FromFloat(x) + r1 = r0 & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L4 :: bool +L1: + r3 = r0 < 65536 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = r0 >= -65536 :: signed + if r4 goto L3 else goto L4 :: bool +L3: + r5 = r0 >> 1 + r6 = truncate r5: native_int to int16 + r7 = r6 + goto L5 +L4: + CPyInt16_Overflow() + unreachable +L5: + return r7 diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index 725e183657b1..4b69fa3a6bbb 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -413,7 +413,10 @@ L0: return 1 [case testI32ExplicitConversionFromNativeInt] -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i32: + return i32(x) def from_i32(x: i32) -> i32: return i32(x) @@ -421,6 +424,12 @@ def from_i32(x: i32) -> i32: def from_i64(x: i64) -> i32: return i32(x) [out] +def from_i16(x): + x :: int16 + r0 :: int32 +L0: + r0 = extend signed x: int16 to int32 + return r0 def from_i32(x): x :: int32 L0: diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index a18171c41d57..38561178ddd0 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1506,7 +1506,10 @@ L0: return 1 [case testI64ExplicitConversionFromNativeInt] -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 + +def from_i16(x: i16) -> i64: + return i64(x) def from_i32(x: i32) -> i64: return i64(x) @@ -1514,6 +1517,12 @@ def from_i32(x: i32) -> i64: def from_i64(x: i64) -> i64: return i64(x) [out] +def from_i16(x): + x :: int16 + r0 :: int64 +L0: + r0 = extend signed x: int16 to int64 + return r0 def from_i32(x): x :: int32 r0 :: int64 diff --git a/mypyc/test-data/run-i16.test b/mypyc/test-data/run-i16.test new file mode 100644 index 000000000000..fbb0c15220bc --- /dev/null +++ b/mypyc/test-data/run-i16.test @@ -0,0 +1,338 @@ +[case testI16BasicOps] +from typing import Any, Tuple + +from mypy_extensions import i16, i32, i64 + +from testutil import assertRaises + +def test_box_and_unbox() -> None: + values = (list(range(-2**15, -2**15 + 100)) + + list(range(-1000, 1000)) + + list(range(2**15 - 100, 2**15))) + for i in values: + o: Any = i + x: i16 = o + o2: Any = x + assert o == o2 + assert x == i + with assertRaises(OverflowError, "int too large to convert to i16"): + o = 2**15 + x2: i16 = o + with assertRaises(OverflowError, "int too large to convert to i16"): + o = -2**15 - 1 + x3: i16 = o + +def div_by_7(x: i16) -> i16: + return x // 7 +def div_by_neg_7(x: i16) -> i16: + return x // -7 + +def div(x: i16, y: i16) -> i16: + return x // y + +def test_divide_by_constant() -> None: + for i in range(-1000, 1000): + assert div_by_7(i) == i // 7 + for i in range(-2**15, -2**15 + 1000): + assert div_by_7(i) == i // 7 + for i in range(2**15 - 1000, 2**15): + assert div_by_7(i) == i // 7 + +def test_divide_by_negative_constant() -> None: + for i in range(-1000, 1000): + assert div_by_neg_7(i) == i // -7 + for i in range(-2**15, -2**15 + 1000): + assert div_by_neg_7(i) == i // -7 + for i in range(2**15 - 1000, 2**15): + assert div_by_neg_7(i) == i // -7 + +def test_divide_by_variable() -> None: + values = (list(range(-50, 50)) + + list(range(-2**15, -2**15 + 10)) + + list(range(2**15 - 10, 2**15))) + for x in values: + for y in values: + if y != 0: + if x // y == 2**15: + with assertRaises(OverflowError, "integer division overflow"): + div(x, y) + else: + assert div(x, y) == x // y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + div(x, y) + +def mod_by_7(x: i16) -> i16: + return x % 7 + +def mod_by_neg_7(x: i16) -> i16: + return x // -7 + +def mod(x: i16, y: i16) -> i16: + return x % y + +def test_mod_by_constant() -> None: + for i in range(-1000, 1000): + assert mod_by_7(i) == i % 7 + for i in range(-2**15, -2**15 + 1000): + assert mod_by_7(i) == i % 7 + for i in range(2**15 - 1000, 2**15): + assert mod_by_7(i) == i % 7 + +def test_mod_by_negative_constant() -> None: + for i in range(-1000, 1000): + assert mod_by_neg_7(i) == i // -7 + for i in range(-2**15, -2**15 + 1000): + assert mod_by_neg_7(i) == i // -7 + for i in range(2**15 - 1000, 2**15): + assert mod_by_neg_7(i) == i // -7 + +def test_mod_by_variable() -> None: + values = (list(range(-50, 50)) + + list(range(-2**15, -2**15 + 10)) + + list(range(2**15 - 10, 2**15))) + for x in values: + for y in values: + if y != 0: + assert mod(x, y) == x % y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + mod(x, y) + +def test_simple_arithmetic_ops() -> None: + zero: i16 = int() + one: i16 = zero + 1 + two: i16 = one + 1 + neg_one: i16 = -one + assert one + one == 2 + assert one + two == 3 + assert one + neg_one == 0 + assert one - one == 0 + assert one - two == -1 + assert one * one == 1 + assert one * two == 2 + assert two * two == 4 + assert two * neg_one == -2 + assert neg_one * one == -1 + assert neg_one * neg_one == 1 + assert two * 0 == 0 + assert 0 * two == 0 + assert -one == -1 + assert -two == -2 + assert -neg_one == 1 + assert -zero == 0 + +def test_bitwise_ops() -> None: + x: i16 = 13855 + int() + y: i16 = 367 + int() + z: i16 = -11091 + int() + zero: i16 = int() + one: i16 = zero + 1 + two: i16 = zero + 2 + neg_one: i16 = -one + + assert x & y == 15 + assert x & z == 5133 + assert z & z == z + assert x & zero == 0 + + assert x | y == 14207 + assert x | z == -2369 + assert z | z == z + assert x | 0 == x + + assert x ^ y == 14192 + assert x ^ z == -7502 + assert z ^ z == 0 + assert z ^ 0 == z + + assert x << one == 27710 + assert x << two == -10116 + assert z << two == 21172 + assert z << 0 == z + + assert x >> one == 6927 + assert x >> two == 3463 + assert z >> two == -2773 + assert z >> 0 == z + + assert ~x == -13856 + assert ~z == 11090 + assert ~zero == -1 + assert ~neg_one == 0 + +def eq(x: i16, y: i16) -> bool: + return x == y + +def test_eq() -> None: + assert eq(int(), int()) + assert eq(5 + int(), 5 + int()) + assert eq(-5 + int(), -5 + int()) + assert not eq(int(), 1 + int()) + assert not eq(5 + int(), 6 + int()) + assert not eq(-5 + int(), -6 + int()) + assert not eq(-5 + int(), 5 + int()) + +def test_comparisons() -> None: + one: i16 = 1 + int() + one2: i16 = 1 + int() + two: i16 = 2 + int() + assert one < two + assert not (one < one2) + assert not (two < one) + assert two > one + assert not (one > one2) + assert not (one > two) + assert one <= two + assert one <= one2 + assert not (two <= one) + assert two >= one + assert one >= one2 + assert not (one >= two) + assert one == one2 + assert not (one == two) + assert one != two + assert not (one != one2) + +def test_mixed_comparisons() -> None: + i16_3: i16 = int() + 3 + int_5 = int() + 5 + assert i16_3 < int_5 + assert int_5 > i16_3 + b = i16_3 > int_5 + assert not b + + int_largest = int() + (1 << 15) - 1 + assert int_largest > i16_3 + int_smallest = int() - (1 << 15) + assert i16_3 > int_smallest + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert i16_3 < int_too_big + with assertRaises(OverflowError): + assert int_too_big < i16_3 + with assertRaises(OverflowError): + assert i16_3 > int_too_small + with assertRaises(OverflowError): + assert int_too_small < i16_3 + +def test_mixed_arithmetic_and_bitwise_ops() -> None: + i16_3: i16 = int() + 3 + int_5 = int() + 5 + assert i16_3 + int_5 == 8 + assert int_5 - i16_3 == 2 + assert i16_3 << int_5 == 96 + assert int_5 << i16_3 == 40 + assert i16_3 ^ int_5 == 6 + assert int_5 | i16_3 == 7 + + int_largest = int() + (1 << 15) - 1 + assert int_largest - i16_3 == 32764 + int_smallest = int() - (1 << 15) + assert int_smallest + i16_3 == -32765 + + int_too_big = int() + (1 << 15) + int_too_small = int() - (1 << 15) - 1 + with assertRaises(OverflowError): + assert i16_3 & int_too_big + with assertRaises(OverflowError): + assert int_too_small & i16_3 + +def test_coerce_to_and_from_int() -> None: + for shift in range(0, 16): + for sign in 1, -1: + for delta in range(-5, 5): + n = sign * (1 << shift) + delta + if -(1 << 15) <= n < (1 << 15): + x: i16 = n + m: int = x + assert m == n + +def test_explicit_conversion_to_i16() -> None: + x = i16(5) + assert x == 5 + y = int() - 113 + x = i16(y) + assert x == -113 + n64: i64 = 1733 + x = i16(n64) + assert x == 1733 + n32: i32 = -1733 + x = i16(n32) + assert x == -1733 + z = i16(x) + assert z == -1733 + +def test_explicit_conversion_overflow() -> None: + max_i16 = int() + 2**15 - 1 + x = i16(max_i16) + assert x == 2**15 - 1 + assert int(x) == max_i16 + + min_i16 = int() - 2**15 + y = i16(min_i16) + assert y == -2**15 + assert int(y) == min_i16 + + too_big = int() + 2**15 + with assertRaises(OverflowError): + x = i16(too_big) + + too_small = int() - 2**15 - 1 + with assertRaises(OverflowError): + x = i16(too_small) + +def test_i16_from_large_small_literal() -> None: + x = i16(2**15 - 1) + assert x == 2**15 - 1 + x = i16(-2**15) + assert x == -2**15 + +def test_i16_truncate_from_i64() -> None: + large = i64(2**32 + 65536 + 157 + int()) + x = i16(large) + assert x == 157 + small = i64(-2**32 - 65536 - 157 + int()) + x = i16(small) + assert x == -157 + large2 = i64(2**15 + int()) + x = i16(large2) + assert x == -2**15 + small2 = i64(-2**15 - 1 - int()) + x = i16(small2) + assert x == 2**15 - 1 + +def test_i16_truncate_from_i32() -> None: + large = i32(2**16 + 2**30 + 5 + int()) + assert i16(large) == 5 + small = i32(-2**16 - 2**30 - 1 + int()) + assert i16(small) == -1 + +def from_float(x: float) -> i16: + return i16(x) + +def test_explicit_conversion_from_float() -> None: + assert from_float(0.0) == 0 + assert from_float(1.456) == 1 + assert from_float(-1234.567) == -1234 + assert from_float(2**15 - 1) == 2**15 - 1 + assert from_float(-2**15) == -2**15 + # The error message could be better, but this is acceptable + with assertRaises(OverflowError, "int too large to convert to i16"): + assert from_float(float(2**15)) + with assertRaises(OverflowError, "int too large to convert to i16"): + # One ulp below the lowest valid i64 value + from_float(float(-2**15 - 1)) + +def test_tuple_i16() -> None: + a: i16 = 1 + b: i16 = 2 + t = (a, b) + a, b = t + assert a == 1 + assert b == 2 + x: Any = t + tt: Tuple[i16, i16] = x + assert tt == (1, 2) diff --git a/mypyc/test-data/run-i32.test b/mypyc/test-data/run-i32.test index af99fb79d35e..bb1fa43bb9fd 100644 --- a/mypyc/test-data/run-i32.test +++ b/mypyc/test-data/run-i32.test @@ -1,7 +1,7 @@ [case testI32BasicOps] from typing import Any, Tuple -from mypy_extensions import i32, i64 +from mypy_extensions import i16, i32, i64 from testutil import assertRaises @@ -259,11 +259,15 @@ def test_explicit_conversion_to_i32() -> None: n64: i64 = 1733 x = i32(n64) assert x == 1733 - n32 = -1733 + n32: i32 = -1733 x = i32(n32) assert x == -1733 z = i32(x) assert z == -1733 + a: i16 = int() + 19764 + assert i32(a) == 19764 + a = int() - 1 + assert i32(a) == -1 def test_explicit_conversion_overflow() -> None: max_i32 = int() + 2**31 - 1 diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index bcde39fed5ff..1a82ac3e2dd1 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1,7 +1,7 @@ [case testI64BasicOps] from typing import List, Any, Tuple, Union -from mypy_extensions import i64, i32 +from mypy_extensions import i64, i32, i16 from testutil import assertRaises @@ -282,6 +282,10 @@ def test_explicit_conversion_to_i64() -> None: assert x == -1733 z = i64(x) assert z == -1733 + a: i16 = int() + 19764 + assert i64(a) == 19764 + a = int() - 1 + assert i64(a) == -1 def test_explicit_conversion_overflow() -> None: max_i64 = int() + 2**63 - 1 @@ -1500,3 +1504,16 @@ def test_implement_trait_attribute() -> None: a.y = 8 assert a.x == 7 assert a.y == 8 + +class DunderErr: + def __contains__(self, i: i64) -> bool: + raise IndexError() + +def test_dunder_arg_check() -> None: + o: Any = DunderErr() + with assertRaises(TypeError): + 'x' in o + with assertRaises(TypeError): + 2**63 in o + with assertRaises(IndexError): + 1 in o diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index fe347c855661..2b14619a9884 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -42,6 +42,7 @@ "irbuild-strip-asserts.test", "irbuild-i64.test", "irbuild-i32.test", + "irbuild-i16.test", "irbuild-vectorcall.test", "irbuild-unreachable.test", "irbuild-isinstance.test", diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index dc054ac9002f..a4071b9c3de5 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -41,6 +41,7 @@ "run-integers.test", "run-i64.test", "run-i32.test", + "run-i16.test", "run-floats.test", "run-math.test", "run-bools.test", diff --git a/mypyc/test/test_typeops.py b/mypyc/test/test_typeops.py index 0d9860d88ffe..ff2c05ad983e 100644 --- a/mypyc/test/test_typeops.py +++ b/mypyc/test/test_typeops.py @@ -8,6 +8,7 @@ RUnion, bit_rprimitive, bool_rprimitive, + int16_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, @@ -18,31 +19,44 @@ from mypyc.rt_subtype import is_runtime_subtype from mypyc.subtype import is_subtype +native_int_types = [int64_rprimitive, int32_rprimitive, int16_rprimitive] + class TestSubtype(unittest.TestCase): def test_bit(self) -> None: assert is_subtype(bit_rprimitive, bool_rprimitive) assert is_subtype(bit_rprimitive, int_rprimitive) assert is_subtype(bit_rprimitive, short_int_rprimitive) - assert is_subtype(bit_rprimitive, int64_rprimitive) - assert is_subtype(bit_rprimitive, int32_rprimitive) + for rt in native_int_types: + assert is_subtype(bit_rprimitive, rt) def test_bool(self) -> None: assert not is_subtype(bool_rprimitive, bit_rprimitive) assert is_subtype(bool_rprimitive, int_rprimitive) assert is_subtype(bool_rprimitive, short_int_rprimitive) - assert is_subtype(bool_rprimitive, int64_rprimitive) - assert is_subtype(bool_rprimitive, int32_rprimitive) + for rt in native_int_types: + assert is_subtype(bool_rprimitive, rt) def test_int64(self) -> None: + assert is_subtype(int64_rprimitive, int64_rprimitive) assert is_subtype(int64_rprimitive, int_rprimitive) assert not is_subtype(int64_rprimitive, short_int_rprimitive) assert not is_subtype(int64_rprimitive, int32_rprimitive) + assert not is_subtype(int64_rprimitive, int16_rprimitive) def test_int32(self) -> None: + assert is_subtype(int32_rprimitive, int32_rprimitive) assert is_subtype(int32_rprimitive, int_rprimitive) assert not is_subtype(int32_rprimitive, short_int_rprimitive) assert not is_subtype(int32_rprimitive, int64_rprimitive) + assert not is_subtype(int32_rprimitive, int16_rprimitive) + + def test_int16(self) -> None: + assert is_subtype(int16_rprimitive, int16_rprimitive) + assert is_subtype(int16_rprimitive, int_rprimitive) + assert not is_subtype(int16_rprimitive, short_int_rprimitive) + assert not is_subtype(int16_rprimitive, int64_rprimitive) + assert not is_subtype(int16_rprimitive, int32_rprimitive) class TestRuntimeSubtype(unittest.TestCase): diff --git a/test-data/unit/lib-stub/mypy_extensions.pyi b/test-data/unit/lib-stub/mypy_extensions.pyi index 56fac31e7219..b7ff01064315 100644 --- a/test-data/unit/lib-stub/mypy_extensions.pyi +++ b/test-data/unit/lib-stub/mypy_extensions.pyi @@ -50,7 +50,37 @@ class FlexibleAlias(Generic[_T, _U]): ... class __SupportsInt(Protocol[T_co]): def __int__(self) -> int: pass -_Int = Union[int, i32, i64] +_Int = Union[int, i16, i32, i64] + +class i16: + def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... + def __add__(self, x: i16) -> i16: ... + def __radd__(self, x: i16) -> i16: ... + def __sub__(self, x: i16) -> i16: ... + def __rsub__(self, x: i16) -> i16: ... + def __mul__(self, x: i16) -> i16: ... + def __rmul__(self, x: i16) -> i16: ... + def __floordiv__(self, x: i16) -> i16: ... + def __rfloordiv__(self, x: i16) -> i16: ... + def __mod__(self, x: i16) -> i16: ... + def __rmod__(self, x: i16) -> i16: ... + def __and__(self, x: i16) -> i16: ... + def __rand__(self, x: i16) -> i16: ... + def __or__(self, x: i16) -> i16: ... + def __ror__(self, x: i16) -> i16: ... + def __xor__(self, x: i16) -> i16: ... + def __rxor__(self, x: i16) -> i16: ... + def __lshift__(self, x: i16) -> i16: ... + def __rlshift__(self, x: i16) -> i16: ... + def __rshift__(self, x: i16) -> i16: ... + def __rrshift__(self, x: i16) -> i16: ... + def __neg__(self) -> i16: ... + def __invert__(self) -> i16: ... + def __pos__(self) -> i16: ... + def __lt__(self, x: i16) -> bool: ... + def __le__(self, x: i16) -> bool: ... + def __ge__(self, x: i16) -> bool: ... + def __gt__(self, x: i16) -> bool: ... class i32: def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... From c2dc3ff4ee2283f42968aaf2a92bb5dabb05ec01 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 23 Jun 2023 18:20:57 +0100 Subject: [PATCH 0058/1617] [mypyc] Fix classes with __dict__ on 3.12 (#15471) Got this working with a little trial and error. Work on mypyc/mypyc#995. --- mypyc/codegen/emitclass.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index eac7a2207972..3606f5267a8a 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -284,8 +284,9 @@ def emit_line() -> None: fields["tp_members"] = members_name fields["tp_basicsize"] = f"{base_size} + 2*sizeof(PyObject *)" - fields["tp_dictoffset"] = base_size - fields["tp_weaklistoffset"] = weak_offset + if emitter.capi_version < (3, 12): + fields["tp_dictoffset"] = base_size + fields["tp_weaklistoffset"] = weak_offset else: fields["tp_basicsize"] = base_size @@ -341,6 +342,8 @@ def emit_line() -> None: # This is just a placeholder to please CPython. It will be # overridden during setup. fields["tp_call"] = "PyVectorcall_Call" + if has_managed_dict(cl, emitter): + flags.append("Py_TPFLAGS_MANAGED_DICT") fields["tp_flags"] = " | ".join(flags) emitter.emit_line(f"static PyTypeObject {emitter.type_struct_name(cl)}_template_ = {{") @@ -730,7 +733,9 @@ def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) - for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_visit(f"self->{emitter.attr(attr)}", rtype) - if cl.has_dict: + if has_managed_dict(cl, emitter): + emitter.emit_line("_PyObject_VisitManagedDict((PyObject *)self, visit, arg);") + elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that emitter.emit_gc_visit( @@ -751,7 +756,9 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_clear(f"self->{emitter.attr(attr)}", rtype) - if cl.has_dict: + if has_managed_dict(cl, emitter): + emitter.emit_line("_PyObject_ClearManagedDict((PyObject *)self);") + elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that emitter.emit_gc_clear( @@ -1040,3 +1047,15 @@ def generate_property_setter( ) emitter.emit_line("return 0;") emitter.emit_line("}") + + +def has_managed_dict(cl: ClassIR, emitter: Emitter) -> bool: + """Should the class get the Py_TPFLAGS_MANAGED_DICT flag?""" + # On 3.11 and earlier the flag doesn't exist and we use + # tp_dictoffset instead. If a class inherits from Exception, the + # flag conflicts with tp_dictoffset set in the base class. + return ( + emitter.capi_version >= (3, 12) + and cl.has_dict + and cl.builtin_base != "PyBaseExceptionObject" + ) From efe84d492bd77f20c777b523f2e681d1f83f7ac4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 23 Jun 2023 18:21:06 +0100 Subject: [PATCH 0059/1617] [mypyc] Fix generators on Python 3.12 (#15472) I think we relied on undocumented behavior on older Python versions. Now it works consistently across all supported Python versions. Work on mypyc/mypyc#995. --- mypyc/lib-rt/exc_ops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypyc/lib-rt/exc_ops.c b/mypyc/lib-rt/exc_ops.c index a7536994d059..d8307ecf21f8 100644 --- a/mypyc/lib-rt/exc_ops.c +++ b/mypyc/lib-rt/exc_ops.c @@ -24,6 +24,12 @@ void CPy_Reraise(void) { } void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObject *traceback) { + if (!PyType_Check(type) && value == Py_None) { + // The first argument must be an exception instance + value = type; + type = (PyObject *)Py_TYPE(value); + } + // Set the value and traceback of an error. Because calling // PyErr_Restore takes away a reference to each object passed in // as an argument, we manually increase the reference count of From ef87305e2aef19c57a3cd485106de825eb45a0c6 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 23 Jun 2023 18:11:11 -0700 Subject: [PATCH 0060/1617] Revert "Fix spurious errors on builtins.open (#15161)" (#15508) --- mypy/semanal_main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 8e8c455dd686..d7e9712fa427 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -75,7 +75,6 @@ "abc", "collections", "collections.abc", - "typing_extensions", ] From 1fab96bf829c2ff6f1a10e3a47718f77a4a4a5ad Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 24 Jun 2023 07:56:46 +0300 Subject: [PATCH 0061/1617] Remove special handling of `kw_only` in `dataclass` plugin serialization (#15502) It is not needed, because it is always serialized as `bool` and is always present. It was added a long time ago in https://github.com/python/mypy/pull/10867 as a compat layer. But, since then we've added at least one `is_neither_frozen_nor_nonfrozen` attribute that is serialized / deserialized in a common way. So, let's remove this hack for good. --- mypy/plugins/dataclasses.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 913b1ef23312..cf58e577056c 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -158,8 +158,6 @@ def deserialize( cls, info: TypeInfo, data: JsonDict, api: SemanticAnalyzerPluginInterface ) -> DataclassAttribute: data = data.copy() - if data.get("kw_only") is None: - data["kw_only"] = False typ = deserialize_and_fixup_type(data.pop("type"), api) return cls(type=typ, info=info, **data) From 9bd85638361de256d732f88396206711c2e10f1d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 24 Jun 2023 14:30:27 +0100 Subject: [PATCH 0062/1617] [mypyc] Fix int operations on Python 3.12 (#15470) The representation of `int` objects was changed in Python 3.12. Define some helper macros to make it possible to support all Python versions we care about without too much code duplication. Work on https://github.com/mypyc/mypyc/issues/995. Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypyc/lib-rt/int_ops.c | 33 ++++++++++++------ mypyc/lib-rt/mypyc_util.h | 40 +++++++++++++++++++++ mypyc/lib-rt/pythonsupport.h | 67 ++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 56720d1992e8..2a118d99b5eb 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -308,10 +308,10 @@ PyObject *CPyBool_Str(bool b) { } static void CPyLong_NormalizeUnsigned(PyLongObject *v) { - Py_ssize_t i = v->ob_base.ob_size; - while (i > 0 && v->ob_digit[i - 1] == 0) + Py_ssize_t i = CPY_LONG_SIZE_UNSIGNED(v); + while (i > 0 && CPY_LONG_DIGIT(v, i - 1) == 0) i--; - v->ob_base.ob_size = i; + CPyLong_SetUnsignedSize(v, i); } // Bitwise op '&', '|' or '^' using the generic (slow) API @@ -361,8 +361,8 @@ static digit *GetIntDigits(CPyTagged n, Py_ssize_t *size, digit *buf) { return buf; } else { PyLongObject *obj = (PyLongObject *)CPyTagged_LongAsObject(n); - *size = obj->ob_base.ob_size; - return obj->ob_digit; + *size = CPY_LONG_SIZE_SIGNED(obj); + return &CPY_LONG_DIGIT(obj, 0); } } @@ -399,20 +399,20 @@ static CPyTagged BitwiseLongOp(CPyTagged a, CPyTagged b, char op) { Py_ssize_t i; if (op == '&') { for (i = 0; i < asize; i++) { - r->ob_digit[i] = adigits[i] & bdigits[i]; + CPY_LONG_DIGIT(r, i) = adigits[i] & bdigits[i]; } } else { if (op == '|') { for (i = 0; i < asize; i++) { - r->ob_digit[i] = adigits[i] | bdigits[i]; + CPY_LONG_DIGIT(r, i) = adigits[i] | bdigits[i]; } } else { for (i = 0; i < asize; i++) { - r->ob_digit[i] = adigits[i] ^ bdigits[i]; + CPY_LONG_DIGIT(r, i) = adigits[i] ^ bdigits[i]; } } for (; i < bsize; i++) { - r->ob_digit[i] = bdigits[i]; + CPY_LONG_DIGIT(r, i) = bdigits[i]; } } CPyLong_NormalizeUnsigned(r); @@ -521,7 +521,7 @@ int64_t CPyLong_AsInt64(PyObject *o) { Py_ssize_t size = Py_SIZE(lobj); if (likely(size == 1)) { // Fast path - return lobj->ob_digit[0]; + return CPY_LONG_DIGIT(lobj, 0); } else if (likely(size == 0)) { return 0; } @@ -576,14 +576,25 @@ int64_t CPyInt64_Remainder(int64_t x, int64_t y) { int32_t CPyLong_AsInt32(PyObject *o) { if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + return CPY_LONG_DIGIT(lobj, 0); + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else PyLongObject *lobj = (PyLongObject *)o; Py_ssize_t size = lobj->ob_base.ob_size; if (likely(size == 1)) { // Fast path - return lobj->ob_digit[0]; + return CPY_LONG_DIGIT(lobj, 0); } else if (likely(size == 0)) { return 0; } + #endif } // Slow path int overflow; diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 3bc785ac6526..e7e9fd768715 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -72,4 +72,44 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) { // Are we targeting Python 3.12 or newer? #define CPY_3_12_FEATURES (PY_VERSION_HEX >= 0x030c0000) +#if CPY_3_12_FEATURES + +// Same as macros in CPython internal/pycore_long.h, but with a CPY_ prefix +#define CPY_NON_SIZE_BITS 3 +#define CPY_SIGN_ZERO 1 +#define CPY_SIGN_NEGATIVE 2 +#define CPY_SIGN_MASK 3 + +#define CPY_LONG_DIGIT(o, n) ((o)->long_value.ob_digit[n]) + +// Only available on Python 3.12 and later +#define CPY_LONG_TAG(o) ((o)->long_value.lv_tag) +#define CPY_LONG_IS_NEGATIVE(o) (((o)->long_value.lv_tag & CPY_SIGN_MASK) == CPY_SIGN_NEGATIVE) +// Only available on Python 3.12 and later +#define CPY_LONG_SIZE(o) ((o)->long_value.lv_tag >> CPY_NON_SIZE_BITS) +// Number of digits; negative for negative ints +#define CPY_LONG_SIZE_SIGNED(o) (CPY_LONG_IS_NEGATIVE(o) ? -CPY_LONG_SIZE(o) : CPY_LONG_SIZE(o)) +// Number of digits, assuming int is non-negative +#define CPY_LONG_SIZE_UNSIGNED(o) CPY_LONG_SIZE(o) + +static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { + if (n == 0) + o->long_value.lv_tag = CPY_SIGN_ZERO; + else + o->long_value.lv_tag = n << CPY_NON_SIZE_BITS; +} + +#else + +#define CPY_LONG_DIGIT(o, n) ((o)->ob_digit[n]) +#define CPY_LONG_IS_NEGATIVE(o) (((o)->ob_base.ob_size < 0) +#define CPY_LONG_SIZE_SIGNED(o) ((o)->ob_base.ob_size) +#define CPY_LONG_SIZE_UNSIGNED(o) ((o)->ob_base.ob_size) + +static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { + o->ob_base.ob_size = n; +} + +#endif + #endif diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 6751f6f4af02..1d493b45b89d 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -129,6 +129,65 @@ init_subclass(PyTypeObject *type, PyObject *kwds) return 0; } +#if CPY_3_12_FEATURES + +static inline Py_ssize_t +CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) +{ + /* This version by Tim Peters */ + PyLongObject *v = (PyLongObject *)vv; + size_t x, prev; + Py_ssize_t res; + Py_ssize_t i; + int sign; + + *overflow = 0; + + res = -1; + i = CPY_LONG_TAG(v); + + // TODO: Combine zero and non-zero cases helow? + if (likely(i == (1 << CPY_NON_SIZE_BITS))) { + res = CPY_LONG_DIGIT(v, 0); + } else if (likely(i == CPY_SIGN_ZERO)) { + res = 0; + } else if (i == ((1 << CPY_NON_SIZE_BITS) | CPY_SIGN_NEGATIVE)) { + res = -(sdigit)CPY_LONG_DIGIT(v, 0); + } else { + sign = 1; + x = 0; + if (i & CPY_SIGN_NEGATIVE) { + sign = -1; + } + i >>= CPY_NON_SIZE_BITS; + while (--i >= 0) { + prev = x; + x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); + if ((x >> PyLong_SHIFT) != prev) { + *overflow = sign; + goto exit; + } + } + /* Haven't lost any bits, but casting to long requires extra + * care (see comment above). + */ + if (x <= (size_t)CPY_TAGGED_MAX) { + res = (Py_ssize_t)x * sign; + } + else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { + res = CPY_TAGGED_MIN; + } + else { + *overflow = sign; + /* res is already set to -1 */ + } + } + exit: + return res; +} + +#else + // Adapted from longobject.c in Python 3.7.0 /* This function adapted from PyLong_AsLongLongAndOverflow, but with @@ -156,11 +215,11 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) i = Py_SIZE(v); if (likely(i == 1)) { - res = v->ob_digit[0]; + res = CPY_LONG_DIGIT(v, 0); } else if (likely(i == 0)) { res = 0; } else if (i == -1) { - res = -(sdigit)v->ob_digit[0]; + res = -(sdigit)CPY_LONG_DIGIT(v, 0); } else { sign = 1; x = 0; @@ -170,7 +229,7 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) + v->ob_digit[i]; + x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); if ((x >> PyLong_SHIFT) != prev) { *overflow = sign; goto exit; @@ -194,6 +253,8 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) return res; } +#endif + // Adapted from listobject.c in Python 3.7.0 static int list_resize(PyListObject *self, Py_ssize_t newsize) From 2bb70781e9c1d5a27b20b8e68980c19ec1ee25fb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 25 Jun 2023 10:22:00 +0100 Subject: [PATCH 0063/1617] [mypyc] Fix i16 on Python 3.12 (#15510) Two recent mypyc PRs didn't work together (#15470 and #15464). Fix them. --- mypyc/lib-rt/int_ops.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 2a118d99b5eb..528401692a3a 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -654,6 +654,18 @@ void CPyInt32_Overflow() { int16_t CPyLong_AsInt16(PyObject *o) { if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + digit x = CPY_LONG_DIGIT(lobj, 0); + if (x < 0x8000) + return x; + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else PyLongObject *lobj = (PyLongObject *)o; Py_ssize_t size = lobj->ob_base.ob_size; if (likely(size == 1)) { @@ -664,6 +676,7 @@ int16_t CPyLong_AsInt16(PyObject *o) { } else if (likely(size == 0)) { return 0; } + #endif } // Slow path int overflow; From cee00306a2ded471658b71d2f1c9024bc22a62b1 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Sun, 25 Jun 2023 05:43:56 -0400 Subject: [PATCH 0064/1617] Constant fold more unary and binary expressions (#15202) Now mypy can constant fold these additional operations: - Float arithmetic - Mixed int and float arithmetic - String multiplication - Complex plus or minus a literal real (eg. 1+j2) While this can be useful with literal types, the main goal is to improve constant folding in mypyc (mypyc/mypyc#772). mypyc can also fold bytes addition and multiplication, but mypy cannot as byte values can't be easily stored anywhere. --- mypy/constant_fold.py | 118 +++++++-- mypy/nodes.py | 2 +- mypy/semanal.py | 2 +- mypyc/irbuild/builder.py | 12 +- mypyc/irbuild/callable_class.py | 2 +- mypyc/irbuild/constant_fold.py | 64 +++-- mypyc/irbuild/expression.py | 15 +- mypyc/test-data/fixtures/ir.py | 6 + mypyc/test-data/irbuild-basic.test | 7 +- mypyc/test-data/irbuild-constant-fold.test | 281 +++++++++++++++++++-- 10 files changed, 412 insertions(+), 97 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index a1011397eba8..6881ecae9e88 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -8,11 +8,21 @@ from typing import Union from typing_extensions import Final -from mypy.nodes import Expression, FloatExpr, IntExpr, NameExpr, OpExpr, StrExpr, UnaryExpr, Var +from mypy.nodes import ( + ComplexExpr, + Expression, + FloatExpr, + IntExpr, + NameExpr, + OpExpr, + StrExpr, + UnaryExpr, + Var, +) # All possible result types of constant folding -ConstantValue = Union[int, bool, float, str] -CONST_TYPES: Final = (int, bool, float, str) +ConstantValue = Union[int, bool, float, complex, str] +CONST_TYPES: Final = (int, bool, float, complex, str) def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | None: @@ -39,6 +49,8 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non return expr.value if isinstance(expr, FloatExpr): return expr.value + if isinstance(expr, ComplexExpr): + return expr.value elif isinstance(expr, NameExpr): if expr.name == "True": return True @@ -56,26 +68,60 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non elif isinstance(expr, OpExpr): left = constant_fold_expr(expr.left, cur_mod_id) right = constant_fold_expr(expr.right, cur_mod_id) - if isinstance(left, int) and isinstance(right, int): - return constant_fold_binary_int_op(expr.op, left, right) - elif isinstance(left, str) and isinstance(right, str): - return constant_fold_binary_str_op(expr.op, left, right) + if left is not None and right is not None: + return constant_fold_binary_op(expr.op, left, right) elif isinstance(expr, UnaryExpr): value = constant_fold_expr(expr.expr, cur_mod_id) - if isinstance(value, int): - return constant_fold_unary_int_op(expr.op, value) - if isinstance(value, float): - return constant_fold_unary_float_op(expr.op, value) + if value is not None: + return constant_fold_unary_op(expr.op, value) return None -def constant_fold_binary_int_op(op: str, left: int, right: int) -> int | None: +def constant_fold_binary_op( + op: str, left: ConstantValue, right: ConstantValue +) -> ConstantValue | None: + if isinstance(left, int) and isinstance(right, int): + return constant_fold_binary_int_op(op, left, right) + + # Float and mixed int/float arithmetic. + if isinstance(left, float) and isinstance(right, float): + return constant_fold_binary_float_op(op, left, right) + elif isinstance(left, float) and isinstance(right, int): + return constant_fold_binary_float_op(op, left, right) + elif isinstance(left, int) and isinstance(right, float): + return constant_fold_binary_float_op(op, left, right) + + # String concatenation and multiplication. + if op == "+" and isinstance(left, str) and isinstance(right, str): + return left + right + elif op == "*" and isinstance(left, str) and isinstance(right, int): + return left * right + elif op == "*" and isinstance(left, int) and isinstance(right, str): + return left * right + + # Complex construction. + if op == "+" and isinstance(left, (int, float)) and isinstance(right, complex): + return left + right + elif op == "+" and isinstance(left, complex) and isinstance(right, (int, float)): + return left + right + elif op == "-" and isinstance(left, (int, float)) and isinstance(right, complex): + return left - right + elif op == "-" and isinstance(left, complex) and isinstance(right, (int, float)): + return left - right + + return None + + +def constant_fold_binary_int_op(op: str, left: int, right: int) -> int | float | None: if op == "+": return left + right if op == "-": return left - right elif op == "*": return left * right + elif op == "/": + if right != 0: + return left / right elif op == "//": if right != 0: return left // right @@ -102,25 +148,41 @@ def constant_fold_binary_int_op(op: str, left: int, right: int) -> int | None: return None -def constant_fold_unary_int_op(op: str, value: int) -> int | None: - if op == "-": - return -value - elif op == "~": - return ~value - elif op == "+": - return value +def constant_fold_binary_float_op(op: str, left: int | float, right: int | float) -> float | None: + assert not (isinstance(left, int) and isinstance(right, int)), (op, left, right) + if op == "+": + return left + right + elif op == "-": + return left - right + elif op == "*": + return left * right + elif op == "/": + if right != 0: + return left / right + elif op == "//": + if right != 0: + return left // right + elif op == "%": + if right != 0: + return left % right + elif op == "**": + if (left < 0 and isinstance(right, int)) or left > 0: + try: + ret = left**right + except OverflowError: + return None + else: + assert isinstance(ret, float), ret + return ret + return None -def constant_fold_unary_float_op(op: str, value: float) -> float | None: - if op == "-": +def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: + if op == "-" and isinstance(value, (int, float)): return -value - elif op == "+": + elif op == "~" and isinstance(value, int): + return ~value + elif op == "+" and isinstance(value, (int, float)): return value return None - - -def constant_fold_binary_str_op(op: str, left: str, right: str) -> str | None: - if op == "+": - return left + right - return None diff --git a/mypy/nodes.py b/mypy/nodes.py index 2f4f3f788385..52dd9948e0c1 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1002,7 +1002,7 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None: # If constant value is a simple literal, # store the literal value (unboxed) for the benefit of # tools like mypyc. - self.final_value: int | float | bool | str | None = None + self.final_value: int | float | complex | bool | str | None = None # Where the value was set (only for class attributes) self.final_unset_in_class = False self.final_set_in_init = False diff --git a/mypy/semanal.py b/mypy/semanal.py index 249a57d550b2..43960d972101 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3394,7 +3394,7 @@ def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Typ return None value = constant_fold_expr(rvalue, self.cur_mod_id) - if value is None: + if value is None or isinstance(value, complex): return None if isinstance(value, bool): diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 26aa17071974..86c28882d738 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -535,16 +535,14 @@ def load_final_static( error_msg=f'value for final name "{error_name}" was not set', ) - def load_final_literal_value(self, val: int | str | bytes | float | bool, line: int) -> Value: - """Load value of a final name or class-level attribute.""" + def load_literal_value(self, val: int | str | bytes | float | complex | bool) -> Value: + """Load value of a final name, class-level attribute, or constant folded expression.""" if isinstance(val, bool): if val: return self.true() else: return self.false() elif isinstance(val, int): - # TODO: take care of negative integer initializers - # (probably easier to fix this in mypy itself). return self.builder.load_int(val) elif isinstance(val, float): return self.builder.load_float(val) @@ -552,8 +550,10 @@ def load_final_literal_value(self, val: int | str | bytes | float | bool, line: return self.builder.load_str(val) elif isinstance(val, bytes): return self.builder.load_bytes(val) + elif isinstance(val, complex): + return self.builder.load_complex(val) else: - assert False, "Unsupported final literal value" + assert False, "Unsupported literal value" def get_assignment_target( self, lvalue: Lvalue, line: int = -1, *, for_read: bool = False @@ -1013,7 +1013,7 @@ def emit_load_final( line: line number where loading occurs """ if final_var.final_value is not None: # this is safe even for non-native names - return self.load_final_literal_value(final_var.final_value, line) + return self.load_literal_value(final_var.final_value) elif native: return self.load_final_static(fullname, self.mapper.type_to_rtype(typ), line, name) else: diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py index d3ee54a208cd..599dbb81f767 100644 --- a/mypyc/irbuild/callable_class.py +++ b/mypyc/irbuild/callable_class.py @@ -17,7 +17,7 @@ def setup_callable_class(builder: IRBuilder) -> None: - """Generate an (incomplete) callable class representing function. + """Generate an (incomplete) callable class representing a function. This can be a nested function or a function within a non-extension class. Also set up the 'self' variable for that class. diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index bc71052f5418..dc21be4689e2 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -13,13 +13,10 @@ from typing import Union from typing_extensions import Final -from mypy.constant_fold import ( - constant_fold_binary_int_op, - constant_fold_binary_str_op, - constant_fold_unary_float_op, - constant_fold_unary_int_op, -) +from mypy.constant_fold import constant_fold_binary_op, constant_fold_unary_op from mypy.nodes import ( + BytesExpr, + ComplexExpr, Expression, FloatExpr, IntExpr, @@ -31,10 +28,11 @@ Var, ) from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.util import bytes_from_str # All possible result types of constant folding -ConstantValue = Union[int, str, float] -CONST_TYPES: Final = (int, str, float) +ConstantValue = Union[int, float, complex, str, bytes] +CONST_TYPES: Final = (int, float, complex, str, bytes) def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | None: @@ -44,35 +42,55 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | """ if isinstance(expr, IntExpr): return expr.value + if isinstance(expr, FloatExpr): + return expr.value if isinstance(expr, StrExpr): return expr.value - if isinstance(expr, FloatExpr): + if isinstance(expr, BytesExpr): + return bytes_from_str(expr.value) + if isinstance(expr, ComplexExpr): return expr.value elif isinstance(expr, NameExpr): node = expr.node if isinstance(node, Var) and node.is_final: - value = node.final_value - if isinstance(value, (CONST_TYPES)): - return value + final_value = node.final_value + if isinstance(final_value, (CONST_TYPES)): + return final_value elif isinstance(expr, MemberExpr): final = builder.get_final_ref(expr) if final is not None: fn, final_var, native = final if final_var.is_final: - value = final_var.final_value - if isinstance(value, (CONST_TYPES)): - return value + final_value = final_var.final_value + if isinstance(final_value, (CONST_TYPES)): + return final_value elif isinstance(expr, OpExpr): left = constant_fold_expr(builder, expr.left) right = constant_fold_expr(builder, expr.right) - if isinstance(left, int) and isinstance(right, int): - return constant_fold_binary_int_op(expr.op, left, right) - elif isinstance(left, str) and isinstance(right, str): - return constant_fold_binary_str_op(expr.op, left, right) + if left is not None and right is not None: + return constant_fold_binary_op_extended(expr.op, left, right) elif isinstance(expr, UnaryExpr): value = constant_fold_expr(builder, expr.expr) - if isinstance(value, int): - return constant_fold_unary_int_op(expr.op, value) - if isinstance(value, float): - return constant_fold_unary_float_op(expr.op, value) + if value is not None and not isinstance(value, bytes): + return constant_fold_unary_op(expr.op, value) + return None + + +def constant_fold_binary_op_extended( + op: str, left: ConstantValue, right: ConstantValue +) -> ConstantValue | None: + """Like mypy's constant_fold_binary_op(), but includes bytes support. + + mypy cannot use constant folded bytes easily so it's simpler to only support them in mypyc. + """ + if not isinstance(left, bytes) and not isinstance(right, bytes): + return constant_fold_binary_op(op, left, right) + + if op == "+" and isinstance(left, bytes) and isinstance(right, bytes): + return left + right + elif op == "*" and isinstance(left, bytes) and isinstance(right, int): + return left * right + elif op == "*" and isinstance(left, int) and isinstance(right, bytes): + return left * right + return None diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 4ebc422ed535..ada3d47cefab 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -55,7 +55,6 @@ Assign, BasicBlock, ComparisonOp, - Float, Integer, LoadAddress, LoadLiteral, @@ -92,7 +91,6 @@ tokenizer_printf_style, ) from mypyc.irbuild.specialize import apply_function_specialization, apply_method_specialization -from mypyc.irbuild.util import bytes_from_str from mypyc.primitives.bytes_ops import bytes_slice_op from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, dict_set_item_op from mypyc.primitives.generic_ops import iter_op @@ -575,12 +573,8 @@ def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: Return None otherwise. """ value = constant_fold_expr(builder, expr) - if isinstance(value, int): - return builder.load_int(value) - elif isinstance(value, str): - return builder.load_str(value) - elif isinstance(value, float): - return Float(value) + if value is not None: + return builder.load_literal_value(value) return None @@ -662,10 +656,6 @@ def set_literal_values(builder: IRBuilder, items: Sequence[Expression]) -> list[ values.append(True) elif item.fullname == "builtins.False": values.append(False) - elif isinstance(item, (BytesExpr, FloatExpr, ComplexExpr)): - # constant_fold_expr() doesn't handle these (yet?) - v = bytes_from_str(item.value) if isinstance(item, BytesExpr) else item.value - values.append(v) elif isinstance(item, TupleExpr): tuple_values = set_literal_values(builder, item.items) if tuple_values is not None: @@ -685,7 +675,6 @@ def precompute_set_literal(builder: IRBuilder, s: SetExpr) -> Value | None: Supported items: - Anything supported by irbuild.constant_fold.constant_fold_expr() - None, True, and False - - Float, byte, and complex literals - Tuple literals with only items listed above """ values = set_literal_values(builder, s.items) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 0b081b079bda..bf06613ad2a8 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -87,6 +87,8 @@ def __init__(self) -> None: pass @overload def __init__(self, x: object) -> None: pass def __add__(self, x: str) -> str: pass + def __mul__(self, x: int) -> str: pass + def __rmul__(self, x: int) -> str: pass def __eq__(self, x: object) -> bool: pass def __ne__(self, x: object) -> bool: pass def __lt__(self, x: str) -> bool: ... @@ -134,7 +136,9 @@ def __ge__(self, x: float) -> bool: ... class complex: def __init__(self, x: object, y: object = None) -> None: pass def __add__(self, n: complex) -> complex: pass + def __radd__(self, n: float) -> complex: pass def __sub__(self, n: complex) -> complex: pass + def __rsub__(self, n: float) -> complex: pass def __mul__(self, n: complex) -> complex: pass def __truediv__(self, n: complex) -> complex: pass def __neg__(self) -> complex: pass @@ -145,6 +149,8 @@ def __init__(self) -> None: ... @overload def __init__(self, x: object) -> None: ... def __add__(self, x: bytes) -> bytes: ... + def __mul__(self, x: int) -> bytes: ... + def __rmul__(self, x: int) -> bytes: ... def __eq__(self, x: object) -> bool: ... def __ne__(self, x: object) -> bool: ... @overload diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 496eca77e090..556e0a4bbc50 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1172,13 +1172,16 @@ L0: [case testLoadComplex] def load() -> complex: - return 5j+1.0 + real = 1 + return 5j+real [out] def load(): + real :: int r0, r1, r2 :: object L0: + real = 2 r0 = 5j - r1 = box(float, 1.0) + r1 = box(int, real) r2 = PyNumber_Add(r0, r1) return r2 diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 866953f0c09a..97b13ab337c7 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -3,6 +3,7 @@ def bin_ops() -> None: add = 15 + 47 add_mul = (2 + 3) * 5 sub = 7 - 11 + div = 3 / 2 bit_and = 6 & 10 bit_or = 6 | 10 bit_xor = 6 ^ 10 @@ -25,11 +26,14 @@ def pow() -> None: p3 = 0**0 [out] def bin_ops(): - add, add_mul, sub, bit_and, bit_or, bit_xor, lshift, rshift, lshift0, rshift0 :: int + add, add_mul, sub :: int + div :: float + bit_and, bit_or, bit_xor, lshift, rshift, lshift0, rshift0 :: int L0: add = 124 add_mul = 50 sub = -8 + div = 1.5 bit_and = 4 bit_or = 28 bit_xor = 24 @@ -117,35 +121,28 @@ L0: [case testIntConstantFoldingUnsupportedCases] def error_cases() -> None: - div_by_zero = 5 // 0 + div_by_zero = 5 / 0 + floor_div_by_zero = 5 // 0 mod_by_zero = 5 % 0 lshift_neg = 6 << -1 rshift_neg = 7 >> -1 -def unsupported_div() -> None: - x = 4 / 6 - y = 10 / 5 def unsupported_pow() -> None: p = 3 ** (-1) [out] def error_cases(): - r0, div_by_zero, r1, mod_by_zero, r2, lshift_neg, r3, rshift_neg :: int + r0, div_by_zero :: float + r1, floor_div_by_zero, r2, mod_by_zero, r3, lshift_neg, r4, rshift_neg :: int L0: - r0 = CPyTagged_FloorDivide(10, 0) + r0 = CPyTagged_TrueDivide(10, 0) div_by_zero = r0 - r1 = CPyTagged_Remainder(10, 0) - mod_by_zero = r1 - r2 = CPyTagged_Lshift(12, -2) - lshift_neg = r2 - r3 = CPyTagged_Rshift(14, -2) - rshift_neg = r3 - return 1 -def unsupported_div(): - r0, x, r1, y :: float -L0: - r0 = CPyTagged_TrueDivide(8, 12) - x = r0 - r1 = CPyTagged_TrueDivide(20, 10) - y = r1 + r1 = CPyTagged_FloorDivide(10, 0) + floor_div_by_zero = r1 + r2 = CPyTagged_Remainder(10, 0) + mod_by_zero = r2 + r3 = CPyTagged_Lshift(12, -2) + lshift_neg = r3 + r4 = CPyTagged_Rshift(14, -2) + rshift_neg = r4 return 1 def unsupported_pow(): r0, r1, r2 :: object @@ -224,20 +221,260 @@ L0: a = 12 return 1 +[case testFloatConstantFolding] +from typing_extensions import Final + +N: Final = 1.5 +N2: Final = 1.5 * 2 + +def bin_ops() -> None: + add = 0.5 + 0.5 + add_mul = (1.5 + 3.5) * 5.0 + sub = 7.0 - 7.5 + div = 3.0 / 2.0 + floor_div = 3.0 // 2.0 +def bin_ops_neg() -> None: + add = 0.5 + -0.5 + add_mul = (-1.5 + 3.5) * -5.0 + add_mul2 = (1.5 + -3.5) * -5.0 + sub = 7.0 - -7.5 + div = 3.0 / -2.0 + floor_div = 3.0 // -2.0 +def unary_ops() -> None: + neg1 = -5.5 + neg2 = --1.5 + neg3 = -0.0 + pos = +5.5 +def pow() -> None: + p0 = 16.0**0 + p1 = 16.0**0.5 + p2 = (-5.0)**3 + p3 = 16.0**(-0) + p4 = 16.0**(-0.5) + p5 = (-2.0)**(-1) +def error_cases() -> None: + div = 2.0 / 0.0 + floor_div = 2.0 // 0.0 + power_imag = (-2.0)**0.5 + power_imag2 = (-2.0)**(-0.5) + power_overflow = 2.0**10000.0 +def final_floats() -> None: + add1 = N + 1.2 + add2 = N + N2 + add3 = -1.2 + N2 +[out] +def bin_ops(): + add, add_mul, sub, div, floor_div :: float +L0: + add = 1.0 + add_mul = 25.0 + sub = -0.5 + div = 1.5 + floor_div = 1.0 + return 1 +def bin_ops_neg(): + add, add_mul, add_mul2, sub, div, floor_div :: float +L0: + add = 0.0 + add_mul = -10.0 + add_mul2 = 10.0 + sub = 14.5 + div = -1.5 + floor_div = -2.0 + return 1 +def unary_ops(): + neg1, neg2, neg3, pos :: float +L0: + neg1 = -5.5 + neg2 = 1.5 + neg3 = -0.0 + pos = 5.5 + return 1 +def pow(): + p0, p1, p2, p3, p4, p5 :: float +L0: + p0 = 1.0 + p1 = 4.0 + p2 = -125.0 + p3 = 1.0 + p4 = 0.25 + p5 = -0.5 + return 1 +def error_cases(): + r0 :: bit + r1 :: bool + r2, div, r3, floor_div :: float + r4, r5, r6 :: object + r7, power_imag :: float + r8, r9, r10 :: object + r11, power_imag2 :: float + r12, r13, r14 :: object + r15, power_overflow :: float +L0: + r0 = 0.0 == 0.0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = raise ZeroDivisionError('float division by zero') + unreachable +L2: + r2 = 2.0 / 0.0 + div = r2 + r3 = CPyFloat_FloorDivide(2.0, 0.0) + floor_div = r3 + r4 = box(float, -2.0) + r5 = box(float, 0.5) + r6 = CPyNumber_Power(r4, r5) + r7 = unbox(float, r6) + power_imag = r7 + r8 = box(float, -2.0) + r9 = box(float, -0.5) + r10 = CPyNumber_Power(r8, r9) + r11 = unbox(float, r10) + power_imag2 = r11 + r12 = box(float, 2.0) + r13 = box(float, 10000.0) + r14 = CPyNumber_Power(r12, r13) + r15 = unbox(float, r14) + power_overflow = r15 + return 1 +def final_floats(): + add1, add2, add3 :: float +L0: + add1 = 2.7 + add2 = 4.5 + add3 = 1.8 + return 1 + +[case testMixedFloatIntConstantFolding] +def bin_ops() -> None: + add = 1 + 0.5 + sub = 1 - 0.5 + mul = 0.5 * 5 + div = 5 / 0.5 + floor_div = 9.5 // 5 +def error_cases() -> None: + div = 2.0 / 0 + floor_div = 2.0 // 0 + power_overflow = 2.0**10000 +[out] +def bin_ops(): + add, sub, mul, div, floor_div :: float +L0: + add = 1.5 + sub = 0.5 + mul = 2.5 + div = 10.0 + floor_div = 1.0 + return 1 +def error_cases(): + r0 :: bit + r1 :: bool + r2, div, r3, floor_div :: float + r4, r5, r6 :: object + r7, power_overflow :: float +L0: + r0 = 0.0 == 0.0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = raise ZeroDivisionError('float division by zero') + unreachable +L2: + r2 = 2.0 / 0.0 + div = r2 + r3 = CPyFloat_FloorDivide(2.0, 0.0) + floor_div = r3 + r4 = box(float, 2.0) + r5 = box(float, 10000.0) + r6 = CPyNumber_Power(r4, r5) + r7 = unbox(float, r6) + power_overflow = r7 + return 1 + [case testStrConstantFolding] from typing_extensions import Final S: Final = 'z' +N: Final = 2 def f() -> None: x = 'foo' + 'bar' y = 'x' + 'y' + S + mul = "foobar" * 2 + mul2 = N * "foobar" [out] def f(): - r0, x, r1, y :: str + r0, x, r1, y, r2, mul, r3, mul2 :: str L0: r0 = 'foobar' x = r0 r1 = 'xyz' y = r1 + r2 = 'foobarfoobar' + mul = r2 + r3 = 'foobarfoobar' + mul2 = r3 + return 1 + +[case testBytesConstantFolding] +from typing_extensions import Final + +N: Final = 2 + +def f() -> None: + # Unfortunately, mypy doesn't store the bytes value of final refs. + x = b'foo' + b'bar' + mul = b"foobar" * 2 + mul2 = N * b"foobar" +[out] +def f(): + r0, x, r1, mul, r2, mul2 :: bytes +L0: + r0 = b'foobar' + x = r0 + r1 = b'foobarfoobar' + mul = r1 + r2 = b'foobarfoobar' + mul2 = r2 + return 1 + +[case testComplexConstantFolding] +from typing_extensions import Final + +N: Final = 1 +FLOAT_N: Final = 1.5 + +def integral() -> None: + pos = 1+2j + pos_2 = 2j+N + neg = 1-2j + neg_2 = 2j-N +def floating() -> None: + pos = 1.5+2j + pos_2 = 2j+FLOAT_N + neg = 1.5-2j + neg_2 = 2j-FLOAT_N +[out] +def integral(): + r0, pos, r1, pos_2, r2, neg, r3, neg_2 :: object +L0: + r0 = (1+2j) + pos = r0 + r1 = (1+2j) + pos_2 = r1 + r2 = (1-2j) + neg = r2 + r3 = (-1+2j) + neg_2 = r3 + return 1 +def floating(): + r0, pos, r1, pos_2, r2, neg, r3, neg_2 :: object +L0: + r0 = (1.5+2j) + pos = r0 + r1 = (1.5+2j) + pos_2 = r1 + r2 = (1.5-2j) + neg = r2 + r3 = (-1.5+2j) + neg_2 = r3 return 1 From 9ad3f381ad28be818758e0b24443d36e95b5f943 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Mon, 26 Jun 2023 02:37:18 -0400 Subject: [PATCH 0065/1617] Add ReadTheDocs configuration files (#15514) --- .readthedocs.yaml | 18 ++++++++++++++++++ mypyc/.readthedocs.yaml | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 mypyc/.readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000000..8ec33ee641ed --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + configuration: docs/source/conf.py + +formats: [pdf, htmlzip, epub] + +python: + install: + - requirements: docs/requirements-docs.txt diff --git a/mypyc/.readthedocs.yaml b/mypyc/.readthedocs.yaml new file mode 100644 index 000000000000..90831dfd7069 --- /dev/null +++ b/mypyc/.readthedocs.yaml @@ -0,0 +1,18 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + configuration: mypyc/doc/conf.py + +formats: [pdf, htmlzip, epub] + +python: + install: + - requirements: docs/requirements-docs.txt From 9511daa60f53be95bf3b7b67382a4d9862c73b46 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 26 Jun 2023 20:48:01 +0300 Subject: [PATCH 0066/1617] Support better `__post_init__` method signature for `dataclasses` (#15503) Now we use a similar approach to https://github.com/python/mypy/pull/14849 First, we generate a private name to store in a metadata (with `-`, so - no conflicts, ever). Next, we check override to be compatible: we take the currect signature and compare it to the ideal one we have. Simple and it works :) Closes #15498 Closes #9254 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ivan Levkivskyi --- mypy/checker.py | 9 +- mypy/message_registry.py | 1 + mypy/messages.py | 27 +-- mypy/plugins/dataclasses.py | 86 +++++++++- test-data/unit/check-dataclasses.test | 211 ++++++++++++++++++++++++ test-data/unit/fixtures/dataclasses.pyi | 1 + 6 files changed, 318 insertions(+), 17 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1026376cce63..cdce42ddaaa1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -136,6 +136,7 @@ from mypy.options import Options from mypy.patterns import AsPattern, StarredPattern from mypy.plugin import CheckerPluginInterface, Plugin +from mypy.plugins import dataclasses as dataclasses_plugin from mypy.scope import Scope from mypy.semanal import is_trivial_body, refers_to_fullname, set_callable_name from mypy.semanal_enum import ENUM_BASES, ENUM_SPECIAL_PROPS @@ -1044,6 +1045,9 @@ def check_func_item( if name == "__exit__": self.check__exit__return_type(defn) + if name == "__post_init__": + if dataclasses_plugin.is_processed_dataclass(defn.info): + dataclasses_plugin.check_post_init(self, defn, defn.info) @contextmanager def enter_attribute_inference_context(self) -> Iterator[None]: @@ -1851,7 +1855,7 @@ def check_method_or_accessor_override_for_base( found_base_method = True # Check the type of override. - if name not in ("__init__", "__new__", "__init_subclass__"): + if name not in ("__init__", "__new__", "__init_subclass__", "__post_init__"): # Check method override # (__init__, __new__, __init_subclass__ are special). if self.check_method_override_for_base_with_name(defn, name, base): @@ -2812,6 +2816,9 @@ def check_assignment( if name == "__match_args__" and inferred is not None: typ = self.expr_checker.accept(rvalue) self.check_match_args(inferred, typ, lvalue) + if name == "__post_init__": + if dataclasses_plugin.is_processed_dataclass(self.scope.active_class()): + self.fail(message_registry.DATACLASS_POST_INIT_MUST_BE_A_FUNCTION, rvalue) # Defer PartialType's super type checking. if ( diff --git a/mypy/message_registry.py b/mypy/message_registry.py index c5164d48fd13..4e08f0dab5ed 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -277,6 +277,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: DATACLASS_FIELD_ALIAS_MUST_BE_LITERAL: Final = ( '"alias" argument to dataclass field must be a string literal' ) +DATACLASS_POST_INIT_MUST_BE_A_FUNCTION: Final = '"__post_init__" method must be an instance method' # fastparse FAILED_TO_MERGE_OVERLOADS: Final = ErrorMessage( diff --git a/mypy/messages.py b/mypy/messages.py index 9d703a1a974a..b74a795a4318 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1253,18 +1253,21 @@ def argument_incompatible_with_supertype( code=codes.OVERRIDE, secondary_context=secondary_context, ) - self.note( - "This violates the Liskov substitution principle", - context, - code=codes.OVERRIDE, - secondary_context=secondary_context, - ) - self.note( - "See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides", - context, - code=codes.OVERRIDE, - secondary_context=secondary_context, - ) + if name != "__post_init__": + # `__post_init__` is special, it can be incompatible by design. + # So, this note is misleading. + self.note( + "This violates the Liskov substitution principle", + context, + code=codes.OVERRIDE, + secondary_context=secondary_context, + ) + self.note( + "See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides", + context, + code=codes.OVERRIDE, + secondary_context=secondary_context, + ) if name == "__eq__" and type_name: multiline_msg = self.comparison_method_example_msg(class_name=type_name) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index cf58e577056c..bb3009dddf10 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterator, Optional +from typing import TYPE_CHECKING, Iterator, Optional from typing_extensions import Final from mypy import errorcodes, message_registry @@ -26,6 +26,7 @@ DataclassTransformSpec, Expression, FuncDef, + FuncItem, IfStmt, JsonDict, NameExpr, @@ -55,6 +56,7 @@ from mypy.types import ( AnyType, CallableType, + FunctionLike, Instance, LiteralType, NoneType, @@ -69,19 +71,23 @@ ) from mypy.typevars import fill_typevars +if TYPE_CHECKING: + from mypy.checker import TypeChecker + # The set of decorators that generate dataclasses. dataclass_makers: Final = {"dataclass", "dataclasses.dataclass"} SELF_TVAR_NAME: Final = "_DT" -_TRANSFORM_SPEC_FOR_DATACLASSES = DataclassTransformSpec( +_TRANSFORM_SPEC_FOR_DATACLASSES: Final = DataclassTransformSpec( eq_default=True, order_default=False, kw_only_default=False, frozen_default=False, field_specifiers=("dataclasses.Field", "dataclasses.field"), ) -_INTERNAL_REPLACE_SYM_NAME = "__mypy-replace" +_INTERNAL_REPLACE_SYM_NAME: Final = "__mypy-replace" +_INTERNAL_POST_INIT_SYM_NAME: Final = "__mypy-__post_init__" class DataclassAttribute: @@ -350,6 +356,8 @@ def transform(self) -> bool: if self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES: self._add_internal_replace_method(attributes) + if "__post_init__" in info.names: + self._add_internal_post_init_method(attributes) info.metadata["dataclass"] = { "attributes": [attr.serialize() for attr in attributes], @@ -385,7 +393,47 @@ def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> fallback=self._api.named_type("builtins.function"), ) - self._cls.info.names[_INTERNAL_REPLACE_SYM_NAME] = SymbolTableNode( + info.names[_INTERNAL_REPLACE_SYM_NAME] = SymbolTableNode( + kind=MDEF, node=FuncDef(typ=signature), plugin_generated=True + ) + + def _add_internal_post_init_method(self, attributes: list[DataclassAttribute]) -> None: + arg_types: list[Type] = [fill_typevars(self._cls.info)] + arg_kinds = [ARG_POS] + arg_names: list[str | None] = ["self"] + + info = self._cls.info + for attr in attributes: + if not attr.is_init_var: + continue + attr_type = attr.expand_type(info) + assert attr_type is not None + arg_types.append(attr_type) + # We always use `ARG_POS` without a default value, because it is practical. + # Consider this case: + # + # @dataclass + # class My: + # y: dataclasses.InitVar[str] = 'a' + # def __post_init__(self, y: str) -> None: ... + # + # We would be *required* to specify `y: str = ...` if default is added here. + # But, most people won't care about adding default values to `__post_init__`, + # because it is not designed to be called directly, and duplicating default values + # for the sake of type-checking is unpleasant. + arg_kinds.append(ARG_POS) + arg_names.append(attr.name) + + signature = CallableType( + arg_types=arg_types, + arg_kinds=arg_kinds, + arg_names=arg_names, + ret_type=NoneType(), + fallback=self._api.named_type("builtins.function"), + name="__post_init__", + ) + + info.names[_INTERNAL_POST_INIT_SYM_NAME] = SymbolTableNode( kind=MDEF, node=FuncDef(typ=signature), plugin_generated=True ) @@ -1052,3 +1100,33 @@ def replace_function_sig_callback(ctx: FunctionSigContext) -> CallableType: fallback=ctx.default_signature.fallback, name=f"{ctx.default_signature.name} of {inst_type_str}", ) + + +def is_processed_dataclass(info: TypeInfo | None) -> bool: + return info is not None and "dataclass" in info.metadata + + +def check_post_init(api: TypeChecker, defn: FuncItem, info: TypeInfo) -> None: + if defn.type is None: + return + + ideal_sig = info.get_method(_INTERNAL_POST_INIT_SYM_NAME) + if ideal_sig is None or ideal_sig.type is None: + return + + # We set it ourself, so it is always fine: + assert isinstance(ideal_sig.type, ProperType) + assert isinstance(ideal_sig.type, FunctionLike) + # Type of `FuncItem` is always `FunctionLike`: + assert isinstance(defn.type, FunctionLike) + + api.check_override( + override=defn.type, + original=ideal_sig.type, + name="__post_init__", + name_in_super="__post_init__", + supertype="dataclass", + original_class_or_static=False, + override_class_or_static=False, + node=defn, + ) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 1f6c8d143243..4a6e737ddd8d 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2197,6 +2197,217 @@ reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" a2 = replace(a, x='42') # E: Argument "x" to "replace" of "A[int]" has incompatible type "str"; expected "int" reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" +[case testPostInitCorrectSignature] +from typing import Any, Generic, TypeVar, Callable, Self +from dataclasses import dataclass, InitVar + +@dataclass +class Test1: + x: int + def __post_init__(self) -> None: ... + +@dataclass +class Test2: + x: int + y: InitVar[int] + z: str + def __post_init__(self, y: int) -> None: ... + +@dataclass +class Test3: + x: InitVar[int] + y: InitVar[str] + def __post_init__(self, x: int, y: str) -> None: ... + +@dataclass +class Test4: + x: int + y: InitVar[str] + z: InitVar[bool] = True + def __post_init__(self, y: str, z: bool) -> None: ... + +@dataclass +class Test5: + y: InitVar[str] = 'a' + z: InitVar[bool] = True + def __post_init__(self, y: str = 'a', z: bool = True) -> None: ... + +F = TypeVar('F', bound=Callable[..., Any]) +def identity(f: F) -> F: return f + +@dataclass +class Test6: + y: InitVar[str] + @identity # decorated method works + def __post_init__(self, y: str) -> None: ... + +T = TypeVar('T') + +@dataclass +class Test7(Generic[T]): + t: InitVar[T] + def __post_init__(self, t: T) -> None: ... + +@dataclass +class Test8: + s: InitVar[Self] + def __post_init__(self, s: Self) -> None: ... +[builtins fixtures/dataclasses.pyi] + +[case testPostInitSubclassing] +from dataclasses import dataclass, InitVar + +@dataclass +class Base: + a: str + x: InitVar[int] + def __post_init__(self, x: int) -> None: ... + +@dataclass +class Child(Base): + b: str + y: InitVar[str] + def __post_init__(self, x: int, y: str) -> None: ... + +@dataclass +class GrandChild(Child): + c: int + z: InitVar[str] = "a" + def __post_init__(self, x: int, y: str, z: str) -> None: ... +[builtins fixtures/dataclasses.pyi] + +[case testPostInitNotADataclassCheck] +from dataclasses import dataclass, InitVar + +class Regular: + __post_init__ = 1 # can be whatever + +class Base: + x: InitVar[int] + def __post_init__(self) -> None: ... # can be whatever + +@dataclass +class Child(Base): + y: InitVar[str] + def __post_init__(self, y: str) -> None: ... +[builtins fixtures/dataclasses.pyi] + +[case testPostInitMissingParam] +from dataclasses import dataclass, InitVar + +@dataclass +class Child: + y: InitVar[str] + def __post_init__(self) -> None: ... +[builtins fixtures/dataclasses.pyi] +[out] +main:6: error: Signature of "__post_init__" incompatible with supertype "dataclass" +main:6: note: Superclass: +main:6: note: def __post_init__(self: Child, y: str) -> None +main:6: note: Subclass: +main:6: note: def __post_init__(self: Child) -> None + +[case testPostInitWrongTypeAndName] +from dataclasses import dataclass, InitVar + +@dataclass +class Test1: + y: InitVar[str] + def __post_init__(self, x: int) -> None: ... # E: Argument 2 of "__post_init__" is incompatible with supertype "dataclass"; supertype defines the argument type as "str" + +@dataclass +class Test2: + y: InitVar[str] = 'a' + def __post_init__(self, x: int) -> None: ... # E: Argument 2 of "__post_init__" is incompatible with supertype "dataclass"; supertype defines the argument type as "str" +[builtins fixtures/dataclasses.pyi] + +[case testPostInitExtraParam] +from dataclasses import dataclass, InitVar + +@dataclass +class Child: + y: InitVar[str] + def __post_init__(self, y: str, z: int) -> None: ... +[builtins fixtures/dataclasses.pyi] +[out] +main:6: error: Signature of "__post_init__" incompatible with supertype "dataclass" +main:6: note: Superclass: +main:6: note: def __post_init__(self: Child, y: str) -> None +main:6: note: Subclass: +main:6: note: def __post_init__(self: Child, y: str, z: int) -> None + +[case testPostInitReturnType] +from dataclasses import dataclass, InitVar + +@dataclass +class Child: + y: InitVar[str] + def __post_init__(self, y: str) -> int: ... # E: Return type "int" of "__post_init__" incompatible with return type "None" in supertype "dataclass" +[builtins fixtures/dataclasses.pyi] + +[case testPostInitDecoratedMethodError] +from dataclasses import dataclass, InitVar +from typing import Any, Callable, TypeVar + +F = TypeVar('F', bound=Callable[..., Any]) +def identity(f: F) -> F: return f + +@dataclass +class Klass: + y: InitVar[str] + @identity + def __post_init__(self) -> None: ... +[builtins fixtures/dataclasses.pyi] +[out] +main:11: error: Signature of "__post_init__" incompatible with supertype "dataclass" +main:11: note: Superclass: +main:11: note: def __post_init__(self: Klass, y: str) -> None +main:11: note: Subclass: +main:11: note: def __post_init__(self: Klass) -> None + +[case testPostInitIsNotAFunction] +from dataclasses import dataclass, InitVar + +@dataclass +class Test: + y: InitVar[str] + __post_init__ = 1 # E: "__post_init__" method must be an instance method +[builtins fixtures/dataclasses.pyi] + +[case testPostInitClassMethod] +from dataclasses import dataclass, InitVar + +@dataclass +class Test: + y: InitVar[str] + @classmethod + def __post_init__(cls) -> None: ... +[builtins fixtures/dataclasses.pyi] +[out] +main:7: error: Signature of "__post_init__" incompatible with supertype "dataclass" +main:7: note: Superclass: +main:7: note: def __post_init__(self: Test, y: str) -> None +main:7: note: Subclass: +main:7: note: @classmethod +main:7: note: def __post_init__(cls: Type[Test]) -> None + +[case testPostInitStaticMethod] +from dataclasses import dataclass, InitVar + +@dataclass +class Test: + y: InitVar[str] + @staticmethod + def __post_init__() -> None: ... +[builtins fixtures/dataclasses.pyi] +[out] +main:7: error: Signature of "__post_init__" incompatible with supertype "dataclass" +main:7: note: Superclass: +main:7: note: def __post_init__(self: Test, y: str) -> None +main:7: note: Subclass: +main:7: note: @staticmethod +main:7: note: def __post_init__() -> None + [case testProtocolNoCrash] from typing import Protocol, Union, ClassVar from dataclasses import dataclass, field diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi index 710b8659d265..059c853a621f 100644 --- a/test-data/unit/fixtures/dataclasses.pyi +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -47,4 +47,5 @@ class list(Generic[_T], Sequence[_T]): class function: pass class classmethod: pass +class staticmethod: pass property = object() From 7ce3568d823c52e734983333271d315b637a61e6 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Mon, 26 Jun 2023 13:12:43 -0700 Subject: [PATCH 0067/1617] Exclude the same special attributes from Protocol as CPython (#15490) --- mypy/nodes.py | 21 ++++++++++ test-data/unit/check-protocols.test | 64 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/mypy/nodes.py b/mypy/nodes.py index 52dd9948e0c1..212ddb5def37 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2802,6 +2802,25 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_temp_node(self) +# Special attributes not collected as protocol members by Python 3.12 +# See typing._SPECIAL_NAMES +EXCLUDED_PROTOCOL_ATTRIBUTES: Final = frozenset( + { + "__abstractmethods__", + "__annotations__", + "__dict__", + "__doc__", + "__init__", + "__module__", + "__new__", + "__slots__", + "__subclasshook__", + "__weakref__", + "__class_getitem__", # Since Python 3.9 + } +) + + class TypeInfo(SymbolNode): """The type structure of a single class. @@ -3116,6 +3135,8 @@ def protocol_members(self) -> list[str]: if isinstance(node.node, (TypeAlias, TypeVarExpr, MypyFile)): # These are auxiliary definitions (and type aliases are prohibited). continue + if name in EXCLUDED_PROTOCOL_ATTRIBUTES: + continue members.add(name) return sorted(list(members)) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 6976b8ee0a39..6ba1fde4d022 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2789,6 +2789,70 @@ class A(Protocol): [builtins fixtures/tuple.pyi] +[case testProtocolSlotsIsNotProtocolMember] +# https://github.com/python/mypy/issues/11884 +from typing import Protocol + +class Foo(Protocol): + __slots__ = () +class NoSlots: + pass +class EmptySlots: + __slots__ = () +class TupleSlots: + __slots__ = ('x', 'y') +class StringSlots: + __slots__ = 'x y' +class InitSlots: + __slots__ = ('x',) + def __init__(self) -> None: + self.x = None +def foo(f: Foo): + pass + +# All should pass: +foo(NoSlots()) +foo(EmptySlots()) +foo(TupleSlots()) +foo(StringSlots()) +foo(InitSlots()) +[builtins fixtures/tuple.pyi] + +[case testProtocolSlotsAndRuntimeCheckable] +from typing import Protocol, runtime_checkable + +@runtime_checkable +class Foo(Protocol): + __slots__ = () +class Bar: + pass +issubclass(Bar, Foo) # Used to be an error, when `__slots__` counted as a protocol member +[builtins fixtures/isinstance.pyi] +[typing fixtures/typing-full.pyi] + + +[case testProtocolWithClassGetItem] +# https://github.com/python/mypy/issues/11886 +from typing import Any, Iterable, Protocol, Union + +class B: + ... + +class C: + def __class_getitem__(cls, __item: Any) -> Any: + ... + +class SupportsClassGetItem(Protocol): + __slots__: Union[str, Iterable[str]] = () + def __class_getitem__(cls, __item: Any) -> Any: + ... + +b1: SupportsClassGetItem = B() +c1: SupportsClassGetItem = C() +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + + [case testNoneVsProtocol] # mypy: strict-optional from typing_extensions import Protocol From 8290bb81db80b139185a3543bd459f904841fe44 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 27 Jun 2023 00:25:59 +0100 Subject: [PATCH 0068/1617] Support flexible TypedDict creation/update (#15425) Fixes #9408 Fixes #4122 Fixes #6462 Supersedes #13353 This PR enables two similar technically unsafe behaviors for TypedDicts, as @JukkaL explained in https://github.com/python/mypy/issues/6462#issuecomment-466464229 allowing an "incomplete" TypedDict as an argument to `.update()` is technically unsafe (and a similar argument applies to `**` syntax in TypedDict literals). These are however very common patterns (judging from number of duplicates to above issues), so I think we should support them. Here is what I propose: * Always support cases that are safe (like passing the type itself to `update`) * Allow popular but technically unsafe cases _by default_ * Have a new flag (as part of `--strict`) to fall back to current behavior Note that unfortunately we can't use just a custom new error code, since we need to conditionally tweak some types in a plugin. Btw there are couple TODOs I add here: * First is for unsafe behavior for repeated TypedDict keys. This is not new, I just noticed it when working on this * Second is for tricky corner case involving multiple `**` items where we may have false-negatives in strict mode. Note that I don't test all the possible combinations here (since the phase space is huge), but I think I am testing all main ingredients (and I will be glad to add more if needed): * All syntax variants for TypedDicts creation are handled * Various shadowing/overrides scenarios * Required vs non-required keys handling * Union types (both as item and target types) * Inference for generic TypedDicts * New strictness flag More than half of the tests I took from the original PR #13353 --- docs/source/command_line.rst | 28 ++ mypy/checkexpr.py | 255 ++++++++++--- mypy/main.py | 13 +- mypy/messages.py | 18 + mypy/options.py | 6 +- mypy/plugins/default.py | 27 ++ mypy/semanal.py | 4 +- mypy/subtypes.py | 10 +- mypy/types.py | 4 + .../unit/check-parameter-specification.test | 2 +- test-data/unit/check-typeddict.test | 361 ++++++++++++++++++ 11 files changed, 659 insertions(+), 69 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 2809294092ab..d9de5cd8f9bd 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -612,6 +612,34 @@ of the above sections. assert text is not None # OK, check against None is allowed as a special case. +.. option:: --extra-checks + + This flag enables additional checks that are technically correct but may be + impractical in real code. In particular, it prohibits partial overlap in + ``TypedDict`` updates, and makes arguments prepended via ``Concatenate`` + positional-only. For example: + + .. code-block:: python + + from typing import TypedDict + + class Foo(TypedDict): + a: int + + class Bar(TypedDict): + a: int + b: int + + def test(foo: Foo, bar: Bar) -> None: + # This is technically unsafe since foo can have a subtype of Foo at + # runtime, where type of key "b" is incompatible with int, see below + bar.update(foo) + + class Bad(Foo): + b: str + bad: Bad = {"a": 0, "b": "no"} + test(bad, bar) + .. option:: --strict This flag mode enables all optional error checking flags. You can see the diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 43896171eadc..986e58c21762 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4,8 +4,9 @@ import itertools import time +from collections import defaultdict from contextlib import contextmanager -from typing import Callable, ClassVar, Iterator, List, Optional, Sequence, cast +from typing import Callable, ClassVar, Iterable, Iterator, List, Optional, Sequence, cast from typing_extensions import Final, TypeAlias as _TypeAlias, overload import mypy.checker @@ -695,74 +696,183 @@ def check_typeddict_call( context: Context, orig_callee: Type | None, ) -> Type: - if args and all([ak == ARG_NAMED for ak in arg_kinds]): - # ex: Point(x=42, y=1337) - assert all(arg_name is not None for arg_name in arg_names) - item_names = cast(List[str], arg_names) - item_args = args - return self.check_typeddict_call_with_kwargs( - callee, dict(zip(item_names, item_args)), context, orig_callee - ) + if args and all([ak in (ARG_NAMED, ARG_STAR2) for ak in arg_kinds]): + # ex: Point(x=42, y=1337, **extras) + # This is a bit ugly, but this is a price for supporting all possible syntax + # variants for TypedDict constructors. + kwargs = zip([StrExpr(n) if n is not None else None for n in arg_names], args) + result = self.validate_typeddict_kwargs(kwargs=kwargs, callee=callee) + if result is not None: + validated_kwargs, always_present_keys = result + return self.check_typeddict_call_with_kwargs( + callee, validated_kwargs, context, orig_callee, always_present_keys + ) + return AnyType(TypeOfAny.from_error) if len(args) == 1 and arg_kinds[0] == ARG_POS: unique_arg = args[0] if isinstance(unique_arg, DictExpr): - # ex: Point({'x': 42, 'y': 1337}) + # ex: Point({'x': 42, 'y': 1337, **extras}) return self.check_typeddict_call_with_dict( - callee, unique_arg, context, orig_callee + callee, unique_arg.items, context, orig_callee ) if isinstance(unique_arg, CallExpr) and isinstance(unique_arg.analyzed, DictExpr): - # ex: Point(dict(x=42, y=1337)) + # ex: Point(dict(x=42, y=1337, **extras)) return self.check_typeddict_call_with_dict( - callee, unique_arg.analyzed, context, orig_callee + callee, unique_arg.analyzed.items, context, orig_callee ) if not args: # ex: EmptyDict() - return self.check_typeddict_call_with_kwargs(callee, {}, context, orig_callee) + return self.check_typeddict_call_with_kwargs(callee, {}, context, orig_callee, set()) self.chk.fail(message_registry.INVALID_TYPEDDICT_ARGS, context) return AnyType(TypeOfAny.from_error) - def validate_typeddict_kwargs(self, kwargs: DictExpr) -> dict[str, Expression] | None: - item_args = [item[1] for item in kwargs.items] - - item_names = [] # List[str] - for item_name_expr, item_arg in kwargs.items: - literal_value = None + def validate_typeddict_kwargs( + self, kwargs: Iterable[tuple[Expression | None, Expression]], callee: TypedDictType + ) -> tuple[dict[str, list[Expression]], set[str]] | None: + # All (actual or mapped from ** unpacks) expressions that can match given key. + result = defaultdict(list) + # Keys that are guaranteed to be present no matter what (e.g. for all items of a union) + always_present_keys = set() + # Indicates latest encountered ** unpack among items. + last_star_found = None + + for item_name_expr, item_arg in kwargs: if item_name_expr: key_type = self.accept(item_name_expr) values = try_getting_str_literals(item_name_expr, key_type) + literal_value = None if values and len(values) == 1: literal_value = values[0] - if literal_value is None: - key_context = item_name_expr or item_arg - self.chk.fail( - message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - key_context, - code=codes.LITERAL_REQ, - ) - return None + if literal_value is None: + key_context = item_name_expr or item_arg + self.chk.fail( + message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, + key_context, + code=codes.LITERAL_REQ, + ) + return None + else: + # A directly present key unconditionally shadows all previously found + # values from ** items. + # TODO: for duplicate keys, type-check all values. + result[literal_value] = [item_arg] + always_present_keys.add(literal_value) else: - item_names.append(literal_value) - return dict(zip(item_names, item_args)) + last_star_found = item_arg + if not self.validate_star_typeddict_item( + item_arg, callee, result, always_present_keys + ): + return None + if self.chk.options.extra_checks and last_star_found is not None: + absent_keys = [] + for key in callee.items: + if key not in callee.required_keys and key not in result: + absent_keys.append(key) + if absent_keys: + # Having an optional key not explicitly declared by a ** unpacked + # TypedDict is unsafe, it may be an (incompatible) subtype at runtime. + # TODO: catch the cases where a declared key is overridden by a subsequent + # ** item without it (and not again overriden with complete ** item). + self.msg.non_required_keys_absent_with_star(absent_keys, last_star_found) + return result, always_present_keys + + def validate_star_typeddict_item( + self, + item_arg: Expression, + callee: TypedDictType, + result: dict[str, list[Expression]], + always_present_keys: set[str], + ) -> bool: + """Update keys/expressions from a ** expression in TypedDict constructor. + + Note `result` and `always_present_keys` are updated in place. Return true if the + expression `item_arg` may valid in `callee` TypedDict context. + """ + with self.chk.local_type_map(), self.msg.filter_errors(): + inferred = get_proper_type(self.accept(item_arg, type_context=callee)) + possible_tds = [] + if isinstance(inferred, TypedDictType): + possible_tds = [inferred] + elif isinstance(inferred, UnionType): + for item in get_proper_types(inferred.relevant_items()): + if isinstance(item, TypedDictType): + possible_tds.append(item) + elif not self.valid_unpack_fallback_item(item): + self.msg.unsupported_target_for_star_typeddict(item, item_arg) + return False + elif not self.valid_unpack_fallback_item(inferred): + self.msg.unsupported_target_for_star_typeddict(inferred, item_arg) + return False + all_keys: set[str] = set() + for td in possible_tds: + all_keys |= td.items.keys() + for key in all_keys: + arg = TempNode( + UnionType.make_union([td.items[key] for td in possible_tds if key in td.items]) + ) + arg.set_line(item_arg) + if all(key in td.required_keys for td in possible_tds): + always_present_keys.add(key) + # Always present keys override previously found values. This is done + # to support use cases like `Config({**defaults, **overrides})`, where + # some `overrides` types are narrower that types in `defaults`, and + # former are too wide for `Config`. + if result[key]: + first = result[key][0] + if not isinstance(first, TempNode): + # We must always preserve any non-synthetic values, so that + # we will accept them even if they are shadowed. + result[key] = [first, arg] + else: + result[key] = [arg] + else: + result[key] = [arg] + else: + # If this key is not required at least in some item of a union + # it may not shadow previous item, so we need to type check both. + result[key].append(arg) + return True + + def valid_unpack_fallback_item(self, typ: ProperType) -> bool: + if isinstance(typ, AnyType): + return True + if not isinstance(typ, Instance) or not typ.type.has_base("typing.Mapping"): + return False + mapped = map_instance_to_supertype(typ, self.chk.lookup_typeinfo("typing.Mapping")) + return all(isinstance(a, AnyType) for a in get_proper_types(mapped.args)) def match_typeddict_call_with_dict( - self, callee: TypedDictType, kwargs: DictExpr, context: Context + self, + callee: TypedDictType, + kwargs: list[tuple[Expression | None, Expression]], + context: Context, ) -> bool: - validated_kwargs = self.validate_typeddict_kwargs(kwargs=kwargs) - if validated_kwargs is not None: + result = self.validate_typeddict_kwargs(kwargs=kwargs, callee=callee) + if result is not None: + validated_kwargs, _ = result return callee.required_keys <= set(validated_kwargs.keys()) <= set(callee.items.keys()) else: return False def check_typeddict_call_with_dict( - self, callee: TypedDictType, kwargs: DictExpr, context: Context, orig_callee: Type | None + self, + callee: TypedDictType, + kwargs: list[tuple[Expression | None, Expression]], + context: Context, + orig_callee: Type | None, ) -> Type: - validated_kwargs = self.validate_typeddict_kwargs(kwargs=kwargs) - if validated_kwargs is not None: + result = self.validate_typeddict_kwargs(kwargs=kwargs, callee=callee) + if result is not None: + validated_kwargs, always_present_keys = result return self.check_typeddict_call_with_kwargs( - callee, kwargs=validated_kwargs, context=context, orig_callee=orig_callee + callee, + kwargs=validated_kwargs, + context=context, + orig_callee=orig_callee, + always_present_keys=always_present_keys, ) else: return AnyType(TypeOfAny.from_error) @@ -803,20 +913,37 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType def check_typeddict_call_with_kwargs( self, callee: TypedDictType, - kwargs: dict[str, Expression], + kwargs: dict[str, list[Expression]], context: Context, orig_callee: Type | None, + always_present_keys: set[str], ) -> Type: actual_keys = kwargs.keys() - if not (callee.required_keys <= actual_keys <= callee.items.keys()): - expected_keys = [ - key - for key in callee.items.keys() - if key in callee.required_keys or key in actual_keys - ] - self.msg.unexpected_typeddict_keys( - callee, expected_keys=expected_keys, actual_keys=list(actual_keys), context=context - ) + if not ( + callee.required_keys <= always_present_keys and actual_keys <= callee.items.keys() + ): + if not (actual_keys <= callee.items.keys()): + self.msg.unexpected_typeddict_keys( + callee, + expected_keys=[ + key + for key in callee.items.keys() + if key in callee.required_keys or key in actual_keys + ], + actual_keys=list(actual_keys), + context=context, + ) + if not (callee.required_keys <= always_present_keys): + self.msg.unexpected_typeddict_keys( + callee, + expected_keys=[ + key for key in callee.items.keys() if key in callee.required_keys + ], + actual_keys=[ + key for key in always_present_keys if key in callee.required_keys + ], + context=context, + ) if callee.required_keys > actual_keys: # found_set is a sub-set of the required_keys # This means we're missing some keys and as such, we can't @@ -839,7 +966,10 @@ def check_typeddict_call_with_kwargs( with self.msg.filter_errors(), self.chk.local_type_map(): orig_ret_type, _ = self.check_callable_call( infer_callee, - list(kwargs.values()), + # We use first expression for each key to infer type variables of a generic + # TypedDict. This is a bit arbitrary, but in most cases will work better than + # trying to infer a union or a join. + [args[0] for args in kwargs.values()], [ArgKind.ARG_NAMED] * len(kwargs), context, list(kwargs.keys()), @@ -856,17 +986,18 @@ def check_typeddict_call_with_kwargs( for item_name, item_expected_type in ret_type.items.items(): if item_name in kwargs: - item_value = kwargs[item_name] - self.chk.check_simple_assignment( - lvalue_type=item_expected_type, - rvalue=item_value, - context=item_value, - msg=ErrorMessage( - message_registry.INCOMPATIBLE_TYPES.value, code=codes.TYPEDDICT_ITEM - ), - lvalue_name=f'TypedDict item "{item_name}"', - rvalue_name="expression", - ) + item_values = kwargs[item_name] + for item_value in item_values: + self.chk.check_simple_assignment( + lvalue_type=item_expected_type, + rvalue=item_value, + context=item_value, + msg=ErrorMessage( + message_registry.INCOMPATIBLE_TYPES.value, code=codes.TYPEDDICT_ITEM + ), + lvalue_name=f'TypedDict item "{item_name}"', + rvalue_name="expression", + ) return orig_ret_type @@ -4382,7 +4513,7 @@ def check_typeddict_literal_in_context( self, e: DictExpr, typeddict_context: TypedDictType ) -> Type: orig_ret_type = self.check_typeddict_call_with_dict( - callee=typeddict_context, kwargs=e, context=e, orig_callee=None + callee=typeddict_context, kwargs=e.items, context=e, orig_callee=None ) ret_type = get_proper_type(orig_ret_type) if isinstance(ret_type, TypedDictType): @@ -4482,7 +4613,9 @@ def find_typeddict_context( for item in context.items: item_contexts = self.find_typeddict_context(item, dict_expr) for item_context in item_contexts: - if self.match_typeddict_call_with_dict(item_context, dict_expr, dict_expr): + if self.match_typeddict_call_with_dict( + item_context, dict_expr.items, dict_expr + ): items.append(item_context) return items # No TypedDict type in context. diff --git a/mypy/main.py b/mypy/main.py index b60c5b2a6bba..22ff3e32a718 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -826,10 +826,12 @@ def add_invertible_flag( ) add_invertible_flag( - "--strict-concatenate", + "--extra-checks", default=False, strict_flag=True, - help="Make arguments prepended via Concatenate be truly positional-only", + help="Enable additional checks that are technically correct but may be impractical " + "in real code. For example, this prohibits partial overlap in TypedDict updates, " + "and makes arguments prepended via Concatenate positional-only", group=strictness_group, ) @@ -1155,6 +1157,8 @@ def add_invertible_flag( parser.add_argument( "--disable-memoryview-promotion", action="store_true", help=argparse.SUPPRESS ) + # This flag is deprecated, it has been moved to --extra-checks + parser.add_argument("--strict-concatenate", action="store_true", help=argparse.SUPPRESS) # options specifying code to check code_group = parser.add_argument_group( @@ -1226,8 +1230,11 @@ def add_invertible_flag( parser.error(f"Cannot find config file '{config_file}'") options = Options() + strict_option_set = False def set_strict_flags() -> None: + nonlocal strict_option_set + strict_option_set = True for dest, value in strict_flag_assignments: setattr(options, dest, value) @@ -1379,6 +1386,8 @@ def set_strict_flags() -> None: "Warning: --enable-recursive-aliases is deprecated;" " recursive types are enabled by default" ) + if options.strict_concatenate and not strict_option_set: + print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") # Set target. if special_opts.modules + special_opts.packages: diff --git a/mypy/messages.py b/mypy/messages.py index b74a795a4318..ea7923c59778 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1757,6 +1757,24 @@ def need_annotation_for_var( def explicit_any(self, ctx: Context) -> None: self.fail('Explicit "Any" is not allowed', ctx) + def unsupported_target_for_star_typeddict(self, typ: Type, ctx: Context) -> None: + self.fail( + "Unsupported type {} for ** expansion in TypedDict".format( + format_type(typ, self.options) + ), + ctx, + code=codes.TYPEDDICT_ITEM, + ) + + def non_required_keys_absent_with_star(self, keys: list[str], ctx: Context) -> None: + self.fail( + "Non-required {} not explicitly found in any ** item".format( + format_key_list(keys, short=True) + ), + ctx, + code=codes.TYPEDDICT_ITEM, + ) + def unexpected_typeddict_keys( self, typ: TypedDictType, diff --git a/mypy/options.py b/mypy/options.py index f75734124eb0..e1d731c1124c 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -40,6 +40,7 @@ class BuildType: "disallow_untyped_defs", "enable_error_code", "enabled_error_codes", + "extra_checks", "follow_imports_for_stubs", "follow_imports", "ignore_errors", @@ -200,9 +201,12 @@ def __init__(self) -> None: # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors. self.strict_equality = False - # Make arguments prepended via Concatenate be truly positional-only. + # Deprecated, use extra_checks instead. self.strict_concatenate = False + # Enable additional checks that are technically correct but impractical. + self.extra_checks = False + # Report an error for any branches inferred to be unreachable as a result of # type analysis. self.warn_unreachable = False diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index b83c0192a14b..f5dea0621177 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -31,7 +31,9 @@ TypedDictType, TypeOfAny, TypeVarType, + UnionType, get_proper_type, + get_proper_types, ) @@ -404,6 +406,31 @@ def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: assert isinstance(arg_type, TypedDictType) arg_type = arg_type.as_anonymous() arg_type = arg_type.copy_modified(required_keys=set()) + if ctx.args and ctx.args[0]: + with ctx.api.msg.filter_errors(): + inferred = get_proper_type( + ctx.api.get_expression_type(ctx.args[0][0], type_context=arg_type) + ) + possible_tds = [] + if isinstance(inferred, TypedDictType): + possible_tds = [inferred] + elif isinstance(inferred, UnionType): + possible_tds = [ + t + for t in get_proper_types(inferred.relevant_items()) + if isinstance(t, TypedDictType) + ] + items = [] + for td in possible_tds: + item = arg_type.copy_modified( + required_keys=(arg_type.required_keys | td.required_keys) + & arg_type.items.keys() + ) + if not ctx.api.options.extra_checks: + item = item.copy_modified(item_names=list(td.items)) + items.append(item) + if items: + arg_type = make_simplified_union(items) return signature.copy_modified(arg_types=[arg_type]) return signature diff --git a/mypy/semanal.py b/mypy/semanal.py index 43960d972101..d18cc4298fed 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5084,14 +5084,14 @@ def translate_dict_call(self, call: CallExpr) -> DictExpr | None: For other variants of dict(...), return None. """ - if not all(kind == ARG_NAMED for kind in call.arg_kinds): + if not all(kind in (ARG_NAMED, ARG_STAR2) for kind in call.arg_kinds): # Must still accept those args. for a in call.args: a.accept(self) return None expr = DictExpr( [ - (StrExpr(cast(str, key)), value) # since they are all ARG_NAMED + (StrExpr(key) if key is not None else None, value) for key, value in zip(call.arg_names, call.args) ] ) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a3b28a3e24de..c9de56edfa36 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -694,7 +694,9 @@ def visit_callable_type(self, left: CallableType) -> bool: right, is_compat=self._is_subtype, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, - strict_concatenate=self.options.strict_concatenate if self.options else True, + strict_concatenate=(self.options.extra_checks or self.options.strict_concatenate) + if self.options + else True, ) elif isinstance(right, Overloaded): return all(self._is_subtype(left, item) for item in right.items) @@ -858,7 +860,11 @@ def visit_overloaded(self, left: Overloaded) -> bool: else: # If this one overlaps with the supertype in any way, but it wasn't # an exact match, then it's a potential error. - strict_concat = self.options.strict_concatenate if self.options else True + strict_concat = ( + (self.options.extra_checks or self.options.strict_concatenate) + if self.options + else True + ) if left_index not in matched_overloads and ( is_callable_compatible( left_item, diff --git a/mypy/types.py b/mypy/types.py index 33673b58f775..131383790ec8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2437,6 +2437,7 @@ def copy_modified( *, fallback: Instance | None = None, item_types: list[Type] | None = None, + item_names: list[str] | None = None, required_keys: set[str] | None = None, ) -> TypedDictType: if fallback is None: @@ -2447,6 +2448,9 @@ def copy_modified( items = dict(zip(self.items, item_types)) if required_keys is None: required_keys = self.required_keys + if item_names is not None: + items = {k: v for (k, v) in items.items() if k in item_names} + required_keys &= set(item_names) return TypedDictType(items, required_keys, fallback, self.line, self.column) def create_anonymous_fallback(self) -> Instance: diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index cafcaca0a14c..bebbbf4b1676 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -570,7 +570,7 @@ reveal_type(f(n)) # N: Revealed type is "def (builtins.int, builtins.bytes) -> [builtins fixtures/paramspec.pyi] [case testParamSpecConcatenateNamedArgs] -# flags: --python-version 3.8 --strict-concatenate +# flags: --python-version 3.8 --extra-checks # this is one noticeable deviation from PEP but I believe it is for the better from typing_extensions import ParamSpec, Concatenate from typing import Callable, TypeVar diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index fc487d2d553d..4d2d64848515 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2885,3 +2885,364 @@ d: A d[''] # E: TypedDict "A" has no key "" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFlexibleUpdate] +from mypy_extensions import TypedDict + +A = TypedDict("A", {"foo": int, "bar": int}) +B = TypedDict("B", {"foo": int}) + +a = A({"foo": 1, "bar": 2}) +b = B({"foo": 2}) +a.update({"foo": 2}) +a.update(b) +a.update(a) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictStrictUpdate] +# flags: --extra-checks +from mypy_extensions import TypedDict + +A = TypedDict("A", {"foo": int, "bar": int}) +B = TypedDict("B", {"foo": int}) + +a = A({"foo": 1, "bar": 2}) +b = B({"foo": 2}) +a.update({"foo": 2}) # OK +a.update(b) # E: Argument 1 to "update" of "TypedDict" has incompatible type "B"; expected "TypedDict({'foo': int, 'bar'?: int})" +a.update(a) # OK +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFlexibleUpdateUnion] +from typing import Union +from mypy_extensions import TypedDict + +A = TypedDict("A", {"foo": int, "bar": int}) +B = TypedDict("B", {"foo": int}) +C = TypedDict("C", {"bar": int}) + +a = A({"foo": 1, "bar": 2}) +u: Union[B, C] +a.update(u) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFlexibleUpdateUnionExtra] +from typing import Union +from mypy_extensions import TypedDict + +A = TypedDict("A", {"foo": int, "bar": int}) +B = TypedDict("B", {"foo": int, "extra": int}) +C = TypedDict("C", {"bar": int, "extra": int}) + +a = A({"foo": 1, "bar": 2}) +u: Union[B, C] +a.update(u) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFlexibleUpdateUnionStrict] +# flags: --extra-checks +from typing import Union, NotRequired +from mypy_extensions import TypedDict + +A = TypedDict("A", {"foo": int, "bar": int}) +A1 = TypedDict("A1", {"foo": int, "bar": NotRequired[int]}) +A2 = TypedDict("A2", {"foo": NotRequired[int], "bar": int}) +B = TypedDict("B", {"foo": int}) +C = TypedDict("C", {"bar": int}) + +a = A({"foo": 1, "bar": 2}) +u: Union[B, C] +a.update(u) # E: Argument 1 to "update" of "TypedDict" has incompatible type "Union[B, C]"; expected "Union[TypedDict({'foo': int, 'bar'?: int}), TypedDict({'foo'?: int, 'bar': int})]" +u2: Union[A1, A2] +a.update(u2) # OK +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackSame] +# flags: --extra-checks +from typing import TypedDict + +class Foo(TypedDict): + a: int + b: int + +foo1: Foo = {"a": 1, "b": 1} +foo2: Foo = {**foo1, "b": 2} +foo3 = Foo(**foo1, b=2) +foo4 = Foo({**foo1, "b": 2}) +foo5 = Foo(dict(**foo1, b=2)) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackCompatible] +# flags: --extra-checks +from typing import TypedDict + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + a: int + b: int + +foo: Foo = {"a": 1} +bar: Bar = {**foo, "b": 2} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackIncompatible] +from typing import TypedDict + +class Foo(TypedDict): + a: int + b: str + +class Bar(TypedDict): + a: int + b: int + +foo: Foo = {"a": 1, "b": "a"} +bar1: Bar = {**foo, "b": 2} # Incompatible item is overriden +bar2: Bar = {**foo, "a": 2} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackNotRequiredKeyIncompatible] +from typing import TypedDict, NotRequired + +class Foo(TypedDict): + a: NotRequired[str] + +class Bar(TypedDict): + a: NotRequired[int] + +foo: Foo = {} +bar: Bar = {**foo} # E: Incompatible types (expression has type "str", TypedDict item "a" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictUnpackMissingOrExtraKey] +from typing import TypedDict + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + a: int + b: int + +foo1: Foo = {"a": 1} +bar1: Bar = {"a": 1, "b": 1} +foo2: Foo = {**bar1} # E: Extra key "b" for TypedDict "Foo" +bar2: Bar = {**foo1} # E: Missing key "b" for TypedDict "Bar" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackNotRequiredKeyExtra] +from typing import TypedDict, NotRequired + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + a: int + b: NotRequired[int] + +foo1: Foo = {"a": 1} +bar1: Bar = {"a": 1} +foo2: Foo = {**bar1} # E: Extra key "b" for TypedDict "Foo" +bar2: Bar = {**foo1} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackRequiredKeyMissing] +from typing import TypedDict, NotRequired + +class Foo(TypedDict): + a: NotRequired[int] + +class Bar(TypedDict): + a: int + +foo: Foo = {"a": 1} +bar: Bar = {**foo} # E: Missing key "a" for TypedDict "Bar" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackMultiple] +# flags: --extra-checks +from typing import TypedDict + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + b: int + +class Baz(TypedDict): + a: int + b: int + c: int + +foo: Foo = {"a": 1} +bar: Bar = {"b": 1} +baz: Baz = {**foo, **bar, "c": 1} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackNested] +from typing import TypedDict + +class Foo(TypedDict): + a: int + b: int + +class Bar(TypedDict): + c: Foo + d: int + +foo: Foo = {"a": 1, "b": 1} +bar: Bar = {"c": foo, "d": 1} +bar2: Bar = {**bar, "c": {**bar["c"], "b": 2}, "d": 2} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackNestedError] +from typing import TypedDict + +class Foo(TypedDict): + a: int + b: int + +class Bar(TypedDict): + c: Foo + d: int + +foo: Foo = {"a": 1, "b": 1} +bar: Bar = {"c": foo, "d": 1} +bar2: Bar = {**bar, "c": {**bar["c"], "b": "wrong"}, "d": 2} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackOverrideRequired] +from mypy_extensions import TypedDict + +Details = TypedDict('Details', {'first_name': str, 'last_name': str}) +DetailsSubset = TypedDict('DetailsSubset', {'first_name': str, 'last_name': str}, total=False) +defaults: Details = {'first_name': 'John', 'last_name': 'Luther'} + +def generate(data: DetailsSubset) -> Details: + return {**defaults, **data} # OK +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackUntypedDict] +from typing import Any, Dict, TypedDict + +class Bar(TypedDict): + pass + +foo: Dict[str, Any] = {} +bar: Bar = {**foo} # E: Unsupported type "Dict[str, Any]" for ** expansion in TypedDict +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackIntoUnion] +from typing import TypedDict, Union + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + b: int + +foo: Foo = {'a': 1} +foo_or_bar: Union[Foo, Bar] = {**foo} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackFromUnion] +from typing import TypedDict, Union + +class Foo(TypedDict): + a: int + b: int + +class Bar(TypedDict): + b: int + +foo_or_bar: Union[Foo, Bar] = {'b': 1} +foo: Bar = {**foo_or_bar} # E: Extra key "a" for TypedDict "Bar" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackUnionRequiredMissing] +from typing import TypedDict, NotRequired, Union + +class Foo(TypedDict): + a: int + b: int + +class Bar(TypedDict): + a: int + b: NotRequired[int] + +foo_or_bar: Union[Foo, Bar] = {"a": 1} +foo: Foo = {**foo_or_bar} # E: Missing key "b" for TypedDict "Foo" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackInference] +from typing import TypedDict, Generic, TypeVar + +class Foo(TypedDict): + a: int + b: str + +T = TypeVar("T") +class TD(TypedDict, Generic[T]): + a: T + b: str + +foo: Foo +bar = TD(**foo) +reveal_type(bar) # N: Revealed type is "TypedDict('__main__.TD', {'a': builtins.int, 'b': builtins.str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackStrictMode] +# flags: --extra-checks +from typing import TypedDict, NotRequired + +class Foo(TypedDict): + a: int + +class Bar(TypedDict): + a: int + b: NotRequired[int] + +foo: Foo +bar: Bar = {**foo} # E: Non-required key "b" not explicitly found in any ** item +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackAny] +from typing import Any, TypedDict, NotRequired, Dict, Union + +class Foo(TypedDict): + a: int + b: NotRequired[int] + +x: Any +y: Dict[Any, Any] +z: Union[Any, Dict[Any, Any]] +t1: Foo = {**x} # E: Missing key "a" for TypedDict "Foo" +t2: Foo = {**y} # E: Missing key "a" for TypedDict "Foo" +t3: Foo = {**z} # E: Missing key "a" for TypedDict "Foo" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 4d94271b37dd2912e3030c5c5d71fd22099e0eb2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:48:39 +0200 Subject: [PATCH 0069/1617] Fix check-manifest action (#15531) Fix sdist and python wheel build. https://github.com/mgedmin/check-manifest https://github.com/mypyc/mypy_mypyc-wheels/actions/runs/5384036273/jobs/9771489841#step:5:32 --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fc70d4279681..399168663bed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,3 +76,6 @@ extend-exclude = [ # typeshed has its own .pyi-specific configuration "mypy/typeshed/*", ] + +[tool.check-manifest] +ignore = ["**/.readthedocs.yaml"] From 4012c50382641aa4a15fcb7155f281469bc7a5fa Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 28 Jun 2023 02:03:57 +0100 Subject: [PATCH 0070/1617] Improve ruff config (#15516) --- .github/workflows/test.yml | 2 +- .pre-commit-config.yaml | 5 +---- CONTRIBUTING.md | 11 ++--------- README.md | 2 +- mypy/copytype.py | 2 +- pyproject.toml | 30 +++++++++++++++--------------- test-requirements.txt | 1 - tox.ini | 1 + 8 files changed, 22 insertions(+), 32 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6fc8fb11c6f4..5bd20f2773c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,7 +96,7 @@ jobs: # We also run these checks with pre-commit in CI, # but it's useful to run them with tox too, # to ensure the tox env works as expected - - name: Formatting with Black + isort and code style with ruff + - name: Formatting and code style with Black + ruff python: '3.10' arch: x64 os: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cbb3672bd986..1ad273ce5584 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,11 +9,8 @@ repos: rev: 23.3.0 # must match test-requirements.txt hooks: - id: black - - repo: https://github.com/pycqa/isort - rev: 5.12.0 # must match test-requirements.txt - hooks: - - id: isort - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.272 # must match test-requirements.txt hooks: - id: ruff + args: [--exit-non-zero-on-fix] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99ba2f8c9a76..82e55f437e87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,11 +88,8 @@ pytest -n0 -k 'test_name' # Run all test cases in the "test-data/unit/check-dataclasses.test" file pytest mypy/test/testcheck.py::TypeCheckSuite::check-dataclasses.test -# Run the linter -ruff . - -# Run formatters -black . && isort . +# Run the formatters and linters +python runtests.py lint ``` For an in-depth guide on running and writing tests, @@ -154,10 +151,6 @@ advice about good pull requests for open-source projects applies; we have [our own writeup](https://github.com/python/mypy/wiki/Good-Pull-Request) of this advice. -We are using `black` and `isort` to enforce a consistent coding style. -Run `black . && isort .` before your commits, otherwise you would receive -a CI failure. - Also, do not squash your commits after you have submitted a pull request, as this erases context during review. We will squash commits when the pull request is merged. diff --git a/README.md b/README.md index 164957b1491a..8b1ebbc0f2cb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Mypy: Static Typing for Python [![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +[![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) Got a question? --------------- diff --git a/mypy/copytype.py b/mypy/copytype.py index 0b63c8e07ae8..4ca381c4a8c4 100644 --- a/mypy/copytype.py +++ b/mypy/copytype.py @@ -28,7 +28,7 @@ ) # type_visitor needs to be imported after types -from mypy.type_visitor import TypeVisitor # isort: skip +from mypy.type_visitor import TypeVisitor # ruff: isort: skip def copy_type(t: ProperType) -> ProperType: diff --git a/pyproject.toml b/pyproject.toml index 399168663bed..22e28f8f6f68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,29 +27,18 @@ force-exclude = ''' ^/test-data ''' -[tool.isort] -py_version = 37 -profile = "black" -line_length = 99 -combine_as_imports = true -skip_gitignore = true -extra_standard_library = ["typing_extensions"] -skip_glob = [ - "mypy/typeshed/*", - "mypyc/test-data/*", - "test-data/*", -] - [tool.ruff] line-length = 99 target-version = "py37" +fix = true select = [ - "E", # pycoderstyle (error) + "E", # pycodestyle (error) "F", # pyflakes "B", # flake8-bugbear + "I", # isort "RUF100", # Unused noqa comments - "PGH004" # blanket noqa comments + "PGH004" # blanket noqa comments ] ignore = [ @@ -64,6 +53,13 @@ ignore = [ "E741", # Ambiguous variable name ] +unfixable = [ + "F841", # unused variable. ruff keeps the call, but mostly we want to get rid of it all + "F601", # automatic fix might obscure issue + "F602", # automatic fix might obscure issue + "B018", # automatic fix might obscure issue +] + extend-exclude = [ "@*", # Sphinx configuration is irrelevant @@ -77,5 +73,9 @@ extend-exclude = [ "mypy/typeshed/*", ] +[tool.ruff.isort] +combine-as-imports = true +extra-standard-library = ["typing_extensions"] + [tool.check-manifest] ignore = ["**/.readthedocs.yaml"] diff --git a/test-requirements.txt b/test-requirements.txt index f7d37058e544..6b046e1469eb 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,6 @@ attrs>=18.0 black==23.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 -isort[colors]==5.12.0; python_version >= "3.8" # must match version in .pre-commit-config.yaml lxml>=4.9.1; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' pre-commit pre-commit-hooks==4.4.0 diff --git a/tox.ini b/tox.ini index 2ddda8281beb..b5314114570b 100644 --- a/tox.ini +++ b/tox.ini @@ -43,6 +43,7 @@ commands = [testenv:lint] description = check the code style skip_install = true +deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure [testenv:type] From 310b914b6046830aafb0468f65464d75362991ab Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 28 Jun 2023 11:17:39 +0100 Subject: [PATCH 0071/1617] Add missing docs for error codes (#15539) I am adding these mostly to get rid of the allowlist in `html_builder.py`, please feel free to tweak these docs, cc @JukkaL @hauntsaninja @JelleZijlstra --- docs/source/error_code_list.rst | 97 ++++++++++++++++++++++++++++++++ docs/source/error_code_list2.rst | 26 +++++++++ docs/source/html_builder.py | 14 +---- mypy/errorcodes.py | 2 +- 4 files changed, 125 insertions(+), 14 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 1654c5910f98..f935e025e589 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -323,6 +323,38 @@ Example: else: raise ValueError('not defined for zero') +.. _code-empty-body: + +Check that functions don't have empty bodies outside stubs [empty-body] +----------------------------------------------------------------------- + +This error code is similar to the ``[return]`` code but is emitted specifically +for functions and methods with empty bodies (if they are annotated with +non-trivial return type). Such a distinction exists because in some contexts +an empty body can be valid, for example for an abstract method or in a stub +file. Also old versions of mypy used to unconditionally allow functions with +empty bodies, so having a dedicated error code simplifies cross-version +compatibility. + +Note that empty bodies are allowed for methods in *protocols*, and such methods +are considered implicitly abstract: + +.. code-block:: python + + from abc import abstractmethod + from typing import Protocol + + class RegularABC: + @abstractmethod + def foo(self) -> int: + pass # OK + def bar(self) -> int: + pass # Error: Missing return statement [empty-body] + + class Proto(Protocol): + def bar(self) -> int: + pass # OK + .. _code-return-value: Check that return value is compatible [return-value] @@ -947,6 +979,28 @@ otherwise unused variable: _ = f() # No error +.. _code-top-level-await: + +Warn about top level await expressions [top-level-await] +-------------------------------------------------------- + +This error code is separate from the general ``[syntax]`` errors, because in +some environments (e.g. IPython) a top level ``await`` is allowed. In such +environments a user may want to use ``--disable-error-code=top-level-await``, +that allows to still have errors for other improper uses of ``await``, for +example: + +.. code-block:: python + + async def f() -> None: + ... + + top = await f() # Error: "await" outside function [top-level-await] + + def g() -> None: + # This is a blocker error and cannot be silenced. + await f() # Error: "await" outside coroutine ("async def") + .. _code-assert-type: Check types in assert_type [assert-type] @@ -978,6 +1032,27 @@ Functions will always evaluate to true in boolean contexts. if f: # Error: Function "Callable[[], Any]" could always be true in boolean context [truthy-function] pass +.. _code-str-format: + +Check that string formatting/interpolation is type-safe [str-format] +-------------------------------------------------------------------- + +Mypy will check that f-strings, ``str.format()`` calls, and ``%`` interpolations +are valid (when corresponding template is a literal string). This includes +checking number and types of replacements, for example: + +.. code-block:: python + + # Error: Cannot find replacement for positional format specifier 1 [str-format] + "{} and {}".format("spam") + "{} and {}".format("spam", "eggs") # OK + # Error: Not all arguments converted during string formatting [str-format] + "{} and {}".format("spam", "eggs", "cheese") + + # Error: Incompatible types in string interpolation + # (expression has type "float", placeholder has type "int") [str-format] + "{:d}".format(3.14) + .. _code-str-bytes-safe: Check for implicit bytes coercions [str-bytes-safe] @@ -998,6 +1073,28 @@ Warn about cases where a bytes object may be converted to a string in an unexpec print(f"The alphabet starts with {b!r}") # The alphabet starts with b'abc' print(f"The alphabet starts with {b.decode('utf-8')}") # The alphabet starts with abc +.. _code-annotation-unchecked: + +Notify about an annotation in an unchecked function [annotation-unchecked] +-------------------------------------------------------------------------- + +Sometimes a user may accidentally omit an annotation for a function, and mypy +will not check the body of this function (unless one uses +:option:`--check-untyped-defs ` or +:option:`--disallow-untyped-defs `). To avoid +such situations go unnoticed, mypy will show a note, if there are any type +annotations in an unchecked function: + +.. code-block:: python + + def test_assignment(): # "-> None" return annotation is missing + # Note: By default the bodies of untyped functions are not checked, + # consider using --check-untyped-defs [annotation-unchecked] + x: int = "no way" + +Note that mypy will still exit with return code ``0``, since such behaviour is +specified by :pep:`484`. + .. _code-syntax: Report syntax errors [syntax] diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 11f463f93018..e1d47f7cbec0 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -255,6 +255,32 @@ mypy generates an error if it thinks that an expression is redundant. [i for i in range(x) if isinstance(i, int)] +.. _code-possibly-undefined: + +Warn about variables that are defined only in some execution paths [possibly-undefined] +--------------------------------------------------------------------------------------- + +If you use :option:`--enable-error-code possibly-undefined `, +mypy generates an error if it cannot verify that a variable will be defined in +all execution paths. This includes situations when a variable definition +appears in a loop, in a conditional branch, in an except handler, etc. For +example: + +.. code-block:: python + + # Use "mypy --enable-error-code possibly-undefined ..." + + from typing import Iterable + + def test(values: Iterable[int], flag: bool) -> None: + if flag: + a = 1 + z = a + 1 # Error: Name "a" may be undefined [possibly-undefined] + + for v in values: + b = v + z = b + 1 # Error: Name "b" may be undefined [possibly-undefined] + .. _code-truthy-bool: Check that expression is not implicitly true in boolean context [truthy-bool] diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py index 405b80ac53d2..3064833b5631 100644 --- a/docs/source/html_builder.py +++ b/docs/source/html_builder.py @@ -23,19 +23,7 @@ def write_doc(self, docname: str, doctree: document) -> None: def _verify_error_codes(self) -> None: from mypy.errorcodes import error_codes - known_missing = { - # TODO: fix these before next release - "annotation-unchecked", - "empty-body", - "possibly-undefined", - "str-format", - "top-level-await", - } - missing_error_codes = { - c - for c in error_codes - if f"code-{c}" not in self._ref_to_doc and c not in known_missing - } + missing_error_codes = {c for c in error_codes if f"code-{c}" not in self._ref_to_doc} if missing_error_codes: raise ValueError( f"Some error codes are not documented: {', '.join(sorted(missing_error_codes))}" diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index e87b04b6f473..b9448f3d5af9 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -144,7 +144,7 @@ def __hash__(self) -> int: "safe-super", "Warn about calls to abstract methods with empty/trivial bodies", "General" ) TOP_LEVEL_AWAIT: Final = ErrorCode( - "top-level-await", "Warn about top level await experessions", "General" + "top-level-await", "Warn about top level await expressions", "General" ) # These error codes aren't enabled by default. From fca4cae2537e2aabba2bc5393ef81f2d80cdb5ae Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 28 Jun 2023 21:57:11 +0100 Subject: [PATCH 0072/1617] Add option to show links to error code docs (once per code) (#15449) Fixes #7186 We can probably add some kind of redirect from mypy-lang.org, but I think RTD link is already OK. This PR will need to wait until next release, unless we want to use `/latest` in the link. --- mypy/errors.py | 78 +++++++++++++++++++++++++++++++++ mypy/main.py | 6 +++ mypy/options.py | 1 + mypy_self_check.ini | 1 + test-data/unit/check-flags.test | 15 +++++++ 5 files changed, 101 insertions(+) diff --git a/mypy/errors.py b/mypy/errors.py index 0e61f5ecf0cd..6739d30f16a4 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -20,8 +20,27 @@ # Show error codes for some note-level messages (these usually appear alone # and not as a comment for a previous error-level message). SHOW_NOTE_CODES: Final = {codes.ANNOTATION_UNCHECKED} + +# Do not add notes with links to error code docs to errors with these codes. +# We can tweak this set as we get more experience about what is helpful and what is not. +HIDE_LINK_CODES: Final = { + # This is a generic error code, so it has no useful docs + codes.MISC, + # These are trivial and have some custom notes (e.g. for list being invariant) + codes.ASSIGNMENT, + codes.ARG_TYPE, + codes.RETURN_VALUE, + # Undefined name/attribute errors are self-explanatory + codes.ATTR_DEFINED, + codes.NAME_DEFINED, + # Overrides have a custom link to docs + codes.OVERRIDE, +} + allowed_duplicates: Final = ["@overload", "Got:", "Expected:"] +BASE_RTD_URL: Final = "https://mypy.rtfd.io/en/stable/_refs.html#code" + # Keep track of the original error code when the error code of a message is changed. # This is used to give notes about out-of-date "type: ignore" comments. original_error_codes: Final = {codes.LITERAL_REQ: codes.MISC, codes.TYPE_ABSTRACT: codes.MISC} @@ -107,6 +126,7 @@ def __init__( allow_dups: bool, origin: tuple[str, Iterable[int]] | None = None, target: str | None = None, + priority: int = 0, ) -> None: self.import_ctx = import_ctx self.file = file @@ -125,6 +145,7 @@ def __init__( self.allow_dups = allow_dups self.origin = origin or (file, [line]) self.target = target + self.priority = priority # Type used internally to represent errors: @@ -530,6 +551,35 @@ def add_error_info(self, info: ErrorInfo) -> None: allow_dups=False, ) self._add_error_info(file, note) + if ( + self.options.show_error_code_links + and not self.options.hide_error_codes + and info.code is not None + and info.code not in HIDE_LINK_CODES + ): + message = f"See {BASE_RTD_URL}-{info.code.code} for more info" + if message in self.only_once_messages: + return + self.only_once_messages.add(message) + info = ErrorInfo( + import_ctx=info.import_ctx, + file=info.file, + module=info.module, + typ=info.type, + function_or_member=info.function_or_member, + line=info.line, + column=info.column, + end_line=info.end_line, + end_column=info.end_column, + severity="note", + message=message, + code=info.code, + blocker=False, + only_once=True, + allow_dups=False, + priority=20, + ) + self._add_error_info(file, info) def has_many_errors(self) -> bool: if self.options.many_errors_threshold < 0: @@ -1041,6 +1091,34 @@ def sort_messages(self, errors: list[ErrorInfo]) -> list[ErrorInfo]: # Sort the errors specific to a file according to line number and column. a = sorted(errors[i0:i], key=lambda x: (x.line, x.column)) + a = self.sort_within_context(a) + result.extend(a) + return result + + def sort_within_context(self, errors: list[ErrorInfo]) -> list[ErrorInfo]: + """For the same location decide which messages to show first/last. + + Currently, we only compare within the same error code, to decide the + order of various additional notes. + """ + result = [] + i = 0 + while i < len(errors): + i0 = i + # Find neighbouring errors with the same position and error code. + while ( + i + 1 < len(errors) + and errors[i + 1].line == errors[i].line + and errors[i + 1].column == errors[i].column + and errors[i + 1].end_line == errors[i].end_line + and errors[i + 1].end_column == errors[i].end_column + and errors[i + 1].code == errors[i].code + ): + i += 1 + i += 1 + + # Sort the messages specific to a given error by priority. + a = sorted(errors[i0:i], key=lambda x: x.priority) result.extend(a) return result diff --git a/mypy/main.py b/mypy/main.py index 22ff3e32a718..516bb1ee9b54 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -887,6 +887,12 @@ def add_invertible_flag( help="Hide error codes in error messages", group=error_group, ) + add_invertible_flag( + "--show-error-code-links", + default=False, + help="Show links to error code documentation", + group=error_group, + ) add_invertible_flag( "--pretty", default=False, diff --git a/mypy/options.py b/mypy/options.py index e1d731c1124c..daa666dc7638 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -313,6 +313,7 @@ def __init__(self) -> None: self.show_column_numbers: bool = False self.show_error_end: bool = False self.hide_error_codes = False + self.show_error_code_links = False # Use soft word wrap and show trimmed source snippets with error location markers. self.pretty = False self.dump_graph = False diff --git a/mypy_self_check.ini b/mypy_self_check.ini index d20fcd60a9cb..62083d144621 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -9,6 +9,7 @@ plugins = misc/proper_plugin.py python_version = 3.7 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr +show_error_code_links = True [mypy-mypy.visitor] # See docstring for NodeVisitor for motivation. diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 6ec0849146c0..c356028f6620 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2195,3 +2195,18 @@ cb(lambda x: a) # OK fn = lambda x: a cb(fn) + +[case testShowErrorCodeLinks] +# flags: --show-error-codes --show-error-code-links + +x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +list(1) # E: No overload variant of "list" matches argument type "int" [call-overload] \ + # N: Possible overload variants: \ + # N: def [T] __init__(self) -> List[T] \ + # N: def [T] __init__(self, x: Iterable[T]) -> List[T] \ + # N: See https://mypy.rtfd.io/en/stable/_refs.html#code-call-overload for more info +list(2) # E: No overload variant of "list" matches argument type "int" [call-overload] \ + # N: Possible overload variants: \ + # N: def [T] __init__(self) -> List[T] \ + # N: def [T] __init__(self, x: Iterable[T]) -> List[T] +[builtins fixtures/list.pyi] From 9a4a5aa77a303c2e56e9dc9bd9327974bf777894 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Wed, 28 Jun 2023 18:25:43 -0400 Subject: [PATCH 0073/1617] Process NamedTuple decorators in semantic analyzer (#15513) --- mypy/semanal.py | 4 ++++ mypyc/test-data/run-tuples.test | 6 ++++++ test-data/unit/semanal-namedtuple.test | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index d18cc4298fed..1eb14e499e4d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1744,6 +1744,10 @@ def analyze_namedtuple_classdef( self.setup_type_vars(defn, tvar_defs) self.setup_alias_type_vars(defn) with self.scope.class_scope(defn.info): + for deco in defn.decorators: + deco.accept(self) + if isinstance(deco, RefExpr) and deco.fullname in FINAL_DECORATOR_NAMES: + info.is_final = True with self.named_tuple_analyzer.save_namedtuple_body(info): self.analyze_class_body_common(defn) return True diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index f6c92b9c720f..0851c15e57fd 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -98,6 +98,7 @@ assert f(Sub(3, 2)) == 3 -- Ref: https://github.com/mypyc/mypyc/issues/924 [case testNamedTupleClassSyntax] from typing import Dict, List, NamedTuple, Optional, Tuple, Union +from typing_extensions import final class FuncIR: pass @@ -121,6 +122,11 @@ class Record(NamedTuple): # Ref: https://github.com/mypyc/mypyc/issues/938 class ClassIR: pass +# Ref: https://github.com/mypyc/mypyc/issues/927 +@final +class Inextensible(NamedTuple): + x: int + [file driver.py] from typing import ForwardRef, Optional from native import ClassIR, FuncIR, Record diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test index df1d5679c892..f396f799028f 100644 --- a/test-data/unit/semanal-namedtuple.test +++ b/test-data/unit/semanal-namedtuple.test @@ -225,3 +225,23 @@ class B(A): pass [out] main:2: error: Unsupported dynamic base class "NamedTuple" main:2: error: Name "NamedTuple" is not defined + +[case testNamedTupleWithDecorator] +from typing import final, NamedTuple + +@final +class A(NamedTuple("N", [("x", int)])): + pass +[builtins fixtures/tuple.pyi] +[out] +MypyFile:1( + ImportFrom:1(typing, [final, NamedTuple]) + ClassDef:4( + A + TupleType( + Tuple[builtins.int, fallback=__main__.N@4]) + Decorators( + NameExpr(final [typing.final])) + BaseType( + __main__.N@4) + PassStmt:5())) From 05c81d69b724afd20f501b6427aa3f740083a916 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 28 Jun 2023 17:53:59 -0700 Subject: [PATCH 0074/1617] Fix tests on 3.7 (#15543) --- mypyc/irbuild/classdef.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 59b1c05a0ddb..fcb2afba0939 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -498,6 +498,9 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: if builder.options.capi_version < (3, 8): # TypedDict was added to typing in Python 3.8. module = "typing_extensions" + # It needs to be "_TypedDict" on typing_extensions 4.7.0+ + # and "TypedDict" otherwise. + name = "_TypedDict" else: # In Python 3.9 TypedDict is not a real type. name = "_TypedDict" From 66d03daf35bde2dcc8fdc746aa31b2f59dcba515 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:40:24 -0700 Subject: [PATCH 0075/1617] Fix find occurrences flag (#15528) --- mypy/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 516bb1ee9b54..9d95bb6cb1f6 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1349,12 +1349,12 @@ def set_strict_flags() -> None: # Set build flags. if special_opts.find_occurrences: - state.find_occurrences = special_opts.find_occurrences.split(".") - assert state.find_occurrences is not None - if len(state.find_occurrences) < 2: + _find_occurrences = tuple(special_opts.find_occurrences.split(".")) + if len(_find_occurrences) < 2: parser.error("Can only find occurrences of class members.") - if len(state.find_occurrences) != 2: + if len(_find_occurrences) != 2: parser.error("Can only find occurrences of non-nested class members.") + state.find_occurrences = _find_occurrences # type: ignore[assignment] # Set reports. for flag, val in vars(special_opts).items(): From 06282b55613a5c36c29b8ba1aafb067ac475e047 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 29 Jun 2023 19:41:20 +0100 Subject: [PATCH 0076/1617] Consolidate config in pyproject.toml (#15548) There's no real need for us to have separate `setup.cfg` and `pytest.ini` files these days --- pyproject.toml | 44 ++++++++++++++++++++++++++++++++++++++++++++ pytest.ini | 27 --------------------------- setup.cfg | 16 ---------------- 3 files changed, 44 insertions(+), 43 deletions(-) delete mode 100644 pytest.ini delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 22e28f8f6f68..0b14dd419d08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,3 +79,47 @@ extra-standard-library = ["typing_extensions"] [tool.check-manifest] ignore = ["**/.readthedocs.yaml"] + +[tool.pytest.ini_options] +minversion = "6.0.0" +testpaths = ["mypy/test", "mypyc/test"] +python_files = 'test*.py' + +# Where do the test cases come from? We provide our own collection +# logic by implementing `pytest_pycollect_makeitem` in mypy.test.data; +# the test files import that module, and pytest sees the magic name +# and invokes it at the relevant moment. See +# https://doc.pytest.org/en/latest/how-to/writing_plugins.html#collection-hooks + +# Both our plugin and unittest provide their own collection logic, +# So we can disable the default python collector by giving it empty +# patterns to search for. +# Note that unittest requires that no "Test*" classes exist. +python_classes = [] +python_functions = [] + +# always run in parallel (requires pytest-xdist, see test-requirements.txt) +# and enable strict mode: require all markers +# to be defined and raise on invalid config values +addopts = "-nauto --strict-markers --strict-config" + +# treat xpasses as test failures so they get converted to regular tests as soon as possible +xfail_strict = true + +[tool.coverage.run] +branch = true +source = "mypy" +parallel = true + +[tool.coverage.report] +show_missing = true +skip_covered = true +omit = 'mypy/test/*' +exclude_lines = [ + '\#\s*pragma: no cover', + '^\s*raise AssertionError\b', + '^\s*raise NotImplementedError\b', + '^\s*return NotImplemented\b', + '^\s*raise$', + '''^if __name__ == ['"]__main__['"]:$''', +] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index a123b0f11328..000000000000 --- a/pytest.ini +++ /dev/null @@ -1,27 +0,0 @@ -[pytest] -minversion = 6.0.0 - -testpaths = mypy/test mypyc/test - -python_files = test*.py - -# Where do the test cases come from? We provide our own collection -# logic by implementing `pytest_pycollect_makeitem` in mypy.test.data; -# the test files import that module, and pytest sees the magic name -# and invokes it at the relevant moment. See -# https://doc.pytest.org/en/latest/how-to/writing_plugins.html#collection-hooks - -# Both our plugin and unittest provide their own collection logic, -# So we can disable the default python collector by giving it empty -# patterns to search for. -# Note that unittest requires that no "Test*" classes exist. -python_classes = -python_functions = - -# always run in parallel (requires pytest-xdist, see test-requirements.txt) -# and enable strict mode: require all markers -# to be defined and raise on invalid config values -addopts = -nauto --strict-markers --strict-config - -# treat xpasses as test failures so they get converted to regular tests as soon as possible -xfail_strict = true diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 04d75ac8d19f..000000000000 --- a/setup.cfg +++ /dev/null @@ -1,16 +0,0 @@ -[coverage:run] -branch = true -source = mypy -parallel = true - -[coverage:report] -show_missing = true -skip_covered = True -omit = mypy/test/* -exclude_lines = - \#\s*pragma: no cover - ^\s*raise AssertionError\b - ^\s*raise NotImplementedError\b - ^\s*return NotImplemented\b - ^\s*raise$ - ^if __name__ == ['"]__main__['"]:$ From 95dde9d8dc9007b28881eee10231bdd8ae0c9d43 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:43:50 -0700 Subject: [PATCH 0077/1617] Switch pre-commit to using mypyc compiled Black wheels (#15544) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ad273ce5584..a56e1af938b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer - - repo: https://github.com/psf/black + - repo: https://github.com/hauntsaninja/black-pre-commit-mirror rev: 23.3.0 # must match test-requirements.txt hooks: - id: black From b995e1620789a3ab2fc5dbcf0698a9077b0f5731 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 29 Jun 2023 23:34:15 +0100 Subject: [PATCH 0078/1617] Rebind self-types in subclass methods without Self annotation (#15541) Fixes #15529 The fix is straightforward, hopefully there will be no fallout. (Note that #14075 would also fix this, but I am still not sure we should do that) --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/checkmember.py | 14 ++++++++++++-- mypy/plugins/dataclasses.py | 3 ++- mypy/semanal.py | 2 ++ test-data/unit/check-selftype.test | 17 +++++++++++++++++ test-data/unit/pythoneval.test | 17 +++++++++++++++++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 7af61a532b7b..36fea9daa3e3 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -735,12 +735,12 @@ def analyze_var( """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. - - itype is the class object in which var is defined + itype is the instance type in which attribute should be looked up original_type is the type of E in the expression E.var if implicit is True, the original Var was created as an assignment to self """ # Found a member variable. + original_itype = itype itype = map_instance_to_supertype(itype, var.info) typ = var.type if typ: @@ -756,6 +756,16 @@ def analyze_var( get_proper_type(mx.original_type) ): t = expand_self_type(var, t, mx.original_type) + elif ( + mx.is_self + and original_itype.type != var.info + # If an attribute with Self-type was defined in a supertype, we need to + # rebind the Self type variable to Self type variable of current class... + and original_itype.type.self_type is not None + # ...unless `self` has an explicit non-trivial annotation. + and original_itype == mx.chk.scope.active_self_type() + ): + t = expand_self_type(var, t, original_itype.type.self_type) t = get_proper_type(expand_type_by_instance(t, itype)) freeze_all_type_vars(t) result: Type = t diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index bb3009dddf10..9e054493828f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -355,7 +355,8 @@ def transform(self) -> bool: self._add_dataclass_fields_magic_attribute() if self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES: - self._add_internal_replace_method(attributes) + with state.strict_optional_set(self._api.options.strict_optional): + self._add_internal_replace_method(attributes) if "__post_init__" in info.names: self._add_internal_post_init_method(attributes) diff --git a/mypy/semanal.py b/mypy/semanal.py index 1eb14e499e4d..25393096bc5f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1692,6 +1692,8 @@ def is_core_builtin_class(self, defn: ClassDef) -> bool: def analyze_class_body_common(self, defn: ClassDef) -> None: """Parts of class body analysis that are common to all kinds of class defs.""" self.enter_class(defn.info) + if any(b.self_type is not None for b in defn.info.mro): + self.setup_self_type() defn.defs.accept(self) self.apply_class_plugin_hooks(defn) self.leave_class() diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 53c24584cb73..96d5b2306427 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1867,3 +1867,20 @@ class B: return B() # E: Incompatible return value type (got "B", expected "A") [builtins fixtures/isinstancelist.pyi] + +[case testAttributeOnSelfAttributeInSubclass] +from typing import List, Self + +class A: + x: Self + xs: List[Self] + +class B(A): + extra: int + + def meth(self) -> None: + reveal_type(self.x) # N: Revealed type is "Self`0" + reveal_type(self.xs[0]) # N: Revealed type is "Self`0" + reveal_type(self.x.extra) # N: Revealed type is "builtins.int" + reveal_type(self.xs[0].extra) # N: Revealed type is "builtins.int" +[builtins fixtures/list.pyi] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index b43dcbd7088f..1460002e1b65 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2092,3 +2092,20 @@ def fst(kv: Tuple[K, V]) -> K: pairs = [(len(s), s) for s in ["one", "two", "three"]] grouped = groupby(pairs, key=fst) [out] + +[case testDataclassReplaceOptional] +# flags: --strict-optional +from dataclasses import dataclass, replace +from typing import Optional + +@dataclass +class A: + x: Optional[int] + +a = A(x=42) +reveal_type(a) +a2 = replace(a, x=None) # OK +reveal_type(a2) +[out] +_testDataclassReplaceOptional.py:10: note: Revealed type is "_testDataclassReplaceOptional.A" +_testDataclassReplaceOptional.py:12: note: Revealed type is "_testDataclassReplaceOptional.A" From 21beadc0aef5149111ae88504ec1514b943d4f78 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 30 Jun 2023 16:41:30 +0100 Subject: [PATCH 0079/1617] Enforce typing_extensions >=4.7.0 on py37 (#15556) The changes made in #15543 mean that mypy's tests will no longer pass if you've got `typing_extensions<4.7` installed and you're running on Python 3.7. --- mypy-requirements.txt | 3 ++- mypyc/irbuild/classdef.py | 10 ++++++++-- pyproject.toml | 3 ++- setup.py | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 7043765f09f4..ba6f069661ad 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -1,5 +1,6 @@ # NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml -typing_extensions>=4.1.0 +typing_extensions>=4.1.0; python_version >= '3.8' +typing_extensions>=4.7.0; python_version < '3.8' mypy_extensions>=1.0.0 typed_ast>=1.4.0,<2; python_version<'3.8' tomli>=1.1.0; python_version<'3.11' diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index fcb2afba0939..ef8db97c818e 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -2,6 +2,7 @@ from __future__ import annotations +import typing_extensions from abc import abstractmethod from typing import Callable from typing_extensions import Final @@ -498,9 +499,14 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: if builder.options.capi_version < (3, 8): # TypedDict was added to typing in Python 3.8. module = "typing_extensions" - # It needs to be "_TypedDict" on typing_extensions 4.7.0+ - # and "TypedDict" otherwise. + # TypedDict is not a real type on typing_extensions 4.7.0+ name = "_TypedDict" + if isinstance(typing_extensions.TypedDict, type): + raise RuntimeError( + "It looks like you may have an old version " + "of typing_extensions installed. " + "typing_extensions>=4.7.0 is required on Python 3.7." + ) else: # In Python 3.9 TypedDict is not a real type. name = "_TypedDict" diff --git a/pyproject.toml b/pyproject.toml index 0b14dd419d08..96a05d545946 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,8 @@ requires = [ "setuptools >= 40.6.2", "wheel >= 0.30.0", # the following is from mypy-requirements.txt - "typing_extensions>=4.1.0", + "typing_extensions>=4.1.0; python_version >= '3.8'", + "typing_extensions>=4.7.0; python_version < '3.8'", "mypy_extensions>=1.0.0", "typed_ast>=1.4.0,<2; python_version<'3.8'", "tomli>=1.1.0; python_version<'3.11'", diff --git a/setup.py b/setup.py index 81494a5566e8..85d412540013 100644 --- a/setup.py +++ b/setup.py @@ -222,7 +222,8 @@ def run(self): # When changing this, also update mypy-requirements.txt. install_requires=[ "typed_ast >= 1.4.0, < 2; python_version<'3.8'", - "typing_extensions>=4.1.0", + "typing_extensions>=4.1.0; python_version >= '3.8'", + "typing_extensions>=4.7.0; python_version < '3.8'", "mypy_extensions >= 1.0.0", "tomli>=1.1.0; python_version<'3.11'", ], From 92602c523975495af544bd493d0bd59d15334440 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Fri, 30 Jun 2023 11:47:00 -0400 Subject: [PATCH 0080/1617] [mypyc] Document more unsupported features & update supported features (#15524) Towards mypyc/mypyc#978. --------- Co-authored-by: Jukka Lehtosalo Co-authored-by: Alex Waygood --- mypyc/doc/differences_from_python.rst | 38 ++++++++++++++++++--------- mypyc/doc/native_classes.rst | 12 +++++++-- mypyc/doc/using_type_annotations.rst | 4 +-- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst index 16faae60303f..f1d4d05a3a87 100644 --- a/mypyc/doc/differences_from_python.rst +++ b/mypyc/doc/differences_from_python.rst @@ -268,19 +268,27 @@ used in compiled code, or there are some limitations. You can partially work around some of these limitations by running your code in interpreted mode. -Operator overloading -******************** +Nested classes +************** -Native classes can only use these dunder methods to override operators: +Nested classes are not supported. -* ``__eq__`` -* ``__ne__`` -* ``__getitem__`` -* ``__setitem__`` +Conditional functions or classes +******************************** -.. note:: +Function and class definitions guarded by an if-statement are not supported. + +Dunder methods +************** - This limitation will be lifted in the future. +Native classes **cannot** use these dunders. If defined, they will not +work as expected. + +* ``__del__`` +* ``__index__`` +* ``__getattr__``, ``__getattribute__`` +* ``__setattr__`` +* ``__delattr__`` Generator expressions ********************* @@ -299,10 +307,16 @@ Descriptors Native classes can't contain arbitrary descriptors. Properties, static methods and class methods are supported. -Stack introspection -******************* +Introspection +************* + +Various methods of introspection may break by using mypyc. Here's an +non-exhaustive list of what won't work: -Frames of compiled functions can't be inspected using ``inspect``. +- Instance ``__annotations__`` is usually not kept +- Frames of compiled functions can't be inspected using ``inspect`` +- Compiled methods aren't considered methods by ``inspect.ismethod`` +- ``inspect.signature`` chokes on compiled functions Profiling hooks and tracing *************************** diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst index 2b4a0892b790..b2935a6f7185 100644 --- a/mypyc/doc/native_classes.rst +++ b/mypyc/doc/native_classes.rst @@ -63,6 +63,8 @@ classes: * ``IndexError`` * ``LookupError`` * ``UserWarning`` +* ``typing.NamedTuple`` +* ``enum.Enum`` By default, a non-native class can't inherit a native class, and you can't inherit from a native class outside the compilation unit that @@ -104,6 +106,11 @@ through an instance. Example:: print(o.cv) # OK (2) o.cv = 3 # Error! +.. tip:: + + Constant class variables can be declared using ``typing.Final`` or + ``typing.Final[]``. + Generic native classes ---------------------- @@ -150,9 +157,10 @@ decorators can be used with native classes, however: * ``mypy_extensions.trait`` (for defining :ref:`trait types `) * ``mypy_extensions.mypyc_attr`` (see :ref:`above `) * ``dataclasses.dataclass`` +* ``@attr.s(auto_attribs=True)`` -Dataclasses have partial native support, and they aren't as efficient -as pure native classes. +Dataclasses and attrs classes have partial native support, and they aren't as +efficient as pure native classes. .. note:: diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index f095a6717271..5bfff388e433 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -194,8 +194,8 @@ Traits have some special properties: * You shouldn't create instances of traits (though mypyc does not prevent it yet). -* Traits can subclass other traits, but they can't subclass non-trait - classes (other than ``object``). +* Traits can subclass other traits or native classes, but the MRO must be + linear (just like with native classes). * Accessing methods or attributes through a trait type is somewhat less efficient than through a native class type, but this is much From 3730899fce2917e312511dc68d813e5dc42d5685 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:35:52 +0100 Subject: [PATCH 0081/1617] Define mypy_extensions.i16 in stubs (#15562) I forgot to add this when I added mypyc support for the i16 native integer type. The mypy_extensions stubs are maintained here instead of typeshed. This is a copy of the stubs for `i32`, with references to `i32` replaced with `i16`. --- .../stubs/mypy-extensions/mypy_extensions.pyi | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi index 40e24645fb77..86a071500b34 100644 --- a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi +++ b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi @@ -146,3 +146,38 @@ class i32: def __ge__(self, x: i32) -> bool: ... def __gt__(self, x: i32) -> bool: ... def __index__(self) -> int: ... + +class i16: + @overload + def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> i16: ... + @overload + def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> i16: ... + + def __add__(self, x: i16) -> i16: ... + def __radd__(self, x: i16) -> i16: ... + def __sub__(self, x: i16) -> i16: ... + def __rsub__(self, x: i16) -> i16: ... + def __mul__(self, x: i16) -> i16: ... + def __rmul__(self, x: i16) -> i16: ... + def __floordiv__(self, x: i16) -> i16: ... + def __rfloordiv__(self, x: i16) -> i16: ... + def __mod__(self, x: i16) -> i16: ... + def __rmod__(self, x: i16) -> i16: ... + def __and__(self, x: i16) -> i16: ... + def __rand__(self, x: i16) -> i16: ... + def __or__(self, x: i16) -> i16: ... + def __ror__(self, x: i16) -> i16: ... + def __xor__(self, x: i16) -> i16: ... + def __rxor__(self, x: i16) -> i16: ... + def __lshift__(self, x: i16) -> i16: ... + def __rlshift__(self, x: i16) -> i16: ... + def __rshift__(self, x: i16) -> i16: ... + def __rrshift__(self, x: i16) -> i16: ... + def __neg__(self) -> i16: ... + def __invert__(self) -> i16: ... + def __pos__(self) -> i16: ... + def __lt__(self, x: i16) -> bool: ... + def __le__(self, x: i16) -> bool: ... + def __ge__(self, x: i16) -> bool: ... + def __gt__(self, x: i16) -> bool: ... + def __index__(self) -> int: ... From 19c5d5f7848a94c810c8d840a42e29ffe3f8906f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 1 Jul 2023 17:36:42 +0100 Subject: [PATCH 0082/1617] [mypyc] Improve failure reporting in default run test driver (#15563) Fix stdout flushing to avoid empty lines getting out of alignment with the rest of the output. Display the name of a failed test function, since it's sometimes not visible in the traceback (at least if something incorrectly propagates exceptions). --- mypyc/test-data/driver/driver.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mypyc/test-data/driver/driver.py b/mypyc/test-data/driver/driver.py index 6717f402f72d..c9d179224a30 100644 --- a/mypyc/test-data/driver/driver.py +++ b/mypyc/test-data/driver/driver.py @@ -18,7 +18,7 @@ try: test_func() except Exception as e: - failures.append(sys.exc_info()) + failures.append((name, sys.exc_info())) if failures: from traceback import print_exception, format_tb @@ -32,12 +32,17 @@ def extract_line(tb): return m.group(1) # Sort failures by line number of test function. - failures = sorted(failures, key=lambda e: extract_line(e[2])) + failures = sorted(failures, key=lambda e: extract_line(e[1][2])) # If there are multiple failures, print stack traces of all but the final failure. - for e in failures[:-1]: + for name, e in failures[:-1]: + print(f'<< {name} >>') + sys.stdout.flush() print_exception(*e) print() + sys.stdout.flush() # Raise exception for the last failure. Test runner will show the traceback. - raise failures[-1][1] + print(f'<< {failures[-1][0]} >>') + sys.stdout.flush() + raise failures[-1][1][1] From 2edaf35ec1f91c139dfe1930b1b0d1e1cac59599 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 2 Jul 2023 18:59:35 -0700 Subject: [PATCH 0083/1617] Drop support for running with Python 3.7 (#15566) --- .github/workflows/docs.yml | 2 +- .github/workflows/test.yml | 49 ++-- build-requirements.txt | 1 - docs/source/additional_features.rst | 7 +- docs/source/getting_started.rst | 2 +- misc/build-debug-python.sh | 2 +- mypy-requirements.txt | 4 +- mypy/defaults.py | 2 +- mypy/dmypy_server.py | 2 - mypy/fastparse.py | 271 +++++------------- mypy/messages.py | 2 + mypy/pyinfo.py | 8 +- mypy/test/meta/test_update_data.py | 5 +- mypy/test/testcheck.py | 4 +- mypy/test/testdaemon.py | 4 - mypy/test/testfinegrained.py | 5 +- mypy/test/testpep561.py | 5 - mypy/test/teststubgen.py | 1 + mypy/test/teststubtest.py | 73 +++-- mypy/util.py | 6 +- mypy_self_check.ini | 2 +- mypyc/test/test_run.py | 6 +- pyproject.toml | 5 +- setup.py | 13 +- test-data/unit/check-columns.test | 4 - test-data/unit/check-incremental.test | 4 +- .../unit/check-parameter-specification.test | 7 - test-data/unit/check-typeguard.test | 4 - test-data/unit/cmdline.test | 21 -- test-data/unit/daemon.test | 8 +- test-data/unit/fine-grained-blockers.test | 10 - .../unit/fine-grained-cache-incremental.test | 6 +- test-data/unit/fine-grained.test | 22 -- test-data/unit/pythoneval.test | 2 +- test-data/unit/semanal-errors.test | 38 --- test-data/unit/semanal-statements.test | 2 - 36 files changed, 163 insertions(+), 446 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3e06b5e909f1..5dc86a1159f4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.7' + python-version: '3.8' - name: Install tox run: pip install --upgrade 'setuptools!=50' tox==4.4.4 - name: Setup tox environment diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5bd20f2773c5..05e52ad95a17 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,37 +26,27 @@ jobs: fail-fast: false matrix: include: - - name: Test suite with py37-windows-64 - python: '3.7' - arch: x64 - os: windows-latest - toxenv: py37 - - name: Test suite with py38-ubuntu + # Make sure to run mypyc compiled unit tests for both + # the oldest and newest supported Python versions + - name: Test suite with py38-ubuntu, mypyc-compiled python: '3.8' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2" - - name: Test suite with py39-ubuntu - python: '3.9' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 2" - - name: Test suite with py37-ubuntu, mypyc-compiled - python: '3.7' + test_mypyc: true + - name: Test suite with py38-windows-64 + python: '3.8' arch: x64 - os: ubuntu-latest - toxenv: py + os: windows-latest + toxenv: py38 tox_extra_args: "-n 2" - test_mypyc: true - - name: Test suite with py310-ubuntu, mypyc-compiled - python: '3.10' + - name: Test suite with py39-ubuntu + python: '3.9' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2" - test_mypyc: true - name: Test suite with py310-ubuntu python: '3.10' arch: x64 @@ -70,29 +60,32 @@ jobs: toxenv: py tox_extra_args: "-n 2" test_mypyc: true - - name: mypyc runtime tests with py37-macos - python: '3.7' + + - name: mypyc runtime tests with py38-macos + python: '3.8.17' arch: x64 os: macos-latest toxenv: py tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" - - name: mypyc runtime tests with py37-debug-build-ubuntu - python: '3.7.13' + - name: mypyc runtime tests with py38-debug-build-ubuntu + python: '3.8.17' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" debug_build: true - - name: Type check our own code (py37-ubuntu) - python: '3.7' + + - name: Type check our own code (py38-ubuntu) + python: '3.8' arch: x64 os: ubuntu-latest toxenv: type - - name: Type check our own code (py37-windows-64) - python: '3.7' + - name: Type check our own code (py38-windows-64) + python: '3.8' arch: x64 os: windows-latest toxenv: type + # We also run these checks with pre-commit in CI, # but it's useful to run them with tox too, # to ensure the tox env works as expected diff --git a/build-requirements.txt b/build-requirements.txt index 0b1e6d43103a..aac1b95eddf7 100644 --- a/build-requirements.txt +++ b/build-requirements.txt @@ -2,4 +2,3 @@ -r mypy-requirements.txt types-psutil types-setuptools -types-typed-ast>=1.5.8.5,<1.6.0 diff --git a/docs/source/additional_features.rst b/docs/source/additional_features.rst index 10122e9b2fa9..5dd136476eaa 100644 --- a/docs/source/additional_features.rst +++ b/docs/source/additional_features.rst @@ -9,10 +9,9 @@ of the previous sections. Dataclasses *********** -In Python 3.7, a new :py:mod:`dataclasses` module has been added to the standard library. -This module allows defining and customizing simple boilerplate-free classes. -They can be defined using the :py:func:`@dataclasses.dataclass -` decorator: +The :py:mod:`dataclasses` module allows defining and customizing simple +boilerplate-free classes. They can be defined using the +:py:func:`@dataclasses.dataclass ` decorator: .. code-block:: python diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 9b927097cfd2..11f915005695 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -16,7 +16,7 @@ may not make much sense otherwise. Installing and running mypy *************************** -Mypy requires Python 3.7 or later to run. You can install mypy using pip: +Mypy requires Python 3.8 or later to run. You can install mypy using pip: .. code-block:: shell diff --git a/misc/build-debug-python.sh b/misc/build-debug-python.sh index f652d6ad9937..8dd1bff4c9ed 100755 --- a/misc/build-debug-python.sh +++ b/misc/build-debug-python.sh @@ -26,7 +26,7 @@ fi curl -O https://www.python.org/ftp/python/$VERSION/Python-$VERSION.tgz tar zxf Python-$VERSION.tgz cd Python-$VERSION -CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" ./configure CFLAGS="-DPy_DEBUG -DPy_TRACE_REFS -DPYMALLOC_DEBUG" --with-pydebug --prefix=$PREFIX +CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" ./configure CFLAGS="-DPy_DEBUG -DPy_TRACE_REFS -DPYMALLOC_DEBUG" --with-pydebug --prefix=$PREFIX --with-trace-refs make -j4 make install $PREFIX/bin/python3 -m pip install virtualenv diff --git a/mypy-requirements.txt b/mypy-requirements.txt index ba6f069661ad..f81412be761e 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -1,6 +1,4 @@ # NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml -typing_extensions>=4.1.0; python_version >= '3.8' -typing_extensions>=4.7.0; python_version < '3.8' +typing_extensions>=4.1.0 mypy_extensions>=1.0.0 -typed_ast>=1.4.0,<2; python_version<'3.8' tomli>=1.1.0; python_version<'3.11' diff --git a/mypy/defaults.py b/mypy/defaults.py index d167997464f4..59a99cd58888 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -8,7 +8,7 @@ # Earliest fully supported Python 3.x version. Used as the default Python # version in tests. Mypy wheels should be built starting with this version, # and CI tests should be run on this version (and later versions). -PYTHON3_VERSION: Final = (3, 7) +PYTHON3_VERSION: Final = (3, 8) # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 54544f4c01ce..2427f83c5767 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -896,8 +896,6 @@ def cmd_inspect( force_reload: bool = False, ) -> dict[str, object]: """Locate and inspect expression(s).""" - if sys.version_info < (3, 8): - return {"error": 'Python 3.8 required for "inspect" command'} if not self.fine_grained_manager: return { "error": 'Command "inspect" is only valid after a "check" command' diff --git a/mypy/fastparse.py b/mypy/fastparse.py index fe59ff48bdfc..3f4e2f4c927d 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -118,123 +118,57 @@ ) from mypy.util import bytes_to_human_readable_repr, unnamed_function -try: - # pull this into a final variable to make mypyc be quiet about the - # the default argument warning - PY_MINOR_VERSION: Final = sys.version_info[1] - - # Check if we can use the stdlib ast module instead of typed_ast. - if sys.version_info >= (3, 8): - import ast as ast3 - - assert ( - "kind" in ast3.Constant._fields - ), f"This 3.8.0 alpha ({sys.version.split()[0]}) is too old; 3.8.0a3 required" - # TODO: Num, Str, Bytes, NameConstant, Ellipsis are deprecated in 3.8. - # TODO: Index, ExtSlice are deprecated in 3.9. - from ast import ( - AST, - Attribute, - Bytes, - Call, - Ellipsis as ast3_Ellipsis, - Expression as ast3_Expression, - FunctionType, - Index, - Name, - NameConstant, - Num, - Starred, - Str, - UnaryOp, - USub, - ) +# pull this into a final variable to make mypyc be quiet about the +# the default argument warning +PY_MINOR_VERSION: Final = sys.version_info[1] - def ast3_parse( - source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION - ) -> AST: - return ast3.parse( - source, - filename, - mode, - type_comments=True, # This works the magic - feature_version=feature_version, - ) +import ast as ast3 - NamedExpr = ast3.NamedExpr - Constant = ast3.Constant - else: - from typed_ast import ast3 - from typed_ast.ast3 import ( - AST, - Attribute, - Bytes, - Call, - Ellipsis as ast3_Ellipsis, - Expression as ast3_Expression, - FunctionType, - Index, - Name, - NameConstant, - Num, - Starred, - Str, - UnaryOp, - USub, - ) +# TODO: Index, ExtSlice are deprecated in 3.9. +from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UnaryOp, USub - def ast3_parse( - source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION - ) -> AST: - return ast3.parse(source, filename, mode, feature_version=feature_version) - - # These don't exist before 3.8 - NamedExpr = Any - Constant = Any - - if sys.version_info >= (3, 10): - Match = ast3.Match - MatchValue = ast3.MatchValue - MatchSingleton = ast3.MatchSingleton - MatchSequence = ast3.MatchSequence - MatchStar = ast3.MatchStar - MatchMapping = ast3.MatchMapping - MatchClass = ast3.MatchClass - MatchAs = ast3.MatchAs - MatchOr = ast3.MatchOr - AstNode = Union[ast3.expr, ast3.stmt, ast3.pattern, ast3.ExceptHandler] - else: - Match = Any - MatchValue = Any - MatchSingleton = Any - MatchSequence = Any - MatchStar = Any - MatchMapping = Any - MatchClass = Any - MatchAs = Any - MatchOr = Any - AstNode = Union[ast3.expr, ast3.stmt, ast3.ExceptHandler] - if sys.version_info >= (3, 11): - TryStar = ast3.TryStar - else: - TryStar = Any -except ImportError: - try: - from typed_ast import ast35 # type: ignore[attr-defined] # noqa: F401 - except ImportError: - print( - "The typed_ast package is not installed.\n" - "You can install it with `python3 -m pip install typed-ast`.", - file=sys.stderr, - ) - else: - print( - "You need a more recent version of the typed_ast package.\n" - "You can update to the latest version with " - "`python3 -m pip install -U typed-ast`.", - file=sys.stderr, - ) - sys.exit(1) + +def ast3_parse( + source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION +) -> AST: + return ast3.parse( + source, + filename, + mode, + type_comments=True, # This works the magic + feature_version=feature_version, + ) + + +NamedExpr = ast3.NamedExpr +Constant = ast3.Constant + +if sys.version_info >= (3, 10): + Match = ast3.Match + MatchValue = ast3.MatchValue + MatchSingleton = ast3.MatchSingleton + MatchSequence = ast3.MatchSequence + MatchStar = ast3.MatchStar + MatchMapping = ast3.MatchMapping + MatchClass = ast3.MatchClass + MatchAs = ast3.MatchAs + MatchOr = ast3.MatchOr + AstNode = Union[ast3.expr, ast3.stmt, ast3.pattern, ast3.ExceptHandler] +else: + Match = Any + MatchValue = Any + MatchSingleton = Any + MatchSequence = Any + MatchStar = Any + MatchMapping = Any + MatchClass = Any + MatchAs = Any + MatchOr = Any + AstNode = Union[ast3.expr, ast3.stmt, ast3.ExceptHandler] +if sys.version_info >= (3, 11): + TryStar = ast3.TryStar +else: + TryStar = Any N = TypeVar("N", bound=Node) @@ -370,7 +304,7 @@ def parse_type_comment( raise SyntaxError else: ignored = None - assert isinstance(typ, ast3_Expression) + assert isinstance(typ, ast3.Expression) converted = TypeConverter( errors, line=line, override_column=column, is_evaluated=False ).visit(typ.body) @@ -961,8 +895,10 @@ def do_func_def( func_type_ast = ast3_parse(n.type_comment, "", "func_type") assert isinstance(func_type_ast, FunctionType) # for ellipsis arg - if len(func_type_ast.argtypes) == 1 and isinstance( - func_type_ast.argtypes[0], ast3_Ellipsis + if ( + len(func_type_ast.argtypes) == 1 + and isinstance(func_type_ast.argtypes[0], Constant) + and func_type_ast.argtypes[0].value is Ellipsis ): if n.returns: # PEP 484 disallows both type annotations and type comments @@ -1052,15 +988,9 @@ def do_func_def( func_type.line = lineno if n.decorator_list: - if sys.version_info < (3, 8): - # Before 3.8, [typed_]ast the line number points to the first decorator. - # In 3.8, it points to the 'def' line, where we want it. - deco_line = lineno - lineno += len(n.decorator_list) # this is only approximately true - else: - # Set deco_line to the old pre-3.8 lineno, in order to keep - # existing "# type: ignore" comments working: - deco_line = n.decorator_list[0].lineno + # Set deco_line to the old pre-3.8 lineno, in order to keep + # existing "# type: ignore" comments working: + deco_line = n.decorator_list[0].lineno var = Var(func_def.name) var.is_ready = False @@ -1188,12 +1118,9 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: cdef.decorators = self.translate_expr_list(n.decorator_list) # Set lines to match the old mypy 0.700 lines, in order to keep # existing "# type: ignore" comments working: - if sys.version_info < (3, 8): - cdef.line = n.lineno + len(n.decorator_list) - cdef.deco_line = n.lineno - else: - cdef.line = n.lineno - cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + cdef.line = n.lineno + cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + cdef.column = n.col_offset cdef.end_line = getattr(n, "end_lineno", None) cdef.end_column = getattr(n, "end_col_offset", None) @@ -1580,9 +1507,9 @@ def visit_Constant(self, n: Constant) -> Any: if val is None: e = NameExpr("None") elif isinstance(val, str): - e = StrExpr(n.s) + e = StrExpr(val) elif isinstance(val, bytes): - e = BytesExpr(bytes_to_human_readable_repr(n.s)) + e = BytesExpr(bytes_to_human_readable_repr(val)) elif isinstance(val, bool): # Must check before int! e = NameExpr(str(val)) elif isinstance(val, int): @@ -1597,28 +1524,6 @@ def visit_Constant(self, n: Constant) -> Any: raise RuntimeError("Constant not implemented for " + str(type(val))) return self.set_line(e, n) - # Num(object n) -- a number as a PyObject. - def visit_Num(self, n: ast3.Num) -> IntExpr | FloatExpr | ComplexExpr: - # The n field has the type complex, but complex isn't *really* - # a parent of int and float, and this causes isinstance below - # to think that the complex branch is always picked. Avoid - # this by throwing away the type. - val: object = n.n - if isinstance(val, int): - e: IntExpr | FloatExpr | ComplexExpr = IntExpr(val) - elif isinstance(val, float): - e = FloatExpr(val) - elif isinstance(val, complex): - e = ComplexExpr(val) - else: - raise RuntimeError("num not implemented for " + str(type(val))) - return self.set_line(e, n) - - # Str(string s) - def visit_Str(self, n: Str) -> StrExpr: - e = StrExpr(n.s) - return self.set_line(e, n) - # JoinedStr(expr* values) def visit_JoinedStr(self, n: ast3.JoinedStr) -> Expression: # Each of n.values is a str or FormattedValue; we just concatenate @@ -1643,7 +1548,7 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression: # to allow mypyc to support f-strings with format specifiers and conversions. val_exp = self.visit(n.value) val_exp.set_line(n.lineno, n.col_offset) - conv_str = "" if n.conversion is None or n.conversion < 0 else "!" + chr(n.conversion) + conv_str = "" if n.conversion < 0 else "!" + chr(n.conversion) format_string = StrExpr("{" + conv_str + ":{}}") format_spec_exp = self.visit(n.format_spec) if n.format_spec is not None else StrExpr("") format_string.set_line(n.lineno, n.col_offset) @@ -1654,21 +1559,6 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression: ) return self.set_line(result_expression, n) - # Bytes(bytes s) - def visit_Bytes(self, n: ast3.Bytes) -> BytesExpr | StrExpr: - e = BytesExpr(bytes_to_human_readable_repr(n.s)) - return self.set_line(e, n) - - # NameConstant(singleton value) - def visit_NameConstant(self, n: NameConstant) -> NameExpr: - e = NameExpr(str(n.value)) - return self.set_line(e, n) - - # Ellipsis - def visit_Ellipsis(self, n: ast3_Ellipsis) -> EllipsisExpr: - e = EllipsisExpr() - return self.set_line(e, n) - # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr: value = n.value @@ -1955,9 +1845,9 @@ def translate_argument_list(self, l: Sequence[ast3.expr]) -> TypeList: return TypeList([self.visit(e) for e in l], line=self.line) def _extract_argument_name(self, n: ast3.expr) -> str | None: - if isinstance(n, Str): - return n.s.strip() - elif isinstance(n, NameConstant) and str(n.value) == "None": + if isinstance(n, Constant) and isinstance(n.value, str): + return n.value.strip() + elif isinstance(n, Constant) and n.value is None: return None self.fail( message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format(type(n).__name__), @@ -1983,12 +1873,6 @@ def visit_BinOp(self, n: ast3.BinOp) -> Type: uses_pep604_syntax=True, ) - def visit_NameConstant(self, n: NameConstant) -> Type: - if isinstance(n.value, bool): - return RawExpressionType(n.value, "builtins.bool", line=self.line) - else: - return UnboundType(str(n.value), line=self.line, column=n.col_offset) - # Only for 3.8 and newer def visit_Constant(self, n: Constant) -> Type: val = n.value @@ -2041,23 +1925,6 @@ def numeric_type(self, value: object, n: AST) -> Type: numeric_value, type_name, line=self.line, column=getattr(n, "col_offset", -1) ) - # These next three methods are only used if we are on python < - # 3.8, using typed_ast. They are defined unconditionally because - # mypyc can't handle conditional method definitions. - - # Num(number n) - def visit_Num(self, n: Num) -> Type: - return self.numeric_type(n.n, n) - - # Str(string s) - def visit_Str(self, n: Str) -> Type: - return parse_type_string(n.s, "builtins.str", self.line, n.col_offset) - - # Bytes(bytes s) - def visit_Bytes(self, n: Bytes) -> Type: - contents = bytes_to_human_readable_repr(n.s) - return RawExpressionType(contents, "builtins.bytes", self.line, column=n.col_offset) - def visit_Index(self, n: ast3.Index) -> Type: # cast for mypyc's benefit on Python 3.9 value = self.visit(cast(Any, n).value) @@ -2086,10 +1953,10 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: for s in dims: if getattr(s, "col_offset", None) is None: if isinstance(s, ast3.Index): - s.col_offset = s.value.col_offset # type: ignore[attr-defined] + s.col_offset = s.value.col_offset elif isinstance(s, ast3.Slice): assert s.lower is not None - s.col_offset = s.lower.col_offset # type: ignore[attr-defined] + s.col_offset = s.lower.col_offset sliceval = ast3.Tuple(dims, n.ctx) empty_tuple_index = False @@ -2130,10 +1997,6 @@ def visit_Attribute(self, n: Attribute) -> Type: else: return self.invalid_type(n) - # Ellipsis - def visit_Ellipsis(self, n: ast3_Ellipsis) -> Type: - return EllipsisType(line=self.line) - # List(expr* elts, expr_context ctx) def visit_List(self, n: ast3.List) -> Type: assert isinstance(n.ctx, ast3.Load) diff --git a/mypy/messages.py b/mypy/messages.py index ea7923c59778..8e59e7329168 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -235,6 +235,8 @@ def span_from_context(ctx: Context) -> Iterable[int]: Current logic is a bit tricky, to keep as much backwards compatibility as possible. We may reconsider this to always be a single line (or otherwise simplify it) when we drop Python 3.7. + + TODO: address this in follow up PR """ if isinstance(ctx, (ClassDef, FuncDef)): return range(ctx.deco_line or ctx.line, ctx.line + 1) diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index 778b0b163ce6..f262ac8b2132 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -2,10 +2,10 @@ """Utilities to find the site and prefix information of a Python executable. -This file MUST remain compatible with all Python 3.7+ versions. Since we cannot make any assumptions about the -Python being executed, this module should not use *any* dependencies outside of the standard -library found in Python 3.7. This file is run each mypy run, so it should be kept as fast as -possible. +This file MUST remain compatible with all Python 3.8+ versions. Since we cannot make any +assumptions about the Python being executed, this module should not use *any* dependencies outside +of the standard library found in Python 3.8. This file is run each mypy run, so it should be kept +as fast as possible. """ import sys diff --git a/mypy/test/meta/test_update_data.py b/mypy/test/meta/test_update_data.py index 67f9a7f56ebd..4e4bdd193dbf 100644 --- a/mypy/test/meta/test_update_data.py +++ b/mypy/test/meta/test_update_data.py @@ -29,10 +29,7 @@ def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str: test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" args = [sys.executable, "-m", "pytest", "-n", "0", "-s", "--update-data", test_nodeid] - if sys.version_info >= (3, 8): - cmd = shlex.join(args) - else: - cmd = " ".join(args) + cmd = shlex.join(args) for i in range(max_attempts - 1, -1, -1): res = subprocess.run(args, cwd=p_root) if res.returncode == 0: diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 58c0ee803359..20dfbb77f3e0 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -36,9 +36,7 @@ # Includes all check-* files with the .test extension in the test-data/unit directory typecheck_files = find_test_files(pattern="check-*.test") -# Tests that use Python 3.8-only AST features (like expression-scoped ignores): -if sys.version_info < (3, 8): - typecheck_files.remove("check-python38.test") +# Tests that use Python version specific features: if sys.version_info < (3, 9): typecheck_files.remove("check-python39.test") if sys.version_info < (3, 10): diff --git a/mypy/test/testdaemon.py b/mypy/test/testdaemon.py index e3cdf44d89f2..7115e682e60d 100644 --- a/mypy/test/testdaemon.py +++ b/mypy/test/testdaemon.py @@ -13,8 +13,6 @@ import tempfile import unittest -import pytest - from mypy.dmypy_server import filter_out_missing_top_level_packages from mypy.fscache import FileSystemCache from mypy.modulefinder import SearchPaths @@ -30,8 +28,6 @@ class DaemonSuite(DataSuite): files = daemon_files def run_case(self, testcase: DataDrivenTestCase) -> None: - if testcase.name.endswith("_python38") and sys.version_info < (3, 8): - pytest.skip("Not supported on this version of Python") try: test_daemon(testcase) finally: diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 5b4c816b5c38..ba0526d32558 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -16,7 +16,6 @@ import os import re -import sys import unittest from typing import Any @@ -70,9 +69,6 @@ def should_skip(self, testcase: DataDrivenTestCase) -> bool: else: if testcase.only_when == "-only_when_cache": return True - - if "Inspect" in testcase.name and sys.version_info < (3, 8): - return True return False def run_case(self, testcase: DataDrivenTestCase) -> None: @@ -321,6 +317,7 @@ def maybe_suggest(self, step: int, server: Server, src: str, tmp_dir: str) -> li # JSON contains already escaped \ on Windows, so requires a bit of care. val = val.replace("\\\\", "\\") val = val.replace(os.path.realpath(tmp_dir) + os.path.sep, "") + val = val.replace(os.path.abspath(tmp_dir) + os.path.sep, "") output.extend(val.strip().split("\n")) return normalize_messages(output) diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index da5025faef03..48d0658cd1e9 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -98,11 +98,6 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: assert testcase.old_cwd is not None, "test was not properly set up" python = sys.executable - if sys.version_info < (3, 8) and testcase.location[-1] == "testTypedPkgSimpleEditable": - # Python 3.7 doesn't ship with new enough pip to support PEP 660 - # This is a quick hack to skip the test; we'll drop Python 3.7 support soon enough - return - assert python is not None, "Should be impossible" pkgs, pip_args = parse_pkgs(testcase.input[0]) mypy_args = parse_mypy_args(testcase.input[1]) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index b21e06c0896a..884cf87ac5d8 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -677,6 +677,7 @@ class StubgenPythonSuite(DataSuite): base_path = "." files = ["stubgen.test"] + @unittest.skipIf(sys.platform == "win32", "clean up fails on Windows") def run_case(self, testcase: DataDrivenTestCase) -> None: with local_sys_path_set(): self.run_case_inner(testcase) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 275b09c3a240..661d46e9fd8a 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -232,17 +232,16 @@ def test_arg_name(self) -> Iterator[Case]: runtime="def bad(num, text) -> None: pass", error="bad", ) - if sys.version_info >= (3, 8): - yield Case( - stub="def good_posonly(__number: int, text: str) -> None: ...", - runtime="def good_posonly(num, /, text): pass", - error=None, - ) - yield Case( - stub="def bad_posonly(__number: int, text: str) -> None: ...", - runtime="def bad_posonly(flag, /, text): pass", - error="bad_posonly", - ) + yield Case( + stub="def good_posonly(__number: int, text: str) -> None: ...", + runtime="def good_posonly(num, /, text): pass", + error=None, + ) + yield Case( + stub="def bad_posonly(__number: int, text: str) -> None: ...", + runtime="def bad_posonly(flag, /, text): pass", + error="bad_posonly", + ) yield Case( stub=""" class BadMethod: @@ -283,22 +282,21 @@ def test_arg_kind(self) -> Iterator[Case]: runtime="def stub_posonly(number, text): pass", error="stub_posonly", ) - if sys.version_info >= (3, 8): - yield Case( - stub="def good_posonly(__number: int, text: str) -> None: ...", - runtime="def good_posonly(number, /, text): pass", - error=None, - ) - yield Case( - stub="def runtime_posonly(number: int, text: str) -> None: ...", - runtime="def runtime_posonly(number, /, text): pass", - error="runtime_posonly", - ) - yield Case( - stub="def stub_posonly_570(number: int, /, text: str) -> None: ...", - runtime="def stub_posonly_570(number, text): pass", - error="stub_posonly_570", - ) + yield Case( + stub="def good_posonly(__number: int, text: str) -> None: ...", + runtime="def good_posonly(number, /, text): pass", + error=None, + ) + yield Case( + stub="def runtime_posonly(number: int, text: str) -> None: ...", + runtime="def runtime_posonly(number, /, text): pass", + error="runtime_posonly", + ) + yield Case( + stub="def stub_posonly_570(number: int, /, text: str) -> None: ...", + runtime="def stub_posonly_570(number, text): pass", + error="stub_posonly_570", + ) @collect_cases def test_default_presence(self) -> Iterator[Case]: @@ -582,17 +580,16 @@ def f4(a: str, *args, b: int, **kwargs) -> str: ... runtime="def f4(a, *args, b, **kwargs): pass", error=None, ) - if sys.version_info >= (3, 8): - yield Case( - stub=""" - @overload - def f5(__a: int) -> int: ... - @overload - def f5(__b: str) -> str: ... - """, - runtime="def f5(x, /): pass", - error=None, - ) + yield Case( + stub=""" + @overload + def f5(__a: int) -> int: ... + @overload + def f5(__b: str) -> str: ... + """, + runtime="def f5(x, /): pass", + error=None, + ) @collect_cases def test_property(self) -> Iterator[Case]: diff --git a/mypy/util.py b/mypy/util.py index 2c225c7fe651..2960818d0984 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -421,10 +421,10 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 7): + if sys.version_info[:2] < (3, 8): sys.exit( - "Running {name} with Python 3.6 or lower is not supported; " - "please upgrade to 3.7 or newer".format(name=program) + "Running {name} with Python 3.7 or lower is not supported; " + "please upgrade to 3.8 or newer".format(name=program) ) diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 62083d144621..7413e6407d4f 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -6,7 +6,7 @@ show_traceback = True pretty = True always_false = MYPYC plugins = misc/proper_plugin.py -python_version = 3.7 +python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr show_error_code_links = True diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index a4071b9c3de5..2dd1c025123f 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -64,12 +64,10 @@ "run-dunders.test", "run-singledispatch.test", "run-attrs.test", + "run-python37.test", + "run-python38.test", ] -files.append("run-python37.test") -if sys.version_info >= (3, 8): - files.append("run-python38.test") - if sys.version_info >= (3, 10): files.append("run-match.test") diff --git a/pyproject.toml b/pyproject.toml index 96a05d545946..0f2712be52dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,15 +6,12 @@ requires = [ "setuptools >= 40.6.2", "wheel >= 0.30.0", # the following is from mypy-requirements.txt - "typing_extensions>=4.1.0; python_version >= '3.8'", - "typing_extensions>=4.7.0; python_version < '3.8'", + "typing_extensions>=4.1.0", "mypy_extensions>=1.0.0", - "typed_ast>=1.4.0,<2; python_version<'3.8'", "tomli>=1.1.0; python_version<'3.11'", # the following is from build-requirements.txt "types-psutil", "types-setuptools", - "types-typed-ast>=1.5.8.5,<1.6.0", ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 85d412540013..bbb655ea4537 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 7, 0): - sys.stderr.write("ERROR: You need Python 3.7 or later to use mypy.\n") +if sys.version_info < (3, 8, 0): + sys.stderr.write("ERROR: You need Python 3.8 or later to use mypy.\n") exit(1) # we'll import stuff from the source tree, let's ensure is on the sys path @@ -186,7 +186,6 @@ def run(self): "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -221,20 +220,18 @@ def run(self): cmdclass=cmdclass, # When changing this, also update mypy-requirements.txt. install_requires=[ - "typed_ast >= 1.4.0, < 2; python_version<'3.8'", - "typing_extensions>=4.1.0; python_version >= '3.8'", - "typing_extensions>=4.7.0; python_version < '3.8'", + "typing_extensions>=4.1.0", "mypy_extensions >= 1.0.0", "tomli>=1.1.0; python_version<'3.11'", ], # Same here. extras_require={ "dmypy": "psutil >= 4.0", - "python2": "typed_ast >= 1.4.0, < 2", + "python2": "", "reports": "lxml", "install-types": "pip", }, - python_requires=">=3.7", + python_requires=">=3.8", include_package_data=True, project_urls={ "News": "https://mypy-lang.org/news.html", diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 7d1114ceab7a..9d9a7d9ac039 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -386,10 +386,6 @@ f(g( )) x[0] [out] -main:2:10:2:10: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:6:3:6:3: error: Argument 1 to "f" has incompatible type "int"; expected "str" -main:8:1:8:1: error: Value of type "int" is not indexable -[out version>=3.8] main:2:10:2:17: error: Incompatible types in assignment (expression has type "str", variable has type "int") main:6:3:7:1: error: Argument 1 to "f" has incompatible type "int"; expected "str" main:8:1:8:4: error: Value of type "int" is not indexable diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 661afca807f4..c3153cd8643a 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3740,7 +3740,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.7/@deps.meta.json.2] +[file ../.mypy_cache/3.8/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file ../.mypy_cache/.gitignore] # Another hack to not trigger a .gitignore creation failure "false positive" @@ -3775,7 +3775,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by deleting -- the proto deps file. -[delete ../.mypy_cache/3.7/@deps.meta.json.2] +[delete ../.mypy_cache/3.8/@deps.meta.json.2] [file b.py.2] # uh -- Every file should get reloaded, since the cache was invalidated diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index bebbbf4b1676..3d05faed74f1 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -592,8 +592,6 @@ def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: f2(lambda x: 42)(42, x=42) [builtins fixtures/paramspec.pyi] [out] -main:10: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer -[out version>=3.8] main:17: error: Incompatible return value type (got "Callable[[Arg(int, 'x'), **P], R]", expected "Callable[[int, **P], R]") main:17: note: This is likely because "result" has named arguments: "x". Consider marking them positional-only @@ -620,9 +618,6 @@ def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: # reason for rejection: f2(lambda x: 42)(42, x=42) [builtins fixtures/paramspec.pyi] -[out] -main:11: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer -[out version>=3.8] [case testParamSpecConcatenateWithTypeVar] from typing_extensions import ParamSpec, Concatenate @@ -662,8 +657,6 @@ reveal_type(abc) bar(abc) [builtins fixtures/paramspec.pyi] [out] -main:13: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer -[out version>=3.8] main:16: note: Revealed type is "__main__.Foo[[builtins.int, b: builtins.str]]" [case testSolveParamSpecWithSelfType] diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index b4c007148903..a307e4c8b6a0 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -620,10 +620,6 @@ def bad_typeguard(*, x: object) -> TypeGuard[int]: # line 15 [builtins fixtures/classmethod.pyi] [out] main:4: error: TypeGuard functions must have a positional argument -main:11: error: TypeGuard functions must have a positional argument -main:15: error: TypeGuard functions must have a positional argument -[out version>=3.8] -main:4: error: TypeGuard functions must have a positional argument main:12: error: TypeGuard functions must have a positional argument main:15: error: TypeGuard functions must have a positional argument diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index b87bb7f17eb0..6e9fdf6dab65 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1122,19 +1122,6 @@ class AnotherCustomClassDefinedBelow: def another_even_more_interesting_method(self, arg: Union[int, str, float]) -> None: self.very_important_attribute_with_long_name: OneCustomClassName = OneCustomClassName().some_interesting_method(arg) [out] -some_file.py:3: error: Unsupported operand types for + ("int" and "str") - 42 + 'no way' - ^ -some_file.py:11: error: Incompatible types in assignment (expression has type -"AnotherCustomClassDefinedBelow", variable has type "OneCustomClassName") - ...t_attribute_with_long_name: OneCustomClassName = OneCustomClassName().... - ^ -some_file.py:11: error: Argument 1 to "some_interesting_method" of -"OneCustomClassName" has incompatible type "Union[int, str, float]"; expected -"AnotherCustomClassDefinedBelow" - ...OneCustomClassName = OneCustomClassName().some_interesting_method(arg) - ^ -[out version>=3.8] some_file.py:3: error: Unsupported operand types for + ("int" and "str") 42 + 'no way' ^~~~~~~~ @@ -1165,14 +1152,6 @@ def test_tabs() -> str: def test_between(x: str) -> None: ... test_between(1 + 1) [out] -tabs.py:2: error: Incompatible return value type (got "None", expected "str") - return None - ^ -tabs.py:4: error: Argument 1 to "test_between" has incompatible type "int"; -expected "str" - test_between(1 + 1) - ^ -[out version>=3.8] tabs.py:2: error: Incompatible return value type (got "None", expected "str") return None ^~~~ diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 1fae1514111c..f208b4e78e54 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -360,7 +360,7 @@ def bar() -> None: x = foo('abc') # type: str foo(arg='xyz') -[case testDaemonGetType_python38] +[case testDaemonGetType] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.8 Daemon started $ dmypy inspect foo:1:2:3:4 @@ -423,7 +423,7 @@ def unreachable(x: int) -> None: return x # line 17 -[case testDaemonGetTypeInexact_python38] +[case testDaemonGetTypeInexact] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary Daemon started $ dmypy check foo.py --export-types @@ -478,7 +478,7 @@ def unreachable(x: int, y: int) -> None: return x and y # line 11 -[case testDaemonGetAttrs_python38] +[case testDaemonGetAttrs] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary Daemon started $ dmypy check foo.py bar.py --export-types @@ -520,7 +520,7 @@ class B: var: Union[A, B] var # line 10 -[case testDaemonGetDefinition_python38] +[case testDaemonGetDefinition] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary Daemon started $ dmypy check foo.py bar/baz.py bar/__init__.py --export-types diff --git a/test-data/unit/fine-grained-blockers.test b/test-data/unit/fine-grained-blockers.test index a134fb1d4301..33dedd887114 100644 --- a/test-data/unit/fine-grained-blockers.test +++ b/test-data/unit/fine-grained-blockers.test @@ -48,16 +48,6 @@ a.py:1: error: invalid syntax [syntax] def f(x: int) -> ^ == -main:3: error: Missing positional argument "x" in call to "f" [call-arg] - a.f() - ^ -== -[out version>=3.8] -== -a.py:1: error: invalid syntax [syntax] - def f(x: int) -> - ^ -== main:3: error: Missing positional argument "x" in call to "f" [call-arg] a.f() ^~~~~ diff --git a/test-data/unit/fine-grained-cache-incremental.test b/test-data/unit/fine-grained-cache-incremental.test index 50f93dd35af3..00157333efd7 100644 --- a/test-data/unit/fine-grained-cache-incremental.test +++ b/test-data/unit/fine-grained-cache-incremental.test @@ -202,7 +202,7 @@ a.py:8: note: x: expected "int", got "str" [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.7/@deps.meta.json.2] +[file ../.mypy_cache/3.8/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file b.py.2] @@ -234,8 +234,8 @@ x = 10 [file p/c.py] class C: pass -[delete ../.mypy_cache/3.7/b.meta.json.2] -[delete ../.mypy_cache/3.7/p/c.meta.json.2] +[delete ../.mypy_cache/3.8/b.meta.json.2] +[delete ../.mypy_cache/3.8/p/c.meta.json.2] [out] == diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index a0d7d302fd08..3cb234364a58 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -95,11 +95,6 @@ class A: def g(self, a: A) -> None: pass [out] == -main:5: error: Missing positional argument "a" in call to "g" of "A" [call-arg] - a.g() # E - ^ -[out version>=3.8] -== main:5: error: Missing positional argument "a" in call to "g" of "A" [call-arg] a.g() # E ^~~~~ @@ -10101,23 +10096,6 @@ object + 1 1() [out] -b.py:1: error: Unsupported left operand type for + ("Type[object]") - object + 1 - ^ -a.py:1: error: Unsupported operand types for + ("int" and "str") - 1 + '' - ^ -== -b.py:1: error: Unsupported left operand type for + ("Type[object]") - object + 1 - ^ -b.py:2: error: "int" not callable - 1() - ^ -a.py:1: error: Unsupported operand types for + ("int" and "str") - 1 + '' - ^ -[out version>=3.8] b.py:1: error: Unsupported left operand type for + ("Type[object]") object + 1 ^~~~~~~~~~ diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 1460002e1b65..e4eecc33871c 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1441,7 +1441,7 @@ accepts_named_tuple(b) accepts_named_tuple(1) accepts_named_tuple((1, 2)) [out] -_testNamedTupleTypeInheritanceSpecialCase.py:8: note: Revealed type is "collections.OrderedDict[builtins.str, Any]" +_testNamedTupleTypeInheritanceSpecialCase.py:8: note: Revealed type is "builtins.dict[builtins.str, Any]" _testNamedTupleTypeInheritanceSpecialCase.py:9: note: Revealed type is "builtins.tuple[builtins.str, ...]" _testNamedTupleTypeInheritanceSpecialCase.py:10: note: Revealed type is "builtins.dict[builtins.str, Any]" _testNamedTupleTypeInheritanceSpecialCase.py:17: error: Argument 1 to "accepts_named_tuple" has incompatible type "int"; expected "NamedTuple" diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 9c6ac2833c8b..a098dd8791d4 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -361,8 +361,6 @@ main:2: error: "yield" outside function [case testInvalidLvalues1] 1 = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [out version>=3.10] main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? @@ -370,8 +368,6 @@ main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '= [case testInvalidLvalues2] (1) = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [out version>=3.10] main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? @@ -379,37 +375,27 @@ main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '= [case testInvalidLvalues3] (1, 1) = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [case testInvalidLvalues4] [1, 1] = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [case testInvalidLvalues6] x = y = z = 1 # ok x, (y, 1) = 1 [out] -main:2: error: can't assign to literal -[out version>=3.8] main:2: error: cannot assign to literal [case testInvalidLvalues7] x, [y, 1] = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [case testInvalidLvalues8] x, [y, [z, 1]] = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [case testInvalidLvalues9] @@ -417,15 +403,11 @@ x, (y) = 1 # ok x, (y, (z, z)) = 1 # ok x, (y, (z, 1)) = 1 [out] -main:3: error: can't assign to literal -[out version>=3.8] main:3: error: cannot assign to literal [case testInvalidLvalues10] x + x = 1 [out] -main:1: error: can't assign to operator -[out version>=3.8] main:1: error: cannot assign to operator [out version>=3.10] main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? @@ -433,8 +415,6 @@ main:1: error: cannot assign to expression here. Maybe you meant '==' instead of [case testInvalidLvalues11] -x = 1 [out] -main:1: error: can't assign to operator -[out version>=3.8] main:1: error: cannot assign to operator [out version>=3.10] main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? @@ -442,8 +422,6 @@ main:1: error: cannot assign to expression here. Maybe you meant '==' instead of [case testInvalidLvalues12] 1.1 = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [out version>=3.10] main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? @@ -451,8 +429,6 @@ main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '= [case testInvalidLvalues13] 'x' = 1 [out] -main:1: error: can't assign to literal -[out version>=3.8] main:1: error: cannot assign to literal [out version>=3.10] main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? @@ -460,8 +436,6 @@ main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '= [case testInvalidLvalues14] x() = 1 [out] -main:1: error: can't assign to function call -[out version>=3.8] main:1: error: cannot assign to function call [out version>=3.10] main:1: error: cannot assign to function call here. Maybe you meant '==' instead of '='? @@ -518,16 +492,12 @@ main:2: error: Can use starred expression only as assignment target x = 1 del x(1) [out] -main:2: error: can't delete function call -[out version>=3.8] main:2: error: cannot delete function call [case testInvalidDel2] x = 1 del x + 1 [out] -main:2: error: can't delete operator -[out version>=3.8] main:2: error: cannot delete operator [out version>=3.10] main:2: error: cannot delete expression @@ -927,8 +897,6 @@ import typing def f(): pass f() = 1 # type: int [out] -main:3: error: can't assign to function call -[out version>=3.8] main:3: error: cannot assign to function call [out version>=3.10] main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? @@ -1007,8 +975,6 @@ x, y = 1, 2 # type: int # E: Tuple type expected for multiple variables a = 1 a() = None # type: int [out] -main:2: error: can't assign to function call -[out version>=3.8] main:2: error: cannot assign to function call [out version>=3.10] main:2: error: cannot assign to function call here. Maybe you meant '==' instead of '='? @@ -1311,8 +1277,6 @@ main:2: note: Did you forget to import it from "typing"? (Suggestion: "from typi def f(): pass with f() as 1: pass [out] -main:2: error: can't assign to literal -[out version>=3.8] main:2: error: cannot assign to literal [case testInvalidTypeAnnotation] @@ -1327,8 +1291,6 @@ import typing def f() -> None: f() = 1 # type: int [out] -main:3: error: can't assign to function call -[out version>=3.8] main:3: error: cannot assign to function call [out version>=3.10] main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index 9b43a5b853c0..c143805f4564 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -557,8 +557,6 @@ MypyFile:1( def f(x, y) -> None: del x, y + 1 [out] -main:2: error: can't delete operator -[out version>=3.8] main:2: error: cannot delete operator [out version>=3.10] main:2: error: cannot delete expression From 96eadfd29ab30517e8a2d44361bd6292d29284f4 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:46:00 -0700 Subject: [PATCH 0084/1617] Fix wheel build trigger (#15568) --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index da140375d603..e728d741d90d 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.7' + python-version: '3.11' - name: Trigger script env: WHEELS_PUSH_TOKEN: ${{ secrets.WHEELS_PUSH_TOKEN }} From 259c822938c4d3461ba80e8162faf082d487162a Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 2 Jul 2023 20:30:38 -0700 Subject: [PATCH 0085/1617] Unskip some tests that appear to pass (#15567) These mostly date back to https://github.com/python/mypy/pull/4369 There are also some skipped tests in here that fail that I don't touch --- mypy/test/testsubtypes.py | 3 +-- mypy/test/testtypes.py | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index c76a34ff00d7..464f64d2b846 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -2,7 +2,7 @@ from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT from mypy.subtypes import is_subtype -from mypy.test.helpers import Suite, skip +from mypy.test.helpers import Suite from mypy.test.typefixture import InterfaceTypeFixture, TypeFixture from mypy.types import Instance, TupleType, Type, UnpackType @@ -69,7 +69,6 @@ def test_interface_subtyping(self) -> None: self.assert_equivalent(self.fx.f, self.fx.f) self.assert_not_subtype(self.fx.a, self.fx.f) - @skip def test_generic_interface_subtyping(self) -> None: # TODO make this work fx2 = InterfaceTypeFixture() diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 5f6943de3199..246350dc02cd 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -899,13 +899,11 @@ def ov(*items: CallableType) -> Overloaded: self.assert_join(ov(c(fx.a, fx.a), c(fx.b, fx.b)), c(any, fx.b), c(any, fx.b)) self.assert_join(ov(c(fx.a, fx.a), c(any, fx.b)), c(fx.b, fx.b), c(any, fx.b)) - @skip def test_join_interface_types(self) -> None: self.assert_join(self.fx.f, self.fx.f, self.fx.f) self.assert_join(self.fx.f, self.fx.f2, self.fx.o) self.assert_join(self.fx.f, self.fx.f3, self.fx.f) - @skip def test_join_interface_and_class_types(self) -> None: self.assert_join(self.fx.o, self.fx.f, self.fx.o) self.assert_join(self.fx.a, self.fx.f, self.fx.o) @@ -1180,7 +1178,6 @@ def test_meet_class_types_with_shared_interfaces(self) -> None: self.assert_meet(self.fx.e, self.fx.e2, self.fx.nonet) self.assert_meet(self.fx.e2, self.fx.e3, self.fx.nonet) - @skip def test_meet_with_generic_interfaces(self) -> None: fx = InterfaceTypeFixture() self.assert_meet(fx.gfa, fx.m1, fx.m1) From a759c490d21bb9d73e788da4a8dc5c10b077a482 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 3 Jul 2023 20:05:49 +0300 Subject: [PATCH 0086/1617] Fix `ast` warnings for Python3.12 (#15558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warnings before: https://github.com/python/mypy/issues/15330#issuecomment-1615010604 Warnings after: ``` (.venv312) ~/Desktop/mypy master ✗ » python -m mypy ex.py Success: no issues found in 1 source file ``` Zero :) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/fastparse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 3f4e2f4c927d..fa185be21f9e 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1873,7 +1873,6 @@ def visit_BinOp(self, n: ast3.BinOp) -> Type: uses_pep604_syntax=True, ) - # Only for 3.8 and newer def visit_Constant(self, n: Constant) -> Type: val = n.value if val is None: @@ -1881,7 +1880,7 @@ def visit_Constant(self, n: Constant) -> Type: return UnboundType("None", line=self.line) if isinstance(val, str): # Parse forward reference. - return parse_type_string(n.s, "builtins.str", self.line, n.col_offset) + return parse_type_string(val, "builtins.str", self.line, n.col_offset) if val is Ellipsis: # '...' is valid in some types. return EllipsisType(line=self.line) From 4df2215981593d14faf25e3dec658fffeed9c41f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 3 Jul 2023 20:06:23 +0100 Subject: [PATCH 0087/1617] [mypyc] Fix 3.12 issue with pickling of instances with __dict__ (#15574) This fixes the testPickling test case in mypyc/test-data/run-classes.test on Python 3.12. Handle another case that was missed in #15471. On 3.12 we rely on Py_TPFLAGS_MANAGED_DICT and shouldn't define an explicit `__dict__` member. Work on mypyc/mypyc#995. --- mypyc/codegen/emitclass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 3606f5267a8a..84d19d69d377 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -270,7 +270,7 @@ def emit_line() -> None: # that isn't what we want. # XXX: there is no reason for the __weakref__ stuff to be mixed up with __dict__ - if cl.has_dict: + if cl.has_dict and not has_managed_dict(cl, emitter): # __dict__ lives right after the struct and __weakref__ lives right after that # TODO: They should get members in the struct instead of doing this nonsense. weak_offset = f"{base_size} + sizeof(PyObject *)" From 2e9c9b4ec9eece1209c6df1e559ed371cdfbd1b3 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 3 Jul 2023 22:07:10 +0300 Subject: [PATCH 0088/1617] Solve import cycles in expandtype and testtypes.py (#15580) --- mypy/expandtype.py | 6 ++++-- mypy/test/testtypes.py | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 5bbdd5311da7..24835a5ee4d6 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -5,7 +5,6 @@ from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, Var from mypy.state import state -from mypy.type_visitor import TypeTranslator from mypy.types import ( ANY_STRATEGY, AnyType, @@ -44,6 +43,9 @@ ) from mypy.typevartuples import find_unpack_in_list, split_with_instance +# Solving the import cycle: +import mypy.type_visitor # ruff: isort: skip + # WARNING: these functions should never (directly or indirectly) depend on # is_subtype(), meet_types(), join_types() etc. # TODO: add a static dependency test for this. @@ -167,7 +169,7 @@ def freshen_all_functions_type_vars(t: T) -> T: return result -class FreshenCallableVisitor(TypeTranslator): +class FreshenCallableVisitor(mypy.type_visitor.TypeTranslator): def visit_callable_type(self, t: CallableType) -> Type: result = super().visit_callable_type(t) assert isinstance(result, ProperType) and isinstance(result, CallableType) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 246350dc02cd..b1f21b3be79b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -5,9 +5,7 @@ import re from unittest import TestCase, skipUnless -import mypy.expandtype from mypy.erasetype import erase_type, remove_instance_last_known_values -from mypy.expandtype import expand_type from mypy.indirection import TypeIndirectionVisitor from mypy.join import join_simple, join_types from mypy.meet import meet_types, narrow_declared_type @@ -53,6 +51,9 @@ has_recursive_types, ) +# Solving the import cycle: +import mypy.expandtype # ruff: isort: skip + class TypesSuite(Suite): def setUp(self) -> None: @@ -268,7 +269,7 @@ def assert_expand( for id, t in map_items: lower_bounds[id] = t - exp = expand_type(orig, lower_bounds) + exp = mypy.expandtype.expand_type(orig, lower_bounds) # Remove erased tags (asterisks). assert_equal(str(exp).replace("*", ""), str(result)) From 5e4d097a1801bcc6f4c160f99cd1ee95f24768aa Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 3 Jul 2023 20:20:48 +0100 Subject: [PATCH 0089/1617] Fix strict optional handling in dataclasses (#15571) There were few cases when someone forgot to call `strict_optional_set()` in dataclasses plugin, let's move the calls directly to two places where they are needed for typeops. This may cause a tiny perf regression, but is much more robust in terms of preventing bugs. --- mypy/plugins/dataclasses.py | 30 +++++++++++++++++------------- test-data/unit/pythoneval.test | 18 +++++++++++++++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 9e054493828f..efa1338962a0 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -104,6 +104,7 @@ def __init__( info: TypeInfo, kw_only: bool, is_neither_frozen_nor_nonfrozen: bool, + api: SemanticAnalyzerPluginInterface, ) -> None: self.name = name self.alias = alias @@ -116,6 +117,7 @@ def __init__( self.info = info self.kw_only = kw_only self.is_neither_frozen_nor_nonfrozen = is_neither_frozen_nor_nonfrozen + self._api = api def to_argument(self, current_info: TypeInfo) -> Argument: arg_kind = ARG_POS @@ -138,7 +140,10 @@ def expand_type(self, current_info: TypeInfo) -> Optional[Type]: # however this plugin is called very late, so all types should be fully ready. # Also, it is tricky to avoid eager expansion of Self types here (e.g. because # we serialize attributes). - return expand_type(self.type, {self.info.self_type.id: fill_typevars(current_info)}) + with state.strict_optional_set(self._api.options.strict_optional): + return expand_type( + self.type, {self.info.self_type.id: fill_typevars(current_info)} + ) return self.type def to_var(self, current_info: TypeInfo) -> Var: @@ -165,13 +170,14 @@ def deserialize( ) -> DataclassAttribute: data = data.copy() typ = deserialize_and_fixup_type(data.pop("type"), api) - return cls(type=typ, info=info, **data) + return cls(type=typ, info=info, **data, api=api) def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: """Expands type vars in the context of a subtype when an attribute is inherited from a generic super type.""" if self.type is not None: - self.type = map_type_from_supertype(self.type, sub_type, self.info) + with state.strict_optional_set(self._api.options.strict_optional): + self.type = map_type_from_supertype(self.type, sub_type, self.info) class DataclassTransformer: @@ -230,12 +236,11 @@ def transform(self) -> bool: and ("__init__" not in info.names or info.names["__init__"].plugin_generated) and attributes ): - with state.strict_optional_set(self._api.options.strict_optional): - args = [ - attr.to_argument(info) - for attr in attributes - if attr.is_in_init and not self._is_kw_only_type(attr.type) - ] + args = [ + attr.to_argument(info) + for attr in attributes + if attr.is_in_init and not self._is_kw_only_type(attr.type) + ] if info.fallback_to_any: # Make positional args optional since we don't know their order. @@ -355,8 +360,7 @@ def transform(self) -> bool: self._add_dataclass_fields_magic_attribute() if self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES: - with state.strict_optional_set(self._api.options.strict_optional): - self._add_internal_replace_method(attributes) + self._add_internal_replace_method(attributes) if "__post_init__" in info.names: self._add_internal_post_init_method(attributes) @@ -546,8 +550,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: # TODO: We shouldn't be performing type operations during the main # semantic analysis pass, since some TypeInfo attributes might # still be in flux. This should be performed in a later phase. - with state.strict_optional_set(self._api.options.strict_optional): - attr.expand_typevar_from_subtype(cls.info) + attr.expand_typevar_from_subtype(cls.info) found_attrs[name] = attr sym_node = cls.info.names.get(name) @@ -693,6 +696,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: is_neither_frozen_nor_nonfrozen=_has_direct_dataclass_transform_metaclass( cls.info ), + api=self._api, ) all_attrs = list(found_attrs.values()) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index e4eecc33871c..abc0f6a464a9 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2094,7 +2094,6 @@ grouped = groupby(pairs, key=fst) [out] [case testDataclassReplaceOptional] -# flags: --strict-optional from dataclasses import dataclass, replace from typing import Optional @@ -2107,5 +2106,18 @@ reveal_type(a) a2 = replace(a, x=None) # OK reveal_type(a2) [out] -_testDataclassReplaceOptional.py:10: note: Revealed type is "_testDataclassReplaceOptional.A" -_testDataclassReplaceOptional.py:12: note: Revealed type is "_testDataclassReplaceOptional.A" +_testDataclassReplaceOptional.py:9: note: Revealed type is "_testDataclassReplaceOptional.A" +_testDataclassReplaceOptional.py:11: note: Revealed type is "_testDataclassReplaceOptional.A" + +[case testDataclassStrictOptionalAlwaysSet] +from dataclasses import dataclass +from typing import Callable, Optional + +@dataclass +class Description: + name_fn: Callable[[Optional[int]], Optional[str]] + +def f(d: Description) -> None: + reveal_type(d.name_fn) +[out] +_testDataclassStrictOptionalAlwaysSet.py:9: note: Revealed type is "def (Union[builtins.int, None]) -> Union[builtins.str, None]" From 49d95cf6b40fb3ebee2183e95206c91c81e7a107 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 3 Jul 2023 20:37:03 +0100 Subject: [PATCH 0090/1617] Don't add fictional parameters to `NamedTuple._make()` (#15578) --- mypy/semanal_namedtuple.py | 7 +------ test-data/unit/check-newsemanal.test | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index c690d4ec6d20..5eac02833d24 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -605,16 +605,11 @@ def make_init_arg(var: Var) -> Argument: add_method("__new__", ret=selftype, args=[make_init_arg(var) for var in vars], is_new=True) add_method("_asdict", args=[], ret=ordereddictype) - special_form_any = AnyType(TypeOfAny.special_form) add_method( "_make", ret=selftype, is_classmethod=True, - args=[ - Argument(Var("iterable", iterable_type), iterable_type, None, ARG_POS), - Argument(Var("new"), special_form_any, EllipsisExpr(), ARG_NAMED_OPT), - Argument(Var("len"), special_form_any, EllipsisExpr(), ARG_NAMED_OPT), - ], + args=[Argument(Var("iterable", iterable_type), iterable_type, None, ARG_POS)], ) self_tvar_expr = TypeVarExpr( diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 99f4141a4d64..de8212b3e695 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -992,7 +992,7 @@ class SubO(Out): pass o: SubO -reveal_type(SubO._make) # N: Revealed type is "def (iterable: typing.Iterable[Any], *, new: Any =, len: Any =) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" +reveal_type(SubO._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" reveal_type(o._replace(y=Other())) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" [builtins fixtures/tuple.pyi] @@ -1009,7 +1009,7 @@ o: Out reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]" reveal_type(o.x.t) # N: Revealed type is "__main__.Other" -reveal_type(Out._make) # N: Revealed type is "def (iterable: typing.Iterable[Any], *, new: Any =, len: Any =) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" +reveal_type(Out._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerIncompleteRefShadowsBuiltin1] From dcdcc60b3082b99b0088e0f5c443f90a60091d77 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 3 Jul 2023 22:37:47 +0300 Subject: [PATCH 0091/1617] Run `pyupgrade --py38-plus` on the source code (#15575) --- misc/analyze_cache.py | 4 ++-- misc/incremental_checker.py | 4 ++-- mypy/build.py | 3 ++- mypy/checker.py | 5 +++-- mypy/checkexpr.py | 6 +++--- mypy/checkpattern.py | 3 +-- mypy/checkstrformat.py | 4 ++-- mypy/config_parser.py | 3 ++- mypy/constant_fold.py | 3 +-- mypy/constraints.py | 3 +-- mypy/defaults.py | 2 +- mypy/dmypy_server.py | 4 ++-- mypy/dmypy_util.py | 3 +-- mypy/errorcodes.py | 2 +- mypy/errors.py | 4 ++-- mypy/evalexpr.py | 2 +- mypy/expandtype.py | 3 +-- mypy/fastparse.py | 4 ++-- mypy/find_sources.py | 5 ++--- mypy/fixup.py | 3 +-- mypy/ipc.py | 3 +-- mypy/literals.py | 4 ++-- mypy/main.py | 3 +-- mypy/message_registry.py | 3 +-- mypy/messages.py | 3 +-- mypy/modulefinder.py | 4 ++-- mypy/nodes.py | 3 ++- mypy/operators.py | 2 +- mypy/options.py | 5 ++--- mypy/plugins/attrs.py | 4 ++-- mypy/plugins/dataclasses.py | 5 ++--- mypy/plugins/enums.py | 3 +-- mypy/plugins/functools.py | 3 +-- mypy/plugins/singledispatch.py | 4 ++-- mypy/reachability.py | 3 +-- mypy/renaming.py | 3 +-- mypy/report.py | 4 ++-- mypy/semanal.py | 4 ++-- mypy/semanal_classprop.py | 2 +- mypy/semanal_enum.py | 3 +-- mypy/semanal_main.py | 4 ++-- mypy/semanal_namedtuple.py | 3 +-- mypy/semanal_shared.py | 4 ++-- mypy/semanal_typeddict.py | 2 +- mypy/server/mergecheck.py | 2 +- mypy/server/objgraph.py | 3 +-- mypy/server/trigger.py | 2 +- mypy/server/update.py | 4 ++-- mypy/sharedparse.py | 2 +- mypy/state.py | 3 +-- mypy/stats.py | 3 +-- mypy/stubdoc.py | 4 ++-- mypy/stubgen.py | 3 +-- mypy/stubgenc.py | 5 ++--- mypy/subtypes.py | 4 ++-- mypy/test/data.py | 4 ++-- mypy/type_visitor.py | 3 +-- mypy/typeanal.py | 4 ++-- mypy/types.py | 3 ++- mypy/typestate.py | 4 ++-- mypy/util.py | 4 ++-- mypyc/analysis/attrdefined.py | 5 ++--- mypyc/codegen/cstring.py | 2 +- mypyc/codegen/emit.py | 5 ++--- mypyc/codegen/emitfunc.py | 8 ++++---- mypyc/codegen/literals.py | 6 +++--- mypyc/common.py | 3 +-- mypyc/ir/class_ir.py | 6 +++--- mypyc/ir/func_ir.py | 5 ++--- mypyc/ir/ops.py | 19 +++++++++---------- mypyc/ir/pprint.py | 3 +-- mypyc/ir/rtypes.py | 3 +-- mypyc/irbuild/builder.py | 4 ++-- mypyc/irbuild/classdef.py | 3 +-- mypyc/irbuild/constant_fold.py | 3 +-- mypyc/irbuild/format_str_tokenizer.py | 2 +- mypyc/irbuild/ll_builder.py | 3 +-- mypyc/irbuild/specialize.py | 2 +- mypyc/primitives/registry.py | 13 ++++++------- mypyc/test-data/fixtures/testutil.py | 2 +- 80 files changed, 137 insertions(+), 168 deletions(-) diff --git a/misc/analyze_cache.py b/misc/analyze_cache.py index 45c44139b473..33205f5132fc 100644 --- a/misc/analyze_cache.py +++ b/misc/analyze_cache.py @@ -6,8 +6,8 @@ import os import os.path from collections import Counter -from typing import Any, Dict, Iterable -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Dict, Final, Iterable +from typing_extensions import TypeAlias as _TypeAlias ROOT: Final = ".mypy_cache/3.5" diff --git a/misc/incremental_checker.py b/misc/incremental_checker.py index 85239b6462b8..4e42aef333bb 100755 --- a/misc/incremental_checker.py +++ b/misc/incremental_checker.py @@ -44,8 +44,8 @@ import textwrap import time from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter -from typing import Any, Dict -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Dict, Final +from typing_extensions import TypeAlias as _TypeAlias CACHE_PATH: Final = ".incremental_checker_cache.json" MYPY_REPO_URL: Final = "https://github.com/python/mypy.git" diff --git a/mypy/build.py b/mypy/build.py index 5a6f8c00896e..5a0a481ae1a2 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -31,6 +31,7 @@ Callable, ClassVar, Dict, + Final, Iterator, Mapping, NamedTuple, @@ -38,7 +39,7 @@ Sequence, TextIO, ) -from typing_extensions import Final, TypeAlias as _TypeAlias, TypedDict +from typing_extensions import TypeAlias as _TypeAlias, TypedDict import mypy.semanal_main from mypy.checker import TypeChecker diff --git a/mypy/checker.py b/mypy/checker.py index cdce42ddaaa1..bf200299d8b3 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -9,6 +9,7 @@ AbstractSet, Callable, Dict, + Final, Generic, Iterable, Iterator, @@ -22,7 +23,7 @@ cast, overload, ) -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing_extensions import TypeAlias as _TypeAlias import mypy.checkexpr from mypy import errorcodes as codes, message_registry, nodes, operators @@ -1546,7 +1547,7 @@ def check_reverse_op_method( if opt_meta is not None: forward_inst = opt_meta - def has_readable_member(typ: Union[UnionType, Instance], name: str) -> bool: + def has_readable_member(typ: UnionType | Instance, name: str) -> bool: # TODO: Deal with attributes of TupleType etc. if isinstance(typ, Instance): return typ.type.has_readable_member(name) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 986e58c21762..aee8d58a69f6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6,8 +6,8 @@ import time from collections import defaultdict from contextlib import contextmanager -from typing import Callable, ClassVar, Iterable, Iterator, List, Optional, Sequence, cast -from typing_extensions import Final, TypeAlias as _TypeAlias, overload +from typing import Callable, ClassVar, Final, Iterable, Iterator, List, Optional, Sequence, cast +from typing_extensions import TypeAlias as _TypeAlias, overload import mypy.checker import mypy.errorcodes as codes @@ -5581,7 +5581,7 @@ def replace_callable_return_type(c: CallableType, new_ret_type: Type) -> Callabl return c.copy_modified(ret_type=new_ret_type) -def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> Optional[CallableType]: +def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> CallableType | None: """Make free type variables generic in the type if possible. This will translate the type `tp` while trying to create valid bindings for diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 50d7b8a9826f..e432675b0b5a 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -3,8 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import NamedTuple -from typing_extensions import Final +from typing import Final, NamedTuple import mypy.checker from mypy import message_registry diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 974985d8b4fc..cda603be086b 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -13,8 +13,8 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Callable, Dict, Match, Pattern, Tuple, Union, cast -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import TYPE_CHECKING, Callable, Dict, Final, Match, Pattern, Tuple, Union, cast +from typing_extensions import TypeAlias as _TypeAlias import mypy.errorcodes as codes from mypy.errors import Errors diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 05af2ba6e21e..47b0bc3acabc 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -19,6 +19,7 @@ Any, Callable, Dict, + Final, Iterable, List, Mapping, @@ -28,7 +29,7 @@ Tuple, Union, ) -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing_extensions import TypeAlias as _TypeAlias from mypy import defaults from mypy.options import PER_MODULE_OPTIONS, Options diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 6881ecae9e88..4582b2a7396d 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -5,8 +5,7 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Final +from typing import Final, Union from mypy.nodes import ( ComplexExpr, diff --git a/mypy/constraints.py b/mypy/constraints.py index 803b9819be6f..f9124630a706 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -2,8 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Iterable, List, Sequence, cast -from typing_extensions import Final +from typing import TYPE_CHECKING, Final, Iterable, List, Sequence, cast import mypy.subtypes import mypy.typeops diff --git a/mypy/defaults.py b/mypy/defaults.py index 59a99cd58888..2a881975a27c 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from typing_extensions import Final +from typing import Final PYTHON2_VERSION: Final = (2, 7) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 2427f83c5767..a50ebc5415ba 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -17,8 +17,8 @@ import time import traceback from contextlib import redirect_stderr, redirect_stdout -from typing import AbstractSet, Any, Callable, List, Sequence, Tuple -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import AbstractSet, Any, Callable, Final, List, Sequence, Tuple +from typing_extensions import TypeAlias as _TypeAlias import mypy.build import mypy.errors diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index a1b419617f73..2aae41d998da 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -6,8 +6,7 @@ from __future__ import annotations import json -from typing import Any -from typing_extensions import Final +from typing import Any, Final from mypy.ipc import IPCBase diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index b9448f3d5af9..68ae4b49a806 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -6,7 +6,7 @@ from __future__ import annotations from collections import defaultdict -from typing_extensions import Final +from typing import Final from mypy_extensions import mypyc_attr diff --git a/mypy/errors.py b/mypy/errors.py index 6739d30f16a4..2badac3e3d6d 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -4,8 +4,8 @@ import sys import traceback from collections import defaultdict -from typing import Callable, Iterable, NoReturn, Optional, TextIO, Tuple, TypeVar -from typing_extensions import Final, Literal, TypeAlias as _TypeAlias +from typing import Callable, Final, Iterable, NoReturn, Optional, TextIO, Tuple, TypeVar +from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes from mypy.errorcodes import IMPORT, ErrorCode diff --git a/mypy/evalexpr.py b/mypy/evalexpr.py index 2bc6966fa2fa..4b3abb1be3e2 100644 --- a/mypy/evalexpr.py +++ b/mypy/evalexpr.py @@ -7,7 +7,7 @@ """ import ast -from typing_extensions import Final +from typing import Final import mypy.nodes from mypy.visitor import ExpressionVisitor diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 24835a5ee4d6..83d9bf4c8725 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -1,7 +1,6 @@ from __future__ import annotations -from typing import Iterable, Mapping, Sequence, TypeVar, cast, overload -from typing_extensions import Final +from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, Var from mypy.state import state diff --git a/mypy/fastparse.py b/mypy/fastparse.py index fa185be21f9e..eeeda16f9d8d 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -4,8 +4,8 @@ import re import sys import warnings -from typing import Any, Callable, List, Optional, Sequence, TypeVar, Union, cast -from typing_extensions import Final, Literal, overload +from typing import Any, Callable, Final, List, Optional, Sequence, TypeVar, Union, cast +from typing_extensions import Literal, overload from mypy import defaults, errorcodes as codes, message_registry from mypy.errors import Errors diff --git a/mypy/find_sources.py b/mypy/find_sources.py index a3ef2d3db052..3565fc4609cd 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -4,8 +4,7 @@ import functools import os -from typing import Sequence -from typing_extensions import Final +from typing import Final, Sequence from mypy.fscache import FileSystemCache from mypy.modulefinder import PYTHON_EXTENSIONS, BuildSource, matches_exclude, mypy_path @@ -160,7 +159,7 @@ def crawl_up(self, path: str) -> tuple[str, str]: def crawl_up_dir(self, dir: str) -> tuple[str, str]: return self._crawl_up_helper(dir) or ("", dir) - @functools.lru_cache() # noqa: B019 + @functools.lru_cache # noqa: B019 def _crawl_up_helper(self, dir: str) -> tuple[str, str] | None: """Given a directory, maybe returns module and base directory. diff --git a/mypy/fixup.py b/mypy/fixup.py index 15f4c13c20fa..2b2e1210ee4e 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -2,8 +2,7 @@ from __future__ import annotations -from typing import Any -from typing_extensions import Final +from typing import Any, Final from mypy.lookup import lookup_fully_qualified from mypy.nodes import ( diff --git a/mypy/ipc.py b/mypy/ipc.py index 21ef61918de5..d026f2429a0f 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -12,8 +12,7 @@ import sys import tempfile from types import TracebackType -from typing import Callable -from typing_extensions import Final +from typing import Callable, Final if sys.platform == "win32": # This may be private, but it is needed for IPC on Windows, and is basically stable diff --git a/mypy/literals.py b/mypy/literals.py index 53ba559c56bb..cba5712644be 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -1,7 +1,7 @@ from __future__ import annotations -from typing import Any, Iterable, Optional, Tuple -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Final, Iterable, Optional, Tuple +from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( LITERAL_NO, diff --git a/mypy/main.py b/mypy/main.py index 9d95bb6cb1f6..f6e617e4d84f 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -8,8 +8,7 @@ import sys import time from gettext import gettext -from typing import IO, Any, NoReturn, Sequence, TextIO -from typing_extensions import Final +from typing import IO, Any, Final, NoReturn, Sequence, TextIO from mypy import build, defaults, state, util from mypy.config_parser import get_config_module_names, parse_config_file, parse_version diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 4e08f0dab5ed..bd3b8571b69e 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -8,8 +8,7 @@ from __future__ import annotations -from typing import NamedTuple -from typing_extensions import Final +from typing import Final, NamedTuple from mypy import errorcodes as codes diff --git a/mypy/messages.py b/mypy/messages.py index 8e59e7329168..021ad2c7390c 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -16,8 +16,7 @@ import re from contextlib import contextmanager from textwrap import dedent -from typing import Any, Callable, Collection, Iterable, Iterator, List, Sequence, cast -from typing_extensions import Final +from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, Sequence, cast import mypy.typeops from mypy import errorcodes as codes, message_registry diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index e0406bffcc7b..c780015c639d 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -21,8 +21,8 @@ else: import tomli as tomllib -from typing import Dict, List, NamedTuple, Optional, Tuple, Union -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union +from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo from mypy.fscache import FileSystemCache diff --git a/mypy/nodes.py b/mypy/nodes.py index 212ddb5def37..eb093f5d45b0 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -11,6 +11,7 @@ Any, Callable, Dict, + Final, Iterator, List, Optional, @@ -20,7 +21,7 @@ Union, cast, ) -from typing_extensions import Final, TypeAlias as _TypeAlias, TypeGuard +from typing_extensions import TypeAlias as _TypeAlias, TypeGuard from mypy_extensions import trait diff --git a/mypy/operators.py b/mypy/operators.py index 2b383ef199bb..07ec5a24fa77 100644 --- a/mypy/operators.py +++ b/mypy/operators.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final # Map from binary operator id to related method name (in Python 3). op_methods: Final = { diff --git a/mypy/options.py b/mypy/options.py index daa666dc7638..75343acd38bb 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -4,8 +4,7 @@ import re import sys import sysconfig -from typing import Any, Callable, Dict, Mapping, Pattern -from typing_extensions import Final +from typing import Any, Callable, Final, Mapping, Pattern from mypy import defaults from mypy.errorcodes import ErrorCode, error_codes @@ -529,7 +528,7 @@ def compile_glob(self, s: str) -> Pattern[str]: return re.compile(expr + "\\Z") def select_options_affecting_cache(self) -> Mapping[str, object]: - result: Dict[str, object] = {} + result: dict[str, object] = {} for opt in OPTIONS_AFFECTING_CACHE: val = getattr(self, opt) if opt in ("disabled_error_codes", "enabled_error_codes"): diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index e05a8e44b2f8..a6a383405ac6 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -4,8 +4,8 @@ from collections import defaultdict from functools import reduce -from typing import Iterable, List, Mapping, cast -from typing_extensions import Final, Literal +from typing import Final, Iterable, List, Mapping, cast +from typing_extensions import Literal import mypy.plugin # To avoid circular imports. from mypy.applytype import apply_generic_arguments diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index efa1338962a0..7b3795b91a74 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,8 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Iterator, Optional -from typing_extensions import Final +from typing import TYPE_CHECKING, Final, Iterator from mypy import errorcodes, message_registry from mypy.expandtype import expand_type, expand_type_by_instance @@ -134,7 +133,7 @@ def to_argument(self, current_info: TypeInfo) -> Argument: kind=arg_kind, ) - def expand_type(self, current_info: TypeInfo) -> Optional[Type]: + def expand_type(self, current_info: TypeInfo) -> Type | None: if self.type is not None and self.info.self_type is not None: # In general, it is not safe to call `expand_type()` during semantic analyzis, # however this plugin is called very late, so all types should be fully ready. diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 1acf42d11ee6..7869a8b5cdfa 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -12,8 +12,7 @@ """ from __future__ import annotations -from typing import Iterable, Sequence, TypeVar, cast -from typing_extensions import Final +from typing import Final, Iterable, Sequence, TypeVar, cast import mypy.plugin # To avoid circular imports. from mypy.nodes import TypeInfo diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index eba4d77f2343..0aa2824c9b51 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -1,8 +1,7 @@ """Plugin for supporting the functools standard library module.""" from __future__ import annotations -from typing import NamedTuple -from typing_extensions import Final +from typing import Final, NamedTuple import mypy.plugin from mypy.nodes import ARG_POS, ARG_STAR2, Argument, FuncItem, Var diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index a44493f900b1..c5ce20233a0a 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -1,7 +1,7 @@ from __future__ import annotations -from typing import NamedTuple, Sequence, TypeVar, Union -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Final, NamedTuple, Sequence, TypeVar, Union +from typing_extensions import TypeAlias as _TypeAlias from mypy.messages import format_type from mypy.nodes import ARG_POS, Argument, Block, ClassDef, Context, SymbolTable, TypeInfo, Var diff --git a/mypy/reachability.py b/mypy/reachability.py index 8602fc645e2b..a25b9dff4581 100644 --- a/mypy/reachability.py +++ b/mypy/reachability.py @@ -2,8 +2,7 @@ from __future__ import annotations -from typing import Tuple, TypeVar -from typing_extensions import Final +from typing import Final, Tuple, TypeVar from mypy.literals import literal from mypy.nodes import ( diff --git a/mypy/renaming.py b/mypy/renaming.py index 2fa3ef168a66..c960eb4b1ce8 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -1,8 +1,7 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Iterator -from typing_extensions import Final +from typing import Final, Iterator from mypy.nodes import ( AssignmentStmt, diff --git a/mypy/report.py b/mypy/report.py index 81d49baf50da..5d93351aa37d 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -12,8 +12,8 @@ import tokenize from abc import ABCMeta, abstractmethod from operator import attrgetter -from typing import Any, Callable, Dict, Iterator, Tuple -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Callable, Dict, Final, Iterator, Tuple +from typing_extensions import TypeAlias as _TypeAlias from urllib.request import pathname2url from mypy import stats diff --git a/mypy/semanal.py b/mypy/semanal.py index 25393096bc5f..75d41b344698 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -51,8 +51,8 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Any, Callable, Collection, Iterable, Iterator, List, TypeVar, cast -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, TypeVar, cast +from typing_extensions import TypeAlias as _TypeAlias from mypy import errorcodes as codes, message_registry from mypy.constant_fold import constant_fold_expr diff --git a/mypy/semanal_classprop.py b/mypy/semanal_classprop.py index 3f5bc9c4c2de..dfd4e5b6f122 100644 --- a/mypy/semanal_classprop.py +++ b/mypy/semanal_classprop.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final from mypy.errors import Errors from mypy.nodes import ( diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index f8d321ffada9..cd11204c3bcc 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -5,8 +5,7 @@ from __future__ import annotations -from typing import cast -from typing_extensions import Final +from typing import Final, cast from mypy.nodes import ( ARG_NAMED, diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index d7e9712fa427..51a7014fac1a 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -27,8 +27,8 @@ from __future__ import annotations from contextlib import nullcontext -from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import TYPE_CHECKING, Callable, Final, List, Optional, Tuple, Union +from typing_extensions import TypeAlias as _TypeAlias import mypy.build import mypy.state diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 5eac02833d24..42f7b10f3333 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -6,8 +6,7 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Iterator, List, Mapping, cast -from typing_extensions import Final +from typing import Final, Iterator, List, Mapping, cast from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.nodes import ( diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index d097e1fb08dc..425e5906926a 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -3,8 +3,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import Callable, overload -from typing_extensions import Final, Literal, Protocol +from typing import Callable, Final, overload +from typing_extensions import Literal, Protocol from mypy_extensions import trait diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 47a3f558aa13..aba5bf69b130 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final from mypy import errorcodes as codes, message_registry from mypy.errorcodes import ErrorCode diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index ef6f5b86c8f3..6f044a5ea8b9 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final from mypy.nodes import Decorator, FakeInfo, FuncDef, SymbolNode, Var from mypy.server.objgraph import get_path, get_reachable_graph diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index 37d0f4d2ca1a..a13fd8412934 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -5,8 +5,7 @@ import types import weakref from collections.abc import Iterable -from typing import Iterator, Mapping -from typing_extensions import Final +from typing import Final, Iterator, Mapping method_descriptor_type: Final = type(object.__dir__) method_wrapper_type: Final = type(object().__ne__) diff --git a/mypy/server/trigger.py b/mypy/server/trigger.py index 5f2115739d38..97b5f89cd3ba 100644 --- a/mypy/server/trigger.py +++ b/mypy/server/trigger.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final # Used as a suffix for triggers to handle "from m import *" dependencies (see also # make_wildcard_trigger) diff --git a/mypy/server/update.py b/mypy/server/update.py index dafed6642d1e..0cc7a2229514 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -118,8 +118,8 @@ import re import sys import time -from typing import Callable, NamedTuple, Sequence, Union -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Callable, Final, NamedTuple, Sequence, Union +from typing_extensions import TypeAlias as _TypeAlias from mypy.build import ( DEBUG_FINE_GRAINED, diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 6f864ccce816..ef2e4f720664 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final """Shared logic between our three mypy parser files.""" diff --git a/mypy/state.py b/mypy/state.py index 2e44a936f819..cd3a360dd15f 100644 --- a/mypy/state.py +++ b/mypy/state.py @@ -1,8 +1,7 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Iterator -from typing_extensions import Final +from typing import Final, Iterator # These are global mutable state. Don't add anything here unless there's a very # good reason. diff --git a/mypy/stats.py b/mypy/stats.py index 5f4b9d4d201f..b8803e03b9d2 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -5,8 +5,7 @@ import os from collections import Counter from contextlib import contextmanager -from typing import Iterator -from typing_extensions import Final +from typing import Final, Iterator from mypy import nodes from mypy.argmap import map_formals_to_actuals diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 7c8751bbd6ed..232d9e9762e9 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -10,8 +10,8 @@ import io import re import tokenize -from typing import Any, MutableMapping, MutableSequence, NamedTuple, Sequence, Tuple -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Final, MutableMapping, MutableSequence, NamedTuple, Sequence, Tuple +from typing_extensions import TypeAlias as _TypeAlias # Type alias for signatures strings in format ('func_name', '(arg, opt_arg=False)'). Sig: _TypeAlias = Tuple[str, str] diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ba71456af4a4..229559ac8120 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -49,8 +49,7 @@ import sys import traceback from collections import defaultdict -from typing import Iterable, Mapping -from typing_extensions import Final +from typing import Final, Iterable, Mapping import mypy.build import mypy.mixedtraverser diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 4fc9f8c6fdfa..8aa1fb3d2c0a 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -12,8 +12,7 @@ import re from abc import abstractmethod from types import ModuleType -from typing import Any, Iterable, Mapping -from typing_extensions import Final +from typing import Any, Final, Iterable, Mapping from mypy.moduleinspect import is_c_module from mypy.stubdoc import ( @@ -502,7 +501,7 @@ def generate_c_type_stub( """ raw_lookup = getattr(obj, "__dict__") # noqa: B009 items = sorted(get_members(obj), key=lambda x: method_name_sort_key(x[0])) - names = set(x[0] for x in items) + names = {x[0] for x in items} methods: list[str] = [] types: list[str] = [] static_properties: list[str] = [] diff --git a/mypy/subtypes.py b/mypy/subtypes.py index c9de56edfa36..a6dc071f92b0 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1,8 +1,8 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Any, Callable, Iterator, List, TypeVar, cast -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Callable, Final, Iterator, List, TypeVar, cast +from typing_extensions import TypeAlias as _TypeAlias import mypy.applytype import mypy.constraints diff --git a/mypy/test/data.py b/mypy/test/data.py index 9b3931ee8be6..940776bf7b19 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -12,8 +12,8 @@ from abc import abstractmethod from dataclasses import dataclass from pathlib import Path -from typing import Any, Iterator, NamedTuple, NoReturn, Pattern, Union -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Any, Final, Iterator, NamedTuple, NoReturn, Pattern, Union +from typing_extensions import TypeAlias as _TypeAlias import pytest diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 2efae49e9e10..cbfa43a77b81 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -14,8 +14,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Any, Callable, Generic, Iterable, Sequence, TypeVar, cast -from typing_extensions import Final +from typing import Any, Callable, Final, Generic, Iterable, Sequence, TypeVar, cast from mypy_extensions import mypyc_attr, trait diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d577f355dbc8..d894e2cc8c51 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -4,8 +4,8 @@ import itertools from contextlib import contextmanager -from typing import Callable, Iterable, Iterator, List, Sequence, Tuple, TypeVar -from typing_extensions import Final, Protocol +from typing import Callable, Final, Iterable, Iterator, List, Sequence, Tuple, TypeVar +from typing_extensions import Protocol from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode diff --git a/mypy/types.py b/mypy/types.py index 131383790ec8..784ef538f197 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -9,6 +9,7 @@ Any, ClassVar, Dict, + Final, Iterable, NamedTuple, NewType, @@ -17,7 +18,7 @@ Union, cast, ) -from typing_extensions import Final, Self, TypeAlias as _TypeAlias, TypeGuard, overload +from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard, overload import mypy.nodes from mypy.bogus_type import Bogus diff --git a/mypy/typestate.py b/mypy/typestate.py index ff5933af5928..b32fb0ef6df1 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -5,8 +5,8 @@ from __future__ import annotations -from typing import Dict, Set, Tuple -from typing_extensions import Final, TypeAlias as _TypeAlias +from typing import Dict, Final, Set, Tuple +from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import TypeInfo from mypy.server.trigger import make_trigger diff --git a/mypy/util.py b/mypy/util.py index 2960818d0984..268ba8f9de81 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -11,8 +11,8 @@ import sys import time from importlib import resources as importlib_resources -from typing import IO, Callable, Container, Iterable, Sequence, Sized, TypeVar -from typing_extensions import Final, Literal +from typing import IO, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar +from typing_extensions import Literal try: import curses diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py index 02e02a82a4f9..350158246cdb 100644 --- a/mypyc/analysis/attrdefined.py +++ b/mypyc/analysis/attrdefined.py @@ -63,8 +63,7 @@ def foo(self) -> int: from __future__ import annotations -from typing import Set, Tuple -from typing_extensions import Final +from typing import Final, Set, Tuple from mypyc.analysis.dataflow import ( CFG, @@ -414,7 +413,7 @@ def update_always_defined_attrs_using_subclasses(cl: ClassIR, seen: set[ClassIR] seen.add(cl) -def detect_undefined_bitmap(cl: ClassIR, seen: Set[ClassIR]) -> None: +def detect_undefined_bitmap(cl: ClassIR, seen: set[ClassIR]) -> None: if cl.is_trait: return diff --git a/mypyc/codegen/cstring.py b/mypyc/codegen/cstring.py index e006f12e09ec..853787f8161d 100644 --- a/mypyc/codegen/cstring.py +++ b/mypyc/codegen/cstring.py @@ -21,7 +21,7 @@ from __future__ import annotations import string -from typing_extensions import Final +from typing import Final CHAR_MAP: Final = [f"\\{i:03o}" for i in range(256)] diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 8f0e0bc65edc..1bd376754ab9 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -5,8 +5,7 @@ import pprint import sys import textwrap -from typing import Callable -from typing_extensions import Final +from typing import Callable, Final from mypyc.codegen.literals import Literals from mypyc.common import ( @@ -926,7 +925,7 @@ def emit_unbox( elif is_float_rprimitive(typ): assert not optional # Not supported for overlapping error values if declare_dest: - self.emit_line("double {};".format(dest)) + self.emit_line(f"double {dest};") # TODO: Don't use __float__ and __index__ self.emit_line(f"{dest} = PyFloat_AsDouble({src});") self.emit_lines(f"if ({dest} == -1.0 && PyErr_Occurred()) {{", failure, "}") diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 7e6d775d74b4..b4d31544b196 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Final +from typing import Final from mypyc.analysis.blockfreq import frequently_executed_blocks from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer @@ -686,10 +686,10 @@ def visit_float_op(self, op: FloatOp) -> None: lhs = self.reg(op.lhs) rhs = self.reg(op.rhs) if op.op != FloatOp.MOD: - self.emit_line("%s = %s %s %s;" % (dest, lhs, op.op_str[op.op], rhs)) + self.emit_line(f"{dest} = {lhs} {op.op_str[op.op]} {rhs};") else: # TODO: This may set errno as a side effect, that is a little sketchy. - self.emit_line("%s = fmod(%s, %s);" % (dest, lhs, rhs)) + self.emit_line(f"{dest} = fmod({lhs}, {rhs});") def visit_float_neg(self, op: FloatNeg) -> None: dest = self.reg(op) @@ -700,7 +700,7 @@ def visit_float_comparison_op(self, op: FloatComparisonOp) -> None: dest = self.reg(op) lhs = self.reg(op.lhs) rhs = self.reg(op.rhs) - self.emit_line("%s = %s %s %s;" % (dest, lhs, op.op_str[op.op], rhs)) + self.emit_line(f"{dest} = {lhs} {op.op_str[op.op]} {rhs};") def visit_load_mem(self, op: LoadMem) -> None: dest = self.reg(op) diff --git a/mypyc/codegen/literals.py b/mypyc/codegen/literals.py index 8f84089221c3..1f0c3bc6ec7b 100644 --- a/mypyc/codegen/literals.py +++ b/mypyc/codegen/literals.py @@ -1,7 +1,7 @@ from __future__ import annotations -from typing import FrozenSet, List, Tuple, Union -from typing_extensions import Final, TypeGuard +from typing import Final, FrozenSet, Tuple, Union +from typing_extensions import TypeGuard # Supported Python literal types. All tuple / frozenset items must have supported # literal types as well, but we can't represent the type precisely. @@ -140,7 +140,7 @@ def encoded_complex_values(self) -> list[str]: def encoded_tuple_values(self) -> list[str]: return self._encode_collection_values(self.tuple_literals) - def encoded_frozenset_values(self) -> List[str]: + def encoded_frozenset_values(self) -> list[str]: return self._encode_collection_values(self.frozenset_literals) def _encode_collection_values( diff --git a/mypyc/common.py b/mypyc/common.py index 05e13370cb98..4615bf30d742 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -2,8 +2,7 @@ import sys import sysconfig -from typing import Any, Dict -from typing_extensions import Final +from typing import Any, Dict, Final from mypy.util import unnamed_function diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index a5ac2133ce13..682e30629118 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, NamedTuple, Optional +from typing import List, NamedTuple from mypyc.common import PROPSET_PREFIX, JsonDict from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature @@ -73,7 +73,7 @@ class VTableMethod(NamedTuple): cls: "ClassIR" name: str method: FuncIR - shadow_method: Optional[FuncIR] + shadow_method: FuncIR | None VTableEntries = List[VTableMethod] @@ -192,7 +192,7 @@ def __init__( # bitmap for types such as native ints that can't have a dedicated error # value that doesn't overlap a valid value. The bitmap is used if the # value of an attribute is the same as the error value. - self.bitmap_attrs: List[str] = [] + self.bitmap_attrs: list[str] = [] def __repr__(self) -> str: return ( diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index dbb45fc7ec29..44847c7bb0b3 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -2,8 +2,7 @@ from __future__ import annotations -from typing import Sequence -from typing_extensions import Final +from typing import Final, Sequence from mypy.nodes import ARG_POS, ArgKind, Block, FuncDef from mypyc.common import BITMAP_BITS, JsonDict, bitmap_name, get_id_from_name, short_id_from_name @@ -86,7 +85,7 @@ def real_args(self) -> tuple[RuntimeArg, ...]: return self.args[: -self.num_bitmap_args] return self.args - def bound_sig(self) -> "FuncSignature": + def bound_sig(self) -> FuncSignature: if self.num_bitmap_args: return FuncSignature(self.args[1 : -self.num_bitmap_args], self.ret_type) else: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6007f8a4ce04..cb8d9662820c 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -12,8 +12,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, Dict, Generic, List, NamedTuple, Sequence, TypeVar, Union -from typing_extensions import Final +from typing import TYPE_CHECKING, Final, Generic, List, NamedTuple, Sequence, TypeVar, Union from mypy_extensions import trait @@ -1204,10 +1203,10 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: self.rhs = rhs self.op = op - def sources(self) -> List[Value]: + def sources(self) -> list[Value]: return [self.lhs, self.rhs] - def accept(self, visitor: "OpVisitor[T]") -> T: + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_op(self) @@ -1226,10 +1225,10 @@ def __init__(self, src: Value, line: int = -1) -> None: self.type = float_rprimitive self.src = src - def sources(self) -> List[Value]: + def sources(self) -> list[Value]: return [self.src] - def accept(self, visitor: "OpVisitor[T]") -> T: + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_neg(self) @@ -1254,10 +1253,10 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: self.rhs = rhs self.op = op - def sources(self) -> List[Value]: + def sources(self) -> list[Value]: return [self.lhs, self.rhs] - def accept(self, visitor: "OpVisitor[T]") -> T: + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_comparison_op(self) @@ -1575,5 +1574,5 @@ def visit_keep_alive(self, op: KeepAlive) -> T: # (Serialization and deserialization *will* be used for incremental # compilation but so far it is not hooked up to anything.) class DeserMaps(NamedTuple): - classes: Dict[str, "ClassIR"] - functions: Dict[str, "FuncIR"] + classes: dict[str, "ClassIR"] + functions: dict[str, "FuncIR"] diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 4d10a91835ca..c86060c49594 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -3,8 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Sequence, Union -from typing_extensions import Final +from typing import Any, Final, Sequence, Union from mypyc.common import short_name from mypyc.ir.func_ir import FuncIR, all_values_full diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 61f83c9c041e..fe0c51ea2221 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -23,8 +23,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar -from typing_extensions import Final +from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 86c28882d738..10f057a29bbb 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -13,8 +13,8 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Any, Callable, Iterator, Sequence, Union -from typing_extensions import Final, overload +from typing import Any, Callable, Final, Iterator, Sequence, Union +from typing_extensions import overload from mypy.build import Graph from mypy.maptype import map_instance_to_supertype diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index ef8db97c818e..fc2bb4a1fc2f 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -4,8 +4,7 @@ import typing_extensions from abc import abstractmethod -from typing import Callable -from typing_extensions import Final +from typing import Callable, Final from mypy.nodes import ( AssignmentStmt, diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index dc21be4689e2..12a4b15dd40c 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -10,8 +10,7 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Final +from typing import Final, Union from mypy.constant_fold import constant_fold_binary_op, constant_fold_unary_op from mypy.nodes import ( diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 480c683aa164..0b46887811fb 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -3,7 +3,7 @@ from __future__ import annotations from enum import Enum, unique -from typing_extensions import Final +from typing import Final from mypy.checkstrformat import ( ConversionSpecifier, diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a37bcc0dbb86..e34f03704047 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -10,8 +10,7 @@ from __future__ import annotations -from typing import Callable, Optional, Sequence, Tuple -from typing_extensions import Final +from typing import Callable, Final, Optional, Sequence, Tuple from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, ArgKind diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 06af4d6d7426..2f22b4bfc9d2 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -755,7 +755,7 @@ def translate_bool(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value @specialize_function("builtins.float") -def translate_float(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: +def translate_float(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: return None arg = expr.args[0] diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 1e2cf2695ee7..aa96b35aec56 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -37,8 +37,7 @@ from __future__ import annotations -from typing import List, NamedTuple, Optional, Tuple -from typing_extensions import Final +from typing import Final, NamedTuple from mypyc.ir.ops import StealsDescription from mypyc.ir.rtypes import RType @@ -50,16 +49,16 @@ class CFunctionDescription(NamedTuple): name: str - arg_types: List[RType] + arg_types: list[RType] return_type: RType - var_arg_type: Optional[RType] - truncated_type: Optional[RType] + var_arg_type: RType | None + truncated_type: RType | None c_function_name: str error_kind: int steals: StealsDescription is_borrowed: bool - ordering: Optional[List[int]] - extra_int_constants: List[Tuple[int, RType]] + ordering: list[int] | None + extra_int_constants: list[tuple[int, RType]] priority: int diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index 5a4b1d0f549e..7f00ee5aea00 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -7,7 +7,7 @@ Any, Iterator, TypeVar, Generator, Optional, List, Tuple, Sequence, Union, Callable, Awaitable, ) -from typing_extensions import Final +from typing import Final FLOAT_MAGIC: Final = -113.0 From e86f097ee9dac7488ccd0d444182e33733710c42 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 3 Jul 2023 23:26:27 +0300 Subject: [PATCH 0092/1617] Target py3.8+ for black (#15583) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0f2712be52dd..1fc52df4bcb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 99 -target-version = ["py37", "py38", "py39", "py310", "py311"] +target-version = ["py38", "py39", "py310", "py311"] skip-magic-trailing-comma = true force-exclude = ''' ^/mypy/typeshed| From 781dc8f82adacce730293479517fd0fb5944c255 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 3 Jul 2023 22:59:27 +0100 Subject: [PATCH 0093/1617] [mypyc] Fix self-compilation on Python 3.12 (#15582) --- mypyc/build.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/mypyc/build.py b/mypyc/build.py index 5fc041e2dcf2..9889577d4add 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -40,23 +40,31 @@ from mypyc.namegen import exported_name from mypyc.options import CompilerOptions -if TYPE_CHECKING: - from distutils.core import Extension as _distutils_Extension - from typing_extensions import TypeAlias +if sys.version_info < (3, 12): + if TYPE_CHECKING: + from distutils.core import Extension as _distutils_Extension + from typing_extensions import TypeAlias - from setuptools import Extension as _setuptools_Extension + from setuptools import Extension as _setuptools_Extension - Extension: TypeAlias = Union[_setuptools_Extension, _distutils_Extension] + Extension: TypeAlias = Union[_setuptools_Extension, _distutils_Extension] - -try: - # Import setuptools so that it monkey-patch overrides distutils + try: + # Import setuptools so that it monkey-patch overrides distutils + import setuptools + except ImportError: + pass + from distutils import ccompiler, sysconfig +else: import setuptools -except ImportError: - if sys.version_info >= (3, 12): - # Raise on Python 3.12, since distutils will go away forever - raise -from distutils import ccompiler, sysconfig + from setuptools import Extension + from setuptools._distutils import ( + ccompiler as _ccompiler, # type: ignore[attr-defined] + sysconfig as _sysconfig, # type: ignore[attr-defined] + ) + + ccompiler = _ccompiler + sysconfig = _sysconfig def get_extension() -> type[Extension]: @@ -65,11 +73,13 @@ def get_extension() -> type[Extension]: use_setuptools = "setuptools" in sys.modules extension_class: type[Extension] - if not use_setuptools: + if sys.version_info < (3, 12) and not use_setuptools: import distutils.core extension_class = distutils.core.Extension else: + if not use_setuptools: + sys.exit("error: setuptools not installed") extension_class = setuptools.Extension return extension_class From cb0a44630a0608994116e3de3be07b6d3b14381a Mon Sep 17 00:00:00 2001 From: Federico Padua Date: Tue, 4 Jul 2023 16:28:49 +0200 Subject: [PATCH 0094/1617] Fix variable name in comment in `getting_started.rst` (#15587) This PR fixes a word within a code block of the `Getting Started/Types from libraries` documentation. In particular, the variable name `file_path` is used instead of the correct `template_path`. This commit fixes that and it replaces `file_path` with `template_path`. This PR doesn't change the behaviour of `mypy` code since it involves just a word change in the documentation, so no tests are provided and it is expected that current working code works as usual, given the change made by this PR. --- docs/source/getting_started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 11f915005695..463c73b2fe76 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -264,7 +264,7 @@ Python standard library. For example, here is a function which uses the from pathlib import Path def load_template(template_path: Path, name: str) -> str: - # Mypy knows that `file_path` has a `read_text` method that returns a str + # Mypy knows that `template_path` has a `read_text` method that returns a str template = template_path.read_text() # ...so it understands this line type checks return template.replace('USERNAME', name) From 913477ee68094b137d3a70f4724a460db1400a7e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 4 Jul 2023 17:00:04 +0200 Subject: [PATCH 0095/1617] Target py38 for ruff (#15585) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1fc52df4bcb8..67201acb9b94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ force-exclude = ''' [tool.ruff] line-length = 99 -target-version = "py37" +target-version = "py38" fix = true select = [ From 85ba5745afcc646c1728dbb690bfb196537abe04 Mon Sep 17 00:00:00 2001 From: Erik Kemperman Date: Tue, 4 Jul 2023 20:17:31 +0200 Subject: [PATCH 0096/1617] Handle type same as typing.Type in classmethod 1st arg (#15297) --- mypy/semanal.py | 16 ++++++++++++- test-data/unit/check-selftype.test | 36 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 75d41b344698..1f15691ae205 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1008,7 +1008,21 @@ def is_expected_self_type(self, typ: Type, is_classmethod: bool) -> bool: return self.is_expected_self_type(typ.item, is_classmethod=False) if isinstance(typ, UnboundType): sym = self.lookup_qualified(typ.name, typ, suppress_errors=True) - if sym is not None and sym.fullname == "typing.Type" and typ.args: + if ( + sym is not None + and ( + sym.fullname == "typing.Type" + or ( + sym.fullname == "builtins.type" + and ( + self.is_stub_file + or self.is_future_flag_set("annotations") + or self.options.python_version >= (3, 9) + ) + ) + ) + and typ.args + ): return self.is_expected_self_type(typ.args[0], is_classmethod=False) return False if isinstance(typ, TypeVarType): diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 96d5b2306427..4155e3343851 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1665,6 +1665,23 @@ class C: return cls() [builtins fixtures/classmethod.pyi] +[case testTypingSelfRedundantAllowed_pep585] +# flags: --python-version 3.9 +from typing import Self + +class C: + def f(self: Self) -> Self: + d: Defer + class Defer: ... + return self + + @classmethod + def g(cls: type[Self]) -> Self: + d: DeferAgain + class DeferAgain: ... + return cls() +[builtins fixtures/classmethod.pyi] + [case testTypingSelfRedundantWarning] # mypy: enable-error-code="redundant-self" @@ -1683,6 +1700,25 @@ class C: return cls() [builtins fixtures/classmethod.pyi] +[case testTypingSelfRedundantWarning_pep585] +# flags: --python-version 3.9 +# mypy: enable-error-code="redundant-self" + +from typing import Self + +class C: + def copy(self: Self) -> Self: # E: Redundant "Self" annotation for the first method argument + d: Defer + class Defer: ... + return self + + @classmethod + def g(cls: type[Self]) -> Self: # E: Redundant "Self" annotation for the first method argument + d: DeferAgain + class DeferAgain: ... + return cls() +[builtins fixtures/classmethod.pyi] + [case testTypingSelfAssertType] from typing import Self, assert_type From 59ba4293679dbed20a6d67fc3eff88d8f98eda43 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:40:46 +0100 Subject: [PATCH 0097/1617] When sync-typeshed fails to apply CP allow manual user merges (#15591) If misc/sync-typeshed.py fails to apply a cherry pick, it just fails. Let's try to give the user a chance to manually merge the CP and continue with the script. This should block for user input only in cases where stdin is a tty. So automation should continue failing. (but the only way to test is by running it) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alex Waygood --- misc/sync-typeshed.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index a8aca425696a..cbaa7029620f 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -185,7 +185,21 @@ def main() -> None: "9f3bbbeb1", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: - subprocess.run(["git", "cherry-pick", commit], check=True) + try: + subprocess.run(["git", "cherry-pick", commit], check=True) + except subprocess.CalledProcessError: + if not sys.__stdin__.isatty(): + # We're in an automated context + raise + + # Allow the option to merge manually + print( + f"Commit {commit} failed to cherry pick." + " In a separate shell, please manually merge and continue cherry pick." + ) + rsp = input("Did you finish the cherry pick? [y/N]: ") + if rsp.lower() not in {"y", "yes"}: + raise print(f"Cherry-picked {commit}.") if args.make_pr: From c44fe1a6bf09413d0dd74a354e479e8b041c4a04 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 4 Jul 2023 18:18:52 +0100 Subject: [PATCH 0098/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/fc7d4722eaa54803926cee5730e1f784979c0531 --- mypy/typeshed/stdlib/_collections_abc.pyi | 2 +- mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/asyncio/__init__.pyi | 4 +- mypy/typeshed/stdlib/asyncio/base_events.pyi | 4 +- mypy/typeshed/stdlib/asyncio/events.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 103 +++++++++++++++++- mypy/typeshed/stdlib/codecs.pyi | 1 + mypy/typeshed/stdlib/collections/__init__.pyi | 10 +- mypy/typeshed/stdlib/email/utils.pyi | 9 +- mypy/typeshed/stdlib/enum.pyi | 11 +- mypy/typeshed/stdlib/functools.pyi | 40 ++++--- mypy/typeshed/stdlib/http/client.pyi | 39 ++++--- mypy/typeshed/stdlib/inspect.pyi | 30 ++++- mypy/typeshed/stdlib/pydoc.pyi | 30 +++-- mypy/typeshed/stdlib/socket.pyi | 2 + mypy/typeshed/stdlib/tempfile.pyi | 1 + mypy/typeshed/stdlib/types.pyi | 45 ++++---- mypy/typeshed/stdlib/typing.pyi | 41 ++++--- mypy/typeshed/stdlib/unittest/mock.pyi | 5 +- 19 files changed, 290 insertions(+), 97 deletions(-) diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 05b5421c21f3..ba2f638d81c9 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from types import MappingProxyType -from typing import ( # noqa: Y022,Y038 +from typing import ( # noqa: Y022,Y038,Y057 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 756ee86d3342..25d604218a00 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -151,7 +151,11 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index 2ce066cac982..c11465184389 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -36,8 +36,8 @@ _T = TypeVar("_T") # Aliases imported by multiple submodules in typeshed if sys.version_info >= (3, 12): - _AwaitableLike: TypeAlias = Awaitable[_T] - _CoroutineLike: TypeAlias = Coroutine[Any, Any, _T] + _AwaitableLike: TypeAlias = Awaitable[_T] # noqa: Y047 + _CoroutineLike: TypeAlias = Coroutine[Any, Any, _T] # noqa: Y047 else: _AwaitableLike: TypeAlias = Generator[Any, None, _T] | Awaitable[_T] _CoroutineLike: TypeAlias = Generator[Any, None, _T] | Coroutine[Any, Any, _T] diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index fd765fdb0614..9924f728f6ea 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -263,7 +263,7 @@ class BaseEventLoop(AbstractEventLoop): server_hostname: str | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport: ... + ) -> Transport | None: ... async def connect_accepted_socket( self, protocol_factory: Callable[[], _ProtocolT], @@ -317,7 +317,7 @@ class BaseEventLoop(AbstractEventLoop): server_side: bool = False, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None, - ) -> Transport: ... + ) -> Transport | None: ... async def connect_accepted_socket( self, protocol_factory: Callable[[], _ProtocolT], diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 11112bb2e87d..2054f6e522a1 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -358,7 +358,7 @@ class AbstractEventLoop: server_hostname: str | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport: ... + ) -> Transport | None: ... async def create_unix_server( self, protocol_factory: _ProtocolFactory, @@ -418,7 +418,7 @@ class AbstractEventLoop: server_side: bool = False, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None, - ) -> Transport: ... + ) -> Transport | None: ... async def create_unix_server( self, protocol_factory: _ProtocolFactory, diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 4337a44c7c68..45a17b33d979 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -53,9 +53,10 @@ from typing import ( # noqa: Y022 overload, type_check_only, ) -from typing_extensions import ( # type: ignore +from typing_extensions import ( Concatenate, Literal, + LiteralString, ParamSpec, Self, SupportsIndex, @@ -432,8 +433,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -441,12 +451,21 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: + @overload + def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - def format(self, *args: object, **kwargs: object) -> str: ... # type: ignore + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload + def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def isalnum(self) -> bool: ... @@ -461,32 +480,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -497,6 +575,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -504,13 +585,25 @@ class str(Sequence[str]): def __ge__(self, __value: str) -> bool: ... def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - def __mod__(self, __value: Any) -> str: ... # type: ignore + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload + def __mod__(self, __value: Any) -> str: ... + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -1665,11 +1758,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 3f6d2d3d16b7..c9b6a4a82da6 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -96,6 +96,7 @@ class _IncrementalDecoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalDecoder: ... class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): + _is_text_encoding: bool @property def encode(self) -> _Encoder: ... @property diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index d5ca17c749eb..e56baf8b52c9 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -1,6 +1,6 @@ import sys from _collections_abc import dict_items, dict_keys, dict_values -from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT +from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from typing import Any, Generic, NoReturn, TypeVar, overload from typing_extensions import Self, SupportsIndex, final @@ -299,10 +299,10 @@ class Counter(dict[_T, int], Generic[_T]): def __pos__(self) -> Counter[_T]: ... def __neg__(self) -> Counter[_T]: ... # several type: ignores because __iadd__ is supposedly incompatible with __add__, etc. - def __iadd__(self, other: Counter[_T]) -> Self: ... # type: ignore[misc] - def __isub__(self, other: Counter[_T]) -> Self: ... - def __iand__(self, other: Counter[_T]) -> Self: ... - def __ior__(self, other: Counter[_T]) -> Self: ... # type: ignore[override,misc] + def __iadd__(self, other: SupportsItems[_T, int]) -> Self: ... # type: ignore[misc] + def __isub__(self, other: SupportsItems[_T, int]) -> Self: ... + def __iand__(self, other: SupportsItems[_T, int]) -> Self: ... + def __ior__(self, other: SupportsItems[_T, int]) -> Self: ... # type: ignore[override,misc] if sys.version_info >= (3, 10): def total(self) -> int: ... def __le__(self, other: Counter[Any]) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 090ddf9e31bc..ed63b6b32312 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -1,5 +1,6 @@ import datetime import sys +from _typeshed import Unused from email import _ParamType from email.charset import Charset from typing import overload @@ -51,7 +52,13 @@ else: def mktime_tz(data: _PDTZ) -> int: ... def formatdate(timeval: float | None = None, localtime: bool = False, usegmt: bool = False) -> str: ... def format_datetime(dt: datetime.datetime, usegmt: bool = False) -> str: ... -def localtime(dt: datetime.datetime | None = None, isdst: int = -1) -> datetime.datetime: ... + +if sys.version_info >= (3, 12): + def localtime(dt: datetime.datetime | None = None, isdst: Unused = None) -> datetime.datetime: ... + +else: + def localtime(dt: datetime.datetime | None = None, isdst: int = -1) -> datetime.datetime: ... + def make_msgid(idstring: str | None = None, domain: str | None = None) -> str: ... def decode_rfc2231(s: str) -> tuple[str | None, str | None, str]: ... def encode_rfc2231(s: str, charset: str | None = None, language: str | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 383c336ed2c7..96a96dbce10e 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -4,7 +4,7 @@ import types from _typeshed import SupportsKeysAndGetItem, Unused from abc import ABCMeta from builtins import property as _builtins_property -from collections.abc import Iterable, Iterator, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping from typing import Any, Generic, TypeVar, overload from typing_extensions import Literal, Self, TypeAlias @@ -34,6 +34,9 @@ if sys.version_info >= (3, 11): "verify", ] +if sys.version_info >= (3, 12): + __all__ += ["pickle_by_enum_name", "pickle_by_global_name"] + _EnumMemberT = TypeVar("_EnumMemberT") _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum]) @@ -234,6 +237,8 @@ if sys.version_info >= (3, 11): _value_: str @_magic_enum_attr def value(self) -> str: ... + @staticmethod + def _generate_next_value_(name: str, start: int, count: int, last_values: list[str]) -> str: ... class EnumCheck(StrEnum): CONTINUOUS: str @@ -289,3 +294,7 @@ class auto(IntFlag): @_magic_enum_attr def value(self) -> Any: ... def __new__(cls) -> Self: ... + +if sys.version_info >= (3, 12): + def pickle_by_global_name(self: Enum, proto: int) -> str: ... + def pickle_by_enum_name(self: _EnumMemberT, proto: int) -> tuple[Callable[..., Any], tuple[type[_EnumMemberT], str]]: ... diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 1b4e59b7c120..8adc3d82292e 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,10 +28,12 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -85,31 +87,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 9c7c0c1c4a12..41ece1b050b8 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -1,6 +1,7 @@ import email.message import io import ssl +import sys import types from _typeshed import ReadableBuffer, SupportsRead, WriteableBuffer from collections.abc import Callable, Iterable, Iterator, Mapping @@ -175,19 +176,31 @@ class HTTPConnection: class HTTPSConnection(HTTPConnection): # Can be `None` if `.connect()` was not called: sock: ssl.SSLSocket | Any - def __init__( - self, - host: str, - port: int | None = None, - key_file: str | None = None, - cert_file: str | None = None, - timeout: float | None = ..., - source_address: tuple[str, int] | None = None, - *, - context: ssl.SSLContext | None = None, - check_hostname: bool | None = None, - blocksize: int = 8192, - ) -> None: ... + if sys.version_info >= (3, 12): + def __init__( + self, + host: str, + port: str | None = None, + *, + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + context: ssl.SSLContext | None = None, + blocksize: int = 8192, + ) -> None: ... + else: + def __init__( + self, + host: str, + port: int | None = None, + key_file: str | None = None, + cert_file: str | None = None, + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + *, + context: ssl.SSLContext | None = None, + check_hostname: bool | None = None, + blocksize: int = 8192, + ) -> None: ... class HTTPException(Exception): ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 2d004a8e6b57..9af4c39bae9e 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -2,6 +2,7 @@ import dis import enum import sys import types +from _typeshed import StrPath from collections import OrderedDict from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet from types import ( @@ -127,8 +128,21 @@ if sys.version_info >= (3, 11): "walktree", ] + if sys.version_info >= (3, 12): + __all__ += [ + "markcoroutinefunction", + "AGEN_CLOSED", + "AGEN_CREATED", + "AGEN_RUNNING", + "AGEN_SUSPENDED", + "getasyncgenlocals", + "getasyncgenstate", + "BufferFlags", + ] + _P = ParamSpec("_P") _T = TypeVar("_T") +_F = TypeVar("_F", bound=Callable[..., Any]) _T_cont = TypeVar("_T_cont", contravariant=True) _V_cont = TypeVar("_V_cont", contravariant=True) @@ -177,12 +191,15 @@ if sys.version_info >= (3, 11): @overload def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... -def getmodulename(path: str) -> str | None: ... +def getmodulename(path: StrPath) -> str | None: ... def ismodule(object: object) -> TypeGuard[ModuleType]: ... def isclass(object: object) -> TypeGuard[type[Any]]: ... def ismethod(object: object) -> TypeGuard[MethodType]: ... def isfunction(object: object) -> TypeGuard[FunctionType]: ... +if sys.version_info >= (3, 12): + def markcoroutinefunction(func: _F) -> _F: ... + if sys.version_info >= (3, 8): @overload def isgeneratorfunction(obj: Callable[..., Generator[Any, Any, Any]]) -> bool: ... @@ -359,6 +376,17 @@ class _ParameterKind(enum.IntEnum): @property def description(self) -> str: ... +if sys.version_info >= (3, 12): + AGEN_CREATED: Literal["AGEN_CREATED"] + AGEN_RUNNING: Literal["AGEN_RUNNING"] + AGEN_SUSPENDED: Literal["AGEN_SUSPENDED"] + AGEN_CLOSED: Literal["AGEN_CLOSED"] + + def getasyncgenstate( + agen: AsyncGenerator[Any, Any] + ) -> Literal["AGEN_CREATED", "AGEN_RUNNING", "AGEN_SUSPENDED", "AGEN_CLOSED"]: ... + def getasyncgenlocals(agen: AsyncGeneratorType[Any, Any]) -> dict[str, Any]: ... + class Parameter: def __init__(self, name: str, kind: _ParameterKind, *, default: Any = ..., annotation: Any = ...) -> None: ... empty = _empty diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index ed97f1918e01..c993af390bbb 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -195,12 +195,24 @@ def resolve(thing: str | object, forceload: bool = ...) -> tuple[object, str] | def render_doc( thing: str | object, title: str = "Python Library Documentation: %s", forceload: bool = ..., renderer: Doc | None = None ) -> str: ... -def doc( - thing: str | object, - title: str = "Python Library Documentation: %s", - forceload: bool = ..., - output: SupportsWrite[str] | None = None, -) -> None: ... + +if sys.version_info >= (3, 12): + def doc( + thing: str | object, + title: str = "Python Library Documentation: %s", + forceload: bool = ..., + output: SupportsWrite[str] | None = None, + is_cli: bool = False, + ) -> None: ... + +else: + def doc( + thing: str | object, + title: str = "Python Library Documentation: %s", + forceload: bool = ..., + output: SupportsWrite[str] | None = None, + ) -> None: ... + def writedoc(thing: str | object, forceload: bool = ...) -> None: ... def writedocs(dir: str, pkgpath: str = "", done: Any | None = None) -> None: ... @@ -216,7 +228,11 @@ class Helper: def __call__(self, request: str | Helper | object = ...) -> None: ... def interact(self) -> None: ... def getline(self, prompt: str) -> str: ... - def help(self, request: Any) -> None: ... + if sys.version_info >= (3, 12): + def help(self, request: Any, is_cli: bool = False) -> None: ... + else: + def help(self, request: Any) -> None: ... + def intro(self) -> None: ... def list(self, items: _list[str], columns: int = 4, width: int = 80) -> None: ... def listkeywords(self) -> None: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index e1ffc573b52e..5dd92ec8e116 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -468,6 +468,8 @@ if sys.version_info >= (3, 12): ETHERTYPE_IPV6 as ETHERTYPE_IPV6, ETHERTYPE_VLAN as ETHERTYPE_VLAN, ) +if sys.version_info >= (3, 11) and sys.platform == "darwin": + from _socket import TCP_CONNECTION_INFO as TCP_CONNECTION_INFO # Re-exported from errno EBADF: int diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index b251f8b9d029..ea04303683b5 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -380,6 +380,7 @@ else: # It does not actually derive from IO[AnyStr], but it does mostly behave # like one. class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): + _file: IO[AnyStr] @property def encoding(self) -> str: ... # undocumented @property diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 6909c765cb7d..e5468ce4ed3c 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -17,7 +17,7 @@ from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping from typing import Any, ClassVar, Generic, Mapping, Protocol, TypeVar, overload # noqa: Y022 -from typing_extensions import Literal, ParamSpec, TypeVarTuple, final +from typing_extensions import Literal, ParamSpec, Self, TypeVarTuple, final __all__ = [ "FunctionType", @@ -63,11 +63,8 @@ if sys.version_info >= (3, 12): _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_T_co = TypeVar("_T_co", covariant=True) -_T_contra = TypeVar("_T_contra", contravariant=True) _KT = TypeVar("_KT") _VT_co = TypeVar("_VT_co", covariant=True) -_V_co = TypeVar("_V_co", covariant=True) @final class _Cell: @@ -351,27 +348,31 @@ class ModuleType: # using `builtins.__import__` or `importlib.import_module` less painful def __getattr__(self, name: str) -> Any: ... +_YieldT_co = TypeVar("_YieldT_co", covariant=True) +_SendT_contra = TypeVar("_SendT_contra", contravariant=True) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True) + @final -class GeneratorType(Generator[_T_co, _T_contra, _V_co]): +class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): @property - def gi_yieldfrom(self) -> GeneratorType[_T_co, _T_contra, Any] | None: ... + def gi_yieldfrom(self) -> GeneratorType[_YieldT_co, _SendT_contra, Any] | None: ... if sys.version_info >= (3, 11): @property def gi_suspended(self) -> bool: ... __name__: str __qualname__: str - def __iter__(self) -> GeneratorType[_T_co, _T_contra, _V_co]: ... - def __next__(self) -> _T_co: ... - def send(self, __arg: _T_contra) -> _T_co: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _YieldT_co: ... + def send(self, __arg: _SendT_contra) -> _YieldT_co: ... @overload def throw( self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... - ) -> _T_co: ... + ) -> _YieldT_co: ... @overload - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _T_co: ... + def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... @final -class AsyncGeneratorType(AsyncGenerator[_T_co, _T_contra]): +class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @property def ag_await(self) -> Awaitable[Any] | None: ... __name__: str @@ -380,21 +381,21 @@ class AsyncGeneratorType(AsyncGenerator[_T_co, _T_contra]): @property def ag_suspended(self) -> bool: ... - def __aiter__(self) -> AsyncGeneratorType[_T_co, _T_contra]: ... - def __anext__(self) -> Coroutine[Any, Any, _T_co]: ... - def asend(self, __val: _T_contra) -> Coroutine[Any, Any, _T_co]: ... + def __aiter__(self) -> Self: ... + def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... + def asend(self, __val: _SendT_contra) -> Coroutine[Any, Any, _YieldT_co]: ... @overload async def athrow( self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... - ) -> _T_co: ... + ) -> _YieldT_co: ... @overload - async def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _T_co: ... + async def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... def aclose(self) -> Coroutine[Any, Any, None]: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @final -class CoroutineType(Coroutine[_T_co, _T_contra, _V_co]): +class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): __name__: str __qualname__: str @property @@ -404,14 +405,14 @@ class CoroutineType(Coroutine[_T_co, _T_contra, _V_co]): def cr_suspended(self) -> bool: ... def close(self) -> None: ... - def __await__(self) -> Generator[Any, None, _V_co]: ... - def send(self, __arg: _T_contra) -> _T_co: ... + def __await__(self) -> Generator[Any, None, _ReturnT_co]: ... + def send(self, __arg: _SendT_contra) -> _YieldT_co: ... @overload def throw( self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... - ) -> _T_co: ... + ) -> _YieldT_co: ... @overload - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _T_co: ... + def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... class _StaticFunctionType: # Fictional type to correct the type of MethodType.__func__. diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 08d7da5e8622..2c5f820ea365 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -289,10 +289,8 @@ _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. -_V_co = TypeVar("_V_co", covariant=True) # Any type covariant containers. _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. -_T_contra = TypeVar("_T_contra", contravariant=True) # Ditto contravariant. _TC = TypeVar("_TC", bound=Type[object]) def no_type_check(arg: _F) -> _F: ... @@ -397,20 +395,24 @@ class Reversible(Iterable[_T_co], Protocol[_T_co]): @abstractmethod def __reversed__(self) -> Iterator[_T_co]: ... -class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]): - def __next__(self) -> _T_co: ... +_YieldT_co = TypeVar("_YieldT_co", covariant=True) +_SendT_contra = TypeVar("_SendT_contra", contravariant=True) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True) + +class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): + def __next__(self) -> _YieldT_co: ... @abstractmethod - def send(self, __value: _T_contra) -> _T_co: ... + def send(self, __value: _SendT_contra) -> _YieldT_co: ... @overload @abstractmethod def throw( self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None - ) -> _T_co: ... + ) -> _YieldT_co: ... @overload @abstractmethod - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _T_co: ... + def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _YieldT_co: ... def close(self) -> None: ... - def __iter__(self) -> Generator[_T_co, _T_contra, _V_co]: ... + def __iter__(self) -> Generator[_YieldT_co, _SendT_contra, _ReturnT_co]: ... @property def gi_code(self) -> CodeType: ... @property @@ -425,7 +427,7 @@ class Awaitable(Protocol[_T_co]): @abstractmethod def __await__(self) -> Generator[Any, None, _T_co]: ... -class Coroutine(Awaitable[_V_co], Generic[_T_co, _T_contra, _V_co]): +class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): __name__: str __qualname__: str @property @@ -437,15 +439,15 @@ class Coroutine(Awaitable[_V_co], Generic[_T_co, _T_contra, _V_co]): @property def cr_running(self) -> bool: ... @abstractmethod - def send(self, __value: _T_contra) -> _T_co: ... + def send(self, __value: _SendT_contra) -> _YieldT_co: ... @overload @abstractmethod def throw( self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None - ) -> _T_co: ... + ) -> _YieldT_co: ... @overload @abstractmethod - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _T_co: ... + def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _YieldT_co: ... @abstractmethod def close(self) -> None: ... @@ -453,7 +455,10 @@ class Coroutine(Awaitable[_V_co], Generic[_T_co, _T_contra, _V_co]): # The parameters correspond to Generator, but the 4th is the original type. @type_check_only class AwaitableGenerator( - Awaitable[_V_co], Generator[_T_co, _T_contra, _V_co], Generic[_T_co, _T_contra, _V_co, _S], metaclass=ABCMeta + Awaitable[_ReturnT_co], + Generator[_YieldT_co, _SendT_contra, _ReturnT_co], + Generic[_YieldT_co, _SendT_contra, _ReturnT_co, _S], + metaclass=ABCMeta, ): ... @runtime_checkable @@ -467,18 +472,18 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): def __anext__(self) -> Awaitable[_T_co]: ... def __aiter__(self) -> AsyncIterator[_T_co]: ... -class AsyncGenerator(AsyncIterator[_T_co], Generic[_T_co, _T_contra]): - def __anext__(self) -> Awaitable[_T_co]: ... +class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): + def __anext__(self) -> Awaitable[_YieldT_co]: ... @abstractmethod - def asend(self, __value: _T_contra) -> Awaitable[_T_co]: ... + def asend(self, __value: _SendT_contra) -> Awaitable[_YieldT_co]: ... @overload @abstractmethod def athrow( self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None - ) -> Awaitable[_T_co]: ... + ) -> Awaitable[_YieldT_co]: ... @overload @abstractmethod - def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> Awaitable[_T_co]: ... + def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> Awaitable[_YieldT_co]: ... def aclose(self) -> Awaitable[None]: ... @property def ag_await(self) -> Any: ... diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 1f554da52d5d..0ed0701cc450 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -3,13 +3,14 @@ from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, S from contextlib import _GeneratorContextManager from types import TracebackType from typing import Any, Generic, TypeVar, overload -from typing_extensions import Final, Literal, Self, TypeAlias +from typing_extensions import Final, Literal, ParamSpec, Self, TypeAlias _T = TypeVar("_T") _TT = TypeVar("_TT", bound=type[Any]) _R = TypeVar("_R") _F = TypeVar("_F", bound=Callable[..., Any]) _AF = TypeVar("_AF", bound=Callable[..., Coroutine[Any, Any, Any]]) +_P = ParamSpec("_P") if sys.version_info >= (3, 8): __all__ = ( @@ -234,7 +235,7 @@ class _patch(Generic[_T]): @overload def __call__(self, func: _TT) -> _TT: ... @overload - def __call__(self, func: Callable[..., _R]) -> Callable[..., _R]: ... + def __call__(self, func: Callable[_P, _R]) -> Callable[_P, _R]: ... if sys.version_info >= (3, 8): def decoration_helper( self, patched: _patch[Any], args: Sequence[Any], keywargs: Any From 9f4c0d8af2c0542f749563535de2c28da736a9a7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0099/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 45a17b33d979..58669c9b4500 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,7 +56,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, - LiteralString, ParamSpec, Self, SupportsIndex, @@ -433,17 +432,8 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -451,20 +441,11 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: - @overload - def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -480,91 +461,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -575,9 +497,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -585,25 +504,13 @@ class str(Sequence[str]): def __ge__(self, __value: str) -> bool: ... def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From 56f43433673fe97de14b903cbeb9bc940b67f09d Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0100/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 58669c9b4500..7415a1b7680d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1665,11 +1665,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... From 71c4269df306e53cb0eec7474dcde26e5a72d45e Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0101/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 25d604218a00..756ee86d3342 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -151,11 +151,7 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 186fbb18e1cb30c8e51b79fe2d193c15a49d4588 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0102/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 8adc3d82292e..1b4e59b7c120 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,12 +28,10 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -87,41 +85,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From a9f8df7cda032f637946bb2ea7a60f790f81350f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 4 Jul 2023 20:04:46 +0100 Subject: [PATCH 0103/1617] Remove `six` as a test dependency (#15589) --- mypyc/test-data/run-classes.test | 17 +++-------------- test-requirements.txt | 3 +-- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 268e07f6bde4..59617714f7e7 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1370,9 +1370,8 @@ except TypeError as e: [case testMetaclass] from meta import Meta -import six -class Nothing1(metaclass=Meta): +class Nothing(metaclass=Meta): pass def ident(x): return x @@ -1381,15 +1380,7 @@ def ident(x): return x class Test: pass -class Nothing2(six.with_metaclass(Meta, Test)): - pass - -@six.add_metaclass(Meta) -class Nothing3: - pass - [file meta.py] -from typing import Any class Meta(type): def __new__(mcs, name, bases, dct): dct['X'] = 10 @@ -1397,10 +1388,8 @@ class Meta(type): [file driver.py] -from native import Nothing1, Nothing2, Nothing3 -assert Nothing1.X == 10 -assert Nothing2.X == 10 -assert Nothing3.X == 10 +from native import Nothing +assert Nothing.X == 10 [case testPickling] from mypy_extensions import trait, mypyc_attr diff --git a/test-requirements.txt b/test-requirements.txt index 6b046e1469eb..661187823368 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,5 +14,4 @@ pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.0.272 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 -six -tomli>=1.1.0 +tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 From 1e14d13a4453a0a68909650faba86f683a1b90a5 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 4 Jul 2023 20:15:43 +0100 Subject: [PATCH 0104/1617] Update commits for sync-typeshed cherry picks (#15592) Got commits from https://github.com/python/mypy/commits/master after merging https://github.com/python/mypy/pull/15590 --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index cbaa7029620f..3f870d574d38 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "6f913a148", # LiteralString reverts - "475a46a78", # sum reverts - "f5e5c117d", # ctypes reverts - "9f3bbbeb1", # ParamSpec for functools.wraps + "9f4c0d8af", # LiteralString reverts + "56f434336", # sum reverts + "71c4269df", # ctypes reverts + "186fbb18e", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: From 7d1a89998e90acd36a5673399f5e75bc029526f9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:48:17 +0200 Subject: [PATCH 0105/1617] Add foundation to enable `--strict-optional` in all test files (#15586) --- mypy/test/helpers.py | 11 ++++++--- mypy/test/testcheck.py | 46 +++++++++++++++++++++++++++++++++++- mypy/test/testfinegrained.py | 7 +++++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 630ba305f349..1e034f94dd92 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -7,7 +7,7 @@ import shutil import sys import time -from typing import Any, Callable, Iterable, Iterator, Pattern +from typing import Any, Callable, Collection, Iterable, Iterator, Pattern # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly @@ -317,7 +317,10 @@ def assert_type(typ: type, value: object) -> None: def parse_options( - program_text: str, testcase: DataDrivenTestCase, incremental_step: int + program_text: str, + testcase: DataDrivenTestCase, + incremental_step: int, + no_strict_optional_files: Collection[str] = (), ) -> Options: """Parse comments like '# flags: --foo' in a test case.""" options = Options() @@ -340,7 +343,9 @@ def parse_options( flag_list = [] options = Options() # TODO: Enable strict optional in test cases by default (requires *many* test case changes) - options.strict_optional = False + if os.path.basename(testcase.file) in no_strict_optional_files: + options.strict_optional = False + options.error_summary = False options.hide_error_codes = True options.force_uppercase_builtins = True diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 20dfbb77f3e0..6c7007764f58 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -49,6 +49,48 @@ typecheck_files.remove("check-modules-case.test") +# TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated +no_strict_optional_files = { + "check-abstract.test", + "check-async-await.test", + "check-basic.test", + "check-bound.test", + "check-classes.test", + "check-dynamic-typing.test", + "check-enum.test", + "check-expressions.test", + "check-formatting.test", + "check-functions.test", + "check-generic-subtyping.test", + "check-generics.test", + "check-incremental.test", + "check-inference-context.test", + "check-inference.test", + "check-inline-config.test", + "check-isinstance.test", + "check-kwargs.test", + "check-lists.test", + "check-literal.test", + "check-modules.test", + "check-namedtuple.test", + "check-newsemanal.test", + "check-overloading.test", + "check-plugin-attrs.test", + "check-protocols.test", + "check-selftype.test", + "check-serialize.test", + "check-statements.test", + "check-super.test", + "check-tuples.test", + "check-type-aliases.test", + "check-type-checks.test", + "check-typeddict.test", + "check-typevar-values.test", + "check-unions.test", + "check-varargs.test", +} + + class TypeCheckSuite(DataSuite): files = typecheck_files @@ -121,7 +163,9 @@ def run_case_once( perform_file_operations(operations) # Parse options after moving files (in case mypy.ini is being moved). - options = parse_options(original_program_text, testcase, incremental_step) + options = parse_options( + original_program_text, testcase, incremental_step, no_strict_optional_files + ) options.use_builtins_fixtures = True if not testcase.name.endswith("_no_incomplete"): options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index ba0526d32558..a1dde823cb5f 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -45,6 +45,9 @@ # Set to True to perform (somewhat expensive) checks for duplicate AST nodes after merge CHECK_CONSISTENCY = False +# TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated +no_strict_optional_files = {"fine-grained.test", "fine-grained-suggest.test"} + class FineGrainedSuite(DataSuite): files = find_test_files( @@ -140,7 +143,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bool) -> Options: # This handles things like '# flags: --foo'. - options = parse_options(source, testcase, incremental_step=1) + options = parse_options( + source, testcase, incremental_step=1, no_strict_optional_files=no_strict_optional_files + ) options.incremental = True options.use_builtins_fixtures = True options.show_traceback = True From c13f1d416e907f58bc77d086b84819f500f1bde9 Mon Sep 17 00:00:00 2001 From: Erik Kemperman Date: Wed, 5 Jul 2023 09:34:11 +0200 Subject: [PATCH 0106/1617] Better consistency with explicit staticmethods (#15353) --- mypy/checker.py | 5 ++- mypy/checkmember.py | 6 +--- mypy/nodes.py | 2 +- mypy/semanal.py | 8 +++-- mypy/typeops.py | 2 +- test-data/unit/check-selftype.test | 52 ++++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index bf200299d8b3..5ed1c792778b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1164,11 +1164,10 @@ def check_func_def( isinstance(defn, FuncDef) and ref_type is not None and i == 0 - and not defn.is_static + and (not defn.is_static or defn.name == "__new__") and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2] ): - isclass = defn.is_class or defn.name in ("__new__", "__init_subclass__") - if isclass: + if defn.is_class or defn.name == "__new__": ref_type = mypy.types.TypeType.make_normalized(ref_type) erased = get_proper_type(erase_to_bound(arg_type)) if not is_subtype(ref_type, erased, ignore_type_params=True): diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 36fea9daa3e3..343dfe3de243 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -319,11 +319,7 @@ def analyze_instance_member_access( mx.msg.cant_assign_to_method(mx.context) signature = function_type(method, mx.named_type("builtins.function")) signature = freshen_all_functions_type_vars(signature) - if name == "__new__" or method.is_static: - # __new__ is special and behaves like a static method -- don't strip - # the first argument. - pass - else: + if not method.is_static: if name != "__call__": # TODO: use proper treatment of special methods on unions instead # of this hack here and below (i.e. mx.self_type). diff --git a/mypy/nodes.py b/mypy/nodes.py index eb093f5d45b0..2d763fc482d3 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -512,7 +512,7 @@ class FuncBase(Node): "info", "is_property", "is_class", # Uses "@classmethod" (explicit or implicit) - "is_static", # Uses "@staticmethod" + "is_static", # Uses "@staticmethod" (explicit or implicit) "is_final", # Uses "@final" "is_explicit_override", # Uses "@override" "_fullname", diff --git a/mypy/semanal.py b/mypy/semanal.py index 1f15691ae205..f4f281e7a77a 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -959,9 +959,11 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type: bool) -> None: """Check basic signature validity and tweak annotation of self/cls argument.""" - # Only non-static methods are special. + # Only non-static methods are special, as well as __new__. functype = func.type - if not func.is_static: + if func.name == "__new__": + func.is_static = True + if not func.is_static or func.name == "__new__": if func.name in ["__init_subclass__", "__class_getitem__"]: func.is_class = True if not func.arguments: @@ -1397,7 +1399,7 @@ def analyze_function_body(self, defn: FuncItem) -> None: # The first argument of a non-static, non-class method is like 'self' # (though the name could be different), having the enclosing class's # instance type. - if is_method and not defn.is_static and defn.arguments: + if is_method and (not defn.is_static or defn.name == "__new__") and defn.arguments: if not defn.is_class: defn.arguments[0].variable.is_self = True else: diff --git a/mypy/typeops.py b/mypy/typeops.py index 58a641a54ab7..519d3de995f5 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -710,7 +710,7 @@ def callable_type( fdef: FuncItem, fallback: Instance, ret_type: Type | None = None ) -> CallableType: # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal - if fdef.info and not fdef.is_static and fdef.arg_names: + if fdef.info and (not fdef.is_static or fdef.name == "__new__") and fdef.arg_names: self_type: Type = fill_typevars(fdef.info) if fdef.is_class or fdef.name == "__new__": self_type = TypeType.make_normalized(self_type) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 4155e3343851..dc7a6c239fe6 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -509,6 +509,58 @@ class E: def __init_subclass__(cls) -> None: reveal_type(cls) # N: Revealed type is "Type[__main__.E]" +[case testSelfTypeNew_explicit] +from typing import TypeVar, Type + +T = TypeVar('T', bound='A') +class A: + @staticmethod + def __new__(cls: Type[T]) -> T: + return cls() + + @classmethod + def __init_subclass__(cls: Type[T]) -> None: + pass + +class B: + @staticmethod + def __new__(cls: Type[T]) -> T: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + return cls() + + @classmethod + def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + pass + +class C: + @staticmethod + def __new__(cls: Type[C]) -> C: + return cls() + + @classmethod + def __init_subclass__(cls: Type[C]) -> None: + pass + +class D: + @staticmethod + def __new__(cls: D) -> D: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + return cls + + @classmethod + def __init_subclass__(cls: D) -> None: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + pass + +class E: + @staticmethod + def __new__(cls) -> E: + reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + return cls() + + @classmethod + def __init_subclass__(cls) -> None: + reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + +[builtins fixtures/classmethod.pyi] + [case testSelfTypePropertyUnion] from typing import Union class A: From 8f66718d996c11fc1f86c21331b31dcc57763cf0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 5 Jul 2023 20:55:45 +0200 Subject: [PATCH 0107/1617] Enable strict optional for more test files (1) (#15595) --- mypy/test/testcheck.py | 4 -- test-data/unit/check-abstract.test | 47 ++++++++------ test-data/unit/check-async-await.test | 4 +- test-data/unit/check-classes.test | 92 +++++++++++++++------------ test-data/unit/check-enum.test | 4 +- 5 files changed, 82 insertions(+), 69 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 6c7007764f58..c9e66e4270b6 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -51,13 +51,9 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { - "check-abstract.test", - "check-async-await.test", "check-basic.test", "check-bound.test", - "check-classes.test", "check-dynamic-typing.test", - "check-enum.test", "check-expressions.test", "check-formatting.test", "check-functions.test", diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 3a0a30a14462..1c85fae93dd4 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -9,11 +9,11 @@ from abc import abstractmethod, ABCMeta -i = None # type: I -j = None # type: J -a = None # type: A -b = None # type: B -c = None # type: C +i: I +j: J +a: A +b: B +c: C def f(): i, j, a, b, c # Prevent redefinition @@ -44,10 +44,10 @@ class C(I): pass from abc import abstractmethod, ABCMeta -i = None # type: I -j = None # type: J -a = None # type: A -o = None # type: object +i: I +j: J +a: A +o: object def f(): i, j, a, o # Prevent redefinition @@ -73,9 +73,9 @@ class A(J): pass [case testInheritingAbstractClassInSubclass] from abc import abstractmethod, ABCMeta -i = None # type: I -a = None # type: A -b = None # type: B +i: I +a: A +b: B if int(): i = a # E: Incompatible types in assignment (expression has type "A", variable has type "I") @@ -106,8 +106,8 @@ class I(metaclass=ABCMeta): @abstractmethod def f(self): pass -o = None # type: object -t = None # type: type +o: object +t: type o = I t = I @@ -122,8 +122,10 @@ class I(metaclass=ABCMeta): class A(I): pass class B: pass -i, a, b = None, None, None # type: (I, A, B) -o = None # type: object +i: I +a: A +b: B +o: object if int(): a = cast(I, o) # E: Incompatible types in assignment (expression has type "I", variable has type "A") @@ -220,6 +222,7 @@ f(GoodAlias) [out] [case testInstantiationAbstractsInTypeForVariables] +# flags: --no-strict-optional from typing import Type from abc import abstractmethod @@ -399,7 +402,9 @@ class I(metaclass=ABCMeta): @abstractmethod def f(self, a: int) -> str: pass -i, a, b = None, None, None # type: (I, int, str) +i: I +a: int +b: str if int(): a = i.f(a) # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -419,7 +424,9 @@ class J(metaclass=ABCMeta): def f(self, a: int) -> str: pass class I(J): pass -i, a, b = None, None, None # type: (I, int, str) +i: I +a: int +b: str if int(): a = i.f(1) # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -505,7 +512,7 @@ class B(metaclass=ABCMeta): @abstractmethod def g(self) -> None: pass class C(A, B): pass -x = None # type: C +x: C x.f() x.g() x.f(x) # E: Too many arguments for "f" of "A" @@ -737,6 +744,7 @@ class A(metaclass=ABCMeta): def x(self, x: int) -> None: pass [case testReadWriteDeleteAbstractProperty] +# flags: --no-strict-optional from abc import ABC, abstractmethod class Abstract(ABC): @property @@ -853,6 +861,7 @@ main:8: error: Cannot instantiate abstract class "B" with abstract attribute "x" main:9: error: "int" has no attribute "y" [case testSuperWithAbstractProperty] +# flags: --no-strict-optional from abc import abstractproperty, ABCMeta class A(metaclass=ABCMeta): @abstractproperty diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index d0df2c9e0104..bcf55d84ff26 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -291,7 +291,7 @@ async def f() -> None: [typing fixtures/typing-async.pyi] [case testAsyncWithErrorBadAenter2] - +# flags: --no-strict-optional class C: def __aenter__(self) -> None: pass async def __aexit__(self, x, y, z) -> None: pass @@ -313,7 +313,7 @@ async def f() -> None: [typing fixtures/typing-async.pyi] [case testAsyncWithErrorBadAexit2] - +# flags: --no-strict-optional class C: async def __aenter__(self) -> int: pass def __aexit__(self, x, y, z) -> None: pass diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 2c80eb7b49bc..69227e50f6fa 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -8,8 +8,8 @@ class A: class B: def bar(self, x: 'B', y: A) -> None: pass -a = None # type: A -b = None # type: B +a: A +b: B a.foo(B()) # E: Argument 1 to "foo" of "A" has incompatible type "B"; expected "A" a.bar(B(), A()) # E: "A" has no attribute "bar" @@ -23,7 +23,7 @@ class A: def bar(self, x: 'B') -> None: pass class B(A): pass -a = None # type: A +a: A a.foo(A()) a.foo(B()) a.bar(A()) # E: Argument 1 to "bar" of "A" has incompatible type "A"; expected "B" @@ -34,7 +34,7 @@ class A: def foo(self, x: 'B') -> None: pass class B(A): pass -a = None # type: B +a: B a.foo(A()) # Fail a.foo(B()) @@ -46,7 +46,7 @@ main:6: error: Argument 1 to "foo" of "A" has incompatible type "A"; expected "B class A: def foo(self, x: 'A') -> None: pass -a = None # type: A +a: A a.foo() # Fail a.foo(object(), A()) # Fail [out] @@ -103,7 +103,8 @@ main:5: error: "A" has no attribute "g" import typing class A: def f(self): pass -A().f = None # E: Cannot assign to a method +A().f = None # E: Cannot assign to a method \ + # E: Incompatible types in assignment (expression has type "None", variable has type "Callable[[], Any]") [case testOverrideAttributeWithMethod] @@ -202,8 +203,8 @@ class A: self.a = aa self.b = bb class B: pass -a = None # type: A -b = None # type: B +a: A +b: B a.a = b # Fail a.b = a # Fail b.a # Fail @@ -217,8 +218,8 @@ main:11: error: "B" has no attribute "a" [case testExplicitAttributeInBody] class A: - x = None # type: A -a = None # type: A + x: A +a: A a.x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") a.x = A() @@ -458,9 +459,10 @@ class A: def g(self) -> 'A': pass class B(A): def f(self) -> A: pass # Fail - def g(self) -> None: pass + def g(self) -> None: pass # Fail [out] main:6: error: Return type "A" of "f" incompatible with return type "None" in supertype "A" +main:7: error: Return type "None" of "g" incompatible with return type "A" in supertype "A" [case testOverride__new__WithDifferentSignature] class A: @@ -907,7 +909,7 @@ b = __init__() # type: B # E: Incompatible types in assignment (expression has t from typing import Any, cast class A: def __init__(self, a: 'A') -> None: pass -a = None # type: A +a: A a.__init__(a) # E: Accessing "__init__" on an instance is unsound, since instance.__init__ could be from an incompatible subclass (cast(Any, a)).__init__(a) @@ -1017,13 +1019,14 @@ class A: A.f(A()) A.f(object()) # E: Argument 1 to "f" of "A" has incompatible type "object"; expected "A" A.f() # E: Missing positional argument "self" in call to "f" of "A" -A.f(None, None) # E: Too many arguments for "f" of "A" +A.f(None, None) # E: Too many arguments for "f" of "A" \ + # E: Argument 1 to "f" of "A" has incompatible type "None"; expected "A" [case testAccessAttributeViaClass] import typing class B: pass class A: - x = None # type: A + x: A a = A.x # type: A b = A.x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -1060,7 +1063,7 @@ A.f() # E: Missing positional argument "self" in call to "f" of "A" import typing class B: pass class A: - x = None # type: B + x: B A.x = B() A.x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "B") @@ -1077,8 +1080,8 @@ A.x = A() # E: Incompatible types in assignment (expression has type "A", vari class B: pass class A: def __init__(self, b: B) -> None: pass -a = None # type: A -b = None # type: B +a: A +b: B A.__init__(a, b) A.__init__(b, b) # E: Argument 1 to "__init__" of "A" has incompatible type "B"; expected "A" A.__init__(a, a) # E: Argument 2 to "__init__" of "A" has incompatible type "A"; expected "B" @@ -1087,13 +1090,15 @@ A.__init__(a, a) # E: Argument 2 to "__init__" of "A" has incompatible type "A"; import typing class A: def f(self): pass -A.f = None # E: Cannot assign to a method +A.f = None # E: Cannot assign to a method \ + # E: Incompatible types in assignment (expression has type "None", variable has type "Callable[[A], Any]") [case testAssignToNestedClassViaClass] import typing class A: class B: pass -A.B = None # E: Cannot assign to a type +A.B = None # E: Cannot assign to a type \ + # E: Incompatible types in assignment (expression has type "None", variable has type "Type[B]") [targets __main__] [case testAccessingClassAttributeWithTypeInferenceIssue] @@ -1139,7 +1144,7 @@ A[int, int].x # E: Access to generic instance variables via class is ambiguous def f() -> None: class A: def g(self) -> None: pass - a = None # type: A + a: A a.g() a.g(a) # E: Too many arguments for "g" of "A" [targets __main__, __main__.f] @@ -1200,7 +1205,7 @@ class A: def f() -> None: class A: pass - a = None # type: A + a: A if int(): a = A() a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") @@ -1209,7 +1214,7 @@ def f() -> None: [case testExternalReferenceToClassWithinClass] class A: class B: pass -b = None # type: A.B +b: A.B if int(): b = A.B() if int(): @@ -1256,19 +1261,19 @@ reveal_type(Foo().Meta.name) # N: Revealed type is "builtins.str" class A: def __init__(self): - self.x = None # type: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs -a = None # type: A + self.x: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs +a: A a.x = 1 a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testAccessAttributeDeclaredInInitBeforeDeclaration] -a = None # type: A +a: A a.x = 1 a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") class A: def __init__(self): - self.x = None # type: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs + self.x: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs -- Special cases @@ -1960,8 +1965,8 @@ from typing import _promote class A: pass @_promote(A) class B: pass -a = None # type: A -b = None # type: B +a: A +b: B if int(): b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = b @@ -1974,8 +1979,8 @@ class A: pass class B: pass @_promote(B) class C: pass -a = None # type: A -c = None # type: C +a: A +c: C if int(): c = a # E: Incompatible types in assignment (expression has type "A", variable has type "C") a = c @@ -2734,8 +2739,8 @@ main:8: note: This violates the Liskov substitution principle main:8: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides [case testGetattribute] - -a, b = None, None # type: A, B +a: A +b: B class A: def __getattribute__(self, x: str) -> A: return A() @@ -2790,8 +2795,8 @@ main:4: error: Invalid signature "Callable[[B, A], B]" for "__getattribute__" main:6: error: Invalid signature "Callable[[C, str, str], C]" for "__getattribute__" [case testGetattr] - -a, b = None, None # type: A, B +a: A +b: B class A: def __getattr__(self, x: str) -> A: return A() @@ -3072,8 +3077,9 @@ C(bar='') # E: Unexpected keyword argument "bar" for "C" [builtins fixtures/__new__.pyi] [case testClassWith__new__AndCompatibilityWithType] +from typing import Optional class C: - def __new__(cls, foo: int = None) -> 'C': + def __new__(cls, foo: Optional[int] = None) -> 'C': obj = object.__new__(cls) return obj def f(x: type) -> None: pass @@ -3596,15 +3602,15 @@ main:7: note: Revealed type is "builtins.list[Type[__main__.B]]" [case testTypeEquivalentTypeAny] from typing import Type, Any -a = None # type: Type[Any] +a: Type[Any] b = a # type: type -x = None # type: type +x: type y = x # type: Type[Any] class C: ... -p = None # type: type +p: type q = p # type: Type[C] [builtins fixtures/list.pyi] @@ -3614,9 +3620,9 @@ q = p # type: Type[C] from typing import Type, Any, TypeVar, Generic class C: ... -x = None # type: type -y = None # type: Type[Any] -z = None # type: Type[C] +x: type +y: Type[Any] +z: Type[C] lst = [x, y, z] reveal_type(lst) # N: Revealed type is "builtins.list[builtins.type]" @@ -4994,7 +5000,7 @@ a[0]() # E: "int" not callable [file m.py] from typing import Tuple -a = None # type: A +a: A class A(Tuple[int, str]): pass [builtins fixtures/tuple.pyi] @@ -5102,6 +5108,7 @@ Foos = NewType('Foos', List[Foo]) # type: ignore def frob(foos: Dict[Any, Foos]) -> None: foo = foos.get(1) + assert foo dict(foo) [builtins fixtures/dict.pyi] [out] @@ -5116,6 +5123,7 @@ x: C class C: def frob(self, foos: Dict[Any, Foos]) -> None: foo = foos.get(1) + assert foo dict(foo) reveal_type(x.frob) # N: Revealed type is "def (foos: builtins.dict[Any, __main__.Foos])" diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index b62ed3d94210..ce7e173f635d 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -297,7 +297,7 @@ f(E.X) from enum import IntEnum class E(IntEnum): a = 1 -x = None # type: int +x: int reveal_type(E(x)) [out] main:5: note: Revealed type is "__main__.E" @@ -306,7 +306,7 @@ main:5: note: Revealed type is "__main__.E" from enum import IntEnum class E(IntEnum): a = 1 -s = None # type: str +s: str reveal_type(E[s]) [out] main:5: note: Revealed type is "__main__.E" From d4865b25dc6653f5d0a94a17d1fc8c445192bb32 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 5 Jul 2023 20:57:34 +0200 Subject: [PATCH 0108/1617] Enable strict optional for more test files (2) (#15596) --- mypy/test/testcheck.py | 13 ---------- test-data/unit/check-formatting.test | 15 +++++++++--- test-data/unit/check-incremental.test | 8 +++--- test-data/unit/check-inline-config.test | 31 ++++++++++++------------ test-data/unit/check-lists.test | 20 +++++++++++---- test-data/unit/check-newsemanal.test | 1 + test-data/unit/check-protocols.test | 17 ++++++++----- test-data/unit/check-selftype.test | 9 ++++--- test-data/unit/check-serialize.test | 1 + test-data/unit/check-super.test | 9 ++++--- test-data/unit/check-type-aliases.test | 6 ++--- test-data/unit/check-type-checks.test | 6 ++--- test-data/unit/check-typeddict.test | 12 ++++----- test-data/unit/check-typevar-values.test | 30 +++++++++++------------ 14 files changed, 97 insertions(+), 81 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index c9e66e4270b6..1e5c47c2be25 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -55,33 +55,20 @@ "check-bound.test", "check-dynamic-typing.test", "check-expressions.test", - "check-formatting.test", "check-functions.test", "check-generic-subtyping.test", "check-generics.test", - "check-incremental.test", "check-inference-context.test", "check-inference.test", - "check-inline-config.test", "check-isinstance.test", "check-kwargs.test", - "check-lists.test", "check-literal.test", "check-modules.test", "check-namedtuple.test", - "check-newsemanal.test", "check-overloading.test", "check-plugin-attrs.test", - "check-protocols.test", - "check-selftype.test", - "check-serialize.test", "check-statements.test", - "check-super.test", "check-tuples.test", - "check-type-aliases.test", - "check-type-checks.test", - "check-typeddict.test", - "check-typevar-values.test", "check-unions.test", "check-varargs.test", } diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index 588b2c11714e..f63abbb33034 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -4,7 +4,10 @@ [case testStringInterpolationType] from typing import Tuple -i, f, s, t = None, None, None, None # type: (int, float, str, Tuple[int]) +i: int +f: float +s: str +t: Tuple[int] '%d' % i '%f' % f '%s' % s @@ -21,7 +24,9 @@ i, f, s, t = None, None, None, None # type: (int, float, str, Tuple[int]) [case testStringInterpolationSAcceptsAnyType] from typing import Any -i, o, s = None, None, None # type: (int, object, str) +i: int +o: object +s: str '%s %s %s' % (i, o, s) [builtins fixtures/primitives.pyi] @@ -139,8 +144,10 @@ class BytesThing: def __getitem__(self, __key: bytes) -> str: ... -a = None # type: Any -ds, do, di = None, None, None # type: Dict[str, int], Dict[object, int], Dict[int, int] +a: Any +ds: Dict[str, int] +do: Dict[object, int] +di: Dict[int, int] '%(a)' % 1 # E: Format requires a mapping (expression has type "int", expected type for mapping is "SupportsKeysAndGetItem[str, Any]") '%()d' % a '%()d' % ds diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index c3153cd8643a..5203b0828122 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1734,12 +1734,12 @@ class R: pass [file r/s.py] from . import m R = m.R -a = None # type: R +a: R [file r/s.py.2] from . import m R = m.R -a = None # type: R +a: R [case testIncrementalBaseClassAttributeConflict] class A: pass @@ -2603,7 +2603,7 @@ def output() -> IntNode[str]: return Node(1, 'x') x = output() # type: IntNode -y = None # type: IntNode +y: IntNode y.x = 1 y.y = 1 y.y = 'x' @@ -2625,7 +2625,7 @@ def output() -> IntNode[str]: return Node(1, 'x') x = output() # type: IntNode -y = None # type: IntNode +y: IntNode y.x = 1 y.y = 1 y.y = 'x' diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 0cc2bd71270a..71030b5c9b97 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -4,8 +4,8 @@ # mypy: disallow-any-generics, no-warn-no-return -from typing import List -def foo() -> List: # E: Missing type parameters for generic type "List" +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" 20 [builtins fixtures/list.pyi] @@ -15,8 +15,8 @@ def foo() -> List: # E: Missing type parameters for generic type "List" # mypy: disallow-any-generics # mypy: no-warn-no-return -from typing import List -def foo() -> List: # E: Missing type parameters for generic type "List" +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" 20 [builtins fixtures/list.pyi] @@ -25,8 +25,8 @@ def foo() -> List: # E: Missing type parameters for generic type "List" # mypy: disallow-any-generics=true, warn-no-return=0 -from typing import List -def foo() -> List: # E: Missing type parameters for generic type "List" +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" 20 [builtins fixtures/list.pyi] @@ -36,8 +36,8 @@ def foo() -> List: # E: Missing type parameters for generic type "List" # mypy: disallow-any-generics = true, warn-no-return = 0 -from typing import List -def foo() -> List: # E: Missing type parameters for generic type "List" +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" 20 [builtins fixtures/list.pyi] @@ -84,20 +84,20 @@ import a [file a.py] # mypy: disallow-any-generics, no-warn-no-return -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [file a.py.2] # mypy: no-warn-no-return -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [file a.py.3] -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [out] tmp/a.py:4: error: Missing type parameters for generic type "List" @@ -131,8 +131,9 @@ tmp/a.py:4: error: Missing type parameters for generic type "List" import a, b [file a.py] # mypy: no-warn-no-return +from typing import Optional -def foo() -> int: +def foo() -> Optional[int]: 20 [file b.py] diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test index 899b7c5b209f..9809024afdbb 100644 --- a/test-data/unit/check-lists.test +++ b/test-data/unit/check-lists.test @@ -3,8 +3,12 @@ [case testNestedListAssignment] from typing import List -a1, b1, c1 = None, None, None # type: (A, B, C) -a2, b2, c2 = None, None, None # type: (A, B, C) +a1: A +a2: A +b1: B +b2: B +c1: C +c2: C if int(): a1, [b1, c1] = a2, [b2, c2] @@ -21,7 +25,9 @@ class C: pass [case testNestedListAssignmentToTuple] from typing import List -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a, b = [a, b] a, b = [a] # E: Need more than 1 value to unpack (2 expected) @@ -35,7 +41,9 @@ class C: pass [case testListAssignmentFromTuple] from typing import List -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C t = a, b if int(): @@ -55,7 +63,9 @@ class C: pass [case testListAssignmentUnequalAmountToUnpack] from typing import List -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C def f() -> None: # needed because test parser tries to parse [a, b] as section header [a, b] = [a, b] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index de8212b3e695..77a1553d4715 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2149,6 +2149,7 @@ x: C class C: def frob(self, foos: Dict[Any, Foos]) -> None: foo = foos.get(1) + assert foo dict(foo) [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 6ba1fde4d022..5d5ba54304a3 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -319,7 +319,9 @@ class MyHashable(Protocol): class C: __my_hash__ = None -var: MyHashable = C() # E: Incompatible types in assignment (expression has type "C", variable has type "MyHashable") +var: MyHashable = C() # E: Incompatible types in assignment (expression has type "C", variable has type "MyHashable") \ + # N: Following member(s) of "C" have conflicts: \ + # N: __my_hash__: expected "Callable[[], int]", got "None" [case testNoneDisablesProtocolSubclassingWithStrictOptional] # flags: --strict-optional @@ -1263,13 +1265,13 @@ if int(): [builtins fixtures/classmethod.pyi] [case testOverloadedMethodsInProtocols] -from typing import overload, Protocol, Union +from typing import overload, Protocol, Union, Optional class P(Protocol): @overload - def f(self, x: int) -> int: pass + def f(self, x: int) -> Optional[int]: pass @overload - def f(self, x: str) -> str: pass + def f(self, x: str) -> Optional[str]: pass class C: def f(self, x: Union[int, str]) -> None: @@ -1286,9 +1288,9 @@ main:18: error: Incompatible types in assignment (expression has type "D", varia main:18: note: Following member(s) of "D" have conflicts: main:18: note: Expected: main:18: note: @overload -main:18: note: def f(self, x: int) -> int +main:18: note: def f(self, x: int) -> Optional[int] main:18: note: @overload -main:18: note: def f(self, x: str) -> str +main:18: note: def f(self, x: str) -> Optional[str] main:18: note: Got: main:18: note: def f(self, x: int) -> None @@ -1441,6 +1443,7 @@ def g(x: P, y: P2) -> None: pass reveal_type(f(g)) # N: Revealed type is "__main__.P2" [case testMeetOfIncompatibleProtocols] +# flags: --no-strict-optional from typing import Protocol, Callable, TypeVar class P(Protocol): @@ -1634,6 +1637,7 @@ f(Alias) # E: Only concrete class can be given where "Type[P]" is expected f(GoodAlias) [case testInstantiationProtocolInTypeForVariables] +# flags: --no-strict-optional from typing import Type, Protocol class P(Protocol): @@ -2397,6 +2401,7 @@ x: P = None [out] [case testNoneSubtypeOfAllProtocolsWithoutStrictOptional] +# flags: --no-strict-optional from typing import Protocol class P(Protocol): attr: int diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index dc7a6c239fe6..d366e7c33799 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -897,7 +897,8 @@ class Base(Protocol): class TweakFunc: def func(self: Base) -> int: - return reveal_type(super().func()) # N: Revealed type is "builtins.int" + return reveal_type(super().func()) # E: Call to abstract method "func" of "Base" with trivial body via super() is unsafe \ + # N: Revealed type is "builtins.int" class Good: def func(self) -> int: ... @@ -1269,14 +1270,14 @@ class AClass: ... def foo(x: Type[AClass]) -> None: - reveal_type(x.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass, id2: None =) -> builtins.int)" + reveal_type(x.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> Union[builtins.int, None], def (id: __main__.AClass, id2: None =) -> Union[builtins.int, None])" y = x() - reveal_type(y.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass, id2: None =) -> builtins.int)" + reveal_type(y.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> Union[builtins.int, None], def (id: __main__.AClass, id2: None =) -> Union[builtins.int, None])" y.delete(10, 20) y.delete(y) def bar(x: AClass) -> None: - reveal_type(x.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass, id2: None =) -> builtins.int)" + reveal_type(x.delete) # N: Revealed type is "Overload(def (id: builtins.int, id2: builtins.int) -> Union[builtins.int, None], def (id: __main__.AClass, id2: None =) -> Union[builtins.int, None])" x.delete(10, 20) [builtins fixtures/classmethod.pyi] diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index 66d5d879ae68..e5d1d6b170f9 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -1275,6 +1275,7 @@ main:2: error: Too many arguments for "f" main:2: error: Too many arguments for "f" [case testSerializeDummyType] +# flags: --no-strict-optional import a [file a.py] import b diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index b3379e505be7..48a0a0250ecf 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -11,7 +11,8 @@ class B: def f(self) -> 'B': pass class A(B): def f(self) -> 'A': - a, b = None, None # type: (A, B) + a: A + b: B if int(): a = super().f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = super().g() # E: "g" undefined in superclass @@ -26,7 +27,8 @@ class B: def f(self, y: 'A') -> None: pass class A(B): def f(self, y: Any) -> None: - a, b = None, None # type: (A, B) + a: A + b: B super().f(b) # E: Argument 1 to "f" of "B" has incompatible type "B"; expected "A" super().f(a) self.f(b) @@ -35,6 +37,7 @@ class A(B): [out] [case testAccessingSuperInit] +# flags: --no-strict-optional import typing class B: def __init__(self, x: A) -> None: pass @@ -90,7 +93,7 @@ class B(A): def __new__(cls, x: int, y: str = '') -> 'B': super().__new__(cls, 1) super().__new__(cls, 1, '') # E: Too many arguments for "__new__" of "A" - return None + return cls(1) B('') # E: Argument 1 to "B" has incompatible type "str"; expected "int" B(1) B(1, 'x') diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 05a03ecaf7b0..3bfcf6a9afea 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -28,7 +28,7 @@ f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[int, st [case testCallableTypeAlias] from typing import Callable A = Callable[[int], None] -f = None # type: A +f: A f(1) f('') # E: Argument 1 has incompatible type "str"; expected "int" [targets __main__] @@ -169,11 +169,11 @@ f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "str" [case testEmptyTupleTypeAlias] from typing import Tuple, Callable EmptyTuple = Tuple[()] -x = None # type: EmptyTuple +x: EmptyTuple reveal_type(x) # N: Revealed type is "Tuple[]" EmptyTupleCallable = Callable[[Tuple[()]], None] -f = None # type: EmptyTupleCallable +f: EmptyTupleCallable reveal_type(f) # N: Revealed type is "def (Tuple[])" [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-type-checks.test b/test-data/unit/check-type-checks.test index 106f2d680ba4..03c8de4177f3 100644 --- a/test-data/unit/check-type-checks.test +++ b/test-data/unit/check-type-checks.test @@ -2,9 +2,9 @@ [case testSimpleIsinstance] -x = None # type: object -n = None # type: int -s = None # type: str +x: object +n: int +s: str if int(): n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") if isinstance(x, int): diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 4d2d64848515..739d1ba6eb75 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1121,8 +1121,8 @@ D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D reveal_type(d['x']) # N: Revealed type is "builtins.int" reveal_type(d['y']) # N: Revealed type is "builtins.str" -reveal_type(d.get('x')) # N: Revealed type is "builtins.int" -reveal_type(d.get('y')) # N: Revealed type is "builtins.str" +reveal_type(d.get('x')) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(d.get('y')) # N: Revealed type is "Union[builtins.str, None]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -1642,7 +1642,7 @@ def f1(x: int, y: str, z: bytes) -> None: ... def f2(x: int, y: str) -> None: ... td: TD -d = None # type: Dict[Any, Any] +d: Dict[Any, Any] f1(**td, **d) f1(**d, **td) @@ -1740,8 +1740,8 @@ class TDB(TypedDict): td: Union[TDA, TDB] -reveal_type(td.get('a')) # N: Revealed type is "builtins.int" -reveal_type(td.get('b')) # N: Revealed type is "Union[builtins.str, builtins.int]" +reveal_type(td.get('a')) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(td.get('b')) # N: Revealed type is "Union[builtins.str, None, builtins.int]" reveal_type(td.get('c')) # N: Revealed type is "builtins.object" reveal_type(td['a']) # N: Revealed type is "builtins.int" @@ -1805,7 +1805,7 @@ from mypy_extensions import TypedDict class A(TypedDict): x: int -d: Union[A, None] +d: A d.update({'x': 1}) [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 83340c52b63b..effaf620f1f0 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -97,8 +97,8 @@ def f(x: AB) -> AB: from typing import TypeVar T = TypeVar('T', int, str) def f(x: T) -> T: - a = None # type: T - b = None # type: T + a: T + b: T if 1: a = x b = x @@ -248,10 +248,10 @@ def g(a: T) -> None: from typing import TypeVar, Generic, Any X = TypeVar('X', int, str) class A(Generic[X]): pass -a = None # type: A[int] -b = None # type: A[str] -d = None # type: A[object] # E: Value of type variable "X" of "A" cannot be "object" -c = None # type: A[Any] +a: A[int] +b: A[str] +d: A[object] # E: Value of type variable "X" of "A" cannot be "object" +c: A[Any] [case testConstructGenericTypeWithTypevarValuesAndTypeInference] from typing import TypeVar, Generic, Any, cast @@ -272,11 +272,11 @@ Z = TypeVar('Z') class D(Generic[X]): def __init__(self, x: X) -> None: pass def f(x: X) -> None: - a = None # type: D[X] + a: D[X] def g(x: Y) -> None: - a = None # type: D[Y] + a: D[Y] def h(x: Z) -> None: - a = None # type: D[Z] + a: D[Z] [out] main:11: error: Invalid type argument value for "D" main:13: error: Type variable "Z" not valid as type argument value for "D" @@ -287,7 +287,7 @@ X = TypeVar('X', int, str) class S(str): pass class C(Generic[X]): def __init__(self, x: X) -> None: pass -x = None # type: C[str] +x: C[str] y = C(S()) if int(): x = y @@ -412,10 +412,10 @@ class B: pass X = TypeVar('X', A, B) Y = TypeVar('Y', int, str) class C(Generic[X, Y]): pass -a = None # type: C[A, int] -b = None # type: C[B, str] -c = None # type: C[int, int] # E: Value of type variable "X" of "C" cannot be "int" -d = None # type: C[A, A] # E: Value of type variable "Y" of "C" cannot be "A" +a: C[A, int] +b: C[B, str] +c: C[int, int] # E: Value of type variable "X" of "C" cannot be "int" +d: C[A, A] # E: Value of type variable "Y" of "C" cannot be "A" [case testCallGenericFunctionUsingMultipleTypevarsWithValues] from typing import TypeVar @@ -512,7 +512,7 @@ class C(A[str]): from typing import TypeVar, Generic T = TypeVar('T', int, str) class C(Generic[T]): - def f(self, x: int = None) -> None: pass + def f(self, x: int = 2) -> None: pass [case testTypevarValuesWithOverloadedFunctionSpecialCase] from foo import * From fae7d90c7f18d44f7ebe7c25d63325c9811e9fe0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:38:29 +0200 Subject: [PATCH 0109/1617] Enable strict optional for more test files (4) (#15601) --- mypy/test/testcheck.py | 3 - test-data/unit/check-basic.test | 26 ++-- test-data/unit/check-bound.test | 10 +- test-data/unit/check-dynamic-typing.test | 155 ++++++++++++----------- 4 files changed, 99 insertions(+), 95 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 1e5c47c2be25..e00ae047ae8d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -51,9 +51,6 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { - "check-basic.test", - "check-bound.test", - "check-dynamic-typing.test", "check-expressions.test", "check-functions.test", "check-generic-subtyping.test", diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index e10e69267c5a..408c3599672b 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -2,8 +2,8 @@ [out] [case testAssignmentAndVarDef] -a = None # type: A -b = None # type: B +a: A +b: B if int(): a = a if int(): @@ -17,14 +17,14 @@ class A: class B: def __init__(self): pass -x = None # type: A +x: A x = A() if int(): x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") [case testInheritInitFromObject] class A(object): pass class B(object): pass -x = None # type: A +x: A if int(): x = A() if int(): @@ -32,8 +32,8 @@ if int(): [case testImplicitInheritInitFromObject] class A: pass class B: pass -x = None # type: A -o = None # type: object +x: A +o: object if int(): x = o # E: Incompatible types in assignment (expression has type "object", variable has type "A") if int(): @@ -59,7 +59,7 @@ x = B() # type: A y = A() # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B") [case testDeclaredVariableInParentheses] -(x) = None # type: int +(x) = 2 # type: int if int(): x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") if int(): @@ -135,8 +135,8 @@ main:6: error: Missing positional arguments "baz", "bas" in call to "foo" [case testLocalVariables] def f() -> None: - x = None # type: A - y = None # type: B + x: A + y: B if int(): x = x x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -229,12 +229,12 @@ reveal_type(__annotations__) # N: Revealed type is "builtins.dict[builtins.str, [case testLocalVariableShadowing] class A: pass class B: pass -a = None # type: A +a: A if int(): a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = A() def f() -> None: - a = None # type: B + a: B if int(): a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = B() @@ -242,8 +242,8 @@ a = B() # E: Incompatible types in assignment (expression has type "B", va a = A() [case testGlobalDefinedInBlockWithType] class A: pass -while A: - a = None # type: A +while 1: + a: A if int(): a = A() a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") diff --git a/test-data/unit/check-bound.test b/test-data/unit/check-bound.test index eb97bde32e1f..1c713fd77c38 100644 --- a/test-data/unit/check-bound.test +++ b/test-data/unit/check-bound.test @@ -37,15 +37,16 @@ T = TypeVar('T', bound=A) class G(Generic[T]): def __init__(self, x: T) -> None: pass -v = None # type: G[A] -w = None # type: G[B] -x = None # type: G[str] # E: Type argument "str" of "G" must be a subtype of "A" +v: G[A] +w: G[B] +x: G[str] # E: Type argument "str" of "G" must be a subtype of "A" y = G('a') # E: Value of type variable "T" of "G" cannot be "str" z = G(A()) z = G(B()) [case testBoundVoid] +# flags: --no-strict-optional from typing import TypeVar, Generic T = TypeVar('T', bound=int) class C(Generic[T]): @@ -70,10 +71,11 @@ def g(): pass f(g()) C(g()) -z = None # type: C +z: C [case testBoundHigherOrderWithVoid] +# flags: --no-strict-optional from typing import TypeVar, Callable class A: pass T = TypeVar('T', bound=A) diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index dd4cc1579639..0dc05a7a0ea1 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -4,8 +4,8 @@ [case testAssignmentWithDynamic] from typing import Any -d = None # type: Any -a = None # type: A +d: Any +a: A if int(): a = d # Everything ok @@ -20,8 +20,9 @@ class A: pass [case testMultipleAssignmentWithDynamic] from typing import Any -d = None # type: Any -a, b = None, None # type: (A, B) +d: Any +a: A +b: B if int(): d, a = b, b # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -51,7 +52,8 @@ from typing import Any def f(x: Any) -> 'A': pass -a, b = None, None # type: (A, B) +a: A +b: B if int(): b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -75,7 +77,8 @@ from typing import Any def f(x: 'A') -> Any: pass -a, b = None, None # type: (A, B) +a: A +b: B a = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "A" @@ -88,10 +91,10 @@ class B: pass [case testBinaryOperationsWithDynamicLeftOperand] from typing import Any -d = None # type: Any -a = None # type: A -c = None # type: C -b = None # type: bool +d: Any +a: A +c: C +b: bool n = 0 d in a # E: Unsupported right operand type for in ("A") @@ -151,10 +154,10 @@ class dict: pass [case testBinaryOperationsWithDynamicAsRightOperand] from typing import Any -d = None # type: Any -a = None # type: A -c = None # type: C -b = None # type: bool +d: Any +a: A +c: C +b: bool n = 0 a and d @@ -224,9 +227,9 @@ class dict: pass [case testDynamicWithUnaryExpressions] from typing import Any -d = None # type: Any -a = None # type: A -b = None # type: bool +d: Any +a: A +b: bool if int(): a = not d # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -238,8 +241,8 @@ class A: pass [case testDynamicWithMemberAccess] from typing import Any -d = None # type: Any -a = None # type: A +d: Any +a: A if int(): a = d.foo(a()) # E: "A" not callable @@ -256,8 +259,8 @@ class A: pass [case testIndexingWithDynamic] from typing import Any -d = None # type: Any -a = None # type: A +d: Any +a: A if int(): a = d[a()] # E: "A" not callable @@ -270,10 +273,10 @@ d[a], d[a] = a, a class A: pass -[case testTupleExpressionsWithDynamci] +[case testTupleExpressionsWithDynamic] from typing import Tuple, Any -t2 = None # type: Tuple[A, A] -d = None # type: Any +t2: Tuple[A, A] +d: Any if int(): t2 = (d, d, d) # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[A, A]") @@ -289,9 +292,9 @@ class A: pass class B: pass def f() -> None: pass -d = None # type: Any -a = None # type: A -b = None # type: B +d: Any +a: A +b: B if int(): b = cast(A, d) # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): @@ -309,8 +312,8 @@ def g(a: 'A') -> None: class A: pass class B: pass -d = None # type: Any -t = None # type: Tuple[A, A] +d: Any +t: Tuple[A, A] # TODO: callable types, overloaded functions d = None # All ok @@ -362,10 +365,10 @@ class A: pass [case testImplicitGlobalFunctionSignature] from typing import Any, Callable -x = None # type: Any -a = None # type: A -g = None # type: Callable[[], None] -h = None # type: Callable[[A], None] +x: Any +a: A +g: Callable[[], None] +h: Callable[[A], None] def f(x): pass @@ -384,10 +387,10 @@ class A: pass [case testImplicitGlobalFunctionSignatureWithDifferentArgCounts] from typing import Callable -g0 = None # type: Callable[[], None] -g1 = None # type: Callable[[A], None] -g2 = None # type: Callable[[A, A], None] -a = None # type: A +g0: Callable[[], None] +g1: Callable[[A], None] +g2: Callable[[A, A], None] +a: A def f0(): pass def f2(x, y): pass @@ -415,16 +418,17 @@ from typing import Callable class A: pass class B: pass -a, b = None, None # type: (A, B) +a: A +b: B def f01(x = b): pass def f13(x, y = b, z = b): pass -g0 = None # type: Callable[[], None] -g1 = None # type: Callable[[A], None] -g2 = None # type: Callable[[A, A], None] -g3 = None # type: Callable[[A, A, A], None] -g4 = None # type: Callable[[A, A, A, A], None] +g0: Callable[[], None] +g1: Callable[[A], None] +g2: Callable[[A, A], None] +g3: Callable[[A, A, A], None] +g4: Callable[[A, A, A, A], None] f01(a, a) # E: Too many arguments for "f01" f13() # E: Missing positional argument "x" in call to "f13" @@ -456,7 +460,7 @@ if int(): [builtins fixtures/tuple.pyi] [case testSkipTypeCheckingWithImplicitSignature] -a = None # type: A +a: A def f(): a() def g(x): @@ -469,7 +473,7 @@ class A: pass [builtins fixtures/bool.pyi] [case testSkipTypeCheckingWithImplicitSignatureAndDefaultArgs] -a = None # type: A +a: A def f(x=a()): a() def g(x, y=a, z=a()): @@ -478,10 +482,10 @@ class A: pass [case testImplicitMethodSignature] from typing import Callable -g0 = None # type: Callable[[], None] -g1 = None # type: Callable[[A], None] -g2 = None # type: Callable[[A, A], None] -a = None # type: A +g0: Callable[[], None] +g1: Callable[[A], None] +g2: Callable[[A, A], None] +a: A if int(): g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") @@ -502,7 +506,7 @@ if int(): [case testSkipTypeCheckingImplicitMethod] -a = None # type: A +a: A class A: def f(self): a() @@ -511,9 +515,9 @@ class A: [case testImplicitInheritedMethod] from typing import Callable -g0 = None # type: Callable[[], None] -g1 = None # type: Callable[[A], None] -a = None # type: A +g0: Callable[[], None] +g1: Callable[[A], None] +a: A if int(): g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") @@ -559,9 +563,9 @@ from typing import Callable class A: def __init__(self, a, b): pass -f1 = None # type: Callable[[A], A] -f2 = None # type: Callable[[A, A], A] -a = None # type: A +f1: Callable[[A], A] +f2: Callable[[A, A], A] +a: A A(a) # E: Missing positional argument "b" in call to "A" if int(): @@ -576,7 +580,7 @@ class A: pass class B: def __init__(self): pass -t = None # type: type +t: type t = A t = B -- Type compatibility @@ -585,11 +589,11 @@ t = B [case testTupleTypeCompatibility] from typing import Any, Tuple -t1 = None # type: Tuple[Any, A] -t2 = None # type: Tuple[A, Any] -t3 = None # type: Tuple[Any, Any] -t4 = None # type: Tuple[A, A] -t5 = None # type: Tuple[Any, Any, Any] +t1: Tuple[Any, A] +t2: Tuple[A, Any] +t3: Tuple[Any, Any] +t4: Tuple[A, A] +t5: Tuple[Any, Any, Any] def f(): t1, t2, t3, t4, t5 # Prevent redefinition @@ -614,11 +618,11 @@ class A: pass [builtins fixtures/tuple.pyi] [case testFunctionTypeCompatibilityAndReturnTypes] -from typing import Any, Callable -f1 = None # type: Callable[[], Any] -f11 = None # type: Callable[[], Any] -f2 = None # type: Callable[[], A] -f3 = None # type: Callable[[], None] +from typing import Any, Callable, Optional +f1: Callable[[], Any] +f11: Callable[[], Any] +f2: Callable[[], Optional[A]] +f3: Callable[[], None] f2 = f3 @@ -631,9 +635,9 @@ class A: pass [case testFunctionTypeCompatibilityAndArgumentTypes] from typing import Any, Callable -f1 = None # type: Callable[[A, Any], None] -f2 = None # type: Callable[[Any, A], None] -f3 = None # type: Callable[[A, A], None] +f1: Callable[[A, Any], None] +f2: Callable[[Any, A], None] +f3: Callable[[A, A], None] f1 = f1 f1 = f2 @@ -651,8 +655,8 @@ class A: pass [case testFunctionTypeCompatibilityAndArgumentCounts] from typing import Any, Callable -f1 = None # type: Callable[[Any], None] -f2 = None # type: Callable[[Any, Any], None] +f1: Callable[[Any], None] +f2: Callable[[Any, Any], None] if int(): f1 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") @@ -664,7 +668,8 @@ if int(): [case testOverridingMethodWithDynamicTypes] from typing import Any -a, b = None, None # type: (A, B) +a: A +b: B b.f(b) # E: Argument 1 to "f" of "B" has incompatible type "B"; expected "A" a = a.f(b) @@ -682,8 +687,8 @@ class A(B): [builtins fixtures/tuple.pyi] [case testOverridingMethodWithImplicitDynamicTypes] - -a, b = None, None # type: (A, B) +a: A +b: B b.f(b) # E: Argument 1 to "f" of "B" has incompatible type "B"; expected "A" a = a.f(b) From fa8853bda7035ef517984b4afbaa6d953318fa9d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 6 Jul 2023 03:03:53 +0200 Subject: [PATCH 0110/1617] Enable strict optional for more test files (5) (#15602) Followup to https://github.com/python/mypy/pull/15586 Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/test/testcheck.py | 1 - test-data/unit/check-expressions.test | 251 ++++++++++++++++---------- 2 files changed, 156 insertions(+), 96 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index e00ae047ae8d..47e01910cf2c 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -51,7 +51,6 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { - "check-expressions.test", "check-functions.test", "check-generic-subtyping.test", "check-generics.test", diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 1fa551f6a2e4..8231b0a3265f 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -13,11 +13,12 @@ [case testNoneAsRvalue] import typing -a = None # type: A +a: A class A: pass [out] [case testNoneAsArgument] +# flags: --no-strict-optional import typing def f(x: 'A', y: 'B') -> None: pass f(None, None) @@ -32,7 +33,7 @@ class B(A): pass [case testIntLiteral] a = 0 -b = None # type: A +b: A if int(): b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "A") if int(): @@ -42,7 +43,7 @@ class A: [case testStrLiteral] a = '' -b = None # type: A +b: A if int(): b = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "A") if int(): @@ -56,7 +57,7 @@ class A: [case testFloatLiteral] a = 0.0 -b = None # type: A +b: A if str(): b = 1.1 # E: Incompatible types in assignment (expression has type "float", variable has type "A") if str(): @@ -67,7 +68,7 @@ class A: [case testComplexLiteral] a = 0.0j -b = None # type: A +b: A if str(): b = 1.1j # E: Incompatible types in assignment (expression has type "complex", variable has type "A") if str(): @@ -77,7 +78,8 @@ class A: [builtins fixtures/dict.pyi] [case testBytesLiteral] -b, a = None, None # type: (bytes, A) +b: bytes +a: A if str(): b = b'foo' if str(): @@ -90,10 +92,10 @@ class A: pass [builtins fixtures/dict.pyi] [case testUnicodeLiteralInPython3] -s = None # type: str +s: str if int(): s = u'foo' -b = None # type: bytes +b: bytes if int(): b = u'foo' # E: Incompatible types in assignment (expression has type "str", variable has type "bytes") [builtins fixtures/primitives.pyi] @@ -104,7 +106,9 @@ if int(): [case testAdd] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a + c # E: Unsupported operand types for + ("A" and "C") if int(): @@ -124,7 +128,9 @@ class C: [builtins fixtures/tuple.pyi] [case testSub] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a - c # E: Unsupported operand types for - ("A" and "C") if int(): @@ -144,7 +150,9 @@ class C: [builtins fixtures/tuple.pyi] [case testMul] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a * c # E: Unsupported operand types for * ("A" and "C") if int(): @@ -164,7 +172,9 @@ class C: [builtins fixtures/tuple.pyi] [case testMatMul] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a @ c # E: Unsupported operand types for @ ("A" and "C") if int(): @@ -184,7 +194,9 @@ class C: [builtins fixtures/tuple.pyi] [case testDiv] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a / c # E: Unsupported operand types for / ("A" and "C") a = a / b # E: Incompatible types in assignment (expression has type "C", variable has type "A") @@ -203,7 +215,9 @@ class C: [builtins fixtures/tuple.pyi] [case testIntDiv] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a // c # E: Unsupported operand types for // ("A" and "C") a = a // b # E: Incompatible types in assignment (expression has type "C", variable has type "A") @@ -222,7 +236,9 @@ class C: [builtins fixtures/tuple.pyi] [case testMod] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a % c # E: Unsupported operand types for % ("A" and "C") if int(): @@ -242,7 +258,9 @@ class C: [builtins fixtures/tuple.pyi] [case testPow] -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a ** c # E: Unsupported operand types for ** ("A" and "C") if int(): @@ -262,8 +280,8 @@ class C: [builtins fixtures/tuple.pyi] [case testMiscBinaryOperators] - -a, b = None, None # type: (A, B) +a: A +b: B b = a & a # Fail b = a | b # Fail b = a ^ a # Fail @@ -291,7 +309,8 @@ main:6: error: Unsupported operand types for << ("A" and "B") main:7: error: Unsupported operand types for >> ("A" and "A") [case testBooleanAndOr] -a, b = None, None # type: (A, bool) +a: A +b: bool if int(): b = b and b if int(): @@ -310,8 +329,8 @@ class A: pass [case testRestrictedTypeAnd] -b = None # type: bool -i = None # type: str +b: bool +i: str j = not b and i if j: reveal_type(j) # N: Revealed type is "builtins.str" @@ -319,8 +338,8 @@ if j: [case testRestrictedTypeOr] -b = None # type: bool -i = None # type: str +b: bool +i: str j = b or i if not j: reveal_type(j) # N: Revealed type is "builtins.str" @@ -343,7 +362,9 @@ def f(a: List[str], b: bool) -> bool: [builtins fixtures/list.pyi] [case testNonBooleanOr] -c, d, b = None, None, None # type: (C, D, bool) +c: C +d: D +b: bool if int(): c = c or c if int(): @@ -362,7 +383,11 @@ class D(C): pass [case testInOperator] from typing import Iterator, Iterable, Any -a, b, c, d, e = None, None, None, None, None # type: (A, B, bool, D, Any) +a: A +b: B +c: bool +d: D +e: Any if int(): c = c in a # E: Unsupported operand types for in ("bool" and "A") if int(): @@ -389,7 +414,11 @@ class D(Iterable[A]): [case testNotInOperator] from typing import Iterator, Iterable, Any -a, b, c, d, e = None, None, None, None, None # type: (A, B, bool, D, Any) +a: A +b: B +c: bool +d: D +e: Any if int(): c = c not in a # E: Unsupported operand types for in ("bool" and "A") if int(): @@ -415,7 +444,9 @@ class D(Iterable[A]): [builtins fixtures/bool.pyi] [case testNonBooleanContainsReturnValue] -a, b, c = None, None, None # type: (A, bool, str) +a: A +b: bool +c: str if int(): b = a not in a if int(): @@ -434,8 +465,8 @@ a = 1 in ([1] + ['x']) # E: List item 0 has incompatible type "str"; expected " [builtins fixtures/list.pyi] [case testEq] - -a, b = None, None # type: (A, bool) +a: A +b: bool if int(): a = a == b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -451,7 +482,9 @@ class A: [builtins fixtures/bool.pyi] [case testLtAndGt] -a, b, bo = None, None, None # type: (A, B, bool) +a: A +b: B +bo: bool if int(): a = a < b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -470,7 +503,9 @@ class B: [builtins fixtures/bool.pyi] [case cmpIgnoredPy3] -a, b, bo = None, None, None # type: (A, B, bool) +a: A +b: B +bo: bool bo = a <= b # E: Unsupported left operand type for <= ("A") class A: @@ -480,7 +515,9 @@ class B: [builtins fixtures/bool.pyi] [case testLeAndGe] -a, b, bo = None, None, None # type: (A, B, bool) +a: A +b: B +bo: bool if int(): a = a <= b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -499,8 +536,9 @@ class B: [builtins fixtures/bool.pyi] [case testChainedComp] - -a, b, bo = None, None, None # type: (A, B, bool) +a: A +b: B +bo: bool a < a < b < b # Fail a < b < b < b a < a > a < b # Fail @@ -513,13 +551,15 @@ class B: def __gt__(self, o: 'B') -> bool: pass [builtins fixtures/bool.pyi] [out] -main:3: error: Unsupported operand types for < ("A" and "A") -main:5: error: Unsupported operand types for < ("A" and "A") -main:5: error: Unsupported operand types for > ("A" and "A") +main:4: error: Unsupported operand types for < ("A" and "A") +main:6: error: Unsupported operand types for < ("A" and "A") +main:6: error: Unsupported operand types for > ("A" and "A") [case testChainedCompBoolRes] -a, b, bo = None, None, None # type: (A, B, bool) +a: A +b: B +bo: bool if int(): bo = a < b < b if int(): @@ -535,8 +575,12 @@ class B: [case testChainedCompResTyp] -x, y = None, None # type: (X, Y) -a, b, p, bo = None, None, None, None # type: (A, B, P, bool) +x: X +y: Y +a: A +b: B +p: P +bo: bool if int(): b = y == y == y if int(): @@ -566,7 +610,8 @@ class Y: [case testIs] -a, b = None, None # type: (A, bool) +a: A +b: bool if int(): a = a is b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -579,7 +624,8 @@ class A: pass [builtins fixtures/bool.pyi] [case testIsNot] -a, b = None, None # type: (A, bool) +a: A +b: bool if int(): a = a is not b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -604,8 +650,8 @@ class A: def __add__(self, x: int) -> int: pass class B: def __radd__(self, x: A) -> str: pass -s = None # type: str -n = None # type: int +s: str +n: int if int(): n = A() + 1 if int(): @@ -618,8 +664,8 @@ class A: def __add__(self, x: 'A') -> object: pass class B: def __radd__(self, x: A) -> str: pass -s = None # type: str -n = None # type: int +s: str +n: int if int(): s = A() + B() n = A() + B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -632,7 +678,7 @@ class A: def __add__(self, x: N) -> int: pass class B: def __radd__(self, x: N) -> str: pass -s = None # type: str +s: str s = A() + B() # E: Unsupported operand types for + ("A" and "B") [case testBinaryOperatorWithAnyRightOperand] @@ -647,8 +693,8 @@ class A: def __lt__(self, x: C) -> int: pass # E: Signatures of "__lt__" of "A" and "__gt__" of "C" are unsafely overlapping class B: def __gt__(self, x: A) -> str: pass -s = None # type: str -n = None # type: int +s: str +n: int if int(): n = A() < C() s = A() < B() @@ -743,8 +789,8 @@ divmod('foo', d) # E: Unsupported operand types for divmod ("str" and "Decimal" [case testUnaryMinus] - -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = -a # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): @@ -760,7 +806,8 @@ class B: [builtins fixtures/tuple.pyi] [case testUnaryPlus] -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = +a # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): @@ -776,7 +823,8 @@ class B: [builtins fixtures/tuple.pyi] [case testUnaryNot] -a, b = None, None # type: (A, bool) +a: A +b: bool if int(): a = not b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") if int(): @@ -788,7 +836,8 @@ class A: [builtins fixtures/bool.pyi] [case testUnaryBitwiseNeg] -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = ~a # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): @@ -809,8 +858,9 @@ class B: [case testIndexing] - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): c = a[c] # E: Invalid index type "C" for "A"; expected type "B" if int(): @@ -828,8 +878,9 @@ class C: pass [builtins fixtures/tuple.pyi] [case testIndexingAsLvalue] - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a[c] = c # Fail a[b] = a # Fail b[a] = c # Fail @@ -844,16 +895,17 @@ class C: pass [builtins fixtures/tuple.pyi] [out] -main:3: error: Invalid index type "C" for "A"; expected type "B" -main:4: error: Incompatible types in assignment (expression has type "A", target has type "C") -main:5: error: Unsupported target for indexed assignment ("B") +main:4: error: Invalid index type "C" for "A"; expected type "B" +main:5: error: Incompatible types in assignment (expression has type "A", target has type "C") +main:6: error: Unsupported target for indexed assignment ("B") [case testOverloadedIndexing] from foo import * [file foo.pyi] from typing import overload - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a[b] a[c] a[1] # E: No overload variant of "__getitem__" of "A" matches argument type "int" \ @@ -861,7 +913,8 @@ a[1] # E: No overload variant of "__getitem__" of "A" matches argument type "in # N: def __getitem__(self, B, /) -> int \ # N: def __getitem__(self, C, /) -> str -i, s = None, None # type: (int, str) +i: int +s: str if int(): i = a[b] if int(): @@ -893,7 +946,9 @@ from typing import cast, Any class A: pass class B: pass class C(A): pass -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C if int(): a = cast(A, a()) # E: "A" not callable @@ -916,7 +971,8 @@ if int(): [case testAnyCast] from typing import cast, Any -a, b = None, None # type: (A, B) +a: A +b: B a = cast(Any, a()) # Fail a = cast(Any, b) b = cast(Any, a) @@ -924,7 +980,7 @@ class A: pass class B: pass [builtins fixtures/tuple.pyi] [out] -main:3: error: "A" not callable +main:4: error: "A" not callable -- assert_type() @@ -1003,7 +1059,8 @@ class A: def __call__(self) -> None: pass -a, o = None, None # type: (A, object) +a: A +o: object if int(): a = f() # E: "f" does not return a value if int(): @@ -1040,7 +1097,7 @@ def f() -> None: pass class A: def __add__(self, x: 'A') -> 'A': pass -a = None # type: A +a: A [f()] # E: "f" does not return a value f() + a # E: "f" does not return a value a + f() # E: "f" does not return a value @@ -1058,7 +1115,8 @@ class A: def __add__(self, x: 'A') -> 'A': pass -a, b = None, None # type: (A, bool) +a: A +b: bool f() in a # E: "f" does not return a value # E: Unsupported right operand type for in ("A") a < f() # E: "f" does not return a value f() <= a # E: "f" does not return a value @@ -1075,7 +1133,8 @@ b or f() # E: "f" does not return a value [case testGetSlice] -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = a[1:2] # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): @@ -1101,7 +1160,7 @@ class B: pass [case testSlicingWithInvalidBase] -a = None # type: A +a: A a[1:2] # E: Invalid index type "slice" for "A"; expected type "int" a[:] # E: Invalid index type "slice" for "A"; expected type "int" class A: @@ -1110,7 +1169,7 @@ class A: [case testSlicingWithNonindexable] -o = None # type: object +o: object o[1:2] # E: Value of type "object" is not indexable o[:] # E: Value of type "object" is not indexable [builtins fixtures/slice.pyi] @@ -1143,7 +1202,7 @@ class SupportsIndex(Protocol): [case testNoneSliceBounds] from typing import Any -a = None # type: Any +a: Any a[None:1] a[1:None] a[None:] @@ -1153,7 +1212,7 @@ a[:None] [case testNoneSliceBoundsWithStrictOptional] # flags: --strict-optional from typing import Any -a = None # type: Any +a: Any a[None:1] a[1:None] a[None:] @@ -1182,6 +1241,7 @@ def void() -> None: x = lambda: void() # type: typing.Callable[[], None] [case testNoCrashOnLambdaGenerator] +# flags: --no-strict-optional from typing import Iterator, Callable # These should not crash @@ -1211,7 +1271,7 @@ def f() -> None: [case testSimpleListComprehension] from typing import List -a = None # type: List[A] +a: List[A] a = [x for x in a] b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B] class A: pass @@ -1220,7 +1280,7 @@ class B: pass [case testSimpleListComprehensionNestedTuples] from typing import List, Tuple -l = None # type: List[Tuple[A, Tuple[A, B]]] +l: List[Tuple[A, Tuple[A, B]]] a = [a2 for a1, (a2, b1) in l] # type: List[A] b = [a2 for a1, (a2, b1) in l] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B] class A: pass @@ -1229,7 +1289,7 @@ class B: pass [case testSimpleListComprehensionNestedTuples2] from typing import List, Tuple -l = None # type: List[Tuple[int, Tuple[int, str]]] +l: List[Tuple[int, Tuple[int, str]]] a = [f(d) for d, (i, s) in l] b = [f(s) for d, (i, s) in l] # E: Argument 1 to "f" has incompatible type "str"; expected "int" @@ -1252,14 +1312,14 @@ def f(a: A) -> B: pass [case testErrorInListComprehensionCondition] from typing import List -a = None # type: List[A] +a: List[A] a = [x for x in a if x()] # E: "A" not callable class A: pass [builtins fixtures/for.pyi] [case testTypeInferenceOfListComprehension] from typing import List -a = None # type: List[A] +a: List[A] o = [x for x in a] # type: List[object] class A: pass [builtins fixtures/for.pyi] @@ -1267,7 +1327,7 @@ class A: pass [case testSimpleListComprehensionInClassBody] from typing import List class A: - a = None # type: List[A] + a: List[A] a = [x for x in a] b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B] class B: pass @@ -1281,7 +1341,7 @@ class B: pass [case testSimpleSetComprehension] from typing import Set -a = None # type: Set[A] +a: Set[A] a = {x for x in a} b = {x for x in a} # type: Set[B] # E: Set comprehension has incompatible type Set[A]; expected Set[B] class A: pass @@ -1295,8 +1355,8 @@ class B: pass [case testSimpleDictionaryComprehension] from typing import Dict, List, Tuple -abd = None # type: Dict[A, B] -abl = None # type: List[Tuple[A, B]] +abd: Dict[A, B] +abl: List[Tuple[A, B]] abd = {a: b for a, b in abl} x = {a: b for a, b in abl} # type: Dict[B, A] y = {a: b for a, b in abl} # type: A @@ -1312,7 +1372,7 @@ main:6: error: Incompatible types in assignment (expression has type "Dict[A, B] [case testDictionaryComprehensionWithNonDirectMapping] from typing import Dict, List, Tuple abd: Dict[A, B] -abl = None # type: List[Tuple[A, B]] +abl: List[Tuple[A, B]] abd = {a: f(b) for a, b in abl} class A: pass class B: pass @@ -1332,10 +1392,10 @@ main:4: error: Argument 1 to "f" has incompatible type "B"; expected "A" from typing import Iterator # The implementation is mostly identical to list comprehensions, so only a few # test cases is ok. -a = None # type: Iterator[int] +a: Iterator[int] if int(): a = (x for x in a) -b = None # type: Iterator[str] +b: Iterator[str] if int(): b = (x for x in a) # E: Generator has incompatible item type "int"; expected "str" [builtins fixtures/for.pyi] @@ -1344,7 +1404,7 @@ if int(): from typing import Callable, Iterator, List a = [] # type: List[Callable[[], str]] -b = None # type: Iterator[Callable[[], int]] +b: Iterator[Callable[[], int]] if int(): b = (x for x in a) # E: Generator has incompatible item type "Callable[[], str]"; expected "Callable[[], int]" [builtins fixtures/list.pyi] @@ -1441,14 +1501,14 @@ class A: def __add__(self, a: 'A') -> 'A': pass def f() -> None: pass -a = None # type: A +a: A None + a # E: Unsupported left operand type for + ("None") f + a # E: Unsupported left operand type for + ("Callable[[], None]") a + f # E: Unsupported operand types for + ("A" and "Callable[[], None]") cast(A, f) [case testOperatorMethodWithInvalidArgCount] -a = None # type: A +a: A a + a # Fail class A: @@ -1462,7 +1522,7 @@ from typing import Any class A: def __init__(self, _add: Any) -> None: self.__add__ = _add -a = None # type: A +a: A a + a [out] @@ -1471,15 +1531,16 @@ a + a class A: def f(self, x: int) -> str: pass __add__ = f -s = None # type: str +s: str s = A() + 1 A() + (A() + 1) [out] main:7: error: Argument 1 has incompatible type "str"; expected "int" [case testIndexedLvalueWithSubtypes] - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a[c] = c a[b] = c a[c] = b @@ -1501,7 +1562,7 @@ class C(B): [case testEllipsis] -a = None # type: A +a: A if str(): a = ... # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "A") b = ... From e0b159e0bb6bd414d2999bfcecbb5432541ec3fd Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 6 Jul 2023 10:07:13 +0200 Subject: [PATCH 0111/1617] Enable strict optional for more test files (6) (#15603) --- mypy/test/testcheck.py | 3 - test-data/unit/check-functions.test | 109 +++++++------- test-data/unit/check-generic-subtyping.test | 135 +++++++++-------- test-data/unit/check-generics.test | 154 +++++++++++--------- 4 files changed, 216 insertions(+), 185 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 47e01910cf2c..f15e5afabf0e 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -51,9 +51,6 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { - "check-functions.test", - "check-generic-subtyping.test", - "check-generics.test", "check-inference-context.test", "check-inference.test", "check-isinstance.test", diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index b5d540b105e3..141d18ae2666 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -10,8 +10,9 @@ [case testCallingVariableWithFunctionType] from typing import Callable -f = None # type: Callable[[A], B] -a, b = None, None # type: (A, B) +f: Callable[[A], B] +a: A +b: B if int(): a = f(a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): @@ -82,9 +83,9 @@ from typing import Callable class A: pass class B(A): pass -f = None # type: Callable[[B], A] -g = None # type: Callable[[A], A] # subtype of f -h = None # type: Callable[[B], B] # subtype of f +f: Callable[[B], A] +g: Callable[[A], A] # subtype of f +h: Callable[[B], B] # subtype of f if int(): g = h # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], A]") if int(): @@ -132,7 +133,7 @@ ff = g from typing import Callable def f(a: int, b: str) -> None: pass -f_nonames = None # type: Callable[[int, str], None] +f_nonames: Callable[[int, str], None] def g(a: int, b: str = "") -> None: pass def h(aa: int, b: str = "") -> None: pass @@ -160,7 +161,7 @@ if int(): from typing import Any, Callable def everything(*args: Any, **kwargs: Any) -> None: pass -everywhere = None # type: Callable[..., None] +everywhere: Callable[..., None] def specific_1(a: int, b: str) -> None: pass def specific_2(a: int, *, b: str) -> None: pass @@ -238,6 +239,7 @@ if int(): gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") [case testFunctionTypeCompatibilityWithOtherTypes] +# flags: --no-strict-optional from typing import Callable f = None # type: Callable[[], None] a, o = None, None # type: (A, object) @@ -272,8 +274,8 @@ def g(x: int) -> Tuple[()]: [case testFunctionSubtypingWithVoid] from typing import Callable -f = None # type: Callable[[], None] -g = None # type: Callable[[], object] +f: Callable[[], None] +g: Callable[[], object] if int(): f = g # E: Incompatible types in assignment (expression has type "Callable[[], object]", variable has type "Callable[[], None]") if int(): @@ -286,9 +288,9 @@ if int(): [case testFunctionSubtypingWithMultipleArgs] from typing import Callable -f = None # type: Callable[[A, A], None] -g = None # type: Callable[[A, B], None] -h = None # type: Callable[[B, B], None] +f: Callable[[A, A], None] +g: Callable[[A, B], None] +h: Callable[[B, B], None] if int(): f = g # E: Incompatible types in assignment (expression has type "Callable[[A, B], None]", variable has type "Callable[[A, A], None]") if int(): @@ -313,9 +315,9 @@ class B(A): pass [case testFunctionTypesWithDifferentArgumentCounts] from typing import Callable -f = None # type: Callable[[], None] -g = None # type: Callable[[A], None] -h = None # type: Callable[[A, A], None] +f: Callable[[], None] +g: Callable[[A], None] +h: Callable[[A, A], None] if int(): f = g # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[], None]") @@ -342,8 +344,8 @@ class A: def f() -> None: pass -t = None # type: type -a = None # type: A +t: type +a: A if int(): a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") @@ -356,9 +358,9 @@ if int(): from foo import * [file foo.pyi] from typing import Callable, overload -f = None # type: Callable[[AA], A] -g = None # type: Callable[[B], B] -h = None # type: Callable[[A], AA] +f: Callable[[AA], A] +g: Callable[[B], B] +h: Callable[[A], AA] if int(): h = i # E: Incompatible types in assignment (expression has type overloaded function, variable has type "Callable[[A], AA]") @@ -395,11 +397,13 @@ def j(x: A) -> AA: from foo import * [file foo.pyi] from typing import Callable, overload -g1 = None # type: Callable[[A], A] -g2 = None # type: Callable[[B], B] -g3 = None # type: Callable[[C], C] -g4 = None # type: Callable[[A], B] -a, b, c = None, None, None # type: (A, B, C) +g1: Callable[[A], A] +g2: Callable[[B], B] +g3: Callable[[C], C] +g4: Callable[[A], B] +a: A +b: B +c: C if int(): b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -448,15 +452,15 @@ f([D]) # E: List item 0 has incompatible type "Type[D]"; expected "Callable[[An [case testSubtypingTypeTypeAsCallable] from typing import Callable, Type class A: pass -x = None # type: Callable[..., A] -y = None # type: Type[A] +x: Callable[..., A] +y: Type[A] x = y [case testSubtypingCallableAsTypeType] from typing import Callable, Type class A: pass -x = None # type: Callable[..., A] -y = None # type: Type[A] +x: Callable[..., A] +y: Type[A] if int(): y = x # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "Type[A]") @@ -573,11 +577,11 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "i [case testMethodAsDataAttribute] from typing import Any, Callable, ClassVar class B: pass -x = None # type: Any +x: Any class A: f = x # type: ClassVar[Callable[[A], None]] g = x # type: ClassVar[Callable[[A, B], None]] -a = None # type: A +a: A a.f() a.g(B()) a.f(a) # E: Too many arguments @@ -586,21 +590,21 @@ a.g() # E: Too few arguments [case testMethodWithInvalidMethodAsDataAttribute] from typing import Any, Callable, ClassVar class B: pass -x = None # type: Any +x: Any class A: f = x # type: ClassVar[Callable[[], None]] g = x # type: ClassVar[Callable[[B], None]] -a = None # type: A +a: A a.f() # E: Attribute function "f" with type "Callable[[], None]" does not accept self argument a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]" [case testMethodWithDynamicallyTypedMethodAsDataAttribute] from typing import Any, Callable, ClassVar class B: pass -x = None # type: Any +x: Any class A: f = x # type: ClassVar[Callable[[Any], Any]] -a = None # type: A +a: A a.f() a.f(a) # E: Too many arguments @@ -627,7 +631,7 @@ class A: @overload def f(self, b: B) -> None: pass g = f -a = None # type: A +a: A a.g() a.g(B()) a.g(a) # E: No overload variant matches argument type "A" \ @@ -640,7 +644,7 @@ a.g(a) # E: No overload variant matches argument type "A" \ class A: def f(self, x): pass g = f -a = None # type: A +a: A a.g(object()) a.g(a, a) # E: Too many arguments a.g() # E: Too few arguments @@ -652,7 +656,7 @@ class B: pass class A(Generic[t]): def f(self, x: t) -> None: pass g = f -a = None # type: A[B] +a: A[B] a.g(B()) a.g(a) # E: Argument 1 has incompatible type "A[B]"; expected "B" @@ -661,11 +665,11 @@ from typing import Any, TypeVar, Generic, Callable, ClassVar t = TypeVar('t') class B: pass class C: pass -x = None # type: Any +x: Any class A(Generic[t]): f = x # type: ClassVar[Callable[[A[B]], None]] -ab = None # type: A[B] -ac = None # type: A[C] +ab: A[B] +ac: A[C] ab.f() ac.f() # E: Invalid self argument "A[C]" to attribute function "f" with type "Callable[[A[B]], None]" @@ -674,21 +678,21 @@ from typing import Any, TypeVar, Generic, Callable, ClassVar t = TypeVar('t') class B: pass class C: pass -x = None # type: Any +x: Any class A(Generic[t]): f = x # type: ClassVar[Callable[[A], None]] -ab = None # type: A[B] -ac = None # type: A[C] +ab: A[B] +ac: A[C] ab.f() ac.f() [case testCallableDataAttribute] from typing import Callable, ClassVar class A: - g = None # type: ClassVar[Callable[[A], None]] + g: ClassVar[Callable[[A], None]] def __init__(self, f: Callable[[], None]) -> None: self.f = f -a = A(None) +a = A(lambda: None) a.f() a.g() a.f(a) # E: Too many arguments @@ -895,7 +899,7 @@ def dec(x) -> Callable[[Any], None]: pass class A: @dec def f(self, a, b, c): pass -a = None # type: A +a: A a.f() a.f(None) # E: Too many arguments for "f" of "A" @@ -1945,9 +1949,9 @@ def a(f: Callable[[VarArg(int)], int]): from typing import Callable from mypy_extensions import Arg, DefaultArg -int_str_fun = None # type: Callable[[int, str], str] -int_opt_str_fun = None # type: Callable[[int, DefaultArg(str, None)], str] -int_named_str_fun = None # type: Callable[[int, Arg(str, 's')], str] +int_str_fun: Callable[[int, str], str] +int_opt_str_fun: Callable[[int, DefaultArg(str, None)], str] +int_named_str_fun: Callable[[int, Arg(str, 's')], str] def isf(ii: int, ss: str) -> str: return ss @@ -2140,6 +2144,7 @@ main:8: error: Cannot use a covariant type variable as a parameter from typing import TypeVar, Generic, Callable [case testRejectContravariantReturnType] +# flags: --no-strict-optional from typing import TypeVar, Generic t = TypeVar('t', contravariant=True) @@ -2148,9 +2153,10 @@ class A(Generic[t]): return None [builtins fixtures/bool.pyi] [out] -main:5: error: Cannot use a contravariant type variable as return type +main:6: error: Cannot use a contravariant type variable as return type [case testAcceptCovariantReturnType] +# flags: --no-strict-optional from typing import TypeVar, Generic t = TypeVar('t', covariant=True) @@ -2158,6 +2164,7 @@ class A(Generic[t]): def foo(self) -> t: return None [builtins fixtures/bool.pyi] + [case testAcceptContravariantArgument] from typing import TypeVar, Generic diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index a34e054fd827..11c92d07021a 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -9,9 +9,9 @@ [case testSubtypingAndInheritingNonGenericTypeFromGenericType] from typing import TypeVar, Generic T = TypeVar('T') -ac = None # type: A[C] -ad = None # type: A[D] -b = None # type: B +ac: A[C] +ad: A[D] +b: B if int(): b = ad # E: Incompatible types in assignment (expression has type "A[D]", variable has type "B") @@ -31,9 +31,9 @@ class D: pass [case testSubtypingAndInheritingGenericTypeFromNonGenericType] from typing import TypeVar, Generic T = TypeVar('T') -a = None # type: A -bc = None # type: B[C] -bd = None # type: B[D] +a: A +bc: B[C] +bd: B[D] if int(): bc = bd # E: Incompatible types in assignment (expression has type "B[D]", variable has type "B[C]") @@ -56,10 +56,10 @@ class D: pass from typing import TypeVar, Generic T = TypeVar('T') S = TypeVar('S') -ac = None # type: A[C] -ad = None # type: A[D] -bcc = None # type: B[C, C] -bdc = None # type: B[D, C] +ac: A[C] +ad: A[D] +bcc: B[C, C] +bdc: B[D, C] if int(): ad = bcc # E: Incompatible types in assignment (expression has type "B[C, C]", variable has type "A[D]") @@ -86,12 +86,12 @@ T = TypeVar('T') S = TypeVar('S') X = TypeVar('X') Y = TypeVar('Y') -ae = None # type: A[A[E]] -af = None # type: A[A[F]] +ae: A[A[E]] +af: A[A[F]] -cef = None # type: C[E, F] -cff = None # type: C[F, F] -cfe = None # type: C[F, E] +cef: C[E, F] +cff: C[F, F] +cfe: C[F, E] if int(): ae = cef # E: Incompatible types in assignment (expression has type "C[E, F]", variable has type "A[A[E]]") @@ -125,8 +125,9 @@ class C: pass from typing import TypeVar, Generic T = TypeVar('T') S = TypeVar('S') -b = None # type: B[C, D] -c, d = None, None # type: (C, D) +b: B[C, D] +c: C +d: D b.f(c) # E: Argument 1 to "f" of "A" has incompatible type "C"; expected "D" b.f(d) @@ -142,7 +143,9 @@ class D: pass [case testAccessingMethodInheritedFromGenericTypeInNonGenericType] from typing import TypeVar, Generic T = TypeVar('T') -b, c, d = None, None, None # type: (B, C, D) +b: B +c: C +d: D b.f(c) # E: Argument 1 to "f" of "A" has incompatible type "C"; expected "D" b.f(d) @@ -163,8 +166,9 @@ class A(Generic[T]): def __init__(self, a: T) -> None: self.a = a -b = None # type: B[C, D] -c, d = None, None # type: (C, D) +b: B[C, D] +c: C +d: D b.a = c # E: Incompatible types in assignment (expression has type "C", variable has type "D") b.a = d @@ -311,9 +315,9 @@ main:14: note: def [T1 <: str, S] f(self, x: T1, y: S) -> None [case testInheritanceFromGenericWithImplicitDynamicAndSubtyping] from typing import TypeVar, Generic T = TypeVar('T') -a = None # type: A -bc = None # type: B[C] -bd = None # type: B[D] +a: A +bc: B[C] +bd: B[D] if int(): a = bc # E: Incompatible types in assignment (expression has type "B[C]", variable has type "A") @@ -337,9 +341,9 @@ class B(Generic[T]): class A(B): pass class C: pass -a = None # type: A -c = None # type: C -bc = None # type: B[C] +a: A +c: C +bc: B[C] a.x = c # E: Incompatible types in assignment (expression has type "C", variable has type "B[Any]") a.f(c) # E: Argument 1 to "f" of "B" has incompatible type "C"; expected "B[Any]" @@ -350,9 +354,9 @@ a.f(bc) [case testInheritanceFromGenericWithImplicitDynamic] from typing import TypeVar, Generic T = TypeVar('T') -a = None # type: A -c = None # type: C -bc = None # type: B[C] +a: A +c: C +bc: B[C] class B(Generic[T]): def f(self, a: 'B[T]') -> None: pass @@ -458,10 +462,10 @@ from typing import TypeVar, Generic from abc import abstractmethod T = TypeVar('T') S = TypeVar('S') -acd = None # type: A[C, D] -adc = None # type: A[D, C] -ic = None # type: I[C] -id = None # type: I[D] +acd: A[C, D] +adc: A[D, C] +ic: I[C] +id: I[D] if int(): ic = acd # E: Incompatible types in assignment (expression has type "A[C, D]", variable has type "I[C]") @@ -482,8 +486,11 @@ class D: pass [case testSubtypingWithTypeImplementingGenericABCViaInheritance] from typing import TypeVar, Generic S = TypeVar('S') -a, b = None, None # type: (A, B) -ic, id, ie = None, None, None # type: (I[C], I[D], I[E]) +a: A +b: B +ic: I[C] +id: I[D] +ie: I[E] class I(Generic[S]): pass class B(I[C]): pass @@ -523,7 +530,9 @@ main:5: error: Class "B" has base "I" duplicated inconsistently from typing import TypeVar, Generic from abc import abstractmethod, ABCMeta t = TypeVar('t') -a, i, j = None, None, None # type: (A[object], I[object], J[object]) +a: A[object] +i: I[object] +j: J[object] (ii, jj) = (i, j) if int(): ii = a @@ -573,8 +582,9 @@ class D: pass from typing import Any, TypeVar, Generic from abc import abstractmethod T = TypeVar('T') -a = None # type: A -ic, id = None, None # type: (I[C], I[D]) +a: A +ic: I[C] +id: I[D] if int(): id = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[D]") @@ -625,9 +635,9 @@ class D: pass from typing import Any, TypeVar, Generic from abc import abstractmethod T = TypeVar('T') -a = None # type: Any -ic = None # type: I[C] -id = None # type: I[D] +a: Any +ic: I[C] +id: I[D] ic = a id = a @@ -645,9 +655,9 @@ class D: pass from typing import Any, TypeVar, Generic from abc import abstractmethod T = TypeVar('T') -a = None # type: Any -ic = None # type: I[C] -id = None # type: I[D] +a: Any +ic: I[C] +id: I[D] ic = a id = a @@ -666,9 +676,9 @@ class D: pass from typing import Any, TypeVar, Generic from abc import abstractmethod T = TypeVar('T') -a = None # type: Any -jc = None # type: J[C] -jd = None # type: J[D] +a: Any +jc: J[C] +jd: J[D] jc = a jd = a @@ -700,8 +710,9 @@ class I(Generic[T]): class A: pass class B: pass -a, b = None, None # type: (A, B) -ia = None # type: I[A] +a: A +b: B +ia: I[A] ia.f(b) # E: Argument 1 to "f" of "I" has incompatible type "B"; expected "A" ia.f(a) @@ -717,8 +728,9 @@ class J(Generic[T]): class I(J[T], Generic[T]): pass class A: pass class B: pass -a, b = None, None # type: (A, B) -ia = None # type: I[A] +a: A +b: B +ia: I[A] ia.f(b) # E: Argument 1 to "f" of "J" has incompatible type "B"; expected "A" ia.f(a) @@ -731,7 +743,8 @@ ia.f(a) [case testMultipleAssignmentAndGenericSubtyping] from typing import Iterable -n, s = None, None # type: int, str +n: int +s: str class Nums(Iterable[int]): def __iter__(self): pass def __next__(self): pass @@ -754,9 +767,9 @@ class A: pass class B(A): pass class C(B): pass -a = None # type: G[A] -b = None # type: G[B] -c = None # type: G[C] +a: G[A] +b: G[B] +c: G[C] if int(): b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") @@ -773,9 +786,9 @@ class A: pass class B(A): pass class C(B): pass -a = None # type: G[A] -b = None # type: G[B] -c = None # type: G[C] +a: G[A] +b: G[B] +c: G[C] if int(): b = a @@ -792,9 +805,9 @@ class A: pass class B(A): pass class C(B): pass -a = None # type: G[A] -b = None # type: G[B] -c = None # type: G[C] +a: G[A] +b: G[B] +c: G[C] if int(): b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b78fd21d4817..42e3d23eddb9 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -5,7 +5,9 @@ [case testGenericMethodReturnType] from typing import TypeVar, Generic T = TypeVar('T') -a, b, c = None, None, None # type: (A[B], B, C) +a: A[B] +b: B +c: C if int(): c = a.f() # E: Incompatible types in assignment (expression has type "B", variable has type "C") b = a.f() @@ -24,9 +26,9 @@ T = TypeVar('T') class A(Generic[T]): def f(self, a: T) -> None: pass -a = None # type: A[B] -b = None # type: B -c = None # type: C +a: A[B] +b: B +c: C a.f(c) # E: Argument 1 to "f" of "A" has incompatible type "C"; expected "B" a.f(b) @@ -40,7 +42,9 @@ class A(Generic[T]): def __init__(self, v: T) -> None: self.v = v -a, b, c = None, None, None # type: (A[B], B, C) +a: A[B] +b: B +c: C a.v = c # Fail a.v = b @@ -48,27 +52,31 @@ class B: pass class C: pass [builtins fixtures/tuple.pyi] [out] -main:8: error: Incompatible types in assignment (expression has type "C", variable has type "B") +main:10: error: Incompatible types in assignment (expression has type "C", variable has type "B") [case testGenericMemberVariable2] from typing import TypeVar, Generic T = TypeVar('T') -a, b, c = None, None, None # type: (A[B], B, C) +a: A[B] +b: B +c: C a.v = c # Fail a.v = b class A(Generic[T]): - v = None # type: T + v: T class B: pass class C: pass [builtins fixtures/tuple.pyi] [out] -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "B") +main:6: error: Incompatible types in assignment (expression has type "C", variable has type "B") [case testSimpleGenericSubtyping] from typing import TypeVar, Generic T = TypeVar('T') -b, bb, c = None, None, None # type: (A[B], A[B], A[C]) +b: A[B] +bb: A[B] +c: A[C] if int(): c = b # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") b = c # E: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") @@ -86,7 +94,9 @@ class C(B): pass [case testGenericTypeCompatibilityWithAny] from typing import Any, TypeVar, Generic T = TypeVar('T') -b, c, d = None, None, None # type: (A[B], A[C], A[Any]) +b: A[B] +c: A[C] +d: A[Any] b = d c = d @@ -102,9 +112,9 @@ class C(B): pass [case testTypeVariableAsTypeArgument] from typing import TypeVar, Generic T = TypeVar('T') -a = None # type: A[B] -b = None # type: A[B] -c = None # type: A[C] +a: A[B] +b: A[B] +c: A[C] a.v = c # E: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") if int(): @@ -123,9 +133,9 @@ class C: pass from typing import TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -a = None # type: A[B, C] -s = None # type: B -t = None # type: C +a: A[B, C] +s: B +t: C if int(): t = a.s # E: Incompatible types in assignment (expression has type "B", variable has type "C") @@ -136,8 +146,8 @@ if int(): t = a.t class A(Generic[S, T]): - s = None # type: S - t = None # type: T + s: S + t: T class B: pass class C: pass @@ -145,9 +155,9 @@ class C: pass from typing import TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -a = None # type: A[B, C] -s = None # type: B -t = None # type: C +a: A[B, C] +s: B +t: C a.f(s, s) # Fail a.f(t, t) # Fail @@ -165,9 +175,9 @@ main:9: error: Argument 1 to "f" of "A" has incompatible type "C"; expected "B" from typing import TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -bc = None # type: A[B, C] -bb = None # type: A[B, B] -cb = None # type: A[C, B] +bc: A[B, C] +bb: A[B, B] +cb: A[C, B] if int(): bb = bc # E: Incompatible types in assignment (expression has type "A[B, C]", variable has type "A[B, B]") @@ -180,8 +190,8 @@ if int(): bc = bc class A(Generic[S, T]): - s = None # type: S - t = None # type: T + s: S + t: T class B: pass class C(B):pass @@ -195,7 +205,7 @@ class C(B):pass from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): - a = None # type: T + a: T def f(self, b: T) -> T: self.f(x) # Fail @@ -203,7 +213,7 @@ class A(Generic[T]): self.a = self.f(self.a) return self.a c = self # type: A[T] -x = None # type: B +x: B class B: pass [out] main:7: error: Argument 1 to "f" of "A" has incompatible type "B"; expected "T" @@ -215,8 +225,8 @@ S = TypeVar('S') T = TypeVar('T') class A(Generic[S, T]): def f(self) -> None: - s = None # type: S - t = None # type: T + s: S + t: T if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") t = s # E: Incompatible types in assignment (expression has type "S", variable has type "T") @@ -230,6 +240,7 @@ class B: pass [out] [case testCompatibilityOfNoneWithTypeVar] +# flags: --no-strict-optional from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): @@ -239,6 +250,7 @@ class A(Generic[T]): [out] [case testCompatibilityOfTypeVarWithObject] +# flags: --no-strict-optional from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): @@ -261,9 +273,9 @@ class A(Generic[T]): from typing import TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -a = None # type: A[B, C] -b = None # type: B -c = None # type: C +a: A[B, C] +b: B +c: C if int(): b = a + b # E: Incompatible types in assignment (expression has type "C", variable has type "B") @@ -286,9 +298,9 @@ class C: pass [case testOperatorAssignmentWithIndexLvalue1] from typing import TypeVar, Generic T = TypeVar('T') -b = None # type: B -c = None # type: C -ac = None # type: A[C] +b: B +c: C +ac: A[C] ac[b] += b # Fail ac[c] += c # Fail @@ -309,9 +321,9 @@ main:8: error: Invalid index type "C" for "A[C]"; expected type "B" [case testOperatorAssignmentWithIndexLvalue2] from typing import TypeVar, Generic T = TypeVar('T') -b = None # type: B -c = None # type: C -ac = None # type: A[C] +b: B +c: C +ac: A[C] ac[b] += c # Fail ac[c] += c # Fail @@ -337,10 +349,10 @@ main:9: error: Invalid index type "B" for "A[C]"; expected type "C" [case testNestedGenericTypes] from typing import TypeVar, Generic T = TypeVar('T') -aab = None # type: A[A[B]] -aac = None # type: A[A[C]] -ab = None # type: A[B] -ac = None # type: A[C] +aab: A[A[B]] +aac: A[A[C]] +ab: A[B] +ac: A[C] if int(): ac = aab.x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") @@ -353,8 +365,8 @@ ab.y = aab ac.y = aac class A(Generic[T]): - x = None # type: T - y = None # type: A[A[T]] + x: T + y: A[A[T]] class B: pass @@ -377,12 +389,12 @@ def f(s: S, t: T) -> p[T, A]: a = t # type: S # E: Incompatible types in assignment (expression has type "T", variable has type "S") if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") - p_s_a = None # type: p[S, A] + p_s_a: p[S, A] if s: return p_s_a # E: Incompatible return value type (got "p[S, A]", expected "p[T, A]") b = t # type: T c = s # type: S - p_t_a = None # type: p[T, A] + p_t_a: p[T, A] return p_t_a [out] @@ -396,16 +408,16 @@ class A(Generic[T]): def f(self, s: S, t: T) -> p[S, T]: if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") - p_s_s = None # type: p[S, S] + p_s_s: p[S, S] if s: return p_s_s # E: Incompatible return value type (got "p[S, S]", expected "p[S, T]") - p_t_t = None # type: p[T, T] + p_t_t: p[T, T] if t: return p_t_t # E: Incompatible return value type (got "p[T, T]", expected "p[S, T]") if 1: t = t s = s - p_s_t = None # type: p[S, T] + p_s_t: p[S, T] return p_s_t [out] @@ -442,7 +454,7 @@ A[int, str, int]() # E: Type application has too many types (2 expected) [out] [case testInvalidTypeApplicationType] -a = None # type: A +a: A class A: pass a[A]() # E: Value of type "A" is not indexable A[A]() # E: The type "Type[A]" is not generic and not indexable @@ -546,7 +558,7 @@ IntIntNode = Node[int, int] SameNode = Node[T, T] def output_bad() -> IntNode[str]: - return Node(1, 1) # Eroor - bad return type, see out + return Node(1, 1) # Error - bad return type, see out def input(x: IntNode[str]) -> None: pass @@ -576,7 +588,7 @@ reveal_type(y) # N: Revealed type is "__main__.Node[builtins.str, builtins.str]" def wrap(x: T) -> IntNode[T]: return Node(1, x) -z = None # type: str +z: str reveal_type(wrap(z)) # N: Revealed type is "__main__.Node[builtins.int, builtins.str]" [out] @@ -686,7 +698,7 @@ def output() -> IntNode[str]: return Node(1, 'x') x = output() # type: IntNode # This is OK (implicit Any) -y = None # type: IntNode +y: IntNode y.x = 1 y.x = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") y.y = 1 # Both are OK (implicit Any) @@ -707,7 +719,7 @@ class Node(Generic[T]): return self.x ListedNode = Node[List[T]] -l = None # type: ListedNode[int] +l: ListedNode[int] l.x.append(1) l.meth().append(1) reveal_type(l.meth()) # N: Revealed type is "builtins.list[builtins.int]" @@ -848,7 +860,7 @@ def use_cb(arg: T, cb: C2[T]) -> Node[T]: return cb(arg, arg) use_cb(1, 1) # E: Argument 2 to "use_cb" has incompatible type "int"; expected "Callable[[int, int], Node[int]]" -my_cb = None # type: C2[int] +my_cb: C2[int] use_cb('x', my_cb) # E: Argument 2 to "use_cb" has incompatible type "Callable[[int, int], Node[int]]"; expected "Callable[[str, str], Node[str]]" reveal_type(use_cb(1, my_cb)) # N: Revealed type is "__main__.Node[builtins.int]" [builtins fixtures/tuple.pyi] @@ -884,7 +896,7 @@ from typing import TypeVar from a import Node, TupledNode T = TypeVar('T') -n = None # type: TupledNode[int] +n: TupledNode[int] n.x = 1 n.y = (1, 1) n.y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, int]") @@ -1282,9 +1294,9 @@ from typing import List class A: pass class B: pass class B2(B): pass -a = None # type: A -b = None # type: B -b2 = None # type: B2 +a: A +b: B +b2: B2 list_a = [a] list_b = [b] @@ -1313,8 +1325,8 @@ e, f = list_a # type: (A, object) [case testMultipleAssignmentWithListAndIndexing] from typing import List -a = None # type: List[A] -b = None # type: List[int] +a: List[A] +b: List[int] a[1], b[1] = a # E: Incompatible types in assignment (expression has type "A", target has type "int") a[1], a[2] = a @@ -1335,8 +1347,8 @@ class dict: pass [case testMultipleAssignmentWithIterable] from typing import Iterable, TypeVar -a = None # type: int -b = None # type: str +a: int +b: str T = TypeVar('T') def f(x: T) -> Iterable[T]: pass @@ -1380,7 +1392,7 @@ X = TypeVar('X') Y = TypeVar('Y') Z = TypeVar('Z') class OO: pass -a = None # type: A[object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object] +a: A[object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object] def f(a: OO) -> None: pass @@ -1393,7 +1405,7 @@ class A(Generic[B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W from typing import TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -a = None # type: A[object, B] +a: A[object, B] def f(a: 'B') -> None: pass f(a) # E: Argument 1 to "f" has incompatible type "A[object, B]"; expected "B" @@ -1405,7 +1417,7 @@ class B: pass from typing import Callable, TypeVar, Generic S = TypeVar('S') T = TypeVar('T') -a = None # type: A[object, Callable[[], None]] +a: A[object, Callable[[], None]] def f(a: 'B') -> None: pass f(a) # E: Argument 1 to "f" has incompatible type "A[object, Callable[[], None]]"; expected "B" @@ -1424,7 +1436,8 @@ from foo import * from typing import overload, List class A: pass class B: pass -a, b = None, None # type: (A, B) +a: A +b: B @overload def f(a: List[A]) -> A: pass @@ -1452,7 +1465,8 @@ def f(a: B) -> B: pass @overload def f(a: List[T]) -> T: pass -a, b = None, None # type: (A, B) +a: A +b: B if int(): b = f([a]) # E: Incompatible types in assignment (expression has type "A", variable has type "B") From 8c70e8043db2c3b537bdfaa42632451950f51b68 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Jul 2023 18:09:27 +0100 Subject: [PATCH 0112/1617] [mypyc] Support the u8 native integer type (#15564) This is mostly similar to `i16` that I added recently in #15464, but there are some differences: * Some adjustments were needed to support unsigned integers * Add overflow checking of literals, since it's easy to over/underflow when using `u8` due to limited range * Rename primitive integer types from `int16` to `i16` (etc.) to match the user-visible types (needed to get some error messages consistent, and it's generally nicer) * Overall make things a bit more consistent * Actually update `mypy_extensions` stubs This is an unsigned type to make it easier to work with binary/bytes data. The item values for `bytes` are unsigned 8-bit values, in particular. This type will become much more useful once we support packed arrays. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/types.py | 1 + .../stubs/mypy-extensions/mypy_extensions.pyi | 35 ++ mypyc/codegen/emit.py | 11 +- mypyc/doc/float_operations.rst | 2 + mypyc/doc/int_operations.rst | 42 +- mypyc/doc/using_type_annotations.rst | 15 +- mypyc/ir/ops.py | 1 + mypyc/ir/rtypes.py | 77 ++- mypyc/irbuild/builder.py | 8 +- mypyc/irbuild/expression.py | 19 +- mypyc/irbuild/ll_builder.py | 101 +++- mypyc/irbuild/mapper.py | 3 + mypyc/irbuild/specialize.py | 60 +- mypyc/lib-rt/CPy.h | 2 + mypyc/lib-rt/int_ops.c | 49 ++ mypyc/lib-rt/mypyc_util.h | 5 +- mypyc/primitives/int_ops.py | 8 + mypyc/test-data/exceptions.test | 28 +- mypyc/test-data/irbuild-any.test | 6 +- mypyc/test-data/irbuild-basic.test | 60 +- mypyc/test-data/irbuild-bool.test | 44 +- mypyc/test-data/irbuild-bytes.test | 4 +- mypyc/test-data/irbuild-classes.test | 24 +- mypyc/test-data/irbuild-dict.test | 28 +- mypyc/test-data/irbuild-dunders.test | 4 +- mypyc/test-data/irbuild-float.test | 2 +- mypyc/test-data/irbuild-generics.test | 4 +- mypyc/test-data/irbuild-glue-methods.test | 24 +- mypyc/test-data/irbuild-i16.test | 112 ++-- mypyc/test-data/irbuild-i32.test | 106 ++-- mypyc/test-data/irbuild-i64.test | 384 ++++++------- mypyc/test-data/irbuild-isinstance.test | 16 +- mypyc/test-data/irbuild-lists.test | 14 +- mypyc/test-data/irbuild-match.test | 140 ++--- mypyc/test-data/irbuild-optional.test | 10 +- mypyc/test-data/irbuild-set.test | 76 +-- mypyc/test-data/irbuild-singledispatch.test | 8 +- mypyc/test-data/irbuild-statements.test | 32 +- mypyc/test-data/irbuild-str.test | 4 +- mypyc/test-data/irbuild-try.test | 8 +- mypyc/test-data/irbuild-tuple.test | 2 +- mypyc/test-data/irbuild-u8.test | 543 ++++++++++++++++++ mypyc/test-data/irbuild-unreachable.test | 4 +- mypyc/test-data/refcount.test | 6 +- mypyc/test-data/run-u8.test | 303 ++++++++++ mypyc/test/test_irbuild.py | 1 + mypyc/test/test_ircheck.py | 4 +- mypyc/test/test_run.py | 1 + mypyc/test/test_struct.py | 4 +- test-data/unit/lib-stub/mypy_extensions.pyi | 32 +- 50 files changed, 1843 insertions(+), 634 deletions(-) create mode 100644 mypyc/test-data/irbuild-u8.test create mode 100644 mypyc/test-data/run-u8.test diff --git a/mypy/types.py b/mypy/types.py index 784ef538f197..ba629a3553cf 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -155,6 +155,7 @@ "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16", + "mypy_extensions.u8", ) DATACLASS_TRANSFORM_NAMES: Final = ( diff --git a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi index 86a071500b34..b6358a0022f3 100644 --- a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi +++ b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi @@ -181,3 +181,38 @@ class i16: def __ge__(self, x: i16) -> bool: ... def __gt__(self, x: i16) -> bool: ... def __index__(self) -> int: ... + +class u8: + @overload + def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> u8: ... + @overload + def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> u8: ... + + def __add__(self, x: u8) -> u8: ... + def __radd__(self, x: u8) -> u8: ... + def __sub__(self, x: u8) -> u8: ... + def __rsub__(self, x: u8) -> u8: ... + def __mul__(self, x: u8) -> u8: ... + def __rmul__(self, x: u8) -> u8: ... + def __floordiv__(self, x: u8) -> u8: ... + def __rfloordiv__(self, x: u8) -> u8: ... + def __mod__(self, x: u8) -> u8: ... + def __rmod__(self, x: u8) -> u8: ... + def __and__(self, x: u8) -> u8: ... + def __rand__(self, x: u8) -> u8: ... + def __or__(self, x: u8) -> u8: ... + def __ror__(self, x: u8) -> u8: ... + def __xor__(self, x: u8) -> u8: ... + def __rxor__(self, x: u8) -> u8: ... + def __lshift__(self, x: u8) -> u8: ... + def __rlshift__(self, x: u8) -> u8: ... + def __rshift__(self, x: u8) -> u8: ... + def __rrshift__(self, x: u8) -> u8: ... + def __neg__(self) -> u8: ... + def __invert__(self) -> u8: ... + def __pos__(self) -> u8: ... + def __lt__(self, x: u8) -> bool: ... + def __le__(self, x: u8) -> bool: ... + def __ge__(self, x: u8) -> bool: ... + def __gt__(self, x: u8) -> bool: ... + def __index__(self) -> int: ... diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 1bd376754ab9..7d41ee7e162b 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -47,6 +47,7 @@ is_short_int_rprimitive, is_str_rprimitive, is_tuple_rprimitive, + is_uint8_rprimitive, object_rprimitive, optional_value_type, ) @@ -922,6 +923,14 @@ def emit_unbox( self.emit_line(f"{dest} = CPyLong_AsInt16({src});") if not isinstance(error, AssignHandler): self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) + elif is_uint8_rprimitive(typ): + # Whether we are borrowing or not makes no difference. + assert not optional # Not supported for overlapping error values + if declare_dest: + self.emit_line(f"uint8_t {dest};") + self.emit_line(f"{dest} = CPyLong_AsUInt8({src});") + if not isinstance(error, AssignHandler): + self.emit_unbox_failure_with_overlapping_error_value(dest, typ, failure) elif is_float_rprimitive(typ): assert not optional # Not supported for overlapping error values if declare_dest: @@ -1013,7 +1022,7 @@ def emit_box( self.emit_lines(f"{declaration}{dest} = Py_None;") if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) - elif is_int32_rprimitive(typ) or is_int16_rprimitive(typ): + elif is_int32_rprimitive(typ) or is_int16_rprimitive(typ) or is_uint8_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLong({src});") elif is_int64_rprimitive(typ): self.emit_line(f"{declaration}{dest} = PyLong_FromLongLong({src});") diff --git a/mypyc/doc/float_operations.rst b/mypyc/doc/float_operations.rst index 1705b7672409..feae5a806c70 100644 --- a/mypyc/doc/float_operations.rst +++ b/mypyc/doc/float_operations.rst @@ -15,6 +15,7 @@ Construction * ``float(x: i64)`` * ``float(x: i32)`` * ``float(x: i16)`` +* ``float(x: u8)`` * ``float(x: str)`` * ``float(x: float)`` (no-op) @@ -32,6 +33,7 @@ Functions * ``i64(f)`` (convert to 64-bit signed integer) * ``i32(f)`` (convert to 32-bit signed integer) * ``i16(f)`` (convert to 16-bit signed integer) +* ``u8(f)`` (convert to 8-bit unsigned integer) * ``abs(f)`` * ``math.sin(f)`` * ``math.cos(f)`` diff --git a/mypyc/doc/int_operations.rst b/mypyc/doc/int_operations.rst index 88a4a9d778a1..eb875f5c9452 100644 --- a/mypyc/doc/int_operations.rst +++ b/mypyc/doc/int_operations.rst @@ -9,11 +9,12 @@ Mypyc supports these integer types: * ``i64`` (64-bit signed integer) * ``i32`` (32-bit signed integer) * ``i16`` (16-bit signed integer) +* ``u8`` (8-bit unsigned integer) -``i64``, ``i32`` and ``i16`` are *native integer types* and must be imported -from the ``mypy_extensions`` module. ``int`` corresponds to the Python -``int`` type, but uses a more efficient runtime representation (tagged -pointer). Native integer types are value types. +``i64``, ``i32``, ``i16`` and ``u8`` are *native integer types* and +are available in the ``mypy_extensions`` module. ``int`` corresponds +to the Python ``int`` type, but uses a more efficient runtime +representation (tagged pointer). Native integer types are value types. All integer types have optimized primitive operations, but the native integer types are more efficient than ``int``, since they don't @@ -34,6 +35,7 @@ Construction * ``int(x: i64)`` * ``int(x: i32)`` * ``int(x: i16)`` +* ``int(x: u8)`` * ``int(x: str)`` * ``int(x: str, base: int)`` * ``int(x: int)`` (no-op) @@ -42,21 +44,23 @@ Construction * ``i64(x: int)`` * ``i64(x: float)`` +* ``i64(x: i64)`` (no-op) * ``i64(x: i32)`` * ``i64(x: i16)`` +* ``i64(x: u8)`` * ``i64(x: str)`` * ``i64(x: str, base: int)`` -* ``i64(x: i64)`` (no-op) ``i32`` type: * ``i32(x: int)`` * ``i32(x: float)`` * ``i32(x: i64)`` (truncate) +* ``i32(x: i32)`` (no-op) * ``i32(x: i16)`` +* ``i32(x: u8)`` * ``i32(x: str)`` * ``i32(x: str, base: int)`` -* ``i32(x: i32)`` (no-op) ``i16`` type: @@ -64,9 +68,10 @@ Construction * ``i16(x: float)`` * ``i16(x: i64)`` (truncate) * ``i16(x: i32)`` (truncate) +* ``i16(x: i16)`` (no-op) +* ``i16(x: u8)`` * ``i16(x: str)`` * ``i16(x: str, base: int)`` -* ``i16(x: i16)`` (no-op) Conversions from ``int`` to a native integer type raise ``OverflowError`` if the value is too large or small. Conversions from @@ -80,6 +85,8 @@ Implicit conversions ``int`` values can be implicitly converted to a native integer type, for convenience. This means that these are equivalent:: + from mypy_extensions import i64 + def implicit() -> None: # Implicit conversion of 0 (int) to i64 x: i64 = 0 @@ -107,18 +114,23 @@ Operators * Comparisons (``==``, ``!=``, ``<``, etc.) * Augmented assignment (``x += y``, etc.) -If one of the above native integer operations overflows or underflows, -the behavior is undefined. Native integer types should only be used if -all possible values are small enough for the type. For this reason, -the arbitrary-precision ``int`` type is recommended unless the -performance of integer operations is critical. +If one of the above native integer operations overflows or underflows +with signed operands, the behavior is undefined. Signed native integer +types should only be used if all possible values are small enough for +the type. For this reason, the arbitrary-precision ``int`` type is +recommended for signed values unless the performance of integer +operations is critical. + +Operations on unsigned integers (``u8``) wrap around on overflow. It's a compile-time error to mix different native integer types in a binary operation such as addition. An explicit conversion is required:: - def add(x: i64, y: i32) -> None: - a = x + y # Error (i64 + i32) - b = x + i64(y) # OK + from mypy_extensions import i64, i32 + + def add(x: i64, y: i32) -> None: + a = x + y # Error (i64 + i32) + b = x + i64(y) # OK You can freely mix a native integer value and an arbitrary-precision ``int`` value in an operation. The native integer type is "sticky" diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 5bfff388e433..04c923819d54 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -33,6 +33,7 @@ implementations: * ``i64`` (:ref:`documentation `, :ref:`native operations `) * ``i32`` (:ref:`documentation `, :ref:`native operations `) * ``i16`` (:ref:`documentation `, :ref:`native operations `) +* ``u8`` (:ref:`documentation `, :ref:`native operations `) * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) @@ -344,13 +345,13 @@ Native integer types -------------------- You can use the native integer types ``i64`` (64-bit signed integer), -``i32`` (32-bit signed integer), and ``i16`` (16-bit signed integer) -if you know that integer values will always fit within fixed -bounds. These types are faster than the arbitrary-precision ``int`` -type, since they don't require overflow checks on operations. ``i32`` -and ``i16`` may also use less memory than ``int`` values. The types -are imported from the ``mypy_extensions`` module (installed via ``pip -install mypy_extensions``). +``i32`` (32-bit signed integer), ``i16`` (16-bit signed integer), and +``u8`` (8-bit unsigned integer) if you know that integer values will +always fit within fixed bounds. These types are faster than the +arbitrary-precision ``int`` type, since they don't require overflow +checks on operations. They may also use less memory than ``int`` +values. The types are imported from the ``mypy_extensions`` module +(installed via ``pip install mypy_extensions``). Example:: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index cb8d9662820c..d80c479211b7 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1162,6 +1162,7 @@ class ComparisonOp(RegisterOp): } signed_ops: Final = {"==": EQ, "!=": NEQ, "<": SLT, ">": SGT, "<=": SLE, ">=": SGE} + unsigned_ops: Final = {"==": EQ, "!=": NEQ, "<": ULT, ">": UGT, "<=": ULE, ">=": UGE} def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: super().__init__(line) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fe0c51ea2221..fa46feb0b59a 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -23,7 +23,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar +from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar +from typing_extensions import Final, TypeGuard from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator @@ -209,9 +210,8 @@ def __init__( # This is basically an arbitrary value that is pretty # unlikely to overlap with a real value. self.c_undefined = "-113" - elif ctype in ("CPyPtr", "uint32_t", "uint64_t"): - # TODO: For low-level integers, we need to invent an overlapping - # error value, similar to int64_t above. + elif ctype == "CPyPtr": + # TODO: Invent an overlapping error value? self.c_undefined = "0" elif ctype == "PyObject *": # Boxed types use the null pointer as the error value. @@ -222,6 +222,8 @@ def __init__( self.c_undefined = "NULL" elif ctype == "double": self.c_undefined = "-113.0" + elif ctype in ("uint8_t", "uint16_t", "uint32_t", "uint64_t"): + self.c_undefined = "239" # An arbitrary number else: assert False, "Unrecognized ctype: %r" % ctype @@ -290,7 +292,7 @@ def __hash__(self) -> int: # Low level integer types (correspond to C integer types) int16_rprimitive: Final = RPrimitive( - "int16", + "i16", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -300,7 +302,7 @@ def __hash__(self) -> int: error_overlap=True, ) int32_rprimitive: Final = RPrimitive( - "int32", + "i32", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -310,7 +312,7 @@ def __hash__(self) -> int: error_overlap=True, ) int64_rprimitive: Final = RPrimitive( - "int64", + "i64", is_unboxed=True, is_refcounted=False, is_native_int=True, @@ -319,23 +321,49 @@ def __hash__(self) -> int: size=8, error_overlap=True, ) +uint8_rprimitive: Final = RPrimitive( + "u8", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=False, + ctype="uint8_t", + size=1, + error_overlap=True, +) + +# The following unsigned native int types (u16, u32, u64) are not +# exposed to the user. They are for internal use within mypyc only. + +u16_rprimitive: Final = RPrimitive( + "u16", + is_unboxed=True, + is_refcounted=False, + is_native_int=True, + is_signed=False, + ctype="uint16_t", + size=2, + error_overlap=True, +) uint32_rprimitive: Final = RPrimitive( - "uint32", + "u32", is_unboxed=True, is_refcounted=False, is_native_int=True, is_signed=False, ctype="uint32_t", size=4, + error_overlap=True, ) uint64_rprimitive: Final = RPrimitive( - "uint64", + "u64", is_unboxed=True, is_refcounted=False, is_native_int=True, is_signed=False, ctype="uint64_t", size=8, + error_overlap=True, ) # The C 'int' type @@ -441,11 +469,11 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive -def is_int16_rprimitive(rtype: RType) -> bool: +def is_int16_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int16_rprimitive -def is_int32_rprimitive(rtype: RType) -> bool: +def is_int32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int32_rprimitive or ( rtype is c_pyssize_t_rprimitive and rtype._ctype == "int32_t" ) @@ -457,8 +485,17 @@ def is_int64_rprimitive(rtype: RType) -> bool: ) -def is_fixed_width_rtype(rtype: RType) -> bool: - return is_int64_rprimitive(rtype) or is_int32_rprimitive(rtype) or is_int16_rprimitive(rtype) +def is_fixed_width_rtype(rtype: RType) -> TypeGuard[RPrimitive]: + return ( + is_int64_rprimitive(rtype) + or is_int32_rprimitive(rtype) + or is_int16_rprimitive(rtype) + or is_uint8_rprimitive(rtype) + ) + + +def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return rtype is uint8_rprimitive def is_uint32_rprimitive(rtype: RType) -> bool: @@ -551,6 +588,8 @@ def visit_rprimitive(self, t: RPrimitive) -> str: return "4" # "4 byte integer" elif t._ctype == "int16_t": return "2" # "2 byte integer" + elif t._ctype == "uint8_t": + return "U1" # "1 byte unsigned integer" elif t._ctype == "double": return "F" assert not t.is_unboxed, f"{t} unexpected unboxed type" @@ -985,3 +1024,15 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> RArray: names=["ob_base", "ob_item", "allocated"], types=[PyVarObject, pointer_rprimitive, c_pyssize_t_rprimitive], ) + + +def check_native_int_range(rtype: RPrimitive, n: int) -> bool: + """Is n within the range of a native, fixed-width int type? + + Assume the type is a fixed-width int type. + """ + if not rtype.is_signed: + return 0 <= n < (1 << (8 * rtype.size)) + else: + limit = 1 << (rtype.size * 8 - 1) + return -limit <= n < limit diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 10f057a29bbb..8c68f91bf633 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -159,7 +159,7 @@ def __init__( options: CompilerOptions, singledispatch_impls: dict[FuncDef, list[RegisterImplInfo]], ) -> None: - self.builder = LowLevelIRBuilder(current_module, mapper, options) + self.builder = LowLevelIRBuilder(current_module, errors, mapper, options) self.builders = [self.builder] self.symtables: list[dict[SymbolNode, SymbolTarget]] = [{}] self.runtime_args: list[list[RuntimeArg]] = [[]] @@ -224,6 +224,7 @@ def set_module(self, module_name: str, module_path: str) -> None: """ self.module_name = module_name self.module_path = module_path + self.builder.set_module(module_name, module_path) @overload def accept(self, node: Expression, *, can_borrow: bool = False) -> Value: @@ -1102,7 +1103,10 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None: def enter(self, fn_info: FuncInfo | str = "") -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) - self.builder = LowLevelIRBuilder(self.current_module, self.mapper, self.options) + self.builder = LowLevelIRBuilder( + self.current_module, self.errors, self.mapper, self.options + ) + self.builder.set_module(self.module_name, self.module_path) self.builders.append(self.builder) self.symtables.append({}) self.runtime_args.append([]) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index ada3d47cefab..8d205b432d2d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -815,21 +815,30 @@ def transform_basic_comparison( return builder.compare_tagged(left, right, op, line) if is_fixed_width_rtype(left.type) and op in int_comparison_op_mapping: if right.type == left.type: - op_id = ComparisonOp.signed_ops[op] + if left.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op(left, right, op_id, line) elif isinstance(right, Integer): - op_id = ComparisonOp.signed_ops[op] + if left.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( - left, Integer(right.value >> 1, left.type), op_id, line + left, builder.coerce(right, left.type, line), op_id, line ) elif ( is_fixed_width_rtype(right.type) and op in int_comparison_op_mapping and isinstance(left, Integer) ): - op_id = ComparisonOp.signed_ops[op] + if right.type.is_signed: + op_id = ComparisonOp.signed_ops[op] + else: + op_id = ComparisonOp.unsigned_ops[op] return builder.builder.comparison_op( - Integer(left.value >> 1, right.type), right, op_id, line + builder.coerce(left, right.type, line), right, op_id, line ) negate = False diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index e34f03704047..984b6a4deec0 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -27,6 +27,7 @@ use_method_vectorcall, use_vectorcall, ) +from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR, all_concrete_classes from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.ops import ( @@ -94,6 +95,7 @@ c_pointer_rprimitive, c_pyssize_t_rprimitive, c_size_t_rprimitive, + check_native_int_range, dict_rprimitive, float_rprimitive, int_rprimitive, @@ -114,6 +116,7 @@ is_str_rprimitive, is_tagged, is_tuple_rprimitive, + is_uint8_rprimitive, list_rprimitive, none_rprimitive, object_pointer_rprimitive, @@ -159,6 +162,7 @@ int_to_int32_op, int_to_int64_op, ssize_t_to_int_op, + uint8_overflow, ) from mypyc.primitives.list_ops import list_build_op, list_extend_op, new_list_op from mypyc.primitives.misc_ops import bool_op, fast_isinstance_op, none_object_op @@ -216,8 +220,11 @@ class LowLevelIRBuilder: - def __init__(self, current_module: str, mapper: Mapper, options: CompilerOptions) -> None: + def __init__( + self, current_module: str, errors: Errors, mapper: Mapper, options: CompilerOptions + ) -> None: self.current_module = current_module + self.errors = errors self.mapper = mapper self.options = options self.args: list[Register] = [] @@ -228,6 +235,11 @@ def __init__(self, current_module: str, mapper: Mapper, options: CompilerOptions # temporaries. Use flush_keep_alives() to mark the end of the live range. self.keep_alives: list[Value] = [] + def set_module(self, module_name: str, module_path: str) -> None: + """Set the name and path of the current module.""" + self.module_name = module_name + self.module_path = module_path + # Basic operations def add(self, op: Op) -> Value: @@ -323,7 +335,9 @@ def coerce( and is_short_int_rprimitive(src_type) and is_fixed_width_rtype(target_type) ): - # TODO: range check + value = src.numeric_value() + if not check_native_int_range(target_type, value): + self.error(f'Value {value} is out of range for "{target_type}"', line) return Integer(src.value >> 1, target_type) elif is_int_rprimitive(src_type) and is_fixed_width_rtype(target_type): return self.coerce_int_to_fixed_width(src, target_type, line) @@ -413,10 +427,16 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - # Add a range check when the target type is smaller than the source tyoe fast2, fast3 = BasicBlock(), BasicBlock() upper_bound = 1 << (size * 8 - 1) + if not target_type.is_signed: + upper_bound *= 2 check2 = self.add(ComparisonOp(src, Integer(upper_bound, src.type), ComparisonOp.SLT)) self.add(Branch(check2, fast2, slow, Branch.BOOL)) self.activate_block(fast2) - check3 = self.add(ComparisonOp(src, Integer(-upper_bound, src.type), ComparisonOp.SGE)) + if target_type.is_signed: + lower_bound = -upper_bound + else: + lower_bound = 0 + check3 = self.add(ComparisonOp(src, Integer(lower_bound, src.type), ComparisonOp.SGE)) self.add(Branch(check3, fast3, slow, Branch.BOOL)) self.activate_block(fast3) tmp = self.int_op( @@ -463,6 +483,10 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - # Slow path just always generates an OverflowError self.call_c(int16_overflow, [], line) self.add(Unreachable()) + elif is_uint8_rprimitive(target_type): + # Slow path just always generates an OverflowError + self.call_c(uint8_overflow, [], line) + self.add(Unreachable()) else: assert False, target_type @@ -476,9 +500,13 @@ def coerce_short_int_to_fixed_width(self, src: Value, target_type: RType, line: assert False, (src.type, target_type) def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: - if (is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) or is_int16_rprimitive(src.type): + if ( + (is_int32_rprimitive(src.type) and PLATFORM_SIZE == 8) + or is_int16_rprimitive(src.type) + or is_uint8_rprimitive(src.type) + ): # Simple case -- just sign extend and shift. - extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=True)) + extended = self.add(Extend(src, c_pyssize_t_rprimitive, signed=src.type.is_signed)) return self.int_op( int_rprimitive, extended, @@ -1310,9 +1338,8 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if is_fixed_width_rtype(rtype) or is_tagged(rtype): return self.fixed_width_int_op(ltype, lreg, rreg, op_id, line) if isinstance(rreg, Integer): - # TODO: Check what kind of Integer return self.fixed_width_int_op( - ltype, lreg, Integer(rreg.value >> 1, ltype), op_id, line + ltype, lreg, self.coerce(rreg, ltype, line), op_id, line ) elif op in ComparisonOp.signed_ops: if is_int_rprimitive(rtype): @@ -1323,7 +1350,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if is_fixed_width_rtype(rreg.type): return self.comparison_op(lreg, rreg, op_id, line) if isinstance(rreg, Integer): - return self.comparison_op(lreg, Integer(rreg.value >> 1, ltype), op_id, line) + return self.comparison_op(lreg, self.coerce(rreg, ltype, line), op_id, line) elif is_fixed_width_rtype(rtype): if op in FIXED_WIDTH_INT_BINARY_OPS: if op.endswith("="): @@ -1333,9 +1360,8 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: else: op_id = IntOp.DIV if isinstance(lreg, Integer): - # TODO: Check what kind of Integer return self.fixed_width_int_op( - rtype, Integer(lreg.value >> 1, rtype), rreg, op_id, line + rtype, self.coerce(lreg, rtype, line), rreg, op_id, line ) if is_tagged(ltype): return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line) @@ -1349,7 +1375,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: lreg = self.coerce(lreg, rtype, line) op_id = ComparisonOp.signed_ops[op] if isinstance(lreg, Integer): - return self.comparison_op(Integer(lreg.value >> 1, rtype), rreg, op_id, line) + return self.comparison_op(self.coerce(lreg, rtype, line), rreg, op_id, line) if is_fixed_width_rtype(lreg.type): return self.comparison_op(lreg, rreg, op_id, line) @@ -1611,8 +1637,13 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: # Translate to '0 - x' return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line) elif expr_op == "~": - # Translate to 'x ^ -1' - return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) + if typ.is_signed: + # Translate to 'x ^ -1' + return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) + else: + # Translate to 'x ^ 0xff...' + mask = (1 << (typ.size * 8)) - 1 + return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line) elif expr_op == "+": return value if is_float_rprimitive(typ): @@ -2025,7 +2056,9 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: def compare_floats(self, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(FloatComparisonOp(lhs, rhs, op, line)) - def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: + def fixed_width_int_op( + self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int + ) -> Value: """Generate a binary op using Python fixed-width integer semantics. These may differ in overflow/rounding behavior from native/C ops. @@ -2037,35 +2070,60 @@ def fixed_width_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: lhs = self.coerce(lhs, type, line) rhs = self.coerce(rhs, type, line) if op == IntOp.DIV: - # Inline simple division by a constant, so that C - # compilers can optimize more if isinstance(rhs, Integer) and rhs.value not in (-1, 0): - return self.inline_fixed_width_divide(type, lhs, rhs, line) + if not type.is_signed: + return self.int_op(type, lhs, rhs, IntOp.DIV, line) + else: + # Inline simple division by a constant, so that C + # compilers can optimize more + return self.inline_fixed_width_divide(type, lhs, rhs, line) if is_int64_rprimitive(type): prim = int64_divide_op elif is_int32_rprimitive(type): prim = int32_divide_op elif is_int16_rprimitive(type): prim = int16_divide_op + elif is_uint8_rprimitive(type): + self.check_for_zero_division(rhs, type, line) + return self.int_op(type, lhs, rhs, op, line) else: assert False, type return self.call_c(prim, [lhs, rhs], line) if op == IntOp.MOD: - # Inline simple % by a constant, so that C - # compilers can optimize more if isinstance(rhs, Integer) and rhs.value not in (-1, 0): - return self.inline_fixed_width_mod(type, lhs, rhs, line) + if not type.is_signed: + return self.int_op(type, lhs, rhs, IntOp.MOD, line) + else: + # Inline simple % by a constant, so that C + # compilers can optimize more + return self.inline_fixed_width_mod(type, lhs, rhs, line) if is_int64_rprimitive(type): prim = int64_mod_op elif is_int32_rprimitive(type): prim = int32_mod_op elif is_int16_rprimitive(type): prim = int16_mod_op + elif is_uint8_rprimitive(type): + self.check_for_zero_division(rhs, type, line) + return self.int_op(type, lhs, rhs, op, line) else: assert False, type return self.call_c(prim, [lhs, rhs], line) return self.int_op(type, lhs, rhs, op, line) + def check_for_zero_division(self, rhs: Value, type: RType, line: int) -> None: + err, ok = BasicBlock(), BasicBlock() + is_zero = self.binary_op(rhs, Integer(0, type), "==", line) + self.add(Branch(is_zero, err, ok, Branch.BOOL)) + self.activate_block(err) + self.add( + RaiseStandardError( + RaiseStandardError.ZERO_DIVISION_ERROR, "integer division or modulo by zero", line + ) + ) + self.add(Unreachable()) + self.activate_block(ok) + def inline_fixed_width_divide(self, type: RType, lhs: Value, rhs: Value, line: int) -> Value: # Perform floor division (native division truncates) res = Register(type) @@ -2332,6 +2390,9 @@ def _create_dict(self, keys: list[Value], values: list[Value], line: int) -> Val else: return self.call_c(dict_new_op, [], line) + def error(self, msg: str, line: int) -> None: + self.errors.error(msg, self.module_path, line) + def num_positional_args(arg_values: list[Value], arg_kinds: list[ArgKind] | None) -> int: if arg_kinds is None: diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index a4712f4af524..5b77b4b1537b 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -43,6 +43,7 @@ set_rprimitive, str_rprimitive, tuple_rprimitive, + uint8_rprimitive, ) @@ -105,6 +106,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return int32_rprimitive elif typ.type.fullname == "mypy_extensions.i16": return int16_rprimitive + elif typ.type.fullname == "mypy_extensions.u8": + return uint8_rprimitive else: return object_rprimitive elif isinstance(typ, TupleType): diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 2f22b4bfc9d2..7c5958457886 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -44,6 +44,7 @@ ) from mypyc.ir.rtypes import ( RInstance, + RPrimitive, RTuple, RType, bool_rprimitive, @@ -62,9 +63,11 @@ is_int64_rprimitive, is_int_rprimitive, is_list_rprimitive, + is_uint8_rprimitive, list_rprimitive, set_rprimitive, str_rprimitive, + uint8_rprimitive, ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import ( @@ -166,15 +169,19 @@ def translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va @specialize_function("mypy_extensions.i64") @specialize_function("mypy_extensions.i32") @specialize_function("mypy_extensions.i16") +@specialize_function("mypy_extensions.u8") def translate_builtins_with_unary_dunder( builder: IRBuilder, expr: CallExpr, callee: RefExpr ) -> Value | None: - """Specialize calls on native classes that implement the associated dunder.""" + """Specialize calls on native classes that implement the associated dunder. + + E.g. i64(x) gets specialized to x.__int__() if x is a native instance. + """ if len(expr.args) == 1 and expr.arg_kinds == [ARG_POS] and isinstance(callee, NameExpr): arg = expr.args[0] arg_typ = builder.node_type(arg) shortname = callee.fullname.split(".")[1] - if shortname in ("i64", "i32", "i16"): + if shortname in ("i64", "i32", "i16", "u8"): method = "__int__" else: method = f"__{shortname}__" @@ -686,6 +693,9 @@ def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int32_rprimitive(arg_type) or is_int16_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Extend(val, int64_rprimitive, signed=True, line=expr.line)) + elif is_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int64_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) return builder.coerce(val, int64_rprimitive, expr.line) @@ -706,8 +716,12 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int16_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Extend(val, int32_rprimitive, signed=True, line=expr.line)) + elif is_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int32_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) + val = truncate_literal(val, int32_rprimitive) return builder.coerce(val, int32_rprimitive, expr.line) return None @@ -723,12 +737,54 @@ def translate_i16(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value elif is_int32_rprimitive(arg_type) or is_int64_rprimitive(arg_type): val = builder.accept(arg) return builder.add(Truncate(val, int16_rprimitive, line=expr.line)) + elif is_uint8_rprimitive(arg_type): + val = builder.accept(arg) + return builder.add(Extend(val, int16_rprimitive, signed=False, line=expr.line)) elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): val = builder.accept(arg) + val = truncate_literal(val, int16_rprimitive) return builder.coerce(val, int16_rprimitive, expr.line) return None +@specialize_function("mypy_extensions.u8") +def translate_u8(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: + return None + arg = expr.args[0] + arg_type = builder.node_type(arg) + if is_uint8_rprimitive(arg_type): + return builder.accept(arg) + elif ( + is_int16_rprimitive(arg_type) + or is_int32_rprimitive(arg_type) + or is_int64_rprimitive(arg_type) + ): + val = builder.accept(arg) + return builder.add(Truncate(val, uint8_rprimitive, line=expr.line)) + elif is_int_rprimitive(arg_type) or is_bool_rprimitive(arg_type): + val = builder.accept(arg) + val = truncate_literal(val, uint8_rprimitive) + return builder.coerce(val, uint8_rprimitive, expr.line) + return None + + +def truncate_literal(value: Value, rtype: RPrimitive) -> Value: + """If value is an integer literal value, truncate it to given native int rtype. + + For example, truncate 256 into 0 if rtype is u8. + """ + if not isinstance(value, Integer): + return value # Not a literal, nothing to do + x = value.numeric_value() + max_unsigned = (1 << (rtype.size * 8)) - 1 + x = x & max_unsigned + if rtype.is_signed and x >= (max_unsigned + 1) // 2: + # Adjust to make it a negative value + x -= max_unsigned + 1 + return Integer(x, rtype) + + @specialize_function("builtins.int") def translate_int(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 689526e0e826..64b716945b94 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -159,6 +159,8 @@ int16_t CPyLong_AsInt16(PyObject *o); int16_t CPyInt16_Divide(int16_t x, int16_t y); int16_t CPyInt16_Remainder(int16_t x, int16_t y); void CPyInt16_Overflow(void); +uint8_t CPyLong_AsUInt8(PyObject *o); +void CPyUInt8_Overflow(void); double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y); static inline int CPyTagged_CheckLong(CPyTagged x) { diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 528401692a3a..b57d88c6ac93 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -734,6 +734,55 @@ void CPyInt16_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); } + +uint8_t CPyLong_AsUInt8(PyObject *o) { + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + digit x = CPY_LONG_DIGIT(lobj, 0); + if (x < 256) + return x; + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + digit x = lobj->ob_digit[0]; + if (x < 256) + return x; + } else if (likely(size == 0)) { + return 0; + } + #endif + } + // Slow path + int overflow; + long result = PyLong_AsLongAndOverflow(o, &overflow); + if (result < 0 || result >= 256) { + overflow = 1; + result = -1; + } + if (result == -1) { + if (PyErr_Occurred()) { + return CPY_LL_UINT_ERROR; + } else if (overflow) { + PyErr_SetString(PyExc_OverflowError, "int too large or small to convert to u8"); + return CPY_LL_UINT_ERROR; + } + } + return result; +} + +void CPyUInt8_Overflow() { + PyErr_SetString(PyExc_OverflowError, "int too large or small to convert to u8"); +} + double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { if (unlikely(y == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "division by zero"); diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index e7e9fd768715..3c888a581a33 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -53,9 +53,12 @@ typedef PyObject CPyModule; // Tag bit used for long integers #define CPY_INT_TAG 1 -// Error value for fixed-width (low-level) integers +// Error value for signed fixed-width (low-level) integers #define CPY_LL_INT_ERROR -113 +// Error value for unsigned fixed-width (low-level) integers +#define CPY_LL_UINT_ERROR 239 + // Error value for floats #define CPY_FLOAT_ERROR -113.0 diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index ef79bbc51ce6..95f9cc5ff43f 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -43,6 +43,7 @@ "mypy_extensions.i64", "mypy_extensions.i32", "mypy_extensions.i16", + "mypy_extensions.u8", ): # These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? @@ -294,3 +295,10 @@ class IntComparisonOpDescription(NamedTuple): c_function_name="CPyInt16_Overflow", error_kind=ERR_ALWAYS, ) + +uint8_overflow = custom_op( + arg_types=[], + return_type=void_rtype, + c_function_name="CPyUInt8_Overflow", + error_kind=ERR_ALWAYS, +) diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 16bf8ba1eb89..ed43b86ebdb4 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -34,7 +34,7 @@ def f(x, y, z): x :: list y, z :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: object r4 :: bit @@ -528,10 +528,10 @@ def f(): L0: return 0 def g(): - r0 :: int64 + r0 :: i64 r1 :: bit r2 :: object - r3 :: int64 + r3 :: i64 L0: r0 = f() r1 = r0 == -113 @@ -542,7 +542,7 @@ L2: r2 = PyErr_Occurred() if not is_error(r2) goto L3 (error at g:7) else goto L1 L3: - r3 = :: int64 + r3 = :: i64 return r3 [case testExceptionWithNativeAttributeGetAndSet] @@ -612,16 +612,16 @@ def f(c: C) -> None: [out] def C.__init__(self, x, y): self :: __main__.C - x :: int32 - y :: int64 + x :: i32 + y :: i64 L0: self.x = x self.y = y return 1 def f(c): c :: __main__.C - r0 :: int32 - r1 :: int64 + r0 :: i32 + r1 :: i64 L0: r0 = c.x r1 = c.y @@ -636,15 +636,15 @@ def f(x: i64) -> i64: return y [out] def f(x): - x, r0, y :: int64 - __locals_bitmap0 :: uint32 + x, r0, y :: i64 + __locals_bitmap0 :: u32 r1 :: bit - r2, r3 :: uint32 + r2, r3 :: u32 r4 :: bit r5 :: bool - r6 :: int64 + r6 :: i64 L0: - r0 = :: int64 + r0 = :: i64 y = r0 __locals_bitmap0 = 0 r1 = x != 0 @@ -665,7 +665,7 @@ L4: L5: return y L6: - r6 = :: int64 + r6 = :: i64 return r6 [case testExceptionWithFloatAttribute] diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 8274e3d5c619..98f3dae9ee88 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -51,7 +51,7 @@ def f(a, n, c): r5 :: int r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit L0: r0 = box(int, n) @@ -99,10 +99,10 @@ def f2(a, n, l): n :: int l :: list r0, r1, r2, r3, r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9, r10 :: bit r11 :: list r12 :: object diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 556e0a4bbc50..33fc8cfaa83b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -713,7 +713,7 @@ def __top_level__(): r18 :: str r19 :: dict r20 :: str - r21 :: int32 + r21 :: i32 r22 :: bit r23 :: object_ptr r24 :: object_ptr[1] @@ -1146,7 +1146,7 @@ def f(x: Any, y: Any, z: Any) -> None: [out] def f(x, y, z): x, y, z :: object - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = PyObject_SetItem(x, y, z) @@ -1425,13 +1425,13 @@ def lst(x: List[int]) -> int: [out] def obj(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool if r2 goto L1 else goto L2 :: bool L1: return 2 @@ -1533,7 +1533,7 @@ def opt_o(x): r0 :: object r1 :: bit r2 :: object - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool L0: @@ -1544,7 +1544,7 @@ L1: r2 = cast(object, x) r3 = PyObject_IsTrue(r2) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool if r5 goto L2 else goto L3 :: bool L2: return 2 @@ -1616,7 +1616,7 @@ def __top_level__(): r5 :: dict r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: dict r11 :: str @@ -1731,7 +1731,7 @@ def main() -> None: def foo(x): x :: union[int, str] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: __main__.B @@ -1740,7 +1740,7 @@ L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = B() @@ -1903,7 +1903,7 @@ def g(): r8 :: str r9 :: object r10 :: dict - r11 :: int32 + r11 :: i32 r12 :: bit r13 :: tuple r14 :: object @@ -1933,7 +1933,7 @@ def h(): r6 :: str r7 :: object r8 :: dict - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object r12 :: tuple @@ -2062,7 +2062,7 @@ def f(): r26, r27 :: bit r28 :: int r29 :: object - r30 :: int32 + r30 :: i32 r31 :: bit r32 :: short_int L0: @@ -2161,7 +2161,7 @@ def f(): r26, r27 :: bit r28 :: int r29, r30 :: object - r31 :: int32 + r31 :: i32 r32 :: bit r33 :: short_int L0: @@ -2430,7 +2430,7 @@ def __top_level__(): r22, r23 :: object r24 :: dict r25 :: str - r26 :: int32 + r26 :: i32 r27 :: bit r28 :: str r29 :: dict @@ -2439,14 +2439,14 @@ def __top_level__(): r34 :: tuple r35 :: dict r36 :: str - r37 :: int32 + r37 :: i32 r38 :: bit r39 :: dict r40 :: str r41, r42, r43 :: object r44 :: dict r45 :: str - r46 :: int32 + r46 :: i32 r47 :: bit r48 :: str r49 :: dict @@ -2457,14 +2457,14 @@ def __top_level__(): r54, r55 :: object r56 :: dict r57 :: str - r58 :: int32 + r58 :: i32 r59 :: bit r60 :: list r61, r62, r63 :: object r64, r65, r66, r67 :: ptr r68 :: dict r69 :: str - r70 :: int32 + r70 :: i32 r71 :: bit L0: r0 = builtins :: module @@ -2632,7 +2632,7 @@ def A.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.A rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -2644,7 +2644,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -2836,7 +2836,7 @@ def c(): r11 :: bool r12 :: dict r13 :: str - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: str r17 :: object @@ -2886,7 +2886,7 @@ def __top_level__(): r18, r19 :: object r20 :: dict r21 :: str - r22 :: int32 + r22 :: i32 r23 :: bit L0: r0 = builtins :: module @@ -3157,7 +3157,7 @@ def lol(x: Any): def lol(x): x :: object r0, r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object L0: @@ -3462,13 +3462,13 @@ def f(x: object) -> bool: [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool return r2 [case testLocalImports] @@ -3493,7 +3493,7 @@ def root(): r7 :: dict r8 :: str r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: dict r13, r14 :: object @@ -3504,7 +3504,7 @@ def root(): r19 :: dict r20 :: str r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit L0: r0 = __main__.globals :: static @@ -3550,7 +3550,7 @@ def submodule(): r7 :: dict r8 :: str r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: dict r13 :: str @@ -3589,14 +3589,14 @@ def f(x: object) -> bool: [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = load_address PyBool_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testRangeObject] diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index 9257d8d63f7e..731d393d69ab 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -29,18 +29,18 @@ L0: return r0 def bool_to_i64(b): b :: bool - r0 :: int64 + r0 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 return r0 def i64_to_bool(n): - n :: int64 + n :: i64 r0 :: bit L0: r0 = n != 0 return r0 def bit_to_int(n1, n2): - n1, n2 :: int64 + n1, n2 :: i64 r0 :: bit r1 :: bool r2 :: int @@ -50,12 +50,12 @@ L0: r2 = extend r1: builtins.bool to builtins.int return r2 def bit_to_i64(n1, n2): - n1, n2 :: int64 + n1, n2 :: i64 r0 :: bit - r1 :: int64 + r1 :: i64 L0: r0 = n1 == n2 - r1 = extend r0: bit to int64 + r1 = extend r0: bit to i64 return r1 [case testConversionToBool] @@ -100,13 +100,13 @@ L0: return r3 def always_truthy_instance_to_bool(o): o :: __main__.C - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: bool L0: r0 = PyObject_IsTrue(o) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool return r2 def instance_to_bool(o): o :: __main__.D @@ -236,20 +236,20 @@ L0: r2 = r1 == y return r2 def neq1(x, y): - x :: int64 + x :: i64 y :: bool - r0 :: int64 + r0 :: i64 r1 :: bit L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x != r0 return r1 def neq2(x, y): x :: bool - y, r0 :: int64 + y, r0 :: i64 r1 :: bit L0: - r0 = extend x: builtins.bool to int64 + r0 = extend x: builtins.bool to i64 r1 = r0 != y return r1 @@ -327,19 +327,19 @@ L3: return r8 def gt1(x, y): x :: bool - y, r0 :: int64 + y, r0 :: i64 r1 :: bit L0: - r0 = extend x: builtins.bool to int64 + r0 = extend x: builtins.bool to i64 r1 = r0 < y :: signed return r1 def gt2(x, y): - x :: int64 + x :: i64 y :: bool - r0 :: int64 + r0 :: i64 r1 :: bit L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x < r0 :: signed return r1 @@ -386,11 +386,11 @@ L0: r2 = CPyTagged_Invert(r1) return r2 def mixed_bitand(x, y): - x :: int64 + x :: i64 y :: bool - r0, r1 :: int64 + r0, r1 :: i64 L0: - r0 = extend y: builtins.bool to int64 + r0 = extend y: builtins.bool to i64 r1 = x & r0 return r1 diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index f13a1a956580..8e97a7f4a569 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -71,7 +71,7 @@ def neq(x: bytes, y: bytes) -> bool: [out] def eq(x, y): x, y :: bytes - r0 :: int32 + r0 :: i32 r1, r2 :: bit L0: r0 = CPyBytes_Compare(x, y) @@ -80,7 +80,7 @@ L0: return r2 def neq(x, y): x, y :: bytes - r0 :: int32 + r0 :: i32 r1, r2 :: bit L0: r0 = CPyBytes_Compare(x, y) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 0a7076e5f0ad..55e55dbf3286 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -213,7 +213,7 @@ def __top_level__(): r16, r17 :: object r18 :: dict r19 :: str - r20 :: int32 + r20 :: i32 r21 :: bit r22 :: object r23 :: str @@ -221,22 +221,22 @@ def __top_level__(): r26 :: bool r27 :: str r28 :: tuple - r29 :: int32 + r29 :: i32 r30 :: bit r31 :: dict r32 :: str - r33 :: int32 + r33 :: i32 r34 :: bit r35 :: object r36 :: str r37, r38 :: object r39 :: str r40 :: tuple - r41 :: int32 + r41 :: i32 r42 :: bit r43 :: dict r44 :: str - r45 :: int32 + r45 :: i32 r46 :: bit r47, r48 :: object r49 :: dict @@ -251,11 +251,11 @@ def __top_level__(): r60 :: bool r61, r62 :: str r63 :: tuple - r64 :: int32 + r64 :: i32 r65 :: bit r66 :: dict r67 :: str - r68 :: int32 + r68 :: i32 r69 :: bit L0: r0 = builtins :: module @@ -837,7 +837,7 @@ def Base.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Base rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -849,7 +849,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -957,7 +957,7 @@ def Derived.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Derived rhs, r0, r1 :: object r2 :: bit - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool r6 :: object @@ -969,7 +969,7 @@ L0: L1: r3 = PyObject_Not(r0) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool r6 = box(bool, r5) return r6 L2: @@ -1029,7 +1029,7 @@ def foo(x: WelpDict) -> None: [out] def foo(x): x :: dict - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = CPyDict_Update(x, x) diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 362031b84e76..1a84f3fe3098 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -21,7 +21,7 @@ def f(d: Dict[int, bool]) -> None: def f(d): d :: dict r0, r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = object 0 @@ -83,14 +83,14 @@ def f(d: Dict[int, int]) -> bool: def f(d): d :: dict r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = object 4 r1 = PyDict_Contains(d, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: return 1 @@ -110,14 +110,14 @@ def f(d: Dict[int, int]) -> bool: def f(d): d :: dict r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = object 4 r1 = PyDict_Contains(d, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 if r4 goto L1 else goto L2 :: bool L1: @@ -134,7 +134,7 @@ def f(a: Dict[int, int], b: Dict[int, int]) -> None: [out] def f(a, b): a, b :: dict - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = CPyDict_Update(a, b) @@ -160,7 +160,7 @@ def increment(d): r7 :: object r8, k :: str r9, r10, r11 :: object - r12 :: int32 + r12 :: i32 r13, r14, r15 :: bit L0: r0 = 0 @@ -201,10 +201,10 @@ def f(x, y): r0 :: str r1 :: object r2 :: dict - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = 'z' @@ -252,7 +252,7 @@ def print_dict_methods(d1, d2): r7 :: object r8, v :: int r9 :: object - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: bool r13, r14 :: bit @@ -266,7 +266,7 @@ def print_dict_methods(d1, d2): r22, r23 :: object r24, r25, k :: int r26, r27, r28, r29, r30 :: object - r31 :: int32 + r31 :: i32 r32, r33, r34 :: bit L0: r0 = 0 @@ -286,7 +286,7 @@ L2: r9 = box(int, v) r10 = PyDict_Contains(d2, r9) r11 = r10 >= 0 :: signed - r12 = truncate r10: int32 to builtins.bool + r12 = truncate r10: i32 to builtins.bool if r12 goto L3 else goto L4 :: bool L3: return 1 @@ -345,7 +345,7 @@ def union_of_dicts(d): r12, r13 :: object r14 :: int r15 :: object - r16 :: int32 + r16 :: i32 r17, r18, r19 :: bit L0: r0 = PyDict_New() @@ -393,7 +393,7 @@ def typeddict(d): r9, k :: str v :: object r10 :: str - r11 :: int32 + r11 :: i32 r12 :: bit r13 :: object r14, r15, r16 :: bit diff --git a/mypyc/test-data/irbuild-dunders.test b/mypyc/test-data/irbuild-dunders.test index 3c140d927c0f..b50b6eeae162 100644 --- a/mypyc/test-data/irbuild-dunders.test +++ b/mypyc/test-data/irbuild-dunders.test @@ -103,14 +103,14 @@ L0: def h(d): d :: __main__.D r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = d.__contains__(14) r1 = PyObject_IsTrue(r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 return r4 diff --git a/mypyc/test-data/irbuild-float.test b/mypyc/test-data/irbuild-float.test index e3a60852574b..35e2eff62b86 100644 --- a/mypyc/test-data/irbuild-float.test +++ b/mypyc/test-data/irbuild-float.test @@ -241,7 +241,7 @@ def f(x: float = 1.5) -> float: [out] def f(x, __bitmap): x :: float - __bitmap, r0 :: uint32 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index fe4a94992717..35920889e596 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -132,7 +132,7 @@ def f(x: T, y: T) -> T: [out] def f(x, y): x, y, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: object @@ -140,7 +140,7 @@ L0: r0 = PyObject_RichCompare(y, x, 4) r1 = PyObject_IsTrue(r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = y diff --git a/mypyc/test-data/irbuild-glue-methods.test b/mypyc/test-data/irbuild-glue-methods.test index 6d749bf5dd84..3012c79586f2 100644 --- a/mypyc/test-data/irbuild-glue-methods.test +++ b/mypyc/test-data/irbuild-glue-methods.test @@ -343,8 +343,8 @@ L0: return 1 def D.f(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -371,8 +371,8 @@ class D(C): [out] def C.f(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -384,10 +384,10 @@ L2: return 1 def D.f(self, x, y, __bitmap): self :: __main__.D - x, y :: int64 - __bitmap, r0 :: uint32 + x, y :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: uint32 + r2 :: u32 r3 :: bit L0: r0 = __bitmap & 1 @@ -405,8 +405,8 @@ L4: return 1 def D.f__C_glue(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap :: uint32 + x :: i64 + __bitmap :: u32 r0 :: None L0: r0 = D.f(self, x, 0, __bitmap) @@ -432,6 +432,6 @@ class E: class EE(E): def f(self, x: int) -> None: pass # Line 18 [out] -main:7: error: An argument with type "int64" cannot be given a default value in a method override -main:13: error: Incompatible argument type "int64" (base class has type "int") -main:18: error: Incompatible argument type "int" (base class has type "int64") +main:7: error: An argument with type "i64" cannot be given a default value in a method override +main:13: error: Incompatible argument type "i64" (base class has type "int") +main:18: error: Incompatible argument type "int" (base class has type "i64") diff --git a/mypyc/test-data/irbuild-i16.test b/mypyc/test-data/irbuild-i16.test index 0f34ea53818d..a03c9df2c6ac 100644 --- a/mypyc/test-data/irbuild-i16.test +++ b/mypyc/test-data/irbuild-i16.test @@ -20,7 +20,7 @@ def compare(x: i16, y: i16) -> None: f = -5 < x [out] def add_op(x, y): - x, y, r0, r1, r2, r3, r4 :: int16 + x, y, r0, r1, r2, r3, r4 :: i16 L0: r0 = y + x x = r0 @@ -34,7 +34,7 @@ L0: x = r4 return x def compare(x, y): - x, y :: int16 + x, y :: i16 r0 :: bit a :: bool r1 :: bit @@ -72,7 +72,7 @@ def unary(x: i16) -> i16: return y [out] def unary(x): - x, r0, y, r1 :: int16 + x, r0, y, r1 :: i16 L0: r0 = 0 - x y = r0 @@ -90,15 +90,15 @@ def div_by_constant(x: i16) -> i16: return x [out] def div_by_constant(x): - x, r0, r1 :: int16 + x, r0, r1 :: i16 r2, r3, r4 :: bit - r5 :: int16 + r5 :: i16 r6 :: bit - r7, r8, r9 :: int16 + r7, r8, r9 :: i16 r10, r11, r12 :: bit - r13 :: int16 + r13 :: i16 r14 :: bit - r15 :: int16 + r15 :: i16 L0: r0 = x / 5 r1 = r0 @@ -141,11 +141,11 @@ def mod_by_constant(x: i16) -> i16: return x [out] def mod_by_constant(x): - x, r0, r1 :: int16 + x, r0, r1 :: i16 r2, r3, r4, r5 :: bit - r6, r7, r8 :: int16 + r6, r7, r8 :: i16 r9, r10, r11, r12 :: bit - r13 :: int16 + r13 :: i16 L0: r0 = x % 5 r1 = r0 @@ -185,13 +185,27 @@ def divmod(x: i16, y: i16) -> i16: return a % y [out] def divmod(x, y): - x, y, r0, a, r1 :: int16 + x, y, r0, a, r1 :: i16 L0: r0 = CPyInt16_Divide(x, y) a = r0 r1 = CPyInt16_Remainder(a, y) return r1 +[case testI16BinaryOperationWithOutOfRangeOperand] +from mypy_extensions import i16 + +def out_of_range(x: i16) -> None: + x + (-32769) + (-32770) + x + x * 32768 + x + 32767 # OK + (-32768) + x # OK +[out] +main:4: error: Value -32769 is out of range for "i16" +main:5: error: Value -32770 is out of range for "i16" +main:6: error: Value 32768 is out of range for "i16" + [case testI16BoxAndUnbox] from typing import Any from mypy_extensions import i16 @@ -202,12 +216,12 @@ def f(x: Any) -> Any: [out] def f(x): x :: object - r0, y :: int16 + r0, y :: i16 r1 :: object L0: - r0 = unbox(int16, x) + r0 = unbox(i16, x) y = r0 - r1 = box(int16, y) + r1 = box(i16, y) return r1 [case testI16MixedCompare1] @@ -217,11 +231,11 @@ def f(x: int, y: i16) -> bool: [out] def f(x, y): x :: int - y :: int16 + y :: i16 r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 r7 :: bit L0: r0 = x & 1 @@ -235,7 +249,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -251,12 +265,12 @@ def f(x: i16, y: int) -> bool: return x == y [out] def f(x, y): - x :: int16 + x :: i16 y :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 r7 :: bit L0: r0 = y & 1 @@ -270,7 +284,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = y >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -287,11 +301,11 @@ def i16_to_int(a: i16) -> int: return a [out] def i16_to_int(a): - a :: int16 + a :: i16 r0 :: native_int r1 :: int L0: - r0 = extend signed a: int16 to native_int + r0 = extend signed a: i16 to native_int r1 = r0 << 1 return r1 @@ -303,12 +317,12 @@ def f(a: i16) -> None: x += a [out] def f(a): - a :: int16 + a :: i16 x :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6, r7 :: int16 + r5, r6, r7 :: i16 r8 :: native_int r9 :: int L0: @@ -324,7 +338,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -332,7 +346,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend signed r7: int16 to native_int + r8 = extend signed r7: i16 to native_int r9 = r8 << 1 x = r9 return 1 @@ -346,7 +360,7 @@ def f() -> None: z: i16 = 5 + 7 [out] def f(): - x, y, z :: int16 + x, y, z :: i16 L0: x = 0 y = -127 @@ -366,20 +380,20 @@ def from_i64(x: i64) -> i16: return i16(x) [out] def from_i16(x): - x :: int16 + x :: i16 L0: return x def from_i32(x): - x :: int32 - r0 :: int16 + x :: i32 + r0 :: i16 L0: - r0 = truncate x: int32 to int16 + r0 = truncate x: i32 to i16 return r0 def from_i64(x): - x :: int64 - r0 :: int16 + x :: i64 + r0 :: i16 L0: - r0 = truncate x: int64 to int16 + r0 = truncate x: i64 to i16 return r0 [case testI16ExplicitConversionFromInt] @@ -393,7 +407,7 @@ def f(x): r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int16 + r5, r6 :: i16 L0: r0 = x & 1 r1 = r0 == 0 @@ -406,7 +420,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int16 + r5 = truncate r4: native_int to i16 r6 = r5 goto L5 L4: @@ -422,13 +436,21 @@ def f() -> None: x = i16(0) y = i16(11) z = i16(-3) + a = i16(32767) + b = i16(32768) # Truncate + c = i16(-32768) + d = i16(-32769) # Truncate [out] def f(): - x, y, z :: int16 + x, y, z, a, b, c, d :: i16 L0: x = 0 y = 11 z = -3 + a = 32767 + b = -32768 + c = -32768 + d = 32767 return 1 [case testI16ExplicitConversionFromVariousTypes] @@ -452,17 +474,17 @@ def float_to_i16(x: float) -> i16: [out] def bool_to_i16(b): b :: bool - r0 :: int16 + r0 :: i16 L0: - r0 = extend b: builtins.bool to int16 + r0 = extend b: builtins.bool to i16 return r0 def str_to_i16(s): s :: str r0 :: object - r1 :: int16 + r1 :: i16 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int16, r0) + r1 = unbox(i16, r0) return r1 def C.__int__(self): self :: __main__.C @@ -470,7 +492,7 @@ L0: return 5 def instance_to_i16(c): c :: __main__.C - r0 :: int16 + r0 :: i16 L0: r0 = c.__int__() return r0 @@ -480,7 +502,7 @@ def float_to_i16(x): r1 :: native_int r2, r3, r4 :: bit r5 :: native_int - r6, r7 :: int16 + r6, r7 :: i16 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -494,7 +516,7 @@ L2: if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 - r6 = truncate r5: native_int to int16 + r6 = truncate r5: native_int to i16 r7 = r6 goto L5 L4: diff --git a/mypyc/test-data/irbuild-i32.test b/mypyc/test-data/irbuild-i32.test index 4b69fa3a6bbb..7dcb722ec906 100644 --- a/mypyc/test-data/irbuild-i32.test +++ b/mypyc/test-data/irbuild-i32.test @@ -20,7 +20,7 @@ def compare(x: i32, y: i32) -> None: f = -5 < x [out] def add_op(x, y): - x, y, r0, r1, r2, r3, r4 :: int32 + x, y, r0, r1, r2, r3, r4 :: i32 L0: r0 = y + x x = r0 @@ -34,7 +34,7 @@ L0: x = r4 return x def compare(x, y): - x, y :: int32 + x, y :: i32 r0 :: bit a :: bool r1 :: bit @@ -72,7 +72,7 @@ def unary(x: i32) -> i32: return y [out] def unary(x): - x, r0, y, r1 :: int32 + x, r0, y, r1 :: i32 L0: r0 = 0 - x y = r0 @@ -90,15 +90,15 @@ def div_by_constant(x: i32) -> i32: return x [out] def div_by_constant(x): - x, r0, r1 :: int32 + x, r0, r1 :: i32 r2, r3, r4 :: bit - r5 :: int32 + r5 :: i32 r6 :: bit - r7, r8, r9 :: int32 + r7, r8, r9 :: i32 r10, r11, r12 :: bit - r13 :: int32 + r13 :: i32 r14 :: bit - r15 :: int32 + r15 :: i32 L0: r0 = x / 5 r1 = r0 @@ -141,11 +141,11 @@ def mod_by_constant(x: i32) -> i32: return x [out] def mod_by_constant(x): - x, r0, r1 :: int32 + x, r0, r1 :: i32 r2, r3, r4, r5 :: bit - r6, r7, r8 :: int32 + r6, r7, r8 :: i32 r9, r10, r11, r12 :: bit - r13 :: int32 + r13 :: i32 L0: r0 = x % 5 r1 = r0 @@ -185,7 +185,7 @@ def divmod(x: i32, y: i32) -> i32: return a % y [out] def divmod(x, y): - x, y, r0, a, r1 :: int32 + x, y, r0, a, r1 :: i32 L0: r0 = CPyInt32_Divide(x, y) a = r0 @@ -202,12 +202,12 @@ def f(x: Any) -> Any: [out] def f(x): x :: object - r0, y :: int32 + r0, y :: i32 r1 :: object L0: - r0 = unbox(int32, x) + r0 = unbox(i32, x) y = r0 - r1 = box(int32, y) + r1 = box(i32, y) return r1 [case testI32MixedCompare1_64bit] @@ -217,11 +217,11 @@ def f(x: int, y: i32) -> bool: [out] def f(x, y): x :: int - y :: int32 + y :: i32 r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 r7 :: bit L0: r0 = x & 1 @@ -235,7 +235,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -251,12 +251,12 @@ def f(x: i32, y: int) -> bool: return x == y [out] def f(x, y): - x :: int32 + x :: i32 y :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 r7 :: bit L0: r0 = y & 1 @@ -270,7 +270,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = y >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -287,13 +287,13 @@ def f(x: int, y: i32) -> bool: [out] def f(x, y): x :: int - y :: int32 + y :: i32 r0 :: native_int r1 :: bit - r2, r3 :: int32 + r2, r3 :: i32 r4 :: ptr r5 :: c_ptr - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = x & 1 @@ -320,11 +320,11 @@ def i32_to_int(a: i32) -> int: return a [out] def i32_to_int(a): - a :: int32 + a :: i32 r0 :: native_int r1 :: int L0: - r0 = extend signed a: int32 to native_int + r0 = extend signed a: i32 to native_int r1 = r0 << 1 return r1 @@ -335,7 +335,7 @@ def i32_to_int(a: i32) -> int: return a [out] def i32_to_int(a): - a :: int32 + a :: i32 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -362,12 +362,12 @@ def f(a: i32) -> None: x += a [out] def f(a): - a :: int32 + a :: i32 x :: int r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6, r7 :: int32 + r5, r6, r7 :: i32 r8 :: native_int r9 :: int L0: @@ -383,7 +383,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -391,7 +391,7 @@ L4: unreachable L5: r7 = r6 + a - r8 = extend signed r7: int32 to native_int + r8 = extend signed r7: i32 to native_int r9 = r8 << 1 x = r9 return 1 @@ -405,7 +405,7 @@ def f() -> None: z: i32 = 5 + 7 [out] def f(): - x, y, z :: int32 + x, y, z :: i32 L0: x = 0 y = -127 @@ -425,20 +425,20 @@ def from_i64(x: i64) -> i32: return i32(x) [out] def from_i16(x): - x :: int16 - r0 :: int32 + x :: i16 + r0 :: i32 L0: - r0 = extend signed x: int16 to int32 + r0 = extend signed x: i16 to i32 return r0 def from_i32(x): - x :: int32 + x :: i32 L0: return x def from_i64(x): - x :: int64 - r0 :: int32 + x :: i64 + r0 :: i32 L0: - r0 = truncate x: int64 to int32 + r0 = truncate x: i64 to i32 return r0 [case testI32ExplicitConversionFromInt_64bit] @@ -452,7 +452,7 @@ def f(x): r0 :: native_int r1, r2, r3 :: bit r4 :: native_int - r5, r6 :: int32 + r5, r6 :: i32 L0: r0 = x & 1 r1 = r0 == 0 @@ -465,7 +465,7 @@ L2: if r3 goto L3 else goto L4 :: bool L3: r4 = x >> 1 - r5 = truncate r4: native_int to int32 + r5 = truncate r4: native_int to i32 r6 = r5 goto L5 L4: @@ -474,20 +474,22 @@ L4: L5: return r6 -[case testI32ExplicitConversionFromLiteral] +[case testI32ExplicitConversionFromLiteral_64bit] from mypy_extensions import i32 def f() -> None: x = i32(0) y = i32(11) z = i32(-3) + a = i32(2**31) [out] def f(): - x, y, z :: int32 + x, y, z, a :: i32 L0: x = 0 y = 11 z = -3 + a = -2147483648 return 1 [case testI32ExplicitConversionFromVariousTypes_64bit] @@ -511,17 +513,17 @@ def float_to_i32(x: float) -> i32: [out] def bool_to_i32(b): b :: bool - r0 :: int32 + r0 :: i32 L0: - r0 = extend b: builtins.bool to int32 + r0 = extend b: builtins.bool to i32 return r0 def str_to_i32(s): s :: str r0 :: object - r1 :: int32 + r1 :: i32 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int32, r0) + r1 = unbox(i32, r0) return r1 def C.__int__(self): self :: __main__.C @@ -529,7 +531,7 @@ L0: return 5 def instance_to_i32(c): c :: __main__.C - r0 :: int32 + r0 :: i32 L0: r0 = c.__int__() return r0 @@ -539,7 +541,7 @@ def float_to_i32(x): r1 :: native_int r2, r3, r4 :: bit r5 :: native_int - r6, r7 :: int32 + r6, r7 :: i32 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -553,7 +555,7 @@ L2: if r4 goto L3 else goto L4 :: bool L3: r5 = r0 >> 1 - r6 = truncate r5: native_int to int32 + r6 = truncate r5: native_int to i32 r7 = r6 goto L5 L4: @@ -573,10 +575,10 @@ def float_to_i32(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int32 + r3, r4 :: i32 r5 :: ptr r6 :: c_ptr - r7 :: int32 + r7 :: i32 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index 38561178ddd0..07f549c9fcc2 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -7,7 +7,7 @@ def f() -> i64: return y [out] def f(): - x, y :: int64 + x, y :: i64 L0: x = 5 y = x @@ -40,7 +40,7 @@ def all_comparisons(x: i64) -> int: return y [out] def min(x, y): - x, y :: int64 + x, y :: i64 r0 :: bit L0: r0 = x < y :: signed @@ -52,7 +52,7 @@ L2: L3: unreachable def all_comparisons(x): - x :: int64 + x :: i64 r0 :: bit y :: int r1, r2, r3, r4, r5 :: bit @@ -110,7 +110,7 @@ def f(x: i64, y: i64) -> i64: return y - z [out] def f(x, y): - x, y, r0, z, r1 :: int64 + x, y, r0, z, r1 :: i64 L0: r0 = x + y z = r0 @@ -125,7 +125,7 @@ def f() -> i64: return -i [out] def f(): - i, r0 :: int64 + i, r0 :: i64 L0: i = -3 r0 = 0 - i @@ -140,7 +140,7 @@ def unary(x: i64) -> i64: return x [out] def unary(x): - x, r0, y :: int64 + x, r0, y :: i64 L0: r0 = x ^ -1 y = r0 @@ -157,12 +157,12 @@ def f(a: Any) -> None: [out] def f(a): a :: object - r0, b :: int64 + r0, b :: i64 r1 :: object L0: - r0 = unbox(int64, a) + r0 = unbox(i64, a) b = r0 - r1 = box(int64, b) + r1 = box(i64, b) a = r1 return 1 @@ -177,20 +177,20 @@ def set(a: List[i64], i: i64, x: i64) -> None: [out] def get(a, i): a :: list - i :: int64 + i :: i64 r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyList_GetItemInt64(a, i) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def set(a, i, x): a :: list - i, x :: int64 + i, x :: i64 r0 :: object r1 :: bit L0: - r0 = box(int64, x) + r0 = box(i64, x) r1 = CPyList_SetItemInt64(a, i, r0) return 1 @@ -203,7 +203,7 @@ def f() -> i64: return 3 - b [out] def f(): - a, r0, b, r1 :: int64 + a, r0, b, r1 :: i64 L0: a = 1 r0 = a + 2 @@ -222,7 +222,7 @@ def f(a: i64) -> i64: return 3 [out] def f(a): - a :: int64 + a :: i64 r0, r1 :: bit L0: r0 = a < 3 :: signed @@ -257,7 +257,7 @@ def others(a: i64, b: i64) -> i64: return a [out] def add(a): - a, b, r0, r1 :: int64 + a, b, r0, r1 :: i64 L0: b = a r0 = b + 1 @@ -266,7 +266,7 @@ L0: a = r1 return a def others(a, b): - a, b, r0, r1, r2, r3, r4, r5, r6 :: int64 + a, b, r0, r1, r2, r3, r4, r5, r6 :: i64 L0: r0 = a - b a = r0 @@ -307,7 +307,7 @@ def unary(a: i64) -> i64: return ~a [out] def forward(a, b): - a, b, r0, r1, r2, r3, r4 :: int64 + a, b, r0, r1, r2, r3, r4 :: i64 L0: r0 = a & 1 b = r0 @@ -321,7 +321,7 @@ L0: b = r4 return b def reverse(a, b): - a, b, r0, r1, r2, r3, r4 :: int64 + a, b, r0, r1, r2, r3, r4 :: i64 L0: r0 = 1 & a b = r0 @@ -335,7 +335,7 @@ L0: b = r4 return b def unary(a): - a, r0 :: int64 + a, r0 :: i64 L0: r0 = a ^ -1 return r0 @@ -355,11 +355,11 @@ def divide_by_zero(x: i64) -> i64: return x // 0 [out] def constant_divisor(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4 :: bit - r5 :: int64 + r5 :: i64 r6 :: bit - r7 :: int64 + r7 :: i64 L0: r0 = x / 7 r1 = r0 @@ -377,22 +377,22 @@ L2: L3: return r1 def variable_divisor(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Divide(x, y) return r0 def constant_lhs(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(27, x) return r0 def divide_by_neg_one(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(x, -1) return r0 def divide_by_zero(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Divide(x, 0) return r0 @@ -410,9 +410,9 @@ def mod_by_zero(x: i64) -> i64: return x % 0 [out] def constant_divisor(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4, r5 :: bit - r6 :: int64 + r6 :: i64 L0: r0 = x % 7 r1 = r0 @@ -429,17 +429,17 @@ L2: L3: return r1 def variable_divisor(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, y) return r0 def constant_lhs(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Remainder(27, x) return r0 def mod_by_zero(x): - x, r0 :: int64 + x, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, 0) return r0 @@ -455,11 +455,11 @@ def by_variable(x: i64, y: i64) -> i64: return x [out] def by_constant(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4 :: bit - r5 :: int64 + r5 :: i64 r6 :: bit - r7 :: int64 + r7 :: i64 L0: r0 = x / 7 r1 = r0 @@ -478,7 +478,7 @@ L3: x = r1 return x def by_variable(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Divide(x, y) x = r0 @@ -495,9 +495,9 @@ def by_variable(x: i64, y: i64) -> i64: return x [out] def by_constant(x): - x, r0, r1 :: int64 + x, r0, r1 :: i64 r2, r3, r4, r5 :: bit - r6 :: int64 + r6 :: i64 L0: r0 = x % 7 r1 = r0 @@ -515,7 +515,7 @@ L3: x = r1 return x def by_variable(x, y): - x, y, r0 :: int64 + x, y, r0 :: i64 L0: r0 = CPyInt64_Remainder(x, y) x = r0 @@ -532,14 +532,14 @@ def f(x: i64) -> None: g(n) [out] def g(a): - a :: int64 + a :: i64 L0: return 1 def f(x): - x, r0, n :: int64 + x, r0, n :: i64 r1 :: bit r2 :: None - r3 :: int64 + r3 :: i64 L0: r0 = 0 n = r0 @@ -566,10 +566,10 @@ def int_to_i64(a): a :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = a & 1 r1 = r0 == 0 @@ -594,7 +594,7 @@ def i64_to_int(a: i64) -> int: return a [out] def i64_to_int(a): - a :: int64 + a :: i64 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -620,7 +620,7 @@ def i64_to_int(a: i64) -> int: return a [out] def i64_to_int(a): - a :: int64 + a :: i64 r0, r1 :: bit r2, r3 :: int r4 :: native_int @@ -636,7 +636,7 @@ L2: r3 = r2 goto L4 L3: - r4 = truncate a: int64 to native_int + r4 = truncate a: i64 to native_int r5 = r4 << 1 r3 = r5 L4: @@ -658,23 +658,23 @@ def h() -> i64: return x + y + t[0] [out] def f(x, y): - x, y :: int64 - r0 :: tuple[int64, int64] + x, y :: i64 + r0 :: tuple[i64, i64] L0: r0 = (x, y) return r0 def g(): r0 :: tuple[int, int] - r1 :: tuple[int64, int64] + r1 :: tuple[i64, i64] L0: r0 = (2, 4) r1 = (1, 2) return r1 def h(): - r0 :: tuple[int64, int64] - r1, x, r2, y :: int64 - r3, t :: tuple[int64, int64] - r4, r5, r6 :: int64 + r0 :: tuple[i64, i64] + r1, x, r2, y :: i64 + r3, t :: tuple[i64, i64] + r4, r5, r6 :: i64 L0: r0 = g() r1 = r0[0] @@ -694,14 +694,14 @@ def f(x: i64, y: int) -> i64: return x + y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: r0 = y & 1 r1 = r0 == 0 @@ -727,13 +727,13 @@ def f(x: int, y: i64) -> i64: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -760,14 +760,14 @@ def f(y: i64) -> int: return x [out] def f(y): - y :: int64 + y :: i64 x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 r8, r9 :: bit r10, r11, r12 :: int L0: @@ -812,13 +812,13 @@ def f(y: int) -> i64: [out] def f(y): y :: int - x :: int64 + x :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6, r7 :: int64 + r6, r7 :: i64 L0: x = 0 r0 = y & 1 @@ -846,13 +846,13 @@ def f(x: int, y: i64) -> bool: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 r7 :: bit L0: r0 = x & 1 @@ -878,14 +878,14 @@ def f(x: i64, y: int) -> bool: return x == y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 r7 :: bit L0: r0 = y & 1 @@ -912,20 +912,20 @@ def f(x: int, y: i64) -> bool: [out] def f(x, y): x :: int - y :: int64 + y :: i64 r0 :: native_int r1 :: bit - r2, r3, r4 :: int64 + r2, r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 r8 :: bit L0: r0 = x & 1 r1 = r0 == 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend signed x: builtins.int to int64 + r2 = extend signed x: builtins.int to i64 r3 = r2 >> 1 r4 = r3 goto L3 @@ -949,7 +949,7 @@ def f(x: i64) -> i64: return 3 [out] def f(x): - x :: int64 + x :: i64 r0, r1 :: bit L0: r0 = x != 0 @@ -975,14 +975,14 @@ def g(x: i64, y: int) -> int: return y [out] def f(x, y): - x :: int64 + x :: i64 y :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = y & 1 r1 = r0 == 0 @@ -1001,7 +1001,7 @@ L3: x = r3 return x def g(x, y): - x :: int64 + x :: i64 y :: int r0, r1 :: bit r2, r3, r4 :: int @@ -1043,7 +1043,7 @@ class C: [out] def add_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = c.x r1 = c.y @@ -1051,7 +1051,7 @@ L0: return r2 def inplace_add_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 r3 :: bool L0: r0 = c.x @@ -1062,9 +1062,9 @@ L0: def add_borrow(d): d :: __main__.D r0 :: __main__.C - r1 :: int64 + r1 :: i64 r2 :: __main__.C - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = borrow d.c r1 = r0.x @@ -1095,7 +1095,7 @@ class C: [out] def bitwise_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = c.x r1 = c.y @@ -1103,7 +1103,7 @@ L0: return r2 def inplace_bitwide_simple(c): c :: __main__.C - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 r3 :: bool L0: r0 = c.x @@ -1114,9 +1114,9 @@ L0: def bitwise_borrow(d): d :: __main__.D r0 :: __main__.C - r1 :: int64 + r1 :: i64 r2 :: __main__.C - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = borrow d.c r1 = r0.x @@ -1137,7 +1137,7 @@ class C: s: str [out] def f(n): - n :: int64 + n :: i64 r0 :: __main__.C r1 :: list r2, r3 :: ptr @@ -1170,13 +1170,13 @@ def f(a: List[i64], n: i64) -> bool: [out] def f(a, n): a :: list - n :: int64 + n :: i64 r0 :: object - r1 :: int64 + r1 :: i64 r2 :: bit L0: r0 = CPyList_GetItemInt64Borrow(a, n) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) r2 = r1 == 0 keep_alive a, n if r2 goto L1 else goto L2 :: bool @@ -1201,11 +1201,11 @@ def g(a: List[i64], y: i64) -> bool: [out] def f(a, y): a :: list - y :: int64 + y :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1221,11 +1221,11 @@ L2: return 0 def g(a, y): a :: list - y :: int64 + y :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1248,7 +1248,7 @@ def f(n: i64) -> List[i64]: return [n] * n [out] def f(n): - n :: int64 + n :: i64 r0 :: list r1 :: object r2, r3 :: ptr @@ -1257,7 +1257,7 @@ def f(n): r9 :: list L0: r0 = PyList_New(1) - r1 = box(int64, n) + r1 = box(i64, n) r2 = get_element_ptr r0 ob_item :: PyListObject r3 = load_mem r2 :: ptr* set_mem r3, r1 :: builtins.object* @@ -1297,11 +1297,11 @@ def lt_i64(a: List[i64], n: i64) -> bool: [out] def add_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = get_element_ptr a ob_size :: PyVarObject r1 = load_mem r0 :: native_int* @@ -1312,11 +1312,11 @@ L0: return r4 def add_i64_2(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3, r4 :: int64 + r3, r4 :: i64 L0: r0 = get_element_ptr a ob_size :: PyVarObject r1 = load_mem r0 :: native_int* @@ -1327,11 +1327,11 @@ L0: return r4 def eq_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1347,11 +1347,11 @@ L2: return 0 def lt_i64(a, n): a :: list - n :: int64 + n :: i64 r0 :: ptr r1 :: native_int r2 :: short_int - r3 :: int64 + r3 :: i64 r4 :: bit L0: r0 = get_element_ptr a ob_size :: PyVarObject @@ -1376,10 +1376,10 @@ def f(x: Optional[i64]) -> i64: return x [out] def f(x): - x :: union[int64, None] + x :: union[i64, None] r0 :: object r1 :: bit - r2 :: int64 + r2 :: i64 L0: r0 = load_address _Py_NoneStruct r1 = x == r0 @@ -1387,7 +1387,7 @@ L0: L1: return 1 L2: - r2 = unbox(int64, x) + r2 = unbox(i64, x) return r2 [case testI64DefaultValueSingle] @@ -1400,10 +1400,10 @@ def g() -> i64: return f(7) + f(8, 9) [out] def f(x, y, __bitmap): - x, y :: int64 - __bitmap, r0 :: uint32 + x, y :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: int64 + r2 :: i64 L0: r0 = __bitmap & 1 r1 = r0 == 0 @@ -1414,7 +1414,7 @@ L2: r2 = x + y return r2 def g(): - r0, r1, r2 :: int64 + r0, r1, r2 :: i64 L0: r0 = f(7, 0, 0) r1 = f(8, 9, 1) @@ -1431,12 +1431,12 @@ def g() -> i64: return f(7) + f(8, 9) + f(1, 2, 3) + f(4, 5, 6, 7) [out] def f(a, b, c, d, __bitmap): - a, b :: int64 + a, b :: i64 c :: int - d :: int64 - __bitmap, r0 :: uint32 + d :: i64 + __bitmap, r0 :: u32 r1 :: bit - r2 :: uint32 + r2 :: u32 r3 :: bit L0: r0 = __bitmap & 1 @@ -1458,9 +1458,9 @@ L6: return 0 def g(): r0 :: int - r1 :: int64 + r1 :: i64 r2 :: int - r3, r4, r5, r6, r7, r8 :: int64 + r3, r4, r5, r6, r7, r8 :: i64 L0: r0 = :: int r1 = f(7, 0, r0, 0, 0) @@ -1486,8 +1486,8 @@ def f(c: C) -> None: [out] def C.m(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1518,19 +1518,19 @@ def from_i64(x: i64) -> i64: return i64(x) [out] def from_i16(x): - x :: int16 - r0 :: int64 + x :: i16 + r0 :: i64 L0: - r0 = extend signed x: int16 to int64 + r0 = extend signed x: i16 to i64 return r0 def from_i32(x): - x :: int32 - r0 :: int64 + x :: i32 + r0 :: i64 L0: - r0 = extend signed x: int32 to int64 + r0 = extend signed x: i32 to i64 return r0 def from_i64(x): - x :: int64 + x :: i64 L0: return x @@ -1544,10 +1544,10 @@ def f(x): x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -1572,7 +1572,7 @@ def f(x: i64) -> int: return int(x) [out] def f(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3, r4 :: int L0: @@ -1600,7 +1600,7 @@ def f() -> None: z = i64(-3) [out] def f(): - x, y, z :: int64 + x, y, z :: i64 L0: x = 0 y = 11 @@ -1615,9 +1615,9 @@ def f() -> None: y = x [out] def f(): - r0, x :: int64 + r0, x :: i64 r1 :: bit - y, r2 :: int64 + y, r2 :: i64 L0: r0 = 0 x = r0 @@ -1642,9 +1642,9 @@ def f() -> None: y = x [out] def f(): - r0, x :: int64 + r0, x :: i64 r1 :: bit - y, r2 :: int64 + y, r2 :: i64 L0: r0 = 0 x = r0 @@ -1671,8 +1671,8 @@ class D(C): [out] def C.f(self, x, __bitmap): self :: __main__.C - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1684,8 +1684,8 @@ L2: return 1 def D.f(self, x, __bitmap): self :: __main__.D - x :: int64 - __bitmap, r0 :: uint32 + x :: i64 + __bitmap, r0 :: u32 r1 :: bit L0: r0 = __bitmap & 1 @@ -1751,26 +1751,26 @@ def compare_bool_to_i64(n: i64, b: bool) -> bool: return True [out] def add_bool_to_int(n, b): - n :: int64 + n :: i64 b :: bool - r0, r1 :: int64 + r0, r1 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 r1 = n + r0 return r1 def compare_bool_to_i64(n, b): - n :: int64 + n :: i64 b :: bool - r0 :: int64 + r0 :: i64 r1 :: bit - r2 :: int64 + r2 :: i64 r3 :: bit L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 r1 = n == r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend b: builtins.bool to int64 + r2 = extend b: builtins.bool to i64 r3 = r2 != n return r3 L2: @@ -1788,18 +1788,18 @@ def cast_int(x: int) -> i64: [out] def cast_object(o): o :: object - r0 :: int64 + r0 :: i64 L0: - r0 = unbox(int64, o) + r0 = unbox(i64, o) return r0 def cast_int(x): x :: int r0 :: native_int r1 :: bit - r2, r3 :: int64 + r2, r3 :: i64 r4 :: ptr r5 :: c_ptr - r6 :: int64 + r6 :: i64 L0: r0 = x & 1 r1 = r0 == 0 @@ -1828,16 +1828,16 @@ def cast_int(x): x :: int r0 :: native_int r1 :: bit - r2, r3, r4 :: int64 + r2, r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = x & 1 r1 = r0 == 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = extend signed x: builtins.int to int64 + r2 = extend signed x: builtins.int to i64 r3 = r2 >> 1 r4 = r3 goto L3 @@ -1874,25 +1874,25 @@ def float_to_i64(x: float) -> i64: [out] def bool_to_i64(b): b :: bool - r0 :: int64 + r0 :: i64 L0: - r0 = extend b: builtins.bool to int64 + r0 = extend b: builtins.bool to i64 return r0 def str_to_i64(s): s :: str r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyLong_FromStr(s) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def str_to_i64_with_base(s): s :: str r0 :: object - r1 :: int64 + r1 :: i64 L0: r0 = CPyLong_FromStrWithBase(s, 4) - r1 = unbox(int64, r0) + r1 = unbox(i64, r0) return r1 def C.__int__(self): self :: __main__.C @@ -1900,7 +1900,7 @@ L0: return 5 def instance_to_i64(c): c :: __main__.C - r0 :: int64 + r0 :: i64 L0: r0 = c.__int__() return r0 @@ -1909,10 +1909,10 @@ def float_to_i64(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int64 + r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 @@ -1942,17 +1942,17 @@ def float_to_i64(x): r0 :: int r1 :: native_int r2 :: bit - r3, r4, r5 :: int64 + r3, r4, r5 :: i64 r6 :: ptr r7 :: c_ptr - r8 :: int64 + r8 :: i64 L0: r0 = CPyTagged_FromFloat(x) r1 = r0 & 1 r2 = r1 == 0 if r2 goto L1 else goto L2 :: bool L1: - r3 = extend signed r0: builtins.int to int64 + r3 = extend signed r0: builtins.int to i64 r4 = r3 >> 1 r5 = r4 goto L3 @@ -1972,7 +1972,7 @@ def i64_to_float(x: i64) -> float: return float(x) [out] def i64_to_float(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3, r4 :: int r5 :: float @@ -2000,7 +2000,7 @@ def i64_to_float(x: i64) -> float: return float(x) [out] def i64_to_float(x): - x :: int64 + x :: i64 r0, r1 :: bit r2, r3 :: int r4 :: native_int @@ -2017,7 +2017,7 @@ L2: r3 = r2 goto L4 L3: - r4 = truncate x: int64 to native_int + r4 = truncate x: i64 to native_int r5 = r4 << 1 r3 = r5 L4: @@ -2042,22 +2042,22 @@ def narrow2(x: Union[C, i64]) -> i64: return x.a [out] def narrow1(x): - x :: union[__main__.C, int64] + x :: union[__main__.C, i64] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool - r4 :: int64 + r4 :: i64 r5 :: __main__.C - r6 :: int64 + r6 :: i64 L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: - r4 = unbox(int64, x) + r4 = unbox(i64, x) return r4 L2: r5 = borrow cast(__main__.C, x) @@ -2065,22 +2065,22 @@ L2: keep_alive x return r6 def narrow2(x): - x :: union[__main__.C, int64] + x :: union[__main__.C, i64] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool - r4 :: int64 + r4 :: i64 r5 :: __main__.C - r6 :: int64 + r6 :: i64 L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: - r4 = unbox(int64, x) + r4 = unbox(i64, x) return r4 L2: r5 = borrow cast(__main__.C, x) @@ -2099,17 +2099,17 @@ def g(n: int) -> None: t: tuple[i64, i64] = (1, n) [out] def f(t): - t :: tuple[int, int64, int] + t :: tuple[int, i64, int] r0 :: int - r1 :: int64 + r1 :: i64 r2 :: int r3 :: native_int r4 :: bit - r5, r6 :: int64 + r5, r6 :: i64 r7 :: ptr r8 :: c_ptr - r9 :: int64 - r10, tt :: tuple[int, int64, int64] + r9 :: i64 + r10, tt :: tuple[int, i64, i64] L0: r0 = t[0] r1 = t[1] @@ -2137,11 +2137,11 @@ def g(n): r1 :: int r2 :: native_int r3 :: bit - r4, r5 :: int64 + r4, r5 :: i64 r6 :: ptr r7 :: c_ptr - r8 :: int64 - r9, t :: tuple[int64, int64] + r8 :: i64 + r9, t :: tuple[i64, i64] L0: r0 = (2, n) r1 = r0[1] diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test index 6bb92d0a947e..78da2e9c1e19 100644 --- a/mypyc/test-data/irbuild-isinstance.test +++ b/mypyc/test-data/irbuild-isinstance.test @@ -5,14 +5,14 @@ def is_int(value: object) -> bool: [out] def is_int(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testIsinstanceNotBool1] @@ -22,14 +22,14 @@ def is_not_bool(value: object) -> bool: [out] def is_not_bool(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool L0: r0 = load_address PyBool_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool r4 = r3 ^ 1 return r4 @@ -42,18 +42,18 @@ def is_not_bool_and_is_int(value: object) -> bool: [out] def is_not_bool_and_is_int(value): value, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3, r4 :: bool r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8, r9 :: bool L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(value, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L2 else goto L1 :: bool L1: r4 = r3 @@ -62,7 +62,7 @@ L2: r5 = load_address PyBool_Type r6 = PyObject_IsInstance(value, r5) r7 = r6 >= 0 :: signed - r8 = truncate r6: int32 to builtins.bool + r8 = truncate r6: i32 to builtins.bool r9 = r8 ^ 1 r4 = r9 L3: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index eaeff9432446..80c4fe5fcd5e 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -197,7 +197,7 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = box(int, x) @@ -255,7 +255,7 @@ def f(x, y): r1, r2 :: object r3, r4, r5 :: ptr r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit L0: r0 = PyList_New(2) @@ -283,14 +283,14 @@ def f(x, y): x :: list y :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = box(int, y) r1 = PySequence_Contains(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 [case testListInsert] @@ -302,7 +302,7 @@ def f(x, y): x :: list y :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = box(int, y) @@ -462,7 +462,7 @@ def nested_union(a: Union[List[str], List[Optional[str]]]) -> None: def narrow(a): a :: union[list, int] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: list @@ -474,7 +474,7 @@ L0: r0 = load_address PyList_Type r1 = PyObject_IsInstance(a, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = borrow cast(list, a) diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index 2afe3d862f51..a078ae0defdb 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -608,22 +608,22 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str r12, r13, r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: str r19, r20, r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit r24 :: bool r25 :: str @@ -637,7 +637,7 @@ L0: r0 = __main__.Position :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'x' @@ -646,7 +646,7 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L2 else goto L5 :: bool L2: r11 = 'y' @@ -655,7 +655,7 @@ L2: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L3 else goto L5 :: bool L3: r18 = 'z' @@ -664,7 +664,7 @@ L3: r21 = PyObject_RichCompare(r19, r20, 2) r22 = PyObject_IsTrue(r21) r23 = r22 >= 0 :: signed - r24 = truncate r22: int32 to builtins.bool + r24 = truncate r22: i32 to builtins.bool if r24 goto L4 else goto L5 :: bool L4: r25 = 'matched' @@ -693,22 +693,22 @@ def f(x): [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str r12, r13, r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: str r19, r20, r21 :: object - r22 :: int32 + r22 :: i32 r23 :: bit r24 :: bool r25 :: str @@ -722,7 +722,7 @@ L0: r0 = __main__.Position :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'z' @@ -731,7 +731,7 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L2 else goto L5 :: bool L2: r11 = 'y' @@ -740,7 +740,7 @@ L2: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L3 else goto L5 :: bool L3: r18 = 'x' @@ -749,7 +749,7 @@ L3: r21 = PyObject_RichCompare(r19, r20, 2) r22 = PyObject_IsTrue(r21) r23 = r22 >= 0 :: signed - r24 = truncate r22: int32 to builtins.bool + r24 = truncate r22: i32 to builtins.bool if r24 goto L4 else goto L5 :: bool L4: r25 = 'matched' @@ -776,16 +776,16 @@ def f(x): [out] def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12 :: object - r13 :: int32 + r13 :: i32 r14 :: bit r15 :: bool r16 :: str @@ -799,7 +799,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = 'num' @@ -808,14 +808,14 @@ L1: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L4 else goto L2 :: bool L2: r11 = object 2 r12 = PyObject_RichCompare(r5, r11, 2) r13 = PyObject_IsTrue(r12) r14 = r13 >= 0 :: signed - r15 = truncate r13: int32 to builtins.bool + r15 = truncate r13: i32 to builtins.bool if r15 goto L4 else goto L3 :: bool L3: goto L5 @@ -856,18 +856,18 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4, y :: __main__.C r5 :: str r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool r12 :: str r13, r14, r15 :: object - r16 :: int32 + r16 :: i32 r17 :: bit r18 :: bool r19 :: str @@ -881,7 +881,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L5 :: bool L1: r4 = cast(__main__.C, x) @@ -893,7 +893,7 @@ L2: r8 = PyObject_RichCompare(r6, r7, 2) r9 = PyObject_IsTrue(r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool if r11 goto L3 else goto L5 :: bool L3: r12 = 'b' @@ -902,7 +902,7 @@ L3: r15 = PyObject_RichCompare(r13, r14, 2) r16 = PyObject_IsTrue(r15) r17 = r16 >= 0 :: signed - r18 = truncate r16: int32 to builtins.bool + r18 = truncate r16: i32 to builtins.bool if r18 goto L4 else goto L5 :: bool L4: r19 = 'matched' @@ -940,7 +940,7 @@ L0: return 1 def f(x): x, r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str @@ -957,7 +957,7 @@ L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L3 :: bool L1: r4 = 'x' @@ -986,7 +986,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str r3 :: object @@ -1021,15 +1021,15 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: str @@ -1054,7 +1054,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L4 :: bool L3: r11 = 'matched' @@ -1078,7 +1078,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2, rest :: dict r3 :: str @@ -1117,19 +1117,19 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, rest :: dict - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: str r15 :: object @@ -1153,7 +1153,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = CPyDict_FromAny(x) @@ -1182,7 +1182,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit @@ -1224,16 +1224,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: str @@ -1258,7 +1258,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1266,7 +1266,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L5 :: bool L4: r17 = 'matched' @@ -1290,16 +1290,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: str @@ -1324,7 +1324,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L5 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1332,7 +1332,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L5 :: bool L4: r17 = 'matched' @@ -1356,16 +1356,16 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5, r6, r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11, r12, r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: native_int @@ -1392,7 +1392,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L6 :: bool L3: r11 = PySequence_GetItem(x, 1) @@ -1400,7 +1400,7 @@ L3: r13 = PyObject_RichCompare(r11, r12, 2) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L4 else goto L6 :: bool L4: r17 = r2 - 0 @@ -1428,21 +1428,21 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5 :: object r6 :: str r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool r11 :: native_int r12 :: object r13 :: str r14 :: object - r15 :: int32 + r15 :: i32 r16 :: bit r17 :: bool r18 :: native_int @@ -1469,7 +1469,7 @@ L2: r7 = PyObject_RichCompare(r5, r6, 2) r8 = PyObject_IsTrue(r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool if r10 goto L3 else goto L6 :: bool L3: r11 = r2 - 1 @@ -1478,7 +1478,7 @@ L3: r14 = PyObject_RichCompare(r12, r13, 2) r15 = PyObject_IsTrue(r14) r16 = r15 >= 0 :: signed - r17 = truncate r15: int32 to builtins.bool + r17 = truncate r15: i32 to builtins.bool if r17 goto L4 else goto L6 :: bool L4: r18 = r2 - 1 @@ -1506,18 +1506,18 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit r5 :: native_int r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool r12 :: native_int r13, r14, r15 :: object - r16 :: int32 + r16 :: i32 r17 :: bit r18 :: bool r19 :: native_int @@ -1545,7 +1545,7 @@ L2: r8 = PyObject_RichCompare(r6, r7, 2) r9 = PyObject_IsTrue(r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool if r11 goto L3 else goto L6 :: bool L3: r12 = r2 - 1 @@ -1554,7 +1554,7 @@ L3: r15 = PyObject_RichCompare(r13, r14, 2) r16 = PyObject_IsTrue(r15) r17 = r16 >= 0 :: signed - r18 = truncate r16: int32 to builtins.bool + r18 = truncate r16: i32 to builtins.bool if r18 goto L4 else goto L6 :: bool L4: r19 = r2 - 2 @@ -1620,7 +1620,7 @@ def f(x): [out] def f(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: native_int r3, r4 :: bit @@ -1674,7 +1674,7 @@ def f(x: A | int) -> int: def f(x): x :: union[__main__.A, int] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4 :: str @@ -1687,7 +1687,7 @@ L0: r0 = __main__.A :: type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L3 :: bool L1: r4 = 'a' diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index e98cf1b19e2e..e89018a727da 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -91,7 +91,7 @@ def f(x): r0 :: object r1 :: bit r2 :: __main__.A - r3 :: int32 + r3 :: i32 r4 :: bit r5 :: bool L0: @@ -102,7 +102,7 @@ L1: r2 = cast(__main__.A, x) r3 = PyObject_IsTrue(r2) r4 = r3 >= 0 :: signed - r5 = truncate r3: int32 to builtins.bool + r5 = truncate r3: i32 to builtins.bool if r5 goto L2 else goto L3 :: bool L2: return 2 @@ -252,7 +252,7 @@ def f(x: Union[int, A]) -> int: def f(x): x :: union[int, __main__.A] r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool r4, r5 :: int @@ -262,7 +262,7 @@ L0: r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool if r3 goto L1 else goto L2 :: bool L1: r4 = unbox(int, x) @@ -337,7 +337,7 @@ L3: def set(o, s): o :: union[__main__.A, __main__.B] s, r0 :: str - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = 'a' diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index b6c551124769..a56ebe3438fa 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -6,13 +6,13 @@ def f() -> Set[int]: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit L0: r0 = PySet_New(0) @@ -90,7 +90,7 @@ def test1(): r14 :: object r15, x, r16 :: int r17 :: object - r18 :: int32 + r18 :: i32 r19 :: bit r20 :: short_int a :: set @@ -138,7 +138,7 @@ def test2(): r2, r3, r4 :: object r5, x, r6 :: int r7 :: object - r8 :: int32 + r8 :: i32 r9, r10 :: bit b :: set L0: @@ -179,7 +179,7 @@ def test3(): r15 :: object r16, x, r17 :: int r18 :: object - r19 :: int32 + r19 :: i32 r20, r21, r22 :: bit c :: set L0: @@ -225,7 +225,7 @@ def test4(): r2 :: bit r3 :: int r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: short_int d :: set @@ -256,7 +256,7 @@ def test5(): r2 :: bit r3 :: int r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: short_int e :: set @@ -331,18 +331,18 @@ def test(): r29 :: bit r30 :: int r31 :: object - r32 :: int32 + r32 :: i32 r33 :: bit r34 :: short_int r35, r36, r37 :: object r38, y, r39 :: int r40 :: object - r41 :: int32 + r41 :: i32 r42, r43 :: bit r44, r45, r46 :: object r47, x, r48 :: int r49 :: object - r50 :: int32 + r50 :: i32 r51, r52 :: bit a :: set L0: @@ -452,13 +452,13 @@ def f() -> int: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: ptr r11 :: native_int @@ -489,14 +489,14 @@ def f() -> bool: def f(): r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit x :: set r7 :: object - r8 :: int32 + r8 :: i32 r9 :: bit r10 :: bool L0: @@ -511,7 +511,7 @@ L0: r7 = object 5 r8 = PySet_Contains(x, r7) r9 = r8 >= 0 :: signed - r10 = truncate r8: int32 to builtins.bool + r10 = truncate r8: i32 to builtins.bool return r10 [case testSetRemove] @@ -542,7 +542,7 @@ def f() -> Set[int]: def f(): r0, x :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = PySet_New(0) @@ -562,7 +562,7 @@ def f() -> Set[int]: def f(): r0, x :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = PySet_New(0) @@ -581,7 +581,7 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: int32 + r1 :: i32 r2 :: bit L0: r0 = PySet_New(0) @@ -612,7 +612,7 @@ def update(s: Set[int], x: List[int]) -> None: def update(s, x): s :: set x :: list - r0 :: int32 + r0 :: i32 r1 :: bit L0: r0 = _PySet_Update(s, x) @@ -627,17 +627,17 @@ def f(x: Set[int], y: Set[int]) -> Set[int]: def f(x, y): x, y, r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object - r5 :: int32 + r5 :: i32 r6 :: bit - r7 :: int32 + r7 :: i32 r8 :: bit - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit L0: r0 = PySet_New(0) @@ -672,14 +672,14 @@ def not_precomputed_nested_set(i: int) -> bool: def precomputed(i): i :: object r0 :: set - r1 :: int32 + r1 :: i32 r2 :: bit r3 :: bool L0: r0 = frozenset({(), (None, (27,)), 1, 2.0, 3, 4j, False, b'bar', 'daylily', 'foo'}) r1 = PySet_Contains(r0, i) r2 = r1 >= 0 :: signed - r3 = truncate r1: int32 to builtins.bool + r3 = truncate r1: i32 to builtins.bool return r3 def not_precomputed_non_final_name(i): i :: int @@ -689,10 +689,10 @@ def not_precomputed_non_final_name(i): r3 :: int r4 :: set r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: bool L0: @@ -707,23 +707,23 @@ L0: r8 = box(int, i) r9 = PySet_Contains(r4, r8) r10 = r9 >= 0 :: signed - r11 = truncate r9: int32 to builtins.bool + r11 = truncate r9: i32 to builtins.bool return r11 def not_precomputed_nested_set(i): i :: int r0 :: set r1 :: object - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: object r5 :: set - r6 :: int32 + r6 :: i32 r7 :: bit r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: bool L0: @@ -741,7 +741,7 @@ L0: r11 = box(int, i) r12 = PySet_Contains(r5, r11) r13 = r12 >= 0 :: signed - r14 = truncate r12: int32 to builtins.bool + r14 = truncate r12: i32 to builtins.bool return r14 [case testForSetLiteral] @@ -809,7 +809,7 @@ def not_precomputed(): r3 :: int r4 :: set r5 :: object - r6 :: int32 + r6 :: i32 r7 :: bit r8, r9 :: object r10, not_optimized :: int diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test index 4e18bbf50d4e..10970a385966 100644 --- a/mypyc/test-data/irbuild-singledispatch.test +++ b/mypyc/test-data/irbuild-singledispatch.test @@ -16,7 +16,7 @@ def f_obj.__init__(__mypyc_self__): __mypyc_self__ :: __main__.f_obj r0, r1 :: dict r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit L0: r0 = PyDict_New() @@ -39,7 +39,7 @@ def f_obj.__call__(__mypyc_self__, arg): r9 :: object r10 :: dict r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: object r15 :: ptr @@ -148,7 +148,7 @@ def f_obj.__init__(__mypyc_self__): __mypyc_self__ :: __main__.f_obj r0, r1 :: dict r2 :: str - r3 :: int32 + r3 :: i32 r4 :: bit L0: r0 = PyDict_New() @@ -171,7 +171,7 @@ def f_obj.__call__(__mypyc_self__, x): r9 :: object r10 :: dict r11 :: object - r12 :: int32 + r12 :: i32 r13 :: bit r14 :: object r15 :: ptr diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 090c7ed9f3df..062abd47d163 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -651,11 +651,11 @@ def f(l: List[int], t: Tuple[int, ...]) -> None: def f(l, t): l :: list t :: tuple - r0 :: int32 + r0 :: i32 r1 :: bit r2, r3, x :: object r4, y :: int - r5 :: int32 + r5 :: i32 r6 :: bit r7, r8 :: object r9 :: int @@ -701,13 +701,13 @@ L2: return 2 def literal_msg(x): x :: object - r0 :: int32 + r0 :: i32 r1 :: bit r2, r3 :: bool L0: r0 = PyObject_IsTrue(x) r1 = r0 >= 0 :: signed - r2 = truncate r0: int32 to builtins.bool + r2 = truncate r0: i32 to builtins.bool if r2 goto L2 else goto L1 :: bool L1: r3 = raise AssertionError('message') @@ -756,7 +756,7 @@ def delList(): r3, r4, r5 :: ptr l :: list r6 :: object - r7 :: int32 + r7 :: i32 r8 :: bit L0: r0 = PyList_New(2) @@ -779,13 +779,13 @@ def delListMultiple(): r8, r9, r10, r11, r12, r13, r14, r15 :: ptr l :: list r16 :: object - r17 :: int32 + r17 :: i32 r18 :: bit r19 :: object - r20 :: int32 + r20 :: i32 r21 :: bit r22 :: object - r23 :: int32 + r23 :: i32 r24 :: bit L0: r0 = PyList_New(7) @@ -837,7 +837,7 @@ def delDict(): r2, r3 :: object r4, d :: dict r5 :: str - r6 :: int32 + r6 :: i32 r7 :: bit L0: r0 = 'one' @@ -855,9 +855,9 @@ def delDictMultiple(): r4, r5, r6, r7 :: object r8, d :: dict r9, r10 :: str - r11 :: int32 + r11 :: i32 r12 :: bit - r13 :: int32 + r13 :: i32 r14 :: bit L0: r0 = 'one' @@ -901,7 +901,7 @@ L0: def delAttribute(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit L0: r0 = Dummy(2, 4) @@ -913,10 +913,10 @@ L0: def delAttributeMultiple(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: int32 + r2 :: i32 r3 :: bit r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit L0: r0 = Dummy(2, 4) @@ -1029,7 +1029,7 @@ def f(a, b): r6, r7 :: object r8, x :: int r9, y :: bool - r10 :: int32 + r10 :: i32 r11 :: bit r12 :: bool r13 :: short_int @@ -1055,7 +1055,7 @@ L3: y = r9 r10 = PyObject_IsTrue(b) r11 = r10 >= 0 :: signed - r12 = truncate r10: int32 to builtins.bool + r12 = truncate r10: i32 to builtins.bool if r12 goto L4 else goto L5 :: bool L4: x = 2 diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 63be7250ebd1..9851e0f4fb24 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -65,7 +65,7 @@ def neq(x: str, y: str) -> bool: [out] def eq(x, y): x, y :: str - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: object r3, r4, r5 :: bit @@ -84,7 +84,7 @@ L3: return r5 def neq(x, y): x, y :: str - r0 :: int32 + r0 :: i32 r1 :: bit r2 :: object r3, r4, r5 :: bit diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index faf3fa1dbd2f..a5b7b9a55b86 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -337,7 +337,7 @@ def foo(x): r11, r12 :: object r13, r14 :: tuple[object, object, object] r15, r16, r17, r18 :: object - r19 :: int32 + r19 :: i32 r20 :: bit r21 :: bool r22 :: bit @@ -372,7 +372,7 @@ L3: (handler for L2) r18 = PyObject_CallFunctionObjArgs(r3, r0, r15, r16, r17, 0) r19 = PyObject_IsTrue(r18) r20 = r19 >= 0 :: signed - r21 = truncate r19: int32 to builtins.bool + r21 = truncate r19: i32 to builtins.bool if r21 goto L5 else goto L4 :: bool L4: CPy_Reraise() @@ -448,7 +448,7 @@ def foo(x): r9, r10, r11 :: object r12 :: None r13 :: object - r14 :: int32 + r14 :: i32 r15 :: bit r16 :: bool r17 :: bit @@ -478,7 +478,7 @@ L3: (handler for L2) r13 = box(None, r12) r14 = PyObject_IsTrue(r13) r15 = r14 >= 0 :: signed - r16 = truncate r14: int32 to builtins.bool + r16 = truncate r14: i32 to builtins.bool if r16 goto L5 else goto L4 :: bool L4: CPy_Reraise() diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 6a86a6c6781b..a47f3db6a725 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -103,7 +103,7 @@ def f(x, y): r1, r2 :: object r3, r4, r5 :: ptr r6, r7, r8 :: object - r9 :: int32 + r9 :: i32 r10 :: bit r11 :: tuple L0: diff --git a/mypyc/test-data/irbuild-u8.test b/mypyc/test-data/irbuild-u8.test new file mode 100644 index 000000000000..14f691c9451f --- /dev/null +++ b/mypyc/test-data/irbuild-u8.test @@ -0,0 +1,543 @@ +# Test cases for u8 native ints. Focus on things that are different from i64; no need to +# duplicate all i64 test cases here. + +[case testU8BinaryOp] +from mypy_extensions import u8 + +def add_op(x: u8, y: u8) -> u8: + x = y + x + y = x + 5 + y += x + y += 7 + x = 5 + y + return x +def compare(x: u8, y: u8) -> None: + a = x == y + b = x == 5 + c = x < y + d = x < 5 + e = 5 == x + f = 5 < x +[out] +def add_op(x, y): + x, y, r0, r1, r2, r3, r4 :: u8 +L0: + r0 = y + x + x = r0 + r1 = x + 5 + y = r1 + r2 = y + x + y = r2 + r3 = y + 7 + y = r3 + r4 = 5 + y + x = r4 + return x +def compare(x, y): + x, y :: u8 + r0 :: bit + a :: bool + r1 :: bit + b :: bool + r2 :: bit + c :: bool + r3 :: bit + d :: bool + r4 :: bit + e :: bool + r5 :: bit + f :: bool +L0: + r0 = x == y + a = r0 + r1 = x == 5 + b = r1 + r2 = x < y :: unsigned + c = r2 + r3 = x < 5 :: unsigned + d = r3 + r4 = 5 == x + e = r4 + r5 = 5 < x :: unsigned + f = r5 + return 1 + +[case testU8UnaryOp] +from mypy_extensions import u8 + +def unary(x: u8) -> u8: + y = -x + x = ~y + y = +x + return y +[out] +def unary(x): + x, r0, y, r1 :: u8 +L0: + r0 = 0 - x + y = r0 + r1 = y ^ 255 + x = r1 + y = x + return y + +[case testU8DivisionByConstant] +from mypy_extensions import u8 + +def div_by_constant(x: u8) -> u8: + x = x // 5 + x //= 17 + return x +[out] +def div_by_constant(x): + x, r0, r1 :: u8 +L0: + r0 = x / 5 + x = r0 + r1 = x / 17 + x = r1 + return x + +[case testU8ModByConstant] +from mypy_extensions import u8 + +def mod_by_constant(x: u8) -> u8: + x = x % 5 + x %= 17 + return x +[out] +def mod_by_constant(x): + x, r0, r1 :: u8 +L0: + r0 = x % 5 + x = r0 + r1 = x % 17 + x = r1 + return x + +[case testU8DivModByVariable] +from mypy_extensions import u8 + +def divmod(x: u8, y: u8) -> u8: + a = x // y + return a % y +[out] +def divmod(x, y): + x, y :: u8 + r0 :: bit + r1 :: bool + r2, a :: u8 + r3 :: bit + r4 :: bool + r5 :: u8 +L0: + r0 = y == 0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = raise ZeroDivisionError('integer division or modulo by zero') + unreachable +L2: + r2 = x / y + a = r2 + r3 = y == 0 + if r3 goto L3 else goto L4 :: bool +L3: + r4 = raise ZeroDivisionError('integer division or modulo by zero') + unreachable +L4: + r5 = a % y + return r5 + +[case testU8BinaryOperationWithOutOfRangeOperand] +from mypy_extensions import u8 + +def out_of_range(x: u8) -> None: + x + (-1) + (-2) + x + x * 256 + -1 < x + x > -5 + x == 1000 + x + 255 # OK + 255 + x # OK +[out] +main:4: error: Value -1 is out of range for "u8" +main:5: error: Value -2 is out of range for "u8" +main:6: error: Value 256 is out of range for "u8" +main:7: error: Value -1 is out of range for "u8" +main:8: error: Value -5 is out of range for "u8" +main:9: error: Value 1000 is out of range for "u8" + +[case testU8DetectMoreOutOfRangeLiterals] +from mypy_extensions import u8 + +def out_of_range() -> None: + a: u8 = 256 + b: u8 = -1 + f(256) + # The following are ok + c: u8 = 0 + d: u8 = 255 + f(0) + f(255) + +def f(x: u8) -> None: pass +[out] +main:4: error: Value 256 is out of range for "u8" +main:5: error: Value -1 is out of range for "u8" +main:6: error: Value 256 is out of range for "u8" + +[case testU8BoxAndUnbox] +from typing import Any +from mypy_extensions import u8 + +def f(x: Any) -> Any: + y: u8 = x + return y +[out] +def f(x): + x :: object + r0, y :: u8 + r1 :: object +L0: + r0 = unbox(u8, x) + y = r0 + r1 = box(u8, y) + return r1 + +[case testU8MixedCompare1] +from mypy_extensions import u8 +def f(x: int, y: u8) -> bool: + return x == y +[out] +def f(x, y): + x :: int + y :: u8 + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: u8 + r7 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to u8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = r6 == y + return r7 + +[case testU8MixedCompare2] +from mypy_extensions import u8 +def f(x: u8, y: int) -> bool: + return x == y +[out] +def f(x, y): + x :: u8 + y :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: u8 + r7 :: bit +L0: + r0 = y & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = y < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = y >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = y >> 1 + r5 = truncate r4: native_int to u8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = x == r6 + return r7 + +[case testU8ConvertToInt] +from mypy_extensions import u8 + +def u8_to_int(a: u8) -> int: + return a +[out] +def u8_to_int(a): + a :: u8 + r0 :: native_int + r1 :: int +L0: + r0 = extend a: u8 to native_int + r1 = r0 << 1 + return r1 + +[case testU8OperatorAssignmentMixed] +from mypy_extensions import u8 + +def f(a: u8) -> None: + x = 0 + x += a +[out] +def f(a): + a :: u8 + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6, r7 :: u8 + r8 :: native_int + r9 :: int +L0: + x = 0 + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to u8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + r7 = r6 + a + r8 = extend r7: u8 to native_int + r9 = r8 << 1 + x = r9 + return 1 + +[case testU8InitializeFromLiteral] +from mypy_extensions import u8, i64 + +def f() -> None: + x: u8 = 0 + y: u8 = 255 + z: u8 = 5 + 7 +[out] +def f(): + x, y, z :: u8 +L0: + x = 0 + y = 255 + z = 12 + return 1 + +[case testU8ExplicitConversionFromNativeInt] +from mypy_extensions import i64, i32, i16, u8 + +def from_u8(x: u8) -> u8: + return u8(x) + +def from_i16(x: i16) -> u8: + return u8(x) + +def from_i32(x: i32) -> u8: + return u8(x) + +def from_i64(x: i64) -> u8: + return u8(x) +[out] +def from_u8(x): + x :: u8 +L0: + return x +def from_i16(x): + x :: i16 + r0 :: u8 +L0: + r0 = truncate x: i16 to u8 + return r0 +def from_i32(x): + x :: i32 + r0 :: u8 +L0: + r0 = truncate x: i32 to u8 + return r0 +def from_i64(x): + x :: i64 + r0 :: u8 +L0: + r0 = truncate x: i64 to u8 + return r0 + +[case testU8ExplicitConversionToNativeInt] +from mypy_extensions import i64, i32, i16, u8 + +def to_i16(x: u8) -> i16: + return i16(x) + +def to_i32(x: u8) -> i32: + return i32(x) + +def to_i64(x: u8) -> i64: + return i64(x) +[out] +def to_i16(x): + x :: u8 + r0 :: i16 +L0: + r0 = extend x: u8 to i16 + return r0 +def to_i32(x): + x :: u8 + r0 :: i32 +L0: + r0 = extend x: u8 to i32 + return r0 +def to_i64(x): + x :: u8 + r0 :: i64 +L0: + r0 = extend x: u8 to i64 + return r0 + +[case testU8ExplicitConversionFromInt] +from mypy_extensions import u8 + +def f(x: int) -> u8: + return u8(x) +[out] +def f(x): + x :: int + r0 :: native_int + r1, r2, r3 :: bit + r4 :: native_int + r5, r6 :: u8 +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L4 :: bool +L1: + r2 = x < 512 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = x >= 0 :: signed + if r3 goto L3 else goto L4 :: bool +L3: + r4 = x >> 1 + r5 = truncate r4: native_int to u8 + r6 = r5 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + return r6 + +[case testU8ExplicitConversionFromLiteral] +from mypy_extensions import u8 + +def f() -> None: + x = u8(0) + y = u8(11) + z = u8(-3) # Truncate + zz = u8(258) # Truncate + a = u8(255) +[out] +def f(): + x, y, z, zz, a :: u8 +L0: + x = 0 + y = 11 + z = 253 + zz = 2 + a = 255 + return 1 + +[case testU8ExplicitConversionFromVariousTypes] +from mypy_extensions import u8 + +def bool_to_u8(b: bool) -> u8: + return u8(b) + +def str_to_u8(s: str) -> u8: + return u8(s) + +class C: + def __int__(self) -> u8: + return 5 + +def instance_to_u8(c: C) -> u8: + return u8(c) + +def float_to_u8(x: float) -> u8: + return u8(x) +[out] +def bool_to_u8(b): + b :: bool + r0 :: u8 +L0: + r0 = extend b: builtins.bool to u8 + return r0 +def str_to_u8(s): + s :: str + r0 :: object + r1 :: u8 +L0: + r0 = CPyLong_FromStr(s) + r1 = unbox(u8, r0) + return r1 +def C.__int__(self): + self :: __main__.C +L0: + return 5 +def instance_to_u8(c): + c :: __main__.C + r0 :: u8 +L0: + r0 = c.__int__() + return r0 +def float_to_u8(x): + x :: float + r0 :: int + r1 :: native_int + r2, r3, r4 :: bit + r5 :: native_int + r6, r7 :: u8 +L0: + r0 = CPyTagged_FromFloat(x) + r1 = r0 & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L4 :: bool +L1: + r3 = r0 < 512 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = r0 >= 0 :: signed + if r4 goto L3 else goto L4 :: bool +L3: + r5 = r0 >> 1 + r6 = truncate r5: native_int to u8 + r7 = r6 + goto L5 +L4: + CPyUInt8_Overflow() + unreachable +L5: + return r7 diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test index 2c164491a5a1..1c024a249bf1 100644 --- a/mypyc/test-data/irbuild-unreachable.test +++ b/mypyc/test-data/irbuild-unreachable.test @@ -11,7 +11,7 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object r8, r9, r10 :: bit @@ -68,7 +68,7 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: int32 + r5 :: i32 r6 :: bit r7 :: object r8, r9, r10 :: bit diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 372956a00cab..3db4caa39566 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -702,7 +702,7 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: int32 + r1 :: i32 r2 :: bit L0: inc_ref x :: int @@ -1504,10 +1504,10 @@ def f(x): x, r0 :: int r1 :: native_int r2 :: bit - r3, r4 :: int64 + r3, r4 :: i64 r5 :: ptr r6 :: c_ptr - r7 :: int64 + r7 :: i64 L0: r0 = CPyTagged_Add(x, 2) r1 = r0 & 1 diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test new file mode 100644 index 000000000000..cddb031e3352 --- /dev/null +++ b/mypyc/test-data/run-u8.test @@ -0,0 +1,303 @@ +[case testU8BasicOps] +from typing import Any, Tuple + +from mypy_extensions import u8, i16, i32, i64 +from typing_extensions import Final + +from testutil import assertRaises + +ERROR: Final = 239 + +def test_box_and_unbox() -> None: + for i in range(0, 256): + o: Any = i + x: u8 = o + o2: Any = x + assert o == o2 + assert x == i + with assertRaises(OverflowError, "int too large or small to convert to u8"): + o = 256 + x2: u8 = o + with assertRaises(OverflowError, "int too large or small to convert to u8"): + o = -1 + x3: u8 = o + +def div_by_7(x: u8) -> u8: + return x // 7 + +def div(x: u8, y: u8) -> u8: + return x // y + +def test_divide_by_constant() -> None: + for i in range(0, 256): + assert div_by_7(i) == i // 7 + +def test_divide_by_variable() -> None: + for x in range(0, 256): + for y in range(0, 256): + if y != 0: + assert div(x, y) == x // y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + div(x, y) + +def mod_by_7(x: u8) -> u8: + return x % 7 + +def mod(x: u8, y: u8) -> u8: + return x % y + +def test_mod_by_constant() -> None: + for i in range(0, 256): + assert mod_by_7(i) == i % 7 + +def test_mod_by_variable() -> None: + for x in range(0, 256): + for y in range(0, 256): + if y != 0: + assert mod(x, y) == x % y + else: + with assertRaises(ZeroDivisionError, "integer division or modulo by zero"): + mod(x, y) + +def test_simple_arithmetic_ops() -> None: + zero: u8 = int() + one: u8 = zero + 1 + two: u8 = one + 1 + neg_one: u8 = -one + assert neg_one == 255 + assert one + one == 2 + assert one + two == 3 + assert one + neg_one == 0 + assert one - one == 0 + assert one - two == 255 + assert one * one == 1 + assert one * two == 2 + assert two * two == 4 + assert two * neg_one == 254 + assert neg_one * one == 255 + assert neg_one * neg_one == 1 + assert two * 0 == 0 + assert 0 * two == 0 + assert -one == 255 + assert -two == 254 + assert -neg_one == 1 + assert -zero == 0 + +def test_bitwise_ops() -> None: + x: u8 = 184 + int() + y: u8 = 79 + int() + z: u8 = 113 + int() + zero: u8 = int() + one: u8 = zero + 1 + two: u8 = zero + 2 + neg_one: u8 = -one + + assert x & y == 8 + assert x & z == 48 + assert z & z == z + assert x & zero == 0 + + assert x | y == 255 + assert x | z == 249 + assert z | z == z + assert x | 0 == x + + assert x ^ y == 247 + assert x ^ z == 201 + assert z ^ z == 0 + assert z ^ 0 == z + + assert x << one == 112 + assert x << two == 224 + assert z << two == 196 + assert z << 0 == z + + assert x >> one == 92 + assert x >> two == 46 + assert z >> two == 28 + assert z >> 0 == z + + for i in range(256): + t: u8 = i + assert ~t == (~(i + int()) & 0xff) + +def eq(x: u8, y: u8) -> bool: + return x == y + +def test_eq() -> None: + assert eq(int(), int()) + assert eq(5 + int(), 5 + int()) + assert not eq(int(), 1 + int()) + assert not eq(5 + int(), 6 + int()) + +def test_comparisons() -> None: + one: u8 = 1 + int() + one2: u8 = 1 + int() + two: u8 = 2 + int() + assert one < two + assert not (one < one2) + assert not (two < one) + assert two > one + assert not (one > one2) + assert not (one > two) + assert one <= two + assert one <= one2 + assert not (two <= one) + assert two >= one + assert one >= one2 + assert not (one >= two) + assert one == one2 + assert not (one == two) + assert one != two + assert not (one != one2) + +def test_mixed_comparisons() -> None: + u8_3: u8 = int() + 3 + int_5 = int() + 5 + assert u8_3 < int_5 + assert int_5 > u8_3 + b = u8_3 > int_5 + assert not b + + int_largest = int() + 255 + assert int_largest > u8_3 + int_smallest = int() + assert u8_3 > int_smallest + + int_too_big = int() + 256 + int_too_small = int() -1 + with assertRaises(OverflowError): + assert u8_3 < int_too_big + with assertRaises(OverflowError): + assert int_too_big < u8_3 + with assertRaises(OverflowError): + assert u8_3 > int_too_small + with assertRaises(OverflowError): + assert int_too_small < u8_3 + +def test_mixed_arithmetic_and_bitwise_ops() -> None: + u8_3: u8 = int() + 3 + int_5 = int() + 5 + assert u8_3 + int_5 == 8 + assert int_5 - u8_3 == 2 + assert u8_3 << int_5 == 96 + assert int_5 << u8_3 == 40 + assert u8_3 ^ int_5 == 6 + assert int_5 | u8_3 == 7 + + int_largest = int() + 255 + assert int_largest - u8_3 == 252 + int_smallest = int() + assert int_smallest + u8_3 == 3 + + int_too_big = int() + 256 + int_too_small = int() - 1 + with assertRaises(OverflowError): + assert u8_3 & int_too_big + with assertRaises(OverflowError): + assert int_too_small & u8_3 + +def test_coerce_to_and_from_int() -> None: + for n in range(0, 256): + x: u8 = n + m: int = x + assert m == n + +def test_explicit_conversion_to_u8() -> None: + x = u8(5) + assert x == 5 + y = int() + ERROR + x = u8(y) + assert x == ERROR + n64: i64 = 233 + x = u8(n64) + assert x == 233 + n32: i32 = 234 + x = u8(n32) + assert x == 234 + z = u8(x) + assert z == 234 + n16: i16 = 231 + x = u8(n16) + assert x == 231 + +def test_explicit_conversion_overflow() -> None: + max_u8 = int() + 255 + x = u8(max_u8) + assert x == 255 + assert int(x) == max_u8 + + min_u8 = int() + y = u8(min_u8) + assert y == 0 + assert int(y) == min_u8 + + too_big = int() + 256 + with assertRaises(OverflowError): + x = u8(too_big) + + too_small = int() - 1 + with assertRaises(OverflowError): + x = u8(too_small) + +def test_u8_from_large_small_literal() -> None: + x = u8(255) # XXX u8(2**15 - 1) + assert x == 255 + x = u8(0) + assert x == 0 + +def test_u8_truncate_from_i64() -> None: + large = i64(2**32 + 256 + 157 + int()) + x = u8(large) + assert x == 157 + small = i64(-2**32 - 256 - 157 + int()) + x = u8(small) + assert x == 256 - 157 + large2 = i64(2**8 + int()) + x = u8(large2) + assert x == 0 + small2 = i64(-2**8 - 1 - int()) + x = u8(small2) + assert x == 255 + +def test_u8_truncate_from_i32() -> None: + large = i32(2**16 + 2**8 + 5 + int()) + assert u8(large) == 5 + small = i32(-2**16 - 2**8 - 1 + int()) + assert u8(small) == 255 + +def from_float(x: float) -> u8: + return u8(x) + +def test_explicit_conversion_from_float() -> None: + assert from_float(0.0) == 0 + assert from_float(1.456) == 1 + assert from_float(234.567) == 234 + assert from_float(255) == 255 + assert from_float(0) == 0 + assert from_float(-0.999) == 0 + # The error message could be better, but this is acceptable + with assertRaises(OverflowError, "int too large or small to convert to u8"): + assert from_float(float(256)) + with assertRaises(OverflowError, "int too large or small to convert to u8"): + # One ulp below the lowest valid i64 value + from_float(float(-1.0)) + +def test_tuple_u8() -> None: + a: u8 = 1 + b: u8 = 2 + t = (a, b) + a, b = t + assert a == 1 + assert b == 2 + x: Any = t + tt: Tuple[u8, u8] = x + assert tt == (1, 2) + +def test_convert_u8_to_native_int() -> None: + for i in range(256): + x: u8 = i + assert i16(x) == i + assert i32(x) == i + assert i64(x) == i diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 2b14619a9884..5b3f678d8f17 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -43,6 +43,7 @@ "irbuild-i64.test", "irbuild-i32.test", "irbuild-i16.test", + "irbuild-u8.test", "irbuild-vectorcall.test", "irbuild-unreachable.test", "irbuild-isinstance.test", diff --git a/mypyc/test/test_ircheck.py b/mypyc/test/test_ircheck.py index 008963642272..7f7063cdc5e6 100644 --- a/mypyc/test/test_ircheck.py +++ b/mypyc/test/test_ircheck.py @@ -117,7 +117,7 @@ def test_invalid_return_type(self) -> None: blocks=[self.basic_block([ret])], ) assert_has_error( - fn, FnError(source=ret, desc="Cannot coerce source type int32 to dest type int64") + fn, FnError(source=ret, desc="Cannot coerce source type i32 to dest type i64") ) def test_invalid_assign(self) -> None: @@ -130,7 +130,7 @@ def test_invalid_assign(self) -> None: blocks=[self.basic_block([assign, ret])], ) assert_has_error( - fn, FnError(source=assign, desc="Cannot coerce source type int32 to dest type int64") + fn, FnError(source=assign, desc="Cannot coerce source type i32 to dest type i64") ) def test_can_coerce_to(self) -> None: diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 2dd1c025123f..df9d44eab73f 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -42,6 +42,7 @@ "run-i64.test", "run-i32.test", "run-i16.test", + "run-u8.test", "run-floats.test", "run-math.test", "run-bools.test", diff --git a/mypyc/test/test_struct.py b/mypyc/test/test_struct.py index 2b0298cadeda..82990e6afd82 100644 --- a/mypyc/test/test_struct.py +++ b/mypyc/test/test_struct.py @@ -55,8 +55,8 @@ def test_struct_str(self) -> None: "b:}>" ) r1 = RStruct("Bar", ["c"], [int32_rprimitive]) - assert str(r1) == "Bar{c:int32}" - assert repr(r1) == "}>" + assert str(r1) == "Bar{c:i32}" + assert repr(r1) == "}>" r2 = RStruct("Baz", [], []) assert str(r2) == "Baz{}" assert repr(r2) == "" diff --git a/test-data/unit/lib-stub/mypy_extensions.pyi b/test-data/unit/lib-stub/mypy_extensions.pyi index b7ff01064315..4295c33f81ad 100644 --- a/test-data/unit/lib-stub/mypy_extensions.pyi +++ b/test-data/unit/lib-stub/mypy_extensions.pyi @@ -50,7 +50,37 @@ class FlexibleAlias(Generic[_T, _U]): ... class __SupportsInt(Protocol[T_co]): def __int__(self) -> int: pass -_Int = Union[int, i16, i32, i64] +_Int = Union[int, u8, i16, i32, i64] + +class u8: + def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... + def __add__(self, x: u8) -> u8: ... + def __radd__(self, x: u8) -> u8: ... + def __sub__(self, x: u8) -> u8: ... + def __rsub__(self, x: u8) -> u8: ... + def __mul__(self, x: u8) -> u8: ... + def __rmul__(self, x: u8) -> u8: ... + def __floordiv__(self, x: u8) -> u8: ... + def __rfloordiv__(self, x: u8) -> u8: ... + def __mod__(self, x: u8) -> u8: ... + def __rmod__(self, x: u8) -> u8: ... + def __and__(self, x: u8) -> u8: ... + def __rand__(self, x: u8) -> u8: ... + def __or__(self, x: u8) -> u8: ... + def __ror__(self, x: u8) -> u8: ... + def __xor__(self, x: u8) -> u8: ... + def __rxor__(self, x: u8) -> u8: ... + def __lshift__(self, x: u8) -> u8: ... + def __rlshift__(self, x: u8) -> u8: ... + def __rshift__(self, x: u8) -> u8: ... + def __rrshift__(self, x: u8) -> u8: ... + def __neg__(self) -> u8: ... + def __invert__(self) -> u8: ... + def __pos__(self) -> u8: ... + def __lt__(self, x: u8) -> bool: ... + def __le__(self, x: u8) -> bool: ... + def __ge__(self, x: u8) -> bool: ... + def __gt__(self, x: u8) -> bool: ... class i16: def __init__(self, x: Union[_Int, str, bytes, SupportsInt], base: int = 10) -> None: ... From 79114d19df14b28effc8bbd7cb376521d83b0046 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Jul 2023 18:09:57 +0100 Subject: [PATCH 0113/1617] [mypyc] Fix multiple inheritance with a protocol on Python 3.12 (#15572) Fix the testProtocol test case in mypyc/test-data/run-classes.test. There was a comment indicating that the code that causes the test to fail on 3.12 is required Python versions older than 3.7, so let's remove it. We only support 3.8 and later these days. Work on https://github.com/mypyc/mypyc/issues/995. --- mypyc/lib-rt/misc_ops.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index 88a76fb210d7..f28eeb57e646 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -177,42 +177,6 @@ PyObject *CPyType_FromTemplate(PyObject *template, if (!name) goto error; - // If there is a metaclass other than type, we would like to call - // its __new__ function. Unfortunately there doesn't seem to be a - // good way to mix a C extension class and creating it via a - // metaclass. We need to do it anyways, though, in order to - // support subclassing Generic[T] prior to Python 3.7. - // - // We solve this with a kind of atrocious hack: create a parallel - // class using the metaclass, determine the bases of the real - // class by pulling them out of the parallel class, creating the - // real class, and then merging its dict back into the original - // class. There are lots of cases where this won't really work, - // but for the case of GenericMeta setting a bunch of properties - // on the class we should be fine. - if (metaclass != &PyType_Type) { - assert(bases && "non-type metaclasses require non-NULL bases"); - - PyObject *ns = PyDict_New(); - if (!ns) - goto error; - - if (bases != orig_bases) { - if (PyDict_SetItemString(ns, "__orig_bases__", orig_bases) < 0) - goto error; - } - - dummy_class = (PyTypeObject *)PyObject_CallFunctionObjArgs( - (PyObject *)metaclass, name, bases, ns, NULL); - Py_DECREF(ns); - if (!dummy_class) - goto error; - - Py_DECREF(bases); - bases = dummy_class->tp_bases; - Py_INCREF(bases); - } - // Allocate the type and then copy the main stuff in. t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); if (!t) From cf863877aac1e1b54a929aca08125bfdcd4bf8f9 Mon Sep 17 00:00:00 2001 From: Stavros Ntentos <133706+stdedos@users.noreply.github.com> Date: Thu, 6 Jul 2023 22:12:34 +0300 Subject: [PATCH 0114/1617] "mypy Gitter" is dead; it is called "python/typing" (#15610) --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 64a2e6494c1b..a88773308d5e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,6 +2,6 @@ contact_links: - about: "Please check the linked documentation page before filing new issues." name: "Common issues and solutions" url: "https://mypy.readthedocs.io/en/stable/common_issues.html" - - about: "Please ask and answer any questions on the mypy Gitter." + - about: "Please ask and answer any questions on the python/typing Gitter." name: "Questions or Chat" url: "https://gitter.im/python/typing" From d65e1e7523a30323a617ecb55cde8a9ed89e653a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:04:20 +0200 Subject: [PATCH 0115/1617] Add upper bound for lxml (#15608) --- test-requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 661187823368..195909b71b8d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,8 @@ attrs>=18.0 black==23.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 -lxml>=4.9.1; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' +# lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 +lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' pre-commit pre-commit-hooks==4.4.0 psutil>=4.0 From 91e4ce496fd6032c8670d1d3ce350930ab4f3aa4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 7 Jul 2023 05:05:14 +0200 Subject: [PATCH 0116/1617] Enable strict optional for more test files (7) (#15604) --- mypy/test/testcheck.py | 5 - test-data/unit/check-inference-context.test | 176 ++++++++++---------- test-data/unit/check-inference.test | 111 ++++++------ test-data/unit/check-isinstance.test | 23 +-- test-data/unit/check-kwargs.test | 37 ++-- test-data/unit/check-literal.test | 18 +- 6 files changed, 199 insertions(+), 171 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index f15e5afabf0e..c91bd915096f 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -51,11 +51,6 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { - "check-inference-context.test", - "check-inference.test", - "check-isinstance.test", - "check-kwargs.test", - "check-literal.test", "check-modules.test", "check-namedtuple.test", "check-overloading.test", diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 625ab091a6a9..13cfec46eeab 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -13,9 +13,9 @@ def f() -> 'A[T]': pass class A(Generic[T]): pass class B: pass -ab = None # type: A[B] -ao = None # type: A[object] -b = None # type: B +ab: A[B] +ao: A[object] +b: B if int(): ao = f() @@ -28,9 +28,9 @@ from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): pass class B: pass -ab = None # type: A[B] -ao = None # type: A[object] -b = None # type: B +ab: A[B] +ao: A[object] +b: B if int(): ao = A() @@ -48,11 +48,11 @@ class A(Generic[T]): pass class B: pass class C: pass -b = None # type: B -c = None # type: C -ab = None # type: A[B] -ao = None # type: A[object] -ac = None # type: A[C] +b: B +c: C +ab: A[B] +ao: A[object] +ac: A[C] if int(): ac = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "C" @@ -77,10 +77,10 @@ if int(): from typing import TypeVar, Generic T = TypeVar('T') def g() -> None: - ao = None # type: A[object] - ab = None # type: A[B] - o = None # type: object - b = None # type: B + ao: A[object] + ab: A[B] + o: object + b: B x = f(o) if int(): @@ -111,9 +111,9 @@ class A(Generic[T]): pass from typing import TypeVar, Generic T = TypeVar('T') def g() -> None: - ao = None # type: A[object] - ab = None # type: A[B] - b = None # type: B + ao: A[object] + ab: A[B] + b: B x, y = f(b), f(b) if int(): ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") @@ -130,9 +130,9 @@ class B: pass from typing import TypeVar, List, Generic T = TypeVar('T') def h() -> None: - ao = None # type: A[object] - ab = None # type: A[B] - b = None # type: B + ao: A[object] + ab: A[B] + b: B x, y = g(f(b)) if int(): ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") @@ -162,10 +162,10 @@ def f(a: T) -> 'Tuple[A[T], A[T]]': pass class A(Generic[T]): pass class B: pass -b = None # type: B -o = None # type: object -ab = None # type: A[B] -ao = None # type: A[object] +b: B +o: object +ab: A[B] +ao: A[object] if int(): ab, ao = f(b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") @@ -192,10 +192,10 @@ def h(a: S, b: T) -> 'Tuple[A[S], A[S], A[T], A[T]]': pass class A(Generic[T]): pass class B: pass -b = None # type: B -o = None # type: object -ab = None # type: A[B] -ao = None # type: A[object] +b: B +o: object +ab: A[B] +ao: A[object] if int(): ao, ao, ab = f(b, b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") @@ -229,12 +229,12 @@ class A(Generic[T]): pass class B: pass class C(B): pass -ac = None # type: A[C] -ab = None # type: A[B] -ao = None # type: A[object] -b = None # type: B -c = None # type: C -o = None # type: object +ac: A[C] +ab: A[B] +ao: A[object] +b: B +c: C +o: object if int(): ab = f(b, o) # E: Argument 2 to "f" has incompatible type "object"; expected "B" @@ -266,11 +266,11 @@ def f(a: T) -> 'A[T]': pass class A(Generic[T]): pass class B: pass -aab = None # type: A[A[B]] -aao = None # type: A[A[object]] -ao = None # type: A[object] -b = None # type: B -o = None # type: object +aab: A[A[B]] +aao: A[A[object]] +ao: A[object] +b: B +o: object if int(): aab = f(f(o)) # E: Argument 1 to "f" has incompatible type "object"; expected "B" @@ -279,6 +279,7 @@ if int(): aab = f(f(b)) aao = f(f(b)) ao = f(f(b)) + [case testNestedGenericFunctionCall2] from typing import TypeVar, Generic T = TypeVar('T') @@ -289,10 +290,10 @@ def g(a: T) -> 'A[T]': pass class A(Generic[T]): pass class B: pass -ab = None # type: A[B] -ao = None # type: A[object] -b = None # type: B -o = None # type: object +ab: A[B] +ao: A[object] +b: B +o: object if int(): ab = f(g(o)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" @@ -300,6 +301,7 @@ if int(): if int(): ab = f(g(b)) ao = f(g(b)) + [case testNestedGenericFunctionCall3] from typing import TypeVar, Generic T = TypeVar('T') @@ -310,10 +312,10 @@ def g(a: T) -> 'A[T]': pass class A(Generic[T]): pass class B: pass -ab = None # type: A[B] -ao = None # type: A[object] -b = None # type: B -o = None # type: object +ab: A[B] +ao: A[object] +b: B +o: object if int(): ab = f(g(o), g(b)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" @@ -334,9 +336,9 @@ if int(): [case testMethodCallWithContextInference] from typing import TypeVar, Generic T = TypeVar('T') -o = None # type: object -b = None # type: B -c = None # type: C +o: object +b: B +c: C def f(a: T) -> 'A[T]': pass class A(Generic[T]): @@ -344,9 +346,9 @@ class A(Generic[T]): class B: pass class C(B): pass -ao = None # type: A[object] -ab = None # type: A[B] -ac = None # type: A[C] +ao: A[object] +ab: A[B] +ac: A[C] ab.g(f(o)) # E: Argument 1 to "f" has incompatible type "object"; expected "B" if int(): @@ -365,9 +367,9 @@ ab.g(f(c)) [case testEmptyListExpression] from typing import List -aa = None # type: List[A] -ao = None # type: List[object] -a = None # type: A +aa: List[A] +ao: List[object] +a: A def f(): a, aa, ao # Prevent redefinition a = [] # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") @@ -379,15 +381,15 @@ class A: pass [builtins fixtures/list.pyi] [case testSingleItemListExpressions] -from typing import List -aa = None # type: List[A] -ab = None # type: List[B] -ao = None # type: List[object] -a = None # type: A -b = None # type: B +from typing import List, Optional +aa: List[Optional[A]] +ab: List[B] +ao: List[object] +a: A +b: B def f(): aa, ab, ao # Prevent redefinition -aa = [b] # E: List item 0 has incompatible type "B"; expected "A" +aa = [b] # E: List item 0 has incompatible type "B"; expected "Optional[A]" ab = [a] # E: List item 0 has incompatible type "A"; expected "B" aa = [a] @@ -402,11 +404,11 @@ class B: pass [case testMultiItemListExpressions] from typing import List -aa = None # type: List[A] -ab = None # type: List[B] -ao = None # type: List[object] -a = None # type: A -b = None # type: B +aa: List[A] +ab: List[B] +ao: List[object] +a: A +b: B def f(): ab, aa, ao # Prevent redefinition ab = [b, a] # E: List item 1 has incompatible type "A"; expected "B" @@ -433,6 +435,7 @@ class B: pass [out] [case testNestedListExpressions] +# flags: --no-strict-optional from typing import List aao = None # type: List[List[object]] aab = None # type: List[List[B]] @@ -596,17 +599,17 @@ y = C() # type: Any [case testInferLambdaArgumentTypeUsingContext] from typing import Callable -f = None # type: Callable[[B], A] +f: Callable[[B], A] if int(): f = lambda x: x.o f = lambda x: x.x # E: "B" has no attribute "x" class A: pass class B: - o = None # type: A + o: A [case testInferLambdaReturnTypeUsingContext] from typing import List, Callable -f = None # type: Callable[[], List[A]] +f: Callable[[], List[A]] if int(): f = lambda: [] f = lambda: [B()] # E: List item 0 has incompatible type "B"; expected "A" @@ -680,6 +683,7 @@ def foo(arg: Callable[..., T]) -> None: pass foo(lambda: 1) [case testLambdaNoneInContext] +# flags: --no-strict-optional from typing import Callable def f(x: Callable[[], None]) -> None: pass def g(x: Callable[[], int]) -> None: pass @@ -687,13 +691,13 @@ f(lambda: None) g(lambda: None) [case testIsinstanceInInferredLambda] -from typing import TypeVar, Callable +from typing import TypeVar, Callable, Optional T = TypeVar('T') S = TypeVar('S') class A: pass class B(A): pass class C(A): pass -def f(func: Callable[[T], S], *z: T, r: S = None) -> S: pass +def f(func: Callable[[T], S], *z: T, r: Optional[S] = None) -> S: pass f(lambda x: 0 if isinstance(x, B) else 1) # E: Cannot infer type argument 1 of "f" f(lambda x: 0 if isinstance(x, B) else 1, A())() # E: "int" not callable f(lambda x: x if isinstance(x, B) else B(), A(), r=B())() # E: "B" not callable @@ -739,7 +743,9 @@ from typing import List class A: pass class B: pass class C(B): pass -a, b, c = None, None, None # type: (List[A], List[B], List[C]) +a: List[A] +b: List[B] +c: List[C] if int(): a = a or [] if int(): @@ -762,7 +768,7 @@ from typing import List, TypeVar t = TypeVar('t') s = TypeVar('s') # Some type variables can be inferred using context, but not all of them. -a = None # type: List[A] +a: List[A] def f(a: s, b: t) -> List[s]: pass class A: pass class B: pass @@ -780,7 +786,7 @@ def f(a: s, b: t) -> List[s]: pass class A: pass class B: pass # Like testSomeTypeVarsInferredFromContext, but tvars in different order. -a = None # type: List[A] +a: List[A] if int(): a = f(A(), B()) if int(): @@ -800,8 +806,8 @@ map( [case testChainedAssignmentInferenceContexts] from typing import List -i = None # type: List[int] -s = None # type: List[str] +i: List[int] +s: List[str] if int(): i = i = [] if int(): @@ -821,10 +827,10 @@ a.x = [''] # E: List item 0 has incompatible type "str"; expected "int" [case testListMultiplyInContext] from typing import List -a = None # type: List[int] +a: List[int] if int(): - a = [None] * 3 - a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int" + a = [None] * 3 # E: List item 0 has incompatible type "None"; expected "int" + a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int" [builtins fixtures/list.pyi] [case testUnionTypeContext] @@ -847,7 +853,7 @@ g(f(a)) [case testStar2Context] from typing import Any, Dict, Tuple, Iterable -def f1(iterable: Iterable[Tuple[str, Any]] = None) -> None: +def f1(iterable: Iterable[Tuple[str, Any]] = ()) -> None: f2(**dict(iterable)) def f2(iterable: Iterable[Tuple[str, Any]], **kw: Any) -> None: pass @@ -913,7 +919,7 @@ T = TypeVar('T') def f(x: Union[T, List[int]]) -> Union[T, List[int]]: pass reveal_type(f(1)) # N: Revealed type is "Union[builtins.int, builtins.list[builtins.int]]" reveal_type(f([])) # N: Revealed type is "builtins.list[builtins.int]" -reveal_type(f(None)) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(f(None)) # N: Revealed type is "Union[None, builtins.list[builtins.int]]" [builtins fixtures/list.pyi] [case testUnionWithGenericTypeItemContextAndStrictOptional] @@ -941,7 +947,7 @@ c = C[List[int]]() reveal_type(c.f('')) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.str]" reveal_type(c.f([1])) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(c.f([])) # N: Revealed type is "builtins.list[builtins.int]" -reveal_type(c.f(None)) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(c.f(None)) # N: Revealed type is "Union[builtins.list[builtins.int], None]" [builtins fixtures/list.pyi] [case testGenericMethodCalledInGenericContext] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 166e173e7301..ee13cb3830fc 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -54,7 +54,7 @@ class B: pass [case testInferringLvarTypeFromGvar] -g = None # type: B +g: B def f() -> None: a = g @@ -78,7 +78,7 @@ def g(): pass [case testInferringExplicitDynamicTypeForLvar] from typing import Any -g = None # type: Any +g: Any def f(a: Any) -> None: b = g @@ -95,8 +95,8 @@ def f(a: Any) -> None: def f() -> None: a = A(), B() - aa = None # type: A - bb = None # type: B + aa: A + bb: B if int(): bb = a[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") aa = a[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -122,8 +122,8 @@ class A: pass from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): pass -a_i = None # type: A[int] -a_s = None # type: A[str] +a_i: A[int] +a_s: A[str] def f() -> None: a_int = A() # type: A[int] @@ -182,7 +182,7 @@ class B: pass [case testInferringLvarTypesInTupleAssignment] from typing import Tuple def f() -> None: - t = None # type: Tuple[A, B] + t: Tuple[A, B] a, b = t if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -200,7 +200,7 @@ class B: pass [case testInferringLvarTypesInNestedTupleAssignment1] from typing import Tuple def f() -> None: - t = None # type: Tuple[A, B] + t: Tuple[A, B] a1, (a, b) = A(), t if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -386,7 +386,7 @@ reveal_type(C) # N: Revealed type is "__main__.Foo" [case testInferringLvarTypesInMultiDefWithInvalidTuple] from typing import Tuple -t = None # type: Tuple[object, object, object] +t: Tuple[object, object, object] def f() -> None: a, b = t # Fail @@ -543,9 +543,9 @@ if int(): [case testInferSimpleGenericFunction] from typing import Tuple, TypeVar T = TypeVar('T') -a = None # type: A -b = None # type: B -c = None # type: Tuple[A, object] +a: A +b: B +c: Tuple[A, object] def id(a: T) -> T: pass @@ -569,8 +569,8 @@ from typing import TypeVar T = TypeVar('T') def f() -> None: a = id - b = None # type: int - c = None # type: str + b: int + c: str if int(): b = a(c) # E: Incompatible types in assignment (expression has type "str", variable has type "int") b = a(b) @@ -584,7 +584,7 @@ def id(x: T) -> T: from typing import TypeVar T = TypeVar('T') class A: pass -a = None # type: A +a: A def ff() -> None: x = f() # E: Need type annotation for "x" @@ -605,8 +605,8 @@ class A: pass class B(A): pass T = TypeVar('T') -a = None # type: A -b = None # type: B +a: A +b: B def f(a: T, b: T) -> T: pass @@ -629,10 +629,11 @@ def f(a: T, b: S) -> Tuple[T, S]: pass class A: pass class B: pass -a, b = None, None # type: (A, B) -taa = None # type: Tuple[A, A] -tab = None # type: Tuple[A, B] -tba = None # type: Tuple[B, A] +a: A +b: B +taa: Tuple[A, A] +tab: Tuple[A, B] +tba: Tuple[B, A] if int(): taa = f(a, b) # E: Argument 2 to "f" has incompatible type "B"; expected "A" @@ -651,9 +652,9 @@ if int(): [case testConstraintSolvingWithSimpleGenerics] from typing import TypeVar, Generic T = TypeVar('T') -ao = None # type: A[object] -ab = None # type: A[B] -ac = None # type: A[C] +ao: A[object] +ab: A[B] +ac: A[C] def f(a: 'A[T]') -> 'A[T]': pass @@ -683,8 +684,8 @@ if int(): [case testConstraintSolvingFailureWithSimpleGenerics] from typing import TypeVar, Generic T = TypeVar('T') -ao = None # type: A[object] -ab = None # type: A[B] +ao: A[object] +ab: A[B] def f(a: 'A[T]', b: 'A[T]') -> None: pass @@ -696,7 +697,9 @@ f(ao, ab) # E: Cannot infer type argument 1 of "f" f(ab, ao) # E: Cannot infer type argument 1 of "f" f(ao, ao) f(ab, ab) + [case testTypeInferenceWithCalleeDefaultArgs] +# flags: --no-strict-optional from typing import TypeVar T = TypeVar('T') a = None # type: A @@ -809,7 +812,9 @@ class C(A, I, J): pass def f(a: T, b: T) -> T: pass T = TypeVar('T') -a, i, j = None, None, None # type: (A, I, J) +a: A +i: I +j: J a = f(B(), C()) @@ -846,7 +851,7 @@ from typing import Callable, Type, TypeVar T = TypeVar('T') def f(x: Callable[..., T]) -> T: return x() class A: pass -x = None # type: Type[A] +x: Type[A] y = f(x) reveal_type(y) # N: Revealed type is "__main__.A" @@ -1031,7 +1036,8 @@ class A: pass class B: pass def d_ab() -> Dict[A, B]: return {} def d_aa() -> Dict[A, A]: return {} -a, b = None, None # type: (A, B) +a: A +b: B d = {a:b} if int(): d = d_ab() @@ -1041,7 +1047,8 @@ if int(): [case testSetLiteral] from typing import Any, Set -a, x = None, None # type: (int, Any) +a: int +x: Any def s_i() -> Set[int]: return set() def s_s() -> Set[str]: return set() s = {a} @@ -1113,7 +1120,8 @@ list_2 = [f, h] [case testInferenceOfFor1] -a, b = None, None # type: (A, B) +a: A +b: B class A: pass class B: pass @@ -1132,7 +1140,9 @@ class A: pass class B: pass class C: pass -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C for x, (y, z) in [(A(), (B(), C()))]: b = x # E: Incompatible types in assignment (expression has type "A", variable has type "B") c = y # E: Incompatible types in assignment (expression has type "B", variable has type "C") @@ -1152,7 +1162,8 @@ for xxx, yyy in [(None, None)]: class A: pass class B: pass -a, b = None, None # type: (A, B) +a: A +b: B for x, y in [[A()]]: b = x # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -1234,7 +1245,7 @@ class B: pass [case testMultipleAssignmentWithPartialDefinition] -a = None # type: A +a: A if int(): x, a = a, a if int(): @@ -1246,7 +1257,7 @@ if int(): class A: pass [case testMultipleAssignmentWithPartialDefinition2] -a = None # type: A +a: A if int(): a, x = [a, a] if int(): @@ -1260,7 +1271,7 @@ class A: pass [case testMultipleAssignmentWithPartialDefinition3] from typing import Any, cast -a = None # type: A +a: A if int(): x, a = cast(Any, a) if int(): @@ -1275,7 +1286,7 @@ class A: pass class A: pass class B: pass -if A: +if int(): a = A() if int(): a = A() @@ -1380,6 +1391,7 @@ main:4: error: Cannot infer type argument 1 of "f" main:4: error: Argument 2 to "f" has incompatible type "int"; expected "str" [case testInferLambdaNone] +# flags: --no-strict-optional from typing import Callable def f(x: Callable[[], None]) -> None: pass def g(x: Callable[[], int]) -> None: pass @@ -1442,8 +1454,8 @@ def g(cond: bool) -> Any: [case testOrOperationWithGenericOperands] from typing import List -a = None # type: List[A] -o = None # type: List[object] +a: List[A] +o: List[object] a2 = a or [] if int(): a = a2 @@ -1464,7 +1476,7 @@ x.y # E: "object" has no attribute "y" [case testAccessDataAttributeBeforeItsTypeIsAvailable] -a = None # type: A +a: A a.x.y # E: Cannot determine type of "x" class A: def __init__(self) -> None: @@ -1481,7 +1493,7 @@ from typing import List, _promote class A: pass @_promote(A) class B: pass -a = None # type: List[A] +a: List[A] x1 = [A(), B()] x2 = [B(), A()] x3 = [B(), B()] @@ -1504,7 +1516,7 @@ class A: pass class B: pass @_promote(B) class C: pass -a = None # type: List[A] +a: List[A] x1 = [A(), C()] x2 = [C(), A()] x3 = [B(), C()] @@ -1915,6 +1927,7 @@ reveal_type(dd) # N: Revealed type is "builtins.dict[builtins.str, builtins.int [builtins fixtures/dict.pyi] [case testInferFromEmptyDictWhenUsingInSpecialCase] +# flags: --no-strict-optional d = None if 'x' in d: # E: "None" has no attribute "__iter__" (not iterable) pass @@ -1937,6 +1950,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] [case testInferSetTypeFromInplaceOr] +# flags: --no-strict-optional a = set() a |= {'x'} reveal_type(a) # N: Revealed type is "builtins.set[builtins.str]" @@ -1953,7 +1967,8 @@ def f() -> None: x = None else: x = 1 - x() # E: "int" not callable + x() # E: "int" not callable \ + # E: "None" not callable [out] [case testLocalVariablePartiallyTwiceInitializedToNone] @@ -1964,7 +1979,8 @@ def f() -> None: x = None else: x = 1 - x() # E: "int" not callable + x() # E: "int" not callable \ + # E: "None" not callable [out] [case testLvarInitializedToNoneWithoutType] @@ -1978,7 +1994,8 @@ def f() -> None: x = None if object(): x = 1 -x() # E: "int" not callable +x() # E: "int" not callable \ + # E: "None" not callable [case testPartiallyInitializedToNoneAndThenToPartialList] x = None @@ -2339,8 +2356,8 @@ reveal_type(A.g) # N: Revealed type is "def (x: builtins.str)" [case testUnificationRedundantUnion] from typing import Union -a = None # type: Union[int, str] -b = None # type: Union[str, tuple] +a: Union[int, str] +b: Union[str, tuple] def f(): pass def g(x: Union[int, str]): pass c = a if f() else b diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 2d010b8ba38d..3403e726d8b5 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -10,7 +10,7 @@ y = x [case testJoinAny] from typing import List, Any -x = None # type: List[Any] +x: List[Any] def foo() -> List[int]: pass def bar() -> List[str]: pass @@ -51,9 +51,9 @@ def f(x: Union[int, str, List]) -> None: [case testClassAttributeInitialization] class A: - x = None # type: int + x: int def __init__(self) -> None: - self.y = None # type: int + self.y: int z = self.x w = self.y @@ -71,7 +71,7 @@ def foo(x: Union[str, int]): y + [1] # E: List item 0 has incompatible type "int"; expected "str" z = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") -x = None # type: int +x: int y = [x] [builtins fixtures/isinstancelist.pyi] @@ -124,7 +124,7 @@ x = 'a' [case testUnionMultiAssignment] from typing import Union -x = None # type: Union[int, str] +x: Union[int, str] if int(): x = 1 x = 'a' @@ -488,7 +488,7 @@ x.y # OK: x is known to be a B [case testIsInstanceBasic] from typing import Union -x = None # type: Union[int, str] +x: Union[int, str] if isinstance(x, str): x = x + 1 # E: Unsupported operand types for + ("str" and "int") x = x + 'a' @@ -499,7 +499,7 @@ else: [case testIsInstanceIndexing] from typing import Union -x = None # type: Union[int, str] +x: Union[int, str] j = [x] if isinstance(j[0], str): j[0] = j[0] + 'a' @@ -671,7 +671,7 @@ foo() from typing import Union def foo() -> None: - x = None # type: Union[int, str] + x: Union[int, str] if isinstance(x, int): for z in [1,2]: break @@ -686,7 +686,7 @@ foo() [case testIsInstanceThreeUnion] from typing import Union, List -x = None # type: Union[int, str, List[int]] +x: Union[int, str, List[int]] while bool(): if isinstance(x, int): @@ -706,7 +706,7 @@ x + [1] # E: Unsupported operand types for + ("int" and "List[int] [case testIsInstanceThreeUnion2] from typing import Union, List -x = None # type: Union[int, str, List[int]] +x: Union[int, str, List[int]] while bool(): if isinstance(x, int): x + 1 @@ -725,7 +725,7 @@ x + [1] # E: Unsupported operand types for + ("int" and "List[int] from typing import Union, List while bool(): - x = None # type: Union[int, str, List[int]] + x: Union[int, str, List[int]] def f(): x # Prevent redefinition x = 1 if isinstance(x, int): @@ -1787,6 +1787,7 @@ issubclass(x, (int, Iterable[int])) # E: Parameterized generics cannot be used [typing fixtures/typing-full.pyi] [case testIssubclassWithMetaclasses] +# flags: --no-strict-optional class FooMetaclass(type): ... class Foo(metaclass=FooMetaclass): ... class Bar: ... diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 81fdc444aced..b3ee47aa6fdf 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -54,6 +54,7 @@ f(b=[], a=A()) [builtins fixtures/list.pyi] [case testGivingArgumentAsPositionalAndKeywordArg] +# flags: --no-strict-optional import typing class A: pass class B: pass @@ -61,11 +62,13 @@ def f(a: 'A', b: 'B' = None) -> None: pass f(A(), a=A()) # E: "f" gets multiple values for keyword argument "a" [case testGivingArgumentAsPositionalAndKeywordArg2] +# flags: --no-strict-optional import typing class A: pass class B: pass def f(a: 'A' = None, b: 'B' = None) -> None: pass f(A(), a=A()) # E: "f" gets multiple values for keyword argument "a" + [case testPositionalAndKeywordForSameArg] # This used to crash in check_argument_count(). See #1095. def f(a: int): pass @@ -134,7 +137,7 @@ f(otter=A()) # E: Missing positional argument "other" in call to "f" [case testKeywordArgumentsWithDynamicallyTypedCallable] from typing import Any -f = None # type: Any +f: Any f(x=f(), z=None()) # E: "None" not callable f(f, zz=None()) # E: "None" not callable f(x=None) @@ -143,10 +146,12 @@ f(x=None) from typing import Callable class A: pass class B: pass -f = None # type: Callable[[A, B], None] +f: Callable[[A, B], None] f(a=A(), b=B()) # E: Unexpected keyword argument "a" # E: Unexpected keyword argument "b" f(A(), b=B()) # E: Unexpected keyword argument "b" + [case testKeywordOnlyArguments] +# flags: --no-strict-optional import typing class A: pass class B: pass @@ -172,7 +177,9 @@ i(A(), b=B()) i(A(), aa=A()) # E: Missing named argument "b" for "i" i(A(), b=B(), aa=A()) i(A(), aa=A(), b=B()) + [case testKeywordOnlyArgumentsFastparse] +# flags: --no-strict-optional import typing class A: pass @@ -290,10 +297,10 @@ from typing import Dict class A: pass class B: pass def f( **kwargs: 'A') -> None: pass -d = None # type: Dict[str, A] +d: Dict[str, A] f(**d) f(x=A(), **d) -d2 = None # type: Dict[str, B] +d2: Dict[str, B] f(**d2) # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A" f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type "**Dict[str, B]"; expected "A" f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A" @@ -324,9 +331,9 @@ reveal_type(formatter.__call__) # N: Revealed type is "def (message: builtins.s [case testPassingMappingForKeywordVarArg] from typing import Mapping def f(**kwargs: 'A') -> None: pass -b = None # type: Mapping -d = None # type: Mapping[A, A] -m = None # type: Mapping[str, A] +b: Mapping +d: Mapping[A, A] +m: Mapping[str, A] f(**d) # E: Keywords must be strings f(**m) f(**b) @@ -337,7 +344,7 @@ class A: pass from typing import Mapping class MappingSubclass(Mapping[str, str]): pass def f(**kwargs: 'A') -> None: pass -d = None # type: MappingSubclass +d: MappingSubclass f(**d) class A: pass [builtins fixtures/dict.pyi] @@ -357,9 +364,9 @@ f(**kwargs) # E: Argument after ** must be a mapping, not "Optional[Any]" [case testPassingKeywordVarArgsToNonVarArgsFunction] from typing import Any, Dict def f(a: 'A', b: 'B') -> None: pass -d = None # type: Dict[str, Any] +d: Dict[str, Any] f(**d) -d2 = None # type: Dict[str, A] +d2: Dict[str, A] f(**d2) # E: Argument 1 to "f" has incompatible type "**Dict[str, A]"; expected "B" class A: pass class B: pass @@ -368,8 +375,8 @@ class B: pass [case testBothKindsOfVarArgs] from typing import Any, List, Dict def f(a: 'A', b: 'A') -> None: pass -l = None # type: List[Any] -d = None # type: Dict[Any, Any] +l: List[Any] +d: Dict[Any, Any] f(*l, **d) class A: pass [builtins fixtures/dict.pyi] @@ -380,8 +387,8 @@ def f1(a: 'A', b: 'A') -> None: pass def f2(a: 'A') -> None: pass def f3(a: 'A', **kwargs: 'A') -> None: pass def f4(**kwargs: 'A') -> None: pass -d = None # type: Dict[Any, Any] -d2 = None # type: Dict[Any, Any] +d: Dict[Any, Any] +d2: Dict[Any, Any] f1(**d, **d2) f2(**d, **d2) f3(**d, **d2) @@ -392,7 +399,7 @@ class A: pass [case testPassingKeywordVarArgsToVarArgsOnlyFunction] from typing import Any, Dict def f(*args: 'A') -> None: pass -d = None # type: Dict[Any, Any] +d: Dict[Any, Any] f(**d) class A: pass [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 1a529051259f..104dd4d144e0 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -753,16 +753,16 @@ Foo = Literal[5] [case testLiteralBiasTowardsAssumingForwardReferencesForTypeComments] from typing_extensions import Literal -a = None # type: Foo +a: Foo reveal_type(a) # N: Revealed type is "__main__.Foo" -b = None # type: "Foo" +b: "Foo" reveal_type(b) # N: Revealed type is "__main__.Foo" -c = None # type: Literal["Foo"] +c: Literal["Foo"] reveal_type(c) # N: Revealed type is "Literal['Foo']" -d = None # type: Literal[Foo] # E: Parameter 1 of Literal[...] is invalid +d: Literal[Foo] # E: Parameter 1 of Literal[...] is invalid class Foo: pass [builtins fixtures/tuple.pyi] @@ -1760,7 +1760,7 @@ reveal_type(func1(identity(b))) # N: Revealed type is "builtins.int" -- [case testLiteralMeets] -from typing import TypeVar, List, Callable, Union +from typing import TypeVar, List, Callable, Union, Optional from typing_extensions import Literal a: Callable[[Literal[1]], int] @@ -1794,12 +1794,14 @@ def f2(x: Literal[1], y: Literal[2]) -> None: pass def f3(x: Literal[1], y: int) -> None: pass def f4(x: Literal[1], y: object) -> None: pass def f5(x: Literal[1], y: Union[Literal[1], Literal[2]]) -> None: pass +def f6(x: Optional[Literal[1]], y: Optional[Literal[2]]) -> None: pass reveal_type(unify(f1)) # N: Revealed type is "Literal[1]" -reveal_type(unify(f2)) # N: Revealed type is "None" +reveal_type(unify(f2)) # N: Revealed type is "" reveal_type(unify(f3)) # N: Revealed type is "Literal[1]" reveal_type(unify(f4)) # N: Revealed type is "Literal[1]" reveal_type(unify(f5)) # N: Revealed type is "Literal[1]" +reveal_type(unify(f6)) # N: Revealed type is "None" [builtins fixtures/list.pyi] [out] @@ -2013,7 +2015,7 @@ optional_keys: Literal["d", "e"] bad_keys: Literal["a", "bad"] reveal_type(test[good_keys]) # N: Revealed type is "Union[__main__.A, __main__.B]" -reveal_type(test.get(good_keys)) # N: Revealed type is "Union[__main__.A, __main__.B]" +reveal_type(test.get(good_keys)) # N: Revealed type is "Union[__main__.A, __main__.B, None]" reveal_type(test.get(good_keys, 3)) # N: Revealed type is "Union[__main__.A, Literal[3]?, __main__.B]" reveal_type(test.pop(optional_keys)) # N: Revealed type is "Union[__main__.D, __main__.E]" reveal_type(test.pop(optional_keys, 3)) # N: Revealed type is "Union[__main__.D, __main__.E, Literal[3]?]" @@ -2066,7 +2068,7 @@ x[bad_keys] # E: TypedDict "D1" has no key "d" \ # E: TypedDict "D2" has no key "a" reveal_type(x[good_keys]) # N: Revealed type is "Union[__main__.B, __main__.C]" -reveal_type(x.get(good_keys)) # N: Revealed type is "Union[__main__.B, __main__.C]" +reveal_type(x.get(good_keys)) # N: Revealed type is "Union[__main__.B, __main__.C, None]" reveal_type(x.get(good_keys, 3)) # N: Revealed type is "Union[__main__.B, Literal[3]?, __main__.C]" reveal_type(x.get(bad_keys)) # N: Revealed type is "builtins.object" reveal_type(x.get(bad_keys, 3)) # N: Revealed type is "builtins.object" From ef0b763fb25790892e208cd2691d272494ea7720 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:46:25 +0200 Subject: [PATCH 0117/1617] Enable strict optional for more test files (8) (#15606) Followup to https://github.com/python/mypy/pull/15586 --- mypy/test/testcheck.py | 5 - test-data/unit/check-namedtuple.test | 36 ++--- test-data/unit/check-overloading.test | 35 +++-- test-data/unit/check-plugin-attrs.test | 3 + test-data/unit/check-statements.test | 102 +++++++------- test-data/unit/check-tuples.test | 184 ++++++++++++++----------- 6 files changed, 202 insertions(+), 163 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index c91bd915096f..a77da090f740 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -52,11 +52,6 @@ # TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated no_strict_optional_files = { "check-modules.test", - "check-namedtuple.test", - "check-overloading.test", - "check-plugin-attrs.test", - "check-statements.test", - "check-tuples.test", "check-unions.test", "check-varargs.test", } diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 5eed70246e8c..83cc8c099deb 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -2,7 +2,7 @@ from collections import namedtuple X = namedtuple('X', 'x y') -x = None # type: X +x: X a, b = x b = x[0] a = x[1] @@ -14,7 +14,7 @@ x[2] # E: Tuple index out of range from collections import namedtuple X = namedtuple('X', ('x', 'y')) -x = None # type: X +x: X a, b = x b = x[0] a = x[1] @@ -32,7 +32,7 @@ X = namedtuple('X', 'x, _y, _z') # E: "namedtuple()" field names cannot start w from collections import namedtuple X = namedtuple('X', 'x y') -x = None # type: X +x: X x.x x.y x.z # E: "X" has no attribute "z" @@ -63,13 +63,13 @@ class A(NamedTuple): from collections import namedtuple X = namedtuple('X', 'x y') -x = None # type: X +x: X x.x = 5 # E: Property "x" defined in "X" is read-only x.y = 5 # E: Property "y" defined in "X" is read-only x.z = 5 # E: "X" has no attribute "z" class A(X): pass -a = None # type: A +a: A a.x = 5 # E: Property "x" defined in "X" is read-only a.y = 5 # E: Property "y" defined in "X" is read-only -- a.z = 5 # not supported yet @@ -292,7 +292,7 @@ A = NamedTuple('A', [('a', int), ('b', str)]) class B(A): pass a = A(1, '') b = B(1, '') -t = None # type: Tuple[int, str] +t: Tuple[int, str] if int(): b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): @@ -357,7 +357,7 @@ C(2).b from collections import namedtuple X = namedtuple('X', ['x', 'y']) -x = None # type: X +x: X reveal_type(x._asdict()) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] @@ -366,7 +366,7 @@ reveal_type(x._asdict()) # N: Revealed type is "builtins.dict[builtins.str, Any from collections import namedtuple X = namedtuple('X', ['x', 'y']) -x = None # type: X +x: X reveal_type(x._replace()) # N: Revealed type is "Tuple[Any, Any, fallback=__main__.X]" x._replace(y=5) x._replace(x=3) @@ -391,7 +391,7 @@ X._replace(x=1, y=2) # E: Missing positional argument "_self" in call to "_repl from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) -x = None # type: X +x: X reveal_type(x._replace()) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]" x._replace(x=5) x._replace(y=5) # E: Argument "y" to "_replace" of "X" has incompatible type "int"; expected "str" @@ -405,7 +405,7 @@ reveal_type(X._make([5, 'a'])) # N: Revealed type is "Tuple[builtins.int, built X._make('a b') # E: Argument 1 to "_make" of "X" has incompatible type "str"; expected "Iterable[Any]" -- # FIX: not a proper class method --- x = None # type: X +-- x: X -- reveal_type(x._make([5, 'a'])) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]" -- x._make('a b') # E: Argument 1 to "_make" of "X" has incompatible type "str"; expected Iterable[Any] @@ -423,7 +423,7 @@ from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) reveal_type(X._source) # N: Revealed type is "builtins.str" -x = None # type: X +x: X reveal_type(x._source) # N: Revealed type is "builtins.str" [builtins fixtures/tuple.pyi] @@ -459,7 +459,7 @@ from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) reveal_type(X._field_types) # N: Revealed type is "builtins.dict[builtins.str, Any]" -x = None # type: X +x: X reveal_type(x._field_types) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] @@ -472,7 +472,7 @@ def f(x: A) -> None: pass class B(NamedTuple('B', []), A): pass f(B()) -x = None # type: A +x: A if int(): x = B() @@ -482,7 +482,7 @@ def g(x: C) -> None: pass class D(NamedTuple('D', []), A): pass g(D()) # E: Argument 1 to "g" has incompatible type "D"; expected "C" -y = None # type: C +y: C if int(): y = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") [builtins fixtures/tuple.pyi] @@ -499,9 +499,9 @@ class A(NamedTuple('A', [('x', str)])): class B(A): pass -a = None # type: A +a: A a = A('').member() -b = None # type: B +b: B b = B('').member() a = B('') a = B('').member() @@ -511,14 +511,14 @@ a = B('').member() from typing import NamedTuple, TypeVar A = NamedTuple('A', [('x', str)]) reveal_type(A('hello')._replace(x='')) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.A]" -a = None # type: A +a: A a = A('hello')._replace(x='') class B(A): pass reveal_type(B('hello')._replace(x='')) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.B]" -b = None # type: B +b: B b = B('hello')._replace(x='') [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 96fbaa77514e..4851cc96e6da 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -450,7 +450,8 @@ class C: pass from foo import * [file foo.pyi] from typing import overload -a, b = None, None # type: (A, B) +a: A +b: B if int(): b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): @@ -492,7 +493,8 @@ class C: pass from foo import * [file foo.pyi] from typing import overload -a, b = None, None # type: (A, B) +a: A +b: B if int(): b = a.f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): @@ -514,7 +516,8 @@ class B: pass from foo import * [file foo.pyi] from typing import overload -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = f(a) if int(): @@ -549,7 +552,10 @@ from foo import * [file foo.pyi] from typing import overload, TypeVar, Generic t = TypeVar('t') -ab, ac, b, c = None, None, None, None # type: (A[B], A[C], B, C) +ab: A[B] +ac: A[C] +b: B +c: C if int(): b = f(ab) c = f(ac) @@ -569,7 +575,8 @@ class C: pass from foo import * [file foo.pyi] from typing import overload -a, b = None, None # type: (A, B) +a: A +b: B a = A(a) a = A(b) a = A(object()) # E: No overload variant of "A" matches argument type "object" \ @@ -589,8 +596,8 @@ class B: pass from foo import * [file foo.pyi] from typing import overload, Callable -o = None # type: object -a = None # type: A +o: object +a: A if int(): a = f # E: Incompatible types in assignment (expression has type overloaded function, variable has type "A") @@ -607,7 +614,8 @@ class A: pass from foo import * [file foo.pyi] from typing import overload -t, a = None, None # type: (type, A) +t: type +a: A if int(): a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") @@ -625,7 +633,8 @@ class B: pass from foo import * [file foo.pyi] from typing import overload -a, b = None, None # type: int, str +a: int +b: str if int(): a = A()[a] if int(): @@ -647,7 +656,9 @@ from foo import * [file foo.pyi] from typing import TypeVar, Generic, overload t = TypeVar('t') -a, b, c = None, None, None # type: (A, B, C[A]) +a: A +b: B +c: C[A] if int(): a = c[a] b = c[a] # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -761,7 +772,8 @@ from typing import overload def f(t: type) -> 'A': pass @overload def f(t: 'A') -> 'B': pass -a, b = None, None # type: (A, B) +a: A +b: B if int(): a = f(A) if int(): @@ -1215,6 +1227,7 @@ f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "Tuple[in [builtins fixtures/tuple.pyi] [case testPreferExactSignatureMatchInOverload] +# flags: --no-strict-optional from foo import * [file foo.pyi] from typing import overload, List diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 5b8c361906a8..edae6c096015 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -672,6 +672,7 @@ class A(Generic[T]): [builtins fixtures/classmethod.pyi] [case testAttrsForwardReference] +# flags: --no-strict-optional import attr @attr.s(auto_attribs=True) class A: @@ -687,6 +688,7 @@ A(B(None)) [builtins fixtures/list.pyi] [case testAttrsForwardReferenceInClass] +# flags: --no-strict-optional import attr @attr.s(auto_attribs=True) class A: @@ -1593,6 +1595,7 @@ def f(t: TA) -> None: [builtins fixtures/plugin_attrs.pyi] [case testNonattrsFields] +# flags: --no-strict-optional from typing import Any, cast, Type from attrs import fields diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 29e2c5ba3266..8a232d52968f 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -95,10 +95,10 @@ def f() -> Iterator[int]: [case testIfStatement] -a = None # type: A -a2 = None # type: A -a3 = None # type: A -b = None # type: bool +a: A +a2: A +a3: A +b: bool if a: a = b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") elif a2: @@ -124,8 +124,8 @@ class A: pass [case testWhileStatement] -a = None # type: A -b = None # type: bool +a: A +b: bool while a: a = b # Fail else: @@ -142,13 +142,14 @@ main:7: error: Incompatible types in assignment (expression has type "bool", var [case testForStatement] class A: pass -a = None # type: A -b = None # type: object +a: A +b: object for a in [A()]: a = b # E: Incompatible types in assignment (expression has type "object", variable has type "A") else: a = b # E: Incompatible types in assignment (expression has type "object", variable has type "A") [builtins fixtures/list.pyi] + [case testBreakStatement] import typing while None: @@ -205,8 +206,9 @@ for a, b in x: # type: int, int, int # E: Incompatible number of tuple items [case testPlusAssign] - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a += b # Fail b += a # Fail c += a # Fail @@ -221,13 +223,14 @@ class B: class C: pass [builtins fixtures/tuple.pyi] [out] -main:3: error: Unsupported operand types for + ("A" and "B") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "B") -main:5: error: Unsupported left operand type for + ("C") +main:4: error: Unsupported operand types for + ("A" and "B") +main:5: error: Incompatible types in assignment (expression has type "C", variable has type "B") +main:6: error: Unsupported left operand type for + ("C") [case testMinusAssign] - -a, b, c = None, None, None # type: (A, B, C) +a: A +b: B +c: C a -= b # Fail b -= a # Fail c -= a # Fail @@ -242,13 +245,13 @@ class B: class C: pass [builtins fixtures/tuple.pyi] [out] -main:3: error: Unsupported operand types for - ("A" and "B") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "B") -main:5: error: Unsupported left operand type for - ("C") +main:4: error: Unsupported operand types for - ("A" and "B") +main:5: error: Incompatible types in assignment (expression has type "C", variable has type "B") +main:6: error: Unsupported left operand type for - ("C") [case testMulAssign] - -a, c = None, None # type: (A, C) +a: A +c: C a *= a # Fail c *= a # Fail a *= c @@ -263,7 +266,8 @@ main:3: error: Unsupported operand types for * ("A" and "A") main:4: error: Unsupported left operand type for * ("C") [case testMatMulAssign] -a, c = None, None # type: (A, C) +a: A +c: C a @= a # E: Unsupported operand types for @ ("A" and "A") c @= a # E: Unsupported left operand type for @ ("C") a @= c @@ -275,8 +279,8 @@ class C: pass [builtins fixtures/tuple.pyi] [case testDivAssign] - -a, c = None, None # type: (A, C) +a: A +c: C a /= a # Fail c /= a # Fail a /= c @@ -291,8 +295,8 @@ main:3: error: Unsupported operand types for / ("A" and "A") main:4: error: Unsupported left operand type for / ("C") [case testPowAssign] - -a, c = None, None # type: (A, C) +a: A +c: C a **= a # Fail c **= a # Fail a **= c @@ -307,8 +311,8 @@ main:3: error: Unsupported operand types for ** ("A" and "A") main:4: error: Unsupported left operand type for ** ("C") [case testSubtypesInOperatorAssignment] - -a, b = None, None # type: (A, B) +a: A +b: B b += b b += a a += b @@ -321,8 +325,8 @@ class B(A): pass [out] [case testAdditionalOperatorsInOpAssign] - -a, c = None, None # type: (A, C) +a: A +c: C a &= a # Fail a >>= a # Fail a //= a # Fail @@ -389,9 +393,9 @@ main:2: error: Unsupported left operand type for + ("None") [case testRaiseStatement] -e = None # type: BaseException -f = None # type: MyError -a = None # type: A +e: BaseException +f: MyError +a: A raise a # Fail raise e raise f @@ -441,7 +445,7 @@ raise MyKwError # E: Missing named argument "kwonly" for "MyKwError" [case testRaiseExceptionType] import typing -x = None # type: typing.Type[BaseException] +x: typing.Type[BaseException] raise x [builtins fixtures/exception.pyi] @@ -453,21 +457,21 @@ raise x # E: Exception must be derived from BaseException [case testRaiseUnion] import typing -x = None # type: typing.Union[BaseException, typing.Type[BaseException]] +x: typing.Union[BaseException, typing.Type[BaseException]] raise x [builtins fixtures/exception.pyi] [case testRaiseNonExceptionUnionFails] import typing -x = None # type: typing.Union[BaseException, int] +x: typing.Union[BaseException, int] raise x # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] [case testRaiseFromStatement] -e = None # type: BaseException -f = None # type: MyError -a = None # type: A -x = None # type: BaseException +e: BaseException +f: MyError +a: A +x: BaseException del x raise e from a # E: Exception must be derived from BaseException raise e from e @@ -505,27 +509,30 @@ main:5: error: Incompatible types in assignment (expression has type "object", v try: pass except BaseException as e: - a, o = None, None # type: (BaseException, object) + a: BaseException + o: object e = a e = o # Fail class A: pass class B: pass [builtins fixtures/exception.pyi] [out] -main:7: error: Incompatible types in assignment (expression has type "object", variable has type "BaseException") +main:8: error: Incompatible types in assignment (expression has type "object", variable has type "BaseException") [case testTypeErrorInBlock] class A: pass class B: pass -while object: - x = None # type: A +while int(): + x: A if int(): x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + [case testTypeErrorInvolvingBaseException] class A: pass -x, a = None, None # type: (BaseException, A) +x: BaseException +a: A if int(): a = BaseException() # E: Incompatible types in assignment (expression has type "BaseException", variable has type "A") if int(): @@ -1042,7 +1049,8 @@ main:12: error: Exception type must be derived from BaseException (or be a tuple [case testDelStmtWithIndex] -a, b = None, None # type: (A, B) +a: A +b: B del b[a] del b[b] # E: Argument 1 to "__delitem__" of "B" has incompatible type "B"; expected "A" del a[a] # E: "A" has no attribute "__delitem__" @@ -1965,6 +1973,7 @@ def f() -> None: [out] [case testChainedAssignmentWithType] +# flags: --no-strict-optional x = y = None # type: int if int(): x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -1982,7 +1991,8 @@ if int(): [case testAssignListToStarExpr] from typing import List -bs, cs = None, None # type: List[A], List[B] +bs: List[A] +cs: List[B] if int(): *bs, b = bs if int(): diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index f8d6573a56fe..5cb89a6854be 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -4,11 +4,11 @@ [case testTupleAssignmentWithTupleTypes] from typing import Tuple -t1 = None # type: Tuple[A] -t2 = None # type: Tuple[B] -t3 = None # type: Tuple[A, A] -t4 = None # type: Tuple[A, B] -t5 = None # type: Tuple[B, A] +t1: Tuple[A] +t2: Tuple[B] +t3: Tuple[A, A] +t4: Tuple[A, B] +t5: Tuple[B, A] if int(): t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[B]", variable has type "Tuple[A]") @@ -39,9 +39,9 @@ class B: pass [case testTupleSubtyping] from typing import Tuple -t1 = None # type: Tuple[A, A] -t2 = None # type: Tuple[A, B] -t3 = None # type: Tuple[B, A] +t1: Tuple[A, A] +t2: Tuple[A, B] +t3: Tuple[B, A] if int(): t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") @@ -57,6 +57,7 @@ class B(A): pass [builtins fixtures/tuple.pyi] [case testTupleCompatibilityWithOtherTypes] +# flags: --no-strict-optional from typing import Tuple a, o = None, None # type: (A, object) t = None # type: Tuple[A, A] @@ -80,8 +81,8 @@ class A: pass [case testNestedTupleTypes] from typing import Tuple -t1 = None # type: Tuple[A, Tuple[A, A]] -t2 = None # type: Tuple[B, Tuple[B, B]] +t1: Tuple[A, Tuple[A, A]] +t2: Tuple[B, Tuple[B, B]] if int(): t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") @@ -94,8 +95,8 @@ class B(A): pass [case testNestedTupleTypes2] from typing import Tuple -t1 = None # type: Tuple[A, Tuple[A, A]] -t2 = None # type: Tuple[B, Tuple[B, B]] +t1: Tuple[A, Tuple[A, A]] +t2: Tuple[B, Tuple[B, B]] if int(): t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") @@ -108,8 +109,8 @@ class B(A): pass [case testSubtypingWithNamedTupleType] from typing import Tuple -t1 = None # type: Tuple[A, A] -t2 = None # type: tuple +t1: Tuple[A, A] +t2: tuple if int(): t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]") @@ -120,6 +121,7 @@ class A: pass [builtins fixtures/tuple.pyi] [case testTupleInitializationWithNone] +# flags: --no-strict-optional from typing import Tuple t = None # type: Tuple[A, A] t = None @@ -132,6 +134,7 @@ class A: pass [case testTupleExpressions] +# flags: --no-strict-optional from typing import Tuple t1 = None # type: tuple t2 = None # type: Tuple[A] @@ -177,12 +180,13 @@ def f() -> None: pass [case testIndexingTuples] from typing import Tuple -t1 = None # type: Tuple[A, B] -t2 = None # type: Tuple[A] -t3 = None # type: Tuple[A, B, C, D, E] -a, b = None, None # type: (A, B) -x = None # type: Tuple[A, B, C] -y = None # type: Tuple[A, C, E] +t1: Tuple[A, B] +t2: Tuple[A] +t3: Tuple[A, B, C, D, E] +a: A +b: B +x: Tuple[A, B, C] +y: Tuple[A, C, E] n = 0 if int(): @@ -221,9 +225,10 @@ class E: pass [case testIndexingTuplesWithNegativeIntegers] from typing import Tuple -t1 = None # type: Tuple[A, B] -t2 = None # type: Tuple[A] -a, b = None, None # type: A, B +t1: Tuple[A, B] +t2: Tuple[A] +a: A +b: B if int(): a = t1[-1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -251,7 +256,7 @@ from typing import Tuple class A: pass class B: pass -t = None # type: Tuple[A, B] +t: Tuple[A, B] n = 0 t[0] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]") @@ -265,6 +270,7 @@ t[n] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]") [case testMultipleAssignmentWithTuples] +# flags: --no-strict-optional from typing import Tuple t1 = None # type: Tuple[A, B] t2 = None # type: Tuple[A, B, A] @@ -291,6 +297,7 @@ class B: pass [builtins fixtures/tuple.pyi] [case testMultipleAssignmentWithSquareBracketTuples] +# flags: --no-strict-optional from typing import Tuple def avoid_confusing_test_parser() -> None: @@ -322,8 +329,8 @@ class B: pass [case testMultipleAssignmentWithInvalidNumberOfValues] from typing import Tuple -t1 = None # type: Tuple[A, A, A] -a = None # type: A +t1: Tuple[A, A, A] +a: A a, a = t1 # E: Too many values to unpack (2 expected, 3 provided) a, a, a, a = t1 # E: Need more than 3 values to unpack (4 expected) @@ -334,8 +341,8 @@ class A: pass [builtins fixtures/tuple.pyi] [case testMultipleAssignmentWithTupleExpressionRvalue] - -a, b = None, None # type: (A, B) +a: A +b: B if int(): a, b = a, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -354,7 +361,8 @@ class B: pass [builtins fixtures/tuple.pyi] [case testSubtypingInMultipleAssignment] -a, b = None, None # type: (A, B) +a: A +b: B if int(): b, b = a, b # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -371,7 +379,7 @@ class B(A): pass [builtins fixtures/tuple.pyi] [case testInitializationWithMultipleValues] - +# flags: --no-strict-optional a, b = None, None # type: (A, B) a1, b1 = a, a # type: (A, B) # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -387,8 +395,8 @@ class B: pass [builtins fixtures/tuple.pyi] [case testMultipleAssignmentWithNonTupleRvalue] - -a, b = None, None # type: (A, B) +a: A +b: B def f(): pass a, b = None # E: "None" object is not iterable @@ -400,9 +408,10 @@ class B: pass [builtins fixtures/tuple.pyi] [case testMultipleAssignmentWithIndexedLvalues] - -a, b = None, None # type: (A, B) -aa, bb = None, None # type: (AA, BB) +a: A +b: B +aa: AA +bb: BB a[a], b[b] = a, bb # E: Incompatible types in assignment (expression has type "A", target has type "AA") a[a], b[b] = aa, b # E: Incompatible types in assignment (expression has type "B", target has type "BB") @@ -420,6 +429,7 @@ class BB: pass [builtins fixtures/tuple.pyi] [case testMultipleDeclarationWithParentheses] +# flags: --no-strict-optional (a, b) = (None, None) # type: int, str if int(): a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -430,8 +440,8 @@ if int(): [builtins fixtures/tuple.pyi] [case testMultipleAssignmentWithExtraParentheses] - -a, b = None, None # type: (A, B) +a: A +b: B if int(): (a, b) = (a, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -458,6 +468,7 @@ class B: pass [builtins fixtures/tuple.pyi] [case testMultipleAssignmentUsingSingleTupleType] +# flags: --no-strict-optional from typing import Tuple a, b = None, None # type: Tuple[int, str] if int(): @@ -490,6 +501,7 @@ aa, bb, *cc = t # E: Need type annotation for "cc" (hint: "cc: List[] = . [builtins fixtures/list.pyi] [case testAssignmentToStarAnnotation] +# flags: --no-strict-optional from typing import List li, lo = None, None # type: List[int], List[object] a, b, *c = 1, 2 # type: int, int, List[int] @@ -501,7 +513,7 @@ if int(): [case testAssignmentToStarCount1] from typing import List -ca = None # type: List[int] +ca: List[int] c = [1] if int(): a, b, *c = 1, # E: Need more than 1 value to unpack (2 expected) @@ -515,7 +527,7 @@ if int(): [case testAssignmentToStarCount2] from typing import List -ca = None # type: List[int] +ca: List[int] t1 = 1, t2 = 1, 2 t3 = 1, 2, 3 @@ -541,7 +553,7 @@ c = a c = q [case testAssignmentToComplexStar] from typing import List -li = None # type: List[int] +li: List[int] if int(): a, *(li) = 1, a, *(b, c) = 1, 2 # E: Need more than 1 value to unpack (2 expected) @@ -553,9 +565,9 @@ if int(): [case testAssignmentToStarFromTupleType] from typing import List, Tuple -li = None # type: List[int] -la = None # type: List[A] -ta = None # type: Tuple[A, A, A] +li: List[int] +la: List[A] +ta: Tuple[A, A, A] if int(): a, *la = ta if int(): @@ -573,8 +585,8 @@ class A: pass [case testAssignmentToStarFromTupleInference] from typing import List class A: pass -li = None # type: List[int] -la = None # type: List[A] +li: List[int] +la: List[A] a, *l = A(), A() if int(): l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") @@ -588,8 +600,8 @@ from typing import List class A: pass -li = None # type: List[int] -la = None # type: List[A] +li: List[int] +la: List[A] a, *l = [A(), A()] if int(): l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") @@ -600,9 +612,9 @@ if int(): [case testAssignmentToStarFromTupleTypeInference] from typing import List, Tuple -li = None # type: List[int] -la = None # type: List[A] -ta = None # type: Tuple[A, A, A] +li: List[int] +la: List[A] +ta: Tuple[A, A, A] a, *l = ta if int(): l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") @@ -615,8 +627,8 @@ class A: pass [case testAssignmentToStarFromListTypeInference] from typing import List -li = None # type: List[int] -la = None # type: List[A] +li: List[int] +la: List[A] a, *l = la if int(): l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") @@ -656,9 +668,12 @@ reveal_type(e2) # N: Revealed type is "builtins.list[builtins.int]" [case testNestedTupleAssignment1] - -a1, b1, c1 = None, None, None # type: (A, B, C) -a2, b2, c2 = None, None, None # type: (A, B, C) +a1: A +a2: A +b1: B +b2: B +c1: C +c2: C if int(): a1, (b1, c1) = a2, (b2, c2) @@ -673,9 +688,12 @@ class C: pass [builtins fixtures/tuple.pyi] [case testNestedTupleAssignment2] - -a1, b1, c1 = None, None, None # type: (A, B, C) -a2, b2, c2 = None, None, None # type: (A, B, C) +a1: A +a2: A +b1: B +b2: B +c1: C +c2: C t = a1, b1 if int(): @@ -714,7 +732,7 @@ class A: def __add__(self, x: 'A') -> 'A': pass def f(x: 'A') -> None: pass -a = None # type: A +a: A (a, a) + a # E: Unsupported operand types for + ("Tuple[A, A]" and "A") a + (a, a) # E: Unsupported operand types for + ("A" and "Tuple[A, A]") @@ -724,7 +742,7 @@ f((a, a)) # E: Argument 1 to "f" has incompatible type "Tuple[A, A]"; expected [case testLargeTuplesInErrorMessages] -a = None # type: LongTypeName +a: LongTypeName a + (a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a) # Fail class LongTypeName: @@ -740,7 +758,7 @@ main:3: error: Unsupported operand types for + ("LongTypeName" and "Tuple[LongTy [case testTupleMethods] from typing import Tuple -t = None # type: Tuple[int, str] +t: Tuple[int, str] i = 0 s = '' b = bool() @@ -869,7 +887,7 @@ class A(tuple): pass import m [file m.pyi] from typing import Tuple -a = None # type: A +a: A class A(Tuple[int, str]): pass x, y = a x() # E: "int" not callable @@ -932,14 +950,14 @@ fb(aa) # E: Argument 1 to "fb" has incompatible type "Tuple[A, A]"; expected "Tu [case testSubtypingTupleIsContainer] from typing import Container -a = None # type: Container[str] +a: Container[str] a = () [typing fixtures/typing-full.pyi] [builtins fixtures/tuple.pyi] [case testSubtypingTupleIsSized] from typing import Sized -a = None # type: Sized +a: Sized a = () [typing fixtures/typing-medium.pyi] [builtins fixtures/tuple.pyi] @@ -1046,8 +1064,8 @@ class B1(A): pass class B2(A): pass class C: pass -x = None # type: Tuple[A, ...] -y = None # type: Tuple[Union[B1, C], Union[B2, C]] +x: Tuple[A, ...] +y: Tuple[Union[B1, C], Union[B2, C]] def g(x: T) -> Tuple[T, T]: return (x, x) @@ -1063,13 +1081,13 @@ from typing import Tuple class A: pass class B(A): pass -fixtup = None # type: Tuple[B, B] +fixtup: Tuple[B, B] -vartup_b = None # type: Tuple[B, ...] +vartup_b: Tuple[B, ...] reveal_type(fixtup if int() else vartup_b) # N: Revealed type is "builtins.tuple[__main__.B, ...]" reveal_type(vartup_b if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.B, ...]" -vartup_a = None # type: Tuple[A, ...] +vartup_a: Tuple[A, ...] reveal_type(fixtup if int() else vartup_a) # N: Revealed type is "builtins.tuple[__main__.A, ...]" reveal_type(vartup_a if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" @@ -1083,13 +1101,13 @@ from typing import Tuple, List class A: pass class B(A): pass -fixtup = None # type: Tuple[B, B] +fixtup: Tuple[B, B] -lst_b = None # type: List[B] +lst_b: List[B] reveal_type(fixtup if int() else lst_b) # N: Revealed type is "typing.Sequence[__main__.B]" reveal_type(lst_b if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.B]" -lst_a = None # type: List[A] +lst_a: List[A] reveal_type(fixtup if int() else lst_a) # N: Revealed type is "typing.Sequence[__main__.A]" reveal_type(lst_a if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.A]" @@ -1103,15 +1121,15 @@ class A: pass empty = () -fixtup = None # type: Tuple[A] +fixtup: Tuple[A] reveal_type(fixtup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" reveal_type(empty if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -vartup = None # type: Tuple[A, ...] +vartup: Tuple[A, ...] reveal_type(empty if int() else vartup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" reveal_type(vartup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -lst = None # type: List[A] +lst: List[A] reveal_type(empty if int() else lst) # N: Revealed type is "typing.Sequence[__main__.A]" reveal_type(lst if int() else empty) # N: Revealed type is "typing.Sequence[__main__.A]" @@ -1128,9 +1146,9 @@ class NTup(NamedTuple): class SubTuple(Tuple[bool]): ... class SubVarTuple(Tuple[int, ...]): ... -ntup = None # type: NTup -subtup = None # type: SubTuple -vartup = None # type: SubVarTuple +ntup: NTup +subtup: SubTuple +vartup: SubVarTuple reveal_type(ntup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(subtup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -1141,8 +1159,8 @@ reveal_type(subtup if int() else vartup) # N: Revealed type is "builtins.tuple[ [case testTupleJoinIrregular] from typing import Tuple -tup1 = None # type: Tuple[bool, int] -tup2 = None # type: Tuple[bool] +tup1: Tuple[bool, int] +tup2: Tuple[bool] reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -1168,9 +1186,9 @@ class NTup2(NamedTuple): class SubTuple(Tuple[bool, int, int]): ... -tup1 = None # type: NTup1 -tup2 = None # type: NTup2 -subtup = None # type: SubTuple +tup1: NTup1 +tup2: NTup2 +subtup: SubTuple reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" From d106c847904ac84d92dc828de5057d80e8774c44 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:11:09 +0200 Subject: [PATCH 0118/1617] Enable strict optional for more test files (9) (#15607) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup to https://github.com/python/mypy/pull/15586 This will be the last one 🚀 --- mypy/test/testcheck.py | 12 +--- test-data/unit/check-modules.test | 21 +++++-- test-data/unit/check-unions.test | 44 ++++++++------- test-data/unit/check-varargs.test | 92 ++++++++++++++++++------------- 4 files changed, 93 insertions(+), 76 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index a77da090f740..20dfbb77f3e0 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -49,14 +49,6 @@ typecheck_files.remove("check-modules-case.test") -# TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated -no_strict_optional_files = { - "check-modules.test", - "check-unions.test", - "check-varargs.test", -} - - class TypeCheckSuite(DataSuite): files = typecheck_files @@ -129,9 +121,7 @@ def run_case_once( perform_file_operations(operations) # Parse options after moving files (in case mypy.ini is being moved). - options = parse_options( - original_program_text, testcase, incremental_step, no_strict_optional_files - ) + options = parse_options(original_program_text, testcase, incremental_step) options.use_builtins_fixtures = True if not testcase.name.endswith("_no_incomplete"): options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 8a30237843a5..fc3daff64fbd 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -180,7 +180,8 @@ x = object() [case testChainedAssignmentAndImports] import m -i, s = None, None # type: (int, str) +i: int +s: str if int(): i = m.x if int(): @@ -585,6 +586,7 @@ x = 1+0 [case testConditionalImportAndAssign] +# flags: --no-strict-optional try: from m import x except: @@ -676,6 +678,7 @@ class B(A): ... [case testImportVariableAndAssignNone] +# flags: --no-strict-optional try: from m import x except: @@ -684,6 +687,7 @@ except: x = 1 [case testImportFunctionAndAssignNone] +# flags: --no-strict-optional try: from m import f except: @@ -709,6 +713,7 @@ except: def f(): pass [case testAssignToFuncDefViaGlobalDecl2] +# flags: --no-strict-optional import typing from m import f def g() -> None: @@ -721,6 +726,7 @@ def f(): pass [out] [case testAssignToFuncDefViaNestedModules] +# flags: --no-strict-optional import m.n m.n.f = None m.n.f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") @@ -730,6 +736,7 @@ def f(): pass [out] [case testAssignToFuncDefViaModule] +# flags: --no-strict-optional import m m.f = None m.f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") @@ -738,6 +745,7 @@ def f(): pass [out] [case testConditionalImportAndAssignNoneToModule] +# flags: --no-strict-optional if object(): import m else: @@ -758,6 +766,7 @@ else: [out] [case testImportAndAssignToModule] +# flags: --no-strict-optional import m m = None m.f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "str" @@ -930,8 +939,8 @@ tmp/a/b/__init__.py:3: error: Name "a" is not defined [case testSubmoduleMixingLocalAndQualifiedNames] from a.b import MyClass -val1 = None # type: a.b.MyClass # E: Name "a" is not defined -val2 = None # type: MyClass +val1: a.b.MyClass # E: Name "a" is not defined +val2: MyClass [file a/__init__.py] [file a/b.py] @@ -1501,7 +1510,7 @@ import bar from foo import * def bar(y: AnyAlias) -> None: pass -l = None # type: ListAlias[int] +l: ListAlias[int] reveal_type(l) [file foo.py] @@ -1532,7 +1541,7 @@ Row = Dict[str, int] [case testImportStarAliasGeneric] from y import * -notes = None # type: G[X] +notes: G[X] another = G[X]() second = XT[str]() last = XT[G]() @@ -1561,7 +1570,7 @@ from typing import Any def bar(x: Any, y: AnyCallable) -> Any: return 'foo' -cb = None # type: AnyCallable +cb: AnyCallable reveal_type(cb) # N: Revealed type is "def (*Any, **Any) -> Any" [file foo.py] diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 65d5c1abc7e8..28d83aa54ccc 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -55,12 +55,12 @@ class B: y = 2 class C: pass class D: pass -u = None # type: Union[A, C, D] -v = None # type: Union[C, D] -w = None # type: Union[A, B] -x = None # type: Union[A, C] -y = None # type: int -z = None # type: str +u: Union[A, C, D] +v: Union[C, D] +w: Union[A, B] +x: Union[A, C] +y: int +z: str if int(): y = w.y @@ -89,9 +89,9 @@ class B: class C: def foo(self) -> str: pass -x = None # type: Union[A, B] -y = None # type: Union[A, C] -i = None # type: int +x: Union[A, B] +y: Union[A, C] +i: int x.foo() y.foo() @@ -103,7 +103,7 @@ if int(): [case testUnionIndexing] from typing import Union, List -x = None # type: Union[List[int], str] +x: Union[List[int], str] x[2] x[2] + 1 # E: Unsupported operand types for + ("str" and "int") \ # N: Left operand is of type "Union[int, str]" @@ -132,6 +132,7 @@ def f(x: Union[int, str]) -> int: pass def f(x: type) -> str: pass [case testUnionWithNoneItem] +# flags: --no-strict-optional from typing import Union def f() -> Union[int, None]: pass x = 1 @@ -221,6 +222,7 @@ else: # N: def g(x: Union[int, str]) -> None [case testUnionSimplificationSpecialCases] +# flags: --no-strict-optional from typing import Any, TypeVar, Union class C(Any): pass @@ -266,9 +268,10 @@ class M(Generic[V]): def f(x: M[C]) -> None: y = x.get(None) - reveal_type(y) # N: Revealed type is "__main__.C" + reveal_type(y) # N: Revealed type is "Union[__main__.C, None]" [case testUnionSimplificationSpecialCases2] +# flags: --no-strict-optional from typing import Any, TypeVar, Union class C(Any): pass @@ -317,10 +320,10 @@ S = TypeVar('S') R = TypeVar('R') def u(x: T, y: S, z: R) -> Union[R, S, T]: pass -a = None # type: Any +a: Any reveal_type(u(1, 1, 1)) # N: Revealed type is "builtins.int" -reveal_type(u(C(), C(), None)) # N: Revealed type is "__main__.C" +reveal_type(u(C(), C(), None)) # N: Revealed type is "Union[None, __main__.C]" reveal_type(u(a, a, 1)) # N: Revealed type is "Union[builtins.int, Any]" reveal_type(u(a, C(), a)) # N: Revealed type is "Union[Any, __main__.C]" reveal_type(u('', 1, 1)) # N: Revealed type is "Union[builtins.int, builtins.str]" @@ -370,9 +373,9 @@ T = TypeVar('T') S = TypeVar('S') def u(x: T, y: S) -> Union[S, T]: pass -t_o = None # type: Type[object] -t_s = None # type: Type[str] -t_a = None # type: Type[Any] +t_o: Type[object] +t_s: Type[str] +t_a: Type[Any] # Two identical items reveal_type(u(t_o, t_o)) # N: Revealed type is "Type[builtins.object]" @@ -397,10 +400,10 @@ T = TypeVar('T') S = TypeVar('S') def u(x: T, y: S) -> Union[S, T]: pass -t_o = None # type: Type[object] -t_s = None # type: Type[str] -t_a = None # type: Type[Any] -t = None # type: type +t_o: Type[object] +t_s: Type[str] +t_a: Type[Any] +t: type # Union with object reveal_type(u(t_o, object())) # N: Revealed type is "builtins.object" @@ -1010,6 +1013,7 @@ MYTYPE = List[Union[str, "MYTYPE"]] # E: Cannot resolve name "MYTYPE" (possible [builtins fixtures/list.pyi] [case testNonStrictOptional] +# flags: --no-strict-optional from typing import Optional, List def union_test1(x): diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 92b9f7f04f26..4da9e0e5033e 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -8,8 +8,8 @@ [case testVarArgsWithinFunction] from typing import Tuple def f( *b: 'B') -> None: - ab = None # type: Tuple[B, ...] - ac = None # type: Tuple[C, ...] + ab: Tuple[B, ...] + ac: Tuple[C, ...] if int(): b = ac # E: Incompatible types in assignment (expression has type "Tuple[C, ...]", variable has type "Tuple[B, ...]") ac = b # E: Incompatible types in assignment (expression has type "Tuple[B, ...]", variable has type "Tuple[C, ...]") @@ -46,9 +46,9 @@ class A: pass class B(A): pass class C: pass -a = None # type: A -b = None # type: B -c = None # type: C +a: A +b: B +c: C f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A" f(a, b, c) # E: Argument 3 to "f" has incompatible type "C"; expected "A" @@ -67,9 +67,9 @@ class A: pass class B(A): pass class C: pass -a = None # type: A -b = None # type: B -c = None # type: C +a: A +b: B +c: C f(a) # E: Argument 1 to "f" has incompatible type "A"; expected "C" f(c, c) # E: Argument 2 to "f" has incompatible type "C"; expected "A" @@ -88,9 +88,9 @@ class A: pass class B(A): pass class C: pass -a = None # type: A -b = None # type: B -c = None # type: C +a: A +b: B +c: C f(a) # E: Argument 1 to "f" has incompatible type "A"; expected "Optional[C]" f(c, c) # E: Argument 2 to "f" has incompatible type "C"; expected "A" @@ -103,8 +103,8 @@ f(c, b, b, a, b) [case testCallVarargsFunctionWithIterable] from typing import Iterable -it1 = None # type: Iterable[int] -it2 = None # type: Iterable[str] +it1: Iterable[int] +it2: Iterable[str] def f(*x: int) -> None: pass f(*it1) f(*it2) # E: Argument 1 to "f" has incompatible type "*Iterable[str]"; expected "int" @@ -127,7 +127,7 @@ reveal_type(f(*x, *y)) # N: Revealed type is "Tuple[builtins.int, builtins.str, [case testCallVarargsFunctionWithIterableAndPositional] from typing import Iterable -it1 = None # type: Iterable[int] +it1: Iterable[int] def f(*x: int) -> None: pass f(*it1, 1, 2) f(*it1, 1, *it1, 2) @@ -161,10 +161,10 @@ class A: pass class B(A): pass class C: pass -a = None # type: A -b = None # type: B -c = None # type: C -o = None # type: object +a: A +b: B +c: C +o: object if int(): a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") @@ -188,6 +188,7 @@ if int(): [builtins fixtures/list.pyi] [case testTypeInferenceWithCalleeVarArgsAndDefaultArgs] +# flags: --no-strict-optional from typing import TypeVar T = TypeVar('T') a = None # type: A @@ -229,10 +230,10 @@ def f(a: 'A', b: 'B') -> None: class A: pass class B: pass -aa = None # type: List[A] -ab = None # type: List[B] -a = None # type: A -b = None # type: B +aa: List[A] +ab: List[B] +a: A +b: B f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" f(a, *ab) # Ok @@ -248,10 +249,10 @@ class B: pass class C: pass class CC(C): pass -a = None # type: A -b = None # type: B -c = None # type: C -cc = None # type: CC +a: A +b: B +c: C +cc: CC f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B, B]"; expected "C" f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, C]"; expected "A" @@ -267,7 +268,7 @@ f(a, *(b, cc)) [builtins fixtures/tuple.pyi] [case testInvalidVarArg] - +# flags: --no-strict-optional def f(a: 'A') -> None: pass @@ -293,7 +294,10 @@ def g(a: 'A', *b: 'A') -> None: pass class A: pass class B: pass -aa, ab, a, b = None, None, None, None # type: (List[A], List[B], A, B) +aa: List[A] +ab: List[B] +a: A +b: B f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" f(a, *aa) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" f(b, *ab) # E: Argument 1 to "f" has incompatible type "B"; expected "A" @@ -315,7 +319,10 @@ class B: pass class C: pass class CC(C): pass -a, b, c, cc = None, None, None, None # type: (A, B, C, CC) +a: A +b: B +c: C +cc: CC f(*(b, b, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, B]"; expected "A" f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "B" @@ -342,7 +349,8 @@ def f(a: 'A') -> None: pass def g(a: 'A', *b: 'A') -> None: pass class A: pass -d, a = None, None # type: (Any, A) +d: Any +a: A f(a, a, *d) # E: Too many arguments for "f" f(a, *d) # Ok f(*d) # Ok @@ -351,6 +359,7 @@ g(*d) g(a, *d) g(a, a, *d) [builtins fixtures/list.pyi] + [case testListVarArgsAndSubtyping] from typing import List def f( *a: 'A') -> None: @@ -362,8 +371,8 @@ def g( *a: 'B') -> None: class A: pass class B(A): pass -aa = None # type: List[A] -ab = None # type: List[B] +aa: List[A] +ab: List[B] g(*aa) # E: Argument 1 to "g" has incompatible type "*List[A]"; expected "B" f(*aa) @@ -449,6 +458,7 @@ foo(*()) [case testIntersectionTypesAndVarArgs] +# flags: --no-strict-optional from foo import * [file foo.pyi] from typing import overload @@ -518,7 +528,9 @@ def f(a: S, *b: T) -> Tuple[S, T]: class A: pass class B: pass -a, b, aa = None, None, None # type: (A, B, List[A]) +a: A +b: B +aa: List[A] if int(): a, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" @@ -553,7 +565,8 @@ def f(a: S, b: T) -> Tuple[S, T]: pass class A: pass class B: pass -a, b = None, None # type: (A, B) +a: A +b: B if int(): a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" @@ -575,10 +588,11 @@ if int(): from typing import List, TypeVar, Generic, Tuple T = TypeVar('T') S = TypeVar('S') -a, b = None, None # type: (A, B) -ao = None # type: List[object] -aa = None # type: List[A] -ab = None # type: List[B] +a: A +b: B +ao: List[object] +aa: List[A] +ab: List[B] class G(Generic[T]): def f(self, *a: S) -> Tuple[List[S], List[T]]: @@ -649,7 +663,7 @@ f(1, '') # E: Argument 2 to "f" has incompatible type "str"; expected "int" [case testVarArgsFunctionSubtyping] from typing import Callable -x = None # type: Callable[[int], None] +x: Callable[[int], None] def f(*x: int) -> None: pass def g(*x: str) -> None: pass x = f From 6cd8c00983a294b4b142ee0f01e91912363d3450 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:40:01 +0200 Subject: [PATCH 0119/1617] Enable strict optional for more test files (3) (#15597) --- mypy/test/helpers.py | 11 ++---- mypy/test/testfinegrained.py | 7 +--- test-data/unit/fine-grained-suggest.test | 2 +- test-data/unit/fine-grained.test | 46 +++++++++++++----------- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 1e034f94dd92..d2c92614048a 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -7,7 +7,7 @@ import shutil import sys import time -from typing import Any, Callable, Collection, Iterable, Iterator, Pattern +from typing import Any, Callable, Iterable, Iterator, Pattern # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly @@ -317,10 +317,7 @@ def assert_type(typ: type, value: object) -> None: def parse_options( - program_text: str, - testcase: DataDrivenTestCase, - incremental_step: int, - no_strict_optional_files: Collection[str] = (), + program_text: str, testcase: DataDrivenTestCase, incremental_step: int ) -> Options: """Parse comments like '# flags: --foo' in a test case.""" options = Options() @@ -342,10 +339,6 @@ def parse_options( else: flag_list = [] options = Options() - # TODO: Enable strict optional in test cases by default (requires *many* test case changes) - if os.path.basename(testcase.file) in no_strict_optional_files: - options.strict_optional = False - options.error_summary = False options.hide_error_codes = True options.force_uppercase_builtins = True diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index a1dde823cb5f..ba0526d32558 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -45,9 +45,6 @@ # Set to True to perform (somewhat expensive) checks for duplicate AST nodes after merge CHECK_CONSISTENCY = False -# TODO: Enable strict optional in test cases by default. Remove files here, once test cases are updated -no_strict_optional_files = {"fine-grained.test", "fine-grained-suggest.test"} - class FineGrainedSuite(DataSuite): files = find_test_files( @@ -143,9 +140,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bool) -> Options: # This handles things like '# flags: --foo'. - options = parse_options( - source, testcase, incremental_step=1, no_strict_optional_files=no_strict_optional_files - ) + options = parse_options(source, testcase, incremental_step=1) options.incremental = True options.use_builtins_fixtures = True options.show_traceback = True diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test index a28e3204b93f..47de16b8d765 100644 --- a/test-data/unit/fine-grained-suggest.test +++ b/test-data/unit/fine-grained-suggest.test @@ -1090,7 +1090,7 @@ optional2(10) optional2('test') def optional3(x: Optional[List[Any]]): - assert not x + assert x return x[0] optional3(test) diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 3cb234364a58..7d854bab424c 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -191,7 +191,7 @@ main:3: error: "A" has no attribute "x" [case testVariableTypeBecomesInvalid] import m def f() -> None: - a = None # type: m.A + a: m.A [file m.py] class A: pass [file m.py.2] @@ -1724,15 +1724,15 @@ f = 1 main:1: error: Module "a" has no attribute "f" [case testDecoratedMethodRefresh] -from typing import Iterator, Callable, List +from typing import Iterator, Callable, List, Optional from a import f import a -def dec(f: Callable[['A'], Iterator[int]]) -> Callable[[int], int]: pass +def dec(f: Callable[['A'], Optional[Iterator[int]]]) -> Callable[[int], int]: pass class A: @dec - def f(self) -> Iterator[int]: + def f(self) -> Optional[Iterator[int]]: self.x = a.g() # type: int return None [builtins fixtures/list.pyi] @@ -4626,6 +4626,7 @@ class User: == [case testNoStrictOptionalModule] +# flags: --no-strict-optional import a a.y = a.x [file a.py] @@ -4643,9 +4644,10 @@ y: int [out] == == -main:2: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "int") +main:3: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "int") [case testNoStrictOptionalFunction] +# flags: --no-strict-optional import a from typing import Optional def f() -> None: @@ -4666,9 +4668,10 @@ def g(x: str) -> None: [out] == == -main:5: error: Argument 1 to "g" has incompatible type "Optional[int]"; expected "str" +main:6: error: Argument 1 to "g" has incompatible type "Optional[int]"; expected "str" [case testNoStrictOptionalMethod] +# flags: --no-strict-optional import a from typing import Optional class C: @@ -4693,7 +4696,7 @@ class B: [out] == == -main:6: error: Argument 1 to "g" of "B" has incompatible type "Optional[int]"; expected "str" +main:7: error: Argument 1 to "g" of "B" has incompatible type "Optional[int]"; expected "str" [case testStrictOptionalModule] # flags: --strict-optional @@ -5276,10 +5279,11 @@ class I(metaclass=ABCMeta): @abstractmethod def f(self) -> None: pass [file b.py] +from typing import Optional from z import I class Foo(I): pass -def x() -> Foo: return None +def x() -> Optional[Foo]: return None [file z.py.2] from abc import abstractmethod, ABCMeta class I(metaclass=ABCMeta): @@ -5301,10 +5305,11 @@ class I(metaclass=ABCMeta): @abstractmethod def f(self) -> None: pass [file b.py] +from typing import Optional from a import I class Foo(I): pass -def x() -> Foo: return None +def x() -> Optional[Foo]: return None [file a.py.2] from abc import abstractmethod, ABCMeta class I(metaclass=ABCMeta): @@ -7769,7 +7774,8 @@ from typing import List import b class A(b.B): def meth(self) -> None: - self.x, *self.y = None, None # type: str, List[str] + self.x: str + self.y: List[str] [file b.py] from typing import List class B: @@ -7781,7 +7787,7 @@ class B: [builtins fixtures/list.pyi] [out] == -main:5: error: Incompatible types in assignment (expression has type "List[str]", base class "B" defined the type as "List[int]") +main:6: error: Incompatible types in assignment (expression has type "List[str]", base class "B" defined the type as "List[int]") [case testLiskovFineVariableCleanDefInMethodNested-only_when_nocache] from b import B @@ -9109,27 +9115,27 @@ import a [file a.py] # mypy: no-warn-no-return -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [file a.py.2] # mypy: disallow-any-generics, no-warn-no-return -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [file a.py.3] # mypy: no-warn-no-return -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [file a.py.4] -from typing import List -def foo() -> List: +from typing import List, Optional +def foo() -> Optional[List]: 20 [out] == @@ -9867,7 +9873,7 @@ x = 0 # Arbitrary change to trigger reprocessing [builtins fixtures/dict.pyi] [out] == -a.py:5: note: Revealed type is "def (x: builtins.int) -> builtins.str" +a.py:5: note: Revealed type is "def (x: builtins.int) -> Union[builtins.str, None]" [case testTypeVarTupleCached] import a From 15929457fabe5bd5e768d495ea11689d4d8be634 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 8 Jul 2023 21:35:07 +0200 Subject: [PATCH 0120/1617] Cleanup after enabling strict default for test files (#15627) --- mypy/test/testcheck.py | 2 -- test-data/unit/hacks.txt | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 20dfbb77f3e0..7b81deeafe9d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -128,8 +128,6 @@ def run_case_once( options.show_traceback = True # Enable some options automatically based on test file name. - if "optional" in testcase.file: - options.strict_optional = True if "columns" in testcase.file: options.show_column_numbers = True if "errorcodes" in testcase.file: diff --git a/test-data/unit/hacks.txt b/test-data/unit/hacks.txt index 43114a8af004..15b1065cb7a9 100644 --- a/test-data/unit/hacks.txt +++ b/test-data/unit/hacks.txt @@ -5,17 +5,6 @@ Due to historical reasons, test cases contain things that may appear baffling without extra context. This file attempts to describe most of them. -Strict optional is disabled be default --------------------------------------- - -Strict optional checking is enabled in mypy by default, but test cases -must enable it explicitly, either through `# flags: --strict-optional` -or by including `optional` as a substring in your test file name. - -The reason for this is that many test cases written before strict -optional was implemented use the idiom `x = None # type: t`, and -updating all of these test cases would take a lot of work. - Dummy if statements to prevent redefinition ------------------------------------------- From ebfea94c9f1dc081f8f3f0de9e78a5c83ff8af40 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 8 Jul 2023 23:25:43 +0200 Subject: [PATCH 0121/1617] stubdoc: Fix crash on non-str docstring (#15623) --- mypy/stubdoc.py | 2 +- mypy/test/teststubgen.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 232d9e9762e9..145f57fd7751 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -254,7 +254,7 @@ def infer_sig_from_docstring(docstr: str | None, name: str) -> list[FunctionSig] * docstr: docstring * name: name of function for which signatures are to be found """ - if not docstr: + if not (isinstance(docstr, str) and docstr): return None state = DocStringParser(name) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 884cf87ac5d8..79d380785a39 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1213,6 +1213,27 @@ def test(arg0: str) -> None: assert_equal(output, ["def test(arg0: foo.bar.Action) -> other.Thing: ..."]) assert_equal(set(imports), {"import foo", "import other"}) + def test_generate_c_function_no_crash_for_non_str_docstring(self) -> None: + def test(arg0: str) -> None: + ... + + test.__doc__ = property(lambda self: "test(arg0: str) -> None") # type: ignore[assignment] + + output: list[str] = [] + imports: list[str] = [] + mod = ModuleType(self.__module__, "") + generate_c_function_stub( + mod, + "test", + test, + output=output, + imports=imports, + known_modules=[mod.__name__], + sig_generators=get_sig_generators(parse_options([])), + ) + assert_equal(output, ["def test(*args, **kwargs) -> Any: ..."]) + assert_equal(imports, []) + def test_generate_c_property_with_pybind11(self) -> None: """Signatures included by PyBind11 inside property.fget are read.""" From f72e4e548b3fd03b5a1b7e2ba175f35bc2e9148c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 9 Jul 2023 17:45:00 +0200 Subject: [PATCH 0122/1617] Support optional for custom dataclass descriptors (#15628) Fixes: #15020 --- mypy/plugins/dataclasses.py | 3 ++- test-data/unit/check-dataclass-transform.test | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 7b3795b91a74..c78a1b313878 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -680,7 +680,8 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: ) current_attr_names.add(lhs.name) - init_type = self._infer_dataclass_attr_init_type(sym, lhs.name, stmt) + with state.strict_optional_set(self._api.options.strict_optional): + init_type = self._infer_dataclass_attr_init_type(sym, lhs.name, stmt) found_attrs[lhs.name] = DataclassAttribute( name=lhs.name, alias=alias, diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 53a6eea95bfa..9029582ece82 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -1019,18 +1019,19 @@ class Desc: def __get__(self, instance: object, owner: Any) -> str: ... def __get__(self, instance, owner): ... - def __set__(self, instance: Any, value: bytes) -> None: ... + def __set__(self, instance: Any, value: bytes | None) -> None: ... @my_dataclass class C: x: Desc c = C(x=b'x') -C(x=1) # E: Argument "x" to "C" has incompatible type "int"; expected "bytes" +c = C(x=None) +C(x=1) # E: Argument "x" to "C" has incompatible type "int"; expected "Optional[bytes]" reveal_type(c.x) # N: Revealed type is "builtins.str" reveal_type(C.x) # N: Revealed type is "builtins.int" c.x = b'x' -c.x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "bytes") +c.x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Optional[bytes]") [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] From 67cc05926b037243b71e857a394318c3a09d1daf Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 9 Jul 2023 17:54:29 +0200 Subject: [PATCH 0123/1617] stubgen: Support `ParamSpec` and `TypeVarTuple` (#15626) These are treated the same way as `TypeVar` except imported from `typing_extensions` instead of `typing` by default. --- mypy/stubgen.py | 8 ++++++-- test-data/unit/stubgen.test | 26 ++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 229559ac8120..e7fa65d7f949 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -660,7 +660,7 @@ def visit_mypy_file(self, o: MypyFile) -> None: "_typeshed": ["Incomplete"], "typing": ["Any", "TypeVar", "NamedTuple"], "collections.abc": ["Generator"], - "typing_extensions": ["TypedDict"], + "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], } for pkg, imports in known_imports.items(): for t in imports: @@ -1158,10 +1158,14 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: Used to know if assignments look like type aliases, function alias, or module alias. """ - # Assignment of TypeVar(...) are passed through + # Assignment of TypeVar(...) and other typevar-likes are passed through if isinstance(expr, CallExpr) and self.get_fullname(expr.callee) in ( "typing.TypeVar", "typing_extensions.TypeVar", + "typing.ParamSpec", + "typing_extensions.ParamSpec", + "typing.TypeVarTuple", + "typing_extensions.TypeVarTuple", ): return True elif isinstance(expr, EllipsisExpr): diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index e1818dc4c4bc..9c7221e7ec54 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1036,11 +1036,16 @@ y: C [case testTypeVarPreserved] tv = TypeVar('tv') +ps = ParamSpec('ps') +tvt = TypeVarTuple('tvt') [out] from typing import TypeVar +from typing_extensions import ParamSpec, TypeVarTuple tv = TypeVar('tv') +ps = ParamSpec('ps') +tvt = TypeVarTuple('tvt') [case testTypeVarArgsPreserved] tv = TypeVar('tv', int, str) @@ -1052,29 +1057,37 @@ tv = TypeVar('tv', int, str) [case testTypeVarNamedArgsPreserved] tv = TypeVar('tv', bound=bool, covariant=True) +ps = ParamSpec('ps', bound=bool, covariant=True) [out] from typing import TypeVar +from typing_extensions import ParamSpec tv = TypeVar('tv', bound=bool, covariant=True) +ps = ParamSpec('ps', bound=bool, covariant=True) [case TypeVarImportAlias] -from typing import TypeVar as t_TV -from typing_extensions import TypeVar as te_TV +from typing import TypeVar as t_TV, ParamSpec as t_PS +from typing_extensions import TypeVar as te_TV, TypeVarTuple as te_TVT from x import TypeVar as x_TV T = t_TV('T') U = te_TV('U') V = x_TV('V') +PS = t_PS('PS') +TVT = te_TVT('TVT') + [out] from _typeshed import Incomplete -from typing import TypeVar as t_TV -from typing_extensions import TypeVar as te_TV +from typing import ParamSpec as t_PS, TypeVar as t_TV +from typing_extensions import TypeVar as te_TV, TypeVarTuple as te_TVT T = t_TV('T') U = te_TV('U') V: Incomplete +PS = t_PS('PS') +TVT = te_TVT('TVT') [case testTypeVarFromImportAlias] import typing as t @@ -1085,6 +1098,9 @@ T = t.TypeVar('T') U = te.TypeVar('U') V = x.TypeVar('V') +PS = t.ParamSpec('PS') +TVT = te.TypeVarTuple('TVT') + [out] import typing as t import typing_extensions as te @@ -1093,6 +1109,8 @@ from _typeshed import Incomplete T = t.TypeVar('T') U = te.TypeVar('U') V: Incomplete +PS = t.ParamSpec('PS') +TVT = te.TypeVarTuple('TVT') [case testTypeAliasPreserved] alias = str From 0a020fa73cf5339a80d81c5b44e17116a5c5307e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 12 Jul 2023 11:22:18 +0100 Subject: [PATCH 0124/1617] Bump pytest to >=7.4.0 (#15611) The 7.4.0 release of pytest broke mypy because we were using some undocumented, private API that was removed. Ideally we'd stop using the private API, but nobody seems to remember why we started using the private API in the first place (see https://github.com/python/mypy/pull/15501#issuecomment-1607083799, and following comments). For now it (unfortunately) seems safer to just migrate to the new private API rather than try to figure out an alternative using public API. I also took @bluetech's advice in https://github.com/python/mypy/pull/15501#issuecomment-1606259499 to improve the type annotations in the method in question. --- mypy/test/data.py | 7 +++++-- test-requirements.txt | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 940776bf7b19..f9bb951650df 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -378,7 +378,10 @@ def teardown(self) -> None: def reportinfo(self) -> tuple[str, int, str]: return self.file, self.line, self.name - def repr_failure(self, excinfo: Any, style: Any | None = None) -> str: + def repr_failure( + self, excinfo: pytest.ExceptionInfo[BaseException], style: Any | None = None + ) -> str: + excrepr: object if isinstance(excinfo.value, SystemExit): # We assume that before doing exit() (which raises SystemExit) we've printed # enough context about what happened so that a stack trace is not useful. @@ -388,7 +391,7 @@ def repr_failure(self, excinfo: Any, style: Any | None = None) -> str: elif isinstance(excinfo.value, pytest.fail.Exception) and not excinfo.value.pytrace: excrepr = excinfo.exconly() else: - self.parent._prunetraceback(excinfo) + excinfo.traceback = self.parent._traceback_filter(excinfo) excrepr = excinfo.getrepr(style="short") return f"data: {self.file}:{self.line}:\n{excrepr}" diff --git a/test-requirements.txt b/test-requirements.txt index 195909b71b8d..5340973a4de1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,9 +8,7 @@ lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_ pre-commit pre-commit-hooks==4.4.0 psutil>=4.0 -# pytest 6.2.3 does not support Python 3.10 -# TODO: fix use of removed private APIs so we can use the latest pytest -pytest>=6.2.4,<7.4.0 +pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.0.272 # must match version in .pre-commit-config.yaml From 569cfc9e6212e0bb841b1a299f0d8615b34cddee Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 12 Jul 2023 05:22:52 -0700 Subject: [PATCH 0125/1617] tests: enforce meaningful version checks (#15635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up to #15430 — would've made https://github.com/python/mypy/pull/15566#issuecomment-1616170947 unnecessary. --- mypy/test/data.py | 12 +++++++---- mypy/test/meta/test_parse_data.py | 34 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index f9bb951650df..47c96ce70744 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -167,13 +167,17 @@ def _item_fail(msg: str) -> NoReturn: version = tuple(int(x) for x in version_str.split(".")) except ValueError: _item_fail(f"{version_str!r} is not a valid python version") - if version < defaults.PYTHON3_VERSION: - _item_fail( - f"Version check against {version}; must be >= {defaults.PYTHON3_VERSION}" - ) if compare_op == ">=": + if version <= defaults.PYTHON3_VERSION: + _item_fail( + f"{arg} always true since minimum runtime version is {defaults.PYTHON3_VERSION}" + ) version_check = sys.version_info >= version elif compare_op == "==": + if version < defaults.PYTHON3_VERSION: + _item_fail( + f"{arg} always false since minimum runtime version is {defaults.PYTHON3_VERSION}" + ) if not 1 < len(version) < 4: _item_fail( f'Only minor or patch version checks are currently supported with "==": {version_str!r}' diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index cc1b4ff6eeed..6593dbc45704 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -67,3 +67,37 @@ def test_parse_invalid_section(self) -> None: f".test:{expected_lineno}: Invalid section header [unknownsection] in case 'abc'" ) assert expected in actual + + def test_bad_ge_version_check(self) -> None: + # Arrange + data = self._dedent( + """ + [case abc] + s: str + [out version>=3.8] + abc + """ + ) + + # Act + actual = self._run_pytest(data) + + # Assert + assert "version>=3.8 always true since minimum runtime version is (3, 8)" in actual + + def test_bad_eq_version_check(self) -> None: + # Arrange + data = self._dedent( + """ + [case abc] + s: str + [out version==3.7] + abc + """ + ) + + # Act + actual = self._run_pytest(data) + + # Assert + assert "version==3.7 always false since minimum runtime version is (3, 8)" in actual From 38a710418dd839e4af52bd74c7afa8eb954a315b Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 12 Jul 2023 06:44:52 -0700 Subject: [PATCH 0126/1617] tests: --update-data incompatible with xdist (#15641) Fixes a [gotcha](https://github.com/python/mypy/pull/15283#issuecomment-1631820071) with `--update-data`. When using `--update-data` with parallelized tests (xdist), the line offsets determined at test time may become wrong by update time, as multiple workers perform their updates to the same file. This makes it so we exit with a usage error when `--update-data` is used with parallelism. --- mypy/test/data.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/test/data.py b/mypy/test/data.py index 47c96ce70744..66dafaff775a 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -621,6 +621,13 @@ def pytest_addoption(parser: Any) -> None: ) +def pytest_configure(config: pytest.Config) -> None: + if config.getoption("--update-data") and config.getoption("--numprocesses", default=1) > 1: + raise pytest.UsageError( + "--update-data incompatible with parallelized tests; re-run with -n 1" + ) + + # This function name is special to pytest. See # https://doc.pytest.org/en/latest/how-to/writing_plugins.html#collection-hooks def pytest_pycollect_makeitem(collector: Any, name: str, obj: object) -> Any | None: From 87fa107661d8008f905caaf2eac8935cfab81efa Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 12 Jul 2023 06:45:52 -0700 Subject: [PATCH 0127/1617] .git-blame-ignore-revs: add #12711 (#15643) --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 006809c4a0e1..be8582a6b221 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,3 +4,5 @@ 23ee1e7aff357e656e3102435ad0fe3b5074571e # Use variable annotations (#10723) f98f78216ba9d6ab68c8e69c19e9f3c7926c5efe +# run pyupgrade (#12711) +fc335cb16315964b923eb1927e3aad1516891c28 From 4d394c1cd116b0b8efbc8c73b6f49245cd46917f Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 12 Jul 2023 06:46:24 -0700 Subject: [PATCH 0128/1617] attrs: define defaults to slots=True (#15642) Fixes #15639. The new-style API `attrs.define` [enables slots by default](https://www.attrs.org/en/stable/api.html#attrs.define). --- mypy/plugins/attrs.py | 3 ++- mypy/plugins/default.py | 4 +++- test-data/unit/check-plugin-attrs.test | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index a6a383405ac6..7f1196c16801 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -294,6 +294,7 @@ def attr_class_maker_callback( ctx: mypy.plugin.ClassDefContext, auto_attribs_default: bool | None = False, frozen_default: bool = False, + slots_default: bool = False, ) -> bool: """Add necessary dunder methods to classes decorated with attr.s. @@ -314,7 +315,7 @@ def attr_class_maker_callback( init = _get_decorator_bool_argument(ctx, "init", True) frozen = _get_frozen(ctx, frozen_default) order = _determine_eq_order(ctx) - slots = _get_decorator_bool_argument(ctx, "slots", False) + slots = _get_decorator_bool_argument(ctx, "slots", slots_default) auto_attribs = _get_decorator_optional_bool_argument(ctx, "auto_attribs", auto_attribs_default) kw_only = _get_decorator_bool_argument(ctx, "kw_only", False) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index f5dea0621177..b60fc3873c04 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -159,7 +159,9 @@ def get_class_decorator_hook_2( attrs.attr_class_maker_callback, auto_attribs_default=None, frozen_default=True ) elif fullname in attrs.attr_define_makers: - return partial(attrs.attr_class_maker_callback, auto_attribs_default=None) + return partial( + attrs.attr_class_maker_callback, auto_attribs_default=None, slots_default=True + ) return None diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index edae6c096015..3d1e2d730af8 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1631,6 +1631,24 @@ reveal_type(A.__attrs_init__) # N: Revealed type is "def (self: __main__.A, b: [case testAttrsClassWithSlots] import attr +@attr.define +class Define: + b: int = attr.ib() + + def __attrs_post_init__(self) -> None: + self.b = 1 + self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.Define" + + +@attr.define(slots=False) +class DefineSlotsFalse: + b: int = attr.ib() + + def __attrs_post_init__(self) -> None: + self.b = 1 + self.c = 2 + + @attr.s(slots=True) class A: b: int = attr.ib() From edb41e0490ca01aea5f70a99aad9c8441773cc6e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 12 Jul 2023 18:27:09 +0300 Subject: [PATCH 0129/1617] Check tuples of abstract types (#15366) The PR is quite simple (and I would like to keep it this way): - Before we were only checking `type[]` type - Now we also check `tuple[type[], ...]` type There might be other types that we want to add in the future here: `TypedDictType`, etc? But, let's do it one by one, because smaller PRs are easier to merge :) Closes #15264 --- mypy/checkexpr.py | 30 +++++++++++++++++++++--------- test-data/unit/check-abstract.test | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index aee8d58a69f6..46a5e35f320d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2352,15 +2352,7 @@ def check_arg( if isinstance(caller_type, DeletedType): self.msg.deleted_as_rvalue(caller_type, context) # Only non-abstract non-protocol class can be given where Type[...] is expected... - elif ( - isinstance(caller_type, CallableType) - and isinstance(callee_type, TypeType) - and caller_type.is_type_obj() - and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) - and isinstance(callee_type.item, Instance) - and (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol) - and not self.chk.allow_abstract_call - ): + elif self.has_abstract_type_part(caller_type, callee_type): self.msg.concrete_only_call(callee_type, context) elif not is_subtype(caller_type, callee_type, options=self.chk.options): code = self.msg.incompatible_argument( @@ -5484,6 +5476,26 @@ def narrow_type_from_binder( return narrow_declared_type(known_type, restriction) return known_type + def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: + # TODO: support other possible types here + if isinstance(caller_type, TupleType) and isinstance(callee_type, TupleType): + return any( + self.has_abstract_type(get_proper_type(caller), get_proper_type(callee)) + for caller, callee in zip(caller_type.items, callee_type.items) + ) + return self.has_abstract_type(caller_type, callee_type) + + def has_abstract_type(self, caller_type: ProperType, callee_type: ProperType) -> bool: + return ( + isinstance(caller_type, CallableType) + and isinstance(callee_type, TypeType) + and caller_type.is_type_obj() + and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) + and isinstance(callee_type.item, Instance) + and (callee_type.item.type.is_abstract or callee_type.item.type.is_protocol) + and not self.chk.allow_abstract_call + ) + def has_any_type(t: Type, ignore_in_type_obj: bool = False) -> bool: """Whether t contains an Any type""" diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 1c85fae93dd4..dc64476beda6 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -198,6 +198,24 @@ x: Type[B] f(x) # OK [out] +[case testAbstractTypeInADict] +from typing import Dict, Type +from abc import abstractmethod + +class Class: + @abstractmethod + def method(self) -> None: + pass + +my_dict_init: Dict[int, Type[Class]] = {0: Class} # E: Only concrete class can be given where "Tuple[int, Type[Class]]" is expected + +class Child(Class): + def method(self) -> None: ... + +other_dict_init: Dict[int, Type[Class]] = {0: Child} # ok +[builtins fixtures/dict.pyi] +[out] + [case testInstantiationAbstractsInTypeForAliases] from typing import Type from abc import abstractmethod From 235a3bb456ef782ef9a6005616aeea4e3a2a432a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 12 Jul 2023 16:50:12 +0100 Subject: [PATCH 0130/1617] Fix parse test case on Python 3.12 (#15577) This fixes the test case testFStringWithFormatSpecifierExpression on Python 3.12. Strip redundant empty string literal from AST generated from f-string. It's not generated on earlier Python versions and it seems fine to just drop it. --- mypy/fastparse.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index eeeda16f9d8d..f7a98e9b2b8f 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1535,6 +1535,12 @@ def visit_JoinedStr(self, n: ast3.JoinedStr) -> Expression: # Don't make unnecessary join call if there is only one str to join if len(strs_to_join.items) == 1: return self.set_line(strs_to_join.items[0], n) + elif len(strs_to_join.items) > 1: + last = strs_to_join.items[-1] + if isinstance(last, StrExpr) and last.value == "": + # 3.12 can add an empty literal at the end. Delete it for consistency + # between Python versions. + del strs_to_join.items[-1:] join_method = MemberExpr(empty_string, "join") join_method.set_line(empty_string) result_expression = CallExpr(join_method, [strs_to_join], [ARG_POS], [None]) From 4a1a38eee6b1558c0891584a6fdff84eab49c7a6 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 12 Jul 2023 22:51:50 +0300 Subject: [PATCH 0131/1617] Add runtime `__slots__` attribute to dataclasses (#15649) Closes https://github.com/python/mypy/issues/15647 --- mypy/plugins/dataclasses.py | 8 +++++++- test-data/unit/check-dataclasses.test | 29 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index c78a1b313878..b1dc016a0279 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -469,9 +469,15 @@ def add_slots( self._cls, ) return - info.slots = generated_slots + # Now, insert `.__slots__` attribute to class namespace: + slots_type = TupleType( + [self._api.named_type("builtins.str") for _ in generated_slots], + self._api.named_type("builtins.tuple"), + ) + add_attribute_to_class(self._api, self._cls, "__slots__", slots_type) + def reset_init_only_vars(self, info: TypeInfo, attributes: list[DataclassAttribute]) -> None: """Remove init-only vars from the class and reset init var declarations.""" for attr in attributes: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 4a6e737ddd8d..131521aa98e4 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1547,6 +1547,35 @@ class Other: [builtins fixtures/dataclasses.pyi] +[case testDataclassWithSlotsRuntimeAttr] +# flags: --python-version 3.10 +from dataclasses import dataclass + +@dataclass(slots=True) +class Some: + x: int + y: str + z: bool + +reveal_type(Some.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]" + +@dataclass(slots=True) +class Other: + x: int + y: str + +reveal_type(Other.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str]" + + +@dataclass +class NoSlots: + x: int + y: str + +NoSlots.__slots__ # E: "Type[NoSlots]" has no attribute "__slots__" +[builtins fixtures/dataclasses.pyi] + + [case testSlotsDefinitionWithTwoPasses1] # flags: --python-version 3.10 # https://github.com/python/mypy/issues/11821 From fbe588ff73abd26d1a775e0bfc3db14e933159aa Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:57:08 -0700 Subject: [PATCH 0132/1617] Ensure 3.12 tests pass (#15648) --- .github/workflows/test.yml | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05e52ad95a17..f594353ed05a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,6 +60,13 @@ jobs: toxenv: py tox_extra_args: "-n 2" test_mypyc: true + - name: Test suite with py312-ubuntu, mypyc-compiled + python: '3.12-dev' + arch: x64 + os: ubuntu-latest + toxenv: py + tox_extra_args: "-n 2" + test_mypyc: true - name: mypyc runtime tests with py38-macos python: '3.8.17' @@ -134,24 +141,6 @@ jobs: - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} - python-nightly: - runs-on: ubuntu-latest - name: Test suite with Python nightly - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.12-dev' - - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.4.4 - - name: Setup tox environment - run: tox run -e py --notest - - name: Test - run: tox run -e py --skip-pkg-install -- "-n 2" - continue-on-error: true - - name: Mark as a success - run: exit 0 - python_32bits: runs-on: ubuntu-latest name: Test mypyc suite with 32-bit Python From b78f4b536325f77995550f69a260398b8e579734 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 12 Jul 2023 22:59:53 +0300 Subject: [PATCH 0133/1617] Add runtime `__slots__` attribute to `attrs` (#15651) This is similar to https://github.com/python/mypy/pull/15649 but for `attrs` :) Refs https://github.com/python/mypy/issues/15647 --- mypy/plugins/attrs.py | 7 +++++++ test-data/unit/check-plugin-attrs.test | 27 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 7f1196c16801..0f748cc140e8 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -896,6 +896,13 @@ def _add_slots(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> # Unlike `@dataclasses.dataclass`, `__slots__` is rewritten here. ctx.cls.info.slots = {attr.name for attr in attributes} + # Also, inject `__slots__` attribute to class namespace: + slots_type = TupleType( + [ctx.api.named_type("builtins.str") for _ in attributes], + fallback=ctx.api.named_type("builtins.tuple"), + ) + add_attribute_to_class(api=ctx.api, cls=ctx.cls, name="__slots__", typ=slots_type) + def _add_match_args(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None: if ( diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 3d1e2d730af8..88a541c28ac2 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1676,6 +1676,33 @@ class C: self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.C" [builtins fixtures/plugin_attrs.pyi] +[case testRuntimeSlotsAttr] +from attr import dataclass + +@dataclass(slots=True) +class Some: + x: int + y: str + z: bool + +reveal_type(Some.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]" + +@dataclass(slots=True) +class Other: + x: int + y: str + +reveal_type(Other.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str]" + + +@dataclass +class NoSlots: + x: int + y: str + +NoSlots.__slots__ # E: "Type[NoSlots]" has no attribute "__slots__" +[builtins fixtures/plugin_attrs.pyi] + [case testAttrsWithMatchArgs] # flags: --python-version 3.10 import attr From 8a5d8f085185c41fce15ab108db236f1d94e5b62 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 13 Jul 2023 00:43:08 -0700 Subject: [PATCH 0134/1617] type_narrowing.rst: fix syntax, consistency (#15652) --- docs/source/type_narrowing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 72a816679140..4bc0fda70138 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -271,7 +271,7 @@ Generic TypeGuards else: reveal_type(names) # tuple[str, ...] -Typeguards with parameters +TypeGuards with parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~ Type guard functions can accept extra arguments: @@ -293,7 +293,7 @@ Type guard functions can accept extra arguments: TypeGuards as methods ~~~~~~~~~~~~~~~~~~~~~ - A method can also serve as the ``TypeGuard``: +A method can also serve as a ``TypeGuard``: .. code-block:: python From dfea43ff96976435ee5f37d1294cca792b8f26cf Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:37:27 +0200 Subject: [PATCH 0135/1617] Add error code "explicit-override" for strict @override mode (PEP 698) (#15512) Add the strict mode for [PEP 698](https://peps.python.org/pep-0698/#strict-enforcement-per-project). Closes: #14072 --- docs/source/class_basics.rst | 9 +- docs/source/error_code_list2.rst | 39 +++++ mypy/checker.py | 49 ++++-- mypy/errorcodes.py | 6 + mypy/messages.py | 10 ++ test-data/unit/check-functions.test | 182 ++++++++++++++++++-- test-data/unit/fixtures/typing-override.pyi | 25 +++ 7 files changed, 291 insertions(+), 29 deletions(-) create mode 100644 test-data/unit/fixtures/typing-override.pyi diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 82bbf00b830d..73f95f1c5658 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -210,7 +210,9 @@ override has a compatible signature: In order to ensure that your code remains correct when renaming methods, it can be helpful to explicitly mark a method as overriding a base -method. This can be done with the ``@override`` decorator. If the base +method. This can be done with the ``@override`` decorator. ``@override`` +can be imported from ``typing`` starting with Python 3.12 or from +``typing_extensions`` for use with older Python versions. If the base method is then renamed while the overriding method is not, mypy will show an error: @@ -233,6 +235,11 @@ show an error: def g(self, y: str) -> None: # Error: no corresponding base method found ... +.. note:: + + Use :ref:`--enable-error-code explicit-override ` to require + that method overrides use the ``@override`` decorator. Emit an error if it is missing. + You can also override a statically typed method with a dynamically typed one. This allows dynamically typed code to override methods defined in library classes without worrying about their type diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index e1d47f7cbec0..30fad0793771 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -442,3 +442,42 @@ Example: # The following will not generate an error on either # Python 3.8, or Python 3.9 42 + "testing..." # type: ignore + +.. _code-explicit-override: + +Check that ``@override`` is used when overriding a base class method [explicit-override] +---------------------------------------------------------------------------------------- + +If you use :option:`--enable-error-code explicit-override ` +mypy generates an error if you override a base class method without using the +``@override`` decorator. An error will not be emitted for overrides of ``__init__`` +or ``__new__``. See `PEP 698 `_. + +.. note:: + + Starting with Python 3.12, the ``@override`` decorator can be imported from ``typing``. + To use it with older Python versions, import it from ``typing_extensions`` instead. + +Example: + +.. code-block:: python + + # Use "mypy --enable-error-code explicit-override ..." + + from typing import override + + class Parent: + def f(self, x: int) -> None: + pass + + def g(self, y: int) -> None: + pass + + + class Child(Parent): + def f(self, x: int) -> None: # Error: Missing @override decorator + pass + + @override + def g(self, y: int) -> None: + pass diff --git a/mypy/checker.py b/mypy/checker.py index 5ed1c792778b..71c9746ce24f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -643,9 +643,14 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: if defn.impl: defn.impl.accept(self) if defn.info: - found_base_method = self.check_method_override(defn) - if defn.is_explicit_override and found_base_method is False: + found_method_base_classes = self.check_method_override(defn) + if ( + defn.is_explicit_override + and not found_method_base_classes + and found_method_base_classes is not None + ): self.msg.no_overridable_method(defn.name, defn) + self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl) self.check_inplace_operator_method(defn) if not defn.is_property: self.check_overlapping_overloads(defn) @@ -972,7 +977,8 @@ def _visit_func_def(self, defn: FuncDef) -> None: # overload, the legality of the override has already # been typechecked, and decorated methods will be # checked when the decorator is. - self.check_method_override(defn) + found_method_base_classes = self.check_method_override(defn) + self.check_explicit_override_decorator(defn, found_method_base_classes) self.check_inplace_operator_method(defn) if defn.original_def: # Override previous definition. @@ -1813,23 +1819,41 @@ def expand_typevars( else: return [(defn, typ)] - def check_method_override(self, defn: FuncDef | OverloadedFuncDef | Decorator) -> bool | None: + def check_explicit_override_decorator( + self, + defn: FuncDef | OverloadedFuncDef, + found_method_base_classes: list[TypeInfo] | None, + context: Context | None = None, + ) -> None: + if ( + found_method_base_classes + and not defn.is_explicit_override + and defn.name not in ("__init__", "__new__") + ): + self.msg.explicit_override_decorator_missing( + defn.name, found_method_base_classes[0].fullname, context or defn + ) + + def check_method_override( + self, defn: FuncDef | OverloadedFuncDef | Decorator + ) -> list[TypeInfo] | None: """Check if function definition is compatible with base classes. This may defer the method if a signature is not available in at least one base class. Return ``None`` if that happens. - Return ``True`` if an attribute with the method name was found in the base class. + Return a list of base classes which contain an attribute with the method name. """ # Check against definitions in base classes. - found_base_method = False + found_method_base_classes: list[TypeInfo] = [] for base in defn.info.mro[1:]: result = self.check_method_or_accessor_override_for_base(defn, base) if result is None: # Node was deferred, we will have another attempt later. return None - found_base_method |= result - return found_base_method + if result: + found_method_base_classes.append(base) + return found_method_base_classes def check_method_or_accessor_override_for_base( self, defn: FuncDef | OverloadedFuncDef | Decorator, base: TypeInfo @@ -4739,9 +4763,14 @@ def visit_decorator(self, e: Decorator) -> None: self.check_incompatible_property_override(e) # For overloaded functions we already checked override for overload as a whole. if e.func.info and not e.func.is_dynamic() and not e.is_overload: - found_base_method = self.check_method_override(e) - if e.func.is_explicit_override and found_base_method is False: + found_method_base_classes = self.check_method_override(e) + if ( + e.func.is_explicit_override + and not found_method_base_classes + and found_method_base_classes is not None + ): self.msg.no_overridable_method(e.func.name, e.func) + self.check_explicit_override_decorator(e.func, found_method_base_classes) if e.func.info and e.func.name in ("__init__", "__new__"): if e.type and not isinstance(get_proper_type(e.type), (FunctionLike, AnyType)): diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 68ae4b49a806..717629ad1f11 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -235,6 +235,12 @@ def __hash__(self) -> int: UNUSED_IGNORE: Final = ErrorCode( "unused-ignore", "Ensure that all type ignores are used", "General", default_enabled=False ) +EXPLICIT_OVERRIDE_REQUIRED: Final = ErrorCode( + "explicit-override", + "Require @override decorator if method is overriding a base class method", + "General", + default_enabled=False, +) # Syntax errors are often blocking. diff --git a/mypy/messages.py b/mypy/messages.py index 021ad2c7390c..ae7fba1473ac 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1525,6 +1525,16 @@ def no_overridable_method(self, name: str, context: Context) -> None: context, ) + def explicit_override_decorator_missing( + self, name: str, base_name: str, context: Context + ) -> None: + self.fail( + f'Method "{name}" is not using @override ' + f'but is overriding a method in class "{base_name}"', + context, + code=codes.EXPLICIT_OVERRIDE_REQUIRED, + ) + def final_cant_override_writable(self, name: str, ctx: Context) -> None: self.fail(f'Cannot override writable attribute "{name}" with a final one', ctx) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 141d18ae2666..0de4798ea1f5 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2759,8 +2759,7 @@ class E(D): pass class F(E): @override def f(self, x: int) -> str: pass -[typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] [case explicitOverrideStaticmethod] # flags: --python-version 3.12 @@ -2792,8 +2791,8 @@ class D(A): def f(x: str) -> str: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \ # N: This violates the Liskov substitution principle \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides -[typing fixtures/typing-full.pyi] -[builtins fixtures/callable.pyi] +[typing fixtures/typing-override.pyi] +[builtins fixtures/staticmethod.pyi] [case explicitOverrideClassmethod] # flags: --python-version 3.12 @@ -2825,8 +2824,8 @@ class D(A): def f(cls, x: str) -> str: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \ # N: This violates the Liskov substitution principle \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides -[typing fixtures/typing-full.pyi] -[builtins fixtures/callable.pyi] +[typing fixtures/typing-override.pyi] +[builtins fixtures/classmethod.pyi] [case explicitOverrideProperty] # flags: --python-version 3.12 @@ -2860,8 +2859,8 @@ class D(A): # N: str \ # N: Subclass: \ # N: int +[typing fixtures/typing-override.pyi] [builtins fixtures/property.pyi] -[typing fixtures/typing-full.pyi] [case explicitOverrideSettableProperty] # flags: --python-version 3.12 @@ -2898,8 +2897,8 @@ class D(A): @f.setter def f(self, value: int) -> None: pass +[typing fixtures/typing-override.pyi] [builtins fixtures/property.pyi] -[typing fixtures/typing-full.pyi] [case invalidExplicitOverride] # flags: --python-version 3.12 @@ -2914,8 +2913,7 @@ class A: pass def g() -> None: @override # E: "override" used with a non-method def h(b: bool) -> int: pass -[typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] [case explicitOverrideSpecialMethods] # flags: --python-version 3.12 @@ -2931,8 +2929,7 @@ class B(A): class C: @override def __init__(self, a: int) -> None: pass -[typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] [case explicitOverrideFromExtensions] from typing_extensions import override @@ -2943,7 +2940,6 @@ class A: class B(A): @override def f2(self, x: int) -> str: pass # E: Method "f2" is marked as an override, but no base method was found with this name -[typing fixtures/typing-full.pyi] [builtins fixtures/tuple.pyi] [case explicitOverrideOverloads] @@ -2960,8 +2956,7 @@ class B(A): def f2(self, x: str) -> str: pass @override def f2(self, x: int | str) -> str: pass -[typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] [case explicitOverrideNotOnOverloadsImplementation] # flags: --python-version 3.12 @@ -2985,8 +2980,7 @@ class C(A): @overload def f(self, y: str) -> str: pass def f(self, y: int | str) -> str: pass -[typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] [case explicitOverrideOnMultipleOverloads] # flags: --python-version 3.12 @@ -3012,5 +3006,157 @@ class C(A): def f(self, y: str) -> str: pass @override def f(self, y: int | str) -> str: pass -[typing fixtures/typing-full.pyi] +[typing fixtures/typing-override.pyi] + +[case explicitOverrideCyclicDependency] +# flags: --python-version 3.12 +import b +[file a.py] +from typing import override +import b +import c + +class A(b.B): + @override # This is fine + @c.deco + def meth(self) -> int: ... +[file b.py] +import a +import c + +class B: + @c.deco + def meth(self) -> int: ... +[file c.py] +from typing import TypeVar, Tuple, Callable +T = TypeVar('T') +def deco(f: Callable[..., T]) -> Callable[..., Tuple[T, int]]: ... [builtins fixtures/tuple.pyi] +[typing fixtures/typing-override.pyi] + +[case requireExplicitOverrideMethod] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import override + +class A: + def f(self, x: int) -> str: pass + +class B(A): + @override + def f(self, y: int) -> str: pass + +class C(A): + def f(self, y: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" + +class D(B): + def f(self, y: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.B" +[typing fixtures/typing-override.pyi] + +[case requireExplicitOverrideSpecialMethod] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import Callable, Self, TypeVar, override, overload + +T = TypeVar('T') +def some_decorator(f: Callable[..., T]) -> Callable[..., T]: ... + +# Don't require override decorator for __init__ and __new__ +# See: https://github.com/python/typing/issues/1376 +class A: + def __init__(self) -> None: pass + def __new__(cls) -> Self: pass + +class B(A): + def __init__(self) -> None: pass + def __new__(cls) -> Self: pass + +class C(A): + @some_decorator + def __init__(self) -> None: pass + + @some_decorator + def __new__(cls) -> Self: pass + +class D(A): + @overload + def __init__(self, x: int) -> None: ... + @overload + def __init__(self, x: str) -> None: ... + def __init__(self, x): pass + + @overload + def __new__(cls, x: int) -> Self: pass + @overload + def __new__(cls, x: str) -> Self: pass + def __new__(cls, x): pass +[typing fixtures/typing-override.pyi] + +[case requireExplicitOverrideProperty] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import override + +class A: + @property + def prop(self) -> int: pass + +class B(A): + @override + @property + def prop(self) -> int: pass + +class C(A): + @property + def prop(self) -> int: pass # E: Method "prop" is not using @override but is overriding a method in class "__main__.A" +[typing fixtures/typing-override.pyi] +[builtins fixtures/property.pyi] + +[case requireExplicitOverrideOverload] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import overload, override + +class A: + @overload + def f(self, x: int) -> str: ... + @overload + def f(self, x: str) -> str: ... + def f(self, x): pass + +class B(A): + @overload + def f(self, y: int) -> str: ... + @overload + def f(self, y: str) -> str: ... + @override + def f(self, y): pass + +class C(A): + @overload + @override + def f(self, y: int) -> str: ... + @overload + def f(self, y: str) -> str: ... + def f(self, y): pass + +class D(A): + @overload + def f(self, y: int) -> str: ... + @overload + def f(self, y: str) -> str: ... + def f(self, y): pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" +[typing fixtures/typing-override.pyi] + +[case requireExplicitOverrideMultipleInheritance] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import override + +class A: + def f(self, x: int) -> str: pass +class B: + def f(self, y: int) -> str: pass + +class C(A, B): + @override + def f(self, z: int) -> str: pass + +class D(A, B): + def f(self, z: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" +[typing fixtures/typing-override.pyi] diff --git a/test-data/unit/fixtures/typing-override.pyi b/test-data/unit/fixtures/typing-override.pyi new file mode 100644 index 000000000000..606ca63d4f0d --- /dev/null +++ b/test-data/unit/fixtures/typing-override.pyi @@ -0,0 +1,25 @@ +TypeVar = 0 +Generic = 0 +Any = 0 +overload = 0 +Type = 0 +Literal = 0 +Optional = 0 +Self = 0 +Tuple = 0 +ClassVar = 0 +Callable = 0 + +T = TypeVar('T') +T_co = TypeVar('T_co', covariant=True) +KT = TypeVar('KT') + +class Iterable(Generic[T_co]): pass +class Iterator(Iterable[T_co]): pass +class Sequence(Iterable[T_co]): pass +class Mapping(Iterable[KT], Generic[KT, T_co]): + def keys(self) -> Iterable[T]: pass # Approximate return type + def __getitem__(self, key: T) -> T_co: pass + + +def override(__arg: T) -> T: ... From 3bf85217386806b0f68bf8857b61379ae2f6ad1e Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 13 Jul 2023 06:42:39 -0700 Subject: [PATCH 0136/1617] Consistently avoid type-checking unreachable code (#15386) - On module-level, now we'll skip remaining statements once unreachable. This brings the behavior in line with function-level behavior. - For module and function code, if `--warn-unreachable` is enabled, we'll emit an error, just once, on the first unreachable statement that's not a no-op statement. Previously a no-op statement would not have the "Unreachable statement" error, but the subsequent statements did not have the error either, e.g. ```diff raise Exception assert False # no error since it's a "no-op statement" -foo = 42 +foo = 42 # E: Unreachable statement spam = "ham" # no error since we warn just once ``` --- mypy/checker.py | 29 ++++---- mypyc/test-data/run-misc.test | 23 ++---- test-data/unit/check-classes.test | 27 ++++--- test-data/unit/check-fastparse.test | 2 +- test-data/unit/check-incremental.test | 6 +- test-data/unit/check-inference-context.test | 6 +- test-data/unit/check-native-int.test | 12 ++-- test-data/unit/check-statements.test | 78 ++++++++++++++------- test-data/unit/check-typevar-tuple.test | 3 +- test-data/unit/check-unreachable-code.test | 8 +-- test-data/unit/fine-grained.test | 6 +- 11 files changed, 120 insertions(+), 80 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 71c9746ce24f..e2ff8a6ec2a4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -464,14 +464,14 @@ def check_first_pass(self) -> None: with self.tscope.module_scope(self.tree.fullname): with self.enter_partial_types(), self.binder.top_frame_context(): for d in self.tree.defs: - if ( - self.binder.is_unreachable() - and self.should_report_unreachable_issues() - and not self.is_raising_or_empty(d) - ): - self.msg.unreachable_statement(d) - break - self.accept(d) + if self.binder.is_unreachable(): + if not self.should_report_unreachable_issues(): + break + if not self.is_noop_for_reachability(d): + self.msg.unreachable_statement(d) + break + else: + self.accept(d) assert not self.current_node_deferred @@ -2706,10 +2706,13 @@ def visit_block(self, b: Block) -> None: return for s in b.body: if self.binder.is_unreachable(): - if self.should_report_unreachable_issues() and not self.is_raising_or_empty(s): + if not self.should_report_unreachable_issues(): + break + if not self.is_noop_for_reachability(s): self.msg.unreachable_statement(s) - break - self.accept(s) + break + else: + self.accept(s) def should_report_unreachable_issues(self) -> bool: return ( @@ -2719,11 +2722,11 @@ def should_report_unreachable_issues(self) -> bool: and not self.binder.is_unreachable_warning_suppressed() ) - def is_raising_or_empty(self, s: Statement) -> bool: + def is_noop_for_reachability(self, s: Statement) -> bool: """Returns 'true' if the given statement either throws an error of some kind or is a no-op. - We use this function mostly while handling the '--warn-unreachable' flag. When + We use this function while handling the '--warn-unreachable' flag. When that flag is present, we normally report an error on any unreachable statement. But if that statement is just something like a 'pass' or a just-in-case 'assert False', reporting an error would be annoying. diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index fd0eb5022236..c40e0fc55f0e 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -1108,25 +1108,14 @@ assert not C # make the initial import fail assert False -class C: - def __init__(self): - self.x = 1 - self.y = 2 -def test() -> None: - a = C() [file driver.py] # load native, cause PyInit to be run, create the module but don't finish initializing the globals -try: - import native -except: - pass -try: - # try accessing those globals that were never properly initialized - import native - native.test() -# should fail with AssertionError due to `assert False` in other function -except AssertionError: - pass +for _ in range(2): + try: + import native + raise RuntimeError('exception expected') + except AssertionError: + pass [case testRepeatedUnderscoreFunctions] def _(arg): pass diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 69227e50f6fa..957eb9214d7c 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7725,10 +7725,14 @@ class D: def __new__(cls) -> NoReturn: ... def __init__(self) -> NoReturn: ... -reveal_type(A()) # N: Revealed type is "" -reveal_type(B()) # N: Revealed type is "" -reveal_type(C()) # N: Revealed type is "" -reveal_type(D()) # N: Revealed type is "" +if object(): + reveal_type(A()) # N: Revealed type is "" +if object(): + reveal_type(B()) # N: Revealed type is "" +if object(): + reveal_type(C()) # N: Revealed type is "" +if object(): + reveal_type(D()) # N: Revealed type is "" [case testOverloadedNewAndInitNoReturn] from typing import NoReturn, overload @@ -7767,13 +7771,20 @@ class D: def __init__(self, a: int) -> None: ... def __init__(self, a: int = ...) -> None: ... -reveal_type(A()) # N: Revealed type is "" +if object(): + reveal_type(A()) # N: Revealed type is "" reveal_type(A(1)) # N: Revealed type is "__main__.A" -reveal_type(B()) # N: Revealed type is "" + +if object(): + reveal_type(B()) # N: Revealed type is "" reveal_type(B(1)) # N: Revealed type is "__main__.B" -reveal_type(C()) # N: Revealed type is "" + +if object(): + reveal_type(C()) # N: Revealed type is "" reveal_type(C(1)) # N: Revealed type is "__main__.C" -reveal_type(D()) # N: Revealed type is "" + +if object(): + reveal_type(D()) # N: Revealed type is "" reveal_type(D(1)) # N: Revealed type is "__main__.D" [case testClassScopeImportWithWrapperAndError] diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test index 2e4473c2716b..132a34503b89 100644 --- a/test-data/unit/check-fastparse.test +++ b/test-data/unit/check-fastparse.test @@ -228,8 +228,8 @@ def g(): # E: Type signature has too many arguments assert 1, 2 assert (1, 2) # E: Assertion is always true, perhaps remove parentheses? assert (1, 2), 3 # E: Assertion is always true, perhaps remove parentheses? -assert () assert (1,) # E: Assertion is always true, perhaps remove parentheses? +assert () [builtins fixtures/tuple.pyi] [case testFastParseAssertMessage] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 5203b0828122..cd009887a5b5 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5413,7 +5413,8 @@ reveal_type(z) [out] tmp/c.py:2: note: Revealed type is "a." [out2] -tmp/c.py:2: note: Revealed type is "a.A" +tmp/b.py:2: error: Cannot determine type of "y" +tmp/c.py:2: note: Revealed type is "Any" [case testIsInstanceAdHocIntersectionIncrementalUnreachaableToIntersection] import c @@ -5444,7 +5445,8 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "a.A" +tmp/b.py:2: error: Cannot determine type of "y" +tmp/c.py:2: note: Revealed type is "Any" [out2] tmp/c.py:2: note: Revealed type is "a." diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 13cfec46eeab..59f515490964 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -625,8 +625,10 @@ reveal_type((lambda x, y: x + y)(1, 2)) # N: Revealed type is "builtins.int" reveal_type((lambda s, i: s)(i=0, s='x')) # N: Revealed type is "Literal['x']?" reveal_type((lambda s, i: i)(i=0, s='x')) # N: Revealed type is "Literal[0]?" reveal_type((lambda x, s, i: x)(1.0, i=0, s='x')) # N: Revealed type is "builtins.float" -(lambda x, s, i: x)() # E: Too few arguments -(lambda: 0)(1) # E: Too many arguments +if object(): + (lambda x, s, i: x)() # E: Too few arguments +if object(): + (lambda: 0)(1) # E: Too many arguments -- varargs are not handled, but it should not crash reveal_type((lambda *k, s, i: i)(type, i=0, s='x')) # N: Revealed type is "Any" reveal_type((lambda s, *k, i: i)(i=0, s='x')) # N: Revealed type is "Any" diff --git a/test-data/unit/check-native-int.test b/test-data/unit/check-native-int.test index 1e945d0af27d..1129512694f4 100644 --- a/test-data/unit/check-native-int.test +++ b/test-data/unit/check-native-int.test @@ -87,8 +87,10 @@ reveal_type(meet(f32, f)) # N: Revealed type is "mypy_extensions.i32" reveal_type(meet(f, f32)) # N: Revealed type is "mypy_extensions.i32" reveal_type(meet(f64, f)) # N: Revealed type is "mypy_extensions.i64" reveal_type(meet(f, f64)) # N: Revealed type is "mypy_extensions.i64" -reveal_type(meet(f32, f64)) # N: Revealed type is "" -reveal_type(meet(f64, f32)) # N: Revealed type is "" +if object(): + reveal_type(meet(f32, f64)) # N: Revealed type is "" +if object(): + reveal_type(meet(f64, f32)) # N: Revealed type is "" reveal_type(meet(f, fa)) # N: Revealed type is "builtins.int" reveal_type(meet(f32, fa)) # N: Revealed type is "mypy_extensions.i32" @@ -148,8 +150,10 @@ def meet(c1: Callable[[T], None], c2: Callable[[T], None]) -> T: def ff(x: float) -> None: pass def fi32(x: i32) -> None: pass -reveal_type(meet(ff, fi32)) # N: Revealed type is "" -reveal_type(meet(fi32, ff)) # N: Revealed type is "" +if object(): + reveal_type(meet(ff, fi32)) # N: Revealed type is "" +if object(): + reveal_type(meet(fi32, ff)) # N: Revealed type is "" [builtins fixtures/dict.pyi] [case testNativeIntForLoopRange] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 8a232d52968f..023e2935a158 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -409,11 +409,16 @@ main:5: error: Exception must be derived from BaseException class A: pass class MyError(BaseException): pass def f(): pass -raise BaseException -raise MyError -raise A # E: Exception must be derived from BaseException -raise object # E: Exception must be derived from BaseException -raise f # E: Exception must be derived from BaseException +if object(): + raise BaseException +if object(): + raise MyError +if object(): + raise A # E: Exception must be derived from BaseException +if object(): + raise object # E: Exception must be derived from BaseException +if object(): + raise f # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] [case testRaiseClassObjectCustomInit] @@ -429,18 +434,30 @@ class MyKwError(Exception): class MyErrorWithDefault(Exception): def __init__(self, optional=1) -> None: ... -raise BaseException -raise Exception -raise BaseException(1) -raise Exception(2) -raise MyBaseError(4) -raise MyError(5, 6) -raise MyKwError(kwonly=7) -raise MyErrorWithDefault(8) -raise MyErrorWithDefault -raise MyBaseError # E: Too few arguments for "MyBaseError" -raise MyError # E: Too few arguments for "MyError" -raise MyKwError # E: Missing named argument "kwonly" for "MyKwError" +if object(): + raise BaseException +if object(): + raise Exception +if object(): + raise BaseException(1) +if object(): + raise Exception(2) +if object(): + raise MyBaseError(4) +if object(): + raise MyError(5, 6) +if object(): + raise MyKwError(kwonly=7) +if object(): + raise MyErrorWithDefault(8) +if object(): + raise MyErrorWithDefault +if object(): + raise MyBaseError # E: Too few arguments for "MyBaseError" +if object(): + raise MyError # E: Too few arguments for "MyError" +if object(): + raise MyKwError # E: Missing named argument "kwonly" for "MyKwError" [builtins fixtures/exception.pyi] [case testRaiseExceptionType] @@ -473,10 +490,14 @@ f: MyError a: A x: BaseException del x -raise e from a # E: Exception must be derived from BaseException -raise e from e -raise e from f -raise e from x # E: Trying to read deleted variable "x" +if object(): + raise e from a # E: Exception must be derived from BaseException +if object(): + raise e from e +if object(): + raise e from f +if object(): + raise e from x # E: Trying to read deleted variable "x" class A: pass class MyError(BaseException): pass [builtins fixtures/exception.pyi] @@ -486,11 +507,16 @@ import typing class A: pass class MyError(BaseException): pass def f(): pass -raise BaseException from BaseException -raise BaseException from MyError -raise BaseException from A # E: Exception must be derived from BaseException -raise BaseException from object # E: Exception must be derived from BaseException -raise BaseException from f # E: Exception must be derived from BaseException +if object(): + raise BaseException from BaseException +if object(): + raise BaseException from MyError +if object(): + raise BaseException from A # E: Exception must be derived from BaseException +if object(): + raise BaseException from object # E: Exception must be derived from BaseException +if object(): + raise BaseException from f # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] [case testTryFinallyStatement] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index e1fae05eac63..1024f90ee6b7 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -17,7 +17,8 @@ reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected +if object(): + f(0) # E: Argument 1 to "f" has incompatible type "int"; expected def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: return a diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index b2fd44043435..1db2a16e2e1c 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -873,15 +873,15 @@ def expect_str(x: str) -> str: pass x: int if False: assert False - reveal_type(x) + reveal_type(x) # E: Statement is unreachable if False: raise Exception() - reveal_type(x) + reveal_type(x) # E: Statement is unreachable if False: assert_never(x) - reveal_type(x) + reveal_type(x) # E: Statement is unreachable if False: nonthrowing_assert_never(x) # E: Statement is unreachable @@ -890,7 +890,7 @@ if False: if False: # Ignore obvious type errors assert_never(expect_str(x)) - reveal_type(x) + reveal_type(x) # E: Statement is unreachable [builtins fixtures/exception.pyi] [case testNeverVariants] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 7d854bab424c..11a8f03590f7 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9667,7 +9667,8 @@ reveal_type(z) [out] c.py:2: note: Revealed type is "a." == -c.py:2: note: Revealed type is "a.A" +c.py:2: note: Revealed type is "Any" +b.py:2: error: Cannot determine type of "y" [case testIsInstanceAdHocIntersectionFineGrainedIncrementalUnreachaableToIntersection] import c @@ -9698,7 +9699,8 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "a.A" +b.py:2: error: Cannot determine type of "y" +c.py:2: note: Revealed type is "Any" == c.py:2: note: Revealed type is "a." From 39833810ddcd29561f3ffed44703380aa26a68be Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 13 Jul 2023 12:27:28 -0700 Subject: [PATCH 0137/1617] Fix testLiteralMeets failure (#15659) --- test-data/unit/check-literal.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 104dd4d144e0..abdbf733a679 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1797,7 +1797,8 @@ def f5(x: Literal[1], y: Union[Literal[1], Literal[2]]) -> None: pass def f6(x: Optional[Literal[1]], y: Optional[Literal[2]]) -> None: pass reveal_type(unify(f1)) # N: Revealed type is "Literal[1]" -reveal_type(unify(f2)) # N: Revealed type is "" +if object(): + reveal_type(unify(f2)) # N: Revealed type is "" reveal_type(unify(f3)) # N: Revealed type is "Literal[1]" reveal_type(unify(f4)) # N: Revealed type is "Literal[1]" reveal_type(unify(f5)) # N: Revealed type is "Literal[1]" From 2ebd51e881490f4d20635cde92ef9e3edbbad68c Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 14 Jul 2023 00:40:02 +0300 Subject: [PATCH 0138/1617] Teach `stubgen` to work with `complex` and unary expressions (#15661) --- mypy/stubgen.py | 46 +++++++++++++++++++++++++++++++++-- test-data/unit/stubgen.test | 48 ++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index e7fa65d7f949..9084da2053cf 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -80,6 +80,7 @@ CallExpr, ClassDef, ComparisonExpr, + ComplexExpr, Decorator, DictExpr, EllipsisExpr, @@ -1396,6 +1397,8 @@ def is_private_member(self, fullname: str) -> bool: def get_str_type_of_node( self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True ) -> str: + rvalue = self.maybe_unwrap_unary_expr(rvalue) + if isinstance(rvalue, IntExpr): return "int" if isinstance(rvalue, StrExpr): @@ -1404,8 +1407,13 @@ def get_str_type_of_node( return "bytes" if isinstance(rvalue, FloatExpr): return "float" - if isinstance(rvalue, UnaryExpr) and isinstance(rvalue.expr, IntExpr): - return "int" + if isinstance(rvalue, ComplexExpr): # 1j + return "complex" + if isinstance(rvalue, OpExpr) and rvalue.op in ("-", "+"): # -1j + 1 + if isinstance(self.maybe_unwrap_unary_expr(rvalue.left), ComplexExpr) or isinstance( + self.maybe_unwrap_unary_expr(rvalue.right), ComplexExpr + ): + return "complex" if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"): return "bool" if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None": @@ -1417,6 +1425,40 @@ def get_str_type_of_node( else: return "" + def maybe_unwrap_unary_expr(self, expr: Expression) -> Expression: + """Unwrap (possibly nested) unary expressions. + + But, some unary expressions can change the type of expression. + While we want to preserve it. For example, `~True` is `int`. + So, we only allow a subset of unary expressions to be unwrapped. + """ + if not isinstance(expr, UnaryExpr): + return expr + + # First, try to unwrap `[+-]+ (int|float|complex)` expr: + math_ops = ("+", "-") + if expr.op in math_ops: + while isinstance(expr, UnaryExpr): + if expr.op not in math_ops or not isinstance( + expr.expr, (IntExpr, FloatExpr, ComplexExpr, UnaryExpr) + ): + break + expr = expr.expr + return expr + + # Next, try `not bool` expr: + if expr.op == "not": + while isinstance(expr, UnaryExpr): + if expr.op != "not" or not isinstance(expr.expr, (NameExpr, UnaryExpr)): + break + if isinstance(expr.expr, NameExpr) and expr.expr.name not in ("True", "False"): + break + expr = expr.expr + return expr + + # This is some other unary expr, we cannot do anything with it (yet?). + return expr + def print_annotation(self, t: Type) -> str: printer = AnnotationPrinter(self) return t.accept(printer) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 9c7221e7ec54..b387aa840dc9 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -127,10 +127,52 @@ class A: def g() -> None: ... -[case testVariable] -x = 1 +[case testVariables] +i = 1 +s = 'a' +f = 1.5 +c1 = 1j +c2 = 0j + 1 +bl1 = True +bl2 = False +bts = b'' +[out] +i: int +s: str +f: float +c1: complex +c2: complex +bl1: bool +bl2: bool +bts: bytes + +[case testVariablesWithUnary] +i = +-1 +f = -1.5 +c1 = -1j +c2 = -1j + 1 +bl1 = not True +bl2 = not not False +[out] +i: int +f: float +c1: complex +c2: complex +bl1: bool +bl2: bool + +[case testVariablesWithUnaryWrong] +i = not +1 +bl1 = -True +bl2 = not -False +bl3 = -(not False) [out] -x: int +from _typeshed import Incomplete + +i: Incomplete +bl1: Incomplete +bl2: Incomplete +bl3: Incomplete [case testAnnotatedVariable] x: int = 1 From 7a9418356082092d2cb1585acb816b2074cff43e Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Fri, 14 Jul 2023 02:58:23 -0700 Subject: [PATCH 0139/1617] Fix dataclass/protocol crash on joining types (#15629) The root cause is hacky creation of incomplete symbols; instead switching to `add_method_to_class` which does the necessary housekeeping. Fixes #15618. --- mypy/checker.py | 7 +- mypy/plugins/dataclasses.py | 137 +++++++++++--------------- test-data/unit/check-dataclasses.test | 23 +++++ test-data/unit/deps.test | 4 +- 4 files changed, 84 insertions(+), 87 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e2ff8a6ec2a4..f2873c7d58e4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1200,9 +1200,10 @@ def check_func_def( elif isinstance(arg_type, TypeVarType): # Refuse covariant parameter type variables # TODO: check recursively for inner type variables - if arg_type.variance == COVARIANT and defn.name not in ( - "__init__", - "__new__", + if ( + arg_type.variance == COVARIANT + and defn.name not in ("__init__", "__new__", "__post_init__") + and not is_private(defn.name) # private methods are not inherited ): ctx: Context = arg_type if ctx.line < 0: diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index b1dc016a0279..a4babe7faf61 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterator +from typing import TYPE_CHECKING, Final, Iterator, Literal from mypy import errorcodes, message_registry from mypy.expandtype import expand_type, expand_type_by_instance @@ -86,7 +86,7 @@ field_specifiers=("dataclasses.Field", "dataclasses.field"), ) _INTERNAL_REPLACE_SYM_NAME: Final = "__mypy-replace" -_INTERNAL_POST_INIT_SYM_NAME: Final = "__mypy-__post_init__" +_INTERNAL_POST_INIT_SYM_NAME: Final = "__mypy-post_init" class DataclassAttribute: @@ -118,14 +118,33 @@ def __init__( self.is_neither_frozen_nor_nonfrozen = is_neither_frozen_nor_nonfrozen self._api = api - def to_argument(self, current_info: TypeInfo) -> Argument: - arg_kind = ARG_POS - if self.kw_only and self.has_default: - arg_kind = ARG_NAMED_OPT - elif self.kw_only and not self.has_default: - arg_kind = ARG_NAMED - elif not self.kw_only and self.has_default: - arg_kind = ARG_OPT + def to_argument( + self, current_info: TypeInfo, *, of: Literal["__init__", "replace", "__post_init__"] + ) -> Argument: + if of == "__init__": + arg_kind = ARG_POS + if self.kw_only and self.has_default: + arg_kind = ARG_NAMED_OPT + elif self.kw_only and not self.has_default: + arg_kind = ARG_NAMED + elif not self.kw_only and self.has_default: + arg_kind = ARG_OPT + elif of == "replace": + arg_kind = ARG_NAMED if self.is_init_var and not self.has_default else ARG_NAMED_OPT + elif of == "__post_init__": + # We always use `ARG_POS` without a default value, because it is practical. + # Consider this case: + # + # @dataclass + # class My: + # y: dataclasses.InitVar[str] = 'a' + # def __post_init__(self, y: str) -> None: ... + # + # We would be *required* to specify `y: str = ...` if default is added here. + # But, most people won't care about adding default values to `__post_init__`, + # because it is not designed to be called directly, and duplicating default values + # for the sake of type-checking is unpleasant. + arg_kind = ARG_POS return Argument( variable=self.to_var(current_info), type_annotation=self.expand_type(current_info), @@ -236,7 +255,7 @@ def transform(self) -> bool: and attributes ): args = [ - attr.to_argument(info) + attr.to_argument(info, of="__init__") for attr in attributes if attr.is_in_init and not self._is_kw_only_type(attr.type) ] @@ -375,70 +394,26 @@ def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass to be used later whenever 'dataclasses.replace' is called for this dataclass. """ - arg_types: list[Type] = [] - arg_kinds = [] - arg_names: list[str | None] = [] - - info = self._cls.info - for attr in attributes: - attr_type = attr.expand_type(info) - assert attr_type is not None - arg_types.append(attr_type) - arg_kinds.append( - ARG_NAMED if attr.is_init_var and not attr.has_default else ARG_NAMED_OPT - ) - arg_names.append(attr.name) - - signature = CallableType( - arg_types=arg_types, - arg_kinds=arg_kinds, - arg_names=arg_names, - ret_type=NoneType(), - fallback=self._api.named_type("builtins.function"), - ) - - info.names[_INTERNAL_REPLACE_SYM_NAME] = SymbolTableNode( - kind=MDEF, node=FuncDef(typ=signature), plugin_generated=True + add_method_to_class( + self._api, + self._cls, + _INTERNAL_REPLACE_SYM_NAME, + args=[attr.to_argument(self._cls.info, of="replace") for attr in attributes], + return_type=NoneType(), + is_staticmethod=True, ) def _add_internal_post_init_method(self, attributes: list[DataclassAttribute]) -> None: - arg_types: list[Type] = [fill_typevars(self._cls.info)] - arg_kinds = [ARG_POS] - arg_names: list[str | None] = ["self"] - - info = self._cls.info - for attr in attributes: - if not attr.is_init_var: - continue - attr_type = attr.expand_type(info) - assert attr_type is not None - arg_types.append(attr_type) - # We always use `ARG_POS` without a default value, because it is practical. - # Consider this case: - # - # @dataclass - # class My: - # y: dataclasses.InitVar[str] = 'a' - # def __post_init__(self, y: str) -> None: ... - # - # We would be *required* to specify `y: str = ...` if default is added here. - # But, most people won't care about adding default values to `__post_init__`, - # because it is not designed to be called directly, and duplicating default values - # for the sake of type-checking is unpleasant. - arg_kinds.append(ARG_POS) - arg_names.append(attr.name) - - signature = CallableType( - arg_types=arg_types, - arg_kinds=arg_kinds, - arg_names=arg_names, - ret_type=NoneType(), - fallback=self._api.named_type("builtins.function"), - name="__post_init__", - ) - - info.names[_INTERNAL_POST_INIT_SYM_NAME] = SymbolTableNode( - kind=MDEF, node=FuncDef(typ=signature), plugin_generated=True + add_method_to_class( + self._api, + self._cls, + _INTERNAL_POST_INIT_SYM_NAME, + args=[ + attr.to_argument(self._cls.info, of="__post_init__") + for attr in attributes + if attr.is_init_var + ], + return_type=NoneType(), ) def add_slots( @@ -1120,20 +1095,18 @@ def is_processed_dataclass(info: TypeInfo | None) -> bool: def check_post_init(api: TypeChecker, defn: FuncItem, info: TypeInfo) -> None: if defn.type is None: return - - ideal_sig = info.get_method(_INTERNAL_POST_INIT_SYM_NAME) - if ideal_sig is None or ideal_sig.type is None: - return - - # We set it ourself, so it is always fine: - assert isinstance(ideal_sig.type, ProperType) - assert isinstance(ideal_sig.type, FunctionLike) - # Type of `FuncItem` is always `FunctionLike`: assert isinstance(defn.type, FunctionLike) + ideal_sig_method = info.get_method(_INTERNAL_POST_INIT_SYM_NAME) + assert ideal_sig_method is not None and ideal_sig_method.type is not None + ideal_sig = ideal_sig_method.type + assert isinstance(ideal_sig, ProperType) # we set it ourselves + assert isinstance(ideal_sig, CallableType) + ideal_sig = ideal_sig.copy_modified(name="__post_init__") + api.check_override( override=defn.type, - original=ideal_sig.type, + original=ideal_sig, name="__post_init__", name_in_super="__post_init__", supertype="dataclass", diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 131521aa98e4..adcaa60a5b19 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -744,6 +744,17 @@ s: str = a.bar() # E: Incompatible types in assignment (expression has type "in [builtins fixtures/dataclasses.pyi] +[case testDataclassGenericCovariant] +from dataclasses import dataclass +from typing import Generic, TypeVar + +T_co = TypeVar("T_co", covariant=True) + +@dataclass +class MyDataclass(Generic[T_co]): + a: T_co + +[builtins fixtures/dataclasses.pyi] [case testDataclassUntypedGenericInheritance] # flags: --python-version 3.7 @@ -2449,3 +2460,15 @@ class Test(Protocol): def reset(self) -> None: self.x = DEFAULT [builtins fixtures/dataclasses.pyi] + +[case testProtocolNoCrashOnJoining] +from dataclasses import dataclass +from typing import Protocol + +@dataclass +class MyDataclass(Protocol): ... + +a: MyDataclass +b = [a, a] # trigger joining the types + +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index fe5107b1529d..b43a2ace5eed 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1388,7 +1388,7 @@ class B(A): -> , m -> -> , m.B.__init__ - -> + -> , m.B.__mypy-replace -> -> -> @@ -1420,7 +1420,7 @@ class B(A): -> -> , m.B.__init__ -> - -> + -> , m.B.__mypy-replace -> -> -> From 211d74252b506bf0aa7ced5053428004a57c2ae9 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:13:38 +0300 Subject: [PATCH 0140/1617] Update dev version to 1.6.0+dev (#15671) Created the [release-1.5 branch](https://github.com/python/mypy/tree/release-1.5), updating version --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 42cda2fc7794..512890ce7d2b 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.5.0+dev" +__version__ = "1.6.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From a538cc98d54031f25e44787a90649ea909877f12 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:33:32 +0300 Subject: [PATCH 0141/1617] fix cherry-pick-typeshed (#15672) It should exclude test_cases too --- misc/cherry-pick-typeshed.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/cherry-pick-typeshed.py b/misc/cherry-pick-typeshed.py index af08009c2a8f..7e3b8b56e65f 100644 --- a/misc/cherry-pick-typeshed.py +++ b/misc/cherry-pick-typeshed.py @@ -53,6 +53,7 @@ def main() -> None: "--index", "--directory=mypy/typeshed", "--exclude=**/tests/**", + "--exclude=**/test_cases/**", diff_file, ], check=True, From 1958cb62f4de7492fb154323f3fdb7a0b6b51fa7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:18:54 +0200 Subject: [PATCH 0142/1617] Remove `--py2` argument (#15670) --- mypy/defaults.py | 2 -- mypy/main.py | 8 -------- mypy/test/helpers.py | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/mypy/defaults.py b/mypy/defaults.py index 2a881975a27c..1bd87de74bc9 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -3,8 +3,6 @@ import os from typing import Final -PYTHON2_VERSION: Final = (2, 7) - # Earliest fully supported Python 3.x version. Used as the default Python # version in tests. Mypy wheels should be built starting with this version, # and CI tests should be run on this version (and later versions). diff --git a/mypy/main.py b/mypy/main.py index f6e617e4d84f..6173fd6fc1a8 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -594,14 +594,6 @@ def add_invertible_flag( help="Type check code assuming it will be running on Python x.y", dest="special-opts:python_version", ) - platform_group.add_argument( - "-2", - "--py2", - dest="special-opts:python_version", - action="store_const", - const=defaults.PYTHON2_VERSION, - help="Use Python 2 mode (same as --python-version 2.7)", - ) platform_group.add_argument( "--platform", action="store", diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index d2c92614048a..d1850219e60a 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -345,7 +345,7 @@ def parse_options( options.force_union_syntax = 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): + if all(flag.split("=")[0] != "--python-version" for flag in flag_list): options.python_version = testfile_pyversion(testcase.file) if testcase.config.getoption("--mypy-verbose"): From 14743a1cdd2a07ecc56ce01cc9d54130fb32931e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 15 Jul 2023 10:51:04 +0300 Subject: [PATCH 0143/1617] Bump minimum Python type check target version to 3.7 (#15668) --- mypy/checkexpr.py | 2 +- mypy/checkstrformat.py | 15 ------ mypy/defaults.py | 2 +- mypy/messages.py | 13 +---- mypy/semanal.py | 18 ++----- mypy/semanal_namedtuple.py | 3 -- mypy/semanal_pass1.py | 7 ++- test-data/unit/README.md | 2 +- test-data/unit/check-async-await.test | 40 ++------------- test-data/unit/check-class-namedtuple.test | 58 ++-------------------- test-data/unit/check-fastparse.test | 1 - test-data/unit/check-flags.test | 12 ++--- test-data/unit/check-formatting.test | 7 --- test-data/unit/check-generic-alias.test | 4 -- test-data/unit/check-generics.test | 2 +- test-data/unit/check-incremental.test | 1 - test-data/unit/check-inference.test | 1 - test-data/unit/check-modules.test | 21 -------- test-data/unit/check-namedtuple.test | 13 +---- test-data/unit/check-narrowing.test | 1 - test-data/unit/check-newsemanal.test | 12 ----- test-data/unit/check-newsyntax.test | 41 ++++----------- test-data/unit/check-singledispatch.test | 14 ------ test-data/unit/check-tuples.test | 3 +- test-data/unit/check-typeddict.test | 16 +----- test-data/unit/check-underscores.test | 6 --- test-data/unit/check-union-or-syntax.test | 11 ++-- test-data/unit/check-unreachable-code.test | 6 +-- test-data/unit/cmdline.test | 22 ++++---- test-data/unit/daemon.test | 20 ++++---- test-data/unit/parse-errors.test | 11 +--- test-data/unit/pythoneval.test | 2 +- test-data/unit/reports.test | 2 +- 33 files changed, 75 insertions(+), 314 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 46a5e35f320d..62e2298ba59d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3072,7 +3072,7 @@ def visit_op_expr(self, e: OpExpr) -> Type: # Expressions of form [...] * e get special type inference. return self.check_list_multiply(e) if e.op == "%": - if isinstance(e.left, BytesExpr) and self.chk.options.python_version >= (3, 5): + if isinstance(e.left, BytesExpr): return self.strfrm_checker.check_str_interpolation(e.left, e.right) if isinstance(e.left, StrExpr): return self.strfrm_checker.check_str_interpolation(e.left, e.right) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index cda603be086b..eeb9e7633756 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -682,14 +682,6 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi self.exprchk.accept(expr) specifiers = parse_conversion_specifiers(expr.value) has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr) - if isinstance(expr, BytesExpr) and self.chk.options.python_version < (3, 5): - self.msg.fail( - "Bytes formatting is only supported in Python 3.5 and later", - replacements, - code=codes.STRING_FORMATTING, - ) - return AnyType(TypeOfAny.from_error) - if has_mapping_keys is None: pass # Error was reported elif has_mapping_keys: @@ -1023,13 +1015,6 @@ def conversion_type( NUMERIC_TYPES = NUMERIC_TYPES_NEW if format_call else NUMERIC_TYPES_OLD INT_TYPES = REQUIRE_INT_NEW if format_call else REQUIRE_INT_OLD if p == "b" and not format_call: - if self.chk.options.python_version < (3, 5): - self.msg.fail( - 'Format character "b" is only supported in Python 3.5 and later', - context, - code=codes.STRING_FORMATTING, - ) - return None if not isinstance(expr, BytesExpr): self.msg.fail( 'Format character "b" is only supported on bytes patterns', diff --git a/mypy/defaults.py b/mypy/defaults.py index 1bd87de74bc9..6a09a61a461e 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -10,7 +10,7 @@ # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. -PYTHON3_VERSION_MIN: Final = (3, 4) +PYTHON3_VERSION_MIN: Final = (3, 7) # Keep in sync with typeshed's python support CACHE_DIR: Final = ".mypy_cache" CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"] diff --git a/mypy/messages.py b/mypy/messages.py index ae7fba1473ac..8b88cc1678a4 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1728,7 +1728,6 @@ def need_annotation_for_var( self, node: SymbolNode, context: Context, python_version: tuple[int, int] | None = None ) -> None: hint = "" - has_variable_annotations = not python_version or python_version >= (3, 6) pep604_supported = not python_version or python_version >= (3, 10) # type to recommend the user adds recommended_type = None @@ -1749,18 +1748,10 @@ def need_annotation_for_var( type_dec = f"{type_dec}, {type_dec}" recommended_type = f"{alias}[{type_dec}]" if recommended_type is not None: - if has_variable_annotations: - hint = f' (hint: "{node.name}: {recommended_type} = ...")' - else: - hint = f' (hint: "{node.name} = ... # type: {recommended_type}")' - - if has_variable_annotations: - needed = "annotation" - else: - needed = "comment" + hint = f' (hint: "{node.name}: {recommended_type} = ...")' self.fail( - f'Need type {needed} for "{unmangle(node.name)}"{hint}', + f'Need type annotation for "{unmangle(node.name)}"{hint}', context, code=codes.VAR_ANNOTATED, ) diff --git a/mypy/semanal.py b/mypy/semanal.py index f4f281e7a77a..5b1aea4239f5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2521,12 +2521,7 @@ def visit_import_from(self, imp: ImportFrom) -> None: elif fullname in self.missing_modules: missing_submodule = True # If it is still not resolved, check for a module level __getattr__ - if ( - module - and not node - and (module.is_stub or self.options.python_version >= (3, 7)) - and "__getattr__" in module.names - ): + if module and not node and "__getattr__" in module.names: # We store the fullname of the original definition so that we can # detect whether two imported names refer to the same thing. fullname = module_id + "." + id @@ -5446,11 +5441,8 @@ def visit_yield_expr(self, e: YieldExpr) -> None: blocker=True, ) elif self.function_stack[-1].is_coroutine: - if self.options.python_version < (3, 6): - self.fail('"yield" in async function', e, serious=True, blocker=True) - else: - self.function_stack[-1].is_generator = True - self.function_stack[-1].is_async_generator = True + self.function_stack[-1].is_generator = True + self.function_stack[-1].is_async_generator = True else: self.function_stack[-1].is_generator = True if e.expr: @@ -5721,9 +5713,7 @@ def get_module_symbol(self, node: MypyFile, name: str) -> SymbolTableNode | None sym = SymbolTableNode(GDEF, self.modules[fullname]) elif self.is_incomplete_namespace(module): self.record_incomplete_ref() - elif "__getattr__" in names and ( - node.is_stub or self.options.python_version >= (3, 7) - ): + elif "__getattr__" in names: gvar = self.create_getattr_var(names["__getattr__"], name, fullname) if gvar: sym = SymbolTableNode(GDEF, gvar) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 42f7b10f3333..51ea90e07f3d 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -142,9 +142,6 @@ def check_namedtuple_classdef( * valid statements or None, if any of the types are not ready. """ - if self.options.python_version < (3, 6) and not is_stub_file: - self.fail("NamedTuple class syntax is only supported in Python 3.6", defn) - return [], [], {}, [] if len(defn.base_type_exprs) > 1: self.fail("NamedTuple should be a single base", defn) items: list[str] = [] diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 2df06feacca8..aaa01969217a 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -45,10 +45,9 @@ class SemanticAnalyzerPreAnalysis(TraverserVisitor): import sys - def do_stuff(): - # type: () -> None: - if sys.python_version < (3,): - import xyz # Only available in Python 2 + def do_stuff() -> None: + if sys.version_info >= (3, 10): + import xyz # Only available in Python 3.10+ xyz.whatever() ... diff --git a/test-data/unit/README.md b/test-data/unit/README.md index f2c727b43543..5a9416603541 100644 --- a/test-data/unit/README.md +++ b/test-data/unit/README.md @@ -12,7 +12,7 @@ feature you added. If you added a new `check-*.test` file, it will be autodiscov Add the test in this format anywhere in the file: [case testNewSyntaxBasics] - # flags: --python-version 3.6 + # flags: --python-version 3.10 x: int x = 5 y: int = 5 diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index bcf55d84ff26..3b7ef53b6bd6 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -183,7 +183,6 @@ async def f() -> None: [typing fixtures/typing-async.pyi] [case testAsyncForComprehension] -# flags: --python-version 3.6 from typing import Generic, Iterable, TypeVar, AsyncIterator, Tuple T = TypeVar('T') @@ -223,7 +222,6 @@ async def generatorexp(obj: Iterable[int]): [typing fixtures/typing-async.pyi] [case testAsyncForComprehensionErrors] -# flags: --python-version 3.6 from typing import Generic, Iterable, TypeVar, AsyncIterator, Tuple T = TypeVar('T') @@ -240,16 +238,10 @@ class asyncify(Generic[T], AsyncIterator[T]): raise StopAsyncIteration async def wrong_iterable(obj: Iterable[int]): - [i async for i in obj] - [i for i in asyncify(obj)] - {i: i async for i in obj} - {i: i for i in asyncify(obj)} - -[out] -main:18: error: "Iterable[int]" has no attribute "__aiter__" (not async iterable) -main:19: error: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) -main:20: error: "Iterable[int]" has no attribute "__aiter__" (not async iterable) -main:21: error: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) + [i async for i in obj] # E: "Iterable[int]" has no attribute "__aiter__" (not async iterable) + [i for i in asyncify(obj)] # E: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) + {i: i async for i in obj} # E: "Iterable[int]" has no attribute "__aiter__" (not async iterable) + {i: i for i in asyncify(obj)} # E: "asyncify[int]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] @@ -340,17 +332,6 @@ async def f() -> None: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] -[case testNoYieldInAsyncDef] -# flags: --python-version 3.5 - -async def f(): - yield None # E: "yield" in async function -async def g(): - yield # E: "yield" in async function -async def h(): - x = yield # E: "yield" in async function -[builtins fixtures/async_await.pyi] - [case testNoYieldFromInAsyncDef] async def f(): @@ -422,7 +403,6 @@ def f() -> Generator[int, str, int]: -- --------------------------------------------------------------------- [case testAsyncGenerator] -# flags: --python-version 3.6 from typing import AsyncGenerator, Generator async def f() -> int: @@ -450,7 +430,6 @@ async def wrong_return() -> Generator[int, None, None]: # E: The return type of [typing fixtures/typing-async.pyi] [case testAsyncGeneratorReturnIterator] -# flags: --python-version 3.6 from typing import AsyncIterator async def gen() -> AsyncIterator[int]: @@ -466,7 +445,6 @@ async def use_gen() -> None: [typing fixtures/typing-async.pyi] [case testAsyncGeneratorManualIter] -# flags: --python-version 3.6 from typing import AsyncGenerator async def genfunc() -> AsyncGenerator[int, None]: @@ -484,7 +462,6 @@ async def user() -> None: [typing fixtures/typing-async.pyi] [case testAsyncGeneratorAsend] -# flags: --python-version 3.6 from typing import AsyncGenerator async def f() -> None: @@ -505,7 +482,6 @@ async def h() -> None: [typing fixtures/typing-async.pyi] [case testAsyncGeneratorAthrow] -# flags: --python-version 3.6 from typing import AsyncGenerator async def gen() -> AsyncGenerator[str, int]: @@ -524,7 +500,6 @@ async def h() -> None: [typing fixtures/typing-async.pyi] [case testAsyncGeneratorNoSyncIteration] -# flags: --python-version 3.6 from typing import AsyncGenerator async def gen() -> AsyncGenerator[int, None]: @@ -532,17 +507,13 @@ async def gen() -> AsyncGenerator[int, None]: yield i def h() -> None: - for i in gen(): + for i in gen(): # E: "AsyncGenerator[int, None]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) pass [builtins fixtures/dict.pyi] [typing fixtures/typing-async.pyi] -[out] -main:9: error: "AsyncGenerator[int, None]" has no attribute "__iter__"; maybe "__aiter__"? (not iterable) - [case testAsyncGeneratorNoYieldFrom] -# flags: --python-version 3.6 from typing import AsyncGenerator async def f() -> AsyncGenerator[int, None]: @@ -555,7 +526,6 @@ async def gen() -> AsyncGenerator[int, None]: [typing fixtures/typing-async.pyi] [case testAsyncGeneratorNoReturnWithValue] -# flags: --python-version 3.6 from typing import AsyncGenerator async def return_int() -> AsyncGenerator[int, None]: diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index ab2f5f3f6b48..1916cb41bb74 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -1,13 +1,4 @@ -[case testNewNamedTupleOldPythonVersion] -# flags: --python-version 3.5 -from typing import NamedTuple - -class E(NamedTuple): # E: NamedTuple class syntax is only supported in Python 3.6 - pass -[builtins fixtures/tuple.pyi] - [case testNewNamedTupleNoUnderscoreFields] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -17,7 +8,6 @@ class X(NamedTuple): [builtins fixtures/tuple.pyi] [case testNewNamedTupleAccessingAttributes] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -31,7 +21,6 @@ x.z # E: "X" has no attribute "z" [builtins fixtures/tuple.pyi] [case testNewNamedTupleAttributesAreReadOnly] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -47,7 +36,6 @@ a.x = 5 # E: Property "x" defined in "X" is read-only [builtins fixtures/tuple.pyi] [case testNewNamedTupleCreateWithPositionalArguments] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -62,7 +50,6 @@ x = X(1, '2', 3) # E: Too many arguments for "X" [builtins fixtures/tuple.pyi] [case testNewNamedTupleShouldBeSingleBase] -# flags: --python-version 3.6 from typing import NamedTuple class A: ... @@ -71,7 +58,6 @@ class X(NamedTuple, A): # E: NamedTuple should be a single base [builtins fixtures/tuple.pyi] [case testCreateNewNamedTupleWithKeywordArguments] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -85,7 +71,6 @@ x = X(y='x') # E: Missing positional argument "x" in call to "X" [builtins fixtures/tuple.pyi] [case testNewNamedTupleCreateAndUseAsTuple] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -98,7 +83,6 @@ a, b, c = x # E: Need more than 2 values to unpack (3 expected) [builtins fixtures/tuple.pyi] [case testNewNamedTupleWithItemTypes] -# flags: --python-version 3.6 from typing import NamedTuple class N(NamedTuple): @@ -116,7 +100,6 @@ if int(): [builtins fixtures/tuple.pyi] [case testNewNamedTupleConstructorArgumentTypes] -# flags: --python-version 3.6 from typing import NamedTuple class N(NamedTuple): @@ -130,7 +113,6 @@ N(b='x', a=1) [builtins fixtures/tuple.pyi] [case testNewNamedTupleAsBaseClass] -# flags: --python-version 3.6 from typing import NamedTuple class N(NamedTuple): @@ -151,7 +133,6 @@ if int(): [builtins fixtures/tuple.pyi] [case testNewNamedTupleSelfTypeWithNamedTupleAsBase] -# flags: --python-version 3.6 from typing import NamedTuple class A(NamedTuple): @@ -172,7 +153,6 @@ class B(A): [out] [case testNewNamedTupleTypeReferenceToClassDerivedFrom] -# flags: --python-version 3.6 from typing import NamedTuple class A(NamedTuple): @@ -194,7 +174,6 @@ class B(A): [builtins fixtures/tuple.pyi] [case testNewNamedTupleSubtyping] -# flags: --python-version 3.6 from typing import NamedTuple, Tuple class A(NamedTuple): @@ -222,7 +201,6 @@ if int(): [builtins fixtures/tuple.pyi] [case testNewNamedTupleSimpleTypeInference] -# flags: --python-version 3.6 from typing import NamedTuple, Tuple class A(NamedTuple): @@ -239,7 +217,6 @@ a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int] [builtins fixtures/list.pyi] [case testNewNamedTupleMissingClassAttribute] -# flags: --python-version 3.6 from typing import NamedTuple class MyNamedTuple(NamedTuple): @@ -250,7 +227,6 @@ MyNamedTuple.x # E: "Type[MyNamedTuple]" has no attribute "x" [builtins fixtures/tuple.pyi] [case testNewNamedTupleEmptyItems] -# flags: --python-version 3.6 from typing import NamedTuple class A(NamedTuple): @@ -258,7 +234,6 @@ class A(NamedTuple): [builtins fixtures/tuple.pyi] [case testNewNamedTupleForwardRef] -# flags: --python-version 3.6 from typing import NamedTuple class A(NamedTuple): @@ -271,7 +246,6 @@ a = A(1) # E: Argument 1 to "A" has incompatible type "int"; expected "B" [builtins fixtures/tuple.pyi] [case testNewNamedTupleProperty36] -# flags: --python-version 3.6 from typing import NamedTuple class A(NamedTuple): @@ -288,7 +262,6 @@ C(2).b [builtins fixtures/property.pyi] [case testNewNamedTupleAsDict] -# flags: --python-version 3.6 from typing import NamedTuple, Any class X(NamedTuple): @@ -301,7 +274,6 @@ reveal_type(x._asdict()) # N: Revealed type is "builtins.dict[builtins.str, Any [builtins fixtures/dict.pyi] [case testNewNamedTupleReplaceTyped] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -315,7 +287,6 @@ x._replace(y=5) # E: Argument "y" to "_replace" of "X" has incompatible type "i [builtins fixtures/tuple.pyi] [case testNewNamedTupleFields] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -333,7 +304,6 @@ reveal_type(X.__annotations__) # N: Revealed type is "typing.Mapping[builtins.s [builtins fixtures/dict.pyi] [case testNewNamedTupleUnit] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -345,7 +315,6 @@ x._fields[0] # E: Tuple index out of range [builtins fixtures/tuple.pyi] [case testNewNamedTupleJoinNamedTuple] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -360,7 +329,6 @@ reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[ [builtins fixtures/list.pyi] [case testNewNamedTupleJoinTuple] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -373,7 +341,6 @@ reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[Tuple[b [builtins fixtures/list.pyi] [case testNewNamedTupleWithTooManyArguments] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -383,25 +350,17 @@ class X(NamedTuple): [builtins fixtures/tuple.pyi] [case testNewNamedTupleWithInvalidItems2] -# flags: --python-version 3.6 import typing class X(typing.NamedTuple): x: int - y = 1 - x.x: int + y = 1 # E: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" + x.x: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" z: str = 'z' - aa: int - -[out] -main:6: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" -main:7: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" -main:9: error: Non-default NamedTuple fields cannot follow default fields - + aa: int # E: Non-default NamedTuple fields cannot follow default fields [builtins fixtures/list.pyi] [case testNewNamedTupleWithoutTypesSpecified] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -410,7 +369,6 @@ class X(NamedTuple): [builtins fixtures/tuple.pyi] [case testTypeUsingTypeCNamedTuple] -# flags: --python-version 3.6 from typing import NamedTuple, Type class N(NamedTuple): @@ -418,13 +376,10 @@ class N(NamedTuple): y: str def f(a: Type[N]): - a() + a() # E: Missing positional arguments "x", "y" in call to "N" [builtins fixtures/list.pyi] -[out] -main:9: error: Missing positional arguments "x", "y" in call to "N" [case testNewNamedTupleWithDefaults] -# flags: --python-version 3.6 from typing import List, NamedTuple, Optional class X(NamedTuple): @@ -464,7 +419,7 @@ UserDefined(1) # E: Argument 1 to "UserDefined" has incompatible type "int"; ex [builtins fixtures/list.pyi] [case testNewNamedTupleWithDefaultsStrictOptional] -# flags: --strict-optional --python-version 3.6 +# flags: --strict-optional from typing import List, NamedTuple, Optional class HasNone(NamedTuple): @@ -483,7 +438,6 @@ class CannotBeNone(NamedTuple): [builtins fixtures/list.pyi] [case testNewNamedTupleWrongType] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -492,7 +446,6 @@ class X(NamedTuple): [builtins fixtures/tuple.pyi] [case testNewNamedTupleErrorInDefault] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): @@ -500,7 +453,6 @@ class X(NamedTuple): [builtins fixtures/tuple.pyi] [case testNewNamedTupleInheritance] -# flags: --python-version 3.6 from typing import NamedTuple class X(NamedTuple): diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test index 132a34503b89..534967b1edbf 100644 --- a/test-data/unit/check-fastparse.test +++ b/test-data/unit/check-fastparse.test @@ -31,7 +31,6 @@ def f(x): # E: Invalid type comment or annotation pass [case testFastParseInvalidTypes3] -# flags: --python-version 3.6 # All of these should not crash from typing import Callable, Tuple, Iterable diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index c356028f6620..3750c44ed7f3 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1733,7 +1733,7 @@ def h() -> List[Any]: # E: Explicit "Any" is not allowed [builtins fixtures/list.pyi] [case testDisallowAnyExplicitVarDeclaration] -# flags: --python-version 3.6 --disallow-any-explicit +# flags: --disallow-any-explicit from typing import Any v: Any = '' # E: Explicit "Any" is not allowed w = '' # type: Any # E: Explicit "Any" is not allowed @@ -1741,7 +1741,7 @@ class X: y = '' # type: Any # E: Explicit "Any" is not allowed [case testDisallowAnyExplicitGenericVarDeclaration] -# flags: --python-version 3.6 --disallow-any-explicit +# flags: --disallow-any-explicit from typing import Any, List v: List[Any] = [] # E: Explicit "Any" is not allowed [builtins fixtures/list.pyi] @@ -1836,7 +1836,7 @@ N = TypedDict('N', {'x': str, 'y': List}) # no error [builtins fixtures/dict.pyi] [case testDisallowAnyGenericsTupleNoTypeParams] -# flags: --python-version 3.6 --disallow-any-generics +# flags: --disallow-any-generics from typing import Tuple def f(s: Tuple) -> None: pass # E: Missing type parameters for generic type "Tuple" @@ -1877,7 +1877,7 @@ def g(l: L[str]) -> None: pass # no error [builtins fixtures/list.pyi] [case testDisallowAnyGenericsGenericAlias] -# flags: --python-version 3.6 --disallow-any-generics +# flags: --disallow-any-generics from typing import TypeVar, Tuple T = TypeVar('T') @@ -1892,7 +1892,7 @@ x: A = ('a', 'b', 1) # E: Missing type parameters for generic type "A" [builtins fixtures/tuple.pyi] [case testDisallowAnyGenericsPlainList] -# flags: --python-version 3.6 --disallow-any-generics +# flags: --disallow-any-generics from typing import List def f(l: List) -> None: pass # E: Missing type parameters for generic type "List" @@ -1905,7 +1905,7 @@ y: List = [] # E: Missing type parameters for generic type "List" [builtins fixtures/list.pyi] [case testDisallowAnyGenericsCustomGenericClass] -# flags: --python-version 3.6 --disallow-any-generics +# flags: --disallow-any-generics from typing import Generic, TypeVar, Any T = TypeVar('T') diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index f63abbb33034..7d23c2e199f1 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -103,7 +103,6 @@ a = None # type: Any [typing fixtures/typing-medium.pyi] [case testStringInterpolationC] -# flags: --python-version 3.6 '%c' % 1 '%c' % 1.0 # E: "%c" requires int or char (expression has type "float") '%c' % 's' @@ -232,18 +231,12 @@ t5: Iterable[str] = ('A', 'B') -- Bytes interpolation -- -------------------- - -[case testBytesInterpolationBefore35] -# flags: --python-version 3.4 -b'%b' % 1 # E: Unsupported left operand type for % ("bytes") - [case testBytesInterpolation] b'%b' % 1 # E: Incompatible types in string interpolation (expression has type "int", placeholder has type "bytes") b'%b' % b'1' b'%a' % 3 [case testBytesInterpolationC] -# flags: --python-version 3.6 b'%c' % 1 b'%c' % 1.0 # E: "%c" requires an integer in range(256) or a single byte (expression has type "float") b'%c' % 's' # E: "%c" requires an integer in range(256) or a single byte (expression has type "str") diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 574a57607d11..8c90b5adba34 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -200,7 +200,6 @@ t23: collections.abc.ValuesView[str] [case testGenericBuiltinTupleTyping] -# flags: --python-version 3.6 from typing import Tuple t01: Tuple = () @@ -248,7 +247,6 @@ reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.i [builtins fixtures/tuple.pyi] [case testTypeAliasWithBuiltinTupleInStub] -# flags: --python-version 3.6 import m reveal_type(m.a) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(m.b) # N: Revealed type is "Tuple[builtins.int, builtins.str]" @@ -261,7 +259,6 @@ b: B [builtins fixtures/tuple.pyi] [case testTypeAliasWithBuiltinListInStub] -# flags: --python-version 3.6 import m reveal_type(m.a) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(m.b) # N: Revealed type is "builtins.list[builtins.list[builtins.int]]" @@ -280,7 +277,6 @@ d: type[str] [case testTypeAliasWithBuiltinListAliasInStub] -# flags: --python-version 3.6 import m reveal_type(m.a()[0]) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 42e3d23eddb9..90d46c217451 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -596,7 +596,7 @@ main:13: error: Argument 2 to "Node" has incompatible type "int"; expected "str" -- Error formatting is a bit different (and probably better) with new analyzer [case testGenericTypeAliasesWrongAliases] -# flags: --show-column-numbers --python-version 3.6 --no-strict-optional +# flags: --show-column-numbers --no-strict-optional from typing import TypeVar, Generic, List, Callable, Tuple, Union T = TypeVar('T') S = TypeVar('S') diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index cd009887a5b5..d8461fc78815 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5508,7 +5508,6 @@ class Foo: class C: pass [case testIncrementalNestedNamedTuple] -# flags: --python-version 3.6 import a [file a.py] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index ee13cb3830fc..3c4a0943556a 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -913,7 +913,6 @@ def call(c: Callable[[int], Any], i: int) -> None: [out] [case testCallableMeetAndJoin] -# flags: --python-version 3.6 from typing import Callable, Any, TypeVar class A: ... diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index fc3daff64fbd..4992b6589bb3 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2067,16 +2067,6 @@ def __getattr__(name): ... [builtins fixtures/module.pyi] -[case testModuleLevelGetattrNotStub36] -# flags: --python-version 3.6 -import has_getattr -reveal_type(has_getattr.any_attribute) # E: Module has no attribute "any_attribute" \ - # N: Revealed type is "Any" -[file has_getattr.py] -def __getattr__(name) -> str: ... - -[builtins fixtures/module.pyi] - [case testModuleLevelGetattrNotStub37] # flags: --python-version 3.7 @@ -2111,17 +2101,6 @@ def __getattr__(name: str) -> int: ... [builtins fixtures/module.pyi] -[case testModuleLevelGetattrImportFromNotStub36] -# flags: --python-version 3.6 -from non_stub import name # E: Module "non_stub" has no attribute "name" -reveal_type(name) # N: Revealed type is "Any" - -[file non_stub.py] -from typing import Any -def __getattr__(name: str) -> Any: ... - -[builtins fixtures/module.pyi] - [case testModuleLevelGetattrImportFromNotStub37] # flags: --python-version 3.7 from non_stub import name diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 83cc8c099deb..d69b924971e1 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -38,18 +38,7 @@ x.y x.z # E: "X" has no attribute "z" [builtins fixtures/tuple.pyi] -[case testNamedTupleClassPython35] -# flags: --python-version 3.5 -from typing import NamedTuple - -class A(NamedTuple): - x = 3 # type: int -[builtins fixtures/tuple.pyi] -[out] -main:4: error: NamedTuple class syntax is only supported in Python 3.6 - -[case testNamedTupleClassInStubPython35] -# flags: --python-version 3.5 +[case testNamedTupleClassInStub] import foo [file foo.pyi] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index c329ccf840a8..f06af0057f0f 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1138,7 +1138,6 @@ reveal_type(x) # N: Revealed type is "builtins.bool" [builtins fixtures/primitives.pyi] [case testNarrowingTypedDictUsingEnumLiteral] -# flags: --python-version 3.6 from typing import Union from typing_extensions import TypedDict, Literal from enum import Enum diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 77a1553d4715..8300957ee511 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2571,18 +2571,6 @@ import n [file n.pyi] class C: pass -[case testNewAnalyzerModuleGetAttrInPython36] -# flags: --python-version 3.6 -import m -import n - -x: m.n.C # E: Name "m.n.C" is not defined -y: n.D # E: Name "n.D" is not defined -[file m.py] -import n -[file n.py] -def __getattr__(x): pass - [case testNewAnalyzerModuleGetAttrInPython37] # flags: --python-version 3.7 import m diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index cfcbfc598c51..0815d7af1933 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -1,15 +1,8 @@ -[case testNewSyntaxRequire36] -# flags: --python-version 3.5 -x: int = 5 # E: Variable annotation syntax is only supported in Python 3.6 and greater -[out] - [case testNewSyntaxSyntaxError] -# flags: --python-version 3.6 x: int: int # E: invalid syntax [out] [case testNewSyntaxBasics] -# flags: --python-version 3.6 x: int x = 5 y: int = 5 @@ -19,11 +12,10 @@ a = 5 # E: Incompatible types in assignment (expression has type "int", variabl b: str = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str") zzz: int -zzz: str # E: Name "zzz" already defined on line 10 +zzz: str # E: Name "zzz" already defined on line 9 [out] [case testNewSyntaxWithDict] -# flags: --python-version 3.6 from typing import Dict, Any d: Dict[int, str] = {} @@ -34,7 +26,6 @@ d['ab'] = 'ab' # E: Invalid index type "str" for "Dict[int, str]"; expected typ [out] [case testNewSyntaxWithRevealType] -# flags: --python-version 3.6 from typing import Dict def tst_local(dct: Dict[int, T]) -> Dict[T, int]: @@ -46,7 +37,6 @@ reveal_type(tst_local({1: 'a'})) # N: Revealed type is "builtins.dict[builtins. [out] [case testNewSyntaxWithInstanceVars] -# flags: --python-version 3.6 class TstInstance: a: str def __init__(self) -> None: @@ -59,20 +49,20 @@ TstInstance().a = 'ab' [out] [case testNewSyntaxWithClassVars] -# flags: --strict-optional --python-version 3.6 +# flags: --strict-optional class CCC: a: str = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") [out] [case testNewSyntaxWithStrictOptional] -# flags: --strict-optional --python-version 3.6 +# flags: --strict-optional strict: int strict = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") strict2: int = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [out] [case testNewSyntaxWithStrictOptionalFunctions] -# flags: --strict-optional --python-version 3.6 +# flags: --strict-optional def f() -> None: x: int if int(): @@ -80,7 +70,7 @@ def f() -> None: [out] [case testNewSyntaxWithStrictOptionalClasses] -# flags: --strict-optional --python-version 3.6 +# flags: --strict-optional class C: def meth(self) -> None: x: int = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") @@ -88,25 +78,18 @@ class C: [out] [case testNewSyntaxSpecialAssign] -# flags: --python-version 3.6 class X: x: str x[0]: int x.x: int [out] -main:4: error: Unexpected type declaration -main:4: error: Unsupported target for indexed assignment ("str") -main:5: error: Type cannot be declared in assignment to non-self attribute -main:5: error: "str" has no attribute "x" - -[case testNewSyntaxAsyncComprehensionError] -# flags: --python-version 3.5 -async def f(): - results = [i async for i in aiter() if i % 2] # E: Async comprehensions are only supported in Python 3.6 and greater +main:3: error: Unexpected type declaration +main:3: error: Unsupported target for indexed assignment ("str") +main:4: error: Type cannot be declared in assignment to non-self attribute +main:4: error: "str" has no attribute "x" [case testNewSyntaxFStringBasics] -# flags: --python-version 3.6 f'foobar' f'{"foobar"}' f'foo{"bar"}' @@ -118,22 +101,19 @@ a = f'{"foobar"}' [builtins fixtures/f_string.pyi] [case testNewSyntaxFStringExpressionsOk] -# flags: --python-version 3.6 f'.{1 + 1}.' f'.{1 + 1}.{"foo" + "bar"}' [builtins fixtures/f_string.pyi] [case testNewSyntaxFStringExpressionsErrors] -# flags: --python-version 3.6 f'{1 + ""}' f'.{1 + ""}' [builtins fixtures/f_string.pyi] [out] +main:1: error: Unsupported operand types for + ("int" and "str") main:2: error: Unsupported operand types for + ("int" and "str") -main:3: error: Unsupported operand types for + ("int" and "str") [case testNewSyntaxFStringParseFormatOptions] -# flags: --python-version 3.6 value = 10.5142 width = 10 precision = 4 @@ -141,7 +121,6 @@ f'result: {value:{width}.{precision}}' [builtins fixtures/f_string.pyi] [case testNewSyntaxFStringSingleField] -# flags: --python-version 3.6 v = 1 reveal_type(f'{v}') # N: Revealed type is "builtins.str" reveal_type(f'{1}') # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-singledispatch.test b/test-data/unit/check-singledispatch.test index 1bc34c6fdaab..1adec1575b7e 100644 --- a/test-data/unit/check-singledispatch.test +++ b/test-data/unit/check-singledispatch.test @@ -80,20 +80,6 @@ def g(arg: int) -> None: # E: Argument to register "str" is incompatible with ty [builtins fixtures/args.pyi] -[case testDispatchBasedOnTypeAnnotationsRequires37-xfail] -# flags: --python-version 3.6 -# the docs for singledispatch say that register didn't accept type annotations until python 3.7 -from functools import singledispatch - -@singledispatch -def f(arg) -> None: - pass -@f.register -def g(arg: int) -> None: # E: Singledispatch based on type annotations is only supported in Python 3.7 and greater - pass - -[builtins fixtures/args.pyi] - [case testTypePassedAsArgumentToRegister] from functools import singledispatch diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 5cb89a6854be..f64d24a4ed6b 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1455,8 +1455,7 @@ x7, x8, y7, y8 = *points2, *points3 # E: Contiguous iterable with same type expe x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same type expected [builtins fixtures/tuple.pyi] -[case testAssignEmptyPy36] -# flags: --python-version 3.6 +[case testAssignEmpty] () = [] [case testAssignEmptyBogus] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 739d1ba6eb75..983fa8c17aec 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -87,7 +87,6 @@ D = TypedDict('D', { -- Define TypedDict (Class syntax) [case testCanCreateTypedDictWithClass] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point(TypedDict): @@ -99,7 +98,6 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtin [builtins fixtures/dict.pyi] [case testCanCreateTypedDictWithSubclass] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point1D(TypedDict): @@ -113,7 +111,6 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': built [builtins fixtures/dict.pyi] [case testCanCreateTypedDictWithSubclass2] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point1D(TypedDict): @@ -126,7 +123,6 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': built [builtins fixtures/dict.pyi] [case testCanCreateTypedDictClassEmpty] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class EmptyDict(TypedDict): @@ -138,10 +134,7 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.EmptyDict', {})" [case testCanCreateTypedDictWithClassOldVersion] -# flags: --python-version 3.5 - -# Test that we can use class-syntax to merge TypedDicts even in -# versions without type annotations +# Test that we can use class-syntax to merge function-based TypedDicts from mypy_extensions import TypedDict @@ -165,7 +158,6 @@ foo({'name': 'lol', 'year': 2009, 'based_on': 0}) # E: Incompatible types (expr -- Define TypedDict (Class syntax errors) [case testCannotCreateTypedDictWithClassOtherBases] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class A: pass @@ -195,7 +187,6 @@ class C(TypedDict, TypedDict): # E: Duplicate base class "TypedDict" [typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithClassWithOtherStuff] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point(TypedDict): @@ -251,7 +242,6 @@ Point = TypedDict('Point', {'x': int, 'y': int, '_fallback': object}) [builtins fixtures/dict.pyi] [case testCanCreateTypedDictWithClassUnderscores] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point(TypedDict): @@ -263,7 +253,6 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins [builtins fixtures/dict.pyi] [case testCannotCreateTypedDictWithDuplicateKey1] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Bad(TypedDict): @@ -291,7 +280,6 @@ reveal_type(d2) # N: Revealed type is "TypedDict('__main__.D2', {'x': builtins.s [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOverwriting] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point1(TypedDict): @@ -306,7 +294,6 @@ reveal_type(b) # N: Revealed type is "TypedDict('__main__.Bad', {'x': builtins.i [builtins fixtures/dict.pyi] [case testCanCreateTypedDictWithClassOverwriting2] -# flags: --python-version 3.6 from mypy_extensions import TypedDict class Point1(TypedDict): @@ -1774,7 +1761,6 @@ reveal_type(td.pop('c')) # E: TypedDict "TDA" has no key "c" \ [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithTypingExtensions] -# flags: --python-version 3.6 from typing_extensions import TypedDict class Point(TypedDict): diff --git a/test-data/unit/check-underscores.test b/test-data/unit/check-underscores.test index ac9fad2ca792..2a789b3314f3 100644 --- a/test-data/unit/check-underscores.test +++ b/test-data/unit/check-underscores.test @@ -1,10 +1,4 @@ -[case testUnderscoresRequire36] -# flags: --python-version 3.5 -x = 1000_000 # E: Underscores in numeric literals are only supported in Python 3.6 and greater -[out] - [case testUnderscoresBasics] -# flags: --python-version 3.6 x: int x = 1000_000 x = 0x_FF_FF_FF_FF diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index 58526cfd0623..f342d0ca34a5 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -66,8 +66,8 @@ x: List[int | str] reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] -[case testUnionOrSyntaxWithQuotedFunctionTypes] -# flags: --python-version 3.4 +[case testUnionOrSyntaxWithQuotedFunctionTypesPre310] +# flags: --python-version 3.9 from typing import Union def f(x: 'Union[int, str, None]') -> 'Union[int, None]': reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" @@ -79,8 +79,8 @@ def g(x: "int | str | None") -> "int | None": return 42 reveal_type(g) # N: Revealed type is "def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]" -[case testUnionOrSyntaxWithQuotedVariableTypes] -# flags: --python-version 3.6 +[case testUnionOrSyntaxWithQuotedVariableTypesPre310] +# flags: --python-version 3.9 y: "int | str" = 42 reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" @@ -124,7 +124,6 @@ cast(str | int, 'x') # E: Cast target is not a type [typing fixtures/typing-full.pyi] [case testUnionOrSyntaxInComment] -# flags: --python-version 3.6 x = 1 # type: int | str [case testUnionOrSyntaxFutureImport] @@ -138,7 +137,7 @@ x: int | None x: int | None # E: X | Y syntax for unions requires Python 3.10 [case testUnionOrSyntaxInStubFile] -# flags: --python-version 3.6 +# flags: --python-version 3.9 from lib import x [file lib.pyi] x: int | None diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 1db2a16e2e1c..82ff35f53702 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -422,9 +422,9 @@ x = 1 [out] [case testCustomSysVersionInfo] -# flags: --python-version 3.5 +# flags: --python-version 3.11 import sys -if sys.version_info == (3, 5): +if sys.version_info == (3, 11): x = "foo" else: x = 3 @@ -433,7 +433,7 @@ reveal_type(x) # N: Revealed type is "builtins.str" [out] [case testCustomSysVersionInfo2] -# flags: --python-version 3.5 +# flags: --python-version 3.11 import sys if sys.version_info == (3, 6): x = "foo" diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 6e9fdf6dab65..42f0ee8a9ec6 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -296,7 +296,7 @@ mypy.ini: [mypy]: ignore_missing_imports: Not a boolean: nah [file mypy.ini] \[mypy] \[mypy-*] -python_version = 3.4 +python_version = 3.11 [out] mypy.ini: [mypy-*]: Per-module sections should only specify per-module flags (python_version) == Return code: 0 @@ -592,7 +592,7 @@ main.py:1: error: Cannot find implementation or library stub for module named "a \[tool.mypy] python_version = 3.10 [out] -pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.4 or higher). You may need to put quotes around your Python version +pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.7 or higher). You may need to put quotes around your Python version == Return code: 0 [case testPythonVersionTooOld10] @@ -604,13 +604,13 @@ python_version = 1.0 mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 3) == Return code: 0 -[case testPythonVersionTooOld33] +[case testPythonVersionTooOld36] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.3 +python_version = 3.6 [out] -mypy.ini: [mypy]: python_version: Python 3.3 is not supported (must be 3.4 or higher) +mypy.ini: [mypy]: python_version: Python 3.6 is not supported (must be 3.7 or higher) == Return code: 0 [case testPythonVersionTooNew40] @@ -633,18 +633,18 @@ usage: mypy [-h] [-v] [-V] [more options; see below] mypy: error: Mypy no longer supports checking Python 2 code. Consider pinning to mypy<0.980 if you need to check Python 2 code. == Return code: 2 -[case testPythonVersionAccepted34] +[case testPythonVersionAccepted37] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.4 +python_version = 3.7 [out] -[case testPythonVersionAccepted36] +[case testPythonVersionAccepted311] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.6 +python_version = 3.11 [out] -- This should be a dumping ground for tests of plugins that are sensitive to @@ -676,11 +676,11 @@ int_pow.py:10: note: Revealed type is "builtins.int" int_pow.py:11: note: Revealed type is "Any" == Return code: 0 -[case testDisallowAnyGenericsBuiltinCollections] +[case testDisallowAnyGenericsBuiltinCollectionsPre39] # cmd: mypy m.py [file mypy.ini] \[mypy] -python_version=3.6 +python_version = 3.8 \[mypy-m] disallow_any_generics = True diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index f208b4e78e54..18a03a92207d 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -159,18 +159,18 @@ def plugin(version): return Dummy [case testDaemonRunRestartGlobs] -- Ensure dmypy is not restarted if the configuration doesn't change and it contains globs -- Note: Backslash path separator in output is replaced with forward slash so the same test succeeds on Windows as well -$ dmypy run -- foo --follow-imports=error --python-version=3.6 +$ dmypy run -- foo --follow-imports=error Daemon started foo/lol.py:1: error: Name "fail" is not defined Found 1 error in 1 file (checked 3 source files) == Return code: 1 -$ dmypy run -- foo --follow-imports=error --python-version=3.6 +$ dmypy run -- foo --follow-imports=error foo/lol.py:1: error: Name "fail" is not defined Found 1 error in 1 file (checked 3 source files) == Return code: 1 $ {python} -c "print('[mypy]')" >mypy.ini $ {python} -c "print('ignore_errors=True')" >>mypy.ini -$ dmypy run -- foo --follow-imports=error --python-version=3.6 +$ dmypy run -- foo --follow-imports=error Restarting: configuration changed Daemon stopped Daemon started @@ -264,7 +264,7 @@ $ dmypy stop Daemon stopped [case testDaemonWarningSuccessExitCode-posix] -$ dmypy run -- foo.py --follow-imports=error +$ dmypy run -- foo.py --follow-imports=error --python-version=3.11 Daemon started foo.py:2: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs Success: no issues found in 1 source file @@ -282,13 +282,13 @@ def foo(): [case testDaemonQuickstart] $ {python} -c "print('x=1')" >foo.py $ {python} -c "print('x=1')" >bar.py -$ mypy --local-partial-types --cache-fine-grained --follow-imports=error --no-sqlite-cache --python-version=3.6 -- foo.py bar.py +$ mypy --local-partial-types --cache-fine-grained --follow-imports=error --no-sqlite-cache --python-version=3.11 -- foo.py bar.py Success: no issues found in 2 source files -$ {python} -c "import shutil; shutil.copy('.mypy_cache/3.6/bar.meta.json', 'asdf.json')" +$ {python} -c "import shutil; shutil.copy('.mypy_cache/3.11/bar.meta.json', 'asdf.json')" -- update bar's timestamp but don't change the file $ {python} -c "import time;time.sleep(1)" $ {python} -c "print('x=1')" >bar.py -$ dmypy run -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.6 +$ dmypy run -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.11 Daemon started Success: no issues found in 2 source files $ dmypy status --fswatcher-dump-file test.json @@ -296,11 +296,11 @@ Daemon is up and running $ dmypy stop Daemon stopped -- copy the original bar cache file back so that the mtime mismatches -$ {python} -c "import shutil; shutil.copy('asdf.json', '.mypy_cache/3.6/bar.meta.json')" +$ {python} -c "import shutil; shutil.copy('asdf.json', '.mypy_cache/3.11/bar.meta.json')" -- sleep guarantees timestamp changes $ {python} -c "import time;time.sleep(1)" $ {python} -c "print('lol')" >foo.py -$ dmypy run --log-file=log -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.6 --quickstart-file test.json +$ dmypy run --log-file=log -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.11 --quickstart-file test.json Daemon started foo.py:1: error: Name "lol" is not defined Found 1 error in 1 file (checked 2 source files) @@ -309,7 +309,7 @@ Found 1 error in 1 file (checked 2 source files) $ {python} -c "import sys; sys.stdout.write(open('log').read())" -- make sure the meta file didn't get updated. we use this as an imperfect proxy for -- whether the source file got rehashed, which we don't want it to have been. -$ {python} -c "x = open('.mypy_cache/3.6/bar.meta.json').read(); y = open('asdf.json').read(); assert x == y" +$ {python} -c "x = open('.mypy_cache/3.11/bar.meta.json').read(); y = open('asdf.json').read(); assert x == y" [case testDaemonSuggest] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test index 33cf9b4f91b4..c6b1c00a6169 100644 --- a/test-data/unit/parse-errors.test +++ b/test-data/unit/parse-errors.test @@ -273,17 +273,10 @@ file:3: error: Syntax error in type comment file:3: error: Inconsistent use of "*" in function signature file:3: error: Inconsistent use of "**" in function signature -[case testPrintStatementInPython35] -# flags: --python-version 3.5 +[case testPrintStatementInPython3] print 1 [out] -file:2: error: Missing parentheses in call to 'print' - -[case testPrintStatementInPython37] -# flags: --python-version 3.7 -print 1 -[out] -file:2: error: Missing parentheses in call to 'print'. Did you mean print(1)? +file:1: error: Missing parentheses in call to 'print'. Did you mean print(1)? [case testInvalidConditionInConditionalExpression] 1 if 2, 3 else 4 diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index abc0f6a464a9..289005b36d9a 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -867,7 +867,7 @@ _program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[st _program.py:24: error: Invalid index type "str" for "MyDDict[Dict[, ]]"; expected type "int" [case testNoSubcriptionOfStdlibCollections] -# flags: --python-version 3.6 +# flags: --python-version 3.7 import collections from collections import Counter from typing import TypeVar diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 50dabb1fdea9..a6cde503ca09 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -311,7 +311,7 @@ Total 0 14 100.00% [case testAnyExpressionsReportTypesOfAny] -# cmd: mypy --python-version=3.6 --any-exprs-report report n.py +# cmd: mypy --any-exprs-report report n.py [file n.py] from typing import Any, List From d7f9f06710cec4f0bb3cd432786264fba4809897 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 15 Jul 2023 11:38:50 +0100 Subject: [PATCH 0144/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/a83e55990ca7f9b9f93271b9087a3f433f54d94a --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/_decimal.pyi | 1 + mypy/typeshed/stdlib/_weakref.pyi | 1 + mypy/typeshed/stdlib/asyncio/events.pyi | 1 + mypy/typeshed/stdlib/asyncio/taskgroups.pyi | 8 +- mypy/typeshed/stdlib/builtins.pyi | 108 +++++++++++++++++- mypy/typeshed/stdlib/collections/__init__.pyi | 23 +++- mypy/typeshed/stdlib/datetime.pyi | 4 + mypy/typeshed/stdlib/doctest.pyi | 3 + mypy/typeshed/stdlib/email/charset.pyi | 9 +- mypy/typeshed/stdlib/email/utils.pyi | 2 +- mypy/typeshed/stdlib/errno.pyi | 14 ++- mypy/typeshed/stdlib/functools.pyi | 40 ++++--- mypy/typeshed/stdlib/ipaddress.pyi | 1 + mypy/typeshed/stdlib/json/__init__.pyi | 9 -- mypy/typeshed/stdlib/linecache.pyi | 6 +- mypy/typeshed/stdlib/pathlib.pyi | 1 + mypy/typeshed/stdlib/plistlib.pyi | 1 + mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 20 ++++ mypy/typeshed/stdlib/statistics.pyi | 1 + mypy/typeshed/stdlib/tkinter/__init__.pyi | 6 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 4 +- mypy/typeshed/stdlib/typing.pyi | 1 + mypy/typeshed/stdlib/unittest/mock.pyi | 27 ++++- mypy/typeshed/stdlib/uuid.pyi | 40 +++---- 25 files changed, 264 insertions(+), 73 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 756ee86d3342..25d604218a00 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -151,7 +151,11 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 60c609456954..9a90760bd2c2 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -73,6 +73,7 @@ class Decimal: def from_float(cls, __f: float) -> Self: ... def __bool__(self) -> bool: ... def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __hash__(self) -> int: ... def as_tuple(self) -> DecimalTuple: ... def as_integer_ratio(self) -> tuple[int, int]: ... def to_eng_string(self, context: Context | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index b6044fac4628..2402d0bfe721 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -22,6 +22,7 @@ class ReferenceType(Generic[_T]): __callback__: Callable[[ReferenceType[_T]], Any] def __new__(cls, __o: _T, __callback: Callable[[ReferenceType[_T]], Any] | None = ...) -> Self: ... def __call__(self) -> _T | None: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 2054f6e522a1..b1b0fcfa5fd7 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -86,6 +86,7 @@ class TimerHandle(Handle): loop: AbstractEventLoop, context: Context | None = None, ) -> None: ... + def __hash__(self) -> int: ... def when(self) -> float: ... def __lt__(self, other: TimerHandle) -> bool: ... def __le__(self, other: TimerHandle) -> bool: ... diff --git a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi index 08ea8f66559c..47d9bb2f699e 100644 --- a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi +++ b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi @@ -1,5 +1,4 @@ -# This only exists in 3.11+. See VERSIONS. - +import sys from contextvars import Context from types import TracebackType from typing import TypeVar @@ -8,7 +7,10 @@ from typing_extensions import Self from . import _CoroutineLike from .tasks import Task -__all__ = ["TaskGroup"] +if sys.version_info >= (3, 12): + __all__ = ("TaskGroup",) +else: + __all__ = ["TaskGroup"] _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 7415a1b7680d..ea917bddb799 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,6 +56,7 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, + LiteralString, ParamSpec, Self, SupportsIndex, @@ -315,6 +316,7 @@ class int: def __float__(self) -> float: ... def __int__(self) -> int: ... def __abs__(self) -> int: ... + def __hash__(self) -> int: ... def __bool__(self) -> bool: ... def __index__(self) -> int: ... @@ -378,6 +380,7 @@ class float: def __int__(self) -> int: ... def __float__(self) -> float: ... def __abs__(self) -> float: ... + def __hash__(self) -> int: ... def __bool__(self) -> bool: ... class complex: @@ -417,6 +420,7 @@ class complex: def __neg__(self) -> complex: ... def __pos__(self) -> complex: ... def __abs__(self) -> float: ... + def __hash__(self) -> int: ... def __bool__(self) -> bool: ... if sys.version_info >= (3, 11): def __complex__(self) -> complex: ... @@ -432,8 +436,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -441,11 +454,20 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: + @overload + def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -461,32 +483,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -497,6 +578,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -504,13 +588,26 @@ class str(Sequence[str]): def __ge__(self, __value: str) -> bool: ... def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... + def __hash__(self) -> int: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload def __mod__(self, __value: Any) -> str: ... + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -597,6 +694,7 @@ class bytes(Sequence[int]): def maketrans(__frm: ReadableBuffer, __to: ReadableBuffer) -> bytes: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... + def __hash__(self) -> int: ... @overload def __getitem__(self, __key: SupportsIndex) -> int: ... @overload @@ -1004,7 +1102,13 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + @overload + def __or__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ... + @overload def __or__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ... + @overload def __ror__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] @@ -1665,11 +1769,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index e56baf8b52c9..36d79101908d 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -83,8 +83,14 @@ class UserDict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def fromkeys(cls, iterable: Iterable[_T], value: _S) -> UserDict[_T, _S]: ... if sys.version_info >= (3, 9): + @overload + def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... + @overload def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... - def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + @overload # type: ignore[misc] + def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... + @overload # type: ignore[misc] + def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... @@ -391,6 +397,15 @@ class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): def __missing__(self, __key: _KT) -> _VT: ... def __copy__(self) -> Self: ... def copy(self) -> Self: ... + if sys.version_info >= (3, 9): + @overload + def __or__(self, __value: Mapping[_KT, _VT]) -> Self: ... + @overload + def __or__(self, __value: Mapping[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, __value: Mapping[_KT, _VT]) -> Self: ... + @overload + def __ror__(self, __value: Mapping[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] @@ -425,7 +440,13 @@ class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def fromkeys(cls, __iterable: Iterable[_T], __value: _S) -> ChainMap[_T, _S]: ... if sys.version_info >= (3, 9): + @overload + def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... + @overload def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ... + @overload def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 2bb2264c97b1..00d511915f20 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -35,6 +35,7 @@ class timezone(tzinfo): def tzname(self, __dt: datetime | None) -> str: ... def utcoffset(self, __dt: datetime | None) -> timedelta: ... def dst(self, __dt: datetime | None) -> None: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 11): UTC: timezone @@ -106,6 +107,7 @@ class date: @overload def __sub__(self, __value: date) -> timedelta: ... + def __hash__(self) -> int: ... def weekday(self) -> int: ... def isoweekday(self) -> int: ... if sys.version_info >= (3, 9): @@ -143,6 +145,7 @@ class time: def __lt__(self, __value: time) -> bool: ... def __ge__(self, __value: time) -> bool: ... def __gt__(self, __value: time) -> bool: ... + def __hash__(self) -> int: ... def isoformat(self, timespec: str = ...) -> str: ... @classmethod def fromisoformat(cls, __time_string: str) -> Self: ... @@ -217,6 +220,7 @@ class timedelta: def __ge__(self, __value: timedelta) -> bool: ... def __gt__(self, __value: timedelta) -> bool: ... def __bool__(self) -> bool: ... + def __hash__(self) -> int: ... class datetime(date): min: ClassVar[datetime] diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 88d066fdc23c..f3c05781ad92 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -85,6 +85,7 @@ class Example: indent: int = 0, options: dict[int, bool] | None = None, ) -> None: ... + def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... class DocTest: @@ -103,6 +104,7 @@ class DocTest: lineno: int | None, docstring: str | None, ) -> None: ... + def __hash__(self) -> int: ... def __lt__(self, other: DocTest) -> bool: ... def __eq__(self, other: object) -> bool: ... @@ -210,6 +212,7 @@ class DocTestCase(unittest.TestCase): ) -> None: ... def runTest(self) -> None: ... def format_failure(self, err: str) -> str: ... + def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... class SkipDocTestCase(DocTestCase): diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index e612847c75b6..d61950a26424 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,4 +1,6 @@ -from collections.abc import Iterator +from collections.abc import Callable, Iterator +from email.message import Message +from typing import overload __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] @@ -14,10 +16,13 @@ class Charset: input_codec: str | None output_codec: str | None def __init__(self, input_charset: str = "us-ascii") -> None: ... - def get_body_encoding(self) -> str: ... + def get_body_encoding(self) -> str | Callable[[Message], None]: ... def get_output_charset(self) -> str | None: ... def header_encode(self, string: str) -> str: ... def header_encode_lines(self, string: str, maxlengths: Iterator[int]) -> list[str]: ... + @overload + def body_encode(self, string: None) -> None: ... + @overload def body_encode(self, string: str) -> str: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, __value: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index ed63b6b32312..186e768050be 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -60,7 +60,7 @@ else: def localtime(dt: datetime.datetime | None = None, isdst: int = -1) -> datetime.datetime: ... def make_msgid(idstring: str | None = None, domain: str | None = None) -> str: ... -def decode_rfc2231(s: str) -> tuple[str | None, str | None, str]: ... +def decode_rfc2231(s: str) -> tuple[str | None, str | None, str]: ... # May return list[str]. See issue #10431 for details. def encode_rfc2231(s: str, charset: str | None = None, language: str | None = None) -> str: ... def collapse_rfc2231_value(value: _ParamType, errors: str = "replace", fallback_charset: str = "us-ascii") -> str: ... def decode_params(params: list[tuple[str, str]]) -> list[tuple[str, _ParamType]]: ... diff --git a/mypy/typeshed/stdlib/errno.pyi b/mypy/typeshed/stdlib/errno.pyi index 28874d44ff5f..84d2b44a6a61 100644 --- a/mypy/typeshed/stdlib/errno.pyi +++ b/mypy/typeshed/stdlib/errno.pyi @@ -91,9 +91,15 @@ ECANCELED: int # undocumented ENOTRECOVERABLE: int # undocumented EOWNERDEAD: int # undocumented +if sys.platform == "sunos5" or sys.platform == "solaris": # noqa: Y008 + ELOCKUNMAPPED: int + ENOTACTIVE: int + if sys.platform != "win32": ENOTBLK: int EMULTIHOP: int + +if sys.platform == "darwin": # All of the below are undocumented EAUTH: int EBADARCH: int @@ -112,9 +118,8 @@ if sys.platform != "win32": EPWROFF: int ERPCMISMATCH: int ESHLIBVERS: int - - if sys.platform != "darwin" or sys.version_info >= (3, 11): - EQFULL: int # undocumented + if sys.version_info >= (3, 11): + EQFULL: int if sys.platform != "darwin": EDEADLOCK: int @@ -164,9 +169,6 @@ if sys.platform != "win32" and sys.platform != "darwin": ENOKEY: int ENOMEDIUM: int ERFKILL: int - EL: int - ELOCKUNMAPPED: int - ENOTACTIVE: int if sys.platform == "win32": # All of these are undocumented diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 1b4e59b7c120..8adc3d82292e 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,10 +28,12 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -85,31 +87,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 7a4146885b29..fc42cf03e2bb 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -34,6 +34,7 @@ class _IPAddressBase: class _BaseAddress(_IPAddressBase, SupportsInt): def __init__(self, address: object) -> None: ... def __add__(self, other: int) -> Self: ... + def __hash__(self) -> int: ... def __int__(self) -> int: ... def __sub__(self, other: int) -> Self: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/json/__init__.pyi b/mypy/typeshed/stdlib/json/__init__.pyi index dc0cdff926d4..63e9718ee151 100644 --- a/mypy/typeshed/stdlib/json/__init__.pyi +++ b/mypy/typeshed/stdlib/json/__init__.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import SupportsRead, SupportsWrite from collections.abc import Callable from typing import Any @@ -7,8 +6,6 @@ from .decoder import JSONDecodeError as JSONDecodeError, JSONDecoder as JSONDeco from .encoder import JSONEncoder as JSONEncoder __all__ = ["dump", "dumps", "load", "loads", "JSONDecoder", "JSONDecodeError", "JSONEncoder"] -if sys.version_info >= (3, 12): - __all__ += ["AttrDict"] def dumps( obj: Any, @@ -62,9 +59,3 @@ def load( **kwds: Any, ) -> Any: ... def detect_encoding(b: bytes | bytearray) -> str: ... # undocumented - -if sys.version_info >= (3, 12): - class AttrDict(dict[str, Any]): - def __getattr__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... diff --git a/mypy/typeshed/stdlib/linecache.pyi b/mypy/typeshed/stdlib/linecache.pyi index 8e317dd38990..2e050e13b621 100644 --- a/mypy/typeshed/stdlib/linecache.pyi +++ b/mypy/typeshed/stdlib/linecache.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Protocol +from collections.abc import Callable +from typing import Any from typing_extensions import TypeAlias if sys.version_info >= (3, 9): @@ -10,8 +11,7 @@ else: _ModuleGlobals: TypeAlias = dict[str, Any] _ModuleMetadata: TypeAlias = tuple[int, float | None, list[str], str] -class _SourceLoader(Protocol): - def __call__(self) -> str | None: ... +_SourceLoader: TypeAlias = tuple[Callable[[], str | None]] cache: dict[str, _SourceLoader | _ModuleMetadata] # undocumented diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 3c2ae0fe7ab1..a509ec3af9f2 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -39,6 +39,7 @@ class PurePath(PathLike[str]): @property def stem(self) -> str: ... def __new__(cls, *args: StrPath) -> Self: ... + def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... def __fspath__(self) -> str: ... def __lt__(self, other: PurePath) -> bool: ... diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index 5b76c935f76e..bd5525484514 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -102,6 +102,7 @@ if sys.version_info >= (3, 8): def __init__(self, data: int) -> None: ... def __index__(self) -> int: ... def __reduce__(self) -> tuple[type[Self], tuple[int]]: ... + def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... class InvalidFileException(ValueError): diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 24974f787c62..cff0f5e5ff1d 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -196,6 +196,25 @@ if sys.version_info >= (3, 11): SQLITE_WARNING: int SQLITE_WARNING_AUTOINDEX: int +if sys.version_info >= (3, 12): + LEGACY_TRANSACTION_CONTROL: int + SQLITE_DBCONFIG_DEFENSIVE: int + SQLITE_DBCONFIG_DQS_DDL: int + SQLITE_DBCONFIG_DQS_DML: int + SQLITE_DBCONFIG_ENABLE_FKEY: int + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: int + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: int + SQLITE_DBCONFIG_ENABLE_QPSG: int + SQLITE_DBCONFIG_ENABLE_TRIGGER: int + SQLITE_DBCONFIG_ENABLE_VIEW: int + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: int + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: int + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: int + SQLITE_DBCONFIG_RESET_DATABASE: int + SQLITE_DBCONFIG_TRIGGER_EQP: int + SQLITE_DBCONFIG_TRUSTED_SCHEMA: int + SQLITE_DBCONFIG_WRITABLE_SCHEMA: int + # Can take or return anything depending on what's in the registry. @overload def adapt(__obj: Any, __proto: Any) -> Any: ... @@ -419,6 +438,7 @@ class Row: def __getitem__(self, __key: int | str) -> Any: ... @overload def __getitem__(self, __key: slice) -> tuple[Any, ...]: ... + def __hash__(self) -> int: ... def __iter__(self) -> Iterator[Any]: ... def __len__(self) -> int: ... # These return NotImplemented for anything that is not a Row. diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index af5fcec6ad0c..07174f4531b9 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -113,6 +113,7 @@ if sys.version_info >= (3, 8): __radd__ = __add__ def __rsub__(self, x2: float | NormalDist) -> NormalDist: ... __rmul__ = __mul__ + def __hash__(self) -> int: ... if sys.version_info >= (3, 12): def correlation( diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 3291b0c9dd98..a03c48c039dd 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -500,7 +500,7 @@ class Misc: bbox = grid_bbox def grid_columnconfigure( self, - index: _GridIndex, + index: _GridIndex | list[int] | tuple[int, ...], cnf: _GridIndexInfo = {}, *, minsize: _ScreenUnits = ..., @@ -510,7 +510,7 @@ class Misc: ) -> _GridIndexInfo | Any: ... # can be None but annoying to check def grid_rowconfigure( self, - index: _GridIndex, + index: _GridIndex | list[int] | tuple[int, ...], cnf: _GridIndexInfo = {}, *, minsize: _ScreenUnits = ..., @@ -1633,6 +1633,7 @@ class Canvas(Widget, XView, YView): activefill: str = ..., activestipple: str = ..., anchor: _Anchor = ..., + angle: float | str = ..., disabledfill: str = ..., disabledstipple: str = ..., fill: str = ..., @@ -1653,6 +1654,7 @@ class Canvas(Widget, XView, YView): activefill: str = ..., activestipple: str = ..., anchor: _Anchor = ..., + angle: float | str = ..., disabledfill: str = ..., disabledstipple: str = ..., fill: str = ..., diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index 009fdf51a440..d73566fc0917 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -961,7 +961,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): master: tkinter.Misc | None = None, *, class_: str = ..., - columns: str | list[str] | tuple[str, ...] = ..., + columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = ..., cursor: tkinter._Cursor = ..., displaycolumns: str | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., height: int = ..., @@ -983,7 +983,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): self, cnf: dict[str, Any] | None = None, *, - columns: str | list[str] | tuple[str, ...] = ..., + columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = ..., cursor: tkinter._Cursor = ..., displaycolumns: str | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., height: int = ..., diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 2c5f820ea365..7496a0920690 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -921,6 +921,7 @@ class ForwardRef: def _evaluate(self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None) -> Any | None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 11): def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 0ed0701cc450..db1cc7d9bfc9 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -234,6 +234,8 @@ class _patch(Generic[_T]): def copy(self) -> _patch[_T]: ... @overload def __call__(self, func: _TT) -> _TT: ... + # If new==DEFAULT, this should add a MagicMock parameter to the function + # arguments. See the _patch_default_new class below for this functionality. @overload def __call__(self, func: Callable[_P, _R]) -> Callable[_P, _R]: ... if sys.version_info >= (3, 8): @@ -257,6 +259,22 @@ class _patch(Generic[_T]): def start(self) -> _T: ... def stop(self) -> None: ... +if sys.version_info >= (3, 8): + _Mock: TypeAlias = MagicMock | AsyncMock +else: + _Mock: TypeAlias = MagicMock + +# This class does not exist at runtime, it's a hack to make this work: +# @patch("foo") +# def bar(..., mock: MagicMock) -> None: ... +class _patch_default_new(_patch[_Mock]): + @overload + def __call__(self, func: _TT) -> _TT: ... + # Can't use the following as ParamSpec is only allowed as last parameter: + # def __call__(self, func: Callable[_P, _R]) -> Callable[Concatenate[_P, MagicMock], _R]: ... + @overload + def __call__(self, func: Callable[..., _R]) -> Callable[..., _R]: ... + class _patch_dict: in_dict: Any values: Any @@ -273,11 +291,8 @@ class _patch_dict: start: Any stop: Any -if sys.version_info >= (3, 8): - _Mock: TypeAlias = MagicMock | AsyncMock -else: - _Mock: TypeAlias = MagicMock - +# This class does not exist at runtime, it's a hack to add methods to the +# patch() function. class _patcher: TEST_PREFIX: str dict: type[_patch_dict] @@ -307,7 +322,7 @@ class _patcher: autospec: Any | None = ..., new_callable: Any | None = ..., **kwargs: Any, - ) -> _patch[_Mock]: ... + ) -> _patch_default_new: ... @overload @staticmethod def object( # type: ignore[misc] diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index 74ce4ebd6b47..fd87646531a6 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -1,11 +1,9 @@ +import builtins import sys from _typeshed import Unused from enum import Enum from typing_extensions import TypeAlias -# Because UUID has properties called int and bytes we need to rename these temporarily. -_Int: TypeAlias = int -_Bytes: TypeAlias = bytes _FieldsType: TypeAlias = tuple[int, int, int, int, int, int] class SafeUUID(Enum): @@ -17,49 +15,49 @@ class UUID: def __init__( self, hex: str | None = None, - bytes: _Bytes | None = None, - bytes_le: _Bytes | None = None, + bytes: builtins.bytes | None = None, + bytes_le: builtins.bytes | None = None, fields: _FieldsType | None = None, - int: _Int | None = None, - version: _Int | None = None, + int: builtins.int | None = None, + version: builtins.int | None = None, *, is_safe: SafeUUID = ..., ) -> None: ... @property def is_safe(self) -> SafeUUID: ... @property - def bytes(self) -> _Bytes: ... + def bytes(self) -> builtins.bytes: ... @property - def bytes_le(self) -> _Bytes: ... + def bytes_le(self) -> builtins.bytes: ... @property - def clock_seq(self) -> _Int: ... + def clock_seq(self) -> builtins.int: ... @property - def clock_seq_hi_variant(self) -> _Int: ... + def clock_seq_hi_variant(self) -> builtins.int: ... @property - def clock_seq_low(self) -> _Int: ... + def clock_seq_low(self) -> builtins.int: ... @property def fields(self) -> _FieldsType: ... @property def hex(self) -> str: ... @property - def int(self) -> _Int: ... + def int(self) -> builtins.int: ... @property - def node(self) -> _Int: ... + def node(self) -> builtins.int: ... @property - def time(self) -> _Int: ... + def time(self) -> builtins.int: ... @property - def time_hi_version(self) -> _Int: ... + def time_hi_version(self) -> builtins.int: ... @property - def time_low(self) -> _Int: ... + def time_low(self) -> builtins.int: ... @property - def time_mid(self) -> _Int: ... + def time_mid(self) -> builtins.int: ... @property def urn(self) -> str: ... @property def variant(self) -> str: ... @property - def version(self) -> _Int | None: ... - def __int__(self) -> _Int: ... + def version(self) -> builtins.int | None: ... + def __int__(self) -> builtins.int: ... def __eq__(self, other: object) -> bool: ... def __lt__(self, other: UUID) -> bool: ... def __le__(self, other: UUID) -> bool: ... @@ -72,7 +70,7 @@ if sys.version_info >= (3, 9): else: def getnode(*, getters: Unused = None) -> int: ... # undocumented -def uuid1(node: _Int | None = None, clock_seq: _Int | None = None) -> UUID: ... +def uuid1(node: int | None = None, clock_seq: int | None = None) -> UUID: ... if sys.version_info >= (3, 12): def uuid3(namespace: UUID, name: str | bytes) -> UUID: ... From 2f6b6e66ced53a0412c46851afb963f35a012f79 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0145/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index ea917bddb799..3de404b76652 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,7 +56,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, - LiteralString, ParamSpec, Self, SupportsIndex, @@ -436,17 +435,8 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -454,20 +444,11 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: - @overload - def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -483,91 +464,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -578,9 +500,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -589,25 +508,13 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From 120af30e706a0e0e29faf915c56c5b6781c9b204 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0146/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 3de404b76652..d6ca39049c77 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1676,11 +1676,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... From 1866d28f156c413ce8cc9fec0b317a7d02c28565 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0147/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 25d604218a00..756ee86d3342 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -151,11 +151,7 @@ class Array(Generic[_CT], _CData): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 3240da455e06a292669de22b0ef313dad43cb094 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0148/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 8adc3d82292e..1b4e59b7c120 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,12 +28,10 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWapper = TypeVar("_RWapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -87,41 +85,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From 3f601c3641ecde3557520ddc64a18baa40b12e35 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 17 Jul 2023 02:09:28 +0100 Subject: [PATCH 0149/1617] Remove unneeded `--strict-optional` flags from test cases (#15684) --- test-data/unit/check-abstract.test | 17 --------- test-data/unit/check-basic.test | 1 - test-data/unit/check-class-namedtuple.test | 1 - test-data/unit/check-classes.test | 36 ++++++++----------- test-data/unit/check-columns.test | 1 - test-data/unit/check-custom-plugin.test | 2 +- test-data/unit/check-dataclasses.test | 8 ++--- test-data/unit/check-enum.test | 4 --- test-data/unit/check-errorcodes.test | 7 +--- test-data/unit/check-expressions.test | 5 ++- test-data/unit/check-flags.test | 2 +- test-data/unit/check-functions.test | 2 -- test-data/unit/check-generics.test | 7 ++-- test-data/unit/check-incremental.test | 13 ++----- test-data/unit/check-inference-context.test | 13 ------- test-data/unit/check-inference.test | 19 +++------- test-data/unit/check-inline-config.test | 4 +-- test-data/unit/check-isinstance.test | 15 -------- test-data/unit/check-kwargs.test | 1 - test-data/unit/check-lists.test | 1 - test-data/unit/check-literal.test | 6 ---- test-data/unit/check-modules.test | 1 - test-data/unit/check-narrowing.test | 13 ++----- test-data/unit/check-native-int.test | 2 -- test-data/unit/check-newsyntax.test | 4 --- test-data/unit/check-overloading.test | 11 ++---- .../unit/check-parameter-specification.test | 1 - test-data/unit/check-plugin-attrs.test | 2 -- test-data/unit/check-protocols.test | 19 ++++------ test-data/unit/check-python310.test | 5 --- test-data/unit/check-python38.test | 10 +++--- test-data/unit/check-recursive-types.test | 4 --- test-data/unit/check-serialize.test | 1 - test-data/unit/check-type-aliases.test | 1 - test-data/unit/check-typeddict.test | 8 ----- test-data/unit/check-typeguard.test | 1 - test-data/unit/check-unions.test | 5 --- test-data/unit/check-unreachable-code.test | 1 - test-data/unit/check-varargs.test | 1 - test-data/unit/check-warnings.test | 2 +- test-data/unit/deps.test | 1 - test-data/unit/fine-grained-suggest.test | 17 --------- test-data/unit/fine-grained.test | 22 ++++-------- test-data/unit/pythoneval.test | 9 +++-- 44 files changed, 58 insertions(+), 248 deletions(-) diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index dc64476beda6..299074050baa 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -1125,7 +1125,6 @@ b.y = 1 -- ----------------------------------------------- [case testEmptyBodyProhibitedFunction] -# flags: --strict-optional from typing import overload, Union def func1(x: str) -> int: pass # E: Missing return statement @@ -1148,7 +1147,6 @@ def func5(x: Union[int, str]) -> Union[int, str]: # E: Missing return statement """Some function.""" [case testEmptyBodyProhibitedMethodNonAbstract] -# flags: --strict-optional from typing import overload, Union class A: @@ -1183,7 +1181,6 @@ class C: [builtins fixtures/classmethod.pyi] [case testEmptyBodyProhibitedPropertyNonAbstract] -# flags: --strict-optional class A: @property def x(self) -> int: ... # E: Missing return statement @@ -1212,7 +1209,6 @@ class C: [builtins fixtures/property.pyi] [case testEmptyBodyNoteABCMeta] -# flags: --strict-optional from abc import ABC class A(ABC): @@ -1221,7 +1217,6 @@ class A(ABC): ... [case testEmptyBodyAllowedFunctionStub] -# flags: --strict-optional import stub [file stub.pyi] from typing import overload, Union @@ -1232,7 +1227,6 @@ def func3(x: str) -> int: """Some function.""" [case testEmptyBodyAllowedMethodNonAbstractStub] -# flags: --strict-optional import stub [file stub.pyi] from typing import overload, Union @@ -1254,7 +1248,6 @@ class B: [builtins fixtures/classmethod.pyi] [case testEmptyBodyAllowedPropertyNonAbstractStub] -# flags: --strict-optional import stub [file stub.pyi] class A: @@ -1285,7 +1278,6 @@ class C: [builtins fixtures/property.pyi] [case testEmptyBodyAllowedMethodAbstract] -# flags: --strict-optional from typing import overload, Union from abc import abstractmethod @@ -1333,7 +1325,6 @@ class C: [builtins fixtures/classmethod.pyi] [case testEmptyBodyAllowedPropertyAbstract] -# flags: --strict-optional from abc import abstractmethod class A: @property @@ -1372,7 +1363,6 @@ class C: [builtins fixtures/property.pyi] [case testEmptyBodyImplicitlyAbstractProtocol] -# flags: --strict-optional from typing import Protocol, overload, Union class P1(Protocol): @@ -1413,7 +1403,6 @@ C3() [builtins fixtures/classmethod.pyi] [case testEmptyBodyImplicitlyAbstractProtocolProperty] -# flags: --strict-optional from typing import Protocol class P1(Protocol): @@ -1443,7 +1432,6 @@ C2() [builtins fixtures/property.pyi] [case testEmptyBodyImplicitlyAbstractProtocolStub] -# flags: --strict-optional from stub import P1, P2, P3, P4 class B1(P1): ... @@ -1479,7 +1467,6 @@ class P4(Protocol): [builtins fixtures/classmethod.pyi] [case testEmptyBodyUnsafeAbstractSuper] -# flags: --strict-optional from stub import StubProto, StubAbstract from typing import Protocol from abc import abstractmethod @@ -1528,7 +1515,6 @@ class StubAbstract: def meth(self) -> int: ... [case testEmptyBodyUnsafeAbstractSuperProperty] -# flags: --strict-optional from stub import StubProto, StubAbstract from typing import Protocol from abc import abstractmethod @@ -1586,7 +1572,6 @@ class StubAbstract: [builtins fixtures/property.pyi] [case testEmptyBodyUnsafeAbstractSuperOverloads] -# flags: --strict-optional from stub import StubProto from typing import Protocol, overload, Union @@ -1671,7 +1656,6 @@ class SubAbstract(Abstract): return super().meth() [case testEmptyBodyNoSuperWarningOptionalReturn] -# flags: --strict-optional from typing import Protocol, Optional from abc import abstractmethod @@ -1689,7 +1673,6 @@ class SubAbstract(Abstract): return super().meth() [case testEmptyBodyTypeCheckingOnly] -# flags: --strict-optional from typing import TYPE_CHECKING class C: diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 408c3599672b..61a7160ce4f4 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -385,7 +385,6 @@ y = x # E: Incompatible types in assignment (expression has type "Dict[str, int] [builtins fixtures/dict.pyi] [case testDistinctTypes] -# flags: --strict-optional import b [file a.py] diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index 1916cb41bb74..a095f212b900 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -419,7 +419,6 @@ UserDefined(1) # E: Argument 1 to "UserDefined" has incompatible type "int"; ex [builtins fixtures/list.pyi] [case testNewNamedTupleWithDefaultsStrictOptional] -# flags: --strict-optional from typing import List, NamedTuple, Optional class HasNone(NamedTuple): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 957eb9214d7c..b9e65ef4ad20 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -935,7 +935,6 @@ if int(): b = D2() [case testConstructorJoinsWithCustomMetaclass] -# flags: --strict-optional from typing import TypeVar import abc @@ -1629,7 +1628,6 @@ a = A() reveal_type(a.f) # N: Revealed type is "__main__.D" [case testAccessingDescriptorFromClass] -# flags: --strict-optional from d import D, Base class A(Base): f = D() @@ -1647,7 +1645,6 @@ class D: [builtins fixtures/bool.pyi] [case testAccessingDescriptorFromClassWrongBase] -# flags: --strict-optional from d import D, Base class A: f = D() @@ -1664,13 +1661,13 @@ class D: def __get__(self, inst: Base, own: Type[Base]) -> str: pass [builtins fixtures/bool.pyi] [out] -main:5: error: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "Type[Base]" -main:5: note: Revealed type is "d.D" -main:6: error: No overload variant of "__get__" of "D" matches argument types "A", "Type[A]" -main:6: note: Possible overload variants: -main:6: note: def __get__(self, inst: None, own: Type[Base]) -> D -main:6: note: def __get__(self, inst: Base, own: Type[Base]) -> str -main:6: note: Revealed type is "Any" +main:4: error: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "Type[Base]" +main:4: note: Revealed type is "d.D" +main:5: error: No overload variant of "__get__" of "D" matches argument types "A", "Type[A]" +main:5: note: Possible overload variants: +main:5: note: def __get__(self, inst: None, own: Type[Base]) -> D +main:5: note: def __get__(self, inst: Base, own: Type[Base]) -> str +main:5: note: Revealed type is "Any" [case testAccessingGenericNonDataDescriptor] from typing import TypeVar, Type, Generic, Any @@ -1702,7 +1699,6 @@ a.g = '' a.g = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testAccessingGenericDescriptorFromClass] -# flags: --strict-optional from d import D class A: f = D(10) # type: D[A, int] @@ -1724,7 +1720,6 @@ class D(Generic[T, V]): [builtins fixtures/bool.pyi] [case testAccessingGenericDescriptorFromInferredClass] -# flags: --strict-optional from typing import Type from d import D class A: @@ -1745,11 +1740,10 @@ class D(Generic[T, V]): def __get__(self, inst: T, own: Type[T]) -> V: pass [builtins fixtures/bool.pyi] [out] -main:8: note: Revealed type is "d.D[__main__.A, builtins.int]" -main:9: note: Revealed type is "d.D[__main__.A, builtins.str]" +main:7: note: Revealed type is "d.D[__main__.A, builtins.int]" +main:8: note: Revealed type is "d.D[__main__.A, builtins.str]" [case testAccessingGenericDescriptorFromClassBadOverload] -# flags: --strict-optional from d import D class A: f = D(10) # type: D[A, int] @@ -1766,11 +1760,11 @@ class D(Generic[T, V]): def __get__(self, inst: T, own: Type[T]) -> V: pass [builtins fixtures/bool.pyi] [out] -main:5: error: No overload variant of "__get__" of "D" matches argument types "None", "Type[A]" -main:5: note: Possible overload variants: -main:5: note: def __get__(self, inst: None, own: None) -> D[A, int] -main:5: note: def __get__(self, inst: A, own: Type[A]) -> int -main:5: note: Revealed type is "Any" +main:4: error: No overload variant of "__get__" of "D" matches argument types "None", "Type[A]" +main:4: note: Possible overload variants: +main:4: note: def __get__(self, inst: None, own: None) -> D[A, int] +main:4: note: def __get__(self, inst: A, own: Type[A]) -> int +main:4: note: Revealed type is "Any" [case testAccessingNonDataDescriptorSubclass] from typing import Any @@ -6484,7 +6478,6 @@ def deco(f: Callable[..., T]) -> Callable[..., Tuple[T, int]]: ... [out] [case testOptionalDescriptorsBinder] -# flags: --strict-optional from typing import Type, TypeVar, Optional T = TypeVar('T') @@ -6698,7 +6691,6 @@ class C(Generic[T]): [builtins fixtures/isinstancelist.pyi] [case testIsInstanceTypeSubclass] -# flags: --strict-optional from typing import Type, Optional class Base: ... class One(Base): diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 9d9a7d9ac039..44524b9df943 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -27,7 +27,6 @@ A().f(1, 1) # E:10: Argument 2 to "f" of "A" has incompatible type "int"; expect (A().f(1, 'hello', 'hi')) # E:2: Too many arguments for "f" of "A" [case testColumnsInvalidArgumentType] -# flags: --strict-optional def f(x: int, y: str) -> None: ... def g(*x: int) -> None: pass def h(**x: int) -> None: pass diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index ec5bce219dbd..9a0668f98c21 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -802,7 +802,7 @@ else: plugins=/test-data/unit/plugins/union_method.py [case testGetMethodHooksOnUnionsStrictOptional] -# flags: --config-file tmp/mypy.ini --strict-optional +# flags: --config-file tmp/mypy.ini from typing import Union class Foo: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index adcaa60a5b19..3866442230bf 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1127,7 +1127,6 @@ class Foo: [case testNoComplainFieldNoneStrict] # flags: --python-version 3.7 -# flags: --strict-optional from dataclasses import dataclass, field from typing import Optional @@ -1264,7 +1263,7 @@ class Deferred: pass [builtins fixtures/dataclasses.pyi] [case testDeferredDataclassInitSignatureSubclass] -# flags: --strict-optional --python-version 3.7 +# flags: --python-version 3.7 from dataclasses import dataclass from typing import Optional @@ -1745,7 +1744,7 @@ reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[ [builtins fixtures/dataclasses.pyi] [case testDataclassInheritOptionalType] -# flags: --python-version 3.7 --strict-optional +# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any, Callable, Generic, TypeVar, List, Optional @@ -1979,7 +1978,6 @@ B = List[C] [builtins fixtures/dataclasses.pyi] [case testDataclassSelfType] -# flags: --strict-optional from dataclasses import dataclass from typing import Self, TypeVar, Generic, Optional @@ -2104,7 +2102,6 @@ a2 = replace(a, q='42') # E: Argument "q" to "replace" of "A" has incompatible reveal_type(a2) # N: Revealed type is "__main__.A" [case testReplaceUnion] -# flags: --strict-optional from typing import Generic, Union, TypeVar from dataclasses import dataclass, replace, InitVar @@ -2136,7 +2133,6 @@ _ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union [builtins fixtures/dataclasses.pyi] [case testReplaceUnionOfTypeVar] -# flags: --strict-optional from typing import Generic, Union, TypeVar from dataclasses import dataclass, replace diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index ce7e173f635d..6779ae266454 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -953,7 +953,6 @@ else: [builtins fixtures/bool.pyi] [case testEnumReachabilityWithNone] -# flags: --strict-optional from enum import Enum from typing import Optional @@ -1016,7 +1015,6 @@ reveal_type(x3) # N: Revealed type is "Union[__main__.Foo, __main__.Bar]" [builtins fixtures/bool.pyi] [case testEnumReachabilityPEP484ExampleWithFinal] -# flags: --strict-optional from typing import Union from typing_extensions import Final from enum import Enum @@ -1063,7 +1061,6 @@ def process(response: Union[str, Reason] = '') -> str: [case testEnumReachabilityPEP484ExampleSingleton] -# flags: --strict-optional from typing import Union from typing_extensions import Final from enum import Enum @@ -1088,7 +1085,6 @@ def func(x: Union[int, None, Empty] = _empty) -> int: [builtins fixtures/primitives.pyi] [case testEnumReachabilityPEP484ExampleSingletonWithMethod] -# flags: --strict-optional from typing import Union from typing_extensions import Final from enum import Enum diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1e7dc9364855..1efbab7de322 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -732,7 +732,6 @@ class InvalidReturn: [builtins fixtures/bool.pyi] [case testErrorCodeOverloadedOperatorMethod] -# flags: --strict-optional from typing import Optional, overload class A: @@ -758,7 +757,6 @@ class C: x - C() # type: ignore[operator] [case testErrorCodeMultiLineBinaryOperatorOperand] -# flags: --strict-optional from typing import Optional class C: pass @@ -897,7 +895,6 @@ if any_or_object: [builtins fixtures/list.pyi] [case testTruthyFunctions] -# flags: --strict-optional def f(): pass if f: # E: Function "f" could always be true in boolean context [truthy-function] @@ -907,7 +904,7 @@ if not f: # E: Function "f" could always be true in boolean context [truthy-fu conditional_result = 'foo' if f else 'bar' # E: Function "f" could always be true in boolean context [truthy-function] [case testTruthyIterable] -# flags: --strict-optional --enable-error-code truthy-iterable +# flags: --enable-error-code truthy-iterable from typing import Iterable def func(var: Iterable[str]) -> None: if var: # E: "var" has type "Iterable[str]" which can always be true in boolean context. Consider using "Collection[str]" instead. [truthy-iterable] @@ -995,7 +992,6 @@ var: int = "" # E: Incompatible types in assignment (expression has type "str", show_error_codes = True [case testErrorCodeUnsafeSuper_no_empty] -# flags: --strict-optional from abc import abstractmethod class Base: @@ -1008,7 +1004,6 @@ class Sub(Base): [builtins fixtures/exception.pyi] [case testDedicatedErrorCodeForEmpty_no_empty] -# flags: --strict-optional from typing import Optional def foo() -> int: ... # E: Missing return statement [empty-body] def bar() -> None: ... diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 8231b0a3265f..40ee28830b21 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1210,7 +1210,6 @@ a[:None] [builtins fixtures/slice.pyi] [case testNoneSliceBoundsWithStrictOptional] -# flags: --strict-optional from typing import Any a: Any a[None:1] @@ -2049,7 +2048,7 @@ x is 42 [typing fixtures/typing-full.pyi] [case testStrictEqualityStrictOptional] -# flags: --strict-equality --strict-optional +# flags: --strict-equality x: str if x is not None: # OK even with strict-optional @@ -2065,7 +2064,7 @@ if x is not None: # OK without strict-optional [builtins fixtures/bool.pyi] [case testStrictEqualityEqNoOptionalOverlap] -# flags: --strict-equality --strict-optional +# flags: --strict-equality from typing import Optional x: Optional[str] diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 3750c44ed7f3..e21157eae991 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -427,7 +427,7 @@ async def h() -> NoReturn: # E: Implicit return in function which does not retu [typing fixtures/typing-async.pyi] [case testNoWarnNoReturn] -# flags: --no-warn-no-return --strict-optional +# flags: --no-warn-no-return import typing def implicit_optional_return(arg) -> typing.Optional[str]: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 0de4798ea1f5..a8722d8190b9 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2545,7 +2545,6 @@ reveal_type(bar(None)) # N: Revealed type is "None" [out] [case testNoComplainOverloadNoneStrict] -# flags: --strict-optional from typing import overload, Optional @overload def bar(x: None) -> None: @@ -2574,7 +2573,6 @@ xx: Optional[int] = X(x_in) [out] [case testNoComplainInferredNoneStrict] -# flags: --strict-optional from typing import TypeVar, Optional T = TypeVar('T') def X(val: T) -> T: ... diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 90d46c217451..34588bfceb3d 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2323,7 +2323,6 @@ class B(A): [builtins fixtures/classmethod.pyi] [case testSubclassingGenericSelfClassMethodOptional] -# flags: --strict-optional from typing import TypeVar, Type, Optional AT = TypeVar('AT', bound='A') @@ -2935,7 +2934,7 @@ reveal_type(dec(id)) # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1] [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericProtocol] -# flags: --strict-optional --new-type-inference +# flags: --new-type-inference from typing import TypeVar, Protocol, Generic, Optional T = TypeVar('T') @@ -2951,7 +2950,7 @@ reveal_type(lift(g)) # N: Revealed type is "def [T] (Union[T`1, None]) -> Union [builtins fixtures/list.pyi] [case testInferenceAgainstGenericSplitOrder] -# flags: --strict-optional --new-type-inference +# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2966,7 +2965,7 @@ reveal_type(dec(id, id)) # N: Revealed type is "def (builtins.int) -> builtins. [builtins fixtures/list.pyi] [case testInferenceAgainstGenericSplitOrderGeneric] -# flags: --strict-optional --new-type-inference +# flags: --new-type-inference from typing import TypeVar, Callable, Tuple S = TypeVar('S') diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index d8461fc78815..80f5e4e7d12d 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2901,7 +2901,6 @@ tmp/main.py:2: error: Expression has type "Any" tmp/main.py:2: error: Expression has type "Any" [case testIncrementalStrictOptional] -# flags: --strict-optional import a 1 + a.foo() [file a.py] @@ -2911,8 +2910,8 @@ from typing import Optional def foo() -> Optional[int]: return 0 [out1] [out2] -main:3: error: Unsupported operand types for + ("int" and "None") -main:3: note: Right operand is of type "Optional[int]" +main:2: error: Unsupported operand types for + ("int" and "None") +main:2: note: Right operand is of type "Optional[int]" [case testAttrsIncrementalSubclassingCached] from a import A @@ -3457,7 +3456,6 @@ main:2: error: Cannot find implementation or library stub for module named "a" main:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [case testIncrementalInheritanceAddAnnotation] -# flags: --strict-optional import a [file a.py] import b @@ -5757,7 +5755,6 @@ class C: [builtins fixtures/tuple.pyi] [case testNamedTupleUpdateNonRecursiveToRecursiveCoarse] -# flags: --strict-optional import c [file a.py] from b import M @@ -5800,7 +5797,6 @@ tmp/c.py:5: error: Incompatible types in assignment (expression has type "Option tmp/c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" [case testTupleTypeUpdateNonRecursiveToRecursiveCoarse] -# flags: --strict-optional import c [file a.py] from b import M @@ -5833,7 +5829,6 @@ tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypeAliasUpdateNonRecursiveToRecursiveCoarse] -# flags: --strict-optional import c [file a.py] from b import M @@ -5866,7 +5861,6 @@ tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypedDictUpdateNonRecursiveToRecursiveCoarse] -# flags: --strict-optional import c [file a.py] from b import M @@ -6061,7 +6055,6 @@ tmp/m.py:9: note: Got: tmp/m.py:9: note: def update() -> str [case testAbstractBodyTurnsEmptyCoarse] -# flags: --strict-optional from b import Base class Sub(Base): @@ -6081,7 +6074,7 @@ class Base: def meth(self) -> int: ... [out] [out2] -main:6: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe +main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe [case testNoCrashDoubleReexportFunctionEmpty] import m diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 59f515490964..ba36c1548532 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -925,7 +925,6 @@ reveal_type(f(None)) # N: Revealed type is "Union[None, builtins.list[builtins.i [builtins fixtures/list.pyi] [case testUnionWithGenericTypeItemContextAndStrictOptional] -# flags: --strict-optional from typing import TypeVar, Union, List T = TypeVar('T') @@ -953,7 +952,6 @@ reveal_type(c.f(None)) # N: Revealed type is "Union[builtins.list[builtins.int], [builtins fixtures/list.pyi] [case testGenericMethodCalledInGenericContext] -# flags: --strict-optional from typing import TypeVar, Generic _KT = TypeVar('_KT') @@ -1221,7 +1219,6 @@ x: Iterable[Union[A, B]] = f(B()) [builtins fixtures/list.pyi] [case testWideOuterContextOptional] -# flags: --strict-optional from typing import Optional, Type, TypeVar class Custom: @@ -1235,7 +1232,6 @@ def b(x: T) -> Optional[T]: return a(x) [case testWideOuterContextOptionalGenericReturn] -# flags: --strict-optional from typing import Optional, Type, TypeVar, Iterable class Custom: @@ -1249,7 +1245,6 @@ def b(x: T) -> Iterable[Optional[T]]: return a(x) [case testWideOuterContextOptionalMethod] -# flags: --strict-optional from typing import Optional, Type, TypeVar class A: pass @@ -1282,7 +1277,6 @@ def bar(xs: List[S]) -> S: [builtins fixtures/list.pyi] [case testWideOuterContextOptionalTypeVarReturn] -# flags: --strict-optional from typing import Callable, Iterable, List, Optional, TypeVar class C: @@ -1298,7 +1292,6 @@ def g(l: List[C], x: str) -> Optional[C]: [builtins fixtures/list.pyi] [case testWideOuterContextOptionalTypeVarReturnLambda] -# flags: --strict-optional from typing import Callable, Iterable, List, Optional, TypeVar class C: @@ -1335,7 +1328,6 @@ y: List[str] = f([]) \ [builtins fixtures/list.pyi] [case testWideOuterContextNoArgs] -# flags: --strict-optional from typing import TypeVar, Optional T = TypeVar('T', bound=int) @@ -1344,7 +1336,6 @@ def f(x: Optional[T] = None) -> T: ... y: str = f() [case testWideOuterContextNoArgsError] -# flags: --strict-optional from typing import TypeVar, Optional, List T = TypeVar('T', bound=int) @@ -1427,7 +1418,6 @@ bar({1: 2}) [builtins fixtures/dict.pyi] [case testOptionalTypeNarrowedByGenericCall] -# flags: --strict-optional from typing import Dict, Optional d: Dict[str, str] = {} @@ -1439,7 +1429,6 @@ def foo(arg: Optional[str] = None) -> None: [builtins fixtures/dict.pyi] [case testOptionalTypeNarrowedByGenericCall2] -# flags: --strict-optional from typing import Dict, Optional d: Dict[str, str] = {} @@ -1451,7 +1440,6 @@ if x: [builtins fixtures/dict.pyi] [case testOptionalTypeNarrowedByGenericCall3] -# flags: --strict-optional from typing import Generic, TypeVar, Union T = TypeVar("T") @@ -1464,7 +1452,6 @@ def foo(arg: Union[str, int]) -> None: [builtins fixtures/isinstance.pyi] [case testOptionalTypeNarrowedByGenericCall4] -# flags: --strict-optional from typing import Optional, List, Generic, TypeVar T = TypeVar("T", covariant=True) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 3c4a0943556a..e0f29a19ec1d 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1402,7 +1402,6 @@ f(b) g(b) [case testLambdaDefaultContext] -# flags: --strict-optional from typing import Callable def f(a: Callable[..., None] = lambda *a, **k: None): pass @@ -1811,7 +1810,6 @@ reveal_type(C().a) # N: Revealed type is "builtins.dict[builtins.int, builtins. [builtins fixtures/dict.pyi] [case testInferAttributeInitializedToNoneAndAssigned] -# flags: --strict-optional class C: def __init__(self) -> None: self.a = None @@ -1858,7 +1856,6 @@ reveal_type(C().a) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] [case testInferAttributeInitializedToNoneAndAssignedOtherMethod] -# flags: --strict-optional class C: def __init__(self) -> None: self.a = None @@ -1891,7 +1888,6 @@ reveal_type(C().a) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] [case testInferAttributeInitializedToNoneAndAssignedClassBody] -# flags: --strict-optional class C: a = None def __init__(self) -> None: @@ -2538,7 +2534,6 @@ if bool(): [out] [case testDontMarkUnreachableAfterInferenceUninhabited2] -# flags: --strict-optional from typing import TypeVar, Optional T = TypeVar('T') def f(x: Optional[T] = None) -> T: pass @@ -2609,7 +2604,7 @@ x = '' reveal_type(x) # N: Revealed type is "builtins.str" [case testLocalPartialTypesWithGlobalInitializedToNoneStrictOptional] -# flags: --local-partial-types --strict-optional +# flags: --local-partial-types x = None def f() -> None: @@ -2761,7 +2756,7 @@ class B(A): reveal_type(B.x) # N: Revealed type is "None" [case testLocalPartialTypesWithInheritance2] -# flags: --local-partial-types --strict-optional +# flags: --local-partial-types class A: x: str @@ -2769,7 +2764,7 @@ class B(A): x = None # E: Incompatible types in assignment (expression has type "None", base class "A" defined the type as "str") [case testLocalPartialTypesWithAnyBaseClass] -# flags: --local-partial-types --strict-optional +# flags: --local-partial-types from typing import Any A: Any @@ -2781,7 +2776,7 @@ class C(B): y = None [case testLocalPartialTypesInMultipleMroItems] -# flags: --local-partial-types --strict-optional +# flags: --local-partial-types from typing import Optional class A: @@ -3106,7 +3101,6 @@ class B(A): x = 2 # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "str") [case testInheritedAttributeStrictOptional] -# flags: --strict-optional class A: x: str @@ -3209,7 +3203,6 @@ x: Inv[int] reveal_type(f(x)) # N: Revealed type is "builtins.int" [case testOptionalTypeVarAgainstOptional] -# flags: --strict-optional from typing import Optional, TypeVar, Iterable, Iterator, List _T = TypeVar('_T') @@ -3256,7 +3249,6 @@ reveal_type(b) # N: Revealed type is "collections.defaultdict[builtins.int, buil [builtins fixtures/dict.pyi] [case testPartialDefaultDictListValueStrictOptional] -# flags: --strict-optional from collections import defaultdict a = defaultdict(list) a['x'].append(1) @@ -3333,7 +3325,6 @@ def g() -> None: pass reveal_type(f(g)) # N: Revealed type is "None" [case testInferCallableReturningNone2] -# flags: --strict-optional from typing import Callable, TypeVar T = TypeVar("T") @@ -3404,7 +3395,6 @@ def collection_from_dict_value(model: Type[T2]) -> None: [builtins fixtures/isinstancelist.pyi] [case testRegression11705_Strict] -# flags: --strict-optional # See: https://github.com/python/mypy/issues/11705 from typing import Dict, Optional, NamedTuple class C(NamedTuple): @@ -3454,7 +3444,6 @@ foo(("a", {"a": "b"}, "b")) [builtins fixtures/dict.pyi] [case testUseSupertypeAsInferenceContext] -# flags: --strict-optional from typing import List, Optional class B: diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 71030b5c9b97..bedba811d95b 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -165,7 +165,6 @@ main:1: error: Unrecognized option: skip_file = True main:1: error: Setting "strict" not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see "mypy -h" for the list of flags enabled in strict mode) [case testInlineErrorCodes] -# flags: --strict-optional # mypy: enable-error-code="ignore-without-code,truthy-bool" class Foo: pass @@ -175,7 +174,7 @@ if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool_ 42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) [case testInlineErrorCodesOverrideConfig] -# flags: --strict-optional --config-file tmp/mypy.ini +# flags: --config-file tmp/mypy.ini import foo import tests.bar import tests.baz @@ -243,7 +242,6 @@ class C: self.x = 1 [case testIgnoreErrorsWithUnsafeSuperCall_no_empty] -# flags: --strict-optional from m import C diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 3403e726d8b5..361d4db78752 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1801,7 +1801,6 @@ if issubclass(fm, Bar): [builtins fixtures/isinstance.pyi] [case testIssubclassWithMetaclassesStrictOptional] -# flags: --strict-optional class FooMetaclass(type): ... class BarMetaclass(type): ... class Foo(metaclass=FooMetaclass): ... @@ -1906,7 +1905,6 @@ def narrow_any_to_str_then_reassign_to_int() -> None: [builtins fixtures/isinstance.pyi] [case testNarrowTypeAfterInList] -# flags: --strict-optional from typing import List, Optional x: List[int] @@ -1924,7 +1922,6 @@ else: [out] [case testNarrowTypeAfterInListOfOptional] -# flags: --strict-optional from typing import List, Optional x: List[Optional[int]] @@ -1938,7 +1935,6 @@ else: [out] [case testNarrowTypeAfterInListNonOverlapping] -# flags: --strict-optional from typing import List, Optional x: List[str] @@ -1952,7 +1948,6 @@ else: [out] [case testNarrowTypeAfterInListNested] -# flags: --strict-optional from typing import List, Optional, Any x: Optional[int] @@ -1967,7 +1962,6 @@ if x in nested_any: [out] [case testNarrowTypeAfterInTuple] -# flags: --strict-optional from typing import Optional class A: pass class B(A): pass @@ -1982,7 +1976,6 @@ else: [out] [case testNarrowTypeAfterInNamedTuple] -# flags: --strict-optional from typing import NamedTuple, Optional class NT(NamedTuple): x: int @@ -1998,7 +1991,6 @@ else: [out] [case testNarrowTypeAfterInDict] -# flags: --strict-optional from typing import Dict, Optional x: Dict[str, int] y: Optional[str] @@ -2015,7 +2007,6 @@ else: [out] [case testNarrowTypeAfterInNoAnyOrObject] -# flags: --strict-optional from typing import Any, List, Optional x: List[Any] z: List[object] @@ -2035,7 +2026,6 @@ else: [out] [case testNarrowTypeAfterInUserDefined] -# flags: --strict-optional from typing import Container, Optional class C(Container[int]): @@ -2057,7 +2047,6 @@ else: [out] [case testNarrowTypeAfterInSet] -# flags: --strict-optional from typing import Optional, Set s: Set[str] @@ -2074,7 +2063,6 @@ else: [out] [case testNarrowTypeAfterInTypedDict] -# flags: --strict-optional from typing import Optional from mypy_extensions import TypedDict class TD(TypedDict): @@ -2150,7 +2138,6 @@ else: [builtins fixtures/isinstance.pyi] [case testIsInstanceInitialNoneCheckSkipsImpossibleCasesNoStrictOptional] -# flags: --strict-optional from typing import Optional, Union class A: pass @@ -2197,7 +2184,6 @@ def foo2(x: Optional[str]) -> None: [builtins fixtures/isinstance.pyi] [case testNoneCheckDoesNotNarrowWhenUsingTypeVars] -# flags: --strict-optional # Note: this test (and the following one) are testing checker.conditional_type_map: # if you set the 'prohibit_none_typevar_overlap' keyword argument to False when calling @@ -2249,7 +2235,6 @@ def bar(x: Union[List[str], List[int], None]) -> None: [builtins fixtures/isinstancelist.pyi] [case testNoneAndGenericTypesOverlapStrictOptional] -# flags: --strict-optional from typing import Union, Optional, List # This test is the same as the one above, except for strict-optional. diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index b3ee47aa6fdf..4beac047e278 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -350,7 +350,6 @@ class A: pass [builtins fixtures/dict.pyi] [case testInvalidTypeForKeywordVarArg] -# flags: --strict-optional from typing import Dict, Any, Optional class A: pass def f(**kwargs: 'A') -> None: pass diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test index 9809024afdbb..77acdafd3319 100644 --- a/test-data/unit/check-lists.test +++ b/test-data/unit/check-lists.test @@ -89,7 +89,6 @@ reveal_type(c) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] [case testComprehensionShadowBinder] -# flags: --strict-optional def foo(x: object) -> None: if isinstance(x, str): [reveal_type(x) for x in [1, 2, 3]] # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index abdbf733a679..f63f4026c4b6 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -659,7 +659,6 @@ def foo(b: Literal[T]) -> Tuple[T]: pass # E: Parameter 1 of Literal[...] is i -- [case testLiteralMultipleValues] -# flags: --strict-optional from typing_extensions import Literal a: Literal[1, 2, 3] b: Literal["a", "b", "c"] @@ -689,7 +688,6 @@ reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2], Literal[3]] [out] [case testLiteralNestedUsage] -# flags: --strict-optional from typing_extensions import Literal a: Literal[Literal[3], 4, Literal["foo"]] @@ -818,7 +816,6 @@ foo(c) # E: Argument 1 to "foo" has incompatible type "Literal[4, 'foo']"; expe [out] [case testLiteralCheckSubtypingStrictOptional] -# flags: --strict-optional from typing import Any, NoReturn from typing_extensions import Literal @@ -1807,7 +1804,6 @@ reveal_type(unify(f6)) # N: Revealed type is "None" [out] [case testLiteralMeetsWithStrictOptional] -# flags: --strict-optional from typing import TypeVar, Callable, Union from typing_extensions import Literal @@ -1834,7 +1830,6 @@ reveal_type(unify(func)) # N: Revealed type is "" -- [case testLiteralIntelligentIndexingTuples] -# flags: --strict-optional from typing import Tuple, NamedTuple, Optional, Final from typing_extensions import Literal @@ -2247,7 +2242,6 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [out] [case testLiteralFinalErasureInMutableDatastructures1] -# flags: --strict-optional from typing_extensions import Final var1: Final = [0, None] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 4992b6589bb3..bdf860cba89d 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -567,7 +567,6 @@ x = 1 x = 1 [case testAssignToFuncDefViaImport] -# flags: --strict-optional # Errors differ with the new analyzer. (Old analyzer gave error on the # input, which is maybe better, but no error about f, which seems diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index f06af0057f0f..22014d4c645c 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -747,7 +747,6 @@ def test3(switch: FlipFlopEnum) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitStrLiteral] -# flags: --strict-optional from typing_extensions import Literal, Final A_final: Final = "A" @@ -794,7 +793,6 @@ reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B' [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitEnumLiteral] -# flags: --strict-optional from typing import Union from typing_extensions import Literal, Final from enum import Enum @@ -879,7 +877,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityDisabledForCustomEqualityChain] -# flags: --strict-optional --strict-equality --warn-unreachable +# flags: --strict-equality --warn-unreachable from typing import Union from typing_extensions import Literal @@ -916,7 +914,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingUnreachableCases] -# flags: --strict-optional --strict-equality --warn-unreachable +# flags: --strict-equality --warn-unreachable from typing import Union from typing_extensions import Literal @@ -964,7 +962,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingUnreachableCases2] -# flags: --strict-optional --strict-equality --warn-unreachable +# flags: --strict-equality --warn-unreachable from typing import Union from typing_extensions import Literal @@ -1064,7 +1062,6 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanIdentityCheck] -# flags: --strict-optional from typing import Optional from typing_extensions import Literal @@ -1087,7 +1084,6 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanTruthiness] -# flags: --strict-optional from typing import Optional from typing_extensions import Literal @@ -1109,7 +1105,6 @@ reveal_type(opt_bool_val) # N: Revealed type is "Union[builtins.bool, None]" [builtins fixtures/primitives.pyi] [case testNarrowingBooleanBoolOp] -# flags: --strict-optional from typing import Optional from typing_extensions import Literal @@ -1161,7 +1156,6 @@ def f(d: Union[Foo, Bar]) -> None: [builtins fixtures/dict.pyi] [case testNarrowingUsingMetaclass] -# flags: --strict-optional from typing import Type class M(type): @@ -1181,7 +1175,6 @@ def f(t: Type[C]) -> None: reveal_type(t) # N: Revealed type is "Type[__main__.C]" [case testNarrowingUsingTypeVar] -# flags: --strict-optional from typing import Type, TypeVar class A: pass diff --git a/test-data/unit/check-native-int.test b/test-data/unit/check-native-int.test index 1129512694f4..30314eebcb31 100644 --- a/test-data/unit/check-native-int.test +++ b/test-data/unit/check-native-int.test @@ -69,7 +69,6 @@ reveal_type(join(a, n64)) # N: Revealed type is "Any" [builtins fixtures/dict.pyi] [case testNativeIntMeets] -# flags: --strict-optional from typing import TypeVar, Callable, Any from mypy_extensions import i32, i64 @@ -130,7 +129,6 @@ reveal_type(y) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] [case testNativeIntFloatConversion] -# flags: --strict-optional from typing import TypeVar, Callable from mypy_extensions import i32 diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index 0815d7af1933..3ed4c6d3d8e2 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -49,20 +49,17 @@ TstInstance().a = 'ab' [out] [case testNewSyntaxWithClassVars] -# flags: --strict-optional class CCC: a: str = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") [out] [case testNewSyntaxWithStrictOptional] -# flags: --strict-optional strict: int strict = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") strict2: int = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [out] [case testNewSyntaxWithStrictOptionalFunctions] -# flags: --strict-optional def f() -> None: x: int if int(): @@ -70,7 +67,6 @@ def f() -> None: [out] [case testNewSyntaxWithStrictOptionalClasses] -# flags: --strict-optional class C: def meth(self) -> None: x: int = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4851cc96e6da..f49a15ada85c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -3264,7 +3264,6 @@ f(x, B()) # E: Argument 1 to "f" has incompatible type "Union[A, B]"; expected [builtins fixtures/tuple.pyi] [case testOverloadInferUnionWithMixOfPositionalAndOptionalArgs] -# flags: --strict-optional from typing import overload, Union, Optional class A: ... @@ -3603,7 +3602,6 @@ reveal_type(g(b)) # N: Revealed type is "builtins.str" reveal_type(g(c)) # N: Revealed type is "builtins.str" [case testOverloadsAndNoneWithStrictOptional] -# flags: --strict-optional from typing import overload, Optional @overload @@ -3651,7 +3649,6 @@ reveal_type(mymap(f3, seq)) # N: Revealed type is "typing.Iterable[builtins.str [typing fixtures/typing-medium.pyi] [case testOverloadsNoneAndTypeVarsWithStrictOptional] -# flags: --strict-optional from typing import Callable, Iterable, TypeVar, overload, Optional T = TypeVar('T') @@ -3708,7 +3705,6 @@ def test_narrow_int() -> None: [typing fixtures/typing-medium.pyi] [case testOverloadsAndNoReturnNarrowTypeWithStrictOptional1] -# flags: --strict-optional from typing import overload, Union, NoReturn @overload @@ -3772,7 +3768,6 @@ def test_narrow_none() -> None: [typing fixtures/typing-medium.pyi] [case testOverloadsAndNoReturnNarrowTypeWithStrictOptional2] -# flags: --strict-optional from typing import overload, Union, TypeVar, NoReturn, Optional T = TypeVar('T') @@ -3836,7 +3831,6 @@ def test_narrow_none_v2() -> None: [typing fixtures/typing-medium.pyi] [case testOverloadsAndNoReturnNarrowTypeWithStrictOptional3] -# flags: --strict-optional from typing import overload, TypeVar, NoReturn, Optional @overload @@ -4648,7 +4642,6 @@ def none_second(x: int) -> int: return x [case testOverloadsWithNoneComingSecondIsOkInStrictOptional] -# flags: --strict-optional from typing import overload, Optional @overload @@ -4672,8 +4665,8 @@ def none_loose_impl(x: int) -> int: ... def none_loose_impl(x: int) -> int: return x [out] -main:22: error: Overloaded function implementation does not accept all possible arguments of signature 1 -main:22: error: Overloaded function implementation cannot produce return type of signature 1 +main:21: error: Overloaded function implementation does not accept all possible arguments of signature 1 +main:21: error: Overloaded function implementation cannot produce return type of signature 1 [case testTooManyUnionsException] from typing import overload, Union diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 3d05faed74f1..114fe1f8438a 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1307,7 +1307,6 @@ reveal_type(bar(C(fn=foo, x=1))) # N: Revealed type is "__main__.C[[x: builtins [builtins fixtures/paramspec.pyi] [case testParamSpecClassConstructor] -# flags: --strict-optional from typing import ParamSpec, Callable P = ParamSpec("P") diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 88a541c28ac2..913584224764 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1199,7 +1199,6 @@ class C: [builtins fixtures/bool.pyi] [case testAttrsOptionalConverter] -# flags: --strict-optional import attr from attr.converters import optional from typing import Optional @@ -1219,7 +1218,6 @@ A(None, None) [builtins fixtures/plugin_attrs.pyi] [case testAttrsOptionalConverterNewPackage] -# flags: --strict-optional import attrs from attrs.converters import optional from typing import Optional diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 5d5ba54304a3..dba01be50fee 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -324,7 +324,6 @@ var: MyHashable = C() # E: Incompatible types in assignment (expression has typ # N: __my_hash__: expected "Callable[[], int]", got "None" [case testNoneDisablesProtocolSubclassingWithStrictOptional] -# flags: --strict-optional from typing import Protocol class MyHashable(Protocol): @@ -336,7 +335,6 @@ class C(MyHashable): (expression has type "None", base class "MyHashable" defined the type as "Callable[[MyHashable], int]") [case testProtocolsWithNoneAndStrictOptional] -# flags: --strict-optional from typing import Protocol class P(Protocol): x = 0 # type: int @@ -348,12 +346,12 @@ x: P = C() # Error! def f(x: P) -> None: pass f(C()) # Error! [out] -main:9: error: Incompatible types in assignment (expression has type "C", variable has type "P") -main:9: note: Following member(s) of "C" have conflicts: -main:9: note: x: expected "int", got "None" -main:11: error: Argument 1 to "f" has incompatible type "C"; expected "P" -main:11: note: Following member(s) of "C" have conflicts: -main:11: note: x: expected "int", got "None" +main:8: error: Incompatible types in assignment (expression has type "C", variable has type "P") +main:8: note: Following member(s) of "C" have conflicts: +main:8: note: x: expected "int", got "None" +main:10: error: Argument 1 to "f" has incompatible type "C"; expected "P" +main:10: note: Following member(s) of "C" have conflicts: +main:10: note: x: expected "int", got "None" -- Semanal errors in protocol types -- -------------------------------- @@ -2412,7 +2410,6 @@ x: P = None [out] [case testNoneSubtypeOfEmptyProtocolStrict] -# flags: --strict-optional from typing import Protocol class P(Protocol): pass @@ -2959,7 +2956,6 @@ class MyClass: [case testPartialAttributeNoneTypeStrictOptional] -# flags: --strict-optional from typing import Optional, Protocol, runtime_checkable @runtime_checkable @@ -3080,7 +3076,6 @@ def round(number: SupportsRound[_T], ndigits: int) -> _T: ... round(C(), 1) [case testEmptyBodyImplicitlyAbstractProtocol] -# flags: --strict-optional from typing import Protocol, overload, Union class P1(Protocol): @@ -3127,7 +3122,6 @@ C3() [builtins fixtures/classmethod.pyi] [case testEmptyBodyImplicitlyAbstractProtocolProperty] -# flags: --strict-optional from typing import Protocol class P1(Protocol): @@ -3222,7 +3216,6 @@ D() # E: Cannot instantiate abstract class "D" with abstract attribute "meth" [builtins fixtures/exception.pyi] [case testEmptyBodyNoneCompatibleProtocol] -# flags: --strict-optional from abc import abstractmethod from typing import Any, Optional, Protocol, Union, overload from typing_extensions import TypeAlias diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 6416fa02bbce..c07a90b49e63 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1140,7 +1140,6 @@ match m: reveal_type(a) [case testMatchRedefiningPatternGuard] -# flags: --strict-optional m: str match m: @@ -1382,7 +1381,6 @@ def f(x: int | str) -> int: # E: Missing return statement [builtins fixtures/isinstance.pyi] [case testMatchNarrowDownUnionPartially] -# flags: --strict-optional def f(x: int | str) -> None: match x: @@ -1493,7 +1491,6 @@ def f(x: A) -> None: reveal_type(y) # N: Revealed type is "Union[__main__., __main__.]" [case testMatchWithBreakAndContinue] -# flags: --strict-optional def f(x: int | str | None) -> None: i = int() while i: @@ -1626,7 +1623,6 @@ def func(e: Union[str, tuple[str]]) -> None: [builtins fixtures/tuple.pyi] [case testMatchTupleOptionalNoCrash] -# flags: --strict-optional foo: tuple[int] | None match foo: case x,: @@ -1865,7 +1861,6 @@ def f() -> None: reveal_type(y.a) # N: Revealed type is "builtins.int" [case testNarrowedVariableInNestedModifiedInMatch] -# flags: --strict-optional from typing import Optional def match_stmt_error1(x: Optional[str]) -> None: diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 423daaf5ae8f..d83f29f2186a 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -223,7 +223,7 @@ h(arg=0) # E: Unexpected keyword argument "arg" for "h" i(arg=0) # E: Unexpected keyword argument "arg" [case testWalrus] -# flags: --strict-optional --python-version 3.8 +# flags: --python-version 3.8 from typing import NamedTuple, Optional, List from typing_extensions import Final @@ -427,7 +427,7 @@ else: [builtins fixtures/list.pyi] [case testWalrusConditionalTypeCheck] -# flags: --strict-optional --python-version 3.8 +# flags: --python-version 3.8 from typing import Optional maybe_str: Optional[str] @@ -729,7 +729,6 @@ def f1() -> None: [builtins fixtures/dict.pyi] [case testNarrowOnSelfInGeneric] -# flags: --strict-optional from typing import Generic, TypeVar, Optional T = TypeVar("T", int, str) @@ -741,8 +740,8 @@ class C(Generic[T]): reveal_type(y) return None [out] -main:10: note: Revealed type is "builtins.int" -main:10: note: Revealed type is "builtins.str" +main:9: note: Revealed type is "builtins.int" +main:9: note: Revealed type is "builtins.str" [case testTypeGuardWithPositionalOnlyArg] # flags: --python-version 3.8 @@ -778,7 +777,6 @@ class C: [builtins fixtures/list.pyi] [case testNarrowedVariableInNestedModifiedInWalrus] -# flags: --strict-optional from typing import Optional def walrus_with_nested_error(x: Optional[str]) -> None: diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index dc1ae448c0d1..84593933a2de 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -422,7 +422,6 @@ reveal_type(d) # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] [case testBasicRecursiveNamedTuple] -# flags: --strict-optional from typing import NamedTuple, Optional NT = NamedTuple("NT", [("x", Optional[NT]), ("y", int)]) @@ -457,7 +456,6 @@ reveal_type(f(tnt, nt)) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] [case testBasicRecursiveNamedTupleClass] -# flags: --strict-optional from typing import NamedTuple, Optional class NT(NamedTuple): @@ -684,7 +682,6 @@ itd2 = TD(x=0, y=TD(x=0, y=TD(x=0, y=None))) [typing fixtures/typing-typeddict.pyi] [case testRecursiveTypedDictMethods] -# flags: --strict-optional from typing import TypedDict class TD(TypedDict, total=False): @@ -787,7 +784,6 @@ reveal_type(std) # N: Revealed type is "TypedDict('__main__.STD', {'val': built [typing fixtures/typing-typeddict.pyi] [case testRecursiveClassLevelAlias] -# flags: --strict-optional from typing import Union, Sequence class A: diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index e5d1d6b170f9..81da94c0591c 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -740,7 +740,6 @@ main:4: note: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fal -- [case testSerializeOptionalType] -# flags: --strict-optional import a [file a.py] import b diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 3bfcf6a9afea..42f22e89d6b7 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -305,7 +305,6 @@ reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/bool.pyi] [case testNoneAliasStrict] -# flags: --strict-optional from typing import Optional, Union void = type(None) x: int diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 983fa8c17aec..7de8e6416f35 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -595,7 +595,6 @@ reveal_type(f(g)) # N: Revealed type is "TypedDict({'x': builtins.int, 'y': bui [builtins fixtures/dict.pyi] [case testMeetOfTypedDictsWithIncompatibleCommonKeysIsUninhabited] -# flags: --strict-optional from mypy_extensions import TypedDict from typing import TypeVar, Callable XYa = TypedDict('XYa', {'x': int, 'y': int}) @@ -619,7 +618,6 @@ reveal_type(f(g)) # N: Revealed type is "TypedDict({'x': builtins.int, 'z': bui # TODO: It would be more accurate for the meet to be TypedDict instead. [case testMeetOfTypedDictWithCompatibleMappingIsUninhabitedForNow] -# flags: --strict-optional from mypy_extensions import TypedDict from typing import TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) @@ -631,7 +629,6 @@ reveal_type(f(g)) # N: Revealed type is "" [builtins fixtures/dict.pyi] [case testMeetOfTypedDictWithIncompatibleMappingIsUninhabited] -# flags: --strict-optional from mypy_extensions import TypedDict from typing import TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) @@ -643,7 +640,6 @@ reveal_type(f(g)) # N: Revealed type is "" [builtins fixtures/dict.pyi] [case testMeetOfTypedDictWithCompatibleMappingSuperclassIsUninhabitedForNow] -# flags: --strict-optional from mypy_extensions import TypedDict from typing import TypeVar, Callable, Iterable X = TypedDict('X', {'x': int}) @@ -677,7 +673,6 @@ reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'?: builtins.int, 'y': bu [builtins fixtures/dict.pyi] [case testMeetOfTypedDictsWithIncompatibleNonTotalAndTotal] -# flags: --strict-optional from mypy_extensions import TypedDict from typing import TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}, total=False) @@ -972,7 +967,6 @@ if int(): -- Other TypedDict methods [case testTypedDictGetMethod] -# flags: --strict-optional from mypy_extensions import TypedDict class A: pass D = TypedDict('D', {'x': int, 'y': str}) @@ -986,7 +980,6 @@ reveal_type(d.get('y', None)) # N: Revealed type is "Union[builtins.str, None]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetMethodTypeContext] -# flags: --strict-optional from typing import List from mypy_extensions import TypedDict class A: pass @@ -1044,7 +1037,6 @@ p.get('x', 1 + 'y') # E: Unsupported operand types for + ("int" and "str") [typing fixtures/typing-typeddict.pyi] [case testTypedDictChainedGetWithEmptyDictDefault] -# flags: --strict-optional from mypy_extensions import TypedDict C = TypedDict('C', {'a': int}) D = TypedDict('D', {'x': C, 'y': str}) diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index a307e4c8b6a0..b3b168e5c7c6 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -248,7 +248,6 @@ def main1(a: object) -> None: [builtins fixtures/tuple.pyi] [case testTypeGuardOverload] -# flags: --strict-optional from typing import overload, Any, Callable, Iterable, Iterator, List, Optional, TypeVar from typing_extensions import TypeGuard diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 28d83aa54ccc..f6fd27e59e4d 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -929,7 +929,6 @@ reveal_type(z) # N: Revealed type is "Union[builtins.int, __main__.A, builtins.s [out] [case testUnpackUnionNoCrashOnPartialNone] -# flags: --strict-optional from typing import Dict, Tuple, List, Any a: Any @@ -944,7 +943,6 @@ if x: [out] [case testUnpackUnionNoCrashOnPartialNone2] -# flags: --strict-optional from typing import Dict, Tuple, List, Any a: Any @@ -960,7 +958,6 @@ if x: [out] [case testUnpackUnionNoCrashOnPartialNoneBinder] -# flags: --strict-optional from typing import Dict, Tuple, List, Any x: object @@ -975,7 +972,6 @@ if x: [out] [case testUnpackUnionNoCrashOnPartialList] -# flags: --strict-optional from typing import Dict, Tuple, List, Any a: Any @@ -1081,7 +1077,6 @@ def bar(a: T4, b: T4) -> T4: # test multi-level alias [builtins fixtures/ops.pyi] [case testJoinUnionWithUnionAndAny] -# flags: --strict-optional from typing import TypeVar, Union, Any T = TypeVar("T") def f(x: T, y: T) -> T: diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 82ff35f53702..76ecd9f51e35 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -615,7 +615,6 @@ reveal_type(x) # N: Revealed type is "__main__.B" [typing fixtures/typing-medium.pyi] [case testUnreachableWhenSuperclassIsAny] -# flags: --strict-optional from typing import Any # This can happen if we're importing a class from a missing module diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 4da9e0e5033e..6e118597551f 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -630,7 +630,6 @@ if int(): [builtins fixtures/list.pyi] [case testCallerTupleVarArgsAndGenericCalleeVarArg] -# flags: --strict-optional from typing import TypeVar T = TypeVar('T') diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 10c7968be475..90f40777d6b7 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -207,7 +207,7 @@ def f() -> Any: return g() [out] [case testOKReturnAnyIfProperSubtype] -# flags: --warn-return-any --strict-optional +# flags: --warn-return-any from typing import Any, Optional class Test(object): diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index b43a2ace5eed..c3295b79e4ed 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -612,7 +612,6 @@ class A: -> , m.A.f, m.C [case testPartialNoneTypeAttributeCrash2] -# flags: --strict-optional class C: pass class A: diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test index 47de16b8d765..02373091ad54 100644 --- a/test-data/unit/fine-grained-suggest.test +++ b/test-data/unit/fine-grained-suggest.test @@ -62,7 +62,6 @@ foo('3', '4') == [case testSuggestInferFunc1] -# flags: --strict-optional # suggest: foo.foo [file foo.py] def foo(arg, lol=None): @@ -85,7 +84,6 @@ def untyped(x) -> None: == [case testSuggestInferFunc2] -# flags: --strict-optional # suggest: foo.foo [file foo.py] def foo(arg): @@ -222,7 +220,6 @@ Foo('lol') == [case testSuggestInferMethod1] -# flags: --strict-optional # suggest: --no-any foo.Foo.foo [file foo.py] class Foo: @@ -248,7 +245,6 @@ def bar() -> None: == [case testSuggestInferMethod2] -# flags: --strict-optional # suggest: foo.Foo.foo [file foo.py] class Foo: @@ -275,7 +271,6 @@ def bar() -> None: == [case testSuggestInferMethod3] -# flags: --strict-optional # suggest2: foo.Foo.foo [file foo.py] class Foo: @@ -372,7 +367,6 @@ def has_nested(x): == [case testSuggestInferFunctionUnreachable] -# flags: --strict-optional # suggest: foo.foo [file foo.py] import sys @@ -390,7 +384,6 @@ foo('test') == [case testSuggestInferMethodStep2] -# flags: --strict-optional # suggest2: foo.Foo.foo [file foo.py] class Foo: @@ -417,7 +410,6 @@ def bar() -> None: (Union[str, int, None], Optional[int]) -> Union[int, str] [case testSuggestInferNestedMethod] -# flags: --strict-optional # suggest: foo.Foo.Bar.baz [file foo.py] class Foo: @@ -435,7 +427,6 @@ def bar() -> None: == [case testSuggestCallable] -# flags: --strict-optional # suggest: foo.foo # suggest: foo.bar # suggest: --flex-any=0.9 foo.bar @@ -483,7 +474,6 @@ No guesses that match criteria! == [case testSuggestNewSemanal] -# flags: --strict-optional # suggest: foo.Foo.foo # suggest: foo.foo [file foo.py] @@ -521,7 +511,6 @@ def baz() -> None: == [case testSuggestInferFuncDecorator1] -# flags: --strict-optional # suggest: foo.foo [file foo.py] from typing import TypeVar @@ -543,7 +532,6 @@ def bar() -> None: == [case testSuggestInferFuncDecorator2] -# flags: --strict-optional # suggest: foo.foo [file foo.py] from typing import TypeVar, Callable, Any @@ -565,7 +553,6 @@ def bar() -> None: == [case testSuggestInferFuncDecorator3] -# flags: --strict-optional # suggest: foo.foo [file foo.py] from typing import TypeVar, Callable, Any @@ -589,7 +576,6 @@ def bar() -> None: == [case testSuggestInferFuncDecorator4] -# flags: --strict-optional # suggest: foo.foo [file dec.py] from typing import TypeVar, Callable, Any @@ -616,7 +602,6 @@ def bar() -> None: == [case testSuggestFlexAny1] -# flags: --strict-optional # suggest: --flex-any=0.4 m.foo # suggest: --flex-any=0.7 m.foo # suggest: --flex-any=0.4 m.bar @@ -661,7 +646,6 @@ No guesses that match criteria! [case testSuggestFlexAny2] -# flags: --strict-optional # suggest: --flex-any=0.5 m.baz # suggest: --flex-any=0.0 m.baz # suggest: --flex-any=0.5 m.F.foo @@ -693,7 +677,6 @@ No guesses that match criteria! == [case testSuggestClassMethod] -# flags: --strict-optional # suggest: foo.F.bar # suggest: foo.F.baz # suggest: foo.F.eggs diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 11a8f03590f7..66c5ee46db2f 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -2083,7 +2083,6 @@ a.py:5: error: "list" expects 1 type argument, but 2 given == [case testPreviousErrorInOverloadedFunction] -# flags: --strict-optional import a [file a.py] from typing import overload @@ -3494,7 +3493,6 @@ def foo() -> None: b.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testNamedTupleUpdateNonRecursiveToRecursiveFine] -# flags: --strict-optional import c [file a.py] from b import M @@ -3537,7 +3535,6 @@ c.py:5: error: Incompatible types in assignment (expression has type "Optional[N c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" [case testTupleTypeUpdateNonRecursiveToRecursiveFine] -# flags: --strict-optional import c [file a.py] from b import M @@ -3570,7 +3567,6 @@ c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypeAliasUpdateNonRecursiveToRecursiveFine] -# flags: --strict-optional import c [file a.py] from b import M @@ -4699,7 +4695,6 @@ class B: main:7: error: Argument 1 to "g" of "B" has incompatible type "Optional[int]"; expected "str" [case testStrictOptionalModule] -# flags: --strict-optional import a a.y = a.x [file a.py] @@ -4712,10 +4707,9 @@ x: Optional[int] y: int [out] == -main:3: error: Incompatible types in assignment (expression has type "Optional[int]", variable has type "int") +main:2: error: Incompatible types in assignment (expression has type "Optional[int]", variable has type "int") [case testStrictOptionalFunction] -# flags: --strict-optional import a from typing import Optional def f() -> None: @@ -4731,10 +4725,9 @@ def g(x: int) -> None: pass [out] == -main:6: error: Argument 1 to "g" has incompatible type "Optional[int]"; expected "int" +main:5: error: Argument 1 to "g" has incompatible type "Optional[int]"; expected "int" [case testStrictOptionalMethod] -# flags: --strict-optional import a from typing import Optional class C: @@ -4753,7 +4746,7 @@ class B: pass [out] == -main:7: error: Argument 1 to "g" of "B" has incompatible type "Optional[int]"; expected "int" +main:6: error: Argument 1 to "g" of "B" has incompatible type "Optional[int]"; expected "int" [case testPerFileStrictOptionalModule] import a @@ -7953,7 +7946,7 @@ class Foo(a.I): == [case testImplicitOptionalRefresh1] -# flags: --strict-optional --implicit-optional +# flags: --implicit-optional from x import f def foo(x: int = None) -> None: f() @@ -9793,7 +9786,6 @@ class ExampleClass(Generic[T]): [out] == [case testStrictNoneAttribute] -# flags: --strict-optional from typing import Generic, TypeVar T = TypeVar('T', int, str) @@ -10046,7 +10038,6 @@ class C(B): ... main.py:4: note: Revealed type is "def () -> builtins.str" [case testAbstractBodyTurnsEmpty] -# flags: --strict-optional from b import Base class Sub(Base): @@ -10066,10 +10057,9 @@ class Base: def meth(self) -> int: ... [out] == -main:6: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe +main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe [case testAbstractBodyTurnsEmptyProtocol] -# flags: --strict-optional from b import Base class Sub(Base): @@ -10086,7 +10076,7 @@ class Base(Protocol): def meth(self) -> int: ... [out] == -main:6: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe +main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe [case testPrettyMessageSorting] # flags: --pretty diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 289005b36d9a..754cb21c3ff8 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1649,7 +1649,6 @@ foo(list((list(""), ""))) [out] [case testNarrowTypeForDictKeys] -# flags: --strict-optional from typing import Dict, KeysView, Optional d: Dict[str, int] @@ -1667,10 +1666,10 @@ else: reveal_type(k) [out] -_testNarrowTypeForDictKeys.py:7: note: Revealed type is "builtins.str" -_testNarrowTypeForDictKeys.py:9: note: Revealed type is "Union[builtins.str, None]" -_testNarrowTypeForDictKeys.py:14: note: Revealed type is "builtins.str" -_testNarrowTypeForDictKeys.py:16: note: Revealed type is "Union[builtins.str, None]" +_testNarrowTypeForDictKeys.py:6: note: Revealed type is "builtins.str" +_testNarrowTypeForDictKeys.py:8: note: Revealed type is "Union[builtins.str, None]" +_testNarrowTypeForDictKeys.py:13: note: Revealed type is "builtins.str" +_testNarrowTypeForDictKeys.py:15: note: Revealed type is "Union[builtins.str, None]" [case testTypeAliasWithNewStyleUnion] # flags: --python-version 3.10 From b6b6624655826985f75dfd970e2c29f7690ce323 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Mon, 17 Jul 2023 15:27:52 -0400 Subject: [PATCH 0150/1617] tests: skip-path-normalization should be a testcase option (#15660) The "Skip path normalization" option applies to all [out]s of a test case, so it's more correct for it to be a "case" option rather than an "out" option. This also simplifies the parsing of "out" sections' args. --- mypy/test/data.py | 13 +++++++------ test-data/unit/check-literal.test | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 66dafaff775a..de0267daf918 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -65,7 +65,6 @@ def parse_test_case(case: DataDrivenTestCase) -> None: join = posixpath.join out_section_missing = case.suite.required_out_section - normalize_output = True files: list[tuple[str, str]] = [] # path and contents output_files: list[tuple[str, str | Pattern[str]]] = [] # output path and contents @@ -156,8 +155,6 @@ def _item_fail(msg: str) -> NoReturn: version_check = True for arg in args: - if arg == "skip-path-normalization": - normalize_output = False if arg.startswith("version"): compare_op = arg[7:9] if compare_op not in {">=", "=="}: @@ -185,7 +182,7 @@ def _item_fail(msg: str) -> NoReturn: version_check = sys.version_info[: len(version)] == version if version_check: tmp_output = [expand_variables(line) for line in item.data] - if os.path.sep == "\\" and normalize_output: + if os.path.sep == "\\" and case.normalize_output: tmp_output = [fix_win_path(line) for line in tmp_output] if item.id == "out" or item.id == "out1": output = tmp_output @@ -239,7 +236,6 @@ def _item_fail(msg: str) -> NoReturn: case.expected_rechecked_modules = rechecked_modules case.deleted_paths = deleted_paths case.triggered = triggered or [] - case.normalize_output = normalize_output case.expected_fine_grained_targets = targets case.test_modules = test_modules @@ -269,7 +265,7 @@ class DataDrivenTestCase(pytest.Item): # Whether or not we should normalize the output to standardize things like # forward vs backward slashes in file paths for Windows vs Linux. - normalize_output = True + normalize_output: bool # Extra attributes used by some tests. last_line: int @@ -281,10 +277,12 @@ def __init__( self, parent: DataSuiteCollector, suite: DataSuite, + *, file: str, name: str, writescache: bool, only_when: str, + normalize_output: bool, platform: str | None, skip: bool, xfail: bool, @@ -296,6 +294,7 @@ def __init__( self.file = file self.writescache = writescache self.only_when = only_when + self.normalize_output = normalize_output if (platform == "windows" and sys.platform != "win32") or ( platform == "posix" and sys.platform == "win32" ): @@ -651,6 +650,7 @@ def pytest_pycollect_makeitem(collector: Any, name: str, obj: object) -> Any | N r"(?P[a-zA-Z_0-9]+)" r"(?P-writescache)?" r"(?P-only_when_cache|-only_when_nocache)?" + r"(?P-skip_path_normalization)?" r"(-(?Pposix|windows))?" r"(?P-skip)?" r"(?P-xfail)?" @@ -694,6 +694,7 @@ def split_test_cases( platform=m.group("platform"), skip=bool(m.group("skip")), xfail=bool(m.group("xfail")), + normalize_output=not m.group("skip_path_normalization"), data=data, line=line_no, ) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index f63f4026c4b6..4498b2ddc9cf 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -278,7 +278,7 @@ reveal_type(c_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Liter [builtins fixtures/tuple.pyi] [out] -[case testLiteralUnicodeWeirdCharacters] +[case testLiteralUnicodeWeirdCharacters-skip_path_normalization] from typing import Any from typing_extensions import Literal @@ -334,7 +334,7 @@ a1 = b3 a1 = c3 # E: Incompatible types in assignment (expression has type "Literal['¬b ∧ λ(p)']", variable has type "Literal['\x00¬b ∧ λ(p)']") [builtins fixtures/tuple.pyi] -[out skip-path-normalization] +[out] [case testLiteralRenamingImportWorks] from typing_extensions import Literal as Foo @@ -478,7 +478,7 @@ reveal_type(f5) # N: Revealed type is "def (x: Literal['foo']) -> Literal['foo' [builtins fixtures/tuple.pyi] [out] -[case testLiteralBasicStrUsageSlashes] +[case testLiteralBasicStrUsageSlashes-skip_path_normalization] from typing_extensions import Literal a: Literal[r"foo\nbar"] @@ -487,7 +487,7 @@ b: Literal["foo\nbar"] reveal_type(a) reveal_type(b) [builtins fixtures/tuple.pyi] -[out skip-path-normalization] +[out] main:6: note: Revealed type is "Literal['foo\\nbar']" main:7: note: Revealed type is "Literal['foo\nbar']" From 89ad125fa5a31a7c82f267e957d3c94da6b52f61 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 19 Jul 2023 01:07:53 +0100 Subject: [PATCH 0151/1617] Update commit hashes following typeshed sync (#15690) Followup to #15681 --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 3f870d574d38..36967f86262e 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "9f4c0d8af", # LiteralString reverts - "56f434336", # sum reverts - "71c4269df", # ctypes reverts - "186fbb18e", # ParamSpec for functools.wraps + "2f6b6e66c", # LiteralString reverts + "120af30e7", # sum reverts + "1866d28f1", # ctypes reverts + "3240da455", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: From 88c0c644c78e2f31ed25a523ad1f74727f2d647f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 20 Jul 2023 21:06:59 +0300 Subject: [PATCH 0152/1617] Update LICENSE with copyright year (#15727) --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 991496cb4878..55d01ee19ad8 100644 --- a/LICENSE +++ b/LICENSE @@ -4,8 +4,8 @@ Mypy (and mypyc) are licensed under the terms of the MIT license, reproduced bel The MIT License -Copyright (c) 2012-2022 Jukka Lehtosalo and contributors -Copyright (c) 2015-2022 Dropbox, Inc. +Copyright (c) 2012-2023 Jukka Lehtosalo and contributors +Copyright (c) 2015-2023 Dropbox, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From 383137baaf36876dcf935c2f5be053b6419097d0 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 20 Jul 2023 22:38:20 +0300 Subject: [PATCH 0153/1617] Remove more unused scripts (#15728) --- misc/async_matrix.py | 149 ----------------------- misc/fix_annotate.py | 218 ---------------------------------- misc/remove-eol-whitespace.sh | 8 -- 3 files changed, 375 deletions(-) delete mode 100644 misc/async_matrix.py delete mode 100644 misc/fix_annotate.py delete mode 100644 misc/remove-eol-whitespace.sh diff --git a/misc/async_matrix.py b/misc/async_matrix.py deleted file mode 100644 index d4612dd81799..000000000000 --- a/misc/async_matrix.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 -"""Test various combinations of generators/coroutines. - -This was used to cross-check the errors in the test case -testFullCoroutineMatrix in test-data/unit/check-async-await.test. -""" - -from __future__ import annotations - -import sys -from types import coroutine -from typing import Any, Awaitable, Generator, Iterator - -# The various things you might try to use in `await` or `yield from`. - - -def plain_generator() -> Generator[str, None, int]: - yield "a" - return 1 - - -async def plain_coroutine() -> int: - return 1 - - -@coroutine -def decorated_generator() -> Generator[str, None, int]: - yield "a" - return 1 - - -@coroutine -async def decorated_coroutine() -> int: - return 1 - - -class It(Iterator[str]): - stop = False - - def __iter__(self) -> It: - return self - - def __next__(self) -> str: - if self.stop: - raise StopIteration("end") - else: - self.stop = True - return "a" - - -def other_iterator() -> It: - return It() - - -class Aw(Awaitable[int]): - def __await__(self) -> Generator[str, Any, int]: - yield "a" - return 1 - - -def other_coroutine() -> Aw: - return Aw() - - -# The various contexts in which `await` or `yield from` might occur. - - -def plain_host_generator(func) -> Generator[str, None, None]: - yield "a" - x = 0 - f = func() - try: - x = yield from f # noqa: F841 - finally: - try: - f.close() - except AttributeError: - pass - - -async def plain_host_coroutine(func) -> None: - x = 0 - x = await func() # noqa: F841 - - -@coroutine -def decorated_host_generator(func) -> Generator[str, None, None]: - yield "a" - x = 0 - f = func() - try: - x = yield from f # noqa: F841 - finally: - try: - f.close() - except AttributeError: - pass - - -@coroutine -async def decorated_host_coroutine(func) -> None: - x = 0 - x = await func() # noqa: F841 - - -# Main driver. - - -def main() -> None: - verbose = "-v" in sys.argv - for host in [ - plain_host_generator, - plain_host_coroutine, - decorated_host_generator, - decorated_host_coroutine, - ]: - print() - print("==== Host:", host.__name__) - for func in [ - plain_generator, - plain_coroutine, - decorated_generator, - decorated_coroutine, - other_iterator, - other_coroutine, - ]: - print(" ---- Func:", func.__name__) - try: - f = host(func) - for i in range(10): - try: - x = f.send(None) - if verbose: - print(" yield:", x) - except StopIteration as e: - if verbose: - print(" stop:", e.value) - break - else: - if verbose: - print(" ???? still going") - except Exception as e: - print(" error:", repr(e)) - - -# Run main(). - -if __name__ == "__main__": - main() diff --git a/misc/fix_annotate.py b/misc/fix_annotate.py deleted file mode 100644 index fc8ac27466d5..000000000000 --- a/misc/fix_annotate.py +++ /dev/null @@ -1,218 +0,0 @@ -"""Fixer for lib2to3 that inserts mypy annotations into all methods. - -The simplest way to run this is to copy it into lib2to3's "fixes" -subdirectory and then run "2to3 -f annotate" over your files. - -The fixer transforms e.g. - - def foo(self, bar, baz=12): - return bar + baz - -into - - def foo(self, bar, baz=12): - # type: (Any, int) -> Any - return bar + baz - -It does not do type inference but it recognizes some basic default -argument values such as numbers and strings (and assumes their type -implies the argument type). - -It also uses some basic heuristics to decide whether to ignore the -first argument: - - - always if it's named 'self' - - if there's a @classmethod decorator - -Finally, it knows that __init__() is supposed to return None. -""" - -from __future__ import annotations - -import os -import re -from lib2to3.fixer_base import BaseFix -from lib2to3.fixer_util import syms, token, touch_import -from lib2to3.patcomp import compile_pattern -from lib2to3.pytree import Leaf, Node - - -class FixAnnotate(BaseFix): - # This fixer is compatible with the bottom matcher. - BM_compatible = True - - # This fixer shouldn't run by default. - explicit = True - - # The pattern to match. - PATTERN = """ - funcdef< 'def' name=any parameters< '(' [args=any] ')' > ':' suite=any+ > - """ - - counter = None if not os.getenv("MAXFIXES") else int(os.getenv("MAXFIXES")) - - def transform(self, node, results): - if FixAnnotate.counter is not None: - if FixAnnotate.counter <= 0: - return - suite = results["suite"] - children = suite[0].children - - # NOTE: I've reverse-engineered the structure of the parse tree. - # It's always a list of nodes, the first of which contains the - # entire suite. Its children seem to be: - # - # [0] NEWLINE - # [1] INDENT - # [2...n-2] statements (the first may be a docstring) - # [n-1] DEDENT - # - # Comments before the suite are part of the INDENT's prefix. - # - # "Compact" functions (e.g. "def foo(x, y): return max(x, y)") - # have a different structure that isn't matched by PATTERN. - # - # print('-'*60) - # print(node) - # for i, ch in enumerate(children): - # print(i, repr(ch.prefix), repr(ch)) - # - # Check if there's already an annotation. - for ch in children: - if ch.prefix.lstrip().startswith("# type:"): - return # There's already a # type: comment here; don't change anything. - - # Compute the annotation - annot = self.make_annotation(node, results) - - # Insert '# type: {annot}' comment. - # For reference, see lib2to3/fixes/fix_tuple_params.py in stdlib. - if len(children) >= 2 and children[1].type == token.INDENT: - children[1].prefix = "{}# type: {}\n{}".format( - children[1].value, annot, children[1].prefix - ) - children[1].changed() - if FixAnnotate.counter is not None: - FixAnnotate.counter -= 1 - - # Also add 'from typing import Any' at the top. - if "Any" in annot: - touch_import("typing", "Any", node) - - def make_annotation(self, node, results): - name = results["name"] - assert isinstance(name, Leaf), repr(name) - assert name.type == token.NAME, repr(name) - decorators = self.get_decorators(node) - is_method = self.is_method(node) - if name.value == "__init__" or not self.has_return_exprs(node): - restype = "None" - else: - restype = "Any" - args = results.get("args") - argtypes = [] - if isinstance(args, Node): - children = args.children - elif isinstance(args, Leaf): - children = [args] - else: - children = [] - # Interpret children according to the following grammar: - # (('*'|'**')? NAME ['=' expr] ','?)* - stars = inferred_type = "" - in_default = False - at_start = True - for child in children: - if isinstance(child, Leaf): - if child.value in ("*", "**"): - stars += child.value - elif child.type == token.NAME and not in_default: - if not is_method or not at_start or "staticmethod" in decorators: - inferred_type = "Any" - else: - # Always skip the first argument if it's named 'self'. - # Always skip the first argument of a class method. - if child.value == "self" or "classmethod" in decorators: - pass - else: - inferred_type = "Any" - elif child.value == "=": - in_default = True - elif in_default and child.value != ",": - if child.type == token.NUMBER: - if re.match(r"\d+[lL]?$", child.value): - inferred_type = "int" - else: - inferred_type = "float" # TODO: complex? - elif child.type == token.STRING: - if child.value.startswith(("u", "U")): - inferred_type = "unicode" - else: - inferred_type = "str" - elif child.type == token.NAME and child.value in ("True", "False"): - inferred_type = "bool" - elif child.value == ",": - if inferred_type: - argtypes.append(stars + inferred_type) - # Reset - stars = inferred_type = "" - in_default = False - at_start = False - if inferred_type: - argtypes.append(stars + inferred_type) - return "(" + ", ".join(argtypes) + ") -> " + restype - - # The parse tree has a different shape when there is a single - # decorator vs. when there are multiple decorators. - DECORATED = "decorated< (d=decorator | decorators< dd=decorator+ >) funcdef >" - decorated = compile_pattern(DECORATED) - - def get_decorators(self, node): - """Return a list of decorators found on a function definition. - - This is a list of strings; only simple decorators - (e.g. @staticmethod) are returned. - - If the function is undecorated or only non-simple decorators - are found, return []. - """ - if node.parent is None: - return [] - results = {} - if not self.decorated.match(node.parent, results): - return [] - decorators = results.get("dd") or [results["d"]] - decs = [] - for d in decorators: - for child in d.children: - if isinstance(child, Leaf) and child.type == token.NAME: - decs.append(child.value) - return decs - - def is_method(self, node): - """Return whether the node occurs (directly) inside a class.""" - node = node.parent - while node is not None: - if node.type == syms.classdef: - return True - if node.type == syms.funcdef: - return False - node = node.parent - return False - - RETURN_EXPR = "return_stmt< 'return' any >" - return_expr = compile_pattern(RETURN_EXPR) - - def has_return_exprs(self, node): - """Traverse the tree below node looking for 'return expr'. - - Return True if at least 'return expr' is found, False if not. - (If both 'return' and 'return expr' are found, return True.) - """ - results = {} - if self.return_expr.match(node, results): - return True - return any( - child.type not in (syms.funcdef, syms.classdef) and self.has_return_exprs(child) - for child in node.children - ) diff --git a/misc/remove-eol-whitespace.sh b/misc/remove-eol-whitespace.sh deleted file mode 100644 index 5cf666997e34..000000000000 --- a/misc/remove-eol-whitespace.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# Remove trailing whitespace from all non-binary files in a git repo. - -# From https://gist.github.com/dpaluy/3690668; originally from here: -# https://unix.stackexchange.com/questions/36233/how-to-skip-file-in-sed-if-it-contains-regex/36240#36240 - -git grep -I --name-only -z -e '' | xargs -0 sed -i -e 's/[ \t]\+\(\r\?\)$/\1/' From 14e7768c3bd8d1164e887ce3becba3459ebcfaa4 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 21 Jul 2023 22:27:36 +0300 Subject: [PATCH 0154/1617] Raise errors on unbound TypeVars with values (#15732) Completes a `TODO` item :) Refs https://github.com/python/mypy/issues/15724 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 7 +++++++ test-data/unit/check-typevar-unbound.test | 3 +-- test-data/unit/deps-generics.test | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index f2873c7d58e4..724a1dd1f7d7 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1069,6 +1069,7 @@ def check_func_def( """Type check a function definition.""" # Expand type variables with value restrictions to ordinary types. expanded = self.expand_typevars(defn, typ) + original_typ = typ for item, typ in expanded: old_binder = self.binder self.binder = ConditionalTypeBinder() @@ -1126,6 +1127,12 @@ def check_func_def( message_registry.RETURN_TYPE_CANNOT_BE_CONTRAVARIANT, typ.ret_type ) self.check_unbound_return_typevar(typ) + elif ( + isinstance(original_typ.ret_type, TypeVarType) and original_typ.ret_type.values + ): + # Since type vars with values are expanded, the return type is changed + # to a raw value. This is a hack to get it back. + self.check_unbound_return_typevar(original_typ) # Check that Generator functions have the appropriate return type. if defn.is_generator: diff --git a/test-data/unit/check-typevar-unbound.test b/test-data/unit/check-typevar-unbound.test index d3e54c75e373..ed6beaa100db 100644 --- a/test-data/unit/check-typevar-unbound.test +++ b/test-data/unit/check-typevar-unbound.test @@ -15,8 +15,7 @@ def g() -> U: # E: A function returning TypeVar should receive at least one argu V = TypeVar('V', int, str) -# TODO: this should also give an error -def h() -> V: +def h() -> V: # E: A function returning TypeVar should receive at least one argument containing the same TypeVar ... [case testInnerFunctionTypeVar] diff --git a/test-data/unit/deps-generics.test b/test-data/unit/deps-generics.test index c78f3fad90c0..6baa57266d2f 100644 --- a/test-data/unit/deps-generics.test +++ b/test-data/unit/deps-generics.test @@ -159,7 +159,7 @@ class D: pass T = TypeVar('T', A, B) S = TypeVar('S', C, D) -def f(x: T) -> S: +def f(x: T, y: S) -> S: pass [out] -> , , m, m.A, m.f From 6bdcc92002a5e1a6feb1528d0221802f7514c836 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 22 Jul 2023 21:20:36 +0100 Subject: [PATCH 0155/1617] stubtest: Fix `__mypy-replace` false positives (#15689) --- mypy/stubtest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index f06faa962b07..906a8c923b37 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -496,7 +496,11 @@ def verify_typeinfo( ) # Check everything already defined on the stub class itself (i.e. not inherited) - to_check = set(stub.names) + # + # Filter out non-identifier names, as these are (hopefully always?) whacky/fictional things + # (like __mypy-replace or __mypy-post_init, etc.) that don't exist at runtime, + # and exist purely for internal mypy reasons + to_check = {name for name in stub.names if name.isidentifier()} # Check all public things on the runtime class to_check.update( m for m in vars(runtime) if not is_probably_private(m) and m not in IGNORABLE_CLASS_DUNDERS From d2022a0007c0eb176ccaf37a9aa54c958be7fb10 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 23 Jul 2023 01:43:44 +0200 Subject: [PATCH 0156/1617] Add `__match_args__` to dataclasses with no fields (#15749) --- mypy/plugins/dataclasses.py | 1 - test-data/unit/check-dataclasses.test | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index a4babe7faf61..d782acf50af5 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -365,7 +365,6 @@ def transform(self) -> bool: and ( "__match_args__" not in info.names or info.names["__match_args__"].plugin_generated ) - and attributes and py_version >= (3, 10) ): str_type = self._api.named_type("builtins.str") diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 3866442230bf..1e01a72921f7 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1892,6 +1892,11 @@ class Two: bar: int t: Two reveal_type(t.__match_args__) # N: Revealed type is "Tuple[Literal['bar']]" +@dataclass +class Empty: + ... +e: Empty +reveal_type(e.__match_args__) # N: Revealed type is "Tuple[]" [builtins fixtures/dataclasses.pyi] [case testDataclassWithoutMatchArgs] From 01c6994ac01e3822fe89c5dc46f8bc8b656c8263 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Wed, 26 Jul 2023 03:34:51 -0400 Subject: [PATCH 0157/1617] Don't flag intentionally empty generators unreachable (#15722) Co-authored-by: Alex Waygood --- mypy/binder.py | 8 ------- mypy/checker.py | 25 ++++++++++++++++++++-- test-data/unit/check-unreachable-code.test | 16 ++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 37c0b6bb9006..8a68f24f661e 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -42,13 +42,6 @@ def __init__(self, id: int, conditional_frame: bool = False) -> None: self.types: dict[Key, Type] = {} self.unreachable = False self.conditional_frame = conditional_frame - - # Should be set only if we're entering a frame where it's not - # possible to accurately determine whether or not contained - # statements will be unreachable or not. - # - # Long-term, we should improve mypy to the point where we no longer - # need this field. self.suppress_unreachable_warnings = False def __repr__(self) -> str: @@ -174,7 +167,6 @@ def is_unreachable(self) -> bool: return any(f.unreachable for f in self.frames) def is_unreachable_warning_suppressed(self) -> bool: - # TODO: See todo in 'is_unreachable' return any(f.suppress_unreachable_warnings for f in self.frames) def cleanse(self, expr: Expression) -> None: diff --git a/mypy/checker.py b/mypy/checker.py index 724a1dd1f7d7..e0cd02e74573 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -132,6 +132,7 @@ Var, WhileStmt, WithStmt, + YieldExpr, is_final_node, ) from mypy.options import Options @@ -1241,13 +1242,17 @@ def check_func_def( new_frame.types[key] = narrowed_type self.binder.declarations[key] = old_binder.declarations[key] with self.scope.push_function(defn): - # We suppress reachability warnings when we use TypeVars with value + # We suppress reachability warnings for empty generator functions + # (return; yield) which have a "yield" that's unreachable by definition + # since it's only there to promote the function into a generator function. + # + # We also suppress reachability warnings when we use TypeVars with value # restrictions: we only want to report a warning if a certain statement is # marked as being suppressed in *all* of the expansions, but we currently # have no good way of doing this. # # TODO: Find a way of working around this limitation - if len(expanded) >= 2: + if _is_empty_generator_function(item) or len(expanded) >= 2: self.binder.suppress_unreachable_warnings() self.accept(item.body) unreachable = self.binder.is_unreachable() @@ -6968,6 +6973,22 @@ def is_literal_not_implemented(n: Expression) -> bool: return isinstance(n, NameExpr) and n.fullname == "builtins.NotImplemented" +def _is_empty_generator_function(func: FuncItem) -> bool: + """ + Checks whether a function's body is 'return; yield' (the yield being added only + to promote the function into a generator function). + """ + body = func.body.body + return ( + len(body) == 2 + and isinstance(ret_stmt := body[0], ReturnStmt) + and (ret_stmt.expr is None or is_literal_none(ret_stmt.expr)) + and isinstance(expr_stmt := body[1], ExpressionStmt) + and isinstance(yield_expr := expr_stmt.expr, YieldExpr) + and (yield_expr.expr is None or is_literal_none(yield_expr.expr)) + ) + + def builtin_item_type(tp: Type) -> Type | None: """Get the item type of a builtin container. diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 76ecd9f51e35..7a6c2cbfd1c7 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1446,3 +1446,19 @@ def f() -> None: Foo()['a'] = 'a' x = 0 # This should not be reported as unreachable [builtins fixtures/exception.pyi] + +[case testIntentionallyEmptyGeneratorFunction] +# flags: --warn-unreachable +from typing import Generator + +def f() -> Generator[None, None, None]: + return + yield + +[case testIntentionallyEmptyGeneratorFunction_None] +# flags: --warn-unreachable +from typing import Generator + +def f() -> Generator[None, None, None]: + return None + yield None From b901d21194400b856a88df62a3d7db871936a50d Mon Sep 17 00:00:00 2001 From: Marcel Johannesmann Date: Wed, 26 Jul 2023 20:50:13 +0200 Subject: [PATCH 0158/1617] docs: add missing verb (#15765) --- docs/source/cheat_sheet_py3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index 297427e72aca..fe5761ca6187 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -104,7 +104,7 @@ Functions print(value + "!" * excitement) # Note that arguments without a type are dynamically typed (treated as Any) - # and that functions without any annotations not checked + # and that functions without any annotations are not checked def untyped(x): x.anything() + 1 + "string" # no errors From a8467c43fb6423cc3f7f330f361e6b5af0bf284f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 28 Jul 2023 14:59:18 +0300 Subject: [PATCH 0159/1617] [stubgen] Add required `...` rhs to `NamedTuple` fields with default values (#15680) Closes https://github.com/python/mypy/issues/15638 --- mypy/stubgen.py | 19 ++++++++++++- test-data/unit/stubgen.test | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 9084da2053cf..a77ee738d56f 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -102,6 +102,7 @@ OverloadedFuncDef, Statement, StrExpr, + TempNode, TupleExpr, TypeInfo, UnaryExpr, @@ -637,6 +638,7 @@ def __init__( self._state = EMPTY self._toplevel_names: list[str] = [] self._include_private = include_private + self._current_class: ClassDef | None = None self.import_tracker = ImportTracker() # Was the tree semantically analysed before? self.analyzed = analyzed @@ -886,6 +888,7 @@ def get_fullname(self, expr: Expression) -> str: return resolved_name def visit_class_def(self, o: ClassDef) -> None: + self._current_class = o self.method_names = find_method_names(o.defs.body) sep: int | None = None if not self._indent and self._state != EMPTY: @@ -922,6 +925,7 @@ def visit_class_def(self, o: ClassDef) -> None: else: self._state = CLASS self.method_names = set() + self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: """Get list of base classes for a class.""" @@ -1330,7 +1334,20 @@ def get_init( typename += f"[{final_arg}]" else: typename = self.get_str_type_of_node(rvalue) - return f"{self._indent}{lvalue}: {typename}\n" + initializer = self.get_assign_initializer(rvalue) + return f"{self._indent}{lvalue}: {typename}{initializer}\n" + + def get_assign_initializer(self, rvalue: Expression) -> str: + """Does this rvalue need some special initializer value?""" + if self._current_class and self._current_class.info: + # Current rules + # 1. Return `...` if we are dealing with `NamedTuple` and it has an existing default value + if self._current_class.info.is_named_tuple and not isinstance(rvalue, TempNode): + return " = ..." + # TODO: support other possible cases, where initializer is important + + # By default, no initializer is required: + return "" def add(self, string: str) -> None: """Add text to generated stub.""" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index b387aa840dc9..f6b71a994153 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -698,6 +698,62 @@ class Y(NamedTuple): a: int b: str +[case testNamedTupleClassSyntax_semanal] +from typing import NamedTuple + +class A(NamedTuple): + x: int + y: str = 'a' + +class B(A): + z1: str + z2 = 1 + z3: str = 'b' + +class RegularClass: + x: int + y: str = 'a' + class NestedNamedTuple(NamedTuple): + x: int + y: str = 'a' + z: str = 'b' +[out] +from typing import NamedTuple + +class A(NamedTuple): + x: int + y: str = ... + +class B(A): + z1: str + z2: int + z3: str + +class RegularClass: + x: int + y: str + class NestedNamedTuple(NamedTuple): + x: int + y: str = ... + z: str + + +[case testNestedClassInNamedTuple_semanal-xfail] +from typing import NamedTuple + +# TODO: make sure that nested classes in `NamedTuple` are supported: +class NamedTupleWithNestedClass(NamedTuple): + class Nested: + x: int + y: str = 'a' +[out] +from typing import NamedTuple + +class NamedTupleWithNestedClass(NamedTuple): + class Nested: + x: int + y: str + [case testEmptyNamedtuple] import collections, typing X = collections.namedtuple('X', []) From da1853ff7f764157511ece4305a11369f63353f5 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 29 Jul 2023 11:05:41 +0300 Subject: [PATCH 0160/1617] Correctly narrow types for `tuple[type[X], ...]` (#15691) `flatten_types` forgot about the second way we represent `tuple` inside. Closes https://github.com/python/mypy/issues/15443 --------- Co-authored-by: Ilya Priven --- mypy/checker.py | 2 ++ test-data/unit/check-narrowing.test | 47 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index e0cd02e74573..30dbdd01d972 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7146,6 +7146,8 @@ def flatten_types(t: Type) -> list[Type]: t = get_proper_type(t) if isinstance(t, TupleType): return [b for a in t.items for b in flatten_types(a)] + elif is_named_instance(t, "builtins.tuple"): + return [t.args[0]] else: return [t] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 22014d4c645c..b763e0ff3b68 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1261,3 +1261,50 @@ def g() -> None: def foo(): ... foo() [builtins fixtures/dict.pyi] + + +[case testNarrowingWithTupleOfTypes] +from typing import Tuple, Type + +class Base: ... + +class Impl1(Base): ... +class Impl2(Base): ... + +impls: Tuple[Type[Base], ...] = (Impl1, Impl2) +some: object + +if isinstance(some, impls): + reveal_type(some) # N: Revealed type is "__main__.Base" +else: + reveal_type(some) # N: Revealed type is "builtins.object" + +raw: Tuple[type, ...] +if isinstance(some, raw): + reveal_type(some) # N: Revealed type is "builtins.object" +else: + reveal_type(some) # N: Revealed type is "builtins.object" +[builtins fixtures/dict.pyi] + + +[case testNarrowingWithTupleOfTypesPy310Plus] +# flags: --python-version 3.10 +class Base: ... + +class Impl1(Base): ... +class Impl2(Base): ... + +some: int | Base + +impls: tuple[type[Base], ...] = (Impl1, Impl2) +if isinstance(some, impls): + reveal_type(some) # N: Revealed type is "__main__.Base" +else: + reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]" + +raw: tuple[type, ...] +if isinstance(some, raw): + reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]" +else: + reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]" +[builtins fixtures/dict.pyi] From 14efdf2f1ec098b59b65796b3a37bd84210eca85 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 29 Jul 2023 14:47:02 +0300 Subject: [PATCH 0161/1617] [stubtest] Test `NamedTuple` definitions with default fields (#15774) This is a test case for https://github.com/python/mypy/pull/15680 from `stubtest`'s point of view. --- mypy/test/teststubtest.py | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 661d46e9fd8a..cd72bd9300d1 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -68,6 +68,7 @@ class Mapping(Generic[_K, _V]): ... class Match(Generic[AnyStr]): ... class Sequence(Iterable[_T_co]): ... class Tuple(Sequence[_T_co]): ... +class NamedTuple(tuple[Any, ...]): ... def overload(func: _T) -> _T: ... """ @@ -82,6 +83,7 @@ def overload(func: _T) -> _T: ... class object: __module__: str def __init__(self) -> None: pass + def __repr__(self) -> str: pass class type: ... class tuple(Sequence[T_co], Generic[T_co]): ... @@ -1599,6 +1601,72 @@ class Y(TypedDict): error=None, ) + @collect_cases + def test_named_tuple(self) -> Iterator[Case]: + yield Case( + stub="from typing import NamedTuple", + runtime="from typing import NamedTuple", + error=None, + ) + yield Case( + stub=""" + class X1(NamedTuple): + bar: int + foo: str = ... + """, + runtime=""" + class X1(NamedTuple): + bar: int + foo: str = 'a' + """, + error=None, + ) + yield Case( + stub=""" + class X2(NamedTuple): + bar: int + foo: str + """, + runtime=""" + class X2(NamedTuple): + bar: int + foo: str = 'a' + """, + # `__new__` will miss a default value for a `foo` parameter, + # but we don't generate special errors for `foo` missing `...` part. + error="X2.__new__", + ) + + @collect_cases + def test_named_tuple_typing_and_collections(self) -> Iterator[Case]: + yield Case( + stub="from typing import NamedTuple", + runtime="from collections import namedtuple", + error=None, + ) + yield Case( + stub=""" + class X1(NamedTuple): + bar: int + foo: str = ... + """, + runtime=""" + X1 = namedtuple('X1', ['bar', 'foo'], defaults=['a']) + """, + error=None, + ) + yield Case( + stub=""" + class X2(NamedTuple): + bar: int + foo: str + """, + runtime=""" + X2 = namedtuple('X1', ['bar', 'foo'], defaults=['a']) + """, + error="X2.__new__", + ) + @collect_cases def test_type_var(self) -> Iterator[Case]: yield Case( From 6040b237e31978b7f6764266a3d162acb68c7884 Mon Sep 17 00:00:00 2001 From: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> Date: Sat, 29 Jul 2023 18:55:19 +0200 Subject: [PATCH 0162/1617] Remove the Python 37 environment from the `tox.ini` (#15693) Remove the Python 37 environment from the `tox.ini` since Python 3.7 is now end of life. Co-authored-by: Alex Waygood --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b5314114570b..8fc76aed7d0e 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,10 @@ minversion = 4.4.4 skip_missing_interpreters = {env:TOX_SKIP_MISSING_INTERPRETERS:True} envlist = - py37, py38, py39, py310, + py311, docs, lint, type, From 8792ff1b81c98644c2563d6526dcba633fba719c Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 29 Jul 2023 22:33:04 +0300 Subject: [PATCH 0163/1617] Raise `RuntimeError` with better error messages (#15778) While working on https://github.com/python/mypy/pull/15776 I've noticed that some `RuntimeError` do not have enough metadata to understand what is going on. CI: https://github.com/python/mypy/actions/runs/5700479199/job/15450345887 This PR adds more context to error messages. --- mypy/erasetype.py | 2 +- mypy/nodes.py | 6 +++--- mypy/patterns.py | 2 +- mypy/server/astmerge.py | 4 ++-- mypy/types.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 6533d0c4e0f9..fbbb4f80b578 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -71,7 +71,7 @@ def visit_erased_type(self, t: ErasedType) -> ProperType: def visit_partial_type(self, t: PartialType) -> ProperType: # Should not get here. - raise RuntimeError() + raise RuntimeError("Cannot erase partial types") def visit_deleted_type(self, t: DeletedType) -> ProperType: return t diff --git a/mypy/nodes.py b/mypy/nodes.py index 2d763fc482d3..ebd222f4f253 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -203,7 +203,7 @@ def str_with_options(self, options: Options) -> str: return ans def accept(self, visitor: NodeVisitor[T]) -> T: - raise RuntimeError("Not implemented") + raise RuntimeError("Not implemented", type(self)) @trait @@ -213,7 +213,7 @@ class Statement(Node): __slots__ = () def accept(self, visitor: StatementVisitor[T]) -> T: - raise RuntimeError("Not implemented") + raise RuntimeError("Not implemented", type(self)) @trait @@ -223,7 +223,7 @@ class Expression(Node): __slots__ = () def accept(self, visitor: ExpressionVisitor[T]) -> T: - raise RuntimeError("Not implemented") + raise RuntimeError("Not implemented", type(self)) class FakeExpression(Expression): diff --git a/mypy/patterns.py b/mypy/patterns.py index 32c27d2a5b3c..839864ef5879 100644 --- a/mypy/patterns.py +++ b/mypy/patterns.py @@ -19,7 +19,7 @@ class Pattern(Node): __slots__ = () def accept(self, visitor: PatternVisitor[T]) -> T: - raise RuntimeError("Not implemented") + raise RuntimeError("Not implemented", type(self)) class AsPattern(Pattern): diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 5e3759227c7b..f58a4eedabc8 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -467,13 +467,13 @@ def visit_overloaded(self, t: Overloaded) -> None: def visit_erased_type(self, t: ErasedType) -> None: # This type should exist only temporarily during type inference - raise RuntimeError + raise RuntimeError("Cannot handle erased type") def visit_deleted_type(self, typ: DeletedType) -> None: pass def visit_partial_type(self, typ: PartialType) -> None: - raise RuntimeError + raise RuntimeError("Cannot handle partial type") def visit_tuple_type(self, typ: TupleType) -> None: for item in typ.items: diff --git a/mypy/types.py b/mypy/types.py index ba629a3553cf..9eeaa2cc4c3f 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -260,7 +260,7 @@ def can_be_false_default(self) -> bool: return True def accept(self, visitor: TypeVisitor[T]) -> T: - raise RuntimeError("Not implemented") + raise RuntimeError("Not implemented", type(self)) def __repr__(self) -> str: return self.accept(TypeStrVisitor(options=Options())) From 710ad44916fa89b430407c02a62a6df98f3a06f8 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 30 Jul 2023 00:50:37 +0300 Subject: [PATCH 0164/1617] Better `tox` configuration (#15777) It solves two problems: 1. `fix_annotate` and `async_matrix` were removed in https://github.com/python/mypy/pull/15728 2. It is better to reuse stuff like `runtests.py` not to rewrite the same command we already have --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- runtests.py | 12 +++++++++++- tox.ini | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/runtests.py b/runtests.py index 66fade81ffab..80ef8d814ee1 100755 --- a/runtests.py +++ b/runtests.py @@ -48,7 +48,17 @@ # time to run. cmds = { # Self type check - "self": [executable, "-m", "mypy", "--config-file", "mypy_self_check.ini", "-p", "mypy"], + "self": [ + executable, + "-m", + "mypy", + "--config-file", + "mypy_self_check.ini", + "-p", + "mypy", + "-p", + "mypyc", + ], # Lint "lint": ["pre-commit", "run", "--all-files"], # Fast test cases only (this is the bulk of the test suite) diff --git a/tox.ini b/tox.ini index 8fc76aed7d0e..5a728e27fec4 100644 --- a/tox.ini +++ b/tox.ini @@ -53,5 +53,5 @@ passenv = MYPY_FORCE_COLOR MYPY_FORCE_TERMINAL_WIDTH commands = - python -m mypy --config-file mypy_self_check.ini -p mypy -p mypyc - python -m mypy --config-file mypy_self_check.ini misc --exclude misc/fix_annotate.py --exclude misc/async_matrix.py --exclude misc/sync-typeshed.py + python runtests.py self + python -m mypy --config-file mypy_self_check.ini misc --exclude misc/sync-typeshed.py From 002502a0111852c360f2255830951473bcfec4a7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:13:21 -0700 Subject: [PATCH 0165/1617] Fix inference for attrs.fields (#15688) --- mypy/checker.py | 5 ++++- test-data/unit/check-plugin-attrs.test | 3 +++ test-data/unit/fixtures/plugin_attrs.pyi | 11 +++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 30dbdd01d972..a8cb2b862fbc 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4632,7 +4632,10 @@ def analyze_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: if int_type: return iterator, int_type - if isinstance(iterable, TupleType): + if ( + isinstance(iterable, TupleType) + and iterable.partial_fallback.type.fullname == "builtins.tuple" + ): joined: Type = UninhabitedType() for item in iterable.items: joined = join_types(joined, item) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 913584224764..7a7bcb65fe98 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1568,6 +1568,9 @@ reveal_type(f(A)[0]) # N: Revealed type is "attr.Attribute[builtins.int]" reveal_type(f(A).b) # N: Revealed type is "attr.Attribute[builtins.int]" f(A).x # E: "____main___A_AttrsAttributes__" has no attribute "x" +for ff in f(A): + reveal_type(ff) # N: Revealed type is "attr.Attribute[Any]" + [builtins fixtures/plugin_attrs.pyi] [case testAttrsGenericFields] diff --git a/test-data/unit/fixtures/plugin_attrs.pyi b/test-data/unit/fixtures/plugin_attrs.pyi index f62104809e74..57e5ecd1b2bc 100644 --- a/test-data/unit/fixtures/plugin_attrs.pyi +++ b/test-data/unit/fixtures/plugin_attrs.pyi @@ -1,5 +1,5 @@ # Builtins stub used to support attrs plugin tests. -from typing import Union, overload +from typing import Union, overload, Generic, Sequence, TypeVar, Type, Iterable, Iterator class object: def __init__(self) -> None: pass @@ -24,6 +24,13 @@ class complex: class str: pass class ellipsis: pass -class tuple: pass class list: pass class dict: pass + +T = TypeVar("T") +Tco = TypeVar('Tco', covariant=True) +class tuple(Sequence[Tco], Generic[Tco]): + def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... + def __iter__(self) -> Iterator[Tco]: pass + def __contains__(self, item: object) -> bool: pass + def __getitem__(self, x: int) -> Tco: pass From d71afbf89437bdf34566f50923759ead2736d93a Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 30 Jul 2023 16:53:47 +0300 Subject: [PATCH 0166/1617] Change `tuple[]` repr to `tuple[()]` (#15783) Closes https://github.com/python/mypy/issues/15782 --- mypy/messages.py | 5 +++-- mypy/test/testtypes.py | 4 ++-- mypy/types.py | 2 +- test-data/unit/check-async-await.test | 2 +- test-data/unit/check-dataclasses.test | 2 +- test-data/unit/check-namedtuple.test | 12 +++++++++++- test-data/unit/check-overloading.test | 2 +- test-data/unit/check-python310.test | 4 ++-- test-data/unit/check-tuples.test | 21 +++++++++++++++------ test-data/unit/check-type-aliases.test | 8 ++++---- test-data/unit/check-typevar-tuple.test | 2 +- test-data/unit/fine-grained.test | 2 +- test-data/unit/typexport-basic.test | 8 ++++---- 13 files changed, 47 insertions(+), 27 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 8b88cc1678a4..c9bf26f8952e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2507,10 +2507,11 @@ 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) + type_items = format_list(typ.items) or "()" if options.use_lowercase_names(): - s = f"tuple[{format_list(typ.items)}]" + s = f"tuple[{type_items}]" else: - s = f"Tuple[{format_list(typ.items)}]" + s = f"Tuple[{type_items}]" return s elif isinstance(typ, TypedDictType): # If the TypedDictType is named, return the name diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index b1f21b3be79b..59457dfa5d3b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -129,10 +129,10 @@ def test_callable_type_with_var_args(self) -> None: ) assert_equal(str(c3), "def (X? =, *Y?) -> Any") - def test_tuple_type(self) -> None: + def test_tuple_type_upper(self) -> None: options = Options() options.force_uppercase_builtins = True - assert_equal(TupleType([], self.fx.std_tuple).str_with_options(options), "Tuple[]") + 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( TupleType( diff --git a/mypy/types.py b/mypy/types.py index 9eeaa2cc4c3f..d13cff00c06d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3197,7 +3197,7 @@ def visit_overloaded(self, t: Overloaded) -> str: return f"Overload({', '.join(a)})" def visit_tuple_type(self, t: TupleType) -> str: - s = self.list_str(t.items) + 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 diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 3b7ef53b6bd6..af6c31624b96 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -475,7 +475,7 @@ async def gen() -> AsyncGenerator[int, str]: async def h() -> None: g = gen() - await g.asend(()) # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "Tuple[]"; expected "str" + await g.asend(()) # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "Tuple[()]"; expected "str" reveal_type(await g.asend('hello')) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 1e01a72921f7..7881dfbcf1bb 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1896,7 +1896,7 @@ reveal_type(t.__match_args__) # N: Revealed type is "Tuple[Literal['bar']]" class Empty: ... e: Empty -reveal_type(e.__match_args__) # N: Revealed type is "Tuple[]" +reveal_type(e.__match_args__) # N: Revealed type is "Tuple[()]" [builtins fixtures/dataclasses.pyi] [case testDataclassWithoutMatchArgs] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index d69b924971e1..6e3628060617 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -931,6 +931,16 @@ reveal_type(A().b) # N: Revealed type is "typing.NamedTuple" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testEmptyNamedTupleTypeRepr] +from typing import NamedTuple + +N = NamedTuple('N', []) +n: N +reveal_type(N) # N: Revealed type is "def () -> Tuple[(), fallback=__main__.N]" +reveal_type(n) # N: Revealed type is "Tuple[(), fallback=__main__.N]" +[builtins fixtures/tuple.pyi] + [case testNamedTupleWrongfile] from typing import NamedTuple from b import Type1 @@ -1036,7 +1046,7 @@ def good6() -> NamedTuple: def bad1() -> NamedTuple: return 1 # E: Incompatible return value type (got "int", expected "NamedTuple") def bad2() -> NamedTuple: - return () # E: Incompatible return value type (got "Tuple[]", expected "NamedTuple") + return () # E: Incompatible return value type (got "Tuple[()]", expected "NamedTuple") def bad3() -> NamedTuple: return (1, 2) # E: Incompatible return value type (got "Tuple[int, int]", expected "NamedTuple") diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index f49a15ada85c..89e5aea210b4 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1145,7 +1145,7 @@ def f(x: str) -> None: pass f(1.1) f('') f(1) -f(()) # E: No overload variant of "f" matches argument type "Tuple[]" \ +f(()) # E: No overload variant of "f" matches argument type "Tuple[()]" \ # N: Possible overload variants: \ # N: def f(x: float) -> None \ # N: def f(x: str) -> None diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index c07a90b49e63..75293ce9d193 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1568,8 +1568,8 @@ class AnnAssign(stmt): value: str simple: int -reveal_type(AST.__match_args__) # N: Revealed type is "Tuple[]" -reveal_type(stmt.__match_args__) # N: Revealed type is "Tuple[]" +reveal_type(AST.__match_args__) # N: Revealed type is "Tuple[()]" +reveal_type(stmt.__match_args__) # N: Revealed type is "Tuple[()]" reveal_type(AnnAssign.__match_args__) # N: Revealed type is "Tuple[Literal['target']?, Literal['annotation']?, Literal['value']?, Literal['simple']?]" AnnAssign.__match_args__ = ('a', 'b', 'c', 'd') # E: Cannot assign to "__match_args__" diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index f64d24a4ed6b..cff261774663 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -143,7 +143,7 @@ t3 = None # type: Tuple[A, B] a, b, c = None, None, None # type: (A, B, C) if int(): - t2 = () # E: Incompatible types in assignment (expression has type "Tuple[]", variable has type "Tuple[A]") + t2 = () # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[A]") if int(): t2 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") if int(): @@ -1244,9 +1244,9 @@ f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Any, . from typing import Tuple def f(a: Tuple[()]) -> None: pass f(()) -f((1,)) # E: Argument 1 to "f" has incompatible type "Tuple[int]"; expected "Tuple[]" -f(('', '')) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[]" -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[]" +f((1,)) # E: Argument 1 to "f" has incompatible type "Tuple[int]"; expected "Tuple[()]" +f(('', '')) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[()]" +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[()]" [builtins fixtures/tuple.pyi] [case testNonliteralTupleIndex] @@ -1467,7 +1467,7 @@ from typing import Tuple t = ('',) * 2 reveal_type(t) # N: Revealed type is "Tuple[builtins.str, builtins.str]" t2 = ('',) * -1 -reveal_type(t2) # N: Revealed type is "Tuple[]" +reveal_type(t2) # N: Revealed type is "Tuple[()]" t3 = ('', 1) * 2 reveal_type(t3) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]" def f() -> Tuple[str, ...]: @@ -1475,12 +1475,21 @@ def f() -> Tuple[str, ...]: reveal_type(f() * 2) # N: Revealed type is "builtins.tuple[builtins.str, ...]" [builtins fixtures/tuple.pyi] +[case testEmptyTupleTypeRepr] +from typing import Tuple + +def f() -> Tuple[()]: ... + +reveal_type(f) # N: Revealed type is "def () -> Tuple[()]" +reveal_type(f()) # N: Revealed type is "Tuple[()]" +[builtins fixtures/tuple.pyi] + [case testMultiplyTupleByIntegerLiteralReverse] from typing import Tuple t = 2 * ('',) reveal_type(t) # N: Revealed type is "Tuple[builtins.str, builtins.str]" t2 = -1 * ('',) -reveal_type(t2) # N: Revealed type is "Tuple[]" +reveal_type(t2) # N: Revealed type is "Tuple[()]" t3 = 2 * ('', 1) reveal_type(t3) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]" def f() -> Tuple[str, ...]: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 42f22e89d6b7..3ca0c5ef0a4b 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -12,7 +12,7 @@ U = Union[int, str] def f(x: U) -> None: pass f(1) f('') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]" [targets __main__, __main__.f] [builtins fixtures/tuple.pyi] @@ -64,7 +64,7 @@ from _m import U def f(x: U) -> None: pass f(1) f('x') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]" [file _m.py] from typing import Union U = Union[int, str] @@ -170,11 +170,11 @@ f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "str" from typing import Tuple, Callable EmptyTuple = Tuple[()] x: EmptyTuple -reveal_type(x) # N: Revealed type is "Tuple[]" +reveal_type(x) # N: Revealed type is "Tuple[()]" EmptyTupleCallable = Callable[[Tuple[()]], None] f: EmptyTupleCallable -reveal_type(f) # N: Revealed type is "def (Tuple[])" +reveal_type(f) # N: Revealed type is "def (Tuple[()])" [builtins fixtures/list.pyi] [case testForwardTypeAlias] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 1024f90ee6b7..e822cea9304f 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -58,7 +58,7 @@ f_args3: Tuple[int, str, bool] reveal_type(f(f_args)) # N: Revealed type is "Tuple[builtins.str, builtins.str]" reveal_type(f(f_args2)) # N: Revealed type is "Tuple[builtins.str]" reveal_type(f(f_args3)) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.bool]" -f(empty) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Tuple[int]" +f(empty) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Tuple[int]" f(bad_args) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[int, str]" # TODO: This hits a crash where we assert len(templates.items) == 1. See visit_tuple_type # in mypy/constraints.py. diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 66c5ee46db2f..68f72a2aa992 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -8026,7 +8026,7 @@ A = NamedTuple('A', F) # type: ignore [builtins fixtures/list.pyi] [out] == -b.py:3: note: Revealed type is "Tuple[, fallback=a.A]" +b.py:3: note: Revealed type is "Tuple[(), fallback=a.A]" [case testImportOnTopOfAlias1] from a import A diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index 0dcd0098f177..cd2afe2c1c75 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -294,8 +294,8 @@ import typing x = () [builtins fixtures/primitives.pyi] [out] -NameExpr(2) : Tuple[] -TupleExpr(2) : Tuple[] +NameExpr(2) : Tuple[()] +TupleExpr(2) : Tuple[()] [case testInferTwoTypes] ## NameExpr @@ -313,8 +313,8 @@ def f() -> None: x = () [builtins fixtures/primitives.pyi] [out] -NameExpr(3) : Tuple[] -TupleExpr(3) : Tuple[] +NameExpr(3) : Tuple[()] +TupleExpr(3) : Tuple[()] -- Basic generics From cb813259c3b9dff6aaa8686793cf6a0634cf1f69 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 30 Jul 2023 18:48:12 +0300 Subject: [PATCH 0167/1617] Update pre-commit deps (#15784) Closes https://github.com/python/mypy/pull/15526 --- .pre-commit-config.yaml | 4 ++-- test-requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a56e1af938b8..7a4aada8d593 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,11 +6,11 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/hauntsaninja/black-pre-commit-mirror - rev: 23.3.0 # must match test-requirements.txt + rev: 23.7.0 # must match test-requirements.txt hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.272 # must match test-requirements.txt + rev: v0.0.280 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/test-requirements.txt b/test-requirements.txt index 5340973a4de1..6f7bec0375ad 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==23.3.0 # must match version in .pre-commit-config.yaml +black==23.7.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' @@ -11,6 +11,6 @@ psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.0.272 # must match version in .pre-commit-config.yaml +ruff==0.0.280 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 From 54bc37ccade0476a1738b33cd34b6eb35d7124e1 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 1 Aug 2023 15:59:34 +0100 Subject: [PATCH 0168/1617] reduce frequency of pre-commit.ci autoupdate PRs (#15798) --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a4aada8d593..8ee89cbb912f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,3 +14,5 @@ repos: hooks: - id: ruff args: [--exit-non-zero-on-fix] +ci: + autoupdate_schedule: quarterly From 2b613e5ba1ada5a44f88a90528af834bf9f770a7 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Thu, 3 Aug 2023 02:31:00 +0300 Subject: [PATCH 0169/1617] Fix type narrowing of `== None` and `in (None,)` conditions (#15760) --- mypy/checker.py | 10 +++++----- mypy/checkexpr.py | 9 +++++++-- mypy/plugins/common.py | 4 ++-- mypy/suggestions.py | 6 +++--- mypy/types_utils.py | 6 +++--- test-data/unit/check-narrowing.test | 26 ++++++++++++++++++++++++++ test-data/unit/fixtures/primitives.pyi | 3 ++- 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index a8cb2b862fbc..0c27da8b5ac8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -216,7 +216,7 @@ is_literal_type, is_named_instance, ) -from mypy.types_utils import is_optional, remove_optional, store_argument_type, strip_type +from mypy.types_utils import is_overlapping_none, remove_optional, store_argument_type, strip_type from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars, fill_typevars_with_any, has_no_typevars from mypy.util import is_dunder, is_sunder, is_typeshed_file @@ -5660,13 +5660,13 @@ def has_no_custom_eq_checks(t: Type) -> bool: if left_index in narrowable_operand_index_to_hash: # We only try and narrow away 'None' for now - if is_optional(item_type): + if is_overlapping_none(item_type): collection_item_type = get_proper_type( builtin_item_type(iterable_type) ) if ( collection_item_type is not None - and not is_optional(collection_item_type) + and not is_overlapping_none(collection_item_type) and not ( isinstance(collection_item_type, Instance) and collection_item_type.type.fullname == "builtins.object" @@ -6073,7 +6073,7 @@ def refine_away_none_in_comparison( non_optional_types = [] for i in chain_indices: typ = operand_types[i] - if not is_optional(typ): + if not is_overlapping_none(typ): non_optional_types.append(typ) # Make sure we have a mixture of optional and non-optional types. @@ -6083,7 +6083,7 @@ def refine_away_none_in_comparison( if_map = {} for i in narrowable_operand_indices: expr_type = operand_types[i] - if not is_optional(expr_type): + if not is_overlapping_none(expr_type): continue if any(is_overlapping_erased_types(expr_type, t) for t in non_optional_types): if_map[operands[i]] = remove_optional(expr_type) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 62e2298ba59d..114cde8327e0 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -169,7 +169,12 @@ is_named_instance, split_with_prefix_and_suffix, ) -from mypy.types_utils import is_generic_instance, is_optional, is_self_type_like, remove_optional +from mypy.types_utils import ( + is_generic_instance, + is_overlapping_none, + is_self_type_like, + remove_optional, +) from mypy.typestate import type_state from mypy.typevars import fill_typevars from mypy.typevartuples import find_unpack_in_list @@ -1809,7 +1814,7 @@ def infer_function_type_arguments_using_context( # valid results. erased_ctx = replace_meta_vars(ctx, ErasedType()) ret_type = callable.ret_type - if is_optional(ret_type) and is_optional(ctx): + if is_overlapping_none(ret_type) and is_overlapping_none(ctx): # If both the context and the return type are optional, unwrap the optional, # since in 99% cases this is what a user expects. In other words, we replace # Optional[T] <: Optional[int] diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 65d967577bea..55f2870cadb4 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -43,7 +43,7 @@ deserialize_type, get_proper_type, ) -from mypy.types_utils import is_optional +from mypy.types_utils import is_overlapping_none from mypy.typevars import fill_typevars from mypy.util import get_unique_redefinition_name @@ -141,7 +141,7 @@ def find_shallow_matching_overload_item(overload: Overloaded, call: CallExpr) -> break elif ( arg_none - and not is_optional(arg_type) + and not is_overlapping_none(arg_type) and not ( isinstance(arg_type, Instance) and arg_type.type.fullname == "builtins.object" diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 8e1225f00a2f..268f3032fc9b 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -79,7 +79,7 @@ UnionType, get_proper_type, ) -from mypy.types_utils import is_optional, remove_optional +from mypy.types_utils import is_overlapping_none, remove_optional from mypy.util import split_target @@ -752,7 +752,7 @@ def score_type(self, t: Type, arg_pos: bool) -> int: return 20 if any(has_any_type(x) for x in t.items): return 15 - if not is_optional(t): + if not is_overlapping_none(t): return 10 if isinstance(t, CallableType) and (has_any_type(t) or is_tricky_callable(t)): return 10 @@ -868,7 +868,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> str: return t.fallback.accept(self) def visit_union_type(self, t: UnionType) -> str: - if len(t.items) == 2 and is_optional(t): + if len(t.items) == 2 and is_overlapping_none(t): return f"Optional[{remove_optional(t).accept(self)}]" else: return super().visit_union_type(t) diff --git a/mypy/types_utils.py b/mypy/types_utils.py index 43bca05d6bf9..7f2e38ef3753 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -101,10 +101,10 @@ def is_generic_instance(tp: Type) -> bool: return isinstance(tp, Instance) and bool(tp.args) -def is_optional(t: Type) -> bool: +def is_overlapping_none(t: Type) -> bool: t = get_proper_type(t) - return isinstance(t, UnionType) and any( - isinstance(get_proper_type(e), NoneType) for e in t.items + return isinstance(t, NoneType) or ( + isinstance(t, UnionType) and any(isinstance(get_proper_type(e), NoneType) for e in t.items) ) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index b763e0ff3b68..291f73a45230 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1263,6 +1263,32 @@ def g() -> None: [builtins fixtures/dict.pyi] +[case testNarrowingOptionalEqualsNone] +from typing import Optional + +class A: ... + +val: Optional[A] + +if val == None: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +else: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +if val != None: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +else: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + +if val in (None,): + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +else: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +if val not in (None,): + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +else: + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" +[builtins fixtures/primitives.pyi] + [case testNarrowingWithTupleOfTypes] from typing import Tuple, Type diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index b74252857d6f..c9b1e3f4e983 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -45,7 +45,8 @@ class memoryview(Sequence[int]): def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass def __getitem__(self, item: int) -> int: pass -class tuple(Generic[T]): pass +class tuple(Generic[T]): + def __contains__(self, other: object) -> bool: pass class list(Sequence[T]): def __iter__(self) -> Iterator[T]: pass def __contains__(self, other: object) -> bool: pass From 0d708cb9c9d5291c1c988ef90a1b77307ed5315c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 4 Aug 2023 00:17:42 +0100 Subject: [PATCH 0170/1617] New type inference: complete transitive closure (#15754) This is a first follow-up for #15287 (I like how my PR titles sound like research paper titles, LOL) This PR completes the new type inference foundations by switching to a complete and well founded algorithm [1] for transitive closure (that replaces more ad hoc initial algorithm that covered 80% of cases and was good for experimenting with new inference scheme). In particular the algorithm in this PR covers two important edge cases (see tests). Some comments: * I don't intend to switch the default for `--new-type-inference`, I just want to see the effect of the switch on `mypy_primer`, I will switch back to false before merging * This flag is still not ready to be publicly announced, I am going to make another 2-3 PRs from the list in #15287 before making this public. * I am not adding yet the unit tests as discussed in previous PR. This PR is already quite big, and the next one (support for upper bounds and values) should be much smaller. I am going to add unit tests only for `transitive_closure()` which is the core of new logic. * While working on this I fixed couple bugs exposed in `TypeVarTuple` support: one is rare technical corner case, another one is serious, template and actual where swapped during constraint inference, effectively causing outer/return context to be completely ignored for instances. * It is better to review the PR with "ignore whitespace" option turned on (there is big chunk in solve.py that is just change of indentation). * There is one questionable design choice I am making in this PR, I am adding `extra_tvars` as an attribute of `Constraint` class, while it logically should not be attributed to any individual constraint, but rather to the full list of constrains. However, doing this properly would require changing the return type of `infer_constrains()` and all related functions, which would be a really big refactoring. [1] Definition 7.1 in https://inria.hal.science/inria-00073205/document --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 72 ++--- mypy/checkexpr.py | 40 ++- mypy/constraints.py | 63 +++-- mypy/expandtype.py | 5 + mypy/infer.py | 10 +- mypy/solve.py | 387 +++++++++++++------------- mypy/subtypes.py | 3 +- mypy/test/testconstraints.py | 3 - mypy/test/testsolve.py | 50 ++-- mypy/typeops.py | 6 +- mypy_self_check.ini | 1 + test-data/unit/check-generics.test | 20 +- test-data/unit/check-overloading.test | 15 + 13 files changed, 356 insertions(+), 319 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0c27da8b5ac8..b786155079e5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -734,8 +734,10 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # def foo(x: str) -> str: ... # # See Python 2's map function for a concrete example of this kind of overload. + current_class = self.scope.active_class() + type_vars = current_class.defn.type_vars if current_class else [] with state.strict_optional_set(True): - if is_unsafe_overlapping_overload_signatures(sig1, sig2): + if is_unsafe_overlapping_overload_signatures(sig1, sig2, type_vars): self.msg.overloaded_signatures_overlap(i + 1, i + j + 2, item.func) if impl_type is not None: @@ -1702,7 +1704,9 @@ def is_unsafe_overlapping_op( first = forward_tweaked second = reverse_tweaked - return is_unsafe_overlapping_overload_signatures(first, second) + current_class = self.scope.active_class() + type_vars = current_class.defn.type_vars if current_class else [] + return is_unsafe_overlapping_overload_signatures(first, second, type_vars) def check_inplace_operator_method(self, defn: FuncBase) -> None: """Check an inplace operator method such as __iadd__. @@ -3918,11 +3922,12 @@ def is_valid_defaultdict_partial_value_type(self, t: ProperType) -> bool: return True if len(t.args) == 1: arg = get_proper_type(t.args[0]) - # TODO: This is too permissive -- we only allow TypeVarType since - # they leak in cases like defaultdict(list) due to a bug. - # This can result in incorrect types being inferred, but only - # in rare cases. - if isinstance(arg, (TypeVarType, UninhabitedType, NoneType)): + if self.options.new_type_inference: + allowed = isinstance(arg, (UninhabitedType, NoneType)) + else: + # Allow leaked TypeVars for legacy inference logic. + allowed = isinstance(arg, (UninhabitedType, NoneType, TypeVarType)) + if allowed: return True return False @@ -7179,7 +7184,7 @@ def are_argument_counts_overlapping(t: CallableType, s: CallableType) -> bool: def is_unsafe_overlapping_overload_signatures( - signature: CallableType, other: CallableType + signature: CallableType, other: CallableType, class_type_vars: list[TypeVarLikeType] ) -> bool: """Check if two overloaded signatures are unsafely overlapping or partially overlapping. @@ -7198,8 +7203,8 @@ def is_unsafe_overlapping_overload_signatures( # This lets us identify cases where the two signatures use completely # incompatible types -- e.g. see the testOverloadingInferUnionReturnWithMixedTypevars # test case. - signature = detach_callable(signature) - other = detach_callable(other) + signature = detach_callable(signature, class_type_vars) + other = detach_callable(other, class_type_vars) # Note: We repeat this check twice in both directions due to a slight # asymmetry in 'is_callable_compatible'. When checking for partial overlaps, @@ -7230,7 +7235,7 @@ def is_unsafe_overlapping_overload_signatures( ) -def detach_callable(typ: CallableType) -> CallableType: +def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) -> CallableType: """Ensures that the callable's type variables are 'detached' and independent of the context. A callable normally keeps track of the type variables it uses within its 'variables' field. @@ -7240,42 +7245,17 @@ def detach_callable(typ: CallableType) -> CallableType: This function will traverse the callable and find all used type vars and add them to the variables field if it isn't already present. - The caller can then unify on all type variables whether or not the callable is originally - from a class or not.""" - type_list = typ.arg_types + [typ.ret_type] - - appear_map: dict[str, list[int]] = {} - for i, inner_type in enumerate(type_list): - typevars_available = get_type_vars(inner_type) - for var in typevars_available: - if var.fullname not in appear_map: - appear_map[var.fullname] = [] - appear_map[var.fullname].append(i) - - used_type_var_names = set() - for var_name, appearances in appear_map.items(): - used_type_var_names.add(var_name) - - all_type_vars = get_type_vars(typ) - new_variables = [] - for var in set(all_type_vars): - if var.fullname not in used_type_var_names: - continue - new_variables.append( - TypeVarType( - name=var.name, - fullname=var.fullname, - id=var.id, - values=var.values, - upper_bound=var.upper_bound, - default=var.default, - variance=var.variance, - ) - ) - out = typ.copy_modified( - variables=new_variables, arg_types=type_list[:-1], ret_type=type_list[-1] + The caller can then unify on all type variables whether the callable is originally from + the class or not.""" + if not class_type_vars: + # Fast path, nothing to update. + return typ + seen_type_vars = set() + for t in typ.arg_types + [typ.ret_type]: + seen_type_vars |= set(get_type_vars(t)) + return typ.copy_modified( + variables=list(typ.variables) + [tv for tv in class_type_vars if tv in seen_type_vars] ) - return out def overload_can_never_match(signature: CallableType, other: CallableType) -> bool: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 114cde8327e0..9e46d9ee39cb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1857,7 +1857,7 @@ def infer_function_type_arguments_using_context( # expects_literal(identity(3)) # Should type-check if not is_generic_instance(ctx) and not is_literal_type_like(ctx): return callable.copy_modified() - args = infer_type_arguments(callable.type_var_ids(), ret_type, erased_ctx) + args = infer_type_arguments(callable.variables, ret_type, erased_ctx) # Only substitute non-Uninhabited and non-erased types. new_args: list[Type | None] = [] for arg in args: @@ -1906,7 +1906,7 @@ def infer_function_type_arguments( else: pass1_args.append(arg) - inferred_args = infer_function_type_arguments( + inferred_args, _ = infer_function_type_arguments( callee_type, pass1_args, arg_kinds, @@ -1948,7 +1948,7 @@ def infer_function_type_arguments( # variables while allowing for polymorphic solutions, i.e. for solutions # potentially involving free variables. # TODO: support the similar inference for return type context. - poly_inferred_args = infer_function_type_arguments( + poly_inferred_args, free_vars = infer_function_type_arguments( callee_type, arg_types, arg_kinds, @@ -1957,30 +1957,28 @@ def infer_function_type_arguments( strict=self.chk.in_checked_function(), allow_polymorphic=True, ) - for i, pa in enumerate(get_proper_types(poly_inferred_args)): - if isinstance(pa, (NoneType, UninhabitedType)) or has_erased_component(pa): - # Indicate that free variables should not be applied in the call below. - poly_inferred_args[i] = None poly_callee_type = self.apply_generic_arguments( callee_type, poly_inferred_args, context ) - yes_vars = poly_callee_type.variables - no_vars = {v for v in callee_type.variables if v not in poly_callee_type.variables} - if not set(get_type_vars(poly_callee_type)) & no_vars: - # Try applying inferred polymorphic type if possible, e.g. Callable[[T], T] can - # be interpreted as def [T] (T) -> T, but dict[T, T] cannot be expressed. - applied = apply_poly(poly_callee_type, yes_vars) - if applied is not None and poly_inferred_args != [UninhabitedType()] * len( - poly_inferred_args - ): - freeze_all_type_vars(applied) - return applied + # Try applying inferred polymorphic type if possible, e.g. Callable[[T], T] can + # be interpreted as def [T] (T) -> T, but dict[T, T] cannot be expressed. + applied = apply_poly(poly_callee_type, free_vars) + if applied is not None and all( + a is not None and not isinstance(get_proper_type(a), UninhabitedType) + for a in poly_inferred_args + ): + freeze_all_type_vars(applied) + return applied # If it didn't work, erase free variables as , to avoid confusing errors. + unknown = UninhabitedType() + unknown.ambiguous = True inferred_args = [ - expand_type(a, {v.id: UninhabitedType() for v in callee_type.variables}) + expand_type( + a, {v.id: unknown for v in list(callee_type.variables) + free_vars} + ) if a is not None else None - for a in inferred_args + for a in poly_inferred_args ] else: # In dynamically typed functions use implicit 'Any' types for @@ -2019,7 +2017,7 @@ def infer_function_type_arguments_pass2( arg_types = self.infer_arg_types_in_context(callee_type, args, arg_kinds, formal_to_actual) - inferred_args = infer_function_type_arguments( + inferred_args, _ = infer_function_type_arguments( callee_type, arg_types, arg_kinds, diff --git a/mypy/constraints.py b/mypy/constraints.py index f9124630a706..299c6292a259 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -73,6 +73,10 @@ def __init__(self, type_var: TypeVarLikeType, op: int, target: Type) -> None: self.op = op self.target = target self.origin_type_var = type_var + # These are additional type variables that should be solved for together with type_var. + # TODO: A cleaner solution may be to modify the return type of infer_constraints() + # to include these instead, but this is a rather big refactoring. + self.extra_tvars: list[TypeVarLikeType] = [] def __repr__(self) -> str: op_str = "<:" @@ -168,7 +172,9 @@ def infer_constraints_for_callable( return constraints -def infer_constraints(template: Type, actual: Type, direction: int) -> list[Constraint]: +def infer_constraints( + template: Type, actual: Type, direction: int, skip_neg_op: bool = False +) -> list[Constraint]: """Infer type constraints. Match a template type, which may contain type variable references, @@ -187,7 +193,9 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons ((T, S), (X, Y)) --> T :> X and S :> Y (X[T], Any) --> T <: Any and T :> Any - The constraints are represented as Constraint objects. + The constraints are represented as Constraint objects. If skip_neg_op == True, + then skip adding reverse (polymorphic) constraints (since this is already a call + to infer such constraints). """ if any( get_proper_type(template) == get_proper_type(t) @@ -202,13 +210,15 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons # Return early on an empty branch. return [] type_state.inferring.append((template, actual)) - res = _infer_constraints(template, actual, direction) + res = _infer_constraints(template, actual, direction, skip_neg_op) type_state.inferring.pop() return res - return _infer_constraints(template, actual, direction) + return _infer_constraints(template, actual, direction, skip_neg_op) -def _infer_constraints(template: Type, actual: Type, direction: int) -> list[Constraint]: +def _infer_constraints( + template: Type, actual: Type, direction: int, skip_neg_op: bool +) -> list[Constraint]: orig_template = template template = get_proper_type(template) actual = get_proper_type(actual) @@ -284,7 +294,7 @@ def _infer_constraints(template: Type, actual: Type, direction: int) -> list[Con return [] # Remaining cases are handled by ConstraintBuilderVisitor. - return template.accept(ConstraintBuilderVisitor(actual, direction)) + return template.accept(ConstraintBuilderVisitor(actual, direction, skip_neg_op)) def infer_constraints_if_possible( @@ -510,10 +520,14 @@ class ConstraintBuilderVisitor(TypeVisitor[List[Constraint]]): # TODO: The value may be None. Is that actually correct? actual: ProperType - def __init__(self, actual: ProperType, direction: int) -> None: + def __init__(self, actual: ProperType, direction: int, skip_neg_op: bool) -> None: # Direction must be SUBTYPE_OF or SUPERTYPE_OF. self.actual = actual self.direction = direction + # Whether to skip polymorphic inference (involves inference in opposite direction) + # this is used to prevent infinite recursion when both template and actual are + # generic callables. + self.skip_neg_op = skip_neg_op # Trivial leaf types @@ -648,13 +662,13 @@ def visit_instance(self, template: Instance) -> list[Constraint]: assert mapped.type.type_var_tuple_prefix is not None assert mapped.type.type_var_tuple_suffix is not None - unpack_constraints, mapped_args, instance_args = build_constraints_for_unpack( - mapped.args, - mapped.type.type_var_tuple_prefix, - mapped.type.type_var_tuple_suffix, + unpack_constraints, instance_args, mapped_args = build_constraints_for_unpack( instance.args, instance.type.type_var_tuple_prefix, instance.type.type_var_tuple_suffix, + mapped.args, + mapped.type.type_var_tuple_prefix, + mapped.type.type_var_tuple_suffix, self.direction, ) res.extend(unpack_constraints) @@ -879,6 +893,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # Note that non-normalized callables can be created in annotations # using e.g. callback protocols. template = template.with_unpacked_kwargs() + extra_tvars = False if isinstance(self.actual, CallableType): res: list[Constraint] = [] cactual = self.actual.with_unpacked_kwargs() @@ -890,6 +905,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: type_state.infer_polymorphic and cactual.variables and cactual.param_spec() is None + and not self.skip_neg_op # Technically, the correct inferred type for application of e.g. # Callable[..., T] -> Callable[..., T] (with literal ellipsis), to a generic # like U -> U, should be Callable[..., Any], but if U is a self-type, we can @@ -897,18 +913,15 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # depends on this old behaviour. and not any(tv.id.raw_id == 0 for tv in cactual.variables) ): - # If actual is generic, unify it with template. Note: this is - # not an ideal solution (which would be adding the generic variables - # to the constraint inference set), but it's a good first approximation, - # and this will prevent leaking these variables in the solutions. - # Note: this may infer constraints like T <: S or T <: List[S] - # that contain variables in the target. - unified = mypy.subtypes.unify_generic_callable( - cactual, template, ignore_return=True + # If the actual callable is generic, infer constraints in the opposite + # direction, and indicate to the solver there are extra type variables + # to solve for (see more details in mypy/solve.py). + res.extend( + infer_constraints( + cactual, template, neg_op(self.direction), skip_neg_op=True + ) ) - if unified is not None: - cactual = unified - res.extend(infer_constraints(cactual, template, neg_op(self.direction))) + extra_tvars = True # We can't infer constraints from arguments if the template is Callable[..., T] # (with literal '...'). @@ -978,6 +991,9 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: cactual_ret_type = cactual.type_guard res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) + if extra_tvars: + for c in res: + c.extra_tvars = list(cactual.variables) return res elif isinstance(self.actual, AnyType): param_spec = template.param_spec() @@ -1205,6 +1221,9 @@ def find_and_build_constraints_for_unpack( def build_constraints_for_unpack( + # TODO: this naming is misleading, these should be "actual", not "mapped" + # both template and actual can be mapped before, depending on direction. + # Also the convention is to put template related args first. mapped: tuple[Type, ...], mapped_prefix_len: int | None, mapped_suffix_len: int | None, diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 83d9bf4c8725..b599b49e4c12 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -272,6 +272,11 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: return repl def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + # Sometimes solver may need to expand a type variable with (a copy of) itself + # (usually together with other TypeVars, but it is hard to filter out TypeVarTuples). + repl = self.variables[t.id] + if isinstance(repl, TypeVarTupleType): + return repl raise NotImplementedError def visit_unpack_type(self, t: UnpackType) -> Type: diff --git a/mypy/infer.py b/mypy/infer.py index 66ca4169e2ff..f34087910e4b 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -12,7 +12,7 @@ ) from mypy.nodes import ArgKind from mypy.solve import solve_constraints -from mypy.types import CallableType, Instance, Type, TypeVarId +from mypy.types import CallableType, Instance, Type, TypeVarLikeType class ArgumentInferContext(NamedTuple): @@ -37,7 +37,7 @@ def infer_function_type_arguments( context: ArgumentInferContext, strict: bool = True, allow_polymorphic: bool = False, -) -> list[Type | None]: +) -> tuple[list[Type | None], list[TypeVarLikeType]]: """Infer the type arguments of a generic function. Return an array of lower bound types for the type variables -1 (at @@ -57,14 +57,14 @@ def infer_function_type_arguments( ) # Solve constraints. - type_vars = callee_type.type_var_ids() + type_vars = callee_type.variables return solve_constraints(type_vars, constraints, strict, allow_polymorphic) def infer_type_arguments( - type_var_ids: list[TypeVarId], template: Type, actual: Type, is_supertype: bool = False + type_vars: Sequence[TypeVarLikeType], template: Type, actual: Type, is_supertype: bool = False ) -> list[Type | None]: # Like infer_function_type_arguments, but only match a single type # against a generic type. constraints = infer_constraints(template, actual, SUPERTYPE_OF if is_supertype else SUBTYPE_OF) - return solve_constraints(type_var_ids, constraints) + return solve_constraints(type_vars, constraints)[0] diff --git a/mypy/solve.py b/mypy/solve.py index 6693d66f3479..02df90aff1e1 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Iterable +from collections import defaultdict +from typing import Iterable, Sequence +from typing_extensions import TypeAlias as _TypeAlias -from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, neg_op +from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op from mypy.expandtype import expand_type from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.join import join_types @@ -17,6 +19,7 @@ Type, TypeOfAny, TypeVarId, + TypeVarLikeType, TypeVarType, UninhabitedType, UnionType, @@ -25,45 +28,72 @@ ) from mypy.typestate import type_state +Bounds: _TypeAlias = "dict[TypeVarId, set[Type]]" +Graph: _TypeAlias = "set[tuple[TypeVarId, TypeVarId]]" +Solutions: _TypeAlias = "dict[TypeVarId, Type | None]" + def solve_constraints( - vars: list[TypeVarId], + original_vars: Sequence[TypeVarLikeType], constraints: list[Constraint], strict: bool = True, allow_polymorphic: bool = False, -) -> list[Type | None]: +) -> tuple[list[Type | None], list[TypeVarLikeType]]: """Solve type constraints. - Return the best type(s) for type variables; each type can be None if the value of the variable - could not be solved. + Return the best type(s) for type variables; each type can be None if the value of + the variable could not be solved. If a variable has no constraints, if strict=True then arbitrarily - pick NoneType as the value of the type variable. If strict=False, - pick AnyType. + pick UninhabitedType as the value of the type variable. If strict=False, pick AnyType. + If allow_polymorphic=True, then use the full algorithm that can potentially return + free type variables in solutions (these require special care when applying). Otherwise, + use a simplified algorithm that just solves each type variable individually if possible. """ + vars = [tv.id for tv in original_vars] if not vars: - return [] + return [], [] + + originals = {tv.id: tv for tv in original_vars} + extra_vars: list[TypeVarId] = [] + # Get additional type variables from generic actuals. + for c in constraints: + extra_vars.extend([v.id for v in c.extra_tvars if v.id not in vars + extra_vars]) + originals.update({v.id: v for v in c.extra_tvars if v.id not in originals}) if allow_polymorphic: # Constraints like T :> S and S <: T are semantically the same, but they are # represented differently. Normalize the constraint list w.r.t this equivalence. - constraints = normalize_constraints(constraints, vars) + constraints = normalize_constraints(constraints, vars + extra_vars) # Collect a list of constraints for each type variable. - cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars} + cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars + extra_vars} for con in constraints: - if con.type_var in vars: + if con.type_var in vars + extra_vars: cmap[con.type_var].append(con) if allow_polymorphic: - solutions = solve_non_linear(vars, constraints, cmap) + if constraints: + solutions, free_vars = solve_with_dependent( + vars + extra_vars, constraints, vars, originals + ) + else: + solutions = {} + free_vars = [] else: solutions = {} + free_vars = [] for tv, cs in cmap.items(): if not cs: continue lowers = [c.target for c in cs if c.op == SUPERTYPE_OF] uppers = [c.target for c in cs if c.op == SUBTYPE_OF] - solutions[tv] = solve_one(lowers, uppers, []) + solution = solve_one(lowers, uppers) + + # Do not leak type variables in non-polymorphic solutions. + if solution is None or not get_vars( + solution, [tv for tv in extra_vars if tv not in vars] + ): + solutions[tv] = solution res: list[Type | None] = [] for v in vars: @@ -78,129 +108,128 @@ def solve_constraints( else: candidate = AnyType(TypeOfAny.special_form) res.append(candidate) - return res + return res, [originals[tv] for tv in free_vars] -def solve_non_linear( - vars: list[TypeVarId], constraints: list[Constraint], cmap: dict[TypeVarId, list[Constraint]] -) -> dict[TypeVarId, Type | None]: - """Solve set of constraints that may include non-linear ones, like T <: List[S]. +def solve_with_dependent( + vars: list[TypeVarId], + constraints: list[Constraint], + original_vars: list[TypeVarId], + originals: dict[TypeVarId, TypeVarLikeType], +) -> tuple[Solutions, list[TypeVarId]]: + """Solve set of constraints that may depend on each other, like T <: List[S]. The whole algorithm consists of five steps: - * Propagate via linear constraints to get all possible constraints for each variable + * Propagate via linear constraints and use secondary constraints to get transitive closure * Find dependencies between type variables, group them in SCCs, and sort topologically - * Check all SCC are intrinsically linear, we can't solve (express) T <: List[T] + * Check that all SCC are intrinsically linear, we can't solve (express) T <: List[T] * Variables in leaf SCCs that don't have constant bounds are free (choose one per SCC) - * Solve constraints iteratively starting from leafs, updating targets after each step. + * Solve constraints iteratively starting from leafs, updating bounds after each step. """ - extra_constraints = [] - for tvar in vars: - extra_constraints.extend(propagate_constraints_for(tvar, SUBTYPE_OF, cmap)) - extra_constraints.extend(propagate_constraints_for(tvar, SUPERTYPE_OF, cmap)) - constraints += remove_dups(extra_constraints) - - # Recompute constraint map after propagating. - cmap = {tv: [] for tv in vars} - for con in constraints: - if con.type_var in vars: - cmap[con.type_var].append(con) + graph, lowers, uppers = transitive_closure(vars, constraints) - dmap = compute_dependencies(cmap) + dmap = compute_dependencies(vars, graph, lowers, uppers) sccs = list(strongly_connected_components(set(vars), dmap)) - if all(check_linear(scc, cmap) for scc in sccs): - raw_batches = list(topsort(prepare_sccs(sccs, dmap))) - leafs = raw_batches[0] - free_vars = [] - for scc in leafs: - # If all constrain targets in this SCC are type variables within the - # same SCC then the only meaningful solution we can express, is that - # each variable is equal to a new free variable. For example if we - # have T <: S, S <: U, we deduce: T = S = U = . - if all( - isinstance(c.target, TypeVarType) and c.target.id in vars - for tv in scc - for c in cmap[tv] - ): - # For convenience with current type application machinery, we randomly - # choose one of the existing type variables in SCC and designate it as free - # instead of defining a new type variable as a common solution. - # TODO: be careful about upper bounds (or values) when introducing free vars. - free_vars.append(sorted(scc, key=lambda x: x.raw_id)[0]) - - # Flatten the SCCs that are independent, we can solve them together, - # since we don't need to update any targets in between. - batches = [] - for batch in raw_batches: - next_bc = [] - for scc in batch: - next_bc.extend(list(scc)) - batches.append(next_bc) - - solutions: dict[TypeVarId, Type | None] = {} - for flat_batch in batches: - solutions.update(solve_iteratively(flat_batch, cmap, free_vars)) - # We remove the solutions like T = T for free variables. This will indicate - # to the apply function, that they should not be touched. - # TODO: return list of free type variables explicitly, this logic is fragile - # (but if we do, we need to be careful everything works in incremental modes). - for tv in free_vars: - if tv in solutions: - del solutions[tv] - return solutions - return {} + if not all(check_linear(scc, lowers, uppers) for scc in sccs): + return {}, [] + raw_batches = list(topsort(prepare_sccs(sccs, dmap))) + + free_vars = [] + for scc in raw_batches[0]: + # If there are no bounds on this SCC, then the only meaningful solution we can + # express, is that each variable is equal to a new free variable. For example, + # if we have T <: S, S <: U, we deduce: T = S = U = . + if all(not lowers[tv] and not uppers[tv] for tv in scc): + # For convenience with current type application machinery, we use a stable + # choice that prefers the original type variables (not polymorphic ones) in SCC. + # TODO: be careful about upper bounds (or values) when introducing free vars. + free_vars.append(sorted(scc, key=lambda x: (x not in original_vars, x.raw_id))[0]) + + # Update lowers/uppers with free vars, so these can now be used + # as valid solutions. + for l, u in graph.copy(): + if l in free_vars: + lowers[u].add(originals[l]) + if u in free_vars: + uppers[l].add(originals[u]) + + # Flatten the SCCs that are independent, we can solve them together, + # since we don't need to update any targets in between. + batches = [] + for batch in raw_batches: + next_bc = [] + for scc in batch: + next_bc.extend(list(scc)) + batches.append(next_bc) + + solutions: dict[TypeVarId, Type | None] = {} + for flat_batch in batches: + res = solve_iteratively(flat_batch, graph, lowers, uppers) + solutions.update(res) + return solutions, free_vars def solve_iteratively( - batch: list[TypeVarId], cmap: dict[TypeVarId, list[Constraint]], free_vars: list[TypeVarId] -) -> dict[TypeVarId, Type | None]: - """Solve constraints sequentially, updating constraint targets after each step. - - We solve for type variables that appear in `batch`. If a constraint target is not constant - (i.e. constraint looks like T :> F[S, ...]), we substitute solutions found so far in - the target F[S, ...]. This way we can gradually solve for all variables in the batch taking - one solvable variable at a time (i.e. such a variable that has at least one constant bound). - - Importantly, variables in free_vars are considered constants, so for example if we have just - one initial constraint T <: List[S], we will have two SCCs {T} and {S}, then we first - designate S as free, and therefore T = List[S] is a valid solution for T. + batch: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds +) -> Solutions: + """Solve transitive closure sequentially, updating upper/lower bounds after each step. + + Transitive closure is represented as a linear graph plus lower/upper bounds for each + type variable, see transitive_closure() docstring for details. + + We solve for type variables that appear in `batch`. If a bound is not constant (i.e. it + looks like T :> F[S, ...]), we substitute solutions found so far in the target F[S, ...] + after solving the batch. + + Importantly, after solving each variable in a batch, we move it from linear graph to + upper/lower bounds, this way we can guarantee consistency of solutions (see comment below + for an example when this is important). """ solutions = {} - relevant_constraints = [] - for tv in batch: - relevant_constraints.extend(cmap.get(tv, [])) - lowers, uppers = transitive_closure(batch, relevant_constraints) s_batch = set(batch) - not_allowed_vars = [v for v in batch if v not in free_vars] while s_batch: - for tv in s_batch: - if any(not get_vars(l, not_allowed_vars) for l in lowers[tv]) or any( - not get_vars(u, not_allowed_vars) for u in uppers[tv] - ): + for tv in sorted(s_batch, key=lambda x: x.raw_id): + if lowers[tv] or uppers[tv]: solvable_tv = tv break else: break # Solve each solvable type variable separately. s_batch.remove(solvable_tv) - result = solve_one(lowers[solvable_tv], uppers[solvable_tv], not_allowed_vars) + result = solve_one(lowers[solvable_tv], uppers[solvable_tv]) solutions[solvable_tv] = result if result is None: - # TODO: support backtracking lower/upper bound choices + # TODO: support backtracking lower/upper bound choices and order within SCCs. # (will require switching this function from iterative to recursive). continue - # Update the (transitive) constraints if there is a solution. - subs = {solvable_tv: result} - lowers = {tv: {expand_type(l, subs) for l in lowers[tv]} for tv in lowers} - uppers = {tv: {expand_type(u, subs) for u in uppers[tv]} for tv in uppers} - for v in cmap: - for c in cmap[v]: - c.target = expand_type(c.target, subs) + + # Update the (transitive) bounds from graph if there is a solution. + # This is needed to guarantee solutions will never contradict the initial + # constraints. For example, consider {T <: S, T <: A, S :> B} with A :> B. + # If we would not update the uppers/lowers from graph, we would infer T = A, S = B + # which is not correct. + for l, u in graph.copy(): + if l == u: + continue + if l == solvable_tv: + lowers[u].add(result) + graph.remove((l, u)) + if u == solvable_tv: + uppers[l].add(result) + graph.remove((l, u)) + + # We can update uppers/lowers only once after solving the whole SCC, + # since uppers/lowers can't depend on type variables in the SCC + # (and we would reject such SCC as non-linear and therefore not solvable). + subs = {tv: s for (tv, s) in solutions.items() if s is not None} + for tv in lowers: + lowers[tv] = {expand_type(lt, subs) for lt in lowers[tv]} + for tv in uppers: + uppers[tv] = {expand_type(ut, subs) for ut in uppers[tv]} return solutions -def solve_one( - lowers: Iterable[Type], uppers: Iterable[Type], not_allowed_vars: list[TypeVarId] -) -> Type | None: +def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None: """Solve constraints by finding by using meets of upper bounds, and joins of lower bounds.""" bottom: Type | None = None top: Type | None = None @@ -210,10 +239,6 @@ def solve_one( # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint references. for target in lowers: - # There may be multiple steps needed to solve all vars within a - # (linear) SCC. We ignore targets pointing to not yet solved vars. - if get_vars(target, not_allowed_vars): - continue if bottom is None: bottom = target else: @@ -225,9 +250,6 @@ def solve_one( bottom = join_types(bottom, target) for target in uppers: - # Same as above. - if get_vars(target, not_allowed_vars): - continue if top is None: top = target else: @@ -262,6 +284,7 @@ def normalize_constraints( This includes two things currently: * Complement T :> S by S <: T * Remove strict duplicates + * Remove constrains for unrelated variables """ res = constraints.copy() for c in constraints: @@ -270,96 +293,81 @@ def normalize_constraints( return [c for c in remove_dups(constraints) if c.type_var in vars] -def propagate_constraints_for( - var: TypeVarId, direction: int, cmap: dict[TypeVarId, list[Constraint]] -) -> list[Constraint]: - """Propagate via linear constraints to get additional constraints for `var`. - - For example if we have constraints: - [T <: int, S <: T, S :> str] - we can add two more - [S <: int, T :> str] - """ - extra_constraints = [] - seen = set() - front = [var] - if cmap[var]: - var_def = cmap[var][0].origin_type_var - else: - return [] - while front: - tv = front.pop(0) - for c in cmap[tv]: - if ( - isinstance(c.target, TypeVarType) - and c.target.id not in seen - and c.target.id in cmap - and c.op == direction - ): - front.append(c.target.id) - seen.add(c.target.id) - elif c.op == direction: - new_c = Constraint(var_def, direction, c.target) - if new_c not in cmap[var]: - extra_constraints.append(new_c) - return extra_constraints - - def transitive_closure( tvars: list[TypeVarId], constraints: list[Constraint] -) -> tuple[dict[TypeVarId, set[Type]], dict[TypeVarId, set[Type]]]: +) -> tuple[Graph, Bounds, Bounds]: """Find transitive closure for given constraints on type variables. Transitive closure gives maximal set of lower/upper bounds for each type variable, such that we cannot deduce any further bounds by chaining other existing bounds. + The transitive closure is represented by: + * A set of lower and upper bounds for each type variable, where only constant and + non-linear terms are included in the bounds. + * A graph of linear constraints between type variables (represented as a set of pairs) + Such separation simplifies reasoning, and allows an efficient and simple incremental + transitive closure algorithm that we use here. + For example if we have initial constraints [T <: S, S <: U, U <: int], the transitive closure is given by: - * {} <: T <: {S, U, int} - * {T} <: S <: {U, int} - * {T, S} <: U <: {int} + * {} <: T <: {int} + * {} <: S <: {int} + * {} <: U <: {int} + * {T <: S, S <: U, T <: U} """ - # TODO: merge propagate_constraints_for() into this function. - # TODO: add secondary constraints here to make the algorithm complete. - uppers: dict[TypeVarId, set[Type]] = {tv: set() for tv in tvars} - lowers: dict[TypeVarId, set[Type]] = {tv: set() for tv in tvars} - graph: set[tuple[TypeVarId, TypeVarId]] = set() + uppers: Bounds = defaultdict(set) + lowers: Bounds = defaultdict(set) + graph: Graph = {(tv, tv) for tv in tvars} - # Prime the closure with the initial trivial values. - for c in constraints: - if isinstance(c.target, TypeVarType) and c.target.id in tvars: - if c.op == SUBTYPE_OF: - graph.add((c.type_var, c.target.id)) - else: - graph.add((c.target.id, c.type_var)) - if c.op == SUBTYPE_OF: - uppers[c.type_var].add(c.target) - else: - lowers[c.type_var].add(c.target) - - # At this stage we know that constant bounds have been propagated already, so we - # only need to propagate linear constraints. - for c in constraints: + remaining = set(constraints) + while remaining: + c = remaining.pop() if isinstance(c.target, TypeVarType) and c.target.id in tvars: if c.op == SUBTYPE_OF: lower, upper = c.type_var, c.target.id else: lower, upper = c.target.id, c.type_var - extras = { + if (lower, upper) in graph: + continue + graph |= { (l, u) for l in tvars for u in tvars if (l, lower) in graph and (upper, u) in graph } - graph |= extras for u in tvars: if (upper, u) in graph: lowers[u] |= lowers[lower] for l in tvars: if (l, lower) in graph: uppers[l] |= uppers[upper] - return lowers, uppers + for lt in lowers[lower]: + for ut in uppers[upper]: + # TODO: what if secondary constraints result in inference + # against polymorphic actual (also in below branches)? + remaining |= set(infer_constraints(lt, ut, SUBTYPE_OF)) + remaining |= set(infer_constraints(ut, lt, SUPERTYPE_OF)) + elif c.op == SUBTYPE_OF: + if c.target in uppers[c.type_var]: + continue + for l in tvars: + if (l, c.type_var) in graph: + uppers[l].add(c.target) + for lt in lowers[c.type_var]: + remaining |= set(infer_constraints(lt, c.target, SUBTYPE_OF)) + remaining |= set(infer_constraints(c.target, lt, SUPERTYPE_OF)) + else: + assert c.op == SUPERTYPE_OF + if c.target in lowers[c.type_var]: + continue + for u in tvars: + if (c.type_var, u) in graph: + lowers[u].add(c.target) + for ut in uppers[c.type_var]: + remaining |= set(infer_constraints(ut, c.target, SUPERTYPE_OF)) + remaining |= set(infer_constraints(c.target, ut, SUBTYPE_OF)) + return graph, lowers, uppers def compute_dependencies( - cmap: dict[TypeVarId, list[Constraint]] + tvars: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds ) -> dict[TypeVarId, list[TypeVarId]]: """Compute dependencies between type variables induced by constraints. @@ -367,25 +375,30 @@ def compute_dependencies( we will need to solve for S first before we can solve for T. """ res = {} - vars = list(cmap.keys()) - for tv in cmap: + for tv in tvars: deps = set() - for c in cmap[tv]: - deps |= get_vars(c.target, vars) + for lt in lowers[tv]: + deps |= get_vars(lt, tvars) + for ut in uppers[tv]: + deps |= get_vars(ut, tvars) + for other in tvars: + if other == tv: + continue + if (tv, other) in graph or (other, tv) in graph: + deps.add(other) res[tv] = list(deps) return res -def check_linear(scc: set[TypeVarId], cmap: dict[TypeVarId, list[Constraint]]) -> bool: +def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool: """Check there are only linear constraints between type variables in SCC. Linear are constraints like T <: S (while T <: F[S] are non-linear). """ for tv in scc: - if any( - get_vars(c.target, list(scc)) and not isinstance(c.target, TypeVarType) - for c in cmap[tv] - ): + if any(get_vars(lt, list(scc)) for lt in lowers[tv]): + return False + if any(get_vars(ut, list(scc)) for ut in uppers[tv]): return False return True diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a6dc071f92b0..5712d7375e50 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1708,8 +1708,7 @@ def unify_generic_callable( type.ret_type, target.ret_type, return_constraint_direction ) constraints.extend(c) - type_var_ids = [tvar.id for tvar in type.variables] - inferred_vars = mypy.solve.solve_constraints(type_var_ids, constraints) + inferred_vars, _ = mypy.solve.solve_constraints(type.variables, constraints) if None in inferred_vars: return None non_none_inferred_vars = cast(List[Type], inferred_vars) diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index b46f31327150..f40996145cba 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -1,7 +1,5 @@ from __future__ import annotations -import pytest - from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints from mypy.test.helpers import Suite from mypy.test.typefixture import TypeFixture @@ -22,7 +20,6 @@ def test_basic_type_variable(self) -> None: Constraint(type_var=fx.t, op=direction, target=fx.a) ] - @pytest.mark.xfail def test_basic_type_var_tuple_subtype(self) -> None: fx = self.fx assert infer_constraints( diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py index d6c585ef4aaa..5d67203dbbf5 100644 --- a/mypy/test/testsolve.py +++ b/mypy/test/testsolve.py @@ -6,7 +6,7 @@ from mypy.solve import solve_constraints from mypy.test.helpers import Suite, assert_equal from mypy.test.typefixture import TypeFixture -from mypy.types import Type, TypeVarId, TypeVarType +from mypy.types import Type, TypeVarLikeType, TypeVarType class SolveSuite(Suite): @@ -17,26 +17,24 @@ def test_empty_input(self) -> None: self.assert_solve([], [], []) def test_simple_supertype_constraints(self) -> None: + self.assert_solve([self.fx.t], [self.supc(self.fx.t, self.fx.a)], [(self.fx.a, self.fx.o)]) self.assert_solve( - [self.fx.t.id], [self.supc(self.fx.t, self.fx.a)], [(self.fx.a, self.fx.o)] - ) - self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.a), self.supc(self.fx.t, self.fx.b)], [(self.fx.a, self.fx.o)], ) def test_simple_subtype_constraints(self) -> None: - self.assert_solve([self.fx.t.id], [self.subc(self.fx.t, self.fx.a)], [self.fx.a]) + self.assert_solve([self.fx.t], [self.subc(self.fx.t, self.fx.a)], [self.fx.a]) self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.subc(self.fx.t, self.fx.a), self.subc(self.fx.t, self.fx.b)], [self.fx.b], ) def test_both_kinds_of_constraints(self) -> None: self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.b), self.subc(self.fx.t, self.fx.a)], [(self.fx.b, self.fx.a)], ) @@ -44,21 +42,19 @@ def test_both_kinds_of_constraints(self) -> None: def test_unsatisfiable_constraints(self) -> None: # The constraints are impossible to satisfy. self.assert_solve( - [self.fx.t.id], - [self.supc(self.fx.t, self.fx.a), self.subc(self.fx.t, self.fx.b)], - [None], + [self.fx.t], [self.supc(self.fx.t, self.fx.a), self.subc(self.fx.t, self.fx.b)], [None] ) def test_exactly_specified_result(self) -> None: self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.b), self.subc(self.fx.t, self.fx.b)], [(self.fx.b, self.fx.b)], ) def test_multiple_variables(self) -> None: self.assert_solve( - [self.fx.t.id, self.fx.s.id], + [self.fx.t, self.fx.s], [ self.supc(self.fx.t, self.fx.b), self.supc(self.fx.s, self.fx.c), @@ -68,40 +64,38 @@ def test_multiple_variables(self) -> None: ) def test_no_constraints_for_var(self) -> None: - self.assert_solve([self.fx.t.id], [], [self.fx.uninhabited]) - self.assert_solve( - [self.fx.t.id, self.fx.s.id], [], [self.fx.uninhabited, self.fx.uninhabited] - ) + self.assert_solve([self.fx.t], [], [self.fx.uninhabited]) + self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited]) self.assert_solve( - [self.fx.t.id, self.fx.s.id], + [self.fx.t, self.fx.s], [self.supc(self.fx.s, self.fx.a)], [self.fx.uninhabited, (self.fx.a, self.fx.o)], ) def test_simple_constraints_with_dynamic_type(self) -> None: self.assert_solve( - [self.fx.t.id], [self.supc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] + [self.fx.t], [self.supc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] ) self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.supc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)], ) self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.supc(self.fx.t, self.fx.a)], [(self.fx.anyt, self.fx.anyt)], ) self.assert_solve( - [self.fx.t.id], [self.subc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] + [self.fx.t], [self.subc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] ) self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.subc(self.fx.t, self.fx.anyt), self.subc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)], ) - # self.assert_solve([self.fx.t.id], + # self.assert_solve([self.fx.t], # [self.subc(self.fx.t, self.fx.anyt), # self.subc(self.fx.t, self.fx.a)], # [(self.fx.anyt, self.fx.anyt)]) @@ -111,20 +105,20 @@ def test_both_normal_and_any_types_in_results(self) -> None: # If one of the bounds is any, we promote the other bound to # any as well, since otherwise the type range does not make sense. self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.a), self.subc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)], ) self.assert_solve( - [self.fx.t.id], + [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.subc(self.fx.t, self.fx.a)], [(self.fx.anyt, self.fx.anyt)], ) def assert_solve( self, - vars: list[TypeVarId], + vars: list[TypeVarLikeType], constraints: list[Constraint], results: list[None | Type | tuple[Type, Type]], ) -> None: @@ -134,7 +128,7 @@ def assert_solve( res.append(r[0]) else: res.append(r) - actual = solve_constraints(vars, constraints) + actual, _ = solve_constraints(vars, constraints) assert_equal(str(actual), str(res)) def supc(self, type_var: TypeVarType, bound: Type) -> Constraint: diff --git a/mypy/typeops.py b/mypy/typeops.py index 519d3de995f5..65ab4340403c 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -313,7 +313,9 @@ class B(A): pass original_type = get_proper_type(original_type) all_ids = func.type_var_ids() - typeargs = infer_type_arguments(all_ids, self_param_type, original_type, is_supertype=True) + typeargs = infer_type_arguments( + func.variables, self_param_type, original_type, is_supertype=True + ) if ( is_classmethod # TODO: why do we need the extra guards here? @@ -322,7 +324,7 @@ class B(A): pass ): # In case we call a classmethod through an instance x, fallback to type(x) typeargs = infer_type_arguments( - all_ids, self_param_type, TypeType(original_type), is_supertype=True + func.variables, self_param_type, TypeType(original_type), is_supertype=True ) ids = [tid for tid in all_ids if any(tid == t.id for t in get_type_vars(self_param_type))] diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 7413e6407d4f..fcdbe641d6d6 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -8,6 +8,7 @@ always_false = MYPYC plugins = misc/proper_plugin.py python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ +new_type_inference = True enable_error_code = ignore-without-code,redundant-expr show_error_code_links = True diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 34588bfceb3d..5c510a11b970 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2911,9 +2911,23 @@ def test1(x: V) -> V: ... def test2(x: V, y: V) -> V: ... reveal_type(dec1(test1)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" -# TODO: support this situation -reveal_type(dec2(test2)) # N: Revealed type is "def (builtins.object) -> def (builtins.object) -> builtins.object" -[builtins fixtures/paramspec.pyi] +reveal_type(dec2(test2)) # N: Revealed type is "def [T] (T`3) -> def (T`3) -> T`3" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableNewVariable] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[S], T]) -> Callable[[S], T]: + ... +def test(x: List[U]) -> List[U]: + ... +reveal_type(dec(test)) # N: Revealed type is "def [U] (builtins.list[U`-1]) -> builtins.list[U`-1]" +[builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericAlias] # flags: --new-type-inference diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 89e5aea210b4..50acd7d77c8c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6554,3 +6554,18 @@ class Snafu(object): reveal_type(Snafu().snafu('123')) # N: Revealed type is "builtins.str" reveal_type(Snafu.snafu('123')) # N: Revealed type is "builtins.str" [builtins fixtures/staticmethod.pyi] + +[case testOverloadedWithInternalTypeVars] +# flags: --new-type-inference +import m + +[file m.pyi] +from typing import Callable, TypeVar, overload + +T = TypeVar("T") +S = TypeVar("S", bound=str) + +@overload +def foo(x: int = ...) -> Callable[[T], T]: ... +@overload +def foo(x: S = ...) -> Callable[[T], T]: ... From 5617cdd03d12ff73622c8d4b496979e0377b1675 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 4 Aug 2023 16:39:11 +0200 Subject: [PATCH 0171/1617] Update black pre-commit mirror link (#15815) The black pre-commit mirror is now hosted at: https://github.com/psf/black-pre-commit-mirror --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ee89cbb912f..f2367f63bb3d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer - - repo: https://github.com/hauntsaninja/black-pre-commit-mirror + - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.7.0 # must match test-requirements.txt hooks: - id: black From 2aaeda4b84a863004a6694a7d562462fbe531ece Mon Sep 17 00:00:00 2001 From: EXPLOSION Date: Wed, 9 Aug 2023 15:17:13 +0900 Subject: [PATCH 0172/1617] Reconsider constraints involving parameter specifications (#15272) - Fixes https://github.com/python/mypy/issues/15037 - Fixes https://github.com/python/mypy/issues/15065 - Fixes https://github.com/python/mypy/issues/15073 - Fixes https://github.com/python/mypy/issues/15388 - Fixes https://github.com/python/mypy/issues/15086 Yet another part of https://github.com/python/mypy/pull/14903 that's finally been extracted! --- mypy/constraints.py | 129 ++++++++++++++---- mypy/test/testconstraints.py | 62 +++++++++ mypy/test/typefixture.py | 42 ++++++ .../unit/check-parameter-specification.test | 32 ++++- 4 files changed, 241 insertions(+), 24 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 299c6292a259..9c55b56dd70e 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -82,15 +82,19 @@ def __repr__(self) -> str: op_str = "<:" if self.op == SUPERTYPE_OF: op_str = ":>" - return f"{self.type_var} {op_str} {self.target}" + return f"{self.origin_type_var} {op_str} {self.target}" def __hash__(self) -> int: - return hash((self.type_var, self.op, self.target)) + return hash((self.origin_type_var, self.op, self.target)) def __eq__(self, other: object) -> bool: if not isinstance(other, Constraint): return False - return (self.type_var, self.op, self.target) == (other.type_var, other.op, other.target) + return (self.origin_type_var, self.op, self.target) == ( + other.origin_type_var, + other.op, + other.target, + ) def infer_constraints_for_callable( @@ -698,25 +702,54 @@ def visit_instance(self, template: Instance) -> list[Constraint]: ) elif isinstance(tvar, ParamSpecType) and isinstance(mapped_arg, ParamSpecType): suffix = get_proper_type(instance_arg) + prefix = mapped_arg.prefix + length = len(prefix.arg_types) if isinstance(suffix, CallableType): - prefix = mapped_arg.prefix from_concat = bool(prefix.arg_types) or suffix.from_concatenate suffix = suffix.copy_modified(from_concatenate=from_concat) if isinstance(suffix, (Parameters, CallableType)): # no such thing as variance for ParamSpecs # TODO: is there a case I am missing? - # TODO: constraints between prefixes - prefix = mapped_arg.prefix - suffix = suffix.copy_modified( - suffix.arg_types[len(prefix.arg_types) :], - suffix.arg_kinds[len(prefix.arg_kinds) :], - suffix.arg_names[len(prefix.arg_names) :], + length = min(length, len(suffix.arg_types)) + + constrained_to = suffix.copy_modified( + suffix.arg_types[length:], + suffix.arg_kinds[length:], + suffix.arg_names[length:], + ) + constrained_from = mapped_arg.copy_modified( + prefix=prefix.copy_modified( + prefix.arg_types[length:], + prefix.arg_kinds[length:], + prefix.arg_names[length:], + ) ) - res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) + + res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained_to)) + res.append(Constraint(constrained_from, SUBTYPE_OF, constrained_to)) elif isinstance(suffix, ParamSpecType): - res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) + suffix_prefix = suffix.prefix + length = min(length, len(suffix_prefix.arg_types)) + + constrained = suffix.copy_modified( + prefix=suffix_prefix.copy_modified( + suffix_prefix.arg_types[length:], + suffix_prefix.arg_kinds[length:], + suffix_prefix.arg_names[length:], + ) + ) + constrained_from = mapped_arg.copy_modified( + prefix=prefix.copy_modified( + prefix.arg_types[length:], + prefix.arg_kinds[length:], + prefix.arg_names[length:], + ) + ) + + res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained)) + res.append(Constraint(constrained_from, SUBTYPE_OF, constrained)) else: # This case should have been handled above. assert not isinstance(tvar, TypeVarTupleType) @@ -768,26 +801,56 @@ def visit_instance(self, template: Instance) -> list[Constraint]: template_arg, ParamSpecType ): suffix = get_proper_type(mapped_arg) + prefix = template_arg.prefix + length = len(prefix.arg_types) if isinstance(suffix, CallableType): prefix = template_arg.prefix from_concat = bool(prefix.arg_types) or suffix.from_concatenate suffix = suffix.copy_modified(from_concatenate=from_concat) + # TODO: this is almost a copy-paste of code above: make this into a function if isinstance(suffix, (Parameters, CallableType)): # no such thing as variance for ParamSpecs # TODO: is there a case I am missing? - # TODO: constraints between prefixes - prefix = template_arg.prefix + length = min(length, len(suffix.arg_types)) - suffix = suffix.copy_modified( - suffix.arg_types[len(prefix.arg_types) :], - suffix.arg_kinds[len(prefix.arg_kinds) :], - suffix.arg_names[len(prefix.arg_names) :], + constrained_to = suffix.copy_modified( + suffix.arg_types[length:], + suffix.arg_kinds[length:], + suffix.arg_names[length:], ) - res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) + constrained_from = template_arg.copy_modified( + prefix=prefix.copy_modified( + prefix.arg_types[length:], + prefix.arg_kinds[length:], + prefix.arg_names[length:], + ) + ) + + res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained_to)) + res.append(Constraint(constrained_from, SUBTYPE_OF, constrained_to)) elif isinstance(suffix, ParamSpecType): - res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) + suffix_prefix = suffix.prefix + length = min(length, len(suffix_prefix.arg_types)) + + constrained = suffix.copy_modified( + prefix=suffix_prefix.copy_modified( + suffix_prefix.arg_types[length:], + suffix_prefix.arg_kinds[length:], + suffix_prefix.arg_names[length:], + ) + ) + constrained_from = template_arg.copy_modified( + prefix=prefix.copy_modified( + prefix.arg_types[length:], + prefix.arg_kinds[length:], + prefix.arg_names[length:], + ) + ) + + res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained)) + res.append(Constraint(constrained_from, SUBTYPE_OF, constrained)) else: # This case should have been handled above. assert not isinstance(tvar, TypeVarTupleType) @@ -954,9 +1017,19 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: prefix_len = len(prefix.arg_types) cactual_ps = cactual.param_spec() + cactual_prefix: Parameters | CallableType + if cactual_ps: + cactual_prefix = cactual_ps.prefix + else: + cactual_prefix = cactual + + max_prefix_len = len( + [k for k in cactual_prefix.arg_kinds if k in (ARG_POS, ARG_OPT)] + ) + prefix_len = min(prefix_len, max_prefix_len) + + # we could check the prefixes match here, but that should be caught elsewhere. if not cactual_ps: - max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]) - prefix_len = min(prefix_len, max_prefix_len) res.append( Constraint( param_spec, @@ -970,7 +1043,17 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) ) else: - res.append(Constraint(param_spec, SUBTYPE_OF, cactual_ps)) + # earlier, cactual_prefix = cactual_ps.prefix. thus, this is guaranteed + assert isinstance(cactual_prefix, Parameters) + + constrained_by = cactual_ps.copy_modified( + prefix=cactual_prefix.copy_modified( + cactual_prefix.arg_types[prefix_len:], + cactual_prefix.arg_kinds[prefix_len:], + cactual_prefix.arg_names[prefix_len:], + ) + ) + res.append(Constraint(param_spec, SUBTYPE_OF, constrained_by)) # compare prefixes cactual_prefix = cactual.copy_modified( diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index f40996145cba..be1d435f9cca 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -156,3 +156,65 @@ def test_var_length_tuple_with_fixed_length_tuple(self) -> None: Instance(fx.std_tuplei, [fx.a]), SUPERTYPE_OF, ) + + def test_paramspec_constrained_with_concatenate(self) -> None: + # for legibility (and my own understanding), `Tester.normal()` is `Tester[P]` + # and `Tester.concatenate()` is `Tester[Concatenate[A, P]]` + # ... and 2nd arg to infer_constraints ends up on LHS of equality + fx = self.fx + + # I don't think we can parametrize... + for direction in (SUPERTYPE_OF, SUBTYPE_OF): + print(f"direction is {direction}") + # equiv to: x: Tester[Q] = Tester.normal() + assert set( + infer_constraints(Instance(fx.gpsi, [fx.p]), Instance(fx.gpsi, [fx.q]), direction) + ) == { + Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), + Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), + } + + # equiv to: x: Tester[Q] = Tester.concatenate() + assert set( + infer_constraints( + Instance(fx.gpsi, [fx.p_concatenate]), Instance(fx.gpsi, [fx.q]), direction + ) + ) == { + Constraint(type_var=fx.p_concatenate, op=SUPERTYPE_OF, target=fx.q), + Constraint(type_var=fx.p_concatenate, op=SUBTYPE_OF, target=fx.q), + } + + # equiv to: x: Tester[Concatenate[B, Q]] = Tester.normal() + assert set( + infer_constraints( + Instance(fx.gpsi, [fx.p]), Instance(fx.gpsi, [fx.q_concatenate]), direction + ) + ) == { + Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q_concatenate), + Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q_concatenate), + } + + # equiv to: x: Tester[Concatenate[B, Q]] = Tester.concatenate() + assert set( + infer_constraints( + Instance(fx.gpsi, [fx.p_concatenate]), + Instance(fx.gpsi, [fx.q_concatenate]), + direction, + ) + ) == { + # this is correct as we assume other parts of mypy will warn that [B] != [A] + Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), + Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), + } + + # equiv to: x: Tester[Concatenate[A, Q]] = Tester.concatenate() + assert set( + infer_constraints( + Instance(fx.gpsi, [fx.p_concatenate]), + Instance(fx.gpsi, [fx.q_concatenate]), + direction, + ) + ) == { + Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), + Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), + } diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index bf1500a3cdec..df78eeb62956 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -5,6 +5,8 @@ from __future__ import annotations +from typing import Sequence + from mypy.nodes import ( ARG_OPT, ARG_POS, @@ -26,6 +28,9 @@ Instance, LiteralType, NoneType, + Parameters, + ParamSpecFlavor, + ParamSpecType, Type, TypeAliasType, TypeOfAny, @@ -238,6 +243,31 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy "GV2", mro=[self.oi], typevars=["T", "Ts", "S"], typevar_tuple_index=1 ) + def make_parameter_specification( + name: str, id: int, concatenate: Sequence[Type] + ) -> ParamSpecType: + return ParamSpecType( + name, + name, + id, + ParamSpecFlavor.BARE, + self.o, + AnyType(TypeOfAny.from_omitted_generics), + prefix=Parameters( + concatenate, [ARG_POS for _ in concatenate], [None for _ in concatenate] + ), + ) + + self.p = make_parameter_specification("P", 1, []) + self.p_concatenate = make_parameter_specification("P", 1, [self.a]) + self.q = make_parameter_specification("Q", 2, []) + self.q_concatenate = make_parameter_specification("Q", 2, [self.b]) + self.q_concatenate_a = make_parameter_specification("Q", 2, [self.a]) + + self.gpsi = self.make_type_info( + "GPS", mro=[self.oi], typevars=["P"], paramspec_indexes={0} + ) + def _add_bool_dunder(self, type_info: TypeInfo) -> None: signature = CallableType([], [], [], Instance(self.bool_type_info, []), self.function) bool_func = FuncDef("__bool__", [], Block([])) @@ -299,6 +329,7 @@ def make_type_info( bases: list[Instance] | None = None, typevars: list[str] | None = None, typevar_tuple_index: int | None = None, + paramspec_indexes: set[int] | None = None, variances: list[int] | None = None, ) -> TypeInfo: """Make a TypeInfo suitable for use in unit tests.""" @@ -326,6 +357,17 @@ def make_type_info( AnyType(TypeOfAny.from_omitted_generics), ) ) + elif paramspec_indexes is not None and id - 1 in paramspec_indexes: + v.append( + ParamSpecType( + n, + n, + id, + ParamSpecFlavor.BARE, + self.o, + AnyType(TypeOfAny.from_omitted_generics), + ) + ) else: if variances: variance = variances[id - 1] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 114fe1f8438a..f11b9aa599ed 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -776,7 +776,7 @@ _P = ParamSpec("_P") class Job(Generic[_P]): def __init__(self, target: Callable[_P, None]) -> None: - self.target = target + ... def func( action: Union[Job[int], Callable[[int], None]], @@ -1535,6 +1535,36 @@ def identity(func: Callable[P, None]) -> Callable[P, None]: ... def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... [builtins fixtures/paramspec.pyi] +[case testComplicatedParamSpecReturnType] +# regression test for https://github.com/python/mypy/issues/15073 +from typing import TypeVar, Callable +from typing_extensions import ParamSpec, Concatenate + +R = TypeVar("R") +P = ParamSpec("P") + +def f( +) -> Callable[[Callable[Concatenate[Callable[P, R], P], R]], Callable[P, R]]: + def r(fn: Callable[Concatenate[Callable[P, R], P], R]) -> Callable[P, R]: ... + return r +[builtins fixtures/paramspec.pyi] + +[case testParamSpecToParamSpecAssignment] +# minimized from https://github.com/python/mypy/issues/15037 +# ~ the same as https://github.com/python/mypy/issues/15065 +from typing import Callable +from typing_extensions import Concatenate, ParamSpec + +P = ParamSpec("P") + +def f(f: Callable[Concatenate[int, P], None]) -> Callable[P, None]: ... + +x: Callable[ + [Callable[Concatenate[int, P], None]], + Callable[P, None], +] = f +[builtins fixtures/paramspec.pyi] + [case testParamSpecDecoratorAppliedToGeneric] # flags: --new-type-inference from typing import Callable, List, TypeVar From a7c48520560c3adf7176b91d16f4d0750ab8dfa9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 9 Aug 2023 08:23:03 +0100 Subject: [PATCH 0173/1617] =?UTF-8?q?Revert=20"Reconsider=20constraints=20?= =?UTF-8?q?involving=20parameter=20specifications=20(#1=E2=80=A6=20(#15832?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …5272)" This reverts commit 2aaeda4b84a863004a6694a7d562462fbe531ece. (Explain how this PR changes mypy.) --- mypy/constraints.py | 129 ++++-------------- mypy/test/testconstraints.py | 62 --------- mypy/test/typefixture.py | 42 ------ .../unit/check-parameter-specification.test | 32 +---- 4 files changed, 24 insertions(+), 241 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 9c55b56dd70e..299c6292a259 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -82,19 +82,15 @@ def __repr__(self) -> str: op_str = "<:" if self.op == SUPERTYPE_OF: op_str = ":>" - return f"{self.origin_type_var} {op_str} {self.target}" + return f"{self.type_var} {op_str} {self.target}" def __hash__(self) -> int: - return hash((self.origin_type_var, self.op, self.target)) + return hash((self.type_var, self.op, self.target)) def __eq__(self, other: object) -> bool: if not isinstance(other, Constraint): return False - return (self.origin_type_var, self.op, self.target) == ( - other.origin_type_var, - other.op, - other.target, - ) + return (self.type_var, self.op, self.target) == (other.type_var, other.op, other.target) def infer_constraints_for_callable( @@ -702,54 +698,25 @@ def visit_instance(self, template: Instance) -> list[Constraint]: ) elif isinstance(tvar, ParamSpecType) and isinstance(mapped_arg, ParamSpecType): suffix = get_proper_type(instance_arg) - prefix = mapped_arg.prefix - length = len(prefix.arg_types) if isinstance(suffix, CallableType): + prefix = mapped_arg.prefix from_concat = bool(prefix.arg_types) or suffix.from_concatenate suffix = suffix.copy_modified(from_concatenate=from_concat) if isinstance(suffix, (Parameters, CallableType)): # no such thing as variance for ParamSpecs # TODO: is there a case I am missing? - length = min(length, len(suffix.arg_types)) - - constrained_to = suffix.copy_modified( - suffix.arg_types[length:], - suffix.arg_kinds[length:], - suffix.arg_names[length:], - ) - constrained_from = mapped_arg.copy_modified( - prefix=prefix.copy_modified( - prefix.arg_types[length:], - prefix.arg_kinds[length:], - prefix.arg_names[length:], - ) + # TODO: constraints between prefixes + prefix = mapped_arg.prefix + suffix = suffix.copy_modified( + suffix.arg_types[len(prefix.arg_types) :], + suffix.arg_kinds[len(prefix.arg_kinds) :], + suffix.arg_names[len(prefix.arg_names) :], ) - - res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained_to)) - res.append(Constraint(constrained_from, SUBTYPE_OF, constrained_to)) + res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) elif isinstance(suffix, ParamSpecType): - suffix_prefix = suffix.prefix - length = min(length, len(suffix_prefix.arg_types)) - - constrained = suffix.copy_modified( - prefix=suffix_prefix.copy_modified( - suffix_prefix.arg_types[length:], - suffix_prefix.arg_kinds[length:], - suffix_prefix.arg_names[length:], - ) - ) - constrained_from = mapped_arg.copy_modified( - prefix=prefix.copy_modified( - prefix.arg_types[length:], - prefix.arg_kinds[length:], - prefix.arg_names[length:], - ) - ) - - res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained)) - res.append(Constraint(constrained_from, SUBTYPE_OF, constrained)) + res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) else: # This case should have been handled above. assert not isinstance(tvar, TypeVarTupleType) @@ -801,56 +768,26 @@ def visit_instance(self, template: Instance) -> list[Constraint]: template_arg, ParamSpecType ): suffix = get_proper_type(mapped_arg) - prefix = template_arg.prefix - length = len(prefix.arg_types) if isinstance(suffix, CallableType): prefix = template_arg.prefix from_concat = bool(prefix.arg_types) or suffix.from_concatenate suffix = suffix.copy_modified(from_concatenate=from_concat) - # TODO: this is almost a copy-paste of code above: make this into a function if isinstance(suffix, (Parameters, CallableType)): # no such thing as variance for ParamSpecs # TODO: is there a case I am missing? - length = min(length, len(suffix.arg_types)) + # TODO: constraints between prefixes + prefix = template_arg.prefix - constrained_to = suffix.copy_modified( - suffix.arg_types[length:], - suffix.arg_kinds[length:], - suffix.arg_names[length:], + suffix = suffix.copy_modified( + suffix.arg_types[len(prefix.arg_types) :], + suffix.arg_kinds[len(prefix.arg_kinds) :], + suffix.arg_names[len(prefix.arg_names) :], ) - constrained_from = template_arg.copy_modified( - prefix=prefix.copy_modified( - prefix.arg_types[length:], - prefix.arg_kinds[length:], - prefix.arg_names[length:], - ) - ) - - res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained_to)) - res.append(Constraint(constrained_from, SUBTYPE_OF, constrained_to)) + res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) elif isinstance(suffix, ParamSpecType): - suffix_prefix = suffix.prefix - length = min(length, len(suffix_prefix.arg_types)) - - constrained = suffix.copy_modified( - prefix=suffix_prefix.copy_modified( - suffix_prefix.arg_types[length:], - suffix_prefix.arg_kinds[length:], - suffix_prefix.arg_names[length:], - ) - ) - constrained_from = template_arg.copy_modified( - prefix=prefix.copy_modified( - prefix.arg_types[length:], - prefix.arg_kinds[length:], - prefix.arg_names[length:], - ) - ) - - res.append(Constraint(constrained_from, SUPERTYPE_OF, constrained)) - res.append(Constraint(constrained_from, SUBTYPE_OF, constrained)) + res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) else: # This case should have been handled above. assert not isinstance(tvar, TypeVarTupleType) @@ -1017,19 +954,9 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: prefix_len = len(prefix.arg_types) cactual_ps = cactual.param_spec() - cactual_prefix: Parameters | CallableType - if cactual_ps: - cactual_prefix = cactual_ps.prefix - else: - cactual_prefix = cactual - - max_prefix_len = len( - [k for k in cactual_prefix.arg_kinds if k in (ARG_POS, ARG_OPT)] - ) - prefix_len = min(prefix_len, max_prefix_len) - - # we could check the prefixes match here, but that should be caught elsewhere. if not cactual_ps: + max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]) + prefix_len = min(prefix_len, max_prefix_len) res.append( Constraint( param_spec, @@ -1043,17 +970,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) ) else: - # earlier, cactual_prefix = cactual_ps.prefix. thus, this is guaranteed - assert isinstance(cactual_prefix, Parameters) - - constrained_by = cactual_ps.copy_modified( - prefix=cactual_prefix.copy_modified( - cactual_prefix.arg_types[prefix_len:], - cactual_prefix.arg_kinds[prefix_len:], - cactual_prefix.arg_names[prefix_len:], - ) - ) - res.append(Constraint(param_spec, SUBTYPE_OF, constrained_by)) + res.append(Constraint(param_spec, SUBTYPE_OF, cactual_ps)) # compare prefixes cactual_prefix = cactual.copy_modified( diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index be1d435f9cca..f40996145cba 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -156,65 +156,3 @@ def test_var_length_tuple_with_fixed_length_tuple(self) -> None: Instance(fx.std_tuplei, [fx.a]), SUPERTYPE_OF, ) - - def test_paramspec_constrained_with_concatenate(self) -> None: - # for legibility (and my own understanding), `Tester.normal()` is `Tester[P]` - # and `Tester.concatenate()` is `Tester[Concatenate[A, P]]` - # ... and 2nd arg to infer_constraints ends up on LHS of equality - fx = self.fx - - # I don't think we can parametrize... - for direction in (SUPERTYPE_OF, SUBTYPE_OF): - print(f"direction is {direction}") - # equiv to: x: Tester[Q] = Tester.normal() - assert set( - infer_constraints(Instance(fx.gpsi, [fx.p]), Instance(fx.gpsi, [fx.q]), direction) - ) == { - Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), - Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), - } - - # equiv to: x: Tester[Q] = Tester.concatenate() - assert set( - infer_constraints( - Instance(fx.gpsi, [fx.p_concatenate]), Instance(fx.gpsi, [fx.q]), direction - ) - ) == { - Constraint(type_var=fx.p_concatenate, op=SUPERTYPE_OF, target=fx.q), - Constraint(type_var=fx.p_concatenate, op=SUBTYPE_OF, target=fx.q), - } - - # equiv to: x: Tester[Concatenate[B, Q]] = Tester.normal() - assert set( - infer_constraints( - Instance(fx.gpsi, [fx.p]), Instance(fx.gpsi, [fx.q_concatenate]), direction - ) - ) == { - Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q_concatenate), - Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q_concatenate), - } - - # equiv to: x: Tester[Concatenate[B, Q]] = Tester.concatenate() - assert set( - infer_constraints( - Instance(fx.gpsi, [fx.p_concatenate]), - Instance(fx.gpsi, [fx.q_concatenate]), - direction, - ) - ) == { - # this is correct as we assume other parts of mypy will warn that [B] != [A] - Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), - Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), - } - - # equiv to: x: Tester[Concatenate[A, Q]] = Tester.concatenate() - assert set( - infer_constraints( - Instance(fx.gpsi, [fx.p_concatenate]), - Instance(fx.gpsi, [fx.q_concatenate]), - direction, - ) - ) == { - Constraint(type_var=fx.p, op=SUPERTYPE_OF, target=fx.q), - Constraint(type_var=fx.p, op=SUBTYPE_OF, target=fx.q), - } diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index df78eeb62956..bf1500a3cdec 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -5,8 +5,6 @@ from __future__ import annotations -from typing import Sequence - from mypy.nodes import ( ARG_OPT, ARG_POS, @@ -28,9 +26,6 @@ Instance, LiteralType, NoneType, - Parameters, - ParamSpecFlavor, - ParamSpecType, Type, TypeAliasType, TypeOfAny, @@ -243,31 +238,6 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy "GV2", mro=[self.oi], typevars=["T", "Ts", "S"], typevar_tuple_index=1 ) - def make_parameter_specification( - name: str, id: int, concatenate: Sequence[Type] - ) -> ParamSpecType: - return ParamSpecType( - name, - name, - id, - ParamSpecFlavor.BARE, - self.o, - AnyType(TypeOfAny.from_omitted_generics), - prefix=Parameters( - concatenate, [ARG_POS for _ in concatenate], [None for _ in concatenate] - ), - ) - - self.p = make_parameter_specification("P", 1, []) - self.p_concatenate = make_parameter_specification("P", 1, [self.a]) - self.q = make_parameter_specification("Q", 2, []) - self.q_concatenate = make_parameter_specification("Q", 2, [self.b]) - self.q_concatenate_a = make_parameter_specification("Q", 2, [self.a]) - - self.gpsi = self.make_type_info( - "GPS", mro=[self.oi], typevars=["P"], paramspec_indexes={0} - ) - def _add_bool_dunder(self, type_info: TypeInfo) -> None: signature = CallableType([], [], [], Instance(self.bool_type_info, []), self.function) bool_func = FuncDef("__bool__", [], Block([])) @@ -329,7 +299,6 @@ def make_type_info( bases: list[Instance] | None = None, typevars: list[str] | None = None, typevar_tuple_index: int | None = None, - paramspec_indexes: set[int] | None = None, variances: list[int] | None = None, ) -> TypeInfo: """Make a TypeInfo suitable for use in unit tests.""" @@ -357,17 +326,6 @@ def make_type_info( AnyType(TypeOfAny.from_omitted_generics), ) ) - elif paramspec_indexes is not None and id - 1 in paramspec_indexes: - v.append( - ParamSpecType( - n, - n, - id, - ParamSpecFlavor.BARE, - self.o, - AnyType(TypeOfAny.from_omitted_generics), - ) - ) else: if variances: variance = variances[id - 1] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f11b9aa599ed..114fe1f8438a 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -776,7 +776,7 @@ _P = ParamSpec("_P") class Job(Generic[_P]): def __init__(self, target: Callable[_P, None]) -> None: - ... + self.target = target def func( action: Union[Job[int], Callable[[int], None]], @@ -1535,36 +1535,6 @@ def identity(func: Callable[P, None]) -> Callable[P, None]: ... def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... [builtins fixtures/paramspec.pyi] -[case testComplicatedParamSpecReturnType] -# regression test for https://github.com/python/mypy/issues/15073 -from typing import TypeVar, Callable -from typing_extensions import ParamSpec, Concatenate - -R = TypeVar("R") -P = ParamSpec("P") - -def f( -) -> Callable[[Callable[Concatenate[Callable[P, R], P], R]], Callable[P, R]]: - def r(fn: Callable[Concatenate[Callable[P, R], P], R]) -> Callable[P, R]: ... - return r -[builtins fixtures/paramspec.pyi] - -[case testParamSpecToParamSpecAssignment] -# minimized from https://github.com/python/mypy/issues/15037 -# ~ the same as https://github.com/python/mypy/issues/15065 -from typing import Callable -from typing_extensions import Concatenate, ParamSpec - -P = ParamSpec("P") - -def f(f: Callable[Concatenate[int, P], None]) -> Callable[P, None]: ... - -x: Callable[ - [Callable[Concatenate[int, P], None]], - Callable[P, None], -] = f -[builtins fixtures/paramspec.pyi] - [case testParamSpecDecoratorAppliedToGeneric] # flags: --new-type-inference from typing import Callable, List, TypeVar From 8c219539380208bf5b8d189aafd4dec10f941f98 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 9 Aug 2023 16:33:38 +0100 Subject: [PATCH 0174/1617] New type inference: add support for upper bounds and values (#15813) This is a third PR in series following https://github.com/python/mypy/pull/15287 and https://github.com/python/mypy/pull/15754. This one is quite simple: I just add basic support for polymorphic inference involving type variables with upper bounds and values. A complete support would be quite complicated, and it will be a corner case to already rare situation. Finally, it is written in a way that is easy to tune in the future. I also use this PR to add some unit tests for all three PRs so far, other two PRs only added integration tests (and I clean up existing unit tests as well). --- mypy/solve.py | 80 +++++++++-- mypy/test/testsolve.py | 205 +++++++++++++++++++++++++---- mypy/test/typefixture.py | 4 + test-data/unit/check-generics.test | 28 ++++ 4 files changed, 277 insertions(+), 40 deletions(-) diff --git a/mypy/solve.py b/mypy/solve.py index 02df90aff1e1..72b3d6f26618 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -10,11 +10,13 @@ from mypy.expandtype import expand_type from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.join import join_types -from mypy.meet import meet_types +from mypy.meet import meet_type_list, meet_types from mypy.subtypes import is_subtype from mypy.typeops import get_type_vars from mypy.types import ( AnyType, + Instance, + NoneType, ProperType, Type, TypeOfAny, @@ -108,7 +110,7 @@ def solve_constraints( else: candidate = AnyType(TypeOfAny.special_form) res.append(candidate) - return res, [originals[tv] for tv in free_vars] + return res, free_vars def solve_with_dependent( @@ -116,7 +118,7 @@ def solve_with_dependent( constraints: list[Constraint], original_vars: list[TypeVarId], originals: dict[TypeVarId, TypeVarLikeType], -) -> tuple[Solutions, list[TypeVarId]]: +) -> tuple[Solutions, list[TypeVarLikeType]]: """Solve set of constraints that may depend on each other, like T <: List[S]. The whole algorithm consists of five steps: @@ -135,23 +137,24 @@ def solve_with_dependent( raw_batches = list(topsort(prepare_sccs(sccs, dmap))) free_vars = [] + free_solutions = {} for scc in raw_batches[0]: # If there are no bounds on this SCC, then the only meaningful solution we can # express, is that each variable is equal to a new free variable. For example, # if we have T <: S, S <: U, we deduce: T = S = U = . if all(not lowers[tv] and not uppers[tv] for tv in scc): - # For convenience with current type application machinery, we use a stable - # choice that prefers the original type variables (not polymorphic ones) in SCC. - # TODO: be careful about upper bounds (or values) when introducing free vars. - free_vars.append(sorted(scc, key=lambda x: (x not in original_vars, x.raw_id))[0]) + best_free = choose_free([originals[tv] for tv in scc], original_vars) + if best_free: + free_vars.append(best_free.id) + free_solutions[best_free.id] = best_free # Update lowers/uppers with free vars, so these can now be used # as valid solutions. - for l, u in graph.copy(): + for l, u in graph: if l in free_vars: - lowers[u].add(originals[l]) + lowers[u].add(free_solutions[l]) if u in free_vars: - uppers[l].add(originals[u]) + uppers[l].add(free_solutions[u]) # Flatten the SCCs that are independent, we can solve them together, # since we don't need to update any targets in between. @@ -166,7 +169,7 @@ def solve_with_dependent( for flat_batch in batches: res = solve_iteratively(flat_batch, graph, lowers, uppers) solutions.update(res) - return solutions, free_vars + return solutions, [free_solutions[tv] for tv in free_vars] def solve_iteratively( @@ -276,6 +279,61 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None: return candidate +def choose_free( + scc: list[TypeVarLikeType], original_vars: list[TypeVarId] +) -> TypeVarLikeType | None: + """Choose the best solution for an SCC containing only type variables. + + This is needed to preserve e.g. the upper bound in a situation like this: + def dec(f: Callable[[T], S]) -> Callable[[T], S]: ... + + @dec + def test(x: U) -> U: ... + + where U <: A. + """ + + if len(scc) == 1: + # Fast path, choice is trivial. + return scc[0] + + common_upper_bound = meet_type_list([t.upper_bound for t in scc]) + common_upper_bound_p = get_proper_type(common_upper_bound) + # We include None for when strict-optional is disabled. + if isinstance(common_upper_bound_p, (UninhabitedType, NoneType)): + # This will cause to infer , which is better than a free TypeVar + # that has an upper bound . + return None + + values: list[Type] = [] + for tv in scc: + if isinstance(tv, TypeVarType) and tv.values: + if values: + # It is too tricky to support multiple TypeVars with values + # within the same SCC. + return None + values = tv.values.copy() + + if values and not is_trivial_bound(common_upper_bound_p): + # If there are both values and upper bound present, we give up, + # since type variables having both are not supported. + return None + + # For convenience with current type application machinery, we use a stable + # choice that prefers the original type variables (not polymorphic ones) in SCC. + best = sorted(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id))[0] + if isinstance(best, TypeVarType): + return best.copy_modified(values=values, upper_bound=common_upper_bound) + if is_trivial_bound(common_upper_bound_p): + # TODO: support more cases for ParamSpecs/TypeVarTuples + return best + return None + + +def is_trivial_bound(tp: ProperType) -> bool: + return isinstance(tp, Instance) and tp.type.fullname == "builtins.object" + + def normalize_constraints( constraints: list[Constraint], vars: list[TypeVarId] ) -> list[Constraint]: diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py index 5d67203dbbf5..6566b03ef5e9 100644 --- a/mypy/test/testsolve.py +++ b/mypy/test/testsolve.py @@ -3,10 +3,10 @@ from __future__ import annotations from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint -from mypy.solve import solve_constraints +from mypy.solve import Bounds, Graph, solve_constraints, transitive_closure from mypy.test.helpers import Suite, assert_equal from mypy.test.typefixture import TypeFixture -from mypy.types import Type, TypeVarLikeType, TypeVarType +from mypy.types import Type, TypeVarId, TypeVarLikeType, TypeVarType class SolveSuite(Suite): @@ -17,11 +17,11 @@ def test_empty_input(self) -> None: self.assert_solve([], [], []) def test_simple_supertype_constraints(self) -> None: - self.assert_solve([self.fx.t], [self.supc(self.fx.t, self.fx.a)], [(self.fx.a, self.fx.o)]) + self.assert_solve([self.fx.t], [self.supc(self.fx.t, self.fx.a)], [self.fx.a]) self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.a), self.supc(self.fx.t, self.fx.b)], - [(self.fx.a, self.fx.o)], + [self.fx.a], ) def test_simple_subtype_constraints(self) -> None: @@ -36,7 +36,7 @@ def test_both_kinds_of_constraints(self) -> None: self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.b), self.subc(self.fx.t, self.fx.a)], - [(self.fx.b, self.fx.a)], + [self.fx.b], ) def test_unsatisfiable_constraints(self) -> None: @@ -49,7 +49,7 @@ def test_exactly_specified_result(self) -> None: self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.b), self.subc(self.fx.t, self.fx.b)], - [(self.fx.b, self.fx.b)], + [self.fx.b], ) def test_multiple_variables(self) -> None: @@ -60,7 +60,7 @@ def test_multiple_variables(self) -> None: self.supc(self.fx.s, self.fx.c), self.subc(self.fx.t, self.fx.a), ], - [(self.fx.b, self.fx.a), (self.fx.c, self.fx.o)], + [self.fx.b, self.fx.c], ) def test_no_constraints_for_var(self) -> None: @@ -69,36 +69,32 @@ def test_no_constraints_for_var(self) -> None: self.assert_solve( [self.fx.t, self.fx.s], [self.supc(self.fx.s, self.fx.a)], - [self.fx.uninhabited, (self.fx.a, self.fx.o)], + [self.fx.uninhabited, self.fx.a], ) def test_simple_constraints_with_dynamic_type(self) -> None: - self.assert_solve( - [self.fx.t], [self.supc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] - ) + self.assert_solve([self.fx.t], [self.supc(self.fx.t, self.fx.anyt)], [self.fx.anyt]) self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.supc(self.fx.t, self.fx.anyt)], - [(self.fx.anyt, self.fx.anyt)], + [self.fx.anyt], ) self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.supc(self.fx.t, self.fx.a)], - [(self.fx.anyt, self.fx.anyt)], + [self.fx.anyt], ) - self.assert_solve( - [self.fx.t], [self.subc(self.fx.t, self.fx.anyt)], [(self.fx.anyt, self.fx.anyt)] - ) + self.assert_solve([self.fx.t], [self.subc(self.fx.t, self.fx.anyt)], [self.fx.anyt]) self.assert_solve( [self.fx.t], [self.subc(self.fx.t, self.fx.anyt), self.subc(self.fx.t, self.fx.anyt)], - [(self.fx.anyt, self.fx.anyt)], + [self.fx.anyt], ) # self.assert_solve([self.fx.t], # [self.subc(self.fx.t, self.fx.anyt), # self.subc(self.fx.t, self.fx.a)], - # [(self.fx.anyt, self.fx.anyt)]) + # [self.fx.anyt]) # TODO: figure out what this should be after changes to meet(any, X) def test_both_normal_and_any_types_in_results(self) -> None: @@ -107,29 +103,180 @@ def test_both_normal_and_any_types_in_results(self) -> None: self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.a), self.subc(self.fx.t, self.fx.anyt)], - [(self.fx.anyt, self.fx.anyt)], + [self.fx.anyt], ) self.assert_solve( [self.fx.t], [self.supc(self.fx.t, self.fx.anyt), self.subc(self.fx.t, self.fx.a)], - [(self.fx.anyt, self.fx.anyt)], + [self.fx.anyt], + ) + + def test_poly_no_constraints(self) -> None: + self.assert_solve( + [self.fx.t, self.fx.u], + [], + [self.fx.uninhabited, self.fx.uninhabited], + allow_polymorphic=True, + ) + + def test_poly_trivial_free(self) -> None: + self.assert_solve( + [self.fx.t, self.fx.u], + [self.subc(self.fx.t, self.fx.a)], + [self.fx.a, self.fx.u], + [self.fx.u], + allow_polymorphic=True, + ) + + def test_poly_free_pair(self) -> None: + self.assert_solve( + [self.fx.t, self.fx.u], + [self.subc(self.fx.t, self.fx.u)], + [self.fx.t, self.fx.t], + [self.fx.t], + allow_polymorphic=True, + ) + + def test_poly_free_pair_with_bounds(self) -> None: + t_prime = self.fx.t.copy_modified(upper_bound=self.fx.b) + self.assert_solve( + [self.fx.t, self.fx.ub], + [self.subc(self.fx.t, self.fx.ub)], + [t_prime, t_prime], + [t_prime], + allow_polymorphic=True, + ) + + def test_poly_free_pair_with_bounds_uninhabited(self) -> None: + self.assert_solve( + [self.fx.ub, self.fx.uc], + [self.subc(self.fx.ub, self.fx.uc)], + [self.fx.uninhabited, self.fx.uninhabited], + [], + allow_polymorphic=True, + ) + + def test_poly_bounded_chain(self) -> None: + # B <: T <: U <: S <: A + self.assert_solve( + [self.fx.t, self.fx.u, self.fx.s], + [ + self.supc(self.fx.t, self.fx.b), + self.subc(self.fx.t, self.fx.u), + self.subc(self.fx.u, self.fx.s), + self.subc(self.fx.s, self.fx.a), + ], + [self.fx.b, self.fx.b, self.fx.b], + allow_polymorphic=True, + ) + + def test_poly_reverse_overlapping_chain(self) -> None: + # A :> T <: S :> B + self.assert_solve( + [self.fx.t, self.fx.s], + [ + self.subc(self.fx.t, self.fx.s), + self.subc(self.fx.t, self.fx.a), + self.supc(self.fx.s, self.fx.b), + ], + [self.fx.a, self.fx.a], + allow_polymorphic=True, + ) + + def test_poly_reverse_split_chain(self) -> None: + # B :> T <: S :> A + self.assert_solve( + [self.fx.t, self.fx.s], + [ + self.subc(self.fx.t, self.fx.s), + self.subc(self.fx.t, self.fx.b), + self.supc(self.fx.s, self.fx.a), + ], + [self.fx.b, self.fx.a], + allow_polymorphic=True, + ) + + def test_poly_unsolvable_chain(self) -> None: + # A <: T <: U <: S <: B + self.assert_solve( + [self.fx.t, self.fx.u, self.fx.s], + [ + self.supc(self.fx.t, self.fx.a), + self.subc(self.fx.t, self.fx.u), + self.subc(self.fx.u, self.fx.s), + self.subc(self.fx.s, self.fx.b), + ], + [None, None, None], + allow_polymorphic=True, + ) + + def test_simple_chain_closure(self) -> None: + self.assert_transitive_closure( + [self.fx.t.id, self.fx.s.id], + [ + self.supc(self.fx.t, self.fx.b), + self.subc(self.fx.t, self.fx.s), + self.subc(self.fx.s, self.fx.a), + ], + {(self.fx.t.id, self.fx.s.id)}, + {self.fx.t.id: {self.fx.b}, self.fx.s.id: {self.fx.b}}, + {self.fx.t.id: {self.fx.a}, self.fx.s.id: {self.fx.a}}, + ) + + def test_reverse_chain_closure(self) -> None: + self.assert_transitive_closure( + [self.fx.t.id, self.fx.s.id], + [ + self.subc(self.fx.t, self.fx.s), + self.subc(self.fx.t, self.fx.a), + self.supc(self.fx.s, self.fx.b), + ], + {(self.fx.t.id, self.fx.s.id)}, + {self.fx.t.id: set(), self.fx.s.id: {self.fx.b}}, + {self.fx.t.id: {self.fx.a}, self.fx.s.id: set()}, + ) + + def test_secondary_constraint_closure(self) -> None: + self.assert_transitive_closure( + [self.fx.t.id, self.fx.s.id], + [self.supc(self.fx.s, self.fx.gt), self.subc(self.fx.s, self.fx.ga)], + set(), + {self.fx.t.id: set(), self.fx.s.id: {self.fx.gt}}, + {self.fx.t.id: {self.fx.a}, self.fx.s.id: {self.fx.ga}}, ) def assert_solve( self, vars: list[TypeVarLikeType], constraints: list[Constraint], - results: list[None | Type | tuple[Type, Type]], + results: list[None | Type], + free_vars: list[TypeVarLikeType] | None = None, + allow_polymorphic: bool = False, + ) -> None: + if free_vars is None: + free_vars = [] + actual, actual_free = solve_constraints( + vars, constraints, allow_polymorphic=allow_polymorphic + ) + assert_equal(actual, results) + assert_equal(actual_free, free_vars) + + def assert_transitive_closure( + self, + vars: list[TypeVarId], + constraints: list[Constraint], + graph: Graph, + lowers: Bounds, + uppers: Bounds, ) -> None: - res: list[Type | None] = [] - for r in results: - if isinstance(r, tuple): - res.append(r[0]) - else: - res.append(r) - actual, _ = solve_constraints(vars, constraints) - assert_equal(str(actual), str(res)) + actual_graph, actual_lowers, actual_uppers = transitive_closure(vars, constraints) + # Add trivial elements. + for v in vars: + graph.add((v, v)) + assert_equal(actual_graph, graph) + assert_equal(dict(actual_lowers), lowers) + assert_equal(dict(actual_uppers), uppers) def supc(self, type_var: TypeVarType, bound: Type) -> Constraint: return Constraint(type_var, SUPERTYPE_OF, bound) diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index bf1500a3cdec..81af765f8585 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -219,6 +219,10 @@ def make_type_var( self._add_bool_dunder(self.bool_type_info) self._add_bool_dunder(self.ai) + # TypeVars with non-trivial bounds + self.ub = make_type_var("UB", 5, [], self.b, variance) # UB`5 (type variable) + self.uc = make_type_var("UC", 6, [], self.c, variance) # UC`6 (type variable) + def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleType: return TypeVarTupleType( name, diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 5c510a11b970..d1842a74d634 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3007,3 +3007,31 @@ class C: c: C reveal_type(c.test()) # N: Revealed type is "__main__.C" + +[case testInferenceAgainstGenericBoundsAndValues] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +class B: ... +class C(B): ... + +S = TypeVar('S') +T = TypeVar('T') +UB = TypeVar('UB', bound=B) +UC = TypeVar('UC', bound=C) +V = TypeVar('V', int, str) + +def dec1(f: Callable[[S], T]) -> Callable[[S], List[T]]: + ... +def dec2(f: Callable[[UC], T]) -> Callable[[UC], List[T]]: + ... +def id1(x: UB) -> UB: + ... +def id2(x: V) -> V: + ... + +reveal_type(dec1(id1)) # N: Revealed type is "def [S <: __main__.B] (S`1) -> builtins.list[S`1]" +reveal_type(dec1(id2)) # N: Revealed type is "def [S in (builtins.int, builtins.str)] (S`3) -> builtins.list[S`3]" +reveal_type(dec2(id1)) # N: Revealed type is "def [UC <: __main__.C] (UC`5) -> builtins.list[UC`5]" +reveal_type(dec2(id2)) # N: Revealed type is "def () -> builtins.list[]" \ + # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[], ]" From 78339b97dc911c8c6841184eaddbbc30d0e406da Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 10 Aug 2023 01:50:20 -0700 Subject: [PATCH 0175/1617] Use error subcodes to differentiate import errors (#14740) Resolves #9789 Users could use `--disable-error-code=import-untyped` to only ignore errors about libraries not having stubs, but continue to get errors about e.g. typos in an import name. The error subcode mechanism is new from #14570. Note that users will now get a different error code depending on whether or not a package is installed, and may not know that they can use the parent error code to ignore the issue regardless. I think this is okay, in general type checking results can change if you run them in two different environments. Note also that with `--warn-unused-ignore` / `--strict` mypy will complain about not having the most specific error code --- mypy/build.py | 11 ++++++++++- mypy/errorcodes.py | 6 ++++++ mypy/errors.py | 8 ++++++-- test-data/unit/check-errorcodes.test | 14 +++++++------- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 5a0a481ae1a2..eed5005d182e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2780,7 +2780,16 @@ def module_not_found( else: daemon = manager.options.fine_grained_incremental msg, notes = reason.error_message_templates(daemon) - errors.report(line, 0, msg.format(module=target), code=codes.IMPORT) + if reason == ModuleNotFoundReason.NOT_FOUND: + code = codes.IMPORT_NOT_FOUND + elif ( + reason == ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS + or reason == ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + ): + code = codes.IMPORT_UNTYPED + else: + code = codes.IMPORT + errors.report(line, 0, msg.format(module=target), code=code) top_level, second_level = get_top_two_prefixes(target) if second_level in legacy_bundled_packages or second_level in non_bundled_packages: top_level = second_level diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 717629ad1f11..e7d0c16f2d2d 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -107,6 +107,12 @@ def __hash__(self) -> int: IMPORT: Final = ErrorCode( "import", "Require that imported module can be found or has stubs", "General" ) +IMPORT_NOT_FOUND: Final = ErrorCode( + "import-not-found", "Require that imported module can be found", "General", sub_code_of=IMPORT +) +IMPORT_UNTYPED: Final = ErrorCode( + "import-untyped", "Require that imported module has stubs", "General", sub_code_of=IMPORT +) NO_REDEF: Final = ErrorCode("no-redef", "Check that each name is defined once", "General") FUNC_RETURNS_VALUE: Final = ErrorCode( "func-returns-value", "Check that called function returns a value in value context", "General" diff --git a/mypy/errors.py b/mypy/errors.py index 2badac3e3d6d..680b7f1d31ea 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -8,7 +8,7 @@ from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes -from mypy.errorcodes import IMPORT, ErrorCode +from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode from mypy.message_registry import ErrorMessage from mypy.options import Options from mypy.scope import Scope @@ -510,7 +510,11 @@ def add_error_info(self, info: ErrorInfo) -> None: if info.message in self.only_once_messages: return self.only_once_messages.add(info.message) - if self.seen_import_error and info.code is not IMPORT and self.has_many_errors(): + if ( + self.seen_import_error + and info.code not in (IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND) + and self.has_many_errors() + ): # Missing stubs can easily cause thousands of errors about # Any types, especially when upgrading to mypy 0.900, # which no longer bundles third-party library stubs. Avoid diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1efbab7de322..796e1c1ea98e 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -183,7 +183,7 @@ from defusedxml import xyz # type: ignore[import] [case testErrorCodeBadIgnore] import nostub # type: ignore xyz # E: Invalid "type: ignore" comment [syntax] \ - # E: Cannot find implementation or library stub for module named "nostub" [import] \ + # E: Cannot find implementation or library stub for module named "nostub" [import-not-found] \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports import nostub # type: ignore[ # E: Invalid "type: ignore" comment [syntax] import nostub # type: ignore[foo # E: Invalid "type: ignore" comment [syntax] @@ -211,7 +211,7 @@ def f(x, # type: int # type: ignore[ pass [out] main:2: error: Invalid "type: ignore" comment [syntax] -main:2: error: Cannot find implementation or library stub for module named "nostub" [import] +main:2: error: Cannot find implementation or library stub for module named "nostub" [import-not-found] main:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports main:3: error: Invalid "type: ignore" comment [syntax] main:4: error: Invalid "type: ignore" comment [syntax] @@ -522,12 +522,12 @@ if int() is str(): # E: Non-overlapping identity check (left operand type: "int [builtins fixtures/primitives.pyi] [case testErrorCodeMissingModule] -from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import] -from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import] -import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import] -from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import] +from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import-not-found] +from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import-not-found] +import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import-not-found] +from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import-not-found] from pkg import bad # E: Module "pkg" has no attribute "bad" [attr-defined] -from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import] \ +from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import-not-found] \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [file pkg/__init__.py] From eab5b5083adf1b54ab1691f5ecc5a846863420de Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 10 Aug 2023 02:32:08 -0700 Subject: [PATCH 0176/1617] Document new import error codes (#15840) See https://github.com/python/mypy/pull/14740 My PR was pretty old and predates the nice check to ensure error codes are documented. --- docs/source/error_code_list.rst | 38 +++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index f935e025e589..f7f702aa7fcb 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -648,8 +648,18 @@ the issue: .. _code-import: -Check that import target can be found [import] ----------------------------------------------- +Check for an issue with imports [import] +---------------------------------------- + +Mypy generates an error if it can't resolve an `import` statement. +This is a parent error code of `import-not-found` and `import-untyped` + +See :ref:`ignore-missing-imports` for how to work around these errors. + +.. _code-import-not-found: + +Check that import target can be found [import-not-found] +-------------------------------------------------------- Mypy generates an error if it can't find the source code or a stub file for an imported module. @@ -658,11 +668,31 @@ Example: .. code-block:: python - # Error: Cannot find implementation or library stub for module named 'acme' [import] - import acme + # Error: Cannot find implementation or library stub for module named "m0dule_with_typo" [import-not-found] + import m0dule_with_typo See :ref:`ignore-missing-imports` for how to work around these errors. +.. _code-import-untyped: + +Check that import target can be found [import-untyped] +-------------------------------------------------------- + +Mypy generates an error if it can find the source code for an imported module, +but that module does not provide type annotations (via :ref:`PEP 561 `). + +Example: + +.. code-block:: python + + # Error: Library stubs not installed for "bs4" [import-untyped] + import bs4 + # Error: Skipping analyzing "no_py_typed": module is installed, but missing library stubs or py.typed marker [import-untyped] + import no_py_typed + +In some cases, these errors can be fixed by installing an appropriate +stub package. See :ref:`ignore-missing-imports` for more details. + .. _code-no-redef: Check that each name is defined once [no-redef] From d0d63b4644a6bb99793b32548c5197cf7600544f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 10 Aug 2023 13:03:39 +0300 Subject: [PATCH 0177/1617] The oldest CAPI version we support right now is 3.7 (#15839) Looks like `capi_version < 3.7` is not supported, so I changed the lowest version to be `3.7`. Based on the discord discussion. --- mypyc/codegen/emitclass.py | 8 ++------ mypyc/codegen/emitmodule.py | 5 ++--- mypyc/common.py | 5 ----- mypyc/test/testutil.py | 4 ++-- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 84d19d69d377..62e1b4b2dea1 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -18,7 +18,7 @@ generate_richcompare_wrapper, generate_set_del_item_wrapper, ) -from mypyc.common import BITMAP_BITS, BITMAP_TYPE, NATIVE_PREFIX, PREFIX, REG_PREFIX, use_fastcall +from mypyc.common import BITMAP_BITS, BITMAP_TYPE, NATIVE_PREFIX, PREFIX, REG_PREFIX from mypyc.ir.class_ir import ClassIR, VTableEntries from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR from mypyc.ir.rtypes import RTuple, RType, object_rprimitive @@ -794,11 +794,7 @@ def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: continue emitter.emit_line(f'{{"{fn.name}",') emitter.emit_line(f" (PyCFunction){PREFIX}{fn.cname(emitter.names)},") - if use_fastcall(emitter.capi_version): - flags = ["METH_FASTCALL"] - else: - flags = ["METH_VARARGS"] - flags.append("METH_KEYWORDS") + flags = ["METH_FASTCALL", "METH_KEYWORDS"] if fn.decl.kind == FUNC_STATICMETHOD: flags.append("METH_STATIC") elif fn.decl.kind == FUNC_CLASSMETHOD: diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index f360fabbe8f6..caf2058ea7c4 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -43,7 +43,6 @@ TOP_LEVEL_NAME, shared_lib_name, short_id_from_name, - use_fastcall, use_vectorcall, ) from mypyc.errors import Errors @@ -1107,8 +1106,8 @@ def is_fastcall_supported(fn: FuncIR, capi_version: tuple[int, int]) -> bool: # We can use vectorcalls (PEP 590) when supported return use_vectorcall(capi_version) # TODO: Support fastcall for __init__. - return use_fastcall(capi_version) and fn.name != "__init__" - return use_fastcall(capi_version) + return fn.name != "__init__" + return True def collect_literals(fn: FuncIR, literals: Literals) -> None: diff --git a/mypyc/common.py b/mypyc/common.py index 4615bf30d742..3d07f6c3d0d3 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -98,11 +98,6 @@ def short_name(name: str) -> str: return name -def use_fastcall(capi_version: tuple[int, int]) -> bool: - # We can use METH_FASTCALL for faster wrapper functions on Python 3.7+. - return capi_version >= (3, 7) - - def use_vectorcall(capi_version: tuple[int, int]) -> bool: # We can use vectorcalls to make calls on Python 3.8+ (PEP 590). return capi_version >= (3, 8) diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 796811a6363c..6446af3427af 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -102,7 +102,7 @@ def build_ir_for_single_file2( # By default generate IR compatible with the earliest supported Python C API. # If a test needs more recent API features, this should be overridden. - compiler_options = compiler_options or CompilerOptions(capi_version=(3, 5)) + compiler_options = compiler_options or CompilerOptions(capi_version=(3, 7)) options = Options() options.show_traceback = True options.hide_error_codes = True @@ -272,7 +272,7 @@ def infer_ir_build_options_from_test_name(name: str) -> CompilerOptions | None: return None if "_32bit" in name and not IS_32_BIT_PLATFORM: return None - options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 5)) + options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 7)) # A suffix like _python3.8 is used to set the target C API version. m = re.search(r"_python([3-9]+)_([0-9]+)(_|\b)", name) if m: From c7d2fa1525c9cbf0ab8859fd9ded526658677c28 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 10 Aug 2023 11:32:50 -0700 Subject: [PATCH 0178/1617] Fix over eager types-google-cloud-ndb suggestion (#15347) Fixes #15343 --- mypy/build.py | 30 ++++++++++++++++-------------- mypy/modulefinder.py | 9 ++------- mypy/stubinfo.py | 6 ++---- mypy/util.py | 11 ----------- test-data/unit/check-modules.test | 24 +++++++++++++----------- 5 files changed, 33 insertions(+), 47 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index eed5005d182e..525d5f436e7e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -55,7 +55,6 @@ DecodeError, decode_python_encoding, get_mypy_comments, - get_top_two_prefixes, hash_digest, is_stub_package_file, is_sub_path, @@ -91,12 +90,7 @@ from mypy.plugins.default import DefaultPlugin from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor from mypy.stats import dump_type_stats -from mypy.stubinfo import ( - is_legacy_bundled_package, - legacy_bundled_packages, - non_bundled_packages, - stub_package_name, -) +from mypy.stubinfo import legacy_bundled_packages, non_bundled_packages, stub_distribution_name from mypy.types import Type from mypy.typestate import reset_global_state, type_state from mypy.version import __version__ @@ -2665,14 +2659,18 @@ def find_module_and_diagnose( # search path or the module has not been installed. ignore_missing_imports = options.ignore_missing_imports - top_level, second_level = get_top_two_prefixes(id) + + id_components = id.split(".") # Don't honor a global (not per-module) ignore_missing_imports # setting for modules that used to have bundled stubs, as # otherwise updating mypy can silently result in new false # negatives. (Unless there are stubs but they are incomplete.) global_ignore_missing_imports = manager.options.ignore_missing_imports if ( - (is_legacy_bundled_package(top_level) or is_legacy_bundled_package(second_level)) + any( + ".".join(id_components[:i]) in legacy_bundled_packages + for i in range(len(id_components), 0, -1) + ) and global_ignore_missing_imports and not options.ignore_missing_imports_per_module and result is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED @@ -2790,15 +2788,19 @@ def module_not_found( else: code = codes.IMPORT errors.report(line, 0, msg.format(module=target), code=code) - top_level, second_level = get_top_two_prefixes(target) - if second_level in legacy_bundled_packages or second_level in non_bundled_packages: - top_level = second_level + + components = target.split(".") + for i in range(len(components), 0, -1): + module = ".".join(components[:i]) + if module in legacy_bundled_packages or module in non_bundled_packages: + break + for note in notes: if "{stub_dist}" in note: - note = note.format(stub_dist=stub_package_name(top_level)) + note = note.format(stub_dist=stub_distribution_name(module)) errors.report(line, 0, note, severity="note", only_once=True, code=codes.IMPORT) if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: - manager.missing_stub_packages.add(stub_package_name(top_level)) + manager.missing_stub_packages.add(stub_distribution_name(module)) errors.set_import_context(save_import_context) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index c780015c639d..c36a382848bf 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -337,14 +337,9 @@ def _find_module_non_stub_helper( # If this is not a directory then we can't traverse further into it if not self.fscache.isdir(dir_path): break - if approved_stub_package_exists(components[0]): - if len(components) == 1 or ( - self.find_module(components[0]) - is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED - ): + for i in range(len(components), 0, -1): + if approved_stub_package_exists(".".join(components[:i])): return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED - if approved_stub_package_exists(".".join(components[:2])): - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED if plausible_match: return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index e6e549ad280f..0d76a6215238 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -9,15 +9,13 @@ def approved_stub_package_exists(prefix: str) -> bool: return is_legacy_bundled_package(prefix) or prefix in non_bundled_packages -def stub_package_name(prefix: str) -> str: +def stub_distribution_name(prefix: str) -> str: return legacy_bundled_packages.get(prefix) or non_bundled_packages[prefix] # Stubs for these third-party packages used to be shipped with mypy. # # Map package name to PyPI stub distribution name. -# -# Package name can have one or two components ('a' or 'a.b'). legacy_bundled_packages = { "aiofiles": "types-aiofiles", "bleach": "types-bleach", @@ -116,7 +114,7 @@ def stub_package_name(prefix: str) -> str: "flask_sqlalchemy": "types-Flask-SQLAlchemy", "fpdf": "types-fpdf2", "gdb": "types-gdb", - "google.cloud": "types-google-cloud-ndb", + "google.cloud.ndb": "types-google-cloud-ndb", "hdbcli": "types-hdbcli", "html5lib": "types-html5lib", "httplib2": "types-httplib2", diff --git a/mypy/util.py b/mypy/util.py index 268ba8f9de81..8a079c5256bc 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -308,17 +308,6 @@ def get_prefix(fullname: str) -> str: return fullname.rsplit(".", 1)[0] -def get_top_two_prefixes(fullname: str) -> tuple[str, str]: - """Return one and two component prefixes of a fully qualified name. - - Given 'a.b.c.d', return ('a', 'a.b'). - - If fullname has only one component, return (fullname, fullname). - """ - components = fullname.split(".", 3) - return components[0], ".".join(components[:2]) - - def correct_relative_import( cur_mod_id: str, relative: int, target: str, is_cur_package_init_file: bool ) -> tuple[str, bool]: diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index bdf860cba89d..3da5996ed274 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3121,26 +3121,28 @@ import google.cloud from google.cloud import x [case testErrorFromGoogleCloud] -import google.cloud +import google.cloud # E: Cannot find implementation or library stub for module named "google.cloud" \ + # E: Cannot find implementation or library stub for module named "google" from google.cloud import x -import google.non_existent +import google.non_existent # E: Cannot find implementation or library stub for module named "google.non_existent" from google.non_existent import x -[out] -main:1: error: Library stubs not installed for "google.cloud" -main:1: note: Hint: "python3 -m pip install types-google-cloud-ndb" -main:1: note: (or run "mypy --install-types" to install all missing stub packages) -main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main:1: error: Cannot find implementation or library stub for module named "google" -main:3: error: Cannot find implementation or library stub for module named "google.non_existent" + +import google.cloud.ndb # E: Library stubs not installed for "google.cloud.ndb" \ + # N: Hint: "python3 -m pip install types-google-cloud-ndb" \ + # N: (or run "mypy --install-types" to install all missing stub packages) \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +from google.cloud import ndb [case testMissingSubmoduleOfInstalledStubPackage] import bleach.xyz from bleach.abc import fgh [file bleach/__init__.pyi] [out] -main:1: error: Cannot find implementation or library stub for module named "bleach.xyz" +main:1: error: Library stubs not installed for "bleach.xyz" +main:1: note: Hint: "python3 -m pip install types-bleach" +main:1: note: (or run "mypy --install-types" to install all missing stub packages) main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main:2: error: Cannot find implementation or library stub for module named "bleach.abc" +main:2: error: Library stubs not installed for "bleach.abc" [case testMissingSubmoduleOfInstalledStubPackageIgnored] # flags: --ignore-missing-imports From cfd01d9f7fdceb5eb8e367e8f1a6a1efb5ede38c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:49:27 -0700 Subject: [PATCH 0179/1617] Improve error code disabling documentation (#15841) Provide a concrete example of what file level comments would look like. Sort text into sections a little better. --- docs/source/error_codes.rst | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/source/error_codes.rst b/docs/source/error_codes.rst index 65ae0e5816e8..a71168cadf30 100644 --- a/docs/source/error_codes.rst +++ b/docs/source/error_codes.rst @@ -43,11 +43,7 @@ Silencing errors based on error codes You can use a special comment ``# type: ignore[code, ...]`` to only ignore errors with a specific error code (or codes) on a particular line. This can be used even if you have not configured mypy to show -error codes. Currently it's only possible to disable arbitrary error -codes on individual lines using this comment. - -You can also use :option:`--disable-error-code ` -to disable specific error codes globally. +error codes. This example shows how to ignore an error about an imported name mypy thinks is undefined: @@ -58,17 +54,17 @@ thinks is undefined: # definition. from foolib import foo # type: ignore[attr-defined] - -Enabling specific error codes ------------------------------ +Enabling/disabling specific error codes globally +------------------------------------------------ There are command-line flags and config file settings for enabling certain optional error codes, such as :option:`--disallow-untyped-defs `, which enables the ``no-untyped-def`` error code. -You can use :option:`--enable-error-code ` to -enable specific error codes that don't have a dedicated command-line -flag or config file setting. +You can use :option:`--enable-error-code ` +and :option:`--disable-error-code ` +to enable or disable specific error codes that don't have a dedicated +command-line flag or config file setting. Per-module enabling/disabling error codes ----------------------------------------- @@ -107,8 +103,9 @@ still keep the other two error codes enabled. The overall logic is following: * Individual config sections *adjust* them per glob/module -* Inline ``# mypy: ...`` comments can further *adjust* them for a specific - module +* Inline ``# mypy: disable-error-code="..."`` comments can further + *adjust* them for a specific module. + For example: ``# mypy: disable-error-code="truthy-bool, ignore-without-code"`` So one can e.g. enable some code globally, disable it for all tests in the corresponding config section, and then re-enable it with an inline From 7f22aaa5783e25c2bbac81ad520d5b7702b39e4f Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Sat, 12 Aug 2023 09:09:32 +0200 Subject: [PATCH 0180/1617] Add tox.ini to sdist (#15853) Fixes #14142 --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 1c26ae16fc78..b77b762b4852 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -39,9 +39,10 @@ graft test-data include conftest.py include runtests.py include pytest.ini +include tox.ini include LICENSE mypyc/README.md -exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md tox.ini action.yml .editorconfig +exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md action.yml .editorconfig exclude .git-blame-ignore-revs .pre-commit-config.yaml global-exclude *.py[cod] From 89c6596f0285b2f4b4b4c93f8f5696cae7a4398e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 00:10:26 -0700 Subject: [PATCH 0181/1617] Sync typeshed (#15792) Source commit: https://github.com/python/typeshed/commit/fe2ebd69af14d376825f21182d415223bd037485 Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: hauntsaninja --- mypy/typeshed/stdlib/_collections_abc.pyi | 2 + mypy/typeshed/stdlib/_weakref.pyi | 3 + mypy/typeshed/stdlib/abc.pyi | 2 +- mypy/typeshed/stdlib/argparse.pyi | 10 +- mypy/typeshed/stdlib/array.pyi | 6 + mypy/typeshed/stdlib/asyncio/futures.pyi | 2 +- mypy/typeshed/stdlib/builtins.pyi | 34 +++++- mypy/typeshed/stdlib/collections/__init__.pyi | 7 +- mypy/typeshed/stdlib/contextvars.pyi | 14 ++- mypy/typeshed/stdlib/datetime.pyi | 6 + mypy/typeshed/stdlib/email/charset.pyi | 4 +- mypy/typeshed/stdlib/email/policy.pyi | 8 +- mypy/typeshed/stdlib/enum.pyi | 1 + mypy/typeshed/stdlib/ftplib.pyi | 2 +- mypy/typeshed/stdlib/http/client.pyi | 4 + mypy/typeshed/stdlib/importlib/machinery.pyi | 1 + .../stdlib/importlib/metadata/__init__.pyi | 3 + mypy/typeshed/stdlib/inspect.pyi | 2 + mypy/typeshed/stdlib/ipaddress.pyi | 13 ++- .../stdlib/multiprocessing/managers.pyi | 10 +- mypy/typeshed/stdlib/pdb.pyi | 3 + mypy/typeshed/stdlib/pydoc.pyi | 2 + mypy/typeshed/stdlib/re.pyi | 2 + mypy/typeshed/stdlib/shelve.pyi | 4 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 13 ++- mypy/typeshed/stdlib/ssl.pyi | 1 + mypy/typeshed/stdlib/tkinter/ttk.pyi | 106 ++++++++++-------- mypy/typeshed/stdlib/traceback.pyi | 7 +- mypy/typeshed/stdlib/tracemalloc.pyi | 5 + mypy/typeshed/stdlib/types.pyi | 13 +++ mypy/typeshed/stdlib/typing.pyi | 11 +- mypy/typeshed/stdlib/typing_extensions.pyi | 90 ++++++++++++++- mypy/typeshed/stdlib/unittest/case.pyi | 3 + mypy/typeshed/stdlib/unittest/mock.pyi | 6 +- mypy/typeshed/stdlib/urllib/request.pyi | 8 +- mypy/typeshed/stdlib/uuid.pyi | 1 + mypy/typeshed/stdlib/weakref.pyi | 9 +- mypy/typeshed/stdlib/winreg.pyi | 1 + .../typeshed/stdlib/xml/etree/ElementTree.pyi | 1 + test-data/unit/pythoneval.test | 6 +- 40 files changed, 326 insertions(+), 100 deletions(-) diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index ba2f638d81c9..2b57f157a0e4 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -69,6 +69,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -81,6 +82,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_items(ItemsView[_KT_co, _VT_co], Generic[_KT_co, _VT_co]): # undocumented + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index 2402d0bfe721..ce0f681248ab 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -11,17 +11,20 @@ _T = TypeVar("_T") @final class CallableProxyType(Generic[_C]): # "weakcallableproxy" + def __eq__(self, __value: object) -> bool: ... def __getattr__(self, attr: str) -> Any: ... __call__: _C @final class ProxyType(Generic[_T]): # "weakproxy" + def __eq__(self, __value: object) -> bool: ... def __getattr__(self, attr: str) -> Any: ... class ReferenceType(Generic[_T]): __callback__: Callable[[ReferenceType[_T]], Any] def __new__(cls, __o: _T, __callback: Callable[[ReferenceType[_T]], Any] | None = ...) -> Self: ... def __call__(self) -> _T | None: ... + def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index ec04d8f85d12..43893a298341 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -31,7 +31,7 @@ def abstractmethod(funcobj: _FuncT) -> _FuncT: ... class abstractclassmethod(classmethod[_T, _P, _R_co]): __isabstractmethod__: Literal[True] - def __init__(self, callable: Callable[Concatenate[_T, _P], _R_co]) -> None: ... + def __init__(self, callable: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... class abstractstaticmethod(staticmethod[_P, _R_co]): __isabstractmethod__: Literal[True] diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index e41048516dd9..b59dd56ab921 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -85,7 +85,7 @@ class _ActionsContainer: self, *name_or_flags: str, action: _ActionStr | type[Action] = ..., - nargs: int | _NArgsStr | _SUPPRESS_T = ..., + nargs: int | _NArgsStr | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., type: Callable[[str], _T] | FileType = ..., @@ -171,7 +171,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ) -> None: ... @overload - def parse_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> Namespace: ... # type: ignore[misc] + def parse_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... # type: ignore[misc] @overload def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... @overload @@ -210,7 +210,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def format_usage(self) -> str: ... def format_help(self) -> str: ... @overload - def parse_known_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + def parse_known_args(self, args: Sequence[str] | None = None, namespace: None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] @overload def parse_known_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... @overload @@ -219,13 +219,13 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def exit(self, status: int = 0, message: str | None = None) -> NoReturn: ... def error(self, message: str) -> NoReturn: ... @overload - def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> Namespace: ... # type: ignore[misc] + def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... # type: ignore[misc] @overload def parse_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... @overload def parse_intermixed_args(self, *, namespace: _N) -> _N: ... @overload - def parse_known_intermixed_args(self, args: Sequence[str] | None = None, namespace: Namespace | None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + def parse_known_intermixed_args(self, args: Sequence[str] | None = None, namespace: None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] @overload def parse_known_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... @overload diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 8b003503bc9b..b533f9240073 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -6,6 +6,9 @@ from collections.abc import Iterable from typing import Any, Generic, MutableSequence, TypeVar, overload # noqa: Y022 from typing_extensions import Literal, Self, SupportsIndex, TypeAlias +if sys.version_info >= (3, 12): + from types import GenericAlias + _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"] _FloatTypeCode: TypeAlias = Literal["f", "d"] _UnicodeTypeCode: TypeAlias = Literal["u"] @@ -70,6 +73,7 @@ class array(MutableSequence[_T], Generic[_T]): def __setitem__(self, __key: slice, __value: array[_T]) -> None: ... def __delitem__(self, __key: SupportsIndex | slice) -> None: ... def __add__(self, __value: array[_T]) -> array[_T]: ... + def __eq__(self, __value: object) -> bool: ... def __ge__(self, __value: array[_T]) -> bool: ... def __gt__(self, __value: array[_T]) -> bool: ... def __iadd__(self, __value: array[_T]) -> Self: ... # type: ignore[override] @@ -82,5 +86,7 @@ class array(MutableSequence[_T], Generic[_T]): def __deepcopy__(self, __unused: Any) -> array[_T]: ... def __buffer__(self, __flags: int) -> memoryview: ... def __release_buffer__(self, __buffer: memoryview) -> None: ... + if sys.version_info >= (3, 12): + def __class_getitem__(cls, item: Any) -> GenericAlias: ... ArrayType = array diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index 79209f5ed4fb..af05425d02a2 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -31,7 +31,7 @@ def isfuture(obj: object) -> TypeGuard[Future[Any]]: ... class Future(Awaitable[_T], Iterable[_T]): _state: str @property - def _exception(self) -> BaseException: ... + def _exception(self) -> BaseException | None: ... _blocking: bool @property def _log_traceback(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index d6ca39049c77..66c644d09a4d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -131,6 +131,9 @@ class staticmethod(Generic[_P, _R_co]): @property def __isabstractmethod__(self) -> bool: ... def __init__(self, __f: Callable[_P, _R_co]) -> None: ... + @overload + def __get__(self, __instance: None, __owner: type) -> Callable[_P, _R_co]: ... + @overload def __get__(self, __instance: _T, __owner: type[_T] | None = None) -> Callable[_P, _R_co]: ... if sys.version_info >= (3, 10): __name__: str @@ -141,16 +144,19 @@ class staticmethod(Generic[_P, _R_co]): class classmethod(Generic[_T, _P, _R_co]): @property - def __func__(self) -> Callable[Concatenate[_T, _P], _R_co]: ... + def __func__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... @property def __isabstractmethod__(self) -> bool: ... - def __init__(self, __f: Callable[Concatenate[_T, _P], _R_co]) -> None: ... + def __init__(self, __f: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... + @overload def __get__(self, __instance: _T, __owner: type[_T] | None = None) -> Callable[_P, _R_co]: ... + @overload + def __get__(self, __instance: None, __owner: type[_T]) -> Callable[_P, _R_co]: ... if sys.version_info >= (3, 10): __name__: str __qualname__: str @property - def __wrapped__(self) -> Callable[Concatenate[_T, _P], _R_co]: ... + def __wrapped__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... class type: @property @@ -781,6 +787,8 @@ class memoryview(Sequence[int]): def __contains__(self, __x: object) -> bool: ... def __iter__(self) -> Iterator[int]: ... def __len__(self) -> int: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... @overload def __setitem__(self, __key: slice, __value: ReadableBuffer) -> None: ... @overload @@ -848,6 +856,7 @@ class slice: def __init__(self, __stop: Any) -> None: ... @overload def __init__(self, __start: Any, __stop: Any, __step: Any = ...) -> None: ... + def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] def indices(self, __len: SupportsIndex) -> tuple[int, int, int]: ... @@ -864,6 +873,8 @@ class tuple(Sequence[_T_co], Generic[_T_co]): def __le__(self, __value: tuple[_T_co, ...]) -> bool: ... def __gt__(self, __value: tuple[_T_co, ...]) -> bool: ... def __ge__(self, __value: tuple[_T_co, ...]) -> bool: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... @overload def __add__(self, __value: tuple[_T_co, ...]) -> tuple[_T_co, ...]: ... @overload @@ -952,6 +963,7 @@ class list(MutableSequence[_T], Generic[_T]): def __ge__(self, __value: list[_T]) -> bool: ... def __lt__(self, __value: list[_T]) -> bool: ... def __le__(self, __value: list[_T]) -> bool: ... + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @@ -991,19 +1003,24 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def fromkeys(cls, __iterable: Iterable[_T], __value: _S) -> dict[_T, _S]: ... # Positional-only in dict, but not in MutableMapping - @overload + @overload # type: ignore[override] def get(self, __key: _KT) -> _VT | None: ... @overload - def get(self, __key: _KT, __default: _VT | _T) -> _VT | _T: ... + def get(self, __key: _KT, __default: _VT) -> _VT: ... + @overload + def get(self, __key: _KT, __default: _T) -> _VT | _T: ... @overload def pop(self, __key: _KT) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T: ... + def pop(self, __key: _KT, __default: _VT) -> _VT: ... + @overload + def pop(self, __key: _KT, __default: _T) -> _VT | _T: ... def __len__(self) -> int: ... def __getitem__(self, __key: _KT) -> _VT: ... def __setitem__(self, __key: _KT, __value: _VT) -> None: ... def __delitem__(self, __key: _KT) -> None: ... def __iter__(self) -> Iterator[_KT]: ... + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 8): def __reversed__(self) -> Iterator[_KT]: ... __hash__: ClassVar[None] # type: ignore[assignment] @@ -1058,6 +1075,7 @@ class set(MutableSet[_T], Generic[_T]): def __lt__(self, __value: AbstractSet[object]) -> bool: ... def __ge__(self, __value: AbstractSet[object]) -> bool: ... def __gt__(self, __value: AbstractSet[object]) -> bool: ... + def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @@ -1086,6 +1104,8 @@ class frozenset(AbstractSet[_T_co], Generic[_T_co]): def __lt__(self, __value: AbstractSet[object]) -> bool: ... def __ge__(self, __value: AbstractSet[object]) -> bool: ... def __gt__(self, __value: AbstractSet[object]) -> bool: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @@ -1111,6 +1131,8 @@ class range(Sequence[int]): def count(self, __value: int) -> int: ... def index(self, __value: int) -> int: ... # type: ignore[override] def __len__(self) -> int: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... def __contains__(self, __key: object) -> bool: ... def __iter__(self) -> Iterator[int]: ... @overload diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 36d79101908d..8ceecd1f354e 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -153,6 +153,7 @@ class UserString(Sequence[UserString]): def __gt__(self, string: str | UserString) -> bool: ... def __ge__(self, string: str | UserString) -> bool: ... def __eq__(self, string: object) -> bool: ... + def __hash__(self) -> int: ... def __contains__(self, char: object) -> bool: ... def __len__(self) -> int: ... def __getitem__(self, index: SupportsIndex | slice) -> Self: ... @@ -257,6 +258,7 @@ class deque(MutableSequence[_T], Generic[_T]): def __le__(self, __value: deque[_T]) -> bool: ... def __gt__(self, __value: deque[_T]) -> bool: ... def __ge__(self, __value: deque[_T]) -> bool: ... + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @@ -365,6 +367,7 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): def setdefault(self: OrderedDict[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ... @overload def setdefault(self, key: _KT, default: _VT) -> _VT: ... + def __eq__(self, __value: object) -> bool: ... class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): default_factory: Callable[[], _VT] | None @@ -429,7 +432,9 @@ class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def pop(self, key: _KT) -> _VT: ... @overload - def pop(self, key: _KT, default: _VT | _T) -> _VT | _T: ... + def pop(self, key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T) -> _VT | _T: ... def copy(self) -> Self: ... __copy__ = copy # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, so the signature should be kept in line with `dict.fromkeys`. diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index ef6e2700e667..63b5f80aea6c 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -18,16 +18,21 @@ class ContextVar(Generic[_T]): def __init__(self, name: str) -> None: ... @overload def __init__(self, name: str, *, default: _T) -> None: ... + def __hash__(self) -> int: ... @property def name(self) -> str: ... @overload def get(self) -> _T: ... if sys.version_info >= (3, 8): @overload - def get(self, default: _D | _T) -> _D | _T: ... + def get(self, default: _T) -> _T: ... + @overload + def get(self, default: _D) -> _D | _T: ... else: @overload - def get(self, __default: _D | _T) -> _D | _T: ... + def get(self, __default: _T) -> _T: ... + @overload + def get(self, __default: _D) -> _D | _T: ... def set(self, __value: _T) -> Token[_T]: ... def reset(self, __token: Token[_T]) -> None: ... @@ -52,7 +57,9 @@ def copy_context() -> Context: ... class Context(Mapping[ContextVar[Any], Any]): def __init__(self) -> None: ... @overload - def get(self, __key: ContextVar[_T]) -> _T | None: ... + def get(self, __key: ContextVar[_T], __default: None = None) -> _T | None: ... # type: ignore[misc] # overlapping overloads + @overload + def get(self, __key: ContextVar[_T], __default: _T) -> _T: ... @overload def get(self, __key: ContextVar[_T], __default: _D) -> _T | _D: ... def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... @@ -60,3 +67,4 @@ class Context(Mapping[ContextVar[Any], Any]): def __getitem__(self, __key: ContextVar[_T]) -> _T: ... def __iter__(self) -> Iterator[ContextVar[Any]]: ... def __len__(self) -> int: ... + def __eq__(self, __value: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 00d511915f20..36577c5b7e1b 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -36,6 +36,7 @@ class timezone(tzinfo): def utcoffset(self, __dt: datetime | None) -> timedelta: ... def dst(self, __dt: datetime | None) -> None: ... def __hash__(self) -> int: ... + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 11): UTC: timezone @@ -87,6 +88,7 @@ class date: def __lt__(self, __value: date) -> bool: ... def __ge__(self, __value: date) -> bool: ... def __gt__(self, __value: date) -> bool: ... + def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 8): def __add__(self, __value: timedelta) -> Self: ... def __radd__(self, __value: timedelta) -> Self: ... @@ -145,6 +147,7 @@ class time: def __lt__(self, __value: time) -> bool: ... def __ge__(self, __value: time) -> bool: ... def __gt__(self, __value: time) -> bool: ... + def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... def isoformat(self, timespec: str = ...) -> str: ... @classmethod @@ -219,6 +222,7 @@ class timedelta: def __lt__(self, __value: timedelta) -> bool: ... def __ge__(self, __value: timedelta) -> bool: ... def __gt__(self, __value: timedelta) -> bool: ... + def __eq__(self, __value: object) -> bool: ... def __bool__(self) -> bool: ... def __hash__(self) -> int: ... @@ -310,6 +314,8 @@ class datetime(date): def __lt__(self, __value: datetime) -> bool: ... # type: ignore[override] def __ge__(self, __value: datetime) -> bool: ... # type: ignore[override] def __gt__(self, __value: datetime) -> bool: ... # type: ignore[override] + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 8): @overload # type: ignore[override] def __sub__(self, __value: timedelta) -> Self: ... diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index d61950a26424..f8de016ab8bf 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -19,11 +19,11 @@ class Charset: def get_body_encoding(self) -> str | Callable[[Message], None]: ... def get_output_charset(self) -> str | None: ... def header_encode(self, string: str) -> str: ... - def header_encode_lines(self, string: str, maxlengths: Iterator[int]) -> list[str]: ... + def header_encode_lines(self, string: str, maxlengths: Iterator[int]) -> list[str | None]: ... @overload def body_encode(self, string: None) -> None: ... @overload - def body_encode(self, string: str) -> str: ... + def body_encode(self, string: str | bytes) -> str: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, __value: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index dc7f18489bfa..804044031fcd 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -53,7 +53,7 @@ compat32: Compat32 class EmailPolicy(Policy): utf8: bool refold_source: str - header_factory: Callable[[str, str], str] + header_factory: Callable[[str, Any], Any] content_manager: ContentManager def __init__( self, @@ -70,9 +70,9 @@ class EmailPolicy(Policy): content_manager: ContentManager = ..., ) -> None: ... def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... - def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... - def header_fetch_parse(self, name: str, value: str) -> str: ... - def fold(self, name: str, value: str) -> str: ... + def header_store_parse(self, name: str, value: Any) -> tuple[str, Any]: ... + def header_fetch_parse(self, name: str, value: str) -> Any: ... + def fold(self, name: str, value: str) -> Any: ... def fold_binary(self, name: str, value: str) -> bytes: ... default: EmailPolicy diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 96a96dbce10e..60cc27215fd0 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -190,6 +190,7 @@ class Enum(metaclass=EnumMeta): # and in practice using `object` here has the same effect as using `Any`. def __new__(cls, value: object) -> Self: ... def __dir__(self) -> list[str]: ... + def __hash__(self) -> int: ... def __format__(self, format_spec: str) -> str: ... def __reduce_ex__(self, proto: Unused) -> tuple[Any, ...]: ... diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index f24d14fbf2b6..2d2ffa9aff03 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -87,7 +87,7 @@ class FTP: def makepasv(self) -> tuple[str, int]: ... def login(self, user: str = "", passwd: str = "", acct: str = "") -> str: ... # In practice, `rest` rest can actually be anything whose str() is an integer sequence, so to make it simple we allow integers. - def ntransfercmd(self, cmd: str, rest: int | str | None = None) -> tuple[socket, int]: ... + def ntransfercmd(self, cmd: str, rest: int | str | None = None) -> tuple[socket, int | None]: ... def transfercmd(self, cmd: str, rest: int | str | None = None) -> socket: ... def retrbinary( self, cmd: str, callback: Callable[[bytes], object], blocksize: int = 8192, rest: int | str | None = None diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 41ece1b050b8..4b5ed3d8bda0 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -115,6 +115,10 @@ class HTTPResponse(io.BufferedIOBase, BinaryIO): # type: ignore[misc] # incomp chunk_left: int | None length: int | None will_close: bool + # url is set on instances of the class in urllib.request.AbstractHTTPHandler.do_open + # to match urllib.response.addinfourl's interface. + # It's not set in HTTPResponse.__init__ or any other method on the class + url: str def __init__(self, sock: socket, debuglevel: int = 0, method: str | None = None, url: str | None = None) -> None: ... def peek(self, n: int = -1) -> bytes: ... def read(self, amt: int | None = None) -> bytes: ... diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index 5aaefce87e3a..f5037da00d5f 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -148,3 +148,4 @@ class ExtensionFileLoader(importlib.abc.ExecutionLoader): def exec_module(self, module: types.ModuleType) -> None: ... def get_code(self, fullname: str) -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 083453cd3c9a..0af33bc876c4 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -66,6 +66,9 @@ class EntryPoint(_EntryPointBase): extras: list[str] = ..., ) -> bool: ... # undocumented + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + if sys.version_info >= (3, 10): class EntryPoints(list[EntryPoint]): # use as list is deprecated since 3.10 # int argument is deprecated since 3.10 diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 9af4c39bae9e..601d23e786ac 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -354,6 +354,7 @@ class Signature: def from_callable(cls, obj: _IntrospectableCallable, *, follow_wrapped: bool = True) -> Self: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 10): def get_annotations( @@ -413,6 +414,7 @@ class Parameter: annotation: Any = ..., ) -> Self: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class BoundArguments: arguments: OrderedDict[str, Any] diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index fc42cf03e2bb..945e8bcbbdee 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -78,6 +78,7 @@ class _BaseNetwork(_IPAddressBase, Container[_A], Iterable[_A], Generic[_A]): def __getitem__(self, n: int) -> _A: ... def __iter__(self) -> Iterator[_A]: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def __lt__(self, other: Self) -> bool: ... if sys.version_info >= (3, 11): def __ge__(self, other: Self) -> bool: ... @@ -148,7 +149,10 @@ class _BaseV4: class IPv4Address(_BaseV4, _BaseAddress): ... class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): ... -class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): ... + +class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class _BaseV6: @property @@ -169,11 +173,16 @@ class IPv6Address(_BaseV6, _BaseAddress): @property def scope_id(self) -> str | None: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]): @property def is_site_local(self) -> bool: ... -class IPv6Interface(IPv6Address, _BaseInterface[IPv6Address, IPv6Network]): ... +class IPv6Interface(IPv6Address, _BaseInterface[IPv6Address, IPv6Network]): + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def v4_int_to_packed(address: int) -> bytes: ... def v6_int_to_packed(address: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 27a903fb9987..9cfc1ebbdd5e 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -73,14 +73,18 @@ class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): def __delitem__(self, __key: _KT) -> None: ... def __iter__(self) -> Iterator[_KT]: ... def copy(self) -> dict[_KT, _VT]: ... - @overload + @overload # type: ignore[override] def get(self, __key: _KT) -> _VT | None: ... @overload - def get(self, __key: _KT, __default: _VT | _T) -> _VT | _T: ... + def get(self, __key: _KT, __default: _VT) -> _VT: ... + @overload + def get(self, __key: _KT, __default: _T) -> _VT | _T: ... @overload def pop(self, __key: _KT) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T: ... + def pop(self, __key: _KT, __default: _VT) -> _VT: ... + @overload + def pop(self, __key: _KT, __default: _T) -> _VT | _T: ... def keys(self) -> list[_KT]: ... # type: ignore[override] def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] def values(self) -> list[_VT]: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index e0d69e7d30fa..4cc708d9d5fe 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -125,6 +125,9 @@ class Pdb(Bdb, Cmd): def sigint_handler(self, signum: signal.Signals, frame: FrameType) -> None: ... def message(self, msg: str) -> None: ... def error(self, msg: str) -> None: ... + if sys.version_info >= (3, 12): + def set_convenience_variable(self, frame: FrameType, name: str, value: Any) -> None: ... + def _select_frame(self, number: int) -> None: ... def _getval_except(self, arg: str, frame: FrameType | None = None) -> object: ... def _print_lines( diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index c993af390bbb..7791c977aa8b 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -61,6 +61,7 @@ class Doc: def getdocloc(self, object: object, basedir: str = ...) -> str | None: ... class HTMLRepr(Repr): + def __init__(self) -> None: ... def escape(self, text: str) -> str: ... def repr(self, object: object) -> str: ... def repr1(self, x: object, level: complex) -> str: ... @@ -148,6 +149,7 @@ class HTMLDoc(Doc): def filelink(self, url: str, path: str) -> str: ... class TextRepr(Repr): + def __init__(self) -> None: ... def repr1(self, x: object, level: complex) -> str: ... def repr_string(self, x: str, level: complex) -> str: ... def repr_str(self, x: str, level: complex) -> str: ... diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 4e53141ade84..29ee8b66815e 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -175,6 +175,8 @@ class Pattern(Generic[AnyStr]): def subn(self, repl: AnyStr | Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = 0) -> tuple[AnyStr, int]: ... def __copy__(self) -> Pattern[AnyStr]: ... def __deepcopy__(self, __memo: Any) -> Pattern[AnyStr]: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/shelve.pyi b/mypy/typeshed/stdlib/shelve.pyi index 82d0b03f4049..b162b3a85766 100644 --- a/mypy/typeshed/stdlib/shelve.pyi +++ b/mypy/typeshed/stdlib/shelve.pyi @@ -15,8 +15,10 @@ class Shelf(MutableMapping[str, _VT]): ) -> None: ... def __iter__(self) -> Iterator[str]: ... def __len__(self) -> int: ... + @overload # type: ignore[override] + def get(self, key: str, default: None = None) -> _VT | None: ... # type: ignore[misc] # overlapping overloads @overload - def get(self, key: str) -> _VT | None: ... + def get(self, key: str, default: _VT) -> _VT: ... @overload def get(self, key: str, default: _T) -> _VT | _T: ... def __getitem__(self, key: str) -> _VT: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index cff0f5e5ff1d..41f731e21e26 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -233,8 +233,9 @@ def connect( ) -> Connection: ... def enable_callback_tracebacks(__enable: bool) -> None: ... -# takes a pos-or-keyword argument because there is a C wrapper -def enable_shared_cache(enable: int) -> None: ... +if sys.version_info < (3, 12): + # takes a pos-or-keyword argument because there is a C wrapper + def enable_shared_cache(enable: int) -> None: ... if sys.version_info >= (3, 10): def register_adapter(__type: type[_T], __adapter: _Adapter[_T]) -> None: ... @@ -298,6 +299,11 @@ class Connection: isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' @property def total_changes(self) -> int: ... + if sys.version_info >= (3, 12): + @property + def autocommit(self) -> int: ... + @autocommit.setter + def autocommit(self, val: int) -> None: ... row_factory: Any text_factory: Any def __init__( @@ -375,6 +381,9 @@ class Connection: def getlimit(self, __category: int) -> int: ... def serialize(self, *, name: str = "main") -> bytes: ... def deserialize(self, __data: ReadableBuffer, *, name: str = "main") -> None: ... + if sys.version_info >= (3, 12): + def getconfig(self, __op: int) -> bool: ... + def setconfig(self, __op: int, __enable: bool = True) -> bool: ... def __call__(self, __sql: str) -> _Statement: ... def __enter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 20b8802bd7b9..446bbf8d1009 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -485,6 +485,7 @@ class SSLSession: def time(self) -> int: ... @property def timeout(self) -> int: ... + def __eq__(self, __value: object) -> bool: ... class SSLErrorNumber(enum.IntEnum): SSL_ERROR_EOF: int diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index d73566fc0917..bb416717a378 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -953,8 +953,6 @@ class _TreeviewColumnDict(TypedDict): anchor: tkinter._Anchor id: str -_TreeviewColumnId: TypeAlias = int | str # manual page: "COLUMN IDENTIFIERS" - class Treeview(Widget, tkinter.XView, tkinter.YView): def __init__( self, @@ -963,7 +961,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): class_: str = ..., columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: str | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., + displaycolumns: str | int | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., height: int = ..., name: str = ..., padding: _Padding = ..., @@ -985,7 +983,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): *, columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: str | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., + displaycolumns: str | int | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., height: int = ..., padding: _Padding = ..., selectmode: Literal["extended", "browse", "none"] = ..., @@ -998,23 +996,23 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure - def bbox(self, item, column: _TreeviewColumnId | None = None) -> tuple[int, int, int, int] | Literal[""]: ... # type: ignore[override] - def get_children(self, item: str | None = None) -> tuple[str, ...]: ... - def set_children(self, item: str, *newchildren: str) -> None: ... + def bbox(self, item: str | int, column: str | int | None = None) -> tuple[int, int, int, int] | Literal[""]: ... # type: ignore[override] + def get_children(self, item: str | int | None = None) -> tuple[str, ...]: ... + def set_children(self, item: str | int, *newchildren: str | int) -> None: ... @overload - def column(self, column: _TreeviewColumnId, option: Literal["width", "minwidth"]) -> int: ... + def column(self, column: str | int, option: Literal["width", "minwidth"]) -> int: ... @overload - def column(self, column: _TreeviewColumnId, option: Literal["stretch"]) -> bool: ... # actually 0 or 1 + def column(self, column: str | int, option: Literal["stretch"]) -> bool: ... # actually 0 or 1 @overload - def column(self, column: _TreeviewColumnId, option: Literal["anchor"]) -> _tkinter.Tcl_Obj: ... + def column(self, column: str | int, option: Literal["anchor"]) -> _tkinter.Tcl_Obj: ... @overload - def column(self, column: _TreeviewColumnId, option: Literal["id"]) -> str: ... + def column(self, column: str | int, option: Literal["id"]) -> str: ... @overload - def column(self, column: _TreeviewColumnId, option: str) -> Any: ... + def column(self, column: str | int, option: str) -> Any: ... @overload def column( self, - column: _TreeviewColumnId, + column: str | int, option: None = None, *, width: int = ..., @@ -1023,29 +1021,29 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): anchor: tkinter._Anchor = ..., # id is read-only ) -> _TreeviewColumnDict | None: ... - def delete(self, *items: str) -> None: ... - def detach(self, *items: str) -> None: ... - def exists(self, item: str) -> bool: ... + def delete(self, *items: str | int) -> None: ... + def detach(self, *items: str | int) -> None: ... + def exists(self, item: str | int) -> bool: ... @overload # type: ignore[override] def focus(self, item: None = None) -> str: ... # can return empty string @overload - def focus(self, item: str) -> Literal[""]: ... + def focus(self, item: str | int) -> Literal[""]: ... @overload - def heading(self, column: _TreeviewColumnId, option: Literal["text"]) -> str: ... + def heading(self, column: str | int, option: Literal["text"]) -> str: ... @overload - def heading(self, column: _TreeviewColumnId, option: Literal["image"]) -> tuple[str] | str: ... + def heading(self, column: str | int, option: Literal["image"]) -> tuple[str] | str: ... @overload - def heading(self, column: _TreeviewColumnId, option: Literal["anchor"]) -> _tkinter.Tcl_Obj: ... + def heading(self, column: str | int, option: Literal["anchor"]) -> _tkinter.Tcl_Obj: ... @overload - def heading(self, column: _TreeviewColumnId, option: Literal["command"]) -> str: ... + def heading(self, column: str | int, option: Literal["command"]) -> str: ... @overload - def heading(self, column: _TreeviewColumnId, option: str) -> Any: ... + def heading(self, column: str | int, option: str) -> Any: ... @overload - def heading(self, column: _TreeviewColumnId, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[misc] + def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[misc] @overload def heading( self, - column: _TreeviewColumnId, + column: str | int, option: None = None, *, text: str = ..., @@ -1058,14 +1056,14 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): def identify_column(self, x: int) -> str: ... def identify_region(self, x: int, y: int) -> Literal["heading", "separator", "tree", "cell", "nothing"]: ... def identify_element(self, x: int, y: int) -> str: ... # don't know what possible return values are - def index(self, item: str) -> int: ... + def index(self, item: str | int) -> int: ... def insert( self, parent: str, index: int | Literal["end"], - iid: str | None = None, + iid: str | int | None = None, *, - id: str = ..., # same as iid + id: str | int = ..., # same as iid text: str = ..., image: tkinter._ImageSpec = ..., values: list[Any] | tuple[Any, ...] = ..., @@ -1073,23 +1071,23 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): tags: str | list[str] | tuple[str, ...] = ..., ) -> str: ... @overload - def item(self, item: str, option: Literal["text"]) -> str: ... + def item(self, item: str | int, option: Literal["text"]) -> str: ... @overload - def item(self, item: str, option: Literal["image"]) -> tuple[str] | Literal[""]: ... + def item(self, item: str | int, option: Literal["image"]) -> tuple[str] | Literal[""]: ... @overload - def item(self, item: str, option: Literal["values"]) -> tuple[Any, ...] | Literal[""]: ... + def item(self, item: str | int, option: Literal["values"]) -> tuple[Any, ...] | Literal[""]: ... @overload - def item(self, item: str, option: Literal["open"]) -> bool: ... # actually 0 or 1 + def item(self, item: str | int, option: Literal["open"]) -> bool: ... # actually 0 or 1 @overload - def item(self, item: str, option: Literal["tags"]) -> tuple[str, ...] | Literal[""]: ... + def item(self, item: str | int, option: Literal["tags"]) -> tuple[str, ...] | Literal[""]: ... @overload - def item(self, item: str, option: str) -> Any: ... + def item(self, item: str | int, option: str) -> Any: ... @overload - def item(self, item: str, option: None = None) -> _TreeviewItemDict: ... # type: ignore[misc] + def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... # type: ignore[misc] @overload def item( self, - item: str, + item: str | int, option: None = None, *, text: str = ..., @@ -1098,27 +1096,39 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): open: bool = ..., tags: str | list[str] | tuple[str, ...] = ..., ) -> None: ... - def move(self, item: str, parent: str, index: int) -> None: ... + def move(self, item: str | int, parent: str, index: int) -> None: ... reattach = move - def next(self, item: str) -> str: ... # returning empty string means last item - def parent(self, item: str) -> str: ... - def prev(self, item: str) -> str: ... # returning empty string means first item - def see(self, item: str) -> None: ... + def next(self, item: str | int) -> str: ... # returning empty string means last item + def parent(self, item: str | int) -> str: ... + def prev(self, item: str | int) -> str: ... # returning empty string means first item + def see(self, item: str | int) -> None: ... if sys.version_info >= (3, 8): def selection(self) -> tuple[str, ...]: ... else: def selection(self, selop: Incomplete | None = ..., items: Incomplete | None = None) -> tuple[str, ...]: ... - def selection_set(self, items: str | list[str] | tuple[str, ...]) -> None: ... - def selection_add(self, items: str | list[str] | tuple[str, ...]) -> None: ... - def selection_remove(self, items: str | list[str] | tuple[str, ...]) -> None: ... - def selection_toggle(self, items: str | list[str] | tuple[str, ...]) -> None: ... @overload - def set(self, item: str, column: None = None, value: None = None) -> dict[str, Any]: ... + def selection_set(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + @overload + def selection_set(self, *items: str | int) -> None: ... + @overload + def selection_add(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + @overload + def selection_add(self, *items: str | int) -> None: ... + @overload + def selection_remove(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + @overload + def selection_remove(self, *items: str | int) -> None: ... + @overload + def selection_toggle(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + @overload + def selection_toggle(self, *items: str | int) -> None: ... + @overload + def set(self, item: str | int, column: None = None, value: None = None) -> dict[str, Any]: ... @overload - def set(self, item: str, column: _TreeviewColumnId, value: None = None) -> Any: ... + def set(self, item: str | int, column: str | int, value: None = None) -> Any: ... @overload - def set(self, item: str, column: _TreeviewColumnId, value: Any) -> Literal[""]: ... + def set(self, item: str | int, column: str | int, value: Any) -> Literal[""]: ... # There's no tag_unbind() or 'add' argument for whatever reason. # Also, it's 'callback' instead of 'func' here. @overload @@ -1150,7 +1160,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def tag_has(self, tagname: str, item: None = None) -> tuple[str, ...]: ... @overload - def tag_has(self, tagname: str, item: str) -> bool: ... + def tag_has(self, tagname: str, item: str | int) -> bool: ... class LabeledScale(Frame): label: Incomplete diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index a6d6d3e168b3..47449dfe8143 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import SupportsWrite +from _typeshed import SupportsWrite, Unused from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType from typing import Any, overload @@ -84,7 +84,10 @@ def format_list(extracted_list: list[FrameSummary]) -> list[str]: ... def print_list(extracted_list: list[FrameSummary], file: SupportsWrite[str] | None = None) -> None: ... if sys.version_info >= (3, 10): - def format_exception_only(__exc: type[BaseException] | None, value: BaseException | None = ...) -> list[str]: ... + @overload + def format_exception_only(__exc: BaseException | None) -> list[str]: ... + @overload + def format_exception_only(__exc: Unused, value: BaseException | None) -> list[str]: ... else: def format_exception_only(etype: type[BaseException] | None, value: BaseException | None) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi index 3dc8b8603fe5..6448a16ce11a 100644 --- a/mypy/typeshed/stdlib/tracemalloc.pyi +++ b/mypy/typeshed/stdlib/tracemalloc.pyi @@ -37,6 +37,7 @@ class Statistic: traceback: Traceback def __init__(self, traceback: Traceback, size: int, count: int) -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class StatisticDiff: count: int @@ -46,6 +47,7 @@ class StatisticDiff: traceback: Traceback def __init__(self, traceback: Traceback, size: int, size_diff: int, count: int, count_diff: int) -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... _FrameTuple: TypeAlias = tuple[str, int] @@ -56,6 +58,7 @@ class Frame: def lineno(self) -> int: ... def __init__(self, frame: _FrameTuple) -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def __lt__(self, other: Frame) -> bool: ... if sys.version_info >= (3, 11): def __gt__(self, other: Frame) -> bool: ... @@ -80,6 +83,7 @@ class Trace: def traceback(self) -> Traceback: ... def __init__(self, trace: _TraceTuple) -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class Traceback(Sequence[Frame]): if sys.version_info >= (3, 9): @@ -97,6 +101,7 @@ class Traceback(Sequence[Frame]): def __contains__(self, frame: Frame) -> bool: ... # type: ignore[override] def __len__(self) -> int: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def __lt__(self, other: Traceback) -> bool: ... if sys.version_info >= (3, 11): def __gt__(self, other: Traceback) -> bool: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index e5468ce4ed3c..2f4bd1a88047 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -71,6 +71,7 @@ class _Cell: if sys.version_info >= (3, 8): def __init__(self, __contents: object = ...) -> None: ... + def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] cell_contents: Any @@ -113,6 +114,8 @@ LambdaType = FunctionType @final class CodeType: + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... @property def co_argcount(self) -> int: ... if sys.version_info >= (3, 8): @@ -326,6 +329,7 @@ class MappingProxyType(Mapping[_KT, _VT_co], Generic[_KT, _VT_co]): class SimpleNamespace: __hash__: ClassVar[None] # type: ignore[assignment] def __init__(self, **kwargs: Any) -> None: ... + def __eq__(self, __value: object) -> bool: ... def __getattribute__(self, __name: str) -> Any: ... def __setattr__(self, __name: str, __value: Any) -> None: ... def __delattr__(self, __name: str) -> None: ... @@ -442,6 +446,8 @@ class MethodType: def __qualname__(self) -> str: ... # inherited from the added function def __init__(self, __func: Callable[..., Any], __obj: object) -> None: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... @final class BuiltinFunctionType: @@ -452,6 +458,8 @@ class BuiltinFunctionType: @property def __qualname__(self) -> str: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... BuiltinMethodType = BuiltinFunctionType @@ -479,6 +487,7 @@ class MethodWrapperType: def __call__(self, *args: Any, **kwargs: Any) -> Any: ... def __eq__(self, __value: object) -> bool: ... def __ne__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... @final class MethodDescriptorType: @@ -603,6 +612,8 @@ if sys.version_info >= (3, 9): def __parameters__(self) -> tuple[Any, ...]: ... def __init__(self, origin: type, args: Any) -> None: ... def __getitem__(self, __typeargs: Any) -> GenericAlias: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... if sys.version_info >= (3, 11): @property def __unpacked__(self) -> bool: ... @@ -626,3 +637,5 @@ if sys.version_info >= (3, 10): def __args__(self) -> tuple[Any, ...]: ... def __or__(self, __value: Any) -> UnionType: ... def __ror__(self, __value: Any) -> UnionType: ... + def __eq__(self, __value: object) -> bool: ... + def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 7496a0920690..6a307368642f 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -226,12 +226,14 @@ if sys.version_info >= (3, 10): @property def __origin__(self) -> ParamSpec: ... def __init__(self, origin: ParamSpec) -> None: ... + def __eq__(self, other: object) -> bool: ... @_final class ParamSpecKwargs: @property def __origin__(self) -> ParamSpec: ... def __init__(self, origin: ParamSpec) -> None: ... + def __eq__(self, other: object) -> bool: ... @_final class ParamSpec: @@ -563,6 +565,7 @@ class AbstractSet(Collection[_T_co], Generic[_T_co]): def __or__(self, other: AbstractSet[_T]) -> AbstractSet[_T_co | _T]: ... def __sub__(self, other: AbstractSet[Any]) -> AbstractSet[_T_co]: ... def __xor__(self, other: AbstractSet[_T]) -> AbstractSet[_T_co | _T]: ... + def __eq__(self, other: object) -> bool: ... def isdisjoint(self, other: Iterable[Any]) -> bool: ... class MutableSet(AbstractSet[_T], Generic[_T]): @@ -647,7 +650,9 @@ class MutableMapping(Mapping[_KT, _VT], Generic[_KT, _VT]): @overload def pop(self, __key: _KT) -> _VT: ... @overload - def pop(self, __key: _KT, default: _VT | _T) -> _VT | _T: ... + def pop(self, __key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, __key: _KT, default: _T) -> _VT | _T: ... def popitem(self) -> tuple[_KT, _VT]: ... # This overload should be allowed only if the value type is compatible with None. # @@ -953,3 +958,7 @@ if sys.version_info >= (3, 12): if sys.version_info >= (3, 10): def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... + +if sys.version_info >= (3, 13): + def is_protocol(__tp: type) -> bool: ... + def get_protocol_members(__tp: type) -> frozenset[str]: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 93087a45a108..efcc13e42047 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -4,26 +4,52 @@ import sys import typing from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import IdentityFunction, Incomplete -from collections.abc import Iterable -from typing import ( # noqa: Y022,Y039 +from typing import ( # noqa: Y022,Y037,Y038,Y039 + IO as IO, TYPE_CHECKING as TYPE_CHECKING, + AbstractSet as AbstractSet, Any as Any, + AnyStr as AnyStr, AsyncContextManager as AsyncContextManager, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, Awaitable as Awaitable, - Callable, + BinaryIO as BinaryIO, + Callable as Callable, ChainMap as ChainMap, ClassVar as ClassVar, + Collection as Collection, + Container as Container, ContextManager as ContextManager, Coroutine as Coroutine, Counter as Counter, DefaultDict as DefaultDict, Deque as Deque, - Mapping, + Dict as Dict, + ForwardRef as ForwardRef, + FrozenSet as FrozenSet, + Generator as Generator, + Generic as Generic, + Hashable as Hashable, + ItemsView as ItemsView, + Iterable as Iterable, + Iterator as Iterator, + KeysView as KeysView, + List as List, + Mapping as Mapping, + MappingView as MappingView, + Match as Match, + MutableMapping as MutableMapping, + MutableSequence as MutableSequence, + MutableSet as MutableSet, NoReturn as NoReturn, - Sequence, + Optional as Optional, + Pattern as Pattern, + Reversible as Reversible, + Sequence as Sequence, + Set as Set, + Sized as Sized, SupportsAbs as SupportsAbs, SupportsBytes as SupportsBytes, SupportsComplex as SupportsComplex, @@ -31,8 +57,15 @@ from typing import ( # noqa: Y022,Y039 SupportsInt as SupportsInt, SupportsRound as SupportsRound, Text as Text, + TextIO as TextIO, + Tuple as Tuple, Type as Type, + Union as Union, + ValuesView as ValuesView, _Alias, + cast as cast, + no_type_check as no_type_check, + no_type_check_decorator as no_type_check_decorator, overload as overload, type_check_only, ) @@ -109,11 +142,50 @@ __all__ = [ "get_original_bases", "get_overloads", "get_type_hints", + "AbstractSet", + "AnyStr", + "BinaryIO", + "Callable", + "Collection", + "Container", + "Dict", + "ForwardRef", + "FrozenSet", + "Generator", + "Generic", + "Hashable", + "IO", + "ItemsView", + "Iterable", + "Iterator", + "KeysView", + "List", + "Mapping", + "MappingView", + "Match", + "MutableMapping", + "MutableSequence", + "MutableSet", + "Optional", + "Pattern", + "Reversible", + "Sequence", + "Set", + "Sized", + "TextIO", + "Tuple", + "Union", + "ValuesView", + "cast", + "get_protocol_members", + "is_protocol", + "no_type_check", + "no_type_check_decorator", ] _T = typing.TypeVar("_T") _F = typing.TypeVar("_F", bound=Callable[..., Any]) -_TC = typing.TypeVar("_TC", bound=Type[object]) +_TC = typing.TypeVar("_TC", bound=type[object]) # unfortunately we have to duplicate this class definition from typing.pyi or we break pytype class _SpecialForm: @@ -403,3 +475,9 @@ else: # Not actually a Protocol at runtime; see # https://github.com/python/typeshed/issues/10224 for why we're defining it this way def __buffer__(self, __flags: int) -> memoryview: ... + +if sys.version_info >= (3, 13): + from typing import get_protocol_members as get_protocol_members, is_protocol as is_protocol +else: + def is_protocol(__tp: type) -> bool: ... + def get_protocol_members(__tp: type) -> frozenset[str]: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index 45c39e3f3010..1f58f266ee89 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -86,6 +86,7 @@ class TestCase: _testMethodDoc: str def __init__(self, methodName: str = "runTest") -> None: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def setUp(self) -> None: ... def tearDown(self) -> None: ... @classmethod @@ -304,6 +305,8 @@ class FunctionTestCase(TestCase): description: str | None = None, ) -> None: ... def runTest(self) -> None: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... class _AssertRaisesContext(Generic[_E]): exception: _E diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index db1cc7d9bfc9..66120197b269 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -389,7 +389,11 @@ if sys.version_info >= (3, 8): class AsyncMagicMixin(MagicMixin): def __init__(self, *args: Any, **kw: Any) -> None: ... - class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): ... + class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): + # Improving the `reset_mock` signature. + # It is defined on `AsyncMockMixin` with `*args, **kwargs`, which is not ideal. + # But, `NonCallableMock` super-class has the better version. + def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... class MagicProxy: name: str diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 8f99c5837871..079c9755528c 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -173,7 +173,7 @@ class HTTPPasswordMgr: def add_password(self, realm: str, uri: str | Sequence[str], user: str, passwd: str) -> None: ... def find_user_password(self, realm: str, authuri: str) -> tuple[str | None, str | None]: ... def is_suburi(self, base: str, test: str) -> bool: ... # undocumented - def reduce_uri(self, uri: str, default_port: bool = True) -> str: ... # undocumented + def reduce_uri(self, uri: str, default_port: bool = True) -> tuple[str, str]: ... # undocumented class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr): def add_password(self, realm: str | None, uri: str | Sequence[str], user: str, passwd: str) -> None: ... @@ -184,7 +184,7 @@ class HTTPPasswordMgrWithPriorAuth(HTTPPasswordMgrWithDefaultRealm): self, realm: str | None, uri: str | Sequence[str], user: str, passwd: str, is_authenticated: bool = False ) -> None: ... def update_authenticated(self, uri: str | Sequence[str], is_authenticated: bool = False) -> None: ... - def is_authenticated(self, authuri: str) -> bool: ... + def is_authenticated(self, authuri: str) -> bool | None: ... class AbstractBasicAuthHandler: rx: ClassVar[Pattern[str]] # undocumented @@ -212,7 +212,7 @@ class AbstractDigestAuthHandler: def http_error_auth_reqed(self, auth_header: str, host: str, req: Request, headers: HTTPMessage) -> None: ... def retry_http_digest_auth(self, req: Request, auth: str) -> _UrlopenRet | None: ... def get_cnonce(self, nonce: str) -> str: ... - def get_authorization(self, req: Request, chal: Mapping[str, str]) -> str: ... + def get_authorization(self, req: Request, chal: Mapping[str, str]) -> str | None: ... def get_algorithm_impls(self, algorithm: str) -> tuple[Callable[[str], str], Callable[[str, str], str]]: ... def get_entity_digest(self, data: ReadableBuffer | None, chal: Mapping[str, str]) -> str | None: ... @@ -269,7 +269,7 @@ class ftpwrapper: # undocumented def file_close(self) -> None: ... def init(self) -> None: ... def real_close(self) -> None: ... - def retrfile(self, file: str, type: str) -> tuple[addclosehook, int]: ... + def retrfile(self, file: str, type: str) -> tuple[addclosehook, int | None]: ... class FTPHandler(BaseHandler): def ftp_open(self, req: Request) -> addinfourl: ... diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index fd87646531a6..e1ea424f9680 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -63,6 +63,7 @@ class UUID: def __le__(self, other: UUID) -> bool: ... def __gt__(self, other: UUID) -> bool: ... def __ge__(self, other: UUID) -> bool: ... + def __hash__(self) -> builtins.int: ... if sys.version_info >= (3, 9): def getnode() -> int: ... diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 13f48fe85a8d..ecb98d4269d5 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -45,6 +45,7 @@ class WeakMethod(ref[_CallableT], Generic[_CallableT]): def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class WeakValueDictionary(MutableMapping[_KT, _VT]): @overload @@ -74,7 +75,9 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): @overload def pop(self, key: _KT) -> _VT: ... @overload - def pop(self, key: _KT, default: _VT | _T = ...) -> _VT | _T: ... + def pop(self, key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T) -> _VT | _T: ... if sys.version_info >= (3, 9): def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... @@ -117,7 +120,9 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): @overload def pop(self, key: _KT) -> _VT: ... @overload - def pop(self, key: _KT, default: _VT | _T = ...) -> _VT | _T: ... + def pop(self, key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T) -> _VT | _T: ... if sys.version_info >= (3, 9): def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index 70ea6a1ced11..337bd9706050 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -98,3 +98,4 @@ if sys.platform == "win32": ) -> bool | None: ... def Close(self) -> None: ... def Detach(self) -> int: ... + def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 2cf8dbbe7025..d8ff2f5b6090 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -142,6 +142,7 @@ class QName: def __gt__(self, other: QName | str) -> bool: ... def __ge__(self, other: QName | str) -> bool: ... def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class ElementTree: def __init__(self, element: Element | None = None, file: _FileRead | None = None) -> None: ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 754cb21c3ff8..58dfb172cf76 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1482,14 +1482,12 @@ frozenset({1}) == [1] # Error {1: 2}.keys() == frozenset({1}) {1: 2}.items() == {(1, 2)} -{1: 2}.keys() == {'no'} # Error +{1: 2}.keys() == {'no'} # OK {1: 2}.values() == {2} # Error -{1: 2}.keys() == [1] # Error +{1: 2}.keys() == [1] # OK [out] _testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "FrozenSet[int]", right operand type: "List[int]") -_testStrictEqualityAllowlist.py:11: error: Non-overlapping equality check (left operand type: "dict_keys[int, int]", right operand type: "Set[str]") _testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "dict_values[int, int]", right operand type: "Set[int]") -_testStrictEqualityAllowlist.py:13: error: Non-overlapping equality check (left operand type: "dict_keys[int, int]", right operand type: "List[int]") [case testUnreachableWithStdlibContextManagers] # mypy: warn-unreachable, strict-optional From 0e4521aa2d0b643b67777b4136bc27d97e622c56 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sat, 12 Aug 2023 04:36:03 -0400 Subject: [PATCH 0182/1617] Fix inheriting from generic @frozen attrs class (#15700) Fixes #15658. --- mypy/plugins/attrs.py | 2 +- test-data/unit/check-plugin-attrs.test | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 0f748cc140e8..d444c18852dd 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -803,7 +803,7 @@ def _make_frozen(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) else: # This variable belongs to a super class so create new Var so we # can modify it. - var = Var(attribute.name, ctx.cls.info[attribute.name].type) + var = Var(attribute.name, attribute.init_type) var.info = ctx.cls.info var._fullname = f"{ctx.cls.info.fullname}.{var.name}" ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 7a7bcb65fe98..3534d206c060 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2253,3 +2253,27 @@ c = attrs.assoc(c, name=42) # E: Argument "name" to "assoc" of "C" has incompat [builtins fixtures/plugin_attrs.pyi] [typing fixtures/typing-medium.pyi] + +[case testFrozenInheritFromGeneric] +from typing import Generic, TypeVar +from attrs import field, frozen + +T = TypeVar('T') + +def f(s: str) -> int: + ... + +@frozen +class A(Generic[T]): + x: T + y: int = field(converter=f) + +@frozen +class B(A[int]): + pass + +b = B(42, 'spam') +reveal_type(b.x) # N: Revealed type is "builtins.int" +reveal_type(b.y) # N: Revealed type is "builtins.int" + +[builtins fixtures/plugin_attrs.pyi] From 742b5c68cae5b33c7b53768e874d9bab4344567e Mon Sep 17 00:00:00 2001 From: Jannic Warken Date: Sat, 12 Aug 2023 10:56:09 +0200 Subject: [PATCH 0183/1617] Support __bool__ with Literal in --warn-unreachable (#15645) This adds support for `Literal` as return type of `__bool__` in the reachability analysis. Fixes https://github.com/python/mypy/issues/7008 --- mypy/typeops.py | 12 +++----- test-data/unit/check-unreachable-code.test | 32 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 65ab4340403c..4233cc1b2b33 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -602,10 +602,8 @@ def true_only(t: Type) -> ProperType: else: ret_type = _get_type_special_method_bool_ret_type(t) - if ret_type and ret_type.can_be_false and not ret_type.can_be_true: - new_t = copy_type(t) - new_t.can_be_true = False - return new_t + if ret_type and not ret_type.can_be_true: + return UninhabitedType(line=t.line, column=t.column) new_t = copy_type(t) new_t.can_be_false = False @@ -637,10 +635,8 @@ def false_only(t: Type) -> ProperType: else: ret_type = _get_type_special_method_bool_ret_type(t) - if ret_type and ret_type.can_be_true and not ret_type.can_be_false: - new_t = copy_type(t) - new_t.can_be_false = False - return new_t + if ret_type and not ret_type.can_be_false: + return UninhabitedType(line=t.line) new_t = copy_type(t) new_t.can_be_true = False diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 7a6c2cbfd1c7..20b5dea9fc87 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1379,6 +1379,38 @@ def f() -> None: x = 1 # E: Statement is unreachable [builtins fixtures/dict.pyi] +[case testUnreachableLiteralFrom__bool__] +# flags: --warn-unreachable +from typing_extensions import Literal + +class Truth: + def __bool__(self) -> Literal[True]: ... + +class Lie: + def __bool__(self) -> Literal[False]: ... + +class Maybe: + def __bool__(self) -> Literal[True | False]: ... + +t = Truth() +if t: + x = 1 +else: + x = 2 # E: Statement is unreachable + +if Lie(): + x = 3 # E: Statement is unreachable + +if Maybe(): + x = 4 + + +def foo() -> bool: ... + +y = Truth() or foo() # E: Right operand of "or" is never evaluated +z = Lie() and foo() # E: Right operand of "and" is never evaluated +[builtins fixtures/dict.pyi] + [case testUnreachableModuleBody1] # flags: --warn-unreachable from typing import NoReturn From 9dbb1232c0f2158f72d099d35d85252696d308ff Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 12 Aug 2023 11:05:08 +0200 Subject: [PATCH 0184/1617] Apply TypeVar defaults to functions (PEP 696) (#15387) Use TypeVar defaults to resolve fallback return type of functions. **Note**: Defaults for TypeVarTuples don't yet work, probably a result of the limited support for `Unpack` / `TypeVarTuple`. Ref: #14851 --- mypy/applytype.py | 5 +++- test-data/unit/check-typevar-defaults.test | 35 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 55a51d4adbb6..f8be63362a6b 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -18,6 +18,7 @@ TypeVarLikeType, TypeVarTupleType, TypeVarType, + UninhabitedType, UnpackType, get_proper_type, ) @@ -32,13 +33,15 @@ def get_target_type( context: Context, skip_unsatisfied: bool, ) -> Type | None: + p_type = get_proper_type(type) + if isinstance(p_type, UninhabitedType) and tvar.has_default(): + return tvar.default if isinstance(tvar, ParamSpecType): return type if isinstance(tvar, TypeVarTupleType): return type assert isinstance(tvar, TypeVarType) values = tvar.values - p_type = get_proper_type(type) if values: if isinstance(p_type, AnyType): return type diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 514186aa7518..36ec125eb1a4 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -81,3 +81,38 @@ T2 = TypeVar("T2", bound=List[str], default=List[int]) # E: TypeVar default mus T3 = TypeVar("T3", int, str, default=bytes) # E: TypeVar default must be one of the constraint types T4 = TypeVar("T4", int, str, default=Union[int, str]) # E: TypeVar default must be one of the constraint types T5 = TypeVar("T5", float, str, default=int) # E: TypeVar default must be one of the constraint types + +[case testTypeVarDefaultsFunctions] +from typing import TypeVar, ParamSpec, List, Union, Callable, Tuple +from typing_extensions import TypeVarTuple, Unpack + +T1 = TypeVar("T1", default=str) +T2 = TypeVar("T2", bound=str, default=str) +T3 = TypeVar("T3", bytes, str, default=str) +P1 = ParamSpec("P1", default=[int, str]) +Ts1 = TypeVarTuple("Ts1", default=Unpack[Tuple[int, str]]) + +def callback1(x: str) -> None: ... + +def func_a1(x: Union[int, T1]) -> T1: ... +reveal_type(func_a1(2)) # N: Revealed type is "builtins.str" +reveal_type(func_a1(2.1)) # N: Revealed type is "builtins.float" + +def func_a2(x: Union[int, T1]) -> List[T1]: ... +reveal_type(func_a2(2)) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(func_a2(2.1)) # N: Revealed type is "builtins.list[builtins.float]" + +def func_a3(x: Union[int, T2]) -> T2: ... +reveal_type(func_a3(2)) # N: Revealed type is "builtins.str" + +def func_a4(x: Union[int, T3]) -> T3: ... +reveal_type(func_a4(2)) # N: Revealed type is "builtins.str" + +def func_b1(x: Union[int, Callable[P1, None]]) -> Callable[P1, None]: ... +reveal_type(func_b1(callback1)) # N: Revealed type is "def (x: builtins.str)" +reveal_type(func_b1(2)) # N: Revealed type is "def (builtins.int, builtins.str)" + +def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: ... +# reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO +# reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO +[builtins fixtures/tuple.pyi] From 3631528796cbf2a5a825d6a5fb2010853a0a46bd Mon Sep 17 00:00:00 2001 From: Gregory Santosa <94944372+gregorysantosa@users.noreply.github.com> Date: Sat, 12 Aug 2023 02:10:27 -0700 Subject: [PATCH 0185/1617] 'await' in non-async function is a blocking error (#15384) Fixes, https://github.com/python/mypy/issues/15339 --- mypy/errorcodes.py | 4 +++- mypy/semanal.py | 7 ++++++- test-data/unit/check-async-await.test | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index e7d0c16f2d2d..3594458fa362 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -152,7 +152,9 @@ def __hash__(self) -> int: TOP_LEVEL_AWAIT: Final = ErrorCode( "top-level-await", "Warn about top level await expressions", "General" ) - +AWAIT_NOT_ASYNC: Final = ErrorCode( + "await-not-async", 'Warn about "await" outside coroutine ("async def")', "General" +) # These error codes aren't enabled by default. NO_UNTYPED_DEF: Final[ErrorCode] = ErrorCode( "no-untyped-def", "Check that every function has an annotation", "General" diff --git a/mypy/semanal.py b/mypy/semanal.py index 5b1aea4239f5..e21fc9f1c23f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5455,7 +5455,12 @@ def visit_await_expr(self, expr: AwaitExpr) -> None: # support top level awaits. self.fail('"await" outside function', expr, serious=True, code=codes.TOP_LEVEL_AWAIT) elif not self.function_stack[-1].is_coroutine: - self.fail('"await" outside coroutine ("async def")', expr, serious=True, blocker=True) + self.fail( + '"await" outside coroutine ("async def")', + expr, + serious=True, + code=codes.AWAIT_NOT_ASYNC, + ) expr.expr.accept(self) # diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index af6c31624b96..653025a0bb24 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -974,7 +974,7 @@ crasher = [await foo(x) for x in [1, 2, 3]] # E: "await" outside function [top def bad() -> None: # These are always critical / syntax issues: - y = [await foo(x) for x in [1, 2, 3]] # E: "await" outside coroutine ("async def") + y = [await foo(x) for x in [1, 2, 3]] # E: "await" outside coroutine ("async def") [await-not-async] async def good() -> None: y = [await foo(x) for x in [1, 2, 3]] # OK [builtins fixtures/async_await.pyi] From 041a8af015881655a88f5aff5351576a1ef9010b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 21:15:48 +0100 Subject: [PATCH 0186/1617] [pre-commit.ci] pre-commit autoupdate (#15796) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.0.280 → v0.0.281](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.280...v0.0.281) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f2367f63bb3d..8650a2868cd6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.280 # must match test-requirements.txt + rev: v0.0.281 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] From 9787a26f97fd6f216260aac89aa2253ed655195b Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 12 Aug 2023 14:00:59 -0700 Subject: [PATCH 0187/1617] Document await-not-async error code (#15858) --- docs/source/error_code_list.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index f7f702aa7fcb..157f90249af8 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1027,9 +1027,20 @@ example: top = await f() # Error: "await" outside function [top-level-await] +.. _code-await-not-async: + +Warn about await expressions used outside of coroutines [await-not-async] +------------------------------------------------------------------------- + +``await`` must be used inside a coroutine. + +.. code-block:: python + + async def f() -> None: + ... + def g() -> None: - # This is a blocker error and cannot be silenced. - await f() # Error: "await" outside coroutine ("async def") + await f() # Error: "await" outside coroutine ("async def") [await-not-async] .. _code-assert-type: From 117b9147d975c51e27dbea9ab415bc0b3bf4ac69 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 13 Aug 2023 21:19:55 +0100 Subject: [PATCH 0188/1617] Add option to selectively disable --disallow-untyped-calls (#15845) Fixes #10757 It is surprisingly one of the most upvoted issues. Also it looks quite easy to implement, so why not. Note I also try to improve docs for per-module logic for `disallow_untyped_calls`, as there is currently some confusion. --------- Co-authored-by: Ivan Levkivskyi --- docs/source/command_line.rst | 28 +++++++++++++++++ docs/source/config_file.rst | 33 +++++++++++++++++++- mypy/checkexpr.py | 23 +++++++++----- mypy/config_parser.py | 18 +++++++++++ mypy/main.py | 17 +++++++++- mypy/options.py | 4 +++ test-data/unit/check-flags.test | 55 +++++++++++++++++++++++++++++++++ 7 files changed, 169 insertions(+), 9 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index d9de5cd8f9bd..727d500e2d4d 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -350,6 +350,34 @@ definitions or calls. This flag reports an error whenever a function with type annotations calls a function defined without annotations. +.. option:: --untyped-calls-exclude + + This flag allows to selectively disable :option:`--disallow-untyped-calls` + for functions and methods defined in specific packages, modules, or classes. + Note that each exclude entry acts as a prefix. For example (assuming there + are no type annotations for ``third_party_lib`` available): + + .. code-block:: python + + # mypy --disallow-untyped-calls + # --untyped-calls-exclude=third_party_lib.module_a + # --untyped-calls-exclude=foo.A + from third_party_lib.module_a import some_func + from third_party_lib.module_b import other_func + import foo + + some_func() # OK, function comes from module `third_party_lib.module_a` + other_func() # E: Call to untyped function "other_func" in typed context + + foo.A().meth() # OK, method was defined in class `foo.A` + foo.B().meth() # E: Call to untyped function "meth" in typed context + + # file foo.py + class A: + def meth(self): pass + class B: + def meth(self): pass + .. option:: --disallow-untyped-defs This flag reports an error whenever it encounters a function definition diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 9e79ff99937b..c0798bbf03f1 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -490,7 +490,38 @@ section of the command line docs. :default: False Disallows calling functions without type annotations from functions with type - annotations. + annotations. Note that when used in per-module options, it enables/disables + this check **inside** the module(s) specified, not for functions that come + from that module(s), for example config like this: + + .. code-block:: ini + + [mypy] + disallow_untyped_calls = True + + [mypy-some.library.*] + disallow_untyped_calls = False + + will disable this check inside ``some.library``, not for your code that + imports ``some.library``. If you want to selectively disable this check for + all your code that imports ``some.library`` you should instead use + :confval:`untyped_calls_exclude`, for example: + + .. code-block:: ini + + [mypy] + disallow_untyped_calls = True + untyped_calls_exclude = some.library + +.. confval:: untyped_calls_exclude + + :type: comma-separated list of strings + + Selectively excludes functions and methods defined in specific packages, + modules, and classes from action of :confval:`disallow_untyped_calls`. + This also applies to all submodules of packages (i.e. everything inside + a given prefix). Note, this option does not support per-file configuration, + the exclusions list is defined globally for all your code. .. confval:: disallow_untyped_defs diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9e46d9ee39cb..6df64b32493c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -529,13 +529,6 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> callee_type = get_proper_type( self.accept(e.callee, type_context, always_allow_any=True, is_callee=True) ) - if ( - self.chk.options.disallow_untyped_calls - and self.chk.in_checked_function() - and isinstance(callee_type, CallableType) - and callee_type.implicit - ): - self.msg.untyped_function_call(callee_type, e) # Figure out the full name of the callee for plugin lookup. object_type = None @@ -561,6 +554,22 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> ): member = e.callee.name object_type = self.chk.lookup_type(e.callee.expr) + + if ( + self.chk.options.disallow_untyped_calls + and self.chk.in_checked_function() + and isinstance(callee_type, CallableType) + and callee_type.implicit + ): + if fullname is None and member is not None: + assert object_type is not None + fullname = self.method_fullname(object_type, member) + if not fullname or not any( + fullname == p or fullname.startswith(f"{p}.") + for p in self.chk.options.untyped_calls_exclude + ): + self.msg.untyped_function_call(callee_type, e) + ret_type = self.check_call_expr_with_callee_type( callee_type, e, fullname, object_type, member ) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 47b0bc3acabc..a84f3594a0d2 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -81,6 +81,20 @@ def validate_codes(codes: list[str]) -> list[str]: return codes +def validate_package_allow_list(allow_list: list[str]) -> list[str]: + for p in allow_list: + msg = f"Invalid allow list entry: {p}" + if "*" in p: + raise argparse.ArgumentTypeError( + f"{msg} (entries are already prefixes so must not contain *)" + ) + if "\\" in p or "/" in p: + raise argparse.ArgumentTypeError( + f"{msg} (entries must be packages like foo.bar not directories or files)" + ) + return allow_list + + def expand_path(path: str) -> str: """Expand the user home directory and any environment variables contained within the provided path. @@ -164,6 +178,9 @@ def split_commas(value: str) -> list[str]: "plugins": lambda s: [p.strip() for p in split_commas(s)], "always_true": lambda s: [p.strip() for p in split_commas(s)], "always_false": lambda s: [p.strip() for p in split_commas(s)], + "untyped_calls_exclude": lambda s: validate_package_allow_list( + [p.strip() for p in split_commas(s)] + ), "enable_incomplete_feature": lambda s: [p.strip() for p in split_commas(s)], "disable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]), "enable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]), @@ -187,6 +204,7 @@ def split_commas(value: str) -> list[str]: "plugins": try_split, "always_true": try_split, "always_false": try_split, + "untyped_calls_exclude": lambda s: validate_package_allow_list(try_split(s)), "enable_incomplete_feature": try_split, "disable_error_code": lambda s: validate_codes(try_split(s)), "enable_error_code": lambda s: validate_codes(try_split(s)), diff --git a/mypy/main.py b/mypy/main.py index 6173fd6fc1a8..30f6cfe97455 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -11,7 +11,12 @@ from typing import IO, Any, Final, NoReturn, Sequence, TextIO from mypy import build, defaults, state, util -from mypy.config_parser import get_config_module_names, parse_config_file, parse_version +from mypy.config_parser import ( + get_config_module_names, + parse_config_file, + parse_version, + validate_package_allow_list, +) from mypy.errorcodes import error_codes from mypy.errors import CompileError from mypy.find_sources import InvalidSourceList, create_source_list @@ -675,6 +680,14 @@ def add_invertible_flag( " from functions with type annotations", group=untyped_group, ) + untyped_group.add_argument( + "--untyped-calls-exclude", + metavar="MODULE", + action="append", + default=[], + help="Disable --disallow-untyped-calls for functions/methods coming" + " from specific package, module, or class", + ) add_invertible_flag( "--disallow-untyped-defs", default=False, @@ -1307,6 +1320,8 @@ def set_strict_flags() -> None: % ", ".join(sorted(overlap)) ) + validate_package_allow_list(options.untyped_calls_exclude) + # Process `--enable-error-code` and `--disable-error-code` flags disabled_codes = set(options.disable_error_code) enabled_codes = set(options.enable_error_code) diff --git a/mypy/options.py b/mypy/options.py index 75343acd38bb..9b2e88335b24 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -136,6 +136,10 @@ def __init__(self) -> None: # Disallow calling untyped functions from typed ones self.disallow_untyped_calls = False + # Always allow untyped calls for function coming from modules/packages + # in this list (each item effectively acts as a prefix match) + self.untyped_calls_exclude: list[str] = [] + # Disallow defining untyped (or incompletely typed) functions self.disallow_untyped_defs = False diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index e21157eae991..96f78d81dd16 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2077,6 +2077,61 @@ y = 1 f(reveal_type(y)) # E: Call to untyped function "f" in typed context \ # N: Revealed type is "builtins.int" +[case testDisallowUntypedCallsAllowListFlags] +# flags: --disallow-untyped-calls --untyped-calls-exclude=foo --untyped-calls-exclude=bar.A +from foo import test_foo +from bar import A, B +from baz import test_baz +from foobar import bad + +test_foo(42) # OK +test_baz(42) # E: Call to untyped function "test_baz" in typed context +bad(42) # E: Call to untyped function "bad" in typed context + +a: A +b: B +a.meth() # OK +b.meth() # E: Call to untyped function "meth" in typed context +[file foo.py] +def test_foo(x): pass +[file foobar.py] +def bad(x): pass +[file bar.py] +class A: + def meth(self): pass +class B: + def meth(self): pass +[file baz.py] +def test_baz(x): pass + +[case testDisallowUntypedCallsAllowListConfig] +# flags: --config-file tmp/mypy.ini +from foo import test_foo +from bar import A, B +from baz import test_baz + +test_foo(42) # OK +test_baz(42) # E: Call to untyped function "test_baz" in typed context + +a: A +b: B +a.meth() # OK +b.meth() # E: Call to untyped function "meth" in typed context +[file foo.py] +def test_foo(x): pass +[file bar.py] +class A: + def meth(self): pass +class B: + def meth(self): pass +[file baz.py] +def test_baz(x): pass + +[file mypy.ini] +\[mypy] +disallow_untyped_calls = True +untyped_calls_exclude = foo, bar.A + [case testPerModuleErrorCodes] # flags: --config-file tmp/mypy.ini import tests.foo From 98881d2cbf6b5a410b5eec2971edc80146422bac Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:20:23 -0700 Subject: [PATCH 0189/1617] Add regression test for expand type -> simplified union crash (#15864) See #13431 Authored by ilevkivskyi --- test-data/unit/check-callable.test | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test index 7d25eb271f53..07c42de74bb3 100644 --- a/test-data/unit/check-callable.test +++ b/test-data/unit/check-callable.test @@ -587,3 +587,14 @@ class C(B): def f(self, x: int) -> C: ... class B: ... [builtins fixtures/classmethod.pyi] + +[case testCallableUnionCallback] +from typing import Union, Callable, TypeVar + +TA = TypeVar("TA", bound="A") +class A: + def __call__(self: TA, other: Union[Callable, TA]) -> TA: ... +a: A +a() # E: Missing positional argument "other" in call to "__call__" of "A" +a(a) +a(lambda: None) From edbfdaa802fd6d951026545b0eddcba5494fbb0b Mon Sep 17 00:00:00 2001 From: chylek <1331917+chylek@users.noreply.github.com> Date: Sun, 13 Aug 2023 22:46:58 +0200 Subject: [PATCH 0190/1617] Add option to include docstrings with stubgen (#13284) ### Description Closes #11965. Add a --include-docstrings flag to stubgen. This was suggested in #11965 along with a use case. When using this flag, the .pyi files will include docstrings for Python classes and functions and for C extension functions. The flag is optional and does not change the default stubgen behaviour. When using the flag, the resulting function stubs that contain docstring will no longer be one-liners, but functions without a docstring still retain the default one-liner style. Example input: ```python class A: """class docstring""" def func(): """func docstring""" ... def nodoc(): ... ``` output: ```python class A: """class docstring""" def func() -> None: """func docstring""" ... def nodoc() -> None: ... ``` ## Test Plan Tests `testIncludeDocstrings` and `testIgnoreDocstrings` were added to `test-data/unit/stubgen.test` to ensure the code works as intended. All other tests passed as well. C extension docstrings are tested using an updated bash script `misc/test_stubgenc.sh` with test data in `test-data/pybind11_mypy_demo/stubgen-include-docs` in same fashion as in an already existing test. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- docs/source/stubgen.rst | 5 + misc/test-stubgenc.sh | 30 +++-- mypy/fastparse.py | 4 + mypy/nodes.py | 4 + mypy/options.py | 6 + mypy/stubgen.py | 42 ++++++- mypy/stubgenc.py | 29 +++-- mypy/util.py | 17 +++ test-data/pybind11_mypy_demo/src/main.cpp | 4 +- .../pybind11_mypy_demo/__init__.pyi | 0 .../pybind11_mypy_demo/basics.pyi | 112 ++++++++++++++++++ test-data/unit/stubgen.test | 79 ++++++++++++ 12 files changed, 311 insertions(+), 21 deletions(-) create mode 100644 test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi create mode 100644 test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi diff --git a/docs/source/stubgen.rst b/docs/source/stubgen.rst index f06c9c066bb7..2de0743572e7 100644 --- a/docs/source/stubgen.rst +++ b/docs/source/stubgen.rst @@ -163,6 +163,11 @@ Additional flags Instead, only export imported names that are not referenced in the module that contains the import. +.. option:: --include-docstrings + + Include docstrings in stubs. This will add docstrings to Python function and + classes stubs and to C extension function stubs. + .. option:: --search-path PATH Specify module search directories, separated by colons (only used if diff --git a/misc/test-stubgenc.sh b/misc/test-stubgenc.sh index 7da135f0bf16..7713e1b04e43 100755 --- a/misc/test-stubgenc.sh +++ b/misc/test-stubgenc.sh @@ -3,17 +3,33 @@ set -e set -x -cd "$(dirname $0)/.." +cd "$(dirname "$0")/.." # Install dependencies, demo project and mypy python -m pip install -r test-requirements.txt python -m pip install ./test-data/pybind11_mypy_demo python -m pip install . -# Remove expected stubs and generate new inplace -STUBGEN_OUTPUT_FOLDER=./test-data/pybind11_mypy_demo/stubgen -rm -rf $STUBGEN_OUTPUT_FOLDER/* -stubgen -p pybind11_mypy_demo -o $STUBGEN_OUTPUT_FOLDER +EXIT=0 -# Compare generated stubs to expected ones -git diff --exit-code $STUBGEN_OUTPUT_FOLDER +# performs the stubgenc test +# first argument is the test result folder +# everything else is passed to stubgen as its arguments +function stubgenc_test() { + # Remove expected stubs and generate new inplace + STUBGEN_OUTPUT_FOLDER=./test-data/pybind11_mypy_demo/$1 + rm -rf "${STUBGEN_OUTPUT_FOLDER:?}/*" + stubgen -o "$STUBGEN_OUTPUT_FOLDER" "${@:2}" + + # Compare generated stubs to expected ones + if ! git diff --exit-code "$STUBGEN_OUTPUT_FOLDER"; + then + EXIT=$? + fi +} + +# create stubs without docstrings +stubgenc_test stubgen -p pybind11_mypy_demo +# create stubs with docstrings +stubgenc_test stubgen-include-docs -p pybind11_mypy_demo --include-docstrings +exit $EXIT diff --git a/mypy/fastparse.py b/mypy/fastparse.py index f7a98e9b2b8f..3a26cfe7d6ff 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1008,6 +1008,8 @@ def do_func_def( # FuncDef overrides set_line -- can't use self.set_line func_def.set_line(lineno, n.col_offset, end_line, end_column) retval = func_def + if self.options.include_docstrings: + func_def.docstring = ast3.get_docstring(n, clean=False) self.class_and_function_stack.pop() return retval @@ -1121,6 +1123,8 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: cdef.line = n.lineno cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + if self.options.include_docstrings: + cdef.docstring = ast3.get_docstring(n, clean=False) cdef.column = n.col_offset cdef.end_line = getattr(n, "end_lineno", None) cdef.end_column = getattr(n, "end_col_offset", None) diff --git a/mypy/nodes.py b/mypy/nodes.py index ebd222f4f253..452a4f643255 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -751,6 +751,7 @@ class FuncDef(FuncItem, SymbolNode, Statement): "is_mypy_only", # Present only when a function is decorated with @typing.datasclass_transform or similar "dataclass_transform_spec", + "docstring", ) __match_args__ = ("name", "arguments", "type", "body") @@ -779,6 +780,7 @@ def __init__( # Definitions that appear in if TYPE_CHECKING are marked with this flag. self.is_mypy_only = False self.dataclass_transform_spec: DataclassTransformSpec | None = None + self.docstring: str | None = None @property def name(self) -> str: @@ -1081,6 +1083,7 @@ class ClassDef(Statement): "analyzed", "has_incompatible_baseclass", "deco_line", + "docstring", "removed_statements", ) @@ -1127,6 +1130,7 @@ def __init__( self.has_incompatible_baseclass = False # Used for error reporting (to keep backwad compatibility with pre-3.8) self.deco_line: int | None = None + self.docstring: str | None = None self.removed_statements = [] @property diff --git a/mypy/options.py b/mypy/options.py index 9b2e88335b24..5e451c0aa0a3 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -283,6 +283,12 @@ def __init__(self) -> None: # mypy. (Like mypyc.) self.preserve_asts = False + # If True, function and class docstrings will be extracted and retained. + # This isn't exposed as a command line option + # because it is intended for software integrating with + # mypy. (Like stubgen.) + self.include_docstrings = False + # Paths of user plugins self.plugins: list[str] = [] diff --git a/mypy/stubgen.py b/mypy/stubgen.py index a77ee738d56f..b6fc3e8b7377 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -243,6 +243,7 @@ def __init__( verbose: bool, quiet: bool, export_less: bool, + include_docstrings: bool, ) -> None: # See parse_options for descriptions of the flags. self.pyversion = pyversion @@ -261,6 +262,7 @@ def __init__( self.verbose = verbose self.quiet = quiet self.export_less = export_less + self.include_docstrings = include_docstrings class StubSource: @@ -624,6 +626,7 @@ def __init__( include_private: bool = False, analyzed: bool = False, export_less: bool = False, + include_docstrings: bool = False, ) -> None: # Best known value of __all__. self._all_ = _all_ @@ -638,6 +641,7 @@ def __init__( self._state = EMPTY self._toplevel_names: list[str] = [] self._include_private = include_private + self._include_docstrings = include_docstrings self._current_class: ClassDef | None = None self.import_tracker = ImportTracker() # Was the tree semantically analysed before? @@ -809,7 +813,13 @@ def visit_func_def(self, o: FuncDef) -> None: retfield = " -> " + retname self.add(", ".join(args)) - self.add(f"){retfield}: ...\n") + self.add(f"){retfield}:") + if self._include_docstrings and o.docstring: + docstring = mypy.util.quote_docstring(o.docstring) + self.add(f"\n{self._indent} {docstring}\n") + else: + self.add(" ...\n") + self._state = FUNC def is_none_expr(self, expr: Expression) -> bool: @@ -910,8 +920,11 @@ def visit_class_def(self, o: ClassDef) -> None: if base_types: self.add(f"({', '.join(base_types)})") self.add(":\n") - n = len(self._output) self._indent += " " + if self._include_docstrings and o.docstring: + docstring = mypy.util.quote_docstring(o.docstring) + self.add(f"{self._indent}{docstring}\n") + n = len(self._output) self._vars.append([]) super().visit_class_def(o) self._indent = self._indent[:-4] @@ -920,7 +933,8 @@ def visit_class_def(self, o: ClassDef) -> None: if len(self._output) == n: if self._state == EMPTY_CLASS and sep is not None: self._output[sep] = "" - self._output[-1] = self._output[-1][:-1] + " ...\n" + if not (self._include_docstrings and o.docstring): + self._output[-1] = self._output[-1][:-1] + " ...\n" self._state = EMPTY_CLASS else: self._state = CLASS @@ -1710,6 +1724,7 @@ def mypy_options(stubgen_options: Options) -> MypyOptions: options.show_traceback = True options.transform_source = remove_misplaced_type_comments options.preserve_asts = True + options.include_docstrings = stubgen_options.include_docstrings # Override cache_dir if provided in the environment environ_cache_dir = os.getenv("MYPY_CACHE_DIR", "") @@ -1773,6 +1788,7 @@ def generate_stub_from_ast( parse_only: bool = False, include_private: bool = False, export_less: bool = False, + include_docstrings: bool = False, ) -> None: """Use analysed (or just parsed) AST to generate type stub for single file. @@ -1784,6 +1800,7 @@ def generate_stub_from_ast( include_private=include_private, analyzed=not parse_only, export_less=export_less, + include_docstrings=include_docstrings, ) assert mod.ast is not None, "This function must be used only with analyzed modules" mod.ast.accept(gen) @@ -1845,7 +1862,12 @@ def generate_stubs(options: Options) -> None: files.append(target) with generate_guarded(mod.module, target, options.ignore_errors, options.verbose): generate_stub_from_ast( - mod, target, options.parse_only, options.include_private, options.export_less + mod, + target, + options.parse_only, + options.include_private, + options.export_less, + include_docstrings=options.include_docstrings, ) # Separately analyse C modules using different logic. @@ -1859,7 +1881,11 @@ def generate_stubs(options: Options) -> None: files.append(target) with generate_guarded(mod.module, target, options.ignore_errors, options.verbose): generate_stub_for_c_module( - mod.module, target, known_modules=all_modules, sig_generators=sig_generators + mod.module, + target, + known_modules=all_modules, + sig_generators=sig_generators, + include_docstrings=options.include_docstrings, ) num_modules = len(py_modules) + len(c_modules) if not options.quiet and num_modules > 0: @@ -1913,6 +1939,11 @@ def parse_options(args: list[str]) -> Options: action="store_true", help="don't implicitly export all names imported from other modules in the same package", ) + parser.add_argument( + "--include-docstrings", + action="store_true", + help="include existing docstrings with the stubs", + ) parser.add_argument("-v", "--verbose", action="store_true", help="show more verbose messages") parser.add_argument("-q", "--quiet", action="store_true", help="show fewer messages") parser.add_argument( @@ -1993,6 +2024,7 @@ def parse_options(args: list[str]) -> Options: verbose=ns.verbose, quiet=ns.quiet, export_less=ns.export_less, + include_docstrings=ns.include_docstrings, ) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 8aa1fb3d2c0a..31487f9d0dcf 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -14,6 +14,7 @@ from types import ModuleType from typing import Any, Final, Iterable, Mapping +import mypy.util from mypy.moduleinspect import is_c_module from mypy.stubdoc import ( ArgSig, @@ -169,6 +170,7 @@ def generate_stub_for_c_module( target: str, known_modules: list[str], sig_generators: Iterable[SignatureGenerator], + include_docstrings: bool = False, ) -> None: """Generate stub for C module. @@ -201,6 +203,7 @@ def generate_stub_for_c_module( known_modules=known_modules, imports=imports, sig_generators=sig_generators, + include_docstrings=include_docstrings, ) done.add(name) types: list[str] = [] @@ -216,6 +219,7 @@ def generate_stub_for_c_module( known_modules=known_modules, imports=imports, sig_generators=sig_generators, + include_docstrings=include_docstrings, ) done.add(name) variables = [] @@ -319,15 +323,17 @@ def generate_c_function_stub( self_var: str | None = None, cls: type | None = None, class_name: str | None = None, + include_docstrings: bool = False, ) -> None: """Generate stub for a single function or method. - The result (always a single line) will be appended to 'output'. + The result will be appended to 'output'. If necessary, any required names will be added to 'imports'. The 'class_name' is used to find signature of __init__ or __new__ in 'class_sigs'. """ inferred: list[FunctionSig] | None = None + docstr: str | None = None if class_name: # method: assert cls is not None, "cls should be provided for methods" @@ -379,13 +385,19 @@ def generate_c_function_stub( # a sig generator indicates @classmethod by specifying the cls arg if class_name and signature.args and signature.args[0].name == "cls": output.append("@classmethod") - output.append( - "def {function}({args}) -> {ret}: ...".format( - function=name, - args=", ".join(args), - ret=strip_or_import(signature.ret_type, module, known_modules, imports), - ) + output_signature = "def {function}({args}) -> {ret}:".format( + function=name, + args=", ".join(args), + ret=strip_or_import(signature.ret_type, module, known_modules, imports), ) + if include_docstrings and docstr: + docstr_quoted = mypy.util.quote_docstring(docstr.strip()) + docstr_indented = "\n ".join(docstr_quoted.split("\n")) + output.append(output_signature) + output.extend(f" {docstr_indented}".split("\n")) + else: + output_signature += " ..." + output.append(output_signature) def strip_or_import( @@ -493,6 +505,7 @@ def generate_c_type_stub( known_modules: list[str], imports: list[str], sig_generators: Iterable[SignatureGenerator], + include_docstrings: bool = False, ) -> None: """Generate stub for a single class using runtime introspection. @@ -535,6 +548,7 @@ def generate_c_type_stub( cls=obj, class_name=class_name, sig_generators=sig_generators, + include_docstrings=include_docstrings, ) elif is_c_property(raw_value): generate_c_property_stub( @@ -557,6 +571,7 @@ def generate_c_type_stub( imports=imports, known_modules=known_modules, sig_generators=sig_generators, + include_docstrings=include_docstrings, ) else: attrs.append((attr, value)) diff --git a/mypy/util.py b/mypy/util.py index 8a079c5256bc..d0f2f8c6cc36 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -809,3 +809,20 @@ def plural_s(s: int | Sized) -> str: return "s" else: return "" + + +def quote_docstring(docstr: str) -> str: + """Returns docstring correctly encapsulated in a single or double quoted form.""" + # Uses repr to get hint on the correct quotes and escape everything properly. + # Creating multiline string for prettier output. + docstr_repr = "\n".join(re.split(r"(?<=[^\\])\\n", repr(docstr))) + + if docstr_repr.startswith("'"): + # Enforce double quotes when it's safe to do so. + # That is when double quotes are not in the string + # or when it doesn't end with a single quote. + if '"' not in docstr_repr[1:-1] and docstr_repr[-2] != "'": + return f'"""{docstr_repr[1:-1]}"""' + return f"''{docstr_repr}''" + else: + return f'""{docstr_repr}""' diff --git a/test-data/pybind11_mypy_demo/src/main.cpp b/test-data/pybind11_mypy_demo/src/main.cpp index ff0f93bf7017..00e5b2f4e871 100644 --- a/test-data/pybind11_mypy_demo/src/main.cpp +++ b/test-data/pybind11_mypy_demo/src/main.cpp @@ -119,8 +119,8 @@ void bind_basics(py::module& basics) { using namespace basics; // Functions - basics.def("answer", &answer); - basics.def("sum", &sum); + basics.def("answer", &answer, "answer docstring, with end quote\""); // tests explicit docstrings + basics.def("sum", &sum, "multiline docstring test, edge case quotes \"\"\"'''"); basics.def("midpoint", &midpoint, py::arg("left"), py::arg("right")); basics.def("weighted_midpoint", weighted_midpoint, py::arg("left"), py::arg("right"), py::arg("alpha")=0.5); diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi new file mode 100644 index 000000000000..676d7f6d3f15 --- /dev/null +++ b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi @@ -0,0 +1,112 @@ +from typing import ClassVar + +from typing import overload +PI: float + +class Point: + class AngleUnit: + __members__: ClassVar[dict] = ... # read-only + __entries: ClassVar[dict] = ... + degree: ClassVar[Point.AngleUnit] = ... + radian: ClassVar[Point.AngleUnit] = ... + def __init__(self, value: int) -> None: + """__init__(self: pybind11_mypy_demo.basics.Point.AngleUnit, value: int) -> None""" + def __eq__(self, other: object) -> bool: + """__eq__(self: object, other: object) -> bool""" + def __getstate__(self) -> int: + """__getstate__(self: object) -> int""" + def __hash__(self) -> int: + """__hash__(self: object) -> int""" + def __index__(self) -> int: + """__index__(self: pybind11_mypy_demo.basics.Point.AngleUnit) -> int""" + def __int__(self) -> int: + """__int__(self: pybind11_mypy_demo.basics.Point.AngleUnit) -> int""" + def __ne__(self, other: object) -> bool: + """__ne__(self: object, other: object) -> bool""" + def __setstate__(self, state: int) -> None: + """__setstate__(self: pybind11_mypy_demo.basics.Point.AngleUnit, state: int) -> None""" + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + + class LengthUnit: + __members__: ClassVar[dict] = ... # read-only + __entries: ClassVar[dict] = ... + inch: ClassVar[Point.LengthUnit] = ... + mm: ClassVar[Point.LengthUnit] = ... + pixel: ClassVar[Point.LengthUnit] = ... + def __init__(self, value: int) -> None: + """__init__(self: pybind11_mypy_demo.basics.Point.LengthUnit, value: int) -> None""" + def __eq__(self, other: object) -> bool: + """__eq__(self: object, other: object) -> bool""" + def __getstate__(self) -> int: + """__getstate__(self: object) -> int""" + def __hash__(self) -> int: + """__hash__(self: object) -> int""" + def __index__(self) -> int: + """__index__(self: pybind11_mypy_demo.basics.Point.LengthUnit) -> int""" + def __int__(self) -> int: + """__int__(self: pybind11_mypy_demo.basics.Point.LengthUnit) -> int""" + def __ne__(self, other: object) -> bool: + """__ne__(self: object, other: object) -> bool""" + def __setstate__(self, state: int) -> None: + """__setstate__(self: pybind11_mypy_demo.basics.Point.LengthUnit, state: int) -> None""" + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + angle_unit: ClassVar[Point.AngleUnit] = ... + length_unit: ClassVar[Point.LengthUnit] = ... + x_axis: ClassVar[Point] = ... # read-only + y_axis: ClassVar[Point] = ... # read-only + origin: ClassVar[Point] = ... + x: float + y: float + @overload + def __init__(self) -> None: + """__init__(*args, **kwargs) + Overloaded function. + + 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None + + 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None""" + @overload + def __init__(self, x: float, y: float) -> None: + """__init__(*args, **kwargs) + Overloaded function. + + 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None + + 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None""" + @overload + def distance_to(self, x: float, y: float) -> float: + """distance_to(*args, **kwargs) + Overloaded function. + + 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float + + 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float""" + @overload + def distance_to(self, other: Point) -> float: + """distance_to(*args, **kwargs) + Overloaded function. + + 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float + + 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float""" + @property + def length(self) -> float: ... + +def answer() -> int: + '''answer() -> int + + answer docstring, with end quote"''' +def midpoint(left: float, right: float) -> float: + """midpoint(left: float, right: float) -> float""" +def sum(arg0: int, arg1: int) -> int: + '''sum(arg0: int, arg1: int) -> int + + multiline docstring test, edge case quotes """\'\'\'''' +def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: + """weighted_midpoint(left: float, right: float, alpha: float = 0.5) -> float""" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index f6b71a994153..774a17b76161 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -3183,6 +3183,85 @@ def f2(): def f1(): ... def f2(): ... +[case testIncludeDocstrings] +# flags: --include-docstrings +class A: + """class docstring + + a multiline docstring""" + def func(): + """func docstring + don't forget to indent""" + ... + def nodoc(): + ... +class B: + def quoteA(): + '''func docstring with quotes"""\\n + and an end quote\'''' + ... + def quoteB(): + '''func docstring with quotes""" + \'\'\' + and an end quote\\"''' + ... + def quoteC(): + """func docstring with end quote\\\"""" + ... + def quoteD(): + r'''raw with quotes\"''' + ... +[out] +class A: + """class docstring + + a multiline docstring""" + def func() -> None: + """func docstring + don't forget to indent""" + def nodoc() -> None: ... + +class B: + def quoteA() -> None: + '''func docstring with quotes"""\\n + and an end quote\'''' + def quoteB() -> None: + '''func docstring with quotes""" + \'\'\' + and an end quote\\"''' + def quoteC() -> None: + '''func docstring with end quote\\"''' + def quoteD() -> None: + '''raw with quotes\\"''' + +[case testIgnoreDocstrings] +class A: + """class docstring + + a multiline docstring""" + def func(): + """func docstring + + don't forget to indent""" + def nodoc(): + ... + +class B: + def func(): + """func docstring""" + ... + def nodoc(): + ... + +[out] +class A: + def func() -> None: ... + def nodoc() -> None: ... + +class B: + def func() -> None: ... + def nodoc() -> None: ... + [case testKnownMagicMethodsReturnTypes] class Some: def __len__(self): ... From 11a94be6f408d5bce391a1ec3931ce7197ca207b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 14 Aug 2023 13:58:52 +0100 Subject: [PATCH 0191/1617] Add regression test for fixed bug involving bytes formatting (#15867) Adds a regression test for #12665, which is a strange bug that was fixed somewhat by accident --- test-data/unit/check-formatting.test | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index 7d23c2e199f1..75651124b76f 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -484,6 +484,23 @@ class D(bytes): '{}'.format(D()) [builtins fixtures/primitives.pyi] +[case testNoSpuriousFormattingErrorsDuringFailedOverlodMatch] +from typing import overload, Callable + +@overload +def sub(pattern: str, repl: Callable[[str], str]) -> str: ... +@overload +def sub(pattern: bytes, repl: Callable[[bytes], bytes]) -> bytes: ... +def sub(pattern: object, repl: object) -> object: + pass + +def better_snakecase(text: str) -> str: + # Mypy used to emit a spurious error here + # warning about interpolating bytes into an f-string: + text = sub(r"([A-Z])([A-Z]+)([A-Z](?:[^A-Z]|$))", lambda match: f"{match}") + return text +[builtins fixtures/primitives.pyi] + [case testFormatCallFinal] from typing_extensions import Final From a1fcad5bd6a6f71fac6f1a2f235302b2172ddd7d Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 14 Aug 2023 21:54:09 +0100 Subject: [PATCH 0192/1617] Add missing type annotations to the `primitives.pyi` fixture (#15871) This fixes some weird test failures I was seeing locally when trying to run just the tests in `check-enum.test` (invoked via `pytest mypy/test/testcheck.py::TypeCheckSuite::check-enum.test`) --- test-data/unit/fixtures/primitives.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index c9b1e3f4e983..63128a8ae03d 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -12,7 +12,7 @@ class object: def __ne__(self, other: object) -> bool: pass class type: - def __init__(self, x) -> None: pass + def __init__(self, x: object) -> None: pass class int: # Note: this is a simplification of the actual signature @@ -30,7 +30,7 @@ class str(Sequence[str]): def __iter__(self) -> Iterator[str]: pass def __contains__(self, other: object) -> bool: pass def __getitem__(self, item: int) -> str: pass - def format(self, *args, **kwargs) -> str: pass + def format(self, *args: object, **kwargs: object) -> str: pass class bytes(Sequence[int]): def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass From 854a9f8f82a6dae085d3514897961871fe7005b1 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 14 Aug 2023 22:05:56 +0100 Subject: [PATCH 0193/1617] Allow None vs TypeVar overlap for overloads (#15846) Fixes #8881 This is technically unsafe, and I remember we explicitly discussed this a while ago, but related use cases turn out to be more common than I expected (judging by how popular the issue is). Also the fix is really simple. --------- Co-authored-by: Ivan Levkivskyi Co-authored-by: Alex Waygood --- mypy/checker.py | 24 ++++++-- mypy/checkexpr.py | 86 +++++++++++++++++++++------ mypy/subtypes.py | 15 ++++- test-data/unit/check-overloading.test | 39 ++++++++++-- 4 files changed, 135 insertions(+), 29 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b786155079e5..3bd9c494a890 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7216,22 +7216,32 @@ def is_unsafe_overlapping_overload_signatures( # # This discrepancy is unfortunately difficult to get rid of, so we repeat the # checks twice in both directions for now. + # + # Note that we ignore possible overlap between type variables and None. This + # is technically unsafe, but unsafety is tiny and this prevents some common + # use cases like: + # @overload + # def foo(x: None) -> None: .. + # @overload + # def foo(x: T) -> Foo[T]: ... return is_callable_compatible( signature, other, - is_compat=is_overlapping_types_no_promote_no_uninhabited, + is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, is_compat_return=lambda l, r: not is_subtype_no_promote(l, r), ignore_return=False, check_args_covariantly=True, allow_partial_overlap=True, + no_unify_none=True, ) or is_callable_compatible( other, signature, - is_compat=is_overlapping_types_no_promote_no_uninhabited, + is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, is_compat_return=lambda l, r: not is_subtype_no_promote(r, l), ignore_return=False, check_args_covariantly=False, allow_partial_overlap=True, + no_unify_none=True, ) @@ -7717,12 +7727,18 @@ def is_subtype_no_promote(left: Type, right: Type) -> bool: return is_subtype(left, right, ignore_promotions=True) -def is_overlapping_types_no_promote_no_uninhabited(left: Type, right: Type) -> bool: +def is_overlapping_types_no_promote_no_uninhabited_no_none(left: Type, right: Type) -> bool: # For the purpose of unsafe overload checks we consider list[] and list[int] # non-overlapping. This is consistent with how we treat list[int] and list[str] as # non-overlapping, despite [] belongs to both. Also this will prevent false positives # for failed type inference during unification. - return is_overlapping_types(left, right, ignore_promotions=True, ignore_uninhabited=True) + return is_overlapping_types( + left, + right, + ignore_promotions=True, + ignore_uninhabited=True, + prohibit_none_typevar_overlap=True, + ) def is_private(node_name: str) -> bool: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 6df64b32493c..d00bbb288f3e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2409,6 +2409,11 @@ def check_overload_call( # typevar. See https://github.com/python/mypy/issues/4063 for related discussion. erased_targets: list[CallableType] | None = None unioned_result: tuple[Type, Type] | None = None + + # Determine whether we need to encourage union math. This should be generally safe, + # as union math infers better results in the vast majority of cases, but it is very + # computationally intensive. + none_type_var_overlap = self.possible_none_type_var_overlap(arg_types, plausible_targets) union_interrupted = False # did we try all union combinations? if any(self.real_union(arg) for arg in arg_types): try: @@ -2421,6 +2426,7 @@ def check_overload_call( arg_names, callable_name, object_type, + none_type_var_overlap, context, ) except TooManyUnions: @@ -2453,8 +2459,10 @@ def check_overload_call( # If any of checks succeed, stop early. if inferred_result is not None and unioned_result is not None: # Both unioned and direct checks succeeded, choose the more precise type. - if is_subtype(inferred_result[0], unioned_result[0]) and not isinstance( - get_proper_type(inferred_result[0]), AnyType + if ( + is_subtype(inferred_result[0], unioned_result[0]) + and not isinstance(get_proper_type(inferred_result[0]), AnyType) + and not none_type_var_overlap ): return inferred_result return unioned_result @@ -2504,7 +2512,8 @@ def check_overload_call( callable_name=callable_name, object_type=object_type, ) - if union_interrupted: + # Do not show the extra error if the union math was forced. + if union_interrupted and not none_type_var_overlap: self.chk.fail(message_registry.TOO_MANY_UNION_COMBINATIONS, context) return result @@ -2659,6 +2668,44 @@ def overload_erased_call_targets( matches.append(typ) return matches + def possible_none_type_var_overlap( + self, arg_types: list[Type], plausible_targets: list[CallableType] + ) -> bool: + """Heuristic to determine whether we need to try forcing union math. + + This is needed to avoid greedy type variable match in situations like this: + @overload + def foo(x: None) -> None: ... + @overload + def foo(x: T) -> list[T]: ... + + x: int | None + foo(x) + we want this call to infer list[int] | None, not list[int | None]. + """ + if not plausible_targets or not arg_types: + return False + has_optional_arg = False + for arg_type in get_proper_types(arg_types): + if not isinstance(arg_type, UnionType): + continue + for item in get_proper_types(arg_type.items): + if isinstance(item, NoneType): + has_optional_arg = True + break + if not has_optional_arg: + return False + + min_prefix = min(len(c.arg_types) for c in plausible_targets) + for i in range(min_prefix): + if any( + isinstance(get_proper_type(c.arg_types[i]), NoneType) for c in plausible_targets + ) and any( + isinstance(get_proper_type(c.arg_types[i]), TypeVarType) for c in plausible_targets + ): + return True + return False + def union_overload_result( self, plausible_targets: list[CallableType], @@ -2668,6 +2715,7 @@ def union_overload_result( arg_names: Sequence[str | None] | None, callable_name: str | None, object_type: Type | None, + none_type_var_overlap: bool, context: Context, level: int = 0, ) -> list[tuple[Type, Type]] | None: @@ -2707,20 +2755,23 @@ def union_overload_result( # Step 3: Try a direct match before splitting to avoid unnecessary union splits # and save performance. - with self.type_overrides_set(args, arg_types): - direct = self.infer_overload_return_type( - plausible_targets, - args, - arg_types, - arg_kinds, - arg_names, - callable_name, - object_type, - context, - ) - if direct is not None and not isinstance(get_proper_type(direct[0]), (UnionType, AnyType)): - # We only return non-unions soon, to avoid greedy match. - return [direct] + if not none_type_var_overlap: + with self.type_overrides_set(args, arg_types): + direct = self.infer_overload_return_type( + plausible_targets, + args, + arg_types, + arg_kinds, + arg_names, + callable_name, + object_type, + context, + ) + if direct is not None and not isinstance( + get_proper_type(direct[0]), (UnionType, AnyType) + ): + # We only return non-unions soon, to avoid greedy match. + return [direct] # Step 4: Split the first remaining union type in arguments into items and # try to match each item individually (recursive). @@ -2738,6 +2789,7 @@ def union_overload_result( arg_names, callable_name, object_type, + none_type_var_overlap, context, level + 1, ) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 5712d7375e50..da92f7398d4e 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1299,6 +1299,7 @@ def is_callable_compatible( check_args_covariantly: bool = False, allow_partial_overlap: bool = False, strict_concatenate: bool = False, + no_unify_none: bool = False, ) -> bool: """Is the left compatible with the right, using the provided compatibility check? @@ -1415,7 +1416,9 @@ def g(x: int) -> int: ... # (below) treats type variables on the two sides as independent. if left.variables: # Apply generic type variables away in left via type inference. - unified = unify_generic_callable(left, right, ignore_return=ignore_return) + unified = unify_generic_callable( + left, right, ignore_return=ignore_return, no_unify_none=no_unify_none + ) if unified is None: return False left = unified @@ -1427,7 +1430,9 @@ def g(x: int) -> int: ... # So, we repeat the above checks in the opposite direction. This also # lets us preserve the 'symmetry' property of allow_partial_overlap. if allow_partial_overlap and right.variables: - unified = unify_generic_callable(right, left, ignore_return=ignore_return) + unified = unify_generic_callable( + right, left, ignore_return=ignore_return, no_unify_none=no_unify_none + ) if unified is not None: right = unified @@ -1687,6 +1692,8 @@ def unify_generic_callable( target: NormalizedCallableType, ignore_return: bool, return_constraint_direction: int | None = None, + *, + no_unify_none: bool = False, ) -> NormalizedCallableType | None: """Try to unify a generic callable type with another callable type. @@ -1708,6 +1715,10 @@ def unify_generic_callable( type.ret_type, target.ret_type, return_constraint_direction ) constraints.extend(c) + if no_unify_none: + constraints = [ + c for c in constraints if not isinstance(get_proper_type(c.target), NoneType) + ] inferred_vars, _ = mypy.solve.solve_constraints(type.variables, constraints) if None in inferred_vars: return None diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 50acd7d77c8c..4910dfe05d31 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -2185,36 +2185,63 @@ def bar2(*x: int) -> int: ... [builtins fixtures/tuple.pyi] [case testOverloadDetectsPossibleMatchesWithGenerics] -from typing import overload, TypeVar, Generic +# flags: --strict-optional +from typing import overload, TypeVar, Generic, Optional, List T = TypeVar('T') +# The examples below are unsafe, but it is a quite common pattern +# so we ignore the possibility of type variables taking value `None` +# for the purpose of overload overlap checks. @overload -def foo(x: None, y: None) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo(x: None, y: None) -> str: ... @overload def foo(x: T, y: T) -> int: ... def foo(x): ... +oi: Optional[int] +reveal_type(foo(None, None)) # N: Revealed type is "builtins.str" +reveal_type(foo(None, 42)) # N: Revealed type is "builtins.int" +reveal_type(foo(42, 42)) # N: Revealed type is "builtins.int" +reveal_type(foo(oi, None)) # N: Revealed type is "Union[builtins.int, builtins.str]" +reveal_type(foo(oi, 42)) # N: Revealed type is "builtins.int" +reveal_type(foo(oi, oi)) # N: Revealed type is "Union[builtins.int, builtins.str]" + +@overload +def foo_list(x: None) -> None: ... +@overload +def foo_list(x: T) -> List[T]: ... +def foo_list(x): ... + +reveal_type(foo_list(oi)) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + # What if 'T' is 'object'? @overload -def bar(x: None, y: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def bar(x: None, y: int) -> str: ... @overload def bar(x: T, y: T) -> int: ... def bar(x, y): ... class Wrapper(Generic[T]): @overload - def foo(self, x: None, y: None) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def foo(self, x: None, y: None) -> str: ... @overload def foo(self, x: T, y: None) -> int: ... def foo(self, x): ... @overload - def bar(self, x: None, y: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def bar(self, x: None, y: int) -> str: ... @overload def bar(self, x: T, y: T) -> int: ... def bar(self, x, y): ... +@overload +def baz(x: str, y: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +@overload +def baz(x: T, y: T) -> int: ... +def baz(x): ... +[builtins fixtures/tuple.pyi] + [case testOverloadFlagsPossibleMatches] from wrapper import * [file wrapper.pyi] @@ -3996,7 +4023,7 @@ T = TypeVar('T') class FakeAttribute(Generic[T]): @overload - def dummy(self, instance: None, owner: Type[T]) -> 'FakeAttribute[T]': ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def dummy(self, instance: None, owner: Type[T]) -> 'FakeAttribute[T]': ... @overload def dummy(self, instance: T, owner: Type[T]) -> int: ... def dummy(self, instance: Optional[T], owner: Type[T]) -> Union['FakeAttribute[T]', int]: ... From b49be105d2940e3a0607f5ec76f519931b0d0a08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 18:09:10 -0700 Subject: [PATCH 0194/1617] Sync typeshed (#15873) Source commit: https://github.com/python/typeshed/commit/74aac1aa891cbb568b124c955010b19d40f9fda7 --- mypy/typeshed/stdlib/asyncio/base_events.pyi | 47 ++++++++++++++++++- mypy/typeshed/stdlib/asyncio/constants.pyi | 2 + mypy/typeshed/stdlib/asyncio/events.pyi | 2 + mypy/typeshed/stdlib/asyncio/streams.pyi | 11 ++++- mypy/typeshed/stdlib/asyncio/tasks.pyi | 23 ++++++++- mypy/typeshed/stdlib/enum.pyi | 10 ++-- .../stdlib/importlib/metadata/__init__.pyi | 1 + mypy/typeshed/stdlib/logging/__init__.pyi | 18 ++++++- mypy/typeshed/stdlib/socket.pyi | 1 + mypy/typeshed/stdlib/sre_parse.pyi | 24 ++++++++-- mypy/typeshed/stdlib/ssl.pyi | 41 +++++++++++----- mypy/typeshed/stdlib/turtle.pyi | 7 +++ mypy/typeshed/stdlib/typing.pyi | 10 +++- mypy/typeshed/stdlib/typing_extensions.pyi | 10 +++- 14 files changed, 175 insertions(+), 32 deletions(-) diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index 9924f728f6ea..e2b55da8c718 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -107,7 +107,48 @@ class BaseEventLoop(AbstractEventLoop): flags: int = 0, ) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... async def getnameinfo(self, sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int = 0) -> tuple[str, str]: ... - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 12): + @overload + async def create_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + host: str = ..., + port: int = ..., + *, + ssl: _SSLContext = None, + family: int = 0, + proto: int = 0, + flags: int = 0, + sock: None = None, + local_addr: tuple[str, int] | None = None, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + happy_eyeballs_delay: float | None = None, + interleave: int | None = None, + all_errors: bool = False, + ) -> tuple[Transport, _ProtocolT]: ... + @overload + async def create_connection( + self, + protocol_factory: Callable[[], _ProtocolT], + host: None = None, + port: None = None, + *, + ssl: _SSLContext = None, + family: int = 0, + proto: int = 0, + flags: int = 0, + sock: socket, + local_addr: None = None, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + happy_eyeballs_delay: float | None = None, + interleave: int | None = None, + all_errors: bool = False, + ) -> tuple[Transport, _ProtocolT]: ... + elif sys.version_info >= (3, 11): @overload async def create_connection( self, @@ -426,5 +467,7 @@ class BaseEventLoop(AbstractEventLoop): # Debug flag management. def get_debug(self) -> bool: ... def set_debug(self, enabled: bool) -> None: ... - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 12): + async def shutdown_default_executor(self, timeout: float | None = None) -> None: ... + elif sys.version_info >= (3, 9): async def shutdown_default_executor(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/constants.pyi b/mypy/typeshed/stdlib/asyncio/constants.pyi index af209fa9ee62..60d8529209c2 100644 --- a/mypy/typeshed/stdlib/asyncio/constants.pyi +++ b/mypy/typeshed/stdlib/asyncio/constants.pyi @@ -11,6 +11,8 @@ if sys.version_info >= (3, 11): SSL_SHUTDOWN_TIMEOUT: float FLOW_CONTROL_HIGH_WATER_SSL_READ: Literal[256] FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Literal[512] +if sys.version_info >= (3, 12): + THREAD_JOIN_TIMEOUT: Literal[300] class _SendfileMode(enum.Enum): UNSUPPORTED: int diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index b1b0fcfa5fd7..cde63b279b0d 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -76,6 +76,8 @@ class Handle: def cancel(self) -> None: ... def _run(self) -> None: ... def cancelled(self) -> bool: ... + if sys.version_info >= (3, 12): + def get_context(self) -> Context: ... class TimerHandle(Handle): def __init__( diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index f30c57305d93..804be1ca5065 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -148,7 +148,16 @@ class StreamWriter: async def wait_closed(self) -> None: ... def get_extra_info(self, name: str, default: Any = None) -> Any: ... async def drain(self) -> None: ... - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 12): + async def start_tls( + self, + sslcontext: ssl.SSLContext, + *, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> None: ... + elif sys.version_info >= (3, 11): async def start_tls( self, sslcontext: ssl.SSLContext, *, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None ) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index d8c101f281fc..5ea30d3791de 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -285,7 +285,26 @@ else: # since the only reason why `asyncio.Future` is invariant is the `set_result()` method, # and `asyncio.Task.set_result()` always raises. class Task(Future[_T_co], Generic[_T_co]): # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues] - if sys.version_info >= (3, 8): + if sys.version_info >= (3, 12): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None, + context: Context | None = None, + eager_start: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None, + context: Context | None = None, + ) -> None: ... + elif sys.version_info >= (3, 8): def __init__( self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... ) -> None: ... @@ -295,6 +314,8 @@ class Task(Future[_T_co], Generic[_T_co]): # type: ignore[type-var] # pyright: def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... def get_name(self) -> str: ... def set_name(self, __value: object) -> None: ... + if sys.version_info >= (3, 12): + def get_context(self) -> Context: ... def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ... def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 60cc27215fd0..a8ba7bf157c2 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -2,7 +2,6 @@ import _typeshed import sys import types from _typeshed import SupportsKeysAndGetItem, Unused -from abc import ABCMeta from builtins import property as _builtins_property from collections.abc import Callable, Iterable, Iterator, Mapping from typing import Any, Generic, TypeVar, overload @@ -76,12 +75,8 @@ class _EnumDict(dict[str, Any]): @overload def update(self, members: Iterable[tuple[str, Any]], **more_members: Any) -> None: ... -# Note: EnumMeta actually subclasses type directly, not ABCMeta. -# This is a temporary workaround to allow multiple creation of enums with builtins -# such as str as mixins, which due to the handling of ABCs of builtin types, cause -# spurious inconsistent metaclass structure. See #1595. # Structurally: Iterable[T], Reversible[T], Container[T] where T is the enum itself -class EnumMeta(ABCMeta): +class EnumMeta(type): if sys.version_info >= (3, 11): def __new__( metacls: type[_typeshed.Self], @@ -193,6 +188,9 @@ class Enum(metaclass=EnumMeta): def __hash__(self) -> int: ... def __format__(self, format_spec: str) -> str: ... def __reduce_ex__(self, proto: Unused) -> tuple[Any, ...]: ... + if sys.version_info >= (3, 12): + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any) -> Self: ... if sys.version_info >= (3, 11): class ReprEnum(Enum): ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 0af33bc876c4..0f8a6f56cf88 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -180,6 +180,7 @@ class MetadataPathFinder(DistributionFinder): def invalidate_caches(cls) -> None: ... class PathDistribution(Distribution): + _path: Path def __init__(self, path: Path) -> None: ... def read_text(self, filename: StrPath) -> str: ... def locate_file(self, path: StrPath) -> PathLike[str]: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 6ebd305aacb8..db797d4180ea 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -60,6 +60,8 @@ __all__ = [ if sys.version_info >= (3, 11): __all__ += ["getLevelNamesMapping"] +if sys.version_info >= (3, 12): + __all__ += ["getHandlerByName", "getHandlerNames"] _SysExcInfoType: TypeAlias = tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] _ExcInfoType: TypeAlias = None | bool | _SysExcInfoType | BaseException @@ -83,7 +85,10 @@ class Filterer: filters: list[_FilterType] def addFilter(self, filter: _FilterType) -> None: ... def removeFilter(self, filter: _FilterType) -> None: ... - def filter(self, record: LogRecord) -> bool: ... + if sys.version_info >= (3, 12): + def filter(self, record: LogRecord) -> bool | LogRecord: ... + else: + def filter(self, record: LogRecord) -> bool: ... class Manager: # undocumented root: RootLogger @@ -111,6 +116,8 @@ class Logger(Filterer): def isEnabledFor(self, level: int) -> bool: ... def getEffectiveLevel(self) -> int: ... def getChild(self, suffix: str) -> Self: ... # see python/typing#980 + if sys.version_info >= (3, 12): + def getChildren(self) -> set[Logger]: ... if sys.version_info >= (3, 8): def debug( self, @@ -324,6 +331,10 @@ class Handler(Filterer): def format(self, record: LogRecord) -> str: ... def emit(self, record: LogRecord) -> None: ... +if sys.version_info >= (3, 12): + def getHandlerByName(name: str) -> Handler | None: ... + def getHandlerNames() -> frozenset[str]: ... + class Formatter: converter: Callable[[float | None], struct_time] _fmt: str | None # undocumented @@ -370,7 +381,10 @@ class Filter: name: str # undocumented nlen: int # undocumented def __init__(self, name: str = "") -> None: ... - def filter(self, record: LogRecord) -> bool: ... + if sys.version_info >= (3, 12): + def filter(self, record: LogRecord) -> bool | LogRecord: ... + else: + def filter(self, record: LogRecord) -> bool: ... class LogRecord: # args can be set to None by logging.handlers.QueueHandler diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index 5dd92ec8e116..da06ce2c2b06 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -201,6 +201,7 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_LINGER2 as TCP_LINGER2, TCP_QUICKACK as TCP_QUICKACK, TCP_SYNCNT as TCP_SYNCNT, + TCP_USER_TIMEOUT as TCP_USER_TIMEOUT, TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP, ) if sys.platform != "win32": diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi index 56f10bb41d57..8ef65223dc34 100644 --- a/mypy/typeshed/stdlib/sre_parse.pyi +++ b/mypy/typeshed/stdlib/sre_parse.pyi @@ -87,25 +87,39 @@ class Tokenizer: def seek(self, index: int) -> None: ... def error(self, msg: str, offset: int = 0) -> _Error: ... - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 12): + def checkgroupname(self, name: str, offset: int) -> None: ... + elif sys.version_info >= (3, 11): def checkgroupname(self, name: str, offset: int, nested: int) -> None: ... def fix_flags(src: str | bytes, flags: int) -> int: ... _TemplateType: TypeAlias = tuple[list[tuple[int, int]], list[str | None]] _TemplateByteType: TypeAlias = tuple[list[tuple[int, int]], list[bytes | None]] -if sys.version_info >= (3, 8): - def parse(str: str, flags: int = 0, state: State | None = None) -> SubPattern: ... + +if sys.version_info >= (3, 12): + @overload + def parse_template(source: str, pattern: _Pattern[Any]) -> _TemplateType: ... + @overload + def parse_template(source: bytes, pattern: _Pattern[Any]) -> _TemplateByteType: ... + +elif sys.version_info >= (3, 8): @overload def parse_template(source: str, state: _Pattern[Any]) -> _TemplateType: ... @overload def parse_template(source: bytes, state: _Pattern[Any]) -> _TemplateByteType: ... else: - def parse(str: str, flags: int = 0, pattern: Pattern | None = None) -> SubPattern: ... @overload def parse_template(source: str, pattern: _Pattern[Any]) -> _TemplateType: ... @overload def parse_template(source: bytes, pattern: _Pattern[Any]) -> _TemplateByteType: ... -def expand_template(template: _TemplateType, match: Match[Any]) -> str: ... +if sys.version_info >= (3, 8): + def parse(str: str, flags: int = 0, state: State | None = None) -> SubPattern: ... + +else: + def parse(str: str, flags: int = 0, pattern: Pattern | None = None) -> SubPattern: ... + +if sys.version_info < (3, 12): + def expand_template(template: _TemplateType, match: Match[Any]) -> str: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 446bbf8d1009..dd7285196ed9 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -44,18 +44,20 @@ class SSLCertVerificationError(SSLError, ValueError): CertificateError = SSLCertVerificationError -def wrap_socket( - sock: socket.socket, - keyfile: StrOrBytesPath | None = None, - certfile: StrOrBytesPath | None = None, - server_side: bool = False, - cert_reqs: int = ..., - ssl_version: int = ..., - ca_certs: str | None = None, - do_handshake_on_connect: bool = True, - suppress_ragged_eofs: bool = True, - ciphers: str | None = None, -) -> SSLSocket: ... +if sys.version_info < (3, 12): + def wrap_socket( + sock: socket.socket, + keyfile: StrOrBytesPath | None = None, + certfile: StrOrBytesPath | None = None, + server_side: bool = False, + cert_reqs: int = ..., + ssl_version: int = ..., + ca_certs: str | None = None, + do_handshake_on_connect: bool = True, + suppress_ragged_eofs: bool = True, + ciphers: str | None = None, + ) -> SSLSocket: ... + def create_default_context( purpose: Purpose = ..., *, @@ -95,7 +97,10 @@ else: _create_default_https_context: Callable[..., SSLContext] def RAND_bytes(__n: int) -> bytes: ... -def RAND_pseudo_bytes(__n: int) -> tuple[bytes, bool]: ... + +if sys.version_info < (3, 12): + def RAND_pseudo_bytes(__n: int) -> tuple[bytes, bool]: ... + def RAND_status() -> bool: ... def RAND_egd(path: str) -> None: ... def RAND_add(__string: str | ReadableBuffer, __entropy: float) -> None: ... @@ -198,6 +203,11 @@ class Options(enum.IntFlag): OP_ENABLE_MIDDLEBOX_COMPAT: int if sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: int + if sys.version_info >= (3, 12): + OP_LEGACY_SERVER_CONNECT: int + if sys.version_info >= (3, 12) and sys.platform != "linux": + OP_ENABLE_KTLS: int + OP_IGNORE_UNEXPECTED_EOF: int OP_ALL: Options OP_NO_SSLv2: Options @@ -216,6 +226,11 @@ if sys.version_info >= (3, 8): OP_ENABLE_MIDDLEBOX_COMPAT: Options if sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: Options +if sys.version_info >= (3, 12): + OP_LEGACY_SERVER_CONNECT: Options +if sys.version_info >= (3, 12) and sys.platform != "linux": + OP_ENABLE_KTLS: Options + OP_IGNORE_UNEXPECTED_EOF: Options HAS_NEVER_CHECK_COMMON_NAME: bool HAS_SSLv2: bool diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 5df3e4b90cb5..80ea40879dee 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Callable, Sequence from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar from typing import Any, ClassVar, overload @@ -249,6 +250,9 @@ class TNavigator: def reset(self) -> None: ... def degrees(self, fullcircle: float = 360.0) -> None: ... def radians(self) -> None: ... + if sys.version_info >= (3, 12): + def teleport(self, x: float | None = None, y: float | None = None, *, fill_gap: bool = False) -> None: ... + def forward(self, distance: float) -> None: ... def back(self, distance: float) -> None: ... def right(self, angle: float) -> None: ... @@ -321,6 +325,9 @@ class TPen: def color(self, r: float, g: float, b: float) -> None: ... @overload def color(self, color1: _Color, color2: _Color) -> None: ... + if sys.version_info >= (3, 12): + def teleport(self, x: float | None = None, y: float | None = None, *, fill_gap: bool = False) -> None: ... + def showturtle(self) -> None: ... def hideturtle(self) -> None: ... def isvisible(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 6a307368642f..a9bffdf5214f 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -900,8 +900,16 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): def keys(self) -> dict_keys[str, object]: ... def values(self) -> dict_values[str, object]: ... if sys.version_info >= (3, 9): + @overload def __or__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... - def __ior__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... + @overload + def __or__(self, __value: dict[str, Any]) -> dict[str, object]: ... + @overload + def __ror__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... + @overload + def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... + # supposedly incompatible definitions of __or__ and __ior__ + def __ior__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... # type: ignore[misc] @_final class ForwardRef: diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index efcc13e42047..9320dc50b6bb 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -233,8 +233,16 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): def values(self) -> dict_values[str, object]: ... def __delitem__(self, k: Never) -> None: ... if sys.version_info >= (3, 9): + @overload def __or__(self, __value: Self) -> Self: ... - def __ior__(self, __value: Self) -> Self: ... + @overload + def __or__(self, __value: dict[str, Any]) -> dict[str, object]: ... + @overload + def __ror__(self, __value: Self) -> Self: ... + @overload + def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... + # supposedly incompatible definitions of `__ior__` and `__or__`: + def __ior__(self, __value: Self) -> Self: ... # type: ignore[misc] # TypedDict is a (non-subscriptable) special form. TypedDict: object From fda7a460485cb856c595d4d0593a0ec6c0fe03e9 Mon Sep 17 00:00:00 2001 From: Albert Tugushev Date: Tue, 15 Aug 2023 17:40:37 +0200 Subject: [PATCH 0195/1617] Fix all the missing references found within the docs (#15875) Fixes #13196. Enable the nit-picky mode on sphinx-build in tox, as this will facilitate the detection of potential issues related to missing references. --- docs/source/error_code_list.rst | 2 +- docs/source/more_types.rst | 18 +++++++++--------- docs/source/runtime_troubles.rst | 4 ++-- tox.ini | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 157f90249af8..1f75ac54d525 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -835,7 +835,7 @@ ellipsis ``...``, a docstring, and a ``raise NotImplementedError`` statement. Check the target of NewType [valid-newtype] ------------------------------------------- -The target of a :py:func:`NewType ` definition must be a class type. It can't +The target of a :py:class:`~typing.NewType` definition must be a class type. It can't be a union type, ``Any``, or various other special types. You can also get this error if the target has been imported from a diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 542ff1c57c71..4e6e9204fdca 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -2,7 +2,7 @@ More types ========== This section introduces a few additional kinds of types, including :py:data:`~typing.NoReturn`, -:py:func:`NewType `, and types for async code. It also discusses +:py:class:`~typing.NewType`, and types for async code. It also discusses how to give functions more precise types using overloads. All of these are only situationally useful, so feel free to skip this section and come back when you have a need for some of them. @@ -11,7 +11,7 @@ Here's a quick summary of what's covered here: * :py:data:`~typing.NoReturn` lets you tell mypy that a function never returns normally. -* :py:func:`NewType ` lets you define a variant of a type that is treated as a +* :py:class:`~typing.NewType` lets you define a variant of a type that is treated as a separate type by mypy but is identical to the original type at runtime. For example, you can have ``UserId`` as a variant of ``int`` that is just an ``int`` at runtime. @@ -75,7 +75,7 @@ certain values from base class instances. Example: ... However, this approach introduces some runtime overhead. To avoid this, the typing -module provides a helper object :py:func:`NewType ` that creates simple unique types with +module provides a helper object :py:class:`~typing.NewType` that creates simple unique types with almost zero runtime overhead. Mypy will treat the statement ``Derived = NewType('Derived', Base)`` as being roughly equivalent to the following definition: @@ -113,12 +113,12 @@ implicitly casting from ``UserId`` where ``int`` is expected. Examples: num: int = UserId(5) + 1 -:py:func:`NewType ` accepts exactly two arguments. The first argument must be a string literal +:py:class:`~typing.NewType` accepts exactly two arguments. The first argument must be a string literal containing the name of the new type and must equal the name of the variable to which the new type is assigned. The second argument must be a properly subclassable class, i.e., not a type construct like :py:data:`~typing.Union`, etc. -The callable returned by :py:func:`NewType ` accepts only one argument; this is equivalent to +The callable returned by :py:class:`~typing.NewType` accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above). Example: @@ -139,12 +139,12 @@ Example: tcp_packet = TcpPacketId(127, 0) # Fails in type checker and at runtime You cannot use :py:func:`isinstance` or :py:func:`issubclass` on the object returned by -:py:func:`~typing.NewType`, nor can you subclass an object returned by :py:func:`~typing.NewType`. +:py:class:`~typing.NewType`, nor can you subclass an object returned by :py:class:`~typing.NewType`. .. note:: - Unlike type aliases, :py:func:`NewType ` will create an entirely new and - unique type when used. The intended purpose of :py:func:`NewType ` is to help you + Unlike type aliases, :py:class:`~typing.NewType` will create an entirely new and + unique type when used. The intended purpose of :py:class:`~typing.NewType` is to help you detect cases where you accidentally mixed together the old base type and the new derived type. @@ -160,7 +160,7 @@ You cannot use :py:func:`isinstance` or :py:func:`issubclass` on the object retu name_by_id(3) # ints and UserId are synonymous - But a similar example using :py:func:`NewType ` will not typecheck: + But a similar example using :py:class:`~typing.NewType` will not typecheck: .. code-block:: python diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index 909215a774a9..66ab7b3a84c7 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -86,7 +86,7 @@ required to be valid Python syntax. For more details, see :pep:`563`. * :ref:`type aliases `; * :ref:`type narrowing `; - * type definitions (see :py:class:`~typing.TypeVar`, :py:func:`~typing.NewType`, :py:class:`~typing.NamedTuple`); + * type definitions (see :py:class:`~typing.TypeVar`, :py:class:`~typing.NewType`, :py:class:`~typing.NamedTuple`); * base classes. .. code-block:: python @@ -263,7 +263,7 @@ If your subclass is also generic, you can use the following: reveal_type(task_queue.get()) # Reveals str In Python 3.9, we can just inherit directly from ``Queue[str]`` or ``Queue[T]`` -since its :py:class:`queue.Queue` implements :py:meth:`__class_getitem__`, so +since its :py:class:`queue.Queue` implements :py:meth:`~object.__class_getitem__`, so the class object can be subscripted at runtime without issue. Using types defined in stubs but not at runtime diff --git a/tox.ini b/tox.ini index 5a728e27fec4..a809c4d2c570 100644 --- a/tox.ini +++ b/tox.ini @@ -37,7 +37,7 @@ passenv = VERIFY_MYPY_ERROR_CODES deps = -rdocs/requirements-docs.txt commands = - sphinx-build -d "{toxworkdir}/docs_doctree" docs/source "{toxworkdir}/docs_out" --color -W -bhtml {posargs} + sphinx-build -n -d "{toxworkdir}/docs_doctree" docs/source "{toxworkdir}/docs_out" --color -W -bhtml {posargs} python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))' [testenv:lint] From 14418bc3d2c38b9ea776da6029e9d9dc6650b7ea Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 15 Aug 2023 20:31:26 +0100 Subject: [PATCH 0196/1617] Polymorphic inference: support for parameter specifications and lambdas (#15837) This is a third follow-up for https://github.com/python/mypy/pull/15287 (likely there will be just one more PR, for `TypeVarTuple`s, and few less important items I mentioned in the original PR I will leave for more distant future). After all this PR turned out to be larger than I wanted. The problem is that `Concatenate` support for `ParamSpec` was quite broken, and this caused many of my tests fail. So I decided to include some major cleanup in this PR (I tried splitting it into a separate PR but it turned out to be tricky). After all, if one ignores added tests, it is almost net zero line count. The main problems that I encountered are: * First, valid substitutions for a `ParamSpecType` were: another `ParamSpecType`, `Parameters`, and `CallableType` (and also `AnyType` and `UninhabitedType` but those seem to be handled trivially). Having `CallableType` in this list caused various missed cases, bogus `get_proper_type()`s, and was generally counter-intuitive. * Second (and probably bigger) issue is that it is possible to represent `Concatenate` in two different forms: as a prefix for `ParamSpecType` (used mostly for instances), and as separate argument types (used mostly for callables). The problem is that some parts of the code were implicitly relying on it being in one or the other form, while some other code uncontrollably switched between the two. I propose to fix this by introducing some simplifications and rules (some of which I enforce by asserts): * Only valid non-trivial substitutions (and consequently upper/lower bound in constraints) for `ParamSpecType` are `ParamSpecType` and `Parameters`. * When `ParamSpecType` appears in a callable it must have an empty `prefix`. * `Parameters` cannot contain other `Parameters` (and ideally also `ParamSpecType`s) among argument types. * For inference we bring `Concatenate` to common representation (because both callables and instances may appear in the same expression). Using the `ParamSpecType` representation with `prefix` looks significantly simpler (especially in solver). Apart from this actual implementation of polymorphic inference is simple/straightforward, I just handle the additional `ParamSpecType` cases (in addition to `TypeVarType`) for inference, for solver, and for application. I also enabled polymorphic inference for lambda expressions, since they are handled by similar code paths. Some minor comments: * I fixed couple minor bugs uncovered by this PR (see e.g. test case for accidental `TypeVar` id clash). * I switch few tests to `--new-type-inference` because there error messages are slightly different, and so it is easier for me to test global flip to `True` locally. * I may tweak some of the "ground rules" if `mypy_primer` output will be particularly bad. --------- Co-authored-by: Ivan Levkivskyi --- mypy/applytype.py | 11 +- mypy/checker.py | 13 +- mypy/checkexpr.py | 123 ++++++++-- mypy/constraints.py | 148 ++++++----- mypy/expandtype.py | 102 +++----- mypy/join.py | 10 +- mypy/meet.py | 5 +- mypy/solve.py | 38 +-- mypy/subtypes.py | 14 +- mypy/test/testtypes.py | 2 +- mypy/type_visitor.py | 2 +- mypy/typeanal.py | 22 +- mypy/typeops.py | 19 +- mypy/types.py | 45 ++-- test-data/unit/check-functions.test | 8 +- test-data/unit/check-generics.test | 230 +++++++++++++++++- test-data/unit/check-inference-context.test | 3 +- test-data/unit/check-inference.test | 10 +- test-data/unit/check-overloading.test | 21 +- .../unit/check-parameter-specification.test | 47 +++- 20 files changed, 639 insertions(+), 234 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index f8be63362a6b..6abe7f0022f8 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -9,7 +9,6 @@ AnyType, CallableType, Instance, - Parameters, ParamSpecType, PartialType, TupleType, @@ -112,9 +111,13 @@ def apply_generic_arguments( if param_spec is not None: nt = id_to_type.get(param_spec.id) if nt is not None: - nt = get_proper_type(nt) - if isinstance(nt, (CallableType, Parameters)): - callable = callable.expand_param_spec(nt) + # ParamSpec expansion is special-cased, so we need to always expand callable + # as a whole, not expanding arguments individually. + callable = expand_type(callable, id_to_type) + assert isinstance(callable, CallableType) + return callable.copy_modified( + variables=[tv for tv in tvars if tv.id not in id_to_type] + ) # Apply arguments to argument types. var_arg = callable.var_arg() diff --git a/mypy/checker.py b/mypy/checker.py index 3bd9c494a890..5d97a0dec713 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4280,12 +4280,14 @@ def check_return_stmt(self, s: ReturnStmt) -> None: return_type = self.return_types[-1] return_type = get_proper_type(return_type) + is_lambda = isinstance(self.scope.top_function(), LambdaExpr) if isinstance(return_type, UninhabitedType): - self.fail(message_registry.NO_RETURN_EXPECTED, s) - return + # Avoid extra error messages for failed inference in lambdas + if not is_lambda or not return_type.ambiguous: + self.fail(message_registry.NO_RETURN_EXPECTED, s) + return if s.expr: - is_lambda = isinstance(self.scope.top_function(), LambdaExpr) declared_none_return = isinstance(return_type, NoneType) declared_any_return = isinstance(return_type, AnyType) @@ -7376,6 +7378,11 @@ def visit_erased_type(self, t: ErasedType) -> bool: # This can happen inside a lambda. return True + def visit_type_var(self, t: TypeVarType) -> bool: + # This is needed to prevent leaking into partial types during + # multi-step type inference. + return t.id.is_meta_var() + class SetNothingToAny(TypeTranslator): """Replace all ambiguous types with Any (to avoid spurious extra errors).""" diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index d00bbb288f3e..68ea7c30ed6f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -17,7 +17,12 @@ from mypy.checkstrformat import StringFormatterChecker from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars from mypy.errors import ErrorWatcher, report_internal_error -from mypy.expandtype import expand_type, expand_type_by_instance, freshen_function_type_vars +from mypy.expandtype import ( + expand_type, + expand_type_by_instance, + freshen_all_functions_type_vars, + freshen_function_type_vars, +) from mypy.infer import ArgumentInferContext, infer_function_type_arguments, infer_type_arguments from mypy.literals import literal from mypy.maptype import map_instance_to_supertype @@ -122,6 +127,7 @@ false_only, fixup_partial_type, function_type, + get_all_type_vars, get_type_vars, is_literal_type_like, make_simplified_union, @@ -145,6 +151,7 @@ LiteralValue, NoneType, Overloaded, + Parameters, ParamSpecFlavor, ParamSpecType, PartialType, @@ -167,6 +174,7 @@ get_proper_types, has_recursive_types, is_named_instance, + remove_dups, split_with_prefix_and_suffix, ) from mypy.types_utils import ( @@ -1579,6 +1587,16 @@ def check_callable_call( lambda i: self.accept(args[i]), ) + # This is tricky: return type may contain its own type variables, like in + # def [S] (S) -> def [T] (T) -> tuple[S, T], so we need to update their ids + # to avoid possible id clashes if this call itself appears in a generic + # function body. + ret_type = get_proper_type(callee.ret_type) + if isinstance(ret_type, CallableType) and ret_type.variables: + fresh_ret_type = freshen_all_functions_type_vars(callee.ret_type) + freeze_all_type_vars(fresh_ret_type) + callee = callee.copy_modified(ret_type=fresh_ret_type) + if callee.is_generic(): need_refresh = any( isinstance(v, (ParamSpecType, TypeVarTupleType)) for v in callee.variables @@ -1597,7 +1615,7 @@ def check_callable_call( lambda i: self.accept(args[i]), ) callee = self.infer_function_type_arguments( - callee, args, arg_kinds, formal_to_actual, context + callee, args, arg_kinds, arg_names, formal_to_actual, need_refresh, context ) if need_refresh: formal_to_actual = map_actuals_to_formals( @@ -1864,6 +1882,8 @@ def infer_function_type_arguments_using_context( # def identity(x: T) -> T: return x # # expects_literal(identity(3)) # Should type-check + # TODO: we may want to add similar exception if all arguments are lambdas, since + # in this case external context is almost everything we have. if not is_generic_instance(ctx) and not is_literal_type_like(ctx): return callable.copy_modified() args = infer_type_arguments(callable.variables, ret_type, erased_ctx) @@ -1885,7 +1905,9 @@ def infer_function_type_arguments( callee_type: CallableType, args: list[Expression], arg_kinds: list[ArgKind], + arg_names: Sequence[str | None] | None, formal_to_actual: list[list[int]], + need_refresh: bool, context: Context, ) -> CallableType: """Infer the type arguments for a generic callee type. @@ -1927,7 +1949,14 @@ def infer_function_type_arguments( if 2 in arg_pass_nums: # Second pass of type inference. (callee_type, inferred_args) = self.infer_function_type_arguments_pass2( - callee_type, args, arg_kinds, formal_to_actual, inferred_args, context + callee_type, + args, + arg_kinds, + arg_names, + formal_to_actual, + inferred_args, + need_refresh, + context, ) if ( @@ -1953,6 +1982,17 @@ def infer_function_type_arguments( or set(get_type_vars(a)) & set(callee_type.variables) for a in inferred_args ): + if need_refresh: + # Technically we need to refresh formal_to_actual after *each* inference pass, + # since each pass can expand ParamSpec or TypeVarTuple. Although such situations + # are very rare, not doing this can cause crashes. + formal_to_actual = map_actuals_to_formals( + arg_kinds, + arg_names, + callee_type.arg_kinds, + callee_type.arg_names, + lambda a: self.accept(args[a]), + ) # If the regular two-phase inference didn't work, try inferring type # variables while allowing for polymorphic solutions, i.e. for solutions # potentially involving free variables. @@ -2000,8 +2040,10 @@ def infer_function_type_arguments_pass2( callee_type: CallableType, args: list[Expression], arg_kinds: list[ArgKind], + arg_names: Sequence[str | None] | None, formal_to_actual: list[list[int]], old_inferred_args: Sequence[Type | None], + need_refresh: bool, context: Context, ) -> tuple[CallableType, list[Type | None]]: """Perform second pass of generic function type argument inference. @@ -2023,6 +2065,14 @@ def infer_function_type_arguments_pass2( if isinstance(arg, (NoneType, UninhabitedType)) or has_erased_component(arg): inferred_args[i] = None callee_type = self.apply_generic_arguments(callee_type, inferred_args, context) + if need_refresh: + formal_to_actual = map_actuals_to_formals( + arg_kinds, + arg_names, + callee_type.arg_kinds, + callee_type.arg_names, + lambda a: self.accept(args[a]), + ) arg_types = self.infer_arg_types_in_context(callee_type, args, arg_kinds, formal_to_actual) @@ -4735,8 +4785,22 @@ def infer_lambda_type_using_context( # they must be considered as indeterminate. We use ErasedType since it # does not affect type inference results (it is for purposes like this # only). - callable_ctx = get_proper_type(replace_meta_vars(ctx, ErasedType())) - assert isinstance(callable_ctx, CallableType) + if self.chk.options.new_type_inference: + # With new type inference we can preserve argument types even if they + # are generic, since new inference algorithm can handle constraints + # like S <: T (we still erase return type since it's ultimately unknown). + extra_vars = [] + for arg in ctx.arg_types: + meta_vars = [tv for tv in get_all_type_vars(arg) if tv.id.is_meta_var()] + extra_vars.extend([tv for tv in meta_vars if tv not in extra_vars]) + callable_ctx = ctx.copy_modified( + ret_type=replace_meta_vars(ctx.ret_type, ErasedType()), + variables=list(ctx.variables) + extra_vars, + ) + else: + erased_ctx = replace_meta_vars(ctx, ErasedType()) + assert isinstance(erased_ctx, ProperType) and isinstance(erased_ctx, CallableType) + callable_ctx = erased_ctx # The callable_ctx may have a fallback of builtins.type if the context # is a constructor -- but this fallback doesn't make sense for lambdas. @@ -5693,18 +5757,28 @@ def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None: self.bound_tvars: set[TypeVarLikeType] = set() self.seen_aliases: set[TypeInfo] = set() - def visit_callable_type(self, t: CallableType) -> Type: - found_vars = set() + def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]: + found_vars = [] for arg in t.arg_types: - found_vars |= set(get_type_vars(arg)) & self.poly_tvars + for tv in get_all_type_vars(arg): + if isinstance(tv, ParamSpecType): + normalized: TypeVarLikeType = tv.copy_modified( + flavor=ParamSpecFlavor.BARE, prefix=Parameters([], [], []) + ) + else: + normalized = tv + if normalized in self.poly_tvars and normalized not in self.bound_tvars: + found_vars.append(normalized) + return remove_dups(found_vars) - found_vars -= self.bound_tvars - self.bound_tvars |= found_vars + def visit_callable_type(self, t: CallableType) -> Type: + found_vars = self.collect_vars(t) + self.bound_tvars |= set(found_vars) result = super().visit_callable_type(t) - self.bound_tvars -= found_vars + self.bound_tvars -= set(found_vars) assert isinstance(result, ProperType) and isinstance(result, CallableType) - result.variables = list(result.variables) + list(found_vars) + result.variables = list(result.variables) + found_vars return result def visit_type_var(self, t: TypeVarType) -> Type: @@ -5713,8 +5787,9 @@ def visit_type_var(self, t: TypeVarType) -> Type: return super().visit_type_var(t) def visit_param_spec(self, t: ParamSpecType) -> Type: - # TODO: Support polymorphic apply for ParamSpec. - raise PolyTranslationError() + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_param_spec(t) def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: # TODO: Support polymorphic apply for TypeVarTuple. @@ -5730,6 +5805,26 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: raise PolyTranslationError() def visit_instance(self, t: Instance) -> Type: + if t.type.has_param_spec_type: + # We need this special-casing to preserve the possibility to store a + # generic function in an instance type. Things like + # forall T . Foo[[x: T], T] + # are not really expressible in current type system, but this looks like + # a useful feature, so let's keep it. + param_spec_index = next( + i for (i, tv) in enumerate(t.type.defn.type_vars) if isinstance(tv, ParamSpecType) + ) + p = get_proper_type(t.args[param_spec_index]) + if isinstance(p, Parameters): + found_vars = self.collect_vars(p) + self.bound_tvars |= set(found_vars) + new_args = [a.accept(self) for a in t.args] + self.bound_tvars -= set(found_vars) + + repl = new_args[param_spec_index] + assert isinstance(repl, ProperType) and isinstance(repl, Parameters) + repl.variables = list(repl.variables) + list(found_vars) + return t.copy_modified(args=new_args) # There is the same problem with callback protocols as with aliases # (callback protocols are essentially more flexible aliases to callables). # Note: consider supporting bindings in instances, e.g. LRUCache[[x: T], T]. diff --git a/mypy/constraints.py b/mypy/constraints.py index 299c6292a259..04c3378ce16b 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -9,7 +9,7 @@ from mypy.argmap import ArgTypeExpander from mypy.erasetype import erase_typevars from mypy.maptype import map_instance_to_supertype -from mypy.nodes import ARG_OPT, ARG_POS, CONTRAVARIANT, COVARIANT, ArgKind +from mypy.nodes import ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, CONTRAVARIANT, COVARIANT, ArgKind from mypy.types import ( TUPLE_LIKE_INSTANCE_NAMES, AnyType, @@ -40,7 +40,6 @@ UninhabitedType, UnionType, UnpackType, - callable_with_ellipsis, get_proper_type, has_recursive_types, has_type_vars, @@ -166,6 +165,8 @@ def infer_constraints_for_callable( actual_type = mapper.expand_actual_type( actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] ) + # TODO: if callee has ParamSpec, we need to collect all actuals that map to star + # args and create single constraint between P and resulting Parameters instead. c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF) constraints.extend(c) @@ -577,10 +578,21 @@ def visit_unpack_type(self, template: UnpackType) -> list[Constraint]: raise RuntimeError("Mypy bug: unpack should be handled at a higher level.") def visit_parameters(self, template: Parameters) -> list[Constraint]: - # constraining Any against C[P] turns into infer_against_any([P], Any) - # ... which seems like the only case this can happen. Better to fail loudly. + # Constraining Any against C[P] turns into infer_against_any([P], Any) + # ... which seems like the only case this can happen. Better to fail loudly otherwise. if isinstance(self.actual, AnyType): return self.infer_against_any(template.arg_types, self.actual) + if type_state.infer_polymorphic and isinstance(self.actual, Parameters): + # For polymorphic inference we need to be able to infer secondary constraints + # in situations like [x: T] <: P <: [x: int]. + res = [] + if len(template.arg_types) == len(self.actual.arg_types): + for tt, at in zip(template.arg_types, self.actual.arg_types): + # This avoids bogus constraints like T <: P.args + if isinstance(at, ParamSpecType): + continue + res.extend(infer_constraints(tt, at, self.direction)) + return res raise RuntimeError("Parameters cannot be constrained to") # Non-leaf types @@ -686,7 +698,6 @@ def visit_instance(self, template: Instance) -> list[Constraint]: # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. for tvar, mapped_arg, instance_arg in zip(tvars, mapped_args, instance_args): - # TODO(PEP612): More ParamSpec work (or is Parameters the only thing accepted) if isinstance(tvar, TypeVarType): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. @@ -697,25 +708,26 @@ def visit_instance(self, template: Instance) -> list[Constraint]: infer_constraints(mapped_arg, instance_arg, neg_op(self.direction)) ) elif isinstance(tvar, ParamSpecType) and isinstance(mapped_arg, ParamSpecType): - suffix = get_proper_type(instance_arg) - - if isinstance(suffix, CallableType): - prefix = mapped_arg.prefix - from_concat = bool(prefix.arg_types) or suffix.from_concatenate - suffix = suffix.copy_modified(from_concatenate=from_concat) - - if isinstance(suffix, (Parameters, CallableType)): - # no such thing as variance for ParamSpecs - # TODO: is there a case I am missing? + prefix = mapped_arg.prefix + if isinstance(instance_arg, Parameters): + # No such thing as variance for ParamSpecs, consider them invariant # TODO: constraints between prefixes - prefix = mapped_arg.prefix - suffix = suffix.copy_modified( - suffix.arg_types[len(prefix.arg_types) :], - suffix.arg_kinds[len(prefix.arg_kinds) :], - suffix.arg_names[len(prefix.arg_names) :], + suffix: Type = instance_arg.copy_modified( + instance_arg.arg_types[len(prefix.arg_types) :], + instance_arg.arg_kinds[len(prefix.arg_kinds) :], + instance_arg.arg_names[len(prefix.arg_names) :], ) + res.append(Constraint(mapped_arg, SUBTYPE_OF, suffix)) res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) - elif isinstance(suffix, ParamSpecType): + elif isinstance(instance_arg, ParamSpecType): + suffix = instance_arg.copy_modified( + prefix=Parameters( + instance_arg.prefix.arg_types[len(prefix.arg_types) :], + instance_arg.prefix.arg_kinds[len(prefix.arg_kinds) :], + instance_arg.prefix.arg_names[len(prefix.arg_names) :], + ) + ) + res.append(Constraint(mapped_arg, SUBTYPE_OF, suffix)) res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) else: # This case should have been handled above. @@ -767,26 +779,26 @@ def visit_instance(self, template: Instance) -> list[Constraint]: elif isinstance(tvar, ParamSpecType) and isinstance( template_arg, ParamSpecType ): - suffix = get_proper_type(mapped_arg) - - if isinstance(suffix, CallableType): - prefix = template_arg.prefix - from_concat = bool(prefix.arg_types) or suffix.from_concatenate - suffix = suffix.copy_modified(from_concatenate=from_concat) - - if isinstance(suffix, (Parameters, CallableType)): - # no such thing as variance for ParamSpecs - # TODO: is there a case I am missing? + prefix = template_arg.prefix + if isinstance(mapped_arg, Parameters): + # No such thing as variance for ParamSpecs, consider them invariant # TODO: constraints between prefixes - prefix = template_arg.prefix - - suffix = suffix.copy_modified( - suffix.arg_types[len(prefix.arg_types) :], - suffix.arg_kinds[len(prefix.arg_kinds) :], - suffix.arg_names[len(prefix.arg_names) :], + suffix = mapped_arg.copy_modified( + mapped_arg.arg_types[len(prefix.arg_types) :], + mapped_arg.arg_kinds[len(prefix.arg_kinds) :], + mapped_arg.arg_names[len(prefix.arg_names) :], ) + res.append(Constraint(template_arg, SUBTYPE_OF, suffix)) res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) - elif isinstance(suffix, ParamSpecType): + elif isinstance(mapped_arg, ParamSpecType): + suffix = mapped_arg.copy_modified( + prefix=Parameters( + mapped_arg.prefix.arg_types[len(prefix.arg_types) :], + mapped_arg.prefix.arg_kinds[len(prefix.arg_kinds) :], + mapped_arg.prefix.arg_names[len(prefix.arg_names) :], + ) + ) + res.append(Constraint(template_arg, SUBTYPE_OF, suffix)) res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) else: # This case should have been handled above. @@ -848,7 +860,7 @@ def visit_instance(self, template: Instance) -> list[Constraint]: elif isinstance(actual, TupleType) and self.direction == SUPERTYPE_OF: return infer_constraints(template, mypy.typeops.tuple_fallback(actual), self.direction) elif isinstance(actual, TypeVarType): - if not actual.values: + if not actual.values and not actual.id.is_meta_var(): return infer_constraints(template, actual.upper_bound, self.direction) return [] elif isinstance(actual, ParamSpecType): @@ -892,6 +904,8 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # Normalize callables before matching against each other. # Note that non-normalized callables can be created in annotations # using e.g. callback protocols. + # TODO: check that callables match? Ideally we should not infer constraints + # callables that can never be subtypes of one another in given direction. template = template.with_unpacked_kwargs() extra_tvars = False if isinstance(self.actual, CallableType): @@ -899,12 +913,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: cactual = self.actual.with_unpacked_kwargs() param_spec = template.param_spec() if param_spec is None: - # FIX verify argument counts # TODO: Erase template variables if it is generic? if ( type_state.infer_polymorphic and cactual.variables - and cactual.param_spec() is None and not self.skip_neg_op # Technically, the correct inferred type for application of e.g. # Callable[..., T] -> Callable[..., T] (with literal ellipsis), to a generic @@ -926,7 +938,8 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # We can't infer constraints from arguments if the template is Callable[..., T] # (with literal '...'). if not template.is_ellipsis_args: - if find_unpack_in_list(template.arg_types) is not None: + unpack_present = find_unpack_in_list(template.arg_types) + if unpack_present is not None: ( unpack_constraints, cactual_args_t, @@ -941,47 +954,70 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: else: template_args = template.arg_types cactual_args = cactual.arg_types - # The lengths should match, but don't crash (it will error elsewhere). + # TODO: use some more principled "formal to actual" logic + # instead of this lock-step loop over argument types. This identical + # logic should be used in 5 places: in Parameters vs Parameters + # inference, in Instance vs Instance inference for prefixes (two + # branches), and in Callable vs Callable inference (two branches). for t, a in zip(template_args, cactual_args): + # This avoids bogus constraints like T <: P.args + if isinstance(a, ParamSpecType): + # TODO: can we infer something useful for *T vs P? + continue # Negate direction due to function argument type contravariance. res.extend(infer_constraints(t, a, neg_op(self.direction))) else: - # sometimes, it appears we try to get constraints between two paramspec callables? - - # TODO: Direction - # TODO: check the prefixes match prefix = param_spec.prefix prefix_len = len(prefix.arg_types) cactual_ps = cactual.param_spec() + if type_state.infer_polymorphic and cactual.variables and not self.skip_neg_op: + # Similar logic to the branch above. + res.extend( + infer_constraints( + cactual, template, neg_op(self.direction), skip_neg_op=True + ) + ) + extra_tvars = True + if not cactual_ps: max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]) prefix_len = min(prefix_len, max_prefix_len) res.append( Constraint( param_spec, - SUBTYPE_OF, - cactual.copy_modified( + neg_op(self.direction), + Parameters( arg_types=cactual.arg_types[prefix_len:], arg_kinds=cactual.arg_kinds[prefix_len:], arg_names=cactual.arg_names[prefix_len:], - ret_type=UninhabitedType(), + variables=cactual.variables + if not type_state.infer_polymorphic + else [], ), ) ) else: - res.append(Constraint(param_spec, SUBTYPE_OF, cactual_ps)) + if len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types): + cactual_ps = cactual_ps.copy_modified( + prefix=Parameters( + arg_types=cactual_ps.prefix.arg_types[prefix_len:], + arg_kinds=cactual_ps.prefix.arg_kinds[prefix_len:], + arg_names=cactual_ps.prefix.arg_names[prefix_len:], + ) + ) + res.append(Constraint(param_spec, neg_op(self.direction), cactual_ps)) - # compare prefixes + # Compare prefixes as well cactual_prefix = cactual.copy_modified( arg_types=cactual.arg_types[:prefix_len], arg_kinds=cactual.arg_kinds[:prefix_len], arg_names=cactual.arg_names[:prefix_len], ) - # TODO: see above "FIX" comments for param_spec is None case - # TODO: this assumes positional arguments for t, a in zip(prefix.arg_types, cactual_prefix.arg_types): + if isinstance(a, ParamSpecType): + continue res.extend(infer_constraints(t, a, neg_op(self.direction))) template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type @@ -993,7 +1029,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) if extra_tvars: for c in res: - c.extra_tvars = list(cactual.variables) + c.extra_tvars += cactual.variables return res elif isinstance(self.actual, AnyType): param_spec = template.param_spec() @@ -1006,7 +1042,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: Constraint( param_spec, SUBTYPE_OF, - callable_with_ellipsis(any_type, any_type, template.fallback), + Parameters([any_type, any_type], [ARG_STAR, ARG_STAR2], [None, None]), ) ] res.extend(infer_constraints(template.ret_type, any_type, self.direction)) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index b599b49e4c12..0e98ed048197 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -231,44 +231,27 @@ def visit_type_var(self, t: TypeVarType) -> Type: return repl def visit_param_spec(self, t: ParamSpecType) -> Type: - # set prefix to something empty so we don't duplicate it - repl = get_proper_type( - self.variables.get(t.id, t.copy_modified(prefix=Parameters([], [], []))) - ) - if isinstance(repl, Instance): - # TODO: what does prefix mean in this case? - # TODO: why does this case even happen? Instances aren't plural. - return repl - elif isinstance(repl, (ParamSpecType, Parameters, CallableType)): - if isinstance(repl, ParamSpecType): - return repl.copy_modified( - flavor=t.flavor, - prefix=t.prefix.copy_modified( - arg_types=t.prefix.arg_types + repl.prefix.arg_types, - arg_kinds=t.prefix.arg_kinds + repl.prefix.arg_kinds, - arg_names=t.prefix.arg_names + repl.prefix.arg_names, - ), - ) - else: - # if the paramspec is *P.args or **P.kwargs: - if t.flavor != ParamSpecFlavor.BARE: - assert isinstance(repl, CallableType), "Should not be able to get here." - # Is this always the right thing to do? - param_spec = repl.param_spec() - if param_spec: - return param_spec.with_flavor(t.flavor) - else: - return repl - else: - return Parameters( - t.prefix.arg_types + repl.arg_types, - t.prefix.arg_kinds + repl.arg_kinds, - t.prefix.arg_names + repl.arg_names, - variables=[*t.prefix.variables, *repl.variables], - ) - + # Set prefix to something empty, so we don't duplicate it below. + repl = self.variables.get(t.id, t.copy_modified(prefix=Parameters([], [], []))) + if isinstance(repl, ParamSpecType): + return repl.copy_modified( + flavor=t.flavor, + prefix=t.prefix.copy_modified( + arg_types=self.expand_types(t.prefix.arg_types + repl.prefix.arg_types), + arg_kinds=t.prefix.arg_kinds + repl.prefix.arg_kinds, + arg_names=t.prefix.arg_names + repl.prefix.arg_names, + ), + ) + elif isinstance(repl, Parameters): + assert t.flavor == ParamSpecFlavor.BARE + return Parameters( + self.expand_types(t.prefix.arg_types + repl.arg_types), + t.prefix.arg_kinds + repl.arg_kinds, + t.prefix.arg_names + repl.arg_names, + variables=[*t.prefix.variables, *repl.variables], + ) else: - # TODO: should this branch be removed? better not to fail silently + # TODO: replace this with "assert False" return repl def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: @@ -387,7 +370,7 @@ def interpolate_args_for_unpack( def visit_callable_type(self, t: CallableType) -> CallableType: param_spec = t.param_spec() if param_spec is not None: - repl = get_proper_type(self.variables.get(param_spec.id)) + repl = self.variables.get(param_spec.id) # If a ParamSpec in a callable type is substituted with a # callable type, we can't use normal substitution logic, # since ParamSpec is actually split into two components @@ -395,35 +378,30 @@ def visit_callable_type(self, t: CallableType) -> CallableType: # must expand both of them with all the argument types, # kinds and names in the replacement. The return type in # the replacement is ignored. - if isinstance(repl, (CallableType, Parameters)): - # Substitute *args: P.args, **kwargs: P.kwargs - prefix = param_spec.prefix - # we need to expand the types in the prefix, so might as well - # not get them in the first place - t = t.expand_param_spec(repl, no_prefix=True) + if isinstance(repl, Parameters): + # We need to expand both the types in the prefix and the ParamSpec itself + t = t.expand_param_spec(repl) return t.copy_modified( - arg_types=self.expand_types(prefix.arg_types) + t.arg_types, - arg_kinds=prefix.arg_kinds + t.arg_kinds, - arg_names=prefix.arg_names + t.arg_names, + arg_types=self.expand_types(t.arg_types), + arg_kinds=t.arg_kinds, + arg_names=t.arg_names, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), ) - # TODO: Conceptually, the "len(t.arg_types) == 2" should not be here. However, this - # errors without it. Either figure out how to eliminate this or place an - # explanation for why this is necessary. - elif isinstance(repl, ParamSpecType) and len(t.arg_types) == 2: - # We're substituting one paramspec for another; this can mean that the prefix - # changes. (e.g. sub Concatenate[int, P] for Q) + elif isinstance(repl, ParamSpecType): + # We're substituting one ParamSpec for another; this can mean that the prefix + # changes, e.g. substitute Concatenate[int, P] in place of Q. prefix = repl.prefix - old_prefix = param_spec.prefix - - # Check assumptions. I'm not sure what order to place new prefix vs old prefix: - assert not old_prefix.arg_types or not prefix.arg_types - - t = t.copy_modified( - arg_types=prefix.arg_types + old_prefix.arg_types + t.arg_types, - arg_kinds=prefix.arg_kinds + old_prefix.arg_kinds + t.arg_kinds, - arg_names=prefix.arg_names + old_prefix.arg_names + t.arg_names, + clean_repl = repl.copy_modified(prefix=Parameters([], [], [])) + return t.copy_modified( + arg_types=self.expand_types(t.arg_types[:-2] + prefix.arg_types) + + [ + clean_repl.with_flavor(ParamSpecFlavor.ARGS), + clean_repl.with_flavor(ParamSpecFlavor.KWARGS), + ], + arg_kinds=t.arg_kinds[:-2] + prefix.arg_kinds + t.arg_kinds[-2:], + arg_names=t.arg_names[:-2] + prefix.arg_names + t.arg_names[-2:], + ret_type=t.ret_type.accept(self), ) var_arg = t.var_arg() diff --git a/mypy/join.py b/mypy/join.py index f4af59f4e50b..806c644a680c 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -315,8 +315,14 @@ def visit_unpack_type(self, t: UnpackType) -> UnpackType: raise NotImplementedError def visit_parameters(self, t: Parameters) -> ProperType: - if self.s == t: - return t + if isinstance(self.s, Parameters): + if len(t.arg_types) != len(self.s.arg_types): + return self.default(self.s) + return t.copy_modified( + # Note that since during constraint inference we already treat whole ParamSpec as + # contravariant, we should join individual items, not meet them like for Callables + arg_types=[join_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)] + ) else: return self.default(self.s) diff --git a/mypy/meet.py b/mypy/meet.py index 29c4d3663503..e3a22a226575 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -701,11 +701,12 @@ def visit_unpack_type(self, t: UnpackType) -> ProperType: raise NotImplementedError def visit_parameters(self, t: Parameters) -> ProperType: - # TODO: is this the right variance? - if isinstance(self.s, (Parameters, CallableType)): + if isinstance(self.s, Parameters): if len(t.arg_types) != len(self.s.arg_types): return self.default(self.s) return t.copy_modified( + # Note that since during constraint inference we already treat whole ParamSpec as + # contravariant, we should meet individual items, not join them like for Callables arg_types=[meet_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)] ) else: diff --git a/mypy/solve.py b/mypy/solve.py index 72b3d6f26618..4b2b899c2a8d 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -6,17 +6,18 @@ from typing import Iterable, Sequence from typing_extensions import TypeAlias as _TypeAlias -from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op +from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints from mypy.expandtype import expand_type from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.join import join_types from mypy.meet import meet_type_list, meet_types from mypy.subtypes import is_subtype -from mypy.typeops import get_type_vars +from mypy.typeops import get_all_type_vars from mypy.types import ( AnyType, Instance, NoneType, + ParamSpecType, ProperType, Type, TypeOfAny, @@ -26,7 +27,6 @@ UninhabitedType, UnionType, get_proper_type, - remove_dups, ) from mypy.typestate import type_state @@ -62,10 +62,6 @@ def solve_constraints( for c in constraints: extra_vars.extend([v.id for v in c.extra_tvars if v.id not in vars + extra_vars]) originals.update({v.id: v for v in c.extra_tvars if v.id not in originals}) - if allow_polymorphic: - # Constraints like T :> S and S <: T are semantically the same, but they are - # represented differently. Normalize the constraint list w.r.t this equivalence. - constraints = normalize_constraints(constraints, vars + extra_vars) # Collect a list of constraints for each type variable. cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars + extra_vars} @@ -334,23 +330,6 @@ def is_trivial_bound(tp: ProperType) -> bool: return isinstance(tp, Instance) and tp.type.fullname == "builtins.object" -def normalize_constraints( - constraints: list[Constraint], vars: list[TypeVarId] -) -> list[Constraint]: - """Normalize list of constraints (to simplify life for the non-linear solver). - - This includes two things currently: - * Complement T :> S by S <: T - * Remove strict duplicates - * Remove constrains for unrelated variables - """ - res = constraints.copy() - for c in constraints: - if isinstance(c.target, TypeVarType): - res.append(Constraint(c.target, neg_op(c.op), c.origin_type_var)) - return [c for c in remove_dups(constraints) if c.type_var in vars] - - def transitive_closure( tvars: list[TypeVarId], constraints: list[Constraint] ) -> tuple[Graph, Bounds, Bounds]: @@ -380,7 +359,14 @@ def transitive_closure( remaining = set(constraints) while remaining: c = remaining.pop() - if isinstance(c.target, TypeVarType) and c.target.id in tvars: + # Note that ParamSpec constraint P <: Q may be considered linear only if Q has no prefix, + # for cases like P <: Concatenate[T, Q] we should consider this non-linear and put {P} and + # {T, Q} into separate SCCs. + if ( + isinstance(c.target, TypeVarType) + or isinstance(c.target, ParamSpecType) + and not c.target.prefix.arg_types + ) and c.target.id in tvars: if c.op == SUBTYPE_OF: lower, upper = c.type_var, c.target.id else: @@ -463,4 +449,4 @@ def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool: def get_vars(target: Type, vars: list[TypeVarId]) -> set[TypeVarId]: """Find type variables for which we are solving in a target type.""" - return {tv.id for tv in get_type_vars(target)} & set(vars) + return {tv.id for tv in get_all_type_vars(target)} & set(vars) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index da92f7398d4e..60fccc7e357c 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1705,11 +1705,15 @@ def unify_generic_callable( return_constraint_direction = mypy.constraints.SUBTYPE_OF constraints: list[mypy.constraints.Constraint] = [] - for arg_type, target_arg_type in zip(type.arg_types, target.arg_types): - c = mypy.constraints.infer_constraints( - arg_type, target_arg_type, mypy.constraints.SUPERTYPE_OF - ) - constraints.extend(c) + # There is some special logic for inference in callables, so better use them + # as wholes instead of picking separate arguments. + cs = mypy.constraints.infer_constraints( + type.copy_modified(ret_type=UninhabitedType()), + target.copy_modified(ret_type=UninhabitedType()), + mypy.constraints.SUBTYPE_OF, + skip_neg_op=True, + ) + constraints.extend(cs) if not ignore_return: c = mypy.constraints.infer_constraints( type.ret_type, target.ret_type, return_constraint_direction diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 59457dfa5d3b..56ac86058ce4 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1464,7 +1464,7 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 8 + ALLOWED_GET_PROPER_TYPES = 6 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index cbfa43a77b81..1860a43eb14f 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -348,7 +348,7 @@ def visit_type_var(self, t: TypeVarType) -> T: return self.query_types([t.upper_bound, t.default] + t.values) def visit_param_spec(self, t: ParamSpecType) -> T: - return self.query_types([t.upper_bound, t.default]) + return self.query_types([t.upper_bound, t.default, t.prefix]) def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: return self.query_types([t.upper_bound, t.default]) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d894e2cc8c51..8ac73cdf8aac 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1244,9 +1244,23 @@ def analyze_callable_type(self, t: UnboundType) -> Type: ) else: # Callable[P, RET] (where P is ParamSpec) - maybe_ret = self.analyze_callable_args_for_paramspec( - callable_args, ret_type, fallback - ) or self.analyze_callable_args_for_concatenate(callable_args, ret_type, fallback) + with self.tvar_scope_frame(): + # Temporarily bind ParamSpecs to allow code like this: + # my_fun: Callable[Q, Foo[Q]] + # We usually do this later in visit_callable_type(), but the analysis + # below happens at very early stage. + variables = [] + for name, tvar_expr in self.find_type_var_likes(callable_args): + variables.append(self.tvar_scope.bind_new(name, tvar_expr)) + maybe_ret = self.analyze_callable_args_for_paramspec( + callable_args, ret_type, fallback + ) or self.analyze_callable_args_for_concatenate( + callable_args, ret_type, fallback + ) + if maybe_ret: + maybe_ret = maybe_ret.copy_modified( + ret_type=ret_type.accept(self), variables=variables + ) if maybe_ret is None: # Callable[?, RET] (where ? is something invalid) self.fail( @@ -1532,6 +1546,7 @@ def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = Fa if analyzed.prefix.arg_types: self.fail("Invalid location for Concatenate", t, code=codes.VALID_TYPE) self.note("You can use Concatenate as the first argument to Callable", t) + analyzed = AnyType(TypeOfAny.from_error) else: self.fail( f'Invalid location for ParamSpec "{analyzed.name}"', t, code=codes.VALID_TYPE @@ -1541,6 +1556,7 @@ def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = Fa "'Callable[{}, int]'".format(analyzed.name), t, ) + analyzed = AnyType(TypeOfAny.from_error) return analyzed def anal_var_def(self, var_def: TypeVarLikeType) -> TypeVarLikeType: diff --git a/mypy/typeops.py b/mypy/typeops.py index 4233cc1b2b33..d746ea701fde 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -948,22 +948,31 @@ def coerce_to_literal(typ: Type) -> Type: def get_type_vars(tp: Type) -> list[TypeVarType]: - return tp.accept(TypeVarExtractor()) + return cast("list[TypeVarType]", tp.accept(TypeVarExtractor())) -class TypeVarExtractor(TypeQuery[List[TypeVarType]]): - def __init__(self) -> None: +def get_all_type_vars(tp: Type) -> list[TypeVarLikeType]: + # TODO: should we always use this function instead of get_type_vars() above? + return tp.accept(TypeVarExtractor(include_all=True)) + + +class TypeVarExtractor(TypeQuery[List[TypeVarLikeType]]): + def __init__(self, include_all: bool = False) -> None: super().__init__(self._merge) + self.include_all = include_all - def _merge(self, iter: Iterable[list[TypeVarType]]) -> list[TypeVarType]: + def _merge(self, iter: Iterable[list[TypeVarLikeType]]) -> list[TypeVarLikeType]: out = [] for item in iter: out.extend(item) return out - def visit_type_var(self, t: TypeVarType) -> list[TypeVarType]: + def visit_type_var(self, t: TypeVarType) -> list[TypeVarLikeType]: return [t] + def visit_param_spec(self, t: ParamSpecType) -> list[TypeVarLikeType]: + return [t] if self.include_all else [] + def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool: """Does this type have a custom special method such as __format__() or __eq__()? diff --git a/mypy/types.py b/mypy/types.py index d13cff00c06d..359ca713616b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1577,6 +1577,7 @@ def __init__( self.arg_kinds = arg_kinds self.arg_names = list(arg_names) assert len(arg_types) == len(arg_kinds) == len(arg_names) + assert not any(isinstance(t, Parameters) for t in arg_types) self.min_args = arg_kinds.count(ARG_POS) self.is_ellipsis_args = is_ellipsis_args self.variables = variables or [] @@ -1788,6 +1789,11 @@ def __init__( ) -> None: super().__init__(line, column) assert len(arg_types) == len(arg_kinds) == len(arg_names) + for t, k in zip(arg_types, arg_kinds): + if isinstance(t, ParamSpecType): + assert not t.prefix.arg_types + # TODO: should we assert that only ARG_STAR contain ParamSpecType? + # See testParamSpecJoin, that relies on passing e.g `P.args` as plain argument. if variables is None: variables = [] self.arg_types = list(arg_types) @@ -2033,36 +2039,21 @@ def param_spec(self) -> ParamSpecType | None: if not isinstance(arg_type, ParamSpecType): return None - # sometimes paramspectypes are analyzed in from mysterious places, - # e.g. def f(prefix..., *args: P.args, **kwargs: P.kwargs) -> ...: ... - prefix = arg_type.prefix - if not prefix.arg_types: - # TODO: confirm that all arg kinds are positional - prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) - + # Prepend prefix for def f(prefix..., *args: P.args, **kwargs: P.kwargs) -> ... + # TODO: confirm that all arg kinds are positional + prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) - def expand_param_spec( - self, c: CallableType | Parameters, no_prefix: bool = False - ) -> CallableType: + def expand_param_spec(self, c: Parameters) -> CallableType: + # TODO: try deleting variables from Parameters after new type inference is default. variables = c.variables - - if no_prefix: - return self.copy_modified( - arg_types=c.arg_types, - arg_kinds=c.arg_kinds, - arg_names=c.arg_names, - is_ellipsis_args=c.is_ellipsis_args, - variables=[*variables, *self.variables], - ) - else: - return self.copy_modified( - arg_types=self.arg_types[:-2] + c.arg_types, - arg_kinds=self.arg_kinds[:-2] + c.arg_kinds, - arg_names=self.arg_names[:-2] + c.arg_names, - is_ellipsis_args=c.is_ellipsis_args, - variables=[*variables, *self.variables], - ) + return self.copy_modified( + arg_types=self.arg_types[:-2] + c.arg_types, + arg_kinds=self.arg_kinds[:-2] + c.arg_kinds, + arg_names=self.arg_names[:-2] + c.arg_names, + is_ellipsis_args=c.is_ellipsis_args, + variables=[*variables, *self.variables], + ) def with_unpacked_kwargs(self) -> NormalizedCallableType: if not self.unpack_kwargs: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index a8722d8190b9..f49541420cc0 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2330,7 +2330,7 @@ T = TypeVar('T') def deco() -> Callable[[T], T]: pass reveal_type(deco) # N: Revealed type is "def () -> def [T] (T`-1) -> T`-1" f = deco() -reveal_type(f) # N: Revealed type is "def [T] (T`-1) -> T`-1" +reveal_type(f) # N: Revealed type is "def [T] (T`1) -> T`1" i = f(3) reveal_type(i) # N: Revealed type is "builtins.int" @@ -2343,7 +2343,7 @@ U = TypeVar('U') def deco(x: U) -> Callable[[T, U], T]: pass reveal_type(deco) # N: Revealed type is "def [U] (x: U`-1) -> def [T] (T`-2, U`-1) -> T`-2" f = deco("foo") -reveal_type(f) # N: Revealed type is "def [T] (T`-2, builtins.str) -> T`-2" +reveal_type(f) # N: Revealed type is "def [T] (T`1, builtins.str) -> T`1" i = f(3, "eggs") reveal_type(i) # N: Revealed type is "builtins.int" @@ -2354,9 +2354,9 @@ T = TypeVar('T') R = TypeVar('R') def deco() -> Callable[[T], Callable[[T, R], R]]: pass f = deco() -reveal_type(f) # N: Revealed type is "def [T] (T`-1) -> def [R] (T`-1, R`-2) -> R`-2" +reveal_type(f) # N: Revealed type is "def [T] (T`2) -> def [R] (T`2, R`1) -> R`1" g = f(3) -reveal_type(g) # N: Revealed type is "def [R] (builtins.int, R`-2) -> R`-2" +reveal_type(g) # N: Revealed type is "def [R] (builtins.int, R`3) -> R`3" s = g(4, "foo") reveal_type(s) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index d1842a74d634..8c7c4e035961 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2713,6 +2713,7 @@ reveal_type(func(1)) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [case testGenericLambdaGenericMethodNoCrash] +# flags: --new-type-inference from typing import TypeVar, Union, Callable, Generic S = TypeVar("S") @@ -2723,7 +2724,7 @@ def f(x: Callable[[G[T]], int]) -> T: ... class G(Generic[T]): def g(self, x: S) -> Union[S, T]: ... -f(lambda x: x.g(0)) # E: Cannot infer type argument 1 of "f" +f(lambda x: x.g(0)) # E: Incompatible return value type (got "Union[int, T]", expected "int") [case testDictStarInference] class B: ... @@ -3035,3 +3036,230 @@ reveal_type(dec1(id2)) # N: Revealed type is "def [S in (builtins.int, builtins reveal_type(dec2(id1)) # N: Revealed type is "def [UC <: __main__.C] (UC`5) -> builtins.list[UC`5]" reveal_type(dec2(id2)) # N: Revealed type is "def () -> builtins.list[]" \ # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[], ]" + +[case testInferenceAgainstGenericLambdas] +# flags: --new-type-inference +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') + +def dec1(f: Callable[[T], T]) -> Callable[[T], List[T]]: + ... +def dec2(f: Callable[[S], T]) -> Callable[[S], List[T]]: + ... +def dec3(f: Callable[[List[S]], T]) -> Callable[[S], T]: + def g(x: S) -> T: + return f([x]) + return g +def dec4(f: Callable[[S], List[T]]) -> Callable[[S], T]: + ... +def dec5(f: Callable[[int], T]) -> Callable[[int], List[T]]: + def g(x: int) -> List[T]: + return [f(x)] * x + return g + +reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" +reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]" +reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`6) -> S`6" +reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`8) -> S`8" +reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" +reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" +reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`15) -> builtins.list[S`15]" +dec4(lambda x: x) # E: Incompatible return value type (got "S", expected "List[object]") +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecBasicInList] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import ParamSpec + +T = TypeVar('T') +P = ParamSpec('P') +U = TypeVar('U') +V = TypeVar('V') + +def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... +def pair(x: U, y: V) -> Tuple[U, V]: ... +reveal_type(dec(id)) # N: Revealed type is "def [T] (x: T`2) -> builtins.list[T`2]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (x: T`4, y: T`4) -> builtins.list[T`4]" +reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecBasicDeList] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import ParamSpec + +T = TypeVar('T') +P = ParamSpec('P') +U = TypeVar('U') +V = TypeVar('V') + +def dec(f: Callable[P, List[T]]) -> Callable[P, T]: ... +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... +reveal_type(dec(id)) # N: Revealed type is "def [T] (x: builtins.list[T`2]) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (x: builtins.list[T`4], y: builtins.list[T`4]) -> T`4" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecPopOff] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +S = TypeVar('S') +P = ParamSpec('P') +U = TypeVar('U') +V = TypeVar('V') + +def dec(f: Callable[Concatenate[T, P], S]) -> Callable[P, Callable[[T], S]]: ... +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... +def pair(x: U, y: V) -> Tuple[U, V]: ... +reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" +reveal_type(dec(either)) # N: Revealed type is "def [T] (y: T`4) -> def (T`4) -> T`4" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`7) -> Tuple[T`7, V`-2]" +reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, P, S] (def (T`-1, *P.args, **P.kwargs) -> S`-3) -> def (*P.args, **P.kwargs) -> def (T`-1) -> S`-3" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecPopOn] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +S = TypeVar('S') +P = ParamSpec('P') +U = TypeVar('U') +V = TypeVar('V') + +def dec(f: Callable[P, Callable[[T], S]]) -> Callable[Concatenate[T, P], S]: ... +def id() -> Callable[[U], U]: ... +def either(x: U) -> Callable[[U], U]: ... +def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, x: T`5) -> T`5" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`8, x: U`-1) -> Tuple[T`8, U`-1]" +# This is counter-intuitive but looks correct, dec matches itself only if P is empty +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`11, f: def () -> def (T`11) -> S`12) -> S`12" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecVsParamSpec] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple, Generic +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +P = ParamSpec('P') +Q = ParamSpec('Q') + +class Foo(Generic[P]): ... +class Bar(Generic[P, T]): ... + +def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... +def f(*args: Q.args, **kwargs: Q.kwargs) -> Foo[Q]: ... +reveal_type(dec(f)) # N: Revealed type is "def [P] (*P.args, **P.kwargs) -> builtins.list[__main__.Foo[P`1]]" +g: Callable[Concatenate[int, Q], Foo[Q]] +reveal_type(dec(g)) # N: Revealed type is "def [Q] (builtins.int, *Q.args, **Q.kwargs) -> builtins.list[__main__.Foo[Q`-1]]" +h: Callable[Concatenate[T, Q], Bar[Q, T]] +reveal_type(dec(h)) # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwargs) -> builtins.list[__main__.Bar[Q`-2, T`-1]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecVsParamSpecConcatenate] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple, Generic +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +P = ParamSpec('P') +Q = ParamSpec('Q') + +class Foo(Generic[P]): ... +class Bar(Generic[P, T]): ... + +def dec(f: Callable[P, int]) -> Callable[P, Foo[P]]: ... +h: Callable[Concatenate[T, Q], int] +g: Callable[Concatenate[T, Q], int] +h = g +reveal_type(dec(h)) # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwargs) -> __main__.Foo[[T`-1, **Q`-2]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecSecondary] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple, Generic +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +P = ParamSpec('P') +Q = ParamSpec('Q') + +class Foo(Generic[P]): ... + +def dec(f: Callable[P, Foo[P]]) -> Callable[P, Foo[P]]: ... +g: Callable[[T], Foo[[int]]] +reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[[builtins.int]]" +h: Callable[Q, Foo[[int]]] +reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[[builtins.int]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericParamSpecSecondOrder] +# flags: --new-type-inference +from typing import TypeVar, Callable +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar('T') +S = TypeVar('S') +P = ParamSpec('P') +Q = ParamSpec('Q') +U = TypeVar('U') +W = ParamSpec('W') + +def transform( + dec: Callable[[Callable[P, T]], Callable[Q, S]] +) -> Callable[[Callable[Concatenate[int, P], T]], Callable[Concatenate[int, Q], S]]: ... + +def dec(f: Callable[W, U]) -> Callable[W, U]: ... +def dec2(f: Callable[Concatenate[str, W], U]) -> Callable[Concatenate[bytes, W], U]: ... +reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`2) -> def (builtins.int, *P.args, **P.kwargs) -> T`2" +reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`6) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`6" +[builtins fixtures/tuple.pyi] + +[case testNoAccidentalVariableClashInNestedGeneric] +# flags: --new-type-inference +from typing import TypeVar, Callable, Generic, Tuple + +T = TypeVar('T') +S = TypeVar('S') +U = TypeVar('U') + +def pipe(x: T, f1: Callable[[T], S], f2: Callable[[S], U]) -> U: ... +def and_then(a: T) -> Callable[[S], Tuple[S, T]]: ... + +def apply(a: S, b: T) -> None: + v1 = and_then(b) + v2: Callable[[Tuple[S, T]], None] + return pipe(a, v1, v2) +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericParamSpecSpuriousBoundsNotUsed] +# flags: --new-type-inference +from typing import TypeVar, Callable, Generic +from typing_extensions import ParamSpec, Concatenate + +Q = ParamSpec("Q") +class Foo(Generic[Q]): ... + +T1 = TypeVar("T1", bound=Foo[...]) +T2 = TypeVar("T2", bound=Foo[...]) +P = ParamSpec("P") +def pop_off(fn: Callable[Concatenate[T1, P], T2]) -> Callable[P, Callable[[T1], T2]]: + ... + +@pop_off +def test(command: Foo[Q]) -> Foo[Q]: ... +reveal_type(test) # N: Revealed type is "def () -> def [Q] (__main__.Foo[Q`-1]) -> __main__.Foo[Q`-1]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index ba36c1548532..5f25b007dd47 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -693,6 +693,7 @@ f(lambda: None) g(lambda: None) [case testIsinstanceInInferredLambda] +# flags: --new-type-inference from typing import TypeVar, Callable, Optional T = TypeVar('T') S = TypeVar('S') @@ -700,7 +701,7 @@ class A: pass class B(A): pass class C(A): pass def f(func: Callable[[T], S], *z: T, r: Optional[S] = None) -> S: pass -f(lambda x: 0 if isinstance(x, B) else 1) # E: Cannot infer type argument 1 of "f" +reveal_type(f(lambda x: 0 if isinstance(x, B) else 1)) # N: Revealed type is "builtins.int" f(lambda x: 0 if isinstance(x, B) else 1, A())() # E: "int" not callable f(lambda x: x if isinstance(x, B) else B(), A(), r=B())() # E: "B" not callable f( diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index e0f29a19ec1d..9ee30b4df859 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1375,19 +1375,19 @@ class B: pass [builtins fixtures/list.pyi] [case testUninferableLambda] +# flags: --new-type-inference from typing import TypeVar, Callable X = TypeVar('X') def f(x: Callable[[X], X]) -> X: pass -y = f(lambda x: x) # E: Cannot infer type argument 1 of "f" +y = f(lambda x: x) # E: Need type annotation for "y" [case testUninferableLambdaWithTypeError] +# flags: --new-type-inference from typing import TypeVar, Callable X = TypeVar('X') def f(x: Callable[[X], X], y: str) -> X: pass -y = f(lambda x: x, 1) # Fail -[out] -main:4: error: Cannot infer type argument 1 of "f" -main:4: error: Argument 2 to "f" has incompatible type "int"; expected "str" +y = f(lambda x: x, 1) # E: Need type annotation for "y" \ + # E: Argument 2 to "f" has incompatible type "int"; expected "str" [case testInferLambdaNone] # flags: --no-strict-optional diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4910dfe05d31..e59b12d47980 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6483,7 +6483,7 @@ P = ParamSpec("P") R = TypeVar("R") @overload -def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... @overload def func(x: Callable[P, R]) -> Callable[Concatenate[str, P], R]: ... def func(x: Callable[..., R]) -> Callable[..., R]: ... @@ -6501,7 +6501,7 @@ eggs = lambda: 'eggs' reveal_type(func(eggs)) # N: Revealed type is "def (builtins.str) -> builtins.str" spam: Callable[..., str] = lambda x, y: 'baz' -reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> builtins.str" +reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> Any" [builtins fixtures/paramspec.pyi] @@ -6596,3 +6596,20 @@ S = TypeVar("S", bound=str) def foo(x: int = ...) -> Callable[[T], T]: ... @overload def foo(x: S = ...) -> Callable[[T], T]: ... + +[case testOverloadGenericStarArgOverlap] +from typing import Any, Callable, TypeVar, overload, Union, Tuple, List + +F = TypeVar("F", bound=Callable[..., Any]) +S = TypeVar("S", bound=int) + +def id(f: F) -> F: ... + +@overload +def struct(*cols: S) -> int: ... +@overload +def struct(__cols: Union[List[S], Tuple[S, ...]]) -> int: ... +@id +def struct(*cols: Union[S, Union[List[S], Tuple[S, ...]]]) -> int: + pass +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 114fe1f8438a..f523cb005a2c 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1029,7 +1029,7 @@ j = Job(generic_f) reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1)" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`2)" reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] @@ -1048,10 +1048,10 @@ class Job(Generic[_P, _T]): def generic_f(x: _T) -> _T: ... j = Job(generic_f) -reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1], _T`-1]" +reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`2], _T`2]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1) -> _T`-1" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`3) -> _T`3" reveal_type(jf(1)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] @@ -1307,7 +1307,7 @@ reveal_type(bar(C(fn=foo, x=1))) # N: Revealed type is "__main__.C[[x: builtins [builtins fixtures/paramspec.pyi] [case testParamSpecClassConstructor] -from typing import ParamSpec, Callable +from typing import ParamSpec, Callable, TypeVar P = ParamSpec("P") @@ -1315,7 +1315,10 @@ class SomeClass: def __init__(self, a: str) -> None: pass -def func(t: Callable[P, SomeClass], val: Callable[P, SomeClass]) -> None: +def func(t: Callable[P, SomeClass], val: Callable[P, SomeClass]) -> Callable[P, SomeClass]: + pass + +def func_regular(t: Callable[[T], SomeClass], val: Callable[[T], SomeClass]) -> Callable[[T], SomeClass]: pass def constructor(a: str) -> SomeClass: @@ -1324,9 +1327,13 @@ def constructor(a: str) -> SomeClass: def wrong_constructor(a: bool) -> SomeClass: return SomeClass("a") +def wrong_name_constructor(b: bool) -> SomeClass: + return SomeClass("a") + func(SomeClass, constructor) -func(SomeClass, wrong_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[VarArg(), KwArg()], SomeClass]" \ - # E: Argument 2 to "func" has incompatible type "Callable[[bool], SomeClass]"; expected "Callable[[VarArg(), KwArg()], SomeClass]" +reveal_type(func(SomeClass, wrong_constructor)) # N: Revealed type is "def (a: ) -> __main__.SomeClass" +reveal_type(func_regular(SomeClass, wrong_constructor)) # N: Revealed type is "def () -> __main__.SomeClass" +func(SomeClass, wrong_name_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[], SomeClass]" [builtins fixtures/paramspec.pyi] [case testParamSpecInTypeAliasBasic] @@ -1466,8 +1473,7 @@ reveal_type(gs) # N: Revealed type is "builtins.list[def (builtins.int, builtin T = TypeVar("T") class C(Generic[T]): ... -C[Callable[P, int]]() # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ - # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas +C[Callable[P, int]]() [builtins fixtures/paramspec.pyi] [case testConcatDeferralNoCrash] @@ -1547,5 +1553,26 @@ U = TypeVar("U") def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def test(x: U) -> U: ... reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]" -reveal_type(dec(test)) # N: Revealed type is "def [U] (x: U`-1) -> builtins.list[U`-1]" +reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`2) -> builtins.list[T`2]" + +class A: ... +TA = TypeVar("TA", bound=A) + +def test_with_bound(x: TA) -> TA: ... +reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`4) -> builtins.list[T`4]" +dec(test_with_bound)(0) # E: Value of type variable "T" of function cannot be "int" +dec(test_with_bound)(A()) # OK +[builtins fixtures/paramspec.pyi] + +[case testParamSpecNestedApplyNoCrash] +from typing import Callable, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") + +def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ... +def test() -> None: ... +# TODO: avoid this error, although it may be non-trivial. +apply(apply, test) # E: Argument 2 to "apply" has incompatible type "Callable[[], None]"; expected "Callable[P, T]" [builtins fixtures/paramspec.pyi] From 76c16a484b8b4fa6ae10abf4e96a286315ed7093 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:15:42 +0100 Subject: [PATCH 0197/1617] [dmypy] special case stdout and stderr in show_stats too (#15881) When running dmypy, the communication between client and server is via JSON. The JSON contains the keys "out" and "err" for the actual result of "check" command, and "stdout" and "stderr" for the any other stdout and stderr text. show_stats is shown when running with --verbose. It's meant to show other keys in the JSON response, like python version or time taken. It already had some special casing to only show 1 line of text for "out" and "err". Let's add "stdout" and "stderr" to the special casing as well. Also, let's show the remaining number of characters as well. Finally, added a comment in code about stdout, stderr, out, err and how we shouldn't confuse them. (I did) Some more cleanup is needed in this area of the codebase, but will be a separate PR. show_stats outputs something like this: ``` err : out : analytics/scripts/presto/report_query_lo ... 100 more characters platform : linux python_version : 3_9 roundtrip_time : 31.996 status : 2 stderr : \nLOG: Mypy Version: 1.6.0+de ... 50186630 more characters stdout : ``` --- mypy/dmypy/client.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 0e9120608509..c3a2308d1b44 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -562,6 +562,7 @@ def check_output( sys.stdout.write(out) sys.stdout.flush() sys.stderr.write(err) + sys.stderr.flush() if verbose: show_stats(response) if junit_xml: @@ -588,13 +589,14 @@ def check_output( def show_stats(response: Mapping[str, object]) -> None: for key, value in sorted(response.items()): - if key not in ("out", "err"): - print("%-24s: %10s" % (key, "%.3f" % value if isinstance(value, float) else value)) - else: + if key in ("out", "err", "stdout", "stderr"): + # Special case text output to display just 40 characters of text value = repr(value)[1:-1] if len(value) > 50: - value = value[:40] + " ..." + value = f"{value[:40]} ... {len(value)-40} more characters" print("%-24s: %s" % (key, value)) + continue + print("%-24s: %10s" % (key, "%.3f" % value if isinstance(value, float) else value)) @action(hang_parser) @@ -668,6 +670,8 @@ def request( # TODO: Other errors, e.g. ValueError, UnicodeError else: # Display debugging output written to stdout/stderr in the server process for convenience. + # This should not be confused with "out" and "err" fields in the response. + # Those fields hold the output of the "check" command, and are handled in check_output(). stdout = response.get("stdout") if stdout: sys.stdout.write(stdout) From b3d09374dac20c8e775e4380a6b44a56d7b22699 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 17 Aug 2023 15:35:22 +0100 Subject: [PATCH 0198/1617] Fix subtyping between ParamSpecs (#15892) Fixes https://github.com/python/mypy/issues/14169 Fixes https://github.com/python/mypy/issues/14168 Two sings here: * Actually check prefix when we should * `strict_concatenate` check should be off by default (IIUC it is not mandated by the PEP) --- mypy/expandtype.py | 3 +- mypy/messages.py | 18 +++-- mypy/subtypes.py | 17 +++-- test-data/unit/check-overloading.test | 2 +- .../unit/check-parameter-specification.test | 70 +++++++++++++++++++ 5 files changed, 94 insertions(+), 16 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 0e98ed048197..01d9c4463174 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -383,8 +383,6 @@ def visit_callable_type(self, t: CallableType) -> CallableType: t = t.expand_param_spec(repl) return t.copy_modified( arg_types=self.expand_types(t.arg_types), - arg_kinds=t.arg_kinds, - arg_names=t.arg_names, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), ) @@ -402,6 +400,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: arg_kinds=t.arg_kinds[:-2] + prefix.arg_kinds + t.arg_kinds[-2:], arg_names=t.arg_names[:-2] + prefix.arg_names + t.arg_names[-2:], ret_type=t.ret_type.accept(self), + from_concatenate=t.from_concatenate or bool(repl.prefix.arg_types), ) var_arg = t.var_arg() diff --git a/mypy/messages.py b/mypy/messages.py index c9bf26f8952e..aab30ee29108 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2116,9 +2116,11 @@ def report_protocol_problems( return # Report member type conflicts - conflict_types = get_conflict_protocol_types(subtype, supertype, class_obj=class_obj) + conflict_types = get_conflict_protocol_types( + subtype, supertype, class_obj=class_obj, options=self.options + ) if conflict_types and ( - not is_subtype(subtype, erase_type(supertype)) + not is_subtype(subtype, erase_type(supertype), options=self.options) or not subtype.type.defn.type_vars or not supertype.type.defn.type_vars ): @@ -2780,7 +2782,11 @@ def [T <: int] f(self, x: int, y: T) -> None slash = True # If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list - if isinstance(tp.definition, FuncDef) and hasattr(tp.definition, "arguments"): + if ( + isinstance(tp.definition, FuncDef) + and hasattr(tp.definition, "arguments") + and not tp.from_concatenate + ): definition_arg_names = [arg.variable.name for arg in tp.definition.arguments] if ( len(definition_arg_names) > len(tp.arg_names) @@ -2857,7 +2863,7 @@ def get_missing_protocol_members(left: Instance, right: Instance, skip: list[str def get_conflict_protocol_types( - left: Instance, right: Instance, class_obj: bool = False + left: Instance, right: Instance, class_obj: bool = False, options: Options | None = None ) -> list[tuple[str, Type, Type]]: """Find members that are defined in 'left' but have incompatible types. Return them as a list of ('member', 'got', 'expected'). @@ -2872,9 +2878,9 @@ def get_conflict_protocol_types( subtype = mypy.typeops.get_protocol_member(left, member, class_obj) if not subtype: continue - is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True) + is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True, options=options) if IS_SETTABLE in get_member_flags(member, right): - is_compat = is_compat and is_subtype(supertype, subtype) + is_compat = is_compat and is_subtype(supertype, subtype, options=options) if not is_compat: conflicts.append((member, subtype, supertype)) return conflicts diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 60fccc7e357c..11847858c62c 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -600,7 +600,7 @@ def check_mixed( type_state.record_negative_subtype_cache_entry(self._subtype_kind, left, right) return nominal if right.type.is_protocol and is_protocol_implementation( - left, right, proper_subtype=self.proper_subtype + left, right, proper_subtype=self.proper_subtype, options=self.options ): return True # We record negative cache entry here, and not in the protocol check like we do for @@ -647,7 +647,7 @@ def visit_param_spec(self, left: ParamSpecType) -> bool: and right.id == left.id and right.flavor == left.flavor ): - return True + return self._is_subtype(left.prefix, right.prefix) if isinstance(right, Parameters) and are_trivial_parameters(right): return True return self._is_subtype(left.upper_bound, self.right) @@ -696,7 +696,7 @@ def visit_callable_type(self, left: CallableType) -> bool: ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, strict_concatenate=(self.options.extra_checks or self.options.strict_concatenate) if self.options - else True, + else False, ) elif isinstance(right, Overloaded): return all(self._is_subtype(left, item) for item in right.items) @@ -863,7 +863,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: strict_concat = ( (self.options.extra_checks or self.options.strict_concatenate) if self.options - else True + else False ) if left_index not in matched_overloads and ( is_callable_compatible( @@ -1003,6 +1003,7 @@ def is_protocol_implementation( proper_subtype: bool = False, class_obj: bool = False, skip: list[str] | None = None, + options: Options | None = None, ) -> bool: """Check whether 'left' implements the protocol 'right'. @@ -1068,7 +1069,9 @@ def f(self) -> A: ... # Nominal check currently ignores arg names # NOTE: If we ever change this, be sure to also change the call to # SubtypeVisitor.build_subtype_kind(...) down below. - is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=ignore_names) + is_compat = is_subtype( + subtype, supertype, ignore_pos_arg_names=ignore_names, options=options + ) else: is_compat = is_proper_subtype(subtype, supertype) if not is_compat: @@ -1080,7 +1083,7 @@ def f(self) -> A: ... superflags = get_member_flags(member, right) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. - if not is_subtype(supertype, subtype): + if not is_subtype(supertype, subtype, options=options): return False if not class_obj: if IS_SETTABLE not in superflags: @@ -1479,7 +1482,7 @@ def are_parameters_compatible( ignore_pos_arg_names: bool = False, check_args_covariantly: bool = False, allow_partial_overlap: bool = False, - strict_concatenate_check: bool = True, + strict_concatenate_check: bool = False, ) -> bool: """Helper function for is_callable_compatible, used for Parameter compatibility""" if right.is_ellipsis_args: diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index e59b12d47980..4a4c19b4a0e9 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6483,7 +6483,7 @@ P = ParamSpec("P") R = TypeVar("R") @overload -def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... +def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types @overload def func(x: Callable[P, R]) -> Callable[Concatenate[str, P], R]: ... def func(x: Callable[..., R]) -> Callable[..., R]: ... diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f523cb005a2c..b06944389623 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1576,3 +1576,73 @@ def test() -> None: ... # TODO: avoid this error, although it may be non-trivial. apply(apply, test) # E: Argument 2 to "apply" has incompatible type "Callable[[], None]"; expected "Callable[P, T]" [builtins fixtures/paramspec.pyi] + +[case testParamSpecPrefixSubtypingGenericInvalid] +from typing import Generic +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") + +class A(Generic[P]): + def foo(self, *args: P.args, **kwargs: P.kwargs): + ... + +def bar(b: A[P]) -> A[Concatenate[int, P]]: + return b # E: Incompatible return value type (got "A[P]", expected "A[[int, **P]]") +[builtins fixtures/paramspec.pyi] + +[case testParamSpecPrefixSubtypingProtocolInvalid] +from typing import Protocol +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") + +class A(Protocol[P]): + def foo(self, *args: P.args, **kwargs: P.kwargs): + ... + +def bar(b: A[P]) -> A[Concatenate[int, P]]: + return b # E: Incompatible return value type (got "A[P]", expected "A[[int, **P]]") +[builtins fixtures/paramspec.pyi] + +[case testParamSpecPrefixSubtypingValidNonStrict] +from typing import Protocol +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") + +class A(Protocol[P]): + def foo(self, a: int, *args: P.args, **kwargs: P.kwargs): + ... + +class B(Protocol[P]): + def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs): + ... + +def bar(b: B[P]) -> A[Concatenate[int, P]]: + return b +[builtins fixtures/paramspec.pyi] + +[case testParamSpecPrefixSubtypingInvalidStrict] +# flags: --extra-checks +from typing import Protocol +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") + +class A(Protocol[P]): + def foo(self, a: int, *args: P.args, **kwargs: P.kwargs): + ... + +class B(Protocol[P]): + def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs): + ... + +def bar(b: B[P]) -> A[Concatenate[int, P]]: + return b # E: Incompatible return value type (got "B[P]", expected "A[[int, **P]]") \ + # N: Following member(s) of "B[P]" have conflicts: \ + # N: Expected: \ + # N: def foo(self, a: int, int, /, *args: P.args, **kwargs: P.kwargs) -> Any \ + # N: Got: \ + # N: def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs) -> Any +[builtins fixtures/paramspec.pyi] From fa84534b9a9c6bdfc2a155d2e916da0c308402b9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 18 Aug 2023 14:24:41 +0100 Subject: [PATCH 0199/1617] Basic support for decorated overloads (#15898) Fixes https://github.com/python/mypy/issues/15737 Fixes https://github.com/python/mypy/issues/12844 Fixes https://github.com/python/mypy/issues/12716 My goal was to fix the `ParamSpec` issues, but it turns out decorated overloads were not supported at all. Namely: * Decorators on overload items were ignored, caller would see original undecorated item types * Overload item overlap checks were performed for original types, while arguably we should use decorated types * Overload items completeness w.r.t. to implementation was checked with decorated implementation, and undecorated items Here I add basic support using same logic as for regular decorated functions: initially set type to `None` and defer callers until definition is type-checked. Note this results in few more `Cannot determine type` in case of other errors, but I think it is fine. Note I also add special-casing for "inline" applications of generic functions to overload arguments. This use case was mentioned few times alongside overloads. The general fix would be tricky, and my special-casing should cover typical use cases. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 91 ++++++++++++------- mypy/checkexpr.py | 73 +++++++++++++-- mypy/checkmember.py | 12 ++- mypy/semanal.py | 11 ++- test-data/unit/check-generics.test | 4 +- test-data/unit/check-newsemanal.test | 3 +- test-data/unit/check-overloading.test | 27 ++++++ .../unit/check-parameter-specification.test | 28 ++++++ test-data/unit/lib-stub/functools.pyi | 2 +- 9 files changed, 206 insertions(+), 45 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5d97a0dec713..7625bf28a88c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -636,13 +636,30 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: self.visit_decorator(defn.items[0]) for fdef in defn.items: assert isinstance(fdef, Decorator) - self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) + if defn.is_property: + self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) + else: + # Perform full check for real overloads to infer type of all decorated + # overload variants. + self.visit_decorator_inner(fdef, allow_empty=True) if fdef.func.abstract_status in (IS_ABSTRACT, IMPLICITLY_ABSTRACT): num_abstract += 1 if num_abstract not in (0, len(defn.items)): self.fail(message_registry.INCONSISTENT_ABSTRACT_OVERLOAD, defn) if defn.impl: defn.impl.accept(self) + if not defn.is_property: + self.check_overlapping_overloads(defn) + if defn.type is None: + item_types = [] + for item in defn.items: + assert isinstance(item, Decorator) + item_type = self.extract_callable_type(item.var.type, item) + if item_type is not None: + item_types.append(item_type) + if item_types: + defn.type = Overloaded(item_types) + # Check override validity after we analyzed current definition. if defn.info: found_method_base_classes = self.check_method_override(defn) if ( @@ -653,10 +670,35 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: self.msg.no_overridable_method(defn.name, defn) self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl) self.check_inplace_operator_method(defn) - if not defn.is_property: - self.check_overlapping_overloads(defn) return None + def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> CallableType | None: + """Get type as seen by an overload item caller.""" + inner_type = get_proper_type(inner_type) + outer_type: CallableType | None = None + if inner_type is not None and not isinstance(inner_type, AnyType): + if isinstance(inner_type, CallableType): + outer_type = inner_type + elif isinstance(inner_type, Instance): + inner_call = get_proper_type( + analyze_member_access( + name="__call__", + typ=inner_type, + context=ctx, + is_lvalue=False, + is_super=False, + is_operator=True, + msg=self.msg, + original_type=inner_type, + chk=self, + ) + ) + if isinstance(inner_call, CallableType): + outer_type = inner_call + if outer_type is None: + self.msg.not_callable(inner_type, ctx) + return outer_type + def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # At this point we should have set the impl already, and all remaining # items are decorators @@ -680,40 +722,20 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # This can happen if we've got an overload with a different # decorator or if the implementation is untyped -- we gave up on the types. - inner_type = get_proper_type(inner_type) - if inner_type is not None and not isinstance(inner_type, AnyType): - if isinstance(inner_type, CallableType): - impl_type = inner_type - elif isinstance(inner_type, Instance): - inner_call = get_proper_type( - analyze_member_access( - name="__call__", - typ=inner_type, - context=defn.impl, - is_lvalue=False, - is_super=False, - is_operator=True, - msg=self.msg, - original_type=inner_type, - chk=self, - ) - ) - if isinstance(inner_call, CallableType): - impl_type = inner_call - if impl_type is None: - self.msg.not_callable(inner_type, defn.impl) + impl_type = self.extract_callable_type(inner_type, defn.impl) is_descriptor_get = defn.info and defn.name == "__get__" for i, item in enumerate(defn.items): - # TODO overloads involving decorators assert isinstance(item, Decorator) - sig1 = self.function_type(item.func) - assert isinstance(sig1, CallableType) + sig1 = self.extract_callable_type(item.var.type, item) + if sig1 is None: + continue for j, item2 in enumerate(defn.items[i + 1 :]): assert isinstance(item2, Decorator) - sig2 = self.function_type(item2.func) - assert isinstance(sig2, CallableType) + sig2 = self.extract_callable_type(item2.var.type, item2) + if sig2 is None: + continue if not are_argument_counts_overlapping(sig1, sig2): continue @@ -4751,17 +4773,20 @@ def visit_decorator(self, e: Decorator) -> None: e.var.type = AnyType(TypeOfAny.special_form) e.var.is_ready = True return + self.visit_decorator_inner(e) + def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None: if self.recurse_into_functions: with self.tscope.function_scope(e.func): - self.check_func_item(e.func, name=e.func.name) + self.check_func_item(e.func, name=e.func.name, allow_empty=allow_empty) # Process decorators from the inside out to determine decorated signature, which # may be different from the declared signature. sig: Type = self.function_type(e.func) for d in reversed(e.decorators): if refers_to_fullname(d, OVERLOAD_NAMES): - self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) + if not allow_empty: + self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) continue dec = self.expr_checker.accept(d) temp = self.temp_node(sig, context=e) @@ -4788,6 +4813,8 @@ def visit_decorator(self, e: Decorator) -> None: self.msg.fail("Too many arguments for property", e) self.check_incompatible_property_override(e) # For overloaded functions we already checked override for overload as a whole. + if allow_empty: + return if e.func.info and not e.func.is_dynamic() and not e.is_overload: found_method_base_classes = self.check_method_override(e) if ( diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 68ea7c30ed6f..797473f7f58f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -353,12 +353,13 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: elif isinstance(node, FuncDef): # Reference to a global function. result = function_type(node, self.named_type("builtins.function")) - elif isinstance(node, OverloadedFuncDef) and node.type is not None: - # node.type is None when there are multiple definitions of a function - # and it's decorated by something that is not typing.overload - # TODO: use a dummy Overloaded instead of AnyType in this case - # like we do in mypy.types.function_type()? - result = node.type + elif isinstance(node, OverloadedFuncDef): + if node.type is None: + if self.chk.in_checked_function() and node.items: + self.chk.handle_cannot_determine_type(node.name, e) + result = AnyType(TypeOfAny.from_error) + else: + result = node.type elif isinstance(node, TypeInfo): # Reference to a type object. if node.typeddict_type: @@ -1337,6 +1338,55 @@ def transform_callee_type( return callee + def is_generic_decorator_overload_call( + self, callee_type: CallableType, args: list[Expression] + ) -> Overloaded | None: + """Check if this looks like an application of a generic function to overload argument.""" + assert callee_type.variables + if len(callee_type.arg_types) != 1 or len(args) != 1: + # TODO: can we handle more general cases? + return None + if not isinstance(get_proper_type(callee_type.arg_types[0]), CallableType): + return None + if not isinstance(get_proper_type(callee_type.ret_type), CallableType): + return None + with self.chk.local_type_map(): + with self.msg.filter_errors(): + arg_type = get_proper_type(self.accept(args[0], type_context=None)) + if isinstance(arg_type, Overloaded): + return arg_type + return None + + def handle_decorator_overload_call( + self, callee_type: CallableType, overloaded: Overloaded, ctx: Context + ) -> tuple[Type, Type] | None: + """Type-check application of a generic callable to an overload. + + We check call on each individual overload item, and then combine results into a new + overload. This function should be only used if callee_type takes and returns a Callable. + """ + result = [] + inferred_args = [] + for item in overloaded.items: + arg = TempNode(typ=item) + with self.msg.filter_errors() as err: + item_result, inferred_arg = self.check_call(callee_type, [arg], [ARG_POS], ctx) + if err.has_new_errors(): + # This overload doesn't match. + continue + p_item_result = get_proper_type(item_result) + if not isinstance(p_item_result, CallableType): + continue + p_inferred_arg = get_proper_type(inferred_arg) + if not isinstance(p_inferred_arg, CallableType): + continue + inferred_args.append(p_inferred_arg) + result.append(p_item_result) + if not result or not inferred_args: + # None of the overload matched (or overload was initially malformed). + return None + return Overloaded(result), Overloaded(inferred_args) + def check_call_expr_with_callee_type( self, callee_type: Type, @@ -1451,6 +1501,17 @@ def check_call( callee = get_proper_type(callee) if isinstance(callee, CallableType): + if callee.variables: + overloaded = self.is_generic_decorator_overload_call(callee, args) + if overloaded is not None: + # Special casing for inline application of generic callables to overloads. + # Supporting general case would be tricky, but this should cover 95% of cases. + overloaded_result = self.handle_decorator_overload_call( + callee, overloaded, context + ) + if overloaded_result is not None: + return overloaded_result + return self.check_callable_call( callee, args, diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 343dfe3de243..2b0717f181a9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -317,7 +317,17 @@ def analyze_instance_member_access( 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")) + if not isinstance(method, OverloadedFuncDef): + signature = function_type(method, mx.named_type("builtins.function")) + else: + if method.type is None: + # Overloads may be not ready if they are decorated. Handle this in same + # manner as we would handle a regular decorated function: defer if possible. + if not mx.no_deferral and method.items: + mx.not_ready_callback(method.name, mx.context) + return AnyType(TypeOfAny.special_form) + assert isinstance(method.type, Overloaded) + signature = method.type signature = freshen_all_functions_type_vars(signature) if not method.is_static: if name != "__call__": diff --git a/mypy/semanal.py b/mypy/semanal.py index e21fc9f1c23f..9d968d1da781 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1153,7 +1153,16 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: elif not non_overload_indexes: self.handle_missing_overload_implementation(defn) - if types: + if types and not any( + # If some overload items are decorated with other decorators, then + # the overload type will be determined during type checking. + isinstance(it, Decorator) and len(it.decorators) > 1 + for it in defn.items + ): + # TODO: should we enforce decorated overloads consistency somehow? + # Some existing code uses both styles: + # * Put decorator only on implementation, use "effective" types in overloads + # * Put decorator everywhere, use "bare" types in overloads. defn.type = Overloaded(types) defn.type.line = defn.line diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 8c7c4e035961..1fac42b492a8 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3062,10 +3062,10 @@ def dec5(f: Callable[[int], T]) -> Callable[[int], List[T]]: reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]" reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`6) -> S`6" -reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`8) -> S`8" +reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`9) -> S`9" reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" -reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`15) -> builtins.list[S`15]" +reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`16) -> builtins.list[S`16]" dec4(lambda x: x) # E: Incompatible return value type (got "S", expected "List[object]") [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 8300957ee511..ff8d346e74a1 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3207,8 +3207,7 @@ class User: self.first_name = value def __init__(self, name: str) -> None: - self.name = name # E: Cannot assign to a method \ - # E: Incompatible types in assignment (expression has type "str", variable has type "Callable[..., Any]") + self.name = name # E: Cannot assign to a method [case testNewAnalyzerMemberNameMatchesTypedDict] from typing import Union, Any diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4a4c19b4a0e9..b778dc50b376 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6613,3 +6613,30 @@ def struct(__cols: Union[List[S], Tuple[S, ...]]) -> int: ... def struct(*cols: Union[S, Union[List[S], Tuple[S, ...]]]) -> int: pass [builtins fixtures/tuple.pyi] + +[case testRegularGenericDecoratorOverload] +from typing import Callable, overload, TypeVar, List + +S = TypeVar("S") +T = TypeVar("T") +def transform(func: Callable[[S], List[T]]) -> Callable[[S], T]: ... + +@overload +def foo(x: int) -> List[float]: ... +@overload +def foo(x: str) -> List[str]: ... +def foo(x): ... + +reveal_type(transform(foo)) # N: Revealed type is "Overload(def (builtins.int) -> builtins.float, def (builtins.str) -> builtins.str)" + +@transform +@overload +def bar(x: int) -> List[float]: ... +@transform +@overload +def bar(x: str) -> List[str]: ... +@transform +def bar(x): ... + +reveal_type(bar) # N: Revealed type is "Overload(def (builtins.int) -> builtins.float, def (builtins.str) -> builtins.str)" +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index b06944389623..3a8ecdf81c7d 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1646,3 +1646,31 @@ def bar(b: B[P]) -> A[Concatenate[int, P]]: # N: Got: \ # N: def foo(self, a: int, b: int, *args: P.args, **kwargs: P.kwargs) -> Any [builtins fixtures/paramspec.pyi] + +[case testParamSpecDecoratorOverload] +from typing import Callable, overload, TypeVar, List +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") +def transform(func: Callable[P, List[T]]) -> Callable[P, T]: ... + +@overload +def foo(x: int) -> List[float]: ... +@overload +def foo(x: str) -> List[str]: ... +def foo(x): ... + +reveal_type(transform(foo)) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.float, def (x: builtins.str) -> builtins.str)" + +@transform +@overload +def bar(x: int) -> List[float]: ... +@transform +@overload +def bar(x: str) -> List[str]: ... +@transform +def bar(x): ... + +reveal_type(bar) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.float, def (x: builtins.str) -> builtins.str)" +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/lib-stub/functools.pyi b/test-data/unit/lib-stub/functools.pyi index 9e62a14c2f34..e665b2bad0c2 100644 --- a/test-data/unit/lib-stub/functools.pyi +++ b/test-data/unit/lib-stub/functools.pyi @@ -1,4 +1,4 @@ -from typing import Generic, TypeVar, Callable, Any, Mapping +from typing import Generic, TypeVar, Callable, Any, Mapping, overload _T = TypeVar("_T") From b02ddf1db45f6cd1b3a4cf0f40e768b36f5636a7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 18 Aug 2023 16:18:06 +0100 Subject: [PATCH 0200/1617] Polymorphic inference: basic support for variadic types (#15879) This is the fifth PR in the series started by #15287, and a last one for the foreseeable future. This completes polymorphic inference sufficiently for extensive experimentation, and enabling polymorphic fallback by default. Remaining items for which I am going to open follow-up issues: * Enable `--new-type-inference` by default (should be done before everything else in this list). * Use polymorphic inference during unification. * Use polymorphic inference as primary an only mechanism, rather than a fallback if basic inference fails in some way. * Move `apply_poly()` logic from `checkexpr.py` to `applytype.py` (this one depends on everything above). * Experiment with backtracking in the new solver. * Experiment with universal quantification for types other that `Callable` (btw we already have a hacky support for capturing a generic function in an instance with `ParamSpec`). Now some comments on the PR proper. First of all I decided to do some clean-up of `TypeVarTuple` support, but added only strictly necessary parts of the cleanup here. Everything else will be in follow up PR(s). The polymorphic inference/solver/application is practically trivial here, so here is my view on how I see large-scale structure of `TypeVarTuple` implementation: * There should be no special-casing in `applytype.py`, so I deleted everything from there (as I did for `ParamSpec`) and complemented `visit_callable_type()` in `expandtype.py`. Basically, `applytype.py` should have three simple steps: validate substitutions (upper bounds, values, argument kinds etc.); call `expand_type()`; update callable type variables (currently we just reduce the number, but in future we may also add variables there, see TODO that I added). * The only valid positions for a variadic item (a.k.a. `UnpackType`) are inside `Instance`s, `TupleType`s, and `CallableType`s. I like how there is an agreement that for callables there should never be a prefix, and instead prefix should be represented with regular positional arguments. I think that ideally we should enforce this with an `assert` in `CallableType` constructor (similar to how I did this for `ParamSpec`). * Completing `expand_type()` should be a priority (since it describes basic semantics of `TypeVarLikeType`s). I think I made good progress in this direction. IIUC the only valid substitution for `*Ts` are `TupleType.items`, `*tuple[X, ...]`, `Any`, and ``, so it was not hard. * I propose to only allow `TupleType` (mostly for `semanal.py`, see item below), plain `TypeVarTupleType`, and a homogeneous `tuple` instances inside `UnpackType`. Supporting unions of those is not specified by the PEP and support will likely be quite tricky to implement. Also I propose to even eagerly expand type aliases to tuples (since there is no point in supporting recursive types like `A = Tuple[int, *A]`). * I propose to forcefully flatten nested `TupleType`s, there should be no things like `Tuple[X1, *Tuple[X2, *Ts, Y2], Y1]` etc after semantic analysis. (Similarly to how we always flatten `Parameters` for `ParamSpec`, and how we flatten nested unions in `UnionType` _constructor_). Currently we do the flattening/normalization of tuples in `expand_type()` etc. * I suspect `build_constraints_for_unpack()` may be broken, at least when it was used for tuples and callables it did something wrong in few cases I tested (and there are other symptoms I mentioned in a TODO). I therefore re-implemented logic for callables/tuples using a separate dedicated helper. I will investigate more later. As I mentioned above I only implemented strictly minimal amount of the above plan to make my tests pass, but still wanted to write this out to see if there are any objections (or maybe I don't understand something). If there are no objections to this plan, I will continue it in separate PR(s). Btw, I like how with this plan we will have clear logical parallels between `TypeVarTuple` implementation and (recently updated) `ParamSpec` implementation. --------- Co-authored-by: Ivan Levkivskyi --- mypy/applytype.py | 64 ++------ mypy/checkexpr.py | 24 ++- mypy/constraints.py | 192 +++++++++++++++++++----- mypy/expandtype.py | 135 ++++++++--------- mypy/solve.py | 37 +++-- mypy/typeops.py | 3 + mypy/types.py | 7 +- mypy/typevartuples.py | 19 --- test-data/unit/check-generics.test | 144 +++++++++++++++++- test-data/unit/check-typevar-tuple.test | 24 +-- 10 files changed, 440 insertions(+), 209 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 6abe7f0022f8..884be287e33d 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -3,15 +3,13 @@ from typing import Callable, Sequence import mypy.subtypes -from mypy.expandtype import expand_type, expand_unpack_with_variables -from mypy.nodes import ARG_STAR, Context +from mypy.expandtype import expand_type +from mypy.nodes import Context from mypy.types import ( AnyType, CallableType, - Instance, ParamSpecType, PartialType, - TupleType, Type, TypeVarId, TypeVarLikeType, @@ -21,7 +19,6 @@ UnpackType, get_proper_type, ) -from mypy.typevartuples import find_unpack_in_list, replace_starargs def get_target_type( @@ -107,6 +104,8 @@ def apply_generic_arguments( if target_type is not None: id_to_type[tvar.id] = target_type + # TODO: validate arg_kinds/arg_names for ParamSpec and TypeVarTuple replacements, + # not just type variable bounds above. param_spec = callable.param_spec() if param_spec is not None: nt = id_to_type.get(param_spec.id) @@ -122,55 +121,9 @@ def apply_generic_arguments( # Apply arguments to argument types. var_arg = callable.var_arg() if var_arg is not None and isinstance(var_arg.typ, UnpackType): - star_index = callable.arg_kinds.index(ARG_STAR) - callable = callable.copy_modified( - arg_types=( - [expand_type(at, id_to_type) for at in callable.arg_types[:star_index]] - + [callable.arg_types[star_index]] - + [expand_type(at, id_to_type) for at in callable.arg_types[star_index + 1 :]] - ) - ) - - unpacked_type = get_proper_type(var_arg.typ.type) - if isinstance(unpacked_type, TupleType): - # Assuming for now that because we convert prefixes to positional arguments, - # the first argument is always an unpack. - expanded_tuple = expand_type(unpacked_type, id_to_type) - if isinstance(expanded_tuple, TupleType): - # TODO: handle the case where the tuple has an unpack. This will - # hit an assert below. - expanded_unpack = find_unpack_in_list(expanded_tuple.items) - if expanded_unpack is not None: - callable = callable.copy_modified( - arg_types=( - callable.arg_types[:star_index] - + [expanded_tuple] - + callable.arg_types[star_index + 1 :] - ) - ) - else: - callable = replace_starargs(callable, expanded_tuple.items) - else: - # TODO: handle the case for if we get a variable length tuple. - assert False, f"mypy bug: unimplemented case, {expanded_tuple}" - elif isinstance(unpacked_type, TypeVarTupleType): - expanded_tvt = expand_unpack_with_variables(var_arg.typ, id_to_type) - if isinstance(expanded_tvt, list): - for t in expanded_tvt: - assert not isinstance(t, UnpackType) - callable = replace_starargs(callable, expanded_tvt) - else: - assert isinstance(expanded_tvt, Instance) - assert expanded_tvt.type.fullname == "builtins.tuple" - callable = callable.copy_modified( - arg_types=( - callable.arg_types[:star_index] - + [expanded_tvt.args[0]] - + callable.arg_types[star_index + 1 :] - ) - ) - else: - assert False, "mypy bug: unhandled case applying unpack" + callable = expand_type(callable, id_to_type) + assert isinstance(callable, CallableType) + return callable.copy_modified(variables=[tv for tv in tvars if tv.id not in id_to_type]) else: callable = callable.copy_modified( arg_types=[expand_type(at, id_to_type) for at in callable.arg_types] @@ -183,6 +136,9 @@ def apply_generic_arguments( type_guard = None # The callable may retain some type vars if only some were applied. + # TODO: move apply_poly() logic from checkexpr.py here when new inference + # becomes universally used (i.e. in all passes + in unification). + # With this new logic we can actually *add* some new free variables. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 797473f7f58f..420cfd990820 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2373,11 +2373,15 @@ def check_argument_types( ] actual_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1) - assert isinstance(orig_callee_arg_type, TupleType) - assert orig_callee_arg_type.items - callee_arg_types = orig_callee_arg_type.items + # TODO: can we really assert this? What if formal is just plain Unpack[Ts]? + assert isinstance(orig_callee_arg_type, UnpackType) + assert isinstance(orig_callee_arg_type.type, ProperType) and isinstance( + orig_callee_arg_type.type, TupleType + ) + assert orig_callee_arg_type.type.items + callee_arg_types = orig_callee_arg_type.type.items callee_arg_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * ( - len(orig_callee_arg_type.items) - 1 + len(orig_callee_arg_type.type.items) - 1 ) expanded_tuple = True @@ -5853,8 +5857,9 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: return super().visit_param_spec(t) def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: - # TODO: Support polymorphic apply for TypeVarTuple. - raise PolyTranslationError() + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_type_var_tuple(t) def visit_type_alias_type(self, t: TypeAliasType) -> Type: if not t.args: @@ -5888,7 +5893,6 @@ def visit_instance(self, t: Instance) -> Type: return t.copy_modified(args=new_args) # There is the same problem with callback protocols as with aliases # (callback protocols are essentially more flexible aliases to callables). - # Note: consider supporting bindings in instances, e.g. LRUCache[[x: T], T]. if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]: if t.type in self.seen_aliases: raise PolyTranslationError() @@ -5923,6 +5927,12 @@ def __init__(self) -> None: def visit_type_var(self, t: TypeVarType) -> bool: return True + def visit_param_spec(self, t: ParamSpecType) -> bool: + return True + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: + return True + def has_erased_component(t: Type | None) -> bool: return t is not None and t.accept(HasErasedComponentsQuery()) diff --git a/mypy/constraints.py b/mypy/constraints.py index 04c3378ce16b..26504ed06b3e 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -9,7 +9,16 @@ from mypy.argmap import ArgTypeExpander from mypy.erasetype import erase_typevars from mypy.maptype import map_instance_to_supertype -from mypy.nodes import ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, CONTRAVARIANT, COVARIANT, ArgKind +from mypy.nodes import ( + ARG_OPT, + ARG_POS, + ARG_STAR, + ARG_STAR2, + CONTRAVARIANT, + COVARIANT, + ArgKind, + TypeInfo, +) from mypy.types import ( TUPLE_LIKE_INSTANCE_NAMES, AnyType, @@ -70,6 +79,8 @@ class Constraint: def __init__(self, type_var: TypeVarLikeType, op: int, target: Type) -> None: self.type_var = type_var.id self.op = op + # TODO: should we add "assert not isinstance(target, UnpackType)"? + # UnpackType is a synthetic type, and is never valid as a constraint target. self.target = target self.origin_type_var = type_var # These are additional type variables that should be solved for together with type_var. @@ -940,17 +951,20 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: if not template.is_ellipsis_args: unpack_present = find_unpack_in_list(template.arg_types) if unpack_present is not None: - ( - unpack_constraints, - cactual_args_t, - template_args_t, - ) = find_and_build_constraints_for_unpack( - tuple(cactual.arg_types), tuple(template.arg_types), self.direction + # We need to re-normalize args to the form they appear in tuples, + # for callables we always pack the suffix inside another tuple. + unpack = template.arg_types[unpack_present] + assert isinstance(unpack, UnpackType) + tuple_type = get_tuple_fallback_from_unpack(unpack) + template_types = repack_callable_args(template, tuple_type) + actual_types = repack_callable_args(cactual, tuple_type) + # Now we can use the same general helper as for tuple types. + unpack_constraints = build_constraints_for_simple_unpack( + template_types, actual_types, neg_op(self.direction) ) - template_args = list(template_args_t) - cactual_args = list(cactual_args_t) + template_args = [] + cactual_args = [] res.extend(unpack_constraints) - assert len(template_args) == len(cactual_args) else: template_args = template.arg_types cactual_args = cactual.arg_types @@ -961,7 +975,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # branches), and in Callable vs Callable inference (two branches). for t, a in zip(template_args, cactual_args): # This avoids bogus constraints like T <: P.args - if isinstance(a, ParamSpecType): + if isinstance(a, (ParamSpecType, UnpackType)): # TODO: can we infer something useful for *T vs P? continue # Negate direction due to function argument type contravariance. @@ -1093,13 +1107,11 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]: return [Constraint(type_var=unpacked_type, op=self.direction, target=actual)] else: assert isinstance(actual, TupleType) - ( - unpack_constraints, - actual_items, - template_items, - ) = find_and_build_constraints_for_unpack( - tuple(actual.items), tuple(template.items), self.direction + unpack_constraints = build_constraints_for_simple_unpack( + template.items, actual.items, self.direction ) + actual_items: tuple[Type, ...] = () + template_items: tuple[Type, ...] = () res.extend(unpack_constraints) elif isinstance(actual, TupleType): actual_items = tuple(actual.items) @@ -1232,28 +1244,132 @@ def find_matching_overload_items( return res -def find_and_build_constraints_for_unpack( - mapped: tuple[Type, ...], template: tuple[Type, ...], direction: int -) -> tuple[list[Constraint], tuple[Type, ...], tuple[Type, ...]]: - mapped_prefix_len = find_unpack_in_list(mapped) - if mapped_prefix_len is not None: - mapped_suffix_len: int | None = len(mapped) - mapped_prefix_len - 1 +def get_tuple_fallback_from_unpack(unpack: UnpackType) -> TypeInfo | None: + """Get builtins.tuple type from available types to construct homogeneous tuples.""" + tp = get_proper_type(unpack.type) + if isinstance(tp, Instance) and tp.type.fullname == "builtins.tuple": + return tp.type + if isinstance(tp, TypeVarTupleType): + return tp.tuple_fallback.type + if isinstance(tp, TupleType): + for base in tp.partial_fallback.type.mro: + if base.fullname == "builtins.tuple": + return base + return None + + +def repack_callable_args(callable: CallableType, tuple_type: TypeInfo | None) -> list[Type]: + """Present callable with star unpack in a normalized form. + + Since positional arguments cannot follow star argument, they are packed in a suffix, + while prefix is represented as individual positional args. We want to put all in a single + list with unpack in the middle, and prefix/suffix on the sides (as they would appear + in e.g. a TupleType). + """ + if ARG_STAR not in callable.arg_kinds: + return callable.arg_types + star_index = callable.arg_kinds.index(ARG_STAR) + arg_types = callable.arg_types[:star_index] + star_type = callable.arg_types[star_index] + suffix_types = [] + if not isinstance(star_type, UnpackType): + if tuple_type is not None: + # Re-normalize *args: X -> *args: *tuple[X, ...] + star_type = UnpackType(Instance(tuple_type, [star_type])) + else: + # This is unfortunate, something like tuple[Any, ...] would be better. + star_type = UnpackType(AnyType(TypeOfAny.from_error)) else: - mapped_suffix_len = None + tp = get_proper_type(star_type.type) + if isinstance(tp, TupleType): + assert isinstance(tp.items[0], UnpackType) + star_type = tp.items[0] + suffix_types = tp.items[1:] + return arg_types + [star_type] + suffix_types - template_prefix_len = find_unpack_in_list(template) - assert template_prefix_len is not None - template_suffix_len = len(template) - template_prefix_len - 1 - return build_constraints_for_unpack( - mapped, - mapped_prefix_len, - mapped_suffix_len, - template, - template_prefix_len, - template_suffix_len, - direction, +def build_constraints_for_simple_unpack( + template_args: list[Type], actual_args: list[Type], direction: int +) -> list[Constraint]: + """Infer constraints between two lists of types with variadic items. + + This function is only supposed to be called when a variadic item is present in templates. + If there is no variadic item the actuals, we simply use split_with_prefix_and_suffix() + and infer prefix <: prefix, suffix <: suffix, variadic <: middle. If there is a variadic + item in the actuals we need to be more careful, only common prefix/suffix can generate + constraints, also we can only infer constraints for variadic template item, if template + prefix/suffix are shorter that actual ones, otherwise there may be partial overlap + between variadic items, for example if template prefix is longer: + + templates: T1, T2, Ts, Ts, Ts, ... + actuals: A1, As, As, As, ... + + Note: this function can only be called for builtin variadic constructors: Tuple and Callable, + for Instances variance depends on position, and a much more complex function + build_constraints_for_unpack() should be used. + """ + template_unpack = find_unpack_in_list(template_args) + assert template_unpack is not None + template_prefix = template_unpack + template_suffix = len(template_args) - template_prefix - 1 + + t_unpack = None + res = [] + + actual_unpack = find_unpack_in_list(actual_args) + if actual_unpack is None: + t_unpack = template_args[template_unpack] + if template_prefix + template_suffix > len(actual_args): + # These can't be subtypes of each-other, return fast. + assert isinstance(t_unpack, UnpackType) + if isinstance(t_unpack.type, TypeVarTupleType): + # Set TypeVarTuple to empty to improve error messages. + return [ + Constraint( + t_unpack.type, direction, TupleType([], t_unpack.type.tuple_fallback) + ) + ] + else: + return [] + common_prefix = template_prefix + common_suffix = template_suffix + else: + actual_prefix = actual_unpack + actual_suffix = len(actual_args) - actual_prefix - 1 + common_prefix = min(template_prefix, actual_prefix) + common_suffix = min(template_suffix, actual_suffix) + if actual_prefix >= template_prefix and actual_suffix >= template_suffix: + # This is the only case where we can guarantee there will be no partial overlap. + t_unpack = template_args[template_unpack] + + # Handle constraints from prefixes/suffixes first. + start, middle, end = split_with_prefix_and_suffix( + tuple(actual_args), common_prefix, common_suffix ) + for t, a in zip(template_args[:common_prefix], start): + res.extend(infer_constraints(t, a, direction)) + if common_suffix: + for t, a in zip(template_args[-common_suffix:], end): + res.extend(infer_constraints(t, a, direction)) + + if t_unpack is not None: + # Add constraint(s) for variadic item when possible. + assert isinstance(t_unpack, UnpackType) + tp = get_proper_type(t_unpack.type) + if isinstance(tp, Instance) and tp.type.fullname == "builtins.tuple": + # Homogeneous case *tuple[T, ...] <: [X, Y, Z, ...]. + for a in middle: + # TODO: should we use union instead of join here? + if not isinstance(a, UnpackType): + res.extend(infer_constraints(tp.args[0], a, direction)) + else: + a_tp = get_proper_type(a.type) + # This is the case *tuple[T, ...] <: *tuple[A, ...]. + if isinstance(a_tp, Instance) and a_tp.type.fullname == "builtins.tuple": + res.extend(infer_constraints(tp.args[0], a_tp.args[0], direction)) + elif isinstance(tp, TypeVarTupleType): + res.append(Constraint(tp, direction, TupleType(list(middle), tp.tuple_fallback))) + return res def build_constraints_for_unpack( @@ -1268,6 +1384,10 @@ def build_constraints_for_unpack( template_suffix_len: int, direction: int, ) -> tuple[list[Constraint], tuple[Type, ...], tuple[Type, ...]]: + # TODO: this function looks broken: + # a) it should take into account variances, but it doesn't + # b) it looks like both call sites always pass identical values to args (2, 3) and (5, 6) + # because after map_instance_to_supertype() both template and actual have same TypeInfo. if mapped_prefix_len is None: mapped_prefix_len = template_prefix_len if mapped_suffix_len is None: @@ -1314,4 +1434,4 @@ def build_constraints_for_unpack( if len(template_unpack.items) == len(mapped_middle): for template_arg, item in zip(template_unpack.items, mapped_middle): res.extend(infer_constraints(template_arg, item, direction)) - return (res, mapped_prefix + mapped_suffix, template_prefix + template_suffix) + return res, mapped_prefix + mapped_suffix, template_prefix + template_suffix diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 01d9c4463174..6f69e09936db 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -257,7 +257,7 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: # Sometimes solver may need to expand a type variable with (a copy of) itself # (usually together with other TypeVars, but it is hard to filter out TypeVarTuples). - repl = self.variables[t.id] + repl = self.variables.get(t.id, t) if isinstance(repl, TypeVarTupleType): return repl raise NotImplementedError @@ -269,45 +269,54 @@ def visit_unpack_type(self, t: UnpackType) -> Type: # Relevant sections that can call unpack should call expand_unpack() # instead. # However, if the item is a variadic tuple, we can simply carry it over. + # In particular, if we expand A[*tuple[T, ...]] with substitutions {T: str}, # it is hard to assert this without getting proper type. return UnpackType(t.type.accept(self)) - def expand_unpack(self, t: UnpackType) -> list[Type] | Instance | AnyType | None: - return expand_unpack_with_variables(t, self.variables) + def expand_unpack(self, t: UnpackType) -> list[Type] | AnyType | UninhabitedType: + assert isinstance(t.type, TypeVarTupleType) + repl = get_proper_type(self.variables.get(t.type.id, t.type)) + if isinstance(repl, TupleType): + return repl.items + elif ( + isinstance(repl, Instance) + and repl.type.fullname == "builtins.tuple" + or isinstance(repl, TypeVarTupleType) + ): + return [UnpackType(typ=repl)] + elif isinstance(repl, (AnyType, UninhabitedType)): + # tuple[Any, ...] for Any would be better, but we don't have + # the type info to construct that type here. + return repl + else: + raise RuntimeError(f"Invalid type replacement to expand: {repl}") def visit_parameters(self, t: Parameters) -> Type: return t.copy_modified(arg_types=self.expand_types(t.arg_types)) + # TODO: can we simplify this method? It is too long. def interpolate_args_for_unpack( self, t: CallableType, var_arg: UnpackType ) -> tuple[list[str | None], list[ArgKind], list[Type]]: star_index = t.arg_kinds.index(ARG_STAR) - # We have something like Unpack[Tuple[X1, X2, Unpack[Ts], Y1, Y2]] var_arg_type = get_proper_type(var_arg.type) + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] if isinstance(var_arg_type, TupleType): expanded_tuple = var_arg_type.accept(self) - # TODO: handle the case that expanded_tuple is a variable length tuple. assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) expanded_items = expanded_tuple.items else: + # We have plain Unpack[Ts] expanded_items_res = self.expand_unpack(var_arg) if isinstance(expanded_items_res, list): expanded_items = expanded_items_res - elif ( - isinstance(expanded_items_res, Instance) - and expanded_items_res.type.fullname == "builtins.tuple" - ): - # TODO: We shouldnt't simply treat this as a *arg because of suffix handling - # (there cannot be positional args after a *arg) + else: + # We got Any or arg_types = ( - t.arg_types[:star_index] - + [expanded_items_res.args[0]] - + t.arg_types[star_index + 1 :] + t.arg_types[:star_index] + [expanded_items_res] + t.arg_types[star_index + 1 :] ) - return (t.arg_names, t.arg_kinds, arg_types) - else: - return (t.arg_names, t.arg_kinds, t.arg_types) + return t.arg_names, t.arg_kinds, arg_types expanded_unpack_index = find_unpack_in_list(expanded_items) # This is the case where we just have Unpack[Tuple[X1, X2, X3]] @@ -337,13 +346,14 @@ def interpolate_args_for_unpack( expanded_unpack = expanded_items[expanded_unpack_index] assert isinstance(expanded_unpack, UnpackType) - # Extract the typevartuple so we can get a tuple fallback from it. + # Extract the TypeVarTuple, so we can get a tuple fallback from it. expanded_unpacked_tvt = expanded_unpack.type if isinstance(expanded_unpacked_tvt, TypeVarTupleType): fallback = expanded_unpacked_tvt.tuple_fallback else: # This can happen when tuple[Any, ...] is used to "patch" a variadic - # generic type without type arguments provided. + # generic type without type arguments provided, or when substitution is + # homogeneous tuple. assert isinstance(expanded_unpacked_tvt, ProperType) assert isinstance(expanded_unpacked_tvt, Instance) assert expanded_unpacked_tvt.type.fullname == "builtins.tuple" @@ -354,18 +364,31 @@ def interpolate_args_for_unpack( arg_kinds = ( t.arg_kinds[:star_index] + [ARG_POS] * prefix_len + t.arg_kinds[star_index:] ) - arg_types = ( - self.expand_types(t.arg_types[:star_index]) - + expanded_items[:prefix_len] - # Constructing the Unpack containing the tuple without the prefix. - + [ - UnpackType(TupleType(expanded_items[prefix_len:], fallback)) - if len(expanded_items) - prefix_len > 1 - else expanded_items[0] - ] - + self.expand_types(t.arg_types[star_index + 1 :]) - ) - return (arg_names, arg_kinds, arg_types) + if ( + len(expanded_items) == 1 + and isinstance(expanded_unpack.type, ProperType) + and isinstance(expanded_unpack.type, Instance) + ): + assert expanded_unpack.type.type.fullname == "builtins.tuple" + # Normalize *args: *tuple[X, ...] -> *args: X + arg_types = ( + self.expand_types(t.arg_types[:star_index]) + + [expanded_unpack.type.args[0]] + + self.expand_types(t.arg_types[star_index + 1 :]) + ) + else: + arg_types = ( + self.expand_types(t.arg_types[:star_index]) + + expanded_items[:prefix_len] + # Constructing the Unpack containing the tuple without the prefix. + + [ + UnpackType(TupleType(expanded_items[prefix_len:], fallback)) + if len(expanded_items) - prefix_len > 1 + else expanded_items[prefix_len] + ] + + self.expand_types(t.arg_types[star_index + 1 :]) + ) + return arg_names, arg_kinds, arg_types def visit_callable_type(self, t: CallableType) -> CallableType: param_spec = t.param_spec() @@ -430,7 +453,7 @@ def visit_overloaded(self, t: Overloaded) -> Type: def expand_types_with_unpack( self, typs: Sequence[Type] - ) -> list[Type] | AnyType | UninhabitedType | Instance: + ) -> list[Type] | AnyType | UninhabitedType: """Expands a list of types that has an unpack. In corner cases, this can return a type rather than a list, in which case this @@ -444,15 +467,8 @@ def expand_types_with_unpack( for item in typs: if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): unpacked_items = self.expand_unpack(item) - if unpacked_items is None: - # TODO: better error, something like tuple of unknown? - return UninhabitedType() - elif isinstance(unpacked_items, Instance): - if len(typs) == 1: - return unpacked_items - else: - assert False, "Invalid unpack of variable length tuple" - elif isinstance(unpacked_items, AnyType): + if isinstance(unpacked_items, (AnyType, UninhabitedType)): + # TODO: better error for , something like tuple of unknown? return unpacked_items else: items.extend(unpacked_items) @@ -464,6 +480,14 @@ def expand_types_with_unpack( def visit_tuple_type(self, t: TupleType) -> Type: items = self.expand_types_with_unpack(t.items) if isinstance(items, list): + if len(items) == 1: + # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] + item = items[0] + if isinstance(item, UnpackType): + assert isinstance(item.type, ProperType) + if isinstance(item.type, Instance): + assert item.type.type.fullname == "builtins.tuple" + return item.type fallback = t.partial_fallback.accept(self) assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) return t.copy_modified(items=items, fallback=fallback) @@ -509,6 +533,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: # alias itself), so we just expand the arguments. args = self.expand_types_with_unpack(t.args) if isinstance(args, list): + # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]? return t.copy_modified(args=args) else: return args @@ -520,34 +545,6 @@ def expand_types(self, types: Iterable[Type]) -> list[Type]: return a -def expand_unpack_with_variables( - t: UnpackType, variables: Mapping[TypeVarId, Type] -) -> list[Type] | Instance | AnyType | None: - """May return either a list of types to unpack to, any, or a single - variable length tuple. The latter may not be valid in all contexts. - """ - if isinstance(t.type, TypeVarTupleType): - repl = get_proper_type(variables.get(t.type.id, t)) - if isinstance(repl, TupleType): - return repl.items - elif isinstance(repl, Instance) and repl.type.fullname == "builtins.tuple": - return repl - elif isinstance(repl, AnyType): - # tuple[Any, ...] would be better, but we don't have - # the type info to construct that type here. - return repl - elif isinstance(repl, TypeVarTupleType): - return [UnpackType(typ=repl)] - elif isinstance(repl, UnpackType): - return [repl] - elif isinstance(repl, UninhabitedType): - return None - else: - raise NotImplementedError(f"Invalid type replacement to expand: {repl}") - else: - raise NotImplementedError(f"Invalid type to expand: {t.type}") - - @overload def expand_self_type(var: Var, typ: ProperType, replacement: ProperType) -> ProperType: ... diff --git a/mypy/solve.py b/mypy/solve.py index 4b2b899c2a8d..5945d97ed85a 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, Sequence +from typing import Iterable, Sequence, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints @@ -19,13 +19,16 @@ NoneType, ParamSpecType, ProperType, + TupleType, Type, TypeOfAny, TypeVarId, TypeVarLikeType, + TypeVarTupleType, TypeVarType, UninhabitedType, UnionType, + UnpackType, get_proper_type, ) from mypy.typestate import type_state @@ -330,6 +333,23 @@ def is_trivial_bound(tp: ProperType) -> bool: return isinstance(tp, Instance) and tp.type.fullname == "builtins.object" +def find_linear(c: Constraint) -> Tuple[bool, TypeVarId | None]: + """Find out if this constraint represent a linear relationship, return target id if yes.""" + if isinstance(c.origin_type_var, TypeVarType): + if isinstance(c.target, TypeVarType): + return True, c.target.id + if isinstance(c.origin_type_var, ParamSpecType): + if isinstance(c.target, ParamSpecType) and not c.target.prefix.arg_types: + return True, c.target.id + if isinstance(c.origin_type_var, TypeVarTupleType): + target = get_proper_type(c.target) + if isinstance(target, TupleType) and len(target.items) == 1: + item = target.items[0] + if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): + return True, item.type.id + return False, None + + def transitive_closure( tvars: list[TypeVarId], constraints: list[Constraint] ) -> tuple[Graph, Bounds, Bounds]: @@ -361,16 +381,15 @@ def transitive_closure( c = remaining.pop() # Note that ParamSpec constraint P <: Q may be considered linear only if Q has no prefix, # for cases like P <: Concatenate[T, Q] we should consider this non-linear and put {P} and - # {T, Q} into separate SCCs. - if ( - isinstance(c.target, TypeVarType) - or isinstance(c.target, ParamSpecType) - and not c.target.prefix.arg_types - ) and c.target.id in tvars: + # {T, Q} into separate SCCs. Similarly, Ts <: Tuple[*Us] considered linear, while + # Ts <: Tuple[*Us, U] is non-linear. + is_linear, target_id = find_linear(c) + if is_linear and target_id in tvars: + assert target_id is not None if c.op == SUBTYPE_OF: - lower, upper = c.type_var, c.target.id + lower, upper = c.type_var, target_id else: - lower, upper = c.target.id, c.type_var + lower, upper = target_id, c.type_var if (lower, upper) in graph: continue graph |= { diff --git a/mypy/typeops.py b/mypy/typeops.py index d746ea701fde..22dbd9e9f42e 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -973,6 +973,9 @@ def visit_type_var(self, t: TypeVarType) -> list[TypeVarLikeType]: def visit_param_spec(self, t: ParamSpecType) -> list[TypeVarLikeType]: return [t] if self.include_all else [] + def visit_type_var_tuple(self, t: TypeVarTupleType) -> list[TypeVarLikeType]: + return [t] if self.include_all else [] + def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool: """Does this type have a custom special method such as __format__() or __eq__()? diff --git a/mypy/types.py b/mypy/types.py index 359ca713616b..d4e2fc7cb63c 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1045,7 +1045,8 @@ class UnpackType(ProperType): or unpacking * syntax. The inner type should be either a TypeVarTuple, a constant size - tuple, or a variable length tuple, or a union of one of those. + tuple, or a variable length tuple. Type aliases to these are not allowed, + except during semantic analysis. """ __slots__ = ["type"] @@ -2260,6 +2261,10 @@ def __init__( ) -> None: super().__init__(line, column) self.partial_fallback = fallback + # TODO: flatten/normalize unpack items (very similar to unions) here. + # Probably also for instances, type aliases, callables, and Unpack itself. For example, + # tuple[*tuple[X, ...], ...] -> tuple[X, ...] and Tuple[*tuple[X, ...]] -> tuple[X, ...]. + # Currently normalization happens in expand_type() et al., which is sub-optimal. self.items = items self.implicit = implicit diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index ac5f4e43c3bf..29c800140eec 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -4,9 +4,7 @@ from typing import Sequence -from mypy.nodes import ARG_POS, ARG_STAR from mypy.types import ( - CallableType, Instance, ProperType, Type, @@ -179,20 +177,3 @@ def extract_unpack(types: Sequence[Type]) -> ProperType | None: if isinstance(types[0], UnpackType): return get_proper_type(types[0].type) return None - - -def replace_starargs(callable: CallableType, types: list[Type]) -> CallableType: - star_index = callable.arg_kinds.index(ARG_STAR) - arg_kinds = ( - callable.arg_kinds[:star_index] - + [ARG_POS] * len(types) - + callable.arg_kinds[star_index + 1 :] - ) - arg_names = ( - callable.arg_names[:star_index] - + [None] * len(types) - + callable.arg_names[star_index + 1 :] - ) - arg_types = callable.arg_types[:star_index] + types + callable.arg_types[star_index + 1 :] - - return callable.copy_modified(arg_types=arg_types, arg_names=arg_names, arg_kinds=arg_kinds) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 1fac42b492a8..95a7bdd2b2cd 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3144,7 +3144,7 @@ def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> T`2" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, x: T`5) -> T`5" reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`8, x: U`-1) -> Tuple[T`8, U`-1]" -# This is counter-intuitive but looks correct, dec matches itself only if P is empty +# This is counter-intuitive but looks correct, dec matches itself only if P can be empty reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`11, f: def () -> def (T`11) -> S`12) -> S`12" [builtins fixtures/list.pyi] @@ -3179,7 +3179,6 @@ P = ParamSpec('P') Q = ParamSpec('Q') class Foo(Generic[P]): ... -class Bar(Generic[P, T]): ... def dec(f: Callable[P, int]) -> Callable[P, Foo[P]]: ... h: Callable[Concatenate[T, Q], int] @@ -3263,3 +3262,144 @@ def pop_off(fn: Callable[Concatenate[T1, P], T2]) -> Callable[P, Callable[[T1], def test(command: Foo[Q]) -> Foo[Q]: ... reveal_type(test) # N: Revealed type is "def () -> def [Q] (__main__.Foo[Q`-1]) -> __main__.Foo[Q`-1]" [builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericVariadicBasicInList] +# flags: --new-type-inference +from typing import Tuple, TypeVar, List, Callable +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +def dec(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], List[T]]: ... + +U = TypeVar("U") +V = TypeVar("V") +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... +def pair(x: U, y: V) -> Tuple[U, V]: ... + +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> builtins.list[T`2]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`4, T`4) -> builtins.list[T`4]" +reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericVariadicBasicDeList] +# flags: --new-type-inference +from typing import Tuple, TypeVar, List, Callable +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +def dec(f: Callable[[Unpack[Ts]], List[T]]) -> Callable[[Unpack[Ts]], T]: ... + +U = TypeVar("U") +V = TypeVar("V") +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... + +reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`2]) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`4], builtins.list[T`4]) -> T`4" +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericVariadicPopOff] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +def dec(f: Callable[[T, Unpack[Ts]], S]) -> Callable[[Unpack[Ts]], Callable[[T], S]]: ... + +U = TypeVar("U") +V = TypeVar("V") +def id(x: U) -> U: ... +def either(x: U, y: U) -> U: ... +def pair(x: U, y: V) -> Tuple[U, V]: ... + +reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`4) -> def (T`4) -> T`4" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (V`-2) -> def [T] (T`7) -> Tuple[T`7, V`-2]" +reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, Ts, S] (def (T`-1, *Unpack[Ts`-2]) -> S`-3) -> def (*Unpack[Ts`-2]) -> def (T`-1) -> S`-3" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericVariadicPopOn] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Tuple +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +def dec(f: Callable[[Unpack[Ts]], Callable[[T], S]]) -> Callable[[T, Unpack[Ts]], S]: ... + +U = TypeVar("U") +V = TypeVar("V") +def id() -> Callable[[U], U]: ... +def either(x: U) -> Callable[[U], U]: ... +def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... + +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> T`5" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`8, U`-1) -> Tuple[T`8, U`-1]" +# This is counter-intuitive but looks correct, dec matches itself only if Ts is empty +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`11, def () -> def (T`11) -> S`12) -> S`12" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericVariadicVsVariadic] +# flags: --new-type-inference +from typing import TypeVar, Callable, List, Generic +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +Us = TypeVarTuple("Us") + +class Foo(Generic[Unpack[Ts]]): ... +class Bar(Generic[Unpack[Ts], T]): ... + +def dec(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], List[T]]: ... +# TODO: do not crash on Foo[Us] (with missing Unpack), instead give an error. +def f(*args: Unpack[Us]) -> Foo[Unpack[Us]]: ... +reveal_type(dec(f)) # N: Revealed type is "def [Ts] (*Unpack[Ts`1]) -> builtins.list[__main__.Foo[Unpack[Ts`1]]]" +g: Callable[[Unpack[Us]], Foo[Unpack[Us]]] +reveal_type(dec(g)) # N: Revealed type is "def [Ts] (*Unpack[Ts`3]) -> builtins.list[__main__.Foo[Unpack[Ts`3]]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericVariadicVsVariadicConcatenate] +# flags: --new-type-inference +from typing import TypeVar, Callable, Generic +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +Us = TypeVarTuple("Us") + +class Foo(Generic[Unpack[Ts]]): ... + +def dec(f: Callable[[Unpack[Ts]], int]) -> Callable[[Unpack[Ts]], Foo[Unpack[Ts]]]: ... +h: Callable[[T, Unpack[Us]], int] +g: Callable[[T, Unpack[Us]], int] +h = g +reveal_type(dec(h)) # N: Revealed type is "def [T, Us] (T`-1, *Unpack[Us`-2]) -> __main__.Foo[T`-1, Unpack[Us`-2]]" +[builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericVariadicSecondary] +# flags: --new-type-inference +from typing import TypeVar, Callable, Generic +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +Us = TypeVarTuple("Us") + +class Foo(Generic[Unpack[Ts]]): ... + +def dec(f: Callable[[Unpack[Ts]], Foo[Unpack[Ts]]]) -> Callable[[Unpack[Ts]], Foo[Unpack[Ts]]]: ... +g: Callable[[T], Foo[int]] +reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" +h: Callable[[Unpack[Us]], Foo[int]] +reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index e822cea9304f..b28b2ead45e7 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -479,18 +479,18 @@ vargs: Tuple[int, ...] vargs_str: Tuple[str, ...] call(target=func, args=(0, 'foo')) -call(target=func, args=('bar', 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[object, str], None]" -call(target=func, args=(True, 'foo', 0)) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" -call(target=func, args=(0, 0, 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" -call(target=func, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" +call(target=func, args=('bar', 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[str, str], None]" +call(target=func, args=(True, 'foo', 0)) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[bool, str, int], None]" +call(target=func, args=(0, 0, 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int, int, str], None]" +call(target=func, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(int)], None]" # NOTE: This behavior may be a bit contentious, it is maybe inconsistent with our handling of # PEP646 but consistent with our handling of callable constraints. call(target=func2, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, int], None]"; expected "Callable[[VarArg(int)], None]" call(target=func3, args=vargs) call(target=func3, args=(0,1)) -call(target=func3, args=(0,'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[VarArg(object)], None]" -call(target=func3, args=vargs_str) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[VarArg(object)], None]" +call(target=func3, args=(0,'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[int, str], None]" +call(target=func3, args=vargs_str) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[VarArg(str)], None]" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646CallableWithPrefixSuffix] @@ -561,11 +561,11 @@ class A: vargs: Tuple[int, ...] vargs_str: Tuple[str, ...] -call(A().func) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" +call(A().func) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[], None]" call(A().func, 0, 'foo') -call(A().func, 0, 'foo', 0) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" -call(A().func, 0) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(object)], None]" -call(A().func, 0, 1) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int, object], None]" +call(A().func, 0, 'foo', 0) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int, str, int], None]" +call(A().func, 0) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int], None]" +call(A().func, 0, 1) # E: Argument 1 to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int, int], None]" call(A().func2, 0, 0) call(A().func3, 0, 1, 2) call(A().func3) @@ -689,7 +689,7 @@ reveal_type(x) # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins. B = Callable[[T, Unpack[Ts]], int] y: B -reveal_type(y) # N: Revealed type is "def (Any, *Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +reveal_type(y) # N: Revealed type is "def (Any, *Any) -> builtins.int" C = G[T, Unpack[Ts], T] z: C @@ -711,7 +711,7 @@ reveal_type(x) # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins. B = Callable[[T, S, Unpack[Ts]], int] y: B[int] # E: Bad number of arguments for type alias, expected: at least 2, given: 1 -reveal_type(y) # N: Revealed type is "def (Any, Any, *Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +reveal_type(y) # N: Revealed type is "def (Any, Any, *Any) -> builtins.int" C = G[T, Unpack[Ts], S] z: C[int] # E: Bad number of arguments for type alias, expected: at least 2, given: 1 From 1db3eb3c991b82c2b0f9b7bd0a2547e140dd402a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 19 Aug 2023 14:49:39 +0100 Subject: [PATCH 0201/1617] Allow Ellipsis in Concatenate; cleanup ParamSpec literals (#15905) Fixes https://github.com/python/mypy/issues/14761 Fixes https://github.com/python/mypy/issues/15318 Fixes https://github.com/python/mypy/issues/14656 Fixes https://github.com/python/mypy/issues/13518 I noticed there is a bunch of inconsistencies in `semanal`/`typeanal` for ParamSpecs, so I decided do a small cleanup. Using this opportunity I also allow `Concatenate[int, ...]` (with literal Ellipsis), and reduce verbosity of some errors. cc @A5rocks --- mypy/semanal.py | 14 ++-- mypy/typeanal.py | 54 ++++++++++---- test-data/unit/check-literal.test | 3 +- .../unit/check-parameter-specification.test | 71 ++++++++++++++++++- test-data/unit/check-typevar-defaults.test | 6 +- test-data/unit/semanal-errors.test | 8 +-- 6 files changed, 123 insertions(+), 33 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 9d968d1da781..ef66c9276664 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5285,20 +5285,18 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: else: items = [index] - # whether param spec literals be allowed here - # TODO: should this be computed once and passed in? - # or is there a better way to do this? + # TODO: this needs a clean-up. + # Probably always allow Parameters literals, and validate in semanal_typeargs.py base = expr.base if isinstance(base, RefExpr) and isinstance(base.node, TypeAlias): alias = base.node - target = get_proper_type(alias.target) - if isinstance(target, Instance): - has_param_spec = target.type.has_param_spec_type - num_args = len(target.type.type_vars) + if any(isinstance(t, ParamSpecType) for t in alias.alias_tvars): + has_param_spec = True + num_args = len(alias.alias_tvars) else: has_param_spec = False num_args = -1 - elif isinstance(base, NameExpr) and isinstance(base.node, TypeInfo): + elif isinstance(base, RefExpr) and isinstance(base.node, TypeInfo): has_param_spec = base.node.has_param_spec_type num_args = len(base.node.type_vars) else: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 8ac73cdf8aac..b15b5c7654ba 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -226,6 +226,8 @@ def __init__( self.allow_required = allow_required # Are we in a context where ParamSpec literals are allowed? self.allow_param_spec_literals = allow_param_spec_literals + # Are we in context where literal "..." specifically is allowed? + self.allow_ellipsis = False # Should we report an error whenever we encounter a RawExpressionType outside # of a Literal context: e.g. whenever we encounter an invalid type? Normally, # we want to report an error, but the caller may want to do more specialized @@ -461,9 +463,9 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type: self.api.fail("Concatenate needs type arguments", t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) - # last argument has to be ParamSpec - ps = self.anal_type(t.args[-1], allow_param_spec=True) - if not isinstance(ps, ParamSpecType): + # Last argument has to be ParamSpec or Ellipsis. + ps = self.anal_type(t.args[-1], allow_param_spec=True, allow_ellipsis=True) + if not isinstance(ps, (ParamSpecType, Parameters)): if isinstance(ps, UnboundType) and self.allow_unbound_tvars: sym = self.lookup_qualified(ps.name, t) if sym is not None and isinstance(sym.node, ParamSpecExpr): @@ -477,11 +479,11 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type: # TODO: this may not work well with aliases, if those worked. # Those should be special-cased. - elif ps.prefix.arg_types: + elif isinstance(ps, ParamSpecType) and ps.prefix.arg_types: self.api.fail("Nested Concatenates are invalid", t, code=codes.VALID_TYPE) args = self.anal_array(t.args[:-1]) - pre = ps.prefix + pre = ps.prefix if isinstance(ps, ParamSpecType) else ps # mypy can't infer this :( names: list[str | None] = [None] * len(args) @@ -489,7 +491,7 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type: pre = Parameters( args + pre.arg_types, [ARG_POS] * len(args) + pre.arg_kinds, names + pre.arg_names ) - return ps.copy_modified(prefix=pre) + return ps.copy_modified(prefix=pre) if isinstance(ps, ParamSpecType) else pre def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Type | None: """Bind special type that is recognized through magic name such as 'typing.Any'. @@ -880,7 +882,7 @@ def visit_deleted_type(self, t: DeletedType) -> Type: return t def visit_type_list(self, t: TypeList) -> Type: - # paramspec literal (Z[[int, str, Whatever]]) + # Parameters literal (Z[[int, str, Whatever]]) if self.allow_param_spec_literals: params = self.analyze_callable_args(t) if params: @@ -893,7 +895,8 @@ def visit_type_list(self, t: TypeList) -> Type: self.fail( 'Bracketed expression "[...]" is not valid as a type', t, code=codes.VALID_TYPE ) - self.note('Did you mean "List[...]"?', t) + if len(t.items) == 1: + self.note('Did you mean "List[...]"?', t) return AnyType(TypeOfAny.from_error) def visit_callable_argument(self, t: CallableArgument) -> Type: @@ -1106,7 +1109,7 @@ def visit_partial_type(self, t: PartialType) -> Type: assert False, "Internal error: Unexpected partial type" def visit_ellipsis_type(self, t: EllipsisType) -> Type: - if self.allow_param_spec_literals: + if self.allow_ellipsis or self.allow_param_spec_literals: any_type = AnyType(TypeOfAny.explicit) return Parameters( [any_type, any_type], [ARG_STAR, ARG_STAR2], [None, None], is_ellipsis_args=True @@ -1174,7 +1177,7 @@ def analyze_callable_args_for_paramspec( def analyze_callable_args_for_concatenate( self, callable_args: Type, ret_type: Type, fallback: Instance - ) -> CallableType | None: + ) -> CallableType | AnyType | None: """Construct a 'Callable[C, RET]', where C is Concatenate[..., P], returning None if we cannot. """ @@ -1189,7 +1192,7 @@ def analyze_callable_args_for_concatenate( return None tvar_def = self.anal_type(callable_args, allow_param_spec=True) - if not isinstance(tvar_def, ParamSpecType): + if not isinstance(tvar_def, (ParamSpecType, Parameters)): if self.allow_unbound_tvars and isinstance(tvar_def, UnboundType): sym = self.lookup_qualified(tvar_def.name, callable_args) if sym is not None and isinstance(sym.node, ParamSpecExpr): @@ -1198,7 +1201,18 @@ def analyze_callable_args_for_concatenate( return callable_with_ellipsis( AnyType(TypeOfAny.explicit), ret_type=ret_type, fallback=fallback ) - return None + # Error was already given, so prevent further errors. + return AnyType(TypeOfAny.from_error) + if isinstance(tvar_def, Parameters): + # This comes from Concatenate[int, ...] + return CallableType( + arg_types=tvar_def.arg_types, + arg_names=tvar_def.arg_names, + arg_kinds=tvar_def.arg_kinds, + ret_type=ret_type, + fallback=fallback, + from_concatenate=True, + ) # ick, CallableType should take ParamSpecType prefix = tvar_def.prefix @@ -1257,7 +1271,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type: ) or self.analyze_callable_args_for_concatenate( callable_args, ret_type, fallback ) - if maybe_ret: + if isinstance(maybe_ret, CallableType): maybe_ret = maybe_ret.copy_modified( ret_type=ret_type.accept(self), variables=variables ) @@ -1274,6 +1288,8 @@ def analyze_callable_type(self, t: UnboundType) -> Type: t, ) return AnyType(TypeOfAny.from_error) + elif isinstance(maybe_ret, AnyType): + return maybe_ret ret = maybe_ret else: if self.options.disallow_any_generics: @@ -1527,17 +1543,27 @@ def anal_array( self.allow_param_spec_literals = old_allow_param_spec_literals return self.check_unpacks_in_list(res) - def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = False) -> Type: + def anal_type( + self, + t: Type, + nested: bool = True, + *, + allow_param_spec: bool = False, + allow_ellipsis: bool = False, + ) -> Type: if nested: self.nesting_level += 1 old_allow_required = self.allow_required self.allow_required = False + old_allow_ellipsis = self.allow_ellipsis + self.allow_ellipsis = allow_ellipsis try: analyzed = t.accept(self) finally: if nested: self.nesting_level -= 1 self.allow_required = old_allow_required + self.allow_ellipsis = old_allow_ellipsis if ( not allow_param_spec and isinstance(analyzed, ParamSpecType) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 4498b2ddc9cf..ecd4fc0a1f00 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -611,8 +611,7 @@ from typing_extensions import Literal a: (1, 2, 3) # E: Syntax error in type annotation \ # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) b: Literal[[1, 2, 3]] # E: Parameter 1 of Literal[...] is invalid -c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid as a type \ - # N: Did you mean "List[...]"? +c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid as a type [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 3a8ecdf81c7d..dee8a971f925 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -38,6 +38,74 @@ def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpe # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' [builtins fixtures/paramspec.pyi] +[case testParamSpecImports] +import lib +from lib import Base + +class C(Base[[int]]): + def test(self, x: int): ... + +class D(lib.Base[[int]]): + def test(self, x: int): ... + +class E(lib.Base[...]): ... +reveal_type(E().test) # N: Revealed type is "def (*Any, **Any)" + +[file lib.py] +from typing import Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") +class Base(Generic[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + ... +[builtins fixtures/paramspec.pyi] + +[case testParamSpecEllipsisInAliases] +from typing import Any, Callable, Generic, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec('P') +R = TypeVar('R') +Alias = Callable[P, R] + +class B(Generic[P]): ... +Other = B[P] + +T = TypeVar('T', bound=Alias[..., Any]) +Alias[..., Any] # E: Type application is only supported for generic classes +B[...] +Other[...] +[builtins fixtures/paramspec.pyi] + +[case testParamSpecEllipsisInConcatenate] +from typing import Any, Callable, Generic, TypeVar +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec('P') +R = TypeVar('R') +Alias = Callable[P, R] + +IntFun = Callable[Concatenate[int, ...], None] +f: IntFun +reveal_type(f) # N: Revealed type is "def (builtins.int, *Any, **Any)" + +g: Callable[Concatenate[int, ...], None] +reveal_type(g) # N: Revealed type is "def (builtins.int, *Any, **Any)" + +class B(Generic[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + ... + +x: B[Concatenate[int, ...]] +reveal_type(x.test) # N: Revealed type is "def (builtins.int, *Any, **Any)" + +Bad = Callable[Concatenate[int, [int, str]], None] # E: The last parameter to Concatenate needs to be a ParamSpec \ + # E: Bracketed expression "[...]" is not valid as a type +def bad(fn: Callable[Concatenate[P, int], None]): # E: The last parameter to Concatenate needs to be a ParamSpec + ... +[builtins fixtures/paramspec.pyi] + [case testParamSpecContextManagerLike] from typing import Callable, List, Iterator, TypeVar from typing_extensions import ParamSpec @@ -1431,8 +1499,7 @@ from typing import ParamSpec, Generic, List, TypeVar, Callable P = ParamSpec("P") T = TypeVar("T") A = List[T] -def f(x: A[[int, str]]) -> None: ... # E: Bracketed expression "[...]" is not valid as a type \ - # N: Did you mean "List[...]"? +def f(x: A[[int, str]]) -> None: ... # E: Bracketed expression "[...]" is not valid as a type def g(x: A[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 36ec125eb1a4..9015d353fa08 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -59,9 +59,9 @@ from typing import TypeVar, ParamSpec, Tuple from typing_extensions import TypeVarTuple, Unpack T1 = TypeVar("T1", default=2) # E: TypeVar "default" must be a type -T2 = TypeVar("T2", default=[int, str]) # E: Bracketed expression "[...]" is not valid as a type \ - # N: Did you mean "List[...]"? \ - # E: TypeVar "default" must be a type +T2 = TypeVar("T2", default=[int]) # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type P1 = ParamSpec("P1", default=int) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec P2 = ParamSpec("P2", default=2) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index a098dd8791d4..09d4da54bff3 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -810,8 +810,8 @@ class C(Generic[t]): pass cast(str + str, None) # E: Cast target is not a type cast(C[str][str], None) # E: Cast target is not a type cast(C[str + str], None) # E: Cast target is not a type -cast([int, str], None) # E: Bracketed expression "[...]" is not valid as a type \ - # N: Did you mean "List[...]"? +cast([int], None) # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? [out] [case testInvalidCastTargetType] @@ -859,8 +859,8 @@ Any(arg=str) # E: Any(...) is no longer supported. Use cast(Any, ...) instead [case testTypeListAsType] -def f(x:[int, str]) -> None: # E: Bracketed expression "[...]" is not valid as a type \ - # N: Did you mean "List[...]"? +def f(x: [int]) -> None: # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? pass [out] From d7d502e5e63092322d57c607e2affac6cb5234b8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 19 Aug 2023 14:49:55 +0100 Subject: [PATCH 0202/1617] Support self-types containing ParamSpec (#15903) Fixes https://github.com/python/mypy/issues/14968 Fixes https://github.com/python/mypy/issues/13911 The fix is simple, as I predicted on Discord, we simply should use `get_all_type_vars()` instead of `get_type_vars()` (that specifically returns only `TypeVarType`). I also use this opportunity to tidy-up code in `bind_self()`, it should be now more readable, and much faster (especially when compiled with mypyc). cc @A5rocks --------- Co-authored-by: Alex Waygood --- mypy/typeops.py | 34 ++++++++++-------------- test-data/unit/check-selftype.test | 42 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 22dbd9e9f42e..e01aad950573 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -303,7 +303,7 @@ class B(A): pass return cast(F, func) self_param_type = get_proper_type(func.arg_types[0]) - variables: Sequence[TypeVarLikeType] = [] + variables: Sequence[TypeVarLikeType] if func.variables and supported_self_type(self_param_type): from mypy.infer import infer_type_arguments @@ -312,46 +312,40 @@ class B(A): pass original_type = erase_to_bound(self_param_type) original_type = get_proper_type(original_type) - all_ids = func.type_var_ids() + # Find which of method type variables appear in the type of "self". + self_ids = {tv.id for tv in get_all_type_vars(self_param_type)} + self_vars = [tv for tv in func.variables if tv.id in self_ids] + + # Solve for these type arguments using the actual class or instance type. typeargs = infer_type_arguments( - func.variables, self_param_type, original_type, is_supertype=True + self_vars, self_param_type, original_type, is_supertype=True ) if ( is_classmethod - # TODO: why do we need the extra guards here? and any(isinstance(get_proper_type(t), UninhabitedType) for t in typeargs) and isinstance(original_type, (Instance, TypeVarType, TupleType)) ): - # In case we call a classmethod through an instance x, fallback to type(x) + # In case we call a classmethod through an instance x, fallback to type(x). typeargs = infer_type_arguments( - func.variables, self_param_type, TypeType(original_type), is_supertype=True + self_vars, self_param_type, TypeType(original_type), is_supertype=True ) - ids = [tid for tid in all_ids if any(tid == t.id for t in get_type_vars(self_param_type))] - - # Technically, some constrains might be unsolvable, make them . + # Update the method signature with the solutions found. + # Technically, some constraints might be unsolvable, make them . to_apply = [t if t is not None else UninhabitedType() for t in typeargs] - - def expand(target: Type) -> Type: - return expand_type(target, {id: to_apply[all_ids.index(id)] for id in ids}) - - arg_types = [expand(x) for x in func.arg_types[1:]] - ret_type = expand(func.ret_type) - variables = [v for v in func.variables if v.id not in ids] + func = expand_type(func, {tv.id: arg for tv, arg in zip(self_vars, to_apply)}) + variables = [v for v in func.variables if v not in self_vars] else: - arg_types = func.arg_types[1:] - ret_type = func.ret_type variables = func.variables original_type = get_proper_type(original_type) if isinstance(original_type, CallableType) and original_type.is_type_obj(): original_type = TypeType.make_normalized(original_type.ret_type) res = func.copy_modified( - arg_types=arg_types, + arg_types=func.arg_types[1:], arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, - ret_type=ret_type, bound_args=[original_type], ) return cast(F, res) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index d366e7c33799..77d2d519214a 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1973,3 +1973,45 @@ class B(A): reveal_type(self.x.extra) # N: Revealed type is "builtins.int" reveal_type(self.xs[0].extra) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] + +[case testSelfTypesWithParamSpecExtract] +from typing import Any, Callable, Generic, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +F = TypeVar("F", bound=Callable[..., Any]) +class Example(Generic[F]): + def __init__(self, fn: F) -> None: + ... + def __call__(self: Example[Callable[P, Any]], *args: P.args, **kwargs: P.kwargs) -> None: + ... + +def test_fn(a: int, b: str) -> None: + ... + +example = Example(test_fn) +example() # E: Missing positional arguments "a", "b" in call to "__call__" of "Example" +example(1, "b") # OK +[builtins fixtures/list.pyi] + +[case testSelfTypesWithParamSpecInfer] +from typing import TypeVar, Protocol, Type, Callable +from typing_extensions import ParamSpec + +R = TypeVar("R", covariant=True) +P = ParamSpec("P") +class AsyncP(Protocol[P]): + def meth(self, *args: P.args, **kwargs: P.kwargs) -> None: + ... + +class Async: + @classmethod + def async_func(cls: Type[AsyncP[P]]) -> Callable[P, int]: + ... + +class Add(Async): + def meth(self, x: int, y: int) -> None: ... + +reveal_type(Add.async_func()) # N: Revealed type is "def (x: builtins.int, y: builtins.int) -> builtins.int" +reveal_type(Add().async_func()) # N: Revealed type is "def (x: builtins.int, y: builtins.int) -> builtins.int" +[builtins fixtures/classmethod.pyi] From 5af76714fa2c526007e045f9c834781f60660e6e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 19 Aug 2023 11:41:26 -0700 Subject: [PATCH 0203/1617] Improve match narrowing and reachability analysis (#15882) Fixes #12534, fixes #15878 --- mypy/checker.py | 17 ++++++++++- test-data/unit/check-python310.test | 45 ++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7625bf28a88c..87dff91758f5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4973,7 +4973,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: self.push_type_map(pattern_map) self.push_type_map(pattern_type.captures) if g is not None: - with self.binder.frame_context(can_skip=True, fall_through=3): + with self.binder.frame_context(can_skip=False, fall_through=3): gt = get_proper_type(self.expr_checker.accept(g)) if isinstance(gt, DeletedType): @@ -4982,6 +4982,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None: guard_map, guard_else_map = self.find_isinstance_check(g) else_map = or_conditional_maps(else_map, guard_else_map) + # If the guard narrowed the subject, copy the narrowed types over + if isinstance(p, AsPattern): + case_target = p.pattern or p.name + if isinstance(case_target, NameExpr): + for type_map in (guard_map, else_map): + if not type_map: + continue + for expr in list(type_map): + if not ( + isinstance(expr, NameExpr) + and expr.fullname == case_target.fullname + ): + continue + type_map[s.subject] = type_map[expr] + self.push_type_map(guard_map) self.accept(b) else: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 75293ce9d193..0fe6a3d5a5cc 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1372,7 +1372,7 @@ match m: reveal_type(m) # N: Revealed type is "__main__.Medal" [case testMatchNarrowUsingPatternGuardSpecialCase] -def f(x: int | str) -> int: # E: Missing return statement +def f(x: int | str) -> int: match x: case x if isinstance(x, str): return 0 @@ -1973,3 +1973,46 @@ def f2(x: T) -> None: case DataFrame(): # type: ignore[misc] pass [builtins fixtures/primitives.pyi] + +[case testMatchGuardReachability] +# flags: --warn-unreachable +def f1(e: int) -> int: + match e: + case x if True: + return x + case _: + return 0 # E: Statement is unreachable + e = 0 # E: Statement is unreachable + + +def f2(e: int) -> int: + match e: + case x if bool(): + return x + case _: + return 0 + e = 0 # E: Statement is unreachable + +def f3(e: int | str | bytes) -> int: + match e: + case x if isinstance(x, int): + return x + case [x]: + return 0 # E: Statement is unreachable + case str(x): + return 0 + reveal_type(e) # N: Revealed type is "builtins.bytes" + return 0 + +def f4(e: int | str | bytes) -> int: + match e: + case int(x): + pass + case [x]: + return 0 # E: Statement is unreachable + case x if isinstance(x, str): + return 0 + reveal_type(e) # N: Revealed type is "Union[builtins.int, builtins.bytes]" + return 0 + +[builtins fixtures/primitives.pyi] From 5d909f1cdd7dde5e58259dc153959a38d084b988 Mon Sep 17 00:00:00 2001 From: Max Murin Date: Sun, 20 Aug 2023 21:15:18 -0700 Subject: [PATCH 0204/1617] Sync typeshed for 1.6 (#15918) Use the sync-typeshed script to sync the latest typeshed before the 1.6 release. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 13 +++++++++++++ mypy/typeshed/stdlib/argparse.pyi | 17 ++++++++++++++++- mypy/typeshed/stdlib/os/__init__.pyi | 2 +- mypy/typeshed/stdlib/ssl.pyi | 4 ++-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 5d03142c6d71..7ae67292e8cd 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -36,6 +36,19 @@ Incomplete: TypeAlias = Any # To describe a function parameter that is unused and will work with anything. Unused: TypeAlias = object +# Used to mark arguments that default to a sentinel value. This prevents +# stubtest from complaining about the default value not matching. +# +# def foo(x: int | None = sentinel) -> None: ... +# +# In cases where the sentinel object is exported and can be used by user code, +# a construct like this is better: +# +# _SentinelType = NewType("_SentinelType", object) +# sentinel: _SentinelType +# def foo(x: int | None | _SentinelType = ...) -> None: ... +sentinel = Any # noqa: Y026 + # stable class IdentityFunction(Protocol): def __call__(self, __x: _T) -> _T: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index b59dd56ab921..0004250b17a9 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from typing import IO, Any, Generic, NewType, NoReturn, Protocol, TypeVar, overload @@ -334,7 +335,21 @@ class Action(_AttributeHolder): if sys.version_info >= (3, 9): def format_usage(self) -> str: ... -if sys.version_info >= (3, 9): +if sys.version_info >= (3, 12): + class BooleanOptionalAction(Action): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: _T | str | None = None, + type: Callable[[str], _T] | FileType | None = sentinel, # noqa: Y011 + choices: Iterable[_T] | None = sentinel, # noqa: Y011 + required: bool = False, + help: str | None = None, + metavar: str | tuple[str, ...] | None = sentinel, # noqa: Y011 + ) -> None: ... + +elif sys.version_info >= (3, 9): class BooleanOptionalAction(Action): def __init__( self, diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index efe80d82ffba..994595aae781 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -912,7 +912,7 @@ else: @property def si_code(self) -> int: ... - def waitid(__idtype: int, __ident: int, __options: int) -> waitid_result: ... + def waitid(__idtype: int, __ident: int, __options: int) -> waitid_result | None: ... def wait3(options: int) -> tuple[int, int, Any]: ... def wait4(pid: int, options: int) -> tuple[int, int, Any]: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index dd7285196ed9..1c49b130e48f 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -436,7 +436,7 @@ class SSLContext: server_side: bool = False, do_handshake_on_connect: bool = True, suppress_ragged_eofs: bool = True, - server_hostname: str | None = None, + server_hostname: str | bytes | None = None, session: SSLSession | None = None, ) -> SSLSocket: ... def wrap_bio( @@ -444,7 +444,7 @@ class SSLContext: incoming: MemoryBIO, outgoing: MemoryBIO, server_side: bool = False, - server_hostname: str | None = None, + server_hostname: str | bytes | None = None, session: SSLSession | None = None, ) -> SSLObject: ... def session_stats(self) -> dict[str, int]: ... From 2c1fd97986064161c542956bb3d9d5043dc0a480 Mon Sep 17 00:00:00 2001 From: KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> Date: Mon, 21 Aug 2023 20:35:04 +1000 Subject: [PATCH 0205/1617] =?UTF-8?q?(=F0=9F=8E=81)=20`StubGenerator.add?= =?UTF-8?q?=5Ftyping=5Fimport`=20returns=20the=20name=20(#15912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I noticed that almost all calls to `typing_name` also call `add_typing_import`. Co-authored-by: KotlinIsland --- mypy/stubgen.py | 55 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index b6fc3e8b7377..aca836c52ce8 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -786,25 +786,20 @@ def visit_func_def(self, o: FuncDef) -> None: elif o.name in KNOWN_MAGIC_METHODS_RETURN_TYPES: retname = KNOWN_MAGIC_METHODS_RETURN_TYPES[o.name] elif has_yield_expression(o) or has_yield_from_expression(o): - self.add_typing_import("Generator") + generator_name = self.add_typing_import("Generator") yield_name = "None" send_name = "None" return_name = "None" if has_yield_from_expression(o): - self.add_typing_import("Incomplete") - yield_name = send_name = self.typing_name("Incomplete") + yield_name = send_name = self.add_typing_import("Incomplete") else: for expr, in_assignment in all_yield_expressions(o): if expr.expr is not None and not self.is_none_expr(expr.expr): - self.add_typing_import("Incomplete") - yield_name = self.typing_name("Incomplete") + yield_name = self.add_typing_import("Incomplete") if in_assignment: - self.add_typing_import("Incomplete") - send_name = self.typing_name("Incomplete") + send_name = self.add_typing_import("Incomplete") if has_return_statement(o): - self.add_typing_import("Incomplete") - return_name = self.typing_name("Incomplete") - generator_name = self.typing_name("Generator") + return_name = self.add_typing_import("Incomplete") retname = f"{generator_name}[{yield_name}, {send_name}, {return_name}]" elif not has_return_statement(o) and o.abstract_status == NOT_ABSTRACT: retname = "None" @@ -965,21 +960,19 @@ def get_base_types(self, cdef: ClassDef) -> list[str]: nt_fields = self._get_namedtuple_fields(base) assert isinstance(base.args[0], StrExpr) typename = base.args[0].value - if nt_fields is not None: - fields_str = ", ".join(f"({f!r}, {t})" for f, t in nt_fields) - namedtuple_name = self.typing_name("NamedTuple") - base_types.append(f"{namedtuple_name}({typename!r}, [{fields_str}])") - self.add_typing_import("NamedTuple") - else: + if nt_fields is None: # Invalid namedtuple() call, cannot determine fields - base_types.append(self.typing_name("Incomplete")) + base_types.append(self.add_typing_import("Incomplete")) + continue + fields_str = ", ".join(f"({f!r}, {t})" for f, t in nt_fields) + namedtuple_name = self.add_typing_import("NamedTuple") + base_types.append(f"{namedtuple_name}({typename!r}, [{fields_str}])") elif self.is_typed_namedtuple(base): base_types.append(base.accept(p)) else: # At this point, we don't know what the base class is, so we # just use Incomplete as the base class. - base_types.append(self.typing_name("Incomplete")) - self.add_typing_import("Incomplete") + base_types.append(self.add_typing_import("Incomplete")) for name, value in cdef.keywords.items(): if name == "metaclass": continue # handled separately @@ -1059,9 +1052,9 @@ def _get_namedtuple_fields(self, call: CallExpr) -> list[tuple[str, str]] | None field_names.append(field.value) else: return None # Invalid namedtuple fields type - if field_names: - self.add_typing_import("Incomplete") - incomplete = self.typing_name("Incomplete") + if not field_names: + return [] + incomplete = self.add_typing_import("Incomplete") return [(field_name, incomplete) for field_name in field_names] elif self.is_typed_namedtuple(call): fields_arg = call.args[1] @@ -1092,8 +1085,7 @@ def process_namedtuple(self, lvalue: NameExpr, rvalue: CallExpr) -> None: if fields is None: self.annotate_as_incomplete(lvalue) return - self.add_typing_import("NamedTuple") - bases = self.typing_name("NamedTuple") + bases = self.add_typing_import("NamedTuple") # TODO: Add support for generic NamedTuples. Requires `Generic` as base class. class_def = f"{self._indent}class {lvalue.name}({bases}):" if len(fields) == 0: @@ -1143,14 +1135,13 @@ def process_typeddict(self, lvalue: NameExpr, rvalue: CallExpr) -> None: total = arg else: items.append((arg_name, arg)) - self.add_typing_import("TypedDict") + bases = self.add_typing_import("TypedDict") p = AliasPrinter(self) if any(not key.isidentifier() or keyword.iskeyword(key) for key, _ in items): # Keep the call syntax if there are non-identifier or reserved keyword keys. self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") self._state = VAR else: - bases = self.typing_name("TypedDict") # TODO: Add support for generic TypedDicts. Requires `Generic` as base class. if total is not None: bases += f", total={total.accept(p)}" @@ -1167,8 +1158,7 @@ def process_typeddict(self, lvalue: NameExpr, rvalue: CallExpr) -> None: self._state = CLASS def annotate_as_incomplete(self, lvalue: NameExpr) -> None: - self.add_typing_import("Incomplete") - self.add(f"{self._indent}{lvalue.name}: {self.typing_name('Incomplete')}\n") + self.add(f"{self._indent}{lvalue.name}: {self.add_typing_import('Incomplete')}\n") self._state = VAR def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: @@ -1384,13 +1374,14 @@ def typing_name(self, name: str) -> str: else: return name - def add_typing_import(self, name: str) -> None: + def add_typing_import(self, name: str) -> str: """Add a name to be imported for typing, unless it's imported already. The import will be internal to the stub. """ name = self.typing_name(name) self.import_tracker.require_name(name) + return name def add_import_line(self, line: str) -> None: """Add a line of text to the import section, unless it's already there.""" @@ -1448,11 +1439,9 @@ def get_str_type_of_node( if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"): return "bool" if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None": - self.add_typing_import("Incomplete") - return f"{self.typing_name('Incomplete')} | None" + return f"{self.add_typing_import('Incomplete')} | None" if can_be_any: - self.add_typing_import("Incomplete") - return self.typing_name("Incomplete") + return self.add_typing_import("Incomplete") else: return "" From e804e8d740631ecbdb3a70330a3ea8497e114e3a Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 21 Aug 2023 15:32:35 +0300 Subject: [PATCH 0206/1617] Fix `assert_type` failures when some nodes are deferred (#15920) Now it is quite the same as `reveal_type`. Which is defined here: https://github.com/python/mypy/blob/2c1fd97986064161c542956bb3d9d5043dc0a480/mypy/checkexpr.py#L4297 Closes #15851 --- mypy/checkexpr.py | 3 +++ test-data/unit/check-expressions.test | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 420cfd990820..4d04390da84a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4271,6 +4271,9 @@ def visit_assert_type_expr(self, expr: AssertTypeExpr) -> Type: allow_none_return=True, always_allow_any=True, ) + if self.chk.current_node_deferred: + return source_type + target_type = expr.type proper_source_type = get_proper_type(source_type) if ( diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 40ee28830b21..c213255997f8 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1045,6 +1045,23 @@ def reduce_it(s: Scalar) -> Scalar: assert_type(reduce_it(True), Scalar) [builtins fixtures/tuple.pyi] +[case testAssertTypeWithDeferredNodes] +from typing import Callable, TypeVar, assert_type + +T = TypeVar("T") + +def dec(f: Callable[[], T]) -> Callable[[], T]: + return f + +def func() -> None: + some = _inner_func() + assert_type(some, int) + +@dec +def _inner_func() -> int: + return 1 +[builtins fixtures/tuple.pyi] + -- None return type -- ---------------- From 7141d6bcff9e26e774e88712015ca6bbe8307c9e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 21 Aug 2023 23:24:46 +0100 Subject: [PATCH 0207/1617] More principled approach for callable vs callable inference (#15910) Fixes https://github.com/python/mypy/issues/702 (one of the oldest open issues) The approach is quite simple, I essentially replicate the logic from subtyping check, while replacing each `is_subtype()` call with `infer_constraints()` call. Note that we don't have various options available in `constraints.py` so I use all checks, even those that may be skipped with some strictness flags (so we can infer as many constraints as possible). Depending on the output of `mypy_primer` we can try to tune this. Note that while I was looking at subtyping code, I noticed couple inconsistencies for ParamSpecs, I added TODOs for them (and updated some existing TODOs). I also deleted some code that should be dead code after my previous cleanup. Among inconsistencies most notably, subtyping between `Parameters` uses wrong (opposite) direction. Normally, `Parameters` entity behaves covariantly (w.r.t. types of individual arguments) as a single big argument, like a tuple plus a map. But then this entity appears in a contravariant position in `Callable`. This is how we handle it in `constraints.py`, `join.py`, `meet.py` etc. I tried to fix the left/right order in `visit_parameters()`, but then one test failed (and btw same test would also fail if I would try to fix variance in `visit_instance()`). I decided to leave this for separate PR(s). --- mypy/constraints.py | 132 +++++++++++++++++++------ mypy/subtypes.py | 32 +++---- mypy/types.py | 8 +- test-data/unit/check-inference.test | 133 ++++++++++++++++++++++++++ test-data/unit/check-overloading.test | 10 ++ 5 files changed, 260 insertions(+), 55 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 26504ed06b3e..47f312117264 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -595,15 +595,11 @@ def visit_parameters(self, template: Parameters) -> list[Constraint]: return self.infer_against_any(template.arg_types, self.actual) if type_state.infer_polymorphic and isinstance(self.actual, Parameters): # For polymorphic inference we need to be able to infer secondary constraints - # in situations like [x: T] <: P <: [x: int]. - res = [] - if len(template.arg_types) == len(self.actual.arg_types): - for tt, at in zip(template.arg_types, self.actual.arg_types): - # This avoids bogus constraints like T <: P.args - if isinstance(at, ParamSpecType): - continue - res.extend(infer_constraints(tt, at, self.direction)) - return res + # in situations like [x: T] <: P <: [x: int]. Note we invert direction, since + # this function expects direction between callables. + return infer_callable_arguments_constraints( + template, self.actual, neg_op(self.direction) + ) raise RuntimeError("Parameters cannot be constrained to") # Non-leaf types @@ -722,7 +718,8 @@ def visit_instance(self, template: Instance) -> list[Constraint]: prefix = mapped_arg.prefix if isinstance(instance_arg, Parameters): # No such thing as variance for ParamSpecs, consider them invariant - # TODO: constraints between prefixes + # TODO: constraints between prefixes using + # infer_callable_arguments_constraints() suffix: Type = instance_arg.copy_modified( instance_arg.arg_types[len(prefix.arg_types) :], instance_arg.arg_kinds[len(prefix.arg_kinds) :], @@ -793,7 +790,8 @@ def visit_instance(self, template: Instance) -> list[Constraint]: prefix = template_arg.prefix if isinstance(mapped_arg, Parameters): # No such thing as variance for ParamSpecs, consider them invariant - # TODO: constraints between prefixes + # TODO: constraints between prefixes using + # infer_callable_arguments_constraints() suffix = mapped_arg.copy_modified( mapped_arg.arg_types[len(prefix.arg_types) :], mapped_arg.arg_kinds[len(prefix.arg_kinds) :], @@ -962,24 +960,12 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: unpack_constraints = build_constraints_for_simple_unpack( template_types, actual_types, neg_op(self.direction) ) - template_args = [] - cactual_args = [] res.extend(unpack_constraints) else: - template_args = template.arg_types - cactual_args = cactual.arg_types - # TODO: use some more principled "formal to actual" logic - # instead of this lock-step loop over argument types. This identical - # logic should be used in 5 places: in Parameters vs Parameters - # inference, in Instance vs Instance inference for prefixes (two - # branches), and in Callable vs Callable inference (two branches). - for t, a in zip(template_args, cactual_args): - # This avoids bogus constraints like T <: P.args - if isinstance(a, (ParamSpecType, UnpackType)): - # TODO: can we infer something useful for *T vs P? - continue # Negate direction due to function argument type contravariance. - res.extend(infer_constraints(t, a, neg_op(self.direction))) + res.extend( + infer_callable_arguments_constraints(template, cactual, self.direction) + ) else: prefix = param_spec.prefix prefix_len = len(prefix.arg_types) @@ -1028,11 +1014,9 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: arg_kinds=cactual.arg_kinds[:prefix_len], arg_names=cactual.arg_names[:prefix_len], ) - - for t, a in zip(prefix.arg_types, cactual_prefix.arg_types): - if isinstance(a, ParamSpecType): - continue - res.extend(infer_constraints(t, a, neg_op(self.direction))) + res.extend( + infer_callable_arguments_constraints(prefix, cactual_prefix, self.direction) + ) template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type if template.type_guard is not None: @@ -1435,3 +1419,89 @@ def build_constraints_for_unpack( for template_arg, item in zip(template_unpack.items, mapped_middle): res.extend(infer_constraints(template_arg, item, direction)) return res, mapped_prefix + mapped_suffix, template_prefix + template_suffix + + +def infer_directed_arg_constraints(left: Type, right: Type, direction: int) -> list[Constraint]: + """Infer constraints between two arguments using direction between original callables.""" + if isinstance(left, (ParamSpecType, UnpackType)) or isinstance( + right, (ParamSpecType, UnpackType) + ): + # This avoids bogus constraints like T <: P.args + # TODO: can we infer something useful for *T vs P? + return [] + if direction == SUBTYPE_OF: + # We invert direction to account for argument contravariance. + return infer_constraints(left, right, neg_op(direction)) + else: + return infer_constraints(right, left, neg_op(direction)) + + +def infer_callable_arguments_constraints( + template: CallableType | Parameters, actual: CallableType | Parameters, direction: int +) -> list[Constraint]: + """Infer constraints between argument types of two callables. + + This function essentially extracts four steps from are_parameters_compatible() in + subtypes.py that involve subtype checks between argument types. We keep the argument + matching logic, but ignore various strictness flags present there, and checks that + do not involve subtyping. Then in place of every subtype check we put an infer_constraints() + call for the same types. + """ + res = [] + if direction == SUBTYPE_OF: + left, right = template, actual + else: + left, right = actual, template + left_star = left.var_arg() + left_star2 = left.kw_arg() + right_star = right.var_arg() + right_star2 = right.kw_arg() + + # Numbering of steps below matches the one in are_parameters_compatible() for convenience. + # Phase 1a: compare star vs star arguments. + if left_star is not None and right_star is not None: + res.extend(infer_directed_arg_constraints(left_star.typ, right_star.typ, direction)) + if left_star2 is not None and right_star2 is not None: + res.extend(infer_directed_arg_constraints(left_star2.typ, right_star2.typ, direction)) + + # Phase 1b: compare left args with corresponding non-star right arguments. + for right_arg in right.formal_arguments(): + left_arg = mypy.typeops.callable_corresponding_argument(left, right_arg) + if left_arg is None: + continue + res.extend(infer_directed_arg_constraints(left_arg.typ, right_arg.typ, direction)) + + # Phase 1c: compare left args with right *args. + if right_star is not None: + right_by_position = right.try_synthesizing_arg_from_vararg(None) + assert right_by_position is not None + i = right_star.pos + assert i is not None + while i < len(left.arg_kinds) and left.arg_kinds[i].is_positional(): + left_by_position = left.argument_by_position(i) + assert left_by_position is not None + res.extend( + infer_directed_arg_constraints( + left_by_position.typ, right_by_position.typ, direction + ) + ) + i += 1 + + # Phase 1d: compare left args with right **kwargs. + if right_star2 is not None: + right_names = {name for name in right.arg_names if name is not None} + left_only_names = set() + for name, kind in zip(left.arg_names, left.arg_kinds): + if name is None or kind.is_star() or name in right_names: + continue + left_only_names.add(name) + + right_by_name = right.try_synthesizing_arg_from_kwarg(None) + assert right_by_name is not None + for name in left_only_names: + left_by_name = left.argument_by_name(name) + assert left_by_name is not None + res.extend( + infer_directed_arg_constraints(left_by_name.typ, right_by_name.typ, direction) + ) + return res diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 11847858c62c..288de10cc234 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -590,6 +590,7 @@ def check_mixed( ): nominal = False else: + # TODO: everywhere else ParamSpecs are handled as invariant. if not check_type_parameter( lefta, righta, COVARIANT, self.proper_subtype, self.subtype_context ): @@ -666,13 +667,12 @@ def visit_unpack_type(self, left: UnpackType) -> bool: return False def visit_parameters(self, left: Parameters) -> bool: - if isinstance(self.right, (Parameters, CallableType)): - right = self.right - if isinstance(right, CallableType): - right = right.with_unpacked_kwargs() + if isinstance(self.right, Parameters): + # TODO: direction here should be opposite, this function expects + # order of callables, while parameters are contravariant. return are_parameters_compatible( left, - right, + self.right, is_compat=self._is_subtype, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, ) @@ -723,14 +723,6 @@ def visit_callable_type(self, left: CallableType) -> bool: elif isinstance(right, TypeType): # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and self._is_subtype(left.ret_type, right.item) - elif isinstance(right, Parameters): - # this doesn't check return types.... but is needed for is_equivalent - return are_parameters_compatible( - left.with_unpacked_kwargs(), - right, - is_compat=self._is_subtype, - ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, - ) else: return False @@ -1456,7 +1448,6 @@ def g(x: int) -> int: ... right, is_compat=is_compat, ignore_pos_arg_names=ignore_pos_arg_names, - check_args_covariantly=check_args_covariantly, allow_partial_overlap=allow_partial_overlap, strict_concatenate_check=strict_concatenate_check, ) @@ -1480,7 +1471,6 @@ def are_parameters_compatible( *, is_compat: Callable[[Type, Type], bool], ignore_pos_arg_names: bool = False, - check_args_covariantly: bool = False, allow_partial_overlap: bool = False, strict_concatenate_check: bool = False, ) -> bool: @@ -1534,7 +1524,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1b: Check non-star args: for every arg right can accept, left must # also accept. The only exception is if we are allowing partial - # partial overlaps: in that case, we ignore optional args on the right. + # overlaps: in that case, we ignore optional args on the right. for right_arg in right.formal_arguments(): left_arg = mypy.typeops.callable_corresponding_argument(left, right_arg) if left_arg is None: @@ -1548,7 +1538,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1c: Check var args. Right has an infinite series of optional positional # arguments. Get all further positional args of left, and make sure - # they're more general then the corresponding member in right. + # they're more general than the corresponding member in right. if right_star is not None: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) @@ -1575,7 +1565,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1d: Check kw args. Right has an infinite series of optional named # arguments. Get all further named args of left, and make sure - # they're more general then the corresponding member in right. + # they're more general than the corresponding member in right. if right_star2 is not None: right_names = {name for name in right.arg_names if name is not None} left_only_names = set() @@ -1643,6 +1633,10 @@ def are_args_compatible( allow_partial_overlap: bool, is_compat: Callable[[Type, Type], bool], ) -> bool: + if left.required and right.required: + # If both arguments are required allow_partial_overlap has no effect. + allow_partial_overlap = False + def is_different(left_item: object | None, right_item: object | None) -> bool: """Checks if the left and right items are different. @@ -1670,7 +1664,7 @@ def is_different(left_item: object | None, right_item: object | None) -> bool: # If right's argument is optional, left's must also be # (unless we're relaxing the checks to allow potential - # rather then definite compatibility). + # rather than definite compatibility). if not allow_partial_overlap and not right.required and left.required: return False diff --git a/mypy/types.py b/mypy/types.py index d4e2fc7cb63c..301ce6e0cf18 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1545,9 +1545,6 @@ class FormalArgument(NamedTuple): required: bool -# TODO: should this take bound typevars too? what would this take? -# ex: class Z(Generic[P, T]): ...; Z[[V], V] -# What does a typevar even mean in this context? class Parameters(ProperType): """Type that represents the parameters to a function. @@ -1559,6 +1556,8 @@ class Parameters(ProperType): "arg_names", "min_args", "is_ellipsis_args", + # TODO: variables don't really belong here, but they are used to allow hacky support + # for forall . Foo[[x: T], T] by capturing generic callable with ParamSpec, see #15909 "variables", ) @@ -1602,7 +1601,7 @@ def copy_modified( variables=variables if variables is not _dummy else self.variables, ) - # the following are copied from CallableType. Is there a way to decrease code duplication? + # TODO: here is a lot of code duplication with Callable type, fix this. def var_arg(self) -> FormalArgument | None: """The formal argument for *args.""" for position, (type, kind) in enumerate(zip(self.arg_types, self.arg_kinds)): @@ -2046,7 +2045,6 @@ def param_spec(self) -> ParamSpecType | None: return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) def expand_param_spec(self, c: Parameters) -> CallableType: - # TODO: try deleting variables from Parameters after new type inference is default. variables = c.variables return self.copy_modified( arg_types=self.arg_types[:-2] + c.arg_types, diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 9ee30b4df859..56d3fe2b4ce7 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3553,3 +3553,136 @@ class E(D): ... reveal_type([E(), D()]) # N: Revealed type is "builtins.list[__main__.D]" reveal_type([D(), E()]) # N: Revealed type is "builtins.list[__main__.D]" + +[case testCallableInferenceAgainstCallablePosVsStar] +from typing import TypeVar, Callable, Tuple + +T = TypeVar('T') +S = TypeVar('S') + +def f(x: Callable[[T, S], None]) -> Tuple[T, S]: ... +def g(*x: int) -> None: ... +reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableStarVsPos] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T, S]): + def __call__(self, __x: T, *args: S) -> None: ... + +def f(x: Call[T, S]) -> Tuple[T, S]: ... +def g(*x: int) -> None: ... +reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableNamedVsStar] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T, S]): + def __call__(self, *, x: T, y: S) -> None: ... + +def f(x: Call[T, S]) -> Tuple[T, S]: ... +def g(**kwargs: int) -> None: ... +reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableStarVsNamed] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T, S]): + def __call__(self, *, x: T, **kwargs: S) -> None: ... + +def f(x: Call[T, S]) -> Tuple[T, S]: ... +def g(**kwargs: int) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableNamedVsNamed] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T, S]): + def __call__(self, *, x: T, y: S) -> None: ... + +def f(x: Call[T, S]) -> Tuple[T, S]: ... + +# Note: order of names is different w.r.t. protocol +def g(*, y: int, x: str) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.str, builtins.int]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallablePosOnlyVsNamed] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T]): + def __call__(self, *, x: T) -> None: ... + +def f(x: Call[T]) -> Tuple[T, T]: ... + +def g(__x: str) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableNamedVsPosOnly] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T]): + def __call__(self, __x: T) -> None: ... + +def f(x: Call[T]) -> Tuple[T, T]: ... + +def g(*, x: str) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallablePosOnlyVsKwargs] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T]): + def __call__(self, __x: T) -> None: ... + +def f(x: Call[T]) -> Tuple[T, T]: ... + +def g(**x: str) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[]" +[builtins fixtures/list.pyi] + +[case testCallableInferenceAgainstCallableNamedVsArgs] +from typing import TypeVar, Callable, Tuple, Protocol + +T = TypeVar('T', contravariant=True) +S = TypeVar('S', contravariant=True) + +class Call(Protocol[T]): + def __call__(self, *, x: T) -> None: ... + +def f(x: Call[T]) -> Tuple[T, T]: ... + +def g(*args: str) -> None: pass +reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[]" +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index b778dc50b376..ede4a2e4cf62 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6640,3 +6640,13 @@ def bar(x): ... reveal_type(bar) # N: Revealed type is "Overload(def (builtins.int) -> builtins.float, def (builtins.str) -> builtins.str)" [builtins fixtures/paramspec.pyi] + +[case testOverloadOverlapWithNameOnlyArgs] +from typing import overload + +@overload +def d(x: int) -> int: ... +@overload +def d(f: int, *, x: int) -> str: ... +def d(*args, **kwargs): ... +[builtins fixtures/tuple.pyi] From 48835a362d86eb9964b0350e4453daf14c76fe8e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 23 Aug 2023 06:47:32 +0100 Subject: [PATCH 0208/1617] Fix stubtest mypy enum.Flag edge case (#15933) Fix edge-case stubtest crashes when an instance of an enum.Flag that is not a member of that enum.Flag is used as a parameter default Fixes #15923. Note: the test cases I've added reproduce the crash, but only if you're using a compiled version of mypy. (Some of them only repro the crash on <=py310, but some repro it on py311+ as well.) We run stubtest tests in CI with compiled mypy, so they do repro the crash in the context of our CI. --- mypy/stubtest.py | 2 +- mypy/test/teststubtest.py | 103 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 906a8c923b37..b2506e6dcc02 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1553,7 +1553,7 @@ def anytype() -> mypy.types.AnyType: value: bool | int | str if isinstance(runtime, bytes): value = bytes_to_human_readable_repr(runtime) - elif isinstance(runtime, enum.Enum): + elif isinstance(runtime, enum.Enum) and isinstance(runtime.name, str): value = runtime.name elif isinstance(runtime, (bool, int, str)): value = runtime diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index cd72bd9300d1..a6733a9e8bd0 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -64,6 +64,7 @@ def __init__(self, name: str) -> None: ... class Coroutine(Generic[_T_co, _S, _R]): ... class Iterable(Generic[_T_co]): ... +class Iterator(Iterable[_T_co]): ... class Mapping(Generic[_K, _V]): ... class Match(Generic[AnyStr]): ... class Sequence(Iterable[_T_co]): ... @@ -86,7 +87,9 @@ def __init__(self) -> None: pass def __repr__(self) -> str: pass class type: ... -class tuple(Sequence[T_co], Generic[T_co]): ... +class tuple(Sequence[T_co], Generic[T_co]): + def __ge__(self, __other: tuple[T_co, ...]) -> bool: pass + class dict(Mapping[KT, VT]): ... class function: pass @@ -105,6 +108,39 @@ def classmethod(f: T) -> T: ... def staticmethod(f: T) -> T: ... """ +stubtest_enum_stub = """ +import sys +from typing import Any, TypeVar, Iterator + +_T = TypeVar('_T') + +class EnumMeta(type): + def __len__(self) -> int: pass + def __iter__(self: type[_T]) -> Iterator[_T]: pass + def __reversed__(self: type[_T]) -> Iterator[_T]: pass + def __getitem__(self: type[_T], name: str) -> _T: pass + +class Enum(metaclass=EnumMeta): + def __new__(cls: type[_T], value: object) -> _T: pass + def __repr__(self) -> str: pass + def __str__(self) -> str: pass + def __format__(self, format_spec: str) -> str: pass + def __hash__(self) -> Any: pass + def __reduce_ex__(self, proto: Any) -> Any: pass + name: str + value: Any + +class Flag(Enum): + def __or__(self: _T, other: _T) -> _T: pass + def __and__(self: _T, other: _T) -> _T: pass + def __xor__(self: _T, other: _T) -> _T: pass + def __invert__(self: _T) -> _T: pass + if sys.version_info >= (3, 11): + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ +""" + def run_stubtest( stub: str, runtime: str, options: list[str], config_file: str | None = None @@ -114,6 +150,8 @@ def run_stubtest( f.write(stubtest_builtins_stub) with open("typing.pyi", "w") as f: f.write(stubtest_typing_stub) + with open("enum.pyi", "w") as f: + f.write(stubtest_enum_stub) with open(f"{TEST_MODULE_NAME}.pyi", "w") as f: f.write(stub) with open(f"{TEST_MODULE_NAME}.py", "w") as f: @@ -954,16 +992,15 @@ def fizz(self): pass @collect_cases def test_enum(self) -> Iterator[Case]: + yield Case(stub="import enum", runtime="import enum", error=None) yield Case( stub=""" - import enum class X(enum.Enum): a: int b: str c: str """, runtime=""" - import enum class X(enum.Enum): a = 1 b = "asdf" @@ -971,6 +1008,66 @@ class X(enum.Enum): """, error="X.c", ) + yield Case( + stub=""" + class Flags1(enum.Flag): + a: int + b: int + def foo(x: Flags1 = ...) -> None: ... + """, + runtime=""" + class Flags1(enum.Flag): + a = 1 + b = 2 + def foo(x=Flags1.a|Flags1.b): pass + """, + error=None, + ) + yield Case( + stub=""" + class Flags2(enum.Flag): + a: int + b: int + def bar(x: Flags2 | None = None) -> None: ... + """, + runtime=""" + class Flags2(enum.Flag): + a = 1 + b = 2 + def bar(x=Flags2.a|Flags2.b): pass + """, + error="bar", + ) + yield Case( + stub=""" + class Flags3(enum.Flag): + a: int + b: int + def baz(x: Flags3 | None = ...) -> None: ... + """, + runtime=""" + class Flags3(enum.Flag): + a = 1 + b = 2 + def baz(x=Flags3(0)): pass + """, + error=None, + ) + yield Case( + stub=""" + class Flags4(enum.Flag): + a: int + b: int + def spam(x: Flags4 | None = None) -> None: ... + """, + runtime=""" + class Flags4(enum.Flag): + a = 1 + b = 2 + def spam(x=Flags4(0)): pass + """, + error="spam", + ) @collect_cases def test_decorator(self) -> Iterator[Case]: From 6f650cff9ab21f81069e0ae30c92eae94219ea63 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 23 Aug 2023 20:26:27 +0100 Subject: [PATCH 0209/1617] Streamline some elements of variadic types support (#15924) Fixes https://github.com/python/mypy/issues/13981 Fixes https://github.com/python/mypy/issues/15241 Fixes https://github.com/python/mypy/issues/15495 Fixes https://github.com/python/mypy/issues/15633 Fixes https://github.com/python/mypy/issues/15667 Fixes https://github.com/python/mypy/issues/15897 Fixes https://github.com/python/mypy/issues/15929 OK, I started following the plan outlined in https://github.com/python/mypy/pull/15879. In this PR I focused mostly on "kinematics". Here are some notes (in random order): * I decided to normalize `TupleType` and `Instance` items in `semanal_typeargs.py` (not in the type constructors, like for unions). It looks like a simpler way to normalize for now. After this, we can rely on the fact that only non-trivial (more below on what is trivial) variadic items in a type list is either `*Ts` or `*tuple[X, ...]`. A single top-level `TupleType` can appear in an unpack only as type of `*args`. * Callables turned out to be tricky. There is certain tight coupling between `FuncDef.type` and `FuncDef.arguments` that makes it hard to normalize prefix to be expressed as individual arguments _at definition_. I faced exactly the same problem when I implemented `**` unpacking for TypedDicts. So we have two choices: either handle prefixes everywhere, or use normalization helper in relevant code. I propose to go with the latter (it worked well for `**` unpacking). * I decided to switch `Unpack` to be disallowed by default in `typeanal.py`, only very specific potions are allowed now. Although this required plumbing `allow_unpack` all the way from `semanal.py`, conceptually it is simple. This is similar to how `ParamSpec` is handled. * This PR fixes all currently open crash issues (some intentionally, some accidentally) plus a bunch of TODOs I found in the tests (but not all). * I decided to simplify `TypeAliasExpr` (and made it simple reference to the `SymbolNode`, like e.g. `TypedDictExpr` and `NamedTupleExpr`). This is not strictly necessary for this PR, but it makes some parts of it a bit simpler, and I wanted to do it for long time. Here is a more detailed plan of what I am leaving for future PRs (in rough order of priority): * Close non-crash open issues (it looks like there are only three, and all seem to be straightforward) * Handle trivial items in `UnpackType` gracefully. These are `` and `Any` (and also potentially `object`). They can appear e.g. after a user error. Currently they can cause assert crashes. (Not sure what is the best way to do this). * Go over current places where `Unpack` is handled, and verify both possible variadic items are handled. * Audit variadic `Instance` constrains and subtyping (the latter is probably OK, but the former may be broken). * Audit `Callable` and `Tuple` subtyping for variadic-related edge cases (constraints seem OK for these). * Figure out story about `map_instance_to_supertype()` (if no changes are needed, add tests for subclassing). * Clear most remaining TODOs. * Go once more over the large scale picture and check whether we have some important parts missing (or unhandled interactions between those). * Verify various "advanced" typing features work well with `TypeVarTuple`s (and add some support if missing but looks important). * Enable this feature by default. I hope to finish these in next few weeks. --- mypy/checker.py | 5 +- mypy/checkexpr.py | 11 +- mypy/constraints.py | 46 ++++-- mypy/expandtype.py | 111 +++----------- mypy/message_registry.py | 3 +- mypy/mixedtraverser.py | 2 +- mypy/nodes.py | 17 +-- mypy/semanal.py | 18 ++- mypy/semanal_typeargs.py | 45 ++++-- mypy/server/astmerge.py | 5 - mypy/server/deps.py | 2 +- mypy/strconv.py | 2 +- mypy/subtypes.py | 18 ++- mypy/typeanal.py | 72 ++++++++-- mypy/typeops.py | 9 +- mypy/types.py | 80 ++++++++++- mypy/types_utils.py | 12 +- mypy/typevartuples.py | 15 +- test-data/unit/check-generics.test | 1 - test-data/unit/check-typevar-tuple.test | 183 +++++++++++++++++++----- test-data/unit/check-varargs.test | 2 +- test-data/unit/semanal-errors.test | 9 +- 22 files changed, 439 insertions(+), 229 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 87dff91758f5..a44601b83e21 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4665,10 +4665,7 @@ def analyze_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: isinstance(iterable, TupleType) and iterable.partial_fallback.type.fullname == "builtins.tuple" ): - joined: Type = UninhabitedType() - for item in iterable.items: - joined = join_types(joined, item) - return iterator, joined + return iterator, tuple_fallback(iterable).args[0] else: # Non-tuple iterable. return iterator, echk.check_method_call_by_name("__next__", iterator, [], [], expr)[0] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4d04390da84a..6de317f587cb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -168,7 +168,7 @@ UninhabitedType, UnionType, UnpackType, - flatten_nested_tuples, + find_unpack_in_list, flatten_nested_unions, get_proper_type, get_proper_types, @@ -185,7 +185,6 @@ ) from mypy.typestate import type_state from mypy.typevars import fill_typevars -from mypy.typevartuples import find_unpack_in_list from mypy.util import split_module_names from mypy.visitor import ExpressionVisitor @@ -1600,7 +1599,7 @@ def check_callable_call( See the docstring of check_call for more information. """ # Always unpack **kwargs before checking a call. - callee = callee.with_unpacked_kwargs() + callee = callee.with_unpacked_kwargs().with_normalized_var_args() if callable_name is None and callee.name: callable_name = callee.name ret_type = get_proper_type(callee.ret_type) @@ -2409,7 +2408,12 @@ def check_argument_types( + unpacked_type.items[inner_unpack_index + 1 :] ) callee_arg_kinds = [ARG_POS] * len(actuals) + elif isinstance(unpacked_type, TypeVarTupleType): + callee_arg_types = [orig_callee_arg_type] + callee_arg_kinds = [ARG_STAR] else: + # TODO: Any and can appear in Unpack (as a result of user error), + # fail gracefully here and elsewhere (and/or normalize them away). assert isinstance(unpacked_type, Instance) assert unpacked_type.type.fullname == "builtins.tuple" callee_arg_types = [unpacked_type.args[0]] * len(actuals) @@ -4451,7 +4455,6 @@ class C(Generic[T, Unpack[Ts]]): ... prefix = next(i for (i, v) in enumerate(vars) if isinstance(v, TypeVarTupleType)) suffix = len(vars) - prefix - 1 - args = flatten_nested_tuples(args) if len(args) < len(vars) - 1: self.msg.incompatible_type_application(len(vars), len(args), ctx) return [AnyType(TypeOfAny.from_error)] * len(vars) diff --git a/mypy/constraints.py b/mypy/constraints.py index 47f312117264..edce11e778ab 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -49,6 +49,7 @@ UninhabitedType, UnionType, UnpackType, + find_unpack_in_list, get_proper_type, has_recursive_types, has_type_vars, @@ -57,7 +58,7 @@ ) from mypy.types_utils import is_union_with_any from mypy.typestate import type_state -from mypy.typevartuples import extract_unpack, find_unpack_in_list, split_with_mapped_and_template +from mypy.typevartuples import extract_unpack, split_with_mapped_and_template if TYPE_CHECKING: from mypy.infer import ArgumentInferContext @@ -155,16 +156,33 @@ def infer_constraints_for_callable( # not to hold we can always handle the prefixes too. inner_unpack = unpacked_type.items[0] assert isinstance(inner_unpack, UnpackType) - inner_unpacked_type = inner_unpack.type - assert isinstance(inner_unpacked_type, TypeVarTupleType) + inner_unpacked_type = get_proper_type(inner_unpack.type) suffix_len = len(unpacked_type.items) - 1 - constraints.append( - Constraint( - inner_unpacked_type, - SUPERTYPE_OF, - TupleType(actual_types[:-suffix_len], inner_unpacked_type.tuple_fallback), + if isinstance(inner_unpacked_type, TypeVarTupleType): + # Variadic item can be either *Ts... + constraints.append( + Constraint( + inner_unpacked_type, + SUPERTYPE_OF, + TupleType( + actual_types[:-suffix_len], inner_unpacked_type.tuple_fallback + ), + ) ) - ) + else: + # ...or it can be a homogeneous tuple. + assert ( + isinstance(inner_unpacked_type, Instance) + and inner_unpacked_type.type.fullname == "builtins.tuple" + ) + for at in actual_types[:-suffix_len]: + constraints.extend( + infer_constraints(inner_unpacked_type.args[0], at, SUPERTYPE_OF) + ) + # Now handle the suffix (if any). + if suffix_len: + for tt, at in zip(unpacked_type.items[1:], actual_types[-suffix_len:]): + constraints.extend(infer_constraints(tt, at, SUPERTYPE_OF)) else: assert False, "mypy bug: unhandled constraint inference case" else: @@ -863,6 +881,16 @@ def visit_instance(self, template: Instance) -> list[Constraint]: and self.direction == SUPERTYPE_OF ): for item in actual.items: + if isinstance(item, UnpackType): + unpacked = get_proper_type(item.type) + if isinstance(unpacked, TypeVarType): + # Cannot infer anything for T from [T, ...] <: *Ts + continue + assert ( + isinstance(unpacked, Instance) + and unpacked.type.fullname == "builtins.tuple" + ) + item = unpacked.args[0] cb = infer_constraints(template.args[0], item, SUPERTYPE_OF) res.extend(cb) return res diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 6f69e09936db..e71f6429d9c0 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -2,7 +2,7 @@ from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload -from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, Var +from mypy.nodes import ARG_STAR, Var from mypy.state import state from mypy.types import ( ANY_STRATEGY, @@ -35,12 +35,11 @@ UninhabitedType, UnionType, UnpackType, - flatten_nested_tuples, flatten_nested_unions, get_proper_type, split_with_prefix_and_suffix, ) -from mypy.typevartuples import find_unpack_in_list, split_with_instance +from mypy.typevartuples import split_with_instance # Solving the import cycle: import mypy.type_visitor # ruff: isort: skip @@ -294,11 +293,10 @@ def expand_unpack(self, t: UnpackType) -> list[Type] | AnyType | UninhabitedType def visit_parameters(self, t: Parameters) -> Type: return t.copy_modified(arg_types=self.expand_types(t.arg_types)) - # TODO: can we simplify this method? It is too long. - def interpolate_args_for_unpack( - self, t: CallableType, var_arg: UnpackType - ) -> tuple[list[str | None], list[ArgKind], list[Type]]: + def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> list[Type]: star_index = t.arg_kinds.index(ARG_STAR) + prefix = self.expand_types(t.arg_types[:star_index]) + suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] @@ -306,89 +304,19 @@ def interpolate_args_for_unpack( expanded_tuple = var_arg_type.accept(self) assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) expanded_items = expanded_tuple.items + fallback = var_arg_type.partial_fallback else: # We have plain Unpack[Ts] + assert isinstance(var_arg_type, TypeVarTupleType) + fallback = var_arg_type.tuple_fallback expanded_items_res = self.expand_unpack(var_arg) if isinstance(expanded_items_res, list): expanded_items = expanded_items_res else: # We got Any or - arg_types = ( - t.arg_types[:star_index] + [expanded_items_res] + t.arg_types[star_index + 1 :] - ) - return t.arg_names, t.arg_kinds, arg_types - - expanded_unpack_index = find_unpack_in_list(expanded_items) - # This is the case where we just have Unpack[Tuple[X1, X2, X3]] - # (for example if either the tuple had no unpacks, or the unpack in the - # tuple got fully expanded to something with fixed length) - if expanded_unpack_index is None: - arg_names = ( - t.arg_names[:star_index] - + [None] * len(expanded_items) - + t.arg_names[star_index + 1 :] - ) - arg_kinds = ( - t.arg_kinds[:star_index] - + [ARG_POS] * len(expanded_items) - + t.arg_kinds[star_index + 1 :] - ) - arg_types = ( - self.expand_types(t.arg_types[:star_index]) - + expanded_items - + self.expand_types(t.arg_types[star_index + 1 :]) - ) - else: - # If Unpack[Ts] simplest form still has an unpack or is a - # homogenous tuple, then only the prefix can be represented as - # positional arguments, and we pass Tuple[Unpack[Ts-1], Y1, Y2] - # as the star arg, for example. - expanded_unpack = expanded_items[expanded_unpack_index] - assert isinstance(expanded_unpack, UnpackType) - - # Extract the TypeVarTuple, so we can get a tuple fallback from it. - expanded_unpacked_tvt = expanded_unpack.type - if isinstance(expanded_unpacked_tvt, TypeVarTupleType): - fallback = expanded_unpacked_tvt.tuple_fallback - else: - # This can happen when tuple[Any, ...] is used to "patch" a variadic - # generic type without type arguments provided, or when substitution is - # homogeneous tuple. - assert isinstance(expanded_unpacked_tvt, ProperType) - assert isinstance(expanded_unpacked_tvt, Instance) - assert expanded_unpacked_tvt.type.fullname == "builtins.tuple" - fallback = expanded_unpacked_tvt - - prefix_len = expanded_unpack_index - arg_names = t.arg_names[:star_index] + [None] * prefix_len + t.arg_names[star_index:] - arg_kinds = ( - t.arg_kinds[:star_index] + [ARG_POS] * prefix_len + t.arg_kinds[star_index:] - ) - if ( - len(expanded_items) == 1 - and isinstance(expanded_unpack.type, ProperType) - and isinstance(expanded_unpack.type, Instance) - ): - assert expanded_unpack.type.type.fullname == "builtins.tuple" - # Normalize *args: *tuple[X, ...] -> *args: X - arg_types = ( - self.expand_types(t.arg_types[:star_index]) - + [expanded_unpack.type.args[0]] - + self.expand_types(t.arg_types[star_index + 1 :]) - ) - else: - arg_types = ( - self.expand_types(t.arg_types[:star_index]) - + expanded_items[:prefix_len] - # Constructing the Unpack containing the tuple without the prefix. - + [ - UnpackType(TupleType(expanded_items[prefix_len:], fallback)) - if len(expanded_items) - prefix_len > 1 - else expanded_items[prefix_len] - ] - + self.expand_types(t.arg_types[star_index + 1 :]) - ) - return arg_names, arg_kinds, arg_types + return prefix + [expanded_items_res] + suffix + new_unpack = UnpackType(TupleType(expanded_items, fallback)) + return prefix + [new_unpack] + suffix def visit_callable_type(self, t: CallableType) -> CallableType: param_spec = t.param_spec() @@ -427,20 +355,20 @@ def visit_callable_type(self, t: CallableType) -> CallableType: ) var_arg = t.var_arg() + needs_normalization = False if var_arg is not None and isinstance(var_arg.typ, UnpackType): - arg_names, arg_kinds, arg_types = self.interpolate_args_for_unpack(t, var_arg.typ) + needs_normalization = True + arg_types = self.interpolate_args_for_unpack(t, var_arg.typ) else: - arg_names = t.arg_names - arg_kinds = t.arg_kinds arg_types = self.expand_types(t.arg_types) - - return t.copy_modified( + expanded = t.copy_modified( arg_types=arg_types, - arg_names=arg_names, - arg_kinds=arg_kinds, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), ) + if needs_normalization: + return expanded.with_normalized_var_args() + return expanded def visit_overloaded(self, t: Overloaded) -> Type: items: list[CallableType] = [] @@ -460,9 +388,6 @@ def expand_types_with_unpack( indicates use of Any or some error occurred earlier. In this case callers should simply propagate the resulting type. """ - # TODO: this will cause a crash on aliases like A = Tuple[int, Unpack[A]]. - # Although it is unlikely anyone will write this, we should fail gracefully. - typs = flatten_nested_tuples(typs) items: list[Type] = [] for item in typs: if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): diff --git a/mypy/message_registry.py b/mypy/message_registry.py index bd3b8571b69e..713ec2e3c759 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -171,7 +171,8 @@ def with_additional_msg(self, info: str) -> ErrorMessage: IMPLICIT_GENERIC_ANY_BUILTIN: Final = ( 'Implicit generic "Any". Use "{}" and specify generic parameters' ) -INVALID_UNPACK = "{} cannot be unpacked (must be tuple or TypeVarTuple)" +INVALID_UNPACK: Final = "{} cannot be unpacked (must be tuple or TypeVarTuple)" +INVALID_UNPACK_POSITION: Final = "Unpack is only valid in a variadic position" # TypeVar INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}' diff --git a/mypy/mixedtraverser.py b/mypy/mixedtraverser.py index 771f87fc6bd6..dfde41859c67 100644 --- a/mypy/mixedtraverser.py +++ b/mypy/mixedtraverser.py @@ -49,7 +49,7 @@ def visit_class_def(self, o: ClassDef) -> None: def visit_type_alias_expr(self, o: TypeAliasExpr) -> None: super().visit_type_alias_expr(o) self.in_type_alias_expr = True - o.type.accept(self) + o.node.target.accept(self) self.in_type_alias_expr = False def visit_type_var_expr(self, o: TypeVarExpr) -> None: diff --git a/mypy/nodes.py b/mypy/nodes.py index 452a4f643255..7efb01c1b18e 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2625,27 +2625,14 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleExpr: class TypeAliasExpr(Expression): """Type alias expression (rvalue).""" - __slots__ = ("type", "tvars", "no_args", "node") + __slots__ = ("node",) - __match_args__ = ("type", "tvars", "no_args", "node") + __match_args__ = ("node",) - # The target type. - type: mypy.types.Type - # Names of type variables used to define the alias - tvars: list[str] - # Whether this alias was defined in bare form. Used to distinguish - # between - # A = List - # and - # A = List[Any] - no_args: bool node: TypeAlias def __init__(self, node: TypeAlias) -> None: super().__init__() - self.type = node.target - self.tvars = [v.name for v in node.alias_tvars] - self.no_args = node.no_args self.node = node def accept(self, visitor: ExpressionVisitor[T]) -> T: diff --git a/mypy/semanal.py b/mypy/semanal.py index ef66c9276664..55d4e6a3f506 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3680,7 +3680,10 @@ def disable_invalid_recursive_aliases( """Prohibit and fix recursive type aliases that are invalid/unsupported.""" messages = [] if is_invalid_recursive_alias({current_node}, current_node.target): - messages.append("Invalid recursive alias: a union item of itself") + target = ( + "tuple" if isinstance(get_proper_type(current_node.target), TupleType) else "union" + ) + messages.append(f"Invalid recursive alias: a {target} item of itself") if detect_diverging_alias( current_node, current_node.target, self.lookup_qualified, self.tvar_scope ): @@ -4213,6 +4216,7 @@ def get_typevarlike_argument( *, allow_unbound_tvars: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, report_invalid_typevar_arg: bool = True, ) -> ProperType | None: try: @@ -4224,6 +4228,7 @@ def get_typevarlike_argument( report_invalid_types=False, allow_unbound_tvars=allow_unbound_tvars, allow_param_spec_literals=allow_param_spec_literals, + allow_unpack=allow_unpack, ) if analyzed is None: # Type variables are special: we need to place them in the symbol table @@ -4375,6 +4380,7 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: s, allow_unbound_tvars=True, report_invalid_typevar_arg=False, + allow_unpack=True, ) default = tv_arg or AnyType(TypeOfAny.from_error) if not isinstance(default, UnpackType): @@ -5289,6 +5295,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: # Probably always allow Parameters literals, and validate in semanal_typeargs.py base = expr.base if isinstance(base, RefExpr) and isinstance(base.node, TypeAlias): + allow_unpack = base.node.tvar_tuple_index is not None alias = base.node if any(isinstance(t, ParamSpecType) for t in alias.alias_tvars): has_param_spec = True @@ -5297,9 +5304,11 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: has_param_spec = False num_args = -1 elif isinstance(base, RefExpr) and isinstance(base.node, TypeInfo): + allow_unpack = base.node.has_type_var_tuple_type has_param_spec = base.node.has_param_spec_type num_args = len(base.node.type_vars) else: + allow_unpack = False has_param_spec = False num_args = -1 @@ -5317,6 +5326,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: allow_unbound_tvars=self.allow_unbound_tvars, allow_placeholder=True, allow_param_spec_literals=has_param_spec, + allow_unpack=allow_unpack, ) if analyzed is None: return None @@ -6486,6 +6496,7 @@ def expr_to_analyzed_type( allow_type_any: bool = False, allow_unbound_tvars: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, ) -> Type | None: if isinstance(expr, CallExpr): # This is a legacy syntax intended mostly for Python 2, we keep it for @@ -6516,6 +6527,7 @@ def expr_to_analyzed_type( allow_type_any=allow_type_any, allow_unbound_tvars=allow_unbound_tvars, allow_param_spec_literals=allow_param_spec_literals, + allow_unpack=allow_unpack, ) def analyze_type_expr(self, expr: Expression) -> None: @@ -6537,6 +6549,7 @@ def type_analyzer( allow_placeholder: bool = False, allow_required: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, allow_type_any: bool = False, @@ -6555,6 +6568,7 @@ def type_analyzer( allow_placeholder=allow_placeholder, allow_required=allow_required, allow_param_spec_literals=allow_param_spec_literals, + allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, allow_type_any=allow_type_any, ) @@ -6575,6 +6589,7 @@ def anal_type( allow_placeholder: bool = False, allow_required: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, allow_type_any: bool = False, @@ -6612,6 +6627,7 @@ def anal_type( allow_placeholder=allow_placeholder, allow_required=allow_required, allow_param_spec_literals=allow_param_spec_literals, + allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, prohibit_self_type=prohibit_self_type, allow_type_any=allow_type_any, diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index e188955dabbb..8d8ef66b5c69 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -14,13 +14,14 @@ from mypy.errors import Errors from mypy.messages import format_type from mypy.mixedtraverser import MixedTraverserVisitor -from mypy.nodes import Block, ClassDef, Context, FakeInfo, FuncItem, MypyFile +from mypy.nodes import ARG_STAR, Block, ClassDef, Context, FakeInfo, FuncItem, MypyFile from mypy.options import Options from mypy.scope import Scope from mypy.subtypes import is_same_type, is_subtype from mypy.typeanal import set_any_tvars from mypy.types import ( AnyType, + CallableType, Instance, Parameters, ParamSpecType, @@ -116,20 +117,39 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: # the expansion, most likely it will result in the same kind of error. get_proper_type(t).accept(self) + def visit_tuple_type(self, t: TupleType) -> None: + t.items = flatten_nested_tuples(t.items) + # We could also normalize Tuple[*tuple[X, ...]] -> tuple[X, ...] like in + # expand_type() but we can't do this here since it is not a translator visitor, + # and we need to return an Instance instead of TupleType. + super().visit_tuple_type(t) + + def visit_callable_type(self, t: CallableType) -> None: + super().visit_callable_type(t) + # Normalize trivial unpack in var args as *args: *tuple[X, ...] -> *args: X + if t.is_var_arg: + star_index = t.arg_kinds.index(ARG_STAR) + star_type = t.arg_types[star_index] + if isinstance(star_type, UnpackType): + p_type = get_proper_type(star_type.type) + if isinstance(p_type, Instance): + assert p_type.type.fullname == "builtins.tuple" + t.arg_types[star_index] = p_type.args[0] + def visit_instance(self, t: Instance) -> None: # Type argument counts were checked in the main semantic analyzer pass. We assume # that the counts are correct here. info = t.type if isinstance(info, FakeInfo): return # https://github.com/python/mypy/issues/11079 + t.args = tuple(flatten_nested_tuples(t.args)) + # TODO: fix #15410 and #15411. self.validate_args(info.name, t.args, info.defn.type_vars, t) super().visit_instance(t) def validate_args( self, name: str, args: Sequence[Type], type_vars: list[TypeVarLikeType], ctx: Context ) -> bool: - # TODO: we need to do flatten_nested_tuples and validate arg count for instances - # similar to how do we do this for type aliases above, but this may have perf penalty. if any(isinstance(v, TypeVarTupleType) for v in type_vars): prefix = next(i for (i, v) in enumerate(type_vars) if isinstance(v, TypeVarTupleType)) tvt = type_vars[prefix] @@ -198,6 +218,7 @@ def validate_args( return is_error def visit_unpack_type(self, typ: UnpackType) -> None: + super().visit_unpack_type(typ) proper_type = get_proper_type(typ.type) if isinstance(proper_type, TupleType): return @@ -205,18 +226,14 @@ def visit_unpack_type(self, typ: UnpackType) -> None: return if isinstance(proper_type, Instance) and proper_type.type.fullname == "builtins.tuple": return - if ( - isinstance(proper_type, UnboundType) - or isinstance(proper_type, AnyType) - and proper_type.type_of_any == TypeOfAny.from_error - ): + if isinstance(proper_type, AnyType) and proper_type.type_of_any == TypeOfAny.from_error: return - - # TODO: Infer something when it can't be unpacked to allow rest of - # typechecking to work. - self.fail( - message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), typ - ) + if not isinstance(proper_type, UnboundType): + # Avoid extra errors if there were some errors already. + self.fail( + message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), typ + ) + typ.type = AnyType(TypeOfAny.from_error) def check_type_var_values( self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index f58a4eedabc8..862c3898a383 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -73,7 +73,6 @@ SymbolNode, SymbolTable, TypeAlias, - TypeAliasExpr, TypedDictExpr, TypeInfo, Var, @@ -326,10 +325,6 @@ def visit_enum_call_expr(self, node: EnumCallExpr) -> None: self.process_synthetic_type_info(node.info) super().visit_enum_call_expr(node) - def visit_type_alias_expr(self, node: TypeAliasExpr) -> None: - self.fixup_type(node.type) - super().visit_type_alias_expr(node) - # Others def visit_var(self, node: Var) -> None: diff --git a/mypy/server/deps.py b/mypy/server/deps.py index ed85b74f2206..9ed2d4549629 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -472,7 +472,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: self.add_dependency(make_trigger(class_name + ".__init__")) self.add_dependency(make_trigger(class_name + ".__new__")) if isinstance(rvalue, IndexExpr) and isinstance(rvalue.analyzed, TypeAliasExpr): - self.add_type_dependencies(rvalue.analyzed.type) + self.add_type_dependencies(rvalue.analyzed.node.target) elif typ: self.add_type_dependencies(typ) else: diff --git a/mypy/strconv.py b/mypy/strconv.py index c428addd43aa..42a07c7f62fa 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -511,7 +511,7 @@ def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> str: return self.dump(a, o) def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> str: - return f"TypeAliasExpr({self.stringify_type(o.type)})" + return f"TypeAliasExpr({self.stringify_type(o.node.target)})" def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> str: 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})" diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 288de10cc234..58ae4efdf582 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -660,6 +660,8 @@ def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: return self._is_subtype(left.upper_bound, self.right) def visit_unpack_type(self, left: UnpackType) -> bool: + # TODO: Ideally we should not need this (since it is not a real type). + # Instead callers (upper level types) should handle it when it appears in type list. if isinstance(self.right, UnpackType): return self._is_subtype(left.type, self.right.type) if isinstance(self.right, Instance) and self.right.type.fullname == "builtins.object": @@ -744,7 +746,15 @@ def visit_tuple_type(self, left: TupleType) -> bool: # TODO: We shouldn't need this special case. This is currently needed # for isinstance(x, tuple), though it's unclear why. return True - return all(self._is_subtype(li, iter_type) for li in left.items) + for li in left.items: + if isinstance(li, UnpackType): + unpack = get_proper_type(li.type) + if isinstance(unpack, Instance): + assert unpack.type.fullname == "builtins.tuple" + li = unpack.args[0] + if not self._is_subtype(li, iter_type): + return False + return True elif self._is_subtype(left.partial_fallback, right) and self._is_subtype( mypy.typeops.tuple_fallback(left), right ): @@ -752,6 +762,7 @@ def visit_tuple_type(self, left: TupleType) -> bool: return False elif isinstance(right, TupleType): if len(left.items) != len(right.items): + # TODO: handle tuple with variadic items better. return False if any(not self._is_subtype(l, r) for l, r in zip(left.items, right.items)): return False @@ -1385,8 +1396,8 @@ def g(x: int) -> int: ... whether or not we check the args covariantly. """ # Normalize both types before comparing them. - left = left.with_unpacked_kwargs() - right = right.with_unpacked_kwargs() + left = left.with_unpacked_kwargs().with_normalized_var_args() + right = right.with_unpacked_kwargs().with_normalized_var_args() if is_compat_return is None: is_compat_return = is_compat @@ -1539,6 +1550,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1c: Check var args. Right has an infinite series of optional positional # arguments. Get all further positional args of left, and make sure # they're more general than the corresponding member in right. + # TODO: are we handling UnpackType correctly here? if right_star is not None: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b15b5c7654ba..14b37539afea 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -195,6 +195,7 @@ def __init__( allow_placeholder: bool = False, allow_required: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, allowed_alias_tvars: list[TypeVarLikeType] | None = None, @@ -241,6 +242,8 @@ def __init__( self.prohibit_self_type = prohibit_self_type # Allow variables typed as Type[Any] and type (useful for base classes). self.allow_type_any = allow_type_any + self.allow_type_var_tuple = False + self.allow_unpack = allow_unpack def lookup_qualified( self, name: str, ctx: Context, suppress_errors: bool = False @@ -277,7 +280,10 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) return PlaceholderType( node.fullname, self.anal_array( - t.args, allow_param_spec=True, allow_param_spec_literals=True + t.args, + allow_param_spec=True, + allow_param_spec_literals=True, + allow_unpack=True, ), t.line, ) @@ -365,6 +371,13 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.fail(f'TypeVarTuple "{t.name}" is unbound', t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, TypeVarTupleType) + if not self.allow_type_var_tuple: + self.fail( + f'TypeVarTuple "{t.name}" is only valid with an unpack', + t, + code=codes.VALID_TYPE, + ) + return AnyType(TypeOfAny.from_error) if len(t.args) > 0: self.fail( f'Type variable "{t.name}" used with arguments', t, code=codes.VALID_TYPE @@ -390,6 +403,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) t.args, allow_param_spec=True, allow_param_spec_literals=node.has_param_spec_type, + allow_unpack=node.tvar_tuple_index is not None, ) if node.has_param_spec_type and len(node.alias_tvars) == 1: an_args = self.pack_paramspec_args(an_args) @@ -531,7 +545,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ instance = self.named_type("builtins.tuple", [self.anal_type(t.args[0])]) instance.line = t.line return instance - return self.tuple_type(self.anal_array(t.args)) + return self.tuple_type(self.anal_array(t.args, allow_unpack=True)) elif fullname == "typing.Union": items = self.anal_array(t.args) return UnionType.make_union(items) @@ -631,7 +645,13 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ if len(t.args) != 1: self.fail("Unpack[...] requires exactly one type argument", t) return AnyType(TypeOfAny.from_error) - return UnpackType(self.anal_type(t.args[0]), line=t.line, column=t.column) + if not self.allow_unpack: + self.fail(message_registry.INVALID_UNPACK_POSITION, t, code=codes.VALID_TYPE) + return AnyType(TypeOfAny.from_error) + self.allow_type_var_tuple = True + result = UnpackType(self.anal_type(t.args[0]), line=t.line, column=t.column) + self.allow_type_var_tuple = False + return result elif fullname in SELF_TYPE_NAMES: if t.args: self.fail("Self type cannot have type arguments", t) @@ -666,7 +686,7 @@ def analyze_type_with_type_info( if len(args) > 0 and info.fullname == "builtins.tuple": fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line) - return TupleType(self.anal_array(args), fallback, ctx.line) + return TupleType(self.anal_array(args, allow_unpack=True), fallback, ctx.line) # Analyze arguments and (usually) construct Instance type. The # number of type arguments and their values are @@ -679,7 +699,10 @@ def analyze_type_with_type_info( instance = Instance( info, self.anal_array( - args, allow_param_spec=True, allow_param_spec_literals=info.has_param_spec_type + args, + allow_param_spec=True, + allow_param_spec_literals=info.has_param_spec_type, + allow_unpack=info.has_type_var_tuple_type, ), ctx.line, ctx.column, @@ -715,7 +738,7 @@ def analyze_type_with_type_info( if info.special_alias: return instantiate_type_alias( info.special_alias, - # TODO: should we allow NamedTuples generic in ParamSpec? + # TODO: should we allow NamedTuples generic in ParamSpec and TypeVarTuple? self.anal_array(args), self.fail, False, @@ -723,7 +746,9 @@ def analyze_type_with_type_info( self.options, use_standard_error=True, ) - return tup.copy_modified(items=self.anal_array(tup.items), fallback=instance) + return tup.copy_modified( + items=self.anal_array(tup.items, allow_unpack=True), fallback=instance + ) td = info.typeddict_type if td is not None: # The class has a TypedDict[...] base class so it will be @@ -940,7 +965,23 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: self.anal_star_arg_type(t.arg_types[-1], ARG_STAR2, nested=nested), ] else: - arg_types = self.anal_array(t.arg_types, nested=nested) + arg_types = self.anal_array(t.arg_types, nested=nested, allow_unpack=True) + star_index = None + if ARG_STAR in arg_kinds: + star_index = arg_kinds.index(ARG_STAR) + star2_index = None + if ARG_STAR2 in arg_kinds: + star2_index = arg_kinds.index(ARG_STAR2) + validated_args: list[Type] = [] + for i, at in enumerate(arg_types): + if isinstance(at, UnpackType) and i not in (star_index, star2_index): + self.fail( + message_registry.INVALID_UNPACK_POSITION, at, code=codes.VALID_TYPE + ) + validated_args.append(AnyType(TypeOfAny.from_error)) + else: + validated_args.append(at) + arg_types = validated_args # If there were multiple (invalid) unpacks, the arg types list will become shorter, # we need to trim the kinds/names as well to avoid crashes. arg_kinds = t.arg_kinds[: len(arg_types)] @@ -1012,7 +1053,7 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: line=t.line, column=t.column, ) - return self.anal_type(t, nested=nested) + return self.anal_type(t, nested=nested, allow_unpack=True) def visit_overloaded(self, t: Overloaded) -> Type: # Overloaded types are manually constructed in semanal.py by analyzing the @@ -1051,7 +1092,7 @@ def visit_tuple_type(self, t: TupleType) -> Type: if t.partial_fallback.type else self.named_type("builtins.tuple", [any_type]) ) - return TupleType(self.anal_array(t.items), fallback, t.line) + return TupleType(self.anal_array(t.items, allow_unpack=True), fallback, t.line) def visit_typeddict_type(self, t: TypedDictType) -> Type: items = { @@ -1534,12 +1575,17 @@ def anal_array( *, allow_param_spec: bool = False, allow_param_spec_literals: bool = False, + allow_unpack: bool = False, ) -> list[Type]: old_allow_param_spec_literals = self.allow_param_spec_literals self.allow_param_spec_literals = allow_param_spec_literals res: list[Type] = [] for t in a: - res.append(self.anal_type(t, nested, allow_param_spec=allow_param_spec)) + res.append( + self.anal_type( + t, nested, allow_param_spec=allow_param_spec, allow_unpack=allow_unpack + ) + ) self.allow_param_spec_literals = old_allow_param_spec_literals return self.check_unpacks_in_list(res) @@ -1549,6 +1595,7 @@ def anal_type( nested: bool = True, *, allow_param_spec: bool = False, + allow_unpack: bool = False, allow_ellipsis: bool = False, ) -> Type: if nested: @@ -1557,6 +1604,8 @@ def anal_type( self.allow_required = False old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis + old_allow_unpack = self.allow_unpack + self.allow_unpack = allow_unpack try: analyzed = t.accept(self) finally: @@ -1564,6 +1613,7 @@ def anal_type( self.nesting_level -= 1 self.allow_required = old_allow_required self.allow_ellipsis = old_allow_ellipsis + self.allow_unpack = old_allow_unpack if ( not allow_param_spec and isinstance(analyzed, ParamSpecType) diff --git a/mypy/typeops.py b/mypy/typeops.py index e01aad950573..0e0bc348942e 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -105,19 +105,18 @@ def tuple_fallback(typ: TupleType) -> Instance: unpacked_type = get_proper_type(item.type) if isinstance(unpacked_type, TypeVarTupleType): items.append(unpacked_type.upper_bound) - elif isinstance(unpacked_type, TupleType): - # TODO: might make sense to do recursion here to support nested unpacks - # of tuple constants - items.extend(unpacked_type.items) elif ( isinstance(unpacked_type, Instance) and unpacked_type.type.fullname == "builtins.tuple" ): items.append(unpacked_type.args[0]) + elif isinstance(unpacked_type, (AnyType, UninhabitedType)): + continue else: - raise NotImplementedError + raise NotImplementedError(unpacked_type) else: items.append(item) + # TODO: we should really use a union here, tuple types are special. return Instance(info, [join_type_list(items)], extra_attrs=typ.partial_fallback.extra_attrs) diff --git a/mypy/types.py b/mypy/types.py index 301ce6e0cf18..c71412f4ea58 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2075,6 +2075,68 @@ def with_unpacked_kwargs(self) -> NormalizedCallableType: ) ) + def with_normalized_var_args(self) -> Self: + var_arg = self.var_arg() + if not var_arg or not isinstance(var_arg.typ, UnpackType): + return self + unpacked = get_proper_type(var_arg.typ.type) + if not isinstance(unpacked, TupleType): + # Note that we don't normalize *args: *tuple[X, ...] -> *args: X, + # this should be done once in semanal_typeargs.py for user-defined types, + # and we ourselves should never construct such type. + return self + unpack_index = find_unpack_in_list(unpacked.items) + if unpack_index == 0 and len(unpacked.items) > 1: + # Already normalized. + return self + + # Boilerplate: + var_arg_index = self.arg_kinds.index(ARG_STAR) + types_prefix = self.arg_types[:var_arg_index] + kinds_prefix = self.arg_kinds[:var_arg_index] + names_prefix = self.arg_names[:var_arg_index] + types_suffix = self.arg_types[var_arg_index + 1 :] + kinds_suffix = self.arg_kinds[var_arg_index + 1 :] + names_suffix = self.arg_names[var_arg_index + 1 :] + no_name: str | None = None # to silence mypy + + # Now we have something non-trivial to do. + if unpack_index is None: + # Plain *Tuple[X, Y, Z] -> replace with ARG_POS completely + types_middle = unpacked.items + kinds_middle = [ARG_POS] * len(unpacked.items) + names_middle = [no_name] * len(unpacked.items) + else: + # *Tuple[X, *Ts, Y, Z] or *Tuple[X, *tuple[T, ...], X, Z], here + # we replace the prefix by ARG_POS (this is how some places expect + # Callables to be represented) + nested_unpack = unpacked.items[unpack_index] + assert isinstance(nested_unpack, UnpackType) + nested_unpacked = get_proper_type(nested_unpack.type) + if unpack_index == len(unpacked.items) - 1: + # Normalize also single item tuples like + # *args: *Tuple[*tuple[X, ...]] -> *args: X + # *args: *Tuple[*Ts] -> *args: *Ts + # This may be not strictly necessary, but these are very verbose. + if isinstance(nested_unpacked, Instance): + assert nested_unpacked.type.fullname == "builtins.tuple" + new_unpack = nested_unpacked.args[0] + else: + assert isinstance(nested_unpacked, TypeVarTupleType) + new_unpack = nested_unpack + else: + new_unpack = UnpackType( + unpacked.copy_modified(items=unpacked.items[unpack_index:]) + ) + types_middle = unpacked.items[:unpack_index] + [new_unpack] + kinds_middle = [ARG_POS] * unpack_index + [ARG_STAR] + names_middle = [no_name] * unpack_index + [self.arg_names[var_arg_index]] + return self.copy_modified( + arg_types=types_prefix + types_middle + types_suffix, + arg_kinds=kinds_prefix + kinds_middle + kinds_suffix, + arg_names=names_prefix + names_middle + names_suffix, + ) + def __hash__(self) -> int: # self.is_type_obj() will fail if self.fallback.type is a FakeInfo if isinstance(self.fallback.type, FakeInfo): @@ -2259,10 +2321,6 @@ def __init__( ) -> None: super().__init__(line, column) self.partial_fallback = fallback - # TODO: flatten/normalize unpack items (very similar to unions) here. - # Probably also for instances, type aliases, callables, and Unpack itself. For example, - # tuple[*tuple[X, ...], ...] -> tuple[X, ...] and Tuple[*tuple[X, ...]] -> tuple[X, ...]. - # Currently normalization happens in expand_type() et al., which is sub-optimal. self.items = items self.implicit = implicit @@ -3426,6 +3484,20 @@ def flatten_nested_unions( return flat_items +def find_unpack_in_list(items: Sequence[Type]) -> int | None: + unpack_index: int | None = None + for i, item in enumerate(items): + if isinstance(item, UnpackType): + # We cannot fail here, so we must check this in an earlier + # semanal phase. + # Funky code here avoids mypyc narrowing the type of unpack_index. + old_index = unpack_index + assert old_index is None + # Don't return so that we can also sanity check there is only one. + unpack_index = i + return unpack_index + + def flatten_nested_tuples(types: Sequence[Type]) -> list[Type]: """Recursively flatten TupleTypes nested with Unpack. diff --git a/mypy/types_utils.py b/mypy/types_utils.py index 7f2e38ef3753..f289ac3e9ed1 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -54,7 +54,7 @@ def strip_type(typ: Type) -> Type: def is_invalid_recursive_alias(seen_nodes: set[TypeAlias], target: Type) -> bool: - """Flag aliases like A = Union[int, A] (and similar mutual aliases). + """Flag aliases like A = Union[int, A], T = tuple[int, *T] (and similar mutual aliases). Such aliases don't make much sense, and cause problems in later phases. """ @@ -64,9 +64,15 @@ def is_invalid_recursive_alias(seen_nodes: set[TypeAlias], target: Type) -> bool assert target.alias, f"Unfixed type alias {target.type_ref}" return is_invalid_recursive_alias(seen_nodes | {target.alias}, get_proper_type(target)) assert isinstance(target, ProperType) - if not isinstance(target, UnionType): + if not isinstance(target, (UnionType, TupleType)): return False - return any(is_invalid_recursive_alias(seen_nodes, item) for item in target.items) + if isinstance(target, UnionType): + return any(is_invalid_recursive_alias(seen_nodes, item) for item in target.items) + for item in target.items: + if isinstance(item, UnpackType): + if is_invalid_recursive_alias(seen_nodes, item.type): + return True + return False def is_bad_type_type_item(item: Type) -> bool: diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index 29c800140eec..bcb5e96b615c 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -9,25 +9,12 @@ ProperType, Type, UnpackType, + find_unpack_in_list, get_proper_type, split_with_prefix_and_suffix, ) -def find_unpack_in_list(items: Sequence[Type]) -> int | None: - unpack_index: int | None = None - for i, item in enumerate(items): - if isinstance(item, UnpackType): - # We cannot fail here, so we must check this in an earlier - # semanal phase. - # Funky code here avoids mypyc narrowing the type of unpack_index. - old_index = unpack_index - assert old_index is None - # Don't return so that we can also sanity check there is only one. - unpack_index = i - return unpack_index - - def split_with_instance( typ: Instance, ) -> tuple[tuple[Type, ...], tuple[Type, ...], tuple[Type, ...]]: diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 95a7bdd2b2cd..93674c0c2d5c 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3360,7 +3360,6 @@ class Foo(Generic[Unpack[Ts]]): ... class Bar(Generic[Unpack[Ts], T]): ... def dec(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], List[T]]: ... -# TODO: do not crash on Foo[Us] (with missing Unpack), instead give an error. def f(*args: Unpack[Us]) -> Foo[Unpack[Us]]: ... reveal_type(dec(f)) # N: Revealed type is "def [Ts] (*Unpack[Ts`1]) -> builtins.list[__main__.Foo[Unpack[Ts`1]]]" g: Callable[[Unpack[Us]], Foo[Unpack[Us]]] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index b28b2ead45e7..58fc1265ae99 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -60,9 +60,10 @@ reveal_type(f(f_args2)) # N: Revealed type is "Tuple[builtins.str]" reveal_type(f(f_args3)) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.bool]" f(empty) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Tuple[int]" f(bad_args) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[int, str]" -# TODO: This hits a crash where we assert len(templates.items) == 1. See visit_tuple_type -# in mypy/constraints.py. -#f(var_len_tuple) + +# The reason for error in subtle: actual can be empty, formal cannot. +reveal_type(f(var_len_tuple)) # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]]]" \ + # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]" g_args: Tuple[str, int] reveal_type(g(g_args)) # N: Revealed type is "Tuple[builtins.str, builtins.str]" @@ -123,13 +124,10 @@ reveal_type(empty) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tup bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]] # E: More than one Unpack in a type is not allowed reveal_type(bad) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" -# TODO: This is tricky to fix because we need typeanal to know whether the current -# location is valid for an Unpack or not. -# bad2: Unpack[Tuple[int, ...]] +bad2: Unpack[Tuple[int, ...]] # E: Unpack is only valid in a variadic position m1: Mixed1[int, str, bool] reveal_type(m1) # N: Revealed type is "__main__.Mixed1[builtins.int, builtins.str, builtins.bool]" - [builtins fixtures/tuple.pyi] [case testTypeVarTupleGenericClassWithFunctions] @@ -148,7 +146,6 @@ def foo(t: Variadic[int, Unpack[Ts], object]) -> Tuple[int, Unpack[Ts]]: v: Variadic[int, str, bool, object] reveal_type(foo(v)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" - [builtins fixtures/tuple.pyi] [case testTypeVarTupleGenericClassWithMethods] @@ -168,7 +165,6 @@ class Variadic(Generic[T, Unpack[Ts], S]): v: Variadic[float, str, bool, object] reveal_type(v.foo(0)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" - [builtins fixtures/tuple.pyi] [case testTypeVarTupleIsNotValidAliasTarget] @@ -211,8 +207,8 @@ shape = (Height(480), Width(640)) x: Array[Height, Width] = Array(shape) reveal_type(abs(x)) # N: Revealed type is "__main__.Array[__main__.Height, __main__.Width]" reveal_type(x + x) # N: Revealed type is "__main__.Array[__main__.Height, __main__.Width]" - [builtins fixtures/tuple.pyi] + [case testTypeVarTuplePep646ArrayExampleWithDType] from typing import Generic, Tuple, TypeVar, Protocol, NewType from typing_extensions import TypeVarTuple, Unpack @@ -247,7 +243,6 @@ shape = (Height(480), Width(640)) x: Array[float, Height, Width] = Array(shape) reveal_type(abs(x)) # N: Revealed type is "__main__.Array[builtins.float, __main__.Height, __main__.Width]" reveal_type(x + x) # N: Revealed type is "__main__.Array[builtins.float, __main__.Height, __main__.Width]" - [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646ArrayExampleInfer] @@ -293,8 +288,8 @@ c = del_batch_axis(b) reveal_type(c) # N: Revealed type is "__main__.Array[__main__.Height, __main__.Width]" d = add_batch_channels(a) reveal_type(d) # N: Revealed type is "__main__.Array[__main__.Batch, __main__.Height, __main__.Width, __main__.Channels]" - [builtins fixtures/tuple.pyi] + [case testTypeVarTuplePep646TypeVarConcatenation] from typing import Generic, TypeVar, NewType, Tuple from typing_extensions import TypeVarTuple, Unpack @@ -311,6 +306,7 @@ def prefix_tuple( z = prefix_tuple(x=0, y=(True, 'a')) reveal_type(z) # N: Revealed type is "Tuple[builtins.int, builtins.bool, builtins.str]" [builtins fixtures/tuple.pyi] + [case testTypeVarTuplePep646TypeVarTupleUnpacking] from typing import Generic, TypeVar, NewType, Any, Tuple from typing_extensions import TypeVarTuple, Unpack @@ -363,8 +359,6 @@ reveal_type(bad) # N: Revealed type is "def [Ts, Ts2] (x: Tuple[builtins.int, U def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None: # E: More than one Unpack in a type is not allowed ... reveal_type(bad2) # N: Revealed type is "def (x: Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])" - - [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsBasic] @@ -380,8 +374,8 @@ def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: return args reveal_type(args_to_tuple(1, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Literal['a']?]" - [builtins fixtures/tuple.pyi] + [case testTypeVarTuplePep646TypeVarStarArgs] from typing import Tuple from typing_extensions import TypeVarTuple, Unpack @@ -410,8 +404,6 @@ with_prefix_suffix(*bad_t) # E: Too few arguments for "with_prefix_suffix" def foo(*args: Unpack[Ts]) -> None: reveal_type(with_prefix_suffix(True, "bar", *args, 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" - - [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsFixedLengthTuple] @@ -422,17 +414,23 @@ def foo(*args: Unpack[Tuple[int, str]]) -> None: reveal_type(args) # N: Revealed type is "Tuple[builtins.int, builtins.str]" foo(0, "foo") -foo(0, 1) # E: Argument 2 to "foo" has incompatible type "int"; expected "Unpack[Tuple[int, str]]" -foo("foo", "bar") # E: Argument 1 to "foo" has incompatible type "str"; expected "Unpack[Tuple[int, str]]" -foo(0, "foo", 1) # E: Invalid number of arguments -foo(0) # E: Invalid number of arguments -foo() # E: Invalid number of arguments +foo(0, 1) # E: Argument 2 to "foo" has incompatible type "int"; expected "str" +foo("foo", "bar") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" +foo(0, "foo", 1) # E: Too many arguments for "foo" +foo(0) # E: Too few arguments for "foo" +foo() # E: Too few arguments for "foo" foo(*(0, "foo")) -# TODO: fix this case to do something sensible. -#def foo2(*args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: -# reveal_type(args) +def foo2(*args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: + reveal_type(args) # N: Revealed type is "Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]" + +# It is hard to normalize callable types in definition, because there is deep relation between `FuncDef.type` +# and `FuncDef.arguments`, therefore various typeops need to be sure to normalize Callable types before using them. +reveal_type(foo2) # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" +class C: + def foo2(self, *args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: ... +reveal_type(C().foo2) # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsVariableLengthTuple] @@ -443,8 +441,7 @@ def foo(*args: Unpack[Tuple[int, ...]]) -> None: reveal_type(args) # N: Revealed type is "builtins.tuple[builtins.int, ...]" foo(0, 1, 2) -# TODO: this should say 'expected "int"' rather than the unpack -foo(0, 1, "bar") # E: Argument 3 to "foo" has incompatible type "str"; expected "Unpack[Tuple[int, ...]]" +foo(0, 1, "bar") # E: Argument 3 to "foo" has incompatible type "str"; expected "int" def foo2(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]) -> None: @@ -453,9 +450,9 @@ def foo2(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]) -> None # reveal_type(args[1]) foo2("bar", 1, 2, 3, False, True) -foo2(0, 1, 2, 3, False, True) # E: Argument 1 to "foo2" has incompatible type "int"; expected "Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]" -foo2("bar", "bar", 2, 3, False, True) # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]" -foo2("bar", 1, 2, 3, 4, True) # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]" +foo2(0, 1, 2, 3, False, True) # E: Argument 1 to "foo2" has incompatible type "int"; expected "str" +foo2("bar", "bar", 2, 3, False, True) # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]" +foo2("bar", 1, 2, 3, 4, True) # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]" foo2(*("bar", 1, 2, 3, False, True)) [builtins fixtures/tuple.pyi] @@ -550,8 +547,7 @@ def call( *args: Unpack[Ts], ) -> None: ... - # TODO: exposes unhandled case in checkexpr - # target(*args) + target(*args) class A: def func(self, arg1: int, arg2: str) -> None: ... @@ -569,7 +565,6 @@ call(A().func, 0, 1) # E: Argument 1 to "call" has incompatible type "Callable[ call(A().func2, 0, 0) call(A().func3, 0, 1, 2) call(A().func3) - [builtins fixtures/tuple.pyi] [case testVariadicAliasBasicTuple] @@ -805,3 +800,125 @@ reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple y: A[Unpack[Tuple[bool, ...]]] reveal_type(y) # N: Revealed type is "Tuple[builtins.bool, Unpack[builtins.tuple[builtins.bool, ...]], builtins.bool, builtins.bool]" [builtins fixtures/tuple.pyi] + +[case testBanPathologicalRecursiveTuples] +from typing import Tuple +from typing_extensions import Unpack +A = Tuple[int, Unpack[A]] # E: Invalid recursive alias: a tuple item of itself +B = Tuple[int, Unpack[C]] # E: Invalid recursive alias: a tuple item of itself \ + # E: Name "C" is used before definition +C = Tuple[int, Unpack[B]] +x: A +y: B +z: C +reveal_type(x) # N: Revealed type is "Any" +reveal_type(y) # N: Revealed type is "Any" +reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[Any]]" +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericVariadicWithBadType] +# flags: --new-type-inference +from typing import TypeVar, Callable, Generic +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +Us = TypeVarTuple("Us") + +class Foo(Generic[Unpack[Ts]]): ... + +def dec(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], T]: ... +def f(*args: Unpack[Us]) -> Foo[Us]: ... # E: TypeVarTuple "Us" is only valid with an unpack +dec(f) # No crash +[builtins fixtures/tuple.pyi] + +[case testHomogeneousGenericTupleUnpackInferenceNoCrash1] +from typing import Any, TypeVar, Tuple, Type, Optional +from typing_extensions import Unpack + +T = TypeVar("T") +def convert(obj: Any, *to_classes: Unpack[Tuple[Type[T], ...]]) -> Optional[T]: + ... + +x = convert(1, int, float) +reveal_type(x) # N: Revealed type is "Union[builtins.float, None]" +[builtins fixtures/tuple.pyi] + +[case testHomogeneousGenericTupleUnpackInferenceNoCrash2] +from typing import TypeVar, Tuple, Callable, Iterable +from typing_extensions import Unpack + +T = TypeVar("T") +def combine(x: T, y: T) -> T: ... +def reduce(fn: Callable[[T, T], T], xs: Iterable[T]) -> T: ... + +def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[str, ...]], bool]]) -> None: + reduce(combine, xs) +[builtins fixtures/tuple.pyi] + +[case testVariadicStarArgsCallNoCrash] +from typing import TypeVar, Callable, Tuple +from typing_extensions import TypeVarTuple, Unpack + +X = TypeVar("X") +Y = TypeVar("Y") +Xs = TypeVarTuple("Xs") +Ys = TypeVarTuple("Ys") + +def nil() -> Tuple[()]: + return () + +def cons( + f: Callable[[X], Y], + g: Callable[[Unpack[Xs]], Tuple[Unpack[Ys]]], +) -> Callable[[X, Unpack[Xs]], Tuple[Y, Unpack[Ys]]]: + def wrapped(x: X, *xs: Unpack[Xs]) -> Tuple[Y, Unpack[Ys]]: + y, ys = f(x), g(*xs) + return y, *ys + return wrapped + +def star(f: Callable[[X], Y]) -> Callable[[Unpack[Tuple[X, ...]]], Tuple[Y, ...]]: + def wrapped(*xs: X): + if not xs: + return nil() + return cons(f, star(f))(*xs) + return wrapped +[builtins fixtures/tuple.pyi] + +[case testInvalidTypeVarTupleUseNoCrash] +from typing_extensions import TypeVarTuple + +Ts = TypeVarTuple("Ts") + +def f(x: Ts) -> Ts: # E: TypeVarTuple "Ts" is only valid with an unpack + return x + +v = f(1, 2, "A") # E: Too many arguments for "f" +reveal_type(v) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleSimpleDecoratorWorks] +from typing import TypeVar, Callable +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +T = TypeVar("T") + +def decorator(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], T]: + def wrapper(*args: Unpack[Ts]) -> T: + return f(*args) + return wrapper + +@decorator +def f(a: int, b: int) -> int: ... +reveal_type(f) # N: Revealed type is "def (builtins.int, builtins.int) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTupleWithUnpackIterator] +from typing import Tuple +from typing_extensions import Unpack + +def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[float, ...]], bool]]) -> None: + for x in xs: + reveal_type(x) # N: Revealed type is "builtins.float" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 6e118597551f..fe09fb43c97c 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -775,7 +775,7 @@ class Person(TypedDict): name: str age: int -def foo(x: Unpack[Person]) -> None: # E: "Person" cannot be unpacked (must be tuple or TypeVarTuple) +def foo(x: Unpack[Person]) -> None: # E: Unpack is only valid in a variadic position ... def bar(x: int, *args: Unpack[Person]) -> None: # E: "Person" cannot be unpacked (must be tuple or TypeVarTuple) ... diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 09d4da54bff3..f21ba5253437 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1457,7 +1457,7 @@ homogenous_tuple: Tuple[Unpack[Tuple[int, ...]]] bad: Tuple[Unpack[int]] # E: "int" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/tuple.pyi] -[case testTypeVarTuple] +[case testTypeVarTupleErrors] from typing import Generic from typing_extensions import TypeVarTuple, Unpack @@ -1471,15 +1471,14 @@ TP5 = TypeVarTuple(t='TP5') # E: TypeVarTuple() expects a string literal as fir TP6 = TypeVarTuple('TP6', bound=int) # E: Unexpected keyword argument "bound" for "TypeVarTuple" x: TVariadic # E: TypeVarTuple "TVariadic" is unbound -y: Unpack[TVariadic] # E: TypeVarTuple "TVariadic" is unbound +y: Unpack[TVariadic] # E: Unpack is only valid in a variadic position class Variadic(Generic[Unpack[TVariadic], Unpack[TVariadic2]]): # E: Can only use one type var tuple in a class def pass -# TODO: this should generate an error -#def bad_args(*args: TVariadic): -# pass +def bad_args(*args: TVariadic): # E: TypeVarTuple "TVariadic" is only valid with an unpack + pass def bad_kwargs(**kwargs: Unpack[TVariadic]): # E: Unpack item in ** argument must be a TypedDict pass From 0b303b53479897e24d57affef6a8cdfffbd08e3d Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 24 Aug 2023 00:47:22 +0100 Subject: [PATCH 0210/1617] stubtest: error if typeshed is missing modules from the stdlib (#15729) We currently flag modules missing from third-party stubs in stubtest, but don't do similarly for typeshed's stdlib stubs. This PR adds that functionality for typeshed's stdlib stubs as well. --- mypy/stubtest.py | 83 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index b2506e6dcc02..d8a613034b3a 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -11,6 +11,7 @@ import copy import enum import importlib +import importlib.machinery import inspect import os import pkgutil @@ -25,7 +26,7 @@ from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path -from typing import Any, Generic, Iterator, TypeVar, Union +from typing import AbstractSet, Any, Generic, Iterator, TypeVar, Union from typing_extensions import get_origin, is_typeddict import mypy.build @@ -1639,7 +1640,7 @@ def get_stub(module: str) -> nodes.MypyFile | None: def get_typeshed_stdlib_modules( custom_typeshed_dir: str | None, version_info: tuple[int, int] | None = None -) -> list[str]: +) -> set[str]: """Returns a list of stdlib modules in typeshed (for current Python version).""" stdlib_py_versions = mypy.modulefinder.load_stdlib_py_versions(custom_typeshed_dir) if version_info is None: @@ -1661,14 +1662,75 @@ def exists_in_version(module: str) -> bool: typeshed_dir = Path(mypy.build.default_data_dir()) / "typeshed" stdlib_dir = typeshed_dir / "stdlib" - modules = [] + modules: set[str] = set() for path in stdlib_dir.rglob("*.pyi"): if path.stem == "__init__": path = path.parent module = ".".join(path.relative_to(stdlib_dir).parts[:-1] + (path.stem,)) if exists_in_version(module): - modules.append(module) - return sorted(modules) + modules.add(module) + return modules + + +def get_importable_stdlib_modules() -> set[str]: + """Return all importable stdlib modules at runtime.""" + all_stdlib_modules: AbstractSet[str] + if sys.version_info >= (3, 10): + all_stdlib_modules = sys.stdlib_module_names + else: + all_stdlib_modules = set(sys.builtin_module_names) + python_exe_dir = Path(sys.executable).parent + for m in pkgutil.iter_modules(): + finder = m.module_finder + if isinstance(finder, importlib.machinery.FileFinder): + finder_path = Path(finder.path) + if ( + python_exe_dir in finder_path.parents + and "site-packages" not in finder_path.parts + ): + all_stdlib_modules.add(m.name) + + importable_stdlib_modules: set[str] = set() + for module_name in all_stdlib_modules: + if module_name in ANNOYING_STDLIB_MODULES: + continue + + try: + runtime = silent_import_module(module_name) + except ImportError: + continue + else: + importable_stdlib_modules.add(module_name) + + try: + # some stdlib modules (e.g. `nt`) don't have __path__ set... + runtime_path = runtime.__path__ + runtime_name = runtime.__name__ + except AttributeError: + continue + + for submodule in pkgutil.walk_packages(runtime_path, runtime_name + "."): + submodule_name = submodule.name + + # There are many annoying *.__main__ stdlib modules, + # and including stubs for them isn't really that useful anyway: + # tkinter.__main__ opens a tkinter windows; unittest.__main__ raises SystemExit; etc. + # + # The idlelib.* submodules are similarly annoying in opening random tkinter windows, + # and we're unlikely to ever add stubs for idlelib in typeshed + # (see discussion in https://github.com/python/typeshed/pull/9193) + if submodule_name.endswith(".__main__") or submodule_name.startswith("idlelib."): + continue + + try: + silent_import_module(submodule_name) + # importing multiprocessing.popen_forkserver on Windows raises AttributeError... + except Exception: + continue + else: + importable_stdlib_modules.add(submodule_name) + + return importable_stdlib_modules def get_allowlist_entries(allowlist_file: str) -> Iterator[str]: @@ -1699,6 +1761,10 @@ class _Arguments: version: str +# typeshed added a stub for __main__, but that causes stubtest to check itself +ANNOYING_STDLIB_MODULES: typing_extensions.Final = frozenset({"antigravity", "this", "__main__"}) + + def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: """This is stubtest! It's time to test the stubs!""" # Load the allowlist. This is a series of strings corresponding to Error.object_desc @@ -1721,10 +1787,9 @@ def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: "cannot pass both --check-typeshed and a list of modules", ) return 1 - modules = get_typeshed_stdlib_modules(args.custom_typeshed_dir) - # typeshed added a stub for __main__, but that causes stubtest to check itself - annoying_modules = {"antigravity", "this", "__main__"} - modules = [m for m in modules if m not in annoying_modules] + typeshed_modules = get_typeshed_stdlib_modules(args.custom_typeshed_dir) + runtime_modules = get_importable_stdlib_modules() + modules = sorted((typeshed_modules | runtime_modules) - ANNOYING_STDLIB_MODULES) if not modules: print(_style("error:", color="red", bold=True), "no modules to check") From 4077dc6c4b87b273bfd4552d75faaafa6c016c25 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 24 Aug 2023 08:29:00 +0100 Subject: [PATCH 0211/1617] stubtest: fix edge case for bytes enum subclasses (#15943) --- mypy/stubtest.py | 6 +++--- mypy/test/teststubtest.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d8a613034b3a..34bb985b702e 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1552,10 +1552,10 @@ def anytype() -> mypy.types.AnyType: fallback = mypy.types.Instance(type_info, [anytype() for _ in type_info.type_vars]) value: bool | int | str - if isinstance(runtime, bytes): - value = bytes_to_human_readable_repr(runtime) - elif isinstance(runtime, enum.Enum) and isinstance(runtime.name, str): + if isinstance(runtime, enum.Enum) and isinstance(runtime.name, str): value = runtime.name + elif isinstance(runtime, bytes): + value = bytes_to_human_readable_repr(runtime) elif isinstance(runtime, (bool, int, str)): value = runtime else: diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index a6733a9e8bd0..a52d9ef5de31 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1068,6 +1068,26 @@ def spam(x=Flags4(0)): pass """, error="spam", ) + yield Case( + stub=""" + from typing_extensions import Final, Literal + class BytesEnum(bytes, enum.Enum): + a: bytes + FOO: Literal[BytesEnum.a] + BAR: Final = BytesEnum.a + BAZ: BytesEnum + EGGS: bytes + """, + runtime=""" + class BytesEnum(bytes, enum.Enum): + a = b'foo' + FOO = BytesEnum.a + BAR = BytesEnum.a + BAZ = BytesEnum.a + EGGS = BytesEnum.a + """, + error=None, + ) @collect_cases def test_decorator(self) -> Iterator[Case]: From dc7344539bd6e40825486dfaaa3d0bc34a64784e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 24 Aug 2023 19:56:13 +0300 Subject: [PATCH 0212/1617] Do not advertise to create your own `assert_never` helper (#15947) --- docs/source/literal_types.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index a66d300bd0fd..283bf7f9dba1 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -329,13 +329,10 @@ perform an exhaustiveness check, you need to update your code to use an .. code-block:: python from typing import Literal, NoReturn + from typing_extensions import assert_never PossibleValues = Literal['one', 'two'] - def assert_never(value: NoReturn) -> NoReturn: - # This also works at runtime as well - assert False, f'This code should never be reached, got: {value}' - def validate(x: PossibleValues) -> bool: if x == 'one': return True @@ -443,10 +440,7 @@ Let's start with a definition: from enum import Enum from typing import NoReturn - - def assert_never(value: NoReturn) -> NoReturn: - # This also works in runtime as well: - assert False, f'This code should never be reached, got: {value}' + from typing_extensions import assert_never class Direction(Enum): up = 'up' From 351371d20c0b9e014528238761a6eeedf8dfb926 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 24 Aug 2023 20:10:47 +0100 Subject: [PATCH 0213/1617] Fix type arguments validation for variadic instances (#15944) Fixes https://github.com/python/mypy/issues/15410 Fixes https://github.com/python/mypy/issues/15411 --- mypy/expandtype.py | 8 +-- mypy/semanal_typeargs.py | 23 +++++++- mypy/test/testtypes.py | 2 +- mypy/typeanal.py | 8 ++- mypy/types.py | 1 + test-data/unit/check-typevar-tuple.test | 78 +++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 8 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index e71f6429d9c0..dc3dae670c1f 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -409,10 +409,10 @@ def visit_tuple_type(self, t: TupleType) -> Type: # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] item = items[0] if isinstance(item, UnpackType): - assert isinstance(item.type, ProperType) - if isinstance(item.type, Instance): - assert item.type.type.fullname == "builtins.tuple" - return item.type + unpacked = get_proper_type(item.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + return unpacked fallback = t.partial_fallback.accept(self) assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) return t.copy_modified(items=items, fallback=fallback) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 8d8ef66b5c69..1a37ac57be30 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -18,7 +18,7 @@ from mypy.options import Options from mypy.scope import Scope from mypy.subtypes import is_same_type, is_subtype -from mypy.typeanal import set_any_tvars +from mypy.typeanal import fix_type_var_tuple_argument, set_any_tvars from mypy.types import ( AnyType, CallableType, @@ -143,7 +143,26 @@ def visit_instance(self, t: Instance) -> None: if isinstance(info, FakeInfo): return # https://github.com/python/mypy/issues/11079 t.args = tuple(flatten_nested_tuples(t.args)) - # TODO: fix #15410 and #15411. + if t.type.has_type_var_tuple_type: + # Regular Instances are already validated in typeanal.py. + # TODO: do something with partial overlap (probably just reject). + # also in other places where split_with_prefix_and_suffix() is used. + correct = len(t.args) >= len(t.type.type_vars) - 1 + if any( + isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) + for a in t.args + ): + correct = True + if not correct: + exp_len = f"at least {len(t.type.type_vars) - 1}" + self.fail( + f"Bad number of arguments, expected: {exp_len}, given: {len(t.args)}", + t, + code=codes.TYPE_ARG, + ) + any_type = AnyType(TypeOfAny.from_error) + t.args = (any_type,) * len(t.type.type_vars) + fix_type_var_tuple_argument(any_type, t) self.validate_args(info.name, t.args, info.defn.type_vars, t) super().visit_instance(t) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 56ac86058ce4..12e7b207b00a 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1464,7 +1464,7 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 6 + ALLOWED_GET_PROPER_TYPES = 7 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 14b37539afea..806b9967039e 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1795,6 +1795,13 @@ def fix_instance( fix_type_var_tuple_argument(any_type, t) return + + if t.type.has_type_var_tuple_type: + # This can be only correctly analyzed when all arguments are fully + # analyzed, because there may be a variadic item among them, so we + # do this in semanal_typeargs.py. + return + # Invalid number of type parameters. fail( wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name), @@ -1805,7 +1812,6 @@ def fix_instance( # otherwise the type checker may crash as it expects # things to be right. t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars) - fix_type_var_tuple_argument(AnyType(TypeOfAny.from_error), t) t.invalid = True diff --git a/mypy/types.py b/mypy/types.py index c71412f4ea58..214978eab774 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -322,6 +322,7 @@ def _expand_once(self) -> Type: assert isinstance(self.alias.target, Instance) # type: ignore[misc] return self.alias.target.copy_modified(args=self.args) + # TODO: this logic duplicates the one in expand_type_by_instance(). if self.alias.tvar_tuple_index is None: mapping = {v.id: s for (v, s) in zip(self.alias.alias_tvars, self.args)} else: diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 58fc1265ae99..ee81597edadf 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -922,3 +922,81 @@ def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[float, ...]], bool]]) -> None: for x in xs: reveal_type(x) # N: Revealed type is "builtins.float" [builtins fixtures/tuple.pyi] + +[case testFixedUnpackItemInInstanceArguments] +from typing import TypeVar, Callable, Tuple, Generic +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") + +class C(Generic[T, Unpack[Ts], S]): + prefix: T + suffix: S + middle: Tuple[Unpack[Ts]] + +Ints = Tuple[int, int] +c: C[Unpack[Ints]] +reveal_type(c.prefix) # N: Revealed type is "builtins.int" +reveal_type(c.suffix) # N: Revealed type is "builtins.int" +reveal_type(c.middle) # N: Revealed type is "Tuple[()]" +[builtins fixtures/tuple.pyi] + +[case testVariadicUnpackItemInInstanceArguments] +from typing import TypeVar, Callable, Tuple, Generic +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") + +class Other(Generic[Unpack[Ts]]): ... +class C(Generic[T, Unpack[Ts], S]): + prefix: T + suffix: S + x: Tuple[Unpack[Ts]] + y: Callable[[Unpack[Ts]], None] + z: Other[Unpack[Ts]] + +Ints = Tuple[int, ...] +c: C[Unpack[Ints]] +reveal_type(c.prefix) # N: Revealed type is "builtins.int" +reveal_type(c.suffix) # N: Revealed type is "builtins.int" +reveal_type(c.x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(c.y) # N: Revealed type is "def (*builtins.int)" +reveal_type(c.z) # N: Revealed type is "__main__.Other[Unpack[builtins.tuple[builtins.int, ...]]]" +[builtins fixtures/tuple.pyi] + +[case testTooFewItemsInInstanceArguments] +from typing import Generic, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +class C(Generic[T, Unpack[Ts], S]): ... + +c: C[int] # E: Bad number of arguments, expected: at least 2, given: 1 +reveal_type(c) # N: Revealed type is "__main__.C[Any, Unpack[builtins.tuple[Any, ...]], Any]" +[builtins fixtures/tuple.pyi] + +[case testVariadicClassUpperBoundCheck] +from typing import Tuple, TypeVar, Generic +from typing_extensions import Unpack, TypeVarTuple + +class A: ... +class B: ... +class C: ... +class D: ... + +T = TypeVar("T", bound=int) +S = TypeVar("S", bound=str) +Ts = TypeVarTuple("Ts") + +class G(Generic[T, Unpack[Ts], S]): ... +First = Tuple[A, B] +Second = Tuple[C, D] +x: G[Unpack[First], Unpack[Second]] # E: Type argument "A" of "G" must be a subtype of "int" \ + # E: Type argument "D" of "G" must be a subtype of "str" +[builtins fixtures/tuple.pyi] From 9e1f4df133e155f213cf3714bf796bb9e8698907 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 24 Aug 2023 20:11:24 +0100 Subject: [PATCH 0214/1617] Use TypeVar refresh uniformly for class object access (#15945) Fixes https://github.com/python/mypy/issues/15934 I think this is a right thing to do, it may even fix some other rare accidental `TypeVar` clashes not involving self-types. This causes a bit of churn in tests, but not too much. --- mypy/checkmember.py | 4 +-- test-data/unit/check-classes.test | 2 +- test-data/unit/check-incremental.test | 26 +++++++++--------- .../unit/check-parameter-specification.test | 8 +++--- test-data/unit/check-plugin-attrs.test | 16 +++++------ test-data/unit/check-selftype.test | 27 ++++++++++++++++--- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2b0717f181a9..1bdc00a6eb59 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1198,12 +1198,12 @@ class B(A[str]): pass # (i.e. appear in the return type of the class object on which the method was accessed). if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] + t = freshen_all_functions_type_vars(t) if is_classmethod: - t = freshen_all_functions_type_vars(t) t = bind_self(t, original_type, is_classmethod=True) assert isuper is not None t = expand_type_by_instance(t, isuper) - freeze_all_type_vars(t) + freeze_all_type_vars(t) return t.copy_modified(variables=list(tvars) + list(t.variables)) elif isinstance(t, Overloaded): return Overloaded( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index b9e65ef4ad20..04b51bb603c5 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -1162,7 +1162,7 @@ def test() -> None: reveal_type(x) # N: Revealed type is "T`-1" reveal_type(x.returns_int()) # N: Revealed type is "builtins.int" return foo - reveal_type(Foo.bar) # N: Revealed type is "def [T <: __main__.Foo@5] (self: __main__.Foo@5, foo: T`-1) -> T`-1" + reveal_type(Foo.bar) # N: Revealed type is "def [T <: __main__.Foo@5] (self: __main__.Foo@5, foo: T`1) -> T`1" [case testGenericClassWithInvalidTypevarUseWithinFunction] from typing import TypeVar diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 80f5e4e7d12d..fcab0545b982 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3035,10 +3035,10 @@ main:15: error: Unsupported left operand type for >= ("NoCmp") [case testAttrsIncrementalDunder] from a import A reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> a.A" -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" +reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" A(1) < A(2) A(1) <= A(2) @@ -3072,10 +3072,10 @@ class A: [stale] [out2] main:2: note: Revealed type is "def (a: builtins.int) -> a.A" -main:3: note: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -main:4: note: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -main:5: note: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -main:6: note: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" +main:3: note: Revealed type is "def [_AT] (self: _AT`1, other: _AT`1) -> builtins.bool" +main:4: note: Revealed type is "def [_AT] (self: _AT`2, other: _AT`2) -> builtins.bool" +main:5: note: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" +main:6: note: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" main:15: error: Unsupported operand types for < ("A" and "int") main:16: error: Unsupported operand types for <= ("A" and "int") main:17: error: Unsupported operand types for > ("A" and "int") @@ -3963,10 +3963,10 @@ class A: tmp/b.py:3: note: Revealed type is "def (a: builtins.int) -> a.A" tmp/b.py:4: note: Revealed type is "def (builtins.object, builtins.object) -> builtins.bool" tmp/b.py:5: note: Revealed type is "def (builtins.object, builtins.object) -> builtins.bool" -tmp/b.py:6: note: Revealed type is "def [_DT] (self: _DT`-1, other: _DT`-1) -> builtins.bool" -tmp/b.py:7: note: Revealed type is "def [_DT] (self: _DT`-1, other: _DT`-1) -> builtins.bool" -tmp/b.py:8: note: Revealed type is "def [_DT] (self: _DT`-1, other: _DT`-1) -> builtins.bool" -tmp/b.py:9: note: Revealed type is "def [_DT] (self: _DT`-1, other: _DT`-1) -> builtins.bool" +tmp/b.py:6: note: Revealed type is "def [_DT] (self: _DT`1, other: _DT`1) -> builtins.bool" +tmp/b.py:7: note: Revealed type is "def [_DT] (self: _DT`2, other: _DT`2) -> builtins.bool" +tmp/b.py:8: note: Revealed type is "def [_DT] (self: _DT`3, other: _DT`3) -> builtins.bool" +tmp/b.py:9: note: Revealed type is "def [_DT] (self: _DT`4, other: _DT`4) -> builtins.bool" tmp/b.py:18: error: Unsupported operand types for < ("A" and "int") tmp/b.py:19: error: Unsupported operand types for <= ("A" and "int") tmp/b.py:20: error: Unsupported operand types for > ("A" and "int") @@ -6325,7 +6325,7 @@ reveal_type(D.meth) reveal_type(D().meth) [out] [out2] -tmp/m.py:4: note: Revealed type is "def [Self <: lib.C] (self: Self`0, other: Self`0) -> Self`0" +tmp/m.py:4: note: Revealed type is "def [Self <: lib.C] (self: Self`1, other: Self`1) -> Self`1" tmp/m.py:5: note: Revealed type is "def (other: m.D) -> m.D" [case testIncrementalNestedGenericCallableCrash] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index dee8a971f925..dba73974aef6 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -901,8 +901,8 @@ class A: def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`-2, *_P.args, **_P.kwargs) -> _R`-2" -reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`5, *_P.args, **_P.kwargs) -> _R`5" +reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`3, *_P.args, **_P.kwargs) -> _R`3" +reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`7, *_P.args, **_P.kwargs) -> _R`7" def f(x: int) -> int: ... @@ -934,8 +934,8 @@ class A: def func(self, action: Job[_P, None]) -> Job[_P, None]: ... -reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`-1, None]) -> __main__.Job[_P`-1, None]" -reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]" +reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`2, None]) -> __main__.Job[_P`2, None]" +reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`4, None]) -> __main__.Job[_P`4, None]" reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" def f(x: int, y: int) -> None: ... diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 3534d206c060..7580531bebc9 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -185,10 +185,10 @@ from attr import attrib, attrs class A: a: int reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> __main__.A" -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" +reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" A(1) < A(2) A(1) <= A(2) @@ -989,10 +989,10 @@ class C(A, B): pass @attr.s class D(A): pass -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" -reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" +reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`8, other: _AT`8) -> builtins.bool" A() < A() B() < B() diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 77d2d519214a..d5024412ca97 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1484,7 +1484,7 @@ class C: return self class D(C): ... -reveal_type(C.meth) # N: Revealed type is "def [Self <: __main__.C] (self: Self`0) -> builtins.list[Self`0]" +reveal_type(C.meth) # N: Revealed type is "def [Self <: __main__.C] (self: Self`1) -> builtins.list[Self`1]" C.attr # E: Access to generic instance variables via class is ambiguous reveal_type(D().meth()) # N: Revealed type is "builtins.list[__main__.D]" reveal_type(D().attr) # N: Revealed type is "builtins.list[__main__.D]" @@ -1793,7 +1793,7 @@ class C: def bar(self) -> Self: ... def foo(self, x: S) -> Tuple[Self, S]: ... -reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`0, x: S`-1) -> Tuple[Self`0, S`-1]" +reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> Tuple[Self`1, S`2]" reveal_type(C().foo(42)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" [builtins fixtures/tuple.pyi] @@ -1903,7 +1903,7 @@ class C: class D(C): ... -reveal_type(D.f) # N: Revealed type is "def [T] (T`-1) -> T`-1" +reveal_type(D.f) # N: Revealed type is "def [T] (T`1) -> T`1" reveal_type(D().f) # N: Revealed type is "def () -> __main__.D" [case testTypingSelfOnSuperTypeVarValues] @@ -2015,3 +2015,24 @@ class Add(Async): reveal_type(Add.async_func()) # N: Revealed type is "def (x: builtins.int, y: builtins.int) -> builtins.int" reveal_type(Add().async_func()) # N: Revealed type is "def (x: builtins.int, y: builtins.int) -> builtins.int" [builtins fixtures/classmethod.pyi] + +[case testSelfTypeMethodOnClassObject] +from typing import Self + +class Object: # Needed to mimic object in typeshed + ref: Self + +class Foo: + def foo(self) -> Self: + return self + +class Ben(Object): + MY_MAP = { + "foo": Foo.foo, + } + @classmethod + def doit(cls) -> Foo: + reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]" + foo_method = cls.MY_MAP["foo"] + return foo_method(Foo()) +[builtins fixtures/isinstancelist.pyi] From f9b1db6519cd88a081e8b8597240e166eb513245 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Aug 2023 21:41:25 +0100 Subject: [PATCH 0215/1617] Fix crash on invalid type variable with ParamSpec (#15953) Fixes https://github.com/python/mypy/issues/15948 The fix is straightforward: invalid type variable resulted in applying type arguments packing/simplification when we shouldn't. Making the latter more strict fixes the issue. --------- Co-authored-by: Jelle Zijlstra --- mypy/typeanal.py | 37 +++++++++++++++---- .../unit/check-parameter-specification.test | 23 ++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 806b9967039e..e29cca09be63 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -458,11 +458,30 @@ def pack_paramspec_args(self, an_args: Sequence[Type]) -> list[Type]: # These do not support mypy_extensions VarArgs, etc. as they were already analyzed # TODO: should these be re-analyzed to get rid of this inconsistency? count = len(an_args) - if count > 0: - first_arg = get_proper_type(an_args[0]) - if not (count == 1 and isinstance(first_arg, (Parameters, ParamSpecType, AnyType))): - return [Parameters(an_args, [ARG_POS] * count, [None] * count)] - return list(an_args) + if count == 0: + return [] + if count == 1 and isinstance(get_proper_type(an_args[0]), AnyType): + # Single Any is interpreted as ..., rather that a single argument with Any type. + # I didn't find this in the PEP, but it sounds reasonable. + return list(an_args) + if any(isinstance(a, (Parameters, ParamSpecType)) for a in an_args): + if len(an_args) > 1: + first_wrong = next( + arg for arg in an_args if isinstance(arg, (Parameters, ParamSpecType)) + ) + self.fail( + "Nested parameter specifications are not allowed", + first_wrong, + code=codes.VALID_TYPE, + ) + return [AnyType(TypeOfAny.from_error)] + return list(an_args) + first = an_args[0] + return [ + Parameters( + an_args, [ARG_POS] * count, [None] * count, line=first.line, column=first.column + ) + ] def cannot_resolve_type(self, t: UnboundType) -> None: # TODO: Move error message generation to messages.py. We'd first @@ -503,7 +522,11 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type: names: list[str | None] = [None] * len(args) pre = Parameters( - args + pre.arg_types, [ARG_POS] * len(args) + pre.arg_kinds, names + pre.arg_names + args + pre.arg_types, + [ARG_POS] * len(args) + pre.arg_kinds, + names + pre.arg_names, + line=t.line, + column=t.column, ) return ps.copy_modified(prefix=pre) if isinstance(ps, ParamSpecType) else pre @@ -913,7 +936,7 @@ def visit_type_list(self, t: TypeList) -> Type: if params: ts, kinds, names = params # bind these types - return Parameters(self.anal_array(ts), kinds, names) + return Parameters(self.anal_array(ts), kinds, names, line=t.line, column=t.column) else: return AnyType(TypeOfAny.from_error) else: diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index dba73974aef6..257fb9241373 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1741,3 +1741,26 @@ def bar(x): ... reveal_type(bar) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.float, def (x: builtins.str) -> builtins.str)" [builtins fixtures/paramspec.pyi] + +[case testParamSpecDecoratorOverloadNoCrashOnInvalidTypeVar] +from typing import Any, Callable, List +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = 1 + +Alias = Callable[P, List[T]] # type: ignore +def dec(fn: Callable[P, T]) -> Alias[P, T]: ... # type: ignore +f: Any +dec(f) # No crash +[builtins fixtures/paramspec.pyi] + +[case testParamSpecErrorNestedParams] +from typing import Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") +class C(Generic[P]): ... +c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed +reveal_type(c) # N: Revealed type is "__main__.C[Any]" +[builtins fixtures/paramspec.pyi] From 7f65cc7570eaa4206ae086680e1c1d0489897efa Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 25 Aug 2023 22:30:39 +0100 Subject: [PATCH 0216/1617] Infer ParamSpec constraint from arguments (#15896) Fixes https://github.com/python/mypy/issues/12278 Fixes https://github.com/python/mypy/issues/13191 (more tricky nested use cases with optional/keyword args still don't work, but they are quite tricky to fix and may selectively fixed later) This unfortunately requires some special-casing, here is its summary: * If actual argument for `Callable[P, T]` is non-generic and non-lambda, do not put it into inference second pass. * If we are able to infer constraints for `P` without using arguments mapped to `*args: P.args` etc., do not add the constraint for `P` vs those arguments (this applies to both top-level callable constraints, and for nested callable constraints against callables that are known to have imprecise argument kinds). (Btw TODO I added is not related to this PR, I just noticed something obviously wrong) --- mypy/checkexpr.py | 41 +++++- mypy/constraints.py | 136 +++++++++++++----- mypy/expandtype.py | 2 + mypy/infer.py | 3 +- mypy/types.py | 22 +++ .../unit/check-parameter-specification.test | 82 +++++++++-- test-data/unit/fixtures/paramspec.pyi | 3 +- test-data/unit/typexport-basic.test | 24 ++-- 8 files changed, 244 insertions(+), 69 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 6de317f587cb..4430d0773cfa 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1987,7 +1987,7 @@ def infer_function_type_arguments( ) arg_pass_nums = self.get_arg_infer_passes( - callee_type.arg_types, formal_to_actual, len(args) + callee_type, args, arg_types, formal_to_actual, len(args) ) pass1_args: list[Type | None] = [] @@ -2001,6 +2001,7 @@ def infer_function_type_arguments( callee_type, pass1_args, arg_kinds, + arg_names, formal_to_actual, context=self.argument_infer_context(), strict=self.chk.in_checked_function(), @@ -2061,6 +2062,7 @@ def infer_function_type_arguments( callee_type, arg_types, arg_kinds, + arg_names, formal_to_actual, context=self.argument_infer_context(), strict=self.chk.in_checked_function(), @@ -2140,6 +2142,7 @@ def infer_function_type_arguments_pass2( callee_type, arg_types, arg_kinds, + arg_names, formal_to_actual, context=self.argument_infer_context(), ) @@ -2152,7 +2155,12 @@ def argument_infer_context(self) -> ArgumentInferContext: ) def get_arg_infer_passes( - self, arg_types: list[Type], formal_to_actual: list[list[int]], num_actuals: int + self, + callee: CallableType, + args: list[Expression], + arg_types: list[Type], + formal_to_actual: list[list[int]], + num_actuals: int, ) -> list[int]: """Return pass numbers for args for two-pass argument type inference. @@ -2163,8 +2171,28 @@ def get_arg_infer_passes( lambdas more effectively. """ res = [1] * num_actuals - for i, arg in enumerate(arg_types): - if arg.accept(ArgInferSecondPassQuery()): + for i, arg in enumerate(callee.arg_types): + skip_param_spec = False + p_formal = get_proper_type(callee.arg_types[i]) + if isinstance(p_formal, CallableType) and p_formal.param_spec(): + for j in formal_to_actual[i]: + p_actual = get_proper_type(arg_types[j]) + # This is an exception from the usual logic where we put generic Callable + # arguments in the second pass. If we have a non-generic actual, it is + # likely to infer good constraints, for example if we have: + # def run(Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + # def test(x: int, y: int) -> int: ... + # run(test, 1, 2) + # we will use `test` for inference, since it will allow to infer also + # argument *names* for P <: [x: int, y: int]. + if ( + isinstance(p_actual, CallableType) + and not p_actual.variables + and not isinstance(args[j], LambdaExpr) + ): + skip_param_spec = True + break + if not skip_param_spec and arg.accept(ArgInferSecondPassQuery()): for j in formal_to_actual[i]: res[j] = 2 return res @@ -4903,7 +4931,9 @@ def infer_lambda_type_using_context( self.chk.fail(message_registry.CANNOT_INFER_LAMBDA_TYPE, e) return None, None - return callable_ctx, callable_ctx + # Type of lambda must have correct argument names, to prevent false + # negatives when lambdas appear in `ParamSpec` context. + return callable_ctx.copy_modified(arg_names=e.arg_names), callable_ctx def visit_super_expr(self, e: SuperExpr) -> Type: """Type check a super expression (non-lvalue).""" @@ -5921,6 +5951,7 @@ def __init__(self) -> None: super().__init__(types.ANY_STRATEGY) def visit_callable_type(self, t: CallableType) -> bool: + # TODO: we need to check only for type variables of original callable. return self.query_types(t.arg_types) or t.accept(HasTypeVarQuery()) diff --git a/mypy/constraints.py b/mypy/constraints.py index edce11e778ab..0e59b5459fd4 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -108,6 +108,7 @@ def infer_constraints_for_callable( callee: CallableType, arg_types: Sequence[Type | None], arg_kinds: list[ArgKind], + arg_names: Sequence[str | None] | None, formal_to_actual: list[list[int]], context: ArgumentInferContext, ) -> list[Constraint]: @@ -118,6 +119,20 @@ def infer_constraints_for_callable( constraints: list[Constraint] = [] mapper = ArgTypeExpander(context) + param_spec = callee.param_spec() + param_spec_arg_types = [] + param_spec_arg_names = [] + param_spec_arg_kinds = [] + + incomplete_star_mapping = False + for i, actuals in enumerate(formal_to_actual): + for actual in actuals: + if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): + # We can't use arguments to infer ParamSpec constraint, if only some + # are present in the current inference pass. + incomplete_star_mapping = True + break + for i, actuals in enumerate(formal_to_actual): if isinstance(callee.arg_types[i], UnpackType): unpack_type = callee.arg_types[i] @@ -194,11 +209,47 @@ def infer_constraints_for_callable( actual_type = mapper.expand_actual_type( actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] ) - # TODO: if callee has ParamSpec, we need to collect all actuals that map to star - # args and create single constraint between P and resulting Parameters instead. - c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF) - constraints.extend(c) - + if ( + param_spec + and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2) + and not incomplete_star_mapping + ): + # If actual arguments are mapped to ParamSpec type, we can't infer individual + # constraints, instead store them and infer single constraint at the end. + # It is impossible to map actual kind to formal kind, so use some heuristic. + # This inference is used as a fallback, so relying on heuristic should be OK. + param_spec_arg_types.append( + mapper.expand_actual_type( + actual_arg_type, arg_kinds[actual], None, arg_kinds[actual] + ) + ) + actual_kind = arg_kinds[actual] + param_spec_arg_kinds.append( + ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind + ) + param_spec_arg_names.append(arg_names[actual] if arg_names else None) + else: + c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF) + constraints.extend(c) + if ( + param_spec + and not any(c.type_var == param_spec.id for c in constraints) + and not incomplete_star_mapping + ): + # Use ParamSpec constraint from arguments only if there are no other constraints, + # since as explained above it is quite ad-hoc. + constraints.append( + Constraint( + param_spec, + SUPERTYPE_OF, + Parameters( + arg_types=param_spec_arg_types, + arg_kinds=param_spec_arg_kinds, + arg_names=param_spec_arg_names, + imprecise_arg_kinds=True, + ), + ) + ) return constraints @@ -949,6 +1000,14 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: res: list[Constraint] = [] cactual = self.actual.with_unpacked_kwargs() param_spec = template.param_spec() + + template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type + if template.type_guard is not None: + template_ret_type = template.type_guard + if cactual.type_guard is not None: + cactual_ret_type = cactual.type_guard + res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) + if param_spec is None: # TODO: Erase template variables if it is generic? if ( @@ -1008,51 +1067,50 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) extra_tvars = True + # Compare prefixes as well + cactual_prefix = cactual.copy_modified( + arg_types=cactual.arg_types[:prefix_len], + arg_kinds=cactual.arg_kinds[:prefix_len], + arg_names=cactual.arg_names[:prefix_len], + ) + res.extend( + infer_callable_arguments_constraints(prefix, cactual_prefix, self.direction) + ) + + param_spec_target: Type | None = None + skip_imprecise = ( + any(c.type_var == param_spec.id for c in res) and cactual.imprecise_arg_kinds + ) if not cactual_ps: max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]) prefix_len = min(prefix_len, max_prefix_len) - res.append( - Constraint( - param_spec, - neg_op(self.direction), - Parameters( - arg_types=cactual.arg_types[prefix_len:], - arg_kinds=cactual.arg_kinds[prefix_len:], - arg_names=cactual.arg_names[prefix_len:], - variables=cactual.variables - if not type_state.infer_polymorphic - else [], - ), + # This logic matches top-level callable constraint exception, if we managed + # to get other constraints for ParamSpec, don't infer one with imprecise kinds + if not skip_imprecise: + param_spec_target = Parameters( + arg_types=cactual.arg_types[prefix_len:], + arg_kinds=cactual.arg_kinds[prefix_len:], + arg_names=cactual.arg_names[prefix_len:], + variables=cactual.variables + if not type_state.infer_polymorphic + else [], + imprecise_arg_kinds=cactual.imprecise_arg_kinds, ) - ) else: - if len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types): - cactual_ps = cactual_ps.copy_modified( + if ( + len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types) + and not skip_imprecise + ): + param_spec_target = cactual_ps.copy_modified( prefix=Parameters( arg_types=cactual_ps.prefix.arg_types[prefix_len:], arg_kinds=cactual_ps.prefix.arg_kinds[prefix_len:], arg_names=cactual_ps.prefix.arg_names[prefix_len:], + imprecise_arg_kinds=cactual_ps.prefix.imprecise_arg_kinds, ) ) - res.append(Constraint(param_spec, neg_op(self.direction), cactual_ps)) - - # Compare prefixes as well - cactual_prefix = cactual.copy_modified( - arg_types=cactual.arg_types[:prefix_len], - arg_kinds=cactual.arg_kinds[:prefix_len], - arg_names=cactual.arg_names[:prefix_len], - ) - res.extend( - infer_callable_arguments_constraints(prefix, cactual_prefix, self.direction) - ) - - template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type - if template.type_guard is not None: - template_ret_type = template.type_guard - if cactual.type_guard is not None: - cactual_ret_type = cactual.type_guard - - res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) + if param_spec_target is not None: + res.append(Constraint(param_spec, neg_op(self.direction), param_spec_target)) if extra_tvars: for c in res: c.extra_tvars += cactual.variables diff --git a/mypy/expandtype.py b/mypy/expandtype.py index dc3dae670c1f..7168d7c30b0d 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -336,6 +336,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: arg_types=self.expand_types(t.arg_types), ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), + imprecise_arg_kinds=(t.imprecise_arg_kinds or repl.imprecise_arg_kinds), ) elif isinstance(repl, ParamSpecType): # We're substituting one ParamSpec for another; this can mean that the prefix @@ -352,6 +353,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: arg_names=t.arg_names[:-2] + prefix.arg_names + t.arg_names[-2:], ret_type=t.ret_type.accept(self), from_concatenate=t.from_concatenate or bool(repl.prefix.arg_types), + imprecise_arg_kinds=(t.imprecise_arg_kinds or prefix.imprecise_arg_kinds), ) var_arg = t.var_arg() diff --git a/mypy/infer.py b/mypy/infer.py index f34087910e4b..ba4a1d2bc9b1 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -33,6 +33,7 @@ def infer_function_type_arguments( callee_type: CallableType, arg_types: Sequence[Type | None], arg_kinds: list[ArgKind], + arg_names: Sequence[str | None] | None, formal_to_actual: list[list[int]], context: ArgumentInferContext, strict: bool = True, @@ -53,7 +54,7 @@ def infer_function_type_arguments( """ # Infer constraints. constraints = infer_constraints_for_callable( - callee_type, arg_types, arg_kinds, formal_to_actual, context + callee_type, arg_types, arg_kinds, arg_names, formal_to_actual, context ) # Solve constraints. diff --git a/mypy/types.py b/mypy/types.py index 214978eab774..cf2c343655dd 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1560,6 +1560,7 @@ class Parameters(ProperType): # TODO: variables don't really belong here, but they are used to allow hacky support # for forall . Foo[[x: T], T] by capturing generic callable with ParamSpec, see #15909 "variables", + "imprecise_arg_kinds", ) def __init__( @@ -1570,6 +1571,7 @@ def __init__( *, variables: Sequence[TypeVarLikeType] | None = None, is_ellipsis_args: bool = False, + imprecise_arg_kinds: bool = False, line: int = -1, column: int = -1, ) -> None: @@ -1582,6 +1584,7 @@ def __init__( self.min_args = arg_kinds.count(ARG_POS) self.is_ellipsis_args = is_ellipsis_args self.variables = variables or [] + self.imprecise_arg_kinds = imprecise_arg_kinds def copy_modified( self, @@ -1591,6 +1594,7 @@ def copy_modified( *, variables: Bogus[Sequence[TypeVarLikeType]] = _dummy, is_ellipsis_args: Bogus[bool] = _dummy, + imprecise_arg_kinds: Bogus[bool] = _dummy, ) -> Parameters: return Parameters( arg_types=arg_types if arg_types is not _dummy else self.arg_types, @@ -1600,6 +1604,11 @@ def copy_modified( is_ellipsis_args if is_ellipsis_args is not _dummy else self.is_ellipsis_args ), variables=variables if variables is not _dummy else self.variables, + imprecise_arg_kinds=( + imprecise_arg_kinds + if imprecise_arg_kinds is not _dummy + else self.imprecise_arg_kinds + ), ) # TODO: here is a lot of code duplication with Callable type, fix this. @@ -1696,6 +1705,7 @@ def serialize(self) -> JsonDict: "arg_kinds": [int(x.value) for x in self.arg_kinds], "arg_names": self.arg_names, "variables": [tv.serialize() for tv in self.variables], + "imprecise_arg_kinds": self.imprecise_arg_kinds, } @classmethod @@ -1706,6 +1716,7 @@ def deserialize(cls, data: JsonDict) -> Parameters: [ArgKind(x) for x in data["arg_kinds"]], data["arg_names"], variables=[cast(TypeVarLikeType, deserialize_type(v)) for v in data["variables"]], + imprecise_arg_kinds=data["imprecise_arg_kinds"], ) def __hash__(self) -> int: @@ -1762,6 +1773,7 @@ class CallableType(FunctionLike): "type_guard", # T, if -> TypeGuard[T] (ret_type is bool in this case). "from_concatenate", # whether this callable is from a concatenate object # (this is used for error messages) + "imprecise_arg_kinds", "unpack_kwargs", # Was an Unpack[...] with **kwargs used to define this callable? ) @@ -1786,6 +1798,7 @@ def __init__( def_extras: dict[str, Any] | None = None, type_guard: Type | None = None, from_concatenate: bool = False, + imprecise_arg_kinds: bool = False, unpack_kwargs: bool = False, ) -> None: super().__init__(line, column) @@ -1812,6 +1825,7 @@ def __init__( self.special_sig = special_sig self.from_type_type = from_type_type self.from_concatenate = from_concatenate + self.imprecise_arg_kinds = imprecise_arg_kinds if not bound_args: bound_args = () self.bound_args = bound_args @@ -1854,6 +1868,7 @@ def copy_modified( def_extras: Bogus[dict[str, Any]] = _dummy, type_guard: Bogus[Type | None] = _dummy, from_concatenate: Bogus[bool] = _dummy, + imprecise_arg_kinds: Bogus[bool] = _dummy, unpack_kwargs: Bogus[bool] = _dummy, ) -> CT: modified = CallableType( @@ -1879,6 +1894,11 @@ def copy_modified( from_concatenate=( from_concatenate if from_concatenate is not _dummy else self.from_concatenate ), + imprecise_arg_kinds=( + imprecise_arg_kinds + if imprecise_arg_kinds is not _dummy + else self.imprecise_arg_kinds + ), unpack_kwargs=unpack_kwargs if unpack_kwargs is not _dummy else self.unpack_kwargs, ) # Optimization: Only NewTypes are supported as subtypes since @@ -2191,6 +2211,7 @@ def serialize(self) -> JsonDict: "def_extras": dict(self.def_extras), "type_guard": self.type_guard.serialize() if self.type_guard is not None else None, "from_concatenate": self.from_concatenate, + "imprecise_arg_kinds": self.imprecise_arg_kinds, "unpack_kwargs": self.unpack_kwargs, } @@ -2214,6 +2235,7 @@ def deserialize(cls, data: JsonDict) -> CallableType: deserialize_type(data["type_guard"]) if data["type_guard"] is not None else None ), from_concatenate=data["from_concatenate"], + imprecise_arg_kinds=data["imprecise_arg_kinds"], unpack_kwargs=data["unpack_kwargs"], ) diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 257fb9241373..ed1d59b376d2 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -239,7 +239,6 @@ reveal_type(f(g, 1, y='x')) # N: Revealed type is "None" f(g, 'x', y='x') # E: Argument 2 to "f" has incompatible type "str"; expected "int" f(g, 1, y=1) # E: Argument "y" to "f" has incompatible type "int"; expected "str" f(g) # E: Missing positional arguments "x", "y" in call to "f" - [builtins fixtures/dict.pyi] [case testParamSpecSpecialCase] @@ -415,14 +414,19 @@ P = ParamSpec('P') T = TypeVar('T') # Similar to atexit.register -def register(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[P, T]: ... # N: "register" defined here +def register(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[P, T]: ... def f(x: int) -> None: pass +def g(x: int, y: str) -> None: pass reveal_type(register(lambda: f(1))) # N: Revealed type is "def ()" -reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (x: Any)" -register(lambda x: f(x)) # E: Missing positional argument "x" in call to "register" -register(lambda x: f(x), y=1) # E: Unexpected keyword argument "y" for "register" +reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (x: Literal[1]?)" +register(lambda x: f(x)) # E: Cannot infer type of lambda \ + # E: Argument 1 to "register" has incompatible type "Callable[[Any], None]"; expected "Callable[[], None]" +register(lambda x: f(x), y=1) # E: Argument 1 to "register" has incompatible type "Callable[[Arg(int, 'x')], None]"; expected "Callable[[Arg(int, 'y')], None]" +reveal_type(register(lambda x: f(x), 1)) # N: Revealed type is "def (Literal[1]?)" +reveal_type(register(lambda x, y: g(x, y), 1, "a")) # N: Revealed type is "def (Literal[1]?, Literal['a']?)" +reveal_type(register(lambda x, y: g(x, y), 1, y="a")) # N: Revealed type is "def (Literal[1]?, y: Literal['a']?)" [builtins fixtures/dict.pyi] [case testParamSpecInvalidCalls] @@ -909,8 +913,7 @@ def f(x: int) -> int: reveal_type(A().func(f, 42)) # N: Revealed type is "builtins.int" -# TODO: this should reveal `int` -reveal_type(A().func(lambda x: x + x, 42)) # N: Revealed type is "Any" +reveal_type(A().func(lambda x: x + x, 42)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] [case testParamSpecConstraintOnOtherParamSpec] @@ -1355,7 +1358,6 @@ P = ParamSpec('P') class Some(Generic[P]): def call(self, *args: P.args, **kwargs: P.kwargs): ... -# TODO: this probably should be reported. def call(*args: P.args, **kwargs: P.kwargs): ... [builtins fixtures/paramspec.pyi] @@ -1631,7 +1633,41 @@ dec(test_with_bound)(0) # E: Value of type variable "T" of function cannot be " dec(test_with_bound)(A()) # OK [builtins fixtures/paramspec.pyi] +[case testParamSpecArgumentParamInferenceRegular] +from typing import TypeVar, Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") +class Foo(Generic[P]): + def call(self, *args: P.args, **kwargs: P.kwargs) -> None: ... +def test(*args: P.args, **kwargs: P.kwargs) -> Foo[P]: ... + +reveal_type(test(1, 2)) # N: Revealed type is "__main__.Foo[[Literal[1]?, Literal[2]?]]" +reveal_type(test(x=1, y=2)) # N: Revealed type is "__main__.Foo[[x: Literal[1]?, y: Literal[2]?]]" +ints = [1, 2, 3] +reveal_type(test(*ints)) # N: Revealed type is "__main__.Foo[[*builtins.int]]" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecArgumentParamInferenceGeneric] +# flags: --new-type-inference +from typing import Callable, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +R = TypeVar("R") +def call(f: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: + return f(*args, **kwargs) + +T = TypeVar("T") +def identity(x: T) -> T: + return x + +reveal_type(call(identity, 2)) # N: Revealed type is "builtins.int" +y: int = call(identity, 2) +[builtins fixtures/paramspec.pyi] + [case testParamSpecNestedApplyNoCrash] +# flags: --new-type-inference from typing import Callable, TypeVar from typing_extensions import ParamSpec @@ -1639,9 +1675,33 @@ P = ParamSpec("P") T = TypeVar("T") def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ... -def test() -> None: ... -# TODO: avoid this error, although it may be non-trivial. -apply(apply, test) # E: Argument 2 to "apply" has incompatible type "Callable[[], None]"; expected "Callable[P, T]" +def test() -> int: ... +reveal_type(apply(apply, test)) # N: Revealed type is "builtins.int" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecNestedApplyPosVsNamed] +from typing import Callable, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") + +def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> None: ... +def test(x: int) -> int: ... +apply(apply, test, x=42) # OK +apply(apply, test, 42) # Also OK (but requires some special casing) +[builtins fixtures/paramspec.pyi] + +[case testParamSpecApplyPosVsNamedOptional] +from typing import Callable, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec("P") +T = TypeVar("T") + +def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> None: ... +def test(x: str = ..., y: int = ...) -> int: ... +apply(test, y=42) # OK [builtins fixtures/paramspec.pyi] [case testParamSpecPrefixSubtypingGenericInvalid] diff --git a/test-data/unit/fixtures/paramspec.pyi b/test-data/unit/fixtures/paramspec.pyi index 5e4b8564e238..9b0089f6a7e9 100644 --- a/test-data/unit/fixtures/paramspec.pyi +++ b/test-data/unit/fixtures/paramspec.pyi @@ -30,7 +30,8 @@ class list(Sequence[T], Generic[T]): def __iter__(self) -> Iterator[T]: ... class int: - def __neg__(self) -> 'int': ... + def __neg__(self) -> int: ... + def __add__(self, other: int) -> int: ... class bool(int): ... class float: ... diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index cd2afe2c1c75..c4c3a1d36f83 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -727,7 +727,7 @@ class A: pass class B: a = None # type: A [out] -LambdaExpr(2) : def (B) -> A +LambdaExpr(2) : def (x: B) -> A MemberExpr(2) : A NameExpr(2) : B @@ -756,7 +756,7 @@ class B: a = None # type: A [builtins fixtures/list.pyi] [out] -LambdaExpr(2) : def (B) -> builtins.list[A] +LambdaExpr(2) : def (x: B) -> builtins.list[A] ListExpr(2) : builtins.list[A] [case testLambdaAndHigherOrderFunction] @@ -775,7 +775,7 @@ map( CallExpr(9) : builtins.list[B] NameExpr(9) : def (f: def (A) -> B, a: builtins.list[A]) -> builtins.list[B] CallExpr(10) : B -LambdaExpr(10) : def (A) -> B +LambdaExpr(10) : def (x: A) -> B NameExpr(10) : def (a: A) -> B NameExpr(10) : builtins.list[A] NameExpr(10) : A @@ -795,7 +795,7 @@ map( [builtins fixtures/list.pyi] [out] NameExpr(10) : def (f: def (A) -> builtins.list[B], a: builtins.list[A]) -> builtins.list[B] -LambdaExpr(11) : def (A) -> builtins.list[B] +LambdaExpr(11) : def (x: A) -> builtins.list[B] ListExpr(11) : builtins.list[B] NameExpr(11) : def (a: A) -> B NameExpr(11) : builtins.list[A] @@ -817,7 +817,7 @@ map( -- context. Perhaps just fail instead? CallExpr(7) : builtins.list[Any] NameExpr(7) : def (f: builtins.list[def (A) -> Any], a: builtins.list[A]) -> builtins.list[Any] -LambdaExpr(8) : def (A) -> A +LambdaExpr(8) : def (x: A) -> A ListExpr(8) : builtins.list[def (A) -> Any] NameExpr(8) : A NameExpr(9) : builtins.list[A] @@ -838,7 +838,7 @@ map( [out] CallExpr(9) : builtins.list[B] NameExpr(9) : def (f: def (A) -> B, a: builtins.list[A]) -> builtins.list[B] -LambdaExpr(10) : def (A) -> B +LambdaExpr(10) : def (x: A) -> B MemberExpr(10) : B NameExpr(10) : A NameExpr(11) : builtins.list[A] @@ -860,7 +860,7 @@ map( CallExpr(9) : builtins.list[B] NameExpr(9) : def (f: def (A) -> B, a: builtins.list[A]) -> builtins.list[B] NameExpr(10) : builtins.list[A] -LambdaExpr(11) : def (A) -> B +LambdaExpr(11) : def (x: A) -> B MemberExpr(11) : B NameExpr(11) : A @@ -1212,7 +1212,7 @@ f( [builtins fixtures/list.pyi] [out] NameExpr(8) : Overload(def (x: builtins.int, f: def (builtins.int) -> builtins.int), def (x: builtins.str, f: def (builtins.str) -> builtins.str)) -LambdaExpr(9) : def (builtins.int) -> builtins.int +LambdaExpr(9) : def (x: builtins.int) -> builtins.int NameExpr(9) : builtins.int [case testExportOverloadArgTypeNested] @@ -1231,10 +1231,10 @@ f( lambda x: x) [builtins fixtures/list.pyi] [out] -LambdaExpr(9) : def (builtins.int) -> builtins.int -LambdaExpr(10) : def (builtins.int) -> builtins.int -LambdaExpr(12) : def (builtins.str) -> builtins.str -LambdaExpr(13) : def (builtins.str) -> builtins.str +LambdaExpr(9) : def (y: builtins.int) -> builtins.int +LambdaExpr(10) : def (x: builtins.int) -> builtins.int +LambdaExpr(12) : def (y: builtins.str) -> builtins.str +LambdaExpr(13) : def (x: builtins.str) -> builtins.str -- TODO -- From 29abf398d6a9e88e899df8a1941019105821f9f0 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 26 Aug 2023 21:30:13 +0100 Subject: [PATCH 0217/1617] Support PEP 646 syntax for Callable (#15951) Fixes https://github.com/python/mypy/issues/15412 Two new things here as specified by PEP 646: * Using star for an (explicit) type unpaking in callables, like `Callable[[str, *tuple[int, ...]], None]` * Allowing suffix items after a variadic item, like `Callable[[X, Unpack[Ys], Z], bool]` Implementation is straightforward. Btw while working in this I accidentally fixed a nasty bug, tuple types were often not given any line/column numbers, so if such type becomes a location of an error, it is impossible to ignore. --- mypy/exprtotype.py | 10 +++- mypy/fastparse.py | 14 ++++- mypy/typeanal.py | 73 +++++++++++++++++++------ test-data/unit/check-typevar-tuple.test | 58 +++++++++++++++----- 4 files changed, 123 insertions(+), 32 deletions(-) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index bbc284a5188a..b82d35607ef1 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -17,6 +17,7 @@ NameExpr, OpExpr, RefExpr, + StarExpr, StrExpr, TupleExpr, UnaryExpr, @@ -35,6 +36,7 @@ TypeOfAny, UnboundType, UnionType, + UnpackType, ) @@ -56,6 +58,7 @@ def expr_to_unanalyzed_type( options: Options | None = None, allow_new_syntax: bool = False, _parent: Expression | None = None, + allow_unpack: bool = False, ) -> ProperType: """Translate an expression to the corresponding type. @@ -163,7 +166,10 @@ def expr_to_unanalyzed_type( return CallableArgument(typ, name, arg_const, expr.line, expr.column) elif isinstance(expr, ListExpr): return TypeList( - [expr_to_unanalyzed_type(t, options, allow_new_syntax, expr) for t in expr.items], + [ + expr_to_unanalyzed_type(t, options, allow_new_syntax, expr, allow_unpack=True) + for t in expr.items + ], line=expr.line, column=expr.column, ) @@ -189,5 +195,7 @@ def expr_to_unanalyzed_type( return RawExpressionType(None, "builtins.complex", line=expr.line, column=expr.column) elif isinstance(expr, EllipsisExpr): return EllipsisType(expr.line) + elif allow_unpack and isinstance(expr, StarExpr): + return UnpackType(expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax)) else: raise TypeTranslationError() diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 3a26cfe7d6ff..6aa626afb81e 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -115,6 +115,7 @@ TypeOfAny, UnboundType, UnionType, + UnpackType, ) from mypy.util import bytes_to_human_readable_repr, unnamed_function @@ -1730,6 +1731,7 @@ def __init__( self.override_column = override_column self.node_stack: list[AST] = [] self.is_evaluated = is_evaluated + self.allow_unpack = False def convert_column(self, column: int) -> int: """Apply column override if defined; otherwise return column. @@ -2006,10 +2008,20 @@ def visit_Attribute(self, n: Attribute) -> Type: else: return self.invalid_type(n) + # Used for Callable[[X *Ys, Z], R] + def visit_Starred(self, n: ast3.Starred) -> Type: + return UnpackType(self.visit(n.value)) + # List(expr* elts, expr_context ctx) def visit_List(self, n: ast3.List) -> Type: assert isinstance(n.ctx, ast3.Load) - return self.translate_argument_list(n.elts) + old_allow_unpack = self.allow_unpack + # We specifically only allow starred expressions in a list to avoid + # confusing errors for top-level unpacks (e.g. in base classes). + self.allow_unpack = True + result = self.translate_argument_list(n.elts) + self.allow_unpack = old_allow_unpack + return result def stringify_name(n: AST) -> str | None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index e29cca09be63..1955d2bc3c43 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -568,7 +568,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ instance = self.named_type("builtins.tuple", [self.anal_type(t.args[0])]) instance.line = t.line return instance - return self.tuple_type(self.anal_array(t.args, allow_unpack=True)) + return self.tuple_type( + self.anal_array(t.args, allow_unpack=True), line=t.line, column=t.column + ) elif fullname == "typing.Union": items = self.anal_array(t.args) return UnionType.make_union(items) @@ -968,7 +970,10 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: return t def visit_unpack_type(self, t: UnpackType) -> Type: - raise NotImplementedError + if not self.allow_unpack: + self.fail(message_registry.INVALID_UNPACK_POSITION, t.type, code=codes.VALID_TYPE) + return AnyType(TypeOfAny.from_error) + return UnpackType(self.anal_type(t.type)) def visit_parameters(self, t: Parameters) -> Type: raise NotImplementedError("ParamSpec literals cannot have unbound TypeVars") @@ -1364,12 +1369,22 @@ def analyze_callable_type(self, t: UnboundType) -> Type: assert isinstance(ret, CallableType) return ret.accept(self) + def refers_to_full_names(self, arg: UnboundType, names: Sequence[str]) -> bool: + sym = self.lookup_qualified(arg.name, arg) + if sym is not None: + if sym.fullname in names: + return True + return False + def analyze_callable_args( self, arglist: TypeList ) -> tuple[list[Type], list[ArgKind], list[str | None]] | None: args: list[Type] = [] kinds: list[ArgKind] = [] names: list[str | None] = [] + seen_unpack = False + unpack_types: list[Type] = [] + invalid_unpacks = [] for arg in arglist.items: if isinstance(arg, CallableArgument): args.append(arg.typ) @@ -1390,20 +1405,42 @@ def analyze_callable_args( if arg.name is not None and kind.is_star(): self.fail(f"{arg.constructor} arguments should not have names", arg) return None - elif isinstance(arg, UnboundType): - kind = ARG_POS - # Potentially a unpack. - sym = self.lookup_qualified(arg.name, arg) - if sym is not None: - if sym.fullname in ("typing_extensions.Unpack", "typing.Unpack"): - kind = ARG_STAR - args.append(arg) - kinds.append(kind) - names.append(None) + elif ( + isinstance(arg, UnboundType) + and self.refers_to_full_names(arg, ("typing_extensions.Unpack", "typing.Unpack")) + or isinstance(arg, UnpackType) + ): + if seen_unpack: + # Multiple unpacks, preserve them, so we can give an error later. + invalid_unpacks.append(arg) + continue + seen_unpack = True + unpack_types.append(arg) + else: + if seen_unpack: + unpack_types.append(arg) + else: + args.append(arg) + kinds.append(ARG_POS) + names.append(None) + if seen_unpack: + if len(unpack_types) == 1: + args.append(unpack_types[0]) else: - args.append(arg) - kinds.append(ARG_POS) - names.append(None) + first = unpack_types[0] + if isinstance(first, UnpackType): + # UnpackType doesn't have its own line/column numbers, + # so use the unpacked type for error messages. + first = first.type + args.append( + UnpackType(self.tuple_type(unpack_types, line=first.line, column=first.column)) + ) + kinds.append(ARG_STAR) + names.append(None) + for arg in invalid_unpacks: + args.append(arg) + kinds.append(ARG_STAR) + names.append(None) # Note that arglist below is only used for error context. check_arg_names(names, [arglist] * len(args), self.fail, "Callable") check_arg_kinds(kinds, [arglist] * len(args), self.fail) @@ -1713,9 +1750,11 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]: self.fail("More than one Unpack in a type is not allowed", final_unpack) return new_items - def tuple_type(self, items: list[Type]) -> TupleType: + def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: any_type = AnyType(TypeOfAny.special_form) - return TupleType(items, fallback=self.named_type("builtins.tuple", [any_type])) + return TupleType( + items, fallback=self.named_type("builtins.tuple", [any_type]), line=line, column=column + ) TypeVarLikeList = List[Tuple[str, TypeVarLikeExpr]] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index ee81597edadf..c7716f3e8346 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -509,6 +509,51 @@ call_prefix(target=func_prefix, args=(0, 'foo')) call_prefix(target=func2_prefix, args=(0, 'foo')) # E: Argument "target" to "call_prefix" has incompatible type "Callable[[str, int, str], None]"; expected "Callable[[bytes, int, str], None]" [builtins fixtures/tuple.pyi] +[case testTypeVarTuplePep646CallableSuffixSyntax] +from typing import Callable, Tuple, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +x: Callable[[str, Unpack[Tuple[int, ...]], bool], None] +reveal_type(x) # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +A = Callable[[T, Unpack[Ts], S], int] +y: A[int, str, bool] +reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str, builtins.bool) -> builtins.int" +z: A[Unpack[Tuple[int, ...]]] +reveal_type(z) # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTuplePep646CallableInvalidSyntax] +from typing import Callable, Tuple, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +Us = TypeVarTuple("Us") +a: Callable[[Unpack[Ts], Unpack[Us]], int] # E: Var args may not appear after named or var args \ + # E: More than one Unpack in a type is not allowed +reveal_type(a) # N: Revealed type is "def [Ts, Us] (*Unpack[Ts`-1]) -> builtins.int" +b: Callable[[Unpack], int] # E: Unpack[...] requires exactly one type argument +reveal_type(b) # N: Revealed type is "def (*Any) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTuplePep646CallableNewSyntax] +from typing import Callable, Generic, Tuple +from typing_extensions import ParamSpec + +x: Callable[[str, *Tuple[int, ...]], None] +reveal_type(x) # N: Revealed type is "def (builtins.str, *builtins.int)" +y: Callable[[str, *Tuple[int, ...], bool], None] +reveal_type(y) # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" + +P = ParamSpec("P") +class C(Generic[P]): ... +bad: C[[int, *Tuple[int, ...], int]] # E: Unpack is only valid in a variadic position +reveal_type(bad) # N: Revealed type is "__main__.C[[builtins.int, *Any]]" +[builtins fixtures/tuple.pyi] + [case testTypeVarTuplePep646UnspecifiedParameters] from typing import Tuple, Generic, TypeVar from typing_extensions import Unpack, TypeVarTuple @@ -635,19 +680,6 @@ x: A[str, str] reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] -[case testVariadicAliasWrongCallable] -from typing import TypeVar, Callable -from typing_extensions import Unpack, TypeVarTuple - -T = TypeVar("T") -S = TypeVar("S") -Ts = TypeVarTuple("Ts") - -A = Callable[[T, Unpack[Ts], S], int] # E: Required positional args may not appear after default, named or var args -x: A[int, str, int, str] -reveal_type(x) # N: Revealed type is "def (builtins.int, builtins.str, builtins.int, builtins.str) -> builtins.int" -[builtins fixtures/tuple.pyi] - [case testVariadicAliasMultipleUnpacks] from typing import Tuple, Generic, Callable from typing_extensions import Unpack, TypeVarTuple From efecd591e4198232f35e1db66bf99e56fc2f068b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 26 Aug 2023 21:34:38 +0100 Subject: [PATCH 0218/1617] Support user defined variadic tuple types (#15961) Fixes https://github.com/python/mypy/issues/15946 Note this actually adds support also for variadic NamedTuples and variadic TypedDicts. Not that anyone requested this, but since generic NamedTuples and generic TypedDicts are supported using the same mechanism (special aliases) as generic tuple types (like `class A(Tuple[T, S]): ...` in the issue), it looked more risky and arbitrary to _not_support them. Btw the implementation is simple, but while I was working on this, I accidentally found a problem with my general idea of doing certain type normlaizations in `semanal_typeargs.py`. The problem is that sometimes we can call `get_proper_type()` during semantic analysis, so all the code that gets triggered by this (mostly `expand_type()`) can't really rely on types being normalized. Fortunately, with just few tweaks I manged to make the code mostly robust to such scenarios (TBH there are few possible holes left, but this is getting really complex, I think it is better to release this, and see if people will ever hit such scenarios, then fix accordingly). --- mypy/expandtype.py | 7 +- mypy/maptype.py | 1 - mypy/nodes.py | 12 +++- mypy/semanal.py | 10 ++- mypy/semanal_typeargs.py | 14 ++-- mypy/semanal_typeddict.py | 1 + mypy/typeanal.py | 12 ++-- mypy/types.py | 26 ++++++-- test-data/unit/check-typevar-tuple.test | 87 +++++++++++++++++++++++++ 9 files changed, 149 insertions(+), 21 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 7168d7c30b0d..ef8ebe1a9128 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -269,7 +269,8 @@ def visit_unpack_type(self, t: UnpackType) -> Type: # instead. # However, if the item is a variadic tuple, we can simply carry it over. # In particular, if we expand A[*tuple[T, ...]] with substitutions {T: str}, - # it is hard to assert this without getting proper type. + # it is hard to assert this without getting proper type. Another important + # example is non-normalized types when called from semanal.py. return UnpackType(t.type.accept(self)) def expand_unpack(self, t: UnpackType) -> list[Type] | AnyType | UninhabitedType: @@ -414,6 +415,10 @@ def visit_tuple_type(self, t: TupleType) -> Type: unpacked = get_proper_type(item.type) if isinstance(unpacked, Instance): assert unpacked.type.fullname == "builtins.tuple" + if t.partial_fallback.type.fullname != "builtins.tuple": + # If it is a subtype (like named tuple) we need to preserve it, + # this essentially mimics the logic in tuple_fallback(). + return t.partial_fallback.accept(self) return unpacked fallback = t.partial_fallback.accept(self) assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) diff --git a/mypy/maptype.py b/mypy/maptype.py index cae904469fed..4951306573c2 100644 --- a/mypy/maptype.py +++ b/mypy/maptype.py @@ -113,6 +113,5 @@ def instance_to_type_environment(instance: Instance) -> dict[TypeVarId, Type]: required number of type arguments. So this environment consists of the class's type variables mapped to the Instance's actual arguments. The type variables are mapped by their `id`. - """ return {binder.id: arg for binder, arg in zip(instance.type.defn.type_vars, instance.args)} diff --git a/mypy/nodes.py b/mypy/nodes.py index 7efb01c1b18e..9b4ba5e76667 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3546,7 +3546,12 @@ def from_tuple_type(cls, info: TypeInfo) -> TypeAlias: assert info.tuple_type # TODO: is it possible to refactor this to set the correct type vars here? return TypeAlias( - info.tuple_type.copy_modified(fallback=mypy.types.Instance(info, info.defn.type_vars)), + info.tuple_type.copy_modified( + # Create an Instance similar to fill_typevars(). + fallback=mypy.types.Instance( + info, mypy.types.type_vars_as_args(info.defn.type_vars) + ) + ), info.fullname, info.line, info.column, @@ -3563,7 +3568,10 @@ def from_typeddict_type(cls, info: TypeInfo) -> TypeAlias: # TODO: is it possible to refactor this to set the correct type vars here? return TypeAlias( info.typeddict_type.copy_modified( - fallback=mypy.types.Instance(info, info.defn.type_vars) + # Create an Instance similar to fill_typevars(). + fallback=mypy.types.Instance( + info, mypy.types.type_vars_as_args(info.defn.type_vars) + ) ), info.fullname, info.line, diff --git a/mypy/semanal.py b/mypy/semanal.py index 55d4e6a3f506..be7e733a0816 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -277,6 +277,7 @@ get_proper_types, is_named_instance, remove_dups, + type_vars_as_args, ) from mypy.types_utils import is_invalid_recursive_alias, store_argument_type from mypy.typevars import fill_typevars @@ -1702,12 +1703,17 @@ def setup_type_vars(self, defn: ClassDef, tvar_defs: list[TypeVarLikeType]) -> N def setup_alias_type_vars(self, defn: ClassDef) -> None: assert defn.info.special_alias is not None defn.info.special_alias.alias_tvars = list(defn.type_vars) + # It is a bit unfortunate that we need to inline some logic from TypeAlias constructor, + # but it is required, since type variables may change during semantic analyzer passes. + for i, t in enumerate(defn.type_vars): + if isinstance(t, TypeVarTupleType): + defn.info.special_alias.tvar_tuple_index = i target = defn.info.special_alias.target assert isinstance(target, ProperType) if isinstance(target, TypedDictType): - target.fallback.args = tuple(defn.type_vars) + target.fallback.args = type_vars_as_args(defn.type_vars) elif isinstance(target, TupleType): - target.partial_fallback.args = tuple(defn.type_vars) + target.partial_fallback.args = type_vars_as_args(defn.type_vars) else: assert False, f"Unexpected special alias type: {type(target)}" diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 1a37ac57be30..1ae6fada8f38 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -86,31 +86,31 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: # correct aliases. Also, variadic aliases are better to check when fully analyzed, # so we do this here. assert t.alias is not None, f"Unfixed type alias {t.type_ref}" - args = flatten_nested_tuples(t.args) + # TODO: consider moving this validation to typeanal.py, expanding invalid aliases + # during semantic analysis may cause crashes. if t.alias.tvar_tuple_index is not None: - correct = len(args) >= len(t.alias.alias_tvars) - 1 + correct = len(t.args) >= len(t.alias.alias_tvars) - 1 if any( isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) - for a in args + for a in t.args ): correct = True else: - correct = len(args) == len(t.alias.alias_tvars) + correct = len(t.args) == len(t.alias.alias_tvars) if not correct: if t.alias.tvar_tuple_index is not None: exp_len = f"at least {len(t.alias.alias_tvars) - 1}" else: exp_len = f"{len(t.alias.alias_tvars)}" self.fail( - f"Bad number of arguments for type alias, expected: {exp_len}, given: {len(args)}", + "Bad number of arguments for type alias," + f" expected: {exp_len}, given: {len(t.args)}", t, code=codes.TYPE_ARG, ) t.args = set_any_tvars( t.alias, t.line, t.column, self.options, from_error=True, fail=self.fail ).args - else: - t.args = args is_error = self.validate_args(t.alias.name, t.args, t.alias.alias_tvars, t) if not is_error: # If there was already an error for the alias itself, there is no point in checking diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index aba5bf69b130..fb3fa713e3fb 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -252,6 +252,7 @@ def map_items_to_base( if not tvars: mapped_items[key] = type_in_base continue + # TODO: simple zip can't be used for variadic types. mapped_items[key] = expand_type( type_in_base, {t.id: a for (t, a) in zip(tvars, base_args)} ) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 1955d2bc3c43..ed1a8073887b 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -82,6 +82,7 @@ UnionType, UnpackType, callable_with_ellipsis, + flatten_nested_tuples, flatten_nested_unions, get_proper_type, has_type_vars, @@ -763,8 +764,8 @@ def analyze_type_with_type_info( if info.special_alias: return instantiate_type_alias( info.special_alias, - # TODO: should we allow NamedTuples generic in ParamSpec and TypeVarTuple? - self.anal_array(args), + # TODO: should we allow NamedTuples generic in ParamSpec? + self.anal_array(args, allow_unpack=True), self.fail, False, ctx, @@ -782,7 +783,7 @@ def analyze_type_with_type_info( return instantiate_type_alias( info.special_alias, # TODO: should we allow TypedDicts generic in ParamSpec? - self.anal_array(args), + self.anal_array(args, allow_unpack=True), self.fail, False, ctx, @@ -1948,7 +1949,10 @@ def instantiate_type_alias( # 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. - typ = TypeAliasType(node, args, ctx.line, ctx.column) + # Type aliases are special, since they can be expanded during semantic analysis, + # so we need to normalize them as soon as possible. + # TODO: can this cause an infinite recursion? + typ = TypeAliasType(node, flatten_nested_tuples(args), ctx.line, ctx.column) assert typ.alias is not None # HACK: Implement FlexibleAlias[T, typ] by expanding it to typ here. if ( diff --git a/mypy/types.py b/mypy/types.py index cf2c343655dd..fb360fb892f1 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1045,9 +1045,12 @@ class UnpackType(ProperType): """Type operator Unpack from PEP646. Can be either with Unpack[] or unpacking * syntax. - The inner type should be either a TypeVarTuple, a constant size - tuple, or a variable length tuple. Type aliases to these are not allowed, - except during semantic analysis. + The inner type should be either a TypeVarTuple, or a variable length tuple. + In an exceptional case of callable star argument it can be a fixed length tuple. + + Note: the above restrictions are only guaranteed by normalizations after semantic + analysis, if your code needs to handle UnpackType *during* semantic analysis, it is + wild west, technically anything can be present in the wrapped type. """ __slots__ = ["type"] @@ -2143,7 +2146,11 @@ def with_normalized_var_args(self) -> Self: assert nested_unpacked.type.fullname == "builtins.tuple" new_unpack = nested_unpacked.args[0] else: - assert isinstance(nested_unpacked, TypeVarTupleType) + if not isinstance(nested_unpacked, TypeVarTupleType): + # We found a non-nomralized tuple type, this means this method + # is called during semantic analysis (e.g. from get_proper_type()) + # there is no point in normalizing callables at this stage. + return self new_unpack = nested_unpack else: new_unpack = UnpackType( @@ -3587,6 +3594,17 @@ def remove_dups(types: list[T]) -> list[T]: return new_types +def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]: + """Represent type variables as they would appear in a type argument list.""" + args: list[Type] = [] + for tv in type_vars: + if isinstance(tv, TypeVarTupleType): + args.append(UnpackType(tv)) + else: + args.append(tv) + return tuple(args) + + # This cyclic import is unfortunate, but to avoid it we would need to move away all uses # of get_proper_type() from types.py. Majority of them have been removed, but few remaining # are quite tricky to get rid of, but ultimately we want to do it at some point. diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index c7716f3e8346..a36c4d4d6741 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1032,3 +1032,90 @@ Second = Tuple[C, D] x: G[Unpack[First], Unpack[Second]] # E: Type argument "A" of "G" must be a subtype of "int" \ # E: Type argument "D" of "G" must be a subtype of "str" [builtins fixtures/tuple.pyi] + +[case testVariadicTupleType] +from typing import Tuple, Callable +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class A(Tuple[Unpack[Ts]]): + fn: Callable[[Unpack[Ts]], None] + +x: A[int] +reveal_type(x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.A[builtins.int]]" +reveal_type(x[0]) # N: Revealed type is "builtins.int" +reveal_type(x.fn) # N: Revealed type is "def (builtins.int)" + +y: A[int, str] +reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" +reveal_type(y[0]) # N: Revealed type is "builtins.int" +reveal_type(y.fn) # N: Revealed type is "def (builtins.int, builtins.str)" + +z: A[Unpack[Tuple[int, ...]]] +reveal_type(z) # N: Revealed type is "__main__.A[Unpack[builtins.tuple[builtins.int, ...]]]" +# TODO: this requires fixing map_instance_to_supertype(). +# reveal_type(z[0]) +reveal_type(z.fn) # N: Revealed type is "def (*builtins.int)" + +t: A[int, Unpack[Tuple[int, str]], str] +reveal_type(t) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" +reveal_type(t[0]) # N: Revealed type is "builtins.int" +reveal_type(t.fn) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str, builtins.str)" +[builtins fixtures/tuple.pyi] + +[case testVariadicNamedTuple] +from typing import Tuple, Callable, NamedTuple, Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class A(NamedTuple, Generic[Unpack[Ts], T]): + fn: Callable[[Unpack[Ts]], None] + val: T + +y: A[int, str] +reveal_type(y) # N: Revealed type is "Tuple[def (builtins.int), builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" +reveal_type(y[0]) # N: Revealed type is "def (builtins.int)" +reveal_type(y.fn) # N: Revealed type is "def (builtins.int)" + +z: A[Unpack[Tuple[int, ...]]] +reveal_type(z) # N: Revealed type is "Tuple[def (*builtins.int), builtins.int, fallback=__main__.A[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]" +reveal_type(z.fn) # N: Revealed type is "def (*builtins.int)" + +t: A[int, Unpack[Tuple[int, str]], str] +reveal_type(t) # N: Revealed type is "Tuple[def (builtins.int, builtins.int, builtins.str), builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" + +def test(x: int, y: str) -> None: ... +nt = A(fn=test, val=42) +reveal_type(nt) # N: Revealed type is "Tuple[def (builtins.int, builtins.str), builtins.int, fallback=__main__.A[builtins.int, builtins.str, builtins.int]]" + +def bad() -> int: ... +nt2 = A(fn=bad, val=42) # E: Argument "fn" to "A" has incompatible type "Callable[[], int]"; expected "Callable[[], None]" +[builtins fixtures/tuple.pyi] + +[case testVariadicTypedDict] +from typing import Tuple, Callable, Generic +from typing_extensions import TypeVarTuple, Unpack, TypedDict + +Ts = TypeVarTuple("Ts") +class A(TypedDict, Generic[Unpack[Ts], T]): + fn: Callable[[Unpack[Ts]], None] + val: T + +y: A[int, str] +reveal_type(y) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (builtins.int), 'val': builtins.str})" +reveal_type(y["fn"]) # N: Revealed type is "def (builtins.int)" + +z: A[Unpack[Tuple[int, ...]]] +reveal_type(z) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (*builtins.int), 'val': builtins.int})" +reveal_type(z["fn"]) # N: Revealed type is "def (*builtins.int)" + +t: A[int, Unpack[Tuple[int, str]], str] +reveal_type(t) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (builtins.int, builtins.int, builtins.str), 'val': builtins.str})" + +def test(x: int, y: str) -> None: ... +td = A({"fn": test, "val": 42}) +reveal_type(td) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (builtins.int, builtins.str), 'val': builtins.int})" + +def bad() -> int: ... +td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]") +[builtins fixtures/tuple.pyi] From d7b24514d7301f86031b7d1e2215cf8c2476bec0 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 27 Aug 2023 23:20:13 +0100 Subject: [PATCH 0219/1617] Fixes to stubtest's new check for missing stdlib modules (#15960) - It's not easy to predict where stdlib modules are going to be located. (It varies between platforms, and between venvs and conda envs; on some platforms it's in a completely different directory to the Python executable.) - Some modules appear to raise `SystemExit` when stubtest tries to import them in CI, leading stubtest to instantly exit without logging a message to the terminal. - Importing some `test.*` submodules leads to unraisable exceptions being printed to the terminal at the end of the stubtest run, which is somewhat annoying. --- mypy/stubtest.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 34bb985b702e..a804835a632b 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -23,6 +23,7 @@ import typing import typing_extensions import warnings +from collections import defaultdict from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path @@ -1679,16 +1680,22 @@ def get_importable_stdlib_modules() -> set[str]: all_stdlib_modules = sys.stdlib_module_names else: all_stdlib_modules = set(sys.builtin_module_names) - python_exe_dir = Path(sys.executable).parent + modules_by_finder: defaultdict[importlib.machinery.FileFinder, set[str]] = defaultdict(set) for m in pkgutil.iter_modules(): - finder = m.module_finder - if isinstance(finder, importlib.machinery.FileFinder): - finder_path = Path(finder.path) - if ( - python_exe_dir in finder_path.parents - and "site-packages" not in finder_path.parts - ): - all_stdlib_modules.add(m.name) + if isinstance(m.module_finder, importlib.machinery.FileFinder): + modules_by_finder[m.module_finder].add(m.name) + for finder, module_group in modules_by_finder.items(): + if ( + "site-packages" not in Path(finder.path).parents + # if "_queue" is present, it's most likely the module finder + # for stdlib extension modules; + # if "queue" is present, it's most likely the module finder + # for pure-Python stdlib modules. + # In either case, we'll want to add all the modules that the finder has to offer us. + # This is a bit hacky, but seems to work well in a cross-platform way. + and {"_queue", "queue"} & module_group + ): + all_stdlib_modules.update(module_group) importable_stdlib_modules: set[str] = set() for module_name in all_stdlib_modules: @@ -1719,13 +1726,25 @@ def get_importable_stdlib_modules() -> set[str]: # The idlelib.* submodules are similarly annoying in opening random tkinter windows, # and we're unlikely to ever add stubs for idlelib in typeshed # (see discussion in https://github.com/python/typeshed/pull/9193) - if submodule_name.endswith(".__main__") or submodule_name.startswith("idlelib."): + # + # test.* modules do weird things like raising exceptions in __del__ methods, + # leading to unraisable exceptions being logged to the terminal + # as a warning at the end of the stubtest run + if ( + submodule_name.endswith(".__main__") + or submodule_name.startswith("idlelib.") + or submodule_name.startswith("test.") + ): continue try: silent_import_module(submodule_name) + except KeyboardInterrupt: + raise # importing multiprocessing.popen_forkserver on Windows raises AttributeError... - except Exception: + # some submodules also appear to raise SystemExit as well on some Python versions + # (not sure exactly which) + except BaseException: continue else: importable_stdlib_modules.add(submodule_name) From 010da0b2f48dc92be2f79495fd4551c92351868f Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Mon, 28 Aug 2023 04:03:50 -0400 Subject: [PATCH 0220/1617] attrs, dataclasses: don't enforce slots when base doesn't (#15976) Doing the same thing we do for regular classes. Fixes #15975 --- mypy/plugins/attrs.py | 5 +++++ mypy/plugins/dataclasses.py | 6 ++++++ test-data/unit/check-dataclasses.test | 16 ++++++++++++++++ test-data/unit/check-plugin-attrs.test | 15 +++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index d444c18852dd..3d326a5f4e80 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -893,6 +893,11 @@ def _add_attrs_magic_attribute( def _add_slots(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None: + if any(p.slots is None for p in ctx.cls.info.mro[1:-1]): + # At least one type in mro (excluding `self` and `object`) + # does not have concrete `__slots__` defined. Ignoring. + return + # Unlike `@dataclasses.dataclass`, `__slots__` is rewritten here. ctx.cls.info.slots = {attr.name for attr in attributes} diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index d782acf50af5..39b597491e9e 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -443,6 +443,12 @@ def add_slots( self._cls, ) return + + if any(p.slots is None for p in info.mro[1:-1]): + # At least one type in mro (excluding `self` and `object`) + # does not have concrete `__slots__` defined. Ignoring. + return + info.slots = generated_slots # Now, insert `.__slots__` attribute to class namespace: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 7881dfbcf1bb..91c409807497 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1519,6 +1519,22 @@ class Some: self.y = 1 # E: Trying to assign name "y" that is not in "__slots__" of type "__main__.Some" [builtins fixtures/dataclasses.pyi] +[case testDataclassWithSlotsDerivedFromNonSlot] +# flags: --python-version 3.10 +from dataclasses import dataclass + +class A: + pass + +@dataclass(slots=True) +class B(A): + x: int + + def __post_init__(self) -> None: + self.y = 42 + +[builtins fixtures/dataclasses.pyi] + [case testDataclassWithSlotsConflict] # flags: --python-version 3.10 from dataclasses import dataclass diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 7580531bebc9..e8598132c50e 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1677,6 +1677,21 @@ class C: self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.C" [builtins fixtures/plugin_attrs.pyi] +[case testAttrsClassWithSlotsDerivedFromNonSlots] +import attrs + +class A: + pass + +@attrs.define(slots=True) +class B(A): + x: int + + def __attrs_post_init__(self) -> None: + self.y = 42 + +[builtins fixtures/plugin_attrs.pyi] + [case testRuntimeSlotsAttr] from attr import dataclass From 171402834faece2e20760f0d02e96aa3714324c2 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 29 Aug 2023 10:17:52 +0100 Subject: [PATCH 0221/1617] Optimize Unpack for failures (#15967) This is a small but possibly important PR. Wherever possible we should represent user error and/or failed type inference as `*tuple[Any, ...]`/`*tuple[, ...]`, rather than `Unpack[Any]`/`Unpack[]` or plain `Any`/``. This way we will not need any special casing for failure conditions in various places without risking a crash instead of a graceful failure (error message). --- mypy/expandtype.py | 23 ++++++----------------- mypy/semanal_main.py | 2 ++ mypy/semanal_typeargs.py | 21 ++++++++++++++------- test-data/unit/check-typevar-tuple.test | 5 ++--- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index ef8ebe1a9128..26353c043cb7 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -273,7 +273,7 @@ def visit_unpack_type(self, t: UnpackType) -> Type: # example is non-normalized types when called from semanal.py. return UnpackType(t.type.accept(self)) - def expand_unpack(self, t: UnpackType) -> list[Type] | AnyType | UninhabitedType: + def expand_unpack(self, t: UnpackType) -> list[Type]: assert isinstance(t.type, TypeVarTupleType) repl = get_proper_type(self.variables.get(t.type.id, t.type)) if isinstance(repl, TupleType): @@ -285,9 +285,9 @@ def expand_unpack(self, t: UnpackType) -> list[Type] | AnyType | UninhabitedType ): return [UnpackType(typ=repl)] elif isinstance(repl, (AnyType, UninhabitedType)): - # tuple[Any, ...] for Any would be better, but we don't have - # the type info to construct that type here. - return repl + # Replace *Ts = Any with *Ts = *tuple[Any, ...] and some for . + # These types may appear here as a result of user error or failed inference. + return [UnpackType(t.type.tuple_fallback.copy_modified(args=[repl]))] else: raise RuntimeError(f"Invalid type replacement to expand: {repl}") @@ -310,12 +310,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l # We have plain Unpack[Ts] assert isinstance(var_arg_type, TypeVarTupleType) fallback = var_arg_type.tuple_fallback - expanded_items_res = self.expand_unpack(var_arg) - if isinstance(expanded_items_res, list): - expanded_items = expanded_items_res - else: - # We got Any or - return prefix + [expanded_items_res] + suffix + expanded_items = self.expand_unpack(var_arg) new_unpack = UnpackType(TupleType(expanded_items, fallback)) return prefix + [new_unpack] + suffix @@ -394,14 +389,8 @@ def expand_types_with_unpack( items: list[Type] = [] for item in typs: if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): - unpacked_items = self.expand_unpack(item) - if isinstance(unpacked_items, (AnyType, UninhabitedType)): - # TODO: better error for , something like tuple of unknown? - return unpacked_items - else: - items.extend(unpacked_items) + items.extend(self.expand_unpack(item)) else: - # Must preserve original aliases when possible. items.append(item.accept(self)) return items diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 51a7014fac1a..ec09deb0952f 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -381,6 +381,7 @@ def check_type_arguments(graph: Graph, scc: list[str], errors: Errors) -> None: errors, state.options, is_typeshed_file(state.options.abs_custom_typeshed_dir, state.path or ""), + state.manager.semantic_analyzer.named_type, ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): @@ -399,6 +400,7 @@ def check_type_arguments_in_targets( errors, state.options, is_typeshed_file(state.options.abs_custom_typeshed_dir, state.path or ""), + state.manager.semantic_analyzer.named_type, ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 1ae6fada8f38..749b02391e06 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Sequence +from typing import Callable, Sequence from mypy import errorcodes as codes, message_registry from mypy.errorcodes import ErrorCode @@ -42,11 +42,18 @@ class TypeArgumentAnalyzer(MixedTraverserVisitor): - def __init__(self, errors: Errors, options: Options, is_typeshed_file: bool) -> None: + def __init__( + self, + errors: Errors, + options: Options, + is_typeshed_file: bool, + named_type: Callable[[str, list[Type]], Instance], + ) -> None: super().__init__() self.errors = errors self.options = options self.is_typeshed_file = is_typeshed_file + self.named_type = named_type self.scope = Scope() # Should we also analyze function definitions, or only module top-levels? self.recurse_into_functions = True @@ -243,16 +250,16 @@ def visit_unpack_type(self, typ: UnpackType) -> None: return if isinstance(proper_type, TypeVarTupleType): return + # TODO: this should probably be .has_base("builtins.tuple"), also elsewhere. if isinstance(proper_type, Instance) and proper_type.type.fullname == "builtins.tuple": return - if isinstance(proper_type, AnyType) and proper_type.type_of_any == TypeOfAny.from_error: - return - if not isinstance(proper_type, UnboundType): - # Avoid extra errors if there were some errors already. + if not isinstance(proper_type, (UnboundType, AnyType)): + # Avoid extra errors if there were some errors already. Also interpret plain Any + # as tuple[Any, ...] (this is better for the code in type checker). self.fail( message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), typ ) - typ.type = AnyType(TypeOfAny.from_error) + typ.type = self.named_type("builtins.tuple", [AnyType(TypeOfAny.from_error)]) def check_type_var_values( self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index a36c4d4d6741..c8b33ec96b06 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -17,8 +17,7 @@ reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -if object(): - f(0) # E: Argument 1 to "f" has incompatible type "int"; expected +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[, ...]" def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: return a @@ -26,7 +25,7 @@ def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: reveal_type(g(args, args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(g(args, args2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(g(args, args3)) # N: Revealed type is "builtins.tuple[builtins.object, ...]" -reveal_type(g(any, any)) # N: Revealed type is "Any" +reveal_type(g(any, any)) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleMixed] From 6c16143c3a68c99f6e4c99974c44cf3abf867103 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 29 Aug 2023 23:12:02 +0300 Subject: [PATCH 0222/1617] Improve GitHub Actions specs (#15965) Two main changes: 1. Always use secure permissions, when some workflow does not do anything, it has to be `contents: read` only 2. Be more consistent with canceling workflows --- .github/workflows/build_wheels.yml | 3 +++ .github/workflows/docs.yml | 7 +++++++ .github/workflows/mypy_primer.yml | 3 +++ .github/workflows/test.yml | 3 +++ .github/workflows/test_stubgenc.yml | 7 +++++++ 5 files changed, 23 insertions(+) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index e728d741d90d..3f4ea5e42f9b 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -5,6 +5,9 @@ on: branches: [main, master, 'release*'] tags: ['*'] +permissions: + contents: write + jobs: build-wheels: if: github.repository == 'python/mypy' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5dc86a1159f4..9f3a6121ae16 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,6 +14,13 @@ on: - CREDITS - LICENSE +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: docs: runs-on: ubuntu-latest diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index e7e4af1f07b7..2958b8fc325b 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -15,6 +15,9 @@ on: - 'mypy/test/**' - 'test-data/**' +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f594353ed05a..0e335a59d1d0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,9 @@ on: - CREDITS - LICENSE +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index db9bf413faa3..33466b9870ff 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -12,6 +12,13 @@ on: - 'mypy/stubdoc.py' - 'test-data/stubgen/**' +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: stubgenc: # Check stub file generation for a small pybind11 project From d6df8e883e927920bbe50aab779e7591e31533c6 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 29 Aug 2023 19:29:19 -0400 Subject: [PATCH 0223/1617] dataclasses.replace: fall through to typeshed sig (#15962) If the dataclasses plugin cannot determine a signature for `dataclasses.replace`, it should not report an error. The underlying typeshed signature will get a shot at verifying the type and reporting an error, and it would enable the following pattern (without typing `replace`'s kwargs, though) --- mypy/plugins/dataclasses.py | 25 +---------- test-data/unit/check-dataclass-transform.test | 2 +- test-data/unit/check-dataclasses.test | 44 +++++++++++++++---- test-data/unit/lib-stub/dataclasses.pyi | 20 ++++++++- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 39b597491e9e..8b34c28b6832 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -972,25 +972,6 @@ def _has_direct_dataclass_transform_metaclass(info: TypeInfo) -> bool: ) -def _fail_not_dataclass(ctx: FunctionSigContext, t: Type, parent_t: Type) -> None: - t_name = format_type_bare(t, ctx.api.options) - if parent_t is t: - msg = ( - f'Argument 1 to "replace" has a variable type "{t_name}" not bound to a dataclass' - if isinstance(t, TypeVarType) - else f'Argument 1 to "replace" has incompatible type "{t_name}"; expected a dataclass' - ) - else: - pt_name = format_type_bare(parent_t, ctx.api.options) - msg = ( - f'Argument 1 to "replace" has type "{pt_name}" whose item "{t_name}" is not bound to a dataclass' - if isinstance(t, TypeVarType) - else f'Argument 1 to "replace" has incompatible type "{pt_name}" whose item "{t_name}" is not a dataclass' - ) - - ctx.api.fail(msg, ctx.context) - - def _get_expanded_dataclasses_fields( ctx: FunctionSigContext, typ: ProperType, display_typ: ProperType, parent_typ: ProperType ) -> list[CallableType] | None: @@ -999,9 +980,7 @@ def _get_expanded_dataclasses_fields( For generic classes, the field types are expanded. If the type contains Any or a non-dataclass, returns None; in the latter case, also reports an error. """ - if isinstance(typ, AnyType): - return None - elif isinstance(typ, UnionType): + if isinstance(typ, UnionType): ret: list[CallableType] | None = [] for item in typ.relevant_items(): item = get_proper_type(item) @@ -1018,14 +997,12 @@ def _get_expanded_dataclasses_fields( elif isinstance(typ, Instance): replace_sym = typ.type.get_method(_INTERNAL_REPLACE_SYM_NAME) if replace_sym is None: - _fail_not_dataclass(ctx, display_typ, parent_typ) return None replace_sig = replace_sym.type assert isinstance(replace_sig, ProperType) assert isinstance(replace_sig, CallableType) return [expand_type_by_instance(replace_sig, typ)] else: - _fail_not_dataclass(ctx, display_typ, parent_typ) return None diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 9029582ece82..58cd5e5a90f8 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -853,7 +853,7 @@ class Person: name: str p = Person('John') -y = replace(p, name='Bob') # E: Argument 1 to "replace" has incompatible type "Person"; expected a dataclass +y = replace(p, name='Bob') [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 91c409807497..1f5f5635de4e 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2122,6 +2122,8 @@ a2 = replace(a, x='42', q=42) # E: Argument "x" to "replace" of "A" has incompa a2 = replace(a, q='42') # E: Argument "q" to "replace" of "A" has incompatible type "str"; expected "int" reveal_type(a2) # N: Revealed type is "__main__.A" +[builtins fixtures/tuple.pyi] + [case testReplaceUnion] from typing import Generic, Union, TypeVar from dataclasses import dataclass, replace, InitVar @@ -2151,7 +2153,7 @@ _ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "re _ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[, ]"; expected _ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool" -[builtins fixtures/dataclasses.pyi] +[builtins fixtures/tuple.pyi] [case testReplaceUnionOfTypeVar] from typing import Generic, Union, TypeVar @@ -2171,7 +2173,9 @@ TA = TypeVar('TA', bound=A) TB = TypeVar('TB', bound=B) def f(b_or_t: Union[TA, TB, int]) -> None: - a2 = replace(b_or_t) # E: Argument 1 to "replace" has type "Union[TA, TB, int]" whose item "TB" is not bound to a dataclass # E: Argument 1 to "replace" has incompatible type "Union[TA, TB, int]" whose item "int" is not a dataclass + a2 = replace(b_or_t) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[TA, TB, int]" + +[builtins fixtures/tuple.pyi] [case testReplaceTypeVarBoundNotDataclass] from dataclasses import dataclass, replace @@ -2183,16 +2187,18 @@ TNone = TypeVar('TNone', bound=None) TUnion = TypeVar('TUnion', bound=Union[str, int]) def f1(t: TInt) -> None: - _ = replace(t, x=42) # E: Argument 1 to "replace" has a variable type "TInt" not bound to a dataclass + _ = replace(t, x=42) # E: Value of type variable "_DataclassT" of "replace" cannot be "TInt" def f2(t: TAny) -> TAny: - return replace(t, x='spam') # E: Argument 1 to "replace" has a variable type "TAny" not bound to a dataclass + return replace(t, x='spam') # E: Value of type variable "_DataclassT" of "replace" cannot be "TAny" def f3(t: TNone) -> TNone: - return replace(t, x='spam') # E: Argument 1 to "replace" has a variable type "TNone" not bound to a dataclass + return replace(t, x='spam') # E: Value of type variable "_DataclassT" of "replace" cannot be "TNone" def f4(t: TUnion) -> TUnion: - return replace(t, x='spam') # E: Argument 1 to "replace" has incompatible type "TUnion" whose item "str" is not a dataclass # E: Argument 1 to "replace" has incompatible type "TUnion" whose item "int" is not a dataclass + return replace(t, x='spam') # E: Value of type variable "_DataclassT" of "replace" cannot be "TUnion" + +[builtins fixtures/tuple.pyi] [case testReplaceTypeVarBound] from dataclasses import dataclass, replace @@ -2217,6 +2223,8 @@ def f(t: TA) -> TA: f(A(x=42)) f(B(x=42)) +[builtins fixtures/tuple.pyi] + [case testReplaceAny] from dataclasses import replace from typing import Any @@ -2225,17 +2233,33 @@ a: Any a2 = replace(a) reveal_type(a2) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + [case testReplaceNotDataclass] from dataclasses import replace -replace(5) # E: Argument 1 to "replace" has incompatible type "int"; expected a dataclass +replace(5) # E: Value of type variable "_DataclassT" of "replace" cannot be "int" class C: pass -replace(C()) # E: Argument 1 to "replace" has incompatible type "C"; expected a dataclass +replace(C()) # E: Value of type variable "_DataclassT" of "replace" cannot be "C" -replace(None) # E: Argument 1 to "replace" has incompatible type "None"; expected a dataclass +replace(None) # E: Value of type variable "_DataclassT" of "replace" cannot be "None" + +[builtins fixtures/tuple.pyi] + +[case testReplaceIsDataclass] +from dataclasses import is_dataclass, replace + +def f(x: object) -> None: + _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "object" + if is_dataclass(x): + _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, Type[DataclassInstance]]" + if not isinstance(x, type): + _ = replace(x) + +[builtins fixtures/tuple.pyi] [case testReplaceGeneric] from dataclasses import dataclass, replace, InitVar @@ -2254,6 +2278,8 @@ reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" a2 = replace(a, x='42') # E: Argument "x" to "replace" of "A[int]" has incompatible type "str"; expected "int" reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" +[builtins fixtures/tuple.pyi] + [case testPostInitCorrectSignature] from typing import Any, Generic, TypeVar, Callable, Self from dataclasses import dataclass, InitVar diff --git a/test-data/unit/lib-stub/dataclasses.pyi b/test-data/unit/lib-stub/dataclasses.pyi index b2b48c2ae486..cf43747757bd 100644 --- a/test-data/unit/lib-stub/dataclasses.pyi +++ b/test-data/unit/lib-stub/dataclasses.pyi @@ -1,6 +1,14 @@ -from typing import Any, Callable, Generic, Mapping, Optional, TypeVar, overload, Type +from typing import Any, Callable, Generic, Literal, Mapping, Optional, TypeVar, overload, Type, \ + Protocol, ClassVar +from typing_extensions import TypeGuard + +# DataclassInstance is in _typeshed.pyi normally, but alas we can't do the same for lib-stub +# due to test-data/unit/lib-stub/builtins.pyi not having 'tuple'. +class DataclassInstance(Protocol): + __dataclass_fields__: ClassVar[dict[str, Field[Any]]] _T = TypeVar('_T') +_DataclassT = TypeVar("_DataclassT", bound=DataclassInstance) class InitVar(Generic[_T]): ... @@ -33,4 +41,12 @@ def field(*, class Field(Generic[_T]): pass -def replace(__obj: _T, **changes: Any) -> _T: ... +@overload +def is_dataclass(obj: DataclassInstance) -> Literal[True]: ... +@overload +def is_dataclass(obj: type) -> TypeGuard[type[DataclassInstance]]: ... +@overload +def is_dataclass(obj: object) -> TypeGuard[DataclassInstance | type[DataclassInstance]]: ... + + +def replace(__obj: _DataclassT, **changes: Any) -> _DataclassT: ... From 379b52f2cfff4955589df714cb2dd904be482e76 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:46:46 -0700 Subject: [PATCH 0224/1617] Try upgrading tox (#15992) Fixes #15990 --- .github/workflows/docs.yml | 2 +- .github/workflows/test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9f3a6121ae16..037738d4b3aa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: with: python-version: '3.8' - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.4.4 + run: pip install --upgrade 'setuptools!=50' tox==4.11.0 - name: Setup tox environment run: tox run -e ${{ env.TOXENV }} --notest - name: Test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e335a59d1d0..d2e7e7258500 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -133,7 +133,7 @@ jobs: ./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV source $VENV/bin/activate - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.4.4 + run: pip install --upgrade 'setuptools!=50' tox==4.11.0 - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} run: | @@ -185,7 +185,7 @@ jobs: default: 3.11.1 command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');" - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.4.4 + run: pip install --upgrade 'setuptools!=50' tox==4.11.0 - name: Setup tox environment run: tox run -e py --notest - name: Test From 2298829ab3b7339427ec957ec5c21955d3657c6f Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 29 Aug 2023 20:49:24 -0400 Subject: [PATCH 0225/1617] attrs: remove fields type check (#15983) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since https://github.com/python-attrs/attrs/pull/890 (≥ 22.1.0) `attrs.fields` is typed to accept a protocol. Since https://github.com/python-attrs/attrs/pull/997 (≥ 22.2.0) `attrs.has` is a type-guard. Support both by removing the explicit error reporting and letting it fall through to the type stub. Fixes #15980. --- mypy/plugins/attrs.py | 5 ----- test-data/unit/check-plugin-attrs.test | 16 ++++++++++------ test-data/unit/lib-stub/attrs/__init__.pyi | 13 ++++++++++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3d326a5f4e80..6f5b6f35da07 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1111,9 +1111,4 @@ def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl assert ret_type is not None return ctx.default_signature.copy_modified(arg_types=arg_types, ret_type=ret_type) - ctx.api.fail( - f'Argument 1 to "fields" has incompatible type "{format_type_bare(proper_type, ctx.api.options)}"; expected an attrs class', - ctx.context, - ) - return ctx.default_signature diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index e8598132c50e..1465bab2bb7b 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1596,16 +1596,18 @@ def f(t: TA) -> None: [builtins fixtures/plugin_attrs.pyi] [case testNonattrsFields] -# flags: --no-strict-optional from typing import Any, cast, Type -from attrs import fields +from attrs import fields, has class A: b: int c: str -fields(A) # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected an attrs class -fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected an attrs class +if has(A): + fields(A) +else: + fields(A) # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected "Type[AttrsInstance]" +fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected "Type[AttrsInstance]" fields(cast(Any, 42)) fields(cast(Type[Any], 43)) @@ -2167,7 +2169,8 @@ TA = TypeVar('TA', bound=A) TB = TypeVar('TB', bound=B) def f(b_or_t: TA | TB | int) -> None: - a2 = attrs.evolve(b_or_t) # E: Argument 1 to "evolve" has type "Union[TA, TB, int]" whose item "TB" is not bound to an attrs class # E: Argument 1 to "evolve" has incompatible type "Union[TA, TB, int]" whose item "int" is not an attrs class + a2 = attrs.evolve(b_or_t) # E: Argument 1 to "evolve" has type "Union[TA, TB, int]" whose item "TB" is not bound to an attrs class \ + # E: Argument 1 to "evolve" has incompatible type "Union[TA, TB, int]" whose item "int" is not an attrs class [builtins fixtures/plugin_attrs.pyi] @@ -2216,7 +2219,8 @@ def h(t: TNone) -> None: _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TNone" not bound to an attrs class def x(t: TUnion) -> None: - _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "str" is not an attrs class # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "int" is not an attrs class + _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "str" is not an attrs class \ + # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "int" is not an attrs class [builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/lib-stub/attrs/__init__.pyi b/test-data/unit/lib-stub/attrs/__init__.pyi index a575f97da9bc..7a88170d7271 100644 --- a/test-data/unit/lib-stub/attrs/__init__.pyi +++ b/test-data/unit/lib-stub/attrs/__init__.pyi @@ -1,7 +1,14 @@ -from typing import TypeVar, overload, Callable, Any, Optional, Union, Sequence, Mapping, Generic +from typing import TypeVar, overload, Callable, Any, Optional, Union, Sequence, Mapping, \ + Protocol, ClassVar, Type +from typing_extensions import TypeGuard from attr import Attribute as Attribute + +class AttrsInstance(Protocol): + __attrs_attrs__: ClassVar[Any] + + _T = TypeVar('_T') _C = TypeVar('_C', bound=type) @@ -131,5 +138,5 @@ def field( def evolve(inst: _T, **changes: Any) -> _T: ... def assoc(inst: _T, **changes: Any) -> _T: ... - -def fields(cls: type) -> Any: ... +def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ... +def fields(cls: Type[AttrsInstance]) -> Any: ... From 5783af495f22e2abc42b3c153b0bea2faa9b72e7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 29 Aug 2023 18:50:20 -0700 Subject: [PATCH 0226/1617] Fix inference for properties with __call__ (#15926) Fixes #5858 --- mypy/checkmember.py | 21 +++++++++++++-------- test-data/unit/check-functions.test | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 1bdc00a6eb59..f7d002f17eb9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence, cast +from typing import TYPE_CHECKING, Callable, Optional, Sequence, cast from mypy import meet, message_registry, subtypes from mypy.erasetype import erase_typevars @@ -776,12 +776,17 @@ def analyze_var( freeze_all_type_vars(t) result: Type = t typ = get_proper_type(typ) - if ( - var.is_initialized_in_class - and (not is_instance_var(var) or mx.is_operator) - and isinstance(typ, FunctionLike) - and not typ.is_type_obj() - ): + + call_type: Optional[ProperType] = None + if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator): + if isinstance(typ, FunctionLike) and not typ.is_type_obj(): + call_type = typ + elif var.is_property: + call_type = get_proper_type(_analyze_member_access("__call__", typ, mx)) + else: + call_type = typ + + if isinstance(call_type, FunctionLike) and not call_type.is_type_obj(): if mx.is_lvalue: if var.is_property: if not var.is_settable_property: @@ -792,7 +797,7 @@ def analyze_var( if not var.is_staticmethod: # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. - functype = typ + functype: FunctionLike = call_type # Use meet to narrow original_type to the dispatched type. # For example, assume # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index f49541420cc0..4cc523a595d1 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3158,3 +3158,20 @@ class C(A, B): class D(A, B): def f(self, z: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" [typing fixtures/typing-override.pyi] + +[case testCallableProperty] +from typing import Callable + +class something_callable: + def __call__(self, fn) -> str: ... + +def decorator(fn: Callable[..., int]) -> something_callable: ... + +class A: + @property + @decorator + def f(self) -> int: ... + +reveal_type(A.f) # N: Revealed type is "__main__.something_callable" +reveal_type(A().f) # N: Revealed type is "builtins.str" +[builtins fixtures/property.pyi] From 0ae0c750b7c39c875f5ea536408143fe32d920d8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 30 Aug 2023 03:08:05 +0100 Subject: [PATCH 0227/1617] Fix ParamSpec inference for callback protocols (#15986) Fixes https://github.com/python/mypy/issues/15984 Fix is straightforward, `ParamSpec` inference special-casing should put instances with `__call__` and callable types on same ground. --- mypy/checkexpr.py | 4 ++++ test-data/unit/check-parameter-specification.test | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4430d0773cfa..218568007b9e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2185,6 +2185,10 @@ def get_arg_infer_passes( # run(test, 1, 2) # we will use `test` for inference, since it will allow to infer also # argument *names* for P <: [x: int, y: int]. + if isinstance(p_actual, Instance): + call_method = find_member("__call__", p_actual, p_actual, is_operator=True) + if call_method is not None: + p_actual = get_proper_type(call_method) if ( isinstance(p_actual, CallableType) and not p_actual.variables diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index ed1d59b376d2..a98c92ce14e7 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1824,3 +1824,18 @@ class C(Generic[P]): ... c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed reveal_type(c) # N: Revealed type is "__main__.C[Any]" [builtins fixtures/paramspec.pyi] + +[case testParamSpecInferenceWithCallbackProtocol] +from typing import Protocol, Callable, ParamSpec + +class CB(Protocol): + def __call__(self, x: str, y: int) -> None: ... + +P = ParamSpec('P') +def g(fn: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + +cb: CB +g(cb, y=0, x='a') # OK +g(cb, y='a', x=0) # E: Argument "y" to "g" has incompatible type "str"; expected "int" \ + # E: Argument "x" to "g" has incompatible type "int"; expected "str" +[builtins fixtures/paramspec.pyi] From a7e0f6f8b0ec5de2fe7b804c9ac7160893ae5bf8 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Tue, 29 Aug 2023 22:13:01 -0400 Subject: [PATCH 0228/1617] Add hint for AsyncIterator incompatible return type (#15883) For issue described in #5070 and documented in #14973, add a contextual link to the docs. --- mypy/messages.py | 16 ++++++++++++++++ test-data/unit/check-async-await.test | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/mypy/messages.py b/mypy/messages.py index aab30ee29108..1933b74d27bd 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1310,6 +1310,22 @@ def return_type_incompatible_with_supertype( code=codes.OVERRIDE, ) + original = get_proper_type(original) + override = get_proper_type(override) + if ( + isinstance(original, Instance) + and isinstance(override, Instance) + and override.type.fullname == "typing.AsyncIterator" + and original.type.fullname == "typing.Coroutine" + and len(original.args) == 3 + and original.args[2] == override + ): + self.note(f'Consider declaring "{name}" in {target} without "async"', context) + self.note( + "See https://mypy.readthedocs.io/en/stable/more_types.html#asynchronous-iterators", + context, + ) + def override_target(self, name: str, name_in_super: str, supertype: str) -> str: target = f'supertype "{supertype}"' if name_in_super != name: diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 653025a0bb24..7afdbd687135 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -1021,3 +1021,19 @@ def coro() -> Generator[int, None, None]: reveal_type(coro) # N: Revealed type is "def () -> typing.AwaitableGenerator[builtins.int, None, None, typing.Generator[builtins.int, None, None]]" [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] + +[case asyncIteratorInProtocol] +from typing import AsyncIterator, Protocol + +class P(Protocol): + async def launch(self) -> AsyncIterator[int]: + raise BaseException + +class Launcher(P): + def launch(self) -> AsyncIterator[int]: # E: Return type "AsyncIterator[int]" of "launch" incompatible with return type "Coroutine[Any, Any, AsyncIterator[int]]" in supertype "P" \ + # N: Consider declaring "launch" in supertype "P" without "async" \ + # N: See https://mypy.readthedocs.io/en/stable/more_types.html#asynchronous-iterators + raise BaseException + +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] From 9a0aca14724eeef580eefcd3b340678313a1930a Mon Sep 17 00:00:00 2001 From: Max Murin Date: Wed, 30 Aug 2023 02:24:06 -0700 Subject: [PATCH 0229/1617] Update version number to 1.7.0+dev (#15989) We've cut the release branch for 1.6, so the dev version should now be 1.7. Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 512890ce7d2b..7cfc68d6e553 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.6.0+dev" +__version__ = "1.7.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From df4717ee2cbbeb9e47fbd0e60edcaa6f81bbd7bb Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 31 Aug 2023 05:39:09 -0700 Subject: [PATCH 0230/1617] Represent bottom type as Never in messages (#15996) Fixes #15950 --- mypy/checker.py | 12 +++++----- mypy/checkexpr.py | 4 ++-- mypy/expandtype.py | 2 +- mypy/meet.py | 4 ++-- mypy/messages.py | 6 ++--- mypy/solve.py | 4 ++-- mypy/typeops.py | 2 +- mypy/types.py | 2 +- test-data/unit/check-classes.test | 16 ++++++------- test-data/unit/check-dataclass-transform.test | 12 +++++----- test-data/unit/check-dataclasses.test | 8 +++---- test-data/unit/check-generic-subtyping.test | 2 +- test-data/unit/check-generics.test | 12 +++++----- test-data/unit/check-inference-context.test | 16 ++++++------- test-data/unit/check-inference.test | 24 +++++++++---------- test-data/unit/check-isinstance.test | 4 ++-- test-data/unit/check-literal.test | 4 ++-- test-data/unit/check-narrowing.test | 2 +- test-data/unit/check-native-int.test | 8 +++---- test-data/unit/check-overloading.test | 12 +++++----- .../unit/check-parameter-specification.test | 6 ++--- test-data/unit/check-plugin-attrs.test | 10 ++++---- test-data/unit/check-protocols.test | 2 +- test-data/unit/check-python310.test | 2 +- test-data/unit/check-selftype.test | 4 ++-- test-data/unit/check-singledispatch.test | 2 +- test-data/unit/check-typeddict.test | 10 ++++---- test-data/unit/check-typevar-tuple.test | 2 +- test-data/unit/check-unreachable-code.test | 10 ++++---- test-data/unit/check-varargs.test | 10 ++++---- test-data/unit/pythoneval-asyncio.test | 2 +- test-data/unit/pythoneval.test | 6 ++--- 32 files changed, 111 insertions(+), 111 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index a44601b83e21..fffa87c4f634 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3934,7 +3934,7 @@ def is_valid_defaultdict_partial_value_type(self, t: ProperType) -> bool: Examples: * t is 'int' --> True - * t is 'list[]' --> True + * t is 'list[Never]' --> True * t is 'dict[...]' --> False (only generic types with a single type argument supported) """ @@ -3980,7 +3980,7 @@ def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type x = [] # type: ignore x.append(1) # Should be ok! - We implement this here by giving x a valid type (replacing inferred with Any). + We implement this here by giving x a valid type (replacing inferred Never with Any). """ fallback = self.inference_error_fallback_type(type) self.set_inferred_type(var, lvalue, fallback) @@ -7403,7 +7403,7 @@ def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: class InvalidInferredTypes(BoolTypeQuery): """Find type components that are not valid for an inferred type. - These include type, and any types resulting from failed + These include type, and any uninhabited types resulting from failed (ambiguous) type inference. """ @@ -7424,7 +7424,7 @@ def visit_type_var(self, t: TypeVarType) -> bool: class SetNothingToAny(TypeTranslator): - """Replace all ambiguous types with Any (to avoid spurious extra errors).""" + """Replace all ambiguous Uninhabited types with Any (to avoid spurious extra errors).""" def visit_uninhabited_type(self, t: UninhabitedType) -> Type: if t.ambiguous: @@ -7432,7 +7432,7 @@ def visit_uninhabited_type(self, t: UninhabitedType) -> Type: return t def visit_type_alias_type(self, t: TypeAliasType) -> Type: - # Target of the alias cannot be an ambiguous , so we just + # Target of the alias cannot be an ambiguous UninhabitedType, so we just # replace the arguments. return t.copy_modified(args=[a.accept(self) for a in t.args]) @@ -7774,7 +7774,7 @@ def is_subtype_no_promote(left: Type, right: Type) -> bool: def is_overlapping_types_no_promote_no_uninhabited_no_none(left: Type, right: Type) -> bool: - # For the purpose of unsafe overload checks we consider list[] and list[int] + # For the purpose of unsafe overload checks we consider list[Never] and list[int] # non-overlapping. This is consistent with how we treat list[int] and list[str] as # non-overlapping, despite [] belongs to both. Also this will prevent false positives # for failed type inference during unification. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 218568007b9e..22a9852545b7 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2080,7 +2080,7 @@ def infer_function_type_arguments( ): freeze_all_type_vars(applied) return applied - # If it didn't work, erase free variables as , to avoid confusing errors. + # If it didn't work, erase free variables as uninhabited, to avoid confusing errors. unknown = UninhabitedType() unknown.ambiguous = True inferred_args = [ @@ -2444,7 +2444,7 @@ def check_argument_types( callee_arg_types = [orig_callee_arg_type] callee_arg_kinds = [ARG_STAR] else: - # TODO: Any and can appear in Unpack (as a result of user error), + # TODO: Any and Never can appear in Unpack (as a result of user error), # fail gracefully here and elsewhere (and/or normalize them away). assert isinstance(unpacked_type, Instance) assert unpacked_type.type.fullname == "builtins.tuple" diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 26353c043cb7..be8ecb9ccfd9 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -285,7 +285,7 @@ def expand_unpack(self, t: UnpackType) -> list[Type]: ): return [UnpackType(typ=repl)] elif isinstance(repl, (AnyType, UninhabitedType)): - # Replace *Ts = Any with *Ts = *tuple[Any, ...] and some for . + # Replace *Ts = Any with *Ts = *tuple[Any, ...] and some for Never. # These types may appear here as a result of user error or failed inference. return [UnpackType(t.type.tuple_fallback.copy_modified(args=[repl]))] else: diff --git a/mypy/meet.py b/mypy/meet.py index e3a22a226575..2efde4ac7588 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -968,11 +968,11 @@ def typed_dict_mapping_overlap( As usual empty, dictionaries lie in a gray area. In general, List[str] and List[str] are considered non-overlapping despite empty list belongs to both. However, List[int] - and List[] are considered overlapping. + and List[Never] are considered overlapping. So here we follow the same logic: a TypedDict with no required keys is considered non-overlapping with Mapping[str, ], but is considered overlapping with - Mapping[, ]. This way we avoid false positives for overloads, and also + Mapping[Never, Never]. This way we avoid false positives for overloads, and also avoid false positives for comparisons like SomeTypedDict == {} under --strict-equality. """ left, right = get_proper_types((left, right)) diff --git a/mypy/messages.py b/mypy/messages.py index 1933b74d27bd..cda4cda25ee4 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2068,7 +2068,7 @@ def report_protocol_problems( if supertype.type.fullname in exclusions.get(type(subtype), []): return if any(isinstance(tp, UninhabitedType) for tp in get_proper_types(supertype.args)): - # We don't want to add notes for failed inference (e.g. Iterable[]). + # We don't want to add notes for failed inference (e.g. Iterable[Never]). # This will be only confusing a user even more. return @@ -2395,7 +2395,7 @@ def quote_type_string(type_string: str) -> str: """Quotes a type representation for use in messages.""" no_quote_regex = r"^<(tuple|union): \d+ items>$" if ( - type_string in ["Module", "overloaded function", "", ""] + type_string in ["Module", "overloaded function", "Never", ""] or type_string.startswith("Module ") or re.match(no_quote_regex, type_string) is not None or type_string.endswith("?") @@ -2597,7 +2597,7 @@ def format_literal_value(typ: LiteralType) -> str: if typ.is_noreturn: return "NoReturn" else: - return "" + return "Never" elif isinstance(typ, TypeType): type_name = "type" if options.use_lowercase_names() else "Type" return f"{type_name}[{format(typ.item)}]" diff --git a/mypy/solve.py b/mypy/solve.py index 5945d97ed85a..95377ea9f93e 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -300,8 +300,8 @@ def test(x: U) -> U: ... common_upper_bound_p = get_proper_type(common_upper_bound) # We include None for when strict-optional is disabled. if isinstance(common_upper_bound_p, (UninhabitedType, NoneType)): - # This will cause to infer , which is better than a free TypeVar - # that has an upper bound . + # This will cause to infer Never, which is better than a free TypeVar + # that has an upper bound Never. return None values: list[Type] = [] diff --git a/mypy/typeops.py b/mypy/typeops.py index 0e0bc348942e..f9c1914cc9a8 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -330,7 +330,7 @@ class B(A): pass ) # Update the method signature with the solutions found. - # Technically, some constraints might be unsolvable, make them . + # Technically, some constraints might be unsolvable, make them Never. to_apply = [t if t is not None else UninhabitedType() for t in typeargs] func = expand_type(func, {tv.id: arg for tv, arg in zip(self_vars, to_apply)}) variables = [v for v in func.variables if v not in self_vars] diff --git a/mypy/types.py b/mypy/types.py index fb360fb892f1..f974157ce84d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3103,7 +3103,7 @@ def visit_none_type(self, t: NoneType) -> str: return "None" def visit_uninhabited_type(self, t: UninhabitedType) -> str: - return "" + return "Never" def visit_erased_type(self, t: ErasedType) -> str: return "" diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 04b51bb603c5..4bc1e50f7be9 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7718,13 +7718,13 @@ class D: def __init__(self) -> NoReturn: ... if object(): - reveal_type(A()) # N: Revealed type is "" + reveal_type(A()) # N: Revealed type is "Never" if object(): - reveal_type(B()) # N: Revealed type is "" + reveal_type(B()) # N: Revealed type is "Never" if object(): - reveal_type(C()) # N: Revealed type is "" + reveal_type(C()) # N: Revealed type is "Never" if object(): - reveal_type(D()) # N: Revealed type is "" + reveal_type(D()) # N: Revealed type is "Never" [case testOverloadedNewAndInitNoReturn] from typing import NoReturn, overload @@ -7764,19 +7764,19 @@ class D: def __init__(self, a: int = ...) -> None: ... if object(): - reveal_type(A()) # N: Revealed type is "" + reveal_type(A()) # N: Revealed type is "Never" reveal_type(A(1)) # N: Revealed type is "__main__.A" if object(): - reveal_type(B()) # N: Revealed type is "" + reveal_type(B()) # N: Revealed type is "Never" reveal_type(B(1)) # N: Revealed type is "__main__.B" if object(): - reveal_type(C()) # N: Revealed type is "" + reveal_type(C()) # N: Revealed type is "Never" reveal_type(C(1)) # N: Revealed type is "__main__.C" if object(): - reveal_type(D()) # N: Revealed type is "" + reveal_type(D()) # N: Revealed type is "Never" reveal_type(D(1)) # N: Revealed type is "__main__.D" [case testClassScopeImportWithWrapperAndError] diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 58cd5e5a90f8..743c7fef8aa9 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -506,7 +506,7 @@ class FunctionModel: integer_: tuple FunctionModel(string_="abc", integer_=1) -FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[, ...]"; expected "int" +FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -529,7 +529,7 @@ class FunctionModel: integer_: int FunctionModel(string_="abc", integer_=1) -FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[, ...]"; expected "int" +FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -552,7 +552,7 @@ class BaseClassModel(ModelBase): integer_: tuple BaseClassModel(string_="abc", integer_=1) -BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[, ...]"; expected "int" +BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -574,7 +574,7 @@ class BaseClassModel(ModelBase): integer_: int BaseClassModel(string_="abc", integer_=1) -BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[, ...]"; expected "int" +BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -599,7 +599,7 @@ class MetaClassModel(ModelBaseWithMeta): integer_: tuple MetaClassModel(string_="abc", integer_=1) -MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[, ...]"; expected "int" +MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -624,7 +624,7 @@ class MetaClassModel(ModelBaseWithMeta): integer_: int MetaClassModel(string_="abc", integer_=1) -MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[, ...]"; expected "int" +MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 1f5f5635de4e..8a50e7124d05 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2134,8 +2134,8 @@ T = TypeVar('T') class A(Generic[T]): x: T # exercises meet(T=int, int) = int y: bool # exercises meet(bool, int) = bool - z: str # exercises meet(str, bytes) = - w: dict # exercises meet(dict, ) = + z: str # exercises meet(str, bytes) = Never + w: dict # exercises meet(dict, Never) = Never init_var: InitVar[int] # exercises (non-optional, optional) = non-optional @dataclass @@ -2149,8 +2149,8 @@ class B: a_or_b: Union[A[int], B] _ = replace(a_or_b, x=42, y=True, init_var=42) _ = replace(a_or_b, x=42, y=True) # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]" -_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected -_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[, ]"; expected +_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected Never +_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never _ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 11c92d07021a..fd40f128ff4a 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -434,7 +434,7 @@ B(1) C(1) C('a') # E: Argument 1 to "C" has incompatible type "str"; expected "int" D(A(1)) -D(1) # E: Argument 1 to "D" has incompatible type "int"; expected "A[]" +D(1) # E: Argument 1 to "D" has incompatible type "int"; expected "A[Never]" [case testInheritedConstructor2] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 93674c0c2d5c..0781451e07ce 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -573,7 +573,7 @@ def func(x: IntNode[T]) -> IntNode[T]: return x reveal_type(func) # N: Revealed type is "def [T] (x: __main__.Node[builtins.int, T`-1]) -> __main__.Node[builtins.int, T`-1]" -func(1) # E: Argument 1 to "func" has incompatible type "int"; expected "Node[int, ]" +func(1) # E: Argument 1 to "func" has incompatible type "int"; expected "Node[int, Never]" func(Node('x', 1)) # E: Argument 1 to "Node" has incompatible type "str"; expected "int" reveal_type(func(Node(1, 'x'))) # N: Revealed type is "__main__.Node[builtins.int, builtins.str]" @@ -834,7 +834,7 @@ reveal_type(x) # N: Revealed type is "builtins.int" def f2(x: IntTP[T]) -> IntTP[T]: return x -f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, ]" +f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, Never]" reveal_type(f2((1, 'x'))) # N: Revealed type is "Tuple[builtins.int, builtins.str]" [builtins fixtures/for.pyi] @@ -904,7 +904,7 @@ n.y = 'x' # E: Incompatible types in assignment (expression has type "str", vari def f(x: Node[T, T]) -> TupledNode[T]: return Node(x.x, (x.x, x.x)) -f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Node[, ]" +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Node[Never, Never]" f(Node(1, 'x')) # E: Cannot infer type argument 1 of "f" reveal_type(Node('x', 'x')) # N: Revealed type is "a.Node[builtins.str, builtins.str]" @@ -2279,7 +2279,7 @@ class Box(Generic[T]): class IteratorBox(Box[Iterator[T]]): ... -@IteratorBox.wrap # E: Argument 1 to "wrap" of "Box" has incompatible type "Callable[[], int]"; expected "Callable[[], Iterator[]]" +@IteratorBox.wrap # E: Argument 1 to "wrap" of "Box" has incompatible type "Callable[[], int]"; expected "Callable[[], Iterator[Never]]" def g() -> int: ... [builtins fixtures/classmethod.pyi] @@ -3034,8 +3034,8 @@ def id2(x: V) -> V: reveal_type(dec1(id1)) # N: Revealed type is "def [S <: __main__.B] (S`1) -> builtins.list[S`1]" reveal_type(dec1(id2)) # N: Revealed type is "def [S in (builtins.int, builtins.str)] (S`3) -> builtins.list[S`3]" reveal_type(dec2(id1)) # N: Revealed type is "def [UC <: __main__.C] (UC`5) -> builtins.list[UC`5]" -reveal_type(dec2(id2)) # N: Revealed type is "def () -> builtins.list[]" \ - # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[], ]" +reveal_type(dec2(id2)) # N: Revealed type is "def (Never) -> builtins.list[Never]" \ + # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[Never], Never]" [case testInferenceAgainstGenericLambdas] # flags: --new-type-inference diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 5f25b007dd47..169fee65f127 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -22,7 +22,7 @@ if int(): if int(): ab = f() if int(): - b = f() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") + b = f() # E: Incompatible types in assignment (expression has type "A[Never]", variable has type "B") [case testBasicContextInferenceForConstructor] from typing import TypeVar, Generic T = TypeVar('T') @@ -37,7 +37,7 @@ if int(): if int(): ab = A() if int(): - b = A() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") + b = A() # E: Incompatible types in assignment (expression has type "A[Never]", variable has type "B") [case testIncompatibleContextInference] from typing import TypeVar, Generic T = TypeVar('T') @@ -372,7 +372,7 @@ ao: List[object] a: A def f(): a, aa, ao # Prevent redefinition -a = [] # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") +a = [] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A") aa = [] ao = [] @@ -842,7 +842,7 @@ T = TypeVar('T') def f(x: Union[List[T], str]) -> None: pass f([1]) f('') -f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[List[], str]" +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[List[Never], str]" [builtins fixtures/isinstancelist.pyi] [case testIgnoringInferenceContext] @@ -911,7 +911,7 @@ from typing import TypeVar, Callable, Generic T = TypeVar('T') class A(Generic[T]): pass -reveal_type(A()) # N: Revealed type is "__main__.A[]" +reveal_type(A()) # N: Revealed type is "__main__.A[Never]" b = reveal_type(A()) # type: A[int] # N: Revealed type is "__main__.A[builtins.int]" [case testUnionWithGenericTypeItemContext] @@ -1311,7 +1311,7 @@ from typing import List, TypeVar T = TypeVar('T', bound=int) def f(x: List[T]) -> T: ... -# mypy infers List[] here, and is a subtype of str +# mypy infers List[Never] here, and Never is a subtype of str y: str = f([]) [builtins fixtures/list.pyi] @@ -1323,7 +1323,7 @@ def f(x: List[T]) -> List[T]: ... # TODO: improve error message for such cases, see #3283 and #5706 y: List[str] = f([]) \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[str]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] @@ -1343,7 +1343,7 @@ T = TypeVar('T', bound=int) def f(x: Optional[T] = None) -> List[T]: ... y: List[str] = f() \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[str]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 56d3fe2b4ce7..36b028977591 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -876,10 +876,10 @@ g('a')() # E: "List[str]" not callable # The next line is a case where there are multiple ways to satisfy a constraint # involving a Union. Either T = List[str] or T = str would turn out to be valid, # but mypy doesn't know how to branch on these two options (and potentially have -# to backtrack later) and defaults to T = . The result is an +# to backtrack later) and defaults to T = Never. The result is an # awkward error message. Either a better error message, or simply accepting the # call, would be preferable here. -g(['a']) # E: Argument 1 to "g" has incompatible type "List[str]"; expected "List[]" +g(['a']) # E: Argument 1 to "g" has incompatible type "List[str]"; expected "List[Never]" h(g(['a'])) @@ -972,7 +972,7 @@ from typing import TypeVar, Union, List T = TypeVar('T') def f() -> List[T]: pass d1 = f() # type: Union[List[int], str] -d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "List[]", variable has type "Union[int, str]") +d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "Union[int, str]") def g(x: T) -> List[T]: pass d3 = g(1) # type: Union[List[int], List[str]] [builtins fixtures/list.pyi] @@ -3126,7 +3126,7 @@ T = TypeVar('T') def f() -> Callable[..., NoReturn]: ... x = f() -reveal_type(x) # N: Revealed type is "def (*Any, **Any) -> " +reveal_type(x) # N: Revealed type is "def (*Any, **Any) -> Never" [case testDeferralInNestedScopes] @@ -3635,8 +3635,8 @@ class Call(Protocol[T]): def f(x: Call[T]) -> Tuple[T, T]: ... def g(__x: str) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[]" +reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsPosOnly] @@ -3651,8 +3651,8 @@ class Call(Protocol[T]): def f(x: Call[T]) -> Tuple[T, T]: ... def g(*, x: str) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[]" +reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallablePosOnlyVsKwargs] @@ -3667,8 +3667,8 @@ class Call(Protocol[T]): def f(x: Call[T]) -> Tuple[T, T]: ... def g(**x: str) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[]" +reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsArgs] @@ -3683,6 +3683,6 @@ class Call(Protocol[T]): def f(x: Call[T]) -> Tuple[T, T]: ... def g(*args: str) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[, ]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[]" +reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 361d4db78752..b7ee38b69d00 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1812,9 +1812,9 @@ reveal_type(fm) # N: Revealed type is "__main__.FooMetaclass" if issubclass(fm, Foo): reveal_type(fm) # N: Revealed type is "Type[__main__.Foo]" if issubclass(fm, Bar): - reveal_type(fm) # N: Revealed type is "" + reveal_type(fm) # N: Revealed type is "Never" if issubclass(fm, Baz): - reveal_type(fm) # N: Revealed type is "" + reveal_type(fm) # N: Revealed type is "Never" [builtins fixtures/isinstance.pyi] [case testIsinstanceAndNarrowTypeVariable] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index ecd4fc0a1f00..08c709c6b777 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1794,7 +1794,7 @@ def f6(x: Optional[Literal[1]], y: Optional[Literal[2]]) -> None: pass reveal_type(unify(f1)) # N: Revealed type is "Literal[1]" if object(): - reveal_type(unify(f2)) # N: Revealed type is "" + reveal_type(unify(f2)) # N: Revealed type is "Never" reveal_type(unify(f3)) # N: Revealed type is "Literal[1]" reveal_type(unify(f4)) # N: Revealed type is "Literal[1]" reveal_type(unify(f5)) # N: Revealed type is "Literal[1]" @@ -1819,7 +1819,7 @@ T = TypeVar('T') def unify(func: Callable[[T, T], None]) -> T: pass def func(x: Literal[1], y: Literal[2]) -> None: pass -reveal_type(unify(func)) # N: Revealed type is "" +reveal_type(unify(func)) # N: Revealed type is "Never" [builtins fixtures/list.pyi] [out] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 291f73a45230..c86cffd453df 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1189,7 +1189,7 @@ def f(t: Type[T], a: A, b: B) -> None: reveal_type(a) # N: Revealed type is "__main__.A" if type(b) is t: - reveal_type(b) # N: Revealed type is "" + reveal_type(b) # N: Revealed type is "Never" else: reveal_type(b) # N: Revealed type is "__main__.B" diff --git a/test-data/unit/check-native-int.test b/test-data/unit/check-native-int.test index 30314eebcb31..2f852ca522c5 100644 --- a/test-data/unit/check-native-int.test +++ b/test-data/unit/check-native-int.test @@ -87,9 +87,9 @@ reveal_type(meet(f, f32)) # N: Revealed type is "mypy_extensions.i32" reveal_type(meet(f64, f)) # N: Revealed type is "mypy_extensions.i64" reveal_type(meet(f, f64)) # N: Revealed type is "mypy_extensions.i64" if object(): - reveal_type(meet(f32, f64)) # N: Revealed type is "" + reveal_type(meet(f32, f64)) # N: Revealed type is "Never" if object(): - reveal_type(meet(f64, f32)) # N: Revealed type is "" + reveal_type(meet(f64, f32)) # N: Revealed type is "Never" reveal_type(meet(f, fa)) # N: Revealed type is "builtins.int" reveal_type(meet(f32, fa)) # N: Revealed type is "mypy_extensions.i32" @@ -149,9 +149,9 @@ def ff(x: float) -> None: pass def fi32(x: i32) -> None: pass if object(): - reveal_type(meet(ff, fi32)) # N: Revealed type is "" + reveal_type(meet(ff, fi32)) # N: Revealed type is "Never" if object(): - reveal_type(meet(fi32, ff)) # N: Revealed type is "" + reveal_type(meet(fi32, ff)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] [case testNativeIntForLoopRange] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index ede4a2e4cf62..4546c7171856 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -3397,11 +3397,11 @@ def wrapper() -> None: # Note: These should be fine, but mypy has an unrelated bug # that makes them error out? - a2_overload: A = SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "W1[]" - a2_union: A = SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "Union[W1[], W2[]]" + a2_overload: A = SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "W1[Never]" + a2_union: A = SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "Union[W1[Never], W2[Never]]" - SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "W1[]" - SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "Union[W1[], W2[]]" + SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "W1[Never]" + SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[A]]"; expected "Union[W1[Never], W2[Never]]" [case testOverloadingInferUnionReturnWithBadObjectTypevarReturn] from typing import overload, Union, TypeVar, Generic @@ -3425,8 +3425,8 @@ class SomeType(Generic[T]): def wrapper(mysterious: T) -> T: obj1: Union[W1[A], W2[B]] - SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "W1[]" - SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "Union[W1[], W2[]]" + SomeType().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "W1[Never]" + SomeType().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "Union[W1[Never], W2[Never]]" SomeType[A]().foo(obj1) # E: Argument 1 to "foo" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "W1[A]" SomeType[A]().bar(obj1) # E: Argument 1 to "bar" of "SomeType" has incompatible type "Union[W1[A], W2[B]]"; expected "Union[W1[A], W2[A]]" diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index a98c92ce14e7..d80069644194 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1401,9 +1401,9 @@ def wrong_name_constructor(b: bool) -> SomeClass: return SomeClass("a") func(SomeClass, constructor) -reveal_type(func(SomeClass, wrong_constructor)) # N: Revealed type is "def (a: ) -> __main__.SomeClass" -reveal_type(func_regular(SomeClass, wrong_constructor)) # N: Revealed type is "def () -> __main__.SomeClass" -func(SomeClass, wrong_name_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[], SomeClass]" +reveal_type(func(SomeClass, wrong_constructor)) # N: Revealed type is "def (a: Never) -> __main__.SomeClass" +reveal_type(func_regular(SomeClass, wrong_constructor)) # N: Revealed type is "def (Never) -> __main__.SomeClass" +func(SomeClass, wrong_name_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[Never], SomeClass]" [builtins fixtures/paramspec.pyi] [case testParamSpecInTypeAliasBasic] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 1465bab2bb7b..fb5f1f9472c2 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1181,7 +1181,7 @@ def my_factory() -> int: return 7 @attr.s class A: - x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[]", variable has type "int") + x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "int") y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/list.pyi] @@ -2131,8 +2131,8 @@ T = TypeVar('T') class A(Generic[T]): x: T # exercises meet(T=int, int) = int y: bool # exercises meet(bool, int) = bool - z: str # exercises meet(str, bytes) = - w: dict # exercises meet(dict, ) = + z: str # exercises meet(str, bytes) = Never + w: dict # exercises meet(dict, Never) = Never @attrs.define @@ -2144,8 +2144,8 @@ class B: a_or_b: A[int] | B a2 = attrs.evolve(a_or_b, x=42, y=True) -a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected -a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[, ]"; expected +a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected Never +a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never [builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index dba01be50fee..e73add454a67 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -928,7 +928,7 @@ class L: def last(seq: Linked[T]) -> T: pass -last(L()) # E: Argument 1 to "last" has incompatible type "L"; expected "Linked[]" +last(L()) # E: Argument 1 to "last" has incompatible type "L"; expected "Linked[Never]" [case testMutuallyRecursiveProtocols] from typing import Protocol, Sequence, List diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 0fe6a3d5a5cc..f81da23d148c 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1144,7 +1144,7 @@ m: str match m: case a if a := 1: # E: Incompatible types in assignment (expression has type "int", variable has type "str") - reveal_type(a) # N: Revealed type is "" + reveal_type(a) # N: Revealed type is "Never" [case testMatchAssigningPatternGuard] m: str diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index d5024412ca97..bf7a928ff51d 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1520,7 +1520,7 @@ from typing import Self, TypeVar, Tuple T = TypeVar("T") class C: def meth(self: T) -> Tuple[Self, T]: ... # E: Method cannot have explicit self annotation and Self type -reveal_type(C().meth()) # N: Revealed type is "Tuple[, __main__.C]" +reveal_type(C().meth()) # N: Revealed type is "Tuple[Never, __main__.C]" [builtins fixtures/property.pyi] [case testTypingSelfProperty] @@ -1558,7 +1558,7 @@ class C: class D(C): ... reveal_type(D.meth()) # N: Revealed type is "__main__.D" -reveal_type(D.bad()) # N: Revealed type is "" +reveal_type(D.bad()) # N: Revealed type is "Never" [builtins fixtures/classmethod.pyi] [case testTypingSelfOverload] diff --git a/test-data/unit/check-singledispatch.test b/test-data/unit/check-singledispatch.test index 1adec1575b7e..e63d4c073e86 100644 --- a/test-data/unit/check-singledispatch.test +++ b/test-data/unit/check-singledispatch.test @@ -300,7 +300,7 @@ h('a', 1) # E: Argument 2 to "h" has incompatible type "int"; expected "str" [case testDontCrashWhenRegisteringAfterError] import functools -a = functools.singledispatch('a') # E: Need type annotation for "a" # E: Argument 1 to "singledispatch" has incompatible type "str"; expected "Callable[..., ]" +a = functools.singledispatch('a') # E: Need type annotation for "a" # E: Argument 1 to "singledispatch" has incompatible type "str"; expected "Callable[..., Never]" @a.register(int) def default(val) -> int: diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 7de8e6416f35..b8953f05b6a5 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -602,7 +602,7 @@ YbZ = TypedDict('YbZ', {'y': object, 'z': int}) T = TypeVar('T') def f(x: Callable[[T, T], None]) -> T: pass def g(x: XYa, y: YbZ) -> None: pass -reveal_type(f(g)) # N: Revealed type is "" +reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] [case testMeetOfTypedDictsWithNoCommonKeysHasAllKeysAndNewFallback] @@ -625,7 +625,7 @@ M = Mapping[str, int] T = TypeVar('T') def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass -reveal_type(f(g)) # N: Revealed type is "" +reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] [case testMeetOfTypedDictWithIncompatibleMappingIsUninhabited] @@ -636,7 +636,7 @@ M = Mapping[str, str] T = TypeVar('T') def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass -reveal_type(f(g)) # N: Revealed type is "" +reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] [case testMeetOfTypedDictWithCompatibleMappingSuperclassIsUninhabitedForNow] @@ -680,7 +680,7 @@ YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') def f(x: Callable[[T, T], None]) -> T: pass def g(x: XY, y: YZ) -> None: pass -reveal_type(f(g)) # N: Revealed type is "" +reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] @@ -1856,7 +1856,7 @@ class Config(TypedDict): b: str x: Config -x == {} # E: Non-overlapping equality check (left operand type: "Config", right operand type: "Dict[, ]") +x == {} # E: Non-overlapping equality check (left operand type: "Config", right operand type: "Dict[Never, Never]") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index c8b33ec96b06..f7faab4818c9 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -17,7 +17,7 @@ reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[, ...]" +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Never, ...]" def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: return a diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 20b5dea9fc87..acb5ca6ea609 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -900,15 +900,15 @@ from typing_extensions import NoReturn as TENoReturn from mypy_extensions import NoReturn as MENoReturn bottom1: Never -reveal_type(bottom1) # N: Revealed type is "" +reveal_type(bottom1) # N: Revealed type is "Never" bottom2: TENever -reveal_type(bottom2) # N: Revealed type is "" +reveal_type(bottom2) # N: Revealed type is "Never" bottom3: NoReturn -reveal_type(bottom3) # N: Revealed type is "" +reveal_type(bottom3) # N: Revealed type is "Never" bottom4: TENoReturn -reveal_type(bottom4) # N: Revealed type is "" +reveal_type(bottom4) # N: Revealed type is "Never" bottom5: MENoReturn -reveal_type(bottom5) # N: Revealed type is "" +reveal_type(bottom5) # N: Revealed type is "Never" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index fe09fb43c97c..54546f3973b3 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -604,27 +604,27 @@ class B: pass if int(): a, aa = G().f(*[a]) \ # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant if int(): - aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") + aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A") if int(): ab, aa = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant \ # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" if int(): ao, ao = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[object]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[object]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant if int(): aa, aa = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index 556414cf3252..069374b9635c 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -472,7 +472,7 @@ async def bad(arg: P) -> T: pass [out] _program.py:8: note: Revealed type is "def [T] (arg: P?) -> typing.Coroutine[Any, Any, T`-1]" -_program.py:9: error: Value of type "Coroutine[Any, Any, ]" must be used +_program.py:9: error: Value of type "Coroutine[Any, Any, Never]" must be used _program.py:9: note: Are you missing an await? _program.py:11: error: Variable "_testForwardRefToBadAsyncShouldNotCrash_newsemanal.P" is not valid as a type _program.py:11: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 58dfb172cf76..c5be30eac1b7 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -863,8 +863,8 @@ MyDDict(dict)[0] _program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Optional[Callable[[], str]]" _program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" _program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str") -_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[]]"; expected "defaultdict[int, List[]]" -_program.py:24: error: Invalid index type "str" for "MyDDict[Dict[, ]]"; expected type "int" +_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[Never]]"; expected "defaultdict[int, List[Never]]" +_program.py:24: error: Invalid index type "str" for "MyDDict[Dict[Never, Never]]"; expected type "int" [case testNoSubcriptionOfStdlibCollections] # flags: --python-version 3.7 @@ -2004,7 +2004,7 @@ Foo().__dict__ = {} _testInferenceOfDunderDictOnClassObjects.py:2: note: Revealed type is "types.MappingProxyType[builtins.str, Any]" _testInferenceOfDunderDictOnClassObjects.py:3: note: Revealed type is "builtins.dict[builtins.str, Any]" _testInferenceOfDunderDictOnClassObjects.py:4: error: Property "__dict__" defined in "type" is read-only -_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[, ]", variable has type "MappingProxyType[str, Any]") +_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[Never, Never]", variable has type "MappingProxyType[str, Any]") [case testTypeVarTuple] # flags: --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack --python-version=3.11 From 2a6d9cbc45eba360934ddee7b43c607b3edb3095 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 00:37:41 -0700 Subject: [PATCH 0231/1617] Sync typeshed (#16009) Source commit: https://github.com/python/typeshed/commit/f28cb8b8562ccc382d018129ba4886f241c6db9c --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/asyncio/taskgroups.pyi | 7 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 30 +++++--- mypy/typeshed/stdlib/configparser.pyi | 81 ++++++++++++++------- mypy/typeshed/stdlib/csv.pyi | 2 +- mypy/typeshed/stdlib/enum.pyi | 6 +- mypy/typeshed/stdlib/genericpath.pyi | 8 +- mypy/typeshed/stdlib/gzip.pyi | 6 +- mypy/typeshed/stdlib/ntpath.pyi | 5 ++ mypy/typeshed/stdlib/os/__init__.pyi | 9 ++- mypy/typeshed/stdlib/posixpath.pyi | 9 +++ mypy/typeshed/stdlib/pydoc.pyi | 4 +- mypy/typeshed/stdlib/ssl.pyi | 10 ++- mypy/typeshed/stdlib/tempfile.pyi | 2 +- mypy/typeshed/stdlib/unittest/__init__.pyi | 4 +- mypy/typeshed/stdlib/unittest/loader.pyi | 7 +- mypy/typeshed/stdlib/unittest/main.pyi | 54 ++++++++++---- mypy/typeshed/stdlib/unittest/result.pyi | 7 ++ mypy/typeshed/stdlib/unittest/runner.pyi | 62 ++++++++++++---- 19 files changed, 233 insertions(+), 86 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 756ee86d3342..165bb5337784 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -69,7 +69,7 @@ class _CData(metaclass=_CDataMeta): def __buffer__(self, __flags: int) -> memoryview: ... def __release_buffer__(self, __buffer: memoryview) -> None: ... -class _SimpleCData(Generic[_T], _CData): +class _SimpleCData(_CData, Generic[_T]): value: _T # The TypeVar can be unsolved here, # but we can't use overloads without creating many, many mypy false-positive errors @@ -78,7 +78,7 @@ class _SimpleCData(Generic[_T], _CData): class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... -class _Pointer(Generic[_CT], _PointerLike, _CData): +class _Pointer(_PointerLike, _CData, Generic[_CT]): _type_: type[_CT] contents: _CT @overload @@ -140,7 +140,7 @@ class _StructUnionBase(_CData, metaclass=_StructUnionMeta): class Union(_StructUnionBase): ... class Structure(_StructUnionBase): ... -class Array(Generic[_CT], _CData): +class Array(_CData, Generic[_CT]): @property @abstractmethod def _length_(self) -> int: ... diff --git a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi index 47d9bb2f699e..aec3f1127f15 100644 --- a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi +++ b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi @@ -1,10 +1,11 @@ import sys from contextvars import Context from types import TracebackType -from typing import TypeVar +from typing import Any, TypeVar from typing_extensions import Self from . import _CoroutineLike +from .events import AbstractEventLoop from .tasks import Task if sys.version_info >= (3, 12): @@ -15,6 +16,10 @@ else: _T = TypeVar("_T") class TaskGroup: + _loop: AbstractEventLoop | None + _tasks: set[Task[Any]] + async def __aenter__(self) -> Self: ... async def __aexit__(self, et: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> None: ... def create_task(self, coro: _CoroutineLike[_T], *, name: str | None = None, context: Context | None = None) -> Task[_T]: ... + def _on_task_done(self, task: Task[object]) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 5ea30d3791de..3bc65e3703c5 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -243,12 +243,6 @@ if sys.version_info >= (3, 10): async def sleep(delay: float) -> None: ... @overload async def sleep(delay: float, result: _T) -> _T: ... - @overload - async def wait(fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED") -> tuple[set[_FT], set[_FT]]: ... # type: ignore[misc] - @overload - async def wait( - fs: Iterable[Awaitable[_T]], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" - ) -> tuple[set[Task[_T]], set[Task[_T]]]: ... async def wait_for(fut: _FutureLike[_T], timeout: float | None) -> _T: ... else: @@ -257,6 +251,25 @@ else: async def sleep(delay: float, *, loop: AbstractEventLoop | None = None) -> None: ... @overload async def sleep(delay: float, result: _T, *, loop: AbstractEventLoop | None = None) -> _T: ... + async def wait_for(fut: _FutureLike[_T], timeout: float | None, *, loop: AbstractEventLoop | None = None) -> _T: ... + +if sys.version_info >= (3, 11): + @overload + async def wait(fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED") -> tuple[set[_FT], set[_FT]]: ... # type: ignore[misc] + @overload + async def wait( + fs: Iterable[Task[_T]], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> tuple[set[Task[_T]], set[Task[_T]]]: ... + +elif sys.version_info >= (3, 10): + @overload + async def wait(fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED") -> tuple[set[_FT], set[_FT]]: ... # type: ignore[misc] + @overload + async def wait( + fs: Iterable[Awaitable[_T]], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> tuple[set[Task[_T]], set[Task[_T]]]: ... + +else: @overload async def wait( # type: ignore[misc] fs: Iterable[_FT], @@ -273,7 +286,6 @@ else: timeout: float | None = None, return_when: str = "ALL_COMPLETED", ) -> tuple[set[Task[_T]], set[Task[_T]]]: ... - async def wait_for(fut: _FutureLike[_T], timeout: float | None, *, loop: AbstractEventLoop | None = None) -> _T: ... if sys.version_info >= (3, 12): _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co] @@ -291,7 +303,7 @@ class Task(Future[_T_co], Generic[_T_co]): # type: ignore[type-var] # pyright: coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., - name: str | None, + name: str | None = ..., context: Context | None = None, eager_start: bool = False, ) -> None: ... @@ -301,7 +313,7 @@ class Task(Future[_T_co], Generic[_T_co]): # type: ignore[type-var] # pyright: coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., - name: str | None, + name: str | None = ..., context: Context | None = None, ) -> None: ... elif sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index 6f9f788310d1..e6fedb0328c2 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -5,31 +5,53 @@ from re import Pattern from typing import Any, ClassVar, TypeVar, overload from typing_extensions import Literal, TypeAlias -__all__ = [ - "NoSectionError", - "DuplicateOptionError", - "DuplicateSectionError", - "NoOptionError", - "InterpolationError", - "InterpolationDepthError", - "InterpolationMissingOptionError", - "InterpolationSyntaxError", - "ParsingError", - "MissingSectionHeaderError", - "ConfigParser", - "RawConfigParser", - "Interpolation", - "BasicInterpolation", - "ExtendedInterpolation", - "LegacyInterpolation", - "SectionProxy", - "ConverterMapping", - "DEFAULTSECT", - "MAX_INTERPOLATION_DEPTH", -] - -if sys.version_info < (3, 12): - __all__ += ["SafeConfigParser"] +if sys.version_info >= (3, 12): + __all__ = ( + "NoSectionError", + "DuplicateOptionError", + "DuplicateSectionError", + "NoOptionError", + "InterpolationError", + "InterpolationDepthError", + "InterpolationMissingOptionError", + "InterpolationSyntaxError", + "ParsingError", + "MissingSectionHeaderError", + "ConfigParser", + "RawConfigParser", + "Interpolation", + "BasicInterpolation", + "ExtendedInterpolation", + "LegacyInterpolation", + "SectionProxy", + "ConverterMapping", + "DEFAULTSECT", + "MAX_INTERPOLATION_DEPTH", + ) +else: + __all__ = [ + "NoSectionError", + "DuplicateOptionError", + "DuplicateSectionError", + "NoOptionError", + "InterpolationError", + "InterpolationDepthError", + "InterpolationMissingOptionError", + "InterpolationSyntaxError", + "ParsingError", + "MissingSectionHeaderError", + "ConfigParser", + "SafeConfigParser", + "RawConfigParser", + "Interpolation", + "BasicInterpolation", + "ExtendedInterpolation", + "LegacyInterpolation", + "SectionProxy", + "ConverterMapping", + "DEFAULTSECT", + "MAX_INTERPOLATION_DEPTH", + ] _Section: TypeAlias = Mapping[str, str] _Parser: TypeAlias = MutableMapping[str, _Section] @@ -128,7 +150,8 @@ class RawConfigParser(_Parser): def read_file(self, f: Iterable[str], source: str | None = None) -> None: ... def read_string(self, string: str, source: str = "") -> None: ... def read_dict(self, dictionary: Mapping[str, Mapping[str, Any]], source: str = "") -> None: ... - def readfp(self, fp: Iterable[str], filename: str | None = None) -> None: ... + if sys.version_info < (3, 12): + def readfp(self, fp: Iterable[str], filename: str | None = None) -> None: ... # These get* methods are partially applied (with the same names) in # SectionProxy; the stubs should be kept updated together @overload @@ -277,7 +300,11 @@ class InterpolationSyntaxError(InterpolationError): ... class ParsingError(Error): source: str errors: list[tuple[int, str]] - def __init__(self, source: str | None = None, filename: str | None = None) -> None: ... + if sys.version_info >= (3, 12): + def __init__(self, source: str) -> None: ... + else: + def __init__(self, source: str | None = None, filename: str | None = None) -> None: ... + def append(self, lineno: int, line: str) -> None: ... class MissingSectionHeaderError(ParsingError): diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 139ba7af2208..a9c7fe0492c8 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -69,7 +69,7 @@ class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... -class DictReader(Generic[_T], Iterator[_DictReadMapping[_T | Any, str | Any]]): +class DictReader(Iterator[_DictReadMapping[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None restkey: str | None restval: str | None diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index a8ba7bf157c2..e6eaf6c413dc 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -33,7 +33,7 @@ if sys.version_info >= (3, 11): "verify", ] -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 11): __all__ += ["pickle_by_enum_name", "pickle_by_global_name"] _EnumMemberT = TypeVar("_EnumMemberT") @@ -188,7 +188,7 @@ class Enum(metaclass=EnumMeta): def __hash__(self) -> int: ... def __format__(self, format_spec: str) -> str: ... def __reduce_ex__(self, proto: Unused) -> tuple[Any, ...]: ... - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 11): def __copy__(self) -> Self: ... def __deepcopy__(self, memo: Any) -> Self: ... @@ -294,6 +294,6 @@ class auto(IntFlag): def value(self) -> Any: ... def __new__(cls) -> Self: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 11): def pickle_by_global_name(self: Enum, proto: int) -> str: ... def pickle_by_enum_name(self: _EnumMemberT, proto: int) -> tuple[Callable[..., Any], tuple[type[_EnumMemberT], str]]: ... diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi index 46426b63c852..be08f7a3cb79 100644 --- a/mypy/typeshed/stdlib/genericpath.pyi +++ b/mypy/typeshed/stdlib/genericpath.pyi @@ -1,5 +1,6 @@ import os -from _typeshed import BytesPath, FileDescriptorOrPath, StrPath, SupportsRichComparisonT +import sys +from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRichComparisonT from collections.abc import Sequence from typing import overload from typing_extensions import Literal, LiteralString @@ -17,6 +18,8 @@ __all__ = [ "sameopenfile", "samestat", ] +if sys.version_info >= (3, 12): + __all__ += ["islink"] # All overloads can return empty string. Ideally, Literal[""] would be a valid # Iterable[T], so that list[T] | Literal[""] could be used as a return @@ -36,6 +39,9 @@ def getsize(filename: FileDescriptorOrPath) -> int: ... def isfile(path: FileDescriptorOrPath) -> bool: ... def isdir(s: FileDescriptorOrPath) -> bool: ... +if sys.version_info >= (3, 12): + def islink(path: StrOrBytesPath) -> bool: ... + # These return float if os.stat_float_times() == True, # but int is a subclass of float. def getatime(filename: FileDescriptorOrPath) -> float: ... diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 1ec8b4b8ca7c..d001849e609c 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -139,8 +139,10 @@ class GzipFile(_compression.BaseStream): fileobj: _ReadableFileobj | _WritableFileobj | None = None, mtime: float | None = None, ) -> None: ... - @property - def filename(self) -> str: ... + if sys.version_info < (3, 12): + @property + def filename(self) -> str: ... + @property def mtime(self) -> int | None: ... crc: int diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi index f1fa137c6d88..1a58b52de050 100644 --- a/mypy/typeshed/stdlib/ntpath.pyi +++ b/mypy/typeshed/stdlib/ntpath.pyi @@ -42,6 +42,9 @@ from posixpath import ( splitext as splitext, supports_unicode_filenames as supports_unicode_filenames, ) + +if sys.version_info >= (3, 12): + from posixpath import isjunction as isjunction, splitroot as splitroot from typing import AnyStr, overload from typing_extensions import LiteralString @@ -85,6 +88,8 @@ __all__ = [ "samestat", "commonpath", ] +if sys.version_info >= (3, 12): + __all__ += ["isjunction", "splitroot"] altsep: LiteralString diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 994595aae781..961858ce3c19 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -388,6 +388,8 @@ class DirEntry(Generic[AnyStr]): def __fspath__(self) -> AnyStr: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... + if sys.version_info >= (3, 12): + def is_junction(self) -> bool: ... @final class statvfs_result(structseq[int], tuple[int, int, int, int, int, int, int, int, int, int, int]): @@ -602,7 +604,12 @@ def isatty(__fd: int) -> bool: ... if sys.platform != "win32" and sys.version_info >= (3, 11): def login_tty(__fd: int) -> None: ... -def lseek(__fd: int, __position: int, __how: int) -> int: ... +if sys.version_info >= (3, 11): + def lseek(__fd: int, __position: int, __whence: int) -> int: ... + +else: + def lseek(__fd: int, __position: int, __how: int) -> int: ... + def open(path: StrOrBytesPath, flags: int, mode: int = 0o777, *, dir_fd: int | None = None) -> int: ... def pipe() -> tuple[int, int]: ... def read(__fd: int, __length: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index 1945190be5f8..45a8ad7ec6a4 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -58,6 +58,8 @@ __all__ = [ "relpath", "commonpath", ] +if sys.version_info >= (3, 12): + __all__ += ["isjunction", "splitroot"] supports_unicode_filenames: bool # aliases (also in os) @@ -150,3 +152,10 @@ def isabs(s: StrOrBytesPath) -> bool: ... def islink(path: FileDescriptorOrPath) -> bool: ... def ismount(path: FileDescriptorOrPath) -> bool: ... def lexists(path: FileDescriptorOrPath) -> bool: ... + +if sys.version_info >= (3, 12): + def isjunction(path: StrOrBytesPath) -> bool: ... + @overload + def splitroot(p: AnyOrLiteralStr) -> tuple[AnyOrLiteralStr, AnyOrLiteralStr, AnyOrLiteralStr]: ... + @overload + def splitroot(p: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr, AnyStr]: ... diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index 7791c977aa8b..1b09bcb059e4 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -198,7 +198,7 @@ def render_doc( thing: str | object, title: str = "Python Library Documentation: %s", forceload: bool = ..., renderer: Doc | None = None ) -> str: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 11): def doc( thing: str | object, title: str = "Python Library Documentation: %s", @@ -230,7 +230,7 @@ class Helper: def __call__(self, request: str | Helper | object = ...) -> None: ... def interact(self) -> None: ... def getline(self, prompt: str) -> str: ... - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 11): def help(self, request: Any, is_cli: bool = False) -> None: ... else: def help(self, request: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 1c49b130e48f..73762cd75e79 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -201,12 +201,13 @@ class Options(enum.IntFlag): OP_NO_RENEGOTIATION: int if sys.version_info >= (3, 8): OP_ENABLE_MIDDLEBOX_COMPAT: int - if sys.platform == "linux": - OP_IGNORE_UNEXPECTED_EOF: int if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: int if sys.version_info >= (3, 12) and sys.platform != "linux": OP_ENABLE_KTLS: int + if sys.version_info >= (3, 11): + OP_IGNORE_UNEXPECTED_EOF: int + elif sys.version_info >= (3, 8) and sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: int OP_ALL: Options @@ -224,12 +225,13 @@ OP_NO_TICKET: Options OP_NO_RENEGOTIATION: Options if sys.version_info >= (3, 8): OP_ENABLE_MIDDLEBOX_COMPAT: Options - if sys.platform == "linux": - OP_IGNORE_UNEXPECTED_EOF: Options if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: Options if sys.version_info >= (3, 12) and sys.platform != "linux": OP_ENABLE_KTLS: Options +if sys.version_info >= (3, 11): + OP_IGNORE_UNEXPECTED_EOF: Options +elif sys.version_info >= (3, 8) and sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: Options HAS_NEVER_CHECK_COMMON_NAME: bool diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index ea04303683b5..61bcde24255b 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -321,7 +321,7 @@ else: dir: GenericPath[AnyStr] | None = None, ) -> IO[Any]: ... -class _TemporaryFileWrapper(Generic[AnyStr], IO[AnyStr]): +class _TemporaryFileWrapper(IO[AnyStr], Generic[AnyStr]): file: IO[AnyStr] # io.TextIOWrapper, io.BufferedReader or io.BufferedWriter name: str delete: bool diff --git a/mypy/typeshed/stdlib/unittest/__init__.pyi b/mypy/typeshed/stdlib/unittest/__init__.pyi index 33820c793fa5..f96d6fb185c5 100644 --- a/mypy/typeshed/stdlib/unittest/__init__.pyi +++ b/mypy/typeshed/stdlib/unittest/__init__.pyi @@ -65,5 +65,7 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 11): __all__ += ["enterModuleContext", "doModuleCleanups"] -def load_tests(loader: TestLoader, tests: TestSuite, pattern: str | None) -> TestSuite: ... +if sys.version_info < (3, 12): + def load_tests(loader: TestLoader, tests: TestSuite, pattern: str | None) -> TestSuite: ... + def __dir__() -> set[str]: ... diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index f3850c939d07..202309ac1d93 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -1,3 +1,4 @@ +import sys import unittest.case import unittest.suite from collections.abc import Callable, Sequence @@ -18,7 +19,11 @@ class TestLoader: testNamePatterns: list[str] | None suiteClass: _SuiteClass def loadTestsFromTestCase(self, testCaseClass: type[unittest.case.TestCase]) -> unittest.suite.TestSuite: ... - def loadTestsFromModule(self, module: ModuleType, *args: Any, pattern: Any = None) -> unittest.suite.TestSuite: ... + if sys.version_info >= (3, 12): + def loadTestsFromModule(self, module: ModuleType, *, pattern: str | None = None) -> unittest.suite.TestSuite: ... + else: + def loadTestsFromModule(self, module: ModuleType, *args: Any, pattern: str | None = None) -> unittest.suite.TestSuite: ... + def loadTestsFromName(self, name: str, module: ModuleType | None = None) -> unittest.suite.TestSuite: ... def loadTestsFromNames(self, names: Sequence[str], module: ModuleType | None = None) -> unittest.suite.TestSuite: ... def getTestCaseNames(self, testCaseClass: type[unittest.case.TestCase]) -> Sequence[str]: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 6d970c920096..d29e9a2b8da8 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -1,3 +1,4 @@ +import sys import unittest.case import unittest.loader import unittest.result @@ -23,22 +24,43 @@ class TestProgram: progName: str | None warnings: str | None testNamePatterns: list[str] | None - def __init__( - self, - module: None | str | ModuleType = "__main__", - defaultTest: str | Iterable[str] | None = None, - argv: list[str] | None = None, - testRunner: type[_TestRunner] | _TestRunner | None = None, - testLoader: unittest.loader.TestLoader = ..., - exit: bool = True, - verbosity: int = 1, - failfast: bool | None = None, - catchbreak: bool | None = None, - buffer: bool | None = None, - warnings: str | None = None, - *, - tb_locals: bool = False, - ) -> None: ... + if sys.version_info >= (3, 12): + durations: unittest.result._DurationsType | None + def __init__( + self, + module: None | str | ModuleType = "__main__", + defaultTest: str | Iterable[str] | None = None, + argv: list[str] | None = None, + testRunner: type[_TestRunner] | _TestRunner | None = None, + testLoader: unittest.loader.TestLoader = ..., + exit: bool = True, + verbosity: int = 1, + failfast: bool | None = None, + catchbreak: bool | None = None, + buffer: bool | None = None, + warnings: str | None = None, + *, + tb_locals: bool = False, + durations: unittest.result._DurationsType | None = None, + ) -> None: ... + else: + def __init__( + self, + module: None | str | ModuleType = "__main__", + defaultTest: str | Iterable[str] | None = None, + argv: list[str] | None = None, + testRunner: type[_TestRunner] | _TestRunner | None = None, + testLoader: unittest.loader.TestLoader = ..., + exit: bool = True, + verbosity: int = 1, + failfast: bool | None = None, + catchbreak: bool | None = None, + buffer: bool | None = None, + warnings: str | None = None, + *, + tb_locals: bool = False, + ) -> None: ... + def usageExit(self, msg: Any = None) -> None: ... def parseArgs(self, argv: list[str]) -> None: ... def createTests(self, from_discovery: bool = False, Loader: unittest.loader.TestLoader | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/result.pyi b/mypy/typeshed/stdlib/unittest/result.pyi index 8d78bc0f7dcf..dfc505936f59 100644 --- a/mypy/typeshed/stdlib/unittest/result.pyi +++ b/mypy/typeshed/stdlib/unittest/result.pyi @@ -1,9 +1,12 @@ +import sys import unittest.case from _typeshed import OptExcInfo from collections.abc import Callable from typing import Any, TextIO, TypeVar +from typing_extensions import TypeAlias _F = TypeVar("_F", bound=Callable[..., Any]) +_DurationsType: TypeAlias = list[tuple[str, float]] STDOUT_LINE: str STDERR_LINE: str @@ -22,6 +25,8 @@ class TestResult: buffer: bool failfast: bool tb_locals: bool + if sys.version_info >= (3, 12): + collectedDurations: _DurationsType def __init__(self, stream: TextIO | None = None, descriptions: bool | None = None, verbosity: int | None = None) -> None: ... def printErrors(self) -> None: ... def wasSuccessful(self) -> bool: ... @@ -37,3 +42,5 @@ class TestResult: def addExpectedFailure(self, test: unittest.case.TestCase, err: OptExcInfo) -> None: ... def addUnexpectedSuccess(self, test: unittest.case.TestCase) -> None: ... def addSubTest(self, test: unittest.case.TestCase, subtest: unittest.case.TestCase, err: OptExcInfo | None) -> None: ... + if sys.version_info >= (3, 12): + def addDuration(self, test: unittest.case.TestCase, elapsed: float) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index c0ddcdb49208..0033083ac406 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -1,6 +1,8 @@ +import sys import unittest.case import unittest.result import unittest.suite +from _typeshed import Incomplete from collections.abc import Callable, Iterable from typing import TextIO from typing_extensions import TypeAlias @@ -14,23 +16,57 @@ class TextTestResult(unittest.result.TestResult): separator2: str showAll: bool # undocumented stream: TextIO # undocumented - def __init__(self, stream: TextIO, descriptions: bool, verbosity: int) -> None: ... + if sys.version_info >= (3, 12): + durations: unittest.result._DurationsType | None + def __init__( + self, stream: TextIO, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None + ) -> None: ... + else: + def __init__(self, stream: TextIO, descriptions: bool, verbosity: int) -> None: ... + def getDescription(self, test: unittest.case.TestCase) -> str: ... def printErrorList(self, flavour: str, errors: Iterable[tuple[unittest.case.TestCase, str]]) -> None: ... class TextTestRunner: resultclass: _ResultClassType - def __init__( - self, - stream: TextIO | None = None, - descriptions: bool = True, - verbosity: int = 1, - failfast: bool = False, - buffer: bool = False, - resultclass: _ResultClassType | None = None, - warnings: type[Warning] | None = None, - *, - tb_locals: bool = False, - ) -> None: ... + # TODO: add `_WritelnDecorator` type + # stream: _WritelnDecorator + stream: Incomplete + descriptions: bool + verbosity: int + failfast: bool + buffer: bool + warnings: str | None + tb_locals: bool + + if sys.version_info >= (3, 12): + durations: unittest.result._DurationsType | None + def __init__( + self, + stream: TextIO | None = None, + descriptions: bool = True, + verbosity: int = 1, + failfast: bool = False, + buffer: bool = False, + resultclass: _ResultClassType | None = None, + warnings: str | None = None, + *, + tb_locals: bool = False, + durations: unittest.result._DurationsType | None = None, + ) -> None: ... + else: + def __init__( + self, + stream: TextIO | None = None, + descriptions: bool = True, + verbosity: int = 1, + failfast: bool = False, + buffer: bool = False, + resultclass: _ResultClassType | None = None, + warnings: str | None = None, + *, + tb_locals: bool = False, + ) -> None: ... + def _makeResult(self) -> unittest.result.TestResult: ... def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... From d440490270b643b2be333b5b27b154813f016ab6 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 1 Sep 2023 03:40:24 -0700 Subject: [PATCH 0232/1617] Deduplicate iterable logic (#16006) This e.g. makes sure both code paths receive my fix in #15688 --- mypy/checker.py | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index fffa87c4f634..fa7c645873d0 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -41,7 +41,6 @@ from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode from mypy.errors import Errors, ErrorWatcher, report_internal_error from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance -from mypy.join import join_types from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash from mypy.maptype import map_instance_to_supertype from mypy.meet import is_overlapping_erased_types, is_overlapping_types @@ -4653,42 +4652,32 @@ def analyze_async_iterable_item_type(self, expr: Expression) -> tuple[Type, Type def analyze_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: """Analyse iterable expression and return iterator and iterator item types.""" - echk = self.expr_checker - iterable = get_proper_type(echk.accept(expr)) - iterator = echk.check_method_call_by_name("__iter__", iterable, [], [], expr)[0] - + iterator, iterable = self.analyze_iterable_item_type_without_expression( + self.expr_checker.accept(expr), context=expr + ) int_type = self.analyze_range_native_int_type(expr) if int_type: return iterator, int_type - - if ( - isinstance(iterable, TupleType) - and iterable.partial_fallback.type.fullname == "builtins.tuple" - ): - return iterator, tuple_fallback(iterable).args[0] - else: - # Non-tuple iterable. - return iterator, echk.check_method_call_by_name("__next__", iterator, [], [], expr)[0] + return iterator, iterable def analyze_iterable_item_type_without_expression( self, type: Type, context: Context ) -> tuple[Type, Type]: """Analyse iterable type and return iterator and iterator item types.""" echk = self.expr_checker + iterable: Type iterable = get_proper_type(type) iterator = echk.check_method_call_by_name("__iter__", iterable, [], [], context)[0] - if isinstance(iterable, TupleType): - joined: Type = UninhabitedType() - for item in iterable.items: - joined = join_types(joined, item) - return iterator, joined + if ( + isinstance(iterable, TupleType) + and iterable.partial_fallback.type.fullname == "builtins.tuple" + ): + return iterator, tuple_fallback(iterable).args[0] else: # Non-tuple iterable. - return ( - iterator, - echk.check_method_call_by_name("__next__", iterator, [], [], context)[0], - ) + iterable = echk.check_method_call_by_name("__next__", iterator, [], [], context)[0] + return iterator, iterable def analyze_range_native_int_type(self, expr: Expression) -> Type | None: """Try to infer native int item type from arguments to range(...). From 803f61097b0eba6505c976d72ce2176b8c64d987 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 1 Sep 2023 15:33:01 -0700 Subject: [PATCH 0233/1617] Fix crash when parsing error code config with typo (#16005) Fixes #16002 --- mypy/config_parser.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a84f3594a0d2..7748c3b25966 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -434,11 +434,26 @@ def parse_section( """ results: dict[str, object] = {} report_dirs: dict[str, str] = {} + + # Because these fields exist on Options, without proactive checking, we would accept them + # and crash later + invalid_options = { + "enabled_error_codes": "enable_error_code", + "disabled_error_codes": "disable_error_code", + } + for key in section: invert = False options_key = key if key in config_types: ct = config_types[key] + elif key in invalid_options: + print( + f"{prefix}Unrecognized option: {key} = {section[key]}" + f" (did you mean {invalid_options[key]}?)", + file=stderr, + ) + continue else: dv = None # We have to keep new_semantic_analyzer in Options From 0c29507e6ef870eb96da222a734dc8ef8e5fbe24 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 1 Sep 2023 19:16:11 -0700 Subject: [PATCH 0234/1617] Make PEP 695 constructs give a reasonable error message (#16013) Mypy does not yet support PEP 695 Fixes #16011, linking #15238 --- mypy/fastparse.py | 31 +++++++++++++++ mypy/test/helpers.py | 4 +- mypy/test/testcheck.py | 2 + test-data/unit/check-python312.test | 59 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 test-data/unit/check-python312.test diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 6aa626afb81e..a96e697d40bf 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -144,6 +144,11 @@ def ast3_parse( NamedExpr = ast3.NamedExpr Constant = ast3.Constant +if sys.version_info >= (3, 12): + ast_TypeAlias = ast3.TypeAlias +else: + ast_TypeAlias = Any + if sys.version_info >= (3, 10): Match = ast3.Match MatchValue = ast3.MatchValue @@ -936,6 +941,14 @@ def do_func_def( arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: + if sys.version_info >= (3, 12) and n.type_params: + self.fail( + ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), + n.type_params[0].lineno, + n.type_params[0].col_offset, + blocker=False, + ) + arg_types = [a.type_annotation for a in args] return_type = TypeConverter( self.errors, line=n.returns.lineno if n.returns else lineno @@ -1110,6 +1123,14 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.class_and_function_stack.append("C") keywords = [(kw.arg, self.visit(kw.value)) for kw in n.keywords if kw.arg] + if sys.version_info >= (3, 12) and n.type_params: + self.fail( + ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), + n.type_params[0].lineno, + n.type_params[0].col_offset, + blocker=False, + ) + cdef = ClassDef( n.name, self.as_required_block(n.body), @@ -1717,6 +1738,16 @@ def visit_MatchOr(self, n: MatchOr) -> OrPattern: node = OrPattern([self.visit(pattern) for pattern in n.patterns]) return self.set_line(node, n) + def visit_TypeAlias(self, n: ast_TypeAlias) -> AssignmentStmt: + self.fail( + ErrorMessage("PEP 695 type aliases are not yet supported", code=codes.VALID_TYPE), + n.lineno, + n.col_offset, + blocker=False, + ) + node = AssignmentStmt([NameExpr(n.name.id)], self.visit(n.value)) + return self.set_line(node, n) + class TypeConverter: def __init__( diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index d1850219e60a..7447391593d5 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -241,7 +241,9 @@ def num_skipped_suffix_lines(a1: list[str], a2: list[str]) -> int: def testfile_pyversion(path: str) -> tuple[int, int]: - if path.endswith("python311.test"): + if path.endswith("python312.test"): + return 3, 12 + elif path.endswith("python311.test"): return 3, 11 elif path.endswith("python310.test"): return 3, 10 diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 7b81deeafe9d..b20e8cc25f3d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -43,6 +43,8 @@ typecheck_files.remove("check-python310.test") if sys.version_info < (3, 11): typecheck_files.remove("check-python311.test") +if sys.version_info < (3, 12): + typecheck_files.remove("check-python312.test") # Special tests for platforms with case-insensitive filesystems. if sys.platform not in ("darwin", "win32"): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test new file mode 100644 index 000000000000..91aca7794071 --- /dev/null +++ b/test-data/unit/check-python312.test @@ -0,0 +1,59 @@ +[case test695TypeAlias] +type MyInt = int # E: PEP 695 type aliases are not yet supported + +def f(x: MyInt) -> MyInt: + return reveal_type(x) # N: Revealed type is "builtins.int" + +type MyList[T] = list[T] # E: PEP 695 type aliases are not yet supported \ + # E: Name "T" is not defined + +def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases + return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]" + +[case test695Class] +class MyGen[T]: # E: PEP 695 generics are not yet supported + def __init__(self, x: T) -> None: # E: Name "T" is not defined + self.x = x + +def f(x: MyGen[int]): # E: "MyGen" expects no type arguments, but 1 given + reveal_type(x.x) # N: Revealed type is "Any" + +[case test695Function] +def f[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ + # E: Name "T" is not defined + return reveal_type(x) # N: Revealed type is "Any" + +reveal_type(f(1)) # N: Revealed type is "Any" + +async def g[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ + # E: Name "T" is not defined + return reveal_type(x) # N: Revealed type is "Any" + +reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ + # N: Are you missing an await? \ + # N: Revealed type is "typing.Coroutine[Any, Any, Any]" + +[case test695TypeVar] +from typing import Callable +type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported +type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported \ + # E: Value of type "int" is not indexable \ + # E: Name "P" is not defined +type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported \ + # E: Type expected within [...] \ + # E: The type "Type[Tuple[Any, ...]]" is not generic and not indexable \ + # E: Name "Ts" is not defined + +class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported +class Cls2[**P]: ... # E: PEP 695 generics are not yet supported +class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported + +def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported +def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported \ + # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ + # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ + # E: Name "P" is not defined +def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported \ + # E: Name "Ts" is not defined +[builtins fixtures/tuple.pyi] From 17e9e228f2efaeab2ca063cca44411feaa370dd5 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 1 Sep 2023 22:47:37 -0700 Subject: [PATCH 0235/1617] Match note error codes to import error codes (#16004) Fixes #16003. Follow up to #14740 --- mypy/build.py | 2 +- mypy/errors.py | 2 +- mypy/report.py | 2 +- mypy/test/testcheck.py | 2 +- mypy/test/testcmdline.py | 2 +- mypy/test/testreports.py | 4 ++-- test-data/unit/pep561.test | 1 + 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 525d5f436e7e..39629c2dc455 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2798,7 +2798,7 @@ def module_not_found( for note in notes: if "{stub_dist}" in note: note = note.format(stub_dist=stub_distribution_name(module)) - errors.report(line, 0, note, severity="note", only_once=True, code=codes.IMPORT) + errors.report(line, 0, note, severity="note", only_once=True, code=code) if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: manager.missing_stub_packages.add(stub_distribution_name(module)) errors.set_import_context(save_import_context) diff --git a/mypy/errors.py b/mypy/errors.py index 680b7f1d31ea..a678b790cb8c 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -469,7 +469,7 @@ def _add_error_info(self, file: str, info: ErrorInfo) -> None: self.error_info_map[file].append(info) if info.blocker: self.has_blockers.add(file) - if info.code is IMPORT: + if info.code in (IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND): self.seen_import_error = True def _filter_error(self, file: str, info: ErrorInfo) -> bool: diff --git a/mypy/report.py b/mypy/report.py index 5d93351aa37d..d5f16464c0fb 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -25,7 +25,7 @@ from mypy.version import __version__ try: - from lxml import etree # type: ignore[import] + from lxml import etree # type: ignore[import-untyped] LXML_INSTALLED = True except ImportError: diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index b20e8cc25f3d..85fbe5dc2990 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -26,7 +26,7 @@ from mypy.test.update_data import update_testcase_output try: - import lxml # type: ignore[import] + import lxml # type: ignore[import-untyped] except ImportError: lxml = None diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 30ecef07a821..9bc02d319964 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -20,7 +20,7 @@ ) try: - import lxml # type: ignore[import] + import lxml # type: ignore[import-untyped] except ImportError: lxml = None diff --git a/mypy/test/testreports.py b/mypy/test/testreports.py index a422b4bb2a7b..5ff315f83ba8 100644 --- a/mypy/test/testreports.py +++ b/mypy/test/testreports.py @@ -7,7 +7,7 @@ from mypy.test.helpers import Suite, assert_equal try: - import lxml # type: ignore[import] + import lxml # type: ignore[import-untyped] except ImportError: lxml = None @@ -22,7 +22,7 @@ def test_get_line_rate(self) -> None: @pytest.mark.skipif(lxml is None, reason="Cannot import lxml. Is it installed?") def test_as_xml(self) -> None: - import lxml.etree as etree # type: ignore[import] + import lxml.etree as etree # type: ignore[import-untyped] cobertura_package = CoberturaPackage("foobar") cobertura_package.covered_lines = 21 diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index e8ebbd03dca7..9969c2894c36 100644 --- a/test-data/unit/pep561.test +++ b/test-data/unit/pep561.test @@ -167,6 +167,7 @@ a.bf(False) b.bf(False) a.bf(1) b.bf(1) +import typedpkg_ns.whatever as c # type: ignore[import-untyped] [out] testNamespacePkgWStubs.py:4: error: Skipping analyzing "typedpkg_ns.b.bbb": module is installed, but missing library stubs or py.typed marker testNamespacePkgWStubs.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports From 6884aa2b27f7ec25ebeffab9e39b35807642a9d2 Mon Sep 17 00:00:00 2001 From: DS/Charlie <82801887+ds-cbo@users.noreply.github.com> Date: Sat, 2 Sep 2023 08:57:28 +0200 Subject: [PATCH 0236/1617] Fix case Any() in match statement (#14479) Fixes #14477 --- mypy/checkpattern.py | 6 ++++++ test-data/unit/check-python310.test | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index e432675b0b5a..3f9a99b21530 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -462,6 +462,12 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: typ: Type = Instance(type_info, [any_type] * len(type_info.defn.type_vars)) elif isinstance(type_info, TypeAlias): typ = type_info.target + elif ( + isinstance(type_info, Var) + and type_info.type is not None + and isinstance(get_proper_type(type_info.type), AnyType) + ): + typ = type_info.type else: if isinstance(type_info, Var) and type_info.type is not None: name = type_info.type.str_with_options(self.options) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index f81da23d148c..640e64c78d5f 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -896,6 +896,16 @@ match m: reveal_type(i) reveal_type(j) +[case testMatchClassPatternAny] +from typing import Any + +Foo: Any +m: object + +match m: + case Foo(): + pass + [case testMatchClassPatternNestedGenerics] # From cpython test_patma.py x = [[{0: 0}]] From 1655b0ce16af04cc76cf769a693366e9206a03de Mon Sep 17 00:00:00 2001 From: Albert Tugushev Date: Sat, 2 Sep 2023 09:02:40 +0200 Subject: [PATCH 0237/1617] Reword the error message related to void functions (#15876) Fixes #3226. Aims to provide better assistance to users who may be confused when their void functions technically return None. Co-authored-by: Ilya Priven Co-authored-by: hauntsaninja --- docs/source/error_code_list.rst | 2 +- mypy/messages.py | 15 ++---- test-data/unit/check-errorcodes.test | 6 +-- test-data/unit/check-expressions.test | 66 +++++++++++++------------- test-data/unit/check-functions.test | 2 +- test-data/unit/check-inference.test | 8 ++-- test-data/unit/check-optional.test | 6 +-- test-data/unit/check-tuples.test | 4 +- test-data/unit/check-varargs.test | 4 +- test-data/unit/pythoneval-asyncio.test | 2 +- 10 files changed, 54 insertions(+), 61 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 1f75ac54d525..a865a4dd1532 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -741,7 +741,7 @@ returns ``None``: # OK: we don't do anything with the return value f() - # Error: "f" does not return a value [func-returns-value] + # Error: "f" does not return a value (it only ever returns None) [func-returns-value] if f(): print("not false") diff --git a/mypy/messages.py b/mypy/messages.py index cda4cda25ee4..4b71bd876dcc 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1021,18 +1021,11 @@ def duplicate_argument_value(self, callee: CallableType, index: int, context: Co def does_not_return_value(self, callee_type: Type | None, context: Context) -> None: """Report an error about use of an unusable type.""" - name: str | None = None callee_type = get_proper_type(callee_type) - if isinstance(callee_type, FunctionLike): - name = callable_name(callee_type) - if name is not None: - self.fail( - f"{capitalize(name)} does not return a value", - context, - code=codes.FUNC_RETURNS_VALUE, - ) - else: - self.fail("Function does not return a value", context, code=codes.FUNC_RETURNS_VALUE) + callee_name = callable_name(callee_type) if isinstance(callee_type, FunctionLike) else None + name = callee_name or "Function" + message = f"{name} does not return a value (it only ever returns None)" + self.fail(message, context, code=codes.FUNC_RETURNS_VALUE) def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None: """Report an error about using an deleted type as an rvalue.""" diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 796e1c1ea98e..df14e328ed72 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -553,15 +553,15 @@ from typing import Callable def f() -> None: pass -x = f() # E: "f" does not return a value [func-returns-value] +x = f() # E: "f" does not return a value (it only ever returns None) [func-returns-value] class A: def g(self) -> None: pass -y = A().g() # E: "g" of "A" does not return a value [func-returns-value] +y = A().g() # E: "g" of "A" does not return a value (it only ever returns None) [func-returns-value] c: Callable[[], None] -z = c() # E: Function does not return a value [func-returns-value] +z = c() # E: Function does not return a value (it only ever returns None) [func-returns-value] [case testErrorCodeInstantiateAbstract] from abc import abstractmethod diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index c213255997f8..a3c1bc8795f2 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1079,15 +1079,15 @@ class A: a: A o: object if int(): - a = f() # E: "f" does not return a value + a = f() # E: "f" does not return a value (it only ever returns None) if int(): - o = a() # E: Function does not return a value + o = a() # E: Function does not return a value (it only ever returns None) if int(): - o = A().g(a) # E: "g" of "A" does not return a value + o = A().g(a) # E: "g" of "A" does not return a value (it only ever returns None) if int(): - o = A.g(a, a) # E: "g" of "A" does not return a value -A().g(f()) # E: "f" does not return a value -x: A = f() # E: "f" does not return a value + o = A.g(a, a) # E: "g" of "A" does not return a value (it only ever returns None) +A().g(f()) # E: "f" does not return a value (it only ever returns None) +x: A = f() # E: "f" does not return a value (it only ever returns None) f() A().g(a) [builtins fixtures/tuple.pyi] @@ -1096,15 +1096,15 @@ A().g(a) import typing def f() -> None: pass -if f(): # E: "f" does not return a value +if f(): # E: "f" does not return a value (it only ever returns None) pass -elif f(): # E: "f" does not return a value +elif f(): # E: "f" does not return a value (it only ever returns None) pass -while f(): # E: "f" does not return a value +while f(): # E: "f" does not return a value (it only ever returns None) pass def g() -> object: - return f() # E: "f" does not return a value -raise f() # E: "f" does not return a value + return f() # E: "f" does not return a value (it only ever returns None) +raise f() # E: "f" does not return a value (it only ever returns None) [builtins fixtures/exception.pyi] [case testNoneReturnTypeWithExpressions] @@ -1115,13 +1115,13 @@ class A: def __add__(self, x: 'A') -> 'A': pass a: A -[f()] # E: "f" does not return a value -f() + a # E: "f" does not return a value -a + f() # E: "f" does not return a value -f() == a # E: "f" does not return a value -a != f() # E: "f" does not return a value +[f()] # E: "f" does not return a value (it only ever returns None) +f() + a # E: "f" does not return a value (it only ever returns None) +a + f() # E: "f" does not return a value (it only ever returns None) +f() == a # E: "f" does not return a value (it only ever returns None) +a != f() # E: "f" does not return a value (it only ever returns None) cast(A, f()) -f().foo # E: "f" does not return a value +f().foo # E: "f" does not return a value (it only ever returns None) [builtins fixtures/list.pyi] [case testNoneReturnTypeWithExpressions2] @@ -1134,14 +1134,14 @@ class A: a: A b: bool -f() in a # E: "f" does not return a value # E: Unsupported right operand type for in ("A") -a < f() # E: "f" does not return a value -f() <= a # E: "f" does not return a value -a in f() # E: "f" does not return a value --f() # E: "f" does not return a value -not f() # E: "f" does not return a value -f() and b # E: "f" does not return a value -b or f() # E: "f" does not return a value +f() in a # E: "f" does not return a value (it only ever returns None) # E: Unsupported right operand type for in ("A") +a < f() # E: "f" does not return a value (it only ever returns None) +f() <= a # E: "f" does not return a value (it only ever returns None) +a in f() # E: "f" does not return a value (it only ever returns None) +-f() # E: "f" does not return a value (it only ever returns None) +not f() # E: "f" does not return a value (it only ever returns None) +f() and b # E: "f" does not return a value (it only ever returns None) +b or f() # E: "f" does not return a value (it only ever returns None) [builtins fixtures/bool.pyi] @@ -1441,7 +1441,7 @@ if int(): [case testConditionalExpressionWithEmptyCondition] import typing def f() -> None: pass -x = 1 if f() else 2 # E: "f" does not return a value +x = 1 if f() else 2 # E: "f" does not return a value (it only ever returns None) [case testConditionalExpressionWithSubtyping] import typing @@ -1504,7 +1504,7 @@ from typing import List, Union x = [] y = "" x.append(y) if bool() else x.append(y) -z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value +z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value (it only ever returns None) [builtins fixtures/list.pyi] -- Special cases @@ -1604,7 +1604,7 @@ def f(x: int) -> None: [builtins fixtures/for.pyi] [out] main:1: error: The return type of a generator function should be "Generator" or one of its supertypes -main:2: error: "f" does not return a value +main:2: error: "f" does not return a value (it only ever returns None) main:2: error: Argument 1 to "f" has incompatible type "str"; expected "int" [case testYieldExpressionWithNone] @@ -1624,7 +1624,7 @@ from typing import Iterator def f() -> Iterator[int]: yield 5 def g() -> Iterator[int]: - a = yield from f() # E: Function does not return a value + a = yield from f() # E: Function does not return a value (it only ever returns None) [case testYieldFromGeneratorHasValue] from typing import Iterator, Generator @@ -1639,12 +1639,12 @@ def g() -> Iterator[int]: [case testYieldFromTupleExpression] from typing import Generator def g() -> Generator[int, None, None]: - x = yield from () # E: Function does not return a value - x = yield from (0, 1, 2) # E: Function does not return a value + x = yield from () # E: Function does not return a value (it only ever returns None) + x = yield from (0, 1, 2) # E: Function does not return a value (it only ever returns None) x = yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "object", expected type "int") \ - # E: Function does not return a value + # E: Function does not return a value (it only ever returns None) x = yield from ("ERROR",) # E: Incompatible types in "yield from" (actual type "str", expected type "int") \ - # E: Function does not return a value + # E: Function does not return a value (it only ever returns None) [builtins fixtures/tuple.pyi] -- dict(...) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 4cc523a595d1..cd098a84d4d3 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -250,7 +250,7 @@ if int(): if int(): f = o # E: Incompatible types in assignment (expression has type "object", variable has type "Callable[[], None]") if int(): - f = f() # E: Function does not return a value + f = f() # E: Function does not return a value (it only ever returns None) if int(): f = f diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 36b028977591..f9a4d58c74af 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -33,8 +33,8 @@ class B: pass [case testLvarInitializedToVoid] import typing def f() -> None: - a = g() # E: "g" does not return a value - #b, c = g() # "g" does not return a value TODO + a = g() # E: "g" does not return a value (it only ever returns None) + #b, c = g() # "g" does not return a value (it only ever returns None) TODO def g() -> None: pass [out] @@ -1180,7 +1180,7 @@ for e, f in [[]]: # E: Need type annotation for "e" \ [case testForStatementInferenceWithVoid] def f() -> None: pass -for x in f(): # E: "f" does not return a value +for x in f(): # E: "f" does not return a value (it only ever returns None) pass [builtins fixtures/for.pyi] @@ -2118,7 +2118,7 @@ arr = [] arr.append(arr.append(1)) [builtins fixtures/list.pyi] [out] -main:3: error: "append" of "list" does not return a value +main:3: error: "append" of "list" does not return a value (it only ever returns None) -- Multipass -- --------- diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index ae247b0047f1..70f3c4486e14 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -361,9 +361,9 @@ def f() -> None: def g(x: Optional[int]) -> int: pass -x = f() # E: "f" does not return a value -f() + 1 # E: "f" does not return a value -g(f()) # E: "f" does not return a value +x = f() # E: "f" does not return a value (it only ever returns None) +f() + 1 # E: "f" does not return a value (it only ever returns None) +g(f()) # E: "f" does not return a value (it only ever returns None) [case testEmptyReturn] def f() -> None: diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index cff261774663..0e7c81edc498 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -169,8 +169,8 @@ class C(B): pass import typing def f() -> None: pass -(None, f()) # E: "f" does not return a value -(f(), None) # E: "f" does not return a value +(None, f()) # E: "f" does not return a value (it only ever returns None) +(f(), None) # E: "f" does not return a value (it only ever returns None) [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 54546f3973b3..ef2c3c57fad5 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -52,8 +52,8 @@ c: C f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A" f(a, b, c) # E: Argument 3 to "f" has incompatible type "C"; expected "A" -f(g()) # E: "g" does not return a value -f(a, g()) # E: "g" does not return a value +f(g()) # E: "g" does not return a value (it only ever returns None) +f(a, g()) # E: "g" does not return a value (it only ever returns None) f() f(a) f(b) diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index 069374b9635c..4a185557495b 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -261,7 +261,7 @@ try: finally: loop.close() [out] -_program.py:11: error: Function does not return a value +_program.py:11: error: Function does not return a value (it only ever returns None) [case testErrorReturnIsNotTheSameType] from typing import Any From 5adf934804c512c37a9506aeae426622abf92cb5 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 2 Sep 2023 00:53:35 -0700 Subject: [PATCH 0238/1617] Fix crash with report generation on namespace packages (again) (#16019) Fixes #15979. Fix is similar to that in `iterate_python_lines`. --- mypy/report.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mypy/report.py b/mypy/report.py index d5f16464c0fb..86fcee0521a6 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -171,8 +171,12 @@ def on_file( ) -> None: # Count physical lines. This assumes the file's encoding is a # superset of ASCII (or at least uses \n in its line endings). - with open(tree.path, "rb") as f: - physical_lines = len(f.readlines()) + try: + with open(tree.path, "rb") as f: + physical_lines = len(f.readlines()) + except IsADirectoryError: + # can happen with namespace packages + physical_lines = 0 func_counter = FuncCounterVisitor() tree.accept(func_counter) From fb32db7237ec1847960c93cfb17c8f24182d5d77 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sat, 2 Sep 2023 17:09:03 -0400 Subject: [PATCH 0239/1617] docs: document dataclass_transform behavior (#16017) Document behavior discussed in https://github.com/python/typing/discussions/1456. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- docs/source/additional_features.rst | 45 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/docs/source/additional_features.rst b/docs/source/additional_features.rst index 5dd136476eaa..ae625c157654 100644 --- a/docs/source/additional_features.rst +++ b/docs/source/additional_features.rst @@ -71,12 +71,12 @@ and :pep:`557`. Caveats/Known Issues ==================== -Some functions in the :py:mod:`dataclasses` module, such as :py:func:`~dataclasses.replace` and :py:func:`~dataclasses.asdict`, +Some functions in the :py:mod:`dataclasses` module, such as :py:func:`~dataclasses.asdict`, have imprecise (too permissive) types. This will be fixed in future releases. Mypy does not yet recognize aliases of :py:func:`dataclasses.dataclass `, and will -probably never recognize dynamically computed decorators. The following examples -do **not** work: +probably never recognize dynamically computed decorators. The following example +does **not** work: .. code-block:: python @@ -94,16 +94,37 @@ do **not** work: """ attribute: int - @dataclass_wrapper - class DynamicallyDecorated: - """ - Mypy doesn't recognize this as a dataclass because it is decorated by a - function returning `dataclass` rather than by `dataclass` itself. - """ - attribute: int - AliasDecorated(attribute=1) # error: Unexpected keyword argument - DynamicallyDecorated(attribute=1) # error: Unexpected keyword argument + + +To have Mypy recognize a wrapper of :py:func:`dataclasses.dataclass ` +as a dataclass decorator, consider using the :py:func:`~typing.dataclass_transform` decorator: + +.. code-block:: python + + from dataclasses import dataclass, Field + from typing import TypeVar, dataclass_transform + + T = TypeVar('T') + + @dataclass_transform(field_specifiers=(Field,)) + def my_dataclass(cls: type[T]) -> type[T]: + ... + return dataclass(cls) + + +Data Class Transforms +********************* + +Mypy supports the :py:func:`~typing.dataclass_transform` decorator as described in +`PEP 681 `_. + +.. note:: + + Pragmatically, mypy will assume such classes have the internal attribute :code:`__dataclass_fields__` + (even though they might lack it in runtime) and will assume functions such as :py:func:`dataclasses.is_dataclass` + and :py:func:`dataclasses.fields` treat them as if they were dataclasses + (even though they may fail at runtime). .. _attrs_package: From 6a6d2e8a2d919af7557063de8f1faa580969b011 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sat, 2 Sep 2023 17:09:34 -0400 Subject: [PATCH 0240/1617] meta tests: refactor run_pytest (#15481) Factor `run_pytest` out of mypy/test/meta/test_*.py. --- mypy/test/meta/_pytest.py | 72 ++++++++++++++++++++++++++++++ mypy/test/meta/test_parse_data.py | 65 +++++++-------------------- mypy/test/meta/test_update_data.py | 53 +++++++--------------- 3 files changed, 104 insertions(+), 86 deletions(-) create mode 100644 mypy/test/meta/_pytest.py diff --git a/mypy/test/meta/_pytest.py b/mypy/test/meta/_pytest.py new file mode 100644 index 000000000000..b8648f033143 --- /dev/null +++ b/mypy/test/meta/_pytest.py @@ -0,0 +1,72 @@ +import shlex +import subprocess +import sys +import textwrap +import uuid +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable + +from mypy.test.config import test_data_prefix + + +@dataclass +class PytestResult: + input: str + input_updated: str # any updates made by --update-data + stdout: str + stderr: str + + +def dedent_docstring(s: str) -> str: + return textwrap.dedent(s).lstrip() + + +def run_pytest_data_suite( + data_suite: str, + *, + data_file_prefix: str = "check", + pytest_node_prefix: str = "mypy/test/testcheck.py::TypeCheckSuite", + extra_args: Iterable[str], + max_attempts: int, +) -> PytestResult: + """ + Runs a suite of data test cases through pytest until either tests pass + or until a maximum number of attempts (needed for incremental tests). + + :param data_suite: the actual "suite" i.e. the contents of a .test file + """ + p_test_data = Path(test_data_prefix) + p_root = p_test_data.parent.parent + p = p_test_data / f"{data_file_prefix}-meta-{uuid.uuid4()}.test" + assert not p.exists() + data_suite = dedent_docstring(data_suite) + try: + p.write_text(data_suite) + + test_nodeid = f"{pytest_node_prefix}::{p.name}" + extra_args = [sys.executable, "-m", "pytest", "-n", "0", "-s", *extra_args, test_nodeid] + cmd = shlex.join(extra_args) + for i in range(max_attempts - 1, -1, -1): + print(f">> {cmd}") + proc = subprocess.run(extra_args, capture_output=True, check=False, cwd=p_root) + if proc.returncode == 0: + break + prefix = "NESTED PYTEST STDOUT" + for line in proc.stdout.decode().splitlines(): + print(f"{prefix}: {line}") + prefix = " " * len(prefix) + prefix = "NESTED PYTEST STDERR" + for line in proc.stderr.decode().splitlines(): + print(f"{prefix}: {line}") + prefix = " " * len(prefix) + print(f"Exit code {proc.returncode} ({i} attempts remaining)") + + return PytestResult( + input=data_suite, + input_updated=p.read_text(), + stdout=proc.stdout.decode(), + stderr=proc.stderr.decode(), + ) + finally: + p.unlink() diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index 6593dbc45704..797fdd7b2c8c 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -2,37 +2,18 @@ A "meta test" which tests the parsing of .test files. This is not meant to become exhaustive but to ensure we maintain a basic level of ergonomics for mypy contributors. """ -import subprocess -import sys -import textwrap -import uuid -from pathlib import Path - -from mypy.test.config import test_data_prefix from mypy.test.helpers import Suite +from mypy.test.meta._pytest import PytestResult, run_pytest_data_suite -class ParseTestDataSuite(Suite): - def _dedent(self, s: str) -> str: - return textwrap.dedent(s).lstrip() +def _run_pytest(data_suite: str) -> PytestResult: + return run_pytest_data_suite(data_suite, extra_args=[], max_attempts=1) - def _run_pytest(self, data_suite: str) -> str: - p_test_data = Path(test_data_prefix) - p_root = p_test_data.parent.parent - p = p_test_data / f"check-meta-{uuid.uuid4()}.test" - assert not p.exists() - try: - p.write_text(data_suite) - test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" - args = [sys.executable, "-m", "pytest", "-n", "0", "-s", test_nodeid] - proc = subprocess.run(args, cwd=p_root, capture_output=True, check=False) - return proc.stdout.decode() - finally: - p.unlink() +class ParseTestDataSuite(Suite): def test_parse_invalid_case(self) -> None: - # Arrange - data = self._dedent( + # Act + result = _run_pytest( """ [case abc] s: str @@ -41,15 +22,12 @@ def test_parse_invalid_case(self) -> None: """ ) - # Act - actual = self._run_pytest(data) - # Assert - assert "Invalid testcase id 'foo-XFAIL'" in actual + assert "Invalid testcase id 'foo-XFAIL'" in result.stdout def test_parse_invalid_section(self) -> None: - # Arrange - data = self._dedent( + # Act + result = _run_pytest( """ [case abc] s: str @@ -58,19 +36,16 @@ def test_parse_invalid_section(self) -> None: """ ) - # Act - actual = self._run_pytest(data) - # Assert - expected_lineno = data.splitlines().index("[unknownsection]") + 1 + expected_lineno = result.input.splitlines().index("[unknownsection]") + 1 expected = ( f".test:{expected_lineno}: Invalid section header [unknownsection] in case 'abc'" ) - assert expected in actual + assert expected in result.stdout def test_bad_ge_version_check(self) -> None: - # Arrange - data = self._dedent( + # Act + actual = _run_pytest( """ [case abc] s: str @@ -79,15 +54,12 @@ def test_bad_ge_version_check(self) -> None: """ ) - # Act - actual = self._run_pytest(data) - # Assert - assert "version>=3.8 always true since minimum runtime version is (3, 8)" in actual + assert "version>=3.8 always true since minimum runtime version is (3, 8)" in actual.stdout def test_bad_eq_version_check(self) -> None: - # Arrange - data = self._dedent( + # Act + actual = _run_pytest( """ [case abc] s: str @@ -96,8 +68,5 @@ def test_bad_eq_version_check(self) -> None: """ ) - # Act - actual = self._run_pytest(data) - # Assert - assert "version==3.7 always false since minimum runtime version is (3, 8)" in actual + assert "version==3.7 always false since minimum runtime version is (3, 8)" in actual.stdout diff --git a/mypy/test/meta/test_update_data.py b/mypy/test/meta/test_update_data.py index 4e4bdd193dbf..40b70157a0e3 100644 --- a/mypy/test/meta/test_update_data.py +++ b/mypy/test/meta/test_update_data.py @@ -3,47 +3,23 @@ Updating the expected output, especially when it's in the form of inline (comment) assertions, can be brittle, which is why we're "meta-testing" here. """ -import shlex -import subprocess -import sys -import textwrap -import uuid -from pathlib import Path - -from mypy.test.config import test_data_prefix from mypy.test.helpers import Suite +from mypy.test.meta._pytest import PytestResult, dedent_docstring, run_pytest_data_suite -class UpdateDataSuite(Suite): - def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str: - """ - Runs a suite of data test cases through 'pytest --update-data' until either tests pass - or until a maximum number of attempts (needed for incremental tests). - """ - p_test_data = Path(test_data_prefix) - p_root = p_test_data.parent.parent - p = p_test_data / f"check-meta-{uuid.uuid4()}.test" - assert not p.exists() - try: - p.write_text(textwrap.dedent(data_suite).lstrip()) - - test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}" - args = [sys.executable, "-m", "pytest", "-n", "0", "-s", "--update-data", test_nodeid] - cmd = shlex.join(args) - for i in range(max_attempts - 1, -1, -1): - res = subprocess.run(args, cwd=p_root) - if res.returncode == 0: - break - print(f"`{cmd}` returned {res.returncode}: {i} attempts remaining") - - return p.read_text() - finally: - p.unlink() +def _run_pytest_update_data(data_suite: str) -> PytestResult: + """ + Runs a suite of data test cases through 'pytest --update-data' until either tests pass + or until a maximum number of attempts (needed for incremental tests). + """ + return run_pytest_data_suite(data_suite, extra_args=["--update-data"], max_attempts=3) + +class UpdateDataSuite(Suite): def test_update_data(self) -> None: # Note: We test multiple testcases rather than 'test case per test case' # so we could also exercise rewriting multiple testcases at once. - actual = self._run_pytest_update_data( + result = _run_pytest_update_data( """ [case testCorrect] s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str") @@ -97,12 +73,12 @@ def test_update_data(self) -> None: [file b.py] s2: str = 43 # E: baz [builtins fixtures/list.pyi] - """, - max_attempts=3, + """ ) # Assert - expected = """ + expected = dedent_docstring( + """ [case testCorrect] s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str") @@ -154,4 +130,5 @@ def test_update_data(self) -> None: s2: str = 43 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/list.pyi] """ - assert actual == textwrap.dedent(expected).lstrip() + ) + assert result.input_updated == expected From cc8a4b50f7e65004a97c9ba51c69f7c9340370d9 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sat, 2 Sep 2023 17:29:42 -0400 Subject: [PATCH 0241/1617] Document we're not tracking relationships between symbols (#16018) Fixes #15653. I did not use erictraut's "quantum entanglement" metaphor, though I find it to be quite illustrative :) --- docs/source/type_narrowing.rst | 42 ++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 4bc0fda70138..4c5c2851edd0 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -3,7 +3,7 @@ Type narrowing ============== -This section is dedicated to several type narrowing +This section is dedicated to several type narrowing techniques which are supported by mypy. Type narrowing is when you convince a type checker that a broader type is actually more specific, for instance, that an object of type ``Shape`` is actually of the narrower type ``Square``. @@ -14,10 +14,11 @@ Type narrowing expressions The simplest way to narrow a type is to use one of the supported expressions: -- :py:func:`isinstance` like in ``isinstance(obj, float)`` will narrow ``obj`` to have ``float`` type -- :py:func:`issubclass` like in ``issubclass(cls, MyClass)`` will narrow ``cls`` to be ``Type[MyClass]`` -- :py:class:`type` like in ``type(obj) is int`` will narrow ``obj`` to have ``int`` type -- :py:func:`callable` like in ``callable(obj)`` will narrow object to callable type +- :py:func:`isinstance` like in :code:`isinstance(obj, float)` will narrow ``obj`` to have ``float`` type +- :py:func:`issubclass` like in :code:`issubclass(cls, MyClass)` will narrow ``cls`` to be ``Type[MyClass]`` +- :py:class:`type` like in :code:`type(obj) is int` will narrow ``obj`` to have ``int`` type +- :py:func:`callable` like in :code:`callable(obj)` will narrow object to callable type +- :code:`obj is not None` will narrow object to its :ref:`non-optional form ` Type narrowing is contextual. For example, based on the condition, mypy will narrow an expression only within an ``if`` branch: @@ -83,6 +84,7 @@ We can also use ``assert`` to narrow types in the same context: reveal_type(x) # Revealed type is "builtins.int" print(x + '!') # Typechecks with `mypy`, but fails in runtime. + issubclass ~~~~~~~~~~ @@ -359,3 +361,33 @@ What happens here? .. note:: The same will work with ``isinstance(x := a, float)`` as well. + +Limitations +----------- + +Mypy's analysis is limited to individual symbols and it will not track +relationships between symbols. For example, in the following code +it's easy to deduce that if :code:`a` is None then :code:`b` must not be, +therefore :code:`a or b` will always be a string, but Mypy will not be able to tell that: + +.. code-block:: python + + def f(a: str | None, b: str | None) -> str: + if a is not None or b is not None: + return a or b # Incompatible return value type (got "str | None", expected "str") + return 'spam' + +Tracking these sort of cross-variable conditions in a type checker would add significant complexity +and performance overhead. + +You can use an ``assert`` to convince the type checker, override it with a :ref:`cast ` +or rewrite the function to be slightly more verbose: + +.. code-block:: python + + def f(a: str | None, b: str | None) -> str: + if a is not None: + return a + elif b is not None: + return b + return 'spam' From f83d6eb9070137f0b060bb5a8b81858bf8910424 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 3 Sep 2023 03:59:28 -0400 Subject: [PATCH 0242/1617] ruff: add pyupgrade (#16023) For example, this [review comment](https://github.com/python/mypy/pull/15481#discussion_r1313755961) could've been spared with [UP036](https://beta.ruff.rs/docs/rules/outdated-version-block/). --- mypy/checkmember.py | 4 ++-- mypy/config_parser.py | 22 +++++++++++++--------- mypy/main.py | 3 +-- mypy/messages.py | 15 +++++++-------- mypy/solve.py | 4 ++-- mypyc/ir/class_ir.py | 2 +- mypyc/ir/ops.py | 4 ++-- mypyc/ir/rtypes.py | 4 ++-- pyproject.toml | 4 +++- 9 files changed, 33 insertions(+), 29 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f7d002f17eb9..60430839ff62 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Optional, Sequence, cast +from typing import TYPE_CHECKING, Callable, Sequence, cast from mypy import meet, message_registry, subtypes from mypy.erasetype import erase_typevars @@ -777,7 +777,7 @@ def analyze_var( result: Type = t typ = get_proper_type(typ) - call_type: Optional[ProperType] = None + call_type: ProperType | None = None if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator): if isinstance(typ, FunctionLike) and not typ.is_type_obj(): call_type = typ diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 7748c3b25966..4dbd6477c81e 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -292,14 +292,18 @@ def parse_config_file( ) if report_dirs: print( - "%sPer-module sections should not specify reports (%s)" - % (prefix, ", ".join(s + "_report" for s in sorted(report_dirs))), + prefix, + "Per-module sections should not specify reports ({})".format( + ", ".join(s + "_report" for s in sorted(report_dirs)) + ), file=stderr, ) if set(updates) - PER_MODULE_OPTIONS: print( - "%sPer-module sections should only specify per-module flags (%s)" - % (prefix, ", ".join(sorted(set(updates) - PER_MODULE_OPTIONS))), + prefix, + "Per-module sections should only specify per-module flags ({})".format( + ", ".join(sorted(set(updates) - PER_MODULE_OPTIONS)) + ), file=stderr, ) updates = {k: v for k, v in updates.items() if k in PER_MODULE_OPTIONS} @@ -315,8 +319,9 @@ def parse_config_file( "*" in x and x != "*" for x in glob.split(".") ): print( - "%sPatterns must be fully-qualified module names, optionally " - "with '*' in some components (e.g spam.*.eggs.*)" % prefix, + prefix, + "Patterns must be fully-qualified module names, optionally " + "with '*' in some components (e.g spam.*.eggs.*)", file=stderr, ) else: @@ -329,7 +334,7 @@ def get_prefix(file_read: str, name: str) -> str: else: module_name_str = name - return f"{file_read}: [{module_name_str}]: " + return f"{file_read}: [{module_name_str}]:" def is_toml(filename: str) -> bool: @@ -411,8 +416,7 @@ def destructure_overrides(toml_data: dict[str, Any]) -> dict[str, Any]: raise ConfigTOMLValueError( "toml config file contains " "[[tool.mypy.overrides]] sections with conflicting " - "values. Module '%s' has two different values for '%s'" - % (module, new_key) + f"values. Module '{module}' has two different values for '{new_key}'" ) result[old_config_name][new_key] = new_value diff --git a/mypy/main.py b/mypy/main.py index 30f6cfe97455..a4357dca7890 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -187,8 +187,7 @@ def flush_errors(new_messages: list[str], serious: bool) -> None: and not options.non_interactive ): print( - "Warning: unused section(s) in %s: %s" - % ( + "Warning: unused section(s) in {}: {}".format( options.config_file, get_config_module_names( options.config_file, diff --git a/mypy/messages.py b/mypy/messages.py index 4b71bd876dcc..bba9c3c3cdea 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1454,20 +1454,19 @@ def cannot_determine_type_in_base(self, name: str, base: str, context: Context) self.fail(f'Cannot determine type of "{name}" in base class "{base}"', context) def no_formal_self(self, name: str, item: CallableType, context: Context) -> None: + type = format_type(item, self.options) self.fail( - 'Attribute function "%s" with type %s does not accept self argument' - % (name, format_type(item, self.options)), - context, + f'Attribute function "{name}" with type {type} does not accept self argument', context ) def incompatible_self_argument( self, name: str, arg: Type, sig: CallableType, is_classmethod: bool, context: Context ) -> None: kind = "class attribute function" if is_classmethod else "attribute function" + arg_type = format_type(arg, self.options) + sig_type = format_type(sig, self.options) self.fail( - 'Invalid self argument %s to %s "%s" with type %s' - % (format_type(arg, self.options), kind, name, format_type(sig, self.options)), - context, + f'Invalid self argument {arg_type} to {kind} "{name}" with type {sig_type}', context ) def incompatible_conditional_function_def( @@ -1487,8 +1486,8 @@ def cannot_instantiate_abstract_class( ) -> None: attrs = format_string_list([f'"{a}"' for a in abstract_attributes]) self.fail( - 'Cannot instantiate abstract class "%s" with abstract ' - "attribute%s %s" % (class_name, plural_s(abstract_attributes), attrs), + f'Cannot instantiate abstract class "{class_name}" with abstract ' + f"attribute{plural_s(abstract_attributes)} {attrs}", context, code=codes.ABSTRACT, ) diff --git a/mypy/solve.py b/mypy/solve.py index 95377ea9f93e..17e1ca047818 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, Sequence, Tuple +from typing import Iterable, Sequence from typing_extensions import TypeAlias as _TypeAlias from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints @@ -333,7 +333,7 @@ def is_trivial_bound(tp: ProperType) -> bool: return isinstance(tp, Instance) and tp.type.fullname == "builtins.object" -def find_linear(c: Constraint) -> Tuple[bool, TypeVarId | None]: +def find_linear(c: Constraint) -> tuple[bool, TypeVarId | None]: """Find out if this constraint represent a linear relationship, return target id if yes.""" if isinstance(c.origin_type_var, TypeVarType): if isinstance(c.target, TypeVarType): diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 682e30629118..61f0fc36e1b3 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -70,7 +70,7 @@ class VTableMethod(NamedTuple): - cls: "ClassIR" + cls: "ClassIR" # noqa: UP037 name: str method: FuncIR shadow_method: FuncIR | None diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index d80c479211b7..2d64cc79d822 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1575,5 +1575,5 @@ def visit_keep_alive(self, op: KeepAlive) -> T: # (Serialization and deserialization *will* be used for incremental # compilation but so far it is not hooked up to anything.) class DeserMaps(NamedTuple): - classes: dict[str, "ClassIR"] - functions: dict[str, "FuncIR"] + classes: dict[str, ClassIR] + functions: dict[str, FuncIR] diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fa46feb0b59a..fecfaee5ef77 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -23,8 +23,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar -from typing_extensions import Final, TypeGuard +from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar +from typing_extensions import TypeGuard from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator diff --git a/pyproject.toml b/pyproject.toml index 67201acb9b94..18ba23671d9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,8 @@ select = [ "B", # flake8-bugbear "I", # isort "RUF100", # Unused noqa comments - "PGH004" # blanket noqa comments + "PGH004", # blanket noqa comments + "UP", # pyupgrade ] ignore = [ @@ -49,6 +50,7 @@ ignore = [ "E501", # conflicts with black "E731", # Do not assign a `lambda` expression, use a `def` "E741", # Ambiguous variable name + "UP032", # 'f-string always preferable to format' is controversial ] unfixable = [ From 87365eb3b2ef5f89c19de2708a826f3c80e914a6 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 3 Sep 2023 15:24:24 +0300 Subject: [PATCH 0243/1617] Exclude `assert False` from coverage (#16026) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 18ba23671d9c..1d6562756e22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,5 +121,6 @@ exclude_lines = [ '^\s*raise NotImplementedError\b', '^\s*return NotImplemented\b', '^\s*raise$', + '^assert False\b', '''^if __name__ == ['"]__main__['"]:$''', ] From 92e054b7dad3641fe74326ef60e773b974ca614f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 3 Sep 2023 20:01:51 +0300 Subject: [PATCH 0244/1617] Do not set `is_final` twice for `FuncBase` subclasses (#16030) --- mypy/nodes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 9b4ba5e76667..db42dd6b3949 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -573,7 +573,6 @@ def __init__(self, items: list[OverloadPart]) -> None: if items: # TODO: figure out how to reliably set end position (we don't know the impl here). self.set_line(items[0].line, items[0].column) - self.is_final = False @property def name(self) -> str: @@ -772,7 +771,6 @@ def __init__( # Is this an abstract method with trivial body? # Such methods can't be called via super(). self.is_trivial_body = False - self.is_final = False # Original conditional definition self.original_def: None | FuncDef | Var | Decorator = None # Used for error reporting (to keep backward compatibility with pre-3.8) From 488ad4f31dca387f87093e8d0b0fef2e021daa0b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Sep 2023 12:53:26 +0300 Subject: [PATCH 0245/1617] Bundle `misc/proper_plugin.py` as a part of `mypy` (#16036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I modified ```diff diff --git mypy/binder.py mypy/binder.py index 8a68f24f6..194883f86 100644 --- mypy/binder.py +++ mypy/binder.py @@ -345,7 +345,7 @@ class ConditionalTypeBinder: self._cleanse_key(dep) def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type | None: - type = get_proper_type(type) + # type = get_proper_type(type) if isinstance(type, AnyType): return get_declaration(expr) key = literal_hash(expr) ``` to see if it still works. It is: ```python » python runtests.py self run self: ['/Users/sobolev/Desktop/mypy/.venv/bin/python', '-m', 'mypy', '--config-file', 'mypy_self_check.ini', '-p', 'mypy', '-p', 'mypyc'] mypy/binder.py:349: error: Never apply isinstance() to unexpanded types; use mypy.types.get_proper_type() first [misc] if isinstance(type, AnyType): ^~~~~~~~~~~~~~~~~~~~~~~~~ mypy/binder.py:349: note: If you pass on the original type after the check, always use its unexpanded version Found 1 error in 1 file (checked 288 source files) FAILED: self ``` I will add this plugin to my CI checks in like ~5 plugins I maintain :) - https://github.com/typeddjango/django-stubs - https://github.com/typeddjango/djangorestframework-stubs - https://github.com/dry-python/returns - https://github.com/dry-python/classes - https://github.com/wemake-services/mypy-extras Closes https://github.com/python/mypy/issues/16035 --- MANIFEST.in | 1 - docs/source/extending_mypy.rst | 9 +++++++++ {misc => mypy/plugins}/proper_plugin.py | 9 +++++++++ mypy_self_check.ini | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) rename {misc => mypy/plugins}/proper_plugin.py (95%) diff --git a/MANIFEST.in b/MANIFEST.in index b77b762b4852..a1c15446de3f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -34,7 +34,6 @@ include build-requirements.txt include test-requirements.txt include mypy_self_check.ini prune misc -include misc/proper_plugin.py graft test-data include conftest.py include runtests.py diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index 506f548db687..bbbec2ad3880 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -237,3 +237,12 @@ mypy's cache for that module so that it can be rechecked. This hook should be used to report to mypy any relevant configuration data, so that mypy knows to recheck the module if the configuration changes. The hooks should return data encodable as JSON. + +Useful tools +************ + +Mypy ships ``mypy.plugins.proper_plugin`` plugin which can be useful +for plugin authors, since it finds missing ``get_proper_type()`` calls, +which is a pretty common mistake. + +It is recommended to enable it is a part of your plugin's CI. diff --git a/misc/proper_plugin.py b/mypy/plugins/proper_plugin.py similarity index 95% rename from misc/proper_plugin.py rename to mypy/plugins/proper_plugin.py index a6e6dc03b625..ab93f0d126db 100644 --- a/misc/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -1,3 +1,12 @@ +""" +This plugin is helpful for mypy development itself. +By default, it is not enabled for mypy users. + +It also can be used by plugin developers as a part of their CI checks. + +It finds missing ``get_proper_type()`` call, which can lead to multiple errors. +""" + from __future__ import annotations from typing import Callable diff --git a/mypy_self_check.ini b/mypy_self_check.ini index fcdbe641d6d6..6e1ad8187b7a 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -5,7 +5,7 @@ disallow_any_unimported = True show_traceback = True pretty = True always_false = MYPYC -plugins = misc/proper_plugin.py +plugins = mypy.plugins.proper_plugin python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ new_type_inference = True From 8738886861682e0d168ea321c2cc6ee5b566cb8b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Sep 2023 13:23:42 +0300 Subject: [PATCH 0246/1617] Add type annotations to `test-data/unit/plugins` (#16028) Closes https://github.com/python/mypy/issues/16027 --- mypy/plugin.py | 2 +- test-data/unit/plugins/add_classmethod.py | 8 ++-- test-data/unit/plugins/arg_kinds.py | 19 ++++---- test-data/unit/plugins/arg_names.py | 48 ++++++++++++------- test-data/unit/plugins/attrhook.py | 14 +++--- test-data/unit/plugins/attrhook2.py | 16 ++++--- test-data/unit/plugins/badreturn.py | 2 +- test-data/unit/plugins/badreturn2.py | 6 ++- test-data/unit/plugins/callable_instance.py | 19 +++++--- test-data/unit/plugins/class_attr_hook.py | 15 +++--- test-data/unit/plugins/class_callable.py | 41 ++++++++++------ .../unit/plugins/common_api_incremental.py | 36 +++++++------- test-data/unit/plugins/config_data.py | 9 ++-- test-data/unit/plugins/custom_errorcode.py | 14 ++++-- test-data/unit/plugins/customentry.py | 20 +++++--- test-data/unit/plugins/customize_mro.py | 15 ++++-- test-data/unit/plugins/decimal_to_int.py | 19 +++++--- test-data/unit/plugins/depshook.py | 12 ++--- test-data/unit/plugins/descriptor.py | 32 ++++++++----- test-data/unit/plugins/dyn_class.py | 46 +++++++++++------- .../unit/plugins/dyn_class_from_method.py | 24 +++++++--- test-data/unit/plugins/fnplugin.py | 20 +++++--- .../unit/plugins/fully_qualified_test_hook.py | 24 +++++++--- test-data/unit/plugins/function_sig_hook.py | 20 ++++---- test-data/unit/plugins/method_in_decorator.py | 22 +++++---- test-data/unit/plugins/method_sig_hook.py | 27 +++++++---- test-data/unit/plugins/named_callable.py | 31 +++++++----- test-data/unit/plugins/plugin2.py | 20 +++++--- test-data/unit/plugins/type_anal_hook.py | 31 ++++++------ test-data/unit/plugins/union_method.py | 32 ++++++++----- tox.ini | 1 + 31 files changed, 407 insertions(+), 238 deletions(-) diff --git a/mypy/plugin.py b/mypy/plugin.py index 4d62c2bd184b..38016191de8f 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -247,7 +247,7 @@ def fail( @abstractmethod def named_generic_type(self, name: str, args: list[Type]) -> Instance: - """Construct an instance of a builtin type with given type arguments.""" + """Construct an instance of a generic type with given type arguments.""" raise NotImplementedError @abstractmethod diff --git a/test-data/unit/plugins/add_classmethod.py b/test-data/unit/plugins/add_classmethod.py index 5aacc69a8f01..9bc2c4e079dd 100644 --- a/test-data/unit/plugins/add_classmethod.py +++ b/test-data/unit/plugins/add_classmethod.py @@ -1,4 +1,6 @@ -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable from mypy.nodes import ARG_POS, Argument, Var from mypy.plugin import ClassDefContext, Plugin @@ -7,7 +9,7 @@ class ClassMethodPlugin(Plugin): - def get_base_class_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]: + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: if "BaseAddMethod" in fullname: return add_extra_methods_hook return None @@ -24,5 +26,5 @@ def add_extra_methods_hook(ctx: ClassDefContext) -> None: ) -def plugin(version): +def plugin(version: str) -> type[ClassMethodPlugin]: return ClassMethodPlugin diff --git a/test-data/unit/plugins/arg_kinds.py b/test-data/unit/plugins/arg_kinds.py index 5392e64c4f11..388a3c738b62 100644 --- a/test-data/unit/plugins/arg_kinds.py +++ b/test-data/unit/plugins/arg_kinds.py @@ -1,18 +1,19 @@ -from typing import Optional, Callable -from mypy.plugin import Plugin, MethodContext, FunctionContext +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.types import Type class ArgKindsPlugin(Plugin): - def get_function_hook(self, fullname: str - ) -> Optional[Callable[[FunctionContext], Type]]: - if 'func' in fullname: + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if "func" in fullname: return extract_arg_kinds_from_function return None - def get_method_hook(self, fullname: str - ) -> Optional[Callable[[MethodContext], Type]]: - if 'Class.method' in fullname: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if "Class.method" in fullname: return extract_arg_kinds_from_method return None @@ -27,5 +28,5 @@ def extract_arg_kinds_from_method(ctx: MethodContext) -> Type: return ctx.default_return_type -def plugin(version): +def plugin(version: str) -> type[ArgKindsPlugin]: return ArgKindsPlugin diff --git a/test-data/unit/plugins/arg_names.py b/test-data/unit/plugins/arg_names.py index 6c1cbb9415cc..981c1a2eb12d 100644 --- a/test-data/unit/plugins/arg_names.py +++ b/test-data/unit/plugins/arg_names.py @@ -1,35 +1,51 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, MethodContext, FunctionContext +from typing import Callable + +from mypy.nodes import StrExpr +from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.types import Type class ArgNamesPlugin(Plugin): - def get_function_hook(self, fullname: str - ) -> Optional[Callable[[FunctionContext], Type]]: - if fullname in {'mod.func', 'mod.func_unfilled', 'mod.func_star_expr', - 'mod.ClassInit', 'mod.Outer.NestedClassInit'}: + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname in { + "mod.func", + "mod.func_unfilled", + "mod.func_star_expr", + "mod.ClassInit", + "mod.Outer.NestedClassInit", + }: return extract_classname_and_set_as_return_type_function return None - def get_method_hook(self, fullname: str - ) -> Optional[Callable[[MethodContext], Type]]: - if fullname in {'mod.Class.method', 'mod.Class.myclassmethod', 'mod.Class.mystaticmethod', - 'mod.ClassUnfilled.method', 'mod.ClassStarExpr.method', - 'mod.ClassChild.method', 'mod.ClassChild.myclassmethod'}: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if fullname in { + "mod.Class.method", + "mod.Class.myclassmethod", + "mod.Class.mystaticmethod", + "mod.ClassUnfilled.method", + "mod.ClassStarExpr.method", + "mod.ClassChild.method", + "mod.ClassChild.myclassmethod", + }: return extract_classname_and_set_as_return_type_method return None def extract_classname_and_set_as_return_type_function(ctx: FunctionContext) -> Type: - classname = ctx.args[ctx.callee_arg_names.index('classname')][0].value - return ctx.api.named_generic_type(classname, []) + arg = ctx.args[ctx.callee_arg_names.index("classname")][0] + if not isinstance(arg, StrExpr): + return ctx.default_return_type + return ctx.api.named_generic_type(arg.value, []) def extract_classname_and_set_as_return_type_method(ctx: MethodContext) -> Type: - classname = ctx.args[ctx.callee_arg_names.index('classname')][0].value - return ctx.api.named_generic_type(classname, []) + arg = ctx.args[ctx.callee_arg_names.index("classname")][0] + if not isinstance(arg, StrExpr): + return ctx.default_return_type + return ctx.api.named_generic_type(arg.value, []) -def plugin(version): +def plugin(version: str) -> type[ArgNamesPlugin]: return ArgNamesPlugin diff --git a/test-data/unit/plugins/attrhook.py b/test-data/unit/plugins/attrhook.py index c177072aa47f..9500734daa6c 100644 --- a/test-data/unit/plugins/attrhook.py +++ b/test-data/unit/plugins/attrhook.py @@ -1,12 +1,14 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, AttributeContext -from mypy.types import Type, Instance +from typing import Callable + +from mypy.plugin import AttributeContext, Plugin +from mypy.types import Instance, Type class AttrPlugin(Plugin): - def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeContext], Type]]: - if fullname == 'm.Signal.__call__': + def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: + if fullname == "m.Signal.__call__": return signal_call_callback return None @@ -17,5 +19,5 @@ def signal_call_callback(ctx: AttributeContext) -> Type: return ctx.default_attr_type -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/attrhook2.py b/test-data/unit/plugins/attrhook2.py index cc14341a6f97..2d41a0fdf52f 100644 --- a/test-data/unit/plugins/attrhook2.py +++ b/test-data/unit/plugins/attrhook2.py @@ -1,14 +1,16 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, AttributeContext -from mypy.types import Type, AnyType, TypeOfAny +from typing import Callable + +from mypy.plugin import AttributeContext, Plugin +from mypy.types import AnyType, Type, TypeOfAny class AttrPlugin(Plugin): - def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeContext], Type]]: - if fullname == 'm.Magic.magic_field': + def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: + if fullname == "m.Magic.magic_field": return magic_field_callback - if fullname == 'm.Magic.nonexistent_field': + if fullname == "m.Magic.nonexistent_field": return nonexistent_field_callback return None @@ -22,5 +24,5 @@ def nonexistent_field_callback(ctx: AttributeContext) -> Type: return AnyType(TypeOfAny.from_error) -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/badreturn.py b/test-data/unit/plugins/badreturn.py index fd7430606dd6..9dce3b3e99c2 100644 --- a/test-data/unit/plugins/badreturn.py +++ b/test-data/unit/plugins/badreturn.py @@ -1,2 +1,2 @@ -def plugin(version): +def plugin(version: str) -> None: pass diff --git a/test-data/unit/plugins/badreturn2.py b/test-data/unit/plugins/badreturn2.py index c7e0447841c1..1ae551ecbf20 100644 --- a/test-data/unit/plugins/badreturn2.py +++ b/test-data/unit/plugins/badreturn2.py @@ -1,5 +1,9 @@ +from __future__ import annotations + + class MyPlugin: pass -def plugin(version): + +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/callable_instance.py b/test-data/unit/plugins/callable_instance.py index 40e7df418539..a9f562effb34 100644 --- a/test-data/unit/plugins/callable_instance.py +++ b/test-data/unit/plugins/callable_instance.py @@ -1,23 +1,30 @@ +from __future__ import annotations + +from typing import Callable + from mypy.plugin import MethodContext, Plugin from mypy.types import Instance, Type + class CallableInstancePlugin(Plugin): - def get_function_hook(self, fullname): - assert not fullname.endswith(' of Foo') + def get_function_hook(self, fullname: str) -> None: + assert not fullname.endswith(" of Foo") - def get_method_hook(self, fullname): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: # Ensure that all names are fully qualified - assert not fullname.endswith(' of Foo') + assert not fullname.endswith(" of Foo") - if fullname == '__main__.Class.__call__': + if fullname == "__main__.Class.__call__": return my_hook return None + def my_hook(ctx: MethodContext) -> Type: if isinstance(ctx.type, Instance) and len(ctx.type.args) == 1: return ctx.type.args[0] return ctx.default_return_type -def plugin(version): + +def plugin(version: str) -> type[CallableInstancePlugin]: return CallableInstancePlugin diff --git a/test-data/unit/plugins/class_attr_hook.py b/test-data/unit/plugins/class_attr_hook.py index 348e5df0ee03..5d6a87df48bb 100644 --- a/test-data/unit/plugins/class_attr_hook.py +++ b/test-data/unit/plugins/class_attr_hook.py @@ -1,20 +1,23 @@ -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable from mypy.plugin import AttributeContext, Plugin from mypy.types import Type as MypyType class ClassAttrPlugin(Plugin): - def get_class_attribute_hook(self, fullname: str - ) -> Optional[Callable[[AttributeContext], MypyType]]: - if fullname == '__main__.Cls.attr': + def get_class_attribute_hook( + self, fullname: str + ) -> Callable[[AttributeContext], MypyType] | None: + if fullname == "__main__.Cls.attr": return my_hook return None def my_hook(ctx: AttributeContext) -> MypyType: - return ctx.api.named_generic_type('builtins.int', []) + return ctx.api.named_generic_type("builtins.int", []) -def plugin(_version: str): +def plugin(_version: str) -> type[ClassAttrPlugin]: return ClassAttrPlugin diff --git a/test-data/unit/plugins/class_callable.py b/test-data/unit/plugins/class_callable.py index 07f75ec80ac1..9fab30e60458 100644 --- a/test-data/unit/plugins/class_callable.py +++ b/test-data/unit/plugins/class_callable.py @@ -1,32 +1,43 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + from mypy.nodes import NameExpr -from mypy.types import UnionType, NoneType, Instance +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Instance, NoneType, Type, UnionType, get_proper_type + class AttrPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname.startswith('mod.Attr'): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname.startswith("mod.Attr"): return attr_hook return None -def attr_hook(ctx): - assert isinstance(ctx.default_return_type, Instance) - if ctx.default_return_type.type.fullname == 'mod.Attr': - attr_base = ctx.default_return_type + +def attr_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + assert isinstance(default, Instance) + if default.type.fullname == "mod.Attr": + attr_base = default else: attr_base = None - for base in ctx.default_return_type.type.bases: - if base.type.fullname == 'mod.Attr': + for base in default.type.bases: + if base.type.fullname == "mod.Attr": attr_base = base break assert attr_base is not None last_arg_exprs = ctx.args[-1] - if any(isinstance(expr, NameExpr) and expr.name == 'True' for expr in last_arg_exprs): + if any(isinstance(expr, NameExpr) and expr.name == "True" for expr in last_arg_exprs): return attr_base assert len(attr_base.args) == 1 arg_type = attr_base.args[0] - return Instance(attr_base.type, [UnionType([arg_type, NoneType()])], - line=ctx.default_return_type.line, - column=ctx.default_return_type.column) + return Instance( + attr_base.type, + [UnionType([arg_type, NoneType()])], + line=default.line, + column=default.column, + ) + -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/common_api_incremental.py b/test-data/unit/plugins/common_api_incremental.py index 2dcd559777ec..b14b2f92073e 100644 --- a/test-data/unit/plugins/common_api_incremental.py +++ b/test-data/unit/plugins/common_api_incremental.py @@ -1,44 +1,48 @@ -from mypy.plugin import Plugin -from mypy.nodes import ( - ClassDef, Block, TypeInfo, SymbolTable, SymbolTableNode, MDEF, GDEF, Var -) +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, MDEF, Block, ClassDef, SymbolTable, SymbolTableNode, TypeInfo, Var +from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if fullname == 'lib.declarative_base': + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if fullname == "lib.declarative_base": return add_info_hook return None - def get_base_class_hook(self, fullname: str): + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: sym = self.lookup_fully_qualified(fullname) if sym and isinstance(sym.node, TypeInfo): - if sym.node.metadata.get('magic'): + if sym.node.metadata.get("magic"): return add_magic_hook return None -def add_info_hook(ctx) -> None: +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info - obj = ctx.api.named_type('builtins.object') + obj = ctx.api.named_type("builtins.object", []) info.mro = [info, obj.type] info.bases = [obj] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) - info.metadata['magic'] = True + info.metadata["magic"] = {"value": True} -def add_magic_hook(ctx) -> None: +def add_magic_hook(ctx: ClassDefContext) -> None: info = ctx.cls.info - str_type = ctx.api.named_type_or_none('builtins.str', []) + str_type = ctx.api.named_type_or_none("builtins.str", []) assert str_type is not None - var = Var('__magic__', str_type) + var = Var("__magic__", str_type) var.info = info - info.names['__magic__'] = SymbolTableNode(MDEF, var) + info.names["__magic__"] = SymbolTableNode(MDEF, var) -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/config_data.py b/test-data/unit/plugins/config_data.py index 059e036d5e32..9b828bc9ac0a 100644 --- a/test-data/unit/plugins/config_data.py +++ b/test-data/unit/plugins/config_data.py @@ -1,6 +1,7 @@ -import os -import json +from __future__ import annotations +import json +import os from typing import Any from mypy.plugin import Plugin, ReportConfigContext @@ -8,11 +9,11 @@ class ConfigDataPlugin(Plugin): def report_config_data(self, ctx: ReportConfigContext) -> Any: - path = os.path.join('tmp/test.json') + path = os.path.join("tmp/test.json") with open(path) as f: data = json.load(f) return data.get(ctx.id) -def plugin(version): +def plugin(version: str) -> type[ConfigDataPlugin]: return ConfigDataPlugin diff --git a/test-data/unit/plugins/custom_errorcode.py b/test-data/unit/plugins/custom_errorcode.py index 0e2209a32eca..0af87658e59f 100644 --- a/test-data/unit/plugins/custom_errorcode.py +++ b/test-data/unit/plugins/custom_errorcode.py @@ -1,20 +1,24 @@ +from __future__ import annotations + +from typing import Callable + from mypy.errorcodes import ErrorCode -from mypy.plugin import Plugin -from mypy.types import AnyType, TypeOfAny +from mypy.plugin import FunctionContext, Plugin +from mypy.types import AnyType, Type, TypeOfAny CUSTOM_ERROR = ErrorCode(code="custom", description="", category="Custom") class CustomErrorCodePlugin(Plugin): - def get_function_hook(self, fullname): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: if fullname.endswith(".main"): return self.emit_error return None - def emit_error(self, ctx): + def emit_error(self, ctx: FunctionContext) -> Type: ctx.api.fail("Custom error", ctx.context, code=CUSTOM_ERROR) return AnyType(TypeOfAny.from_error) -def plugin(version): +def plugin(version: str) -> type[CustomErrorCodePlugin]: return CustomErrorCodePlugin diff --git a/test-data/unit/plugins/customentry.py b/test-data/unit/plugins/customentry.py index b3dacfd4cf44..1a7ed3348e12 100644 --- a/test-data/unit/plugins/customentry.py +++ b/test-data/unit/plugins/customentry.py @@ -1,14 +1,22 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == '__main__.f': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "__main__.f": return my_hook assert fullname return None -def my_hook(ctx): - return ctx.api.named_generic_type('builtins.int', []) -def register(version): +def my_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.int", []) + + +def register(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/customize_mro.py b/test-data/unit/plugins/customize_mro.py index 0f2396d98965..3b13b2e9d998 100644 --- a/test-data/unit/plugins/customize_mro.py +++ b/test-data/unit/plugins/customize_mro.py @@ -1,10 +1,17 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import ClassDefContext, Plugin + class DummyPlugin(Plugin): - def get_customize_class_mro_hook(self, fullname): - def analyze(classdef_ctx): + def get_customize_class_mro_hook(self, fullname: str) -> Callable[[ClassDefContext], None]: + def analyze(classdef_ctx: ClassDefContext) -> None: pass + return analyze -def plugin(version): + +def plugin(version: str) -> type[DummyPlugin]: return DummyPlugin diff --git a/test-data/unit/plugins/decimal_to_int.py b/test-data/unit/plugins/decimal_to_int.py index 94aa33ef6df1..2318b2367d33 100644 --- a/test-data/unit/plugins/decimal_to_int.py +++ b/test-data/unit/plugins/decimal_to_int.py @@ -1,14 +1,21 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import AnalyzeTypeContext, Plugin +from mypy.types import Type class MyPlugin(Plugin): - def get_type_analyze_hook(self, fullname): + def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None: if fullname in ("decimal.Decimal", "_decimal.Decimal"): return decimal_to_int_hook return None -def plugin(version): - return MyPlugin -def decimal_to_int_hook(ctx): - return ctx.api.named_type('builtins.int', []) +def decimal_to_int_hook(ctx: AnalyzeTypeContext) -> Type: + return ctx.api.named_type("builtins.int", []) + + +def plugin(version: str) -> type[MyPlugin]: + return MyPlugin diff --git a/test-data/unit/plugins/depshook.py b/test-data/unit/plugins/depshook.py index 76277f3cb82b..bb2460de1196 100644 --- a/test-data/unit/plugins/depshook.py +++ b/test-data/unit/plugins/depshook.py @@ -1,15 +1,15 @@ -from typing import List, Tuple +from __future__ import annotations -from mypy.plugin import Plugin from mypy.nodes import MypyFile +from mypy.plugin import Plugin class DepsPlugin(Plugin): - def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]: - if file.fullname == '__main__': - return [(10, 'err', -1)] + def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]: + if file.fullname == "__main__": + return [(10, "err", -1)] return [] -def plugin(version): +def plugin(version: str) -> type[DepsPlugin]: return DepsPlugin diff --git a/test-data/unit/plugins/descriptor.py b/test-data/unit/plugins/descriptor.py index afbadcdfb671..d38853367906 100644 --- a/test-data/unit/plugins/descriptor.py +++ b/test-data/unit/plugins/descriptor.py @@ -1,28 +1,38 @@ -from mypy.plugin import Plugin -from mypy.types import NoneType, CallableType +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import MethodContext, MethodSigContext, Plugin +from mypy.types import CallableType, NoneType, Type, get_proper_type class DescriptorPlugin(Plugin): - def get_method_hook(self, fullname): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: if fullname == "__main__.Desc.__get__": return get_hook return None - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: if fullname == "__main__.Desc.__set__": return set_hook return None -def get_hook(ctx): - if isinstance(ctx.arg_types[0][0], NoneType): - return ctx.api.named_type("builtins.str") - return ctx.api.named_type("builtins.int") +def get_hook(ctx: MethodContext) -> Type: + arg = get_proper_type(ctx.arg_types[0][0]) + if isinstance(arg, NoneType): + return ctx.api.named_generic_type("builtins.str", []) + return ctx.api.named_generic_type("builtins.int", []) -def set_hook(ctx): +def set_hook(ctx: MethodSigContext) -> CallableType: return CallableType( - [ctx.api.named_type("__main__.Cls"), ctx.api.named_type("builtins.int")], + [ + ctx.api.named_generic_type("__main__.Cls", []), + ctx.api.named_generic_type("builtins.int", []), + ], ctx.default_signature.arg_kinds, ctx.default_signature.arg_names, ctx.default_signature.ret_type, @@ -30,5 +40,5 @@ def set_hook(ctx): ) -def plugin(version): +def plugin(version: str) -> type[DescriptorPlugin]: return DescriptorPlugin diff --git a/test-data/unit/plugins/dyn_class.py b/test-data/unit/plugins/dyn_class.py index 54bf377aa8ef..18e948e3dd2a 100644 --- a/test-data/unit/plugins/dyn_class.py +++ b/test-data/unit/plugins/dyn_class.py @@ -1,47 +1,57 @@ -from mypy.plugin import Plugin -from mypy.nodes import ( - ClassDef, Block, TypeInfo, SymbolTable, SymbolTableNode, GDEF, Var -) -from mypy.types import Instance +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, Block, ClassDef, SymbolTable, SymbolTableNode, TypeInfo, Var +from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin +from mypy.types import Instance, get_proper_type DECL_BASES = set() + class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if fullname == 'mod.declarative_base': + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if fullname == "mod.declarative_base": return add_info_hook return None - def get_base_class_hook(self, fullname: str): + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: if fullname in DECL_BASES: return replace_col_hook return None -def add_info_hook(ctx): + +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info - obj = ctx.api.named_type('builtins.object') + obj = ctx.api.named_type("builtins.object") info.mro = [info, obj.type] info.bases = [obj] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) DECL_BASES.add(class_def.fullname) -def replace_col_hook(ctx): + +def replace_col_hook(ctx: ClassDefContext) -> None: info = ctx.cls.info for sym in info.names.values(): node = sym.node - if isinstance(node, Var) and isinstance(node.type, Instance): - if node.type.type.fullname == 'mod.Column': - new_sym = ctx.api.lookup_fully_qualified_or_none('mod.Instr') + if isinstance(node, Var) and isinstance( + (node_type := get_proper_type(node.type)), Instance + ): + if node_type.type.fullname == "mod.Column": + new_sym = ctx.api.lookup_fully_qualified_or_none("mod.Instr") if new_sym: new_info = new_sym.node assert isinstance(new_info, TypeInfo) - node.type = Instance(new_info, node.type.args, - node.type.line, - node.type.column) + node.type = Instance( + new_info, node_type.args, node_type.line, node_type.column + ) + -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/dyn_class_from_method.py b/test-data/unit/plugins/dyn_class_from_method.py index 4c3904907750..b84754654084 100644 --- a/test-data/unit/plugins/dyn_class_from_method.py +++ b/test-data/unit/plugins/dyn_class_from_method.py @@ -1,28 +1,38 @@ -from mypy.nodes import (Block, ClassDef, GDEF, SymbolTable, SymbolTableNode, TypeInfo) +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, Block, ClassDef, RefExpr, SymbolTable, SymbolTableNode, TypeInfo from mypy.plugin import DynamicClassDefContext, Plugin from mypy.types import Instance class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if 'from_queryset' in fullname: + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if "from_queryset" in fullname: return add_info_hook return None -def add_info_hook(ctx: DynamicClassDefContext): +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info + assert isinstance(ctx.call.args[0], RefExpr) queryset_type_fullname = ctx.call.args[0].fullname - queryset_info = ctx.api.lookup_fully_qualified_or_none(queryset_type_fullname).node # type: TypeInfo - obj = ctx.api.named_type('builtins.object') + queryset_node = ctx.api.lookup_fully_qualified_or_none(queryset_type_fullname) + assert queryset_node is not None + queryset_info = queryset_node.node + assert isinstance(queryset_info, TypeInfo) + obj = ctx.api.named_type("builtins.object") info.mro = [info, queryset_info, obj.type] info.bases = [Instance(queryset_info, [])] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/fnplugin.py b/test-data/unit/plugins/fnplugin.py index 684d6343458e..a5a7e57101c2 100644 --- a/test-data/unit/plugins/fnplugin.py +++ b/test-data/unit/plugins/fnplugin.py @@ -1,14 +1,22 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == '__main__.f': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "__main__.f": return my_hook assert fullname is not None return None -def my_hook(ctx): - return ctx.api.named_generic_type('builtins.int', []) -def plugin(version): +def my_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.int", []) + + +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/fully_qualified_test_hook.py b/test-data/unit/plugins/fully_qualified_test_hook.py index 529cf25a1215..9230091bba1a 100644 --- a/test-data/unit/plugins/fully_qualified_test_hook.py +++ b/test-data/unit/plugins/fully_qualified_test_hook.py @@ -1,16 +1,28 @@ -from mypy.plugin import CallableType, MethodSigContext, Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import MethodSigContext, Plugin +from mypy.types import CallableType + class FullyQualifiedTestPlugin(Plugin): - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: # Ensure that all names are fully qualified - if 'FullyQualifiedTest' in fullname: - assert fullname.startswith('__main__.') and not ' of ' in fullname, fullname + if "FullyQualifiedTest" in fullname: + assert fullname.startswith("__main__.") and " of " not in fullname, fullname return my_hook return None + def my_hook(ctx: MethodSigContext) -> CallableType: - return ctx.default_signature.copy_modified(ret_type=ctx.api.named_generic_type('builtins.int', [])) + return ctx.default_signature.copy_modified( + ret_type=ctx.api.named_generic_type("builtins.int", []) + ) + -def plugin(version): +def plugin(version: str) -> type[FullyQualifiedTestPlugin]: return FullyQualifiedTestPlugin diff --git a/test-data/unit/plugins/function_sig_hook.py b/test-data/unit/plugins/function_sig_hook.py index 4d901b96716e..a8d3cf058062 100644 --- a/test-data/unit/plugins/function_sig_hook.py +++ b/test-data/unit/plugins/function_sig_hook.py @@ -1,9 +1,16 @@ -from mypy.plugin import CallableType, FunctionSigContext, Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionSigContext, Plugin +from mypy.types import CallableType class FunctionSigPlugin(Plugin): - def get_function_signature_hook(self, fullname): - if fullname == '__main__.dynamic_signature': + def get_function_signature_hook( + self, fullname: str + ) -> Callable[[FunctionSigContext], CallableType] | None: + if fullname == "__main__.dynamic_signature": return my_hook return None @@ -13,11 +20,8 @@ def my_hook(ctx: FunctionSigContext) -> CallableType: if len(arg1_args) != 1: return ctx.default_signature arg1_type = ctx.api.get_expression_type(arg1_args[0]) - return ctx.default_signature.copy_modified( - arg_types=[arg1_type], - ret_type=arg1_type, - ) + return ctx.default_signature.copy_modified(arg_types=[arg1_type], ret_type=arg1_type) -def plugin(version): +def plugin(version: str) -> type[FunctionSigPlugin]: return FunctionSigPlugin diff --git a/test-data/unit/plugins/method_in_decorator.py b/test-data/unit/plugins/method_in_decorator.py index 99774dfcc7ef..3fba7692266c 100644 --- a/test-data/unit/plugins/method_in_decorator.py +++ b/test-data/unit/plugins/method_in_decorator.py @@ -1,19 +1,25 @@ -from mypy.types import CallableType, Type -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable + from mypy.plugin import MethodContext, Plugin +from mypy.types import CallableType, Type, get_proper_type class MethodDecoratorPlugin(Plugin): - def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]: - if 'Foo.a' in fullname: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if "Foo.a" in fullname: return method_decorator_callback return None + def method_decorator_callback(ctx: MethodContext) -> Type: - if isinstance(ctx.default_return_type, CallableType): - str_type = ctx.api.named_generic_type('builtins.str', []) - return ctx.default_return_type.copy_modified(ret_type=str_type) + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + str_type = ctx.api.named_generic_type("builtins.str", []) + return default.copy_modified(ret_type=str_type) return ctx.default_return_type -def plugin(version): + +def plugin(version: str) -> type[MethodDecoratorPlugin]: return MethodDecoratorPlugin diff --git a/test-data/unit/plugins/method_sig_hook.py b/test-data/unit/plugins/method_sig_hook.py index 25c2842e6620..b78831cc45d5 100644 --- a/test-data/unit/plugins/method_sig_hook.py +++ b/test-data/unit/plugins/method_sig_hook.py @@ -1,30 +1,41 @@ -from mypy.plugin import CallableType, CheckerPluginInterface, MethodSigContext, Plugin -from mypy.types import Instance, Type +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import CheckerPluginInterface, MethodSigContext, Plugin +from mypy.types import CallableType, Instance, Type, get_proper_type + class MethodSigPlugin(Plugin): - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: # Ensure that all names are fully qualified - assert not fullname.endswith(' of Foo') + assert not fullname.endswith(" of Foo") - if fullname.startswith('__main__.Foo.'): + if fullname.startswith("__main__.Foo."): return my_hook return None + def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.str": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) return typ + def my_hook(ctx: MethodSigContext) -> CallableType: return ctx.default_signature.copy_modified( arg_types=[_str_to_int(ctx.api, t) for t in ctx.default_signature.arg_types], ret_type=_str_to_int(ctx.api, ctx.default_signature.ret_type), ) -def plugin(version): + +def plugin(version: str) -> type[MethodSigPlugin]: return MethodSigPlugin diff --git a/test-data/unit/plugins/named_callable.py b/test-data/unit/plugins/named_callable.py index e40d181d2bad..c37e11c32125 100644 --- a/test-data/unit/plugins/named_callable.py +++ b/test-data/unit/plugins/named_callable.py @@ -1,28 +1,33 @@ -from mypy.plugin import Plugin -from mypy.types import CallableType +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import CallableType, Type, get_proper_type class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == 'm.decorator1': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "m.decorator1": return decorator_call_hook - if fullname == 'm._decorated': # This is a dummy name generated by the plugin + if fullname == "m._decorated": # This is a dummy name generated by the plugin return decorate_hook return None -def decorator_call_hook(ctx): - if isinstance(ctx.default_return_type, CallableType): - return ctx.default_return_type.copy_modified(name='m._decorated') +def decorator_call_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + return default.copy_modified(name="m._decorated") return ctx.default_return_type -def decorate_hook(ctx): - if isinstance(ctx.default_return_type, CallableType): - return ctx.default_return_type.copy_modified( - ret_type=ctx.api.named_generic_type('builtins.str', [])) +def decorate_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + return default.copy_modified(ret_type=ctx.api.named_generic_type("builtins.str", [])) return ctx.default_return_type -def plugin(version): +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/plugin2.py b/test-data/unit/plugins/plugin2.py index b530a62d23aa..e486d96ea8bf 100644 --- a/test-data/unit/plugins/plugin2.py +++ b/test-data/unit/plugins/plugin2.py @@ -1,13 +1,21 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class Plugin2(Plugin): - def get_function_hook(self, fullname): - if fullname in ('__main__.f', '__main__.g'): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname in ("__main__.f", "__main__.g"): return str_hook return None -def str_hook(ctx): - return ctx.api.named_generic_type('builtins.str', []) -def plugin(version): +def str_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.str", []) + + +def plugin(version: str) -> type[Plugin2]: return Plugin2 diff --git a/test-data/unit/plugins/type_anal_hook.py b/test-data/unit/plugins/type_anal_hook.py index 86d18d8c8611..c380bbe873fe 100644 --- a/test-data/unit/plugins/type_anal_hook.py +++ b/test-data/unit/plugins/type_anal_hook.py @@ -1,22 +1,23 @@ -from typing import Optional, Callable +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import AnalyzeTypeContext, Plugin -from mypy.plugin import Plugin, AnalyzeTypeContext -from mypy.types import Type, TypeList, AnyType, CallableType, TypeOfAny # The official name changed to NoneType but we have an alias for plugin compat reasons # so we'll keep testing that here. -from mypy.types import NoneTyp +from mypy.types import AnyType, CallableType, NoneTyp, Type, TypeList, TypeOfAny + class TypeAnalyzePlugin(Plugin): - def get_type_analyze_hook(self, fullname: str - ) -> Optional[Callable[[AnalyzeTypeContext], Type]]: - if fullname == 'm.Signal': + def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None: + if fullname == "m.Signal": return signal_type_analyze_callback return None def signal_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type: - if (len(ctx.type.args) != 1 - or not isinstance(ctx.type.args[0], TypeList)): + if len(ctx.type.args) != 1 or not isinstance(ctx.type.args[0], TypeList): ctx.api.fail('Invalid "Signal" type (expected "Signal[[t, ...]]")', ctx.context) return AnyType(TypeOfAny.from_error) @@ -27,13 +28,11 @@ def signal_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type: return AnyType(TypeOfAny.from_error) # Error generated elsewhere arg_types, arg_kinds, arg_names = analyzed arg_types = [ctx.api.analyze_type(arg) for arg in arg_types] - type_arg = CallableType(arg_types, - arg_kinds, - arg_names, - NoneTyp(), - ctx.api.named_type('builtins.function', [])) - return ctx.api.named_type('m.Signal', [type_arg]) + type_arg = CallableType( + arg_types, arg_kinds, arg_names, NoneTyp(), ctx.api.named_type("builtins.function", []) + ) + return ctx.api.named_type("m.Signal", [type_arg]) -def plugin(version): +def plugin(version: str) -> type[TypeAnalyzePlugin]: return TypeAnalyzePlugin diff --git a/test-data/unit/plugins/union_method.py b/test-data/unit/plugins/union_method.py index a7621553f6ad..7c62ffb8c0cc 100644 --- a/test-data/unit/plugins/union_method.py +++ b/test-data/unit/plugins/union_method.py @@ -1,34 +1,40 @@ -from mypy.plugin import ( - CallableType, CheckerPluginInterface, MethodSigContext, MethodContext, Plugin -) -from mypy.types import Instance, Type +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import CheckerPluginInterface, MethodContext, MethodSigContext, Plugin +from mypy.types import CallableType, Instance, Type, get_proper_type class MethodPlugin(Plugin): - def get_method_signature_hook(self, fullname): - if fullname.startswith('__main__.Foo.'): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: + if fullname.startswith("__main__.Foo."): return my_meth_sig_hook return None - def get_method_hook(self, fullname): - if fullname.startswith('__main__.Bar.'): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if fullname.startswith("__main__.Bar."): return my_meth_hook return None def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.str": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) return typ def _float_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.float': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.float": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_float_to_int(api, t) for t in typ.args]) return typ @@ -45,5 +51,5 @@ def my_meth_hook(ctx: MethodContext) -> Type: return _float_to_int(ctx.api, ctx.default_return_type) -def plugin(version): +def plugin(version: str) -> type[MethodPlugin]: return MethodPlugin diff --git a/tox.ini b/tox.ini index a809c4d2c570..e07acdc5200d 100644 --- a/tox.ini +++ b/tox.ini @@ -55,3 +55,4 @@ passenv = commands = python runtests.py self python -m mypy --config-file mypy_self_check.ini misc --exclude misc/sync-typeshed.py + python -m mypy --config-file mypy_self_check.ini test-data/unit/plugins From bd212bcc2229779c0f6c96b16bf9d685e98884c1 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Sep 2023 18:43:05 +0300 Subject: [PATCH 0247/1617] Remove type aliases that are long supported (#16039) Some builtin aliases are available for all python versions that we support. So, there's no need to check them in `semanal`: https://github.com/python/mypy/blob/8738886861682e0d168ea321c2cc6ee5b566cb8b/mypy/semanal.py#L673-L689 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/nodes.py | 13 +------------ mypy/semanal.py | 5 ++++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index db42dd6b3949..d29e99ccace7 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -138,18 +138,7 @@ def set_line( # This keeps track of the oldest supported Python version where the corresponding # alias source is available. -type_aliases_source_versions: Final = { - "typing.List": (2, 7), - "typing.Dict": (2, 7), - "typing.Set": (2, 7), - "typing.FrozenSet": (2, 7), - "typing.ChainMap": (3, 3), - "typing.Counter": (2, 7), - "typing.DefaultDict": (2, 7), - "typing.Deque": (2, 7), - "typing.OrderedDict": (3, 7), - "typing.LiteralString": (3, 11), -} +type_aliases_source_versions: Final = {"typing.LiteralString": (3, 11)} # This keeps track of aliases in `typing_extensions`, which we treat specially. typing_extensions_aliases: Final = { diff --git a/mypy/semanal.py b/mypy/semanal.py index be7e733a0816..ec4d32aefeb9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -680,7 +680,10 @@ def add_builtin_aliases(self, tree: MypyFile) -> None: """ assert tree.fullname == "typing" for alias, target_name in type_aliases.items(): - if type_aliases_source_versions[alias] > self.options.python_version: + if ( + alias in type_aliases_source_versions + and type_aliases_source_versions[alias] > self.options.python_version + ): # This alias is not available on this Python version. continue name = alias.split(".")[-1] From c712079e1cbd74e2ea37da02d66152810fb69903 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Sep 2023 18:44:51 +0300 Subject: [PATCH 0248/1617] Do not use deprecated `add_method` in `attrs` plugin (#16037) CC @ikonst --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/plugins/attrs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 6f5b6f35da07..3ddc234a7e4a 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -51,7 +51,7 @@ _get_bool_argument, _get_decorator_bool_argument, add_attribute_to_class, - add_method, + add_method_to_class, deserialize_and_fixup_type, ) from mypy.server.trigger import make_wildcard_trigger @@ -952,7 +952,9 @@ def add_method( tvd: If the method is generic these should be the type variables. """ self_type = self_type if self_type is not None else self.self_type - add_method(self.ctx, method_name, args, ret_type, self_type, tvd) + add_method_to_class( + self.ctx.api, self.ctx.cls, method_name, args, ret_type, self_type, tvd + ) def _get_attrs_init_type(typ: Instance) -> CallableType | None: From 4496a005a84f7daedc1ef2e801583127f5995f75 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Sep 2023 19:28:00 +0300 Subject: [PATCH 0249/1617] Use latest `actions/checkout@v4` (#16042) Looks like recent CI failures are related. Release docs: https://github.com/actions/checkout/releases/tag/v4.0.0 --- .github/workflows/build_wheels.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/mypy_primer.yml | 2 +- .github/workflows/sync_typeshed.yml | 2 +- .github/workflows/test.yml | 4 ++-- .github/workflows/test_stubgenc.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 3f4ea5e42f9b..f1438279673d 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'python/mypy' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.11' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 037738d4b3aa..6c53afb9aa7c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: TOX_SKIP_MISSING_INTERPRETERS: False VERIFY_MYPY_ERROR_CODES: 1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.8' diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 2958b8fc325b..f8991e27970a 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -33,7 +33,7 @@ jobs: shard-index: [0, 1, 2, 3, 4] fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: mypy_to_test fetch-depth: 0 diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index 1db2e846f099..de9e0aad599f 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -15,7 +15,7 @@ jobs: if: github.repository == 'python/mypy' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # TODO: use whatever solution ends up working for diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d2e7e7258500..3bcd9e059589 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -119,7 +119,7 @@ jobs: # Pytest PYTEST_ADDOPTS: --color=yes steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -162,7 +162,7 @@ jobs: CXX: i686-linux-gnu-g++ CC: i686-linux-gnu-gcc steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install 32-bit build dependencies run: | sudo dpkg --add-architecture i386 && \ diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 33466b9870ff..a2fb3e9dce6b 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup 🐍 3.8 uses: actions/setup-python@v4 From 5d9d13ebc9899ec43699b8e91ec5587d6f962283 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 5 Sep 2023 14:38:58 +0300 Subject: [PATCH 0250/1617] Document `force_union_syntax` and `force_uppercase_builtins` (#16048) Users don't know about them: https://github.com/typeddjango/pytest-mypy-plugins/issues/126 Since they are quite important for testing, I think that it is a must to include them. --------- Co-authored-by: Alex Waygood --- docs/source/config_file.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index c0798bbf03f1..b5ce23ff11ec 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -816,6 +816,22 @@ 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 + :default: False + + Always use ``Union[]`` and ``Optional[]`` for union types + in error messages (instead of the ``|`` operator), + even on Python 3.10+. Incremental mode **************** From c0906408c10d24d748711fa24be5befb2c794d4c Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 5 Sep 2023 14:57:38 +0300 Subject: [PATCH 0251/1617] Add docs about `--force-uppercase-builtins` and `--force-union-syntax` (#16049) Refs https://github.com/python/mypy/pull/16048 --- docs/source/command_line.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 727d500e2d4d..4e954c7c2ccb 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -789,6 +789,17 @@ in error messages. useful or they may be overly noisy. If ``N`` is negative, there is no limit. The default limit is 200. +.. 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 + in error messages (instead of the ``|`` operator), + even on Python 3.10+. + .. _incremental: From ed9b8990025a81a12e32bec59f2f3bfab3d7c71b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:43:24 +0200 Subject: [PATCH 0252/1617] Clear cache when adding --new-type-inference (#16059) Add `new_type_inference` to the list of options affecting the cache. --- mypy/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/options.py b/mypy/options.py index 5e451c0aa0a3..007ae0a78aa1 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -62,6 +62,7 @@ class BuildType: | { "platform", "bazel", + "new_type_inference", "plugins", "disable_bytearray_promotion", "disable_memoryview_promotion", From 175c5a59f18df9d56b3c2fb0e2a9669dd196c311 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Thu, 7 Sep 2023 18:30:07 +0200 Subject: [PATCH 0253/1617] Introduce error category [unsafe-overload] (#16061) fixes #16060 Co-authored-by: Alex Waygood --- mypy/errorcodes.py | 7 +++++++ mypy/messages.py | 1 + mypy/types.py | 2 +- test-data/unit/check-errorcodes.test | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 3594458fa362..70b8cffe9053 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -261,3 +261,10 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final = ErrorCode("misc", "Miscellaneous other checks", "General") + +UNSAFE_OVERLOAD: Final[ErrorCode] = ErrorCode( + "unsafe-overload", + "Warn if multiple @overload variants overlap in unsafe ways", + "General", + sub_code_of=MISC, +) diff --git a/mypy/messages.py b/mypy/messages.py index bba9c3c3cdea..a58c5f91c4b1 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1604,6 +1604,7 @@ def overloaded_signatures_overlap(self, index1: int, index2: int, context: Conte "Overloaded function signatures {} and {} overlap with " "incompatible return types".format(index1, index2), context, + code=codes.UNSAFE_OVERLOAD, ) def overloaded_signature_will_never_match( diff --git a/mypy/types.py b/mypy/types.py index f974157ce84d..cee4595b67cc 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3019,7 +3019,7 @@ def get_proper_type(typ: Type | None) -> ProperType | None: @overload -def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[misc] +def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[unsafe-overload] ... diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index df14e328ed72..72edf2f22c05 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1072,3 +1072,17 @@ A.f = h # type: ignore[assignment] # E: Unused "type: ignore" comment, use nar [case testUnusedIgnoreEnableCode] # flags: --enable-error-code=unused-ignore x = 1 # type: ignore # E: Unused "type: ignore" comment [unused-ignore] + +[case testErrorCodeUnsafeOverloadError] +from typing import overload, Union + +@overload +def unsafe_func(x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types [unsafe-overload] +@overload +def unsafe_func(x: object) -> str: ... +def unsafe_func(x: object) -> Union[int, str]: + if isinstance(x, int): + return 42 + else: + return "some string" +[builtins fixtures/isinstancelist.pyi] From 816ba3b33dd157def6b7d8c0b0fcca65ff2cbc05 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 7 Sep 2023 21:21:46 +0100 Subject: [PATCH 0254/1617] Build the docs in CI for all PRs touching the `mypy/` directory (#16068) 1. #16061 added a new error code, but didn't add any docs for the new error code 2. Because nothing in the `docs/` directory was modified, the docs CI job didn't run on that PR 3. Now the docs build is failing on `master` because we have an error code without any documentation: https://github.com/python/mypy/actions/runs/6112378542/job/16589719563 --- .github/workflows/docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6c53afb9aa7c..ad6b57c53fd9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,6 +8,10 @@ on: pull_request: paths: - 'docs/**' + # We now have a docs check that fails if any error codes don't have documentation, + # so it's important to do the docs build on all PRs touching mypy/errorcodes.py + # in case somebody's adding a new error code without any docs + - 'mypy/errorcodes.py' - 'mypyc/doc/**' - '**/*.rst' - '**/*.md' From 8b73cc22c6a251682f777b104677fa0e1ed5fd67 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 7 Sep 2023 23:23:25 +0100 Subject: [PATCH 0255/1617] Complete type analysis of variadic types (#15991) This PR closes the first part of support for `TypeVarTuple`: the "static" analysis of types (of course everything is static in mypy, but some parts are more static): `semanal`/`typeanal`, `expand_type()`, `map_instance_to_supertype()`, `erase_type()` (things that precede and/or form foundation for type inference and subtyping). This one was quite tricky, supporting unpacks of forward references required some thinking. What is included in this PR: * Moving argument count validation from `semanal_typeargs` to `typeanal`. In one of previous PRs I mentioned that `get_proper_type()` may be called during semantic analysis causing troubles if we have invalid aliases. So we need to move validation to early stage. For instances, this is not required, but I strongly prefer keeping instances and aliases similar. And ideally at some point we can combine the logic, since it gets more and more similar. At some point we may want to prohibit using `get_proper_type()` during semantic analysis, but I don't want to block `TypeVarTuple` support on this, since this may be a significant refactoring. * Fixing `map_instance_to_supertype()` and `erase_type()`. These two are straightforward, we either use `expand_type()` logic directly (by calling it), or following the same logic. * Few simplifications in `expandtype` and `typeops` following previous normalizations of representation, unless there is a flaw in my logic, removed branches should be all dead code. * Allow (only fixed) unpacks in argument lists for non-variadic types. They were prohibited for no good reason. * (Somewhat limited) support for forward references in unpacks. As I mentioned this one is tricky because of how forward references are represented. Usually they follow either a life cycle like: `Any` -> ``, or `` -> `` -> `` (second one is relatively rare and usually only appears for potentially recursive things like base classes or type alias targets). It looks like `` can never appear as a _valid_ unpack target, I don't have a proof for this, but I was not able to trigger this, so I am not handling it (possible downside is that there may be extra errors about invalid argument count for invalid unpack targets). If I am wrong and this can happen in some valid cases, we can add handling for unpacks of placeholders later. Currently, the handling for `Any` stage of forward references is following: if we detect it, we simply create a dummy valid alias or instance. This logic should work for the same reason having plain `Any` worked in the first place (and why all tests pass if we delete `visit_placeholder_type()`): because (almost) each time we analyze a type, it is either already complete, or we analyze it _from scratch_, i.e. we call `expr_to_unanalyzed_type()`, then `visit_unbound_type()` etc. We almost never store "partially analyzed" types (there are guards against incomplete references and placeholders in annotations), and when we do, it is done in a controlled way that guarantees a type will be re-analyzed again. Since this is such a tricky subject, I didn't add any complex logic to support more tricky use cases (like multiple forward references to fixed unpacks in single list). I propose that we release this, and then see what kind of bug reports we will get. * Additional validation for type arguments position to ensure that `TypeVarTuple`s are never split. Total count is not enough to ban case where we have type variables `[T, *Ts, S, U]` and arguments `[int, int, *Us, int]`. We need to explicitly ensure that actual suffix and prefix are longer or equal to formal ones. Such splitting would be very hard to support, and is explicitly banned by the PEP. * Few minor cleanups. Some random comments: * It is tricky to preserve valid parts of type arguments, if there is an argument count error involving an unpack. So after such error I simply set all arguments to `Any` (or `*tuple[Any, ...]` when needed). * I know there is some code duplication. I tried to factor it away, but it turned out non-trivial. I may do some de-duplication pass after everything is done, and it is easier to see the big picture. * Type applications (i.e. when we have `A[int, int]` in runtime context) are wild west currently. I decided to postpone variadic support for them to a separate PR, because there is already some support (we will just need to handle edge cases and more error conditions) and I wanted minimize size of this PR. * Something I wanted to mention in one of previous PRs but forgot: Long time ago I proposed to normalize away type aliases inside `Unpack`, but I abandoned this idea, it doesn't really give us any benefits. As I said, this is the last PR for the "static part", in the next PR I will work on fixing subtyping and inference for variadic instances. And then will continue with remaining items I mentioned in my master plan in https://github.com/python/mypy/pull/15924 Fixes https://github.com/python/mypy/issues/15978 --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/erasetype.py | 34 ++++- mypy/expandtype.py | 66 ++++----- mypy/maptype.py | 22 +-- mypy/semanal_typeargs.py | 61 ++------ mypy/test/testtypes.py | 2 +- mypy/typeanal.py | 177 ++++++++++++++++++------ mypy/typeops.py | 4 +- test-data/unit/check-typevar-tuple.test | 123 +++++++++++++++- 8 files changed, 329 insertions(+), 160 deletions(-) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index fbbb4f80b578..d1a01fb6c779 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -165,9 +165,41 @@ def visit_type_var(self, t: TypeVarType) -> Type: return self.replacement return t + # TODO: below two methods duplicate some logic with expand_type(). + # In fact, we may want to refactor this whole visitor to use expand_type(). + def visit_instance(self, t: Instance) -> Type: + result = super().visit_instance(t) + assert isinstance(result, ProperType) and isinstance(result, Instance) + if t.type.fullname == "builtins.tuple": + # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] + arg = result.args[0] + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + return unpacked + return result + + def visit_tuple_type(self, t: TupleType) -> Type: + result = super().visit_tuple_type(t) + assert isinstance(result, ProperType) and isinstance(result, TupleType) + if len(result.items) == 1: + # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] + item = result.items[0] + if isinstance(item, UnpackType): + unpacked = get_proper_type(item.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + if result.partial_fallback.type.fullname != "builtins.tuple": + # If it is a subtype (like named tuple) we need to preserve it, + # this essentially mimics the logic in tuple_fallback(). + return result.partial_fallback.accept(self) + return unpacked + return result + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: if self.erase_id(t.id): - return self.replacement + return t.tuple_fallback.copy_modified(args=[self.replacement]) return t def visit_param_spec(self, t: ParamSpecType) -> Type: diff --git a/mypy/expandtype.py b/mypy/expandtype.py index be8ecb9ccfd9..c29fcb167777 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -212,10 +212,15 @@ def visit_erased_type(self, t: ErasedType) -> Type: def visit_instance(self, t: Instance) -> Type: args = self.expand_types_with_unpack(list(t.args)) - if isinstance(args, list): - return t.copy_modified(args=args) - else: - return args + if t.type.fullname == "builtins.tuple": + # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] + arg = args[0] + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + args = list(unpacked.args) + return t.copy_modified(args=args) def visit_type_var(self, t: TypeVarType) -> Type: # Normally upper bounds can't contain other type variables, the only exception is @@ -285,7 +290,7 @@ def expand_unpack(self, t: UnpackType) -> list[Type]: ): return [UnpackType(typ=repl)] elif isinstance(repl, (AnyType, UninhabitedType)): - # Replace *Ts = Any with *Ts = *tuple[Any, ...] and some for Never. + # Replace *Ts = Any with *Ts = *tuple[Any, ...] and same for Never. # These types may appear here as a result of user error or failed inference. return [UnpackType(t.type.tuple_fallback.copy_modified(args=[repl]))] else: @@ -377,15 +382,8 @@ def visit_overloaded(self, t: Overloaded) -> Type: items.append(new_item) return Overloaded(items) - def expand_types_with_unpack( - self, typs: Sequence[Type] - ) -> list[Type] | AnyType | UninhabitedType: - """Expands a list of types that has an unpack. - - In corner cases, this can return a type rather than a list, in which case this - indicates use of Any or some error occurred earlier. In this case callers should - simply propagate the resulting type. - """ + def expand_types_with_unpack(self, typs: Sequence[Type]) -> list[Type]: + """Expands a list of types that has an unpack.""" items: list[Type] = [] for item in typs: if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): @@ -396,24 +394,21 @@ def expand_types_with_unpack( def visit_tuple_type(self, t: TupleType) -> Type: items = self.expand_types_with_unpack(t.items) - if isinstance(items, list): - if len(items) == 1: - # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] - item = items[0] - if isinstance(item, UnpackType): - unpacked = get_proper_type(item.type) - if isinstance(unpacked, Instance): - assert unpacked.type.fullname == "builtins.tuple" - if t.partial_fallback.type.fullname != "builtins.tuple": - # If it is a subtype (like named tuple) we need to preserve it, - # this essentially mimics the logic in tuple_fallback(). - return t.partial_fallback.accept(self) - return unpacked - fallback = t.partial_fallback.accept(self) - assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) - return t.copy_modified(items=items, fallback=fallback) - else: - return items + if len(items) == 1: + # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] + item = items[0] + if isinstance(item, UnpackType): + unpacked = get_proper_type(item.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + if t.partial_fallback.type.fullname != "builtins.tuple": + # If it is a subtype (like named tuple) we need to preserve it, + # this essentially mimics the logic in tuple_fallback(). + return t.partial_fallback.accept(self) + return unpacked + fallback = t.partial_fallback.accept(self) + assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) + return t.copy_modified(items=items, fallback=fallback) def visit_typeddict_type(self, t: TypedDictType) -> Type: fallback = t.fallback.accept(self) @@ -453,11 +448,8 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: # Target of the type alias cannot contain type variables (not bound by the type # alias itself), so we just expand the arguments. args = self.expand_types_with_unpack(t.args) - if isinstance(args, list): - # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]? - return t.copy_modified(args=args) - else: - return args + # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]? + return t.copy_modified(args=args) def expand_types(self, types: Iterable[Type]) -> list[Type]: a: list[Type] = [] diff --git a/mypy/maptype.py b/mypy/maptype.py index 4951306573c2..0d54a83127df 100644 --- a/mypy/maptype.py +++ b/mypy/maptype.py @@ -1,8 +1,8 @@ from __future__ import annotations -from mypy.expandtype import expand_type +from mypy.expandtype import expand_type_by_instance from mypy.nodes import TypeInfo -from mypy.types import AnyType, Instance, TupleType, Type, TypeOfAny, TypeVarId, has_type_vars +from mypy.types import AnyType, Instance, TupleType, TypeOfAny, has_type_vars def map_instance_to_supertype(instance: Instance, superclass: TypeInfo) -> Instance: @@ -25,8 +25,7 @@ def map_instance_to_supertype(instance: Instance, superclass: TypeInfo) -> Insta if not alias._is_recursive: # Unfortunately we can't support this for generic recursive tuples. # If we skip this special casing we will fall back to tuple[Any, ...]. - env = instance_to_type_environment(instance) - tuple_type = expand_type(instance.type.tuple_type, env) + tuple_type = expand_type_by_instance(instance.type.tuple_type, instance) if isinstance(tuple_type, TupleType): # Make the import here to avoid cyclic imports. import mypy.typeops @@ -91,8 +90,7 @@ def map_instance_to_direct_supertypes(instance: Instance, supertype: TypeInfo) - for b in typ.bases: if b.type == supertype: - env = instance_to_type_environment(instance) - t = expand_type(b, env) + t = expand_type_by_instance(b, instance) assert isinstance(t, Instance) result.append(t) @@ -103,15 +101,3 @@ def map_instance_to_direct_supertypes(instance: Instance, supertype: TypeInfo) - # type arguments implicitly. any_type = AnyType(TypeOfAny.unannotated) return [Instance(supertype, [any_type] * len(supertype.type_vars))] - - -def instance_to_type_environment(instance: Instance) -> dict[TypeVarId, Type]: - """Given an Instance, produce the resulting type environment for type - variables bound by the Instance's class definition. - - An Instance is a type application of a class (a TypeInfo) to its - required number of type arguments. So this environment consists - of the class's type variables mapped to the Instance's actual - arguments. The type variables are mapped by their `id`. - """ - return {binder.id: arg for binder, arg in zip(instance.type.defn.type_vars, instance.args)} diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 749b02391e06..3e11951376c9 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -18,7 +18,6 @@ from mypy.options import Options from mypy.scope import Scope from mypy.subtypes import is_same_type, is_subtype -from mypy.typeanal import fix_type_var_tuple_argument, set_any_tvars from mypy.types import ( AnyType, CallableType, @@ -88,36 +87,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: # types, since errors there have already been reported. return self.seen_aliases.add(t) - # Some recursive aliases may produce spurious args. In principle this is not very - # important, as we would simply ignore them when expanding, but it is better to keep - # correct aliases. Also, variadic aliases are better to check when fully analyzed, - # so we do this here. assert t.alias is not None, f"Unfixed type alias {t.type_ref}" - # TODO: consider moving this validation to typeanal.py, expanding invalid aliases - # during semantic analysis may cause crashes. - if t.alias.tvar_tuple_index is not None: - correct = len(t.args) >= len(t.alias.alias_tvars) - 1 - if any( - isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) - for a in t.args - ): - correct = True - else: - correct = len(t.args) == len(t.alias.alias_tvars) - if not correct: - if t.alias.tvar_tuple_index is not None: - exp_len = f"at least {len(t.alias.alias_tvars) - 1}" - else: - exp_len = f"{len(t.alias.alias_tvars)}" - self.fail( - "Bad number of arguments for type alias," - f" expected: {exp_len}, given: {len(t.args)}", - t, - code=codes.TYPE_ARG, - ) - t.args = set_any_tvars( - t.alias, t.line, t.column, self.options, from_error=True, fail=self.fail - ).args is_error = self.validate_args(t.alias.name, t.args, t.alias.alias_tvars, t) if not is_error: # If there was already an error for the alias itself, there is no point in checking @@ -144,34 +114,21 @@ def visit_callable_type(self, t: CallableType) -> None: t.arg_types[star_index] = p_type.args[0] def visit_instance(self, t: Instance) -> None: + super().visit_instance(t) # Type argument counts were checked in the main semantic analyzer pass. We assume # that the counts are correct here. info = t.type if isinstance(info, FakeInfo): return # https://github.com/python/mypy/issues/11079 - t.args = tuple(flatten_nested_tuples(t.args)) - if t.type.has_type_var_tuple_type: - # Regular Instances are already validated in typeanal.py. - # TODO: do something with partial overlap (probably just reject). - # also in other places where split_with_prefix_and_suffix() is used. - correct = len(t.args) >= len(t.type.type_vars) - 1 - if any( - isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) - for a in t.args - ): - correct = True - if not correct: - exp_len = f"at least {len(t.type.type_vars) - 1}" - self.fail( - f"Bad number of arguments, expected: {exp_len}, given: {len(t.args)}", - t, - code=codes.TYPE_ARG, - ) - any_type = AnyType(TypeOfAny.from_error) - t.args = (any_type,) * len(t.type.type_vars) - fix_type_var_tuple_argument(any_type, t) self.validate_args(info.name, t.args, info.defn.type_vars, t) - super().visit_instance(t) + if t.type.fullname == "builtins.tuple" and len(t.args) == 1: + # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] + arg = t.args[0] + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + t.args = unpacked.args def validate_args( self, name: str, args: Sequence[Type], type_vars: list[TypeVarLikeType], ctx: Context diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 12e7b207b00a..59457dfa5d3b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1464,7 +1464,7 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 7 + ALLOWED_GET_PROPER_TYPES = 8 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index ed1a8073887b..e297f2bf1631 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -82,6 +82,7 @@ UnionType, UnpackType, callable_with_ellipsis, + find_unpack_in_list, flatten_nested_tuples, flatten_nested_unions, get_proper_type, @@ -404,7 +405,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) t.args, allow_param_spec=True, allow_param_spec_literals=node.has_param_spec_type, - allow_unpack=node.tvar_tuple_index is not None, + allow_unpack=True, # Fixed length unpacks can be used for non-variadic aliases. ) if node.has_param_spec_type and len(node.alias_tvars) == 1: an_args = self.pack_paramspec_args(an_args) @@ -425,9 +426,8 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) # when it is top-level instance, so no need to recurse. if ( isinstance(res, Instance) # type: ignore[misc] - and len(res.args) != len(res.type.type_vars) and not self.defining_alias - and not res.type.has_type_var_tuple_type + and not validate_instance(res, self.fail) ): fix_instance( res, @@ -510,9 +510,6 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type: code=codes.VALID_TYPE, ) return AnyType(TypeOfAny.from_error) - - # TODO: this may not work well with aliases, if those worked. - # Those should be special-cased. elif isinstance(ps, ParamSpecType) and ps.prefix.arg_types: self.api.fail("Nested Concatenates are invalid", t, code=codes.VALID_TYPE) @@ -728,7 +725,7 @@ def analyze_type_with_type_info( args, allow_param_spec=True, allow_param_spec_literals=info.has_param_spec_type, - allow_unpack=info.has_type_var_tuple_type, + allow_unpack=True, # Fixed length tuples can be used for non-variadic types. ), ctx.line, ctx.column, @@ -736,19 +733,9 @@ def analyze_type_with_type_info( if len(info.type_vars) == 1 and info.has_param_spec_type: instance.args = tuple(self.pack_paramspec_args(instance.args)) - if info.has_type_var_tuple_type: - if instance.args: - # -1 to account for empty tuple - valid_arg_length = len(instance.args) >= len(info.type_vars) - 1 - # Empty case is special cased and we want to infer a Tuple[Any, ...] - # instead of the empty tuple, so no - 1 here. - else: - valid_arg_length = False - else: - valid_arg_length = len(instance.args) == len(info.type_vars) - # Check type argument count. - if not valid_arg_length and not self.defining_alias: + instance.args = tuple(flatten_nested_tuples(instance.args)) + if not self.defining_alias and not validate_instance(instance, self.fail): fix_instance( instance, self.fail, @@ -1342,9 +1329,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type: callable_args, ret_type, fallback ) if isinstance(maybe_ret, CallableType): - maybe_ret = maybe_ret.copy_modified( - ret_type=ret_type.accept(self), variables=variables - ) + maybe_ret = maybe_ret.copy_modified(variables=variables) if maybe_ret is None: # Callable[?, RET] (where ? is something invalid) self.fail( @@ -1736,6 +1721,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]: num_unpacks = 0 final_unpack = None for item in items: + # TODO: handle forward references here, they appear as Unpack[Any]. if isinstance(item, UnpackType) and not isinstance( get_proper_type(item.type), TupleType ): @@ -1856,25 +1842,13 @@ def fix_instance( any_type = get_omitted_any(disallow_any, fail, note, t, options, fullname, unexpanded_type) t.args = (any_type,) * len(t.type.type_vars) fix_type_var_tuple_argument(any_type, t) - return - - if t.type.has_type_var_tuple_type: - # This can be only correctly analyzed when all arguments are fully - # analyzed, because there may be a variadic item among them, so we - # do this in semanal_typeargs.py. - return - - # Invalid number of type parameters. - fail( - wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name), - t, - code=codes.TYPE_ARG, - ) # Construct the correct number of type arguments, as # otherwise the type checker may crash as it expects # things to be right. - t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars) + any_type = AnyType(TypeOfAny.from_error) + t.args = tuple(any_type for _ in t.type.type_vars) + fix_type_var_tuple_argument(any_type, t) t.invalid = True @@ -1903,6 +1877,15 @@ def instantiate_type_alias( ctx: context where expansion happens unexpanded_type, disallow_any, use_standard_error: used to customize error messages """ + # Type aliases are special, since they can be expanded during semantic analysis, + # so we need to normalize them as soon as possible. + # TODO: can this cause an infinite recursion? + args = flatten_nested_tuples(args) + if any(unknown_unpack(a) for a in args): + # This type is not ready to be validated, because of unknown total count. + # Note that we keep the kind of Any for consistency. + return set_any_tvars(node, ctx.line, ctx.column, options, special_form=True) + exp_len = len(node.alias_tvars) act_len = len(args) if ( @@ -1937,22 +1920,54 @@ def instantiate_type_alias( tp.line = ctx.line tp.column = ctx.column return tp - if act_len != exp_len and node.tvar_tuple_index is None: + if node.tvar_tuple_index is None: + if any(isinstance(a, UnpackType) for a in args): + # A variadic unpack in fixed size alias (fixed unpacks must be flattened by the caller) + fail(message_registry.INVALID_UNPACK_POSITION, ctx, code=codes.VALID_TYPE) + return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True) + correct = act_len == exp_len + else: + correct = act_len >= exp_len - 1 + for a in args: + if isinstance(a, UnpackType): + unpacked = get_proper_type(a.type) + if isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple": + # Variadic tuple is always correct. + correct = True + if not correct: if use_standard_error: # This is used if type alias is an internal representation of another type, # for example a generic TypedDict or NamedTuple. msg = wrong_type_arg_count(exp_len, str(act_len), node.name) else: - msg = f"Bad number of arguments for type alias, expected: {exp_len}, given: {act_len}" + if node.tvar_tuple_index is not None: + exp_len_str = f"at least {exp_len - 1}" + else: + exp_len_str = str(exp_len) + msg = ( + "Bad number of arguments for type alias," + f" expected: {exp_len_str}, given: {act_len}" + ) fail(msg, ctx, code=codes.TYPE_ARG) return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True) + elif node.tvar_tuple_index is not None: + # We also need to check if we are not performing a type variable tuple split. + unpack = find_unpack_in_list(args) + if unpack is not None: + unpack_arg = args[unpack] + assert isinstance(unpack_arg, UnpackType) + if isinstance(unpack_arg.type, TypeVarTupleType): + exp_prefix = node.tvar_tuple_index + act_prefix = unpack + exp_suffix = len(node.alias_tvars) - node.tvar_tuple_index - 1 + act_suffix = len(args) - unpack - 1 + if act_prefix < exp_prefix or act_suffix < exp_suffix: + fail("TypeVarTuple cannot be split", ctx, code=codes.TYPE_ARG) + 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. - # Type aliases are special, since they can be expanded during semantic analysis, - # so we need to normalize them as soon as possible. - # TODO: can this cause an infinite recursion? - typ = TypeAliasType(node, flatten_nested_tuples(args), ctx.line, ctx.column) + typ = TypeAliasType(node, args, ctx.line, ctx.column) assert typ.alias is not None # HACK: Implement FlexibleAlias[T, typ] by expanding it to typ here. if ( @@ -1973,11 +1988,14 @@ def set_any_tvars( *, from_error: bool = False, disallow_any: bool = False, + special_form: bool = False, fail: MsgCallback | None = None, unexpanded_type: Type | None = None, ) -> TypeAliasType: if from_error or disallow_any: type_of_any = TypeOfAny.from_error + elif special_form: + type_of_any = TypeOfAny.special_form else: type_of_any = TypeOfAny.from_omitted_generics if disallow_any and node.alias_tvars: @@ -2227,6 +2245,63 @@ def make_optional_type(t: Type) -> Type: return UnionType([t, NoneType()], t.line, t.column) +def validate_instance(t: Instance, fail: MsgCallback) -> bool: + """Check if this is a well-formed instance with respect to argument count/positions.""" + # TODO: combine logic with instantiate_type_alias(). + if any(unknown_unpack(a) for a in t.args): + # This type is not ready to be validated, because of unknown total count. + # TODO: is it OK to fill with TypeOfAny.from_error instead of special form? + return False + if t.type.has_type_var_tuple_type: + correct = len(t.args) >= len(t.type.type_vars) - 1 + if any( + isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) + for a in t.args + ): + correct = True + if not correct: + exp_len = f"at least {len(t.type.type_vars) - 1}" + fail( + f"Bad number of arguments, expected: {exp_len}, given: {len(t.args)}", + t, + code=codes.TYPE_ARG, + ) + return False + elif not t.args: + # The Any arguments should be set by the caller. + return False + else: + # We also need to check if we are not performing a type variable tuple split. + unpack = find_unpack_in_list(t.args) + if unpack is not None: + unpack_arg = t.args[unpack] + assert isinstance(unpack_arg, UnpackType) + if isinstance(unpack_arg.type, TypeVarTupleType): + assert t.type.type_var_tuple_prefix is not None + assert t.type.type_var_tuple_suffix is not None + exp_prefix = t.type.type_var_tuple_prefix + act_prefix = unpack + exp_suffix = t.type.type_var_tuple_suffix + act_suffix = len(t.args) - unpack - 1 + if act_prefix < exp_prefix or act_suffix < exp_suffix: + fail("TypeVarTuple cannot be split", t, code=codes.TYPE_ARG) + return False + elif any(isinstance(a, UnpackType) for a in t.args): + # A variadic unpack in fixed size instance (fixed unpacks must be flattened by the caller) + fail(message_registry.INVALID_UNPACK_POSITION, t, code=codes.VALID_TYPE) + return False + elif len(t.args) != len(t.type.type_vars): + # Invalid number of type parameters. + if t.args: + fail( + wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name), + t, + code=codes.TYPE_ARG, + ) + return False + return True + + 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. @@ -2244,7 +2319,7 @@ def __init__(self, fail: MsgCallback, note: MsgCallback, options: Options) -> No def visit_instance(self, typ: Instance) -> None: super().visit_instance(typ) - if len(typ.args) != len(typ.type.type_vars) and not typ.type.has_type_var_tuple_type: + if not validate_instance(typ, self.fail): fix_instance( typ, self.fail, @@ -2269,3 +2344,17 @@ def visit_unbound_type(self, t: UnboundType) -> bool: if sym and sym.fullname in SELF_TYPE_NAMES: return True return super().visit_unbound_type(t) + + +def unknown_unpack(t: Type) -> bool: + """Check if a given type is an unpack of an unknown type. + + Unfortunately, there is no robust way to distinguish forward references from + genuine undefined names here. But this worked well so far, although it looks + quite fragile. + """ + if isinstance(t, UnpackType): + unpacked = get_proper_type(t.type) + if isinstance(unpacked, AnyType) and unpacked.type_of_any == TypeOfAny.special_form: + return True + return False diff --git a/mypy/typeops.py b/mypy/typeops.py index f9c1914cc9a8..3efa3cc3e965 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -110,10 +110,8 @@ def tuple_fallback(typ: TupleType) -> Instance: and unpacked_type.type.fullname == "builtins.tuple" ): items.append(unpacked_type.args[0]) - elif isinstance(unpacked_type, (AnyType, UninhabitedType)): - continue else: - raise NotImplementedError(unpacked_type) + raise NotImplementedError else: items.append(item) # TODO: we should really use a union here, tuple types are special. diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index f7faab4818c9..2b47ff30cdfb 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -619,8 +619,7 @@ T = TypeVar("T") Ts = TypeVarTuple("Ts") A = List[Tuple[T, Unpack[Ts], T]] -B = A[Unpack[Ts]] -x: B[int, str, str] +x: A[int, str, str] reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, builtins.str, builtins.int]]" [builtins fixtures/tuple.pyi] @@ -1052,8 +1051,7 @@ reveal_type(y.fn) # N: Revealed type is "def (builtins.int, builtins.str)" z: A[Unpack[Tuple[int, ...]]] reveal_type(z) # N: Revealed type is "__main__.A[Unpack[builtins.tuple[builtins.int, ...]]]" -# TODO: this requires fixing map_instance_to_supertype(). -# reveal_type(z[0]) +reveal_type(z[0]) # N: Revealed type is "builtins.int" reveal_type(z.fn) # N: Revealed type is "def (*builtins.int)" t: A[int, Unpack[Tuple[int, str]], str] @@ -1118,3 +1116,120 @@ reveal_type(td) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (buil def bad() -> int: ... td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]") [builtins fixtures/tuple.pyi] + +[case testFixedUnpackWithRegularInstance] +from typing import Tuple, Generic, TypeVar +from typing_extensions import Unpack + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +T3 = TypeVar("T3") +T4 = TypeVar("T4") + +class C(Generic[T1, T2, T3, T4]): ... +x: C[int, Unpack[Alias], str] +Alias = Tuple[int, str] +reveal_type(x) # N: Revealed type is "__main__.C[builtins.int, builtins.int, builtins.str, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testVariadicUnpackWithRegularInstance] +from typing import Tuple, Generic, TypeVar +from typing_extensions import Unpack + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +T3 = TypeVar("T3") +T4 = TypeVar("T4") + +class C(Generic[T1, T2, T3, T4]): ... +x: C[int, Unpack[Alias], str, str] # E: Unpack is only valid in a variadic position +Alias = Tuple[int, ...] +reveal_type(x) # N: Revealed type is "__main__.C[Any, Any, Any, Any]" +y: C[int, Unpack[Undefined]] # E: Name "Undefined" is not defined +reveal_type(y) # N: Revealed type is "__main__.C[Any, Any, Any, Any]" +[builtins fixtures/tuple.pyi] + +[case testVariadicAliasInvalidUnpackNoCrash] +from typing import Tuple, Generic, Union, List +from typing_extensions import Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +Alias = Tuple[int, Unpack[Ts], str] + +A = Union[int, str] +x: List[Alias[int, Unpack[A], str]] # E: "Union[int, str]" cannot be unpacked (must be tuple or TypeVarTuple) +reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str, builtins.str]]" +y: List[Alias[int, Unpack[Undefined], str]] # E: Name "Undefined" is not defined +reveal_type(y) # N: Revealed type is "builtins.list[Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str]]" +[builtins fixtures/tuple.pyi] + +[case testVariadicAliasForwardRefToFixedUnpack] +from typing import Tuple, Generic, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +Alias = Tuple[T, Unpack[Ts], S] +x: Alias[int, Unpack[Other]] +Other = Tuple[int, str] +reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testVariadicAliasForwardRefToVariadicUnpack] +from typing import Tuple, Generic, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +Alias = Tuple[T, Unpack[Ts], S] +x: Alias[int, Unpack[Other]] +Other = Tuple[int, ...] +reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testVariadicInstanceStrictPrefixSuffixCheck] +from typing import Tuple, Generic, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +class C(Generic[T, Unpack[Ts], S]): ... + +def foo(x: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + y: C[int, Unpack[Ts]] # E: TypeVarTuple cannot be split + z: C[Unpack[Ts], int] # E: TypeVarTuple cannot be split + return x +[builtins fixtures/tuple.pyi] + +[case testVariadicAliasStrictPrefixSuffixCheck] +from typing import Tuple, TypeVar +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +Alias = Tuple[T, Unpack[Ts], S] + +def foo(x: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + y: Alias[int, Unpack[Ts]] # E: TypeVarTuple cannot be split + z: Alias[Unpack[Ts], int] # E: TypeVarTuple cannot be split + return x +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleWithIsInstance] +# flags: --warn-unreachable +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +TP = TypeVarTuple("TP") +class A(Tuple[Unpack[TP]]): ... + +def test(d: A[int, str]) -> None: + if isinstance(d, A): + reveal_type(d) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" + else: + reveal_type(d) # E: Statement is unreachable +[builtins fixtures/isinstancelist.pyi] From 49419835045b09c98b545171abb10384b6ecf6a9 Mon Sep 17 00:00:00 2001 From: Matt Bogosian Date: Fri, 8 Sep 2023 01:46:14 -0500 Subject: [PATCH 0256/1617] Differentiate between venv and tox setups in CONTRIBUTING.md (#16067) --- CONTRIBUTING.md | 42 ++++++++++++++++++++++++++++++------------ tox.ini | 1 + 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82e55f437e87..46292c301406 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,18 +62,6 @@ like this: python3 runtests.py ``` -You can also use `tox` to run tests (`tox` handles setting up the test environment for you): - -```bash -tox run -e py - -# Or some specific python version: -tox run -e py39 - -# Or some specific command: -tox run -e lint -``` - Some useful commands for running specific tests include: ```bash @@ -95,6 +83,36 @@ python runtests.py lint For an in-depth guide on running and writing tests, see [the README in the test-data directory](test-data/unit/README.md). +#### Using `tox` + +You can also use [`tox`](https://tox.wiki/en/latest/) to run tests and other commands. +`tox` handles setting up test environments for you. + +```bash +# Run tests +tox run -e py + +# Run tests using some specific Python version +tox run -e py311 + +# Run a specific command +tox run -e lint + +# Run a single test from the test suite +tox run -e py -- -n0 -k 'test_name' + +# Run all test cases in the "test-data/unit/check-dataclasses.test" file using +# Python 3.11 specifically +tox run -e py311 -- mypy/test/testcheck.py::TypeCheckSuite::check-dataclasses.test + +# Set up a development environment with all the project libraries and run a command +tox -e dev -- mypy --verbose test_case.py +tox -e dev --override testenv:dev.allowlist_externals+=env -- env # inspect the environment +``` + +If you don't already have `tox` installed, you can use a virtual environment as +described above to install `tox` via `pip` (e.g., ``python3 -m pip install tox``). + ## First time contributors If you're looking for things to help with, browse our [issue tracker](https://github.com/python/mypy/issues)! diff --git a/tox.ini b/tox.ini index e07acdc5200d..31aed1a1ef48 100644 --- a/tox.ini +++ b/tox.ini @@ -30,6 +30,7 @@ deps = commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' + {posargs} [testenv:docs] description = invoke sphinx-build to build the HTML docs From f9dc5610423d368bcf804b6a88a2d8502e62df1c Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 10 Sep 2023 01:55:57 -0400 Subject: [PATCH 0257/1617] Fix __post_init__() internal error (#16080) Fixes #16057. --- mypy/checker.py | 5 ++++- mypy/nodes.py | 1 - mypy/plugins/dataclasses.py | 4 ++-- test-data/unit/check-dataclasses.test | 4 ++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index fa7c645873d0..5a74f019dcf4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1076,6 +1076,8 @@ def check_func_item( if name == "__exit__": self.check__exit__return_type(defn) + # TODO: the following logic should move to the dataclasses plugin + # https://github.com/python/mypy/issues/15515 if name == "__post_init__": if dataclasses_plugin.is_processed_dataclass(defn.info): dataclasses_plugin.check_post_init(self, defn, defn.info) @@ -2882,7 +2884,8 @@ def check_assignment( typ = self.expr_checker.accept(rvalue) self.check_match_args(inferred, typ, lvalue) if name == "__post_init__": - if dataclasses_plugin.is_processed_dataclass(self.scope.active_class()): + active_class = self.scope.active_class() + if active_class and dataclasses_plugin.is_processed_dataclass(active_class): self.fail(message_registry.DATACLASS_POST_INIT_MUST_BE_A_FUNCTION, rvalue) # Defer PartialType's super type checking. diff --git a/mypy/nodes.py b/mypy/nodes.py index d29e99ccace7..6556cd910b46 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -515,7 +515,6 @@ def __init__(self) -> None: # Original, not semantically analyzed type (used for reprocessing) self.unanalyzed_type: mypy.types.ProperType | None = None # If method, reference to TypeInfo - # TODO: Type should be Optional[TypeInfo] self.info = FUNC_NO_INFO self.is_property = False self.is_class = False diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 8b34c28b6832..99f079705c3f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -1070,8 +1070,8 @@ def replace_function_sig_callback(ctx: FunctionSigContext) -> CallableType: ) -def is_processed_dataclass(info: TypeInfo | None) -> bool: - return info is not None and "dataclass" in info.metadata +def is_processed_dataclass(info: TypeInfo) -> bool: + return bool(info) and "dataclass" in info.metadata def check_post_init(api: TypeChecker, defn: FuncItem, info: TypeInfo) -> None: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 8a50e7124d05..35df84658259 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2280,6 +2280,10 @@ reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" [builtins fixtures/tuple.pyi] +[case testPostInitNotMethod] +def __post_init__() -> None: + pass + [case testPostInitCorrectSignature] from typing import Any, Generic, TypeVar, Callable, Self from dataclasses import dataclass, InitVar From ed18fea5b17ef3a969b37b4906dd7c237ddb1825 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 9 Sep 2023 23:35:07 -0700 Subject: [PATCH 0258/1617] Document and rename overload-overlap error code (#16074) A new error code was introduced in https://github.com/python/mypy/pull/16061 As per https://github.com/python/mypy/pull/16068, we didn't previously run doc builds on changes to errorcodes.py, causing tests to fail on master when this was merged. Renaming the code as per: https://github.com/python/mypy/pull/16061#issuecomment-1710613890 All type ignores should be unsafe, so we should save the unsafe adjective for things that are really unsafe. As it stands, there are many cases where overloads overlap somewhat benignly. Fixes #8656 --- docs/source/error_code_list.rst | 35 ++++++++++++++++++++++++++++ docs/source/more_types.rst | 5 +++- mypy/errorcodes.py | 4 ++-- mypy/messages.py | 2 +- mypy/types.py | 2 +- test-data/unit/check-errorcodes.test | 2 +- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index a865a4dd1532..4decd37e6e8a 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1114,6 +1114,41 @@ Warn about cases where a bytes object may be converted to a string in an unexpec print(f"The alphabet starts with {b!r}") # The alphabet starts with b'abc' print(f"The alphabet starts with {b.decode('utf-8')}") # The alphabet starts with abc +.. _code-overload-overlap: + +Check that overloaded functions don't overlap [overload-overlap] +---------------------------------------------------------------- + +Warn if multiple ``@overload`` variants overlap in potentially unsafe ways. +This guards against the following situation: + +.. code-block:: python + + from typing import overload + + class A: ... + class B(A): ... + + @overload + def foo(x: B) -> int: ... # Error: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] + @overload + def foo(x: A) -> str: ... + def foo(x): ... + + def takes_a(a: A) -> str: + return foo(a) + + a: A = B() + value = takes_a(a) + # mypy will think that value is a str, but it could actually be an int + reveal_type(value) # Revealed type is "builtins.str" + + +Note that in cases where you ignore this error, mypy will usually still infer the +types you expect. + +See :ref:`overloading ` for more explanation. + .. _code-annotation-unchecked: Notify about an annotation in an unchecked function [annotation-unchecked] diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 4e6e9204fdca..b27764a9e87c 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -501,7 +501,7 @@ To prevent these kinds of issues, mypy will detect and prohibit inherently unsaf overlapping overloads on a best-effort basis. Two variants are considered unsafely overlapping when both of the following are true: -1. All of the arguments of the first variant are compatible with the second. +1. All of the arguments of the first variant are potentially compatible with the second. 2. The return type of the first variant is *not* compatible with (e.g. is not a subtype of) the second. @@ -510,6 +510,9 @@ the ``object`` argument in the second, yet the ``int`` return type is not a subt ``str``. Both conditions are true, so mypy will correctly flag ``unsafe_func`` as being unsafe. +Note that in cases where you ignore the overlapping overload error, mypy will usually +still infer the types you expect at callsites. + However, mypy will not detect *all* unsafe uses of overloads. For example, suppose we modify the above snippet so it calls ``summarize`` instead of ``unsafe_func``: diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 70b8cffe9053..cd9978c2f31c 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -262,8 +262,8 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final = ErrorCode("misc", "Miscellaneous other checks", "General") -UNSAFE_OVERLOAD: Final[ErrorCode] = ErrorCode( - "unsafe-overload", +OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( + "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", "General", sub_code_of=MISC, diff --git a/mypy/messages.py b/mypy/messages.py index a58c5f91c4b1..b6fdaf06a8e0 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1604,7 +1604,7 @@ def overloaded_signatures_overlap(self, index1: int, index2: int, context: Conte "Overloaded function signatures {} and {} overlap with " "incompatible return types".format(index1, index2), context, - code=codes.UNSAFE_OVERLOAD, + code=codes.OVERLOAD_OVERLAP, ) def overloaded_signature_will_never_match( diff --git a/mypy/types.py b/mypy/types.py index cee4595b67cc..04d90c9dc124 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3019,7 +3019,7 @@ def get_proper_type(typ: Type | None) -> ProperType | None: @overload -def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[unsafe-overload] +def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[overload-overlap] ... diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 72edf2f22c05..ac7c8b4c9f9d 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1077,7 +1077,7 @@ x = 1 # type: ignore # E: Unused "type: ignore" comment [unused-ignore] from typing import overload, Union @overload -def unsafe_func(x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types [unsafe-overload] +def unsafe_func(x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] @overload def unsafe_func(x: object) -> str: ... def unsafe_func(x: object) -> Union[int, str]: From 9a35360739ced871feb6331a14a7bbacce00c7dc Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 10 Sep 2023 21:11:49 +0300 Subject: [PATCH 0259/1617] Add `add_overloaded_method_to_class` helper to `plugins/common.py` (#16038) There are several changes: 1. `add_overloaded_method_to_class` itself. It is very useful for plugin authors, because right now it is quite easy to add a regular method, but it is very hard to add a method with `@overload`s. I don't think that user must face all the chalenges that I've covered in this method. Moreover, it is quite easy even for experienced developers to forget some flags / props / etc (I am pretty sure that I might forgot something in the implementation) 2. `add_overloaded_method_to_class` and `add_method_to_class` now return added nodes, it is also helpful if you want to do something with this node in your plugin after it is created 3. I've refactored how `add_method_to_class` works and reused its parts in the new method as well 4. `tvar_def` in `add_method_to_class` can now accept a list of type vars, not just one Notice that `add_method_to_class` is unchanged from the user's POV, it should continue to work as before. Tests are also updated to check that our overloads are correct. Things to do later (in the next PRs / releases): 1. We can possibly add `is_final` param to methods as well 2. We can also support `@property` in a separate method at some point --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/plugins/common.py | 136 +++++++++++++++--- test-data/unit/check-custom-plugin.test | 24 +++- test-data/unit/check-incremental.test | 38 +++++ test-data/unit/deps.test | 6 +- .../unit/plugins/add_overloaded_method.py | 41 ++++++ 5 files changed, 222 insertions(+), 23 deletions(-) create mode 100644 test-data/unit/plugins/add_overloaded_method.py diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 55f2870cadb4..84d50b7086c6 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import NamedTuple + from mypy.argmap import map_actuals_to_formals from mypy.fixup import TypeFixer from mypy.nodes import ( @@ -16,9 +18,11 @@ JsonDict, NameExpr, Node, + OverloadedFuncDef, PassStmt, RefExpr, SymbolTableNode, + TypeInfo, Var, ) from mypy.plugin import CheckerPluginInterface, ClassDefContext, SemanticAnalyzerPluginInterface @@ -209,24 +213,99 @@ def add_method( ) +class MethodSpec(NamedTuple): + """Represents a method signature to be added, except for `name`.""" + + args: list[Argument] + return_type: Type + self_type: Type | None = None + tvar_defs: list[TypeVarType] | None = None + + def add_method_to_class( api: SemanticAnalyzerPluginInterface | CheckerPluginInterface, cls: ClassDef, name: str, + # MethodSpec items kept for backward compatibility: args: list[Argument], return_type: Type, self_type: Type | None = None, - tvar_def: TypeVarType | None = None, + tvar_def: list[TypeVarType] | TypeVarType | None = None, is_classmethod: bool = False, is_staticmethod: bool = False, -) -> None: +) -> FuncDef | Decorator: """Adds a new method to a class definition.""" + _prepare_class_namespace(cls, name) - assert not ( - is_classmethod is True and is_staticmethod is True - ), "Can't add a new method that's both staticmethod and classmethod." + if tvar_def is not None and not isinstance(tvar_def, list): + tvar_def = [tvar_def] + + func, sym = _add_method_by_spec( + api, + cls.info, + name, + MethodSpec(args=args, return_type=return_type, self_type=self_type, tvar_defs=tvar_def), + is_classmethod=is_classmethod, + is_staticmethod=is_staticmethod, + ) + cls.info.names[name] = sym + cls.info.defn.defs.body.append(func) + return func + +def add_overloaded_method_to_class( + api: SemanticAnalyzerPluginInterface | CheckerPluginInterface, + cls: ClassDef, + name: str, + items: list[MethodSpec], + is_classmethod: bool = False, + is_staticmethod: bool = False, +) -> OverloadedFuncDef: + """Adds a new overloaded method to a class definition.""" + assert len(items) >= 2, "Overloads must contain at least two cases" + + # Save old definition, if it exists. + _prepare_class_namespace(cls, name) + + # Create function bodies for each passed method spec. + funcs: list[Decorator | FuncDef] = [] + for item in items: + func, _sym = _add_method_by_spec( + api, + cls.info, + name=name, + spec=item, + is_classmethod=is_classmethod, + is_staticmethod=is_staticmethod, + ) + if isinstance(func, FuncDef): + var = Var(func.name, func.type) + var.set_line(func.line) + func.is_decorated = True + func.deco_line = func.line + + deco = Decorator(func, [], var) + else: + deco = func + deco.is_overload = True + funcs.append(deco) + + # Create the final OverloadedFuncDef node: + overload_def = OverloadedFuncDef(funcs) + overload_def.info = cls.info + overload_def.is_class = is_classmethod + overload_def.is_static = is_staticmethod + sym = SymbolTableNode(MDEF, overload_def) + sym.plugin_generated = True + + cls.info.names[name] = sym + cls.info.defn.defs.body.append(overload_def) + return overload_def + + +def _prepare_class_namespace(cls: ClassDef, name: str) -> None: info = cls.info + assert info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. @@ -235,6 +314,29 @@ def add_method_to_class( if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) + # NOTE: we would like the plugin generated node to dominate, but we still + # need to keep any existing definitions so they get semantically analyzed. + if name in info.names: + # Get a nice unique name instead. + r_name = get_unique_redefinition_name(name, info.names) + info.names[r_name] = info.names[name] + + +def _add_method_by_spec( + api: SemanticAnalyzerPluginInterface | CheckerPluginInterface, + info: TypeInfo, + name: str, + spec: MethodSpec, + *, + is_classmethod: bool, + is_staticmethod: bool, +) -> tuple[FuncDef | Decorator, SymbolTableNode]: + args, return_type, self_type, tvar_defs = spec + + assert not ( + is_classmethod is True and is_staticmethod is True + ), "Can't add a new method that's both staticmethod and classmethod." + if isinstance(api, SemanticAnalyzerPluginInterface): function_type = api.named_type("builtins.function") else: @@ -258,8 +360,8 @@ def add_method_to_class( arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) - if tvar_def: - signature.variables = [tvar_def] + if tvar_defs: + signature.variables = tvar_defs func = FuncDef(name, args, Block([PassStmt()])) func.info = info @@ -269,13 +371,6 @@ def add_method_to_class( func._fullname = info.fullname + "." + name func.line = info.line - # NOTE: we would like the plugin generated node to dominate, but we still - # need to keep any existing definitions so they get semantically analyzed. - if name in info.names: - # Get a nice unique name instead. - r_name = get_unique_redefinition_name(name, info.names) - info.names[r_name] = info.names[name] - # Add decorator for is_staticmethod. It's unnecessary for is_classmethod. if is_staticmethod: func.is_decorated = True @@ -286,12 +381,12 @@ def add_method_to_class( dec = Decorator(func, [], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) - else: - sym = SymbolTableNode(MDEF, func) - sym.plugin_generated = True - info.names[name] = sym + sym.plugin_generated = True + return dec, sym - info.defn.defs.body.append(func) + sym = SymbolTableNode(MDEF, func) + sym.plugin_generated = True + return func, sym def add_attribute_to_class( @@ -304,7 +399,7 @@ def add_attribute_to_class( override_allow_incompatible: bool = False, fullname: str | None = None, is_classvar: bool = False, -) -> None: +) -> Var: """ Adds a new attribute to a class definition. This currently only generates the symbol table entry and no corresponding AssignmentStatement @@ -335,6 +430,7 @@ def add_attribute_to_class( info.names[name] = SymbolTableNode( MDEF, node, plugin_generated=True, no_serialize=no_serialize ) + return node def deserialize_and_fixup_type(data: str | JsonDict, api: SemanticAnalyzerPluginInterface) -> Type: diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 9a0668f98c21..22374d09cf9f 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1011,13 +1011,35 @@ class BaseAddMethod: pass class MyClass(BaseAddMethod): pass -my_class = MyClass() reveal_type(MyClass.foo_classmethod) # N: Revealed type is "def ()" reveal_type(MyClass.foo_staticmethod) # N: Revealed type is "def (builtins.int) -> builtins.str" + +my_class = MyClass() +reveal_type(my_class.foo_classmethod) # N: Revealed type is "def ()" +reveal_type(my_class.foo_staticmethod) # N: Revealed type is "def (builtins.int) -> builtins.str" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/add_classmethod.py +[case testAddOverloadedMethodPlugin] +# flags: --config-file tmp/mypy.ini +class AddOverloadedMethod: pass + +class MyClass(AddOverloadedMethod): + pass + +reveal_type(MyClass.method) # N: Revealed type is "Overload(def (self: __main__.MyClass, arg: builtins.int) -> builtins.str, def (self: __main__.MyClass, arg: builtins.str) -> builtins.int)" +reveal_type(MyClass.clsmethod) # N: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +reveal_type(MyClass.stmethod) # N: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" + +my_class = MyClass() +reveal_type(my_class.method) # N: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +reveal_type(my_class.clsmethod) # N: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +reveal_type(my_class.stmethod) # N: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/add_overloaded_method.py + [case testCustomErrorCodePlugin] # flags: --config-file tmp/mypy.ini --show-error-codes def main() -> int: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index fcab0545b982..b4cd21aa552c 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5935,6 +5935,44 @@ tmp/b.py:4: note: Revealed type is "def ()" tmp/b.py:5: note: Revealed type is "def (builtins.int) -> builtins.str" tmp/b.py:6: note: Revealed type is "def ()" tmp/b.py:7: note: Revealed type is "def (builtins.int) -> builtins.str" + +[case testIncrementalAddOverloadedMethodPlugin] +# flags: --config-file tmp/mypy.ini +import b + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/add_overloaded_method.py + +[file a.py] +class AddOverloadedMethod: pass + +class MyClass(AddOverloadedMethod): + pass + +[file b.py] +import a + +[file b.py.2] +import a + +reveal_type(a.MyClass.method) +reveal_type(a.MyClass.clsmethod) +reveal_type(a.MyClass.stmethod) + +my_class = a.MyClass() +reveal_type(my_class.method) +reveal_type(my_class.clsmethod) +reveal_type(my_class.stmethod) +[rechecked b] +[out2] +tmp/b.py:3: note: Revealed type is "Overload(def (self: a.MyClass, arg: builtins.int) -> builtins.str, def (self: a.MyClass, arg: builtins.str) -> builtins.int)" +tmp/b.py:4: note: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +tmp/b.py:5: note: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +tmp/b.py:8: note: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +tmp/b.py:9: note: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" +tmp/b.py:10: note: Revealed type is "Overload(def (arg: builtins.int) -> builtins.str, def (arg: builtins.str) -> builtins.int)" + [case testGenericNamedTupleSerialization] import b [file a.py] diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index c3295b79e4ed..5e77ff1d85e0 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1387,12 +1387,13 @@ class B(A): -> , m -> -> , m.B.__init__ - -> , m.B.__mypy-replace + -> , m, m.B.__mypy-replace -> -> -> -> m, m.A, m.B -> m + -> m -> m -> m.B -> m @@ -1419,12 +1420,13 @@ class B(A): -> -> , m.B.__init__ -> - -> , m.B.__mypy-replace + -> , m, m.B.__mypy-replace -> -> -> -> m, m.A, m.B -> m + -> m -> m -> m.B -> m diff --git a/test-data/unit/plugins/add_overloaded_method.py b/test-data/unit/plugins/add_overloaded_method.py new file mode 100644 index 000000000000..efda848f790c --- /dev/null +++ b/test-data/unit/plugins/add_overloaded_method.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import ARG_POS, Argument, Var +from mypy.plugin import ClassDefContext, Plugin +from mypy.plugins.common import MethodSpec, add_overloaded_method_to_class + + +class OverloadedMethodPlugin(Plugin): + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: + if "AddOverloadedMethod" in fullname: + return add_overloaded_method_hook + return None + + +def add_overloaded_method_hook(ctx: ClassDefContext) -> None: + add_overloaded_method_to_class(ctx.api, ctx.cls, "method", _generate_method_specs(ctx)) + add_overloaded_method_to_class( + ctx.api, ctx.cls, "clsmethod", _generate_method_specs(ctx), is_classmethod=True + ) + add_overloaded_method_to_class( + ctx.api, ctx.cls, "stmethod", _generate_method_specs(ctx), is_staticmethod=True + ) + + +def _generate_method_specs(ctx: ClassDefContext) -> list[MethodSpec]: + return [ + MethodSpec( + args=[Argument(Var("arg"), ctx.api.named_type("builtins.int"), None, ARG_POS)], + return_type=ctx.api.named_type("builtins.str"), + ), + MethodSpec( + args=[Argument(Var("arg"), ctx.api.named_type("builtins.str"), None, ARG_POS)], + return_type=ctx.api.named_type("builtins.int"), + ), + ] + + +def plugin(version: str) -> type[OverloadedMethodPlugin]: + return OverloadedMethodPlugin From 9e520c38777267495642845f070be4383f50342d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 11 Sep 2023 20:02:31 +0100 Subject: [PATCH 0260/1617] Allow TypedDict unpacking in Callable types (#16083) Fixes https://github.com/python/mypy/issues/16082 Currently we only allow `Unpack` of a TypedDict when it appears in a function definition. This PR also allows this in `Callable` types, similarly to how we do this for variadic types. Note this still doesn't allow having both variadic unpack and a TypedDict unpack in the same `Callable`. Supporting this is tricky, so let's not so this until people will actually ask for this. FWIW we can always suggest callback protocols for such tricky cases. --- mypy/exprtotype.py | 4 +++- mypy/fastparse.py | 2 +- mypy/semanal_typeargs.py | 4 +++- mypy/typeanal.py | 13 ++++++++++++- mypy/types.py | 7 +++++-- test-data/unit/check-varargs.test | 15 +++++++++++++++ 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index b82d35607ef1..5f0ef79acbd7 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -196,6 +196,8 @@ def expr_to_unanalyzed_type( elif isinstance(expr, EllipsisExpr): return EllipsisType(expr.line) elif allow_unpack and isinstance(expr, StarExpr): - return UnpackType(expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax)) + return UnpackType( + expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax), from_star_syntax=True + ) else: raise TypeTranslationError() diff --git a/mypy/fastparse.py b/mypy/fastparse.py index a96e697d40bf..fe158d468ce8 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2041,7 +2041,7 @@ def visit_Attribute(self, n: Attribute) -> Type: # Used for Callable[[X *Ys, Z], R] def visit_Starred(self, n: ast3.Starred) -> Type: - return UnpackType(self.visit(n.value)) + return UnpackType(self.visit(n.value), from_star_syntax=True) # List(expr* elts, expr_context ctx) def visit_List(self, n: ast3.List) -> Type: diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 3e11951376c9..ed04b30e90ba 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -214,7 +214,9 @@ def visit_unpack_type(self, typ: UnpackType) -> None: # Avoid extra errors if there were some errors already. Also interpret plain Any # as tuple[Any, ...] (this is better for the code in type checker). self.fail( - message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), typ + message_registry.INVALID_UNPACK.format(format_type(proper_type, self.options)), + typ.type, + code=codes.VALID_TYPE, ) typ.type = self.named_type("builtins.tuple", [AnyType(TypeOfAny.from_error)]) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index e297f2bf1631..385c5d35d67f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -961,7 +961,7 @@ def visit_unpack_type(self, t: UnpackType) -> Type: if not self.allow_unpack: self.fail(message_registry.INVALID_UNPACK_POSITION, t.type, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) - return UnpackType(self.anal_type(t.type)) + return UnpackType(self.anal_type(t.type), from_star_syntax=t.from_star_syntax) def visit_parameters(self, t: Parameters) -> Type: raise NotImplementedError("ParamSpec literals cannot have unbound TypeVars") @@ -969,6 +969,7 @@ def visit_parameters(self, t: Parameters) -> Type: def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope with self.tvar_scope_frame(): + unpacked_kwargs = False if self.defining_alias: variables = t.variables else: @@ -996,6 +997,15 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: ) validated_args.append(AnyType(TypeOfAny.from_error)) else: + if nested and isinstance(at, UnpackType) and i == star_index: + # TODO: it would be better to avoid this get_proper_type() call. + p_at = get_proper_type(at.type) + if isinstance(p_at, TypedDictType) and not at.from_star_syntax: + # Automatically detect Unpack[Foo] in Callable as backwards + # compatible syntax for **Foo, if Foo is a TypedDict. + at = p_at + arg_kinds[i] = ARG_STAR2 + unpacked_kwargs = True validated_args.append(at) arg_types = validated_args # If there were multiple (invalid) unpacks, the arg types list will become shorter, @@ -1013,6 +1023,7 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: fallback=(t.fallback if t.fallback.type else self.named_type("builtins.function")), variables=self.anal_var_defs(variables), type_guard=special, + unpack_kwargs=unpacked_kwargs, ) return ret diff --git a/mypy/types.py b/mypy/types.py index 04d90c9dc124..22fcd601d6a0 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1053,11 +1053,14 @@ class UnpackType(ProperType): wild west, technically anything can be present in the wrapped type. """ - __slots__ = ["type"] + __slots__ = ["type", "from_star_syntax"] - def __init__(self, typ: Type, line: int = -1, column: int = -1) -> None: + def __init__( + self, typ: Type, line: int = -1, column: int = -1, from_star_syntax: bool = False + ) -> None: super().__init__(line, column) self.type = typ + self.from_star_syntax = from_star_syntax def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_unpack_type(self) diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index ef2c3c57fad5..41668e991972 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -1079,3 +1079,18 @@ class C: class D: def __init__(self, **kwds: Unpack[int, str]) -> None: ... # E: Unpack[...] requires exactly one type argument [builtins fixtures/dict.pyi] + +[case testUnpackInCallableType] +from typing import Callable +from typing_extensions import Unpack, TypedDict + +class TD(TypedDict): + key: str + value: str + +foo: Callable[[Unpack[TD]], None] +foo(key="yes", value=42) # E: Argument "value" has incompatible type "int"; expected "str" +foo(key="yes", value="ok") + +bad: Callable[[*TD], None] # E: "TD" cannot be unpacked (must be tuple or TypeVarTuple) +[builtins fixtures/dict.pyi] From 66fbf5b526ad8cfa127dd5cca68dcb2f770b1dd7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 12 Sep 2023 18:19:53 +0100 Subject: [PATCH 0261/1617] [mypyc] Make tuple packing and unpacking more efficient (#16022) Previously returning a tuple from a function resulted in redundant increfs and decrefs for each item, and similarly unpacking the returned tuple in an assignment had extra incref/decref pair per item. This PR introduces these changes to make this better: * Creating a tuple steals the items always. * Accessing a tuple item optionally borrows the item. * A borrowed reference can be turned into a regular one using the new `Unborrow` op. * The no-op `KeepAlive` op can steal the operands to avoid decrefing the operands. Assignment from tuple now uses the three final features to avoid increfs and decrefs when unpacking a tuple in assignment. The docstrings in this PR contain additional explanation of how this works. In a micro-benchmark this improved performance by about 2-5%. In realistic examples the impact is likely small, but every little helps. Here is an example where this helps: ``` def f() -> tuple[C, C]: return C(), C() # Avoid 2 increfs and 2 decrefs def g() -> None: x, y = f() # Avoid 2 increfs and 2 decrefs ... ``` --------- Co-authored-by: Alex Waygood --- mypyc/analysis/dataflow.py | 4 ++ mypyc/analysis/ircheck.py | 4 ++ mypyc/analysis/selfleaks.py | 4 ++ mypyc/codegen/emitfunc.py | 11 +++- mypyc/ir/ops.py | 67 ++++++++++++++++++++++++- mypyc/ir/pprint.py | 14 +++++- mypyc/irbuild/ll_builder.py | 3 ++ mypyc/irbuild/statement.py | 23 +++++++++ mypyc/test-data/irbuild-statements.test | 29 ++++++----- mypyc/test-data/refcount.test | 60 ++++++++++++++++++++++ 10 files changed, 200 insertions(+), 19 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index ee2ff06b0f03..cade0c823962 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -46,6 +46,7 @@ Truncate, TupleGet, TupleSet, + Unborrow, Unbox, Unreachable, Value, @@ -272,6 +273,9 @@ def visit_load_address(self, op: LoadAddress) -> GenAndKill[T]: def visit_keep_alive(self, op: KeepAlive) -> GenAndKill[T]: return self.visit_register_op(op) + def visit_unborrow(self, op: Unborrow) -> GenAndKill[T]: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor[Value]): """Visitor for finding defined registers. diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index 2e6b7320e898..a31b1517b036 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -44,6 +44,7 @@ Truncate, TupleGet, TupleSet, + Unborrow, Unbox, Unreachable, Value, @@ -422,3 +423,6 @@ def visit_load_address(self, op: LoadAddress) -> None: def visit_keep_alive(self, op: KeepAlive) -> None: pass + + def visit_unborrow(self, op: Unborrow) -> None: + pass diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py index 288c366e50e5..80c2bc348bc2 100644 --- a/mypyc/analysis/selfleaks.py +++ b/mypyc/analysis/selfleaks.py @@ -40,6 +40,7 @@ Truncate, TupleGet, TupleSet, + Unborrow, Unbox, Unreachable, ) @@ -184,6 +185,9 @@ def visit_load_address(self, op: LoadAddress) -> GenAndKill: def visit_keep_alive(self, op: KeepAlive) -> GenAndKill: return CLEAN + def visit_unborrow(self, op: Unborrow) -> GenAndKill: + return CLEAN + def check_register_op(self, op: RegisterOp) -> GenAndKill: if any(src is self.self_reg for src in op.sources()): return DIRTY diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index b4d31544b196..3bce84d3ea59 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -55,6 +55,7 @@ Truncate, TupleGet, TupleSet, + Unborrow, Unbox, Unreachable, Value, @@ -260,7 +261,6 @@ def visit_tuple_set(self, op: TupleSet) -> None: else: for i, item in enumerate(op.items): self.emit_line(f"{dest}.f{i} = {self.reg(item)};") - self.emit_inc_ref(dest, tuple_type) def visit_assign(self, op: Assign) -> None: dest = self.reg(op.dest) @@ -499,7 +499,8 @@ def visit_tuple_get(self, op: TupleGet) -> None: dest = self.reg(op) src = self.reg(op.src) self.emit_line(f"{dest} = {src}.f{op.index};") - self.emit_inc_ref(dest, op.type) + if not op.is_borrowed: + self.emit_inc_ref(dest, op.type) def get_dest_assign(self, dest: Value) -> str: if not dest.is_void: @@ -746,6 +747,12 @@ def visit_keep_alive(self, op: KeepAlive) -> None: # This is a no-op. pass + def visit_unborrow(self, op: Unborrow) -> None: + # This is a no-op that propagates the source value. + dest = self.reg(op) + src = self.reg(op.src) + self.emit_line(f"{dest} = {src};") + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 2d64cc79d822..04c50d1e2841 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -792,6 +792,9 @@ def __init__(self, items: list[Value], line: int) -> None: def sources(self) -> list[Value]: return self.items.copy() + def stolen(self) -> list[Value]: + return self.items.copy() + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_tuple_set(self) @@ -801,13 +804,14 @@ class TupleGet(RegisterOp): error_kind = ERR_NEVER - def __init__(self, src: Value, index: int, line: int = -1) -> None: + def __init__(self, src: Value, index: int, line: int = -1, *, borrow: bool = False) -> None: super().__init__(line) self.src = src self.index = index assert isinstance(src.type, RTuple), "TupleGet only operates on tuples" assert index >= 0 self.type = src.type.types[index] + self.is_borrowed = borrow def sources(self) -> list[Value]: return [self.src] @@ -1387,21 +1391,76 @@ class KeepAlive(RegisterOp): If we didn't have "keep_alive x", x could be freed immediately after taking the address of 'item', resulting in a read after free on the second line. + + If 'steal' is true, the value is considered to be stolen at + this op, i.e. it won't be decref'd. You need to ensure that + the value is freed otherwise, perhaps by using borrowing + followed by Unborrow. + + Be careful with steal=True -- this can cause memory leaks. """ error_kind = ERR_NEVER - def __init__(self, src: list[Value]) -> None: + def __init__(self, src: list[Value], *, steal: bool = False) -> None: assert src self.src = src + self.steal = steal def sources(self) -> list[Value]: return self.src.copy() + def stolen(self) -> list[Value]: + if self.steal: + return self.src.copy() + return [] + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_keep_alive(self) +class Unborrow(RegisterOp): + """A no-op op to create a regular reference from a borrowed one. + + Borrowed references can only be used temporarily and the reference + counts won't be managed. This value will be refcounted normally. + + This is mainly useful if you split an aggregate value, such as + a tuple, into components using borrowed values (to avoid increfs), + and want to treat the components as sharing the original managed + reference. You'll also need to use KeepAlive with steal=True to + "consume" the original tuple reference: + + # t is a 2-tuple + r0 = borrow t[0] + r1 = borrow t[1] + r2 = unborrow r0 + r3 = unborrow r1 + # now (r2, r3) represent the tuple as separate items, and the + # original tuple can be considered dead and available to be + # stolen + keep_alive steal t + + Be careful with this -- this can easily cause double freeing. + """ + + error_kind = ERR_NEVER + + def __init__(self, src: Value) -> None: + assert src.is_borrowed + self.src = src + self.type = src.type + + def sources(self) -> list[Value]: + return [self.src] + + def stolen(self) -> list[Value]: + return [] + + def accept(self, visitor: OpVisitor[T]) -> T: + return visitor.visit_unborrow(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1548,6 +1607,10 @@ def visit_load_address(self, op: LoadAddress) -> T: def visit_keep_alive(self, op: KeepAlive) -> T: raise NotImplementedError + @abstractmethod + def visit_unborrow(self, op: Unborrow) -> T: + raise NotImplementedError + # TODO: Should the following definition live somewhere else? diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index c86060c49594..5578049256f1 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -51,6 +51,7 @@ Truncate, TupleGet, TupleSet, + Unborrow, Unbox, Unreachable, Value, @@ -153,7 +154,7 @@ def visit_init_static(self, op: InitStatic) -> str: return self.format("%s = %r :: %s", name, op.value, op.namespace) def visit_tuple_get(self, op: TupleGet) -> str: - return self.format("%r = %r[%d]", op, op.src, op.index) + return self.format("%r = %s%r[%d]", op, self.borrow_prefix(op), op.src, op.index) def visit_tuple_set(self, op: TupleSet) -> str: item_str = ", ".join(self.format("%r", item) for item in op.items) @@ -274,7 +275,16 @@ def visit_load_address(self, op: LoadAddress) -> str: return self.format("%r = load_address %s", op, op.src) def visit_keep_alive(self, op: KeepAlive) -> str: - return self.format("keep_alive %s" % ", ".join(self.format("%r", v) for v in op.src)) + if op.steal: + steal = "steal " + else: + steal = "" + return self.format( + "keep_alive {}{}".format(steal, ", ".join(self.format("%r", v) for v in op.src)) + ) + + def visit_unborrow(self, op: Unborrow) -> str: + return self.format("%r = unborrow %r", op, op.src) # Helpers diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 984b6a4deec0..d1ea91476a66 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -266,6 +266,9 @@ def goto_and_activate(self, block: BasicBlock) -> None: self.goto(block) self.activate_block(block) + def keep_alive(self, values: list[Value], *, steal: bool = False) -> None: + self.add(KeepAlive(values, steal=steal)) + def push_error_handler(self, handler: BasicBlock | None) -> None: self.error_handlers.append(handler) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 63297618108c..d7e01456139d 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -59,11 +59,13 @@ Register, Return, TupleGet, + Unborrow, Unreachable, Value, ) from mypyc.ir.rtypes import ( RInstance, + RTuple, c_pyssize_t_rprimitive, exc_rtuple, is_tagged, @@ -183,8 +185,29 @@ def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: line = stmt.rvalue.line rvalue_reg = builder.accept(stmt.rvalue) + if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(first_lvalue, rvalue_reg) + + # Special-case multiple assignments like 'x, y = expr' to reduce refcount ops. + if ( + isinstance(first_lvalue, (TupleExpr, ListExpr)) + and isinstance(rvalue_reg.type, RTuple) + and len(rvalue_reg.type.types) == len(first_lvalue.items) + and len(lvalues) == 1 + and all(is_simple_lvalue(item) for item in first_lvalue.items) + and any(t.is_refcounted for t in rvalue_reg.type.types) + ): + n = len(first_lvalue.items) + for i in range(n): + target = builder.get_assignment_target(first_lvalue.items[i]) + rvalue_item = builder.add(TupleGet(rvalue_reg, i, borrow=True)) + rvalue_item = builder.add(Unborrow(rvalue_item)) + builder.assign(target, rvalue_item, line) + builder.builder.keep_alive([rvalue_reg], steal=True) + builder.flush_keep_alives() + return + for lvalue in lvalues: target = builder.get_assignment_target(lvalue) builder.assign(target, rvalue_reg, line) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 062abd47d163..490b41336e88 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -502,16 +502,16 @@ L0: [case testMultipleAssignmentBasicUnpacking] from typing import Tuple, Any -def from_tuple(t: Tuple[int, str]) -> None: +def from_tuple(t: Tuple[bool, None]) -> None: x, y = t def from_any(a: Any) -> None: x, y = a [out] def from_tuple(t): - t :: tuple[int, str] - r0, x :: int - r1, y :: str + t :: tuple[bool, None] + r0, x :: bool + r1, y :: None L0: r0 = t[0] x = r0 @@ -563,16 +563,19 @@ def from_any(a: Any) -> None: [out] def from_tuple(t): t :: tuple[int, object] - r0 :: int - r1, x, r2 :: object - r3, y :: int + r0, r1 :: int + r2, x, r3, r4 :: object + r5, y :: int L0: - r0 = t[0] - r1 = box(int, r0) - x = r1 - r2 = t[1] - r3 = unbox(int, r2) - y = r3 + r0 = borrow t[0] + r1 = unborrow r0 + r2 = box(int, r1) + x = r2 + r3 = borrow t[1] + r4 = unborrow r3 + r5 = unbox(int, r4) + y = r5 + keep_alive steal t return 1 def from_any(a): a, r0, r1 :: object diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 3db4caa39566..0f2c134ae21e 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -656,6 +656,66 @@ L1: L2: return 4 +[case testReturnTuple] +from typing import Tuple + +class C: pass +def f() -> Tuple[C, C]: + a = C() + b = C() + return a, b +[out] +def f(): + r0, a, r1, b :: __main__.C + r2 :: tuple[__main__.C, __main__.C] +L0: + r0 = C() + a = r0 + r1 = C() + b = r1 + r2 = (a, b) + return r2 + +[case testDecomposeTuple] +from typing import Tuple + +class C: + a: int + +def f() -> int: + x, y = g() + return x.a + y.a + +def g() -> Tuple[C, C]: + return C(), C() +[out] +def f(): + r0 :: tuple[__main__.C, __main__.C] + r1, r2, x, r3, r4, y :: __main__.C + r5, r6, r7 :: int +L0: + r0 = g() + r1 = borrow r0[0] + r2 = unborrow r1 + x = r2 + r3 = borrow r0[1] + r4 = unborrow r3 + y = r4 + r5 = borrow x.a + r6 = borrow y.a + r7 = CPyTagged_Add(r5, r6) + dec_ref x + dec_ref y + return r7 +def g(): + r0, r1 :: __main__.C + r2 :: tuple[__main__.C, __main__.C] +L0: + r0 = C() + r1 = C() + r2 = (r0, r1) + return r2 + [case testUnicodeLiteral] def f() -> str: return "some string" From b3275572ec9b65d0a1b5157c5f73ad4004a356b4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 13 Sep 2023 23:41:08 +0100 Subject: [PATCH 0262/1617] Subtyping and inference of user defined variadic types (#16076) The second part of support for user defined variadic types comes as a single PR, it was hard to split into smaller parts. This part covers subtyping and inference (and relies on the first part: type analysis, normalization, and expansion, concluded by https://github.com/python/mypy/pull/15991). Note btw that the third (and last) part that covers actually using all the stuff in `checkexpr.py` will likely come as several smaller PRs. Some comments on this PR: * First good news: it looks like instances subtyping/inference can be handled in a really simple way, we just need to find correct type arguments mapping for each type variable, and perform procedures argument by argument (note this heavily relies on the normalization). Also callable subtyping inference for variadic items effectively defers to corresponding tuple types. This way all code paths will ultimately go through variadic tuple subtyping/inference (there is still a bunch of boilerplate to do the mapping, but it is quite simple). * Second some bad news: a lot of edge cases involving `*tuple[X, ...]` were missing everywhere (even couple cases in the code I touched before). I added all that were either simple or important. We can handle more if users will ask, since it is quite tricky. * Note that I handle variadic tuples essentially as infinite unions, the core of the logic for this (and for most of this PR FWIW) is in `variadic_tuple_subtype()`. * Previously `Foo[*tuple[int, ...]]` was considered a subtype of `Foo[int, int]`. I think this is wrong. I didn't find where this is required in the PEP (see one case below however), and mypy currently considers `tuple[int, ...]` not a subtype of `tuple[int, int]` (vice versa are subtypes), and similarly `(*args: int)` vs `(x: int, y: int)` for callables. Because of the logic I described in the first comment, the same logic now uniformly applies to instances as well. * Note however the PEP requires special casing of `Foo[*tuple[Any, ...]]` (equivalent to bare `Foo`), and I agree we should do this. I added a minimal special case for this. Note we also do this for callables as well (`*args: Any` is very different from `*args: object`). And I think we should special case `tuple[Any, ...] <: tuple[int, int]` as well. In the future we can even extend the special casing to `tuple[int, *tuple[Any, ...], int]` in the spirit of https://github.com/python/mypy/pull/15913 * In this PR I specifically only handle the PEP required item from above for instances. For plain tuples I left a TODO, @hauntsaninja may implement it since it is needed for other unrelated PR. * I make the default upper bound for `TypeVarTupleType` to be `tuple[object, ...]`. I think it can never be `object` (and this simplifies some subtyping corner cases). * TBH I didn't look into callables subtyping/inference very deeply (unlike instances and tuples), if needed we can improve their handling later. * Note I remove some failing unit tests because they test non-nomralized forms that should never appear now. We should probably add some more unit tests, but TBH I am quite tired now. --- mypy/constraints.py | 231 +++++++++---------- mypy/erasetype.py | 11 +- mypy/expandtype.py | 3 +- mypy/fixup.py | 17 +- mypy/join.py | 154 ++++++++++++- mypy/meet.py | 122 +++++++++- mypy/semanal.py | 3 +- mypy/semanal_typeargs.py | 3 +- mypy/solve.py | 8 +- mypy/subtypes.py | 241 ++++++++++++-------- mypy/test/testconstraints.py | 42 +--- mypy/test/testsubtypes.py | 83 +------ mypy/test/testtypes.py | 77 +++++++ mypy/test/typefixture.py | 11 +- mypy/typeops.py | 7 +- mypy/typevartuples.py | 134 ----------- test-data/unit/check-incremental.test | 19 ++ test-data/unit/check-typevar-tuple.test | 285 +++++++++++++++++++++++- test-data/unit/semanal-types.test | 7 +- 19 files changed, 943 insertions(+), 515 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 0e59b5459fd4..0524e38f9643 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterable, List, Sequence, cast +from typing import TYPE_CHECKING, Final, Iterable, List, Sequence import mypy.subtypes import mypy.typeops @@ -58,7 +58,6 @@ ) from mypy.types_utils import is_union_with_any from mypy.typestate import type_state -from mypy.typevartuples import extract_unpack, split_with_mapped_and_template if TYPE_CHECKING: from mypy.infer import ArgumentInferContext @@ -745,28 +744,23 @@ def visit_instance(self, template: Instance) -> list[Constraint]: tvars = mapped.type.defn.type_vars if instance.type.has_type_var_tuple_type: + # Variadic types need special handling to map each type argument to + # the correct corresponding type variable. assert instance.type.type_var_tuple_prefix is not None assert instance.type.type_var_tuple_suffix is not None - assert mapped.type.type_var_tuple_prefix is not None - assert mapped.type.type_var_tuple_suffix is not None - - unpack_constraints, instance_args, mapped_args = build_constraints_for_unpack( - instance.args, - instance.type.type_var_tuple_prefix, - instance.type.type_var_tuple_suffix, - mapped.args, - mapped.type.type_var_tuple_prefix, - mapped.type.type_var_tuple_suffix, - self.direction, + prefix_len = instance.type.type_var_tuple_prefix + suffix_len = instance.type.type_var_tuple_suffix + tvt = instance.type.defn.type_vars[prefix_len] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + i_prefix, i_middle, i_suffix = split_with_prefix_and_suffix( + instance.args, prefix_len, suffix_len ) - res.extend(unpack_constraints) - - tvars_prefix, _, tvars_suffix = split_with_prefix_and_suffix( - tuple(tvars), - instance.type.type_var_tuple_prefix, - instance.type.type_var_tuple_suffix, + m_prefix, m_middle, m_suffix = split_with_prefix_and_suffix( + mapped.args, prefix_len, suffix_len ) - tvars = cast("list[TypeVarLikeType]", list(tvars_prefix + tvars_suffix)) + instance_args = i_prefix + (TupleType(list(i_middle), fallback),) + i_suffix + mapped_args = m_prefix + (TupleType(list(m_middle), fallback),) + m_suffix else: mapped_args = mapped.args instance_args = instance.args @@ -806,44 +800,38 @@ def visit_instance(self, template: Instance) -> list[Constraint]: ) res.append(Constraint(mapped_arg, SUBTYPE_OF, suffix)) res.append(Constraint(mapped_arg, SUPERTYPE_OF, suffix)) - else: - # This case should have been handled above. - assert not isinstance(tvar, TypeVarTupleType) + elif isinstance(tvar, TypeVarTupleType): + # Handle variadic type variables covariantly for consistency. + res.extend(infer_constraints(mapped_arg, instance_arg, self.direction)) return res elif self.direction == SUPERTYPE_OF and instance.type.has_base(template.type.fullname): mapped = map_instance_to_supertype(instance, template.type) tvars = template.type.defn.type_vars if template.type.has_type_var_tuple_type: - assert mapped.type.type_var_tuple_prefix is not None - assert mapped.type.type_var_tuple_suffix is not None + # Variadic types need special handling to map each type argument to + # the correct corresponding type variable. assert template.type.type_var_tuple_prefix is not None assert template.type.type_var_tuple_suffix is not None - - unpack_constraints, mapped_args, template_args = build_constraints_for_unpack( - mapped.args, - mapped.type.type_var_tuple_prefix, - mapped.type.type_var_tuple_suffix, - template.args, - template.type.type_var_tuple_prefix, - template.type.type_var_tuple_suffix, - self.direction, + prefix_len = template.type.type_var_tuple_prefix + suffix_len = template.type.type_var_tuple_suffix + tvt = template.type.defn.type_vars[prefix_len] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + t_prefix, t_middle, t_suffix = split_with_prefix_and_suffix( + template.args, prefix_len, suffix_len ) - res.extend(unpack_constraints) - - tvars_prefix, _, tvars_suffix = split_with_prefix_and_suffix( - tuple(tvars), - template.type.type_var_tuple_prefix, - template.type.type_var_tuple_suffix, + m_prefix, m_middle, m_suffix = split_with_prefix_and_suffix( + mapped.args, prefix_len, suffix_len ) - tvars = cast("list[TypeVarLikeType]", list(tvars_prefix + tvars_suffix)) + template_args = t_prefix + (TupleType(list(t_middle), fallback),) + t_suffix + mapped_args = m_prefix + (TupleType(list(m_middle), fallback),) + m_suffix else: mapped_args = mapped.args template_args = template.args # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. for tvar, mapped_arg, template_arg in zip(tvars, mapped_args, template_args): - assert not isinstance(tvar, TypeVarTupleType) if isinstance(tvar, TypeVarType): # The constraints for generic type parameters depend on variance. # Include constraints from both directions if invariant. @@ -878,9 +866,9 @@ def visit_instance(self, template: Instance) -> list[Constraint]: ) res.append(Constraint(template_arg, SUBTYPE_OF, suffix)) res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) - else: - # This case should have been handled above. - assert not isinstance(tvar, TypeVarTupleType) + elif isinstance(tvar, TypeVarTupleType): + # Handle variadic type variables covariantly for consistency. + res.extend(infer_constraints(template_arg, mapped_arg, self.direction)) return res if ( template.type.is_protocol @@ -1049,7 +1037,8 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) res.extend(unpack_constraints) else: - # Negate direction due to function argument type contravariance. + # TODO: do we need some special-casing when unpack is present in actual + # callable but not in template callable? res.extend( infer_callable_arguments_constraints(template, cactual, self.direction) ) @@ -1170,11 +1159,29 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]: res: list[Constraint] = [] if unpack_index is not None: if is_varlength_tuple: + # Variadic tuple can be only a supertype of a tuple type, but even if + # direction is opposite, inferring something may give better error messages. unpack_type = template.items[unpack_index] assert isinstance(unpack_type, UnpackType) - unpacked_type = unpack_type.type - assert isinstance(unpacked_type, TypeVarTupleType) - return [Constraint(type_var=unpacked_type, op=self.direction, target=actual)] + unpacked_type = get_proper_type(unpack_type.type) + if isinstance(unpacked_type, TypeVarTupleType): + res = [ + Constraint(type_var=unpacked_type, op=self.direction, target=actual) + ] + else: + assert ( + isinstance(unpacked_type, Instance) + and unpacked_type.type.fullname == "builtins.tuple" + ) + res = infer_constraints(unpacked_type, actual, self.direction) + assert isinstance(actual, Instance) # ensured by is_varlength_tuple == True + for i, ti in enumerate(template.items): + if i == unpack_index: + # This one we just handled above. + continue + # For Tuple[T, *Ts, S] <: tuple[X, ...] infer also T <: X and S <: X. + res.extend(infer_constraints(ti, actual.args[0], self.direction)) + return res else: assert isinstance(actual, TupleType) unpack_constraints = build_constraints_for_simple_unpack( @@ -1184,8 +1191,36 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]: template_items: tuple[Type, ...] = () res.extend(unpack_constraints) elif isinstance(actual, TupleType): - actual_items = tuple(actual.items) - template_items = tuple(template.items) + a_unpack_index = find_unpack_in_list(actual.items) + if a_unpack_index is not None: + # The case where template tuple doesn't have an unpack, but actual tuple + # has an unpack. We can infer something if actual unpack is a variadic tuple. + # Tuple[T, S, U] <: tuple[X, *tuple[Y, ...], Z] => T <: X, S <: Y, U <: Z. + a_unpack = actual.items[a_unpack_index] + assert isinstance(a_unpack, UnpackType) + a_unpacked = get_proper_type(a_unpack.type) + if len(actual.items) + 1 <= len(template.items): + a_prefix_len = a_unpack_index + a_suffix_len = len(actual.items) - a_unpack_index - 1 + t_prefix, t_middle, t_suffix = split_with_prefix_and_suffix( + tuple(template.items), a_prefix_len, a_suffix_len + ) + actual_items = tuple(actual.items[:a_prefix_len]) + if a_suffix_len: + actual_items += tuple(actual.items[-a_suffix_len:]) + template_items = t_prefix + t_suffix + if isinstance(a_unpacked, Instance): + assert a_unpacked.type.fullname == "builtins.tuple" + for tm in t_middle: + res.extend( + infer_constraints(tm, a_unpacked.args[0], self.direction) + ) + else: + actual_items = () + template_items = () + else: + actual_items = tuple(actual.items) + template_items = tuple(template.items) else: return res @@ -1236,8 +1271,13 @@ def visit_type_alias_type(self, template: TypeAliasType) -> list[Constraint]: def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> list[Constraint]: res: list[Constraint] = [] for t in types: - if isinstance(t, UnpackType) and isinstance(t.type, TypeVarTupleType): - res.append(Constraint(t.type, self.direction, any_type)) + if isinstance(t, UnpackType): + if isinstance(t.type, TypeVarTupleType): + res.append(Constraint(t.type, self.direction, any_type)) + else: + unpacked = get_proper_type(t.type) + assert isinstance(unpacked, Instance) + res.extend(infer_constraints(unpacked, any_type, self.direction)) else: # Note that we ignore variance and simply always use the # original direction. This is because for Any targets direction is @@ -1374,9 +1414,8 @@ def build_constraints_for_simple_unpack( templates: T1, T2, Ts, Ts, Ts, ... actuals: A1, As, As, As, ... - Note: this function can only be called for builtin variadic constructors: Tuple and Callable, - for Instances variance depends on position, and a much more complex function - build_constraints_for_unpack() should be used. + Note: this function can only be called for builtin variadic constructors: Tuple and Callable. + For instances, you should first find correct type argument mapping. """ template_unpack = find_unpack_in_list(template_args) assert template_unpack is not None @@ -1409,7 +1448,8 @@ def build_constraints_for_simple_unpack( common_prefix = min(template_prefix, actual_prefix) common_suffix = min(template_suffix, actual_suffix) if actual_prefix >= template_prefix and actual_suffix >= template_suffix: - # This is the only case where we can guarantee there will be no partial overlap. + # This is the only case where we can guarantee there will be no partial overlap + # (note however partial overlap is OK for variadic tuples, it is handled below). t_unpack = template_args[template_unpack] # Handle constraints from prefixes/suffixes first. @@ -1439,74 +1479,21 @@ def build_constraints_for_simple_unpack( res.extend(infer_constraints(tp.args[0], a_tp.args[0], direction)) elif isinstance(tp, TypeVarTupleType): res.append(Constraint(tp, direction, TupleType(list(middle), tp.tuple_fallback))) + elif actual_unpack is not None: + # A special case for a variadic tuple unpack, we simply infer T <: X from + # Tuple[..., *tuple[T, ...], ...] <: Tuple[..., *tuple[X, ...], ...]. + actual_unpack_type = actual_args[actual_unpack] + assert isinstance(actual_unpack_type, UnpackType) + a_unpacked = get_proper_type(actual_unpack_type.type) + if isinstance(a_unpacked, Instance) and a_unpacked.type.fullname == "builtins.tuple": + t_unpack = template_args[template_unpack] + assert isinstance(t_unpack, UnpackType) + tp = get_proper_type(t_unpack.type) + if isinstance(tp, Instance) and tp.type.fullname == "builtins.tuple": + res.extend(infer_constraints(tp.args[0], a_unpacked.args[0], direction)) return res -def build_constraints_for_unpack( - # TODO: this naming is misleading, these should be "actual", not "mapped" - # both template and actual can be mapped before, depending on direction. - # Also the convention is to put template related args first. - mapped: tuple[Type, ...], - mapped_prefix_len: int | None, - mapped_suffix_len: int | None, - template: tuple[Type, ...], - template_prefix_len: int, - template_suffix_len: int, - direction: int, -) -> tuple[list[Constraint], tuple[Type, ...], tuple[Type, ...]]: - # TODO: this function looks broken: - # a) it should take into account variances, but it doesn't - # b) it looks like both call sites always pass identical values to args (2, 3) and (5, 6) - # because after map_instance_to_supertype() both template and actual have same TypeInfo. - if mapped_prefix_len is None: - mapped_prefix_len = template_prefix_len - if mapped_suffix_len is None: - mapped_suffix_len = template_suffix_len - - split_result = split_with_mapped_and_template( - mapped, - mapped_prefix_len, - mapped_suffix_len, - template, - template_prefix_len, - template_suffix_len, - ) - assert split_result is not None - ( - mapped_prefix, - mapped_middle, - mapped_suffix, - template_prefix, - template_middle, - template_suffix, - ) = split_result - - template_unpack = extract_unpack(template_middle) - res = [] - - if template_unpack is not None: - if isinstance(template_unpack, TypeVarTupleType): - res.append( - Constraint( - template_unpack, - direction, - TupleType(list(mapped_middle), template_unpack.tuple_fallback), - ) - ) - elif ( - isinstance(template_unpack, Instance) - and template_unpack.type.fullname == "builtins.tuple" - ): - for item in mapped_middle: - res.extend(infer_constraints(template_unpack.args[0], item, direction)) - - elif isinstance(template_unpack, TupleType): - if len(template_unpack.items) == len(mapped_middle): - for template_arg, item in zip(template_unpack.items, mapped_middle): - res.extend(infer_constraints(template_arg, item, direction)) - return res, mapped_prefix + mapped_suffix, template_prefix + template_suffix - - def infer_directed_arg_constraints(left: Type, right: Type, direction: int) -> list[Constraint]: """Infer constraints between two arguments using direction between original callables.""" if isinstance(left, (ParamSpecType, UnpackType)) or isinstance( diff --git a/mypy/erasetype.py b/mypy/erasetype.py index d1a01fb6c779..24471f918319 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -77,7 +77,16 @@ def visit_deleted_type(self, t: DeletedType) -> ProperType: return t def visit_instance(self, t: Instance) -> ProperType: - return Instance(t.type, [AnyType(TypeOfAny.special_form)] * len(t.args), t.line) + args: list[Type] = [] + for tv in t.type.defn.type_vars: + # Valid erasure for *Ts is *tuple[Any, ...], not just Any. + if isinstance(tv, TypeVarTupleType): + args.append( + tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) + ) + else: + args.append(AnyType(TypeOfAny.special_form)) + return Instance(t.type, args, t.line) def visit_type_var(self, t: TypeVarType) -> ProperType: return AnyType(TypeOfAny.special_form) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index c29fcb167777..b233561e19c2 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -255,7 +255,8 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: variables=[*t.prefix.variables, *repl.variables], ) else: - # TODO: replace this with "assert False" + # We could encode Any as trivial parameters etc., but it would be too verbose. + # TODO: assert this is a trivial type, like Any, Never, or object. return repl def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: diff --git a/mypy/fixup.py b/mypy/fixup.py index 2b2e1210ee4e..5ffc47120734 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -81,11 +81,17 @@ def visit_type_info(self, info: TypeInfo) -> None: info.update_tuple_type(info.tuple_type) if info.special_alias: info.special_alias.alias_tvars = list(info.defn.type_vars) + for i, t in enumerate(info.defn.type_vars): + if isinstance(t, TypeVarTupleType): + info.special_alias.tvar_tuple_index = i if info.typeddict_type: info.typeddict_type.accept(self.type_fixer) info.update_typeddict_type(info.typeddict_type) if info.special_alias: info.special_alias.alias_tvars = list(info.defn.type_vars) + for i, t in enumerate(info.defn.type_vars): + if isinstance(t, TypeVarTupleType): + info.special_alias.tvar_tuple_index = i if info.declared_metaclass: info.declared_metaclass.accept(self.type_fixer) if info.metaclass_type: @@ -166,11 +172,7 @@ def visit_decorator(self, d: Decorator) -> None: def visit_class_def(self, c: ClassDef) -> None: for v in c.type_vars: - if isinstance(v, TypeVarType): - for value in v.values: - value.accept(self.type_fixer) - v.upper_bound.accept(self.type_fixer) - v.default.accept(self.type_fixer) + v.accept(self.type_fixer) def visit_type_var_expr(self, tv: TypeVarExpr) -> None: for value in tv.values: @@ -184,6 +186,7 @@ def visit_paramspec_expr(self, p: ParamSpecExpr) -> None: def visit_type_var_tuple_expr(self, tv: TypeVarTupleExpr) -> None: tv.upper_bound.accept(self.type_fixer) + tv.tuple_fallback.accept(self.type_fixer) tv.default.accept(self.type_fixer) def visit_var(self, v: Var) -> None: @@ -314,6 +317,7 @@ def visit_param_spec(self, p: ParamSpecType) -> None: p.default.accept(self) def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + t.tuple_fallback.accept(self) t.upper_bound.accept(self) t.default.accept(self) @@ -336,9 +340,6 @@ def visit_union_type(self, ut: UnionType) -> None: for it in ut.items: it.accept(self) - def visit_void(self, o: Any) -> None: - pass # Nothing to descend into. - def visit_type_type(self, t: TypeType) -> None: t.item.accept(self) diff --git a/mypy/join.py b/mypy/join.py index 806c644a680c..e4429425d98a 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -43,8 +43,10 @@ UninhabitedType, UnionType, UnpackType, + find_unpack_in_list, get_proper_type, get_proper_types, + split_with_prefix_and_suffix, ) @@ -67,7 +69,25 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: args: list[Type] = [] # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. - for ta, sa, type_var in zip(t.args, s.args, t.type.defn.type_vars): + if t.type.has_type_var_tuple_type: + # We handle joins of variadic instances by simply creating correct mapping + # for type arguments and compute the individual joins same as for regular + # instances. All the heavy lifting is done in the join of tuple types. + assert s.type.type_var_tuple_prefix is not None + assert s.type.type_var_tuple_suffix is not None + prefix = s.type.type_var_tuple_prefix + suffix = s.type.type_var_tuple_suffix + tvt = s.type.defn.type_vars[prefix] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + s_prefix, s_middle, s_suffix = split_with_prefix_and_suffix(s.args, prefix, suffix) + t_prefix, t_middle, t_suffix = split_with_prefix_and_suffix(t.args, prefix, suffix) + s_args = s_prefix + (TupleType(list(s_middle), fallback),) + s_suffix + t_args = t_prefix + (TupleType(list(t_middle), fallback),) + t_suffix + else: + t_args = t.args + s_args = s.args + for ta, sa, type_var in zip(t_args, s_args, t.type.defn.type_vars): ta_proper = get_proper_type(ta) sa_proper = get_proper_type(sa) new_type: Type | None = None @@ -93,6 +113,18 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # If the types are different but equivalent, then an Any is involved # so using a join in the contravariant case is also OK. new_type = join_types(ta, sa, self) + elif isinstance(type_var, TypeVarTupleType): + new_type = get_proper_type(join_types(ta, sa, self)) + # Put the joined arguments back into instance in the normal form: + # a) Tuple[X, Y, Z] -> [X, Y, Z] + # b) tuple[X, ...] -> [*tuple[X, ...]] + if isinstance(new_type, Instance): + assert new_type.type.fullname == "builtins.tuple" + new_type = UnpackType(new_type) + else: + assert isinstance(new_type, TupleType) + args.extend(new_type.items) + continue else: # ParamSpec type variables behave the same, independent of variance if not is_equivalent(ta, sa): @@ -440,6 +472,113 @@ def visit_overloaded(self, t: Overloaded) -> ProperType: return join_types(t, call) return join_types(t.fallback, s) + def join_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: + """Join two tuple types while handling variadic entries. + + This is surprisingly tricky, and we don't handle some tricky corner cases. + Most of the trickiness comes from the variadic tuple items like *tuple[X, ...] + since they can have arbitrary partial overlaps (while *Ts can't be split). + """ + s_unpack_index = find_unpack_in_list(s.items) + t_unpack_index = find_unpack_in_list(t.items) + if s_unpack_index is None and t_unpack_index is None: + if s.length() == t.length(): + items: list[Type] = [] + for i in range(t.length()): + items.append(join_types(t.items[i], s.items[i])) + return items + return None + if s_unpack_index is not None and t_unpack_index is not None: + # The most complex case: both tuples have an upack item. + s_unpack = s.items[s_unpack_index] + assert isinstance(s_unpack, UnpackType) + s_unpacked = get_proper_type(s_unpack.type) + t_unpack = t.items[t_unpack_index] + assert isinstance(t_unpack, UnpackType) + t_unpacked = get_proper_type(t_unpack.type) + if s.length() == t.length() and s_unpack_index == t_unpack_index: + # We can handle a case where arity is perfectly aligned, e.g. + # join(Tuple[X1, *tuple[Y1, ...], Z1], Tuple[X2, *tuple[Y2, ...], Z2]). + # We can essentially perform the join elementwise. + prefix_len = t_unpack_index + suffix_len = t.length() - t_unpack_index - 1 + items = [] + for si, ti in zip(s.items[:prefix_len], t.items[:prefix_len]): + items.append(join_types(si, ti)) + joined = join_types(s_unpacked, t_unpacked) + if isinstance(joined, TypeVarTupleType): + items.append(UnpackType(joined)) + elif isinstance(joined, Instance) and joined.type.fullname == "builtins.tuple": + items.append(UnpackType(joined)) + else: + if isinstance(t_unpacked, Instance): + assert t_unpacked.type.fullname == "builtins.tuple" + tuple_instance = t_unpacked + else: + assert isinstance(t_unpacked, TypeVarTupleType) + tuple_instance = t_unpacked.tuple_fallback + items.append( + UnpackType( + tuple_instance.copy_modified( + args=[object_from_instance(tuple_instance)] + ) + ) + ) + if suffix_len: + for si, ti in zip(s.items[-suffix_len:], t.items[-suffix_len:]): + items.append(join_types(si, ti)) + return items + if s.length() == 1 or t.length() == 1: + # Another case we can handle is when one of tuple is purely variadic + # (i.e. a non-normalized form of tuple[X, ...]), in this case the join + # will be again purely variadic. + if not (isinstance(s_unpacked, Instance) and isinstance(t_unpacked, Instance)): + return None + assert s_unpacked.type.fullname == "builtins.tuple" + assert t_unpacked.type.fullname == "builtins.tuple" + mid_joined = join_types(s_unpacked.args[0], t_unpacked.args[0]) + t_other = [a for i, a in enumerate(t.items) if i != t_unpack_index] + s_other = [a for i, a in enumerate(s.items) if i != s_unpack_index] + other_joined = join_type_list(s_other + t_other) + mid_joined = join_types(mid_joined, other_joined) + return [UnpackType(s_unpacked.copy_modified(args=[mid_joined]))] + # TODO: are there other case we can handle (e.g. both prefix/suffix are shorter)? + return None + if s_unpack_index is not None: + variadic = s + unpack_index = s_unpack_index + fixed = t + else: + assert t_unpack_index is not None + variadic = t + unpack_index = t_unpack_index + fixed = s + # Case where one tuple has variadic item and the other one doesn't. The join will + # be variadic, since fixed tuple is a subtype of variadic, but not vice versa. + unpack = variadic.items[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + if not isinstance(unpacked, Instance): + return None + if fixed.length() < variadic.length() - 1: + # There are no non-trivial types that are supertype of both. + return None + prefix_len = unpack_index + suffix_len = variadic.length() - prefix_len - 1 + prefix, middle, suffix = split_with_prefix_and_suffix( + tuple(fixed.items), prefix_len, suffix_len + ) + items = [] + for fi, vi in zip(prefix, variadic.items[:prefix_len]): + items.append(join_types(fi, vi)) + mid_joined = join_type_list(list(middle)) + mid_joined = join_types(mid_joined, unpacked.args[0]) + items.append(UnpackType(unpacked.copy_modified(args=[mid_joined]))) + if suffix_len: + for fi, vi in zip(suffix, variadic.items[-suffix_len:]): + items.append(join_types(fi, vi)) + return items + def visit_tuple_type(self, t: TupleType) -> ProperType: # When given two fixed-length tuples: # * If they have the same length, join their subtypes item-wise: @@ -452,19 +591,22 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: # Tuple[int, bool] + Tuple[bool, ...] becomes Tuple[int, ...] # * Joining with any Sequence also returns a Sequence: # Tuple[int, bool] + List[bool] becomes Sequence[int] - if isinstance(self.s, TupleType) and self.s.length() == t.length(): + if isinstance(self.s, TupleType): if self.instance_joiner is None: self.instance_joiner = InstanceJoiner() fallback = self.instance_joiner.join_instances( mypy.typeops.tuple_fallback(self.s), mypy.typeops.tuple_fallback(t) ) assert isinstance(fallback, Instance) - if self.s.length() == t.length(): - items: list[Type] = [] - for i in range(t.length()): - items.append(join_types(t.items[i], self.s.items[i])) + items = self.join_tuples(self.s, t) + if items is not None: return TupleType(items, fallback) else: + # TODO: should this be a default fallback behaviour like for meet? + if is_proper_subtype(self.s, t): + return t + if is_proper_subtype(t, self.s): + return self.s return fallback else: return join_types(self.s, mypy.typeops.tuple_fallback(t)) diff --git a/mypy/meet.py b/mypy/meet.py index 2efde4ac7588..0fa500d32c30 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -44,8 +44,10 @@ UninhabitedType, UnionType, UnpackType, + find_unpack_in_list, get_proper_type, get_proper_types, + split_with_prefix_and_suffix, ) # TODO Describe this module. @@ -721,8 +723,41 @@ def visit_instance(self, t: Instance) -> ProperType: args: list[Type] = [] # N.B: We use zip instead of indexing because the lengths might have # mismatches during daemon reprocessing. - for ta, sia in zip(t.args, self.s.args): - args.append(self.meet(ta, sia)) + if t.type.has_type_var_tuple_type: + # We handle meet of variadic instances by simply creating correct mapping + # for type arguments and compute the individual meets same as for regular + # instances. All the heavy lifting is done in the meet of tuple types. + s = self.s + assert s.type.type_var_tuple_prefix is not None + assert s.type.type_var_tuple_suffix is not None + prefix = s.type.type_var_tuple_prefix + suffix = s.type.type_var_tuple_suffix + tvt = s.type.defn.type_vars[prefix] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + s_prefix, s_middle, s_suffix = split_with_prefix_and_suffix( + s.args, prefix, suffix + ) + t_prefix, t_middle, t_suffix = split_with_prefix_and_suffix( + t.args, prefix, suffix + ) + s_args = s_prefix + (TupleType(list(s_middle), fallback),) + s_suffix + t_args = t_prefix + (TupleType(list(t_middle), fallback),) + t_suffix + else: + t_args = t.args + s_args = self.s.args + for ta, sa, tv in zip(t_args, s_args, t.type.defn.type_vars): + meet = self.meet(ta, sa) + if isinstance(tv, TypeVarTupleType): + # Correctly unpack possible outcomes of meets of tuples: it can be + # either another tuple type or Never (normalized as *tuple[Never, ...]) + if isinstance(meet, TupleType): + args.extend(meet.items) + continue + else: + assert isinstance(meet, UninhabitedType) + meet = UnpackType(tv.tuple_fallback.copy_modified(args=[meet])) + args.append(meet) return Instance(t.type, args) else: if state.strict_optional: @@ -811,11 +846,82 @@ def visit_overloaded(self, t: Overloaded) -> ProperType: return meet_types(t, call) return meet_types(t.fallback, s) + def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: + """Meet two tuple types while handling variadic entries. + + This is surprisingly tricky, and we don't handle some tricky corner cases. + Most of the trickiness comes from the variadic tuple items like *tuple[X, ...] + since they can have arbitrary partial overlaps (while *Ts can't be split). This + function is roughly a mirror of join_tuples() w.r.t. to the fact that fixed + tuples are subtypes of variadic ones but not vice versa. + """ + s_unpack_index = find_unpack_in_list(s.items) + t_unpack_index = find_unpack_in_list(t.items) + if s_unpack_index is None and t_unpack_index is None: + if s.length() == t.length(): + items: list[Type] = [] + for i in range(t.length()): + items.append(self.meet(t.items[i], s.items[i])) + return items + return None + if s_unpack_index is not None and t_unpack_index is not None: + # The only simple case we can handle if both tuples are variadic + # is when they are purely variadic. Other cases are tricky because + # a variadic item is effectively a union of tuples of all length, thus + # potentially causing overlap between a suffix in `s` and a prefix + # in `t` (see how this is handled in is_subtype() for details). + # TODO: handle more cases (like when both prefix/suffix are shorter in s or t). + if s.length() == 1 and t.length() == 1: + s_unpack = s.items[0] + assert isinstance(s_unpack, UnpackType) + s_unpacked = get_proper_type(s_unpack.type) + t_unpack = t.items[0] + assert isinstance(t_unpack, UnpackType) + t_unpacked = get_proper_type(t_unpack.type) + if not (isinstance(s_unpacked, Instance) and isinstance(t_unpacked, Instance)): + return None + meet = self.meet(s_unpacked, t_unpacked) + if not isinstance(meet, Instance): + return None + return [UnpackType(meet)] + return None + if s_unpack_index is not None: + variadic = s + unpack_index = s_unpack_index + fixed = t + else: + assert t_unpack_index is not None + variadic = t + unpack_index = t_unpack_index + fixed = s + # If one tuple is variadic one, and the other one is fixed, the meet will be fixed. + unpack = variadic.items[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + if not isinstance(unpacked, Instance): + return None + if fixed.length() < variadic.length() - 1: + return None + prefix_len = unpack_index + suffix_len = variadic.length() - prefix_len - 1 + prefix, middle, suffix = split_with_prefix_and_suffix( + tuple(fixed.items), prefix_len, suffix_len + ) + items = [] + for fi, vi in zip(prefix, variadic.items[:prefix_len]): + items.append(self.meet(fi, vi)) + for mi in middle: + items.append(self.meet(mi, unpacked.args[0])) + if suffix_len: + for fi, vi in zip(suffix, variadic.items[-suffix_len:]): + items.append(self.meet(fi, vi)) + return items + def visit_tuple_type(self, t: TupleType) -> ProperType: - if isinstance(self.s, TupleType) and self.s.length() == t.length(): - items: list[Type] = [] - for i in range(t.length()): - items.append(self.meet(t.items[i], self.s.items[i])) + if isinstance(self.s, TupleType): + items = self.meet_tuples(self.s, t) + if items is None: + return self.default(self.s) # TODO: What if the fallbacks are different? return TupleType(items, tuple_fallback(t)) elif isinstance(self.s, Instance): @@ -825,6 +931,10 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: elif is_proper_subtype(t, self.s): # A named tuple that inherits from a normal class return t + elif self.s.type.has_type_var_tuple_type and is_subtype(t, self.s): + # This is a bit ad-hoc but more principled handling is tricky, and this + # special case is important for type narrowing in binder to work. + return t return self.default(self.s) def visit_typeddict_type(self, t: TypedDictType) -> ProperType: diff --git a/mypy/semanal.py b/mypy/semanal.py index ec4d32aefeb9..70403eed57ae 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4414,7 +4414,8 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: typevartuple_var = TypeVarTupleExpr( name, self.qualified_name(name), - self.object_type(), + # Upper bound for *Ts is *tuple[object, ...], it can never be object. + tuple_fallback.copy_modified(), tuple_fallback, default, INVARIANT, diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index ed04b30e90ba..a25bab8de054 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -207,7 +207,8 @@ def visit_unpack_type(self, typ: UnpackType) -> None: return if isinstance(proper_type, TypeVarTupleType): return - # TODO: this should probably be .has_base("builtins.tuple"), also elsewhere. + # TODO: this should probably be .has_base("builtins.tuple"), also elsewhere. This is + # tricky however, since this needs map_instance_to_supertype() available in many places. if isinstance(proper_type, Instance) and proper_type.type.fullname == "builtins.tuple": return if not isinstance(proper_type, (UnboundType, AnyType)): diff --git a/mypy/solve.py b/mypy/solve.py index 17e1ca047818..7cdf1c10c9b5 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -144,6 +144,8 @@ def solve_with_dependent( if all(not lowers[tv] and not uppers[tv] for tv in scc): best_free = choose_free([originals[tv] for tv in scc], original_vars) if best_free: + # TODO: failing to choose may cause leaking type variables, + # we need to fail gracefully instead. free_vars.append(best_free.id) free_solutions[best_free.id] = best_free @@ -323,13 +325,15 @@ def test(x: U) -> U: ... best = sorted(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id))[0] if isinstance(best, TypeVarType): return best.copy_modified(values=values, upper_bound=common_upper_bound) - if is_trivial_bound(common_upper_bound_p): + if is_trivial_bound(common_upper_bound_p, allow_tuple=True): # TODO: support more cases for ParamSpecs/TypeVarTuples return best return None -def is_trivial_bound(tp: ProperType) -> bool: +def is_trivial_bound(tp: ProperType, allow_tuple: bool = False) -> bool: + if isinstance(tp, Instance) and tp.type.fullname == "builtins.tuple": + return allow_tuple and is_trivial_bound(get_proper_type(tp.args[0])) return isinstance(tp, Instance) and tp.type.fullname == "builtins.object" diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 58ae4efdf582..fdde1c24670e 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -58,13 +58,14 @@ UninhabitedType, UnionType, UnpackType, + find_unpack_in_list, get_proper_type, is_named_instance, + split_with_prefix_and_suffix, ) from mypy.types_utils import flatten_types from mypy.typestate import SubtypeKind, type_state from mypy.typevars import fill_typevars_with_any -from mypy.typevartuples import extract_unpack, fully_split_with_mapped_and_template # Flags for detected protocol members IS_SETTABLE: Final = 1 @@ -278,7 +279,13 @@ def _is_subtype( left = get_proper_type(left) right = get_proper_type(right) - if not proper_subtype and isinstance(right, (AnyType, UnboundType, ErasedType)): + # Note: Unpack type should not be a subtype of Any, since it may represent + # multiple types. This should always go through the visitor, to check arity. + if ( + not proper_subtype + and isinstance(right, (AnyType, UnboundType, ErasedType)) + and not isinstance(left, UnpackType) + ): # TODO: should we consider all types proper subtypes of UnboundType and/or # ErasedType as we do for non-proper subtyping. return True @@ -437,6 +444,34 @@ def visit_instance(self, left: Instance) -> bool: right = self.right if isinstance(right, TupleType) and right.partial_fallback.type.is_enum: return self._is_subtype(left, mypy.typeops.tuple_fallback(right)) + if isinstance(right, TupleType): + if len(right.items) == 1: + # Non-normalized Tuple type (may be left after semantic analysis + # because semanal_typearg visitor is not a type translator). + item = right.items[0] + if isinstance(item, UnpackType): + unpacked = get_proper_type(item.type) + if isinstance(unpacked, Instance): + return self._is_subtype(left, unpacked) + if left.type.has_base(right.partial_fallback.type.fullname): + # Special case to consider Foo[*tuple[Any, ...]] (i.e. bare Foo) a + # subtype of Foo[], when Foo is user defined variadic tuple type. + mapped = map_instance_to_supertype(left, right.partial_fallback.type) + if len(mapped.args) == 1 and isinstance(mapped.args[0], UnpackType): + unpacked = get_proper_type(mapped.args[0].type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + if isinstance(get_proper_type(unpacked.args[0]), AnyType): + return not self.proper_subtype + # TODO: we need a special case similar to above to consider (something that maps to) + # tuple[Any, ...] a subtype of Tuple[]. + return False + if isinstance(right, TypeVarTupleType): + # tuple[Any, ...] is like Any in the world of tuples (see special case above). + if left.type.has_base("builtins.tuple"): + mapped = map_instance_to_supertype(left, right.tuple_fallback.type) + if isinstance(get_proper_type(mapped.args[0]), AnyType): + return not self.proper_subtype if isinstance(right, Instance): if type_state.is_cached_subtype_check(self._subtype_kind, left, right): return True @@ -476,106 +511,37 @@ def visit_instance(self, left: Instance) -> bool: t = erased nominal = True if right.type.has_type_var_tuple_type: - assert left.type.type_var_tuple_prefix is not None - assert left.type.type_var_tuple_suffix is not None + # For variadic instances we simply find the correct type argument mappings, + # all the heavy lifting is done by the tuple subtyping. assert right.type.type_var_tuple_prefix is not None assert right.type.type_var_tuple_suffix is not None - split_result = fully_split_with_mapped_and_template( - left.args, - left.type.type_var_tuple_prefix, - left.type.type_var_tuple_suffix, - right.args, - right.type.type_var_tuple_prefix, - right.type.type_var_tuple_suffix, + prefix = right.type.type_var_tuple_prefix + suffix = right.type.type_var_tuple_suffix + tvt = right.type.defn.type_vars[prefix] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + left_prefix, left_middle, left_suffix = split_with_prefix_and_suffix( + t.args, prefix, suffix ) - if split_result is None: - return False - - ( - left_prefix, - left_mprefix, - left_middle, - left_msuffix, - left_suffix, - right_prefix, - right_mprefix, - right_middle, - right_msuffix, - right_suffix, - ) = split_result - - left_unpacked = extract_unpack(left_middle) - right_unpacked = extract_unpack(right_middle) - - # Helper for case 2 below so we can treat them the same. - def check_mixed( - unpacked_type: ProperType, compare_to: tuple[Type, ...] - ) -> bool: - if ( - isinstance(unpacked_type, Instance) - and unpacked_type.type.fullname == "builtins.tuple" - ): - return all(is_equivalent(l, unpacked_type.args[0]) for l in compare_to) - if isinstance(unpacked_type, TypeVarTupleType): - return False - if isinstance(unpacked_type, AnyType): - return True - if isinstance(unpacked_type, TupleType): - if len(unpacked_type.items) != len(compare_to): - return False - for t1, t2 in zip(unpacked_type.items, compare_to): - if not is_equivalent(t1, t2): - return False - return True - return False - - # Case 1: Both are unpacks, in this case we check what is being - # unpacked is the same. - if left_unpacked is not None and right_unpacked is not None: - if not is_equivalent(left_unpacked, right_unpacked): - return False - - # Case 2: Only one of the types is an unpack. The equivalence - # case is mostly the same but we check some additional - # things when unpacking on the right. - elif left_unpacked is not None and right_unpacked is None: - if not check_mixed(left_unpacked, right_middle): - return False - elif left_unpacked is None and right_unpacked is not None: - if not check_mixed(right_unpacked, left_middle): - return False - - # Case 3: Neither type is an unpack. In this case we just compare - # the items themselves. - else: - if len(left_middle) != len(right_middle): - return False - for left_t, right_t in zip(left_middle, right_middle): - if not is_equivalent(left_t, right_t): - return False - - assert len(left_mprefix) == len(right_mprefix) - assert len(left_msuffix) == len(right_msuffix) - - for left_item, right_item in zip( - left_mprefix + left_msuffix, right_mprefix + right_msuffix - ): - if not is_equivalent(left_item, right_item): - return False - - left_items = t.args[: right.type.type_var_tuple_prefix] - right_items = right.args[: right.type.type_var_tuple_prefix] - if right.type.type_var_tuple_suffix: - left_items += t.args[-right.type.type_var_tuple_suffix :] - right_items += right.args[-right.type.type_var_tuple_suffix :] - unpack_index = right.type.type_var_tuple_prefix - assert unpack_index is not None - type_params = zip( - left_prefix + left_suffix, - right_prefix + right_suffix, - right.type.defn.type_vars[:unpack_index] - + right.type.defn.type_vars[unpack_index + 1 :], + right_prefix, right_middle, right_suffix = split_with_prefix_and_suffix( + right.args, prefix, suffix + ) + left_args = ( + left_prefix + (TupleType(list(left_middle), fallback),) + left_suffix ) + right_args = ( + right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix + ) + if len(t.args) == 1 and isinstance(t.args[0], UnpackType): + unpacked = get_proper_type(t.args[0].type) + if isinstance(unpacked, Instance): + assert unpacked.type.fullname == "builtins.tuple" + if ( + isinstance(get_proper_type(unpacked.args[0]), AnyType) + and not self.proper_subtype + ): + return True + type_params = zip(left_args, right_args, right.type.defn.type_vars) else: type_params = zip(t.args, right.args, right.type.defn.type_vars) if not self.subtype_context.ignore_type_params: @@ -761,8 +727,12 @@ def visit_tuple_type(self, left: TupleType) -> bool: return True return False elif isinstance(right, TupleType): + # If right has a variadic unpack this needs special handling. If there is a TypeVarTuple + # unpack, item count must coincide. If the left has variadic unpack but right + # doesn't have one, we will fall through to False down the line. + if self.variadic_tuple_subtype(left, right): + return True if len(left.items) != len(right.items): - # TODO: handle tuple with variadic items better. return False if any(not self._is_subtype(l, r) for l, r in zip(left.items, right.items)): return False @@ -778,6 +748,79 @@ def visit_tuple_type(self, left: TupleType) -> bool: else: return False + def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: + """Check subtyping between two potentially variadic tuples. + + Most non-trivial cases here are due to variadic unpacks like *tuple[X, ...], + we handle such unpacks as infinite unions Tuple[()] | Tuple[X] | Tuple[X, X] | ... + + Note: the cases where right is fixed or has *Ts unpack should be handled + by the caller. + """ + right_unpack_index = find_unpack_in_list(right.items) + if right_unpack_index is None: + # This case should be handled by the caller. + return False + right_unpack = right.items[right_unpack_index] + assert isinstance(right_unpack, UnpackType) + right_unpacked = get_proper_type(right_unpack.type) + if not isinstance(right_unpacked, Instance): + # This case should be handled by the caller. + return False + assert right_unpacked.type.fullname == "builtins.tuple" + right_item = right_unpacked.args[0] + right_prefix = right_unpack_index + right_suffix = len(right.items) - right_prefix - 1 + left_unpack_index = find_unpack_in_list(left.items) + if left_unpack_index is None: + # Simple case: left is fixed, simply find correct mapping to the right + # (effectively selecting item with matching length from an infinite union). + if len(left.items) < right_prefix + right_suffix: + return False + prefix, middle, suffix = split_with_prefix_and_suffix( + tuple(left.items), right_prefix, right_suffix + ) + if not all( + self._is_subtype(li, ri) for li, ri in zip(prefix, right.items[:right_prefix]) + ): + return False + if right_suffix and not all( + self._is_subtype(li, ri) for li, ri in zip(suffix, right.items[-right_suffix:]) + ): + return False + return all(self._is_subtype(li, right_item) for li in middle) + else: + if len(left.items) < len(right.items): + # There are some items on the left that will never have a matching length + # on the right. + return False + left_unpack = left.items[left_unpack_index] + assert isinstance(left_unpack, UnpackType) + left_unpacked = get_proper_type(left_unpack.type) + if not isinstance(left_unpacked, Instance): + # *Ts unpacks can't be split. + return False + assert left_unpacked.type.fullname == "builtins.tuple" + left_item = left_unpacked.args[0] + + # The most tricky case with two variadic unpacks we handle similar to union + # subtyping: *each* item on the left, must be a subtype of *some* item on the right. + # For this we first check the "asymptotic case", i.e. that both unpacks a subtypes, + # and then check subtyping for all finite overlaps. + if not self._is_subtype(left_item, right_item): + return False + left_prefix = left_unpack_index + left_suffix = len(left.items) - left_prefix - 1 + max_overlap = max(0, right_prefix - left_prefix, right_suffix - left_suffix) + for overlap in range(max_overlap + 1): + repr_items = left.items[:left_prefix] + [left_item] * overlap + if left_suffix: + repr_items += left.items[-left_suffix:] + left_repr = left.copy_modified(items=repr_items) + if not self._is_subtype(left_repr, right): + return False + return True + def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, Instance): diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index f40996145cba..5ec292f07056 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -82,40 +82,11 @@ def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None: Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.d), } - def test_unpack_tuple(self) -> None: - fx = self.fx - assert set( - infer_constraints( - Instance( - fx.gvi, - [ - UnpackType( - TupleType([fx.t, fx.s], fallback=Instance(fx.std_tuplei, [fx.o])) - ) - ], - ), - Instance(fx.gvi, [fx.a, fx.b]), - SUPERTYPE_OF, - ) - ) == { - Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.a), - Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.b), - } - def test_unpack_with_prefix_and_suffix(self) -> None: fx = self.fx assert set( infer_constraints( - Instance( - fx.gv2i, - [ - fx.u, - UnpackType( - TupleType([fx.t, fx.s], fallback=Instance(fx.std_tuplei, [fx.o])) - ), - fx.u, - ], - ), + Instance(fx.gv2i, [fx.u, fx.t, fx.s, fx.u]), Instance(fx.gv2i, [fx.a, fx.b, fx.c, fx.d]), SUPERTYPE_OF, ) @@ -130,16 +101,7 @@ def test_unpack_tuple_length_non_match(self) -> None: fx = self.fx assert set( infer_constraints( - Instance( - fx.gv2i, - [ - fx.u, - UnpackType( - TupleType([fx.t, fx.s], fallback=Instance(fx.std_tuplei, [fx.o])) - ), - fx.u, - ], - ), + Instance(fx.gv2i, [fx.u, fx.t, fx.s, fx.u]), Instance(fx.gv2i, [fx.a, fx.b, fx.d]), SUPERTYPE_OF, ) diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 464f64d2b846..480fe38a90a7 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -4,7 +4,7 @@ from mypy.subtypes import is_subtype from mypy.test.helpers import Suite from mypy.test.typefixture import InterfaceTypeFixture, TypeFixture -from mypy.types import Instance, TupleType, Type, UnpackType +from mypy.types import Instance, Type, UnpackType class SubtypingSuite(Suite): @@ -221,10 +221,6 @@ def test_type_var_tuple(self) -> None: Instance(self.fx.gvi, [UnpackType(self.fx.us)]), ) - self.assert_subtype( - Instance(self.fx.gvi, [UnpackType(self.fx.anyt)]), - Instance(self.fx.gvi, [self.fx.anyt]), - ) self.assert_not_subtype( Instance(self.fx.gvi, [UnpackType(self.fx.ss)]), Instance(self.fx.gvi, []) ) @@ -272,83 +268,8 @@ def test_type_var_tuple_with_prefix_suffix(self) -> None: Instance(self.fx.gvi, [self.fx.a, UnpackType(self.fx.ss), self.fx.b, self.fx.c]), ) - def test_type_var_tuple_unpacked_varlength_tuple(self) -> None: - self.assert_subtype( - Instance( - self.fx.gvi, - [ - UnpackType( - TupleType( - [self.fx.a, self.fx.b], - fallback=Instance(self.fx.std_tuplei, [self.fx.o]), - ) - ) - ], - ), - Instance(self.fx.gvi, [self.fx.a, self.fx.b]), - ) - - def test_type_var_tuple_unpacked_tuple(self) -> None: - self.assert_subtype( - Instance( - self.fx.gvi, - [ - UnpackType( - TupleType( - [self.fx.a, self.fx.b], - fallback=Instance(self.fx.std_tuplei, [self.fx.o]), - ) - ) - ], - ), - Instance(self.fx.gvi, [self.fx.a, self.fx.b]), - ) - self.assert_subtype( - Instance( - self.fx.gvi, - [ - UnpackType( - TupleType( - [self.fx.a, self.fx.b], - fallback=Instance(self.fx.std_tuplei, [self.fx.o]), - ) - ) - ], - ), - Instance(self.fx.gvi, [self.fx.anyt, self.fx.anyt]), - ) - self.assert_not_subtype( - Instance( - self.fx.gvi, - [ - UnpackType( - TupleType( - [self.fx.a, self.fx.b], - fallback=Instance(self.fx.std_tuplei, [self.fx.o]), - ) - ) - ], - ), - Instance(self.fx.gvi, [self.fx.a]), - ) - self.assert_not_subtype( - Instance( - self.fx.gvi, - [ - UnpackType( - TupleType( - [self.fx.a, self.fx.b], - fallback=Instance(self.fx.std_tuplei, [self.fx.o]), - ) - ) - ], - ), - # Order flipped here. - Instance(self.fx.gvi, [self.fx.b, self.fx.a]), - ) - def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None: - self.assert_equivalent( + self.assert_subtype( Instance(self.fx.gvi, [self.fx.a, self.fx.a]), Instance(self.fx.gvi, [UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))]), ) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 59457dfa5d3b..e8dd623bec53 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -47,6 +47,7 @@ UnboundType, UninhabitedType, UnionType, + UnpackType, get_proper_type, has_recursive_types, ) @@ -986,6 +987,54 @@ def test_literal_type(self) -> None: UnionType([lit2, lit3]), UnionType([lit1, lit2]), UnionType([lit2, lit3, lit1]) ) + def test_variadic_tuple_joins(self) -> None: + # These tests really test just the "arity", to be sure it is handled correctly. + self.assert_join( + self.tuple(self.fx.a, self.fx.a), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + ) + self.assert_join( + self.tuple(self.fx.a, self.fx.a), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + ) + self.assert_join( + self.tuple(self.fx.a, self.fx.a), + self.tuple(self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + ) + self.assert_join( + self.tuple( + self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a + ), + self.tuple( + self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a + ), + self.tuple( + self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a + ), + ) + self.assert_join( + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple( + self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a + ), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + ) + self.assert_join( + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + ) + self.assert_join( + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + self.tuple( + self.fx.b, UnpackType(Instance(self.fx.std_tuplei, [self.fx.b])), self.fx.b + ), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + ) + # There are additional test cases in check-inference.test. # TODO: Function types + varargs and default args. @@ -1221,6 +1270,34 @@ def assert_meet_uninhabited(self, s: Type, t: Type) -> None: with state.strict_optional_set(True): self.assert_meet(s, t, self.fx.uninhabited) + def test_variadic_tuple_meets(self) -> None: + # These tests really test just the "arity", to be sure it is handled correctly. + self.assert_meet( + self.tuple(self.fx.a, self.fx.a), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(self.fx.a, self.fx.a), + ) + self.assert_meet( + self.tuple(self.fx.a, self.fx.a), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + self.tuple(self.fx.a, self.fx.a), + ) + self.assert_meet( + self.tuple(self.fx.a, self.fx.a), + self.tuple(self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(self.fx.a, self.fx.a), + ) + self.assert_meet( + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + ) + self.assert_meet( + self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), + self.tuple(self.fx.b, UnpackType(Instance(self.fx.std_tuplei, [self.fx.b]))), + self.tuple(self.fx.b, UnpackType(Instance(self.fx.std_tuplei, [self.fx.b]))), + ) + def assert_meet(self, s: Type, t: Type, meet: Type) -> None: self.assert_simple_meet(s, t, meet) self.assert_simple_meet(t, s, meet) diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index 81af765f8585..b7bde16e6be2 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -233,9 +233,10 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy AnyType(TypeOfAny.from_omitted_generics), ) - self.ts = make_type_var_tuple("Ts", 1, self.o) # Ts`1 (type var tuple) - self.ss = make_type_var_tuple("Ss", 2, self.o) # Ss`2 (type var tuple) - self.us = make_type_var_tuple("Us", 3, self.o) # Us`3 (type var tuple) + obj_tuple = self.std_tuple.copy_modified(args=[self.o]) + self.ts = make_type_var_tuple("Ts", 1, obj_tuple) # Ts`1 (type var tuple) + self.ss = make_type_var_tuple("Ss", 2, obj_tuple) # Ss`2 (type var tuple) + self.us = make_type_var_tuple("Us", 3, obj_tuple) # Us`3 (type var tuple) self.gvi = self.make_type_info("GV", mro=[self.oi], typevars=["Ts"], typevar_tuple_index=0) self.gv2i = self.make_type_info( @@ -325,8 +326,8 @@ def make_type_info( n, n, id, - self.o, - self.std_tuple, + self.std_tuple.copy_modified(args=[self.o]), + self.std_tuple.copy_modified(args=[self.o]), AnyType(TypeOfAny.from_omitted_generics), ) ) diff --git a/mypy/typeops.py b/mypy/typeops.py index 3efa3cc3e965..3f50232f04c1 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -104,8 +104,8 @@ def tuple_fallback(typ: TupleType) -> Instance: if isinstance(item, UnpackType): unpacked_type = get_proper_type(item.type) if isinstance(unpacked_type, TypeVarTupleType): - items.append(unpacked_type.upper_bound) - elif ( + unpacked_type = get_proper_type(unpacked_type.upper_bound) + if ( isinstance(unpacked_type, Instance) and unpacked_type.type.fullname == "builtins.tuple" ): @@ -654,8 +654,7 @@ def erase_def_to_union_or_bound(tdef: TypeVarLikeType) -> Type: # TODO(PEP612): fix for ParamSpecType if isinstance(tdef, ParamSpecType): return AnyType(TypeOfAny.from_error) - assert isinstance(tdef, TypeVarType) - if tdef.values: + if isinstance(tdef, TypeVarType) and tdef.values: return make_simplified_union(tdef.values) else: return tdef.upper_bound diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index bcb5e96b615c..af2effbd4035 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -9,7 +9,6 @@ ProperType, Type, UnpackType, - find_unpack_in_list, get_proper_type, split_with_prefix_and_suffix, ) @@ -25,139 +24,6 @@ def split_with_instance( ) -def split_with_mapped_and_template( - mapped: tuple[Type, ...], - mapped_prefix_len: int | None, - mapped_suffix_len: int | None, - template: tuple[Type, ...], - template_prefix_len: int, - template_suffix_len: int, -) -> ( - tuple[ - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - ] - | None -): - split_result = fully_split_with_mapped_and_template( - mapped, - mapped_prefix_len, - mapped_suffix_len, - template, - template_prefix_len, - template_suffix_len, - ) - if split_result is None: - return None - - ( - mapped_prefix, - mapped_middle_prefix, - mapped_middle_middle, - mapped_middle_suffix, - mapped_suffix, - template_prefix, - template_middle_prefix, - template_middle_middle, - template_middle_suffix, - template_suffix, - ) = split_result - - return ( - mapped_prefix + mapped_middle_prefix, - mapped_middle_middle, - mapped_middle_suffix + mapped_suffix, - template_prefix + template_middle_prefix, - template_middle_middle, - template_middle_suffix + template_suffix, - ) - - -def fully_split_with_mapped_and_template( - mapped: tuple[Type, ...], - mapped_prefix_len: int | None, - mapped_suffix_len: int | None, - template: tuple[Type, ...], - template_prefix_len: int, - template_suffix_len: int, -) -> ( - tuple[ - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - tuple[Type, ...], - ] - | None -): - if mapped_prefix_len is not None: - assert mapped_suffix_len is not None - mapped_prefix, mapped_middle, mapped_suffix = split_with_prefix_and_suffix( - tuple(mapped), mapped_prefix_len, mapped_suffix_len - ) - else: - mapped_prefix = tuple() - mapped_suffix = tuple() - mapped_middle = mapped - - template_prefix, template_middle, template_suffix = split_with_prefix_and_suffix( - tuple(template), template_prefix_len, template_suffix_len - ) - - unpack_prefix = find_unpack_in_list(template_middle) - if unpack_prefix is None: - return ( - mapped_prefix, - (), - mapped_middle, - (), - mapped_suffix, - template_prefix, - (), - template_middle, - (), - template_suffix, - ) - - unpack_suffix = len(template_middle) - unpack_prefix - 1 - # mapped_middle is too short to do the unpack - if unpack_prefix + unpack_suffix > len(mapped_middle): - return None - - ( - mapped_middle_prefix, - mapped_middle_middle, - mapped_middle_suffix, - ) = split_with_prefix_and_suffix(mapped_middle, unpack_prefix, unpack_suffix) - ( - template_middle_prefix, - template_middle_middle, - template_middle_suffix, - ) = split_with_prefix_and_suffix(template_middle, unpack_prefix, unpack_suffix) - - return ( - mapped_prefix, - mapped_middle_prefix, - mapped_middle_middle, - mapped_middle_suffix, - mapped_suffix, - template_prefix, - template_middle_prefix, - template_middle_middle, - template_middle_suffix, - template_suffix, - ) - - def extract_unpack(types: Sequence[Type]) -> ProperType | None: """Given a list of types, extracts either a single type from an unpack, or returns None.""" if len(types) == 1: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index b4cd21aa552c..06f87a26e7a1 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6460,3 +6460,22 @@ P = ParamSpec("P") class C(Generic[P]): def __init__(self, fn: Callable[P, int]) -> None: ... [builtins fixtures/dict.pyi] + +[case testVariadicTupleIncrementalUpdateNoCrash] +import m +[file m.py] +from typing import Any +from lib import C + +x: C[Any] +[file m.py.2] +from lib import C + +x: C[int] +[file lib.py] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Tuple[Unpack[Ts]]): ... +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 2b47ff30cdfb..d38d492fe9b2 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1221,7 +1221,7 @@ def foo(x: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: [case testTypeVarTupleWithIsInstance] # flags: --warn-unreachable -from typing import Tuple +from typing import Generic, Tuple from typing_extensions import TypeVarTuple, Unpack TP = TypeVarTuple("TP") @@ -1232,4 +1232,287 @@ def test(d: A[int, str]) -> None: reveal_type(d) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" else: reveal_type(d) # E: Statement is unreachable + +class B(Generic[Unpack[TP]]): ... + +def test2(d: B[int, str]) -> None: + if isinstance(d, B): + reveal_type(d) # N: Revealed type is "__main__.B[builtins.int, builtins.str]" + else: + reveal_type(d) # E: Statement is unreachable [builtins fixtures/isinstancelist.pyi] + +[case testVariadicTupleSubtyping] +from typing import Tuple +from typing_extensions import Unpack + +def f1(x: Tuple[float, ...]) -> None: ... +def f2(x: Tuple[float, Unpack[Tuple[float, ...]]]) -> None: ... +def f3(x: Tuple[Unpack[Tuple[float, ...]], float]) -> None: ... +def f4(x: Tuple[float, Unpack[Tuple[float, ...]], float]) -> None: ... + +t1: Tuple[int, int] +t2: Tuple[int, Unpack[Tuple[int, ...]]] +t3: Tuple[Unpack[Tuple[int, ...]], int] +t4: Tuple[int, Unpack[Tuple[int, ...]], int] +t5: Tuple[int, ...] + +tl: Tuple[int, int, Unpack[Tuple[int, ...]]] +tr: Tuple[Unpack[Tuple[int, ...]], int, int] + +f1(t1) +f1(t2) +f1(t3) +f1(t4) +f1(t5) + +f1(tl) +f1(tr) + +f2(t1) +f2(t2) +f2(t3) +f2(t4) +f2(t5) # E: Argument 1 to "f2" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]]]" + +f2(tl) +f2(tr) + +f3(t1) +f3(t2) +f3(t3) +f3(t4) +f3(t5) # E: Argument 1 to "f3" has incompatible type "Tuple[int, ...]"; expected "Tuple[Unpack[Tuple[float, ...]], float]" + +f3(tl) +f3(tr) + +f4(t1) +f4(t2) # E: Argument 1 to "f4" has incompatible type "Tuple[int, Unpack[Tuple[int, ...]]]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" +f4(t3) # E: Argument 1 to "f4" has incompatible type "Tuple[Unpack[Tuple[int, ...]], int]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" +f4(t4) +f4(t5) # E: Argument 1 to "f4" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" + +f4(tl) +f4(tr) + +t5_verbose: Tuple[Unpack[Tuple[int, ...]]] +t5 = t5_verbose # OK +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleInference] +from typing import List, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +def f(x: Tuple[int, Unpack[Tuple[T, ...]]]) -> T: ... + +vt0: Tuple[int, ...] +f(vt0) # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]" + +vt1: Tuple[Unpack[Tuple[int, ...]], int] +reveal_type(f(vt1)) # N: Revealed type is "builtins.int" + +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +def g(x: Tuple[T, Unpack[Ts], S]) -> Tuple[T, Unpack[Ts], S]: ... +g(vt0) # E: Argument 1 to "g" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]], int]" + +U = TypeVar("U") +def h(x: List[Tuple[T, S, U]]) -> Tuple[T, S, U]: ... +vt2: Tuple[Unpack[Tuple[int, ...]], int] +vt2 = h(reveal_type([])) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, builtins.int]]" +[builtins fixtures/tuple.pyi] + +[case testVariadicSelfTypeErasure] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class Array(Generic[Unpack[Ts]]): + def _close(self) -> None: ... + + def close(self) -> None: + self._close() +[builtins fixtures/tuple.pyi] + +[case testVariadicSubclassFixed] +from typing import Generic, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): ... +class C(B[int, str]): ... +class D(B[Unpack[Tuple[int, ...]]]): ... + +def fii(x: B[int, int]) -> None: ... +def fis(x: B[int, str]) -> None: ... +def fiv(x: B[Unpack[Tuple[int, ...]]]) -> None: ... + +fii(C()) # E: Argument 1 to "fii" has incompatible type "C"; expected "B[int, int]" +fii(D()) # E: Argument 1 to "fii" has incompatible type "D"; expected "B[int, int]" +fis(C()) +fis(D()) # E: Argument 1 to "fis" has incompatible type "D"; expected "B[int, str]" +fiv(C()) # E: Argument 1 to "fiv" has incompatible type "C"; expected "B[Unpack[Tuple[int, ...]]]" +fiv(D()) +[builtins fixtures/tuple.pyi] + +[case testVariadicSubclassSame] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): ... +class C(B[Unpack[Ts]]): ... + +def fii(x: B[int, int]) -> None: ... +def fis(x: B[int, str]) -> None: ... +def fiv(x: B[Unpack[Tuple[int, ...]]]) -> None: ... + +cii: C[int, int] +cis: C[int, str] +civ: C[Unpack[Tuple[int, ...]]] + +fii(cii) +fii(cis) # E: Argument 1 to "fii" has incompatible type "C[int, str]"; expected "B[int, int]" +fii(civ) # E: Argument 1 to "fii" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int]" + +fis(cii) # E: Argument 1 to "fis" has incompatible type "C[int, int]"; expected "B[int, str]" +fis(cis) +fis(civ) # E: Argument 1 to "fis" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, str]" + +fiv(cii) +fiv(cis) # E: Argument 1 to "fiv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]" +fiv(civ) +[builtins fixtures/tuple.pyi] + +[case testVariadicSubclassExtra] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): ... + +T = TypeVar("T") +class C(B[int, Unpack[Ts], T]): ... + +def ff(x: B[int, int, int]) -> None: ... +def fv(x: B[Unpack[Tuple[int, ...]]]) -> None: ... + +cii: C[int, int] +cis: C[int, str] +civ: C[Unpack[Tuple[int, ...]]] + +ff(cii) +ff(cis) # E: Argument 1 to "ff" has incompatible type "C[int, str]"; expected "B[int, int, int]" +ff(civ) # E: Argument 1 to "ff" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int, int]" + +fv(cii) +fv(cis) # E: Argument 1 to "fv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]" +fv(civ) +[builtins fixtures/tuple.pyi] + +[case testVariadicSubclassVariadic] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): ... +T = TypeVar("T") +class C(B[Unpack[Tuple[T, ...]]]): ... + +def ff(x: B[int, int]) -> None: ... +def fv(x: B[Unpack[Tuple[int, ...]]]) -> None: ... + +ci: C[int] +ff(ci) # E: Argument 1 to "ff" has incompatible type "C[int]"; expected "B[int, int]" +fv(ci) +[builtins fixtures/tuple.pyi] + +[case testVariadicSubclassMethodAccess] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): + def meth(self) -> Tuple[Unpack[Ts]]: ... + +class C1(B[int, str]): ... +class C2(B[Unpack[Ts]]): ... +T = TypeVar("T") +class C3(B[int, Unpack[Ts], T]): ... +class C4(B[Unpack[Tuple[T, ...]]]): ... + +c1: C1 +reveal_type(c1.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + +c2f: C2[int, str] +c2v: C2[Unpack[Tuple[int, ...]]] +reveal_type(c2f.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(c2v.meth()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" + +c3f: C3[int, str] +c3v: C3[Unpack[Tuple[int, ...]]] +reveal_type(c3f.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]" +reveal_type(c3v.meth()) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" + +c4: C4[int] +reveal_type(c4.meth()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleAnySubtype] +from typing import Any, Generic, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): ... +class C1(B[Unpack[Tuple[Any, ...]]]): ... +c1 = C1() +class C2(B): ... +c2 = C2() +x: B[int, str] +x = c1 +x = c2 +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleAnySubtypeTupleType] +from typing import Any, Generic, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Tuple[Unpack[Ts]]): ... +class C1(B[Unpack[Tuple[Any, ...]]]): ... +c1 = C1() +class C2(B): ... +c2 = C2() +x: B[int, str] +x = c1 +x = c2 +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleAnyOverload] +from typing import Any, Generic, overload, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class Array(Generic[Unpack[Ts]]): ... + +class A: + @overload + def f(self, x: Tuple[Unpack[Ts]]) -> Array[Unpack[Ts]]: ... + @overload + def f(self, x: Any) -> Any: ... + def f(self, x: Any) -> Any: + ... +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleInferAgainstAny] +from typing import Any, Tuple, TypeVar +from typing_extensions import Unpack + +T = TypeVar("T") + +def test(x: int, t: Tuple[T, ...]) -> Tuple[int, Unpack[Tuple[T, ...]]]: + ... +a: Any = test(42, ()) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 71a5c6dd87b5..5e05d099b958 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1559,8 +1559,8 @@ MypyFile:1( ImportFrom:1(typing_extensions, [TypeVarTuple]) AssignmentStmt:2( NameExpr(TV* [__main__.TV]) - TypeVarTupleExpr:2())) - + TypeVarTupleExpr:2( + UpperBound(builtins.tuple[builtins.object, ...])))) [builtins fixtures/tuple.pyi] [case testTypeVarTupleCallable] @@ -1576,7 +1576,8 @@ MypyFile:1( ImportFrom:2(typing, [Callable]) AssignmentStmt:3( NameExpr(Ts* [__main__.Ts]) - TypeVarTupleExpr:3()) + TypeVarTupleExpr:3( + UpperBound(builtins.tuple[builtins.object, ...]))) FuncDef:5( foo Args( From f41e24c8b31a110c2f01a753acba458977e41bfc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 14 Sep 2023 10:42:34 +0100 Subject: [PATCH 0263/1617] Lenient handling of trivial Callable suffixes (#15913) Fixes https://github.com/python/mypy/issues/15734 Fixes https://github.com/python/mypy/issues/15188 Fixes https://github.com/python/mypy/issues/14321 Fixes https://github.com/python/mypy/issues/13107 (plain Callable was already working, this fixes the protocol example) Fixes https://github.com/python/mypy/issues/16058 It looks like treating trivial suffixes (especially for erased callables) as "whatever works" is a right thing, because it reflects the whole idea of why we normally check subtyping with respect to an e.g. erased type. As you can see this fixes a bunch of issues. Note it was necessary to make couple more tweaks to make everything work smoothly: * Adjust self-type erasure level in `checker.py` to match other places. * Explicitly allow `Callable` as a `self`/`cls` annotation (actually I am not sure we need to keep this check at all, since we now have good inference for self-types, and we check they are safe either at definition site or at call site). --- mypy/checker.py | 4 +- mypy/checkmember.py | 2 + mypy/messages.py | 3 + mypy/subtypes.py | 19 ++- mypy/typeops.py | 4 + test-data/unit/check-callable.test | 31 ++++ test-data/unit/check-modules.test | 12 +- .../unit/check-parameter-specification.test | 139 +++++++++++++++++- test-data/unit/fixtures/paramspec.pyi | 1 + 9 files changed, 204 insertions(+), 11 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5a74f019dcf4..95a65b0a8cd1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1208,7 +1208,9 @@ def check_func_def( ): if defn.is_class or defn.name == "__new__": ref_type = mypy.types.TypeType.make_normalized(ref_type) - erased = get_proper_type(erase_to_bound(arg_type)) + # This level of erasure matches the one in checkmember.check_self_arg(), + # better keep these two checks consistent. + erased = get_proper_type(erase_typevars(erase_to_bound(arg_type))) if not is_subtype(ref_type, erased, ignore_type_params=True): if ( isinstance(erased, Instance) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 60430839ff62..59af0d402e14 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -896,6 +896,8 @@ def f(self: S) -> T: ... return functype else: selfarg = get_proper_type(item.arg_types[0]) + # This level of erasure matches the one in checker.check_func_def(), + # better keep these two checks consistent. if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))): new_items.append(item) elif isinstance(selfarg, ParamSpecType): diff --git a/mypy/messages.py b/mypy/messages.py index b6fdaf06a8e0..8bc190b7d66d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2132,6 +2132,9 @@ def report_protocol_problems( not is_subtype(subtype, erase_type(supertype), options=self.options) or not subtype.type.defn.type_vars or not supertype.type.defn.type_vars + # Always show detailed message for ParamSpec + or subtype.type.has_param_spec_type + or supertype.type.has_param_spec_type ): type_name = format_type(subtype, self.options, module_names=True) self.note(f"Following member(s) of {type_name} have conflicts:", context, code=code) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index fdde1c24670e..e8339a8c4d69 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1519,6 +1519,18 @@ def are_trivial_parameters(param: Parameters | NormalizedCallableType) -> bool: ) +def is_trivial_suffix(param: Parameters | NormalizedCallableType) -> bool: + param_star = param.var_arg() + param_star2 = param.kw_arg() + return ( + param.arg_kinds[-2:] == [ARG_STAR, ARG_STAR2] + and param_star is not None + and isinstance(get_proper_type(param_star.typ), AnyType) + and param_star2 is not None + and isinstance(get_proper_type(param_star2.typ), AnyType) + ) + + def are_parameters_compatible( left: Parameters | NormalizedCallableType, right: Parameters | NormalizedCallableType, @@ -1540,6 +1552,7 @@ def are_parameters_compatible( # Treat "def _(*a: Any, **kw: Any) -> X" similarly to "Callable[..., X]" if are_trivial_parameters(right): return True + trivial_suffix = is_trivial_suffix(right) # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must @@ -1570,7 +1583,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N if right_arg is None: return False if left_arg is None: - return not allow_partial_overlap + return not allow_partial_overlap and not trivial_suffix return not is_compat(right_arg.typ, left_arg.typ) if _incompatible(left_star, right_star) or _incompatible(left_star2, right_star2): @@ -1594,7 +1607,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # arguments. Get all further positional args of left, and make sure # they're more general than the corresponding member in right. # TODO: are we handling UnpackType correctly here? - if right_star is not None: + if right_star is not None and not trivial_suffix: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) assert right_by_position is not None @@ -1621,7 +1634,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1d: Check kw args. Right has an infinite series of optional named # arguments. Get all further named args of left, and make sure # they're more general than the corresponding member in right. - if right_star2 is not None: + if right_star2 is not None and not trivial_suffix: right_names = {name for name in right.arg_names if name is not None} left_only_names = set() for name, kind in zip(left.arg_names, left.arg_kinds): diff --git a/mypy/typeops.py b/mypy/typeops.py index 3f50232f04c1..10efa32c4b91 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -251,6 +251,10 @@ def supported_self_type(typ: ProperType) -> bool: """ if isinstance(typ, TypeType): return supported_self_type(typ.item) + if isinstance(typ, CallableType): + # Special case: allow class callable instead of Type[...] as cls annotation, + # as well as callable self for callback protocols. + return True return isinstance(typ, TypeVarType) or ( isinstance(typ, Instance) and typ != fill_typevars(typ.type) ) diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test index 07c42de74bb3..8a611a689be5 100644 --- a/test-data/unit/check-callable.test +++ b/test-data/unit/check-callable.test @@ -598,3 +598,34 @@ a: A a() # E: Missing positional argument "other" in call to "__call__" of "A" a(a) a(lambda: None) + +[case testCallableSubtypingTrivialSuffix] +from typing import Any, Protocol + +class Call(Protocol): + def __call__(self, x: int, *args: Any, **kwargs: Any) -> None: ... + +def f1() -> None: ... +a1: Call = f1 # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "Call") \ + # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" +def f2(x: str) -> None: ... +a2: Call = f2 # E: Incompatible types in assignment (expression has type "Callable[[str], None]", variable has type "Call") \ + # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" +def f3(y: int) -> None: ... +a3: Call = f3 # E: Incompatible types in assignment (expression has type "Callable[[int], None]", variable has type "Call") \ + # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" +def f4(x: int) -> None: ... +a4: Call = f4 + +def f5(x: int, y: int) -> None: ... +a5: Call = f5 + +def f6(x: int, y: int = 0) -> None: ... +a6: Call = f6 + +def f7(x: int, *, y: int) -> None: ... +a7: Call = f7 + +def f8(x: int, *args: int, **kwargs: str) -> None: ... +a8: Call = f8 +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 3da5996ed274..94368f6c1113 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3193,7 +3193,7 @@ from test1 import aaaa # E: Module "test1" has no attribute "aaaa" import b [file a.py] class Foo: - def frobnicate(self, x, *args, **kwargs): pass + def frobnicate(self, x: str, *args, **kwargs): pass [file b.py] from a import Foo class Bar(Foo): @@ -3201,21 +3201,21 @@ class Bar(Foo): [file b.py.2] from a import Foo class Bar(Foo): - def frobnicate(self, *args) -> None: pass + def frobnicate(self, *args: int) -> None: pass [file b.py.3] from a import Foo class Bar(Foo): - def frobnicate(self, *args) -> None: pass # type: ignore[override] # I know + def frobnicate(self, *args: int) -> None: pass # type: ignore[override] # I know [builtins fixtures/dict.pyi] [out1] tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" tmp/b.py:3: note: Superclass: -tmp/b.py:3: note: def frobnicate(self, x: Any, *args: Any, **kwargs: Any) -> Any +tmp/b.py:3: note: def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any tmp/b.py:3: note: Subclass: tmp/b.py:3: note: def frobnicate(self) -> None [out2] tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" tmp/b.py:3: note: Superclass: -tmp/b.py:3: note: def frobnicate(self, x: Any, *args: Any, **kwargs: Any) -> Any +tmp/b.py:3: note: def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any tmp/b.py:3: note: Subclass: -tmp/b.py:3: note: def frobnicate(self, *args: Any) -> None +tmp/b.py:3: note: def frobnicate(self, *args: int) -> None diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index d80069644194..da831d29dd43 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1729,7 +1729,12 @@ class A(Protocol[P]): ... def bar(b: A[P]) -> A[Concatenate[int, P]]: - return b # E: Incompatible return value type (got "A[P]", expected "A[[int, **P]]") + return b # E: Incompatible return value type (got "A[P]", expected "A[[int, **P]]") \ + # N: Following member(s) of "A[P]" have conflicts: \ + # N: Expected: \ + # N: def foo(self, int, /, *args: P.args, **kwargs: P.kwargs) -> Any \ + # N: Got: \ + # N: def foo(self, *args: P.args, **kwargs: P.kwargs) -> Any [builtins fixtures/paramspec.pyi] [case testParamSpecPrefixSubtypingValidNonStrict] @@ -1825,6 +1830,138 @@ c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed reveal_type(c) # N: Revealed type is "__main__.C[Any]" [builtins fixtures/paramspec.pyi] +[case testParamSpecConcatenateSelfType] +from typing import Callable +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +class A: + def __init__(self, a_param_1: str) -> None: ... + + @classmethod + def add_params(cls: Callable[P, A]) -> Callable[Concatenate[float, P], A]: + def new_constructor(i: float, *args: P.args, **kwargs: P.kwargs) -> A: + return cls(*args, **kwargs) + return new_constructor + + @classmethod + def remove_params(cls: Callable[Concatenate[str, P], A]) -> Callable[P, A]: + def new_constructor(*args: P.args, **kwargs: P.kwargs) -> A: + return cls("my_special_str", *args, **kwargs) + return new_constructor + +reveal_type(A.add_params()) # N: Revealed type is "def (builtins.float, a_param_1: builtins.str) -> __main__.A" +reveal_type(A.remove_params()) # N: Revealed type is "def () -> __main__.A" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecConcatenateCallbackProtocol] +from typing import Protocol, TypeVar +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +R = TypeVar("R", covariant=True) + +class Path: ... + +class Function(Protocol[P, R]): + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: ... + +def file_cache(fn: Function[Concatenate[Path, P], R]) -> Function[P, R]: + def wrapper(*args: P.args, **kw: P.kwargs) -> R: + return fn(Path(), *args, **kw) + return wrapper + +@file_cache +def get_thing(path: Path, *, some_arg: int) -> int: ... +reveal_type(get_thing) # N: Revealed type is "__main__.Function[[*, some_arg: builtins.int], builtins.int]" +get_thing(some_arg=1) # OK +[builtins fixtures/paramspec.pyi] + +[case testParamSpecConcatenateKeywordOnly] +from typing import Callable, TypeVar +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +R = TypeVar("R") + +class Path: ... + +def file_cache(fn: Callable[Concatenate[Path, P], R]) -> Callable[P, R]: + def wrapper(*args: P.args, **kw: P.kwargs) -> R: + return fn(Path(), *args, **kw) + return wrapper + +@file_cache +def get_thing(path: Path, *, some_arg: int) -> int: ... +reveal_type(get_thing) # N: Revealed type is "def (*, some_arg: builtins.int) -> builtins.int" +get_thing(some_arg=1) # OK +[builtins fixtures/paramspec.pyi] + +[case testParamSpecConcatenateCallbackApply] +from typing import Callable, Protocol +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") + +class FuncType(Protocol[P]): + def __call__(self, x: int, s: str, *args: P.args, **kw_args: P.kwargs) -> str: ... + +def forwarder1(fp: FuncType[P], *args: P.args, **kw_args: P.kwargs) -> str: + return fp(0, '', *args, **kw_args) + +def forwarder2(fp: Callable[Concatenate[int, str, P], str], *args: P.args, **kw_args: P.kwargs) -> str: + return fp(0, '', *args, **kw_args) + +def my_f(x: int, s: str, d: bool) -> str: ... +forwarder1(my_f, True) # OK +forwarder2(my_f, True) # OK +forwarder1(my_f, 1.0) # E: Argument 2 to "forwarder1" has incompatible type "float"; expected "bool" +forwarder2(my_f, 1.0) # E: Argument 2 to "forwarder2" has incompatible type "float"; expected "bool" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecCallbackProtocolSelf] +from typing import Callable, Protocol, TypeVar +from typing_extensions import ParamSpec, Concatenate + +Params = ParamSpec("Params") +Result = TypeVar("Result", covariant=True) + +class FancyMethod(Protocol): + def __call__(self, arg1: int, arg2: str) -> bool: ... + def return_me(self: Callable[Params, Result]) -> Callable[Params, Result]: ... + def return_part(self: Callable[Concatenate[int, Params], Result]) -> Callable[Params, Result]: ... + +m: FancyMethod +reveal_type(m.return_me()) # N: Revealed type is "def (arg1: builtins.int, arg2: builtins.str) -> builtins.bool" +reveal_type(m.return_part()) # N: Revealed type is "def (arg2: builtins.str) -> builtins.bool" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecInferenceCallableAgainstAny] +from typing import Callable, TypeVar, Any +from typing_extensions import ParamSpec, Concatenate + +_P = ParamSpec("_P") +_R = TypeVar("_R") + +class A: ... +a = A() + +def a_func( + func: Callable[Concatenate[A, _P], _R], +) -> Callable[Concatenate[Any, _P], _R]: + def wrapper(__a: Any, *args: _P.args, **kwargs: _P.kwargs) -> _R: + return func(a, *args, **kwargs) + return wrapper + +def test(a, *args): ... +x: Any +y: object + +a_func(test) +x = a_func(test) +y = a_func(test) +[builtins fixtures/paramspec.pyi] + [case testParamSpecInferenceWithCallbackProtocol] from typing import Protocol, Callable, ParamSpec diff --git a/test-data/unit/fixtures/paramspec.pyi b/test-data/unit/fixtures/paramspec.pyi index 9b0089f6a7e9..dfb5e126f242 100644 --- a/test-data/unit/fixtures/paramspec.pyi +++ b/test-data/unit/fixtures/paramspec.pyi @@ -16,6 +16,7 @@ class object: class function: ... class ellipsis: ... +class classmethod: ... class type: def __init__(self, *a: object) -> None: ... From 2c2d126cc742f2467045d36780c33bb8fb77a614 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:27:54 -0700 Subject: [PATCH 0264/1617] Fix tuple[Any, ...] subtyping (#16108) Follow up to #16073 and #16076 Fix needed for https://github.com/python/mypy/pull/16053/files#r1316481395 I add test cases that would have caught my previous incorrect PR. I add an explicit case for the new desirable behaviour we see with zip. --- mypy/main.py | 2 +- mypy/subtypes.py | 6 +- test-data/unit/check-tuples.test | 164 +++++++++++++++++++++++++++++-- 3 files changed, 160 insertions(+), 12 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index a4357dca7890..3eb8a76a6de3 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1359,7 +1359,7 @@ def set_strict_flags() -> None: parser.error("Can only find occurrences of class members.") if len(_find_occurrences) != 2: parser.error("Can only find occurrences of non-nested class members.") - state.find_occurrences = _find_occurrences # type: ignore[assignment] + state.find_occurrences = _find_occurrences # Set reports. for flag, val in vars(special_opts).items(): diff --git a/mypy/subtypes.py b/mypy/subtypes.py index e8339a8c4d69..9ed2e4af4051 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -463,8 +463,10 @@ def visit_instance(self, left: Instance) -> bool: assert unpacked.type.fullname == "builtins.tuple" if isinstance(get_proper_type(unpacked.args[0]), AnyType): return not self.proper_subtype - # TODO: we need a special case similar to above to consider (something that maps to) - # tuple[Any, ...] a subtype of Tuple[]. + if mapped.type.fullname == "builtins.tuple" and isinstance( + get_proper_type(mapped.args[0]), AnyType + ): + return not self.proper_subtype return False if isinstance(right, TypeVarTupleType): # tuple[Any, ...] is like Any in the world of tuples (see special case above). diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 0e7c81edc498..391fa20db738 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -107,19 +107,147 @@ class A: pass class B(A): pass [builtins fixtures/tuple.pyi] -[case testSubtypingWithNamedTupleType] -from typing import Tuple -t1: Tuple[A, A] -t2: tuple - -if int(): - t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]") -if int(): - t2 = t1 +[case testSubtypingWithTupleType] +from __future__ import annotations +from typing import Any, Tuple + +tuple_aa: tuple[A, A] +Tuple_aa: Tuple[A, A] + +tuple_obj: tuple[object, ...] +Tuple_obj: Tuple[object, ...] + +tuple_obj_one: tuple[object] +Tuple_obj_one: Tuple[object] + +tuple_obj_two: tuple[object, object] +Tuple_obj_two: Tuple[object, object] + +tuple_any_implicit: tuple +Tuple_any_implicit: Tuple + +tuple_any: tuple[Any, ...] +Tuple_any: Tuple[Any, ...] + +tuple_any_one: tuple[Any] +Tuple_any_one: Tuple[Any] + +tuple_any_two: tuple[Any, Any] +Tuple_any_two: Tuple[Any, Any] + +def takes_tuple_aa(t: tuple[A, A]): ... + +takes_tuple_aa(tuple_aa) +takes_tuple_aa(Tuple_aa) +takes_tuple_aa(tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]" +takes_tuple_aa(Tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]" +takes_tuple_aa(Tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]" +takes_tuple_aa(Tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_any_implicit) +takes_tuple_aa(Tuple_any_implicit) +takes_tuple_aa(tuple_any) +takes_tuple_aa(Tuple_any) +takes_tuple_aa(tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]" +takes_tuple_aa(Tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_any_two) +takes_tuple_aa(Tuple_any_two) + +def takes_tuple_any_implicit(t: tuple): ... + +takes_tuple_any_implicit(tuple_aa) +takes_tuple_any_implicit(Tuple_aa) +takes_tuple_any_implicit(tuple_obj) +takes_tuple_any_implicit(Tuple_obj) +takes_tuple_any_implicit(tuple_obj_one) +takes_tuple_any_implicit(Tuple_obj_one) +takes_tuple_any_implicit(tuple_obj_two) +takes_tuple_any_implicit(Tuple_obj_two) +takes_tuple_any_implicit(tuple_any_implicit) +takes_tuple_any_implicit(Tuple_any_implicit) +takes_tuple_any_implicit(tuple_any) +takes_tuple_any_implicit(Tuple_any) +takes_tuple_any_implicit(tuple_any_one) +takes_tuple_any_implicit(Tuple_any_one) +takes_tuple_any_implicit(tuple_any_two) +takes_tuple_any_implicit(Tuple_any_two) + +def takes_tuple_any_one(t: tuple[Any]): ... + +takes_tuple_any_one(tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]" +takes_tuple_any_one(Tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]" +takes_tuple_any_one(Tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_obj_one) +takes_tuple_any_one(Tuple_obj_one) +takes_tuple_any_one(tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]" +takes_tuple_any_one(Tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_any_implicit) +takes_tuple_any_one(Tuple_any_implicit) +takes_tuple_any_one(tuple_any) +takes_tuple_any_one(Tuple_any) +takes_tuple_any_one(tuple_any_one) +takes_tuple_any_one(Tuple_any_one) +takes_tuple_any_one(tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]" +takes_tuple_any_one(Tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]" class A: pass [builtins fixtures/tuple.pyi] +[case testSubtypingWithTupleTypeSubclass] +from __future__ import annotations +from typing import Any, Tuple + +class A: ... + +inst_tuple_aa: Tuple[A, A] + +class tuple_aa_subclass(Tuple[A, A]): ... +inst_tuple_aa_subclass: tuple_aa_subclass + +class tuple_any_subclass(Tuple[Any, ...]): ... +inst_tuple_any_subclass: tuple_any_subclass + +class tuple_any_one_subclass(Tuple[Any]): ... +inst_tuple_any_one_subclass: tuple_any_one_subclass + +class tuple_any_two_subclass(Tuple[Any, Any]): ... +inst_tuple_any_two_subclass: tuple_any_two_subclass + +class tuple_obj_subclass(Tuple[object, ...]): ... +inst_tuple_obj_subclass: tuple_obj_subclass + +class tuple_obj_one_subclass(Tuple[object]): ... +inst_tuple_obj_one_subclass: tuple_obj_one_subclass + +class tuple_obj_two_subclass(Tuple[object, object]): ... +inst_tuple_obj_two_subclass: tuple_obj_two_subclass + +def takes_tuple_aa(t: Tuple[A, A]): ... + +takes_tuple_aa(inst_tuple_aa) +takes_tuple_aa(inst_tuple_aa_subclass) +takes_tuple_aa(inst_tuple_any_subclass) +takes_tuple_aa(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "Tuple[A, A]" +takes_tuple_aa(inst_tuple_any_two_subclass) +takes_tuple_aa(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "Tuple[A, A]" +takes_tuple_aa(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "Tuple[A, A]" +takes_tuple_aa(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "Tuple[A, A]" + +def takes_tuple_aa_subclass(t: tuple_aa_subclass): ... + +takes_tuple_aa_subclass(inst_tuple_aa) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "Tuple[A, A]"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_aa_subclass) +takes_tuple_aa_subclass(inst_tuple_any_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_subclass"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_one_subclass"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_any_two_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_two_subclass"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_subclass"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_one_subclass"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_two_subclass"; expected "tuple_aa_subclass" + +[builtins fixtures/tuple.pyi] + [case testTupleInitializationWithNone] # flags: --no-strict-optional from typing import Tuple @@ -1522,3 +1650,21 @@ class Bar(aaaaaaaaaa): # E: Name "aaaaaaaaaa" is not defined class FooBarTuple(Tuple[Foo, Bar]): ... [builtins fixtures/tuple.pyi] + + +[case testTupleOverloadZipAny] +from typing import Any, Iterable, Iterator, Tuple, TypeVar, overload + +T = TypeVar("T") + +@overload +def zip(__i: Iterable[T]) -> Iterator[Tuple[T]]: ... +@overload +def zip(*i: Iterable[Any]) -> Iterator[Tuple[Any, ...]]: ... +def zip(i): ... + +def g(t: Tuple): + # Ideally, we'd infer that these are iterators of tuples + reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[Any]" + reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Any]" +[builtins fixtures/tuple.pyi] From d77310ae61e8e784aae46b2011f35900b9392e15 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 15 Sep 2023 00:17:32 -0700 Subject: [PATCH 0265/1617] Better diffs in tests (#16112) It's annoying that one line change causes everything else to show up as a diff. Just use difflib instead. I also highlight the changed lines. We can't use FancyFormatter because it doesn't work well with pytest. --- mypy/test/helpers.py | 128 +++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 7447391593d5..a53e16e27dfa 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -1,6 +1,7 @@ from __future__ import annotations import contextlib +import difflib import os import pathlib import re @@ -43,64 +44,81 @@ def run_mypy(args: list[str]) -> None: pytest.fail(msg="Sample check failed", pytrace=False) -def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) -> None: - """Assert that two string arrays are equal. +def diff_ranges( + left: list[str], right: list[str] +) -> tuple[list[tuple[int, int]], list[tuple[int, int]]]: + seq = difflib.SequenceMatcher(None, left, right) + # note last triple is a dummy, so don't need to worry + blocks = seq.get_matching_blocks() - Display any differences in a human-readable form. - """ - actual = clean_up(actual) - if actual != expected: - num_skip_start = num_skipped_prefix_lines(expected, actual) - num_skip_end = num_skipped_suffix_lines(expected, actual) + i = 0 + j = 0 + left_ranges = [] + right_ranges = [] + for block in blocks: + # mismatched range + left_ranges.append((i, block.a)) + right_ranges.append((j, block.b)) - sys.stderr.write("Expected:\n") + i = block.a + block.size + j = block.b + block.size - # If omit some lines at the beginning, indicate it by displaying a line - # with '...'. - if num_skip_start > 0: - sys.stderr.write(" ...\n") + # matched range + left_ranges.append((block.a, i)) + right_ranges.append((block.b, j)) + return left_ranges, right_ranges - # Keep track of the first different line. - first_diff = -1 - # Display only this many first characters of identical lines. - width = 75 +def render_diff_range( + ranges: list[tuple[int, int]], content: list[str], colour: str | None = None +) -> None: + for i, line_range in enumerate(ranges): + is_matching = i % 2 == 1 + lines = content[line_range[0] : line_range[1]] + for j, line in enumerate(lines): + if ( + is_matching + # elide the middle of matching blocks + and j >= 3 + and j < len(lines) - 3 + ): + if j == 3: + sys.stderr.write(" ...\n") + continue - for i in range(num_skip_start, len(expected) - num_skip_end): - if i >= len(actual) or expected[i] != actual[i]: - if first_diff < 0: - first_diff = i - sys.stderr.write(f" {expected[i]:<45} (diff)") - else: - e = expected[i] - sys.stderr.write(" " + e[:width]) - if len(e) > width: - sys.stderr.write("...") - sys.stderr.write("\n") - if num_skip_end > 0: - sys.stderr.write(" ...\n") + if not is_matching and colour: + sys.stderr.write(colour) - sys.stderr.write("Actual:\n") + sys.stderr.write(" " + line) - if num_skip_start > 0: - sys.stderr.write(" ...\n") + if not is_matching: + if colour: + sys.stderr.write("\033[0m") + sys.stderr.write(" (diff)") - for j in range(num_skip_start, len(actual) - num_skip_end): - if j >= len(expected) or expected[j] != actual[j]: - sys.stderr.write(f" {actual[j]:<45} (diff)") - else: - a = actual[j] - sys.stderr.write(" " + a[:width]) - if len(a) > width: - sys.stderr.write("...") sys.stderr.write("\n") - if not actual: - sys.stderr.write(" (empty)\n") - if num_skip_end > 0: - sys.stderr.write(" ...\n") - sys.stderr.write("\n") +def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) -> None: + """Assert that two string arrays are equal. + + Display any differences in a human-readable form. + """ + actual = clean_up(actual) + if expected != actual: + expected_ranges, actual_ranges = diff_ranges(expected, actual) + sys.stderr.write("Expected:\n") + red = "\033[31m" if sys.platform != "win32" else None + render_diff_range(expected_ranges, expected, colour=red) + sys.stderr.write("Actual:\n") + green = "\033[32m" if sys.platform != "win32" else None + render_diff_range(actual_ranges, actual, colour=green) + + sys.stderr.write("\n") + first_diff = next( + (i for i, (a, b) in enumerate(zip(expected, actual)) if a != b), + max(len(expected), len(actual)), + ) if 0 <= first_diff < len(actual) and ( len(expected[first_diff]) >= MIN_LINE_LENGTH_FOR_ALIGNMENT or len(actual[first_diff]) >= MIN_LINE_LENGTH_FOR_ALIGNMENT @@ -109,6 +127,10 @@ def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) # long lines. show_align_message(expected[first_diff], actual[first_diff]) + sys.stderr.write( + "Update the test output using --update-data -n0 " + "(you can additionally use the -k selector to update only specific tests)" + ) pytest.fail(msg, pytrace=False) @@ -226,20 +248,6 @@ def local_sys_path_set() -> Iterator[None]: sys.path = old_sys_path -def num_skipped_prefix_lines(a1: list[str], a2: list[str]) -> int: - num_eq = 0 - while num_eq < min(len(a1), len(a2)) and a1[num_eq] == a2[num_eq]: - num_eq += 1 - return max(0, num_eq - 4) - - -def num_skipped_suffix_lines(a1: list[str], a2: list[str]) -> int: - num_eq = 0 - while num_eq < min(len(a1), len(a2)) and a1[-num_eq - 1] == a2[-num_eq - 1]: - num_eq += 1 - return max(0, num_eq - 4) - - def testfile_pyversion(path: str) -> tuple[int, int]: if path.endswith("python312.test"): return 3, 12 From 402c8ffa821d35a68dfe010a59f1dd9ea3dbb02a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 15 Sep 2023 09:42:20 +0100 Subject: [PATCH 0266/1617] Fix crash on malformed TypedDict in incremental mode (#16115) Fixes https://github.com/python/mypy/issues/15557 FWIW I simply copy the logic for handling malformed definitions from named tuples, that seems to be much more robust. --- mypy/semanal_typeddict.py | 14 ++++++++++---- test-data/unit/check-incremental.test | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index fb3fa713e3fb..a9a4cd868f27 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -366,7 +366,13 @@ def check_typeddict( name, items, types, total, tvar_defs, ok = res if not ok: # Error. Construct dummy return value. - info = self.build_typeddict_typeinfo("TypedDict", [], [], set(), call.line, None) + if var_name: + name = var_name + if is_func_scope: + name += "@" + str(call.line) + else: + name = var_name = "TypedDict@" + str(call.line) + info = self.build_typeddict_typeinfo(name, [], [], set(), call.line, None) else: if var_name is not None and name != var_name: self.fail( @@ -395,9 +401,9 @@ def check_typeddict( name, items, types, required_keys, call.line, existing_info ) info.line = node.line - # Store generated TypeInfo under both names, see semanal_namedtuple for more details. - if name != var_name or is_func_scope: - self.api.add_symbol_skip_local(name, info) + # Store generated TypeInfo under both names, see semanal_namedtuple for more details. + if name != var_name or is_func_scope: + self.api.add_symbol_skip_local(name, info) if var_name: self.api.add_symbol(var_name, info, node) call.analyzed = TypedDictExpr(info) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 06f87a26e7a1..801bbd4e77b4 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6479,3 +6479,28 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") class C(Tuple[Unpack[Ts]]): ... [builtins fixtures/tuple.pyi] + +[case testNoIncrementalCrashOnInvalidTypedDict] +import m +[file m.py] +import counts +[file m.py.2] +import counts +# touch +[file counts.py] +from typing_extensions import TypedDict +Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore +[builtins fixtures/dict.pyi] + +[case testNoIncrementalCrashOnInvalidTypedDictFunc] +import m +[file m.py] +import counts +[file m.py.2] +import counts +# touch +[file counts.py] +from typing_extensions import TypedDict +def test() -> None: + Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore +[builtins fixtures/dict.pyi] From 2bbc42f898031d2aa3e26f1272604ce879ff57dd Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Fri, 15 Sep 2023 10:44:31 +0200 Subject: [PATCH 0267/1617] stubgen: generate valid dataclass stubs (#15625) Fixes #12441 Fixes #9986 Fixes #15966 --- mypy/stubgen.py | 57 +++++++++-- mypy/test/teststubgen.py | 11 +++ test-data/unit/stubgen.test | 182 ++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 6 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index aca836c52ce8..ca7249465746 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -657,6 +657,7 @@ def __init__( self.defined_names: set[str] = set() # Short names of methods defined in the body of the current class self.method_names: set[str] = set() + self.processing_dataclass = False def visit_mypy_file(self, o: MypyFile) -> None: self.module = o.fullname # Current module being processed @@ -706,6 +707,12 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: self.clear_decorators() def visit_func_def(self, o: FuncDef) -> None: + is_dataclass_generated = ( + self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated + ) + if is_dataclass_generated and o.name != "__init__": + # Skip methods generated by the @dataclass decorator (except for __init__) + return if ( self.is_private_name(o.name, o.fullname) or self.is_not_in_all(o.name) @@ -771,6 +778,12 @@ def visit_func_def(self, o: FuncDef) -> None: else: arg = name + annotation args.append(arg) + if o.name == "__init__" and is_dataclass_generated and "**" in args: + # The dataclass plugin generates invalid nameless "*" and "**" arguments + new_name = "".join(a.split(":", 1)[0] for a in args).replace("*", "") + args[args.index("*")] = f"*{new_name}_" # this name is guaranteed to be unique + args[args.index("**")] = f"**{new_name}__" # same here + retname = None if o.name != "__init__" and isinstance(o.unanalyzed_type, CallableType): if isinstance(get_proper_type(o.unanalyzed_type.ret_type), AnyType): @@ -899,6 +912,9 @@ def visit_class_def(self, o: ClassDef) -> None: if not self._indent and self._state != EMPTY: sep = len(self._output) self.add("\n") + decorators = self.get_class_decorators(o) + for d in decorators: + self.add(f"{self._indent}@{d}\n") self.add(f"{self._indent}class {o.name}") self.record_name(o.name) base_types = self.get_base_types(o) @@ -934,6 +950,7 @@ def visit_class_def(self, o: ClassDef) -> None: else: self._state = CLASS self.method_names = set() + self.processing_dataclass = False self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: @@ -979,6 +996,21 @@ def get_base_types(self, cdef: ClassDef) -> list[str]: base_types.append(f"{name}={value.accept(p)}") return base_types + def get_class_decorators(self, cdef: ClassDef) -> list[str]: + decorators: list[str] = [] + p = AliasPrinter(self) + for d in cdef.decorators: + if self.is_dataclass(d): + decorators.append(d.accept(p)) + self.import_tracker.require_name(get_qualified_name(d)) + self.processing_dataclass = True + return decorators + + def is_dataclass(self, expr: Expression) -> bool: + if isinstance(expr, CallExpr): + expr = expr.callee + return self.get_fullname(expr) == "dataclasses.dataclass" + def visit_block(self, o: Block) -> None: # Unreachable statements may be partially uninitialized and that may # cause trouble. @@ -1336,6 +1368,9 @@ def get_init( # Final without type argument is invalid in stubs. final_arg = self.get_str_type_of_node(rvalue) typename += f"[{final_arg}]" + elif self.processing_dataclass: + # attribute without annotation is not a dataclass field, don't add annotation. + return f"{self._indent}{lvalue} = ...\n" else: typename = self.get_str_type_of_node(rvalue) initializer = self.get_assign_initializer(rvalue) @@ -1343,12 +1378,20 @@ def get_init( def get_assign_initializer(self, rvalue: Expression) -> str: """Does this rvalue need some special initializer value?""" - if self._current_class and self._current_class.info: - # Current rules - # 1. Return `...` if we are dealing with `NamedTuple` and it has an existing default value - if self._current_class.info.is_named_tuple and not isinstance(rvalue, TempNode): - return " = ..." - # TODO: support other possible cases, where initializer is important + if not self._current_class: + return "" + # Current rules + # 1. Return `...` if we are dealing with `NamedTuple` or `dataclass` field and + # it has an existing default value + if ( + self._current_class.info + and self._current_class.info.is_named_tuple + and not isinstance(rvalue, TempNode) + ): + return " = ..." + if self.processing_dataclass and not (isinstance(rvalue, TempNode) and rvalue.no_rhs): + return " = ..." + # TODO: support other possible cases, where initializer is important # By default, no initializer is required: return "" @@ -1410,6 +1453,8 @@ def is_private_name(self, name: str, fullname: str | None = None) -> bool: return False if fullname in EXTRA_EXPORTED: return False + if name == "_": + return False return name.startswith("_") and (not name.endswith("__") or name in IGNORED_DUNDERS) def is_private_member(self, fullname: str) -> bool: diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 79d380785a39..7e30515ac892 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -724,11 +724,22 @@ def run_case_inner(self, testcase: DataDrivenTestCase) -> None: def parse_flags(self, program_text: str, extra: list[str]) -> Options: flags = re.search("# flags: (.*)$", program_text, flags=re.MULTILINE) + pyversion = None if flags: flag_list = flags.group(1).split() + for i, flag in enumerate(flag_list): + if flag.startswith("--python-version="): + pyversion = flag.split("=", 1)[1] + del flag_list[i] + break else: flag_list = [] options = parse_options(flag_list + extra) + if pyversion: + # A hack to allow testing old python versions with new language constructs + # This should be rarely used in general as stubgen output should not be version-specific + major, minor = pyversion.split(".", 1) + options.pyversion = (int(major), int(minor)) if "--verbose" not in flag_list: options.quiet = True else: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 774a17b76161..828680fadcf2 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -3512,3 +3512,185 @@ def gen2() -> _Generator[_Incomplete, _Incomplete, _Incomplete]: ... class X(_Incomplete): ... class Y(_Incomplete): ... + +[case testDataclass] +import dataclasses +import dataclasses as dcs +from dataclasses import dataclass, InitVar, KW_ONLY +from dataclasses import dataclass as dc +from typing import ClassVar + +@dataclasses.dataclass +class X: + a: int + b: str = "hello" + c: ClassVar + d: ClassVar = 200 + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) + _: KW_ONLY + h: int = 1 + i: InitVar[str] + j: InitVar = 100 + non_field = None + +@dcs.dataclass +class Y: ... + +@dataclass +class Z: ... + +@dc +class W: ... + +@dataclass(init=False, repr=False) +class V: ... + +[out] +import dataclasses +import dataclasses as dcs +from dataclasses import InitVar, KW_ONLY, dataclass, dataclass as dc +from typing import ClassVar + +@dataclasses.dataclass +class X: + a: int + b: str = ... + c: ClassVar + d: ClassVar = ... + f: list[int] = ... + g: int = ... + _: KW_ONLY + h: int = ... + i: InitVar[str] + j: InitVar = ... + non_field = ... + +@dcs.dataclass +class Y: ... +@dataclass +class Z: ... +@dc +class W: ... +@dataclass(init=False, repr=False) +class V: ... + +[case testDataclass_semanal] +from dataclasses import dataclass, InitVar +from typing import ClassVar + +@dataclass +class X: + a: int + b: str = "hello" + c: ClassVar + d: ClassVar = 200 + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) + h: int = 1 + i: InitVar[str] + j: InitVar = 100 + non_field = None + +@dataclass(init=False, repr=False, frozen=True) +class Y: ... + +[out] +from dataclasses import InitVar, dataclass +from typing import ClassVar + +@dataclass +class X: + a: int + b: str = ... + c: ClassVar + d: ClassVar = ... + f: list[int] = ... + g: int = ... + h: int = ... + i: InitVar[str] + j: InitVar = ... + non_field = ... + def __init__(self, a, b, f, g, h, i, j) -> None: ... + +@dataclass(init=False, repr=False, frozen=True) +class Y: ... + +[case testDataclassWithKwOnlyField_semanal] +# flags: --python-version=3.10 +from dataclasses import dataclass, InitVar, KW_ONLY +from typing import ClassVar + +@dataclass +class X: + a: int + b: str = "hello" + c: ClassVar + d: ClassVar = 200 + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) + _: KW_ONLY + h: int = 1 + i: InitVar[str] + j: InitVar = 100 + non_field = None + +@dataclass(init=False, repr=False, frozen=True) +class Y: ... + +[out] +from dataclasses import InitVar, KW_ONLY, dataclass +from typing import ClassVar + +@dataclass +class X: + a: int + b: str = ... + c: ClassVar + d: ClassVar = ... + f: list[int] = ... + g: int = ... + _: KW_ONLY + h: int = ... + i: InitVar[str] + j: InitVar = ... + non_field = ... + def __init__(self, a, b, f, g, *, h, i, j) -> None: ... + +@dataclass(init=False, repr=False, frozen=True) +class Y: ... + +[case testDataclassWithExplicitGeneratedMethodsOverrides_semanal] +from dataclasses import dataclass + +@dataclass +class X: + a: int + def __init__(self, a: int, b: str = ...) -> None: ... + def __post_init__(self) -> None: ... + +[out] +from dataclasses import dataclass + +@dataclass +class X: + a: int + def __init__(self, a: int, b: str = ...) -> None: ... + def __post_init__(self) -> None: ... + +[case testDataclassInheritsFromAny_semanal] +from dataclasses import dataclass +import missing + +@dataclass +class X(missing.Base): + a: int + +[out] +import missing +from dataclasses import dataclass + +@dataclass +class X(missing.Base): + a: int + def __init__(self, *selfa_, a, **selfa__) -> None: ... From 88ae1e4c1541e5b03d695cf63d1265b972e427d9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 15 Sep 2023 21:53:35 +0100 Subject: [PATCH 0268/1617] Fix crash on star unpack in TypedDict (#16116) Fixes https://github.com/python/mypy/issues/16107 Fixes https://github.com/python/mypy/issues/15891 I only vaguely remember why I added those context managers, it seemed to me giving full TypedDict as context may cause false positives. But since the current way causes crashes, let's just not do this (we will see if there will be actual false positives). --- mypy/checkexpr.py | 3 +-- test-data/unit/check-typeddict.test | 12 ++++++++++ test-data/unit/reports.test | 34 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 22a9852545b7..f46c8cb15c6f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -813,8 +813,7 @@ def validate_star_typeddict_item( Note `result` and `always_present_keys` are updated in place. Return true if the expression `item_arg` may valid in `callee` TypedDict context. """ - with self.chk.local_type_map(), self.msg.filter_errors(): - inferred = get_proper_type(self.accept(item_arg, type_context=callee)) + inferred = get_proper_type(self.accept(item_arg, type_context=callee)) possible_tds = [] if isinstance(inferred, TypedDictType): possible_tds = [inferred] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index b8953f05b6a5..7ee9ef0b708b 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3224,3 +3224,15 @@ t2: Foo = {**y} # E: Missing key "a" for TypedDict "Foo" t3: Foo = {**z} # E: Missing key "a" for TypedDict "Foo" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictUnpackError] +from typing import TypedDict + +class Foo(TypedDict): + a: int + +def foo(x: int) -> Foo: ... + +f: Foo = {**foo("no")} # E: Argument 1 to "foo" has incompatible type "str"; expected "int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index a6cde503ca09..16061d9c32bf 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -69,6 +69,40 @@ def untyped_function(): +[case testCoberturaStarUnpacking] +# cmd: mypy --cobertura-xml-report build a.py +[file a.py] +from typing import TypedDict + +class MyDict(TypedDict): + a: int + +def foo(a: int) -> MyDict: + return {"a": a} +md: MyDict = MyDict(**foo(42)) +[outfile build/cobertura.xml] + + + $PWD + + + + + + + + + + + + + + + + + + + [case testAnyExprReportDivisionByZero] # cmd: mypy --any-exprs-report=out -c 'pass' From 80232b0cd6305b848c0d454bac04a5fb30578766 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Fri, 15 Sep 2023 20:36:25 +0100 Subject: [PATCH 0269/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/0ea043253e70d0304478a6d0b58bcda4cc583d08 --- mypy/typeshed/stdlib/_ctypes.pyi | 18 ++- mypy/typeshed/stdlib/asyncio/tasks.pyi | 105 ++++++++++++++---- mypy/typeshed/stdlib/builtins.pyi | 105 +++++++++++++++++- mypy/typeshed/stdlib/collections/__init__.pyi | 17 ++- mypy/typeshed/stdlib/csv.pyi | 8 +- mypy/typeshed/stdlib/ctypes/wintypes.pyi | 89 ++++++++------- mypy/typeshed/stdlib/enum.pyi | 22 +++- mypy/typeshed/stdlib/functools.pyi | 40 ++++--- mypy/typeshed/stdlib/http/client.pyi | 3 + mypy/typeshed/stdlib/http/cookies.pyi | 6 +- mypy/typeshed/stdlib/imaplib.pyi | 11 +- mypy/typeshed/stdlib/importlib/__init__.pyi | 6 +- mypy/typeshed/stdlib/importlib/abc.pyi | 60 ++++++---- mypy/typeshed/stdlib/importlib/machinery.pyi | 33 ++++-- .../stdlib/importlib/metadata/__init__.pyi | 12 +- .../stdlib/importlib/metadata/_meta.pyi | 38 +++++-- .../stdlib/importlib/resources/__init__.pyi | 11 +- mypy/typeshed/stdlib/importlib/util.pyi | 11 +- mypy/typeshed/stdlib/pathlib.pyi | 19 +++- mypy/typeshed/stdlib/poplib.pyi | 29 +++-- mypy/typeshed/stdlib/smtplib.pyi | 42 +++++-- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 2 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 7 +- mypy/typeshed/stdlib/turtle.pyi | 6 + mypy/typeshed/stdlib/typing.pyi | 9 +- mypy/typeshed/stdlib/unittest/mock.pyi | 20 +++- mypy/typeshed/stdlib/urllib/request.pyi | 18 ++- mypy/typeshed/stdlib/weakref.pyi | 8 ++ mypy/typeshed/stdlib/zipfile.pyi | 14 ++- 29 files changed, 574 insertions(+), 195 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 165bb5337784..1f15ac057988 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -122,15 +122,23 @@ class CFuncPtr(_PointerLike, _CData): def __call__(self, *args: Any, **kwargs: Any) -> Any: ... -class _CField: +_GetT = TypeVar("_GetT") +_SetT = TypeVar("_SetT") + +class _CField(Generic[_CT, _GetT, _SetT]): offset: int size: int + @overload + def __get__(self, __instance: None, __owner: type[Any] | None) -> Self: ... + @overload + def __get__(self, __instance: Any, __owner: type[Any] | None) -> _GetT: ... + def __set__(self, __instance: Any, __value: _SetT) -> None: ... class _StructUnionMeta(_CDataMeta): _fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]] _pack_: int _anonymous_: Sequence[str] - def __getattr__(self, name: str) -> _CField: ... + def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... class _StructUnionBase(_CData, metaclass=_StructUnionMeta): def __init__(self, *args: Any, **kw: Any) -> None: ... @@ -151,7 +159,11 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 3bc65e3703c5..b6929deb0fae 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -2,7 +2,7 @@ import concurrent.futures import sys from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator from types import FrameType -from typing import Any, Generic, TextIO, TypeVar, overload +from typing import Any, Generic, Protocol, TextIO, TypeVar, overload from typing_extensions import Literal, TypeAlias from . import _CoroutineLike @@ -14,27 +14,52 @@ if sys.version_info >= (3, 9): if sys.version_info >= (3, 11): from contextvars import Context -__all__ = ( - "Task", - "create_task", - "FIRST_COMPLETED", - "FIRST_EXCEPTION", - "ALL_COMPLETED", - "wait", - "wait_for", - "as_completed", - "sleep", - "gather", - "shield", - "ensure_future", - "run_coroutine_threadsafe", - "current_task", - "all_tasks", - "_register_task", - "_unregister_task", - "_enter_task", - "_leave_task", -) +if sys.version_info >= (3, 12): + __all__ = ( + "Task", + "create_task", + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "wait", + "wait_for", + "as_completed", + "sleep", + "gather", + "shield", + "ensure_future", + "run_coroutine_threadsafe", + "current_task", + "all_tasks", + "create_eager_task_factory", + "eager_task_factory", + "_register_task", + "_unregister_task", + "_enter_task", + "_leave_task", + ) +else: + __all__ = ( + "Task", + "create_task", + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "wait", + "wait_for", + "as_completed", + "sleep", + "gather", + "shield", + "ensure_future", + "run_coroutine_threadsafe", + "current_task", + "all_tasks", + "_register_task", + "_unregister_task", + "_enter_task", + "_leave_task", + ) _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -356,5 +381,41 @@ else: def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... + +if sys.version_info >= (3, 12): + _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True) + + class _CustomTaskConstructor(Protocol[_TaskT_co]): + def __call__( + self, + __coro: _TaskCompatibleCoro[Any], + *, + loop: AbstractEventLoop, + name: str | None, + context: Context | None, + eager_start: bool, + ) -> _TaskT_co: ... + + class _EagerTaskFactoryType(Protocol[_TaskT_co]): + def __call__( + self, + loop: AbstractEventLoop, + coro: _TaskCompatibleCoro[Any], + *, + name: str | None = None, + context: Context | None = None, + ) -> _TaskT_co: ... + + def create_eager_task_factory( + custom_task_constructor: _CustomTaskConstructor[_TaskT_co], + ) -> _EagerTaskFactoryType[_TaskT_co]: ... + def eager_task_factory( + loop: AbstractEventLoop | None, + coro: _TaskCompatibleCoro[_T_co], + *, + name: str | None = None, + context: Context | None = None, + ) -> Task[_T_co]: ... + def _register_task(task: Task[Any]) -> None: ... def _unregister_task(task: Task[Any]) -> None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 66c644d09a4d..cf4f857c5524 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,6 +56,7 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, + LiteralString, ParamSpec, Self, SupportsIndex, @@ -441,8 +442,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -450,11 +460,20 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: + @overload + def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -470,32 +489,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -506,6 +584,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -514,13 +595,25 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload def __mod__(self, __value: Any) -> str: ... + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -1027,13 +1120,13 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @overload - def __or__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ... + def __or__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ... @overload - def __or__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... + def __or__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... @overload - def __ror__(self, __value: Mapping[_KT, _VT]) -> dict[_KT, _VT]: ... + def __ror__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ... @overload - def __ror__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, __value: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... @@ -1698,11 +1791,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 8ceecd1f354e..3b8d92f78612 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -96,6 +96,11 @@ class UserDict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... @overload def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... + if sys.version_info >= (3, 12): + @overload + def get(self, key: _KT, default: None = None) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _T) -> _VT | _T: ... class UserList(MutableSequence[_T]): data: list[_T] @@ -402,13 +407,13 @@ class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): def copy(self) -> Self: ... if sys.version_info >= (3, 9): @overload - def __or__(self, __value: Mapping[_KT, _VT]) -> Self: ... + def __or__(self, __value: dict[_KT, _VT]) -> Self: ... @overload - def __or__(self, __value: Mapping[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... + def __or__(self, __value: dict[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... @overload - def __ror__(self, __value: Mapping[_KT, _VT]) -> Self: ... + def __ror__(self, __value: dict[_KT, _VT]) -> Self: ... @overload - def __ror__(self, __value: Mapping[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, __value: dict[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] @@ -422,6 +427,10 @@ class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): def __iter__(self) -> Iterator[_KT]: ... def __len__(self) -> int: ... def __contains__(self, key: object) -> bool: ... + @overload + def get(self, key: _KT, default: None = None) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _T) -> _VT | _T: ... def __missing__(self, key: _KT) -> _VT: ... # undocumented def __bool__(self) -> bool: ... # Keep ChainMap.setdefault in line with MutableMapping.setdefault, modulo positional-only differences. diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index a9c7fe0492c8..53425fbcccb1 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -71,8 +71,8 @@ class unix_dialect(Dialect): ... class DictReader(Iterator[_DictReadMapping[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None - restkey: str | None - restval: str | None + restkey: _T | None + restval: str | Any | None reader: _reader dialect: _DialectLike line_num: int @@ -81,8 +81,8 @@ class DictReader(Iterator[_DictReadMapping[_T | Any, str | Any]], Generic[_T]): self, f: Iterable[str], fieldnames: Sequence[_T], - restkey: str | None = None, - restval: str | None = None, + restkey: _T | None = None, + restval: str | Any | None = None, dialect: _DialectLike = "excel", *, delimiter: str = ",", diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 3bd27934750a..59c7ae3e599f 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -1,6 +1,7 @@ from ctypes import ( Array, Structure, + _CField, _Pointer, _SimpleCData, c_byte, @@ -20,6 +21,7 @@ from ctypes import ( c_wchar, c_wchar_p, ) +from typing import TypeVar from typing_extensions import TypeAlias BYTE = c_byte @@ -101,39 +103,42 @@ HWND = HANDLE SC_HANDLE = HANDLE SERVICE_STATUS_HANDLE = HANDLE +_CIntLikeT = TypeVar("_CIntLikeT", bound=_SimpleCData[int]) +_CIntLikeField: TypeAlias = _CField[_CIntLikeT, int, _CIntLikeT | int] + class RECT(Structure): - left: LONG - top: LONG - right: LONG - bottom: LONG + left: _CIntLikeField[LONG] + top: _CIntLikeField[LONG] + right: _CIntLikeField[LONG] + bottom: _CIntLikeField[LONG] RECTL = RECT _RECTL = RECT tagRECT = RECT class _SMALL_RECT(Structure): - Left: SHORT - Top: SHORT - Right: SHORT - Bottom: SHORT + Left: _CIntLikeField[SHORT] + Top: _CIntLikeField[SHORT] + Right: _CIntLikeField[SHORT] + Bottom: _CIntLikeField[SHORT] SMALL_RECT = _SMALL_RECT class _COORD(Structure): - X: SHORT - Y: SHORT + X: _CIntLikeField[SHORT] + Y: _CIntLikeField[SHORT] class POINT(Structure): - x: LONG - y: LONG + x: _CIntLikeField[LONG] + y: _CIntLikeField[LONG] POINTL = POINT _POINTL = POINT tagPOINT = POINT class SIZE(Structure): - cx: LONG - cy: LONG + cx: _CIntLikeField[LONG] + cy: _CIntLikeField[LONG] SIZEL = SIZE tagSIZE = SIZE @@ -141,45 +146,45 @@ tagSIZE = SIZE def RGB(red: int, green: int, blue: int) -> int: ... class FILETIME(Structure): - dwLowDateTime: DWORD - dwHighDateTime: DWORD + dwLowDateTime: _CIntLikeField[DWORD] + dwHighDateTime: _CIntLikeField[DWORD] _FILETIME = FILETIME class MSG(Structure): - hWnd: HWND - message: UINT - wParam: WPARAM - lParam: LPARAM - time: DWORD - pt: POINT + hWnd: _CField[HWND, int | None, HWND | int | None] + message: _CIntLikeField[UINT] + wParam: _CIntLikeField[WPARAM] + lParam: _CIntLikeField[LPARAM] + time: _CIntLikeField[DWORD] + pt: _CField[POINT, POINT, POINT] tagMSG = MSG MAX_PATH: int class WIN32_FIND_DATAA(Structure): - dwFileAttributes: DWORD - ftCreationTime: FILETIME - ftLastAccessTime: FILETIME - ftLastWriteTime: FILETIME - nFileSizeHigh: DWORD - nFileSizeLow: DWORD - dwReserved0: DWORD - dwReserved1: DWORD - cFileName: Array[CHAR] - cAlternateFileName: Array[CHAR] + dwFileAttributes: _CIntLikeField[DWORD] + ftCreationTime: _CField[FILETIME, FILETIME, FILETIME] + ftLastAccessTime: _CField[FILETIME, FILETIME, FILETIME] + ftLastWriteTime: _CField[FILETIME, FILETIME, FILETIME] + nFileSizeHigh: _CIntLikeField[DWORD] + nFileSizeLow: _CIntLikeField[DWORD] + dwReserved0: _CIntLikeField[DWORD] + dwReserved1: _CIntLikeField[DWORD] + cFileName: _CField[Array[CHAR], bytes, bytes] + cAlternateFileName: _CField[Array[CHAR], bytes, bytes] class WIN32_FIND_DATAW(Structure): - dwFileAttributes: DWORD - ftCreationTime: FILETIME - ftLastAccessTime: FILETIME - ftLastWriteTime: FILETIME - nFileSizeHigh: DWORD - nFileSizeLow: DWORD - dwReserved0: DWORD - dwReserved1: DWORD - cFileName: Array[WCHAR] - cAlternateFileName: Array[WCHAR] + dwFileAttributes: _CIntLikeField[DWORD] + ftCreationTime: _CField[FILETIME, FILETIME, FILETIME] + ftLastAccessTime: _CField[FILETIME, FILETIME, FILETIME] + ftLastWriteTime: _CField[FILETIME, FILETIME, FILETIME] + nFileSizeHigh: _CIntLikeField[DWORD] + nFileSizeLow: _CIntLikeField[DWORD] + dwReserved0: _CIntLikeField[DWORD] + dwReserved1: _CIntLikeField[DWORD] + cFileName: _CField[Array[WCHAR], str, str] + cAlternateFileName: _CField[Array[WCHAR], str, str] # These pointer type definitions use _Pointer[...] instead of POINTER(...), to allow them # to be used in type annotations. diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index e6eaf6c413dc..10ea19257144 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -119,10 +119,12 @@ class EnumMeta(type): def __len__(self) -> int: ... def __bool__(self) -> Literal[True]: ... def __dir__(self) -> list[str]: ... - # Simple value lookup + + # Overload 1: Value lookup on an already existing enum class (simple case) @overload def __call__(cls: type[_EnumMemberT], value: Any, names: None = None) -> _EnumMemberT: ... - # Functional Enum API + + # Overload 2: Functional API for constructing new enum classes. if sys.version_info >= (3, 11): @overload def __call__( @@ -148,6 +150,18 @@ class EnumMeta(type): type: type | None = None, start: int = 1, ) -> type[Enum]: ... + + # Overload 3 (py312+ only): Value lookup on an already existing enum class (complex case) + # + # >>> class Foo(enum.Enum): + # ... X = 1, 2, 3 + # >>> Foo(1, 2, 3) + # + # + if sys.version_info >= (3, 12): + @overload + def __call__(cls: type[_EnumMemberT], value: Any, *values: Any) -> _EnumMemberT: ... + _member_names_: list[str] # undocumented _member_map_: dict[str, Enum] # undocumented _value2member_map_: dict[Any, Enum] # undocumented @@ -160,6 +174,7 @@ if sys.version_info >= (3, 11): def __set_name__(self, ownerclass: type[Enum], name: str) -> None: ... name: str clsname: str + member: Enum | None _magic_enum_attr = property else: _magic_enum_attr = types.DynamicClassAttribute @@ -191,6 +206,9 @@ class Enum(metaclass=EnumMeta): if sys.version_info >= (3, 11): def __copy__(self) -> Self: ... def __deepcopy__(self, memo: Any) -> Self: ... + if sys.version_info >= (3, 12): + @classmethod + def __signature__(cls) -> str: ... if sys.version_info >= (3, 11): class ReprEnum(Enum): ... diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 1b4e59b7c120..0d08cdb19e3f 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,10 +28,12 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWrapper = TypeVar("_RWrapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -85,31 +87,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 4b5ed3d8bda0..3e5e496ab501 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -169,6 +169,9 @@ class HTTPConnection: ) -> None: ... def getresponse(self) -> HTTPResponse: ... def set_debuglevel(self, level: int) -> None: ... + if sys.version_info >= (3, 12): + def get_proxy_response_headers(self) -> HTTPMessage | None: ... + def set_tunnel(self, host: str, port: int | None = None, headers: Mapping[str, str] | None = None) -> None: ... def connect(self) -> None: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/http/cookies.pyi b/mypy/typeshed/stdlib/http/cookies.pyi index e24ef9cbdd2e..3d19bb108c2d 100644 --- a/mypy/typeshed/stdlib/http/cookies.pyi +++ b/mypy/typeshed/stdlib/http/cookies.pyi @@ -49,12 +49,12 @@ class Morsel(dict[str, Any], Generic[_T]): class BaseCookie(dict[str, Morsel[_T]], Generic[_T]): def __init__(self, input: _DataType | None = None) -> None: ... - def value_decode(self, val: str) -> _T: ... - def value_encode(self, val: _T) -> str: ... + def value_decode(self, val: str) -> tuple[_T, str]: ... + def value_encode(self, val: _T) -> tuple[_T, str]: ... def output(self, attrs: list[str] | None = None, header: str = "Set-Cookie:", sep: str = "\r\n") -> str: ... __str__ = output def js_output(self, attrs: list[str] | None = None) -> str: ... def load(self, rawdata: _DataType) -> None: ... def __setitem__(self, key: str, value: str | Morsel[_T]) -> None: ... -class SimpleCookie(BaseCookie[_T], Generic[_T]): ... +class SimpleCookie(BaseCookie[str]): ... diff --git a/mypy/typeshed/stdlib/imaplib.pyi b/mypy/typeshed/stdlib/imaplib.pyi index 7781559c3888..a61848c9af13 100644 --- a/mypy/typeshed/stdlib/imaplib.pyi +++ b/mypy/typeshed/stdlib/imaplib.pyi @@ -108,9 +108,14 @@ class IMAP4: def print_log(self) -> None: ... class IMAP4_SSL(IMAP4): - keyfile: str - certfile: str - if sys.version_info >= (3, 9): + if sys.version_info < (3, 12): + keyfile: str + certfile: str + if sys.version_info >= (3, 12): + def __init__( + self, host: str = "", port: int = 993, *, ssl_context: SSLContext | None = None, timeout: float | None = None + ) -> None: ... + elif sys.version_info >= (3, 9): def __init__( self, host: str = "", diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi index 8d73319f8c3d..8506efc01171 100644 --- a/mypy/typeshed/stdlib/importlib/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/__init__.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Mapping, Sequence from importlib.abc import Loader from types import ModuleType @@ -15,6 +16,9 @@ def __import__( # `importlib.import_module` return type should be kept the same as `builtins.__import__` def import_module(name: str, package: str | None = None) -> ModuleType: ... -def find_loader(name: str, path: str | None = None) -> Loader | None: ... + +if sys.version_info < (3, 12): + def find_loader(name: str, path: str | None = None) -> Loader | None: ... + def invalidate_caches() -> None: ... def reload(module: ModuleType) -> ModuleType: ... diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 4bf46104ba6d..28c33205a4df 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -20,7 +20,6 @@ from typing_extensions import Literal if sys.version_info >= (3, 11): __all__ = [ "Loader", - "Finder", "MetaPathFinder", "PathEntryFinder", "ResourceLoader", @@ -28,16 +27,19 @@ if sys.version_info >= (3, 11): "ExecutionLoader", "FileLoader", "SourceLoader", - "ResourceReader", - "Traversable", - "TraversableResources", ] -class Finder(metaclass=ABCMeta): ... + if sys.version_info < (3, 12): + __all__ += ["Finder", "ResourceReader", "Traversable", "TraversableResources"] + +if sys.version_info < (3, 12): + class Finder(metaclass=ABCMeta): ... class Loader(metaclass=ABCMeta): def load_module(self, fullname: str) -> types.ModuleType: ... - def module_repr(self, module: types.ModuleType) -> str: ... + if sys.version_info < (3, 12): + def module_repr(self, module: types.ModuleType) -> str: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... # Not defined on the actual class for backwards-compatibility reasons, # but expected in new code. @@ -68,21 +70,37 @@ class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): def get_source(self, fullname: str) -> str | None: ... def path_stats(self, path: str) -> Mapping[str, Any]: ... -# Please keep in sync with sys._MetaPathFinder -class MetaPathFinder(Finder): - def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... - def invalidate_caches(self) -> None: ... - # Not defined on the actual class, but expected to exist. - def find_spec( - self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ... - ) -> ModuleSpec | None: ... - -class PathEntryFinder(Finder): - def find_module(self, fullname: str) -> Loader | None: ... - def find_loader(self, fullname: str) -> tuple[Loader | None, Sequence[str]]: ... - def invalidate_caches(self) -> None: ... - # Not defined on the actual class, but expected to exist. - def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... +# The base classes differ on 3.12: +if sys.version_info >= (3, 12): + # Please keep in sync with sys._MetaPathFinder + class MetaPathFinder(metaclass=ABCMeta): + def invalidate_caches(self) -> None: ... + # Not defined on the actual class, but expected to exist. + def find_spec( + self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ... + ) -> ModuleSpec | None: ... + + class PathEntryFinder(metaclass=ABCMeta): + def invalidate_caches(self) -> None: ... + # Not defined on the actual class, but expected to exist. + def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... + +else: + # Please keep in sync with sys._MetaPathFinder + class MetaPathFinder(Finder): + def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... + def invalidate_caches(self) -> None: ... + # Not defined on the actual class, but expected to exist. + def find_spec( + self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ... + ) -> ModuleSpec | None: ... + + class PathEntryFinder(Finder): + def find_module(self, fullname: str) -> Loader | None: ... + def find_loader(self, fullname: str) -> tuple[Loader | None, Sequence[str]]: ... + def invalidate_caches(self) -> None: ... + # Not defined on the actual class, but expected to exist. + def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... class FileLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): name: str diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index f5037da00d5f..1a9680ab3c46 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -31,8 +31,10 @@ class ModuleSpec: class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): # MetaPathFinder - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + @classmethod def find_spec( cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None @@ -47,8 +49,9 @@ class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader) @classmethod def get_source(cls, fullname: str) -> None: ... # Loader - @staticmethod - def module_repr(module: types.ModuleType) -> str: ... + if sys.version_info < (3, 12): + @staticmethod + def module_repr(module: types.ModuleType) -> str: ... if sys.version_info >= (3, 10): @staticmethod def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... @@ -62,8 +65,10 @@ class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader) class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): # MetaPathFinder - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + @classmethod def find_spec( cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None @@ -78,8 +83,9 @@ class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): @classmethod def get_source(cls, fullname: str) -> None: ... # Loader - @staticmethod - def module_repr(m: types.ModuleType) -> str: ... + if sys.version_info < (3, 12): + @staticmethod + def module_repr(m: types.ModuleType) -> str: ... if sys.version_info >= (3, 10): @staticmethod def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... @@ -91,8 +97,10 @@ class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): def exec_module(module: types.ModuleType) -> None: ... class WindowsRegistryFinder(importlib.abc.MetaPathFinder): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + @classmethod def find_spec( cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None @@ -116,8 +124,9 @@ class PathFinder: def find_spec( cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None ) -> ModuleSpec | None: ... - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... SOURCE_SUFFIXES: list[str] DEBUG_BYTECODE_SUFFIXES: list[str] diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 0f8a6f56cf88..e52756544e9a 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -88,6 +88,7 @@ if sys.version_info >= (3, 10): @property def groups(self) -> set[str]: ... +if sys.version_info >= (3, 10) and sys.version_info < (3, 12): class SelectableGroups(dict[str, EntryPoints]): # use as dict is deprecated since 3.10 @classmethod def load(cls, eps: Iterable[EntryPoint]) -> Self: ... @@ -195,6 +196,16 @@ def distributions( if sys.version_info >= (3, 10): def metadata(distribution_name: str) -> PackageMetadata: ... + +else: + def metadata(distribution_name: str) -> Message: ... + +if sys.version_info >= (3, 12): + def entry_points( + *, name: str = ..., value: str = ..., group: str = ..., module: str = ..., attr: str = ..., extras: list[str] = ... + ) -> EntryPoints: ... + +elif sys.version_info >= (3, 10): @overload def entry_points() -> SelectableGroups: ... # type: ignore[misc] @overload @@ -203,7 +214,6 @@ if sys.version_info >= (3, 10): ) -> EntryPoints: ... else: - def metadata(distribution_name: str) -> Message: ... def entry_points() -> dict[str, list[EntryPoint]]: ... def version(distribution_name: str) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi index e3504fe4036a..64fefa9a84e2 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi @@ -1,5 +1,6 @@ +import sys from collections.abc import Iterator -from typing import Any, Protocol, TypeVar +from typing import Any, Protocol, TypeVar, overload _T = TypeVar("_T") @@ -8,15 +9,32 @@ class PackageMetadata(Protocol): def __contains__(self, item: str) -> bool: ... def __getitem__(self, key: str) -> str: ... def __iter__(self) -> Iterator[str]: ... - def get_all(self, name: str, failobj: _T = ...) -> list[Any] | _T: ... @property def json(self) -> dict[str, str | list[str]]: ... + @overload + def get_all(self, name: str, failobj: None = None) -> list[Any] | None: ... + @overload + def get_all(self, name: str, failobj: _T) -> list[Any] | _T: ... + if sys.version_info >= (3, 12): + @overload + def get(self, name: str, failobj: None = None) -> str | None: ... + @overload + def get(self, name: str, failobj: _T) -> _T | str: ... -class SimplePath(Protocol): - def joinpath(self) -> SimplePath: ... - def parent(self) -> SimplePath: ... - def read_text(self) -> str: ... - # There was a bug in `SimplePath` definition in cpython, see #8451 - # Strictly speaking `__div__` was defined in 3.10, not __truediv__, - # but it should have always been `__truediv__`. - def __truediv__(self) -> SimplePath: ... +if sys.version_info >= (3, 12): + class SimplePath(Protocol[_T]): + def joinpath(self) -> _T: ... + @property + def parent(self) -> _T: ... + def read_text(self) -> str: ... + def __truediv__(self, other: _T | str) -> _T: ... + +else: + class SimplePath(Protocol): + def joinpath(self) -> SimplePath: ... + def parent(self) -> SimplePath: ... + def read_text(self) -> str: ... + # There was a bug in `SimplePath` definition in cpython, see #8451 + # Strictly speaking `__div__` was defined in 3.10, not __truediv__, + # but it should have always been `__truediv__`. + def __truediv__(self) -> SimplePath: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi index ba3d9b087754..8d656563772c 100644 --- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi @@ -7,6 +7,9 @@ from types import ModuleType from typing import Any, BinaryIO, TextIO from typing_extensions import TypeAlias +if sys.version_info >= (3, 9): + from importlib.abc import Traversable + __all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] if sys.version_info >= (3, 9): @@ -31,9 +34,13 @@ def is_resource(package: Package, name: str) -> bool: ... def contents(package: Package) -> Iterator[str]: ... if sys.version_info >= (3, 9): - from importlib.abc import Traversable - def files(package: Package) -> Traversable: ... def as_file(path: Traversable) -> AbstractContextManager[Path]: ... +if sys.version_info >= (3, 12): + def files(anchor: Package | None = ...) -> Traversable: ... + +elif sys.version_info >= (3, 9): + def files(package: Package) -> Traversable: ... + if sys.version_info >= (3, 10): from importlib.abc import ResourceReader as ResourceReader diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index f988eb270a26..6608f70d4469 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -1,5 +1,6 @@ import importlib.abc import importlib.machinery +import sys import types from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Callable @@ -8,9 +9,11 @@ from typing_extensions import ParamSpec _P = ParamSpec("_P") -def module_for_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... -def set_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... -def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... +if sys.version_info < (3, 12): + def module_for_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... + def set_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... + def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... + def resolve_name(name: str, package: str | None) -> str: ... MAGIC_NUMBER: bytes @@ -37,4 +40,4 @@ class LazyLoader(importlib.abc.Loader): def factory(cls, loader: importlib.abc.Loader) -> Callable[..., LazyLoader]: ... def exec_module(self, module: types.ModuleType) -> None: ... -def source_hash(source_bytes: ReadableBuffer) -> int: ... +def source_hash(source_bytes: ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index a509ec3af9f2..10ffa4a778e8 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -8,6 +8,7 @@ from _typeshed import ( ReadableBuffer, StrOrBytesPath, StrPath, + Unused, ) from collections.abc import Callable, Generator, Iterator, Sequence from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper @@ -38,9 +39,13 @@ class PurePath(PathLike[str]): def suffixes(self) -> list[str]: ... @property def stem(self) -> str: ... - def __new__(cls, *args: StrPath) -> Self: ... + if sys.version_info >= (3, 12): + def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... + def __init__(self, *args: StrPath) -> None: ... + else: + def __new__(cls, *args: StrPath) -> Self: ... + def __hash__(self) -> int: ... - def __eq__(self, other: object) -> bool: ... def __fspath__(self) -> str: ... def __lt__(self, other: PurePath) -> bool: ... def __le__(self, other: PurePath) -> bool: ... @@ -53,7 +58,9 @@ class PurePath(PathLike[str]): def as_uri(self) -> str: ... def is_absolute(self) -> bool: ... def is_reserved(self) -> bool: ... - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 12): + def is_relative_to(self, __other: StrPath, *_deprecated: StrPath) -> bool: ... + elif sys.version_info >= (3, 9): def is_relative_to(self, *other: StrPath) -> bool: ... if sys.version_info >= (3, 12): @@ -61,7 +68,11 @@ class PurePath(PathLike[str]): else: def match(self, path_pattern: str) -> bool: ... - def relative_to(self, *other: StrPath) -> Self: ... + if sys.version_info >= (3, 12): + def relative_to(self, __other: StrPath, *_deprecated: StrPath, walk_up: bool = False) -> Self: ... + else: + def relative_to(self, *other: StrPath) -> Self: ... + def with_name(self, name: str) -> Self: ... if sys.version_info >= (3, 9): def with_stem(self, stem: str) -> Self: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index c64e47e8ef72..808e7e5222af 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -1,5 +1,6 @@ import socket import ssl +import sys from builtins import list as _list # conflicts with a method named "list" from re import Pattern from typing import Any, BinaryIO, NoReturn, overload @@ -51,14 +52,20 @@ class POP3: def stls(self, context: ssl.SSLContext | None = None) -> bytes: ... class POP3_SSL(POP3): - def __init__( - self, - host: str, - port: int = 995, - keyfile: str | None = None, - certfile: str | None = None, - timeout: float = ..., - context: ssl.SSLContext | None = None, - ) -> None: ... - # "context" is actually the last argument, but that breaks LSP and it doesn't really matter because all the arguments are ignored - def stls(self, context: Any = None, keyfile: Any = None, certfile: Any = None) -> NoReturn: ... + if sys.version_info >= (3, 12): + def __init__( + self, host: str, port: int = 995, *, timeout: float = ..., context: ssl.SSLContext | None = None + ) -> None: ... + def stls(self, context: Any = None) -> NoReturn: ... + else: + def __init__( + self, + host: str, + port: int = 995, + keyfile: str | None = None, + certfile: str | None = None, + timeout: float = ..., + context: ssl.SSLContext | None = None, + ) -> None: ... + # "context" is actually the last argument, but that breaks LSP and it doesn't really matter because all the arguments are ignored + def stls(self, context: Any = None, keyfile: Any = None, certfile: Any = None) -> NoReturn: ... diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi index 584fa164fec9..e584d7f571a7 100644 --- a/mypy/typeshed/stdlib/smtplib.pyi +++ b/mypy/typeshed/stdlib/smtplib.pyi @@ -128,7 +128,13 @@ class SMTP: def auth_plain(self, challenge: ReadableBuffer | None = None) -> str: ... def auth_login(self, challenge: ReadableBuffer | None = None) -> str: ... def login(self, user: str, password: str, *, initial_response_ok: bool = True) -> _Reply: ... - def starttls(self, keyfile: str | None = None, certfile: str | None = None, context: SSLContext | None = None) -> _Reply: ... + if sys.version_info >= (3, 12): + def starttls(self, *, context: SSLContext | None = None) -> _Reply: ... + else: + def starttls( + self, keyfile: str | None = None, certfile: str | None = None, context: SSLContext | None = None + ) -> _Reply: ... + def sendmail( self, from_addr: str, @@ -152,17 +158,29 @@ class SMTP_SSL(SMTP): keyfile: str | None certfile: str | None context: SSLContext - def __init__( - self, - host: str = "", - port: int = 0, - local_hostname: str | None = None, - keyfile: str | None = None, - certfile: str | None = None, - timeout: float = ..., - source_address: _SourceAddress | None = None, - context: SSLContext | None = None, - ) -> None: ... + if sys.version_info >= (3, 12): + def __init__( + self, + host: str = "", + port: int = 0, + local_hostname: str | None = None, + *, + timeout: float = ..., + source_address: _SourceAddress | None = None, + context: SSLContext | None = None, + ) -> None: ... + else: + def __init__( + self, + host: str = "", + port: int = 0, + local_hostname: str | None = None, + keyfile: str | None = None, + certfile: str | None = None, + timeout: float = ..., + source_address: _SourceAddress | None = None, + context: SSLContext | None = None, + ) -> None: ... LMTP_PORT: int diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 41f731e21e26..e85f49207763 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -351,7 +351,7 @@ class Connection: @overload def cursor(self, cursorClass: None = None) -> Cursor: ... @overload - def cursor(self, cursorClass: Callable[[], _CursorT]) -> _CursorT: ... + def cursor(self, cursorClass: Callable[[Connection], _CursorT]) -> _CursorT: ... def execute(self, sql: str, parameters: _Parameters = ...) -> Cursor: ... def executemany(self, __sql: str, __parameters: Iterable[_Parameters]) -> Cursor: ... def executescript(self, __sql_script: str) -> Cursor: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index a03c48c039dd..a0a88a8ac82e 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -6,7 +6,7 @@ from enum import Enum from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, NamedTuple, Protocol, TypeVar, overload, type_check_only +from typing import Any, Generic, NamedTuple, TypeVar, overload, type_check_only from typing_extensions import Literal, TypeAlias, TypedDict if sys.version_info >= (3, 9): @@ -720,9 +720,6 @@ class Wm: def wm_withdraw(self) -> None: ... withdraw = wm_withdraw -class _ExceptionReportingCallback(Protocol): - def __call__(self, __exc: type[BaseException], __val: BaseException, __tb: TracebackType | None) -> object: ... - class Tk(Misc, Wm): master: None def __init__( @@ -764,7 +761,7 @@ class Tk(Misc, Wm): config = configure def destroy(self) -> None: ... def readprofile(self, baseName: str, className: str) -> None: ... - report_callback_exception: _ExceptionReportingCallback + report_callback_exception: Callable[[type[BaseException], BaseException, TracebackType | None], object] # Tk has __getattr__ so that tk_instance.foo falls back to tk_instance.tk.foo # Please keep in sync with _tkinter.TkappType. # Some methods are intentionally missing because they are inherited from Misc instead. diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 80ea40879dee..36cd5f1f6e9d 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -129,6 +129,9 @@ __all__ = [ "Terminator", ] +if sys.version_info >= (3, 12): + __all__ += ["teleport"] + # Note: '_Color' is the alias we use for arguments and _AnyColor is the # alias we use for return types. Really, these two aliases should be the # same, but as per the "no union returns" typeshed policy, we'll return @@ -648,6 +651,9 @@ def shape(name: None = None) -> str: ... @overload def shape(name: str) -> None: ... +if sys.version_info >= (3, 12): + def teleport(x: float | None = None, y: float | None = None, *, fill_gap: bool = False) -> None: ... + # Unsafely overlaps when no arguments are provided @overload def shapesize() -> tuple[float, float, float]: ... # type: ignore[misc] diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index a9bffdf5214f..2c1ebe6d7f95 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -126,6 +126,9 @@ if sys.version_info >= (3, 11): "reveal_type", ] +if sys.version_info >= (3, 12): + __all__ += ["TypeAliasType", "override"] + ContextManager = AbstractContextManager AsyncContextManager = AbstractAsyncContextManager @@ -323,7 +326,9 @@ AnyStr = TypeVar("AnyStr", str, bytes) # noqa: Y001 # Technically in 3.7 this inherited from GenericMeta. But let's not reflect that, since # type checkers tend to assume that Protocols all have the ABCMeta metaclass. -class _ProtocolMeta(ABCMeta): ... +class _ProtocolMeta(ABCMeta): + if sys.version_info >= (3, 12): + def __init__(cls, *args: Any, **kwargs: Any) -> None: ... # Abstract base classes. @@ -945,7 +950,7 @@ if sys.version_info >= (3, 10): def _type_repr(obj: object) -> str: ... if sys.version_info >= (3, 12): - def override(__arg: _F) -> _F: ... + def override(__method: _F) -> _F: ... @_final class TypeAliasType: def __init__( diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 66120197b269..baf025bdeb5a 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -106,7 +106,25 @@ class Base: # We subclass with "Any" because mocks are explicitly designed to stand in for other types, # something that can't be expressed with our static type system. class NonCallableMock(Base, Any): - def __new__(__cls, *args: Any, **kw: Any) -> Self: ... + if sys.version_info >= (3, 12): + def __new__( + cls, + spec: list[str] | object | type[object] | None = None, + wraps: Any | None = None, + name: str | None = None, + spec_set: list[str] | object | type[object] | None = None, + parent: NonCallableMock | None = None, + _spec_state: Any | None = None, + _new_name: str = "", + _new_parent: NonCallableMock | None = None, + _spec_as_instance: bool = False, + _eat_self: bool | None = None, + unsafe: bool = False, + **kwargs: Any, + ) -> Self: ... + else: + def __new__(__cls, *args: Any, **kw: Any) -> Self: ... + def __init__( self, spec: list[str] | object | type[object] | None = None, diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 079c9755528c..a4849dfa2e6e 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -235,7 +235,11 @@ class _HTTPConnectionProtocol(Protocol): ) -> HTTPConnection: ... class AbstractHTTPHandler(BaseHandler): # undocumented - def __init__(self, debuglevel: int = 0) -> None: ... + if sys.version_info >= (3, 12): + def __init__(self, debuglevel: int | None = None) -> None: ... + else: + def __init__(self, debuglevel: int = 0) -> None: ... + def set_http_debuglevel(self, level: int) -> None: ... def do_request_(self, request: Request) -> Request: ... def do_open(self, http_class: _HTTPConnectionProtocol, req: Request, **http_conn_args: Any) -> HTTPResponse: ... @@ -245,9 +249,15 @@ class HTTPHandler(AbstractHTTPHandler): def http_request(self, request: Request) -> Request: ... # undocumented class HTTPSHandler(AbstractHTTPHandler): - def __init__( - self, debuglevel: int = 0, context: ssl.SSLContext | None = None, check_hostname: bool | None = None - ) -> None: ... + if sys.version_info >= (3, 12): + def __init__( + self, debuglevel: int | None = None, context: ssl.SSLContext | None = None, check_hostname: bool | None = None + ) -> None: ... + else: + def __init__( + self, debuglevel: int = 0, context: ssl.SSLContext | None = None, check_hostname: bool | None = None + ) -> None: ... + def https_open(self, req: Request) -> HTTPResponse: ... def https_request(self, request: Request) -> Request: ... # undocumented diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index ecb98d4269d5..ca5366602ceb 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -65,6 +65,10 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def copy(self) -> WeakValueDictionary[_KT, _VT]: ... __copy__ = copy def __deepcopy__(self, memo: Any) -> Self: ... + @overload + def get(self, key: _KT, default: None = None) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _T) -> _VT | _T: ... # These are incompatible with Mapping def keys(self) -> Iterator[_KT]: ... # type: ignore[override] def values(self) -> Iterator[_VT]: ... # type: ignore[override] @@ -107,6 +111,10 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def copy(self) -> WeakKeyDictionary[_KT, _VT]: ... __copy__ = copy def __deepcopy__(self, memo: Any) -> Self: ... + @overload + def get(self, key: _KT, default: None = None) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _T) -> _VT | _T: ... # These are incompatible with Mapping def keys(self) -> Iterator[_KT]: ... # type: ignore[override] def values(self) -> Iterator[_VT]: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile.pyi index abda7a3b9625..dc07eb3f2a38 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile.pyi @@ -257,7 +257,11 @@ if sys.version_info >= (3, 8): @property def open(self) -> _PathOpenProtocol: ... - def iterdir(self) -> Iterator[Path]: ... + if sys.version_info >= (3, 10): + def iterdir(self) -> Iterator[Self]: ... + else: + def iterdir(self) -> Iterator[Path]: ... + def is_dir(self) -> bool: ... def is_file(self) -> bool: ... def exists(self) -> bool: ... @@ -274,6 +278,14 @@ if sys.version_info >= (3, 8): def joinpath(self, *other: StrPath) -> Path: ... else: def joinpath(self, add: StrPath) -> Path: ... # undocumented + if sys.version_info >= (3, 12): + def glob(self, pattern: str) -> Iterator[Self]: ... + def rglob(self, pattern: str) -> Iterator[Self]: ... + def is_symlink(self) -> Literal[False]: ... + def relative_to(self, other: Path, *extra: StrPath) -> str: ... + def match(self, path_pattern: str) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def __truediv__(self, add: StrPath) -> Path: ... From 9859fe7ba3ae046ed3aa822f869ec4866978eac8 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0270/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index cf4f857c5524..2c7331e87b1a 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -56,7 +56,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, - LiteralString, ParamSpec, Self, SupportsIndex, @@ -442,17 +441,8 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -460,20 +450,11 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: - @overload - def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -489,91 +470,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -584,9 +506,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -595,25 +514,13 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From 378a866e90f534a0f0e9bbbca1324317ba784bbb Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0271/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 2c7331e87b1a..9e413579e0fb 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1698,11 +1698,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] else: @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... From 2816b97d5c61355d089b291b861df4c64f4aa96a Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0272/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 1f15ac057988..538c07d54aad 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -159,11 +159,7 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 7d987a1056e4c6ee6f75aa4841f7e0c73ca9b496 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0273/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 0d08cdb19e3f..1b4e59b7c120 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,12 +28,10 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWrapper = TypeVar("_RWrapper") @overload def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... @@ -87,41 +85,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From ec665cc8b4f59e81ec28ea946bc673cb20028751 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Fri, 15 Sep 2023 20:50:25 +0100 Subject: [PATCH 0274/1617] Fix the newly-uncovered stubtest bug --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a804835a632b..a5028581f7a1 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1686,7 +1686,7 @@ def get_importable_stdlib_modules() -> set[str]: modules_by_finder[m.module_finder].add(m.name) for finder, module_group in modules_by_finder.items(): if ( - "site-packages" not in Path(finder.path).parents + "site-packages" not in Path(finder.path).parts # if "_queue" is present, it's most likely the module finder # for stdlib extension modules; # if "queue" is present, it's most likely the module finder From 0222bf492e035ab1062a6d6fc38abc249a8ae211 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 16 Sep 2023 21:57:54 +0100 Subject: [PATCH 0275/1617] Update hashes in `sync-typeshed.py` following typeshed sync (#16126) Followup to #16121 --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 36967f86262e..77f921a89b1b 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "2f6b6e66c", # LiteralString reverts - "120af30e7", # sum reverts - "1866d28f1", # ctypes reverts - "3240da455", # ParamSpec for functools.wraps + "9859fe7ba", # LiteralString reverts + "378a866e9", # sum reverts + "2816b97d5", # ctypes reverts + "7d987a105", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: From b65cd9ae6e1ae4b25e4af4f0e855646bbe382b29 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Sun, 17 Sep 2023 05:39:48 -0400 Subject: [PATCH 0276/1617] dataclass.replace: allow transformed classes (#15915) We [already synthesize](https://github.com/python/mypy/issues/15843#issuecomment-1685159995) `__dataclass_fields__` for all classes including `@dataclass_transform`'d ones, thus assume more than PEP-681 says. We might as well assume `dataclasses.replace` applies on all same classes. This way we risk false positive since it'll raise in runtime. Fixes #15843. --- mypy/plugins/dataclasses.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 99f079705c3f..f2ae3fd3d01e 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -375,9 +375,7 @@ def transform(self) -> bool: add_attribute_to_class(self._api, self._cls, "__match_args__", match_args_type) self._add_dataclass_fields_magic_attribute() - - if self._spec is _TRANSFORM_SPEC_FOR_DATACLASSES: - self._add_internal_replace_method(attributes) + self._add_internal_replace_method(attributes) if "__post_init__" in info.names: self._add_internal_post_init_method(attributes) From c99b93646c7edb5ae33a84c9b322b289b97e0117 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 17 Sep 2023 05:22:59 -0700 Subject: [PATCH 0277/1617] Fix mypyc regression with pretty (#16124) Fixes #15877 Regression was introduced by #15070. Previously Errors objects created in mypyc build would just use all the default values, now they use the actual options object involved --- mypy/errors.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index a678b790cb8c..4e62a48aeb27 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -909,8 +909,7 @@ def file_messages(self, path: str) -> list[str]: return [] self.flushed_files.add(path) source_lines = None - if self.options.pretty: - assert self.read_source + if self.options.pretty and self.read_source: source_lines = self.read_source(path) return self.format_messages(self.error_info_map[path], source_lines) From bf7eab682a2bc63ec90e868610231a105a9b415f Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sun, 17 Sep 2023 13:30:30 -0700 Subject: [PATCH 0278/1617] Use comments in issue template (#15742) Fixes #15741 --- .github/ISSUE_TEMPLATE/feature.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md index 135bc2bd3b94..984e552e51b1 100644 --- a/.github/ISSUE_TEMPLATE/feature.md +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -6,8 +6,8 @@ labels: "feature" **Feature** -(A clear and concise description of your feature proposal.) + **Pitch** -(Please explain why this feature should be implemented and how it would be used. Add examples, if applicable.) + From 9b9152484c6b1ba3934373ca0c7600f71392fb06 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sun, 17 Sep 2023 13:32:20 -0700 Subject: [PATCH 0279/1617] Make it easier to copy commands from docs README (#16133) Fixes #16132. --- docs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index 0d574c9213a5..e72164c78560 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,13 +15,13 @@ Install Sphinx and other dependencies (i.e. theme) needed for the documentation. From the `docs` directory, use `pip`: ``` -$ pip install -r requirements-docs.txt +pip install -r requirements-docs.txt ``` Build the documentation like this: ``` -$ make html +make html ``` The built documentation will be placed in the `docs/build` directory. Open @@ -33,13 +33,13 @@ Helpful documentation build commands Clean the documentation build: ``` -$ make clean +make clean ``` Test and check the links found in the documentation: ``` -$ make linkcheck +make linkcheck ``` Documentation on Read The Docs From 1dcff0d2235ba6570f290a126f1bdd762f2d4991 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:50:32 -0700 Subject: [PATCH 0280/1617] Preserve implicitly exported types via attribute access (#16129) Resolves #13965. Follow up to #13967. Unblocks #14086 --- mypy/checkmember.py | 15 ++++++++++++++- test-data/unit/check-flags.test | 26 +++++++++++++++++++------- test-data/unit/check-modules.test | 3 ++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 59af0d402e14..4316a59281c3 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -24,6 +24,7 @@ FuncDef, IndexExpr, MypyFile, + NameExpr, OverloadedFuncDef, SymbolNode, SymbolTable, @@ -608,7 +609,19 @@ 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) + ret = report_missing_attribute(mx.original_type, itype, name, mx) + # Avoid paying double jeopardy if we can't find the member due to --no-implicit-reexport + if ( + mx.module_symbol_table is not None + and name in mx.module_symbol_table + and not mx.module_symbol_table[name].module_public + ): + v = mx.module_symbol_table[name].node + e = NameExpr(name) + e.set_line(mx.context) + e.node = v + return mx.chk.expr_checker.analyze_ref_expr(e, lvalue=mx.is_lvalue) + return ret def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Context) -> None: diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 96f78d81dd16..06b7cab8391b 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1611,14 +1611,22 @@ from other_module_2 import a # E: Module "other_module_2" does not explicitly e reveal_type(a) # N: Revealed type is "builtins.int" import other_module_2 -# TODO: this should also reveal builtins.int, see #13965 -reveal_type(other_module_2.a) # E: "object" does not explicitly export attribute "a" [attr-defined] \ - # N: Revealed type is "Any" +reveal_type(other_module_2.a) # E: Module "other_module_2" does not explicitly export attribute "a" [attr-defined] \ + # N: Revealed type is "builtins.int" + +from other_module_2 import b # E: Module "other_module_2" does not explicitly export attribute "b" [attr-defined] +reveal_type(b) # N: Revealed type is "def (a: builtins.int) -> builtins.str" + +import other_module_2 +reveal_type(other_module_2.b) # E: Module "other_module_2" does not explicitly export attribute "b" [attr-defined] \ + # N: Revealed type is "def (a: builtins.int) -> builtins.str" [file other_module_1.py] a = 5 +def b(a: int) -> str: ... [file other_module_2.py] -from other_module_1 import a +from other_module_1 import a, b +[builtins fixtures/module.pyi] [case testNoImplicitReexportRespectsAll] # flags: --no-implicit-reexport @@ -1649,11 +1657,15 @@ __all__ = ('b',) [case testNoImplicitReexportGetAttr] # flags: --no-implicit-reexport --python-version 3.7 from other_module_2 import a # E: Module "other_module_2" does not explicitly export attribute "a" +reveal_type(a) # N: Revealed type is "builtins.int" +from other_module_2 import b # E: Module "other_module_2" does not explicitly export attribute "b" +reveal_type(b) # N: Revealed type is "builtins.str" [file other_module_1.py] -from typing import Any -def __getattr__(name: str) -> Any: ... +b: str = "asdf" +def __getattr__(name: str) -> int: ... [file other_module_2.py] -from other_module_1 import a +from other_module_1 import a, b +def __getattr__(name: str) -> bytes: ... [builtins fixtures/tuple.pyi] [case textNoImplicitReexportSuggestions] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 94368f6c1113..abbdf4987c46 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -1862,7 +1862,8 @@ import stub reveal_type(stub.y) # N: Revealed type is "builtins.int" reveal_type(stub.z) # E: Module "stub" does not explicitly export attribute "z" \ - # N: Revealed type is "Any" + # N: Revealed type is "builtins.int" + [file stub.pyi] from substub import y as y From ba978f461e1f88327f9caa2e83774caaaeee1a6a Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Tue, 19 Sep 2023 08:10:31 +0200 Subject: [PATCH 0281/1617] Call dynamic class hook on generic classes (#16052) Fixes: #8359 CC @sobolevn `get_dynamic_class_hook()` will now additionally be called for generic classes with parameters. e.g. ```python y = SomeGenericClass[type, ...].method() ``` --- mypy/semanal.py | 7 ++++ test-data/unit/check-custom-plugin.test | 12 +++++- .../unit/plugins/dyn_class_from_method.py | 40 ++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 70403eed57ae..e19cd86d5e89 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3205,6 +3205,13 @@ def apply_dynamic_class_hook(self, s: AssignmentStmt) -> None: if isinstance(callee_expr, RefExpr) and callee_expr.fullname: method_name = call.callee.name fname = callee_expr.fullname + "." + method_name + elif ( + isinstance(callee_expr, IndexExpr) + and isinstance(callee_expr.base, RefExpr) + and isinstance(callee_expr.analyzed, TypeApplication) + ): + method_name = call.callee.name + fname = callee_expr.base.fullname + "." + method_name elif isinstance(callee_expr, CallExpr): # check if chain call call = callee_expr diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 22374d09cf9f..63529cf165ce 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -684,12 +684,16 @@ plugins=/test-data/unit/plugins/dyn_class.py [case testDynamicClassHookFromClassMethod] # flags: --config-file tmp/mypy.ini -from mod import QuerySet, Manager +from mod import QuerySet, Manager, GenericQuerySet MyManager = Manager.from_queryset(QuerySet) +ManagerFromGenericQuerySet = GenericQuerySet[int].as_manager() reveal_type(MyManager()) # N: Revealed type is "__main__.MyManager" reveal_type(MyManager().attr) # N: Revealed type is "builtins.str" +reveal_type(ManagerFromGenericQuerySet()) # N: Revealed type is "__main__.ManagerFromGenericQuerySet" +reveal_type(ManagerFromGenericQuerySet().attr) # N: Revealed type is "builtins.int" +queryset: GenericQuerySet[int] = ManagerFromGenericQuerySet() def func(manager: MyManager) -> None: reveal_type(manager) # N: Revealed type is "__main__.MyManager" @@ -704,6 +708,12 @@ class QuerySet: class Manager: @classmethod def from_queryset(cls, queryset_cls: Type[QuerySet]): ... +T = TypeVar("T") +class GenericQuerySet(Generic[T]): + attr: T + + @classmethod + def as_manager(cls): ... [builtins fixtures/classmethod.pyi] [file mypy.ini] diff --git a/test-data/unit/plugins/dyn_class_from_method.py b/test-data/unit/plugins/dyn_class_from_method.py index b84754654084..2630b16be66e 100644 --- a/test-data/unit/plugins/dyn_class_from_method.py +++ b/test-data/unit/plugins/dyn_class_from_method.py @@ -2,7 +2,19 @@ from typing import Callable -from mypy.nodes import GDEF, Block, ClassDef, RefExpr, SymbolTable, SymbolTableNode, TypeInfo +from mypy.nodes import ( + GDEF, + Block, + ClassDef, + IndexExpr, + MemberExpr, + NameExpr, + RefExpr, + SymbolTable, + SymbolTableNode, + TypeApplication, + TypeInfo, +) from mypy.plugin import DynamicClassDefContext, Plugin from mypy.types import Instance @@ -13,6 +25,8 @@ def get_dynamic_class_hook( ) -> Callable[[DynamicClassDefContext], None] | None: if "from_queryset" in fullname: return add_info_hook + if "as_manager" in fullname: + return as_manager_hook return None @@ -34,5 +48,29 @@ def add_info_hook(ctx: DynamicClassDefContext) -> None: ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) +def as_manager_hook(ctx: DynamicClassDefContext) -> None: + class_def = ClassDef(ctx.name, Block([])) + class_def.fullname = ctx.api.qualified_name(ctx.name) + + info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) + class_def.info = info + assert isinstance(ctx.call.callee, MemberExpr) + assert isinstance(ctx.call.callee.expr, IndexExpr) + assert isinstance(ctx.call.callee.expr.analyzed, TypeApplication) + assert isinstance(ctx.call.callee.expr.analyzed.expr, NameExpr) + + queryset_type_fullname = ctx.call.callee.expr.analyzed.expr.fullname + queryset_node = ctx.api.lookup_fully_qualified_or_none(queryset_type_fullname) + assert queryset_node is not None + queryset_info = queryset_node.node + assert isinstance(queryset_info, TypeInfo) + parameter_type = ctx.call.callee.expr.analyzed.types[0] + + obj = ctx.api.named_type("builtins.object") + info.mro = [info, queryset_info, obj.type] + info.bases = [Instance(queryset_info, [parameter_type])] + ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) + + def plugin(version: str) -> type[DynPlugin]: return DynPlugin From 249f3f8285d9d2a0f77273ace805dac0eef684c6 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:53:31 -0700 Subject: [PATCH 0282/1617] Fix inference for overloaded __call__ with generic self (#16053) Fixes #8283 Co-authored-by: ilevkivskyi --- mypy/checkexpr.py | 4 ++- mypy/checkmember.py | 13 ++++--- mypy/subtypes.py | 51 +++++++++++++++------------ test-data/unit/check-overloading.test | 24 +++++++++++++ test-data/unit/check-tuples.test | 14 ++++++++ 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f46c8cb15c6f..7b9b84938930 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1475,6 +1475,7 @@ def check_call( callable_node: Expression | None = None, callable_name: str | None = None, object_type: Type | None = None, + original_type: Type | None = None, ) -> tuple[Type, Type]: """Type check a call. @@ -1537,7 +1538,7 @@ def check_call( is_super=False, is_operator=True, msg=self.msg, - original_type=callee, + original_type=original_type or callee, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -1578,6 +1579,7 @@ def check_call( callable_node, callable_name, object_type, + original_type=callee, ) else: return self.msg.not_callable(callee, context), AnyType(TypeOfAny.from_error) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 4316a59281c3..1557b62917dc 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -331,13 +331,12 @@ def analyze_instance_member_access( signature = method.type signature = freshen_all_functions_type_vars(signature) if not method.is_static: - if name != "__call__": - # TODO: use proper treatment of special methods on unions instead - # of this hack here and below (i.e. mx.self_type). - dispatched_type = meet.meet_types(mx.original_type, typ) - signature = check_self_arg( - signature, dispatched_type, method.is_class, mx.context, name, mx.msg - ) + # TODO: use proper treatment of special methods on unions instead + # of this hack here and below (i.e. mx.self_type). + dispatched_type = meet.meet_types(mx.original_type, typ) + signature = check_self_arg( + signature, dispatched_type, method.is_class, mx.context, name, mx.msg + ) signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class) # TODO: should we skip these steps for static methods as well? # Since generic static methods should not be allowed. diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 9ed2e4af4051..c5399db0a494 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -454,19 +454,22 @@ def visit_instance(self, left: Instance) -> bool: if isinstance(unpacked, Instance): return self._is_subtype(left, unpacked) if left.type.has_base(right.partial_fallback.type.fullname): - # Special case to consider Foo[*tuple[Any, ...]] (i.e. bare Foo) a - # subtype of Foo[], when Foo is user defined variadic tuple type. - mapped = map_instance_to_supertype(left, right.partial_fallback.type) - if len(mapped.args) == 1 and isinstance(mapped.args[0], UnpackType): - unpacked = get_proper_type(mapped.args[0].type) - if isinstance(unpacked, Instance): - assert unpacked.type.fullname == "builtins.tuple" - if isinstance(get_proper_type(unpacked.args[0]), AnyType): - return not self.proper_subtype - if mapped.type.fullname == "builtins.tuple" and isinstance( - get_proper_type(mapped.args[0]), AnyType - ): - return not self.proper_subtype + if not self.proper_subtype: + # Special case to consider Foo[*tuple[Any, ...]] (i.e. bare Foo) a + # subtype of Foo[], when Foo is user defined variadic tuple type. + mapped = map_instance_to_supertype(left, right.partial_fallback.type) + for arg in map(get_proper_type, mapped.args): + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if not isinstance(unpacked, Instance): + break + assert unpacked.type.fullname == "builtins.tuple" + if not isinstance(get_proper_type(unpacked.args[0]), AnyType): + break + elif not isinstance(arg, AnyType): + break + else: + return True return False if isinstance(right, TypeVarTupleType): # tuple[Any, ...] is like Any in the world of tuples (see special case above). @@ -534,15 +537,19 @@ def visit_instance(self, left: Instance) -> bool: right_args = ( right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix ) - if len(t.args) == 1 and isinstance(t.args[0], UnpackType): - unpacked = get_proper_type(t.args[0].type) - if isinstance(unpacked, Instance): - assert unpacked.type.fullname == "builtins.tuple" - if ( - isinstance(get_proper_type(unpacked.args[0]), AnyType) - and not self.proper_subtype - ): - return True + if not self.proper_subtype: + for arg in map(get_proper_type, t.args): + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if not isinstance(unpacked, Instance): + break + assert unpacked.type.fullname == "builtins.tuple" + if not isinstance(get_proper_type(unpacked.args[0]), AnyType): + break + elif not isinstance(arg, AnyType): + break + else: + return True type_params = zip(left_args, right_args, right.type.defn.type_vars) else: type_params = zip(t.args, right.args, right.type.defn.type_vars) diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4546c7171856..443a6fb5cb10 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6650,3 +6650,27 @@ def d(x: int) -> int: ... def d(f: int, *, x: int) -> str: ... def d(*args, **kwargs): ... [builtins fixtures/tuple.pyi] + +[case testOverloadCallableGenericSelf] +from typing import Any, TypeVar, Generic, overload, reveal_type + +T = TypeVar("T") + +class MyCallable(Generic[T]): + def __init__(self, t: T): + self.t = t + + @overload + def __call__(self: "MyCallable[int]") -> str: ... + @overload + def __call__(self: "MyCallable[str]") -> int: ... + def __call__(self): ... + +c = MyCallable(5) +reveal_type(c) # N: Revealed type is "__main__.MyCallable[builtins.int]" +reveal_type(c()) # N: Revealed type is "builtins.str" + +c2 = MyCallable("test") +reveal_type(c2) # N: Revealed type is "__main__.MyCallable[builtins.str]" +reveal_type(c2()) # should be int # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 391fa20db738..ed2c3550a04e 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1434,7 +1434,21 @@ def foo(o: CallableTuple) -> int: class CallableTuple(Tuple[str, int]): def __call__(self, n: int, m: int) -> int: return n +[builtins fixtures/tuple.pyi] + +[case testTypeTupleGenericCall] +from typing import Generic, Tuple, TypeVar + +T = TypeVar('T') +def foo(o: CallableTuple[int]) -> int: + reveal_type(o) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple[builtins.int]]" + reveal_type(o.count(3)) # N: Revealed type is "builtins.int" + return o(1, 2) + +class CallableTuple(Tuple[str, T]): + def __call__(self, n: int, m: int) -> int: + return n [builtins fixtures/tuple.pyi] [case testTupleCompatibleWithSequence] From c9929e2c906d377ca7026c4be10f88a1bd7ecff1 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 19 Sep 2023 18:42:06 +0300 Subject: [PATCH 0283/1617] Fix crash on dataclass field / property collision (#16147) I think the current error message is enough: https://github.com/python/mypy/issues/16141 CC @ikonst and @hauntsaninja --- mypy/plugins/dataclasses.py | 5 +++++ test-data/unit/check-dataclasses.test | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index f2ae3fd3d01e..a51b393fcbc4 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -23,6 +23,7 @@ ClassDef, Context, DataclassTransformSpec, + Decorator, Expression, FuncDef, FuncItem, @@ -575,6 +576,10 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: # but the only alternative would be to modify the SymbolTable, # and it's a little hairy to do that in a plugin. continue + if isinstance(node, Decorator): + # This might be a property / field name clash. + # We will issue an error later. + continue assert isinstance(node, Var) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 35df84658259..d37ae569cc5e 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2519,3 +2519,15 @@ a: MyDataclass b = [a, a] # trigger joining the types [builtins fixtures/dataclasses.pyi] + +[case testPropertyAndFieldRedefinitionNoCrash] +from dataclasses import dataclass + +@dataclass +class Foo: + @property + def c(self) -> int: + return 0 + + c: int # E: Name "c" already defined on line 5 +[builtins fixtures/dataclasses.pyi] From 7089a7fe635cfbed2916bb4f67243b317ccf37ea Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Tue, 19 Sep 2023 22:24:36 -0700 Subject: [PATCH 0284/1617] Do not consider `import a.b as b` an explicit reexport (#14086) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The point of the `import a as a` and `from a import b as b` syntax for explicit reexport is that it indicates an intention to do something different from the ordinary `import a` and `from a import b`. That is not the case with `import a.b as b`. Even mypy’s own code includes `import mypy.types as types`, which was not intended to be a reexport; if it were, it would be written `from mypy import types as types`. Pyright agrees that `import a.b as b` should not reexport. Signed-off-by: Anders Kaseorg --- mypy/semanal.py | 2 +- test-data/unit/check-modules.test | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e19cd86d5e89..6e103e5d382c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2466,7 +2466,7 @@ def visit_import(self, i: Import) -> None: if as_id is not None: base_id = id imported_id = as_id - module_public = use_implicit_reexport or id.split(".")[-1] == as_id + module_public = use_implicit_reexport or id == as_id else: base_id = id.split(".")[0] imported_id = base_id diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index abbdf4987c46..44585fdd8d1a 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -1818,6 +1818,8 @@ m = n # E: Cannot assign multiple modules to name "m" without explicit "types.M from stub import Iterable # E: Module "stub" does not explicitly export attribute "Iterable" from stub import D # E: Module "stub" does not explicitly export attribute "D" from stub import C +from stub import foo +from stub import bar # E: Module "stub" does not explicitly export attribute "bar" c = C() reveal_type(c.x) # N: Revealed type is "builtins.int" @@ -1828,6 +1830,8 @@ reveal_type(it) # N: Revealed type is "typing.Iterable[builtins.int]" from typing import Iterable from substub import C as C from substub import C as D +from package import foo as foo +import package.bar as bar def fun(x: Iterable[str]) -> Iterable[int]: pass @@ -1835,6 +1839,10 @@ def fun(x: Iterable[str]) -> Iterable[int]: pass class C: x: int +[file package/foo.pyi] + +[file package/bar.pyi] + [builtins fixtures/module.pyi] [case testNoReExportFromStubsMemberType] From ff81a1c7abc91d9984fc73b9f2b9eab198001c8e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 20 Sep 2023 13:50:48 +0300 Subject: [PATCH 0285/1617] Remove `is_classmethod_class` slot from `CallableType` (#16151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This slot was not used anywhere: ``` » ag is_classmethod_class . ``` Moreover, since it was not initialized this code was failing with `AttributeError`: ```python x: CallableType for i in dir(x): print(i, getattr(x, i)) # failing on `is_classmethod_class` ``` --- mypy/types.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 22fcd601d6a0..2b5aec7789f7 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1763,8 +1763,6 @@ class CallableType(FunctionLike): "definition", # For error messages. May be None. "variables", # Type variables for a generic function "is_ellipsis_args", # Is this Callable[..., t] (with literal '...')? - "is_classmethod_class", # Is this callable constructed for the benefit - # of a classmethod's 'cls' argument? "implicit", # Was this type implicitly generated instead of explicitly # specified by the user? "special_sig", # Non-None for signatures that require special handling From 9edda9a79790d8f7263234eca9509657ea0c37f0 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sun, 24 Sep 2023 23:31:21 +0900 Subject: [PATCH 0286/1617] Fix typo in dataclasses.py (#16173) ``` heirarchy -> hierarchy ``` --- mypy/plugins/dataclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index a51b393fcbc4..685d1b342055 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -730,7 +730,7 @@ def _freeze(self, attributes: list[DataclassAttribute]) -> None: for attr in attributes: # Classes that directly specify a dataclass_transform metaclass must be neither frozen # non non-frozen per PEP681. Though it is surprising, this means that attributes from - # such a class must be writable even if the rest of the class heirarchy is frozen. This + # such a class must be writable even if the rest of the class hierarchy is frozen. This # matches the behavior of Pyright (the reference implementation). if attr.is_neither_frozen_nor_nonfrozen: continue From 0c8b76195a773363721d5521653bcdf9989d8768 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 25 Sep 2023 02:28:08 +0200 Subject: [PATCH 0287/1617] stubgen: multiple fixes to the generated imports (#15624) * Fix handling of nested imports. Instead of assuming that a name is imported from a top level package, look in the imports for this name starting from the parent submodule up until the import is found * Fix "from imports" getting reexported unnecessarily * Fix import sorting when having import aliases Fixes #13661 Fixes #7006 --- mypy/stubgen.py | 24 ++++++++++----- test-data/unit/stubgen.test | 60 +++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ca7249465746..e8c12ee4d99b 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -496,7 +496,9 @@ def add_import(self, module: str, alias: str | None = None) -> None: name = name.rpartition(".")[0] def require_name(self, name: str) -> None: - self.required_names.add(name.split(".")[0]) + while name not in self.direct_imports and "." in name: + name = name.rsplit(".", 1)[0] + self.required_names.add(name) def reexport(self, name: str) -> None: """Mark a given non qualified name as needed in __all__. @@ -516,7 +518,10 @@ def import_lines(self) -> list[str]: # be imported from it. the names can also be alias in the form 'original as alias' module_map: Mapping[str, list[str]] = defaultdict(list) - for name in sorted(self.required_names): + for name in sorted( + self.required_names, + key=lambda n: (self.reverse_alias[n], n) if n in self.reverse_alias else (n, ""), + ): # If we haven't seen this name in an import statement, ignore it if name not in self.module_for: continue @@ -540,7 +545,7 @@ def import_lines(self) -> list[str]: assert "." not in name # Because reexports only has nonqualified names result.append(f"import {name} as {name}\n") else: - result.append(f"import {self.direct_imports[name]}\n") + result.append(f"import {name}\n") # Now generate all the from ... import ... lines collected in module_map for module, names in sorted(module_map.items()): @@ -595,7 +600,7 @@ def visit_name_expr(self, e: NameExpr) -> None: self.refs.add(e.name) def visit_instance(self, t: Instance) -> None: - self.add_ref(t.type.fullname) + self.add_ref(t.type.name) super().visit_instance(t) def visit_unbound_type(self, t: UnboundType) -> None: @@ -614,7 +619,10 @@ def visit_callable_type(self, t: CallableType) -> None: t.ret_type.accept(self) def add_ref(self, fullname: str) -> None: - self.refs.add(fullname.split(".")[-1]) + self.refs.add(fullname) + while "." in fullname: + fullname = fullname.rsplit(".", 1)[0] + self.refs.add(fullname) class StubGenerator(mypy.traverser.TraverserVisitor): @@ -1295,6 +1303,7 @@ def visit_import_from(self, o: ImportFrom) -> None: if ( as_name is None and name not in self.referenced_names + and not any(n.startswith(name + ".") for n in self.referenced_names) and (not self._all_ or name in IGNORED_DUNDERS) and not is_private and module not in ("abc", "asyncio") + TYPING_MODULE_NAMES @@ -1303,14 +1312,15 @@ def visit_import_from(self, o: ImportFrom) -> None: # exported, unless there is an explicit __all__. Note that we need to special # case 'abc' since some references are deleted during semantic analysis. exported = True - top_level = full_module.split(".")[0] + top_level = full_module.split(".", 1)[0] + self_top_level = self.module.split(".", 1)[0] if ( as_name is None and not self.export_less and (not self._all_ or name in IGNORED_DUNDERS) and self.module and not is_private - and top_level in (self.module.split(".")[0], "_" + self.module.split(".")[0]) + and top_level in (self_top_level, "_" + self_top_level) ): # Export imports from the same package, since we can't reliably tell whether they # are part of the public API. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 828680fadcf2..23dbf36a551b 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -2772,9 +2772,9 @@ y: b.Y z: p.a.X [out] +import p.a import p.a as a import p.b as b -import p.a x: a.X y: b.Y @@ -2787,7 +2787,7 @@ from p import a x: a.X [out] -from p import a as a +from p import a x: a.X @@ -2809,7 +2809,7 @@ from p import a x: a.X [out] -from p import a as a +from p import a x: a.X @@ -2859,6 +2859,60 @@ import p.a x: a.X y: p.a.Y +[case testNestedImports] +import p +import p.m1 +import p.m2 + +x: p.X +y: p.m1.Y +z: p.m2.Z + +[out] +import p +import p.m1 +import p.m2 + +x: p.X +y: p.m1.Y +z: p.m2.Z + +[case testNestedImportsAliased] +import p as t +import p.m1 as pm1 +import p.m2 as pm2 + +x: t.X +y: pm1.Y +z: pm2.Z + +[out] +import p as t +import p.m1 as pm1 +import p.m2 as pm2 + +x: t.X +y: pm1.Y +z: pm2.Z + +[case testNestedFromImports] +from p import m1 +from p.m1 import sm1 +from p.m2 import sm2 + +x: m1.X +y: sm1.Y +z: sm2.Z + +[out] +from p import m1 +from p.m1 import sm1 +from p.m2 import sm2 + +x: m1.X +y: sm1.Y +z: sm2.Z + [case testOverload_fromTypingImport] from typing import Tuple, Union, overload From 4b66fa9de07828621fee1d53abd533f3903e570a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 27 Sep 2023 00:29:11 +0100 Subject: [PATCH 0288/1617] Special-case type inference of empty collections (#16122) Fixes https://github.com/python/mypy/issues/230 Fixes https://github.com/python/mypy/issues/6463 I bet it fixes some other duplicates, I closed couple yesterday, but likely there are more. This may look a bit ad-hoc, but after some thinking this now starts to make sense to me for two reasons: * Unless I am missing something, this should be completely safe. Special-casing only applies to inferred types (i.e. empty collection literals etc). * Empty collections _are_ actually special. Even if we solve some classes of issues with more principled solutions (e.g. I want to re-work type inference against unions in near future), there will always be some corner cases involving empty collections. Similar issues keep coming, so I think it is a good idea to add this special-casing (especially taking into account how simple it is, and that it closer some "popular" issues). --- mypy/solve.py | 14 ++++++++++++ mypy/subtypes.py | 7 ++++++ mypy/test/testpep561.py | 2 +- test-data/unit/check-inference-context.test | 11 ++-------- test-data/unit/check-inference.test | 24 +++++++++++++++++++++ test-data/unit/check-varargs.test | 24 ++++----------------- 6 files changed, 52 insertions(+), 30 deletions(-) diff --git a/mypy/solve.py b/mypy/solve.py index 7cdf1c10c9b5..52e6549e98a6 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -239,6 +239,20 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None: top: Type | None = None candidate: Type | None = None + # Filter out previous results of failed inference, they will only spoil the current pass... + new_uppers = [] + for u in uppers: + pu = get_proper_type(u) + if not isinstance(pu, UninhabitedType) or not pu.ambiguous: + new_uppers.append(u) + uppers = new_uppers + + # ...unless this is the only information we have, then we just pass it on. + if not uppers and not lowers: + candidate = UninhabitedType() + candidate.ambiguous = True + return candidate + # Process each bound separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint references. diff --git a/mypy/subtypes.py b/mypy/subtypes.py index c5399db0a494..822c4b0ebf32 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -18,6 +18,7 @@ ARG_STAR2, CONTRAVARIANT, COVARIANT, + INVARIANT, Decorator, FuncBase, OverloadedFuncDef, @@ -342,6 +343,12 @@ def _is_subtype( def check_type_parameter( left: Type, right: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext ) -> bool: + # It is safe to consider empty collection literals and similar as covariant, since + # such type can't be stored in a variable, see checker.is_valid_inferred_type(). + if variance == INVARIANT: + p_left = get_proper_type(left) + if isinstance(p_left, UninhabitedType) and p_left.ambiguous: + variance = COVARIANT if variance == COVARIANT: if proper_subtype: return is_proper_subtype(left, right, subtype_context=subtype_context) diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index 48d0658cd1e9..9d2628c1fa5f 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -131,7 +131,7 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: steps = testcase.find_steps() if steps != [[]]: - steps = [[]] + steps # type: ignore[assignment] + steps = [[]] + steps for i, operations in enumerate(steps): perform_file_operations(operations) diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 169fee65f127..773a9ffd8274 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -1321,11 +1321,7 @@ from typing import List, TypeVar T = TypeVar('T', bound=int) def f(x: List[T]) -> List[T]: ... -# TODO: improve error message for such cases, see #3283 and #5706 -y: List[str] = f([]) \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[str]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant +y: List[str] = f([]) [builtins fixtures/list.pyi] [case testWideOuterContextNoArgs] @@ -1342,10 +1338,7 @@ from typing import TypeVar, Optional, List T = TypeVar('T', bound=int) def f(x: Optional[T] = None) -> List[T]: ... -y: List[str] = f() \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[str]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant +y: List[str] = f() [builtins fixtures/list.pyi] [case testUseCovariantGenericOuterContext] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index f9a4d58c74af..caa44cb40ad4 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3686,3 +3686,27 @@ def g(*args: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] + +[case testInferenceWorksWithEmptyCollectionsNested] +from typing import List, TypeVar, NoReturn +T = TypeVar('T') +def f(a: List[T], b: List[T]) -> T: pass +x = ["yes"] +reveal_type(f(x, [])) # N: Revealed type is "builtins.str" +reveal_type(f(["yes"], [])) # N: Revealed type is "builtins.str" + +empty: List[NoReturn] +f(x, empty) # E: Cannot infer type argument 1 of "f" +f(["no"], empty) # E: Cannot infer type argument 1 of "f" +[builtins fixtures/list.pyi] + +[case testInferenceWorksWithEmptyCollectionsUnion] +from typing import Any, Dict, NoReturn, NoReturn, Union + +def foo() -> Union[Dict[str, Any], Dict[int, Any]]: + return {} + +empty: Dict[NoReturn, NoReturn] +def bar() -> Union[Dict[str, Any], Dict[int, Any]]: + return empty +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 41668e991972..2495a883aa71 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -602,31 +602,15 @@ class A: pass class B: pass if int(): - a, aa = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant - + a, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") if int(): aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A") if int(): - ab, aa = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant \ - # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" - + ab, aa = G().f(*[a]) # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" if int(): - ao, ao = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[object]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant + ao, ao = G().f(*[a]) if int(): - aa, aa = G().f(*[a]) \ - # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ - # N: Consider using "Sequence" instead, which is covariant + aa, aa = G().f(*[a]) [builtins fixtures/list.pyi] [case testCallerTupleVarArgsAndGenericCalleeVarArg] From 5f6961b38acd7381ff3f8071f1f31db192cba368 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 27 Sep 2023 23:34:50 +0100 Subject: [PATCH 0289/1617] Use upper bounds as fallback solutions for inference (#16184) Fixes https://github.com/python/mypy/issues/13220 This looks a bit ad-hoc, but it is probably the least disruptive solution possible. --- mypy/solve.py | 35 +++++++++++++++++++++++++++++ test-data/unit/check-inference.test | 8 +++++++ 2 files changed, 43 insertions(+) diff --git a/mypy/solve.py b/mypy/solve.py index 52e6549e98a6..4d0ca6b7af24 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -109,6 +109,13 @@ def solve_constraints( else: candidate = AnyType(TypeOfAny.special_form) res.append(candidate) + + if not free_vars: + # Most of the validation for solutions is done in applytype.py, but here we can + # quickly test solutions w.r.t. to upper bounds, and use the latter (if possible), + # if solutions are actually not valid (due to poor inference context). + res = pre_validate_solutions(res, original_vars, constraints) + return res, free_vars @@ -487,3 +494,31 @@ def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool: def get_vars(target: Type, vars: list[TypeVarId]) -> set[TypeVarId]: """Find type variables for which we are solving in a target type.""" return {tv.id for tv in get_all_type_vars(target)} & set(vars) + + +def pre_validate_solutions( + solutions: list[Type | None], + original_vars: Sequence[TypeVarLikeType], + constraints: list[Constraint], +) -> list[Type | None]: + """Check is each solution satisfies the upper bound of the corresponding type variable. + + If it doesn't satisfy the bound, check if bound itself satisfies all constraints, and + if yes, use it instead as a fallback solution. + """ + new_solutions: list[Type | None] = [] + for t, s in zip(original_vars, solutions): + if s is not None and not is_subtype(s, t.upper_bound): + bound_satisfies_all = True + for c in constraints: + if c.op == SUBTYPE_OF and not is_subtype(t.upper_bound, c.target): + bound_satisfies_all = False + break + if c.op == SUPERTYPE_OF and not is_subtype(c.target, t.upper_bound): + bound_satisfies_all = False + break + if bound_satisfies_all: + new_solutions.append(t.upper_bound) + continue + new_solutions.append(s) + return new_solutions diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index caa44cb40ad4..348eb8b60076 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3542,6 +3542,14 @@ T = TypeVar("T") def type_or_callable(value: T, tp: Union[Type[T], Callable[[int], T]]) -> T: ... reveal_type(type_or_callable(A("test"), A)) # N: Revealed type is "__main__.A" +[case testUpperBoundAsInferenceFallback] +from typing import Callable, TypeVar, Any, Mapping, Optional +T = TypeVar("T", bound=Mapping[str, Any]) +def raises(opts: Optional[T]) -> T: pass +def assertRaises(cb: Callable[..., object]) -> None: pass +assertRaises(raises) # OK +[builtins fixtures/dict.pyi] + [case testJoinWithAnyFallback] from unknown import X # type: ignore[import] From d25d68065c18a30d975685bd7a13cb7d085a200c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 28 Sep 2023 00:27:13 +0100 Subject: [PATCH 0290/1617] Use type variable bound when it appears as actual during inference (#16178) This should help with re-enabling the use of `ParamSpec` in `functools.wraps` (as it looks like some of the new errors in https://github.com/AlexWaygood/mypy/pull/11 are caused by not handling this). --------- Co-authored-by: hauntsaninja Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/constraints.py | 12 ++++++++++++ test-data/unit/check-inference.test | 30 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/mypy/constraints.py b/mypy/constraints.py index 0524e38f9643..b61d882da3c4 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -328,6 +328,18 @@ def _infer_constraints( if isinstance(template, TypeVarType): return [Constraint(template, direction, actual)] + if ( + isinstance(actual, TypeVarType) + and not actual.id.is_meta_var() + and direction == SUPERTYPE_OF + ): + # Unless template is also a type variable (or a union that contains one), using the upper + # bound for inference will usually give better result for actual that is a type variable. + if not isinstance(template, UnionType) or not any( + isinstance(t, TypeVarType) for t in template.items + ): + actual = get_proper_type(actual.upper_bound) + # Now handle the case of either template or actual being a Union. # For a Union to be a subtype of another type, every item of the Union # must be a subtype of that type, so concatenate the constraints. diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 348eb8b60076..0a95ffdd50cf 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3695,6 +3695,36 @@ reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" [builtins fixtures/list.pyi] +[case testInferenceAgainstTypeVarActualBound] +from typing import Callable, TypeVar + +T = TypeVar("T") +S = TypeVar("S") +def test(f: Callable[[T], S]) -> Callable[[T], S]: ... + +F = TypeVar("F", bound=Callable[..., object]) +def dec(f: F) -> F: + reveal_type(test(f)) # N: Revealed type is "def (Any) -> builtins.object" + return f + +[case testInferenceAgainstTypeVarActualUnionBound] +from typing import Protocol, TypeVar, Union + +T_co = TypeVar("T_co", covariant=True) +class SupportsFoo(Protocol[T_co]): + def foo(self) -> T_co: ... + +class A: + def foo(self) -> A: ... +class B: + def foo(self) -> B: ... + +def foo(f: SupportsFoo[T_co]) -> T_co: ... + +ABT = TypeVar("ABT", bound=Union[A, B]) +def simpler(k: ABT): + foo(k) + [case testInferenceWorksWithEmptyCollectionsNested] from typing import List, TypeVar, NoReturn T = TypeVar('T') From 0291ec90d46655d47fcf220be7eab8b5f7c035e7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 28 Sep 2023 22:32:36 +0100 Subject: [PATCH 0291/1617] Better support for variadic calls and indexing (#16131) This improves support for two features that were supported but only partially: variadic calls, and variadic indexing. Some notes: * I did not add dedicated support for slicing of tuples with homogeneous variadic items (except for cases covered by TypeVarTuple support, i.e. those not involving splitting of variadic item). This is tricky and it is not clear what cases people actually want. I left a TODO for this. * I prohibit multiple variadic items in a call expression. Technically, we can support some situations involving these, but this is tricky, and prohibiting this would be in the "spirit" of the PEP, IMO. * I may have still missed some cases for the calls, since there are so many options. If you have ideas for additional test cases, please let me know. * It was necessary to fix overload ambiguity logic to make some tests pass. This goes beyond TypeVarTuple support, but I think this is a correct change. --- mypy/checkexpr.py | 156 ++++++++++++++++++++---- mypy/constraints.py | 31 +++-- mypy/erasetype.py | 4 +- mypy/message_registry.py | 1 + mypy/types.py | 55 +++++++-- test-data/unit/check-overloading.test | 3 +- test-data/unit/check-tuples.test | 5 +- test-data/unit/check-typevar-tuple.test | 108 ++++++++++++++-- 8 files changed, 306 insertions(+), 57 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 7b9b84938930..95ab75e24585 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1640,6 +1640,27 @@ def check_callable_call( callee.type_object().name, abstract_attributes, context ) + var_arg = callee.var_arg() + if var_arg and isinstance(var_arg.typ, UnpackType): + # It is hard to support multiple variadic unpacks (except for old-style *args: int), + # fail gracefully to avoid crashes later. + seen_unpack = False + for arg, arg_kind in zip(args, arg_kinds): + if arg_kind != ARG_STAR: + continue + arg_type = get_proper_type(self.accept(arg)) + if not isinstance(arg_type, TupleType) or any( + isinstance(t, UnpackType) for t in arg_type.items + ): + if seen_unpack: + self.msg.fail( + "Passing multiple variadic unpacks in a call is not supported", + context, + code=codes.CALL_ARG, + ) + return AnyType(TypeOfAny.from_error), callee + seen_unpack = True + formal_to_actual = map_actuals_to_formals( arg_kinds, arg_names, @@ -2405,7 +2426,7 @@ def check_argument_types( ] actual_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1) - # TODO: can we really assert this? What if formal is just plain Unpack[Ts]? + # If we got here, the callee was previously inferred to have a suffix. assert isinstance(orig_callee_arg_type, UnpackType) assert isinstance(orig_callee_arg_type.type, ProperType) and isinstance( orig_callee_arg_type.type, TupleType @@ -2431,22 +2452,29 @@ def check_argument_types( inner_unpack = unpacked_type.items[inner_unpack_index] assert isinstance(inner_unpack, UnpackType) inner_unpacked_type = get_proper_type(inner_unpack.type) - # We assume heterogenous tuples are desugared earlier - assert isinstance(inner_unpacked_type, Instance) - assert inner_unpacked_type.type.fullname == "builtins.tuple" - callee_arg_types = ( - unpacked_type.items[:inner_unpack_index] - + [inner_unpacked_type.args[0]] - * (len(actuals) - len(unpacked_type.items) + 1) - + unpacked_type.items[inner_unpack_index + 1 :] - ) - callee_arg_kinds = [ARG_POS] * len(actuals) + if isinstance(inner_unpacked_type, TypeVarTupleType): + # This branch mimics the expanded_tuple case above but for + # the case where caller passed a single * unpacked tuple argument. + callee_arg_types = unpacked_type.items + callee_arg_kinds = [ + ARG_POS if i != inner_unpack_index else ARG_STAR + for i in range(len(unpacked_type.items)) + ] + else: + # We assume heterogeneous tuples are desugared earlier. + assert isinstance(inner_unpacked_type, Instance) + assert inner_unpacked_type.type.fullname == "builtins.tuple" + callee_arg_types = ( + unpacked_type.items[:inner_unpack_index] + + [inner_unpacked_type.args[0]] + * (len(actuals) - len(unpacked_type.items) + 1) + + unpacked_type.items[inner_unpack_index + 1 :] + ) + callee_arg_kinds = [ARG_POS] * len(actuals) elif isinstance(unpacked_type, TypeVarTupleType): callee_arg_types = [orig_callee_arg_type] callee_arg_kinds = [ARG_STAR] else: - # TODO: Any and Never can appear in Unpack (as a result of user error), - # fail gracefully here and elsewhere (and/or normalize them away). assert isinstance(unpacked_type, Instance) assert unpacked_type.type.fullname == "builtins.tuple" callee_arg_types = [unpacked_type.args[0]] * len(actuals) @@ -2458,8 +2486,10 @@ def check_argument_types( assert len(actual_types) == len(actuals) == len(actual_kinds) if len(callee_arg_types) != len(actual_types): - # TODO: Improve error message - self.chk.fail("Invalid number of arguments", context) + if len(actual_types) > len(callee_arg_types): + self.chk.msg.too_many_arguments(callee, context) + else: + self.chk.msg.too_few_arguments(callee, context, None) continue assert len(callee_arg_types) == len(actual_types) @@ -2764,11 +2794,17 @@ def infer_overload_return_type( ) is_match = not w.has_new_errors() if is_match: - # Return early if possible; otherwise record info so we can + # Return early if possible; otherwise record info, so we can # check for ambiguity due to 'Any' below. if not args_contain_any: return ret_type, infer_type - matches.append(typ) + p_infer_type = get_proper_type(infer_type) + if isinstance(p_infer_type, CallableType): + # Prefer inferred types if possible, this will avoid false triggers for + # Any-ambiguity caused by arguments with Any passed to generic overloads. + matches.append(p_infer_type) + else: + matches.append(typ) return_types.append(ret_type) inferred_types.append(infer_type) type_maps.append(m) @@ -4109,6 +4145,12 @@ def visit_index_with_type( # Visit the index, just to make sure we have a type for it available self.accept(index) + if isinstance(left_type, TupleType) and any( + isinstance(it, UnpackType) for it in left_type.items + ): + # Normalize variadic tuples for consistency. + left_type = expand_type(left_type, {}) + if isinstance(left_type, UnionType): original_type = original_type or left_type # Don't combine literal types, since we may need them for type narrowing. @@ -4129,12 +4171,15 @@ def visit_index_with_type( if ns is not None: out = [] for n in ns: - if n < 0: - n += len(left_type.items) - if 0 <= n < len(left_type.items): - out.append(left_type.items[n]) + item = self.visit_tuple_index_helper(left_type, n) + if item is not None: + out.append(item) else: self.chk.fail(message_registry.TUPLE_INDEX_OUT_OF_RANGE, e) + if any(isinstance(t, UnpackType) for t in left_type.items): + self.chk.note( + f"Variadic tuple can have length {left_type.length() - 1}", e + ) return AnyType(TypeOfAny.from_error) return make_simplified_union(out) else: @@ -4158,6 +4203,46 @@ def visit_index_with_type( e.method_type = method_type return result + def visit_tuple_index_helper(self, left: TupleType, n: int) -> Type | None: + unpack_index = find_unpack_in_list(left.items) + if unpack_index is None: + if n < 0: + n += len(left.items) + if 0 <= n < len(left.items): + return left.items[n] + return None + unpack = left.items[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + if isinstance(unpacked, TypeVarTupleType): + # Usually we say that TypeVarTuple can't be split, be in case of + # indexing it seems benign to just return the fallback item, similar + # to what we do when indexing a regular TypeVar. + middle = unpacked.tuple_fallback.args[0] + else: + assert isinstance(unpacked, Instance) + assert unpacked.type.fullname == "builtins.tuple" + middle = unpacked.args[0] + if n >= 0: + if n < unpack_index: + return left.items[n] + if n >= len(left.items) - 1: + # For tuple[int, *tuple[str, ...], int] we allow either index 0 or 1, + # since variadic item may have zero items. + return None + return UnionType.make_union( + [middle] + left.items[unpack_index + 1 : n + 2], left.line, left.column + ) + n += len(left.items) + if n <= 0: + # Similar to above, we only allow -1, and -2 for tuple[int, *tuple[str, ...], int] + return None + if n > unpack_index: + return left.items[n] + return UnionType.make_union( + left.items[n - 1 : unpack_index] + [middle], left.line, left.column + ) + def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Type: begin: Sequence[int | None] = [None] end: Sequence[int | None] = [None] @@ -4183,7 +4268,11 @@ def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Typ items: list[Type] = [] for b, e, s in itertools.product(begin, end, stride): - items.append(left_type.slice(b, e, s)) + item = left_type.slice(b, e, s) + if item is None: + self.chk.fail(message_registry.AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE, slic) + return AnyType(TypeOfAny.from_error) + items.append(item) return make_simplified_union(items) def try_getting_int_literals(self, index: Expression) -> list[int] | None: @@ -4192,7 +4281,7 @@ def try_getting_int_literals(self, index: Expression) -> list[int] | None: Otherwise, returns None. Specifically, this function is guaranteed to return a list with - one or more ints if one one the following is true: + one or more ints if one the following is true: 1. 'expr' is a IntExpr or a UnaryExpr backed by an IntExpr 2. 'typ' is a LiteralType containing an int @@ -4223,11 +4312,30 @@ def try_getting_int_literals(self, index: Expression) -> list[int] | None: def nonliteral_tuple_index_helper(self, left_type: TupleType, index: Expression) -> Type: self.check_method_call_by_name("__getitem__", left_type, [index], [ARG_POS], context=index) # We could return the return type from above, but unions are often better than the join - union = make_simplified_union(left_type.items) + union = self.union_tuple_fallback_item(left_type) if isinstance(index, SliceExpr): return self.chk.named_generic_type("builtins.tuple", [union]) return union + def union_tuple_fallback_item(self, left_type: TupleType) -> Type: + # TODO: this duplicates logic in typeops.tuple_fallback(). + items = [] + for item in left_type.items: + if isinstance(item, UnpackType): + unpacked_type = get_proper_type(item.type) + if isinstance(unpacked_type, TypeVarTupleType): + unpacked_type = get_proper_type(unpacked_type.upper_bound) + if ( + isinstance(unpacked_type, Instance) + and unpacked_type.type.fullname == "builtins.tuple" + ): + items.append(unpacked_type.args[0]) + else: + raise NotImplementedError + else: + items.append(item) + return make_simplified_union(items) + def visit_typeddict_index_expr( self, td_type: TypedDictType, index: Expression, setitem: bool = False ) -> Type: diff --git a/mypy/constraints.py b/mypy/constraints.py index b61d882da3c4..ebd6765e8e82 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -137,25 +137,38 @@ def infer_constraints_for_callable( unpack_type = callee.arg_types[i] assert isinstance(unpack_type, UnpackType) - # In this case we are binding all of the actuals to *args + # In this case we are binding all the actuals to *args, # and we want a constraint that the typevar tuple being unpacked # is equal to a type list of all the actuals. actual_types = [] + + unpacked_type = get_proper_type(unpack_type.type) + if isinstance(unpacked_type, TypeVarTupleType): + tuple_instance = unpacked_type.tuple_fallback + elif isinstance(unpacked_type, TupleType): + tuple_instance = unpacked_type.partial_fallback + else: + assert False, "mypy bug: unhandled constraint inference case" + for actual in actuals: actual_arg_type = arg_types[actual] if actual_arg_type is None: continue - actual_types.append( - mapper.expand_actual_type( - actual_arg_type, - arg_kinds[actual], - callee.arg_names[i], - callee.arg_kinds[i], - ) + expanded_actual = mapper.expand_actual_type( + actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] ) - unpacked_type = get_proper_type(unpack_type.type) + if arg_kinds[actual] != ARG_STAR or isinstance( + get_proper_type(actual_arg_type), TupleType + ): + actual_types.append(expanded_actual) + else: + # If we are expanding an iterable inside * actual, append a homogeneous item instead + actual_types.append( + UnpackType(tuple_instance.copy_modified(args=[expanded_actual])) + ) + if isinstance(unpacked_type, TypeVarTupleType): constraints.append( Constraint( diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 24471f918319..7231ede66c65 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -82,7 +82,9 @@ def visit_instance(self, t: Instance) -> ProperType: # Valid erasure for *Ts is *tuple[Any, ...], not just Any. if isinstance(tv, TypeVarTupleType): args.append( - tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) + UnpackType( + tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) + ) ) else: args.append(AnyType(TypeOfAny.special_form)) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 713ec2e3c759..d75a1fab1b66 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -83,6 +83,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: INCOMPATIBLE_TYPES_IN_CAPTURE: Final = ErrorMessage("Incompatible types in capture pattern") MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None') TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range") +AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE: Final = ErrorMessage("Ambiguous slice of a variadic tuple") INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer, SupportsIndex or None") CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda") CANNOT_ACCESS_INIT: Final = ( diff --git a/mypy/types.py b/mypy/types.py index 2b5aec7789f7..9817043db6c2 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2416,14 +2416,53 @@ def copy_modified( items = self.items return TupleType(items, fallback, self.line, self.column) - def slice(self, begin: int | None, end: int | None, stride: int | None) -> TupleType: - return TupleType( - self.items[begin:end:stride], - self.partial_fallback, - self.line, - self.column, - self.implicit, - ) + def slice(self, begin: int | None, end: int | None, stride: int | None) -> TupleType | None: + if any(isinstance(t, UnpackType) for t in self.items): + total = len(self.items) + unpack_index = find_unpack_in_list(self.items) + assert unpack_index is not None + if begin is None and end is None: + # We special-case this to support reversing variadic tuples. + # General support for slicing is tricky, so we handle only simple cases. + if stride == -1: + slice_items = self.items[::-1] + elif stride is None or stride == 1: + slice_items = self.items + else: + return None + elif (begin is None or unpack_index >= begin >= 0) and ( + end is not None and unpack_index >= end >= 0 + ): + # Start and end are in the prefix, everything works in this case. + slice_items = self.items[begin:end:stride] + elif (begin is not None and unpack_index - total < begin < 0) and ( + end is None or unpack_index - total < end < 0 + ): + # Start and end are in the suffix, everything works in this case. + slice_items = self.items[begin:end:stride] + elif (begin is None or unpack_index >= begin >= 0) and ( + end is None or unpack_index - total < end < 0 + ): + # Start in the prefix, end in the suffix, we can support only trivial strides. + if stride is None or stride == 1: + slice_items = self.items[begin:end:stride] + else: + return None + elif (begin is not None and unpack_index - total < begin < 0) and ( + end is not None and unpack_index >= end >= 0 + ): + # Start in the suffix, end in the prefix, we can support only trivial strides. + if stride is None or stride == -1: + slice_items = self.items[begin:end:stride] + else: + return None + else: + # TODO: there some additional cases we can support for homogeneous variadic + # items, we can "eat away" finite number of items. + return None + else: + slice_items = self.items[begin:end:stride] + return TupleType(slice_items, self.partial_fallback, self.line, self.column, self.implicit) class TypedDictType(ProperType): diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 443a6fb5cb10..b97eeb48115c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6501,8 +6501,7 @@ eggs = lambda: 'eggs' reveal_type(func(eggs)) # N: Revealed type is "def (builtins.str) -> builtins.str" spam: Callable[..., str] = lambda x, y: 'baz' -reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> Any" - +reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> builtins.str" [builtins fixtures/paramspec.pyi] [case testGenericOverloadOverlapWithType] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index ed2c3550a04e..9dfee38bc0c6 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1678,7 +1678,6 @@ def zip(*i: Iterable[Any]) -> Iterator[Tuple[Any, ...]]: ... def zip(i): ... def g(t: Tuple): - # Ideally, we'd infer that these are iterators of tuples - reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[Any]" - reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Any]" + reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]" + reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Tuple[Any]]" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index d38d492fe9b2..e8d7966029e3 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -366,13 +366,25 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") -# TODO: add less trivial tests with prefix/suffix etc. -# TODO: add tests that call with a type var tuple instead of just args. def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: reveal_type(args) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" - return args + reveal_type(args_to_tuple(1, *args)) # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1]]" + reveal_type(args_to_tuple(*args, 'a')) # N: Revealed type is "Tuple[Unpack[Ts`-1], Literal['a']?]" + reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1], Literal['a']?]" + args_to_tuple(*args, *args) # E: Passing multiple variadic unpacks in a call is not supported + ok = (1, 'a') + reveal_type(args_to_tuple(*ok, *ok)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.int, builtins.str]" + if int(): + return args + else: + return args_to_tuple(*args) reveal_type(args_to_tuple(1, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Literal['a']?]" +vt: Tuple[int, ...] +reveal_type(args_to_tuple(1, *vt)) # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(args_to_tuple(*vt, 'a')) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" +reveal_type(args_to_tuple(1, *vt, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" +args_to_tuple(*vt, *vt) # E: Passing multiple variadic unpacks in a call is not supported [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgs] @@ -381,8 +393,17 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") +def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: + with_prefix_suffix(*args) # E: Too few arguments for "with_prefix_suffix" \ + # E: Argument 1 to "with_prefix_suffix" has incompatible type "*Tuple[Unpack[Ts]]"; expected "bool" + new_args = (True, "foo", *args, 5) + with_prefix_suffix(*new_args) + return args + def with_prefix_suffix(*args: Unpack[Tuple[bool, str, Unpack[Ts], int]]) -> Tuple[bool, str, Unpack[Ts], int]: reveal_type(args) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" + reveal_type(args_to_tuple(*args)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" + reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "Tuple[Literal[1]?, builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int, Literal['a']?]" return args reveal_type(with_prefix_suffix(True, "bar", "foo", 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" @@ -395,8 +416,7 @@ t = (True, "bar", "foo", 5) reveal_type(with_prefix_suffix(*t)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, builtins.str, builtins.int]" reveal_type(with_prefix_suffix(True, *("bar", "foo"), 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" -# TODO: handle list case -#reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5)) +reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[builtins.tuple[builtins.str, ...]], builtins.int]" bad_t = (True, "bar") with_prefix_suffix(*bad_t) # E: Too few arguments for "with_prefix_suffix" @@ -434,7 +454,7 @@ reveal_type(C().foo2) # N: Revealed type is "def (*args: Unpack[Tuple[builtins. [case testTypeVarTuplePep646TypeVarStarArgsVariableLengthTuple] from typing import Tuple -from typing_extensions import Unpack +from typing_extensions import Unpack, TypeVarTuple def foo(*args: Unpack[Tuple[int, ...]]) -> None: reveal_type(args) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -442,11 +462,28 @@ def foo(*args: Unpack[Tuple[int, ...]]) -> None: foo(0, 1, 2) foo(0, 1, "bar") # E: Argument 3 to "foo" has incompatible type "str"; expected "int" - def foo2(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]) -> None: reveal_type(args) # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.bool, builtins.bool]" - # TODO: generate an error - # reveal_type(args[1]) + reveal_type(args[1]) # N: Revealed type is "builtins.int" + +def foo3(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], str, float]]) -> None: + reveal_type(args[0]) # N: Revealed type is "builtins.str" + reveal_type(args[1]) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(args[2]) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.float]" + args[3] # E: Tuple index out of range \ + # N: Variadic tuple can have length 3 + reveal_type(args[-1]) # N: Revealed type is "builtins.float" + reveal_type(args[-2]) # N: Revealed type is "builtins.str" + reveal_type(args[-3]) # N: Revealed type is "Union[builtins.str, builtins.int]" + args[-4] # E: Tuple index out of range \ + # N: Variadic tuple can have length 3 + reveal_type(args[::-1]) # N: Revealed type is "Tuple[builtins.float, builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" + args[::2] # E: Ambiguous slice of a variadic tuple + args[:2] # E: Ambiguous slice of a variadic tuple + +Ts = TypeVarTuple("Ts") +def foo4(*args: Unpack[Tuple[str, Unpack[Ts], bool, bool]]) -> None: + reveal_type(args[1]) # N: Revealed type is "builtins.object" foo2("bar", 1, 2, 3, False, True) foo2(0, 1, 2, 3, False, True) # E: Argument 1 to "foo2" has incompatible type "int"; expected "str" @@ -908,7 +945,7 @@ def cons( return wrapped def star(f: Callable[[X], Y]) -> Callable[[Unpack[Tuple[X, ...]]], Tuple[Y, ...]]: - def wrapped(*xs: X): + def wrapped(*xs: X) -> Tuple[Y, ...]: if not xs: return nil() return cons(f, star(f))(*xs) @@ -1516,3 +1553,54 @@ def test(x: int, t: Tuple[T, ...]) -> Tuple[int, Unpack[Tuple[T, ...]]]: ... a: Any = test(42, ()) [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleIndexTypeVar] +from typing import Any, List, Sequence, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def f(data: Sequence[Tuple[Unpack[Ts]]]) -> List[Any]: + return [d[0] for d in data] # E: Tuple index out of range \ + # N: Variadic tuple can have length 0 + +T = TypeVar("T") +def g(data: Sequence[Tuple[T, Unpack[Ts]]]) -> List[T]: + return [d[0] for d in data] # OK +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleOverloadMatch] +from typing import Any, Generic, overload, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +_Ts = TypeVarTuple("_Ts") +_T = TypeVar("_T") +_T2 = TypeVar("_T2") + +class Container(Generic[_T]): ... +class Array(Generic[Unpack[_Ts]]): ... + +@overload +def build(entity: Container[_T], /) -> Array[_T]: ... +@overload +def build(entity: Container[_T], entity2: Container[_T2], /) -> Array[_T, _T2]: ... +@overload +def build(*entities: Container[Any]) -> Array[Unpack[Tuple[Any, ...]]]: ... +def build(*entities: Container[Any]) -> Array[Unpack[Tuple[Any, ...]]]: + ... + +def test(a: Container[Any], b: Container[int], c: Container[str]): + reveal_type(build(a, b)) # N: Revealed type is "__main__.Array[Any, builtins.int]" + reveal_type(build(b, c)) # N: Revealed type is "__main__.Array[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleIndexOldStyleNonNormalizedAndNonLiteral] +from typing import Any, Tuple +from typing_extensions import Unpack + +t: Tuple[Unpack[Tuple[int, ...]]] +reveal_type(t[42]) # N: Revealed type is "builtins.int" +i: int +reveal_type(t[i]) # N: Revealed type is "builtins.int" +t1: Tuple[int, Unpack[Tuple[int, ...]]] +reveal_type(t1[i]) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] From fddfc8dfb29ef9adec02f46eda8e92f74bdd7c9c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 28 Sep 2023 23:02:38 +0100 Subject: [PATCH 0292/1617] Fix walrus interaction with empty collections (#16197) This fixes a regression caused by https://github.com/python/mypy/pull/16122 --- mypy/checkexpr.py | 5 ++++- test-data/unit/check-python38.test | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 95ab75e24585..c132b35e5a2a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4093,7 +4093,10 @@ def visit_assignment_expr(self, e: AssignmentExpr) -> Type: value = self.accept(e.value) self.chk.check_assignment(e.target, e.value) self.chk.check_final(e) - self.chk.store_type(e.target, value) + if not has_uninhabited_component(value): + # TODO: can we get rid of this extra store_type()? + # Usually, check_assignment() already stores the lvalue type correctly. + self.chk.store_type(e.target, value) self.find_partial_type_ref_fast_path(e.target) return value diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index d83f29f2186a..1e99c760b67a 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -826,3 +826,11 @@ main:5: error: Dict entry 0 has incompatible type "str": "str"; expected "str": main:5: error: Unpacked dict entry 1 has incompatible type "Dict[str, str]"; expected "SupportsKeysAndGetItem[str, int]" dct: Dict[str, int] = {"a": "b", **other} ^~~~~ + +[case testWalrusAssignmentEmptyCollection] +from typing import List + +y: List[int] +if (y := []): + reveal_type(y) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] From 181cbe88f1396f2f52770f59b6bbb13c6521980a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 29 Sep 2023 00:42:38 +0100 Subject: [PATCH 0293/1617] Add more tests for variadic Callables (#16198) Supersedes https://github.com/python/mypy/pull/15254 Note the error message for one of the test is slightly different. Although it _may_ suggest that `Unpack[...]` is a valid type on its own, this error is kind of more consistent with old style `*args: int` annotations. --- test-data/unit/check-typevar-tuple.test | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index e8d7966029e3..850b7ef8a524 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1604,3 +1604,52 @@ reveal_type(t[i]) # N: Revealed type is "builtins.int" t1: Tuple[int, Unpack[Tuple[int, ...]]] reveal_type(t1[i]) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleNotConcreteCallable] +from typing_extensions import Unpack, TypeVarTuple +from typing import Callable, TypeVar, Tuple + +T = TypeVar("T") +Args = TypeVarTuple("Args") +Args2 = TypeVarTuple("Args2") + +def submit(fn: Callable[[Unpack[Args]], T], *args: Unpack[Args]) -> T: + ... + +def submit2(fn: Callable[[int, Unpack[Args]], T], *args: Unpack[Tuple[int, Unpack[Args]]]) -> T: + ... + +def foo(func: Callable[[Unpack[Args]], T], *args: Unpack[Args]) -> T: + return submit(func, *args) + +def foo2(func: Callable[[Unpack[Args2]], T], *args: Unpack[Args2]) -> T: + return submit(func, *args) + +def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: + return submit2(func, 1, *args) + +def foo_bad(func: Callable[[Unpack[Args2]], T], *args: Unpack[Args2]) -> T: + return submit2(func, 1, *args) # E: Argument 1 to "submit2" has incompatible type "Callable[[VarArg(Unpack[Args2])], T]"; expected "Callable[[int, VarArg(Unpack[Args2])], T]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleParamSpecInteraction] +from typing_extensions import Unpack, TypeVarTuple, ParamSpec +from typing import Callable, TypeVar + +T = TypeVar("T") +Args = TypeVarTuple("Args") +Args2 = TypeVarTuple("Args2") +P = ParamSpec("P") + +def submit(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + ... + +def foo(func: Callable[[Unpack[Args]], T], *args: Unpack[Args]) -> T: + return submit(func, *args) + +def foo2(func: Callable[[Unpack[Args]], T], *args: Unpack[Args2]) -> T: + return submit(func, *args) # E: Argument 2 to "submit" has incompatible type "*Tuple[Unpack[Args2]]"; expected "Unpack[Args]" + +def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: + return submit(func, 1, *args) +[builtins fixtures/tuple.pyi] From acccdd8a25b019e6b08180e2f95417a29651435e Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 30 Sep 2023 15:13:09 -0700 Subject: [PATCH 0294/1617] Fix error code on "Maybe you forgot to use await" note (#16203) Fixes #16202 --- mypy/checker.py | 6 +++--- mypy/checkexpr.py | 2 +- mypy/checkmember.py | 6 +++--- mypy/messages.py | 26 ++++++++++++++++++++------ test-data/unit/check-async-await.test | 27 +++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 95a65b0a8cd1..bdb636541db0 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6237,7 +6237,7 @@ def check_subtype( assert call is not None if not is_subtype(subtype, call, options=self.options): self.msg.note_call(supertype, call, context, code=msg.code) - self.check_possible_missing_await(subtype, supertype, context) + self.check_possible_missing_await(subtype, supertype, context, code=msg.code) return False def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None: @@ -6271,7 +6271,7 @@ def checking_await_set(self) -> Iterator[None]: self.checking_missing_await = False def check_possible_missing_await( - self, subtype: Type, supertype: Type, context: Context + self, subtype: Type, supertype: Type, context: Context, code: ErrorCode | None ) -> None: """Check if the given type becomes a subtype when awaited.""" if self.checking_missing_await: @@ -6285,7 +6285,7 @@ def check_possible_missing_await( aw_type, supertype, context, msg=message_registry.INCOMPATIBLE_TYPES ): return - self.msg.possible_missing_await(context) + self.msg.possible_missing_await(context, code) def contains_none(self, t: Type) -> bool: t = get_proper_type(t) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c132b35e5a2a..df4077100efb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2563,7 +2563,7 @@ def check_arg( original_caller_type, callee_type, context, code=code ) if not self.msg.prefer_simple_messages(): - self.chk.check_possible_missing_await(caller_type, callee_type, context) + self.chk.check_possible_missing_await(caller_type, callee_type, context, code) def check_overload_call( self, diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 1557b62917dc..5a4f3875ad04 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -272,11 +272,11 @@ def report_missing_attribute( mx: MemberContext, override_info: TypeInfo | None = None, ) -> Type: - res_type = mx.msg.has_no_attr(original_type, typ, name, mx.context, mx.module_symbol_table) + error_code = 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): - mx.msg.possible_missing_await(mx.context) - return res_type + mx.msg.possible_missing_await(mx.context, error_code) + return AnyType(TypeOfAny.from_error) # The several functions that follow implement analyze_member_access for various diff --git a/mypy/messages.py b/mypy/messages.py index 8bc190b7d66d..47ebd94f3d21 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -355,7 +355,7 @@ def has_no_attr( member: str, context: Context, module_symbol_table: SymbolTable | None = None, - ) -> Type: + ) -> ErrorCode | None: """Report a missing or non-accessible member. original_type is the top-level type on which the error occurred. @@ -370,44 +370,49 @@ def has_no_attr( directly available on original_type If member corresponds to an operator, use the corresponding operator - name in the messages. Return type Any. + name in the messages. Return the error code that was produced, if any. """ original_type = get_proper_type(original_type) typ = get_proper_type(typ) if isinstance(original_type, Instance) and original_type.type.has_readable_member(member): self.fail(f'Member "{member}" is not assignable', context) + return None elif member == "__contains__": self.fail( f"Unsupported right operand type for in ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) + return codes.OPERATOR elif member in op_methods.values(): # Access to a binary operator member (e.g. _add). This case does # not handle indexing operations. for op, method in op_methods.items(): if method == member: self.unsupported_left_operand(op, original_type, context) - break + return codes.OPERATOR elif member == "__neg__": self.fail( f"Unsupported operand type for unary - ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) + return codes.OPERATOR elif member == "__pos__": self.fail( f"Unsupported operand type for unary + ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) + return codes.OPERATOR elif member == "__invert__": self.fail( f"Unsupported operand type for ~ ({format_type(original_type, self.options)})", context, code=codes.OPERATOR, ) + return codes.OPERATOR elif member == "__getitem__": # Indexed get. # TODO: Fix this consistently in format_type @@ -418,12 +423,14 @@ def has_no_attr( ), context, ) + return None else: self.fail( f"Value of type {format_type(original_type, self.options)} is not indexable", context, code=codes.INDEX, ) + return codes.INDEX elif member == "__setitem__": # Indexed set. self.fail( @@ -433,6 +440,7 @@ def has_no_attr( context, code=codes.INDEX, ) + return codes.INDEX elif member == "__call__": if isinstance(original_type, Instance) and ( original_type.type.fullname == "builtins.function" @@ -440,12 +448,14 @@ def has_no_attr( # "'function' not callable" is a confusing error message. # Explain that the problem is that the type of the function is not known. self.fail("Cannot call function of unknown type", context, code=codes.OPERATOR) + return codes.OPERATOR else: self.fail( message_registry.NOT_CALLABLE.format(format_type(original_type, self.options)), context, code=codes.OPERATOR, ) + return codes.OPERATOR else: # The non-special case: a missing ordinary attribute. extra = "" @@ -501,6 +511,7 @@ def has_no_attr( context, code=codes.ATTR_DEFINED, ) + return codes.ATTR_DEFINED elif isinstance(original_type, UnionType): # The checker passes "object" in lieu of "None" for attribute # checks, so we manually convert it back. @@ -518,6 +529,7 @@ def has_no_attr( context, code=codes.UNION_ATTR, ) + return codes.UNION_ATTR elif isinstance(original_type, TypeVarType): bound = get_proper_type(original_type.upper_bound) if isinstance(bound, UnionType): @@ -531,6 +543,7 @@ def has_no_attr( context, code=codes.UNION_ATTR, ) + return codes.UNION_ATTR else: self.fail( '{} has no attribute "{}"{}'.format( @@ -539,7 +552,8 @@ def has_no_attr( context, code=codes.ATTR_DEFINED, ) - return AnyType(TypeOfAny.from_error) + return codes.ATTR_DEFINED + return None def unsupported_operand_types( self, @@ -1107,8 +1121,8 @@ def unpacking_strings_disallowed(self, context: Context) -> None: def type_not_iterable(self, type: Type, context: Context) -> None: 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) + def possible_missing_await(self, context: Context, code: ErrorCode | None) -> None: + self.note('Maybe you forgot to use "await"?', context, code=code) def incompatible_operator_assignment(self, op: str, context: Context) -> None: self.fail(f"Result type of {op} incompatible in assignment", context) diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 7afdbd687135..f0fa206645dd 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -165,6 +165,33 @@ async def f() -> None: [out] main:4: error: "List[int]" has no attribute "__aiter__" (not async iterable) +[case testAsyncForErrorNote] + +from typing import AsyncIterator, AsyncGenerator +async def g() -> AsyncGenerator[str, None]: + pass + +async def f() -> None: + async for x in g(): + pass +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] +[out] +main:7: error: "Coroutine[Any, Any, AsyncGenerator[str, None]]" has no attribute "__aiter__" (not async iterable) +main:7: note: Maybe you forgot to use "await"? + +[case testAsyncForErrorCanBeIgnored] + +from typing import AsyncIterator, AsyncGenerator +async def g() -> AsyncGenerator[str, None]: + pass + +async def f() -> None: + async for x in g(): # type: ignore[attr-defined] + pass +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] + [case testAsyncForTypeComments] from typing import AsyncIterator, Union From 7a62481c4ed4007a0323118d3e1b8727b2136434 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:05:55 -0700 Subject: [PATCH 0295/1617] Sync typeshed (#16206) Source commit: https://github.com/python/typeshed/commit/559d31c4a33045310a30843dd7fac88a62cc5915 --- mypy/typeshed/stdlib/_ctypes.pyi | 6 ++ mypy/typeshed/stdlib/_curses.pyi | 23 ++--- mypy/typeshed/stdlib/_posixsubprocess.pyi | 16 +-- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 8 +- mypy/typeshed/stdlib/abc.pyi | 3 +- mypy/typeshed/stdlib/ast.pyi | 8 ++ mypy/typeshed/stdlib/builtins.pyi | 74 ++++++++------ mypy/typeshed/stdlib/codecs.pyi | 8 +- mypy/typeshed/stdlib/collections/__init__.pyi | 9 ++ mypy/typeshed/stdlib/fcntl.pyi | 5 + mypy/typeshed/stdlib/http/server.pyi | 1 + mypy/typeshed/stdlib/logging/handlers.pyi | 2 + mypy/typeshed/stdlib/mmap.pyi | 2 + mypy/typeshed/stdlib/multiprocessing/util.pyi | 12 +-- mypy/typeshed/stdlib/os/__init__.pyi | 98 +++++++++++++++---- mypy/typeshed/stdlib/posix.pyi | 42 ++++++++ mypy/typeshed/stdlib/resource.pyi | 9 +- mypy/typeshed/stdlib/signal.pyi | 8 +- mypy/typeshed/stdlib/ssl.pyi | 2 - mypy/typeshed/stdlib/sys.pyi | 5 +- mypy/typeshed/stdlib/syslog.pyi | 6 +- mypy/typeshed/stdlib/termios.pyi | 10 +- mypy/typeshed/stdlib/tty.pyi | 15 ++- mypy/typeshed/stdlib/types.pyi | 40 ++++---- mypy/typeshed/stdlib/typing.pyi | 4 +- mypy/typeshed/stdlib/typing_extensions.pyi | 7 ++ mypy/typeshed/stdlib/unittest/case.pyi | 4 +- mypy/typeshed/stdlib/xml/sax/__init__.pyi | 19 ++-- mypy/typeshed/stdlib/xml/sax/handler.pyi | 27 ++--- mypy/typeshed/stdlib/xml/sax/saxutils.pyi | 64 ++++++------ mypy/typeshed/stdlib/xml/sax/xmlreader.pyi | 82 +++++++++------- mypy/typeshed/stdlib/xxlimited.pyi | 2 + 32 files changed, 404 insertions(+), 217 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 538c07d54aad..b48b1f7d318c 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -56,6 +56,12 @@ class _CData(metaclass=_CDataMeta): _b_base_: int _b_needsfree_: bool _objects: Mapping[Any, int] | None + # At runtime the following classmethods are available only on classes, not + # on instances. This can't be reflected properly in the type system: + # + # Structure.from_buffer(...) # valid at runtime + # Structure(...).from_buffer(...) # invalid at runtime + # @classmethod def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... @classmethod diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 61881fc09199..e2319a5fcc1f 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -276,12 +276,7 @@ if sys.platform != "win32": def can_change_color() -> bool: ... def cbreak(__flag: bool = True) -> None: ... def color_content(__color_number: int) -> tuple[int, int, int]: ... - # Changed in Python 3.8.8 and 3.9.2 - if sys.version_info >= (3, 8): - def color_pair(pair_number: int) -> int: ... - else: - def color_pair(__color_number: int) -> int: ... - + def color_pair(__pair_number: int) -> int: ... def curs_set(__visibility: int) -> int: ... def def_prog_mode() -> None: ... def def_shell_mode() -> None: ... @@ -366,7 +361,10 @@ if sys.platform != "win32": ) -> bytes: ... def typeahead(__fd: int) -> None: ... def unctrl(__ch: _ChType) -> bytes: ... - def unget_wch(__ch: int | str) -> None: ... + if sys.version_info < (3, 12) or sys.platform != "darwin": + # The support for macos was dropped in 3.12 + def unget_wch(__ch: int | str) -> None: ... + def ungetch(__ch: _ChType) -> None: ... def ungetmouse(__id: int, __x: int, __y: int, __z: int, __bstate: int) -> None: ... def update_lines_cols() -> None: ... @@ -441,10 +439,13 @@ if sys.platform != "win32": def getch(self) -> int: ... @overload def getch(self, y: int, x: int) -> int: ... - @overload - def get_wch(self) -> int | str: ... - @overload - def get_wch(self, y: int, x: int) -> int | str: ... + if sys.version_info < (3, 12) or sys.platform != "darwin": + # The support for macos was dropped in 3.12 + @overload + def get_wch(self) -> int | str: ... + @overload + def get_wch(self, y: int, x: int) -> int | str: ... + @overload def getkey(self) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index ca95336bb503..1708063720ba 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -6,15 +6,15 @@ from typing_extensions import SupportsIndex if sys.platform != "win32": def cloexec_pipe() -> tuple[int, int]: ... def fork_exec( - __process_args: Sequence[StrOrBytesPath] | None, + __args: Sequence[StrOrBytesPath] | None, __executable_list: Sequence[bytes], __close_fds: bool, - __fds_to_keep: tuple[int, ...], - __cwd_obj: str, - __env_list: Sequence[bytes] | None, + __pass_fds: tuple[int, ...], + __cwd: str, + __env: Sequence[bytes] | None, __p2cread: int, __p2cwrite: int, - __c2pred: int, + __c2pread: int, __c2pwrite: int, __errread: int, __errwrite: int, @@ -23,9 +23,9 @@ if sys.platform != "win32": __restore_signals: int, __call_setsid: int, __pgid_to_set: int, - __gid_object: SupportsIndex | None, - __groups_list: list[int] | None, - __uid_object: SupportsIndex | None, + __gid: SupportsIndex | None, + __extra_groups: list[int] | None, + __uid: SupportsIndex | None, __child_umask: int, __preexec_fn: Callable[[], None], __allow_vfork: bool, diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 7ae67292e8cd..8e92138c748a 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -7,8 +7,8 @@ from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as Abst from dataclasses import Field from os import PathLike from types import FrameType, TracebackType -from typing import Any, AnyStr, ClassVar, Generic, Protocol, TypeVar, overload -from typing_extensions import Buffer, Final, Literal, LiteralString, TypeAlias, final +from typing import Any, AnyStr, ClassVar, Generic, Protocol, SupportsFloat, SupportsInt, TypeVar, overload +from typing_extensions import Buffer, Final, Literal, LiteralString, SupportsIndex, TypeAlias, final _KT = TypeVar("_KT") _KT_co = TypeVar("_KT_co", covariant=True) @@ -312,3 +312,7 @@ TraceFunction: TypeAlias = Callable[[FrameType, str, Any], TraceFunction | None] # https://github.com/microsoft/pyright/issues/4339 class DataclassInstance(Protocol): __dataclass_fields__: ClassVar[dict[str, Field[Any]]] + +# Anything that can be passed to the int/float constructors +ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc +ConvertibleToFloat: TypeAlias = str | ReadableBuffer | SupportsFloat | SupportsIndex diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index 43893a298341..7fe1d09f7589 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -40,7 +40,8 @@ class abstractstaticmethod(staticmethod[_P, _R_co]): class abstractproperty(property): __isabstractmethod__: Literal[True] -class ABC(metaclass=ABCMeta): ... +class ABC(metaclass=ABCMeta): + __slots__ = () def get_cache_token() -> object: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 377138141340..a61b4e35fd56 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -138,8 +138,10 @@ class NodeVisitor: def visit_withitem(self, node: withitem) -> Any: ... if sys.version_info >= (3, 10): def visit_Match(self, node: Match) -> Any: ... + def visit_match_case(self, node: match_case) -> Any: ... def visit_MatchValue(self, node: MatchValue) -> Any: ... def visit_MatchSequence(self, node: MatchSequence) -> Any: ... + def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ... def visit_MatchStar(self, node: MatchStar) -> Any: ... def visit_MatchMapping(self, node: MatchMapping) -> Any: ... def visit_MatchClass(self, node: MatchClass) -> Any: ... @@ -149,6 +151,12 @@ class NodeVisitor: if sys.version_info >= (3, 11): def visit_TryStar(self, node: TryStar) -> Any: ... + if sys.version_info >= (3, 12): + def visit_TypeVar(self, node: TypeVar) -> Any: ... + def visit_ParamSpec(self, node: ParamSpec) -> Any: ... + def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ... + def visit_TypeAlias(self, node: TypeAlias) -> Any: ... + # visit methods for deprecated nodes def visit_ExtSlice(self, node: ExtSlice) -> Any: ... def visit_Index(self, node: Index) -> Any: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 9e413579e0fb..dedd72933028 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -5,6 +5,8 @@ import types from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import ( AnyStr_co, + ConvertibleToFloat, + ConvertibleToInt, FileDescriptorOrPath, OpenBinaryMode, OpenBinaryModeReading, @@ -24,7 +26,6 @@ from _typeshed import ( SupportsRDivMod, SupportsRichComparison, SupportsRichComparisonT, - SupportsTrunc, SupportsWrite, ) from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized @@ -48,7 +49,6 @@ from typing import ( # noqa: Y022 SupportsBytes, SupportsComplex, SupportsFloat, - SupportsInt, TypeVar, overload, type_check_only, @@ -220,7 +220,7 @@ _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0] # noqa: Y026 class int: @overload - def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> Self: ... + def __new__(cls, __x: ConvertibleToInt = ...) -> Self: ... @overload def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> Self: ... if sys.version_info >= (3, 8): @@ -326,7 +326,7 @@ class int: def __index__(self) -> int: ... class float: - def __new__(cls, __x: SupportsFloat | SupportsIndex | str | ReadableBuffer = ...) -> Self: ... + def __new__(cls, __x: ConvertibleToFloat = ...) -> Self: ... def as_integer_ratio(self) -> tuple[int, int]: ... def hex(self) -> str: ... def is_integer(self) -> bool: ... @@ -774,7 +774,7 @@ class memoryview(Sequence[int]): def contiguous(self) -> bool: ... @property def nbytes(self) -> int: ... - def __init__(self, obj: ReadableBuffer) -> None: ... + def __new__(cls, obj: ReadableBuffer) -> Self: ... def __enter__(self) -> Self: ... def __exit__( self, __exc_type: type[BaseException] | None, __exc_val: BaseException | None, __exc_tb: TracebackType | None @@ -853,9 +853,9 @@ class slice: @property def stop(self) -> Any: ... @overload - def __init__(self, __stop: Any) -> None: ... + def __new__(cls, __stop: Any) -> Self: ... @overload - def __init__(self, __start: Any, __stop: Any, __step: Any = ...) -> None: ... + def __new__(cls, __start: Any, __stop: Any, __step: Any = ...) -> Self: ... def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] def indices(self, __len: SupportsIndex) -> tuple[int, int, int]: ... @@ -1110,7 +1110,7 @@ class frozenset(AbstractSet[_T_co], Generic[_T_co]): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... class enumerate(Iterator[tuple[int, _T]], Generic[_T]): - def __init__(self, iterable: Iterable[_T], start: int = ...) -> None: ... + def __new__(cls, iterable: Iterable[_T], start: int = ...) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... if sys.version_info >= (3, 9): @@ -1125,9 +1125,9 @@ class range(Sequence[int]): @property def step(self) -> int: ... @overload - def __init__(self, __stop: SupportsIndex) -> None: ... + def __new__(cls, __stop: SupportsIndex) -> Self: ... @overload - def __init__(self, __start: SupportsIndex, __stop: SupportsIndex, __step: SupportsIndex = ...) -> None: ... + def __new__(cls, __start: SupportsIndex, __stop: SupportsIndex, __step: SupportsIndex = ...) -> Self: ... def count(self, __value: int) -> int: ... def index(self, __value: int) -> int: ... # type: ignore[override] def __len__(self) -> int: ... @@ -1320,11 +1320,11 @@ def exit(code: sys._ExitCode = None) -> NoReturn: ... class filter(Iterator[_T], Generic[_T]): @overload - def __init__(self, __function: None, __iterable: Iterable[_T | None]) -> None: ... + def __new__(cls, __function: None, __iterable: Iterable[_T | None]) -> Self: ... @overload - def __init__(self, __function: Callable[[_S], TypeGuard[_T]], __iterable: Iterable[_S]) -> None: ... + def __new__(cls, __function: Callable[[_S], TypeGuard[_T]], __iterable: Iterable[_S]) -> Self: ... @overload - def __init__(self, __function: Callable[[_T], Any], __iterable: Iterable[_T]) -> None: ... + def __new__(cls, __function: Callable[[_T], Any], __iterable: Iterable[_T]) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -1379,35 +1379,35 @@ def locals() -> dict[str, Any]: ... class map(Iterator[_S], Generic[_S]): @overload - def __init__(self, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> None: ... + def __new__(cls, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> Self: ... @overload - def __init__(self, __func: Callable[[_T1, _T2], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> None: ... + def __new__(cls, __func: Callable[[_T1, _T2], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> Self: ... @overload - def __init__( - self, __func: Callable[[_T1, _T2, _T3], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] - ) -> None: ... + def __new__( + cls, __func: Callable[[_T1, _T2, _T3], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] + ) -> Self: ... @overload - def __init__( - self, + def __new__( + cls, __func: Callable[[_T1, _T2, _T3, _T4], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4], - ) -> None: ... + ) -> Self: ... @overload - def __init__( - self, + def __new__( + cls, __func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4], __iter5: Iterable[_T5], - ) -> None: ... + ) -> Self: ... @overload - def __init__( - self, + def __new__( + cls, __func: Callable[..., _S], __iter1: Iterable[Any], __iter2: Iterable[Any], @@ -1416,7 +1416,7 @@ class map(Iterator[_S], Generic[_S]): __iter5: Iterable[Any], __iter6: Iterable[Any], *iterables: Iterable[Any], - ) -> None: ... + ) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _S: ... @@ -1725,6 +1725,8 @@ def vars(__object: Any = ...) -> dict[str, Any]: ... class zip(Iterator[_T_co], Generic[_T_co]): if sys.version_info >= (3, 10): + @overload + def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... @overload def __new__(cls, __iter1: Iterable[_T1], *, strict: bool = ...) -> zip[tuple[_T1]]: ... @overload @@ -1767,6 +1769,8 @@ class zip(Iterator[_T_co], Generic[_T_co]): strict: bool = ..., ) -> zip[tuple[Any, ...]]: ... else: + @overload + def __new__(cls) -> zip[Any]: ... @overload def __new__(cls, __iter1: Iterable[_T1]) -> zip[tuple[_T1]]: ... @overload @@ -1812,11 +1816,17 @@ def __import__( ) -> types.ModuleType: ... def __build_class__(__func: Callable[[], _Cell | Any], __name: str, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... -# Actually the type of Ellipsis is , but since it's -# not exposed anywhere under that name, we make it private here. -@final -@type_check_only -class ellipsis: ... +if sys.version_info >= (3, 10): + # In Python 3.10, EllipsisType is exposed publicly in the types module. + @final + class ellipsis: ... + +else: + # Actually the type of Ellipsis is , but since it's + # not exposed anywhere under that name, we make it private here. + @final + @type_check_only + class ellipsis: ... Ellipsis: ellipsis diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index c9b6a4a82da6..f8c92392a599 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -78,16 +78,16 @@ class _Stream(_WritableStream, _ReadableStream, Protocol): ... # They were much more common in Python 2 than in Python 3. class _Encoder(Protocol): - def __call__(self, input: str, errors: str = ...) -> tuple[bytes, int]: ... # signature of Codec().encode + def __call__(self, __input: str, __errors: str = ...) -> tuple[bytes, int]: ... # signature of Codec().encode class _Decoder(Protocol): - def __call__(self, input: bytes, errors: str = ...) -> tuple[str, int]: ... # signature of Codec().decode + def __call__(self, __input: bytes, __errors: str = ...) -> tuple[str, int]: ... # signature of Codec().decode class _StreamReader(Protocol): - def __call__(self, stream: _ReadableStream, errors: str = ...) -> StreamReader: ... + def __call__(self, __stream: _ReadableStream, __errors: str = ...) -> StreamReader: ... class _StreamWriter(Protocol): - def __call__(self, stream: _WritableStream, errors: str = ...) -> StreamWriter: ... + def __call__(self, __stream: _WritableStream, __errors: str = ...) -> StreamWriter: ... class _IncrementalEncoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalEncoder: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 3b8d92f78612..1d560117a54f 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -373,6 +373,15 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): @overload def setdefault(self, key: _KT, default: _VT) -> _VT: ... def __eq__(self, __value: object) -> bool: ... + if sys.version_info >= (3, 9): + @overload + def __or__(self, __value: dict[_KT, _VT]) -> Self: ... + @overload + def __or__(self, __value: dict[_T1, _T2]) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, __value: dict[_KT, _VT]) -> Self: ... + @overload + def __ror__(self, __value: dict[_T1, _T2]) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): default_factory: Callable[[], _VT] | None diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 6aec7515f330..56fd5679a1c8 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -101,6 +101,11 @@ if sys.platform != "win32": I_STR: int I_SWROPT: int I_UNLINK: int + + if sys.version_info >= (3, 12) and sys.platform == "linux": + FICLONE: int + FICLONERANGE: int + @overload def fcntl(__fd: FileDescriptorLike, __cmd: int, __arg: int = 0) -> int: ... @overload diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi index c9700f70e791..22c33bc3787a 100644 --- a/mypy/typeshed/stdlib/http/server.pyi +++ b/mypy/typeshed/stdlib/http/server.pyi @@ -54,6 +54,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): extensions_map: dict[str, str] if sys.version_info >= (3, 12): index_pages: ClassVar[tuple[str, ...]] + directory: str def __init__( self, request: socketserver._RequestType, diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index ad5bf392b50f..2280dbad4c5d 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -7,6 +7,7 @@ from collections.abc import Callable from logging import FileHandler, Handler, LogRecord from re import Pattern from socket import SocketKind, socket +from threading import Thread from typing import Any, ClassVar, Protocol, TypeVar _T = TypeVar("_T") @@ -264,6 +265,7 @@ class QueueListener: handlers: tuple[Handler, ...] # undocumented respect_handler_level: bool # undocumented queue: _QueueLike[Any] # undocumented + _thread: Thread | None # undocumented def __init__(self, queue: _QueueLike[Any], *handlers: Handler, respect_handler_level: bool = False) -> None: ... def dequeue(self, block: bool) -> LogRecord: ... def prepare(self, record: LogRecord) -> Any: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 38e1924392c4..09319980692f 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -16,6 +16,8 @@ if sys.platform == "linux": MAP_EXECUTABLE: int if sys.version_info >= (3, 10): MAP_POPULATE: int +if sys.version_info >= (3, 11) and sys.platform != "win32" and sys.platform != "darwin": + MAP_STACK: int if sys.platform != "win32": MAP_ANON: int diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index 7ca650511e51..aeb46f85a327 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -1,9 +1,8 @@ import threading -from _typeshed import Incomplete, ReadableBuffer, SupportsTrunc, Unused +from _typeshed import ConvertibleToInt, Incomplete, Unused from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence from logging import Logger, _Level as _LoggingLevel -from typing import Any, SupportsInt -from typing_extensions import SupportsIndex +from typing import Any __all__ = [ "sub_debug", @@ -77,9 +76,4 @@ class ForkAwareLocal(threading.local): ... MAXFD: int def close_all_fds_except(fds: Iterable[int]) -> None: ... -def spawnv_passfds( - path: bytes, - # args is anything that can be passed to the int constructor - args: Sequence[str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc], - passfds: Sequence[int], -) -> int: ... +def spawnv_passfds(path: bytes, args: Sequence[ConvertibleToInt], passfds: Sequence[int]) -> int: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 961858ce3c19..fa4c55011eba 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -2,6 +2,7 @@ import sys from _typeshed import ( AnyStr_co, BytesPath, + FileDescriptor, FileDescriptorLike, FileDescriptorOrPath, GenericPath, @@ -121,6 +122,12 @@ if sys.platform == "linux": GRND_NONBLOCK: int GRND_RANDOM: int +if sys.platform == "darwin" and sys.version_info >= (3, 12): + PRIO_DARWIN_BG: int + PRIO_DARWIN_NONUI: int + PRIO_DARWIN_PROCESS: int + PRIO_DARWIN_THREAD: int + SEEK_SET: int SEEK_CUR: int SEEK_END: int @@ -252,12 +259,14 @@ environ: _Environ[str] if sys.platform != "win32": environb: _Environ[bytes] +if sys.version_info >= (3, 11) or sys.platform != "win32": + EX_OK: int + if sys.platform != "win32": confstr_names: dict[str, int] pathconf_names: dict[str, int] sysconf_names: dict[str, int] - EX_OK: int EX_USAGE: int EX_DATAERR: int EX_NOINPUT: int @@ -339,6 +348,11 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo if sys.version_info >= (3, 8): @property def st_reparse_tag(self) -> int: ... + if sys.version_info >= (3, 12): + @property + def st_birthtime(self) -> float: ... # time of file creation in seconds + @property + def st_birthtime_ns(self) -> int: ... # time of file creation in nanoseconds else: @property def st_blocks(self) -> int: ... # number of blocks allocated for file @@ -347,13 +361,13 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo @property def st_rdev(self) -> int: ... # type of device if an inode device if sys.platform != "linux": - # These properties are available on MacOS, but not on Windows or Ubuntu. + # These properties are available on MacOS, but not Ubuntu. # On other Unix systems (such as FreeBSD), the following attributes may be # available (but may be only filled out if root tries to use them): @property def st_gen(self) -> int: ... # file generation number @property - def st_birthtime(self) -> int: ... # time of file creation + def st_birthtime(self) -> float: ... # time of file creation in seconds if sys.platform == "darwin": @property def st_flags(self) -> int: ... # user defined flags for file @@ -484,8 +498,8 @@ if sys.platform != "win32": def setpgid(__pid: int, __pgrp: int) -> None: ... def setregid(__rgid: int, __egid: int) -> None: ... if sys.platform != "darwin": - def setresgid(rgid: int, egid: int, sgid: int) -> None: ... - def setresuid(ruid: int, euid: int, suid: int) -> None: ... + def setresgid(__rgid: int, __egid: int, __sgid: int) -> None: ... + def setresuid(__ruid: int, __euid: int, __suid: int) -> None: ... def setreuid(__ruid: int, __euid: int) -> None: ... def getsid(__pid: int) -> int: ... @@ -614,13 +628,15 @@ def open(path: StrOrBytesPath, flags: int, mode: int = 0o777, *, dir_fd: int | N def pipe() -> tuple[int, int]: ... def read(__fd: int, __length: int) -> bytes: ... +if sys.version_info >= (3, 12) or sys.platform != "win32": + def get_blocking(__fd: int) -> bool: ... + def set_blocking(__fd: int, __blocking: bool) -> None: ... + if sys.platform != "win32": def fchmod(fd: int, mode: int) -> None: ... def fchown(fd: int, uid: int, gid: int) -> None: ... def fpathconf(__fd: int, __name: str | int) -> int: ... def fstatvfs(__fd: int) -> statvfs_result: ... - def get_blocking(__fd: int) -> bool: ... - def set_blocking(__fd: int, __blocking: bool) -> None: ... def lockf(__fd: int, __command: int, __length: int) -> None: ... def openpty() -> tuple[int, int]: ... # some flavors of Unix if sys.platform != "darwin": @@ -641,18 +657,20 @@ if sys.platform != "win32": RWF_SYNC: int RWF_HIPRI: int RWF_NOWAIT: int - @overload - def sendfile(out_fd: int, in_fd: int, offset: int | None, count: int) -> int: ... - @overload - def sendfile( - out_fd: int, - in_fd: int, - offset: int, - count: int, - headers: Sequence[ReadableBuffer] = ..., - trailers: Sequence[ReadableBuffer] = ..., - flags: int = 0, - ) -> int: ... # FreeBSD and Mac OS X only + + if sys.platform == "linux": + def sendfile(out_fd: FileDescriptor, in_fd: FileDescriptor, offset: int | None, count: int) -> int: ... + else: + def sendfile( + out_fd: FileDescriptor, + in_fd: FileDescriptor, + offset: int, + count: int, + headers: Sequence[ReadableBuffer] = ..., + trailers: Sequence[ReadableBuffer] = ..., + flags: int = 0, + ) -> int: ... # FreeBSD and Mac OS X only + def readv(__fd: int, __buffers: SupportsLenAndGetItem[WriteableBuffer]) -> int: ... def writev(__fd: int, __buffers: SupportsLenAndGetItem[ReadableBuffer]) -> int: ... @@ -1042,3 +1060,45 @@ if sys.version_info >= (3, 9): if sys.platform == "linux": def pidfd_open(pid: int, flags: int = ...) -> int: ... + +if sys.version_info >= (3, 12) and sys.platform == "win32": + def listdrives() -> list[str]: ... + def listmounts(volume: str) -> list[str]: ... + def listvolumes() -> list[str]: ... + +if sys.version_info >= (3, 10) and sys.platform == "linux": + EFD_CLOEXEC: int + EFD_NONBLOCK: int + EFD_SEMAPHORE: int + SPLICE_F_MORE: int + SPLICE_F_MOVE: int + SPLICE_F_NONBLOCK: int + def eventfd(initval: int, flags: int = 524288) -> FileDescriptor: ... + def eventfd_read(fd: FileDescriptor) -> int: ... + def eventfd_write(fd: FileDescriptor, value: int) -> None: ... + def splice( + src: FileDescriptor, + dst: FileDescriptor, + count: int, + offset_src: int | None = ..., + offset_dst: int | None = ..., + flags: int = 0, + ) -> int: ... + +if sys.version_info >= (3, 12) and sys.platform == "linux": + CLONE_FILES: int + CLONE_FS: int + CLONE_NEWCGROUP: int + CLONE_NEWIPC: int + CLONE_NEWNET: int + CLONE_NEWNS: int + CLONE_NEWPID: int + CLONE_NEWTIME: int + CLONE_NEWUSER: int + CLONE_NEWUTS: int + CLONE_SIGHAND: int + CLONE_SYSVSEM: int + CLONE_THREAD: int + CLONE_VM: int + def unshare(flags: int) -> None: ... + def setns(fd: FileDescriptorLike, nstype: int = 0) -> None: ... diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index ffd96757586b..ab6bf2e63be5 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -236,6 +236,20 @@ if sys.platform != "win32": removexattr as removexattr, setxattr as setxattr, ) + + if sys.version_info >= (3, 10): + from os import ( + EFD_CLOEXEC as EFD_CLOEXEC, + EFD_NONBLOCK as EFD_NONBLOCK, + EFD_SEMAPHORE as EFD_SEMAPHORE, + SPLICE_F_MORE as SPLICE_F_MORE, + SPLICE_F_MOVE as SPLICE_F_MOVE, + SPLICE_F_NONBLOCK as SPLICE_F_NONBLOCK, + eventfd as eventfd, + eventfd_read as eventfd_read, + eventfd_write as eventfd_write, + splice as splice, + ) else: from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod @@ -314,6 +328,34 @@ if sys.platform != "win32": if sys.platform != "darwin": from os import RWF_DSYNC as RWF_DSYNC, RWF_HIPRI as RWF_HIPRI, RWF_NOWAIT as RWF_NOWAIT, RWF_SYNC as RWF_SYNC + if sys.version_info >= (3, 12) and sys.platform == "linux": + from os import ( + CLONE_FILES as CLONE_FILES, + CLONE_FS as CLONE_FS, + CLONE_NEWCGROUP as CLONE_NEWCGROUP, + CLONE_NEWIPC as CLONE_NEWIPC, + CLONE_NEWNET as CLONE_NEWNET, + CLONE_NEWNS as CLONE_NEWNS, + CLONE_NEWPID as CLONE_NEWPID, + CLONE_NEWTIME as CLONE_NEWTIME, + CLONE_NEWUSER as CLONE_NEWUSER, + CLONE_NEWUTS as CLONE_NEWUTS, + CLONE_SIGHAND as CLONE_SIGHAND, + CLONE_SYSVSEM as CLONE_SYSVSEM, + CLONE_THREAD as CLONE_THREAD, + CLONE_VM as CLONE_VM, + setns as setns, + unshare as unshare, + ) + + if sys.version_info >= (3, 12) and sys.platform == "darwin": + from os import ( + PRIO_DARWIN_BG as PRIO_DARWIN_BG, + PRIO_DARWIN_NONUI as PRIO_DARWIN_NONUI, + PRIO_DARWIN_PROCESS as PRIO_DARWIN_PROCESS, + PRIO_DARWIN_THREAD as PRIO_DARWIN_THREAD, + ) + # Not same as os.environ or os.environb # Because of this variable, we can't do "from posix import *" in os/__init__.pyi environ: dict[bytes, bytes] diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi index f2e979ff89af..57cefb4681ac 100644 --- a/mypy/typeshed/stdlib/resource.pyi +++ b/mypy/typeshed/stdlib/resource.pyi @@ -1,6 +1,5 @@ import sys from _typeshed import structseq -from typing import overload from typing_extensions import Final, final if sys.platform != "win32": @@ -86,8 +85,8 @@ if sys.platform != "win32": def getrusage(__who: int) -> struct_rusage: ... def setrlimit(__resource: int, __limits: tuple[int, int]) -> None: ... if sys.platform == "linux": - @overload - def prlimit(pid: int, resource: int, limits: tuple[int, int]) -> tuple[int, int]: ... - @overload - def prlimit(pid: int, resource: int) -> tuple[int, int]: ... + if sys.version_info >= (3, 12): + def prlimit(__pid: int, __resource: int, __limits: tuple[int, int] | None = None) -> tuple[int, int]: ... + else: + def prlimit(__pid: int, __resource: int, __limits: tuple[int, int] = ...) -> tuple[int, int]: ... error = OSError diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 4c961a0c9aab..72c78f1b69f5 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -170,8 +170,12 @@ else: @property def si_band(self) -> int: ... - def sigtimedwait(sigset: Iterable[int], timeout: float) -> struct_siginfo | None: ... - def sigwaitinfo(sigset: Iterable[int]) -> struct_siginfo: ... + if sys.version_info >= (3, 10): + def sigtimedwait(__sigset: Iterable[int], __timeout: float) -> struct_siginfo | None: ... + def sigwaitinfo(__sigset: Iterable[int]) -> struct_siginfo: ... + else: + def sigtimedwait(sigset: Iterable[int], timeout: float) -> struct_siginfo | None: ... + def sigwaitinfo(sigset: Iterable[int]) -> struct_siginfo: ... if sys.version_info >= (3, 8): def strsignal(__signalnum: _SIGNUM) -> str | None: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 73762cd75e79..faf667afb475 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -203,7 +203,6 @@ class Options(enum.IntFlag): OP_ENABLE_MIDDLEBOX_COMPAT: int if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: int - if sys.version_info >= (3, 12) and sys.platform != "linux": OP_ENABLE_KTLS: int if sys.version_info >= (3, 11): OP_IGNORE_UNEXPECTED_EOF: int @@ -227,7 +226,6 @@ if sys.version_info >= (3, 8): OP_ENABLE_MIDDLEBOX_COMPAT: Options if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: Options -if sys.version_info >= (3, 12) and sys.platform != "linux": OP_ENABLE_KTLS: Options if sys.version_info >= (3, 11): OP_IGNORE_UNEXPECTED_EOF: Options diff --git a/mypy/typeshed/stdlib/sys.pyi b/mypy/typeshed/stdlib/sys.pyi index ca049124053a..a5e819d53326 100644 --- a/mypy/typeshed/stdlib/sys.pyi +++ b/mypy/typeshed/stdlib/sys.pyi @@ -225,9 +225,10 @@ class _thread_info(_UninstantiableStructseq, tuple[_ThreadInfoName, _ThreadInfoL def version(self) -> str | None: ... thread_info: _thread_info +_ReleaseLevel: TypeAlias = Literal["alpha", "beta", "candidate", "final"] @final -class _version_info(_UninstantiableStructseq, tuple[int, int, int, str, int]): +class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel, int]): @property def major(self) -> int: ... @property @@ -235,7 +236,7 @@ class _version_info(_UninstantiableStructseq, tuple[int, int, int, str, int]): @property def micro(self) -> int: ... @property - def releaselevel(self) -> str: ... + def releaselevel(self) -> _ReleaseLevel: ... @property def serial(self) -> int: ... diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index cfa8df887c1b..0b769301a482 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -36,11 +36,11 @@ if sys.platform != "win32": LOG_USER: Literal[8] LOG_UUCP: Literal[64] LOG_WARNING: Literal[4] - def LOG_MASK(a: int) -> int: ... - def LOG_UPTO(a: int) -> int: ... + def LOG_MASK(__pri: int) -> int: ... + def LOG_UPTO(__pri: int) -> int: ... def closelog() -> None: ... def openlog(ident: str = ..., logoption: int = ..., facility: int = ...) -> None: ... - def setlogmask(x: int) -> int: ... + def setlogmask(__maskpri: int) -> int: ... @overload def syslog(priority: int, message: str) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index bf8d7bee2473..776396cce407 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -3,10 +3,12 @@ from _typeshed import FileDescriptorLike from typing import Any from typing_extensions import TypeAlias -if sys.platform != "win32": - # Must be a list of length 7, containing 6 ints and a list of NCCS 1-character bytes or ints. - _Attr: TypeAlias = list[int | list[bytes | int]] +# Must be a list of length 7, containing 6 ints and a list of NCCS 1-character bytes or ints. +_Attr: TypeAlias = list[int | list[bytes | int]] | list[int | list[bytes]] | list[int | list[int]] +# Same as _Attr for return types; we use Any to avoid a union. +_AttrReturn: TypeAlias = list[Any] +if sys.platform != "win32": B0: int B1000000: int B110: int @@ -252,7 +254,7 @@ if sys.platform != "win32": XCASE: int XTABS: int - def tcgetattr(__fd: FileDescriptorLike) -> list[Any]: ... # Returns _Attr; we use Any to avoid a union in the return type + def tcgetattr(__fd: FileDescriptorLike) -> _AttrReturn: ... def tcsetattr(__fd: FileDescriptorLike, __when: int, __attributes: _Attr) -> None: ... def tcsendbreak(__fd: FileDescriptorLike, __duration: int) -> None: ... def tcdrain(__fd: FileDescriptorLike) -> None: ... diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index 43f2e1cf9087..add0d57a8d4b 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -1,9 +1,16 @@ import sys +import termios from typing import IO from typing_extensions import TypeAlias if sys.platform != "win32": __all__ = ["setraw", "setcbreak"] + if sys.version_info >= (3, 12): + __all__ += ["cfmakeraw", "cfmakecbreak"] + + _ModeSetterReturn: TypeAlias = termios._AttrReturn + else: + _ModeSetterReturn: TypeAlias = None _FD: TypeAlias = int | IO[str] @@ -15,5 +22,9 @@ if sys.platform != "win32": ISPEED: int OSPEED: int CC: int - def setraw(fd: _FD, when: int = 2) -> None: ... - def setcbreak(fd: _FD, when: int = 2) -> None: ... + def setraw(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... + def setcbreak(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... + + if sys.version_info >= (3, 12): + def cfmakeraw(mode: termios._Attr) -> None: ... + def cfmakecbreak(mode: termios._Attr) -> None: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 2f4bd1a88047..8559063834c9 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -69,7 +69,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) @final class _Cell: if sys.version_info >= (3, 8): - def __init__(self, __contents: object = ...) -> None: ... + def __new__(cls, __contents: object = ...) -> Self: ... def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] @@ -96,14 +96,14 @@ class FunctionType: __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str - def __init__( - self, + def __new__( + cls, code: CodeType, globals: dict[str, Any], name: str | None = ..., argdefs: tuple[object, ...] | None = ..., closure: tuple[_Cell, ...] | None = ..., - ) -> None: ... + ) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @overload def __get__(self, __instance: None, __owner: type) -> FunctionType: ... @@ -162,8 +162,8 @@ class CodeType: def co_positions(self) -> Iterable[tuple[int | None, int | None, int | None, int | None]]: ... if sys.version_info >= (3, 11): - def __init__( - self, + def __new__( + cls, __argcount: int, __posonlyargcount: int, __kwonlyargcount: int, @@ -182,10 +182,10 @@ class CodeType: __exceptiontable: bytes, __freevars: tuple[str, ...] = ..., __cellvars: tuple[str, ...] = ..., - ) -> None: ... + ) -> Self: ... elif sys.version_info >= (3, 10): - def __init__( - self, + def __new__( + cls, __argcount: int, __posonlyargcount: int, __kwonlyargcount: int, @@ -202,10 +202,10 @@ class CodeType: __linetable: bytes, __freevars: tuple[str, ...] = ..., __cellvars: tuple[str, ...] = ..., - ) -> None: ... + ) -> Self: ... elif sys.version_info >= (3, 8): - def __init__( - self, + def __new__( + cls, __argcount: int, __posonlyargcount: int, __kwonlyargcount: int, @@ -222,10 +222,10 @@ class CodeType: __lnotab: bytes, __freevars: tuple[str, ...] = ..., __cellvars: tuple[str, ...] = ..., - ) -> None: ... + ) -> Self: ... else: - def __init__( - self, + def __new__( + cls, __argcount: int, __kwonlyargcount: int, __nlocals: int, @@ -241,7 +241,7 @@ class CodeType: __lnotab: bytes, __freevars: tuple[str, ...] = ..., __cellvars: tuple[str, ...] = ..., - ) -> None: ... + ) -> Self: ... if sys.version_info >= (3, 11): def replace( self, @@ -311,7 +311,7 @@ class CodeType: @final class MappingProxyType(Mapping[_KT, _VT_co], Generic[_KT, _VT_co]): __hash__: ClassVar[None] # type: ignore[assignment] - def __init__(self, mapping: SupportsKeysAndGetItem[_KT, _VT_co]) -> None: ... + def __new__(cls, mapping: SupportsKeysAndGetItem[_KT, _VT_co]) -> Self: ... def __getitem__(self, __key: _KT) -> _VT_co: ... def __iter__(self) -> Iterator[_KT]: ... def __len__(self) -> int: ... @@ -444,7 +444,7 @@ class MethodType: def __name__(self) -> str: ... # inherited from the added function @property def __qualname__(self) -> str: ... # inherited from the added function - def __init__(self, __func: Callable[..., Any], __obj: object) -> None: ... + def __new__(cls, __func: Callable[..., Any], __obj: object) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... @@ -513,7 +513,7 @@ class ClassMethodDescriptorType: @final class TracebackType: - def __init__(self, tb_next: TracebackType | None, tb_frame: FrameType, tb_lasti: int, tb_lineno: int) -> None: ... + def __new__(cls, tb_next: TracebackType | None, tb_frame: FrameType, tb_lasti: int, tb_lineno: int) -> Self: ... tb_next: TracebackType | None # the rest are read-only even in 3.7 @property @@ -610,7 +610,7 @@ if sys.version_info >= (3, 9): def __args__(self) -> tuple[Any, ...]: ... @property def __parameters__(self) -> tuple[Any, ...]: ... - def __init__(self, origin: type, args: Any) -> None: ... + def __new__(cls, origin: type, args: Any) -> Self: ... def __getitem__(self, __typeargs: Any) -> GenericAlias: ... def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 2c1ebe6d7f95..6deb0ffd02b3 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -709,8 +709,10 @@ class IO(Iterator[AnyStr], Generic[AnyStr]): # See #8726 @property def mode(self) -> str: ... + # Usually str, but may be bytes if a bytes path was passed to open(). See #10737. + # If PEP 696 becomes available, we may want to use a defaulted TypeVar here. @property - def name(self) -> str: ... + def name(self) -> str | Any: ... @abstractmethod def close(self) -> None: ... @property diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 9320dc50b6bb..b5e2341cd020 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -149,6 +149,7 @@ __all__ = [ "Collection", "Container", "Dict", + "Doc", "ForwardRef", "FrozenSet", "Generator", @@ -489,3 +490,9 @@ if sys.version_info >= (3, 13): else: def is_protocol(__tp: type) -> bool: ... def get_protocol_members(__tp: type) -> frozenset[str]: ... + +class Doc: + documentation: str + def __init__(self, __documentation: str) -> None: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index 1f58f266ee89..aa04e16d62ec 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -126,9 +126,9 @@ class TestCase: @overload def assertLess(self, a: _T, b: SupportsDunderGT[_T], msg: Any = None) -> None: ... @overload - def assertLessEqual(self, a: SupportsDunderLT[_T], b: _T, msg: Any = None) -> None: ... + def assertLessEqual(self, a: SupportsDunderLE[_T], b: _T, msg: Any = None) -> None: ... @overload - def assertLessEqual(self, a: _T, b: SupportsDunderGT[_T], msg: Any = None) -> None: ... + def assertLessEqual(self, a: _T, b: SupportsDunderGE[_T], msg: Any = None) -> None: ... # `assertRaises`, `assertRaisesRegex`, and `assertRaisesRegexp` # are not using `ParamSpec` intentionally, # because they might be used with explicitly wrong arg types to raise some error in tests. diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi index 8bcf902df8d8..f726eae0516f 100644 --- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi @@ -2,12 +2,18 @@ import sys from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co from collections.abc import Iterable from typing import Any, NoReturn, Protocol +from typing_extensions import TypeAlias from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler from xml.sax.xmlreader import Locator, XMLReader class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]): def close(self) -> None: ... +if sys.version_info >= (3, 8): + _Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str] +else: + _Source: TypeAlias = str | _SupportsReadClose[bytes] | _SupportsReadClose[str] + class SAXException(Exception): def __init__(self, msg: str, exception: Exception | None = None) -> None: ... def getMessage(self) -> str: ... @@ -28,20 +34,13 @@ class SAXReaderNotAvailable(SAXNotSupportedException): ... default_parser_list: list[str] if sys.version_info >= (3, 8): + def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ... - def parse( - source: StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str], - handler: ContentHandler, - errorHandler: ErrorHandler = ..., - ) -> None: ... else: + def make_parser(parser_list: list[str] = []) -> XMLReader: ... - def parse( - source: str | _SupportsReadClose[bytes] | _SupportsReadClose[str], - handler: ContentHandler, - errorHandler: ErrorHandler = ..., - ) -> None: ... +def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ... def parseString(string: ReadableBuffer | str, handler: ContentHandler, errorHandler: ErrorHandler | None = ...) -> None: ... def _create_parser(parser_name: str) -> XMLReader: ... diff --git a/mypy/typeshed/stdlib/xml/sax/handler.pyi b/mypy/typeshed/stdlib/xml/sax/handler.pyi index 63b725bd6da6..30fe31d51374 100644 --- a/mypy/typeshed/stdlib/xml/sax/handler.pyi +++ b/mypy/typeshed/stdlib/xml/sax/handler.pyi @@ -1,5 +1,6 @@ import sys from typing import NoReturn +from xml.sax import xmlreader version: str @@ -9,19 +10,19 @@ class ErrorHandler: def warning(self, exception: BaseException) -> None: ... class ContentHandler: - def setDocumentLocator(self, locator): ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, whitespace): ... - def processingInstruction(self, target, data): ... - def skippedEntity(self, name): ... + def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... + def startDocument(self) -> None: ... + def endDocument(self) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix) -> None: ... + def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def characters(self, content: str) -> None: ... + def ignorableWhitespace(self, whitespace: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def skippedEntity(self, name: str) -> None: ... class DTDHandler: def notationDecl(self, name, publicId, systemId): ... diff --git a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi index 0d9223770c6a..06e03a1e4d06 100644 --- a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi +++ b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi @@ -2,7 +2,7 @@ from _typeshed import SupportsWrite from codecs import StreamReaderWriter, StreamWriter from collections.abc import Mapping from io import RawIOBase, TextIOBase -from xml.sax import handler, xmlreader +from xml.sax import _Source, handler, xmlreader def escape(data: str, entities: Mapping[str, str] = {}) -> str: ... def unescape(data: str, entities: Mapping[str, str] = {}) -> str: ... @@ -15,46 +15,46 @@ class XMLGenerator(handler.ContentHandler): encoding: str = "iso-8859-1", short_empty_elements: bool = False, ) -> None: ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, content): ... - def processingInstruction(self, target, data): ... + def startDocument(self) -> None: ... + def endDocument(self) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def characters(self, content: str) -> None: ... + def ignorableWhitespace(self, content: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... class XMLFilterBase(xmlreader.XMLReader): def __init__(self, parent: xmlreader.XMLReader | None = None) -> None: ... def error(self, exception): ... def fatalError(self, exception): ... def warning(self, exception): ... - def setDocumentLocator(self, locator): ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, chars): ... - def processingInstruction(self, target, data): ... - def skippedEntity(self, name): ... + def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... + def startDocument(self) -> None: ... + def endDocument(self) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def characters(self, content: str) -> None: ... + def ignorableWhitespace(self, chars: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def skippedEntity(self, name: str) -> None: ... def notationDecl(self, name, publicId, systemId): ... def unparsedEntityDecl(self, name, publicId, systemId, ndata): ... def resolveEntity(self, publicId, systemId): ... - def parse(self, source): ... + def parse(self, source: _Source) -> None: ... def setLocale(self, locale): ... - def getFeature(self, name): ... - def setFeature(self, name, state): ... - def getProperty(self, name): ... - def setProperty(self, name, value): ... - def getParent(self): ... - def setParent(self, parent): ... + def getFeature(self, name: str) -> object: ... + def setFeature(self, name: str, state: object) -> None: ... + def getProperty(self, name: str) -> object: ... + def setProperty(self, name: str, value: object) -> None: ... + def getParent(self) -> xmlreader.XMLReader: ... + def setParent(self, parent: xmlreader.XMLReader) -> None: ... def prepare_input_source(source, base=""): ... diff --git a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi index 0bf167b04a37..74d2efb010cd 100644 --- a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi @@ -1,20 +1,23 @@ from collections.abc import Mapping +from typing import overload +from typing_extensions import Self, TypeAlias +from xml.sax.handler import ContentHandler, DTDHandler, EntityResolver, ErrorHandler class XMLReader: def parse(self, source): ... - def getContentHandler(self): ... - def setContentHandler(self, handler): ... - def getDTDHandler(self): ... - def setDTDHandler(self, handler): ... - def getEntityResolver(self): ... - def setEntityResolver(self, resolver): ... - def getErrorHandler(self): ... - def setErrorHandler(self, handler): ... + def getContentHandler(self) -> ContentHandler: ... + def setContentHandler(self, handler: ContentHandler) -> None: ... + def getDTDHandler(self) -> DTDHandler: ... + def setDTDHandler(self, handler: DTDHandler) -> None: ... + def getEntityResolver(self) -> EntityResolver: ... + def setEntityResolver(self, resolver: EntityResolver) -> None: ... + def getErrorHandler(self) -> ErrorHandler: ... + def setErrorHandler(self, handler: ErrorHandler) -> None: ... def setLocale(self, locale): ... - def getFeature(self, name): ... - def setFeature(self, name, state): ... - def getProperty(self, name): ... - def setProperty(self, name, value): ... + def getFeature(self, name: str) -> object: ... + def setFeature(self, name: str, state: object) -> None: ... + def getProperty(self, name: str) -> object: ... + def setProperty(self, name: str, value: object) -> None: ... class IncrementalParser(XMLReader): def __init__(self, bufsize: int = 65536) -> None: ... @@ -45,27 +48,40 @@ class InputSource: class AttributesImpl: def __init__(self, attrs: Mapping[str, str]) -> None: ... - def getLength(self): ... - def getType(self, name): ... - def getValue(self, name): ... - def getValueByQName(self, name): ... - def getNameByQName(self, name): ... - def getQNameByName(self, name): ... - def getNames(self): ... - def getQNames(self): ... + def getLength(self) -> int: ... + def getType(self, name: str) -> str: ... + def getValue(self, name: str) -> str: ... + def getValueByQName(self, name: str) -> str: ... + def getNameByQName(self, name: str) -> str: ... + def getQNameByName(self, name: str) -> str: ... + def getNames(self) -> list[str]: ... + def getQNames(self) -> list[str]: ... def __len__(self) -> int: ... - def __getitem__(self, name): ... - def keys(self): ... - def __contains__(self, name): ... - def get(self, name, alternative=None): ... - def copy(self): ... - def items(self): ... - def values(self): ... + def __getitem__(self, name: str) -> str: ... + def keys(self) -> list[str]: ... + def __contains__(self, name: str) -> bool: ... + @overload + def get(self, name: str, alternative: None = None) -> str | None: ... + @overload + def get(self, name: str, alternative: str) -> str: ... + def copy(self) -> Self: ... + def items(self) -> list[tuple[str, str]]: ... + def values(self) -> list[str]: ... + +_NSName: TypeAlias = tuple[str | None, str] class AttributesNSImpl(AttributesImpl): - def __init__(self, attrs: Mapping[tuple[str, str], str], qnames: Mapping[tuple[str, str], str]) -> None: ... - def getValueByQName(self, name): ... - def getNameByQName(self, name): ... - def getQNameByName(self, name): ... - def getQNames(self): ... - def copy(self): ... + def __init__(self, attrs: Mapping[_NSName, str], qnames: Mapping[_NSName, str]) -> None: ... + def getType(self, name: _NSName) -> str: ... # type: ignore[override] + def getValue(self, name: _NSName) -> str: ... # type: ignore[override] + def getNameByQName(self, name: str) -> _NSName: ... # type: ignore[override] + def getQNameByName(self, name: _NSName) -> str: ... # type: ignore[override] + def getNames(self) -> list[_NSName]: ... # type: ignore[override] + def __getitem__(self, name: _NSName) -> str: ... # type: ignore[override] + def keys(self) -> list[_NSName]: ... # type: ignore[override] + def __contains__(self, name: _NSName) -> bool: ... # type: ignore[override] + @overload # type: ignore[override] + def get(self, name: _NSName, alternative: None = None) -> str | None: ... + @overload # type: ignore[override] + def get(self, name: _NSName, alternative: str) -> str: ... + def items(self) -> list[tuple[_NSName, str]]: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index b2fb72ad2c0b..d4f41bbaf22a 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -7,6 +7,8 @@ class Str: ... @final class Xxo: def demo(self) -> None: ... + if sys.version_info >= (3, 11) and sys.platform != "win32": + x_exports: int def foo(__i: int, __j: int) -> Any: ... def new() -> Xxo: ... From 3d3e482e03c1efeaca9a1033acf06f56c1dfdf86 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:06:33 -0700 Subject: [PATCH 0296/1617] Fix cases of type object handling for overloads (#16168) Fixes most of #12320. I didn't add tests for every code path because it's niche. I also didn't fix everything, in particular the cases where we proceed to use `ret_type` --- mypy/checker.py | 4 ++-- mypy/checkexpr.py | 8 ++++---- mypy/messages.py | 2 +- mypy/plugins/proper_plugin.py | 3 +-- mypy/typeops.py | 2 +- test-data/unit/check-abstract.test | 11 ++++++++++- test-data/unit/pythoneval.test | 4 ++-- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index bdb636541db0..1a7a7e25d525 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2986,7 +2986,7 @@ def check_assignment( p_rvalue_type = get_proper_type(rvalue_type) p_lvalue_type = get_proper_type(lvalue_type) if ( - isinstance(p_rvalue_type, CallableType) + isinstance(p_rvalue_type, FunctionLike) and p_rvalue_type.is_type_obj() and ( p_rvalue_type.type_object().is_abstract @@ -3771,7 +3771,7 @@ def split_around_star( def type_is_iterable(self, type: Type) -> bool: type = get_proper_type(type) - if isinstance(type, CallableType) and type.is_type_obj(): + if isinstance(type, FunctionLike) and type.is_type_obj(): type = type.fallback return is_subtype( type, self.named_generic_type("typing.Iterable", [AnyType(TypeOfAny.special_form)]) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index df4077100efb..e81fba9bc9ef 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -694,7 +694,7 @@ def check_runtime_protocol_test(self, e: CallExpr) -> None: for expr in mypy.checker.flatten(e.args[1]): tp = get_proper_type(self.chk.lookup_type(expr)) if ( - isinstance(tp, CallableType) + isinstance(tp, FunctionLike) and tp.is_type_obj() and tp.type_object().is_protocol and not tp.type_object().runtime_protocol @@ -704,7 +704,7 @@ def check_runtime_protocol_test(self, e: CallExpr) -> None: def check_protocol_issubclass(self, e: CallExpr) -> None: for expr in mypy.checker.flatten(e.args[1]): tp = get_proper_type(self.chk.lookup_type(expr)) - if isinstance(tp, CallableType) and tp.is_type_obj() and tp.type_object().is_protocol: + if isinstance(tp, FunctionLike) and tp.is_type_obj() and tp.type_object().is_protocol: attr_members = non_method_protocol_members(tp.type_object()) if attr_members: self.chk.msg.report_non_method_protocol(tp.type_object(), attr_members, e) @@ -4190,7 +4190,7 @@ def visit_index_with_type( elif isinstance(left_type, TypedDictType): return self.visit_typeddict_index_expr(left_type, e.index) elif ( - isinstance(left_type, CallableType) + isinstance(left_type, FunctionLike) and left_type.is_type_obj() and left_type.type_object().is_enum ): @@ -5832,7 +5832,7 @@ def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperTyp def has_abstract_type(self, caller_type: ProperType, callee_type: ProperType) -> bool: return ( - isinstance(caller_type, CallableType) + isinstance(caller_type, FunctionLike) and isinstance(callee_type, TypeType) and caller_type.is_type_obj() and (caller_type.type_object().is_abstract or caller_type.type_object().is_protocol) diff --git a/mypy/messages.py b/mypy/messages.py index 47ebd94f3d21..5d03bf1babb9 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -416,7 +416,7 @@ def has_no_attr( elif member == "__getitem__": # Indexed get. # TODO: Fix this consistently in format_type - if isinstance(original_type, CallableType) and original_type.is_type_obj(): + if isinstance(original_type, FunctionLike) and original_type.is_type_obj(): self.fail( "The type {} is not generic and not indexable".format( format_type(original_type, self.options) diff --git a/mypy/plugins/proper_plugin.py b/mypy/plugins/proper_plugin.py index ab93f0d126db..a1fd05272b65 100644 --- a/mypy/plugins/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -17,7 +17,6 @@ from mypy.subtypes import is_proper_subtype from mypy.types import ( AnyType, - CallableType, FunctionLike, Instance, NoneTyp, @@ -131,7 +130,7 @@ def is_dangerous_target(typ: ProperType) -> bool: """Is this a dangerous target (right argument) for an isinstance() check?""" if isinstance(typ, TupleType): return any(is_dangerous_target(get_proper_type(t)) for t in typ.items) - if isinstance(typ, CallableType) and typ.is_type_obj(): + if isinstance(typ, FunctionLike) and typ.is_type_obj(): return typ.type_object().has_base("mypy.types.Type") return False diff --git a/mypy/typeops.py b/mypy/typeops.py index 10efa32c4b91..37817933a397 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -989,7 +989,7 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool return any(custom_special_method(t, name) for t in typ.items) if isinstance(typ, TupleType): return custom_special_method(tuple_fallback(typ), name, check_all) - if isinstance(typ, CallableType) and typ.is_type_obj(): + if isinstance(typ, FunctionLike) and typ.is_type_obj(): # Look up __method__ on the metaclass for class objects. return custom_special_method(typ.fallback, name, check_all) if isinstance(typ, AnyType): diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 299074050baa..7f91eb8e7145 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -241,7 +241,7 @@ f(GoodAlias) [case testInstantiationAbstractsInTypeForVariables] # flags: --no-strict-optional -from typing import Type +from typing import Type, overload from abc import abstractmethod class A: @@ -269,6 +269,15 @@ if int(): var_old = B # E: Can only assign concrete classes to a variable of type "Type[A]" if int(): var_old = C # OK + +class D(A): + @overload + def __new__(cls, a) -> "D": ... + @overload + def __new__(cls) -> "D": ... + def __new__(cls, a=None) -> "D": ... +if int(): + var = D # E: Can only assign concrete classes to a variable of type "Type[A]" [out] [case testInstantiationAbstractsInTypeForClassMethods] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index c5be30eac1b7..3d8e8d09a5ad 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1801,9 +1801,9 @@ C = str | int D: TypeAlias = str | int [out] _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Value of type "Type[type]" is not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: The type "Type[type]" is not generic and not indexable _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Value of type "Type[type]" is not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: The type "Type[type]" is not generic and not indexable _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type From 99ba048f4887eb0fbd55cde3f4243f6c177cbf7e Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 1 Oct 2023 13:56:12 -0700 Subject: [PATCH 0297/1617] tuple slice should not propagate fallback (#16154) Fixes #8776 --- mypy/checkexpr.py | 2 +- mypy/types.py | 9 +++++++-- test-data/unit/check-literal.test | 9 +++++---- test-data/unit/check-tuples.test | 10 ++++++++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e81fba9bc9ef..a2141680b6cb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4271,7 +4271,7 @@ def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Typ items: list[Type] = [] for b, e, s in itertools.product(begin, end, stride): - item = left_type.slice(b, e, s) + item = left_type.slice(b, e, s, fallback=self.named_type("builtins.tuple")) if item is None: self.chk.fail(message_registry.AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE, slic) return AnyType(TypeOfAny.from_error) diff --git a/mypy/types.py b/mypy/types.py index 9817043db6c2..34ea96be25ee 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2416,7 +2416,12 @@ def copy_modified( items = self.items return TupleType(items, fallback, self.line, self.column) - def slice(self, begin: int | None, end: int | None, stride: int | None) -> TupleType | None: + def slice( + self, begin: int | None, end: int | None, stride: int | None, *, fallback: Instance | None + ) -> TupleType | None: + if fallback is None: + fallback = self.partial_fallback + if any(isinstance(t, UnpackType) for t in self.items): total = len(self.items) unpack_index = find_unpack_in_list(self.items) @@ -2462,7 +2467,7 @@ def slice(self, begin: int | None, end: int | None, stride: int | None) -> Tuple return None else: slice_items = self.items[begin:end:stride] - return TupleType(slice_items, self.partial_fallback, self.line, self.column, self.implicit) + return TupleType(slice_items, fallback, self.line, self.column, self.implicit) class TypedDictType(ProperType): diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 08c709c6b777..d9ad68385ad1 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1872,8 +1872,9 @@ reveal_type(tup2[idx3]) # N: Revealed type is "__main__.D" reveal_type(tup2[idx4]) # N: Revealed type is "__main__.E" reveal_type(tup2[idx_neg1]) # N: Revealed type is "__main__.E" tup2[idx5] # E: Tuple index out of range -reveal_type(tup2[idx2:idx4]) # N: Revealed type is "Tuple[__main__.C, __main__.D, fallback=__main__.Tup2Class]" -reveal_type(tup2[::idx2]) # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E, fallback=__main__.Tup2Class]" +reveal_type(tup2[idx2:idx4]) # N: Revealed type is "Tuple[__main__.C, __main__.D]" +reveal_type(tup2[::idx2]) # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E]" +tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression has type "Tuple[A, B, C, D, E]", variable has type "Tup2Class") [builtins fixtures/slice.pyi] [case testLiteralIntelligentIndexingTypedDict] @@ -1977,8 +1978,8 @@ reveal_type(tup1[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, _ tup1[idx_bad] # E: Tuple index out of range reveal_type(tup2[idx1]) # N: Revealed type is "Union[__main__.B, __main__.C]" -reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C, fallback=__main__.Tup2Class], Tuple[__main__.B, __main__.C, __main__.D, fallback=__main__.Tup2Class], Tuple[__main__.C, fallback=__main__.Tup2Class], Tuple[__main__.C, __main__.D, fallback=__main__.Tup2Class]]" -reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E, fallback=__main__.Tup2Class], Tuple[__main__.A, __main__.C, __main__.E, fallback=__main__.Tup2Class]]" +reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]" +reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]" tup2[idx_bad] # E: Tuple index out of range [builtins fixtures/slice.pyi] [out] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 9dfee38bc0c6..1447321c0c49 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1681,3 +1681,13 @@ def g(t: Tuple): reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]" reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Tuple[Any]]" [builtins fixtures/tuple.pyi] + +[case testTupleSubclassSlice] +from typing import Tuple + +class A: ... + +class tuple_aa_subclass(Tuple[A, A]): ... + +inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:] # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "tuple_aa_subclass") +[builtins fixtures/tuple.pyi] From bcd4ff231554102a6698615882074e440ebfc3c9 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 1 Oct 2023 23:48:53 +0100 Subject: [PATCH 0298/1617] stubtest: hint when args in stub need to be keyword-only (#16210) --- mypy/stubtest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a5028581f7a1..e80ea4eac71f 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -890,7 +890,10 @@ def _verify_signature( # If the variable is in runtime.kwonly, it's just mislabelled as not a # keyword-only argument if stub_arg.variable.name not in runtime.kwonly: - yield f'runtime does not have argument "{stub_arg.variable.name}"' + msg = f'runtime does not have argument "{stub_arg.variable.name}"' + if runtime.varkw is not None: + msg += ". Maybe you forgot to make it keyword-only in the stub?" + yield msg else: yield f'stub argument "{stub_arg.variable.name}" is not keyword-only' if stub.varpos is not None: From 96803e0817c751e82fe88695647e20a8f050dee9 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 3 Oct 2023 02:43:29 -0700 Subject: [PATCH 0299/1617] Add meta test for new diff logic (#16211) Follow up to #16112 --- mypy/test/helpers.py | 23 +++++++++------ mypy/test/meta/test_diff_helper.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 mypy/test/meta/test_diff_helper.py diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index a53e16e27dfa..dc34931427ec 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -8,7 +8,7 @@ import shutil import sys import time -from typing import Any, Callable, Iterable, Iterator, Pattern +from typing import IO, Any, Callable, Iterable, Iterator, Pattern # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly @@ -70,7 +70,12 @@ def diff_ranges( def render_diff_range( - ranges: list[tuple[int, int]], content: list[str], colour: str | None = None + ranges: list[tuple[int, int]], + content: list[str], + *, + colour: str | None = None, + output: IO[str] = sys.stderr, + indent: int = 2, ) -> None: for i, line_range in enumerate(ranges): is_matching = i % 2 == 1 @@ -83,20 +88,20 @@ def render_diff_range( and j < len(lines) - 3 ): if j == 3: - sys.stderr.write(" ...\n") + output.write(" " * indent + "...\n") continue if not is_matching and colour: - sys.stderr.write(colour) + output.write(colour) - sys.stderr.write(" " + line) + output.write(" " * indent + line) if not is_matching: if colour: - sys.stderr.write("\033[0m") - sys.stderr.write(" (diff)") + output.write("\033[0m") + output.write(" (diff)") - sys.stderr.write("\n") + output.write("\n") def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) -> None: @@ -129,7 +134,7 @@ def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) sys.stderr.write( "Update the test output using --update-data -n0 " - "(you can additionally use the -k selector to update only specific tests)" + "(you can additionally use the -k selector to update only specific tests)\n" ) pytest.fail(msg, pytrace=False) diff --git a/mypy/test/meta/test_diff_helper.py b/mypy/test/meta/test_diff_helper.py new file mode 100644 index 000000000000..047751fee1d2 --- /dev/null +++ b/mypy/test/meta/test_diff_helper.py @@ -0,0 +1,47 @@ +import io + +from mypy.test.helpers import Suite, diff_ranges, render_diff_range + + +class DiffHelperSuite(Suite): + def test_render_diff_range(self) -> None: + expected = ["hello", "world"] + actual = ["goodbye", "world"] + + expected_ranges, actual_ranges = diff_ranges(expected, actual) + + output = io.StringIO() + render_diff_range(expected_ranges, expected, output=output) + assert output.getvalue() == " hello (diff)\n world\n" + output = io.StringIO() + render_diff_range(actual_ranges, actual, output=output) + assert output.getvalue() == " goodbye (diff)\n world\n" + + expected = ["a", "b", "c", "d", "e", "f", "g", "h", "circle", "i", "j"] + actual = ["a", "b", "c", "d", "e", "f", "g", "h", "square", "i", "j"] + + expected_ranges, actual_ranges = diff_ranges(expected, actual) + + output = io.StringIO() + render_diff_range(expected_ranges, expected, output=output, indent=0) + assert output.getvalue() == "a\nb\nc\n...\nf\ng\nh\ncircle (diff)\ni\nj\n" + output = io.StringIO() + render_diff_range(actual_ranges, actual, output=output, indent=0) + assert output.getvalue() == "a\nb\nc\n...\nf\ng\nh\nsquare (diff)\ni\nj\n" + + def test_diff_ranges(self) -> None: + a = ["hello", "world"] + b = ["hello", "world"] + + assert diff_ranges(a, b) == ( + [(0, 0), (0, 2), (2, 2), (2, 2)], + [(0, 0), (0, 2), (2, 2), (2, 2)], + ) + + a = ["hello", "world"] + b = ["goodbye", "world"] + + assert diff_ranges(a, b) == ( + [(0, 1), (1, 2), (2, 2), (2, 2)], + [(0, 1), (1, 2), (2, 2), (2, 2)], + ) From d839a0b1013873e27eae334a21b56fa57cd5e178 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 4 Oct 2023 03:31:18 -0400 Subject: [PATCH 0300/1617] tests: avoid leaving artifacts in the source tree (#16201) When running the mypy unittests, most of the time any output files are produced into a temporary directory and cleaned up. In one case, it wasn't. Fix this for test_capi. --- mypyc/lib-rt/setup.py | 29 ++++++++++++++++++++- mypyc/test/test_external.py | 50 ++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index a31b705cd723..ef81b794c9bd 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -5,7 +5,10 @@ from __future__ import annotations +import os +import subprocess import sys +from distutils.command.build_ext import build_ext from distutils.core import Extension, setup from typing import Any @@ -17,6 +20,30 @@ kwargs = {} compile_args = ["--std=c++11"] + +class build_ext_custom(build_ext): + def get_library_names(self): + return ["gtest"] + + def run(self): + gtest_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), "..", "external", "googletest") + ) + + os.makedirs(self.build_temp, exist_ok=True) + + # Build Google Test, the C++ framework we use for testing C code. + # The source code for Google Test is copied to this repository. + subprocess.check_call( + ["make", "-f", os.path.join(gtest_dir, "make", "Makefile"), f"GTEST_DIR={gtest_dir}"], + cwd=self.build_temp, + ) + + self.library_dirs = [self.build_temp] + + return build_ext.run(self) + + setup( name="test_capi", version="0.1", @@ -34,10 +61,10 @@ ], depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"], extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args, - library_dirs=["../external/googletest/make"], libraries=["gtest"], include_dirs=["../external/googletest", "../external/googletest/include"], **kwargs, ) ], + cmdclass={"build_ext": build_ext_custom}, ) diff --git a/mypyc/test/test_external.py b/mypyc/test/test_external.py index 6deabd81255e..22eb8019133c 100644 --- a/mypyc/test/test_external.py +++ b/mypyc/test/test_external.py @@ -5,6 +5,7 @@ import os import subprocess import sys +import tempfile import unittest base_dir = os.path.join(os.path.dirname(__file__), "..", "..") @@ -16,34 +17,33 @@ class TestExternal(unittest.TestCase): @unittest.skipIf(sys.platform.startswith("win"), "rt tests don't work on windows") def test_c_unit_test(self) -> None: """Run C unit tests in a subprocess.""" - # Build Google Test, the C++ framework we use for testing C code. - # The source code for Google Test is copied to this repository. cppflags: list[str] = [] env = os.environ.copy() if sys.platform == "darwin": cppflags += ["-mmacosx-version-min=10.10", "-stdlib=libc++"] env["CPPFLAGS"] = " ".join(cppflags) - subprocess.check_call( - ["make", "libgtest.a"], - env=env, - cwd=os.path.join(base_dir, "mypyc", "external", "googletest", "make"), - ) # Build Python wrapper for C unit tests. - env = os.environ.copy() - env["CPPFLAGS"] = " ".join(cppflags) - status = subprocess.check_call( - [sys.executable, "setup.py", "build_ext", "--inplace"], - env=env, - cwd=os.path.join(base_dir, "mypyc", "lib-rt"), - ) - # Run C unit tests. - env = os.environ.copy() - if "GTEST_COLOR" not in os.environ: - env["GTEST_COLOR"] = "yes" # Use fancy colors - status = subprocess.call( - [sys.executable, "-c", "import sys, test_capi; sys.exit(test_capi.run_tests())"], - env=env, - cwd=os.path.join(base_dir, "mypyc", "lib-rt"), - ) - if status != 0: - raise AssertionError("make test: C unit test failure") + + with tempfile.TemporaryDirectory() as tmpdir: + status = subprocess.check_call( + [ + sys.executable, + "setup.py", + "build_ext", + f"--build-lib={tmpdir}", + f"--build-temp={tmpdir}", + ], + env=env, + cwd=os.path.join(base_dir, "mypyc", "lib-rt"), + ) + # Run C unit tests. + env = os.environ.copy() + if "GTEST_COLOR" not in os.environ: + env["GTEST_COLOR"] = "yes" # Use fancy colors + status = subprocess.call( + [sys.executable, "-c", "import sys, test_capi; sys.exit(test_capi.run_tests())"], + env=env, + cwd=tmpdir, + ) + if status != 0: + raise AssertionError("make test: C unit test failure") From b1ba661122dc39d9bbc53cf5df334c9f56b1a729 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 4 Oct 2023 03:43:37 -0400 Subject: [PATCH 0301/1617] __qualname__ and __module__ are available in class bodies (#16215) Resolves #10570 Resolves #6473 --- mypy/semanal.py | 5 ++++- test-data/unit/check-classes.test | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6e103e5d382c..a476b62b31ec 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5569,7 +5569,7 @@ def lookup( if not suppress_errors: self.name_not_defined(name, ctx) return None - # 2. Class attributes (if within class definition) + # 2a. Class attributes (if within class definition) if self.type and not self.is_func_scope() and name in self.type.names: node = self.type.names[name] if not node.implicit: @@ -5579,6 +5579,9 @@ def lookup( # Defined through self.x assignment implicit_name = True implicit_node = node + # 2b. Class attributes __qualname__ and __module__ + if self.type and not self.is_func_scope() and name in {"__qualname__", "__module__"}: + return SymbolTableNode(MDEF, Var(name, self.str_type())) # 3. Local (function) scopes for table in reversed(self.locals): if table is not None and name in table: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 4bc1e50f7be9..cd60ec7c9a9c 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -8001,3 +8001,11 @@ f5(1) # E: Argument 1 to "f5" has incompatible type "int"; expected "Integral" # N: Types from "numbers" aren't supported for static type checking \ # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ # N: Consider using a protocol instead, such as typing.SupportsFloat + +[case testImplicitClassScopedNames] +class C: + reveal_type(__module__) # N: Revealed type is "builtins.str" + reveal_type(__qualname__) # N: Revealed type is "builtins.str" + def f(self) -> None: + __module__ # E: Name "__module__" is not defined + __qualname__ # E: Name "__qualname__" is not defined From a1df3353a7bc0d7ff7b3459e95d0f9684b325e9b Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 4 Oct 2023 14:49:05 +0100 Subject: [PATCH 0302/1617] Bump ruff and black to their latest versions (#16221) Closes #16218 --- .pre-commit-config.yaml | 4 ++-- mypy/build.py | 2 +- mypy/main.py | 2 +- mypy/metastore.py | 2 +- mypy/plugins/common.py | 2 +- pyproject.toml | 1 + setup.py | 2 +- test-requirements.txt | 4 ++-- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8650a2868cd6..e92d498fa3cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,11 +6,11 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.7.0 # must match test-requirements.txt + rev: 23.9.1 # must match test-requirements.txt hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.281 # must match test-requirements.txt + rev: v0.0.292 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/mypy/build.py b/mypy/build.py index 39629c2dc455..b481cc6ad0dc 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -3024,7 +3024,7 @@ def dump_graph(graph: Graph, stdout: TextIO | None = None) -> None: if state.path: try: size = os.path.getsize(state.path) - except os.error: + except OSError: pass node.sizes[mod] = size for dep in state.dependencies: diff --git a/mypy/main.py b/mypy/main.py index 3eb8a76a6de3..dff1a0362ba2 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -33,7 +33,7 @@ def stat_proxy(path: str) -> os.stat_result: try: st = orig_stat(path) - except os.error as err: + except OSError as err: print(f"stat({path!r}) -> {err}") raise else: diff --git a/mypy/metastore.py b/mypy/metastore.py index 16cbd5adc9c8..0547f94cd671 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -112,7 +112,7 @@ def write(self, name: str, data: str, mtime: float | None = None) -> bool: if mtime is not None: os.utime(path, times=(mtime, mtime)) - except os.error: + except OSError: return False return True diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 84d50b7086c6..03041bfcebcd 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -154,7 +154,7 @@ def find_shallow_matching_overload_item(overload: Overloaded, call: CallExpr) -> ): ok = False break - elif isinstance(arg_type, LiteralType) and type(arg_type.value) is bool: + elif isinstance(arg_type, LiteralType) and isinstance(arg_type.value, bool): if not any(parse_bool(arg) == arg_type.value for arg in args): ok = False break diff --git a/pyproject.toml b/pyproject.toml index 1d6562756e22..de32618f1a39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ unfixable = [ "F601", # automatic fix might obscure issue "F602", # automatic fix might obscure issue "B018", # automatic fix might obscure issue + "UP036", # sometimes it's better to just noqa this ] extend-exclude = [ diff --git a/setup.py b/setup.py index bbb655ea4537..7e7793a406d0 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 8, 0): +if sys.version_info < (3, 8, 0): # noqa: UP036 sys.stderr.write("ERROR: You need Python 3.8 or later to use mypy.\n") exit(1) diff --git a/test-requirements.txt b/test-requirements.txt index 6f7bec0375ad..bdaad16fa88e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==23.7.0 # must match version in .pre-commit-config.yaml +black==23.9.1 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' @@ -11,6 +11,6 @@ psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.0.280 # must match version in .pre-commit-config.yaml +ruff==0.0.292 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 From 10dfafe089a75dc117586ebab35723da66309398 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 4 Oct 2023 18:58:28 +0100 Subject: [PATCH 0303/1617] Remove stubs packages from `stubinfo.py` where the runtime package has added a `py.typed` file (#16226) All of these stubs packages have been removed from typeshed, due to the runtime package having added a `py.typed` file. --- mypy/stubinfo.py | 9 --------- test-data/unit/pythoneval.test | 12 ++++++------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 0d76a6215238..9d8dfbe43f37 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -26,17 +26,14 @@ def stub_distribution_name(prefix: str) -> str: "croniter": "types-croniter", "dataclasses": "types-dataclasses", "dateparser": "types-dateparser", - "datetimerange": "types-DateTimeRange", "dateutil": "types-python-dateutil", "decorator": "types-decorator", "deprecated": "types-Deprecated", "docutils": "types-docutils", "first": "types-first", - "geoip2": "types-geoip2", "gflags": "types-python-gflags", "google.protobuf": "types-protobuf", "markdown": "types-Markdown", - "maxminddb": "types-maxminddb", "mock": "types-mock", "OpenSSL": "types-pyOpenSSL", "paramiko": "types-paramiko", @@ -80,8 +77,6 @@ def stub_distribution_name(prefix: str) -> str: "PIL": "types-Pillow", "PyInstaller": "types-pyinstaller", "Xlib": "types-python-xlib", - "annoy": "types-annoy", - "appdirs": "types-appdirs", "aws_xray_sdk": "types-aws-xray-sdk", "babel": "types-babel", "backports.ssl_match_hostname": "types-backports.ssl_match_hostname", @@ -96,7 +91,6 @@ def stub_distribution_name(prefix: str) -> str: "consolemenu": "types-console-menu", "crontab": "types-python-crontab", "d3dshot": "types-D3DShot", - "dj_database_url": "types-dj-database-url", "dockerfile_parse": "types-dockerfile-parse", "docopt": "types-docopt", "editdistance": "types-editdistance", @@ -111,7 +105,6 @@ def stub_distribution_name(prefix: str) -> str: "flake8_typing_imports": "types-flake8-typing-imports", "flask_cors": "types-Flask-Cors", "flask_migrate": "types-Flask-Migrate", - "flask_sqlalchemy": "types-Flask-SQLAlchemy", "fpdf": "types-fpdf2", "gdb": "types-gdb", "google.cloud.ndb": "types-google-cloud-ndb", @@ -162,7 +155,6 @@ def stub_distribution_name(prefix: str) -> str: "tree_sitter": "types-tree-sitter", "tree_sitter_languages": "types-tree-sitter-languages", "ttkthemes": "types-ttkthemes", - "urllib3": "types-urllib3", "vobject": "types-vobject", "whatthepatch": "types-whatthepatch", "win32": "types-pywin32", @@ -172,7 +164,6 @@ def stub_distribution_name(prefix: str) -> str: "win32comext": "types-pywin32", "win32gui": "types-pywin32", "xmltodict": "types-xmltodict", - "xxhash": "types-xxhash", "zxcvbn": "types-zxcvbn", # Stub packages that are not from typeshed # Since these can be installed automatically via --install-types, we have a high trust bar diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 3d8e8d09a5ad..7dd2b2f76f8c 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1568,24 +1568,24 @@ note: A user-defined top-level module with name "typing" is not supported # flags: --ignore-missing-imports import scribe # No Python 3 stubs available for scribe from scribe import x -import maxminddb # Python 3 stubs available for maxminddb +import docutils # Python 3 stubs available for docutils import foobar_asdf import jack # This has a stubs package but was never bundled with mypy, so ignoring works [out] -_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "maxminddb" -_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-maxminddb" +_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "docutils" +_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-docutils" _testIgnoreImportIfNoPython3StubAvailable.py:4: note: (or run "mypy --install-types" to install all missing stub packages) _testIgnoreImportIfNoPython3StubAvailable.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [case testNoPython3StubAvailable] import scribe from scribe import x -import maxminddb +import docutils [out] _testNoPython3StubAvailable.py:1: error: Cannot find implementation or library stub for module named "scribe" _testNoPython3StubAvailable.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "maxminddb" -_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-maxminddb" +_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "docutils" +_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-docutils" _testNoPython3StubAvailable.py:3: note: (or run "mypy --install-types" to install all missing stub packages) From d54e8b30301620ce5cc59a0c304b8423f07a7b60 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 8 Oct 2023 19:32:51 +0100 Subject: [PATCH 0304/1617] Support variadic tuple packing/unpacking (#16205) This is includes also related things such as tuple concatenation, special-cased tuple "re-packing", and star tuple unpacking in homogeneous collections. It looks like we are very close to the finish line (the only major missing feature is type narrowing using `len()`, apart from this I just need to do couple technical things, and make one final search for missed code paths). Some notes: * Unfortunately, star items on l.h.s create lists at runtime. This means there are various cases where `list[object]` is the best type we can have. * Note I now infer "precise" types for expressions like `(x, *y, z)`, where `y` is say `tuple[int, ...]`. This may cause errors for code that previously worked (when we will turn this feature on). For example `(1, *[], 2)[42]` will be an error. As usual, I propose to try to be strict, and relax if people will complain (FWIW, I expect very few false positives from this). * It may look like `Unpack` can now "leak" if it was never used explicitly. This is not the case, it is just that experimental features are enabled in tests. * There are couple minor changes that affect code without variadic types. Previously tuple type context was used inconsistently for situations with star unpacks, I clean it up a bit (for my tests). Also I infer `Any`-like l.h.s types after an error in tuple unpacking (when needed) to avoid extra "Cannot determine type" errors in my tests. --- mypy/argmap.py | 23 ++- mypy/checker.py | 107 ++++++++++++- mypy/checkexpr.py | 113 +++++++++++++- mypy/constraints.py | 6 +- mypy/message_registry.py | 3 + mypyc/irbuild/mapper.py | 8 +- test-data/unit/check-tuples.test | 8 +- test-data/unit/check-typevar-tuple.test | 192 ++++++++++++++++++++++++ 8 files changed, 437 insertions(+), 23 deletions(-) diff --git a/mypy/argmap.py b/mypy/argmap.py index ec8463fd0625..e6700c9f1092 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -14,6 +14,8 @@ Type, TypedDictType, TypeOfAny, + TypeVarTupleType, + UnpackType, get_proper_type, ) @@ -174,6 +176,7 @@ def expand_actual_type( actual_kind: nodes.ArgKind, formal_name: str | None, formal_kind: nodes.ArgKind, + allow_unpack: bool = False, ) -> Type: """Return the actual (caller) type(s) of a formal argument with the given kinds. @@ -189,6 +192,11 @@ def expand_actual_type( original_actual = actual_type actual_type = get_proper_type(actual_type) if actual_kind == nodes.ARG_STAR: + if isinstance(actual_type, TypeVarTupleType): + # This code path is hit when *Ts is passed to a callable and various + # special-handling didn't catch this. The best thing we can do is to use + # the upper bound. + actual_type = get_proper_type(actual_type.upper_bound) if isinstance(actual_type, Instance) and actual_type.args: from mypy.subtypes import is_subtype @@ -209,7 +217,20 @@ def expand_actual_type( self.tuple_index = 1 else: self.tuple_index += 1 - return actual_type.items[self.tuple_index - 1] + item = actual_type.items[self.tuple_index - 1] + if isinstance(item, UnpackType) and not allow_unpack: + # An upack item that doesn't have special handling, use upper bound as above. + unpacked = get_proper_type(item.type) + if isinstance(unpacked, TypeVarTupleType): + fallback = get_proper_type(unpacked.upper_bound) + else: + fallback = unpacked + assert ( + isinstance(fallback, Instance) + and fallback.type.fullname == "builtins.tuple" + ) + item = fallback.args[0] + return item elif isinstance(actual_type, ParamSpecType): # ParamSpec is valid in *args but it can't be unpacked. return actual_type diff --git a/mypy/checker.py b/mypy/checker.py index 1a7a7e25d525..e1b65a95ae98 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -205,10 +205,13 @@ TypeType, TypeVarId, TypeVarLikeType, + TypeVarTupleType, TypeVarType, UnboundType, UninhabitedType, UnionType, + UnpackType, + find_unpack_in_list, flatten_nested_unions, get_proper_type, get_proper_types, @@ -3430,6 +3433,37 @@ def is_assignable_slot(self, lvalue: Lvalue, typ: Type | None) -> bool: return all(self.is_assignable_slot(lvalue, u) for u in typ.items) return False + def flatten_rvalues(self, rvalues: list[Expression]) -> list[Expression]: + """Flatten expression list by expanding those * items that have tuple type. + + For each regular type item in the tuple type use a TempNode(), for an Unpack + item use a corresponding StarExpr(TempNode()). + """ + new_rvalues = [] + for rv in rvalues: + if not isinstance(rv, StarExpr): + new_rvalues.append(rv) + continue + typ = get_proper_type(self.expr_checker.accept(rv.expr)) + if not isinstance(typ, TupleType): + new_rvalues.append(rv) + continue + for t in typ.items: + if not isinstance(t, UnpackType): + new_rvalues.append(TempNode(t)) + else: + unpacked = get_proper_type(t.type) + if isinstance(unpacked, TypeVarTupleType): + fallback = unpacked.upper_bound + else: + assert ( + isinstance(unpacked, Instance) + and unpacked.type.fullname == "builtins.tuple" + ) + fallback = unpacked + new_rvalues.append(StarExpr(TempNode(fallback))) + return new_rvalues + def check_assignment_to_multiple_lvalues( self, lvalues: list[Lvalue], @@ -3439,18 +3473,16 @@ def check_assignment_to_multiple_lvalues( ) -> None: if isinstance(rvalue, (TupleExpr, ListExpr)): # Recursively go into Tuple or List expression rhs instead of - # using the type of rhs, because this allowed more fine grained + # using the type of rhs, because this allows more fine-grained # control in cases like: a, b = [int, str] where rhs would get # type List[object] rvalues: list[Expression] = [] iterable_type: Type | None = None last_idx: int | None = None - for idx_rval, rval in enumerate(rvalue.items): + for idx_rval, rval in enumerate(self.flatten_rvalues(rvalue.items)): if isinstance(rval, StarExpr): typs = get_proper_type(self.expr_checker.accept(rval.expr)) - if isinstance(typs, TupleType): - rvalues.extend([TempNode(typ) for typ in typs.items]) - elif self.type_is_iterable(typs) and isinstance(typs, Instance): + if self.type_is_iterable(typs) and isinstance(typs, Instance): if iterable_type is not None and iterable_type != self.iterable_item_type( typs, rvalue ): @@ -3517,8 +3549,32 @@ def check_assignment_to_multiple_lvalues( self.check_multi_assignment(lvalues, rvalue, context, infer_lvalue_type) def check_rvalue_count_in_assignment( - self, lvalues: list[Lvalue], rvalue_count: int, context: Context + self, + lvalues: list[Lvalue], + rvalue_count: int, + context: Context, + rvalue_unpack: int | None = None, ) -> bool: + if rvalue_unpack is not None: + if not any(isinstance(e, StarExpr) for e in lvalues): + self.fail("Variadic tuple unpacking requires a star target", context) + return False + if len(lvalues) > rvalue_count: + self.fail(message_registry.TOO_MANY_TARGETS_FOR_VARIADIC_UNPACK, context) + return False + left_star_index = next(i for i, lv in enumerate(lvalues) if isinstance(lv, StarExpr)) + left_prefix = left_star_index + left_suffix = len(lvalues) - left_star_index - 1 + right_prefix = rvalue_unpack + right_suffix = rvalue_count - rvalue_unpack - 1 + if left_suffix > right_suffix or left_prefix > right_prefix: + # Case of asymmetric unpack like: + # rv: tuple[int, *Ts, int, int] + # x, y, *xs, z = rv + # it is technically valid, but is tricky to reason about. + # TODO: support this (at least if the r.h.s. unpack is a homogeneous tuple). + self.fail(message_registry.TOO_MANY_TARGETS_FOR_VARIADIC_UNPACK, context) + return True if any(isinstance(lvalue, StarExpr) for lvalue in lvalues): if len(lvalues) - 1 > rvalue_count: self.msg.wrong_number_values_to_unpack(rvalue_count, len(lvalues) - 1, context) @@ -3552,6 +3608,13 @@ def check_multi_assignment( if len(relevant_items) == 1: rvalue_type = get_proper_type(relevant_items[0]) + if ( + isinstance(rvalue_type, TupleType) + and find_unpack_in_list(rvalue_type.items) is not None + ): + # Normalize for consistent handling with "old-style" homogeneous tuples. + rvalue_type = expand_type(rvalue_type, {}) + if isinstance(rvalue_type, AnyType): for lv in lvalues: if isinstance(lv, StarExpr): @@ -3663,7 +3726,10 @@ def check_multi_assignment_from_tuple( undefined_rvalue: bool, infer_lvalue_type: bool = True, ) -> None: - if self.check_rvalue_count_in_assignment(lvalues, len(rvalue_type.items), context): + rvalue_unpack = find_unpack_in_list(rvalue_type.items) + if self.check_rvalue_count_in_assignment( + lvalues, len(rvalue_type.items), context, rvalue_unpack=rvalue_unpack + ): star_index = next( (i for i, lv in enumerate(lvalues) if isinstance(lv, StarExpr)), len(lvalues) ) @@ -3708,12 +3774,37 @@ def check_multi_assignment_from_tuple( self.check_assignment(lv, self.temp_node(rv_type, context), infer_lvalue_type) if star_lv: list_expr = ListExpr( - [self.temp_node(rv_type, context) for rv_type in star_rv_types] + [ + self.temp_node(rv_type, context) + if not isinstance(rv_type, UnpackType) + else StarExpr(self.temp_node(rv_type.type, context)) + for rv_type in star_rv_types + ] ) list_expr.set_line(context) self.check_assignment(star_lv.expr, list_expr, infer_lvalue_type) for lv, rv_type in zip(right_lvs, right_rv_types): self.check_assignment(lv, self.temp_node(rv_type, context), infer_lvalue_type) + else: + # Store meaningful Any types for lvalues, errors are already given + # by check_rvalue_count_in_assignment() + if infer_lvalue_type: + for lv in lvalues: + if ( + isinstance(lv, NameExpr) + and isinstance(lv.node, Var) + and lv.node.type is None + ): + lv.node.type = AnyType(TypeOfAny.from_error) + elif isinstance(lv, StarExpr): + if ( + isinstance(lv.expr, NameExpr) + and isinstance(lv.expr.node, Var) + and lv.expr.node.type is None + ): + lv.expr.node.type = self.named_generic_type( + "builtins.list", [AnyType(TypeOfAny.from_error)] + ) def lvalue_type_for_inference(self, lvalues: list[Lvalue], rvalue_type: TupleType) -> Type: star_index = next( diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a2141680b6cb..fd155ff87379 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -95,6 +95,7 @@ YieldExpr, YieldFromExpr, ) +from mypy.options import TYPE_VAR_TUPLE from mypy.plugin import ( FunctionContext, FunctionSigContext, @@ -2510,7 +2511,11 @@ def check_argument_types( ) self.msg.invalid_keyword_var_arg(actual_type, is_mapping, context) expanded_actual = mapper.expand_actual_type( - actual_type, actual_kind, callee.arg_names[i], callee_arg_kind + actual_type, + actual_kind, + callee.arg_names[i], + callee_arg_kind, + allow_unpack=isinstance(callee_arg_type, UnpackType), ) check_arg( expanded_actual, @@ -3338,7 +3343,45 @@ def visit_op_expr(self, e: OpExpr) -> Type: if isinstance(proper_right_type, TupleType): right_radd_method = proper_right_type.partial_fallback.type.get("__radd__") if right_radd_method is None: - return self.concat_tuples(proper_left_type, proper_right_type) + # One cannot have two variadic items in the same tuple. + if ( + find_unpack_in_list(proper_left_type.items) is None + or find_unpack_in_list(proper_right_type.items) is None + ): + return self.concat_tuples(proper_left_type, proper_right_type) + elif ( + TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature + and isinstance(proper_right_type, Instance) + and self.chk.type_is_iterable(proper_right_type) + ): + # Handle tuple[X, Y] + tuple[Z, ...] = tuple[X, Y, *tuple[Z, ...]]. + right_radd_method = proper_right_type.type.get("__radd__") + if ( + right_radd_method is None + and proper_left_type.partial_fallback.type.fullname == "builtins.tuple" + and find_unpack_in_list(proper_left_type.items) is None + ): + item_type = self.chk.iterable_item_type(proper_right_type, e) + mapped = self.chk.named_generic_type("builtins.tuple", [item_type]) + return proper_left_type.copy_modified( + items=proper_left_type.items + [UnpackType(mapped)] + ) + if TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature: + # Handle tuple[X, ...] + tuple[Y, Z] = tuple[*tuple[X, ...], Y, Z]. + if ( + e.op == "+" + and isinstance(proper_left_type, Instance) + and proper_left_type.type.fullname == "builtins.tuple" + ): + proper_right_type = get_proper_type(self.accept(e.right)) + if ( + isinstance(proper_right_type, TupleType) + and proper_right_type.partial_fallback.type.fullname == "builtins.tuple" + and find_unpack_in_list(proper_right_type.items) is None + ): + return proper_right_type.copy_modified( + items=[UnpackType(proper_left_type)] + proper_right_type.items + ) if e.op in operators.op_methods: method = operators.op_methods[e.op] @@ -4721,6 +4764,19 @@ def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: )[0] return remove_instance_last_known_values(out) + def tuple_context_matches(self, expr: TupleExpr, ctx: TupleType) -> bool: + ctx_unpack_index = find_unpack_in_list(ctx.items) + if ctx_unpack_index is None: + # For fixed tuples accept everything that can possibly match, even if this + # requires all star items to be empty. + return len([e for e in expr.items if not isinstance(e, StarExpr)]) <= len(ctx.items) + # For variadic context, the only easy case is when structure matches exactly. + # TODO: try using tuple type context in more cases. + if len([e for e in expr.items if not isinstance(e, StarExpr)]) != 1: + return False + expr_star_index = next(i for i, lv in enumerate(expr.items) if isinstance(lv, StarExpr)) + return len(expr.items) == len(ctx.items) and ctx_unpack_index == expr_star_index + def visit_tuple_expr(self, e: TupleExpr) -> Type: """Type check a tuple expression.""" # Try to determine type context for type inference. @@ -4730,7 +4786,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: tuples_in_context = [ t for t in get_proper_types(type_context.items) - if (isinstance(t, TupleType) and len(t.items) == len(e.items)) + if (isinstance(t, TupleType) and self.tuple_context_matches(e, t)) or is_named_instance(t, TUPLE_LIKE_INSTANCE_NAMES) ] if len(tuples_in_context) == 1: @@ -4740,7 +4796,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: # more than one. Either way, we can't decide on a context. pass - if isinstance(type_context, TupleType): + if isinstance(type_context, TupleType) and self.tuple_context_matches(e, type_context): type_context_items = type_context.items elif type_context and is_named_instance(type_context, TUPLE_LIKE_INSTANCE_NAMES): assert isinstance(type_context, Instance) @@ -4751,6 +4807,11 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: # items that match a position in e, and we'll worry about type # mismatches later. + unpack_in_context = False + if type_context_items is not None: + unpack_in_context = find_unpack_in_list(type_context_items) is not None + seen_unpack_in_items = False + # Infer item types. Give up if there's a star expression # that's not a Tuple. items: list[Type] = [] @@ -4763,12 +4824,44 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: # TupleExpr, flatten it, so we can benefit from the # context? Counterargument: Why would anyone write # (1, *(2, 3)) instead of (1, 2, 3) except in a test? - tt = self.accept(item.expr) + if unpack_in_context: + # Note: this logic depends on full structure match in tuple_context_matches(). + assert type_context_items + ctx_item = type_context_items[j] + assert isinstance(ctx_item, UnpackType) + ctx = ctx_item.type + else: + ctx = None + tt = self.accept(item.expr, ctx) tt = get_proper_type(tt) if isinstance(tt, TupleType): + if find_unpack_in_list(tt.items) is not None: + if seen_unpack_in_items: + # Multiple unpack items are not allowed in tuples, + # fall back to instance type. + return self.check_lst_expr(e, "builtins.tuple", "") + else: + seen_unpack_in_items = True items.extend(tt.items) - j += len(tt.items) + # Note: this logic depends on full structure match in tuple_context_matches(). + if unpack_in_context: + j += 1 + else: + # If there is an unpack in expressions, but not in context, this will + # result in an error later, just do something predictable here. + j += len(tt.items) else: + if ( + TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature + and not seen_unpack_in_items + ): + # Handle (x, *y, z), where y is e.g. tuple[Y, ...]. + if isinstance(tt, Instance) and self.chk.type_is_iterable(tt): + item_type = self.chk.iterable_item_type(tt, e) + mapped = self.chk.named_generic_type("builtins.tuple", [item_type]) + items.append(UnpackType(mapped)) + seen_unpack_in_items = True + continue # A star expression that's not a Tuple. # Treat the whole thing as a variable-length tuple. return self.check_lst_expr(e, "builtins.tuple", "") @@ -4781,7 +4874,13 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: items.append(tt) # This is a partial fallback item type. A precise type will be calculated on demand. fallback_item = AnyType(TypeOfAny.special_form) - return TupleType(items, self.chk.named_generic_type("builtins.tuple", [fallback_item])) + result: ProperType = TupleType( + items, self.chk.named_generic_type("builtins.tuple", [fallback_item]) + ) + if seen_unpack_in_items: + # Return already normalized tuple type just in case. + result = expand_type(result, {}) + return result def fast_dict_type(self, e: DictExpr) -> Type | None: """ diff --git a/mypy/constraints.py b/mypy/constraints.py index ebd6765e8e82..58d0f4dbed29 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -156,7 +156,11 @@ def infer_constraints_for_callable( continue expanded_actual = mapper.expand_actual_type( - actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] + actual_arg_type, + arg_kinds[actual], + callee.arg_names[i], + callee.arg_kinds[i], + allow_unpack=True, ) if arg_kinds[actual] != ARG_STAR or isinstance( diff --git a/mypy/message_registry.py b/mypy/message_registry.py index d75a1fab1b66..dc46eb503390 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -84,6 +84,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage: MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None') TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range") AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE: Final = ErrorMessage("Ambiguous slice of a variadic tuple") +TOO_MANY_TARGETS_FOR_VARIADIC_UNPACK: Final = ErrorMessage( + "Too many assignment targets for variadic unpack" +) INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer, SupportsIndex or None") CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda") CANNOT_ACCESS_INIT: Final = ( diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 5b77b4b1537b..a3abbb1f84fb 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -19,6 +19,7 @@ UnboundType, UninhabitedType, UnionType, + find_unpack_in_list, get_proper_type, ) from mypyc.ir.class_ir import ClassIR @@ -112,8 +113,11 @@ def type_to_rtype(self, typ: Type | None) -> RType: return object_rprimitive elif isinstance(typ, TupleType): # Use our unboxed tuples for raw tuples but fall back to - # being boxed for NamedTuple. - if typ.partial_fallback.type.fullname == "builtins.tuple": + # being boxed for NamedTuple or for variadic tuples. + if ( + typ.partial_fallback.type.fullname == "builtins.tuple" + and find_unpack_in_list(typ.items) is None + ): return RTuple([self.type_to_rtype(t) for t in typ.items]) else: return tuple_rprimitive diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 1447321c0c49..76225360a7c1 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1100,15 +1100,15 @@ reveal_type(b) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtin [case testTupleWithStarExpr2] a = [1] b = (0, *a) -reveal_type(b) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr3] a = [''] b = (0, *a) -reveal_type(b) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" c = (*a, '') -reveal_type(c) # N: Revealed type is "builtins.tuple[builtins.str, ...]" +reveal_type(c) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr4] @@ -1333,7 +1333,7 @@ reveal_type(subtup if int() else tup2) # N: Revealed type is "builtins.tuple[bu [case testTupleWithUndersizedContext] a = ([1], 'x') if int(): - a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]") + a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[Never], str, int]", variable has type "Tuple[List[int], str]") [builtins fixtures/tuple.pyi] [case testTupleWithOversizedContext] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 850b7ef8a524..0212518bdec0 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1527,6 +1527,198 @@ x = c1 x = c2 [builtins fixtures/tuple.pyi] +[case testUnpackingVariadicTuplesTypeVar] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: + x1, y1, z1 = arg # E: Variadic tuple unpacking requires a star target + reveal_type(x1) # N: Revealed type is "Any" + reveal_type(y1) # N: Revealed type is "Any" + reveal_type(z1) # N: Revealed type is "Any" + x2, *y2, z2 = arg + reveal_type(x2) # N: Revealed type is "builtins.int" + reveal_type(y2) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z2) # N: Revealed type is "builtins.str" + x3, *y3 = arg + reveal_type(x3) # N: Revealed type is "builtins.int" + reveal_type(y3) # N: Revealed type is "builtins.list[builtins.object]" + *y4, z4 = arg + reveal_type(y4) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z4) # N: Revealed type is "builtins.str" + x5, xx5, *y5, z5, zz5 = arg # E: Too many assignment targets for variadic unpack + reveal_type(x5) # N: Revealed type is "Any" + reveal_type(xx5) # N: Revealed type is "Any" + reveal_type(y5) # N: Revealed type is "builtins.list[Any]" + reveal_type(z5) # N: Revealed type is "Any" + reveal_type(zz5) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testUnpackingVariadicTuplesHomogeneous] +from typing import Tuple +from typing_extensions import Unpack + +def bar(arg: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + x1, y1, z1 = arg # E: Variadic tuple unpacking requires a star target + reveal_type(x1) # N: Revealed type is "Any" + reveal_type(y1) # N: Revealed type is "Any" + reveal_type(z1) # N: Revealed type is "Any" + x2, *y2, z2 = arg + reveal_type(x2) # N: Revealed type is "builtins.int" + reveal_type(y2) # N: Revealed type is "builtins.list[builtins.float]" + reveal_type(z2) # N: Revealed type is "builtins.str" + x3, *y3 = arg + reveal_type(x3) # N: Revealed type is "builtins.int" + reveal_type(y3) # N: Revealed type is "builtins.list[builtins.object]" + *y4, z4 = arg + reveal_type(y4) # N: Revealed type is "builtins.list[builtins.float]" + reveal_type(z4) # N: Revealed type is "builtins.str" + x5, xx5, *y5, z5, zz5 = arg # E: Too many assignment targets for variadic unpack + reveal_type(x5) # N: Revealed type is "Any" + reveal_type(xx5) # N: Revealed type is "Any" + reveal_type(y5) # N: Revealed type is "builtins.list[Any]" + reveal_type(z5) # N: Revealed type is "Any" + reveal_type(zz5) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testRepackingVariadicTuplesTypeVar] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: + x1, *y1, z1 = *arg, + reveal_type(x1) # N: Revealed type is "builtins.int" + reveal_type(y1) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z1) # N: Revealed type is "builtins.str" + x2, *y2, z2 = 1, *arg, 2 + reveal_type(x2) # N: Revealed type is "builtins.int" + reveal_type(y2) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z2) # N: Revealed type is "builtins.int" + x3, *y3 = *arg, 42 + reveal_type(x3) # N: Revealed type is "builtins.int" + reveal_type(y3) # N: Revealed type is "builtins.list[builtins.object]" + *y4, z4 = 42, *arg + reveal_type(y4) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z4) # N: Revealed type is "builtins.str" + x5, xx5, *y5, z5, zz5 = 1, *arg, 2 + reveal_type(x5) # N: Revealed type is "builtins.int" + reveal_type(xx5) # N: Revealed type is "builtins.int" + reveal_type(y5) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z5) # N: Revealed type is "builtins.str" + reveal_type(zz5) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testRepackingVariadicTuplesHomogeneous] +from typing import Tuple +from typing_extensions import Unpack + +def foo(arg: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + x1, *y1, z1 = *arg, + reveal_type(x1) # N: Revealed type is "builtins.int" + reveal_type(y1) # N: Revealed type is "builtins.list[builtins.float]" + reveal_type(z1) # N: Revealed type is "builtins.str" + x2, *y2, z2 = 1, *arg, 2 + reveal_type(x2) # N: Revealed type is "builtins.int" + reveal_type(y2) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z2) # N: Revealed type is "builtins.int" + x3, *y3 = *arg, 42 + reveal_type(x3) # N: Revealed type is "builtins.int" + reveal_type(y3) # N: Revealed type is "builtins.list[builtins.object]" + *y4, z4 = 42, *arg + reveal_type(y4) # N: Revealed type is "builtins.list[builtins.float]" + reveal_type(z4) # N: Revealed type is "builtins.str" + x5, xx5, *y5, z5, zz5 = 1, *arg, 2 + reveal_type(x5) # N: Revealed type is "builtins.int" + reveal_type(xx5) # N: Revealed type is "builtins.int" + reveal_type(y5) # N: Revealed type is "builtins.list[builtins.float]" + reveal_type(z5) # N: Revealed type is "builtins.str" + reveal_type(zz5) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testPackingVariadicTuplesTypeVar] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: + x = *arg, + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + y = 1, *arg, 2 + reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[Ts`-1], builtins.str, builtins.int]" + z = (*arg, *arg) + reveal_type(z) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +[builtins fixtures/tuple.pyi] + +[case testPackingVariadicTuplesHomogeneous] +from typing import Tuple +from typing_extensions import Unpack + +a: Tuple[float, ...] +b: Tuple[int, Unpack[Tuple[float, ...]], str] + +x = *a, +reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.float, ...]" +y = 1, *a, 2 +reveal_type(y) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" +z = (*a, *a) +reveal_type(z) # N: Revealed type is "builtins.tuple[builtins.float, ...]" + +x2 = *b, +reveal_type(x2) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" +y2 = 1, *b, 2 +reveal_type(y2) # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str, builtins.int]" +z2 = (*b, *b) +reveal_type(z2) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleInListSetExpr] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +vt: Tuple[int, Unpack[Tuple[float, ...]], int] +reveal_type([1, *vt]) # N: Revealed type is "builtins.list[builtins.float]" +reveal_type({1, *vt}) # N: Revealed type is "builtins.set[builtins.float]" + +Ts = TypeVarTuple("Ts") +def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: + reveal_type([1, *arg]) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type({1, *arg}) # N: Revealed type is "builtins.set[builtins.object]" +[builtins fixtures/isinstancelist.pyi] + +[case testVariadicTupleInTupleContext] +from typing import Tuple, Optional +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def test(x: Optional[Tuple[Unpack[Ts]]] = None) -> Tuple[Unpack[Ts]]: ... + +vt: Tuple[int, Unpack[Tuple[float, ...]], int] +vt = 1, *test(), 2 # OK, type context is used +vt2 = 1, *test(), 2 # E: Need type annotation for "vt2" +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleConcatenation] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +vtf: Tuple[float, ...] +vt: Tuple[int, Unpack[Tuple[float, ...]], int] + +reveal_type(vt + (1, 2)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]" +reveal_type((1, 2) + vt) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" +reveal_type(vt + vt) # N: Revealed type is "builtins.tuple[builtins.float, ...]" +reveal_type(vtf + (1, 2)) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]" +reveal_type((1, 2) + vtf) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]" + +Ts = TypeVarTuple("Ts") +def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: + reveal_type(arg + (1, 2)) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str, Literal[1]?, Literal[2]?]" + reveal_type((1, 2) + arg) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(arg + arg) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleAnyOverload] from typing import Any, Generic, overload, Tuple from typing_extensions import TypeVarTuple, Unpack From 3c7bdb22407dea87039e9fd3c551df157794c9f0 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 8 Oct 2023 21:36:58 +0300 Subject: [PATCH 0305/1617] Use SPDX license identifier (#16230) It does not change the license itself, only its identifier in `setup.py`, so external tools can read it better. Full list: https://spdx.org/licenses/ Closes https://github.com/python/mypy/issues/16228 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7e7793a406d0..5dba26fb10e0 100644 --- a/setup.py +++ b/setup.py @@ -202,7 +202,7 @@ def run(self): author="Jukka Lehtosalo", author_email="jukka.lehtosalo@iki.fi", url="https://www.mypy-lang.org/", - license="MIT License", + license="MIT", py_modules=[], ext_modules=ext_modules, packages=find_packages(), From ff7ac75387d3b5c7d0eaa4573bf2a0723bf3a3fc Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 8 Oct 2023 11:39:01 -0700 Subject: [PATCH 0306/1617] Add an extra for mypyc dependencies (#16229) Fixes #15579 --- mypyc/doc/getting_started.rst | 6 +++--- setup.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mypyc/doc/getting_started.rst b/mypyc/doc/getting_started.rst index 2db8aae149ec..adc617419ffa 100644 --- a/mypyc/doc/getting_started.rst +++ b/mypyc/doc/getting_started.rst @@ -38,17 +38,17 @@ Installation ------------ Mypyc is shipped as part of the mypy distribution. Install mypy like -this (you need Python 3.5 or later): +this (you need Python 3.8 or later): .. code-block:: - $ python3 -m pip install -U mypy + $ python3 -m pip install -U 'mypy[mypyc]' On some systems you need to use this instead: .. code-block:: - $ python -m pip install -U mypy + $ python -m pip install -U 'mypy[mypyc]' Example program --------------- diff --git a/setup.py b/setup.py index 5dba26fb10e0..dcbdc96b3ccf 100644 --- a/setup.py +++ b/setup.py @@ -227,6 +227,7 @@ def run(self): # Same here. extras_require={ "dmypy": "psutil >= 4.0", + "mypyc": "setuptools >= 50", "python2": "", "reports": "lxml", "install-types": "pip", From e87b62fcda423a9cd6db9076f66459fe47491568 Mon Sep 17 00:00:00 2001 From: KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:00:29 +1000 Subject: [PATCH 0307/1617] =?UTF-8?q?(=F0=9F=8E=81)=20drop=20'dev'=20from?= =?UTF-8?q?=203.12=20in=20the=20CI=20(#16239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: KotlinIsland --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3bcd9e059589..afa5d5823ea9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,7 @@ jobs: tox_extra_args: "-n 2" test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled - python: '3.12-dev' + python: '3.12' arch: x64 os: ubuntu-latest toxenv: py From 8b6d21373f44959d8aa194723e871e5468ad5c71 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 9 Oct 2023 23:33:18 -0700 Subject: [PATCH 0308/1617] Fix partially defined in the case of missing type maps (#15995) Thanks AlexWaygood for sending me on this adventure. This took me a while for me to debug! When we don't need to warn about unreachable code, we don't end up calling `self.is_noop_for_reachability(s)` (which is meant to tell us whether the code should be warned about or is `raise AssertionError` or `typing.assert_never(never)` or something). https://github.com/python/mypy/blob/6f650cff9ab21f81069e0ae30c92eae94219ea63/mypy/checker.py#L2748 This innocuous check has a side effect that turns out to be important for the partially undefined checks. These checks work by reaching into the type map populated by the checker. But if we never actually ended up analysing the code, we never populate the type map. This therefore changes things to assume that if we couldn't find the expression in the type map, it's probably because it was unreachable. --- mypy/partially_defined.py | 2 +- test-data/unit/check-possibly-undefined.test | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index 47cbd671f168..b7f577110fa8 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -506,7 +506,7 @@ def visit_break_stmt(self, o: BreakStmt) -> None: self.tracker.skip_branch() def visit_expression_stmt(self, o: ExpressionStmt) -> None: - if isinstance(self.type_map.get(o.expr, None), UninhabitedType): + if isinstance(self.type_map.get(o.expr, None), (UninhabitedType, type(None))): self.tracker.skip_branch() super().visit_expression_stmt(o) diff --git a/test-data/unit/check-possibly-undefined.test b/test-data/unit/check-possibly-undefined.test index ebceef88b537..ae277949c049 100644 --- a/test-data/unit/check-possibly-undefined.test +++ b/test-data/unit/check-possibly-undefined.test @@ -1026,3 +1026,20 @@ class B: else: # Same as above but in a loop. b = a # E: Name "a" may be undefined + +[case testUnreachableCausingMissingTypeMap] +# flags: --enable-error-code possibly-undefined --enable-error-code used-before-def --no-warn-unreachable +# Regression test for https://github.com/python/mypy/issues/15958 +from typing import Union, NoReturn + +def assert_never(__x: NoReturn) -> NoReturn: ... + +def foo(x: Union[int, str]) -> None: + if isinstance(x, str): + f = "foo" + elif isinstance(x, int): + f = "bar" + else: + assert_never(x) + f # OK +[builtins fixtures/tuple.pyi] From 2c1009ed7cde0247c859bbccb852490b8c91bd97 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:58:18 +0100 Subject: [PATCH 0309/1617] show dmypy errors post serving (#16250) After dmypy starts serving, stdout and stderr gets captured. If we have an error, we assume we can send it to the client. However, if we have an error outside of client communication, that error is lost. The easiest way to see this is to run dmypy in daemonize mode, run a check once, then Control-C to send a KeyboardInterrupt. That exception is not printed though it should. After this change you can clearly see it. ``` term1$ python3 -m mypy.dmypy daemon term2$ python3 -m mypy.dmypy check -v test.py [... some output ...] term1$ [Control-C] ^CTraceback (most recent call last): File "/home/svalentin/src/mypy-svalentin/mypy/dmypy_server.py", line 220, in serve with server: File "/home/svalentin/src/mypy-svalentin/mypy/ipc.py", line 232, in __enter__ self.connection, _ = self.sock.accept() File "/usr/lib/python3.8/socket.py", line 292, in accept fd, addr = self._accept() KeyboardInterrupt Traceback (most recent call last): File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main return _run_code(code, main_globals, None, File "/usr/lib/python3.8/runpy.py", line 87, in _run_code exec(code, run_globals) File "/home/svalentin/src/mypy-svalentin/mypy/dmypy/__main__.py", line 6, in console_entry() File "/home/svalentin/src/mypy-svalentin/mypy/dmypy/client.py", line 748, in console_entry main(sys.argv[1:]) File "/home/svalentin/src/mypy-svalentin/mypy/dmypy/client.py", line 275, in main args.action(args) File "/home/svalentin/src/mypy-svalentin/mypy/dmypy/client.py", line 629, in do_daemon Server(options, args.status_file, timeout=args.timeout).serve() File "/home/svalentin/src/mypy-svalentin/mypy/dmypy_server.py", line 220, in serve with server: File "/home/svalentin/src/mypy-svalentin/mypy/ipc.py", line 232, in __enter__ self.connection, _ = self.sock.accept() File "/usr/lib/python3.8/socket.py", line 292, in accept fd, addr = self._accept() KeyboardInterrupt ``` --- mypy/dmypy_server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index a50ebc5415ba..faa9a23fadfb 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -210,6 +210,8 @@ def serve(self) -> None: """Serve requests, synchronously (no thread or fork).""" command = None server = IPCServer(CONNECTION_NAME, self.timeout) + orig_stdout = sys.stdout + orig_stderr = sys.stderr try: with open(self.status_file, "w") as f: json.dump({"pid": os.getpid(), "connection_name": server.connection_name}, f) @@ -252,6 +254,10 @@ def serve(self) -> None: reset_global_state() sys.exit(0) finally: + # Revert stdout/stderr so we can see any errors. + sys.stdout = orig_stdout + sys.stderr = orig_stderr + # If the final command is something other than a clean # stop, remove the status file. (We can't just # simplify the logic and always remove the file, since From 72605dc12a89b9c12a502ebfad494b4b9d9b5160 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 12 Oct 2023 21:25:30 +0100 Subject: [PATCH 0310/1617] Fix crash on ParamSpec unification (#16251) Fixes https://github.com/python/mypy/issues/16245 Fixes https://github.com/python/mypy/issues/16248 Unfortunately I was a bit reckless with parentheses, but in my defense `unify_generic_callable()` is kind of broken for long time, as it can return "solutions" like ```{1: T`1}```. We need a more principled approach there (IIRC there is already an issue about this in the scope of `--new-type-inference`). (The fix is quite trivial so I am not going to wait for review too long to save time, unless there will be some issues in `mypy_primer` etc.) --- mypy/expandtype.py | 10 +++-- mypy/types.py | 10 ----- .../unit/check-parameter-specification.test | 37 +++++++++++++++++++ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index b233561e19c2..4acb51e22268 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -241,7 +241,7 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: return repl.copy_modified( flavor=t.flavor, prefix=t.prefix.copy_modified( - arg_types=self.expand_types(t.prefix.arg_types + repl.prefix.arg_types), + arg_types=self.expand_types(t.prefix.arg_types) + repl.prefix.arg_types, arg_kinds=t.prefix.arg_kinds + repl.prefix.arg_kinds, arg_names=t.prefix.arg_names + repl.prefix.arg_names, ), @@ -249,7 +249,7 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: elif isinstance(repl, Parameters): assert t.flavor == ParamSpecFlavor.BARE return Parameters( - self.expand_types(t.prefix.arg_types + repl.arg_types), + self.expand_types(t.prefix.arg_types) + repl.arg_types, t.prefix.arg_kinds + repl.arg_kinds, t.prefix.arg_names + repl.arg_names, variables=[*t.prefix.variables, *repl.variables], @@ -333,12 +333,14 @@ def visit_callable_type(self, t: CallableType) -> CallableType: # the replacement is ignored. if isinstance(repl, Parameters): # We need to expand both the types in the prefix and the ParamSpec itself - t = t.expand_param_spec(repl) return t.copy_modified( - arg_types=self.expand_types(t.arg_types), + arg_types=self.expand_types(t.arg_types[:-2]) + repl.arg_types, + arg_kinds=t.arg_kinds[:-2] + repl.arg_kinds, + arg_names=t.arg_names[:-2] + repl.arg_names, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), imprecise_arg_kinds=(t.imprecise_arg_kinds or repl.imprecise_arg_kinds), + variables=[*repl.variables, *t.variables], ) elif isinstance(repl, ParamSpecType): # We're substituting one ParamSpec for another; this can mean that the prefix diff --git a/mypy/types.py b/mypy/types.py index 34ea96be25ee..09ba68aae88a 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2069,16 +2069,6 @@ def param_spec(self) -> ParamSpecType | None: prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) - def expand_param_spec(self, c: Parameters) -> CallableType: - variables = c.variables - return self.copy_modified( - arg_types=self.arg_types[:-2] + c.arg_types, - arg_kinds=self.arg_kinds[:-2] + c.arg_kinds, - arg_names=self.arg_names[:-2] + c.arg_names, - is_ellipsis_args=c.is_ellipsis_args, - variables=[*variables, *self.variables], - ) - def with_unpacked_kwargs(self) -> NormalizedCallableType: if not self.unpack_kwargs: return cast(NormalizedCallableType, self) diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index da831d29dd43..bb7859070f00 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1976,3 +1976,40 @@ g(cb, y=0, x='a') # OK g(cb, y='a', x=0) # E: Argument "y" to "g" has incompatible type "str"; expected "int" \ # E: Argument "x" to "g" has incompatible type "int"; expected "str" [builtins fixtures/paramspec.pyi] + +[case testParamSpecNoCrashOnUnificationAlias] +import mod +[file mod.pyi] +from typing import Callable, Protocol, TypeVar, overload +from typing_extensions import ParamSpec + +P = ParamSpec("P") +R_co = TypeVar("R_co", covariant=True) +Handler = Callable[P, R_co] + +class HandlerDecorator(Protocol): + def __call__(self, handler: Handler[P, R_co]) -> Handler[P, R_co]: ... + +@overload +def event(event_handler: Handler[P, R_co]) -> Handler[P, R_co]: ... +@overload +def event(namespace: str, *args, **kwargs) -> HandlerDecorator: ... +[builtins fixtures/paramspec.pyi] + +[case testParamSpecNoCrashOnUnificationCallable] +import mod +[file mod.pyi] +from typing import Callable, Protocol, TypeVar, overload +from typing_extensions import ParamSpec + +P = ParamSpec("P") +R_co = TypeVar("R_co", covariant=True) + +class HandlerDecorator(Protocol): + def __call__(self, handler: Callable[P, R_co]) -> Callable[P, R_co]: ... + +@overload +def event(event_handler: Callable[P, R_co]) -> Callable[P, R_co]: ... +@overload +def event(namespace: str, *args, **kwargs) -> HandlerDecorator: ... +[builtins fixtures/paramspec.pyi] From fbc48afccdf47de43fba73f2bc0eaf43a3f7b310 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 13 Oct 2023 11:28:41 +0200 Subject: [PATCH 0311/1617] Fix `coverage` config (#16258) fixes #16255 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de32618f1a39..c43253fed982 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,13 +109,13 @@ xfail_strict = true [tool.coverage.run] branch = true -source = "mypy" +source = ["mypy"] parallel = true [tool.coverage.report] show_missing = true skip_covered = true -omit = 'mypy/test/*' +omit = ['mypy/test/*'] exclude_lines = [ '\#\s*pragma: no cover', '^\s*raise AssertionError\b', From 2e52e98fd2873775a58616c097e93c96f58fc991 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 13 Oct 2023 11:30:54 +0100 Subject: [PATCH 0312/1617] Fix crash on ParamSpec unification (for real) (#16259) Fixes https://github.com/python/mypy/issues/16257 Parenthesis strike back. I hope this is the last place where I had put them wrong. --- mypy/expandtype.py | 3 +- .../unit/check-parameter-specification.test | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 4acb51e22268..44716e6da013 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -348,7 +348,8 @@ def visit_callable_type(self, t: CallableType) -> CallableType: prefix = repl.prefix clean_repl = repl.copy_modified(prefix=Parameters([], [], [])) return t.copy_modified( - arg_types=self.expand_types(t.arg_types[:-2] + prefix.arg_types) + arg_types=self.expand_types(t.arg_types[:-2]) + + prefix.arg_types + [ clean_repl.with_flavor(ParamSpecFlavor.ARGS), clean_repl.with_flavor(ParamSpecFlavor.KWARGS), diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index bb7859070f00..5b6024da687e 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2013,3 +2013,36 @@ def event(event_handler: Callable[P, R_co]) -> Callable[P, R_co]: ... @overload def event(namespace: str, *args, **kwargs) -> HandlerDecorator: ... [builtins fixtures/paramspec.pyi] + +[case testParamSpecNoCrashOnUnificationPrefix] +from typing import Any, Callable, TypeVar, overload +from typing_extensions import ParamSpec, Concatenate + +T = TypeVar("T") +U = TypeVar("U") +V = TypeVar("V") +W = TypeVar("W") +P = ParamSpec("P") + +@overload +def call( + func: Callable[Concatenate[T, P], U], + x: T, + *args: Any, + **kwargs: Any, +) -> U: ... +@overload +def call( + func: Callable[Concatenate[T, U, P], V], + x: T, + y: U, + *args: Any, + **kwargs: Any, +) -> V: ... +def call(*args: Any, **kwargs: Any) -> Any: ... + +def test1(x: int) -> str: ... +def test2(x: int, y: int) -> str: ... +reveal_type(call(test1, 1)) # N: Revealed type is "builtins.str" +reveal_type(call(test2, 1, 2)) # N: Revealed type is "builtins.str" +[builtins fixtures/paramspec.pyi] From feb0fa75ca7f3abb1217d94f6ffb55994b9a31c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 00:33:28 -0700 Subject: [PATCH 0313/1617] Sync typeshed (#16266) --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_ast.pyi | 2 +- mypy/typeshed/stdlib/_ctypes.pyi | 2 + mypy/typeshed/stdlib/_curses.pyi | 3 +- mypy/typeshed/stdlib/_locale.pyi | 100 +++++++++ mypy/typeshed/stdlib/_msi.pyi | 1 + mypy/typeshed/stdlib/_winapi.pyi | 1 + mypy/typeshed/stdlib/argparse.pyi | 6 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 68 ++++++- .../stdlib/asyncio/windows_events.pyi | 12 +- mypy/typeshed/stdlib/locale.pyi | 191 +++++++++--------- mypy/typeshed/stdlib/mimetypes.pyi | 3 +- mypy/typeshed/stdlib/mmap.pyi | 2 +- mypy/typeshed/stdlib/msilib/text.pyi | 2 +- mypy/typeshed/stdlib/msvcrt.pyi | 6 +- mypy/typeshed/stdlib/os/__init__.pyi | 19 +- mypy/typeshed/stdlib/posix.pyi | 5 +- mypy/typeshed/stdlib/select.pyi | 4 +- mypy/typeshed/stdlib/selectors.pyi | 18 +- mypy/typeshed/stdlib/signal.pyi | 10 +- mypy/typeshed/stdlib/socket.pyi | 4 +- mypy/typeshed/stdlib/ssl.pyi | 6 +- mypy/typeshed/stdlib/subprocess.pyi | 1 + mypy/typeshed/stdlib/winreg.pyi | 2 + mypy/typeshed/stdlib/winsound.pyi | 1 + mypy/typeshed/stdlib/zipfile.pyi | 36 +++- 26 files changed, 363 insertions(+), 143 deletions(-) create mode 100644 mypy/typeshed/stdlib/_locale.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 49433e346765..9d4636a29a1d 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -35,6 +35,7 @@ _dummy_threading: 2.7-3.8 _heapq: 2.7- _imp: 3.0- _json: 2.7- +_locale: 2.7- _markupbase: 2.7- _msi: 2.7- _operator: 3.4- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 05e2a08fdc88..402b770c0462 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -602,7 +602,7 @@ if sys.version_info >= (3, 12): name: _Identifier class TypeAlias(stmt): - __match_args__ = ("name", "typeparams", "value") + __match_args__ = ("name", "type_params", "value") name: Name type_params: list[type_param] value: expr diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index b48b1f7d318c..8a891971e9f1 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -44,6 +44,8 @@ if sys.platform == "win32": def FormatError(code: int = ...) -> str: ... def get_last_error() -> int: ... def set_last_error(value: int) -> int: ... + def LoadLibrary(__name: str, __load_flags: int = 0) -> int: ... + def FreeLibrary(__handle: int) -> None: ... class _CDataMeta(type): # By default mypy complains about the following two methods, because strictly speaking cls diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index e2319a5fcc1f..3604f7abedb5 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -61,7 +61,8 @@ if sys.platform != "win32": A_DIM: int A_HORIZONTAL: int A_INVIS: int - A_ITALIC: int + if sys.platform != "darwin": + A_ITALIC: int A_LEFT: int A_LOW: int A_NORMAL: int diff --git a/mypy/typeshed/stdlib/_locale.pyi b/mypy/typeshed/stdlib/_locale.pyi new file mode 100644 index 000000000000..2b2fe03e4510 --- /dev/null +++ b/mypy/typeshed/stdlib/_locale.pyi @@ -0,0 +1,100 @@ +import sys +from _typeshed import StrPath +from collections.abc import Iterable, Mapping + +LC_CTYPE: int +LC_COLLATE: int +LC_TIME: int +LC_MONETARY: int +LC_NUMERIC: int +LC_ALL: int +CHAR_MAX: int + +def setlocale(category: int, locale: str | Iterable[str | None] | None = None) -> str: ... +def localeconv() -> Mapping[str, int | str | list[int]]: ... + +if sys.version_info >= (3, 11): + def getencoding() -> str: ... + +def strcoll(__os1: str, __os2: str) -> int: ... +def strxfrm(__string: str) -> str: ... + +# native gettext functions +# https://docs.python.org/3/library/locale.html#access-to-message-catalogs +# https://github.com/python/cpython/blob/f4c03484da59049eb62a9bf7777b963e2267d187/Modules/_localemodule.c#L626 +if sys.platform != "win32": + LC_MESSAGES: int + + ABDAY_1: int + ABDAY_2: int + ABDAY_3: int + ABDAY_4: int + ABDAY_5: int + ABDAY_6: int + ABDAY_7: int + + ABMON_1: int + ABMON_2: int + ABMON_3: int + ABMON_4: int + ABMON_5: int + ABMON_6: int + ABMON_7: int + ABMON_8: int + ABMON_9: int + ABMON_10: int + ABMON_11: int + ABMON_12: int + + DAY_1: int + DAY_2: int + DAY_3: int + DAY_4: int + DAY_5: int + DAY_6: int + DAY_7: int + + ERA: int + ERA_D_T_FMT: int + ERA_D_FMT: int + ERA_T_FMT: int + + MON_1: int + MON_2: int + MON_3: int + MON_4: int + MON_5: int + MON_6: int + MON_7: int + MON_8: int + MON_9: int + MON_10: int + MON_11: int + MON_12: int + + CODESET: int + D_T_FMT: int + D_FMT: int + T_FMT: int + T_FMT_AMPM: int + AM_STR: int + PM_STR: int + + RADIXCHAR: int + THOUSEP: int + YESEXPR: int + NOEXPR: int + CRNCYSTR: int + ALT_DIGITS: int + + def nl_langinfo(__key: int) -> str: ... + + # This is dependent on `libintl.h` which is a part of `gettext` + # system dependency. These functions might be missing. + # But, we always say that they are present. + def gettext(__msg: str) -> str: ... + def dgettext(__domain: str | None, __msg: str) -> str: ... + def dcgettext(__domain: str | None, __msg: str, __category: int) -> str: ... + def textdomain(__domain: str | None) -> str: ... + def bindtextdomain(__domain: str, __dir: StrPath | None) -> str: ... + def bind_textdomain_codeset(__domain: str, __codeset: str | None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi index 2fdbdfd0e9f4..160406a6d8d5 100644 --- a/mypy/typeshed/stdlib/_msi.pyi +++ b/mypy/typeshed/stdlib/_msi.pyi @@ -1,6 +1,7 @@ import sys if sys.platform == "win32": + class MSIError(Exception): ... # Actual typename View, not exposed by the implementation class _View: def Execute(self, params: _Record | None = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index b51d844701ac..e887fb38a7fa 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -255,3 +255,4 @@ if sys.platform == "win32": if sys.version_info >= (3, 12): def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ... + def NeedCurrentDirectoryForExePath(__exe_name: str) -> bool: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 0004250b17a9..924cc8986114 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -342,11 +342,11 @@ if sys.version_info >= (3, 12): option_strings: Sequence[str], dest: str, default: _T | str | None = None, - type: Callable[[str], _T] | FileType | None = sentinel, # noqa: Y011 - choices: Iterable[_T] | None = sentinel, # noqa: Y011 + type: Callable[[str], _T] | FileType | None = sentinel, + choices: Iterable[_T] | None = sentinel, required: bool = False, help: str | None = None, - metavar: str | tuple[str, ...] | None = sentinel, # noqa: Y011 + metavar: str | tuple[str, ...] | None = sentinel, ) -> None: ... elif sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index b6929deb0fae..366ac7fa35e3 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -68,6 +68,7 @@ _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") +_T6 = TypeVar("_T6") _FT = TypeVar("_FT", bound=Future[Any]) _FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] _TaskYieldType: TypeAlias = Future[object] | None @@ -131,6 +132,19 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload + def gather( # type: ignore[misc] + __coro_or_future1: _FutureLike[_T1], + __coro_or_future2: _FutureLike[_T2], + __coro_or_future3: _FutureLike[_T3], + __coro_or_future4: _FutureLike[_T4], + __coro_or_future5: _FutureLike[_T5], + __coro_or_future6: _FutureLike[_T6], + *, + return_exceptions: Literal[False] = False, + ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... + @overload + def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[misc] + @overload def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[misc] @overload def gather( # type: ignore[misc] @@ -166,7 +180,27 @@ if sys.version_info >= (3, 10): tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather(*coros_or_futures: _FutureLike[Any], return_exceptions: bool = False) -> Future[list[Any]]: ... + def gather( # type: ignore[misc] + __coro_or_future1: _FutureLike[_T1], + __coro_or_future2: _FutureLike[_T2], + __coro_or_future3: _FutureLike[_T3], + __coro_or_future4: _FutureLike[_T4], + __coro_or_future5: _FutureLike[_T5], + __coro_or_future6: _FutureLike[_T6], + *, + return_exceptions: bool, + ) -> Future[ + tuple[ + _T1 | BaseException, + _T2 | BaseException, + _T3 | BaseException, + _T4 | BaseException, + _T5 | BaseException, + _T6 | BaseException, + ] + ]: ... + @overload + def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: bool) -> Future[list[_T | BaseException]]: ... else: @overload @@ -212,6 +246,22 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload + def gather( # type: ignore[misc] + __coro_or_future1: _FutureLike[_T1], + __coro_or_future2: _FutureLike[_T2], + __coro_or_future3: _FutureLike[_T3], + __coro_or_future4: _FutureLike[_T4], + __coro_or_future5: _FutureLike[_T5], + __coro_or_future6: _FutureLike[_T6], + *, + loop: AbstractEventLoop | None = None, + return_exceptions: Literal[False] = False, + ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... + @overload + def gather( # type: ignore[misc] + *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False + ) -> Future[list[_T]]: ... + @overload def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException]]: ... @@ -249,16 +299,24 @@ else: __coro_or_future3: _FutureLike[_T3], __coro_or_future4: _FutureLike[_T4], __coro_or_future5: _FutureLike[_T5], + __coro_or_future6: _FutureLike[_T6], *, loop: AbstractEventLoop | None = None, return_exceptions: bool, ) -> Future[ - tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] + tuple[ + _T1 | BaseException, + _T2 | BaseException, + _T3 | BaseException, + _T4 | BaseException, + _T5 | BaseException, + _T6 | BaseException, + ] ]: ... @overload - def gather( - *coros_or_futures: _FutureLike[Any], loop: AbstractEventLoop | None = None, return_exceptions: bool = False - ) -> Future[list[Any]]: ... + def gather( # type: ignore[misc] + *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: bool + ) -> Future[list[_T | BaseException]]: ... def run_coroutine_threadsafe(coro: _FutureLike[_T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 2942a25c0ac4..8e643dd4a3f2 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -1,6 +1,6 @@ import socket import sys -from _typeshed import Incomplete, WriteableBuffer +from _typeshed import Incomplete, ReadableBuffer, WriteableBuffer from collections.abc import Callable from typing import IO, Any, ClassVar, NoReturn from typing_extensions import Literal @@ -48,6 +48,12 @@ if sys.platform == "win32": def select(self, timeout: int | None = None) -> list[futures.Future[Any]]: ... def recv(self, conn: socket.socket, nbytes: int, flags: int = 0) -> futures.Future[bytes]: ... def recv_into(self, conn: socket.socket, buf: WriteableBuffer, flags: int = 0) -> futures.Future[Any]: ... + def recvfrom( + self, conn: socket.socket, nbytes: int, flags: int = 0 + ) -> futures.Future[tuple[bytes, socket._RetAddress]]: ... + def sendto( + self, conn: socket.socket, buf: ReadableBuffer, flags: int = 0, addr: socket._Address | None = None + ) -> futures.Future[int]: ... def send(self, conn: socket.socket, buf: WriteableBuffer, flags: int = 0) -> futures.Future[Any]: ... def accept(self, listener: socket.socket) -> futures.Future[Any]: ... def connect( @@ -60,6 +66,10 @@ if sys.platform == "win32": async def connect_pipe(self, address: str) -> windows_utils.PipeHandle: ... def wait_for_handle(self, handle: windows_utils.PipeHandle, timeout: int | None = None) -> bool: ... def close(self) -> None: ... + if sys.version_info >= (3, 11): + def recvfrom_into( + self, conn: socket.socket, buf: WriteableBuffer, flags: int = 0 + ) -> futures.Future[tuple[int, socket._RetAddress]]: ... SelectorEventLoop = _WindowsSelectorEventLoop class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index 3753700ea889..2e95c659dbcd 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -1,6 +1,95 @@ import sys -from _typeshed import StrPath -from collections.abc import Callable, Iterable, Mapping +from _locale import ( + CHAR_MAX as CHAR_MAX, + LC_ALL as LC_ALL, + LC_COLLATE as LC_COLLATE, + LC_CTYPE as LC_CTYPE, + LC_MONETARY as LC_MONETARY, + LC_NUMERIC as LC_NUMERIC, + LC_TIME as LC_TIME, + localeconv as localeconv, + setlocale as setlocale, + strcoll as strcoll, + strxfrm as strxfrm, +) + +# This module defines a function "str()", which is why "str" can't be used +# as a type annotation or type alias. +from builtins import str as _str +from collections.abc import Callable +from decimal import Decimal +from typing import Any + +if sys.version_info >= (3, 11): + from _locale import getencoding as getencoding + +# Some parts of the `_locale` module are platform-specific: +if sys.platform != "win32": + from _locale import ( + ABDAY_1 as ABDAY_1, + ABDAY_2 as ABDAY_2, + ABDAY_3 as ABDAY_3, + ABDAY_4 as ABDAY_4, + ABDAY_5 as ABDAY_5, + ABDAY_6 as ABDAY_6, + ABDAY_7 as ABDAY_7, + ABMON_1 as ABMON_1, + ABMON_2 as ABMON_2, + ABMON_3 as ABMON_3, + ABMON_4 as ABMON_4, + ABMON_5 as ABMON_5, + ABMON_6 as ABMON_6, + ABMON_7 as ABMON_7, + ABMON_8 as ABMON_8, + ABMON_9 as ABMON_9, + ABMON_10 as ABMON_10, + ABMON_11 as ABMON_11, + ABMON_12 as ABMON_12, + ALT_DIGITS as ALT_DIGITS, + AM_STR as AM_STR, + CODESET as CODESET, + CRNCYSTR as CRNCYSTR, + D_FMT as D_FMT, + D_T_FMT as D_T_FMT, + DAY_1 as DAY_1, + DAY_2 as DAY_2, + DAY_3 as DAY_3, + DAY_4 as DAY_4, + DAY_5 as DAY_5, + DAY_6 as DAY_6, + DAY_7 as DAY_7, + ERA as ERA, + ERA_D_FMT as ERA_D_FMT, + ERA_D_T_FMT as ERA_D_T_FMT, + ERA_T_FMT as ERA_T_FMT, + LC_MESSAGES as LC_MESSAGES, + MON_1 as MON_1, + MON_2 as MON_2, + MON_3 as MON_3, + MON_4 as MON_4, + MON_5 as MON_5, + MON_6 as MON_6, + MON_7 as MON_7, + MON_8 as MON_8, + MON_9 as MON_9, + MON_10 as MON_10, + MON_11 as MON_11, + MON_12 as MON_12, + NOEXPR as NOEXPR, + PM_STR as PM_STR, + RADIXCHAR as RADIXCHAR, + T_FMT as T_FMT, + T_FMT_AMPM as T_FMT_AMPM, + THOUSEP as THOUSEP, + YESEXPR as YESEXPR, + bind_textdomain_codeset as bind_textdomain_codeset, + bindtextdomain as bindtextdomain, + dcgettext as dcgettext, + dgettext as dgettext, + gettext as gettext, + nl_langinfo as nl_langinfo, + textdomain as textdomain, + ) __all__ = [ "getlocale", @@ -20,7 +109,6 @@ __all__ = [ "normalize", "LC_CTYPE", "LC_COLLATE", - "LC_MESSAGES", "LC_TIME", "LC_MONETARY", "LC_NUMERIC", @@ -34,88 +122,11 @@ if sys.version_info >= (3, 11): if sys.version_info < (3, 12): __all__ += ["format"] -# This module defines a function "str()", which is why "str" can't be used -# as a type annotation or type alias. -from builtins import str as _str -from decimal import Decimal -from typing import Any - -CODESET: int -D_T_FMT: int -D_FMT: int -T_FMT: int -T_FMT_AMPM: int -AM_STR: int -PM_STR: int - -DAY_1: int -DAY_2: int -DAY_3: int -DAY_4: int -DAY_5: int -DAY_6: int -DAY_7: int -ABDAY_1: int -ABDAY_2: int -ABDAY_3: int -ABDAY_4: int -ABDAY_5: int -ABDAY_6: int -ABDAY_7: int - -MON_1: int -MON_2: int -MON_3: int -MON_4: int -MON_5: int -MON_6: int -MON_7: int -MON_8: int -MON_9: int -MON_10: int -MON_11: int -MON_12: int -ABMON_1: int -ABMON_2: int -ABMON_3: int -ABMON_4: int -ABMON_5: int -ABMON_6: int -ABMON_7: int -ABMON_8: int -ABMON_9: int -ABMON_10: int -ABMON_11: int -ABMON_12: int - -RADIXCHAR: int -THOUSEP: int -YESEXPR: int -NOEXPR: int -CRNCYSTR: int - -ERA: int -ERA_D_T_FMT: int -ERA_D_FMT: int -ERA_T_FMT: int - -ALT_DIGITS: int - -LC_CTYPE: int -LC_COLLATE: int -LC_TIME: int -LC_MONETARY: int -LC_MESSAGES: int -LC_NUMERIC: int -LC_ALL: int - -CHAR_MAX: int +if sys.platform != "win32": + __all__ += ["LC_MESSAGES"] class Error(Exception): ... -def setlocale(category: int, locale: _str | Iterable[_str | None] | None = None) -> _str: ... -def localeconv() -> Mapping[_str, int | _str | list[int]]: ... -def nl_langinfo(__key: int) -> _str: ... def getdefaultlocale( envvars: tuple[_str, ...] = ("LC_ALL", "LC_CTYPE", "LANG", "LANGUAGE") ) -> tuple[_str | None, _str | None]: ... @@ -123,8 +134,6 @@ def getlocale(category: int = ...) -> tuple[_str | None, _str | None]: ... def getpreferredencoding(do_setlocale: bool = True) -> _str: ... def normalize(localename: _str) -> _str: ... def resetlocale(category: int = ...) -> None: ... -def strcoll(__os1: _str, __os2: _str) -> int: ... -def strxfrm(__string: _str) -> _str: ... if sys.version_info < (3, 12): def format( @@ -138,20 +147,6 @@ def atof(string: _str, func: Callable[[_str], float] = ...) -> float: ... def atoi(string: _str) -> int: ... def str(val: float) -> _str: ... -# native gettext functions -# https://docs.python.org/3/library/locale.html#access-to-message-catalogs -# https://github.com/python/cpython/blob/f4c03484da59049eb62a9bf7777b963e2267d187/Modules/_localemodule.c#L626 -if sys.platform == "linux" or sys.platform == "darwin": - def gettext(__msg: _str) -> _str: ... - def dgettext(__domain: _str | None, __msg: _str) -> _str: ... - def dcgettext(__domain: _str | None, __msg: _str, __category: int) -> _str: ... - def textdomain(__domain: _str | None) -> _str: ... - def bindtextdomain(__domain: _str, __dir: StrPath | None) -> _str: ... - def bind_textdomain_codeset(__domain: _str, __codeset: _str | None) -> _str | None: ... - -if sys.version_info >= (3, 11): - def getencoding() -> _str: ... - locale_alias: dict[_str, _str] # undocumented locale_encoding_alias: dict[_str, _str] # undocumented windows_locale: dict[int, _str] # undocumented diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index 128a05fa5752..532cc5e3ce39 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -53,5 +53,4 @@ class MimeTypes: def guess_all_extensions(self, type: str, strict: bool = True) -> list[str]: ... def read(self, filename: str, strict: bool = True) -> None: ... def readfp(self, fp: IO[str], strict: bool = True) -> None: ... - if sys.platform == "win32": - def read_windows_registry(self, strict: bool = True) -> None: ... + def read_windows_registry(self, strict: bool = True) -> None: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 09319980692f..9a213a8b8cf0 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -28,7 +28,7 @@ if sys.platform != "win32": PROT_READ: int PROT_WRITE: int - PAGESIZE: int +PAGESIZE: int class mmap(Iterable[int], Sized): if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/msilib/text.pyi b/mypy/typeshed/stdlib/msilib/text.pyi index 1353cf8a2392..441c843ca6cf 100644 --- a/mypy/typeshed/stdlib/msilib/text.pyi +++ b/mypy/typeshed/stdlib/msilib/text.pyi @@ -3,5 +3,5 @@ import sys if sys.platform == "win32": ActionText: list[tuple[str, str, str | None]] UIText: list[tuple[str, str | None]] - + dirname: str tables: list[str] diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index 5849b9b00ca0..768edbc18ab3 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -1,8 +1,9 @@ import sys -from typing_extensions import Literal +from typing_extensions import Final, Literal # This module is only available on Windows if sys.platform == "win32": + CRT_ASSEMBLY_VERSION: Final[str] LK_UNLCK: Literal[0] LK_LOCK: Literal[1] LK_NBLCK: Literal[2] @@ -26,3 +27,6 @@ if sys.platform == "win32": def ungetch(__char: bytes | bytearray) -> None: ... def ungetwch(__unicode_char: str) -> None: ... def heapmin() -> None: ... + def SetErrorMode(__mode: int) -> int: ... + if sys.version_info >= (3, 10): + def GetErrorMode() -> int: ... # undocumented diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index fa4c55011eba..7fd04218fd7c 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -70,9 +70,20 @@ if sys.platform != "win32": POSIX_FADV_WILLNEED: int POSIX_FADV_DONTNEED: int - SF_NODISKIO: int - SF_MNOWAIT: int - SF_SYNC: int + if sys.platform != "linux" and sys.platform != "darwin": + # In the os-module docs, these are marked as being available + # on "Unix, not Emscripten, not WASI." + # However, in the source code, a comment indicates they're "FreeBSD constants". + # sys.platform could have one of many values on a FreeBSD Python build, + # so the sys-module docs recommend doing `if sys.platform.startswith('freebsd')` + # to detect FreeBSD builds. Unfortunately that would be too dynamic + # for type checkers, however. + SF_NODISKIO: int + SF_MNOWAIT: int + SF_SYNC: int + + if sys.version_info >= (3, 11): + SF_NOCACHE: int if sys.platform == "linux": XATTR_SIZE_MAX: int @@ -282,6 +293,8 @@ if sys.platform != "win32": EX_PROTOCOL: int EX_NOPERM: int EX_CONFIG: int + +if sys.platform != "win32" and sys.platform != "darwin": EX_NOTFOUND: int P_NOWAIT: int diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index ab6bf2e63be5..81cc93c5aa66 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -14,7 +14,6 @@ if sys.platform != "win32": EX_NOHOST as EX_NOHOST, EX_NOINPUT as EX_NOINPUT, EX_NOPERM as EX_NOPERM, - EX_NOTFOUND as EX_NOTFOUND, EX_NOUSER as EX_NOUSER, EX_OK as EX_OK, EX_OSERR as EX_OSERR, @@ -29,6 +28,7 @@ if sys.platform != "win32": F_TEST as F_TEST, F_TLOCK as F_TLOCK, F_ULOCK as F_ULOCK, + NGROUPS_MAX as NGROUPS_MAX, O_APPEND as O_APPEND, O_ASYNC as O_ASYNC, O_CREAT as O_CREAT, @@ -222,6 +222,9 @@ if sys.platform != "win32": writev as writev, ) + if sys.platform != "darwin": + from os import EX_NOTFOUND as EX_NOTFOUND + if sys.platform == "linux": from os import ( GRND_NONBLOCK as GRND_NONBLOCK, diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index c86d20c352e0..5e2828e42c30 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -15,7 +15,8 @@ if sys.platform != "win32": POLLOUT: int POLLPRI: int POLLRDBAND: int - POLLRDHUP: int + if sys.platform == "linux": + POLLRDHUP: int POLLRDNORM: int POLLWRBAND: int POLLWRNORM: int @@ -136,7 +137,6 @@ if sys.platform == "linux": EPOLLRDNORM: int EPOLLWRBAND: int EPOLLWRNORM: int - EPOLL_RDHUP: int EPOLL_CLOEXEC: int if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index 90a923f09355..043df9253316 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -59,15 +59,21 @@ class DevpollSelector(BaseSelector): def select(self, timeout: float | None = ...) -> list[tuple[SelectorKey, _EventMask]]: ... def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... -class KqueueSelector(BaseSelector): - def fileno(self) -> int: ... - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... - def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... +if sys.platform != "win32": + class KqueueSelector(BaseSelector): + def fileno(self) -> int: ... + def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... + def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... + def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... + def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... +# Not a real class at runtime, it is just a conditional alias to other real selectors. +# The runtime logic is more fine-grained than a `sys.platform` check; +# not really expressible in the stubs class DefaultSelector(BaseSelector): def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... + if sys.platform != "win32": + def fileno(self) -> int: ... diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 72c78f1b69f5..906a6dabe192 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -10,10 +10,8 @@ NSIG: int class Signals(IntEnum): SIGABRT: int - SIGEMT: int SIGFPE: int SIGILL: int - SIGINFO: int SIGINT: int SIGSEGV: int SIGTERM: int @@ -47,6 +45,9 @@ class Signals(IntEnum): SIGWINCH: int SIGXCPU: int SIGXFSZ: int + if sys.platform != "linux": + SIGEMT: int + SIGINFO: int if sys.platform != "darwin": SIGCLD: int SIGPOLL: int @@ -77,10 +78,8 @@ else: def signal(__signalnum: _SIGNUM, __handler: _HANDLER) -> _HANDLER: ... SIGABRT: Signals -SIGEMT: Signals SIGFPE: Signals SIGILL: Signals -SIGINFO: Signals SIGINT: Signals SIGSEGV: Signals SIGTERM: Signals @@ -90,6 +89,9 @@ if sys.platform == "win32": CTRL_C_EVENT: Signals CTRL_BREAK_EVENT: Signals else: + if sys.platform != "linux": + SIGINFO: Signals + SIGEMT: Signals SIGALRM: Signals SIGBUS: Signals SIGCHLD: Signals diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index da06ce2c2b06..cc0cbe3709af 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -129,7 +129,9 @@ if sys.platform != "darwin" or sys.version_info >= (3, 9): IPV6_RTHDR as IPV6_RTHDR, ) -if sys.platform != "darwin": +if sys.platform == "darwin": + from _socket import PF_SYSTEM as PF_SYSTEM, SYSPROTO_CONTROL as SYSPROTO_CONTROL +else: from _socket import SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index faf667afb475..d7f256d031ac 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -4,7 +4,7 @@ import sys from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Callable, Iterable from typing import Any, NamedTuple, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Never, Self, TypeAlias, TypedDict, final _PCTRTT: TypeAlias = tuple[tuple[str, str], ...] _PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] @@ -367,6 +367,10 @@ class SSLSocket(socket.socket): def pending(self) -> int: ... if sys.version_info >= (3, 8): def verify_client_post_handshake(self) -> None: ... + # These methods always raise `NotImplementedError`: + def recvmsg(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] + def recvmsg_into(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] + def sendmsg(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] class TLSVersion(enum.IntEnum): MINIMUM_SUPPORTED: int diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 346e4d5513d8..1013db7ee984 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2600,6 +2600,7 @@ if sys.platform == "win32": hStdError: Any | None wShowWindow: int lpAttributeList: Mapping[str, Any] + def copy(self) -> STARTUPINFO: ... from _winapi import ( ABOVE_NORMAL_PRIORITY_CLASS as ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS as BELOW_NORMAL_PRIORITY_CLASS, diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index 337bd9706050..613b239ff663 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -99,3 +99,5 @@ if sys.platform == "win32": def Close(self) -> None: ... def Detach(self) -> int: ... def __hash__(self) -> int: ... + @property + def handle(self) -> int: ... diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi index 9b2b57a38986..aa04fdc27a01 100644 --- a/mypy/typeshed/stdlib/winsound.pyi +++ b/mypy/typeshed/stdlib/winsound.pyi @@ -4,6 +4,7 @@ from typing import overload from typing_extensions import Literal if sys.platform == "win32": + SND_APPLICATION: Literal[128] SND_FILENAME: Literal[131072] SND_ALIAS: Literal[65536] SND_LOOP: Literal[8] diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile.pyi index dc07eb3f2a38..b7144f3ab528 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile.pyi @@ -2,9 +2,10 @@ import io import sys from _typeshed import SizedBuffer, StrOrBytesPath, StrPath from collections.abc import Callable, Iterable, Iterator +from io import TextIOWrapper from os import PathLike from types import TracebackType -from typing import IO, Any, Protocol, overload +from typing import IO, Protocol, overload from typing_extensions import Literal, Self, TypeAlias __all__ = [ @@ -223,11 +224,18 @@ class ZipInfo: def FileHeader(self, zip64: bool | None = None) -> bytes: ... if sys.version_info >= (3, 8): - if sys.version_info < (3, 9): - class _PathOpenProtocol(Protocol): - def __call__(self, mode: _ReadWriteMode = "r", pwd: bytes | None = ..., *, force_zip64: bool = ...) -> IO[bytes]: ... + class CompleteDirs(ZipFile): + def resolve_dir(self, name: str) -> str: ... + @overload + @classmethod + def make(cls, source: ZipFile) -> CompleteDirs: ... + @overload + @classmethod + def make(cls: type[Self], source: StrPath | IO[bytes]) -> Self: ... class Path: + root: CompleteDirs + def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... @property def name(self) -> str: ... @property @@ -243,19 +251,25 @@ if sys.version_info >= (3, 8): @property def stem(self) -> str: ... - def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... if sys.version_info >= (3, 9): + @overload def open( self, - mode: _ReadWriteBinaryMode = "r", + mode: Literal["r", "w"] = "r", encoding: str | None = None, - *args: Any, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = ..., + write_through: bool = ..., + *, pwd: bytes | None = None, - **kwargs: Any, - ) -> IO[bytes]: ... + ) -> TextIOWrapper: ... + @overload + def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... else: - @property - def open(self) -> _PathOpenProtocol: ... + def open( + self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False + ) -> IO[bytes]: ... if sys.version_info >= (3, 10): def iterdir(self) -> Iterator[Self]: ... From 940fceb59a55513913c3c9c1eaf89a7f5ee774d6 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 15 Oct 2023 12:12:44 -0700 Subject: [PATCH 0314/1617] [mypyc] Fix direct __dict__ access on inner functions in new Python (#16084) Fixes #16077 --- mypyc/codegen/emitclass.py | 5 ++++- mypyc/test-data/run-functions.test | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 62e1b4b2dea1..8dcf7212b694 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -217,7 +217,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: fields["tp_name"] = f'"{name}"' generate_full = not cl.is_trait and not cl.builtin_base - needs_getseters = cl.needs_getseters or not cl.is_generated + needs_getseters = cl.needs_getseters or not cl.is_generated or cl.has_dict if not cl.builtin_base: fields["tp_new"] = new_name @@ -886,6 +886,9 @@ def generate_getseters_table(cl: ClassIR, name: str, emitter: Emitter) -> None: else: emitter.emit_line("NULL, NULL, NULL},") + if cl.has_dict: + emitter.emit_line('{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},') + emitter.emit_line("{NULL} /* Sentinel */") emitter.emit_line("};") diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index 21993891c4e3..bd8f1a9197dd 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -1256,3 +1256,33 @@ def foo(**kwargs: Unpack[Person]) -> None: foo(name='Jennifer', age=38) [out] Jennifer + +[case testNestedFunctionDunderDict312] +import sys + +def foo() -> None: + def inner() -> str: return "bar" + print(inner.__dict__) # type: ignore[attr-defined] + inner.__dict__.update({"x": 1}) # type: ignore[attr-defined] + print(inner.__dict__) # type: ignore[attr-defined] + print(inner.x) # type: ignore[attr-defined] + +if sys.version_info >= (3, 12): # type: ignore + foo() +[out] +[out version>=3.12] +{} +{'x': 1} +1 + +[case testFunctoolsUpdateWrapper] +import functools + +def bar() -> None: + def inner() -> str: return "bar" + functools.update_wrapper(inner, bar) # type: ignore + print(inner.__dict__) # type: ignore + +bar() +[out] +{'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': } From ff9deb3001d9c7cc84a1e2fed9125bf456b1d68b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 15 Oct 2023 21:44:02 +0100 Subject: [PATCH 0315/1617] Correctly handle runtime type applications of variadic types (#16240) This adds some missing pieces to runtime type application handling for both `TypeVarTuple` and `ParamSpec`. Everything is straightforward (maybe a bit hacky, but we already import `typeanal` in `checkexpr` for similar purposes, e.g. type aliases in runtime context). Fixes https://github.com/python/mypy/issues/14799 --- mypy/checkexpr.py | 34 ++++++++++++++++--- .../unit/check-parameter-specification.test | 13 +++++++ test-data/unit/check-typevar-tuple.test | 20 +++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fd155ff87379..a1dd6d830758 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -28,7 +28,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.meet import is_overlapping_types, narrow_declared_type from mypy.message_registry import ErrorMessage -from mypy.messages import MessageBuilder +from mypy.messages import MessageBuilder, format_type from mypy.nodes import ( ARG_NAMED, ARG_POS, @@ -116,10 +116,12 @@ from mypy.type_visitor import TypeTranslator from mypy.typeanal import ( check_for_explicit_any, + fix_instance, has_any_from_unimported_type, instantiate_type_alias, make_optional_type, set_any_tvars, + validate_instance, ) from mypy.typeops import ( callable_type, @@ -166,10 +168,12 @@ TypeVarLikeType, TypeVarTupleType, TypeVarType, + UnboundType, UninhabitedType, UnionType, UnpackType, find_unpack_in_list, + flatten_nested_tuples, flatten_nested_unions, get_proper_type, get_proper_types, @@ -4637,15 +4641,35 @@ class C(Generic[T, Unpack[Ts]]): ... similar to how it is done in other places using split_with_prefix_and_suffix(). """ vars = t.variables + args = flatten_nested_tuples(args) + + # TODO: this logic is duplicated with semanal_typeargs. + for tv, arg in zip(t.variables, args): + if isinstance(tv, ParamSpecType): + if not isinstance( + get_proper_type(arg), (Parameters, ParamSpecType, AnyType, UnboundType) + ): + self.chk.fail( + "Can only replace ParamSpec with a parameter types list or" + f" another ParamSpec, got {format_type(arg, self.chk.options)}", + ctx, + ) + return [AnyType(TypeOfAny.from_error)] * len(vars) + if not vars or not any(isinstance(v, TypeVarTupleType) for v in vars): return list(args) + assert t.is_type_obj() + info = t.type_object() + # We reuse the logic from semanal phase to reduce code duplication. + fake = Instance(info, args, line=ctx.line, column=ctx.column) + if not validate_instance(fake, self.chk.fail): + fix_instance( + fake, self.chk.fail, self.chk.note, disallow_any=False, options=self.chk.options + ) + args = list(fake.args) prefix = next(i for (i, v) in enumerate(vars) if isinstance(v, TypeVarTupleType)) suffix = len(vars) - prefix - 1 - if len(args) < len(vars) - 1: - self.msg.incompatible_type_application(len(vars), len(args), ctx) - return [AnyType(TypeOfAny.from_error)] * len(vars) - tvt = vars[prefix] assert isinstance(tvt, TypeVarTupleType) start, middle, end = split_with_prefix_and_suffix(tuple(args), prefix, suffix) diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 5b6024da687e..48fadbc96c90 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1977,6 +1977,19 @@ g(cb, y='a', x=0) # E: Argument "y" to "g" has incompatible type "str"; expecte # E: Argument "x" to "g" has incompatible type "int"; expected "str" [builtins fixtures/paramspec.pyi] +[case testParamSpecBadRuntimeTypeApplication] +from typing import ParamSpec, TypeVar, Generic, Callable + +R = TypeVar("R") +P = ParamSpec("P") +class C(Generic[P, R]): + x: Callable[P, R] + +bad = C[int, str]() # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" +reveal_type(bad) # N: Revealed type is "__main__.C[Any, Any]" +reveal_type(bad.x) # N: Revealed type is "def (*Any, **Any) -> Any" +[builtins fixtures/paramspec.pyi] + [case testParamSpecNoCrashOnUnificationAlias] import mod [file mod.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 0212518bdec0..22a30432d098 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1845,3 +1845,23 @@ def foo2(func: Callable[[Unpack[Args]], T], *args: Unpack[Args2]) -> T: def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: return submit(func, 1, *args) [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleRuntimeTypeApplication] +from typing import Generic, TypeVar, Tuple +from typing_extensions import Unpack, TypeVarTuple + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +class C(Generic[T, Unpack[Ts], S]): ... + +Ints = Tuple[int, int] +x = C[Unpack[Ints]]() +reveal_type(x) # N: Revealed type is "__main__.C[builtins.int, builtins.int]" + +y = C[Unpack[Tuple[int, ...]]]() +reveal_type(y) # N: Revealed type is "__main__.C[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" + +z = C[int]() # E: Bad number of arguments, expected: at least 2, given: 1 +reveal_type(z) # N: Revealed type is "__main__.C[Any, Unpack[builtins.tuple[Any, ...]], Any]" +[builtins fixtures/tuple.pyi] From e4355948d797600c7b76da0a916fc5f29d10448e Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Sun, 15 Oct 2023 15:35:20 -0700 Subject: [PATCH 0316/1617] stubgen: unify C extension and pure python stub generators with object oriented design (#15770) This MR is a major overhaul to `stubgen`. It has been tested extensively in the process of creating stubs for multiple large and varied libraries (detailed below). ## User story The impetus of this change is as follows: as a maintainer of third-party stubs I do _not_ want to use `stubgen` as a starting point for hand-editing stub files, I want a framework to regenerate stubs against upstream changes to a library. ## Summary of Changes - Introduces an object-oriented design for C extension stub generation, including a common base class that is shared between inspection-based and parsing-based stub generation. - Generally unifies and harmonizes the behavior between inspection and parsing approaches. For example, function formatting, import tracking, signature generators, and attribute filtering are now handled with the same code. - Adds support for `--include-private` and `--export-less` to c-extensions (inspection-based generation). - Adds support for force enabling inspection-based stub generation (the approach used for C extensions) on pure python code using a new `--inspect-mode` flag. Useful for packages that employ dynamic function or class factories. Also makes it possible to generate stubs for pyc-only modules (yes, this is a real use case) - Adds an alias `--no-analysis` for `--parse-only` to clarify the purpose of this option. - Removes filtering of `__version__` attribute from modules: I've encountered a number of cases in real-world code that utilize this attribute. - Adds a number of tests for inspection mode. Even though these run on pure python code they increase coverage of the C extension code since it shares much of hte same code base. Below I've compiled some basic information about each stub library that I've created using my changes, and a link to the specialized code for procedurally generating the stubs. | Library | code type | other notes | | --- | --- | --- | | [USD](https://github.com/LumaPictures/cg-stubs/blob/master/usd/stubgen_usd.py) | boost-python | integrates types from doxygen | | [katana](https://github.com/LumaPictures/cg-stubs/blob/master/katana/stubgen_katana.py) | pyc and C extensions | uses epydoc docstrings. has pyi-only packages | | [mari](https://github.com/LumaPictures/cg-stubs/blob/master/mari/stubgen_mari.py) | pure python and C extensions | uses epydoc docstrings | | [opencolorio](https://github.com/LumaPictures/cg-stubs/blob/master/ocio/stubgen_ocio.py) | pybind11 | | | [pyside2](https://github.com/LumaPictures/cg-stubs/blob/master/pyside/stubgen_pyside.py) | shiboken | | | substance_painter | pure python | basic / non-custom. reads types from annotations | | pymel | pure python | integrates types parsed from custom docs | I know that this is a pretty big PR, and I know it's a lot to go through, but I've spent a huge amount of time on it and I believe this makes mypy's stubgen tool the absolute best available. If it helps, I also have 13 merged mypy PRs under my belt and I'll be around to fix any issues if they come up. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jelle Zijlstra --- docs/source/stubgen.rst | 14 +- mypy/moduleinspect.py | 4 + mypy/stubdoc.py | 100 +- mypy/stubgen.py | 814 ++++------- mypy/stubgenc.py | 1267 ++++++++++------- mypy/stubutil.py | 604 +++++++- mypy/test/teststubgen.py | 455 ++---- mypy/traverser.py | 3 +- setup.py | 1 - .../stubgen/pybind11_mypy_demo/__init__.pyi | 1 + .../stubgen/pybind11_mypy_demo/basics.pyi | 8 +- test-data/unit/stubgen.test | 296 +++- 12 files changed, 2125 insertions(+), 1442 deletions(-) diff --git a/docs/source/stubgen.rst b/docs/source/stubgen.rst index 2de0743572e7..c9e52956379a 100644 --- a/docs/source/stubgen.rst +++ b/docs/source/stubgen.rst @@ -127,12 +127,22 @@ alter the default behavior: unwanted side effects, such as the running of tests. Stubgen tries to skip test modules even without this option, but this does not always work. -.. option:: --parse-only +.. option:: --no-analysis Don't perform semantic analysis of source files. This may generate worse stubs -- in particular, some module, class, and function aliases may be represented as variables with the ``Any`` type. This is generally only - useful if semantic analysis causes a critical mypy error. + useful if semantic analysis causes a critical mypy error. Does not apply to + C extension modules. Incompatible with :option:`--inspect-mode`. + +.. option:: --inspect-mode + + Import and inspect modules instead of parsing source code. This is the default + behavior for C modules and pyc-only packages. The flag is useful to force + inspection for pure Python modules that make use of dynamically generated + members that would otherwise be omitted when using the default behavior of + code parsing. Implies :option:`--no-analysis` as analysis requires source + code. .. option:: --doc-dir PATH diff --git a/mypy/moduleinspect.py b/mypy/moduleinspect.py index b383fc9dc145..580b31fb4107 100644 --- a/mypy/moduleinspect.py +++ b/mypy/moduleinspect.py @@ -39,6 +39,10 @@ def is_c_module(module: ModuleType) -> bool: return os.path.splitext(module.__dict__["__file__"])[-1] in [".so", ".pyd", ".dll"] +def is_pyc_only(file: str | None) -> bool: + return bool(file and file.endswith(".pyc") and not os.path.exists(file[:-1])) + + class InspectError(Exception): pass diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 145f57fd7751..c277573f0b59 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -8,11 +8,14 @@ import contextlib import io +import keyword import re import tokenize from typing import Any, Final, MutableMapping, MutableSequence, NamedTuple, Sequence, Tuple from typing_extensions import TypeAlias as _TypeAlias +import mypy.util + # Type alias for signatures strings in format ('func_name', '(arg, opt_arg=False)'). Sig: _TypeAlias = Tuple[str, str] @@ -35,12 +38,16 @@ class ArgSig: def __init__(self, name: str, type: str | None = None, default: bool = False): self.name = name - if type and not is_valid_type(type): - raise ValueError("Invalid type: " + type) self.type = type # Does this argument have a default value? self.default = default + def is_star_arg(self) -> bool: + return self.name.startswith("*") and not self.name.startswith("**") + + def is_star_kwarg(self) -> bool: + return self.name.startswith("**") + def __repr__(self) -> str: return "ArgSig(name={}, type={}, default={})".format( repr(self.name), repr(self.type), repr(self.default) @@ -59,7 +66,80 @@ def __eq__(self, other: Any) -> bool: class FunctionSig(NamedTuple): name: str args: list[ArgSig] - ret_type: str + ret_type: str | None + + def is_special_method(self) -> bool: + return bool( + self.name.startswith("__") + and self.name.endswith("__") + and self.args + and self.args[0].name in ("self", "cls") + ) + + def has_catchall_args(self) -> bool: + """Return if this signature has catchall args: (*args, **kwargs)""" + if self.args and self.args[0].name in ("self", "cls"): + args = self.args[1:] + else: + args = self.args + return ( + len(args) == 2 + and all(a.type in (None, "object", "Any", "typing.Any") for a in args) + and args[0].is_star_arg() + and args[1].is_star_kwarg() + ) + + def is_catchall_signature(self) -> bool: + """Return if this signature is the catchall identity: (*args, **kwargs) -> Any""" + return self.has_catchall_args() and self.ret_type in (None, "Any", "typing.Any") + + def format_sig( + self, + indent: str = "", + is_async: bool = False, + any_val: str | None = None, + docstring: str | None = None, + ) -> str: + args: list[str] = [] + for arg in self.args: + arg_def = arg.name + + if arg_def in keyword.kwlist: + arg_def = "_" + arg_def + + if ( + arg.type is None + and any_val is not None + and arg.name not in ("self", "cls") + and not arg.name.startswith("*") + ): + arg_type: str | None = any_val + else: + arg_type = arg.type + if arg_type: + arg_def += ": " + arg_type + if arg.default: + arg_def += " = ..." + + elif arg.default: + arg_def += "=..." + + args.append(arg_def) + + retfield = "" + ret_type = self.ret_type if self.ret_type else any_val + if ret_type is not None: + retfield = " -> " + ret_type + + prefix = "async " if is_async else "" + sig = "{indent}{prefix}def {name}({args}){ret}:".format( + indent=indent, prefix=prefix, name=self.name, args=", ".join(args), ret=retfield + ) + if docstring: + suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" + else: + suffix = " ..." + return f"{sig}{suffix}" # States of the docstring parser. @@ -176,17 +256,17 @@ def add_token(self, token: tokenize.TokenInfo) -> None: # arg_name is empty when there are no args. e.g. func() if self.arg_name: - try: + if self.arg_type and not is_valid_type(self.arg_type): + # wrong type, use Any + self.args.append( + ArgSig(name=self.arg_name, type=None, default=bool(self.arg_default)) + ) + else: self.args.append( ArgSig( name=self.arg_name, type=self.arg_type, default=bool(self.arg_default) ) ) - except ValueError: - # wrong type, use Any - self.args.append( - ArgSig(name=self.arg_name, type=None, default=bool(self.arg_default)) - ) self.arg_name = "" self.arg_type = None self.arg_default = None @@ -240,7 +320,7 @@ def args_kwargs(signature: FunctionSig) -> bool: def infer_sig_from_docstring(docstr: str | None, name: str) -> list[FunctionSig] | None: - """Convert function signature to list of TypedFunctionSig + """Convert function signature to list of FunctionSig Look for function signatures of function in docstring. Signature is a string of the format () -> or perhaps without diff --git a/mypy/stubgen.py b/mypy/stubgen.py index e8c12ee4d99b..395a49fa4e08 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -7,7 +7,7 @@ - or use mypy's mechanisms, if importing is prohibited * (optionally) semantically analysing the sources using mypy (as a single set) * emitting the stubs text: - - for Python modules: from ASTs using StubGenerator + - for Python modules: from ASTs using ASTStubGenerator - for C modules using runtime introspection and (optionally) Sphinx docs During first and third steps some problematic files can be skipped, but any @@ -42,14 +42,12 @@ from __future__ import annotations import argparse -import glob import keyword import os import os.path import sys import traceback -from collections import defaultdict -from typing import Final, Iterable, Mapping +from typing import Final, Iterable import mypy.build import mypy.mixedtraverser @@ -66,7 +64,7 @@ SearchPaths, default_lib_path, ) -from mypy.moduleinspect import ModuleInspect +from mypy.moduleinspect import ModuleInspect, is_pyc_only from mypy.nodes import ( ARG_NAMED, ARG_POS, @@ -85,6 +83,7 @@ DictExpr, EllipsisExpr, Expression, + ExpressionStmt, FloatExpr, FuncBase, FuncDef, @@ -109,20 +108,19 @@ Var, ) from mypy.options import Options as MypyOptions -from mypy.stubdoc import Sig, find_unique_signatures, parse_all_signatures -from mypy.stubgenc import ( - DocstringSignatureGenerator, - ExternalSignatureGenerator, - FallbackSignatureGenerator, - SignatureGenerator, - generate_stub_for_c_module, -) +from mypy.stubdoc import ArgSig, FunctionSig +from mypy.stubgenc import InspectionStubGenerator, generate_stub_for_c_module from mypy.stubutil import ( + BaseStubGenerator, CantImport, + ClassInfo, + FunctionContext, common_dir_prefix, fail_missing, find_module_path_and_all_py3, generate_guarded, + infer_method_arg_types, + infer_method_ret_type, remove_misplaced_type_comments, report_missing, walk_packages, @@ -140,19 +138,13 @@ AnyType, CallableType, Instance, - NoneType, TupleType, Type, - TypeList, - TypeStrVisitor, UnboundType, - UnionType, get_proper_type, ) from mypy.visitor import NodeVisitor -TYPING_MODULE_NAMES: Final = ("typing", "typing_extensions") - # Common ways of naming package containing vendored modules. VENDOR_PACKAGES: Final = ["packages", "vendor", "vendored", "_vendor", "_vendored_packages"] @@ -165,32 +157,6 @@ "/_vendored_packages/", ] -# Special-cased names that are implicitly exported from the stub (from m import y as y). -EXTRA_EXPORTED: Final = { - "pyasn1_modules.rfc2437.univ", - "pyasn1_modules.rfc2459.char", - "pyasn1_modules.rfc2459.univ", -} - -# These names should be omitted from generated stubs. -IGNORED_DUNDERS: Final = { - "__all__", - "__author__", - "__version__", - "__about__", - "__copyright__", - "__email__", - "__license__", - "__summary__", - "__title__", - "__uri__", - "__str__", - "__repr__", - "__getstate__", - "__setstate__", - "__slots__", -} - # These methods are expected to always return a non-trivial value. METHODS_WITH_RETURN_VALUE: Final = { "__ne__", @@ -203,22 +169,6 @@ "__iter__", } -# These magic methods always return the same type. -KNOWN_MAGIC_METHODS_RETURN_TYPES: Final = { - "__len__": "int", - "__length_hint__": "int", - "__init__": "None", - "__del__": "None", - "__bool__": "bool", - "__bytes__": "bytes", - "__format__": "str", - "__contains__": "bool", - "__complex__": "complex", - "__int__": "int", - "__float__": "float", - "__index__": "int", -} - class Options: """Represents stubgen options. @@ -230,6 +180,7 @@ def __init__( self, pyversion: tuple[int, int], no_import: bool, + inspect: bool, doc_dir: str, search_path: list[str], interpreter: str, @@ -248,6 +199,7 @@ def __init__( # See parse_options for descriptions of the flags. self.pyversion = pyversion self.no_import = no_import + self.inspect = inspect self.doc_dir = doc_dir self.search_path = search_path self.interpreter = interpreter @@ -279,6 +231,9 @@ def __init__( self.runtime_all = runtime_all self.ast: MypyFile | None = None + def __repr__(self) -> str: + return f"StubSource({self.source})" + @property def module(self) -> str: return self.source.module @@ -303,71 +258,13 @@ def path(self) -> str | None: ERROR_MARKER: Final = "" -class AnnotationPrinter(TypeStrVisitor): - """Visitor used to print existing annotations in a file. - - The main difference from TypeStrVisitor is a better treatment of - unbound types. - - Notes: - * This visitor doesn't add imports necessary for annotations, this is done separately - by ImportTracker. - * It can print all kinds of types, but the generated strings may not be valid (notably - callable types) since it prints the same string that reveal_type() does. - * For Instance types it prints the fully qualified names. - """ - - # TODO: Generate valid string representation for callable types. - # TODO: Use short names for Instances. - def __init__(self, stubgen: StubGenerator) -> None: - super().__init__(options=mypy.options.Options()) - self.stubgen = stubgen - - def visit_any(self, t: AnyType) -> str: - s = super().visit_any(t) - self.stubgen.import_tracker.require_name(s) - return s - - def visit_unbound_type(self, t: UnboundType) -> str: - s = t.name - self.stubgen.import_tracker.require_name(s) - if t.args: - s += f"[{self.args_str(t.args)}]" - return s - - def visit_none_type(self, t: NoneType) -> str: - return "None" - - def visit_type_list(self, t: TypeList) -> str: - return f"[{self.list_str(t.items)}]" - - def visit_union_type(self, t: UnionType) -> str: - return " | ".join([item.accept(self) for item in t.items]) - - def args_str(self, args: Iterable[Type]) -> str: - """Convert an array of arguments to strings and join the results with commas. - - The main difference from list_str is the preservation of quotes for string - arguments - """ - types = ["builtins.bytes", "builtins.str"] - res = [] - for arg in args: - arg_str = arg.accept(self) - if isinstance(arg, UnboundType) and arg.original_str_fallback in types: - res.append(f"'{arg_str}'") - else: - res.append(arg_str) - return ", ".join(res) - - class AliasPrinter(NodeVisitor[str]): """Visitor used to collect type aliases _and_ type variable definitions. Visit r.h.s of the definition to get the string representation of type alias. """ - def __init__(self, stubgen: StubGenerator) -> None: + def __init__(self, stubgen: ASTStubGenerator) -> None: self.stubgen = stubgen super().__init__() @@ -435,124 +332,6 @@ def visit_op_expr(self, o: OpExpr) -> str: return f"{o.left.accept(self)} {o.op} {o.right.accept(self)}" -class ImportTracker: - """Record necessary imports during stub generation.""" - - def __init__(self) -> None: - # module_for['foo'] has the module name where 'foo' was imported from, or None if - # 'foo' is a module imported directly; examples - # 'from pkg.m import f as foo' ==> module_for['foo'] == 'pkg.m' - # 'from m import f' ==> module_for['f'] == 'm' - # 'import m' ==> module_for['m'] == None - # 'import pkg.m' ==> module_for['pkg.m'] == None - # ==> module_for['pkg'] == None - self.module_for: dict[str, str | None] = {} - - # direct_imports['foo'] is the module path used when the name 'foo' was added to the - # namespace. - # import foo.bar.baz ==> direct_imports['foo'] == 'foo.bar.baz' - # ==> direct_imports['foo.bar'] == 'foo.bar.baz' - # ==> direct_imports['foo.bar.baz'] == 'foo.bar.baz' - self.direct_imports: dict[str, str] = {} - - # reverse_alias['foo'] is the name that 'foo' had originally when imported with an - # alias; examples - # 'import numpy as np' ==> reverse_alias['np'] == 'numpy' - # 'import foo.bar as bar' ==> reverse_alias['bar'] == 'foo.bar' - # 'from decimal import Decimal as D' ==> reverse_alias['D'] == 'Decimal' - self.reverse_alias: dict[str, str] = {} - - # required_names is the set of names that are actually used in a type annotation - self.required_names: set[str] = set() - - # Names that should be reexported if they come from another module - self.reexports: set[str] = set() - - def add_import_from(self, module: str, names: list[tuple[str, str | None]]) -> None: - for name, alias in names: - if alias: - # 'from {module} import {name} as {alias}' - self.module_for[alias] = module - self.reverse_alias[alias] = name - else: - # 'from {module} import {name}' - self.module_for[name] = module - self.reverse_alias.pop(name, None) - self.direct_imports.pop(alias or name, None) - - def add_import(self, module: str, alias: str | None = None) -> None: - if alias: - # 'import {module} as {alias}' - self.module_for[alias] = None - self.reverse_alias[alias] = module - else: - # 'import {module}' - name = module - # add module and its parent packages - while name: - self.module_for[name] = None - self.direct_imports[name] = module - self.reverse_alias.pop(name, None) - name = name.rpartition(".")[0] - - def require_name(self, name: str) -> None: - while name not in self.direct_imports and "." in name: - name = name.rsplit(".", 1)[0] - self.required_names.add(name) - - def reexport(self, name: str) -> None: - """Mark a given non qualified name as needed in __all__. - - This means that in case it comes from a module, it should be - imported with an alias even is the alias is the same as the name. - """ - self.require_name(name) - self.reexports.add(name) - - def import_lines(self) -> list[str]: - """The list of required import lines (as strings with python code).""" - result = [] - - # To summarize multiple names imported from a same module, we collect those - # in the `module_map` dictionary, mapping a module path to the list of names that should - # be imported from it. the names can also be alias in the form 'original as alias' - module_map: Mapping[str, list[str]] = defaultdict(list) - - for name in sorted( - self.required_names, - key=lambda n: (self.reverse_alias[n], n) if n in self.reverse_alias else (n, ""), - ): - # If we haven't seen this name in an import statement, ignore it - if name not in self.module_for: - continue - - m = self.module_for[name] - if m is not None: - # This name was found in a from ... import ... - # Collect the name in the module_map - if name in self.reverse_alias: - name = f"{self.reverse_alias[name]} as {name}" - elif name in self.reexports: - name = f"{name} as {name}" - module_map[m].append(name) - else: - # This name was found in an import ... - # We can already generate the import line - if name in self.reverse_alias: - source = self.reverse_alias[name] - result.append(f"import {source} as {name}\n") - elif name in self.reexports: - assert "." not in name # Because reexports only has nonqualified names - result.append(f"import {name} as {name}\n") - else: - result.append(f"import {name}\n") - - # Now generate all the from ... import ... lines collected in module_map - for module, names in sorted(module_map.items()): - result.append(f"from {module} import {', '.join(sorted(names))}\n") - return result - - def find_defined_names(file: MypyFile) -> set[str]: finder = DefinitionFinder() file.accept(finder) @@ -583,6 +362,10 @@ def find_referenced_names(file: MypyFile) -> set[str]: return finder.refs +def is_none_expr(expr: Expression) -> bool: + return isinstance(expr, NameExpr) and expr.name == "None" + + class ReferenceFinder(mypy.mixedtraverser.MixedTraverserVisitor): """Find all name references (both local and global).""" @@ -625,74 +408,37 @@ def add_ref(self, fullname: str) -> None: self.refs.add(fullname) -class StubGenerator(mypy.traverser.TraverserVisitor): +class ASTStubGenerator(BaseStubGenerator, mypy.traverser.TraverserVisitor): """Generate stub text from a mypy AST.""" def __init__( self, - _all_: list[str] | None, + _all_: list[str] | None = None, include_private: bool = False, analyzed: bool = False, export_less: bool = False, include_docstrings: bool = False, ) -> None: - # Best known value of __all__. - self._all_ = _all_ - self._output: list[str] = [] + super().__init__(_all_, include_private, export_less, include_docstrings) self._decorators: list[str] = [] - self._import_lines: list[str] = [] - # Current indent level (indent is hardcoded to 4 spaces). - self._indent = "" # Stack of defined variables (per scope). self._vars: list[list[str]] = [[]] # What was generated previously in the stub file. self._state = EMPTY - self._toplevel_names: list[str] = [] - self._include_private = include_private - self._include_docstrings = include_docstrings self._current_class: ClassDef | None = None - self.import_tracker = ImportTracker() # Was the tree semantically analysed before? self.analyzed = analyzed - # Disable implicit exports of package-internal imports? - self.export_less = export_less - # Add imports that could be implicitly generated - self.import_tracker.add_import_from("typing", [("NamedTuple", None)]) - # Names in __all__ are required - for name in _all_ or (): - if name not in IGNORED_DUNDERS: - self.import_tracker.reexport(name) - self.defined_names: set[str] = set() # Short names of methods defined in the body of the current class self.method_names: set[str] = set() self.processing_dataclass = False def visit_mypy_file(self, o: MypyFile) -> None: - self.module = o.fullname # Current module being processed + self.module_name = o.fullname # Current module being processed self.path = o.path - self.defined_names = find_defined_names(o) + self.set_defined_names(find_defined_names(o)) self.referenced_names = find_referenced_names(o) - known_imports = { - "_typeshed": ["Incomplete"], - "typing": ["Any", "TypeVar", "NamedTuple"], - "collections.abc": ["Generator"], - "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], - } - for pkg, imports in known_imports.items(): - for t in imports: - if t not in self.defined_names: - alias = None - else: - alias = "_" + t - self.import_tracker.add_import_from(pkg, [(t, alias)]) super().visit_mypy_file(o) - undefined_names = [name for name in self._all_ or [] if name not in self._toplevel_names] - if undefined_names: - if self._state != EMPTY: - self.add("\n") - self.add("# Names in __all__ with no definition:\n") - for name in sorted(undefined_names): - self.add(f"# {name}\n") + self.check_undefined_names() def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: """@property with setters and getters, @overload chain and some others.""" @@ -714,38 +460,14 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: # skip the overload implementation and clear the decorator we just processed self.clear_decorators() - def visit_func_def(self, o: FuncDef) -> None: - is_dataclass_generated = ( - self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated - ) - if is_dataclass_generated and o.name != "__init__": - # Skip methods generated by the @dataclass decorator (except for __init__) - return - if ( - self.is_private_name(o.name, o.fullname) - or self.is_not_in_all(o.name) - or (self.is_recorded_name(o.name) and not o.is_overload) - ): - self.clear_decorators() - return - if not self._indent and self._state not in (EMPTY, FUNC) and not o.is_awaitable_coroutine: - self.add("\n") - if not self.is_top_level(): - self_inits = find_self_initializers(o) - for init, value in self_inits: - if init in self.method_names: - # Can't have both an attribute and a method/property with the same name. - continue - init_code = self.get_init(init, value) - if init_code: - self.add(init_code) - # dump decorators, just before "def ..." - for s in self._decorators: - self.add(s) - self.clear_decorators() - self.add(f"{self._indent}{'async ' if o.is_coroutine else ''}def {o.name}(") - self.record_name(o.name) - args: list[str] = [] + def get_default_function_sig(self, func_def: FuncDef, ctx: FunctionContext) -> FunctionSig: + args = self._get_func_args(func_def, ctx) + retname = self._get_func_return(func_def, ctx) + return FunctionSig(func_def.name, args, retname) + + def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: + args: list[ArgSig] = [] + for i, arg_ in enumerate(o.arguments): var = arg_.variable kind = arg_.kind @@ -759,87 +481,146 @@ def visit_func_def(self, o: FuncDef) -> None: # name their 0th argument other than self/cls is_self_arg = i == 0 and name == "self" is_cls_arg = i == 0 and name == "cls" - annotation = "" + typename: str | None = None if annotated_type and not is_self_arg and not is_cls_arg: # Luckily, an argument explicitly annotated with "Any" has # type "UnboundType" and will not match. if not isinstance(get_proper_type(annotated_type), AnyType): - annotation = f": {self.print_annotation(annotated_type)}" + typename = self.print_annotation(annotated_type) - if kind.is_named() and not any(arg.startswith("*") for arg in args): - args.append("*") + if kind.is_named() and not any(arg.name.startswith("*") for arg in args): + args.append(ArgSig("*")) if arg_.initializer: - if not annotation: + if not typename: typename = self.get_str_type_of_node(arg_.initializer, True, False) - if typename == "": - annotation = "=..." - else: - annotation = f": {typename} = ..." - else: - annotation += " = ..." - arg = name + annotation elif kind == ARG_STAR: - arg = f"*{name}{annotation}" + name = f"*{name}" elif kind == ARG_STAR2: - arg = f"**{name}{annotation}" - else: - arg = name + annotation - args.append(arg) - if o.name == "__init__" and is_dataclass_generated and "**" in args: - # The dataclass plugin generates invalid nameless "*" and "**" arguments - new_name = "".join(a.split(":", 1)[0] for a in args).replace("*", "") - args[args.index("*")] = f"*{new_name}_" # this name is guaranteed to be unique - args[args.index("**")] = f"**{new_name}__" # same here + name = f"**{name}" + + args.append(ArgSig(name, typename, default=bool(arg_.initializer))) + + if ctx.class_info is not None and all( + arg.type is None and arg.default is False for arg in args + ): + new_args = infer_method_arg_types( + ctx.name, ctx.class_info.self_var, [arg.name for arg in args] + ) + if new_args is not None: + args = new_args - retname = None + is_dataclass_generated = ( + self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated + ) + if o.name == "__init__" and is_dataclass_generated and "**" in [a.name for a in args]: + # The dataclass plugin generates invalid nameless "*" and "**" arguments + new_name = "".join(a.name.strip("*") for a in args) + for arg in args: + if arg.name == "*": + arg.name = f"*{new_name}_" # this name is guaranteed to be unique + elif arg.name == "**": + arg.name = f"**{new_name}__" # same here + return args + + def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: if o.name != "__init__" and isinstance(o.unanalyzed_type, CallableType): if isinstance(get_proper_type(o.unanalyzed_type.ret_type), AnyType): # Luckily, a return type explicitly annotated with "Any" has # type "UnboundType" and will enter the else branch. - retname = None # implicit Any + return None # implicit Any else: - retname = self.print_annotation(o.unanalyzed_type.ret_type) - elif o.abstract_status == IS_ABSTRACT or o.name in METHODS_WITH_RETURN_VALUE: + return self.print_annotation(o.unanalyzed_type.ret_type) + if o.abstract_status == IS_ABSTRACT or o.name in METHODS_WITH_RETURN_VALUE: # Always assume abstract methods return Any unless explicitly annotated. Also # some dunder methods should not have a None return type. - retname = None # implicit Any - elif o.name in KNOWN_MAGIC_METHODS_RETURN_TYPES: - retname = KNOWN_MAGIC_METHODS_RETURN_TYPES[o.name] - elif has_yield_expression(o) or has_yield_from_expression(o): - generator_name = self.add_typing_import("Generator") + return None # implicit Any + retname = infer_method_ret_type(o.name) + if retname is not None: + return retname + if has_yield_expression(o) or has_yield_from_expression(o): + generator_name = self.add_name("collections.abc.Generator") yield_name = "None" send_name = "None" return_name = "None" if has_yield_from_expression(o): - yield_name = send_name = self.add_typing_import("Incomplete") + yield_name = send_name = self.add_name("_typeshed.Incomplete") else: for expr, in_assignment in all_yield_expressions(o): - if expr.expr is not None and not self.is_none_expr(expr.expr): - yield_name = self.add_typing_import("Incomplete") + if expr.expr is not None and not is_none_expr(expr.expr): + yield_name = self.add_name("_typeshed.Incomplete") if in_assignment: - send_name = self.add_typing_import("Incomplete") + send_name = self.add_name("_typeshed.Incomplete") if has_return_statement(o): - return_name = self.add_typing_import("Incomplete") - retname = f"{generator_name}[{yield_name}, {send_name}, {return_name}]" - elif not has_return_statement(o) and o.abstract_status == NOT_ABSTRACT: - retname = "None" - retfield = "" - if retname is not None: - retfield = " -> " + retname + return_name = self.add_name("_typeshed.Incomplete") + return f"{generator_name}[{yield_name}, {send_name}, {return_name}]" + if not has_return_statement(o) and o.abstract_status == NOT_ABSTRACT: + return "None" + return None + + def _get_func_docstring(self, node: FuncDef) -> str | None: + if not node.body.body: + return None + expr = node.body.body[0] + if isinstance(expr, ExpressionStmt) and isinstance(expr.expr, StrExpr): + return expr.expr.value + return None - self.add(", ".join(args)) - self.add(f"){retfield}:") - if self._include_docstrings and o.docstring: - docstring = mypy.util.quote_docstring(o.docstring) - self.add(f"\n{self._indent} {docstring}\n") + def visit_func_def(self, o: FuncDef) -> None: + is_dataclass_generated = ( + self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated + ) + if is_dataclass_generated and o.name != "__init__": + # Skip methods generated by the @dataclass decorator (except for __init__) + return + if ( + self.is_private_name(o.name, o.fullname) + or self.is_not_in_all(o.name) + or (self.is_recorded_name(o.name) and not o.is_overload) + ): + self.clear_decorators() + return + if self.is_top_level() and self._state not in (EMPTY, FUNC): + self.add("\n") + if not self.is_top_level(): + self_inits = find_self_initializers(o) + for init, value in self_inits: + if init in self.method_names: + # Can't have both an attribute and a method/property with the same name. + continue + init_code = self.get_init(init, value) + if init_code: + self.add(init_code) + + if self._current_class is not None: + if len(o.arguments): + self_var = o.arguments[0].variable.name + else: + self_var = "self" + class_info = ClassInfo(self._current_class.name, self_var) else: - self.add(" ...\n") + class_info = None + + ctx = FunctionContext( + module_name=self.module_name, + name=o.name, + docstring=self._get_func_docstring(o), + is_abstract=o.abstract_status != NOT_ABSTRACT, + class_info=class_info, + ) - self._state = FUNC + self.record_name(o.name) - def is_none_expr(self, expr: Expression) -> bool: - return isinstance(expr, NameExpr) and expr.name == "None" + default_sig = self.get_default_function_sig(o, ctx) + sigs = self.get_signatures(default_sig, self.sig_generators, ctx) + + for output in self.format_func_def( + sigs, is_coroutine=o.is_coroutine, decorators=self._decorators, docstring=ctx.docstring + ): + self.add(output + "\n") + + self.clear_decorators() + self._state = FUNC def visit_decorator(self, o: Decorator) -> None: if self.is_private_name(o.func.name, o.func.fullname): @@ -917,13 +698,12 @@ def visit_class_def(self, o: ClassDef) -> None: self._current_class = o self.method_names = find_method_names(o.defs.body) sep: int | None = None - if not self._indent and self._state != EMPTY: + if self.is_top_level() and self._state != EMPTY: sep = len(self._output) self.add("\n") decorators = self.get_class_decorators(o) for d in decorators: self.add(f"{self._indent}@{d}\n") - self.add(f"{self._indent}class {o.name}") self.record_name(o.name) base_types = self.get_base_types(o) if base_types: @@ -936,17 +716,16 @@ def visit_class_def(self, o: ClassDef) -> None: base_types.append("metaclass=abc.ABCMeta") self.import_tracker.add_import("abc") self.import_tracker.require_name("abc") - if base_types: - self.add(f"({', '.join(base_types)})") - self.add(":\n") - self._indent += " " + bases = f"({', '.join(base_types)})" if base_types else "" + self.add(f"{self._indent}class {o.name}{bases}:\n") + self.indent() if self._include_docstrings and o.docstring: docstring = mypy.util.quote_docstring(o.docstring) self.add(f"{self._indent}{docstring}\n") n = len(self._output) self._vars.append([]) super().visit_class_def(o) - self._indent = self._indent[:-4] + self.dedent() self._vars.pop() self._vars[-1].append(o.name) if len(self._output) == n: @@ -987,17 +766,17 @@ def get_base_types(self, cdef: ClassDef) -> list[str]: typename = base.args[0].value if nt_fields is None: # Invalid namedtuple() call, cannot determine fields - base_types.append(self.add_typing_import("Incomplete")) + base_types.append(self.add_name("_typeshed.Incomplete")) continue fields_str = ", ".join(f"({f!r}, {t})" for f, t in nt_fields) - namedtuple_name = self.add_typing_import("NamedTuple") + namedtuple_name = self.add_name("typing.NamedTuple") base_types.append(f"{namedtuple_name}({typename!r}, [{fields_str}])") elif self.is_typed_namedtuple(base): base_types.append(base.accept(p)) else: # At this point, we don't know what the base class is, so we # just use Incomplete as the base class. - base_types.append(self.add_typing_import("Incomplete")) + base_types.append(self.add_name("_typeshed.Incomplete")) for name, value in cdef.keywords.items(): if name == "metaclass": continue # handled separately @@ -1063,7 +842,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: init = self.get_init(item.name, o.rvalue, annotation) if init: found = True - if not sep and not self._indent and self._state not in (EMPTY, VAR): + if not sep and self.is_top_level() and self._state not in (EMPTY, VAR): init = "\n" + init sep = True self.add(init) @@ -1092,10 +871,12 @@ def _get_namedtuple_fields(self, call: CallExpr) -> list[tuple[str, str]] | None field_names.append(field.value) else: return None # Invalid namedtuple fields type - if not field_names: + if field_names: + incomplete = self.add_name("_typeshed.Incomplete") + return [(field_name, incomplete) for field_name in field_names] + else: return [] - incomplete = self.add_typing_import("Incomplete") - return [(field_name, incomplete) for field_name in field_names] + elif self.is_typed_namedtuple(call): fields_arg = call.args[1] if not isinstance(fields_arg, (ListExpr, TupleExpr)): @@ -1125,7 +906,7 @@ def process_namedtuple(self, lvalue: NameExpr, rvalue: CallExpr) -> None: if fields is None: self.annotate_as_incomplete(lvalue) return - bases = self.add_typing_import("NamedTuple") + bases = self.add_name("typing.NamedTuple") # TODO: Add support for generic NamedTuples. Requires `Generic` as base class. class_def = f"{self._indent}class {lvalue.name}({bases}):" if len(fields) == 0: @@ -1175,13 +956,13 @@ def process_typeddict(self, lvalue: NameExpr, rvalue: CallExpr) -> None: total = arg else: items.append((arg_name, arg)) - bases = self.add_typing_import("TypedDict") p = AliasPrinter(self) if any(not key.isidentifier() or keyword.iskeyword(key) for key, _ in items): # Keep the call syntax if there are non-identifier or reserved keyword keys. self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") self._state = VAR else: + bases = self.add_name("typing_extensions.TypedDict") # TODO: Add support for generic TypedDicts. Requires `Generic` as base class. if total is not None: bases += f", total={total.accept(p)}" @@ -1198,7 +979,8 @@ def process_typeddict(self, lvalue: NameExpr, rvalue: CallExpr) -> None: self._state = CLASS def annotate_as_incomplete(self, lvalue: NameExpr) -> None: - self.add(f"{self._indent}{lvalue.name}: {self.add_typing_import('Incomplete')}\n") + incomplete = self.add_name("_typeshed.Incomplete") + self.add(f"{self._indent}{lvalue.name}: {incomplete}\n") self._state = VAR def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: @@ -1280,9 +1062,9 @@ def visit_import_from(self, o: ImportFrom) -> None: exported_names: set[str] = set() import_names = [] module, relative = translate_module_name(o.id, o.relative) - if self.module: + if self.module_name: full_module, ok = mypy.util.correct_relative_import( - self.module, relative, module, self.path.endswith(".__init__.py") + self.module_name, relative, module, self.path.endswith(".__init__.py") ) if not ok: full_module = module @@ -1295,37 +1077,7 @@ def visit_import_from(self, o: ImportFrom) -> None: # Vendored six -- translate into plain 'import six'. self.visit_import(Import([("six", None)])) continue - exported = False - if as_name is None and self.module and (self.module + "." + name) in EXTRA_EXPORTED: - # Special case certain names that should be exported, against our general rules. - exported = True - is_private = self.is_private_name(name, full_module + "." + name) - if ( - as_name is None - and name not in self.referenced_names - and not any(n.startswith(name + ".") for n in self.referenced_names) - and (not self._all_ or name in IGNORED_DUNDERS) - and not is_private - and module not in ("abc", "asyncio") + TYPING_MODULE_NAMES - ): - # An imported name that is never referenced in the module is assumed to be - # exported, unless there is an explicit __all__. Note that we need to special - # case 'abc' since some references are deleted during semantic analysis. - exported = True - top_level = full_module.split(".", 1)[0] - self_top_level = self.module.split(".", 1)[0] - if ( - as_name is None - and not self.export_less - and (not self._all_ or name in IGNORED_DUNDERS) - and self.module - and not is_private - and top_level in (self_top_level, "_" + self_top_level) - ): - # Export imports from the same package, since we can't reliably tell whether they - # are part of the public API. - exported = True - if exported: + if self.should_reexport(name, full_module, as_name is not None): self.import_tracker.reexport(name) as_name = name import_names.append((name, as_name)) @@ -1339,7 +1091,7 @@ def visit_import_from(self, o: ImportFrom) -> None: names = [ name for name, alias in o.names - if name in self._all_ and alias is None and name not in IGNORED_DUNDERS + if name in self._all_ and alias is None and name not in self.IGNORED_DUNDERS ] exported_names.update(names) @@ -1373,7 +1125,7 @@ def get_init( isinstance(annotation, UnboundType) and not annotation.args and annotation.name == "Final" - and self.import_tracker.module_for.get("Final") in TYPING_MODULE_NAMES + and self.import_tracker.module_for.get("Final") in self.TYPING_MODULE_NAMES ): # Final without type argument is invalid in stubs. final_arg = self.get_str_type_of_node(rvalue) @@ -1406,67 +1158,14 @@ def get_assign_initializer(self, rvalue: Expression) -> str: # By default, no initializer is required: return "" - def add(self, string: str) -> None: - """Add text to generated stub.""" - self._output.append(string) - def add_decorator(self, name: str, require_name: bool = False) -> None: if require_name: self.import_tracker.require_name(name) - if not self._indent and self._state not in (EMPTY, FUNC): - self._decorators.append("\n") - self._decorators.append(f"{self._indent}@{name}\n") + self._decorators.append(f"@{name}") def clear_decorators(self) -> None: self._decorators.clear() - def typing_name(self, name: str) -> str: - if name in self.defined_names: - # Avoid name clash between name from typing and a name defined in stub. - return "_" + name - else: - return name - - def add_typing_import(self, name: str) -> str: - """Add a name to be imported for typing, unless it's imported already. - - The import will be internal to the stub. - """ - name = self.typing_name(name) - self.import_tracker.require_name(name) - return name - - def add_import_line(self, line: str) -> None: - """Add a line of text to the import section, unless it's already there.""" - if line not in self._import_lines: - self._import_lines.append(line) - - def output(self) -> str: - """Return the text for the stub.""" - imports = "" - if self._import_lines: - imports += "".join(self._import_lines) - imports += "".join(self.import_tracker.import_lines()) - if imports and self._output: - imports += "\n" - return imports + "".join(self._output) - - def is_not_in_all(self, name: str) -> bool: - if self.is_private_name(name): - return False - if self._all_: - return self.is_top_level() and name not in self._all_ - return False - - def is_private_name(self, name: str, fullname: str | None = None) -> bool: - if self._include_private: - return False - if fullname in EXTRA_EXPORTED: - return False - if name == "_": - return False - return name.startswith("_") and (not name.endswith("__") or name in IGNORED_DUNDERS) - def is_private_member(self, fullname: str) -> bool: parts = fullname.split(".") return any(self.is_private_name(part) for part in parts) @@ -1494,9 +1193,9 @@ def get_str_type_of_node( if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"): return "bool" if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None": - return f"{self.add_typing_import('Incomplete')} | None" + return f"{self.add_name('_typeshed.Incomplete')} | None" if can_be_any: - return self.add_typing_import("Incomplete") + return self.add_name("_typeshed.Incomplete") else: return "" @@ -1534,25 +1233,20 @@ def maybe_unwrap_unary_expr(self, expr: Expression) -> Expression: # This is some other unary expr, we cannot do anything with it (yet?). return expr - def print_annotation(self, t: Type) -> str: - printer = AnnotationPrinter(self) - return t.accept(printer) - - def is_top_level(self) -> bool: - """Are we processing the top level of a file?""" - return self._indent == "" - - def record_name(self, name: str) -> None: - """Mark a name as defined. - - This only does anything if at the top level of a module. - """ - if self.is_top_level(): - self._toplevel_names.append(name) - - def is_recorded_name(self, name: str) -> bool: - """Has this name been recorded previously?""" - return self.is_top_level() and name in self._toplevel_names + def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> bool: + is_private = self.is_private_name(name, full_module + "." + name) + if ( + not name_is_alias + and name not in self.referenced_names + and (not self._all_ or name in self.IGNORED_DUNDERS) + and not is_private + and full_module not in ("abc", "asyncio") + self.TYPING_MODULE_NAMES + ): + # An imported name that is never referenced in the module is assumed to be + # exported, unless there is an explicit __all__. Note that we need to special + # case 'abc' since some references are deleted during semantic analysis. + return True + return super().should_reexport(name, full_module, name_is_alias) def find_method_names(defs: list[Statement]) -> set[str]: @@ -1608,6 +1302,17 @@ def remove_blacklisted_modules(modules: list[StubSource]) -> list[StubSource]: ] +def split_pyc_from_py(modules: list[StubSource]) -> tuple[list[StubSource], list[StubSource]]: + py_modules = [] + pyc_modules = [] + for mod in modules: + if is_pyc_only(mod.path): + pyc_modules.append(mod) + else: + py_modules.append(mod) + return pyc_modules, py_modules + + def is_blacklisted_path(path: str) -> bool: return any(substr in (normalize_path_separators(path) + "\n") for substr in BLACKLIST) @@ -1620,10 +1325,10 @@ def normalize_path_separators(path: str) -> str: def collect_build_targets( options: Options, mypy_opts: MypyOptions -) -> tuple[list[StubSource], list[StubSource]]: +) -> tuple[list[StubSource], list[StubSource], list[StubSource]]: """Collect files for which we need to generate stubs. - Return list of Python modules and C modules. + Return list of py modules, pyc modules, and C modules. """ if options.packages or options.modules: if options.no_import: @@ -1646,8 +1351,8 @@ def collect_build_targets( c_modules = [] py_modules = remove_blacklisted_modules(py_modules) - - return py_modules, c_modules + pyc_mod, py_mod = split_pyc_from_py(py_modules) + return py_mod, pyc_mod, c_modules def find_module_paths_using_imports( @@ -1826,98 +1531,90 @@ def generate_asts_for_modules( mod.runtime_all = res.manager.semantic_analyzer.export_map[mod.module] -def generate_stub_from_ast( +def generate_stub_for_py_module( mod: StubSource, target: str, + *, parse_only: bool = False, + inspect: bool = False, include_private: bool = False, export_less: bool = False, include_docstrings: bool = False, + doc_dir: str = "", + all_modules: list[str], ) -> None: """Use analysed (or just parsed) AST to generate type stub for single file. If directory for target doesn't exist it will created. Existing stub will be overwritten. """ - gen = StubGenerator( - mod.runtime_all, - include_private=include_private, - analyzed=not parse_only, - export_less=export_less, - include_docstrings=include_docstrings, - ) - assert mod.ast is not None, "This function must be used only with analyzed modules" - mod.ast.accept(gen) + if inspect: + ngen = InspectionStubGenerator( + module_name=mod.module, + known_modules=all_modules, + _all_=mod.runtime_all, + doc_dir=doc_dir, + include_private=include_private, + export_less=export_less, + include_docstrings=include_docstrings, + ) + ngen.generate_module() + output = ngen.output() + + else: + gen = ASTStubGenerator( + mod.runtime_all, + include_private=include_private, + analyzed=not parse_only, + export_less=export_less, + include_docstrings=include_docstrings, + ) + assert mod.ast is not None, "This function must be used only with analyzed modules" + mod.ast.accept(gen) + output = gen.output() # Write output to file. subdir = os.path.dirname(target) if subdir and not os.path.isdir(subdir): os.makedirs(subdir) with open(target, "w") as file: - file.write("".join(gen.output())) - - -def get_sig_generators(options: Options) -> list[SignatureGenerator]: - sig_generators: list[SignatureGenerator] = [ - DocstringSignatureGenerator(), - FallbackSignatureGenerator(), - ] - if options.doc_dir: - # Collect info from docs (if given). Always check these first. - sigs, class_sigs = collect_docs_signatures(options.doc_dir) - sig_generators.insert(0, ExternalSignatureGenerator(sigs, class_sigs)) - return sig_generators - - -def collect_docs_signatures(doc_dir: str) -> tuple[dict[str, str], dict[str, str]]: - """Gather all function and class signatures in the docs. - - Return a tuple (function signatures, class signatures). - Currently only used for C modules. - """ - all_sigs: list[Sig] = [] - all_class_sigs: list[Sig] = [] - for path in glob.glob(f"{doc_dir}/*.rst"): - with open(path) as f: - loc_sigs, loc_class_sigs = parse_all_signatures(f.readlines()) - all_sigs += loc_sigs - all_class_sigs += loc_class_sigs - sigs = dict(find_unique_signatures(all_sigs)) - class_sigs = dict(find_unique_signatures(all_class_sigs)) - return sigs, class_sigs + file.write(output) def generate_stubs(options: Options) -> None: """Main entry point for the program.""" mypy_opts = mypy_options(options) - py_modules, c_modules = collect_build_targets(options, mypy_opts) - sig_generators = get_sig_generators(options) + py_modules, pyc_modules, c_modules = collect_build_targets(options, mypy_opts) + all_modules = py_modules + pyc_modules + c_modules + all_module_names = sorted(m.module for m in all_modules) # Use parsed sources to generate stubs for Python modules. generate_asts_for_modules(py_modules, options.parse_only, mypy_opts, options.verbose) files = [] - for mod in py_modules: + for mod in py_modules + pyc_modules: assert mod.path is not None, "Not found module was not skipped" target = mod.module.replace(".", "/") - if os.path.basename(mod.path) == "__init__.py": + if os.path.basename(mod.path) in ["__init__.py", "__init__.pyc"]: target += "/__init__.pyi" else: target += ".pyi" target = os.path.join(options.output_dir, target) files.append(target) with generate_guarded(mod.module, target, options.ignore_errors, options.verbose): - generate_stub_from_ast( + generate_stub_for_py_module( mod, target, - options.parse_only, - options.include_private, - options.export_less, + parse_only=options.parse_only, + inspect=options.inspect or mod in pyc_modules, + include_private=options.include_private, + export_less=options.export_less, include_docstrings=options.include_docstrings, + doc_dir=options.doc_dir, + all_modules=all_module_names, ) # Separately analyse C modules using different logic. - all_modules = sorted(m.module for m in (py_modules + c_modules)) for mod in c_modules: - if any(py_mod.module.startswith(mod.module + ".") for py_mod in py_modules + c_modules): + if any(py_mod.module.startswith(mod.module + ".") for py_mod in all_modules): target = mod.module.replace(".", "/") + "/__init__.pyi" else: target = mod.module.replace(".", "/") + ".pyi" @@ -1927,11 +1624,12 @@ def generate_stubs(options: Options) -> None: generate_stub_for_c_module( mod.module, target, - known_modules=all_modules, - sig_generators=sig_generators, - include_docstrings=options.include_docstrings, + known_modules=all_module_names, + doc_dir=options.doc_dir, + include_private=options.include_private, + export_less=options.export_less, ) - num_modules = len(py_modules) + len(c_modules) + num_modules = len(all_modules) if not options.quiet and num_modules > 0: print("Processed %d modules" % num_modules) if len(files) == 1: @@ -1967,10 +1665,21 @@ def parse_options(args: list[str]) -> Options: "respect __all__)", ) parser.add_argument( + "--no-analysis", "--parse-only", + dest="parse_only", action="store_true", help="don't perform semantic analysis of sources, just parse them " - "(only applies to Python modules, might affect quality of stubs)", + "(only applies to Python modules, might affect quality of stubs. " + "Not compatible with --inspect)", + ) + parser.add_argument( + "--inspect-mode", + dest="inspect", + action="store_true", + help="import and inspect modules instead of parsing source code." + "This is the default behavior for c modules and pyc-only packages, but " + "it is also useful for pure python modules with dynamically generated members.", ) parser.add_argument( "--include-private", @@ -2047,6 +1756,8 @@ def parse_options(args: list[str]) -> Options: parser.error("May only specify one of: modules/packages or files.") if ns.quiet and ns.verbose: parser.error("Cannot specify both quiet and verbose messages") + if ns.inspect and ns.parse_only: + parser.error("Cannot specify both --parse-only/--no-analysis and --inspect-mode") # Create the output folder if it doesn't already exist. if not os.path.exists(ns.output_dir): @@ -2055,6 +1766,7 @@ def parse_options(args: list[str]) -> Options: return Options( pyversion=pyversion, no_import=ns.no_import, + inspect=ns.inspect, doc_dir=ns.doc_dir, search_path=ns.search_path.split(":"), interpreter=ns.interpreter, diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 31487f9d0dcf..0ad79a4265b3 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -6,68 +6,38 @@ from __future__ import annotations +import glob import importlib import inspect +import keyword import os.path -import re -from abc import abstractmethod -from types import ModuleType -from typing import Any, Final, Iterable, Mapping +from types import FunctionType, ModuleType +from typing import Any, Mapping -import mypy.util +from mypy.fastparse import parse_type_comment from mypy.moduleinspect import is_c_module from mypy.stubdoc import ( ArgSig, FunctionSig, + Sig, + find_unique_signatures, infer_arg_sig_from_anon_docstring, infer_prop_type_from_docstring, infer_ret_type_sig_from_anon_docstring, infer_ret_type_sig_from_docstring, infer_sig_from_docstring, + parse_all_signatures, ) - -# Members of the typing module to consider for importing by default. -_DEFAULT_TYPING_IMPORTS: Final = ( - "Any", - "Callable", - "ClassVar", - "Dict", - "Iterable", - "Iterator", - "List", - "Optional", - "Tuple", - "Union", +from mypy.stubutil import ( + BaseStubGenerator, + ClassInfo, + FunctionContext, + SignatureGenerator, + infer_method_arg_types, + infer_method_ret_type, ) -class SignatureGenerator: - """Abstract base class for extracting a list of FunctionSigs for each function.""" - - def remove_self_type( - self, inferred: list[FunctionSig] | None, self_var: str - ) -> list[FunctionSig] | None: - """Remove type annotation from self/cls argument""" - if inferred: - for signature in inferred: - if signature.args: - if signature.args[0].name == self_var: - signature.args[0].type = None - return inferred - - @abstractmethod - def get_function_sig( - self, func: object, module_name: str, name: str - ) -> list[FunctionSig] | None: - pass - - @abstractmethod - def get_method_sig( - self, cls: type, func: object, module_name: str, class_name: str, name: str, self_var: str - ) -> list[FunctionSig] | None: - pass - - class ExternalSignatureGenerator(SignatureGenerator): def __init__( self, func_sigs: dict[str, str] | None = None, class_sigs: dict[str, str] | None = None @@ -79,97 +49,104 @@ class signatures (usually corresponds to __init__). self.func_sigs = func_sigs or {} self.class_sigs = class_sigs or {} - def get_function_sig( - self, func: object, module_name: str, name: str - ) -> list[FunctionSig] | None: - if name in self.func_sigs: - return [ - FunctionSig( - name=name, - args=infer_arg_sig_from_anon_docstring(self.func_sigs[name]), - ret_type="Any", - ) - ] - else: - return None + @classmethod + def from_doc_dir(cls, doc_dir: str) -> ExternalSignatureGenerator: + """Instantiate from a directory of .rst files.""" + all_sigs: list[Sig] = [] + all_class_sigs: list[Sig] = [] + for path in glob.glob(f"{doc_dir}/*.rst"): + with open(path) as f: + loc_sigs, loc_class_sigs = parse_all_signatures(f.readlines()) + all_sigs += loc_sigs + all_class_sigs += loc_class_sigs + sigs = dict(find_unique_signatures(all_sigs)) + class_sigs = dict(find_unique_signatures(all_class_sigs)) + return ExternalSignatureGenerator(sigs, class_sigs) - def get_method_sig( - self, cls: type, func: object, module_name: str, class_name: str, name: str, self_var: str + def get_function_sig( + self, default_sig: FunctionSig, ctx: FunctionContext ) -> list[FunctionSig] | None: + # method: if ( - name in ("__new__", "__init__") - and name not in self.func_sigs - and class_name in self.class_sigs + ctx.class_info + and ctx.name in ("__new__", "__init__") + and ctx.name not in self.func_sigs + and ctx.class_info.name in self.class_sigs ): return [ FunctionSig( - name=name, - args=infer_arg_sig_from_anon_docstring(self.class_sigs[class_name]), - ret_type=infer_method_ret_type(name), + name=ctx.name, + args=infer_arg_sig_from_anon_docstring(self.class_sigs[ctx.class_info.name]), + ret_type=infer_method_ret_type(ctx.name), ) ] - inferred = self.get_function_sig(func, module_name, name) - return self.remove_self_type(inferred, self_var) + + # function: + if ctx.name not in self.func_sigs: + return None + + inferred = [ + FunctionSig( + name=ctx.name, + args=infer_arg_sig_from_anon_docstring(self.func_sigs[ctx.name]), + ret_type=None, + ) + ] + if ctx.class_info: + return self.remove_self_type(inferred, ctx.class_info.self_var) + else: + return inferred + + def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> str | None: + return None class DocstringSignatureGenerator(SignatureGenerator): def get_function_sig( - self, func: object, module_name: str, name: str + self, default_sig: FunctionSig, ctx: FunctionContext ) -> list[FunctionSig] | None: - docstr = getattr(func, "__doc__", None) - inferred = infer_sig_from_docstring(docstr, name) + inferred = infer_sig_from_docstring(ctx.docstring, ctx.name) if inferred: - assert docstr is not None - if is_pybind11_overloaded_function_docstring(docstr, name): + assert ctx.docstring is not None + if is_pybind11_overloaded_function_docstring(ctx.docstring, ctx.name): # Remove pybind11 umbrella (*args, **kwargs) for overloaded functions del inferred[-1] - return inferred - def get_method_sig( - self, - cls: type, - func: object, - module_name: str, - class_name: str, - func_name: str, - self_var: str, - ) -> list[FunctionSig] | None: - inferred = self.get_function_sig(func, module_name, func_name) - if not inferred and func_name == "__init__": - # look for class-level constructor signatures of the form () - inferred = self.get_function_sig(cls, module_name, class_name) - return self.remove_self_type(inferred, self_var) + if ctx.class_info: + if not inferred and ctx.name == "__init__": + # look for class-level constructor signatures of the form () + inferred = infer_sig_from_docstring(ctx.class_info.docstring, ctx.class_info.name) + if inferred: + inferred = [sig._replace(name="__init__") for sig in inferred] + return self.remove_self_type(inferred, ctx.class_info.self_var) + else: + return inferred + def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> str | None: + """Infer property type from docstring or docstring signature.""" + if ctx.docstring is not None: + inferred = infer_ret_type_sig_from_anon_docstring(ctx.docstring) + if not inferred: + inferred = infer_ret_type_sig_from_docstring(ctx.docstring, ctx.name) + if not inferred: + inferred = infer_prop_type_from_docstring(ctx.docstring) + return inferred + else: + return None -class FallbackSignatureGenerator(SignatureGenerator): - def get_function_sig( - self, func: object, module_name: str, name: str - ) -> list[FunctionSig] | None: - return [ - FunctionSig( - name=name, - args=infer_arg_sig_from_anon_docstring("(*args, **kwargs)"), - ret_type="Any", - ) - ] - def get_method_sig( - self, cls: type, func: object, module_name: str, class_name: str, name: str, self_var: str - ) -> list[FunctionSig] | None: - return [ - FunctionSig( - name=name, - args=infer_method_args(name, self_var), - ret_type=infer_method_ret_type(name), - ) - ] +def is_pybind11_overloaded_function_docstring(docstring: str, name: str) -> bool: + return docstring.startswith(f"{name}(*args, **kwargs)\nOverloaded function.\n\n") def generate_stub_for_c_module( module_name: str, target: str, known_modules: list[str], - sig_generators: Iterable[SignatureGenerator], + doc_dir: str = "", + *, + include_private: bool = False, + export_less: bool = False, include_docstrings: bool = False, ) -> None: """Generate stub for C module. @@ -184,452 +161,664 @@ def generate_stub_for_c_module( If directory for target doesn't exist it will be created. Existing stub will be overwritten. """ - module = importlib.import_module(module_name) - assert is_c_module(module), f"{module_name} is not a C module" subdir = os.path.dirname(target) if subdir and not os.path.isdir(subdir): os.makedirs(subdir) - imports: list[str] = [] - functions: list[str] = [] - done = set() - items = sorted(get_members(module), key=lambda x: x[0]) - for name, obj in items: - if is_c_function(obj): - generate_c_function_stub( - module, - name, - obj, - output=functions, - known_modules=known_modules, - imports=imports, - sig_generators=sig_generators, - include_docstrings=include_docstrings, - ) - done.add(name) - types: list[str] = [] - for name, obj in items: - if name.startswith("__") and name.endswith("__"): - continue - if is_c_type(obj): - generate_c_type_stub( - module, - name, - obj, - output=types, - known_modules=known_modules, - imports=imports, - sig_generators=sig_generators, - include_docstrings=include_docstrings, - ) - done.add(name) - variables = [] - for name, obj in items: - if name.startswith("__") and name.endswith("__"): - continue - if name not in done and not inspect.ismodule(obj): - type_str = strip_or_import( - get_type_fullname(type(obj)), module, known_modules, imports - ) - variables.append(f"{name}: {type_str}") - output = sorted(set(imports)) - for line in variables: - output.append(line) - for line in types: - if line.startswith("class") and output and output[-1]: - output.append("") - output.append(line) - if output and functions: - output.append("") - for line in functions: - output.append(line) - output = add_typing_import(output) + + gen = InspectionStubGenerator( + module_name, + known_modules, + doc_dir, + include_private=include_private, + export_less=export_less, + include_docstrings=include_docstrings, + ) + gen.generate_module() + output = gen.output() + with open(target, "w") as file: - for line in output: - file.write(f"{line}\n") - - -def add_typing_import(output: list[str]) -> list[str]: - """Add typing imports for collections/types that occur in the generated stub.""" - names = [] - for name in _DEFAULT_TYPING_IMPORTS: - if any(re.search(r"\b%s\b" % name, line) for line in output): - names.append(name) - if names: - return [f"from typing import {', '.join(names)}", ""] + output - else: - return output.copy() - - -def get_members(obj: object) -> list[tuple[str, Any]]: - obj_dict: Mapping[str, Any] = getattr(obj, "__dict__") # noqa: B009 - results = [] - for name in obj_dict: - if is_skipped_attribute(name): - continue - # Try to get the value via getattr - try: - value = getattr(obj, name) - except AttributeError: - continue - else: - results.append((name, value)) - return results + file.write(output) -def is_c_function(obj: object) -> bool: - return inspect.isbuiltin(obj) or type(obj) is type(ord) +class CFunctionStub: + """ + Class that mimics a C function in order to provide parseable docstrings. + """ + def __init__(self, name: str, doc: str, is_abstract: bool = False): + self.__name__ = name + self.__doc__ = doc + self.__abstractmethod__ = is_abstract -def is_c_method(obj: object) -> bool: - return inspect.ismethoddescriptor(obj) or type(obj) in ( - type(str.index), - type(str.__add__), - type(str.__new__), - ) + @classmethod + def _from_sig(cls, sig: FunctionSig, is_abstract: bool = False) -> CFunctionStub: + return CFunctionStub(sig.name, sig.format_sig()[:-4], is_abstract) + @classmethod + def _from_sigs(cls, sigs: list[FunctionSig], is_abstract: bool = False) -> CFunctionStub: + return CFunctionStub( + sigs[0].name, "\n".join(sig.format_sig()[:-4] for sig in sigs), is_abstract + ) -def is_c_classmethod(obj: object) -> bool: - return inspect.isbuiltin(obj) or type(obj).__name__ in ( - "classmethod", - "classmethod_descriptor", - ) + def __get__(self) -> None: + """ + This exists to make this object look like a method descriptor and thus + return true for CStubGenerator.ismethod() + """ + pass -def is_c_property(obj: object) -> bool: - return inspect.isdatadescriptor(obj) or hasattr(obj, "fget") +class InspectionStubGenerator(BaseStubGenerator): + """Stub generator that does not parse code. + Generation is performed by inspecting the module's contents, and thus works + for highly dynamic modules, pyc files, and C modules (via the CStubGenerator + subclass). + """ -def is_c_property_readonly(prop: Any) -> bool: - return hasattr(prop, "fset") and prop.fset is None + def __init__( + self, + module_name: str, + known_modules: list[str], + doc_dir: str = "", + _all_: list[str] | None = None, + include_private: bool = False, + export_less: bool = False, + include_docstrings: bool = False, + module: ModuleType | None = None, + ) -> None: + self.doc_dir = doc_dir + if module is None: + self.module = importlib.import_module(module_name) + else: + self.module = module + self.is_c_module = is_c_module(self.module) + self.known_modules = known_modules + self.resort_members = self.is_c_module + super().__init__(_all_, include_private, export_less, include_docstrings) + self.module_name = module_name + + def get_default_function_sig(self, func: object, ctx: FunctionContext) -> FunctionSig: + argspec = None + if not self.is_c_module: + # Get the full argument specification of the function + try: + argspec = inspect.getfullargspec(func) + except TypeError: + # some callables cannot be inspected, e.g. functools.partial + pass + if argspec is None: + if ctx.class_info is not None: + # method: + return FunctionSig( + name=ctx.name, + args=infer_c_method_args(ctx.name, ctx.class_info.self_var), + ret_type=infer_method_ret_type(ctx.name), + ) + else: + # function: + return FunctionSig( + name=ctx.name, + args=[ArgSig(name="*args"), ArgSig(name="**kwargs")], + ret_type=None, + ) + # Extract the function arguments, defaults, and varargs + args = argspec.args + defaults = argspec.defaults + varargs = argspec.varargs + kwargs = argspec.varkw + annotations = argspec.annotations + + def get_annotation(key: str) -> str | None: + if key not in annotations: + return None + argtype = annotations[key] + if argtype is None: + return "None" + if not isinstance(argtype, str): + return self.get_type_fullname(argtype) + return argtype + + arglist: list[ArgSig] = [] + # Add the arguments to the signature + for i, arg in enumerate(args): + # Check if the argument has a default value + if defaults and i >= len(args) - len(defaults): + default_value = defaults[i - (len(args) - len(defaults))] + if arg in annotations: + argtype = annotations[arg] + else: + argtype = self.get_type_annotation(default_value) + if argtype == "None": + # None is not a useful annotation, but we can infer that the arg + # is optional + incomplete = self.add_name("_typeshed.Incomplete") + argtype = f"{incomplete} | None" + arglist.append(ArgSig(arg, argtype, default=True)) + else: + arglist.append(ArgSig(arg, get_annotation(arg), default=False)) -def is_c_type(obj: object) -> bool: - return inspect.isclass(obj) or type(obj) is type(int) + # Add *args if present + if varargs: + arglist.append(ArgSig(f"*{varargs}", get_annotation(varargs))) + # Add **kwargs if present + if kwargs: + arglist.append(ArgSig(f"**{kwargs}", get_annotation(kwargs))) -def is_pybind11_overloaded_function_docstring(docstr: str, name: str) -> bool: - return docstr.startswith(f"{name}(*args, **kwargs)\n" + "Overloaded function.\n\n") + # add types for known special methods + if ctx.class_info is not None and all( + arg.type is None and arg.default is False for arg in arglist + ): + new_args = infer_method_arg_types( + ctx.name, ctx.class_info.self_var, [arg.name for arg in arglist if arg.name] + ) + if new_args is not None: + arglist = new_args + ret_type = get_annotation("return") or infer_method_ret_type(ctx.name) + return FunctionSig(ctx.name, arglist, ret_type) -def generate_c_function_stub( - module: ModuleType, - name: str, - obj: object, - *, - known_modules: list[str], - sig_generators: Iterable[SignatureGenerator], - output: list[str], - imports: list[str], - self_var: str | None = None, - cls: type | None = None, - class_name: str | None = None, - include_docstrings: bool = False, -) -> None: - """Generate stub for a single function or method. + def get_sig_generators(self) -> list[SignatureGenerator]: + if not self.is_c_module: + return [] + else: + sig_generators: list[SignatureGenerator] = [DocstringSignatureGenerator()] + if self.doc_dir: + # Collect info from docs (if given). Always check these first. + sig_generators.insert(0, ExternalSignatureGenerator.from_doc_dir(self.doc_dir)) + return sig_generators - The result will be appended to 'output'. - If necessary, any required names will be added to 'imports'. - The 'class_name' is used to find signature of __init__ or __new__ in - 'class_sigs'. - """ - inferred: list[FunctionSig] | None = None - docstr: str | None = None - if class_name: - # method: - assert cls is not None, "cls should be provided for methods" - assert self_var is not None, "self_var should be provided for methods" - for sig_gen in sig_generators: - inferred = sig_gen.get_method_sig( - cls, obj, module.__name__, class_name, name, self_var + def strip_or_import(self, type_name: str) -> str: + """Strips unnecessary module names from typ. + + If typ represents a type that is inside module or is a type coming from builtins, remove + module declaration from it. Return stripped name of the type. + + Arguments: + typ: name of the type + """ + local_modules = ["builtins", self.module_name] + parsed_type = parse_type_comment(type_name, 0, 0, None)[1] + assert parsed_type is not None, type_name + return self.print_annotation(parsed_type, self.known_modules, local_modules) + + def get_obj_module(self, obj: object) -> str | None: + """Return module name of the object.""" + return getattr(obj, "__module__", None) + + def is_defined_in_module(self, obj: object) -> bool: + """Check if object is considered defined in the current module.""" + module = self.get_obj_module(obj) + return module is None or module == self.module_name + + def generate_module(self) -> None: + all_items = self.get_members(self.module) + if self.resort_members: + all_items = sorted(all_items, key=lambda x: x[0]) + items = [] + for name, obj in all_items: + if inspect.ismodule(obj) and obj.__name__ in self.known_modules: + module_name = obj.__name__ + if module_name.startswith(self.module_name + "."): + # from {.rel_name} import {mod_name} as {name} + pkg_name, mod_name = module_name.rsplit(".", 1) + rel_module = pkg_name[len(self.module_name) :] or "." + self.import_tracker.add_import_from(rel_module, [(mod_name, name)]) + self.import_tracker.reexport(name) + else: + # import {module_name} as {name} + self.import_tracker.add_import(module_name, name) + self.import_tracker.reexport(name) + elif self.is_defined_in_module(obj) and not inspect.ismodule(obj): + # process this below + items.append((name, obj)) + else: + # from {obj_module} import {obj_name} + obj_module_name = self.get_obj_module(obj) + if obj_module_name: + self.import_tracker.add_import_from(obj_module_name, [(name, None)]) + if self.should_reexport(name, obj_module_name, name_is_alias=False): + self.import_tracker.reexport(name) + + self.set_defined_names(set([name for name, obj in all_items if not inspect.ismodule(obj)])) + + if self.resort_members: + functions: list[str] = [] + types: list[str] = [] + variables: list[str] = [] + else: + output: list[str] = [] + functions = types = variables = output + + for name, obj in items: + if self.is_function(obj): + self.generate_function_stub(name, obj, output=functions) + elif inspect.isclass(obj): + self.generate_class_stub(name, obj, output=types) + else: + self.generate_variable_stub(name, obj, output=variables) + + self._output = [] + + if self.resort_members: + for line in variables: + self._output.append(line + "\n") + for line in types: + if line.startswith("class") and self._output and self._output[-1]: + self._output.append("\n") + self._output.append(line + "\n") + if self._output and functions: + self._output.append("\n") + for line in functions: + self._output.append(line + "\n") + else: + for i, line in enumerate(output): + if ( + self._output + and line.startswith("class") + and ( + not self._output[-1].startswith("class") + or (len(output) > i + 1 and output[i + 1].startswith(" ")) + ) + ) or ( + self._output + and self._output[-1].startswith("def") + and not line.startswith("def") + ): + self._output.append("\n") + self._output.append(line + "\n") + self.check_undefined_names() + + def is_skipped_attribute(self, attr: str) -> bool: + return ( + attr + in ( + "__class__", + "__getattribute__", + "__str__", + "__repr__", + "__doc__", + "__dict__", + "__module__", + "__weakref__", + "__annotations__", ) - if inferred: - # add self/cls var, if not present - for sig in inferred: - if not sig.args or sig.args[0].name not in ("self", "cls"): - sig.args.insert(0, ArgSig(name=self_var)) - break - else: - # function: - for sig_gen in sig_generators: - inferred = sig_gen.get_function_sig(obj, module.__name__, name) - if inferred: - break - - if not inferred: - raise ValueError( - "No signature was found. This should never happen " - "if FallbackSignatureGenerator is provided" + or attr in self.IGNORED_DUNDERS + or is_pybind_skipped_attribute(attr) # For pickling + or keyword.iskeyword(attr) ) - is_overloaded = len(inferred) > 1 if inferred else False - if is_overloaded: - imports.append("from typing import overload") - if inferred: - for signature in inferred: - args: list[str] = [] - for arg in signature.args: - arg_def = arg.name - if arg_def == "None": - arg_def = "_none" # None is not a valid argument name - - if arg.type: - arg_def += ": " + strip_or_import(arg.type, module, known_modules, imports) - - if arg.default: - arg_def += " = ..." - - args.append(arg_def) - - if is_overloaded: - output.append("@overload") - # a sig generator indicates @classmethod by specifying the cls arg - if class_name and signature.args and signature.args[0].name == "cls": - output.append("@classmethod") - output_signature = "def {function}({args}) -> {ret}:".format( - function=name, - args=", ".join(args), - ret=strip_or_import(signature.ret_type, module, known_modules, imports), - ) - if include_docstrings and docstr: - docstr_quoted = mypy.util.quote_docstring(docstr.strip()) - docstr_indented = "\n ".join(docstr_quoted.split("\n")) - output.append(output_signature) - output.extend(f" {docstr_indented}".split("\n")) + def get_members(self, obj: object) -> list[tuple[str, Any]]: + obj_dict: Mapping[str, Any] = getattr(obj, "__dict__") # noqa: B009 + results = [] + for name in obj_dict: + if self.is_skipped_attribute(name): + continue + # Try to get the value via getattr + try: + value = getattr(obj, name) + except AttributeError: + continue else: - output_signature += " ..." - output.append(output_signature) - + results.append((name, value)) + return results -def strip_or_import( - typ: str, module: ModuleType, known_modules: list[str], imports: list[str] -) -> str: - """Strips unnecessary module names from typ. + def get_type_annotation(self, obj: object) -> str: + """ + Given an instance, return a string representation of its type that is valid + to use as a type annotation. + """ + if obj is None or obj is type(None): + return "None" + elif inspect.isclass(obj): + return "type[{}]".format(self.get_type_fullname(obj)) + elif isinstance(obj, FunctionType): + return self.add_name("typing.Callable") + elif isinstance(obj, ModuleType): + return self.add_name("types.ModuleType", require=False) + else: + return self.get_type_fullname(type(obj)) - If typ represents a type that is inside module or is a type coming from builtins, remove - module declaration from it. Return stripped name of the type. + def is_function(self, obj: object) -> bool: + if self.is_c_module: + return inspect.isbuiltin(obj) + else: + return inspect.isfunction(obj) + + def is_method(self, class_info: ClassInfo, name: str, obj: object) -> bool: + if self.is_c_module: + return inspect.ismethoddescriptor(obj) or type(obj) in ( + type(str.index), + type(str.__add__), + type(str.__new__), + ) + else: + # this is valid because it is only called on members of a class + return inspect.isfunction(obj) + + def is_classmethod(self, class_info: ClassInfo, name: str, obj: object) -> bool: + if self.is_c_module: + return inspect.isbuiltin(obj) or type(obj).__name__ in ( + "classmethod", + "classmethod_descriptor", + ) + else: + return inspect.ismethod(obj) - Arguments: - typ: name of the type - module: in which this type is used - known_modules: other modules being processed - imports: list of import statements (may be modified during the call) - """ - local_modules = ["builtins"] - if module: - local_modules.append(module.__name__) - - stripped_type = typ - if any(c in typ for c in "[,"): - for subtyp in re.split(r"[\[,\]]", typ): - stripped_subtyp = strip_or_import(subtyp.strip(), module, known_modules, imports) - if stripped_subtyp != subtyp: - stripped_type = re.sub( - r"(^|[\[, ]+)" + re.escape(subtyp) + r"($|[\], ]+)", - r"\1" + stripped_subtyp + r"\2", - stripped_type, - ) - elif "." in typ: - for module_name in local_modules + list(reversed(known_modules)): - if typ.startswith(module_name + "."): - if module_name in local_modules: - stripped_type = typ[len(module_name) + 1 :] - arg_module = module_name - break + def is_staticmethod(self, class_info: ClassInfo | None, name: str, obj: object) -> bool: + if self.is_c_module: + return False else: - arg_module = typ[: typ.rindex(".")] - if arg_module not in local_modules: - imports.append(f"import {arg_module}") - if stripped_type == "NoneType": - stripped_type = "None" - return stripped_type - - -def is_static_property(obj: object) -> bool: - return type(obj).__name__ == "pybind11_static_property" - - -def generate_c_property_stub( - name: str, - obj: object, - static_properties: list[str], - rw_properties: list[str], - ro_properties: list[str], - readonly: bool, - module: ModuleType | None = None, - known_modules: list[str] | None = None, - imports: list[str] | None = None, -) -> None: - """Generate property stub using introspection of 'obj'. + return class_info is not None and isinstance( + inspect.getattr_static(class_info.cls, name), staticmethod + ) - Try to infer type from docstring, append resulting lines to 'output'. - """ + @staticmethod + def is_abstract_method(obj: object) -> bool: + return getattr(obj, "__abstractmethod__", False) - def infer_prop_type(docstr: str | None) -> str | None: - """Infer property type from docstring or docstring signature.""" - if docstr is not None: - inferred = infer_ret_type_sig_from_anon_docstring(docstr) - if not inferred: - inferred = infer_ret_type_sig_from_docstring(docstr, name) - if not inferred: - inferred = infer_prop_type_from_docstring(docstr) - return inferred - else: - return None + @staticmethod + def is_property(class_info: ClassInfo, name: str, obj: object) -> bool: + return inspect.isdatadescriptor(obj) or hasattr(obj, "fget") - inferred = infer_prop_type(getattr(obj, "__doc__", None)) - if not inferred: - fget = getattr(obj, "fget", None) - inferred = infer_prop_type(getattr(fget, "__doc__", None)) - if not inferred: - inferred = "Any" - - if module is not None and imports is not None and known_modules is not None: - inferred = strip_or_import(inferred, module, known_modules, imports) - - if is_static_property(obj): - trailing_comment = " # read-only" if readonly else "" - static_properties.append(f"{name}: ClassVar[{inferred}] = ...{trailing_comment}") - else: # regular property - if readonly: - ro_properties.append("@property") - ro_properties.append(f"def {name}(self) -> {inferred}: ...") + @staticmethod + def is_property_readonly(prop: Any) -> bool: + return hasattr(prop, "fset") and prop.fset is None + + def is_static_property(self, obj: object) -> bool: + """For c-modules, whether the property behaves like an attribute""" + if self.is_c_module: + # StaticProperty is from boost-python + return type(obj).__name__ in ("pybind11_static_property", "StaticProperty") else: - rw_properties.append(f"{name}: {inferred}") + return False + + def process_inferred_sigs(self, inferred: list[FunctionSig]) -> None: + for i, sig in enumerate(inferred): + for arg in sig.args: + if arg.type is not None: + arg.type = self.strip_or_import(arg.type) + if sig.ret_type is not None: + inferred[i] = sig._replace(ret_type=self.strip_or_import(sig.ret_type)) + + def generate_function_stub( + self, name: str, obj: object, *, output: list[str], class_info: ClassInfo | None = None + ) -> None: + """Generate stub for a single function or method. + + The result (always a single line) will be appended to 'output'. + If necessary, any required names will be added to 'imports'. + The 'class_name' is used to find signature of __init__ or __new__ in + 'class_sigs'. + """ + docstring: Any = getattr(obj, "__doc__", None) + if not isinstance(docstring, str): + docstring = None + + ctx = FunctionContext( + self.module_name, + name, + docstring=docstring, + is_abstract=self.is_abstract_method(obj), + class_info=class_info, + ) + if self.is_private_name(name, ctx.fullname) or self.is_not_in_all(name): + return + self.record_name(ctx.name) + default_sig = self.get_default_function_sig(obj, ctx) + inferred = self.get_signatures(default_sig, self.sig_generators, ctx) + self.process_inferred_sigs(inferred) -def generate_c_type_stub( - module: ModuleType, - class_name: str, - obj: type, - output: list[str], - known_modules: list[str], - imports: list[str], - sig_generators: Iterable[SignatureGenerator], - include_docstrings: bool = False, -) -> None: - """Generate stub for a single class using runtime introspection. + decorators = [] + if len(inferred) > 1: + decorators.append("@{}".format(self.add_name("typing.overload"))) - The result lines will be appended to 'output'. If necessary, any - required names will be added to 'imports'. - """ - raw_lookup = getattr(obj, "__dict__") # noqa: B009 - items = sorted(get_members(obj), key=lambda x: method_name_sort_key(x[0])) - names = {x[0] for x in items} - methods: list[str] = [] - types: list[str] = [] - static_properties: list[str] = [] - rw_properties: list[str] = [] - ro_properties: list[str] = [] - attrs: list[tuple[str, Any]] = [] - for attr, value in items: - # use unevaluated descriptors when dealing with property inspection - raw_value = raw_lookup.get(attr, value) - if is_c_method(value) or is_c_classmethod(value): - if attr == "__new__": - # TODO: We should support __new__. - if "__init__" in names: - # Avoid duplicate functions if both are present. - # But is there any case where .__new__() has a - # better signature than __init__() ? - continue - attr = "__init__" - if is_c_classmethod(value): - self_var = "cls" + if ctx.is_abstract: + decorators.append("@{}".format(self.add_name("abc.abstractmethod"))) + + if class_info is not None: + if self.is_staticmethod(class_info, name, obj): + decorators.append("@staticmethod") else: - self_var = "self" - generate_c_function_stub( - module, - attr, - value, - output=methods, - known_modules=known_modules, - imports=imports, - self_var=self_var, - cls=obj, - class_name=class_name, - sig_generators=sig_generators, - include_docstrings=include_docstrings, - ) - elif is_c_property(raw_value): - generate_c_property_stub( - attr, - raw_value, - static_properties, - rw_properties, - ro_properties, - is_c_property_readonly(raw_value), - module=module, - known_modules=known_modules, - imports=imports, - ) - elif is_c_type(value): - generate_c_type_stub( - module, - attr, - value, - types, - imports=imports, - known_modules=known_modules, - sig_generators=sig_generators, - include_docstrings=include_docstrings, + for sig in inferred: + if not sig.args or sig.args[0].name not in ("self", "cls"): + sig.args.insert(0, ArgSig(name=class_info.self_var)) + # a sig generator indicates @classmethod by specifying the cls arg. + if inferred[0].args and inferred[0].args[0].name == "cls": + decorators.append("@classmethod") + + output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring)) + self._fix_iter(ctx, inferred, output) + + def _fix_iter( + self, ctx: FunctionContext, inferred: list[FunctionSig], output: list[str] + ) -> None: + """Ensure that objects which implement old-style iteration via __getitem__ + are considered iterable. + """ + if ( + ctx.class_info + and ctx.class_info.cls is not None + and ctx.name == "__getitem__" + and "__iter__" not in ctx.class_info.cls.__dict__ + ): + item_type: str | None = None + for sig in inferred: + if sig.args and sig.args[-1].type == "int": + item_type = sig.ret_type + break + if item_type is None: + return + obj = CFunctionStub( + "__iter__", f"def __iter__(self) -> typing.Iterator[{item_type}]\n" ) + self.generate_function_stub("__iter__", obj, output=output, class_info=ctx.class_info) + + def generate_property_stub( + self, + name: str, + raw_obj: object, + obj: object, + static_properties: list[str], + rw_properties: list[str], + ro_properties: list[str], + class_info: ClassInfo | None = None, + ) -> None: + """Generate property stub using introspection of 'obj'. + + Try to infer type from docstring, append resulting lines to 'output'. + + raw_obj : object before evaluation of descriptor (if any) + obj : object after evaluation of descriptor + """ + + docstring = getattr(raw_obj, "__doc__", None) + fget = getattr(raw_obj, "fget", None) + if fget: + alt_docstr = getattr(fget, "__doc__", None) + if alt_docstr and docstring: + docstring += alt_docstr + elif alt_docstr: + docstring = alt_docstr + + ctx = FunctionContext( + self.module_name, name, docstring=docstring, is_abstract=False, class_info=class_info + ) + + if self.is_private_name(name, ctx.fullname) or self.is_not_in_all(name): + return + + self.record_name(ctx.name) + static = self.is_static_property(raw_obj) + readonly = self.is_property_readonly(raw_obj) + if static: + ret_type: str | None = self.strip_or_import(self.get_type_annotation(obj)) else: - attrs.append((attr, value)) + default_sig = self.get_default_function_sig(raw_obj, ctx) + ret_type = default_sig.ret_type + + inferred_type = self.get_property_type(ret_type, self.sig_generators, ctx) + if inferred_type is not None: + inferred_type = self.strip_or_import(inferred_type) - for attr, value in attrs: - static_properties.append( - "{}: ClassVar[{}] = ...".format( - attr, - strip_or_import(get_type_fullname(type(value)), module, known_modules, imports), + if static: + classvar = self.add_name("typing.ClassVar") + trailing_comment = " # read-only" if readonly else "" + if inferred_type is None: + inferred_type = self.add_name("_typeshed.Incomplete") + + static_properties.append( + f"{self._indent}{name}: {classvar}[{inferred_type}] = ...{trailing_comment}" ) - ) - all_bases = type.mro(obj) - if all_bases[-1] is object: - # TODO: Is this always object? - del all_bases[-1] - # remove pybind11_object. All classes generated by pybind11 have pybind11_object in their MRO, - # which only overrides a few functions in object type - if all_bases and all_bases[-1].__name__ == "pybind11_object": - del all_bases[-1] - # remove the class itself - all_bases = all_bases[1:] - # Remove base classes of other bases as redundant. - bases: list[type] = [] - for base in all_bases: - if not any(issubclass(b, base) for b in bases): - bases.append(base) - if bases: - bases_str = "(%s)" % ", ".join( - strip_or_import(get_type_fullname(base), module, known_modules, imports) - for base in bases - ) - else: - bases_str = "" - if types or static_properties or rw_properties or methods or ro_properties: - output.append(f"class {class_name}{bases_str}:") - for line in types: - if ( - output - and output[-1] - and not output[-1].startswith("class") - and line.startswith("class") + else: # regular property + if readonly: + ro_properties.append(f"{self._indent}@property") + sig = FunctionSig(name, [ArgSig("self")], inferred_type) + ro_properties.append(sig.format_sig(indent=self._indent)) + else: + if inferred_type is None: + inferred_type = self.add_name("_typeshed.Incomplete") + + rw_properties.append(f"{self._indent}{name}: {inferred_type}") + + def get_type_fullname(self, typ: type) -> str: + """Given a type, return a string representation""" + if typ is Any: + return "Any" + typename = getattr(typ, "__qualname__", typ.__name__) + module_name = self.get_obj_module(typ) + assert module_name is not None, typ + if module_name != "builtins": + typename = f"{module_name}.{typename}" + return typename + + def get_base_types(self, obj: type) -> list[str]: + all_bases = type.mro(obj) + if all_bases[-1] is object: + # TODO: Is this always object? + del all_bases[-1] + # remove pybind11_object. All classes generated by pybind11 have pybind11_object in their MRO, + # which only overrides a few functions in object type + if all_bases and all_bases[-1].__name__ == "pybind11_object": + del all_bases[-1] + # remove the class itself + all_bases = all_bases[1:] + # Remove base classes of other bases as redundant. + bases: list[type] = [] + for base in all_bases: + if not any(issubclass(b, base) for b in bases): + bases.append(base) + return [self.strip_or_import(self.get_type_fullname(base)) for base in bases] + + def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> None: + """Generate stub for a single class using runtime introspection. + + The result lines will be appended to 'output'. If necessary, any + required names will be added to 'imports'. + """ + raw_lookup = getattr(cls, "__dict__") # noqa: B009 + items = self.get_members(cls) + if self.resort_members: + items = sorted(items, key=lambda x: method_name_sort_key(x[0])) + names = set(x[0] for x in items) + methods: list[str] = [] + types: list[str] = [] + static_properties: list[str] = [] + rw_properties: list[str] = [] + ro_properties: list[str] = [] + attrs: list[tuple[str, Any]] = [] + + self.record_name(class_name) + self.indent() + + class_info = ClassInfo(class_name, "", getattr(cls, "__doc__", None), cls) + + for attr, value in items: + # use unevaluated descriptors when dealing with property inspection + raw_value = raw_lookup.get(attr, value) + if self.is_method(class_info, attr, value) or self.is_classmethod( + class_info, attr, value ): - output.append("") - output.append(" " + line) - for line in static_properties: - output.append(f" {line}") - for line in rw_properties: - output.append(f" {line}") - for line in methods: - output.append(f" {line}") - for line in ro_properties: - output.append(f" {line}") - else: - output.append(f"class {class_name}{bases_str}: ...") + if attr == "__new__": + # TODO: We should support __new__. + if "__init__" in names: + # Avoid duplicate functions if both are present. + # But is there any case where .__new__() has a + # better signature than __init__() ? + continue + attr = "__init__" + # FIXME: make this nicer + if self.is_classmethod(class_info, attr, value): + class_info.self_var = "cls" + else: + class_info.self_var = "self" + self.generate_function_stub(attr, value, output=methods, class_info=class_info) + elif self.is_property(class_info, attr, raw_value): + self.generate_property_stub( + attr, + raw_value, + value, + static_properties, + rw_properties, + ro_properties, + class_info, + ) + elif inspect.isclass(value) and self.is_defined_in_module(value): + self.generate_class_stub(attr, value, types) + else: + attrs.append((attr, value)) + for attr, value in attrs: + if attr == "__hash__" and value is None: + # special case for __hash__ + continue + prop_type_name = self.strip_or_import(self.get_type_annotation(value)) + classvar = self.add_name("typing.ClassVar") + static_properties.append(f"{self._indent}{attr}: {classvar}[{prop_type_name}] = ...") -def get_type_fullname(typ: type) -> str: - return f"{typ.__module__}.{getattr(typ, '__qualname__', typ.__name__)}" + self.dedent() + + bases = self.get_base_types(cls) + if bases: + bases_str = "(%s)" % ", ".join(bases) + else: + bases_str = "" + if types or static_properties or rw_properties or methods or ro_properties: + output.append(f"{self._indent}class {class_name}{bases_str}:") + for line in types: + if ( + output + and output[-1] + and not output[-1].strip().startswith("class") + and line.strip().startswith("class") + ): + output.append("") + output.append(line) + for line in static_properties: + output.append(line) + for line in rw_properties: + output.append(line) + for line in methods: + output.append(line) + for line in ro_properties: + output.append(line) + else: + output.append(f"{self._indent}class {class_name}{bases_str}: ...") + + def generate_variable_stub(self, name: str, obj: object, output: list[str]) -> None: + """Generate stub for a single variable using runtime introspection. + + The result lines will be appended to 'output'. If necessary, any + required names will be added to 'imports'. + """ + if self.is_private_name(name, f"{self.module_name}.{name}") or self.is_not_in_all(name): + return + self.record_name(name) + type_str = self.strip_or_import(self.get_type_annotation(obj)) + output.append(f"{name}: {type_str}") def method_name_sort_key(name: str) -> tuple[int, str]: @@ -648,22 +837,9 @@ def is_pybind_skipped_attribute(attr: str) -> bool: return attr.startswith("__pybind11_module_local_") -def is_skipped_attribute(attr: str) -> bool: - return attr in ( - "__class__", - "__getattribute__", - "__str__", - "__repr__", - "__doc__", - "__dict__", - "__module__", - "__weakref__", - ) or is_pybind_skipped_attribute( # For pickling - attr - ) - - -def infer_method_args(name: str, self_var: str | None = None) -> list[ArgSig]: +def infer_c_method_args( + name: str, self_var: str = "self", arg_names: list[str] | None = None +) -> list[ArgSig]: args: list[ArgSig] | None = None if name.startswith("__") and name.endswith("__"): name = name[2:-2] @@ -703,13 +879,9 @@ def infer_method_args(name: str, self_var: str | None = None) -> list[ArgSig]: args = [] elif name == "setstate": args = [ArgSig(name="state")] + elif name in ("eq", "ne", "lt", "le", "gt", "ge"): + args = [ArgSig(name="other", type="object")] elif name in ( - "eq", - "ne", - "lt", - "le", - "gt", - "ge", "add", "radd", "sub", @@ -761,22 +933,15 @@ def infer_method_args(name: str, self_var: str | None = None) -> list[ArgSig]: elif name == "reduce_ex": args = [ArgSig(name="protocol")] elif name == "exit": - args = [ArgSig(name="type"), ArgSig(name="value"), ArgSig(name="traceback")] + args = [ + ArgSig(name="type", type="type[BaseException] | None"), + ArgSig(name="value", type="BaseException | None"), + ArgSig(name="traceback", type="types.TracebackType | None"), + ] + if args is None: + args = infer_method_arg_types(name, self_var, arg_names) + else: + args = [ArgSig(name=self_var)] + args if args is None: args = [ArgSig(name="*args"), ArgSig(name="**kwargs")] - return [ArgSig(name=self_var or "self")] + args - - -def infer_method_ret_type(name: str) -> str: - if name.startswith("__") and name.endswith("__"): - name = name[2:-2] - if name in ("float", "bool", "bytes", "int"): - return name - # Note: __eq__ and co may return arbitrary types, but bool is good enough for stubgen. - elif name in ("eq", "ne", "lt", "le", "gt", "ge", "contains"): - return "bool" - elif name in ("len", "hash", "sizeof", "trunc", "floor", "ceil"): - return "int" - elif name in ("init", "setitem"): - return "None" - return "Any" + return args diff --git a/mypy/stubutil.py b/mypy/stubutil.py index e15766b66cb3..22e525c14e7c 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -5,19 +5,26 @@ import os.path import re import sys +from abc import abstractmethod +from collections import defaultdict from contextlib import contextmanager -from typing import Iterator +from typing import Final, Iterable, Iterator, Mapping from typing_extensions import overload +from mypy_extensions import mypyc_attr + +import mypy.options from mypy.modulefinder import ModuleNotFoundReason from mypy.moduleinspect import InspectError, ModuleInspect +from mypy.stubdoc import ArgSig, FunctionSig +from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () class CantImport(Exception): - def __init__(self, module: str, message: str): + def __init__(self, module: str, message: str) -> None: self.module = module self.message = message @@ -70,8 +77,9 @@ def find_module_path_and_all_py3( ) -> tuple[str | None, list[str] | None] | None: """Find module and determine __all__ for a Python 3 module. - Return None if the module is a C module. Return (module_path, __all__) if - it is a Python module. Raise CantImport if import failed. + Return None if the module is a C or pyc-only module. + Return (module_path, __all__) if it is a Python module. + Raise CantImport if import failed. """ if module in NOT_IMPORTABLE_MODULES: raise CantImport(module, "") @@ -182,3 +190,591 @@ def common_dir_prefix(paths: list[str]) -> str: cur = path break return cur or "." + + +class AnnotationPrinter(TypeStrVisitor): + """Visitor used to print existing annotations in a file. + + The main difference from TypeStrVisitor is a better treatment of + unbound types. + + Notes: + * This visitor doesn't add imports necessary for annotations, this is done separately + by ImportTracker. + * It can print all kinds of types, but the generated strings may not be valid (notably + callable types) since it prints the same string that reveal_type() does. + * For Instance types it prints the fully qualified names. + """ + + # TODO: Generate valid string representation for callable types. + # TODO: Use short names for Instances. + def __init__( + self, + stubgen: BaseStubGenerator, + known_modules: list[str] | None = None, + local_modules: list[str] | None = None, + ) -> None: + super().__init__(options=mypy.options.Options()) + self.stubgen = stubgen + self.known_modules = known_modules + self.local_modules = local_modules or ["builtins"] + + def visit_any(self, t: AnyType) -> str: + s = super().visit_any(t) + self.stubgen.import_tracker.require_name(s) + return s + + def visit_unbound_type(self, t: UnboundType) -> str: + s = t.name + if self.known_modules is not None and "." in s: + # see if this object is from any of the modules that we're currently processing. + # reverse sort so that subpackages come before parents: e.g. "foo.bar" before "foo". + for module_name in self.local_modules + sorted(self.known_modules, reverse=True): + if s.startswith(module_name + "."): + if module_name in self.local_modules: + s = s[len(module_name) + 1 :] + arg_module = module_name + break + else: + arg_module = s[: s.rindex(".")] + if arg_module not in self.local_modules: + self.stubgen.import_tracker.add_import(arg_module, require=True) + elif s == "NoneType": + # when called without analysis all types are unbound, so this won't hit + # visit_none_type(). + s = "None" + else: + self.stubgen.import_tracker.require_name(s) + if t.args: + s += f"[{self.args_str(t.args)}]" + return s + + def visit_none_type(self, t: NoneType) -> str: + return "None" + + def visit_type_list(self, t: TypeList) -> str: + return f"[{self.list_str(t.items)}]" + + def visit_union_type(self, t: UnionType) -> str: + return " | ".join([item.accept(self) for item in t.items]) + + def args_str(self, args: Iterable[Type]) -> str: + """Convert an array of arguments to strings and join the results with commas. + + The main difference from list_str is the preservation of quotes for string + arguments + """ + types = ["builtins.bytes", "builtins.str"] + res = [] + for arg in args: + arg_str = arg.accept(self) + if isinstance(arg, UnboundType) and arg.original_str_fallback in types: + res.append(f"'{arg_str}'") + else: + res.append(arg_str) + return ", ".join(res) + + +class ClassInfo: + def __init__( + self, name: str, self_var: str, docstring: str | None = None, cls: type | None = None + ) -> None: + self.name = name + self.self_var = self_var + self.docstring = docstring + self.cls = cls + + +class FunctionContext: + def __init__( + self, + module_name: str, + name: str, + docstring: str | None = None, + is_abstract: bool = False, + class_info: ClassInfo | None = None, + ) -> None: + self.module_name = module_name + self.name = name + self.docstring = docstring + self.is_abstract = is_abstract + self.class_info = class_info + self._fullname: str | None = None + + @property + def fullname(self) -> str: + if self._fullname is None: + if self.class_info: + self._fullname = f"{self.module_name}.{self.class_info.name}.{self.name}" + else: + self._fullname = f"{self.module_name}.{self.name}" + return self._fullname + + +def infer_method_ret_type(name: str) -> str | None: + """Infer return types for known special methods""" + if name.startswith("__") and name.endswith("__"): + name = name[2:-2] + if name in ("float", "bool", "bytes", "int", "complex", "str"): + return name + # Note: __eq__ and co may return arbitrary types, but bool is good enough for stubgen. + elif name in ("eq", "ne", "lt", "le", "gt", "ge", "contains"): + return "bool" + elif name in ("len", "length_hint", "index", "hash", "sizeof", "trunc", "floor", "ceil"): + return "int" + elif name in ("format", "repr"): + return "str" + elif name in ("init", "setitem", "del", "delitem"): + return "None" + return None + + +def infer_method_arg_types( + name: str, self_var: str = "self", arg_names: list[str] | None = None +) -> list[ArgSig] | None: + """Infer argument types for known special methods""" + args: list[ArgSig] | None = None + if name.startswith("__") and name.endswith("__"): + if arg_names and len(arg_names) >= 1 and arg_names[0] == "self": + arg_names = arg_names[1:] + + name = name[2:-2] + if name == "exit": + if arg_names is None: + arg_names = ["type", "value", "traceback"] + if len(arg_names) == 3: + arg_types = [ + "type[BaseException] | None", + "BaseException | None", + "types.TracebackType | None", + ] + args = [ + ArgSig(name=arg_name, type=arg_type) + for arg_name, arg_type in zip(arg_names, arg_types) + ] + if args is not None: + return [ArgSig(name=self_var)] + args + return None + + +@mypyc_attr(allow_interpreted_subclasses=True) +class SignatureGenerator: + """Abstract base class for extracting a list of FunctionSigs for each function.""" + + def remove_self_type( + self, inferred: list[FunctionSig] | None, self_var: str + ) -> list[FunctionSig] | None: + """Remove type annotation from self/cls argument""" + if inferred: + for signature in inferred: + if signature.args: + if signature.args[0].name == self_var: + signature.args[0].type = None + return inferred + + @abstractmethod + def get_function_sig( + self, default_sig: FunctionSig, ctx: FunctionContext + ) -> list[FunctionSig] | None: + """Return a list of signatures for the given function. + + If no signature can be found, return None. If all of the registered SignatureGenerators + for the stub generator return None, then the default_sig will be used. + """ + pass + + @abstractmethod + def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> str | None: + """Return the type of the given property""" + pass + + +class ImportTracker: + """Record necessary imports during stub generation.""" + + def __init__(self) -> None: + # module_for['foo'] has the module name where 'foo' was imported from, or None if + # 'foo' is a module imported directly; + # direct_imports['foo'] is the module path used when the name 'foo' was added to the + # namespace. + # reverse_alias['foo'] is the name that 'foo' had originally when imported with an + # alias; examples + # 'from pkg import mod' ==> module_for['mod'] == 'pkg' + # 'from pkg import mod as m' ==> module_for['m'] == 'pkg' + # ==> reverse_alias['m'] == 'mod' + # 'import pkg.mod as m' ==> module_for['m'] == None + # ==> reverse_alias['m'] == 'pkg.mod' + # 'import pkg.mod' ==> module_for['pkg'] == None + # ==> module_for['pkg.mod'] == None + # ==> direct_imports['pkg'] == 'pkg.mod' + # ==> direct_imports['pkg.mod'] == 'pkg.mod' + self.module_for: dict[str, str | None] = {} + self.direct_imports: dict[str, str] = {} + self.reverse_alias: dict[str, str] = {} + + # required_names is the set of names that are actually used in a type annotation + self.required_names: set[str] = set() + + # Names that should be reexported if they come from another module + self.reexports: set[str] = set() + + def add_import_from( + self, module: str, names: list[tuple[str, str | None]], require: bool = False + ) -> None: + for name, alias in names: + if alias: + # 'from {module} import {name} as {alias}' + self.module_for[alias] = module + self.reverse_alias[alias] = name + else: + # 'from {module} import {name}' + self.module_for[name] = module + self.reverse_alias.pop(name, None) + if require: + self.require_name(alias or name) + self.direct_imports.pop(alias or name, None) + + def add_import(self, module: str, alias: str | None = None, require: bool = False) -> None: + if alias: + # 'import {module} as {alias}' + assert "." not in alias # invalid syntax + self.module_for[alias] = None + self.reverse_alias[alias] = module + if require: + self.required_names.add(alias) + else: + # 'import {module}' + name = module + if require: + self.required_names.add(name) + # add module and its parent packages + while name: + self.module_for[name] = None + self.direct_imports[name] = module + self.reverse_alias.pop(name, None) + name = name.rpartition(".")[0] + + def require_name(self, name: str) -> None: + while name not in self.direct_imports and "." in name: + name = name.rsplit(".", 1)[0] + self.required_names.add(name) + + def reexport(self, name: str) -> None: + """Mark a given non qualified name as needed in __all__. + + This means that in case it comes from a module, it should be + imported with an alias even if the alias is the same as the name. + """ + self.require_name(name) + self.reexports.add(name) + + def import_lines(self) -> list[str]: + """The list of required import lines (as strings with python code). + + In order for a module be included in this output, an indentifier must be both + 'required' via require_name() and 'imported' via add_import_from() + or add_import() + """ + result = [] + + # To summarize multiple names imported from a same module, we collect those + # in the `module_map` dictionary, mapping a module path to the list of names that should + # be imported from it. the names can also be alias in the form 'original as alias' + module_map: Mapping[str, list[str]] = defaultdict(list) + + for name in sorted( + self.required_names, + key=lambda n: (self.reverse_alias[n], n) if n in self.reverse_alias else (n, ""), + ): + # If we haven't seen this name in an import statement, ignore it + if name not in self.module_for: + continue + + m = self.module_for[name] + if m is not None: + # This name was found in a from ... import ... + # Collect the name in the module_map + if name in self.reverse_alias: + name = f"{self.reverse_alias[name]} as {name}" + elif name in self.reexports: + name = f"{name} as {name}" + module_map[m].append(name) + else: + # This name was found in an import ... + # We can already generate the import line + if name in self.reverse_alias: + source = self.reverse_alias[name] + result.append(f"import {source} as {name}\n") + elif name in self.reexports: + assert "." not in name # Because reexports only has nonqualified names + result.append(f"import {name} as {name}\n") + else: + result.append(f"import {name}\n") + + # Now generate all the from ... import ... lines collected in module_map + for module, names in sorted(module_map.items()): + result.append(f"from {module} import {', '.join(sorted(names))}\n") + return result + + +@mypyc_attr(allow_interpreted_subclasses=True) +class BaseStubGenerator: + # These names should be omitted from generated stubs. + IGNORED_DUNDERS: Final = { + "__all__", + "__author__", + "__about__", + "__copyright__", + "__email__", + "__license__", + "__summary__", + "__title__", + "__uri__", + "__str__", + "__repr__", + "__getstate__", + "__setstate__", + "__slots__", + "__builtins__", + "__cached__", + "__file__", + "__name__", + "__package__", + "__path__", + "__spec__", + "__loader__", + } + TYPING_MODULE_NAMES: Final = ("typing", "typing_extensions") + # Special-cased names that are implicitly exported from the stub (from m import y as y). + EXTRA_EXPORTED: Final = { + "pyasn1_modules.rfc2437.univ", + "pyasn1_modules.rfc2459.char", + "pyasn1_modules.rfc2459.univ", + } + + def __init__( + self, + _all_: list[str] | None = None, + include_private: bool = False, + export_less: bool = False, + include_docstrings: bool = False, + ): + # Best known value of __all__. + self._all_ = _all_ + self._include_private = include_private + self._include_docstrings = include_docstrings + # Disable implicit exports of package-internal imports? + self.export_less = export_less + self._import_lines: list[str] = [] + self._output: list[str] = [] + # Current indent level (indent is hardcoded to 4 spaces). + self._indent = "" + self._toplevel_names: list[str] = [] + self.import_tracker = ImportTracker() + # Top-level members + self.defined_names: set[str] = set() + self.sig_generators = self.get_sig_generators() + # populated by visit_mypy_file + self.module_name: str = "" + + def get_sig_generators(self) -> list[SignatureGenerator]: + return [] + + def refers_to_fullname(self, name: str, fullname: str | tuple[str, ...]) -> bool: + """Return True if the variable name identifies the same object as the given fullname(s).""" + if isinstance(fullname, tuple): + return any(self.refers_to_fullname(name, fname) for fname in fullname) + module, short = fullname.rsplit(".", 1) + return self.import_tracker.module_for.get(name) == module and ( + name == short or self.import_tracker.reverse_alias.get(name) == short + ) + + def add_name(self, fullname: str, require: bool = True) -> str: + """Add a name to be imported and return the name reference. + + The import will be internal to the stub (i.e don't reexport). + """ + module, name = fullname.rsplit(".", 1) + alias = "_" + name if name in self.defined_names else None + self.import_tracker.add_import_from(module, [(name, alias)], require=require) + return alias or name + + def add_import_line(self, line: str) -> None: + """Add a line of text to the import section, unless it's already there.""" + if line not in self._import_lines: + self._import_lines.append(line) + + def get_imports(self) -> str: + """Return the import statements for the stub.""" + imports = "" + if self._import_lines: + imports += "".join(self._import_lines) + imports += "".join(self.import_tracker.import_lines()) + return imports + + def output(self) -> str: + """Return the text for the stub.""" + imports = self.get_imports() + if imports and self._output: + imports += "\n" + return imports + "".join(self._output) + + def add(self, string: str) -> None: + """Add text to generated stub.""" + self._output.append(string) + + def is_top_level(self) -> bool: + """Are we processing the top level of a file?""" + return self._indent == "" + + def indent(self) -> None: + """Add one level of indentation.""" + self._indent += " " + + def dedent(self) -> None: + """Remove one level of indentation.""" + self._indent = self._indent[:-4] + + def record_name(self, name: str) -> None: + """Mark a name as defined. + + This only does anything if at the top level of a module. + """ + if self.is_top_level(): + self._toplevel_names.append(name) + + def is_recorded_name(self, name: str) -> bool: + """Has this name been recorded previously?""" + return self.is_top_level() and name in self._toplevel_names + + def set_defined_names(self, defined_names: set[str]) -> None: + self.defined_names = defined_names + # Names in __all__ are required + for name in self._all_ or (): + if name not in self.IGNORED_DUNDERS: + self.import_tracker.reexport(name) + + # These are "soft" imports for objects which might appear in annotations but not have + # a corresponding import statement. + known_imports = { + "_typeshed": ["Incomplete"], + "typing": ["Any", "TypeVar", "NamedTuple"], + "collections.abc": ["Generator"], + "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], + } + for pkg, imports in known_imports.items(): + for t in imports: + # require=False means that the import won't be added unless require_name() is called + # for the object during generation. + self.add_name(f"{pkg}.{t}", require=False) + + def check_undefined_names(self) -> None: + print(self._all_) + print(self._toplevel_names) + undefined_names = [name for name in self._all_ or [] if name not in self._toplevel_names] + if undefined_names: + if self._output: + self.add("\n") + self.add("# Names in __all__ with no definition:\n") + for name in sorted(undefined_names): + self.add(f"# {name}\n") + + def get_signatures( + self, + default_signature: FunctionSig, + sig_generators: list[SignatureGenerator], + func_ctx: FunctionContext, + ) -> list[FunctionSig]: + for sig_gen in sig_generators: + inferred = sig_gen.get_function_sig(default_signature, func_ctx) + if inferred: + return inferred + + return [default_signature] + + def get_property_type( + self, + default_type: str | None, + sig_generators: list[SignatureGenerator], + func_ctx: FunctionContext, + ) -> str | None: + for sig_gen in sig_generators: + inferred = sig_gen.get_property_type(default_type, func_ctx) + if inferred: + return inferred + + return default_type + + def format_func_def( + self, + sigs: list[FunctionSig], + is_coroutine: bool = False, + decorators: list[str] | None = None, + docstring: str | None = None, + ) -> list[str]: + lines: list[str] = [] + if decorators is None: + decorators = [] + + for signature in sigs: + # dump decorators, just before "def ..." + for deco in decorators: + lines.append(f"{self._indent}{deco}") + + lines.append( + signature.format_sig( + indent=self._indent, + is_async=is_coroutine, + docstring=docstring if self._include_docstrings else None, + ) + ) + return lines + + def print_annotation( + self, + t: Type, + known_modules: list[str] | None = None, + local_modules: list[str] | None = None, + ) -> str: + printer = AnnotationPrinter(self, known_modules, local_modules) + return t.accept(printer) + + def is_not_in_all(self, name: str) -> bool: + if self.is_private_name(name): + return False + if self._all_: + return self.is_top_level() and name not in self._all_ + return False + + def is_private_name(self, name: str, fullname: str | None = None) -> bool: + if self._include_private: + return False + if fullname in self.EXTRA_EXPORTED: + return False + if name == "_": + return False + return name.startswith("_") and (not name.endswith("__") or name in self.IGNORED_DUNDERS) + + def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> bool: + if ( + not name_is_alias + and self.module_name + and (self.module_name + "." + name) in self.EXTRA_EXPORTED + ): + # Special case certain names that should be exported, against our general rules. + return True + is_private = self.is_private_name(name, full_module + "." + name) + top_level = full_module.split(".")[0] + self_top_level = self.module_name.split(".", 1)[0] + if ( + not name_is_alias + and not self.export_less + and (not self._all_ or name in self.IGNORED_DUNDERS) + and self.module_name + and not is_private + and top_level in (self_top_level, "_" + self_top_level) + ): + # Export imports from the same package, since we can't reliably tell whether they + # are part of the public API. + return True + return False diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 7e30515ac892..ace0b4d95573 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -28,21 +28,19 @@ Options, collect_build_targets, generate_stubs, - get_sig_generators, is_blacklisted_path, is_non_library_module, mypy_options, parse_options, ) -from mypy.stubgenc import ( - generate_c_function_stub, - generate_c_property_stub, - generate_c_type_stub, - infer_method_args, +from mypy.stubgenc import InspectionStubGenerator, infer_c_method_args +from mypy.stubutil import ( + ClassInfo, + common_dir_prefix, infer_method_ret_type, - is_c_property_readonly, + remove_misplaced_type_comments, + walk_packages, ) -from mypy.stubutil import common_dir_prefix, remove_misplaced_type_comments, walk_packages from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_equal, assert_string_arrays_equal, local_sys_path_set @@ -62,7 +60,8 @@ def test_files_found(self) -> None: os.mkdir(os.path.join("subdir", "pack")) self.make_file("subdir", "pack", "__init__.py") opts = parse_options(["subdir"]) - py_mods, c_mods = collect_build_targets(opts, mypy_options(opts)) + py_mods, pyi_mods, c_mods = collect_build_targets(opts, mypy_options(opts)) + assert_equal(pyi_mods, []) assert_equal(c_mods, []) files = {mod.path for mod in py_mods} assert_equal( @@ -87,7 +86,8 @@ def test_packages_found(self) -> None: self.make_file("pack", "a.py") self.make_file("pack", "b.py") opts = parse_options(["-p", "pack"]) - py_mods, c_mods = collect_build_targets(opts, mypy_options(opts)) + py_mods, pyi_mods, c_mods = collect_build_targets(opts, mypy_options(opts)) + assert_equal(pyi_mods, []) assert_equal(c_mods, []) files = {os.path.relpath(mod.path or "FAIL") for mod in py_mods} assert_equal( @@ -111,7 +111,7 @@ def test_module_not_found(self) -> None: os.chdir(tmp) self.make_file(tmp, "mymodule.py", content="import a") opts = parse_options(["-m", "mymodule"]) - py_mods, c_mods = collect_build_targets(opts, mypy_options(opts)) + collect_build_targets(opts, mypy_options(opts)) assert captured_output.getvalue() == "" finally: sys.stdout = sys.__stdout__ @@ -702,10 +702,14 @@ def run_case_inner(self, testcase: DataDrivenTestCase) -> None: out_dir = "out" try: try: - if not testcase.name.endswith("_import"): - options.no_import = True - if not testcase.name.endswith("_semanal"): - options.parse_only = True + if testcase.name.endswith("_inspect"): + options.inspect = True + else: + if not testcase.name.endswith("_import"): + options.no_import = True + if not testcase.name.endswith("_semanal"): + options.parse_only = True + generate_stubs(options) a: list[str] = [] for module in modules: @@ -781,35 +785,28 @@ class StubgencSuite(unittest.TestCase): """ def test_infer_hash_sig(self) -> None: - assert_equal(infer_method_args("__hash__"), [self_arg]) + assert_equal(infer_c_method_args("__hash__"), [self_arg]) assert_equal(infer_method_ret_type("__hash__"), "int") def test_infer_getitem_sig(self) -> None: - assert_equal(infer_method_args("__getitem__"), [self_arg, ArgSig(name="index")]) + assert_equal(infer_c_method_args("__getitem__"), [self_arg, ArgSig(name="index")]) def test_infer_setitem_sig(self) -> None: assert_equal( - infer_method_args("__setitem__"), + infer_c_method_args("__setitem__"), [self_arg, ArgSig(name="index"), ArgSig(name="object")], ) assert_equal(infer_method_ret_type("__setitem__"), "None") + def test_infer_eq_op_sig(self) -> None: + for op in ("eq", "ne", "lt", "le", "gt", "ge"): + assert_equal( + infer_c_method_args(f"__{op}__"), [self_arg, ArgSig(name="other", type="object")] + ) + def test_infer_binary_op_sig(self) -> None: - for op in ( - "eq", - "ne", - "lt", - "le", - "gt", - "ge", - "add", - "radd", - "sub", - "rsub", - "mul", - "rmul", - ): - assert_equal(infer_method_args(f"__{op}__"), [self_arg, ArgSig(name="other")]) + for op in ("add", "radd", "sub", "rsub", "mul", "rmul"): + assert_equal(infer_c_method_args(f"__{op}__"), [self_arg, ArgSig(name="other")]) def test_infer_equality_op_sig(self) -> None: for op in ("eq", "ne", "lt", "le", "gt", "ge", "contains"): @@ -817,46 +814,31 @@ def test_infer_equality_op_sig(self) -> None: def test_infer_unary_op_sig(self) -> None: for op in ("neg", "pos"): - assert_equal(infer_method_args(f"__{op}__"), [self_arg]) + assert_equal(infer_c_method_args(f"__{op}__"), [self_arg]) def test_infer_cast_sig(self) -> None: for op in ("float", "bool", "bytes", "int"): assert_equal(infer_method_ret_type(f"__{op}__"), op) - def test_generate_c_type_stub_no_crash_for_object(self) -> None: + def test_generate_class_stub_no_crash_for_object(self) -> None: output: list[str] = [] mod = ModuleType("module", "") # any module is fine - imports: list[str] = [] - generate_c_type_stub( - mod, - "alias", - object, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) - assert_equal(imports, []) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + + gen.generate_class_stub("alias", object, output) + assert_equal(gen.get_imports().splitlines(), []) assert_equal(output[0], "class alias:") - def test_generate_c_type_stub_variable_type_annotation(self) -> None: + def test_generate_class_stub_variable_type_annotation(self) -> None: # This class mimics the stubgen unit test 'testClassVariable' class TestClassVariableCls: x = 1 output: list[str] = [] - imports: list[str] = [] mod = ModuleType("module", "") # any module is fine - generate_c_type_stub( - mod, - "C", - TestClassVariableCls, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) - assert_equal(imports, []) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClassVariableCls, output) + assert_equal(gen.get_imports().splitlines(), ["from typing import ClassVar"]) assert_equal(output, ["class C:", " x: ClassVar[int] = ..."]) def test_generate_c_type_inheritance(self) -> None: @@ -864,35 +846,19 @@ class TestClass(KeyError): pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType("module, ") - generate_c_type_stub( - mod, - "C", - TestClass, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C(KeyError): ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_inheritance_same_module(self) -> None: output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestBaseClass.__module__, "") - generate_c_type_stub( - mod, - "C", - TestClass, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C(TestBaseClass): ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_inheritance_other_module(self) -> None: import argparse @@ -901,38 +867,22 @@ class TestClass(argparse.Action): pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType("module", "") - generate_c_type_stub( - mod, - "C", - TestClass, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C(argparse.Action): ..."]) - assert_equal(imports, ["import argparse"]) + assert_equal(gen.get_imports().splitlines(), ["import argparse"]) def test_generate_c_type_inheritance_builtin_type(self) -> None: class TestClass(type): pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType("module", "") - generate_c_type_stub( - mod, - "C", - TestClass, - output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C(type): ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_with_docstring(self) -> None: class TestClass: @@ -942,22 +892,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) assert_equal(output, ["def test(self, arg0: int) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_with_docstring_no_self_arg(self) -> None: class TestClass: @@ -967,22 +911,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) assert_equal(output, ["def test(self, arg0: int) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_classmethod(self) -> None: class TestClass: @@ -991,22 +929,16 @@ def test(cls, arg0: str) -> None: pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="cls", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="cls", cls=TestClass, name="TestClass"), ) - assert_equal(output, ["@classmethod", "def test(cls, *args, **kwargs) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(output, ["@classmethod", "def test(cls, *args, **kwargs): ..."]) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_classmethod_with_overloads(self) -> None: class TestClass: @@ -1019,19 +951,13 @@ def test(self, arg0: str) -> None: pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="cls", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="cls", cls=TestClass, name="TestClass"), ) assert_equal( output, @@ -1044,7 +970,7 @@ def test(self, arg0: str) -> None: "def test(cls, arg0: int) -> Any: ...", ], ) - assert_equal(imports, ["from typing import overload"]) + assert_equal(gen.get_imports().splitlines(), ["from typing import overload"]) def test_generate_c_type_with_docstring_empty_default(self) -> None: class TestClass: @@ -1054,22 +980,16 @@ def test(self, arg0: str = "") -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) assert_equal(output, ["def test(self, arg0: str = ...) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_function_other_module_arg(self) -> None: """Test that if argument references type from other module, module will be imported.""" @@ -1082,19 +1002,11 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(self.__module__, "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) assert_equal(output, ["def test(arg0: argparse.Action) -> Any: ..."]) - assert_equal(imports, ["import argparse"]) + assert_equal(gen.get_imports().splitlines(), ["import argparse"]) def test_generate_c_function_same_module(self) -> None: """Test that if annotation references type from same module but using full path, no module @@ -1109,19 +1021,11 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType("argparse", "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) assert_equal(output, ["def test(arg0: Action) -> Action: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_function_other_module(self) -> None: """Test that if annotation references type from other module, module will be imported.""" @@ -1132,19 +1036,11 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(self.__module__, "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) assert_equal(output, ["def test(arg0: argparse.Action) -> argparse.Action: ..."]) - assert_equal(set(imports), {"import argparse"}) + assert_equal(gen.get_imports().splitlines(), ["import argparse"]) def test_generate_c_function_same_module_nested(self) -> None: """Test that if annotation references type from same module but using full path, no module @@ -1159,19 +1055,11 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType("argparse", "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) assert_equal(output, ["def test(arg0: list[Action]) -> list[Action]: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_function_same_module_compound(self) -> None: """Test that if annotation references type from same module but using full path, no module @@ -1186,19 +1074,11 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType("argparse", "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) - assert_equal(output, ["def test(arg0: Union[Action,None]) -> Tuple[Action,None]: ..."]) - assert_equal(imports, []) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) + assert_equal(output, ["def test(arg0: Union[Action, None]) -> Tuple[Action, None]: ..."]) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_function_other_module_nested(self) -> None: """Test that if annotation references type from other module, module will be imported, @@ -1210,19 +1090,13 @@ def test(arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(self.__module__, "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=["foo", "foo.spangle", "bar"], - sig_generators=get_sig_generators(parse_options([])), + gen = InspectionStubGenerator( + mod.__name__, known_modules=["foo", "foo.spangle", "bar"], module=mod ) + gen.generate_function_stub("test", test, output=output) assert_equal(output, ["def test(arg0: foo.bar.Action) -> other.Thing: ..."]) - assert_equal(set(imports), {"import foo", "import other"}) + assert_equal(gen.get_imports().splitlines(), ["import foo", "import other"]) def test_generate_c_function_no_crash_for_non_str_docstring(self) -> None: def test(arg0: str) -> None: @@ -1231,19 +1105,11 @@ def test(arg0: str) -> None: test.__doc__ = property(lambda self: "test(arg0: str) -> None") # type: ignore[assignment] output: list[str] = [] - imports: list[str] = [] mod = ModuleType(self.__module__, "") - generate_c_function_stub( - mod, - "test", - test, - output=output, - imports=imports, - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), - ) - assert_equal(output, ["def test(*args, **kwargs) -> Any: ..."]) - assert_equal(imports, []) + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub("test", test, output=output) + assert_equal(output, ["def test(*args, **kwargs): ..."]) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_property_with_pybind11(self) -> None: """Signatures included by PyBind11 inside property.fget are read.""" @@ -1258,13 +1124,15 @@ def get_attribute(self) -> None: readwrite_properties: list[str] = [] readonly_properties: list[str] = [] - generate_c_property_stub( + mod = ModuleType("module", "") # any module is fine + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_property_stub( "attribute", + TestClass.__dict__["attribute"], TestClass.attribute, [], readwrite_properties, readonly_properties, - is_c_property_readonly(TestClass.attribute), ) assert_equal(readwrite_properties, []) assert_equal(readonly_properties, ["@property", "def attribute(self) -> str: ..."]) @@ -1284,15 +1152,17 @@ def attribute(self, value: int) -> None: readwrite_properties: list[str] = [] readonly_properties: list[str] = [] - generate_c_property_stub( + mod = ModuleType("module", "") # any module is fine + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_property_stub( "attribute", - type(TestClass.attribute), + TestClass.__dict__["attribute"], + TestClass.attribute, [], readwrite_properties, readonly_properties, - is_c_property_readonly(TestClass.attribute), ) - assert_equal(readwrite_properties, ["attribute: Any"]) + assert_equal(readwrite_properties, ["attribute: Incomplete"]) assert_equal(readonly_properties, []) def test_generate_c_type_with_single_arg_generic(self) -> None: @@ -1303,22 +1173,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) assert_equal(output, ["def test(self, arg0: List[int]) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_with_double_arg_generic(self) -> None: class TestClass: @@ -1328,22 +1192,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) - assert_equal(output, ["def test(self, arg0: Dict[str,int]) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(output, ["def test(self, arg0: Dict[str, int]) -> Any: ..."]) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_with_nested_generic(self) -> None: class TestClass: @@ -1353,22 +1211,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) - assert_equal(output, ["def test(self, arg0: Dict[str,List[int]]) -> Any: ..."]) - assert_equal(imports, []) + assert_equal(output, ["def test(self, arg0: Dict[str, List[int]]) -> Any: ..."]) + assert_equal(gen.get_imports().splitlines(), []) def test_generate_c_type_with_generic_using_other_module_first(self) -> None: class TestClass: @@ -1378,22 +1230,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) - assert_equal(output, ["def test(self, arg0: Dict[argparse.Action,int]) -> Any: ..."]) - assert_equal(imports, ["import argparse"]) + assert_equal(output, ["def test(self, arg0: Dict[argparse.Action, int]) -> Any: ..."]) + assert_equal(gen.get_imports().splitlines(), ["import argparse"]) def test_generate_c_type_with_generic_using_other_module_last(self) -> None: class TestClass: @@ -1403,22 +1249,16 @@ def test(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "test", TestClass.test, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) - assert_equal(output, ["def test(self, arg0: Dict[str,argparse.Action]) -> Any: ..."]) - assert_equal(imports, ["import argparse"]) + assert_equal(output, ["def test(self, arg0: Dict[str, argparse.Action]) -> Any: ..."]) + assert_equal(gen.get_imports().splitlines(), ["import argparse"]) def test_generate_c_type_with_overload_pybind11(self) -> None: class TestClass: @@ -1433,19 +1273,13 @@ def __init__(self, arg0: str) -> None: """ output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "__init__", TestClass.__init__, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), ) assert_equal( output, @@ -1458,7 +1292,7 @@ def __init__(self, arg0: str) -> None: "def __init__(self, *args, **kwargs) -> Any: ...", ], ) - assert_equal(set(imports), {"from typing import overload"}) + assert_equal(gen.get_imports().splitlines(), ["from typing import overload"]) def test_generate_c_type_with_overload_shiboken(self) -> None: class TestClass: @@ -1471,19 +1305,18 @@ def __init__(self, arg0: str) -> None: pass output: list[str] = [] - imports: list[str] = [] mod = ModuleType(TestClass.__module__, "") - generate_c_function_stub( - mod, + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( "__init__", TestClass.__init__, output=output, - imports=imports, - self_var="self", - cls=TestClass, - class_name="TestClass", - known_modules=[mod.__name__], - sig_generators=get_sig_generators(parse_options([])), + class_info=ClassInfo( + self_var="self", + cls=TestClass, + name="TestClass", + docstring=getattr(TestClass, "__doc__", None), + ), ) assert_equal( output, @@ -1494,7 +1327,7 @@ def __init__(self, arg0: str) -> None: "def __init__(self, arg0: str, arg1: str) -> None: ...", ], ) - assert_equal(set(imports), {"from typing import overload"}) + assert_equal(gen.get_imports().splitlines(), ["from typing import overload"]) class ArgSigSuite(unittest.TestCase): diff --git a/mypy/traverser.py b/mypy/traverser.py index 2fcc376cfb7c..d11dd395f978 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -2,7 +2,7 @@ from __future__ import annotations -from mypy_extensions import mypyc_attr +from mypy_extensions import mypyc_attr, trait from mypy.nodes import ( REVEAL_TYPE, @@ -94,6 +94,7 @@ from mypy.visitor import NodeVisitor +@trait @mypyc_attr(allow_interpreted_subclasses=True) class TraverserVisitor(NodeVisitor[None]): """A parse tree visitor that traverses the parse tree during visiting. diff --git a/setup.py b/setup.py index dcbdc96b3ccf..e3ebe9dd62ec 100644 --- a/setup.py +++ b/setup.py @@ -112,7 +112,6 @@ def run(self): "stubtest.py", "stubgenc.py", "stubdoc.py", - "stubutil.py", ) ) + ( # Don't want to grab this accidentally diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi index e69de29bb2d1..0cb252f00259 100644 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi +++ b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi @@ -0,0 +1 @@ +from . import basics as basics diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi index ab5a4f4e78d2..6527f5733eaf 100644 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi @@ -1,7 +1,7 @@ -from typing import ClassVar +from typing import ClassVar, overload -from typing import overload PI: float +__version__: str class Point: class AngleUnit: @@ -11,12 +11,10 @@ class Point: radian: ClassVar[Point.AngleUnit] = ... def __init__(self, value: int) -> None: ... def __eq__(self, other: object) -> bool: ... - def __getstate__(self) -> int: ... def __hash__(self) -> int: ... def __index__(self) -> int: ... def __int__(self) -> int: ... def __ne__(self, other: object) -> bool: ... - def __setstate__(self, state: int) -> None: ... @property def name(self) -> str: ... @property @@ -30,12 +28,10 @@ class Point: pixel: ClassVar[Point.LengthUnit] = ... def __init__(self, value: int) -> None: ... def __eq__(self, other: object) -> bool: ... - def __getstate__(self) -> int: ... def __hash__(self) -> int: ... def __index__(self) -> int: ... def __int__(self) -> int: ... def __ne__(self, other: object) -> bool: ... - def __setstate__(self, state: int) -> None: ... @property def name(self) -> str: ... @property diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 23dbf36a551b..d83d74306230 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -11,6 +11,11 @@ def f() -> None: ... [case testTwoFunctions] def f(a, b): + """ + this is a docstring + + more. + """ x = 1 def g(arg): pass @@ -37,11 +42,21 @@ def f(x=True, y=False): ... [out] def f(x: bool = ..., y: bool = ...) -> None: ... +[case testDefaultArgBool_inspect] +def f(x=True, y=False): ... +[out] +def f(x: bool = ..., y: bool = ...): ... + [case testDefaultArgStr] def f(x='foo'): ... [out] def f(x: str = ...) -> None: ... +[case testDefaultArgStr_inspect] +def f(x='foo'): ... +[out] +def f(x: str = ...): ... + [case testDefaultArgBytes] def f(x=b'foo'): ... [out] @@ -300,6 +315,7 @@ __all__ = [] __author__ = '' __version__ = '' [out] +__version__: str [case testBaseClass] class A: ... @@ -361,6 +377,24 @@ class A: def f(self, x) -> None: ... def h(self) -> None: ... +-- a read/write property is treated the same as an attribute +[case testProperty_inspect] +class A: + @property + def f(self): + return 1 + @f.setter + def f(self, x): ... + + def h(self): + self.f = 1 +[out] +from _typeshed import Incomplete + +class A: + f: Incomplete + def h(self): ... + [case testFunctoolsCachedProperty] import functools @@ -435,6 +469,15 @@ class A: @classmethod def f(cls) -> None: ... +[case testClassMethod_inspect] +class A: + @classmethod + def f(cls): ... +[out] +class A: + @classmethod + def f(cls): ... + [case testIfMainCheck] def a(): ... if __name__ == '__main__': @@ -472,6 +515,23 @@ class B: ... class C: def f(self) -> None: ... +[case testNoSpacesBetweenEmptyClasses_inspect] +class X: + def g(self): ... +class A: ... +class B: ... +class C: + def f(self): ... +[out] +class X: + def g(self): ... + +class A: ... +class B: ... + +class C: + def f(self): ... + [case testExceptionBaseClasses] class A(Exception): ... class B(ValueError): ... @@ -490,6 +550,17 @@ class A: class A: def __eq__(self): ... +[case testOmitSomeSpecialMethods_inspect] +class A: + def __str__(self): ... + def __repr__(self): ... + def __eq__(self): ... + def __getstate__(self): ... + def __setstate__(self, state): ... +[out] +class A: + def __eq__(self) -> bool: ... + -- Tests that will perform runtime imports of modules. -- Don't use `_import` suffix if there are unquoted forward references. @@ -507,6 +578,13 @@ def g(): ... [out] def f() -> None: ... +[case testOmitDefsNotInAll_inspect] +__all__ = [] + ['f'] +def f(): ... +def g(): ... +[out] +def f(): ... + [case testVarDefsNotInAll_import] __all__ = [] + ['f', 'g'] def f(): ... @@ -517,6 +595,16 @@ def g(): ... def f() -> None: ... def g() -> None: ... +[case testVarDefsNotInAll_inspect] +__all__ = [] + ['f', 'g'] +def f(): ... +x = 1 +y = 1 +def g(): ... +[out] +def f(): ... +def g(): ... + [case testIncludeClassNotInAll_import] __all__ = [] + ['f'] def f(): ... @@ -526,6 +614,15 @@ def f() -> None: ... class A: ... +[case testIncludeClassNotInAll_inspect] +__all__ = [] + ['f'] +def f(): ... +class A: ... +[out] +def f(): ... + +class A: ... + [case testAllAndClass_import] __all__ = ['A'] class A: @@ -636,6 +733,23 @@ class C: # Names in __all__ with no definition: # g +[case testCommentForUndefinedName_inspect] +__all__ = ['f', 'x', 'C', 'g'] +def f(): ... +x = 1 +class C: + def g(self): ... +[out] +def f(): ... + +x: int + +class C: + def g(self): ... + +# Names in __all__ with no definition: +# g + [case testIgnoreSlots] class A: __slots__ = () @@ -649,6 +763,13 @@ class A: [out] class A: ... +[case testSkipPrivateProperty_inspect] +class A: + @property + def _foo(self): ... +[out] +class A: ... + [case testIncludePrivateProperty] # flags: --include-private class A: @@ -659,6 +780,16 @@ class A: @property def _foo(self) -> None: ... +[case testIncludePrivateProperty_inspect] +# flags: --include-private +class A: + @property + def _foo(self): ... +[out] +class A: + @property + def _foo(self): ... + [case testSkipPrivateStaticAndClassMethod] class A: @staticmethod @@ -668,6 +799,15 @@ class A: [out] class A: ... +[case testSkipPrivateStaticAndClassMethod_inspect] +class A: + @staticmethod + def _foo(): ... + @classmethod + def _bar(cls): ... +[out] +class A: ... + [case testIncludePrivateStaticAndClassMethod] # flags: --include-private class A: @@ -682,6 +822,20 @@ class A: @classmethod def _bar(cls) -> None: ... +[case testIncludePrivateStaticAndClassMethod_inspect] +# flags: --include-private +class A: + @staticmethod + def _foo(): ... + @classmethod + def _bar(cls): ... +[out] +class A: + @staticmethod + def _foo(): ... + @classmethod + def _bar(cls): ... + [case testNamedtuple] import collections, typing, x X = collections.namedtuple('X', ['a', 'b']) @@ -1801,6 +1955,19 @@ class Outer: class Inner: ... A = Outer.Inner +-- needs improvement +[case testNestedClass_inspect] +class Outer: + class Inner: + pass + +A = Outer.Inner +[out] +class Outer: + class Inner: ... + +class A: ... + [case testFunctionAlias_semanal] from asyncio import coroutine @@ -2034,6 +2201,25 @@ class A: def f(x) -> None: ... def g(x, y: str): ... +class A: + def f(self, x) -> None: ... + +-- Same as above +[case testFunctionPartiallyAnnotated_inspect] +def f(x) -> None: + pass + +def g(x, y: str): + pass + +class A: + def f(self, x) -> None: + pass + +[out] +def f(x) -> None: ... +def g(x, y: str): ... + class A: def f(self, x) -> None: ... @@ -2054,6 +2240,24 @@ def f(x: Any): ... def g(x, y: Any) -> str: ... def h(x: Any) -> str: ... +-- Same as above +[case testExplicitAnyArg_inspect] +from typing import Any + +def f(x: Any): + pass +def g(x, y: Any) -> str: + pass +def h(x: Any) -> str: + pass + +[out] +from typing import Any + +def f(x: Any): ... +def g(x, y: Any) -> str: ... +def h(x: Any) -> str: ... + [case testExplicitReturnedAny] from typing import Any @@ -2385,6 +2589,28 @@ def g() -> None: ... +[case testTestFiles_inspect] +# modules: p p.x p.tests p.tests.test_foo + +[file p/__init__.py] +def f(): pass + +[file p/x.py] +def g(): pass + +[file p/tests/__init__.py] + +[file p/tests/test_foo.py] +def test_thing(): pass + +[out] +# p/__init__.pyi +def f(): ... +# p/x.pyi +def g(): ... + + + [case testVerboseFlag] # Just test that --verbose does not break anything in a basic test case. # flags: --verbose @@ -2686,6 +2912,8 @@ __uri__ = '' __version__ = '' [out] +from m import __version__ as __version__ + class A: ... [case testHideDunderModuleAttributesWithAll_import] @@ -2715,6 +2943,7 @@ __uri__ = '' __version__ = '' [out] +from m import __version__ as __version__ [case testAttrsClass_semanal] import attrs @@ -2949,7 +3178,6 @@ class A: @overload def f(self, x: Tuple[int, int]) -> int: ... - @overload def f(x: int, y: int) -> int: ... @overload @@ -2993,7 +3221,6 @@ class A: @overload def f(self, x: Tuple[int, int]) -> int: ... - @overload def f(x: int, y: int) -> int: ... @overload @@ -3068,7 +3295,6 @@ class A: @classmethod def g(cls, x: typing.Tuple[int, int]) -> int: ... - @typing.overload def f(x: int, y: int) -> int: ... @typing.overload @@ -3147,7 +3373,6 @@ class A: @classmethod def g(cls, x: t.Tuple[int, int]) -> int: ... - @t.overload def f(x: int, y: int) -> int: ... @t.overload @@ -3345,6 +3570,67 @@ class Some: def __float__(self) -> float: ... def __index__(self) -> int: ... +-- Same as above +[case testKnownMagicMethodsReturnTypes_inspect] +class Some: + def __len__(self): ... + def __length_hint__(self): ... + def __init__(self): ... + def __del__(self): ... + def __bool__(self): ... + def __bytes__(self): ... + def __format__(self, spec): ... + def __contains__(self, obj): ... + def __complex__(self): ... + def __int__(self): ... + def __float__(self): ... + def __index__(self): ... +[out] +class Some: + def __len__(self) -> int: ... + def __length_hint__(self) -> int: ... + def __init__(self) -> None: ... + def __del__(self) -> None: ... + def __bool__(self) -> bool: ... + def __bytes__(self) -> bytes: ... + def __format__(self, spec) -> str: ... + def __contains__(self, obj) -> bool: ... + def __complex__(self) -> complex: ... + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __index__(self) -> int: ... + + +[case testKnownMagicMethodsArgTypes] +class MismatchNames: + def __exit__(self, tp, val, tb): ... + +class MatchNames: + def __exit__(self, type, value, traceback): ... + +[out] +class MismatchNames: + def __exit__(self, tp: type[BaseException] | None, val: BaseException | None, tb: types.TracebackType | None) -> None: ... + +class MatchNames: + def __exit__(self, type: type[BaseException] | None, value: BaseException | None, traceback: types.TracebackType | None) -> None: ... + +-- Same as above (but can generate import statements) +[case testKnownMagicMethodsArgTypes_inspect] +class MismatchNames: + def __exit__(self, tp, val, tb): ... + +class MatchNames: + def __exit__(self, type, value, traceback): ... + +[out] +import types + +class MismatchNames: + def __exit__(self, tp: type[BaseException] | None, val: BaseException | None, tb: types.TracebackType | None): ... + +class MatchNames: + def __exit__(self, type: type[BaseException] | None, value: BaseException | None, traceback: types.TracebackType | None): ... [case testTypeVarPEP604Bound] from typing import TypeVar @@ -3397,7 +3683,7 @@ from typing import TypedDict X = TypedDict('X', a=int, b=str) Y = TypedDict('X', a=int, b=str, total=False) [out] -from typing import TypedDict +from typing_extensions import TypedDict class X(TypedDict): a: int From 2bcec24635670bcff6efab3d21641f39f0f35857 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:37:23 +0100 Subject: [PATCH 0317/1617] Stream dmypy output instead of dumping everything at the end (#16252) This does 2 things: 1. It changes the IPC code to work with multiple messages. 2. It changes the dmypy client/server communication so that it streams stdout/stderr instead of dumping everything at the end. For 1, we have to provide a way to separate out different messages. I chose to frame messages as bytes separated by whitespace character. That means we have to encode the message in a scheme that escapes whitespace. The `codecs.encode(, 'base64')` seems reasonable. It encodes more than needed but the application is not IPC IO limited so it should be fine. With this convention in place, all we have to do is read from the socket stream until we have a whitespace character. The framing logic can be easily changed. For 2, since we communicate with JSONs, it's easy to add a "finished" key that tells us it's the final response from dmypy. Anything else is just stdout/stderr output. Note: dmypy server also returns out/err which is the output of actual mypy type checking. Right now this change does not stream that output. We can stream that in a followup change. We just have to decide on how to differenciate the 4 text streams (stdout/stderr/out/err) that will now be interleaved. The WriteToConn class could use more love. I just put a bare minimum. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/dmypy/client.py | 35 +++++++++++------------ mypy/dmypy_server.py | 20 ++++++-------- mypy/dmypy_util.py | 33 ++++++++++++++++++++-- mypy/ipc.py | 66 ++++++++++++++++++++++++++++++++++++-------- mypy/test/testipc.py | 52 ++++++++++++++++++++++++++++------ 5 files changed, 155 insertions(+), 51 deletions(-) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index c3a2308d1b44..229740e44db0 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -17,7 +17,7 @@ from typing import Any, Callable, Mapping, NoReturn from mypy.dmypy_os import alive, kill -from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive +from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send from mypy.ipc import IPCClient, IPCException from mypy.util import check_python_version, get_terminal_width, should_force_color from mypy.version import __version__ @@ -659,28 +659,29 @@ def request( # so that it can format the type checking output accordingly. args["is_tty"] = sys.stdout.isatty() or should_force_color() args["terminal_width"] = get_terminal_width() - bdata = json.dumps(args).encode("utf8") _, name = get_status(status_file) try: with IPCClient(name, timeout) as client: - client.write(bdata) - response = receive(client) + send(client, args) + + final = False + while not final: + response = receive(client) + final = bool(response.pop("final", False)) + # Display debugging output written to stdout/stderr in the server process for convenience. + # This should not be confused with "out" and "err" fields in the response. + # Those fields hold the output of the "check" command, and are handled in check_output(). + stdout = response.pop("stdout", None) + if stdout: + sys.stdout.write(stdout) + stderr = response.pop("stderr", None) + if stderr: + sys.stderr.write(stderr) except (OSError, IPCException) as err: return {"error": str(err)} # TODO: Other errors, e.g. ValueError, UnicodeError - else: - # Display debugging output written to stdout/stderr in the server process for convenience. - # This should not be confused with "out" and "err" fields in the response. - # Those fields hold the output of the "check" command, and are handled in check_output(). - stdout = response.get("stdout") - if stdout: - sys.stdout.write(stdout) - stderr = response.get("stderr") - if stderr: - print("-" * 79) - print("stderr:") - sys.stdout.write(stderr) - return response + + return response def get_status(status_file: str) -> tuple[int, str]: diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index faa9a23fadfb..9cc0888fc208 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -23,7 +23,7 @@ import mypy.build import mypy.errors import mypy.main -from mypy.dmypy_util import receive +from mypy.dmypy_util import WriteToConn, receive, send from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache from mypy.fswatcher import FileData, FileSystemWatcher @@ -208,10 +208,12 @@ def _response_metadata(self) -> dict[str, str]: def serve(self) -> None: """Serve requests, synchronously (no thread or fork).""" + command = None server = IPCServer(CONNECTION_NAME, self.timeout) orig_stdout = sys.stdout orig_stderr = sys.stderr + try: with open(self.status_file, "w") as f: json.dump({"pid": os.getpid(), "connection_name": server.connection_name}, f) @@ -219,10 +221,8 @@ def serve(self) -> None: while True: with server: data = receive(server) - debug_stdout = io.StringIO() - debug_stderr = io.StringIO() - sys.stdout = debug_stdout - sys.stderr = debug_stderr + sys.stdout = WriteToConn(server, "stdout") # type: ignore[assignment] + sys.stderr = WriteToConn(server, "stderr") # type: ignore[assignment] resp: dict[str, Any] = {} if "command" not in data: resp = {"error": "No command found in request"} @@ -239,15 +239,13 @@ def serve(self) -> None: tb = traceback.format_exception(*sys.exc_info()) resp = {"error": "Daemon crashed!\n" + "".join(tb)} resp.update(self._response_metadata()) - resp["stdout"] = debug_stdout.getvalue() - resp["stderr"] = debug_stderr.getvalue() - server.write(json.dumps(resp).encode("utf8")) + resp["final"] = True + send(server, resp) raise - resp["stdout"] = debug_stdout.getvalue() - resp["stderr"] = debug_stderr.getvalue() + resp["final"] = True try: resp.update(self._response_metadata()) - server.write(json.dumps(resp).encode("utf8")) + send(server, resp) except OSError: pass # Maybe the client hung up if command == "stop": diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 2aae41d998da..d95cba9f40b5 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -6,7 +6,7 @@ from __future__ import annotations import json -from typing import Any, Final +from typing import Any, Final, Iterable from mypy.ipc import IPCBase @@ -14,7 +14,7 @@ def receive(connection: IPCBase) -> Any: - """Receive JSON data from a connection until EOF. + """Receive single JSON data frame from a connection. Raise OSError if the data received is not valid JSON or if it is not a dict. @@ -23,9 +23,36 @@ def receive(connection: IPCBase) -> Any: if not bdata: raise OSError("No data received") try: - data = json.loads(bdata.decode("utf8")) + data = json.loads(bdata) except Exception as e: raise OSError("Data received is not valid JSON") from e if not isinstance(data, dict): raise OSError(f"Data received is not a dict ({type(data)})") return data + + +def send(connection: IPCBase, data: Any) -> None: + """Send data to a connection encoded and framed. + + The data must be JSON-serializable. We assume that a single send call is a + single frame to be sent on the connect. + """ + connection.write(json.dumps(data)) + + +class WriteToConn: + """Helper class to write to a connection instead of standard output.""" + + def __init__(self, server: IPCBase, output_key: str = "stdout"): + self.server = server + self.output_key = output_key + + def write(self, output: str) -> int: + resp: dict[str, Any] = {} + resp[self.output_key] = output + send(self.server, resp) + return len(output) + + def writelines(self, lines: Iterable[str]) -> None: + for s in lines: + self.write(s) diff --git a/mypy/ipc.py b/mypy/ipc.py index d026f2429a0f..ab01f1b79e7d 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -7,6 +7,7 @@ from __future__ import annotations import base64 +import codecs import os import shutil import sys @@ -40,6 +41,10 @@ class IPCBase: This contains logic shared between the client and server, such as reading and writing. + We want to be able to send multiple "messages" over a single connection and + to be able to separate the messages. We do this by encoding the messages + in an alphabet that does not contain spaces, then adding a space for + separation. The last framed message is also followed by a space. """ connection: _IPCHandle @@ -47,12 +52,30 @@ class IPCBase: def __init__(self, name: str, timeout: float | None) -> None: self.name = name self.timeout = timeout + self.buffer = bytearray() - def read(self, size: int = 100000) -> bytes: - """Read bytes from an IPC connection until its empty.""" - bdata = bytearray() + def frame_from_buffer(self) -> bytearray | None: + """Return a full frame from the bytes we have in the buffer.""" + space_pos = self.buffer.find(b" ") + if space_pos == -1: + return None + # We have a full frame + bdata = self.buffer[:space_pos] + self.buffer = self.buffer[space_pos + 1 :] + return bdata + + def read(self, size: int = 100000) -> str: + """Read bytes from an IPC connection until we have a full frame.""" + bdata: bytearray | None = bytearray() if sys.platform == "win32": while True: + # Check if we already have a message in the buffer before + # receiving any more data from the socket. + bdata = self.frame_from_buffer() + if bdata is not None: + break + + # Receive more data into the buffer. ov, err = _winapi.ReadFile(self.connection, size, overlapped=True) try: if err == _winapi.ERROR_IO_PENDING: @@ -66,7 +89,10 @@ def read(self, size: int = 100000) -> bytes: _, err = ov.GetOverlappedResult(True) more = ov.getbuffer() if more: - bdata.extend(more) + self.buffer.extend(more) + bdata = self.frame_from_buffer() + if bdata is not None: + break if err == 0: # we are done! break @@ -77,17 +103,34 @@ def read(self, size: int = 100000) -> bytes: raise IPCException("ReadFile operation aborted.") else: while True: + # Check if we already have a message in the buffer before + # receiving any more data from the socket. + bdata = self.frame_from_buffer() + if bdata is not None: + break + + # Receive more data into the buffer. more = self.connection.recv(size) if not more: + # Connection closed break - bdata.extend(more) - return bytes(bdata) + self.buffer.extend(more) + + if not bdata: + # Socket was empty and we didn't get any frame. + # This should only happen if the socket was closed. + return "" + return codecs.decode(bdata, "base64").decode("utf8") + + def write(self, data: str) -> None: + """Write to an IPC connection.""" + + # Frame the data by urlencoding it and separating by space. + encoded_data = codecs.encode(data.encode("utf8"), "base64") + b" " - def write(self, data: bytes) -> None: - """Write bytes to an IPC connection.""" if sys.platform == "win32": try: - ov, err = _winapi.WriteFile(self.connection, data, overlapped=True) + ov, err = _winapi.WriteFile(self.connection, encoded_data, overlapped=True) try: if err == _winapi.ERROR_IO_PENDING: timeout = int(self.timeout * 1000) if self.timeout else _winapi.INFINITE @@ -101,12 +144,11 @@ def write(self, data: bytes) -> None: raise bytes_written, err = ov.GetOverlappedResult(True) assert err == 0, err - assert bytes_written == len(data) + assert bytes_written == len(encoded_data) except OSError as e: raise IPCException(f"Failed to write with error: {e.winerror}") from e else: - self.connection.sendall(data) - self.connection.shutdown(socket.SHUT_WR) + self.connection.sendall(encoded_data) def close(self) -> None: if sys.platform == "win32": diff --git a/mypy/test/testipc.py b/mypy/test/testipc.py index 9034f514bb45..8ef656dc4579 100644 --- a/mypy/test/testipc.py +++ b/mypy/test/testipc.py @@ -15,14 +15,25 @@ def server(msg: str, q: Queue[str]) -> None: server = IPCServer(CONNECTION_NAME) q.put(server.connection_name) - data = b"" + data = "" while not data: with server: - server.write(msg.encode()) + server.write(msg) data = server.read() server.cleanup() +def server_multi_message_echo(q: Queue[str]) -> None: + server = IPCServer(CONNECTION_NAME) + q.put(server.connection_name) + data = "" + with server: + while data != "quit": + data = server.read() + server.write(data) + server.cleanup() + + class IPCTests(TestCase): def test_transaction_large(self) -> None: queue: Queue[str] = Queue() @@ -31,8 +42,8 @@ def test_transaction_large(self) -> None: p.start() connection_name = queue.get() with IPCClient(connection_name, timeout=1) as client: - assert client.read() == msg.encode() - client.write(b"test") + assert client.read() == msg + client.write("test") queue.close() queue.join_thread() p.join() @@ -44,12 +55,37 @@ def test_connect_twice(self) -> None: p.start() connection_name = queue.get() with IPCClient(connection_name, timeout=1) as client: - assert client.read() == msg.encode() - client.write(b"") # don't let the server hang up yet, we want to connect again. + assert client.read() == msg + client.write("") # don't let the server hang up yet, we want to connect again. with IPCClient(connection_name, timeout=1) as client: - assert client.read() == msg.encode() - client.write(b"test") + assert client.read() == msg + client.write("test") + queue.close() + queue.join_thread() + p.join() + assert p.exitcode == 0 + + def test_multiple_messages(self) -> None: + queue: Queue[str] = Queue() + p = Process(target=server_multi_message_echo, args=(queue,), daemon=True) + p.start() + connection_name = queue.get() + with IPCClient(connection_name, timeout=1) as client: + # "foo bar" with extra accents on letters. + # In UTF-8 encoding so we don't confuse editors opening this file. + fancy_text = b"f\xcc\xb6o\xcc\xb2\xf0\x9d\x91\x9c \xd0\xb2\xe2\xb7\xa1a\xcc\xb6r\xcc\x93\xcd\x98\xcd\x8c" + client.write(fancy_text.decode("utf-8")) + assert client.read() == fancy_text.decode("utf-8") + + client.write("Test with spaces") + client.write("Test write before reading previous") + time.sleep(0) # yield to the server to force reading of all messages by server. + assert client.read() == "Test with spaces" + assert client.read() == "Test write before reading previous" + + client.write("quit") + assert client.read() == "quit" queue.close() queue.join_thread() p.join() From 85f40b5c8479cbca1d30f912fb95aa243b09c334 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 16 Oct 2023 21:18:55 +0100 Subject: [PATCH 0318/1617] Correctly handle variadic instances with empty arguments (#16238) Fixes https://github.com/python/mypy/issues/16199 It was surprisingly hard to fix, because all possible fixes strongly interfered with the code that makes "no-args" aliases possible: ```python l = list x: l[int] # OK, same as list[int] ``` So after all I re-organized (and actually simplified) that old code. --- mypy/checkexpr.py | 5 ++- mypy/expandtype.py | 2 +- mypy/messages.py | 6 ++- mypy/semanal.py | 28 +++++++++---- mypy/subtypes.py | 4 +- mypy/typeanal.py | 54 +++++++------------------ mypy/types.py | 2 + test-data/unit/check-flags.test | 19 +++++++++ test-data/unit/check-typevar-tuple.test | 49 +++++++++++++++++++++- 9 files changed, 116 insertions(+), 53 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a1dd6d830758..a5c8c80e1580 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4662,7 +4662,10 @@ class C(Generic[T, Unpack[Ts]]): ... info = t.type_object() # We reuse the logic from semanal phase to reduce code duplication. fake = Instance(info, args, line=ctx.line, column=ctx.column) - if not validate_instance(fake, self.chk.fail): + # This code can be only called either from checking a type application, or from + # checking a type alias (after the caller handles no_args aliases), so we know it + # was initially an IndexExpr, and we allow empty tuple type arguments. + if not validate_instance(fake, self.chk.fail, empty_tuple_index=True): fix_instance( fake, self.chk.fail, self.chk.note, disallow_any=False, options=self.chk.options ) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 44716e6da013..cb09a1ee99f5 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -89,7 +89,7 @@ def expand_type_by_instance(typ: Type, instance: Instance) -> Type: def expand_type_by_instance(typ: Type, instance: Instance) -> Type: """Substitute type variables in type using values from an Instance. Type variables are considered to be bound by the class declaration.""" - if not instance.args: + if not instance.args and not instance.type.has_type_var_tuple_type: return typ else: variables: dict[TypeVarId, Type] = {} diff --git a/mypy/messages.py b/mypy/messages.py index 5d03bf1babb9..dc5056f616ea 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2505,8 +2505,10 @@ def format_literal_value(typ: LiteralType) -> str: else: base_str = itype.type.name if not itype.args: - # No type arguments, just return the type name - return base_str + if not itype.type.has_type_var_tuple_type: + # No type arguments, just return the type name + return base_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}, ...]" diff --git a/mypy/semanal.py b/mypy/semanal.py index a476b62b31ec..1111b1df50e9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -231,10 +231,11 @@ check_for_explicit_any, detect_diverging_alias, find_self_type, - fix_instance_types, + fix_instance, has_any_from_unimported_type, no_subscript_builtin_alias, type_constructors, + validate_instance, ) from mypy.typeops import function_type, get_type_vars, try_getting_str_literals_from_type from mypy.types import ( @@ -722,7 +723,9 @@ 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) + fix_instance( + target, self.fail, self.note, disallow_any=False, options=self.options + ) alias_node = TypeAlias( target, alias, @@ -3455,7 +3458,7 @@ def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Typ def analyze_alias( self, name: str, rvalue: Expression, allow_placeholder: bool = False - ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str]]: + ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str], bool]: """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable). If yes, return the corresponding type, a list of @@ -3474,7 +3477,7 @@ def analyze_alias( self.fail( "Invalid type alias: expression is not a valid type", rvalue, code=codes.VALID_TYPE ) - return None, [], set(), [] + return None, [], set(), [], False found_type_vars = typ.accept(TypeVarLikeQuery(self, self.tvar_scope)) tvar_defs: list[TypeVarLikeType] = [] @@ -3508,7 +3511,8 @@ def analyze_alias( new_tvar_defs.append(td) qualified_tvars = [node.fullname for _name, node in found_type_vars] - return analyzed, new_tvar_defs, depends_on, qualified_tvars + empty_tuple_index = typ.empty_tuple_index if isinstance(typ, UnboundType) else False + return analyzed, new_tvar_defs, depends_on, qualified_tvars, empty_tuple_index def is_pep_613(self, s: AssignmentStmt) -> bool: if s.unanalyzed_type is not None and isinstance(s.unanalyzed_type, UnboundType): @@ -3591,9 +3595,10 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: alias_tvars: list[TypeVarLikeType] = [] depends_on: set[str] = set() qualified_tvars: list[str] = [] + empty_tuple_index = False else: tag = self.track_incomplete_refs() - res, alias_tvars, depends_on, qualified_tvars = self.analyze_alias( + res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( lvalue.name, rvalue, allow_placeholder=True ) if not res: @@ -3626,8 +3631,15 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # Note: with the new (lazy) type alias representation we only need to set no_args to True # 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) + no_args = ( + isinstance(res, ProperType) + and isinstance(res, Instance) + and not res.args + and not empty_tuple_index + ) + if isinstance(res, ProperType) and isinstance(res, Instance): + if not validate_instance(res, self.fail, empty_tuple_index): + fix_instance(res, self.fail, self.note, disallow_any=False, options=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/subtypes.py b/mypy/subtypes.py index 822c4b0ebf32..638553883dd8 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -544,7 +544,7 @@ def visit_instance(self, left: Instance) -> bool: right_args = ( right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix ) - if not self.proper_subtype: + if not self.proper_subtype and t.args: for arg in map(get_proper_type, t.args): if isinstance(arg, UnpackType): unpacked = get_proper_type(arg.type) @@ -557,6 +557,8 @@ def visit_instance(self, left: Instance) -> bool: break else: return True + if len(left_args) != len(right_args): + return False type_params = zip(left_args, right_args, right.type.defn.type_vars) else: type_params = zip(t.args, right.args, right.type.defn.type_vars) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 385c5d35d67f..4743126c3d56 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -62,6 +62,7 @@ ParamSpecType, PartialType, PlaceholderType, + ProperType, RawExpressionType, RequiredType, SyntheticTypeVisitor, @@ -89,7 +90,6 @@ has_type_vars, ) from mypy.types_utils import is_bad_type_type_item -from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars T = TypeVar("T") @@ -425,9 +425,10 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) # The only case where instantiate_type_alias() can return an incorrect instance is # when it is top-level instance, so no need to recurse. if ( - isinstance(res, Instance) # type: ignore[misc] - and not self.defining_alias - and not validate_instance(res, self.fail) + isinstance(res, ProperType) + and isinstance(res, Instance) + and not (self.defining_alias and self.nesting_level == 0) + and not validate_instance(res, self.fail, t.empty_tuple_index) ): fix_instance( res, @@ -442,7 +443,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) res = get_proper_type(res) return res elif isinstance(node, TypeInfo): - return self.analyze_type_with_type_info(node, t.args, t) + return self.analyze_type_with_type_info(node, t.args, t, t.empty_tuple_index) elif node.fullname in TYPE_ALIAS_NAMES: return AnyType(TypeOfAny.special_form) # Concatenate is an operator, no need for a proper type @@ -700,7 +701,7 @@ def get_omitted_any(self, typ: Type, fullname: str | None = None) -> AnyType: 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 + self, info: TypeInfo, args: Sequence[Type], ctx: Context, empty_tuple_index: bool ) -> Type: """Bind unbound type when were able to find target TypeInfo. @@ -735,7 +736,9 @@ def analyze_type_with_type_info( # Check type argument count. instance.args = tuple(flatten_nested_tuples(instance.args)) - if not self.defining_alias and not validate_instance(instance, self.fail): + if not (self.defining_alias and self.nesting_level == 0) and not validate_instance( + instance, self.fail, empty_tuple_index + ): fix_instance( instance, self.fail, @@ -1203,7 +1206,7 @@ def visit_placeholder_type(self, t: PlaceholderType) -> Type: else: # TODO: Handle non-TypeInfo assert isinstance(n.node, TypeInfo) - return self.analyze_type_with_type_info(n.node, t.args, t) + return self.analyze_type_with_type_info(n.node, t.args, t, False) def analyze_callable_args_for_paramspec( self, callable_args: Type, ret_type: Type, fallback: Instance @@ -2256,7 +2259,7 @@ def make_optional_type(t: Type) -> Type: return UnionType([t, NoneType()], t.line, t.column) -def validate_instance(t: Instance, fail: MsgCallback) -> bool: +def validate_instance(t: Instance, fail: MsgCallback, empty_tuple_index: bool) -> bool: """Check if this is a well-formed instance with respect to argument count/positions.""" # TODO: combine logic with instantiate_type_alias(). if any(unknown_unpack(a) for a in t.args): @@ -2279,8 +2282,9 @@ def validate_instance(t: Instance, fail: MsgCallback) -> bool: ) return False elif not t.args: - # The Any arguments should be set by the caller. - return False + if not (empty_tuple_index and len(t.type.type_vars) == 1): + # The Any arguments should be set by the caller. + return False else: # We also need to check if we are not performing a type variable tuple split. unpack = find_unpack_in_list(t.args) @@ -2313,34 +2317,6 @@ def validate_instance(t: Instance, fail: MsgCallback) -> bool: return True -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, options)) - - -class InstanceFixer(TypeTraverserVisitor): - def __init__(self, fail: MsgCallback, note: MsgCallback, options: Options) -> None: - self.fail = fail - self.note = note - self.options = options - - def visit_instance(self, typ: Instance) -> None: - super().visit_instance(typ) - if not validate_instance(typ, self.fail): - fix_instance( - typ, - self.fail, - self.note, - disallow_any=False, - options=self.options, - use_generic_error=True, - ) - - def find_self_type(typ: Type, lookup: Callable[[str], SymbolTableNode | None]) -> bool: return typ.accept(HasSelfType(lookup)) diff --git a/mypy/types.py b/mypy/types.py index 09ba68aae88a..ea81609fc605 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3163,6 +3163,8 @@ def visit_instance(self, t: Instance) -> str: s += f"[{self.list_str(t.args)}, ...]" else: s += f"[{self.list_str(t.args)}]" + elif t.type.has_type_var_tuple_type and len(t.type.type_vars) == 1: + s += "[()]" if self.id_mapper: s += f"<{self.id_mapper.id(t.type)}>" return s diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 06b7cab8391b..546d02a07ad0 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2277,3 +2277,22 @@ list(2) # E: No overload variant of "list" matches argument type "int" [call-o # N: def [T] __init__(self) -> List[T] \ # N: def [T] __init__(self, x: Iterable[T]) -> List[T] [builtins fixtures/list.pyi] + +[case testNestedGenericInAliasDisallow] +# flags: --disallow-any-generics +from typing import TypeVar, Generic, List, Union + +class C(Generic[T]): ... + +A = Union[C, List] # E: Missing type parameters for generic type "C" \ + # E: Missing type parameters for generic type "List" +[builtins fixtures/list.pyi] + +[case testNestedGenericInAliasAllow] +# flags: --allow-any-generics +from typing import TypeVar, Generic, List, Union + +class C(Generic[T]): ... + +A = Union[C, List] # OK +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 22a30432d098..4a281fbf0b49 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -118,7 +118,10 @@ variadic_single: Variadic[int] reveal_type(variadic_single) # N: Revealed type is "__main__.Variadic[builtins.int]" empty: Variadic[()] -reveal_type(empty) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[Any, ...]]]" +reveal_type(empty) # N: Revealed type is "__main__.Variadic[()]" + +omitted: Variadic +reveal_type(omitted) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[Any, ...]]]" bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]] # E: More than one Unpack in a type is not allowed reveal_type(bad) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" @@ -1846,6 +1849,50 @@ def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: return submit(func, 1, *args) [builtins fixtures/tuple.pyi] +[case testTypeVarTupleEmptySpecialCase] +from typing import Any, Callable, Generic +from typing_extensions import Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +class MyClass(Generic[Unpack[Ts]]): + func: Callable[[Unpack[Ts]], object] + + def __init__(self, func: Callable[[Unpack[Ts]], object]) -> None: + self.func = func + +explicit: MyClass[()] +reveal_type(explicit) # N: Revealed type is "__main__.MyClass[()]" +reveal_type(explicit.func) # N: Revealed type is "def () -> builtins.object" + +a: Any +explicit_2 = MyClass[()](a) +reveal_type(explicit_2) # N: Revealed type is "__main__.MyClass[()]" +reveal_type(explicit_2.func) # N: Revealed type is "def () -> builtins.object" + +Alias = MyClass[()] +explicit_3: Alias +reveal_type(explicit_3) # N: Revealed type is "__main__.MyClass[()]" +reveal_type(explicit_3.func) # N: Revealed type is "def () -> builtins.object" + +explicit_4 = Alias(a) +reveal_type(explicit_4) # N: Revealed type is "__main__.MyClass[()]" +reveal_type(explicit_4.func) # N: Revealed type is "def () -> builtins.object" + +def no_args() -> None: ... +implicit = MyClass(no_args) +reveal_type(implicit) # N: Revealed type is "__main__.MyClass[()]" +reveal_type(implicit.func) # N: Revealed type is "def () -> builtins.object" + +def one_arg(__a: int) -> None: ... +x = MyClass(one_arg) +x = explicit # E: Incompatible types in assignment (expression has type "MyClass[()]", variable has type "MyClass[int]") + +# Consistently handle special case for no argument aliases +Direct = MyClass +y = Direct(one_arg) +reveal_type(y) # N: Revealed type is "__main__.MyClass[builtins.int]" +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleRuntimeTypeApplication] from typing import Generic, TypeVar, Tuple from typing_extensions import Unpack, TypeVarTuple From f5a3e233c99077317c4cf6fee7745686d67fd21b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 17 Oct 2023 13:23:05 +0300 Subject: [PATCH 0319/1617] Bump test deps: `ruff` and `pre-commit-hooks` (#16273) Release post: https://astral.sh/blog/ruff-v0.1.0 --- .pre-commit-config.yaml | 4 ++-- test-requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e92d498fa3cc..bd2a09b7a8cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ exclude: '^(mypyc/external/)|(mypy/typeshed/)' # Exclude all vendored code from lints repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 # must match test-requirements.txt + rev: v4.5.0 # must match test-requirements.txt hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -10,7 +10,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 # must match test-requirements.txt + rev: v0.1.0 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/test-requirements.txt b/test-requirements.txt index bdaad16fa88e..a1fa98917872 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,11 +6,11 @@ filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' pre-commit -pre-commit-hooks==4.4.0 +pre-commit-hooks==4.5.0 psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.0.292 # must match version in .pre-commit-config.yaml +ruff==0.1.0 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 From 4a9e6e60884c0bab89eb2ec6e947373c871f8aee Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Oct 2023 15:46:34 +0100 Subject: [PATCH 0320/1617] Attempt to fix daemon crash related to ABCs (#16275) I couldn't reproduce the crash with a small example, but this seems to fix this crash in a large codebase: ``` Traceback (most recent call last): File "/Users/jukka/src/mypy/mypy/dmypy_server.py", line 234, in serve resp = self.run_command(command, data) File "/Users/jukka/src/mypy/mypy/dmypy_server.py", line 281, in run_command ret = method(self, **data) File "/Users/jukka/src/mypy/mypy/dmypy_server.py", line 359, in cmd_check return self.check(sources, export_types, is_tty, terminal_width) File "/Users/jukka/src/mypy/mypy/dmypy_server.py", line 413, in check res = self.initialize_fine_grained(sources, is_tty, terminal_width) File "/Users/jukka/src/mypy/mypy/dmypy_server.py", line 498, in initialize_fine_grained messages = self.fine_grained_manager.update(changed, removed) File "/Users/jukka/src/mypy/mypy/server/update.py", line 267, in update result = self.update_one( File "/Users/jukka/src/mypy/mypy/server/update.py", line 369, in update_one result = self.update_module(next_id, next_path, next_id in removed_set, followed) File "/Users/jukka/src/mypy/mypy/server/update.py", line 431, in update_module result = update_module_isolated( File "/Users/jukka/src/mypy/mypy/server/update.py", line 667, in update_module_isolated state.type_check_first_pass() File "/Users/jukka/src/mypy/mypy/build.py", line 2306, in type_check_first_pass self.type_checker().check_first_pass() File "/Users/jukka/src/mypy/mypy/checker.py", line 475, in check_first_pass self.accept(d) File "/Users/jukka/src/mypy/mypy/checker.py", line 587, in accept report_internal_error(err, self.errors.file, stmt.line, self.errors, self.options) File "/Users/jukka/src/mypy/mypy/errors.py", line 1261, in report_internal_error raise err File "/Users/jukka/src/mypy/mypy/checker.py", line 585, in accept stmt.accept(self) File "/Users/jukka/src/mypy/mypy/nodes.py", line 900, in accept return visitor.visit_decorator(self) File "/Users/jukka/src/mypy/mypy/checker.py", line 4773, in visit_decorator self.visit_decorator_inner(e) File "/Users/jukka/src/mypy/mypy/checker.py", line 4778, in visit_decorator_inner self.check_func_item(e.func, name=e.func.name, allow_empty=allow_empty) File "/Users/jukka/src/mypy/mypy/checker.py", line 1071, in check_func_item self.check_func_def(defn, typ, name, allow_empty) File "/Users/jukka/src/mypy/mypy/checker.py", line 1281, in check_func_def self.accept(item.body) File "/Users/jukka/src/mypy/mypy/checker.py", line 587, in accept report_internal_error(err, self.errors.file, stmt.line, self.errors, self.options) File "/Users/jukka/src/mypy/mypy/errors.py", line 1261, in report_internal_error raise err File "/Users/jukka/src/mypy/mypy/checker.py", line 585, in accept stmt.accept(self) File "/Users/jukka/src/mypy/mypy/nodes.py", line 1226, in accept return visitor.visit_block(self) File "/Users/jukka/src/mypy/mypy/checker.py", line 2754, in visit_block self.accept(s) File "/Users/jukka/src/mypy/mypy/checker.py", line 587, in accept report_internal_error(err, self.errors.file, stmt.line, self.errors, self.options) File "/Users/jukka/src/mypy/mypy/errors.py", line 1261, in report_internal_error raise err File "/Users/jukka/src/mypy/mypy/checker.py", line 585, in accept stmt.accept(self) File "/Users/jukka/src/mypy/mypy/nodes.py", line 1313, in accept return visitor.visit_assignment_stmt(self) File "/Users/jukka/src/mypy/mypy/checker.py", line 2802, in visit_assignment_stmt self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax) File "/Users/jukka/src/mypy/mypy/checker.py", line 3009, in check_assignment rvalue_type = self.expr_checker.accept(rvalue, type_context=type_context) File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 5372, in accept report_internal_error( File "/Users/jukka/src/mypy/mypy/errors.py", line 1261, in report_internal_error raise err File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 5370, in accept typ = node.accept(self) File "/Users/jukka/src/mypy/mypy/nodes.py", line 1907, in accept return visitor.visit_call_expr(self) File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 452, in visit_call_expr return self.visit_call_expr_inner(e, allow_none_return=allow_none_return) File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 581, in visit_call_expr_inner ret_type = self.check_call_expr_with_callee_type( File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 1420, in check_call_expr_with_callee_type ret_type, callee_type = self.check_call( File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 1514, in check_call return self.check_callable_call( File "/Users/jukka/src/mypy/mypy/checkexpr.py", line 1638, in check_callable_call self.msg.cannot_instantiate_abstract_class( File "/Users/jukka/src/mypy/mypy/messages.py", line 1479, in cannot_instantiate_abstract_class attrs = format_string_list([f'"{a}"' for a in abstract_attributes]) File "/Users/jukka/src/mypy/mypy/messages.py", line 2948, in format_string_list assert lst AssertionError ``` I suspect that we first set `is_abstract` to true, and later the class was no longer abstract and `abstract_attributes` got cleared, but `is_abstract` was stuck at true. --- mypy/semanal_classprop.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_classprop.py b/mypy/semanal_classprop.py index dfd4e5b6f122..b5f1b2181761 100644 --- a/mypy/semanal_classprop.py +++ b/mypy/semanal_classprop.py @@ -46,6 +46,8 @@ def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: E abstract attribute. Also compute a list of abstract attributes. Report error is required ABCMeta metaclass is missing. """ + typ.is_abstract = False + typ.abstract_attributes = [] if typ.typeddict_type: return # TypedDict can't be abstract concrete: set[str] = set() @@ -56,7 +58,6 @@ def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: E # Special case: NewTypes are considered as always non-abstract, so they can be used as: # Config = NewType('Config', Mapping[str, str]) # default = Config({'cannot': 'modify'}) # OK - typ.abstract_attributes = [] return for base in typ.mro: for name, symnode in base.names.items(): From f3bdf5caaf6ccbba6c5df21b483fb9b716f13851 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 18 Oct 2023 04:40:46 +0100 Subject: [PATCH 0321/1617] Support fancy new syntax for variadic types (#16242) This is the last significant thing I am aware of that is needed for PEP 646 support. After this and other currently open PRs are merged, I will make an additional pass grepping for usual suspects and verifying we didn't miss anything. Then we can flip the switch and announce this as supported. --- mypy/exprtotype.py | 5 ++- mypy/fastparse.py | 8 +--- mypy/messages.py | 2 + mypy/options.py | 3 ++ mypy/semanal.py | 56 ++++++++++++++----------- mypy/typeanal.py | 5 ++- test-data/unit/check-python311.test | 65 +++++++++++++++++++++++++++++ test-data/unit/check-python312.test | 2 - 8 files changed, 111 insertions(+), 35 deletions(-) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index 5f0ef79acbd7..7a50429b81d1 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -103,7 +103,10 @@ def expr_to_unanalyzed_type( return expr_to_unanalyzed_type(args[0], options, allow_new_syntax, expr) else: base.args = tuple( - expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr) for arg in args + expr_to_unanalyzed_type( + arg, options, allow_new_syntax, expr, allow_unpack=True + ) + for arg in args ) if not base.args: base.empty_tuple_index = True diff --git a/mypy/fastparse.py b/mypy/fastparse.py index fe158d468ce8..95d99db84a15 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1762,7 +1762,6 @@ def __init__( self.override_column = override_column self.node_stack: list[AST] = [] self.is_evaluated = is_evaluated - self.allow_unpack = False def convert_column(self, column: int) -> int: """Apply column override if defined; otherwise return column. @@ -2039,19 +2038,14 @@ def visit_Attribute(self, n: Attribute) -> Type: else: return self.invalid_type(n) - # Used for Callable[[X *Ys, Z], R] + # Used for Callable[[X *Ys, Z], R] etc. def visit_Starred(self, n: ast3.Starred) -> Type: return UnpackType(self.visit(n.value), from_star_syntax=True) # List(expr* elts, expr_context ctx) def visit_List(self, n: ast3.List) -> Type: assert isinstance(n.ctx, ast3.Load) - old_allow_unpack = self.allow_unpack - # We specifically only allow starred expressions in a list to avoid - # confusing errors for top-level unpacks (e.g. in base classes). - self.allow_unpack = True result = self.translate_argument_list(n.elts) - self.allow_unpack = old_allow_unpack return result diff --git a/mypy/messages.py b/mypy/messages.py index dc5056f616ea..19aafedd5586 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2516,6 +2516,8 @@ def format_literal_value(typ: LiteralType) -> str: # There are type arguments. Convert the arguments to strings. return f"{base_str}[{format_list(itype.args)}]" elif isinstance(typ, UnpackType): + if options.use_star_unpack(): + return f"*{format(typ.type)}" return f"Unpack[{format(typ.type)}]" elif isinstance(typ, TypeVarType): # This is similar to non-generic instance types. diff --git a/mypy/options.py b/mypy/options.py index 007ae0a78aa1..603ba79935ee 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -385,6 +385,9 @@ def use_or_syntax(self) -> bool: return not self.force_union_syntax return False + def use_star_unpack(self) -> bool: + return self.python_version >= (3, 11) + # To avoid breaking plugin compatibility, keep providing new_semantic_analyzer @property def new_semantic_analyzer(self) -> bool: diff --git a/mypy/semanal.py b/mypy/semanal.py index 1111b1df50e9..9c2452252208 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1992,38 +1992,42 @@ def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList return None def analyze_unbound_tvar(self, t: Type) -> tuple[str, TypeVarLikeExpr] | None: - if not isinstance(t, UnboundType): - return None - unbound = t - sym = self.lookup_qualified(unbound.name, unbound) + if isinstance(t, UnpackType) and isinstance(t.type, UnboundType): + return self.analyze_unbound_tvar_impl(t.type, allow_tvt=True) + if isinstance(t, UnboundType): + sym = self.lookup_qualified(t.name, t) + if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + inner_t = t.args[0] + if isinstance(inner_t, UnboundType): + return self.analyze_unbound_tvar_impl(inner_t, allow_tvt=True) + return None + return self.analyze_unbound_tvar_impl(t) + return None + + def analyze_unbound_tvar_impl( + self, t: UnboundType, allow_tvt: bool = False + ) -> tuple[str, TypeVarLikeExpr] | None: + sym = self.lookup_qualified(t.name, t) if sym and isinstance(sym.node, PlaceholderNode): self.record_incomplete_ref() - if sym and isinstance(sym.node, ParamSpecExpr): + if not allow_tvt and sym and isinstance(sym.node, ParamSpecExpr): if sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope return None - return unbound.name, sym.node - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): - inner_t = unbound.args[0] - if not isinstance(inner_t, UnboundType): + return t.name, sym.node + if allow_tvt and sym and isinstance(sym.node, TypeVarTupleExpr): + if sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): + # It's bound by our type variable scope return None - inner_unbound = inner_t - inner_sym = self.lookup_qualified(inner_unbound.name, inner_unbound) - if inner_sym and isinstance(inner_sym.node, PlaceholderNode): - self.record_incomplete_ref() - if inner_sym and isinstance(inner_sym.node, TypeVarTupleExpr): - if inner_sym.fullname and not self.tvar_scope.allow_binding(inner_sym.fullname): - # It's bound by our type variable scope - return None - return inner_unbound.name, inner_sym.node - if sym is None or not isinstance(sym.node, TypeVarExpr): + return t.name, sym.node + if sym is None or not isinstance(sym.node, TypeVarExpr) or allow_tvt: return None elif sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope return None else: assert isinstance(sym.node, TypeVarExpr) - return unbound.name, sym.node + return t.name, sym.node def get_all_bases_tvars( self, base_type_exprs: list[Expression], removed: list[int] @@ -5333,7 +5337,9 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: has_param_spec = False num_args = -1 elif isinstance(base, RefExpr) and isinstance(base.node, TypeInfo): - allow_unpack = base.node.has_type_var_tuple_type + allow_unpack = ( + base.node.has_type_var_tuple_type or base.node.fullname == "builtins.tuple" + ) has_param_spec = base.node.has_param_spec_type num_args = len(base.node.type_vars) else: @@ -5343,7 +5349,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: for item in items: try: - typearg = self.expr_to_unanalyzed_type(item) + typearg = self.expr_to_unanalyzed_type(item, allow_unpack=True) except TypeTranslationError: self.fail("Type expected within [...]", expr) return None @@ -6608,8 +6614,10 @@ def type_analyzer( tpan.global_scope = not self.type and not self.function_stack return tpan - def expr_to_unanalyzed_type(self, node: Expression) -> ProperType: - return expr_to_unanalyzed_type(node, self.options, self.is_stub_file) + def expr_to_unanalyzed_type(self, node: Expression, allow_unpack: bool = False) -> ProperType: + return expr_to_unanalyzed_type( + node, self.options, self.is_stub_file, allow_unpack=allow_unpack + ) def anal_type( self, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 4743126c3d56..b16d0ac066b4 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -964,7 +964,10 @@ def visit_unpack_type(self, t: UnpackType) -> Type: if not self.allow_unpack: self.fail(message_registry.INVALID_UNPACK_POSITION, t.type, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) - return UnpackType(self.anal_type(t.type), from_star_syntax=t.from_star_syntax) + self.allow_type_var_tuple = True + result = UnpackType(self.anal_type(t.type), from_star_syntax=t.from_star_syntax) + self.allow_type_var_tuple = False + return result def visit_parameters(self, t: Parameters) -> Type: raise NotImplementedError("ParamSpec literals cannot have unbound TypeVars") diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 5870c7e17bcc..37dc3ca0f5b4 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -77,3 +77,68 @@ async def coro() -> Generator[List[Any], None, None]: reveal_type(coro) # N: Revealed type is "def () -> typing.Coroutine[Any, Any, typing.Generator[builtins.list[Any], None, None]]" [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] + +[case testTypeVarTupleNewSyntaxAnnotations] +Ints = tuple[int, int, int] +x: tuple[str, *Ints] +reveal_type(x) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.int, builtins.int]" +y: tuple[int, *tuple[int, ...]] +reveal_type(y) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleNewSyntaxGenerics] +from typing import Generic, TypeVar, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +class C(Generic[T, *Ts]): + attr: tuple[int, *Ts, str] + + def test(self) -> None: + reveal_type(self.attr) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`2], builtins.str]" + self.attr = ci # E: Incompatible types in assignment (expression has type "C[*Tuple[int, ...]]", variable has type "Tuple[int, *Ts, str]") + def meth(self, *args: *Ts) -> T: ... + +ci: C[*tuple[int, ...]] +reveal_type(ci) # N: Revealed type is "__main__.C[Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(ci.meth) # N: Revealed type is "def (*args: builtins.int) -> builtins.int" +c3: C[str, str, str] +reveal_type(c3) # N: Revealed type is "__main__.C[builtins.str, builtins.str, builtins.str]" + +A = C[int, *Ts] +B = tuple[str, *tuple[str, str], str] +z: A[*B] +reveal_type(z) # N: Revealed type is "__main__.C[builtins.int, builtins.str, builtins.str, builtins.str, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleNewSyntaxCallables] +from typing import Generic, overload, TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +class MyClass(Generic[T1, T2]): + @overload + def __init__(self: MyClass[None, None]) -> None: ... + + @overload + def __init__(self: MyClass[T1, None], *types: *tuple[type[T1]]) -> None: ... + + @overload + def __init__(self: MyClass[T1, T2], *types: *tuple[type[T1], type[T2]]) -> None: ... + + def __init__(self: MyClass[T1, T2], *types: *tuple[type, ...]) -> None: + pass + +myclass = MyClass() +reveal_type(myclass) # N: Revealed type is "__main__.MyClass[None, None]" +myclass1 = MyClass(float) +reveal_type(myclass1) # N: Revealed type is "__main__.MyClass[builtins.float, None]" +myclass2 = MyClass(float, float) +reveal_type(myclass2) # N: Revealed type is "__main__.MyClass[builtins.float, builtins.float]" +myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" matches argument types "Type[float]", "Type[float]", "Type[float]" \ + # N: Possible overload variants: \ + # N: def [T1, T2] __init__(self) -> MyClass[None, None] \ + # N: def [T1, T2] __init__(self, Type[T1], /) -> MyClass[T1, None] \ + # N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2] +reveal_type(myclass3) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 91aca7794071..cb89eb34880c 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -41,8 +41,6 @@ type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet suppo # E: Value of type "int" is not indexable \ # E: Name "P" is not defined type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported \ - # E: Type expected within [...] \ - # E: The type "Type[Tuple[Any, ...]]" is not generic and not indexable \ # E: Name "Ts" is not defined class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported From ffe89a21058eaa6eb1c1796d9ab87aece965e2d9 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:51:22 -0700 Subject: [PATCH 0322/1617] Add a changelog (#16280) I pre-populated it with blog post entries since mypy 1.0. There might be some markdown or backslashes that are borked, feel free to push to this PR if you notice anything. --- CHANGELOG.md | 1254 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1254 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000000..d8237795112b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1254 @@ +# Mypy Release Notes + +## Unreleased + +... + +#### Other Notable Changes and Fixes +... + +#### Acknowledgements +... + +## Mypy 1.6 + +[Tuesday, 10 October 2023](https://mypy-lang.blogspot.com/2023/10/mypy-16-released.html) + +We’ve just uploaded mypy 1.6 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Introduce Error Subcodes for Import Errors + +Mypy now uses the error code import-untyped if an import targets an installed library that doesn’t support static type checking, and no stub files are available. Other invalid imports produce the import-not-found error code. They both are subcodes of the import error code, which was previously used for both kinds of import-related errors. + +Use \--disable-error-code=import-untyped to only ignore import errors about installed libraries without stubs. This way mypy will still report errors about typos in import statements, for example. + +If you use \--warn-unused-ignore or \--strict, mypy will complain if you use \# type: ignore\[import\] to ignore an import error. You are expected to use one of the more specific error codes instead. Otherwise, ignoring the import error code continues to silence both errors. + +This feature was contributed by Shantanu (PR [15840](https://github.com/python/mypy/pull/15840), PR [14740](https://github.com/python/mypy/pull/14740)). + +#### Remove Support for Targeting Python 3.6 and Earlier + +Running mypy with \--python-version 3.6, for example, is no longer supported. Python 3.6 hasn’t been properly supported by mypy for some time now, and this makes it explicit. This was contributed by Nikita Sobolev (PR [15668](https://github.com/python/mypy/pull/15668)). + +#### Selective Filtering of \--disallow-untyped-calls Targets + +Using \--disallow-untyped-calls could be annoying when using libraries with missing type information, as mypy would generate many errors about code that uses the library. Now you can use \--untyped-calls-exclude=acme, for example, to disable these errors about calls targeting functions defined in the acme package. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#cmdoption-mypy-untyped-calls-exclude) for more information. + +This feature was contributed by Ivan Levkivskyi (PR [15845](https://github.com/python/mypy/pull/15845)). + +#### Improved Type Inference between Callable Types + +Mypy now does a better job inferring type variables inside arguments of callable types. For example, this code fragment now type checks correctly: + +```python +def f(c: Callable[[T, S], None]) -> Callable[[str, T, S], None]: ... +def g(*x: int) -> None: ... + +reveal_type(f(g)) # Callable[[str, int, int], None] +``` + +This was contributed by Ivan Levkivskyi (PR [15910](https://github.com/python/mypy/pull/15910)). + +#### Don’t Consider None and TypeVar to Overlap in Overloads + +Mypy now doesn’t consider an overload item with an argument type None to overlap with a type variable: + +```python +@overload +def f(x: None) -> None: .. +@overload +def f(x: T) -> Foo[T]: ... +... +``` + +Previously mypy would generate an error about the definition of f above. This is slightly unsafe if the upper bound of T is object, since the value of the type variable could be None. We relaxed the rules a little, since this solves a common issue. + +This feature was contributed by Ivan Levkivskyi (PR [15846](https://github.com/python/mypy/pull/15846)). + +#### Improvements to \--new-type-inference + +The experimental new type inference algorithm (polymorphic inference) introduced as an opt-in feature in mypy 1.5 has several improvements: + +* Improve transitive closure computation during constraint solving (Ivan Levkivskyi, PR [15754](https://github.com/python/mypy/pull/15754)) +* Add support for upper bounds and values with \--new-type-inference (Ivan Levkivskyi, PR [15813](https://github.com/python/mypy/pull/15813)) +* Basic support for variadic types with \--new-type-inference (Ivan Levkivskyi, PR [15879](https://github.com/python/mypy/pull/15879)) +* Polymorphic inference: support for parameter specifications and lambdas (Ivan Levkivskyi, PR [15837](https://github.com/python/mypy/pull/15837)) +* Invalidate cache when adding \--new-type-inference (Marc Mueller, PR [16059](https://github.com/python/mypy/pull/16059)) + +**Note:** We are planning to enable \--new-type-inference by default in mypy 1.7. Please try this out and let us know if you encounter any issues. + +#### ParamSpec Improvements + +* Support self-types containing ParamSpec (Ivan Levkivskyi, PR [15903](https://github.com/python/mypy/pull/15903)) +* Allow “…” in Concatenate, and clean up ParamSpec literals (Ivan Levkivskyi, PR [15905](https://github.com/python/mypy/pull/15905)) +* Fix ParamSpec inference for callback protocols (Ivan Levkivskyi, PR [15986](https://github.com/python/mypy/pull/15986)) +* Infer ParamSpec constraint from arguments (Ivan Levkivskyi, PR [15896](https://github.com/python/mypy/pull/15896)) +* Fix crash on invalid type variable with ParamSpec (Ivan Levkivskyi, PR [15953](https://github.com/python/mypy/pull/15953)) +* Fix subtyping between ParamSpecs (Ivan Levkivskyi, PR [15892](https://github.com/python/mypy/pull/15892)) + +#### Stubgen Improvements + +* Add option to include docstrings with stubgen (chylek, PR [13284](https://github.com/python/mypy/pull/13284)) +* Add required ... initializer to NamedTuple fields with default values (Nikita Sobolev, PR [15680](https://github.com/python/mypy/pull/15680)) + +#### Stubtest Improvements + +* Fix \_\_mypy-replace false positives (Alex Waygood, PR [15689](https://github.com/python/mypy/pull/15689)) +* Fix edge case for bytes enum subclasses (Alex Waygood, PR [15943](https://github.com/python/mypy/pull/15943)) +* Generate error if typeshed is missing modules from the stdlib (Alex Waygood, PR [15729](https://github.com/python/mypy/pull/15729)) +* Fixes to new check for missing stdlib modules (Alex Waygood, PR [15960](https://github.com/python/mypy/pull/15960)) +* Fix stubtest enum.Flag edge case (Alex Waygood, PR [15933](https://github.com/python/mypy/pull/15933)) + +#### Documentation Improvements + +* Do not advertise to create your own assert\_never helper (Nikita Sobolev, PR [15947](https://github.com/python/mypy/pull/15947)) +* Fix all the missing references found within the docs (Albert Tugushev, PR [15875](https://github.com/python/mypy/pull/15875)) +* Document await-not-async error code (Shantanu, PR [15858](https://github.com/python/mypy/pull/15858)) +* Improve documentation of disabling error codes (Shantanu, PR [15841](https://github.com/python/mypy/pull/15841)) + +#### Other Notable Changes and Fixes + +* Make unsupported PEP 695 features (introduced in Python 3.12) give a reasonable error message (Shantanu, PR [16013](https://github.com/python/mypy/pull/16013)) +* Remove the \--py2 command-line argument (Marc Mueller, PR [15670](https://github.com/python/mypy/pull/15670)) +* Change empty tuple from tuple\[\] to tuple\[()\] in error messages (Nikita Sobolev, PR [15783](https://github.com/python/mypy/pull/15783)) +* Fix assert\_type failures when some nodes are deferred (Nikita Sobolev, PR [15920](https://github.com/python/mypy/pull/15920)) +* Generate error on unbound TypeVar with values (Nikita Sobolev, PR [15732](https://github.com/python/mypy/pull/15732)) +* Fix over-eager types-google-cloud-ndb suggestion (Shantanu, PR [15347](https://github.com/python/mypy/pull/15347)) +* Fix type narrowing of \== None and in (None,) conditions (Marti Raudsepp, PR [15760](https://github.com/python/mypy/pull/15760)) +* Fix inference for attrs.fields (Shantanu, PR [15688](https://github.com/python/mypy/pull/15688)) +* Make “await in non-async function” a non-blocking error and give it an error code (Gregory Santosa, PR [15384](https://github.com/python/mypy/pull/15384)) +* Add basic support for decorated overloads (Ivan Levkivskyi, PR [15898](https://github.com/python/mypy/pull/15898)) +* Fix TypeVar regression with self types (Ivan Levkivskyi, PR [15945](https://github.com/python/mypy/pull/15945)) +* Add \_\_match\_args\_\_ to dataclasses with no fields (Ali Hamdan, PR [15749](https://github.com/python/mypy/pull/15749)) +* Include stdout and stderr in dmypy verbose output (Valentin Stanciu, PR [15881](https://github.com/python/mypy/pull/15881)) +* Improve match narrowing and reachability analysis (Shantanu, PR [15882](https://github.com/python/mypy/pull/15882)) +* Support \_\_bool\_\_ with Literal in \--warn-unreachable (Jannic Warken, PR [15645](https://github.com/python/mypy/pull/15645)) +* Fix inheriting from generic @frozen attrs class (Ilya Priven, PR [15700](https://github.com/python/mypy/pull/15700)) +* Correctly narrow types for tuple\[type\[X\], ...\] (Nikita Sobolev, PR [15691](https://github.com/python/mypy/pull/15691)) +* Don't flag intentionally empty generators unreachable (Ilya Priven, PR [15722](https://github.com/python/mypy/pull/15722)) +* Add tox.ini to mypy sdist (Marcel Telka, PR [15853](https://github.com/python/mypy/pull/15853)) +* Fix mypyc regression with pretty (Shantanu, PR [16124](https://github.com/python/mypy/pull/16124)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=6a8d653a671925b0a3af61729ff8cf3f90c9c662+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to Max Murin, who did most of the release manager work for this release (I just did the final steps). + +Thanks to all mypy contributors who contributed to this release: + +* Albert Tugushev +* Alex Waygood +* Ali Hamdan +* chylek +* EXPLOSION +* Gregory Santosa +* Ilya Priven +* Ivan Levkivskyi +* Jannic Warken +* KotlinIsland +* Marc Mueller +* Marcel Johannesmann +* Marcel Telka +* Mark Byrne +* Marti Raudsepp +* Max Murin +* Nikita Sobolev +* Shantanu +* Valentin Stanciu + +Posted by Jukka Lehtosalo + + +## Mypy 1.5 + +[Thursday, 10 August 2023](https://mypy-lang.blogspot.com/2023/08/mypy-15-released.html) + +We’ve just uploaded mypy 1.5 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, deprecations and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Drop Support for Python 3.7 + +Mypy no longer supports running with Python 3.7, which has reached end-of-life. This was contributed by Shantanu (PR [15566](https://github.com/python/mypy/pull/15566)). + +#### Optional Check to Require Explicit @override + +If you enable the explicit-override error code, mypy will generate an error if a method override doesn’t use the @typing.override decorator (as discussed in [PEP 698](https://peps.python.org/pep-0698/#strict-enforcement-per-project)). This way mypy will detect accidentally introduced overrides. Example: + +```python +# mypy: enable-error-code="explicit-override" + +from typing_extensions import override + +class C: + def foo(self) -> None: pass + def bar(self) -> None: pass + +class D(C): + # Error: Method "foo" is not using @override but is + # overriding a method + def foo(self) -> None: + ... + + @override + def bar(self) -> None: # OK + ... +``` + +You can enable the error code via \--enable-error-code=explicit-override on the mypy command line or enable\_error\_code = explicit-override in the mypy config file. + +The override decorator will be available in typing in Python 3.12, but you can also use the backport from a recent version of `typing_extensions` on all supported Python versions. + +This feature was contributed by Marc Mueller(PR [15512](https://github.com/python/mypy/pull/15512)). + +#### More Flexible TypedDict Creation and Update + +Mypy was previously overly strict when type checking TypedDict creation and update operations. Though these checks were often technically correct, they sometimes triggered for apparently valid code. These checks have now been relaxed by default. You can enable stricter checking by using the new \--extra-checks flag. + +Construction using the `**` syntax is now more flexible: + +```python +from typing import TypedDict + +class A(TypedDict): + foo: int + bar: int + +class B(TypedDict): + foo: int + +a: A = {"foo": 1, "bar": 2} +b: B = {"foo": 3} +a2: A = { **a, **b} # OK (previously an error) +``` + +You can also call update() with a TypedDict argument that contains a subset of the keys in the updated TypedDict: +```python +a.update(b) # OK (previously an error) +``` + +This feature was contributed by Ivan Levkivskyi (PR [15425](https://github.com/python/mypy/pull/15425)). + +#### Deprecated Flag: \--strict-concatenate + +The behavior of \--strict-concatenate is now included in the new \--extra-checks flag, and the old flag is deprecated. + +#### Optionally Show Links to Error Code Documentation + +If you use \--show-error-code-links, mypy will add documentation links to (many) reported errors. The links are not shown for error messages that are sufficiently obvious, and they are shown once per error code only. + +Example output: +``` +a.py:1: error: Need type annotation for "foo" (hint: "x: List[] = ...") [var-annotated] +a.py:1: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-var-annotated for more info +``` +This was contributed by Ivan Levkivskyi (PR [15449](https://github.com/python/mypy/pull/15449)). + +#### Consistently Avoid Type Checking Unreachable Code + +If a module top level has unreachable code, mypy won’t type check the unreachable statements. This is consistent with how functions behave. The behavior of \--warn-unreachable is also more consistent now. + +This was contributed by Ilya Priven (PR [15386](https://github.com/python/mypy/pull/15386)). + +#### Experimental Improved Type Inference for Generic Functions + +You can use \--new-type-inference to opt into an experimental new type inference algorithm. It fixes issues when calling a generic functions with an argument that is also a generic function, in particular. This current implementation is still incomplete, but we encourage trying it out and reporting bugs if you encounter regressions. We are planning to enable the new algorithm by default in a future mypy release. + +This feature was contributed by Ivan Levkivskyi (PR [15287](https://github.com/python/mypy/pull/15287)). + +#### Partial Support for Python 3.12 + +Mypy and mypyc now support running on recent Python 3.12 development versions. Not all new Python 3.12 features are supported, and we don’t ship compiled wheels for Python 3.12 yet. + +* Fix ast warnings for Python 3.12 (Nikita Sobolev, PR [15558](https://github.com/python/mypy/pull/15558)) +* mypyc: Fix multiple inheritance with a protocol on Python 3.12 (Jukka Lehtosalo, PR [15572](https://github.com/python/mypy/pull/15572)) +* mypyc: Fix self-compilation on Python 3.12 (Jukka Lehtosalo, PR [15582](https://github.com/python/mypy/pull/15582)) +* mypyc: Fix 3.12 issue with pickling of instances with \_\_dict\_\_ (Jukka Lehtosalo, PR [15574](https://github.com/python/mypy/pull/15574)) +* mypyc: Fix i16 on Python 3.12 (Jukka Lehtosalo, PR [15510](https://github.com/python/mypy/pull/15510)) +* mypyc: Fix int operations on Python 3.12 (Jukka Lehtosalo, PR [15470](https://github.com/python/mypy/pull/15470)) +* mypyc: Fix generators on Python 3.12 (Jukka Lehtosalo, PR [15472](https://github.com/python/mypy/pull/15472)) +* mypyc: Fix classes with \_\_dict\_\_ on 3.12 (Jukka Lehtosalo, PR [15471](https://github.com/python/mypy/pull/15471)) +* mypyc: Fix coroutines on Python 3.12 (Jukka Lehtosalo, PR [15469](https://github.com/python/mypy/pull/15469)) +* mypyc: Don't use \_PyErr\_ChainExceptions on 3.12, since it's deprecated (Jukka Lehtosalo, PR [15468](https://github.com/python/mypy/pull/15468)) +* mypyc: Add Python 3.12 feature macro (Jukka Lehtosalo, PR [15465](https://github.com/python/mypy/pull/15465)) + +#### Improvements to Dataclasses + +* Improve signature of dataclasses.replace (Ilya Priven, PR [14849](https://github.com/python/mypy/pull/14849)) +* Fix dataclass/protocol crash on joining types (Ilya Priven, PR [15629](https://github.com/python/mypy/pull/15629)) +* Fix strict optional handling in dataclasses (Ivan Levkivskyi, PR [15571](https://github.com/python/mypy/pull/15571)) +* Support optional types for custom dataclass descriptors (Marc Mueller, PR [15628](https://github.com/python/mypy/pull/15628)) +* Add `__slots__` attribute to dataclasses (Nikita Sobolev, PR [15649](https://github.com/python/mypy/pull/15649)) +* Support better \_\_post\_init\_\_ method signature for dataclasses (Nikita Sobolev, PR [15503](https://github.com/python/mypy/pull/15503)) + +#### Mypyc Improvements + +* Support unsigned 8-bit native integer type: mypy\_extensions.u8 (Jukka Lehtosalo, PR [15564](https://github.com/python/mypy/pull/15564)) +* Support signed 16-bit native integer type: mypy\_extensions.i16 (Jukka Lehtosalo, PR [15464](https://github.com/python/mypy/pull/15464)) +* Define mypy\_extensions.i16 in stubs (Jukka Lehtosalo, PR [15562](https://github.com/python/mypy/pull/15562)) +* Document more unsupported features and update supported features (Richard Si, PR [15524](https://github.com/python/mypy/pull/15524)) +* Fix final NamedTuple classes (Richard Si, PR [15513](https://github.com/python/mypy/pull/15513)) +* Use C99 compound literals for undefined tuple values (Jukka Lehtosalo, PR [15453](https://github.com/python/mypy/pull/15453)) +* Don't explicitly assign NULL values in setup functions (Logan Hunt, PR [15379](https://github.com/python/mypy/pull/15379)) + +#### Stubgen Improvements + +* Teach stubgen to work with complex and unary expressions (Nikita Sobolev, PR [15661](https://github.com/python/mypy/pull/15661)) +* Support ParamSpec and TypeVarTuple (Ali Hamdan, PR [15626](https://github.com/python/mypy/pull/15626)) +* Fix crash on non-str docstring (Ali Hamdan, PR [15623](https://github.com/python/mypy/pull/15623)) + +#### Documentation Updates + +* Add documentation for additional error codes (Ivan Levkivskyi, PR [15539](https://github.com/python/mypy/pull/15539)) +* Improve documentation of type narrowing (Ilya Priven, PR [15652](https://github.com/python/mypy/pull/15652)) +* Small improvements to protocol documentation (Shantanu, PR [15460](https://github.com/python/mypy/pull/15460)) +* Remove confusing instance variable example in cheat sheet (Adel Atallah, PR [15441](https://github.com/python/mypy/pull/15441)) + +#### Other Notable Fixes and Improvements + +* Constant fold additional unary and binary expressions (Richard Si, PR [15202](https://github.com/python/mypy/pull/15202)) +* Exclude the same special attributes from Protocol as CPython (Kyle Benesch, PR [15490](https://github.com/python/mypy/pull/15490)) +* Change the default value of the slots argument of attrs.define to True, to match runtime behavior (Ilya Priven, PR [15642](https://github.com/python/mypy/pull/15642)) +* Fix type of class attribute if attribute is defined in both class and metaclass (Alex Waygood, PR [14988](https://github.com/python/mypy/pull/14988)) +* Handle type the same as typing.Type in the first argument of classmethods (Erik Kemperman, PR [15297](https://github.com/python/mypy/pull/15297)) +* Fix \--find-occurrences flag (Shantanu, PR [15528](https://github.com/python/mypy/pull/15528)) +* Fix error location for class patterns (Nikita Sobolev, PR [15506](https://github.com/python/mypy/pull/15506)) +* Fix re-added file with errors in mypy daemon (Ivan Levkivskyi, PR [15440](https://github.com/python/mypy/pull/15440)) +* Fix dmypy run on Windows (Ivan Levkivskyi, PR [15429](https://github.com/python/mypy/pull/15429)) +* Fix abstract and non-abstract variant error for property deleter (Shantanu, PR [15395](https://github.com/python/mypy/pull/15395)) +* Remove special casing for "cannot" in error messages (Ilya Priven, PR [15428](https://github.com/python/mypy/pull/15428)) +* Add runtime `__slots__` attribute to attrs classes (Nikita Sobolev, PR [15651](https://github.com/python/mypy/pull/15651)) +* Add get\_expression\_type to CheckerPluginInterface (Ilya Priven, PR [15369](https://github.com/python/mypy/pull/15369)) +* Remove parameters that no longer exist from NamedTuple.\_make() (Alex Waygood, PR [15578](https://github.com/python/mypy/pull/15578)) +* Allow using typing.Self in `__all__` with an explicit @staticmethod decorator (Erik Kemperman, PR [15353](https://github.com/python/mypy/pull/15353)) +* Fix self types in subclass methods without Self annotation (Ivan Levkivskyi, PR [15541](https://github.com/python/mypy/pull/15541)) +* Check for abstract class objects in tuples (Nikita Sobolev, PR [15366](https://github.com/python/mypy/pull/15366)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=fc7d4722eaa54803926cee5730e1f784979c0531+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Adel Atallah +* Alex Waygood +* Ali Hamdan +* Erik Kemperman +* Federico Padua +* Ilya Priven +* Ivan Levkivskyi +* Jelle Zijlstra +* Jared Hance +* Jukka Lehtosalo +* Kyle Benesch +* Logan Hunt +* Marc Mueller +* Nikita Sobolev +* Richard Si +* Shantanu +* Stavros Ntentos +* Valentin Stanciu + +Posted by Valentin Stanciu + + +## Mypy 1.4 + +[Tuesday, 20 June 2023](https://mypy-lang.blogspot.com/2023/06/mypy-140-released.html) + +We’ve just uploaded mypy 1.4 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### The Override Decorator + +Mypy can now ensure that when renaming a method, overrides are also renamed. You can explicitly mark a method as overriding a base class method by using the @typing.override decorator ([PEP 698](https://peps.python.org/pep-0698/)). If the method is then renamed in the base class while the method override is not, mypy will generate an error. The decorator will be available in typing in Python 3.12, but you can also use the backport from a recent version of `typing_extensions` on all supported Python versions. + +This feature was contributed byThomas M Kehrenberg (PR [14609](https://github.com/python/mypy/pull/14609)). + +#### Propagating Type Narrowing to Nested Functions + +Previously, type narrowing was not propagated to nested functions because it would not be sound if the narrowed variable changed between the definition of the nested function and the call site. Mypy will now propagate the narrowed type if the variable is not assigned to after the definition of the nested function: + +```python +def outer(x: str | None = None) -> None: + if x is None: + x = calculate_default() + reveal_type(x) # "str" (narrowed) + + def nested() -> None: + reveal_type(x) # Now "str" (used to be "str | None") + + nested() +``` + +This may generate some new errors because asserts that were previously necessary may become tautological or no-ops. + +This was contributed by Jukka Lehtosalo (PR [15133](https://github.com/python/mypy/pull/15133)). + +#### Narrowing Enum Values Using “==” + +Mypy now allows narrowing enum types using the \== operator. Previously this was only supported when using the is operator. This makes exhaustiveness checking with enum types more usable, as the requirement to use the is operator was not very intuitive. In this example mypy can detect that the developer forgot to handle the value MyEnum.C in example + +```python +from enum import Enum + +class MyEnum(Enum): + A = 0 + B = 1 + C = 2 + +def example(e: MyEnum) -> str: # Error: Missing return statement + if e == MyEnum.A: + return 'x' + elif e == MyEnum.B: + return 'y' +``` + +Adding an extra elif case resolves the error: + +```python +... +def example(e: MyEnum) -> str: # No error -- all values covered + if e == MyEnum.A: + return 'x' + elif e == MyEnum.B: + return 'y' + elif e == MyEnum.C: + return 'z' +``` + +This change can cause false positives in test cases that have assert statements like assert o.x == SomeEnum.X when using \--strict-equality. Example: + +```python +# mypy: strict-equality + +from enum import Enum + +class MyEnum(Enum): + A = 0 + B = 1 + +class C: + x: MyEnum + ... + +def test_something() -> None: + c = C(...) + assert c.x == MyEnum.A + c.do_something_that_changes_x() + assert c.x == MyEnum.B # Error: Non-overlapping equality check +``` + +These errors can be ignored using \# type: ignore\[comparison-overlap\], or you can perform the assertion using a temporary variable as a workaround: + +```python +... +def test_something() -> None: + ... + x = c.x + assert x == MyEnum.A # Does not narrow c.x + c.do_something_that_changes_x() + x = c.x + assert x == MyEnum.B # OK +``` + +This feature was contributed by Shantanu (PR [11521](https://github.com/python/mypy/pull/11521)). + +#### Performance Improvements + +* Speed up simplification of large union types and also fix a recursive tuple crash (Shantanu, PR [15128](https://github.com/python/mypy/pull/15128)) +* Speed up union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) +* Don't type check most function bodies when type checking third-party library code, or generally when ignoring errors (Jukka Lehtosalo, PR [14150](https://github.com/python/mypy/pull/14150)) + +#### Improvements to Plugins + +* attrs.evolve: Support generics and unions (Ilya Konstantinov, PR [15050](https://github.com/python/mypy/pull/15050)) +* Fix ctypes plugin (Alex Waygood) + +#### Fixes to Crashes + +* Fix a crash when function-scope recursive alias appears as upper bound (Ivan Levkivskyi, PR [15159](https://github.com/python/mypy/pull/15159)) +* Fix crash on follow\_imports\_for\_stubs (Ivan Levkivskyi, PR [15407](https://github.com/python/mypy/pull/15407)) +* Fix stubtest crash in explicit init subclass (Shantanu, PR [15399](https://github.com/python/mypy/pull/15399)) +* Fix crash when indexing TypedDict with empty key (Shantanu, PR [15392](https://github.com/python/mypy/pull/15392)) +* Fix crash on NamedTuple as attribute (Ivan Levkivskyi, PR [15404](https://github.com/python/mypy/pull/15404)) +* Correctly track loop depth for nested functions/classes (Ivan Levkivskyi, PR [15403](https://github.com/python/mypy/pull/15403)) +* Fix crash on joins with recursive tuples (Ivan Levkivskyi, PR [15402](https://github.com/python/mypy/pull/15402)) +* Fix crash with custom ErrorCode subclasses (Marc Mueller, PR [15327](https://github.com/python/mypy/pull/15327)) +* Fix crash in dataclass protocol with self attribute assignment (Ivan Levkivskyi, PR [15157](https://github.com/python/mypy/pull/15157)) +* Fix crash on lambda in generic context with generic method in body (Ivan Levkivskyi, PR [15155](https://github.com/python/mypy/pull/15155)) +* Fix recursive type alias crash in make\_simplified\_union (Ivan Levkivskyi, PR [15216](https://github.com/python/mypy/pull/15216)) + +#### Improvements to Error Messages + +* Use lower-case built-in collection types such as list\[…\] instead of List\[…\] in errors when targeting Python 3.9+ (Max Murin, PR [15070](https://github.com/python/mypy/pull/15070)) +* Use X | Y union syntax in error messages when targeting Python 3.10+ (Omar Silva, PR [15102](https://github.com/python/mypy/pull/15102)) +* Use type instead of Type in errors when targeting Python 3.9+ (Rohit Sanjay, PR [15139](https://github.com/python/mypy/pull/15139)) +* Do not show unused-ignore errors in unreachable code, and make it a real error code (Ivan Levkivskyi, PR [15164](https://github.com/python/mypy/pull/15164)) +* Don’t limit the number of errors shown by default (Rohit Sanjay, PR [15138](https://github.com/python/mypy/pull/15138)) +* Improver message for truthy functions (madt2709, PR [15193](https://github.com/python/mypy/pull/15193)) +* Output distinct types when type names are ambiguous (teresa0605, PR [15184](https://github.com/python/mypy/pull/15184)) +* Update message about invalid exception type in try (AJ Rasmussen, PR [15131](https://github.com/python/mypy/pull/15131)) +* Add explanation if argument type is incompatible because of an unsupported numbers type (Jukka Lehtosalo, PR [15137](https://github.com/python/mypy/pull/15137)) +* Add more detail to 'signature incompatible with supertype' messages for non-callables (Ilya Priven, PR [15263](https://github.com/python/mypy/pull/15263)) + +#### Documentation Updates + +* Add \--local-partial-types note to dmypy docs (Alan Du, PR [15259](https://github.com/python/mypy/pull/15259)) +* Update getting started docs for mypyc for Windows (Valentin Stanciu, PR [15233](https://github.com/python/mypy/pull/15233)) +* Clarify usage of callables regarding type object in docs (Viicos, PR [15079](https://github.com/python/mypy/pull/15079)) +* Clarify difference between disallow\_untyped\_defs and disallow\_incomplete\_defs (Ilya Priven, PR [15247](https://github.com/python/mypy/pull/15247)) +* Use attrs and @attrs.define in documentation and tests (Ilya Priven, PR [15152](https://github.com/python/mypy/pull/15152)) + +#### Mypyc Improvements + +* Fix unexpected TypeError for certain variables with an inferred optional type (Richard Si, PR [15206](https://github.com/python/mypy/pull/15206)) +* Inline math literals (Logan Hunt, PR [15324](https://github.com/python/mypy/pull/15324)) +* Support unpacking mappings in dict display (Richard Si, PR [15203](https://github.com/python/mypy/pull/15203)) + +#### Changes to Stubgen + +* Do not remove Generic from base classes (Ali Hamdan, PR [15316](https://github.com/python/mypy/pull/15316)) +* Support yield from statements (Ali Hamdan, PR [15271](https://github.com/python/mypy/pull/15271)) +* Fix missing total from TypedDict class (Ali Hamdan, PR [15208](https://github.com/python/mypy/pull/15208)) +* Fix call-based namedtuple omitted from class bases (Ali Hamdan, PR [14680](https://github.com/python/mypy/pull/14680)) +* Support TypedDict alternative syntax (Ali Hamdan, PR [14682](https://github.com/python/mypy/pull/14682)) +* Make stubgen respect MYPY\_CACHE\_DIR (Henrik Bäärnhielm, PR [14722](https://github.com/python/mypy/pull/14722)) +* Fixes and simplifications (Ali Hamdan, PR [15232](https://github.com/python/mypy/pull/15232)) + +#### Other Notable Fixes and Improvements + +* Fix nested async functions when using TypeVar value restriction (Jukka Lehtosalo, PR [14705](https://github.com/python/mypy/pull/14705)) +* Always allow returning Any from lambda (Ivan Levkivskyi, PR [15413](https://github.com/python/mypy/pull/15413)) +* Add foundation for TypeVar defaults (PEP 696) (Marc Mueller, PR [14872](https://github.com/python/mypy/pull/14872)) +* Update semantic analyzer for TypeVar defaults (PEP 696) (Marc Mueller, PR [14873](https://github.com/python/mypy/pull/14873)) +* Make dict expression inference more consistent (Ivan Levkivskyi, PR [15174](https://github.com/python/mypy/pull/15174)) +* Do not block on duplicate base classes (Nikita Sobolev, PR [15367](https://github.com/python/mypy/pull/15367)) +* Generate an error when both staticmethod and classmethod decorators are used (Juhi Chandalia, PR [15118](https://github.com/python/mypy/pull/15118)) +* Fix assert\_type behaviour with literals (Carl Karsten, PR [15123](https://github.com/python/mypy/pull/15123)) +* Fix match subject ignoring redefinitions (Vincent Vanlaer, PR [15306](https://github.com/python/mypy/pull/15306)) +* Support `__all__`.remove (Shantanu, PR [15279](https://github.com/python/mypy/pull/15279)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=877e06ad1cfd9fd9967c0b0340a86d0c23ea89ce+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Adrian Garcia Badaracco +* AJ Rasmussen +* Alan Du +* Alex Waygood +* Ali Hamdan +* Carl Karsten +* dosisod +* Ethan Smith +* Gregory Santosa +* Heather White +* Henrik Bäärnhielm +* Ilya Konstantinov +* Ilya Priven +* Ivan Levkivskyi +* Juhi Chandalia +* Jukka Lehtosalo +* Logan Hunt +* madt2709 +* Marc Mueller +* Max Murin +* Nikita Sobolev +* Omar Silva +* Özgür +* Richard Si +* Rohit Sanjay +* Shantanu +* teresa0605 +* Thomas M Kehrenberg +* Tin Tvrtković +* Tushar Sadhwani +* Valentin Stanciu +* Viicos +* Vincent Vanlaer +* Wesley Collin Wright +* William Santosa +* yaegassy + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +Posted by Jared Hance + + +## Mypy 1.3 + +[Wednesday, 10 May 2023](https://mypy-lang.blogspot.com/2023/05/mypy-13-released.html) + + We’ve just uploaded mypy 1.3 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Performance Improvements + +* Improve performance of union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) +* Add negative subtype caches (Ivan Levkivskyi, PR [14884](https://github.com/python/mypy/pull/14884)) + +#### Stub Tooling Improvements + +* Stubtest: Check that the stub is abstract if the runtime is, even when the stub is an overloaded method (Alex Waygood, PR [14955](https://github.com/python/mypy/pull/14955)) +* Stubtest: Verify stub methods or properties are decorated with @final if they are decorated with @final at runtime (Alex Waygood, PR [14951](https://github.com/python/mypy/pull/14951)) +* Stubtest: Fix stubtest false positives with TypedDicts at runtime (Alex Waygood, PR [14984](https://github.com/python/mypy/pull/14984)) +* Stubgen: Support @functools.cached\_property (Nikita Sobolev, PR [14981](https://github.com/python/mypy/pull/14981)) +* Improvements to stubgenc (Chad Dombrova, PR [14564](https://github.com/python/mypy/pull/14564)) + +#### Improvements to attrs + +* Add support for converters with TypeVars on generic attrs classes (Chad Dombrova, PR [14908](https://github.com/python/mypy/pull/14908)) +* Fix attrs.evolve on bound TypeVar (Ilya Konstantinov, PR [15022](https://github.com/python/mypy/pull/15022)) + +#### Documentation Updates + +* Improve async documentation (Shantanu, PR [14973](https://github.com/python/mypy/pull/14973)) +* Improvements to cheat sheet (Shantanu, PR [14972](https://github.com/python/mypy/pull/14972)) +* Add documentation for bytes formatting error code (Shantanu, PR [14971](https://github.com/python/mypy/pull/14971)) +* Convert insecure links to use HTTPS (Marti Raudsepp, PR [14974](https://github.com/python/mypy/pull/14974)) +* Also mention overloads in async iterator documentation (Shantanu, PR [14998](https://github.com/python/mypy/pull/14998)) +* stubtest: Improve allowlist documentation (Shantanu, PR [15008](https://github.com/python/mypy/pull/15008)) +* Clarify "Using types... but not at runtime" (Jon Shea, PR [15029](https://github.com/python/mypy/pull/15029)) +* Fix alignment of cheat sheet example (Ondřej Cvacho, PR [15039](https://github.com/python/mypy/pull/15039)) +* Fix error for callback protocol matching against callable type object (Shantanu, PR [15042](https://github.com/python/mypy/pull/15042)) + +#### Error Reporting Improvements + +* Improve bytes formatting error (Shantanu, PR [14959](https://github.com/python/mypy/pull/14959)) + +#### Mypyc Improvements + +* Fix unions of bools and ints (Tomer Chachamu, PR [15066](https://github.com/python/mypy/pull/15066)) + +#### Other Fixes and Improvements + +* Fix narrowing union types that include Self with isinstance (Christoph Tyralla, PR [14923](https://github.com/python/mypy/pull/14923)) +* Allow objects matching SupportsKeysAndGetItem to be unpacked (Bryan Forbes, PR [14990](https://github.com/python/mypy/pull/14990)) +* Check type guard validity for staticmethods (EXPLOSION, PR [14953](https://github.com/python/mypy/pull/14953)) +* Fix sys.platform when cross-compiling with emscripten (Ethan Smith, PR [14888](https://github.com/python/mypy/pull/14888)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=b0ed50e9392a23e52445b630a808153e0e256976+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Alex Waygood +* Amin Alaee +* Bryan Forbes +* Chad Dombrova +* Charlie Denton +* Christoph Tyralla +* dosisod +* Ethan Smith +* EXPLOSION +* Ilya Konstantinov +* Ivan Levkivskyi +* Jon Shea +* Jukka Lehtosalo +* KotlinIsland +* Marti Raudsepp +* Nikita Sobolev +* Ondřej Cvacho +* Shantanu +* sobolevn +* Tomer Chachamu +* Yaroslav Halchenko + +Posted by Wesley Collin Wright. + + +## Mypy 1.2 + +[Thursday, 6 April 2023](https://mypy-lang.blogspot.com/2023/04/mypy-12-released.html) + +We’ve just uploaded mypy 1.2 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Improvements to Dataclass Transforms + +* Support implicit default for "init" parameter in field specifiers (Wesley Collin Wright and Jukka Lehtosalo, PR [15010](https://github.com/python/mypy/pull/15010)) +* Support descriptors in dataclass transform (Jukka Lehtosalo, PR [15006](https://github.com/python/mypy/pull/15006)) +* Fix frozen\_default in incremental mode (Wesley Collin Wright) +* Fix frozen behavior for base classes with direct metaclasses (Wesley Collin Wright, PR [14878](https://github.com/python/mypy/pull/14878)) + +#### Mypyc: Native Floats + +Mypyc now uses a native, unboxed representation for values of type float. Previously these were heap-allocated Python objects. Native floats are faster and use less memory. Code that uses floating-point operations heavily can be several times faster when using native floats. + +Various float operations and math functions also now have optimized implementations. Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/float_operations.html) for a full list. + +This can change the behavior of existing code that uses subclasses of float. When assigning an instance of a subclass of float to a variable with the float type, it gets implicitly converted to a float instance when compiled: + +```python +from lib import MyFloat # MyFloat ia a subclass of "float" + +def example() -> None: + x = MyFloat(1.5) + y: float = x # Implicit conversion from MyFloat to float + print(type(y)) # float, not MyFloat +``` + +Previously, implicit conversions were applied to int subclasses but not float subclasses. + +Also, int values can no longer be assigned to a variable with type float in compiled code, since these types now have incompatible representations. An explicit conversion is required: + +```python +def example(n: int) -> None: + a: float = 1 # Error: cannot assign "int" to "float" + b: float = 1.0 # OK + c: float = n # Error + d: float = float(n) # OK +``` + +This restriction only applies to assignments, since they could otherwise narrow down the type of a variable from float to int. int values can still be implicitly converted to float when passed as arguments to functions that expect float values. + +Note that mypyc still doesn’t support arrays of unboxed float values. Using list\[float\] involves heap-allocated float objects, since list can only store boxed values. Support for efficient floating point arrays is one of the next major planned mypyc features. + +Related changes: + +* Use a native unboxed representation for floats (Jukka Lehtosalo, PR [14880](https://github.com/python/mypy/pull/14880)) +* Document native floats and integers (Jukka Lehtosalo, PR [14927](https://github.com/python/mypy/pull/14927)) +* Fixes to float to int conversion (Jukka Lehtosalo, PR [14936](https://github.com/python/mypy/pull/14936)) + +#### Mypyc: Native Integers + +Mypyc now supports signed 32-bit and 64-bit integer types in addition to the arbitrary-precision int type. You can use the types mypy\_extensions.i32 and mypy\_extensions.i64 to speed up code that uses integer operations heavily. + +Simple example: +```python +from mypy_extensions import i64 + +def inc(x: i64) -> i64: + return x + 1 +``` + +Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/using_type_annotations.html#native-integer-types) for more information. This feature was contributed by Jukka Lehtosalo. + +#### Other Mypyc Fixes and Improvements + +* Support iterating over a TypedDict (Richard Si, PR [14747](https://github.com/python/mypy/pull/14747)) +* Faster coercions between different tuple types (Jukka Lehtosalo, PR [14899](https://github.com/python/mypy/pull/14899)) +* Faster calls via type aliases (Jukka Lehtosalo, PR [14784](https://github.com/python/mypy/pull/14784)) +* Faster classmethod calls via cls (Jukka Lehtosalo, PR [14789](https://github.com/python/mypy/pull/14789)) + +#### Fixes to Crashes + +* Fix crash on class-level import in protocol definition (Ivan Levkivskyi, PR [14926](https://github.com/python/mypy/pull/14926)) +* Fix crash on single item union of alias (Ivan Levkivskyi, PR [14876](https://github.com/python/mypy/pull/14876)) +* Fix crash on ParamSpec in incremental mode (Ivan Levkivskyi, PR [14885](https://github.com/python/mypy/pull/14885)) + +#### Documentation Updates + +* Update adopting \--strict documentation for 1.0 (Shantanu, PR [14865](https://github.com/python/mypy/pull/14865)) +* Some minor documentation tweaks (Jukka Lehtosalo, PR [14847](https://github.com/python/mypy/pull/14847)) +* Improve documentation of top level mypy: disable-error-code comment (Nikita Sobolev, PR [14810](https://github.com/python/mypy/pull/14810)) + +#### Error Reporting Improvements + +* Add error code to `typing_extensions` suggestion (Shantanu, PR [14881](https://github.com/python/mypy/pull/14881)) +* Add a separate error code for top-level await (Nikita Sobolev, PR [14801](https://github.com/python/mypy/pull/14801)) +* Don’t suggest two obsolete stub packages (Jelle Zijlstra, PR [14842](https://github.com/python/mypy/pull/14842)) +* Add suggestions for pandas-stubs and lxml-stubs (Shantanu, PR [14737](https://github.com/python/mypy/pull/14737)) + +#### Other Fixes and Improvements + +* Multiple inheritance considers callable objects as subtypes of functions (Christoph Tyralla, PR [14855](https://github.com/python/mypy/pull/14855)) +* stubtest: Respect @final runtime decorator and enforce it in stubs (Nikita Sobolev, PR [14922](https://github.com/python/mypy/pull/14922)) +* Fix false positives related to type\[\] (sterliakov, PR [14756](https://github.com/python/mypy/pull/14756)) +* Fix duplication of ParamSpec prefixes and properly substitute ParamSpecs (EXPLOSION, PR [14677](https://github.com/python/mypy/pull/14677)) +* Fix line number if `__iter__` is incorrectly reported as missing (Jukka Lehtosalo, PR [14893](https://github.com/python/mypy/pull/14893)) +* Fix incompatible overrides of overloaded generics with self types (Shantanu, PR [14882](https://github.com/python/mypy/pull/14882)) +* Allow SupportsIndex in slice expressions (Shantanu, PR [14738](https://github.com/python/mypy/pull/14738)) +* Support if statements in bodies of dataclasses and classes that use dataclass\_transform (Jacek Chałupka, PR [14854](https://github.com/python/mypy/pull/14854)) +* Allow iterable class objects to be unpacked (including enums) (Alex Waygood, PR [14827](https://github.com/python/mypy/pull/14827)) +* Fix narrowing for walrus expressions used in match statements (Shantanu, PR [14844](https://github.com/python/mypy/pull/14844)) +* Add signature for attr.evolve (Ilya Konstantinov, PR [14526](https://github.com/python/mypy/pull/14526)) +* Fix Any inference when unpacking iterators that don't directly inherit from typing.Iterator (Alex Waygood, PR [14821](https://github.com/python/mypy/pull/14821)) +* Fix unpack with overloaded `__iter__` method (Nikita Sobolev, PR [14817](https://github.com/python/mypy/pull/14817)) +* Reduce size of JSON data in mypy cache (dosisod, PR [14808](https://github.com/python/mypy/pull/14808)) +* Improve “used before definition” checks when a local definition has the same name as a global definition (Stas Ilinskiy, PR [14517](https://github.com/python/mypy/pull/14517)) +* Honor NoReturn as \_\_setitem\_\_ return type to mark unreachable code (sterliakov, PR [12572](https://github.com/python/mypy/pull/12572)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=a544b75320e97424d2d927605316383c755cdac0+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Alex Waygood +* Avasam +* Christoph Tyralla +* dosisod +* EXPLOSION +* Ilya Konstantinov +* Ivan Levkivskyi +* Jacek Chałupka +* Jelle Zijlstra +* Jukka Lehtosalo +* Marc Mueller +* Max Murin +* Nikita Sobolev +* Richard Si +* Shantanu +* Stas Ilinskiy +* sterliakov +* Wesley Collin Wright + +Posted by Jukka Lehtosalo + + +## Mypy 1.1.1 + +[Monday, 6 March 2023](https://mypy-lang.blogspot.com/2023/03/mypy-111-released.html) + + We’ve just uploaded mypy 1.1.1 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Support for `dataclass_transform`` + +This release adds full support for the dataclass\_transform decorator defined in [PEP 681](https://peps.python.org/pep-0681/#decorator-function-example). This allows decorators, base classes, and metaclasses that generate a \_\_init\_\_ method or other methods based on the properties of that class (similar to dataclasses) to have those methods recognized by mypy. + +This was contributed by Wesley Collin Wright. + +#### Dedicated Error Code for Method Assignments + +Mypy can’t safely check all assignments to methods (a form of monkey patching), so mypy generates an error by default. To make it easier to ignore this error, mypy now uses the new error code method-assign for this. By disabling this error code in a file or globally, mypy will no longer complain about assignments to methods if the signatures are compatible. + +Mypy also supports the old error code assignment for these assignments to prevent a backward compatibility break. More generally, we can use this mechanism in the future if we wish to split or rename another existing error code without causing backward compatibility issues. + +This was contributed by Ivan Levkivskyi (PR [14570](https://github.com/python/mypy/pull/14570)). + +#### Fixes to Crashes + +* Fix a crash on walrus in comprehension at class scope (Ivan Levkivskyi, PR [14556](https://github.com/python/mypy/pull/14556)) +* Fix crash related to value-constrained TypeVar (Shantanu, PR [14642](https://github.com/python/mypy/pull/14642)) + +#### Fixes to Cache Corruption + +* Fix generic TypedDict/NamedTuple caching (Ivan Levkivskyi, PR [14675](https://github.com/python/mypy/pull/14675)) + +#### Mypyc Fixes and Improvements + +* Raise "non-trait base must be first..." error less frequently (Richard Si, PR [14468](https://github.com/python/mypy/pull/14468)) +* Generate faster code for bool comparisons and arithmetic (Jukka Lehtosalo, PR [14489](https://github.com/python/mypy/pull/14489)) +* Optimize \_\_(a)enter\_\_/\_\_(a)exit\_\_ for native classes (Jared Hance, PR [14530](https://github.com/python/mypy/pull/14530)) +* Detect if attribute definition conflicts with base class/trait (Jukka Lehtosalo, PR [14535](https://github.com/python/mypy/pull/14535)) +* Support \_\_(r)divmod\_\_ dunders (Richard Si, PR [14613](https://github.com/python/mypy/pull/14613)) +* Support \_\_pow\_\_, \_\_rpow\_\_, and \_\_ipow\_\_ dunders (Richard Si, PR [14616](https://github.com/python/mypy/pull/14616)) +* Fix crash on star unpacking to underscore (Ivan Levkivskyi, PR [14624](https://github.com/python/mypy/pull/14624)) +* Fix iterating over a union of dicts (Richard Si, PR [14713](https://github.com/python/mypy/pull/14713)) + +#### Fixes to Detecting Undefined Names (used-before-def) + +* Correctly handle walrus operator (Stas Ilinskiy, PR [14646](https://github.com/python/mypy/pull/14646)) +* Handle walrus declaration in match subject correctly (Stas Ilinskiy, PR [14665](https://github.com/python/mypy/pull/14665)) + +#### Stubgen Improvements + +Stubgen is a tool for automatically generating draft stubs for libraries. + +* Allow aliases below the top level (Chad Dombrova, PR [14388](https://github.com/python/mypy/pull/14388)) +* Fix crash with PEP 604 union in type variable bound (Shantanu, PR [14557](https://github.com/python/mypy/pull/14557)) +* Preserve PEP 604 unions in generated .pyi files (hamdanal, PR [14601](https://github.com/python/mypy/pull/14601)) + +#### Stubtest Improvements + +Stubtest is a tool for testing that stubs conform to the implementations. + +* Update message format so that it’s easier to go to error location (Avasam, PR [14437](https://github.com/python/mypy/pull/14437)) +* Handle name-mangling edge cases better (Alex Waygood, PR [14596](https://github.com/python/mypy/pull/14596)) + +#### Changes to Error Reporting and Messages + +* Add new TypedDict error code typeddict-unknown-key (JoaquimEsteves, PR [14225](https://github.com/python/mypy/pull/14225)) +* Give arguments a more reasonable location in error messages (Max Murin, PR [14562](https://github.com/python/mypy/pull/14562)) +* In error messages, quote just the module's name (Ilya Konstantinov, PR [14567](https://github.com/python/mypy/pull/14567)) +* Improve misleading message about Enum() (Rodrigo Silva, PR [14590](https://github.com/python/mypy/pull/14590)) +* Suggest importing from `typing_extensions` if definition is not in typing (Shantanu, PR [14591](https://github.com/python/mypy/pull/14591)) +* Consistently use type-abstract error code (Ivan Levkivskyi, PR [14619](https://github.com/python/mypy/pull/14619)) +* Consistently use literal-required error code for TypedDicts (Ivan Levkivskyi, PR [14621](https://github.com/python/mypy/pull/14621)) +* Adjust inconsistent dataclasses plugin error messages (Wesley Collin Wright, PR [14637](https://github.com/python/mypy/pull/14637)) +* Consolidate literal bool argument error messages (Wesley Collin Wright, PR [14693](https://github.com/python/mypy/pull/14693)) + +#### Other Fixes and Improvements + +* Check that type guards accept a positional argument (EXPLOSION, PR [14238](https://github.com/python/mypy/pull/14238)) +* Fix bug with in operator used with a union of Container and Iterable (Max Murin, PR [14384](https://github.com/python/mypy/pull/14384)) +* Support protocol inference for type\[T\] via metaclass (Ivan Levkivskyi, PR [14554](https://github.com/python/mypy/pull/14554)) +* Allow overlapping comparisons between bytes-like types (Shantanu, PR [14658](https://github.com/python/mypy/pull/14658)) +* Fix mypy daemon documentation link in README (Ivan Levkivskyi, PR [14644](https://github.com/python/mypy/pull/14644)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=5ebf892d0710a6e87925b8d138dfa597e7bb11cc+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Alex Waygood +* Avasam +* Chad Dombrova +* dosisod +* EXPLOSION +* hamdanal +* Ilya Konstantinov +* Ivan Levkivskyi +* Jared Hance +* JoaquimEsteves +* Jukka Lehtosalo +* Marc Mueller +* Max Murin +* Michael Lee +* Michael R. Crusoe +* Richard Si +* Rodrigo Silva +* Shantanu +* Stas Ilinskiy +* Wesley Collin Wright +* Yilei "Dolee" Yang +* Yurii Karabas + +We’d also like to thank our employer, Dropbox, for funding the mypy core team. + +Posted by Max Murin + + +## Mypy 1.0 + +[Monday, 6 February 2023](https://mypy-lang.blogspot.com/2023/02/mypy-10-released.html) + +We’ve just uploaded mypy 1.0 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### New Release Versioning Scheme + +Now that mypy reached 1.0, we’ll switch to a new versioning scheme. Mypy version numbers will be of form x.y.z. + +Rules: + +* The major release number (x) is incremented if a feature release includes a significant backward incompatible change that affects a significant fraction of users. +* The minor release number (y) is incremented on each feature release. Minor releases include updated stdlib stubs from typeshed. +* The point release number (z) is incremented when there are fixes only. + +Mypy doesn't use SemVer, since most minor releases have at least minor backward incompatible changes in typeshed, at the very least. Also, many type checking features find new legitimate issues in code. These are not considered backward incompatible changes, unless the number of new errors is very high. + +Any significant backward incompatible change must be announced in the blog post for the previous feature release, before making the change. The previous release must also provide a flag to explicitly enable or disable the new behavior (whenever practical), so that users will be able to prepare for the changes and report issues. We should keep the feature flag for at least a few releases after we've switched the default. + +See [”Release Process” in the mypy wiki](https://github.com/python/mypy/wiki/Release-Process) for more details and for the most up-to-date version of the versioning scheme. + +#### Performance Improvements + +Mypy 1.0 is up to 40% faster than mypy 0.991 when type checking the Dropbox internal codebase. We also set up a daily job to measure the performance of the most recent development version of mypy to make it easier to track changes in performance. + +Many optimizations contributed to this improvement: + +* Improve performance for errors on class with many attributes (Shantanu, PR [14379](https://github.com/python/mypy/pull/14379)) +* Speed up make\_simplified\_union (Jukka Lehtosalo, PR [14370](https://github.com/python/mypy/pull/14370)) +* Micro-optimize get\_proper\_type(s) (Jukka Lehtosalo, PR [14369](https://github.com/python/mypy/pull/14369)) +* Micro-optimize flatten\_nested\_unions (Jukka Lehtosalo, PR [14368](https://github.com/python/mypy/pull/14368)) +* Some semantic analyzer micro-optimizations (Jukka Lehtosalo, PR [14367](https://github.com/python/mypy/pull/14367)) +* A few miscellaneous micro-optimizations (Jukka Lehtosalo, PR [14366](https://github.com/python/mypy/pull/14366)) +* Optimization: Avoid a few uses of contextmanagers in semantic analyzer (Jukka Lehtosalo, PR [14360](https://github.com/python/mypy/pull/14360)) +* Optimization: Enable always defined attributes in Type subclasses (Jukka Lehtosalo, PR [14356](https://github.com/python/mypy/pull/14356)) +* Optimization: Remove expensive context manager in type analyzer (Jukka Lehtosalo, PR [14357](https://github.com/python/mypy/pull/14357)) +* subtypes: fast path for Union/Union subtype check (Hugues, PR [14277](https://github.com/python/mypy/pull/14277)) +* Micro-optimization: avoid Bogus\[int\] types that cause needless boxing (Jukka Lehtosalo, PR [14354](https://github.com/python/mypy/pull/14354)) +* Avoid slow error message logic if errors not shown to user (Jukka Lehtosalo, PR [14336](https://github.com/python/mypy/pull/14336)) +* Speed up the implementation of hasattr() checks (Jukka Lehtosalo, PR [14333](https://github.com/python/mypy/pull/14333)) +* Avoid the use of a context manager in hot code path (Jukka Lehtosalo, PR [14331](https://github.com/python/mypy/pull/14331)) +* Change various type queries into faster bool type queries (Jukka Lehtosalo, PR [14330](https://github.com/python/mypy/pull/14330)) +* Speed up recursive type check (Jukka Lehtosalo, PR [14326](https://github.com/python/mypy/pull/14326)) +* Optimize subtype checking by avoiding a nested function (Jukka Lehtosalo, PR [14325](https://github.com/python/mypy/pull/14325)) +* Optimize type parameter checks in subtype checking (Jukka Lehtosalo, PR [14324](https://github.com/python/mypy/pull/14324)) +* Speed up freshening type variables (Jukka Lehtosalo, PR [14323](https://github.com/python/mypy/pull/14323)) +* Optimize implementation of TypedDict types for \*\*kwds (Jukka Lehtosalo, PR [14316](https://github.com/python/mypy/pull/14316)) + +#### Warn About Variables Used Before Definition + +Mypy will now generate an error if you use a variable before it’s defined. This feature is enabled by default. By default mypy reports an error when it infers that a variable is always undefined. +```python +y = x # E: Name "x" is used before definition [used-before-def] +x = 0 +``` +This feature was contributed by Stas Ilinskiy. + +#### Detect Possibly Undefined Variables (Experimental) + +A new experimental possibly-undefined error code is now available that will detect variables that may be undefined: +```python + if b: + x = 0 + print(x) # Error: Name "x" may be undefined [possibly-undefined] +``` +The error code is disabled be default, since it can generate false positives. + +This feature was contributed by Stas Ilinskiy. + +#### Support the “Self” Type + +There is now a simpler syntax for declaring [generic self types](https://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self) introduced in [PEP 673](https://peps.python.org/pep-0673/): the Self type. You no longer have to define a type variable to use “self types”, and you can use them with attributes. Example from mypy documentation: +```python +from typing import Self + +class Friend: + other: Self | None = None + + @classmethod + def make_pair(cls) -> tuple[Self, Self]: + a, b = cls(), cls() + a.other = b + b.other = a + return a, b + +class SuperFriend(Friend): + pass + +# a and b have the inferred type "SuperFriend", not "Friend" +a, b = SuperFriend.make_pair() +``` +The feature was introduced in Python 3.11. In earlier Python versions a backport of Self is available in `typing_extensions`. + +This was contributed by Ivan Levkivskyi (PR [14041](https://github.com/python/mypy/pull/14041)). + +#### Support ParamSpec in Type Aliases + +ParamSpec and Concatenate can now be used in type aliases. Example: +```python +from typing import ParamSpec, Callable + +P = ParamSpec("P") +A = Callable[P, None] + +def f(c: A[int, str]) -> None: + c(1, "x") +``` +This feature was contributed by Ivan Levkivskyi (PR [14159](https://github.com/python/mypy/pull/14159)). + +#### ParamSpec and Generic Self Types No Longer Experimental + +Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and generic self types are no longer considered experimental. + +#### Miscellaneous New Features + +* Minimal, partial implementation of dataclass\_transform ([PEP 681](https://peps.python.org/pep-0681/)) (Wesley Collin Wright, PR [14523](https://github.com/python/mypy/pull/14523)) +* Add basic support for `typing_extensions`.TypeVar (Marc Mueller, PR [14313](https://github.com/python/mypy/pull/14313)) +* Add \--debug-serialize option (Marc Mueller, PR [14155](https://github.com/python/mypy/pull/14155)) +* Constant fold initializers of final variables (Jukka Lehtosalo, PR [14283](https://github.com/python/mypy/pull/14283)) +* Enable Final instance attributes for attrs (Tin Tvrtković, PR [14232](https://github.com/python/mypy/pull/14232)) +* Allow function arguments as base classes (Ivan Levkivskyi, PR [14135](https://github.com/python/mypy/pull/14135)) +* Allow super() with mixin protocols (Ivan Levkivskyi, PR [14082](https://github.com/python/mypy/pull/14082)) +* Add type inference for dict.keys membership (Matthew Hughes, PR [13372](https://github.com/python/mypy/pull/13372)) +* Generate error for class attribute access if attribute is defined with `__slots__` (Harrison McCarty, PR [14125](https://github.com/python/mypy/pull/14125)) +* Support additional attributes in callback protocols (Ivan Levkivskyi, PR [14084](https://github.com/python/mypy/pull/14084)) + +#### Fixes to Crashes + +* Fix crash on prefixed ParamSpec with forward reference (Ivan Levkivskyi, PR [14569](https://github.com/python/mypy/pull/14569)) +* Fix internal crash when resolving the same partial type twice (Shantanu, PR [14552](https://github.com/python/mypy/pull/14552)) +* Fix crash in daemon mode on new import cycle (Ivan Levkivskyi, PR [14508](https://github.com/python/mypy/pull/14508)) +* Fix crash in mypy daemon (Ivan Levkivskyi, PR [14497](https://github.com/python/mypy/pull/14497)) +* Fix crash on Any metaclass in incremental mode (Ivan Levkivskyi, PR [14495](https://github.com/python/mypy/pull/14495)) +* Fix crash in await inside comprehension outside function (Ivan Levkivskyi, PR [14486](https://github.com/python/mypy/pull/14486)) +* Fix crash in Self type on forward reference in upper bound (Ivan Levkivskyi, PR [14206](https://github.com/python/mypy/pull/14206)) +* Fix a crash when incorrect super() is used outside a method (Ivan Levkivskyi, PR [14208](https://github.com/python/mypy/pull/14208)) +* Fix crash on overriding with frozen attrs (Ivan Levkivskyi, PR [14186](https://github.com/python/mypy/pull/14186)) +* Fix incremental mode crash on generic function appearing in nested position (Ivan Levkivskyi, PR [14148](https://github.com/python/mypy/pull/14148)) +* Fix daemon crash on malformed NamedTuple (Ivan Levkivskyi, PR [14119](https://github.com/python/mypy/pull/14119)) +* Fix crash during ParamSpec inference (Ivan Levkivskyi, PR [14118](https://github.com/python/mypy/pull/14118)) +* Fix crash on nested generic callable (Ivan Levkivskyi, PR [14093](https://github.com/python/mypy/pull/14093)) +* Fix crashes with unpacking SyntaxError (Shantanu, PR [11499](https://github.com/python/mypy/pull/11499)) +* Fix crash on partial type inference within a lambda (Ivan Levkivskyi, PR [14087](https://github.com/python/mypy/pull/14087)) +* Fix crash with enums (Michael Lee, PR [14021](https://github.com/python/mypy/pull/14021)) +* Fix crash with malformed TypedDicts and disllow-any-expr (Michael Lee, PR [13963](https://github.com/python/mypy/pull/13963)) + +#### Error Reporting Improvements + +* More helpful error for missing self (Shantanu, PR [14386](https://github.com/python/mypy/pull/14386)) +* Add error-code truthy-iterable (Marc Mueller, PR [13762](https://github.com/python/mypy/pull/13762)) +* Fix pluralization in error messages (KotlinIsland, PR [14411](https://github.com/python/mypy/pull/14411)) + +#### Mypyc: Support Match Statement + +Mypyc can now compile Python 3.10 match statements. + +This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/13953)). + +#### Other Mypyc Fixes and Improvements + +* Optimize int(x)/float(x)/complex(x) on instances of native classes (Richard Si, PR [14450](https://github.com/python/mypy/pull/14450)) +* Always emit warnings (Richard Si, PR [14451](https://github.com/python/mypy/pull/14451)) +* Faster bool and integer conversions (Jukka Lehtosalo, PR [14422](https://github.com/python/mypy/pull/14422)) +* Support attributes that override properties (Jukka Lehtosalo, PR [14377](https://github.com/python/mypy/pull/14377)) +* Precompute set literals for "in" operations and iteration (Richard Si, PR [14409](https://github.com/python/mypy/pull/14409)) +* Don't load targets with forward references while setting up non-extension class `__all__` (Richard Si, PR [14401](https://github.com/python/mypy/pull/14401)) +* Compile away NewType type calls (Richard Si, PR [14398](https://github.com/python/mypy/pull/14398)) +* Improve error message for multiple inheritance (Joshua Bronson, PR [14344](https://github.com/python/mypy/pull/14344)) +* Simplify union types (Jukka Lehtosalo, PR [14363](https://github.com/python/mypy/pull/14363)) +* Fixes to union simplification (Jukka Lehtosalo, PR [14364](https://github.com/python/mypy/pull/14364)) +* Fix for typeshed changes to Collection (Shantanu, PR [13994](https://github.com/python/mypy/pull/13994)) +* Allow use of enum.Enum (Shantanu, PR [13995](https://github.com/python/mypy/pull/13995)) +* Fix compiling on Arch Linux (dosisod, PR [13978](https://github.com/python/mypy/pull/13978)) + +#### Documentation Improvements + +* Various documentation and error message tweaks (Jukka Lehtosalo, PR [14574](https://github.com/python/mypy/pull/14574)) +* Improve Generics documentation (Shantanu, PR [14587](https://github.com/python/mypy/pull/14587)) +* Improve protocols documentation (Shantanu, PR [14577](https://github.com/python/mypy/pull/14577)) +* Improve dynamic typing documentation (Shantanu, PR [14576](https://github.com/python/mypy/pull/14576)) +* Improve the Common Issues page (Shantanu, PR [14581](https://github.com/python/mypy/pull/14581)) +* Add a top-level TypedDict page (Shantanu, PR [14584](https://github.com/python/mypy/pull/14584)) +* More improvements to getting started documentation (Shantanu, PR [14572](https://github.com/python/mypy/pull/14572)) +* Move truthy-function documentation from “optional checks” to “enabled by default” (Anders Kaseorg, PR [14380](https://github.com/python/mypy/pull/14380)) +* Avoid use of implicit optional in decorator factory documentation (Tom Schraitle, PR [14156](https://github.com/python/mypy/pull/14156)) +* Clarify documentation surrounding install-types (Shantanu, PR [14003](https://github.com/python/mypy/pull/14003)) +* Improve searchability for module level type ignore errors (Shantanu, PR [14342](https://github.com/python/mypy/pull/14342)) +* Advertise mypy daemon in README (Ivan Levkivskyi, PR [14248](https://github.com/python/mypy/pull/14248)) +* Add link to error codes in README (Ivan Levkivskyi, PR [14249](https://github.com/python/mypy/pull/14249)) +* Document that report generation disables cache (Ilya Konstantinov, PR [14402](https://github.com/python/mypy/pull/14402)) +* Stop saying mypy is beta software (Ivan Levkivskyi, PR [14251](https://github.com/python/mypy/pull/14251)) +* Flycheck-mypy is deprecated, since its functionality was merged to Flycheck (Ivan Levkivskyi, PR [14247](https://github.com/python/mypy/pull/14247)) +* Update code example in "Declaring decorators" (ChristianWitzler, PR [14131](https://github.com/python/mypy/pull/14131)) + +#### Stubtest Improvements + +Stubtest is a tool for testing that stubs conform to the implementations. + +* Improve error message for `__all__`\-related errors (Alex Waygood, PR [14362](https://github.com/python/mypy/pull/14362)) +* Improve heuristics for determining whether global-namespace names are imported (Alex Waygood, PR [14270](https://github.com/python/mypy/pull/14270)) +* Catch BaseException on module imports (Shantanu, PR [14284](https://github.com/python/mypy/pull/14284)) +* Associate exported symbol error with `__all__` object\_path (Nikita Sobolev, PR [14217](https://github.com/python/mypy/pull/14217)) +* Add \_\_warningregistry\_\_ to the list of ignored module dunders (Nikita Sobolev, PR [14218](https://github.com/python/mypy/pull/14218)) +* If a default is present in the stub, check that it is correct (Jelle Zijlstra, PR [14085](https://github.com/python/mypy/pull/14085)) + +#### Stubgen Improvements + +Stubgen is a tool for automatically generating draft stubs for libraries. + +* Treat dlls as C modules (Shantanu, PR [14503](https://github.com/python/mypy/pull/14503)) + +#### Other Notable Fixes and Improvements + +* Update stub suggestions based on recent typeshed changes (Alex Waygood, PR [14265](https://github.com/python/mypy/pull/14265)) +* Fix attrs protocol check with cache (Marc Mueller, PR [14558](https://github.com/python/mypy/pull/14558)) +* Fix strict equality check if operand item type has custom \_\_eq\_\_ (Jukka Lehtosalo, PR [14513](https://github.com/python/mypy/pull/14513)) +* Don't consider object always truthy (Jukka Lehtosalo, PR [14510](https://github.com/python/mypy/pull/14510)) +* Properly support union of TypedDicts as dict literal context (Ivan Levkivskyi, PR [14505](https://github.com/python/mypy/pull/14505)) +* Properly expand type in generic class with Self and TypeVar with values (Ivan Levkivskyi, PR [14491](https://github.com/python/mypy/pull/14491)) +* Fix recursive TypedDicts/NamedTuples defined with call syntax (Ivan Levkivskyi, PR [14488](https://github.com/python/mypy/pull/14488)) +* Fix type inference issue when a class inherits from Any (Shantanu, PR [14404](https://github.com/python/mypy/pull/14404)) +* Fix false positive on generic base class with six (Ivan Levkivskyi, PR [14478](https://github.com/python/mypy/pull/14478)) +* Don't read scripts without extensions as modules in namespace mode (Tim Geypens, PR [14335](https://github.com/python/mypy/pull/14335)) +* Fix inference for constrained type variables within unions (Christoph Tyralla, PR [14396](https://github.com/python/mypy/pull/14396)) +* Fix Unpack imported from typing (Marc Mueller, PR [14378](https://github.com/python/mypy/pull/14378)) +* Allow trailing commas in ini configuration of multiline values (Nikita Sobolev, PR [14240](https://github.com/python/mypy/pull/14240)) +* Fix false negatives involving Unions and generators or coroutines (Shantanu, PR [14224](https://github.com/python/mypy/pull/14224)) +* Fix ParamSpec constraint for types as callable (Vincent Vanlaer, PR [14153](https://github.com/python/mypy/pull/14153)) +* Fix type aliases with fixed-length tuples (Jukka Lehtosalo, PR [14184](https://github.com/python/mypy/pull/14184)) +* Fix issues with type aliases and new style unions (Jukka Lehtosalo, PR [14181](https://github.com/python/mypy/pull/14181)) +* Simplify unions less aggressively (Ivan Levkivskyi, PR [14178](https://github.com/python/mypy/pull/14178)) +* Simplify callable overlap logic (Ivan Levkivskyi, PR [14174](https://github.com/python/mypy/pull/14174)) +* Try empty context when assigning to union typed variables (Ivan Levkivskyi, PR [14151](https://github.com/python/mypy/pull/14151)) +* Improvements to recursive types (Ivan Levkivskyi, PR [14147](https://github.com/python/mypy/pull/14147)) +* Make non-numeric non-empty FORCE\_COLOR truthy (Shantanu, PR [14140](https://github.com/python/mypy/pull/14140)) +* Fix to recursive type aliases (Ivan Levkivskyi, PR [14136](https://github.com/python/mypy/pull/14136)) +* Correctly handle Enum name on Python 3.11 (Ivan Levkivskyi, PR [14133](https://github.com/python/mypy/pull/14133)) +* Fix class objects falling back to metaclass for callback protocol (Ivan Levkivskyi, PR [14121](https://github.com/python/mypy/pull/14121)) +* Correctly support self types in callable ClassVar (Ivan Levkivskyi, PR [14115](https://github.com/python/mypy/pull/14115)) +* Fix type variable clash in nested positions and in attributes (Ivan Levkivskyi, PR [14095](https://github.com/python/mypy/pull/14095)) +* Allow class variable as implementation for read only attribute (Ivan Levkivskyi, PR [14081](https://github.com/python/mypy/pull/14081)) +* Prevent warnings from causing dmypy to fail (Andrzej Bartosiński, PR [14102](https://github.com/python/mypy/pull/14102)) +* Correctly process nested definitions in mypy daemon (Ivan Levkivskyi, PR [14104](https://github.com/python/mypy/pull/14104)) +* Don't consider a branch unreachable if there is a possible promotion (Ivan Levkivskyi, PR [14077](https://github.com/python/mypy/pull/14077)) +* Fix incompatible overrides of overloaded methods in concrete subclasses (Shantanu, PR [14017](https://github.com/python/mypy/pull/14017)) +* Fix new style union syntax in type aliases (Jukka Lehtosalo, PR [14008](https://github.com/python/mypy/pull/14008)) +* Fix and optimise overload compatibility checking (Shantanu, PR [14018](https://github.com/python/mypy/pull/14018)) +* Improve handling of redefinitions through imports (Shantanu, PR [13969](https://github.com/python/mypy/pull/13969)) +* Preserve (some) implicitly exported types (Shantanu, PR [13967](https://github.com/python/mypy/pull/13967)) + +#### Typeshed Updates + +Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=ea0ae2155e8a04c9837903c3aff8dd5ad5f36ebc+0&branch=main&path=stdlib) for full list of typeshed changes. + +#### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Alessio Izzo +* Alex Waygood +* Anders Kaseorg +* Andrzej Bartosiński +* Avasam +* ChristianWitzler +* Christoph Tyralla +* dosisod +* Harrison McCarty +* Hugo van Kemenade +* Hugues +* Ilya Konstantinov +* Ivan Levkivskyi +* Jelle Zijlstra +* jhance +* johnthagen +* Jonathan Daniel +* Joshua Bronson +* Jukka Lehtosalo +* KotlinIsland +* Lakshay Bisht +* Lefteris Karapetsas +* Marc Mueller +* Matthew Hughes +* Michael Lee +* Nick Drozd +* Nikita Sobolev +* Richard Si +* Shantanu +* Stas Ilinskiy +* Tim Geypens +* Tin Tvrtković +* Tom Schraitle +* Valentin Stanciu +* Vincent Vanlaer + +We’d also like to thank our employer, Dropbox, for funding the mypy core team. + +Posted by Stas Ilinskiy + +## Previous releases + +For information about previous releases, refer to the posts at https://mypy-lang.blogspot.com/ From 838a1d4be1f3cad230d028b0e9cb8e1fb7a4fa5b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 18 Oct 2023 11:33:10 +0300 Subject: [PATCH 0323/1617] Add `unimported-reveal` error code (#16271) Note: `reveal_type(1) # type: ignore` is problematic, because it silences the output. So, I've added some docs to advertise not doing so. Closes https://github.com/python/mypy/issues/16270 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/error_code_list2.rst | 44 ++++++++++++++++++ mypy/checkexpr.py | 26 +++++++++++ mypy/errorcodes.py | 6 +++ mypy/nodes.py | 11 +++-- mypy/semanal.py | 13 +++++- mypy/types.py | 7 +-- test-data/unit/check-errorcodes.test | 62 +++++++++++++++++++++++++ test-data/unit/fixtures/typing-full.pyi | 3 ++ 8 files changed, 163 insertions(+), 9 deletions(-) diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 30fad0793771..cc5c9b0a1bc6 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -481,3 +481,47 @@ Example: @override def g(self, y: int) -> None: pass + + +.. _code-unimported-reveal: + +Check that ``reveal_type`` is imported from typing or typing_extensions [unimported-reveal] +------------------------------------------------------------------------------------------- + +Mypy used to have ``reveal_type`` as a special builtin +that only existed during type-checking. +In runtime it fails with expected ``NameError``, +which can cause real problem in production, hidden from mypy. + +But, in Python3.11 ``reveal_type`` +`was added to typing.py `_. +``typing_extensions`` ported this helper to all supported Python versions. + +Now users can actually import ``reveal_type`` to make the runtime code safe. + +.. note:: + + Starting with Python 3.11, the ``reveal_type`` function can be imported from ``typing``. + To use it with older Python versions, import it from ``typing_extensions`` instead. + +.. code-block:: python + + # Use "mypy --enable-error-code unimported-reveal" + + x = 1 + reveal_type(x) # Note: Revealed type is "builtins.int" \ + # Error: Name "reveal_type" is not defined + +Correct usage: + +.. code-block:: python + + # Use "mypy --enable-error-code unimported-reveal" + from typing import reveal_type # or `typing_extensions` + + x = 1 + # This won't raise an error: + reveal_type(x) # Note: Revealed type is "builtins.int" + +When this code is enabled, using ``reveal_locals`` is always an error, +because there's no way one can import it. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a5c8c80e1580..1d5233170a10 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -36,6 +36,7 @@ ARG_STAR2, IMPLICITLY_ABSTRACT, LITERAL_TYPE, + REVEAL_LOCALS, REVEAL_TYPE, ArgKind, AssertTypeExpr, @@ -4498,6 +4499,7 @@ def visit_reveal_expr(self, expr: RevealExpr) -> Type: self.msg.note( "'reveal_type' always outputs 'Any' in unchecked functions", expr.expr ) + self.check_reveal_imported(expr) return revealed_type else: # REVEAL_LOCALS @@ -4512,8 +4514,32 @@ def visit_reveal_expr(self, expr: RevealExpr) -> Type: ) self.msg.reveal_locals(names_to_types, expr) + self.check_reveal_imported(expr) return NoneType() + def check_reveal_imported(self, expr: RevealExpr) -> None: + if codes.UNIMPORTED_REVEAL not in self.chk.options.enabled_error_codes: + return + + name = "" + if expr.kind == REVEAL_LOCALS: + name = "reveal_locals" + elif expr.kind == REVEAL_TYPE and not expr.is_imported: + name = "reveal_type" + else: + return + + self.chk.fail(f'Name "{name}" is not defined', expr, code=codes.UNIMPORTED_REVEAL) + if name == "reveal_type": + module = ( + "typing" if self.chk.options.python_version >= (3, 11) else "typing_extensions" + ) + hint = ( + 'Did you forget to import it from "{module}"?' + ' (Suggestion: "from {module} import {name}")' + ).format(module=module, name=name) + self.chk.note(hint, expr, code=codes.UNIMPORTED_REVEAL) + def visit_type_application(self, tapp: TypeApplication) -> Type: """Type check a type application (expr[type, ...]). diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index cd9978c2f31c..98600679da53 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -249,6 +249,12 @@ def __hash__(self) -> int: "General", default_enabled=False, ) +UNIMPORTED_REVEAL: Final = ErrorCode( + "unimported-reveal", + "Require explicit import from typing or typing_extensions for reveal_type", + "General", + default_enabled=False, +) # Syntax errors are often blocking. diff --git a/mypy/nodes.py b/mypy/nodes.py index 6556cd910b46..0e5c078d0227 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2135,21 +2135,26 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: class RevealExpr(Expression): """Reveal type expression reveal_type(expr) or reveal_locals() expression.""" - __slots__ = ("expr", "kind", "local_nodes") + __slots__ = ("expr", "kind", "local_nodes", "is_imported") - __match_args__ = ("expr", "kind", "local_nodes") + __match_args__ = ("expr", "kind", "local_nodes", "is_imported") expr: Expression | None kind: int local_nodes: list[Var] | None def __init__( - self, kind: int, expr: Expression | None = None, local_nodes: list[Var] | None = None + self, + kind: int, + expr: Expression | None = None, + local_nodes: list[Var] | None = None, + is_imported: bool = False, ) -> None: super().__init__() self.expr = expr self.kind = kind self.local_nodes = local_nodes + self.is_imported = is_imported def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_reveal_expr(self) diff --git a/mypy/semanal.py b/mypy/semanal.py index 9c2452252208..179ee7c70bfb 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -243,6 +243,7 @@ DATACLASS_TRANSFORM_NAMES, FINAL_DECORATOR_NAMES, FINAL_TYPE_NAMES, + IMPORTED_REVEAL_TYPE_NAMES, NEVER_NAMES, OVERLOAD_NAMES, OVERRIDE_DECORATOR_NAMES, @@ -5056,7 +5057,17 @@ def visit_call_expr(self, expr: CallExpr) -> None: elif refers_to_fullname(expr.callee, REVEAL_TYPE_NAMES): if not self.check_fixed_args(expr, 1, "reveal_type"): return - expr.analyzed = RevealExpr(kind=REVEAL_TYPE, expr=expr.args[0]) + reveal_imported = False + reveal_type_node = self.lookup("reveal_type", expr, suppress_errors=True) + if ( + reveal_type_node + and isinstance(reveal_type_node.node, FuncBase) + and reveal_type_node.fullname in IMPORTED_REVEAL_TYPE_NAMES + ): + reveal_imported = True + expr.analyzed = RevealExpr( + kind=REVEAL_TYPE, expr=expr.args[0], is_imported=reveal_imported + ) expr.analyzed.line = expr.line expr.analyzed.column = expr.column expr.analyzed.accept(self) diff --git a/mypy/types.py b/mypy/types.py index ea81609fc605..d0c19a08e60a 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -128,11 +128,8 @@ "typing.Reversible", ) -REVEAL_TYPE_NAMES: Final = ( - "builtins.reveal_type", - "typing.reveal_type", - "typing_extensions.reveal_type", -) +IMPORTED_REVEAL_TYPE_NAMES: Final = ("typing.reveal_type", "typing_extensions.reveal_type") +REVEAL_TYPE_NAMES: Final = ("builtins.reveal_type", *IMPORTED_REVEAL_TYPE_NAMES) ASSERT_TYPE_NAMES: Final = ("typing.assert_type", "typing_extensions.assert_type") diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index ac7c8b4c9f9d..2282f21bcfa6 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1086,3 +1086,65 @@ def unsafe_func(x: object) -> Union[int, str]: else: return "some string" [builtins fixtures/isinstancelist.pyi] + + +### +# unimported-reveal +### + +[case testUnimportedRevealType] +# flags: --enable-error-code=unimported-reveal +x = 1 +reveal_type(x) +[out] +main:3: error: Name "reveal_type" is not defined [unimported-reveal] +main:3: note: Did you forget to import it from "typing_extensions"? (Suggestion: "from typing_extensions import reveal_type") +main:3: note: Revealed type is "builtins.int" +[builtins fixtures/isinstancelist.pyi] + +[case testUnimportedRevealTypePy311] +# flags: --enable-error-code=unimported-reveal --python-version=3.11 +x = 1 +reveal_type(x) +[out] +main:3: error: Name "reveal_type" is not defined [unimported-reveal] +main:3: note: Did you forget to import it from "typing"? (Suggestion: "from typing import reveal_type") +main:3: note: Revealed type is "builtins.int" +[builtins fixtures/isinstancelist.pyi] + +[case testUnimportedRevealTypeInUncheckedFunc] +# flags: --enable-error-code=unimported-reveal +def unchecked(): + x = 1 + reveal_type(x) +[out] +main:4: error: Name "reveal_type" is not defined [unimported-reveal] +main:4: note: Did you forget to import it from "typing_extensions"? (Suggestion: "from typing_extensions import reveal_type") +main:4: note: Revealed type is "Any" +main:4: note: 'reveal_type' always outputs 'Any' in unchecked functions +[builtins fixtures/isinstancelist.pyi] + +[case testUnimportedRevealTypeImportedTypingExtensions] +# flags: --enable-error-code=unimported-reveal +from typing_extensions import reveal_type +x = 1 +reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/isinstancelist.pyi] + +[case testUnimportedRevealTypeImportedTyping311] +# flags: --enable-error-code=unimported-reveal --python-version=3.11 +from typing import reveal_type +x = 1 +reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-full.pyi] + +[case testUnimportedRevealLocals] +# flags: --enable-error-code=unimported-reveal +x = 1 +reveal_locals() +[out] +main:3: note: Revealed local types are: +main:3: note: x: builtins.int +main:3: error: Name "reveal_locals" is not defined [unimported-reveal] +[builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 417ae6baf491..e9f0aa199bb4 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -192,3 +192,6 @@ def dataclass_transform( **kwargs: Any, ) -> Callable[[T], T]: ... def override(__arg: T) -> T: ... + +# Was added in 3.11 +def reveal_type(__obj: T) -> T: ... From e1f6d6b4547f118787a68bf503f5c86a2801a2bf Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Oct 2023 21:22:40 +0100 Subject: [PATCH 0324/1617] [mypyc] Avoid cyclic reference in nested functions (#16268) Mypyc used to always put nested functions into the environment object, which results in cyclic references, since the function object contains a reference to the environment. Now we only do this if the body of a nested function refers to a nested function (e.g. due to a recursive call). This means that in the majority of cases we can avoid the cyclic reference. This speeds up self check by an impressive 7%. I'm not sure exactly why the impact is so big, but spending less time in the cyclic garbage collector is probably a big part. --- mypyc/irbuild/builder.py | 5 + mypyc/irbuild/context.py | 2 + mypyc/irbuild/env_class.py | 2 +- mypyc/irbuild/function.py | 43 +++- mypyc/test-data/irbuild-basic.test | 219 ++++++++-------- mypyc/test-data/irbuild-nested.test | 380 ++++++++++++---------------- 6 files changed, 305 insertions(+), 346 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 8c68f91bf633..0757415f6753 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -502,6 +502,11 @@ def non_function_scope(self) -> bool: # Currently the stack always has at least two items: dummy and top-level. return len(self.fn_infos) <= 2 + def top_level_fn_info(self) -> FuncInfo | None: + if self.non_function_scope(): + return None + return self.fn_infos[2] + def init_final_static( self, lvalue: Lvalue, diff --git a/mypyc/irbuild/context.py b/mypyc/irbuild/context.py index 676afb507504..a740f0b821d9 100644 --- a/mypyc/irbuild/context.py +++ b/mypyc/irbuild/context.py @@ -22,6 +22,7 @@ def __init__( contains_nested: bool = False, is_decorated: bool = False, in_non_ext: bool = False, + add_nested_funcs_to_env: bool = False, ) -> None: self.fitem = fitem self.name = name @@ -47,6 +48,7 @@ def __init__( self.contains_nested = contains_nested self.is_decorated = is_decorated self.in_non_ext = in_non_ext + self.add_nested_funcs_to_env = add_nested_funcs_to_env # TODO: add field for ret_type: RType = none_rprimitive diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index ded8072deb63..aa223fe20176 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -107,7 +107,7 @@ def load_env_registers(builder: IRBuilder) -> None: load_outer_envs(builder, fn_info.callable_class) # If this is a FuncDef, then make sure to load the FuncDef into its own environment # class so that the function can be called recursively. - if isinstance(fitem, FuncDef): + if isinstance(fitem, FuncDef) and fn_info.add_nested_funcs_to_env: setup_func_for_recursive_call(builder, fitem, fn_info.callable_class) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 822350ea829b..ebf7fa9a54de 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -19,6 +19,7 @@ ArgKind, ClassDef, Decorator, + FuncBase, FuncDef, FuncItem, LambdaExpr, @@ -222,6 +223,7 @@ def c() -> None: is_decorated = fitem in builder.fdefs_to_decorators is_singledispatch = fitem in builder.singledispatch_impls in_non_ext = False + add_nested_funcs_to_env = has_nested_func_self_reference(builder, fitem) class_name = None if cdef: ir = builder.mapper.type_to_ir[cdef.info] @@ -234,14 +236,15 @@ def c() -> None: func_name = name builder.enter( FuncInfo( - fitem, - func_name, - class_name, - gen_func_ns(builder), - is_nested, - contains_nested, - is_decorated, - in_non_ext, + fitem=fitem, + name=func_name, + class_name=class_name, + namespace=gen_func_ns(builder), + is_nested=is_nested, + contains_nested=contains_nested, + is_decorated=is_decorated, + in_non_ext=in_non_ext, + add_nested_funcs_to_env=add_nested_funcs_to_env, ) ) @@ -267,7 +270,13 @@ def c() -> None: builder.enter(fn_info) setup_env_for_generator_class(builder) load_outer_envs(builder, builder.fn_info.generator_class) - if builder.fn_info.is_nested and isinstance(fitem, FuncDef): + top_level = builder.top_level_fn_info() + if ( + builder.fn_info.is_nested + and isinstance(fitem, FuncDef) + and top_level + and top_level.add_nested_funcs_to_env + ): setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class) create_switch_for_generator_class(builder) add_raise_exception_blocks_to_generator_class(builder, fitem.line) @@ -344,6 +353,20 @@ def c() -> None: return func_ir, func_reg +def has_nested_func_self_reference(builder: IRBuilder, fitem: FuncItem) -> bool: + """Does a nested function contain a self-reference in its body? + + If a nested function only has references in the surrounding function, + we don't need to add it to the environment. + """ + if any(isinstance(sym, FuncBase) for sym in builder.free_variables.get(fitem, set())): + return True + return any( + has_nested_func_self_reference(builder, nested) + for nested in builder.encapsulating_funcs.get(fitem, []) + ) + + def gen_func_ir( builder: IRBuilder, args: list[Register], @@ -768,7 +791,7 @@ def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget: # Get the target associated with the previously defined FuncDef. return builder.lookup(fdef.original_def) - if builder.fn_info.is_generator or builder.fn_info.contains_nested: + if builder.fn_info.is_generator or builder.fn_info.add_nested_funcs_to_env: return builder.lookup(fdef) return builder.add_local_reg(fdef, object_rprimitive) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 33fc8cfaa83b..bf608abb87ad 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -2694,47 +2694,43 @@ L2: def g_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_a_obj r0 :: __main__.a_env - r1, g :: object - r2 :: str - r3 :: object - r4 :: str - r5, r6, r7, r8 :: object - r9 :: str - r10 :: object - r11 :: str - r12, r13 :: object + r1 :: str + r2 :: object + r3 :: str + r4, r5, r6, r7 :: object + r8 :: str + r9 :: object + r10 :: str + r11, r12 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.g - g = r1 - r2 = 'Entering' - r3 = builtins :: module - r4 = 'print' - r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) - r7 = r0.f - r8 = PyObject_CallFunctionObjArgs(r7, 0) - r9 = 'Exited' - r10 = builtins :: module - r11 = 'print' - r12 = CPyObject_GetAttr(r10, r11) - r13 = PyObject_CallFunctionObjArgs(r12, r9, 0) + r1 = 'Entering' + r2 = builtins :: module + r3 = 'print' + r4 = CPyObject_GetAttr(r2, r3) + r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r6 = r0.f + r7 = PyObject_CallFunctionObjArgs(r6, 0) + r8 = 'Exited' + r9 = builtins :: module + r10 = 'print' + r11 = CPyObject_GetAttr(r9, r10) + r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) return 1 def a(f): f :: object r0 :: __main__.a_env r1 :: bool r2 :: __main__.g_a_obj - r3, r4 :: bool - r5 :: object + r3 :: bool + g :: object L0: r0 = a_env() r0.f = f; r1 = is_error r2 = g_a_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.g = r2; r4 = is_error - r5 = r0.g - return r5 + g = r2 + return g def g_b_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -2751,47 +2747,43 @@ L2: def g_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_b_obj r0 :: __main__.b_env - r1, g :: object - r2 :: str - r3 :: object - r4 :: str - r5, r6, r7, r8 :: object - r9 :: str - r10 :: object - r11 :: str - r12, r13 :: object + r1 :: str + r2 :: object + r3 :: str + r4, r5, r6, r7 :: object + r8 :: str + r9 :: object + r10 :: str + r11, r12 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.g - g = r1 - r2 = '---' - r3 = builtins :: module - r4 = 'print' - r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) - r7 = r0.f - r8 = PyObject_CallFunctionObjArgs(r7, 0) - r9 = '---' - r10 = builtins :: module - r11 = 'print' - r12 = CPyObject_GetAttr(r10, r11) - r13 = PyObject_CallFunctionObjArgs(r12, r9, 0) + r1 = '---' + r2 = builtins :: module + r3 = 'print' + r4 = CPyObject_GetAttr(r2, r3) + r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r6 = r0.f + r7 = PyObject_CallFunctionObjArgs(r6, 0) + r8 = '---' + r9 = builtins :: module + r10 = 'print' + r11 = CPyObject_GetAttr(r9, r10) + r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) return 1 def b(f): f :: object r0 :: __main__.b_env r1 :: bool r2 :: __main__.g_b_obj - r3, r4 :: bool - r5 :: object + r3 :: bool + g :: object L0: r0 = b_env() r0.f = f; r1 = is_error r2 = g_b_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.g = r2; r4 = is_error - r5 = r0.g - return r5 + g = r2 + return g def d_c_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -2808,20 +2800,17 @@ L2: def d_c_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.d_c_obj r0 :: __main__.c_env - r1, d :: object - r2 :: str - r3 :: object - r4 :: str - r5, r6 :: object + r1 :: str + r2 :: object + r3 :: str + r4, r5 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.d - d = r1 - r2 = 'd' - r3 = builtins :: module - r4 = 'print' - r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) + r1 = 'd' + r2 = builtins :: module + r3 = 'print' + r4 = CPyObject_GetAttr(r2, r3) + r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) return 1 def c(): r0 :: __main__.c_env @@ -2832,16 +2821,15 @@ def c(): r5, r6 :: object r7 :: dict r8 :: str - r9, r10 :: object - r11 :: bool - r12 :: dict - r13 :: str - r14 :: i32 - r15 :: bit - r16 :: str - r17 :: object - r18 :: str - r19, r20, r21, r22 :: object + r9, r10, d :: object + r11 :: dict + r12 :: str + r13 :: i32 + r14 :: bit + r15 :: str + r16 :: object + r17 :: str + r18, r19, r20 :: object L0: r0 = c_env() r1 = d_c_obj() @@ -2854,18 +2842,17 @@ L0: r8 = 'a' r9 = CPyDict_GetItem(r7, r8) r10 = PyObject_CallFunctionObjArgs(r9, r6, 0) - r0.d = r10; r11 = is_error - r12 = __main__.globals :: static - r13 = 'd' - r14 = CPyDict_SetItem(r12, r13, r10) - r15 = r14 >= 0 :: signed - r16 = 'c' - r17 = builtins :: module - r18 = 'print' - r19 = CPyObject_GetAttr(r17, r18) - r20 = PyObject_CallFunctionObjArgs(r19, r16, 0) - r21 = r0.d - r22 = PyObject_CallFunctionObjArgs(r21, 0) + d = r10 + r11 = __main__.globals :: static + r12 = 'd' + r13 = CPyDict_SetItem(r11, r12, r10) + r14 = r13 >= 0 :: signed + r15 = 'c' + r16 = builtins :: module + r17 = 'print' + r18 = CPyObject_GetAttr(r16, r17) + r19 = PyObject_CallFunctionObjArgs(r18, r15, 0) + r20 = PyObject_CallFunctionObjArgs(d, 0) return 1 def __top_level__(): r0, r1 :: object @@ -2947,47 +2934,43 @@ L2: def g_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_a_obj r0 :: __main__.a_env - r1, g :: object - r2 :: str - r3 :: object - r4 :: str - r5, r6, r7, r8 :: object - r9 :: str - r10 :: object - r11 :: str - r12, r13 :: object + r1 :: str + r2 :: object + r3 :: str + r4, r5, r6, r7 :: object + r8 :: str + r9 :: object + r10 :: str + r11, r12 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.g - g = r1 - r2 = 'Entering' - r3 = builtins :: module - r4 = 'print' - r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) - r7 = r0.f - r8 = PyObject_CallFunctionObjArgs(r7, 0) - r9 = 'Exited' - r10 = builtins :: module - r11 = 'print' - r12 = CPyObject_GetAttr(r10, r11) - r13 = PyObject_CallFunctionObjArgs(r12, r9, 0) + r1 = 'Entering' + r2 = builtins :: module + r3 = 'print' + r4 = CPyObject_GetAttr(r2, r3) + r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r6 = r0.f + r7 = PyObject_CallFunctionObjArgs(r6, 0) + r8 = 'Exited' + r9 = builtins :: module + r10 = 'print' + r11 = CPyObject_GetAttr(r9, r10) + r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) return 1 def a(f): f :: object r0 :: __main__.a_env r1 :: bool r2 :: __main__.g_a_obj - r3, r4 :: bool - r5 :: object + r3 :: bool + g :: object L0: r0 = a_env() r0.f = f; r1 = is_error r2 = g_a_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.g = r2; r4 = is_error - r5 = r0.g - return r5 + g = r2 + return g def __top_level__(): r0, r1 :: object r2 :: bit diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index adef80263533..b2b884705366 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -50,25 +50,22 @@ L2: def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj r0 :: __main__.a_env - r1, inner, r2 :: object + r1 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = box(None, 1) - return r2 + r1 = box(None, 1) + return r1 def a(): r0 :: __main__.a_env r1 :: __main__.inner_a_obj - r2, r3 :: bool - r4 :: object + r2 :: bool + inner :: object L0: r0 = a_env() r1 = inner_a_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.inner = r1; r3 = is_error - r4 = r0.inner - return r4 + inner = r1 + return inner def second_b_first_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -86,15 +83,12 @@ def second_b_first_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.second_b_first_obj r0 :: __main__.first_b_env r1 :: __main__.b_env - r2, second :: object - r3 :: str + r2 :: str L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.__mypyc_env__ - r2 = r0.second - second = r2 - r3 = 'b.first.second: nested function' - return r3 + r2 = 'b.first.second: nested function' + return r2 def first_b_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -111,35 +105,30 @@ L2: def first_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.first_b_obj r0 :: __main__.b_env - r1, first :: object - r2 :: __main__.first_b_env - r3 :: bool - r4 :: __main__.second_b_first_obj - r5, r6 :: bool - r7 :: object + r1 :: __main__.first_b_env + r2 :: bool + r3 :: __main__.second_b_first_obj + r4 :: bool + second :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.first - first = r1 - r2 = first_b_env() - r2.__mypyc_env__ = r0; r3 = is_error - r4 = second_b_first_obj() - r4.__mypyc_env__ = r2; r5 = is_error - r2.second = r4; r6 = is_error - r7 = r2.second - return r7 + r1 = first_b_env() + r1.__mypyc_env__ = r0; r2 = is_error + r3 = second_b_first_obj() + r3.__mypyc_env__ = r1; r4 = is_error + second = r3 + return second def b(): r0 :: __main__.b_env r1 :: __main__.first_b_obj - r2, r3 :: bool - r4 :: object + r2 :: bool + first :: object L0: r0 = b_env() r1 = first_b_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.first = r1; r3 = is_error - r4 = r0.first - return r4 + first = r1 + return first def inner_c_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -157,28 +146,24 @@ def inner_c_obj.__call__(__mypyc_self__, s): __mypyc_self__ :: __main__.inner_c_obj s :: str r0 :: __main__.c_env - r1, inner :: object - r2, r3 :: str + r1, r2 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = '!' - r3 = PyUnicode_Concat(s, r2) - return r3 + r1 = '!' + r2 = PyUnicode_Concat(s, r1) + return r2 def c(num): num :: float r0 :: __main__.c_env r1 :: __main__.inner_c_obj - r2, r3 :: bool - r4 :: object + r2 :: bool + inner :: object L0: r0 = c_env() r1 = inner_c_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.inner = r1; r3 = is_error - r4 = r0.inner - return r4 + inner = r1 + return inner def inner_d_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -196,40 +181,36 @@ def inner_d_obj.__call__(__mypyc_self__, s): __mypyc_self__ :: __main__.inner_d_obj s :: str r0 :: __main__.d_env - r1, inner :: object - r2, r3 :: str + r1, r2 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = '?' - r3 = PyUnicode_Concat(s, r2) - return r3 + r1 = '?' + r2 = PyUnicode_Concat(s, r1) + return r2 def d(num): num :: float r0 :: __main__.d_env r1 :: __main__.inner_d_obj - r2, r3 :: bool - r4 :: str - r5, r6 :: object - r7, a, r8 :: str - r9, r10 :: object - r11, b :: str + r2 :: bool + inner :: object + r3 :: str + r4 :: object + r5, a, r6 :: str + r7 :: object + r8, b :: str L0: r0 = d_env() r1 = inner_d_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.inner = r1; r3 = is_error - r4 = 'one' - r5 = r0.inner - r6 = PyObject_CallFunctionObjArgs(r5, r4, 0) - r7 = cast(str, r6) - a = r7 - r8 = 'two' - r9 = r0.inner - r10 = PyObject_CallFunctionObjArgs(r9, r8, 0) - r11 = cast(str, r10) - b = r11 + inner = r1 + r3 = 'one' + r4 = PyObject_CallFunctionObjArgs(inner, r3, 0) + r5 = cast(str, r4) + a = r5 + r6 = 'two' + r7 = PyObject_CallFunctionObjArgs(inner, r6, 0) + r8 = cast(str, r7) + b = r8 return a def inner(): r0 :: str @@ -290,32 +271,28 @@ L2: def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj r0 :: __main__.a_env - r1, inner :: object - r2 :: int + r1 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = r0.num - return r2 + r1 = r0.num + return r1 def a(num): num :: int r0 :: __main__.a_env r1 :: bool r2 :: __main__.inner_a_obj - r3, r4 :: bool - r5, r6 :: object - r7 :: int + r3 :: bool + inner, r4 :: object + r5 :: int L0: r0 = a_env() r0.num = num; r1 = is_error r2 = inner_a_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.inner = r2; r4 = is_error - r5 = r0.inner - r6 = PyObject_CallFunctionObjArgs(r5, 0) - r7 = unbox(int, r6) - return r7 + inner = r2 + r4 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = unbox(int, r4) + return r5 def inner_b_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -332,36 +309,32 @@ L2: def inner_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_b_obj r0 :: __main__.b_env - r1, inner :: object - r2 :: bool - foo, r3 :: int + r1 :: bool + foo, r2 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r0.num = 8; r2 = is_error + r0.num = 8; r1 = is_error foo = 12 - r3 = r0.num - return r3 + r2 = r0.num + return r2 def b(): r0 :: __main__.b_env r1 :: bool r2 :: __main__.inner_b_obj - r3, r4 :: bool - r5, r6 :: object - r7, r8, r9 :: int + r3 :: bool + inner, r4 :: object + r5, r6, r7 :: int L0: r0 = b_env() r0.num = 6; r1 = is_error r2 = inner_b_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.inner = r2; r4 = is_error - r5 = r0.inner - r6 = PyObject_CallFunctionObjArgs(r5, 0) - r7 = unbox(int, r6) - r8 = r0.num - r9 = CPyTagged_Add(r7, r8) - return r9 + inner = r2 + r4 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = unbox(int, r4) + r6 = r0.num + r7 = CPyTagged_Add(r5, r6) + return r7 def inner_c_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -378,14 +351,11 @@ L2: def inner_c_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_c_obj r0 :: __main__.c_env - r1, inner :: object - r2 :: str + r1 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = 'f.inner: first definition' - return r2 + r1 = 'f.inner: first definition' + return r1 def inner_c_obj_0.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -402,40 +372,37 @@ L2: def inner_c_obj_0.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_c_obj_0 r0 :: __main__.c_env - r1, inner :: object - r2 :: str + r1 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = 'f.inner: second definition' - return r2 + r1 = 'f.inner: second definition' + return r1 def c(flag): flag :: bool r0 :: __main__.c_env r1 :: __main__.inner_c_obj - r2, r3 :: bool - r4 :: __main__.inner_c_obj_0 - r5, r6 :: bool - r7, r8 :: object - r9 :: str + r2 :: bool + inner :: object + r3 :: __main__.inner_c_obj_0 + r4 :: bool + r5 :: object + r6 :: str L0: r0 = c_env() if flag goto L1 else goto L2 :: bool L1: r1 = inner_c_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.inner = r1; r3 = is_error + inner = r1 goto L3 L2: - r4 = inner_c_obj_0() - r4.__mypyc_env__ = r0; r5 = is_error - r0.inner = r4; r6 = is_error + r3 = inner_c_obj_0() + r3.__mypyc_env__ = r0; r4 = is_error + inner = r3 L3: - r7 = r0.inner - r8 = PyObject_CallFunctionObjArgs(r7, 0) - r9 = cast(str, r8) - return r9 + r5 = PyObject_CallFunctionObjArgs(inner, 0) + r6 = cast(str, r5) + return r6 [case testSpecialNested] def a() -> int: @@ -465,15 +432,12 @@ def c_a_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.c_a_b_obj r0 :: __main__.b_a_env r1 :: __main__.a_env - r2, c :: object - r3 :: int + r2 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.__mypyc_env__ - r2 = r0.c - c = r2 - r3 = r1.x - return r3 + r2 = r1.x + return r2 def b_a_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -490,48 +454,43 @@ L2: def b_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.b_a_obj r0 :: __main__.a_env - r1, b :: object - r2 :: __main__.b_a_env - r3 :: bool - r4, r5 :: int - r6 :: bool - r7 :: __main__.c_a_b_obj - r8, r9 :: bool - r10, r11 :: object - r12 :: int + r1 :: __main__.b_a_env + r2 :: bool + r3, r4 :: int + r5 :: bool + r6 :: __main__.c_a_b_obj + r7 :: bool + c, r8 :: object + r9 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.b - b = r1 - r2 = b_a_env() - r2.__mypyc_env__ = r0; r3 = is_error - r4 = r0.x - r5 = CPyTagged_Add(r4, 2) - r0.x = r5; r6 = is_error - r7 = c_a_b_obj() - r7.__mypyc_env__ = r2; r8 = is_error - r2.c = r7; r9 = is_error - r10 = r2.c - r11 = PyObject_CallFunctionObjArgs(r10, 0) - r12 = unbox(int, r11) - return r12 + r1 = b_a_env() + r1.__mypyc_env__ = r0; r2 = is_error + r3 = r0.x + r4 = CPyTagged_Add(r3, 2) + r0.x = r4; r5 = is_error + r6 = c_a_b_obj() + r6.__mypyc_env__ = r1; r7 = is_error + c = r6 + r8 = PyObject_CallFunctionObjArgs(c, 0) + r9 = unbox(int, r8) + return r9 def a(): r0 :: __main__.a_env r1 :: bool r2 :: __main__.b_a_obj - r3, r4 :: bool - r5, r6 :: object - r7 :: int + r3 :: bool + b, r4 :: object + r5 :: int L0: r0 = a_env() r0.x = 2; r1 = is_error r2 = b_a_obj() r2.__mypyc_env__ = r0; r3 = is_error - r0.b = r2; r4 = is_error - r5 = r0.b - r6 = PyObject_CallFunctionObjArgs(r5, 0) - r7 = unbox(int, r6) - return r7 + b = r2 + r4 = PyObject_CallFunctionObjArgs(b, 0) + r5 = unbox(int, r4) + return r5 [case testNestedFunctionInsideStatements] def f(flag: bool) -> str: @@ -559,14 +518,11 @@ L2: def inner_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_f_obj r0 :: __main__.f_env - r1, inner :: object - r2 :: str + r1 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = 'f.inner: first definition' - return r2 + r1 = 'f.inner: first definition' + return r1 def inner_f_obj_0.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -583,40 +539,37 @@ L2: def inner_f_obj_0.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_f_obj_0 r0 :: __main__.f_env - r1, inner :: object - r2 :: str + r1 :: str L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.inner - inner = r1 - r2 = 'f.inner: second definition' - return r2 + r1 = 'f.inner: second definition' + return r1 def f(flag): flag :: bool r0 :: __main__.f_env r1 :: __main__.inner_f_obj - r2, r3 :: bool - r4 :: __main__.inner_f_obj_0 - r5, r6 :: bool - r7, r8 :: object - r9 :: str + r2 :: bool + inner :: object + r3 :: __main__.inner_f_obj_0 + r4 :: bool + r5 :: object + r6 :: str L0: r0 = f_env() if flag goto L1 else goto L2 :: bool L1: r1 = inner_f_obj() r1.__mypyc_env__ = r0; r2 = is_error - r0.inner = r1; r3 = is_error + inner = r1 goto L3 L2: - r4 = inner_f_obj_0() - r4.__mypyc_env__ = r0; r5 = is_error - r0.inner = r4; r6 = is_error + r3 = inner_f_obj_0() + r3.__mypyc_env__ = r0; r4 = is_error + inner = r3 L3: - r7 = r0.inner - r8 = PyObject_CallFunctionObjArgs(r7, 0) - r9 = cast(str, r8) - return r9 + r5 = PyObject_CallFunctionObjArgs(inner, 0) + r6 = cast(str, r5) + return r6 [case testNestedFunctionsCallEachOther] from typing import Callable, List @@ -652,15 +605,12 @@ L2: def foo_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.foo_f_obj r0 :: __main__.f_env - r1, foo :: object - r2, r3 :: int + r1, r2 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.foo - foo = r1 - r2 = r0.a - r3 = CPyTagged_Add(r2, 2) - return r3 + r1 = r0.a + r2 = CPyTagged_Add(r1, 2) + return r2 def bar_f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -677,16 +627,14 @@ L2: def bar_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.bar_f_obj r0 :: __main__.f_env - r1, bar, r2, r3 :: object - r4 :: int + r1, r2 :: object + r3 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.bar - bar = r1 - r2 = r0.foo - r3 = PyObject_CallFunctionObjArgs(r2, 0) - r4 = unbox(int, r3) - return r4 + r1 = r0.foo + r2 = PyObject_CallFunctionObjArgs(r1, 0) + r3 = unbox(int, r2) + return r3 def baz_f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -704,23 +652,21 @@ def baz_f_obj.__call__(__mypyc_self__, n): __mypyc_self__ :: __main__.baz_f_obj n :: int r0 :: __main__.f_env - r1, baz :: object - r2 :: bit - r3 :: int - r4, r5 :: object + r1 :: bit + r2 :: int + r3, r4, r5 :: object r6, r7 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = r0.baz - baz = r1 - r2 = n == 0 - if r2 goto L1 else goto L2 :: bool + r1 = n == 0 + if r1 goto L1 else goto L2 :: bool L1: return 0 L2: - r3 = CPyTagged_Subtract(n, 2) - r4 = box(int, r3) - r5 = PyObject_CallFunctionObjArgs(baz, r4, 0) + r2 = CPyTagged_Subtract(n, 2) + r3 = r0.baz + r4 = box(int, r2) + r5 = PyObject_CallFunctionObjArgs(r3, r4, 0) r6 = unbox(int, r5) r7 = CPyTagged_Add(n, r6) return r7 From 1c218ea2c674d7a06c8ed4c2f95855f1d3fd26da Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 20 Oct 2023 00:13:46 +0100 Subject: [PATCH 0325/1617] Fix daemon false positives related to module-level __getattr__ (#16292) In some cases, mypy daemon could generate false positives about imports targeting packages with a module-level `__getattr__` methods. The root cause was that the `mypy.build.in_partial_package` function would leave a partially initialized module in the `modules` dictionary of `BuildManager`, which could probably cause all sorts of confusion. I fixed this by making sure that ASTs related to temporary `State` objects don't get persisted. Also updated a test case to properly delete a package -- an empty directory is now actually a valid namespace package, so to delete a package we should delete the directory, not just the files inside it. --- mypy/build.py | 10 +++++---- test-data/unit/fine-grained-modules.test | 6 ++---- test-data/unit/fine-grained.test | 27 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index b481cc6ad0dc..1385021aac48 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1991,7 +1991,7 @@ def __init__( raise ModuleNotFound # Parse the file (and then some) to get the dependencies. - self.parse_file() + self.parse_file(temporary=temporary) self.compute_dependencies() @property @@ -2109,7 +2109,7 @@ def fix_cross_refs(self) -> None: # Methods for processing modules from source code. - def parse_file(self) -> None: + def parse_file(self, *, temporary: bool = False) -> None: """Parse file and run first pass of semantic analysis. Everything done here is local to the file. Don't depend on imported @@ -2194,12 +2194,14 @@ def parse_file(self) -> None: else: self.early_errors = manager.ast_cache[self.id][1] - modules[self.id] = self.tree + if not temporary: + modules[self.id] = self.tree if not cached: self.semantic_analysis_pass1() - self.check_blockers() + if not temporary: + self.check_blockers() manager.ast_cache[self.id] = (self.tree, self.early_errors) diff --git a/test-data/unit/fine-grained-modules.test b/test-data/unit/fine-grained-modules.test index 163e859276cb..f28dbaa1113b 100644 --- a/test-data/unit/fine-grained-modules.test +++ b/test-data/unit/fine-grained-modules.test @@ -837,15 +837,13 @@ p.a.f(1) [file p/__init__.py] [file p/a.py] def f(x: str) -> None: pass -[delete p/__init__.py.2] -[delete p/a.py.2] -def f(x: str) -> None: pass +[delete p.2] [out] main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str" == main:1: error: Cannot find implementation or library stub for module named "p.a" main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main:2: error: "object" has no attribute "a" +main:1: error: Cannot find implementation or library stub for module named "p" [case testDeletePackage2] import p diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 68f72a2aa992..cb24467cbf41 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10337,3 +10337,30 @@ b.py:1: note: Use "-> None" if function does not return a value == a.py:1: error: Function is missing a return type annotation a.py:1: note: Use "-> None" if function does not return a value + +[case testModuleLevelGetAttrInStub] +import stub +import a +import b + +[file stub/__init__.pyi] +s: str +def __getattr__(self): pass + +[file a.py] + +[file a.py.2] +from stub import x +from stub.pkg import y +from stub.pkg.sub import z + +[file b.py] + +[file b.py.3] +from stub import s +reveal_type(s) + +[out] +== +== +b.py:2: note: Revealed type is "builtins.str" From 5506cba158d76cd11697d1178d73a552aa617b7c Mon Sep 17 00:00:00 2001 From: Ihor <31508183+nautics889@users.noreply.github.com> Date: Fri, 20 Oct 2023 23:00:39 +0300 Subject: [PATCH 0326/1617] fix: remove redundant `.format()` (#16288) Originally this was added in 040f3ab revision at 562th line. --- mypyc/codegen/emit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 7d41ee7e162b..fce6896e8d11 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -686,7 +686,7 @@ def emit_cast( if likely: check = f"(likely{check})" self.emit_arg_check(src, dest, typ, check, optional) - self.emit_lines(f" {dest} = {src};".format(dest, src), "else {") + self.emit_lines(f" {dest} = {src};", "else {") self.emit_cast_error_handler(error, src, dest, typ, raise_exception) self.emit_line("}") elif is_none_rprimitive(typ): From eecbcb981708bded48d9c17f5fd7ab843b57b2c0 Mon Sep 17 00:00:00 2001 From: Ganden Schaffner Date: Fri, 20 Oct 2023 16:29:04 -0700 Subject: [PATCH 0327/1617] Correctly recognize `typing_extensions.NewType` (#16298) fixes #16297. since the `.+_NAMES` constants in `types.py` are each referenced multiple times while other examples like this (i.e. a `.+_NAMES` tuple/set used only once) are inlined, I've inlined this one. --- mypy/semanal_newtype.py | 2 +- test-data/unit/check-newtype.test | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index a8380309d310..16c6c024800d 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -147,7 +147,7 @@ def analyze_newtype_declaration(self, s: AssignmentStmt) -> tuple[str | None, Ca and isinstance(s.lvalues[0], NameExpr) and isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.callee, RefExpr) - and s.rvalue.callee.fullname == "typing.NewType" + and (s.rvalue.callee.fullname in ("typing.NewType", "typing_extensions.NewType")) ): name = s.lvalues[0].name diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index 0ff6b8396fa7..99fdf5fe7ca3 100644 --- a/test-data/unit/check-newtype.test +++ b/test-data/unit/check-newtype.test @@ -379,3 +379,10 @@ N = NewType('N', XXX) # E: Argument 2 to NewType(...) must be subclassable (got # E: Name "XXX" is not defined x: List[Union[N, int]] [builtins fixtures/list.pyi] + +[case testTypingExtensionsNewType] +# flags: --python-version 3.7 +from typing_extensions import NewType +N = NewType("N", int) +x: N +[builtins fixtures/tuple.pyi] From ff8cebbcf5094012ee914308dc4f9ecaa7f4684c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 21 Oct 2023 00:23:00 -0700 Subject: [PATCH 0328/1617] Lock test dependencies (#16283) This was discussed in the contributor meetup today. This is a simple solution that requires very few changes. If you want to upgrade the lock file, you can pass `--upgrade` or just delete it and regenerate. --- .github/workflows/test.yml | 4 +- MANIFEST.in | 1 + test-requirements.in | 19 +++++++ test-requirements.txt | 101 +++++++++++++++++++++++++++++++------ 4 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 test-requirements.in diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index afa5d5823ea9..86704aca2f91 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -133,7 +133,7 @@ jobs: ./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV source $VENV/bin/activate - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.11.0 + run: pip install setuptools==68.2.2 tox==4.11.0 - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} run: | @@ -185,7 +185,7 @@ jobs: default: 3.11.1 command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');" - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.11.0 + run: pip install setuptools==68.2.2 tox==4.11.0 - name: Setup tox environment run: tox run -e py --notest - name: Test diff --git a/MANIFEST.in b/MANIFEST.in index a1c15446de3f..3ae340c7bd5e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -31,6 +31,7 @@ graft mypyc/doc # files necessary for testing sdist include mypy-requirements.txt include build-requirements.txt +include test-requirements.in include test-requirements.txt include mypy_self_check.ini prune misc diff --git a/test-requirements.in b/test-requirements.in new file mode 100644 index 000000000000..bab3ece29c02 --- /dev/null +++ b/test-requirements.in @@ -0,0 +1,19 @@ +# If you change this file (or mypy-requirements.txt or build-requirements.txt), please run: +# pip-compile --output-file=test-requirements.txt --strip-extras --allow-unsafe test-requirements.in + +-r mypy-requirements.txt +-r build-requirements.txt +attrs>=18.0 +black==23.9.1 # must match version in .pre-commit-config.yaml +filelock>=3.3.0 +# lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 +lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' +pre-commit +pre-commit-hooks==4.5.0 +psutil>=4.0 +pytest>=7.4.0 +pytest-xdist>=1.34.0 +pytest-cov>=2.10.0 +ruff==0.1.0 # must match version in .pre-commit-config.yaml +setuptools>=65.5.1 +tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 diff --git a/test-requirements.txt b/test-requirements.txt index a1fa98917872..3bb9cf29635f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,16 +1,87 @@ --r mypy-requirements.txt --r build-requirements.txt -attrs>=18.0 -black==23.9.1 # must match version in .pre-commit-config.yaml -filelock>=3.3.0 -# lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 -lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' -pre-commit +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in +# +attrs==23.1.0 + # via -r test-requirements.in +black==23.9.1 + # via -r test-requirements.in +cfgv==3.4.0 + # via pre-commit +click==8.1.7 + # via black +coverage==7.3.2 + # via pytest-cov +distlib==0.3.7 + # via virtualenv +execnet==2.0.2 + # via pytest-xdist +filelock==3.12.4 + # via + # -r test-requirements.in + # virtualenv +identify==2.5.30 + # via pre-commit +iniconfig==2.0.0 + # via pytest +lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" + # via -r test-requirements.in +mypy-extensions==1.0.0 + # via + # -r mypy-requirements.txt + # black +nodeenv==1.8.0 + # via pre-commit +packaging==23.2 + # via + # black + # pytest +pathspec==0.11.2 + # via black +platformdirs==3.11.0 + # via + # black + # virtualenv +pluggy==1.3.0 + # via pytest +pre-commit==3.5.0 + # via -r test-requirements.in pre-commit-hooks==4.5.0 -psutil>=4.0 -pytest>=7.4.0 -pytest-xdist>=1.34.0 -pytest-cov>=2.10.0 -ruff==0.1.0 # must match version in .pre-commit-config.yaml -setuptools>=65.5.1 -tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 + # via -r test-requirements.in +psutil==5.9.6 + # via -r test-requirements.in +pytest==7.4.2 + # via + # -r test-requirements.in + # pytest-cov + # pytest-xdist +pytest-cov==4.1.0 + # via -r test-requirements.in +pytest-xdist==3.3.1 + # via -r test-requirements.in +pyyaml==6.0.1 + # via pre-commit +ruamel-yaml==0.17.40 + # via pre-commit-hooks +ruamel-yaml-clib==0.2.8 + # via ruamel-yaml +ruff==0.1.0 + # via -r test-requirements.in +tomli==2.0.1 + # via -r test-requirements.in +types-psutil==5.9.5.17 + # via -r build-requirements.txt +types-setuptools==68.2.0.0 + # via -r build-requirements.txt +typing-extensions==4.8.0 + # via -r mypy-requirements.txt +virtualenv==20.24.5 + # via pre-commit + +# The following packages are considered to be unsafe in a requirements file: +setuptools==68.2.2 + # via + # -r test-requirements.in + # nodeenv From a3af87bf252f0ed0c6e0f977ad4079418b37a70f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 21 Oct 2023 16:41:10 +0100 Subject: [PATCH 0329/1617] Narrow tuple types using len() (#16237) Fixes #1178 Supersedes #10367 This is includes implementation for fixed length tuples, homogeneous tuples, and variadic tuples (and combinations of those). Generally implementation is straightforward. Some notes: * Unfortunately, it is necessary to add a new attribute `min_len` to `TypeVarTupleType`, which is probably fine, as it doesn't have that many attributes so far. * Supporting more general use cases (like `>` comparisons for variadic tuples) can cause quick proliferation of unions. I added two mechanisms to counteract this: not applying the narrowing if the integer literal in comparison is itself large, and collapsing unions of tuples into a single tuple (if possible) after we are done with the narrowing. This looks a bit arbitrary, but I think it is important to have. * Main missing feature here is probably not inferring type information from indirect comparisons like `len(x) > foo() > 1`. Supporting this kind of things in full generality is cumbersome, and we may add cases that turn out to be important later. * Note I am quite careful with indexing "inside" a `TypeVarTuple`, it is not really needed now, but I wanted to make everything future proof, so that it will be easy to add support for upper bounds for `TypeVarTuple`s, like `Nums = TypeVarTuple("Nums", bound=tuple[float, ...])`. * I also fix couple existing inconsistencies with `Any` handling in type narrowing. It looks like they stem from the old incorrect logic that meet of `Any` and `X` should be `X`, while in fact it should be `Any`. These fixes are not strictly necessary, but otherwise there may be new false positives, because I introduce a bunch of additional type narrowing scenarios here. cc @hatal175, thanks for the test cases, and for your nice first attempt to implement this! Co-authored-by: Tal Hayon --- mypy/binder.py | 83 ++++ mypy/checker.py | 359 +++++++++++++++- mypy/checkexpr.py | 53 ++- mypy/meet.py | 6 +- mypy/operators.py | 23 + mypy/options.py | 3 +- mypy/subtypes.py | 2 +- mypy/test/testcheck.py | 2 +- mypy/typeops.py | 2 +- mypy/types.py | 27 +- mypy_self_check.ini | 1 + test-data/unit/check-expressions.test | 13 + test-data/unit/check-namedtuple.test | 2 +- test-data/unit/check-narrowing.test | 576 ++++++++++++++++++++++++++ test-data/unit/fixtures/len.pyi | 39 ++ test-data/unit/lib-stub/typing.pyi | 1 + 16 files changed, 1154 insertions(+), 38 deletions(-) create mode 100644 test-data/unit/fixtures/len.pyi diff --git a/mypy/binder.py b/mypy/binder.py index 8a68f24f661e..3b67d09f16c3 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -12,12 +12,17 @@ from mypy.subtypes import is_same_type, is_subtype from mypy.types import ( AnyType, + Instance, NoneType, PartialType, + ProperType, + TupleType, Type, TypeOfAny, TypeType, UnionType, + UnpackType, + find_unpack_in_list, get_proper_type, ) from mypy.typevars import fill_typevars_with_any @@ -213,6 +218,24 @@ def update_from_options(self, frames: list[Frame]) -> bool: for other in resulting_values[1:]: assert other is not None type = join_simple(self.declarations[key], type, other) + # Try simplifying resulting type for unions involving variadic tuples. + # Technically, everything is still valid without this step, but if we do + # not do this, this may create long unions after exiting an if check like: + # x: tuple[int, ...] + # if len(x) < 10: + # ... + # We want the type of x to be tuple[int, ...] after this block (if it is + # still equivalent to such type). + if isinstance(type, UnionType): + type = collapse_variadic_union(type) + if isinstance(type, ProperType) and isinstance(type, UnionType): + # Simplify away any extra Any's that were added to the declared + # type when popping a frame. + simplified = UnionType.make_union( + [t for t in type.items if not isinstance(get_proper_type(t), AnyType)] + ) + if simplified == self.declarations[key]: + type = simplified if current_value is None or not is_same_type(type, current_value): self._put(key, type) changed = True @@ -453,3 +476,63 @@ def get_declaration(expr: BindableExpression) -> Type | None: elif isinstance(expr.node, TypeInfo): return TypeType(fill_typevars_with_any(expr.node)) return None + + +def collapse_variadic_union(typ: UnionType) -> Type: + """Simplify a union involving variadic tuple if possible. + + This will collapse a type like e.g. + tuple[X, Z] | tuple[X, Y, Z] | tuple[X, Y, Y, *tuple[Y, ...], Z] + back to + tuple[X, *tuple[Y, ...], Z] + which is equivalent, but much simpler form of the same type. + """ + tuple_items = [] + other_items = [] + for t in typ.items: + p_t = get_proper_type(t) + if isinstance(p_t, TupleType): + tuple_items.append(p_t) + else: + other_items.append(t) + if len(tuple_items) <= 1: + # This type cannot be simplified further. + return typ + tuple_items = sorted(tuple_items, key=lambda t: len(t.items)) + first = tuple_items[0] + last = tuple_items[-1] + unpack_index = find_unpack_in_list(last.items) + if unpack_index is None: + return typ + unpack = last.items[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + if not isinstance(unpacked, Instance): + return typ + assert unpacked.type.fullname == "builtins.tuple" + suffix = last.items[unpack_index + 1 :] + + # Check that first item matches the expected pattern and infer prefix. + if len(first.items) < len(suffix): + return typ + if suffix and first.items[-len(suffix) :] != suffix: + return typ + if suffix: + prefix = first.items[: -len(suffix)] + else: + prefix = first.items + + # Check that all middle types match the expected pattern as well. + arg = unpacked.args[0] + for i, it in enumerate(tuple_items[1:-1]): + if it.items != prefix + [arg] * (i + 1) + suffix: + return typ + + # Check the last item (the one with unpack), and choose an appropriate simplified type. + if last.items != prefix + [arg] * (len(typ.items) - 1) + [unpack] + suffix: + return typ + if len(first.items) == 0: + simplified: Type = unpacked.copy_modified() + else: + simplified = TupleType(prefix + [unpack] + suffix, fallback=last.partial_fallback) + return UnionType.make_union([simplified] + other_items) diff --git a/mypy/checker.py b/mypy/checker.py index e1b65a95ae98..02bab37aa13f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -43,7 +43,7 @@ from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash from mypy.maptype import map_instance_to_supertype -from mypy.meet import is_overlapping_erased_types, is_overlapping_types +from mypy.meet import is_overlapping_erased_types, is_overlapping_types, meet_types from mypy.message_registry import ErrorMessage from mypy.messages import ( SUGGESTED_TEST_FIXTURES, @@ -134,7 +134,8 @@ YieldExpr, is_final_node, ) -from mypy.options import Options +from mypy.operators import flip_ops, int_op_to_method, neg_ops +from mypy.options import PRECISE_TUPLE_TYPES, Options from mypy.patterns import AsPattern, StarredPattern from mypy.plugin import CheckerPluginInterface, Plugin from mypy.plugins import dataclasses as dataclasses_plugin @@ -228,6 +229,9 @@ DEFAULT_LAST_PASS: Final = 1 # Pass numbers start at 0 +# Maximum length of fixed tuple types inferred when narrowing from variadic tuples. +MAX_PRECISE_TUPLE_SIZE: Final = 8 + DeferredNodeType: _TypeAlias = Union[FuncDef, LambdaExpr, OverloadedFuncDef, Decorator] FineGrainedDeferredNodeType: _TypeAlias = Union[FuncDef, MypyFile, OverloadedFuncDef] @@ -5829,7 +5833,15 @@ def has_no_custom_eq_checks(t: Type) -> bool: partial_type_maps.append((if_map, else_map)) - return reduce_conditional_maps(partial_type_maps) + # If we have found non-trivial restrictions from the regular comparisons, + # then return soon. Otherwise try to infer restrictions involving `len(x)`. + # TODO: support regular and len() narrowing in the same chain. + if any(m != ({}, {}) for m in partial_type_maps): + return reduce_conditional_maps(partial_type_maps) + else: + # Use meet for `and` maps to get correct results for chained checks + # like `if 1 < len(x) < 4: ...` + return reduce_conditional_maps(self.find_tuple_len_narrowing(node), use_meet=True) elif isinstance(node, AssignmentExpr): if_map = {} else_map = {} @@ -5860,7 +5872,10 @@ def has_no_custom_eq_checks(t: Type) -> bool: # and false if at least one of e1 and e2 is false. return ( and_conditional_maps(left_if_vars, right_if_vars), - or_conditional_maps(left_else_vars, right_else_vars), + # Note that if left else type is Any, we can't add any additional + # types to it, since the right maps were computed assuming + # the left is True, which may be not the case in the else branch. + or_conditional_maps(left_else_vars, right_else_vars, coalesce_any=True), ) elif isinstance(node, OpExpr) and node.op == "or": left_if_vars, left_else_vars = self.find_isinstance_check(node.left) @@ -5875,6 +5890,27 @@ def has_no_custom_eq_checks(t: Type) -> bool: elif isinstance(node, UnaryExpr) and node.op == "not": left, right = self.find_isinstance_check(node.expr) return right, left + elif ( + literal(node) == LITERAL_TYPE + and self.has_type(node) + and self.can_be_narrowed_with_len(self.lookup_type(node)) + # Only translate `if x` to `if len(x) > 0` when possible. + and not custom_special_method(self.lookup_type(node), "__bool__") + and self.options.strict_optional + ): + # Combine a `len(x) > 0` check with the default logic below. + yes_type, no_type = self.narrow_with_len(self.lookup_type(node), ">", 0) + if yes_type is not None: + yes_type = true_only(yes_type) + else: + yes_type = UninhabitedType() + if no_type is not None: + no_type = false_only(no_type) + else: + no_type = UninhabitedType() + if_map = {node: yes_type} if not isinstance(yes_type, UninhabitedType) else None + else_map = {node: no_type} if not isinstance(no_type, UninhabitedType) else None + return if_map, else_map # Restrict the type of the variable to True-ish/False-ish in the if and else branches # respectively @@ -6221,6 +6257,287 @@ def refine_away_none_in_comparison( return if_map, {} + def is_len_of_tuple(self, expr: Expression) -> bool: + """Is this expression a `len(x)` call where x is a tuple or union of tuples?""" + if not isinstance(expr, CallExpr): + return False + if not refers_to_fullname(expr.callee, "builtins.len"): + return False + if len(expr.args) != 1: + return False + expr = expr.args[0] + if literal(expr) != LITERAL_TYPE: + return False + if not self.has_type(expr): + return False + return self.can_be_narrowed_with_len(self.lookup_type(expr)) + + def can_be_narrowed_with_len(self, typ: Type) -> bool: + """Is this a type that can benefit from length check type restrictions? + + Currently supported types are TupleTypes, Instances of builtins.tuple, and + unions involving such types. + """ + if custom_special_method(typ, "__len__"): + # If user overrides builtin behavior, we can't do anything. + return False + p_typ = get_proper_type(typ) + # Note: we are conservative about tuple subclasses, because some code may rely on + # the fact that tuple_type of fallback TypeInfo matches the original TupleType. + if isinstance(p_typ, TupleType): + if any(isinstance(t, UnpackType) for t in p_typ.items): + return p_typ.partial_fallback.type.fullname == "builtins.tuple" + return True + if isinstance(p_typ, Instance): + return p_typ.type.has_base("builtins.tuple") + if isinstance(p_typ, UnionType): + return any(self.can_be_narrowed_with_len(t) for t in p_typ.items) + return False + + def literal_int_expr(self, expr: Expression) -> int | None: + """Is this expression an int literal, or a reference to an int constant? + + If yes, return the corresponding int value, otherwise return None. + """ + if not self.has_type(expr): + return None + expr_type = self.lookup_type(expr) + expr_type = coerce_to_literal(expr_type) + proper_type = get_proper_type(expr_type) + if not isinstance(proper_type, LiteralType): + return None + if not isinstance(proper_type.value, int): + return None + return proper_type.value + + def find_tuple_len_narrowing(self, node: ComparisonExpr) -> list[tuple[TypeMap, TypeMap]]: + """Top-level logic to find type restrictions from a length check on tuples. + + We try to detect `if` checks like the following: + x: tuple[int, int] | tuple[int, int, int] + y: tuple[int, int] | tuple[int, int, int] + if len(x) == len(y) == 2: + a, b = x # OK + c, d = y # OK + + z: tuple[int, ...] + if 1 < len(z) < 4: + x = z # OK + and report corresponding type restrictions to the binder. + """ + # First step: group consecutive `is` and `==` comparisons together. + # This is essentially a simplified version of group_comparison_operands(), + # tuned to the len()-like checks. Note that we don't propagate indirect + # restrictions like e.g. `len(x) > foo() > 1` yet, since it is tricky. + # TODO: propagate indirect len() comparison restrictions. + chained = [] + last_group = set() + for op, left, right in node.pairwise(): + if isinstance(left, AssignmentExpr): + left = left.value + if isinstance(right, AssignmentExpr): + right = right.value + if op in ("is", "=="): + last_group.add(left) + last_group.add(right) + else: + if last_group: + chained.append(("==", list(last_group))) + last_group = set() + if op in {"is not", "!=", "<", "<=", ">", ">="}: + chained.append((op, [left, right])) + if last_group: + chained.append(("==", list(last_group))) + + # Second step: infer type restrictions from each group found above. + type_maps = [] + for op, items in chained: + # TODO: support unions of literal types as len() comparison targets. + if not any(self.literal_int_expr(it) is not None for it in items): + continue + if not any(self.is_len_of_tuple(it) for it in items): + continue + + # At this step we know there is at least one len(x) and one literal in the group. + if op in ("is", "=="): + literal_values = set() + tuples = [] + for it in items: + lit = self.literal_int_expr(it) + if lit is not None: + literal_values.add(lit) + continue + if self.is_len_of_tuple(it): + assert isinstance(it, CallExpr) + tuples.append(it.args[0]) + if len(literal_values) > 1: + # More than one different literal value found, like 1 == len(x) == 2, + # so the corresponding branch is unreachable. + return [(None, {})] + size = literal_values.pop() + if size > MAX_PRECISE_TUPLE_SIZE: + # Avoid creating huge tuples from checks like if len(x) == 300. + continue + for tpl in tuples: + yes_type, no_type = self.narrow_with_len(self.lookup_type(tpl), op, size) + yes_map = None if yes_type is None else {tpl: yes_type} + no_map = None if no_type is None else {tpl: no_type} + type_maps.append((yes_map, no_map)) + else: + left, right = items + if self.is_len_of_tuple(right): + # Normalize `1 < len(x)` and similar as `len(x) > 1`. + left, right = right, left + op = flip_ops.get(op, op) + r_size = self.literal_int_expr(right) + assert r_size is not None + if r_size > MAX_PRECISE_TUPLE_SIZE: + # Avoid creating huge unions from checks like if len(x) > 300. + continue + assert isinstance(left, CallExpr) + yes_type, no_type = self.narrow_with_len( + self.lookup_type(left.args[0]), op, r_size + ) + yes_map = None if yes_type is None else {left.args[0]: yes_type} + no_map = None if no_type is None else {left.args[0]: no_type} + type_maps.append((yes_map, no_map)) + return type_maps + + def narrow_with_len(self, typ: Type, op: str, size: int) -> tuple[Type | None, Type | None]: + """Dispatch tuple type narrowing logic depending on the kind of type we got.""" + typ = get_proper_type(typ) + if isinstance(typ, TupleType): + return self.refine_tuple_type_with_len(typ, op, size) + elif isinstance(typ, Instance): + return self.refine_instance_type_with_len(typ, op, size) + elif isinstance(typ, UnionType): + yes_types = [] + no_types = [] + other_types = [] + for t in typ.items: + if not self.can_be_narrowed_with_len(t): + other_types.append(t) + continue + yt, nt = self.narrow_with_len(t, op, size) + if yt is not None: + yes_types.append(yt) + if nt is not None: + no_types.append(nt) + yes_types += other_types + no_types += other_types + if yes_types: + yes_type = make_simplified_union(yes_types) + else: + yes_type = None + if no_types: + no_type = make_simplified_union(no_types) + else: + no_type = None + return yes_type, no_type + else: + assert False, "Unsupported type for len narrowing" + + def refine_tuple_type_with_len( + self, typ: TupleType, op: str, size: int + ) -> tuple[Type | None, Type | None]: + """Narrow a TupleType using length restrictions.""" + unpack_index = find_unpack_in_list(typ.items) + if unpack_index is None: + # For fixed length tuple situation is trivial, it is either reachable or not, + # depending on the current length, expected length, and the comparison op. + method = int_op_to_method[op] + if method(typ.length(), size): + return typ, None + return None, typ + unpack = typ.items[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + if isinstance(unpacked, TypeVarTupleType): + # For tuples involving TypeVarTuple unpack we can't do much except + # inferring reachability, and recording the restrictions on TypeVarTuple + # for further "manual" use elsewhere. + min_len = typ.length() - 1 + unpacked.min_len + if op in ("==", "is"): + if min_len <= size: + return typ, typ + return None, typ + elif op in ("<", "<="): + if op == "<=": + size += 1 + if min_len < size: + prefix = typ.items[:unpack_index] + suffix = typ.items[unpack_index + 1 :] + # TODO: also record max_len to avoid false negatives? + unpack = UnpackType(unpacked.copy_modified(min_len=size - typ.length() + 1)) + return typ, typ.copy_modified(items=prefix + [unpack] + suffix) + return None, typ + else: + yes_type, no_type = self.refine_tuple_type_with_len(typ, neg_ops[op], size) + return no_type, yes_type + # Homogeneous variadic item is the case where we are most flexible. Essentially, + # we adjust the variadic item by "eating away" from it to satisfy the restriction. + assert isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple" + min_len = typ.length() - 1 + arg = unpacked.args[0] + prefix = typ.items[:unpack_index] + suffix = typ.items[unpack_index + 1 :] + if op in ("==", "is"): + if min_len <= size: + # TODO: return fixed union + prefixed variadic tuple for no_type? + return typ.copy_modified(items=prefix + [arg] * (size - min_len) + suffix), typ + return None, typ + elif op in ("<", "<="): + if op == "<=": + size += 1 + if min_len < size: + # Note: there is some ambiguity w.r.t. to where to put the additional + # items: before or after the unpack. However, such types are equivalent, + # so we always put them before for consistency. + no_type = typ.copy_modified( + items=prefix + [arg] * (size - min_len) + [unpack] + suffix + ) + yes_items = [] + for n in range(size - min_len): + yes_items.append(typ.copy_modified(items=prefix + [arg] * n + suffix)) + return UnionType.make_union(yes_items, typ.line, typ.column), no_type + return None, typ + else: + yes_type, no_type = self.refine_tuple_type_with_len(typ, neg_ops[op], size) + return no_type, yes_type + + def refine_instance_type_with_len( + self, typ: Instance, op: str, size: int + ) -> tuple[Type | None, Type | None]: + """Narrow a homogeneous tuple using length restrictions.""" + base = map_instance_to_supertype(typ, self.lookup_typeinfo("builtins.tuple")) + arg = base.args[0] + # Again, we are conservative about subclasses until we gain more confidence. + allow_precise = ( + PRECISE_TUPLE_TYPES in self.options.enable_incomplete_feature + ) and typ.type.fullname == "builtins.tuple" + if op in ("==", "is"): + # TODO: return fixed union + prefixed variadic tuple for no_type? + return TupleType(items=[arg] * size, fallback=typ), typ + elif op in ("<", "<="): + if op == "<=": + size += 1 + if allow_precise: + unpack = UnpackType(self.named_generic_type("builtins.tuple", [arg])) + no_type: Type | None = TupleType(items=[arg] * size + [unpack], fallback=typ) + else: + no_type = typ + if allow_precise: + items = [] + for n in range(size): + items.append(TupleType([arg] * n, fallback=typ)) + yes_type: Type | None = UnionType.make_union(items, typ.line, typ.column) + else: + yes_type = typ + return yes_type, no_type + else: + yes_type, no_type = self.refine_instance_type_with_len(typ, neg_ops[op], size) + return no_type, yes_type + # # Helpers # @@ -7168,7 +7485,7 @@ def builtin_item_type(tp: Type) -> Type | None: return None -def and_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: +def and_conditional_maps(m1: TypeMap, m2: TypeMap, use_meet: bool = False) -> TypeMap: """Calculate what information we can learn from the truth of (e1 and e2) in terms of the information that we can learn from the truth of e1 and the truth of e2. @@ -7178,22 +7495,31 @@ def and_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: # One of the conditions can never be true. return None # Both conditions can be true; combine the information. Anything - # we learn from either conditions's truth is valid. If the same + # we learn from either conditions' truth is valid. If the same # expression's type is refined by both conditions, we somewhat - # arbitrarily give precedence to m2. (In the future, we could use - # an intersection type.) + # arbitrarily give precedence to m2 unless m1 value is Any. + # In the future, we could use an intersection type or meet_types(). result = m2.copy() m2_keys = {literal_hash(n2) for n2 in m2} for n1 in m1: - if literal_hash(n1) not in m2_keys: + if literal_hash(n1) not in m2_keys or isinstance(get_proper_type(m1[n1]), AnyType): result[n1] = m1[n1] + if use_meet: + # For now, meet common keys only if specifically requested. + # This is currently used for tuple types narrowing, where having + # a precise result is important. + for n1 in m1: + for n2 in m2: + if literal_hash(n1) == literal_hash(n2): + result[n1] = meet_types(m1[n1], m2[n2]) return result -def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: +def or_conditional_maps(m1: TypeMap, m2: TypeMap, coalesce_any: bool = False) -> TypeMap: """Calculate what information we can learn from the truth of (e1 or e2) in terms of the information that we can learn from the truth of e1 and - the truth of e2. + the truth of e2. If coalesce_any is True, consider Any a supertype when + joining restrictions. """ if m1 is None: @@ -7208,11 +7534,16 @@ def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: for n1 in m1: for n2 in m2: if literal_hash(n1) == literal_hash(n2): - result[n1] = make_simplified_union([m1[n1], m2[n2]]) + if coalesce_any and isinstance(get_proper_type(m1[n1]), AnyType): + result[n1] = m1[n1] + else: + result[n1] = make_simplified_union([m1[n1], m2[n2]]) return result -def reduce_conditional_maps(type_maps: list[tuple[TypeMap, TypeMap]]) -> tuple[TypeMap, TypeMap]: +def reduce_conditional_maps( + type_maps: list[tuple[TypeMap, TypeMap]], use_meet: bool = False +) -> tuple[TypeMap, TypeMap]: """Reduces a list containing pairs of if/else TypeMaps into a single pair. We "and" together all of the if TypeMaps and "or" together the else TypeMaps. So @@ -7243,7 +7574,7 @@ def reduce_conditional_maps(type_maps: list[tuple[TypeMap, TypeMap]]) -> tuple[T else: final_if_map, final_else_map = type_maps[0] for if_map, else_map in type_maps[1:]: - final_if_map = and_conditional_maps(final_if_map, if_map) + final_if_map = and_conditional_maps(final_if_map, if_map, use_meet=use_meet) final_else_map = or_conditional_maps(final_else_map, else_map) return final_if_map, final_else_map diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 1d5233170a10..2dc5a93a1de9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3643,6 +3643,14 @@ def dangerous_comparison( left = map_instance_to_supertype(left, abstract_set) right = map_instance_to_supertype(right, abstract_set) return self.dangerous_comparison(left.args[0], right.args[0]) + elif left.type.has_base("typing.Mapping") and right.type.has_base("typing.Mapping"): + # Similar to above: Mapping ignores the classes, it just compares items. + abstract_map = self.chk.lookup_typeinfo("typing.Mapping") + left = map_instance_to_supertype(left, abstract_map) + right = map_instance_to_supertype(right, abstract_map) + return self.dangerous_comparison( + left.args[0], right.args[0] + ) or self.dangerous_comparison(left.args[1], right.args[1]) elif left_name in ("builtins.list", "builtins.tuple") and right_name == left_name: return self.dangerous_comparison(left.args[0], right.args[0]) elif left_name in OVERLAPPING_BYTES_ALLOWLIST and right_name in ( @@ -4228,9 +4236,8 @@ def visit_index_with_type( else: self.chk.fail(message_registry.TUPLE_INDEX_OUT_OF_RANGE, e) if any(isinstance(t, UnpackType) for t in left_type.items): - self.chk.note( - f"Variadic tuple can have length {left_type.length() - 1}", e - ) + min_len = self.min_tuple_length(left_type) + self.chk.note(f"Variadic tuple can have length {min_len}", e) return AnyType(TypeOfAny.from_error) return make_simplified_union(out) else: @@ -4254,6 +4261,16 @@ def visit_index_with_type( e.method_type = method_type return result + def min_tuple_length(self, left: TupleType) -> int: + unpack_index = find_unpack_in_list(left.items) + if unpack_index is None: + return left.length() + unpack = left.items[unpack_index] + assert isinstance(unpack, UnpackType) + if isinstance(unpack.type, TypeVarTupleType): + return left.length() - 1 + unpack.type.min_len + return left.length() - 1 + def visit_tuple_index_helper(self, left: TupleType, n: int) -> Type | None: unpack_index = find_unpack_in_list(left.items) if unpack_index is None: @@ -4267,31 +4284,39 @@ def visit_tuple_index_helper(self, left: TupleType, n: int) -> Type | None: unpacked = get_proper_type(unpack.type) if isinstance(unpacked, TypeVarTupleType): # Usually we say that TypeVarTuple can't be split, be in case of - # indexing it seems benign to just return the fallback item, similar + # indexing it seems benign to just return the upper bound item, similar # to what we do when indexing a regular TypeVar. - middle = unpacked.tuple_fallback.args[0] + bound = get_proper_type(unpacked.upper_bound) + assert isinstance(bound, Instance) + assert bound.type.fullname == "builtins.tuple" + middle = bound.args[0] else: assert isinstance(unpacked, Instance) assert unpacked.type.fullname == "builtins.tuple" middle = unpacked.args[0] + + extra_items = self.min_tuple_length(left) - left.length() + 1 if n >= 0: - if n < unpack_index: - return left.items[n] - if n >= len(left.items) - 1: + if n >= self.min_tuple_length(left): # For tuple[int, *tuple[str, ...], int] we allow either index 0 or 1, # since variadic item may have zero items. return None + if n < unpack_index: + return left.items[n] return UnionType.make_union( - [middle] + left.items[unpack_index + 1 : n + 2], left.line, left.column + [middle] + + left.items[unpack_index + 1 : max(n - extra_items + 2, unpack_index + 1)], + left.line, + left.column, ) - n += len(left.items) - if n <= 0: + n += self.min_tuple_length(left) + if n < 0: # Similar to above, we only allow -1, and -2 for tuple[int, *tuple[str, ...], int] return None - if n > unpack_index: - return left.items[n] + if n >= unpack_index + extra_items: + return left.items[n - extra_items + 1] return UnionType.make_union( - left.items[n - 1 : unpack_index] + [middle], left.line, left.column + left.items[min(n, unpack_index) : unpack_index] + [middle], left.line, left.column ) def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Type: diff --git a/mypy/meet.py b/mypy/meet.py index 0fa500d32c30..e3645c7b5879 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -221,6 +221,8 @@ def get_possible_variants(typ: Type) -> list[Type]: return [typ.upper_bound] elif isinstance(typ, ParamSpecType): return [typ.upper_bound] + elif isinstance(typ, TypeVarTupleType): + return [typ.upper_bound] elif isinstance(typ, UnionType): return list(typ.items) elif isinstance(typ, Overloaded): @@ -694,8 +696,8 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: return self.default(self.s) def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: - if self.s == t: - return self.s + if isinstance(self.s, TypeVarTupleType) and self.s.id == t.id: + return self.s if self.s.min_len > t.min_len else t else: return self.default(self.s) diff --git a/mypy/operators.py b/mypy/operators.py index 07ec5a24fa77..d1f050b58fae 100644 --- a/mypy/operators.py +++ b/mypy/operators.py @@ -101,3 +101,26 @@ reverse_op_method_set: Final = set(reverse_op_methods.values()) unary_op_methods: Final = {"-": "__neg__", "+": "__pos__", "~": "__invert__"} + +int_op_to_method: Final = { + "==": int.__eq__, + "is": int.__eq__, + "<": int.__lt__, + "<=": int.__le__, + "!=": int.__ne__, + "is not": int.__ne__, + ">": int.__gt__, + ">=": int.__ge__, +} + +flip_ops: Final = {"<": ">", "<=": ">=", ">": "<", ">=": "<="} +neg_ops: Final = { + "==": "!=", + "!=": "==", + "is": "is not", + "is not": "is", + "<": ">=", + "<=": ">", + ">": "<=", + ">=": "<", +} diff --git a/mypy/options.py b/mypy/options.py index 603ba79935ee..cb0464d4dc06 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -72,7 +72,8 @@ class BuildType: # Features that are currently incomplete/experimental TYPE_VAR_TUPLE: Final = "TypeVarTuple" UNPACK: Final = "Unpack" -INCOMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) +PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" +INCOMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK, PRECISE_TUPLE_TYPES)) class Options: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 638553883dd8..b79e0e628849 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -640,7 +640,7 @@ def visit_param_spec(self, left: ParamSpecType) -> bool: def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: right = self.right if isinstance(right, TypeVarTupleType) and right.id == left.id: - return True + return left.min_len >= right.min_len return self._is_subtype(left.upper_bound, self.right) def visit_unpack_type(self, left: UnpackType) -> bool: diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 85fbe5dc2990..591421465a97 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -126,7 +126,7 @@ def run_case_once( options = parse_options(original_program_text, testcase, incremental_step) options.use_builtins_fixtures = True if not testcase.name.endswith("_no_incomplete"): - options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] + options.enable_incomplete_feature += [TYPE_VAR_TUPLE, UNPACK] options.show_traceback = True # Enable some options automatically based on test file name. diff --git a/mypy/typeops.py b/mypy/typeops.py index 37817933a397..dff43775fe3d 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -981,7 +981,7 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool method = typ.type.get(name) if method and isinstance(method.node, (SYMBOL_FUNCBASE_TYPES, Decorator, Var)): if method.node.info: - return not method.node.info.fullname.startswith("builtins.") + return not method.node.info.fullname.startswith(("builtins.", "typing.")) return False if isinstance(typ, UnionType): if check_all: diff --git a/mypy/types.py b/mypy/types.py index d0c19a08e60a..d08e9e7a890c 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -802,6 +802,8 @@ class TypeVarTupleType(TypeVarLikeType): See PEP646 for more information. """ + __slots__ = ("tuple_fallback", "min_len") + def __init__( self, name: str, @@ -813,9 +815,13 @@ def __init__( *, line: int = -1, column: int = -1, + min_len: int = 0, ) -> None: super().__init__(name, fullname, id, upper_bound, default, line=line, column=column) self.tuple_fallback = tuple_fallback + # This value is not settable by a user. It is an internal-only thing to support + # len()-narrowing of variadic tuples. + self.min_len = min_len def serialize(self) -> JsonDict: assert not self.id.is_meta_var() @@ -827,6 +833,7 @@ def serialize(self) -> JsonDict: "upper_bound": self.upper_bound.serialize(), "tuple_fallback": self.tuple_fallback.serialize(), "default": self.default.serialize(), + "min_len": self.min_len, } @classmethod @@ -839,18 +846,19 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType: deserialize_type(data["upper_bound"]), Instance.deserialize(data["tuple_fallback"]), deserialize_type(data["default"]), + min_len=data["min_len"], ) def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_type_var_tuple(self) def __hash__(self) -> int: - return hash(self.id) + return hash((self.id, self.min_len)) def __eq__(self, other: object) -> bool: if not isinstance(other, TypeVarTupleType): return NotImplemented - return self.id == other.id + return self.id == other.id and self.min_len == other.min_len def copy_modified( self, @@ -858,6 +866,7 @@ def copy_modified( id: Bogus[TypeVarId | int] = _dummy, upper_bound: Bogus[Type] = _dummy, default: Bogus[Type] = _dummy, + min_len: Bogus[int] = _dummy, **kwargs: Any, ) -> TypeVarTupleType: return TypeVarTupleType( @@ -869,6 +878,7 @@ def copy_modified( self.default if default is _dummy else default, line=self.line, column=self.column, + min_len=self.min_len if min_len is _dummy else min_len, ) @@ -2354,7 +2364,18 @@ def can_be_false_default(self) -> bool: # Corner case: it is a `NamedTuple` with `__bool__` method defined. # It can be anything: both `True` and `False`. return True - return self.length() == 0 + if self.length() == 0: + return True + if self.length() > 1: + return False + # Special case tuple[*Ts] may or may not be false. + item = self.items[0] + if not isinstance(item, UnpackType): + return False + if not isinstance(item.type, TypeVarTupleType): + # Non-normalized tuple[int, ...] can be false. + return True + return item.type.min_len == 0 def can_be_any_bool(self) -> bool: return bool( diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 6e1ad8187b7a..093926d4c415 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -10,6 +10,7 @@ python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ new_type_inference = True enable_error_code = ignore-without-code,redundant-expr +enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True [mypy-mypy.visitor] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index a3c1bc8795f2..4ac5512580d2 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2365,6 +2365,19 @@ b"x" in data [builtins fixtures/primitives.pyi] [typing fixtures/typing-full.pyi] +[case testStrictEqualityWithDifferentMapTypes] +# flags: --strict-equality +from typing import Mapping + +class A(Mapping[int, str]): ... +class B(Mapping[int, str]): ... + +a: A +b: B +assert a == b +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + [case testUnimportedHintAny] def f(x: Any) -> None: # E: Name "Any" is not defined \ # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any") diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 6e3628060617..9fa098b28dee 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -878,7 +878,7 @@ reveal_type(Child.class_method()) # N: Revealed type is "Tuple[builtins.str, fa [builtins fixtures/classmethod.pyi] [case testNamedTupleAsConditionalStrictOptionalDisabled] -# flags: --no-strict-optional +# flags: --no-strict-optional --warn-unreachable from typing import NamedTuple class C(NamedTuple): diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index c86cffd453df..5b7fadf41c79 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1334,3 +1334,579 @@ if isinstance(some, raw): else: reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]" [builtins fixtures/dict.pyi] + +[case testNarrowingWithAnyOps] +from typing import Any + +class C: ... +class D(C): ... +tp: Any + +c: C +if isinstance(c, tp) or isinstance(c, D): + reveal_type(c) # N: Revealed type is "Union[Any, __main__.D]" +else: + reveal_type(c) # N: Revealed type is "__main__.C" +reveal_type(c) # N: Revealed type is "__main__.C" + +c1: C +if isinstance(c1, tp) and isinstance(c1, D): + reveal_type(c1) # N: Revealed type is "Any" +else: + reveal_type(c1) # N: Revealed type is "__main__.C" +reveal_type(c1) # N: Revealed type is "__main__.C" + +c2: C +if isinstance(c2, D) or isinstance(c2, tp): + reveal_type(c2) # N: Revealed type is "Union[__main__.D, Any]" +else: + reveal_type(c2) # N: Revealed type is "__main__.C" +reveal_type(c2) # N: Revealed type is "__main__.C" + +c3: C +if isinstance(c3, D) and isinstance(c3, tp): + reveal_type(c3) # N: Revealed type is "Any" +else: + reveal_type(c3) # N: Revealed type is "__main__.C" +reveal_type(c3) # N: Revealed type is "__main__.C" + +t: Any +if isinstance(t, (list, tuple)) and isinstance(t, tuple): + reveal_type(t) # N: Revealed type is "builtins.tuple[Any, ...]" +else: + reveal_type(t) # N: Revealed type is "Any" +reveal_type(t) # N: Revealed type is "Any" +[builtins fixtures/isinstancelist.pyi] + +[case testNarrowingLenItemAndLenCompare] +from typing import Any + +x: Any +if len(x) == x: + reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTuple] +from typing import Tuple, Union + +VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] + +x: VarTuple +a = b = c = 0 +if len(x) == 3: + a, b, c = x +else: + a, b = x + +if len(x) != 3: + a, b = x +else: + a, b, c = x +[builtins fixtures/len.pyi] + +[case testNarrowingLenHomogeneousTuple] +from typing import Tuple + +x: Tuple[int, ...] +if len(x) == 3: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" +else: + reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" + +if len(x) != 3: + reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTypeUnaffected] +from typing import Union, List + +x: Union[str, List[int]] +if len(x) == 3: + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.list[builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.list[builtins.int]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenAnyListElseNotAffected] +from typing import Any + +def f(self, value: Any) -> Any: + if isinstance(value, list) and len(value) == 0: + reveal_type(value) # N: Revealed type is "builtins.list[Any]" + return value + reveal_type(value) # N: Revealed type is "Any" + return None +[builtins fixtures/len.pyi] + +[case testNarrowingLenMultiple] +from typing import Tuple, Union + +VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] + +x: VarTuple +y: VarTuple +if len(x) == len(y) == 3: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenFinal] +from typing import Tuple, Union +from typing_extensions import Final + +VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] + +x: VarTuple +fin: Final = 3 +if len(x) == fin: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenGreaterThan] +from typing import Tuple, Union + +VarTuple = Union[Tuple[int], Tuple[int, int], Tuple[int, int, int]] + +x: VarTuple +if len(x) > 1: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" + +if len(x) < 2: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + +if len(x) >= 2: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" + +if len(x) <= 2: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBothSidesUnionTuples] +from typing import Tuple, Union + +VarTuple = Union[ + Tuple[int], + Tuple[int, int], + Tuple[int, int, int], + Tuple[int, int, int, int], +] + +x: VarTuple +if 2 <= len(x) <= 3: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenGreaterThanHomogeneousTupleShort] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple + +VarTuple = Tuple[int, ...] + +x: VarTuple +if len(x) < 3: + reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBiggerThanHomogeneousTupleLong] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple + +VarTuple = Tuple[int, ...] + +x: VarTuple +if len(x) < 30: + reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +else: + reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBothSidesHomogeneousTuple] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple + +x: Tuple[int, ...] +if 1 < len(x) < 4: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]]" +reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenUnionTupleUnreachable] +# flags: --warn-unreachable +from typing import Tuple, Union + +x: Union[Tuple[int, int], Tuple[int, int, int]] +if len(x) >= 4: + reveal_type(x) # E: Statement is unreachable +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + +if len(x) < 2: + reveal_type(x) # E: Statement is unreachable +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenMixedTypes] +from typing import Tuple, List, Union + +x: Union[Tuple[int, int], Tuple[int, int, int], List[int]] +a = b = c = 0 +if len(x) == 3: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" + a, b, c = x +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" + a, b = x + +if len(x) != 3: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" + a, b = x +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" + a, b, c = x +[builtins fixtures/len.pyi] + +[case testNarrowingLenTypeVarTupleEquals] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(x: Tuple[int, Unpack[Ts], str]) -> None: + if len(x) == 5: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + + if len(x) != 5: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTypeVarTupleGreaterThan] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(x: Tuple[int, Unpack[Ts], str]) -> None: + if len(x) > 5: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x[5]) # N: Revealed type is "builtins.object" + reveal_type(x[-6]) # N: Revealed type is "builtins.object" + reveal_type(x[-1]) # N: Revealed type is "builtins.str" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + + if len(x) < 5: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + x[5] # E: Tuple index out of range \ + # N: Variadic tuple can have length 5 + x[-6] # E: Tuple index out of range \ + # N: Variadic tuple can have length 5 + x[2] # E: Tuple index out of range \ + # N: Variadic tuple can have length 2 + x[-3] # E: Tuple index out of range \ + # N: Variadic tuple can have length 2 +[builtins fixtures/len.pyi] + +[case testNarrowingLenTypeVarTupleUnreachable] +# flags: --warn-unreachable +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def foo(x: Tuple[int, Unpack[Ts], str]) -> None: + if len(x) == 1: + reveal_type(x) # E: Statement is unreachable + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + + if len(x) != 1: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + else: + reveal_type(x) # E: Statement is unreachable + +def bar(x: Tuple[int, Unpack[Ts], str]) -> None: + if len(x) >= 2: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + else: + reveal_type(x) # E: Statement is unreachable + + if len(x) < 2: + reveal_type(x) # E: Statement is unreachable + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenVariadicTupleEquals] +from typing import Tuple +from typing_extensions import Unpack + +def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + if len(x) == 4: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + + if len(x) != 4: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenVariadicTupleGreaterThan] +from typing import Tuple +from typing_extensions import Unpack + +def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + if len(x) > 3: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], Tuple[builtins.int, builtins.float, builtins.str]]" + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + + if len(x) < 3: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenVariadicTupleUnreachable] +# flags: --warn-unreachable +from typing import Tuple +from typing_extensions import Unpack + +def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + if len(x) == 1: + reveal_type(x) # E: Statement is unreachable + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + + if len(x) != 1: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + else: + reveal_type(x) # E: Statement is unreachable + +def bar(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: + if len(x) >= 2: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + else: + reveal_type(x) # E: Statement is unreachable + + if len(x) < 2: + reveal_type(x) # E: Statement is unreachable + else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBareExpressionPrecise] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple + +x: Tuple[int, ...] +assert x +reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBareExpressionTypeVarTuple] +from typing import Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +def test(*xs: Unpack[Ts]) -> None: + assert xs + xs[0] # OK +[builtins fixtures/len.pyi] + +[case testNarrowingLenBareExpressionWithNonePrecise] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple, Optional + +x: Optional[Tuple[int, ...]] +if x: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[()], None]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenBareExpressionWithNoneImprecise] +from typing import Tuple, Optional + +x: Optional[Tuple[int, ...]] +if x: + reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +else: + reveal_type(x) # N: Revealed type is "Union[builtins.tuple[builtins.int, ...], None]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenMixWithAnyPrecise] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Any + +x: Any +if isinstance(x, (list, tuple)) and len(x) == 0: + reveal_type(x) # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]" +else: + reveal_type(x) # N: Revealed type is "Any" +reveal_type(x) # N: Revealed type is "Any" + +x1: Any +if isinstance(x1, (list, tuple)) and len(x1) > 1: + reveal_type(x1) # N: Revealed type is "Union[Tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]], builtins.list[Any]]" +else: + reveal_type(x1) # N: Revealed type is "Any" +reveal_type(x1) # N: Revealed type is "Any" +[builtins fixtures/len.pyi] + +[case testNarrowingLenMixWithAnyImprecise] +from typing import Any + +x: Any +if isinstance(x, (list, tuple)) and len(x) == 0: + reveal_type(x) # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]" +else: + reveal_type(x) # N: Revealed type is "Any" +reveal_type(x) # N: Revealed type is "Any" + +x1: Any +if isinstance(x1, (list, tuple)) and len(x1) > 1: + reveal_type(x1) # N: Revealed type is "Union[builtins.tuple[Any, ...], builtins.list[Any]]" +else: + reveal_type(x1) # N: Revealed type is "Any" +reveal_type(x1) # N: Revealed type is "Any" +[builtins fixtures/len.pyi] + +[case testNarrowingLenExplicitLiteralTypes] +from typing import Tuple, Union +from typing_extensions import Literal + +VarTuple = Union[ + Tuple[int], + Tuple[int, int], + Tuple[int, int, int], +] +x: VarTuple + +supported: Literal[2] +if len(x) == supported: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + +not_supported_yet: Literal[2, 3] +if len(x) == not_supported_yet: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenUnionOfVariadicTuples] +from typing import Tuple, Union + +x: Union[Tuple[int, ...], Tuple[str, ...]] +if len(x) == 2: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]" +else: + reveal_type(x) # N: Revealed type is "Union[builtins.tuple[builtins.int, ...], builtins.tuple[builtins.str, ...]]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenUnionOfNamedTuples] +from typing import NamedTuple, Union + +class Point2D(NamedTuple): + x: int + y: int +class Point3D(NamedTuple): + x: int + y: int + z: int + +x: Union[Point2D, Point3D] +if len(x) == 2: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Point2D]" +else: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTupleSubclass] +from typing import Tuple + +class Ints(Tuple[int, ...]): + size: int + +x: Ints +if len(x) == 2: + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Ints]" + reveal_type(x.size) # N: Revealed type is "builtins.int" +else: + reveal_type(x) # N: Revealed type is "__main__.Ints" + reveal_type(x.size) # N: Revealed type is "builtins.int" + +reveal_type(x) # N: Revealed type is "__main__.Ints" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTupleSubclassCustomNotAllowed] +from typing import Tuple + +class Ints(Tuple[int, ...]): + def __len__(self) -> int: + return 0 + +x: Ints +if len(x) > 2: + reveal_type(x) # N: Revealed type is "__main__.Ints" +else: + reveal_type(x) # N: Revealed type is "__main__.Ints" +[builtins fixtures/len.pyi] + +[case testNarrowingLenTupleSubclassPreciseNotAllowed] +# flags: --enable-incomplete-feature=PreciseTupleTypes +from typing import Tuple + +class Ints(Tuple[int, ...]): + size: int + +x: Ints +if len(x) > 2: + reveal_type(x) # N: Revealed type is "__main__.Ints" +else: + reveal_type(x) # N: Revealed type is "__main__.Ints" +[builtins fixtures/len.pyi] + +[case testNarrowingLenUnknownLen] +from typing import Any, Tuple, Union + +x: Union[Tuple[int, int], Tuple[int, int, int]] + +n: int +if len(x) == n: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + +a: Any +if len(x) == a: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +else: + reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" +[builtins fixtures/len.pyi] diff --git a/test-data/unit/fixtures/len.pyi b/test-data/unit/fixtures/len.pyi new file mode 100644 index 000000000000..c72596661858 --- /dev/null +++ b/test-data/unit/fixtures/len.pyi @@ -0,0 +1,39 @@ +from typing import Tuple, TypeVar, Generic, Union, Type, Sequence, Mapping +from typing_extensions import Protocol + +T = TypeVar("T") +V = TypeVar("V") + +class object: + def __init__(self) -> None: pass + +class type: + def __init__(self, x) -> None: pass + +class tuple(Generic[T]): + def __len__(self) -> int: pass + +class list(Sequence[T]): pass +class dict(Mapping[T, V]): pass + +class function: pass + +class Sized(Protocol): + def __len__(self) -> int: pass + +def len(__obj: Sized) -> int: ... +def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass + +class int: + def __add__(self, other: int) -> int: pass + def __eq__(self, other: int) -> bool: pass + def __ne__(self, other: int) -> bool: pass + def __lt__(self, n: int) -> bool: pass + def __gt__(self, n: int) -> bool: pass + def __le__(self, n: int) -> bool: pass + def __ge__(self, n: int) -> bool: pass + def __neg__(self) -> int: pass +class float: pass +class bool(int): pass +class str(Sequence[str]): pass +class ellipsis: pass diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index b35b64a383c9..5f458ca687c0 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -48,6 +48,7 @@ class Generator(Iterator[T], Generic[T, U, V]): class Sequence(Iterable[T_co]): def __getitem__(self, n: Any) -> T_co: pass + def __len__(self) -> int: pass # Mapping type is oversimplified intentionally. class Mapping(Iterable[T], Generic[T, T_co]): From 27c4b462aa4cf269397253eca7a88e7fbbf4e43e Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 21 Oct 2023 22:25:20 +0200 Subject: [PATCH 0330/1617] stubgen: fix missing property setter in semantic analysis mode (#16303) The semantic analyzer treats properties as overloaded functions. This was previously ignored by stubgen but regressed in #15232. This PR restores the original behavior. Fixes #16300 --- mypy/stubgen.py | 1 + mypy/stubutil.py | 2 -- test-data/unit/stubgen.test | 18 ++++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 395a49fa4e08..a2f07a35eaa2 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -633,6 +633,7 @@ def process_decorator(self, o: Decorator) -> None: Only preserve certain special decorators such as @abstractmethod. """ + o.func.is_overload = False for decorator in o.original_decorators: if not isinstance(decorator, (NameExpr, MemberExpr)): continue diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 22e525c14e7c..cc3b63098fd2 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -669,8 +669,6 @@ def set_defined_names(self, defined_names: set[str]) -> None: self.add_name(f"{pkg}.{t}", require=False) def check_undefined_names(self) -> None: - print(self._all_) - print(self._toplevel_names) undefined_names = [name for name in self._all_ or [] if name not in self._toplevel_names] if undefined_names: if self._output: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index d83d74306230..64a1353b29b3 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -377,6 +377,24 @@ class A: def f(self, x) -> None: ... def h(self) -> None: ... +[case testProperty_semanal] +class A: + @property + def f(self): + return 1 + @f.setter + def f(self, x): ... + + def h(self): + self.f = 1 +[out] +class A: + @property + def f(self): ... + @f.setter + def f(self, x) -> None: ... + def h(self) -> None: ... + -- a read/write property is treated the same as an attribute [case testProperty_inspect] class A: From 2d54024cb44556302b40fed6e0bd40fd9ef56563 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 21 Oct 2023 17:06:42 -0700 Subject: [PATCH 0331/1617] [mypyc] Don't crash on unreachable statements (#16311) Skip them instead. This applies to statements after break, continue, return and raise statements. It's common to have unreachable statements temporarily while working on a half-finished change, so generating an error is perhaps not the best option. Fixes mypyc/mypyc#1028. --- mypyc/irbuild/builder.py | 11 ++ mypyc/irbuild/statement.py | 5 + mypyc/irbuild/visitor.py | 4 + mypyc/test-data/irbuild-unreachable.test | 137 ++++++++++++++++++++++- 4 files changed, 156 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 0757415f6753..573ca334a5d1 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -165,6 +165,9 @@ def __init__( self.runtime_args: list[list[RuntimeArg]] = [[]] self.function_name_stack: list[str] = [] self.class_ir_stack: list[ClassIR] = [] + # Keep track of whether the next statement in a block is reachable + # or not, separately for each block nesting level + self.block_reachable_stack: list[bool] = [True] self.current_module = current_module self.mapper = mapper @@ -1302,6 +1305,14 @@ def is_native_attr_ref(self, expr: MemberExpr) -> bool: and not obj_rtype.class_ir.get_method(expr.name) ) + def mark_block_unreachable(self) -> None: + """Mark statements in the innermost block being processed as unreachable. + + This should be called after a statement that unconditionally leaves the + block, such as 'break' or 'return'. + """ + self.block_reachable_stack[-1] = False + # Lacks a good type because there wasn't a reasonable type in 3.5 :( def catch_errors(self, line: int) -> Any: return catch_errors(self.module_path, line) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index d7e01456139d..2c17eb2bb14d 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -118,8 +118,13 @@ def transform_block(builder: IRBuilder, block: Block) -> None: if not block.is_unreachable: + builder.block_reachable_stack.append(True) for stmt in block.body: builder.accept(stmt) + if not builder.block_reachable_stack[-1]: + # The rest of the block is unreachable, so skip it + break + builder.block_reachable_stack.pop() # Raise a RuntimeError if we hit a non-empty unreachable block. # Don't complain about empty unreachable blocks, since mypy inserts # those after `if MYPY`. diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index d8725ee04dc5..12e186fd40d8 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -194,6 +194,7 @@ def visit_expression_stmt(self, stmt: ExpressionStmt) -> None: def visit_return_stmt(self, stmt: ReturnStmt) -> None: transform_return_stmt(self.builder, stmt) + self.builder.mark_block_unreachable() def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None: transform_assignment_stmt(self.builder, stmt) @@ -212,12 +213,15 @@ def visit_for_stmt(self, stmt: ForStmt) -> None: def visit_break_stmt(self, stmt: BreakStmt) -> None: transform_break_stmt(self.builder, stmt) + self.builder.mark_block_unreachable() def visit_continue_stmt(self, stmt: ContinueStmt) -> None: transform_continue_stmt(self.builder, stmt) + self.builder.mark_block_unreachable() def visit_raise_stmt(self, stmt: RaiseStmt) -> None: transform_raise_stmt(self.builder, stmt) + self.builder.mark_block_unreachable() def visit_try_stmt(self, stmt: TryStmt) -> None: transform_try_stmt(self.builder, stmt) diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test index 1c024a249bf1..b5188c91ac58 100644 --- a/mypyc/test-data/irbuild-unreachable.test +++ b/mypyc/test-data/irbuild-unreachable.test @@ -1,4 +1,4 @@ -# Test cases for unreachable expressions +# Test cases for unreachable expressions and statements [case testUnreachableMemberExpr] import sys @@ -104,3 +104,138 @@ L5: L6: y = r11 return 1 + +[case testUnreachableStatementAfterReturn] +def f(x: bool) -> int: + if x: + return 1 + f(False) + return 2 +[out] +def f(x): + x :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + return 2 +L2: + return 4 + +[case testUnreachableStatementAfterContinue] +def c() -> bool: + return False + +def f() -> None: + n = True + while n: + if c(): + continue + if int(): + f() + n = False +[out] +def c(): +L0: + return 0 +def f(): + n, r0 :: bool +L0: + n = 1 +L1: + if n goto L2 else goto L5 :: bool +L2: + r0 = c() + if r0 goto L3 else goto L4 :: bool +L3: + goto L1 +L4: + n = 0 + goto L1 +L5: + return 1 + +[case testUnreachableStatementAfterBreak] +def c() -> bool: + return False + +def f() -> None: + n = True + while n: + if c(): + break + if int(): + f() + n = False +[out] +def c(): +L0: + return 0 +def f(): + n, r0 :: bool +L0: + n = 1 +L1: + if n goto L2 else goto L5 :: bool +L2: + r0 = c() + if r0 goto L3 else goto L4 :: bool +L3: + goto L5 +L4: + n = 0 + goto L1 +L5: + return 1 + +[case testUnreachableStatementAfterRaise] +def f(x: bool) -> int: + if x: + raise ValueError() + print('hello') + return 2 +[out] +def f(x): + x :: bool + r0 :: object + r1 :: str + r2, r3 :: object +L0: + if x goto L1 else goto L2 :: bool +L1: + r0 = builtins :: module + r1 = 'ValueError' + r2 = CPyObject_GetAttr(r0, r1) + r3 = PyObject_CallFunctionObjArgs(r2, 0) + CPy_Raise(r3) + unreachable +L2: + return 4 + +[case testUnreachableStatementAfterAssertFalse] +def f(x: bool) -> int: + if x: + assert False + print('hello') + return 2 +[out] +def f(x): + x, r0 :: bool + r1 :: str + r2 :: object + r3 :: str + r4, r5 :: object +L0: + if x goto L1 else goto L4 :: bool +L1: + if 0 goto L3 else goto L2 :: bool +L2: + r0 = raise AssertionError + unreachable +L3: + r1 = 'hello' + r2 = builtins :: module + r3 = 'print' + r4 = CPyObject_GetAttr(r2, r3) + r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) +L4: + return 4 From 341929b10df327796ef60da4837b907d6af1b7d9 Mon Sep 17 00:00:00 2001 From: Ihor <31508183+nautics889@users.noreply.github.com> Date: Mon, 23 Oct 2023 08:16:58 +0300 Subject: [PATCH 0332/1617] refactor: `__str__` in `CFG` class (#16307) (#16308) Closes https://github.com/python/mypy/issues/16307. --- mypyc/analysis/dataflow.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index cade0c823962..57ad2b17fcc5 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -72,11 +72,8 @@ def __init__( self.exits = exits def __str__(self) -> str: - lines = [] - lines.append("exits: %s" % sorted(self.exits, key=lambda e: int(e.label))) - lines.append("succ: %s" % self.succ) - lines.append("pred: %s" % self.pred) - return "\n".join(lines) + exits = sorted(self.exits, key=lambda e: int(e.label)) + return f"exits: {exits}\nsucc: {self.succ}\npred: {self.pred}" def get_cfg(blocks: list[BasicBlock]) -> CFG: From cda163d378d6f85627b72454918cba323bf37749 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 23 Oct 2023 06:48:37 +0100 Subject: [PATCH 0333/1617] Clarify variance convention for Parameters (#16302) Fixes https://github.com/python/mypy/issues/16296 In my big refactoring I flipped the variance convention for the `Parameters` type, but I did it inconsistently in one place. After working some more with ParamSpecs, it now seems to me the original convention is easier to remember. I also now explicitly put it in the type docstring. --- mypy/constraints.py | 9 ++---- mypy/join.py | 13 ++++++--- mypy/meet.py | 6 ++-- mypy/subtypes.py | 2 -- mypy/types.py | 5 +++- .../unit/check-parameter-specification.test | 29 ++++++++++++++++++- 6 files changed, 47 insertions(+), 17 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 58d0f4dbed29..7d782551b261 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -692,11 +692,8 @@ def visit_parameters(self, template: Parameters) -> list[Constraint]: return self.infer_against_any(template.arg_types, self.actual) if type_state.infer_polymorphic and isinstance(self.actual, Parameters): # For polymorphic inference we need to be able to infer secondary constraints - # in situations like [x: T] <: P <: [x: int]. Note we invert direction, since - # this function expects direction between callables. - return infer_callable_arguments_constraints( - template, self.actual, neg_op(self.direction) - ) + # in situations like [x: T] <: P <: [x: int]. + return infer_callable_arguments_constraints(template, self.actual, self.direction) raise RuntimeError("Parameters cannot be constrained to") # Non-leaf types @@ -1128,7 +1125,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) ) if param_spec_target is not None: - res.append(Constraint(param_spec, neg_op(self.direction), param_spec_target)) + res.append(Constraint(param_spec, self.direction, param_spec_target)) if extra_tvars: for c in res: c.extra_tvars += cactual.variables diff --git a/mypy/join.py b/mypy/join.py index e4429425d98a..2e2939f9fbc8 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -350,10 +350,13 @@ def visit_parameters(self, t: Parameters) -> ProperType: if isinstance(self.s, Parameters): if len(t.arg_types) != len(self.s.arg_types): return self.default(self.s) + from mypy.meet import meet_types + return t.copy_modified( - # Note that since during constraint inference we already treat whole ParamSpec as - # contravariant, we should join individual items, not meet them like for Callables - arg_types=[join_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)] + arg_types=[ + meet_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types) + ], + arg_names=combine_arg_names(self.s, t), ) else: return self.default(self.s) @@ -754,7 +757,9 @@ def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: ) -def combine_arg_names(t: CallableType, s: CallableType) -> list[str | None]: +def combine_arg_names( + t: CallableType | Parameters, s: CallableType | Parameters +) -> list[str | None]: """Produces a list of argument names compatible with both callables. For example, suppose 't' and 's' have the following signatures: diff --git a/mypy/meet.py b/mypy/meet.py index e3645c7b5879..1a566aed17de 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -708,10 +708,10 @@ def visit_parameters(self, t: Parameters) -> ProperType: if isinstance(self.s, Parameters): if len(t.arg_types) != len(self.s.arg_types): return self.default(self.s) + from mypy.join import join_types + return t.copy_modified( - # Note that since during constraint inference we already treat whole ParamSpec as - # contravariant, we should meet individual items, not join them like for Callables - arg_types=[meet_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)] + arg_types=[join_types(s_a, t_a) for s_a, t_a in zip(self.s.arg_types, t.arg_types)] ) else: return self.default(self.s) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index b79e0e628849..2ca3357dd722 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -654,8 +654,6 @@ def visit_unpack_type(self, left: UnpackType) -> bool: def visit_parameters(self, left: Parameters) -> bool: if isinstance(self.right, Parameters): - # TODO: direction here should be opposite, this function expects - # order of callables, while parameters are contravariant. return are_parameters_compatible( left, self.right, diff --git a/mypy/types.py b/mypy/types.py index d08e9e7a890c..ae1a1f595fa2 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1562,7 +1562,10 @@ class FormalArgument(NamedTuple): class Parameters(ProperType): """Type that represents the parameters to a function. - Used for ParamSpec analysis.""" + Used for ParamSpec analysis. Note that by convention we handle this + type as a Callable without return type, not as a "tuple with names", + so that it behaves contravariantly, in particular [x: int] <: [int]. + """ __slots__ = ( "arg_types", diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 48fadbc96c90..db8c76fd21e9 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1403,7 +1403,7 @@ def wrong_name_constructor(b: bool) -> SomeClass: func(SomeClass, constructor) reveal_type(func(SomeClass, wrong_constructor)) # N: Revealed type is "def (a: Never) -> __main__.SomeClass" reveal_type(func_regular(SomeClass, wrong_constructor)) # N: Revealed type is "def (Never) -> __main__.SomeClass" -func(SomeClass, wrong_name_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[Never], SomeClass]" +reveal_type(func(SomeClass, wrong_name_constructor)) # N: Revealed type is "def (Never) -> __main__.SomeClass" [builtins fixtures/paramspec.pyi] [case testParamSpecInTypeAliasBasic] @@ -2059,3 +2059,30 @@ def test2(x: int, y: int) -> str: ... reveal_type(call(test1, 1)) # N: Revealed type is "builtins.str" reveal_type(call(test2, 1, 2)) # N: Revealed type is "builtins.str" [builtins fixtures/paramspec.pyi] + +[case testParamSpecCorrectParameterNameInference] +from typing import Callable, Protocol +from typing_extensions import ParamSpec, Concatenate + +def a(i: int) -> None: ... +def b(__i: int) -> None: ... + +class WithName(Protocol): + def __call__(self, i: int) -> None: ... +NoName = Callable[[int], None] + +def f1(__fn: WithName, i: int) -> None: ... +def f2(__fn: NoName, i: int) -> None: ... + +P = ParamSpec("P") +def d(f: Callable[P, None], fn: Callable[Concatenate[Callable[P, None], P], None]) -> Callable[P, None]: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: + fn(f, *args, **kwargs) + return inner + +reveal_type(d(a, f1)) # N: Revealed type is "def (i: builtins.int)" +reveal_type(d(a, f2)) # N: Revealed type is "def (i: builtins.int)" +reveal_type(d(b, f1)) # E: Cannot infer type argument 1 of "d" \ + # N: Revealed type is "def (*Any, **Any)" +reveal_type(d(b, f2)) # N: Revealed type is "def (builtins.int)" +[builtins fixtures/paramspec.pyi] From 8236c93d899fa5225eb23644db802cf1e09196a7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 23 Oct 2023 15:52:42 +0300 Subject: [PATCH 0334/1617] Add `|=` and `|` operators support for `TypedDict` (#16249) Please, note that there are several problems with `__ror__` definitions. 1. `dict.__ror__` does not define support for `Mapping?` types. For example: ```python >>> import types >>> {'a': 1} | types.MappingProxyType({'b': 2}) {'a': 1, 'b': 2} >>> ``` 2. `TypedDict.__ror__` also does not define this support So, I would like to defer this feature for the future, we need some discussion to happen. However, this PR does fully solve the problem OP had. Closes https://github.com/python/mypy/issues/16244 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 19 ++- mypy/checkexpr.py | 55 ++++++- mypy/plugins/default.py | 22 ++- test-data/unit/check-typeddict.test | 143 ++++++++++++++++++ test-data/unit/fixtures/dict.pyi | 19 ++- test-data/unit/fixtures/typing-async.pyi | 1 + test-data/unit/fixtures/typing-full.pyi | 1 + test-data/unit/fixtures/typing-medium.pyi | 1 + .../unit/fixtures/typing-typeddict-iror.pyi | 66 ++++++++ 9 files changed, 316 insertions(+), 11 deletions(-) create mode 100644 test-data/unit/fixtures/typing-typeddict-iror.pyi diff --git a/mypy/checker.py b/mypy/checker.py index 02bab37aa13f..64bbbfa0a55b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7783,14 +7783,25 @@ def infer_operator_assignment_method(typ: Type, operator: str) -> tuple[bool, st """ typ = get_proper_type(typ) method = operators.op_methods[operator] + existing_method = None if isinstance(typ, Instance): - if operator in operators.ops_with_inplace_method: - inplace_method = "__i" + method[2:] - if typ.type.has_readable_member(inplace_method): - return True, inplace_method + existing_method = _find_inplace_method(typ, method, operator) + elif isinstance(typ, TypedDictType): + existing_method = _find_inplace_method(typ.fallback, method, operator) + + if existing_method is not None: + return True, existing_method return False, method +def _find_inplace_method(inst: Instance, method: str, operator: str) -> str | None: + if operator in operators.ops_with_inplace_method: + inplace_method = "__i" + method[2:] + if inst.type.has_readable_member(inplace_method): + return inplace_method + return None + + def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: """Is an inferred type valid and needs no further refinement? diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2dc5a93a1de9..18c1c570ba91 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2,12 +2,13 @@ from __future__ import annotations +import enum import itertools import time from collections import defaultdict from contextlib import contextmanager from typing import Callable, ClassVar, Final, Iterable, Iterator, List, Optional, Sequence, cast -from typing_extensions import TypeAlias as _TypeAlias, overload +from typing_extensions import TypeAlias as _TypeAlias, assert_never, overload import mypy.checker import mypy.errorcodes as codes @@ -277,6 +278,20 @@ class Finished(Exception): """Raised if we can terminate overload argument check early (no match).""" +@enum.unique +class UseReverse(enum.Enum): + """Used in `visit_op_expr` to enable or disable reverse method checks.""" + + DEFAULT = 0 + ALWAYS = 1 + NEVER = 2 + + +USE_REVERSE_DEFAULT: Final = UseReverse.DEFAULT +USE_REVERSE_ALWAYS: Final = UseReverse.ALWAYS +USE_REVERSE_NEVER: Final = UseReverse.NEVER + + class ExpressionChecker(ExpressionVisitor[Type]): """Expression type checker. @@ -3371,6 +3386,24 @@ def visit_op_expr(self, e: OpExpr) -> Type: return proper_left_type.copy_modified( items=proper_left_type.items + [UnpackType(mapped)] ) + + use_reverse: UseReverse = USE_REVERSE_DEFAULT + if e.op == "|": + if is_named_instance(proper_left_type, "builtins.dict"): + # This is a special case for `dict | TypedDict`. + # 1. Find `dict | TypedDict` case + # 2. Switch `dict.__or__` to `TypedDict.__ror__` (the same from both runtime and typing perspective) + proper_right_type = get_proper_type(self.accept(e.right)) + if isinstance(proper_right_type, TypedDictType): + use_reverse = USE_REVERSE_ALWAYS + if isinstance(proper_left_type, TypedDictType): + # This is the reverse case: `TypedDict | dict`, + # simply do not allow the reverse checking: + # do not call `__dict__.__ror__`. + proper_right_type = get_proper_type(self.accept(e.right)) + if is_named_instance(proper_right_type, "builtins.dict"): + use_reverse = USE_REVERSE_NEVER + if TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature: # Handle tuple[X, ...] + tuple[Y, Z] = tuple[*tuple[X, ...], Y, Z]. if ( @@ -3390,7 +3423,25 @@ def visit_op_expr(self, e: OpExpr) -> Type: if e.op in operators.op_methods: method = operators.op_methods[e.op] - result, method_type = self.check_op(method, left_type, e.right, e, allow_reverse=True) + if use_reverse is UseReverse.DEFAULT or use_reverse is UseReverse.NEVER: + result, method_type = self.check_op( + method, + base_type=left_type, + arg=e.right, + context=e, + allow_reverse=use_reverse is UseReverse.DEFAULT, + ) + elif use_reverse is UseReverse.ALWAYS: + result, method_type = self.check_op( + # The reverse operator here gives better error messages: + operators.reverse_op_methods[method], + base_type=self.accept(e.right), + arg=e.left, + context=e, + allow_reverse=False, + ) + else: + assert_never(use_reverse) e.method_type = method_type return result else: diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index b60fc3873c04..ddcc37f465fe 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -74,12 +74,21 @@ def get_method_signature_hook( return typed_dict_setdefault_signature_callback elif fullname in {n + ".pop" for n in TPDICT_FB_NAMES}: return typed_dict_pop_signature_callback - elif fullname in {n + ".update" for n in TPDICT_FB_NAMES}: - return typed_dict_update_signature_callback elif fullname == "_ctypes.Array.__setitem__": return ctypes.array_setitem_callback elif fullname == singledispatch.SINGLEDISPATCH_CALLABLE_CALL_METHOD: return singledispatch.call_singledispatch_function_callback + + typed_dict_updates = set() + for n in TPDICT_FB_NAMES: + typed_dict_updates.add(n + ".update") + typed_dict_updates.add(n + ".__or__") + typed_dict_updates.add(n + ".__ror__") + typed_dict_updates.add(n + ".__ior__") + + if fullname in typed_dict_updates: + return typed_dict_update_signature_callback + return None def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: @@ -401,11 +410,16 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type: def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: - """Try to infer a better signature type for TypedDict.update.""" + """Try to infer a better signature type for methods that update `TypedDict`. + + This includes: `TypedDict.update`, `TypedDict.__or__`, `TypedDict.__ror__`, + and `TypedDict.__ior__`. + """ signature = ctx.default_signature if isinstance(ctx.type, TypedDictType) and len(signature.arg_types) == 1: arg_type = get_proper_type(signature.arg_types[0]) - assert isinstance(arg_type, TypedDictType) + if not isinstance(arg_type, TypedDictType): + return signature arg_type = arg_type.as_anonymous() arg_type = arg_type.copy_modified(required_keys=set()) if ctx.args and ctx.args[0]: diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 7ee9ef0b708b..0e1d800e0468 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3236,3 +3236,146 @@ def foo(x: int) -> Foo: ... f: Foo = {**foo("no")} # E: Argument 1 to "foo" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictWith__or__method] +from typing import Dict +from mypy_extensions import TypedDict + +class Foo(TypedDict): + key: int + +foo1: Foo = {'key': 1} +foo2: Foo = {'key': 2} + +reveal_type(foo1 | foo2) # N: Revealed type is "TypedDict('__main__.Foo', {'key': builtins.int})" +reveal_type(foo1 | {'key': 1}) # N: Revealed type is "TypedDict('__main__.Foo', {'key': builtins.int})" +reveal_type(foo1 | {'key': 'a'}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type(foo1 | {}) # N: Revealed type is "TypedDict('__main__.Foo', {'key': builtins.int})" + +d1: Dict[str, int] +d2: Dict[int, str] + +reveal_type(foo1 | d1) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +foo1 | d2 # E: Unsupported operand types for | ("Foo" and "Dict[int, str]") + + +class Bar(TypedDict): + key: int + value: str + +bar: Bar +reveal_type(bar | {}) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type(bar | {'key': 1, 'value': 'v'}) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type(bar | {'key': 1}) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type(bar | {'value': 'v'}) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type(bar | {'key': 'a'}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type(bar | {'value': 1}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type(bar | {'key': 'a', 'value': 1}) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" + +reveal_type(bar | foo1) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type(bar | d1) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +bar | d2 # E: Unsupported operand types for | ("Bar" and "Dict[int, str]") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictWith__or__method_error] +from mypy_extensions import TypedDict + +class Foo(TypedDict): + key: int + +foo: Foo = {'key': 1} +foo | 1 + +class SubDict(dict): ... +foo | SubDict() +[out] +main:7: error: No overload variant of "__or__" of "TypedDict" matches argument type "int" +main:7: note: Possible overload variants: +main:7: note: def __or__(self, TypedDict({'key'?: int}), /) -> Foo +main:7: note: def __or__(self, Dict[str, Any], /) -> Dict[str, object] +main:10: error: No overload variant of "__ror__" of "dict" matches argument type "Foo" +main:10: note: Possible overload variants: +main:10: note: def __ror__(self, Dict[Any, Any], /) -> Dict[Any, Any] +main:10: note: def [T, T2] __ror__(self, Dict[T, T2], /) -> Dict[Union[Any, T], Union[Any, T2]] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictWith__ror__method] +from typing import Dict +from mypy_extensions import TypedDict + +class Foo(TypedDict): + key: int + +foo: Foo = {'key': 1} + +reveal_type({'key': 1} | foo) # N: Revealed type is "TypedDict('__main__.Foo', {'key': builtins.int})" +reveal_type({'key': 'a'} | foo) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type({} | foo) # N: Revealed type is "TypedDict('__main__.Foo', {'key': builtins.int})" +{1: 'a'} | foo # E: Dict entry 0 has incompatible type "int": "str"; expected "str": "Any" + +d1: Dict[str, int] +d2: Dict[int, str] + +reveal_type(d1 | foo) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +d2 | foo # E: Unsupported operand types for | ("Dict[int, str]" and "Foo") +1 | foo # E: Unsupported left operand type for | ("int") + + +class Bar(TypedDict): + key: int + value: str + +bar: Bar +reveal_type({} | bar) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type({'key': 1, 'value': 'v'} | bar) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type({'key': 1} | bar) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type({'value': 'v'} | bar) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" +reveal_type({'key': 'a'} | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type({'value': 1} | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +reveal_type({'key': 'a', 'value': 1} | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" + +reveal_type(d1 | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" +d2 | bar # E: Unsupported operand types for | ("Dict[int, str]" and "Bar") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictWith__ior__method] +from typing import Dict +from mypy_extensions import TypedDict + +class Foo(TypedDict): + key: int + +foo: Foo = {'key': 1} +foo |= {'key': 2} + +foo |= {} +foo |= {'key': 'a', 'b': 'a'} # E: Expected TypedDict key "key" but found keys ("key", "b") \ + # E: Incompatible types (expression has type "str", TypedDict item "key" has type "int") +foo |= {'b': 2} # E: Unexpected TypedDict key "b" + +d1: Dict[str, int] +d2: Dict[int, str] + +foo |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int})" +foo |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int})" + + +class Bar(TypedDict): + key: int + value: str + +bar: Bar +bar |= {} +bar |= {'key': 1, 'value': 'a'} +bar |= {'key': 'a', 'value': 'a', 'b': 'a'} # E: Expected TypedDict keys ("key", "value") but found keys ("key", "value", "b") \ + # E: Incompatible types (expression has type "str", TypedDict item "key" has type "int") + +bar |= foo +bar |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int, 'value'?: str})" +bar |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index 19d175ff79ab..7c0c8767f7d7 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -3,10 +3,12 @@ from _typeshed import SupportsKeysAndGetItem import _typeshed from typing import ( - TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Sequence + TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Sequence, + Self, ) T = TypeVar('T') +T2 = TypeVar('T2') KT = TypeVar('KT') VT = TypeVar('VT') @@ -34,6 +36,21 @@ class dict(Mapping[KT, VT]): def get(self, k: KT, default: Union[VT, T]) -> Union[VT, T]: pass def __len__(self) -> int: ... + # This was actually added in 3.9: + @overload + def __or__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... + @overload + def __or__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... + @overload + def __ror__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... + @overload + def __ror__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... + # dict.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, __value: _typeshed.SupportsKeysAndGetItem[KT, VT]) -> Self: ... + @overload + def __ior__(self, __value: Iterable[Tuple[KT, VT]]) -> Self: ... + class int: # for convenience def __add__(self, x: Union[int, complex]) -> int: pass def __radd__(self, x: int) -> int: pass diff --git a/test-data/unit/fixtures/typing-async.pyi b/test-data/unit/fixtures/typing-async.pyi index b207dd599c33..9897dfd0b270 100644 --- a/test-data/unit/fixtures/typing-async.pyi +++ b/test-data/unit/fixtures/typing-async.pyi @@ -24,6 +24,7 @@ ClassVar = 0 Final = 0 Literal = 0 NoReturn = 0 +Self = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index e9f0aa199bb4..ef903ace78af 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -30,6 +30,7 @@ Literal = 0 TypedDict = 0 NoReturn = 0 NewType = 0 +Self = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/test-data/unit/fixtures/typing-medium.pyi b/test-data/unit/fixtures/typing-medium.pyi index 03be1d0a664d..c19c5d5d96e2 100644 --- a/test-data/unit/fixtures/typing-medium.pyi +++ b/test-data/unit/fixtures/typing-medium.pyi @@ -28,6 +28,7 @@ NoReturn = 0 NewType = 0 TypeAlias = 0 LiteralString = 0 +Self = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/test-data/unit/fixtures/typing-typeddict-iror.pyi b/test-data/unit/fixtures/typing-typeddict-iror.pyi new file mode 100644 index 000000000000..e452c8497109 --- /dev/null +++ b/test-data/unit/fixtures/typing-typeddict-iror.pyi @@ -0,0 +1,66 @@ +# Test stub for typing module that includes TypedDict `|` operator. +# It only covers `__or__`, `__ror__`, and `__ior__`. +# +# We cannot define these methods in `typing-typeddict.pyi`, +# because they need `dict` with two type args, +# and not all tests using `[typing typing-typeddict.pyi]` have the proper +# `dict` stub. +# +# Keep in sync with `typeshed`'s definition. +from abc import ABCMeta + +cast = 0 +assert_type = 0 +overload = 0 +Any = 0 +Union = 0 +Optional = 0 +TypeVar = 0 +Generic = 0 +Protocol = 0 +Tuple = 0 +Callable = 0 +NamedTuple = 0 +Final = 0 +Literal = 0 +TypedDict = 0 +NoReturn = 0 +Required = 0 +NotRequired = 0 +Self = 0 + +T = TypeVar('T') +T_co = TypeVar('T_co', covariant=True) +V = TypeVar('V') + +# Note: definitions below are different from typeshed, variances are declared +# to silence the protocol variance checks. Maybe it is better to use type: ignore? + +class Sized(Protocol): + def __len__(self) -> int: pass + +class Iterable(Protocol[T_co]): + def __iter__(self) -> 'Iterator[T_co]': pass + +class Iterator(Iterable[T_co], Protocol): + def __next__(self) -> T_co: pass + +class Sequence(Iterable[T_co]): + # misc is for explicit Any. + def __getitem__(self, n: Any) -> T_co: pass # type: ignore[misc] + +class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): + pass + +# Fallback type for all typed dicts (does not exist at runtime). +class _TypedDict(Mapping[str, object]): + @overload + def __or__(self, __value: Self) -> Self: ... + @overload + def __or__(self, __value: dict[str, Any]) -> dict[str, object]: ... + @overload + def __ror__(self, __value: Self) -> Self: ... + @overload + def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... + # supposedly incompatible definitions of __or__ and __ior__ + def __ior__(self, __value: Self) -> Self: ... # type: ignore[misc] From 167dc7095758ddc001119e1c9f330bff4af72b22 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 24 Oct 2023 15:45:04 -0700 Subject: [PATCH 0335/1617] Fix sdist build by not including CHANGELOG.md (#16323) This is an attempt to fix wheel builds. Perhaps we'd want to actually include the changelog in the sdist. We can decide this later after the build has bee fixed. We've been getting these errors: ``` ... lists of files in version control and sdist do not match! missing from sdist: CHANGELOG.md listing source files under version control: 830 files and directories building an sdist: mypy-1.7.0+dev.ffe89a21058eaa6eb1c1796d9ab87aece965e2d9.tar.gz: 829 files and directories copying source files to a temporary directory building a clean sdist: mypy-1.7.0+dev.tar.gz: 829 files and directories suggested MANIFEST.in rules: include *.md Error: Process completed with exit code 1. `` Example failure: https://github.com/mypyc/mypy_mypyc-wheels/actions/runs/6555980362/job/17805243900 --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3ae340c7bd5e..c18b83cc0088 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -42,7 +42,7 @@ include pytest.ini include tox.ini include LICENSE mypyc/README.md -exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md action.yml .editorconfig +exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md CHANGELOG.md action.yml .editorconfig exclude .git-blame-ignore-revs .pre-commit-config.yaml global-exclude *.py[cod] From 090a414ba022f600bd65e7611fa3691903fd5a74 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 25 Oct 2023 07:03:26 -0700 Subject: [PATCH 0336/1617] Run macOS mypyc tests with Python 3.9 (#16326) The 3.8 tests have been flaking for several weeks and I don't think anyone has a good repro or idea as to the cause --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86704aca2f91..4613605425c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,8 +71,8 @@ jobs: tox_extra_args: "-n 2" test_mypyc: true - - name: mypyc runtime tests with py38-macos - python: '3.8.17' + - name: mypyc runtime tests with py39-macos + python: '3.9.18' arch: x64 os: macos-latest toxenv: py From f7d047cd6dc008ab767510211d5c466d1c5e9215 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Oct 2023 13:45:51 +0100 Subject: [PATCH 0337/1617] [mypyc] Generate error on duplicate function definitions (#16309) Previously we produced duplicate functions in C, which caused C compiler errors. --- mypyc/irbuild/builder.py | 9 +++++++++ mypyc/irbuild/function.py | 2 +- mypyc/test-data/irbuild-statements.test | 24 ++++++++++++++++++++++++ mypyc/test-data/run-misc.test | 4 ---- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 573ca334a5d1..5ed617aa925f 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -175,6 +175,7 @@ def __init__( self.graph = graph self.ret_types: list[RType] = [] self.functions: list[FuncIR] = [] + self.function_names: set[tuple[str | None, str]] = set() self.classes: list[ClassIR] = [] self.final_names: list[tuple[str, RType]] = [] self.callable_class_names: set[str] = set() @@ -1326,6 +1327,14 @@ def error(self, msg: str, line: int) -> None: def note(self, msg: str, line: int) -> None: self.errors.note(msg, self.module_path, line) + def add_function(self, func_ir: FuncIR, line: int) -> None: + name = (func_ir.class_name, func_ir.name) + if name in self.function_names: + self.error(f'Duplicate definition of "{name[1]}" not supported by mypyc', line) + return + self.function_names.add(name) + self.functions.append(func_ir) + def gen_arg_defaults(builder: IRBuilder) -> None: """Generate blocks for arguments that have default values. diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index ebf7fa9a54de..b1785f40550e 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -103,7 +103,7 @@ def transform_func_def(builder: IRBuilder, fdef: FuncDef) -> None: if func_reg: builder.assign(get_func_target(builder, fdef), func_reg, fdef.line) maybe_insert_into_registry_dict(builder, fdef) - builder.functions.append(func_ir) + builder.add_function(func_ir, fdef.line) def transform_overloaded_func_def(builder: IRBuilder, o: OverloadedFuncDef) -> None: diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 490b41336e88..b7c67730a05f 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -1123,3 +1123,27 @@ L6: r14 = CPy_NoErrOccured() L7: return 1 + +[case testConditionalFunctionDefinition] +if int(): + def foo() -> int: + return 0 +else: + def foo() -> int: # E + return 1 + +def bar() -> int: + return 0 + +if int(): + def bar() -> int: # E + return 1 +[out] +main:5: error: Duplicate definition of "foo" not supported by mypyc +main:12: error: Duplicate definition of "bar" not supported by mypyc + +[case testRepeatedUnderscoreFunctions] +def _(arg): pass +def _(arg): pass +[out] +main:2: error: Duplicate definition of "_" not supported by mypyc diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index c40e0fc55f0e..f77ba3a1302b 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -1117,10 +1117,6 @@ for _ in range(2): except AssertionError: pass -[case testRepeatedUnderscoreFunctions] -def _(arg): pass -def _(arg): pass - [case testUnderscoreFunctionsInMethods] class A: From 5ef9c82c19941bd376128491b7959f551bd530e7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Oct 2023 16:25:52 +0100 Subject: [PATCH 0338/1617] [daemon] Fix return type change to optional in generic function (#16342) Previously changing a return type to an optional type was not propagated at least in some cases, since astdiff could simplify away the optional type. --- mypy/server/astdiff.py | 4 +++- test-data/unit/diff.test | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 93f178dca35a..5323bf2c57cb 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -74,6 +74,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' Var, ) from mypy.semanal_shared import find_dataclass_transform_spec +from mypy.state import state from mypy.types import ( AnyType, CallableType, @@ -456,7 +457,8 @@ def normalize_callable_variables(self, typ: CallableType) -> CallableType: tv = v.copy_modified(id=tid) tvs.append(tv) tvmap[v.id] = tv - return expand_type(typ, tvmap).copy_modified(variables=tvs) + with state.strict_optional_set(True): + return expand_type(typ, tvmap).copy_modified(variables=tvs) def visit_tuple_type(self, typ: TupleType) -> SnapshotItem: return ("TupleType", snapshot_types(typ.items)) diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 66adfaecd909..8fc74868123e 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1497,3 +1497,36 @@ class C: def meth(self) -> int: return 0 [out] __main__.C.meth + +[case testGenericFunctionWithOptionalReturnType] +from typing import Type, TypeVar + +T = TypeVar("T") + +class C: + @classmethod + def get_by_team_and_id( + cls: Type[T], + raw_member_id: int, + include_removed: bool = False, + ) -> T: + pass + +[file next.py] +from typing import Type, TypeVar, Optional + +T = TypeVar("T") + +class C: + @classmethod + def get_by_team_and_id( + cls: Type[T], + raw_member_id: int, + include_removed: bool = False, + ) -> Optional[T]: + pass + +[builtins fixtures/classmethod.pyi] +[out] +__main__.C.get_by_team_and_id +__main__.Optional From b41c8c1ec4337f158d70d9dfd2032c2ae03a017c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 27 Oct 2023 18:35:48 +0100 Subject: [PATCH 0339/1617] Use upper bound as inference fallback more consistently (#16344) Fixes https://github.com/python/mypy/issues/16331 Fix is straightforward: do not use the fallback, where we would not give the error in the first place. --- mypy/checkexpr.py | 4 +++- mypy/infer.py | 8 ++++++-- mypy/solve.py | 5 ++++- test-data/unit/check-inference.test | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 18c1c570ba91..ddcaa6ee30c9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1989,7 +1989,9 @@ def infer_function_type_arguments_using_context( # in this case external context is almost everything we have. if not is_generic_instance(ctx) and not is_literal_type_like(ctx): return callable.copy_modified() - args = infer_type_arguments(callable.variables, ret_type, erased_ctx) + args = infer_type_arguments( + callable.variables, ret_type, erased_ctx, skip_unsatisfied=True + ) # Only substitute non-Uninhabited and non-erased types. new_args: list[Type | None] = [] for arg in args: diff --git a/mypy/infer.py b/mypy/infer.py index ba4a1d2bc9b1..bcf0c95808ab 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -63,9 +63,13 @@ def infer_function_type_arguments( def infer_type_arguments( - type_vars: Sequence[TypeVarLikeType], template: Type, actual: Type, is_supertype: bool = False + type_vars: Sequence[TypeVarLikeType], + template: Type, + actual: Type, + is_supertype: bool = False, + skip_unsatisfied: bool = False, ) -> list[Type | None]: # Like infer_function_type_arguments, but only match a single type # against a generic type. constraints = infer_constraints(template, actual, SUPERTYPE_OF if is_supertype else SUBTYPE_OF) - return solve_constraints(type_vars, constraints)[0] + return solve_constraints(type_vars, constraints, skip_unsatisfied=skip_unsatisfied)[0] diff --git a/mypy/solve.py b/mypy/solve.py index 4d0ca6b7af24..efe8e487c506 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -43,6 +43,7 @@ def solve_constraints( constraints: list[Constraint], strict: bool = True, allow_polymorphic: bool = False, + skip_unsatisfied: bool = False, ) -> tuple[list[Type | None], list[TypeVarLikeType]]: """Solve type constraints. @@ -54,6 +55,8 @@ def solve_constraints( If allow_polymorphic=True, then use the full algorithm that can potentially return free type variables in solutions (these require special care when applying). Otherwise, use a simplified algorithm that just solves each type variable individually if possible. + + The skip_unsatisfied flag matches the same one in applytype.apply_generic_arguments(). """ vars = [tv.id for tv in original_vars] if not vars: @@ -110,7 +113,7 @@ def solve_constraints( candidate = AnyType(TypeOfAny.special_form) res.append(candidate) - if not free_vars: + if not free_vars and not skip_unsatisfied: # Most of the validation for solutions is done in applytype.py, but here we can # quickly test solutions w.r.t. to upper bounds, and use the latter (if possible), # if solutions are actually not valid (due to poor inference context). diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0a95ffdd50cf..0d162238450a 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3748,3 +3748,22 @@ empty: Dict[NoReturn, NoReturn] def bar() -> Union[Dict[str, Any], Dict[int, Any]]: return empty [builtins fixtures/dict.pyi] + +[case testUpperBoundInferenceFallbackNotOverused] +from typing import TypeVar, Protocol, List + +S = TypeVar("S", covariant=True) +class Foo(Protocol[S]): + def foo(self) -> S: ... +def foo(x: Foo[S]) -> S: ... + +T = TypeVar("T", bound="Base") +class Base: + def foo(self: T) -> T: ... +class C(Base): + pass + +def f(values: List[T]) -> T: ... +x = foo(f([C()])) +reveal_type(x) # N: Revealed type is "__main__.C" +[builtins fixtures/list.pyi] From 5d4046477eb017fcb2cdbf64403a4e67308ef2ed Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 27 Oct 2023 18:36:08 +0100 Subject: [PATCH 0340/1617] Support PEP-646 and PEP-692 in the same callable (#16294) Fixes https://github.com/python/mypy/issues/16285 I was not sure if it is important to support this, but taking into account the current behavior is a crash, and that implementation is quite simple, I think we should do this. Using this opportunity I also improve related error messages a bit. --- mypy/semanal.py | 2 +- mypy/typeanal.py | 59 ++++++++------ mypy/types.py | 7 +- test-data/unit/check-typevar-tuple.test | 104 +++++++++++++++++++++++- test-data/unit/semanal-types.test | 2 +- 5 files changed, 142 insertions(+), 32 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 179ee7c70bfb..342d48256ff5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -950,7 +950,7 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType return typ last_type = get_proper_type(last_type.type) if not isinstance(last_type, TypedDictType): - self.fail("Unpack item in ** argument must be a TypedDict", defn) + self.fail("Unpack item in ** argument must be a TypedDict", last_type) new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)] return typ.copy_modified(arg_types=new_arg_types) overlap = set(typ.arg_names) & set(last_type.items) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b16d0ac066b4..ceb276d3bdd4 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -987,33 +987,40 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: self.anal_star_arg_type(t.arg_types[-2], ARG_STAR, nested=nested), self.anal_star_arg_type(t.arg_types[-1], ARG_STAR2, nested=nested), ] + # If nested is True, it means we are analyzing a Callable[...] type, rather + # than a function definition type. We need to "unpack" ** TypedDict annotation + # here (for function definitions it is done in semanal). + if nested and isinstance(arg_types[-1], UnpackType): + # TODO: it would be better to avoid this get_proper_type() call. + unpacked = get_proper_type(arg_types[-1].type) + if isinstance(unpacked, TypedDictType): + arg_types[-1] = unpacked + unpacked_kwargs = True + arg_types = self.check_unpacks_in_list(arg_types) else: - arg_types = self.anal_array(t.arg_types, nested=nested, allow_unpack=True) star_index = None if ARG_STAR in arg_kinds: star_index = arg_kinds.index(ARG_STAR) star2_index = None if ARG_STAR2 in arg_kinds: star2_index = arg_kinds.index(ARG_STAR2) - validated_args: list[Type] = [] - for i, at in enumerate(arg_types): - if isinstance(at, UnpackType) and i not in (star_index, star2_index): - self.fail( - message_registry.INVALID_UNPACK_POSITION, at, code=codes.VALID_TYPE - ) - validated_args.append(AnyType(TypeOfAny.from_error)) - else: - if nested and isinstance(at, UnpackType) and i == star_index: - # TODO: it would be better to avoid this get_proper_type() call. - p_at = get_proper_type(at.type) - if isinstance(p_at, TypedDictType) and not at.from_star_syntax: - # Automatically detect Unpack[Foo] in Callable as backwards - # compatible syntax for **Foo, if Foo is a TypedDict. - at = p_at - arg_kinds[i] = ARG_STAR2 - unpacked_kwargs = True - validated_args.append(at) - arg_types = validated_args + arg_types = [] + for i, ut in enumerate(t.arg_types): + at = self.anal_type( + ut, nested=nested, allow_unpack=i in (star_index, star2_index) + ) + if nested and isinstance(at, UnpackType) and i == star_index: + # TODO: it would be better to avoid this get_proper_type() call. + p_at = get_proper_type(at.type) + if isinstance(p_at, TypedDictType) and not at.from_star_syntax: + # Automatically detect Unpack[Foo] in Callable as backwards + # compatible syntax for **Foo, if Foo is a TypedDict. + at = p_at + arg_kinds[i] = ARG_STAR2 + unpacked_kwargs = True + arg_types.append(at) + if nested: + arg_types = self.check_unpacks_in_list(arg_types) # If there were multiple (invalid) unpacks, the arg types list will become shorter, # we need to trim the kinds/names as well to avoid crashes. arg_kinds = t.arg_kinds[: len(arg_types)] @@ -1387,8 +1394,9 @@ def analyze_callable_args( names: list[str | None] = [] seen_unpack = False unpack_types: list[Type] = [] - invalid_unpacks = [] - for arg in arglist.items: + invalid_unpacks: list[Type] = [] + second_unpack_last = False + for i, arg in enumerate(arglist.items): if isinstance(arg, CallableArgument): args.append(arg.typ) names.append(arg.name) @@ -1415,6 +1423,11 @@ def analyze_callable_args( ): if seen_unpack: # Multiple unpacks, preserve them, so we can give an error later. + if i == len(arglist.items) - 1 and not invalid_unpacks: + # Special case: if there are just two unpacks, and the second one appears + # as last type argument, it can be still valid, if the second unpacked type + # is a TypedDict. This should be checked by the caller. + second_unpack_last = True invalid_unpacks.append(arg) continue seen_unpack = True @@ -1442,7 +1455,7 @@ def analyze_callable_args( names.append(None) for arg in invalid_unpacks: args.append(arg) - kinds.append(ARG_STAR) + kinds.append(ARG_STAR2 if second_unpack_last else ARG_STAR) names.append(None) # Note that arglist below is only used for error context. check_arg_names(names, [arglist] * len(args), self.fail, "Callable") diff --git a/mypy/types.py b/mypy/types.py index ae1a1f595fa2..43003a9a22b6 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3268,15 +3268,16 @@ def visit_callable_type(self, t: CallableType) -> str: num_skip = 0 s = "" - bare_asterisk = False + asterisk = False for i in range(len(t.arg_types) - num_skip): if s != "": s += ", " - if t.arg_kinds[i].is_named() and not bare_asterisk: + if t.arg_kinds[i].is_named() and not asterisk: s += "*, " - bare_asterisk = True + asterisk = True if t.arg_kinds[i] == ARG_STAR: s += "*" + asterisk = True if t.arg_kinds[i] == ARG_STAR2: s += "**" name = t.arg_names[i] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 4a281fbf0b49..1a2573898170 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -571,8 +571,7 @@ from typing_extensions import Unpack, TypeVarTuple Ts = TypeVarTuple("Ts") Us = TypeVarTuple("Us") -a: Callable[[Unpack[Ts], Unpack[Us]], int] # E: Var args may not appear after named or var args \ - # E: More than one Unpack in a type is not allowed +a: Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one Unpack in a type is not allowed reveal_type(a) # N: Revealed type is "def [Ts, Us] (*Unpack[Ts`-1]) -> builtins.int" b: Callable[[Unpack], int] # E: Unpack[...] requires exactly one type argument reveal_type(b) # N: Revealed type is "def (*Any) -> builtins.int" @@ -730,8 +729,7 @@ A = Tuple[Unpack[Ts], Unpack[Us]] # E: More than one Unpack in a type is not al x: A[int, str] reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str]" -B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: Var args may not appear after named or var args \ - # E: More than one Unpack in a type is not allowed +B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one Unpack in a type is not allowed y: B[int, str] reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int" @@ -1912,3 +1910,101 @@ reveal_type(y) # N: Revealed type is "__main__.C[builtins.int, Unpack[builtins. z = C[int]() # E: Bad number of arguments, expected: at least 2, given: 1 reveal_type(z) # N: Revealed type is "__main__.C[Any, Unpack[builtins.tuple[Any, ...]], Any]" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleBothUnpacksSimple] +from typing import Tuple +from typing_extensions import Unpack, TypeVarTuple, TypedDict + +class Keywords(TypedDict): + a: str + b: str + +Ints = Tuple[int, ...] + +def f(*args: Unpack[Ints], other: str = "no", **kwargs: Unpack[Keywords]) -> None: ... +reveal_type(f) # N: Revealed type is "def (*args: builtins.int, other: builtins.str =, **kwargs: Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" +f(1, 2, a="a", b="b") # OK +f(1, 2, 3) # E: Missing named argument "a" for "f" \ + # E: Missing named argument "b" for "f" + +Ts = TypeVarTuple("Ts") +def g(*args: Unpack[Ts], other: str = "no", **kwargs: Unpack[Keywords]) -> None: ... +reveal_type(g) # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1], other: builtins.str =, **kwargs: Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" +g(1, 2, a="a", b="b") # OK +g(1, 2, 3) # E: Missing named argument "a" for "g" \ + # E: Missing named argument "b" for "g" + +def bad( + *args: Unpack[Keywords], # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) + **kwargs: Unpack[Ints], # E: Unpack item in ** argument must be a TypedDict +) -> None: ... +reveal_type(bad) # N: Revealed type is "def (*args: Any, **kwargs: Any)" + +def bad2( + one: int, + *args: Unpack[Keywords], # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) + other: str = "no", + **kwargs: Unpack[Ints], # E: Unpack item in ** argument must be a TypedDict +) -> None: ... +reveal_type(bad2) # N: Revealed type is "def (one: builtins.int, *args: Any, other: builtins.str =, **kwargs: Any)" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleBothUnpacksCallable] +from typing import Callable, Tuple +from typing_extensions import Unpack, TypedDict + +class Keywords(TypedDict): + a: str + b: str +Ints = Tuple[int, ...] + +cb: Callable[[Unpack[Ints], Unpack[Keywords]], None] +reveal_type(cb) # N: Revealed type is "def (*builtins.int, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" + +cb2: Callable[[int, Unpack[Ints], int, Unpack[Keywords]], None] +reveal_type(cb2) # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]], **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" +cb2(1, 2, 3, a="a", b="b") +cb2(1, a="a", b="b") # E: Too few arguments +cb2(1, 2, 3, a="a") # E: Missing named argument "b" + +bad1: Callable[[Unpack[Ints], Unpack[Ints]], None] # E: More than one Unpack in a type is not allowed +reveal_type(bad1) # N: Revealed type is "def (*builtins.int)" +bad2: Callable[[Unpack[Keywords], Unpack[Keywords]], None] # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) +reveal_type(bad2) # N: Revealed type is "def (*Any, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" +bad3: Callable[[Unpack[Keywords], Unpack[Ints]], None] # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) \ + # E: More than one Unpack in a type is not allowed +reveal_type(bad3) # N: Revealed type is "def (*Any)" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleBothUnpacksApplication] +from typing import Callable, TypeVar, Optional +from typing_extensions import Unpack, TypeVarTuple, TypedDict + +class Keywords(TypedDict): + a: str + b: str + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +def test( + x: int, + func: Callable[[Unpack[Ts]], T], + *args: Unpack[Ts], + other: Optional[str] = None, + **kwargs: Unpack[Keywords], +) -> T: + if bool(): + func(*args, **kwargs) # E: Extra argument "a" from **args + return func(*args) +def test2( + x: int, + func: Callable[[Unpack[Ts], Unpack[Keywords]], T], + *args: Unpack[Ts], + other: Optional[str] = None, + **kwargs: Unpack[Keywords], +) -> T: + if bool(): + func(*args) # E: Missing named argument "a" \ + # E: Missing named argument "b" + return func(*args, **kwargs) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 5e05d099b958..83c44738f055 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1043,7 +1043,7 @@ MypyFile:1( default( Var(y) StrExpr())) - def (*x: builtins.int, *, y: builtins.str =) -> Any + def (*x: builtins.int, y: builtins.str =) -> Any VarArg( Var(x)) Block:1( From 4f05dd506ee4cc8a9f38210be96e974fb8f54a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lind?= Date: Fri, 27 Oct 2023 22:24:50 +0200 Subject: [PATCH 0341/1617] Write stubs with utf-8 encoding (#16329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to ensure that you don't get encoding errors if docstrings contains odd characters like emojis. --------- Co-authored-by: Jørgen Lind Co-authored-by: hauntsaninja --- mypy/stubgen.py | 2 +- test-data/unit/stubgen.test | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index a2f07a35eaa2..837cd723c410 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1578,7 +1578,7 @@ def generate_stub_for_py_module( subdir = os.path.dirname(target) if subdir and not os.path.isdir(subdir): os.makedirs(subdir) - with open(target, "w") as file: + with open(target, "w", encoding="utf-8") as file: file.write(output) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 64a1353b29b3..895500c1ba57 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -3485,7 +3485,7 @@ def f2(): ... class A: """class docstring - a multiline docstring""" + a multiline 😊 docstring""" def func(): """func docstring don't forget to indent""" @@ -3512,7 +3512,7 @@ class B: class A: """class docstring - a multiline docstring""" + a multiline 😊 docstring""" def func() -> None: """func docstring don't forget to indent""" From 5c6ca5cdee906ec7c57be478679cd689fdd15861 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 27 Oct 2023 23:58:48 +0100 Subject: [PATCH 0342/1617] Properly use proper subtyping for callables (#16343) Fixes https://github.com/python/mypy/issues/16338 This is kind of a major change, but it is technically correct: we should not treat `(*args: Any, **kwargs: Any)` special in `is_proper_subtype()` (only in `is_subtype()`). Unfortunately, this requires an additional flag for `is_callable_compatible()`, since currently we are passing the subtype kind information via a callback, which is not applicable to handling argument kinds. --- mypy/checker.py | 11 ++++++++--- mypy/constraints.py | 12 ++++++++++-- mypy/meet.py | 1 + mypy/subtypes.py | 14 +++++++++++--- test-data/unit/check-overloading.test | 22 +++++++++++++++++++++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 64bbbfa0a55b..e68dc4178962 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -800,7 +800,7 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # Is the overload alternative's arguments subtypes of the implementation's? if not is_callable_compatible( - impl, sig1, is_compat=is_subtype, ignore_return=True + impl, sig1, is_compat=is_subtype, is_proper_subtype=False, ignore_return=True ): self.msg.overloaded_signatures_arg_specific(i + 1, defn.impl) @@ -7685,6 +7685,7 @@ def is_unsafe_overlapping_overload_signatures( signature, other, is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, + is_proper_subtype=False, is_compat_return=lambda l, r: not is_subtype_no_promote(l, r), ignore_return=False, check_args_covariantly=True, @@ -7694,6 +7695,7 @@ def is_unsafe_overlapping_overload_signatures( other, signature, is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, + is_proper_subtype=False, is_compat_return=lambda l, r: not is_subtype_no_promote(r, l), ignore_return=False, check_args_covariantly=False, @@ -7744,7 +7746,7 @@ def overload_can_never_match(signature: CallableType, other: CallableType) -> bo signature, {tvar.id: erase_def_to_union_or_bound(tvar) for tvar in signature.variables} ) return is_callable_compatible( - exp_signature, other, is_compat=is_more_precise, ignore_return=True + exp_signature, other, is_compat=is_more_precise, is_proper_subtype=True, ignore_return=True ) @@ -7754,7 +7756,9 @@ def is_more_general_arg_prefix(t: FunctionLike, s: FunctionLike) -> bool: # general than one with fewer items (or just one item)? if isinstance(t, CallableType): if isinstance(s, CallableType): - return is_callable_compatible(t, s, is_compat=is_proper_subtype, ignore_return=True) + return is_callable_compatible( + t, s, is_compat=is_proper_subtype, is_proper_subtype=True, ignore_return=True + ) elif isinstance(t, FunctionLike): if isinstance(s, FunctionLike): if len(t.items) == len(s.items): @@ -7769,6 +7773,7 @@ def is_same_arg_prefix(t: CallableType, s: CallableType) -> bool: t, s, is_compat=is_same_type, + is_proper_subtype=True, ignore_return=True, check_args_covariantly=True, ignore_pos_arg_names=True, diff --git a/mypy/constraints.py b/mypy/constraints.py index 7d782551b261..6f611736a72a 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1352,7 +1352,11 @@ def find_matching_overload_item(overloaded: Overloaded, template: CallableType) # Return type may be indeterminate in the template, so ignore it when performing a # subtype check. if mypy.subtypes.is_callable_compatible( - item, template, is_compat=mypy.subtypes.is_subtype, ignore_return=True + item, + template, + is_compat=mypy.subtypes.is_subtype, + is_proper_subtype=False, + ignore_return=True, ): return item # Fall back to the first item if we can't find a match. This is totally arbitrary -- @@ -1370,7 +1374,11 @@ def find_matching_overload_items( # Return type may be indeterminate in the template, so ignore it when performing a # subtype check. if mypy.subtypes.is_callable_compatible( - item, template, is_compat=mypy.subtypes.is_subtype, ignore_return=True + item, + template, + is_compat=mypy.subtypes.is_subtype, + is_proper_subtype=False, + ignore_return=True, ): res.append(item) if not res: diff --git a/mypy/meet.py b/mypy/meet.py index 1a566aed17de..fa9bd6a83743 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -462,6 +462,7 @@ def _type_object_overlap(left: Type, right: Type) -> bool: left, right, is_compat=_is_overlapping_types, + is_proper_subtype=False, ignore_pos_arg_names=True, allow_partial_overlap=True, ) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 2ca3357dd722..383e6eddd317 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -658,6 +658,8 @@ def visit_parameters(self, left: Parameters) -> bool: left, self.right, is_compat=self._is_subtype, + # TODO: this should pass the current value, but then couple tests fail. + is_proper_subtype=False, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, ) else: @@ -677,6 +679,7 @@ def visit_callable_type(self, left: CallableType) -> bool: left, right, is_compat=self._is_subtype, + is_proper_subtype=self.proper_subtype, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, strict_concatenate=(self.options.extra_checks or self.options.strict_concatenate) if self.options @@ -932,6 +935,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: left_item, right_item, is_compat=self._is_subtype, + is_proper_subtype=self.proper_subtype, ignore_return=True, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, strict_concatenate=strict_concat, @@ -940,6 +944,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: right_item, left_item, is_compat=self._is_subtype, + is_proper_subtype=self.proper_subtype, ignore_return=True, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, strict_concatenate=strict_concat, @@ -1358,6 +1363,7 @@ def is_callable_compatible( right: CallableType, *, is_compat: Callable[[Type, Type], bool], + is_proper_subtype: bool, is_compat_return: Callable[[Type, Type], bool] | None = None, ignore_return: bool = False, ignore_pos_arg_names: bool = False, @@ -1517,6 +1523,7 @@ def g(x: int) -> int: ... left, right, is_compat=is_compat, + is_proper_subtype=is_proper_subtype, ignore_pos_arg_names=ignore_pos_arg_names, allow_partial_overlap=allow_partial_overlap, strict_concatenate_check=strict_concatenate_check, @@ -1552,12 +1559,13 @@ def are_parameters_compatible( right: Parameters | NormalizedCallableType, *, is_compat: Callable[[Type, Type], bool], + is_proper_subtype: bool, ignore_pos_arg_names: bool = False, allow_partial_overlap: bool = False, strict_concatenate_check: bool = False, ) -> bool: """Helper function for is_callable_compatible, used for Parameter compatibility""" - if right.is_ellipsis_args: + if right.is_ellipsis_args and not is_proper_subtype: return True left_star = left.var_arg() @@ -1566,9 +1574,9 @@ def are_parameters_compatible( right_star2 = right.kw_arg() # Treat "def _(*a: Any, **kw: Any) -> X" similarly to "Callable[..., X]" - if are_trivial_parameters(right): + if are_trivial_parameters(right) and not is_proper_subtype: return True - trivial_suffix = is_trivial_suffix(right) + trivial_suffix = is_trivial_suffix(right) and not is_proper_subtype # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index b97eeb48115c..7bca5cc7b508 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6501,7 +6501,7 @@ eggs = lambda: 'eggs' reveal_type(func(eggs)) # N: Revealed type is "def (builtins.str) -> builtins.str" spam: Callable[..., str] = lambda x, y: 'baz' -reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> builtins.str" +reveal_type(func(spam)) # N: Revealed type is "def (*Any, **Any) -> Any" [builtins fixtures/paramspec.pyi] [case testGenericOverloadOverlapWithType] @@ -6673,3 +6673,23 @@ c2 = MyCallable("test") reveal_type(c2) # N: Revealed type is "__main__.MyCallable[builtins.str]" reveal_type(c2()) # should be int # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testOverloadWithStarAnyFallback] +from typing import overload, Any + +class A: + @overload + def f(self, e: str) -> str: ... + @overload + def f(self, *args: Any, **kwargs: Any) -> Any: ... + def f(self, *args, **kwargs): + pass + +class B: + @overload + def f(self, e: str, **kwargs: Any) -> str: ... + @overload + def f(self, *args: Any, **kwargs: Any) -> Any: ... + def f(self, *args, **kwargs): + pass +[builtins fixtures/tuple.pyi] From 42f7cf1a7228844f82f4de22ac94f0e1b5e3ed9b Mon Sep 17 00:00:00 2001 From: Cibin Mathew <10793628+cibinmathew@users.noreply.github.com> Date: Sat, 28 Oct 2023 01:03:55 +0200 Subject: [PATCH 0343/1617] Update starred expr error message to match Python's (#16304) Fixes https://github.com/python/mypy/issues/16287 Update mypy's error on starred expression to match that of Python 3.11 --- mypy/semanal.py | 2 +- test-data/unit/check-statements.test | 2 +- test-data/unit/check-tuples.test | 2 +- test-data/unit/semanal-errors.test | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 342d48256ff5..a114a5a1dcd4 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4997,7 +4997,7 @@ def visit_dict_expr(self, expr: DictExpr) -> None: def visit_star_expr(self, expr: StarExpr) -> None: if not expr.valid: - self.fail("Can use starred expression only as assignment target", expr, blocker=True) + self.fail("can't use starred expression here", expr, blocker=True) else: expr.expr.accept(self) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 023e2935a158..f5b47e7ab97f 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2232,7 +2232,7 @@ def foo(x: int) -> Union[Generator[A, None, None], Generator[B, None, None]]: yield x # E: Incompatible types in "yield" (actual type "int", expected type "Union[A, B]") [case testNoCrashOnStarRightHandSide] -x = *(1, 2, 3) # E: Can use starred expression only as assignment target +x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 76225360a7c1..7070ead43746 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1118,7 +1118,7 @@ a = (0, *b, '') [builtins fixtures/tuple.pyi] [case testUnpackSyntaxError] -*foo # E: Can use starred expression only as assignment target +*foo # E: can't use starred expression here [builtins fixtures/tuple.pyi] [case testUnpackBases] diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index f21ba5253437..82307f30877e 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -480,13 +480,13 @@ c = 1 d = 1 a = *b [out] -main:4: error: Can use starred expression only as assignment target +main:4: error: can't use starred expression here [case testStarExpressionInExp] a = 1 *a + 1 [out] -main:2: error: Can use starred expression only as assignment target +main:2: error: can't use starred expression here [case testInvalidDel1] x = 1 From 9011ca8b4dedc0e7177737b5265f69694afa91b5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 28 Oct 2023 00:04:58 +0100 Subject: [PATCH 0344/1617] Delete recursive aliases flags (#16346) FWIW I decided to keep the old tests (where possible), just to be sure we will not re-introduce various crashes at function scope, where recursive aliases are not allowed. --- mypy/main.py | 14 --- mypy/options.py | 4 - mypy/semanal.py | 4 +- mypy/semanal_namedtuple.py | 6 +- mypy/semanal_newtype.py | 3 +- mypy/semanal_typeddict.py | 11 +- mypy/typeanal.py | 2 +- test-data/unit/check-classes.test | 31 ++--- test-data/unit/check-incremental.test | 15 +-- test-data/unit/check-namedtuple.test | 168 +++++++++++++------------ test-data/unit/check-newsemanal.test | 96 +++++++------- test-data/unit/check-type-aliases.test | 44 ++++--- test-data/unit/check-typeddict.test | 34 ++--- test-data/unit/check-unions.test | 6 +- test-data/unit/cmdline.test | 8 -- 15 files changed, 216 insertions(+), 230 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index dff1a0362ba2..718eb5a7c0c1 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -999,15 +999,6 @@ def add_invertible_flag( action="store_true", help="Enable new experimental type inference algorithm", ) - internals_group.add_argument( - "--disable-recursive-aliases", - action="store_true", - help="Disable experimental support for recursive type aliases", - ) - # Deprecated reverse variant of the above. - internals_group.add_argument( - "--enable-recursive-aliases", action="store_true", help=argparse.SUPPRESS - ) parser.add_argument( "--enable-incomplete-feature", action="append", @@ -1392,11 +1383,6 @@ def set_strict_flags() -> None: if options.logical_deps: options.cache_fine_grained = True - if options.enable_recursive_aliases: - print( - "Warning: --enable-recursive-aliases is deprecated;" - " recursive types are enabled by default" - ) if options.strict_concatenate and not strict_option_set: print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") diff --git a/mypy/options.py b/mypy/options.py index cb0464d4dc06..3447b5dfb1f6 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -362,10 +362,6 @@ def __init__(self) -> None: self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD # Enable new experimental type inference algorithm. self.new_type_inference = False - # Disable recursive type aliases (currently experimental) - self.disable_recursive_aliases = False - # Deprecated reverse version of the above, do not use. - self.enable_recursive_aliases = False # Export line-level, limited, fine-grained dependency information in cache data # (undocumented feature). self.export_ref_info = False diff --git a/mypy/semanal.py b/mypy/semanal.py index a114a5a1dcd4..27491ac695ae 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3608,7 +3608,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: ) if not res: return False - if not self.options.disable_recursive_aliases and not self.is_func_scope(): + if not self.is_func_scope(): # Only marking incomplete for top-level placeholders makes recursive aliases like # `A = Sequence[str | A]` valid here, similar to how we treat base classes in class # definitions, allowing `class str(Sequence[str]): ...` @@ -6296,7 +6296,7 @@ def process_placeholder( def cannot_resolve_name(self, name: str | None, kind: str, ctx: Context) -> None: name_format = f' "{name}"' if name else "" self.fail(f"Cannot resolve {kind}{name_format} (possible cyclic definition)", ctx) - if not self.options.disable_recursive_aliases and self.is_func_scope(): + if self.is_func_scope(): self.note("Recursive types are not allowed at function scope", ctx) def qualified_name(self, name: str) -> str: diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 51ea90e07f3d..80cf1c4e184a 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -182,8 +182,7 @@ def check_namedtuple_classdef( # it would be inconsistent with type aliases. analyzed = self.api.anal_type( stmt.type, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", ) if analyzed is None: @@ -450,8 +449,7 @@ def parse_namedtuple_fields_with_types( # We never allow recursive types at function scope. analyzed = self.api.anal_type( type, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", ) # Workaround #4987 and avoid introducing a bogus UnboundType diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index 16c6c024800d..c9c0c46f7aee 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -207,8 +207,7 @@ def check_newtype_args( self.api.anal_type( unanalyzed_type, report_invalid_types=False, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + allow_placeholder=not self.api.is_func_scope(), ) ) should_defer = False diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index a9a4cd868f27..51424d8800d2 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -228,10 +228,7 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: self.fail("Invalid TypedDict type argument", ctx) return None analyzed = self.api.anal_type( - type, - allow_required=True, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + type, allow_required=True, allow_placeholder=not self.api.is_func_scope() ) if analyzed is None: return None @@ -307,8 +304,7 @@ def analyze_typeddict_classdef_fields( analyzed = self.api.anal_type( stmt.type, allow_required=True, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) if analyzed is None: @@ -504,8 +500,7 @@ def parse_typeddict_fields_with_types( analyzed = self.api.anal_type( type, allow_required=True, - allow_placeholder=not self.options.disable_recursive_aliases - and not self.api.is_func_scope(), + allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) if analyzed is None: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index ceb276d3bdd4..03579404aac9 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -490,7 +490,7 @@ def cannot_resolve_type(self, t: UnboundType) -> None: # need access to MessageBuilder here. Also move the similar # message generation logic in semanal.py. self.api.fail(f'Cannot resolve name "{t.name}" (possible cyclic definition)', t) - if not self.options.disable_recursive_aliases and self.api.is_func_scope(): + if self.api.is_func_scope(): self.note("Recursive types are not allowed at function scope", t) def apply_concatenate_operator(self, t: UnboundType) -> Type: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index cd60ec7c9a9c..983cb8454a05 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -5002,12 +5002,13 @@ class A(Tuple[int, str]): pass -- ----------------------- [case testCrashOnSelfRecursiveNamedTupleVar] -# flags: --disable-recursive-aliases from typing import NamedTuple -N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition) -n: N -reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N]" +def test() -> None: + N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + n: N + reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N@4]" [builtins fixtures/tuple.pyi] [case testCrashOnSelfRecursiveTypedDictVar] @@ -5032,18 +5033,20 @@ lst = [n, m] [builtins fixtures/isinstancelist.pyi] [case testCorrectJoinOfSelfRecursiveTypedDicts] -# flags: --disable-recursive-aliases from mypy_extensions import TypedDict -class N(TypedDict): - x: N # E: Cannot resolve name "N" (possible cyclic definition) -class M(TypedDict): - x: M # E: Cannot resolve name "M" (possible cyclic definition) - -n: N -m: M -lst = [n, m] -reveal_type(lst[0]['x']) # N: Revealed type is "Any" +def test() -> None: + class N(TypedDict): + x: N # E: Cannot resolve name "N" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + class M(TypedDict): + x: M # E: Cannot resolve name "M" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + + n: N + m: M + lst = [n, m] + reveal_type(lst[0]['x']) # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] [case testCrashInForwardRefToNamedTupleWithIsinstance] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 801bbd4e77b4..f2625b869c19 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -4594,7 +4594,6 @@ def outer() -> None: [out2] [case testRecursiveAliasImported] -# flags: --disable-recursive-aliases import a [file a.py] @@ -4620,16 +4619,10 @@ B = List[A] [builtins fixtures/list.pyi] [out] -tmp/lib.pyi:4: error: Module "other" has no attribute "B" -tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition) [out2] -tmp/lib.pyi:4: error: Module "other" has no attribute "B" -tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition) -tmp/a.py:3: note: Revealed type is "builtins.list[Any]" - -[case testRecursiveNamedTupleTypedDict-skip] -# https://github.com/python/mypy/issues/7125 +tmp/a.py:3: note: Revealed type is "builtins.list[builtins.list[...]]" +[case testRecursiveNamedTupleTypedDict] import a [file a.py] import lib @@ -4641,7 +4634,7 @@ reveal_type(x.x['x']) [file lib.pyi] from typing import NamedTuple from other import B -A = NamedTuple('A', [('x', B)]) # type: ignore +A = NamedTuple('A', [('x', B)]) [file other.pyi] from mypy_extensions import TypedDict from lib import A @@ -4649,7 +4642,7 @@ B = TypedDict('B', {'x': A}) [builtins fixtures/dict.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Any}), fallback=lib.A]" +tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]" [case testFollowImportSkipNotInvalidatedOnPresent] # flags: --follow-imports=skip diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 9fa098b28dee..14e075339572 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -607,16 +607,18 @@ tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.N]" tmp/b.py:7: note: Revealed type is "Tuple[Any, fallback=a.N]" [case testSimpleSelfReferentialNamedTuple] -# flags: --disable-recursive-aliases from typing import NamedTuple -class MyNamedTuple(NamedTuple): - parent: 'MyNamedTuple' # E: Cannot resolve name "MyNamedTuple" (possible cyclic definition) -def bar(nt: MyNamedTuple) -> MyNamedTuple: - return nt +def test() -> None: + class MyNamedTuple(NamedTuple): + parent: 'MyNamedTuple' # E: Cannot resolve name "MyNamedTuple" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope -x: MyNamedTuple -reveal_type(x.parent) # N: Revealed type is "Any" + def bar(nt: MyNamedTuple) -> MyNamedTuple: + return nt + + x: MyNamedTuple + reveal_type(x.parent) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] -- Some crazy self-referential named tuples and types dicts @@ -645,106 +647,111 @@ class B: [out] [case testSelfRefNT1] -# flags: --disable-recursive-aliases from typing import Tuple, NamedTuple -Node = NamedTuple('Node', [ - ('name', str), - ('children', Tuple['Node', ...]), # E: Cannot resolve name "Node" (possible cyclic definition) - ]) -n: Node -reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node]" +def test() -> None: + Node = NamedTuple('Node', [ + ('name', str), + ('children', Tuple['Node', ...]), # E: Cannot resolve name "Node" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + ]) + n: Node + reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node@4]" [builtins fixtures/tuple.pyi] [case testSelfRefNT2] -# flags: --disable-recursive-aliases from typing import Tuple, NamedTuple -A = NamedTuple('A', [ - ('x', str), - ('y', Tuple['B', ...]), # E: Cannot resolve name "B" (possible cyclic definition) - ]) -class B(NamedTuple): - x: A - y: int +def test() -> None: + A = NamedTuple('A', [ + ('x', str), + ('y', Tuple['B', ...]), # E: Cannot resolve name "B" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + ]) + class B(NamedTuple): + x: A + y: int -n: A -reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A]" + n: A + reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A@4]" [builtins fixtures/tuple.pyi] [case testSelfRefNT3] -# flags: --disable-recursive-aliases from typing import NamedTuple, Tuple -class B(NamedTuple): - x: Tuple[A, int] # E: Cannot resolve name "A" (possible cyclic definition) - y: int +def test() -> None: + class B(NamedTuple): + x: Tuple[A, int] # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + y: int -A = NamedTuple('A', [ - ('x', str), - ('y', 'B'), - ]) -n: B -m: A -reveal_type(n.x) # N: Revealed type is "Tuple[Any, builtins.int]" -reveal_type(m[0]) # N: Revealed type is "builtins.str" -lst = [m, n] -reveal_type(lst[0]) # N: Revealed type is "Tuple[builtins.object, builtins.object]" + A = NamedTuple('A', [ + ('x', str), + ('y', 'B'), + ]) + n: B + m: A + reveal_type(n.x) # N: Revealed type is "Tuple[Any, builtins.int]" + reveal_type(m[0]) # N: Revealed type is "builtins.str" + lst = [m, n] + reveal_type(lst[0]) # N: Revealed type is "Tuple[builtins.object, builtins.object]" [builtins fixtures/tuple.pyi] [case testSelfRefNT4] -# flags: --disable-recursive-aliases from typing import NamedTuple -class B(NamedTuple): - x: A # E: Cannot resolve name "A" (possible cyclic definition) - y: int +def test() -> None: + class B(NamedTuple): + x: A # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + y: int -class A(NamedTuple): - x: str - y: B + class A(NamedTuple): + x: str + y: B -n: A -reveal_type(n.y[0]) # N: Revealed type is "Any" + n: A + reveal_type(n.y[0]) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] [case testSelfRefNT5] -# flags: --disable-recursive-aliases from typing import NamedTuple -B = NamedTuple('B', [ - ('x', A), # E: Cannot resolve name "A" (possible cyclic definition) # E: Name "A" is used before definition - ('y', int), - ]) -A = NamedTuple('A', [ - ('x', str), - ('y', 'B'), - ]) -n: A -def f(m: B) -> None: pass -reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[Any, builtins.int, fallback=__main__.B], fallback=__main__.A]" -reveal_type(f) # N: Revealed type is "def (m: Tuple[Any, builtins.int, fallback=__main__.B])" +def test() -> None: + B = NamedTuple('B', [ + ('x', A), # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope \ + # E: Name "A" is used before definition + ('y', int), + ]) + A = NamedTuple('A', [ + ('x', str), + ('y', 'B'), + ]) + n: A + def f(m: B) -> None: pass + reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[Any, builtins.int, fallback=__main__.B@4], fallback=__main__.A@8]" + reveal_type(f) # N: Revealed type is "def (m: Tuple[Any, builtins.int, fallback=__main__.B@4])" [builtins fixtures/tuple.pyi] [case testRecursiveNamedTupleInBases] -# flags: --disable-recursive-aliases from typing import List, NamedTuple, Union -Exp = Union['A', 'B'] # E: Cannot resolve name "Exp" (possible cyclic definition) \ - # E: Cannot resolve name "A" (possible cyclic definition) -class A(NamedTuple('A', [('attr', List[Exp])])): pass -class B(NamedTuple('B', [('val', object)])): pass +def test() -> None: + Exp = Union['A', 'B'] # E: Cannot resolve name "Exp" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope \ + # E: Cannot resolve name "A" (possible cyclic definition) + class A(NamedTuple('A', [('attr', List[Exp])])): pass + class B(NamedTuple('B', [('val', object)])): pass -def my_eval(exp: Exp) -> int: - reveal_type(exp) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B]]" + exp: Exp + reveal_type(exp) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" if isinstance(exp, A): - my_eval(exp[0][0]) - return my_eval(exp.attr[0]) + reveal_type(exp[0][0]) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" + reveal_type(exp.attr[0]) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" if isinstance(exp, B): - return exp.val # E: Incompatible return value type (got "object", expected "int") - return 0 - -my_eval(A([B(1), B(2)])) # OK + reveal_type(exp.val) # N: Revealed type is "builtins.object" + reveal_type(A([B(1), B(2)])) # N: Revealed type is "Tuple[builtins.list[Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]], fallback=__main__.A@5]" [builtins fixtures/isinstancelist.pyi] [out] @@ -771,17 +778,18 @@ tp = NamedTuple('tp', [('x', int)]) [out] [case testSubclassOfRecursiveNamedTuple] -# flags: --disable-recursive-aliases from typing import List, NamedTuple -class Command(NamedTuple): - subcommands: List['Command'] # E: Cannot resolve name "Command" (possible cyclic definition) +def test() -> None: + class Command(NamedTuple): + subcommands: List['Command'] # E: Cannot resolve name "Command" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope -class HelpCommand(Command): - pass + class HelpCommand(Command): + pass -hc = HelpCommand(subcommands=[]) -reveal_type(hc) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.HelpCommand]" + hc = HelpCommand(subcommands=[]) + reveal_type(hc) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.HelpCommand@7]" [builtins fixtures/list.pyi] [out] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index ff8d346e74a1..f4d3b9df760e 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -434,13 +434,14 @@ def main() -> None: x # E: Name "x" is not defined [case testNewAnalyzerCyclicDefinitions] -# flags: --disable-recursive-aliases --disable-error-code used-before-def +# flags: --disable-error-code used-before-def gx = gy # E: Cannot resolve name "gy" (possible cyclic definition) gy = gx def main() -> None: class C: def meth(self) -> None: - lx = ly # E: Cannot resolve name "ly" (possible cyclic definition) + lx = ly # E: Cannot resolve name "ly" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope ly = lx [case testNewAnalyzerCyclicDefinitionCrossModule] @@ -1495,22 +1496,25 @@ reveal_type(x[0][0]) # N: Revealed type is "__main__.C" [builtins fixtures/list.pyi] [case testNewAnalyzerAliasToNotReadyDirectBase] -# flags: --disable-recursive-aliases --disable-error-code used-before-def +# flags: --disable-error-code used-before-def from typing import List -x: B -B = List[C] -class C(B): pass +def test() -> None: + x: B + B = List[C] + class C(B): pass -reveal_type(x) -reveal_type(x[0][0]) + reveal_type(x) + reveal_type(x[0][0]) [builtins fixtures/list.pyi] [out] -main:4: error: Cannot resolve name "B" (possible cyclic definition) main:5: error: Cannot resolve name "B" (possible cyclic definition) -main:5: error: Cannot resolve name "C" (possible cyclic definition) -main:8: note: Revealed type is "Any" +main:5: note: Recursive types are not allowed at function scope +main:6: error: Cannot resolve name "B" (possible cyclic definition) +main:6: note: Recursive types are not allowed at function scope +main:6: error: Cannot resolve name "C" (possible cyclic definition) main:9: note: Revealed type is "Any" +main:10: note: Revealed type is "Any" [case testNewAnalyzerAliasToNotReadyTwoDeferralsFunction] # flags: --disable-error-code used-before-def @@ -1530,25 +1534,21 @@ reveal_type(f) # N: Revealed type is "def (x: builtins.list[a.C]) -> builtins.l [builtins fixtures/list.pyi] [case testNewAnalyzerAliasToNotReadyDirectBaseFunction] -# flags: --disable-recursive-aliases --disable-error-code used-before-def +# flags: --disable-error-code used-before-def import a [file a.py] from typing import List from b import D def f(x: B) -> List[B]: ... -B = List[C] # E +B = List[C] class C(B): pass [file b.py] from a import f class D: ... -reveal_type(f) # N +reveal_type(f) # N: Revealed type is "def (x: builtins.list[a.C]) -> builtins.list[builtins.list[a.C]]" [builtins fixtures/list.pyi] -[out] -tmp/b.py:3: note: Revealed type is "def (x: builtins.list[Any]) -> builtins.list[builtins.list[Any]]" -tmp/a.py:5: error: Cannot resolve name "B" (possible cyclic definition) -tmp/a.py:5: error: Cannot resolve name "C" (possible cyclic definition) [case testNewAnalyzerAliasToNotReadyMixed] from typing import List, Union @@ -2118,25 +2118,29 @@ class B(List[C]): [builtins fixtures/list.pyi] [case testNewAnalyzerNewTypeForwardClassAliasDirect] -# flags: --disable-recursive-aliases --disable-error-code used-before-def +# flags: --disable-error-code used-before-def from typing import NewType, List -x: D -reveal_type(x[0][0]) +def test() -> None: + x: D + reveal_type(x[0][0]) -D = List[C] -C = NewType('C', 'B') + D = List[C] + C = NewType('C', 'B') -class B(D): - pass + class B(D): + pass [builtins fixtures/list.pyi] [out] -main:4: error: Cannot resolve name "D" (possible cyclic definition) -main:5: note: Revealed type is "Any" -main:7: error: Cannot resolve name "D" (possible cyclic definition) -main:7: error: Cannot resolve name "C" (possible cyclic definition) -main:8: error: Argument 2 to NewType(...) must be a valid type -main:8: error: Cannot resolve name "B" (possible cyclic definition) +main:5: error: Cannot resolve name "D" (possible cyclic definition) +main:5: note: Recursive types are not allowed at function scope +main:6: note: Revealed type is "Any" +main:8: error: Cannot resolve name "D" (possible cyclic definition) +main:8: note: Recursive types are not allowed at function scope +main:8: error: Cannot resolve name "C" (possible cyclic definition) +main:9: error: Argument 2 to NewType(...) must be a valid type +main:9: error: Cannot resolve name "B" (possible cyclic definition) +main:9: note: Recursive types are not allowed at function scope -- Copied from check-classes.test (tricky corner cases). [case testNewAnalyzerNoCrashForwardRefToBrokenDoubleNewTypeClass] @@ -2154,22 +2158,24 @@ class C: [builtins fixtures/dict.pyi] [case testNewAnalyzerForwardTypeAliasInBase] -# flags: --disable-recursive-aliases from typing import List, Generic, TypeVar, NamedTuple T = TypeVar('T') -class C(A, B): # E: Cannot resolve name "A" (possible cyclic definition) - pass -class G(Generic[T]): pass -A = G[C] # E: Cannot resolve name "A" (possible cyclic definition) -class B(NamedTuple): - x: int +def test() -> None: + class C(A, B): # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + pass + class G(Generic[T]): pass + A = G[C] # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + class B(NamedTuple): + x: int -y: C -reveal_type(y.x) # N: Revealed type is "builtins.int" -reveal_type(y[0]) # N: Revealed type is "builtins.int" -x: A -reveal_type(x) # N: Revealed type is "__main__.G[Tuple[builtins.int, fallback=__main__.C]]" + y: C + reveal_type(y.x) # N: Revealed type is "builtins.int" + reveal_type(y[0]) # N: Revealed type is "builtins.int" + x: A + reveal_type(x) # N: Revealed type is "__main__.G@7[Tuple[builtins.int, fallback=__main__.C@5]]" [builtins fixtures/list.pyi] [case testNewAnalyzerDuplicateTypeVar] @@ -2584,9 +2590,9 @@ import n def __getattr__(x): pass [case testNewAnalyzerReportLoopInMRO2] -# flags: --disable-recursive-aliases def f() -> None: - class A(A): ... # E: Cannot resolve name "A" (possible cyclic definition) + class A(A): ... # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope [case testNewAnalyzerUnsupportedBaseClassInsideFunction] class C: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 3ca0c5ef0a4b..46f5ff07f1ac 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -197,30 +197,35 @@ Alias = Tuple[int, T] [out] [case testRecursiveAliasesErrors1] -# flags: --disable-recursive-aliases -# Recursive aliases are not supported yet. from typing import Type, Callable, Union -A = Union[A, int] # E: Cannot resolve name "A" (possible cyclic definition) -B = Callable[[B], int] # E: Cannot resolve name "B" (possible cyclic definition) -C = Type[C] # E: Cannot resolve name "C" (possible cyclic definition) +def test() -> None: + A = Union[A, int] # E: Cannot resolve name "A" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + B = Callable[[B], int] # E: Cannot resolve name "B" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + C = Type[C] # E: Cannot resolve name "C" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope [case testRecursiveAliasesErrors2] -# flags: --disable-recursive-aliases --disable-error-code=used-before-def -# Recursive aliases are not supported yet. +# flags: --disable-error-code=used-before-def from typing import Type, Callable, Union -A = Union[B, int] -B = Callable[[C], int] -C = Type[A] -x: A -reveal_type(x) +def test() -> None: + A = Union[B, int] + B = Callable[[C], int] + C = Type[A] + x: A + reveal_type(x) [out] main:5: error: Cannot resolve name "A" (possible cyclic definition) +main:5: note: Recursive types are not allowed at function scope main:5: error: Cannot resolve name "B" (possible cyclic definition) main:6: error: Cannot resolve name "B" (possible cyclic definition) +main:6: note: Recursive types are not allowed at function scope main:6: error: Cannot resolve name "C" (possible cyclic definition) main:7: error: Cannot resolve name "C" (possible cyclic definition) +main:7: note: Recursive types are not allowed at function scope main:9: note: Revealed type is "Union[Any, builtins.int]" [case testDoubleForwardAlias] @@ -245,13 +250,16 @@ reveal_type(x[0].x) # N: Revealed type is "builtins.str" [out] [case testJSONAliasApproximation] -# flags: --disable-recursive-aliases from typing import List, Union, Dict -x: JSON # E: Cannot resolve name "JSON" (possible cyclic definition) -JSON = Union[int, str, List[JSON], Dict[str, JSON]] # E: Cannot resolve name "JSON" (possible cyclic definition) -reveal_type(x) # N: Revealed type is "Any" -if isinstance(x, list): - reveal_type(x) # N: Revealed type is "builtins.list[Any]" + +def test() -> None: + x: JSON # E: Cannot resolve name "JSON" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + JSON = Union[int, str, List[JSON], Dict[str, JSON]] # E: Cannot resolve name "JSON" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + reveal_type(x) # N: Revealed type is "Any" + if isinstance(x, list): + reveal_type(x) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/isinstancelist.pyi] [out] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 0e1d800e0468..088b52db0473 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1443,34 +1443,34 @@ reveal_type(x['a']['b']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] [case testSelfRecursiveTypedDictInheriting] - from mypy_extensions import TypedDict -# flags: --disable-recursive-aliases -class MovieBase(TypedDict): - name: str - year: int -class Movie(MovieBase): - director: 'Movie' # E: Cannot resolve name "Movie" (possible cyclic definition) +def test() -> None: + class MovieBase(TypedDict): + name: str + year: int -m: Movie -reveal_type(m['director']['name']) # N: Revealed type is "Any" + class Movie(MovieBase): + director: 'Movie' # E: Cannot resolve name "Movie" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope + m: Movie + reveal_type(m['director']['name']) # N: Revealed type is "Any" [builtins fixtures/dict.pyi] -[out] [case testSubclassOfRecursiveTypedDict] -# flags: --disable-recursive-aliases from typing import List from mypy_extensions import TypedDict -class Command(TypedDict): - subcommands: List['Command'] # E: Cannot resolve name "Command" (possible cyclic definition) +def test() -> None: + class Command(TypedDict): + subcommands: List['Command'] # E: Cannot resolve name "Command" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope -class HelpCommand(Command): - pass + class HelpCommand(Command): + pass -hc = HelpCommand(subcommands=[]) -reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand', {'subcommands': builtins.list[Any]})" + hc = HelpCommand(subcommands=[]) + reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand@8', {'subcommands': builtins.list[Any]})" [builtins fixtures/list.pyi] [out] diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index f6fd27e59e4d..d79ab14184c6 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1003,9 +1003,11 @@ def takes_int(arg: int) -> None: pass takes_int(x) # E: Argument 1 to "takes_int" has incompatible type "Union[ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[int], ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[object], ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[float], ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[str], ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[Any], ExtremelyLongTypeNameWhichIsGenericSoWeCanUseItMultipleTimes[bytes]]"; expected "int" [case testRecursiveForwardReferenceInUnion] -# flags: --disable-recursive-aliases from typing import List, Union -MYTYPE = List[Union[str, "MYTYPE"]] # E: Cannot resolve name "MYTYPE" (possible cyclic definition) + +def test() -> None: + MYTYPE = List[Union[str, "MYTYPE"]] # E: Cannot resolve name "MYTYPE" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope [builtins fixtures/list.pyi] [case testNonStrictOptional] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 42f0ee8a9ec6..cf5e3c438fac 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1472,14 +1472,6 @@ note: A user-defined top-level module with name "typing" is not supported Failed to find builtin module mypy_extensions, perhaps typeshed is broken? == Return code: 2 -[case testRecursiveAliasesFlagDeprecated] -# cmd: mypy --enable-recursive-aliases a.py -[file a.py] -pass -[out] -Warning: --enable-recursive-aliases is deprecated; recursive types are enabled by default -== Return code: 0 - [case testNotesOnlyResultInExitSuccess] # cmd: mypy a.py [file a.py] From 93d4cb0a2ef1723ce92f39ae61fe6a0c010eb90b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 28 Oct 2023 14:15:28 +0100 Subject: [PATCH 0345/1617] Enable new type inference by default (#16345) Fixes https://github.com/python/mypy/issues/15906 I am adding `--old-type-inference` so people can disable the flag if they have issues (for few releases). IIRC there will be some fallback in `mypy_primer`, but last time I checked it was all correct. Also I don't remember if we need to update some tests, but we will see. --- mypy/checker.py | 6 +++--- mypy/checkexpr.py | 12 ++++++++---- mypy/main.py | 14 ++++++++++++-- mypy/options.py | 6 ++++-- mypy_self_check.ini | 1 - test-data/unit/check-generics.test | 9 +++++++-- test-data/unit/check-inference-context.test | 19 +++++++++++++++++++ test-data/unit/cmdline.test | 8 ++++++++ 8 files changed, 61 insertions(+), 14 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e68dc4178962..fd633b209438 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4043,11 +4043,11 @@ def is_valid_defaultdict_partial_value_type(self, t: ProperType) -> bool: return True if len(t.args) == 1: arg = get_proper_type(t.args[0]) - if self.options.new_type_inference: - allowed = isinstance(arg, (UninhabitedType, NoneType)) - else: + if self.options.old_type_inference: # Allow leaked TypeVars for legacy inference logic. allowed = isinstance(arg, (UninhabitedType, NoneType, TypeVarType)) + else: + allowed = isinstance(arg, (UninhabitedType, NoneType)) if allowed: return True return False diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ddcaa6ee30c9..9ece4680f59e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -343,7 +343,7 @@ def __init__( # on whether current expression is a callee, to give better error messages # related to type context. self.is_callee = False - type_state.infer_polymorphic = self.chk.options.new_type_inference + type_state.infer_polymorphic = not self.chk.options.old_type_inference def reset(self) -> None: self.resolved_type = {} @@ -2082,7 +2082,7 @@ def infer_function_type_arguments( elif not first_arg or not is_subtype(self.named_type("builtins.str"), first_arg): self.chk.fail(message_registry.KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE, context) - if self.chk.options.new_type_inference and any( + if not self.chk.options.old_type_inference and any( a is None or isinstance(get_proper_type(a), UninhabitedType) or set(get_type_vars(a)) & set(callee_type.variables) @@ -2181,7 +2181,11 @@ def infer_function_type_arguments_pass2( lambda a: self.accept(args[a]), ) - arg_types = self.infer_arg_types_in_context(callee_type, args, arg_kinds, formal_to_actual) + # Same as during first pass, disable type errors (we still have partial context). + with self.msg.filter_errors(): + arg_types = self.infer_arg_types_in_context( + callee_type, args, arg_kinds, formal_to_actual + ) inferred_args, _ = infer_function_type_arguments( callee_type, @@ -5230,7 +5234,7 @@ def infer_lambda_type_using_context( # they must be considered as indeterminate. We use ErasedType since it # does not affect type inference results (it is for purposes like this # only). - if self.chk.options.new_type_inference: + if not self.chk.options.old_type_inference: # With new type inference we can preserve argument types even if they # are generic, since new inference algorithm can handle constraints # like S <: T (we still erase return type since it's ultimately unknown). diff --git a/mypy/main.py b/mypy/main.py index 718eb5a7c0c1..43ab761072ca 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -995,9 +995,13 @@ def add_invertible_flag( help="Use a custom typing module", ) internals_group.add_argument( - "--new-type-inference", + "--old-type-inference", action="store_true", - help="Enable new experimental type inference algorithm", + help="Disable new experimental type inference algorithm", + ) + # Deprecated reverse variant of the above. + internals_group.add_argument( + "--new-type-inference", action="store_true", help=argparse.SUPPRESS ) parser.add_argument( "--enable-incomplete-feature", @@ -1383,6 +1387,12 @@ def set_strict_flags() -> None: if options.logical_deps: options.cache_fine_grained = True + if options.new_type_inference: + print( + "Warning: --new-type-inference flag is deprecated;" + " new type inference algorithm is already enabled by default" + ) + if options.strict_concatenate and not strict_option_set: print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") diff --git a/mypy/options.py b/mypy/options.py index 3447b5dfb1f6..31d5d584f897 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -62,7 +62,7 @@ class BuildType: | { "platform", "bazel", - "new_type_inference", + "old_type_inference", "plugins", "disable_bytearray_promotion", "disable_memoryview_promotion", @@ -360,7 +360,9 @@ def __init__(self) -> None: # skip most errors after this many messages have been reported. # -1 means unlimited. self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD - # Enable new experimental type inference algorithm. + # Disable new experimental type inference algorithm. + self.old_type_inference = False + # Deprecated reverse version of the above, do not use. self.new_type_inference = False # Export line-level, limited, fine-grained dependency information in cache data # (undocumented feature). diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 093926d4c415..7f1f9689a757 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -8,7 +8,6 @@ always_false = MYPYC plugins = mypy.plugins.proper_plugin python_version = 3.8 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ -new_type_inference = True enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 0781451e07ce..ef3f359e4989 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2724,7 +2724,7 @@ def f(x: Callable[[G[T]], int]) -> T: ... class G(Generic[T]): def g(self, x: S) -> Union[S, T]: ... -f(lambda x: x.g(0)) # E: Incompatible return value type (got "Union[int, T]", expected "int") +reveal_type(f(lambda x: x.g(0))) # N: Revealed type is "builtins.int" [case testDictStarInference] class B: ... @@ -3059,6 +3059,10 @@ def dec5(f: Callable[[int], T]) -> Callable[[int], List[T]]: return [f(x)] * x return g +I = TypeVar("I", bound=int) +def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]: + ... + reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]" reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`6) -> S`6" @@ -3066,7 +3070,8 @@ reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`9) -> S`9" reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`16) -> builtins.list[S`16]" -dec4(lambda x: x) # E: Incompatible return value type (got "S", expected "List[object]") +reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`19]) -> T`19" +dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "List[T]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecBasicInList] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 773a9ffd8274..a933acbf7f32 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -1305,6 +1305,25 @@ def g(l: List[C], x: str) -> Optional[C]: return f(l, lambda c: reveal_type(c).x) # N: Revealed type is "__main__.C" [builtins fixtures/list.pyi] +[case testPartialTypeContextWithTwoLambdas] +from typing import Any, Generic, TypeVar, Callable + +def int_to_any(x: int) -> Any: ... +def any_to_int(x: Any) -> int: ... +def any_to_str(x: Any) -> str: ... + +T = TypeVar("T") +class W(Generic[T]): + def __init__( + self, serialize: Callable[[T], Any], deserialize: Callable[[Any], T] + ) -> None: + ... +reveal_type(W(lambda x: int_to_any(x), lambda x: any_to_int(x))) # N: Revealed type is "__main__.W[builtins.int]" +W( + lambda x: int_to_any(x), # E: Argument 1 to "int_to_any" has incompatible type "str"; expected "int" + lambda x: any_to_str(x) +) + [case testWideOuterContextEmpty] from typing import List, TypeVar diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index cf5e3c438fac..91242eb62fcf 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1472,6 +1472,14 @@ note: A user-defined top-level module with name "typing" is not supported Failed to find builtin module mypy_extensions, perhaps typeshed is broken? == Return code: 2 +[case testNewTypeInferenceFlagDeprecated] +# cmd: mypy --new-type-inference a.py +[file a.py] +pass +[out] +Warning: --new-type-inference flag is deprecated; new type inference algorithm is already enabled by default +== Return code: 0 + [case testNotesOnlyResultInExitSuccess] # cmd: mypy a.py [file a.py] From 6c7faf3af1c442c0802998cbf384f73b79d67478 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 28 Oct 2023 14:19:17 +0100 Subject: [PATCH 0346/1617] Skip expensive repr() in logging call when not needed (#16350) We were spending quite a lot of time in this function when running tests, based on profiling. --- mypy/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index 1385021aac48..605368a6dc51 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -255,7 +255,8 @@ def _build( stdout=stdout, stderr=stderr, ) - manager.trace(repr(options)) + if manager.verbosity() >= 2: + manager.trace(repr(options)) reset_global_state() try: From f33c9a3b97f8226eb0156d50be7885ad96815f7c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 28 Oct 2023 14:26:05 +0100 Subject: [PATCH 0347/1617] Some final touches for variadic types support (#16334) I decided to go again over various parts of variadic types implementation to double-check nothing is missing, checked interaction with various "advanced" features (dataclasses, protocols, self-types, match statement, etc.), added some more tests (including incremental), and `grep`ed for potentially unhandled cases (and did found few crashes). This mostly touches only variadic types but one thing goes beyond, the fix for self-types upper bound, I think it is correct and should be safe. If there are no objections, next PR will flip the switch. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/applytype.py | 7 + mypy/checker.py | 39 +++--- mypy/checkexpr.py | 7 +- mypy/checkpattern.py | 111 ++++++++++++--- mypy/constraints.py | 19 ++- mypy/erasetype.py | 4 +- mypy/join.py | 43 +++++- mypy/maptype.py | 3 + mypy/meet.py | 21 ++- mypy/semanal_shared.py | 20 ++- mypy/subtypes.py | 22 ++- mypy/typeops.py | 3 +- mypy/types_utils.py | 4 +- mypy/typevars.py | 19 ++- test-data/unit/check-incremental.test | 43 ++++++ test-data/unit/check-python310.test | 117 ++++++++++++++++ test-data/unit/check-selftype.test | 20 +++ test-data/unit/check-typevar-tuple.test | 174 ++++++++++++++++++++++++ test-data/unit/fine-grained.test | 122 +++++++++++++++++ 19 files changed, 726 insertions(+), 72 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 884be287e33d..c7da67d6140b 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -3,6 +3,7 @@ from typing import Callable, Sequence import mypy.subtypes +from mypy.erasetype import erase_typevars from mypy.expandtype import expand_type from mypy.nodes import Context from mypy.types import ( @@ -62,6 +63,11 @@ def get_target_type( report_incompatible_typevar_value(callable, type, tvar.name, context) else: upper_bound = tvar.upper_bound + if tvar.name == "Self": + # Internally constructed Self-types contain class type variables in upper bound, + # so we need to erase them to avoid false positives. This is safe because we do + # not support type variables in upper bounds of user defined types. + upper_bound = erase_typevars(upper_bound) if not mypy.subtypes.is_subtype(type, upper_bound): if skip_unsatisfied: return None @@ -121,6 +127,7 @@ def apply_generic_arguments( # Apply arguments to argument types. var_arg = callable.var_arg() if var_arg is not None and isinstance(var_arg.typ, UnpackType): + # Same as for ParamSpec, callable with variadic types needs to be expanded as a whole. callable = expand_type(callable, id_to_type) assert isinstance(callable, CallableType) return callable.copy_modified(variables=[tv for tv in tvars if tv.id not in id_to_type]) diff --git a/mypy/checker.py b/mypy/checker.py index fd633b209438..62ba642256bf 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1852,7 +1852,6 @@ def expand_typevars( if defn.info: # Class type variables tvars += defn.info.defn.type_vars or [] - # TODO(PEP612): audit for paramspec for tvar in tvars: if isinstance(tvar, TypeVarType) and tvar.values: subst.append([(tvar.id, value) for value in tvar.values]) @@ -2538,6 +2537,9 @@ def check_protocol_variance(self, defn: ClassDef) -> None: object_type = Instance(info.mro[-1], []) tvars = info.defn.type_vars for i, tvar in enumerate(tvars): + if not isinstance(tvar, TypeVarType): + # Variance of TypeVarTuple and ParamSpec is underspecified by PEPs. + continue up_args: list[Type] = [ object_type if i == j else AnyType(TypeOfAny.special_form) for j, _ in enumerate(tvars) @@ -2554,7 +2556,7 @@ def check_protocol_variance(self, defn: ClassDef) -> None: expected = CONTRAVARIANT else: expected = INVARIANT - if isinstance(tvar, TypeVarType) and expected != tvar.variance: + if expected != tvar.variance: self.msg.bad_proto_variance(tvar.variance, tvar.name, expected, defn) def check_multiple_inheritance(self, typ: TypeInfo) -> None: @@ -6695,19 +6697,6 @@ def check_possible_missing_await( return self.msg.possible_missing_await(context, code) - def contains_none(self, t: Type) -> bool: - t = get_proper_type(t) - return ( - isinstance(t, NoneType) - or (isinstance(t, UnionType) and any(self.contains_none(ut) for ut in t.items)) - or (isinstance(t, TupleType) and any(self.contains_none(tt) for tt in t.items)) - or ( - isinstance(t, Instance) - and bool(t.args) - and any(self.contains_none(it) for it in t.args) - ) - ) - def named_type(self, name: str) -> Instance: """Return an instance type with given name and implicit Any type args. @@ -7471,10 +7460,22 @@ def builtin_item_type(tp: Type) -> Type | None: return None if not isinstance(get_proper_type(tp.args[0]), AnyType): return tp.args[0] - elif isinstance(tp, TupleType) and all( - not isinstance(it, AnyType) for it in get_proper_types(tp.items) - ): - return make_simplified_union(tp.items) # this type is not externally visible + elif isinstance(tp, TupleType): + normalized_items = [] + for it in tp.items: + # This use case is probably rare, but not handling unpacks here can cause crashes. + if isinstance(it, UnpackType): + unpacked = get_proper_type(it.type) + if isinstance(unpacked, TypeVarTupleType): + unpacked = get_proper_type(unpacked.upper_bound) + assert ( + isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple" + ) + normalized_items.append(unpacked.args[0]) + else: + normalized_items.append(it) + if all(not isinstance(it, AnyType) for it in get_proper_types(normalized_items)): + return make_simplified_union(normalized_items) # this type is not externally visible elif isinstance(tp, TypedDictType): # TypedDict always has non-optional string keys. Find the key type from the Mapping # base class. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9ece4680f59e..df6000050986 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -410,7 +410,7 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = self.alias_type_in_runtime_context( node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue ) - elif isinstance(node, (TypeVarExpr, ParamSpecExpr)): + elif isinstance(node, (TypeVarExpr, ParamSpecExpr, TypeVarTupleExpr)): result = self.object_type() else: if isinstance(node, PlaceholderNode): @@ -3316,6 +3316,7 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType: """Concatenate two fixed length tuples.""" + assert not (find_unpack_in_list(left.items) and find_unpack_in_list(right.items)) return TupleType( items=left.items + right.items, fallback=self.named_type("builtins.tuple") ) @@ -6507,8 +6508,8 @@ def merge_typevars_in_callables_by_name( for tv in target.variables: name = tv.fullname if name not in unique_typevars: - # TODO(PEP612): fix for ParamSpecType - if isinstance(tv, ParamSpecType): + # TODO: support ParamSpecType and TypeVarTuple. + if isinstance(tv, (ParamSpecType, TypeVarTupleType)): continue assert isinstance(tv, TypeVarType) unique_typevars[name] = tv diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 3f9a99b21530..c0061f1c3e72 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -45,9 +45,13 @@ Type, TypedDictType, TypeOfAny, + TypeVarTupleType, UninhabitedType, UnionType, + UnpackType, + find_unpack_in_list, get_proper_type, + split_with_prefix_and_suffix, ) from mypy.typevars import fill_typevars from mypy.visitor import PatternVisitor @@ -239,13 +243,29 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: # # get inner types of original type # + unpack_index = None if isinstance(current_type, TupleType): inner_types = current_type.items - size_diff = len(inner_types) - required_patterns - if size_diff < 0: - return self.early_non_match() - elif size_diff > 0 and star_position is None: - return self.early_non_match() + unpack_index = find_unpack_in_list(inner_types) + if unpack_index is None: + size_diff = len(inner_types) - required_patterns + if size_diff < 0: + return self.early_non_match() + elif size_diff > 0 and star_position is None: + return self.early_non_match() + else: + normalized_inner_types = [] + for it in inner_types: + # Unfortunately, it is not possible to "split" the TypeVarTuple + # into individual items, so we just use its upper bound for the whole + # analysis instead. + if isinstance(it, UnpackType) and isinstance(it.type, TypeVarTupleType): + it = UnpackType(it.type.upper_bound) + normalized_inner_types.append(it) + inner_types = normalized_inner_types + current_type = current_type.copy_modified(items=normalized_inner_types) + if len(inner_types) - 1 > required_patterns and star_position is None: + return self.early_non_match() else: inner_type = self.get_sequence_type(current_type, o) if inner_type is None: @@ -270,10 +290,10 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: self.update_type_map(captures, type_map) new_inner_types = self.expand_starred_pattern_types( - contracted_new_inner_types, star_position, len(inner_types) + contracted_new_inner_types, star_position, len(inner_types), unpack_index is not None ) rest_inner_types = self.expand_starred_pattern_types( - contracted_rest_inner_types, star_position, len(inner_types) + contracted_rest_inner_types, star_position, len(inner_types), unpack_index is not None ) # @@ -281,7 +301,7 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: # new_type: Type rest_type: Type = current_type - if isinstance(current_type, TupleType): + if isinstance(current_type, TupleType) and unpack_index is None: narrowed_inner_types = [] inner_rest_types = [] for inner_type, new_inner_type in zip(inner_types, new_inner_types): @@ -301,6 +321,14 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: if all(is_uninhabited(typ) for typ in inner_rest_types): # All subpatterns always match, so we can apply negative narrowing rest_type = TupleType(rest_inner_types, current_type.partial_fallback) + elif isinstance(current_type, TupleType): + # For variadic tuples it is too tricky to match individual items like for fixed + # tuples, so we instead try to narrow the entire type. + # TODO: use more precise narrowing when possible (e.g. for identical shapes). + new_tuple_type = TupleType(new_inner_types, current_type.partial_fallback) + new_type, rest_type = self.chk.conditional_types_with_intersection( + new_tuple_type, [get_type_range(current_type)], o, default=new_tuple_type + ) else: new_inner_type = UninhabitedType() for typ in new_inner_types: @@ -345,17 +373,45 @@ def contract_starred_pattern_types( If star_pos in None the types are returned unchanged. """ - if star_pos is None: - return types - new_types = types[:star_pos] - star_length = len(types) - num_patterns - new_types.append(make_simplified_union(types[star_pos : star_pos + star_length])) - new_types += types[star_pos + star_length :] - - return new_types + unpack_index = find_unpack_in_list(types) + if unpack_index is not None: + # Variadic tuples require "re-shaping" to match the requested pattern. + unpack = types[unpack_index] + assert isinstance(unpack, UnpackType) + unpacked = get_proper_type(unpack.type) + # This should be guaranteed by the normalization in the caller. + assert isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple" + if star_pos is None: + missing = num_patterns - len(types) + 1 + new_types = types[:unpack_index] + new_types += [unpacked.args[0]] * missing + new_types += types[unpack_index + 1 :] + return new_types + prefix, middle, suffix = split_with_prefix_and_suffix( + tuple([UnpackType(unpacked) if isinstance(t, UnpackType) else t for t in types]), + star_pos, + num_patterns - star_pos, + ) + new_middle = [] + for m in middle: + # The existing code expects the star item type, rather than the type of + # the whole tuple "slice". + if isinstance(m, UnpackType): + new_middle.append(unpacked.args[0]) + else: + new_middle.append(m) + return list(prefix) + [make_simplified_union(new_middle)] + list(suffix) + else: + if star_pos is None: + return types + new_types = types[:star_pos] + star_length = len(types) - num_patterns + new_types.append(make_simplified_union(types[star_pos : star_pos + star_length])) + new_types += types[star_pos + star_length :] + return new_types def expand_starred_pattern_types( - self, types: list[Type], star_pos: int | None, num_types: int + self, types: list[Type], star_pos: int | None, num_types: int, original_unpack: bool ) -> list[Type]: """Undoes the contraction done by contract_starred_pattern_types. @@ -364,6 +420,17 @@ def expand_starred_pattern_types( """ if star_pos is None: return types + if original_unpack: + # In the case where original tuple type has an unpack item, it is not practical + # to coerce pattern type back to the original shape (and may not even be possible), + # so we only restore the type of the star item. + res = [] + for i, t in enumerate(types): + if i != star_pos: + res.append(t) + else: + res.append(UnpackType(self.chk.named_generic_type("builtins.tuple", [t]))) + return res new_types = types[:star_pos] star_length = num_types - len(types) + 1 new_types += [types[star_pos]] * star_length @@ -459,7 +526,15 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: return self.early_non_match() if isinstance(type_info, TypeInfo): any_type = AnyType(TypeOfAny.implementation_artifact) - typ: Type = Instance(type_info, [any_type] * len(type_info.defn.type_vars)) + args: list[Type] = [] + for tv in type_info.defn.type_vars: + if isinstance(tv, TypeVarTupleType): + args.append( + UnpackType(self.chk.named_generic_type("builtins.tuple", [any_type])) + ) + else: + args.append(any_type) + typ: Type = Instance(type_info, args) elif isinstance(type_info, TypeAlias): typ = type_info.target elif ( diff --git a/mypy/constraints.py b/mypy/constraints.py index 6f611736a72a..49e542a49e56 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -28,6 +28,7 @@ Instance, LiteralType, NoneType, + NormalizedCallableType, Overloaded, Parameters, ParamSpecType, @@ -1388,7 +1389,7 @@ def find_matching_overload_items( return res -def get_tuple_fallback_from_unpack(unpack: UnpackType) -> TypeInfo | None: +def get_tuple_fallback_from_unpack(unpack: UnpackType) -> TypeInfo: """Get builtins.tuple type from available types to construct homogeneous tuples.""" tp = get_proper_type(unpack.type) if isinstance(tp, Instance) and tp.type.fullname == "builtins.tuple": @@ -1399,10 +1400,10 @@ def get_tuple_fallback_from_unpack(unpack: UnpackType) -> TypeInfo | None: for base in tp.partial_fallback.type.mro: if base.fullname == "builtins.tuple": return base - return None + assert False, "Invalid unpack type" -def repack_callable_args(callable: CallableType, tuple_type: TypeInfo | None) -> list[Type]: +def repack_callable_args(callable: CallableType, tuple_type: TypeInfo) -> list[Type]: """Present callable with star unpack in a normalized form. Since positional arguments cannot follow star argument, they are packed in a suffix, @@ -1417,12 +1418,8 @@ def repack_callable_args(callable: CallableType, tuple_type: TypeInfo | None) -> star_type = callable.arg_types[star_index] suffix_types = [] if not isinstance(star_type, UnpackType): - if tuple_type is not None: - # Re-normalize *args: X -> *args: *tuple[X, ...] - star_type = UnpackType(Instance(tuple_type, [star_type])) - else: - # This is unfortunate, something like tuple[Any, ...] would be better. - star_type = UnpackType(AnyType(TypeOfAny.from_error)) + # Re-normalize *args: X -> *args: *tuple[X, ...] + star_type = UnpackType(Instance(tuple_type, [star_type])) else: tp = get_proper_type(star_type.type) if isinstance(tp, TupleType): @@ -1544,7 +1541,9 @@ def infer_directed_arg_constraints(left: Type, right: Type, direction: int) -> l def infer_callable_arguments_constraints( - template: CallableType | Parameters, actual: CallableType | Parameters, direction: int + template: NormalizedCallableType | Parameters, + actual: NormalizedCallableType | Parameters, + direction: int, ) -> list[Constraint]: """Infer constraints between argument types of two callables. diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 7231ede66c65..b41eefcd4821 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -100,7 +100,9 @@ def visit_parameters(self, t: Parameters) -> ProperType: raise RuntimeError("Parameters should have been bound to a class") def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: - return AnyType(TypeOfAny.special_form) + # Likely, we can never get here because of aggressive erasure of types that + # can contain this, but better still return a valid replacement. + return t.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) def visit_unpack_type(self, t: UnpackType) -> ProperType: return AnyType(TypeOfAny.special_form) diff --git a/mypy/join.py b/mypy/join.py index 2e2939f9fbc8..d33cbd98726d 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -36,6 +36,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarLikeType, TypeVarTupleType, TypeVarType, TypeVisitor, @@ -715,11 +716,9 @@ def is_similar_callables(t: CallableType, s: CallableType) -> bool: def join_similar_callables(t: CallableType, s: CallableType) -> CallableType: - from mypy.meet import meet_types - arg_types: list[Type] = [] for i in range(len(t.arg_types)): - arg_types.append(meet_types(t.arg_types[i], s.arg_types[i])) + arg_types.append(safe_meet(t.arg_types[i], s.arg_types[i])) # TODO in combine_similar_callables also applies here (names and kinds; user metaclasses) # The fallback type can be either 'function', 'type', or some user-provided metaclass. # The result should always use 'function' as a fallback if either operands are using it. @@ -736,10 +735,42 @@ def join_similar_callables(t: CallableType, s: CallableType) -> CallableType: ) +def safe_join(t: Type, s: Type) -> Type: + # This is a temporary solution to prevent crashes in combine_similar_callables() etc., + # until relevant TODOs on handling arg_kinds will be addressed there. + if not isinstance(t, UnpackType) and not isinstance(s, UnpackType): + return join_types(t, s) + if isinstance(t, UnpackType) and isinstance(s, UnpackType): + return UnpackType(join_types(t.type, s.type)) + return object_or_any_from_type(get_proper_type(t)) + + +def safe_meet(t: Type, s: Type) -> Type: + # Similar to above but for meet_types(). + from mypy.meet import meet_types + + if not isinstance(t, UnpackType) and not isinstance(s, UnpackType): + return meet_types(t, s) + if isinstance(t, UnpackType) and isinstance(s, UnpackType): + unpacked = get_proper_type(t.type) + if isinstance(unpacked, TypeVarTupleType): + fallback_type = unpacked.tuple_fallback.type + elif isinstance(unpacked, TupleType): + fallback_type = unpacked.partial_fallback.type + else: + assert isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple" + fallback_type = unpacked.type + res = meet_types(t.type, s.type) + if isinstance(res, UninhabitedType): + res = Instance(fallback_type, [res]) + return UnpackType(res) + return UninhabitedType() + + def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: arg_types: list[Type] = [] for i in range(len(t.arg_types)): - arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) + arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) # TODO kinds and argument names # TODO what should happen if one fallback is 'type' and the other is a user-provided metaclass? # The fallback type can be either 'function', 'type', or some user-provided metaclass. @@ -806,7 +837,7 @@ def object_or_any_from_type(typ: ProperType) -> ProperType: return object_from_instance(typ.partial_fallback) elif isinstance(typ, TypeType): return object_or_any_from_type(typ.item) - elif isinstance(typ, TypeVarType) and isinstance(typ.upper_bound, ProperType): + elif isinstance(typ, TypeVarLikeType) and isinstance(typ.upper_bound, ProperType): return object_or_any_from_type(typ.upper_bound) elif isinstance(typ, UnionType): for item in typ.items: @@ -814,6 +845,8 @@ def object_or_any_from_type(typ: ProperType) -> ProperType: candidate = object_or_any_from_type(item) if isinstance(candidate, Instance): return candidate + elif isinstance(typ, UnpackType): + object_or_any_from_type(get_proper_type(typ.type)) return AnyType(TypeOfAny.implementation_artifact) diff --git a/mypy/maptype.py b/mypy/maptype.py index 0d54a83127df..59ecb2bc9993 100644 --- a/mypy/maptype.py +++ b/mypy/maptype.py @@ -31,6 +31,9 @@ def map_instance_to_supertype(instance: Instance, superclass: TypeInfo) -> Insta import mypy.typeops return mypy.typeops.tuple_fallback(tuple_type) + elif isinstance(tuple_type, Instance): + # This can happen after normalizing variadic tuples. + return tuple_type if not superclass.type_vars: # Fast path: `superclass` has no type variables to map to. diff --git a/mypy/meet.py b/mypy/meet.py index fa9bd6a83743..d2fb16808425 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -869,16 +869,17 @@ def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: return None if s_unpack_index is not None and t_unpack_index is not None: # The only simple case we can handle if both tuples are variadic - # is when they are purely variadic. Other cases are tricky because + # is when their structure fully matches. Other cases are tricky because # a variadic item is effectively a union of tuples of all length, thus # potentially causing overlap between a suffix in `s` and a prefix # in `t` (see how this is handled in is_subtype() for details). # TODO: handle more cases (like when both prefix/suffix are shorter in s or t). - if s.length() == 1 and t.length() == 1: - s_unpack = s.items[0] + if s.length() == t.length() and s_unpack_index == t_unpack_index: + unpack_index = s_unpack_index + s_unpack = s.items[unpack_index] assert isinstance(s_unpack, UnpackType) s_unpacked = get_proper_type(s_unpack.type) - t_unpack = t.items[0] + t_unpack = t.items[unpack_index] assert isinstance(t_unpack, UnpackType) t_unpacked = get_proper_type(t_unpack.type) if not (isinstance(s_unpacked, Instance) and isinstance(t_unpacked, Instance)): @@ -886,7 +887,13 @@ def meet_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: meet = self.meet(s_unpacked, t_unpacked) if not isinstance(meet, Instance): return None - return [UnpackType(meet)] + m_prefix: list[Type] = [] + for si, ti in zip(s.items[:unpack_index], t.items[:unpack_index]): + m_prefix.append(meet_types(si, ti)) + m_suffix: list[Type] = [] + for si, ti in zip(s.items[unpack_index + 1 :], t.items[unpack_index + 1 :]): + m_suffix.append(meet_types(si, ti)) + return m_prefix + [UnpackType(meet)] + m_suffix return None if s_unpack_index is not None: variadic = s @@ -1006,11 +1013,11 @@ def default(self, typ: Type) -> ProperType: def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType: - from mypy.join import join_types + from mypy.join import safe_join arg_types: list[Type] = [] for i in range(len(t.arg_types)): - arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) + arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) # TODO in combine_similar_callables also applies here (names and kinds) # The fallback type can be either 'function' or 'type'. The result should have 'function' as # fallback only if both operands have it as 'function'. diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 425e5906926a..e8edfe65c8d4 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -45,6 +45,8 @@ TypeOfAny, TypeVarId, TypeVarLikeType, + TypeVarTupleType, + UnpackType, get_proper_type, ) @@ -286,7 +288,23 @@ def calculate_tuple_fallback(typ: TupleType) -> None: """ fallback = typ.partial_fallback assert fallback.type.fullname == "builtins.tuple" - fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:] + items = [] + for item in typ.items: + # TODO: this duplicates some logic in typeops.tuple_fallback(). + if isinstance(item, UnpackType): + unpacked_type = get_proper_type(item.type) + if isinstance(unpacked_type, TypeVarTupleType): + unpacked_type = get_proper_type(unpacked_type.upper_bound) + if ( + isinstance(unpacked_type, Instance) + and unpacked_type.type.fullname == "builtins.tuple" + ): + items.append(unpacked_type.args[0]) + else: + raise NotImplementedError + else: + items.append(item) + fallback.args = (join.join_type_list(items),) class _NamedTypeCallback(Protocol): diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 383e6eddd317..6d129683c3f5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -734,9 +734,13 @@ def visit_tuple_type(self, left: TupleType) -> bool: for li in left.items: if isinstance(li, UnpackType): unpack = get_proper_type(li.type) - if isinstance(unpack, Instance): - assert unpack.type.fullname == "builtins.tuple" - li = unpack.args[0] + if isinstance(unpack, TypeVarTupleType): + unpack = get_proper_type(unpack.upper_bound) + assert ( + isinstance(unpack, Instance) + and unpack.type.fullname == "builtins.tuple" + ) + li = unpack.args[0] if not self._is_subtype(li, iter_type): return False return True @@ -1578,6 +1582,18 @@ def are_parameters_compatible( return True trivial_suffix = is_trivial_suffix(right) and not is_proper_subtype + if ( + right.arg_kinds == [ARG_STAR] + and isinstance(get_proper_type(right.arg_types[0]), AnyType) + and not is_proper_subtype + ): + # Similar to how (*Any, **Any) is considered a supertype of all callables, we consider + # (*Any) a supertype of all callables with positional arguments. This is needed in + # particular because we often refuse to try type inference if actual type is not + # a subtype of erased template type. + if all(k.is_positional() for k in left.arg_kinds) and ignore_pos_arg_names: + return True + # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must # be "more general" than argR if L is to be a subtype of R. diff --git a/mypy/typeops.py b/mypy/typeops.py index dff43775fe3d..2eb3b284e729 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -39,6 +39,7 @@ Instance, LiteralType, NoneType, + NormalizedCallableType, Overloaded, Parameters, ParamSpecType, @@ -364,7 +365,7 @@ def erase_to_bound(t: Type) -> Type: def callable_corresponding_argument( - typ: CallableType | Parameters, model: FormalArgument + typ: NormalizedCallableType | Parameters, model: FormalArgument ) -> FormalArgument | None: """Return the argument a function that corresponds to `model`""" diff --git a/mypy/types_utils.py b/mypy/types_utils.py index f289ac3e9ed1..1cd56eae5835 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -144,8 +144,7 @@ def store_argument_type( elif isinstance(arg_type, UnpackType): unpacked_type = get_proper_type(arg_type.type) if isinstance(unpacked_type, TupleType): - # Instead of using Tuple[Unpack[Tuple[...]]], just use - # Tuple[...] + # Instead of using Tuple[Unpack[Tuple[...]]], just use Tuple[...] arg_type = unpacked_type elif ( isinstance(unpacked_type, Instance) @@ -153,6 +152,7 @@ def store_argument_type( ): arg_type = unpacked_type else: + # TODO: verify that we can only have a TypeVarTuple here. arg_type = TupleType( [arg_type], fallback=named_type("builtins.tuple", [named_type("builtins.object", [])]), diff --git a/mypy/typevars.py b/mypy/typevars.py index 027a8e3f7fc5..3d74a40c303f 100644 --- a/mypy/typevars.py +++ b/mypy/typevars.py @@ -6,6 +6,7 @@ AnyType, Instance, ParamSpecType, + ProperType, TupleType, Type, TypeOfAny, @@ -55,6 +56,7 @@ def fill_typevars(typ: TypeInfo) -> Instance | TupleType: ) tvs.append(tv) inst = Instance(typ, tvs) + # TODO: do we need to also handle typeddict_type here and below? if typ.tuple_type is None: return inst return typ.tuple_type.copy_modified(fallback=inst) @@ -62,10 +64,23 @@ def fill_typevars(typ: TypeInfo) -> Instance | TupleType: def fill_typevars_with_any(typ: TypeInfo) -> Instance | TupleType: """Apply a correct number of Any's as type arguments to a type.""" - inst = Instance(typ, [AnyType(TypeOfAny.special_form)] * len(typ.defn.type_vars)) + args: list[Type] = [] + for tv in typ.defn.type_vars: + # Valid erasure for *Ts is *tuple[Any, ...], not just Any. + if isinstance(tv, TypeVarTupleType): + args.append( + UnpackType(tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)])) + ) + else: + args.append(AnyType(TypeOfAny.special_form)) + inst = Instance(typ, args) if typ.tuple_type is None: return inst - return typ.tuple_type.copy_modified(fallback=inst) + erased_tuple_type = erase_typevars(typ.tuple_type, {tv.id for tv in typ.defn.type_vars}) + assert isinstance(erased_tuple_type, ProperType) + if isinstance(erased_tuple_type, TupleType): + return typ.tuple_type.copy_modified(fallback=inst) + return inst def has_no_typevars(typ: Type) -> bool: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index f2625b869c19..eb7a795f99c0 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6454,6 +6454,49 @@ class C(Generic[P]): def __init__(self, fn: Callable[P, int]) -> None: ... [builtins fixtures/dict.pyi] +[case testVariadicClassIncrementalUpdateRegularToVariadic] +from typing import Any +from lib import C + +x: C[int, str] + +[file lib.py] +from typing import Generic, TypeVar + +T = TypeVar("T") +S = TypeVar("S") +class C(Generic[T, S]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... +[builtins fixtures/tuple.pyi] + +[case testVariadicClassIncrementalUpdateVariadicToRegular] +from typing import Any +from lib import C + +x: C[int, str, int] + +[file lib.py] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... +[file lib.py.2] +from typing import Generic, TypeVar + +T = TypeVar("T") +S = TypeVar("S") +class C(Generic[T, S]): ... +[builtins fixtures/tuple.pyi] +[out2] +main:4: error: "C" expects 2 type arguments, but 3 given + [case testVariadicTupleIncrementalUpdateNoCrash] import m [file m.py] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 640e64c78d5f..d3cdf3af849d 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -700,6 +700,21 @@ match m: reveal_type(m) # N: Revealed type is "__main__.A[Any]" reveal_type(i) # N: Revealed type is "Any" +[case testMatchClassPatternCaptureVariadicGeneric] +from typing import Generic, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple('Ts') +class A(Generic[Unpack[Ts]]): + a: Tuple[Unpack[Ts]] + +m: object +match m: + case A(a=i): + reveal_type(m) # N: Revealed type is "__main__.A[Unpack[builtins.tuple[Any, ...]]]" + reveal_type(i) # N: Revealed type is "builtins.tuple[Any, ...]" +[builtins fixtures/tuple.pyi] + [case testMatchClassPatternCaptureGenericAlreadyKnown] from typing import Generic, TypeVar @@ -2026,3 +2041,105 @@ def f4(e: int | str | bytes) -> int: return 0 [builtins fixtures/primitives.pyi] + +[case testMatchSequencePatternVariadicTupleNotTooShort] +from typing import Tuple +from typing_extensions import Unpack + +fm1: Tuple[int, int, Unpack[Tuple[str, ...]], int] +match fm1: + case [fa1, fb1, fc1]: + reveal_type(fa1) # N: Revealed type is "builtins.int" + reveal_type(fb1) # N: Revealed type is "builtins.int" + reveal_type(fc1) # N: Revealed type is "builtins.int" + +fm2: Tuple[int, int, Unpack[Tuple[str, ...]], int] +match fm2: + case [fa2, fb2]: + reveal_type(fa2) + reveal_type(fb2) + +fm3: Tuple[int, int, Unpack[Tuple[str, ...]], int] +match fm3: + case [fa3, fb3, fc3, fd3, fe3]: + reveal_type(fa3) # N: Revealed type is "builtins.int" + reveal_type(fb3) # N: Revealed type is "builtins.int" + reveal_type(fc3) # N: Revealed type is "builtins.str" + reveal_type(fd3) # N: Revealed type is "builtins.str" + reveal_type(fe3) # N: Revealed type is "builtins.int" + +m1: Tuple[int, Unpack[Tuple[str, ...]], int] +match m1: + case [a1, *b1, c1]: + reveal_type(a1) # N: Revealed type is "builtins.int" + reveal_type(b1) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(c1) # N: Revealed type is "builtins.int" + +m2: Tuple[int, Unpack[Tuple[str, ...]], int] +match m2: + case [a2, b2, *c2, d2, e2]: + reveal_type(a2) # N: Revealed type is "builtins.int" + reveal_type(b2) # N: Revealed type is "builtins.str" + reveal_type(c2) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(d2) # N: Revealed type is "builtins.str" + reveal_type(e2) # N: Revealed type is "builtins.int" + +m3: Tuple[int, int, Unpack[Tuple[str, ...]], int, int] +match m3: + case [a3, *b3, c3]: + reveal_type(a3) # N: Revealed type is "builtins.int" + reveal_type(b3) # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]" + reveal_type(c3) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternTypeVarTupleNotTooShort] +from typing import Tuple +from typing_extensions import Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +def test(xs: Tuple[Unpack[Ts]]) -> None: + fm1: Tuple[int, int, Unpack[Ts], int] + match fm1: + case [fa1, fb1, fc1]: + reveal_type(fa1) # N: Revealed type is "builtins.int" + reveal_type(fb1) # N: Revealed type is "builtins.int" + reveal_type(fc1) # N: Revealed type is "builtins.int" + + fm2: Tuple[int, int, Unpack[Ts], int] + match fm2: + case [fa2, fb2]: + reveal_type(fa2) + reveal_type(fb2) + + fm3: Tuple[int, int, Unpack[Ts], int] + match fm3: + case [fa3, fb3, fc3, fd3, fe3]: + reveal_type(fa3) # N: Revealed type is "builtins.int" + reveal_type(fb3) # N: Revealed type is "builtins.int" + reveal_type(fc3) # N: Revealed type is "builtins.object" + reveal_type(fd3) # N: Revealed type is "builtins.object" + reveal_type(fe3) # N: Revealed type is "builtins.int" + + m1: Tuple[int, Unpack[Ts], int] + match m1: + case [a1, *b1, c1]: + reveal_type(a1) # N: Revealed type is "builtins.int" + reveal_type(b1) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(c1) # N: Revealed type is "builtins.int" + + m2: Tuple[int, Unpack[Ts], int] + match m2: + case [a2, b2, *c2, d2, e2]: + reveal_type(a2) # N: Revealed type is "builtins.int" + reveal_type(b2) # N: Revealed type is "builtins.object" + reveal_type(c2) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(d2) # N: Revealed type is "builtins.object" + reveal_type(e2) # N: Revealed type is "builtins.int" + + m3: Tuple[int, int, Unpack[Ts], int, int] + match m3: + case [a3, *b3, c3]: + reveal_type(a3) # N: Revealed type is "builtins.int" + reveal_type(b3) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(c3) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index bf7a928ff51d..29abe9cb025b 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2036,3 +2036,23 @@ class Ben(Object): foo_method = cls.MY_MAP["foo"] return foo_method(Foo()) [builtins fixtures/isinstancelist.pyi] + +[case testSelfTypeOnGenericClassObjectNewStyleBound] +from typing import Generic, TypeVar, Self + +T = TypeVar("T") +S = TypeVar("S") +class B(Generic[T, S]): + def copy(self) -> Self: ... + +b: B[int, str] +reveal_type(B.copy(b)) # N: Revealed type is "__main__.B[builtins.int, builtins.str]" + +class C(B[T, S]): ... + +c: C[int, str] +reveal_type(C.copy(c)) # N: Revealed type is "__main__.C[builtins.int, builtins.str]" + +B.copy(42) # E: Value of type variable "Self" of "copy" of "B" cannot be "int" +C.copy(42) # E: Value of type variable "Self" of "copy" of "B" cannot be "int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 1a2573898170..7b8a22313b36 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1911,6 +1911,180 @@ z = C[int]() # E: Bad number of arguments, expected: at least 2, given: 1 reveal_type(z) # N: Revealed type is "__main__.C[Any, Unpack[builtins.tuple[Any, ...]], Any]" [builtins fixtures/tuple.pyi] +[case testVariadicTupleTupleSubclassPrefixSuffix] +from typing import Tuple +from typing_extensions import Unpack + +i: int + +class A(Tuple[int, Unpack[Tuple[int, ...]]]): ... +a: A +reveal_type(a[i]) # N: Revealed type is "builtins.int" + +class B(Tuple[Unpack[Tuple[int, ...]], int]): ... +b: B +reveal_type(b[i]) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testVariadicClassSubclassInit] +from typing import Tuple, Generic, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): + def __init__(self, x: Tuple[Unpack[Ts]], *args: Unpack[Ts]) -> None: ... +reveal_type(B) # N: Revealed type is "def [Ts] (x: Tuple[Unpack[Ts`1]], *args: Unpack[Ts`1]) -> __main__.B[Unpack[Ts`1]]" + +T = TypeVar("T") +S = TypeVar("S") +class C(B[T, S]): ... +reveal_type(C) # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2], T`1, S`2) -> __main__.C[T`1, S`2]" +[builtins fixtures/tuple.pyi] + +[case testVariadicClassGenericSelf] +from typing import Tuple, Generic, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +S = TypeVar("S") +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): + def copy(self: T) -> T: ... + def on_pair(self: B[T, S]) -> Tuple[T, S]: ... + +b1: B[int] +reveal_type(b1.on_pair()) # E: Invalid self argument "B[int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \ + # N: Revealed type is "Tuple[Never, Never]" +b2: B[int, str] +reveal_type(b2.on_pair()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +b3: B[int, str, int] +reveal_type(b3.on_pair()) # E: Invalid self argument "B[int, str, int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \ + # N: Revealed type is "Tuple[Never, Never]" + +class C(B[T, S]): ... +c: C[int, str] +reveal_type(c.copy()) # N: Revealed type is "__main__.C[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testVariadicClassNewStyleSelf] +from typing import Generic, TypeVar, Self +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class B(Generic[Unpack[Ts]]): + next: Self + def copy(self) -> Self: + return self.next + +b: B[int, str, int] +reveal_type(b.next) # N: Revealed type is "__main__.B[builtins.int, builtins.str, builtins.int]" +reveal_type(b.copy()) # N: Revealed type is "__main__.B[builtins.int, builtins.str, builtins.int]" +reveal_type(B.copy(b)) # N: Revealed type is "__main__.B[builtins.int, builtins.str, builtins.int]" + +T = TypeVar("T") +S = TypeVar("S") +class C(B[T, S]): ... +c: C[int, str] + +reveal_type(c.next) # N: Revealed type is "__main__.C[builtins.int, builtins.str]" +reveal_type(c.copy()) # N: Revealed type is "__main__.C[builtins.int, builtins.str]" +reveal_type(C.copy(c)) # N: Revealed type is "__main__.C[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleDataclass] +from dataclasses import dataclass +from typing import Generic, TypeVar, Tuple +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") + +@dataclass +class B(Generic[Unpack[Ts]]): + items: Tuple[Unpack[Ts]] + +reveal_type(B) # N: Revealed type is "def [Ts] (items: Tuple[Unpack[Ts`1]]) -> __main__.B[Unpack[Ts`1]]" +b = B((1, "yes")) +reveal_type(b.items) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + +T = TypeVar("T") +S = TypeVar("S") + +@dataclass +class C(B[T, S]): + first: T + second: S + +reveal_type(C) # N: Revealed type is "def [T, S] (items: Tuple[T`1, S`2], first: T`1, second: S`2) -> __main__.C[T`1, S`2]" +c = C((1, "yes"), 2, "no") +reveal_type(c.items) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(c.first) # N: Revealed type is "builtins.int" +reveal_type(c.second) # N: Revealed type is "builtins.str" +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testVariadicTupleInProtocol] +from typing import Protocol, Tuple, List +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class P(Protocol[Unpack[Ts]]): + def items(self) -> Tuple[Unpack[Ts]]: ... + +class PC(Protocol[Unpack[Ts]]): + def meth(self, *args: Unpack[Ts]) -> None: ... + +def get_items(x: P[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: ... +def match(x: PC[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: ... + +class Bad: + def items(self) -> List[int]: ... + def meth(self, *, named: int) -> None: ... + +class Good: + def items(self) -> Tuple[int, str]: ... + def meth(self, __x: int, y: str) -> None: ... + +g: Good +reveal_type(get_items(g)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(match(g)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + +b: Bad +get_items(b) # E: Argument 1 to "get_items" has incompatible type "Bad"; expected "P[Unpack[Tuple[Never, ...]]]" \ + # N: Following member(s) of "Bad" have conflicts: \ + # N: Expected: \ + # N: def items(self) -> Tuple[Never, ...] \ + # N: Got: \ + # N: def items(self) -> List[int] +match(b) # E: Argument 1 to "match" has incompatible type "Bad"; expected "PC[Unpack[Tuple[Never, ...]]]" \ + # N: Following member(s) of "Bad" have conflicts: \ + # N: Expected: \ + # N: def meth(self, *args: Never) -> None \ + # N: Got: \ + # N: def meth(self, *, named: int) -> None +[builtins fixtures/tuple.pyi] + +[case testVariadicTupleCollectionCheck] +from typing import Tuple, Optional +from typing_extensions import Unpack + +allowed: Tuple[int, Unpack[Tuple[int, ...]]] + +x: Optional[int] +if x in allowed: + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testJoinOfVariadicTupleCallablesNoCrash] +from typing import Callable, Tuple + +f: Callable[[int, *Tuple[str, ...], int], None] +g: Callable[[int, *Tuple[str, ...], int], None] +reveal_type([f, g]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.int]])]" + +h: Callable[[int, *Tuple[str, ...], str], None] +reveal_type([f, h]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], Never]])]" +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleBothUnpacksSimple] from typing import Tuple from typing_extensions import Unpack, TypeVarTuple, TypedDict diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index cb24467cbf41..5dc42bd62d9b 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9908,6 +9908,128 @@ x = 0 # Arbitrary change to trigger reprocessing == a.py:3: note: Revealed type is "Tuple[Literal[1]?, Literal['x']?]" +[case testVariadicClassFineUpdateRegularToVariadic] +from typing import Any +from lib import C + +x: C[int, str] + +[file lib.py] +from typing import Generic, TypeVar + +T = TypeVar("T") +S = TypeVar("S") +class C(Generic[T, S]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... +[builtins fixtures/tuple.pyi] +[out] +== + +[case testVariadicClassFineUpdateVariadicToRegular] +from typing import Any +from lib import C + +x: C[int, str, int] + +[file lib.py] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... +[file lib.py.2] +from typing import Generic, TypeVar + +T = TypeVar("T") +S = TypeVar("S") +class C(Generic[T, S]): ... +[builtins fixtures/tuple.pyi] +[out] +== +main:4: error: "C" expects 2 type arguments, but 3 given + +-- Order of error messages is different, so we repeat the test twice. +[case testVariadicClassFineUpdateValidToInvalidCached-only_when_cache] +from typing import Any +from lib import C + +x: C[int, str] + +[file lib.py] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Ts]): ... +[builtins fixtures/tuple.pyi] +[out] +== +main:4: error: "C" expects no type arguments, but 2 given +lib.py:5: error: Free type variable expected in Generic[...] + +[case testVariadicClassFineUpdateValidToInvalid-only_when_nocache] +from typing import Any +from lib import C + +x: C[int, str] + +[file lib.py] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Ts]): ... +[builtins fixtures/tuple.pyi] +[out] +== +lib.py:5: error: Free type variable expected in Generic[...] +main:4: error: "C" expects no type arguments, but 2 given + +[case testVariadicClassFineUpdateInvalidToValid] +from typing import Any +from lib import C + +x: C[int, str] + +[file lib.py] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Ts]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): ... +[builtins fixtures/tuple.pyi] +[out] +lib.py:5: error: Free type variable expected in Generic[...] +main:4: error: "C" expects no type arguments, but 2 given +== + [case testUnpackKwargsUpdateFine] import m [file shared.py] From c4ab46e6acdeab8fd503322311e2b934c9622695 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 28 Oct 2023 15:00:13 +0100 Subject: [PATCH 0348/1617] Cache information about whether file is typeshed file (#16351) We used to check if a file is in typeshed a lot. This seems to speed up self-check by about 2%, and this should also speed up tests a bit. --- mypy/checker.py | 4 ++-- mypy/nodes.py | 11 ++++++++++- mypy/semanal.py | 13 ++----------- mypy/semanal_main.py | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 62ba642256bf..b2804b25e35c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -222,7 +222,7 @@ from mypy.types_utils import is_overlapping_none, remove_optional, store_argument_type, strip_type from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars, fill_typevars_with_any, has_no_typevars -from mypy.util import is_dunder, is_sunder, is_typeshed_file +from mypy.util import is_dunder, is_sunder from mypy.visitor import NodeVisitor T = TypeVar("T") @@ -400,7 +400,7 @@ def __init__( self.pass_num = 0 self.current_node_deferred = False self.is_stub = tree.is_stub - self.is_typeshed_stub = is_typeshed_file(options.abs_custom_typeshed_dir, path) + self.is_typeshed_stub = tree.is_typeshed_file(options) self.inferred_attribute_types = None # If True, process function definitions. If False, don't. This is used diff --git a/mypy/nodes.py b/mypy/nodes.py index 0e5c078d0227..1d7b3e3be84b 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -27,7 +27,7 @@ import mypy.strconv from mypy.options import Options -from mypy.util import short_type +from mypy.util import is_typeshed_file, short_type from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor if TYPE_CHECKING: @@ -283,6 +283,7 @@ class MypyFile(SymbolNode): "is_partial_stub_package", "plugin_deps", "future_import_flags", + "_is_typeshed_file", ) __match_args__ = ("name", "path", "defs") @@ -319,6 +320,7 @@ class MypyFile(SymbolNode): plugin_deps: dict[str, set[str]] # Future imports defined in this file. Populated during semantic analysis. future_import_flags: set[str] + _is_typeshed_file: bool | None def __init__( self, @@ -346,6 +348,7 @@ def __init__( self.is_cache_skeleton = False self.is_partial_stub_package = False self.future_import_flags = set() + self._is_typeshed_file = None def local_definitions(self) -> Iterator[Definition]: """Return all definitions within the module (including nested). @@ -371,6 +374,12 @@ def is_package_init_file(self) -> bool: def is_future_flag_set(self, flag: str) -> bool: return flag in self.future_import_flags + def is_typeshed_file(self, options: Options) -> bool: + # Cache result since this is called a lot + if self._is_typeshed_file is None: + self._is_typeshed_file = is_typeshed_file(options.abs_custom_typeshed_dir, self.path) + return self._is_typeshed_file + def serialize(self) -> JsonDict: return { ".class": "MypyFile", diff --git a/mypy/semanal.py b/mypy/semanal.py index 27491ac695ae..41943e1db8b0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -283,14 +283,7 @@ ) from mypy.types_utils import is_invalid_recursive_alias, store_argument_type from mypy.typevars import fill_typevars -from mypy.util import ( - correct_relative_import, - is_dunder, - is_typeshed_file, - module_prefix, - unmangle, - unnamed_function, -) +from mypy.util import correct_relative_import, is_dunder, module_prefix, unmangle, unnamed_function from mypy.visitor import NodeVisitor T = TypeVar("T") @@ -777,9 +770,7 @@ def file_context( self.cur_mod_id = file_node.fullname with scope.module_scope(self.cur_mod_id): self._is_stub_file = file_node.path.lower().endswith(".pyi") - self._is_typeshed_stub_file = is_typeshed_file( - options.abs_custom_typeshed_dir, file_node.path - ) + self._is_typeshed_stub_file = file_node.is_typeshed_file(options) self.globals = file_node.names self.tvar_scope = TypeVarLikeScope() diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index ec09deb0952f..1185a3821553 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -380,7 +380,7 @@ def check_type_arguments(graph: Graph, scc: list[str], errors: Errors) -> None: analyzer = TypeArgumentAnalyzer( errors, state.options, - is_typeshed_file(state.options.abs_custom_typeshed_dir, state.path or ""), + state.tree.is_typeshed_file(state.options), state.manager.semantic_analyzer.named_type, ) with state.wrap_context(): From c76132f63de5de4d3f9818d070c1cd26d2209d5a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 28 Oct 2023 15:00:23 +0100 Subject: [PATCH 0349/1617] Add fast path for checking self types (#16352) The check was pretty expensive, though usually it's not doing anything non-trivial. Added a fast path for cases where we use the implicit self type, which covers the vast majority of cases. This makes self-check about 4% faster. --- mypy/checker.py | 58 +++++++++++++++++++++++++----------------------- mypy/subtypes.py | 12 ++++++++++ 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b2804b25e35c..f51ba746ea75 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1199,13 +1199,14 @@ def check_func_def( # Push return type. self.return_types.append(typ.ret_type) + with self.scope.push_function(defn): + # We temporary push the definition to get the self type as + # visible from *inside* of this function/method. + ref_type: Type | None = self.scope.active_self_type() + # Store argument types. for i in range(len(typ.arg_types)): arg_type = typ.arg_types[i] - with self.scope.push_function(defn): - # We temporary push the definition to get the self type as - # visible from *inside* of this function/method. - ref_type: Type | None = self.scope.active_self_type() if ( isinstance(defn, FuncDef) and ref_type is not None @@ -1215,30 +1216,31 @@ def check_func_def( ): if defn.is_class or defn.name == "__new__": ref_type = mypy.types.TypeType.make_normalized(ref_type) - # This level of erasure matches the one in checkmember.check_self_arg(), - # better keep these two checks consistent. - erased = get_proper_type(erase_typevars(erase_to_bound(arg_type))) - if not is_subtype(ref_type, erased, ignore_type_params=True): - if ( - isinstance(erased, Instance) - and erased.type.is_protocol - or isinstance(erased, TypeType) - and isinstance(erased.item, Instance) - and erased.item.type.is_protocol - ): - # We allow the explicit self-type to be not a supertype of - # the current class if it is a protocol. For such cases - # the consistency check will be performed at call sites. - msg = None - elif typ.arg_names[i] in {"self", "cls"}: - msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format( - erased.str_with_options(self.options), - ref_type.str_with_options(self.options), - ) - else: - msg = message_registry.MISSING_OR_INVALID_SELF_TYPE - if msg: - self.fail(msg, defn) + if not is_same_type(arg_type, ref_type): + # This level of erasure matches the one in checkmember.check_self_arg(), + # better keep these two checks consistent. + erased = get_proper_type(erase_typevars(erase_to_bound(arg_type))) + if not is_subtype(ref_type, erased, ignore_type_params=True): + if ( + isinstance(erased, Instance) + and erased.type.is_protocol + or isinstance(erased, TypeType) + and isinstance(erased.item, Instance) + and erased.item.type.is_protocol + ): + # We allow the explicit self-type to be not a supertype of + # the current class if it is a protocol. For such cases + # the consistency check will be performed at call sites. + msg = None + elif typ.arg_names[i] in {"self", "cls"}: + msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format( + erased.str_with_options(self.options), + ref_type.str_with_options(self.options), + ) + else: + msg = message_registry.MISSING_OR_INVALID_SELF_TYPE + if msg: + self.fail(msg, defn) elif isinstance(arg_type, TypeVarType): # Refuse covariant parameter type variables # TODO: check recursively for inner type variables diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 6d129683c3f5..7e37751b1c15 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -258,6 +258,18 @@ def is_same_type( This means types may have different representation (e.g. an alias, or a non-simplified union) but are semantically exchangeable in all contexts. """ + # First, use fast path for some common types. This is performance-critical. + if ( + type(a) is Instance + and type(b) is Instance + and a.type == b.type + and len(a.args) == len(b.args) + and a.last_known_value is b.last_known_value + ): + return all(is_same_type(x, y) for x, y in zip(a.args, b.args)) + elif isinstance(a, TypeVarType) and isinstance(b, TypeVarType) and a.id == b.id: + return True + # Note that using ignore_promotions=True (default) makes types like int and int64 # considered not the same type (which is the case at runtime). # Also Union[bool, int] (if it wasn't simplified before) will be different From 2aa2443107534715a650dbe78474e7d91cc9df20 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Oct 2023 09:04:58 -0700 Subject: [PATCH 0350/1617] Avoid importing from setuptools._distutils (#16348) Fixes #16318, as requested by setuptools maintainer --- mypyc/build.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/mypyc/build.py b/mypyc/build.py index 9889577d4add..0af8908e14d0 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -40,8 +40,16 @@ from mypyc.namegen import exported_name from mypyc.options import CompilerOptions -if sys.version_info < (3, 12): - if TYPE_CHECKING: +try: + # Import setuptools so that it monkey-patch overrides distutils + import setuptools +except ImportError: + pass + +if TYPE_CHECKING: + if sys.version_info >= (3, 12): + from setuptools import Extension + else: from distutils.core import Extension as _distutils_Extension from typing_extensions import TypeAlias @@ -49,22 +57,11 @@ Extension: TypeAlias = Union[_setuptools_Extension, _distutils_Extension] - try: - # Import setuptools so that it monkey-patch overrides distutils - import setuptools - except ImportError: - pass - from distutils import ccompiler, sysconfig +if sys.version_info >= (3, 12): + # From setuptools' monkeypatch + from distutils import ccompiler, sysconfig # type: ignore[import-not-found] else: - import setuptools - from setuptools import Extension - from setuptools._distutils import ( - ccompiler as _ccompiler, # type: ignore[attr-defined] - sysconfig as _sysconfig, # type: ignore[attr-defined] - ) - - ccompiler = _ccompiler - sysconfig = _sysconfig + from distutils import ccompiler, sysconfig def get_extension() -> type[Extension]: From 65a068ed21c4563590062ad3fbd9e58fe0e7968d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 29 Oct 2023 06:45:24 +0000 Subject: [PATCH 0351/1617] Speed up type argument checking (#16353) The upper bound is usually `object`, so add a fast path and skip a potentially slow subtype check if that's the case. Also make type annotations more precise. This seems to at least speed up type checker tests, by 1-2% or so. This also potentially speeds up self-check a bit, though probably by less than 1%. --- mypy/semanal_typeargs.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index a25bab8de054..15ea15d612c0 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Callable, Sequence +from typing import Callable from mypy import errorcodes as codes, message_registry from mypy.errorcodes import ErrorCode @@ -88,7 +88,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: return self.seen_aliases.add(t) assert t.alias is not None, f"Unfixed type alias {t.type_ref}" - is_error = self.validate_args(t.alias.name, t.args, t.alias.alias_tvars, t) + is_error = self.validate_args(t.alias.name, tuple(t.args), t.alias.alias_tvars, t) if not is_error: # If there was already an error for the alias itself, there is no point in checking # the expansion, most likely it will result in the same kind of error. @@ -131,7 +131,7 @@ def visit_instance(self, t: Instance) -> None: t.args = unpacked.args def validate_args( - self, name: str, args: Sequence[Type], type_vars: list[TypeVarLikeType], ctx: Context + self, name: str, args: tuple[Type, ...], type_vars: list[TypeVarLikeType], ctx: Context ) -> bool: if any(isinstance(v, TypeVarTupleType) for v in type_vars): prefix = next(i for (i, v) in enumerate(type_vars) if isinstance(v, TypeVarTupleType)) @@ -140,7 +140,7 @@ def validate_args( start, middle, end = split_with_prefix_and_suffix( tuple(args), prefix, len(type_vars) - prefix - 1 ) - args = list(start) + [TupleType(list(middle), tvt.tuple_fallback)] + list(end) + args = start + (TupleType(list(middle), tvt.tuple_fallback),) + end is_error = False for (i, arg), tvar in zip(enumerate(args), type_vars): @@ -174,7 +174,14 @@ def validate_args( arg_values = [arg] if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx): is_error = True - if not is_subtype(arg, tvar.upper_bound): + # Check against upper bound. Since it's object the vast majority of the time, + # add fast path to avoid a potentially slow subtype check. + upper_bound = tvar.upper_bound + object_upper_bound = ( + type(upper_bound) is Instance + and upper_bound.type.fullname == "builtins.object" + ) + if not object_upper_bound and not is_subtype(arg, upper_bound): if self.in_type_alias_expr and isinstance(arg, TypeVarType): # Type aliases are allowed to use unconstrained type variables # error will be checked at substitution point. @@ -184,7 +191,7 @@ def validate_args( message_registry.INVALID_TYPEVAR_ARG_BOUND.format( format_type(arg, self.options), name, - format_type(tvar.upper_bound, self.options), + format_type(upper_bound, self.options), ), ctx, code=codes.TYPE_VAR, From cf045d924d6688f5f4d0c3402f38d30bc81db299 Mon Sep 17 00:00:00 2001 From: dinaldoap <38653153+dinaldoap@users.noreply.github.com> Date: Mon, 30 Oct 2023 02:06:19 -0300 Subject: [PATCH 0352/1617] doc: remove duplicate word (#16365) This PR removes one of the duplicate **in** in the sentence "This option is only useful in in the absence of `__init__.py`" in the file `docs/source/command_line.rst`. --- docs/source/command_line.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 4e954c7c2ccb..5db118334519 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -133,7 +133,7 @@ imports. This flag tells mypy that top-level packages will be based in either the current directory, or a member of the ``MYPYPATH`` environment variable or - :confval:`mypy_path` config option. This option is only useful in + :confval:`mypy_path` config option. This option is only useful in the absence of `__init__.py`. See :ref:`Mapping file paths to modules ` for details. From b8c748a77a27b27599b9c2b4097427e055f4c16c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 30 Oct 2023 11:07:20 +0000 Subject: [PATCH 0353/1617] Fix incremental crash on TypedDict in method (#16364) Fixes https://github.com/python/mypy/issues/16336 All the story with `@`-names is a mess. FWIW I just copied the logic from named tuples, where it works. So although it is a mess, it will be now be a consistent mess, with full parity between `NamedTuple` and `TypedDict`. --- mypy/semanal.py | 7 ++++--- mypy/semanal_typeddict.py | 2 ++ test-data/unit/check-incremental.test | 22 +++++++++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 41943e1db8b0..bd24c48ed24f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1745,7 +1745,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool: if info is None: self.mark_incomplete(defn.name, defn) else: - self.prepare_class_def(defn, info) + self.prepare_class_def(defn, info, custom_names=True) return True return False @@ -2099,8 +2099,9 @@ def prepare_class_def( # Preserve name from previous fine-grained incremental run. global_name = defn.info.name defn.fullname = defn.info._fullname - if defn.info.is_named_tuple: - # Named tuple nested within a class is stored in the class symbol table. + if defn.info.is_named_tuple or defn.info.typeddict_type: + # Named tuples and Typed dicts nested within a class are stored + # in the class symbol table. self.add_symbol_skip_local(global_name, defn.info) else: self.globals[global_name] = SymbolTableNode(GDEF, defn.info) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 51424d8800d2..e9aaee55879a 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -101,6 +101,8 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N fields, types, statements, required_keys = self.analyze_typeddict_classdef_fields(defn) if fields is None: return True, None # Defer + if self.api.is_func_scope() and "@" not in defn.name: + defn.name += "@" + str(defn.line) info = self.build_typeddict_typeinfo( defn.name, fields, types, required_keys, defn.line, existing_info ) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index eb7a795f99c0..806a585bff39 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5135,7 +5135,6 @@ tmp/b.py:4: error: First argument to namedtuple() should be "NT", not "BadName" tmp/b.py:4: error: First argument to namedtuple() should be "NT", not "BadName" [case testNewAnalyzerIncrementalMethodNamedTuple] - import a [file a.py] from b import C @@ -6540,3 +6539,24 @@ from typing_extensions import TypedDict def test() -> None: Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore [builtins fixtures/dict.pyi] + +[case testNoIncrementalCrashOnTypedDictMethod] +import a +[file a.py] +from b import C +x: C +[file a.py.2] +from b import C +x: C +reveal_type(x.h) +[file b.py] +from typing_extensions import TypedDict +class C: + def __init__(self) -> None: + self.h: Hidden + class Hidden(TypedDict): + x: int +[builtins fixtures/dict.pyi] +[out] +[out2] +tmp/a.py:3: note: Revealed type is "TypedDict('b.C.Hidden@5', {'x': builtins.int})" From 4e30e896486b774cdecaef6d3521a585b8acf8bc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 30 Oct 2023 11:55:21 +0000 Subject: [PATCH 0354/1617] Fix dmypy inspect for namespace packages (#16357) Fixes https://github.com/python/mypy/issues/15781 The fix is to switch to already resolved paths instead of relying on `crawl_up()`. This should be more robust w.r.t. various special cases. I also tweak the tests slightly to show full file names, to have a more consistent output. --- mypy/dmypy_server.py | 4 +- mypy/inspections.py | 16 ++--- mypy/test/testfinegrained.py | 2 +- test-data/unit/daemon.test | 18 +++++- test-data/unit/fine-grained-inspect.test | 80 ++++++++++++------------ 5 files changed, 65 insertions(+), 55 deletions(-) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 9cc0888fc208..0db349b5bf82 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -461,6 +461,7 @@ def initialize_fine_grained( messages = result.errors self.fine_grained_manager = FineGrainedBuildManager(result) + original_sources_len = len(sources) if self.following_imports(): sources = find_all_sources_in_build(self.fine_grained_manager.graph, sources) self.update_sources(sources) @@ -525,7 +526,8 @@ def initialize_fine_grained( __, n_notes, __ = count_stats(messages) status = 1 if messages and n_notes < len(messages) else 0 - messages = self.pretty_messages(messages, len(sources), is_tty, terminal_width) + # We use explicit sources length to match the logic in non-incremental mode. + messages = self.pretty_messages(messages, original_sources_len, is_tty, terminal_width) return {"out": "".join(s + "\n" for s in messages), "err": "", "status": status} def fine_grained_increment( diff --git a/mypy/inspections.py b/mypy/inspections.py index cb695a80eef2..45e981a24af2 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -6,7 +6,6 @@ from typing import Callable from mypy.build import State -from mypy.find_sources import InvalidSourceList, SourceFinder from mypy.messages import format_type from mypy.modulefinder import PYTHON_EXTENSIONS from mypy.nodes import ( @@ -206,9 +205,6 @@ def __init__( force_reload: bool = False, ) -> None: self.fg_manager = fg_manager - self.finder = SourceFinder( - self.fg_manager.manager.fscache, self.fg_manager.manager.options - ) self.verbosity = verbosity self.limit = limit self.include_span = include_span @@ -561,16 +557,14 @@ def find_module(self, file: str) -> tuple[State | None, dict[str, object]]: if not any(file.endswith(ext) for ext in PYTHON_EXTENSIONS): return None, {"error": "Source file is not a Python file"} - try: - module, _ = self.finder.crawl_up(os.path.normpath(file)) - except InvalidSourceList: - return None, {"error": "Invalid source file name: " + file} - - state = self.fg_manager.graph.get(module) + # We are using a bit slower but robust way to find a module by path, + # to be sure that namespace packages are handled properly. + abs_path = os.path.abspath(file) + state = next((s for s in self.fg_manager.graph.values() if s.abspath == abs_path), None) self.module = state return ( state, - {"out": f"Unknown module: {module}", "err": "", "status": 1} if state is None else {}, + {"out": f"Unknown module: {file}", "err": "", "status": 1} if state is None else {}, ) def run_inspection( diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index ba0526d32558..c517c54286d7 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -352,7 +352,7 @@ def maybe_inspect(self, step: int, server: Server, src: str) -> list[str]: ) val = res["error"] if "error" in res else res["out"] + res["err"] output.extend(val.strip().split("\n")) - return normalize_messages(output) + return output def get_suggest(self, program_text: str, incremental_step: int) -> list[tuple[str, str]]: step_bit = "1?" if incremental_step == 1 else str(incremental_step) diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 18a03a92207d..ca0cd90911b9 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -379,7 +379,7 @@ $ dmypy inspect foo.pyc:1:1:2:2 Source file is not a Python file == Return code: 2 $ dmypy inspect bar/baz.py:1:1:2:2 -Unknown module: baz +Unknown module: bar/baz.py == Return code: 1 $ dmypy inspect foo.py:3:1:1:1 "end_line" must not be before "line" @@ -434,7 +434,7 @@ $ dmypy inspect foo.pyc:1:2 Source file is not a Python file == Return code: 2 $ dmypy inspect bar/baz.py:1:2 -Unknown module: baz +Unknown module: bar/baz.py == Return code: 1 $ dmypy inspect foo.py:7:5 --include-span 7:5:7:5 -> "int" @@ -571,3 +571,17 @@ class A: x: int class B: x: int + +[case testDaemonInspectSelectCorrectFile] +$ dmypy run test.py --export-types +Daemon started +Success: no issues found in 1 source file +$ dmypy inspect demo/test.py:1:1 +"int" +$ dmypy inspect test.py:1:1 +"str" +[file test.py] +b: str +from demo.test import a +[file demo/test.py] +a: int diff --git a/test-data/unit/fine-grained-inspect.test b/test-data/unit/fine-grained-inspect.test index 2c575ec365b1..f8ce35585c10 100644 --- a/test-data/unit/fine-grained-inspect.test +++ b/test-data/unit/fine-grained-inspect.test @@ -1,8 +1,8 @@ [case testInspectTypeBasic] -# inspect2: --include-kind foo.py:10:13 -# inspect2: --show=type --include-kind foo.py:10:13 -# inspect2: --include-span -vv foo.py:12:5 -# inspect2: --include-span --include-kind foo.py:12:5:12:9 +# inspect2: --include-kind tmp/foo.py:10:13 +# inspect2: --show=type --include-kind tmp/foo.py:10:13 +# inspect2: --include-span -vv tmp/foo.py:12:5 +# inspect2: --include-span --include-kind tmp/foo.py:12:5:12:9 import foo [file foo.py] from typing import TypeVar, Generic @@ -29,10 +29,10 @@ MemberExpr -> "T" CallExpr:12:5:12:9 -> "C[int]" [case testInspectAttrsBasic] -# inspect2: --show=attrs foo.py:6:1 -# inspect2: --show=attrs foo.py:7:1 -# inspect2: --show=attrs foo.py:10:1 -# inspect2: --show=attrs --include-object-attrs foo.py:10:1 +# inspect2: --show=attrs tmp/foo.py:6:1 +# inspect2: --show=attrs tmp/foo.py:7:1 +# inspect2: --show=attrs tmp/foo.py:10:1 +# inspect2: --show=attrs --include-object-attrs tmp/foo.py:10:1 import foo [file foo.py] from bar import Meta @@ -56,12 +56,12 @@ class Meta(type): {"function": ["__name__"], "object": ["__init__"]} [case testInspectDefBasic] -# inspect2: --show=definition foo.py:5:5 -# inspect2: --show=definition --include-kind foo.py:6:3 -# inspect2: --show=definition --include-span foo.py:7:5 -# inspect2: --show=definition foo.py:8:1:8:4 -# inspect2: --show=definition foo.py:8:6:8:8 -# inspect2: --show=definition foo.py:9:3 +# inspect2: --show=definition tmp/foo.py:5:5 +# inspect2: --show=definition --include-kind tmp/foo.py:6:3 +# inspect2: --show=definition --include-span tmp/foo.py:7:5 +# inspect2: --show=definition tmp/foo.py:8:1:8:4 +# inspect2: --show=definition tmp/foo.py:8:6:8:8 +# inspect2: --show=definition tmp/foo.py:9:3 import foo [file foo.py] from bar import var, test, A @@ -95,18 +95,18 @@ def foo(x: Union[int, str]) -> None: [builtins fixtures/classmethod.pyi] [out] == -bar.py:4:0:meth +tmp/bar.py:4:0:meth MemberExpr -> tmp/bar.py:2:5:x 7:1:7:5 -> tmp/bar.py:6:9:y -bar.py:9:1:test -bar.py:8:1:var -baz.py:3:2:foo +tmp/bar.py:9:1:test +tmp/bar.py:8:1:var +tmp/baz.py:3:2:foo [case testInspectFallbackAttributes] -# inspect2: --show=attrs --include-object-attrs foo.py:5:1 -# inspect2: --show=attrs foo.py:8:1 -# inspect2: --show=attrs --include-kind foo.py:10:1 -# inspect2: --show=attrs --include-kind --include-object-attrs foo.py:10:1 +# inspect2: --show=attrs --include-object-attrs tmp/foo.py:5:1 +# inspect2: --show=attrs tmp/foo.py:8:1 +# inspect2: --show=attrs --include-kind tmp/foo.py:10:1 +# inspect2: --show=attrs --include-kind --include-object-attrs tmp/foo.py:10:1 import foo [file foo.py] class B: ... @@ -128,7 +128,7 @@ NameExpr -> {} NameExpr -> {"object": ["__eq__", "__init__", "__ne__"]} [case testInspectTypeVarBoundAttrs] -# inspect2: --show=attrs foo.py:8:13 +# inspect2: --show=attrs tmp/foo.py:8:13 import foo [file foo.py] from typing import TypeVar @@ -144,10 +144,10 @@ def foo(arg: T) -> T: {"C": ["x"]} [case testInspectTypeVarValuesAttrs] -# inspect2: --show=attrs --force-reload foo.py:13:13 -# inspect2: --show=attrs --force-reload --union-attrs foo.py:13:13 -# inspect2: --show=attrs foo.py:16:5 -# inspect2: --show=attrs --union-attrs foo.py:16:5 +# inspect2: --show=attrs --force-reload tmp/foo.py:13:13 +# inspect2: --show=attrs --force-reload --union-attrs tmp/foo.py:13:13 +# inspect2: --show=attrs tmp/foo.py:16:5 +# inspect2: --show=attrs --union-attrs tmp/foo.py:16:5 import foo [file foo.py] from typing import TypeVar, Generic @@ -174,8 +174,8 @@ class C(Generic[T]): {"A": ["x", "z"], "B": ["y", "z"]} [case testInspectTypeVarBoundDef] -# inspect2: --show=definition foo.py:9:13 -# inspect2: --show=definition foo.py:8:9 +# inspect2: --show=definition tmp/foo.py:9:13 +# inspect2: --show=definition tmp/foo.py:8:9 import foo [file foo.py] from typing import TypeVar @@ -189,13 +189,13 @@ def foo(arg: T) -> T: return arg [out] == -foo.py:7:9:arg -foo.py:4:5:x +tmp/foo.py:7:9:arg +tmp/foo.py:4:5:x [case testInspectTypeVarValuesDef] -# inspect2: --show=definition --force-reload foo.py:13:9 -# inspect2: --show=definition --force-reload foo.py:14:13 -# inspect2: --show=definition foo.py:18:7 +# inspect2: --show=definition --force-reload tmp/foo.py:13:9 +# inspect2: --show=definition --force-reload tmp/foo.py:14:13 +# inspect2: --show=definition tmp/foo.py:18:7 import foo [file foo.py] from typing import TypeVar, Generic @@ -218,12 +218,12 @@ class C(Generic[T]): x.z [out] == -foo.py:5:5:z, tmp/foo.py:9:5:z -foo.py:12:9:arg -foo.py:5:5:z, tmp/foo.py:9:5:z +tmp/foo.py:5:5:z, tmp/foo.py:9:5:z +tmp/foo.py:12:9:arg +tmp/foo.py:5:5:z, tmp/foo.py:9:5:z [case testInspectModuleAttrs] -# inspect2: --show=attrs foo.py:2:1 +# inspect2: --show=attrs tmp/foo.py:2:1 import foo [file foo.py] from pack import bar @@ -239,7 +239,7 @@ class C: ... {"": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]} [case testInspectModuleDef] -# inspect2: --show=definition --include-kind foo.py:2:1 +# inspect2: --show=definition --include-kind tmp/foo.py:2:1 import foo [file foo.py] from pack import bar @@ -255,7 +255,7 @@ NameExpr -> tmp/pack/bar.py:1:1:bar MemberExpr -> tmp/pack/bar.py:3:5:x [case testInspectFunctionArgDef] -# inspect2: --show=definition --include-span foo.py:4:13 +# inspect2: --show=definition --include-span tmp/foo.py:4:13 # TODO: for now all arguments have line/column set to function definition. import foo [file foo.py] From b064a5c183b53a84d895bb8e3c36a3a74e24be9c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 30 Oct 2023 11:57:42 +0000 Subject: [PATCH 0355/1617] Fix dmypy inspect on Windows (#16355) Fixes https://github.com/python/mypy/issues/15780 --- mypy/inspections.py | 24 ++++++++++++++++-------- mypy/test/testutil.py | 5 +++++ test-data/unit/daemon.test | 3 +++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/mypy/inspections.py b/mypy/inspections.py index 45e981a24af2..3e660a0bd7a6 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -215,13 +215,6 @@ def __init__( # Module for which inspection was requested. self.module: State | None = None - def parse_location(self, location: str) -> tuple[str, list[int]]: - if location.count(":") not in [2, 4]: - raise ValueError("Format should be file:line:column[:end_line:end_column]") - parts = location.split(":") - module, *rest = parts - return module, [int(p) for p in rest] - def reload_module(self, state: State) -> None: """Reload given module while temporary exporting types.""" old = self.fg_manager.manager.options.export_types @@ -575,7 +568,7 @@ def run_inspection( This can be re-used by various simple inspections. """ try: - file, pos = self.parse_location(location) + file, pos = parse_location(location) except ValueError as err: return {"error": str(err)} @@ -617,3 +610,18 @@ def get_definition(self, location: str) -> dict[str, object]: result["out"] = f"No name or member expressions at {location}" result["status"] = 1 return result + + +def parse_location(location: str) -> tuple[str, list[int]]: + if location.count(":") < 2: + raise ValueError("Format should be file:line:column[:end_line:end_column]") + parts = location.rsplit(":", maxsplit=2) + start, *rest = parts + # Note: we must allow drive prefix like `C:` on Windows. + if start.count(":") < 2: + return start, [int(p) for p in rest] + parts = start.rsplit(":", maxsplit=2) + start, *start_rest = parts + if start.count(":") < 2: + return start, [int(p) for p in start_rest + rest] + raise ValueError("Format should be file:line:column[:end_line:end_column]") diff --git a/mypy/test/testutil.py b/mypy/test/testutil.py index 89184b11a826..571e4d0b11f2 100644 --- a/mypy/test/testutil.py +++ b/mypy/test/testutil.py @@ -3,6 +3,7 @@ import os from unittest import TestCase, mock +from mypy.inspections import parse_location from mypy.util import get_terminal_width @@ -15,3 +16,7 @@ def test_get_terminal_size_in_pty_defaults_to_80(self) -> None: with mock.patch.object(os, "get_terminal_size", return_value=ret): with mock.patch.dict(os.environ, values=mock_environ, clear=True): assert get_terminal_width() == 80 + + def test_parse_location_windows(self) -> None: + assert parse_location(r"C:\test.py:1:1") == (r"C:\test.py", [1, 1]) + assert parse_location(r"C:\test.py:1:1:1:1") == (r"C:\test.py", [1, 1, 1, 1]) diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index ca0cd90911b9..77367eb02bfe 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -372,6 +372,9 @@ foo.py:3: error: Incompatible types in assignment (expression has type "str", va $ dmypy inspect foo:1 Format should be file:line:column[:end_line:end_column] == Return code: 2 +$ dmypy inspect foo:1:2:3 +Source file is not a Python file +== Return code: 2 $ dmypy inspect foo.py:1:2:a:b invalid literal for int() with base 10: 'a' == Return code: 2 From ad0e183b0df7cc3dd94d9e1cd6f5710859beda96 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 30 Oct 2023 12:14:00 +0000 Subject: [PATCH 0356/1617] Enable Unpack/TypeVarTuple support (#16354) Fixes https://github.com/python/mypy/issues/12280 Fixes https://github.com/python/mypy/issues/14697 In this PR: * Enable `TypeVarTuple` and `Unpack` features. * Delete the old blanket `--enable-incomplete-features` flag that was deprecated a year ago. * Switch couple corner cases to `PreciseTupleTypes` feature. * Add the draft docs about the new feature. * Handle a previously unhandled case where variadic tuple appears in string formatting (discovered on mypy self-check, where `PreciseTupleTypes` is already enabled). --------- Co-authored-by: Jelle Zijlstra --- docs/source/command_line.rst | 52 +++++++++++++++++++++++++ mypy/checkexpr.py | 8 ++-- mypy/checkstrformat.py | 19 +++++++++ mypy/main.py | 17 +++----- mypy/options.py | 6 +-- mypy/semanal.py | 5 +-- mypy/test/testcheck.py | 3 -- mypy/test/testfinegrained.py | 3 +- mypy/test/testsemanal.py | 3 +- mypy/test/testtransform.py | 2 - mypy/typeanal.py | 4 +- test-data/unit/check-flags.test | 12 ------ test-data/unit/check-tuples.test | 16 ++++++++ test-data/unit/check-typevar-tuple.test | 3 ++ test-data/unit/cmdline.test | 18 +++++---- 15 files changed, 116 insertions(+), 55 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 5db118334519..a810c35cb77f 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -991,6 +991,58 @@ format into the specified directory. library or specify mypy installation with the setuptools extra ``mypy[reports]``. + +Enabling incomplete/experimental features +***************************************** + +.. option:: --enable-incomplete-feature FEATURE + + Some features may require several mypy releases to implement, for example + due to their complexity, potential for backwards incompatibility, or + ambiguous semantics that would benefit from feedback from the community. + You can enable such features for early preview using this flag. Note that + it is not guaranteed that all features will be ultimately enabled by + default. In *rare cases* we may decide to not go ahead with certain + features. + +List of currently incomplete/experimental features: + +* ``PreciseTupleTypes``: this feature will infer more precise tuple types in + various scenarios. Before variadic types were added to the Python type system + by :pep:`646`, it was impossible to express a type like "a tuple with + at least two integers". The best type available was ``tuple[int, ...]``. + Therefore, mypy applied very lenient checking for variable-length tuples. + Now this type can be expressed as ``tuple[int, int, *tuple[int, ...]]``. + For such more precise types (when explicitly *defined* by a user) mypy, + for example, warns about unsafe index access, and generally handles them + in a type-safe manner. However, to avoid problems in existing code, mypy + does not *infer* these precise types when it technically can. Here are + notable examples where ``PreciseTupleTypes`` infers more precise types: + + .. code-block:: python + + numbers: tuple[int, ...] + + more_numbers = (1, *numbers, 1) + reveal_type(more_numbers) + # Without PreciseTupleTypes: tuple[int, ...] + # With PreciseTupleTypes: tuple[int, *tuple[int, ...], int] + + other_numbers = (1, 1) + numbers + reveal_type(other_numbers) + # Without PreciseTupleTypes: tuple[int, ...] + # With PreciseTupleTypes: tuple[int, int, *tuple[int, ...]] + + if len(numbers) > 2: + reveal_type(numbers) + # Without PreciseTupleTypes: tuple[int, ...] + # With PreciseTupleTypes: tuple[int, int, int, *tuple[int, ...]] + else: + reveal_type(numbers) + # Without PreciseTupleTypes: tuple[int, ...] + # With PreciseTupleTypes: tuple[()] | tuple[int] | tuple[int, int] + + Miscellaneous ************* diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index df6000050986..0207c245b1f9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -97,7 +97,7 @@ YieldExpr, YieldFromExpr, ) -from mypy.options import TYPE_VAR_TUPLE +from mypy.options import PRECISE_TUPLE_TYPES from mypy.plugin import ( FunctionContext, FunctionSigContext, @@ -3377,7 +3377,7 @@ def visit_op_expr(self, e: OpExpr) -> Type: ): return self.concat_tuples(proper_left_type, proper_right_type) elif ( - TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature + PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature and isinstance(proper_right_type, Instance) and self.chk.type_is_iterable(proper_right_type) ): @@ -3411,7 +3411,7 @@ def visit_op_expr(self, e: OpExpr) -> Type: if is_named_instance(proper_right_type, "builtins.dict"): use_reverse = USE_REVERSE_NEVER - if TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature: + if PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature: # Handle tuple[X, ...] + tuple[Y, Z] = tuple[*tuple[X, ...], Y, Z]. if ( e.op == "+" @@ -4988,7 +4988,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: j += len(tt.items) else: if ( - TYPE_VAR_TUPLE in self.chk.options.enable_incomplete_feature + PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature and not seen_unpack_in_items ): # Handle (x, *y, z), where y is e.g. tuple[Y, ...]. diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index eeb9e7633756..39d44e84a9c1 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -47,8 +47,11 @@ TupleType, Type, TypeOfAny, + TypeVarTupleType, TypeVarType, UnionType, + UnpackType, + find_unpack_in_list, get_proper_type, get_proper_types, ) @@ -728,6 +731,22 @@ def check_simple_str_interpolation( rep_types: list[Type] = [] if isinstance(rhs_type, TupleType): rep_types = rhs_type.items + unpack_index = find_unpack_in_list(rep_types) + if unpack_index is not None: + # TODO: we should probably warn about potentially short tuple. + # However, without special-casing for tuple(f(i) for in other_tuple) + # this causes false positive on mypy self-check in report.py. + extras = max(0, len(checkers) - len(rep_types) + 1) + unpacked = rep_types[unpack_index] + assert isinstance(unpacked, UnpackType) + unpacked = get_proper_type(unpacked.type) + if isinstance(unpacked, TypeVarTupleType): + unpacked = get_proper_type(unpacked.upper_bound) + assert ( + isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple" + ) + unpack_items = [unpacked.args[0]] * extras + rep_types = rep_types[:unpack_index] + unpack_items + rep_types[unpack_index + 1 :] elif isinstance(rhs_type, AnyType): return elif isinstance(rhs_type, Instance) and rhs_type.type.fullname == "builtins.tuple": diff --git a/mypy/main.py b/mypy/main.py index 43ab761072ca..1aede530c33e 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -22,7 +22,7 @@ from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path -from mypy.options import INCOMPLETE_FEATURES, BuildType, Options +from mypy.options import COMPLETE_FEATURES, INCOMPLETE_FEATURES, BuildType, Options from mypy.split_namespace import SplitNamespace from mypy.version import __version__ @@ -1151,10 +1151,7 @@ def add_invertible_flag( # --debug-serialize will run tree.serialize() even if cache generation is disabled. # Useful for mypy_primer to detect serialize errors earlier. parser.add_argument("--debug-serialize", action="store_true", help=argparse.SUPPRESS) - # This one is deprecated, but we will keep it for few releases. - parser.add_argument( - "--enable-incomplete-features", action="store_true", help=argparse.SUPPRESS - ) + parser.add_argument( "--disable-bytearray-promotion", action="store_true", help=argparse.SUPPRESS ) @@ -1334,14 +1331,10 @@ def set_strict_flags() -> None: # Validate incomplete features. for feature in options.enable_incomplete_feature: - if feature not in INCOMPLETE_FEATURES: + if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: parser.error(f"Unknown incomplete feature: {feature}") - if options.enable_incomplete_features: - print( - "Warning: --enable-incomplete-features is deprecated, use" - " --enable-incomplete-feature=FEATURE instead" - ) - options.enable_incomplete_feature = list(INCOMPLETE_FEATURES) + if feature in COMPLETE_FEATURES: + print(f"Warning: {feature} is already enabled by default") # Compute absolute path for custom typeshed (if present). if options.custom_typeshed_dir is not None: diff --git a/mypy/options.py b/mypy/options.py index 31d5d584f897..8bb20dbd4410 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -69,11 +69,12 @@ class BuildType: } ) - {"debug_cache"} -# Features that are currently incomplete/experimental +# Features that are currently (or were recently) incomplete/experimental TYPE_VAR_TUPLE: Final = "TypeVarTuple" UNPACK: Final = "Unpack" PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" -INCOMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK, PRECISE_TUPLE_TYPES)) +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES,)) +COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) class Options: @@ -307,7 +308,6 @@ def __init__(self) -> None: self.dump_type_stats = False self.dump_inference_stats = False self.dump_build_stats = False - self.enable_incomplete_features = False # deprecated self.enable_incomplete_feature: list[str] = [] self.timing_stats: str | None = None self.line_checking_stats: str | None = None diff --git a/mypy/semanal.py b/mypy/semanal.py index bd24c48ed24f..6f322af816ea 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -179,7 +179,7 @@ type_aliases_source_versions, typing_extensions_aliases, ) -from mypy.options import TYPE_VAR_TUPLE, Options +from mypy.options import Options from mypy.patterns import ( AsPattern, ClassPattern, @@ -4417,9 +4417,6 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: else: self.fail(f'Unexpected keyword argument "{param_name}" for "TypeVarTuple"', s) - if not self.incomplete_feature_enabled(TYPE_VAR_TUPLE, s): - return False - name = self.extract_typevarlike_name(s, call) if name is None: return False diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 591421465a97..3ad97ced61f2 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -10,7 +10,6 @@ from mypy.build import Graph from mypy.errors import CompileError from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths -from mypy.options import TYPE_VAR_TUPLE, UNPACK from mypy.test.config import test_data_prefix, test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite, FileOperation, module_from_path from mypy.test.helpers import ( @@ -125,8 +124,6 @@ def run_case_once( # Parse options after moving files (in case mypy.ini is being moved). options = parse_options(original_program_text, testcase, incremental_step) options.use_builtins_fixtures = True - if not testcase.name.endswith("_no_incomplete"): - options.enable_incomplete_feature += [TYPE_VAR_TUPLE, UNPACK] options.show_traceback = True # Enable some options automatically based on test file name. diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index c517c54286d7..953f91a60df7 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -28,7 +28,7 @@ from mypy.errors import CompileError from mypy.find_sources import create_source_list from mypy.modulefinder import BuildSource -from mypy.options import TYPE_VAR_TUPLE, UNPACK, Options +from mypy.options import Options from mypy.server.mergecheck import check_consistency from mypy.server.update import sort_messages_preserving_file_order from mypy.test.config import test_temp_dir @@ -149,7 +149,6 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo options.use_fine_grained_cache = self.use_cache and not build_cache options.cache_fine_grained = self.use_cache options.local_partial_types = True - options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] # Treat empty bodies safely for these test cases. options.allow_empty_bodies = not testcase.name.endswith("_no_empty") if re.search("flags:.*--follow-imports", source) is None: diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index 3455f41aa20a..cdecc4739168 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -10,7 +10,7 @@ from mypy.errors import CompileError from mypy.modulefinder import BuildSource from mypy.nodes import TypeInfo -from mypy.options import TYPE_VAR_TUPLE, UNPACK, Options +from mypy.options import Options from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import ( @@ -45,7 +45,6 @@ def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Opti options.semantic_analysis_only = True options.show_traceback = True options.python_version = PYTHON3_VERSION - options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] options.force_uppercase_builtins = True return options diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index ba9fe8668fb4..9388dca02c7a 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -5,7 +5,6 @@ from mypy import build from mypy.errors import CompileError from mypy.modulefinder import BuildSource -from mypy.options import TYPE_VAR_TUPLE, UNPACK from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal, normalize_error_messages, parse_options @@ -38,7 +37,6 @@ def test_transform(testcase: DataDrivenTestCase) -> None: options = parse_options(src, testcase, 1) options.use_builtins_fixtures = True 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( diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 03579404aac9..d238a452e7a9 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -35,7 +35,7 @@ check_arg_names, get_nongen_builtins, ) -from mypy.options import UNPACK, Options +from mypy.options import Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs from mypy.tvar_scope import TypeVarLikeScope @@ -664,8 +664,6 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ # In most contexts, TypeGuard[...] acts as an alias for bool (ignoring its args) return self.named_type("builtins.bool") elif fullname in ("typing.Unpack", "typing_extensions.Unpack"): - if not self.api.incomplete_feature_enabled(UNPACK, t): - return AnyType(TypeOfAny.from_error) if len(t.args) != 1: self.fail("Unpack[...] requires exactly one type argument", t) return AnyType(TypeOfAny.from_error) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 546d02a07ad0..04adaca317c1 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2190,18 +2190,6 @@ x: int = "" # E: Incompatible types in assignment (expression has type "str", v # flags: --hide-error-codes x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") -[case testTypeVarTupleDisabled_no_incomplete] -from typing_extensions import TypeVarTuple -Ts = TypeVarTuple("Ts") # E: "TypeVarTuple" support is experimental, use --enable-incomplete-feature=TypeVarTuple to enable -[builtins fixtures/tuple.pyi] - -[case testTypeVarTupleEnabled_no_incomplete] -# flags: --enable-incomplete-feature=TypeVarTuple -from typing_extensions import TypeVarTuple -Ts = TypeVarTuple("Ts") # OK -[builtins fixtures/tuple.pyi] - - [case testDisableBytearrayPromotion] # flags: --disable-bytearray-promotion def f(x: bytes) -> None: ... diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 7070ead43746..4f468b59fc3f 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1100,12 +1100,28 @@ reveal_type(b) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtin [case testTupleWithStarExpr2] a = [1] b = (0, *a) +reveal_type(b) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/tuple.pyi] + +[case testTupleWithStarExpr2Precise] +# flags: --enable-incomplete-feature=PreciseTupleTypes +a = [1] +b = (0, *a) reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr3] a = [''] b = (0, *a) +reveal_type(b) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +c = (*a, '') +reveal_type(c) # N: Revealed type is "builtins.tuple[builtins.str, ...]" +[builtins fixtures/tuple.pyi] + +[case testTupleWithStarExpr3Precise] +# flags: --enable-incomplete-feature=PreciseTupleTypes +a = [''] +b = (0, *a) reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" c = (*a, '') reveal_type(c) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]" diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 7b8a22313b36..a51b535a873c 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1653,6 +1653,7 @@ def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: [builtins fixtures/tuple.pyi] [case testPackingVariadicTuplesHomogeneous] +# flags: --enable-incomplete-feature=PreciseTupleTypes from typing import Tuple from typing_extensions import Unpack @@ -1689,6 +1690,7 @@ def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: [builtins fixtures/isinstancelist.pyi] [case testVariadicTupleInTupleContext] +# flags: --enable-incomplete-feature=PreciseTupleTypes from typing import Tuple, Optional from typing_extensions import TypeVarTuple, Unpack @@ -1701,6 +1703,7 @@ vt2 = 1, *test(), 2 # E: Need type annotation for "vt2" [builtins fixtures/tuple.pyi] [case testVariadicTupleConcatenation] +# flags: --enable-incomplete-feature=PreciseTupleTypes from typing import Tuple from typing_extensions import TypeVarTuple, Unpack diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 91242eb62fcf..f286f4781ed5 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1421,14 +1421,6 @@ b \d+ b\.c \d+ .* -[case testCmdlineEnableIncompleteFeatures] -# cmd: mypy --enable-incomplete-features a.py -[file a.py] -pass -[out] -Warning: --enable-incomplete-features is deprecated, use --enable-incomplete-feature=FEATURE instead -== Return code: 0 - [case testShadowTypingModuleEarlyLoad] # cmd: mypy dir [file dir/__init__.py] @@ -1585,3 +1577,13 @@ disable_error_code = always_true = MY_VAR, [out] + +[case testTypeVarTupleUnpackEnabled] +# cmd: mypy --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack a.py +[file a.py] +from typing_extensions import TypeVarTuple +Ts = TypeVarTuple("Ts") +[out] +Warning: TypeVarTuple is already enabled by default +Warning: Unpack is already enabled by default +== Return code: 0 From 5624f401b3786ebdbe167c27297ed778cce3faa5 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 30 Oct 2023 14:27:43 +0000 Subject: [PATCH 0357/1617] Fix daemon crash caused by deleted submodule (#16370) If a submodule has been deleted while using a fine-grained cache, the daemon could crash during fixup, since there could be a symbol table entry in a parent package that would appear to refer to itself. Handle the case by adding a placeholder symbol table entry instead. Eventually the parent package will be reprocessed and the symbol table will be completed. --- mypy/fixup.py | 19 +++++++++++++++++-- mypy/nodes.py | 2 ++ test-data/unit/fine-grained.test | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index 5ffc47120734..02c6ab93f29e 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -128,8 +128,23 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: cross_ref, self.modules, raise_on_missing=not self.allow_missing ) if stnode is not None: - assert stnode.node is not None, (table_fullname + "." + key, cross_ref) - value.node = stnode.node + if stnode is value: + # The node seems to refer to itself, which can mean that + # the target is a deleted submodule of the current module, + # and thus lookup falls back to the symbol table of the parent + # package. Here's how this may happen: + # + # pkg/__init__.py: + # from pkg import sub + # + # Now if pkg.sub is deleted, the pkg.sub symbol table entry + # appears to refer to itself. Replace the entry with a + # placeholder to avoid a crash. We can't delete the entry, + # as it would stop dependency propagation. + value.node = Var(key + "@deleted") + else: + assert stnode.node is not None, (table_fullname + "." + key, cross_ref) + value.node = stnode.node elif not self.allow_missing: assert False, f"Could not find cross-ref {cross_ref}" else: diff --git a/mypy/nodes.py b/mypy/nodes.py index 1d7b3e3be84b..d65a23a6b7fe 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3824,6 +3824,8 @@ def __str__(self) -> str: # Include declared type of variables and functions. if self.type is not None: s += f" : {self.type}" + if self.cross_ref: + s += f" cross_ref:{self.cross_ref}" return s def serialize(self, prefix: str, name: str) -> JsonDict: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 5dc42bd62d9b..165a2089b466 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10486,3 +10486,22 @@ reveal_type(s) == == b.py:2: note: Revealed type is "builtins.str" + +[case testRenameSubModule] +import a + +[file a.py] +import pkg.sub + +[file pkg/__init__.py] +[file pkg/sub/__init__.py] +from pkg.sub import mod +[file pkg/sub/mod.py] + +[file pkg/sub/__init__.py.2] +from pkg.sub import modb +[delete pkg/sub/mod.py.2] +[file pkg/sub/modb.py.2] + +[out] +== From 128176ad1150cebd4b4e20ff7f1ec3f9857d1754 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 30 Oct 2023 17:24:35 +0000 Subject: [PATCH 0358/1617] Bump version to 1.8.0+dev (#16372) --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 7cfc68d6e553..2c2c2b052da2 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.7.0+dev" +__version__ = "1.8.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 0ff7a29d5336dad6400a9356bd4116b59c20a875 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 30 Oct 2023 11:48:07 -0700 Subject: [PATCH 0359/1617] stubgen: include __all__ in output (#16356) Fixes #10314 --- CHANGELOG.md | 2 +- mypy/stubutil.py | 56 ++++++++++++++++++++++++++----------- test-data/unit/stubgen.test | 40 +++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8237795112b..74f7c676c279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -... +Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). #### Other Notable Changes and Fixes ... diff --git a/mypy/stubutil.py b/mypy/stubutil.py index cc3b63098fd2..5ec240087145 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -614,10 +614,24 @@ def get_imports(self) -> str: def output(self) -> str: """Return the text for the stub.""" - imports = self.get_imports() - if imports and self._output: - imports += "\n" - return imports + "".join(self._output) + pieces: list[str] = [] + if imports := self.get_imports(): + pieces.append(imports) + if dunder_all := self.get_dunder_all(): + pieces.append(dunder_all) + if self._output: + pieces.append("".join(self._output)) + return "\n".join(pieces) + + def get_dunder_all(self) -> str: + """Return the __all__ list for the stub.""" + if self._all_: + # Note we emit all names in the runtime __all__ here, even if they + # don't actually exist. If that happens, the runtime has a bug, and + # it's not obvious what the correct behavior should be. We choose + # to reflect the runtime __all__ as closely as possible. + return f"__all__ = {self._all_!r}\n" + return "" def add(self, string: str) -> None: """Add text to generated stub.""" @@ -651,8 +665,7 @@ def set_defined_names(self, defined_names: set[str]) -> None: self.defined_names = defined_names # Names in __all__ are required for name in self._all_ or (): - if name not in self.IGNORED_DUNDERS: - self.import_tracker.reexport(name) + self.import_tracker.reexport(name) # These are "soft" imports for objects which might appear in annotations but not have # a corresponding import statement. @@ -751,7 +764,13 @@ def is_private_name(self, name: str, fullname: str | None = None) -> bool: return False if name == "_": return False - return name.startswith("_") and (not name.endswith("__") or name in self.IGNORED_DUNDERS) + if not name.startswith("_"): + return False + if self._all_ and name in self._all_: + return False + if name.startswith("__") and name.endswith("__"): + return name in self.IGNORED_DUNDERS + return True def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> bool: if ( @@ -761,18 +780,21 @@ def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> b ): # Special case certain names that should be exported, against our general rules. return True + if name_is_alias: + return False + if self.export_less: + return False + if not self.module_name: + return False is_private = self.is_private_name(name, full_module + "." + name) + if is_private: + return False top_level = full_module.split(".")[0] self_top_level = self.module_name.split(".", 1)[0] - if ( - not name_is_alias - and not self.export_less - and (not self._all_ or name in self.IGNORED_DUNDERS) - and self.module_name - and not is_private - and top_level in (self_top_level, "_" + self_top_level) - ): + if top_level not in (self_top_level, "_" + self_top_level): # Export imports from the same package, since we can't reliably tell whether they # are part of the public API. - return True - return False + return False + if self._all_: + return name in self._all_ + return True diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 895500c1ba57..2a43ce16383d 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -587,6 +587,8 @@ __all__ = [] + ['f'] def f(): ... def g(): ... [out] +__all__ = ['f'] + def f() -> None: ... [case testOmitDefsNotInAll_semanal] @@ -594,6 +596,8 @@ __all__ = ['f'] def f(): ... def g(): ... [out] +__all__ = ['f'] + def f() -> None: ... [case testOmitDefsNotInAll_inspect] @@ -601,6 +605,8 @@ __all__ = [] + ['f'] def f(): ... def g(): ... [out] +__all__ = ['f'] + def f(): ... [case testVarDefsNotInAll_import] @@ -610,6 +616,8 @@ x = 1 y = 1 def g(): ... [out] +__all__ = ['f', 'g'] + def f() -> None: ... def g() -> None: ... @@ -620,6 +628,8 @@ x = 1 y = 1 def g(): ... [out] +__all__ = ['f', 'g'] + def f(): ... def g(): ... @@ -628,6 +638,8 @@ __all__ = [] + ['f'] def f(): ... class A: ... [out] +__all__ = ['f'] + def f() -> None: ... class A: ... @@ -637,6 +649,8 @@ __all__ = [] + ['f'] def f(): ... class A: ... [out] +__all__ = ['f'] + def f(): ... class A: ... @@ -647,6 +661,8 @@ class A: x = 1 def f(self): ... [out] +__all__ = ['A'] + class A: x: int def f(self) -> None: ... @@ -684,6 +700,8 @@ x = 1 [out] from re import match as match, sub as sub +__all__ = ['match', 'sub', 'x'] + x: int [case testExportModule_import] @@ -694,6 +712,8 @@ y = 2 [out] import re as re +__all__ = ['re', 'x'] + x: int [case testExportModule2_import] @@ -704,6 +724,8 @@ y = 2 [out] import re as re +__all__ = ['re', 'x'] + x: int [case testExportModuleAs_import] @@ -714,6 +736,8 @@ y = 2 [out] import re as rex +__all__ = ['rex', 'x'] + x: int [case testExportModuleInPackage_import] @@ -722,6 +746,8 @@ __all__ = ['p'] [out] import urllib.parse as p +__all__ = ['p'] + [case testExportPackageOfAModule_import] import urllib.parse __all__ = ['urllib'] @@ -729,6 +755,8 @@ __all__ = ['urllib'] [out] import urllib as urllib +__all__ = ['urllib'] + [case testRelativeImportAll] from .x import * [out] @@ -741,6 +769,8 @@ x = 1 class C: def g(self): ... [out] +__all__ = ['f', 'x', 'C', 'g'] + def f() -> None: ... x: int @@ -758,6 +788,8 @@ x = 1 class C: def g(self): ... [out] +__all__ = ['f', 'x', 'C', 'g'] + def f(): ... x: int @@ -2343,6 +2375,8 @@ else: [out] import cookielib as cookielib +__all__ = ['cookielib'] + [case testCannotCalculateMRO_semanal] class X: pass @@ -2788,6 +2822,8 @@ class A: pass # p/__init__.pyi from p.a import A +__all__ = ['a'] + a: A # p/a.pyi class A: ... @@ -2961,7 +2997,9 @@ __uri__ = '' __version__ = '' [out] -from m import __version__ as __version__ +from m import __about__ as __about__, __author__ as __author__, __version__ as __version__ + +__all__ = ['__about__', '__author__', '__version__'] [case testAttrsClass_semanal] import attrs From 4291b2c07f8dd862c6656d51b8267e7fc84ad1dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:24:30 -0700 Subject: [PATCH 0360/1617] Sync typeshed (#16382) Source commit: https://github.com/python/typeshed/commit/3c872ca8fd875f2dc5fe5f5d771e35c58390cd0e Co-authored-by: mypybot <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/VERSIONS | 6 + mypy/typeshed/stdlib/_collections_abc.pyi | 2 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 2 +- mypy/typeshed/stdlib/_weakrefset.pyi | 4 +- mypy/typeshed/stdlib/_winapi.pyi | 2 +- mypy/typeshed/stdlib/array.pyi | 4 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 24 ++-- mypy/typeshed/stdlib/collections/__init__.pyi | 8 +- mypy/typeshed/stdlib/contextlib.pyi | 2 +- mypy/typeshed/stdlib/csv.pyi | 2 +- mypy/typeshed/stdlib/fileinput.pyi | 4 +- mypy/typeshed/stdlib/hashlib.pyi | 9 +- mypy/typeshed/stdlib/importlib/abc.pyi | 88 +++----------- mypy/typeshed/stdlib/importlib/readers.pyi | 68 +++++++++++ .../stdlib/importlib/resources/readers.pyi | 14 +++ .../stdlib/importlib/resources/simple.pyi | 49 ++++++++ mypy/typeshed/stdlib/importlib/simple.pyi | 11 ++ mypy/typeshed/stdlib/inspect.pyi | 8 ++ mypy/typeshed/stdlib/io.pyi | 10 +- mypy/typeshed/stdlib/itertools.pyi | 30 ++--- mypy/typeshed/stdlib/nt.pyi | 111 ++++++++++++++++++ .../stdlib/{sys.pyi => sys/__init__.pyi} | 4 + mypy/typeshed/stdlib/sys/_monitoring.pyi | 52 ++++++++ mypy/typeshed/stdlib/tempfile.pyi | 2 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 84 +++++++++++-- mypy/typeshed/stdlib/types.pyi | 4 +- mypy/typeshed/stdlib/typing.pyi | 10 +- mypy/typeshed/stdlib/weakref.pyi | 2 +- 29 files changed, 476 insertions(+), 144 deletions(-) create mode 100644 mypy/typeshed/stdlib/importlib/readers.pyi create mode 100644 mypy/typeshed/stdlib/importlib/resources/readers.pyi create mode 100644 mypy/typeshed/stdlib/importlib/resources/simple.pyi create mode 100644 mypy/typeshed/stdlib/importlib/simple.pyi create mode 100644 mypy/typeshed/stdlib/nt.pyi rename mypy/typeshed/stdlib/{sys.pyi => sys/__init__.pyi} (99%) create mode 100644 mypy/typeshed/stdlib/sys/_monitoring.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 9d4636a29a1d..d24e85c8fe44 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -152,8 +152,12 @@ imp: 2.7-3.11 importlib: 2.7- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- +importlib.readers: 3.10- importlib.resources: 3.7- importlib.resources.abc: 3.11- +importlib.resources.readers: 3.11- +importlib.resources.simple: 3.11- +importlib.simple: 3.11- inspect: 2.7- io: 2.7- ipaddress: 3.3- @@ -181,6 +185,7 @@ multiprocessing.shared_memory: 3.8- netrc: 2.7- nis: 2.7- nntplib: 2.7- +nt: 2.7- ntpath: 2.7- nturl2path: 2.7- numbers: 2.7- @@ -250,6 +255,7 @@ sunau: 2.7- symbol: 2.7-3.9 symtable: 2.7- sys: 2.7- +sys._monitoring: 3.12- # Doesn't actually exist. See comments in the stub. sysconfig: 2.7- syslog: 2.7- tabnanny: 2.7- diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 2b57f157a0e4..8520e9e4ed9b 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -81,7 +81,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @final -class dict_items(ItemsView[_KT_co, _VT_co], Generic[_KT_co, _VT_co]): # undocumented +class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 10): @property diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 8e92138c748a..ad214a2a5e0d 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -47,7 +47,7 @@ Unused: TypeAlias = object # _SentinelType = NewType("_SentinelType", object) # sentinel: _SentinelType # def foo(x: int | None | _SentinelType = ...) -> None: ... -sentinel = Any # noqa: Y026 +sentinel: Any # stable class IdentityFunction(Protocol): diff --git a/mypy/typeshed/stdlib/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi index d73d79155329..6482ade1271e 100644 --- a/mypy/typeshed/stdlib/_weakrefset.pyi +++ b/mypy/typeshed/stdlib/_weakrefset.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Iterable, Iterator, MutableSet -from typing import Any, Generic, TypeVar, overload +from typing import Any, TypeVar, overload from typing_extensions import Self if sys.version_info >= (3, 9): @@ -11,7 +11,7 @@ __all__ = ["WeakSet"] _S = TypeVar("_S") _T = TypeVar("_T") -class WeakSet(MutableSet[_T], Generic[_T]): +class WeakSet(MutableSet[_T]): @overload def __init__(self, data: None = None) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index e887fb38a7fa..1aec6ce50443 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -54,7 +54,7 @@ if sys.platform == "win32": HIGH_PRIORITY_CLASS: Literal[0x80] INFINITE: Literal[0xFFFFFFFF] if sys.version_info >= (3, 8): - # Ignore the flake8 error -- flake8-pyi assumes + # Ignore the Flake8 error -- flake8-pyi assumes # most numbers this long will be implementation details, # but here we can see that it's a power of 2 INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054 diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index b533f9240073..2ef821fcf87a 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Iterable # pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence -from typing import Any, Generic, MutableSequence, TypeVar, overload # noqa: Y022 +from typing import Any, MutableSequence, TypeVar, overload # noqa: Y022 from typing_extensions import Literal, Self, SupportsIndex, TypeAlias if sys.version_info >= (3, 12): @@ -18,7 +18,7 @@ _T = TypeVar("_T", int, float, str) typecodes: str -class array(MutableSequence[_T], Generic[_T]): +class array(MutableSequence[_T]): @property def typecode(self) -> _TypeCode: ... @property diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 366ac7fa35e3..cdac7d359781 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -2,7 +2,7 @@ import concurrent.futures import sys from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator from types import FrameType -from typing import Any, Generic, Protocol, TextIO, TypeVar, overload +from typing import Any, Protocol, TextIO, TypeVar, overload from typing_extensions import Literal, TypeAlias from . import _CoroutineLike @@ -379,7 +379,7 @@ else: # While this is true in general, here it's sort-of okay to have a covariant subclass, # since the only reason why `asyncio.Future` is invariant is the `set_result()` method, # and `asyncio.Task.set_result()` always raises. -class Task(Future[_T_co], Generic[_T_co]): # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues] +class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues] if sys.version_info >= (3, 12): def __init__( self, diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index dedd72933028..96a1d1e31b17 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -287,7 +287,7 @@ class int: def __pow__(self, __value: _PositiveInteger, __mod: None = None) -> int: ... @overload def __pow__(self, __value: _NegativeInteger, __mod: None = None) -> float: ... - # positive x -> int; negative x -> float + # positive __value -> int; negative __value -> float # return type must be Any as `int | float` causes too many false-positive errors @overload def __pow__(self, __value: int, __mod: None = None) -> Any: ... @@ -346,7 +346,7 @@ class float: def __divmod__(self, __value: float) -> tuple[float, float]: ... @overload def __pow__(self, __value: int, __mod: None = None) -> float: ... - # positive x -> float; negative x -> complex + # positive __value -> float; negative __value -> complex # return type must be Any as `float | complex` causes too many false-positive errors @overload def __pow__(self, __value: float, __mod: None = None) -> Any: ... @@ -860,7 +860,7 @@ class slice: __hash__: ClassVar[None] # type: ignore[assignment] def indices(self, __len: SupportsIndex) -> tuple[int, int, int]: ... -class tuple(Sequence[_T_co], Generic[_T_co]): +class tuple(Sequence[_T_co]): def __new__(cls, __iterable: Iterable[_T_co] = ...) -> Self: ... def __len__(self) -> int: ... def __contains__(self, __key: object) -> bool: ... @@ -912,7 +912,7 @@ class function: # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any. def __get__(self, __instance: object, __owner: type | None = None) -> Any: ... -class list(MutableSequence[_T], Generic[_T]): +class list(MutableSequence[_T]): @overload def __init__(self) -> None: ... @overload @@ -967,7 +967,7 @@ class list(MutableSequence[_T], Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... -class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): +class dict(MutableMapping[_KT, _VT]): # __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics # Also multiprocessing.managers.SyncManager.dict() @overload @@ -1040,7 +1040,7 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def __ior__(self, __value: Iterable[tuple[_KT, _VT]]) -> Self: ... -class set(MutableSet[_T], Generic[_T]): +class set(MutableSet[_T]): @overload def __init__(self) -> None: ... @overload @@ -1080,7 +1080,7 @@ class set(MutableSet[_T], Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... -class frozenset(AbstractSet[_T_co], Generic[_T_co]): +class frozenset(AbstractSet[_T_co]): @overload def __new__(cls) -> Self: ... @overload @@ -1109,7 +1109,7 @@ class frozenset(AbstractSet[_T_co], Generic[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... -class enumerate(Iterator[tuple[int, _T]], Generic[_T]): +class enumerate(Iterator[tuple[int, _T]]): def __new__(cls, iterable: Iterable[_T], start: int = ...) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... @@ -1318,7 +1318,7 @@ else: def exit(code: sys._ExitCode = None) -> NoReturn: ... -class filter(Iterator[_T], Generic[_T]): +class filter(Iterator[_T]): @overload def __new__(cls, __function: None, __iterable: Iterable[_T | None]) -> Self: ... @overload @@ -1377,7 +1377,7 @@ def len(__obj: Sized) -> int: ... def license() -> None: ... def locals() -> dict[str, Any]: ... -class map(Iterator[_S], Generic[_S]): +class map(Iterator[_S]): @overload def __new__(cls, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> Self: ... @overload @@ -1649,7 +1649,7 @@ else: def quit(code: sys._ExitCode = None) -> NoReturn: ... -class reversed(Iterator[_T], Generic[_T]): +class reversed(Iterator[_T]): @overload def __init__(self, __sequence: Reversible[_T]) -> None: ... @overload @@ -1723,7 +1723,7 @@ def vars(__object: type) -> types.MappingProxyType[str, Any]: ... # type: ignor @overload def vars(__object: Any = ...) -> dict[str, Any]: ... -class zip(Iterator[_T_co], Generic[_T_co]): +class zip(Iterator[_T_co]): if sys.version_info >= (3, 10): @overload def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 1d560117a54f..bb214b5ea19b 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -45,7 +45,7 @@ def namedtuple( defaults: Iterable[Any] | None = None, ) -> type[tuple[Any, ...]]: ... -class UserDict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): +class UserDict(MutableMapping[_KT, _VT]): data: dict[_KT, _VT] # __init__ should be kept roughly in line with `dict.__init__`, which has the same semantics @overload @@ -228,7 +228,7 @@ class UserString(Sequence[UserString]): def upper(self) -> Self: ... def zfill(self, width: int) -> Self: ... -class deque(MutableSequence[_T], Generic[_T]): +class deque(MutableSequence[_T]): @property def maxlen(self) -> int | None: ... @overload @@ -383,7 +383,7 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): @overload def __ror__(self, __value: dict[_T1, _T2]) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] -class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): +class defaultdict(dict[_KT, _VT]): default_factory: Callable[[], _VT] | None @overload def __init__(self) -> None: ... @@ -424,7 +424,7 @@ class defaultdict(dict[_KT, _VT], Generic[_KT, _VT]): @overload def __ror__(self, __value: dict[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] -class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): +class ChainMap(MutableMapping[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] def __init__(self, *maps: MutableMapping[_KT, _VT]) -> None: ... def new_child(self, m: MutableMapping[_KT, _VT] | None = None) -> Self: ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index dc2101dc01f7..c1bfedd2d1da 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -94,7 +94,7 @@ if sys.version_info >= (3, 10): ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co], Generic[_T_co]): + class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co]): def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 53425fbcccb1..f48d9d2ff263 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -23,7 +23,7 @@ from _csv import ( ) if sys.version_info >= (3, 12): - from _csv import QUOTE_STRINGS as QUOTE_STRINGS, QUOTE_NOTNULL as QUOTE_NOTNULL + from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence from typing import Any, Generic, TypeVar, overload diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index e9f3713b4eaf..c2fd31d1ea77 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath from collections.abc import Callable, Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Protocol, overload +from typing import IO, Any, AnyStr, Protocol, overload from typing_extensions import Literal, Self, TypeAlias if sys.version_info >= (3, 9): @@ -158,7 +158,7 @@ def fileno() -> int: ... def isfirstline() -> bool: ... def isstdin() -> bool: ... -class FileInput(Iterator[AnyStr], Generic[AnyStr]): +class FileInput(Iterator[AnyStr]): if sys.version_info >= (3, 10): # encoding and errors are added @overload diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 18b1ab549764..ed1321f23b9e 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -113,14 +113,7 @@ shake_128 = _VarLenHash shake_256 = _VarLenHash def scrypt( - password: ReadableBuffer, - *, - salt: ReadableBuffer | None = None, - n: int | None = None, - r: int | None = None, - p: int | None = None, - maxmem: int = 0, - dklen: int = 64, + password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 ) -> bytes: ... @final class _BlakeHash(_Hash): diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 28c33205a4df..438dbafb48c3 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -1,20 +1,12 @@ import _ast import sys import types -from _typeshed import ( - OpenBinaryMode, - OpenBinaryModeReading, - OpenBinaryModeUpdating, - OpenBinaryModeWriting, - OpenTextMode, - ReadableBuffer, - StrPath, -) +from _typeshed import ReadableBuffer, StrPath from abc import ABCMeta, abstractmethod from collections.abc import Iterator, Mapping, Sequence from importlib.machinery import ModuleSpec -from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper -from typing import IO, Any, BinaryIO, NoReturn, Protocol, overload, runtime_checkable +from io import BufferedReader +from typing import IO, Any, Protocol, overload, runtime_checkable from typing_extensions import Literal if sys.version_info >= (3, 11): @@ -139,72 +131,26 @@ if sys.version_info >= (3, 9): def joinpath(self, *descendants: str) -> Traversable: ... else: @abstractmethod - def joinpath(self, child: str) -> Traversable: ... - # The .open method comes from pathlib.pyi and should be kept in sync. - @overload - @abstractmethod - def open( - self, - mode: OpenTextMode = "r", - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - ) -> TextIOWrapper: ... - # Unbuffered binary mode: returns a FileIO - @overload - @abstractmethod - def open( - self, mode: OpenBinaryMode, buffering: Literal[0], encoding: None = None, errors: None = None, newline: None = None - ) -> FileIO: ... - # Buffering is on: return BufferedRandom, BufferedReader, or BufferedWriter - @overload - @abstractmethod - def open( - self, - mode: OpenBinaryModeUpdating, - buffering: Literal[-1, 1] = ..., - encoding: None = None, - errors: None = None, - newline: None = None, - ) -> BufferedRandom: ... - @overload - @abstractmethod - def open( - self, - mode: OpenBinaryModeWriting, - buffering: Literal[-1, 1] = ..., - encoding: None = None, - errors: None = None, - newline: None = None, - ) -> BufferedWriter: ... - @overload - @abstractmethod - def open( - self, - mode: OpenBinaryModeReading, - buffering: Literal[-1, 1] = ..., - encoding: None = None, - errors: None = None, - newline: None = None, - ) -> BufferedReader: ... - # Buffering cannot be determined: fall back to BinaryIO + def joinpath(self, __child: str) -> Traversable: ... + + # The documentation and runtime protocol allows *args, **kwargs arguments, + # but this would mean that all implementors would have to support them, + # which is not the case. @overload @abstractmethod - def open( - self, mode: OpenBinaryMode, buffering: int = ..., encoding: None = None, errors: None = None, newline: None = None - ) -> BinaryIO: ... - # Fallback if mode is not specified + def open(self, mode: Literal["r", "w"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open( - self, mode: str, buffering: int = ..., encoding: str | None = ..., errors: str | None = ..., newline: str | None = ... - ) -> IO[Any]: ... + def open(self, mode: Literal["rb", "wb"]) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... - @abstractmethod - def __truediv__(self, child: str) -> Traversable: ... + if sys.version_info >= (3, 10): + def __truediv__(self, __child: str) -> Traversable: ... + else: + @abstractmethod + def __truediv__(self, __child: str) -> Traversable: ... + @abstractmethod def read_bytes(self) -> bytes: ... @abstractmethod @@ -214,6 +160,6 @@ if sys.version_info >= (3, 9): @abstractmethod def files(self) -> Traversable: ... def open_resource(self, resource: str) -> BufferedReader: ... - def resource_path(self, resource: Any) -> NoReturn: ... + def resource_path(self, resource: Any) -> str: ... def is_resource(self, path: str) -> bool: ... def contents(self) -> Iterator[str]: ... diff --git a/mypy/typeshed/stdlib/importlib/readers.pyi b/mypy/typeshed/stdlib/importlib/readers.pyi new file mode 100644 index 000000000000..f34794601b59 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/readers.pyi @@ -0,0 +1,68 @@ +# On py311+, things are actually defined in importlib.resources.readers, +# and re-exported here, +# but doing it this way leads to less code duplication for us + +import pathlib +import sys +import zipfile +from _typeshed import Incomplete, StrPath +from collections.abc import Iterable, Iterator +from io import BufferedReader +from typing import NoReturn, TypeVar +from typing_extensions import Literal, Never + +if sys.version_info >= (3, 11): + import importlib.resources.abc as abc +else: + import importlib.abc as abc + +if sys.version_info >= (3, 10): + if sys.version_info >= (3, 11): + __all__ = ["FileReader", "ZipReader", "MultiplexedPath", "NamespaceReader"] + + if sys.version_info < (3, 11): + _T = TypeVar("_T") + + def remove_duplicates(items: Iterable[_T]) -> Iterator[_T]: ... + + class FileReader(abc.TraversableResources): + path: pathlib.Path + def __init__(self, loader) -> None: ... + def resource_path(self, resource: StrPath) -> str: ... + def files(self) -> pathlib.Path: ... + + class ZipReader(abc.TraversableResources): + prefix: str + archive: Incomplete + def __init__(self, loader, module: str) -> None: ... + def open_resource(self, resource: str) -> BufferedReader: ... + def is_resource(self, path: StrPath) -> bool: ... + def files(self) -> zipfile.Path: ... + + class MultiplexedPath(abc.Traversable): + def __init__(self, *paths: abc.Traversable) -> None: ... + def iterdir(self) -> Iterator[abc.Traversable]: ... + def read_bytes(self) -> NoReturn: ... + def read_text(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override] + def is_dir(self) -> Literal[True]: ... + def is_file(self) -> Literal[False]: ... + + if sys.version_info >= (3, 12): + def joinpath(self, *descendants: str) -> abc.Traversable: ... + elif sys.version_info >= (3, 11): + def joinpath(self, child: str) -> abc.Traversable: ... # type: ignore[override] + else: + def joinpath(self, child: str) -> abc.Traversable: ... + + if sys.version_info < (3, 12): + __truediv__ = joinpath + + def open(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override] + @property + def name(self) -> str: ... + + class NamespaceReader(abc.TraversableResources): + path: MultiplexedPath + def __init__(self, namespace_path) -> None: ... + def resource_path(self, resource: str) -> str: ... + def files(self) -> MultiplexedPath: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/readers.pyi b/mypy/typeshed/stdlib/importlib/resources/readers.pyi new file mode 100644 index 000000000000..0ab21fd29114 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/readers.pyi @@ -0,0 +1,14 @@ +# On py311+, things are actually defined here +# and re-exported from importlib.readers, +# but doing it this way leads to less code duplication for us + +import sys +from collections.abc import Iterable, Iterator +from typing import TypeVar + +if sys.version_info >= (3, 11): + from importlib.readers import * + + _T = TypeVar("_T") + + def remove_duplicates(items: Iterable[_T]) -> Iterator[_T]: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/simple.pyi b/mypy/typeshed/stdlib/importlib/resources/simple.pyi new file mode 100644 index 000000000000..9502375d00a2 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/simple.pyi @@ -0,0 +1,49 @@ +import abc +import sys +from _typeshed import Incomplete, OpenBinaryMode, OpenTextMode, Unused +from collections.abc import Iterator +from io import TextIOWrapper +from typing import IO, Any, BinaryIO, NoReturn, overload +from typing_extensions import Literal, Never + +if sys.version_info >= (3, 11): + from .abc import Traversable, TraversableResources + + class SimpleReader(abc.ABC): + @property + @abc.abstractmethod + def package(self) -> str: ... + @abc.abstractmethod + def children(self) -> list[SimpleReader]: ... + @abc.abstractmethod + def resources(self) -> list[str]: ... + @abc.abstractmethod + def open_binary(self, resource: str) -> BinaryIO: ... + @property + def name(self) -> str: ... + + class ResourceHandle(Traversable, metaclass=abc.ABCMeta): + parent: ResourceContainer + def __init__(self, parent: ResourceContainer, name: str) -> None: ... + def is_file(self) -> Literal[True]: ... + def is_dir(self) -> Literal[False]: ... + @overload + def open(self, mode: OpenTextMode = "r", *args: Incomplete, **kwargs: Incomplete) -> TextIOWrapper: ... + @overload + def open(self, mode: OpenBinaryMode, *args: Unused, **kwargs: Unused) -> BinaryIO: ... + @overload + def open(self, mode: str, *args: Incomplete, **kwargs: Incomplete) -> IO[Any]: ... + def joinpath(self, name: Never) -> NoReturn: ... # type: ignore[override] + + class ResourceContainer(Traversable, metaclass=abc.ABCMeta): + reader: SimpleReader + def __init__(self, reader: SimpleReader) -> None: ... + def is_dir(self) -> Literal[True]: ... + def is_file(self) -> Literal[False]: ... + def iterdir(self) -> Iterator[ResourceHandle | ResourceContainer]: ... + def open(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override] + if sys.version_info < (3, 12): + def joinpath(self, *descendants: str) -> Traversable: ... + + class TraversableReader(TraversableResources, SimpleReader, metaclass=abc.ABCMeta): + def files(self) -> ResourceContainer: ... diff --git a/mypy/typeshed/stdlib/importlib/simple.pyi b/mypy/typeshed/stdlib/importlib/simple.pyi new file mode 100644 index 000000000000..58d8c6617082 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/simple.pyi @@ -0,0 +1,11 @@ +import sys + +if sys.version_info >= (3, 11): + from .resources.simple import ( + ResourceContainer as ResourceContainer, + ResourceHandle as ResourceHandle, + SimpleReader as SimpleReader, + TraversableReader as TraversableReader, + ) + + __all__ = ["SimpleReader", "ResourceHandle", "ResourceContainer", "TraversableReader"] diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 601d23e786ac..6498719df887 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -294,6 +294,14 @@ _SourceObjectType: TypeAlias = ( def findsource(object: _SourceObjectType) -> tuple[list[str], int]: ... def getabsfile(object: _SourceObjectType, _filename: str | None = None) -> str: ... + +# Special-case the two most common input types here +# to avoid the annoyingly vague `Sequence[str]` return type +@overload +def getblock(lines: list[str]) -> list[str]: ... +@overload +def getblock(lines: tuple[str, ...]) -> tuple[str, ...]: ... +@overload def getblock(lines: Sequence[str]) -> Sequence[str]: ... def getdoc(object: object) -> str | None: ... def getcomments(object: object) -> str | None: ... diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index c114f839594f..b54e0a9fd05b 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -6,7 +6,7 @@ from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer from collections.abc import Callable, Iterable, Iterator from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, TextIO +from typing import IO, Any, BinaryIO, TextIO, TypeVar, overload from typing_extensions import Literal, Self __all__ = [ @@ -33,6 +33,8 @@ __all__ = [ if sys.version_info >= (3, 8): __all__ += ["open_code"] +_T = TypeVar("_T") + DEFAULT_BUFFER_SIZE: Literal[8192] SEEK_SET: Literal[0] @@ -194,3 +196,9 @@ class IncrementalNewlineDecoder(codecs.IncrementalDecoder): @property def newlines(self) -> str | tuple[str, ...] | None: ... def setstate(self, __state: tuple[bytes, int]) -> None: ... + +if sys.version_info >= (3, 10): + @overload + def text_encoding(__encoding: None, __stacklevel: int = 2) -> Literal["locale", "utf-8"]: ... + @overload + def text_encoding(__encoding: _T, __stacklevel: int = 2) -> _T: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 4b5d624c78d7..ffa8e19391dd 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -23,7 +23,7 @@ _Predicate: TypeAlias = Callable[[_T], object] # Technically count can take anything that implements a number protocol and has an add method # but we can't enforce the add method -class count(Iterator[_N], Generic[_N]): +class count(Iterator[_N]): @overload def __new__(cls) -> count[int]: ... @overload @@ -33,12 +33,12 @@ class count(Iterator[_N], Generic[_N]): def __next__(self) -> _N: ... def __iter__(self) -> Self: ... -class cycle(Iterator[_T], Generic[_T]): +class cycle(Iterator[_T]): def __init__(self, __iterable: Iterable[_T]) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... -class repeat(Iterator[_T], Generic[_T]): +class repeat(Iterator[_T]): @overload def __init__(self, object: _T) -> None: ... @overload @@ -47,7 +47,7 @@ class repeat(Iterator[_T], Generic[_T]): def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... -class accumulate(Iterator[_T], Generic[_T]): +class accumulate(Iterator[_T]): if sys.version_info >= (3, 8): @overload def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... @@ -59,7 +59,7 @@ class accumulate(Iterator[_T], Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class chain(Iterator[_T], Generic[_T]): +class chain(Iterator[_T]): def __init__(self, *iterables: Iterable[_T]) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -69,17 +69,17 @@ class chain(Iterator[_T], Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... -class compress(Iterator[_T], Generic[_T]): +class compress(Iterator[_T]): def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class dropwhile(Iterator[_T], Generic[_T]): +class dropwhile(Iterator[_T]): def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class filterfalse(Iterator[_T], Generic[_T]): +class filterfalse(Iterator[_T]): def __init__(self, __predicate: _Predicate[_T] | None, __iterable: Iterable[_T]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -92,7 +92,7 @@ class groupby(Iterator[tuple[_T, Iterator[_S]]], Generic[_T, _S]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T, Iterator[_S]]: ... -class islice(Iterator[_T], Generic[_T]): +class islice(Iterator[_T]): @overload def __init__(self, __iterable: Iterable[_T], __stop: int | None) -> None: ... @overload @@ -100,19 +100,19 @@ class islice(Iterator[_T], Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class starmap(Iterator[_T], Generic[_T]): +class starmap(Iterator[_T]): def __init__(self, __function: Callable[..., _T], __iterable: Iterable[Iterable[Any]]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class takewhile(Iterator[_T], Generic[_T]): +class takewhile(Iterator[_T]): def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def tee(__iterable: Iterable[_T], __n: int = 2) -> tuple[Iterator[_T], ...]: ... -class zip_longest(Iterator[_T_co], Generic[_T_co]): +class zip_longest(Iterator[_T_co]): # one iterable (fillvalue doesn't matter) @overload def __new__(cls, __iter1: Iterable[_T1], *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... @@ -192,7 +192,7 @@ class zip_longest(Iterator[_T_co], Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class product(Iterator[_T_co], Generic[_T_co]): +class product(Iterator[_T_co]): @overload def __new__(cls, __iter1: Iterable[_T1]) -> product[tuple[_T1]]: ... @overload @@ -246,7 +246,7 @@ class permutations(Iterator[tuple[_T, ...]], Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T, ...]: ... -class combinations(Iterator[_T_co], Generic[_T_co]): +class combinations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... @overload @@ -266,7 +266,7 @@ class combinations_with_replacement(Iterator[tuple[_T, ...]], Generic[_T]): def __next__(self) -> tuple[_T, ...]: ... if sys.version_info >= (3, 10): - class pairwise(Iterator[_T_co], Generic[_T_co]): + class pairwise(Iterator[_T_co]): def __new__(cls, __iterable: Iterable[_T]) -> pairwise[tuple[_T, _T]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi new file mode 100644 index 000000000000..4066096f4c71 --- /dev/null +++ b/mypy/typeshed/stdlib/nt.pyi @@ -0,0 +1,111 @@ +import sys + +if sys.platform == "win32": + # Actually defined here and re-exported from os at runtime, + # but this leads to less code duplication + from os import ( + F_OK as F_OK, + O_APPEND as O_APPEND, + O_BINARY as O_BINARY, + O_CREAT as O_CREAT, + O_EXCL as O_EXCL, + O_NOINHERIT as O_NOINHERIT, + O_RANDOM as O_RANDOM, + O_RDONLY as O_RDONLY, + O_RDWR as O_RDWR, + O_SEQUENTIAL as O_SEQUENTIAL, + O_SHORT_LIVED as O_SHORT_LIVED, + O_TEMPORARY as O_TEMPORARY, + O_TEXT as O_TEXT, + O_TRUNC as O_TRUNC, + O_WRONLY as O_WRONLY, + P_DETACH as P_DETACH, + P_NOWAIT as P_NOWAIT, + P_NOWAITO as P_NOWAITO, + P_OVERLAY as P_OVERLAY, + P_WAIT as P_WAIT, + R_OK as R_OK, + TMP_MAX as TMP_MAX, + W_OK as W_OK, + X_OK as X_OK, + DirEntry as DirEntry, + abort as abort, + access as access, + chdir as chdir, + chmod as chmod, + close as close, + closerange as closerange, + cpu_count as cpu_count, + device_encoding as device_encoding, + dup as dup, + dup2 as dup2, + error as error, + execv as execv, + execve as execve, + fspath as fspath, + fstat as fstat, + fsync as fsync, + ftruncate as ftruncate, + get_handle_inheritable as get_handle_inheritable, + get_inheritable as get_inheritable, + get_terminal_size as get_terminal_size, + getcwd as getcwd, + getcwdb as getcwdb, + getlogin as getlogin, + getpid as getpid, + getppid as getppid, + isatty as isatty, + kill as kill, + link as link, + listdir as listdir, + lseek as lseek, + lstat as lstat, + mkdir as mkdir, + open as open, + pipe as pipe, + putenv as putenv, + read as read, + readlink as readlink, + remove as remove, + rename as rename, + replace as replace, + rmdir as rmdir, + scandir as scandir, + set_handle_inheritable as set_handle_inheritable, + set_inheritable as set_inheritable, + spawnv as spawnv, + spawnve as spawnve, + startfile as startfile, + stat as stat, + stat_result as stat_result, + statvfs_result as statvfs_result, + strerror as strerror, + symlink as symlink, + system as system, + terminal_size as terminal_size, + times as times, + times_result as times_result, + truncate as truncate, + umask as umask, + uname_result as uname_result, + unlink as unlink, + urandom as urandom, + utime as utime, + waitpid as waitpid, + write as write, + ) + + if sys.version_info >= (3, 9): + from os import unsetenv as unsetenv, waitstatus_to_exitcode as waitstatus_to_exitcode + if sys.version_info >= (3, 11): + from os import EX_OK as EX_OK + if sys.version_info >= (3, 12): + from os import ( + get_blocking as get_blocking, + listdrives as listdrives, + listmounts as listmounts, + listvolumes as listvolumes, + set_blocking as set_blocking, + ) + + environ: dict[str, str] diff --git a/mypy/typeshed/stdlib/sys.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi similarity index 99% rename from mypy/typeshed/stdlib/sys.pyi rename to mypy/typeshed/stdlib/sys/__init__.pyi index a5e819d53326..cf3b1bc47d75 100644 --- a/mypy/typeshed/stdlib/sys.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -370,3 +370,7 @@ if sys.version_info >= (3, 12): def activate_stack_trampoline(__backend: str) -> None: ... else: def activate_stack_trampoline(__backend: str) -> NoReturn: ... + + from . import _monitoring + + monitoring = _monitoring diff --git a/mypy/typeshed/stdlib/sys/_monitoring.pyi b/mypy/typeshed/stdlib/sys/_monitoring.pyi new file mode 100644 index 000000000000..40aeb9cb5bdb --- /dev/null +++ b/mypy/typeshed/stdlib/sys/_monitoring.pyi @@ -0,0 +1,52 @@ +# This py312+ module provides annotations for `sys.monitoring`. +# It's named `sys._monitoring` in typeshed, +# because trying to import `sys.monitoring` will fail at runtime! +# At runtime, `sys.monitoring` has the unique status +# of being a `types.ModuleType` instance that cannot be directly imported, +# and exists in the `sys`-module namespace despite `sys` not being a package. + +from collections.abc import Callable +from types import CodeType +from typing import Any + +DEBUGGER_ID: int +COVERAGE_ID: int +PROFILER_ID: int +OPTIMIZER_ID: int + +def use_tool_id(__tool_id: int, __name: str) -> None: ... +def free_tool_id(__tool_id: int) -> None: ... +def get_tool(__tool_id: int) -> str | None: ... + +events: _events + +class _events: + BRANCH: int + CALL: int + C_RAISE: int + C_RETURN: int + EXCEPTION_HANDLED: int + INSTRUCTION: int + JUMP: int + LINE: int + NO_EVENTS: int + PY_RESUME: int + PY_RETURN: int + PY_START: int + PY_THROW: int + PY_UNWIND: int + PY_YIELD: int + RAISE: int + RERAISE: int + STOP_ITERATION: int + +def get_events(__tool_id: int) -> int: ... +def set_events(__tool_id: int, __event_set: int) -> None: ... +def get_local_events(__tool_id: int, __code: CodeType) -> int: ... +def set_local_events(__tool_id: int, __code: CodeType, __event_set: int) -> int: ... +def restart_events() -> None: ... + +DISABLE: object +MISSING: object + +def register_callback(__tool_id: int, __event: int, __func: Callable[..., Any] | None) -> Callable[..., Any] | None: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 61bcde24255b..f8dcb24c1daf 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -321,7 +321,7 @@ else: dir: GenericPath[AnyStr] | None = None, ) -> IO[Any]: ... -class _TemporaryFileWrapper(IO[AnyStr], Generic[AnyStr]): +class _TemporaryFileWrapper(IO[AnyStr]): file: IO[AnyStr] # io.TextIOWrapper, io.BufferedReader or io.BufferedWriter name: str delete: bool diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index a0a88a8ac82e..c35db3ef7e34 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -2888,7 +2888,7 @@ class Scrollbar(Widget): def fraction(self, x: int, y: int) -> float: ... def identify(self, x: int, y: int) -> Literal["arrow1", "arrow2", "slider", "trough1", "trough2", ""]: ... def get(self) -> tuple[float, float, float, float] | tuple[float, float]: ... - def set(self, first: float, last: float) -> None: ... + def set(self, first: float | str, last: float | str) -> None: ... _TextIndex: TypeAlias = _tkinter.Tcl_Obj | str | float | Misc @@ -3064,11 +3064,40 @@ class Text(Widget, XView, YView): def edit_separator(self) -> None: ... # actually returns empty string def edit_undo(self) -> None: ... # actually returns empty string def get(self, index1: _TextIndex, index2: _TextIndex | None = None) -> str: ... - # TODO: image_* methods - def image_cget(self, index, option): ... - def image_configure(self, index, cnf: Incomplete | None = None, **kw): ... - def image_create(self, index, cnf={}, **kw): ... - def image_names(self): ... + @overload + def image_cget(self, index: _TextIndex, option: Literal["image", "name"]) -> str: ... + @overload + def image_cget(self, index: _TextIndex, option: Literal["padx", "pady"]) -> int: ... + @overload + def image_cget(self, index: _TextIndex, option: Literal["align"]) -> Literal["baseline", "bottom", "center", "top"]: ... + @overload + def image_cget(self, index: _TextIndex, option: str) -> Any: ... + @overload + def image_configure(self, index: _TextIndex, cnf: str) -> tuple[str, str, str, str, str | int]: ... + @overload + def image_configure( + self, + index: _TextIndex, + cnf: dict[str, Any] | None = {}, + *, + align: Literal["baseline", "bottom", "center", "top"] = ..., + image: _ImageSpec = ..., + name: str = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + ) -> dict[str, tuple[str, str, str, str, str | int]] | None: ... + def image_create( + self, + index: _TextIndex, + cnf: dict[str, Any] | None = {}, + *, + align: Literal["baseline", "bottom", "center", "top"] = ..., + image: _ImageSpec = ..., + name: str = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + ) -> str: ... + def image_names(self) -> tuple[str, ...]: ... def index(self, index: _TextIndex) -> str: ... def insert(self, index: _TextIndex, chars: str, *args: str | list[str] | tuple[str, ...]) -> None: ... @overload @@ -3166,12 +3195,45 @@ class Text(Widget, XView, YView): def tag_ranges(self, tagName: str) -> tuple[_tkinter.Tcl_Obj, ...]: ... # tag_remove and tag_delete are different def tag_remove(self, tagName: str, index1: _TextIndex, index2: _TextIndex | None = None) -> None: ... - # TODO: window_* methods - def window_cget(self, index, option): ... - def window_configure(self, index, cnf: Incomplete | None = None, **kw): ... + @overload + def window_cget(self, index: _TextIndex, option: Literal["padx", "pady"]) -> int: ... + @overload + def window_cget(self, index: _TextIndex, option: Literal["stretch"]) -> bool: ... # actually returns Literal[0, 1] + @overload + def window_cget(self, index: _TextIndex, option: Literal["align"]) -> Literal["baseline", "bottom", "center", "top"]: ... + @overload # window is set to a widget, but read as the string name. + def window_cget(self, index: _TextIndex, option: Literal["create", "window"]) -> str: ... + @overload + def window_cget(self, index: _TextIndex, option: str) -> Any: ... + @overload + def window_configure(self, index: _TextIndex, cnf: str) -> tuple[str, str, str, str, str | int]: ... + @overload + def window_configure( + self, + index: _TextIndex, + cnf: dict[str, Any] | None = None, + *, + align: Literal["baseline", "bottom", "center", "top"] = ..., + create: str = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + stretch: bool | Literal[0, 1] = ..., + window: Misc | str = ..., + ) -> dict[str, tuple[str, str, str, str, str | int]] | None: ... window_config = window_configure - def window_create(self, index, cnf={}, **kw) -> None: ... - def window_names(self): ... + def window_create( + self, + index: _TextIndex, + cnf: dict[str, Any] | None = {}, + *, + align: Literal["baseline", "bottom", "center", "top"] = ..., + create: str = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + stretch: bool | Literal[0, 1] = ..., + window: Misc | str = ..., + ) -> None: ... + def window_names(self) -> tuple[str, ...]: ... def yview_pickplace(self, *what): ... # deprecated class _setit: diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 8559063834c9..a50bbf145b9f 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -16,7 +16,7 @@ from collections.abc import ( from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping -from typing import Any, ClassVar, Generic, Mapping, Protocol, TypeVar, overload # noqa: Y022 +from typing import Any, ClassVar, Mapping, Protocol, TypeVar, overload # noqa: Y022 from typing_extensions import Literal, ParamSpec, Self, TypeVarTuple, final __all__ = [ @@ -309,7 +309,7 @@ class CodeType: ) -> CodeType: ... @final -class MappingProxyType(Mapping[_KT, _VT_co], Generic[_KT, _VT_co]): +class MappingProxyType(Mapping[_KT, _VT_co]): __hash__: ClassVar[None] # type: ignore[assignment] def __new__(cls, mapping: SupportsKeysAndGetItem[_KT, _VT_co]) -> Self: ... def __getitem__(self, __key: _KT) -> _VT_co: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 6deb0ffd02b3..ad5719ca9f56 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -527,7 +527,7 @@ class Sequence(Collection[_T_co], Reversible[_T_co], Generic[_T_co]): def __iter__(self) -> Iterator[_T_co]: ... def __reversed__(self) -> Iterator[_T_co]: ... -class MutableSequence(Sequence[_T], Generic[_T]): +class MutableSequence(Sequence[_T]): @abstractmethod def insert(self, index: int, value: _T) -> None: ... @overload @@ -557,7 +557,7 @@ class MutableSequence(Sequence[_T], Generic[_T]): def remove(self, value: _T) -> None: ... def __iadd__(self, values: Iterable[_T]) -> typing_extensions.Self: ... -class AbstractSet(Collection[_T_co], Generic[_T_co]): +class AbstractSet(Collection[_T_co]): @abstractmethod def __contains__(self, x: object) -> bool: ... def _hash(self) -> int: ... @@ -573,7 +573,7 @@ class AbstractSet(Collection[_T_co], Generic[_T_co]): def __eq__(self, other: object) -> bool: ... def isdisjoint(self, other: Iterable[Any]) -> bool: ... -class MutableSet(AbstractSet[_T], Generic[_T]): +class MutableSet(AbstractSet[_T]): @abstractmethod def add(self, value: _T) -> None: ... @abstractmethod @@ -646,7 +646,7 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]): def __contains__(self, __key: object) -> bool: ... def __eq__(self, __other: object) -> bool: ... -class MutableMapping(Mapping[_KT, _VT], Generic[_KT, _VT]): +class MutableMapping(Mapping[_KT, _VT]): @abstractmethod def __setitem__(self, __key: _KT, __value: _VT) -> None: ... @abstractmethod @@ -703,7 +703,7 @@ TYPE_CHECKING: bool # In stubs, the arguments of the IO class are marked as positional-only. # This differs from runtime, but better reflects the fact that in reality # classes deriving from IO use different names for the arguments. -class IO(Iterator[AnyStr], Generic[AnyStr]): +class IO(Iterator[AnyStr]): # At runtime these are all abstract properties, # but making them abstract in the stub is hugely disruptive, for not much gain. # See #8726 diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index ca5366602ceb..ae88f3a317c1 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -40,7 +40,7 @@ _P = ParamSpec("_P") ProxyTypes: tuple[type[Any], ...] -class WeakMethod(ref[_CallableT], Generic[_CallableT]): +class WeakMethod(ref[_CallableT]): def __new__(cls, meth: _CallableT, callback: Callable[[Self], object] | None = None) -> Self: ... def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... From 14f79c1c6d499df94a0cff3ee627582c13a80a2b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 1 Nov 2023 09:27:53 +0000 Subject: [PATCH 0361/1617] Don't show docs links for plugin error codes (#16383) Fixes https://github.com/python/mypy/issues/16375 --- mypy/errorcodes.py | 3 +++ mypy/errors.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 98600679da53..c6e9de9f31c1 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -274,3 +274,6 @@ def __hash__(self) -> int: "General", sub_code_of=MISC, ) + +# This copy will not include any error codes defined later in the plugins. +mypy_error_codes = error_codes.copy() diff --git a/mypy/errors.py b/mypy/errors.py index 4e62a48aeb27..6e90c28d9c03 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -8,7 +8,7 @@ from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes -from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode +from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes from mypy.message_registry import ErrorMessage from mypy.options import Options from mypy.scope import Scope @@ -560,6 +560,7 @@ def add_error_info(self, info: ErrorInfo) -> None: and not self.options.hide_error_codes and info.code is not None and info.code not in HIDE_LINK_CODES + and info.code.code in mypy_error_codes ): message = f"See {BASE_RTD_URL}-{info.code.code} for more info" if message in self.only_once_messages: From 6a8365484e62b9f05817f04aec144d9b783442fe Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 1 Nov 2023 10:44:58 +0000 Subject: [PATCH 0362/1617] Fix crash on unpack call special-casing (#16381) Fixes https://github.com/python/mypy/issues/16380 Fix is quite straightforward, what was an `assert` really needs to be an `if`. --------- Co-authored-by: Jelle Zijlstra --- mypy/checkexpr.py | 38 +++++++++++-------------- test-data/unit/check-typevar-tuple.test | 22 ++++++++++++++ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0207c245b1f9..95700a52af02 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2440,34 +2440,28 @@ def check_argument_types( # the suffices to the tuple, e.g. a single actual like # Tuple[Unpack[Ts], int] expanded_tuple = False + actual_kinds = [arg_kinds[a] for a in actuals] if len(actuals) > 1: - first_actual_arg_type = get_proper_type(arg_types[actuals[0]]) + p_actual_type = get_proper_type(arg_types[actuals[0]]) if ( - isinstance(first_actual_arg_type, TupleType) - and len(first_actual_arg_type.items) == 1 - and isinstance(first_actual_arg_type.items[0], UnpackType) + isinstance(p_actual_type, TupleType) + and len(p_actual_type.items) == 1 + and isinstance(p_actual_type.items[0], UnpackType) + and actual_kinds == [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1) ): - # TODO: use walrus operator - actual_types = [first_actual_arg_type.items[0]] + [ - arg_types[a] for a in actuals[1:] - ] - actual_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1) - - # If we got here, the callee was previously inferred to have a suffix. - assert isinstance(orig_callee_arg_type, UnpackType) - assert isinstance(orig_callee_arg_type.type, ProperType) and isinstance( - orig_callee_arg_type.type, TupleType - ) - assert orig_callee_arg_type.type.items - callee_arg_types = orig_callee_arg_type.type.items - callee_arg_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * ( - len(orig_callee_arg_type.type.items) - 1 - ) - expanded_tuple = True + actual_types = [p_actual_type.items[0]] + [arg_types[a] for a in actuals[1:]] + if isinstance(orig_callee_arg_type, UnpackType): + p_callee_type = get_proper_type(orig_callee_arg_type.type) + if isinstance(p_callee_type, TupleType): + assert p_callee_type.items + callee_arg_types = p_callee_type.items + callee_arg_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * ( + len(p_callee_type.items) - 1 + ) + expanded_tuple = True if not expanded_tuple: actual_types = [arg_types[a] for a in actuals] - actual_kinds = [arg_kinds[a] for a in actuals] if isinstance(orig_callee_arg_type, UnpackType): unpacked_type = get_proper_type(orig_callee_arg_type.type) if isinstance(unpacked_type, TupleType): diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index a51b535a873c..e85863f0ed04 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2185,3 +2185,25 @@ def test2( # E: Missing named argument "b" return func(*args, **kwargs) [builtins fixtures/tuple.pyi] + +[case testUnpackTupleSpecialCaseNoCrash] +from typing import Tuple, TypeVar +from typing_extensions import Unpack + +T = TypeVar("T") + +def foo(*x: object) -> None: ... +def bar(*x: int) -> None: ... +def baz(*x: T) -> T: ... + +keys: Tuple[Unpack[Tuple[int, ...]]] + +foo(keys, 1) +foo(*keys, 1) + +bar(keys, 1) # E: Argument 1 to "bar" has incompatible type "Tuple[Unpack[Tuple[int, ...]]]"; expected "int" +bar(*keys, 1) # OK + +reveal_type(baz(keys, 1)) # N: Revealed type is "builtins.object" +reveal_type(baz(*keys, 1)) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] From 371219347a6d17e16924bbabf3e693c6874e7138 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 1 Nov 2023 15:33:45 +0000 Subject: [PATCH 0363/1617] Fix file reloading in dmypy with --export-types (#16359) Fixes https://github.com/python/mypy/issues/15794 Unfortunately, this requires to pass `--export-types` to `dmypy run` if one wants to inspect a file that was previously kicked out of the build. --- mypy/dmypy_server.py | 52 +++++++++++++++++++++++++++++++----- mypy/test/testfinegrained.py | 3 ++- test-data/unit/daemon.test | 27 +++++++++++++++++++ 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 0db349b5bf82..42236497f275 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -393,15 +393,21 @@ def cmd_recheck( t1 = time.time() manager = self.fine_grained_manager.manager manager.log(f"fine-grained increment: cmd_recheck: {t1 - t0:.3f}s") - self.options.export_types = export_types + old_export_types = self.options.export_types + self.options.export_types = self.options.export_types or export_types if not self.following_imports(): - messages = self.fine_grained_increment(sources, remove, update) + messages = self.fine_grained_increment( + sources, remove, update, explicit_export_types=export_types + ) else: assert remove is None and update is None - messages = self.fine_grained_increment_follow_imports(sources) + messages = self.fine_grained_increment_follow_imports( + sources, explicit_export_types=export_types + ) res = self.increment_output(messages, sources, is_tty, terminal_width) self.flush_caches() self.update_stats(res) + self.options.export_types = old_export_types return res def check( @@ -412,17 +418,21 @@ def check( If is_tty is True format the output nicely with colors and summary line (unless disabled in self.options). Also pass the terminal_width to formatter. """ - self.options.export_types = export_types + old_export_types = self.options.export_types + self.options.export_types = self.options.export_types or export_types if not self.fine_grained_manager: res = self.initialize_fine_grained(sources, is_tty, terminal_width) else: if not self.following_imports(): - messages = self.fine_grained_increment(sources) + messages = self.fine_grained_increment(sources, explicit_export_types=export_types) else: - messages = self.fine_grained_increment_follow_imports(sources) + messages = self.fine_grained_increment_follow_imports( + sources, explicit_export_types=export_types + ) res = self.increment_output(messages, sources, is_tty, terminal_width) self.flush_caches() self.update_stats(res) + self.options.export_types = old_export_types return res def flush_caches(self) -> None: @@ -535,6 +545,7 @@ def fine_grained_increment( sources: list[BuildSource], remove: list[str] | None = None, update: list[str] | None = None, + explicit_export_types: bool = False, ) -> list[str]: """Perform a fine-grained type checking increment. @@ -545,6 +556,8 @@ def fine_grained_increment( sources: sources passed on the command line remove: paths of files that have been removed update: paths of files that have been changed or created + explicit_export_types: --export-type was passed in a check command + (as opposite to being set in dmypy start) """ assert self.fine_grained_manager is not None manager = self.fine_grained_manager.manager @@ -559,6 +572,10 @@ def fine_grained_increment( # Use the remove/update lists to update fswatcher. # This avoids calling stat() for unchanged files. changed, removed = self.update_changed(sources, remove or [], update or []) + if explicit_export_types: + # If --export-types is given, we need to force full re-checking of all + # explicitly passed files, since we need to visit each expression. + add_all_sources_to_changed(sources, changed) changed += self.find_added_suppressed( self.fine_grained_manager.graph, set(), manager.search_paths ) @@ -577,7 +594,9 @@ def fine_grained_increment( self.previous_sources = sources return messages - def fine_grained_increment_follow_imports(self, sources: list[BuildSource]) -> list[str]: + def fine_grained_increment_follow_imports( + self, sources: list[BuildSource], explicit_export_types: bool = False + ) -> list[str]: """Like fine_grained_increment, but follow imports.""" t0 = time.time() @@ -603,6 +622,9 @@ def fine_grained_increment_follow_imports(self, sources: list[BuildSource]) -> l changed, new_files = self.find_reachable_changed_modules( sources, graph, seen, changed_paths ) + if explicit_export_types: + # Same as in fine_grained_increment(). + add_all_sources_to_changed(sources, changed) sources.extend(new_files) # Process changes directly reachable from roots. @@ -1011,6 +1033,22 @@ def find_all_sources_in_build( return result +def add_all_sources_to_changed(sources: list[BuildSource], changed: list[tuple[str, str]]) -> None: + """Add all (explicit) sources to the list changed files in place. + + Use this when re-processing of unchanged files is needed (e.g. for + the purpose of exporting types for inspections). + """ + changed_set = set(changed) + changed.extend( + [ + (bs.module, bs.path) + for bs in sources + if bs.path and (bs.module, bs.path) not in changed_set + ] + ) + + def fix_module_deps(graph: mypy.build.Graph) -> None: """After an incremental update, update module dependencies to reflect the new state. diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 953f91a60df7..f61a58c425fc 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -149,6 +149,7 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo options.use_fine_grained_cache = self.use_cache and not build_cache options.cache_fine_grained = self.use_cache options.local_partial_types = True + options.export_types = "inspect" in testcase.file # Treat empty bodies safely for these test cases. options.allow_empty_bodies = not testcase.name.endswith("_no_empty") if re.search("flags:.*--follow-imports", source) is None: @@ -163,7 +164,7 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo return options def run_check(self, server: Server, sources: list[BuildSource]) -> list[str]: - response = server.check(sources, export_types=True, is_tty=False, terminal_width=-1) + response = server.check(sources, export_types=False, is_tty=False, terminal_width=-1) out = response["out"] or response["err"] assert isinstance(out, str) return out.splitlines() diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 77367eb02bfe..ca2c969d2f5e 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -360,6 +360,33 @@ def bar() -> None: x = foo('abc') # type: str foo(arg='xyz') +[case testDaemonInspectCheck] +$ dmypy start +Daemon started +$ dmypy check foo.py +Success: no issues found in 1 source file +$ dmypy check foo.py --export-types +Success: no issues found in 1 source file +$ dmypy inspect foo.py:1:1 +"int" +[file foo.py] +x = 1 + +[case testDaemonInspectRun] +$ dmypy run test1.py +Daemon started +Success: no issues found in 1 source file +$ dmypy run test2.py +Success: no issues found in 1 source file +$ dmypy run test1.py --export-types +Success: no issues found in 1 source file +$ dmypy inspect test1.py:1:1 +"int" +[file test1.py] +a: int +[file test2.py] +a: str + [case testDaemonGetType] $ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.8 Daemon started From 44e527a3ea2d4bc66565c64edf837c3560eacb3e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 2 Nov 2023 23:45:04 +0000 Subject: [PATCH 0364/1617] Fix strict-optional in extending generic TypedDict (#16398) Fixes https://github.com/python/mypy/issues/16395 --- mypy/semanal_typeddict.py | 4 +++- test-data/unit/check-typeddict.test | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index e9aaee55879a..5104d31f5c26 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -37,6 +37,7 @@ has_placeholder, require_bool_literal_argument, ) +from mypy.state import state from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type from mypy.types import ( TPDICT_NAMES, @@ -203,7 +204,8 @@ def add_keys_and_types_from_base( any_kind = TypeOfAny.from_error base_args = [AnyType(any_kind) for _ in tvars] - valid_items = self.map_items_to_base(valid_items, tvars, base_args) + with state.strict_optional_set(self.options.strict_optional): + valid_items = self.map_items_to_base(valid_items, tvars, base_args) for key in base_items: if key in keys: self.fail(f'Overwriting TypedDict field "{key}" while merging', ctx) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 088b52db0473..c1c791304a15 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3379,3 +3379,20 @@ bar |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Di bar |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict-iror.pyi] + +[case testGenericTypedDictStrictOptionalExtending] +from typing import Generic, TypeVar, TypedDict, Optional + +T = TypeVar("T") +class Foo(TypedDict, Generic[T], total=False): + a: Optional[str] + g: Optional[T] + +class Bar(Foo[T], total=False): + other: str + +b: Bar[int] +reveal_type(b["a"]) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(b["g"]) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 93e6de4f1c3c1b1ed6c9a54e1c4f355443912a63 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 3 Nov 2023 12:01:54 +0300 Subject: [PATCH 0365/1617] Improve error messages for `super` checks and add more tests (#16393) Now all messages use the same `"super"` formatting, it used to be a bit different. --- mypy/message_registry.py | 4 ++-- test-data/unit/check-super.test | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index dc46eb503390..93581d5aca90 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -206,10 +206,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage: ) TARGET_CLASS_HAS_NO_BASE_CLASS: Final = ErrorMessage("Target class has no base class") SUPER_OUTSIDE_OF_METHOD_NOT_SUPPORTED: Final = ErrorMessage( - "super() outside of a method is not supported" + '"super()" outside of a method is not supported' ) SUPER_ENCLOSING_POSITIONAL_ARGS_REQUIRED: Final = ErrorMessage( - "super() requires one or more positional arguments in enclosing function" + '"super()" requires one or two positional arguments in enclosing function' ) # Self-type diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index 48a0a0250ecf..8816322a270a 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -280,8 +280,12 @@ class B(A): [case testSuperOutsideMethodNoCrash] -class C: - a = super().whatever # E: super() outside of a method is not supported +class A: + x = 1 +class B(A): pass +class C(B): + b = super(B, B).x + a = super().whatever # E: "super()" outside of a method is not supported [case testSuperWithObjectClassAsFirstArgument] class A: @@ -366,13 +370,22 @@ class C(B): [case testSuperInMethodWithNoArguments] class A: def f(self) -> None: pass + @staticmethod + def st() -> int: + return 1 class B(A): def g() -> None: # E: Method must have at least one argument. Did you forget the "self" argument? - super().f() # E: super() requires one or more positional arguments in enclosing function + super().f() # E: "super()" requires one or two positional arguments in enclosing function def h(self) -> None: def a() -> None: - super().f() # E: super() requires one or more positional arguments in enclosing function + super().f() # E: "super()" requires one or two positional arguments in enclosing function + @staticmethod + def st() -> int: + reveal_type(super(B, B).st()) # N: Revealed type is "builtins.int" + super().st() # E: "super()" requires one or two positional arguments in enclosing function + return 2 +[builtins fixtures/staticmethod.pyi] [case testSuperWithUnsupportedTypeObject] from typing import Type From 8c57df01386f3e29d877ca190dc4c5e5af7b92a1 Mon Sep 17 00:00:00 2001 From: Matthew Wright Date: Fri, 3 Nov 2023 07:50:36 -0500 Subject: [PATCH 0366/1617] Allow mypy to output a junit file with per-file results (#16388) Adds a new `--junit-format` flag to MyPy, which affects the structure of the junit file written when `--junit-xml` is specified (it has no effect when not writing a junit file). This flag can take `global` or `per_file` as values: * `--junit-format=global` (the default) preserves the existing junit structure, creating a junit file specifying a single "test" for the entire mypy run. * `--junit-format=per_file` will cause the junit file to have one test entry per file with failures (or a single entry, as in the existing behavior, in the case when typechecking passes). In some settings it can be useful to know which files had typechecking failures (for example, a CI system might want to display failures by file); while that information can be parsed out of the error messages in the existing junit files, it's much more convenient to have that represented in the junit structure. Tests for the old and new junit structure have been added. --- mypy/build.py | 18 ++++--- mypy/config_parser.py | 13 +++++ mypy/main.py | 38 ++++++++++++--- mypy/options.py | 2 + mypy/test/testerrorstream.py | 2 +- mypy/test/testgraph.py | 2 +- mypy/test/testutil.py | 69 ++++++++++++++++++++++++++- mypy/util.py | 92 ++++++++++++++++++++++++++---------- mypyc/build.py | 4 +- 9 files changed, 197 insertions(+), 43 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 605368a6dc51..961198fc2fa4 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -145,7 +145,7 @@ def build( sources: list[BuildSource], options: Options, alt_lib_path: str | None = None, - flush_errors: Callable[[list[str], bool], None] | None = None, + flush_errors: Callable[[str | None, list[str], bool], None] | None = None, fscache: FileSystemCache | None = None, stdout: TextIO | None = None, stderr: TextIO | None = None, @@ -177,7 +177,9 @@ def build( # fields for callers that want the traditional API. messages = [] - def default_flush_errors(new_messages: list[str], is_serious: bool) -> None: + def default_flush_errors( + filename: str | None, new_messages: list[str], is_serious: bool + ) -> None: messages.extend(new_messages) flush_errors = flush_errors or default_flush_errors @@ -197,7 +199,7 @@ def default_flush_errors(new_messages: list[str], is_serious: bool) -> None: # Patch it up to contain either none or all none of the messages, # depending on whether we are flushing errors. serious = not e.use_stdout - flush_errors(e.messages, serious) + flush_errors(None, e.messages, serious) e.messages = messages raise @@ -206,7 +208,7 @@ def _build( sources: list[BuildSource], options: Options, alt_lib_path: str | None, - flush_errors: Callable[[list[str], bool], None], + flush_errors: Callable[[str | None, list[str], bool], None], fscache: FileSystemCache | None, stdout: TextIO, stderr: TextIO, @@ -600,7 +602,7 @@ def __init__( plugin: Plugin, plugins_snapshot: dict[str, str], errors: Errors, - flush_errors: Callable[[list[str], bool], None], + flush_errors: Callable[[str | None, list[str], bool], None], fscache: FileSystemCache, stdout: TextIO, stderr: TextIO, @@ -3458,7 +3460,11 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No for id in stale: graph[id].transitive_error = True for id in stale: - manager.flush_errors(manager.errors.file_messages(graph[id].xpath), False) + manager.flush_errors( + manager.errors.simplify_path(graph[id].xpath), + manager.errors.file_messages(graph[id].xpath), + False, + ) graph[id].write_cache() graph[id].mark_as_rechecked() diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 4dbd6477c81e..a6bf021000c1 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -152,6 +152,17 @@ def check_follow_imports(choice: str) -> str: return choice +def check_junit_format(choice: str) -> str: + choices = ["global", "per_file"] + if choice not in choices: + raise argparse.ArgumentTypeError( + "invalid choice '{}' (choose from {})".format( + choice, ", ".join(f"'{x}'" for x in choices) + ) + ) + return choice + + def split_commas(value: str) -> list[str]: # Uses a bit smarter technique to allow last trailing comma # and to remove last `""` item from the split. @@ -173,6 +184,7 @@ def split_commas(value: str) -> list[str]: "files": split_and_match_files, "quickstart_file": expand_path, "junit_xml": expand_path, + "junit_format": check_junit_format, "follow_imports": check_follow_imports, "no_site_packages": bool, "plugins": lambda s: [p.strip() for p in split_commas(s)], @@ -200,6 +212,7 @@ def split_commas(value: str) -> list[str]: "python_version": parse_version, "mypy_path": lambda s: [expand_path(p) for p in try_split(s, "[,:]")], "files": lambda s: split_and_match_files_list(try_split(s)), + "junit_format": lambda s: check_junit_format(str(s)), "follow_imports": lambda s: check_follow_imports(str(s)), "plugins": try_split, "always_true": try_split, diff --git a/mypy/main.py b/mypy/main.py index 1aede530c33e..5e0dc17c668a 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -7,6 +7,7 @@ import subprocess import sys import time +from collections import defaultdict from gettext import gettext from typing import IO, Any, Final, NoReturn, Sequence, TextIO @@ -158,11 +159,14 @@ def run_build( formatter = util.FancyFormatter(stdout, stderr, options.hide_error_codes) messages = [] + messages_by_file = defaultdict(list) - def flush_errors(new_messages: list[str], serious: bool) -> None: + def flush_errors(filename: str | None, new_messages: list[str], serious: bool) -> None: if options.pretty: new_messages = formatter.fit_in_terminal(new_messages) messages.extend(new_messages) + if new_messages: + messages_by_file[filename].extend(new_messages) if options.non_interactive: # Collect messages and possibly show them later. return @@ -200,7 +204,7 @@ def flush_errors(new_messages: list[str], serious: bool) -> None: ), file=stderr, ) - maybe_write_junit_xml(time.time() - t0, serious, messages, options) + maybe_write_junit_xml(time.time() - t0, serious, messages, messages_by_file, options) return res, messages, blockers @@ -1054,6 +1058,12 @@ def add_invertible_flag( other_group = parser.add_argument_group(title="Miscellaneous") other_group.add_argument("--quickstart-file", help=argparse.SUPPRESS) other_group.add_argument("--junit-xml", help="Write junit.xml to the given file") + imports_group.add_argument( + "--junit-format", + choices=["global", "per_file"], + default="global", + help="If --junit-xml is set, specifies format. global: single test with all errors; per_file: one test entry per file with failures", + ) other_group.add_argument( "--find-occurrences", metavar="CLASS.MEMBER", @@ -1483,18 +1493,32 @@ def process_cache_map( options.cache_map[source] = (meta_file, data_file) -def maybe_write_junit_xml(td: float, serious: bool, messages: list[str], options: Options) -> None: +def maybe_write_junit_xml( + td: float, + serious: bool, + all_messages: list[str], + messages_by_file: dict[str | None, list[str]], + options: Options, +) -> None: if options.junit_xml: py_version = f"{options.python_version[0]}_{options.python_version[1]}" - util.write_junit_xml( - td, serious, messages, options.junit_xml, py_version, options.platform - ) + if options.junit_format == "global": + util.write_junit_xml( + td, serious, {None: all_messages}, options.junit_xml, py_version, options.platform + ) + else: + # per_file + util.write_junit_xml( + td, serious, messages_by_file, options.junit_xml, py_version, options.platform + ) def fail(msg: str, stderr: TextIO, options: Options) -> NoReturn: """Fail with a serious error.""" stderr.write(f"{msg}\n") - maybe_write_junit_xml(0.0, serious=True, messages=[msg], options=options) + maybe_write_junit_xml( + 0.0, serious=True, all_messages=[msg], messages_by_file={None: [msg]}, options=options + ) sys.exit(2) diff --git a/mypy/options.py b/mypy/options.py index 8bb20dbd4410..38a87e423766 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -255,6 +255,8 @@ def __init__(self) -> None: # Write junit.xml to given file self.junit_xml: str | None = None + self.junit_format: str = "global" # global|per_file + # Caching and incremental checking options self.incremental = True self.cache_dir = defaults.CACHE_DIR diff --git a/mypy/test/testerrorstream.py b/mypy/test/testerrorstream.py index 4b98f10fc9ca..5ed112fd31e7 100644 --- a/mypy/test/testerrorstream.py +++ b/mypy/test/testerrorstream.py @@ -29,7 +29,7 @@ def test_error_stream(testcase: DataDrivenTestCase) -> None: logged_messages: list[str] = [] - def flush_errors(msgs: list[str], serious: bool) -> None: + def flush_errors(filename: str | None, msgs: list[str], serious: bool) -> None: if msgs: logged_messages.append("==== Errors flushed ====") logged_messages.extend(msgs) diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index b0d148d5ae9c..0355e75e8c34 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -50,7 +50,7 @@ def _make_manager(self) -> BuildManager: plugin=Plugin(options), plugins_snapshot={}, errors=errors, - flush_errors=lambda msgs, serious: None, + flush_errors=lambda filename, msgs, serious: None, fscache=fscache, stdout=sys.stdout, stderr=sys.stderr, diff --git a/mypy/test/testutil.py b/mypy/test/testutil.py index 571e4d0b11f2..d0d54ffec8c6 100644 --- a/mypy/test/testutil.py +++ b/mypy/test/testutil.py @@ -4,7 +4,7 @@ from unittest import TestCase, mock from mypy.inspections import parse_location -from mypy.util import get_terminal_width +from mypy.util import _generate_junit_contents, get_terminal_width class TestGetTerminalSize(TestCase): @@ -20,3 +20,70 @@ def test_get_terminal_size_in_pty_defaults_to_80(self) -> None: def test_parse_location_windows(self) -> None: assert parse_location(r"C:\test.py:1:1") == (r"C:\test.py", [1, 1]) assert parse_location(r"C:\test.py:1:1:1:1") == (r"C:\test.py", [1, 1, 1, 1]) + + +class TestWriteJunitXml(TestCase): + def test_junit_pass(self) -> None: + serious = False + messages_by_file: dict[str | None, list[str]] = {} + expected = """ + + + + +""" + result = _generate_junit_contents( + dt=1.23, + serious=serious, + messages_by_file=messages_by_file, + version="3.14", + platform="test-plat", + ) + assert result == expected + + def test_junit_fail_two_files(self) -> None: + serious = False + messages_by_file: dict[str | None, list[str]] = { + "file1.py": ["Test failed", "another line"], + "file2.py": ["Another failure", "line 2"], + } + expected = """ + + + Test failed +another line + + + Another failure +line 2 + + +""" + result = _generate_junit_contents( + dt=1.23, + serious=serious, + messages_by_file=messages_by_file, + version="3.14", + platform="test-plat", + ) + assert result == expected + + def test_serious_error(self) -> None: + serious = True + messages_by_file: dict[str | None, list[str]] = {None: ["Error line 1", "Error line 2"]} + expected = """ + + + Error line 1 +Error line 2 + + +""" + result = _generate_junit_contents( + dt=1.23, + serious=serious, + messages_by_file=messages_by_file, + version="3.14", + platform="test-plat", + ) + assert result == expected diff --git a/mypy/util.py b/mypy/util.py index d0f2f8c6cc36..7a13de427e8e 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -234,45 +234,85 @@ def get_mypy_comments(source: str) -> list[tuple[int, str]]: return results -PASS_TEMPLATE: Final = """ - - - - +JUNIT_HEADER_TEMPLATE: Final = """ + """ -FAIL_TEMPLATE: Final = """ - - +JUNIT_TESTCASE_FAIL_TEMPLATE: Final = """ {text} - """ -ERROR_TEMPLATE: Final = """ - - +JUNIT_ERROR_TEMPLATE: Final = """ {text} - """ +JUNIT_TESTCASE_PASS_TEMPLATE: Final = """ + +""" -def write_junit_xml( - dt: float, serious: bool, messages: list[str], path: str, version: str, platform: str -) -> None: - from xml.sax.saxutils import escape +JUNIT_FOOTER: Final = """ +""" - if not messages and not serious: - xml = PASS_TEMPLATE.format(time=dt, ver=version, platform=platform) - elif not serious: - xml = FAIL_TEMPLATE.format( - text=escape("\n".join(messages)), time=dt, ver=version, platform=platform - ) + +def _generate_junit_contents( + dt: float, + serious: bool, + messages_by_file: dict[str | None, list[str]], + version: str, + platform: str, +) -> str: + if serious: + failures = 0 + errors = len(messages_by_file) else: - xml = ERROR_TEMPLATE.format( - text=escape("\n".join(messages)), time=dt, ver=version, platform=platform - ) + failures = len(messages_by_file) + errors = 0 + + xml = JUNIT_HEADER_TEMPLATE.format( + errors=errors, + failures=failures, + time=dt, + # If there are no messages, we still write one "test" indicating success. + tests=len(messages_by_file) or 1, + ) + + if not messages_by_file: + xml += JUNIT_TESTCASE_PASS_TEMPLATE.format(time=dt, ver=version, platform=platform) + else: + for filename, messages in messages_by_file.items(): + if filename is not None: + xml += JUNIT_TESTCASE_FAIL_TEMPLATE.format( + text="\n".join(messages), + filename=filename, + time=dt, + name="mypy-py{ver}-{platform} {filename}".format( + ver=version, platform=platform, filename=filename + ), + ) + else: + xml += JUNIT_TESTCASE_FAIL_TEMPLATE.format( + text="\n".join(messages), + filename="mypy", + time=dt, + name="mypy-py{ver}-{platform}".format(ver=version, platform=platform), + ) + + xml += JUNIT_FOOTER + + return xml + + +def write_junit_xml( + dt: float, + serious: bool, + messages_by_file: dict[str | None, list[str]], + path: str, + version: str, + platform: str, +) -> None: + xml = _generate_junit_contents(dt, serious, messages_by_file, version, platform) # checks for a directory structure in path and creates folders if needed xml_dirs = os.path.dirname(os.path.abspath(path)) diff --git a/mypyc/build.py b/mypyc/build.py index 0af8908e14d0..a9082df81945 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -105,7 +105,9 @@ def emit_messages(options: Options, messages: list[str], dt: float, serious: boo # ... you know, just in case. if options.junit_xml: py_version = f"{options.python_version[0]}_{options.python_version[1]}" - write_junit_xml(dt, serious, messages, options.junit_xml, py_version, options.platform) + write_junit_xml( + dt, serious, {None: messages}, options.junit_xml, py_version, options.platform + ) if messages: print("\n".join(messages)) From 544e6ce119ec6bdd5eabab53a433264c98dc7d9c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 4 Nov 2023 23:42:22 +0000 Subject: [PATCH 0367/1617] Fix type narrowing in lambda expressions (#16407) Fixes https://github.com/python/mypy/issues/4297 Fix is straightforward: without properly pushing lambda expression on the stack, the previous fix @JukkaL added for nested functions doesn't work for lambdas (it thinks that we are at global scope). --- mypy/checkexpr.py | 3 ++- test-data/unit/check-inference-context.test | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 95700a52af02..056b2f7bd2c6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5195,7 +5195,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: else: # Type context available. self.chk.return_types.append(inferred_type.ret_type) - self.chk.check_func_item(e, type_override=type_override) + with self.chk.tscope.function_scope(e): + self.chk.check_func_item(e, type_override=type_override) if not self.chk.has_type(e.expr()): # TODO: return expression must be accepted before exiting function scope. self.accept(e.expr(), allow_none_return=True) diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index a933acbf7f32..afe6548df2d4 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -1482,3 +1482,16 @@ b: Any i = i if isinstance(i, int) else b reveal_type(i) # N: Revealed type is "Union[Any, builtins.int]" [builtins fixtures/isinstance.pyi] + +[case testLambdaInferenceUsesNarrowedTypes] +from typing import Optional, Callable + +def f1(key: Callable[[], str]) -> None: ... +def f2(key: object) -> None: ... + +def g(b: Optional[str]) -> None: + if b: + f1(lambda: reveal_type(b)) # N: Revealed type is "builtins.str" + z: Callable[[], str] = lambda: reveal_type(b) # N: Revealed type is "builtins.str" + f2(lambda: reveal_type(b)) # N: Revealed type is "builtins.str" + lambda: reveal_type(b) # N: Revealed type is "builtins.str" From 285519cca8c64f5fc35bde6bfa52c48d1fb1cea1 Mon Sep 17 00:00:00 2001 From: Matthew Wright Date: Mon, 6 Nov 2023 20:59:57 -0600 Subject: [PATCH 0368/1617] Fix junit writing bug introduced in #16388 (#16417) #16388 introduced a bug where, with `--junit-format=global`, the junit file would indicate an error (with no message) even if everything passed. That was because `_generate_junit_contents` would check if `messages_by_file` was empty or not to determine if there were failures, but with `--junit-format=global` we'd pass in a dictionary of the form `{None: all_messages}`; `all_messages` would be empty, but the resulting dictionary wouldn't be. The fix is to pass in an empty dictionary if there are no messages. I've tested manually with `--junit-format=global` and `--junit-format=per_file` in the successful case to make sure the files are written correctly now. --- mypy/main.py | 7 ++++++- mypyc/build.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 5e0dc17c668a..8a35c2056963 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1504,7 +1504,12 @@ def maybe_write_junit_xml( py_version = f"{options.python_version[0]}_{options.python_version[1]}" if options.junit_format == "global": util.write_junit_xml( - td, serious, {None: all_messages}, options.junit_xml, py_version, options.platform + td, + serious, + {None: all_messages} if all_messages else {}, + options.junit_xml, + py_version, + options.platform, ) else: # per_file diff --git a/mypyc/build.py b/mypyc/build.py index a9082df81945..485803acba46 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -106,7 +106,12 @@ def emit_messages(options: Options, messages: list[str], dt: float, serious: boo if options.junit_xml: py_version = f"{options.python_version[0]}_{options.python_version[1]}" write_junit_xml( - dt, serious, {None: messages}, options.junit_xml, py_version, options.platform + dt, + serious, + {None: messages} if messages else {}, + options.junit_xml, + py_version, + options.platform, ) if messages: print("\n".join(messages)) From bc591c756a453bb6a78a31e734b1f0aa475e90e0 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:41:43 +0000 Subject: [PATCH 0369/1617] fix dmypy after junit_xml change (#16421) https://github.com/python/mypy/pull/16388/ changed the definition of `write_junit_xml` but missed a call site in dmypy. This fixes it. --- mypy/dmypy/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 229740e44db0..9f0751e93609 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -573,7 +573,7 @@ def check_output( write_junit_xml( response["roundtrip_time"], bool(err), - messages, + {None: messages} if messages else {}, junit_xml, response["python_version"], response["platform"], From a1864d4fa498ccd8773c2247eb62282644174d26 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 9 Nov 2023 00:04:36 +0000 Subject: [PATCH 0370/1617] Add error code for mutable covariant override (#16399) Fixes https://github.com/python/mypy/issues/3208 Interestingly, we already prohibit this when the override is a mutable property (goes through `FuncDef`-related code), and in multiple inheritance. The logic there is not very principled, but I just left a TODO instead of extending the scope of this PR. --- docs/source/error_code_list2.rst | 31 +++++++++++++++++++++++++++ mypy/checker.py | 21 +++++++++++++++--- mypy/errorcodes.py | 6 ++++++ mypy/message_registry.py | 3 +++ test-data/unit/check-errorcodes.test | 32 ++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index cc5c9b0a1bc6..9e24f21909d5 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -482,6 +482,37 @@ Example: def g(self, y: int) -> None: pass +.. _code-mutable-override: + +Check that overrides of mutable attributes are safe +--------------------------------------------------- + +This will enable the check for unsafe overrides of mutable attributes. For +historical reasons, and because this is a relatively common pattern in Python, +this check is not enabled by default. The example below is unsafe, and will be +flagged when this error code is enabled: + +.. code-block:: python + + from typing import Any + + class C: + x: float + y: float + z: float + + class D(C): + x: int # Error: Covariant override of a mutable attribute + # (base class "C" defined the type as "float", + # expression has type "int") [mutable-override] + y: float # OK + z: Any # OK + + def f(c: C) -> None: + c.x = 1.1 + d = D() + f(d) + d.x >> 1 # This will crash at runtime, because d.x is now float, not an int .. _code-unimported-reveal: diff --git a/mypy/checker.py b/mypy/checker.py index f51ba746ea75..e4eb58d40715 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2041,7 +2041,6 @@ def check_method_override_for_base_with_name( pass elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): # Check that the types are compatible. - # TODO overloaded signatures self.check_override( typ, original_type, @@ -2056,7 +2055,6 @@ def check_method_override_for_base_with_name( # Assume invariance for a non-callable attribute here. Note # that this doesn't affect read-only properties which can have # covariant overrides. - # pass elif ( original_node @@ -2636,6 +2634,9 @@ class C(B, A[int]): ... # this is unsafe because... first_type = get_proper_type(self.determine_type_of_member(first)) second_type = get_proper_type(self.determine_type_of_member(second)) + # TODO: use more principled logic to decide is_subtype() vs is_equivalent(). + # We should rely on mutability of superclass node, not on types being Callable. + # start with the special case that Instance can be a subtype of FunctionLike call = None if isinstance(first_type, Instance): @@ -3211,7 +3212,7 @@ def check_compatibility_super( if base_static and compare_static: lvalue_node.is_staticmethod = True - return self.check_subtype( + ok = self.check_subtype( compare_type, base_type, rvalue, @@ -3219,6 +3220,20 @@ def check_compatibility_super( "expression has type", f'base class "{base.name}" defined the type as', ) + if ( + ok + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(base_node) + ): + ok = self.check_subtype( + base_type, + compare_type, + rvalue, + message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE, + f'base class "{base.name}" defined the type as', + "expression has type", + ) + return ok return True def lvalue_type_from_base( diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index c6e9de9f31c1..72ee63a6a897 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -255,6 +255,12 @@ def __hash__(self) -> int: "General", default_enabled=False, ) +MUTABLE_OVERRIDE: Final[ErrorCode] = ErrorCode( + "mutable-override", + "Reject covariant overrides for mutable attributes", + "General", + default_enabled=False, +) # Syntax errors are often blocking. diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 93581d5aca90..8dc14e158d90 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -63,6 +63,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage: INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = ErrorMessage( "Incompatible types in assignment", code=codes.ASSIGNMENT ) +COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE: Final = ErrorMessage( + "Covariant override of a mutable attribute", code=codes.MUTABLE_OVERRIDE +) INCOMPATIBLE_TYPES_IN_AWAIT: Final = ErrorMessage('Incompatible types in "await"') INCOMPATIBLE_REDEFINITION: Final = ErrorMessage("Incompatible redefinition") INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = ( diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 2282f21bcfa6..28487a456156 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1148,3 +1148,35 @@ main:3: note: Revealed local types are: main:3: note: x: builtins.int main:3: error: Name "reveal_locals" is not defined [unimported-reveal] [builtins fixtures/isinstancelist.pyi] + +[case testCovariantMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Any + +class C: + x: float + y: float + z: float + w: Any + @property + def foo(self) -> float: ... + @property + def bar(self) -> float: ... + @bar.setter + def bar(self, val: float) -> None: ... + baz: float + bad1: float + bad2: float +class D(C): + x: int # E: Covariant override of a mutable attribute (base class "C" defined the type as "float", expression has type "int") [mutable-override] + y: float + z: Any + w: float + foo: int + bar: int # E: Covariant override of a mutable attribute (base class "C" defined the type as "float", expression has type "int") [mutable-override] + def one(self) -> None: + self.baz = 5 + bad1 = 5 # E: Covariant override of a mutable attribute (base class "C" defined the type as "float", expression has type "int") [mutable-override] + def other(self) -> None: + self.bad2: int = 5 # E: Covariant override of a mutable attribute (base class "C" defined the type as "float", expression has type "int") [mutable-override] +[builtins fixtures/property.pyi] From 583813284c64719d016117096907b94eb1b82e74 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 12:27:46 +0000 Subject: [PATCH 0371/1617] Add script to generate draft changelog entries (#16430) The script format changelog entries based on commit history and has some rules to filter out some changes, such as typeshed sync and changes cherry-picked to the previous release branch. Example of how to run it: ``` $ python misc/generate_changelog.py 1.7 Generating changelog for 1.7 Previous release was 1.6 Merge base: d7b24514d7301f86031b7d1e2215cf8c2476bec0 NOTE: Drop "Fix crash on ParamSpec unification (for real)", since it was in previous release branch NOTE: Drop "Fix crash on ParamSpec unification", since it was in previous release branch NOTE: Drop "Fix mypyc regression with pretty", since it was in previous release branch NOTE: Drop "Clear cache when adding --new-type-inference", since it was in previous release branch NOTE: Drop "Match note error codes to import error codes", since it was in previous release branch NOTE: Drop "Make PEP 695 constructs give a reasonable error message", since it was in previous release branch NOTE: Drop "Fix ParamSpec inference for callback protocols", since it was in previous release branch NOTE: Drop "Try upgrading tox", since it was in previous release branch NOTE: Drop "Optimize Unpack for failures", since it was in previous release branch * Fix crash on unpack call special-casing (Ivan Levkivskyi, PR [16381](https://github.com/python/mypy/pull/16381)) * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) * Fix daemon crash caused by deleted submodule (Jukka Lehtosalo, PR [16370](https://github.com/python/mypy/pull/16370)) ... ``` --- misc/generate_changelog.py | 201 +++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 misc/generate_changelog.py diff --git a/misc/generate_changelog.py b/misc/generate_changelog.py new file mode 100644 index 000000000000..7c7f28b6eeb7 --- /dev/null +++ b/misc/generate_changelog.py @@ -0,0 +1,201 @@ +"""Generate the changelog for a mypy release.""" + +from __future__ import annotations + +import argparse +import re +import subprocess +import sys +from dataclasses import dataclass + + +def find_all_release_branches() -> list[tuple[int, int]]: + result = subprocess.run(["git", "branch", "-r"], text=True, capture_output=True, check=True) + versions = [] + for line in result.stdout.splitlines(): + line = line.strip() + if m := re.match(r"origin/release-([0-9]+)\.([0-9]+)$", line): + major = int(m.group(1)) + minor = int(m.group(2)) + versions.append((major, minor)) + return versions + + +def git_merge_base(rev1: str, rev2: str) -> str: + result = subprocess.run( + ["git", "merge-base", rev1, rev2], text=True, capture_output=True, check=True + ) + return result.stdout.strip() + + +@dataclass +class CommitInfo: + commit: str + author: str + title: str + pr_number: int | None + + +def normalize_author(author: str) -> str: + # Some ad-hoc rules to get more consistent author names. + if author == "AlexWaygood": + return "Alex Waygood" + elif author == "jhance": + return "Jared Hance" + return author + + +def git_commit_log(rev1: str, rev2: str) -> list[CommitInfo]: + result = subprocess.run( + ["git", "log", "--pretty=%H\t%an\t%s", f"{rev1}..{rev2}"], + text=True, + capture_output=True, + check=True, + ) + commits = [] + for line in result.stdout.splitlines(): + commit, author, title = line.strip().split("\t", 2) + pr_number = None + if m := re.match(r".*\(#([0-9]+)\) *$", title): + pr_number = int(m.group(1)) + title = re.sub(r" *\(#[0-9]+\) *$", "", title) + + author = normalize_author(author) + entry = CommitInfo(commit, author, title, pr_number) + commits.append(entry) + return commits + + +def filter_omitted_commits(commits: list[CommitInfo]) -> list[CommitInfo]: + result = [] + for c in commits: + title = c.title + keep = True + if title.startswith("Sync typeshed"): + # Typeshed syncs aren't mentioned in release notes + keep = False + if title.startswith( + ( + "Revert sum literal integer change", + "Remove use of LiteralString in builtins", + "Revert typeshed ctypes change", + "Revert use of `ParamSpec` for `functools.wraps`", + ) + ): + # These are generated by a typeshed sync. + keep = False + if re.search(r"(bump|update).*version.*\+dev", title.lower()): + # Version number updates aren't mentioned + keep = False + if "pre-commit autoupdate" in title: + keep = False + if title.startswith(("Update commit hashes", "Update hashes")): + # Internal tool change + keep = False + if keep: + result.append(c) + return result + + +def normalize_title(title: str) -> str: + # We sometimes add a title prefix when cherry-picking commits to a + # release branch. Attempt to remove these prefixes so that we can + # match them to the corresponding master branch. + if m := re.match(r"\[release [0-9.]+\] *", title, flags=re.I): + title = title.replace(m.group(0), "") + return title + + +def filter_out_commits_from_old_release_branch( + new_commits: list[CommitInfo], old_commits: list[CommitInfo] +) -> list[CommitInfo]: + old_titles = {normalize_title(commit.title) for commit in old_commits} + result = [] + for commit in new_commits: + drop = False + if normalize_title(commit.title) in old_titles: + drop = True + if normalize_title(f"{commit.title} (#{commit.pr_number})") in old_titles: + drop = True + if not drop: + result.append(commit) + else: + print(f'NOTE: Drop "{commit.title}", since it was in previous release branch') + return result + + +def find_changes_between_releases(old_branch: str, new_branch: str) -> list[CommitInfo]: + merge_base = git_merge_base(old_branch, new_branch) + print(f"Merge base: {merge_base}") + new_commits = git_commit_log(merge_base, new_branch) + old_commits = git_commit_log(merge_base, old_branch) + + # Filter out some commits that won't be mentioned in release notes. + new_commits = filter_omitted_commits(new_commits) + + # Filter out commits cherry-picked to old branch. + new_commits = filter_out_commits_from_old_release_branch(new_commits, old_commits) + + return new_commits + + +def format_changelog_entry(c: CommitInfo) -> str: + """ + s = f" * {c.commit[:9]} - {c.title}" + if c.pr_number: + s += f" (#{c.pr_number})" + s += f" ({c.author})" + """ + s = f" * {c.title} ({c.author}" + if c.pr_number: + s += f", PR [{c.pr_number}](https://github.com/python/mypy/pull/{c.pr_number})" + s += ")" + + return s + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("version", help="target mypy version (form X.Y)") + parser.add_argument("--local", action="store_true") + args = parser.parse_args() + version: str = args.version + local: bool = args.local + + if not re.match(r"[0-9]+\.[0-9]+$", version): + sys.exit(f"error: Release must be of form X.Y (not {version!r})") + major, minor = (int(component) for component in version.split(".")) + + if not local: + print("Running 'git fetch' to fetch all release branches...") + subprocess.run(["git", "fetch"], check=True) + + if minor > 0: + prev_major = major + prev_minor = minor - 1 + else: + # For a x.0 release, the previous release is the most recent (x-1).y release. + all_releases = sorted(find_all_release_branches()) + if (major, minor) not in all_releases: + sys.exit(f"error: Can't find release branch for {major}.{minor} at origin") + for i in reversed(range(len(all_releases))): + if all_releases[i][0] == major - 1: + prev_major, prev_minor = all_releases[i] + break + else: + sys.exit("error: Could not determine previous release") + print(f"Generating changelog for {major}.{minor}") + print(f"Previous release was {prev_major}.{prev_minor}") + + new_branch = f"origin/release-{major}.{minor}" + old_branch = f"origin/release-{prev_major}.{prev_minor}" + + changes = find_changes_between_releases(old_branch, new_branch) + + print() + for c in changes: + print(format_changelog_entry(c)) + + +if __name__ == "__main__": + main() From f154b756097962325e231f6999c4bfd5d3f4d226 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 12:33:18 +0000 Subject: [PATCH 0372/1617] Add draft changelog for 1.7 (#16431) Generated using `misc/generate_changelog.py`. --- CHANGELOG.md | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f7c676c279..ccd161520e61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,142 @@ # Mypy Release Notes -## Unreleased +## Next release + +## Mypy 1.7 [unreleased] + +We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + +``` + python3 -m pip install -U mypy +``` + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). #### Other Notable Changes and Fixes -... + + * Fix crash on unpack call special-casing (Ivan Levkivskyi, PR [16381](https://github.com/python/mypy/pull/16381)) + * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) + * Fix daemon crash caused by deleted submodule (Jukka Lehtosalo, PR [16370](https://github.com/python/mypy/pull/16370)) + * Enable Unpack/TypeVarTuple support (Ivan Levkivskyi, PR [16354](https://github.com/python/mypy/pull/16354)) + * Fix dmypy inspect on Windows (Ivan Levkivskyi, PR [16355](https://github.com/python/mypy/pull/16355)) + * Fix dmypy inspect for namespace packages (Ivan Levkivskyi, PR [16357](https://github.com/python/mypy/pull/16357)) + * Fix incremental crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) + * doc: remove duplicate word (dinaldoap, PR [16365](https://github.com/python/mypy/pull/16365)) + * Speed up type argument checking (Jukka Lehtosalo, PR [16353](https://github.com/python/mypy/pull/16353)) + * Avoid importing from setuptools._distutils (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) + * Add fast path for checking self types (Jukka Lehtosalo, PR [16352](https://github.com/python/mypy/pull/16352)) + * Cache information about whether file is typeshed file (Jukka Lehtosalo, PR [16351](https://github.com/python/mypy/pull/16351)) + * Some final touches for variadic types support (Ivan Levkivskyi, PR [16334](https://github.com/python/mypy/pull/16334)) + * Skip expensive repr() in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) + * Enable new type inference by default (Ivan Levkivskyi, PR [16345](https://github.com/python/mypy/pull/16345)) + * Delete recursive aliases flags (Ivan Levkivskyi, PR [16346](https://github.com/python/mypy/pull/16346)) + * Update starred expr error message to match Python's (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) + * Properly use proper subtyping for callables (Ivan Levkivskyi, PR [16343](https://github.com/python/mypy/pull/16343)) + * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) + * Support PEP-646 and PEP-692 in the same callable (Ivan Levkivskyi, PR [16294](https://github.com/python/mypy/pull/16294)) + * Use upper bound as inference fallback more consistently (Ivan Levkivskyi, PR [16344](https://github.com/python/mypy/pull/16344)) + * [daemon] Fix return type change to optional in generic function (Jukka Lehtosalo, PR [16342](https://github.com/python/mypy/pull/16342)) + * [mypyc] Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) + * Run macOS mypyc tests with Python 3.9 (Shantanu, PR [16326](https://github.com/python/mypy/pull/16326)) + * Fix sdist build by not including CHANGELOG.md (Jukka Lehtosalo, PR [16323](https://github.com/python/mypy/pull/16323)) + * Add `|=` and `|` operators support for `TypedDict` (Nikita Sobolev, PR [16249](https://github.com/python/mypy/pull/16249)) + * Clarify variance convention for Parameters (Ivan Levkivskyi, PR [16302](https://github.com/python/mypy/pull/16302)) + * refactor: `__str__` in `CFG` class (#16307) (Ihor, PR [16308](https://github.com/python/mypy/pull/16308)) + * [mypyc] Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) + * stubgen: fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) + * Narrow tuple types using len() (Ivan Levkivskyi, PR [16237](https://github.com/python/mypy/pull/16237)) + * Lock test dependencies (Shantanu, PR [16283](https://github.com/python/mypy/pull/16283)) + * Correctly recognize `typing_extensions.NewType` (Ganden Schaffner, PR [16298](https://github.com/python/mypy/pull/16298)) + * fix: remove redundant `.format()` (Ihor, PR [16288](https://github.com/python/mypy/pull/16288)) + * Fix daemon false positives related to module-level __getattr__ (Jukka Lehtosalo, PR [16292](https://github.com/python/mypy/pull/16292)) + * [mypyc] Avoid cyclic reference in nested functions (Jukka Lehtosalo, PR [16268](https://github.com/python/mypy/pull/16268)) + * Add `unimported-reveal` error code (Nikita Sobolev, PR [16271](https://github.com/python/mypy/pull/16271)) + * Add a changelog (Shantanu, PR [16280](https://github.com/python/mypy/pull/16280)) + * Support fancy new syntax for variadic types (Ivan Levkivskyi, PR [16242](https://github.com/python/mypy/pull/16242)) + * Attempt to fix daemon crash related to ABCs (Jukka Lehtosalo, PR [16275](https://github.com/python/mypy/pull/16275)) + * Bump test deps: `ruff` and `pre-commit-hooks` (Nikita Sobolev, PR [16273](https://github.com/python/mypy/pull/16273)) + * Correctly handle variadic instances with empty arguments (Ivan Levkivskyi, PR [16238](https://github.com/python/mypy/pull/16238)) + * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) + * stubgen: unify C extension and pure python stub generators with object oriented design (Chad Dombrova, PR [15770](https://github.com/python/mypy/pull/15770)) + * Correctly handle runtime type applications of variadic types (Ivan Levkivskyi, PR [16240](https://github.com/python/mypy/pull/16240)) + * [mypyc] Fix direct __dict__ access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) + * Fix `coverage` config (Alex Waygood, PR [16258](https://github.com/python/mypy/pull/16258)) + * show dmypy errors post serving (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) + * Fix partially defined in the case of missing type maps (Shantanu, PR [15995](https://github.com/python/mypy/pull/15995)) + * (🎁) drop 'dev' from 3.12 in the CI (KotlinIsland, PR [16239](https://github.com/python/mypy/pull/16239)) + * Add an extra for mypyc dependencies (Shantanu, PR [16229](https://github.com/python/mypy/pull/16229)) + * Use SPDX license identifier (Nikita Sobolev, PR [16230](https://github.com/python/mypy/pull/16230)) + * Support variadic tuple packing/unpacking (Ivan Levkivskyi, PR [16205](https://github.com/python/mypy/pull/16205)) + * Remove stubs packages from `stubinfo.py` where the runtime package has added a `py.typed` file (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) + * Bump ruff and black to their latest versions (Alex Waygood, PR [16221](https://github.com/python/mypy/pull/16221)) + * __qualname__ and __module__ are available in class bodies (Anthony Sottile, PR [16215](https://github.com/python/mypy/pull/16215)) + * tests: avoid leaving artifacts in the source tree (Eli Schwartz, PR [16201](https://github.com/python/mypy/pull/16201)) + * Add meta test for new diff logic (Shantanu, PR [16211](https://github.com/python/mypy/pull/16211)) + * stubtest: hint when args in stub need to be keyword-only (Alex Waygood, PR [16210](https://github.com/python/mypy/pull/16210)) + * tuple slice should not propagate fallback (Thomas Grainger, PR [16154](https://github.com/python/mypy/pull/16154)) + * Fix cases of type object handling for overloads (Shantanu, PR [16168](https://github.com/python/mypy/pull/16168)) + * Fix error code on "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) + * Add more tests for variadic Callables (Ivan Levkivskyi, PR [16198](https://github.com/python/mypy/pull/16198)) + * Fix walrus interaction with empty collections (Ivan Levkivskyi, PR [16197](https://github.com/python/mypy/pull/16197)) + * Better support for variadic calls and indexing (Ivan Levkivskyi, PR [16131](https://github.com/python/mypy/pull/16131)) + * Use type variable bound when it appears as actual during inference (Ivan Levkivskyi, PR [16178](https://github.com/python/mypy/pull/16178)) + * Use upper bounds as fallback solutions for inference (Ivan Levkivskyi, PR [16184](https://github.com/python/mypy/pull/16184)) + * Special-case type inference of empty collections (Ivan Levkivskyi, PR [16122](https://github.com/python/mypy/pull/16122)) + * stubgen: multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) + * Fix typo in dataclasses.py (Ikko Eltociear Ashimine, PR [16173](https://github.com/python/mypy/pull/16173)) + * Remove `is_classmethod_class` slot from `CallableType` (Nikita Sobolev, PR [16151](https://github.com/python/mypy/pull/16151)) + * Do not consider `import a.b as b` an explicit reexport (Anders Kaseorg, PR [14086](https://github.com/python/mypy/pull/14086)) + * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) + * Fix inference for overloaded __call__ with generic self (Shantanu, PR [16053](https://github.com/python/mypy/pull/16053)) + * Call dynamic class hook on generic classes (Petter Friberg, PR [16052](https://github.com/python/mypy/pull/16052)) + * Preserve implicitly exported types via attribute access (Shantanu, PR [16129](https://github.com/python/mypy/pull/16129)) + * Make it easier to copy commands from docs README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) + * Use comments in issue template (Hamir Mahal, PR [15742](https://github.com/python/mypy/pull/15742)) + * dataclass.replace: allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) + * Fix the newly-uncovered stubtest bug (Alex Waygood) + * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) + * stubgen: generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) + * Fix crash on malformed TypedDict in incremental mode (Ivan Levkivskyi, PR [16115](https://github.com/python/mypy/pull/16115)) + * Better diffs in tests (Shantanu, PR [16112](https://github.com/python/mypy/pull/16112)) + * Fix tuple[Any, ...] subtyping (Shantanu, PR [16108](https://github.com/python/mypy/pull/16108)) + * Lenient handling of trivial Callable suffixes (Ivan Levkivskyi, PR [15913](https://github.com/python/mypy/pull/15913)) + * Subtyping and inference of user defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) + * [mypyc] Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) + * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) + * Add `add_overloaded_method_to_class` helper to `plugins/common.py` (Nikita Sobolev, PR [16038](https://github.com/python/mypy/pull/16038)) + * Document and rename overload-overlap error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) + * Fix __post_init__() internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) + * Differentiate between venv and tox setups in CONTRIBUTING.md (Matt Bogosian, PR [16067](https://github.com/python/mypy/pull/16067)) + * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) + * Build the docs in CI for all PRs touching the `mypy/` directory (Alex Waygood, PR [16068](https://github.com/python/mypy/pull/16068)) + * Introduce error category [unsafe-overload] (Randolf Scholz, PR [16061](https://github.com/python/mypy/pull/16061)) + * Add docs about `--force-uppercase-builtins` and `--force-union-syntax` (Nikita Sobolev, PR [16049](https://github.com/python/mypy/pull/16049)) + * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) + * Use latest `actions/checkout@v4` (Nikita Sobolev, PR [16042](https://github.com/python/mypy/pull/16042)) + * Do not use deprecated `add_method` in `attrs` plugin (Nikita Sobolev, PR [16037](https://github.com/python/mypy/pull/16037)) + * Remove type aliases that are long supported (Nikita Sobolev, PR [16039](https://github.com/python/mypy/pull/16039)) + * Add type annotations to `test-data/unit/plugins` (Nikita Sobolev, PR [16028](https://github.com/python/mypy/pull/16028)) + * Bundle `misc/proper_plugin.py` as a part of `mypy` (Nikita Sobolev, PR [16036](https://github.com/python/mypy/pull/16036)) + * Do not set `is_final` twice for `FuncBase` subclasses (Nikita Sobolev, PR [16030](https://github.com/python/mypy/pull/16030)) + * Exclude `assert False` from coverage (Nikita Sobolev, PR [16026](https://github.com/python/mypy/pull/16026)) + * ruff: add pyupgrade (Ilya Priven, PR [16023](https://github.com/python/mypy/pull/16023)) + * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) + * meta tests: refactor run_pytest (Ilya Priven, PR [15481](https://github.com/python/mypy/pull/15481)) + * docs: document dataclass_transform behavior (Ilya Priven, PR [16017](https://github.com/python/mypy/pull/16017)) + * Fix crash with report generation on namespace packages (again) (Shantanu, PR [16019](https://github.com/python/mypy/pull/16019)) + * Reword the error message related to void functions (Albert Tugushev, PR [15876](https://github.com/python/mypy/pull/15876)) + * Fix case Any() in match statement (DS/Charlie, PR [14479](https://github.com/python/mypy/pull/14479)) + * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) + * Deduplicate iterable logic (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) + * Represent bottom type as Never in messages (Shantanu, PR [15996](https://github.com/python/mypy/pull/15996)) + * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) + * Fix inference for properties with __call__ (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) + * attrs: remove fields type check (Ilya Priven, PR [15983](https://github.com/python/mypy/pull/15983)) + * dataclasses.replace: fall through to typeshed sig (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) + * Improve GitHub Actions specs (Nikita Sobolev, PR [15965](https://github.com/python/mypy/pull/15965)) + * attrs, dataclasses: don't enforce slots when base doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) #### Acknowledgements ... From ed7cc08db1ac78d4ff7191eddaa907e2f96b6ffd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 14:49:34 +0000 Subject: [PATCH 0373/1617] Updates to 1.7 changelog (#16435) Add sections for related changes. Remove some internal changes and make other small tweaks. --- CHANGELOG.md | 198 +++++++++++++++++++++++++++------------------------ 1 file changed, 104 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd161520e61..68efa2cbfc46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,129 +14,139 @@ You can read the full documentation for this release on [Read the Docs](http://m Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). -#### Other Notable Changes and Fixes +#### TypeVarTuple and Unpack Support Enabled + +TODO: Explain + +TypeVarTuple was implemented by Jared Hance and Ivan Levkivskyi over several mypy releases, with help from Jukka Lehtosalo. +Changes included in this release: + + * Enable Unpack/TypeVarTuple support (Ivan Levkivskyi, PR [16354](https://github.com/python/mypy/pull/16354)) * Fix crash on unpack call special-casing (Ivan Levkivskyi, PR [16381](https://github.com/python/mypy/pull/16381)) - * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) + * Some final touches for variadic types support (Ivan Levkivskyi, PR [16334](https://github.com/python/mypy/pull/16334)) + * Support PEP-646 and PEP-692 in the same callable (Ivan Levkivskyi, PR [16294](https://github.com/python/mypy/pull/16294)) + * Support fancy new syntax for variadic types (Ivan Levkivskyi, PR [16242](https://github.com/python/mypy/pull/16242)) + * Correctly handle variadic instances with empty arguments (Ivan Levkivskyi, PR [16238](https://github.com/python/mypy/pull/16238)) + * Correctly handle runtime type applications of variadic types (Ivan Levkivskyi, PR [16240](https://github.com/python/mypy/pull/16240)) + * Support variadic tuple packing/unpacking (Ivan Levkivskyi, PR [16205](https://github.com/python/mypy/pull/16205)) + * Better support for variadic calls and indexing (Ivan Levkivskyi, PR [16131](https://github.com/python/mypy/pull/16131)) + * Subtyping and inference of user defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) + * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) + * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) + +#### Major Changes + +TODO: Write sections about these? + + * Enable new type inference by default (Ivan Levkivskyi, PR [16345](https://github.com/python/mypy/pull/16345)) + * Narrow tuple types using len() (Ivan Levkivskyi, PR [16237](https://github.com/python/mypy/pull/16237)) + * Do not consider `import a.b as b` an explicit reexport (Anders Kaseorg, PR [14086](https://github.com/python/mypy/pull/14086)) + * Add an extra for mypyc dependencies (Shantanu, PR [16229](https://github.com/python/mypy/pull/16229)) + * Add a changelog (Shantanu, PR [16280](https://github.com/python/mypy/pull/16280)) + +#### Mypy Daemon Improvements + * Fix daemon crash caused by deleted submodule (Jukka Lehtosalo, PR [16370](https://github.com/python/mypy/pull/16370)) - * Enable Unpack/TypeVarTuple support (Ivan Levkivskyi, PR [16354](https://github.com/python/mypy/pull/16354)) + * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) * Fix dmypy inspect on Windows (Ivan Levkivskyi, PR [16355](https://github.com/python/mypy/pull/16355)) * Fix dmypy inspect for namespace packages (Ivan Levkivskyi, PR [16357](https://github.com/python/mypy/pull/16357)) - * Fix incremental crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) - * doc: remove duplicate word (dinaldoap, PR [16365](https://github.com/python/mypy/pull/16365)) + * Fix return type change to optional in generic function (Jukka Lehtosalo, PR [16342](https://github.com/python/mypy/pull/16342)) + * Fix daemon false positives related to module-level __getattr__ (Jukka Lehtosalo, PR [16292](https://github.com/python/mypy/pull/16292)) + * Attempt to fix daemon crash related to ABCs (Jukka Lehtosalo, PR [16275](https://github.com/python/mypy/pull/16275)) + * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) + * Show dmypy errors post serving (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) + +#### Mypyc Improvements + + * Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) + * Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) + * Avoid cyclic reference in nested functions (Jukka Lehtosalo, PR [16268](https://github.com/python/mypy/pull/16268)) + * Fix direct __dict__ access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) + * Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) + +#### Improvements to Error Reportong + + * Update starred expr error message to match Python's (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) + * Add `unimported-reveal` error code (Nikita Sobolev, PR [16271](https://github.com/python/mypy/pull/16271)) + * Fix error code on "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) + * Introduce error category [unsafe-overload] (Randolf Scholz, PR [16061](https://github.com/python/mypy/pull/16061)) + * Reword the error message related to void functions (Albert Tugushev, PR [15876](https://github.com/python/mypy/pull/15876)) + * Represent bottom type as Never in messages (Shantanu, PR [15996](https://github.com/python/mypy/pull/15996)) + * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) + * Remove stubs packages from `stubinfo.py` where the runtime package has added a `py.typed` file (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) + +#### Performance Improvements + * Speed up type argument checking (Jukka Lehtosalo, PR [16353](https://github.com/python/mypy/pull/16353)) - * Avoid importing from setuptools._distutils (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) * Add fast path for checking self types (Jukka Lehtosalo, PR [16352](https://github.com/python/mypy/pull/16352)) * Cache information about whether file is typeshed file (Jukka Lehtosalo, PR [16351](https://github.com/python/mypy/pull/16351)) - * Some final touches for variadic types support (Ivan Levkivskyi, PR [16334](https://github.com/python/mypy/pull/16334)) * Skip expensive repr() in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) - * Enable new type inference by default (Ivan Levkivskyi, PR [16345](https://github.com/python/mypy/pull/16345)) + +#### Attrs and Dataclass Improvements + + * dataclass.replace: allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) + * docs: document dataclass_transform behavior (Ilya Priven, PR [16017](https://github.com/python/mypy/pull/16017)) + * dataclasses.replace: fall through to typeshed sig (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) + * attrs: remove fields type check (Ilya Priven, PR [15983](https://github.com/python/mypy/pull/15983)) + * attrs, dataclasses: don't enforce slots when base doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) + +#### Stubgen Improvements + + * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) + * stubgen: fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) + * stubgen: unify C extension and pure python stub generators with object oriented design (Chad Dombrova, PR [15770](https://github.com/python/mypy/pull/15770)) + * stubgen: multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) + * stubgen: generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) + +#### Fixes to Crashes + + * Fix incremental crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) + * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) + * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) + * Fix crash on malformed TypedDict in incremental mode (Ivan Levkivskyi, PR [16115](https://github.com/python/mypy/pull/16115)) + * Fix crash with report generation on namespace packages (again) (Shantanu, PR [16019](https://github.com/python/mypy/pull/16019)) + * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) + * Fix `__post_init__()` internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) + +#### Documentation Updates + + * Make it easier to copy commands from docs README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) + * Document and rename overload-overlap error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) + * Add docs about `--force-uppercase-builtins` and `--force-union-syntax` (Nikita Sobolev, PR [16049](https://github.com/python/mypy/pull/16049)) + * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) + * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) + +#### Other Notable Changes and Fixes + + * Avoid importing from setuptools._distutils (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) * Delete recursive aliases flags (Ivan Levkivskyi, PR [16346](https://github.com/python/mypy/pull/16346)) - * Update starred expr error message to match Python's (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) * Properly use proper subtyping for callables (Ivan Levkivskyi, PR [16343](https://github.com/python/mypy/pull/16343)) - * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) - * Support PEP-646 and PEP-692 in the same callable (Ivan Levkivskyi, PR [16294](https://github.com/python/mypy/pull/16294)) * Use upper bound as inference fallback more consistently (Ivan Levkivskyi, PR [16344](https://github.com/python/mypy/pull/16344)) - * [daemon] Fix return type change to optional in generic function (Jukka Lehtosalo, PR [16342](https://github.com/python/mypy/pull/16342)) - * [mypyc] Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) - * Run macOS mypyc tests with Python 3.9 (Shantanu, PR [16326](https://github.com/python/mypy/pull/16326)) - * Fix sdist build by not including CHANGELOG.md (Jukka Lehtosalo, PR [16323](https://github.com/python/mypy/pull/16323)) * Add `|=` and `|` operators support for `TypedDict` (Nikita Sobolev, PR [16249](https://github.com/python/mypy/pull/16249)) * Clarify variance convention for Parameters (Ivan Levkivskyi, PR [16302](https://github.com/python/mypy/pull/16302)) - * refactor: `__str__` in `CFG` class (#16307) (Ihor, PR [16308](https://github.com/python/mypy/pull/16308)) - * [mypyc] Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) - * stubgen: fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) - * Narrow tuple types using len() (Ivan Levkivskyi, PR [16237](https://github.com/python/mypy/pull/16237)) - * Lock test dependencies (Shantanu, PR [16283](https://github.com/python/mypy/pull/16283)) * Correctly recognize `typing_extensions.NewType` (Ganden Schaffner, PR [16298](https://github.com/python/mypy/pull/16298)) - * fix: remove redundant `.format()` (Ihor, PR [16288](https://github.com/python/mypy/pull/16288)) - * Fix daemon false positives related to module-level __getattr__ (Jukka Lehtosalo, PR [16292](https://github.com/python/mypy/pull/16292)) - * [mypyc] Avoid cyclic reference in nested functions (Jukka Lehtosalo, PR [16268](https://github.com/python/mypy/pull/16268)) - * Add `unimported-reveal` error code (Nikita Sobolev, PR [16271](https://github.com/python/mypy/pull/16271)) - * Add a changelog (Shantanu, PR [16280](https://github.com/python/mypy/pull/16280)) - * Support fancy new syntax for variadic types (Ivan Levkivskyi, PR [16242](https://github.com/python/mypy/pull/16242)) - * Attempt to fix daemon crash related to ABCs (Jukka Lehtosalo, PR [16275](https://github.com/python/mypy/pull/16275)) - * Bump test deps: `ruff` and `pre-commit-hooks` (Nikita Sobolev, PR [16273](https://github.com/python/mypy/pull/16273)) - * Correctly handle variadic instances with empty arguments (Ivan Levkivskyi, PR [16238](https://github.com/python/mypy/pull/16238)) - * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) - * stubgen: unify C extension and pure python stub generators with object oriented design (Chad Dombrova, PR [15770](https://github.com/python/mypy/pull/15770)) - * Correctly handle runtime type applications of variadic types (Ivan Levkivskyi, PR [16240](https://github.com/python/mypy/pull/16240)) - * [mypyc] Fix direct __dict__ access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) - * Fix `coverage` config (Alex Waygood, PR [16258](https://github.com/python/mypy/pull/16258)) - * show dmypy errors post serving (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) * Fix partially defined in the case of missing type maps (Shantanu, PR [15995](https://github.com/python/mypy/pull/15995)) - * (🎁) drop 'dev' from 3.12 in the CI (KotlinIsland, PR [16239](https://github.com/python/mypy/pull/16239)) - * Add an extra for mypyc dependencies (Shantanu, PR [16229](https://github.com/python/mypy/pull/16229)) * Use SPDX license identifier (Nikita Sobolev, PR [16230](https://github.com/python/mypy/pull/16230)) - * Support variadic tuple packing/unpacking (Ivan Levkivskyi, PR [16205](https://github.com/python/mypy/pull/16205)) - * Remove stubs packages from `stubinfo.py` where the runtime package has added a `py.typed` file (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) - * Bump ruff and black to their latest versions (Alex Waygood, PR [16221](https://github.com/python/mypy/pull/16221)) - * __qualname__ and __module__ are available in class bodies (Anthony Sottile, PR [16215](https://github.com/python/mypy/pull/16215)) - * tests: avoid leaving artifacts in the source tree (Eli Schwartz, PR [16201](https://github.com/python/mypy/pull/16201)) - * Add meta test for new diff logic (Shantanu, PR [16211](https://github.com/python/mypy/pull/16211)) - * stubtest: hint when args in stub need to be keyword-only (Alex Waygood, PR [16210](https://github.com/python/mypy/pull/16210)) - * tuple slice should not propagate fallback (Thomas Grainger, PR [16154](https://github.com/python/mypy/pull/16154)) + * `__qualname__` and `__module__` are available in class bodies (Anthony Sottile, PR [16215](https://github.com/python/mypy/pull/16215)) + * stubtest: Hint when args in stub need to be keyword-only (Alex Waygood, PR [16210](https://github.com/python/mypy/pull/16210)) + * Tuple slice should not propagate fallback (Thomas Grainger, PR [16154](https://github.com/python/mypy/pull/16154)) * Fix cases of type object handling for overloads (Shantanu, PR [16168](https://github.com/python/mypy/pull/16168)) - * Fix error code on "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) - * Add more tests for variadic Callables (Ivan Levkivskyi, PR [16198](https://github.com/python/mypy/pull/16198)) * Fix walrus interaction with empty collections (Ivan Levkivskyi, PR [16197](https://github.com/python/mypy/pull/16197)) - * Better support for variadic calls and indexing (Ivan Levkivskyi, PR [16131](https://github.com/python/mypy/pull/16131)) * Use type variable bound when it appears as actual during inference (Ivan Levkivskyi, PR [16178](https://github.com/python/mypy/pull/16178)) * Use upper bounds as fallback solutions for inference (Ivan Levkivskyi, PR [16184](https://github.com/python/mypy/pull/16184)) * Special-case type inference of empty collections (Ivan Levkivskyi, PR [16122](https://github.com/python/mypy/pull/16122)) - * stubgen: multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) - * Fix typo in dataclasses.py (Ikko Eltociear Ashimine, PR [16173](https://github.com/python/mypy/pull/16173)) - * Remove `is_classmethod_class` slot from `CallableType` (Nikita Sobolev, PR [16151](https://github.com/python/mypy/pull/16151)) - * Do not consider `import a.b as b` an explicit reexport (Anders Kaseorg, PR [14086](https://github.com/python/mypy/pull/14086)) - * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) - * Fix inference for overloaded __call__ with generic self (Shantanu, PR [16053](https://github.com/python/mypy/pull/16053)) + * Fix inference for overloaded `__call__` with generic self (Shantanu, PR [16053](https://github.com/python/mypy/pull/16053)) * Call dynamic class hook on generic classes (Petter Friberg, PR [16052](https://github.com/python/mypy/pull/16052)) * Preserve implicitly exported types via attribute access (Shantanu, PR [16129](https://github.com/python/mypy/pull/16129)) - * Make it easier to copy commands from docs README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) - * Use comments in issue template (Hamir Mahal, PR [15742](https://github.com/python/mypy/pull/15742)) - * dataclass.replace: allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) - * Fix the newly-uncovered stubtest bug (Alex Waygood) - * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) - * stubgen: generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) - * Fix crash on malformed TypedDict in incremental mode (Ivan Levkivskyi, PR [16115](https://github.com/python/mypy/pull/16115)) - * Better diffs in tests (Shantanu, PR [16112](https://github.com/python/mypy/pull/16112)) - * Fix tuple[Any, ...] subtyping (Shantanu, PR [16108](https://github.com/python/mypy/pull/16108)) + * Fix a stubtest bug (Alex Waygood) + * Fix `tuple[Any, ...]` subtyping (Shantanu, PR [16108](https://github.com/python/mypy/pull/16108)) * Lenient handling of trivial Callable suffixes (Ivan Levkivskyi, PR [15913](https://github.com/python/mypy/pull/15913)) - * Subtyping and inference of user defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) - * [mypyc] Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) - * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) * Add `add_overloaded_method_to_class` helper to `plugins/common.py` (Nikita Sobolev, PR [16038](https://github.com/python/mypy/pull/16038)) - * Document and rename overload-overlap error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) - * Fix __post_init__() internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) - * Differentiate between venv and tox setups in CONTRIBUTING.md (Matt Bogosian, PR [16067](https://github.com/python/mypy/pull/16067)) - * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) - * Build the docs in CI for all PRs touching the `mypy/` directory (Alex Waygood, PR [16068](https://github.com/python/mypy/pull/16068)) - * Introduce error category [unsafe-overload] (Randolf Scholz, PR [16061](https://github.com/python/mypy/pull/16061)) - * Add docs about `--force-uppercase-builtins` and `--force-union-syntax` (Nikita Sobolev, PR [16049](https://github.com/python/mypy/pull/16049)) - * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) - * Use latest `actions/checkout@v4` (Nikita Sobolev, PR [16042](https://github.com/python/mypy/pull/16042)) - * Do not use deprecated `add_method` in `attrs` plugin (Nikita Sobolev, PR [16037](https://github.com/python/mypy/pull/16037)) - * Remove type aliases that are long supported (Nikita Sobolev, PR [16039](https://github.com/python/mypy/pull/16039)) - * Add type annotations to `test-data/unit/plugins` (Nikita Sobolev, PR [16028](https://github.com/python/mypy/pull/16028)) * Bundle `misc/proper_plugin.py` as a part of `mypy` (Nikita Sobolev, PR [16036](https://github.com/python/mypy/pull/16036)) - * Do not set `is_final` twice for `FuncBase` subclasses (Nikita Sobolev, PR [16030](https://github.com/python/mypy/pull/16030)) - * Exclude `assert False` from coverage (Nikita Sobolev, PR [16026](https://github.com/python/mypy/pull/16026)) - * ruff: add pyupgrade (Ilya Priven, PR [16023](https://github.com/python/mypy/pull/16023)) - * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) - * meta tests: refactor run_pytest (Ilya Priven, PR [15481](https://github.com/python/mypy/pull/15481)) - * docs: document dataclass_transform behavior (Ilya Priven, PR [16017](https://github.com/python/mypy/pull/16017)) - * Fix crash with report generation on namespace packages (again) (Shantanu, PR [16019](https://github.com/python/mypy/pull/16019)) - * Reword the error message related to void functions (Albert Tugushev, PR [15876](https://github.com/python/mypy/pull/15876)) * Fix case Any() in match statement (DS/Charlie, PR [14479](https://github.com/python/mypy/pull/14479)) - * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) * Deduplicate iterable logic (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) - * Represent bottom type as Never in messages (Shantanu, PR [15996](https://github.com/python/mypy/pull/15996)) - * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) - * Fix inference for properties with __call__ (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) - * attrs: remove fields type check (Ilya Priven, PR [15983](https://github.com/python/mypy/pull/15983)) - * dataclasses.replace: fall through to typeshed sig (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) - * Improve GitHub Actions specs (Nikita Sobolev, PR [15965](https://github.com/python/mypy/pull/15965)) - * attrs, dataclasses: don't enforce slots when base doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) + * Fix inference for properties with `__call__` (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) #### Acknowledgements ... From a1648f555636a5e3aeb99dbf59b394e6939c1344 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 15:00:29 +0000 Subject: [PATCH 0374/1617] Add typeshed and acknowledgements sections to 1.7 changelog (#16436) --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68efa2cbfc46..17b0084d6e5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,7 @@ TODO: Write sections about these? * Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) * Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) * Avoid cyclic reference in nested functions (Jukka Lehtosalo, PR [16268](https://github.com/python/mypy/pull/16268)) - * Fix direct __dict__ access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) + * Fix direct `__dict__` access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) * Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) #### Improvements to Error Reportong @@ -148,8 +148,45 @@ TODO: Write sections about these? * Deduplicate iterable logic (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) * Fix inference for properties with `__call__` (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) +#### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + #### Acknowledgements -... + +Thanks to all mypy contributors who contributed to this release: + +* Albert Tugushev +* Alex Waygood +* Ali Hamdan +* Anders Kaseorg +* Anthony Sottile +* Chad Dombrova +* Cibin Mathew +* dinaldoap +* DS/Charlie +* Eli Schwartz +* Ganden Schaffner +* Hamir Mahal +* Ihor +* Ikko Eltociear Ashimine +* Ilya Priven +* Ivan Levkivskyi +* Jelle Zijlstra +* Jukka Lehtosalo +* Jørgen Lind +* KotlinIsland +* Matt Bogosian +* Nikita Sobolev +* Petter Friberg +* Randolf Scholz +* Shantanu +* Thomas Grainger +* Valentin Stanciu + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +Posted by Jukka Lehtosalo ## Mypy 1.6 From cd1ce2fd396dc33c7d3b8049a830870e80d48e1b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 16:22:19 +0000 Subject: [PATCH 0375/1617] Improvements to changelog for mypy 1.7 (#16439) Add sections covering bigger changes. --- CHANGELOG.md | 78 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b0084d6e5d..2cc8da9db0de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Next release +Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). + ## Mypy 1.7 [unreleased] We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: @@ -12,11 +14,32 @@ We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). +#### Using TypedDict for `**kwargs` Typing -#### TypeVarTuple and Unpack Support Enabled +Mypy now has support for using `Unpack[...]` with a TypedDict type to annotate `**kwargs` arguments enabled by default. Example: + +``` +# Or 'from typing_extensions import ...' +from typing import TypedDict, Unpack -TODO: Explain +class Person(TypedDict): + name: str + age: int + +def foo(**kwargs: Unpack[Person]) -> None: + ... + +foo(name="x", age=1) # Ok +foo(name=1) # Error +``` + +Refer to [PEP 692](https://peps.python.org/pep-0692/) for the details. + +This was contributed by Ivan Levkivskyi back in 2022 ([PR 13471](https://github.com/python/mypy/pull/13471)). + +#### TypeVarTuple Support Enabled (Experimental) + +Mypy now has support for variadic generics (TypeVarTuple) enabled by default, as an experimental feature. Refer to [PEP 646](https://peps.python.org/pep-0646/) for the details. TypeVarTuple was implemented by Jared Hance and Ivan Levkivskyi over several mypy releases, with help from Jukka Lehtosalo. @@ -35,15 +58,50 @@ Changes included in this release: * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) -#### Major Changes +#### New Way of Installing Mypyc Dependencies + +If you want to install package dependencies needed by mypyc (not just mypy), you should now install `mypy[mypyc]` instead of just `mypy`: + +``` +python3 -m pip install -U 'mypy[mypyc]' +``` + +Mypy has many more users than mypyc, so always installing mypyc dependencies would often bring unnecessary dependencies. + +This change was contributed by Shantanu (PR [16229](https://github.com/python/mypy/pull/16229)). + +#### New Rules for Re-exports + +Mypy no longer considers an import such as `import a.b as b` as an explicit re-export. The old behavior was arguably inconsistent and surprising. This may impact some stub packages, such as older versions of `types-six`. You can change the import to `from a import b as b`, if treating the import as a re-export was intentional. + +This change was contributed by Anders Kaseorg (PR [14086](https://github.com/python/mypy/pull/14086)). + +#### Improved Type Inference + +The new type inference algorithm that was recently introduced to mypy (but was not enabled by default) is now enabled by default. It improves type inference of calls to generic callables where an argument is also a generic callable, in particular. You can use `--old-type-inference` to disable the new behavior. + +The new algorithm can (rarely) produce different error messages, different error codes, or errors reported on different lines. This is more likely in cases where generic types were used incorrectly. + +The new type inference algorithm was contributed by Ivan Levkivskyi. PR [16345](https://github.com/python/mypy/pull/16345) enabled it by default. + +#### Narrowing Tuple Types Using len() + +Mypy now can narrow tuple types using `len()` checks. Example: + +``` +def f(t: tuple[int, int] | tuple[int, int, int]) -> None: + if len(t) == 2: + a, b = t # Ok + ... +``` + +This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). + +#### Mypy Changelog -TODO: Write sections about these? +We now maintain a [changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) in the mypy Git repository. It mirrors the contents of [mypy release blog posts](https://mypy-lang.blogspot.com/). We will continue to also publish release blog posts. In the future, release blog posts will be created based on the changelog near a release date. - * Enable new type inference by default (Ivan Levkivskyi, PR [16345](https://github.com/python/mypy/pull/16345)) - * Narrow tuple types using len() (Ivan Levkivskyi, PR [16237](https://github.com/python/mypy/pull/16237)) - * Do not consider `import a.b as b` an explicit reexport (Anders Kaseorg, PR [14086](https://github.com/python/mypy/pull/14086)) - * Add an extra for mypyc dependencies (Shantanu, PR [16229](https://github.com/python/mypy/pull/16229)) - * Add a changelog (Shantanu, PR [16280](https://github.com/python/mypy/pull/16280)) +This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull/16280)). #### Mypy Daemon Improvements From 1dc07b3b3d703f0b7b4564fac99461d0144d5870 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 9 Nov 2023 16:49:06 +0000 Subject: [PATCH 0376/1617] Minor tweaks to 1.7 changelog (#16440) --- CHANGELOG.md | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc8da9db0de..bbc8e4714423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,14 +49,13 @@ Changes included in this release: * Fix crash on unpack call special-casing (Ivan Levkivskyi, PR [16381](https://github.com/python/mypy/pull/16381)) * Some final touches for variadic types support (Ivan Levkivskyi, PR [16334](https://github.com/python/mypy/pull/16334)) * Support PEP-646 and PEP-692 in the same callable (Ivan Levkivskyi, PR [16294](https://github.com/python/mypy/pull/16294)) - * Support fancy new syntax for variadic types (Ivan Levkivskyi, PR [16242](https://github.com/python/mypy/pull/16242)) + * Support new `*` syntax for variadic types (Ivan Levkivskyi, PR [16242](https://github.com/python/mypy/pull/16242)) * Correctly handle variadic instances with empty arguments (Ivan Levkivskyi, PR [16238](https://github.com/python/mypy/pull/16238)) * Correctly handle runtime type applications of variadic types (Ivan Levkivskyi, PR [16240](https://github.com/python/mypy/pull/16240)) * Support variadic tuple packing/unpacking (Ivan Levkivskyi, PR [16205](https://github.com/python/mypy/pull/16205)) * Better support for variadic calls and indexing (Ivan Levkivskyi, PR [16131](https://github.com/python/mypy/pull/16131)) - * Subtyping and inference of user defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) + * Subtyping and inference of user-defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) - * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) #### New Way of Installing Mypyc Dependencies @@ -110,10 +109,10 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix dmypy inspect on Windows (Ivan Levkivskyi, PR [16355](https://github.com/python/mypy/pull/16355)) * Fix dmypy inspect for namespace packages (Ivan Levkivskyi, PR [16357](https://github.com/python/mypy/pull/16357)) * Fix return type change to optional in generic function (Jukka Lehtosalo, PR [16342](https://github.com/python/mypy/pull/16342)) - * Fix daemon false positives related to module-level __getattr__ (Jukka Lehtosalo, PR [16292](https://github.com/python/mypy/pull/16292)) - * Attempt to fix daemon crash related to ABCs (Jukka Lehtosalo, PR [16275](https://github.com/python/mypy/pull/16275)) + * Fix daemon false positives related to module-level `__getattr__` (Jukka Lehtosalo, PR [16292](https://github.com/python/mypy/pull/16292)) + * Fix daemon crash related to ABCs (Jukka Lehtosalo, PR [16275](https://github.com/python/mypy/pull/16275)) * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) - * Show dmypy errors post serving (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) + * Make sure all dmypy errors are shown (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) #### Mypyc Improvements @@ -123,70 +122,70 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix direct `__dict__` access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) * Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) -#### Improvements to Error Reportong +#### Improvements to Error Reporting - * Update starred expr error message to match Python's (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) - * Add `unimported-reveal` error code (Nikita Sobolev, PR [16271](https://github.com/python/mypy/pull/16271)) - * Fix error code on "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) - * Introduce error category [unsafe-overload] (Randolf Scholz, PR [16061](https://github.com/python/mypy/pull/16061)) + * Update starred expression error message to match CPython (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) + * Fix error code of "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) + * Use error code `[unsafe-overload]` for unsafe overloads, instead of `[misc]` (Randolf Scholz, PR [16061](https://github.com/python/mypy/pull/16061)) * Reword the error message related to void functions (Albert Tugushev, PR [15876](https://github.com/python/mypy/pull/15876)) * Represent bottom type as Never in messages (Shantanu, PR [15996](https://github.com/python/mypy/pull/15996)) * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) - * Remove stubs packages from `stubinfo.py` where the runtime package has added a `py.typed` file (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) + * Don't suggest stubs packages where the runtime package now ships with types (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) #### Performance Improvements * Speed up type argument checking (Jukka Lehtosalo, PR [16353](https://github.com/python/mypy/pull/16353)) * Add fast path for checking self types (Jukka Lehtosalo, PR [16352](https://github.com/python/mypy/pull/16352)) * Cache information about whether file is typeshed file (Jukka Lehtosalo, PR [16351](https://github.com/python/mypy/pull/16351)) - * Skip expensive repr() in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) + * Skip expensive `repr()` in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) #### Attrs and Dataclass Improvements - * dataclass.replace: allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) - * docs: document dataclass_transform behavior (Ilya Priven, PR [16017](https://github.com/python/mypy/pull/16017)) - * dataclasses.replace: fall through to typeshed sig (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) - * attrs: remove fields type check (Ilya Priven, PR [15983](https://github.com/python/mypy/pull/15983)) - * attrs, dataclasses: don't enforce slots when base doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) + * `dataclass.replace`: Allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) + * `dataclass.replace`: Fall through to typeshed signature (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) + * Document `dataclass_transform` behavior (Ilya Priven, PR [16017](https://github.com/python/mypy/pull/16017)) + * `attrs`: Remove fields type check (Ilya Priven, PR [15983](https://github.com/python/mypy/pull/15983)) + * `attrs`, `dataclasses`: Don't enforce slots when base class doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) + * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) #### Stubgen Improvements * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) - * stubgen: fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) - * stubgen: unify C extension and pure python stub generators with object oriented design (Chad Dombrova, PR [15770](https://github.com/python/mypy/pull/15770)) - * stubgen: multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) - * stubgen: generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) + * Fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) + * Unify C extension and pure python stub generators with object oriented design (Chad Dombrova, PR [15770](https://github.com/python/mypy/pull/15770)) + * Multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) + * Generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) #### Fixes to Crashes - * Fix incremental crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) - * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) + * Fix incremental mode crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) * Fix crash on malformed TypedDict in incremental mode (Ivan Levkivskyi, PR [16115](https://github.com/python/mypy/pull/16115)) - * Fix crash with report generation on namespace packages (again) (Shantanu, PR [16019](https://github.com/python/mypy/pull/16019)) + * Fix crash with report generation on namespace packages (Shantanu, PR [16019](https://github.com/python/mypy/pull/16019)) * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) * Fix `__post_init__()` internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) #### Documentation Updates - * Make it easier to copy commands from docs README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) - * Document and rename overload-overlap error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) - * Add docs about `--force-uppercase-builtins` and `--force-union-syntax` (Nikita Sobolev, PR [16049](https://github.com/python/mypy/pull/16049)) + * Make it easier to copy commands from README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) + * Document and rename `[overload-overlap]` error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) + * Document `--force-uppercase-builtins` and `--force-union-syntax` (Nikita Sobolev, PR [16049](https://github.com/python/mypy/pull/16049)) * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) #### Other Notable Changes and Fixes - * Avoid importing from setuptools._distutils (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) + * Avoid importing from `setuptools._distutils` (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) * Delete recursive aliases flags (Ivan Levkivskyi, PR [16346](https://github.com/python/mypy/pull/16346)) * Properly use proper subtyping for callables (Ivan Levkivskyi, PR [16343](https://github.com/python/mypy/pull/16343)) * Use upper bound as inference fallback more consistently (Ivan Levkivskyi, PR [16344](https://github.com/python/mypy/pull/16344)) + * Add `[unimported-reveal]` error code (Nikita Sobolev, PR [16271](https://github.com/python/mypy/pull/16271)) * Add `|=` and `|` operators support for `TypedDict` (Nikita Sobolev, PR [16249](https://github.com/python/mypy/pull/16249)) * Clarify variance convention for Parameters (Ivan Levkivskyi, PR [16302](https://github.com/python/mypy/pull/16302)) * Correctly recognize `typing_extensions.NewType` (Ganden Schaffner, PR [16298](https://github.com/python/mypy/pull/16298)) * Fix partially defined in the case of missing type maps (Shantanu, PR [15995](https://github.com/python/mypy/pull/15995)) * Use SPDX license identifier (Nikita Sobolev, PR [16230](https://github.com/python/mypy/pull/16230)) - * `__qualname__` and `__module__` are available in class bodies (Anthony Sottile, PR [16215](https://github.com/python/mypy/pull/16215)) + * Make `__qualname__` and `__module__` available in class bodies (Anthony Sottile, PR [16215](https://github.com/python/mypy/pull/16215)) * stubtest: Hint when args in stub need to be keyword-only (Alex Waygood, PR [16210](https://github.com/python/mypy/pull/16210)) * Tuple slice should not propagate fallback (Thomas Grainger, PR [16154](https://github.com/python/mypy/pull/16154)) * Fix cases of type object handling for overloads (Shantanu, PR [16168](https://github.com/python/mypy/pull/16168)) @@ -194,16 +193,17 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Use type variable bound when it appears as actual during inference (Ivan Levkivskyi, PR [16178](https://github.com/python/mypy/pull/16178)) * Use upper bounds as fallback solutions for inference (Ivan Levkivskyi, PR [16184](https://github.com/python/mypy/pull/16184)) * Special-case type inference of empty collections (Ivan Levkivskyi, PR [16122](https://github.com/python/mypy/pull/16122)) + * Allow TypedDict unpacking in Callable types (Ivan Levkivskyi, PR [16083](https://github.com/python/mypy/pull/16083)) * Fix inference for overloaded `__call__` with generic self (Shantanu, PR [16053](https://github.com/python/mypy/pull/16053)) * Call dynamic class hook on generic classes (Petter Friberg, PR [16052](https://github.com/python/mypy/pull/16052)) * Preserve implicitly exported types via attribute access (Shantanu, PR [16129](https://github.com/python/mypy/pull/16129)) * Fix a stubtest bug (Alex Waygood) * Fix `tuple[Any, ...]` subtyping (Shantanu, PR [16108](https://github.com/python/mypy/pull/16108)) * Lenient handling of trivial Callable suffixes (Ivan Levkivskyi, PR [15913](https://github.com/python/mypy/pull/15913)) - * Add `add_overloaded_method_to_class` helper to `plugins/common.py` (Nikita Sobolev, PR [16038](https://github.com/python/mypy/pull/16038)) + * Add `add_overloaded_method_to_class` helper for plugins (Nikita Sobolev, PR [16038](https://github.com/python/mypy/pull/16038)) * Bundle `misc/proper_plugin.py` as a part of `mypy` (Nikita Sobolev, PR [16036](https://github.com/python/mypy/pull/16036)) - * Fix case Any() in match statement (DS/Charlie, PR [14479](https://github.com/python/mypy/pull/14479)) - * Deduplicate iterable logic (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) + * Fix `case Any()` in match statement (DS/Charlie, PR [14479](https://github.com/python/mypy/pull/14479)) + * Make iterable logic more consistent (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) * Fix inference for properties with `__call__` (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) #### Typeshed Updates From b67dc53ad5bee837ea0de7ece9f9e14c634a923e Mon Sep 17 00:00:00 2001 From: robjhornby Date: Fri, 10 Nov 2023 00:59:16 +0000 Subject: [PATCH 0377/1617] Handle TypeVarTupleType when checking overload constraints (#16428) Fixes https://github.com/python/mypy/issues/16427 The test case added in the first commit crashes. The second commit addresses the crash - I don't know whether this fix is correct, it just happens to stop the crash but it leads to a code branch which just `continue`s out of a for loop iteration, so it might be bypassing something it shouldn't. I don't completely understand it. --------- Co-authored-by: Ivan Levkivskyi --- mypy/constraints.py | 2 +- test-data/unit/check-typevar-tuple.test | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 49e542a49e56..88ede372e011 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -949,7 +949,7 @@ def visit_instance(self, template: Instance) -> list[Constraint]: for item in actual.items: if isinstance(item, UnpackType): unpacked = get_proper_type(item.type) - if isinstance(unpacked, TypeVarType): + if isinstance(unpacked, TypeVarTupleType): # Cannot infer anything for T from [T, ...] <: *Ts continue assert ( diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index e85863f0ed04..25babf442d21 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1789,6 +1789,24 @@ def test(a: Container[Any], b: Container[int], c: Container[str]): reveal_type(build(b, c)) # N: Revealed type is "__main__.Array[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] +[case testTypeVarTupleOverloadArbitraryLength] +from typing import Any, Tuple, TypeVar, TypeVarTuple, Unpack, overload + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +@overload +def add(self: Tuple[Unpack[Ts]], other: Tuple[T]) -> Tuple[Unpack[Ts], T]: + ... +@overload +def add(self: Tuple[T, ...], other: Tuple[T, ...]) -> Tuple[T, ...]: + ... +def add(self: Any, other: Any) -> Any: + ... +def test(a: Tuple[int, str], b: Tuple[bool], c: Tuple[bool, ...]): + reveal_type(add(a, b)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" + reveal_type(add(b, c)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleIndexOldStyleNonNormalizedAndNonLiteral] from typing import Any, Tuple from typing_extensions import Unpack From f05663d31e6d9097b152ecf5825b8d9dc50cea54 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 10 Nov 2023 11:32:05 +0000 Subject: [PATCH 0378/1617] Fix handling of tuple type context with unpacks (#16444) Fixes https://github.com/python/mypy/issues/16425 Fix is straightforward. --- mypy/checkexpr.py | 10 +++++----- test-data/unit/check-typevar-tuple.test | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 056b2f7bd2c6..c87d1f6cd31c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4902,7 +4902,7 @@ def tuple_context_matches(self, expr: TupleExpr, ctx: TupleType) -> bool: return len([e for e in expr.items if not isinstance(e, StarExpr)]) <= len(ctx.items) # For variadic context, the only easy case is when structure matches exactly. # TODO: try using tuple type context in more cases. - if len([e for e in expr.items if not isinstance(e, StarExpr)]) != 1: + if len([e for e in expr.items if isinstance(e, StarExpr)]) != 1: return False expr_star_index = next(i for i, lv in enumerate(expr.items) if isinstance(lv, StarExpr)) return len(expr.items) == len(ctx.items) and ctx_unpack_index == expr_star_index @@ -4941,6 +4941,9 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: if type_context_items is not None: unpack_in_context = find_unpack_in_list(type_context_items) is not None seen_unpack_in_items = False + allow_precise_tuples = ( + unpack_in_context or PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature + ) # Infer item types. Give up if there's a star expression # that's not a Tuple. @@ -4981,10 +4984,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: # result in an error later, just do something predictable here. j += len(tt.items) else: - if ( - PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature - and not seen_unpack_in_items - ): + if allow_precise_tuples and not seen_unpack_in_items: # Handle (x, *y, z), where y is e.g. tuple[Y, ...]. if isinstance(tt, Instance) and self.chk.type_is_iterable(tt): item_type = self.chk.iterable_item_type(tt, e) diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 25babf442d21..487f22699724 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2225,3 +2225,13 @@ bar(*keys, 1) # OK reveal_type(baz(keys, 1)) # N: Revealed type is "builtins.object" reveal_type(baz(*keys, 1)) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testVariadicTupleContextNoCrash] +from typing import Tuple, Unpack + +x: Tuple[int, Unpack[Tuple[int, ...]]] = () # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[int, Unpack[Tuple[int, ...]]]") +y: Tuple[int, Unpack[Tuple[int, ...]]] = (1, 2) +z: Tuple[int, Unpack[Tuple[int, ...]]] = (1,) +w: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *[2, 3, 4]) +t: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *(2, 3, 4)) +[builtins fixtures/tuple.pyi] From ec9004677c4e1b749cab89494e60f0c6727d2162 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 10 Nov 2023 11:56:16 +0000 Subject: [PATCH 0379/1617] Changelog updates for mypy 1.7 (#16446) Address feedback and include recently cherry-picked PRs. --- CHANGELOG.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc8e4714423..9b3cd19e6df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,14 @@ foo(name="x", age=1) # Ok foo(name=1) # Error ``` -Refer to [PEP 692](https://peps.python.org/pep-0692/) for the details. +The definition of `foo` above is equivalent to the one below, with keyword-only arguments `name` and `age`: + +``` +def foo(*, name: str, age: int) -> None: + ... +``` + +Refer to [PEP 692](https://peps.python.org/pep-0692/) for more information. Note that unlike in the current version of the PEP, mypy always treats signatures with `Unpack[SomeTypedDict]` as equivalent to their expanded forms with explicit keyword arguments, and there aren't special type checking rules for TypedDict arguments. This was contributed by Ivan Levkivskyi back in 2022 ([PR 13471](https://github.com/python/mypy/pull/13471)). @@ -45,6 +52,8 @@ TypeVarTuple was implemented by Jared Hance and Ivan Levkivskyi over several myp Changes included in this release: + * Fix handling of tuple type context with unpacks (Ivan Levkivskyi, PR [16444](https://github.com/python/mypy/pull/16444)) + * Handle TypeVarTuples when checking overload constraints (robjhornby, PR [16428](https://github.com/python/mypy/pull/16428)) * Enable Unpack/TypeVarTuple support (Ivan Levkivskyi, PR [16354](https://github.com/python/mypy/pull/16354)) * Fix crash on unpack call special-casing (Ivan Levkivskyi, PR [16381](https://github.com/python/mypy/pull/16381)) * Some final touches for variadic types support (Ivan Levkivskyi, PR [16334](https://github.com/python/mypy/pull/16334)) @@ -96,6 +105,14 @@ def f(t: tuple[int, int] | tuple[int, int, int]) -> None: This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). +#### More Precise Tuple Lenghts (Experimental) + +Mypy supports experimental, more precise checking of tuple type lengths through `--enable-incomplete-feature=PreciseTupleTypes`. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#enabling-incomplete-experimental-features) for more information. + +More generally, we are planning to use `--enable-incomplete-feature` to introduce experimental features that would benefit from community feedback. + +This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). + #### Mypy Changelog We now maintain a [changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) in the mypy Git repository. It mirrors the contents of [mypy release blog posts](https://mypy-lang.blogspot.com/). We will continue to also publish release blog posts. In the future, release blog posts will be created based on the changelog near a release date. @@ -175,6 +192,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull #### Other Notable Changes and Fixes + * Propagate narrowed types to lambda expressions (Ivan Levkivskyi, PR [16407](https://github.com/python/mypy/pull/16407)) * Avoid importing from `setuptools._distutils` (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) * Delete recursive aliases flags (Ivan Levkivskyi, PR [16346](https://github.com/python/mypy/pull/16346)) * Properly use proper subtyping for callables (Ivan Levkivskyi, PR [16343](https://github.com/python/mypy/pull/16343)) From 85f405a4cd8c00f1297609829c8c5d12f66fae9d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 10 Nov 2023 14:19:23 +0000 Subject: [PATCH 0380/1617] Script to generate release blog post from changelog (#16447) This is an adaptation of an older script that we used to convert from a Paper doc to HTML. This includes changes by @gvanrossum and @svalentin. --- misc/gen_blog_post_html.py | 171 +++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 misc/gen_blog_post_html.py diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py new file mode 100644 index 000000000000..7170696d5d09 --- /dev/null +++ b/misc/gen_blog_post_html.py @@ -0,0 +1,171 @@ +"""Converter from CHANGELOG.md (Markdown) to HTML suitable for a mypy blog post. + +How to use: + +1. Write release notes in CHANGELOG.md. +2. Make sure the heading for the next release is of form `## Mypy X.Y`. +2. Run `misc/gen_blog_post_html.py X.Y > target.html`. +4. Manually inspect and tweak the result. + +Notes: + +* There are some fragile assumptions. Double check the output. +""" + +import argparse +import html +import os +import re +import sys + + +def format_lists(h: str) -> str: + a = h.splitlines() + r = [] + i = 0 + bullets = ("- ", "* ", " * ") + while i < len(a): + if a[i].startswith(bullets): + r.append("

    ") + while i < len(a) and a[i].startswith(bullets): + r.append("
  • %s" % a[i][2:].lstrip()) + i += 1 + r.append("
") + else: + r.append(a[i]) + i += 1 + return "\n".join(r) + + +def format_code(h: str) -> str: + a = h.splitlines() + r = [] + i = 0 + while i < len(a): + if a[i].startswith(" ") or a[i].startswith("```"): + indent = a[i].startswith(" ") + if not indent: + i += 1 + r.append("
")
+            while i < len(a) and (
+                (indent and a[i].startswith("    ")) or (not indent and not a[i].startswith("```"))
+            ):
+                # Undo > and <
+                line = a[i].replace(">", ">").replace("<", "<")
+                if not indent:
+                    line = "    " + line
+                r.append(html.escape(line))
+                i += 1
+            r.append("
") + if not indent and a[i].startswith("```"): + i += 1 + else: + r.append(a[i]) + i += 1 + return "\n".join(r) + + +def convert(src: str) -> str: + h = src + + # Replace < and >. + h = re.sub(r"<", "<", h) + h = re.sub(r">", ">", h) + + # Title + h = re.sub(r"^## (Mypy [0-9.]+)", r"

\1 Released

", h, flags=re.MULTILINE) + + # Subheadings + h = re.sub(r"\n#### ([A-Z`].*)\n", r"\n

\1

\n", h) + + # Sub-subheadings + h = re.sub(r"\n\*\*([A-Z_`].*)\*\*\n", r"\n

\1

\n", h) + h = re.sub(r"\n`\*\*([A-Z_`].*)\*\*\n", r"\n

`\1

\n", h) + + # Translate `**` + h = re.sub(r"`\*\*`", "**", h) + + # Paragraphs + h = re.sub(r"\n([A-Z])", r"\n

\1", h) + + # Bullet lists + h = format_lists(h) + + # Code blocks + h = format_code(h) + + # Code fragments + h = re.sub(r"`([^`]+)`", r"\1", h) + + # Remove **** noise + h = re.sub(r"\*\*\*\*", "", h) + + # Bold text + h = re.sub(r"\*\*([A-Za-z].*?)\*\*", r" \1", h) + + # Emphasized text + h = re.sub(r" \*([A-Za-z].*?)\*", r" \1", h) + + # Remove redundant PR links to avoid double links (they will be generated below) + h = re.sub(r"\[(#[0-9]+)\]\(https://github.com/python/mypy/pull/[0-9]+/?\)", r"\1", h) + + # Issue and PR links + h = re.sub(r"\((#[0-9]+)\) +\(([^)]+)\)", r"(\2, \1)", h) + h = re.sub( + r"fixes #([0-9]+)", + r'fixes issue \1', + h, + ) + h = re.sub(r"#([0-9]+)", r'PR \1', h) + h = re.sub(r"\) \(PR", ", PR", h) + + # Markdown links + h = re.sub(r"\[([^]]*)\]\(([^)]*)\)", r'\1', h) + + # Add random links in case they are missing + h = re.sub( + r"contributors to typeshed:", + 'contributors to typeshed:', + h, + ) + + # Add missing top-level HTML tags + h = '\n\n\n' + h + "\n" + + return h + + +def extract_version(src: str, version: str) -> str: + a = src.splitlines() + i = 0 + heading = f"## Mypy {version}" + while i < len(a): + if a[i].strip() == heading: + break + i += 1 + else: + raise RuntimeError(f"Can't find heading {heading!r}") + j = i + 1 + while not a[j].startswith("## "): + j += 1 + return "\n".join(a[i:j]) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate HTML release blog post based on CHANGELOG.md and write to stdout." + ) + parser.add_argument("version", help="mypy version, in form X.Y or X.Y.Z") + args = parser.parse_args() + version: str = args.version + if not re.match(r"[0-9]+(\.[0-9]+)+$", version): + sys.exit(f"error: Version must be of form X.Y or X.Y.Z, not {version!r}") + changelog_path = os.path.join(os.path.dirname(__file__), os.path.pardir, "CHANGELOG.md") + src = open(changelog_path).read() + src = extract_version(src, version) + dst = convert(src) + sys.stdout.write(dst) + + +if __name__ == "__main__": + main() From 4f020e7b6f12b3d0c32372667d99dbc13b715429 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 10 Nov 2023 14:25:45 +0000 Subject: [PATCH 0381/1617] Mark mypy 1.7 changelog as final/released (#16448) We are now very close to release. --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b3cd19e6df1..d59fe36901be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,11 @@ Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). -## Mypy 1.7 [unreleased] +## Mypy 1.7 We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: -``` python3 -m pip install -U mypy -``` You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). @@ -42,7 +40,7 @@ def foo(*, name: str, age: int) -> None: Refer to [PEP 692](https://peps.python.org/pep-0692/) for more information. Note that unlike in the current version of the PEP, mypy always treats signatures with `Unpack[SomeTypedDict]` as equivalent to their expanded forms with explicit keyword arguments, and there aren't special type checking rules for TypedDict arguments. -This was contributed by Ivan Levkivskyi back in 2022 ([PR 13471](https://github.com/python/mypy/pull/13471)). +This was contributed by Ivan Levkivskyi back in 2022 (PR [13471](https://github.com/python/mypy/pull/13471)). #### TypeVarTuple Support Enabled (Experimental) From ed03aff13d423fd7da836b4a414ba6787166d993 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 10 Nov 2023 15:11:02 +0000 Subject: [PATCH 0382/1617] Fix typo in changelog (#16449) I also updated the blog post. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d59fe36901be..a5523894a524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,7 +103,7 @@ def f(t: tuple[int, int] | tuple[int, int, int]) -> None: This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). -#### More Precise Tuple Lenghts (Experimental) +#### More Precise Tuple Lengths (Experimental) Mypy supports experimental, more precise checking of tuple type lengths through `--enable-incomplete-feature=PreciseTupleTypes`. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#enabling-incomplete-experimental-features) for more information. From c68bd7ae2cffe8f0377ea9aab54b963b9fac3231 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Nov 2023 12:59:03 +0000 Subject: [PATCH 0383/1617] Fix crash on Callable self in __call__ (#16453) Fixes https://github.com/python/mypy/issues/16450 The fix is a bit ad-hoc, but OTOH there is nothing meaningful we can infer in such situation, so it is probably OK. --- mypy/typeops.py | 12 ++++++++---- test-data/unit/check-selftype.test | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 2eb3b284e729..e92fad0e872c 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -244,15 +244,15 @@ class C(D[E[T]], Generic[T]): ... return expand_type_by_instance(typ, inst_type) -def supported_self_type(typ: ProperType) -> bool: +def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool: """Is this a supported kind of explicit self-types? - Currently, this means a X or Type[X], where X is an instance or + Currently, this means an X or Type[X], where X is an instance or a type variable with an instance upper bound. """ if isinstance(typ, TypeType): return supported_self_type(typ.item) - if isinstance(typ, CallableType): + if allow_callable and isinstance(typ, CallableType): # Special case: allow class callable instead of Type[...] as cls annotation, # as well as callable self for callback protocols. return True @@ -306,7 +306,11 @@ class B(A): pass self_param_type = get_proper_type(func.arg_types[0]) variables: Sequence[TypeVarLikeType] - if func.variables and supported_self_type(self_param_type): + # Having a def __call__(self: Callable[...], ...) can cause infinite recursion. Although + # this special-casing looks not very principled, there is nothing meaningful we can infer + # from such definition, since it is inherently indefinitely recursive. + allow_callable = func.name is None or not func.name.startswith("__call__ of") + if func.variables and supported_self_type(self_param_type, allow_callable=allow_callable): from mypy.infer import infer_type_arguments if original_type is None: diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 29abe9cb025b..e49a7a0e2e2f 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2056,3 +2056,18 @@ reveal_type(C.copy(c)) # N: Revealed type is "__main__.C[builtins.int, builtins B.copy(42) # E: Value of type variable "Self" of "copy" of "B" cannot be "int" C.copy(42) # E: Value of type variable "Self" of "copy" of "B" cannot be "int" [builtins fixtures/tuple.pyi] + +[case testRecursiveSelfTypeCallMethodNoCrash] +from typing import Callable, TypeVar + +T = TypeVar("T") +class Partial: + def __call__(self: Callable[..., T]) -> T: ... + +class Partial2: + def __call__(self: Callable[..., T], x: T) -> T: ... + +p: Partial +reveal_type(p()) # N: Revealed type is "Never" +p2: Partial2 +reveal_type(p2(42)) # N: Revealed type is "builtins.int" From a7c53d33be8e7620d14ecfba496aa912e95bf4fa Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Sat, 11 Nov 2023 21:06:38 +0100 Subject: [PATCH 0384/1617] Support Sphinx 7.x (#16460) This is needed for the Debian package of mypy, as we package the docs and we have upgraded to Sphinx 7.2.6 from 5.3.0 for the next release. Thanks! Bonus: Adjust many links to docs.python.org so that intersphinx can connect them to local file for offline use. --- docs/requirements-docs.txt | 2 +- docs/source/class_basics.rst | 5 ++--- docs/source/config_file.rst | 6 ++---- docs/source/error_code_list2.rst | 3 +-- docs/source/getting_started.rst | 3 +-- docs/source/html_builder.py | 5 +++-- docs/source/more_types.rst | 2 +- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 395964ad9d44..a3504b07824d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,2 +1,2 @@ -sphinx>=4.2.0,<5.0.0 +sphinx>=5.1.0 furo>=2022.3.4 diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 73f95f1c5658..1d80da5830ec 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -263,7 +263,7 @@ effect at runtime: Abstract base classes and multiple inheritance ********************************************** -Mypy supports Python :doc:`abstract base classes ` (ABCs). Abstract classes +Mypy supports Python :doc:`abstract base classes ` (ABCs). Abstract classes have at least one abstract method or property that must be implemented by any *concrete* (non-abstract) subclass. You can define abstract base classes using the :py:class:`abc.ABCMeta` metaclass and the :py:func:`@abc.abstractmethod ` @@ -371,8 +371,7 @@ property or an instance variable. Slots ***** -When a class has explicitly defined -`__slots__ `_, +When a class has explicitly defined :std:term:`__slots__`, mypy will check that all attributes assigned to are members of ``__slots__``: .. code-block:: python diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index b5ce23ff11ec..de769200bf2b 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -238,10 +238,8 @@ section of the command line docs. Crafting a single regular expression that excludes multiple files while remaining human-readable can be a challenge. The above example demonstrates one approach. ``(?x)`` enables the ``VERBOSE`` flag for the subsequent regular expression, which - `ignores most whitespace and supports comments`__. The above is equivalent to: - ``(^one\.py$|two\.pyi$|^three\.)``. - - .. __: https://docs.python.org/3/library/re.html#re.X + :py:data:`ignores most whitespace and supports comments `. + The above is equivalent to: ``(^one\.py$|two\.pyi$|^three\.)``. For more details, see :option:`--exclude `. diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 9e24f21909d5..60f870c57db9 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -524,8 +524,7 @@ that only existed during type-checking. In runtime it fails with expected ``NameError``, which can cause real problem in production, hidden from mypy. -But, in Python3.11 ``reveal_type`` -`was added to typing.py `_. +But, in Python3.11 :py:func:`typing.reveal_type` was added. ``typing_extensions`` ported this helper to all supported Python versions. Now users can actually import ``reveal_type`` to make the runtime code safe. diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 463c73b2fe76..7ea4ddd148ea 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -256,8 +256,7 @@ Mypy can also understand how to work with types from libraries that you use. For instance, mypy comes out of the box with an intimate knowledge of the Python standard library. For example, here is a function which uses the -``Path`` object from the -`pathlib standard library module `_: +``Path`` object from the :doc:`pathlib standard library module `: .. code-block:: python diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py index 3064833b5631..ea3594e0617b 100644 --- a/docs/source/html_builder.py +++ b/docs/source/html_builder.py @@ -9,11 +9,12 @@ from sphinx.addnodes import document from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.environment import BuildEnvironment class MypyHTMLBuilder(StandaloneHTMLBuilder): - def __init__(self, app: Sphinx) -> None: - super().__init__(app) + def __init__(self, app: Sphinx, env: BuildEnvironment) -> None: + super().__init__(app, env) self._ref_to_doc = {} def write_doc(self, docname: str, doctree: document) -> None: diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index b27764a9e87c..cb3ef64b39a7 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -829,7 +829,7 @@ Typing async/await Mypy lets you type coroutines that use the ``async/await`` syntax. For more information regarding coroutines, see :pep:`492` and the -`asyncio documentation `_. +`asyncio documentation `_. Functions defined using ``async def`` are typed similar to normal functions. The return type annotation should be the same as the type of the value you From 8ae84edc3033c3cfa6a6dbfb920d859a9be4277d Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 11 Nov 2023 12:07:58 -0800 Subject: [PATCH 0385/1617] stubtest: special case final and deprecated (#16457) We should probably lean into the type checker harder here Fixes #14950 Fixes https://github.com/python/typeshed/pull/11009#issuecomment-1805013903 --- mypy/stubtest.py | 7 +++++++ mypy/test/teststubtest.py | 19 +++++++++++++++++++ mypy/types.py | 3 +++ 3 files changed, 29 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index e80ea4eac71f..ae410ff2ba6b 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1215,6 +1215,12 @@ def _resolve_funcitem_from_decorator(dec: nodes.OverloadPart) -> nodes.FuncItem def apply_decorator_to_funcitem( decorator: nodes.Expression, func: nodes.FuncItem ) -> nodes.FuncItem | None: + if ( + isinstance(decorator, nodes.CallExpr) + and isinstance(decorator.callee, nodes.RefExpr) + and decorator.callee.fullname in mypy.types.DEPRECATED_TYPE_NAMES + ): + return func if not isinstance(decorator, nodes.RefExpr): return None if not decorator.fullname: @@ -1223,6 +1229,7 @@ def apply_decorator_to_funcitem( if ( decorator.fullname in ("builtins.staticmethod", "abc.abstractmethod") or decorator.fullname in mypy.types.OVERLOAD_NAMES + or decorator.fullname in mypy.types.FINAL_DECORATOR_NAMES ): return func if decorator.fullname == "builtins.classmethod": diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index a52d9ef5de31..0c1817202f1f 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -71,6 +71,8 @@ class Sequence(Iterable[_T_co]): ... class Tuple(Sequence[_T_co]): ... class NamedTuple(tuple[Any, ...]): ... def overload(func: _T) -> _T: ... +def deprecated(__msg: str) -> Callable[[_T], _T]: ... +def final(func: _T) -> _T: ... """ stubtest_builtins_stub = """ @@ -630,6 +632,23 @@ def f5(__b: str) -> str: ... runtime="def f5(x, /): pass", error=None, ) + yield Case( + stub=""" + from typing import deprecated, final + class Foo: + @overload + @final + def f6(self, __a: int) -> int: ... + @overload + @deprecated("evil") + def f6(self, __b: str) -> str: ... + """, + runtime=""" + class Foo: + def f6(self, x, /): pass + """, + error=None, + ) @collect_cases def test_property(self) -> Iterator[Case]: diff --git a/mypy/types.py b/mypy/types.py index 43003a9a22b6..b100cf569086 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -119,6 +119,9 @@ # Supported Annotated type names. ANNOTATED_TYPE_NAMES: Final = ("typing.Annotated", "typing_extensions.Annotated") +# Supported @deprecated type names +DEPRECATED_TYPE_NAMES: Final = ("typing.deprecated", "typing_extensions.deprecated") + # We use this constant in various places when checking `tuple` subtyping: TUPLE_LIKE_INSTANCE_NAMES: Final = ( "builtins.tuple", From f7a05300f70321e18904d908c66522cc792d4123 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 11 Nov 2023 12:09:29 -0800 Subject: [PATCH 0386/1617] stubtest: warn about missing __del__ (#16456) Fixes #16414 --- mypy/stubtest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index ae410ff2ba6b..adffe2003ad4 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1381,7 +1381,6 @@ def verify_typealias( "__annotations__", "__text_signature__", "__weakref__", - "__del__", # Only ever called when an object is being deleted, who cares? "__hash__", "__getattr__", # resulting behaviour might be typed explicitly "__setattr__", # defining this on a class can cause worse type checking From e4c43cb68b742f0f51759565d1d3c4a722d16f55 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 12 Nov 2023 00:00:11 +0300 Subject: [PATCH 0387/1617] [stubtest] support `@type_check_only` decorator (#16422) There are several `TODO` items for the future (not in this PR): - [ ] Add an error code to disallow importing things that are decorated with `@type_check_only` - [ ] Support `@overload`ed functions. But, how? There are two options: we can treat individual overload cases as `@type_check_only` or we can treat the whole func. Since `typeshed` does not have any examples of this, I prefer to defer this discussion to somewhere else and support this when we decide Refs https://github.com/python/mypy/issues/15146 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alex Waygood --- mypy/nodes.py | 7 ++++ mypy/semanal.py | 6 ++++ mypy/stubtest.py | 27 +++++++++++++++ mypy/test/teststubtest.py | 45 +++++++++++++++++++++++++ mypy/types.py | 3 ++ test-data/unit/fixtures/typing-full.pyi | 3 ++ 6 files changed, 91 insertions(+) diff --git a/mypy/nodes.py b/mypy/nodes.py index d65a23a6b7fe..17e06613d1e3 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -513,6 +513,7 @@ class FuncBase(Node): "is_static", # Uses "@staticmethod" (explicit or implicit) "is_final", # Uses "@final" "is_explicit_override", # Uses "@override" + "is_type_check_only", # Uses "@type_check_only" "_fullname", ) @@ -530,6 +531,7 @@ def __init__(self) -> None: self.is_static = False self.is_final = False self.is_explicit_override = False + self.is_type_check_only = False # Name with module prefix self._fullname = "" @@ -2866,6 +2868,7 @@ class is generic then it will be a type constructor of higher kind. "type_var_tuple_suffix", "self_type", "dataclass_transform_spec", + "is_type_check_only", ) _fullname: str # Fully qualified name @@ -3016,6 +3019,9 @@ class is generic then it will be a type constructor of higher kind. # Added if the corresponding class is directly decorated with `typing.dataclass_transform` dataclass_transform_spec: DataclassTransformSpec | None + # Is set to `True` when class is decorated with `@typing.type_check_only` + is_type_check_only: bool + FLAGS: Final = [ "is_abstract", "is_enum", @@ -3072,6 +3078,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None self.metadata = {} self.self_type = None self.dataclass_transform_spec = None + self.is_type_check_only = False def add_type_vars(self) -> None: self.has_type_var_tuple_type = False diff --git a/mypy/semanal.py b/mypy/semanal.py index 6f322af816ea..68f0d04e77ca 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -251,6 +251,7 @@ REVEAL_TYPE_NAMES, TPDICT_NAMES, TYPE_ALIAS_NAMES, + TYPE_CHECK_ONLY_NAMES, TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, @@ -1568,6 +1569,9 @@ def visit_decorator(self, dec: Decorator) -> None: removed.append(i) else: self.fail("@final cannot be used with non-method functions", d) + elif refers_to_fullname(d, TYPE_CHECK_ONLY_NAMES): + # TODO: support `@overload` funcs. + dec.func.is_type_check_only = True elif isinstance(d, CallExpr) and refers_to_fullname( d.callee, DATACLASS_TRANSFORM_NAMES ): @@ -1868,6 +1872,8 @@ def analyze_class_decorator(self, defn: ClassDef, decorator: Expression) -> None self.fail("@runtime_checkable can only be used with protocol classes", defn) elif decorator.fullname in FINAL_DECORATOR_NAMES: defn.info.is_final = True + elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES): + defn.info.is_type_check_only = True elif isinstance(decorator, CallExpr) and refers_to_fullname( decorator.callee, DATACLASS_TRANSFORM_NAMES ): diff --git a/mypy/stubtest.py b/mypy/stubtest.py index adffe2003ad4..46468e8e18e0 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -484,6 +484,19 @@ def _verify_metaclass( def verify_typeinfo( stub: nodes.TypeInfo, runtime: MaybeMissing[type[Any]], object_path: list[str] ) -> Iterator[Error]: + if stub.is_type_check_only: + # This type only exists in stubs, we only check that the runtime part + # is missing. Other checks are not required. + if not isinstance(runtime, Missing): + yield Error( + object_path, + 'is marked as "@type_check_only", but also exists at runtime', + stub, + runtime, + stub_desc=repr(stub), + ) + return + if isinstance(runtime, Missing): yield Error(object_path, "is not present at runtime", stub, runtime, stub_desc=repr(stub)) return @@ -1066,6 +1079,7 @@ def verify_var( def verify_overloadedfuncdef( stub: nodes.OverloadedFuncDef, runtime: MaybeMissing[Any], object_path: list[str] ) -> Iterator[Error]: + # TODO: support `@type_check_only` decorator if isinstance(runtime, Missing): yield Error(object_path, "is not present at runtime", stub, runtime) return @@ -1260,6 +1274,19 @@ def apply_decorator_to_funcitem( def verify_decorator( stub: nodes.Decorator, runtime: MaybeMissing[Any], object_path: list[str] ) -> Iterator[Error]: + if stub.func.is_type_check_only: + # This function only exists in stubs, we only check that the runtime part + # is missing. Other checks are not required. + if not isinstance(runtime, Missing): + yield Error( + object_path, + 'is marked as "@type_check_only", but also exists at runtime', + stub, + runtime, + stub_desc=repr(stub), + ) + return + if isinstance(runtime, Missing): yield Error(object_path, "is not present at runtime", stub, runtime) return diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 0c1817202f1f..6a973d16d7bc 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -71,6 +71,7 @@ class Sequence(Iterable[_T_co]): ... class Tuple(Sequence[_T_co]): ... class NamedTuple(tuple[Any, ...]): ... def overload(func: _T) -> _T: ... +def type_check_only(func: _T) -> _T: ... def deprecated(__msg: str) -> Callable[[_T], _T]: ... def final(func: _T) -> _T: ... """ @@ -2046,6 +2047,50 @@ def some(self) -> int: ... error=None, ) + @collect_cases + def test_type_check_only(self) -> Iterator[Case]: + yield Case( + stub="from typing import type_check_only, overload", + runtime="from typing import overload", + error=None, + ) + # You can have public types that are only defined in stubs + # with `@type_check_only`: + yield Case( + stub=""" + @type_check_only + class A1: ... + """, + runtime="", + error=None, + ) + # Having `@type_check_only` on a type that exists at runtime is an error + yield Case( + stub=""" + @type_check_only + class A2: ... + """, + runtime="class A2: ...", + error="A2", + ) + # The same is true for functions: + yield Case( + stub=""" + @type_check_only + def func1() -> None: ... + """, + runtime="", + error=None, + ) + yield Case( + stub=""" + @type_check_only + def func2() -> None: ... + """, + runtime="def func2() -> None: ...", + error="func2", + ) + def remove_color_code(s: str) -> str: return re.sub("\\x1b.*?m", "", s) # this works! diff --git a/mypy/types.py b/mypy/types.py index b100cf569086..e7738bd7d088 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -113,6 +113,9 @@ # Supported @final decorator names. FINAL_DECORATOR_NAMES: Final = ("typing.final", "typing_extensions.final") +# Supported @type_check_only names. +TYPE_CHECK_ONLY_NAMES: Final = ("typing.type_check_only", "typing_extensions.type_check_only") + # Supported Literal type names. LITERAL_TYPE_NAMES: Final = ("typing.Literal", "typing_extensions.Literal") diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index ef903ace78af..ca8a2413f05f 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -196,3 +196,6 @@ def override(__arg: T) -> T: ... # Was added in 3.11 def reveal_type(__obj: T) -> T: ... + +# Only exists in type checking time: +def type_check_only(__func_or_class: T) -> T: ... From 8f3fe7c0768ffcbcce760b3088702d95f3c57220 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Nov 2023 09:54:52 +0000 Subject: [PATCH 0388/1617] Exclude private attributes from override checks (#16464) Fixes https://github.com/python/mypy/issues/9910 Fixes https://github.com/python/mypy/issues/16452 We already exclude private names from override type compatibility checks etc., but it looks like some override checks were still performed, we need to skip them, since private name is actually a different name in subclass. --------- Co-authored-by: Alex Waygood --- mypy/checker.py | 7 +++++-- test-data/unit/check-dataclasses.test | 13 +++++++++++++ test-data/unit/check-final.test | 13 +++++++++++++ test-data/unit/check-functions.test | 10 ++++++++++ .../unit/fine-grained-dataclass-transform.test | 4 ++-- test-data/unit/fixtures/dataclasses.pyi | 5 +++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e4eb58d40715..b9a9d3affb90 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1879,6 +1879,7 @@ def check_explicit_override_decorator( found_method_base_classes and not defn.is_explicit_override and defn.name not in ("__init__", "__new__") + and not is_private(defn.name) ): self.msg.explicit_override_decorator_missing( defn.name, found_method_base_classes[0].fullname, context or defn @@ -1921,7 +1922,7 @@ def check_method_or_accessor_override_for_base( base_attr = base.names.get(name) if base_attr: # First, check if we override a final (always an error, even with Any types). - if is_final_node(base_attr.node): + if is_final_node(base_attr.node) and not is_private(name): self.msg.cant_override_final(name, base.name, defn) # Second, final can't override anything writeable independently of types. if defn.is_final: @@ -2680,7 +2681,7 @@ class C(B, A[int]): ... # this is unsafe because... ok = True # Final attributes can never be overridden, but can override # non-final read-only attributes. - if is_final_node(second.node): + if is_final_node(second.node) and not is_private(name): self.msg.cant_override_final(name, base2.name, ctx) if is_final_node(first.node): self.check_if_final_var_override_writable(name, second.node, ctx) @@ -3308,6 +3309,8 @@ def check_compatibility_final_super( """ if not isinstance(base_node, (Var, FuncBase, Decorator)): return True + if is_private(node.name): + return True if base_node.is_final and (node.is_final or not isinstance(base_node, Var)): # Give this error only for explicit override attempt with `Final`, or # if we are overriding a final method with variable. diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index d37ae569cc5e..107298875761 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2531,3 +2531,16 @@ class Foo: c: int # E: Name "c" already defined on line 5 [builtins fixtures/dataclasses.pyi] + +[case testDataclassInheritanceWorksWithExplicitOverrides] +# flags: --enable-error-code explicit-override +from dataclasses import dataclass + +@dataclass +class Base: + x: int + +@dataclass +class Child(Base): + y: int +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index da034caced76..a2fd64386707 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1117,3 +1117,16 @@ from typing import Final class MyClass: a: None a: Final[int] = 1 # E: Cannot redefine an existing name as final # E: Name "a" already defined on line 5 + +[case testFinalOverrideAllowedForPrivate] +from typing import Final, final + +class Parent: + __foo: Final[int] = 0 + @final + def __bar(self) -> None: ... + +class Child(Parent): + __foo: Final[int] = 1 + @final + def __bar(self) -> None: ... diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index cd098a84d4d3..b3df5fddafba 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3159,6 +3159,16 @@ class D(A, B): def f(self, z: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" [typing fixtures/typing-override.pyi] +[case testExplicitOverrideAllowedForPrivate] +# flags: --enable-error-code explicit-override --python-version 3.12 + +class B: + def __f(self, y: int) -> str: pass + +class C(B): + def __f(self, y: int) -> str: pass # OK +[typing fixtures/typing-override.pyi] + [case testCallableProperty] from typing import Callable diff --git a/test-data/unit/fine-grained-dataclass-transform.test b/test-data/unit/fine-grained-dataclass-transform.test index cc297bc344aa..89628256fda5 100644 --- a/test-data/unit/fine-grained-dataclass-transform.test +++ b/test-data/unit/fine-grained-dataclass-transform.test @@ -86,9 +86,9 @@ class A(Dataclass): [out] main:7: error: Unexpected keyword argument "x" for "B" -builtins.pyi:13: note: "B" defined here +builtins.pyi:14: note: "B" defined here main:7: error: Unexpected keyword argument "y" for "B" -builtins.pyi:13: note: "B" defined here +builtins.pyi:14: note: "B" defined here == [case frozenInheritanceViaDefault] diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi index 059c853a621f..29f87ae97e62 100644 --- a/test-data/unit/fixtures/dataclasses.pyi +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -3,6 +3,7 @@ from typing import ( Generic, Iterator, Iterable, Mapping, Optional, Sequence, Tuple, TypeVar, Union, overload, ) +from typing_extensions import override _T = TypeVar('_T') _U = TypeVar('_U') @@ -29,8 +30,10 @@ class dict(Mapping[KT, VT]): def __init__(self, **kwargs: VT) -> None: pass @overload def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass + @override def __getitem__(self, key: KT) -> VT: pass def __setitem__(self, k: KT, v: VT) -> None: pass + @override def __iter__(self) -> Iterator[KT]: pass def __contains__(self, item: object) -> int: pass def update(self, a: Mapping[KT, VT]) -> None: pass @@ -42,7 +45,9 @@ class dict(Mapping[KT, VT]): class list(Generic[_T], Sequence[_T]): def __contains__(self, item: object) -> int: pass + @override def __getitem__(self, key: int) -> _T: pass + @override def __iter__(self) -> Iterator[_T]: pass class function: pass From efa5dcb35929f8555c0fd687f493ab15ac13881d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Nov 2023 20:31:25 +0000 Subject: [PATCH 0389/1617] Fix missing meet case exposed by len narrowing (#16470) Fixes https://github.com/python/mypy/issues/16468 The fix is straightforward. Btw when fixing this I noticed that we disregard type arguments when narrowing, for example: ```python x: Sequence[int] if isinstance(x, tuple): reveal_type(x) # tuple[Any, ...], but should be `tuple[int, ...]` ``` I guess fixing this may be tricky, and it is quite old behavior. --- mypy/meet.py | 3 ++- test-data/unit/check-narrowing.test | 13 +++++++++++++ test-data/unit/fixtures/len.pyi | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index d2fb16808425..610185d6bbbf 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -16,6 +16,7 @@ from mypy.typeops import is_recursive_pair, make_simplified_union, tuple_fallback from mypy.types import ( MYPYC_NATIVE_INT_NAMES, + TUPLE_LIKE_INSTANCE_NAMES, AnyType, CallableType, DeletedType, @@ -936,7 +937,7 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: return TupleType(items, tuple_fallback(t)) elif isinstance(self.s, Instance): # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. - if self.s.type.fullname == "builtins.tuple" and self.s.args: + if self.s.type.fullname in TUPLE_LIKE_INSTANCE_NAMES and self.s.args: return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) elif is_proper_subtype(t, self.s): # A named tuple that inherits from a normal class diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 5b7fadf41c79..d0ad1367aca0 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1910,3 +1910,16 @@ if len(x) == a: else: reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" [builtins fixtures/len.pyi] + +[case testNarrowingLenUnionWithUnreachable] +from typing import Union, Sequence + +def f(x: Union[int, Sequence[int]]) -> None: + if ( + isinstance(x, tuple) + and len(x) == 2 + and isinstance(x[0], int) + and isinstance(x[1], int) + ): + reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +[builtins fixtures/len.pyi] diff --git a/test-data/unit/fixtures/len.pyi b/test-data/unit/fixtures/len.pyi index c72596661858..ee39d952701f 100644 --- a/test-data/unit/fixtures/len.pyi +++ b/test-data/unit/fixtures/len.pyi @@ -10,7 +10,7 @@ class object: class type: def __init__(self, x) -> None: pass -class tuple(Generic[T]): +class tuple(Sequence[T]): def __len__(self) -> int: pass class list(Sequence[T]): pass From 023eb4101347dd151a2ce5bf7baf5a60d2de4145 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 13 Nov 2023 11:10:15 +0300 Subject: [PATCH 0390/1617] Refactor class decorator: this enables `type_check_only` support for `TypedDict` and `NamedTuple` (#16469) I've noticed that `TypedDict` and `NamedTuple` classes are special cased during semantic analyzisys. They had their own logic for class-level decorators. This is fine, but we need some common ground. As a side-effect, they can now be `type_check_only`! --- mypy/semanal.py | 25 ++++++++++++++++--------- mypy/test/teststubtest.py | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 68f0d04e77ca..6714e8c56de9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1743,9 +1743,8 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool: if is_typeddict: for decorator in defn.decorators: decorator.accept(self) - if isinstance(decorator, RefExpr): - if decorator.fullname in FINAL_DECORATOR_NAMES and info is not None: - info.is_final = True + if info is not None: + self.analyze_class_decorator_common(defn, info, decorator) if info is None: self.mark_incomplete(defn.name, defn) else: @@ -1781,8 +1780,7 @@ def analyze_namedtuple_classdef( with self.scope.class_scope(defn.info): for deco in defn.decorators: deco.accept(self) - if isinstance(deco, RefExpr) and deco.fullname in FINAL_DECORATOR_NAMES: - info.is_final = True + self.analyze_class_decorator_common(defn, defn.info, deco) with self.named_tuple_analyzer.save_namedtuple_body(info): self.analyze_class_body_common(defn) return True @@ -1864,21 +1862,30 @@ def leave_class(self) -> None: def analyze_class_decorator(self, defn: ClassDef, decorator: Expression) -> None: decorator.accept(self) + self.analyze_class_decorator_common(defn, defn.info, decorator) if isinstance(decorator, RefExpr): if decorator.fullname in RUNTIME_PROTOCOL_DECOS: if defn.info.is_protocol: defn.info.runtime_protocol = True else: self.fail("@runtime_checkable can only be used with protocol classes", defn) - elif decorator.fullname in FINAL_DECORATOR_NAMES: - defn.info.is_final = True - elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES): - defn.info.is_type_check_only = True elif isinstance(decorator, CallExpr) and refers_to_fullname( decorator.callee, DATACLASS_TRANSFORM_NAMES ): defn.info.dataclass_transform_spec = self.parse_dataclass_transform_spec(decorator) + def analyze_class_decorator_common( + self, defn: ClassDef, info: TypeInfo, decorator: Expression + ) -> None: + """Common method for applying class decorators. + + Called on regular classes, typeddicts, and namedtuples. + """ + if refers_to_fullname(decorator, FINAL_DECORATOR_NAMES): + info.is_final = True + elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES): + info.is_type_check_only = True + def clean_up_bases_and_infer_type_variables( self, defn: ClassDef, base_type_exprs: list[Expression], context: Context ) -> tuple[list[Expression], list[TypeVarLikeType], bool]: diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 6a973d16d7bc..58602be3a624 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -2073,6 +2073,28 @@ class A2: ... runtime="class A2: ...", error="A2", ) + # The same is true for NamedTuples and TypedDicts: + yield Case( + stub="from typing_extensions import NamedTuple, TypedDict", + runtime="from typing_extensions import NamedTuple, TypedDict", + error=None, + ) + yield Case( + stub=""" + @type_check_only + class NT1(NamedTuple): ... + """, + runtime="class NT1(NamedTuple): ...", + error="NT1", + ) + yield Case( + stub=""" + @type_check_only + class TD1(TypedDict): ... + """, + runtime="class TD1(TypedDict): ...", + error="TD1", + ) # The same is true for functions: yield Case( stub=""" From 93e65e443eeac1e0f25a88b33851bb5239bab1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Peder=20Brandtz=C3=A6g?= Date: Mon, 13 Nov 2023 16:51:26 +0100 Subject: [PATCH 0391/1617] Add missing language identifiers in changelog code snippets (#16475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets the Markdown renderer highlight the code as Python rather than leaving it un-highlighted :–) This PR does not change mypy, nor do I believe any tests should be written, as it only affects the rendering of a few code snippets in the changelog (other snippets further down in the changelog already use the identifier). --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5523894a524..f28cdb1ccc25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ You can read the full documentation for this release on [Read the Docs](http://m Mypy now has support for using `Unpack[...]` with a TypedDict type to annotate `**kwargs` arguments enabled by default. Example: -``` +```python # Or 'from typing_extensions import ...' from typing import TypedDict, Unpack @@ -33,7 +33,7 @@ foo(name=1) # Error The definition of `foo` above is equivalent to the one below, with keyword-only arguments `name` and `age`: -``` +```python def foo(*, name: str, age: int) -> None: ... ``` @@ -94,7 +94,7 @@ The new type inference algorithm was contributed by Ivan Levkivskyi. PR [16345]( Mypy now can narrow tuple types using `len()` checks. Example: -``` +```python def f(t: tuple[int, int] | tuple[int, int, int]) -> None: if len(t) == 2: a, b = t # Ok From fbb77c31ad48e08105b5d02c2888d803bdcd6fc5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 13 Nov 2023 16:22:25 +0000 Subject: [PATCH 0392/1617] Special-case unions in polymorphic inference (#16461) Fixes https://github.com/python/mypy/issues/16451 This special-casing is unfortunate, but this is the best I came up so far. --- mypy/solve.py | 53 +++++++++++++++---- test-data/unit/check-inference.test | 21 ++++++++ .../unit/check-parameter-specification.test | 22 ++++++++ 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/mypy/solve.py b/mypy/solve.py index efe8e487c506..9770364bf892 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -6,7 +6,7 @@ from typing import Iterable, Sequence from typing_extensions import TypeAlias as _TypeAlias -from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints +from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op from mypy.expandtype import expand_type from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.join import join_types @@ -69,6 +69,10 @@ def solve_constraints( extra_vars.extend([v.id for v in c.extra_tvars if v.id not in vars + extra_vars]) originals.update({v.id: v for v in c.extra_tvars if v.id not in originals}) + if allow_polymorphic: + # Constraints inferred from unions require special handling in polymorphic inference. + constraints = skip_reverse_union_constraints(constraints) + # Collect a list of constraints for each type variable. cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars + extra_vars} for con in constraints: @@ -431,10 +435,7 @@ def transitive_closure( uppers[l] |= uppers[upper] for lt in lowers[lower]: for ut in uppers[upper]: - # TODO: what if secondary constraints result in inference - # against polymorphic actual (also in below branches)? - remaining |= set(infer_constraints(lt, ut, SUBTYPE_OF)) - remaining |= set(infer_constraints(ut, lt, SUPERTYPE_OF)) + add_secondary_constraints(remaining, lt, ut) elif c.op == SUBTYPE_OF: if c.target in uppers[c.type_var]: continue @@ -442,8 +443,7 @@ def transitive_closure( if (l, c.type_var) in graph: uppers[l].add(c.target) for lt in lowers[c.type_var]: - remaining |= set(infer_constraints(lt, c.target, SUBTYPE_OF)) - remaining |= set(infer_constraints(c.target, lt, SUPERTYPE_OF)) + add_secondary_constraints(remaining, lt, c.target) else: assert c.op == SUPERTYPE_OF if c.target in lowers[c.type_var]: @@ -452,11 +452,24 @@ def transitive_closure( if (c.type_var, u) in graph: lowers[u].add(c.target) for ut in uppers[c.type_var]: - remaining |= set(infer_constraints(ut, c.target, SUPERTYPE_OF)) - remaining |= set(infer_constraints(c.target, ut, SUBTYPE_OF)) + add_secondary_constraints(remaining, c.target, ut) return graph, lowers, uppers +def add_secondary_constraints(cs: set[Constraint], lower: Type, upper: Type) -> None: + """Add secondary constraints inferred between lower and upper (in place).""" + if isinstance(get_proper_type(upper), UnionType) and isinstance( + get_proper_type(lower), UnionType + ): + # When both types are unions, this can lead to inferring spurious constraints, + # for example Union[T, int] <: S <: Union[T, int] may infer T <: int. + # To avoid this, just skip them for now. + return + # TODO: what if secondary constraints result in inference against polymorphic actual? + cs.update(set(infer_constraints(lower, upper, SUBTYPE_OF))) + cs.update(set(infer_constraints(upper, lower, SUPERTYPE_OF))) + + def compute_dependencies( tvars: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds ) -> dict[TypeVarId, list[TypeVarId]]: @@ -494,6 +507,28 @@ def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool: return True +def skip_reverse_union_constraints(cs: list[Constraint]) -> list[Constraint]: + """Avoid ambiguities for constraints inferred from unions during polymorphic inference. + + Polymorphic inference implicitly relies on assumption that a reverse of a linear constraint + is a linear constraint. This is however not true in presence of union types, for example + T :> Union[S, int] vs S <: T. Trying to solve such constraints would be detected ambiguous + as (T, S) form a non-linear SCC. However, simply removing the linear part results in a valid + solution T = Union[S, int], S = . + + TODO: a cleaner solution may be to avoid inferring such constraints in first place, but + this would require passing around a flag through all infer_constraints() calls. + """ + reverse_union_cs = set() + for c in cs: + p_target = get_proper_type(c.target) + if isinstance(p_target, UnionType): + for item in p_target.items: + if isinstance(item, TypeVarType): + reverse_union_cs.add(Constraint(item, neg_op(c.op), c.origin_type_var)) + return [c for c in cs if c not in reverse_union_cs] + + def get_vars(target: Type, vars: list[TypeVarId]) -> set[TypeVarId]: """Find type variables for which we are solving in a target type.""" return {tv.id for tv in get_all_type_vars(target)} & set(vars) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0d162238450a..6c98ba2088b1 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3767,3 +3767,24 @@ def f(values: List[T]) -> T: ... x = foo(f([C()])) reveal_type(x) # N: Revealed type is "__main__.C" [builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallableUnion] +from typing import Callable, TypeVar, List, Union + +T = TypeVar("T") +S = TypeVar("S") + +def dec(f: Callable[[S], T]) -> Callable[[S], List[T]]: ... +@dec +def func(arg: T) -> Union[T, str]: + ... +reveal_type(func) # N: Revealed type is "def [S] (S`1) -> builtins.list[Union[S`1, builtins.str]]" +reveal_type(func(42)) # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]" + +def dec2(f: Callable[[S], List[T]]) -> Callable[[S], T]: ... +@dec2 +def func2(arg: T) -> List[Union[T, str]]: + ... +reveal_type(func2) # N: Revealed type is "def [S] (S`4) -> Union[S`4, builtins.str]" +reveal_type(func2(42)) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index db8c76fd21e9..eb6fbf07f045 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2086,3 +2086,25 @@ reveal_type(d(b, f1)) # E: Cannot infer type argument 1 of "d" \ # N: Revealed type is "def (*Any, **Any)" reveal_type(d(b, f2)) # N: Revealed type is "def (builtins.int)" [builtins fixtures/paramspec.pyi] + +[case testInferenceAgainstGenericCallableUnionParamSpec] +from typing import Callable, TypeVar, List, Union +from typing_extensions import ParamSpec + +T = TypeVar("T") +P = ParamSpec("P") + +def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... +@dec +def func(arg: T) -> Union[T, str]: + ... +reveal_type(func) # N: Revealed type is "def [T] (arg: T`-1) -> builtins.list[Union[T`-1, builtins.str]]" +reveal_type(func(42)) # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]" + +def dec2(f: Callable[P, List[T]]) -> Callable[P, T]: ... +@dec2 +def func2(arg: T) -> List[Union[T, str]]: + ... +reveal_type(func2) # N: Revealed type is "def [T] (arg: T`-1) -> Union[T`-1, builtins.str]" +reveal_type(func2(42)) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/paramspec.pyi] From c6cb3c6282003dd3dadcf028735f9ba6190a0c84 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 13 Nov 2023 16:59:50 +0000 Subject: [PATCH 0393/1617] Ignore position if imprecise arguments are matched by name (#16471) Fixes https://github.com/python/mypy/issues/16405 Fixes https://github.com/python/mypy/issues/16412 Imprecise argument kinds inference was added a while ago to support various edge cases with `ParamSpec`. This feature required mapping actual kinds to formal kinds, which is in general undecidable. At that time we decided to not add much special-casing, and wait for some real use-cases. So far there are two relevant issues, and it looks like both of them can be fixed with simple special-casing: ignore argument positions in subtyping if arguments can be matched by name. This adds minor unsafety, and generally doesn't look bad, so I think we should go ahead with it. --------- Co-authored-by: Alex Waygood --- mypy/subtypes.py | 24 ++++++-- .../unit/check-parameter-specification.test | 55 +++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 7e37751b1c15..4fd3f8ff98ca 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1651,7 +1651,12 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N continue return False if not are_args_compatible( - left_arg, right_arg, ignore_pos_arg_names, allow_partial_overlap, is_compat + left_arg, + right_arg, + is_compat, + ignore_pos_arg_names=ignore_pos_arg_names, + allow_partial_overlap=allow_partial_overlap, + allow_imprecise_kinds=right.imprecise_arg_kinds, ): return False @@ -1676,9 +1681,9 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N if not are_args_compatible( left_by_position, right_by_position, - ignore_pos_arg_names, - allow_partial_overlap, is_compat, + ignore_pos_arg_names=ignore_pos_arg_names, + allow_partial_overlap=allow_partial_overlap, ): return False i += 1 @@ -1711,7 +1716,11 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N continue if not are_args_compatible( - left_by_name, right_by_name, ignore_pos_arg_names, allow_partial_overlap, is_compat + left_by_name, + right_by_name, + is_compat, + ignore_pos_arg_names=ignore_pos_arg_names, + allow_partial_overlap=allow_partial_overlap, ): return False @@ -1735,6 +1744,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N and right_by_name != right_by_pos and (right_by_pos.required or right_by_name.required) and strict_concatenate_check + and not right.imprecise_arg_kinds ): return False @@ -1749,9 +1759,11 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N def are_args_compatible( left: FormalArgument, right: FormalArgument, + is_compat: Callable[[Type, Type], bool], + *, ignore_pos_arg_names: bool, allow_partial_overlap: bool, - is_compat: Callable[[Type, Type], bool], + allow_imprecise_kinds: bool = False, ) -> bool: if left.required and right.required: # If both arguments are required allow_partial_overlap has no effect. @@ -1779,7 +1791,7 @@ def is_different(left_item: object | None, right_item: object | None) -> bool: return False # If right is at a specific position, left must have the same: - if is_different(left.pos, right.pos): + if is_different(left.pos, right.pos) and not allow_imprecise_kinds: return False # If right's argument is optional, left's must also be diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index eb6fbf07f045..d65a0214b599 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1687,9 +1687,18 @@ P = ParamSpec("P") T = TypeVar("T") def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> None: ... + def test(x: int) -> int: ... apply(apply, test, x=42) # OK apply(apply, test, 42) # Also OK (but requires some special casing) +apply(apply, test, "bad") # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int], int], str], None]" + +def test2(x: int, y: str) -> None: ... +apply(apply, test2, 42, "yes") +apply(apply, test2, "no", 42) # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], str, int], None]" +apply(apply, test2, x=42, y="yes") +apply(apply, test2, y="yes", x=42) +apply(apply, test2, y=42, x="no") # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], int, str], None]" [builtins fixtures/paramspec.pyi] [case testParamSpecApplyPosVsNamedOptional] @@ -2087,6 +2096,52 @@ reveal_type(d(b, f1)) # E: Cannot infer type argument 1 of "d" \ reveal_type(d(b, f2)) # N: Revealed type is "def (builtins.int)" [builtins fixtures/paramspec.pyi] +[case testParamSpecGenericWithNamedArg1] +from typing import Callable, TypeVar +from typing_extensions import ParamSpec + +R = TypeVar("R") +P = ParamSpec("P") + +def run(func: Callable[[], R], *args: object, backend: str = "asyncio") -> R: ... +class Result: ... +def run_portal() -> Result: ... +def submit(func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R: ... + +reveal_type(submit( # N: Revealed type is "__main__.Result" + run, + run_portal, + backend="asyncio", +)) +submit( + run, # E: Argument 1 to "submit" has incompatible type "Callable[[Callable[[], R], VarArg(object), DefaultNamedArg(str, 'backend')], R]"; expected "Callable[[Callable[[], Result], int], Result]" + run_portal, + backend=int(), +) +[builtins fixtures/paramspec.pyi] + +[case testParamSpecGenericWithNamedArg2] +from typing import Callable, TypeVar, Type +from typing_extensions import ParamSpec + +P= ParamSpec("P") +T = TypeVar("T") + +def smoke_testable(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], Type[T]]: + ... + +@smoke_testable(name="bob", size=512, flt=0.5) +class SomeClass: + def __init__(self, size: int, name: str, flt: float) -> None: + pass + +# Error message is confusing, but this is a known issue, see #4530. +@smoke_testable(name=42, size="bad", flt=0.5) # E: Argument 1 has incompatible type "Type[OtherClass]"; expected "Callable[[int, str, float], OtherClass]" +class OtherClass: + def __init__(self, size: int, name: str, flt: float) -> None: + pass +[builtins fixtures/paramspec.pyi] + [case testInferenceAgainstGenericCallableUnionParamSpec] from typing import Callable, TypeVar, List, Union from typing_extensions import ParamSpec From 0699dde8dc2633861a65ac43701eda09e79de366 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 15 Nov 2023 10:20:25 +0000 Subject: [PATCH 0394/1617] Fix crash on strict-equality with recursive types (#16483) Fixes https://github.com/python/mypy/issues/16473 Potentially we can turn this helper function into a proper visitor, but I don't think it is worth it as of right now. --------- Co-authored-by: Alex Waygood --- mypy/checkexpr.py | 21 +++++++++++++----- mypy/meet.py | 12 +++++++++- test-data/unit/check-expressions.test | 32 +++++++++++++++++++++++++++ test-data/unit/fixtures/list.pyi | 1 + 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c87d1f6cd31c..da61833bbe5b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3617,8 +3617,9 @@ def dangerous_comparison( self, left: Type, right: Type, - original_container: Type | None = None, *, + original_container: Type | None = None, + seen_types: set[tuple[Type, Type]] | None = None, prefer_literal: bool = True, ) -> bool: """Check for dangerous non-overlapping comparisons like 42 == 'no'. @@ -3639,6 +3640,12 @@ def dangerous_comparison( if not self.chk.options.strict_equality: return False + if seen_types is None: + seen_types = set() + if (left, right) in seen_types: + return False + seen_types.add((left, right)) + left, right = get_proper_types((left, right)) # We suppress the error if there is a custom __eq__() method on either @@ -3694,17 +3701,21 @@ def dangerous_comparison( abstract_set = self.chk.lookup_typeinfo("typing.AbstractSet") left = map_instance_to_supertype(left, abstract_set) right = map_instance_to_supertype(right, abstract_set) - return self.dangerous_comparison(left.args[0], right.args[0]) + return self.dangerous_comparison( + left.args[0], right.args[0], seen_types=seen_types + ) elif left.type.has_base("typing.Mapping") and right.type.has_base("typing.Mapping"): # Similar to above: Mapping ignores the classes, it just compares items. abstract_map = self.chk.lookup_typeinfo("typing.Mapping") left = map_instance_to_supertype(left, abstract_map) right = map_instance_to_supertype(right, abstract_map) return self.dangerous_comparison( - left.args[0], right.args[0] - ) or self.dangerous_comparison(left.args[1], right.args[1]) + left.args[0], right.args[0], seen_types=seen_types + ) or self.dangerous_comparison(left.args[1], right.args[1], seen_types=seen_types) elif left_name in ("builtins.list", "builtins.tuple") and right_name == left_name: - return self.dangerous_comparison(left.args[0], right.args[0]) + return self.dangerous_comparison( + left.args[0], right.args[0], seen_types=seen_types + ) elif left_name in OVERLAPPING_BYTES_ALLOWLIST and right_name in ( OVERLAPPING_BYTES_ALLOWLIST ): diff --git a/mypy/meet.py b/mypy/meet.py index 610185d6bbbf..df8b960cdf3f 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -262,6 +262,7 @@ def is_overlapping_types( ignore_promotions: bool = False, prohibit_none_typevar_overlap: bool = False, ignore_uninhabited: bool = False, + seen_types: set[tuple[Type, Type]] | None = None, ) -> bool: """Can a value of type 'left' also be of type 'right' or vice-versa? @@ -275,18 +276,27 @@ def is_overlapping_types( # A type guard forces the new type even if it doesn't overlap the old. return True + if seen_types is None: + seen_types = set() + if (left, right) in seen_types: + return True + if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType): + seen_types.add((left, right)) + left, right = get_proper_types((left, right)) def _is_overlapping_types(left: Type, right: Type) -> bool: """Encode the kind of overlapping check to perform. - This function mostly exists so we don't have to repeat keyword arguments everywhere.""" + This function mostly exists, so we don't have to repeat keyword arguments everywhere. + """ return is_overlapping_types( left, right, ignore_promotions=ignore_promotions, prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, ignore_uninhabited=ignore_uninhabited, + seen_types=seen_types.copy(), ) # We should never encounter this type. diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 4ac5512580d2..8fe68365e5ac 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2378,6 +2378,38 @@ assert a == b [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] +[case testStrictEqualityWithRecursiveMapTypes] +# flags: --strict-equality +from typing import Dict + +R = Dict[str, R] + +a: R +b: R +assert a == b + +R2 = Dict[int, R2] +c: R2 +assert a == c # E: Non-overlapping equality check (left operand type: "Dict[str, R]", right operand type: "Dict[int, R2]") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + +[case testStrictEqualityWithRecursiveListTypes] +# flags: --strict-equality +from typing import List, Union + +R = List[Union[str, R]] + +a: R +b: R +assert a == b + +R2 = List[Union[int, R2]] +c: R2 +assert a == c +[builtins fixtures/list.pyi] +[typing fixtures/typing-full.pyi] + [case testUnimportedHintAny] def f(x: Any) -> None: # E: Name "Any" is not defined \ # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any") diff --git a/test-data/unit/fixtures/list.pyi b/test-data/unit/fixtures/list.pyi index 90fbabe8bc92..3dcdf18b2faa 100644 --- a/test-data/unit/fixtures/list.pyi +++ b/test-data/unit/fixtures/list.pyi @@ -6,6 +6,7 @@ T = TypeVar('T') class object: def __init__(self) -> None: pass + def __eq__(self, other: object) -> bool: pass class type: pass class ellipsis: pass From b425bd60672881addcd55dfae61993fa2ac03cca Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 16 Nov 2023 11:56:53 +0000 Subject: [PATCH 0395/1617] [mypyc] Fix regression with nested functions (#16484) Fixes https://github.com/python/mypy/issues/16480 Fix is straightforward, but also suspicious, since I am not sure how it ever worked without this. --- mypyc/irbuild/prebuildvisitor.py | 3 ++- mypyc/test-data/run-functions.test | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/prebuildvisitor.py b/mypyc/irbuild/prebuildvisitor.py index 519b3445e925..17f907d42111 100644 --- a/mypyc/irbuild/prebuildvisitor.py +++ b/mypyc/irbuild/prebuildvisitor.py @@ -119,9 +119,10 @@ def visit_decorator(self, dec: Decorator) -> None: self.funcs_to_decorators[dec.func] = decorators_to_store super().visit_decorator(dec) - def visit_func_def(self, fdef: FuncItem) -> None: + def visit_func_def(self, fdef: FuncDef) -> None: # TODO: What about overloaded functions? self.visit_func(fdef) + self.visit_symbol_node(fdef) def visit_lambda_expr(self, expr: LambdaExpr) -> None: self.visit_func(expr) diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index bd8f1a9197dd..cf519f30dad8 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -1286,3 +1286,25 @@ def bar() -> None: bar() [out] {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': } + +[case testCallNestedFunctionWithNamed] +def f() -> None: + def a() -> None: + pass + def b() -> None: + a() + b() +[file driver.py] +from native import f +f() + +[case testCallNestedFunctionWithLambda] +def f(x: int) -> int: + def inc(x: int) -> int: + return x + 1 + return (lambda x: inc(x))(1) +[file driver.py] +from native import f +print(f(1)) +[out] +2 From 8c8aa10e2976612ca0ca1fa1e5655fbd535de6f7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 17 Nov 2023 00:48:24 +0000 Subject: [PATCH 0396/1617] Fix crash on unimported Any in TypedDict (#16510) Fixes https://github.com/python/mypy/issues/16498 Fix is trivial: no operations can be done with `Required` types before unwrapping (because they are not real types). --------- Co-authored-by: Jelle Zijlstra --- mypy/semanal_typeddict.py | 20 +++++++++++--------- test-data/unit/check-typeddict.test | 12 ++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 5104d31f5c26..a013cc040e89 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -394,6 +394,17 @@ def check_typeddict( types = [ # unwrap Required[T] to just T t.item if isinstance(t, RequiredType) else t for t in types ] + + # Perform various validations after unwrapping. + for t in types: + check_for_explicit_any( + t, self.options, self.api.is_typeshed_stub_file, self.msg, context=call + ) + if self.options.disallow_any_unimported: + for t in types: + if has_any_from_unimported_type(t): + self.msg.unimported_type_becomes_any("Type of a TypedDict key", t, call) + existing_info = None if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info @@ -451,15 +462,6 @@ def parse_typeddict_args( # One of the types is not ready, defer. return None items, types, ok = res - for t in types: - check_for_explicit_any( - t, self.options, self.api.is_typeshed_stub_file, self.msg, context=call - ) - - if self.options.disallow_any_unimported: - for t in types: - if has_any_from_unimported_type(t): - self.msg.unimported_type_becomes_any("Type of a TypedDict key", t, dictexpr) assert total is not None return args[0].value, items, types, total, tvar_defs, ok diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index c1c791304a15..c584906dd965 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3396,3 +3396,15 @@ reveal_type(b["a"]) # N: Revealed type is "Union[builtins.str, None]" reveal_type(b["g"]) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testNoCrashOnUnImportedAnyNotRequired] +# flags: --disallow-any-unimported +from typing import NotRequired, Required, TypedDict +from thismoduledoesntexist import T # type: ignore[import] + +B = TypedDict("B", { # E: Type of a TypedDict key becomes "Any" due to an unfollowed import + "T1": NotRequired[T], + "T2": Required[T], +}) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 5489fd33f08fd93eff167eda84b00eeab939a419 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 17 Nov 2023 20:49:33 +0000 Subject: [PATCH 0397/1617] Fix crash on invalid enum in method (#16511) Fixes https://github.com/python/mypy/issues/16163 Fix is straightforward: I simply copy the logic we have for invalid TypedDicts/NamedTuples. --- mypy/semanal_enum.py | 11 +++++++---- test-data/unit/check-incremental.test | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index cd11204c3bcc..528b0519cca1 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -106,16 +106,19 @@ class A(enum.Enum): items, values, ok = self.parse_enum_call_args(call, fullname.split(".")[-1]) if not ok: # Error. Construct dummy return value. - info = self.build_enum_call_typeinfo(var_name, [], fullname, node.line) + name = var_name + if is_func_scope: + name += "@" + str(call.line) + info = self.build_enum_call_typeinfo(name, [], fullname, node.line) else: name = cast(StrExpr, call.args[0]).value if name != var_name or is_func_scope: # Give it a unique name derived from the line number. name += "@" + str(call.line) info = self.build_enum_call_typeinfo(name, items, fullname, call.line) - # Store generated TypeInfo under both names, see semanal_namedtuple for more details. - if name != var_name or is_func_scope: - self.api.add_symbol_skip_local(name, info) + # Store generated TypeInfo under both names, see semanal_namedtuple for more details. + if name != var_name or is_func_scope: + self.api.add_symbol_skip_local(name, info) call.analyzed = EnumCallExpr(info, items, values) call.analyzed.set_line(call) info.line = node.line diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 806a585bff39..2c7d908c5f5b 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6560,3 +6560,26 @@ class C: [out] [out2] tmp/a.py:3: note: Revealed type is "TypedDict('b.C.Hidden@5', {'x': builtins.int})" + +[case testNoIncrementalCrashOnInvalidEnumMethod] +import a +[file a.py] +from lib import TheClass +[file a.py.2] +from lib import TheClass +x: TheClass +reveal_type(x.enum_type) +[file lib.py] +import enum + +class TheClass: + def __init__(self) -> None: + names = ["foo"] + pyenum = enum.Enum('Blah', { # type: ignore[misc] + x.upper(): x + for x in names + }) + self.enum_type = pyenum +[out] +[out2] +tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" From 1cc62a211d21cca2d3bf44957d1635d0a02fef30 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:08:54 -0800 Subject: [PATCH 0398/1617] Sync typeshed (#16493) Source commit: https://github.com/python/typeshed/commit/643d911f4fb434176c7f6a342db9109431259273 --- mypy/typeshed/stdlib/_ast.pyi | 2 +- mypy/typeshed/stdlib/_locale.pyi | 4 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 4 ++ mypy/typeshed/stdlib/_typeshed/wsgi.pyi | 2 +- mypy/typeshed/stdlib/_typeshed/xml.pyi | 6 +-- mypy/typeshed/stdlib/_warnings.pyi | 31 ++++++++++-- mypy/typeshed/stdlib/argparse.pyi | 2 +- mypy/typeshed/stdlib/ast.pyi | 11 +++-- mypy/typeshed/stdlib/asyncio/base_events.pyi | 2 +- mypy/typeshed/stdlib/asyncio/events.pyi | 2 +- .../stdlib/asyncio/proactor_events.pyi | 13 +---- mypy/typeshed/stdlib/asyncio/subprocess.pyi | 6 +-- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 9 +--- .../typeshed/stdlib/asyncio/windows_utils.pyi | 12 +---- mypy/typeshed/stdlib/builtins.pyi | 18 +++---- mypy/typeshed/stdlib/cmath.pyi | 2 +- mypy/typeshed/stdlib/codecs.pyi | 4 +- mypy/typeshed/stdlib/collections/__init__.pyi | 7 +++ mypy/typeshed/stdlib/compileall.pyi | 2 +- mypy/typeshed/stdlib/contextlib.pyi | 4 +- mypy/typeshed/stdlib/contextvars.pyi | 15 ++---- mypy/typeshed/stdlib/dbm/__init__.pyi | 2 +- mypy/typeshed/stdlib/doctest.pyi | 4 +- mypy/typeshed/stdlib/email/headerregistry.pyi | 4 +- mypy/typeshed/stdlib/http/client.pyi | 2 +- mypy/typeshed/stdlib/imghdr.pyi | 4 +- mypy/typeshed/stdlib/imp.pyi | 2 +- mypy/typeshed/stdlib/importlib/abc.pyi | 8 ++-- mypy/typeshed/stdlib/ipaddress.pyi | 4 +- mypy/typeshed/stdlib/itertools.pyi | 15 ++++-- mypy/typeshed/stdlib/locale.pyi | 4 +- mypy/typeshed/stdlib/logging/__init__.pyi | 14 +++++- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- .../stdlib/multiprocessing/sharedctypes.pyi | 2 +- mypy/typeshed/stdlib/os/__init__.pyi | 24 ++++++---- mypy/typeshed/stdlib/smtplib.pyi | 4 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 6 +-- mypy/typeshed/stdlib/subprocess.pyi | 48 +++++++++---------- mypy/typeshed/stdlib/sys/__init__.pyi | 4 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 19 ++++---- mypy/typeshed/stdlib/tkinter/dnd.pyi | 2 +- mypy/typeshed/stdlib/types.pyi | 2 +- mypy/typeshed/stdlib/typing.pyi | 6 +-- mypy/typeshed/stdlib/unittest/case.pyi | 22 ++++----- mypy/typeshed/stdlib/unittest/main.pyi | 2 +- mypy/typeshed/stdlib/unittest/util.pyi | 4 +- mypy/typeshed/stdlib/urllib/request.pyi | 3 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 4 +- mypy/typeshed/stdlib/xml/sax/saxutils.pyi | 2 +- 49 files changed, 208 insertions(+), 171 deletions(-) diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 402b770c0462..0302133fc6f9 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -553,7 +553,7 @@ if sys.version_info >= (3, 10): class MatchSingleton(pattern): __match_args__ = ("value",) - value: Literal[True, False, None] + value: Literal[True, False] | None class MatchSequence(pattern): __match_args__ = ("patterns",) diff --git a/mypy/typeshed/stdlib/_locale.pyi b/mypy/typeshed/stdlib/_locale.pyi index 2b2fe03e4510..d7399f15e1a3 100644 --- a/mypy/typeshed/stdlib/_locale.pyi +++ b/mypy/typeshed/stdlib/_locale.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import StrPath -from collections.abc import Iterable, Mapping +from collections.abc import Mapping LC_CTYPE: int LC_COLLATE: int @@ -10,7 +10,7 @@ LC_NUMERIC: int LC_ALL: int CHAR_MAX: int -def setlocale(category: int, locale: str | Iterable[str | None] | None = None) -> str: ... +def setlocale(__category: int, __locale: str | None = None) -> str: ... def localeconv() -> Mapping[str, int | str | list[int]]: ... if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index ad214a2a5e0d..33659cf31a12 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -236,6 +236,10 @@ class SupportsNoArgReadline(Protocol[_T_co]): class SupportsWrite(Protocol[_T_contra]): def write(self, __s: _T_contra) -> object: ... +# stable +class SupportsFlush(Protocol): + def flush(self) -> object: ... + # Unfortunately PEP 688 does not allow us to distinguish read-only # from writable buffers. We use these aliases for readability for now. # Perhaps a future extension of the buffer protocol will allow us to diff --git a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi index de731aea918b..e8ebf6409e7f 100644 --- a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi +++ b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi @@ -11,7 +11,7 @@ from typing import Any, Protocol from typing_extensions import TypeAlias class _Readable(Protocol): - def read(self, size: int = ...) -> bytes: ... + def read(self, __size: int = ...) -> bytes: ... # Optional: def close(self) -> object: ... if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/_typeshed/xml.pyi b/mypy/typeshed/stdlib/_typeshed/xml.pyi index 231c2b86e912..46c5fab097c4 100644 --- a/mypy/typeshed/stdlib/_typeshed/xml.pyi +++ b/mypy/typeshed/stdlib/_typeshed/xml.pyi @@ -4,6 +4,6 @@ from typing import Any, Protocol # As defined https://docs.python.org/3/library/xml.dom.html#domimplementation-objects class DOMImplementation(Protocol): - def hasFeature(self, feature: str, version: str | None) -> bool: ... - def createDocument(self, namespaceUri: str, qualifiedName: str, doctype: Any | None) -> Any: ... - def createDocumentType(self, qualifiedName: str, publicId: str, systemId: str) -> Any: ... + def hasFeature(self, __feature: str, __version: str | None) -> bool: ... + def createDocument(self, __namespaceUri: str, __qualifiedName: str, __doctype: Any | None) -> Any: ... + def createDocumentType(self, __qualifiedName: str, __publicId: str, __systemId: str) -> Any: ... diff --git a/mypy/typeshed/stdlib/_warnings.pyi b/mypy/typeshed/stdlib/_warnings.pyi index 0981dfeaafee..2e571e676c97 100644 --- a/mypy/typeshed/stdlib/_warnings.pyi +++ b/mypy/typeshed/stdlib/_warnings.pyi @@ -1,13 +1,36 @@ +import sys from typing import Any, overload _defaultaction: str _onceregistry: dict[Any, Any] filters: list[tuple[str, str | None, type[Warning], str | None, int]] -@overload -def warn(message: str, category: type[Warning] | None = None, stacklevel: int = 1, source: Any | None = None) -> None: ... -@overload -def warn(message: Warning, category: Any = None, stacklevel: int = 1, source: Any | None = None) -> None: ... +if sys.version_info >= (3, 12): + @overload + def warn( + message: str, + category: type[Warning] | None = None, + stacklevel: int = 1, + source: Any | None = None, + *, + skip_file_prefixes: tuple[str, ...] = (), + ) -> None: ... + @overload + def warn( + message: Warning, + category: Any = None, + stacklevel: int = 1, + source: Any | None = None, + *, + skip_file_prefixes: tuple[str, ...] = (), + ) -> None: ... + +else: + @overload + def warn(message: str, category: type[Warning] | None = None, stacklevel: int = 1, source: Any | None = None) -> None: ... + @overload + def warn(message: Warning, category: Any = None, stacklevel: int = 1, source: Any | None = None) -> None: ... + @overload def warn_explicit( message: str, diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 924cc8986114..e947f67edd55 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -120,7 +120,7 @@ class _ActionsContainer: def _handle_conflict_resolve(self, action: Action, conflicting_actions: Iterable[tuple[str, Action]]) -> None: ... class _FormatterClass(Protocol): - def __call__(self, prog: str) -> HelpFormatter: ... + def __call__(self, *, prog: str) -> HelpFormatter: ... class ArgumentParser(_AttributeHolder, _ActionsContainer): prog: str diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index a61b4e35fd56..5c9cafc189be 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -4,27 +4,30 @@ from _ast import * from _typeshed import ReadableBuffer, Unused from collections.abc import Iterator from typing import Any, TypeVar as _TypeVar, overload -from typing_extensions import Literal +from typing_extensions import Literal, deprecated if sys.version_info >= (3, 8): class _ABC(type): if sys.version_info >= (3, 9): def __init__(cls, *args: Unused) -> None: ... + @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") class Num(Constant, metaclass=_ABC): value: int | float | complex - + @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") class Str(Constant, metaclass=_ABC): value: str # Aliases for value, for backwards compatibility s: str - + @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") class Bytes(Constant, metaclass=_ABC): value: bytes # Aliases for value, for backwards compatibility s: bytes - + @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") class NameConstant(Constant, metaclass=_ABC): ... + + @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") class Ellipsis(Constant, metaclass=_ABC): ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index e2b55da8c718..cdf295d510d4 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -423,7 +423,7 @@ class BaseEventLoop(AbstractEventLoop): bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False, None] = None, + text: Literal[False] | None = None, **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... async def subprocess_exec( diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index cde63b279b0d..4c62043875ba 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -522,7 +522,7 @@ class AbstractEventLoop: bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False, None] = ..., + text: Literal[False] | None = ..., **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi index 33fdf84ade4a..4634bbb2b37c 100644 --- a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi @@ -1,19 +1,13 @@ import sys from collections.abc import Mapping from socket import socket -from typing import Any, ClassVar, Protocol +from typing import Any, ClassVar from typing_extensions import Literal from . import base_events, constants, events, futures, streams, transports __all__ = ("BaseProactorEventLoop",) -if sys.version_info >= (3, 8): - class _WarnCallbackProtocol(Protocol): - def __call__( - self, message: str, category: type[Warning] | None = ..., stacklevel: int = ..., source: Any | None = ... - ) -> object: ... - class _ProactorBasePipeTransport(transports._FlowControlMixin, transports.BaseTransport): def __init__( self, @@ -24,10 +18,7 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin, transports.BaseTr extra: Mapping[Any, Any] | None = None, server: events.AbstractServer | None = None, ) -> None: ... - if sys.version_info >= (3, 8): - def __del__(self, _warn: _WarnCallbackProtocol = ...) -> None: ... - else: - def __del__(self) -> None: ... + def __del__(self) -> None: ... class _ProactorReadPipeTransport(_ProactorBasePipeTransport, transports.ReadTransport): if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index b8877b360527..9b7c82e689bf 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -54,7 +54,7 @@ if sys.version_info >= (3, 11): bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False, None] = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, @@ -120,7 +120,7 @@ elif sys.version_info >= (3, 10): bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False, None] = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, @@ -185,7 +185,7 @@ else: # >= 3.9 bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False, None] = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index e28d64b5287b..dc3d3496ae55 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -84,13 +84,6 @@ if sys.platform != "win32": DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy if sys.version_info >= (3, 8): - from typing import Protocol - - class _Warn(Protocol): - def __call__( - self, message: str, category: type[Warning] | None = ..., stacklevel: int = ..., source: Any | None = ... - ) -> object: ... - class MultiLoopChildWatcher(AbstractChildWatcher): def is_active(self) -> bool: ... def close(self) -> None: ... @@ -109,7 +102,7 @@ if sys.platform != "win32": def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... - def __del__(self, _warn: _Warn = ...) -> None: ... + def __del__(self) -> None: ... def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi index 9f88718b7b70..ed5d8da275c5 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -2,16 +2,12 @@ import subprocess import sys from collections.abc import Callable from types import TracebackType -from typing import Any, AnyStr, Protocol +from typing import Any, AnyStr from typing_extensions import Literal, Self if sys.platform == "win32": __all__ = ("pipe", "Popen", "PIPE", "PipeHandle") - class _WarnFunction(Protocol): - def __call__( - self, message: str, category: type[Warning] = ..., stacklevel: int = ..., source: PipeHandle = ... - ) -> object: ... BUFSIZE: Literal[8192] PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT @@ -19,11 +15,7 @@ if sys.platform == "win32": class PipeHandle: def __init__(self, handle: int) -> None: ... - if sys.version_info >= (3, 8): - def __del__(self, _warn: _WarnFunction = ...) -> None: ... - else: - def __del__(self) -> None: ... - + def __del__(self) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... @property diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 96a1d1e31b17..4f04b6286258 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -18,6 +18,7 @@ from _typeshed import ( SupportsAiter, SupportsAnext, SupportsDivMod, + SupportsFlush, SupportsIter, SupportsKeysAndGetItem, SupportsLenAndGetItem, @@ -1194,7 +1195,7 @@ if sys.version_info >= (3, 10): # See discussion in #7491 and pure-Python implementation of `anext` at https://github.com/python/cpython/blob/ea786a882b9ed4261eafabad6011bc7ef3b5bf94/Lib/test/test_asyncgen.py#L52-L80 def anext(__i: _SupportsSynchronousAnext[_AwaitableT]) -> _AwaitableT: ... @overload - async def anext(__i: SupportsAnext[_T], default: _VT) -> _T | _VT: ... + async def anext(__i: SupportsAnext[_T], __default: _VT) -> _T | _VT: ... # compile() returns a CodeType, unless the flags argument includes PyCF_ONLY_AST (=1024), # in which case it returns ast.AST. We have overloads for flag 0 (the default) and for @@ -1340,9 +1341,9 @@ def getattr(__o: object, __name: str, __default: None) -> Any | None: ... @overload def getattr(__o: object, __name: str, __default: bool) -> Any | bool: ... @overload -def getattr(__o: object, name: str, __default: list[Any]) -> Any | list[Any]: ... +def getattr(__o: object, __name: str, __default: list[Any]) -> Any | list[Any]: ... @overload -def getattr(__o: object, name: str, __default: dict[Any, Any]) -> Any | dict[Any, Any]: ... +def getattr(__o: object, __name: str, __default: dict[Any, Any]) -> Any | dict[Any, Any]: ... @overload def getattr(__o: object, __name: str, __default: _T) -> Any | _T: ... def globals() -> dict[str, Any]: ... @@ -1357,13 +1358,13 @@ class _GetItemIterable(Protocol[_T_co]): def __getitem__(self, __i: int) -> _T_co: ... @overload -def iter(__iterable: SupportsIter[_SupportsNextT]) -> _SupportsNextT: ... +def iter(__object: SupportsIter[_SupportsNextT]) -> _SupportsNextT: ... @overload -def iter(__iterable: _GetItemIterable[_T]) -> Iterator[_T]: ... +def iter(__object: _GetItemIterable[_T]) -> Iterator[_T]: ... @overload -def iter(__function: Callable[[], _T | None], __sentinel: None) -> Iterator[_T]: ... +def iter(__object: Callable[[], _T | None], __sentinel: None) -> Iterator[_T]: ... @overload -def iter(__function: Callable[[], _T], __sentinel: object) -> Iterator[_T]: ... +def iter(__object: Callable[[], _T], __sentinel: object) -> Iterator[_T]: ... # Keep this alias in sync with unittest.case._ClassInfo if sys.version_info >= (3, 10): @@ -1544,8 +1545,7 @@ def open( ) -> IO[Any]: ... def ord(__c: str | bytes | bytearray) -> int: ... -class _SupportsWriteAndFlush(SupportsWrite[_T_contra], Protocol[_T_contra]): - def flush(self) -> None: ... +class _SupportsWriteAndFlush(SupportsWrite[_T_contra], SupportsFlush, Protocol[_T_contra]): ... @overload def print( diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index 0a85600e99b7..658cfb2d40ed 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -30,7 +30,7 @@ def exp(__z: _C) -> complex: ... def isclose(a: _C, b: _C, *, rel_tol: SupportsFloat = 1e-09, abs_tol: SupportsFloat = 0.0) -> bool: ... def isinf(__z: _C) -> bool: ... def isnan(__z: _C) -> bool: ... -def log(__x: _C, __y_obj: _C = ...) -> complex: ... +def log(__x: _C, __base: _C = ...) -> complex: ... def log10(__z: _C) -> complex: ... def phase(__z: _C) -> float: ... def polar(__z: _C) -> tuple[float, float]: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index f8c92392a599..985a52702bc8 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -213,7 +213,7 @@ class StreamWriter(Codec): def reset(self) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... - def __getattr__(self, name: str, getattr: Callable[[str], Any] = ...) -> Any: ... + def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ... class StreamReader(Codec): stream: _ReadableStream @@ -227,7 +227,7 @@ class StreamReader(Codec): def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> str: ... - def __getattr__(self, name: str, getattr: Callable[[str], Any] = ...) -> Any: ... + def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ... # Doesn't actually inherit from TextIO, but wraps a BinaryIO to provide text reading and writing # and delegates attributes to the underlying binary stream with __getattr__. diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index bb214b5ea19b..bb51dec50cab 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -372,6 +372,13 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): def setdefault(self: OrderedDict[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ... @overload def setdefault(self, key: _KT, default: _VT) -> _VT: ... + # Same as dict.pop, but accepts keyword arguments + @overload + def pop(self, key: _KT) -> _VT: ... + @overload + def pop(self, key: _KT, default: _VT) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T) -> _VT | _T: ... def __eq__(self, __value: object) -> bool: ... if sys.version_info >= (3, 9): @overload diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi index 7520c2f5b676..7f101bf79f6d 100644 --- a/mypy/typeshed/stdlib/compileall.pyi +++ b/mypy/typeshed/stdlib/compileall.pyi @@ -6,7 +6,7 @@ from typing import Any, Protocol __all__ = ["compile_dir", "compile_file", "compile_path"] class _SupportsSearch(Protocol): - def search(self, string: str) -> Any: ... + def search(self, __string: str) -> Any: ... if sys.version_info >= (3, 10): def compile_dir( diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index c1bfedd2d1da..ce46d0d39830 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -56,7 +56,7 @@ class AbstractAsyncContextManager(Protocol[_T_co]): class ContextDecorator: def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManager(AbstractContextManager[_T_co], ContextDecorator, Generic[_T_co]): +class _GeneratorContextManager(AbstractContextManager[_T_co], ContextDecorator): # __init__ and all instance attributes are actually inherited from _GeneratorContextManagerBase # _GeneratorContextManagerBase is more trouble than it's worth to include in the stub; see #6676 def __init__(self, func: Callable[..., Iterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... @@ -81,7 +81,7 @@ if sys.version_info >= (3, 10): class AsyncContextDecorator: def __call__(self, func: _AF) -> _AF: ... - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co], AsyncContextDecorator, Generic[_T_co]): + class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co], AsyncContextDecorator): # __init__ and these attributes are actually defined in the base class _GeneratorContextManagerBase, # which is more trouble than it's worth to include in the stub (see #6676) def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index 63b5f80aea6c..a67d0349b46a 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -23,17 +23,10 @@ class ContextVar(Generic[_T]): def name(self) -> str: ... @overload def get(self) -> _T: ... - if sys.version_info >= (3, 8): - @overload - def get(self, default: _T) -> _T: ... - @overload - def get(self, default: _D) -> _D | _T: ... - else: - @overload - def get(self, __default: _T) -> _T: ... - @overload - def get(self, __default: _D) -> _D | _T: ... - + @overload + def get(self, __default: _T) -> _T: ... + @overload + def get(self, __default: _D) -> _D | _T: ... def set(self, __value: _T) -> Token[_T]: ... def reset(self, __token: Token[_T]) -> None: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index 0068d67b6ad1..d7115528868b 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -90,5 +90,5 @@ class _error(Exception): ... error: tuple[type[_error], type[OSError]] -def whichdb(filename: str) -> str: ... +def whichdb(filename: str) -> str | None: ... def open(file: str, flag: _TFlags = "r", mode: int = 0o666) -> _Database: ... diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index f3c05781ad92..7e334ef0c504 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -206,8 +206,8 @@ class DocTestCase(unittest.TestCase): self, test: DocTest, optionflags: int = 0, - setUp: Callable[[DocTest], Any] | None = None, - tearDown: Callable[[DocTest], Any] | None = None, + setUp: Callable[[DocTest], object] | None = None, + tearDown: Callable[[DocTest], object] | None = None, checker: OutputChecker | None = None, ) -> None: ... def runTest(self) -> None: ... diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi index e158e89818f7..94623e96f208 100644 --- a/mypy/typeshed/stdlib/email/headerregistry.pyi +++ b/mypy/typeshed/stdlib/email/headerregistry.pyi @@ -143,9 +143,9 @@ if sys.version_info >= (3, 8): class _HeaderParser(Protocol): max_count: ClassVar[Literal[1] | None] @staticmethod - def value_parser(value: str) -> TokenList: ... + def value_parser(__value: str) -> TokenList: ... @classmethod - def parse(cls, value: str, kwds: dict[str, Any]) -> None: ... + def parse(cls, __value: str, __kwds: dict[str, Any]) -> None: ... class HeaderRegistry: registry: dict[str, type[_HeaderParser]] diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 3e5e496ab501..305568fce6cf 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -176,7 +176,7 @@ class HTTPConnection: def connect(self) -> None: ... def close(self) -> None: ... def putrequest(self, method: str, url: str, skip_host: bool = False, skip_accept_encoding: bool = False) -> None: ... - def putheader(self, header: str, *argument: str) -> None: ... + def putheader(self, header: str | bytes, *argument: str | bytes) -> None: ... def endheaders(self, message_body: _DataType | None = None, *, encode_chunked: bool = False) -> None: ... def send(self, data: _DataType | str) -> None: ... diff --git a/mypy/typeshed/stdlib/imghdr.pyi b/mypy/typeshed/stdlib/imghdr.pyi index ed3647f20fc5..d0960a5a1c5c 100644 --- a/mypy/typeshed/stdlib/imghdr.pyi +++ b/mypy/typeshed/stdlib/imghdr.pyi @@ -6,8 +6,8 @@ __all__ = ["what"] class _ReadableBinary(Protocol): def tell(self) -> int: ... - def read(self, size: int) -> bytes: ... - def seek(self, offset: int) -> Any: ... + def read(self, __size: int) -> bytes: ... + def seek(self, __offset: int) -> Any: ... @overload def what(file: StrPath | _ReadableBinary, h: None = None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/imp.pyi b/mypy/typeshed/stdlib/imp.pyi index 3f2920de9c2b..b532f480fa13 100644 --- a/mypy/typeshed/stdlib/imp.pyi +++ b/mypy/typeshed/stdlib/imp.pyi @@ -45,7 +45,7 @@ class _FileLike(Protocol): def read(self) -> str | bytes: ... def close(self) -> Any: ... def __enter__(self) -> Any: ... - def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> Any: ... + def __exit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: TracebackType | None) -> Any: ... # PathLike doesn't work for the pathname argument here def load_source(name: str, pathname: str, file: _FileLike | None = None) -> types.ModuleType: ... diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 438dbafb48c3..8c395f8a18af 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -69,7 +69,7 @@ if sys.version_info >= (3, 12): def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec( - self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ... + self, __fullname: str, __path: Sequence[str] | None, __target: types.ModuleType | None = ... ) -> ModuleSpec | None: ... class PathEntryFinder(metaclass=ABCMeta): @@ -84,7 +84,7 @@ else: def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec( - self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ... + self, __fullname: str, __path: Sequence[str] | None, __target: types.ModuleType | None = ... ) -> ModuleSpec | None: ... class PathEntryFinder(Finder): @@ -138,10 +138,10 @@ if sys.version_info >= (3, 9): # which is not the case. @overload @abstractmethod - def open(self, mode: Literal["r", "w"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... + def open(self, __mode: Literal["r", "w"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open(self, mode: Literal["rb", "wb"]) -> IO[bytes]: ... + def open(self, __mode: Literal["rb", "wb"]) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 945e8bcbbdee..13a8c4330a50 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,5 +1,5 @@ import sys -from collections.abc import Container, Iterable, Iterator +from collections.abc import Iterable, Iterator from typing import Any, Generic, SupportsInt, TypeVar, overload from typing_extensions import Literal, Self, TypeAlias @@ -70,7 +70,7 @@ class _BaseAddress(_IPAddressBase, SupportsInt): @property def packed(self) -> bytes: ... -class _BaseNetwork(_IPAddressBase, Container[_A], Iterable[_A], Generic[_A]): +class _BaseNetwork(_IPAddressBase, Generic[_A]): network_address: _A netmask: _A def __init__(self, address: object, strict: bool = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index ffa8e19391dd..55f9c92d8cac 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -241,10 +241,19 @@ class product(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class permutations(Iterator[tuple[_T, ...]], Generic[_T]): - def __init__(self, iterable: Iterable[_T], r: int | None = ...) -> None: ... +class permutations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[3]) -> permutations[tuple[_T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[4]) -> permutations[tuple[_T, _T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[5]) -> permutations[tuple[_T, _T, _T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: int | None = ...) -> permutations[tuple[_T, ...]]: ... def __iter__(self) -> Self: ... - def __next__(self) -> tuple[_T, ...]: ... + def __next__(self) -> _T_co: ... class combinations(Iterator[_T_co]): @overload diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index 2e95c659dbcd..c18523e04361 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -8,7 +8,6 @@ from _locale import ( LC_NUMERIC as LC_NUMERIC, LC_TIME as LC_TIME, localeconv as localeconv, - setlocale as setlocale, strcoll as strcoll, strxfrm as strxfrm, ) @@ -16,7 +15,7 @@ from _locale import ( # This module defines a function "str()", which is why "str" can't be used # as a type annotation or type alias. from builtins import str as _str -from collections.abc import Callable +from collections.abc import Callable, Iterable from decimal import Decimal from typing import Any @@ -131,6 +130,7 @@ def getdefaultlocale( envvars: tuple[_str, ...] = ("LC_ALL", "LC_CTYPE", "LANG", "LANGUAGE") ) -> tuple[_str | None, _str | None]: ... def getlocale(category: int = ...) -> tuple[_str | None, _str | None]: ... +def setlocale(category: int, locale: _str | Iterable[_str | None] | None = None) -> _str: ... def getpreferredencoding(do_setlocale: bool = True) -> _str: ... def normalize(localename: _str) -> _str: ... def resetlocale(category: int = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index db797d4180ea..5a72b1fcd799 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -7,7 +7,7 @@ from re import Pattern from string import Template from time import struct_time from types import FrameType, TracebackType -from typing import Any, ClassVar, Generic, TextIO, TypeVar, overload +from typing import Any, ClassVar, Generic, Protocol, TextIO, TypeVar, overload from typing_extensions import Literal, Self, TypeAlias if sys.version_info >= (3, 11): @@ -66,10 +66,20 @@ if sys.version_info >= (3, 12): _SysExcInfoType: TypeAlias = tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None] _ExcInfoType: TypeAlias = None | bool | _SysExcInfoType | BaseException _ArgsType: TypeAlias = tuple[object, ...] | Mapping[str, object] -_FilterType: TypeAlias = Filter | Callable[[LogRecord], bool] _Level: TypeAlias = int | str _FormatStyle: TypeAlias = Literal["%", "{", "$"] +if sys.version_info >= (3, 12): + class _SupportsFilter(Protocol): + def filter(self, __record: LogRecord) -> bool | LogRecord: ... + + _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool | LogRecord] | _SupportsFilter +else: + class _SupportsFilter(Protocol): + def filter(self, __record: LogRecord) -> bool: ... + + _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool] | _SupportsFilter + raiseExceptions: bool logThreads: bool logMultiprocessing: bool diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index dd4b865a3574..c52f1c1f5453 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -91,7 +91,7 @@ class Pool: func: Callable[[_S], _T], iterable: Iterable[_S], chunksize: int | None = None, - callback: Callable[[_T], object] | None = None, + callback: Callable[[list[_T]], object] | None = None, error_callback: Callable[[BaseException], object] | None = None, ) -> MapResult[_T]: ... def imap(self, func: Callable[[_S], _T], iterable: Iterable[_S], chunksize: int | None = 1) -> IMapIterator[_T]: ... @@ -102,7 +102,7 @@ class Pool: func: Callable[..., _T], iterable: Iterable[Iterable[Any]], chunksize: int | None = None, - callback: Callable[[_T], object] | None = None, + callback: Callable[[list[_T]], object] | None = None, error_callback: Callable[[BaseException], object] | None = None, ) -> AsyncResult[list[_T]]: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 686a45d9ae41..636d58842158 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -73,7 +73,7 @@ def synchronized(obj: ctypes.Array[_CT], lock: _LockLike | None = None, ctx: Any def synchronized(obj: _CT, lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedBase[_CT]: ... class _AcquireFunc(Protocol): - def __call__(self, block: bool = ..., timeout: float | None = ...) -> bool: ... + def __call__(self, __block: bool = ..., __timeout: float | None = ...) -> bool: ... class SynchronizedBase(Generic[_CT]): acquire: _AcquireFunc diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 7fd04218fd7c..2810d086ae49 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -923,10 +923,16 @@ def times() -> times_result: ... def waitpid(__pid: int, __options: int) -> tuple[int, int]: ... if sys.platform == "win32": - if sys.version_info >= (3, 8): - def startfile(path: StrOrBytesPath, operation: str | None = None) -> None: ... + if sys.version_info >= (3, 10): + def startfile( + filepath: StrOrBytesPath, + operation: str = ..., + arguments: str = "", + cwd: StrOrBytesPath | None = None, + show_cmd: int = 1, + ) -> None: ... else: - def startfile(filepath: StrOrBytesPath, operation: str | None = None) -> None: ... + def startfile(filepath: StrOrBytesPath, operation: str = ...) -> None: ... else: def spawnlp(mode: int, file: StrOrBytesPath, arg0: StrOrBytesPath, *args: StrOrBytesPath) -> int: ... @@ -964,9 +970,9 @@ else: def WTERMSIG(status: int) -> int: ... if sys.version_info >= (3, 8): def posix_spawn( - path: StrOrBytesPath, - argv: _ExecVArgs, - env: _ExecEnv, + __path: StrOrBytesPath, + __argv: _ExecVArgs, + __env: _ExecEnv, *, file_actions: Sequence[tuple[Any, ...]] | None = ..., setpgroup: int | None = ..., @@ -977,9 +983,9 @@ else: scheduler: tuple[Any, sched_param] | None = ..., ) -> int: ... def posix_spawnp( - path: StrOrBytesPath, - argv: _ExecVArgs, - env: _ExecEnv, + __path: StrOrBytesPath, + __argv: _ExecVArgs, + __env: _ExecEnv, *, file_actions: Sequence[tuple[Any, ...]] | None = ..., setpgroup: int | None = ..., diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi index e584d7f571a7..6db7daebbb41 100644 --- a/mypy/typeshed/stdlib/smtplib.pyi +++ b/mypy/typeshed/stdlib/smtplib.pyi @@ -68,9 +68,9 @@ def quotedata(data: str) -> str: ... class _AuthObject(Protocol): @overload - def __call__(self, challenge: None = None) -> str | None: ... + def __call__(self, __challenge: None = None) -> str | None: ... @overload - def __call__(self, challenge: bytes) -> str: ... + def __call__(self, __challenge: bytes) -> str: ... class SMTP: debuglevel: int diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index e85f49207763..236e093c9909 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -349,10 +349,10 @@ class Connection: def create_function(self, name: str, num_params: int, func: Callable[..., _SqliteData] | None) -> None: ... @overload - def cursor(self, cursorClass: None = None) -> Cursor: ... + def cursor(self, factory: None = None) -> Cursor: ... @overload - def cursor(self, cursorClass: Callable[[Connection], _CursorT]) -> _CursorT: ... - def execute(self, sql: str, parameters: _Parameters = ...) -> Cursor: ... + def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... + def execute(self, __sql: str, __parameters: _Parameters = ...) -> Cursor: ... def executemany(self, __sql: str, __parameters: Iterable[_Parameters]) -> Cursor: ... def executescript(self, __sql_script: str) -> Cursor: ... def interrupt(self) -> None: ... diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 1013db7ee984..b6cc23651ade 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -248,7 +248,7 @@ if sys.version_info >= (3, 11): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -260,7 +260,7 @@ if sys.version_info >= (3, 11): encoding: None = None, errors: None = None, input: ReadableBuffer | None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, timeout: float | None = None, user: str | int | None = None, group: str | int | None = None, @@ -452,7 +452,7 @@ elif sys.version_info >= (3, 10): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -464,7 +464,7 @@ elif sys.version_info >= (3, 10): encoding: None = None, errors: None = None, input: ReadableBuffer | None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, timeout: float | None = None, user: str | int | None = None, group: str | int | None = None, @@ -650,7 +650,7 @@ elif sys.version_info >= (3, 9): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -662,7 +662,7 @@ elif sys.version_info >= (3, 9): encoding: None = None, errors: None = None, input: ReadableBuffer | None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, timeout: float | None = None, user: str | int | None = None, group: str | int | None = None, @@ -829,7 +829,7 @@ else: shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -841,7 +841,7 @@ else: encoding: None = None, errors: None = None, input: ReadableBuffer | None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, timeout: float | None = None, ) -> CompletedProcess[bytes]: ... @overload @@ -1242,7 +1242,7 @@ if sys.version_info >= (3, 11): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -1253,7 +1253,7 @@ if sys.version_info >= (3, 11): input: _InputString | None = ..., encoding: None = None, errors: None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, user: str | int | None = None, group: str | int | None = None, extra_groups: Iterable[str | int] | None = None, @@ -1428,7 +1428,7 @@ elif sys.version_info >= (3, 10): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -1439,7 +1439,7 @@ elif sys.version_info >= (3, 10): input: _InputString | None = ..., encoding: None = None, errors: None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, user: str | int | None = None, group: str | int | None = None, extra_groups: Iterable[str | int] | None = None, @@ -1608,7 +1608,7 @@ elif sys.version_info >= (3, 9): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -1619,7 +1619,7 @@ elif sys.version_info >= (3, 9): input: _InputString | None = ..., encoding: None = None, errors: None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, user: str | int | None = None, group: str | int | None = None, extra_groups: Iterable[str | int] | None = None, @@ -1769,7 +1769,7 @@ else: shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, @@ -1780,7 +1780,7 @@ else: input: _InputString | None = ..., encoding: None = None, errors: None = None, - text: Literal[None, False] = None, + text: Literal[False] | None = None, ) -> bytes: ... @overload def check_output( @@ -1990,14 +1990,14 @@ class Popen(Generic[AnyStr]): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any | None = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = (), *, - text: Literal[None, False] = None, + text: Literal[False] | None = None, encoding: None = None, errors: None = None, user: str | int | None = None, @@ -2175,14 +2175,14 @@ class Popen(Generic[AnyStr]): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any | None = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = (), *, - text: Literal[None, False] = None, + text: Literal[False] | None = None, encoding: None = None, errors: None = None, user: str | int | None = None, @@ -2354,14 +2354,14 @@ class Popen(Generic[AnyStr]): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any | None = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = (), *, - text: Literal[None, False] = None, + text: Literal[False] | None = None, encoding: None = None, errors: None = None, user: str | int | None = None, @@ -2514,14 +2514,14 @@ class Popen(Generic[AnyStr]): shell: bool = False, cwd: StrOrBytesPath | None = None, env: _ENV | None = None, - universal_newlines: Literal[False, None] = None, + universal_newlines: Literal[False] | None = None, startupinfo: Any | None = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = (), *, - text: Literal[None, False] = None, + text: Literal[False] | None = None, encoding: None = None, errors: None = None, ) -> None: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index cf3b1bc47d75..1d4111af3a49 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -17,7 +17,9 @@ _OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall # Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder` class _MetaPathFinder(Protocol): - def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ...) -> ModuleSpec | None: ... + def find_spec( + self, __fullname: str, __path: Sequence[str] | None, __target: ModuleType | None = ... + ) -> ModuleSpec | None: ... # ----- sys variables ----- if sys.platform != "win32": diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index c35db3ef7e34..d0eb97aa5ebd 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -7,7 +7,7 @@ from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType from typing import Any, Generic, NamedTuple, TypeVar, overload, type_check_only -from typing_extensions import Literal, TypeAlias, TypedDict +from typing_extensions import Literal, TypeAlias, TypedDict, deprecated if sys.version_info >= (3, 9): __all__ = [ @@ -273,10 +273,14 @@ class Variable: def trace_add(self, mode: _TraceMode, callback: Callable[[str, str, str], object]) -> str: ... def trace_remove(self, mode: _TraceMode, cbname: str) -> None: ... def trace_info(self) -> list[tuple[tuple[_TraceMode, ...], str]]: ... - def trace_variable(self, mode, callback): ... # deprecated - def trace_vdelete(self, mode, cbname) -> None: ... # deprecated - def trace_vinfo(self): ... # deprecated - trace = trace_variable # deprecated + @deprecated("use trace_add() instead of trace()") + def trace(self, mode, callback): ... + @deprecated("use trace_add() instead of trace_variable()") + def trace_variable(self, mode, callback): ... + @deprecated("use trace_remove() instead of trace_vdelete()") + def trace_vdelete(self, mode, cbname) -> None: ... + @deprecated("use trace_info() instead of trace_vinfo()") + def trace_vinfo(self): ... def __eq__(self, other: object) -> bool: ... class StringVar(Variable): @@ -343,9 +347,8 @@ class Misc: def tk_focusFollowsMouse(self) -> None: ... def tk_focusNext(self) -> Misc | None: ... def tk_focusPrev(self) -> Misc | None: ... - @overload - def after(self, ms: int, func: None = None) -> None: ... - @overload + # .after() can be called without the "func" argument, but it is basically never what you want. + # It behaves like time.sleep() and freezes the GUI app. def after(self, ms: int | Literal["idle"], func: Callable[..., object], *args: Any) -> str: ... # after_idle is essentially partialmethod(after, "idle") def after_idle(self, func: Callable[..., object], *args: Any) -> str: ... diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi index 4a6ab42b3e33..8f438537369c 100644 --- a/mypy/typeshed/stdlib/tkinter/dnd.pyi +++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi @@ -6,7 +6,7 @@ if sys.version_info >= (3, 9): __all__ = ["dnd_start", "DndHandler"] class _DndSource(Protocol): - def dnd_end(self, target: Widget | None, event: Event[Misc] | None) -> None: ... + def dnd_end(self, __target: Widget | None, __event: Event[Misc] | None) -> None: ... class DndHandler: root: ClassVar[Tk | None] diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index a50bbf145b9f..fcaf5264c5e3 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -335,7 +335,7 @@ class SimpleNamespace: def __delattr__(self, __name: str) -> None: ... class _LoaderProtocol(Protocol): - def load_module(self, fullname: str) -> ModuleType: ... + def load_module(self, __fullname: str) -> ModuleType: ... class ModuleType: __name__: str diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index ad5719ca9f56..7694157d70fe 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -513,7 +513,7 @@ class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): @abstractmethod def __len__(self) -> int: ... -class Sequence(Collection[_T_co], Reversible[_T_co], Generic[_T_co]): +class Sequence(Collection[_T_co], Reversible[_T_co]): @overload @abstractmethod def __getitem__(self, index: int) -> _T_co: ... @@ -607,7 +607,7 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, def __xor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __rxor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... -class KeysView(MappingView, AbstractSet[_KT_co], Generic[_KT_co]): +class KeysView(MappingView, AbstractSet[_KT_co]): def __init__(self, mapping: Mapping[_KT_co, Any]) -> None: ... # undocumented def __and__(self, other: Iterable[Any]) -> set[_KT_co]: ... def __rand__(self, other: Iterable[_T]) -> set[_T]: ... @@ -623,7 +623,7 @@ class KeysView(MappingView, AbstractSet[_KT_co], Generic[_KT_co]): def __xor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __rxor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... -class ValuesView(MappingView, Collection[_VT_co], Generic[_VT_co]): +class ValuesView(MappingView, Collection[_VT_co]): def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ... # undocumented def __contains__(self, value: object) -> bool: ... def __iter__(self) -> Iterator[_VT_co]: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index aa04e16d62ec..cc5d683e245a 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -52,7 +52,7 @@ else: ) -> bool | None: ... if sys.version_info >= (3, 8): - def addModuleCleanup(__function: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addModuleCleanup(__function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... def doModuleCleanups() -> None: ... if sys.version_info >= (3, 11): @@ -136,7 +136,7 @@ class TestCase: def assertRaises( self, expected_exception: type[BaseException] | tuple[type[BaseException], ...], - callable: Callable[..., Any], + callable: Callable[..., object], *args: Any, **kwargs: Any, ) -> None: ... @@ -149,7 +149,7 @@ class TestCase: self, expected_exception: type[BaseException] | tuple[type[BaseException], ...], expected_regex: str | Pattern[str], - callable: Callable[..., Any], + callable: Callable[..., object], *args: Any, **kwargs: Any, ) -> None: ... @@ -161,7 +161,7 @@ class TestCase: def assertWarns( self, expected_warning: type[Warning] | tuple[type[Warning], ...], - callable: Callable[_P, Any], + callable: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs, ) -> None: ... @@ -174,7 +174,7 @@ class TestCase: self, expected_warning: type[Warning] | tuple[type[Warning], ...], expected_regex: str | Pattern[str], - callable: Callable[_P, Any], + callable: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs, ) -> None: ... @@ -256,9 +256,9 @@ class TestCase: def id(self) -> str: ... def shortDescription(self) -> str | None: ... if sys.version_info >= (3, 8): - def addCleanup(self, __function: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addCleanup(self, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... else: - def addCleanup(self, function: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addCleanup(self, function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): def enterContext(self, cm: AbstractContextManager[_T]) -> _T: ... @@ -266,7 +266,7 @@ class TestCase: def doCleanups(self) -> None: ... if sys.version_info >= (3, 8): @classmethod - def addClassCleanup(cls, __function: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addClassCleanup(cls, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... @classmethod def doClassCleanups(cls) -> None: ... @@ -299,9 +299,9 @@ class TestCase: class FunctionTestCase(TestCase): def __init__( self, - testFunc: Callable[[], Any], - setUp: Callable[[], Any] | None = None, - tearDown: Callable[[], Any] | None = None, + testFunc: Callable[[], object], + setUp: Callable[[], object] | None = None, + tearDown: Callable[[], object] | None = None, description: str | None = None, ) -> None: ... def runTest(self) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index d29e9a2b8da8..3e8cb7b764c2 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -11,7 +11,7 @@ MAIN_EXAMPLES: str MODULE_EXAMPLES: str class _TestRunner(Protocol): - def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... + def run(self, __test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... # not really documented class TestProgram: diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi index 845accfebedd..c42d1346e4b7 100644 --- a/mypy/typeshed/stdlib/unittest/util.pyi +++ b/mypy/typeshed/stdlib/unittest/util.pyi @@ -1,4 +1,4 @@ -from collections.abc import Sequence +from collections.abc import MutableSequence, Sequence from typing import Any, TypeVar from typing_extensions import TypeAlias @@ -17,7 +17,7 @@ def _common_shorten_repr(*args: str) -> tuple[str, ...]: ... def safe_repr(obj: object, short: bool = False) -> str: ... def strclass(cls: type) -> str: ... def sorted_list_difference(expected: Sequence[_T], actual: Sequence[_T]) -> tuple[list[_T], list[_T]]: ... -def unorderable_list_difference(expected: Sequence[_T], actual: Sequence[_T]) -> tuple[list[_T], list[_T]]: ... +def unorderable_list_difference(expected: MutableSequence[_T], actual: MutableSequence[_T]) -> tuple[list[_T], list[_T]]: ... def three_way_cmp(x: Any, y: Any) -> int: ... def _count_diff_all_purpose(actual: Sequence[_T], expected: Sequence[_T]) -> list[_Mismatch[_T]]: ... def _count_diff_hashable(actual: Sequence[_T], expected: Sequence[_T]) -> list[_Mismatch[_T]]: ... diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index a4849dfa2e6e..237a4d264b51 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -227,7 +227,8 @@ class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): class _HTTPConnectionProtocol(Protocol): def __call__( self, - host: str, + __host: str, + *, port: int | None = ..., timeout: float = ..., source_address: tuple[str, int] | None = ..., diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index d8ff2f5b6090..b08ca88e7e97 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -86,7 +86,7 @@ class Element: attrib: dict[str, str] text: str | None tail: str | None - def __init__(self, tag: str | Callable[..., Element], attrib: dict[str, str] = ..., **extra: str) -> None: ... + def __init__(self, tag: str, attrib: dict[str, str] = ..., **extra: str) -> None: ... def append(self, __subelement: Element) -> None: ... def clear(self) -> None: ... def extend(self, __elements: Iterable[Element]) -> None: ... @@ -132,7 +132,7 @@ def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: def Comment(text: str | None = None) -> Element: ... def ProcessingInstruction(target: str, text: str | None = None) -> Element: ... -PI: Callable[..., Element] +PI = ProcessingInstruction class QName: text: str diff --git a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi index 06e03a1e4d06..528f35963947 100644 --- a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi +++ b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi @@ -11,7 +11,7 @@ def quoteattr(data: str, entities: Mapping[str, str] = {}) -> str: ... class XMLGenerator(handler.ContentHandler): def __init__( self, - out: TextIOBase | RawIOBase | StreamWriter | StreamReaderWriter | SupportsWrite[str] | None = None, + out: TextIOBase | RawIOBase | StreamWriter | StreamReaderWriter | SupportsWrite[bytes] | None = None, encoding: str = "iso-8859-1", short_empty_elements: bool = False, ) -> None: ... From 058f8fd25fc425092bb4116b318cb61e1e43e0ff Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 18 Nov 2023 22:34:59 +0300 Subject: [PATCH 0399/1617] Do not allow `TypedDict` classes with extra keywords (#16438) --- mypy/messages.py | 15 +++++++++++---- mypy/semanal_typeddict.py | 6 ++++++ test-data/unit/check-typeddict.test | 30 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 19aafedd5586..ddb048444695 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -991,10 +991,17 @@ def maybe_note_about_special_args(self, callee: CallableType, context: Context) context, ) + def unexpected_keyword_argument_for_function( + self, for_func: str, name: str, context: Context, *, matches: list[str] | None = None + ) -> None: + msg = f'Unexpected keyword argument "{name}"' + for_func + if matches: + msg += f"; did you mean {pretty_seq(matches, 'or')}?" + self.fail(msg, context, code=codes.CALL_ARG) + def unexpected_keyword_argument( self, callee: CallableType, name: str, arg_type: Type, context: Context ) -> None: - msg = f'Unexpected keyword argument "{name}"' + for_function(callee) # Suggest intended keyword, look for type match else fallback on any match. matching_type_args = [] not_matching_type_args = [] @@ -1008,9 +1015,9 @@ def unexpected_keyword_argument( matches = best_matches(name, matching_type_args, n=3) if not matches: matches = best_matches(name, not_matching_type_args, n=3) - if matches: - msg += f"; did you mean {pretty_seq(matches, 'or')}?" - self.fail(msg, context, code=codes.CALL_ARG) + self.unexpected_keyword_argument_for_function( + for_function(callee), name, context, matches=matches + ) module = find_defining_module(self.modules, callee) if module: assert callee.definition is not None diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index a013cc040e89..f399d8872a32 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -323,6 +323,12 @@ def analyze_typeddict_classdef_fields( total: bool | None = True if "total" in defn.keywords: total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True) + if defn.keywords and defn.keywords.keys() != {"total"}: + for_function = ' for "__init_subclass__" of "TypedDict"' + for key in defn.keywords.keys(): + if key == "total": + continue + self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) required_keys = { field for (field, t) in zip(fields, types) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index c584906dd965..dc808390021a 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3408,3 +3408,33 @@ B = TypedDict("B", { # E: Type of a TypedDict key becomes "Any" due to an unfol }) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictWithClassLevelKeywords] +from typing import TypedDict, Generic, TypeVar + +T = TypeVar('T') + +class Meta(type): ... + +class WithMetaKeyword(TypedDict, metaclass=Meta): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "TypedDict" + ... + +class GenericWithMetaKeyword(TypedDict, Generic[T], metaclass=Meta): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "TypedDict" + ... + +# We still don't allow this, because the implementation is much easier +# and it does not make any practical sense to do it: +class WithTypeMeta(TypedDict, metaclass=type): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "TypedDict" + ... + +class OtherKeywords(TypedDict, a=1, b=2, c=3, total=True): # E: Unexpected keyword argument "a" for "__init_subclass__" of "TypedDict" \ + # E: Unexpected keyword argument "b" for "__init_subclass__" of "TypedDict" \ + # E: Unexpected keyword argument "c" for "__init_subclass__" of "TypedDict" + ... + +class TotalInTheMiddle(TypedDict, a=1, total=True, b=2, c=3): # E: Unexpected keyword argument "a" for "__init_subclass__" of "TypedDict" \ + # E: Unexpected keyword argument "b" for "__init_subclass__" of "TypedDict" \ + # E: Unexpected keyword argument "c" for "__init_subclass__" of "TypedDict" + ... +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 6cbdab8317f40e146058fd46ea96d5bbbea086c4 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 18 Nov 2023 20:22:54 +0000 Subject: [PATCH 0400/1617] Stubtest: more helpful errors if a function is missing from stub (#16517) --- mypy/stubtest.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 46468e8e18e0..98ab8a043aaf 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -102,7 +102,17 @@ def __init__( self.stub_object = stub_object self.runtime_object = runtime_object self.stub_desc = stub_desc or str(getattr(stub_object, "type", stub_object)) - self.runtime_desc = runtime_desc or _truncate(repr(runtime_object), 100) + + if runtime_desc is None: + runtime_sig = safe_inspect_signature(runtime_object) + if runtime_sig is None: + self.runtime_desc = _truncate(repr(runtime_object), 100) + else: + runtime_is_async = inspect.iscoroutinefunction(runtime_object) + description = describe_runtime_callable(runtime_sig, is_async=runtime_is_async) + self.runtime_desc = _truncate(description, 100) + else: + self.runtime_desc = runtime_desc def is_missing_stub(self) -> bool: """Whether or not the error is for something missing from the stub.""" @@ -1000,7 +1010,7 @@ def verify_funcitem( if signature: stub_sig = Signature.from_funcitem(stub) runtime_sig = Signature.from_inspect_signature(signature) - runtime_sig_desc = f'{"async " if runtime_is_coroutine else ""}def {signature}' + runtime_sig_desc = describe_runtime_callable(signature, is_async=runtime_is_coroutine) stub_desc = str(stub_sig) else: runtime_sig_desc, stub_desc = None, None @@ -1482,6 +1492,10 @@ def safe_inspect_signature(runtime: Any) -> inspect.Signature | None: return None +def describe_runtime_callable(signature: inspect.Signature, *, is_async: bool) -> str: + return f'{"async " if is_async else ""}def {signature}' + + def is_subtype_helper(left: mypy.types.Type, right: mypy.types.Type) -> bool: """Checks whether ``left`` is a subtype of ``right``.""" left = mypy.types.get_proper_type(left) From e81309e955b23737a5efe3e24a8f705da60fcb82 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 18 Nov 2023 12:30:36 -0800 Subject: [PATCH 0401/1617] PEP 702: decorator is in warnings, not typing-extensions (#16488) Followup from https://github.com/python/mypy/pull/16457/files#r1392715784 --- mypy/test/teststubtest.py | 4 ++-- mypy/types.py | 2 +- test-data/unit/lib-stub/typing_extensions.pyi | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 58602be3a624..a2e9668a9ac4 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -72,7 +72,6 @@ class Tuple(Sequence[_T_co]): ... class NamedTuple(tuple[Any, ...]): ... def overload(func: _T) -> _T: ... def type_check_only(func: _T) -> _T: ... -def deprecated(__msg: str) -> Callable[[_T], _T]: ... def final(func: _T) -> _T: ... """ @@ -635,7 +634,8 @@ def f5(__b: str) -> str: ... ) yield Case( stub=""" - from typing import deprecated, final + from typing import final + from typing_extensions import deprecated class Foo: @overload @final diff --git a/mypy/types.py b/mypy/types.py index e7738bd7d088..d19766c1de34 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -123,7 +123,7 @@ ANNOTATED_TYPE_NAMES: Final = ("typing.Annotated", "typing_extensions.Annotated") # Supported @deprecated type names -DEPRECATED_TYPE_NAMES: Final = ("typing.deprecated", "typing_extensions.deprecated") +DEPRECATED_TYPE_NAMES: Final = ("warnings.deprecated", "typing_extensions.deprecated") # We use this constant in various places when checking `tuple` subtyping: TUPLE_LIKE_INSTANCE_NAMES: Final = ( diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 216005e3cf83..c88aa5c815c5 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -1,5 +1,5 @@ import typing -from typing import Any, Mapping, Iterable, Iterator, NoReturn as NoReturn, Dict, Tuple, Type +from typing import Any, Callable, Mapping, Iterable, Iterator, NoReturn as NoReturn, Dict, Tuple, Type from typing import TYPE_CHECKING as TYPE_CHECKING from typing import NewType as NewType, overload as overload @@ -75,5 +75,6 @@ def dataclass_transform( ) -> Callable[[T], T]: ... def override(__arg: _T) -> _T: ... +def deprecated(__msg: str) -> Callable[[_T], _T]: ... _FutureFeatureFixture = 0 From 706389d3d551dc8f18ce5e5f48584f351a7a07a1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 18 Nov 2023 12:31:14 -0800 Subject: [PATCH 0402/1617] stubtest: hack for "" defaults (#16433) See python/cpython#87233 --- mypy/stubtest.py | 34 +++++++++++++++++++++++++++++++++- mypy/test/teststubtest.py | 18 ++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 98ab8a043aaf..c02a3efd8dc0 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -55,6 +55,17 @@ def __repr__(self) -> str: T = TypeVar("T") MaybeMissing: typing_extensions.TypeAlias = Union[T, Missing] + +class Unrepresentable: + """Marker object for unrepresentable parameter defaults.""" + + def __repr__(self) -> str: + return "" + + +UNREPRESENTABLE: typing_extensions.Final = Unrepresentable() + + _formatter: typing_extensions.Final = FancyFormatter(sys.stdout, sys.stderr, False) @@ -681,6 +692,7 @@ def _verify_arg_default_value( if ( stub_default is not UNKNOWN and stub_default is not ... + and runtime_arg.default is not UNREPRESENTABLE and ( stub_default != runtime_arg.default # We want the types to match exactly, e.g. in case the stub has @@ -1483,7 +1495,27 @@ def is_read_only_property(runtime: object) -> bool: def safe_inspect_signature(runtime: Any) -> inspect.Signature | None: try: - return inspect.signature(runtime) + try: + return inspect.signature(runtime) + except ValueError: + if ( + hasattr(runtime, "__text_signature__") + and "" in runtime.__text_signature__ + ): + # Try to fix up the signature. Workaround for + # https://github.com/python/cpython/issues/87233 + sig = runtime.__text_signature__.replace("", "...") + sig = inspect._signature_fromstr(inspect.Signature, runtime, sig) # type: ignore[attr-defined] + assert isinstance(sig, inspect.Signature) + new_params = [ + parameter.replace(default=UNREPRESENTABLE) + if parameter.default is ... + else parameter + for parameter in sig.parameters.values() + ] + return sig.replace(parameters=new_params) + else: + raise except Exception: # inspect.signature throws ValueError all the time # catch RuntimeError because of https://bugs.python.org/issue39504 diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index a2e9668a9ac4..34b266115166 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -428,6 +428,16 @@ def test_default_value(self) -> Iterator[Case]: error=None, ) + # Simulate "" + yield Case( + stub="def f11() -> None: ...", + runtime=""" + def f11(text=None) -> None: pass + f11.__text_signature__ = "(text=)" + """, + error="f11", + ) + @collect_cases def test_static_class_method(self) -> Iterator[Case]: yield Case( @@ -2281,6 +2291,14 @@ def f(a: int, b: int, *, c: int, d: int = 0, **kwargs: Any) -> None: == "def (a, b, *, c, d = ..., **kwargs)" ) + def test_builtin_signature_with_unrepresentable_default(self) -> None: + sig = mypy.stubtest.safe_inspect_signature(bytes.hex) + assert sig is not None + assert ( + str(mypy.stubtest.Signature.from_inspect_signature(sig)) + == "def (self, sep = ..., bytes_per_sep = ...)" + ) + def test_config_file(self) -> None: runtime = "temp = 5\n" stub = "from decimal import Decimal\ntemp: Decimal\n" From a3e488d24e25688e74f32ced52ba560f77791b8c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 19 Nov 2023 17:33:41 +0000 Subject: [PATCH 0403/1617] An attempt to fix mypyc tests on MacOS (#16520) Fixes https://github.com/python/mypy/issues/16420 Although this is not 100% clear yet, but after 20 runs on a Mac I have it no longer fails (without this patch it failed 20% of times). Btw, contrary to the comment, _my_ Linux Mint (which is an Ubuntu derivative) works perfectly (i.e. test passed 20 times even after I removed the `sleep()`). So it is not really Mac vs Linux issue. --- mypyc/test/test_run.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index df9d44eab73f..f5c902bf3b3d 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -172,12 +172,10 @@ def run_case_inner(self, testcase: DataDrivenTestCase) -> None: # new by distutils, shift the mtime of all of the # generated artifacts back by a second. fudge_dir_mtimes(WORKDIR, -1) - # On Ubuntu, changing the mtime doesn't work reliably. As + # On some OS, changing the mtime doesn't work reliably. As # a workaround, sleep. - # # TODO: Figure out a better approach, since this slows down tests. - if sys.platform == "linux": - time.sleep(1.0) + time.sleep(1.0) step += 1 with chdir_manager(".."): From 3e6b5528e662d0accb6def21d6963361fe894301 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 22 Nov 2023 11:05:27 +0000 Subject: [PATCH 0404/1617] Make imprecise constraints handling more robust (#16502) Fixes https://github.com/python/mypy/issues/16485 My initial implementation of imprecise constraints fallback was really fragile and ad-hoc, and I now see several edge case scenarios where we may end up using imprecise constraints for a `ParamSpec` while some precise ones are available. So I re-organized it: now we just infer everything as normally, and filter out imprecise (if needed) at the very end, when we have the full picture. I also fix an accidental omission in `expand_type()`. --- mypy/constraints.py | 76 +++++++++++-------- mypy/expandtype.py | 1 + .../unit/check-parameter-specification.test | 23 ++++++ 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 88ede372e011..d6a4b28799e5 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -226,25 +226,22 @@ def infer_constraints_for_callable( actual_type = mapper.expand_actual_type( actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] ) - if ( - param_spec - and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2) - and not incomplete_star_mapping - ): + if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): # If actual arguments are mapped to ParamSpec type, we can't infer individual # constraints, instead store them and infer single constraint at the end. # It is impossible to map actual kind to formal kind, so use some heuristic. # This inference is used as a fallback, so relying on heuristic should be OK. - param_spec_arg_types.append( - mapper.expand_actual_type( - actual_arg_type, arg_kinds[actual], None, arg_kinds[actual] + if not incomplete_star_mapping: + param_spec_arg_types.append( + mapper.expand_actual_type( + actual_arg_type, arg_kinds[actual], None, arg_kinds[actual] + ) ) - ) - actual_kind = arg_kinds[actual] - param_spec_arg_kinds.append( - ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind - ) - param_spec_arg_names.append(arg_names[actual] if arg_names else None) + actual_kind = arg_kinds[actual] + param_spec_arg_kinds.append( + ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind + ) + param_spec_arg_names.append(arg_names[actual] if arg_names else None) else: c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF) constraints.extend(c) @@ -267,6 +264,9 @@ def infer_constraints_for_callable( ), ) ) + if any(isinstance(v, ParamSpecType) for v in callee.variables): + # As a perf optimization filter imprecise constraints only when we can have them. + constraints = filter_imprecise_kinds(constraints) return constraints @@ -1094,29 +1094,18 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: ) param_spec_target: Type | None = None - skip_imprecise = ( - any(c.type_var == param_spec.id for c in res) and cactual.imprecise_arg_kinds - ) if not cactual_ps: max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]) prefix_len = min(prefix_len, max_prefix_len) - # This logic matches top-level callable constraint exception, if we managed - # to get other constraints for ParamSpec, don't infer one with imprecise kinds - if not skip_imprecise: - param_spec_target = Parameters( - arg_types=cactual.arg_types[prefix_len:], - arg_kinds=cactual.arg_kinds[prefix_len:], - arg_names=cactual.arg_names[prefix_len:], - variables=cactual.variables - if not type_state.infer_polymorphic - else [], - imprecise_arg_kinds=cactual.imprecise_arg_kinds, - ) + param_spec_target = Parameters( + arg_types=cactual.arg_types[prefix_len:], + arg_kinds=cactual.arg_kinds[prefix_len:], + arg_names=cactual.arg_names[prefix_len:], + variables=cactual.variables if not type_state.infer_polymorphic else [], + imprecise_arg_kinds=cactual.imprecise_arg_kinds, + ) else: - if ( - len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types) - and not skip_imprecise - ): + if len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types): param_spec_target = cactual_ps.copy_modified( prefix=Parameters( arg_types=cactual_ps.prefix.arg_types[prefix_len:], @@ -1611,3 +1600,24 @@ def infer_callable_arguments_constraints( infer_directed_arg_constraints(left_by_name.typ, right_by_name.typ, direction) ) return res + + +def filter_imprecise_kinds(cs: list[Constraint]) -> list[Constraint]: + """For each ParamSpec remove all imprecise constraints, if at least one precise available.""" + have_precise = set() + for c in cs: + if not isinstance(c.origin_type_var, ParamSpecType): + continue + if ( + isinstance(c.target, ParamSpecType) + or isinstance(c.target, Parameters) + and not c.target.imprecise_arg_kinds + ): + have_precise.add(c.type_var) + new_cs = [] + for c in cs: + if not isinstance(c.origin_type_var, ParamSpecType) or c.type_var not in have_precise: + new_cs.append(c) + if not isinstance(c.target, Parameters) or not c.target.imprecise_arg_kinds: + new_cs.append(c) + return new_cs diff --git a/mypy/expandtype.py b/mypy/expandtype.py index cb09a1ee99f5..3acec4b96d06 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -253,6 +253,7 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: t.prefix.arg_kinds + repl.arg_kinds, t.prefix.arg_names + repl.arg_names, variables=[*t.prefix.variables, *repl.variables], + imprecise_arg_kinds=repl.imprecise_arg_kinds, ) else: # We could encode Any as trivial parameters etc., but it would be too verbose. diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index d65a0214b599..af2be84f5412 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2163,3 +2163,26 @@ def func2(arg: T) -> List[Union[T, str]]: reveal_type(func2) # N: Revealed type is "def [T] (arg: T`-1) -> Union[T`-1, builtins.str]" reveal_type(func2(42)) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/paramspec.pyi] + +[case testParamSpecPreciseKindsUsedIfPossible] +from typing import Callable, Generic +from typing_extensions import ParamSpec + +P = ParamSpec('P') + +class Case(Generic[P]): + def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None: + pass + +def _test(a: int, b: int = 0) -> None: ... + +def parametrize( + func: Callable[P, None], *cases: Case[P], **named_cases: Case[P] +) -> Callable[[], None]: + ... + +parametrize(_test, Case(1, 2), Case(3, 4)) +parametrize(_test, Case(1, b=2), Case(3, b=4)) +parametrize(_test, Case(1, 2), Case(3)) +parametrize(_test, Case(1, 2), Case(3, b=4)) +[builtins fixtures/paramspec.pyi] From fc811aedbf696c54da144851ccaeeceb19ec9a5e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 22 Nov 2023 11:10:17 +0000 Subject: [PATCH 0405/1617] Fix polymorphic application for callback protocols (#16514) Fixes https://github.com/python/mypy/issues/16512 The problems were caused if same callback protocol appeared multiple times in a signature. Previous logic confused this with a recursive callback protocol. --- mypy/checkexpr.py | 16 +++++++++++----- test-data/unit/check-inference.test | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index da61833bbe5b..626584bc3a20 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6209,11 +6209,16 @@ class PolyTranslator(TypeTranslator): See docstring for apply_poly() for details. """ - def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None: + def __init__( + self, + poly_tvars: Iterable[TypeVarLikeType], + bound_tvars: frozenset[TypeVarLikeType] = frozenset(), + seen_aliases: frozenset[TypeInfo] = frozenset(), + ) -> None: self.poly_tvars = set(poly_tvars) # This is a simplified version of TypeVarScope used during semantic analysis. - self.bound_tvars: set[TypeVarLikeType] = set() - self.seen_aliases: set[TypeInfo] = set() + self.bound_tvars = bound_tvars + self.seen_aliases = seen_aliases def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]: found_vars = [] @@ -6289,10 +6294,11 @@ def visit_instance(self, t: Instance) -> Type: if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]: if t.type in self.seen_aliases: raise PolyTranslationError() - self.seen_aliases.add(t.type) call = find_member("__call__", t, t, is_operator=True) assert call is not None - return call.accept(self) + return call.accept( + PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type}) + ) return super().visit_instance(t) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 6c98ba2088b1..953855e502d6 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3788,3 +3788,28 @@ def func2(arg: T) -> List[Union[T, str]]: reveal_type(func2) # N: Revealed type is "def [S] (S`4) -> Union[S`4, builtins.str]" reveal_type(func2(42)) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/list.pyi] + +[case testInferenceAgainstGenericCallbackProtoMultiple] +from typing import Callable, Protocol, TypeVar +from typing_extensions import Concatenate, ParamSpec + +V_co = TypeVar("V_co", covariant=True) +class Metric(Protocol[V_co]): + def __call__(self) -> V_co: ... + +T = TypeVar("T") +P = ParamSpec("P") +def simple_metric(func: Callable[Concatenate[int, P], T]) -> Callable[P, T]: ... + +@simple_metric +def Negate(count: int, /, metric: Metric[float]) -> float: ... +@simple_metric +def Combine(count: int, m1: Metric[T], m2: Metric[T], /, *more: Metric[T]) -> T: ... + +reveal_type(Negate) # N: Revealed type is "def (metric: __main__.Metric[builtins.float]) -> builtins.float" +reveal_type(Combine) # N: Revealed type is "def [T] (def () -> T`4, def () -> T`4, *more: def () -> T`4) -> T`4" + +def m1() -> float: ... +def m2() -> float: ... +reveal_type(Combine(m1, m2)) # N: Revealed type is "builtins.float" +[builtins fixtures/list.pyi] From 242ad2ac4dec105fbed37c177d4cff5944a00f1d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 22 Nov 2023 11:12:57 +0000 Subject: [PATCH 0406/1617] Fix crash on TypeGuard in __call__ (#16516) Fixes https://github.com/python/mypy/issues/16187 Note there may be some more similar crashes, I don't handle them all properly, for now I leave a TODO and replace the `assert` with `if`, so at least we should not crash on an unhandled case. --- mypy/checker.py | 27 +++++++++++++++++---------- test-data/unit/check-typeguard.test | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b9a9d3affb90..7c6f59fafdc8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5670,22 +5670,29 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM if node.arg_kinds[0] != nodes.ARG_POS: # the first argument might be used as a kwarg called_type = get_proper_type(self.lookup_type(node.callee)) - assert isinstance(called_type, (CallableType, Overloaded)) + + # TODO: there are some more cases in check_call() to handle. + if isinstance(called_type, Instance): + call = find_member( + "__call__", called_type, called_type, is_operator=True + ) + if call is not None: + called_type = get_proper_type(call) # *assuming* the overloaded function is correct, there's a couple cases: # 1) The first argument has different names, but is pos-only. We don't # care about this case, the argument must be passed positionally. # 2) The first argument allows keyword reference, therefore must be the # same between overloads. - name = called_type.items[0].arg_names[0] - - if name in node.arg_names: - idx = node.arg_names.index(name) - # we want the idx-th variable to be narrowed - expr = collapse_walrus(node.args[idx]) - else: - self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node) - return {}, {} + if isinstance(called_type, (CallableType, Overloaded)): + name = called_type.items[0].arg_names[0] + if name in node.arg_names: + idx = node.arg_names.index(name) + # we want the idx-th variable to be narrowed + expr = collapse_walrus(node.args[idx]) + else: + self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node) + return {}, {} if literal(expr) == LITERAL_TYPE: # Note: we wrap the target type, so that we can special case later. # Namely, for isinstance() we use a normal meet, while TypeGuard is diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index b3b168e5c7c6..c48887bb016a 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -694,3 +694,18 @@ def foo(x: object) -> TypeGuard[List[str]]: ... def test(f: A[T]) -> T: ... reveal_type(test(foo)) # N: Revealed type is "builtins.str" [builtins fixtures/list.pyi] + +[case testNoCrashOnDunderCallTypeGuard] +from typing_extensions import TypeGuard + +class A: + def __call__(self, x) -> TypeGuard[int]: + return True + +a: A +assert a(x=1) + +x: object +assert a(x=x) +reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] From 5b1a231425ac807b7118aac6a68b633949412a36 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Nov 2023 22:08:09 +0100 Subject: [PATCH 0407/1617] Docs: update soft-error-limit default value to -1 (#16542) Default value of `MANY_ERRORS_THRESHOLD` was set to `-1` in https://github.com/python/mypy/pull/15138, which is also the default value of the `--soft-error-limit` CLI option. However the CLI docs were not updated accordingly. --- docs/source/command_line.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index a810c35cb77f..09836e2ffd20 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -787,7 +787,7 @@ in error messages. disable reporting most additional errors. The limit only applies if it seems likely that most of the remaining errors will not be useful or they may be overly noisy. If ``N`` is negative, there is - no limit. The default limit is 200. + no limit. The default limit is -1. .. option:: --force-uppercase-builtins From 50d6d0b145f3c4be2d7633d61c37244280217d76 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 25 Nov 2023 19:46:10 +0300 Subject: [PATCH 0408/1617] Do not allow class-level keywords for `NamedTuple` (#16526) Refs #16521 --- mypy/semanal.py | 2 +- mypy/semanal_namedtuple.py | 10 +++++++++- mypy/semanal_typeddict.py | 2 +- test-data/unit/check-namedtuple.test | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6714e8c56de9..3e3056a9adf7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -775,7 +775,7 @@ def file_context( self.globals = file_node.names self.tvar_scope = TypeVarLikeScope() - self.named_tuple_analyzer = NamedTupleAnalyzer(options, self) + self.named_tuple_analyzer = NamedTupleAnalyzer(options, self, self.msg) self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg) self.enum_call_analyzer = EnumCallAnalyzer(options, self) self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 80cf1c4e184a..bc3c5dd61894 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -9,6 +9,7 @@ from typing import Final, Iterator, List, Mapping, cast from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type +from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_NAMED_OPT, ARG_OPT, @@ -91,9 +92,12 @@ class NamedTupleAnalyzer: - def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None: + def __init__( + self, options: Options, api: SemanticAnalyzerInterface, msg: MessageBuilder + ) -> None: self.options = options self.api = api + self.msg = msg def analyze_namedtuple_classdef( self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool @@ -204,6 +208,10 @@ def check_namedtuple_classdef( ) else: default_items[name] = stmt.rvalue + if defn.keywords: + for_function = ' for "__init_subclass__" of "NamedTuple"' + for key in defn.keywords: + self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) return items, types, default_items, statements def check_namedtuple( diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index f399d8872a32..13aab4de65e4 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -325,7 +325,7 @@ def analyze_typeddict_classdef_fields( total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True) if defn.keywords and defn.keywords.keys() != {"total"}: for_function = ' for "__init_subclass__" of "TypedDict"' - for key in defn.keywords.keys(): + for key in defn.keywords: if key == "total": continue self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 14e075339572..51b02b500bd1 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1354,3 +1354,20 @@ class Test: self.item: self.Item # E: Name "self.Item" is not defined [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNoClassKeywordsForNamedTuple] +from typing import NamedTuple +class Test1(NamedTuple, x=1, y=2): # E: Unexpected keyword argument "x" for "__init_subclass__" of "NamedTuple" \ + # E: Unexpected keyword argument "y" for "__init_subclass__" of "NamedTuple" + ... + +class Meta(type): ... + +class Test2(NamedTuple, metaclass=Meta): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "NamedTuple" + ... + +# Technically this would work, but it is just easier for the implementation: +class Test3(NamedTuple, metaclass=type): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "NamedTuple" + ... +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] From 9289a336f7e292e33790520b9fe1bdf7ed266124 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 25 Nov 2023 22:32:09 +0000 Subject: [PATCH 0409/1617] Speed up tests by simplifying test fixtures (#16560) Move some definitions away from commonly used fixtures that are only needed in one or two test cases, as they will slow down many test cases. This speeds up `mypy/test/testcheck.py` by about 5% on my Linux desktop. --- test-data/unit/check-class-namedtuple.test | 2 +- test-data/unit/check-expressions.test | 3 +- test-data/unit/check-python310.test | 2 +- test-data/unit/check-tuples.test | 6 ++ test-data/unit/check-type-aliases.test | 2 +- test-data/unit/check-typeddict.test | 4 +- test-data/unit/fixtures/dict-full.pyi | 83 ++++++++++++++++++++++ test-data/unit/fixtures/dict.pyi | 33 ++------- test-data/unit/fixtures/tuple.pyi | 2 - 9 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 test-data/unit/fixtures/dict-full.pyi diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index a095f212b900..f334b9011645 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -301,7 +301,7 @@ reveal_type(X._field_defaults) # N: Revealed type is "builtins.dict[builtins.st # but it's inferred as `Mapping[str, object]` here due to the fixture we're using reveal_type(X.__annotations__) # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]" -[builtins fixtures/dict.pyi] +[builtins fixtures/dict-full.pyi] [case testNewNamedTupleUnit] from typing import NamedTuple diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 8fe68365e5ac..04b3f7a131cc 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1589,8 +1589,7 @@ if str(): ....a # E: "ellipsis" has no attribute "a" class A: pass -[builtins fixtures/dict.pyi] -[out] +[builtins fixtures/dict-full.pyi] -- Yield expression diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index d3cdf3af849d..cbb26a130738 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -931,7 +931,7 @@ match x: reveal_type(x) # N: Revealed type is "builtins.list[builtins.list[builtins.dict[builtins.int, builtins.int]]]" reveal_type(y) # N: Revealed type is "builtins.int" reveal_type(z) # N: Revealed type is "builtins.int" -[builtins fixtures/dict.pyi] +[builtins fixtures/dict-full.pyi] [case testMatchNonFinalMatchArgs] class A: diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 4f468b59fc3f..66115ca0c30d 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -957,6 +957,12 @@ for x in B(), A(): [builtins fixtures/for.pyi] [case testTupleIterable] +from typing import Iterable, Optional, TypeVar + +T = TypeVar("T") + +def sum(iterable: Iterable[T], start: Optional[T] = None) -> T: pass + y = 'a' x = sum((1,2)) if int(): diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 46f5ff07f1ac..4364a9bfa9dc 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1065,4 +1065,4 @@ def eval(e: Expr) -> int: return e[1] elif e[0] == 456: return -eval(e[1]) -[builtins fixtures/dict.pyi] +[builtins fixtures/dict-full.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index dc808390021a..d8022f85574c 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2708,7 +2708,7 @@ class TD(TypedDict): reveal_type(TD.__iter__) # N: Revealed type is "def (typing._TypedDict) -> typing.Iterator[builtins.str]" reveal_type(TD.__annotations__) # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]" reveal_type(TD.values) # N: Revealed type is "def (self: typing.Mapping[T`1, T_co`2]) -> typing.Iterable[T_co`2]" -[builtins fixtures/dict.pyi] +[builtins fixtures/dict-full.pyi] [typing fixtures/typing-typeddict.pyi] [case testGenericTypedDictAlias] @@ -3299,7 +3299,7 @@ main:10: error: No overload variant of "__ror__" of "dict" matches argument type main:10: note: Possible overload variants: main:10: note: def __ror__(self, Dict[Any, Any], /) -> Dict[Any, Any] main:10: note: def [T, T2] __ror__(self, Dict[T, T2], /) -> Dict[Union[Any, T], Union[Any, T2]] -[builtins fixtures/dict.pyi] +[builtins fixtures/dict-full.pyi] [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__ror__method] diff --git a/test-data/unit/fixtures/dict-full.pyi b/test-data/unit/fixtures/dict-full.pyi new file mode 100644 index 000000000000..f20369ce9332 --- /dev/null +++ b/test-data/unit/fixtures/dict-full.pyi @@ -0,0 +1,83 @@ +# Builtins stub used in dictionary-related test cases (more complete). + +from _typeshed import SupportsKeysAndGetItem +import _typeshed +from typing import ( + TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Sequence, + Self, +) + +T = TypeVar('T') +T2 = TypeVar('T2') +KT = TypeVar('KT') +VT = TypeVar('VT') + +class object: + def __init__(self) -> None: pass + def __init_subclass__(cls) -> None: pass + def __eq__(self, other: object) -> bool: pass + +class type: + __annotations__: Mapping[str, object] + +class dict(Mapping[KT, VT]): + @overload + def __init__(self, **kwargs: VT) -> None: pass + @overload + def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass + def __getitem__(self, key: KT) -> VT: pass + def __setitem__(self, k: KT, v: VT) -> None: pass + def __iter__(self) -> Iterator[KT]: pass + def __contains__(self, item: object) -> int: pass + def update(self, a: SupportsKeysAndGetItem[KT, VT]) -> None: pass + @overload + def get(self, k: KT) -> Optional[VT]: pass + @overload + def get(self, k: KT, default: Union[VT, T]) -> Union[VT, T]: pass + def __len__(self) -> int: ... + + # This was actually added in 3.9: + @overload + def __or__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... + @overload + def __or__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... + @overload + def __ror__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... + @overload + def __ror__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... + # dict.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, __value: _typeshed.SupportsKeysAndGetItem[KT, VT]) -> Self: ... + @overload + def __ior__(self, __value: Iterable[Tuple[KT, VT]]) -> Self: ... + +class int: # for convenience + def __add__(self, x: Union[int, complex]) -> int: pass + def __radd__(self, x: int) -> int: pass + def __sub__(self, x: Union[int, complex]) -> int: pass + def __neg__(self) -> int: pass + real: int + imag: int + +class str: pass # for keyword argument key type +class bytes: pass + +class list(Sequence[T]): # needed by some test cases + def __getitem__(self, x: int) -> T: pass + def __iter__(self) -> Iterator[T]: pass + def __mul__(self, x: int) -> list[T]: pass + def __contains__(self, item: object) -> bool: pass + def append(self, item: T) -> None: pass + +class tuple(Generic[T]): pass +class function: pass +class float: pass +class complex: pass +class bool(int): pass + +class ellipsis: + __class__: object +def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass +class BaseException: pass + +def iter(__iterable: Iterable[T]) -> Iterator[T]: pass diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index 7c0c8767f7d7..ed2287511161 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -1,4 +1,7 @@ -# Builtins stub used in dictionary-related test cases. +# Builtins stub used in dictionary-related test cases (stripped down). +# +# NOTE: Use dict-full.pyi if you need more builtins instead of adding here, +# if feasible. from _typeshed import SupportsKeysAndGetItem import _typeshed @@ -14,11 +17,9 @@ VT = TypeVar('VT') class object: def __init__(self) -> None: pass - def __init_subclass__(cls) -> None: pass def __eq__(self, other: object) -> bool: pass -class type: - __annotations__: Mapping[str, object] +class type: pass class dict(Mapping[KT, VT]): @overload @@ -36,28 +37,10 @@ class dict(Mapping[KT, VT]): def get(self, k: KT, default: Union[VT, T]) -> Union[VT, T]: pass def __len__(self) -> int: ... - # This was actually added in 3.9: - @overload - def __or__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... - @overload - def __or__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... - @overload - def __ror__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ... - @overload - def __ror__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ... - # dict.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, __value: _typeshed.SupportsKeysAndGetItem[KT, VT]) -> Self: ... - @overload - def __ior__(self, __value: Iterable[Tuple[KT, VT]]) -> Self: ... - class int: # for convenience def __add__(self, x: Union[int, complex]) -> int: pass def __radd__(self, x: int) -> int: pass def __sub__(self, x: Union[int, complex]) -> int: pass - def __neg__(self) -> int: pass - real: int - imag: int class str: pass # for keyword argument key type class bytes: pass @@ -74,10 +57,8 @@ class function: pass class float: pass class complex: pass class bool(int): pass - -class ellipsis: - __class__: object -def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass +class ellipsis: pass class BaseException: pass +def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass def iter(__iterable: Iterable[T]) -> Iterator[T]: pass diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index e270f3d79d3e..cb6347e9f2fd 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -49,8 +49,6 @@ class list(Sequence[T], Generic[T]): def isinstance(x: object, t: type) -> bool: pass -def sum(iterable: Iterable[T], start: Optional[T] = None) -> T: pass - class BaseException: pass class dict: pass From 1200d1d956e589a0a33c86ef8a7cb3f5a9b64f1f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 25 Nov 2023 22:32:20 +0000 Subject: [PATCH 0410/1617] Add fast path to analyzing special form assignments (#16561) This showed up as hot spot in a CPU profile collected when running tests. This makes `mypy/test/testcheck.py` about 2% faster on my Linux desktop. --- mypy/semanal.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3e3056a9adf7..4128369ace5d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2855,22 +2855,23 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if self.check_and_set_up_type_alias(s): s.is_alias_def = True special_form = True - # * type variable definition - elif self.process_typevar_declaration(s): - special_form = True - elif self.process_paramspec_declaration(s): - special_form = True - elif self.process_typevartuple_declaration(s): - special_form = True - # * type constructors - elif self.analyze_namedtuple_assign(s): - special_form = True - elif self.analyze_typeddict_assign(s): - special_form = True - elif self.newtype_analyzer.process_newtype_declaration(s): - special_form = True - elif self.analyze_enum_assign(s): - special_form = True + elif isinstance(s.rvalue, CallExpr): + # * type variable definition + if self.process_typevar_declaration(s): + special_form = True + elif self.process_paramspec_declaration(s): + special_form = True + elif self.process_typevartuple_declaration(s): + special_form = True + # * type constructors + elif self.analyze_namedtuple_assign(s): + special_form = True + elif self.analyze_typeddict_assign(s): + special_form = True + elif self.newtype_analyzer.process_newtype_declaration(s): + special_form = True + elif self.analyze_enum_assign(s): + special_form = True if special_form: self.record_special_form_lvalue(s) From e69c5cde8643e04a54a644cc27814ab98181541d Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 27 Nov 2023 03:27:08 +0200 Subject: [PATCH 0411/1617] stubgen: Preserve simple defaults in function signatures (#15355) Fixes #13238 See also https://github.com/python/typeshed/issues/8988 --- mypy/stubdoc.py | 15 +++++-- mypy/stubgen.py | 73 +++++++++++++++++++++++++++++- test-data/unit/stubgen.test | 89 ++++++++++++++++++++++++++++--------- 3 files changed, 153 insertions(+), 24 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index c277573f0b59..126ac44e142e 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -36,11 +36,19 @@ def is_valid_type(s: str) -> bool: class ArgSig: """Signature info for a single argument.""" - def __init__(self, name: str, type: str | None = None, default: bool = False): + def __init__( + self, + name: str, + type: str | None = None, + *, + default: bool = False, + default_value: str = "...", + ) -> None: self.name = name self.type = type # Does this argument have a default value? self.default = default + self.default_value = default_value def is_star_arg(self) -> bool: return self.name.startswith("*") and not self.name.startswith("**") @@ -59,6 +67,7 @@ def __eq__(self, other: Any) -> bool: self.name == other.name and self.type == other.type and self.default == other.default + and self.default_value == other.default_value ) return False @@ -119,10 +128,10 @@ def format_sig( if arg_type: arg_def += ": " + arg_type if arg.default: - arg_def += " = ..." + arg_def += f" = {arg.default_value}" elif arg.default: - arg_def += "=..." + arg_def += f"={arg.default_value}" args.append(arg_def) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 837cd723c410..fff6ab058459 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -99,6 +99,7 @@ NameExpr, OpExpr, OverloadedFuncDef, + SetExpr, Statement, StrExpr, TempNode, @@ -491,15 +492,21 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: if kind.is_named() and not any(arg.name.startswith("*") for arg in args): args.append(ArgSig("*")) + default = "..." if arg_.initializer: if not typename: typename = self.get_str_type_of_node(arg_.initializer, True, False) + potential_default, valid = self.get_str_default_of_node(arg_.initializer) + if valid and len(potential_default) <= 200: + default = potential_default elif kind == ARG_STAR: name = f"*{name}" elif kind == ARG_STAR2: name = f"**{name}" - args.append(ArgSig(name, typename, default=bool(arg_.initializer))) + args.append( + ArgSig(name, typename, default=bool(arg_.initializer), default_value=default) + ) if ctx.class_info is not None and all( arg.type is None and arg.default is False for arg in args @@ -1234,6 +1241,70 @@ def maybe_unwrap_unary_expr(self, expr: Expression) -> Expression: # This is some other unary expr, we cannot do anything with it (yet?). return expr + def get_str_default_of_node(self, rvalue: Expression) -> tuple[str, bool]: + """Get a string representation of the default value of a node. + + Returns a 2-tuple of the default and whether or not it is valid. + """ + if isinstance(rvalue, NameExpr): + if rvalue.name in ("None", "True", "False"): + return rvalue.name, True + elif isinstance(rvalue, (IntExpr, FloatExpr)): + return f"{rvalue.value}", True + elif isinstance(rvalue, UnaryExpr): + if isinstance(rvalue.expr, (IntExpr, FloatExpr)): + return f"{rvalue.op}{rvalue.expr.value}", True + elif isinstance(rvalue, StrExpr): + return repr(rvalue.value), True + elif isinstance(rvalue, BytesExpr): + return "b" + repr(rvalue.value).replace("\\\\", "\\"), True + elif isinstance(rvalue, TupleExpr): + items_defaults = [] + for e in rvalue.items: + e_default, valid = self.get_str_default_of_node(e) + if not valid: + break + items_defaults.append(e_default) + else: + closing = ",)" if len(items_defaults) == 1 else ")" + default = "(" + ", ".join(items_defaults) + closing + return default, True + elif isinstance(rvalue, ListExpr): + items_defaults = [] + for e in rvalue.items: + e_default, valid = self.get_str_default_of_node(e) + if not valid: + break + items_defaults.append(e_default) + else: + default = "[" + ", ".join(items_defaults) + "]" + return default, True + elif isinstance(rvalue, SetExpr): + items_defaults = [] + for e in rvalue.items: + e_default, valid = self.get_str_default_of_node(e) + if not valid: + break + items_defaults.append(e_default) + else: + if items_defaults: + default = "{" + ", ".join(items_defaults) + "}" + return default, True + elif isinstance(rvalue, DictExpr): + items_defaults = [] + for k, v in rvalue.items: + if k is None: + break + k_default, k_valid = self.get_str_default_of_node(k) + v_default, v_valid = self.get_str_default_of_node(v) + if not (k_valid and v_valid): + break + items_defaults.append(f"{k_default}: {v_default}") + else: + default = "{" + ", ".join(items_defaults) + "}" + return default, True + return "...", False + def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> bool: is_private = self.is_private_name(name, full_module + "." + name) if ( diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 2a43ce16383d..cd38242ce031 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -27,20 +27,20 @@ def g(arg) -> None: ... def f(a, b=2): ... def g(b=-1, c=0): ... [out] -def f(a, b: int = ...) -> None: ... -def g(b: int = ..., c: int = ...) -> None: ... +def f(a, b: int = 2) -> None: ... +def g(b: int = -1, c: int = 0) -> None: ... [case testDefaultArgNone] def f(x=None): ... [out] from _typeshed import Incomplete -def f(x: Incomplete | None = ...) -> None: ... +def f(x: Incomplete | None = None) -> None: ... [case testDefaultArgBool] def f(x=True, y=False): ... [out] -def f(x: bool = ..., y: bool = ...) -> None: ... +def f(x: bool = True, y: bool = False) -> None: ... [case testDefaultArgBool_inspect] def f(x=True, y=False): ... @@ -48,9 +48,9 @@ def f(x=True, y=False): ... def f(x: bool = ..., y: bool = ...): ... [case testDefaultArgStr] -def f(x='foo'): ... +def f(x='foo',y="how's quotes"): ... [out] -def f(x: str = ...) -> None: ... +def f(x: str = 'foo', y: str = "how's quotes") -> None: ... [case testDefaultArgStr_inspect] def f(x='foo'): ... @@ -58,14 +58,16 @@ def f(x='foo'): ... def f(x: str = ...): ... [case testDefaultArgBytes] -def f(x=b'foo'): ... +def f(x=b'foo',y=b"what's up",z=b'\xc3\xa0 la une'): ... [out] -def f(x: bytes = ...) -> None: ... +def f(x: bytes = b'foo', y: bytes = b"what's up", z: bytes = b'\xc3\xa0 la une') -> None: ... [case testDefaultArgFloat] -def f(x=1.2): ... +def f(x=1.2,y=1e-6,z=0.0,w=-0.0,v=+1.0): ... +def g(x=float("nan"), y=float("inf"), z=float("-inf")): ... [out] -def f(x: float = ...) -> None: ... +def f(x: float = 1.2, y: float = 1e-06, z: float = 0.0, w: float = -0.0, v: float = +1.0) -> None: ... +def g(x=..., y=..., z=...) -> None: ... [case testDefaultArgOther] def f(x=ord): ... @@ -126,10 +128,10 @@ def i(a, *, b=1): ... def j(a, *, b=1, **c): ... [out] def f(a, *b, **c) -> None: ... -def g(a, *b, c: int = ...) -> None: ... -def h(a, *b, c: int = ..., **d) -> None: ... -def i(a, *, b: int = ...) -> None: ... -def j(a, *, b: int = ..., **c) -> None: ... +def g(a, *b, c: int = 1) -> None: ... +def h(a, *b, c: int = 1, **d) -> None: ... +def i(a, *, b: int = 1) -> None: ... +def j(a, *, b: int = 1, **c) -> None: ... [case testClass] class A: @@ -356,8 +358,8 @@ y: Incomplete def f(x, *, y=1): ... def g(x, *, y=1, z=2): ... [out] -def f(x, *, y: int = ...) -> None: ... -def g(x, *, y: int = ..., z: int = ...) -> None: ... +def f(x, *, y: int = 1) -> None: ... +def g(x, *, y: int = 1, z: int = 2) -> None: ... [case testProperty] class A: @@ -1285,8 +1287,8 @@ from _typeshed import Incomplete class A: x: Incomplete - def __init__(self, a: Incomplete | None = ...) -> None: ... - def method(self, a: Incomplete | None = ...) -> None: ... + def __init__(self, a: Incomplete | None = None) -> None: ... + def method(self, a: Incomplete | None = None) -> None: ... [case testAnnotationImportsFrom] import foo @@ -2514,7 +2516,7 @@ from _typeshed import Incomplete as _Incomplete Y: _Incomplete -def g(x: _Incomplete | None = ...) -> None: ... +def g(x: _Incomplete | None = None) -> None: ... x: _Incomplete @@ -3503,7 +3505,7 @@ class P(Protocol): [case testNonDefaultKeywordOnlyArgAfterAsterisk] def func(*, non_default_kwarg: bool, default_kwarg: bool = True): ... [out] -def func(*, non_default_kwarg: bool, default_kwarg: bool = ...): ... +def func(*, non_default_kwarg: bool, default_kwarg: bool = True): ... [case testNestedGenerator] def f1(): @@ -3909,6 +3911,53 @@ def gen2() -> _Generator[_Incomplete, _Incomplete, _Incomplete]: ... class X(_Incomplete): ... class Y(_Incomplete): ... +[case testIgnoreLongDefaults] +def f(x='abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'): ... + +def g(x=b'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'): ... + +def h(x=123456789012345678901234567890123456789012345678901234567890\ +123456789012345678901234567890123456789012345678901234567890\ +123456789012345678901234567890123456789012345678901234567890\ +123456789012345678901234567890123456789012345678901234567890): ... + +[out] +def f(x: str = ...) -> None: ... +def g(x: bytes = ...) -> None: ... +def h(x: int = ...) -> None: ... + +[case testDefaultsOfBuiltinContainers] +def f(x=(), y=(1,), z=(1, 2)): ... +def g(x=[], y=[1, 2]): ... +def h(x={}, y={1: 2, 3: 4}): ... +def i(x={1, 2, 3}): ... +def j(x=[(1,"a"), (2,"b")]): ... + +[out] +def f(x=(), y=(1,), z=(1, 2)) -> None: ... +def g(x=[], y=[1, 2]) -> None: ... +def h(x={}, y={1: 2, 3: 4}) -> None: ... +def i(x={1, 2, 3}) -> None: ... +def j(x=[(1, 'a'), (2, 'b')]) -> None: ... + +[case testDefaultsOfBuiltinContainersWithNonTrivialContent] +def f(x=(1, u.v), y=(k(),), z=(w,)): ... +def g(x=[1, u.v], y=[k()], z=[w]): ... +def h(x={1: u.v}, y={k(): 2}, z={m: m}, w={**n}): ... +def i(x={u.v, 2}, y={3, k()}, z={w}): ... + +[out] +def f(x=..., y=..., z=...) -> None: ... +def g(x=..., y=..., z=...) -> None: ... +def h(x=..., y=..., z=..., w=...) -> None: ... +def i(x=..., y=..., z=...) -> None: ... + [case testDataclass] import dataclasses import dataclasses as dcs From 379d59e1a7e121d5b7f75aed26944620d4ccff37 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 27 Nov 2023 16:41:36 +0000 Subject: [PATCH 0412/1617] Fix multiprocessing warnings when runnign tests on Python 3.12 (#16564) I saw a bunch of warnings when running tests in parallel using pytest. When running tests sequentially using `-n0` I didn't see warnings. This only seems to happen on Linux. The warnings were like these, which can be fixed by avoiding the use of fork, and using forkserver instead: ``` mypy/test/teststubgen.py::StubgenPythonSuite::stubgen.test::testNestedClass_inspect /usr/local/lib/python3.12/multiprocessing/popen_fork.py:66: DeprecationWarning: This process (pid=84587) is multi-threaded, use of fork() may lead to deadlocks in the child. self.pid = os.fork() ``` Relevant discussion: https://discuss.python.org/t/concerns-regarding-deprecation-of-fork-with-alive-threads/33555 --- mypy/moduleinspect.py | 12 ++++++++---- mypy/test/testipc.py | 21 ++++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/mypy/moduleinspect.py b/mypy/moduleinspect.py index 580b31fb4107..35db2132f66c 100644 --- a/mypy/moduleinspect.py +++ b/mypy/moduleinspect.py @@ -8,7 +8,7 @@ import pkgutil import queue import sys -from multiprocessing import Process, Queue +from multiprocessing import Queue, get_context from types import ModuleType @@ -123,9 +123,13 @@ def __init__(self) -> None: self._start() def _start(self) -> None: - self.tasks: Queue[str] = Queue() - self.results: Queue[ModuleProperties | str] = Queue() - self.proc = Process(target=worker, args=(self.tasks, self.results, sys.path)) + if sys.platform == "linux": + ctx = get_context("forkserver") + else: + ctx = get_context("spawn") + self.tasks: Queue[str] = ctx.Queue() + self.results: Queue[ModuleProperties | str] = ctx.Queue() + self.proc = ctx.Process(target=worker, args=(self.tasks, self.results, sys.path)) self.proc.start() self.counter = 0 # Number of successful roundtrips diff --git a/mypy/test/testipc.py b/mypy/test/testipc.py index 8ef656dc4579..0224035a7b61 100644 --- a/mypy/test/testipc.py +++ b/mypy/test/testipc.py @@ -2,7 +2,7 @@ import sys import time -from multiprocessing import Process, Queue +from multiprocessing import Queue, get_context from unittest import TestCase, main import pytest @@ -35,10 +35,17 @@ def server_multi_message_echo(q: Queue[str]) -> None: class IPCTests(TestCase): + def setUp(self) -> None: + if sys.platform == "linux": + # The default "fork" start method is potentially unsafe + self.ctx = get_context("forkserver") + else: + self.ctx = get_context("spawn") + def test_transaction_large(self) -> None: - queue: Queue[str] = Queue() + queue: Queue[str] = self.ctx.Queue() msg = "t" * 200000 # longer than the max read size of 100_000 - p = Process(target=server, args=(msg, queue), daemon=True) + p = self.ctx.Process(target=server, args=(msg, queue), daemon=True) p.start() connection_name = queue.get() with IPCClient(connection_name, timeout=1) as client: @@ -49,9 +56,9 @@ def test_transaction_large(self) -> None: p.join() def test_connect_twice(self) -> None: - queue: Queue[str] = Queue() + queue: Queue[str] = self.ctx.Queue() msg = "this is a test message" - p = Process(target=server, args=(msg, queue), daemon=True) + p = self.ctx.Process(target=server, args=(msg, queue), daemon=True) p.start() connection_name = queue.get() with IPCClient(connection_name, timeout=1) as client: @@ -67,8 +74,8 @@ def test_connect_twice(self) -> None: assert p.exitcode == 0 def test_multiple_messages(self) -> None: - queue: Queue[str] = Queue() - p = Process(target=server_multi_message_echo, args=(queue,), daemon=True) + queue: Queue[str] = self.ctx.Queue() + p = self.ctx.Process(target=server_multi_message_echo, args=(queue,), daemon=True) p.start() connection_name = queue.get() with IPCClient(connection_name, timeout=1) as client: From 69b31445280d7c495fa0268b24fc558bcbe74505 Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Wed, 29 Nov 2023 00:14:38 -0500 Subject: [PATCH 0413/1617] Fix stubgen regressions with pybind11 and mypy 1.7 (#16504) This addresses several regressions identified in https://github.com/python/mypy/issues/16486 The primary regression from https://github.com/python/mypy/pull/15770 is that pybind11 properties with docstrings were erroneously assigned `typeshed. Incomplete`. The reason for the regression is that as of the introduction of the `--include-docstring` feature (https://github.com/python/mypy/pull/13284, not my PR, ftr), `./misc/test-stubgenc.sh` began always reporting success. That has been fixed. It was also pointed out that `--include-docstring` does not work for C-extensions. This was not actually a regression as it turns out this feature was never implemented for C-extensions (though the tests suggested it had been), but luckily my efforts to unify the pure-python and C-extension code-paths made fixing this super easy (barely an inconvenience)! So that is working now. I added back the extended list of `typing` objects that generate implicit imports for the inspection-based stub generator. I originally removed these because I encountered an issue generating stubs for `PySide2` (and another internal library) where there was an object with the same name as one of the `typing` objects and the auto-import created broken stubs. I felt somewhat justified in this decision as there was a straightforward solution -- e.g. use `list` or `typing.List` instead of `List`. That said, I recognize that the problem that I encountered is more niche than the general desire to add import statements for typing objects, so I've changed the behavior back for now, with the intention to eventually add a flag to control this behavior. --- misc/test-stubgenc.sh | 2 +- mypy/stubdoc.py | 3 +- mypy/stubgen.py | 1 + mypy/stubgenc.py | 52 +++++++++++++++++-- mypy/stubutil.py | 18 +++---- test-data/pybind11_mypy_demo/src/main.cpp | 11 +++- .../pybind11_mypy_demo/__init__.pyi | 1 + .../pybind11_mypy_demo/basics.pyi | 32 ++++++------ .../stubgen/pybind11_mypy_demo/basics.pyi | 3 +- 9 files changed, 88 insertions(+), 35 deletions(-) diff --git a/misc/test-stubgenc.sh b/misc/test-stubgenc.sh index 7713e1b04e43..5cb5140eba76 100755 --- a/misc/test-stubgenc.sh +++ b/misc/test-stubgenc.sh @@ -24,7 +24,7 @@ function stubgenc_test() { # Compare generated stubs to expected ones if ! git diff --exit-code "$STUBGEN_OUTPUT_FOLDER"; then - EXIT=$? + EXIT=1 fi } diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 126ac44e142e..86ff6e2bb540 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -383,7 +383,8 @@ def infer_ret_type_sig_from_docstring(docstr: str, name: str) -> str | None: def infer_ret_type_sig_from_anon_docstring(docstr: str) -> str | None: """Convert signature in form of "(self: TestClass, arg0) -> int" to their return type.""" - return infer_ret_type_sig_from_docstring("stub" + docstr.strip(), "stub") + lines = ["stub" + line.strip() for line in docstr.splitlines() if line.strip().startswith("(")] + return infer_ret_type_sig_from_docstring("".join(lines), "stub") def parse_signature(sig: str) -> tuple[str, list[str], list[str]] | None: diff --git a/mypy/stubgen.py b/mypy/stubgen.py index fff6ab058459..23b5fde9dff2 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1700,6 +1700,7 @@ def generate_stubs(options: Options) -> None: doc_dir=options.doc_dir, include_private=options.include_private, export_less=options.export_less, + include_docstrings=options.include_docstrings, ) num_modules = len(all_modules) if not options.quiet and num_modules > 0: diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 0ad79a4265b3..39288197f477 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -126,10 +126,12 @@ def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> s """Infer property type from docstring or docstring signature.""" if ctx.docstring is not None: inferred = infer_ret_type_sig_from_anon_docstring(ctx.docstring) - if not inferred: - inferred = infer_ret_type_sig_from_docstring(ctx.docstring, ctx.name) - if not inferred: - inferred = infer_prop_type_from_docstring(ctx.docstring) + if inferred: + return inferred + inferred = infer_ret_type_sig_from_docstring(ctx.docstring, ctx.name) + if inferred: + return inferred + inferred = infer_prop_type_from_docstring(ctx.docstring) return inferred else: return None @@ -237,6 +239,26 @@ def __init__( self.resort_members = self.is_c_module super().__init__(_all_, include_private, export_less, include_docstrings) self.module_name = module_name + if self.is_c_module: + # Add additional implicit imports. + # C-extensions are given more lattitude since they do not import the typing module. + self.known_imports.update( + { + "typing": [ + "Any", + "Callable", + "ClassVar", + "Dict", + "Iterable", + "Iterator", + "List", + "NamedTuple", + "Optional", + "Tuple", + "Union", + ] + } + ) def get_default_function_sig(self, func: object, ctx: FunctionContext) -> FunctionSig: argspec = None @@ -590,9 +612,29 @@ def generate_function_stub( if inferred[0].args and inferred[0].args[0].name == "cls": decorators.append("@classmethod") + if docstring: + docstring = self._indent_docstring(docstring) output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring)) self._fix_iter(ctx, inferred, output) + def _indent_docstring(self, docstring: str) -> str: + """Fix indentation of docstring extracted from pybind11 or other binding generators.""" + lines = docstring.splitlines(keepends=True) + indent = self._indent + " " + if len(lines) > 1: + if not all(line.startswith(indent) or not line.strip() for line in lines): + # if the docstring is not indented, then indent all but the first line + for i, line in enumerate(lines[1:]): + if line.strip(): + lines[i + 1] = indent + line + # if there's a trailing newline, add a final line to visually indent the quoted docstring + if lines[-1].endswith("\n"): + if len(lines) > 1: + lines.append(indent) + else: + lines[-1] = lines[-1][:-1] + return "".join(lines) + def _fix_iter( self, ctx: FunctionContext, inferred: list[FunctionSig], output: list[str] ) -> None: @@ -640,7 +682,7 @@ def generate_property_stub( if fget: alt_docstr = getattr(fget, "__doc__", None) if alt_docstr and docstring: - docstring += alt_docstr + docstring += "\n" + alt_docstr elif alt_docstr: docstring = alt_docstr diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 5ec240087145..b8d601ed3c6b 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -576,6 +576,14 @@ def __init__( self.sig_generators = self.get_sig_generators() # populated by visit_mypy_file self.module_name: str = "" + # These are "soft" imports for objects which might appear in annotations but not have + # a corresponding import statement. + self.known_imports = { + "_typeshed": ["Incomplete"], + "typing": ["Any", "TypeVar", "NamedTuple"], + "collections.abc": ["Generator"], + "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], + } def get_sig_generators(self) -> list[SignatureGenerator]: return [] @@ -667,15 +675,7 @@ def set_defined_names(self, defined_names: set[str]) -> None: for name in self._all_ or (): self.import_tracker.reexport(name) - # These are "soft" imports for objects which might appear in annotations but not have - # a corresponding import statement. - known_imports = { - "_typeshed": ["Incomplete"], - "typing": ["Any", "TypeVar", "NamedTuple"], - "collections.abc": ["Generator"], - "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], - } - for pkg, imports in known_imports.items(): + for pkg, imports in self.known_imports.items(): for t in imports: # require=False means that the import won't be added unless require_name() is called # for the object during generation. diff --git a/test-data/pybind11_mypy_demo/src/main.cpp b/test-data/pybind11_mypy_demo/src/main.cpp index 00e5b2f4e871..192a90cf8e30 100644 --- a/test-data/pybind11_mypy_demo/src/main.cpp +++ b/test-data/pybind11_mypy_demo/src/main.cpp @@ -44,6 +44,7 @@ #include #include +#include namespace py = pybind11; @@ -102,6 +103,11 @@ struct Point { return distance_to(other.x, other.y); } + std::vector as_vector() + { + return std::vector{x, y}; + } + double x, y; }; @@ -134,14 +140,15 @@ void bind_basics(py::module& basics) { .def(py::init(), py::arg("x"), py::arg("y")) .def("distance_to", py::overload_cast(&Point::distance_to, py::const_), py::arg("x"), py::arg("y")) .def("distance_to", py::overload_cast(&Point::distance_to, py::const_), py::arg("other")) - .def_readwrite("x", &Point::x) + .def("as_list", &Point::as_vector) + .def_readwrite("x", &Point::x, "some docstring") .def_property("y", [](Point& self){ return self.y; }, [](Point& self, double value){ self.y = value; } ) .def_property_readonly("length", &Point::length) .def_property_readonly_static("x_axis", [](py::object cls){return Point::x_axis;}) - .def_property_readonly_static("y_axis", [](py::object cls){return Point::y_axis;}) + .def_property_readonly_static("y_axis", [](py::object cls){return Point::y_axis;}, "another docstring") .def_readwrite_static("length_unit", &Point::length_unit) .def_property_static("angle_unit", [](py::object& /*cls*/){ return Point::angle_unit; }, diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi index e69de29bb2d1..0cb252f00259 100644 --- a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi +++ b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi @@ -0,0 +1 @@ +from . import basics as basics diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi index 676d7f6d3f15..b761291e11f3 100644 --- a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi @@ -1,7 +1,7 @@ -from typing import ClassVar +from typing import ClassVar, List, overload -from typing import overload PI: float +__version__: str class Point: class AngleUnit: @@ -13,8 +13,6 @@ class Point: """__init__(self: pybind11_mypy_demo.basics.Point.AngleUnit, value: int) -> None""" def __eq__(self, other: object) -> bool: """__eq__(self: object, other: object) -> bool""" - def __getstate__(self) -> int: - """__getstate__(self: object) -> int""" def __hash__(self) -> int: """__hash__(self: object) -> int""" def __index__(self) -> int: @@ -23,8 +21,6 @@ class Point: """__int__(self: pybind11_mypy_demo.basics.Point.AngleUnit) -> int""" def __ne__(self, other: object) -> bool: """__ne__(self: object, other: object) -> bool""" - def __setstate__(self, state: int) -> None: - """__setstate__(self: pybind11_mypy_demo.basics.Point.AngleUnit, state: int) -> None""" @property def name(self) -> str: ... @property @@ -40,8 +36,6 @@ class Point: """__init__(self: pybind11_mypy_demo.basics.Point.LengthUnit, value: int) -> None""" def __eq__(self, other: object) -> bool: """__eq__(self: object, other: object) -> bool""" - def __getstate__(self) -> int: - """__getstate__(self: object) -> int""" def __hash__(self) -> int: """__hash__(self: object) -> int""" def __index__(self) -> int: @@ -50,8 +44,6 @@ class Point: """__int__(self: pybind11_mypy_demo.basics.Point.LengthUnit) -> int""" def __ne__(self, other: object) -> bool: """__ne__(self: object, other: object) -> bool""" - def __setstate__(self, state: int) -> None: - """__setstate__(self: pybind11_mypy_demo.basics.Point.LengthUnit, state: int) -> None""" @property def name(self) -> str: ... @property @@ -70,7 +62,8 @@ class Point: 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None - 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None""" + 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None + """ @overload def __init__(self, x: float, y: float) -> None: """__init__(*args, **kwargs) @@ -78,7 +71,10 @@ class Point: 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None - 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None""" + 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None + """ + def as_list(self) -> List[float]: + """as_list(self: pybind11_mypy_demo.basics.Point) -> List[float]""" @overload def distance_to(self, x: float, y: float) -> float: """distance_to(*args, **kwargs) @@ -86,7 +82,8 @@ class Point: 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float - 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float""" + 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float + """ @overload def distance_to(self, other: Point) -> float: """distance_to(*args, **kwargs) @@ -94,19 +91,22 @@ class Point: 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float - 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float""" + 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float + """ @property def length(self) -> float: ... def answer() -> int: '''answer() -> int - answer docstring, with end quote"''' + answer docstring, with end quote" + ''' def midpoint(left: float, right: float) -> float: """midpoint(left: float, right: float) -> float""" def sum(arg0: int, arg1: int) -> int: '''sum(arg0: int, arg1: int) -> int - multiline docstring test, edge case quotes """\'\'\'''' + multiline docstring test, edge case quotes """\'\'\' + ''' def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: """weighted_midpoint(left: float, right: float, alpha: float = 0.5) -> float""" diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi index 6527f5733eaf..6f164a03edcc 100644 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi @@ -1,4 +1,4 @@ -from typing import ClassVar, overload +from typing import ClassVar, List, overload PI: float __version__: str @@ -47,6 +47,7 @@ class Point: def __init__(self) -> None: ... @overload def __init__(self, x: float, y: float) -> None: ... + def as_list(self) -> List[float]: ... @overload def distance_to(self, x: float, y: float) -> float: ... @overload From 95e7fcbe9a4c1d366e90c23f44459032cf0740de Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 30 Nov 2023 17:46:42 +0000 Subject: [PATCH 0414/1617] Fix stubtest's tests to work with the latest typing_extensions release (#16588) Stubtest's tests will start failing when `typing_extensions==4.9.0` comes out, due to some new `ClassVar`s on `typing_extensions.TypedDict`. This PR fixes that. Fixes https://github.com/python/typing_extensions/issues/309 --- test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index c88aa5c815c5..7aca6fad1b42 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -59,6 +59,8 @@ class _TypedDict(Mapping[str, object]): # Stubtest's tests need the following items: __required_keys__: frozenset[str] __optional_keys__: frozenset[str] + __readonly_keys__: frozenset[str] + __mutable_keys__: frozenset[str] __total__: bool def TypedDict(typename: str, fields: Dict[str, Type[_T]], *, total: Any = ...) -> Type[dict]: ... From c0fce06da362e9660f4aec3b58fb72a9a2dfb7a6 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Fri, 1 Dec 2023 15:47:57 +0000 Subject: [PATCH 0415/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/5f12eebda4bfddb247c05fb06c6762bd262a9420 --- mypy/typeshed/stdlib/_codecs.pyi | 4 +- mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/argparse.pyi | 10 +- mypy/typeshed/stdlib/asyncio/base_events.pyi | 2 + .../stdlib/asyncio/base_subprocess.pyi | 1 + mypy/typeshed/stdlib/asyncio/events.pyi | 15 ++- mypy/typeshed/stdlib/asyncio/sslproto.pyi | 2 + mypy/typeshed/stdlib/asyncio/streams.pyi | 3 + mypy/typeshed/stdlib/asyncio/subprocess.pyi | 18 +-- mypy/typeshed/stdlib/asyncio/tasks.pyi | 67 +++++----- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 116 +++++++++++++----- mypy/typeshed/stdlib/builtins.pyi | 105 +++++++++++++++- mypy/typeshed/stdlib/cgi.pyi | 3 +- mypy/typeshed/stdlib/collections/__init__.pyi | 4 +- mypy/typeshed/stdlib/contextvars.pyi | 2 +- mypy/typeshed/stdlib/functools.pyi | 44 ++++--- mypy/typeshed/stdlib/http/client.pyi | 2 +- mypy/typeshed/stdlib/importlib/abc.pyi | 2 +- mypy/typeshed/stdlib/importlib/machinery.pyi | 23 +++- .../stdlib/importlib/metadata/__init__.pyi | 2 +- mypy/typeshed/stdlib/io.pyi | 2 +- mypy/typeshed/stdlib/itertools.pyi | 26 ++-- mypy/typeshed/stdlib/logging/config.pyi | 10 +- mypy/typeshed/stdlib/math.pyi | 2 +- .../stdlib/multiprocessing/connection.pyi | 1 + .../stdlib/multiprocessing/managers.pyi | 1 + mypy/typeshed/stdlib/multiprocessing/pool.pyi | 1 + .../stdlib/multiprocessing/shared_memory.pyi | 1 + mypy/typeshed/stdlib/os/__init__.pyi | 2 +- mypy/typeshed/stdlib/pkgutil.pyi | 3 + mypy/typeshed/stdlib/re.pyi | 14 +-- mypy/typeshed/stdlib/shelve.pyi | 3 +- mypy/typeshed/stdlib/shutil.pyi | 8 +- mypy/typeshed/stdlib/subprocess.pyi | 1 + mypy/typeshed/stdlib/sunau.pyi | 2 + mypy/typeshed/stdlib/telnetlib.pyi | 1 + mypy/typeshed/stdlib/tempfile.pyi | 4 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 1 + mypy/typeshed/stdlib/tkinter/dnd.pyi | 1 + mypy/typeshed/stdlib/tkinter/font.pyi | 1 + mypy/typeshed/stdlib/tkinter/ttk.pyi | 4 +- mypy/typeshed/stdlib/turtle.pyi | 12 +- mypy/typeshed/stdlib/types.pyi | 3 +- mypy/typeshed/stdlib/typing.pyi | 12 +- mypy/typeshed/stdlib/typing_extensions.pyi | 30 +++-- mypy/typeshed/stdlib/unittest/async_case.pyi | 2 + mypy/typeshed/stdlib/unittest/mock.pyi | 4 +- mypy/typeshed/stdlib/urllib/request.pyi | 1 + mypy/typeshed/stdlib/wave.pyi | 9 +- mypy/typeshed/stdlib/weakref.pyi | 2 +- mypy/typeshed/stdlib/xml/sax/xmlreader.pyi | 2 +- mypy/typeshed/stdlib/zipfile.pyi | 8 +- 52 files changed, 436 insertions(+), 169 deletions(-) diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index 51f17f01ca71..f8141d8bad4b 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -47,11 +47,11 @@ _StrToStrEncoding: TypeAlias = Literal["rot13", "rot_13"] @overload def encode(obj: ReadableBuffer, encoding: _BytesToBytesEncoding, errors: str = "strict") -> bytes: ... @overload -def encode(obj: str, encoding: _StrToStrEncoding, errors: str = "strict") -> str: ... # type: ignore[misc] +def encode(obj: str, encoding: _StrToStrEncoding, errors: str = "strict") -> str: ... # type: ignore[overload-overlap] @overload def encode(obj: str, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @overload -def decode(obj: ReadableBuffer, encoding: _BytesToBytesEncoding, errors: str = "strict") -> bytes: ... # type: ignore[misc] +def decode(obj: ReadableBuffer, encoding: _BytesToBytesEncoding, errors: str = "strict") -> bytes: ... # type: ignore[overload-overlap] @overload def decode(obj: str, encoding: _StrToStrEncoding, errors: str = "strict") -> str: ... diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 8a891971e9f1..495e29dfd8ce 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -167,7 +167,11 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index e947f67edd55..0cbbcd242195 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -172,7 +172,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ) -> None: ... @overload - def parse_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... # type: ignore[misc] + def parse_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... @overload def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... @overload @@ -211,7 +211,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def format_usage(self) -> str: ... def format_help(self) -> str: ... @overload - def parse_known_args(self, args: Sequence[str] | None = None, namespace: None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + def parse_known_args(self, args: Sequence[str] | None = None, namespace: None = None) -> tuple[Namespace, list[str]]: ... @overload def parse_known_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... @overload @@ -220,13 +220,15 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def exit(self, status: int = 0, message: str | None = None) -> NoReturn: ... def error(self, message: str) -> NoReturn: ... @overload - def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... # type: ignore[misc] + def parse_intermixed_args(self, args: Sequence[str] | None = None, namespace: None = None) -> Namespace: ... @overload def parse_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ... @overload def parse_intermixed_args(self, *, namespace: _N) -> _N: ... @overload - def parse_known_intermixed_args(self, args: Sequence[str] | None = None, namespace: None = None) -> tuple[Namespace, list[str]]: ... # type: ignore[misc] + def parse_known_intermixed_args( + self, args: Sequence[str] | None = None, namespace: None = None + ) -> tuple[Namespace, list[str]]: ... @overload def parse_known_intermixed_args(self, args: Sequence[str] | None, namespace: _N) -> tuple[_N, list[str]]: ... @overload diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index cdf295d510d4..afddcd918584 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -471,3 +471,5 @@ class BaseEventLoop(AbstractEventLoop): async def shutdown_default_executor(self, timeout: float | None = None) -> None: ... elif sys.version_info >= (3, 9): async def shutdown_default_executor(self) -> None: ... + + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi b/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi index 8f262cd5c760..a5fe24e8768b 100644 --- a/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi @@ -55,6 +55,7 @@ class BaseSubprocessTransport(transports.SubprocessTransport): async def _wait(self) -> int: ... # undocumented def _try_finish(self) -> None: ... # undocumented def _call_connection_lost(self, exc: BaseException | None) -> None: ... # undocumented + def __del__(self) -> None: ... class WriteSubprocessPipeProto(protocols.BaseProtocol): # undocumented def __init__(self, proc: BaseSubprocessTransport, fd: int) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 4c62043875ba..87e7edb461ac 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Coroutine, Generator, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing_extensions import Literal, Self, TypeAlias, deprecated from . import _AwaitableLike, _CoroutineLike from .base_events import Server @@ -613,8 +613,17 @@ def set_event_loop_policy(policy: AbstractEventLoopPolicy | None) -> None: ... def get_event_loop() -> AbstractEventLoop: ... def set_event_loop(loop: AbstractEventLoop | None) -> None: ... def new_event_loop() -> AbstractEventLoop: ... -def get_child_watcher() -> AbstractChildWatcher: ... -def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... + +if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher() -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... + +else: + def get_child_watcher() -> AbstractChildWatcher: ... + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... + def _set_running_loop(__loop: AbstractEventLoop | None) -> None: ... def _get_running_loop() -> AbstractEventLoop: ... def get_running_loop() -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index 09733e5f9a01..393a1fbdc468 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -83,6 +83,8 @@ class _SSLProtocolTransport(transports._FlowControlMixin, transports.Transport): def set_read_buffer_limits(self, high: int | None = None, low: int | None = None) -> None: ... def get_read_buffer_size(self) -> int: ... + def __del__(self) -> None: ... + if sys.version_info >= (3, 11): _SSLProtocolBase: TypeAlias = protocols.BufferedProtocol else: diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 804be1ca5065..81a94425f8de 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -128,6 +128,7 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): client_connected_cb: _ClientConnectedCallback | None = None, loop: events.AbstractEventLoop | None = None, ) -> None: ... + def __del__(self) -> None: ... class StreamWriter: def __init__( @@ -161,6 +162,8 @@ class StreamWriter: async def start_tls( self, sslcontext: ssl.SSLContext, *, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None ) -> None: ... + if sys.version_info >= (3, 11): + def __del__(self) -> None: ... class StreamReader(AsyncIterator[bytes]): def __init__(self, limit: int = 65536, loop: events.AbstractEventLoop | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index 9b7c82e689bf..03aea65f6d54 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -80,14 +80,14 @@ if sys.version_info >= (3, 11): stdout: int | IO[Any] | None = None, stderr: int | IO[Any] | None = None, limit: int = 65536, - # These parameters are forced to these values by BaseEventLoop.subprocess_shell + # These parameters are forced to these values by BaseEventLoop.subprocess_exec universal_newlines: Literal[False] = False, - shell: Literal[True] = True, + shell: Literal[False] = False, bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to - text: bool | None = None, executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, close_fds: bool = True, @@ -145,14 +145,14 @@ elif sys.version_info >= (3, 10): stdout: int | IO[Any] | None = None, stderr: int | IO[Any] | None = None, limit: int = 65536, - # These parameters are forced to these values by BaseEventLoop.subprocess_shell + # These parameters are forced to these values by BaseEventLoop.subprocess_exec universal_newlines: Literal[False] = False, - shell: Literal[True] = True, + shell: Literal[False] = False, bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to - text: bool | None = None, executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, close_fds: bool = True, @@ -210,14 +210,14 @@ else: # >= 3.9 stderr: int | IO[Any] | None = None, loop: events.AbstractEventLoop | None = None, limit: int = 65536, - # These parameters are forced to these values by BaseEventLoop.subprocess_shell + # These parameters are forced to these values by BaseEventLoop.subprocess_exec universal_newlines: Literal[False] = False, - shell: Literal[True] = True, + shell: Literal[False] = False, bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, + text: Literal[False] | None = None, # These parameters are taken by subprocess.Popen, which this ultimately delegates to - text: bool | None = None, executable: StrOrBytesPath | None = None, preexec_fn: Callable[[], Any] | None = None, close_fds: bool = True, diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index cdac7d359781..7c76abaf1dca 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -86,7 +86,7 @@ else: ) -> Iterator[Future[_T]]: ... @overload -def ensure_future(coro_or_future: _FT, *, loop: AbstractEventLoop | None = None) -> _FT: ... # type: ignore[misc] +def ensure_future(coro_or_future: _FT, *, loop: AbstractEventLoop | None = None) -> _FT: ... # type: ignore[overload-overlap] @overload def ensure_future(coro_or_future: Awaitable[_T], *, loop: AbstractEventLoop | None = None) -> Task[_T]: ... @@ -95,17 +95,16 @@ def ensure_future(coro_or_future: Awaitable[_T], *, loop: AbstractEventLoop | No # zip() because typing does not support variadic type variables. See # typing PR #1550 for discussion. # -# The many type: ignores here are because the overloads overlap, -# but having overlapping overloads is the only way to get acceptable type inference in all edge cases. +# N.B. Having overlapping overloads is the only way to get acceptable type inference in all edge cases. if sys.version_info >= (3, 10): @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = False) -> Future[tuple[_T1]]: ... # type: ignore[misc] + def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = False) -> Future[tuple[_T1]]: ... # type: ignore[overload-overlap] @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: Literal[False] = False ) -> Future[tuple[_T1, _T2]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -113,7 +112,7 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -122,7 +121,7 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -132,7 +131,7 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -143,15 +142,15 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload - def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[misc] + def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[overload-overlap] @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[misc] + def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[overload-overlap] @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -159,7 +158,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -168,7 +167,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -180,7 +179,7 @@ if sys.version_info >= (3, 10): tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -204,11 +203,11 @@ if sys.version_info >= (3, 10): else: @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False ) -> Future[tuple[_T1]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, @@ -216,7 +215,7 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -225,7 +224,7 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -235,7 +234,7 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -246,7 +245,7 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -258,15 +257,15 @@ else: return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False ) -> Future[list[_T]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, @@ -274,7 +273,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -283,7 +282,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -293,7 +292,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( # type: ignore[misc] + def gather( # type: ignore[overload-overlap] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -314,7 +313,7 @@ else: ] ]: ... @overload - def gather( # type: ignore[misc] + def gather( *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[list[_T | BaseException]]: ... @@ -338,7 +337,9 @@ else: if sys.version_info >= (3, 11): @overload - async def wait(fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED") -> tuple[set[_FT], set[_FT]]: ... # type: ignore[misc] + async def wait( + fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> tuple[set[_FT], set[_FT]]: ... @overload async def wait( fs: Iterable[Task[_T]], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" @@ -346,7 +347,9 @@ if sys.version_info >= (3, 11): elif sys.version_info >= (3, 10): @overload - async def wait(fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED") -> tuple[set[_FT], set[_FT]]: ... # type: ignore[misc] + async def wait( # type: ignore[overload-overlap] + fs: Iterable[_FT], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> tuple[set[_FT], set[_FT]]: ... @overload async def wait( fs: Iterable[Awaitable[_T]], *, timeout: float | None = None, return_when: str = "ALL_COMPLETED" @@ -354,7 +357,7 @@ elif sys.version_info >= (3, 10): else: @overload - async def wait( # type: ignore[misc] + async def wait( # type: ignore[overload-overlap] fs: Iterable[_FT], *, loop: AbstractEventLoop | None = None, diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index dc3d3496ae55..d440206aa0b9 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -3,7 +3,7 @@ import types from abc import ABCMeta, abstractmethod from collections.abc import Callable from typing import Any -from typing_extensions import Literal, Self +from typing_extensions import Literal, Self, deprecated from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop @@ -11,22 +11,46 @@ from .selector_events import BaseSelectorEventLoop # This is also technically not available on Win, # but other parts of typeshed need this definition. # So, it is special cased. -class AbstractChildWatcher: - @abstractmethod - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... - @abstractmethod - def remove_child_handler(self, pid: int) -> bool: ... - @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def close(self) -> None: ... - @abstractmethod - def __enter__(self) -> Self: ... - @abstractmethod - def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None) -> None: ... - if sys.version_info >= (3, 8): +if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class AbstractChildWatcher: + @abstractmethod + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + @abstractmethod + def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod + def __enter__(self) -> Self: ... + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None + ) -> None: ... + if sys.version_info >= (3, 8): + @abstractmethod + def is_active(self) -> bool: ... + +else: + class AbstractChildWatcher: @abstractmethod - def is_active(self) -> bool: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + @abstractmethod + def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod + def __enter__(self) -> Self: ... + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None + ) -> None: ... + if sys.version_info >= (3, 8): + @abstractmethod + def is_active(self) -> bool: ... if sys.platform != "win32": if sys.version_info >= (3, 9): @@ -62,28 +86,61 @@ if sys.platform != "win32": def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - class SafeChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... - class FastChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + else: + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): - def get_child_watcher(self) -> AbstractChildWatcher: ... - def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + else: + def get_child_watcher(self) -> AbstractChildWatcher: ... + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... SelectorEventLoop = _UnixSelectorEventLoop DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy - if sys.version_info >= (3, 8): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class MultiLoopChildWatcher(AbstractChildWatcher): + def is_active(self) -> bool: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + elif sys.version_info >= (3, 8): class MultiLoopChildWatcher(AbstractChildWatcher): def is_active(self) -> bool: ... def close(self) -> None: ... @@ -95,6 +152,7 @@ if sys.platform != "win32": def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + if sys.version_info >= (3, 8): class ThreadedChildWatcher(AbstractChildWatcher): def is_active(self) -> Literal[True]: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 4f04b6286258..92b5279bcfcd 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -57,12 +57,14 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, + LiteralString, ParamSpec, Self, SupportsIndex, TypeAlias, TypeGuard, TypeVarTuple, + deprecated, final, ) @@ -160,8 +162,9 @@ class classmethod(Generic[_T, _P, _R_co]): def __wrapped__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... class type: + # object.__base__ is None. Otherwise, it would be a type. @property - def __base__(self) -> type: ... + def __base__(self) -> type | None: ... __bases__: tuple[type, ...] @property def __basicsize__(self) -> int: ... @@ -442,8 +445,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -451,11 +463,20 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: + @overload + def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -471,32 +492,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -507,6 +587,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -515,13 +598,25 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload def __mod__(self, __value: Any) -> str: ... + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -844,6 +939,8 @@ class bool(int): @overload def __rxor__(self, __value: int) -> int: ... def __getnewargs__(self) -> tuple[int]: ... + @deprecated("Will throw an error in Python 3.14. Use `not` for logical negation of bools instead.") + def __invert__(self) -> int: ... @final class slice: @@ -1698,11 +1795,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[overload-overlap] else: @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[misc] + def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... @@ -1719,7 +1816,7 @@ else: # (A "SupportsDunderDict" protocol doesn't work) # Use a type: ignore to make complaints about overlapping overloads go away @overload -def vars(__object: type) -> types.MappingProxyType[str, Any]: ... # type: ignore[misc] +def vars(__object: type) -> types.MappingProxyType[str, Any]: ... # type: ignore[overload-overlap] @overload def vars(__object: Any = ...) -> dict[str, Any]: ... diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index a2acfa92d463..21bf8ca25394 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -117,7 +117,8 @@ class FieldStorage: def __contains__(self, key: str) -> bool: ... def __len__(self) -> int: ... def __bool__(self) -> bool: ... - # In Python 3 it returns bytes or str IO depending on an internal flag + def __del__(self) -> None: ... + # Returns bytes or str IO depending on an internal flag def make_file(self) -> IO[Any]: ... def print_exception( diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index bb51dec50cab..955681c6ac0c 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -87,9 +87,9 @@ class UserDict(MutableMapping[_KT, _VT]): def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... @overload def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... - @overload # type: ignore[misc] + @overload def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... - @overload # type: ignore[misc] + @overload def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index a67d0349b46a..825c018d580f 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -50,7 +50,7 @@ def copy_context() -> Context: ... class Context(Mapping[ContextVar[Any], Any]): def __init__(self) -> None: ... @overload - def get(self, __key: ContextVar[_T], __default: None = None) -> _T | None: ... # type: ignore[misc] # overlapping overloads + def get(self, __key: ContextVar[_T], __default: None = None) -> _T | None: ... @overload def get(self, __key: ContextVar[_T], __default: _T) -> _T: ... @overload diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 1b4e59b7c120..451896bed72a 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,15 +28,17 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWrapper = TypeVar("_RWrapper") @overload -def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T) -> _T: ... +def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... @overload -def reduce(function: Callable[[_T, _T], _T], sequence: Iterable[_T]) -> _T: ... +def reduce(__function: Callable[[_T, _T], _T], __sequence: Iterable[_T]) -> _T: ... class _CacheInfo(NamedTuple): hits: int @@ -85,31 +87,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 305568fce6cf..20223695a1a8 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -187,7 +187,7 @@ class HTTPSConnection(HTTPConnection): def __init__( self, host: str, - port: str | None = None, + port: int | None = None, *, timeout: float | None = ..., source_address: tuple[str, int] | None = None, diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 8c395f8a18af..148e12ec7e3f 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -134,7 +134,7 @@ if sys.version_info >= (3, 9): def joinpath(self, __child: str) -> Traversable: ... # The documentation and runtime protocol allows *args, **kwargs arguments, - # but this would mean that all implementors would have to support them, + # but this would mean that all implementers would have to support them, # which is not the case. @overload @abstractmethod diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index 1a9680ab3c46..a0431905a828 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -2,8 +2,9 @@ import importlib.abc import sys import types from _typeshed import ReadableBuffer -from collections.abc import Callable, Iterable, Sequence +from collections.abc import Callable, Iterable, MutableSequence, Sequence from typing import Any +from typing_extensions import Literal, deprecated if sys.version_info >= (3, 8): from importlib.metadata import DistributionFinder, PathDistribution @@ -158,3 +159,23 @@ class ExtensionFileLoader(importlib.abc.ExecutionLoader): def get_code(self, fullname: str) -> None: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... + +if sys.version_info >= (3, 11): + import importlib.readers + + class NamespaceLoader(importlib.abc.InspectLoader): + def __init__( + self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] + ) -> None: ... + def is_package(self, fullname: str) -> Literal[True]: ... + def get_source(self, fullname: str) -> Literal[""]: ... + def get_code(self, fullname: str) -> types.CodeType: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + @deprecated("load_module() is deprecated; use exec_module() instead") + def load_module(self, fullname: str) -> types.ModuleType: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + if sys.version_info < (3, 12): + @staticmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(module: types.ModuleType) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index e52756544e9a..fd470b8f061d 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -207,7 +207,7 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 10): @overload - def entry_points() -> SelectableGroups: ... # type: ignore[misc] + def entry_points() -> SelectableGroups: ... # type: ignore[overload-overlap] @overload def entry_points( *, name: str = ..., value: str = ..., group: str = ..., module: str = ..., attr: str = ..., extras: list[str] = ... diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index b54e0a9fd05b..16270b948f35 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -94,7 +94,7 @@ class BufferedIOBase(IOBase): class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes mode: str - name: FileDescriptorOrPath # type: ignore[assignment] + name: FileDescriptorOrPath def __init__( self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... ) -> None: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 55f9c92d8cac..1bc0b2ec7390 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -10,6 +10,7 @@ _T = TypeVar("_T") _S = TypeVar("_S") _N = TypeVar("_N", int, float, SupportsFloat, SupportsInt, SupportsIndex, SupportsComplex) _T_co = TypeVar("_T_co", covariant=True) +_S_co = TypeVar("_S_co", covariant=True) _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") @@ -84,13 +85,13 @@ class filterfalse(Iterator[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class groupby(Iterator[tuple[_T, Iterator[_S]]], Generic[_T, _S]): +class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): @overload def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... @overload def __new__(cls, iterable: Iterable[_T1], key: Callable[[_T1], _T2]) -> groupby[_T2, _T1]: ... def __iter__(self) -> Self: ... - def __next__(self) -> tuple[_T, Iterator[_S]]: ... + def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... class islice(Iterator[_T]): @overload @@ -100,10 +101,10 @@ class islice(Iterator[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class starmap(Iterator[_T]): - def __init__(self, __function: Callable[..., _T], __iterable: Iterable[Iterable[Any]]) -> None: ... +class starmap(Iterator[_T_co]): + def __new__(cls, __function: Callable[..., _T], __iterable: Iterable[Iterable[Any]]) -> starmap[_T]: ... def __iter__(self) -> Self: ... - def __next__(self) -> _T: ... + def __next__(self) -> _T_co: ... class takewhile(Iterator[_T]): def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... @@ -269,10 +270,19 @@ class combinations(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations_with_replacement(Iterator[tuple[_T, ...]], Generic[_T]): - def __init__(self, iterable: Iterable[_T], r: int) -> None: ... +class combinations_with_replacement(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[3]) -> combinations_with_replacement[tuple[_T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[4]) -> combinations_with_replacement[tuple[_T, _T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[5]) -> combinations_with_replacement[tuple[_T, _T, _T, _T, _T]]: ... + @overload + def __new__(cls, iterable: Iterable[_T], r: int) -> combinations_with_replacement[tuple[_T, ...]]: ... def __iter__(self) -> Self: ... - def __next__(self) -> tuple[_T, ...]: ... + def __next__(self) -> _T_co: ... if sys.version_info >= (3, 10): class pairwise(Iterator[_T_co]): diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index e92658f7f1b3..0a61e5b16870 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -5,7 +5,7 @@ from configparser import RawConfigParser from re import Pattern from threading import Thread from typing import IO, Any, overload -from typing_extensions import Literal, SupportsIndex, TypeAlias, TypedDict +from typing_extensions import Literal, Required, SupportsIndex, TypeAlias, TypedDict from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level @@ -50,18 +50,16 @@ _FilterConfiguration: TypeAlias = _FilterConfigurationTypedDict | dict[str, Any] # Handler config can have additional keys even when not providing a custom factory so we just use `dict`. _HandlerConfiguration: TypeAlias = dict[str, Any] -class _OptionalDictConfigArgs(TypedDict, total=False): +class _DictConfigArgs(TypedDict, total=False): + version: Required[Literal[1]] formatters: dict[str, _FormatterConfiguration] filters: dict[str, _FilterConfiguration] handlers: dict[str, _HandlerConfiguration] loggers: dict[str, _LoggerConfiguration] - root: _RootLoggerConfiguration | None + root: _RootLoggerConfiguration incremental: bool disable_existing_loggers: bool -class _DictConfigArgs(_OptionalDictConfigArgs, TypedDict): - version: Literal[1] - # Accept dict[str, Any] to avoid false positives if called with a dict # type, since dict types are not compatible with TypedDicts. # diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 4a4d592b860d..73b53a713301 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -125,7 +125,7 @@ def pow(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... if sys.version_info >= (3, 8): @overload - def prod(__iterable: Iterable[SupportsIndex], *, start: SupportsIndex = 1) -> int: ... # type: ignore[misc] + def prod(__iterable: Iterable[SupportsIndex], *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] @overload def prod(__iterable: Iterable[_SupportsFloatOrIndex], *, start: _SupportsFloatOrIndex = 1) -> float: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 28696fe6a3a3..333b8820d84d 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -31,6 +31,7 @@ class _ConnectionBase: def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... + def __del__(self) -> None: ... class Connection(_ConnectionBase): ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 9cfc1ebbdd5e..c0ef0a3609d0 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -216,3 +216,4 @@ if sys.version_info >= (3, 8): def get_server(self) -> SharedMemoryServer: ... def SharedMemory(self, size: int) -> _SharedMemory: ... def ShareableList(self, sequence: Iterable[_SLT] | None) -> _ShareableList[_SLT]: ... + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index c52f1c1f5453..5ad4bfe93fe9 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -112,6 +112,7 @@ class Pool: def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... + def __del__(self) -> None: ... class ThreadPool(Pool): def __init__( diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi index ae6e2a0ed19f..adbe8b943de6 100644 --- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi @@ -20,6 +20,7 @@ class SharedMemory: def size(self) -> int: ... def close(self) -> None: ... def unlink(self) -> None: ... + def __del__(self) -> None: ... class ShareableList(Generic[_SLT]): shm: SharedMemory diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 2810d086ae49..45eaf2a66e80 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -248,7 +248,7 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]): unsetenv: Callable[[AnyStr, AnyStr], object], ) -> None: ... - def setdefault(self, key: AnyStr, value: AnyStr) -> AnyStr: ... # type: ignore[override] + def setdefault(self, key: AnyStr, value: AnyStr) -> AnyStr: ... def copy(self) -> dict[AnyStr, AnyStr]: ... def __delitem__(self, key: AnyStr) -> None: ... def __getitem__(self, key: AnyStr) -> AnyStr: ... diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi index 59f1f734cf90..4a0c8d101b7a 100644 --- a/mypy/typeshed/stdlib/pkgutil.pyi +++ b/mypy/typeshed/stdlib/pkgutil.pyi @@ -3,6 +3,7 @@ from _typeshed import SupportsRead from collections.abc import Callable, Iterable, Iterator from importlib.abc import Loader, MetaPathFinder, PathEntryFinder from typing import IO, Any, NamedTuple, TypeVar +from typing_extensions import deprecated __all__ = [ "get_importer", @@ -35,8 +36,10 @@ if sys.version_info < (3, 12): class ImpLoader: def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ... +@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") def find_loader(fullname: str) -> Loader | None: ... def get_importer(path_item: str) -> PathEntryFinder | None: ... +@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") def get_loader(module_or_name: str) -> Loader | None: ... def iter_importers(fullname: str = "") -> Iterator[MetaPathFinder | PathEntryFinder]: ... def iter_modules(path: Iterable[str] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ... diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 29ee8b66815e..ec532ca3cffe 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -67,7 +67,7 @@ class Match(Generic[AnyStr]): @overload def expand(self: Match[str], template: str) -> str: ... @overload - def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... # type: ignore[misc] + def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... # type: ignore[overload-overlap] @overload def expand(self, template: AnyStr) -> AnyStr: ... # group() returns "AnyStr" or "AnyStr | None", depending on the pattern. @@ -117,19 +117,19 @@ class Pattern(Generic[AnyStr]): @overload def search(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[misc] + def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] @overload def search(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def match(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[misc] + def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] @overload def match(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def fullmatch(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def fullmatch(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[misc] + def fullmatch(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] @overload def fullmatch(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload @@ -148,13 +148,13 @@ class Pattern(Generic[AnyStr]): @overload def finditer(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[str]]: ... @overload - def finditer(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[bytes]]: ... # type: ignore[misc] + def finditer(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[bytes]]: ... # type: ignore[overload-overlap] @overload def finditer(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[AnyStr]]: ... @overload def sub(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> str: ... @overload - def sub( # type: ignore[misc] + def sub( # type: ignore[overload-overlap] self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, @@ -165,7 +165,7 @@ class Pattern(Generic[AnyStr]): @overload def subn(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> tuple[str, int]: ... @overload - def subn( # type: ignore[misc] + def subn( # type: ignore[overload-overlap] self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, diff --git a/mypy/typeshed/stdlib/shelve.pyi b/mypy/typeshed/stdlib/shelve.pyi index b162b3a85766..59abeafe6fca 100644 --- a/mypy/typeshed/stdlib/shelve.pyi +++ b/mypy/typeshed/stdlib/shelve.pyi @@ -16,7 +16,7 @@ class Shelf(MutableMapping[str, _VT]): def __iter__(self) -> Iterator[str]: ... def __len__(self) -> int: ... @overload # type: ignore[override] - def get(self, key: str, default: None = None) -> _VT | None: ... # type: ignore[misc] # overlapping overloads + def get(self, key: str, default: None = None) -> _VT | None: ... @overload def get(self, key: str, default: _VT) -> _VT: ... @overload @@ -29,6 +29,7 @@ class Shelf(MutableMapping[str, _VT]): def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... + def __del__(self) -> None: ... def close(self) -> None: ... def sync(self) -> None: ... diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index 38c50d51b129..78e930920073 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -154,13 +154,13 @@ def disk_usage(path: FileDescriptorOrPath) -> _ntuple_diskusage: ... # see https://bugs.python.org/issue33140. We keep it here because it's # in __all__. @overload -def chown(path: StrOrBytesPath, user: str | int, group: None = None) -> None: ... +def chown(path: FileDescriptorOrPath, user: str | int, group: None = None) -> None: ... @overload -def chown(path: StrOrBytesPath, user: None = None, *, group: str | int) -> None: ... +def chown(path: FileDescriptorOrPath, user: None = None, *, group: str | int) -> None: ... @overload -def chown(path: StrOrBytesPath, user: None, group: str | int) -> None: ... +def chown(path: FileDescriptorOrPath, user: None, group: str | int) -> None: ... @overload -def chown(path: StrOrBytesPath, user: str | int, group: str | int) -> None: ... +def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... if sys.version_info >= (3, 8): @overload diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index b6cc23651ade..b89623f05c99 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2564,6 +2564,7 @@ class Popen(Generic[AnyStr]): def __exit__( self, exc_type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... + def __del__(self) -> None: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi index 6109b368c01a..b508a1ea8e20 100644 --- a/mypy/typeshed/stdlib/sunau.pyi +++ b/mypy/typeshed/stdlib/sunau.pyi @@ -34,6 +34,7 @@ class Au_read: def __init__(self, f: _File) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... + def __del__(self) -> None: ... def getfp(self) -> IO[bytes] | None: ... def rewind(self) -> None: ... def close(self) -> None: ... @@ -54,6 +55,7 @@ class Au_write: def __init__(self, f: _File) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... + def __del__(self) -> None: ... def setnchannels(self, nchannels: int) -> None: ... def getnchannels(self) -> int: ... def setsampwidth(self, sampwidth: int) -> None: ... diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi index 10f6e4930f75..d244d54f2fbf 100644 --- a/mypy/typeshed/stdlib/telnetlib.pyi +++ b/mypy/typeshed/stdlib/telnetlib.pyi @@ -119,3 +119,4 @@ class Telnet: def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index f8dcb24c1daf..628f99410732 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -629,7 +629,7 @@ class TemporaryDirectory(Generic[AnyStr]): # The overloads overlap, but they should still work fine. @overload -def mkstemp( # type: ignore[misc] +def mkstemp( # type: ignore[overload-overlap] suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None, text: bool = False ) -> tuple[int, str]: ... @overload @@ -639,7 +639,7 @@ def mkstemp( # The overloads overlap, but they should still work fine. @overload -def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... # type: ignore[misc] +def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... # type: ignore[overload-overlap] @overload def mkdtemp(suffix: bytes | None = None, prefix: bytes | None = None, dir: BytesPath | None = None) -> bytes: ... def mktemp(suffix: str = "", prefix: str = "tmp", dir: StrPath | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index d0eb97aa5ebd..a73b1e275f11 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -282,6 +282,7 @@ class Variable: @deprecated("use trace_info() instead of trace_vinfo()") def trace_vinfo(self): ... def __eq__(self, other: object) -> bool: ... + def __del__(self) -> None: ... class StringVar(Variable): def __init__(self, master: Misc | None = None, value: str | None = None, name: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi index 8f438537369c..5a83bb56679f 100644 --- a/mypy/typeshed/stdlib/tkinter/dnd.pyi +++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi @@ -15,5 +15,6 @@ class DndHandler: def finish(self, event: Event[Misc] | None, commit: int = 0) -> None: ... def on_motion(self, event: Event[Misc]) -> None: ... def on_release(self, event: Event[Misc]) -> None: ... + def __del__(self) -> None: ... def dnd_start(source: _DndSource, event: Event[Misc]) -> DndHandler | None: ... diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 0a557e921914..9dffcd1ba0c6 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -101,6 +101,7 @@ class Font: def metrics(self, *, displayof: tkinter.Misc | None = ...) -> _MetricsDict: ... def measure(self, text: str, displayof: tkinter.Misc | None = None) -> int: ... def __eq__(self, other: object) -> bool: ... + def __del__(self) -> None: ... def families(root: tkinter.Misc | None = None, displayof: tkinter.Misc | None = None) -> tuple[str, ...]: ... def names(root: tkinter.Misc | None = None) -> tuple[str, ...]: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index bb416717a378..2bbbafbcb945 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1039,7 +1039,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def heading(self, column: str | int, option: str) -> Any: ... @overload - def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[misc] + def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[overload-overlap] @overload def heading( self, @@ -1083,7 +1083,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def item(self, item: str | int, option: str) -> Any: ... @overload - def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... # type: ignore[misc] + def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... # type: ignore[overload-overlap] @overload def item( self, diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 36cd5f1f6e9d..fd0723fd73ed 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -336,7 +336,7 @@ class TPen: def isvisible(self) -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload - def pen(self) -> _PenState: ... # type: ignore[misc] + def pen(self) -> _PenState: ... # type: ignore[overload-overlap] @overload def pen( self, @@ -382,7 +382,7 @@ class RawTurtle(TPen, TNavigator): def shape(self, name: str) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapesize(self) -> tuple[float, float, float]: ... # type: ignore[misc] + def shapesize(self) -> tuple[float, float, float]: ... # type: ignore[overload-overlap] @overload def shapesize( self, stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None @@ -393,7 +393,7 @@ class RawTurtle(TPen, TNavigator): def shearfactor(self, shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapetransform(self) -> tuple[float, float, float, float]: ... # type: ignore[misc] + def shapetransform(self) -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] @overload def shapetransform( self, t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None @@ -617,7 +617,7 @@ def isvisible() -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload -def pen() -> _PenState: ... # type: ignore[misc] +def pen() -> _PenState: ... # type: ignore[overload-overlap] @overload def pen( pen: _PenState | None = None, @@ -656,7 +656,7 @@ if sys.version_info >= (3, 12): # Unsafely overlaps when no arguments are provided @overload -def shapesize() -> tuple[float, float, float]: ... # type: ignore[misc] +def shapesize() -> tuple[float, float, float]: ... # type: ignore[overload-overlap] @overload def shapesize(stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None) -> None: ... @overload @@ -666,7 +666,7 @@ def shearfactor(shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload -def shapetransform() -> tuple[float, float, float, float]: ... # type: ignore[misc] +def shapetransform() -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] @overload def shapetransform( t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index fcaf5264c5e3..b26a668d273b 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -593,9 +593,8 @@ _R = TypeVar("_R") _P = ParamSpec("_P") # it's not really an Awaitable, but can be used in an await expression. Real type: Generator & Awaitable -# The type: ignore is due to overlapping overloads, not the use of ParamSpec @overload -def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... # type: ignore[misc] +def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... # type: ignore[overload-overlap] @overload def coroutine(func: _Fn) -> _Fn: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 7694157d70fe..555df0ea47c8 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -281,7 +281,12 @@ if sys.version_info >= (3, 10): class NewType: def __init__(self, name: str, tp: Any) -> None: ... - def __call__(self, __x: _T) -> _T: ... + if sys.version_info >= (3, 11): + @staticmethod + def __call__(__x: _T) -> _T: ... + else: + def __call__(self, x: _T) -> _T: ... + def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... __supertype__: type @@ -970,9 +975,8 @@ if sys.version_info >= (3, 12): @property def __module__(self) -> str | None: ... # type: ignore[override] def __getitem__(self, parameters: Any) -> Any: ... - if sys.version_info >= (3, 10): - def __or__(self, right: Any) -> _SpecialForm: ... - def __ror__(self, left: Any) -> _SpecialForm: ... + def __or__(self, right: Any) -> _SpecialForm: ... + def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 13): def is_protocol(__tp: type) -> bool: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index b5e2341cd020..5c5b756f5256 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -182,6 +182,7 @@ __all__ = [ "is_protocol", "no_type_check", "no_type_check_decorator", + "ReadOnly", ] _T = typing.TypeVar("_T") @@ -220,6 +221,8 @@ def IntVar(name: str) -> Any: ... # returns a new TypeVar class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): __required_keys__: ClassVar[frozenset[str]] __optional_keys__: ClassVar[frozenset[str]] + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] __total__: ClassVar[bool] __orig_bases__: ClassVar[tuple[Any, ...]] def copy(self) -> Self: ... @@ -283,7 +286,6 @@ class SupportsIndex(Protocol, metaclass=abc.ABCMeta): if sys.version_info >= (3, 10): from typing import ( Concatenate as Concatenate, - NewType as NewType, ParamSpecArgs as ParamSpecArgs, ParamSpecKwargs as ParamSpecKwargs, TypeAlias as TypeAlias, @@ -308,18 +310,13 @@ else: TypeGuard: _SpecialForm def is_typeddict(tp: object) -> bool: ... - class NewType: - def __init__(self, name: str, tp: Any) -> None: ... - def __call__(self, __x: _T) -> _T: ... - __supertype__: type - -# New things in 3.11 -# NamedTuples are not new, but the ability to create generic NamedTuples is new in 3.11 +# New and changed things in 3.11 if sys.version_info >= (3, 11): from typing import ( LiteralString as LiteralString, NamedTuple as NamedTuple, Never as Never, + NewType as NewType, NotRequired as NotRequired, Required as Required, Self as Self, @@ -376,6 +373,14 @@ else: def _replace(self, **kwargs: Any) -> Self: ... + class NewType: + def __init__(self, name: str, tp: Any) -> None: ... + def __call__(self, __obj: _T) -> _T: ... + __supertype__: type + if sys.version_info >= (3, 10): + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... + # New things in 3.xx # The `default` parameter was added to TypeVar, ParamSpec, and TypeVarTuple (PEP 696) # The `infer_variance` parameter was added to TypeVar in 3.12 (PEP 695) @@ -449,7 +454,12 @@ class TypeVarTuple: def __init__(self, name: str, *, default: Any | None = None) -> None: ... def __iter__(self) -> Any: ... # Unpack[Self] -def deprecated(__msg: str, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> Callable[[_T], _T]: ... +class deprecated: + message: str + category: type[Warning] | None + stacklevel: int + def __init__(self, __message: str, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... + def __call__(self, __arg: _T) -> _T: ... if sys.version_info >= (3, 12): from collections.abc import Buffer as Buffer @@ -496,3 +506,5 @@ class Doc: def __init__(self, __documentation: str) -> None: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... + +ReadOnly: _SpecialForm diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index c1de205fbd55..b71eec2e0644 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -17,3 +17,5 @@ class IsolatedAsyncioTestCase(TestCase): def addAsyncCleanup(self, __func: Callable[_P, Awaitable[object]], *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): async def enterAsyncContext(self, cm: AbstractAsyncContextManager[_T]) -> _T: ... + if sys.version_info >= (3, 9): + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index baf025bdeb5a..8e96b23ce959 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -318,7 +318,7 @@ class _patcher: # Ideally we'd be able to add an overload for it so that the return type is _patch[MagicMock], # but that's impossible with the current type system. @overload - def __call__( # type: ignore[misc] + def __call__( # type: ignore[overload-overlap] self, target: str, new: _T, @@ -343,7 +343,7 @@ class _patcher: ) -> _patch_default_new: ... @overload @staticmethod - def object( # type: ignore[misc] + def object( target: Any, attribute: str, new: _T, diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 237a4d264b51..ca3feaea262a 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -337,6 +337,7 @@ class URLopener: def open_https(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... # undocumented def open_local_file(self, url: str) -> addinfourl: ... # undocumented def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ... # undocumented + def __del__(self) -> None: ... class FancyURLopener(URLopener): def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ... diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index 0d004d6b2d8a..6b7af2f79da1 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadableBuffer, Unused from typing import IO, Any, BinaryIO, NamedTuple, NoReturn, overload -from typing_extensions import Literal, Self, TypeAlias +from typing_extensions import Literal, Self, TypeAlias, deprecated if sys.version_info >= (3, 9): __all__ = ["open", "Error", "Wave_read", "Wave_write"] @@ -26,6 +26,7 @@ class Wave_read: def __init__(self, f: _File) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... + def __del__(self) -> None: ... def getfp(self) -> BinaryIO | None: ... def rewind(self) -> None: ... def close(self) -> None: ... @@ -37,7 +38,9 @@ class Wave_read: def getcomptype(self) -> str: ... def getcompname(self) -> str: ... def getparams(self) -> _wave_params: ... + @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") def getmarkers(self) -> None: ... + @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") def getmark(self, id: Any) -> NoReturn: ... def setpos(self, pos: int) -> None: ... def readframes(self, nframes: int) -> bytes: ... @@ -46,6 +49,7 @@ class Wave_write: def __init__(self, f: _File) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... + def __del__(self) -> None: ... def setnchannels(self, nchannels: int) -> None: ... def getnchannels(self) -> int: ... def setsampwidth(self, sampwidth: int) -> None: ... @@ -59,8 +63,11 @@ class Wave_write: def getcompname(self) -> str: ... def setparams(self, params: _wave_params | tuple[int, int, int, int, str, str]) -> None: ... def getparams(self) -> _wave_params: ... + @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ... + @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") def getmark(self, id: Any) -> NoReturn: ... + @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") def getmarkers(self) -> None: ... def tell(self) -> int: ... def writeframesraw(self, data: ReadableBuffer) -> None: ... diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index ae88f3a317c1..1bb2eacfb46a 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -75,7 +75,7 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def items(self) -> Iterator[tuple[_KT, _VT]]: ... # type: ignore[override] def itervaluerefs(self) -> Iterator[KeyedRef[_KT, _VT]]: ... def valuerefs(self) -> list[KeyedRef[_KT, _VT]]: ... - def setdefault(self, key: _KT, default: _VT) -> _VT: ... # type: ignore[override] + def setdefault(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT) -> _VT: ... @overload diff --git a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi index 74d2efb010cd..2ccbc95bbef0 100644 --- a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi @@ -82,6 +82,6 @@ class AttributesNSImpl(AttributesImpl): def __contains__(self, name: _NSName) -> bool: ... # type: ignore[override] @overload # type: ignore[override] def get(self, name: _NSName, alternative: None = None) -> str | None: ... - @overload # type: ignore[override] + @overload def get(self, name: _NSName, alternative: str) -> str: ... def items(self) -> list[tuple[_NSName, str]]: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile.pyi index b7144f3ab528..5483b84fe6f6 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile.pyi @@ -26,7 +26,9 @@ __all__ = [ if sys.version_info >= (3, 8): __all__ += ["Path"] -_DateTuple: TypeAlias = tuple[int, int, int, int, int, int] +# TODO: use TypeAlias when mypy bugs are fixed +# https://github.com/python/mypy/issues/16581 +_DateTuple = tuple[int, int, int, int, int, int] # noqa: Y026 _ReadWriteMode: TypeAlias = Literal["r", "w"] _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] _ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"] @@ -187,6 +189,8 @@ class ZipFile: if sys.version_info >= (3, 11): def mkdir(self, zinfo_or_directory_name: str | ZipInfo, mode: int = 0o777) -> None: ... + def __del__(self) -> None: ... + class PyZipFile(ZipFile): def __init__( self, file: str | IO[bytes], mode: _ZipFileMode = "r", compression: int = 0, allowZip64: bool = True, optimize: int = -1 @@ -231,7 +235,7 @@ if sys.version_info >= (3, 8): def make(cls, source: ZipFile) -> CompleteDirs: ... @overload @classmethod - def make(cls: type[Self], source: StrPath | IO[bytes]) -> Self: ... + def make(cls, source: StrPath | IO[bytes]) -> Self: ... class Path: root: CompleteDirs From 588623ff2fd1d842ce3b70d330d8c37a166db8c4 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0416/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 92b5279bcfcd..04f2f8a89539 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -57,7 +57,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( Concatenate, Literal, - LiteralString, ParamSpec, Self, SupportsIndex, @@ -445,17 +444,8 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -463,20 +453,11 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... if sys.version_info >= (3, 8): - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] else: - @overload - def expandtabs(self: LiteralString, tabsize: int = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -492,91 +473,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -587,9 +509,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -598,25 +517,13 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From bdcc90e85be8b18c3a37b4ef83645e1c09f96495 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0417/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 04f2f8a89539..e3d7ee7e5cc1 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1702,11 +1702,11 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # Instead, we special-case the most common examples of this: bool and literal integers. if sys.version_info >= (3, 8): @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[overload-overlap] + def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] else: @overload - def sum(__iterable: Iterable[bool | _LiteralInteger], __start: int = 0) -> int: ... # type: ignore[overload-overlap] + def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... From 3e5d813372e4fc1899319f31425bfc11c27fddb3 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0418/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 495e29dfd8ce..8a891971e9f1 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -167,11 +167,7 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 344298e3a7b1a299092c684c11c28e9f4dc44dd9 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0419/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 451896bed72a..4d8c45e96103 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypedDict, final +from typing_extensions import Literal, Self, TypeAlias, TypedDict, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,12 +28,10 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWrapper = TypeVar("_RWrapper") @overload def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... @@ -87,41 +85,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From eb1ee973778e3cf719948e1653db9760ea49405d Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 3 Dec 2023 00:23:54 +0000 Subject: [PATCH 0420/1617] Update hashes in `sync-typeshed.py` following recent typeshed sync (#16600) Followup to #16598 --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 77f921a89b1b..9d6fd92270a5 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "9859fe7ba", # LiteralString reverts - "378a866e9", # sum reverts - "2816b97d5", # ctypes reverts - "7d987a105", # ParamSpec for functools.wraps + "588623ff2", # LiteralString reverts + "bdcc90e85", # sum reverts + "3e5d81337", # ctypes reverts + "344298e3a", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: From d54cc35a93b1f1bda8f837e0f3ae6f964a1c7feb Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Mon, 4 Dec 2023 08:11:45 +0100 Subject: [PATCH 0421/1617] Change example in test cases with no stubs available (#16513) Fixes https://github.com/python/mypy/issues/16466 --- test-data/unit/pythoneval.test | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 7dd2b2f76f8c..c6ca71f5d56a 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1568,24 +1568,24 @@ note: A user-defined top-level module with name "typing" is not supported # flags: --ignore-missing-imports import scribe # No Python 3 stubs available for scribe from scribe import x -import docutils # Python 3 stubs available for docutils +import python2 # Python 3 stubs available for python2 import foobar_asdf import jack # This has a stubs package but was never bundled with mypy, so ignoring works [out] -_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "docutils" -_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-docutils" +_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "python2" +_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-six" _testIgnoreImportIfNoPython3StubAvailable.py:4: note: (or run "mypy --install-types" to install all missing stub packages) _testIgnoreImportIfNoPython3StubAvailable.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [case testNoPython3StubAvailable] import scribe from scribe import x -import docutils +import python2 [out] _testNoPython3StubAvailable.py:1: error: Cannot find implementation or library stub for module named "scribe" _testNoPython3StubAvailable.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "docutils" -_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-docutils" +_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "python2" +_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-six" _testNoPython3StubAvailable.py:3: note: (or run "mypy --install-types" to install all missing stub packages) From c224da5c7c414f92ded4b7816d16d5dd4ed32193 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Mon, 4 Dec 2023 08:13:58 +0100 Subject: [PATCH 0422/1617] Do not intersect types in isinstance checks if at least one is final (#16330) Fixes #15148 I think it also fixes the [initial bug](https://github.com/python/mypy/issues/12163#issue-1131262225) reported in #12163 (this is why I added a TypeVar test case) but not [this bug](https://github.com/python/mypy/issues/12163#issuecomment-1035865305) reported later in the same issue. --- mypy/checker.py | 13 +++- mypy/messages.py | 2 +- test-data/unit/check-narrowing.test | 99 +++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7c6f59fafdc8..979a55b223c9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5254,6 +5254,15 @@ def _make_fake_typeinfo_and_full_name( pretty_names_list = pretty_seq( format_type_distinctly(*base_classes, options=self.options, bare=True), "and" ) + + new_errors = [] + for base in base_classes: + if base.type.is_final: + new_errors.append((pretty_names_list, f'"{base.type.name}" is final')) + if new_errors: + errors.extend(new_errors) + return None + try: info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) with self.msg.filter_errors() as local_errors: @@ -5266,10 +5275,10 @@ def _make_fake_typeinfo_and_full_name( self.check_multiple_inheritance(info) info.is_intersection = True except MroError: - errors.append((pretty_names_list, "inconsistent method resolution order")) + errors.append((pretty_names_list, "would have inconsistent method resolution order")) return None if local_errors.has_new_errors(): - errors.append((pretty_names_list, "incompatible method signatures")) + errors.append((pretty_names_list, "would have incompatible method signatures")) return None curr_module.names[full_name] = SymbolTableNode(GDEF, info) diff --git a/mypy/messages.py b/mypy/messages.py index ddb048444695..069c4d51e281 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2051,7 +2051,7 @@ def redundant_expr(self, description: str, truthiness: bool, context: Context) - def impossible_intersection( self, formatted_base_class_list: str, reason: str, context: Context ) -> None: - template = "Subclass of {} cannot exist: would have {}" + template = "Subclass of {} cannot exist: {}" self.fail( template.format(formatted_base_class_list, reason), context, code=codes.UNREACHABLE ) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index d0ad1367aca0..a2859dfffa3a 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1020,6 +1020,105 @@ else: reveal_type(true_or_false) # N: Revealed type is "Literal[False]" [builtins fixtures/primitives.pyi] + +[case testNarrowingIsInstanceFinalSubclass] +# flags: --warn-unreachable + +from typing import final + +class N: ... +@final +class F1: ... +@final +class F2: ... + +n: N +f1: F1 + +if isinstance(f1, F1): + reveal_type(f1) # N: Revealed type is "__main__.F1" +else: + reveal_type(f1) # E: Statement is unreachable + +if isinstance(n, F1): # E: Subclass of "N" and "F1" cannot exist: "F1" is final + reveal_type(n) # E: Statement is unreachable +else: + reveal_type(n) # N: Revealed type is "__main__.N" + +if isinstance(f1, N): # E: Subclass of "F1" and "N" cannot exist: "F1" is final + reveal_type(f1) # E: Statement is unreachable +else: + reveal_type(f1) # N: Revealed type is "__main__.F1" + +if isinstance(f1, F2): # E: Subclass of "F1" and "F2" cannot exist: "F1" is final \ + # E: Subclass of "F1" and "F2" cannot exist: "F2" is final + reveal_type(f1) # E: Statement is unreachable +else: + reveal_type(f1) # N: Revealed type is "__main__.F1" +[builtins fixtures/isinstance.pyi] + + +[case testNarrowingIsInstanceFinalSubclassWithUnions] +# flags: --warn-unreachable + +from typing import final, Union + +class N: ... +@final +class F1: ... +@final +class F2: ... + +n_f1: Union[N, F1] +n_f2: Union[N, F2] +f1_f2: Union[F1, F2] + +if isinstance(n_f1, F1): + reveal_type(n_f1) # N: Revealed type is "__main__.F1" +else: + reveal_type(n_f1) # N: Revealed type is "__main__.N" + +if isinstance(n_f2, F1): # E: Subclass of "N" and "F1" cannot exist: "F1" is final \ + # E: Subclass of "F2" and "F1" cannot exist: "F2" is final \ + # E: Subclass of "F2" and "F1" cannot exist: "F1" is final + reveal_type(n_f2) # E: Statement is unreachable +else: + reveal_type(n_f2) # N: Revealed type is "Union[__main__.N, __main__.F2]" + +if isinstance(f1_f2, F1): + reveal_type(f1_f2) # N: Revealed type is "__main__.F1" +else: + reveal_type(f1_f2) # N: Revealed type is "__main__.F2" +[builtins fixtures/isinstance.pyi] + + +[case testNarrowingIsSubclassFinalSubclassWithTypeVar] +# flags: --warn-unreachable + +from typing import final, Type, TypeVar + +@final +class A: ... +@final +class B: ... + +T = TypeVar("T", A, B) + +def f(cls: Type[T]) -> T: + if issubclass(cls, A): + reveal_type(cls) # N: Revealed type is "Type[__main__.A]" + x: bool + if x: + return A() + else: + return B() # E: Incompatible return value type (got "B", expected "A") + assert False + +reveal_type(f(A)) # N: Revealed type is "__main__.A" +reveal_type(f(B)) # N: Revealed type is "__main__.B" +[builtins fixtures/isinstance.pyi] + + [case testNarrowingLiteralIdentityCheck] from typing import Union from typing_extensions import Literal From 7c33e7c03444ae748b82163e7b4e1666dfaf94c7 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Mon, 4 Dec 2023 04:33:25 -0500 Subject: [PATCH 0423/1617] @final class without __bool__ cannot have falsey instances (#16566) Once class C is final, we know that a derived class won't add a `__bool__` or a `__len__` so if they're missing, we can assume every instance of C to be truthy. Relates to #16565 --- mypy/typeops.py | 25 ++++++++++++------- test-data/unit/check-final.test | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index e92fad0e872c..2bf8ffbf47ab 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -569,15 +569,15 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ return items -def _get_type_special_method_bool_ret_type(t: Type) -> Type | None: +def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: t = get_proper_type(t) if isinstance(t, Instance): - bool_method = t.type.get("__bool__") - if bool_method: - callee = get_proper_type(bool_method.type) - if isinstance(callee, CallableType): - return callee.ret_type + sym = t.type.get(name) + if sym: + sym_type = get_proper_type(sym.type) + if isinstance(sym_type, CallableType): + return sym_type.ret_type return None @@ -600,7 +600,9 @@ def true_only(t: Type) -> ProperType: can_be_true_items = [item for item in new_items if item.can_be_true] return make_simplified_union(can_be_true_items, line=t.line, column=t.column) else: - ret_type = _get_type_special_method_bool_ret_type(t) + ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type( + t, name="__len__" + ) if ret_type and not ret_type.can_be_true: return UninhabitedType(line=t.line, column=t.column) @@ -633,9 +635,14 @@ def false_only(t: Type) -> ProperType: can_be_false_items = [item for item in new_items if item.can_be_false] return make_simplified_union(can_be_false_items, line=t.line, column=t.column) else: - ret_type = _get_type_special_method_bool_ret_type(t) + ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type( + t, name="__len__" + ) - if ret_type and not ret_type.can_be_false: + if ret_type: + if not ret_type.can_be_false: + return UninhabitedType(line=t.line) + elif isinstance(t, Instance) and t.type.is_final: return UninhabitedType(line=t.line) new_t = copy_type(t) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index a2fd64386707..b1378a47b1b1 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1130,3 +1130,47 @@ class Child(Parent): __foo: Final[int] = 1 @final def __bar(self) -> None: ... + +[case testFinalWithoutBool] +from typing_extensions import final, Literal + +class A: + pass + +@final +class B: + pass + +@final +class C: + def __len__(self) -> Literal[1]: return 1 + +reveal_type(A() and 42) # N: Revealed type is "Union[__main__.A, Literal[42]?]" +reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" +reveal_type(C() and 42) # N: Revealed type is "Literal[42]?" + +[builtins fixtures/bool.pyi] + +[case testFinalWithoutBoolButWithLen] +from typing_extensions import final, Literal + +# Per Python data model, __len__ is called if __bool__ does not exist. +# In a @final class, __bool__ would not exist. + +@final +class A: + def __len__(self) -> int: ... + +@final +class B: + def __len__(self) -> Literal[1]: return 1 + +@final +class C: + def __len__(self) -> Literal[0]: return 0 + +reveal_type(A() and 42) # N: Revealed type is "Union[__main__.A, Literal[42]?]" +reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" +reveal_type(C() and 42) # N: Revealed type is "__main__.C" + +[builtins fixtures/bool.pyi] From 1cdeecdcc11115a3d15610801fdf91b0ed6bbe0a Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Mon, 4 Dec 2023 12:56:59 -0600 Subject: [PATCH 0424/1617] bump version to 1.9.0+dev (#16615) Preparation step for the 1.8.0 release. --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 2c2c2b052da2..74e80839308c 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.8.0+dev" +__version__ = "1.9.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 88bf6f20754c1713e3eaeee8cc882a7ba6c9483d Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 7 Dec 2023 16:37:31 +0300 Subject: [PATCH 0425/1617] Add `alias` support to `field()` in `attrs` plugin (#16610) Closes https://github.com/python/mypy/issues/16586 CC @ikonst --- mypy/plugins/attrs.py | 23 +++++++++++++++++--- test-data/unit/check-plugin-attrs.test | 25 ++++++++++++++++++++++ test-data/unit/lib-stub/attrs/__init__.pyi | 4 ++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3ddc234a7e4a..81f96c088ecd 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -105,6 +105,7 @@ class Attribute: def __init__( self, name: str, + alias: str | None, info: TypeInfo, has_default: bool, init: bool, @@ -114,6 +115,7 @@ def __init__( init_type: Type | None, ) -> None: self.name = name + self.alias = alias self.info = info self.has_default = has_default self.init = init @@ -171,12 +173,14 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: arg_kind = ARG_OPT if self.has_default else ARG_POS # Attrs removes leading underscores when creating the __init__ arguments. - return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind) + name = self.alias or self.name.lstrip("_") + return Argument(Var(name, init_type), init_type, None, arg_kind) def serialize(self) -> JsonDict: """Serialize this object so it can be saved and restored.""" return { "name": self.name, + "alias": self.alias, "has_default": self.has_default, "init": self.init, "kw_only": self.kw_only, @@ -205,6 +209,7 @@ def deserialize( return Attribute( data["name"], + data["alias"], info, data["has_default"], data["init"], @@ -498,6 +503,7 @@ def _attributes_from_assignment( or if auto_attribs is enabled also like this: x: type x: type = default_value + x: type = attr.ib(...) """ for lvalue in stmt.lvalues: lvalues, rvalues = _parse_assignments(lvalue, stmt) @@ -564,7 +570,7 @@ def _attribute_from_auto_attrib( has_rhs = not isinstance(rvalue, TempNode) sym = ctx.cls.info.names.get(name) init_type = sym.type if sym else None - return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type) + return Attribute(name, None, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type) def _attribute_from_attrib_maker( @@ -628,9 +634,20 @@ def _attribute_from_attrib_maker( converter = convert converter_info = _parse_converter(ctx, converter) + # Custom alias might be defined: + alias = None + alias_expr = _get_argument(rvalue, "alias") + if alias_expr: + alias = ctx.api.parse_str_literal(alias_expr) + if alias is None: + ctx.api.fail( + '"alias" argument to attrs field must be a string literal', + rvalue, + code=LITERAL_REQ, + ) name = unmangle(lhs.name) return Attribute( - name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type + name, alias, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type ) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index fb5f1f9472c2..b2161b91e225 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1055,6 +1055,31 @@ C() C(_x=42) # E: Unexpected keyword argument "_x" for "C" [builtins fixtures/list.pyi] +[case testAttrsAliasForInit] +from attrs import define, field + +@define +class C1: + _x: int = field(alias="x1") + +c1 = C1(x1=42) +reveal_type(c1._x) # N: Revealed type is "builtins.int" +c1.x1 # E: "C1" has no attribute "x1" +C1(_x=42) # E: Unexpected keyword argument "_x" for "C1" + +alias = "x2" +@define +class C2: + _x: int = field(alias=alias) # E: "alias" argument to attrs field must be a string literal + +@define +class C3: + _x: int = field(alias="_x") + +c3 = C3(_x=1) +reveal_type(c3._x) # N: Revealed type is "builtins.int" +[builtins fixtures/plugin_attrs.pyi] + [case testAttrsAutoMustBeAll] import attr @attr.s(auto_attribs=True) diff --git a/test-data/unit/lib-stub/attrs/__init__.pyi b/test-data/unit/lib-stub/attrs/__init__.pyi index 7a88170d7271..f610957a48a3 100644 --- a/test-data/unit/lib-stub/attrs/__init__.pyi +++ b/test-data/unit/lib-stub/attrs/__init__.pyi @@ -79,6 +79,7 @@ def field( eq: Optional[bool] = ..., order: Optional[bool] = ..., on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., ) -> Any: ... # This form catches an explicit None or no default and infers the type from the @@ -98,6 +99,7 @@ def field( eq: Optional[bool] = ..., order: Optional[bool] = ..., on_setattr: Optional[object] = ..., + alias: Optional[str] = ..., ) -> _T: ... # This form catches an explicit default argument. @@ -116,6 +118,7 @@ def field( eq: Optional[bool] = ..., order: Optional[bool] = ..., on_setattr: Optional[object] = ..., + alias: Optional[str] = ..., ) -> _T: ... # This form covers type=non-Type: e.g. forward references (str), Any @@ -134,6 +137,7 @@ def field( eq: Optional[bool] = ..., order: Optional[bool] = ..., on_setattr: Optional[object] = ..., + alias: Optional[str] = ..., ) -> Any: ... def evolve(inst: _T, **changes: Any) -> _T: ... From cbbcdb8a2c0fb70f3b89a518b7aa36925d9c4c37 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:02:57 -0800 Subject: [PATCH 0426/1617] Allow type ignores of PEP 695 constructs (#16608) This is basically a pre-existing bug and affects other errors that ASTConverter might raise, like merging overloads. It could vaguely be nice to move all the set_file_ignored_lines into fastparse, instead of BuildManager.parse_file. Could also clean up the ignore_errors logic a little bit more. Fixes #16607 --- misc/dump-ast.py | 4 ++-- mypy/build.py | 4 ++-- mypy/fastparse.py | 31 ++++++++++++++-------------- mypy/parse.py | 12 +++++++++-- mypy/test/testparse.py | 16 +++++++++++--- test-data/unit/check-errorcodes.test | 6 ++++-- test-data/unit/check-python312.test | 5 +++++ 7 files changed, 52 insertions(+), 26 deletions(-) diff --git a/misc/dump-ast.py b/misc/dump-ast.py index 6f70bbc8c9ed..7fdf905bae0b 100755 --- a/misc/dump-ast.py +++ b/misc/dump-ast.py @@ -9,7 +9,7 @@ import sys from mypy import defaults -from mypy.errors import CompileError +from mypy.errors import CompileError, Errors from mypy.options import Options from mypy.parse import parse @@ -19,7 +19,7 @@ def dump(fname: str, python_version: tuple[int, int], quiet: bool = False) -> No options.python_version = python_version with open(fname, "rb") as f: s = f.read() - tree = parse(s, fname, None, errors=None, options=options) + tree = parse(s, fname, None, errors=Errors(options), options=options) if not quiet: print(tree) diff --git a/mypy/build.py b/mypy/build.py index 961198fc2fa4..b3ca8d06916d 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2174,8 +2174,8 @@ def parse_file(self, *, temporary: bool = False) -> None: self.id, self.xpath, source, - self.ignore_all or self.options.ignore_errors, - self.options, + ignore_errors=self.ignore_all or self.options.ignore_errors, + options=self.options, ) else: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 95d99db84a15..cba01eab2e4e 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -190,7 +190,7 @@ def parse( source: str | bytes, fnam: str, module: str | None, - errors: Errors | None = None, + errors: Errors, options: Options | None = None, ) -> MypyFile: """Parse a source file, without doing any semantic analysis. @@ -199,16 +199,13 @@ def parse( on failure. Otherwise, use the errors object to report parse errors. """ ignore_errors = (options is not None and options.ignore_errors) or ( - errors is not None and fnam in errors.ignored_files + fnam in errors.ignored_files ) # If errors are ignored, we can drop many function bodies to speed up type checking. strip_function_bodies = ignore_errors and (options is None or not options.preserve_asts) - raise_on_error = False + if options is None: options = Options() - if errors is None: - errors = Errors(options) - raise_on_error = True errors.set_file(fnam, module, options=options) is_stub_file = fnam.endswith(".pyi") if is_stub_file: @@ -228,11 +225,9 @@ def parse( options=options, is_stub=is_stub_file, errors=errors, - ignore_errors=ignore_errors, strip_function_bodies=strip_function_bodies, + path=fnam, ).visit(ast) - tree.path = fnam - tree.is_stub = is_stub_file except SyntaxError as e: # alias to please mypyc is_py38_or_earlier = sys.version_info < (3, 9) @@ -254,9 +249,6 @@ def parse( ) tree = MypyFile([], [], False, {}) - if raise_on_error and errors.is_errors(): - errors.raise_error() - assert isinstance(tree, MypyFile) return tree @@ -357,8 +349,8 @@ def __init__( is_stub: bool, errors: Errors, *, - ignore_errors: bool, strip_function_bodies: bool, + path: str, ) -> None: # 'C' for class, 'D' for function signature, 'F' for function, 'L' for lambda self.class_and_function_stack: list[Literal["C", "D", "F", "L"]] = [] @@ -367,8 +359,8 @@ def __init__( self.options = options self.is_stub = is_stub self.errors = errors - self.ignore_errors = ignore_errors self.strip_function_bodies = strip_function_bodies + self.path = path self.type_ignores: dict[int, list[str]] = {} @@ -380,6 +372,10 @@ def note(self, msg: str, line: int, column: int) -> None: def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None: if blocker or not self.options.ignore_errors: + # Make sure self.errors reflects any type ignores that we have parsed + self.errors.set_file_ignored_lines( + self.path, self.type_ignores, self.options.ignore_errors + ) self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code) def fail_merge_overload(self, node: IfStmt) -> None: @@ -858,8 +854,13 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile: self.type_ignores[ti.lineno] = parsed else: self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False) + body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True)) - return MypyFile(body, self.imports, False, self.type_ignores) + + ret = MypyFile(body, self.imports, False, ignored_lines=self.type_ignores) + ret.is_stub = self.is_stub + ret.path = self.path + return ret # --- stmt --- # FunctionDef(identifier name, arguments args, diff --git a/mypy/parse.py b/mypy/parse.py index 8bf9983967ba..ee61760c0ac0 100644 --- a/mypy/parse.py +++ b/mypy/parse.py @@ -6,7 +6,12 @@ def parse( - source: str | bytes, fnam: str, module: str | None, errors: Errors | None, options: Options + source: str | bytes, + fnam: str, + module: str | None, + errors: Errors, + options: Options, + raise_on_error: bool = False, ) -> MypyFile: """Parse a source file, without doing any semantic analysis. @@ -19,4 +24,7 @@ def parse( source = options.transform_source(source) import mypy.fastparse - return mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options) + tree = mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options) + if raise_on_error and errors.is_errors(): + errors.raise_error() + return tree diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 0140eb072821..e33fa7e53ff0 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -8,7 +8,7 @@ from mypy import defaults from mypy.config_parser import parse_mypy_comments -from mypy.errors import CompileError +from mypy.errors import CompileError, Errors from mypy.options import Options from mypy.parse import parse from mypy.test.data import DataDrivenTestCase, DataSuite @@ -51,7 +51,12 @@ def test_parser(testcase: DataDrivenTestCase) -> None: try: n = parse( - bytes(source, "ascii"), fnam="main", module="__main__", errors=None, options=options + bytes(source, "ascii"), + fnam="main", + module="__main__", + errors=Errors(options), + options=options, + raise_on_error=True, ) a = n.str_with_options(options).split("\n") except CompileError as e: @@ -82,7 +87,12 @@ def test_parse_error(testcase: DataDrivenTestCase) -> None: skip() # Compile temporary file. The test file contains non-ASCII characters. parse( - bytes("\n".join(testcase.input), "utf-8"), INPUT_FILE_NAME, "__main__", None, options + bytes("\n".join(testcase.input), "utf-8"), + INPUT_FILE_NAME, + "__main__", + errors=Errors(options), + options=options, + raise_on_error=True, ) raise AssertionError("No errors reported") except CompileError as e: diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 28487a456156..1dd058730f28 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -975,11 +975,13 @@ def f(d: D, s: str) -> None: [typing fixtures/typing-typeddict.pyi] [case testRecommendErrorCode] -# type: ignore[whatever] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever"` [syntax] +# type: ignore[whatever] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever"` [syntax] \ + # N: Error code "syntax" not covered by "type: ignore" comment 1 + "asdf" [case testRecommendErrorCode2] -# type: ignore[whatever, other] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever, other"` [syntax] +# type: ignore[whatever, other] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever, other"` [syntax] \ + # N: Error code "syntax" not covered by "type: ignore" comment 1 + "asdf" [case testShowErrorCodesInConfig] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index cb89eb34880c..285563c19991 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -11,6 +11,11 @@ def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not va # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]" +type MyInt2 = int # type: ignore[valid-type] + +def h(x: MyInt2) -> MyInt2: + return reveal_type(x) # N: Revealed type is "builtins.int" + [case test695Class] class MyGen[T]: # E: PEP 695 generics are not yet supported def __init__(self, x: T) -> None: # E: Name "T" is not defined From 13e7213ad958a50ab4bf28abd63fa1f324f58a69 Mon Sep 17 00:00:00 2001 From: Maarten Huijsmans Date: Fri, 8 Dec 2023 20:00:50 +0100 Subject: [PATCH 0427/1617] Add exist_ok=True in write_junit_xml (#16637) Fixes #16630 --- mypy/util.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/util.py b/mypy/util.py index 7a13de427e8e..be8a22d08a27 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -314,10 +314,9 @@ def write_junit_xml( ) -> None: xml = _generate_junit_contents(dt, serious, messages_by_file, version, platform) - # checks for a directory structure in path and creates folders if needed + # creates folders if needed xml_dirs = os.path.dirname(os.path.abspath(path)) - if not os.path.isdir(xml_dirs): - os.makedirs(xml_dirs) + os.makedirs(xml_dirs, exist_ok=True) with open(path, "wb") as f: f.write(xml.encode("utf-8")) From 0567da99784c376e723907daf4f16df2f03368c1 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Sat, 9 Dec 2023 20:32:42 +0000 Subject: [PATCH 0428/1617] Prefer `exist_ok=True` over explicit `os.path.exists(...)` checks (#16642) This PR replaces explicit `os.path.exists(...)` checks with `os.makedirs(..., exist_ok=True)` where possible. This removes the need for an extra existence check and slightly simplifies the code. This can also prevent race conditions like #16630. --- mypy/report.py | 6 +++--- mypy/stats.py | 5 ----- mypy/stubgen.py | 3 +-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/mypy/report.py b/mypy/report.py index 86fcee0521a6..764cfec7799a 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -99,7 +99,7 @@ class AbstractReporter(metaclass=ABCMeta): def __init__(self, reports: Reports, output_dir: str) -> None: self.output_dir = output_dir if output_dir != "": - stats.ensure_dir_exists(output_dir) + os.makedirs(output_dir, exist_ok=True) @abstractmethod def on_file( @@ -737,7 +737,7 @@ def on_file( if path.startswith(".."): return out_path = os.path.join(self.output_dir, "xml", path + ".xml") - stats.ensure_dir_exists(os.path.dirname(out_path)) + os.makedirs(os.path.dirname(out_path), exist_ok=True) last_xml.write(out_path, encoding="utf-8") def on_finish(self) -> None: @@ -782,7 +782,7 @@ def on_file( if path.startswith(".."): return out_path = os.path.join(self.output_dir, "html", path + ".html") - stats.ensure_dir_exists(os.path.dirname(out_path)) + os.makedirs(os.path.dirname(out_path), exist_ok=True) transformed_html = bytes(self.xslt_html(last_xml, ext=self.param_html)) with open(out_path, "wb") as out_file: out_file.write(transformed_html) diff --git a/mypy/stats.py b/mypy/stats.py index b8803e03b9d2..b167a41b0e34 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -477,11 +477,6 @@ def is_complex(t: Type) -> bool: return is_generic(t) or isinstance(t, (FunctionLike, TupleType, TypeVarType)) -def ensure_dir_exists(dir: str) -> None: - if not os.path.exists(dir): - os.makedirs(dir) - - def is_special_form_any(t: AnyType) -> bool: return get_original_any(t).type_of_any == TypeOfAny.special_form diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 23b5fde9dff2..7ec3d7069fb2 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1833,8 +1833,7 @@ def parse_options(args: list[str]) -> Options: parser.error("Cannot specify both --parse-only/--no-analysis and --inspect-mode") # Create the output folder if it doesn't already exist. - if not os.path.exists(ns.output_dir): - os.makedirs(ns.output_dir) + os.makedirs(ns.output_dir, exist_ok=True) return Options( pyversion=pyversion, From 5fa95693bb3ca312224a5194dbb5149f8e7d7a55 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 12 Dec 2023 19:12:26 +0000 Subject: [PATCH 0429/1617] Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` (#16541) Fixes #16533 --- mypy/expandtype.py | 28 ++++++++++------- test-data/unit/check-typevar-tuple.test | 42 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 3acec4b96d06..f6aa74add9d8 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -307,18 +307,24 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) - # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] - if isinstance(var_arg_type, TupleType): - expanded_tuple = var_arg_type.accept(self) - assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) - expanded_items = expanded_tuple.items - fallback = var_arg_type.partial_fallback + if isinstance(var_arg_type, Instance): + # we have something like Unpack[Tuple[Any, ...]] + new_unpack = var_arg else: - # We have plain Unpack[Ts] - assert isinstance(var_arg_type, TypeVarTupleType) - fallback = var_arg_type.tuple_fallback - expanded_items = self.expand_unpack(var_arg) - new_unpack = UnpackType(TupleType(expanded_items, fallback)) + if isinstance(var_arg_type, TupleType): + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] + expanded_tuple = var_arg_type.accept(self) + assert isinstance(expanded_tuple, ProperType) and isinstance( + expanded_tuple, TupleType + ) + expanded_items = expanded_tuple.items + fallback = var_arg_type.partial_fallback + else: + # We have plain Unpack[Ts] + assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) + fallback = var_arg_type.tuple_fallback + expanded_items = self.expand_unpack(var_arg) + new_unpack = UnpackType(TupleType(expanded_items, fallback)) return prefix + [new_unpack] + suffix def visit_callable_type(self, t: CallableType) -> CallableType: diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 487f22699724..9c8d21114d4c 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2235,3 +2235,45 @@ z: Tuple[int, Unpack[Tuple[int, ...]]] = (1,) w: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *[2, 3, 4]) t: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *(2, 3, 4)) [builtins fixtures/tuple.pyi] + +[case testAliasToCallableWithUnpack] +from typing import Any, Callable, Tuple, Unpack + +_CallableValue = Callable[[Unpack[Tuple[Any, ...]]], Any] +def higher_order(f: _CallableValue) -> None: ... + +def good1(*args: int) -> None: ... +def good2(*args: str) -> int: ... + +def bad1(a: str, b: int, /) -> None: ... +def bad2(c: bytes, *args: int) -> str: ... +def bad3(*, d: str) -> int: ... +def bad4(**kwargs: None) -> None: ... + +higher_order(good1) +higher_order(good2) + +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[VarArg(Any)], Any]" +[builtins fixtures/tuple.pyi] + +[case testAliasToCallableWithUnpack2] +from typing import Any, Callable, Tuple, Unpack + +_CallableValue = Callable[[int, str, Unpack[Tuple[Any, ...]], int], Any] +def higher_order(f: _CallableValue) -> None: ... + +def good(a: int, b: str, *args: Unpack[Tuple[Unpack[Tuple[Any, ...]], int]]) -> int: ... +def bad1(a: str, b: int, /) -> None: ... +def bad2(c: bytes, *args: int) -> str: ... +def bad3(*, d: str) -> int: ... +def bad4(**kwargs: None) -> None: ... + +higher_order(good) +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +[builtins fixtures/tuple.pyi] From 91be28552e062601adc2a07075263994c102d5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tin=20Tvrtkovi=C4=87?= Date: Tue, 12 Dec 2023 21:08:14 +0100 Subject: [PATCH 0430/1617] Improve attrs hashability detection (#16556) Fixes #16550 Improve hashability detection for attrs classes. I added a new parameter to `add_attribute_to_class`, `overwrite_existing`, since I needed it. Frozen classes remain hashable, non-frozen default to no. This can be overriden by passing in `hash=True` or `unsafe_hash=True` (new parameter, added to stubs) to `define()`. Inheritance-wise I think we're correct, if a non-hashable class inherits from a hashable one, it's still unhashable at runtime. Accompanying tests. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/plugins/attrs.py | 15 ++++++ mypy/plugins/common.py | 3 +- test-data/unit/check-plugin-attrs.test | 59 ++++++++++++++++++++++ test-data/unit/fixtures/plugin_attrs.pyi | 1 + test-data/unit/lib-stub/attr/__init__.pyi | 2 + test-data/unit/lib-stub/attrs/__init__.pyi | 2 + 6 files changed, 81 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 81f96c088ecd..19a402492aef 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -310,6 +310,8 @@ def attr_class_maker_callback( it will add an __init__ or all the compare methods. For frozen=True it will turn the attrs into properties. + Hashability will be set according to https://www.attrs.org/en/stable/hashing.html. + See https://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works. If this returns False, some required metadata was not ready yet and we need another @@ -321,6 +323,9 @@ def attr_class_maker_callback( frozen = _get_frozen(ctx, frozen_default) order = _determine_eq_order(ctx) slots = _get_decorator_bool_argument(ctx, "slots", slots_default) + hashable = _get_decorator_bool_argument(ctx, "hash", False) or _get_decorator_bool_argument( + ctx, "unsafe_hash", False + ) auto_attribs = _get_decorator_optional_bool_argument(ctx, "auto_attribs", auto_attribs_default) kw_only = _get_decorator_bool_argument(ctx, "kw_only", False) @@ -359,10 +364,13 @@ def attr_class_maker_callback( adder = MethodAdder(ctx) # If __init__ is not being generated, attrs still generates it as __attrs_init__ instead. _add_init(ctx, attributes, adder, "__init__" if init else ATTRS_INIT_NAME) + if order: _add_order(ctx, adder) if frozen: _make_frozen(ctx, attributes) + elif not hashable: + _remove_hashability(ctx) return True @@ -943,6 +951,13 @@ def _add_match_args(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute add_attribute_to_class(api=ctx.api, cls=ctx.cls, name="__match_args__", typ=match_args) +def _remove_hashability(ctx: mypy.plugin.ClassDefContext) -> None: + """Remove hashability from a class.""" + add_attribute_to_class( + ctx.api, ctx.cls, "__hash__", NoneType(), is_classvar=True, overwrite_existing=True + ) + + class MethodAdder: """Helper to add methods to a TypeInfo. diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 03041bfcebcd..f0ff6f30a3b9 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -399,6 +399,7 @@ def add_attribute_to_class( override_allow_incompatible: bool = False, fullname: str | None = None, is_classvar: bool = False, + overwrite_existing: bool = False, ) -> Var: """ Adds a new attribute to a class definition. @@ -408,7 +409,7 @@ def add_attribute_to_class( # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. - if name in info.names: + if name in info.names and not overwrite_existing: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index b2161b91e225..0f379724553a 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2321,3 +2321,62 @@ reveal_type(b.x) # N: Revealed type is "builtins.int" reveal_type(b.y) # N: Revealed type is "builtins.int" [builtins fixtures/plugin_attrs.pyi] + +[case testDefaultHashability] +from attrs import define + +@define +class A: + a: int + +reveal_type(A.__hash__) # N: Revealed type is "None" + +[builtins fixtures/plugin_attrs.pyi] + +[case testFrozenHashability] +from attrs import frozen + +@frozen +class A: + a: int + +reveal_type(A.__hash__) # N: Revealed type is "def (self: builtins.object) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testManualHashHashability] +from attrs import define + +@define(hash=True) +class A: + a: int + +reveal_type(A.__hash__) # N: Revealed type is "def (self: builtins.object) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testManualUnsafeHashHashability] +from attrs import define + +@define(unsafe_hash=True) +class A: + a: int + +reveal_type(A.__hash__) # N: Revealed type is "def (self: builtins.object) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testSubclassingHashability] +from attrs import define + +@define(unsafe_hash=True) +class A: + a: int + +@define +class B(A): + pass + +reveal_type(B.__hash__) # N: Revealed type is "None" + +[builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/fixtures/plugin_attrs.pyi b/test-data/unit/fixtures/plugin_attrs.pyi index 57e5ecd1b2bc..5b87c47b5bc8 100644 --- a/test-data/unit/fixtures/plugin_attrs.pyi +++ b/test-data/unit/fixtures/plugin_attrs.pyi @@ -5,6 +5,7 @@ class object: def __init__(self) -> None: pass def __eq__(self, o: object) -> bool: pass def __ne__(self, o: object) -> bool: pass + def __hash__(self) -> int: ... class type: pass class bytes: pass diff --git a/test-data/unit/lib-stub/attr/__init__.pyi b/test-data/unit/lib-stub/attr/__init__.pyi index 24ffc0f3f275..466c6913062d 100644 --- a/test-data/unit/lib-stub/attr/__init__.pyi +++ b/test-data/unit/lib-stub/attr/__init__.pyi @@ -131,6 +131,7 @@ def define( *, these: Optional[Mapping[str, Any]] = ..., repr: bool = ..., + unsafe_hash: Optional[bool]=None, hash: Optional[bool] = ..., init: bool = ..., slots: bool = ..., @@ -153,6 +154,7 @@ def define( *, these: Optional[Mapping[str, Any]] = ..., repr: bool = ..., + unsafe_hash: Optional[bool]=None, hash: Optional[bool] = ..., init: bool = ..., slots: bool = ..., diff --git a/test-data/unit/lib-stub/attrs/__init__.pyi b/test-data/unit/lib-stub/attrs/__init__.pyi index f610957a48a3..d0a65c84d9d8 100644 --- a/test-data/unit/lib-stub/attrs/__init__.pyi +++ b/test-data/unit/lib-stub/attrs/__init__.pyi @@ -22,6 +22,7 @@ def define( *, these: Optional[Mapping[str, Any]] = ..., repr: bool = ..., + unsafe_hash: Optional[bool]=None, hash: Optional[bool] = ..., init: bool = ..., slots: bool = ..., @@ -44,6 +45,7 @@ def define( *, these: Optional[Mapping[str, Any]] = ..., repr: bool = ..., + unsafe_hash: Optional[bool]=None, hash: Optional[bool] = ..., init: bool = ..., slots: bool = ..., From 728e03a687c065bf82256675d53309e60290e108 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:29:46 -0800 Subject: [PATCH 0431/1617] Fix tests broken by hatchling (#16655) --- test-data/packages/typedpkg-stubs/pyproject.toml | 2 +- test-data/packages/typedpkg/pyproject.toml | 2 +- test-data/packages/typedpkg_ns_a/pyproject.toml | 2 +- test-data/packages/typedpkg_ns_b-stubs/pyproject.toml | 2 +- test-data/packages/typedpkg_ns_b/pyproject.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test-data/packages/typedpkg-stubs/pyproject.toml b/test-data/packages/typedpkg-stubs/pyproject.toml index 125816151ef8..c984c5d91e0a 100644 --- a/test-data/packages/typedpkg-stubs/pyproject.toml +++ b/test-data/packages/typedpkg-stubs/pyproject.toml @@ -7,5 +7,5 @@ description = 'test' include = ["**/*.pyi"] [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.18"] build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg/pyproject.toml b/test-data/packages/typedpkg/pyproject.toml index 5269c94320e1..6b55d4b3df60 100644 --- a/test-data/packages/typedpkg/pyproject.toml +++ b/test-data/packages/typedpkg/pyproject.toml @@ -4,5 +4,5 @@ version = '0.1' description = 'test' [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.18"] build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_a/pyproject.toml b/test-data/packages/typedpkg_ns_a/pyproject.toml index cc464af75b17..f41ad16b5bc2 100644 --- a/test-data/packages/typedpkg_ns_a/pyproject.toml +++ b/test-data/packages/typedpkg_ns_a/pyproject.toml @@ -7,5 +7,5 @@ description = 'test' include = ["**/*.py", "**/*.pyi", "**/py.typed"] [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.18"] build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml b/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml index d5275d1ed8b3..2c1c206c361d 100644 --- a/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml +++ b/test-data/packages/typedpkg_ns_b-stubs/pyproject.toml @@ -7,5 +7,5 @@ description = 'test' include = ["**/*.pyi"] [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.18"] build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_b/pyproject.toml b/test-data/packages/typedpkg_ns_b/pyproject.toml index 8567af11152e..b8ae0d59072e 100644 --- a/test-data/packages/typedpkg_ns_b/pyproject.toml +++ b/test-data/packages/typedpkg_ns_b/pyproject.toml @@ -4,5 +4,5 @@ version = '0.1' description = 'test' [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.18"] build-backend = "hatchling.build" From 6acccd13dc4e5a630b8cd95061f2bc352e4dbac6 Mon Sep 17 00:00:00 2001 From: Dheeraj <44266862+dheerajreal@users.noreply.github.com> Date: Wed, 13 Dec 2023 05:00:57 +0530 Subject: [PATCH 0432/1617] Update `project_urls` in `setup.py` (#16622) Made the following changes to `project_urls` in `setup.py`: - removed link to "News" - added link to "Changelog" - added link to "Issues" --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e3ebe9dd62ec..140020b18bc4 100644 --- a/setup.py +++ b/setup.py @@ -234,8 +234,9 @@ def run(self): python_requires=">=3.8", include_package_data=True, project_urls={ - "News": "https://mypy-lang.org/news.html", "Documentation": "https://mypy.readthedocs.io/en/stable/index.html", "Repository": "https://github.com/python/mypy", + "Changelog": "https://github.com/python/mypy/blob/master/CHANGELOG.md", + "Issues": "https://github.com/python/mypy/issues", }, ) From 09cb2d17cd16003937835b78bde9a6dbe627204c Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 13 Dec 2023 02:35:45 +0300 Subject: [PATCH 0433/1617] Remove `_is_compatible_stub_package` check from `modulefinder` (#16633) Closes https://github.com/python/mypy/issues/16632 --- mypy/modulefinder.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index c36a382848bf..455aa40e5975 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,18 +13,11 @@ import subprocess import sys from enum import Enum, unique - -from mypy.errors import CompileError - -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo +from mypy.errors import CompileError from mypy.fscache import FileSystemCache from mypy.nodes import MypyFile from mypy.options import Options @@ -426,7 +419,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: for pkg_dir in self.search_paths.package_path: stub_name = components[0] + "-stubs" stub_dir = os.path.join(pkg_dir, stub_name) - if fscache.isdir(stub_dir) and self._is_compatible_stub_package(stub_dir): + if fscache.isdir(stub_dir): stub_typed_file = os.path.join(stub_dir, "py.typed") stub_components = [stub_name] + components[1:] path = os.path.join(pkg_dir, *stub_components[:-1]) @@ -562,19 +555,6 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: else: return ModuleNotFoundReason.NOT_FOUND - def _is_compatible_stub_package(self, stub_dir: str) -> bool: - """Does a stub package support the target Python version? - - Stub packages may contain a metadata file which specifies - whether the stubs are compatible with Python 2 and 3. - """ - metadata_fnam = os.path.join(stub_dir, "METADATA.toml") - if not os.path.isfile(metadata_fnam): - return True - with open(metadata_fnam, "rb") as f: - metadata = tomllib.load(f) - return bool(metadata.get("python3", True)) - def find_modules_recursive(self, module: str) -> list[BuildSource]: module_path = self.find_module(module) if isinstance(module_path, ModuleNotFoundReason): From 5e77c3e96d3f3720d177c74acff07cc3319e8684 Mon Sep 17 00:00:00 2001 From: Froger David Date: Wed, 13 Dec 2023 22:28:12 +0100 Subject: [PATCH 0434/1617] Document --enable-incomplete-feature possible values in "mypy --help" (#16661) By the way, also remove `--enable-incomplete-feature=Unpack --enable-incomplete-feature=TypeVarTuple` when running the tests, as they are no more incomplete features. Fixes #14452 Co-authored-by: David Froger --- docs/source/command_line.rst | 2 +- mypy/main.py | 2 +- mypyc/test/test_run.py | 3 +-- test-data/unit/pythoneval.test | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 09836e2ffd20..c8e0ef3df11f 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -995,7 +995,7 @@ format into the specified directory. Enabling incomplete/experimental features ***************************************** -.. option:: --enable-incomplete-feature FEATURE +.. option:: --enable-incomplete-feature {PreciseTupleTypes} Some features may require several mypy releases to implement, for example due to their complexity, potential for backwards incompatibility, or diff --git a/mypy/main.py b/mypy/main.py index 8a35c2056963..ee090a03d3cf 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1010,7 +1010,7 @@ def add_invertible_flag( parser.add_argument( "--enable-incomplete-feature", action="append", - metavar="FEATURE", + metavar="{" + ",".join(sorted(INCOMPLETE_FEATURES)) + "}", help="Enable support of incomplete/experimental features for early preview", ) internals_group.add_argument( diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index f5c902bf3b3d..467ef8b87a92 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -15,7 +15,7 @@ from mypy import build from mypy.errors import CompileError -from mypy.options import TYPE_VAR_TUPLE, UNPACK, Options +from mypy.options import Options from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase from mypy.test.helpers import assert_module_equivalence, perform_file_operations @@ -194,7 +194,6 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> options.preserve_asts = True options.allow_empty_bodies = True options.incremental = self.separate - options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK] # Avoid checking modules/packages named 'unchecked', to provide a way # to test interacting with code we don't have types for. diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index c6ca71f5d56a..9f6f5d3838a4 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2007,7 +2007,7 @@ _testInferenceOfDunderDictOnClassObjects.py:4: error: Property "__dict__" define _testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[Never, Never]", variable has type "MappingProxyType[str, Any]") [case testTypeVarTuple] -# flags: --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack --python-version=3.11 +# flags: --python-version=3.11 from typing import Any, Callable, Unpack, TypeVarTuple Ts = TypeVarTuple("Ts") @@ -2019,7 +2019,6 @@ def call(callback: Callable[[Unpack[Ts]], Any], *args: Unpack[Ts]) -> Any: ... [case testTypeVarTupleTypingExtensions] -# flags: --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack from typing_extensions import Unpack, TypeVarTuple from typing import Any, Callable From 43ffb49102e2508b5e4767d200872060ddc9f0fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 19:32:04 -0800 Subject: [PATCH 0435/1617] Sync typeshed (#16665) Source commit: https://github.com/python/typeshed/commit/b6740d0bf4b4cf89befae74a0b12ebef6ce7bda6 --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_ctypes.pyi | 4 +- mypy/typeshed/stdlib/_operator.pyi | 21 ++-- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 9 ++ mypy/typeshed/stdlib/asyncio/base_events.pyi | 23 ++-- mypy/typeshed/stdlib/asyncio/events.pyi | 31 +++--- mypy/typeshed/stdlib/asyncio/exceptions.pyi | 7 +- mypy/typeshed/stdlib/asyncio/locks.pyi | 12 ++- mypy/typeshed/stdlib/asyncio/queues.pyi | 9 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 25 ++--- mypy/typeshed/stdlib/builtins.pyi | 2 +- .../stdlib/concurrent/futures/_base.pyi | 6 +- mypy/typeshed/stdlib/ctypes/wintypes.pyi | 102 +++++++++--------- mypy/typeshed/stdlib/dis.pyi | 7 +- mypy/typeshed/stdlib/email/_policybase.pyi | 51 +++++++++ mypy/typeshed/stdlib/email/feedparser.pyi | 5 +- mypy/typeshed/stdlib/email/message.pyi | 57 ++++++++-- mypy/typeshed/stdlib/email/policy.pyi | 46 +------- mypy/typeshed/stdlib/importlib/_abc.pyi | 15 +++ mypy/typeshed/stdlib/importlib/abc.pyi | 36 ++++--- .../stdlib/importlib/metadata/__init__.pyi | 75 +++++++++++-- mypy/typeshed/stdlib/io.pyi | 3 + mypy/typeshed/stdlib/lzma.pyi | 4 +- mypy/typeshed/stdlib/os/__init__.pyi | 10 +- mypy/typeshed/stdlib/pstats.pyi | 5 +- mypy/typeshed/stdlib/pyclbr.pyi | 56 +++++----- mypy/typeshed/stdlib/socketserver.pyi | 4 +- mypy/typeshed/stdlib/sre_parse.pyi | 3 + mypy/typeshed/stdlib/tkinter/__init__.pyi | 13 +-- mypy/typeshed/stdlib/trace.pyi | 24 ++++- mypy/typeshed/stdlib/unittest/case.pyi | 2 + mypy/typeshed/stdlib/urllib/response.pyi | 22 +--- mypy/typeshed/stdlib/xml/sax/__init__.pyi | 28 ++--- mypy/typeshed/stdlib/xml/sax/_exceptions.pyi | 19 ++++ mypy/typeshed/stdlib/xxlimited.pyi | 6 +- 35 files changed, 464 insertions(+), 279 deletions(-) create mode 100644 mypy/typeshed/stdlib/email/_policybase.pyi create mode 100644 mypy/typeshed/stdlib/importlib/_abc.pyi create mode 100644 mypy/typeshed/stdlib/xml/sax/_exceptions.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index d24e85c8fe44..f5e30c4602b4 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -150,6 +150,7 @@ imaplib: 2.7- imghdr: 2.7- imp: 2.7-3.11 importlib: 2.7- +importlib._abc: 3.10- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- importlib.readers: 3.10- diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 8a891971e9f1..0fa041844028 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -51,8 +51,8 @@ class _CDataMeta(type): # By default mypy complains about the following two methods, because strictly speaking cls # might not be a Type[_CT]. However this can never actually happen, because the only class that # uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here. - def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] class _CData(metaclass=_CDataMeta): _b_base_: int diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index e7d1a98c4027..26e69f130728 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -2,14 +2,17 @@ import sys from _typeshed import SupportsGetItem from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, TypeVar, overload -from typing_extensions import ParamSpec, SupportsIndex, TypeAlias, final +from typing_extensions import ParamSpec, SupportsIndex, TypeAlias, TypeVarTuple, Unpack, final _R = TypeVar("_R") _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") _K = TypeVar("_K") _V = TypeVar("_V") _P = ParamSpec("_P") +_Ts = TypeVarTuple("_Ts") # The following protocols return "Any" instead of bool, since the comparison # operators can be overloaded to return an arbitrary object. For example, @@ -105,22 +108,10 @@ class attrgetter(Generic[_T_co]): @final class itemgetter(Generic[_T_co]): - # mypy lacks support for PEP 646 https://github.com/python/mypy/issues/12280 - # So we have to define all of these overloads to simulate unpacking the arguments @overload - def __new__(cls, item: _T_co) -> itemgetter[_T_co]: ... + def __new__(cls, __item: _T) -> itemgetter[_T]: ... @overload - def __new__(cls, item: _T_co, __item2: _T_co) -> itemgetter[tuple[_T_co, _T_co]]: ... - @overload - def __new__(cls, item: _T_co, __item2: _T_co, __item3: _T_co) -> itemgetter[tuple[_T_co, _T_co, _T_co]]: ... - @overload - def __new__( - cls, item: _T_co, __item2: _T_co, __item3: _T_co, __item4: _T_co - ) -> itemgetter[tuple[_T_co, _T_co, _T_co, _T_co]]: ... - @overload - def __new__( - cls, item: _T_co, __item2: _T_co, __item3: _T_co, __item4: _T_co, *items: _T_co - ) -> itemgetter[tuple[_T_co, ...]]: ... + def __new__(cls, __item1: _T1, __item2: _T2, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: # TypeVar "_KT_contra@SupportsGetItem" is contravariant # "tuple[int, int]" is incompatible with protocol "SupportsIndex" diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 33659cf31a12..05892c8aab11 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -320,3 +320,12 @@ class DataclassInstance(Protocol): # Anything that can be passed to the int/float constructors ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc ConvertibleToFloat: TypeAlias = str | ReadableBuffer | SupportsFloat | SupportsIndex + +# A few classes updated from Foo(str, Enum) to Foo(StrEnum). This is a convenience so these +# can be accurate on all python versions without getting too wordy +if sys.version_info >= (3, 11): + from enum import StrEnum as StrEnum +else: + from enum import Enum + + class StrEnum(str, Enum): ... diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index afddcd918584..ff6c42c3645a 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -11,7 +11,7 @@ from collections.abc import Callable, Iterable, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing_extensions import Literal, TypeAlias, TypeVarTuple, Unpack if sys.version_info >= (3, 9): __all__ = ("BaseEventLoop", "Server") @@ -19,6 +19,7 @@ else: __all__ = ("BaseEventLoop",) _T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts") _ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol) _Context: TypeAlias = dict[str, Any] _ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], object] @@ -71,12 +72,14 @@ class BaseEventLoop(AbstractEventLoop): def close(self) -> None: ... async def shutdown_asyncgens(self) -> None: ... # Methods scheduling callbacks. All these return Handles. - def call_soon(self, callback: Callable[..., object], *args: Any, context: Context | None = None) -> Handle: ... + def call_soon( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... def call_later( - self, delay: float, callback: Callable[..., object], *args: Any, context: Context | None = None + self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> TimerHandle: ... def call_at( - self, when: float, callback: Callable[..., object], *args: Any, context: Context | None = None + self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> TimerHandle: ... def time(self) -> float: ... # Future methods @@ -92,8 +95,10 @@ class BaseEventLoop(AbstractEventLoop): def set_task_factory(self, factory: _TaskFactory | None) -> None: ... def get_task_factory(self) -> _TaskFactory | None: ... # Methods for interacting with threads - def call_soon_threadsafe(self, callback: Callable[..., object], *args: Any, context: Context | None = None) -> Handle: ... - def run_in_executor(self, executor: Any, func: Callable[..., _T], *args: Any) -> Future[_T]: ... + def call_soon_threadsafe( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... + def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... def set_default_executor(self, executor: Any) -> None: ... # Network I/O methods returning Futures. async def getaddrinfo( @@ -441,9 +446,9 @@ class BaseEventLoop(AbstractEventLoop): errors: None = None, **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... - def add_reader(self, fd: FileDescriptorLike, callback: Callable[..., Any], *args: Any) -> None: ... + def add_reader(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... def remove_reader(self, fd: FileDescriptorLike) -> bool: ... - def add_writer(self, fd: FileDescriptorLike, callback: Callable[..., Any], *args: Any) -> None: ... + def add_writer(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... def remove_writer(self, fd: FileDescriptorLike) -> bool: ... # The sock_* methods (and probably some others) are not actually implemented on # BaseEventLoop, only on subclasses. We list them here for now for convenience. @@ -457,7 +462,7 @@ class BaseEventLoop(AbstractEventLoop): async def sock_recvfrom_into(self, sock: socket, buf: WriteableBuffer, nbytes: int = 0) -> tuple[int, _RetAddress]: ... async def sock_sendto(self, sock: socket, data: ReadableBuffer, address: _Address) -> int: ... # Signal handling. - def add_signal_handler(self, sig: int, callback: Callable[..., Any], *args: Any) -> None: ... + def add_signal_handler(self, sig: int, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... def remove_signal_handler(self, sig: int) -> bool: ... # Error handlers. def set_exception_handler(self, handler: _ExceptionHandler | None) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 87e7edb461ac..0f51c457fc24 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Coroutine, Generator, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, deprecated +from typing_extensions import Literal, Self, TypeAlias, TypeVarTuple, Unpack, deprecated from . import _AwaitableLike, _CoroutineLike from .base_events import Server @@ -56,6 +56,7 @@ else: ) _T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts") _ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol) _Context: TypeAlias = dict[str, Any] _ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], object] @@ -131,22 +132,24 @@ class AbstractEventLoop: # Methods scheduling callbacks. All these return Handles. if sys.version_info >= (3, 9): # "context" added in 3.9.10/3.10.2 @abstractmethod - def call_soon(self, callback: Callable[..., object], *args: Any, context: Context | None = None) -> Handle: ... + def call_soon( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... @abstractmethod def call_later( - self, delay: float, callback: Callable[..., object], *args: Any, context: Context | None = None + self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> TimerHandle: ... @abstractmethod def call_at( - self, when: float, callback: Callable[..., object], *args: Any, context: Context | None = None + self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> TimerHandle: ... else: @abstractmethod - def call_soon(self, callback: Callable[..., object], *args: Any) -> Handle: ... + def call_soon(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ... @abstractmethod - def call_later(self, delay: float, callback: Callable[..., object], *args: Any) -> TimerHandle: ... + def call_later(self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ... @abstractmethod - def call_at(self, when: float, callback: Callable[..., object], *args: Any) -> TimerHandle: ... + def call_at(self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ... @abstractmethod def time(self) -> float: ... @@ -173,13 +176,15 @@ class AbstractEventLoop: # Methods for interacting with threads if sys.version_info >= (3, 9): # "context" added in 3.9.10/3.10.2 @abstractmethod - def call_soon_threadsafe(self, callback: Callable[..., object], *args: Any, context: Context | None = None) -> Handle: ... + def call_soon_threadsafe( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... else: @abstractmethod - def call_soon_threadsafe(self, callback: Callable[..., object], *args: Any) -> Handle: ... + def call_soon_threadsafe(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ... @abstractmethod - def run_in_executor(self, executor: Any, func: Callable[..., _T], *args: Any) -> Future[_T]: ... + def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... @abstractmethod def set_default_executor(self, executor: Any) -> None: ... # Network I/O methods returning Futures. @@ -542,11 +547,11 @@ class AbstractEventLoop: **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... @abstractmethod - def add_reader(self, fd: FileDescriptorLike, callback: Callable[..., Any], *args: Any) -> None: ... + def add_reader(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_reader(self, fd: FileDescriptorLike) -> bool: ... @abstractmethod - def add_writer(self, fd: FileDescriptorLike, callback: Callable[..., Any], *args: Any) -> None: ... + def add_writer(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_writer(self, fd: FileDescriptorLike) -> bool: ... # Completion based I/O methods returning Futures prior to 3.7 @@ -569,7 +574,7 @@ class AbstractEventLoop: async def sock_sendto(self, sock: socket, data: ReadableBuffer, address: _Address) -> int: ... # Signal handling. @abstractmethod - def add_signal_handler(self, sig: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_signal_handler(self, sig: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_signal_handler(self, sig: int) -> bool: ... # Error handlers. diff --git a/mypy/typeshed/stdlib/asyncio/exceptions.pyi b/mypy/typeshed/stdlib/asyncio/exceptions.pyi index 075fbb805bb9..0746394d582f 100644 --- a/mypy/typeshed/stdlib/asyncio/exceptions.pyi +++ b/mypy/typeshed/stdlib/asyncio/exceptions.pyi @@ -21,7 +21,12 @@ else: ) class CancelledError(BaseException): ... -class TimeoutError(Exception): ... + +if sys.version_info >= (3, 11): + from builtins import TimeoutError as TimeoutError +else: + class TimeoutError(Exception): ... + class InvalidStateError(Exception): ... class SendfileNotAvailableError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index ab4e63ab59b1..394b82a5b3d9 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -10,8 +10,10 @@ from typing_extensions import Literal, Self from .events import AbstractEventLoop from .futures import Future -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 10): from .mixins import _LoopBoundMixin +else: + _LoopBoundMixin = object if sys.version_info >= (3, 11): __all__ = ("Lock", "Event", "Condition", "Semaphore", "BoundedSemaphore", "Barrier") @@ -44,7 +46,7 @@ else: self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None ) -> None: ... -class Lock(_ContextManagerMixin): +class Lock(_ContextManagerMixin, _LoopBoundMixin): if sys.version_info >= (3, 10): def __init__(self) -> None: ... else: @@ -54,7 +56,7 @@ class Lock(_ContextManagerMixin): async def acquire(self) -> Literal[True]: ... def release(self) -> None: ... -class Event: +class Event(_LoopBoundMixin): if sys.version_info >= (3, 10): def __init__(self) -> None: ... else: @@ -65,7 +67,7 @@ class Event: def clear(self) -> None: ... async def wait(self) -> Literal[True]: ... -class Condition(_ContextManagerMixin): +class Condition(_ContextManagerMixin, _LoopBoundMixin): if sys.version_info >= (3, 10): def __init__(self, lock: Lock | None = None) -> None: ... else: @@ -79,7 +81,7 @@ class Condition(_ContextManagerMixin): def notify(self, n: int = 1) -> None: ... def notify_all(self) -> None: ... -class Semaphore(_ContextManagerMixin): +class Semaphore(_ContextManagerMixin, _LoopBoundMixin): _value: int _waiters: deque[Future[Any]] if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index f56a09524e71..bb4ee71f9267 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -5,6 +5,11 @@ from typing import Any, Generic, TypeVar if sys.version_info >= (3, 9): from types import GenericAlias +if sys.version_info >= (3, 10): + from .mixins import _LoopBoundMixin +else: + _LoopBoundMixin = object + __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty") class QueueEmpty(Exception): ... @@ -12,7 +17,9 @@ class QueueFull(Exception): ... _T = TypeVar("_T") -class Queue(Generic[_T]): +# If Generic[_T] is last and _LoopBoundMixin is object, pyright is unhappy. +# We can remove the noqa pragma when dropping 3.9 support. +class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 if sys.version_info >= (3, 10): def __init__(self, maxsize: int = 0) -> None: ... else: diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index d440206aa0b9..ee16035f86a8 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -2,12 +2,13 @@ import sys import types from abc import ABCMeta, abstractmethod from collections.abc import Callable -from typing import Any -from typing_extensions import Literal, Self, deprecated +from typing_extensions import Literal, Self, TypeVarTuple, Unpack, deprecated from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop +_Ts = TypeVarTuple("_Ts") + # This is also technically not available on Win, # but other parts of typeshed need this definition. # So, it is special cased. @@ -15,7 +16,7 @@ if sys.version_info >= (3, 12): @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") class AbstractChildWatcher: @abstractmethod - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod @@ -35,7 +36,7 @@ if sys.version_info >= (3, 12): else: class AbstractChildWatcher: @abstractmethod - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod @@ -91,26 +92,26 @@ if sys.platform != "win32": class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") class FastChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... else: class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... class FastChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... @@ -137,7 +138,7 @@ if sys.platform != "win32": def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... elif sys.version_info >= (3, 8): @@ -148,7 +149,7 @@ if sys.platform != "win32": def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... @@ -161,7 +162,7 @@ if sys.platform != "win32": self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... def __del__(self) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... @@ -174,5 +175,5 @@ if sys.platform != "win32": def is_active(self) -> bool: ... def close(self) -> None: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[..., object], *args: Any) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index e3d7ee7e5cc1..dca3c2160b5a 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -202,7 +202,7 @@ class type: def __instancecheck__(self, __instance: Any) -> bool: ... def __subclasscheck__(self, __subclass: type) -> bool: ... @classmethod - def __prepare__(metacls, __name: str, __bases: tuple[type, ...], **kwds: Any) -> Mapping[str, object]: ... + def __prepare__(metacls, __name: str, __bases: tuple[type, ...], **kwds: Any) -> MutableMapping[str, object]: ... if sys.version_info >= (3, 10): def __or__(self, __value: Any) -> types.UnionType: ... def __ror__(self, __value: Any) -> types.UnionType: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index eb5ca4e2dd35..8a11f47e5670 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -24,7 +24,11 @@ LOGGER: Logger class Error(Exception): ... class CancelledError(Error): ... -class TimeoutError(Error): ... + +if sys.version_info >= (3, 11): + from builtins import TimeoutError as TimeoutError +else: + class TimeoutError(Error): ... if sys.version_info >= (3, 8): class InvalidStateError(Error): ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 59c7ae3e599f..84c9c9157a88 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -186,55 +186,53 @@ class WIN32_FIND_DATAW(Structure): cFileName: _CField[Array[WCHAR], str, str] cAlternateFileName: _CField[Array[WCHAR], str, str] -# These pointer type definitions use _Pointer[...] instead of POINTER(...), to allow them -# to be used in type annotations. -PBOOL: TypeAlias = _Pointer[BOOL] -LPBOOL: TypeAlias = _Pointer[BOOL] -PBOOLEAN: TypeAlias = _Pointer[BOOLEAN] -PBYTE: TypeAlias = _Pointer[BYTE] -LPBYTE: TypeAlias = _Pointer[BYTE] -PCHAR: TypeAlias = _Pointer[CHAR] -LPCOLORREF: TypeAlias = _Pointer[COLORREF] -PDWORD: TypeAlias = _Pointer[DWORD] -LPDWORD: TypeAlias = _Pointer[DWORD] -PFILETIME: TypeAlias = _Pointer[FILETIME] -LPFILETIME: TypeAlias = _Pointer[FILETIME] -PFLOAT: TypeAlias = _Pointer[FLOAT] -PHANDLE: TypeAlias = _Pointer[HANDLE] -LPHANDLE: TypeAlias = _Pointer[HANDLE] -PHKEY: TypeAlias = _Pointer[HKEY] -LPHKL: TypeAlias = _Pointer[HKL] -PINT: TypeAlias = _Pointer[INT] -LPINT: TypeAlias = _Pointer[INT] -PLARGE_INTEGER: TypeAlias = _Pointer[LARGE_INTEGER] -PLCID: TypeAlias = _Pointer[LCID] -PLONG: TypeAlias = _Pointer[LONG] -LPLONG: TypeAlias = _Pointer[LONG] -PMSG: TypeAlias = _Pointer[MSG] -LPMSG: TypeAlias = _Pointer[MSG] -PPOINT: TypeAlias = _Pointer[POINT] -LPPOINT: TypeAlias = _Pointer[POINT] -PPOINTL: TypeAlias = _Pointer[POINTL] -PRECT: TypeAlias = _Pointer[RECT] -LPRECT: TypeAlias = _Pointer[RECT] -PRECTL: TypeAlias = _Pointer[RECTL] -LPRECTL: TypeAlias = _Pointer[RECTL] -LPSC_HANDLE: TypeAlias = _Pointer[SC_HANDLE] -PSHORT: TypeAlias = _Pointer[SHORT] -PSIZE: TypeAlias = _Pointer[SIZE] -LPSIZE: TypeAlias = _Pointer[SIZE] -PSIZEL: TypeAlias = _Pointer[SIZEL] -LPSIZEL: TypeAlias = _Pointer[SIZEL] -PSMALL_RECT: TypeAlias = _Pointer[SMALL_RECT] -PUINT: TypeAlias = _Pointer[UINT] -LPUINT: TypeAlias = _Pointer[UINT] -PULARGE_INTEGER: TypeAlias = _Pointer[ULARGE_INTEGER] -PULONG: TypeAlias = _Pointer[ULONG] -PUSHORT: TypeAlias = _Pointer[USHORT] -PWCHAR: TypeAlias = _Pointer[WCHAR] -PWIN32_FIND_DATAA: TypeAlias = _Pointer[WIN32_FIND_DATAA] -LPWIN32_FIND_DATAA: TypeAlias = _Pointer[WIN32_FIND_DATAA] -PWIN32_FIND_DATAW: TypeAlias = _Pointer[WIN32_FIND_DATAW] -LPWIN32_FIND_DATAW: TypeAlias = _Pointer[WIN32_FIND_DATAW] -PWORD: TypeAlias = _Pointer[WORD] -LPWORD: TypeAlias = _Pointer[WORD] +class PBOOL(_Pointer[BOOL]): ... +class LPBOOL(_Pointer[BOOL]): ... +class PBOOLEAN(_Pointer[BOOLEAN]): ... +class PBYTE(_Pointer[BYTE]): ... +class LPBYTE(_Pointer[BYTE]): ... +class PCHAR(_Pointer[CHAR]): ... +class LPCOLORREF(_Pointer[COLORREF]): ... +class PDWORD(_Pointer[DWORD]): ... +class LPDWORD(_Pointer[DWORD]): ... +class PFILETIME(_Pointer[FILETIME]): ... +class LPFILETIME(_Pointer[FILETIME]): ... +class PFLOAT(_Pointer[FLOAT]): ... +class PHANDLE(_Pointer[HANDLE]): ... +class LPHANDLE(_Pointer[HANDLE]): ... +class PHKEY(_Pointer[HKEY]): ... +class LPHKL(_Pointer[HKL]): ... +class PINT(_Pointer[INT]): ... +class LPINT(_Pointer[INT]): ... +class PLARGE_INTEGER(_Pointer[LARGE_INTEGER]): ... +class PLCID(_Pointer[LCID]): ... +class PLONG(_Pointer[LONG]): ... +class LPLONG(_Pointer[LONG]): ... +class PMSG(_Pointer[MSG]): ... +class LPMSG(_Pointer[MSG]): ... +class PPOINT(_Pointer[POINT]): ... +class LPPOINT(_Pointer[POINT]): ... +class PPOINTL(_Pointer[POINTL]): ... +class PRECT(_Pointer[RECT]): ... +class LPRECT(_Pointer[RECT]): ... +class PRECTL(_Pointer[RECTL]): ... +class LPRECTL(_Pointer[RECTL]): ... +class LPSC_HANDLE(_Pointer[SC_HANDLE]): ... +class PSHORT(_Pointer[SHORT]): ... +class PSIZE(_Pointer[SIZE]): ... +class LPSIZE(_Pointer[SIZE]): ... +class PSIZEL(_Pointer[SIZEL]): ... +class LPSIZEL(_Pointer[SIZEL]): ... +class PSMALL_RECT(_Pointer[SMALL_RECT]): ... +class PUINT(_Pointer[UINT]): ... +class LPUINT(_Pointer[UINT]): ... +class PULARGE_INTEGER(_Pointer[ULARGE_INTEGER]): ... +class PULONG(_Pointer[ULONG]): ... +class PUSHORT(_Pointer[USHORT]): ... +class PWCHAR(_Pointer[WCHAR]): ... +class PWIN32_FIND_DATAA(_Pointer[WIN32_FIND_DATAA]): ... +class LPWIN32_FIND_DATAA(_Pointer[WIN32_FIND_DATAA]): ... +class PWIN32_FIND_DATAW(_Pointer[WIN32_FIND_DATAW]): ... +class LPWIN32_FIND_DATAW(_Pointer[WIN32_FIND_DATAW]): ... +class PWORD(_Pointer[WORD]): ... +class LPWORD(_Pointer[WORD]): ... diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index ab101a517a6f..796d81d8bf70 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -48,7 +48,7 @@ if sys.version_info >= (3, 11): end_col_offset: int | None = None if sys.version_info >= (3, 11): - class Instruction(NamedTuple): + class _Instruction(NamedTuple): opname: str opcode: int arg: int | None @@ -60,7 +60,7 @@ if sys.version_info >= (3, 11): positions: Positions | None = None else: - class Instruction(NamedTuple): + class _Instruction(NamedTuple): opname: str opcode: int arg: int | None @@ -70,6 +70,9 @@ else: starts_line: int | None is_jump_target: bool +class Instruction(_Instruction): + def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ... + class Bytecode: codeobj: types.CodeType first_line: int diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi new file mode 100644 index 000000000000..a3dd61a282ce --- /dev/null +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -0,0 +1,51 @@ +from abc import ABCMeta, abstractmethod +from collections.abc import Callable +from email.errors import MessageDefect +from email.header import Header +from email.message import Message +from typing import Any +from typing_extensions import Self + +class _PolicyBase: + def __add__(self, other: Any) -> Self: ... + def clone(self, **kw: Any) -> Self: ... + +class Policy(_PolicyBase, metaclass=ABCMeta): + max_line_length: int | None + linesep: str + cte_type: str + raise_on_defect: bool + mangle_from_: bool + message_factory: Callable[[Policy], Message] | None + def __init__( + self, + *, + max_line_length: int | None = 78, + linesep: str = "\n", + cte_type: str = "8bit", + raise_on_defect: bool = False, + mangle_from_: bool = False, + message_factory: Callable[[Policy], Message] | None = None, + ) -> None: ... + def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... + def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... + def header_max_count(self, name: str) -> int | None: ... + @abstractmethod + def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... + @abstractmethod + def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... + @abstractmethod + def header_fetch_parse(self, name: str, value: str) -> str: ... + @abstractmethod + def fold(self, name: str, value: str) -> str: ... + @abstractmethod + def fold_binary(self, name: str, value: str) -> bytes: ... + +class Compat32(Policy): + def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... + def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... + def header_fetch_parse(self, name: str, value: str) -> str | Header: ... # type: ignore[override] + def fold(self, name: str, value: str) -> str: ... + def fold_binary(self, name: str, value: str) -> bytes: ... + +compat32: Compat32 diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi index 4b7f73b9c015..22920fc99c69 100644 --- a/mypy/typeshed/stdlib/email/feedparser.pyi +++ b/mypy/typeshed/stdlib/email/feedparser.pyi @@ -15,10 +15,9 @@ class FeedParser(Generic[_MessageT]): def feed(self, data: str) -> None: ... def close(self) -> _MessageT: ... -class BytesFeedParser(Generic[_MessageT]): +class BytesFeedParser(FeedParser[_MessageT]): @overload def __init__(self: BytesFeedParser[Message], _factory: None = None, *, policy: Policy = ...) -> None: ... @overload def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... - def feed(self, data: bytes | bytearray) -> None: ... - def close(self) -> _MessageT: ... + def feed(self, data: bytes | bytearray) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 18852f4d3bb2..71c8da3a6a5a 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -3,17 +3,27 @@ from email import _ParamsType, _ParamType from email.charset import Charset from email.contentmanager import ContentManager from email.errors import MessageDefect +from email.header import Header from email.policy import Policy -from typing import Any, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing import Any, Protocol, TypeVar, overload +from typing_extensions import Literal, Self, TypeAlias __all__ = ["Message", "EmailMessage"] _T = TypeVar("_T") - -_PayloadType: TypeAlias = list[Message] | str | bytes | bytearray +_PayloadType: TypeAlias = Message | str +_EncodedPayloadType: TypeAlias = Message | bytes +_MultipartPayloadType: TypeAlias = list[_PayloadType] _CharsetType: TypeAlias = Charset | str | None +# Type returned by Policy.header_fetch_parse, AnyOf[str | Header] _HeaderType: TypeAlias = Any +_HeaderTypeParam: TypeAlias = str | Header + +class _SupportsEncodeToPayload(Protocol): + def encode(self, __encoding: str) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ... + +class _SupportsDecodeToPayload(Protocol): + def decode(self, __encoding: str, __errors: str) -> _PayloadType | _MultipartPayloadType: ... class Message: policy: Policy # undocumented @@ -23,16 +33,43 @@ class Message: def is_multipart(self) -> bool: ... def set_unixfrom(self, unixfrom: str) -> None: ... def get_unixfrom(self) -> str | None: ... - def attach(self, payload: Message) -> None: ... - def get_payload(self, i: int | None = None, decode: bool = False) -> Any: ... # returns _PayloadType | None - def set_payload(self, payload: _PayloadType, charset: _CharsetType = None) -> None: ... + def attach(self, payload: _PayloadType) -> None: ... + # `i: int` without a multipart payload results in an error + # `| Any`: can be None for cleared or unset payload, but annoying to check + @overload # multipart + def get_payload(self, i: int, decode: Literal[True]) -> None: ... + @overload # multipart + def get_payload(self, i: int, decode: Literal[False] = False) -> _PayloadType | Any: ... + @overload # either + def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | Any: ... + @overload # not multipart + def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | Any: ... + @overload # not multipart, IDEM but w/o kwarg + def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | Any: ... + # If `charset=None` and payload supports both `encode` AND `decode`, then an invalid payload could be passed, but this is unlikely + # Not[_SupportsEncodeToPayload] + @overload + def set_payload( + self, payload: _SupportsDecodeToPayload | _PayloadType | _MultipartPayloadType, charset: None = None + ) -> None: ... + @overload + def set_payload( + self, + payload: _SupportsEncodeToPayload | _SupportsDecodeToPayload | _PayloadType | _MultipartPayloadType, + charset: Charset | str, + ) -> None: ... def set_charset(self, charset: _CharsetType) -> None: ... def get_charset(self) -> _CharsetType: ... def __len__(self) -> int: ... def __contains__(self, name: str) -> bool: ... def __iter__(self) -> Iterator[str]: ... + # Same as `get` with `failobj=None`, but with the expectation that it won't return None in most scenarios + # This is important for protocols using __getitem__, like SupportsKeysAndGetItem + # Morally, the return type should be `AnyOf[_HeaderType, None]`, + # which we could spell as `_HeaderType | Any`, + # *but* `_HeaderType` itself is currently an alias to `Any`... def __getitem__(self, name: str) -> _HeaderType: ... - def __setitem__(self, name: str, val: _HeaderType) -> None: ... + def __setitem__(self, name: str, val: _HeaderTypeParam) -> None: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> list[str]: ... def values(self) -> list[_HeaderType]: ... @@ -46,7 +83,7 @@ class Message: @overload def get_all(self, name: str, failobj: _T) -> list[_HeaderType] | _T: ... def add_header(self, _name: str, _value: str, **_params: _ParamsType) -> None: ... - def replace_header(self, _name: str, _value: _HeaderType) -> None: ... + def replace_header(self, _name: str, _value: _HeaderTypeParam) -> None: ... def get_content_type(self) -> str: ... def get_content_maintype(self) -> str: ... def get_content_subtype(self) -> str: ... @@ -100,7 +137,7 @@ class Message: ) -> None: ... def __init__(self, policy: Policy = ...) -> None: ... # The following two methods are undocumented, but a source code comment states that they are public API - def set_raw(self, name: str, value: _HeaderType) -> None: ... + def set_raw(self, name: str, value: _HeaderTypeParam) -> None: ... def raw_items(self) -> Iterator[tuple[str, _HeaderType]]: ... class MIMEPart(Message): diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index 804044031fcd..5f1cf934eb3c 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -1,55 +1,11 @@ -from abc import ABCMeta, abstractmethod from collections.abc import Callable +from email._policybase import Compat32 as Compat32, Policy as Policy, compat32 as compat32 from email.contentmanager import ContentManager -from email.errors import MessageDefect -from email.header import Header from email.message import Message from typing import Any -from typing_extensions import Self __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] -class Policy(metaclass=ABCMeta): - max_line_length: int | None - linesep: str - cte_type: str - raise_on_defect: bool - mangle_from_: bool - message_factory: Callable[[Policy], Message] | None - def __init__( - self, - *, - max_line_length: int | None = ..., - linesep: str = ..., - cte_type: str = ..., - raise_on_defect: bool = ..., - mangle_from_: bool = ..., - message_factory: Callable[[Policy], Message] | None = ..., - ) -> None: ... - def clone(self, **kw: Any) -> Self: ... - def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... - def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... - def header_max_count(self, name: str) -> int | None: ... - @abstractmethod - def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... - @abstractmethod - def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... - @abstractmethod - def header_fetch_parse(self, name: str, value: str) -> str: ... - @abstractmethod - def fold(self, name: str, value: str) -> str: ... - @abstractmethod - def fold_binary(self, name: str, value: str) -> bytes: ... - -class Compat32(Policy): - def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... - def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... - def header_fetch_parse(self, name: str, value: str) -> str | Header: ... # type: ignore[override] - def fold(self, name: str, value: str) -> str: ... - def fold_binary(self, name: str, value: str) -> bytes: ... - -compat32: Compat32 - class EmailPolicy(Policy): utf8: bool refold_source: str diff --git a/mypy/typeshed/stdlib/importlib/_abc.pyi b/mypy/typeshed/stdlib/importlib/_abc.pyi new file mode 100644 index 000000000000..1a21b9a72cd8 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/_abc.pyi @@ -0,0 +1,15 @@ +import sys +import types +from abc import ABCMeta +from importlib.machinery import ModuleSpec + +if sys.version_info >= (3, 10): + class Loader(metaclass=ABCMeta): + def load_module(self, fullname: str) -> types.ModuleType: ... + if sys.version_info < (3, 12): + def module_repr(self, module: types.ModuleType) -> str: ... + + def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... + # Not defined on the actual class for backwards-compatibility reasons, + # but expected in new code. + def exec_module(self, module: types.ModuleType) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 148e12ec7e3f..eb13240f84ff 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -24,18 +24,19 @@ if sys.version_info >= (3, 11): if sys.version_info < (3, 12): __all__ += ["Finder", "ResourceReader", "Traversable", "TraversableResources"] -if sys.version_info < (3, 12): - class Finder(metaclass=ABCMeta): ... - -class Loader(metaclass=ABCMeta): - def load_module(self, fullname: str) -> types.ModuleType: ... - if sys.version_info < (3, 12): +if sys.version_info >= (3, 10): + from importlib._abc import Loader as Loader +else: + class Loader(metaclass=ABCMeta): + def load_module(self, fullname: str) -> types.ModuleType: ... def module_repr(self, module: types.ModuleType) -> str: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... + # Not defined on the actual class for backwards-compatibility reasons, + # but expected in new code. + def exec_module(self, module: types.ModuleType) -> None: ... - def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... - # Not defined on the actual class for backwards-compatibility reasons, - # but expected in new code. - def exec_module(self, module: types.ModuleType) -> None: ... +if sys.version_info < (3, 12): + class Finder(metaclass=ABCMeta): ... class ResourceLoader(Loader): @abstractmethod @@ -62,10 +63,13 @@ class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): def get_source(self, fullname: str) -> str | None: ... def path_stats(self, path: str) -> Mapping[str, Any]: ... -# The base classes differ on 3.12: -if sys.version_info >= (3, 12): +# The base classes differ starting in 3.10: +if sys.version_info >= (3, 10): # Please keep in sync with sys._MetaPathFinder class MetaPathFinder(metaclass=ABCMeta): + if sys.version_info < (3, 12): + def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... + def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec( @@ -73,6 +77,10 @@ if sys.version_info >= (3, 12): ) -> ModuleSpec | None: ... class PathEntryFinder(metaclass=ABCMeta): + if sys.version_info < (3, 12): + def find_module(self, fullname: str) -> Loader | None: ... + def find_loader(self, fullname: str) -> tuple[Loader | None, Sequence[str]]: ... + def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... @@ -138,10 +146,10 @@ if sys.version_info >= (3, 9): # which is not the case. @overload @abstractmethod - def open(self, __mode: Literal["r", "w"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... + def open(self, __mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open(self, __mode: Literal["rb", "wb"]) -> IO[bytes]: ... + def open(self, __mode: Literal["rb"]) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index fd470b8f061d..a936eece1d3f 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -1,16 +1,21 @@ import abc import pathlib import sys +from _collections_abc import dict_keys, dict_values from _typeshed import StrPath -from collections.abc import Iterable, Mapping +from collections.abc import Iterable, Iterator, Mapping from email.message import Message from importlib.abc import MetaPathFinder from os import PathLike from pathlib import Path from re import Pattern -from typing import Any, ClassVar, NamedTuple, overload +from typing import Any, ClassVar, Generic, NamedTuple, TypeVar, overload from typing_extensions import Self +_T = TypeVar("_T") +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + __all__ = [ "Distribution", "DistributionFinder", @@ -35,14 +40,23 @@ class PackageNotFoundError(ModuleNotFoundError): @property def name(self) -> str: ... # type: ignore[override] -class _EntryPointBase(NamedTuple): - name: str - value: str - group: str +if sys.version_info >= (3, 11): + class DeprecatedTuple: + def __getitem__(self, item: int) -> str: ... + _EntryPointBase = DeprecatedTuple +else: + class _EntryPointBase(NamedTuple): + name: str + value: str + group: str class EntryPoint(_EntryPointBase): pattern: ClassVar[Pattern[str]] if sys.version_info >= (3, 11): + name: str + value: str + group: str + def __init__(self, name: str, value: str, group: str) -> None: ... def load(self) -> Any: ... # Callable[[], Any] or an importable module @@ -68,9 +82,33 @@ class EntryPoint(_EntryPointBase): def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... + if sys.version_info >= (3, 11): + def __lt__(self, other: object) -> bool: ... + if sys.version_info < (3, 12): + def __iter__(self) -> Iterator[Any]: ... # result of iter((str, Self)), really -if sys.version_info >= (3, 10): - class EntryPoints(list[EntryPoint]): # use as list is deprecated since 3.10 +if sys.version_info >= (3, 12): + class EntryPoints(tuple[EntryPoint, ...]): + def __getitem__(self, name: str) -> EntryPoint: ... # type: ignore[override] + def select( + self, + *, + name: str = ..., + value: str = ..., + group: str = ..., + module: str = ..., + attr: str = ..., + extras: list[str] = ..., + ) -> EntryPoints: ... + @property + def names(self) -> set[str]: ... + @property + def groups(self) -> set[str]: ... + +elif sys.version_info >= (3, 10): + class DeprecatedList(list[_T]): ... + + class EntryPoints(DeprecatedList[EntryPoint]): # use as list is deprecated since 3.10 # int argument is deprecated since 3.10 def __getitem__(self, name: int | str) -> EntryPoint: ... # type: ignore[override] def select( @@ -89,7 +127,18 @@ if sys.version_info >= (3, 10): def groups(self) -> set[str]: ... if sys.version_info >= (3, 10) and sys.version_info < (3, 12): - class SelectableGroups(dict[str, EntryPoints]): # use as dict is deprecated since 3.10 + class Deprecated(Generic[_KT, _VT]): + def __getitem__(self, name: _KT) -> _VT: ... + @overload + def get(self, name: _KT) -> _VT | None: ... + @overload + def get(self, name: _KT, default: _T) -> _VT | _T: ... + def __iter__(self) -> Iterator[_KT]: ... + def __contains__(self, *args: object) -> bool: ... + def keys(self) -> dict_keys[_KT, _VT]: ... + def values(self) -> dict_values[_KT, _VT]: ... + + class SelectableGroups(Deprecated[str, EntryPoints], dict[str, EntryPoints]): # use as dict is deprecated since 3.10 @classmethod def load(cls, eps: Iterable[EntryPoint]) -> Self: ... @property @@ -124,7 +173,13 @@ class FileHash: value: str def __init__(self, spec: str) -> None: ... -class Distribution: +if sys.version_info >= (3, 12): + class DeprecatedNonAbstract: ... + _distribution_parent = DeprecatedNonAbstract +else: + _distribution_parent = object + +class Distribution(_distribution_parent): @abc.abstractmethod def read_text(self, filename: str) -> str | None: ... @abc.abstractmethod diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 16270b948f35..ee4eda1b4eb0 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -33,6 +33,9 @@ __all__ = [ if sys.version_info >= (3, 8): __all__ += ["open_code"] +if sys.version_info >= (3, 11): + __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] + _T = TypeVar("_T") DEFAULT_BUFFER_SIZE: Literal[8192] diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index 8e296bb5b357..be61cac08139 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,4 +1,4 @@ -import io +from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Mapping, Sequence from typing import IO, Any, TextIO, overload @@ -104,7 +104,7 @@ class LZMACompressor: class LZMAError(Exception): ... -class LZMAFile(io.BufferedIOBase, IO[bytes]): # type: ignore[misc] # incompatible definitions of writelines in the base classes +class LZMAFile(BaseStream, IO[bytes]): # type: ignore[misc] # incompatible definitions of writelines in the base classes def __init__( self, filename: _PathOrFile | None = None, diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 45eaf2a66e80..7d4b8adcd00a 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -26,7 +26,7 @@ from contextlib import AbstractContextManager from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper as _TextIOWrapper from subprocess import Popen from typing import IO, Any, AnyStr, BinaryIO, Generic, NoReturn, Protocol, TypeVar, overload, runtime_checkable -from typing_extensions import Final, Literal, Self, TypeAlias, final +from typing_extensions import Final, Literal, Self, TypeAlias, Unpack, final from . import path as _path @@ -847,8 +847,12 @@ def execl(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: StrOrBytesPath) - def execlp(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: StrOrBytesPath) -> NoReturn: ... # These are: execle(file, *args, env) but env is pulled from the last element of the args. -def execle(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: Any) -> NoReturn: ... -def execlpe(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: Any) -> NoReturn: ... +def execle( + file: StrOrBytesPath, *args: Unpack[tuple[StrOrBytesPath, Unpack[tuple[StrOrBytesPath, ...]], _ExecEnv]] +) -> NoReturn: ... +def execlpe( + file: StrOrBytesPath, *args: Unpack[tuple[StrOrBytesPath, Unpack[tuple[StrOrBytesPath, ...]], _ExecEnv]] +) -> NoReturn: ... # The docs say `args: tuple or list of strings` # The implementation enforces tuple or list so we can't use Sequence. diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 5d25d1bb3641..44ce33469ff9 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -1,8 +1,7 @@ import sys -from _typeshed import StrOrBytesPath +from _typeshed import StrEnum, StrOrBytesPath from collections.abc import Iterable from cProfile import Profile as _cProfile -from enum import Enum from profile import Profile from typing import IO, Any, overload from typing_extensions import Literal, Self, TypeAlias @@ -14,7 +13,7 @@ else: _Selector: TypeAlias = str | float | int -class SortKey(str, Enum): +class SortKey(StrEnum): CALLS: str CUMULATIVE: str FILENAME: str diff --git a/mypy/typeshed/stdlib/pyclbr.pyi b/mypy/typeshed/stdlib/pyclbr.pyi index 38658a03139c..504a5d5f115a 100644 --- a/mypy/typeshed/stdlib/pyclbr.pyi +++ b/mypy/typeshed/stdlib/pyclbr.pyi @@ -1,20 +1,35 @@ import sys -from collections.abc import Sequence +from collections.abc import Mapping, Sequence __all__ = ["readmodule", "readmodule_ex", "Class", "Function"] -class Class: +class _Object: module: str name: str - super: list[Class | str] | None - methods: dict[str, int] file: int lineno: int if sys.version_info >= (3, 10): end_lineno: int | None - parent: Class | None + parent: _Object | None + + # This is a dict at runtime, but we're typing it as Mapping to + # avoid variance issues in the subclasses + children: Mapping[str, _Object] + + if sys.version_info >= (3, 10): + def __init__( + self, module: str, name: str, file: str, lineno: int, end_lineno: int | None, parent: _Object | None + ) -> None: ... + else: + def __init__(self, module: str, name: str, file: str, lineno: int, parent: _Object | None) -> None: ... + +class Function(_Object): + if sys.version_info >= (3, 10): + is_async: bool + + parent: Function | Class | None children: dict[str, Class | Function] if sys.version_info >= (3, 10): @@ -22,29 +37,20 @@ class Class: self, module: str, name: str, - super_: list[Class | str] | None, file: str, lineno: int, - parent: Class | None = None, + parent: Function | Class | None = None, + is_async: bool = False, *, end_lineno: int | None = None, ) -> None: ... else: - def __init__( - self, module: str, name: str, super: list[Class | str] | None, file: str, lineno: int, parent: Class | None = None - ) -> None: ... - -class Function: - module: str - name: str - file: int - lineno: int - - if sys.version_info >= (3, 10): - end_lineno: int | None - is_async: bool + def __init__(self, module: str, name: str, file: str, lineno: int, parent: Function | Class | None = None) -> None: ... - parent: Function | Class | None +class Class(_Object): + super: list[Class | str] | None + methods: dict[str, int] + parent: Class | None children: dict[str, Class | Function] if sys.version_info >= (3, 10): @@ -52,15 +58,17 @@ class Function: self, module: str, name: str, + super_: list[Class | str] | None, file: str, lineno: int, - parent: Function | Class | None = None, - is_async: bool = False, + parent: Class | None = None, *, end_lineno: int | None = None, ) -> None: ... else: - def __init__(self, module: str, name: str, file: str, lineno: int, parent: Function | Class | None = None) -> None: ... + def __init__( + self, module: str, name: str, super: list[Class | str] | None, file: str, lineno: int, parent: Class | None = None + ) -> None: ... def readmodule(module: str, path: Sequence[str] | None = None) -> dict[str, Class]: ... def readmodule_ex(module: str, path: Sequence[str] | None = None) -> dict[str, Class | Function | list[str]]: ... diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index 6a932f66cd09..5753d1d661b9 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -86,7 +86,7 @@ class UDPServer(TCPServer): def get_request(self) -> tuple[tuple[bytes, _socket], _RetAddress]: ... # type: ignore[override] if sys.platform != "win32": - class UnixStreamServer(BaseServer): + class UnixStreamServer(TCPServer): server_address: _AfUnixAddress # type: ignore[assignment] def __init__( self, @@ -95,7 +95,7 @@ if sys.platform != "win32": bind_and_activate: bool = True, ) -> None: ... - class UnixDatagramServer(BaseServer): + class UnixDatagramServer(UDPServer): server_address: _AfUnixAddress # type: ignore[assignment] def __init__( self, diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi index 8ef65223dc34..2c10bf7e7c3b 100644 --- a/mypy/typeshed/stdlib/sre_parse.pyi +++ b/mypy/typeshed/stdlib/sre_parse.pyi @@ -19,6 +19,9 @@ FLAGS: dict[str, int] TYPE_FLAGS: int GLOBAL_FLAGS: int +if sys.version_info >= (3, 11): + MAXWIDTH: int + if sys.version_info < (3, 11): class Verbose(Exception): ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index a73b1e275f11..5f7c3cb4527d 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,13 +1,12 @@ import _tkinter import sys -from _typeshed import Incomplete, StrOrBytesPath +from _typeshed import Incomplete, StrEnum, StrOrBytesPath from collections.abc import Callable, Mapping, Sequence -from enum import Enum from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType from typing import Any, Generic, NamedTuple, TypeVar, overload, type_check_only -from typing_extensions import Literal, TypeAlias, TypedDict, deprecated +from typing_extensions import Literal, TypeAlias, TypedDict, TypeVarTuple, Unpack, deprecated if sys.version_info >= (3, 9): __all__ = [ @@ -195,7 +194,7 @@ if sys.version_info >= (3, 11): releaselevel: str serial: int -class EventType(str, Enum): +class EventType(StrEnum): Activate: str ButtonPress: str Button = ButtonPress @@ -315,6 +314,8 @@ getdouble: Incomplete def getboolean(s): ... +_Ts = TypeVarTuple("_Ts") + class _GridIndexInfo(TypedDict, total=False): minsize: _ScreenUnits pad: _ScreenUnits @@ -350,9 +351,9 @@ class Misc: def tk_focusPrev(self) -> Misc | None: ... # .after() can be called without the "func" argument, but it is basically never what you want. # It behaves like time.sleep() and freezes the GUI app. - def after(self, ms: int | Literal["idle"], func: Callable[..., object], *args: Any) -> str: ... + def after(self, ms: int | Literal["idle"], func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> str: ... # after_idle is essentially partialmethod(after, "idle") - def after_idle(self, func: Callable[..., object], *args: Any) -> str: ... + def after_idle(self, func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> str: ... def after_cancel(self, id: str) -> None: ... def bell(self, displayof: Literal[0] | Misc | None = 0) -> None: ... def clipboard_get(self, *, displayof: Misc = ..., type: str = ...) -> str: ... diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index 3764a5b06024..14a921c5e6cc 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -1,7 +1,7 @@ import sys import types -from _typeshed import StrPath, TraceFunction -from collections.abc import Callable, Mapping, Sequence +from _typeshed import Incomplete, StrPath, TraceFunction +from collections.abc import Callable, Iterable, Mapping, Sequence from typing import Any, TypeVar from typing_extensions import ParamSpec, TypeAlias @@ -12,6 +12,12 @@ _P = ParamSpec("_P") _FileModuleFunction: TypeAlias = tuple[str, str | None, str] class CoverageResults: + counts: dict[tuple[str, int], int] + counter: dict[tuple[str, int], int] + calledfuncs: dict[_FileModuleFunction, int] + callers: dict[tuple[_FileModuleFunction, _FileModuleFunction], int] + inifile: StrPath | None + outfile: StrPath | None def __init__( self, counts: dict[tuple[str, int], int] | None = None, @@ -27,7 +33,21 @@ class CoverageResults: ) -> tuple[int, int]: ... def is_ignored_filename(self, filename: str) -> bool: ... # undocumented +class _Ignore: + def __init__(self, modules: Iterable[str] | None = None, dirs: Iterable[StrPath] | None = None) -> None: ... + def names(self, filename: str, modulename: str) -> int: ... + class Trace: + inifile: StrPath | None + outfile: StrPath | None + ignore: _Ignore + counts: dict[str, int] + pathtobasename: dict[Incomplete, Incomplete] + donothing: int + trace: int + start_time: int | None + globaltrace: TraceFunction + localtrace: TraceFunction def __init__( self, count: int = 1, diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index cc5d683e245a..7efe6cdc9662 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -249,6 +249,8 @@ class TestCase: def assertListEqual(self, list1: list[Any], list2: list[Any], msg: Any = None) -> None: ... def assertTupleEqual(self, tuple1: tuple[Any, ...], tuple2: tuple[Any, ...], msg: Any = None) -> None: ... def assertSetEqual(self, set1: AbstractSet[object], set2: AbstractSet[object], msg: Any = None) -> None: ... + # assertDictEqual accepts only true dict instances. We can't use that here, since that would make + # assertDictEqual incompatible with TypedDict. def assertDictEqual(self, d1: Mapping[Any, object], d2: Mapping[Any, object], msg: Any = None) -> None: ... def fail(self, msg: Any = None) -> NoReturn: ... def countTestCases(self) -> int: ... diff --git a/mypy/typeshed/stdlib/urllib/response.pyi b/mypy/typeshed/stdlib/urllib/response.pyi index 61ba687076b2..bbec4cacc750 100644 --- a/mypy/typeshed/stdlib/urllib/response.pyi +++ b/mypy/typeshed/stdlib/urllib/response.pyi @@ -1,39 +1,23 @@ import sys +import tempfile from _typeshed import ReadableBuffer from collections.abc import Callable, Iterable from email.message import Message from types import TracebackType -from typing import IO, Any, BinaryIO -from typing_extensions import Self +from typing import IO, Any __all__ = ["addbase", "addclosehook", "addinfo", "addinfourl"] -class addbase(BinaryIO): +class addbase(tempfile._TemporaryFileWrapper[bytes]): fp: IO[bytes] def __init__(self, fp: IO[bytes]) -> None: ... - def __enter__(self) -> Self: ... def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... - def __iter__(self) -> Self: ... - def __next__(self) -> bytes: ... - def close(self) -> None: ... # These methods don't actually exist, but the class inherits at runtime from # tempfile._TemporaryFileWrapper, which uses __getattr__ to delegate to the # underlying file object. To satisfy the BinaryIO interface, we pretend that this # class has these additional methods. - def fileno(self) -> int: ... - def flush(self) -> None: ... - def isatty(self) -> bool: ... - def read(self, n: int = ...) -> bytes: ... - def readable(self) -> bool: ... - def readline(self, limit: int = ...) -> bytes: ... - def readlines(self, hint: int = ...) -> list[bytes]: ... - def seek(self, offset: int, whence: int = ...) -> int: ... - def seekable(self) -> bool: ... - def tell(self) -> int: ... - def truncate(self, size: int | None = ...) -> int: ... - def writable(self) -> bool: ... def write(self, s: ReadableBuffer) -> int: ... def writelines(self, lines: Iterable[ReadableBuffer]) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi index f726eae0516f..d2d6f12d474a 100644 --- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi @@ -1,10 +1,17 @@ import sys from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co from collections.abc import Iterable -from typing import Any, NoReturn, Protocol +from typing import Protocol from typing_extensions import TypeAlias +from xml.sax._exceptions import ( + SAXException as SAXException, + SAXNotRecognizedException as SAXNotRecognizedException, + SAXNotSupportedException as SAXNotSupportedException, + SAXParseException as SAXParseException, + SAXReaderNotAvailable as SAXReaderNotAvailable, +) from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler -from xml.sax.xmlreader import Locator, XMLReader +from xml.sax.xmlreader import XMLReader class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]): def close(self) -> None: ... @@ -14,23 +21,6 @@ if sys.version_info >= (3, 8): else: _Source: TypeAlias = str | _SupportsReadClose[bytes] | _SupportsReadClose[str] -class SAXException(Exception): - def __init__(self, msg: str, exception: Exception | None = None) -> None: ... - def getMessage(self) -> str: ... - def getException(self) -> Exception: ... - def __getitem__(self, ix: Any) -> NoReturn: ... - -class SAXParseException(SAXException): - def __init__(self, msg: str, exception: Exception | None, locator: Locator) -> None: ... - def getColumnNumber(self) -> int: ... - def getLineNumber(self) -> int: ... - def getPublicId(self): ... - def getSystemId(self): ... - -class SAXNotRecognizedException(SAXException): ... -class SAXNotSupportedException(SAXException): ... -class SAXReaderNotAvailable(SAXNotSupportedException): ... - default_parser_list: list[str] if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi new file mode 100644 index 000000000000..8a437a971f13 --- /dev/null +++ b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi @@ -0,0 +1,19 @@ +from typing import NoReturn +from xml.sax.xmlreader import Locator + +class SAXException(Exception): + def __init__(self, msg: str, exception: Exception | None = None) -> None: ... + def getMessage(self) -> str: ... + def getException(self) -> Exception: ... + def __getitem__(self, ix: object) -> NoReturn: ... + +class SAXParseException(SAXException): + def __init__(self, msg: str, exception: Exception | None, locator: Locator) -> None: ... + def getColumnNumber(self) -> int: ... + def getLineNumber(self) -> int: ... + def getPublicId(self): ... + def getSystemId(self): ... + +class SAXNotRecognizedException(SAXException): ... +class SAXNotSupportedException(SAXException): ... +class SAXReaderNotAvailable(SAXNotSupportedException): ... diff --git a/mypy/typeshed/stdlib/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index d4f41bbaf22a..7fc39138fec5 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -2,7 +2,7 @@ import sys from typing import Any from typing_extensions import final -class Str: ... +class Str(str): ... @final class Xxo: @@ -14,10 +14,10 @@ def foo(__i: int, __j: int) -> Any: ... def new() -> Xxo: ... if sys.version_info >= (3, 10): - class Error: ... + class Error(Exception): ... else: - class error: ... + class error(Exception): ... class Null: ... def roj(__b: Any) -> None: ... From 1dd8e7fe654991b01bd80ef7f1f675d9e3910c3a Mon Sep 17 00:00:00 2001 From: Kouroche Bouchiat Date: Sun, 17 Dec 2023 21:32:57 +0100 Subject: [PATCH 0436/1617] Substitute type variables in return type of static methods (#16670) `add_class_tvars` correctly instantiates type variables in the return type for class methods but not for static methods. Check if the analyzed member is a static method in `analyze_class_attribute_access` and substitute the type variable in the return type in `add_class_tvars` accordingly. Fixes #16668. --- mypy/checkmember.py | 15 +++++++++++++-- test-data/unit/check-generics.test | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 5a4f3875ad04..c24edacf0ee1 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1063,11 +1063,14 @@ def analyze_class_attribute_access( is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( isinstance(node.node, FuncBase) and node.node.is_class ) + is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or ( + isinstance(node.node, FuncBase) and node.node.is_static + ) t = get_proper_type(t) if isinstance(t, FunctionLike) and is_classmethod: t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg) result = add_class_tvars( - t, isuper, is_classmethod, mx.self_type, original_vars=original_vars + t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars ) if not mx.is_lvalue: result = analyze_descriptor_access(result, mx) @@ -1177,6 +1180,7 @@ def add_class_tvars( t: ProperType, isuper: Instance | None, is_classmethod: bool, + is_staticmethod: bool, original_type: Type, original_vars: Sequence[TypeVarLikeType] | None = None, ) -> Type: @@ -1195,6 +1199,7 @@ class B(A[str]): pass isuper: Current instance mapped to the superclass where method was defined, this is usually done by map_instance_to_supertype() is_classmethod: True if this method is decorated with @classmethod + is_staticmethod: True if this method is decorated with @staticmethod original_type: The value of the type B in the expression B.foo() or the corresponding component in case of a union (this is used to bind the self-types) original_vars: Type variables of the class callable on which the method was accessed @@ -1220,6 +1225,7 @@ class B(A[str]): pass t = freshen_all_functions_type_vars(t) if is_classmethod: t = bind_self(t, original_type, is_classmethod=True) + if is_classmethod or is_staticmethod: assert isuper is not None t = expand_type_by_instance(t, isuper) freeze_all_type_vars(t) @@ -1230,7 +1236,12 @@ class B(A[str]): pass cast( CallableType, add_class_tvars( - item, isuper, is_classmethod, original_type, original_vars=original_vars + item, + isuper, + is_classmethod, + is_staticmethod, + original_type, + original_vars=original_vars, ), ) for item in t.items diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index ef3f359e4989..e2f65ed39c1e 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2297,6 +2297,19 @@ def func(x: S) -> S: return C[S].get() [builtins fixtures/classmethod.pyi] +[case testGenericStaticMethodInGenericFunction] +from typing import Generic, TypeVar +T = TypeVar('T') +S = TypeVar('S') + +class C(Generic[T]): + @staticmethod + def get() -> T: ... + +def func(x: S) -> S: + return C[S].get() +[builtins fixtures/staticmethod.pyi] + [case testMultipleAssignmentFromAnyIterable] from typing import Any class A: From 08cd371e1a286045139e94622b28e3e9e11b6e63 Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Thu, 21 Dec 2023 07:09:52 -0600 Subject: [PATCH 0437/1617] changelog: add release notes for 1.8 (#16689) --- CHANGELOG.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f28cdb1ccc25..a63ad3693597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,86 @@ ## Next release -Stubgen will now include `__all__` in its output if it is in the input file (PR [16356](https://github.com/python/mypy/pull/16356)). +## Mypy 1.8 + +We’ve just uploaded mypy 1.8 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Typechecking Improvements + * Do not intersect types in isinstance checks if at least one is final (Christoph Tyralla, PR [16330](https://github.com/python/mypy/pull/16330)) + * Detect that @final class without __bool__ cannot have falsey instances (Ilya Priven, PR [16566](https://github.com/python/mypy/pull/16566)) + * Do not allow `TypedDict` classes with extra keywords (Nikita Sobolev, PR [16438](https://github.com/python/mypy/pull/16438)) + * Do not allow class-level keywords for `NamedTuple` (Nikita Sobolev, PR [16526](https://github.com/python/mypy/pull/16526)) + * Make imprecise constraints handling more robust (Ivan Levkivskyi, PR [16502](https://github.com/python/mypy/pull/16502)) + * Fix strict-optional in extending generic TypedDict (Ivan Levkivskyi, PR [16398](https://github.com/python/mypy/pull/16398)) + * Allow type ignores of PEP 695 constructs (Shantanu, PR [16608](https://github.com/python/mypy/pull/16608)) + * Refactor class decorator: this enables `type_check_only` support for `TypedDict` and `NamedTuple` (Nikita Sobolev, PR [16469](https://github.com/python/mypy/pull/16469)) + +#### Performance Improvements + * Add fast path to analyzing special form assignments (Jukka Lehtosalo, PR [16561](https://github.com/python/mypy/pull/16561)) + +#### Improvements to Error Reporting + * Don't show docs links for plugin error codes (Ivan Levkivskyi, PR [16383](https://github.com/python/mypy/pull/16383)) + * Improve error messages for `super` checks and add more tests (Nikita Sobolev, PR [16393](https://github.com/python/mypy/pull/16393)) + * Add error code for mutable covariant override (Ivan Levkivskyi, PR [16399](https://github.com/python/mypy/pull/16399)) + +#### Stubgen Improvements + * Preserve simple defaults in function signatures (Ali Hamdan, PR [15355](https://github.com/python/mypy/pull/15355)) + * Include __all__ in output (Jelle Zijlstra, PR [16356](https://github.com/python/mypy/pull/16356)) + * Fix stubgen regressions with pybind11 and mypy 1.7 (Chad Dombrova, PR [16504](https://github.com/python/mypy/pull/16504)) + +#### Stubtest Improvements + * Improve handling of unrepresentable defaults (Jelle Zijlstra, PR [16433](https://github.com/python/mypy/pull/16433)) + * Print more helpful errors if a function is missing from stub (Alex Waygood, PR [16517](https://github.com/python/mypy/pull/16517)) + * Support `@type_check_only` decorator (Nikita Sobolev, PR [16422](https://github.com/python/mypy/pull/16422)) + * Warn about missing __del__ (Shantanu, PR [16456](https://github.com/python/mypy/pull/16456)) + * Fix crashes with some uses of final and deprecated (Shantanu, PR [16457](https://github.com/python/mypy/pull/16457)) + +#### Fixes to Crashes + * Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` (Alex Waygood, PR [16541](https://github.com/python/mypy/pull/16541)) + * Fix crash on TypeGuard in __call__ (Ivan Levkivskyi, PR [16516](https://github.com/python/mypy/pull/16516)) + * Fix crash on invalid enum in method (Ivan Levkivskyi, PR [16511](https://github.com/python/mypy/pull/16511)) + * Fix crash on unimported Any in TypedDict (Ivan Levkivskyi, PR [16510](https://github.com/python/mypy/pull/16510)) + +#### Documentation Updates + * Update soft-error-limit default value to -1 (Sveinung Gundersen, PR [16542](https://github.com/python/mypy/pull/16542)) + * Support Sphinx 7.x (Michael R. Crusoe, PR [16460](https://github.com/python/mypy/pull/16460)) + +#### Other Notable Changes and Fixes + * Allow mypy to output a junit file with per-file results (Matthew Wright, PR [16388](https://github.com/python/mypy/pull/16388)) + +#### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +#### Acknowledgements + +​Thanks to all mypy contributors who contributed to this release: + +- Alex Waygood +- Ali Hamdan +- Chad Dombrova +- Christoph Tyralla +- Ilya Priven +- Ivan Levkivskyi +- Jelle Zijlstra +- Jukka Lehtosalo +- Marcel Telka +- Matthew Wright +- Michael R. Crusoe +- Nikita Sobolev +- Ole Peder Brandtzæg +- robjhornby +- Shantanu +- Sveinung Gundersen +- Valentin Stanciu + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +Posted by Wesley Collin Wright ## Mypy 1.7 From 7d842e865331b3b6f44a116a886ea3c24eb67461 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 21 Dec 2023 14:50:12 +0000 Subject: [PATCH 0438/1617] Minor updates to mypy 1.8 changelog entries (#16692) --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a63ad3693597..82d79b415f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,39 +10,39 @@ We’ve just uploaded mypy 1.8 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Typechecking Improvements +#### Type-checking Improvements * Do not intersect types in isinstance checks if at least one is final (Christoph Tyralla, PR [16330](https://github.com/python/mypy/pull/16330)) - * Detect that @final class without __bool__ cannot have falsey instances (Ilya Priven, PR [16566](https://github.com/python/mypy/pull/16566)) + * Detect that `@final` class without `__bool__` cannot have falsey instances (Ilya Priven, PR [16566](https://github.com/python/mypy/pull/16566)) * Do not allow `TypedDict` classes with extra keywords (Nikita Sobolev, PR [16438](https://github.com/python/mypy/pull/16438)) * Do not allow class-level keywords for `NamedTuple` (Nikita Sobolev, PR [16526](https://github.com/python/mypy/pull/16526)) * Make imprecise constraints handling more robust (Ivan Levkivskyi, PR [16502](https://github.com/python/mypy/pull/16502)) * Fix strict-optional in extending generic TypedDict (Ivan Levkivskyi, PR [16398](https://github.com/python/mypy/pull/16398)) * Allow type ignores of PEP 695 constructs (Shantanu, PR [16608](https://github.com/python/mypy/pull/16608)) - * Refactor class decorator: this enables `type_check_only` support for `TypedDict` and `NamedTuple` (Nikita Sobolev, PR [16469](https://github.com/python/mypy/pull/16469)) + * Enable `type_check_only` support for `TypedDict` and `NamedTuple` (Nikita Sobolev, PR [16469](https://github.com/python/mypy/pull/16469)) #### Performance Improvements * Add fast path to analyzing special form assignments (Jukka Lehtosalo, PR [16561](https://github.com/python/mypy/pull/16561)) #### Improvements to Error Reporting - * Don't show docs links for plugin error codes (Ivan Levkivskyi, PR [16383](https://github.com/python/mypy/pull/16383)) + * Don't show documentation links for plugin error codes (Ivan Levkivskyi, PR [16383](https://github.com/python/mypy/pull/16383)) * Improve error messages for `super` checks and add more tests (Nikita Sobolev, PR [16393](https://github.com/python/mypy/pull/16393)) * Add error code for mutable covariant override (Ivan Levkivskyi, PR [16399](https://github.com/python/mypy/pull/16399)) #### Stubgen Improvements * Preserve simple defaults in function signatures (Ali Hamdan, PR [15355](https://github.com/python/mypy/pull/15355)) - * Include __all__ in output (Jelle Zijlstra, PR [16356](https://github.com/python/mypy/pull/16356)) + * Include `__all__` in output (Jelle Zijlstra, PR [16356](https://github.com/python/mypy/pull/16356)) * Fix stubgen regressions with pybind11 and mypy 1.7 (Chad Dombrova, PR [16504](https://github.com/python/mypy/pull/16504)) #### Stubtest Improvements * Improve handling of unrepresentable defaults (Jelle Zijlstra, PR [16433](https://github.com/python/mypy/pull/16433)) * Print more helpful errors if a function is missing from stub (Alex Waygood, PR [16517](https://github.com/python/mypy/pull/16517)) * Support `@type_check_only` decorator (Nikita Sobolev, PR [16422](https://github.com/python/mypy/pull/16422)) - * Warn about missing __del__ (Shantanu, PR [16456](https://github.com/python/mypy/pull/16456)) - * Fix crashes with some uses of final and deprecated (Shantanu, PR [16457](https://github.com/python/mypy/pull/16457)) + * Warn about missing `__del__` (Shantanu, PR [16456](https://github.com/python/mypy/pull/16456)) + * Fix crashes with some uses of `final` and `deprecated` (Shantanu, PR [16457](https://github.com/python/mypy/pull/16457)) #### Fixes to Crashes * Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` (Alex Waygood, PR [16541](https://github.com/python/mypy/pull/16541)) - * Fix crash on TypeGuard in __call__ (Ivan Levkivskyi, PR [16516](https://github.com/python/mypy/pull/16516)) + * Fix crash on TypeGuard in `__call__` (Ivan Levkivskyi, PR [16516](https://github.com/python/mypy/pull/16516)) * Fix crash on invalid enum in method (Ivan Levkivskyi, PR [16511](https://github.com/python/mypy/pull/16511)) * Fix crash on unimported Any in TypedDict (Ivan Levkivskyi, PR [16510](https://github.com/python/mypy/pull/16510)) From 3338c574b92a6eae10e28e1546f4f3f8ba4c20f0 Mon Sep 17 00:00:00 2001 From: zipperer <47086307+zipperer@users.noreply.github.com> Date: Sun, 24 Dec 2023 01:06:54 -0600 Subject: [PATCH 0439/1617] Fix typo in getting_started.rst (#16700) --- docs/source/getting_started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 7ea4ddd148ea..049d7af003b5 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -66,7 +66,7 @@ This is the case even if you misuse the function! def greeting(name): return 'Hello ' + name - # These calls will fail when the program run, but mypy does not report an error + # These calls will fail when the program runs, but mypy does not report an error # because "greeting" does not have type annotations. greeting(123) greeting(b"Alice") From 92fa54822588ef2af453f1623d4dda48f4f2712b Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Sun, 24 Dec 2023 11:57:06 -0800 Subject: [PATCH 0440/1617] Fix a single-letter typo in a comment (#16699) --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index d19766c1de34..fcdb61f9719b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -56,7 +56,7 @@ # 1. types.LiteralType's serialize and deserialize methods: this method # needs to make sure it can convert the below types into JSON and back. # -# 2. types.LiteralType's 'alue_repr` method: this method is ultimately used +# 2. types.LiteralType's 'value_repr` method: this method is ultimately used # by TypeStrVisitor's visit_literal_type to generate a reasonable # repr-able output. # From f79ae69be1b0c920eec6bf181e6f8c7689cbae20 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 25 Dec 2023 07:41:30 +0000 Subject: [PATCH 0441/1617] Speed up pythoneval tests (#16559) Skip slow and unnecessary overload checks in stdlib stubs in these tests. These checks already seem to be skipped in most other contexts, but because pythoneval tests use `--no-silence-site-packages`, the checks were enabled. This makes pythoneval tests about 13% faster on my Linux desktop, and it should also speed up CI a bit. --- mypy/checker.py | 4 +++- mypy/main.py | 2 ++ mypy/options.py | 4 ++++ mypy/test/testpythoneval.py | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 979a55b223c9..0ac8f6904973 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -709,7 +709,9 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # At this point we should have set the impl already, and all remaining # items are decorators - if self.msg.errors.file in self.msg.errors.ignored_files: + if self.msg.errors.file in self.msg.errors.ignored_files or ( + self.is_typeshed_stub and self.options.test_env + ): # This is a little hacky, however, the quadratic check here is really expensive, this # method has no side effects, so we should skip it if we aren't going to report # anything. In some other places we swallow errors in stubs, but this error is very diff --git a/mypy/main.py b/mypy/main.py index ee090a03d3cf..2c68466ec977 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1135,6 +1135,8 @@ def add_invertible_flag( parser.add_argument("--dump-graph", action="store_true", help=argparse.SUPPRESS) # --semantic-analysis-only does exactly that. parser.add_argument("--semantic-analysis-only", action="store_true", help=argparse.SUPPRESS) + # Some tests use this to tell mypy that we are running a test. + parser.add_argument("--test-env", action="store_true", help=argparse.SUPPRESS) # --local-partial-types disallows partial types spanning module top level and a function # (implicitly defined in fine-grained incremental mode) parser.add_argument("--local-partial-types", action="store_true", help=argparse.SUPPRESS) diff --git a/mypy/options.py b/mypy/options.py index 38a87e423766..bf9c09f1bf4b 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -321,6 +321,10 @@ def __init__(self) -> None: # Use stub builtins fixtures to speed up tests self.use_builtins_fixtures = False + # This should only be set when running certain mypy tests. + # Use this sparingly to avoid tests diverging from non-test behavior. + self.test_env = False + # -- experimental options -- self.shadow_file: list[list[str]] | None = None self.show_column_numbers: bool = False diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 17baec96cfbc..baeea1853ded 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -53,6 +53,7 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None "--hide-error-codes", "--allow-empty-bodies", "--force-uppercase-builtins", + "--test-env", # Speeds up some checks ] interpreter = python3_path mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}") From 761965d260e54f8e150d8bd7ac3ab3efd6503b93 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 28 Dec 2023 11:27:30 +0000 Subject: [PATCH 0442/1617] Speed up finding function type variables (#16562) Merge two visitors into a single visitor that is a bit more optimized than the old visitors. This speeds ups tests, in particular -- `mypy/test/testcheck.py` is about 4% faster and `mypy/test/testpythoneval.py` is about 3% faster. Also self-check is about 1% faster, both interpreted and compiled. This adds more code, but the new code is largely boilerplate, so the difficulty of maintenance seems roughly the same. --- mypy/semanal.py | 13 ++- mypy/typeanal.py | 252 +++++++++++++++++++++++++++++++---------------- 2 files changed, 176 insertions(+), 89 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 4128369ace5d..48be004daf76 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -224,9 +224,9 @@ from mypy.tvar_scope import TypeVarLikeScope from mypy.typeanal import ( SELF_TYPE_NAMES, + FindTypeVarVisitor, TypeAnalyser, TypeVarLikeList, - TypeVarLikeQuery, analyze_type_alias, check_for_explicit_any, detect_diverging_alias, @@ -2034,6 +2034,11 @@ def analyze_unbound_tvar_impl( assert isinstance(sym.node, TypeVarExpr) return t.name, sym.node + def find_type_var_likes(self, t: Type) -> TypeVarLikeList: + visitor = FindTypeVarVisitor(self, self.tvar_scope) + t.accept(visitor) + return visitor.type_var_likes + def get_all_bases_tvars( self, base_type_exprs: list[Expression], removed: list[int] ) -> TypeVarLikeList: @@ -2046,7 +2051,7 @@ def get_all_bases_tvars( except TypeTranslationError: # This error will be caught later. continue - base_tvars = base.accept(TypeVarLikeQuery(self, self.tvar_scope)) + base_tvars = self.find_type_var_likes(base) tvars.extend(base_tvars) return remove_dups(tvars) @@ -2064,7 +2069,7 @@ def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLi except TypeTranslationError: # This error will be caught later. continue - base_tvars = base.accept(TypeVarLikeQuery(self, self.tvar_scope)) + base_tvars = self.find_type_var_likes(base) tvars.extend(base_tvars) tvars = remove_dups(tvars) # Variables are defined in order of textual appearance. tvar_defs = [] @@ -3490,7 +3495,7 @@ def analyze_alias( ) return None, [], set(), [], False - found_type_vars = typ.accept(TypeVarLikeQuery(self, self.tvar_scope)) + found_type_vars = self.find_type_var_likes(typ) tvar_defs: list[TypeVarLikeType] = [] namespace = self.qualified_name(name) with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d238a452e7a9..4d916315bddd 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1570,32 +1570,32 @@ def tvar_scope_frame(self) -> Iterator[None]: yield self.tvar_scope = old_scope - def find_type_var_likes(self, t: Type, include_callables: bool = True) -> TypeVarLikeList: - return t.accept( - TypeVarLikeQuery(self.api, self.tvar_scope, include_callables=include_callables) - ) - - def infer_type_variables(self, type: CallableType) -> list[tuple[str, TypeVarLikeExpr]]: - """Return list of unique type variables referred to in a callable.""" - names: list[str] = [] - tvars: list[TypeVarLikeExpr] = [] + def find_type_var_likes(self, t: Type) -> TypeVarLikeList: + visitor = FindTypeVarVisitor(self.api, self.tvar_scope) + t.accept(visitor) + return visitor.type_var_likes + + def infer_type_variables( + self, type: CallableType + ) -> tuple[list[tuple[str, TypeVarLikeExpr]], bool]: + """Infer type variables from a callable. + + Return tuple with these items: + - list of unique type variables referred to in a callable + - whether there is a reference to the Self type + """ + visitor = FindTypeVarVisitor(self.api, self.tvar_scope) for arg in type.arg_types: - for name, tvar_expr in self.find_type_var_likes(arg): - if name not in names: - names.append(name) - tvars.append(tvar_expr) + arg.accept(visitor) + # When finding type variables in the return type of a function, don't # look inside Callable types. Type variables only appearing in # functions in the return type belong to those functions, not the # function we're currently analyzing. - for name, tvar_expr in self.find_type_var_likes(type.ret_type, include_callables=False): - if name not in names: - names.append(name) - tvars.append(tvar_expr) + visitor.include_callables = False + type.ret_type.accept(visitor) - if not names: - return [] # Fast path - return list(zip(names, tvars)) + return visitor.type_var_likes, visitor.has_self_type def bind_function_type_variables( self, fun_type: CallableType, defn: Context @@ -1615,10 +1615,7 @@ def bind_function_type_variables( binding = self.tvar_scope.bind_new(var.name, var_expr) defs.append(binding) return defs, has_self_type - typevars = self.infer_type_variables(fun_type) - has_self_type = find_self_type( - fun_type, lambda name: self.api.lookup_qualified(name, defn, suppress_errors=True) - ) + typevars, has_self_type = self.infer_type_variables(fun_type) # Do not define a new type variable if already defined in scope. typevars = [ (name, tvar) for name, tvar in typevars if not self.is_defined_type_var(name, defn) @@ -2062,67 +2059,6 @@ def flatten_tvars(lists: list[list[T]]) -> list[T]: return result -class TypeVarLikeQuery(TypeQuery[TypeVarLikeList]): - """Find TypeVar and ParamSpec references in an unbound type.""" - - def __init__( - self, - api: SemanticAnalyzerCoreInterface, - scope: TypeVarLikeScope, - *, - include_callables: bool = True, - ) -> None: - super().__init__(flatten_tvars) - self.api = api - self.scope = scope - self.include_callables = include_callables - # Only include type variables in type aliases args. This would be anyway - # that case if we expand (as target variables would be overridden with args) - # and it may cause infinite recursion on invalid (diverging) recursive aliases. - self.skip_alias_target = True - - def _seems_like_callable(self, type: UnboundType) -> bool: - if not type.args: - return False - return isinstance(type.args[0], (EllipsisType, TypeList, ParamSpecType)) - - def visit_unbound_type(self, t: UnboundType) -> TypeVarLikeList: - name = t.name - node = None - # Special case P.args and P.kwargs for ParamSpecs only. - if name.endswith("args"): - if name.endswith(".args") or name.endswith(".kwargs"): - base = ".".join(name.split(".")[:-1]) - n = self.api.lookup_qualified(base, t) - if n is not None and isinstance(n.node, ParamSpecExpr): - node = n - name = base - if node is None: - node = self.api.lookup_qualified(name, t) - if ( - node - and isinstance(node.node, TypeVarLikeExpr) - and self.scope.get_binding(node) is None - ): - assert isinstance(node.node, TypeVarLikeExpr) - return [(name, node.node)] - elif not self.include_callables and self._seems_like_callable(t): - return [] - elif node and node.fullname in LITERAL_TYPE_NAMES: - return [] - elif node and node.fullname in ANNOTATED_TYPE_NAMES and t.args: - # Don't query the second argument to Annotated for TypeVars - return self.query_types([t.args[0]]) - else: - return super().visit_unbound_type(t) - - def visit_callable_type(self, t: CallableType) -> TypeVarLikeList: - if self.include_callables: - return super().visit_callable_type(t) - else: - return [] - - class DivergingAliasDetector(TrivialSyntheticTypeTranslator): """See docstring of detect_diverging_alias() for details.""" @@ -2359,3 +2295,149 @@ def unknown_unpack(t: Type) -> bool: if isinstance(unpacked, AnyType) and unpacked.type_of_any == TypeOfAny.special_form: return True return False + + +class FindTypeVarVisitor(SyntheticTypeVisitor[None]): + """Type visitor that looks for type variable types and self types.""" + + def __init__(self, api: SemanticAnalyzerCoreInterface, scope: TypeVarLikeScope) -> None: + self.api = api + self.scope = scope + self.type_var_likes: list[tuple[str, TypeVarLikeExpr]] = [] + self.has_self_type = False + self.seen_aliases: set[TypeAliasType] | None = None + self.include_callables = True + + def _seems_like_callable(self, type: UnboundType) -> bool: + if not type.args: + return False + return isinstance(type.args[0], (EllipsisType, TypeList, ParamSpecType)) + + def visit_unbound_type(self, t: UnboundType) -> None: + name = t.name + node = None + + # Special case P.args and P.kwargs for ParamSpecs only. + if name.endswith("args"): + if name.endswith(".args") or name.endswith(".kwargs"): + base = ".".join(name.split(".")[:-1]) + n = self.api.lookup_qualified(base, t) + if n is not None and isinstance(n.node, ParamSpecExpr): + node = n + name = base + if node is None: + node = self.api.lookup_qualified(name, t) + if node and node.fullname in SELF_TYPE_NAMES: + self.has_self_type = True + if ( + node + and isinstance(node.node, TypeVarLikeExpr) + and self.scope.get_binding(node) is None + ): + if (name, node.node) not in self.type_var_likes: + self.type_var_likes.append((name, node.node)) + elif not self.include_callables and self._seems_like_callable(t): + if find_self_type( + t, lambda name: self.api.lookup_qualified(name, t, suppress_errors=True) + ): + self.has_self_type = True + return + elif node and node.fullname in LITERAL_TYPE_NAMES: + return + elif node and node.fullname in ANNOTATED_TYPE_NAMES and t.args: + # Don't query the second argument to Annotated for TypeVars + self.process_types([t.args[0]]) + elif t.args: + self.process_types(t.args) + + def visit_type_list(self, t: TypeList) -> None: + self.process_types(t.items) + + def visit_callable_argument(self, t: CallableArgument) -> None: + t.typ.accept(self) + + def visit_any(self, t: AnyType) -> None: + pass + + def visit_uninhabited_type(self, t: UninhabitedType) -> None: + pass + + def visit_none_type(self, t: NoneType) -> None: + pass + + def visit_erased_type(self, t: ErasedType) -> None: + pass + + def visit_deleted_type(self, t: DeletedType) -> None: + pass + + def visit_type_var(self, t: TypeVarType) -> None: + self.process_types([t.upper_bound, t.default] + t.values) + + def visit_param_spec(self, t: ParamSpecType) -> None: + self.process_types([t.upper_bound, t.default]) + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + self.process_types([t.upper_bound, t.default]) + + def visit_unpack_type(self, t: UnpackType) -> None: + self.process_types([t.type]) + + def visit_parameters(self, t: Parameters) -> None: + self.process_types(t.arg_types) + + def visit_partial_type(self, t: PartialType) -> None: + pass + + def visit_instance(self, t: Instance) -> None: + self.process_types(t.args) + + def visit_callable_type(self, t: CallableType) -> None: + # FIX generics + self.process_types(t.arg_types) + t.ret_type.accept(self) + + def visit_tuple_type(self, t: TupleType) -> None: + self.process_types(t.items) + + def visit_typeddict_type(self, t: TypedDictType) -> None: + self.process_types(list(t.items.values())) + + def visit_raw_expression_type(self, t: RawExpressionType) -> None: + pass + + def visit_literal_type(self, t: LiteralType) -> None: + pass + + def visit_union_type(self, t: UnionType) -> None: + self.process_types(t.items) + + def visit_overloaded(self, t: Overloaded) -> None: + self.process_types(t.items) # type: ignore[arg-type] + + def visit_type_type(self, t: TypeType) -> None: + t.item.accept(self) + + def visit_ellipsis_type(self, t: EllipsisType) -> None: + pass + + def visit_placeholder_type(self, t: PlaceholderType) -> None: + return self.process_types(t.args) + + def visit_type_alias_type(self, t: TypeAliasType) -> None: + # Skip type aliases in already visited types to avoid infinite recursion. + if self.seen_aliases is None: + self.seen_aliases = set() + elif t in self.seen_aliases: + return + self.seen_aliases.add(t) + self.process_types(t.args) + + def process_types(self, types: list[Type] | tuple[Type, ...]) -> None: + # Redundant type check helps mypyc. + if isinstance(types, list): + for t in types: + t.accept(self) + else: + for t in types: + t.accept(self) From b08c8b5737b3d4aa6f5f1e45c49dc1a5316679fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:58:56 -0800 Subject: [PATCH 0443/1617] Sync typeshed (#16721) Source commit: https://github.com/python/typeshed/commit/1d3c3265180ae4421be4ce6508810708ccd617f0 Co-authored-by: mypybot <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/VERSIONS | 445 +++++++++--------- mypy/typeshed/stdlib/_ast.pyi | 4 +- mypy/typeshed/stdlib/_thread.pyi | 5 + mypy/typeshed/stdlib/asyncore.pyi | 1 + mypy/typeshed/stdlib/base64.pyi | 4 +- .../stdlib/concurrent/futures/process.pyi | 59 ++- .../stdlib/concurrent/futures/thread.pyi | 31 +- mypy/typeshed/stdlib/http/client.pyi | 18 + mypy/typeshed/stdlib/os/__init__.pyi | 7 +- mypy/typeshed/stdlib/selectors.pyi | 38 +- mypy/typeshed/stdlib/threading.pyi | 9 +- mypy/typeshed/stdlib/unittest/_log.pyi | 5 +- mypy/typeshed/stdlib/unittest/case.pyi | 23 +- .../{zipfile.pyi => zipfile/__init__.pyi} | 5 +- mypy/typeshed/stdlib/zipfile/_path.pyi | 95 ++++ 15 files changed, 470 insertions(+), 279 deletions(-) rename mypy/typeshed/stdlib/{zipfile.pyi => zipfile/__init__.pyi} (98%) create mode 100644 mypy/typeshed/stdlib/zipfile/_path.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index f5e30c4602b4..5099a94c93bc 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -1,7 +1,7 @@ # The structure of this file is as follows: # - Blank lines and comments starting with `#` are ignored. # - Lines contain the name of a module, followed by a colon, -# a space, and a version range (for example: `symbol: 2.7-3.9`). +# a space, and a version range (for example: `symbol: 3.0-3.9`). # # Version ranges may be of the form "X.Y-A.B" or "X.Y-". The # first form means that a module was introduced in version X.Y and last @@ -12,57 +12,57 @@ # If a submodule is not listed separately, it has the same lifetime as # its parent module. # -# Python versions before 2.7 are ignored, so any module that was already -# present in 2.7 will have "2.7" as its minimum version. Version ranges +# Python versions before 3.0 are ignored, so any module that was already +# present in 3.0 will have "3.0" as its minimum version. Version ranges # for unsupported versions of Python 3 are generally accurate but we do # not guarantee their correctness. -__future__: 2.7- -__main__: 2.7- -_ast: 2.7- -_bisect: 2.7- +__future__: 3.0- +__main__: 3.0- +_ast: 3.0- +_bisect: 3.0- _bootlocale: 3.4-3.9 -_codecs: 2.7- +_codecs: 3.0- _collections_abc: 3.3- _compat_pickle: 3.1- _compression: 3.5- -_csv: 2.7- -_ctypes: 2.7- -_curses: 2.7- +_csv: 3.0- +_ctypes: 3.0- +_curses: 3.0- _decimal: 3.3- _dummy_thread: 3.0-3.8 -_dummy_threading: 2.7-3.8 -_heapq: 2.7- +_dummy_threading: 3.0-3.8 +_heapq: 3.0- _imp: 3.0- -_json: 2.7- -_locale: 2.7- -_markupbase: 2.7- -_msi: 2.7- +_json: 3.0- +_locale: 3.0- +_markupbase: 3.0- +_msi: 3.0- _operator: 3.4- -_osx_support: 2.7- +_osx_support: 3.0- _posixsubprocess: 3.2- _py_abc: 3.7- _pydecimal: 3.5- -_random: 2.7- +_random: 3.0- _sitebuiltins: 3.4- -_socket: 3.0- # present in 2.7 at runtime, but not in typeshed +_socket: 3.0- # present in 3.0 at runtime, but not in typeshed _stat: 3.4- -_thread: 2.7- -_threading_local: 2.7- -_tkinter: 2.7- +_thread: 3.0- +_threading_local: 3.0- +_tkinter: 3.0- _tracemalloc: 3.4- -_typeshed: 2.7- # not present at runtime, only for type checking -_warnings: 2.7- -_weakref: 2.7- -_weakrefset: 2.7- +_typeshed: 3.0- # not present at runtime, only for type checking +_warnings: 3.0- +_weakref: 3.0- +_weakrefset: 3.0- _winapi: 3.3- -abc: 2.7- -aifc: 2.7- -antigravity: 2.7- -argparse: 2.7- -array: 2.7- -ast: 2.7- -asynchat: 2.7-3.11 +abc: 3.0- +aifc: 3.0-3.12 +antigravity: 3.0- +argparse: 3.0- +array: 3.0- +ast: 3.0- +asynchat: 3.0-3.11 asyncio: 3.4- asyncio.mixins: 3.10- asyncio.exceptions: 3.8- @@ -73,83 +73,83 @@ asyncio.taskgroups: 3.11- asyncio.threads: 3.9- asyncio.timeouts: 3.11- asyncio.trsock: 3.8- -asyncore: 2.7-3.11 -atexit: 2.7- -audioop: 2.7- -base64: 2.7- -bdb: 2.7- -binascii: 2.7- -binhex: 2.7-3.10 -bisect: 2.7- +asyncore: 3.0-3.11 +atexit: 3.0- +audioop: 3.0-3.12 +base64: 3.0- +bdb: 3.0- +binascii: 3.0- +binhex: 3.0-3.10 +bisect: 3.0- builtins: 3.0- -bz2: 2.7- -cProfile: 2.7- -calendar: 2.7- -cgi: 2.7- -cgitb: 2.7- -chunk: 2.7- -cmath: 2.7- -cmd: 2.7- -code: 2.7- -codecs: 2.7- -codeop: 2.7- -collections: 2.7- +bz2: 3.0- +cProfile: 3.0- +calendar: 3.0- +cgi: 3.0-3.12 +cgitb: 3.0-3.12 +chunk: 3.0-3.12 +cmath: 3.0- +cmd: 3.0- +code: 3.0- +codecs: 3.0- +codeop: 3.0- +collections: 3.0- collections.abc: 3.3- -colorsys: 2.7- -compileall: 2.7- +colorsys: 3.0- +compileall: 3.0- concurrent: 3.2- configparser: 3.0- -contextlib: 2.7- +contextlib: 3.0- contextvars: 3.7- -copy: 2.7- -copyreg: 2.7- -crypt: 2.7- -csv: 2.7- -ctypes: 2.7- -curses: 2.7- +copy: 3.0- +copyreg: 3.0- +crypt: 3.0-3.12 +csv: 3.0- +ctypes: 3.0- +curses: 3.0- dataclasses: 3.7- -datetime: 2.7- -dbm: 2.7- -decimal: 2.7- -difflib: 2.7- -dis: 2.7- -distutils: 2.7-3.11 -distutils.command.bdist_msi: 2.7-3.10 -distutils.command.bdist_wininst: 2.7-3.9 -doctest: 2.7- -dummy_threading: 2.7-3.8 -email: 2.7- -encodings: 2.7- -ensurepip: 2.7- +datetime: 3.0- +dbm: 3.0- +decimal: 3.0- +difflib: 3.0- +dis: 3.0- +distutils: 3.0-3.11 +distutils.command.bdist_msi: 3.0-3.10 +distutils.command.bdist_wininst: 3.0-3.9 +doctest: 3.0- +dummy_threading: 3.0-3.8 +email: 3.0- +encodings: 3.0- +ensurepip: 3.0- enum: 3.4- -errno: 2.7- +errno: 3.0- faulthandler: 3.3- -fcntl: 2.7- -filecmp: 2.7- -fileinput: 2.7- -fnmatch: 2.7- -formatter: 2.7-3.9 -fractions: 2.7- -ftplib: 2.7- -functools: 2.7- -gc: 2.7- -genericpath: 2.7- -getopt: 2.7- -getpass: 2.7- -gettext: 2.7- -glob: 2.7- +fcntl: 3.0- +filecmp: 3.0- +fileinput: 3.0- +fnmatch: 3.0- +formatter: 3.0-3.9 +fractions: 3.0- +ftplib: 3.0- +functools: 3.0- +gc: 3.0- +genericpath: 3.0- +getopt: 3.0- +getpass: 3.0- +gettext: 3.0- +glob: 3.0- graphlib: 3.9- -grp: 2.7- -gzip: 2.7- -hashlib: 2.7- -heapq: 2.7- -hmac: 2.7- +grp: 3.0- +gzip: 3.0- +hashlib: 3.0- +heapq: 3.0- +hmac: 3.0- html: 3.0- http: 3.0- -imaplib: 2.7- -imghdr: 2.7- -imp: 2.7-3.11 -importlib: 2.7- +imaplib: 3.0- +imghdr: 3.0-3.12 +imp: 3.0-3.11 +importlib: 3.0- importlib._abc: 3.10- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- @@ -159,150 +159,151 @@ importlib.resources.abc: 3.11- importlib.resources.readers: 3.11- importlib.resources.simple: 3.11- importlib.simple: 3.11- -inspect: 2.7- -io: 2.7- +inspect: 3.0- +io: 3.0- ipaddress: 3.3- -itertools: 2.7- -json: 2.7- -keyword: 2.7- -lib2to3: 2.7- -linecache: 2.7- -locale: 2.7- -logging: 2.7- +itertools: 3.0- +json: 3.0- +keyword: 3.0- +lib2to3: 3.0- +linecache: 3.0- +locale: 3.0- +logging: 3.0- lzma: 3.3- -macpath: 2.7-3.7 -mailbox: 2.7- -mailcap: 2.7- -marshal: 2.7- -math: 2.7- -mimetypes: 2.7- -mmap: 2.7- -modulefinder: 2.7- -msilib: 2.7- -msvcrt: 2.7- -multiprocessing: 2.7- +macpath: 3.0-3.7 +mailbox: 3.0- +mailcap: 3.0-3.12 +marshal: 3.0- +math: 3.0- +mimetypes: 3.0- +mmap: 3.0- +modulefinder: 3.0- +msilib: 3.0-3.12 +msvcrt: 3.0- +multiprocessing: 3.0- multiprocessing.resource_tracker: 3.8- multiprocessing.shared_memory: 3.8- -netrc: 2.7- -nis: 2.7- -nntplib: 2.7- -nt: 2.7- -ntpath: 2.7- -nturl2path: 2.7- -numbers: 2.7- -opcode: 2.7- -operator: 2.7- -optparse: 2.7- -os: 2.7- -ossaudiodev: 2.7- -parser: 2.7-3.9 +netrc: 3.0- +nis: 3.0-3.12 +nntplib: 3.0-3.12 +nt: 3.0- +ntpath: 3.0- +nturl2path: 3.0- +numbers: 3.0- +opcode: 3.0- +operator: 3.0- +optparse: 3.0- +os: 3.0- +ossaudiodev: 3.0-3.12 +parser: 3.0-3.9 pathlib: 3.4- -pdb: 2.7- -pickle: 2.7- -pickletools: 2.7- -pipes: 2.7- -pkgutil: 2.7- -platform: 2.7- -plistlib: 2.7- -poplib: 2.7- -posix: 2.7- -posixpath: 2.7- -pprint: 2.7- -profile: 2.7- -pstats: 2.7- -pty: 2.7- -pwd: 2.7- -py_compile: 2.7- -pyclbr: 2.7- -pydoc: 2.7- -pydoc_data: 2.7- -pyexpat: 2.7- +pdb: 3.0- +pickle: 3.0- +pickletools: 3.0- +pipes: 3.0-3.12 +pkgutil: 3.0- +platform: 3.0- +plistlib: 3.0- +poplib: 3.0- +posix: 3.0- +posixpath: 3.0- +pprint: 3.0- +profile: 3.0- +pstats: 3.0- +pty: 3.0- +pwd: 3.0- +py_compile: 3.0- +pyclbr: 3.0- +pydoc: 3.0- +pydoc_data: 3.0- +pyexpat: 3.0- queue: 3.0- -quopri: 2.7- -random: 2.7- -re: 2.7- -readline: 2.7- +quopri: 3.0- +random: 3.0- +re: 3.0- +readline: 3.0- reprlib: 3.0- -resource: 2.7- -rlcompleter: 2.7- -runpy: 2.7- -sched: 2.7- +resource: 3.0- +rlcompleter: 3.0- +runpy: 3.0- +sched: 3.0- secrets: 3.6- -select: 2.7- +select: 3.0- selectors: 3.4- -shelve: 2.7- -shlex: 2.7- -shutil: 2.7- -signal: 2.7- -site: 2.7- -smtpd: 2.7-3.11 -smtplib: 2.7- -sndhdr: 2.7- -socket: 2.7- +shelve: 3.0- +shlex: 3.0- +shutil: 3.0- +signal: 3.0- +site: 3.0- +smtpd: 3.0-3.11 +smtplib: 3.0- +sndhdr: 3.0-3.12 +socket: 3.0- socketserver: 3.0- -spwd: 2.7- -sqlite3: 2.7- -sre_compile: 2.7- -sre_constants: 2.7- -sre_parse: 2.7- -ssl: 2.7- -stat: 2.7- +spwd: 3.0-3.12 +sqlite3: 3.0- +sre_compile: 3.0- +sre_constants: 3.0- +sre_parse: 3.0- +ssl: 3.0- +stat: 3.0- statistics: 3.4- -string: 2.7- -stringprep: 2.7- -struct: 2.7- -subprocess: 2.7- -sunau: 2.7- -symbol: 2.7-3.9 -symtable: 2.7- -sys: 2.7- +string: 3.0- +stringprep: 3.0- +struct: 3.0- +subprocess: 3.0- +sunau: 3.0-3.12 +symbol: 3.0-3.9 +symtable: 3.0- +sys: 3.0- sys._monitoring: 3.12- # Doesn't actually exist. See comments in the stub. -sysconfig: 2.7- -syslog: 2.7- -tabnanny: 2.7- -tarfile: 2.7- -telnetlib: 2.7- -tempfile: 2.7- -termios: 2.7- -textwrap: 2.7- -this: 2.7- -threading: 2.7- -time: 2.7- -timeit: 2.7- +sysconfig: 3.0- +syslog: 3.0- +tabnanny: 3.0- +tarfile: 3.0- +telnetlib: 3.0-3.12 +tempfile: 3.0- +termios: 3.0- +textwrap: 3.0- +this: 3.0- +threading: 3.0- +time: 3.0- +timeit: 3.0- tkinter: 3.0- -token: 2.7- -tokenize: 2.7- +token: 3.0- +tokenize: 3.0- tomllib: 3.11- -trace: 2.7- -traceback: 2.7- +trace: 3.0- +traceback: 3.0- tracemalloc: 3.4- -tty: 2.7- -turtle: 2.7- -types: 2.7- +tty: 3.0- +turtle: 3.0- +types: 3.0- typing: 3.5- -typing_extensions: 2.7- -unicodedata: 2.7- -unittest: 2.7- +typing_extensions: 3.0- +unicodedata: 3.0- +unittest: 3.0- unittest._log: 3.9- unittest.async_case: 3.8- -urllib: 2.7- -uu: 2.7- -uuid: 2.7- +urllib: 3.0- +uu: 3.0-3.12 +uuid: 3.0- venv: 3.3- -warnings: 2.7- -wave: 2.7- -weakref: 2.7- -webbrowser: 2.7- +warnings: 3.0- +wave: 3.0- +weakref: 3.0- +webbrowser: 3.0- winreg: 3.0- -winsound: 2.7- -wsgiref: 2.7- +winsound: 3.0- +wsgiref: 3.0- wsgiref.types: 3.11- -xdrlib: 2.7- -xml: 2.7- +xdrlib: 3.0-3.12 +xml: 3.0- xmlrpc: 3.0- xxlimited: 3.2- zipapp: 3.5- -zipfile: 2.7- -zipimport: 2.7- -zlib: 2.7- +zipfile: 3.0- +zipfile._path: 3.12- +zipimport: 3.0- +zlib: 3.0- zoneinfo: 3.9- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 0302133fc6f9..0fbf66f7feda 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -586,7 +586,9 @@ if sys.version_info >= (3, 10): patterns: list[pattern] if sys.version_info >= (3, 12): - class type_param(AST): ... + class type_param(AST): + end_lineno: int + end_col_offset: int class TypeVar(type_param): __match_args__ = ("name", "bound") diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index dba8664fbf13..8bd0b179607b 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -46,3 +46,8 @@ if sys.version_info >= (3, 8): if sys.version_info >= (3, 12): def daemon_threads_allowed() -> bool: ... + +class _local: + def __getattribute__(self, __name: str) -> Any: ... + def __setattr__(self, __name: str, __value: Any) -> None: ... + def __delattr__(self, __name: str) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncore.pyi b/mypy/typeshed/stdlib/asyncore.pyi index 47c8e2207022..36d1862fdda7 100644 --- a/mypy/typeshed/stdlib/asyncore.pyi +++ b/mypy/typeshed/stdlib/asyncore.pyi @@ -83,6 +83,7 @@ if sys.platform != "win32": def write(self, data: bytes, flags: int = ...) -> int: ... def close(self) -> None: ... def fileno(self) -> int: ... + def __del__(self) -> None: ... class file_dispatcher(dispatcher): def __init__(self, fd: FileDescriptorLike, map: _MapType | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/base64.pyi b/mypy/typeshed/stdlib/base64.pyi index 24830cbfba04..4629c95d0949 100644 --- a/mypy/typeshed/stdlib/base64.pyi +++ b/mypy/typeshed/stdlib/base64.pyi @@ -27,13 +27,13 @@ if sys.version_info >= (3, 10): __all__ += ["b32hexencode", "b32hexdecode"] def b64encode(s: ReadableBuffer, altchars: ReadableBuffer | None = None) -> bytes: ... -def b64decode(s: str | ReadableBuffer, altchars: ReadableBuffer | None = None, validate: bool = False) -> bytes: ... +def b64decode(s: str | ReadableBuffer, altchars: str | ReadableBuffer | None = None, validate: bool = False) -> bytes: ... def standard_b64encode(s: ReadableBuffer) -> bytes: ... def standard_b64decode(s: str | ReadableBuffer) -> bytes: ... def urlsafe_b64encode(s: ReadableBuffer) -> bytes: ... def urlsafe_b64decode(s: str | ReadableBuffer) -> bytes: ... def b32encode(s: ReadableBuffer) -> bytes: ... -def b32decode(s: str | ReadableBuffer, casefold: bool = False, map01: bytes | None = None) -> bytes: ... +def b32decode(s: str | ReadableBuffer, casefold: bool = False, map01: str | ReadableBuffer | None = None) -> bytes: ... def b16encode(s: ReadableBuffer) -> bytes: ... def b16decode(s: str | ReadableBuffer, casefold: bool = False) -> bytes: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index 000e7a43503a..d3706a9c15a6 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -5,12 +5,14 @@ from multiprocessing.context import BaseContext, Process from multiprocessing.queues import Queue, SimpleQueue from threading import Lock, Semaphore, Thread from types import TracebackType -from typing import Any, Generic, TypeVar +from typing import Any, Generic, TypeVar, overload +from typing_extensions import TypeVarTuple, Unpack from weakref import ref from ._base import BrokenExecutor, Executor, Future _T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts") _threads_wakeups: MutableMapping[Any, Any] _global_shutdown: bool @@ -109,8 +111,8 @@ if sys.version_info >= (3, 11): def _process_worker( call_queue: Queue[_CallItem], result_queue: SimpleQueue[_ResultItem], - initializer: Callable[..., object] | None, - initargs: tuple[Any, ...], + initializer: Callable[[Unpack[_Ts]], object] | None, + initargs: tuple[Unpack[_Ts]], max_tasks: int | None = None, ) -> None: ... @@ -118,8 +120,8 @@ else: def _process_worker( call_queue: Queue[_CallItem], result_queue: SimpleQueue[_ResultItem], - initializer: Callable[..., object] | None, - initargs: tuple[Any, ...], + initializer: Callable[[Unpack[_Ts]], object] | None, + initargs: tuple[Unpack[_Ts]], ) -> None: ... if sys.version_info >= (3, 9): @@ -169,22 +171,61 @@ class ProcessPoolExecutor(Executor): _result_queue: SimpleQueue[Any] _work_ids: Queue[Any] if sys.version_info >= (3, 11): + @overload def __init__( self, max_workers: int | None = None, mp_context: BaseContext | None = None, - initializer: Callable[..., object] | None = None, - initargs: tuple[Any, ...] = (), + initializer: Callable[[], object] | None = None, + initargs: tuple[()] = (), + *, + max_tasks_per_child: int | None = None, + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None = None, + mp_context: BaseContext | None = None, + *, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + max_tasks_per_child: int | None = None, + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None, + mp_context: BaseContext | None, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], *, max_tasks_per_child: int | None = None, ) -> None: ... else: + @overload + def __init__( + self, + max_workers: int | None = None, + mp_context: BaseContext | None = None, + initializer: Callable[[], object] | None = None, + initargs: tuple[()] = (), + ) -> None: ... + @overload def __init__( self, max_workers: int | None = None, mp_context: BaseContext | None = None, - initializer: Callable[..., object] | None = None, - initargs: tuple[Any, ...] = (), + *, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None, + mp_context: BaseContext | None, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], ) -> None: ... if sys.version_info >= (3, 9): def _start_executor_manager_thread(self) -> None: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi index 0b00d524aa3d..f38cf2c57963 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi @@ -2,11 +2,14 @@ import queue import sys from collections.abc import Callable, Iterable, Mapping, Set as AbstractSet from threading import Lock, Semaphore, Thread -from typing import Any, Generic, TypeVar +from typing import Any, Generic, TypeVar, overload +from typing_extensions import TypeVarTuple, Unpack from weakref import ref from ._base import BrokenExecutor, Executor, Future +_Ts = TypeVarTuple("_Ts") + _threads_queues: Mapping[Any, Any] _shutdown: bool _global_shutdown_lock: Lock @@ -31,8 +34,8 @@ class _WorkItem(Generic[_S]): def _worker( executor_reference: ref[Any], work_queue: queue.SimpleQueue[Any], - initializer: Callable[..., object], - initargs: tuple[Any, ...], + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], ) -> None: ... class BrokenThreadPool(BrokenExecutor): ... @@ -48,12 +51,30 @@ class ThreadPoolExecutor(Executor): _initializer: Callable[..., None] | None _initargs: tuple[Any, ...] _work_queue: queue.SimpleQueue[_WorkItem[Any]] + @overload def __init__( self, max_workers: int | None = None, thread_name_prefix: str = "", - initializer: Callable[..., object] | None = None, - initargs: tuple[Any, ...] = (), + initializer: Callable[[], object] | None = None, + initargs: tuple[()] = (), + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None = None, + thread_name_prefix: str = "", + *, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None, + thread_name_prefix: str, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], ) -> None: ... def _adjust_thread_count(self) -> None: ... def _initializer_failed(self) -> None: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 20223695a1a8..fb5450730f60 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -99,6 +99,24 @@ responses: dict[int, str] class HTTPMessage(email.message.Message): def getallmatchingheaders(self, name: str) -> list[str]: ... # undocumented + # override below all of Message's methods that use `_HeaderType` / `_HeaderTypeParam` with `str` + # `HTTPMessage` breaks the Liskov substitution principle by only intending for `str` headers + # This is easier than making `Message` generic + def __getitem__(self, name: str) -> str | None: ... + def __setitem__(self, name: str, val: str) -> None: ... # type: ignore[override] + def values(self) -> list[str]: ... + def items(self) -> list[tuple[str, str]]: ... + @overload + def get(self, name: str, failobj: None = None) -> str | None: ... + @overload + def get(self, name: str, failobj: _T) -> str | _T: ... + @overload + def get_all(self, name: str, failobj: None = None) -> list[str] | None: ... + @overload + def get_all(self, name: str, failobj: _T) -> list[str] | _T: ... + def replace_header(self, _name: str, _value: str) -> None: ... # type: ignore[override] + def set_raw(self, name: str, value: str) -> None: ... # type: ignore[override] + def raw_items(self) -> Iterator[tuple[str, str]]: ... def parse_headers(fp: io.BufferedIOBase, _class: Callable[[], email.message.Message] = ...) -> HTTPMessage: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 7d4b8adcd00a..e92dd7a095d6 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -33,6 +33,9 @@ from . import path as _path if sys.version_info >= (3, 9): from types import GenericAlias +if sys.platform != "win32": + from resource import struct_rusage + # This unnecessary alias is to work around various errors path = _path @@ -962,8 +965,8 @@ else: def waitid(__idtype: int, __ident: int, __options: int) -> waitid_result | None: ... - def wait3(options: int) -> tuple[int, int, Any]: ... - def wait4(pid: int, options: int) -> tuple[int, int, Any]: ... + def wait3(options: int) -> tuple[int, int, struct_rusage]: ... + def wait4(pid: int, options: int) -> tuple[int, int, struct_rusage]: ... def WCOREDUMP(__status: int) -> bool: ... def WIFCONTINUED(status: int) -> bool: ... def WIFSTOPPED(status: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index 043df9253316..a857d0e242ab 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -31,49 +31,37 @@ class BaseSelector(metaclass=ABCMeta): def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... -class SelectSelector(BaseSelector): +class _BaseSelectorImpl(BaseSelector, metaclass=ABCMeta): def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... - def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... + def modify(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... +class SelectSelector(_BaseSelectorImpl): + def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... + +class _PollLikeSelector(_BaseSelectorImpl): + def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... + if sys.platform != "win32": - class PollSelector(BaseSelector): - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... - def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... + class PollSelector(_PollLikeSelector): ... if sys.platform == "linux": - class EpollSelector(BaseSelector): + class EpollSelector(_PollLikeSelector): def fileno(self) -> int: ... - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... - def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... -class DevpollSelector(BaseSelector): +class DevpollSelector(_PollLikeSelector): def fileno(self) -> int: ... - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = ...) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... - def select(self, timeout: float | None = ...) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... if sys.platform != "win32": - class KqueueSelector(BaseSelector): + class KqueueSelector(_BaseSelectorImpl): def fileno(self) -> int: ... - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... # Not a real class at runtime, it is just a conditional alias to other real selectors. # The runtime logic is more fine-grained than a `sys.platform` check; # not really expressible in the stubs -class DefaultSelector(BaseSelector): - def register(self, fileobj: FileDescriptorLike, events: _EventMask, data: Any = None) -> SelectorKey: ... - def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... +class DefaultSelector(_BaseSelectorImpl): def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... if sys.platform != "win32": def fileno(self) -> int: ... diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index badd09cae051..bcd73823c63f 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -1,3 +1,4 @@ +import _thread import sys from _typeshed import ProfileFunction, TraceFunction from collections.abc import Callable, Iterable, Mapping @@ -68,12 +69,8 @@ def stack_size(size: int = ...) -> int: ... TIMEOUT_MAX: float -class ThreadError(Exception): ... - -class local: - def __getattribute__(self, __name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... - def __delattr__(self, __name: str) -> None: ... +ThreadError = _thread.error +local = _thread._local class Thread: name: str diff --git a/mypy/typeshed/stdlib/unittest/_log.pyi b/mypy/typeshed/stdlib/unittest/_log.pyi index 4de5d502e004..011a970d8bbc 100644 --- a/mypy/typeshed/stdlib/unittest/_log.pyi +++ b/mypy/typeshed/stdlib/unittest/_log.pyi @@ -2,7 +2,7 @@ import logging import sys from types import TracebackType from typing import ClassVar, Generic, NamedTuple, TypeVar -from unittest.case import TestCase +from unittest.case import TestCase, _BaseTestCaseContext _L = TypeVar("_L", None, _LoggingWatcher) @@ -10,9 +10,8 @@ class _LoggingWatcher(NamedTuple): records: list[logging.LogRecord] output: list[str] -class _AssertLogsContext(Generic[_L]): +class _AssertLogsContext(_BaseTestCaseContext, Generic[_L]): LOGGING_FORMAT: ClassVar[str] - test_case: TestCase logger_name: str level: int msg: None diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index 7efe6cdc9662..d66f027324f1 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -25,8 +25,26 @@ _P = ParamSpec("_P") DIFF_OMITTED: str class _BaseTestCaseContext: + test_case: TestCase def __init__(self, test_case: TestCase) -> None: ... +class _AssertRaisesBaseContext(_BaseTestCaseContext): + expected: type[BaseException] | tuple[type[BaseException], ...] + expected_regex: Pattern[str] | None + obj_name: str | None + msg: str | None + + def __init__( + self, + expected: type[BaseException] | tuple[type[BaseException], ...], + test_case: TestCase, + expected_regex: str | Pattern[str] | None = None, + ) -> None: ... + + # This returns Self if args is the empty list, and None otherwise. + # but it's not possible to construct an overload which expresses that + def handle(self, name: str, args: list[Any], kwargs: dict[str, Any]) -> Any: ... + if sys.version_info >= (3, 9): from unittest._log import _AssertLogsContext, _LoggingWatcher else: @@ -41,7 +59,6 @@ else: class _AssertLogsContext(_BaseTestCaseContext, Generic[_L]): LOGGING_FORMAT: ClassVar[str] - test_case: TestCase logger_name: str level: int msg: None @@ -310,7 +327,7 @@ class FunctionTestCase(TestCase): def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... -class _AssertRaisesContext(Generic[_E]): +class _AssertRaisesContext(_AssertRaisesBaseContext, Generic[_E]): exception: _E def __enter__(self) -> Self: ... def __exit__( @@ -319,7 +336,7 @@ class _AssertRaisesContext(Generic[_E]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... -class _AssertWarnsContext: +class _AssertWarnsContext(_AssertRaisesBaseContext): warning: WarningMessage filename: str lineno: int diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi similarity index 98% rename from mypy/typeshed/stdlib/zipfile.pyi rename to mypy/typeshed/stdlib/zipfile/__init__.pyi index 5483b84fe6f6..83982be3be08 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -227,7 +227,10 @@ class ZipInfo: def is_dir(self) -> bool: ... def FileHeader(self, zip64: bool | None = None) -> bytes: ... -if sys.version_info >= (3, 8): +if sys.version_info >= (3, 12): + from zipfile._path import CompleteDirs as CompleteDirs, Path as Path + +elif sys.version_info >= (3, 8): class CompleteDirs(ZipFile): def resolve_dir(self, name: str) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi new file mode 100644 index 000000000000..3cf034aec8f4 --- /dev/null +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -0,0 +1,95 @@ +import sys +from _typeshed import StrPath +from collections.abc import Iterator, Sequence +from io import TextIOWrapper +from os import PathLike +from typing import IO, overload +from typing_extensions import Literal, Self, TypeAlias +from zipfile import ZipFile + +_ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] + +if sys.version_info >= (3, 12): + class InitializedState: + def __init__(self, *args: object, **kwargs: object) -> None: ... + def __getstate__(self) -> tuple[list[object], dict[object, object]]: ... + def __setstate__(self, state: Sequence[tuple[list[object], dict[object, object]]]) -> None: ... + + class CompleteDirs(InitializedState, ZipFile): + def resolve_dir(self, name: str) -> str: ... + @overload + @classmethod + def make(cls, source: ZipFile) -> CompleteDirs: ... + @overload + @classmethod + def make(cls, source: StrPath | IO[bytes]) -> Self: ... + + class Path: + root: CompleteDirs + def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... + @property + def name(self) -> str: ... + @property + def parent(self) -> PathLike[str]: ... # undocumented + if sys.version_info >= (3, 10): + @property + def filename(self) -> PathLike[str]: ... # undocumented + if sys.version_info >= (3, 11): + @property + def suffix(self) -> str: ... + @property + def suffixes(self) -> list[str]: ... + @property + def stem(self) -> str: ... + + if sys.version_info >= (3, 9): + @overload + def open( + self, + mode: Literal["r", "w"] = "r", + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = ..., + write_through: bool = ..., + *, + pwd: bytes | None = None, + ) -> TextIOWrapper: ... + @overload + def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... + else: + def open( + self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False + ) -> IO[bytes]: ... + + if sys.version_info >= (3, 10): + def iterdir(self) -> Iterator[Self]: ... + else: + def iterdir(self) -> Iterator[Path]: ... + + def is_dir(self) -> bool: ... + def is_file(self) -> bool: ... + def exists(self) -> bool: ... + def read_text( + self, + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + line_buffering: bool = ..., + write_through: bool = ..., + ) -> str: ... + def read_bytes(self) -> bytes: ... + if sys.version_info >= (3, 10): + def joinpath(self, *other: StrPath) -> Path: ... + else: + def joinpath(self, add: StrPath) -> Path: ... # undocumented + if sys.version_info >= (3, 12): + def glob(self, pattern: str) -> Iterator[Self]: ... + def rglob(self, pattern: str) -> Iterator[Self]: ... + def is_symlink(self) -> Literal[False]: ... + def relative_to(self, other: Path, *extra: StrPath) -> str: ... + def match(self, path_pattern: str) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + + def __truediv__(self, add: StrPath) -> Path: ... From 4aba5ca8450ad3f06805f2bd6dfcd06048a3f1d2 Mon Sep 17 00:00:00 2001 From: Fabian Lewis <49647154+frobian21@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:26:30 +0100 Subject: [PATCH 0444/1617] Fix xml writing bug introduced in #16388 (#16713) #16388 introduced a bug where invalid xml could be produced by `write_junit_xml`, as special characters were no longer being escaped. The fix is to revert the removal of `from xml.sax.saxutils import escape` and `escape("\n".join(messages))` in the `write_junit_xml` function. I've added a small test case which checks that the `<`, `>`, `&` are escaped correctly in the xml. --- mypy/test/testutil.py | 22 ++++++++++++++++++++++ mypy/util.py | 6 ++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mypy/test/testutil.py b/mypy/test/testutil.py index d0d54ffec8c6..a7c3f1c00fee 100644 --- a/mypy/test/testutil.py +++ b/mypy/test/testutil.py @@ -31,6 +31,28 @@ def test_junit_pass(self) -> None: +""" + result = _generate_junit_contents( + dt=1.23, + serious=serious, + messages_by_file=messages_by_file, + version="3.14", + platform="test-plat", + ) + assert result == expected + + def test_junit_fail_escape_xml_chars(self) -> None: + serious = False + messages_by_file: dict[str | None, list[str]] = { + "file1.py": ["Test failed", "another line < > &"] + } + expected = """ + + + Test failed +another line < > & + + """ result = _generate_junit_contents( dt=1.23, diff --git a/mypy/util.py b/mypy/util.py index be8a22d08a27..0277a96a6885 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -263,6 +263,8 @@ def _generate_junit_contents( version: str, platform: str, ) -> str: + from xml.sax.saxutils import escape + if serious: failures = 0 errors = len(messages_by_file) @@ -284,7 +286,7 @@ def _generate_junit_contents( for filename, messages in messages_by_file.items(): if filename is not None: xml += JUNIT_TESTCASE_FAIL_TEMPLATE.format( - text="\n".join(messages), + text=escape("\n".join(messages)), filename=filename, time=dt, name="mypy-py{ver}-{platform} {filename}".format( @@ -293,7 +295,7 @@ def _generate_junit_contents( ) else: xml += JUNIT_TESTCASE_FAIL_TEMPLATE.format( - text="\n".join(messages), + text=escape("\n".join(messages)), filename="mypy", time=dt, name="mypy-py{ver}-{platform}".format(ver=version, platform=platform), From d0d5876d876272c56a339c628197935906c57e3c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 1 Jan 2024 19:47:02 -0800 Subject: [PATCH 0445/1617] Fix various lint (#16726) --- .pre-commit-config.yaml | 2 +- mypy/binder.py | 2 +- mypy/build.py | 10 ++++------ mypy/checker.py | 7 +++---- mypy/checkexpr.py | 6 ++---- mypy/checkpattern.py | 2 +- mypy/checkstrformat.py | 4 ++-- mypy/dmypy_server.py | 2 +- mypy/dmypy_util.py | 2 +- mypy/errors.py | 2 +- mypy/main.py | 6 +++--- mypy/message_registry.py | 2 +- mypy/messages.py | 2 +- mypy/nodes.py | 6 +++--- mypy/patterns.py | 14 ++++++++------ mypy/plugins/enums.py | 4 ++-- mypy/renaming.py | 2 +- mypy/semanal.py | 10 ++++------ mypy/semanal_enum.py | 2 +- mypy/semanal_namedtuple.py | 2 +- mypy/semanal_typeddict.py | 2 +- mypy/server/astmerge.py | 4 ++-- mypy/stubdoc.py | 2 +- mypy/stubgenc.py | 8 ++++---- mypy/stubtest.py | 16 ++++++---------- mypy/stubutil.py | 2 +- mypy/test/helpers.py | 4 ++-- mypy/test/testcheck.py | 5 ++++- mypy/test/testformatter.py | 4 ++-- mypy/test/teststubtest.py | 4 ++-- mypy/typeanal.py | 2 +- mypy/types.py | 2 +- mypy/typestate.py | 4 ++-- mypy/util.py | 2 +- mypyc/codegen/emitmodule.py | 2 +- mypyc/codegen/emitwrapper.py | 4 ++-- mypyc/irbuild/prepare.py | 6 +++--- mypyc/primitives/registry.py | 20 +++++++++++++++----- pyproject.toml | 9 +++++++-- test-requirements.in | 2 +- test-requirements.txt | 2 +- 41 files changed, 102 insertions(+), 93 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd2a09b7a8cf..4090bf0ecb4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.0 # must match test-requirements.txt + rev: v0.1.4 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/mypy/binder.py b/mypy/binder.py index 3b67d09f16c3..9d0a33b54bc2 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -291,7 +291,7 @@ def assign_type( self.type_assignments[expr].append((type, declared_type)) return if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): - return None + return if not literal(expr): return self.invalidate_dependencies(expr) diff --git a/mypy/build.py b/mypy/build.py index b3ca8d06916d..8049fa2d0c3f 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -682,9 +682,7 @@ def __init__( # for efficient lookup self.shadow_map: dict[str, str] = {} if self.options.shadow_file is not None: - self.shadow_map = { - source_file: shadow_file for (source_file, shadow_file) in self.options.shadow_file - } + self.shadow_map = dict(self.options.shadow_file) # a mapping from each file being typechecked to its possible shadow file self.shadow_equivalence_map: dict[str, str | None] = {} self.plugin = plugin @@ -1120,7 +1118,7 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta] module_deps_metas = deps_meta["deps_meta"] assert isinstance(module_deps_metas, dict) if not manager.options.skip_cache_mtime_checks: - for id, meta in module_deps_metas.items(): + for meta in module_deps_metas.values(): try: matched = manager.getmtime(meta["path"]) == meta["mtime"] except FileNotFoundError: @@ -2093,7 +2091,7 @@ def load_tree(self, temporary: bool = False) -> None: self.meta.data_json, self.manager, "Load tree ", "Could not load tree: " ) if data is None: - return None + return t0 = time.time() # TODO: Assert data file wasn't changed. @@ -3383,7 +3381,7 @@ def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> strongly_connected_components() below for a reference. """ if len(ascc) == 1: - return [s for s in ascc] + return list(ascc) pri_spread = set() for id in ascc: state = graph[id] diff --git a/mypy/checker.py b/mypy/checker.py index 0ac8f6904973..1b57ef780104 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -632,7 +632,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: if not defn.items: # In this case we have already complained about none of these being # valid overloads. - return None + return if len(defn.items) == 1: self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, defn) @@ -676,7 +676,6 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: self.msg.no_overridable_method(defn.name, defn) self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl) self.check_inplace_operator_method(defn) - return None def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> CallableType | None: """Get type as seen by an overload item caller.""" @@ -1838,7 +1837,7 @@ def check_match_args(self, var: Var, typ: Type, context: Context) -> None: return typ = get_proper_type(typ) if not isinstance(typ, TupleType) or not all( - [is_string_literal(item) for item in typ.items] + is_string_literal(item) for item in typ.items ): self.msg.note( "__match_args__ must be a tuple containing string literals for checking " @@ -5045,7 +5044,7 @@ def visit_break_stmt(self, s: BreakStmt) -> None: def visit_continue_stmt(self, s: ContinueStmt) -> None: self.binder.handle_continue() - return None + return def visit_match_stmt(self, s: MatchStmt) -> None: with self.binder.frame_context(can_skip=False, fall_through=0): diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 626584bc3a20..3af8d70e78c9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -739,7 +739,7 @@ def check_typeddict_call( context: Context, orig_callee: Type | None, ) -> Type: - if args and all([ak in (ARG_NAMED, ARG_STAR2) for ak in arg_kinds]): + if args and all(ak in (ARG_NAMED, ARG_STAR2) for ak in arg_kinds): # ex: Point(x=42, y=1337, **extras) # This is a bit ugly, but this is a price for supporting all possible syntax # variants for TypedDict constructors. @@ -4017,9 +4017,7 @@ def check_op( left_variants = [base_type] base_type = get_proper_type(base_type) if isinstance(base_type, UnionType): - left_variants = [ - item for item in flatten_nested_unions(base_type.relevant_items()) - ] + left_variants = list(flatten_nested_unions(base_type.relevant_items())) right_type = self.accept(arg) # Step 1: We first try leaving the right arguments alone and destructure diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index c0061f1c3e72..3210dcc3b7ac 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -187,7 +187,7 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType: capture_types[node].append((expr, typ)) captures: dict[Expression, Type] = {} - for var, capture_list in capture_types.items(): + for capture_list in capture_types.values(): typ = UninhabitedType() for _, other in capture_list: typ = join_types(typ, other) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 39d44e84a9c1..c63210a96c44 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -372,7 +372,7 @@ def check_specs_in_format_call( ): # TODO: add support for some custom specs like datetime? self.msg.fail( - "Unrecognized format" ' specification "{}"'.format(spec.format_spec[1:]), + f'Unrecognized format specification "{spec.format_spec[1:]}"', call, code=codes.STRING_FORMATTING, ) @@ -482,7 +482,7 @@ def find_replacements_in_call(self, call: CallExpr, keys: list[str]) -> list[Exp expr = self.get_expr_by_name(key, call) if not expr: self.msg.fail( - "Cannot find replacement for named" ' format specifier "{}"'.format(key), + f'Cannot find replacement for named format specifier "{key}"', call, code=codes.STRING_FORMATTING, ) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 42236497f275..b4c3fe8fe0dc 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -1055,7 +1055,7 @@ def fix_module_deps(graph: mypy.build.Graph) -> None: This can make some suppressed dependencies non-suppressed, and vice versa (if modules have been added to or removed from the build). """ - for module, state in graph.items(): + for state in graph.values(): new_suppressed = [] new_dependencies = [] for dep in state.dependencies + state.suppressed: diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index d95cba9f40b5..fe949e8fc294 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -43,7 +43,7 @@ def send(connection: IPCBase, data: Any) -> None: class WriteToConn: """Helper class to write to a connection instead of standard output.""" - def __init__(self, server: IPCBase, output_key: str = "stdout"): + def __init__(self, server: IPCBase, output_key: str = "stdout") -> None: self.server = server self.output_key = output_key diff --git a/mypy/errors.py b/mypy/errors.py index 6e90c28d9c03..eabe96a2dc73 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -170,7 +170,7 @@ def __init__( *, filter_errors: bool | Callable[[str, ErrorInfo], bool] = False, save_filtered_errors: bool = False, - ): + ) -> None: self.errors = errors self._has_new_errors = False self._filter = filter_errors diff --git a/mypy/main.py b/mypy/main.py index 2c68466ec977..b32624456ce0 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -145,7 +145,7 @@ def main( sys.exit(code) # HACK: keep res alive so that mypyc won't free it before the hard_exit - list([res]) + list([res]) # noqa: C410 def run_build( @@ -349,7 +349,7 @@ class CapturableArgumentParser(argparse.ArgumentParser): yet output must be captured to properly support mypy.api.run. """ - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: self.stdout = kwargs.pop("stdout", sys.stdout) self.stderr = kwargs.pop("stderr", sys.stderr) super().__init__(*args, **kwargs) @@ -415,7 +415,7 @@ def __init__( default: str = argparse.SUPPRESS, help: str = "show program's version number and exit", stdout: IO[str] | None = None, - ): + ) -> None: super().__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help ) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 8dc14e158d90..fb430b63c74b 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -52,7 +52,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: '"return" with value in async generator is not allowed' ) INVALID_RETURN_TYPE_FOR_GENERATOR: Final = ErrorMessage( - 'The return type of a generator function should be "Generator"' " or one of its supertypes" + 'The return type of a generator function should be "Generator" or one of its supertypes' ) INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = ErrorMessage( 'The return type of an async generator function should be "AsyncGenerator" or one of its ' diff --git a/mypy/messages.py b/mypy/messages.py index 069c4d51e281..450faf4c1688 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -3099,7 +3099,7 @@ def append_invariance_notes( ): invariant_type = "Dict" covariant_suggestion = ( - 'Consider using "Mapping" instead, ' "which is covariant in the value type" + 'Consider using "Mapping" instead, which is covariant in the value type' ) if invariant_type and covariant_suggestion: notes.append( diff --git a/mypy/nodes.py b/mypy/nodes.py index 17e06613d1e3..fe41777c55f7 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3137,7 +3137,7 @@ def protocol_members(self) -> list[str]: if name in EXCLUDED_PROTOCOL_ATTRIBUTES: continue members.add(name) - return sorted(list(members)) + return sorted(members) def __getitem__(self, name: str) -> SymbolTableNode: n = self.get(name) @@ -3296,7 +3296,7 @@ def serialize(self) -> JsonDict: else self.typeddict_type.serialize(), "flags": get_flags(self, TypeInfo.FLAGS), "metadata": self.metadata, - "slots": list(sorted(self.slots)) if self.slots is not None else None, + "slots": sorted(self.slots) if self.slots is not None else None, "deletable_attributes": self.deletable_attributes, "self_type": self.self_type.serialize() if self.self_type is not None else None, "dataclass_transform_spec": ( @@ -3966,7 +3966,7 @@ def __init__( # frozen_default was added to CPythonin https://github.com/python/cpython/pull/99958 citing # positive discussion in typing-sig frozen_default: bool | None = None, - ): + ) -> None: self.eq_default = eq_default if eq_default is not None else True self.order_default = order_default if order_default is not None else False self.kw_only_default = kw_only_default if kw_only_default is not None else False diff --git a/mypy/patterns.py b/mypy/patterns.py index 839864ef5879..a01bf6acc876 100644 --- a/mypy/patterns.py +++ b/mypy/patterns.py @@ -60,7 +60,7 @@ class ValuePattern(Pattern): expr: Expression - def __init__(self, expr: Expression): + def __init__(self, expr: Expression) -> None: super().__init__() self.expr = expr @@ -72,7 +72,7 @@ class SingletonPattern(Pattern): # This can be exactly True, False or None value: bool | None - def __init__(self, value: bool | None): + def __init__(self, value: bool | None) -> None: super().__init__() self.value = value @@ -85,7 +85,7 @@ class SequencePattern(Pattern): patterns: list[Pattern] - def __init__(self, patterns: list[Pattern]): + def __init__(self, patterns: list[Pattern]) -> None: super().__init__() self.patterns = patterns @@ -98,7 +98,7 @@ class StarredPattern(Pattern): # a name. capture: NameExpr | None - def __init__(self, capture: NameExpr | None): + def __init__(self, capture: NameExpr | None) -> None: super().__init__() self.capture = capture @@ -111,7 +111,9 @@ class MappingPattern(Pattern): values: list[Pattern] rest: NameExpr | None - def __init__(self, keys: list[Expression], values: list[Pattern], rest: NameExpr | None): + def __init__( + self, keys: list[Expression], values: list[Pattern], rest: NameExpr | None + ) -> None: super().__init__() assert len(keys) == len(values) self.keys = keys @@ -136,7 +138,7 @@ def __init__( positionals: list[Pattern], keyword_keys: list[str], keyword_values: list[Pattern], - ): + ) -> None: super().__init__() assert len(keyword_keys) == len(keyword_values) self.class_ref = class_ref diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 7869a8b5cdfa..2c568f66c62d 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -166,11 +166,11 @@ class SomeEnum: for n in stnodes if n is None or not n.implicit ) - proper_types = list( + proper_types = [ _infer_value_type_with_auto_fallback(ctx, t) for t in node_types if t is None or not isinstance(t, CallableType) - ) + ] underlying_type = _first(proper_types) if underlying_type is None: return ctx.default_attr_type diff --git a/mypy/renaming.py b/mypy/renaming.py index c960eb4b1ce8..8db336205960 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -270,7 +270,7 @@ def flush_refs(self) -> None: This will be called at the end of a scope. """ is_func = self.scope_kinds[-1] == FUNCTION - for name, refs in self.refs[-1].items(): + for refs in self.refs[-1].values(): if len(refs) == 1: # Only one definition -- no renaming needed. continue diff --git a/mypy/semanal.py b/mypy/semanal.py index 48be004daf76..e0a3db2bff1b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4153,7 +4153,7 @@ def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> if len(call.args) < 1: self.fail(f"Too few arguments for {typevarlike_type}()", context) return False - if not isinstance(call.args[0], StrExpr) or not call.arg_kinds[0] == ARG_POS: + if not isinstance(call.args[0], StrExpr) or call.arg_kinds[0] != ARG_POS: self.fail(f"{typevarlike_type}() expects a string literal as first argument", context) return False elif call.args[0].value != name: @@ -4961,9 +4961,7 @@ def visit_name_expr(self, expr: NameExpr) -> None: def bind_name_expr(self, expr: NameExpr, sym: SymbolTableNode) -> None: """Bind name expression to a symbol table node.""" if isinstance(sym.node, TypeVarExpr) and self.tvar_scope.get_binding(sym): - self.fail( - '"{}" is a type variable and only valid in type ' "context".format(expr.name), expr - ) + self.fail(f'"{expr.name}" is a type variable and only valid in type context', expr) elif isinstance(sym.node, PlaceholderNode): self.process_placeholder(expr.name, "name", expr) else: @@ -6809,13 +6807,13 @@ def parse_dataclass_transform_spec(self, call: CallExpr) -> DataclassTransformSp def parse_dataclass_transform_field_specifiers(self, arg: Expression) -> tuple[str, ...]: if not isinstance(arg, TupleExpr): self.fail('"field_specifiers" argument must be a tuple literal', arg) - return tuple() + return () names = [] for specifier in arg.items: if not isinstance(specifier, RefExpr): self.fail('"field_specifiers" must only contain identifiers', specifier) - return tuple() + return () names.append(specifier.fullname) return tuple(names) diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 528b0519cca1..21576ab47a84 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -148,7 +148,7 @@ def parse_enum_call_args( Return a tuple of fields, values, was there an error. """ args = call.args - if not all([arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds]): + if not all(arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds): return self.fail_enum_call_arg(f"Unexpected arguments to {class_name}()", call) if len(args) < 2: return self.fail_enum_call_arg(f"Too few arguments for {class_name}()", call) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index bc3c5dd61894..9a0be9d9c14c 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -85,7 +85,7 @@ ) NAMEDTUP_CLASS_ERROR: Final = ( - "Invalid statement in NamedTuple definition; " 'expected "field_name: field_type [= default]"' + 'Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]"' ) SELF_TVAR_NAME: Final = "_NT" diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 13aab4de65e4..67c05fd74273 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -50,7 +50,7 @@ ) TPDICT_CLASS_ERROR: Final = ( - "Invalid statement in TypedDict definition; " 'expected "field_name: field_type"' + 'Invalid statement in TypedDict definition; expected "field_name: field_type"' ) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 862c3898a383..174c2922c767 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -394,7 +394,7 @@ def process_synthetic_type_info(self, info: TypeInfo) -> None: # have bodies in the AST so we need to iterate over their symbol # tables separately, unlike normal classes. self.process_type_info(info) - for name, node in info.names.items(): + for node in info.names.values(): if node.node: node.node.accept(self) @@ -549,7 +549,7 @@ def fixup(self, node: SN) -> SN: def replace_nodes_in_symbol_table( symbols: SymbolTable, replacements: dict[SymbolNode, SymbolNode] ) -> None: - for name, node in symbols.items(): + for node in symbols.values(): if node.node: if node.node in replacements: new = replacements[node.node] diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 86ff6e2bb540..8c0a4dab696f 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -325,7 +325,7 @@ def args_kwargs(signature: FunctionSig) -> bool: return has_arg("*args", signature) and has_arg("**kwargs", signature) # Move functions with (*args, **kwargs) in their signature to last place. - return list(sorted(self.signatures, key=lambda x: 1 if args_kwargs(x) else 0)) + return sorted(self.signatures, key=lambda x: 1 if args_kwargs(x) else 0) def infer_sig_from_docstring(docstr: str | None, name: str) -> list[FunctionSig] | None: diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 39288197f477..55e46fe0ec25 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -41,7 +41,7 @@ class ExternalSignatureGenerator(SignatureGenerator): def __init__( self, func_sigs: dict[str, str] | None = None, class_sigs: dict[str, str] | None = None - ): + ) -> None: """ Takes a mapping of function/method names to signatures and class name to class signatures (usually corresponds to __init__). @@ -187,7 +187,7 @@ class CFunctionStub: Class that mimics a C function in order to provide parseable docstrings. """ - def __init__(self, name: str, doc: str, is_abstract: bool = False): + def __init__(self, name: str, doc: str, is_abstract: bool = False) -> None: self.__name__ = name self.__doc__ = doc self.__abstractmethod__ = is_abstract @@ -404,7 +404,7 @@ def generate_module(self) -> None: if self.should_reexport(name, obj_module_name, name_is_alias=False): self.import_tracker.reexport(name) - self.set_defined_names(set([name for name, obj in all_items if not inspect.ismodule(obj)])) + self.set_defined_names({name for name, obj in all_items if not inspect.ismodule(obj)}) if self.resort_members: functions: list[str] = [] @@ -765,7 +765,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> items = self.get_members(cls) if self.resort_members: items = sorted(items, key=lambda x: method_name_sort_key(x[0])) - names = set(x[0] for x in items) + names = {x[0] for x in items} methods: list[str] = [] types: list[str] = [] static_properties: list[str] = [] diff --git a/mypy/stubtest.py b/mypy/stubtest.py index c02a3efd8dc0..e7cc24f33d18 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -303,11 +303,9 @@ def _verify_exported_names( # desirable in at least the `names_in_runtime_not_stub` case stub_object=MISSING, runtime_object=MISSING, - stub_desc=( - f"Names exported in the stub but not at runtime: " f"{names_in_stub_not_runtime}" - ), + stub_desc=(f"Names exported in the stub but not at runtime: {names_in_stub_not_runtime}"), runtime_desc=( - f"Names exported at runtime but not in the stub: " f"{names_in_runtime_not_stub}" + f"Names exported at runtime but not in the stub: {names_in_runtime_not_stub}" ), ) @@ -677,7 +675,7 @@ def _verify_arg_default_value( runtime_type is not None and stub_type is not None # Avoid false positives for marker objects - and type(runtime_arg.default) != object + and type(runtime_arg.default) is not object # And ellipsis and runtime_arg.default is not ... and not is_subtype_helper(runtime_type, stub_type) @@ -897,7 +895,7 @@ def _verify_signature( runtime_arg.kind == inspect.Parameter.POSITIONAL_ONLY and not stub_arg.pos_only and not stub_arg.variable.name.startswith("__") - and not stub_arg.variable.name.strip("_") == "self" + and stub_arg.variable.name.strip("_") != "self" and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods ): yield ( @@ -1812,10 +1810,8 @@ def get_importable_stdlib_modules() -> set[str]: # test.* modules do weird things like raising exceptions in __del__ methods, # leading to unraisable exceptions being logged to the terminal # as a warning at the end of the stubtest run - if ( - submodule_name.endswith(".__main__") - or submodule_name.startswith("idlelib.") - or submodule_name.startswith("test.") + if submodule_name.endswith(".__main__") or submodule_name.startswith( + ("idlelib.", "test.") ): continue diff --git a/mypy/stubutil.py b/mypy/stubutil.py index b8d601ed3c6b..e4a97964c547 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -558,7 +558,7 @@ def __init__( include_private: bool = False, export_less: bool = False, include_docstrings: bool = False, - ): + ) -> None: # Best known value of __all__. self._all_ = _all_ self._include_private = include_private diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index dc34931427ec..bae4f6e81ad1 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -145,7 +145,7 @@ def assert_module_equivalence(name: str, expected: Iterable[str], actual: Iterab assert_string_arrays_equal( expected_normalized, actual_normalized, - ("Actual modules ({}) do not match expected modules ({}) " 'for "[{} ...]"').format( + ('Actual modules ({}) do not match expected modules ({}) for "[{} ...]"').format( ", ".join(actual_normalized), ", ".join(expected_normalized), name ), ) @@ -156,7 +156,7 @@ def assert_target_equivalence(name: str, expected: list[str], actual: list[str]) assert_string_arrays_equal( expected, actual, - ("Actual targets ({}) do not match expected targets ({}) " 'for "[{} ...]"').format( + ('Actual targets ({}) do not match expected targets ({}) for "[{} ...]"').format( ", ".join(actual), ", ".join(expected), name ), ) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 3ad97ced61f2..5fba6192dcaf 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -29,6 +29,7 @@ except ImportError: lxml = None + import pytest # List of files that contain test case descriptions. @@ -99,9 +100,11 @@ def _filename(_msg: str) -> str: def run_case_once( self, testcase: DataDrivenTestCase, - operations: list[FileOperation] = [], + operations: list[FileOperation] | None = None, incremental_step: int = 0, ) -> None: + if operations is None: + operations = [] original_program_text = "\n".join(testcase.input) module_data = self.parse_module(original_program_text, incremental_step) diff --git a/mypy/test/testformatter.py b/mypy/test/testformatter.py index f64527e7804a..9f8bb5d82408 100644 --- a/mypy/test/testformatter.py +++ b/mypy/test/testformatter.py @@ -52,14 +52,14 @@ def test_trim_source(self) -> None: def test_split_words(self) -> None: assert split_words("Simple message") == ["Simple", "message"] - assert split_words('Message with "Some[Long, Types]"' " in it") == [ + assert split_words('Message with "Some[Long, Types]" in it') == [ "Message", "with", '"Some[Long, Types]"', "in", "it", ] - assert split_words('Message with "Some[Long, Types]"' " and [error-code]") == [ + assert split_words('Message with "Some[Long, Types]" and [error-code]') == [ "Message", "with", '"Some[Long, Types]"', diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 34b266115166..72b6f6620f83 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -173,7 +173,7 @@ def run_stubtest( class Case: - def __init__(self, stub: str, runtime: str, error: str | None): + def __init__(self, stub: str, runtime: str, error: str | None) -> None: self.stub = stub self.runtime = runtime self.error = error @@ -2226,7 +2226,7 @@ def also_bad(asdf): pass options=["--allowlist", allowlist.name, "--generate-allowlist"], ) assert output == ( - f"note: unused allowlist entry unused.*\n" f"{TEST_MODULE_NAME}.also_bad\n" + f"note: unused allowlist entry unused.*\n{TEST_MODULE_NAME}.also_bad\n" ) finally: os.unlink(allowlist.name) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 4d916315bddd..8a840424f76f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2319,7 +2319,7 @@ def visit_unbound_type(self, t: UnboundType) -> None: # Special case P.args and P.kwargs for ParamSpecs only. if name.endswith("args"): - if name.endswith(".args") or name.endswith(".kwargs"): + if name.endswith((".args", ".kwargs")): base = ".".join(name.split(".")[:-1]) n = self.api.lookup_qualified(base, t) if n is not None and isinstance(n.node, ParamSpecExpr): diff --git a/mypy/types.py b/mypy/types.py index fcdb61f9719b..f02e56a677ae 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -441,7 +441,7 @@ class TypeGuardedType(Type): __slots__ = ("type_guard",) - def __init__(self, type_guard: Type): + def __init__(self, type_guard: Type) -> None: super().__init__(line=type_guard.line, column=type_guard.column) self.type_guard = type_guard diff --git a/mypy/typestate.py b/mypy/typestate.py index b32fb0ef6df1..c5a5da03eae5 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -192,7 +192,7 @@ def record_subtype_cache_entry( # These are unlikely to match, due to the large space of # possible values. Avoid uselessly increasing cache sizes. return - cache = self._subtype_caches.setdefault(right.type, dict()) + cache = self._subtype_caches.setdefault(right.type, {}) cache.setdefault(kind, set()).add((left, right)) def record_negative_subtype_cache_entry( @@ -204,7 +204,7 @@ def record_negative_subtype_cache_entry( return if len(self._negative_subtype_caches) > MAX_NEGATIVE_CACHE_TYPES: self._negative_subtype_caches.clear() - cache = self._negative_subtype_caches.setdefault(right.type, dict()) + cache = self._negative_subtype_caches.setdefault(right.type, {}) subcache = cache.setdefault(kind, set()) if len(subcache) > MAX_NEGATIVE_CACHE_ENTRIES: subcache.clear() diff --git a/mypy/util.py b/mypy/util.py index 0277a96a6885..968774ee7c98 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -451,7 +451,7 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 8): + if sys.version_info[:2] < (3, 8): # noqa: UP036 sys.exit( "Running {name} with Python 3.7 or lower is not supported; " "please upgrade to 3.8 or newer".format(name=program) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index caf2058ea7c4..6c0dfd43b9af 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -993,7 +993,7 @@ def _toposort_visit(name: str) -> None: result.append(decl.declaration) decl.mark = True - for name, marked_declaration in marked_declarations.items(): + for name in marked_declarations: _toposort_visit(name) return result diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py index 791e856c274a..45c6c7a05867 100644 --- a/mypyc/codegen/emitwrapper.py +++ b/mypyc/codegen/emitwrapper.py @@ -145,7 +145,7 @@ def generate_wrapper_function( real_args = list(fn.args) if fn.sig.num_bitmap_args: real_args = real_args[: -fn.sig.num_bitmap_args] - if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD: + if fn.class_name and fn.decl.kind != FUNC_STATICMETHOD: arg = real_args.pop(0) emitter.emit_line(f"PyObject *obj_{arg.name} = self;") @@ -238,7 +238,7 @@ def generate_legacy_wrapper_function( real_args = list(fn.args) if fn.sig.num_bitmap_args: real_args = real_args[: -fn.sig.num_bitmap_args] - if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD: + if fn.class_name and fn.decl.kind != FUNC_STATICMETHOD: arg = real_args.pop(0) emitter.emit_line(f"PyObject *obj_{arg.name} = self;") diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 5e6520048197..29e06439abdd 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -139,7 +139,7 @@ def is_from_module(node: SymbolNode, module: MypyFile) -> bool: def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps) -> None: """Populate a Mapper with deserialized IR from a list of modules.""" for module in modules: - for name, node in module.names.items(): + for node in module.names.values(): if isinstance(node.node, TypeInfo) and is_from_module(node.node, module): ir = deser_ctx.classes[node.node.fullname] mapper.type_to_ir[node.node] = ir @@ -153,7 +153,7 @@ def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps) def get_module_func_defs(module: MypyFile) -> Iterable[FuncDef]: """Collect all of the (non-method) functions declared in a module.""" - for name, node in module.names.items(): + for node in module.names.values(): # We need to filter out functions that are imported or # aliases. The best way to do this seems to be by # checking that the fullname matches. @@ -468,7 +468,7 @@ def prepare_non_ext_class_def( ir = mapper.type_to_ir[cdef.info] info = cdef.info - for name, node in info.names.items(): + for node in info.names.values(): if isinstance(node.node, (FuncDef, Decorator)): prepare_method_def(ir, module_name, cdef, mapper, node.node) elif isinstance(node.node, OverloadedFuncDef): diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index aa96b35aec56..11fca7dc2c70 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -93,7 +93,7 @@ def method_op( var_arg_type: RType | None = None, truncated_type: RType | None = None, ordering: list[int] | None = None, - extra_int_constants: list[tuple[int, RType]] = [], + extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, @@ -122,6 +122,8 @@ def method_op( is_borrowed: if True, returned value is borrowed (no need to decrease refcount) priority: if multiple ops match, the one with the highest priority is picked """ + if extra_int_constants is None: + extra_int_constants = [] ops = method_call_ops.setdefault(name, []) desc = CFunctionDescription( name, @@ -150,7 +152,7 @@ def function_op( var_arg_type: RType | None = None, truncated_type: RType | None = None, ordering: list[int] | None = None, - extra_int_constants: list[tuple[int, RType]] = [], + extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, @@ -165,6 +167,8 @@ def function_op( name: full name of the function arg_types: positional argument types for which this applies """ + if extra_int_constants is None: + extra_int_constants = [] ops = function_ops.setdefault(name, []) desc = CFunctionDescription( name, @@ -193,7 +197,7 @@ def binary_op( var_arg_type: RType | None = None, truncated_type: RType | None = None, ordering: list[int] | None = None, - extra_int_constants: list[tuple[int, RType]] = [], + extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, @@ -205,6 +209,8 @@ def binary_op( Most arguments are similar to method_op(), but exactly two argument types are expected. """ + if extra_int_constants is None: + extra_int_constants = [] ops = binary_ops.setdefault(name, []) desc = CFunctionDescription( name, @@ -232,7 +238,7 @@ def custom_op( var_arg_type: RType | None = None, truncated_type: RType | None = None, ordering: list[int] | None = None, - extra_int_constants: list[tuple[int, RType]] = [], + extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, ) -> CFunctionDescription: @@ -240,6 +246,8 @@ def custom_op( Most arguments are similar to method_op(). """ + if extra_int_constants is None: + extra_int_constants = [] return CFunctionDescription( "", arg_types, @@ -264,7 +272,7 @@ def unary_op( error_kind: int, truncated_type: RType | None = None, ordering: list[int] | None = None, - extra_int_constants: list[tuple[int, RType]] = [], + extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, @@ -276,6 +284,8 @@ def unary_op( Most arguments are similar to method_op(), but exactly one argument type is expected. """ + if extra_int_constants is None: + extra_int_constants = [] ops = unary_ops.setdefault(name, []) desc = CFunctionDescription( name, diff --git a/pyproject.toml b/pyproject.toml index c43253fed982..5fc0cee3f65c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,24 +33,29 @@ fix = true select = [ "E", # pycodestyle (error) "F", # pyflakes + "W", # pycodestyle (warning) "B", # flake8-bugbear "I", # isort "RUF100", # Unused noqa comments "PGH004", # blanket noqa comments "UP", # pyupgrade + "C4", # flake8-comprehensions + "SIM201", "SIM202", # simplify comparisons involving not + "ISC001", # implicitly concatenated string + "RET501", "RET502", # better return None handling ] ignore = [ - "B006", # use of mutable defaults in function signatures "B007", # Loop control variable not used within the loop body. "B011", # Don't use assert False "B023", # Function definition does not bind loop variable - "E203", # conflicts with black + "E2", # conflicts with black "E402", # module level import not at top of file "E501", # conflicts with black "E731", # Do not assign a `lambda` expression, use a `def` "E741", # Ambiguous variable name "UP032", # 'f-string always preferable to format' is controversial + "C416", # There are a few cases where it's nice to have names for the dict items ] unfixable = [ diff --git a/test-requirements.in b/test-requirements.in index bab3ece29c02..2bf8de0aa2f5 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -14,6 +14,6 @@ psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.1.0 # must match version in .pre-commit-config.yaml +ruff==0.1.4 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 diff --git a/test-requirements.txt b/test-requirements.txt index 3bb9cf29635f..57607c1bae57 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -67,7 +67,7 @@ ruamel-yaml==0.17.40 # via pre-commit-hooks ruamel-yaml-clib==0.2.8 # via ruamel-yaml -ruff==0.1.0 +ruff==0.1.4 # via -r test-requirements.in tomli==2.0.1 # via -r test-requirements.in From f9e8e0bda5cfbb54d6a8f9e482aa25da28a1a635 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 1 Jan 2024 20:15:15 -0800 Subject: [PATCH 0446/1617] Allow unary + in Literal (#16729) Implements python/typing#1550 Fixes #16727 (a trivial bug I found while implementing this feature) --- mypy/fastparse.py | 16 +++++++++++----- test-data/unit/check-literal.test | 11 ++++++++--- test-data/unit/fixtures/ops.pyi | 4 ++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index cba01eab2e4e..13b9b5c8a871 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -126,7 +126,7 @@ import ast as ast3 # TODO: Index, ExtSlice are deprecated in 3.9. -from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UnaryOp, USub +from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UAdd, UnaryOp, USub def ast3_parse( @@ -1940,13 +1940,19 @@ def visit_Constant(self, n: Constant) -> Type: # UnaryOp(op, operand) def visit_UnaryOp(self, n: UnaryOp) -> Type: - # We support specifically Literal[-4] and nothing else. - # For example, Literal[+4] or Literal[~6] is not supported. + # We support specifically Literal[-4], Literal[+4], and nothing else. + # For example, Literal[~6] or Literal[not False] is not supported. typ = self.visit(n.operand) - if isinstance(typ, RawExpressionType) and isinstance(n.op, USub): - if isinstance(typ.literal_value, int): + if ( + isinstance(typ, RawExpressionType) + # Use type() because we do not want to allow bools. + and type(typ.literal_value) is int # noqa: E721 + ): + if isinstance(n.op, USub): typ.literal_value *= -1 return typ + if isinstance(n.op, UAdd): + return typ return self.invalid_type(n) def numeric_type(self, value: object, n: AST) -> Type: diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index d9ad68385ad1..ea2aa5068e85 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -591,7 +591,6 @@ from typing_extensions import Literal def dummy() -> int: return 3 a: Literal[3 + 4] # E: Invalid type: Literal[...] cannot contain arbitrary expressions b: Literal[" foo ".trim()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions -c: Literal[+42] # E: Invalid type: Literal[...] cannot contain arbitrary expressions d: Literal[~12] # E: Invalid type: Literal[...] cannot contain arbitrary expressions e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions [builtins fixtures/tuple.pyi] @@ -2739,7 +2738,7 @@ def test() -> None: ... [builtins fixtures/bool.pyi] -[case testNegativeIntLiteral] +[case testUnaryOpLiteral] from typing_extensions import Literal a: Literal[-2] = -2 @@ -2747,8 +2746,14 @@ b: Literal[-1] = -1 c: Literal[0] = 0 d: Literal[1] = 1 e: Literal[2] = 2 +f: Literal[+1] = 1 +g: Literal[+2] = 2 + +x: Literal[+True] = True # E: Invalid type: Literal[...] cannot contain arbitrary expressions +y: Literal[-True] = -1 # E: Invalid type: Literal[...] cannot contain arbitrary expressions +z: Literal[~0] = 0 # E: Invalid type: Literal[...] cannot contain arbitrary expressions [out] -[builtins fixtures/float.pyi] +[builtins fixtures/ops.pyi] [case testNegativeIntLiteralWithFinal] from typing_extensions import Literal, Final diff --git a/test-data/unit/fixtures/ops.pyi b/test-data/unit/fixtures/ops.pyi index 9cc4d22eb0a7..df3b163166ad 100644 --- a/test-data/unit/fixtures/ops.pyi +++ b/test-data/unit/fixtures/ops.pyi @@ -24,8 +24,6 @@ class tuple(Sequence[Tco]): class function: pass -class bool: pass - class str: def __init__(self, x: 'int') -> None: pass def __add__(self, x: 'str') -> 'str': pass @@ -54,6 +52,8 @@ class int: def __gt__(self, x: 'int') -> bool: pass def __ge__(self, x: 'int') -> bool: pass +class bool(int): pass + class float: def __add__(self, x: 'float') -> 'float': pass def __radd__(self, x: 'float') -> 'float': pass From a1b4e32785b69291748579d97cb9456e1198ec21 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:02:15 -0800 Subject: [PATCH 0447/1617] Document how evil `--no-strict-optional` is (#16731) On multiple occasions, I've encountered folks using this, running into issues and then being perplexed when they figure out what `--no-strict-optional` actually does. Most recently in https://github.com/python/mypy/issues/16718#issuecomment-1873374058 This is a non-standard, dangerous option that should not be used in modern typed Python. It's been five and a half years since it was the default behaviour in mypy, so we should deemphasise and warn about its existence. --- docs/source/command_line.rst | 16 ++++---- docs/source/common_issues.rst | 2 +- docs/source/config_file.rst | 9 ++++- docs/source/kinds_of_types.rst | 68 ---------------------------------- 4 files changed, 17 insertions(+), 78 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index c8e0ef3df11f..4a7ead3e8724 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -415,7 +415,6 @@ None and Optional handling ************************** The following flags adjust how mypy handles values of type ``None``. -For more details, see :ref:`no_strict_optional`. .. _implicit-optional: @@ -435,16 +434,19 @@ For more details, see :ref:`no_strict_optional`. **Note:** This was disabled by default starting in mypy 0.980. +.. _no_strict_optional: + .. option:: --no-strict-optional - This flag disables strict checking of :py:data:`~typing.Optional` + This flag effectively disables checking of :py:data:`~typing.Optional` types and ``None`` values. With this option, mypy doesn't - generally check the use of ``None`` values -- they are valid - everywhere. See :ref:`no_strict_optional` for more about this feature. + generally check the use of ``None`` values -- it is treated + as compatible with every type. + + .. warning:: - **Note:** Strict optional checking was enabled by default starting in - mypy 0.600, and in previous versions it had to be explicitly enabled - using ``--strict-optional`` (which is still accepted). + ``--no-strict-optional`` is evil. Avoid using it and definitely do + not use it without understanding what it does. .. _configuring-warnings: diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index e6570b7eef5b..8cc18c863e45 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -119,7 +119,7 @@ and mypy doesn't complain**. return None # No error! You may have disabled strict optional checking (see -:ref:`no_strict_optional` for more). +:ref:`--no-strict-optional ` for more). .. _silencing_checker: diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index de769200bf2b..910b015df658 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -580,10 +580,15 @@ section of the command line docs. :type: boolean :default: True - Enables or disables strict Optional checks. If False, mypy treats ``None`` + Effectively disables checking of :py:data:`~typing.Optional` + types and ``None`` values. With this option, mypy doesn't + generally check the use of ``None`` values -- it is treated as compatible with every type. - **Note:** This was False by default in mypy versions earlier than 0.600. + .. warning:: + + ``strict_optional = false`` is evil. Avoid using it and definitely do + not use it without understanding what it does. Configuring warnings diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index c3180850f119..d07eb40753f3 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -429,74 +429,6 @@ the runtime with some limitations (see :ref:`runtime_troubles`). t2: int | None # equivalent to Optional[int] -.. _no_strict_optional: - -Disabling strict optional checking -********************************** - -Mypy also has an option to treat ``None`` as a valid value for every -type (in case you know Java, it's useful to think of it as similar to -the Java ``null``). In this mode ``None`` is also valid for primitive -types such as ``int`` and ``float``, and :py:data:`~typing.Optional` types are -not required. - -The mode is enabled through the :option:`--no-strict-optional ` command-line -option. In mypy versions before 0.600 this was the default mode. You -can enable this option explicitly for backward compatibility with -earlier mypy versions, in case you don't want to introduce optional -types to your codebase yet. - -It will cause mypy to silently accept some buggy code, such as -this example -- it's not recommended if you can avoid it: - -.. code-block:: python - - def inc(x: int) -> int: - return x + 1 - - x = inc(None) # No error reported by mypy if strict optional mode disabled! - -However, making code "optional clean" can take some work! You can also use -:ref:`the mypy configuration file ` to migrate your code -to strict optional checking one file at a time, since there exists -the per-module flag -:confval:`strict_optional` to control strict optional mode. - -Often it's still useful to document whether a variable can be -``None``. For example, this function accepts a ``None`` argument, -but it's not obvious from its signature: - -.. code-block:: python - - def greeting(name: str) -> str: - if name: - return f'Hello, {name}' - else: - return 'Hello, stranger' - - print(greeting('Python')) # Okay! - print(greeting(None)) # Also okay! - -You can still use :py:data:`Optional[t] ` to document that ``None`` is a -valid argument type, even if strict ``None`` checking is not -enabled: - -.. code-block:: python - - from typing import Optional - - def greeting(name: Optional[str]) -> str: - if name: - return f'Hello, {name}' - else: - return 'Hello, stranger' - -Mypy treats this as semantically equivalent to the previous example -if strict optional checking is disabled, since ``None`` is implicitly -valid for any type, but it's much more -useful for a programmer who is reading the code. This also makes -it easier to migrate to strict ``None`` checking in the future. - .. _type-aliases: Type aliases From fbb738a4626976f83a7412df73533d87483a31b7 Mon Sep 17 00:00:00 2001 From: Ihor <31508183+nautics889@users.noreply.github.com> Date: Thu, 4 Jan 2024 09:40:24 +0200 Subject: [PATCH 0448/1617] [minor] Fix arg name in classmethod (#16741) --- mypy/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index fe41777c55f7..6c23c51dfcd3 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1155,7 +1155,7 @@ def serialize(self) -> JsonDict: } @classmethod - def deserialize(self, data: JsonDict) -> ClassDef: + def deserialize(cls, data: JsonDict) -> ClassDef: assert data[".class"] == "ClassDef" res = ClassDef( data["name"], From 35f402c74205d373b81076ea82f507eb85161a25 Mon Sep 17 00:00:00 2001 From: WeilerMarcel Date: Mon, 8 Jan 2024 10:08:32 +0100 Subject: [PATCH 0449/1617] Support type stub generation for `staticmethod` (#14934) Fixes #13574 This PR fixes the generation of type hints for static methods of pybind11 classes. The code changes are based on the suggestions in #13574. The fix introduces an additional check if the property under inspection is of type `staticmethod`. If it is, the type information is read from the staticmethod's `__func__` attribute, instead of the staticmethod instance itself. I added a test for C++ classes with static methods bound using pybind11. Both, an overloaded and a non-overloaded static method are tested. --- mypy/stubgenc.py | 16 ++++++---- test-data/pybind11_mypy_demo/src/main.cpp | 15 ++++++++++ .../pybind11_mypy_demo/basics.pyi | 30 +++++++++++++++++++ .../stubgen/pybind11_mypy_demo/basics.pyi | 11 +++++++ 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 55e46fe0ec25..3bec0c246d9a 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -530,12 +530,14 @@ def is_classmethod(self, class_info: ClassInfo, name: str, obj: object) -> bool: return inspect.ismethod(obj) def is_staticmethod(self, class_info: ClassInfo | None, name: str, obj: object) -> bool: - if self.is_c_module: + if class_info is None: return False + elif self.is_c_module: + raw_lookup: Mapping[str, Any] = getattr(class_info.cls, "__dict__") # noqa: B009 + raw_value = raw_lookup.get(name, obj) + return isinstance(raw_value, staticmethod) else: - return class_info is not None and isinstance( - inspect.getattr_static(class_info.cls, name), staticmethod - ) + return isinstance(inspect.getattr_static(class_info.cls, name), staticmethod) @staticmethod def is_abstract_method(obj: object) -> bool: @@ -761,7 +763,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> The result lines will be appended to 'output'. If necessary, any required names will be added to 'imports'. """ - raw_lookup = getattr(cls, "__dict__") # noqa: B009 + raw_lookup: Mapping[str, Any] = getattr(cls, "__dict__") # noqa: B009 items = self.get_members(cls) if self.resort_members: items = sorted(items, key=lambda x: method_name_sort_key(x[0])) @@ -793,7 +795,9 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> continue attr = "__init__" # FIXME: make this nicer - if self.is_classmethod(class_info, attr, value): + if self.is_staticmethod(class_info, attr, value): + class_info.self_var = "" + elif self.is_classmethod(class_info, attr, value): class_info.self_var = "cls" else: class_info.self_var = "self" diff --git a/test-data/pybind11_mypy_demo/src/main.cpp b/test-data/pybind11_mypy_demo/src/main.cpp index 192a90cf8e30..8be759d51671 100644 --- a/test-data/pybind11_mypy_demo/src/main.cpp +++ b/test-data/pybind11_mypy_demo/src/main.cpp @@ -118,6 +118,13 @@ const Point Point::y_axis = Point(0, 1); Point::LengthUnit Point::length_unit = Point::LengthUnit::mm; Point::AngleUnit Point::angle_unit = Point::AngleUnit::radian; +struct Foo +{ + static int some_static_method(int a, int b) { return a * 42 + b; } + static int overloaded_static_method(int value) { return value * 42; } + static double overloaded_static_method(double value) { return value * 42; } +}; + } // namespace: basics void bind_basics(py::module& basics) { @@ -166,6 +173,14 @@ void bind_basics(py::module& basics) { .value("radian", Point::AngleUnit::radian) .value("degree", Point::AngleUnit::degree); + // Static methods + py::class_ pyFoo(basics, "Foo"); + + pyFoo + .def_static("some_static_method", &Foo::some_static_method, R"#(None)#", py::arg("a"), py::arg("b")) + .def_static("overloaded_static_method", py::overload_cast(&Foo::overloaded_static_method), py::arg("value")) + .def_static("overloaded_static_method", py::overload_cast(&Foo::overloaded_static_method), py::arg("value")); + // Module-level attributes basics.attr("PI") = std::acos(-1); basics.attr("__version__") = "0.0.1"; diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi index b761291e11f3..3047da622a3e 100644 --- a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi @@ -3,6 +3,36 @@ from typing import ClassVar, List, overload PI: float __version__: str +class Foo: + def __init__(self, *args, **kwargs) -> None: + """Initialize self. See help(type(self)) for accurate signature.""" + @overload + @staticmethod + def overloaded_static_method(value: int) -> int: + """overloaded_static_method(*args, **kwargs) + Overloaded function. + + 1. overloaded_static_method(value: int) -> int + + 2. overloaded_static_method(value: float) -> float + """ + @overload + @staticmethod + def overloaded_static_method(value: float) -> float: + """overloaded_static_method(*args, **kwargs) + Overloaded function. + + 1. overloaded_static_method(value: int) -> int + + 2. overloaded_static_method(value: float) -> float + """ + @staticmethod + def some_static_method(a: int, b: int) -> int: + """some_static_method(a: int, b: int) -> int + + None + """ + class Point: class AngleUnit: __members__: ClassVar[dict] = ... # read-only diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi index 6f164a03edcc..f3acb1677e68 100644 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi @@ -3,6 +3,17 @@ from typing import ClassVar, List, overload PI: float __version__: str +class Foo: + def __init__(self, *args, **kwargs) -> None: ... + @overload + @staticmethod + def overloaded_static_method(value: int) -> int: ... + @overload + @staticmethod + def overloaded_static_method(value: float) -> float: ... + @staticmethod + def some_static_method(a: int, b: int) -> int: ... + class Point: class AngleUnit: __members__: ClassVar[dict] = ... # read-only From edebe024cbe8406cdf191cefe111683d0c2849ea Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:08:49 -0800 Subject: [PATCH 0450/1617] Accept multiline quoted annotations (#16765) See discussion in https://discuss.python.org/t/newlines-within-triple-quoted-type-expressions/42833 --- mypy/fastparse.py | 2 +- test-data/unit/check-basic.test | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 13b9b5c8a871..b3e0fc70a9d6 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -318,7 +318,7 @@ def parse_type_string( string expression "blah" using this function. """ try: - _, node = parse_type_comment(expr_string.strip(), line=line, column=column, errors=None) + _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None) if isinstance(node, UnboundType) and node.original_str_expr is None: node.original_str_expr = expr_string node.original_str_fallback = expr_fallback_name diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 61a7160ce4f4..7a426c3eca9f 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -502,3 +502,19 @@ s2: str = 42 # E: Incompatible types in assignment (expression has type "int", s3: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [file c.py] s3: str = 'foo' + +[case testMultilineQuotedAnnotation] +x: """ + + int | + str + +""" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +y: """( + int | + str +) +""" +reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" From 1fd29ac54a2135ec1d1d30289c625d12e6198eac Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:27:46 -0800 Subject: [PATCH 0451/1617] stubtest: fix pos-only handling in overload resolution (#16750) --- mypy/stubtest.py | 7 ++++- mypy/test/teststubtest.py | 58 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index e7cc24f33d18..6061e98bd7cd 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -826,7 +826,10 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg # argument. To accomplish this, we just make up a fake index-based name. name = ( f"__{index}" - if arg.variable.name.startswith("__") or assume_positional_only + if arg.variable.name.startswith("__") + or arg.pos_only + or assume_positional_only + or arg.variable.name.strip("_") == "self" else arg.variable.name ) all_args.setdefault(name, []).append((arg, index)) @@ -870,6 +873,7 @@ def get_kind(arg_name: str) -> nodes.ArgKind: type_annotation=None, initializer=None, kind=get_kind(arg_name), + pos_only=all(arg.pos_only for arg, _ in all_args[arg_name]), ) if arg.kind.is_positional(): sig.pos.append(arg) @@ -905,6 +909,7 @@ def _verify_signature( if ( runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY and (stub_arg.pos_only or stub_arg.variable.name.startswith("__")) + and stub_arg.variable.name.strip("_") != "self" and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods ): yield ( diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 72b6f6620f83..c0bb2eb6f9da 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -210,7 +210,13 @@ def test(*args: Any, **kwargs: Any) -> None: ) actual_errors = set(output.splitlines()) - assert actual_errors == expected_errors, output + if actual_errors != expected_errors: + output = run_stubtest( + stub="\n\n".join(textwrap.dedent(c.stub.lstrip("\n")) for c in cases), + runtime="\n\n".join(textwrap.dedent(c.runtime.lstrip("\n")) for c in cases), + options=[], + ) + assert actual_errors == expected_errors, output return test @@ -660,6 +666,56 @@ def f6(self, x, /): pass """, error=None, ) + yield Case( + stub=""" + @overload + def f7(a: int, /) -> int: ... + @overload + def f7(b: str, /) -> str: ... + """, + runtime="def f7(x, /): pass", + error=None, + ) + yield Case( + stub=""" + @overload + def f8(a: int, c: int = 0, /) -> int: ... + @overload + def f8(b: str, d: int, /) -> str: ... + """, + runtime="def f8(x, y, /): pass", + error="f8", + ) + yield Case( + stub=""" + @overload + def f9(a: int, c: int = 0, /) -> int: ... + @overload + def f9(b: str, d: int, /) -> str: ... + """, + runtime="def f9(x, y=0, /): pass", + error=None, + ) + yield Case( + stub=""" + class Bar: + @overload + def f1(self) -> int: ... + @overload + def f1(self, a: int, /) -> int: ... + + @overload + def f2(self, a: int, /) -> int: ... + @overload + def f2(self, a: str, /) -> int: ... + """, + runtime=""" + class Bar: + def f1(self, *a) -> int: ... + def f2(self, *a) -> int: ... + """, + error=None, + ) @collect_cases def test_property(self) -> Iterator[Case]: From a8741d8fda2695e189add60b1cfc4adebd2c3e1e Mon Sep 17 00:00:00 2001 From: Fabian Keller Date: Sat, 13 Jan 2024 16:14:16 +0100 Subject: [PATCH 0452/1617] Improve stubgen tests (#16760) Improve test cases around #16486 This PR does not change any actual mypy behavior, only hardens the stubgen tests. The specific changes are: - **dedicated test cases**: The existing pybind11 test cases originate from a pybind11 demo. They cover a specific topic involving geometry types and semi-implemented logic related to it. This somehow distracts from the aspects we are trying to test here from the mypy stubgen perspective, because it hides the actual intent of the bindings. I've simply started adding new test cases that clearly express via their name what the test case is addressing. I've kept the original demo stuff for now, so that the new cases are just an addition (overhead is negligible). - **running mypy on the generated stubs**: In general it is crucial that the output produced by the stubgen can actually be type checked by mypy (this was the regression in #18486). This wasn't covered by the CI check so far. I've added check now, which would have avoided the regression. My goal for follow up PRs would be that we can use `mypy --disallow-untyped-defs` or even `mypy --strict` on the output. - **minor directory restructuring**: So far the expected stub outputs were stored in folder names `stubgen` and `stubgen-include-docs`. This was a bit confusing, because the folder `stubgen` suggested it contains _the_ stubgen (implementation). I went for `expected_stubs_no_docs` and `expected_stubs_with_docs` to make the role of the two folders more explicit. - **minor script bugfix**: Fix a bug in `test-stubgen.sh`: The pre-delete functionality was broken, because the `*` was quoted and therefore did not match. --- .github/workflows/test_stubgenc.yml | 2 +- misc/test-stubgenc.sh | 20 ++- .../pybind11_fixtures/__init__.pyi | 27 ++++ .../pybind11_fixtures/demo.pyi} | 11 -- .../pybind11_fixtures/__init__.pyi | 52 +++++++ .../pybind11_fixtures/demo.pyi} | 60 ++------ .../pyproject.toml | 0 .../setup.py | 4 +- .../src/main.cpp | 143 ++++++++++++++---- .../pybind11_mypy_demo/__init__.pyi | 1 - .../stubgen/pybind11_mypy_demo/__init__.pyi | 1 - 11 files changed, 227 insertions(+), 94 deletions(-) create mode 100644 test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi rename test-data/{pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi => pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi} (86%) create mode 100644 test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi rename test-data/{pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi => pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi} (57%) rename test-data/{pybind11_mypy_demo => pybind11_fixtures}/pyproject.toml (100%) rename test-data/{pybind11_mypy_demo => pybind11_fixtures}/setup.py (85%) rename test-data/{pybind11_mypy_demo => pybind11_fixtures}/src/main.cpp (61%) delete mode 100644 test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi delete mode 100644 test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index a2fb3e9dce6b..3daf01372d2d 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -1,4 +1,4 @@ -name: Test stubgenc on pybind11-mypy-demo +name: Test stubgenc on pybind11_fixtures on: workflow_dispatch: diff --git a/misc/test-stubgenc.sh b/misc/test-stubgenc.sh index 5cb5140eba76..ad66722628d8 100755 --- a/misc/test-stubgenc.sh +++ b/misc/test-stubgenc.sh @@ -7,7 +7,7 @@ cd "$(dirname "$0")/.." # Install dependencies, demo project and mypy python -m pip install -r test-requirements.txt -python -m pip install ./test-data/pybind11_mypy_demo +python -m pip install ./test-data/pybind11_fixtures python -m pip install . EXIT=0 @@ -17,19 +17,29 @@ EXIT=0 # everything else is passed to stubgen as its arguments function stubgenc_test() { # Remove expected stubs and generate new inplace - STUBGEN_OUTPUT_FOLDER=./test-data/pybind11_mypy_demo/$1 - rm -rf "${STUBGEN_OUTPUT_FOLDER:?}/*" + STUBGEN_OUTPUT_FOLDER=./test-data/pybind11_fixtures/$1 + rm -rf "${STUBGEN_OUTPUT_FOLDER:?}" + stubgen -o "$STUBGEN_OUTPUT_FOLDER" "${@:2}" + # Check if generated stubs can actually be type checked by mypy + if ! mypy "$STUBGEN_OUTPUT_FOLDER"; + then + echo "Stubgen test failed, because generated stubs failed to type check." + EXIT=1 + fi + # Compare generated stubs to expected ones if ! git diff --exit-code "$STUBGEN_OUTPUT_FOLDER"; then + echo "Stubgen test failed, because generated stubs differ from expected outputs." EXIT=1 fi } # create stubs without docstrings -stubgenc_test stubgen -p pybind11_mypy_demo +stubgenc_test expected_stubs_no_docs -p pybind11_fixtures # create stubs with docstrings -stubgenc_test stubgen-include-docs -p pybind11_mypy_demo --include-docstrings +stubgenc_test expected_stubs_with_docs -p pybind11_fixtures --include-docstrings + exit $EXIT diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi new file mode 100644 index 000000000000..e113d8a69a5d --- /dev/null +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi @@ -0,0 +1,27 @@ +import os +from . import demo as demo +from typing import List, Optional, Tuple, overload + +class StaticMethods: + def __init__(self, *args, **kwargs) -> None: ... + @overload + @staticmethod + def overloaded_static_method(value: int) -> int: ... + @overload + @staticmethod + def overloaded_static_method(value: float) -> float: ... + @staticmethod + def some_static_method(a: int, b: int) -> int: ... + +class TestStruct: + field_readwrite: int + field_readwrite_docstring: int + def __init__(self, *args, **kwargs) -> None: ... + @property + def field_readonly(self) -> int: ... + +def func_incomplete_signature(*args, **kwargs): ... +def func_returning_optional() -> Optional[int]: ... +def func_returning_pair() -> Tuple[int, float]: ... +def func_returning_path() -> os.PathLike: ... +def func_returning_vector() -> List[float]: ... diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi similarity index 86% rename from test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi rename to test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi index f3acb1677e68..6f164a03edcc 100644 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi @@ -3,17 +3,6 @@ from typing import ClassVar, List, overload PI: float __version__: str -class Foo: - def __init__(self, *args, **kwargs) -> None: ... - @overload - @staticmethod - def overloaded_static_method(value: int) -> int: ... - @overload - @staticmethod - def overloaded_static_method(value: float) -> float: ... - @staticmethod - def some_static_method(a: int, b: int) -> int: ... - class Point: class AngleUnit: __members__: ClassVar[dict] = ... # read-only diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi new file mode 100644 index 000000000000..1dabb0d9a330 --- /dev/null +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi @@ -0,0 +1,52 @@ +import os +from . import demo as demo +from typing import List, Optional, Tuple, overload + +class StaticMethods: + def __init__(self, *args, **kwargs) -> None: + """Initialize self. See help(type(self)) for accurate signature.""" + @overload + @staticmethod + def overloaded_static_method(value: int) -> int: + """overloaded_static_method(*args, **kwargs) + Overloaded function. + + 1. overloaded_static_method(value: int) -> int + + 2. overloaded_static_method(value: float) -> float + """ + @overload + @staticmethod + def overloaded_static_method(value: float) -> float: + """overloaded_static_method(*args, **kwargs) + Overloaded function. + + 1. overloaded_static_method(value: int) -> int + + 2. overloaded_static_method(value: float) -> float + """ + @staticmethod + def some_static_method(a: int, b: int) -> int: + """some_static_method(a: int, b: int) -> int + + None + """ + +class TestStruct: + field_readwrite: int + field_readwrite_docstring: int + def __init__(self, *args, **kwargs) -> None: + """Initialize self. See help(type(self)) for accurate signature.""" + @property + def field_readonly(self) -> int: ... + +def func_incomplete_signature(*args, **kwargs): + """func_incomplete_signature() -> dummy_sub_namespace::HasNoBinding""" +def func_returning_optional() -> Optional[int]: + """func_returning_optional() -> Optional[int]""" +def func_returning_pair() -> Tuple[int, float]: + """func_returning_pair() -> Tuple[int, float]""" +def func_returning_path() -> os.PathLike: + """func_returning_path() -> os.PathLike""" +def func_returning_vector() -> List[float]: + """func_returning_vector() -> List[float]""" diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi similarity index 57% rename from test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi rename to test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi index 3047da622a3e..1527225ed009 100644 --- a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/basics.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi @@ -3,36 +3,6 @@ from typing import ClassVar, List, overload PI: float __version__: str -class Foo: - def __init__(self, *args, **kwargs) -> None: - """Initialize self. See help(type(self)) for accurate signature.""" - @overload - @staticmethod - def overloaded_static_method(value: int) -> int: - """overloaded_static_method(*args, **kwargs) - Overloaded function. - - 1. overloaded_static_method(value: int) -> int - - 2. overloaded_static_method(value: float) -> float - """ - @overload - @staticmethod - def overloaded_static_method(value: float) -> float: - """overloaded_static_method(*args, **kwargs) - Overloaded function. - - 1. overloaded_static_method(value: int) -> int - - 2. overloaded_static_method(value: float) -> float - """ - @staticmethod - def some_static_method(a: int, b: int) -> int: - """some_static_method(a: int, b: int) -> int - - None - """ - class Point: class AngleUnit: __members__: ClassVar[dict] = ... # read-only @@ -40,15 +10,15 @@ class Point: degree: ClassVar[Point.AngleUnit] = ... radian: ClassVar[Point.AngleUnit] = ... def __init__(self, value: int) -> None: - """__init__(self: pybind11_mypy_demo.basics.Point.AngleUnit, value: int) -> None""" + """__init__(self: pybind11_fixtures.demo.Point.AngleUnit, value: int) -> None""" def __eq__(self, other: object) -> bool: """__eq__(self: object, other: object) -> bool""" def __hash__(self) -> int: """__hash__(self: object) -> int""" def __index__(self) -> int: - """__index__(self: pybind11_mypy_demo.basics.Point.AngleUnit) -> int""" + """__index__(self: pybind11_fixtures.demo.Point.AngleUnit) -> int""" def __int__(self) -> int: - """__int__(self: pybind11_mypy_demo.basics.Point.AngleUnit) -> int""" + """__int__(self: pybind11_fixtures.demo.Point.AngleUnit) -> int""" def __ne__(self, other: object) -> bool: """__ne__(self: object, other: object) -> bool""" @property @@ -63,15 +33,15 @@ class Point: mm: ClassVar[Point.LengthUnit] = ... pixel: ClassVar[Point.LengthUnit] = ... def __init__(self, value: int) -> None: - """__init__(self: pybind11_mypy_demo.basics.Point.LengthUnit, value: int) -> None""" + """__init__(self: pybind11_fixtures.demo.Point.LengthUnit, value: int) -> None""" def __eq__(self, other: object) -> bool: """__eq__(self: object, other: object) -> bool""" def __hash__(self) -> int: """__hash__(self: object) -> int""" def __index__(self) -> int: - """__index__(self: pybind11_mypy_demo.basics.Point.LengthUnit) -> int""" + """__index__(self: pybind11_fixtures.demo.Point.LengthUnit) -> int""" def __int__(self) -> int: - """__int__(self: pybind11_mypy_demo.basics.Point.LengthUnit) -> int""" + """__int__(self: pybind11_fixtures.demo.Point.LengthUnit) -> int""" def __ne__(self, other: object) -> bool: """__ne__(self: object, other: object) -> bool""" @property @@ -90,38 +60,38 @@ class Point: """__init__(*args, **kwargs) Overloaded function. - 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None + 1. __init__(self: pybind11_fixtures.demo.Point) -> None - 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None + 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None """ @overload def __init__(self, x: float, y: float) -> None: """__init__(*args, **kwargs) Overloaded function. - 1. __init__(self: pybind11_mypy_demo.basics.Point) -> None + 1. __init__(self: pybind11_fixtures.demo.Point) -> None - 2. __init__(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> None + 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None """ def as_list(self) -> List[float]: - """as_list(self: pybind11_mypy_demo.basics.Point) -> List[float]""" + """as_list(self: pybind11_fixtures.demo.Point) -> List[float]""" @overload def distance_to(self, x: float, y: float) -> float: """distance_to(*args, **kwargs) Overloaded function. - 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float + 1. distance_to(self: pybind11_fixtures.demo.Point, x: float, y: float) -> float - 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float + 2. distance_to(self: pybind11_fixtures.demo.Point, other: pybind11_fixtures.demo.Point) -> float """ @overload def distance_to(self, other: Point) -> float: """distance_to(*args, **kwargs) Overloaded function. - 1. distance_to(self: pybind11_mypy_demo.basics.Point, x: float, y: float) -> float + 1. distance_to(self: pybind11_fixtures.demo.Point, x: float, y: float) -> float - 2. distance_to(self: pybind11_mypy_demo.basics.Point, other: pybind11_mypy_demo.basics.Point) -> float + 2. distance_to(self: pybind11_fixtures.demo.Point, other: pybind11_fixtures.demo.Point) -> float """ @property def length(self) -> float: ... diff --git a/test-data/pybind11_mypy_demo/pyproject.toml b/test-data/pybind11_fixtures/pyproject.toml similarity index 100% rename from test-data/pybind11_mypy_demo/pyproject.toml rename to test-data/pybind11_fixtures/pyproject.toml diff --git a/test-data/pybind11_mypy_demo/setup.py b/test-data/pybind11_fixtures/setup.py similarity index 85% rename from test-data/pybind11_mypy_demo/setup.py rename to test-data/pybind11_fixtures/setup.py index 0da1cfbcef19..e227b49935ea 100644 --- a/test-data/pybind11_mypy_demo/setup.py +++ b/test-data/pybind11_fixtures/setup.py @@ -5,14 +5,14 @@ # Documentation: https://pybind11.readthedocs.io/en/stable/compiling.html ext_modules = [ Pybind11Extension( - "pybind11_mypy_demo", + "pybind11_fixtures", ["src/main.cpp"], cxx_std=17, ), ] setup( - name="pybind11-mypy-demo", + name="pybind11_fixtures", version="0.0.1", ext_modules=ext_modules, ) diff --git a/test-data/pybind11_mypy_demo/src/main.cpp b/test-data/pybind11_fixtures/src/main.cpp similarity index 61% rename from test-data/pybind11_mypy_demo/src/main.cpp rename to test-data/pybind11_fixtures/src/main.cpp index 8be759d51671..4d275ab1fd70 100644 --- a/test-data/pybind11_mypy_demo/src/main.cpp +++ b/test-data/pybind11_fixtures/src/main.cpp @@ -43,12 +43,106 @@ */ #include +#include +#include +#include +#include + #include #include +#include namespace py = pybind11; -namespace basics { +// ---------------------------------------------------------------------------- +// Dedicated test cases +// ---------------------------------------------------------------------------- + +std::vector funcReturningVector() +{ + return std::vector{1.0, 2.0, 3.0}; +} + +std::pair funcReturningPair() +{ + return std::pair{42, 1.0}; +} + +std::optional funcReturningOptional() +{ + return std::nullopt; +} + +std::filesystem::path funcReturningPath() +{ + return std::filesystem::path{"foobar"}; +} + +namespace dummy_sub_namespace { + struct HasNoBinding{}; +} + +// We can enforce the case of an incomplete signature by referring to a type in +// some namespace that doesn't have a pybind11 binding. +dummy_sub_namespace::HasNoBinding funcIncompleteSignature() +{ + return dummy_sub_namespace::HasNoBinding{}; +} + +struct TestStruct +{ + int field_readwrite; + int field_readwrite_docstring; + int field_readonly; +}; + +struct StaticMethods +{ + static int some_static_method(int a, int b) { return 42; } + static int overloaded_static_method(int value) { return 42; } + static double overloaded_static_method(double value) { return 1.0; } +}; + +// Bindings + +void bind_test_cases(py::module& m) { + m.def("func_returning_vector", &funcReturningVector); + m.def("func_returning_pair", &funcReturningPair); + m.def("func_returning_optional", &funcReturningOptional); + m.def("func_returning_path", &funcReturningPath); + + m.def("func_incomplete_signature", &funcIncompleteSignature); + + py::class_(m, "TestStruct") + .def_readwrite("field_readwrite", &TestStruct::field_readwrite) + .def_readwrite("field_readwrite_docstring", &TestStruct::field_readwrite_docstring, "some docstring") + .def_property_readonly( + "field_readonly", + [](const TestStruct& x) { + return x.field_readonly; + }, + "some docstring"); + + // Static methods + py::class_ pyStaticMethods(m, "StaticMethods"); + + pyStaticMethods + .def_static( + "some_static_method", + &StaticMethods::some_static_method, R"#(None)#", py::arg("a"), py::arg("b")) + .def_static( + "overloaded_static_method", + py::overload_cast(&StaticMethods::overloaded_static_method), py::arg("value")) + .def_static( + "overloaded_static_method", + py::overload_cast(&StaticMethods::overloaded_static_method), py::arg("value")); +} + +// ---------------------------------------------------------------------------- +// Original demo +// ---------------------------------------------------------------------------- + +namespace demo { int answer() { return 42; @@ -118,27 +212,22 @@ const Point Point::y_axis = Point(0, 1); Point::LengthUnit Point::length_unit = Point::LengthUnit::mm; Point::AngleUnit Point::angle_unit = Point::AngleUnit::radian; -struct Foo -{ - static int some_static_method(int a, int b) { return a * 42 + b; } - static int overloaded_static_method(int value) { return value * 42; } - static double overloaded_static_method(double value) { return value * 42; } -}; +} // namespace: demo -} // namespace: basics +// Bindings -void bind_basics(py::module& basics) { +void bind_demo(py::module& m) { - using namespace basics; + using namespace demo; // Functions - basics.def("answer", &answer, "answer docstring, with end quote\""); // tests explicit docstrings - basics.def("sum", &sum, "multiline docstring test, edge case quotes \"\"\"'''"); - basics.def("midpoint", &midpoint, py::arg("left"), py::arg("right")); - basics.def("weighted_midpoint", weighted_midpoint, py::arg("left"), py::arg("right"), py::arg("alpha")=0.5); + m.def("answer", &answer, "answer docstring, with end quote\""); // tests explicit docstrings + m.def("sum", &sum, "multiline docstring test, edge case quotes \"\"\"'''"); + m.def("midpoint", &midpoint, py::arg("left"), py::arg("right")); + m.def("weighted_midpoint", weighted_midpoint, py::arg("left"), py::arg("right"), py::arg("alpha")=0.5); // Classes - py::class_ pyPoint(basics, "Point"); + py::class_ pyPoint(m, "Point"); py::enum_ pyLengthUnit(pyPoint, "LengthUnit"); py::enum_ pyAngleUnit(pyPoint, "AngleUnit"); @@ -173,20 +262,18 @@ void bind_basics(py::module& basics) { .value("radian", Point::AngleUnit::radian) .value("degree", Point::AngleUnit::degree); - // Static methods - py::class_ pyFoo(basics, "Foo"); - - pyFoo - .def_static("some_static_method", &Foo::some_static_method, R"#(None)#", py::arg("a"), py::arg("b")) - .def_static("overloaded_static_method", py::overload_cast(&Foo::overloaded_static_method), py::arg("value")) - .def_static("overloaded_static_method", py::overload_cast(&Foo::overloaded_static_method), py::arg("value")); - // Module-level attributes - basics.attr("PI") = std::acos(-1); - basics.attr("__version__") = "0.0.1"; + m.attr("PI") = std::acos(-1); + m.attr("__version__") = "0.0.1"; } -PYBIND11_MODULE(pybind11_mypy_demo, m) { - auto basics = m.def_submodule("basics"); - bind_basics(basics); +// ---------------------------------------------------------------------------- +// Module entry point +// ---------------------------------------------------------------------------- + +PYBIND11_MODULE(pybind11_fixtures, m) { + bind_test_cases(m); + + auto demo = m.def_submodule("demo"); + bind_demo(demo); } diff --git a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi b/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi deleted file mode 100644 index 0cb252f00259..000000000000 --- a/test-data/pybind11_mypy_demo/stubgen-include-docs/pybind11_mypy_demo/__init__.pyi +++ /dev/null @@ -1 +0,0 @@ -from . import basics as basics diff --git a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi b/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi deleted file mode 100644 index 0cb252f00259..000000000000 --- a/test-data/pybind11_mypy_demo/stubgen/pybind11_mypy_demo/__init__.pyi +++ /dev/null @@ -1 +0,0 @@ -from . import basics as basics From e28925ddd34fd128d509b5b4741789578462cbe0 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 13 Jan 2024 16:58:37 +0100 Subject: [PATCH 0453/1617] stubgen: use PEP 604 unions everywhere (#16519) Fixes #12920 --- mypy/stubgen.py | 22 ++++++++++------------ mypy/stubutil.py | 25 +++++++++++++++++-------- test-data/unit/stubgen.test | 30 ++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 7ec3d7069fb2..e753fd1e73d5 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -22,7 +22,7 @@ => Generate out/urllib/parse.pyi. $ stubgen -p urllib - => Generate stubs for whole urlib package (recursively). + => Generate stubs for whole urllib package (recursively). For C modules, you can get more precise function signatures by parsing .rst (Sphinx) documentation for extra information. For this, use the --doc-dir option: @@ -306,6 +306,13 @@ def visit_str_expr(self, node: StrExpr) -> str: return repr(node.value) def visit_index_expr(self, node: IndexExpr) -> str: + base_fullname = self.stubgen.get_fullname(node.base) + if base_fullname == "typing.Union": + if isinstance(node.index, TupleExpr): + return " | ".join([item.accept(self) for item in node.index.items]) + return node.index.accept(self) + if base_fullname == "typing.Optional": + return f"{node.index.accept(self)} | None" base = node.base.accept(self) index = node.index.accept(self) if len(index) > 2 and index.startswith("(") and index.endswith(")"): @@ -682,7 +689,7 @@ def process_decorator(self, o: Decorator) -> None: self.add_decorator(qualname, require_name=False) def get_fullname(self, expr: Expression) -> str: - """Return the full name resolving imports and import aliases.""" + """Return the expression's full name.""" if ( self.analyzed and isinstance(expr, (NameExpr, MemberExpr)) @@ -691,16 +698,7 @@ def get_fullname(self, expr: Expression) -> str: ): return expr.fullname name = get_qualified_name(expr) - if "." not in name: - real_module = self.import_tracker.module_for.get(name) - real_short = self.import_tracker.reverse_alias.get(name, name) - if real_module is None and real_short not in self.defined_names: - real_module = "builtins" # not imported and not defined, must be a builtin - else: - name_module, real_short = name.split(".", 1) - real_module = self.import_tracker.reverse_alias.get(name_module, name_module) - resolved_name = real_short if real_module is None else f"{real_module}.{real_short}" - return resolved_name + return self.resolve_name(name) def visit_class_def(self, o: ClassDef) -> None: self._current_class = o diff --git a/mypy/stubutil.py b/mypy/stubutil.py index e4a97964c547..b7f6131c003d 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -226,6 +226,11 @@ def visit_any(self, t: AnyType) -> str: def visit_unbound_type(self, t: UnboundType) -> str: s = t.name + fullname = self.stubgen.resolve_name(s) + if fullname == "typing.Union": + return " | ".join([item.accept(self) for item in t.args]) + if fullname == "typing.Optional": + return f"{t.args[0].accept(self)} | None" if self.known_modules is not None and "." in s: # see if this object is from any of the modules that we're currently processing. # reverse sort so that subpackages come before parents: e.g. "foo.bar" before "foo". @@ -588,14 +593,18 @@ def __init__( def get_sig_generators(self) -> list[SignatureGenerator]: return [] - def refers_to_fullname(self, name: str, fullname: str | tuple[str, ...]) -> bool: - """Return True if the variable name identifies the same object as the given fullname(s).""" - if isinstance(fullname, tuple): - return any(self.refers_to_fullname(name, fname) for fname in fullname) - module, short = fullname.rsplit(".", 1) - return self.import_tracker.module_for.get(name) == module and ( - name == short or self.import_tracker.reverse_alias.get(name) == short - ) + def resolve_name(self, name: str) -> str: + """Return the full name resolving imports and import aliases.""" + if "." not in name: + real_module = self.import_tracker.module_for.get(name) + real_short = self.import_tracker.reverse_alias.get(name, name) + if real_module is None and real_short not in self.defined_names: + real_module = "builtins" # not imported and not defined, must be a builtin + else: + name_module, real_short = name.split(".", 1) + real_module = self.import_tracker.reverse_alias.get(name_module, name_module) + resolved_name = real_short if real_module is None else f"{real_module}.{real_short}" + return resolved_name def add_name(self, fullname: str, require: bool = True) -> str: """Add a name to be imported and return the name reference. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index cd38242ce031..50437c903530 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4139,3 +4139,33 @@ from dataclasses import dataclass class X(missing.Base): a: int def __init__(self, *selfa_, a, **selfa__) -> None: ... + +[case testAlwaysUsePEP604Union] +import typing +import typing as t +from typing import Optional, Union, Optional as O, Union as U +import x + +union = Union[int, str] +bad_union = Union[int] +nested_union = Optional[Union[int, str]] +not_union = x.Union[int, str] +u = U[int, str] +o = O[int] + +def f1(a: Union["int", Optional[tuple[int, t.Optional[int]]]]) -> int: ... +def f2(a: typing.Union[int | x.Union[int, int], O[float]]) -> int: ... + +[out] +import x +from _typeshed import Incomplete + +union = int | str +bad_union = int +nested_union = int | str | None +not_union: Incomplete +u = int | str +o = int | None + +def f1(a: int | tuple[int, int | None] | None) -> int: ... +def f2(a: int | x.Union[int, int] | float | None) -> int: ... From e64fb9f7fe75d5e5aa722f80576bd2b67b442a4e Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 13 Jan 2024 21:19:44 +0100 Subject: [PATCH 0454/1617] Fix failing stubgen tests (#16779) --- .github/workflows/test_stubgenc.yml | 1 + .pre-commit-config.yaml | 1 + .../expected_stubs_no_docs/pybind11_fixtures/__init__.pyi | 4 ++-- .../expected_stubs_with_docs/pybind11_fixtures/__init__.pyi | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 3daf01372d2d..7bdcfdb305bb 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -10,6 +10,7 @@ on: - 'misc/test-stubgenc.sh' - 'mypy/stubgenc.py' - 'mypy/stubdoc.py' + - 'mypy/stubutil.py' - 'test-data/stubgen/**' permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4090bf0ecb4c..0bbd7b3ce382 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,7 @@ repos: rev: 23.9.1 # must match test-requirements.txt hooks: - id: black + exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.4 # must match test-requirements.txt hooks: diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi index e113d8a69a5d..bb939aa5a5e7 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi @@ -1,6 +1,6 @@ import os from . import demo as demo -from typing import List, Optional, Tuple, overload +from typing import List, Tuple, overload class StaticMethods: def __init__(self, *args, **kwargs) -> None: ... @@ -21,7 +21,7 @@ class TestStruct: def field_readonly(self) -> int: ... def func_incomplete_signature(*args, **kwargs): ... -def func_returning_optional() -> Optional[int]: ... +def func_returning_optional() -> int | None: ... def func_returning_pair() -> Tuple[int, float]: ... def func_returning_path() -> os.PathLike: ... def func_returning_vector() -> List[float]: ... diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi index 1dabb0d9a330..622e5881a147 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi @@ -1,6 +1,6 @@ import os from . import demo as demo -from typing import List, Optional, Tuple, overload +from typing import List, Tuple, overload class StaticMethods: def __init__(self, *args, **kwargs) -> None: @@ -42,7 +42,7 @@ class TestStruct: def func_incomplete_signature(*args, **kwargs): """func_incomplete_signature() -> dummy_sub_namespace::HasNoBinding""" -def func_returning_optional() -> Optional[int]: +def func_returning_optional() -> int | None: """func_returning_optional() -> Optional[int]""" def func_returning_pair() -> Tuple[int, float]: """func_returning_pair() -> Tuple[int, float]""" From 186ace34432014e668de2ed1449f9262c1e7f056 Mon Sep 17 00:00:00 2001 From: Makonnen Makonnen Date: Sat, 13 Jan 2024 22:05:38 -0500 Subject: [PATCH 0455/1617] Improve mypy daemon documentation note about local partial types (#16782) The existing documentation almost makes it seem like the user has the option to toggle this. --- docs/source/mypy_daemon.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/mypy_daemon.rst b/docs/source/mypy_daemon.rst index 7586026b6c81..6c511e14eb95 100644 --- a/docs/source/mypy_daemon.rst +++ b/docs/source/mypy_daemon.rst @@ -61,7 +61,7 @@ you have a large codebase.) .. note:: - The mypy daemon automatically enables ``--local-partial-types`` by default. + The mypy daemon requires ``--local-partial-types`` and automatically enables it. Daemon client commands From b1fe23f5a3a4457cae3430acca6e99a36e3efd00 Mon Sep 17 00:00:00 2001 From: anniel-stripe <97691964+anniel-stripe@users.noreply.github.com> Date: Sat, 13 Jan 2024 19:09:24 -0800 Subject: [PATCH 0456/1617] Support TypedDict functional syntax as class base type (#16703) Fixes https://github.com/python/mypy/issues/16701 This PR allows `TypedDict(...)` calls to be used as a base class. This fixes the error emitted by mypy described in https://github.com/python/mypy/issues/16701 . --- mypy/semanal.py | 8 ++++++++ mypy/semanal_typeddict.py | 19 +++++++++++++++---- test-data/unit/check-typeddict.test | 10 ++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e0a3db2bff1b..4bf9f0c3eabb 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2169,8 +2169,16 @@ def analyze_base_classes( if ( isinstance(base_expr, RefExpr) and base_expr.fullname in TYPED_NAMEDTUPLE_NAMES + TPDICT_NAMES + ) or ( + isinstance(base_expr, CallExpr) + and isinstance(base_expr.callee, RefExpr) + and base_expr.callee.fullname in TPDICT_NAMES ): # Ignore magic bases for now. + # For example: + # class Foo(TypedDict): ... # RefExpr + # class Foo(NamedTuple): ... # RefExpr + # class Foo(TypedDict("Foo", {"a": int})): ... # CallExpr continue try: diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 67c05fd74273..dbec981bdc96 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -79,6 +79,8 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N """ possible = False for base_expr in defn.base_type_exprs: + if isinstance(base_expr, CallExpr): + base_expr = base_expr.callee if isinstance(base_expr, IndexExpr): base_expr = base_expr.base if isinstance(base_expr, RefExpr): @@ -117,7 +119,13 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N typeddict_bases: list[Expression] = [] typeddict_bases_set = set() for expr in defn.base_type_exprs: - if isinstance(expr, RefExpr) and expr.fullname in TPDICT_NAMES: + ok, maybe_type_info, _ = self.check_typeddict(expr, None, False) + if ok and maybe_type_info is not None: + # expr is a CallExpr + info = maybe_type_info + typeddict_bases_set.add(info.fullname) + typeddict_bases.append(expr) + elif isinstance(expr, RefExpr) and expr.fullname in TPDICT_NAMES: if "TypedDict" not in typeddict_bases_set: typeddict_bases_set.add("TypedDict") else: @@ -176,12 +184,11 @@ def add_keys_and_types_from_base( required_keys: set[str], ctx: Context, ) -> None: + base_args: list[Type] = [] if isinstance(base, RefExpr): assert isinstance(base.node, TypeInfo) info = base.node - base_args: list[Type] = [] - else: - assert isinstance(base, IndexExpr) + elif isinstance(base, IndexExpr): assert isinstance(base.base, RefExpr) assert isinstance(base.base.node, TypeInfo) info = base.base.node @@ -189,6 +196,10 @@ def add_keys_and_types_from_base( if args is None: return base_args = args + else: + assert isinstance(base, CallExpr) + assert isinstance(base.analyzed, TypedDictExpr) + info = base.analyzed.info assert info.typeddict_type is not None base_typed_dict = info.typeddict_type diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index d8022f85574c..625b82936e8c 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3438,3 +3438,13 @@ class TotalInTheMiddle(TypedDict, a=1, total=True, b=2, c=3): # E: Unexpected k ... [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testCanCreateClassWithFunctionBasedTypedDictBase] +from mypy_extensions import TypedDict + +class Params(TypedDict("Params", {'x': int})): + pass + +p: Params = {'x': 2} +reveal_type(p) # N: Revealed type is "TypedDict('__main__.Params', {'x': builtins.int})" +[builtins fixtures/dict.pyi] From 261e569e2c256451692dd57c05295ef0bcfb34fd Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sun, 14 Jan 2024 04:15:33 +0100 Subject: [PATCH 0457/1617] Support narrowing unions that include type[None] (#16315) Fixes #16279 See my comment in the referenced issue. --- mypy/checker.py | 18 +++++--- mypy/checkexpr.py | 19 +++++--- test-data/unit/check-narrowing.test | 70 +++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1b57ef780104..cd23e74a8dac 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7121,9 +7121,10 @@ def conditional_types_with_intersection( possible_target_types = [] for tr in type_ranges: item = get_proper_type(tr.item) - if not isinstance(item, Instance) or tr.is_upper_bound: - return yes_type, no_type - possible_target_types.append(item) + if isinstance(item, (Instance, NoneType)): + possible_target_types.append(item) + if not possible_target_types: + return yes_type, no_type out = [] errors: list[tuple[str, str]] = [] @@ -7131,6 +7132,9 @@ def conditional_types_with_intersection( if not isinstance(v, Instance): return yes_type, no_type for t in possible_target_types: + if isinstance(t, NoneType): + errors.append((f'"{v.type.name}" and "NoneType"', '"NoneType" is final')) + continue intersection = self.intersect_instances((v, t), errors) if intersection is None: continue @@ -7174,7 +7178,11 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: elif isinstance(typ, TypeType): # Type[A] means "any type that is a subtype of A" rather than "precisely type A" # we indicate this by setting is_upper_bound flag - types.append(TypeRange(typ.item, is_upper_bound=True)) + is_upper_bound = True + if isinstance(typ.item, NoneType): + # except for Type[None], because "'NoneType' is not an acceptable base type" + is_upper_bound = False + types.append(TypeRange(typ.item, is_upper_bound=is_upper_bound)) elif isinstance(typ, Instance) and typ.type.fullname == "builtins.type": object_type = Instance(typ.type.mro[-1], []) types.append(TypeRange(object_type, is_upper_bound=True)) @@ -7627,7 +7635,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap: if isinstance(t, TypeVarType): t = t.upper_bound # TODO: should we only allow unions of instances as per PEP 484? - if not isinstance(get_proper_type(t), (UnionType, Instance)): + if not isinstance(get_proper_type(t), (UnionType, Instance, NoneType)): # unknown type; error was likely reported earlier return {} converted_type_map[expr] = TypeType.make_normalized(typ) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 3af8d70e78c9..fbedd95e8fd2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -385,6 +385,9 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: if node.typeddict_type: # We special-case TypedDict, because they don't define any constructor. result = self.typeddict_callable(node) + elif node.fullname == "types.NoneType": + # We special case NoneType, because its stub definition is not related to None. + result = TypeType(NoneType()) else: result = type_object_type(node, self.named_type) if isinstance(result, CallableType) and isinstance( # type: ignore[misc] @@ -511,13 +514,13 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> if is_expr_literal_type(typ): self.msg.cannot_use_function_with_type(e.callee.name, "Literal", e) continue - if ( - node - and isinstance(node.node, TypeAlias) - and isinstance(get_proper_type(node.node.target), AnyType) - ): - self.msg.cannot_use_function_with_type(e.callee.name, "Any", e) - continue + if node and isinstance(node.node, TypeAlias): + target = get_proper_type(node.node.target) + if isinstance(target, AnyType): + self.msg.cannot_use_function_with_type(e.callee.name, "Any", e) + continue + if isinstance(target, NoneType): + continue if ( isinstance(typ, IndexExpr) and isinstance(typ.analyzed, (TypeApplication, TypeAliasExpr)) @@ -4731,6 +4734,8 @@ class LongName(Generic[T]): ... return type_object_type(tuple_fallback(item).type, self.named_type) elif isinstance(item, TypedDictType): return self.typeddict_callable_from_context(item) + elif isinstance(item, NoneType): + return TypeType(item, line=item.line, column=item.column) elif isinstance(item, AnyType): return AnyType(TypeOfAny.from_another_any, source_any=item) else: diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index a2859dfffa3a..d50d1f508b85 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2022,3 +2022,73 @@ def f(x: Union[int, Sequence[int]]) -> None: ): reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" [builtins fixtures/len.pyi] + +[case testNarrowingIsSubclassNoneType1] +from typing import Type, Union + +def f(cls: Type[Union[None, int]]) -> None: + if issubclass(cls, int): + reveal_type(cls) # N: Revealed type is "Type[builtins.int]" + else: + reveal_type(cls) # N: Revealed type is "Type[None]" +[builtins fixtures/isinstance.pyi] + +[case testNarrowingIsSubclassNoneType2] +from typing import Type, Union + +def f(cls: Type[Union[None, int]]) -> None: + if issubclass(cls, type(None)): + reveal_type(cls) # N: Revealed type is "Type[None]" + else: + reveal_type(cls) # N: Revealed type is "Type[builtins.int]" +[builtins fixtures/isinstance.pyi] + +[case testNarrowingIsSubclassNoneType3] +from typing import Type, Union + +NoneType_ = type(None) + +def f(cls: Type[Union[None, int]]) -> None: + if issubclass(cls, NoneType_): + reveal_type(cls) # N: Revealed type is "Type[None]" + else: + reveal_type(cls) # N: Revealed type is "Type[builtins.int]" +[builtins fixtures/isinstance.pyi] + +[case testNarrowingIsSubclassNoneType4] +# flags: --python-version 3.10 + +from types import NoneType +from typing import Type, Union + +def f(cls: Type[Union[None, int]]) -> None: + if issubclass(cls, NoneType): + reveal_type(cls) # N: Revealed type is "Type[None]" + else: + reveal_type(cls) # N: Revealed type is "Type[builtins.int]" +[builtins fixtures/isinstance.pyi] + +[case testNarrowingIsInstanceNoIntersectionWithFinalTypeAndNoneType] +# flags: --warn-unreachable --python-version 3.10 + +from types import NoneType +from typing import final + +class X: ... +class Y: ... +@final +class Z: ... + +x: X + +if isinstance(x, (Y, Z)): + reveal_type(x) # N: Revealed type is "__main__." +if isinstance(x, (Y, NoneType)): + reveal_type(x) # N: Revealed type is "__main__.1" +if isinstance(x, (Y, Z, NoneType)): + reveal_type(x) # N: Revealed type is "__main__.2" +if isinstance(x, (Z, NoneType)): # E: Subclass of "X" and "Z" cannot exist: "Z" is final \ + # E: Subclass of "X" and "NoneType" cannot exist: "NoneType" is final + reveal_type(x) # E: Statement is unreachable + +[builtins fixtures/isinstance.pyi] From 75b68fa2c0d32bd08b98b00aece20c22d82b1b15 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 14 Jan 2024 18:19:12 +0100 Subject: [PATCH 0458/1617] stubgen: Do not ignore property deleter (#16781) Fixes #16690 --- mypy/stubgen.py | 2 +- test-data/unit/stubgen.test | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index e753fd1e73d5..1f7a01e6ae3c 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -685,7 +685,7 @@ def process_decorator(self, o: Decorator) -> None: elif fullname in OVERLOAD_NAMES: self.add_decorator(qualname, require_name=True) o.func.is_overload = True - elif qualname.endswith(".setter"): + elif qualname.endswith((".setter", ".deleter")): self.add_decorator(qualname, require_name=False) def get_fullname(self, expr: Expression) -> str: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 50437c903530..751a1a7fbbcf 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -368,6 +368,8 @@ class A: return 1 @f.setter def f(self, x): ... + @f.deleter + def f(self): ... def h(self): self.f = 1 @@ -377,6 +379,8 @@ class A: def f(self): ... @f.setter def f(self, x) -> None: ... + @f.deleter + def f(self) -> None: ... def h(self) -> None: ... [case testProperty_semanal] @@ -386,6 +390,8 @@ class A: return 1 @f.setter def f(self, x): ... + @f.deleter + def f(self): ... def h(self): self.f = 1 @@ -395,6 +401,8 @@ class A: def f(self): ... @f.setter def f(self, x) -> None: ... + @f.deleter + def f(self) -> None: ... def h(self) -> None: ... -- a read/write property is treated the same as an attribute @@ -2338,10 +2346,12 @@ class B: @property def x(self): return 'x' - @x.setter def x(self, value): self.y = 'y' + @x.deleter + def x(self): + del self.y [out] class A: @@ -2355,6 +2365,8 @@ class B: y: str @x.setter def x(self, value) -> None: ... + @x.deleter + def x(self) -> None: ... [case testMisplacedTypeComment] def f(): From 7eab8a429187ffd2b64bbb9ca29f7d24b262d269 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 21 Jan 2024 21:31:53 -0800 Subject: [PATCH 0459/1617] stubtest: Add support for setting enum members to "..." (#16807) Unblock python/typeshed#11299 --- mypy/stubtest.py | 7 +++++++ mypy/test/teststubtest.py | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6061e98bd7cd..9a038105dc82 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1093,6 +1093,13 @@ def verify_var( runtime_type = get_mypy_type_of_runtime_value(runtime.value) if runtime_type is not None and is_subtype_helper(runtime_type, stub.type): should_error = False + # We always allow setting the stub value to ... + proper_type = mypy.types.get_proper_type(stub.type) + if ( + isinstance(proper_type, mypy.types.Instance) + and proper_type.type.fullname == "builtins.ellipsis" + ): + should_error = False if should_error: yield Error( diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index c0bb2eb6f9da..3f231d8afbcc 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1139,6 +1139,25 @@ def baz(x=Flags3(0)): pass """, error=None, ) + yield Case( + runtime=""" + import enum + class SomeObject: ... + + class WeirdEnum(enum.Enum): + a = SomeObject() + b = SomeObject() + """, + stub=""" + import enum + class SomeObject: ... + class WeirdEnum(enum.Enum): + _value_: SomeObject + a = ... + b = ... + """, + error=None, + ) yield Case( stub=""" class Flags4(enum.Flag): From ec06e004426d92637cd0ddd266a464cea723f952 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:30:30 +0100 Subject: [PATCH 0460/1617] Use TypeVar defaults instead of Any when fixing instance types (PEP 696) (#16812) Start using TypeVar defaults when fixing instance types, instead of filling those with `Any`. This PR preserves the way an invalid amount of args is handled. I.e. filling all with `Any` / defaults, instead of cutting off additional args. Thus preserving full backwards compatibility. This can be easily changed later if necessary. `TypeVarTuple` defaults aren't handled correctly yet. Those will require additional logic which would have complicated the change here and made it more difficult to review. Ref: https://github.com/python/mypy/issues/14851 --- mypy/messages.py | 15 ++- mypy/typeanal.py | 87 ++++++++++----- test-data/unit/check-typevar-defaults.test | 123 +++++++++++++++++++++ 3 files changed, 189 insertions(+), 36 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 450faf4c1688..75b8eeb3174c 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -3017,12 +3017,15 @@ def for_function(callee: CallableType) -> str: return "" -def wrong_type_arg_count(n: int, act: str, name: str) -> str: - s = f"{n} type arguments" - if n == 0: - s = "no type arguments" - elif n == 1: - s = "1 type argument" +def wrong_type_arg_count(low: int, high: int, act: str, name: str) -> str: + if low == high: + s = f"{low} type arguments" + if low == 0: + s = "no type arguments" + elif low == 1: + s = "1 type argument" + else: + s = f"between {low} and {high} type arguments" if act == "0": act = "none" return f'"{name}" expects {s}, but {act} given' diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 8a840424f76f..d10f26f5199a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -9,6 +9,7 @@ from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode +from mypy.expandtype import expand_type from mypy.messages import MessageBuilder, format_type_bare, quote_type_string, wrong_type_arg_count from mypy.nodes import ( ARG_NAMED, @@ -75,6 +76,7 @@ TypeOfAny, TypeQuery, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -1834,14 +1836,14 @@ def get_omitted_any( return any_type -def fix_type_var_tuple_argument(any_type: Type, t: Instance) -> None: +def fix_type_var_tuple_argument(t: Instance) -> None: if t.type.has_type_var_tuple_type: args = list(t.args) assert t.type.type_var_tuple_prefix is not None tvt = t.type.defn.type_vars[t.type.type_var_tuple_prefix] assert isinstance(tvt, TypeVarTupleType) args[t.type.type_var_tuple_prefix] = UnpackType( - Instance(tvt.tuple_fallback.type, [any_type]) + Instance(tvt.tuple_fallback.type, [args[t.type.type_var_tuple_prefix]]) ) t.args = tuple(args) @@ -1855,26 +1857,42 @@ def fix_instance( use_generic_error: bool = False, unexpanded_type: Type | None = None, ) -> None: - """Fix a malformed instance by replacing all type arguments with Any. + """Fix a malformed instance by replacing all type arguments with TypeVar default or Any. Also emit a suitable error if this is not due to implicit Any's. """ - if len(t.args) == 0: - if use_generic_error: - fullname: str | None = None - else: - fullname = t.type.fullname - any_type = get_omitted_any(disallow_any, fail, note, t, options, fullname, unexpanded_type) - t.args = (any_type,) * len(t.type.type_vars) - fix_type_var_tuple_argument(any_type, t) - return - # Construct the correct number of type arguments, as - # otherwise the type checker may crash as it expects - # things to be right. - any_type = AnyType(TypeOfAny.from_error) - t.args = tuple(any_type for _ in t.type.type_vars) - fix_type_var_tuple_argument(any_type, t) - t.invalid = True + arg_count = len(t.args) + min_tv_count = sum(not tv.has_default() for tv in t.type.defn.type_vars) + max_tv_count = len(t.type.type_vars) + if arg_count < min_tv_count or arg_count > max_tv_count: + # Don't use existing args if arg_count doesn't match + t.args = () + + args: list[Type] = [*(t.args[:max_tv_count])] + any_type: AnyType | None = None + env: dict[TypeVarId, Type] = {} + + for tv, arg in itertools.zip_longest(t.type.defn.type_vars, t.args, fillvalue=None): + if tv is None: + continue + if arg is None: + if tv.has_default(): + arg = tv.default + else: + if any_type is None: + fullname = None if use_generic_error else t.type.fullname + any_type = get_omitted_any( + disallow_any, fail, note, t, options, fullname, unexpanded_type + ) + arg = any_type + args.append(arg) + env[tv.id] = arg + t.args = tuple(args) + fix_type_var_tuple_argument(t) + if not t.type.has_type_var_tuple_type: + fixed = expand_type(t, env) + assert isinstance(fixed, Instance) + t.args = fixed.args def instantiate_type_alias( @@ -1963,7 +1981,7 @@ def instantiate_type_alias( if use_standard_error: # This is used if type alias is an internal representation of another type, # for example a generic TypedDict or NamedTuple. - msg = wrong_type_arg_count(exp_len, str(act_len), node.name) + msg = wrong_type_arg_count(exp_len, exp_len, str(act_len), node.name) else: if node.tvar_tuple_index is not None: exp_len_str = f"at least {exp_len - 1}" @@ -2217,24 +2235,27 @@ def validate_instance(t: Instance, fail: MsgCallback, empty_tuple_index: bool) - # TODO: is it OK to fill with TypeOfAny.from_error instead of special form? return False if t.type.has_type_var_tuple_type: - correct = len(t.args) >= len(t.type.type_vars) - 1 + min_tv_count = sum( + not tv.has_default() and not isinstance(tv, TypeVarTupleType) + for tv in t.type.defn.type_vars + ) + correct = len(t.args) >= min_tv_count if any( isinstance(a, UnpackType) and isinstance(get_proper_type(a.type), Instance) for a in t.args ): correct = True - if not correct: - exp_len = f"at least {len(t.type.type_vars) - 1}" + if not t.args: + if not (empty_tuple_index and len(t.type.type_vars) == 1): + # The Any arguments should be set by the caller. + return False + elif not correct: fail( - f"Bad number of arguments, expected: {exp_len}, given: {len(t.args)}", + f"Bad number of arguments, expected: at least {min_tv_count}, given: {len(t.args)}", t, code=codes.TYPE_ARG, ) return False - elif not t.args: - if not (empty_tuple_index and len(t.type.type_vars) == 1): - # The Any arguments should be set by the caller. - return False else: # We also need to check if we are not performing a type variable tuple split. unpack = find_unpack_in_list(t.args) @@ -2254,15 +2275,21 @@ def validate_instance(t: Instance, fail: MsgCallback, empty_tuple_index: bool) - elif any(isinstance(a, UnpackType) for a in t.args): # A variadic unpack in fixed size instance (fixed unpacks must be flattened by the caller) fail(message_registry.INVALID_UNPACK_POSITION, t, code=codes.VALID_TYPE) + t.args = () return False elif len(t.args) != len(t.type.type_vars): # Invalid number of type parameters. - if t.args: + arg_count = len(t.args) + min_tv_count = sum(not tv.has_default() for tv in t.type.defn.type_vars) + max_tv_count = len(t.type.type_vars) + if arg_count and (arg_count < min_tv_count or arg_count > max_tv_count): fail( - wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name), + wrong_type_arg_count(min_tv_count, max_tv_count, str(arg_count), t.type.name), t, code=codes.TYPE_ARG, ) + t.args = () + t.invalid = True return False return True diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 9015d353fa08..c4d258d50ee5 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -116,3 +116,126 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: # reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO # reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO [builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultsClass1] +from typing import Generic, TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2", default=int) +T3 = TypeVar("T3", default=str) + +class ClassA1(Generic[T2, T3]): ... + +def func_a1( + a: ClassA1, + b: ClassA1[float], + c: ClassA1[float, float], + d: ClassA1[float, float, float], # E: "ClassA1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + +class ClassA2(Generic[T1, T2, T3]): ... + +def func_a2( + a: ClassA2, + b: ClassA2[float], + c: ClassA2[float, float], + d: ClassA2[float, float, float], + e: ClassA2[float, float, float, float], # E: "ClassA2" expects between 1 and 3 type arguments, but 4 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.int, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.str]" + reveal_type(d) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]" + reveal_type(e) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]" + +[case testTypeVarDefaultsClass2] +from typing import Generic, ParamSpec + +P1 = ParamSpec("P1") +P2 = ParamSpec("P2", default=[int, str]) +P3 = ParamSpec("P3", default=...) + +class ClassB1(Generic[P2, P3]): ... + +def func_b1( + a: ClassB1, + b: ClassB1[[float]], + c: ClassB1[[float], [float]], + d: ClassB1[[float], [float], [float]], # E: "ClassB1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], ...]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + +class ClassB2(Generic[P1, P2]): ... + +def func_b2( + a: ClassB2, + b: ClassB2[[float]], + c: ClassB2[[float], [float]], + d: ClassB2[[float], [float], [float]], # E: "ClassB2" expects between 1 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" + reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]" + reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" + +[case testTypeVarDefaultsClass3] +from typing import Generic, Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T1 = TypeVar("T1") +T3 = TypeVar("T3", default=str) + +Ts1 = TypeVarTuple("Ts1") +Ts2 = TypeVarTuple("Ts2", default=Unpack[Tuple[int, str]]) +Ts3 = TypeVarTuple("Ts3", default=Unpack[Tuple[float, ...]]) +Ts4 = TypeVarTuple("Ts4", default=Unpack[Tuple[()]]) + +class ClassC1(Generic[Unpack[Ts2]]): ... + +def func_c1( + a: ClassC1, + b: ClassC1[float], +) -> None: + # reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]" + +class ClassC2(Generic[T3, Unpack[Ts3]]): ... + +def func_c2( + a: ClassC2, + b: ClassC2[int], + c: ClassC2[int, Unpack[Tuple[()]]], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassC2[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" + # reveal_type(b) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + reveal_type(c) # N: Revealed type is "__main__.ClassC2[builtins.int]" + +class ClassC3(Generic[T3, Unpack[Ts4]]): ... + +def func_c3( + a: ClassC3, + b: ClassC3[int], + c: ClassC3[int, Unpack[Tuple[float]]] +) -> None: + # reveal_type(a) # Revealed type is "__main__.ClassC3[builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "__main__.ClassC3[builtins.int]" + reveal_type(c) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]" + +class ClassC4(Generic[T1, Unpack[Ts1], T3]): ... + +def func_c4( + a: ClassC4, + b: ClassC4[int], + c: ClassC4[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassC4[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" + # reveal_type(b) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO + reveal_type(c) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" +[builtins fixtures/tuple.pyi] From 3838bff555de4237cb77ef2a191a6791a4d0ae7a Mon Sep 17 00:00:00 2001 From: Aleksi Tarvainen Date: Thu, 25 Jan 2024 15:24:09 +0200 Subject: [PATCH 0461/1617] Docs: Add missing class instantiation to cheat sheet (#16817) --- docs/source/cheat_sheet_py3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index fe5761ca6187..7ae8eeb59d66 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -209,6 +209,7 @@ Classes # This will allow access to any A.x, if x is compatible with the return type def __getattr__(self, name: str) -> int: ... + a = A() a.foo = 42 # Works a.bar = 'Ex-parrot' # Fails type checking From 717a263fcb594689ff48b1d1a88a8bc4f10c2652 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:20:24 -0800 Subject: [PATCH 0462/1617] stubtest: adjust symtable logic (#16823) Fixes https://github.com/python/typeshed/issues/11318 --- mypy/stubtest.py | 59 ++++++++++++++++++++------------------- mypy/test/teststubtest.py | 18 ++++++++++++ 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 9a038105dc82..7ab3a2b1e5d0 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -10,6 +10,7 @@ import collections.abc import copy import enum +import functools import importlib import importlib.machinery import inspect @@ -310,35 +311,23 @@ def _verify_exported_names( ) -def _get_imported_symbol_names(runtime: types.ModuleType) -> frozenset[str] | None: - """Retrieve the names in the global namespace which are known to be imported. +@functools.lru_cache +def _module_symbol_table(runtime: types.ModuleType) -> symtable.SymbolTable | None: + """Retrieve the symbol table for the module (or None on failure). - 1). Use inspect to retrieve the source code of the module - 2). Use symtable to parse the source and retrieve names that are known to be imported - from other modules. - - If either of the above steps fails, return `None`. - - Note that if a set of names is returned, - it won't include names imported via `from foo import *` imports. + 1) Use inspect to retrieve the source code of the module + 2) Use symtable to parse the source (and use what symtable knows for its purposes) """ try: source = inspect.getsource(runtime) except (OSError, TypeError, SyntaxError): return None - if not source.strip(): - # The source code for the module was an empty file, - # no point in parsing it with symtable - return frozenset() - try: - module_symtable = symtable.symtable(source, runtime.__name__, "exec") + return symtable.symtable(source, runtime.__name__, "exec") except SyntaxError: return None - return frozenset(sym.get_name() for sym in module_symtable.get_symbols() if sym.is_imported()) - @verify.register(nodes.MypyFile) def verify_mypyfile( @@ -369,25 +358,37 @@ def verify_mypyfile( if not o.module_hidden and (not is_probably_private(m) or hasattr(runtime, m)) } - imported_symbols = _get_imported_symbol_names(runtime) - def _belongs_to_runtime(r: types.ModuleType, attr: str) -> bool: """Heuristics to determine whether a name originates from another module.""" obj = getattr(r, attr) if isinstance(obj, types.ModuleType): return False - if callable(obj): - # It's highly likely to be a class or a function if it's callable, - # so the __module__ attribute will give a good indication of which module it comes from + + symbol_table = _module_symbol_table(r) + if symbol_table is not None: try: - obj_mod = obj.__module__ - except Exception: + symbol = symbol_table.lookup(attr) + except KeyError: pass else: - if isinstance(obj_mod, str): - return bool(obj_mod == r.__name__) - if imported_symbols is not None: - return attr not in imported_symbols + if symbol.is_imported(): + # symtable says we got this from another module + return False + # But we can't just return True here, because symtable doesn't know about symbols + # that come from `from module import *` + if symbol.is_assigned(): + # symtable knows we assigned this symbol in the module + return True + + # The __module__ attribute is unreliable for anything except functions and classes, + # but it's our best guess at this point + try: + obj_mod = obj.__module__ + except Exception: + pass + else: + if isinstance(obj_mod, str): + return bool(obj_mod == r.__name__) return True runtime_public_contents = ( diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 3f231d8afbcc..55f35200a7f5 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1285,6 +1285,24 @@ def test_missing_no_runtime_all(self) -> Iterator[Case]: yield Case(stub="", runtime="from json.scanner import NUMBER_RE", error=None) yield Case(stub="", runtime="from string import ascii_letters", error=None) + @collect_cases + def test_missing_no_runtime_all_terrible(self) -> Iterator[Case]: + yield Case( + stub="", + runtime=""" +import sys +import types +import __future__ +_m = types.SimpleNamespace() +_m.annotations = __future__.annotations +sys.modules["_terrible_stubtest_test_module"] = _m + +from _terrible_stubtest_test_module import * +assert annotations +""", + error=None, + ) + @collect_cases def test_non_public_1(self) -> Iterator[Case]: yield Case( From 09490c890590d1ead414b06a332c7570fd589342 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 28 Jan 2024 02:37:14 +0100 Subject: [PATCH 0463/1617] Use TypeVar defaults instead of Any when fixing TypeAlias types (PEP 696) (#16825) This PR applies the TypeVar defaults to `TypeAlias` types instead of using `Any` exclusively, similar to https://github.com/python/mypy/pull/16812. Again `TypeVarTuple` defaults aren't handled correctly yet. Ref: https://github.com/python/mypy/issues/14851 --- mypy/checkexpr.py | 1 + mypy/typeanal.py | 104 ++++++++----- test-data/unit/check-typevar-defaults.test | 161 +++++++++++++++++++++ 3 files changed, 232 insertions(+), 34 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fbedd95e8fd2..a4b66bb9932c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4711,6 +4711,7 @@ class LongName(Generic[T]): ... item = get_proper_type( set_any_tvars( alias, + [], ctx.line, ctx.column, self.chk.options, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d10f26f5199a..678407acc3ac 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1927,18 +1927,19 @@ def instantiate_type_alias( if any(unknown_unpack(a) for a in args): # This type is not ready to be validated, because of unknown total count. # Note that we keep the kind of Any for consistency. - return set_any_tvars(node, ctx.line, ctx.column, options, special_form=True) + return set_any_tvars(node, [], ctx.line, ctx.column, options, special_form=True) - exp_len = len(node.alias_tvars) + max_tv_count = len(node.alias_tvars) act_len = len(args) if ( - exp_len > 0 + max_tv_count > 0 and act_len == 0 and not (empty_tuple_index and node.tvar_tuple_index is not None) ): # Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...] return set_any_tvars( node, + args, ctx.line, ctx.column, options, @@ -1946,7 +1947,7 @@ def instantiate_type_alias( fail=fail, unexpanded_type=unexpanded_type, ) - if exp_len == 0 and act_len == 0: + if max_tv_count == 0 and act_len == 0: if no_args: assert isinstance(node.target, Instance) # type: ignore[misc] # Note: this is the only case where we use an eager expansion. See more info about @@ -1954,7 +1955,7 @@ def instantiate_type_alias( return Instance(node.target.type, [], line=ctx.line, column=ctx.column) return TypeAliasType(node, [], line=ctx.line, column=ctx.column) if ( - exp_len == 0 + max_tv_count == 0 and act_len > 0 and isinstance(node.target, Instance) # type: ignore[misc] and no_args @@ -1967,32 +1968,48 @@ def instantiate_type_alias( if any(isinstance(a, UnpackType) for a in args): # A variadic unpack in fixed size alias (fixed unpacks must be flattened by the caller) fail(message_registry.INVALID_UNPACK_POSITION, ctx, code=codes.VALID_TYPE) - return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True) - correct = act_len == exp_len + return set_any_tvars(node, [], ctx.line, ctx.column, options, from_error=True) + min_tv_count = sum(not tv.has_default() for tv in node.alias_tvars) + fill_typevars = act_len != max_tv_count + correct = min_tv_count <= act_len <= max_tv_count else: - correct = act_len >= exp_len - 1 + min_tv_count = sum( + not tv.has_default() and not isinstance(tv, TypeVarTupleType) + for tv in node.alias_tvars + ) + correct = act_len >= min_tv_count for a in args: if isinstance(a, UnpackType): unpacked = get_proper_type(a.type) if isinstance(unpacked, Instance) and unpacked.type.fullname == "builtins.tuple": # Variadic tuple is always correct. correct = True - if not correct: - if use_standard_error: - # This is used if type alias is an internal representation of another type, - # for example a generic TypedDict or NamedTuple. - msg = wrong_type_arg_count(exp_len, exp_len, str(act_len), node.name) - else: - if node.tvar_tuple_index is not None: - exp_len_str = f"at least {exp_len - 1}" + fill_typevars = not correct + if fill_typevars: + if not correct: + if use_standard_error: + # This is used if type alias is an internal representation of another type, + # for example a generic TypedDict or NamedTuple. + msg = wrong_type_arg_count(max_tv_count, max_tv_count, str(act_len), node.name) else: - exp_len_str = str(exp_len) - msg = ( - "Bad number of arguments for type alias," - f" expected: {exp_len_str}, given: {act_len}" - ) - fail(msg, ctx, code=codes.TYPE_ARG) - return set_any_tvars(node, ctx.line, ctx.column, options, from_error=True) + if node.tvar_tuple_index is not None: + msg = ( + "Bad number of arguments for type alias," + f" expected: at least {min_tv_count}, given: {act_len}" + ) + elif min_tv_count != max_tv_count: + msg = ( + "Bad number of arguments for type alias," + f" expected between {min_tv_count} and {max_tv_count}, given: {act_len}" + ) + else: + msg = ( + "Bad number of arguments for type alias," + f" expected: {min_tv_count}, given: {act_len}" + ) + fail(msg, ctx, code=codes.TYPE_ARG) + args = [] + return set_any_tvars(node, args, ctx.line, ctx.column, options, from_error=True) elif node.tvar_tuple_index is not None: # We also need to check if we are not performing a type variable tuple split. unpack = find_unpack_in_list(args) @@ -2006,7 +2023,7 @@ def instantiate_type_alias( act_suffix = len(args) - unpack - 1 if act_prefix < exp_prefix or act_suffix < exp_suffix: fail("TypeVarTuple cannot be split", ctx, code=codes.TYPE_ARG) - return set_any_tvars(node, ctx.line, ctx.column, options, 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. @@ -2025,6 +2042,7 @@ def instantiate_type_alias( def set_any_tvars( node: TypeAlias, + args: list[Type], newline: int, newcolumn: int, options: Options, @@ -2041,7 +2059,33 @@ def set_any_tvars( type_of_any = TypeOfAny.special_form else: type_of_any = TypeOfAny.from_omitted_generics - if disallow_any and node.alias_tvars: + any_type = AnyType(type_of_any, line=newline, column=newcolumn) + + env: dict[TypeVarId, Type] = {} + used_any_type = False + has_type_var_tuple_type = False + for tv, arg in itertools.zip_longest(node.alias_tvars, args, fillvalue=None): + if tv is None: + continue + if arg is None: + if tv.has_default(): + arg = tv.default + else: + arg = any_type + used_any_type = True + if isinstance(tv, TypeVarTupleType): + # TODO Handle TypeVarTuple defaults + has_type_var_tuple_type = True + arg = UnpackType(Instance(tv.tuple_fallback.type, [any_type])) + args.append(arg) + env[tv.id] = arg + t = TypeAliasType(node, args, newline, newcolumn) + if not has_type_var_tuple_type: + fixed = expand_type(t, env) + assert isinstance(fixed, TypeAliasType) + t.args = fixed.args + + if used_any_type and disallow_any and node.alias_tvars: assert fail is not None if unexpanded_type: type_str = ( @@ -2057,15 +2101,7 @@ def set_any_tvars( Context(newline, newcolumn), code=codes.TYPE_ARG, ) - any_type = AnyType(type_of_any, line=newline, column=newcolumn) - - args: list[Type] = [] - for tv in node.alias_tvars: - if isinstance(tv, TypeVarTupleType): - args.append(UnpackType(Instance(tv.tuple_fallback.type, [any_type]))) - else: - args.append(any_type) - return TypeAliasType(node, args, newline, newcolumn) + return t def flatten_tvars(lists: list[list[T]]) -> list[T]: diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index c4d258d50ee5..0c531dd3f18b 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -239,3 +239,164 @@ def func_c4( # reveal_type(b) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO reveal_type(c) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultsTypeAlias1] +# flags: --disallow-any-generics +from typing import Any, Dict, List, Tuple, TypeVar, Union + +T1 = TypeVar("T1") +T2 = TypeVar("T2", default=int) +T3 = TypeVar("T3", default=str) +T4 = TypeVar("T4") + +TA1 = Dict[T2, T3] + +def func_a1( + a: TA1, + b: TA1[float], + c: TA1[float, float], + d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3 +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + +TA2 = Tuple[T1, T2, T3] + +def func_a2( + a: TA2, # E: Missing type parameters for generic type "TA2" + b: TA2[float], + c: TA2[float, float], + d: TA2[float, float, float], + e: TA2[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4 +) -> None: + reveal_type(a) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "Tuple[builtins.float, builtins.int, builtins.str]" + reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.str]" + reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]" + reveal_type(e) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]" + +TA3 = Union[Dict[T1, T2], List[T3]] + +def func_a3( + a: TA3, # E: Missing type parameters for generic type "TA3" + b: TA3[float], + c: TA3[float, float], + d: TA3[float, float, float], + e: TA3[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4 +) -> None: + reveal_type(a) # N: Revealed type is "Union[builtins.dict[Any, builtins.int], builtins.list[builtins.str]]" + reveal_type(b) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.int], builtins.list[builtins.str]]" + reveal_type(c) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.float], builtins.list[builtins.str]]" + reveal_type(d) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.float], builtins.list[builtins.float]]" + reveal_type(e) # N: Revealed type is "Union[builtins.dict[Any, builtins.int], builtins.list[builtins.str]]" + +TA4 = Tuple[T1, T4, T2] + +def func_a4( + a: TA4, # E: Missing type parameters for generic type "TA4" + b: TA4[float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 1 + c: TA4[float, float], + d: TA4[float, float, float], + e: TA4[float, float, float, float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 4 +) -> None: + reveal_type(a) # N: Revealed type is "Tuple[Any, Any, builtins.int]" + reveal_type(b) # N: Revealed type is "Tuple[Any, Any, builtins.int]" + reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.int]" + reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]" + reveal_type(e) # N: Revealed type is "Tuple[Any, Any, builtins.int]" +[builtins fixtures/dict.pyi] + +[case testTypeVarDefaultsTypeAlias2] +# flags: --disallow-any-generics +from typing import Any, Generic, ParamSpec + +P1 = ParamSpec("P1") +P2 = ParamSpec("P2", default=[int, str]) +P3 = ParamSpec("P3", default=...) + +class ClassB1(Generic[P2, P3]): ... +TB1 = ClassB1[P2, P3] + +def func_b1( + a: TB1, + b: TB1[[float]], + c: TB1[[float], [float]], + d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3 +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + +class ClassB2(Generic[P1, P2]): ... +TB2 = ClassB2[P1, P2] + +def func_b2( + a: TB2, # E: Missing type parameters for generic type "TB2" + b: TB2[[float]], + c: TB2[[float], [float]], + d: TB2[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 1 and 2, given: 3 +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" + reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]" + reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultsTypeAlias3] +# flags: --disallow-any-generics +from typing import Tuple, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T1 = TypeVar("T1") +T3 = TypeVar("T3", default=str) + +Ts1 = TypeVarTuple("Ts1") +Ts2 = TypeVarTuple("Ts2", default=Unpack[Tuple[int, str]]) +Ts3 = TypeVarTuple("Ts3", default=Unpack[Tuple[float, ...]]) +Ts4 = TypeVarTuple("Ts4", default=Unpack[Tuple[()]]) + +TC1 = Tuple[Unpack[Ts2]] + +def func_c1( + a: TC1, + b: TC1[float], +) -> None: + # reveal_type(a) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "Tuple[builtins.float]" + +TC2 = Tuple[T3, Unpack[Ts3]] + +def func_c2( + a: TC2, + b: TC2[int], + c: TC2[int, Unpack[Tuple[()]]], +) -> None: + # reveal_type(a) # Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + # reveal_type(b) # Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + reveal_type(c) # N: Revealed type is "Tuple[builtins.int]" + +TC3 = Tuple[T3, Unpack[Ts4]] + +def func_c3( + a: TC3, + b: TC3[int], + c: TC3[int, Unpack[Tuple[float]]], +) -> None: + # reveal_type(a) # Revealed type is "Tuple[builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]" + +TC4 = Tuple[T1, Unpack[Ts1], T3] + +def func_c4( + a: TC4, # E: Missing type parameters for generic type "TC4" + b: TC4[int], + c: TC4[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "Tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" + # reveal_type(b) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO + reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]" +[builtins fixtures/tuple.pyi] From 1da0ebe5ab96cb4982184e405260822a18b3502c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:27:19 -0800 Subject: [PATCH 0464/1617] Various docs improvements (#16836) Recommend `--disable-error-code=import-untyped`. It's probably strictly better than `--ignore-missing-imports` for most users. Remove the displaying error codes section, since it's on by default. Some more advice and discoverability for "using mypy with an existing codebase". --- docs/source/config_file.rst | 4 ++++ docs/source/error_codes.rst | 23 +++++++---------------- docs/source/existing_code.rst | 21 ++++++++++++++------- docs/source/running_mypy.rst | 18 +++++++++++------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 910b015df658..ac110cbed9f1 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -3,6 +3,10 @@ The mypy configuration file =========================== +Mypy is very configurable. This is most useful when introducing typing to +an existing codebase. See :ref:`existing-code` for concrete advice for +that situation. + Mypy supports reading configuration settings from a file with the following precedence order: 1. ``./mypy.ini`` diff --git a/docs/source/error_codes.rst b/docs/source/error_codes.rst index a71168cadf30..35fad161f8a2 100644 --- a/docs/source/error_codes.rst +++ b/docs/source/error_codes.rst @@ -19,22 +19,6 @@ Most error codes are shared between multiple related error messages. Error codes may change in future mypy releases. - -Displaying error codes ----------------------- - -Error codes are displayed by default. Use :option:`--hide-error-codes ` -or config ``hide_error_codes = True`` to hide error codes. Error codes are shown inside square brackets: - -.. code-block:: text - - $ mypy prog.py - prog.py:1: error: "str" has no attribute "trim" [attr-defined] - -It's also possible to require error codes for ``type: ignore`` comments. -See :ref:`ignore-without-code` for more information. - - .. _silence-error-codes: Silencing errors based on error codes @@ -121,3 +105,10 @@ Similar logic works for disabling error codes globally. If a given error code is a subcode of another one, it will be mentioned in the documentation for the narrower code. This hierarchy is not nested: there cannot be subcodes of other subcodes. + + +Requiring error codes +--------------------- + +It's possible to require error codes be specified in ``type: ignore`` comments. +See :ref:`ignore-without-code` for more information. diff --git a/docs/source/existing_code.rst b/docs/source/existing_code.rst index c66008f4b782..0a5ac2bfa8f6 100644 --- a/docs/source/existing_code.rst +++ b/docs/source/existing_code.rst @@ -31,8 +31,8 @@ invocation to your codebase, or adding your mypy invocation to existing tools you use to run tests, like ``tox``. * Make sure everyone runs mypy with the same options. Checking a mypy - :ref:`configuration file ` into your codebase can help - with this. + :ref:`configuration file ` into your codebase is the + easiest way to do this. * Make sure everyone type checks the same set of files. See :ref:`specifying-code-to-be-checked` for details. @@ -48,7 +48,7 @@ A simple CI script could look something like this: .. code-block:: text - python3 -m pip install mypy==0.971 + python3 -m pip install mypy==1.8 # Run your standardised mypy invocation, e.g. mypy my_project # This could also look like `scripts/run_mypy.sh`, `tox run -e mypy`, `make mypy`, etc @@ -74,6 +74,11 @@ You could even invert this, by setting ``ignore_errors = True`` in your global config section and only enabling error reporting with ``ignore_errors = False`` for the set of modules you are ready to type check. +The per-module configuration that mypy's configuration file allows can be +extremely useful. Many configuration options can be enabled or disabled +only for specific modules. In particular, you can also enable or disable +various error codes on a per-module basis, see :ref:`error-codes`. + Fixing errors related to imports -------------------------------- @@ -89,7 +94,7 @@ that it can't find, that don't have types, or don't have stub files: Sometimes these can be fixed by installing the relevant packages or stub libraries in the environment you're running ``mypy`` in. -See :ref:`ignore-missing-imports` for a complete reference on these errors +See :ref:`fix-missing-imports` for a complete reference on these errors and the ways in which you can fix them. You'll likely find that you want to suppress all errors from importing @@ -118,13 +123,15 @@ codebase, use a config like this: ignore_missing_imports = True If you get a large number of errors, you may want to ignore all errors -about missing imports, for instance by setting :confval:`ignore_missing_imports` -to true globally. This can hide errors later on, so we recommend avoiding this +about missing imports, for instance by setting +:option:`--disable-error-code=import-untyped `. +or setting :confval:`ignore_missing_imports` to true globally. +This can hide errors later on, so we recommend avoiding this if possible. Finally, mypy allows fine-grained control over specific import following behaviour. It's very easy to silently shoot yourself in the foot when playing -around with these, so it's mostly recommended as a last resort. For more +around with these, so this should be a last resort. For more details, look :ref:`here `. Prioritise annotating widely imported modules diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index b0cefec9dafa..f959e9af2391 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -305,16 +305,20 @@ not catch errors in its use. The ``.*`` after ``foobar`` will ignore imports of ``foobar`` modules and subpackages in addition to the ``foobar`` top-level package namespace. -3. To suppress *all* missing import errors for *all* libraries in your codebase, - invoke mypy with the :option:`--ignore-missing-imports ` command line flag or set - the :confval:`ignore_missing_imports` - config file option to True - in the *global* section of your mypy config file:: +3. To suppress *all* missing import errors for *all* untyped libraries + in your codebase, use :option:`--disable-error-code=import-untyped `. + See :ref:`code-import-untyped` for more details on this error code. + + You can also set :confval:`disable_error_code`, like so:: [mypy] - ignore_missing_imports = True + disable_error_code = import-untyped + - We recommend using this approach only as a last resort: it's equivalent + You can also set the :option:`--ignore-missing-imports ` + command line flag or set the :confval:`ignore_missing_imports` config file + option to True in the *global* section of your mypy config file. We + recommend avoiding ``--ignore-missing-imports`` if possible: it's equivalent to adding a ``# type: ignore`` to all unresolved imports in your codebase. From 418377892f3220958f0d71c3429a9e7e638b8ca0 Mon Sep 17 00:00:00 2001 From: Stefanie Molin <24376333+stefmolin@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:27:56 -0500 Subject: [PATCH 0465/1617] Fix numbering error in docs (#16838) --- docs/source/running_mypy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index f959e9af2391..25b34b247b4b 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -391,11 +391,11 @@ this error, try: installing into the environment you expect by running pip like ``python -m pip ...``. -2. Reading the :ref:`finding-imports` section below to make sure you +3. Reading the :ref:`finding-imports` section below to make sure you understand how exactly mypy searches for and finds modules and modify how you're invoking mypy accordingly. -3. Directly specifying the directory containing the module you want to +4. Directly specifying the directory containing the module you want to type check from the command line, by using the :confval:`mypy_path` or :confval:`files` config file options, or by using the ``MYPYPATH`` environment variable. From 7a746c4dbd8a0d35e9a3889c9fc861b63aec66cc Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:42:46 +0100 Subject: [PATCH 0466/1617] Consider TypeVarTuple to be invariant (#16759) The TypeVarTuple equality checks mentioned in PEP 646 assume TypeVarTuple to be `invariant`. https://peps.python.org/pep-0646/#type-variable-tuple-equality Fixes: #16739 --- mypy/constraints.py | 5 +++-- mypy/test/testconstraints.py | 24 +++++++++++++++++----- test-data/unit/check-typevar-tuple.test | 27 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index d6a4b28799e5..c4eba2ca1ede 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -894,8 +894,9 @@ def visit_instance(self, template: Instance) -> list[Constraint]: res.append(Constraint(template_arg, SUBTYPE_OF, suffix)) res.append(Constraint(template_arg, SUPERTYPE_OF, suffix)) elif isinstance(tvar, TypeVarTupleType): - # Handle variadic type variables covariantly for consistency. - res.extend(infer_constraints(template_arg, mapped_arg, self.direction)) + # Consider variadic type variables to be invariant. + res.extend(infer_constraints(template_arg, mapped_arg, SUBTYPE_OF)) + res.extend(infer_constraints(template_arg, mapped_arg, SUPERTYPE_OF)) return res if ( template.type.is_protocol diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index 5ec292f07056..a701a173cbaa 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -30,13 +30,18 @@ def test_basic_type_var_tuple_subtype(self) -> None: def test_basic_type_var_tuple(self) -> None: fx = self.fx - assert infer_constraints( - Instance(fx.gvi, [UnpackType(fx.ts)]), Instance(fx.gvi, [fx.a, fx.b]), SUPERTYPE_OF - ) == [ + assert set( + infer_constraints( + Instance(fx.gvi, [UnpackType(fx.ts)]), Instance(fx.gvi, [fx.a, fx.b]), SUPERTYPE_OF + ) + ) == { Constraint( type_var=fx.ts, op=SUPERTYPE_OF, target=TupleType([fx.a, fx.b], fx.std_tuple) - ) - ] + ), + Constraint( + type_var=fx.ts, op=SUBTYPE_OF, target=TupleType([fx.a, fx.b], fx.std_tuple) + ), + } def test_type_var_tuple_with_prefix_and_suffix(self) -> None: fx = self.fx @@ -51,6 +56,9 @@ def test_type_var_tuple_with_prefix_and_suffix(self) -> None: Constraint( type_var=fx.ts, op=SUPERTYPE_OF, target=TupleType([fx.b, fx.c], fx.std_tuple) ), + Constraint( + type_var=fx.ts, op=SUBTYPE_OF, target=TupleType([fx.b, fx.c], fx.std_tuple) + ), Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.d), } @@ -64,7 +72,9 @@ def test_unpack_homogenous_tuple(self) -> None: ) ) == { Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.a), + Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.a), Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.b), + Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b), } def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None: @@ -78,7 +88,9 @@ def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None: ) == { Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.a), Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.b), + Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.b), Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.c), + Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.c), Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.d), } @@ -93,7 +105,9 @@ def test_unpack_with_prefix_and_suffix(self) -> None: ) == { Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.a), Constraint(type_var=fx.t, op=SUPERTYPE_OF, target=fx.b), + Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b), Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.c), + Constraint(type_var=fx.s, op=SUBTYPE_OF, target=fx.c), Constraint(type_var=fx.u, op=SUPERTYPE_OF, target=fx.d), } diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 9c8d21114d4c..70229f5d9a62 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2277,3 +2277,30 @@ higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Cal higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleInvariant] +from typing import Generic, Tuple +from typing_extensions import Unpack, TypeVarTuple +Ts = TypeVarTuple("Ts") + +class Array(Generic[Unpack[Ts]]): ... + +def pointwise_multiply(x: Array[Unpack[Ts]], y: Array[Unpack[Ts]]) -> Array[Unpack[Ts]]: ... + +def a1(x: Array[int], y: Array[str], z: Array[int, str]) -> None: + reveal_type(pointwise_multiply(x, x)) # N: Revealed type is "__main__.Array[builtins.int]" + reveal_type(pointwise_multiply(x, y)) # E: Cannot infer type argument 1 of "pointwise_multiply" \ + # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]" + reveal_type(pointwise_multiply(x, z)) # E: Cannot infer type argument 1 of "pointwise_multiply" \ + # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]" + +def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: + ... + +def a2(x: Array[int, str]) -> None: + reveal_type(func(x, 2, "Hello")) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + reveal_type(func(x, 2)) # E: Cannot infer type argument 1 of "func" \ + # N: Revealed type is "builtins.tuple[Any, ...]" + reveal_type(func(x, 2, "Hello", True)) # E: Cannot infer type argument 1 of "func" \ + # N: Revealed type is "builtins.tuple[Any, ...]" +[builtins fixtures/tuple.pyi] From ed50208055e8c5f99caf7da0cea8c99fcfdc5670 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:44:09 +0100 Subject: [PATCH 0467/1617] Update TypeAlias error messages to remove colon (#16831) Small update to adjust the error messages following the suggestion in https://github.com/python/mypy/pull/16825#discussion_r1468507923. --- mypy/typeanal.py | 6 +++--- test-data/unit/check-generics.test | 9 ++++----- test-data/unit/check-parameter-specification.test | 5 +++-- test-data/unit/check-type-aliases.test | 5 ++--- test-data/unit/check-typevar-defaults.test | 14 +++++++------- test-data/unit/check-typevar-tuple.test | 6 +++--- test-data/unit/fine-grained.test | 8 ++++---- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 678407acc3ac..1bcba5f0ca88 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1995,17 +1995,17 @@ def instantiate_type_alias( if node.tvar_tuple_index is not None: msg = ( "Bad number of arguments for type alias," - f" expected: at least {min_tv_count}, given: {act_len}" + f" expected at least {min_tv_count}, given {act_len}" ) elif min_tv_count != max_tv_count: msg = ( "Bad number of arguments for type alias," - f" expected between {min_tv_count} and {max_tv_count}, given: {act_len}" + f" expected between {min_tv_count} and {max_tv_count}, given {act_len}" ) else: msg = ( "Bad number of arguments for type alias," - f" expected: {min_tv_count}, given: {act_len}" + f" expected {min_tv_count}, given {act_len}" ) fail(msg, ctx, code=codes.TYPE_ARG) args = [] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index e2f65ed39c1e..337e92daf365 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -630,7 +630,7 @@ main:11:5: error: "Node" expects 2 type arguments, but 3 given main:15:10: error: "list" expects 1 type argument, but 2 given main:16:19: error: "list" expects 1 type argument, but 2 given main:17:25: error: "Node" expects 2 type arguments, but 1 given -main:19:5: error: Bad number of arguments for type alias, expected: 1, given: 2 +main:19:5: error: Bad number of arguments for type alias, expected 1, given 2 main:22:13: note: Revealed type is "__main__.Node[builtins.int, builtins.str]" main:24:13: note: Revealed type is "__main__.Node[__main__.Node[builtins.int, builtins.int], builtins.list[builtins.int]]" main:26:5: error: Type variable "__main__.T" is invalid as target for type alias @@ -944,7 +944,7 @@ Transform = Callable[[T, int], Tuple[T, R]] [case testGenericTypeAliasesImportingWithoutTypeVarError] from a import Alias -x: Alias[int, str] # E: Bad number of arguments for type alias, expected: 1, given: 2 +x: Alias[int, str] # E: Bad number of arguments for type alias, expected 1, given 2 reveal_type(x) # N: Revealed type is "builtins.list[builtins.list[Any]]" [file a.py] @@ -953,7 +953,6 @@ T = TypeVar('T') Alias = List[List[T]] [builtins fixtures/list.pyi] -[out] [case testGenericAliasWithTypeVarsFromDifferentModules] from mod import Alias, TypeVar @@ -1000,8 +999,8 @@ reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" reveal_type(y) # N: Revealed type is "builtins.int" U[int] # E: Type application targets a non-generic function or class -O[int] # E: Bad number of arguments for type alias, expected: 0, given: 1 # E: Type application is only supported for generic classes -[out] +O[int] # E: Bad number of arguments for type alias, expected 0, given 1 \ + # E: Type application is only supported for generic classes [case testAliasesInClassBodyNormalVsSubscripted] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index af2be84f5412..7a5c5934a94e 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1506,9 +1506,10 @@ def g(x: A[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' C = Callable[P, T] -x: C[int] # E: Bad number of arguments for type alias, expected: 2, given: 1 +x: C[int] # E: Bad number of arguments for type alias, expected 2, given 1 y: C[int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" -z: C[int, str, bytes] # E: Bad number of arguments for type alias, expected: 2, given: 3 +z: C[int, str, bytes] # E: Bad number of arguments for type alias, expected 2, given 3 + [builtins fixtures/paramspec.pyi] [case testTrivialParametersHandledCorrectly] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 4364a9bfa9dc..a43233eed973 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -330,10 +330,9 @@ c: C t: T reveal_type(c) # N: Revealed type is "def (*Any, **Any) -> Any" reveal_type(t) # N: Revealed type is "builtins.tuple[Any, ...]" -bad: C[int] # E: Bad number of arguments for type alias, expected: 0, given: 1 -also_bad: T[int] # E: Bad number of arguments for type alias, expected: 0, given: 1 +bad: C[int] # E: Bad number of arguments for type alias, expected 0, given 1 +also_bad: T[int] # E: Bad number of arguments for type alias, expected 0, given 1 [builtins fixtures/tuple.pyi] -[out] [case testAliasRefOnClass] from typing import Generic, TypeVar, Type diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 0c531dd3f18b..4b509cd4fc40 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -255,7 +255,7 @@ def func_a1( a: TA1, b: TA1[float], c: TA1[float, float], - d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3 + d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 ) -> None: reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" reveal_type(b) # N: Revealed type is "builtins.dict[builtins.float, builtins.str]" @@ -269,7 +269,7 @@ def func_a2( b: TA2[float], c: TA2[float, float], d: TA2[float, float, float], - e: TA2[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4 + e: TA2[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given 4 ) -> None: reveal_type(a) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]" reveal_type(b) # N: Revealed type is "Tuple[builtins.float, builtins.int, builtins.str]" @@ -284,7 +284,7 @@ def func_a3( b: TA3[float], c: TA3[float, float], d: TA3[float, float, float], - e: TA3[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given: 4 + e: TA3[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given 4 ) -> None: reveal_type(a) # N: Revealed type is "Union[builtins.dict[Any, builtins.int], builtins.list[builtins.str]]" reveal_type(b) # N: Revealed type is "Union[builtins.dict[builtins.float, builtins.int], builtins.list[builtins.str]]" @@ -296,10 +296,10 @@ TA4 = Tuple[T1, T4, T2] def func_a4( a: TA4, # E: Missing type parameters for generic type "TA4" - b: TA4[float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 1 + b: TA4[float], # E: Bad number of arguments for type alias, expected between 2 and 3, given 1 c: TA4[float, float], d: TA4[float, float, float], - e: TA4[float, float, float, float], # E: Bad number of arguments for type alias, expected between 2 and 3, given: 4 + e: TA4[float, float, float, float], # E: Bad number of arguments for type alias, expected between 2 and 3, given 4 ) -> None: reveal_type(a) # N: Revealed type is "Tuple[Any, Any, builtins.int]" reveal_type(b) # N: Revealed type is "Tuple[Any, Any, builtins.int]" @@ -323,7 +323,7 @@ def func_b1( a: TB1, b: TB1[[float]], c: TB1[[float], [float]], - d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given: 3 + d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 ) -> None: reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" @@ -337,7 +337,7 @@ def func_b2( a: TB2, # E: Missing type parameters for generic type "TB2" b: TB2[[float]], c: TB2[[float], [float]], - d: TB2[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 1 and 2, given: 3 + d: TB2[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 1 and 2, given 3 ) -> None: reveal_type(a) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" reveal_type(b) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]" diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 70229f5d9a62..cc3dc4ed9f39 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -769,15 +769,15 @@ Ts = TypeVarTuple("Ts") class G(Generic[Unpack[Ts]]): ... A = List[Tuple[T, Unpack[Ts], S]] -x: A[int] # E: Bad number of arguments for type alias, expected: at least 2, given: 1 +x: A[int] # E: Bad number of arguments for type alias, expected at least 2, given 1 reveal_type(x) # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]" B = Callable[[T, S, Unpack[Ts]], int] -y: B[int] # E: Bad number of arguments for type alias, expected: at least 2, given: 1 +y: B[int] # E: Bad number of arguments for type alias, expected at least 2, given 1 reveal_type(y) # N: Revealed type is "def (Any, Any, *Any) -> builtins.int" C = G[T, Unpack[Ts], S] -z: C[int] # E: Bad number of arguments for type alias, expected: at least 2, given: 1 +z: C[int] # E: Bad number of arguments for type alias, expected at least 2, given 1 reveal_type(z) # N: Revealed type is "__main__.G[Any, Unpack[builtins.tuple[Any, ...]], Any]" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 165a2089b466..266d9a9efd01 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -4032,7 +4032,7 @@ def f(x: a.A[str]): [builtins fixtures/dict.pyi] [out] == -b.py:2: error: Bad number of arguments for type alias, expected: 2, given: 1 +b.py:2: error: Bad number of arguments for type alias, expected 2, given 1 [case testAliasFineAdded] import b @@ -5542,7 +5542,7 @@ main:9: error: Function "a.T" is not valid as a type main:9: note: Perhaps you need "Callable[...]" or a callback protocol? main:12: error: Function "a.T" is not valid as a type main:12: note: Perhaps you need "Callable[...]" or a callback protocol? -main:12: error: Bad number of arguments for type alias, expected: 0, given: 1 +main:12: error: Bad number of arguments for type alias, expected 0, given 1 [case testChangeTypeVarToModule] @@ -5576,7 +5576,7 @@ main:9: error: Module "T" is not valid as a type main:9: note: Perhaps you meant to use a protocol matching the module structure? main:12: error: Module "T" is not valid as a type main:12: note: Perhaps you meant to use a protocol matching the module structure? -main:12: error: Bad number of arguments for type alias, expected: 0, given: 1 +main:12: error: Bad number of arguments for type alias, expected 0, given 1 [case testChangeClassToModule] @@ -5628,7 +5628,7 @@ T = int == main:5: error: Free type variable expected in Generic[...] main:9: error: "C" expects no type arguments, but 1 given -main:12: error: Bad number of arguments for type alias, expected: 0, given: 1 +main:12: error: Bad number of arguments for type alias, expected 0, given 1 [case testChangeTypeAliasToModule] From 06b01c80a1a365cb8af80b9eecfbe4b14f04fc3e Mon Sep 17 00:00:00 2001 From: Charlie Denton Date: Tue, 30 Jan 2024 06:39:02 +0000 Subject: [PATCH 0468/1617] Fix `'WriteToConn' object has no attribute 'flush'` (#16801) `WriteToConn` replaces stdout and stderr to capture output, but causes issues because it doesn't implement the `TextIO` API (as expected of `sys.stdout` and `sys.stderr`). By stubbing the rest of the `TextIO` API we prevent issues with other code which uses more of the API than we had previously accounted for. Fixes https://github.com/python/mypy/issues/16678 --- mypy/dmypy_server.py | 4 +-- mypy/dmypy_util.py | 65 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index b4c3fe8fe0dc..3d337eedbf1c 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -221,8 +221,8 @@ def serve(self) -> None: while True: with server: data = receive(server) - sys.stdout = WriteToConn(server, "stdout") # type: ignore[assignment] - sys.stderr = WriteToConn(server, "stderr") # type: ignore[assignment] + sys.stdout = WriteToConn(server, "stdout", sys.stdout.isatty()) + sys.stderr = WriteToConn(server, "stderr", sys.stderr.isatty()) resp: dict[str, Any] = {} if "command" not in data: resp = {"error": "No command found in request"} diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index fe949e8fc294..0baff863b3c3 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -5,8 +5,10 @@ from __future__ import annotations +import io import json -from typing import Any, Final, Iterable +from types import TracebackType +from typing import Any, Final, Iterable, Iterator, TextIO from mypy.ipc import IPCBase @@ -40,12 +42,66 @@ def send(connection: IPCBase, data: Any) -> None: connection.write(json.dumps(data)) -class WriteToConn: +class WriteToConn(TextIO): """Helper class to write to a connection instead of standard output.""" - def __init__(self, server: IPCBase, output_key: str = "stdout") -> None: + def __init__(self, server: IPCBase, output_key: str, isatty: bool) -> None: self.server = server self.output_key = output_key + self._isatty = isatty + + def __enter__(self) -> TextIO: + return self + + def __exit__( + self, + t: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + pass + + def __iter__(self) -> Iterator[str]: + raise io.UnsupportedOperation + + def __next__(self) -> str: + raise io.UnsupportedOperation + + def close(self) -> None: + pass + + def fileno(self) -> int: + raise OSError + + def flush(self) -> None: + pass + + def isatty(self) -> bool: + return self._isatty + + def read(self, n: int = 0) -> str: + raise io.UnsupportedOperation + + def readable(self) -> bool: + return False + + def readline(self, limit: int = 0) -> str: + raise io.UnsupportedOperation + + def readlines(self, hint: int = 0) -> list[str]: + raise io.UnsupportedOperation + + def seek(self, offset: int, whence: int = 0) -> int: + raise io.UnsupportedOperation + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + raise io.UnsupportedOperation + + def truncate(self, size: int | None = 0) -> int: + raise io.UnsupportedOperation def write(self, output: str) -> int: resp: dict[str, Any] = {} @@ -53,6 +109,9 @@ def write(self, output: str) -> int: send(self.server, resp) return len(output) + def writable(self) -> bool: + return True + def writelines(self, lines: Iterable[str]) -> None: for s in lines: self.write(s) From e40935e5e0b55bee9379e35aa27216c3e0287b13 Mon Sep 17 00:00:00 2001 From: thomaswhaley <39359869+thomas-whaley@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:26:22 +1300 Subject: [PATCH 0469/1617] Update new type system discussion links (#16841) As of the 10th of September 2023 the typing-sig mail list is now replaced with [discuss.python.org](https://discuss.python.org/c/typing/32) as explained [here](https://mail.python.org/archives/list/typing-sig@python.org/thread/BENDBBUDRCRMTTK2B5HJ7RCKENFD6DW2/) and [here](https://discuss.python.org/t/about-the-typing-category/34155) The README.md file was referencing the old link. --------- Co-authored-by: Jelle Zijlstra --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8b1ebbc0f2cb..07c170d46cb3 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ To report a bug or request an enhancement: To discuss a new type system feature: -- discuss at [typing-sig mailing list](https://mail.python.org/archives/list/typing-sig@python.org/) -- there is also some historical discussion [here](https://github.com/python/typing/issues) +- discuss at [discuss.python.org](https://discuss.python.org/c/typing/32) +- there is also some historical discussion at the [typing-sig mailing list](https://mail.python.org/archives/list/typing-sig@python.org/) and the [python/typing repo](https://github.com/python/typing/issues) What is mypy? ------------- From 55247c469077963a389941a2386ff2747abe517a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:09:44 +0100 Subject: [PATCH 0470/1617] Apply TypeVar defaults to callables (PEP 696) (#16842) Implement type application for callables with TypeVar defaults. Similar to previous PRs, support for TypeVarTuples is still TODO. Ref: https://github.com/python/mypy/issues/14851 --- mypy/applytype.py | 3 +- mypy/checkexpr.py | 24 ++++-- mypy/messages.py | 21 ++--- test-data/unit/check-typevar-defaults.test | 93 +++++++++++++++++++++- 4 files changed, 122 insertions(+), 19 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index c7da67d6140b..b00372855d9c 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -93,7 +93,8 @@ def apply_generic_arguments( bound or constraints, instead of giving an error. """ tvars = callable.variables - assert len(tvars) == len(orig_types) + min_arg_count = sum(not tv.has_default() for tv in tvars) + assert min_arg_count <= len(orig_types) <= len(tvars) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. # Create a map from type variable id to target type. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a4b66bb9932c..e04d413eab8d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4809,21 +4809,29 @@ def apply_type_arguments_to_callable( tp = get_proper_type(tp) if isinstance(tp, CallableType): - if len(tp.variables) != len(args) and not any( - isinstance(v, TypeVarTupleType) for v in tp.variables - ): + min_arg_count = sum(not v.has_default() for v in tp.variables) + has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in tp.variables) + if ( + len(args) < min_arg_count or len(args) > len(tp.variables) + ) and not has_type_var_tuple: if tp.is_type_obj() and tp.type_object().fullname == "builtins.tuple": # TODO: Specialize the callable for the type arguments return tp - self.msg.incompatible_type_application(len(tp.variables), len(args), ctx) + self.msg.incompatible_type_application( + min_arg_count, len(tp.variables), len(args), ctx + ) return AnyType(TypeOfAny.from_error) return self.apply_generic_arguments(tp, self.split_for_callable(tp, args, ctx), ctx) if isinstance(tp, Overloaded): for it in tp.items: - if len(it.variables) != len(args) and not any( - isinstance(v, TypeVarTupleType) for v in it.variables - ): - self.msg.incompatible_type_application(len(it.variables), len(args), ctx) + min_arg_count = sum(not v.has_default() for v in it.variables) + has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in it.variables) + if ( + len(args) < min_arg_count or len(args) > len(it.variables) + ) and not has_type_var_tuple: + self.msg.incompatible_type_application( + min_arg_count, len(it.variables), len(args), ctx + ) return AnyType(TypeOfAny.from_error) return Overloaded( [ diff --git a/mypy/messages.py b/mypy/messages.py index 75b8eeb3174c..2f2d346c2c51 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1347,18 +1347,21 @@ def override_target(self, name: str, name_in_super: str, supertype: str) -> str: return target def incompatible_type_application( - self, expected_arg_count: int, actual_arg_count: int, context: Context + self, min_arg_count: int, max_arg_count: int, actual_arg_count: int, context: Context ) -> None: - if expected_arg_count == 0: + if max_arg_count == 0: self.fail("Type application targets a non-generic function or class", context) - elif actual_arg_count > expected_arg_count: - self.fail( - f"Type application has too many types ({expected_arg_count} expected)", context - ) + return + + if min_arg_count == max_arg_count: + s = f"{max_arg_count} expected" else: - self.fail( - f"Type application has too few types ({expected_arg_count} expected)", context - ) + s = f"expected between {min_arg_count} and {max_arg_count}" + + if actual_arg_count > max_arg_count: + self.fail(f"Type application has too many types ({s})", context) + else: + self.fail(f"Type application has too few types ({s})", context) def could_not_infer_type_arguments( self, callee_type: CallableType, n: int, context: Context diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 4b509cd4fc40..7c748821401a 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -118,7 +118,7 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsClass1] -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Union, overload T1 = TypeVar("T1") T2 = TypeVar("T2", default=int) @@ -137,6 +137,15 @@ def func_a1( reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]" reveal_type(d) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + k = ClassA1() + reveal_type(k) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + l = ClassA1[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]" + m = ClassA1[float, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]" + n = ClassA1[float, float, float]() # E: Type application has too many types (expected between 0 and 2) + reveal_type(n) # N: Revealed type is "Any" + class ClassA2(Generic[T1, T2, T3]): ... def func_a2( @@ -152,6 +161,44 @@ def func_a2( reveal_type(d) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]" reveal_type(e) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]" + k = ClassA2() # E: Need type annotation for "k" + reveal_type(k) # N: Revealed type is "__main__.ClassA2[Any, builtins.int, builtins.str]" + l = ClassA2[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.int, builtins.str]" + m = ClassA2[float, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.str]" + n = ClassA2[float, float, float]() + reveal_type(n) # N: Revealed type is "__main__.ClassA2[builtins.float, builtins.float, builtins.float]" + o = ClassA2[float, float, float, float]() # E: Type application has too many types (expected between 1 and 3) + reveal_type(o) # N: Revealed type is "Any" + +class ClassA3(Generic[T1, T2]): + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, var: int) -> None: ... + def __init__(self, var: Union[int, None] = None) -> None: ... + +def func_a3( + a: ClassA3, + b: ClassA3[float], + c: ClassA3[float, float], + d: ClassA3[float, float, float], # E: "ClassA3" expects between 1 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]" + reveal_type(b) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.int]" + reveal_type(c) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]" + + k = ClassA3() # E: Need type annotation for "k" + reveal_type(k) # N: Revealed type is "__main__.ClassA3[Any, builtins.int]" + l = ClassA3[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.int]" + m = ClassA3[float, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassA3[builtins.float, builtins.float]" + n = ClassA3[float, float, float]() # E: Type application has too many types (expected between 1 and 2) + reveal_type(n) # N: Revealed type is "Any" + [case testTypeVarDefaultsClass2] from typing import Generic, ParamSpec @@ -172,6 +219,15 @@ def func_b1( reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + k = ClassB1() + reveal_type(k) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + l = ClassB1[[float]]() + reveal_type(l) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + m = ClassB1[[float], [float]]() + reveal_type(m) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + n = ClassB1[[float], [float], [float]]() # E: Type application has too many types (expected between 0 and 2) + reveal_type(n) # N: Revealed type is "Any" + class ClassB2(Generic[P1, P2]): ... def func_b2( @@ -185,6 +241,15 @@ def func_b2( reveal_type(c) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]" reveal_type(d) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" + k = ClassB2() # E: Need type annotation for "k" + reveal_type(k) # N: Revealed type is "__main__.ClassB2[Any, [builtins.int, builtins.str]]" + l = ClassB2[[float]]() + reveal_type(l) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.int, builtins.str]]" + m = ClassB2[[float], [float]]() + reveal_type(m) # N: Revealed type is "__main__.ClassB2[[builtins.float], [builtins.float]]" + n = ClassB2[[float], [float], [float]]() # E: Type application has too many types (expected between 1 and 2) + reveal_type(n) # N: Revealed type is "Any" + [case testTypeVarDefaultsClass3] from typing import Generic, Tuple, TypeVar from typing_extensions import TypeVarTuple, Unpack @@ -206,6 +271,11 @@ def func_c1( # reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]" + # k = ClassC1() # TODO + # reveal_type(k) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO + l = ClassC1[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassC1[builtins.float]" + class ClassC2(Generic[T3, Unpack[Ts3]]): ... def func_c2( @@ -217,6 +287,13 @@ def func_c2( # reveal_type(b) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO reveal_type(c) # N: Revealed type is "__main__.ClassC2[builtins.int]" + # k = ClassC2() # TODO + # reveal_type(k) # Revealed type is "__main__.ClassC2[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + l = ClassC2[int]() + # reveal_type(l) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + m = ClassC2[int, Unpack[Tuple[()]]]() + reveal_type(m) # N: Revealed type is "__main__.ClassC2[builtins.int]" + class ClassC3(Generic[T3, Unpack[Ts4]]): ... def func_c3( @@ -228,6 +305,13 @@ def func_c3( reveal_type(b) # N: Revealed type is "__main__.ClassC3[builtins.int]" reveal_type(c) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]" + # k = ClassC3() # TODO + # reveal_type(k) # Revealed type is "__main__.ClassC3[builtins.str]" # TODO + l = ClassC3[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassC3[builtins.int]" + m = ClassC3[int, Unpack[Tuple[float]]]() + reveal_type(m) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]" + class ClassC4(Generic[T1, Unpack[Ts1], T3]): ... def func_c4( @@ -238,6 +322,13 @@ def func_c4( reveal_type(a) # N: Revealed type is "__main__.ClassC4[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" # reveal_type(b) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO reveal_type(c) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" + + k = ClassC4() # E: Need type annotation for "k" + reveal_type(k) # N: Revealed type is "__main__.ClassC4[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" + l = ClassC4[int]() + # reveal_type(l) # Revealed type is "__main__.ClassC4[builtins.int, builtins.str]" # TODO + m = ClassC4[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsTypeAlias1] From 5bf774234da052efffd14e6c3dd26d93a14e3cc8 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:41:49 +0100 Subject: [PATCH 0471/1617] Fix missing type store for overloads (#16803) Add missing call to store inferred types if an overload match is found early. All other code paths already do that. ### Some background on the issue this fixes I recently saw an interesting pattern in `aiohttp` to type values in an `dict[str, Any]` by subclassing dict. ```py T = TypeVar("T") U = TypeVar("U") class Key(Generic[T]): ... class CustomDict(dict[Key[Any] | str, Any]): @overload # type: ignore[override] def get(self, __key: Key[T]) -> T | None: ... @overload def get(self, __key: Key[T], __default: U) -> T | U: ... @overload def get(self, __key: str) -> Any | None: ... @overload def get(self, __key: str, __default: Any) -> Any: ... def get(self, __key: Key[Any] | str, __default: Any = None) -> Any: """Forward to super implementation.""" return super().get(__key, __default) # overloads for __getitem__, setdefault, pop # ... @overload # type: ignore[override] def __setitem__(self, key: Key[T], value: T) -> None: ... @overload def __setitem__(self, key: str, value: Any) -> None: ... def __setitem__(self, key: Key[Any] | str, value: Any) -> None: """Forward to super implementation.""" return super().__setitem__(key, value) ``` With the exception that these overloads aren't technically compatible with the supertype, they do the job. ```py d = CustomDict() key = Key[int]() other_key = "other" assert_type(d.get(key), int | None) assert_type(d.get("other"), Any | None) ``` The issue exists for the `__setitem__` case. Without this PR the following would create an issue. Here `var` would be inferred as `dict[Never, Never]`, even though it should be `dict[Any, Any]` which is the case for non-subclassed dicts. ```py def a2(d: CustomDict) -> None: if (var := d.get("arg")) is None: var = d["arg"] = {} reveal_type(var) ``` --- mypy/checkexpr.py | 1 + test-data/unit/check-generics.test | 24 ++++++++++++++++++++++++ test-data/unit/typexport-basic.test | 21 +++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e04d413eab8d..c628a3398b59 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2825,6 +2825,7 @@ def infer_overload_return_type( # Return early if possible; otherwise record info, so we can # check for ambiguity due to 'Any' below. if not args_contain_any: + self.chk.store_types(m) return ret_type, infer_type p_infer_type = get_proper_type(infer_type) if isinstance(p_infer_type, CallableType): diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 337e92daf365..469cedb8f6f7 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1480,6 +1480,30 @@ if int(): b = f(b) [builtins fixtures/list.pyi] +[case testGenericDictWithOverload] +from typing import Dict, Generic, TypeVar, Any, overload +T = TypeVar("T") + +class Key(Generic[T]): ... +class CustomDict(dict): + @overload # type: ignore[override] + def __setitem__(self, key: Key[T], value: T) -> None: ... + @overload + def __setitem__(self, key: str, value: Any) -> None: ... + def __setitem__(self, key, value): + return super().__setitem__(key, value) + +def a1(d: Dict[str, Any]) -> None: + if (var := d.get("arg")) is None: + var = d["arg"] = {} + reveal_type(var) # N: Revealed type is "builtins.dict[Any, Any]" + +def a2(d: CustomDict) -> None: + if (var := d.get("arg")) is None: + var = d["arg"] = {} + reveal_type(var) # N: Revealed type is "builtins.dict[Any, Any]" +[builtins fixtures/dict.pyi] + -- Type variable scoping -- --------------------- diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index c4c3a1d36f83..d78cf0f179f2 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -1236,6 +1236,27 @@ LambdaExpr(10) : def (x: builtins.int) -> builtins.int LambdaExpr(12) : def (y: builtins.str) -> builtins.str LambdaExpr(13) : def (x: builtins.str) -> builtins.str +[case testExportOverloadArgTypeDict] +## DictExpr +from typing import TypeVar, Generic, Any, overload, Dict +T = TypeVar("T") +class Key(Generic[T]): ... +@overload +def f(x: Key[T], y: T) -> T: ... +@overload +def f(x: int, y: Any) -> Any: ... +def f(x, y): ... +d: Dict = {} +d.get( + "", {}) +f( + 2, {}) +[builtins fixtures/dict.pyi] +[out] +DictExpr(10) : builtins.dict[Any, Any] +DictExpr(12) : builtins.dict[Any, Any] +DictExpr(14) : builtins.dict[Any, Any] + -- TODO -- -- test expressions From c3cd83a23dcdbd72e6e2a4c2d6c605eadc7bccf4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:21:57 +0100 Subject: [PATCH 0472/1617] Fix disallow-any errors for Instance types (PEP 696) (#16832) Similar to TypeAlias types `Missing type parameters for generic type` should not be emitted if too many arguments are given. There is a separate error message for that. Ref: https://github.com/python/mypy/issues/14851 --- mypy/messages.py | 8 ++++---- mypy/typeanal.py | 5 ++++- test-data/unit/check-typevar-defaults.test | 11 +++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 2f2d346c2c51..c107e874f4fc 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2515,10 +2515,10 @@ def format_literal_value(typ: LiteralType) -> str: else: base_str = itype.type.name if not itype.args: - if not itype.type.has_type_var_tuple_type: - # No type arguments, just return the type name - return base_str - return base_str + "[()]" + if itype.type.has_type_var_tuple_type and len(itype.type.type_vars) == 1: + return base_str + "[()]" + # No type arguments, just return the type name + 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}, ...]" diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 1bcba5f0ca88..601f98e958e2 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1866,7 +1866,11 @@ def fix_instance( max_tv_count = len(t.type.type_vars) if arg_count < min_tv_count or arg_count > max_tv_count: # Don't use existing args if arg_count doesn't match + if arg_count > max_tv_count: + # Already wrong arg count error, don't emit missing type parameters error as well. + disallow_any = False t.args = () + arg_count = 0 args: list[Type] = [*(t.args[:max_tv_count])] any_type: AnyType | None = None @@ -2324,7 +2328,6 @@ def validate_instance(t: Instance, fail: MsgCallback, empty_tuple_index: bool) - t, code=codes.TYPE_ARG, ) - t.args = () t.invalid = True return False return True diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 7c748821401a..6136746cbd0a 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -118,6 +118,7 @@ def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsClass1] +# flags: --disallow-any-generics from typing import Generic, TypeVar, Union, overload T1 = TypeVar("T1") @@ -149,7 +150,7 @@ def func_a1( class ClassA2(Generic[T1, T2, T3]): ... def func_a2( - a: ClassA2, + a: ClassA2, # E: Missing type parameters for generic type "ClassA2" b: ClassA2[float], c: ClassA2[float, float], d: ClassA2[float, float, float], @@ -180,7 +181,7 @@ class ClassA3(Generic[T1, T2]): def __init__(self, var: Union[int, None] = None) -> None: ... def func_a3( - a: ClassA3, + a: ClassA3, # E: Missing type parameters for generic type "ClassA3" b: ClassA3[float], c: ClassA3[float, float], d: ClassA3[float, float, float], # E: "ClassA3" expects between 1 and 2 type arguments, but 3 given @@ -200,6 +201,7 @@ def func_a3( reveal_type(n) # N: Revealed type is "Any" [case testTypeVarDefaultsClass2] +# flags: --disallow-any-generics from typing import Generic, ParamSpec P1 = ParamSpec("P1") @@ -231,7 +233,7 @@ def func_b1( class ClassB2(Generic[P1, P2]): ... def func_b2( - a: ClassB2, + a: ClassB2, # E: Missing type parameters for generic type "ClassB2" b: ClassB2[[float]], c: ClassB2[[float], [float]], d: ClassB2[[float], [float], [float]], # E: "ClassB2" expects between 1 and 2 type arguments, but 3 given @@ -251,6 +253,7 @@ def func_b2( reveal_type(n) # N: Revealed type is "Any" [case testTypeVarDefaultsClass3] +# flags: --disallow-any-generics from typing import Generic, Tuple, TypeVar from typing_extensions import TypeVarTuple, Unpack @@ -315,7 +318,7 @@ def func_c3( class ClassC4(Generic[T1, Unpack[Ts1], T3]): ... def func_c4( - a: ClassC4, + a: ClassC4, # E: Missing type parameters for generic type "ClassC4" b: ClassC4[int], c: ClassC4[int, float], ) -> None: From 8107e53158d83d30bb04d290ac10d8d3ccd344f8 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:46:07 +0100 Subject: [PATCH 0473/1617] Update black to 24.1.1 (#16847) --- .pre-commit-config.yaml | 2 +- mypy/build.py | 1 + mypy/checker.py | 42 +++++++++++++++--------------- mypy/checkexpr.py | 16 ++++++------ mypy/checkpattern.py | 9 +++---- mypy/evalexpr.py | 1 + mypy/expandtype.py | 24 ++++++----------- mypy/fastparse.py | 21 +++++++-------- mypy/join.py | 6 ++--- mypy/main.py | 2 -- mypy/metastore.py | 3 +-- mypy/nodes.py | 12 ++++----- mypy/plugins/attrs.py | 16 +++++++----- mypy/plugins/enums.py | 1 + mypy/plugins/functools.py | 1 + mypy/semanal_shared.py | 9 +++---- mypy/semanal_typeddict.py | 9 +++---- mypy/stubtest.py | 8 +++--- mypy/stubutil.py | 6 ++--- mypy/subtypes.py | 8 +++--- mypy/test/meta/test_parse_data.py | 1 + mypy/test/meta/test_update_data.py | 1 + mypy/test/testargs.py | 1 + mypy/test/testerrorstream.py | 1 + mypy/test/testreports.py | 1 + mypy/test/teststubgen.py | 3 +-- mypy/typeanal.py | 3 +-- mypy/typeops.py | 8 +++--- mypy/types.py | 23 +++++++--------- mypyc/analysis/ircheck.py | 1 + mypyc/codegen/emitfunc.py | 4 +-- mypyc/ir/class_ir.py | 6 ++--- mypyc/irbuild/builder.py | 7 +++-- mypyc/irbuild/nonlocalcontrol.py | 3 +-- test-requirements.in | 2 +- test-requirements.txt | 2 +- 36 files changed, 126 insertions(+), 138 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bbd7b3ce382..6566bb0297d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.9.1 # must match test-requirements.txt + rev: 24.1.1 # must match test-requirements.txt hooks: - id: black exclude: '^(test-data/)' diff --git a/mypy/build.py b/mypy/build.py index 8049fa2d0c3f..65a06211c87e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -8,6 +8,7 @@ The function build() is the main interface to this module. """ + # TODO: More consistent terminology, e.g. path/fnam, module/id, state/file from __future__ import annotations diff --git a/mypy/checker.py b/mypy/checker.py index cd23e74a8dac..391f28e93b1d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -526,12 +526,16 @@ def check_second_pass( # print("XXX in pass %d, class %s, function %s" % # (self.pass_num, type_name, node.fullname or node.name)) done.add(node) - with self.tscope.class_scope( - active_typeinfo - ) if active_typeinfo else nullcontext(): - with self.scope.push_class( - active_typeinfo - ) if active_typeinfo else nullcontext(): + with ( + self.tscope.class_scope(active_typeinfo) + if active_typeinfo + else nullcontext() + ): + with ( + self.scope.push_class(active_typeinfo) + if active_typeinfo + else nullcontext() + ): self.check_partial(node) return True @@ -3802,9 +3806,11 @@ def check_multi_assignment_from_tuple( if star_lv: list_expr = ListExpr( [ - self.temp_node(rv_type, context) - if not isinstance(rv_type, UnpackType) - else StarExpr(self.temp_node(rv_type.type, context)) + ( + self.temp_node(rv_type, context) + if not isinstance(rv_type, UnpackType) + else StarExpr(self.temp_node(rv_type.type, context)) + ) for rv_type in star_rv_types ] ) @@ -6593,8 +6599,7 @@ def check_subtype( notes: list[str] | None = None, code: ErrorCode | None = None, outer_context: Context | None = None, - ) -> bool: - ... + ) -> bool: ... @overload def check_subtype( @@ -6608,8 +6613,7 @@ def check_subtype( *, notes: list[str] | None = None, outer_context: Context | None = None, - ) -> bool: - ... + ) -> bool: ... def check_subtype( self, @@ -7083,14 +7087,12 @@ def conditional_types_with_intersection( type_ranges: list[TypeRange] | None, ctx: Context, default: None = None, - ) -> tuple[Type | None, Type | None]: - ... + ) -> tuple[Type | None, Type | None]: ... @overload def conditional_types_with_intersection( self, expr_type: Type, type_ranges: list[TypeRange] | None, ctx: Context, default: Type - ) -> tuple[Type, Type]: - ... + ) -> tuple[Type, Type]: ... def conditional_types_with_intersection( self, @@ -7348,15 +7350,13 @@ def visit_type_var(self, t: TypeVarType) -> None: @overload def conditional_types( current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: None = None -) -> tuple[Type | None, Type | None]: - ... +) -> tuple[Type | None, Type | None]: ... @overload def conditional_types( current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: Type -) -> tuple[Type, Type]: - ... +) -> tuple[Type, Type]: ... def conditional_types( diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c628a3398b59..ff7b7fa2ff58 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2132,11 +2132,13 @@ def infer_function_type_arguments( unknown = UninhabitedType() unknown.ambiguous = True inferred_args = [ - expand_type( - a, {v.id: unknown for v in list(callee_type.variables) + free_vars} + ( + expand_type( + a, {v.id: unknown for v in list(callee_type.variables) + free_vars} + ) + if a is not None + else None ) - if a is not None - else None for a in poly_inferred_args ] else: @@ -6042,14 +6044,12 @@ def bool_type(self) -> Instance: return self.named_type("builtins.bool") @overload - def narrow_type_from_binder(self, expr: Expression, known_type: Type) -> Type: - ... + def narrow_type_from_binder(self, expr: Expression, known_type: Type) -> Type: ... @overload def narrow_type_from_binder( self, expr: Expression, known_type: Type, skip_non_overlapping: bool - ) -> Type | None: - ... + ) -> Type | None: ... def narrow_type_from_binder( self, expr: Expression, known_type: Type, skip_non_overlapping: bool = False diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 3210dcc3b7ac..7b6a55324741 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -305,11 +305,10 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: narrowed_inner_types = [] inner_rest_types = [] for inner_type, new_inner_type in zip(inner_types, new_inner_types): - ( - narrowed_inner_type, - inner_rest_type, - ) = self.chk.conditional_types_with_intersection( - new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type + (narrowed_inner_type, inner_rest_type) = ( + self.chk.conditional_types_with_intersection( + new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type + ) ) narrowed_inner_types.append(narrowed_inner_type) inner_rest_types.append(inner_rest_type) diff --git a/mypy/evalexpr.py b/mypy/evalexpr.py index 4b3abb1be3e2..e39c5840d47a 100644 --- a/mypy/evalexpr.py +++ b/mypy/evalexpr.py @@ -6,6 +6,7 @@ put it in a mypyc-compiled file. """ + import ast from typing import Final diff --git a/mypy/expandtype.py b/mypy/expandtype.py index f6aa74add9d8..b4bc1aa9b9a5 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -50,18 +50,15 @@ @overload -def expand_type(typ: CallableType, env: Mapping[TypeVarId, Type]) -> CallableType: - ... +def expand_type(typ: CallableType, env: Mapping[TypeVarId, Type]) -> CallableType: ... @overload -def expand_type(typ: ProperType, env: Mapping[TypeVarId, Type]) -> ProperType: - ... +def expand_type(typ: ProperType, env: Mapping[TypeVarId, Type]) -> ProperType: ... @overload -def expand_type(typ: Type, env: Mapping[TypeVarId, Type]) -> Type: - ... +def expand_type(typ: Type, env: Mapping[TypeVarId, Type]) -> Type: ... def expand_type(typ: Type, env: Mapping[TypeVarId, Type]) -> Type: @@ -72,18 +69,15 @@ def expand_type(typ: Type, env: Mapping[TypeVarId, Type]) -> Type: @overload -def expand_type_by_instance(typ: CallableType, instance: Instance) -> CallableType: - ... +def expand_type_by_instance(typ: CallableType, instance: Instance) -> CallableType: ... @overload -def expand_type_by_instance(typ: ProperType, instance: Instance) -> ProperType: - ... +def expand_type_by_instance(typ: ProperType, instance: Instance) -> ProperType: ... @overload -def expand_type_by_instance(typ: Type, instance: Instance) -> Type: - ... +def expand_type_by_instance(typ: Type, instance: Instance) -> Type: ... def expand_type_by_instance(typ: Type, instance: Instance) -> Type: @@ -470,13 +464,11 @@ def expand_types(self, types: Iterable[Type]) -> list[Type]: @overload -def expand_self_type(var: Var, typ: ProperType, replacement: ProperType) -> ProperType: - ... +def expand_self_type(var: Var, typ: ProperType, replacement: ProperType) -> ProperType: ... @overload -def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: - ... +def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: ... def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index b3e0fc70a9d6..a155187992ec 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -608,10 +608,9 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: # Check IfStmt block to determine if function overloads can be merged if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name) if if_overload_name is not None: - ( - if_block_with_overload, - if_unknown_truth_value, - ) = self._get_executable_if_block_with_overloads(stmt) + (if_block_with_overload, if_unknown_truth_value) = ( + self._get_executable_if_block_with_overloads(stmt) + ) if ( current_overload_name is not None @@ -911,9 +910,11 @@ def do_func_def( # PEP 484 disallows both type annotations and type comments self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset) arg_types = [ - a.type_annotation - if a.type_annotation is not None - else AnyType(TypeOfAny.unannotated) + ( + a.type_annotation + if a.type_annotation is not None + else AnyType(TypeOfAny.unannotated) + ) for a in args ] else: @@ -1790,12 +1791,10 @@ def invalid_type(self, node: AST, note: str | None = None) -> RawExpressionType: ) @overload - def visit(self, node: ast3.expr) -> ProperType: - ... + def visit(self, node: ast3.expr) -> ProperType: ... @overload - def visit(self, node: AST | None) -> ProperType | None: - ... + def visit(self, node: AST | None) -> ProperType | None: ... def visit(self, node: AST | None) -> ProperType | None: """Modified visit -- keep track of the stack of nodes""" diff --git a/mypy/join.py b/mypy/join.py index d33cbd98726d..bf88f43d88fe 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -237,13 +237,11 @@ def trivial_join(s: Type, t: Type) -> Type: @overload def join_types( s: ProperType, t: ProperType, instance_joiner: InstanceJoiner | None = None -) -> ProperType: - ... +) -> ProperType: ... @overload -def join_types(s: Type, t: Type, instance_joiner: InstanceJoiner | None = None) -> Type: - ... +def join_types(s: Type, t: Type, instance_joiner: InstanceJoiner | None = None) -> Type: ... def join_types(s: Type, t: Type, instance_joiner: InstanceJoiner | None = None) -> Type: diff --git a/mypy/main.py b/mypy/main.py index b32624456ce0..c2df79d51e83 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -342,7 +342,6 @@ def infer_python_executable(options: Options, special_opts: argparse.Namespace) class CapturableArgumentParser(argparse.ArgumentParser): - """Override ArgumentParser methods that use sys.stdout/sys.stderr directly. This is needed because hijacking sys.std* is not thread-safe, @@ -396,7 +395,6 @@ def error(self, message: str) -> NoReturn: class CapturableVersionAction(argparse.Action): - """Supplement CapturableArgumentParser to handle --version. This is nearly identical to argparse._VersionAction except, diff --git a/mypy/metastore.py b/mypy/metastore.py index 0547f94cd671..4caa7d7f0534 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -63,8 +63,7 @@ def commit(self) -> None: """ @abstractmethod - def list_all(self) -> Iterable[str]: - ... + def list_all(self) -> Iterable[str]: ... def random_string() -> str: diff --git a/mypy/nodes.py b/mypy/nodes.py index 6c23c51dfcd3..1c781320580a 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3287,13 +3287,13 @@ def serialize(self) -> JsonDict: "declared_metaclass": ( None if self.declared_metaclass is None else self.declared_metaclass.serialize() ), - "metaclass_type": None - if self.metaclass_type is None - else self.metaclass_type.serialize(), + "metaclass_type": ( + None if self.metaclass_type is None else self.metaclass_type.serialize() + ), "tuple_type": None if self.tuple_type is None else self.tuple_type.serialize(), - "typeddict_type": None - if self.typeddict_type is None - else self.typeddict_type.serialize(), + "typeddict_type": ( + None if self.typeddict_type is None else self.typeddict_type.serialize() + ), "flags": get_flags(self, TypeInfo.FLAGS), "metadata": self.metadata, "slots": sorted(self.slots) if self.slots is not None else None, diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 19a402492aef..345ea822ed94 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -185,9 +185,11 @@ def serialize(self) -> JsonDict: "init": self.init, "kw_only": self.kw_only, "has_converter": self.converter is not None, - "converter_init_type": self.converter.init_type.serialize() - if self.converter and self.converter.init_type - else None, + "converter_init_type": ( + self.converter.init_type.serialize() + if self.converter and self.converter.init_type + else None + ), "context_line": self.context.line, "context_column": self.context.column, "init_type": self.init_type.serialize() if self.init_type else None, @@ -1073,9 +1075,11 @@ def _meet_fields(types: list[Mapping[str, Type]]) -> Mapping[str, Type]: field_to_types[name].append(typ) return { - name: get_proper_type(reduce(meet_types, f_types)) - if len(f_types) == len(types) - else UninhabitedType() + name: ( + get_proper_type(reduce(meet_types, f_types)) + if len(f_types) == len(types) + else UninhabitedType() + ) for name, f_types in field_to_types.items() } diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 2c568f66c62d..83350fe2fe11 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -10,6 +10,7 @@ we actually bake some of it directly in to the semantic analysis layer (see semanal_enum.py). """ + from __future__ import annotations from typing import Final, Iterable, Sequence, TypeVar, cast diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 0aa2824c9b51..792ed6669503 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -1,4 +1,5 @@ """Plugin for supporting the functools standard library module.""" + from __future__ import annotations from typing import Final, NamedTuple diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index e8edfe65c8d4..b5ec2bb52a0d 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -308,8 +308,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None: class _NamedTypeCallback(Protocol): - def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> Instance: - ... + def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> Instance: ... def paramspec_args( @@ -453,8 +452,7 @@ def require_bool_literal_argument( expression: Expression, name: str, default: Literal[True] | Literal[False], -) -> bool: - ... +) -> bool: ... @overload @@ -463,8 +461,7 @@ def require_bool_literal_argument( expression: Expression, name: str, default: None = None, -) -> bool | None: - ... +) -> bool | None: ... def require_bool_literal_argument( diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index dbec981bdc96..eee98d4d20fa 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -156,12 +156,9 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N # Iterate over bases in reverse order so that leftmost base class' keys take precedence for base in reversed(typeddict_bases): self.add_keys_and_types_from_base(base, keys, types, required_keys, defn) - ( - new_keys, - new_types, - new_statements, - new_required_keys, - ) = self.analyze_typeddict_classdef_fields(defn, keys) + (new_keys, new_types, new_statements, new_required_keys) = ( + self.analyze_typeddict_classdef_fields(defn, keys) + ) if new_keys is None: return True, None # Defer keys.extend(new_keys) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 7ab3a2b1e5d0..225c1c35c1be 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1519,9 +1519,11 @@ def safe_inspect_signature(runtime: Any) -> inspect.Signature | None: sig = inspect._signature_fromstr(inspect.Signature, runtime, sig) # type: ignore[attr-defined] assert isinstance(sig, inspect.Signature) new_params = [ - parameter.replace(default=UNREPRESENTABLE) - if parameter.default is ... - else parameter + ( + parameter.replace(default=UNREPRESENTABLE) + if parameter.default is ... + else parameter + ) for parameter in sig.parameters.values() ] return sig.replace(parameters=new_params) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index b7f6131c003d..1a9c2357c58e 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -140,13 +140,11 @@ def fail_missing(mod: str, reason: ModuleNotFoundReason) -> None: @overload -def remove_misplaced_type_comments(source: bytes) -> bytes: - ... +def remove_misplaced_type_comments(source: bytes) -> bytes: ... @overload -def remove_misplaced_type_comments(source: str) -> str: - ... +def remove_misplaced_type_comments(source: str) -> str: ... def remove_misplaced_type_comments(source: str | bytes) -> str | bytes: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 4fd3f8ff98ca..2d536f892a2a 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -693,9 +693,11 @@ def visit_callable_type(self, left: CallableType) -> bool: is_compat=self._is_subtype, is_proper_subtype=self.proper_subtype, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, - strict_concatenate=(self.options.extra_checks or self.options.strict_concatenate) - if self.options - else False, + strict_concatenate=( + (self.options.extra_checks or self.options.strict_concatenate) + if self.options + else False + ), ) elif isinstance(right, Overloaded): return all(self._is_subtype(left, item) for item in right.items) diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index 797fdd7b2c8c..bff2d6977612 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -2,6 +2,7 @@ A "meta test" which tests the parsing of .test files. This is not meant to become exhaustive but to ensure we maintain a basic level of ergonomics for mypy contributors. """ + from mypy.test.helpers import Suite from mypy.test.meta._pytest import PytestResult, run_pytest_data_suite diff --git a/mypy/test/meta/test_update_data.py b/mypy/test/meta/test_update_data.py index 40b70157a0e3..820fd359893e 100644 --- a/mypy/test/meta/test_update_data.py +++ b/mypy/test/meta/test_update_data.py @@ -3,6 +3,7 @@ Updating the expected output, especially when it's in the form of inline (comment) assertions, can be brittle, which is why we're "meta-testing" here. """ + from mypy.test.helpers import Suite from mypy.test.meta._pytest import PytestResult, dedent_docstring, run_pytest_data_suite diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index b0cc6b19aa80..7c139902fe90 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -4,6 +4,7 @@ defaults, and that argparse doesn't assign any new members to the Options object it creates. """ + from __future__ import annotations import argparse diff --git a/mypy/test/testerrorstream.py b/mypy/test/testerrorstream.py index 5ed112fd31e7..a54a3495ddb2 100644 --- a/mypy/test/testerrorstream.py +++ b/mypy/test/testerrorstream.py @@ -1,4 +1,5 @@ """Tests for mypy incremental error output.""" + from __future__ import annotations from mypy import build diff --git a/mypy/test/testreports.py b/mypy/test/testreports.py index 5ff315f83ba8..f638756ad819 100644 --- a/mypy/test/testreports.py +++ b/mypy/test/testreports.py @@ -1,4 +1,5 @@ """Test cases for reports generated by mypy.""" + from __future__ import annotations import textwrap diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index ace0b4d95573..c138f9953918 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1099,8 +1099,7 @@ def test(arg0: str) -> None: assert_equal(gen.get_imports().splitlines(), ["import foo", "import other"]) def test_generate_c_function_no_crash_for_non_str_docstring(self) -> None: - def test(arg0: str) -> None: - ... + def test(arg0: str) -> None: ... test.__doc__ = property(lambda self: "test(arg0: str) -> None") # type: ignore[assignment] diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 601f98e958e2..2b37d10e2aff 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1775,8 +1775,7 @@ def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: class MsgCallback(Protocol): - def __call__(self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None) -> None: - ... + def __call__(self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None) -> None: ... def get_omitted_any( diff --git a/mypy/typeops.py b/mypy/typeops.py index 2bf8ffbf47ab..5b396308d955 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -915,9 +915,11 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType] if typ.fallback.type.is_enum or isinstance(typ.value, bool): if fullname not in sum_types: sum_types[fullname] = ( - set(typ.fallback.get_enum_values()) - if typ.fallback.type.is_enum - else {True, False}, + ( + set(typ.fallback.get_enum_values()) + if typ.fallback.type.is_enum + else {True, False} + ), [], ) literals, indexes = sum_types[fullname] diff --git a/mypy/types.py b/mypy/types.py index f02e56a677ae..0e87ec701386 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1490,9 +1490,9 @@ def copy_modified( args if args is not _dummy else self.args, self.line, self.column, - last_known_value=last_known_value - if last_known_value is not _dummy - else self.last_known_value, + last_known_value=( + last_known_value if last_known_value is not _dummy else self.last_known_value + ), ) # We intentionally don't copy the extra_attrs here, so they will be erased. new.can_be_true = self.can_be_true @@ -2834,13 +2834,13 @@ def __eq__(self, other: object) -> bool: @overload @staticmethod - def make_union(items: Sequence[ProperType], line: int = -1, column: int = -1) -> ProperType: - ... + def make_union( + items: Sequence[ProperType], line: int = -1, column: int = -1 + ) -> ProperType: ... @overload @staticmethod - def make_union(items: Sequence[Type], line: int = -1, column: int = -1) -> Type: - ... + def make_union(items: Sequence[Type], line: int = -1, column: int = -1) -> Type: ... @staticmethod def make_union(items: Sequence[Type], line: int = -1, column: int = -1) -> Type: @@ -3052,13 +3052,11 @@ def serialize(self) -> str: @overload -def get_proper_type(typ: None) -> None: - ... +def get_proper_type(typ: None) -> None: ... @overload -def get_proper_type(typ: Type) -> ProperType: - ... +def get_proper_type(typ: Type) -> ProperType: ... def get_proper_type(typ: Type | None) -> ProperType | None: @@ -3088,8 +3086,7 @@ def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: @overload def get_proper_types( types: list[Type | None] | tuple[Type | None, ...] -) -> list[ProperType | None]: - ... +) -> list[ProperType | None]: ... def get_proper_types( diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index a31b1517b036..127047e02ff5 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -1,4 +1,5 @@ """Utilities for checking that internal ir is valid and consistent.""" + from __future__ import annotations from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncIR diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 3bce84d3ea59..c08f1f840fa4 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -535,9 +535,7 @@ def visit_method_call(self, op: MethodCall) -> None: obj_args = ( [] if method.decl.kind == FUNC_STATICMETHOD - else [f"(PyObject *)Py_TYPE({obj})"] - if method.decl.kind == FUNC_CLASSMETHOD - else [obj] + else [f"(PyObject *)Py_TYPE({obj})"] if method.decl.kind == FUNC_CLASSMETHOD else [obj] ) args = ", ".join(obj_args + [self.reg(arg) for arg in op.args]) mtype = native_function_type(method, self.emitter) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 61f0fc36e1b3..18f3cbcff987 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -383,9 +383,9 @@ def serialize(self) -> JsonDict: "traits": [cir.fullname for cir in self.traits], "mro": [cir.fullname for cir in self.mro], "base_mro": [cir.fullname for cir in self.base_mro], - "children": [cir.fullname for cir in self.children] - if self.children is not None - else None, + "children": ( + [cir.fullname for cir in self.children] if self.children is not None else None + ), "deletable": self.deletable, "attrs_with_defaults": sorted(self.attrs_with_defaults), "_always_initialized_attrs": sorted(self._always_initialized_attrs), diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 5ed617aa925f..9d160b08505d 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -10,6 +10,7 @@ example, expressions are transformed in mypyc.irbuild.expression and functions are transformed in mypyc.irbuild.function. """ + from __future__ import annotations from contextlib import contextmanager @@ -231,12 +232,10 @@ def set_module(self, module_name: str, module_path: str) -> None: self.builder.set_module(module_name, module_path) @overload - def accept(self, node: Expression, *, can_borrow: bool = False) -> Value: - ... + def accept(self, node: Expression, *, can_borrow: bool = False) -> Value: ... @overload - def accept(self, node: Statement) -> None: - ... + def accept(self, node: Statement) -> None: ... def accept(self, node: Statement | Expression, *, can_borrow: bool = False) -> Value | None: """Transform an expression or a statement. diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py index 02dd51283e95..0ac9bd3cee31 100644 --- a/mypyc/irbuild/nonlocalcontrol.py +++ b/mypyc/irbuild/nonlocalcontrol.py @@ -120,8 +120,7 @@ def __init__(self, outer: NonlocalControl) -> None: self.outer = outer @abstractmethod - def gen_cleanup(self, builder: IRBuilder, line: int) -> None: - ... + def gen_cleanup(self, builder: IRBuilder, line: int) -> None: ... def gen_break(self, builder: IRBuilder, line: int) -> None: self.gen_cleanup(builder, line) diff --git a/test-requirements.in b/test-requirements.in index 2bf8de0aa2f5..5971074a00be 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,7 +4,7 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==23.9.1 # must match version in .pre-commit-config.yaml +black==24.1.1 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' diff --git a/test-requirements.txt b/test-requirements.txt index 57607c1bae57..683efa2a7334 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ # attrs==23.1.0 # via -r test-requirements.in -black==23.9.1 +black==24.1.1 # via -r test-requirements.in cfgv==3.4.0 # via pre-commit From d94b972ad3a2f61076ce1a70cf7e1dca1c9fe289 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:46:29 +0100 Subject: [PATCH 0474/1617] Update ruff to 0.1.15 (#16846) --- .pre-commit-config.yaml | 2 +- mypy/types.py | 2 +- test-requirements.in | 2 +- test-requirements.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6566bb0297d8..eec410457641 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.4 # must match test-requirements.txt + rev: v0.1.15 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/mypy/types.py b/mypy/types.py index 0e87ec701386..b1119c9447e2 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3108,7 +3108,7 @@ def get_proper_types( # to make it easier to gradually get modules working with mypyc. # Import them here, after the types are defined. # This is intended as a re-export also. -from mypy.type_visitor import ( # noqa: F811 +from mypy.type_visitor import ( ALL_STRATEGY as ALL_STRATEGY, ANY_STRATEGY as ANY_STRATEGY, BoolTypeQuery as BoolTypeQuery, diff --git a/test-requirements.in b/test-requirements.in index 5971074a00be..8f9d2cae0cce 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -14,6 +14,6 @@ psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.1.4 # must match version in .pre-commit-config.yaml +ruff==0.1.15 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 diff --git a/test-requirements.txt b/test-requirements.txt index 683efa2a7334..dcd250970a15 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -67,7 +67,7 @@ ruamel-yaml==0.17.40 # via pre-commit-hooks ruamel-yaml-clib==0.2.8 # via ruamel-yaml -ruff==0.1.4 +ruff==0.1.15 # via -r test-requirements.in tomli==2.0.1 # via -r test-requirements.in From 9f510570c703c57f4ad596a4d8df7d9283d18c4c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 1 Feb 2024 19:06:52 +0100 Subject: [PATCH 0475/1617] Remove --python-version 3.7 references from tests (#16848) Remove `--python-version 3.7` from tests if not necessary anymore or use `3.8` where it makes sense instead. --- test-data/unit/check-async-await.test | 5 - test-data/unit/check-callable.test | 1 - test-data/unit/check-dataclass-transform.test | 1 - test-data/unit/check-dataclasses.test | 97 +++---------------- test-data/unit/check-errorcodes.test | 4 +- test-data/unit/check-flags.test | 2 +- test-data/unit/check-generic-alias.test | 8 +- test-data/unit/check-incremental.test | 17 +--- test-data/unit/check-literal.test | 1 - test-data/unit/check-modules.test | 15 +-- test-data/unit/check-namedtuple.test | 1 - test-data/unit/check-narrowing.test | 3 - test-data/unit/check-newsemanal.test | 3 +- test-data/unit/check-newtype.test | 1 - test-data/unit/check-union-or-syntax.test | 1 - test-data/unit/check-unreachable-code.test | 10 +- test-data/unit/deps.test | 1 - test-data/unit/fine-grained.test | 21 ++-- test-requirements.in | 2 +- 19 files changed, 38 insertions(+), 156 deletions(-) diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index f0fa206645dd..713c82c752ce 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -819,8 +819,6 @@ def bar() -> None: [typing fixtures/typing-async.pyi] [case testAsyncForOutsideCoroutine] -# flags: --python-version 3.7 - async def g(): yield 0 @@ -841,8 +839,6 @@ async for x in g(): ... # E: "async for" outside async function [typing fixtures/typing-async.pyi] [case testAsyncWithOutsideCoroutine] -# flags: --python-version 3.7 - class C: async def __aenter__(self): pass async def __aexit__(self, x, y, z): pass @@ -858,7 +854,6 @@ async with C() as x: # E: "async with" outside async function [typing fixtures/typing-async.pyi] [case testAwaitMissingNote] -# flags: --python-version 3.7 from typing import Generic, TypeVar, Generator, Any, Awaitable, Type class C: diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test index 8a611a689be5..39e6c4fa3ff1 100644 --- a/test-data/unit/check-callable.test +++ b/test-data/unit/check-callable.test @@ -525,7 +525,6 @@ else: [builtins fixtures/callable.pyi] [case testBuiltinsTypeAsCallable] -# flags: --python-version 3.7 from __future__ import annotations reveal_type(type) # N: Revealed type is "def (x: Any) -> builtins.type" diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 743c7fef8aa9..51b2e186214f 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -22,7 +22,6 @@ Person('Jonh', 21, None) # E: Too many arguments for "Person" [builtins fixtures/dataclasses.pyi] [case testDataclassTransformIsFoundInTypingExtensions] -# flags: --python-version 3.7 from typing import Type from typing_extensions import dataclass_transform diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 107298875761..b57fe8f548c4 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1,5 +1,4 @@ [case testDataclassesBasic] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -18,7 +17,6 @@ Person('Jonh', 21, None) # E: Too many arguments for "Person" [typing fixtures/typing-medium.pyi] [case testDataclassesCustomInit] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -33,7 +31,6 @@ A('1') [builtins fixtures/dataclasses.pyi] [case testDataclassesBasicInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -56,7 +53,6 @@ Person(21, 'Jonh', None) # E: Too many arguments for "Person" [typing fixtures/typing-medium.pyi] [case testDataclassesDeepInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -83,7 +79,6 @@ reveal_type(D) # N: Revealed type is "def (a: builtins.int, b: builtins.int, c: [builtins fixtures/dataclasses.pyi] [case testDataclassesMultipleInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass, field, InitVar @dataclass class A: @@ -106,7 +101,6 @@ reveal_type(C) # N: Revealed type is "def (b: builtins.bool, a: builtins.bool) [builtins fixtures/dataclasses.pyi] [case testDataclassesDeepInitVarInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass, field, InitVar @dataclass class A: @@ -135,7 +129,6 @@ reveal_type(D) # N: Revealed type is "def (b: builtins.bool) -> __main__.D" [builtins fixtures/dataclasses.pyi] [case testDataclassesOverriding] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -169,7 +162,6 @@ ExtraSpecialPerson(21, 'John', 0.5) [case testDataclassesOverridingWithDefaults] # Issue #5681 https://github.com/python/mypy/issues/5681 -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any @@ -188,7 +180,6 @@ reveal_type(C) # N: Revealed type is "def (some_int: builtins.int, some_str: bu [builtins fixtures/dataclasses.pyi] [case testDataclassIncompatibleOverrides] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -217,7 +208,6 @@ class BadDerived3(Base): [builtins fixtures/dataclasses.pyi] [case testDataclassMultipleInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass class Unrelated: @@ -237,7 +227,6 @@ reveal_type(d.bar) # N: Revealed type is "builtins.int" [builtins fixtures/dataclasses.pyi] [case testDataclassIncompatibleFrozenOverride] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(frozen=True) @@ -252,7 +241,6 @@ class BadDerived(Base): [builtins fixtures/dataclasses.pyi] [case testDataclassesFreezing] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(frozen=True) @@ -265,7 +253,6 @@ john.name = 'Ben' # E: Property "name" defined in "Person" is read-only [builtins fixtures/dataclasses.pyi] [case testDataclassesInconsistentFreezing] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(frozen=True) @@ -287,7 +274,6 @@ class BadFrozenDerived(NormalBase): # E: Cannot inherit frozen dataclass from a [builtins fixtures/dataclasses.pyi] [case testDataclassesFields] -# flags: --python-version 3.7 from dataclasses import dataclass, field @dataclass @@ -303,7 +289,6 @@ john.age = 24 [builtins fixtures/dataclasses.pyi] [case testDataclassesBadInit] -# flags: --python-version 3.7 from dataclasses import dataclass, field @dataclass @@ -318,7 +303,6 @@ class Person: [builtins fixtures/dataclasses.pyi] [case testDataclassesMultiInit] -# flags: --python-version 3.7 from dataclasses import dataclass, field from typing import List @@ -334,7 +318,6 @@ reveal_type(Person) # N: Revealed type is "def (name: builtins.str, friend_name [builtins fixtures/dataclasses.pyi] [case testDataclassesMultiInitDefaults] -# flags: --python-version 3.7 from dataclasses import dataclass, field from typing import List, Optional @@ -351,7 +334,6 @@ reveal_type(Person) # N: Revealed type is "def (name: builtins.str, friend_name [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaults] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -365,7 +347,6 @@ app = Application() [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultFactories] -# flags: --python-version 3.7 from dataclasses import dataclass, field @dataclass @@ -377,7 +358,6 @@ class Application: [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultFactoryTypeChecking] -# flags: --python-version 3.7 from dataclasses import dataclass, field @dataclass @@ -388,7 +368,6 @@ class Application: [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultOrdering] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -533,7 +512,6 @@ class Base: [builtins fixtures/dataclasses.pyi] [case testDataclassesClassmethods] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -549,7 +527,6 @@ app = Application.parse('') [builtins fixtures/dataclasses.pyi] [case testDataclassesOverloadsAndClassmethods] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import overload, Union @@ -582,20 +559,18 @@ reveal_type(A.foo("foo")) # N: Revealed type is "builtins.str" [builtins fixtures/dataclasses.pyi] [case testClassmethodShadowingFieldDoesNotCrash] -# flags: --python-version 3.7 from dataclasses import dataclass # This used to crash -- see #6217 @dataclass class Foo: bar: str - @classmethod # E: Name "bar" already defined on line 7 + @classmethod # E: Name "bar" already defined on line 6 def bar(cls) -> "Foo": return cls('asdf') [builtins fixtures/dataclasses.pyi] [case testDataclassesClassVars] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import ClassVar @@ -613,7 +588,6 @@ Application.COUNTER = 1 [builtins fixtures/dataclasses.pyi] [case testTypeAliasInDataclassDoesNotCrash] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Callable from typing_extensions import TypeAlias @@ -642,7 +616,6 @@ reveal_type(x) # N: Revealed type is "typing._SpecialForm" [typing fixtures/typing-medium.pyi] [case testDataclassOrdering] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(order=True) @@ -673,7 +646,6 @@ app1 >= app3 [builtins fixtures/dataclasses.pyi] [case testDataclassOrderingWithoutEquality] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(eq=False, order=True) # E: "eq" must be True if "order" is True @@ -683,7 +655,6 @@ class Application: [builtins fixtures/dataclasses.pyi] [case testDataclassOrderingWithCustomMethods] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(order=True) @@ -694,7 +665,6 @@ class Application: [builtins fixtures/dataclasses.pyi] [case testDataclassDefaultsInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Optional @@ -712,7 +682,6 @@ reveal_type(SpecializedApplication) # N: Revealed type is "def (id: Union[built [builtins fixtures/dataclasses.pyi] [case testDataclassGenerics] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, List, Optional, TypeVar @@ -757,7 +726,6 @@ class MyDataclass(Generic[T_co]): [builtins fixtures/dataclasses.pyi] [case testDataclassUntypedGenericInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -778,7 +746,6 @@ reveal_type(sub.attr) # N: Revealed type is "Any" [builtins fixtures/dataclasses.pyi] [case testDataclassGenericSubtype] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -805,7 +772,6 @@ reveal_type(sub_str.attr) # N: Revealed type is "builtins.str" [builtins fixtures/dataclasses.pyi] [case testDataclassGenericInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -832,7 +798,6 @@ reveal_type(sub.three) # N: Revealed type is "builtins.float" [builtins fixtures/dataclasses.pyi] [case testDataclassMultiGenericInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -860,7 +825,6 @@ reveal_type(sub.middle_attr) # N: Revealed type is "builtins.str" [builtins fixtures/dataclasses.pyi] [case testDataclassGenericsClassmethod] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -882,7 +846,6 @@ reveal_type(A(0).other) # N: Revealed type is "def (x: builtins.int) -> __main_ [builtins fixtures/dataclasses.pyi] [case testDataclassesForwardRefs] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -901,7 +864,6 @@ A(b=42) # E: Argument "b" to "A" has incompatible type "int"; expected "B" [case testDataclassesInitVars] -# flags: --python-version 3.7 from dataclasses import InitVar, dataclass @dataclass @@ -930,7 +892,6 @@ app.database_name # E: "SpecializedApplication" has no attribute "database_name [builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarsAndDefer] -# flags: --python-version 3.7 from dataclasses import InitVar, dataclass defer: Yes @@ -950,7 +911,6 @@ class Yes: ... [builtins fixtures/dataclasses.pyi] [case testDataclassesNoInitInitVarInheritance] -# flags: --python-version 3.7 from dataclasses import dataclass, field, InitVar @dataclass @@ -967,7 +927,6 @@ sub.bar [builtins fixtures/dataclasses.pyi] [case testDataclassFactory] -# flags: --python-version 3.7 from typing import Type, TypeVar from dataclasses import dataclass @@ -983,7 +942,6 @@ class A: [builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarOverride] -# flags: --python-version 3.7 import dataclasses @dataclasses.dataclass @@ -1006,7 +964,6 @@ class B(A): [builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarNoOverride] -# flags: --python-version 3.7 import dataclasses @dataclasses.dataclass @@ -1032,7 +989,6 @@ B(1, 'a') # E: Argument 2 to "B" has incompatible type "str"; expected "int" [builtins fixtures/dataclasses.pyi] [case testDataclassesInitVarPostInitOverride] -# flags: --python-version 3.7 import dataclasses @dataclasses.dataclass @@ -1068,7 +1024,6 @@ C(1, 'a') # E: Argument 2 to "C" has incompatible type "str"; expected "int" [builtins fixtures/primitives.pyi] [case testDataclassesInitVarIncremental] -# flags: --python-version 3.7 import a [file a.py] @@ -1114,7 +1069,6 @@ tmp/a.py:12: note: Revealed type is "def (a: builtins.int) -> a.B" [case testNoComplainFieldNone] -# flags: --python-version 3.7 # flags: --no-strict-optional from dataclasses import dataclass, field from typing import Optional @@ -1126,7 +1080,6 @@ class Foo: [out] [case testNoComplainFieldNoneStrict] -# flags: --python-version 3.7 from dataclasses import dataclass, field from typing import Optional @@ -1137,7 +1090,7 @@ class Foo: [out] [case testDisallowUntypedWorksForward] -# flags: --disallow-untyped-defs --python-version 3.7 +# flags: --disallow-untyped-defs from dataclasses import dataclass from typing import List @@ -1152,7 +1105,7 @@ reveal_type(B) # N: Revealed type is "def (x: __main__.C) -> __main__.B" [builtins fixtures/dataclasses.pyi] [case testDisallowUntypedWorksForwardBad] -# flags: --disallow-untyped-defs --python-version 3.7 +# flags: --disallow-untyped-defs from dataclasses import dataclass @dataclass @@ -1164,7 +1117,6 @@ reveal_type(B) # N: Revealed type is "def (x: Any) -> __main__.B" [builtins fixtures/dataclasses.pyi] [case testMemberExprWorksAsField] -# flags: --python-version 3.7 import dataclasses @dataclasses.dataclass @@ -1184,7 +1136,6 @@ class C: [builtins fixtures/dict.pyi] [case testDataclassOrderingDeferred] -# flags: --python-version 3.7 from dataclasses import dataclass defer: Yes @@ -1202,7 +1153,6 @@ class Yes: ... [builtins fixtures/dataclasses.pyi] [case testDataclassFieldDeferred] -# flags: --python-version 3.7 from dataclasses import field, dataclass @dataclass @@ -1214,7 +1164,6 @@ C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" [builtins fixtures/dataclasses.pyi] [case testDataclassFieldDeferredFrozen] -# flags: --python-version 3.7 from dataclasses import field, dataclass @dataclass(frozen=True) @@ -1227,7 +1176,6 @@ c.x = 1 # E: Property "x" defined in "C" is read-only [builtins fixtures/dataclasses.pyi] [case testTypeInDataclassDeferredStar] -# flags: --python-version 3.7 import lib [file lib.py] from dataclasses import dataclass @@ -1246,7 +1194,7 @@ import lib [builtins fixtures/dataclasses.pyi] [case testDeferredDataclassInitSignature] -# flags: --python-version 3.7 --no-strict-optional +# flags: --no-strict-optional from dataclasses import dataclass from typing import Optional, Type @@ -1263,7 +1211,6 @@ class Deferred: pass [builtins fixtures/dataclasses.pyi] [case testDeferredDataclassInitSignatureSubclass] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Optional @@ -1279,7 +1226,6 @@ a = C(None, 'abc') [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultsIncremental] -# flags: --python-version 3.7 import a [file a.py] @@ -1311,7 +1257,6 @@ class Person: [builtins fixtures/dataclasses.pyi] [case testDataclassesDefaultsMroOtherFile] -# flags: --python-version 3.7 import a [file a.py] @@ -1340,14 +1285,13 @@ class A2: [builtins fixtures/dataclasses.pyi] [case testDataclassesInheritingDuplicateField] -# flags: --python-version 3.7 # see mypy issue #7792 from dataclasses import dataclass @dataclass class A: x: int = 0 - x: int = 0 # E: Name "x" already defined on line 7 + x: int = 0 # E: Name "x" already defined on line 6 @dataclass class B(A): @@ -1356,7 +1300,6 @@ class B(A): [builtins fixtures/dataclasses.pyi] [case testDataclassInheritanceNoAnnotation] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -1373,7 +1316,6 @@ reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B" [builtins fixtures/dataclasses.pyi] [case testDataclassInheritanceNoAnnotation2] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass(frozen=True) @@ -1390,7 +1332,6 @@ reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B" [builtins fixtures/dataclasses.pyi] [case testDataclassHasAttributeWithFields] -# flags: --python-version 3.7 from dataclasses import dataclass @dataclass @@ -1402,7 +1343,6 @@ reveal_type(A.__dataclass_fields__) # N: Revealed type is "builtins.dict[builti [builtins fixtures/dict.pyi] [case testDataclassCallableFieldAccess] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Callable @@ -1420,7 +1360,6 @@ reveal_type(A.y) # N: Revealed type is "def (builtins.int) -> builtins.int" [builtins fixtures/dataclasses.pyi] [case testDataclassCallableFieldAssignment] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Callable @@ -1439,7 +1378,6 @@ a.x = x2 # E: Incompatible types in assignment (expression has type "Callable[[s [builtins fixtures/dataclasses.pyi] [case testDataclassFieldDoesNotFailOnKwargsUnpacking] -# flags: --python-version 3.7 # https://github.com/python/mypy/issues/10879 from dataclasses import dataclass, field @@ -1447,16 +1385,15 @@ from dataclasses import dataclass, field class Foo: bar: float = field(**{"repr": False}) [out] -main:7: error: Unpacking **kwargs in "field()" is not supported -main:7: error: No overload variant of "field" matches argument type "Dict[str, bool]" -main:7: note: Possible overload variants: -main:7: note: def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T -main:7: note: def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T -main:7: note: def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any +main:6: error: Unpacking **kwargs in "field()" is not supported +main:6: error: No overload variant of "field" matches argument type "Dict[str, bool]" +main:6: note: Possible overload variants: +main:6: note: def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T +main:6: note: def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T +main:6: note: def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any [builtins fixtures/dataclasses.pyi] [case testDataclassFieldWithPositionalArguments] -# flags: --python-version 3.7 from dataclasses import dataclass, field @dataclass @@ -1470,7 +1407,6 @@ class C: [builtins fixtures/dataclasses.pyi] [case testDataclassFieldWithTypedDictUnpacking] -# flags: --python-version 3.7 from dataclasses import dataclass, field from typing_extensions import TypedDict @@ -1668,7 +1604,6 @@ class PublishedMessages: [builtins fixtures/dataclasses.pyi] [case testDataclassesAnyInherit] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any B: Any @@ -1682,7 +1617,6 @@ A(a="foo") # E: Argument "a" to "A" has incompatible type "str"; expected "int" [builtins fixtures/dataclasses.pyi] [case testDataclassesCallableFrozen] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any, Callable @dataclass(frozen=True) @@ -1698,7 +1632,6 @@ A(a=func).a = func # E: Property "a" defined in "A" is read-only [builtins fixtures/dataclasses.pyi] [case testDataclassInFunctionDoesNotCrash] -# flags: --python-version 3.7 from dataclasses import dataclass def foo() -> None: @@ -1728,7 +1661,6 @@ class Derived(A, B): [builtins fixtures/dataclasses.pyi] [case testDataclassGenericInheritance2] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any, Callable, Generic, TypeVar, List @@ -1760,7 +1692,6 @@ reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[ [builtins fixtures/dataclasses.pyi] [case testDataclassInheritOptionalType] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Any, Callable, Generic, TypeVar, List, Optional @@ -1779,7 +1710,6 @@ Child(x=None, y=None) [builtins fixtures/dataclasses.pyi] [case testDataclassGenericInheritanceSpecialCase1] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar, List @@ -1803,7 +1733,6 @@ def g(c: Child1) -> None: [builtins fixtures/dataclasses.pyi] [case testDataclassGenericInheritanceSpecialCase2] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -1829,7 +1758,6 @@ Child2(y=1, key='') [builtins fixtures/dataclasses.pyi] [case testDataclassGenericWithBound] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import Generic, TypeVar @@ -1845,7 +1773,6 @@ C(x=2) [builtins fixtures/dataclasses.pyi] [case testDataclassGenericBoundToInvalidTypeVarDoesNotCrash] -# flags: --python-version 3.7 import dataclasses from typing import Generic, TypeVar @@ -1857,7 +1784,6 @@ class C(Generic[T]): [builtins fixtures/dataclasses.pyi] [case testDataclassInitVarCannotBeSet] -# flags: --python-version 3.7 from dataclasses import dataclass, InitVar @dataclass @@ -1877,7 +1803,6 @@ c.x # E: "C" has no attribute "x" [builtins fixtures/dataclasses.pyi] [case testDataclassCheckTypeVarBounds] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import ClassVar, Protocol, Dict, TypeVar, Generic diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1dd058730f28..7f5f05d37595 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -124,10 +124,10 @@ z # type: ignore[ignore-without-code] # E: Unused "type: ignore" comment [unuse # flags: --enable-error-code ignore-without-code # type: ignore # whole file ignore x -y # type: ignore # ignore the lack of error code since we're ignore the whole file +y # type: ignore # ignore the lack of error code since we ignore the whole file [case testErrorCodeMissingMultiple] -# flags: --enable-error-code ignore-without-code --python-version 3.7 +# flags: --enable-error-code ignore-without-code from __future__ import annotations class A: attr: int diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 04adaca317c1..c90c773e320f 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1655,7 +1655,7 @@ __all__ = ('b',) [builtins fixtures/tuple.pyi] [case testNoImplicitReexportGetAttr] -# flags: --no-implicit-reexport --python-version 3.7 +# flags: --no-implicit-reexport from other_module_2 import a # E: Module "other_module_2" does not explicitly export attribute "a" reveal_type(a) # N: Revealed type is "builtins.int" from other_module_2 import b # E: Module "other_module_2" does not explicitly export attribute "b" diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 8c90b5adba34..3ae815a5cd48 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -1,7 +1,7 @@ -- Test cases for generic aliases [case testGenericBuiltinWarning] -# flags: --python-version 3.7 +# flags: --python-version 3.8 t1: list t2: list[int] # E: "list" is not subscriptable, use "typing.List" instead t3: list[str] # E: "list" is not subscriptable, use "typing.List" instead @@ -21,14 +21,14 @@ t11: type[int] # E: "type" expects no type arguments, but 1 given [case testGenericBuiltinSetWarning] -# flags: --python-version 3.7 +# flags: --python-version 3.8 t1: set t2: set[int] # E: "set" is not subscriptable, use "typing.Set" instead [builtins fixtures/set.pyi] [case testGenericCollectionsWarning] -# flags: --python-version 3.7 +# flags: --python-version 3.8 import collections t01: collections.deque @@ -44,7 +44,6 @@ t10: collections.ChainMap[int, str] # E: "ChainMap" is not subscriptable, use " [case testGenericBuiltinFutureAnnotations] -# flags: --python-version 3.7 from __future__ import annotations t1: list t2: list[int] @@ -64,7 +63,6 @@ t11: type[int] [case testGenericCollectionsFutureAnnotations] -# flags: --python-version 3.7 from __future__ import annotations import collections diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 2c7d908c5f5b..69381227ca8e 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3796,7 +3796,6 @@ import b [rechecked b] [case testIncrementalDataclassesSubclassingCached] -# flags: --python-version 3.7 from a import A from dataclasses import dataclass @@ -3829,7 +3828,6 @@ class A: [out2] [case testIncrementalDataclassesSubclassingCachedType] -# flags: --python-version 3.7 import b [file b.py] @@ -3863,7 +3861,6 @@ class A: tmp/b.py:8: note: Revealed type is "def (x: builtins.int) -> b.B" [case testIncrementalDataclassesArguments] -# flags: --python-version 3.7 import b [file b.py] @@ -3912,7 +3909,6 @@ tmp/b.py:15: error: Unsupported left operand type for > ("NoCmp") tmp/b.py:16: error: Unsupported left operand type for >= ("NoCmp") [case testIncrementalDataclassesDunder] -# flags: --python-version 3.7 import b [file b.py] @@ -3977,7 +3973,6 @@ tmp/b.py:27: error: Unsupported operand types for < ("A" and "int") tmp/b.py:28: error: Unsupported operand types for <= ("A" and "int") [case testIncrementalDataclassesSubclassModified] -# flags: --python-version 3.7 from b import B B(5, 'foo') @@ -4007,11 +4002,10 @@ class B(A): [builtins fixtures/dataclasses.pyi] [out1] [out2] -main:3: error: Argument 2 to "B" has incompatible type "str"; expected "int" +main:2: error: Argument 2 to "B" has incompatible type "str"; expected "int" [rechecked b] [case testIncrementalDataclassesSubclassModifiedErrorFirst] -# flags: --python-version 3.7 from b import B B(5, 'foo') @@ -4040,13 +4034,12 @@ class B(A): [builtins fixtures/dataclasses.pyi] [out1] -main:3: error: Argument 2 to "B" has incompatible type "str"; expected "int" +main:2: error: Argument 2 to "B" has incompatible type "str"; expected "int" [out2] [rechecked b] [case testIncrementalDataclassesThreeFiles] -# flags: --python-version 3.7 from c import C C('foo', 5, True) @@ -4085,10 +4078,9 @@ class C(A, B): [out1] [out2] tmp/c.py:7: error: Incompatible types in assignment (expression has type "bool", base class "B" defined the type as "str") -main:3: error: Argument 2 to "C" has incompatible type "int"; expected "bool" +main:2: error: Argument 2 to "C" has incompatible type "int"; expected "bool" [case testIncrementalDataclassesThreeRuns] -# flags: --python-version 3.7 from a import A A(5) @@ -4116,7 +4108,7 @@ class A: [builtins fixtures/dataclasses.pyi] [out1] [out2] -main:3: error: Argument 1 to "A" has incompatible type "int"; expected "str" +main:2: error: Argument 1 to "A" has incompatible type "int"; expected "str" [out3] [case testParentPatchingMess] @@ -5664,7 +5656,6 @@ A().g() [out2] [case testIncrementalWithDifferentKindsOfNestedTypesWithinMethod] -# flags: --python-version 3.7 import a diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index ea2aa5068e85..de4440ce7f49 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -64,7 +64,6 @@ reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal [out] [case testLiteralInsideOtherTypesTypeCommentsPython3] -# flags: --python-version 3.7 from typing import Tuple, Optional from typing_extensions import Literal diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 44585fdd8d1a..5fd48577e436 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2075,9 +2075,7 @@ def __getattr__(name): ... [builtins fixtures/module.pyi] -[case testModuleLevelGetattrNotStub37] -# flags: --python-version 3.7 - +[case testModuleLevelGetattrNotStub] import has_getattr reveal_type(has_getattr.any_attribute) # N: Revealed type is "builtins.str" @@ -2109,8 +2107,7 @@ def __getattr__(name: str) -> int: ... [builtins fixtures/module.pyi] -[case testModuleLevelGetattrImportFromNotStub37] -# flags: --python-version 3.7 +[case testModuleLevelGetattrImportFromNotStub] from non_stub import name reveal_type(name) # N: Revealed type is "Any" @@ -2145,7 +2142,6 @@ def __getattr__(name: str) -> int: ... [case testModuleLevelGetattrAssignedGood] -# flags: --python-version 3.7 import non_stub reveal_type(non_stub.name) # N: Revealed type is "builtins.int" @@ -2156,7 +2152,6 @@ def make_getattr_good() -> Callable[[str], int]: ... __getattr__ = make_getattr_good() # OK [case testModuleLevelGetattrAssignedBad] -# flags: --python-version 3.7 import non_stub reveal_type(non_stub.name) @@ -2168,10 +2163,9 @@ __getattr__ = make_getattr_bad() [out] tmp/non_stub.py:4: error: Invalid signature "Callable[[], int]" for "__getattr__" -main:3: note: Revealed type is "builtins.int" +main:2: note: Revealed type is "builtins.int" [case testModuleLevelGetattrImportedGood] -# flags: --python-version 3.7 import non_stub reveal_type(non_stub.name) # N: Revealed type is "builtins.int" @@ -2182,7 +2176,6 @@ from has_getattr import __getattr__ def __getattr__(name: str) -> int: ... [case testModuleLevelGetattrImportedBad] -# flags: --python-version 3.7 import non_stub reveal_type(non_stub.name) @@ -2194,7 +2187,7 @@ def __getattr__() -> int: ... [out] tmp/has_getattr.py:1: error: Invalid signature "Callable[[], int]" for "__getattr__" -main:3: note: Revealed type is "builtins.int" +main:2: note: Revealed type is "builtins.int" [builtins fixtures/module.pyi] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 51b02b500bd1..0ce8630e51d9 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -131,7 +131,6 @@ main:6: error: Unexpected keyword argument "unrecognized_arg" for "namedtuple" main:7: error: Too many positional arguments for "namedtuple" [case testNamedTupleDefaults] -# flags: --python-version 3.7 from collections import namedtuple X = namedtuple('X', ['x', 'y'], defaults=(1,)) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index d50d1f508b85..4d117687554e 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1,5 +1,4 @@ [case testNarrowingParentWithStrsBasic] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import NamedTuple, Tuple, Union from typing_extensions import Literal, TypedDict @@ -83,7 +82,6 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingParentWithEnumsBasic] -# flags: --python-version 3.7 from enum import Enum from dataclasses import dataclass from typing import NamedTuple, Tuple, Union @@ -173,7 +171,6 @@ else: [builtins fixtures/narrowing.pyi] [case testNarrowingParentWithIsInstanceBasic] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import NamedTuple, Tuple, Union from typing_extensions import TypedDict diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index f4d3b9df760e..7cbed5637c3a 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2577,8 +2577,7 @@ import n [file n.pyi] class C: pass -[case testNewAnalyzerModuleGetAttrInPython37] -# flags: --python-version 3.7 +[case testNewAnalyzerModuleGetAttr] import m import n diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index 99fdf5fe7ca3..a0a30079f062 100644 --- a/test-data/unit/check-newtype.test +++ b/test-data/unit/check-newtype.test @@ -381,7 +381,6 @@ x: List[Union[N, int]] [builtins fixtures/list.pyi] [case testTypingExtensionsNewType] -# flags: --python-version 3.7 from typing_extensions import NewType N = NewType("N", int) x: N diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index f342d0ca34a5..85e268f348f0 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -127,7 +127,6 @@ cast(str | int, 'x') # E: Cast target is not a type x = 1 # type: int | str [case testUnionOrSyntaxFutureImport] -# flags: --python-version 3.7 from __future__ import annotations x: int | None [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index acb5ca6ea609..b8b438b979c6 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -798,7 +798,7 @@ def baz(x: int) -> int: [builtins fixtures/exception.pyi] [case testUnreachableFlagIgnoresSemanticAnalysisUnreachable] -# flags: --warn-unreachable --python-version 3.7 --platform win32 --always-false FOOBAR +# flags: --warn-unreachable --python-version 3.8 --platform win32 --always-false FOOBAR import sys from typing import TYPE_CHECKING @@ -828,7 +828,7 @@ if sys.version_info == (2, 7): else: reveal_type(x) # N: Revealed type is "builtins.int" -if sys.version_info == (3, 7): +if sys.version_info == (3, 8): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) @@ -1155,7 +1155,7 @@ def f_suppress() -> int: # E: Missing return statement [builtins fixtures/tuple.pyi] [case testUnreachableFlagContextAsyncManagersNoSuppress] -# flags: --warn-unreachable --python-version 3.7 +# flags: --warn-unreachable from contextlib import asynccontextmanager from typing import Optional, AsyncIterator, Any from typing_extensions import Literal @@ -1221,7 +1221,7 @@ async def f_no_suppress_5() -> int: [builtins fixtures/tuple.pyi] [case testUnreachableFlagContextAsyncManagersSuppressed] -# flags: --warn-unreachable --python-version 3.7 +# flags: --warn-unreachable from contextlib import asynccontextmanager from typing import Optional, AsyncIterator, Any from typing_extensions import Literal @@ -1268,7 +1268,7 @@ async def f_mix() -> int: # E: Missing return statement [builtins fixtures/tuple.pyi] [case testUnreachableFlagContextAsyncManagersAbnormal] -# flags: --warn-unreachable --python-version 3.7 +# flags: --warn-unreachable from contextlib import asynccontextmanager from typing import Optional, AsyncIterator, Any from typing_extensions import Literal diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 5e77ff1d85e0..d18b4aae963b 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1369,7 +1369,6 @@ def h() -> None: -> m.D, m.h [case testDataclassDepsOldVersion] -# flags: --python-version 3.7 from dataclasses import dataclass Z = int diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 266d9a9efd01..9c379d8f60da 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -652,7 +652,6 @@ class M(type): a.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testDataclassUpdate1] -# flags: --python-version 3.7 [file a.py] from dataclasses import dataclass @@ -691,7 +690,6 @@ b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str" [builtins fixtures/dataclasses.pyi] [case testDataclassUpdate2] -# flags: --python-version 3.7 [file c.py] Foo = int @@ -722,7 +720,6 @@ b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str" [builtins fixtures/dataclasses.pyi] [case testDataclassUpdate3] -# flags: --python-version 3.7 from b import B B(1, 2) [file b.py] @@ -746,10 +743,9 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Missing positional argument "b" in call to "B" +main:2: error: Missing positional argument "b" in call to "B" [case testDataclassUpdate4] -# flags: --python-version 3.7 from b import B B(1, 2) [file b.py] @@ -773,10 +769,9 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Missing positional argument "b" in call to "B" +main:2: error: Missing positional argument "b" in call to "B" [case testDataclassUpdate5] -# flags: --python-version 3.7 from b import B B(1, 2) [file b.py] @@ -807,11 +802,10 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Missing positional argument "b" in call to "B" +main:2: error: Missing positional argument "b" in call to "B" == [case testDataclassUpdate6] -# flags: --python-version 3.7 from b import B B(1, 2) < B(1, 2) [file b.py] @@ -834,10 +828,9 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Unsupported left operand type for < ("B") +main:2: error: Unsupported left operand type for < ("B") [case testDataclassUpdate8] -# flags: --python-version 3.7 from c import C C(1, 2, 3) [file c.py] @@ -867,10 +860,9 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Missing positional argument "c" in call to "C" +main:2: error: Missing positional argument "c" in call to "C" [case testDataclassUpdate9] -# flags: --python-version 3.7 from c import C C(1, 2, 3) [file c.py] @@ -907,7 +899,7 @@ class A: [builtins fixtures/dataclasses.pyi] [out] == -main:3: error: Missing positional argument "c" in call to "C" +main:2: error: Missing positional argument "c" in call to "C" == [case testAttrsUpdate1] @@ -9799,7 +9791,6 @@ class ExampleClass(Generic[T]): == [case testDataclassCheckTypeVarBoundsInReprocess] -# flags: --python-version 3.7 from dataclasses import dataclass from typing import ClassVar, Protocol, Dict, TypeVar, Generic from m import x diff --git a/test-requirements.in b/test-requirements.in index 8f9d2cae0cce..a158f5c05ee1 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -16,4 +16,4 @@ pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.1.15 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 -tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.7 +tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 From e48a0258a4bb61710045b6f2136f8b119b9fd525 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:13:07 +0100 Subject: [PATCH 0476/1617] Remove unnecessary --python-version 3.8 references from tests (#16850) Remove the `--python-version 3.8` flag where it's used to indicate the min python version requirement. Mypy doesn't support running under Python 3.7 / the tests are only run for 3.8+. --- test-data/unit/check-functools.test | 1 - .../unit/check-parameter-specification.test | 8 +- test-data/unit/check-python38.test | 90 +++++++------------ test-data/unit/check-typeddict.test | 1 - 4 files changed, 32 insertions(+), 68 deletions(-) diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index ebfe86f2b241..e721a56850e1 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -102,7 +102,6 @@ Ord() >= 1 # E: Unsupported left operand type for >= ("Ord") [builtins fixtures/dict.pyi] [case testCachedProperty] -# flags: --python-version 3.8 from functools import cached_property class Parent: @property diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 7a5c5934a94e..b212c7585993 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -642,7 +642,7 @@ reveal_type(f(n)) # N: Revealed type is "def (builtins.int, builtins.bytes) -> [builtins fixtures/paramspec.pyi] [case testParamSpecConcatenateNamedArgs] -# flags: --python-version 3.8 --extra-checks +# flags: --extra-checks # this is one noticeable deviation from PEP but I believe it is for the better from typing_extensions import ParamSpec, Concatenate from typing import Callable, TypeVar @@ -668,8 +668,6 @@ main:17: error: Incompatible return value type (got "Callable[[Arg(int, 'x'), ** main:17: note: This is likely because "result" has named arguments: "x". Consider marking them positional-only [case testNonStrictParamSpecConcatenateNamedArgs] -# flags: --python-version 3.8 - # this is one noticeable deviation from PEP but I believe it is for the better from typing_extensions import ParamSpec, Concatenate from typing import Callable, TypeVar @@ -710,8 +708,6 @@ reveal_type(n(42)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] [case testCallablesAsParameters] -# flags: --python-version 3.8 - # credits to https://github.com/microsoft/pyright/issues/2705 from typing_extensions import ParamSpec, Concatenate from typing import Generic, Callable, Any @@ -729,7 +725,7 @@ reveal_type(abc) bar(abc) [builtins fixtures/paramspec.pyi] [out] -main:16: note: Revealed type is "__main__.Foo[[builtins.int, b: builtins.str]]" +main:14: note: Revealed type is "__main__.Foo[[builtins.int, b: builtins.str]]" [case testSolveParamSpecWithSelfType] from typing_extensions import ParamSpec, Concatenate diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 1e99c760b67a..0f1cbb6e81c4 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -115,25 +115,21 @@ def g(x: int): ... ) # type: ignore # E: Unused "type: ignore" comment [case testPEP570ArgTypesMissing] -# flags: --python-version 3.8 --disallow-untyped-defs +# flags: --disallow-untyped-defs def f(arg, /) -> None: ... # E: Function is missing a type annotation for one or more arguments [case testPEP570ArgTypesBadDefault] -# flags: --python-version 3.8 def f(arg: int = "ERROR", /) -> None: ... # E: Incompatible default for argument "arg" (default has type "str", argument has type "int") [case testPEP570ArgTypesDefault] -# flags: --python-version 3.8 def f(arg: int = 0, /) -> None: reveal_type(arg) # N: Revealed type is "builtins.int" [case testPEP570ArgTypesRequired] -# flags: --python-version 3.8 def f(arg: int, /) -> None: reveal_type(arg) # N: Revealed type is "builtins.int" [case testPEP570Required] -# flags: --python-version 3.8 def f(arg: int, /) -> None: ... # N: "f" defined here f(1) f("ERROR") # E: Argument 1 to "f" has incompatible type "str"; expected "int" @@ -141,7 +137,6 @@ f(arg=1) # E: Unexpected keyword argument "arg" for "f" f(arg="ERROR") # E: Unexpected keyword argument "arg" for "f" [case testPEP570Default] -# flags: --python-version 3.8 def f(arg: int = 0, /) -> None: ... # N: "f" defined here f() f(1) @@ -150,7 +145,7 @@ f(arg=1) # E: Unexpected keyword argument "arg" for "f" f(arg="ERROR") # E: Unexpected keyword argument "arg" for "f" [case testPEP570Calls] -# flags: --python-version 3.8 --no-strict-optional +# flags: --no-strict-optional from typing import Any, Dict def f(p, /, p_or_kw, *, kw) -> None: ... # N: "f" defined here d = None # type: Dict[Any, Any] @@ -163,7 +158,6 @@ f(**d) # E: Missing positional argument "p_or_kw" in call to "f" [builtins fixtures/dict.pyi] [case testPEP570Signatures1] -# flags: --python-version 3.8 def f(p1: bytes, p2: float, /, p_or_kw: int, *, kw: str) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.float" @@ -171,7 +165,6 @@ def f(p1: bytes, p2: float, /, p_or_kw: int, *, kw: str) -> None: reveal_type(kw) # N: Revealed type is "builtins.str" [case testPEP570Signatures2] -# flags: --python-version 3.8 def f(p1: bytes, p2: float = 0.0, /, p_or_kw: int = 0, *, kw: str) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.float" @@ -179,33 +172,28 @@ def f(p1: bytes, p2: float = 0.0, /, p_or_kw: int = 0, *, kw: str) -> None: reveal_type(kw) # N: Revealed type is "builtins.str" [case testPEP570Signatures3] -# flags: --python-version 3.8 def f(p1: bytes, p2: float = 0.0, /, *, kw: int) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.float" reveal_type(kw) # N: Revealed type is "builtins.int" [case testPEP570Signatures4] -# flags: --python-version 3.8 def f(p1: bytes, p2: int = 0, /) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.int" [case testPEP570Signatures5] -# flags: --python-version 3.8 def f(p1: bytes, p2: float, /, p_or_kw: int) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.float" reveal_type(p_or_kw) # N: Revealed type is "builtins.int" [case testPEP570Signatures6] -# flags: --python-version 3.8 def f(p1: bytes, p2: float, /) -> None: reveal_type(p1) # N: Revealed type is "builtins.bytes" reveal_type(p2) # N: Revealed type is "builtins.float" [case testPEP570Unannotated] -# flags: --python-version 3.8 def f(arg, /): ... # N: "f" defined here g = lambda arg, /: arg def h(arg=0, /): ... # N: "h" defined here @@ -223,7 +211,6 @@ h(arg=0) # E: Unexpected keyword argument "arg" for "h" i(arg=0) # E: Unexpected keyword argument "arg" [case testWalrus] -# flags: --python-version 3.8 from typing import NamedTuple, Optional, List from typing_extensions import Final @@ -399,7 +386,6 @@ reveal_type(z2) # E: Name "z2" is not defined # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] [case testWalrusConditionalTypeBinder] -# flags: --python-version 3.8 from typing import Tuple, Union from typing_extensions import Literal @@ -427,7 +413,6 @@ else: [builtins fixtures/list.pyi] [case testWalrusConditionalTypeCheck] -# flags: --python-version 3.8 from typing import Optional maybe_str: Optional[str] @@ -443,7 +428,6 @@ reveal_type(maybe_str) # N: Revealed type is "Union[builtins.str, None]" [builtins fixtures/bool.pyi] [case testWalrusConditionalTypeCheck2] -# flags: --python-version 3.8 from typing import Optional maybe_str: Optional[str] @@ -459,7 +443,6 @@ reveal_type(maybe_str) # N: Revealed type is "Union[builtins.str, None]" [builtins fixtures/bool.pyi] [case testWalrusPartialTypes] -# flags: --python-version 3.8 from typing import List def check_partial_list() -> None: @@ -476,7 +459,7 @@ def check_partial_list() -> None: [builtins fixtures/list.pyi] [case testWalrusAssignmentAndConditionScopeForLiteral] -# flags: --warn-unreachable --python-version 3.8 +# flags: --warn-unreachable if (x := 0): reveal_type(x) # E: Statement is unreachable @@ -486,7 +469,7 @@ else: reveal_type(x) # N: Revealed type is "builtins.int" [case testWalrusAssignmentAndConditionScopeForProperty] -# flags: --warn-unreachable --python-version 3.8 +# flags: --warn-unreachable from typing_extensions import Literal @@ -514,7 +497,7 @@ reveal_type(y) # N: Revealed type is "Literal[False]" [builtins fixtures/property.pyi] [case testWalrusAssignmentAndConditionScopeForFunction] -# flags: --warn-unreachable --python-version 3.8 +# flags: --warn-unreachable from typing_extensions import Literal @@ -547,7 +530,6 @@ reveal_type(z) # N: Revealed type is "Literal[False]" [builtins fixtures/tuple.pyi] [case testWalrusExpr] -# flags: --python-version 3.8 def func() -> None: foo = Foo() if x := foo.x: @@ -558,7 +540,6 @@ class Foo: self.x = 123 [case testWalrusTypeGuard] -# flags: --python-version 3.8 from typing_extensions import TypeGuard def is_float(a: object) -> TypeGuard[float]: pass def main(a: object) -> None: @@ -568,14 +549,12 @@ def main(a: object) -> None: [builtins fixtures/tuple.pyi] [case testWalrusRedefined] -# flags: --python-version 3.8 def foo() -> None: x = 0 [x := x + y for y in [1, 2, 3]] [builtins fixtures/dict.pyi] [case testWalrusUsedBeforeDef] -# flags: --python-version 3.8 class C: def f(self, c: 'C') -> None: pass @@ -583,7 +562,6 @@ class C: (y := C()).f(y) [case testOverloadWithPositionalOnlySelf] -# flags: --python-version 3.8 from typing import overload, Optional class Foo: @@ -608,7 +586,6 @@ class Bar: [builtins fixtures/bool.pyi] [case testOverloadPositionalOnlyErrorMessage] -# flags: --python-version 3.8 from typing import overload @overload @@ -619,13 +596,12 @@ def foo(a): ... foo(a=1) [out] -main:10: error: No overload variant of "foo" matches argument type "int" -main:10: note: Possible overload variants: -main:10: note: def foo(int, /) -> Any -main:10: note: def foo(a: str) -> Any +main:9: error: No overload variant of "foo" matches argument type "int" +main:9: note: Possible overload variants: +main:9: note: def foo(int, /) -> Any +main:9: note: def foo(a: str) -> Any [case testOverloadPositionalOnlyErrorMessageAllTypes] -# flags: --python-version 3.8 from typing import overload @overload @@ -636,13 +612,12 @@ def foo(a, b, *, c): ... foo(a=1) [out] -main:10: error: No overload variant of "foo" matches argument type "int" -main:10: note: Possible overload variants: -main:10: note: def foo(int, /, b: int, *, c: int) -> Any -main:10: note: def foo(a: str, b: int, *, c: int) -> Any +main:9: error: No overload variant of "foo" matches argument type "int" +main:9: note: Possible overload variants: +main:9: note: def foo(int, /, b: int, *, c: int) -> Any +main:9: note: def foo(a: str, b: int, *, c: int) -> Any [case testOverloadPositionalOnlyErrorMessageMultiplePosArgs] -# flags: --python-version 3.8 from typing import overload @overload @@ -653,13 +628,12 @@ def foo(a, b, c, d): ... foo(a=1) [out] -main:10: error: No overload variant of "foo" matches argument type "int" -main:10: note: Possible overload variants: -main:10: note: def foo(int, int, int, /, d: str) -> Any -main:10: note: def foo(a: str, b: int, c: int, d: str) -> Any +main:9: error: No overload variant of "foo" matches argument type "int" +main:9: note: Possible overload variants: +main:9: note: def foo(int, int, int, /, d: str) -> Any +main:9: note: def foo(a: str, b: int, c: int, d: str) -> Any [case testOverloadPositionalOnlyErrorMessageMethod] -# flags: --python-version 3.8 from typing import overload class Some: @@ -673,14 +647,13 @@ class Some: Some().foo(a=1) [out] -main:13: error: No overload variant of "foo" of "Some" matches argument type "int" -main:13: note: Possible overload variants: -main:13: note: def foo(self, int, /) -> Any -main:13: note: def foo(self, float, /) -> Any -main:13: note: def foo(self, a: str) -> Any +main:12: error: No overload variant of "foo" of "Some" matches argument type "int" +main:12: note: Possible overload variants: +main:12: note: def foo(self, int, /) -> Any +main:12: note: def foo(self, float, /) -> Any +main:12: note: def foo(self, a: str) -> Any [case testOverloadPositionalOnlyErrorMessageClassMethod] -# flags: --python-version 3.8 from typing import overload class Some: @@ -699,14 +672,13 @@ class Some: Some.foo(a=1) [builtins fixtures/classmethod.pyi] [out] -main:17: error: No overload variant of "foo" of "Some" matches argument type "int" -main:17: note: Possible overload variants: -main:17: note: def foo(cls, int, /) -> Any -main:17: note: def foo(cls, float, /) -> Any -main:17: note: def foo(cls, a: str) -> Any +main:16: error: No overload variant of "foo" of "Some" matches argument type "int" +main:16: note: Possible overload variants: +main:16: note: def foo(cls, int, /) -> Any +main:16: note: def foo(cls, float, /) -> Any +main:16: note: def foo(cls, a: str) -> Any [case testUnpackWithDuplicateNamePositionalOnly] -# flags: --python-version 3.8 from typing_extensions import Unpack, TypedDict class Person(TypedDict): @@ -717,7 +689,7 @@ def foo(name: str, /, **kwargs: Unpack[Person]) -> None: # Allowed [builtins fixtures/dict.pyi] [case testPossiblyUndefinedWithAssignmentExpr] -# flags: --python-version 3.8 --enable-error-code possibly-undefined +# flags: --enable-error-code possibly-undefined def f1() -> None: d = {0: 1} if int(): @@ -744,7 +716,6 @@ main:9: note: Revealed type is "builtins.int" main:9: note: Revealed type is "builtins.str" [case testTypeGuardWithPositionalOnlyArg] -# flags: --python-version 3.8 from typing_extensions import TypeGuard def typeguard(x: object, /) -> TypeGuard[int]: @@ -755,10 +726,9 @@ if typeguard(n): reveal_type(n) [builtins fixtures/tuple.pyi] [out] -main:9: note: Revealed type is "builtins.int" +main:8: note: Revealed type is "builtins.int" [case testTypeGuardKeywordFollowingWalrus] -# flags: --python-version 3.8 from typing import cast from typing_extensions import TypeGuard @@ -769,7 +739,7 @@ if typeguard(x=(n := cast(object, "hi"))): reveal_type(n) [builtins fixtures/tuple.pyi] [out] -main:9: note: Revealed type is "builtins.int" +main:8: note: Revealed type is "builtins.int" [case testNoCrashOnAssignmentExprClass] class C: diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 625b82936e8c..adf4d210ed0c 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1764,7 +1764,6 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtin [builtins fixtures/dict.pyi] [case testCanCreateTypedDictWithTypingProper] -# flags: --python-version 3.8 from typing import TypedDict class Point(TypedDict): From ba90dc48fef38e48f0615f1f742a767fcde70fa9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 1 Feb 2024 18:44:31 -0800 Subject: [PATCH 0477/1617] Fix duplicated TypeVarTuple test (#16853) https://github.com/python/mypy/pull/15879#discussion_r1475245425 --- test-data/unit/check-generics.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 469cedb8f6f7..f4b7c14bd053 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3441,5 +3441,5 @@ def dec(f: Callable[[Unpack[Ts]], Foo[Unpack[Ts]]]) -> Callable[[Unpack[Ts]], Fo g: Callable[[T], Foo[int]] reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" h: Callable[[Unpack[Us]], Foo[int]] -reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" +reveal_type(dec(h)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" [builtins fixtures/list.pyi] From 67c396908f71e2d5d6aa942fc0b431b8772d4e45 Mon Sep 17 00:00:00 2001 From: jhance Date: Fri, 2 Feb 2024 06:07:40 -0800 Subject: [PATCH 0478/1617] Sync Typeshed (for 1.9 release) (#16844) Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/VERSIONS | 1 - mypy/typeshed/stdlib/_ast.pyi | 69 +- mypy/typeshed/stdlib/_codecs.pyi | 9 +- mypy/typeshed/stdlib/_collections_abc.pyi | 2 +- mypy/typeshed/stdlib/_csv.pyi | 4 +- mypy/typeshed/stdlib/_ctypes.pyi | 4 +- mypy/typeshed/stdlib/_curses.pyi | 18 +- mypy/typeshed/stdlib/_decimal.pyi | 4 +- mypy/typeshed/stdlib/_dummy_threading.pyi | 19 +- mypy/typeshed/stdlib/_heapq.pyi | 3 +- mypy/typeshed/stdlib/_json.pyi | 3 +- mypy/typeshed/stdlib/_operator.pyi | 4 +- mypy/typeshed/stdlib/_osx_support.pyi | 9 +- mypy/typeshed/stdlib/_posixsubprocess.pyi | 2 +- mypy/typeshed/stdlib/_sitebuiltins.pyi | 3 +- mypy/typeshed/stdlib/_socket.pyi | 677 ++++++++++-------- mypy/typeshed/stdlib/_stat.pyi | 4 +- mypy/typeshed/stdlib/_thread.pyi | 33 +- mypy/typeshed/stdlib/_tkinter.pyi | 37 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 24 +- mypy/typeshed/stdlib/_weakref.pyi | 4 +- mypy/typeshed/stdlib/_winapi.pyi | 75 +- mypy/typeshed/stdlib/abc.pyi | 4 +- mypy/typeshed/stdlib/aifc.pyi | 4 +- mypy/typeshed/stdlib/argparse.pyi | 7 +- mypy/typeshed/stdlib/array.pyi | 4 +- mypy/typeshed/stdlib/ast.pyi | 222 +++--- mypy/typeshed/stdlib/asyncio/__init__.pyi | 4 +- mypy/typeshed/stdlib/asyncio/base_events.pyi | 52 +- mypy/typeshed/stdlib/asyncio/base_futures.pyi | 3 +- mypy/typeshed/stdlib/asyncio/constants.pyi | 2 +- mypy/typeshed/stdlib/asyncio/coroutines.pyi | 2 - mypy/typeshed/stdlib/asyncio/events.pyi | 105 +-- mypy/typeshed/stdlib/asyncio/futures.pyi | 19 +- mypy/typeshed/stdlib/asyncio/locks.pyi | 9 +- .../stdlib/asyncio/proactor_events.pyi | 3 +- mypy/typeshed/stdlib/asyncio/runners.pyi | 9 +- mypy/typeshed/stdlib/asyncio/sslproto.pyi | 4 +- mypy/typeshed/stdlib/asyncio/streams.pyi | 58 +- mypy/typeshed/stdlib/asyncio/subprocess.pyi | 20 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 21 +- mypy/typeshed/stdlib/asyncio/timeouts.pyi | 3 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 46 +- .../stdlib/asyncio/windows_events.pyi | 3 +- .../typeshed/stdlib/asyncio/windows_utils.pyi | 4 +- mypy/typeshed/stdlib/bdb.pyi | 14 +- mypy/typeshed/stdlib/binascii.pyi | 12 +- mypy/typeshed/stdlib/binhex.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 360 ++++------ mypy/typeshed/stdlib/bz2.pyi | 4 +- mypy/typeshed/stdlib/cProfile.pyi | 6 +- mypy/typeshed/stdlib/calendar.pyi | 4 +- mypy/typeshed/stdlib/cgi.pyi | 12 - mypy/typeshed/stdlib/cgitb.pyi | 3 +- mypy/typeshed/stdlib/cmath.pyi | 11 +- mypy/typeshed/stdlib/cmd.pyi | 3 +- mypy/typeshed/stdlib/codecs.pyi | 19 +- mypy/typeshed/stdlib/collections/__init__.pyi | 16 +- .../stdlib/concurrent/futures/__init__.pyi | 6 +- .../stdlib/concurrent/futures/_base.pyi | 26 +- mypy/typeshed/stdlib/configparser.pyi | 4 +- mypy/typeshed/stdlib/contextvars.pyi | 4 +- mypy/typeshed/stdlib/csv.pyi | 20 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 76 +- mypy/typeshed/stdlib/ctypes/_endian.pyi | 19 + mypy/typeshed/stdlib/ctypes/wintypes.pyi | 140 ++-- mypy/typeshed/stdlib/dataclasses.pyi | 21 +- mypy/typeshed/stdlib/datetime.pyi | 74 +- mypy/typeshed/stdlib/dbm/__init__.pyi | 3 +- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 2 +- .../stdlib/distutils/cygwinccompiler.pyi | 2 +- mypy/typeshed/stdlib/distutils/filelist.pyi | 3 +- mypy/typeshed/stdlib/distutils/util.pyi | 8 +- .../stdlib/email/_header_value_parser.pyi | 38 +- mypy/typeshed/stdlib/email/headerregistry.pyi | 21 +- mypy/typeshed/stdlib/email/message.pyi | 4 +- mypy/typeshed/stdlib/enum.pyi | 4 +- mypy/typeshed/stdlib/fcntl.pyi | 17 +- mypy/typeshed/stdlib/filecmp.pyi | 3 +- mypy/typeshed/stdlib/fileinput.pyi | 115 +-- mypy/typeshed/stdlib/fractions.pyi | 34 +- mypy/typeshed/stdlib/ftplib.pyi | 4 +- mypy/typeshed/stdlib/functools.pyi | 82 +-- mypy/typeshed/stdlib/gc.pyi | 12 +- mypy/typeshed/stdlib/genericpath.pyi | 4 +- mypy/typeshed/stdlib/gettext.pyi | 26 +- mypy/typeshed/stdlib/grp.pyi | 3 +- mypy/typeshed/stdlib/gzip.pyi | 19 +- mypy/typeshed/stdlib/hashlib.pyi | 15 +- mypy/typeshed/stdlib/heapq.pyi | 3 +- mypy/typeshed/stdlib/hmac.pyi | 25 +- mypy/typeshed/stdlib/http/__init__.pyi | 5 +- mypy/typeshed/stdlib/http/cookiejar.pyi | 58 +- mypy/typeshed/stdlib/http/server.pyi | 2 +- mypy/typeshed/stdlib/imaplib.pyi | 4 +- mypy/typeshed/stdlib/importlib/abc.pyi | 3 +- mypy/typeshed/stdlib/importlib/machinery.pyi | 10 +- mypy/typeshed/stdlib/importlib/readers.pyi | 4 +- .../stdlib/importlib/resources/simple.pyi | 4 +- mypy/typeshed/stdlib/inspect.pyi | 77 +- mypy/typeshed/stdlib/io.pyi | 11 +- mypy/typeshed/stdlib/ipaddress.pyi | 4 +- mypy/typeshed/stdlib/itertools.pyi | 16 +- mypy/typeshed/stdlib/keyword.pyi | 2 +- mypy/typeshed/stdlib/lib2to3/fixer_base.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_apply.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_asserts.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_basestring.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_buffer.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_dict.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_except.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_exec.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_execfile.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_exitfunc.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_filter.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_funcattrs.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_future.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_getcwdu.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_has_key.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_idioms.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_import.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_imports.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_input.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_intern.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_isinstance.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_itertools.pyi | 3 +- .../lib2to3/fixes/fix_itertools_imports.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_long.pyi | 3 +- .../typeshed/stdlib/lib2to3/fixes/fix_map.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_metaclass.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_methodattrs.pyi | 3 +- mypy/typeshed/stdlib/lib2to3/fixes/fix_ne.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_next.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_nonzero.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_numliterals.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_operator.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_paren.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_print.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_raise.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_raw_input.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_reduce.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_reload.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_renames.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_repr.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_set_literal.pyi | 3 +- .../lib2to3/fixes/fix_standarderror.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_sys_exc.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_throw.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_tuple_params.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_types.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_unicode.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_urllib.pyi | 2 +- .../stdlib/lib2to3/fixes/fix_ws_comma.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_xrange.pyi | 3 +- .../stdlib/lib2to3/fixes/fix_xreadlines.pyi | 3 +- .../typeshed/stdlib/lib2to3/fixes/fix_zip.pyi | 3 +- mypy/typeshed/stdlib/lib2to3/main.pyi | 3 +- mypy/typeshed/stdlib/lib2to3/pygram.pyi | 5 +- mypy/typeshed/stdlib/lib2to3/pytree.pyi | 3 +- mypy/typeshed/stdlib/lib2to3/refactor.pyi | 3 +- mypy/typeshed/stdlib/logging/__init__.pyi | 622 +++++----------- mypy/typeshed/stdlib/logging/config.pyi | 17 +- mypy/typeshed/stdlib/lzma.pyi | 4 +- mypy/typeshed/stdlib/macpath.pyi | 104 --- mypy/typeshed/stdlib/mailbox.pyi | 4 +- mypy/typeshed/stdlib/math.pyi | 52 +- mypy/typeshed/stdlib/mimetypes.pyi | 14 +- mypy/typeshed/stdlib/mmap.pyi | 52 +- mypy/typeshed/stdlib/modulefinder.pyi | 24 +- mypy/typeshed/stdlib/msilib/__init__.pyi | 3 +- mypy/typeshed/stdlib/msvcrt.pyi | 2 +- .../stdlib/multiprocessing/__init__.pyi | 14 +- .../stdlib/multiprocessing/connection.pyi | 4 +- .../stdlib/multiprocessing/context.pyi | 17 +- .../stdlib/multiprocessing/dummy/__init__.pyi | 3 +- .../stdlib/multiprocessing/managers.pyi | 27 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 64 +- .../stdlib/multiprocessing/process.pyi | 10 +- .../stdlib/multiprocessing/reduction.pyi | 13 +- .../stdlib/multiprocessing/sharedctypes.pyi | 3 +- mypy/typeshed/stdlib/nntplib.pyi | 4 +- mypy/typeshed/stdlib/ntpath.pyi | 4 +- mypy/typeshed/stdlib/numbers.pyi | 81 +-- mypy/typeshed/stdlib/opcode.pyi | 8 +- mypy/typeshed/stdlib/os/__init__.pyi | 149 ++-- mypy/typeshed/stdlib/ossaudiodev.pyi | 3 +- mypy/typeshed/stdlib/parser.pyi | 3 +- mypy/typeshed/stdlib/pathlib.pyi | 20 +- mypy/typeshed/stdlib/pickle.pyi | 144 ++-- mypy/typeshed/stdlib/platform.pyi | 33 +- mypy/typeshed/stdlib/plistlib.pyi | 118 +-- mypy/typeshed/stdlib/poplib.pyi | 4 +- mypy/typeshed/stdlib/posix.pyi | 185 +++-- mypy/typeshed/stdlib/posixpath.pyi | 8 +- mypy/typeshed/stdlib/pprint.pyi | 37 +- mypy/typeshed/stdlib/pstats.pyi | 4 +- mypy/typeshed/stdlib/pty.pyi | 3 +- mypy/typeshed/stdlib/pwd.pyi | 3 +- mypy/typeshed/stdlib/py_compile.pyi | 30 +- mypy/typeshed/stdlib/pydoc.pyi | 4 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 4 +- mypy/typeshed/stdlib/re.pyi | 4 +- mypy/typeshed/stdlib/resource.pyi | 2 +- mypy/typeshed/stdlib/select.pyi | 4 +- mypy/typeshed/stdlib/shlex.pyi | 11 +- mypy/typeshed/stdlib/shutil.pyi | 63 +- mypy/typeshed/stdlib/signal.pyi | 12 +- mypy/typeshed/stdlib/socket.pyi | 192 +++-- mypy/typeshed/stdlib/spwd.pyi | 3 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 33 +- mypy/typeshed/stdlib/sre_parse.pyi | 50 +- mypy/typeshed/stdlib/ssl.pyi | 29 +- mypy/typeshed/stdlib/statistics.pyi | 105 ++- mypy/typeshed/stdlib/subprocess.pyi | 20 +- mypy/typeshed/stdlib/sunau.pyi | 4 +- mypy/typeshed/stdlib/symbol.pyi | 14 +- mypy/typeshed/stdlib/symtable.pyi | 15 +- mypy/typeshed/stdlib/sys/__init__.pyi | 41 +- mypy/typeshed/stdlib/sysconfig.pyi | 3 +- mypy/typeshed/stdlib/syslog.pyi | 3 +- mypy/typeshed/stdlib/tarfile.pyi | 120 ++-- mypy/typeshed/stdlib/tempfile.pyi | 450 ++++-------- mypy/typeshed/stdlib/threading.pyi | 27 +- mypy/typeshed/stdlib/time.pyi | 40 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 54 +- mypy/typeshed/stdlib/tkinter/constants.pyi | 2 +- mypy/typeshed/stdlib/tkinter/filedialog.pyi | 3 +- mypy/typeshed/stdlib/tkinter/font.pyi | 11 +- mypy/typeshed/stdlib/tkinter/tix.pyi | 3 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 11 +- mypy/typeshed/stdlib/token.pyi | 22 +- mypy/typeshed/stdlib/tokenize.pyi | 15 +- mypy/typeshed/stdlib/traceback.pyi | 7 +- mypy/typeshed/stdlib/tracemalloc.pyi | 4 +- mypy/typeshed/stdlib/types.pyi | 65 +- mypy/typeshed/stdlib/typing.pyi | 129 ++-- mypy/typeshed/stdlib/typing_extensions.pyi | 11 +- mypy/typeshed/stdlib/unicodedata.pyi | 13 +- mypy/typeshed/stdlib/unittest/__init__.pyi | 12 +- mypy/typeshed/stdlib/unittest/case.pyi | 19 +- mypy/typeshed/stdlib/unittest/mock.pyi | 173 ++--- mypy/typeshed/stdlib/urllib/parse.pyi | 4 +- mypy/typeshed/stdlib/urllib/robotparser.pyi | 4 +- mypy/typeshed/stdlib/warnings.pyi | 4 +- mypy/typeshed/stdlib/wave.pyi | 4 +- mypy/typeshed/stdlib/webbrowser.pyi | 2 +- mypy/typeshed/stdlib/winreg.pyi | 4 +- mypy/typeshed/stdlib/winsound.pyi | 3 +- mypy/typeshed/stdlib/xml/dom/minicompat.pyi | 3 +- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 4 +- mypy/typeshed/stdlib/xml/dom/pulldom.pyi | 3 +- mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi | 4 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 294 ++++---- mypy/typeshed/stdlib/xml/sax/__init__.pyi | 15 +- mypy/typeshed/stdlib/xmlrpc/client.pyi | 78 +- mypy/typeshed/stdlib/xxlimited.pyi | 3 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 35 +- mypy/typeshed/stdlib/zipfile/_path.pyi | 4 +- mypy/typeshed/stdlib/zipimport.pyi | 3 +- mypy/typeshed/stdlib/zlib.pyi | 2 +- test-data/unit/pythoneval.test | 40 +- 261 files changed, 2887 insertions(+), 4617 deletions(-) create mode 100644 mypy/typeshed/stdlib/ctypes/_endian.pyi delete mode 100644 mypy/typeshed/stdlib/macpath.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 5099a94c93bc..da395f797881 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -170,7 +170,6 @@ linecache: 3.0- locale: 3.0- logging: 3.0- lzma: 3.3- -macpath: 3.0-3.7 mailbox: 3.0- mailcap: 3.0-3.12 marshal: 3.0- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 0fbf66f7feda..fc3f035cc779 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,12 +1,10 @@ import sys import typing_extensions -from typing import Any, ClassVar -from typing_extensions import Literal +from typing import Any, ClassVar, Literal PyCF_ONLY_AST: Literal[1024] -if sys.version_info >= (3, 8): - PyCF_TYPE_COMMENTS: Literal[4096] - PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +PyCF_TYPE_COMMENTS: Literal[4096] +PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] _Identifier: typing_extensions.TypeAlias = str @@ -19,33 +17,29 @@ class AST: # TODO: Not all nodes have all of the following attributes lineno: int col_offset: int - if sys.version_info >= (3, 8): - end_lineno: int | None - end_col_offset: int | None - type_comment: str | None + end_lineno: int | None + end_col_offset: int | None + type_comment: str | None class mod(AST): ... +class type_ignore(AST): ... -if sys.version_info >= (3, 8): - class type_ignore(AST): ... - - class TypeIgnore(type_ignore): - if sys.version_info >= (3, 10): - __match_args__ = ("lineno", "tag") - tag: str +class TypeIgnore(type_ignore): + if sys.version_info >= (3, 10): + __match_args__ = ("lineno", "tag") + tag: str - class FunctionType(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("argtypes", "returns") - argtypes: list[expr] - returns: expr +class FunctionType(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("argtypes", "returns") + argtypes: list[expr] + returns: expr class Module(mod): if sys.version_info >= (3, 10): __match_args__ = ("body", "type_ignores") body: list[stmt] - if sys.version_info >= (3, 8): - type_ignores: list[TypeIgnore] + type_ignores: list[TypeIgnore] class Interactive(mod): if sys.version_info >= (3, 10): @@ -340,21 +334,6 @@ class JoinedStr(expr): __match_args__ = ("values",) values: list[expr] -if sys.version_info < (3, 8): - class Num(expr): # Deprecated in 3.8; use Constant - n: int | float | complex - - class Str(expr): # Deprecated in 3.8; use Constant - s: str - - class Bytes(expr): # Deprecated in 3.8; use Constant - s: bytes - - class NameConstant(expr): # Deprecated in 3.8; use Constant - value: Any - - class Ellipsis(expr): ... # Deprecated in 3.8; use Constant - class Constant(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "kind") @@ -364,12 +343,11 @@ class Constant(expr): s: Any n: int | float | complex -if sys.version_info >= (3, 8): - class NamedExpr(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "value") - target: Name - value: expr +class NamedExpr(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "value") + target: Name + value: expr class Attribute(expr): if sys.version_info >= (3, 10): @@ -498,8 +476,7 @@ class ExceptHandler(excepthandler): class arguments(AST): if sys.version_info >= (3, 10): __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") - if sys.version_info >= (3, 8): - posonlyargs: list[arg] + posonlyargs: list[arg] args: list[arg] vararg: arg | None kwonlyargs: list[arg] diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index f8141d8bad4b..6de4666e0776 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -2,8 +2,8 @@ import codecs import sys from _typeshed import ReadableBuffer from collections.abc import Callable -from typing import overload -from typing_extensions import Literal, TypeAlias +from typing import Literal, overload +from typing_extensions import TypeAlias # This type is not exposed; it is defined in unicodeobject.c class _EncodingMap: @@ -99,11 +99,6 @@ else: def unicode_escape_decode(__data: str | ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... def unicode_escape_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... - -if sys.version_info < (3, 8): - def unicode_internal_decode(__obj: str | ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... - def unicode_internal_encode(__obj: str | ReadableBuffer, __errors: str | None = None) -> tuple[bytes, int]: ... - def utf_16_be_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... def utf_16_be_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... def utf_16_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 8520e9e4ed9b..0aa09967a895 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -30,9 +30,9 @@ from typing import ( # noqa: Y022,Y038,Y057 Sized as Sized, TypeVar, ValuesView as ValuesView, + final, runtime_checkable, ) -from typing_extensions import final __all__ = [ "Awaitable", diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 19ea487e1530..19f2dc9664b1 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import SupportsWrite from collections.abc import Iterable, Iterator -from typing import Any -from typing_extensions import Final, Literal, TypeAlias +from typing import Any, Final, Literal +from typing_extensions import TypeAlias __version__: Final[str] diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 0fa041844028..ec3d86e41687 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from abc import abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from ctypes import CDLL +from ctypes import CDLL, ArgumentError as ArgumentError from typing import Any, ClassVar, Generic, TypeVar, overload from typing_extensions import Self, TypeAlias @@ -197,8 +197,6 @@ class Array(_CData, Generic[_CT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... -class ArgumentError(Exception): ... - def addressof(obj: _CData) -> int: ... def alignment(obj_or_type: _CData | type[_CData]) -> int: ... def get_errno() -> int: ... diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 3604f7abedb5..adb09a50f47c 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadOnlyBuffer, SupportsRead -from typing import IO, Any, NamedTuple, overload -from typing_extensions import TypeAlias, final +from typing import IO, Any, NamedTuple, final, overload +from typing_extensions import TypeAlias if sys.platform != "win32": # Handled by PyCurses_ConvertToChtype in _cursesmodule.c. @@ -548,10 +548,10 @@ if sys.platform != "win32": def vline(self, ch: _ChType, n: int) -> None: ... @overload def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... - if sys.version_info >= (3, 8): - class _ncurses_version(NamedTuple): - major: int - minor: int - patch: int - ncurses_version: _ncurses_version - window = _CursesWindow # undocumented + + class _ncurses_version(NamedTuple): + major: int + minor: int + patch: int + ncurses_version: _ncurses_version + window = _CursesWindow # undocumented diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 9a90760bd2c2..369d04cd2d5d 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -2,8 +2,8 @@ import numbers import sys from collections.abc import Container, Sequence from types import TracebackType -from typing import Any, ClassVar, NamedTuple, overload -from typing_extensions import Final, Literal, Self, TypeAlias +from typing import Any, ClassVar, Final, Literal, NamedTuple, overload +from typing_extensions import Self, TypeAlias _Decimal: TypeAlias = Decimal | int _DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int] diff --git a/mypy/typeshed/stdlib/_dummy_threading.pyi b/mypy/typeshed/stdlib/_dummy_threading.pyi index abcf3a13a496..21d1d1921c0e 100644 --- a/mypy/typeshed/stdlib/_dummy_threading.pyi +++ b/mypy/typeshed/stdlib/_dummy_threading.pyi @@ -1,4 +1,5 @@ import sys +from _thread import _excepthook, _ExceptHookArgs from _typeshed import ProfileFunction, TraceFunction from collections.abc import Callable, Iterable, Mapping from types import TracebackType @@ -28,11 +29,10 @@ __all__ = [ "settrace", "local", "stack_size", + "ExceptHookArgs", + "excepthook", ] -if sys.version_info >= (3, 8): - __all__ += ["ExceptHookArgs", "excepthook"] - def active_count() -> int: ... def current_thread() -> Thread: ... def currentThread() -> Thread: ... @@ -72,10 +72,8 @@ class Thread: def join(self, timeout: float | None = None) -> None: ... def getName(self) -> str: ... def setName(self, name: str) -> None: ... - if sys.version_info >= (3, 8): - @property - def native_id(self) -> int | None: ... # only available on some platforms - + @property + def native_id(self) -> int | None: ... # only available on some platforms def is_alive(self) -> bool: ... if sys.version_info < (3, 9): def isAlive(self) -> bool: ... @@ -138,11 +136,8 @@ class Event: def clear(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... -if sys.version_info >= (3, 8): - from _thread import _excepthook, _ExceptHookArgs - - excepthook = _excepthook - ExceptHookArgs = _ExceptHookArgs +excepthook = _excepthook +ExceptHookArgs = _ExceptHookArgs class Timer(Thread): def __init__( diff --git a/mypy/typeshed/stdlib/_heapq.pyi b/mypy/typeshed/stdlib/_heapq.pyi index 8d6c3e88103e..28b03a75d4c7 100644 --- a/mypy/typeshed/stdlib/_heapq.pyi +++ b/mypy/typeshed/stdlib/_heapq.pyi @@ -1,5 +1,4 @@ -from typing import Any, TypeVar -from typing_extensions import Final +from typing import Any, Final, TypeVar _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index 130f7ab92e97..a6a62be184d8 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -1,6 +1,5 @@ from collections.abc import Callable -from typing import Any -from typing_extensions import final +from typing import Any, final @final class make_encoder: diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 26e69f130728..acc4a6fb59ca 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import SupportsGetItem from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence -from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, TypeVar, overload -from typing_extensions import ParamSpec, SupportsIndex, TypeAlias, TypeVarTuple, Unpack, final +from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload +from typing_extensions import ParamSpec, TypeAlias, TypeVarTuple, Unpack _R = TypeVar("_R") _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/_osx_support.pyi b/mypy/typeshed/stdlib/_osx_support.pyi index 3eb6f4ddc67c..64dbdd24fd40 100644 --- a/mypy/typeshed/stdlib/_osx_support.pyi +++ b/mypy/typeshed/stdlib/_osx_support.pyi @@ -1,4 +1,3 @@ -import sys from collections.abc import Iterable, Sequence from typing import TypeVar @@ -13,13 +12,7 @@ _COMPILER_CONFIG_VARS: tuple[str, ...] # undocumented _INITPRE: str # undocumented def _find_executable(executable: str, path: str | None = None) -> str | None: ... # undocumented - -if sys.version_info >= (3, 8): - def _read_output(commandstring: str, capture_stderr: bool = False) -> str | None: ... # undocumented - -else: - def _read_output(commandstring: str) -> str | None: ... # undocumented - +def _read_output(commandstring: str, capture_stderr: bool = False) -> str | None: ... # undocumented def _find_build_tool(toolname: str) -> str: ... # undocumented _SYSTEM_VERSION: str | None # undocumented diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 1708063720ba..6c1782433e45 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Callable, Sequence -from typing_extensions import SupportsIndex +from typing import SupportsIndex if sys.platform != "win32": def cloexec_pipe() -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/_sitebuiltins.pyi b/mypy/typeshed/stdlib/_sitebuiltins.pyi index 94ad701d4a73..49e88a196825 100644 --- a/mypy/typeshed/stdlib/_sitebuiltins.pyi +++ b/mypy/typeshed/stdlib/_sitebuiltins.pyi @@ -1,6 +1,5 @@ from collections.abc import Iterable -from typing import ClassVar, NoReturn -from typing_extensions import Literal +from typing import ClassVar, Literal, NoReturn class Quitter: name: str diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 7a0ede62838c..6471cae2e72d 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -1,16 +1,9 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Iterable -from typing import Any, SupportsInt, overload +from typing import Any, SupportsIndex, overload from typing_extensions import TypeAlias -if sys.version_info >= (3, 8): - from typing import SupportsIndex - - _FD: TypeAlias = SupportsIndex -else: - _FD: TypeAlias = SupportsInt - _CMSG: TypeAlias = tuple[int, int, bytes] _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] @@ -20,19 +13,18 @@ _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] _Address: TypeAlias = tuple[Any, ...] | str | ReadableBuffer _RetAddress: TypeAlias = Any -# ----- Constants ----- -# Some socket families are listed in the "Socket families" section of the docs, -# but not the "Constants" section. These are listed at the end of the list of -# constants. -# -# Besides those and the first few constants listed, the constants are listed in -# documentation order. +# ===== Constants ===== +# This matches the order in the CPython documentation +# https://docs.python.org/3/library/socket.html#constants -has_ipv6: bool +if sys.platform != "win32": + AF_UNIX: int AF_INET: int AF_INET6: int +AF_UNSPEC: int + SOCK_STREAM: int SOCK_DGRAM: int SOCK_RAW: int @@ -40,162 +32,29 @@ SOCK_RDM: int SOCK_SEQPACKET: int if sys.platform == "linux": + # Availability: Linux >= 2.6.27 SOCK_CLOEXEC: int SOCK_NONBLOCK: int -# Address families not mentioned in the docs -AF_APPLETALK: int -AF_DECnet: int -AF_IPX: int -AF_SNA: int -AF_UNSPEC: int - -if sys.platform != "win32": - AF_ROUTE: int - AF_SYSTEM: int - AF_UNIX: int - -if sys.platform != "darwin": - AF_IRDA: int - -if sys.platform != "darwin" and sys.platform != "win32": - AF_AAL5: int - AF_ASH: int - AF_ATMPVC: int - AF_ATMSVC: int - AF_AX25: int - AF_BRIDGE: int - AF_ECONET: int - AF_KEY: int - AF_LLC: int - AF_NETBEUI: int - AF_NETROM: int - AF_PPPOX: int - AF_ROSE: int - AF_SECURITY: int - AF_WANPIPE: int - AF_X25: int +# -------------------- +# Many constants of these forms, documented in the Unix documentation on +# sockets and/or the IP protocol, are also defined in the socket module. +# SO_* +# socket.SOMAXCONN +# MSG_* +# SOL_* +# SCM_* +# IPPROTO_* +# IPPORT_* +# INADDR_* +# IP_* +# IPV6_* +# EAI_* +# AI_* +# NI_* +# TCP_* +# -------------------- -# The "many constants" referenced by the docs -SOMAXCONN: int -AI_ADDRCONFIG: int -AI_ALL: int -AI_CANONNAME: int -AI_NUMERICHOST: int -AI_NUMERICSERV: int -AI_PASSIVE: int -AI_V4MAPPED: int -EAI_AGAIN: int -EAI_BADFLAGS: int -EAI_FAIL: int -EAI_FAMILY: int -EAI_MEMORY: int -EAI_NODATA: int -EAI_NONAME: int -EAI_SERVICE: int -EAI_SOCKTYPE: int -INADDR_ALLHOSTS_GROUP: int -INADDR_ANY: int -INADDR_BROADCAST: int -INADDR_LOOPBACK: int -INADDR_MAX_LOCAL_GROUP: int -INADDR_NONE: int -INADDR_UNSPEC_GROUP: int -IPPORT_RESERVED: int -IPPORT_USERRESERVED: int - -if sys.platform != "win32" or sys.version_info >= (3, 8): - IPPROTO_AH: int - IPPROTO_DSTOPTS: int - IPPROTO_EGP: int - IPPROTO_ESP: int - IPPROTO_FRAGMENT: int - IPPROTO_GGP: int - IPPROTO_HOPOPTS: int - IPPROTO_ICMPV6: int - IPPROTO_IDP: int - IPPROTO_IGMP: int - IPPROTO_IPV4: int - IPPROTO_IPV6: int - IPPROTO_MAX: int - IPPROTO_ND: int - IPPROTO_NONE: int - IPPROTO_PIM: int - IPPROTO_PUP: int - IPPROTO_ROUTING: int - IPPROTO_SCTP: int - - if sys.platform != "darwin": - IPPROTO_CBT: int - IPPROTO_ICLFXBM: int - IPPROTO_IGP: int - IPPROTO_L2TP: int - IPPROTO_PGM: int - IPPROTO_RDP: int - IPPROTO_ST: int - -IPPROTO_ICMP: int -IPPROTO_IP: int -IPPROTO_RAW: int -IPPROTO_TCP: int -IPPROTO_UDP: int -IPV6_CHECKSUM: int -IPV6_JOIN_GROUP: int -IPV6_LEAVE_GROUP: int -IPV6_MULTICAST_HOPS: int -IPV6_MULTICAST_IF: int -IPV6_MULTICAST_LOOP: int -IPV6_RECVTCLASS: int -IPV6_TCLASS: int -IPV6_UNICAST_HOPS: int -IPV6_V6ONLY: int - -if sys.platform != "darwin" or sys.version_info >= (3, 9): - IPV6_DONTFRAG: int - IPV6_HOPLIMIT: int - IPV6_HOPOPTS: int - IPV6_PKTINFO: int - IPV6_RECVRTHDR: int - IPV6_RTHDR: int - -IP_ADD_MEMBERSHIP: int -IP_DROP_MEMBERSHIP: int -IP_HDRINCL: int -IP_MULTICAST_IF: int -IP_MULTICAST_LOOP: int -IP_MULTICAST_TTL: int -IP_OPTIONS: int -IP_RECVDSTADDR: int -if sys.version_info >= (3, 10): - IP_RECVTOS: int -elif sys.platform != "win32" and sys.platform != "darwin": - IP_RECVTOS: int -IP_TOS: int -IP_TTL: int -MSG_CTRUNC: int -MSG_DONTROUTE: int - -if sys.platform != "darwin": - MSG_ERRQUEUE: int - -MSG_OOB: int -MSG_PEEK: int -MSG_TRUNC: int -MSG_WAITALL: int -NI_DGRAM: int -NI_MAXHOST: int -NI_MAXSERV: int -NI_NAMEREQD: int -NI_NOFQDN: int -NI_NUMERICHOST: int -NI_NUMERICSERV: int -SHUT_RD: int -SHUT_RDWR: int -SHUT_WR: int -SOL_IP: int -SOL_SOCKET: int -SOL_TCP: int -SOL_UDP: int SO_ACCEPTCONN: int SO_BROADCAST: int SO_DEBUG: int @@ -213,39 +72,99 @@ SO_SNDLOWAT: int SO_SNDTIMEO: int SO_TYPE: int SO_USELOOPBACK: int -if sys.platform == "linux" and sys.version_info >= (3, 11): - SO_INCOMING_CPU: int -TCP_FASTOPEN: int -TCP_KEEPCNT: int -TCP_KEEPINTVL: int +if sys.platform == "win32": + SO_EXCLUSIVEADDRUSE: int +if sys.platform != "win32": + SO_REUSEPORT: int +if sys.platform != "win32" and sys.platform != "darwin": + SO_BINDTODEVICE: int + SO_DOMAIN: int + SO_MARK: int + SO_PASSCRED: int + SO_PASSSEC: int + SO_PEERCRED: int + SO_PEERSEC: int + SO_PRIORITY: int + SO_PROTOCOL: int + SO_SETFIB: int -if sys.platform != "darwin": - TCP_KEEPIDLE: int +SOMAXCONN: int -TCP_MAXSEG: int -TCP_NODELAY: int +MSG_CTRUNC: int +MSG_DONTROUTE: int +MSG_OOB: int +MSG_PEEK: int +MSG_TRUNC: int +MSG_WAITALL: int if sys.platform != "win32": - TCP_NOTSENT_LOWAT: int -if sys.version_info >= (3, 10) and sys.platform == "darwin": - TCP_KEEPALIVE: int -if sys.version_info >= (3, 11) and sys.platform == "darwin": - TCP_CONNECTION_INFO: int - + MSG_DONTWAIT: int + MSG_EOF: int + MSG_EOR: int + MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not if sys.platform != "darwin": MSG_BCAST: int + MSG_ERRQUEUE: int MSG_MCAST: int - SO_EXCLUSIVEADDRUSE: int +if sys.platform != "win32" and sys.platform != "darwin": + MSG_BTAG: int + MSG_CMSG_CLOEXEC: int + MSG_CONFIRM: int + MSG_ETAG: int + MSG_FASTOPEN: int + MSG_MORE: int + MSG_NOTIFICATION: int + +SOL_IP: int +SOL_SOCKET: int +SOL_TCP: int +SOL_UDP: int +if sys.platform != "win32" and sys.platform != "darwin": + SOL_ATALK: int + SOL_AX25: int + SOL_HCI: int + SOL_IPX: int + SOL_NETROM: int + SOL_ROSE: int if sys.platform != "win32": - AI_DEFAULT: int - AI_MASK: int - AI_V4MAPPED_CFG: int - EAI_ADDRFAMILY: int - EAI_BADHINTS: int - EAI_MAX: int - EAI_OVERFLOW: int - EAI_PROTOCOL: int - EAI_SYSTEM: int + SCM_CREDS: int + SCM_RIGHTS: int +if sys.platform != "win32" and sys.platform != "darwin": + SCM_CREDENTIALS: int + +IPPROTO_ICMP: int +IPPROTO_IP: int +IPPROTO_RAW: int +IPPROTO_TCP: int +IPPROTO_UDP: int +IPPROTO_AH: int +IPPROTO_DSTOPTS: int +IPPROTO_EGP: int +IPPROTO_ESP: int +IPPROTO_FRAGMENT: int +IPPROTO_GGP: int +IPPROTO_HOPOPTS: int +IPPROTO_ICMPV6: int +IPPROTO_IDP: int +IPPROTO_IGMP: int +IPPROTO_IPV4: int +IPPROTO_IPV6: int +IPPROTO_MAX: int +IPPROTO_ND: int +IPPROTO_NONE: int +IPPROTO_PIM: int +IPPROTO_PUP: int +IPPROTO_ROUTING: int +IPPROTO_SCTP: int +if sys.platform != "darwin": + IPPROTO_CBT: int + IPPROTO_ICLFXBM: int + IPPROTO_IGP: int + IPPROTO_L2TP: int + IPPROTO_PGM: int + IPPROTO_RDP: int + IPPROTO_ST: int +if sys.platform != "win32": IPPROTO_EON: int IPPROTO_GRE: int IPPROTO_HELLO: int @@ -254,24 +173,78 @@ if sys.platform != "win32": IPPROTO_RSVP: int IPPROTO_TP: int IPPROTO_XTP: int - IPV6_RTHDR_TYPE_0: int +if sys.platform != "win32" and sys.platform != "darwin": + IPPROTO_BIP: int + IPPROTO_MOBILE: int + IPPROTO_VRRP: int +if sys.version_info >= (3, 9) and sys.platform == "linux": + # Availability: Linux >= 2.6.20, FreeBSD >= 10.1 + IPPROTO_UDPLITE: int +if sys.version_info >= (3, 10) and sys.platform == "linux": + IPPROTO_MPTCP: int + +IPPORT_RESERVED: int +IPPORT_USERRESERVED: int + +INADDR_ALLHOSTS_GROUP: int +INADDR_ANY: int +INADDR_BROADCAST: int +INADDR_LOOPBACK: int +INADDR_MAX_LOCAL_GROUP: int +INADDR_NONE: int +INADDR_UNSPEC_GROUP: int + +IP_ADD_MEMBERSHIP: int +IP_DROP_MEMBERSHIP: int +IP_HDRINCL: int +IP_MULTICAST_IF: int +IP_MULTICAST_LOOP: int +IP_MULTICAST_TTL: int +IP_OPTIONS: int +IP_RECVDSTADDR: int +if sys.version_info >= (3, 10): + IP_RECVTOS: int +elif sys.platform != "win32" and sys.platform != "darwin": + IP_RECVTOS: int +IP_TOS: int +IP_TTL: int +if sys.platform != "win32": IP_DEFAULT_MULTICAST_LOOP: int IP_DEFAULT_MULTICAST_TTL: int IP_MAX_MEMBERSHIPS: int IP_RECVOPTS: int IP_RECVRETOPTS: int IP_RETOPTS: int - LOCAL_PEERCRED: int - MSG_DONTWAIT: int - MSG_EOF: int - MSG_EOR: int - MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not - SCM_CREDS: int - SCM_RIGHTS: int - SO_REUSEPORT: int +if sys.platform != "win32" and sys.platform != "darwin": + IP_TRANSPARENT: int + IP_BIND_ADDRESS_NO_PORT: int +if sys.version_info >= (3, 12): + IP_ADD_SOURCE_MEMBERSHIP: int + IP_BLOCK_SOURCE: int + IP_DROP_SOURCE_MEMBERSHIP: int + IP_PKTINFO: int + IP_UNBLOCK_SOURCE: int +IPV6_CHECKSUM: int +IPV6_JOIN_GROUP: int +IPV6_LEAVE_GROUP: int +IPV6_MULTICAST_HOPS: int +IPV6_MULTICAST_IF: int +IPV6_MULTICAST_LOOP: int +IPV6_RECVTCLASS: int +IPV6_TCLASS: int +IPV6_UNICAST_HOPS: int +IPV6_V6ONLY: int +if sys.version_info >= (3, 9) or sys.platform != "darwin": + IPV6_DONTFRAG: int + IPV6_HOPLIMIT: int + IPV6_HOPOPTS: int + IPV6_PKTINFO: int + IPV6_RECVRTHDR: int + IPV6_RTHDR: int if sys.platform != "win32": - if sys.platform != "darwin" or sys.version_info >= (3, 9): + IPV6_RTHDR_TYPE_0: int + if sys.version_info >= (3, 9) or sys.platform != "darwin": IPV6_DSTOPTS: int IPV6_NEXTHOP: int IPV6_PATHMTU: int @@ -283,43 +256,74 @@ if sys.platform != "win32": IPV6_RTHDRDSTOPTS: int IPV6_USE_MIN_MTU: int +EAI_AGAIN: int +EAI_BADFLAGS: int +EAI_FAIL: int +EAI_FAMILY: int +EAI_MEMORY: int +EAI_NODATA: int +EAI_NONAME: int +EAI_SERVICE: int +EAI_SOCKTYPE: int +if sys.platform != "win32": + EAI_ADDRFAMILY: int + EAI_BADHINTS: int + EAI_MAX: int + EAI_OVERFLOW: int + EAI_PROTOCOL: int + EAI_SYSTEM: int + +AI_ADDRCONFIG: int +AI_ALL: int +AI_CANONNAME: int +AI_NUMERICHOST: int +AI_NUMERICSERV: int +AI_PASSIVE: int +AI_V4MAPPED: int +if sys.platform != "win32": + AI_DEFAULT: int + AI_MASK: int + AI_V4MAPPED_CFG: int + +NI_DGRAM: int +NI_MAXHOST: int +NI_MAXSERV: int +NI_NAMEREQD: int +NI_NOFQDN: int +NI_NUMERICHOST: int +NI_NUMERICSERV: int + +TCP_FASTOPEN: int +TCP_KEEPCNT: int +TCP_KEEPINTVL: int +TCP_MAXSEG: int +TCP_NODELAY: int +if sys.platform != "win32": + TCP_NOTSENT_LOWAT: int +if sys.platform != "darwin": + TCP_KEEPIDLE: int +if sys.version_info >= (3, 10) and sys.platform == "darwin": + TCP_KEEPALIVE: int +if sys.version_info >= (3, 11) and sys.platform == "darwin": + TCP_CONNECTION_INFO: int + if sys.platform != "win32" and sys.platform != "darwin": - IPPROTO_BIP: int - IPPROTO_MOBILE: int - IPPROTO_VRRP: int - IPX_TYPE: int - IP_TRANSPARENT: int - MSG_BTAG: int - MSG_CMSG_CLOEXEC: int - MSG_CONFIRM: int - MSG_ETAG: int - MSG_FASTOPEN: int - MSG_MORE: int - MSG_NOTIFICATION: int - SCM_CREDENTIALS: int - SOL_ATALK: int - SOL_AX25: int - SOL_HCI: int - SOL_IPX: int - SOL_NETROM: int - SOL_ROSE: int - SO_BINDTODEVICE: int - SO_MARK: int - SO_PASSCRED: int - SO_PEERCRED: int - SO_PRIORITY: int - SO_SETFIB: int + TCP_CONGESTION: int TCP_CORK: int TCP_DEFER_ACCEPT: int TCP_INFO: int TCP_LINGER2: int TCP_QUICKACK: int TCP_SYNCNT: int + TCP_USER_TIMEOUT: int TCP_WINDOW_CLAMP: int -# Specifically-documented constants +# -------------------- +# Specifically documented constants +# -------------------- if sys.platform == "linux": + # Availability: Linux >= 2.6.25, NetBSD >= 8 AF_CAN: int PF_CAN: int SOL_CAN_BASE: int @@ -336,6 +340,8 @@ if sys.platform == "linux": CAN_RTR_FLAG: int CAN_SFF_MASK: int +if sys.platform == "linux": + # Availability: Linux >= 2.6.25 CAN_BCM: int CAN_BCM_TX_SETUP: int CAN_BCM_TX_DELETE: int @@ -349,10 +355,6 @@ if sys.platform == "linux": CAN_BCM_RX_STATUS: int CAN_BCM_RX_TIMEOUT: int CAN_BCM_RX_CHANGED: int - - CAN_RAW_FD_FRAMES: int - -if sys.platform == "linux" and sys.version_info >= (3, 8): CAN_BCM_SETTIMER: int CAN_BCM_STARTTIMER: int CAN_BCM_TX_COUNTEVT: int @@ -367,11 +369,20 @@ if sys.platform == "linux" and sys.version_info >= (3, 8): CAN_BCM_CAN_FD_FRAME: int if sys.platform == "linux": + # Availability: Linux >= 3.6 + CAN_RAW_FD_FRAMES: int + +if sys.platform == "linux" and sys.version_info >= (3, 9): + # Availability: Linux >= 4.1 + CAN_RAW_JOIN_FILTERS: int + +if sys.platform == "linux": + # Availability: Linux >= 2.6.25 CAN_ISOTP: int if sys.platform == "linux" and sys.version_info >= (3, 9): + # Availability: Linux >= 5.4 CAN_J1939: int - CAN_RAW_JOIN_FILTERS: int J1939_MAX_UNICAST_ADDR: int J1939_IDLE_ADDR: int @@ -396,16 +407,17 @@ if sys.platform == "linux" and sys.version_info >= (3, 9): J1939_NLA_PAD: int J1939_NLA_BYTES_ACKED: int - J1939_EE_INFO_NONE: int J1939_EE_INFO_TX_ABORT: int - J1939_FILTER_MAX: int -if sys.platform == "linux" and sys.version_info >= (3, 10): - IPPROTO_MPTCP: int +if sys.version_info >= (3, 12) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": + # Availability: FreeBSD >= 14.0 + AF_DIVERT: int + PF_DIVERT: int if sys.platform == "linux": + # Availability: Linux >= 2.2 AF_PACKET: int PF_PACKET: int PACKET_BROADCAST: int @@ -416,7 +428,11 @@ if sys.platform == "linux": PACKET_OTHERHOST: int PACKET_OUTGOING: int +if sys.version_info >= (3, 12) and sys.platform == "linux": + ETH_P_ALL: int + if sys.platform == "linux": + # Availability: Linux >= 2.6.30 AF_RDS: int PF_RDS: int SOL_RDS: int @@ -476,6 +492,7 @@ if sys.platform == "linux": TIPC_ZONE_SCOPE: int if sys.platform == "linux": + # Availability: Linux >= 2.6.38 AF_ALG: int SOL_ALG: int ALG_OP_DECRYPT: int @@ -490,6 +507,7 @@ if sys.platform == "linux": ALG_SET_PUBKEY: int if sys.platform == "linux": + # Availability: Linux >= 4.8 (or maybe 3.9, CPython docs are confusing) AF_VSOCK: int IOCTL_VM_SOCKETS_GET_LOCAL_CID: int VMADDR_CID_ANY: int @@ -498,26 +516,65 @@ if sys.platform == "linux": SO_VM_SOCKETS_BUFFER_MAX_SIZE: int SO_VM_SOCKETS_BUFFER_SIZE: int SO_VM_SOCKETS_BUFFER_MIN_SIZE: int - VM_SOCKETS_INVALID_VERSION: int + VM_SOCKETS_INVALID_VERSION: int # undocumented if sys.platform != "win32" or sys.version_info >= (3, 9): + # Documented as only available on BSD, macOS, but empirically sometimes + # available on Windows AF_LINK: int -# BDADDR_* and HCI_* listed with other bluetooth constants below +has_ipv6: bool + +if sys.platform != "darwin": + if sys.platform != "win32" or sys.version_info >= (3, 9): + BDADDR_ANY: str + BDADDR_LOCAL: str if sys.platform != "win32" and sys.platform != "darwin": - SO_DOMAIN: int - SO_PASSSEC: int - SO_PEERSEC: int - SO_PROTOCOL: int - TCP_CONGESTION: int - TCP_USER_TIMEOUT: int + HCI_FILTER: int # not in NetBSD or DragonFlyBSD + HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD + HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD + +if sys.platform == "linux": + AF_QIPCRTR: int # Availability: Linux >= 4.7 + +if sys.version_info >= (3, 11) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": + # FreeBSD + SCM_CREDS2: int + LOCAL_CREDS: int + LOCAL_CREDS_PERSISTENT: int + +if sys.version_info >= (3, 11) and sys.platform == "linux": + SO_INCOMING_CPU: int # Availability: Linux >= 3.9 + +if sys.version_info >= (3, 12) and sys.platform == "win32": + # Availability: Windows + AF_HYPERV: int + HV_PROTOCOL_RAW: int + HVSOCKET_CONNECT_TIMEOUT: int + HVSOCKET_CONNECT_TIMEOUT_MAX: int + HVSOCKET_CONNECTED_SUSPEND: int + HVSOCKET_ADDRESS_FLAG_PASSTHRU: int + HV_GUID_ZERO: str + HV_GUID_WILDCARD: str + HV_GUID_BROADCAST: str + HV_GUID_CHILDREN: str + HV_GUID_LOOPBACK: str + HV_GUID_PARENT: str -if sys.platform == "linux" and sys.version_info >= (3, 8): - AF_QIPCRTR: int +if sys.version_info >= (3, 12): + if sys.platform != "win32": + # Availability: Linux, FreeBSD, macOS + ETHERTYPE_ARP: int + ETHERTYPE_IP: int + ETHERTYPE_IPV6: int + ETHERTYPE_VLAN: int +# -------------------- # Semi-documented constants -# (Listed under "Socket families" in the docs, but not "Constants") +# These are alluded to under the "Socket families" section in the docs +# https://docs.python.org/3/library/socket.html#socket-families +# -------------------- if sys.platform == "linux": # Netlink is defined by Linux @@ -537,12 +594,13 @@ if sys.platform == "linux": NETLINK_W1: int NETLINK_XFRM: int +if sys.platform == "darwin": + PF_SYSTEM: int + SYSPROTO_CONTROL: int + if sys.platform != "darwin": - if sys.platform != "win32" or sys.version_info >= (3, 9): + if sys.version_info >= (3, 9) or sys.platform != "win32": AF_BLUETOOTH: int - BDADDR_ANY: str - BDADDR_LOCAL: str - BTPROTO_RFCOMM: int if sys.platform != "win32" and sys.platform != "darwin": # Linux and some BSD support is explicit in the docs @@ -550,17 +608,65 @@ if sys.platform != "win32" and sys.platform != "darwin": BTPROTO_HCI: int BTPROTO_L2CAP: int BTPROTO_SCO: int # not in FreeBSD - HCI_FILTER: int # not in NetBSD or DragonFlyBSD - # not in FreeBSD, NetBSD, or DragonFlyBSD - HCI_TIME_STAMP: int - HCI_DATA_DIR: int +if sys.platform != "darwin": + if sys.version_info >= (3, 9) or sys.platform != "win32": + BTPROTO_RFCOMM: int -if sys.platform == "darwin": - # PF_SYSTEM is defined by macOS - PF_SYSTEM: int - SYSPROTO_CONTROL: int +if sys.version_info >= (3, 9) and sys.platform == "linux": + UDPLITE_RECV_CSCOV: int + UDPLITE_SEND_CSCOV: int + +# -------------------- +# Documented under socket.shutdown +# -------------------- +SHUT_RD: int +SHUT_RDWR: int +SHUT_WR: int -# ----- Exceptions ----- +# -------------------- +# Undocumented constants +# -------------------- + +# Undocumented address families +AF_APPLETALK: int +AF_DECnet: int +AF_IPX: int +AF_SNA: int + +if sys.platform != "win32": + AF_ROUTE: int + AF_SYSTEM: int + +if sys.platform != "darwin": + AF_IRDA: int + +if sys.platform != "win32" and sys.platform != "darwin": + AF_AAL5: int + AF_ASH: int + AF_ATMPVC: int + AF_ATMSVC: int + AF_AX25: int + AF_BRIDGE: int + AF_ECONET: int + AF_KEY: int + AF_LLC: int + AF_NETBEUI: int + AF_NETROM: int + AF_PPPOX: int + AF_ROSE: int + AF_SECURITY: int + AF_WANPIPE: int + AF_X25: int + +# Miscellaneous undocumented + +if sys.platform != "win32": + LOCAL_PEERCRED: int + +if sys.platform != "win32" and sys.platform != "darwin": + IPX_TYPE: int + +# ===== Exceptions ===== error = OSError @@ -572,7 +678,7 @@ if sys.version_info >= (3, 10): else: class timeout(error): ... -# ----- Classes ----- +# ===== Classes ===== class socket: @property @@ -584,9 +690,11 @@ class socket: @property def timeout(self) -> float | None: ... if sys.platform == "win32": - def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: _FD | bytes | None = ...) -> None: ... + def __init__( + self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ... + ) -> None: ... else: - def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: _FD | None = ...) -> None: ... + def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = ...) -> None: ... def bind(self, __address: _Address) -> None: ... def close(self) -> None: ... @@ -648,10 +756,10 @@ class socket: SocketType = socket -# ----- Functions ----- +# ===== Functions ===== -def close(__fd: _FD) -> None: ... -def dup(__fd: _FD) -> int: ... +def close(__fd: SupportsIndex) -> None: ... +def dup(__fd: SupportsIndex) -> int: ... # the 5th tuple item is an address def getaddrinfo( @@ -687,33 +795,8 @@ if sys.platform != "win32": def CMSG_SPACE(__length: int) -> int: ... def socketpair(__family: int = ..., __type: int = ..., __proto: int = ...) -> tuple[socket, socket]: ... -# Windows added these in 3.8, but didn't have them before -if sys.platform != "win32" or sys.version_info >= (3, 8): - def if_nameindex() -> list[tuple[int, str]]: ... - def if_nametoindex(__name: str) -> int: ... - def if_indextoname(__index: int) -> str: ... +def if_nameindex() -> list[tuple[int, str]]: ... +def if_nametoindex(__name: str) -> int: ... +def if_indextoname(__index: int) -> str: ... -if sys.version_info >= (3, 12): - IP_PKTINFO: int - IP_UNBLOCK_SOURCE: int - IP_BLOCK_SOURCE: int - IP_ADD_SOURCE_MEMBERSHIP: int - IP_DROP_SOURCE_MEMBERSHIP: int - if sys.platform == "win32": - AF_HYPERV: int - HV_PROTOCOL_RAW: int - HVSOCKET_CONNECT_TIMEOUT: int - HVSOCKET_CONNECT_TIMEOUT_MAX: int - HVSOCKET_CONNECTED_SUSPEND: int - HVSOCKET_ADDRESS_FLAG_PASSTHRU: int - HV_GUID_ZERO: str - HV_GUID_WILDCARD: str - HV_GUID_BROADCAST: str - HV_GUID_CHILDREN: str - HV_GUID_LOOPBACK: str - HV_GUID_PARENT: str - else: - ETHERTYPE_ARP: int - ETHERTYPE_IP: int - ETHERTYPE_IPV6: int - ETHERTYPE_VLAN: int +CAPI: object diff --git a/mypy/typeshed/stdlib/_stat.pyi b/mypy/typeshed/stdlib/_stat.pyi index 83d832e4dd8e..347897404edc 100644 --- a/mypy/typeshed/stdlib/_stat.pyi +++ b/mypy/typeshed/stdlib/_stat.pyi @@ -1,5 +1,5 @@ import sys -from typing_extensions import Literal +from typing import Literal SF_APPEND: Literal[0x00040000] SF_ARCHIVED: Literal[0x00010000] @@ -78,7 +78,7 @@ def S_ISSOCK(mode: int) -> bool: ... def S_ISWHT(mode: int) -> bool: ... def filemode(mode: int) -> str: ... -if sys.platform == "win32" and sys.version_info >= (3, 8): +if sys.platform == "win32": IO_REPARSE_TAG_SYMLINK: int IO_REPARSE_TAG_MOUNT_POINT: int IO_REPARSE_TAG_APPEXECLINK: int diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 8bd0b179607b..8b43a81cac8a 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -3,8 +3,7 @@ from _typeshed import structseq from collections.abc import Callable from threading import Thread from types import TracebackType -from typing import Any, NoReturn -from typing_extensions import Final, final +from typing import Any, Final, NoReturn, final error = RuntimeError @@ -28,21 +27,21 @@ def stack_size(size: int = ...) -> int: ... TIMEOUT_MAX: float -if sys.version_info >= (3, 8): - def get_native_id() -> int: ... # only available on some platforms - @final - class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]): - if sys.version_info >= (3, 10): - __match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread") - @property - def exc_type(self) -> type[BaseException]: ... - @property - def exc_value(self) -> BaseException | None: ... - @property - def exc_traceback(self) -> TracebackType | None: ... - @property - def thread(self) -> Thread | None: ... - _excepthook: Callable[[_ExceptHookArgs], Any] +def get_native_id() -> int: ... # only available on some platforms +@final +class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread") + @property + def exc_type(self) -> type[BaseException]: ... + @property + def exc_value(self) -> BaseException | None: ... + @property + def exc_traceback(self) -> TracebackType | None: ... + @property + def thread(self) -> Thread | None: ... + +_excepthook: Callable[[_ExceptHookArgs], Any] if sys.version_info >= (3, 12): def daemon_threads_allowed() -> bool: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index 89610e21d9e7..67e7e3f696e2 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -1,6 +1,5 @@ import sys -from typing import Any, ClassVar -from typing_extensions import Literal, final +from typing import Any, ClassVar, Literal, final # _tkinter is meant to be only used internally by tkinter, but some tkinter # functions e.g. return _tkinter.Tcl_Obj objects. Tcl_Obj represents a Tcl @@ -107,29 +106,15 @@ TK_VERSION: str class TkttType: def deletetimerhandler(self): ... -if sys.version_info >= (3, 8): - def create( - __screenName: str | None = None, - __baseName: str = "", - __className: str = "Tk", - __interactive: bool = False, - __wantobjects: bool = False, - __wantTk: bool = True, - __sync: bool = False, - __use: str | None = None, - ): ... - -else: - def create( - __screenName: str | None = None, - __baseName: str | None = None, - __className: str = "Tk", - __interactive: bool = False, - __wantobjects: bool = False, - __wantTk: bool = True, - __sync: bool = False, - __use: str | None = None, - ): ... - +def create( + __screenName: str | None = None, + __baseName: str = "", + __className: str = "Tk", + __interactive: bool = False, + __wantobjects: bool = False, + __wantTk: bool = True, + __sync: bool = False, + __use: str | None = None, +): ... def getbusywaitinterval(): ... def setbusywaitinterval(__new_val): ... diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 05892c8aab11..e9a24bab28a9 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -7,8 +7,22 @@ from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as Abst from dataclasses import Field from os import PathLike from types import FrameType, TracebackType -from typing import Any, AnyStr, ClassVar, Generic, Protocol, SupportsFloat, SupportsInt, TypeVar, overload -from typing_extensions import Buffer, Final, Literal, LiteralString, SupportsIndex, TypeAlias, final +from typing import ( + Any, + AnyStr, + ClassVar, + Final, + Generic, + Literal, + Protocol, + SupportsFloat, + SupportsIndex, + SupportsInt, + TypeVar, + final, + overload, +) +from typing_extensions import Buffer, LiteralString, TypeAlias _KT = TypeVar("_KT") _KT_co = TypeVar("_KT_co", covariant=True) @@ -19,8 +33,10 @@ _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _T_contra = TypeVar("_T_contra", contravariant=True) -# Use for "self" annotations: -# def __enter__(self: Self) -> Self: ... +# Alternative to `typing_extensions.Self`, exclusively for use with `__new__` +# in metaclasses: +# def __new__(cls: type[Self], ...) -> Self: ... +# In other cases, use `typing_extensions.Self`. Self = TypeVar("Self") # noqa: Y001 # covariant version of typing.AnyStr, useful for protocols diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index ce0f681248ab..f939aa815bd2 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable -from typing import Any, Generic, TypeVar, overload -from typing_extensions import Self, final +from typing import Any, Generic, TypeVar, final, overload +from typing_extensions import Self if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index 1aec6ce50443..21ae149e186e 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -1,8 +1,7 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Sequence -from typing import Any, NoReturn, overload -from typing_extensions import Literal, final +from typing import Any, Literal, NoReturn, final, overload if sys.platform == "win32": ABOVE_NORMAL_PRIORITY_CLASS: Literal[0x8000] @@ -36,12 +35,11 @@ if sys.platform == "win32": FILE_GENERIC_READ: Literal[1179785] FILE_GENERIC_WRITE: Literal[1179926] - if sys.version_info >= (3, 8): - FILE_MAP_ALL_ACCESS: Literal[983071] - FILE_MAP_COPY: Literal[1] - FILE_MAP_EXECUTE: Literal[32] - FILE_MAP_READ: Literal[4] - FILE_MAP_WRITE: Literal[2] + FILE_MAP_ALL_ACCESS: Literal[983071] + FILE_MAP_COPY: Literal[1] + FILE_MAP_EXECUTE: Literal[32] + FILE_MAP_READ: Literal[4] + FILE_MAP_WRITE: Literal[2] FILE_TYPE_CHAR: Literal[2] FILE_TYPE_DISK: Literal[1] @@ -53,23 +51,21 @@ if sys.platform == "win32": GENERIC_WRITE: Literal[0x40000000] HIGH_PRIORITY_CLASS: Literal[0x80] INFINITE: Literal[0xFFFFFFFF] - if sys.version_info >= (3, 8): - # Ignore the Flake8 error -- flake8-pyi assumes - # most numbers this long will be implementation details, - # but here we can see that it's a power of 2 - INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054 + # Ignore the Flake8 error -- flake8-pyi assumes + # most numbers this long will be implementation details, + # but here we can see that it's a power of 2 + INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054 IDLE_PRIORITY_CLASS: Literal[0x40] NORMAL_PRIORITY_CLASS: Literal[0x20] REALTIME_PRIORITY_CLASS: Literal[0x100] NMPWAIT_WAIT_FOREVER: Literal[0xFFFFFFFF] - if sys.version_info >= (3, 8): - MEM_COMMIT: Literal[0x1000] - MEM_FREE: Literal[0x10000] - MEM_IMAGE: Literal[0x1000000] - MEM_MAPPED: Literal[0x40000] - MEM_PRIVATE: Literal[0x20000] - MEM_RESERVE: Literal[0x2000] + MEM_COMMIT: Literal[0x1000] + MEM_FREE: Literal[0x10000] + MEM_IMAGE: Literal[0x1000000] + MEM_MAPPED: Literal[0x40000] + MEM_PRIVATE: Literal[0x20000] + MEM_RESERVE: Literal[0x2000] NULL: Literal[0] OPEN_EXISTING: Literal[3] @@ -81,29 +77,27 @@ if sys.platform == "win32": PIPE_UNLIMITED_INSTANCES: Literal[255] PIPE_WAIT: Literal[0] - if sys.version_info >= (3, 8): - PAGE_EXECUTE: Literal[0x10] - PAGE_EXECUTE_READ: Literal[0x20] - PAGE_EXECUTE_READWRITE: Literal[0x40] - PAGE_EXECUTE_WRITECOPY: Literal[0x80] - PAGE_GUARD: Literal[0x100] - PAGE_NOACCESS: Literal[0x1] - PAGE_NOCACHE: Literal[0x200] - PAGE_READONLY: Literal[0x2] - PAGE_READWRITE: Literal[0x4] - PAGE_WRITECOMBINE: Literal[0x400] - PAGE_WRITECOPY: Literal[0x8] + PAGE_EXECUTE: Literal[0x10] + PAGE_EXECUTE_READ: Literal[0x20] + PAGE_EXECUTE_READWRITE: Literal[0x40] + PAGE_EXECUTE_WRITECOPY: Literal[0x80] + PAGE_GUARD: Literal[0x100] + PAGE_NOACCESS: Literal[0x1] + PAGE_NOCACHE: Literal[0x200] + PAGE_READONLY: Literal[0x2] + PAGE_READWRITE: Literal[0x4] + PAGE_WRITECOMBINE: Literal[0x400] + PAGE_WRITECOPY: Literal[0x8] PROCESS_ALL_ACCESS: Literal[0x1FFFFF] PROCESS_DUP_HANDLE: Literal[0x40] - if sys.version_info >= (3, 8): - SEC_COMMIT: Literal[0x8000000] - SEC_IMAGE: Literal[0x1000000] - SEC_LARGE_PAGES: Literal[0x80000000] - SEC_NOCACHE: Literal[0x10000000] - SEC_RESERVE: Literal[0x4000000] - SEC_WRITECOMBINE: Literal[0x40000000] + SEC_COMMIT: Literal[0x8000000] + SEC_IMAGE: Literal[0x1000000] + SEC_LARGE_PAGES: Literal[0x80000000] + SEC_NOCACHE: Literal[0x10000000] + SEC_RESERVE: Literal[0x4000000] + SEC_WRITECOMBINE: Literal[0x40000000] STARTF_USESHOWWINDOW: Literal[0x1] STARTF_USESTDHANDLES: Literal[0x100] @@ -114,8 +108,7 @@ if sys.platform == "win32": STILL_ACTIVE: Literal[259] SW_HIDE: Literal[0] - if sys.version_info >= (3, 8): - SYNCHRONIZE: Literal[0x100000] + SYNCHRONIZE: Literal[0x100000] WAIT_ABANDONED_0: Literal[128] WAIT_OBJECT_0: Literal[0] WAIT_TIMEOUT: Literal[258] diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index 7fe1d09f7589..c642f8b9f123 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -2,8 +2,8 @@ import _typeshed import sys from _typeshed import SupportsWrite from collections.abc import Callable -from typing import Any, TypeVar -from typing_extensions import Concatenate, Literal, ParamSpec +from typing import Any, Literal, TypeVar +from typing_extensions import Concatenate, ParamSpec _T = TypeVar("_T") _R_co = TypeVar("_R_co", covariant=True) diff --git a/mypy/typeshed/stdlib/aifc.pyi b/mypy/typeshed/stdlib/aifc.pyi index ab0c18ed6623..05bf53986b29 100644 --- a/mypy/typeshed/stdlib/aifc.pyi +++ b/mypy/typeshed/stdlib/aifc.pyi @@ -1,7 +1,7 @@ import sys from types import TracebackType -from typing import IO, Any, NamedTuple, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Literal, NamedTuple, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): __all__ = ["Error", "open"] diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 0cbbcd242195..489cc6b16634 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern -from typing import IO, Any, Generic, NewType, NoReturn, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload +from typing_extensions import Self, TypeAlias __all__ = [ "ArgumentParser", @@ -446,8 +446,7 @@ class _StoreFalseAction(_StoreConstAction): class _AppendAction(Action): ... # undocumented -if sys.version_info >= (3, 8): - class _ExtendAction(_AppendAction): ... +class _ExtendAction(_AppendAction): ... # undocumented class _AppendConstAction(Action): diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 2ef821fcf87a..4b5675d2a76e 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -3,8 +3,8 @@ from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Iterable # pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence -from typing import Any, MutableSequence, TypeVar, overload # noqa: Y022 -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias +from typing import Any, Literal, MutableSequence, SupportsIndex, TypeVar, overload # noqa: Y022 +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 12): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 5c9cafc189be..4ab325b5baa7 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -3,32 +3,34 @@ import sys from _ast import * from _typeshed import ReadableBuffer, Unused from collections.abc import Iterator -from typing import Any, TypeVar as _TypeVar, overload -from typing_extensions import Literal, deprecated +from typing import Any, Literal, TypeVar as _TypeVar, overload +from typing_extensions import deprecated -if sys.version_info >= (3, 8): - class _ABC(type): - if sys.version_info >= (3, 9): - def __init__(cls, *args: Unused) -> None: ... +class _ABC(type): + if sys.version_info >= (3, 9): + def __init__(cls, *args: Unused) -> None: ... - @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") - class Num(Constant, metaclass=_ABC): - value: int | float | complex - @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") - class Str(Constant, metaclass=_ABC): - value: str - # Aliases for value, for backwards compatibility - s: str - @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") - class Bytes(Constant, metaclass=_ABC): - value: bytes - # Aliases for value, for backwards compatibility - s: bytes - @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") - class NameConstant(Constant, metaclass=_ABC): ... +@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") +class Num(Constant, metaclass=_ABC): + value: int | float | complex - @deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") - class Ellipsis(Constant, metaclass=_ABC): ... +@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") +class Str(Constant, metaclass=_ABC): + value: str + # Aliases for value, for backwards compatibility + s: str + +@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") +class Bytes(Constant, metaclass=_ABC): + value: bytes + # Aliases for value, for backwards compatibility + s: bytes + +@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") +class NameConstant(Constant, metaclass=_ABC): ... + +@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") +class Ellipsis(Constant, metaclass=_ABC): ... if sys.version_info >= (3, 9): class slice(AST): ... @@ -90,10 +92,8 @@ class NodeVisitor: def visit_FormattedValue(self, node: FormattedValue) -> Any: ... def visit_JoinedStr(self, node: JoinedStr) -> Any: ... def visit_Constant(self, node: Constant) -> Any: ... - if sys.version_info >= (3, 8): - def visit_NamedExpr(self, node: NamedExpr) -> Any: ... - def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... - + def visit_NamedExpr(self, node: NamedExpr) -> Any: ... + def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... def visit_Attribute(self, node: Attribute) -> Any: ... def visit_Subscript(self, node: Subscript) -> Any: ... def visit_Starred(self, node: Starred) -> Any: ... @@ -181,100 +181,75 @@ class NodeTransformer(NodeVisitor): _T = _TypeVar("_T", bound=AST) -if sys.version_info >= (3, 8): - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", - mode: Literal["exec"] = "exec", - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> Module: ... - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["eval"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> Expression: ... - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["func_type"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> FunctionType: ... - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["single"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> Interactive: ... - @overload - def parse( - source: str | ReadableBuffer, - *, - mode: Literal["eval"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> Expression: ... - @overload - def parse( - source: str | ReadableBuffer, - *, - mode: Literal["func_type"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> FunctionType: ... - @overload - def parse( - source: str | ReadableBuffer, - *, - mode: Literal["single"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> Interactive: ... - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", - mode: str = "exec", - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, - ) -> AST: ... - -else: - @overload - def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", - mode: Literal["exec"] = "exec", - ) -> Module: ... - @overload - def parse( - source: str | ReadableBuffer, filename: str | ReadableBuffer | os.PathLike[Any], mode: Literal["eval"] - ) -> Expression: ... - @overload - def parse( - source: str | ReadableBuffer, filename: str | ReadableBuffer | os.PathLike[Any], mode: Literal["single"] - ) -> Interactive: ... - @overload - def parse(source: str | ReadableBuffer, *, mode: Literal["eval"]) -> Expression: ... - @overload - def parse(source: str | ReadableBuffer, *, mode: Literal["single"]) -> Interactive: ... - @overload - def parse( - source: str | ReadableBuffer, filename: str | ReadableBuffer | os.PathLike[Any] = "", mode: str = "exec" - ) -> AST: ... +@overload +def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: Literal["exec"] = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> Module: ... +@overload +def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["eval"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> Expression: ... +@overload +def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["func_type"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> FunctionType: ... +@overload +def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["single"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> Interactive: ... +@overload +def parse( + source: str | ReadableBuffer, + *, + mode: Literal["eval"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> Expression: ... +@overload +def parse( + source: str | ReadableBuffer, + *, + mode: Literal["func_type"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> FunctionType: ... +@overload +def parse( + source: str | ReadableBuffer, + *, + mode: Literal["single"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> Interactive: ... +@overload +def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: str = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, +) -> AST: ... if sys.version_info >= (3, 9): def unparse(ast_obj: AST) -> str: ... @@ -295,10 +270,7 @@ def increment_lineno(node: _T, n: int = 1) -> _T: ... def iter_child_nodes(node: AST) -> Iterator[AST]: ... def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ... def literal_eval(node_or_string: str | AST) -> Any: ... - -if sys.version_info >= (3, 8): - def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ... - +def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ... def walk(node: AST) -> Iterator[AST]: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index c11465184389..d5bbe8cb0642 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -7,6 +7,7 @@ from typing_extensions import TypeAlias from .base_events import * from .coroutines import * from .events import * +from .exceptions import * from .futures import * from .locks import * from .protocols import * @@ -17,9 +18,6 @@ from .subprocess import * from .tasks import * from .transports import * -if sys.version_info >= (3, 8): - from .exceptions import * - if sys.version_info >= (3, 9): from .threads import * diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index ff6c42c3645a..112cfeefa8f2 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -10,8 +10,8 @@ from asyncio.transports import BaseTransport, DatagramTransport, ReadTransport, from collections.abc import Callable, Iterable, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket -from typing import IO, Any, TypeVar, overload -from typing_extensions import Literal, TypeAlias, TypeVarTuple, Unpack +from typing import IO, Any, Literal, TypeVar, overload +from typing_extensions import TypeAlias, TypeVarTuple, Unpack if sys.version_info >= (3, 9): __all__ = ("BaseEventLoop", "Server") @@ -53,13 +53,8 @@ class Server(AbstractServer): def is_serving(self) -> bool: ... async def start_serving(self) -> None: ... async def serve_forever(self) -> None: ... - if sys.version_info >= (3, 8): - @property - def sockets(self) -> tuple[socket, ...]: ... - else: - @property - def sockets(self) -> list[socket]: ... - + @property + def sockets(self) -> tuple[socket, ...]: ... def close(self) -> None: ... async def wait_closed(self) -> None: ... @@ -87,10 +82,8 @@ class BaseEventLoop(AbstractEventLoop): # Tasks methods if sys.version_info >= (3, 11): def create_task(self, coro: _CoroutineLike[_T], *, name: object = None, context: Context | None = None) -> Task[_T]: ... - elif sys.version_info >= (3, 8): - def create_task(self, coro: _CoroutineLike[_T], *, name: object = None) -> Task[_T]: ... else: - def create_task(self, coro: _CoroutineLike[_T]) -> Task[_T]: ... + def create_task(self, coro: _CoroutineLike[_T], *, name: object = None) -> Task[_T]: ... def set_task_factory(self, factory: _TaskFactory | None) -> None: ... def get_task_factory(self) -> _TaskFactory | None: ... @@ -192,7 +185,7 @@ class BaseEventLoop(AbstractEventLoop): happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - elif sys.version_info >= (3, 8): + else: @overload async def create_connection( self, @@ -229,39 +222,6 @@ class BaseEventLoop(AbstractEventLoop): happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - else: - @overload - async def create_connection( - self, - protocol_factory: Callable[[], _ProtocolT], - host: str = ..., - port: int = ..., - *, - ssl: _SSLContext = None, - family: int = 0, - proto: int = 0, - flags: int = 0, - sock: None = None, - local_addr: tuple[str, int] | None = None, - server_hostname: str | None = None, - ssl_handshake_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... - @overload - async def create_connection( - self, - protocol_factory: Callable[[], _ProtocolT], - host: None = None, - port: None = None, - *, - ssl: _SSLContext = None, - family: int = 0, - proto: int = 0, - flags: int = 0, - sock: socket, - local_addr: None = None, - server_hostname: str | None = None, - ssl_handshake_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... if sys.version_info >= (3, 11): @overload async def create_server( diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi index c51174ef23cd..231766200934 100644 --- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi @@ -1,7 +1,6 @@ from collections.abc import Callable, Sequence from contextvars import Context -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal from . import futures diff --git a/mypy/typeshed/stdlib/asyncio/constants.pyi b/mypy/typeshed/stdlib/asyncio/constants.pyi index 60d8529209c2..559cc02a0faa 100644 --- a/mypy/typeshed/stdlib/asyncio/constants.pyi +++ b/mypy/typeshed/stdlib/asyncio/constants.pyi @@ -1,6 +1,6 @@ import enum import sys -from typing_extensions import Literal +from typing import Literal LOG_THRESHOLD_FOR_CONNLOST_WRITES: Literal[5] ACCEPT_RETRY_DELAY: Literal[1] diff --git a/mypy/typeshed/stdlib/asyncio/coroutines.pyi b/mypy/typeshed/stdlib/asyncio/coroutines.pyi index 14fb627ae6fe..e92b150875f6 100644 --- a/mypy/typeshed/stdlib/asyncio/coroutines.pyi +++ b/mypy/typeshed/stdlib/asyncio/coroutines.pyi @@ -23,6 +23,4 @@ def iscoroutinefunction(func: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable def iscoroutinefunction(func: Callable[_P, object]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... @overload def iscoroutinefunction(func: object) -> TypeGuard[Callable[..., Coroutine[Any, Any, Any]]]: ... - -# Can actually be a generator-style coroutine on Python 3.7 def iscoroutine(obj: object) -> TypeGuard[Coroutine[Any, Any, Any]]: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 0f51c457fc24..649771df8bf1 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -5,8 +5,8 @@ from abc import ABCMeta, abstractmethod from collections.abc import Callable, Coroutine, Generator, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket -from typing import IO, Any, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypeVarTuple, Unpack, deprecated +from typing import IO, Any, Literal, Protocol, TypeVar, overload +from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack, deprecated from . import _AwaitableLike, _CoroutineLike from .base_events import Server @@ -16,44 +16,23 @@ from .tasks import Task from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from .unix_events import AbstractChildWatcher -if sys.version_info >= (3, 8): - __all__ = ( - "AbstractEventLoopPolicy", - "AbstractEventLoop", - "AbstractServer", - "Handle", - "TimerHandle", - "get_event_loop_policy", - "set_event_loop_policy", - "get_event_loop", - "set_event_loop", - "new_event_loop", - "get_child_watcher", - "set_child_watcher", - "_set_running_loop", - "get_running_loop", - "_get_running_loop", - ) - -else: - __all__ = ( - "AbstractEventLoopPolicy", - "AbstractEventLoop", - "AbstractServer", - "Handle", - "TimerHandle", - "SendfileNotAvailableError", - "get_event_loop_policy", - "set_event_loop_policy", - "get_event_loop", - "set_event_loop", - "new_event_loop", - "get_child_watcher", - "set_child_watcher", - "_set_running_loop", - "get_running_loop", - "_get_running_loop", - ) +__all__ = ( + "AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "get_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "set_event_loop", + "new_event_loop", + "get_child_watcher", + "set_child_watcher", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", +) _T = TypeVar("_T") _Ts = TypeVarTuple("_Ts") @@ -162,12 +141,9 @@ class AbstractEventLoop: def create_task( self, coro: _CoroutineLike[_T], *, name: str | None = None, context: Context | None = None ) -> Task[_T]: ... - elif sys.version_info >= (3, 8): - @abstractmethod - def create_task(self, coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... else: @abstractmethod - def create_task(self, coro: _CoroutineLike[_T]) -> Task[_T]: ... + def create_task(self, coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... @abstractmethod def set_task_factory(self, factory: _TaskFactory | None) -> None: ... @@ -242,7 +218,7 @@ class AbstractEventLoop: happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - elif sys.version_info >= (3, 8): + else: @overload @abstractmethod async def create_connection( @@ -281,41 +257,6 @@ class AbstractEventLoop: happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - else: - @overload - @abstractmethod - async def create_connection( - self, - protocol_factory: Callable[[], _ProtocolT], - host: str = ..., - port: int = ..., - *, - ssl: _SSLContext = None, - family: int = 0, - proto: int = 0, - flags: int = 0, - sock: None = None, - local_addr: tuple[str, int] | None = None, - server_hostname: str | None = None, - ssl_handshake_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... - @overload - @abstractmethod - async def create_connection( - self, - protocol_factory: Callable[[], _ProtocolT], - host: None = None, - port: None = None, - *, - ssl: _SSLContext = None, - family: int = 0, - proto: int = 0, - flags: int = 0, - sock: socket, - local_addr: None = None, - server_hostname: str | None = None, - ssl_handshake_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... if sys.version_info >= (3, 11): @overload @abstractmethod @@ -554,7 +495,6 @@ class AbstractEventLoop: def add_writer(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... @abstractmethod def remove_writer(self, fd: FileDescriptorLike) -> bool: ... - # Completion based I/O methods returning Futures prior to 3.7 @abstractmethod async def sock_recv(self, sock: socket, nbytes: int) -> bytes: ... @abstractmethod @@ -632,6 +572,3 @@ else: def _set_running_loop(__loop: AbstractEventLoop | None) -> None: ... def _get_running_loop() -> AbstractEventLoop: ... def get_running_loop() -> AbstractEventLoop: ... - -if sys.version_info < (3, 8): - class SendfileNotAvailableError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index af05425d02a2..44b9528705a5 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -1,25 +1,16 @@ import sys from collections.abc import Awaitable, Callable, Generator, Iterable -from concurrent.futures._base import Error, Future as _ConcurrentFuture -from typing import Any, TypeVar -from typing_extensions import Literal, Self, TypeGuard +from concurrent.futures._base import Future as _ConcurrentFuture +from contextvars import Context +from typing import Any, Literal, TypeVar +from typing_extensions import Self, TypeGuard from .events import AbstractEventLoop -if sys.version_info < (3, 8): - from concurrent.futures import CancelledError as CancelledError, TimeoutError as TimeoutError - - class InvalidStateError(Error): ... - -from contextvars import Context - if sys.version_info >= (3, 9): from types import GenericAlias -if sys.version_info >= (3, 8): - __all__ = ("Future", "wrap_future", "isfuture") -else: - __all__ = ("CancelledError", "TimeoutError", "InvalidStateError", "Future", "wrap_future", "isfuture") +__all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index 394b82a5b3d9..3aac34b6934f 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -4,8 +4,8 @@ from _typeshed import Unused from collections import deque from collections.abc import Callable, Generator from types import TracebackType -from typing import Any, TypeVar -from typing_extensions import Literal, Self +from typing import Any, Literal, TypeVar +from typing_extensions import Self from .events import AbstractEventLoop from .futures import Future @@ -47,6 +47,7 @@ else: ) -> None: ... class Lock(_ContextManagerMixin, _LoopBoundMixin): + _waiters: deque[Future[Any]] | None if sys.version_info >= (3, 10): def __init__(self) -> None: ... else: @@ -57,6 +58,7 @@ class Lock(_ContextManagerMixin, _LoopBoundMixin): def release(self) -> None: ... class Event(_LoopBoundMixin): + _waiters: deque[Future[Any]] if sys.version_info >= (3, 10): def __init__(self) -> None: ... else: @@ -68,6 +70,7 @@ class Event(_LoopBoundMixin): async def wait(self) -> Literal[True]: ... class Condition(_ContextManagerMixin, _LoopBoundMixin): + _waiters: deque[Future[Any]] if sys.version_info >= (3, 10): def __init__(self, lock: Lock | None = None) -> None: ... else: @@ -83,7 +86,7 @@ class Condition(_ContextManagerMixin, _LoopBoundMixin): class Semaphore(_ContextManagerMixin, _LoopBoundMixin): _value: int - _waiters: deque[Future[Any]] + _waiters: deque[Future[Any]] | None if sys.version_info >= (3, 10): def __init__(self, value: int = 1) -> None: ... else: diff --git a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi index 4634bbb2b37c..957fdd6ce255 100644 --- a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi @@ -1,8 +1,7 @@ import sys from collections.abc import Mapping from socket import socket -from typing import Any, ClassVar -from typing_extensions import Literal +from typing import Any, ClassVar, Literal from . import base_events, constants, events, futures, streams, transports diff --git a/mypy/typeshed/stdlib/asyncio/runners.pyi b/mypy/typeshed/stdlib/asyncio/runners.pyi index 847072b633ac..37a85b709cdc 100644 --- a/mypy/typeshed/stdlib/asyncio/runners.pyi +++ b/mypy/typeshed/stdlib/asyncio/runners.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import Unused from collections.abc import Callable, Coroutine from contextvars import Context -from typing import Any, TypeVar -from typing_extensions import Self, final +from typing import Any, TypeVar, final +from typing_extensions import Self from .events import AbstractEventLoop @@ -28,8 +28,5 @@ if sys.version_info >= (3, 12): main: Coroutine[Any, Any, _T], *, debug: bool | None = ..., loop_factory: Callable[[], AbstractEventLoop] | None = ... ) -> _T: ... -elif sys.version_info >= (3, 8): - def run(main: Coroutine[Any, Any, _T], *, debug: bool | None = None) -> _T: ... - else: - def run(main: Coroutine[Any, Any, _T], *, debug: bool = False) -> _T: ... + def run(main: Coroutine[Any, Any, _T], *, debug: bool | None = None) -> _T: ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index 393a1fbdc468..5dcca950e819 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -3,8 +3,8 @@ import sys from collections import deque from collections.abc import Callable from enum import Enum -from typing import Any, ClassVar -from typing_extensions import Literal, TypeAlias +from typing import Any, ClassVar, Literal +from typing_extensions import TypeAlias from . import constants, events, futures, protocols, transports diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 81a94425f8de..4dff8d28b616 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -2,61 +2,27 @@ import ssl import sys from _typeshed import StrPath from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence -from typing import Any -from typing_extensions import Self, SupportsIndex, TypeAlias +from typing import Any, SupportsIndex +from typing_extensions import Self, TypeAlias from . import events, protocols, transports from .base_events import Server if sys.platform == "win32": - if sys.version_info >= (3, 8): - __all__ = ("StreamReader", "StreamWriter", "StreamReaderProtocol", "open_connection", "start_server") - else: - __all__ = ( - "StreamReader", - "StreamWriter", - "StreamReaderProtocol", - "open_connection", - "start_server", - "IncompleteReadError", - "LimitOverrunError", - ) + __all__ = ("StreamReader", "StreamWriter", "StreamReaderProtocol", "open_connection", "start_server") else: - if sys.version_info >= (3, 8): - __all__ = ( - "StreamReader", - "StreamWriter", - "StreamReaderProtocol", - "open_connection", - "start_server", - "open_unix_connection", - "start_unix_server", - ) - else: - __all__ = ( - "StreamReader", - "StreamWriter", - "StreamReaderProtocol", - "open_connection", - "start_server", - "IncompleteReadError", - "LimitOverrunError", - "open_unix_connection", - "start_unix_server", - ) + __all__ = ( + "StreamReader", + "StreamWriter", + "StreamReaderProtocol", + "open_connection", + "start_server", + "open_unix_connection", + "start_unix_server", + ) _ClientConnectedCallback: TypeAlias = Callable[[StreamReader, StreamWriter], Awaitable[None] | None] -if sys.version_info < (3, 8): - class IncompleteReadError(EOFError): - expected: int | None - partial: bytes - def __init__(self, partial: bytes, expected: int | None) -> None: ... - - class LimitOverrunError(Exception): - consumed: int - def __init__(self, message: str, consumed: int) -> None: ... - if sys.version_info >= (3, 10): async def open_connection( host: str | None = None, diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index 03aea65f6d54..19452d4eb469 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -3,16 +3,10 @@ import sys from _typeshed import StrOrBytesPath from asyncio import events, protocols, streams, transports from collections.abc import Callable, Collection -from typing import IO, Any -from typing_extensions import Literal, TypeAlias +from typing import IO, Any, Literal __all__ = ("create_subprocess_exec", "create_subprocess_shell") -if sys.version_info >= (3, 8): - _ExecArg: TypeAlias = StrOrBytesPath -else: - _ExecArg: TypeAlias = str | bytes - PIPE: int STDOUT: int DEVNULL: int @@ -74,8 +68,8 @@ if sys.version_info >= (3, 11): pipesize: int = -1, ) -> Process: ... async def create_subprocess_exec( - program: _ExecArg, - *args: _ExecArg, + program: StrOrBytesPath, + *args: StrOrBytesPath, stdin: int | IO[Any] | None = None, stdout: int | IO[Any] | None = None, stderr: int | IO[Any] | None = None, @@ -139,8 +133,8 @@ elif sys.version_info >= (3, 10): pipesize: int = -1, ) -> Process: ... async def create_subprocess_exec( - program: _ExecArg, - *args: _ExecArg, + program: StrOrBytesPath, + *args: StrOrBytesPath, stdin: int | IO[Any] | None = None, stdout: int | IO[Any] | None = None, stderr: int | IO[Any] | None = None, @@ -203,8 +197,8 @@ else: # >= 3.9 umask: int = -1, ) -> Process: ... async def create_subprocess_exec( - program: _ExecArg, - *args: _ExecArg, + program: StrOrBytesPath, + *args: StrOrBytesPath, stdin: int | IO[Any] | None = None, stdout: int | IO[Any] | None = None, stderr: int | IO[Any] | None = None, diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 7c76abaf1dca..23447ba27aa5 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -2,8 +2,8 @@ import concurrent.futures import sys from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator from types import FrameType -from typing import Any, Protocol, TextIO, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, Literal, Protocol, TextIO, TypeVar, overload +from typing_extensions import TypeAlias from . import _CoroutineLike from .events import AbstractEventLoop @@ -402,16 +402,14 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportGe name: str | None = ..., context: Context | None = None, ) -> None: ... - elif sys.version_info >= (3, 8): + else: def __init__( self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... ) -> None: ... - else: - def __init__(self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ...) -> None: ... - if sys.version_info >= (3, 8): - def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... - def get_name(self) -> str: ... - def set_name(self, __value: object) -> None: ... + + def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... + def get_name(self) -> str: ... + def set_name(self, __value: object) -> None: ... if sys.version_info >= (3, 12): def get_context(self) -> Context: ... @@ -433,11 +431,8 @@ def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... if sys.version_info >= (3, 11): def create_task(coro: _CoroutineLike[_T], *, name: str | None = None, context: Context | None = None) -> Task[_T]: ... -elif sys.version_info >= (3, 8): - def create_task(coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... - else: - def create_task(coro: _CoroutineLike[_T]) -> Task[_T]: ... + def create_task(coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/timeouts.pyi b/mypy/typeshed/stdlib/asyncio/timeouts.pyi index 2d31b777b77d..2f0e40e25680 100644 --- a/mypy/typeshed/stdlib/asyncio/timeouts.pyi +++ b/mypy/typeshed/stdlib/asyncio/timeouts.pyi @@ -1,5 +1,6 @@ from types import TracebackType -from typing_extensions import Self, final +from typing import final +from typing_extensions import Self __all__ = ("Timeout", "timeout", "timeout_at") diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index ee16035f86a8..d2a2fef5c33b 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -2,7 +2,8 @@ import sys import types from abc import ABCMeta, abstractmethod from collections.abc import Callable -from typing_extensions import Literal, Self, TypeVarTuple, Unpack, deprecated +from typing import Literal +from typing_extensions import Self, TypeVarTuple, Unpack, deprecated from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop @@ -29,9 +30,8 @@ if sys.version_info >= (3, 12): def __exit__( self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None ) -> None: ... - if sys.version_info >= (3, 8): - @abstractmethod - def is_active(self) -> bool: ... + @abstractmethod + def is_active(self) -> bool: ... else: class AbstractChildWatcher: @@ -49,9 +49,8 @@ else: def __exit__( self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None ) -> None: ... - if sys.version_info >= (3, 8): - @abstractmethod - def is_active(self) -> bool: ... + @abstractmethod + def is_active(self) -> bool: ... if sys.platform != "win32": if sys.version_info >= (3, 9): @@ -65,7 +64,7 @@ if sys.platform != "win32": "ThreadedChildWatcher", "DefaultEventLoopPolicy", ) - elif sys.version_info >= (3, 8): + else: __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", @@ -75,16 +74,12 @@ if sys.platform != "win32": "ThreadedChildWatcher", "DefaultEventLoopPolicy", ) - else: - __all__ = ("SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", "DefaultEventLoopPolicy") # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. # See discussion in #7412 class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def close(self) -> None: ... - if sys.version_info >= (3, 8): - def is_active(self) -> bool: ... - + def is_active(self) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... if sys.version_info >= (3, 12): @@ -141,7 +136,7 @@ if sys.platform != "win32": def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - elif sys.version_info >= (3, 8): + else: class MultiLoopChildWatcher(AbstractChildWatcher): def is_active(self) -> bool: ... def close(self) -> None: ... @@ -153,18 +148,17 @@ if sys.platform != "win32": def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - if sys.version_info >= (3, 8): - class ThreadedChildWatcher(AbstractChildWatcher): - def is_active(self) -> Literal[True]: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def __del__(self) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + class ThreadedChildWatcher(AbstractChildWatcher): + def is_active(self) -> Literal[True]: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def __del__(self) -> None: ... + def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... if sys.version_info >= (3, 9): class PidfdChildWatcher(AbstractChildWatcher): diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 8e643dd4a3f2..fdf43d3ea91c 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -2,8 +2,7 @@ import socket import sys from _typeshed import Incomplete, ReadableBuffer, WriteableBuffer from collections.abc import Callable -from typing import IO, Any, ClassVar, NoReturn -from typing_extensions import Literal +from typing import IO, Any, ClassVar, Literal, NoReturn from . import events, futures, proactor_events, selector_events, streams, windows_utils diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi index ed5d8da275c5..6b3589adc3cb 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -2,8 +2,8 @@ import subprocess import sys from collections.abc import Callable from types import TracebackType -from typing import Any, AnyStr -from typing_extensions import Literal, Self +from typing import Any, AnyStr, Literal +from typing_extensions import Self if sys.platform == "win32": __all__ = ("pipe", "Popen", "PIPE", "PipeHandle") diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index 2a1fdddff7e9..43012a253164 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import ExcInfo, TraceFunction from collections.abc import Callable, Iterable, Mapping from types import CodeType, FrameType, TracebackType -from typing import IO, Any, SupportsInt, TypeVar -from typing_extensions import Literal, ParamSpec +from typing import IO, Any, Literal, SupportsInt, TypeVar +from typing_extensions import ParamSpec __all__ = ["BdbQuit", "Bdb", "Breakpoint"] @@ -50,11 +50,11 @@ class Bdb: def set_quit(self) -> None: ... def set_break( self, filename: str, lineno: int, temporary: bool = False, cond: str | None = None, funcname: str | None = None - ) -> None: ... - def clear_break(self, filename: str, lineno: int) -> None: ... - def clear_bpbynumber(self, arg: SupportsInt) -> None: ... - def clear_all_file_breaks(self, filename: str) -> None: ... - def clear_all_breaks(self) -> None: ... + ) -> str | None: ... + def clear_break(self, filename: str, lineno: int) -> str | None: ... + def clear_bpbynumber(self, arg: SupportsInt) -> str | None: ... + def clear_all_file_breaks(self, filename: str) -> str | None: ... + def clear_all_breaks(self) -> str | None: ... def get_bpbynumber(self, arg: SupportsInt) -> Breakpoint: ... def get_break(self, filename: str, lineno: int) -> bool: ... def get_breaks(self, filename: str, lineno: int) -> list[Breakpoint]: ... diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi index 759b6c39399a..d48507b90694 100644 --- a/mypy/typeshed/stdlib/binascii.pyi +++ b/mypy/typeshed/stdlib/binascii.pyi @@ -27,16 +27,8 @@ if sys.version_info < (3, 11): def crc_hqx(__data: ReadableBuffer, __crc: int) -> int: ... def crc32(__data: ReadableBuffer, __crc: int = 0) -> int: ... - -if sys.version_info >= (3, 8): - # sep must be str or bytes, not bytearray or any other buffer - def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... - def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... - -else: - def b2a_hex(__data: ReadableBuffer) -> bytes: ... - def hexlify(__data: ReadableBuffer) -> bytes: ... - +def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... +def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... def a2b_hex(__hexstr: _AsciiBuffer) -> bytes: ... def unhexlify(__hexstr: _AsciiBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/binhex.pyi b/mypy/typeshed/stdlib/binhex.pyi index 64ba9f6150b4..d514be3b9b26 100644 --- a/mypy/typeshed/stdlib/binhex.pyi +++ b/mypy/typeshed/stdlib/binhex.pyi @@ -1,6 +1,6 @@ from _typeshed import SizedBuffer -from typing import IO, Any -from typing_extensions import Literal, TypeAlias +from typing import IO, Any, Literal +from typing_extensions import TypeAlias __all__ = ["binhex", "hexbin", "Error"] diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index dca3c2160b5a..1ae1ba03e1ad 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -50,21 +50,23 @@ from typing import ( # noqa: Y022 SupportsBytes, SupportsComplex, SupportsFloat, + SupportsIndex, TypeVar, + final, overload, type_check_only, ) -from typing_extensions import ( + +# we can't import `Literal` from typing or mypy crashes: see #11247 +from typing_extensions import ( # noqa: Y023 Concatenate, Literal, ParamSpec, Self, - SupportsIndex, TypeAlias, TypeGuard, TypeVarTuple, deprecated, - final, ) if sys.version_info >= (3, 9): @@ -115,10 +117,7 @@ class object: # return type of pickle methods is rather hard to express in the current type system # see #6661 and https://docs.python.org/3/library/pickle.html#object.__reduce__ def __reduce__(self) -> str | tuple[Any, ...]: ... - if sys.version_info >= (3, 8): - def __reduce_ex__(self, __protocol: SupportsIndex) -> str | tuple[Any, ...]: ... - else: - def __reduce_ex__(self, __protocol: int) -> str | tuple[Any, ...]: ... + def __reduce_ex__(self, __protocol: SupportsIndex) -> str | tuple[Any, ...]: ... if sys.version_info >= (3, 11): def __getstate__(self) -> object: ... @@ -226,9 +225,7 @@ class int: def __new__(cls, __x: ConvertibleToInt = ...) -> Self: ... @overload def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> Self: ... - if sys.version_info >= (3, 8): - def as_integer_ratio(self) -> tuple[int, Literal[1]]: ... - + def as_integer_ratio(self) -> tuple[int, Literal[1]]: ... @property def real(self) -> int: ... @property @@ -392,22 +389,15 @@ class float: def __bool__(self) -> bool: ... class complex: - if sys.version_info >= (3, 8): - # Python doesn't currently accept SupportsComplex for the second argument - @overload - def __new__( - cls, - real: complex | SupportsComplex | SupportsFloat | SupportsIndex = ..., - imag: complex | SupportsFloat | SupportsIndex = ..., - ) -> Self: ... - @overload - def __new__(cls, real: str | SupportsComplex | SupportsFloat | SupportsIndex | complex) -> Self: ... - else: - @overload - def __new__(cls, real: complex | SupportsComplex | SupportsFloat = ..., imag: complex | SupportsFloat = ...) -> Self: ... - @overload - def __new__(cls, real: str | SupportsComplex | SupportsFloat | complex) -> Self: ... - + # Python doesn't currently accept SupportsComplex for the second argument + @overload + def __new__( + cls, + real: complex | SupportsComplex | SupportsFloat | SupportsIndex = ..., + imag: complex | SupportsFloat | SupportsIndex = ..., + ) -> Self: ... + @overload + def __new__(cls, real: str | SupportsComplex | SupportsFloat | SupportsIndex | complex) -> Self: ... @property def real(self) -> float: ... @property @@ -452,11 +442,10 @@ class str(Sequence[str]): def endswith( self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - if sys.version_info >= (3, 8): - def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] - else: - def expandtabs(self, tabsize: int = 8) -> str: ... # type: ignore[misc] - + @overload + def expandtabs(self: str, tabsize: SupportsIndex = 8) -> str: ... + @overload + def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... @@ -546,19 +535,11 @@ class bytes(Sequence[int]): __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ..., ) -> bool: ... - if sys.version_info >= (3, 8): - def expandtabs(self, tabsize: SupportsIndex = 8) -> bytes: ... - else: - def expandtabs(self, tabsize: int = ...) -> bytes: ... - + def expandtabs(self, tabsize: SupportsIndex = 8) -> bytes: ... def find( self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... - if sys.version_info >= (3, 8): - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... - else: - def hex(self) -> str: ... - + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... def index( self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... @@ -654,20 +635,12 @@ class bytearray(MutableSequence[int]): __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ..., ) -> bool: ... - if sys.version_info >= (3, 8): - def expandtabs(self, tabsize: SupportsIndex = 8) -> bytearray: ... - else: - def expandtabs(self, tabsize: int = ...) -> bytearray: ... - + def expandtabs(self, tabsize: SupportsIndex = 8) -> bytearray: ... def extend(self, __iterable_of_ints: Iterable[SupportsIndex]) -> None: ... def find( self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... - if sys.version_info >= (3, 8): - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... - else: - def hex(self) -> str: ... - + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... def index( self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... @@ -784,7 +757,7 @@ class memoryview(Sequence[int]): ) -> None: ... def cast(self, format: str, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... @overload - def __getitem__(self, __key: SupportsIndex) -> int: ... + def __getitem__(self, __key: SupportsIndex | tuple[SupportsIndex, ...]) -> int: ... @overload def __getitem__(self, __key: slice) -> memoryview: ... def __contains__(self, __x: object) -> bool: ... @@ -795,24 +768,16 @@ class memoryview(Sequence[int]): @overload def __setitem__(self, __key: slice, __value: ReadableBuffer) -> None: ... @overload - def __setitem__(self, __key: SupportsIndex, __value: SupportsIndex) -> None: ... + def __setitem__(self, __key: SupportsIndex | tuple[SupportsIndex, ...], __value: SupportsIndex) -> None: ... if sys.version_info >= (3, 10): def tobytes(self, order: Literal["C", "F", "A"] | None = "C") -> bytes: ... - elif sys.version_info >= (3, 8): - def tobytes(self, order: Literal["C", "F", "A"] | None = None) -> bytes: ... else: - def tobytes(self) -> bytes: ... + def tobytes(self, order: Literal["C", "F", "A"] | None = None) -> bytes: ... def tolist(self) -> list[int]: ... - if sys.version_info >= (3, 8): - def toreadonly(self) -> memoryview: ... - + def toreadonly(self) -> memoryview: ... def release(self) -> None: ... - if sys.version_info >= (3, 8): - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... - else: - def hex(self) -> str: ... - + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... def __buffer__(self, __flags: int) -> memoryview: ... def __release_buffer__(self, __buffer: memoryview) -> None: ... @@ -1026,8 +991,7 @@ class dict(MutableMapping[_KT, _VT]): def __delitem__(self, __key: _KT) -> None: ... def __iter__(self) -> Iterator[_KT]: ... def __eq__(self, __value: object) -> bool: ... - if sys.version_info >= (3, 8): - def __reversed__(self) -> Iterator[_KT]: ... + def __reversed__(self) -> Iterator[_KT]: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): def __class_getitem__(cls, __item: Any) -> GenericAlias: ... @@ -1204,89 +1168,49 @@ if sys.version_info >= (3, 10): # compile() returns a CodeType, unless the flags argument includes PyCF_ONLY_AST (=1024), # in which case it returns ast.AST. We have overloads for flag 0 (the default) and for # explicitly passing PyCF_ONLY_AST. We fall back to Any for other values of flags. -if sys.version_info >= (3, 8): - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: Literal[0], - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, - ) -> CodeType: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - *, - dont_inherit: bool = False, - optimize: int = -1, - _feature_version: int = -1, - ) -> CodeType: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: Literal[1024], - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, - ) -> _ast.AST: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: int, - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, - ) -> Any: ... - -else: - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: Literal[0], - dont_inherit: bool = False, - optimize: int = -1, - ) -> CodeType: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - *, - dont_inherit: bool = False, - optimize: int = -1, - ) -> CodeType: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: Literal[1024], - dont_inherit: bool = False, - optimize: int = -1, - ) -> _ast.AST: ... - @overload - def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], - mode: str, - flags: int, - dont_inherit: bool = False, - optimize: int = -1, - ) -> Any: ... - +@overload +def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | ReadableBuffer | _PathLike[Any], + mode: str, + flags: Literal[0], + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, +) -> CodeType: ... +@overload +def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | ReadableBuffer | _PathLike[Any], + mode: str, + *, + dont_inherit: bool = False, + optimize: int = -1, + _feature_version: int = -1, +) -> CodeType: ... +@overload +def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | ReadableBuffer | _PathLike[Any], + mode: str, + flags: Literal[1024], + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, +) -> _ast.AST: ... +@overload +def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | ReadableBuffer | _PathLike[Any], + mode: str, + flags: int, + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, +) -> Any: ... def copyright() -> None: ... def credits() -> None: ... def delattr(__obj: object, __name: str) -> None: ... @@ -1580,77 +1504,45 @@ _SupportsSomeKindOfPow = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs a _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] ) -if sys.version_info >= (3, 8): - # TODO: `pow(int, int, Literal[0])` fails at runtime, - # but adding a `NoReturn` overload isn't a good solution for expressing that (see #8566). - @overload - def pow(base: int, exp: int, mod: int) -> int: ... - @overload - def pow(base: int, exp: Literal[0], mod: None = None) -> Literal[1]: ... - @overload - def pow(base: int, exp: _PositiveInteger, mod: None = None) -> int: ... - @overload - def pow(base: int, exp: _NegativeInteger, mod: None = None) -> float: ... - # int base & positive-int exp -> int; int base & negative-int exp -> float - # return type must be Any as `int | float` causes too many false-positive errors - @overload - def pow(base: int, exp: int, mod: None = None) -> Any: ... - @overload - def pow(base: _PositiveInteger, exp: float, mod: None = None) -> float: ... - @overload - def pow(base: _NegativeInteger, exp: float, mod: None = None) -> complex: ... - @overload - def pow(base: float, exp: int, mod: None = None) -> float: ... - # float base & float exp could return float or complex - # return type must be Any (same as complex base, complex exp), - # as `float | complex` causes too many false-positive errors - @overload - def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> Any: ... - @overload - def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> complex: ... - @overload - def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... - @overload - def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... - @overload - def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... - @overload - def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... - @overload - def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... +# TODO: `pow(int, int, Literal[0])` fails at runtime, +# but adding a `NoReturn` overload isn't a good solution for expressing that (see #8566). +@overload +def pow(base: int, exp: int, mod: int) -> int: ... +@overload +def pow(base: int, exp: Literal[0], mod: None = None) -> Literal[1]: ... +@overload +def pow(base: int, exp: _PositiveInteger, mod: None = None) -> int: ... +@overload +def pow(base: int, exp: _NegativeInteger, mod: None = None) -> float: ... -else: - @overload - def pow(__x: int, __y: int, __z: int) -> int: ... - @overload - def pow(__x: int, __y: Literal[0], __z: None = None) -> Literal[1]: ... - @overload - def pow(__x: int, __y: _PositiveInteger, __z: None = None) -> int: ... - @overload - def pow(__x: int, __y: _NegativeInteger, __z: None = None) -> float: ... - @overload - def pow(__x: int, __y: int, __z: None = None) -> Any: ... - @overload - def pow(__x: _PositiveInteger, __y: float, __z: None = None) -> float: ... - @overload - def pow(__x: _NegativeInteger, __y: float, __z: None = None) -> complex: ... - @overload - def pow(__x: float, __y: int, __z: None = None) -> float: ... - @overload - def pow(__x: float, __y: complex | _SupportsSomeKindOfPow, __z: None = None) -> Any: ... - @overload - def pow(__x: complex, __y: complex | _SupportsSomeKindOfPow, __z: None = None) -> complex: ... - @overload - def pow(__x: _SupportsPow2[_E, _T_co], __y: _E, __z: None = None) -> _T_co: ... - @overload - def pow(__x: _SupportsPow3NoneOnly[_E, _T_co], __y: _E, __z: None = None) -> _T_co: ... - @overload - def pow(__x: _SupportsPow3[_E, _M, _T_co], __y: _E, __z: _M) -> _T_co: ... - @overload - def pow(__x: _SupportsSomeKindOfPow, __y: float, __z: None = None) -> Any: ... - @overload - def pow(__x: _SupportsSomeKindOfPow, __y: complex, __z: None = None) -> complex: ... +# int base & positive-int exp -> int; int base & negative-int exp -> float +# return type must be Any as `int | float` causes too many false-positive errors +@overload +def pow(base: int, exp: int, mod: None = None) -> Any: ... +@overload +def pow(base: _PositiveInteger, exp: float, mod: None = None) -> float: ... +@overload +def pow(base: _NegativeInteger, exp: float, mod: None = None) -> complex: ... +@overload +def pow(base: float, exp: int, mod: None = None) -> float: ... +# float base & float exp could return float or complex +# return type must be Any (same as complex base, complex exp), +# as `float | complex` causes too many false-positive errors +@overload +def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> Any: ... +@overload +def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> complex: ... +@overload +def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... +@overload +def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... +@overload +def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... +@overload +def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... +@overload +def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... def quit(code: sys._ExitCode = None) -> NoReturn: ... class reversed(Iterator[_T]): @@ -1700,24 +1592,12 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # However, we can't express that in the stub for `sum()` # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. -if sys.version_info >= (3, 8): - @overload - def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] - -else: - @overload - def sum(__iterable: Iterable[bool], __start: int = 0) -> int: ... # type: ignore[overload-overlap] - +@overload +def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... - -if sys.version_info >= (3, 8): - @overload - def sum(__iterable: Iterable[_AddableT1], start: _AddableT2) -> _AddableT1 | _AddableT2: ... - -else: - @overload - def sum(__iterable: Iterable[_AddableT1], __start: _AddableT2) -> _AddableT1 | _AddableT2: ... +@overload +def sum(__iterable: Iterable[_AddableT1], start: _AddableT2) -> _AddableT1 | _AddableT2: ... # The argument to `vars()` has to have a `__dict__` attribute, so the second overload can't be annotated with `object` # (A "SupportsDunderDict" protocol doesn't work) @@ -1821,9 +1701,13 @@ def __import__( def __build_class__(__func: Callable[[], _Cell | Any], __name: str, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... if sys.version_info >= (3, 10): - # In Python 3.10, EllipsisType is exposed publicly in the types module. - @final - class ellipsis: ... + from types import EllipsisType + + # Backwards compatibility hack for folks who relied on the ellipsis type + # existing in typeshed in Python 3.9 and earlier. + ellipsis = EllipsisType + + Ellipsis: EllipsisType else: # Actually the type of Ellipsis is , but since it's @@ -1832,7 +1716,7 @@ else: @type_check_only class ellipsis: ... -Ellipsis: ellipsis + Ellipsis: ellipsis class BaseException: args: tuple[Any, ...] diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index 9ad80ee6f731..620d59a6010c 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -3,8 +3,8 @@ import sys from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Iterable -from typing import IO, Any, Protocol, TextIO, overload -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias, final +from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, final, overload +from typing_extensions import Self, TypeAlias __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"] diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index 8945b21427ab..7d97fa22c394 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import StrOrBytesPath, Unused from collections.abc import Callable from types import CodeType @@ -30,8 +29,7 @@ class Profile: def run(self, cmd: str) -> Self: ... def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... def runcall(self, __func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... - if sys.version_info >= (3, 8): - def __enter__(self) -> Self: ... - def __exit__(self, *exc_info: Unused) -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, *exc_info: Unused) -> None: ... def label(code: str | CodeType) -> _Label: ... # undocumented diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 3f881393e99f..cac39a498ac9 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -4,8 +4,8 @@ import sys from _typeshed import Unused from collections.abc import Iterable, Sequence from time import struct_time -from typing import ClassVar -from typing_extensions import Literal, TypeAlias +from typing import ClassVar, Literal +from typing_extensions import TypeAlias __all__ = [ "IllegalMonthError", diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index 21bf8ca25394..91179c2ed8d5 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import SupportsGetItem, SupportsItemAccess, Unused from builtins import list as _list, type as _type from collections.abc import Iterable, Iterator, Mapping @@ -22,9 +21,6 @@ __all__ = [ "print_environ_usage", ] -if sys.version_info < (3, 8): - __all__ += ["parse_qs", "parse_qsl", "escape"] - def parse( fp: IO[Any] | None = None, environ: SupportsItemAccess[str, str] = ..., @@ -32,11 +28,6 @@ def parse( strict_parsing: bool = ..., separator: str = "&", ) -> dict[str, list[str]]: ... - -if sys.version_info < (3, 8): - def parse_qs(qs: str, keep_blank_values: bool = ..., strict_parsing: bool = ...) -> dict[str, list[str]]: ... - def parse_qsl(qs: str, keep_blank_values: bool = ..., strict_parsing: bool = ...) -> list[tuple[str, str]]: ... - def parse_multipart( fp: IO[Any], pdict: SupportsGetItem[str, bytes], encoding: str = "utf-8", errors: str = "replace", separator: str = "&" ) -> dict[str, list[Any]]: ... @@ -52,9 +43,6 @@ def print_form(form: dict[str, Any]) -> None: ... def print_directory() -> None: ... def print_environ_usage() -> None: ... -if sys.version_info < (3, 8): - def escape(s: str, quote: bool | None = None) -> str: ... - class MiniFieldStorage: # The first five "Any" attributes here are always None, but mypy doesn't support that filename: Any diff --git a/mypy/typeshed/stdlib/cgitb.pyi b/mypy/typeshed/stdlib/cgitb.pyi index 4c315bf6ca39..565725801159 100644 --- a/mypy/typeshed/stdlib/cgitb.pyi +++ b/mypy/typeshed/stdlib/cgitb.pyi @@ -1,8 +1,7 @@ from _typeshed import OptExcInfo, StrOrBytesPath from collections.abc import Callable from types import FrameType, TracebackType -from typing import IO, Any -from typing_extensions import Final +from typing import IO, Any, Final __UNDEF__: Final[object] # undocumented sentinel diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index 658cfb2d40ed..8aad19dafcfb 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -1,10 +1,6 @@ -import sys -from typing import SupportsComplex, SupportsFloat +from typing import SupportsComplex, SupportsFloat, SupportsIndex from typing_extensions import TypeAlias -if sys.version_info >= (3, 8): - from typing import SupportsIndex - e: float pi: float inf: float @@ -13,10 +9,7 @@ nan: float nanj: complex tau: float -if sys.version_info >= (3, 8): - _C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex -else: - _C: TypeAlias = SupportsFloat | SupportsComplex | complex +_C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex def acos(__z: _C) -> complex: ... def acosh(__z: _C) -> complex: ... diff --git a/mypy/typeshed/stdlib/cmd.pyi b/mypy/typeshed/stdlib/cmd.pyi index b658a873410b..9499847fb153 100644 --- a/mypy/typeshed/stdlib/cmd.pyi +++ b/mypy/typeshed/stdlib/cmd.pyi @@ -1,6 +1,5 @@ from collections.abc import Callable -from typing import IO, Any -from typing_extensions import Literal +from typing import IO, Any, Literal __all__ = ["Cmd"] diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 985a52702bc8..7e192d91ddc5 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -1,11 +1,10 @@ -import sys import types from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, Protocol, TextIO -from typing_extensions import Literal, Self +from typing import Any, BinaryIO, Literal, Protocol, TextIO +from typing_extensions import Self __all__ = [ "register", @@ -129,17 +128,9 @@ def getincrementalencoder(encoding: str) -> _IncrementalEncoder: ... def getincrementaldecoder(encoding: str) -> _IncrementalDecoder: ... def getreader(encoding: str) -> _StreamReader: ... def getwriter(encoding: str) -> _StreamWriter: ... - -if sys.version_info >= (3, 8): - def open( - filename: str, mode: str = "r", encoding: str | None = None, errors: str = "strict", buffering: int = -1 - ) -> StreamReaderWriter: ... - -else: - def open( - filename: str, mode: str = "r", encoding: str | None = None, errors: str = "strict", buffering: int = 1 - ) -> StreamReaderWriter: ... - +def open( + filename: str, mode: str = "r", encoding: str | None = None, errors: str = "strict", buffering: int = -1 +) -> StreamReaderWriter: ... def EncodedFile(file: _Stream, data_encoding: str, file_encoding: str | None = None, errors: str = "strict") -> StreamRecoder: ... def iterencode(iterator: Iterable[str], encoding: str, errors: str = "strict") -> Generator[bytes, None, None]: ... def iterdecode(iterator: Iterable[bytes], encoding: str, errors: str = "strict") -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 955681c6ac0c..0df800a4a3be 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -1,8 +1,8 @@ import sys from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT -from typing import Any, Generic, NoReturn, TypeVar, overload -from typing_extensions import Self, SupportsIndex, final +from typing import Any, Generic, NoReturn, SupportsIndex, TypeVar, final, overload +from typing_extensions import Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -169,20 +169,12 @@ class UserString(Sequence[UserString]): def __mul__(self, n: int) -> Self: ... def __rmul__(self, n: int) -> Self: ... def __mod__(self, args: Any) -> Self: ... - if sys.version_info >= (3, 8): - def __rmod__(self, template: object) -> Self: ... - else: - def __rmod__(self, format: Any) -> Self: ... - + def __rmod__(self, template: object) -> Self: ... def capitalize(self) -> Self: ... def casefold(self) -> Self: ... def center(self, width: int, *args: Any) -> Self: ... def count(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ... - if sys.version_info >= (3, 8): - def encode(self: UserString, encoding: str | None = "utf-8", errors: str | None = "strict") -> bytes: ... - else: - def encode(self, encoding: str | None = None, errors: str | None = None) -> Self: ... - + def encode(self: UserString, encoding: str | None = "utf-8", errors: str | None = "strict") -> bytes: ... def endswith(self, suffix: str | tuple[str, ...], start: int | None = 0, end: int | None = sys.maxsize) -> bool: ... def expandtabs(self, tabsize: int = 8) -> Self: ... def find(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi index ff2e72bbf4fb..07314ce9d402 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi @@ -1,5 +1,3 @@ -import sys - from ._base import ( ALL_COMPLETED as ALL_COMPLETED, FIRST_COMPLETED as FIRST_COMPLETED, @@ -8,6 +6,7 @@ from ._base import ( CancelledError as CancelledError, Executor as Executor, Future as Future, + InvalidStateError as InvalidStateError, TimeoutError as TimeoutError, as_completed as as_completed, wait as wait, @@ -15,9 +14,6 @@ from ._base import ( from .process import ProcessPoolExecutor as ProcessPoolExecutor from .thread import ThreadPoolExecutor as ThreadPoolExecutor -if sys.version_info >= (3, 8): - from ._base import InvalidStateError as InvalidStateError - __all__ = ( "FIRST_COMPLETED", "FIRST_EXCEPTION", diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 8a11f47e5670..9ea4d5dff6fb 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -4,8 +4,8 @@ from _typeshed import Unused from collections.abc import Callable, Iterable, Iterator from logging import Logger from types import TracebackType -from typing import Any, Generic, NamedTuple, TypeVar -from typing_extensions import Literal, ParamSpec, Self +from typing import Any, Generic, Literal, NamedTuple, Protocol, TypeVar +from typing_extensions import ParamSpec, Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -30,15 +30,19 @@ if sys.version_info >= (3, 11): else: class TimeoutError(Error): ... -if sys.version_info >= (3, 8): - class InvalidStateError(Error): ... - +class InvalidStateError(Error): ... class BrokenExecutor(RuntimeError): ... _T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) _P = ParamSpec("_P") class Future(Generic[_T]): + _condition: threading.Condition + _state: str + _result: _T | None + _exception: BaseException | None + _waiters: list[_Waiter] def cancel(self) -> bool: ... def cancelled(self) -> bool: ... def running(self) -> bool: ... @@ -71,7 +75,17 @@ class Executor: self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> bool | None: ... -def as_completed(fs: Iterable[Future[_T]], timeout: float | None = None) -> Iterator[Future[_T]]: ... +class _AsCompletedFuture(Protocol[_T_co]): + # as_completed only mutates non-generic aspects of passed Futures and does not do any nominal + # checks. Therefore, we can use a Protocol here to allow as_completed to act covariantly. + # See the tests for concurrent.futures + _condition: threading.Condition + _state: str + _waiters: list[_Waiter] + # Not used by as_completed, but needed to propagate the generic type + def result(self, timeout: float | None = None) -> _T_co: ... + +def as_completed(fs: Iterable[_AsCompletedFuture[_T]], timeout: float | None = None) -> Iterator[Future[_T]]: ... class DoneAndNotDoneFutures(NamedTuple, Generic[_T]): done: set[Future[_T]] diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index e6fedb0328c2..07b57b17d56d 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import StrOrBytesPath, SupportsWrite from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence from re import Pattern -from typing import Any, ClassVar, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, ClassVar, Literal, TypeVar, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 12): __all__ = ( diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index 825c018d580f..3245cdd5dad2 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterator, Mapping -from typing import Any, ClassVar, Generic, TypeVar, overload -from typing_extensions import ParamSpec, final +from typing import Any, ClassVar, Generic, TypeVar, final, overload +from typing_extensions import ParamSpec if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index f48d9d2ff263..56f8bf029b12 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -24,15 +24,11 @@ from _csv import ( if sys.version_info >= (3, 12): from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS + from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence -from typing import Any, Generic, TypeVar, overload -from typing_extensions import Literal, Self - -if sys.version_info >= (3, 8): - from builtins import dict as _DictReadMapping -else: - from collections import OrderedDict as _DictReadMapping +from typing import Any, Generic, Literal, TypeVar, overload +from typing_extensions import Self if sys.version_info >= (3, 12): from types import GenericAlias @@ -69,7 +65,7 @@ class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... -class DictReader(Iterator[_DictReadMapping[_T | Any, str | Any]], Generic[_T]): +class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None @@ -113,7 +109,7 @@ class DictReader(Iterator[_DictReadMapping[_T | Any, str | Any]], Generic[_T]): strict: bool = False, ) -> None: ... def __iter__(self) -> Self: ... - def __next__(self) -> _DictReadMapping[_T | Any, str | Any]: ... + def __next__(self) -> dict[_T | Any, str | Any]: ... if sys.version_info >= (3, 12): def __class_getitem__(cls, item: Any) -> GenericAlias: ... @@ -139,11 +135,7 @@ class DictWriter(Generic[_T]): quoting: _QuotingType = 0, strict: bool = False, ) -> None: ... - if sys.version_info >= (3, 8): - def writeheader(self) -> Any: ... - else: - def writeheader(self) -> None: ... - + def writeheader(self) -> Any: ... def writerow(self, rowdict: Mapping[_T, Any]) -> Any: ... def writerows(self, rowdicts: Iterable[Mapping[_T, Any]]) -> None: ... if sys.version_info >= (3, 12): diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index b14fb93c8163..2fe551fa9dc2 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -3,7 +3,6 @@ from _ctypes import ( POINTER as POINTER, RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL, - ArgumentError as ArgumentError, Array as Array, CFuncPtr as _CFuncPtr, Structure as Structure, @@ -27,12 +26,16 @@ from _ctypes import ( set_errno as set_errno, sizeof as sizeof, ) +from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure from typing import Any, ClassVar, Generic, TypeVar from typing_extensions import TypeAlias if sys.platform == "win32": from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error +if sys.version_info >= (3, 11): + from ctypes._endian import BigEndianUnion as BigEndianUnion, LittleEndianUnion as LittleEndianUnion + if sys.version_info >= (3, 9): from types import GenericAlias @@ -41,32 +44,23 @@ _DLLT = TypeVar("_DLLT", bound=CDLL) DEFAULT_MODE: int +class ArgumentError(Exception): ... + class CDLL: _func_flags_: ClassVar[int] _func_restype_: ClassVar[_CData] _name: str _handle: int _FuncPtr: type[_FuncPointer] - if sys.version_info >= (3, 8): - def __init__( - self, - name: str | None, - mode: int = ..., - handle: int | None = None, - use_errno: bool = False, - use_last_error: bool = False, - winmode: int | None = None, - ) -> None: ... - else: - def __init__( - self, - name: str | None, - mode: int = ..., - handle: int | None = None, - use_errno: bool = False, - use_last_error: bool = False, - ) -> None: ... - + def __init__( + self, + name: str | None, + mode: int = ..., + handle: int | None = None, + use_errno: bool = False, + use_last_error: bool = False, + winmode: int | None = None, + ) -> None: ... def __getattr__(self, name: str) -> _NamedFuncPointer: ... def __getitem__(self, name_or_ordinal: str) -> _NamedFuncPointer: ... @@ -148,30 +142,36 @@ class c_char_p(_PointerLike, _SimpleCData[bytes | None]): def __init__(self, value: int | bytes | None = ...) -> None: ... class c_double(_SimpleCData[float]): ... -class c_longdouble(_SimpleCData[float]): ... +class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double class c_float(_SimpleCData[float]): ... -class c_int(_SimpleCData[int]): ... -class c_int8(_SimpleCData[int]): ... -class c_int16(_SimpleCData[int]): ... -class c_int32(_SimpleCData[int]): ... -class c_int64(_SimpleCData[int]): ... +class c_int(_SimpleCData[int]): ... # can be an alias for c_long class c_long(_SimpleCData[int]): ... -class c_longlong(_SimpleCData[int]): ... +class c_longlong(_SimpleCData[int]): ... # can be an alias for c_long class c_short(_SimpleCData[int]): ... -class c_size_t(_SimpleCData[int]): ... -class c_ssize_t(_SimpleCData[int]): ... +class c_size_t(_SimpleCData[int]): ... # alias for c_uint, c_ulong, or c_ulonglong +class c_ssize_t(_SimpleCData[int]): ... # alias for c_int, c_long, or c_longlong class c_ubyte(_SimpleCData[int]): ... -class c_uint(_SimpleCData[int]): ... -class c_uint8(_SimpleCData[int]): ... -class c_uint16(_SimpleCData[int]): ... -class c_uint32(_SimpleCData[int]): ... -class c_uint64(_SimpleCData[int]): ... +class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ulong(_SimpleCData[int]): ... -class c_ulonglong(_SimpleCData[int]): ... +class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ushort(_SimpleCData[int]): ... class c_void_p(_PointerLike, _SimpleCData[int | None]): ... class c_wchar(_SimpleCData[str]): ... +c_int8 = c_byte + +# these are actually dynamic aliases for c_short, c_int, c_long, or c_longlong +class c_int16(_SimpleCData[int]): ... +class c_int32(_SimpleCData[int]): ... +class c_int64(_SimpleCData[int]): ... + +c_uint8 = c_ubyte + +# these are actually dynamic aliases for c_ushort, c_uint, c_ulong, or c_ulonglong +class c_uint16(_SimpleCData[int]): ... +class c_uint32(_SimpleCData[int]): ... +class c_uint64(_SimpleCData[int]): ... + class c_wchar_p(_PointerLike, _SimpleCData[str | None]): def __init__(self, value: int | str | None = ...) -> None: ... @@ -182,8 +182,6 @@ if sys.platform == "win32": class HRESULT(_SimpleCData[int]): ... # TODO undocumented if sys.version_info >= (3, 12): - c_time_t: type[c_int32 | c_int64] + c_time_t: type[c_int32 | c_int64] # alias for one or the other at runtime class py_object(_CanCastTo, _SimpleCData[_T]): ... -class BigEndianStructure(Structure): ... -class LittleEndianStructure(Structure): ... diff --git a/mypy/typeshed/stdlib/ctypes/_endian.pyi b/mypy/typeshed/stdlib/ctypes/_endian.pyi new file mode 100644 index 000000000000..add6365e615f --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/_endian.pyi @@ -0,0 +1,19 @@ +import sys +from _ctypes import RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL, Structure, Union +from ctypes import DEFAULT_MODE as DEFAULT_MODE, cdll as cdll, pydll as pydll, pythonapi as pythonapi + +if sys.version_info >= (3, 12): + from _ctypes import SIZEOF_TIME_T as SIZEOF_TIME_T + +if sys.platform == "win32": + from ctypes import oledll as oledll, windll as windll + +# At runtime, the native endianness is an alias for Structure, +# while the other is a subclass with a metaclass added in. +class BigEndianStructure(Structure): ... +class LittleEndianStructure(Structure): ... + +# Same thing for these: one is an alias of Union at runtime +if sys.version_info >= (3, 11): + class BigEndianUnion(Union): ... + class LittleEndianUnion(Union): ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 84c9c9157a88..8847860f2002 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -186,53 +186,113 @@ class WIN32_FIND_DATAW(Structure): cFileName: _CField[Array[WCHAR], str, str] cAlternateFileName: _CField[Array[WCHAR], str, str] -class PBOOL(_Pointer[BOOL]): ... -class LPBOOL(_Pointer[BOOL]): ... -class PBOOLEAN(_Pointer[BOOLEAN]): ... +# These are all defined with the POINTER() function, which keeps a cache and will +# return a previously created class if it can. The self-reported __name__ +# of these classes is f"LP_{typ.__name__}", where typ is the original class +# passed in to the POINTER() function. + +# LP_c_short +class PSHORT(_Pointer[SHORT]): ... + +# LP_c_ushort +class PUSHORT(_Pointer[USHORT]): ... + +PWORD = PUSHORT +LPWORD = PUSHORT + +# LP_c_long +class PLONG(_Pointer[LONG]): ... + +LPLONG = PLONG +PBOOL = PLONG +LPBOOL = PLONG + +# LP_c_ulong +class PULONG(_Pointer[ULONG]): ... + +PDWORD = PULONG +LPDWORD = PDWORD +LPCOLORREF = PDWORD +PLCID = PDWORD + +# LP_c_int (or LP_c_long if int and long have the same size) +class PINT(_Pointer[INT]): ... + +LPINT = PINT + +# LP_c_uint (or LP_c_ulong if int and long have the same size) +class PUINT(_Pointer[UINT]): ... + +LPUINT = PUINT + +# LP_c_float +class PFLOAT(_Pointer[FLOAT]): ... + +# LP_c_longlong (or LP_c_long if long and long long have the same size) +class PLARGE_INTEGER(_Pointer[LARGE_INTEGER]): ... + +# LP_c_ulonglong (or LP_c_ulong if long and long long have the same size) +class PULARGE_INTEGER(_Pointer[ULARGE_INTEGER]): ... + +# LP_c_byte types class PBYTE(_Pointer[BYTE]): ... -class LPBYTE(_Pointer[BYTE]): ... + +LPBYTE = PBYTE +PBOOLEAN = PBYTE + +# LP_c_char class PCHAR(_Pointer[CHAR]): ... -class LPCOLORREF(_Pointer[COLORREF]): ... -class PDWORD(_Pointer[DWORD]): ... -class LPDWORD(_Pointer[DWORD]): ... -class PFILETIME(_Pointer[FILETIME]): ... -class LPFILETIME(_Pointer[FILETIME]): ... -class PFLOAT(_Pointer[FLOAT]): ... + +# LP_c_wchar +class PWCHAR(_Pointer[WCHAR]): ... + +# LP_c_void_p class PHANDLE(_Pointer[HANDLE]): ... -class LPHANDLE(_Pointer[HANDLE]): ... -class PHKEY(_Pointer[HKEY]): ... -class LPHKL(_Pointer[HKL]): ... -class PINT(_Pointer[INT]): ... -class LPINT(_Pointer[INT]): ... -class PLARGE_INTEGER(_Pointer[LARGE_INTEGER]): ... -class PLCID(_Pointer[LCID]): ... -class PLONG(_Pointer[LONG]): ... -class LPLONG(_Pointer[LONG]): ... + +LPHANDLE = PHANDLE +PHKEY = PHANDLE +LPHKL = PHANDLE +LPSC_HANDLE = PHANDLE + +# LP_FILETIME +class PFILETIME(_Pointer[FILETIME]): ... + +LPFILETIME = PFILETIME + +# LP_MSG class PMSG(_Pointer[MSG]): ... -class LPMSG(_Pointer[MSG]): ... + +LPMSG = PMSG + +# LP_POINT class PPOINT(_Pointer[POINT]): ... -class LPPOINT(_Pointer[POINT]): ... -class PPOINTL(_Pointer[POINTL]): ... + +LPPOINT = PPOINT +PPOINTL = PPOINT + +# LP_RECT class PRECT(_Pointer[RECT]): ... -class LPRECT(_Pointer[RECT]): ... -class PRECTL(_Pointer[RECTL]): ... -class LPRECTL(_Pointer[RECTL]): ... -class LPSC_HANDLE(_Pointer[SC_HANDLE]): ... -class PSHORT(_Pointer[SHORT]): ... + +LPRECT = PRECT +PRECTL = PRECT +LPRECTL = PRECT + +# LP_SIZE class PSIZE(_Pointer[SIZE]): ... -class LPSIZE(_Pointer[SIZE]): ... -class PSIZEL(_Pointer[SIZEL]): ... -class LPSIZEL(_Pointer[SIZEL]): ... + +LPSIZE = PSIZE +PSIZEL = PSIZE +LPSIZEL = PSIZE + +# LP__SMALL_RECT class PSMALL_RECT(_Pointer[SMALL_RECT]): ... -class PUINT(_Pointer[UINT]): ... -class LPUINT(_Pointer[UINT]): ... -class PULARGE_INTEGER(_Pointer[ULARGE_INTEGER]): ... -class PULONG(_Pointer[ULONG]): ... -class PUSHORT(_Pointer[USHORT]): ... -class PWCHAR(_Pointer[WCHAR]): ... + +# LP_WIN32_FIND_DATAA class PWIN32_FIND_DATAA(_Pointer[WIN32_FIND_DATAA]): ... -class LPWIN32_FIND_DATAA(_Pointer[WIN32_FIND_DATAA]): ... + +LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA + +# LP_WIN32_FIND_DATAW class PWIN32_FIND_DATAW(_Pointer[WIN32_FIND_DATAW]): ... -class LPWIN32_FIND_DATAW(_Pointer[WIN32_FIND_DATAW]): ... -class PWORD(_Pointer[WORD]): ... -class LPWORD(_Pointer[WORD]): ... + +LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 13cffcd70c0e..389a159a915f 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -4,8 +4,8 @@ import types from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping -from typing import Any, Generic, Protocol, TypeVar, overload -from typing_extensions import Literal, TypeAlias, TypeGuard +from typing import Any, Generic, Literal, Protocol, TypeVar, overload +from typing_extensions import TypeAlias, TypeGuard if sys.version_info >= (3, 9): from types import GenericAlias @@ -54,19 +54,10 @@ def asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, An def astuple(obj: DataclassInstance) -> tuple[Any, ...]: ... @overload def astuple(obj: DataclassInstance, *, tuple_factory: Callable[[list[Any]], _T]) -> _T: ... - -if sys.version_info >= (3, 8): - # cls argument is now positional-only - @overload - def dataclass(__cls: None) -> Callable[[type[_T]], type[_T]]: ... - @overload - def dataclass(__cls: type[_T]) -> type[_T]: ... - -else: - @overload - def dataclass(_cls: None) -> Callable[[type[_T]], type[_T]]: ... - @overload - def dataclass(_cls: type[_T]) -> type[_T]: ... +@overload +def dataclass(__cls: None) -> Callable[[type[_T]], type[_T]]: ... +@overload +def dataclass(__cls: type[_T]) -> type[_T]: ... if sys.version_info >= (3, 11): @overload diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 36577c5b7e1b..54ecddec3a9a 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,8 +1,8 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, NamedTuple, NoReturn, TypeVar, overload -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias, final +from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 11): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") @@ -60,10 +60,8 @@ class date: def fromordinal(cls, __n: int) -> Self: ... @classmethod def fromisoformat(cls, __date_string: str) -> Self: ... - if sys.version_info >= (3, 8): - @classmethod - def fromisocalendar(cls, year: int, week: int, day: int) -> Self: ... - + @classmethod + def fromisocalendar(cls, year: int, week: int, day: int) -> Self: ... @property def year(self) -> int: ... @property @@ -89,26 +87,14 @@ class date: def __ge__(self, __value: date) -> bool: ... def __gt__(self, __value: date) -> bool: ... def __eq__(self, __value: object) -> bool: ... - if sys.version_info >= (3, 8): - def __add__(self, __value: timedelta) -> Self: ... - def __radd__(self, __value: timedelta) -> Self: ... - @overload - def __sub__(self, __value: timedelta) -> Self: ... - @overload - def __sub__(self, __value: datetime) -> NoReturn: ... - @overload - def __sub__(self: _D, __value: _D) -> timedelta: ... - else: - # Prior to Python 3.8, arithmetic operations always returned `date`, even in subclasses - def __add__(self, __value: timedelta) -> date: ... - def __radd__(self, __value: timedelta) -> date: ... - @overload - def __sub__(self, __value: timedelta) -> date: ... - @overload - def __sub__(self, __value: datetime) -> NoReturn: ... - @overload - def __sub__(self, __value: date) -> timedelta: ... - + def __add__(self, __value: timedelta) -> Self: ... + def __radd__(self, __value: timedelta) -> Self: ... + @overload + def __sub__(self, __value: timedelta) -> Self: ... + @overload + def __sub__(self, __value: datetime) -> NoReturn: ... + @overload + def __sub__(self: _D, __value: _D) -> timedelta: ... def __hash__(self) -> int: ... def weekday(self) -> int: ... def isoweekday(self) -> int: ... @@ -266,17 +252,8 @@ class datetime(date): @classmethod def utcfromtimestamp(cls, __t: float) -> Self: ... - if sys.version_info >= (3, 8): - @classmethod - def now(cls, tz: _TzInfo | None = None) -> Self: ... - else: - @overload - @classmethod - def now(cls, tz: None = None) -> Self: ... - @overload - @classmethod - def now(cls, tz: _TzInfo) -> datetime: ... - + @classmethod + def now(cls, tz: _TzInfo | None = None) -> Self: ... @classmethod def utcnow(cls) -> Self: ... @classmethod @@ -299,11 +276,7 @@ class datetime(date): *, fold: int = ..., ) -> Self: ... - if sys.version_info >= (3, 8): - def astimezone(self, tz: _TzInfo | None = ...) -> Self: ... - else: - def astimezone(self, tz: _TzInfo | None = ...) -> datetime: ... - + def astimezone(self, tz: _TzInfo | None = ...) -> Self: ... def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... @classmethod def strptime(cls, __date_string: str, __format: str) -> Self: ... @@ -316,16 +289,7 @@ class datetime(date): def __gt__(self, __value: datetime) -> bool: ... # type: ignore[override] def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... - if sys.version_info >= (3, 8): - @overload # type: ignore[override] - def __sub__(self, __value: timedelta) -> Self: ... - @overload - def __sub__(self: _D, __value: _D) -> timedelta: ... - else: - # Prior to Python 3.8, arithmetic operations always returned `datetime`, even in subclasses - def __add__(self, __value: timedelta) -> datetime: ... - def __radd__(self, __value: timedelta) -> datetime: ... - @overload # type: ignore[override] - def __sub__(self, __value: datetime) -> timedelta: ... - @overload - def __sub__(self, __value: timedelta) -> datetime: ... + @overload # type: ignore[override] + def __sub__(self, __value: timedelta) -> Self: ... + @overload + def __sub__(self: _D, __value: _D) -> timedelta: ... diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index d7115528868b..9c6989a1c151 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -1,6 +1,7 @@ from collections.abc import Iterator, MutableMapping from types import TracebackType -from typing_extensions import Literal, Self, TypeAlias +from typing import Literal +from typing_extensions import Self, TypeAlias __all__ = ["open", "whichdb", "error"] diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index e7277aa3f9c4..cc097728f77c 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -56,7 +56,7 @@ class CCompiler: self, sources: list[str], output_dir: str | None = None, - macros: _Macro | None = None, + macros: list[_Macro] | None = None, include_dirs: list[str] | None = None, debug: bool = ..., extra_preargs: list[str] | None = None, diff --git a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi index a990c3e28f36..5f2e623eeff6 100644 --- a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi @@ -1,7 +1,7 @@ from distutils.unixccompiler import UnixCCompiler from distutils.version import LooseVersion from re import Pattern -from typing_extensions import Literal +from typing import Literal def get_msvcr() -> list[str] | None: ... diff --git a/mypy/typeshed/stdlib/distutils/filelist.pyi b/mypy/typeshed/stdlib/distutils/filelist.pyi index bea48ac16ac5..25db2f3cb6cc 100644 --- a/mypy/typeshed/stdlib/distutils/filelist.pyi +++ b/mypy/typeshed/stdlib/distutils/filelist.pyi @@ -1,7 +1,6 @@ from collections.abc import Iterable from re import Pattern -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload # class is entirely undocumented class FileList: diff --git a/mypy/typeshed/stdlib/distutils/util.pyi b/mypy/typeshed/stdlib/distutils/util.pyi index 83b03747fda6..835266edde59 100644 --- a/mypy/typeshed/stdlib/distutils/util.pyi +++ b/mypy/typeshed/stdlib/distutils/util.pyi @@ -1,12 +1,8 @@ -import sys from _typeshed import StrPath, Unused from collections.abc import Callable, Container, Iterable, Mapping -from typing import Any -from typing_extensions import Literal - -if sys.version_info >= (3, 8): - def get_host_platform() -> str: ... +from typing import Any, Literal +def get_host_platform() -> str: ... def get_platform() -> str: ... def convert_path(pathname: str) -> str: ... def change_root(new_root: str, pathname: str) -> str: ... diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index 97008140ec5d..806fc84cf784 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -1,10 +1,9 @@ -import sys from collections.abc import Iterable, Iterator from email.errors import HeaderParseError, MessageDefect from email.policy import Policy from re import Pattern -from typing import Any -from typing_extensions import Final, Self +from typing import Any, Final +from typing_extensions import Self WSP: Final[set[str]] CFWS_LEADER: Final[set[str]] @@ -195,10 +194,9 @@ class DotAtomText(TokenList): token_type: str as_ew_allowed: bool -if sys.version_info >= (3, 8): - class NoFoldLiteral(TokenList): - token_type: str - as_ew_allowed: bool +class NoFoldLiteral(TokenList): + token_type: str + as_ew_allowed: bool class AddrSpec(TokenList): token_type: str @@ -296,17 +294,16 @@ class HeaderLabel(TokenList): token_type: str as_ew_allowed: bool -if sys.version_info >= (3, 8): - class MsgID(TokenList): - token_type: str - as_ew_allowed: bool - def fold(self, policy: Policy) -> str: ... +class MsgID(TokenList): + token_type: str + as_ew_allowed: bool + def fold(self, policy: Policy) -> str: ... - class MessageID(MsgID): - token_type: str +class MessageID(MsgID): + token_type: str - class InvalidMessageID(MessageID): - token_type: str +class InvalidMessageID(MessageID): + token_type: str class Header(TokenList): token_type: str @@ -375,12 +372,9 @@ def get_group_list(value: str) -> tuple[GroupList, str]: ... def get_group(value: str) -> tuple[Group, str]: ... def get_address(value: str) -> tuple[Address, str]: ... def get_address_list(value: str) -> tuple[AddressList, str]: ... - -if sys.version_info >= (3, 8): - def get_no_fold_literal(value: str) -> tuple[NoFoldLiteral, str]: ... - def get_msg_id(value: str) -> tuple[MsgID, str]: ... - def parse_message_id(value: str) -> MessageID: ... - +def get_no_fold_literal(value: str) -> tuple[NoFoldLiteral, str]: ... +def get_msg_id(value: str) -> tuple[MsgID, str]: ... +def parse_message_id(value: str) -> MessageID: ... def parse_mime_version(value: str) -> MIMEVersion: ... def get_invalid_parameter(value: str) -> tuple[InvalidParameter, str]: ... def get_ttext(value: str) -> tuple[ValueTerminal, str]: ... diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi index 94623e96f208..93a2b3ee72b5 100644 --- a/mypy/typeshed/stdlib/email/headerregistry.pyi +++ b/mypy/typeshed/stdlib/email/headerregistry.pyi @@ -1,4 +1,3 @@ -import sys import types from collections.abc import Iterable, Mapping from datetime import datetime as _datetime @@ -7,14 +6,15 @@ from email._header_value_parser import ( ContentDisposition, ContentTransferEncoding, ContentType, + MessageID, MIMEVersion, TokenList, UnstructuredTokenList, ) from email.errors import MessageDefect from email.policy import Policy -from typing import Any, ClassVar, Protocol -from typing_extensions import Literal, Self +from typing import Any, ClassVar, Literal, Protocol +from typing_extensions import Self class BaseHeader(str): # max_count is actually more of an abstract ClassVar (not defined on the base class, but expected to be defined in subclasses) @@ -130,15 +130,12 @@ class ContentTransferEncodingHeader: @staticmethod def value_parser(value: str) -> ContentTransferEncoding: ... -if sys.version_info >= (3, 8): - from email._header_value_parser import MessageID - - class MessageIDHeader: - max_count: ClassVar[Literal[1]] - @classmethod - def parse(cls, value: str, kwds: dict[str, Any]) -> None: ... - @staticmethod - def value_parser(value: str) -> MessageID: ... +class MessageIDHeader: + max_count: ClassVar[Literal[1]] + @classmethod + def parse(cls, value: str, kwds: dict[str, Any]) -> None: ... + @staticmethod + def value_parser(value: str) -> MessageID: ... class _HeaderParser(Protocol): max_count: ClassVar[Literal[1] | None] diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 71c8da3a6a5a..7384f3146a8e 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -5,8 +5,8 @@ from email.contentmanager import ContentManager from email.errors import MessageDefect from email.header import Header from email.policy import Policy -from typing import Any, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Literal, Protocol, TypeVar, overload +from typing_extensions import Self, TypeAlias __all__ = ["Message", "EmailMessage"] diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 10ea19257144..42d0c19d39e7 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -4,8 +4,8 @@ import types from _typeshed import SupportsKeysAndGetItem, Unused from builtins import property as _builtins_property from collections.abc import Callable, Iterable, Iterator, Mapping -from typing import Any, Generic, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Generic, Literal, TypeVar, overload +from typing_extensions import Self, TypeAlias __all__ = ["EnumMeta", "Enum", "IntEnum", "Flag", "IntFlag", "auto", "unique"] diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 56fd5679a1c8..7e5af76c2aae 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import FileDescriptorLike, ReadOnlyBuffer, WriteableBuffer -from typing import Any, overload -from typing_extensions import Buffer, Literal +from typing import Any, Literal, overload +from typing_extensions import Buffer if sys.platform != "win32": FASYNC: int @@ -37,13 +37,12 @@ if sys.platform != "win32": F_NOTIFY: int F_EXLCK: int F_GETLK64: int - if sys.version_info >= (3, 8): - F_ADD_SEALS: int - F_GET_SEALS: int - F_SEAL_GROW: int - F_SEAL_SEAL: int - F_SEAL_SHRINK: int - F_SEAL_WRITE: int + F_ADD_SEALS: int + F_GET_SEALS: int + F_SEAL_GROW: int + F_SEAL_SEAL: int + F_SEAL_SHRINK: int + F_SEAL_WRITE: int if sys.version_info >= (3, 9): F_OFD_GETLK: int F_OFD_SETLK: int diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index 008d7a44e6c4..4f54a9bff6ee 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -1,8 +1,7 @@ import sys from _typeshed import GenericPath, StrOrBytesPath from collections.abc import Callable, Iterable, Sequence -from typing import Any, AnyStr, Generic -from typing_extensions import Literal +from typing import Any, AnyStr, Generic, Literal if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index c2fd31d1ea77..e8d5dd8d2d5b 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath from collections.abc import Callable, Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Protocol, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, AnyStr, Literal, Protocol, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -68,7 +68,7 @@ if sys.version_info >= (3, 10): errors: str | None = None, ) -> FileInput[Any]: ... -elif sys.version_info >= (3, 8): +else: # bufsize is dropped and mode and openhook become keyword-only @overload def input( @@ -98,57 +98,6 @@ elif sys.version_info >= (3, 8): openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, ) -> FileInput[Any]: ... -else: - @overload - def input( - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - mode: _TextMode = "r", - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[str]] | None = None, - ) -> FileInput[str]: ... - # Because mode isn't keyword-only here yet, we need two overloads each for - # the bytes case and the fallback case. - @overload - def input( - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - *, - mode: Literal["rb"], - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[bytes]] | None = None, - ) -> FileInput[bytes]: ... - @overload - def input( - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None, - inplace: bool, - backup: str, - bufsize: int, - mode: Literal["rb"], - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[bytes]] | None = None, - ) -> FileInput[bytes]: ... - @overload - def input( - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - *, - mode: str, - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, - ) -> FileInput[Any]: ... - @overload - def input( - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None, - inplace: bool, - backup: str, - bufsize: int, - mode: str, - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, - ) -> FileInput[Any]: ... - def close() -> None: ... def nextfile() -> None: ... def filename() -> str: ... @@ -198,7 +147,7 @@ class FileInput(Iterator[AnyStr]): errors: str | None = None, ) -> None: ... - elif sys.version_info >= (3, 8): + else: # bufsize is dropped and mode and openhook become keyword-only @overload def __init__( @@ -231,62 +180,6 @@ class FileInput(Iterator[AnyStr]): openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, ) -> None: ... - else: - @overload - def __init__( - self: FileInput[str], - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - mode: _TextMode = "r", - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[str]] | None = None, - ) -> None: ... - # Because mode isn't keyword-only here yet, we need two overloads each for - # the bytes case and the fallback case. - @overload - def __init__( - self: FileInput[bytes], - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - *, - mode: Literal["rb"], - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[bytes]] | None = None, - ) -> None: ... - @overload - def __init__( - self: FileInput[bytes], - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None, - inplace: bool, - backup: str, - bufsize: int, - mode: Literal["rb"], - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[bytes]] | None = None, - ) -> None: ... - @overload - def __init__( - self: FileInput[Any], - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None = None, - inplace: bool = False, - backup: str = "", - bufsize: int = 0, - *, - mode: str, - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, - ) -> None: ... - @overload - def __init__( - self: FileInput[Any], - files: StrOrBytesPath | Iterable[StrOrBytesPath] | None, - inplace: bool, - backup: str, - bufsize: int, - mode: str, - openhook: Callable[[StrOrBytesPath, str], _HasReadlineAndFileno[Any]] | None = None, - ) -> None: ... - def __del__(self) -> None: ... def close(self) -> None: ... def __enter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 7ec8addeb136..43eaa21bd039 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -2,8 +2,8 @@ import sys from collections.abc import Callable from decimal import Decimal from numbers import Integral, Rational, Real -from typing import Any, overload -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias +from typing import Any, Literal, SupportsIndex, overload +from typing_extensions import Self, TypeAlias _ComparableNum: TypeAlias = int | float | Decimal | Real @@ -30,8 +30,7 @@ class Fraction(Rational): @classmethod def from_decimal(cls, dec: Decimal) -> Self: ... def limit_denominator(self, max_denominator: int = 1000000) -> Fraction: ... - if sys.version_info >= (3, 8): - def as_integer_ratio(self) -> tuple[int, int]: ... + def as_integer_ratio(self) -> tuple[int, int]: ... if sys.version_info >= (3, 12): def is_integer(self) -> bool: ... @@ -103,25 +102,14 @@ class Fraction(Rational): def __rmod__(b, a: int | Fraction) -> Fraction: ... @overload def __rmod__(b, a: float) -> float: ... - if sys.version_info >= (3, 8): - @overload - def __divmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ... - @overload - def __divmod__(a, b: float) -> tuple[float, Fraction]: ... - @overload - def __rdivmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ... - @overload - def __rdivmod__(a, b: float) -> tuple[float, Fraction]: ... - else: - @overload - def __divmod__(self, other: int | Fraction) -> tuple[int, Fraction]: ... - @overload - def __divmod__(self, other: float) -> tuple[float, Fraction]: ... - @overload - def __rdivmod__(self, other: int | Fraction) -> tuple[int, Fraction]: ... - @overload - def __rdivmod__(self, other: float) -> tuple[float, Fraction]: ... - + @overload + def __divmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ... + @overload + def __divmod__(a, b: float) -> tuple[float, Fraction]: ... + @overload + def __rdivmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ... + @overload + def __rdivmod__(a, b: float) -> tuple[float, Fraction]: ... @overload def __pow__(a, b: int) -> Fraction: ... @overload diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 2d2ffa9aff03..3bc03a0ff121 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -4,8 +4,8 @@ from collections.abc import Callable, Iterable, Iterator from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, TextIO -from typing_extensions import Literal, Self +from typing import Any, Literal, TextIO +from typing_extensions import Self __all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", "all_errors", "FTP_TLS"] diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 4d8c45e96103..0f1666024f84 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -2,8 +2,8 @@ import sys import types from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized -from typing import Any, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias, TypedDict, final +from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -20,11 +20,10 @@ __all__ = [ "partial", "partialmethod", "singledispatch", + "cached_property", + "singledispatchmethod", ] -if sys.version_info >= (3, 8): - __all__ += ["cached_property", "singledispatchmethod"] - if sys.version_info >= (3, 9): __all__ += ["cache"] @@ -61,14 +60,10 @@ class _lru_cache_wrapper(Generic[_T]): def __copy__(self) -> _lru_cache_wrapper[_T]: ... def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_T]: ... -if sys.version_info >= (3, 8): - @overload - def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... - @overload - def lru_cache(maxsize: Callable[..., _T], typed: bool = False) -> _lru_cache_wrapper[_T]: ... - -else: - def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... +@overload +def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... +@overload +def lru_cache(maxsize: Callable[..., _T], typed: bool = False) -> _lru_cache_wrapper[_T]: ... if sys.version_info >= (3, 12): WRAPPER_ASSIGNMENTS: tuple[ @@ -137,11 +132,7 @@ class partialmethod(Generic[_T]): def __init__(self, __func: Callable[..., _T], *args: Any, **keywords: Any) -> None: ... @overload def __init__(self, __func: _Descriptor, *args: Any, **keywords: Any) -> None: ... - if sys.version_info >= (3, 8): - def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ... - else: - def __get__(self, obj: Any, cls: type[Any] | None) -> Callable[..., _T]: ... - + def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ... @property def __isabstractmethod__(self) -> bool: ... if sys.version_info >= (3, 9): @@ -166,34 +157,33 @@ class _SingleDispatchCallable(Generic[_T]): def singledispatch(func: Callable[..., _T]) -> _SingleDispatchCallable[_T]: ... -if sys.version_info >= (3, 8): - class singledispatchmethod(Generic[_T]): - dispatcher: _SingleDispatchCallable[_T] - func: Callable[..., _T] - def __init__(self, func: Callable[..., _T]) -> None: ... - @property - def __isabstractmethod__(self) -> bool: ... - @overload - def register(self, cls: type[Any], method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... - @overload - def register(self, cls: Callable[..., _T], method: None = None) -> Callable[..., _T]: ... - @overload - def register(self, cls: type[Any], method: Callable[..., _T]) -> Callable[..., _T]: ... - def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... - - class cached_property(Generic[_T]): - func: Callable[[Any], _T] - attrname: str | None - def __init__(self, func: Callable[[Any], _T]) -> None: ... - @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... - @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... - def __set_name__(self, owner: type[Any], name: str) -> None: ... - # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... +class singledispatchmethod(Generic[_T]): + dispatcher: _SingleDispatchCallable[_T] + func: Callable[..., _T] + def __init__(self, func: Callable[..., _T]) -> None: ... + @property + def __isabstractmethod__(self) -> bool: ... + @overload + def register(self, cls: type[Any], method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + @overload + def register(self, cls: Callable[..., _T], method: None = None) -> Callable[..., _T]: ... + @overload + def register(self, cls: type[Any], method: Callable[..., _T]) -> Callable[..., _T]: ... + def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... + +class cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + def __init__(self, func: Callable[[Any], _T]) -> None: ... + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + def __set_name__(self, owner: type[Any], name: str) -> None: ... + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any) -> GenericAlias: ... if sys.version_info >= (3, 9): def cache(__user_function: Callable[..., _T]) -> _lru_cache_wrapper[_T]: ... diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 27cee726ba09..914c41434791 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable -from typing import Any -from typing_extensions import Literal, TypeAlias +from typing import Any, Literal +from typing_extensions import TypeAlias DEBUG_COLLECTABLE: Literal[2] DEBUG_LEAK: Literal[38] @@ -19,13 +19,7 @@ def disable() -> None: ... def enable() -> None: ... def get_count() -> tuple[int, int, int]: ... def get_debug() -> int: ... - -if sys.version_info >= (3, 8): - def get_objects(generation: int | None = None) -> list[Any]: ... - -else: - def get_objects() -> list[Any]: ... - +def get_objects(generation: int | None = None) -> list[Any]: ... def freeze() -> None: ... def unfreeze() -> None: ... def get_freeze_count() -> int: ... diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi index be08f7a3cb79..0dd5dec4b2ec 100644 --- a/mypy/typeshed/stdlib/genericpath.pyi +++ b/mypy/typeshed/stdlib/genericpath.pyi @@ -2,8 +2,8 @@ import os import sys from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRichComparisonT from collections.abc import Sequence -from typing import overload -from typing_extensions import Literal, LiteralString +from typing import Literal, overload +from typing_extensions import LiteralString __all__ = [ "commonprefix", diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index 57e81120b8ca..4c17c0dc5de4 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -2,8 +2,7 @@ import io import sys from _typeshed import StrPath from collections.abc import Callable, Container, Iterable, Sequence -from typing import Any, Protocol, TypeVar, overload -from typing_extensions import Final, Literal +from typing import Any, Final, Literal, Protocol, TypeVar, overload __all__ = [ "NullTranslations", @@ -18,14 +17,15 @@ __all__ = [ "dngettext", "gettext", "ngettext", + "dnpgettext", + "dpgettext", + "npgettext", + "pgettext", ] if sys.version_info < (3, 11): __all__ += ["bind_textdomain_codeset", "ldgettext", "ldngettext", "lgettext", "lngettext"] -if sys.version_info >= (3, 8): - __all__ += ["dnpgettext", "dpgettext", "npgettext", "pgettext"] - class _TranslationsReader(Protocol): def read(self) -> bytes: ... # optional: @@ -37,10 +37,8 @@ class NullTranslations: def add_fallback(self, fallback: NullTranslations) -> None: ... def gettext(self, message: str) -> str: ... def ngettext(self, msgid1: str, msgid2: str, n: int) -> str: ... - if sys.version_info >= (3, 8): - def pgettext(self, context: str, message: str) -> str: ... - def npgettext(self, context: str, msgid1: str, msgid2: str, n: int) -> str: ... - + def pgettext(self, context: str, message: str) -> str: ... + def npgettext(self, context: str, msgid1: str, msgid2: str, n: int) -> str: ... def info(self) -> dict[str, str]: ... def charset(self) -> str | None: ... if sys.version_info < (3, 11): @@ -156,12 +154,10 @@ def dgettext(domain: str, message: str) -> str: ... def dngettext(domain: str, msgid1: str, msgid2: str, n: int) -> str: ... def gettext(message: str) -> str: ... def ngettext(msgid1: str, msgid2: str, n: int) -> str: ... - -if sys.version_info >= (3, 8): - def pgettext(context: str, message: str) -> str: ... - def dpgettext(domain: str, context: str, message: str) -> str: ... - def npgettext(context: str, msgid1: str, msgid2: str, n: int) -> str: ... - def dnpgettext(domain: str, context: str, msgid1: str, msgid2: str, n: int) -> str: ... +def pgettext(context: str, message: str) -> str: ... +def dpgettext(domain: str, context: str, message: str) -> str: ... +def npgettext(context: str, msgid1: str, msgid2: str, n: int) -> str: ... +def dnpgettext(domain: str, context: str, msgid1: str, msgid2: str, n: int) -> str: ... if sys.version_info < (3, 11): def lgettext(message: str) -> str: ... diff --git a/mypy/typeshed/stdlib/grp.pyi b/mypy/typeshed/stdlib/grp.pyi index 4b66b84b6389..bb0d65180918 100644 --- a/mypy/typeshed/stdlib/grp.pyi +++ b/mypy/typeshed/stdlib/grp.pyi @@ -1,7 +1,6 @@ import sys from _typeshed import structseq -from typing import Any -from typing_extensions import Final, final +from typing import Any, Final, final if sys.platform != "win32": @final diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index d001849e609c..2c33d7cf5810 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -3,13 +3,10 @@ import sys import zlib from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath from io import FileIO -from typing import Protocol, TextIO, overload -from typing_extensions import Literal, TypeAlias +from typing import Literal, Protocol, TextIO, overload +from typing_extensions import TypeAlias -if sys.version_info >= (3, 8): - __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] -else: - __all__ = ["GzipFile", "open", "compress", "decompress"] +__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] _ReadBinaryMode: TypeAlias = Literal["r", "rb"] _WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"] @@ -85,8 +82,7 @@ class _PaddedFile: def seek(self, off: int) -> int: ... def seekable(self) -> bool: ... -if sys.version_info >= (3, 8): - class BadGzipFile(OSError): ... +class BadGzipFile(OSError): ... class GzipFile(_compression.BaseStream): myfileobj: FileIO | None @@ -160,10 +156,5 @@ class GzipFile(_compression.BaseStream): class _GzipReader(_compression.DecompressReader): def __init__(self, fp: _ReadableFileobj) -> None: ... -if sys.version_info >= (3, 8): - def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ... - -else: - def compress(data: SizedBuffer, compresslevel: int = 9) -> bytes: ... - +def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ... def decompress(data: ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index ed1321f23b9e..38e846ab7eb3 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Callable, Set as AbstractSet -from typing import Protocol -from typing_extensions import Self, final +from typing import Protocol, final +from typing_extensions import Self if sys.version_info >= (3, 11): __all__ = ( @@ -70,7 +70,7 @@ if sys.version_info >= (3, 9): def sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... def sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... -elif sys.version_info >= (3, 8): +else: def new(name: str, data: ReadableBuffer = b"") -> _Hash: ... def md5(string: ReadableBuffer = b"") -> _Hash: ... def sha1(string: ReadableBuffer = b"") -> _Hash: ... @@ -79,15 +79,6 @@ elif sys.version_info >= (3, 8): def sha384(string: ReadableBuffer = b"") -> _Hash: ... def sha512(string: ReadableBuffer = b"") -> _Hash: ... -else: - def new(name: str, data: ReadableBuffer = b"") -> _Hash: ... - def md5(__string: ReadableBuffer = ...) -> _Hash: ... - def sha1(__string: ReadableBuffer = ...) -> _Hash: ... - def sha224(__string: ReadableBuffer = ...) -> _Hash: ... - def sha256(__string: ReadableBuffer = ...) -> _Hash: ... - def sha384(__string: ReadableBuffer = ...) -> _Hash: ... - def sha512(__string: ReadableBuffer = ...) -> _Hash: ... - algorithms_guaranteed: AbstractSet[str] algorithms_available: AbstractSet[str] diff --git a/mypy/typeshed/stdlib/heapq.pyi b/mypy/typeshed/stdlib/heapq.pyi index 3f4f274b9769..9198febd3cfa 100644 --- a/mypy/typeshed/stdlib/heapq.pyi +++ b/mypy/typeshed/stdlib/heapq.pyi @@ -1,8 +1,7 @@ from _heapq import * from _typeshed import SupportsRichComparison from collections.abc import Callable, Iterable -from typing import Any, TypeVar -from typing_extensions import Final +from typing import Any, Final, TypeVar __all__ = ["heappush", "heappop", "heapify", "heapreplace", "merge", "nlargest", "nsmallest", "heappushpop"] diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index 9ff99a5a05d5..614121529416 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable from types import ModuleType @@ -14,29 +13,19 @@ trans_36: bytes digest_size: None -if sys.version_info >= (3, 8): - # In reality digestmod has a default value, but the function always throws an error - # if the argument is not given, so we pretend it is a required argument. - @overload - def new(key: bytes | bytearray, msg: ReadableBuffer | None, digestmod: _DigestMod) -> HMAC: ... - @overload - def new(key: bytes | bytearray, *, digestmod: _DigestMod) -> HMAC: ... - -else: - def new(key: bytes | bytearray, msg: ReadableBuffer | None = None, digestmod: _DigestMod | None = None) -> HMAC: ... +# In reality digestmod has a default value, but the function always throws an error +# if the argument is not given, so we pretend it is a required argument. +@overload +def new(key: bytes | bytearray, msg: ReadableBuffer | None, digestmod: _DigestMod) -> HMAC: ... +@overload +def new(key: bytes | bytearray, *, digestmod: _DigestMod) -> HMAC: ... class HMAC: digest_size: int block_size: int @property def name(self) -> str: ... - if sys.version_info >= (3, 8): - def __init__(self, key: bytes | bytearray, msg: ReadableBuffer | None = None, digestmod: _DigestMod = "") -> None: ... - else: - def __init__( - self, key: bytes | bytearray, msg: ReadableBuffer | None = None, digestmod: _DigestMod | None = None - ) -> None: ... - + def __init__(self, key: bytes | bytearray, msg: ReadableBuffer | None = None, digestmod: _DigestMod = "") -> None: ... def update(self, msg: ReadableBuffer) -> None: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index 4310c79b9269..2eee06fdaaa9 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -1,6 +1,6 @@ import sys from enum import IntEnum -from typing_extensions import Literal +from typing import Literal if sys.version_info >= (3, 11): from enum import StrEnum @@ -73,8 +73,7 @@ class HTTPStatus(IntEnum): NOT_EXTENDED: int NETWORK_AUTHENTICATION_REQUIRED: int MISDIRECTED_REQUEST: int - if sys.version_info >= (3, 8): - UNAVAILABLE_FOR_LEGAL_REASONS: int + UNAVAILABLE_FOR_LEGAL_REASONS: int if sys.version_info >= (3, 9): EARLY_HINTS: Literal[103] IM_A_TEAPOT: Literal[418] diff --git a/mypy/typeshed/stdlib/http/cookiejar.pyi b/mypy/typeshed/stdlib/http/cookiejar.pyi index 482cbca1d88a..faac20d13125 100644 --- a/mypy/typeshed/stdlib/http/cookiejar.pyi +++ b/mypy/typeshed/stdlib/http/cookiejar.pyi @@ -44,13 +44,7 @@ class CookieJar(Iterable[Cookie]): class FileCookieJar(CookieJar): filename: str delayload: bool - if sys.version_info >= (3, 8): - def __init__( - self, filename: StrPath | None = None, delayload: bool = False, policy: CookiePolicy | None = None - ) -> None: ... - else: - def __init__(self, filename: str | None = None, delayload: bool = False, policy: CookiePolicy | None = None) -> None: ... - + def __init__(self, filename: StrPath | None = None, delayload: bool = False, policy: CookiePolicy | None = None) -> None: ... def save(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ... def load(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ... def revert(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ... @@ -84,40 +78,22 @@ class DefaultCookiePolicy(CookiePolicy): DomainRFC2965Match: ClassVar[int] DomainLiberal: ClassVar[int] DomainStrict: ClassVar[int] - if sys.version_info >= (3, 8): - def __init__( - self, - blocked_domains: Sequence[str] | None = None, - allowed_domains: Sequence[str] | None = None, - netscape: bool = True, - rfc2965: bool = False, - rfc2109_as_netscape: bool | None = None, - hide_cookie2: bool = False, - strict_domain: bool = False, - strict_rfc2965_unverifiable: bool = True, - strict_ns_unverifiable: bool = False, - strict_ns_domain: int = 0, - strict_ns_set_initial_dollar: bool = False, - strict_ns_set_path: bool = False, - secure_protocols: Sequence[str] = ("https", "wss"), - ) -> None: ... - else: - def __init__( - self, - blocked_domains: Sequence[str] | None = None, - allowed_domains: Sequence[str] | None = None, - netscape: bool = True, - rfc2965: bool = False, - rfc2109_as_netscape: bool | None = None, - hide_cookie2: bool = False, - strict_domain: bool = False, - strict_rfc2965_unverifiable: bool = True, - strict_ns_unverifiable: bool = False, - strict_ns_domain: int = 0, - strict_ns_set_initial_dollar: bool = False, - strict_ns_set_path: bool = False, - ) -> None: ... - + def __init__( + self, + blocked_domains: Sequence[str] | None = None, + allowed_domains: Sequence[str] | None = None, + netscape: bool = True, + rfc2965: bool = False, + rfc2109_as_netscape: bool | None = None, + hide_cookie2: bool = False, + strict_domain: bool = False, + strict_rfc2965_unverifiable: bool = True, + strict_ns_unverifiable: bool = False, + strict_ns_domain: int = 0, + strict_ns_set_initial_dollar: bool = False, + strict_ns_set_path: bool = False, + secure_protocols: Sequence[str] = ("https", "wss"), + ) -> None: ... def blocked_domains(self) -> tuple[str, ...]: ... def set_blocked_domains(self, blocked_domains: Sequence[str]) -> None: ... def is_blocked(self, domain: str) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi index 22c33bc3787a..07cde553c1df 100644 --- a/mypy/typeshed/stdlib/http/server.pyi +++ b/mypy/typeshed/stdlib/http/server.pyi @@ -45,7 +45,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): def log_error(self, format: str, *args: Any) -> None: ... def log_message(self, format: str, *args: Any) -> None: ... def version_string(self) -> str: ... - def date_time_string(self, timestamp: int | None = None) -> str: ... + def date_time_string(self, timestamp: float | None = None) -> str: ... def log_date_time_string(self) -> str: ... def address_string(self) -> str: ... def parse_request(self) -> bool: ... # undocumented diff --git a/mypy/typeshed/stdlib/imaplib.pyi b/mypy/typeshed/stdlib/imaplib.pyi index a61848c9af13..6a4d8b2e720a 100644 --- a/mypy/typeshed/stdlib/imaplib.pyi +++ b/mypy/typeshed/stdlib/imaplib.pyi @@ -9,8 +9,8 @@ from re import Pattern from socket import socket as _socket from ssl import SSLContext, SSLSocket from types import TracebackType -from typing import IO, Any, SupportsAbs, SupportsInt -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Literal, SupportsAbs, SupportsInt +from typing_extensions import Self, TypeAlias __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple", "Int2AP", "ParseFlags", "Time2Internaldate", "IMAP4_SSL"] diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index eb13240f84ff..825eab7ffde2 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -6,8 +6,7 @@ from abc import ABCMeta, abstractmethod from collections.abc import Iterator, Mapping, Sequence from importlib.machinery import ModuleSpec from io import BufferedReader -from typing import IO, Any, Protocol, overload, runtime_checkable -from typing_extensions import Literal +from typing import IO, Any, Literal, Protocol, overload, runtime_checkable if sys.version_info >= (3, 11): __all__ = [ diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index a0431905a828..586c2b80ab7b 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -3,11 +3,9 @@ import sys import types from _typeshed import ReadableBuffer from collections.abc import Callable, Iterable, MutableSequence, Sequence -from typing import Any -from typing_extensions import Literal, deprecated - -if sys.version_info >= (3, 8): - from importlib.metadata import DistributionFinder, PathDistribution +from importlib.metadata import DistributionFinder, PathDistribution +from typing import Any, Literal +from typing_extensions import deprecated class ModuleSpec: def __init__( @@ -117,7 +115,7 @@ class PathFinder: if sys.version_info >= (3, 10): @staticmethod def find_distributions(context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - elif sys.version_info >= (3, 8): + else: @classmethod def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... diff --git a/mypy/typeshed/stdlib/importlib/readers.pyi b/mypy/typeshed/stdlib/importlib/readers.pyi index f34794601b59..41d7af966d58 100644 --- a/mypy/typeshed/stdlib/importlib/readers.pyi +++ b/mypy/typeshed/stdlib/importlib/readers.pyi @@ -8,8 +8,8 @@ import zipfile from _typeshed import Incomplete, StrPath from collections.abc import Iterable, Iterator from io import BufferedReader -from typing import NoReturn, TypeVar -from typing_extensions import Literal, Never +from typing import Literal, NoReturn, TypeVar +from typing_extensions import Never if sys.version_info >= (3, 11): import importlib.resources.abc as abc diff --git a/mypy/typeshed/stdlib/importlib/resources/simple.pyi b/mypy/typeshed/stdlib/importlib/resources/simple.pyi index 9502375d00a2..9ff415156365 100644 --- a/mypy/typeshed/stdlib/importlib/resources/simple.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/simple.pyi @@ -3,8 +3,8 @@ import sys from _typeshed import Incomplete, OpenBinaryMode, OpenTextMode, Unused from collections.abc import Iterator from io import TextIOWrapper -from typing import IO, Any, BinaryIO, NoReturn, overload -from typing_extensions import Literal, Never +from typing import IO, Any, BinaryIO, Literal, NoReturn, overload +from typing_extensions import Never if sys.version_info >= (3, 11): from .abc import Traversable, TraversableResources diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 6498719df887..a26dc67f9945 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -25,8 +25,8 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing import Any, ClassVar, NamedTuple, Protocol, TypeVar, overload -from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypeGuard +from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload +from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard if sys.version_info >= (3, 11): __all__ = [ @@ -200,57 +200,29 @@ def isfunction(object: object) -> TypeGuard[FunctionType]: ... if sys.version_info >= (3, 12): def markcoroutinefunction(func: _F) -> _F: ... -if sys.version_info >= (3, 8): - @overload - def isgeneratorfunction(obj: Callable[..., Generator[Any, Any, Any]]) -> bool: ... - @overload - def isgeneratorfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... - @overload - def isgeneratorfunction(obj: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... - @overload - def iscoroutinefunction(obj: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... - @overload - def iscoroutinefunction(obj: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, _T]]]: ... - @overload - def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... - @overload - def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... - -else: - @overload - def isgeneratorfunction(object: Callable[..., Generator[Any, Any, Any]]) -> bool: ... - @overload - def isgeneratorfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... - @overload - def isgeneratorfunction(object: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... - @overload - def iscoroutinefunction(object: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... - @overload - def iscoroutinefunction(object: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, _T]]]: ... - @overload - def iscoroutinefunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... - @overload - def iscoroutinefunction(object: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... - +@overload +def isgeneratorfunction(obj: Callable[..., Generator[Any, Any, Any]]) -> bool: ... +@overload +def isgeneratorfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... +@overload +def isgeneratorfunction(obj: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... +@overload +def iscoroutinefunction(obj: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... +@overload +def iscoroutinefunction(obj: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, _T]]]: ... +@overload +def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... +@overload +def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... def isgenerator(object: object) -> TypeGuard[GeneratorType[Any, Any, Any]]: ... def iscoroutine(object: object) -> TypeGuard[CoroutineType[Any, Any, Any]]: ... def isawaitable(object: object) -> TypeGuard[Awaitable[Any]]: ... - -if sys.version_info >= (3, 8): - @overload - def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... - @overload - def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... - @overload - def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... - -else: - @overload - def isasyncgenfunction(object: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... - @overload - def isasyncgenfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... - @overload - def isasyncgenfunction(object: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... +@overload +def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... +@overload +def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... +@overload +def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... class _SupportsSet(Protocol[_T_cont, _V_cont]): def __set__(self, __instance: _T_cont, __value: _V_cont) -> None: ... @@ -381,9 +353,8 @@ class _ParameterKind(enum.IntEnum): KEYWORD_ONLY: int VAR_KEYWORD: int - if sys.version_info >= (3, 8): - @property - def description(self) -> str: ... + @property + def description(self) -> str: ... if sys.version_info >= (3, 12): AGEN_CREATED: Literal["AGEN_CREATED"] diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index ee4eda1b4eb0..d949971048b0 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -6,12 +6,13 @@ from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer from collections.abc import Callable, Iterable, Iterator from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, TextIO, TypeVar, overload -from typing_extensions import Literal, Self +from typing import IO, Any, BinaryIO, Literal, TextIO, TypeVar, overload +from typing_extensions import Self __all__ = [ "BlockingIOError", "open", + "open_code", "IOBase", "RawIOBase", "FileIO", @@ -30,9 +31,6 @@ __all__ = [ "SEEK_END", ] -if sys.version_info >= (3, 8): - __all__ += ["open_code"] - if sys.version_info >= (3, 11): __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] @@ -46,8 +44,7 @@ SEEK_END: Literal[2] open = builtins.open -if sys.version_info >= (3, 8): - def open_code(path: str) -> IO[bytes]: ... +def open_code(path: str) -> IO[bytes]: ... BlockingIOError = builtins.BlockingIOError diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 13a8c4330a50..98b1893d2a8a 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Iterable, Iterator -from typing import Any, Generic, SupportsInt, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Generic, Literal, SupportsInt, TypeVar, overload +from typing_extensions import Self, TypeAlias # Undocumented length constants IPV4LENGTH: Literal[32] diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 1bc0b2ec7390..1fa76399444a 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable, Iterator -from typing import Any, Generic, SupportsComplex, SupportsFloat, SupportsInt, TypeVar, overload -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias +from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -49,14 +49,10 @@ class repeat(Iterator[_T]): def __length_hint__(self) -> int: ... class accumulate(Iterator[_T]): - if sys.version_info >= (3, 8): - @overload - def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... - @overload - def __init__(self, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> None: ... - else: - def __init__(self, iterable: Iterable[_T], func: Callable[[_T, _T], _T] | None = ...) -> None: ... - + @overload + def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + @overload + def __init__(self, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... diff --git a/mypy/typeshed/stdlib/keyword.pyi b/mypy/typeshed/stdlib/keyword.pyi index 46c386048858..5eb7aab85317 100644 --- a/mypy/typeshed/stdlib/keyword.pyi +++ b/mypy/typeshed/stdlib/keyword.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Sequence -from typing_extensions import Final +from typing import Final if sys.version_info >= (3, 9): __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"] diff --git a/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi b/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi index eef386f709ac..5468ab1db5c3 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi @@ -1,8 +1,7 @@ from _typeshed import Incomplete, StrPath from abc import ABCMeta, abstractmethod from collections.abc import MutableMapping -from typing import ClassVar, TypeVar -from typing_extensions import Literal +from typing import ClassVar, Literal, TypeVar from .pytree import Base, Leaf, Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_apply.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_apply.pyi index 7c5451c15220..e53e3dd86457 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_apply.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_apply.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi index bf73009e9dbf..fb0b472aa12a 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from ..fixer_base import BaseFix diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_basestring.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_basestring.pyi index 84a354d32777..8ed5ccaa7fd3 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_basestring.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_basestring.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_buffer.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_buffer.pyi index 857c1e2241b9..1efca6228ea2 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_buffer.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_buffer.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_dict.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_dict.pyi index 2e66911195bf..08c54c3bc376 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_dict.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_dict.pyi @@ -1,6 +1,5 @@ from _typeshed import Incomplete -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_except.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_except.pyi index b87aacd342e9..30930a2c381e 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_except.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_except.pyi @@ -1,6 +1,5 @@ from collections.abc import Generator, Iterable -from typing import ClassVar, TypeVar -from typing_extensions import Literal +from typing import ClassVar, Literal, TypeVar from .. import fixer_base from ..pytree import Base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_exec.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_exec.pyi index 306937eb9759..71e2a820a564 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_exec.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_exec.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_execfile.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_execfile.pyi index fb245e5a1c1c..8122a6389b12 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_execfile.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_execfile.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi index 10341d7985a8..7fc910c0a1bc 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi @@ -1,7 +1,6 @@ from _typeshed import Incomplete, StrPath from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_filter.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_filter.pyi index 3998a1dd001e..638889be8b65 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_filter.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_filter.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_funcattrs.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_funcattrs.pyi index 59919446ffdd..60487bb1f2a6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_funcattrs.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_funcattrs.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_future.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_future.pyi index 8eb5ca35dcc3..12ed93f21223 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_future.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_future.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_getcwdu.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_getcwdu.pyi index d18a38f69be0..aa3ccf50be9e 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_getcwdu.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_getcwdu.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_has_key.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_has_key.pyi index 1e6b58dd3512..f6f5a072e21b 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_has_key.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_has_key.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi index 8f02252f7bb9..4595c57c7eb9 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_import.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_import.pyi index 436e7f1915b2..bf4b2d00925e 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_import.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_import.pyi @@ -1,7 +1,6 @@ from _typeshed import StrPath from collections.abc import Generator -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi index 277a172d3af9..dd6f72dd88ac 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi @@ -1,7 +1,6 @@ from _typeshed import StrPath from collections.abc import Generator -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_input.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_input.pyi index df52f8d77427..fc1279535bed 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_input.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_input.pyi @@ -1,6 +1,5 @@ from _typeshed import Incomplete -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_intern.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_intern.pyi index f4e71b6da5f2..804b7b2517a5 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_intern.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_intern.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_isinstance.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_isinstance.pyi index e776ea043714..31eefd625317 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_isinstance.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_isinstance.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools.pyi index a19f7b5e8a00..229d86ee71bb 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools_imports.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools_imports.pyi index 1ea0b506aaa2..39a4da506867 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools_imports.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools_imports.pyi @@ -1,6 +1,5 @@ from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal class FixItertoolsImports(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_long.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_long.pyi index c47f4528de47..9ccf2711d7d1 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_long.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_long.pyi @@ -1,6 +1,5 @@ from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal class FixLong(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_map.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_map.pyi index 66e311cba8a8..6e60282cf0be 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_map.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_map.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_metaclass.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_metaclass.pyi index 44626b47072d..1b1ec82032b4 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_metaclass.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_metaclass.pyi @@ -1,6 +1,5 @@ from collections.abc import Generator -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi index 9bda7992dc8b..594b5e2c95c9 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_ne.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_ne.pyi index 95dfacccf219..6ff1220b0472 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_ne.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_ne.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_next.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_next.pyi index a5757d65064a..b13914ae8c01 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_next.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_next.pyi @@ -1,6 +1,5 @@ from _typeshed import StrPath -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_nonzero.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_nonzero.pyi index adf268fdb8e2..5c37fc12ef08 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_nonzero.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_nonzero.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_numliterals.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_numliterals.pyi index 6842e42e45f0..113145e395f6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_numliterals.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_numliterals.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_operator.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_operator.pyi index 6da150a51c0c..b9863d38347b 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_operator.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_operator.pyi @@ -1,6 +1,5 @@ from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal def invocation(s): ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_paren.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_paren.pyi index c730cdc5d0b2..237df6c5ff2c 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_paren.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_paren.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_print.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_print.pyi index 2261c9489299..e9564b04ac75 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_print.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_print.pyi @@ -1,6 +1,5 @@ from _typeshed import Incomplete -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_raise.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_raise.pyi index 756a05ea3ddd..e02c3080f409 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_raise.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_raise.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_raw_input.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_raw_input.pyi index 61d6ad7676ef..d1a0eb0e0a7e 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_raw_input.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_raw_input.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_reduce.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_reduce.pyi index 4ea07fdde00b..f8ad876c21a6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_reduce.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_reduce.pyi @@ -1,6 +1,5 @@ from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal class FixReduce(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_reload.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_reload.pyi index 8045ac507890..820075438eca 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_reload.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_reload.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi index 2ceca053e903..6283f1ab7ce2 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi @@ -1,6 +1,5 @@ from collections.abc import Generator -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_repr.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_repr.pyi index 6f3305846d18..3b192d396dd6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_repr.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_repr.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_set_literal.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_set_literal.pyi index dd18413d6d5a..6962ff326f56 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_set_literal.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_set_literal.pyi @@ -1,6 +1,5 @@ from lib2to3 import fixer_base -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal class FixSetLiteral(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_standarderror.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_standarderror.pyi index fd23af5a711e..ba914bcab5d6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_standarderror.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_standarderror.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_sys_exc.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_sys_exc.pyi index 3dbcd38c4b26..0fa1a4787087 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_sys_exc.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_sys_exc.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_throw.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_throw.pyi index 50e37d44a58b..4c99855e5c37 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_throw.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_throw.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi index 48eadf75341c..bfaa9970c996 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi @@ -1,6 +1,5 @@ from _typeshed import Incomplete -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_types.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_types.pyi index 6ac1344b1e6c..e26dbec71a97 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_types.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_types.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi index af63d1865f2d..80d9d8b6e656 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi @@ -1,6 +1,5 @@ from _typeshed import StrPath -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi index a37e63b31101..625472f609ab 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi @@ -1,5 +1,5 @@ from collections.abc import Generator -from typing_extensions import Literal +from typing import Literal from .fix_imports import FixImports diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_ws_comma.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_ws_comma.pyi index 6231d90c65f1..4ce5cb2c4ac1 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_ws_comma.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_ws_comma.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Leaf diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_xrange.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_xrange.pyi index 89d300ef063a..71318b7660b6 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_xrange.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_xrange.pyi @@ -1,6 +1,5 @@ from _typeshed import Incomplete, StrPath -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base from ..pytree import Node diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_xreadlines.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_xreadlines.pyi index 39757155e5d9..b4794143a003 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_xreadlines.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_xreadlines.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_zip.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_zip.pyi index 0c70717aa2ac..805886ee3180 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_zip.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_zip.pyi @@ -1,5 +1,4 @@ -from typing import ClassVar -from typing_extensions import Literal +from typing import ClassVar, Literal from .. import fixer_base diff --git a/mypy/typeshed/stdlib/lib2to3/main.pyi b/mypy/typeshed/stdlib/lib2to3/main.pyi index cfcaeeaf64ee..5b7fdfca5d65 100644 --- a/mypy/typeshed/stdlib/lib2to3/main.pyi +++ b/mypy/typeshed/stdlib/lib2to3/main.pyi @@ -1,8 +1,7 @@ from _typeshed import FileDescriptorOrPath from collections.abc import Container, Iterable, Iterator, Mapping, Sequence from logging import _ExcInfoType -from typing import AnyStr -from typing_extensions import Literal +from typing import AnyStr, Literal from . import refactor as refactor diff --git a/mypy/typeshed/stdlib/lib2to3/pygram.pyi b/mypy/typeshed/stdlib/lib2to3/pygram.pyi index 2d1e90e79927..86c74b54888a 100644 --- a/mypy/typeshed/stdlib/lib2to3/pygram.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pygram.pyi @@ -1,5 +1,3 @@ -import sys - from .pgen2.grammar import Grammar class Symbols: @@ -112,6 +110,5 @@ class pattern_symbols(Symbols): python_grammar: Grammar python_grammar_no_print_statement: Grammar -if sys.version_info >= (3, 8): - python_grammar_no_print_and_exec_statement: Grammar +python_grammar_no_print_and_exec_statement: Grammar pattern_grammar: Grammar diff --git a/mypy/typeshed/stdlib/lib2to3/pytree.pyi b/mypy/typeshed/stdlib/lib2to3/pytree.pyi index d14446f38565..138333bd58af 100644 --- a/mypy/typeshed/stdlib/lib2to3/pytree.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pytree.pyi @@ -1,7 +1,8 @@ from _typeshed import Incomplete, SupportsGetItem, SupportsLenAndGetItem, Unused from abc import abstractmethod from collections.abc import Iterable, Iterator, MutableSequence -from typing_extensions import Final, Self, TypeAlias +from typing import Final +from typing_extensions import Self, TypeAlias from .fixer_base import BaseFix from .pgen2.grammar import Grammar diff --git a/mypy/typeshed/stdlib/lib2to3/refactor.pyi b/mypy/typeshed/stdlib/lib2to3/refactor.pyi index d750d9c4a6cf..a7f382540648 100644 --- a/mypy/typeshed/stdlib/lib2to3/refactor.pyi +++ b/mypy/typeshed/stdlib/lib2to3/refactor.pyi @@ -3,8 +3,7 @@ from collections.abc import Container, Generator, Iterable, Mapping from logging import Logger, _ExcInfoType from multiprocessing import JoinableQueue from multiprocessing.synchronize import Lock -from typing import Any, ClassVar, NoReturn, overload -from typing_extensions import Final +from typing import Any, ClassVar, Final, NoReturn, overload from .btm_matcher import BottomMatcher from .fixer_base import BaseFix diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 5a72b1fcd799..eae2bcd3e96c 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -7,8 +7,8 @@ from re import Pattern from string import Template from time import struct_time from types import FrameType, TracebackType -from typing import Any, ClassVar, Generic, Protocol, TextIO, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, ClassVar, Generic, Literal, Protocol, TextIO, TypeVar, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 11): from types import GenericAlias @@ -128,173 +128,94 @@ class Logger(Filterer): def getChild(self, suffix: str) -> Self: ... # see python/typing#980 if sys.version_info >= (3, 12): def getChildren(self) -> set[Logger]: ... - if sys.version_info >= (3, 8): - def debug( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def info( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warning( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def error( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def exception( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = True, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def critical( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def log( - self, - level: int, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def _log( - self, - level: int, - msg: object, - args: _ArgsType, - exc_info: _ExcInfoType | None = None, - extra: Mapping[str, object] | None = None, - stack_info: bool = False, - stacklevel: int = 1, - ) -> None: ... # undocumented - else: - def debug( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def info( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warning( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def error( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def critical( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def log( - self, - level: int, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def exception( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = True, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def _log( - self, - level: int, - msg: object, - args: _ArgsType, - exc_info: _ExcInfoType | None = None, - extra: Mapping[str, object] | None = None, - stack_info: bool = False, - ) -> None: ... # undocumented + + def debug( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def info( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def warning( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def error( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def exception( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = True, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def critical( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def log( + self, + level: int, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def _log( + self, + level: int, + msg: object, + args: _ArgsType, + exc_info: _ExcInfoType | None = None, + extra: Mapping[str, object] | None = None, + stack_info: bool = False, + stacklevel: int = 1, + ) -> None: ... # undocumented fatal = critical def addHandler(self, hdlr: Handler) -> None: ... def removeHandler(self, hdlr: Handler) -> None: ... - if sys.version_info >= (3, 8): - def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> tuple[str, int, str, str | None]: ... - else: - def findCaller(self, stack_info: bool = False) -> tuple[str, int, str, str | None]: ... - + def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> tuple[str, int, str, str | None]: ... def handle(self, record: LogRecord) -> None: ... def makeRecord( self, @@ -366,12 +287,10 @@ class Formatter: *, defaults: Mapping[str, Any] | None = None, ) -> None: ... - elif sys.version_info >= (3, 8): + else: def __init__( self, fmt: str | None = None, datefmt: str | None = None, style: _FormatStyle = "%", validate: bool = True ) -> None: ... - else: - def __init__(self, fmt: str | None = None, datefmt: str | None = None, style: _FormatStyle = "%") -> None: ... def format(self, record: LogRecord) -> str: ... def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: ... @@ -451,310 +370,173 @@ class LoggerAdapter(Generic[_L]): def __init__(self, logger: _L, extra: Mapping[str, object]) -> None: ... def process(self, msg: Any, kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]: ... - if sys.version_info >= (3, 8): - def debug( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def info( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def warning( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def error( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def exception( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = True, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def critical( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def log( - self, - level: int, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - else: - def debug( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def info( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def warning( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def error( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def exception( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = True, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def critical( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - def log( - self, - level: int, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - - def isEnabledFor(self, level: int) -> bool: ... - def getEffectiveLevel(self) -> int: ... - def setLevel(self, level: _Level) -> None: ... - def hasHandlers(self) -> bool: ... - def _log( - self, - level: int, - msg: object, - args: _ArgsType, - exc_info: _ExcInfoType | None = None, - extra: Mapping[str, object] | None = None, - stack_info: bool = False, - ) -> None: ... # undocumented - @property - def name(self) -> str: ... # undocumented - if sys.version_info >= (3, 11): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... - -def getLogger(name: str | None = None) -> Logger: ... -def getLoggerClass() -> type[Logger]: ... -def getLogRecordFactory() -> Callable[..., LogRecord]: ... - -if sys.version_info >= (3, 8): def debug( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... def info( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... def warning( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... def warn( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... def error( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, - ) -> None: ... - def critical( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... def exception( + self, msg: object, *args: object, exc_info: _ExcInfoType = True, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... - def log( - level: int, + def critical( + self, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... - -else: - def debug( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def info( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warning( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def warn( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def error( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def critical( + def log( + self, + level: int, msg: object, *args: object, exc_info: _ExcInfoType = None, stack_info: bool = False, + stacklevel: int = 1, extra: Mapping[str, object] | None = None, + **kwargs: object, ) -> None: ... - def exception( - msg: object, - *args: object, - exc_info: _ExcInfoType = True, - stack_info: bool = False, - extra: Mapping[str, object] | None = None, - ) -> None: ... - def log( + def isEnabledFor(self, level: int) -> bool: ... + def getEffectiveLevel(self) -> int: ... + def setLevel(self, level: _Level) -> None: ... + def hasHandlers(self) -> bool: ... + def _log( + self, level: int, msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, + args: _ArgsType, + exc_info: _ExcInfoType | None = None, extra: Mapping[str, object] | None = None, - ) -> None: ... + stack_info: bool = False, + ) -> None: ... # undocumented + @property + def name(self) -> str: ... # undocumented + if sys.version_info >= (3, 11): + def __class_getitem__(cls, item: Any) -> GenericAlias: ... + +def getLogger(name: str | None = None) -> Logger: ... +def getLoggerClass() -> type[Logger]: ... +def getLogRecordFactory() -> Callable[..., LogRecord]: ... +def debug( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def info( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def warning( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def warn( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def error( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def critical( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def exception( + msg: object, + *args: object, + exc_info: _ExcInfoType = True, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... +def log( + level: int, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... fatal = critical @@ -783,20 +565,6 @@ if sys.version_info >= (3, 9): errors: str | None = ..., ) -> None: ... -elif sys.version_info >= (3, 8): - def basicConfig( - *, - filename: StrPath | None = ..., - filemode: str = ..., - format: str = ..., - datefmt: str | None = ..., - style: _FormatStyle = ..., - level: _Level | None = ..., - stream: SupportsWrite[str] | None = ..., - handlers: Iterable[Handler] | None = ..., - force: bool = ..., - ) -> None: ... - else: def basicConfig( *, @@ -808,6 +576,7 @@ else: level: _Level | None = ..., stream: SupportsWrite[str] | None = ..., handlers: Iterable[Handler] | None = ..., + force: bool = ..., ) -> None: ... def shutdown(handlerList: Sequence[Any] = ...) -> None: ... # handlerList is undocumented @@ -863,8 +632,7 @@ class PercentStyle: # undocumented default_format: str asctime_format: str asctime_search: str - if sys.version_info >= (3, 8): - validation_pattern: Pattern[str] + validation_pattern: Pattern[str] _fmt: str if sys.version_info >= (3, 10): def __init__(self, fmt: str, *, defaults: Mapping[str, Any] | None = None) -> None: ... @@ -872,9 +640,7 @@ class PercentStyle: # undocumented def __init__(self, fmt: str) -> None: ... def usesTime(self) -> bool: ... - if sys.version_info >= (3, 8): - def validate(self) -> None: ... - + def validate(self) -> None: ... def format(self, record: Any) -> str: ... class StrFormatStyle(PercentStyle): # undocumented diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 0a61e5b16870..7a26846addbb 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -4,8 +4,8 @@ from collections.abc import Callable, Hashable, Iterable, Sequence from configparser import RawConfigParser from re import Pattern from threading import Thread -from typing import IO, Any, overload -from typing_extensions import Literal, Required, SupportsIndex, TypeAlias, TypedDict +from typing import IO, Any, Literal, SupportsIndex, TypedDict, overload +from typing_extensions import Required, TypeAlias from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level @@ -28,16 +28,9 @@ else: class _LoggerConfiguration(_RootLoggerConfiguration, TypedDict, total=False): propagate: bool -if sys.version_info >= (3, 8): - _FormatterConfigurationTypedDict = TypedDict( - "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False - ) -else: - _FormatterConfigurationTypedDict = TypedDict( - "_FormatterConfigurationTypedDict", - {"class": str, "format": str, "datefmt": str, "style": _FormatStyle, "validate": bool}, - total=False, - ) +_FormatterConfigurationTypedDict = TypedDict( + "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False +) class _FilterConfigurationTypedDict(TypedDict): name: str diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index be61cac08139..05248ee0e710 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,8 +1,8 @@ from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Mapping, Sequence -from typing import IO, Any, TextIO, overload -from typing_extensions import Literal, Self, TypeAlias, final +from typing import IO, Any, Literal, TextIO, final, overload +from typing_extensions import Self, TypeAlias __all__ = [ "CHECK_NONE", diff --git a/mypy/typeshed/stdlib/macpath.pyi b/mypy/typeshed/stdlib/macpath.pyi deleted file mode 100644 index 37821f44b200..000000000000 --- a/mypy/typeshed/stdlib/macpath.pyi +++ /dev/null @@ -1,104 +0,0 @@ -from _typeshed import BytesPath, StrOrBytesPath, StrPath -from genericpath import ( - commonprefix as commonprefix, - exists as exists, - getatime as getatime, - getctime as getctime, - getmtime as getmtime, - getsize as getsize, - isdir as isdir, - isfile as isfile, - samefile as samefile, - sameopenfile as sameopenfile, - samestat as samestat, -) -from os import PathLike - -# Re-export common definitions from posixpath to reduce duplication -from posixpath import ( - abspath as abspath, - curdir as curdir, - defpath as defpath, - devnull as devnull, - expanduser as expanduser, - expandvars as expandvars, - extsep as extsep, - isabs as isabs, - lexists as lexists, - pardir as pardir, - pathsep as pathsep, - sep as sep, - splitdrive as splitdrive, - splitext as splitext, - supports_unicode_filenames as supports_unicode_filenames, -) -from typing import AnyStr, overload - -__all__ = [ - "normcase", - "isabs", - "join", - "splitdrive", - "split", - "splitext", - "basename", - "dirname", - "commonprefix", - "getsize", - "getmtime", - "getatime", - "getctime", - "islink", - "exists", - "lexists", - "isdir", - "isfile", - "expanduser", - "expandvars", - "normpath", - "abspath", - "curdir", - "pardir", - "sep", - "pathsep", - "defpath", - "altsep", - "extsep", - "devnull", - "realpath", - "supports_unicode_filenames", -] - -altsep: str | None - -@overload -def basename(s: PathLike[AnyStr]) -> AnyStr: ... -@overload -def basename(s: AnyStr) -> AnyStr: ... -@overload -def dirname(s: PathLike[AnyStr]) -> AnyStr: ... -@overload -def dirname(s: AnyStr) -> AnyStr: ... -@overload -def normcase(path: PathLike[AnyStr]) -> AnyStr: ... -@overload -def normcase(path: AnyStr) -> AnyStr: ... -@overload -def normpath(s: PathLike[AnyStr]) -> AnyStr: ... -@overload -def normpath(s: AnyStr) -> AnyStr: ... -@overload -def realpath(path: PathLike[AnyStr]) -> AnyStr: ... -@overload -def realpath(path: AnyStr) -> AnyStr: ... -def islink(s: StrOrBytesPath) -> bool: ... - -# Mypy complains that the signatures overlap, but things seem to behave correctly anyway. -@overload -def join(s: StrPath, *paths: StrPath) -> str: ... -@overload -def join(s: BytesPath, *paths: BytesPath) -> bytes: ... -@overload -def split(s: PathLike[AnyStr]) -> tuple[AnyStr, AnyStr]: ... -@overload -def split(s: AnyStr) -> tuple[AnyStr, AnyStr]: ... diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi index 8053fad88ea5..1059bfe917e8 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -5,8 +5,8 @@ from _typeshed import StrPath, SupportsNoArgReadline, SupportsRead from abc import ABCMeta, abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 73b53a713301..ee0693912a8b 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -1,15 +1,12 @@ import sys from collections.abc import Iterable -from typing import Protocol, SupportsFloat, TypeVar, overload -from typing_extensions import SupportsIndex, TypeAlias +from typing import Protocol, SupportsFloat, SupportsIndex, TypeVar, overload +from typing_extensions import TypeAlias _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) -if sys.version_info >= (3, 8): - _SupportsFloatOrIndex: TypeAlias = SupportsFloat | SupportsIndex -else: - _SupportsFloatOrIndex: TypeAlias = SupportsFloat +_SupportsFloatOrIndex: TypeAlias = SupportsFloat | SupportsIndex e: float pi: float @@ -35,18 +32,12 @@ class _SupportsCeil(Protocol[_T_co]): def ceil(__x: _SupportsCeil[_T]) -> _T: ... @overload def ceil(__x: _SupportsFloatOrIndex) -> int: ... - -if sys.version_info >= (3, 8): - def comb(__n: SupportsIndex, __k: SupportsIndex) -> int: ... - +def comb(__n: SupportsIndex, __k: SupportsIndex) -> int: ... def copysign(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... def cos(__x: _SupportsFloatOrIndex) -> float: ... def cosh(__x: _SupportsFloatOrIndex) -> float: ... def degrees(__x: _SupportsFloatOrIndex) -> float: ... - -if sys.version_info >= (3, 8): - def dist(__p: Iterable[_SupportsFloatOrIndex], __q: Iterable[_SupportsFloatOrIndex]) -> float: ... - +def dist(__p: Iterable[_SupportsFloatOrIndex], __q: Iterable[_SupportsFloatOrIndex]) -> float: ... def erf(__x: _SupportsFloatOrIndex) -> float: ... def erfc(__x: _SupportsFloatOrIndex) -> float: ... def exp(__x: _SupportsFloatOrIndex) -> float: ... @@ -56,12 +47,7 @@ if sys.version_info >= (3, 11): def expm1(__x: _SupportsFloatOrIndex) -> float: ... def fabs(__x: _SupportsFloatOrIndex) -> float: ... - -if sys.version_info >= (3, 8): - def factorial(__x: SupportsIndex) -> int: ... - -else: - def factorial(__x: int) -> int: ... +def factorial(__x: SupportsIndex) -> int: ... class _SupportsFloor(Protocol[_T_co]): def __floor__(self) -> _T_co: ... @@ -81,12 +67,7 @@ if sys.version_info >= (3, 9): else: def gcd(__x: SupportsIndex, __y: SupportsIndex) -> int: ... -if sys.version_info >= (3, 8): - def hypot(*coordinates: _SupportsFloatOrIndex) -> float: ... - -else: - def hypot(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... - +def hypot(*coordinates: _SupportsFloatOrIndex) -> float: ... def isclose( a: _SupportsFloatOrIndex, b: _SupportsFloatOrIndex, @@ -97,9 +78,7 @@ def isclose( def isinf(__x: _SupportsFloatOrIndex) -> bool: ... def isfinite(__x: _SupportsFloatOrIndex) -> bool: ... def isnan(__x: _SupportsFloatOrIndex) -> bool: ... - -if sys.version_info >= (3, 8): - def isqrt(__n: SupportsIndex) -> int: ... +def isqrt(__n: SupportsIndex) -> int: ... if sys.version_info >= (3, 9): def lcm(*integers: SupportsIndex) -> int: ... @@ -118,17 +97,12 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 9): def nextafter(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... -if sys.version_info >= (3, 8): - def perm(__n: SupportsIndex, __k: SupportsIndex | None = None) -> int: ... - +def perm(__n: SupportsIndex, __k: SupportsIndex | None = None) -> int: ... def pow(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... - -if sys.version_info >= (3, 8): - @overload - def prod(__iterable: Iterable[SupportsIndex], *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] - @overload - def prod(__iterable: Iterable[_SupportsFloatOrIndex], *, start: _SupportsFloatOrIndex = 1) -> float: ... - +@overload +def prod(__iterable: Iterable[SupportsIndex], *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] +@overload +def prod(__iterable: Iterable[_SupportsFloatOrIndex], *, start: _SupportsFloatOrIndex = 1) -> float: ... def radians(__x: _SupportsFloatOrIndex) -> float: ... def remainder(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... def sin(__x: _SupportsFloatOrIndex) -> float: ... diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index 532cc5e3ce39..e74b214d3ff1 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import StrPath from collections.abc import Sequence from typing import IO @@ -19,12 +18,7 @@ __all__ = [ "common_types", ] -if sys.version_info >= (3, 8): - def guess_type(url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... - -else: - def guess_type(url: str, strict: bool = True) -> tuple[str | None, str | None]: ... - +def guess_type(url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... def guess_all_extensions(type: str, strict: bool = True) -> list[str]: ... def guess_extension(type: str, strict: bool = True) -> str | None: ... def init(files: Sequence[str] | None = None) -> None: ... @@ -45,11 +39,7 @@ class MimeTypes: types_map_inv: tuple[dict[str, str], dict[str, str]] def __init__(self, filenames: tuple[str, ...] = (), strict: bool = True) -> None: ... def guess_extension(self, type: str, strict: bool = True) -> str | None: ... - if sys.version_info >= (3, 8): - def guess_type(self, url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... - else: - def guess_type(self, url: str, strict: bool = True) -> tuple[str | None, str | None]: ... - + def guess_type(self, url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... def guess_all_extensions(self, type: str, strict: bool = True) -> list[str]: ... def read(self, filename: str, strict: bool = True) -> None: ... def readfp(self, fp: IO[str], strict: bool = True) -> None: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 9a213a8b8cf0..6bbb797f054d 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -39,11 +39,7 @@ class mmap(Iterable[int], Sized): ) -> None: ... def close(self) -> None: ... - if sys.version_info >= (3, 8): - def flush(self, offset: int = ..., size: int = ...) -> None: ... - else: - def flush(self, offset: int = ..., size: int = ...) -> int: ... - + def flush(self, offset: int = ..., size: int = ...) -> None: ... def move(self, dest: int, src: int, count: int) -> None: ... def read_byte(self) -> int: ... def readline(self) -> bytes: ... @@ -54,7 +50,7 @@ class mmap(Iterable[int], Sized): def write_byte(self, byte: int) -> None: ... def __len__(self) -> int: ... closed: bool - if sys.version_info >= (3, 8) and sys.platform != "win32": + if sys.platform != "win32": def madvise(self, option: int, start: int = ..., length: int = ...) -> None: ... def find(self, sub: ReadableBuffer, start: int = ..., stop: int = ...) -> int: ... @@ -81,7 +77,7 @@ class mmap(Iterable[int], Sized): def __buffer__(self, __flags: int) -> memoryview: ... def __release_buffer__(self, __buffer: memoryview) -> None: ... -if sys.version_info >= (3, 8) and sys.platform != "win32": +if sys.platform != "win32": MADV_NORMAL: int MADV_RANDOM: int MADV_SEQUENTIAL: int @@ -89,28 +85,28 @@ if sys.version_info >= (3, 8) and sys.platform != "win32": MADV_DONTNEED: int MADV_FREE: int - if sys.platform == "linux": - MADV_REMOVE: int - MADV_DONTFORK: int - MADV_DOFORK: int - MADV_HWPOISON: int - MADV_MERGEABLE: int - MADV_UNMERGEABLE: int - # Seems like this constant is not defined in glibc. - # See https://github.com/python/typeshed/pull/5360 for details - # MADV_SOFT_OFFLINE: int - MADV_HUGEPAGE: int - MADV_NOHUGEPAGE: int - MADV_DONTDUMP: int - MADV_DODUMP: int +if sys.platform == "linux": + MADV_REMOVE: int + MADV_DONTFORK: int + MADV_DOFORK: int + MADV_HWPOISON: int + MADV_MERGEABLE: int + MADV_UNMERGEABLE: int + # Seems like this constant is not defined in glibc. + # See https://github.com/python/typeshed/pull/5360 for details + # MADV_SOFT_OFFLINE: int + MADV_HUGEPAGE: int + MADV_NOHUGEPAGE: int + MADV_DONTDUMP: int + MADV_DODUMP: int - # This Values are defined for FreeBSD but type checkers do not support conditions for these - if sys.platform != "linux" and sys.platform != "darwin": - MADV_NOSYNC: int - MADV_AUTOSYNC: int - MADV_NOCORE: int - MADV_CORE: int - MADV_PROTECT: int +# This Values are defined for FreeBSD but type checkers do not support conditions for these +if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": + MADV_NOSYNC: int + MADV_AUTOSYNC: int + MADV_NOCORE: int + MADV_CORE: int + MADV_PROTECT: int if sys.version_info >= (3, 10) and sys.platform == "darwin": MADV_FREE_REUSABLE: int diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 06bb50d26286..132cac5f1878 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -31,23 +31,13 @@ class ModuleFinder: excludes: Container[str] # undocumented replace_paths: Sequence[tuple[str, str]] # undocumented - if sys.version_info >= (3, 8): - def __init__( - self, - path: list[str] | None = None, - debug: int = 0, - excludes: Container[str] | None = None, - replace_paths: Sequence[tuple[str, str]] | None = None, - ) -> None: ... - else: - def __init__( - self, - path: list[str] | None = None, - debug: int = 0, - excludes: Container[str] = [], - replace_paths: Sequence[tuple[str, str]] = [], - ) -> None: ... - + def __init__( + self, + path: list[str] | None = None, + debug: int = 0, + excludes: Container[str] | None = None, + replace_paths: Sequence[tuple[str, str]] | None = None, + ) -> None: ... def msg(self, level: int, str: str, *args: Any) -> None: ... # undocumented def msgin(self, *args: Any) -> None: ... # undocumented def msgout(self, *args: Any) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/msilib/__init__.pyi b/mypy/typeshed/stdlib/msilib/__init__.pyi index 9f7367d152ba..106805dab931 100644 --- a/mypy/typeshed/stdlib/msilib/__init__.pyi +++ b/mypy/typeshed/stdlib/msilib/__init__.pyi @@ -1,8 +1,7 @@ import sys from collections.abc import Container, Iterable, Sequence from types import ModuleType -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal if sys.platform == "win32": from _msi import * diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index 768edbc18ab3..bfd7ec62a9be 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -1,5 +1,5 @@ import sys -from typing_extensions import Final, Literal +from typing import Final, Literal # This module is only available on Windows if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/multiprocessing/__init__.pyi b/mypy/typeshed/stdlib/multiprocessing/__init__.pyi index 186bd54a021d..2bd6e2883ddb 100644 --- a/mypy/typeshed/stdlib/multiprocessing/__init__.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/__init__.pyi @@ -1,4 +1,3 @@ -import sys from multiprocessing import context, reduction as reducer from multiprocessing.context import ( AuthenticationError as AuthenticationError, @@ -7,7 +6,11 @@ from multiprocessing.context import ( ProcessError as ProcessError, TimeoutError as TimeoutError, ) -from multiprocessing.process import active_children as active_children, current_process as current_process +from multiprocessing.process import ( + active_children as active_children, + current_process as current_process, + parent_process as parent_process, +) # These are technically functions that return instances of these Queue classes. # The stub here doesn't reflect reality exactly -- @@ -19,9 +22,6 @@ from multiprocessing.process import active_children as active_children, current_ from multiprocessing.queues import JoinableQueue as JoinableQueue, Queue as Queue, SimpleQueue as SimpleQueue from multiprocessing.spawn import freeze_support as freeze_support -if sys.version_info >= (3, 8): - from multiprocessing.process import parent_process as parent_process - __all__ = [ "Array", "AuthenticationError", @@ -55,15 +55,13 @@ __all__ = [ "get_logger", "get_start_method", "log_to_stderr", + "parent_process", "reducer", "set_executable", "set_forkserver_preload", "set_start_method", ] -if sys.version_info >= (3, 8): - __all__ += ["parent_process"] - # These functions (really bound methods) # are all autogenerated at runtime here: https://github.com/python/cpython/blob/600c65c094b0b48704d8ec2416930648052ba715/Lib/multiprocessing/__init__.py#L23 RawValue = context._default_context.RawValue diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 333b8820d84d..7045a81b85be 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -3,8 +3,8 @@ import sys import types from _typeshed import ReadableBuffer from collections.abc import Iterable -from typing import Any -from typing_extensions import Self, SupportsIndex, TypeAlias +from typing import Any, SupportsIndex +from typing_extensions import Self, TypeAlias __all__ = ["Client", "Listener", "Pipe", "wait"] diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index fe3b98024548..1cc8d03ea436 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -8,18 +8,15 @@ from multiprocessing.managers import SyncManager from multiprocessing.pool import Pool as _Pool from multiprocessing.process import BaseProcess from multiprocessing.sharedctypes import SynchronizedArray, SynchronizedBase -from typing import Any, ClassVar, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, ClassVar, Literal, TypeVar, overload +from typing_extensions import TypeAlias if sys.platform != "win32": from multiprocessing.connection import Connection else: from multiprocessing.connection import PipeConnection -if sys.version_info >= (3, 8): - __all__ = () -else: - __all__: list[str] = [] +__all__ = () _LockLike: TypeAlias = synchronize.Lock | synchronize.RLock _CT = TypeVar("_CT", bound=_CData) @@ -39,10 +36,8 @@ class BaseContext: # multiprocessing.*, so the signatures should be identical (modulo self). @staticmethod def current_process() -> BaseProcess: ... - if sys.version_info >= (3, 8): - @staticmethod - def parent_process() -> BaseProcess | None: ... - + @staticmethod + def parent_process() -> BaseProcess | None: ... @staticmethod def active_children() -> list[BaseProcess]: ... def cpu_count(self) -> int: ... @@ -151,8 +146,6 @@ class DefaultContext(BaseContext): def __init__(self, context: BaseContext) -> None: ... def get_start_method(self, allow_none: bool = False) -> str: ... def get_all_start_methods(self) -> list[str]: ... - if sys.version_info < (3, 8): - __all__: ClassVar[list[str]] _default_context: DefaultContext diff --git a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi index 967b57ded6c8..804a56e9cbcf 100644 --- a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi @@ -12,8 +12,7 @@ from threading import ( RLock as RLock, Semaphore as Semaphore, ) -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal from .connection import Pipe as Pipe diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index c0ef0a3609d0..eb3ac29b1449 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -4,19 +4,14 @@ import threading from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from types import TracebackType -from typing import Any, AnyStr, ClassVar, Generic, TypeVar, overload -from typing_extensions import Self, SupportsIndex, TypeAlias +from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload +from typing_extensions import Self, TypeAlias from .connection import Connection from .context import BaseContext +from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory -if sys.version_info >= (3, 8): - from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory - - __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"] - -else: - __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token"] +__all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"] if sys.version_info >= (3, 9): from types import GenericAlias @@ -208,12 +203,10 @@ class SyncManager(BaseManager): def list(self) -> ListProxy[Any]: ... class RemoteError(Exception): ... +class SharedMemoryServer(Server): ... -if sys.version_info >= (3, 8): - class SharedMemoryServer(Server): ... - - class SharedMemoryManager(BaseManager): - def get_server(self) -> SharedMemoryServer: ... - def SharedMemory(self, size: int) -> _SharedMemory: ... - def ShareableList(self, sequence: Iterable[_SLT] | None) -> _ShareableList[_SLT]: ... - def __del__(self) -> None: ... +class SharedMemoryManager(BaseManager): + def get_server(self) -> SharedMemoryServer: ... + def SharedMemory(self, size: int) -> _SharedMemory: ... + def ShareableList(self, sequence: Iterable[_SLT] | None) -> _ShareableList[_SLT]: ... + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 5ad4bfe93fe9..465c8e08c134 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,8 +1,8 @@ import sys from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType -from typing import Any, Generic, TypeVar -from typing_extensions import Literal, Self +from typing import Any, Generic, Literal, TypeVar +from typing_extensions import Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -13,18 +13,9 @@ _S = TypeVar("_S") _T = TypeVar("_T") class ApplyResult(Generic[_T]): - if sys.version_info >= (3, 8): - def __init__( - self, pool: Pool, callback: Callable[[_T], object] | None, error_callback: Callable[[BaseException], object] | None - ) -> None: ... - else: - def __init__( - self, - cache: dict[int, ApplyResult[Any]], - callback: Callable[[_T], object] | None, - error_callback: Callable[[BaseException], object] | None, - ) -> None: ... - + def __init__( + self, pool: Pool, callback: Callable[[_T], object] | None, error_callback: Callable[[BaseException], object] | None + ) -> None: ... def get(self, timeout: float | None = None) -> _T: ... def wait(self, timeout: float | None = None) -> None: ... def ready(self) -> bool: ... @@ -36,31 +27,17 @@ class ApplyResult(Generic[_T]): AsyncResult = ApplyResult class MapResult(ApplyResult[list[_T]]): - if sys.version_info >= (3, 8): - def __init__( - self, - pool: Pool, - chunksize: int, - length: int, - callback: Callable[[list[_T]], object] | None, - error_callback: Callable[[BaseException], object] | None, - ) -> None: ... - else: - def __init__( - self, - cache: dict[int, ApplyResult[Any]], - chunksize: int, - length: int, - callback: Callable[[list[_T]], object] | None, - error_callback: Callable[[BaseException], object] | None, - ) -> None: ... + def __init__( + self, + pool: Pool, + chunksize: int, + length: int, + callback: Callable[[list[_T]], object] | None, + error_callback: Callable[[BaseException], object] | None, + ) -> None: ... class IMapIterator(Iterator[_T]): - if sys.version_info >= (3, 8): - def __init__(self, pool: Pool) -> None: ... - else: - def __init__(self, cache: dict[int, IMapIterator[Any]]) -> None: ... - + def __init__(self, pool: Pool) -> None: ... def __iter__(self) -> Self: ... def next(self, timeout: float | None = None) -> _T: ... def __next__(self, timeout: float | None = None) -> _T: ... @@ -120,12 +97,7 @@ class ThreadPool(Pool): ) -> None: ... # undocumented -if sys.version_info >= (3, 8): - INIT: Literal["INIT"] - RUN: Literal["RUN"] - CLOSE: Literal["CLOSE"] - TERMINATE: Literal["TERMINATE"] -else: - RUN: Literal[0] - CLOSE: Literal[1] - TERMINATE: Literal[2] +INIT: Literal["INIT"] +RUN: Literal["RUN"] +CLOSE: Literal["CLOSE"] +TERMINATE: Literal["TERMINATE"] diff --git a/mypy/typeshed/stdlib/multiprocessing/process.pyi b/mypy/typeshed/stdlib/multiprocessing/process.pyi index 9863013fc05f..4d129b27b0e8 100644 --- a/mypy/typeshed/stdlib/multiprocessing/process.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/process.pyi @@ -1,11 +1,7 @@ -import sys from collections.abc import Callable, Iterable, Mapping from typing import Any -if sys.version_info >= (3, 8): - __all__ = ["BaseProcess", "current_process", "active_children", "parent_process"] -else: - __all__ = ["BaseProcess", "current_process", "active_children"] +__all__ = ["BaseProcess", "current_process", "active_children", "parent_process"] class BaseProcess: name: str @@ -40,6 +36,4 @@ class BaseProcess: def current_process() -> BaseProcess: ... def active_children() -> list[BaseProcess]: ... - -if sys.version_info >= (3, 8): - def parent_process() -> BaseProcess | None: ... +def parent_process() -> BaseProcess | None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index e5a8cde8f849..ad80169b463c 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -8,8 +8,7 @@ from copyreg import _DispatchTableType from multiprocessing import connection from pickle import _ReducedType from socket import socket -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal if sys.platform == "win32": __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"] @@ -32,13 +31,9 @@ register = ForkingPickler.register def dump(obj: Any, file: SupportsWrite[bytes], protocol: int | None = None) -> None: ... if sys.platform == "win32": - if sys.version_info >= (3, 8): - def duplicate( - handle: int, target_process: int | None = None, inheritable: bool = False, *, source_process: int | None = None - ) -> int: ... - else: - def duplicate(handle: int, target_process: int | None = None, inheritable: bool = False) -> int: ... - + def duplicate( + handle: int, target_process: int | None = None, inheritable: bool = False, *, source_process: int | None = None + ) -> int: ... def steal_handle(source_pid: int, handle: int) -> int: ... def send_handle(conn: connection.PipeConnection, handle: int, destination_pid: int) -> None: ... def recv_handle(conn: connection.PipeConnection) -> int: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 636d58842158..3979f14cf636 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -4,8 +4,7 @@ from ctypes import _CData, _SimpleCData, c_char from multiprocessing.context import BaseContext from multiprocessing.synchronize import _LockLike from types import TracebackType -from typing import Any, Generic, Protocol, TypeVar, overload -from typing_extensions import Literal +from typing import Any, Generic, Literal, Protocol, TypeVar, overload __all__ = ["RawValue", "RawArray", "Value", "Array", "copy", "synchronized"] diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi index f948c1430c90..969c657e9aab 100644 --- a/mypy/typeshed/stdlib/nntplib.pyi +++ b/mypy/typeshed/stdlib/nntplib.pyi @@ -5,8 +5,8 @@ import sys from _typeshed import Unused from builtins import list as _list # conflicts with a method named "list" from collections.abc import Iterable -from typing import IO, Any, NamedTuple -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Literal, NamedTuple +from typing_extensions import Self, TypeAlias __all__ = [ "NNTP", diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi index 1a58b52de050..bfa880ee03a8 100644 --- a/mypy/typeshed/stdlib/ntpath.pyi +++ b/mypy/typeshed/stdlib/ntpath.pyi @@ -42,11 +42,11 @@ from posixpath import ( splitext as splitext, supports_unicode_filenames as supports_unicode_filenames, ) +from typing import AnyStr, overload +from typing_extensions import LiteralString if sys.version_info >= (3, 12): from posixpath import isjunction as isjunction, splitroot as splitroot -from typing import AnyStr, overload -from typing_extensions import LiteralString __all__ = [ "normcase", diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi index 55f21041ae44..c2a0301d5636 100644 --- a/mypy/typeshed/stdlib/numbers.pyi +++ b/mypy/typeshed/stdlib/numbers.pyi @@ -1,8 +1,9 @@ # Note: these stubs are incomplete. The more complex type # signatures are currently omitted. +from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Any, SupportsFloat, overload +from typing import SupportsFloat, overload __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -16,36 +17,36 @@ class Complex(Number): def __bool__(self) -> bool: ... @property @abstractmethod - def real(self) -> Any: ... + def real(self): ... @property @abstractmethod - def imag(self) -> Any: ... + def imag(self): ... @abstractmethod - def __add__(self, other: Any) -> Any: ... + def __add__(self, other): ... @abstractmethod - def __radd__(self, other: Any) -> Any: ... + def __radd__(self, other): ... @abstractmethod - def __neg__(self) -> Any: ... + def __neg__(self): ... @abstractmethod - def __pos__(self) -> Any: ... - def __sub__(self, other: Any) -> Any: ... - def __rsub__(self, other: Any) -> Any: ... + def __pos__(self): ... + def __sub__(self, other): ... + def __rsub__(self, other): ... @abstractmethod - def __mul__(self, other: Any) -> Any: ... + def __mul__(self, other): ... @abstractmethod - def __rmul__(self, other: Any) -> Any: ... + def __rmul__(self, other): ... @abstractmethod - def __truediv__(self, other: Any) -> Any: ... + def __truediv__(self, other): ... @abstractmethod - def __rtruediv__(self, other: Any) -> Any: ... + def __rtruediv__(self, other): ... @abstractmethod - def __pow__(self, exponent: Any) -> Any: ... + def __pow__(self, exponent): ... @abstractmethod - def __rpow__(self, base: Any) -> Any: ... + def __rpow__(self, base): ... @abstractmethod def __abs__(self) -> Real: ... @abstractmethod - def conjugate(self) -> Any: ... + def conjugate(self): ... @abstractmethod def __eq__(self, other: object) -> bool: ... @@ -63,27 +64,27 @@ class Real(Complex, SupportsFloat): def __round__(self, ndigits: None = None) -> int: ... @abstractmethod @overload - def __round__(self, ndigits: int) -> Any: ... - def __divmod__(self, other: Any) -> Any: ... - def __rdivmod__(self, other: Any) -> Any: ... + def __round__(self, ndigits: int): ... + def __divmod__(self, other): ... + def __rdivmod__(self, other): ... @abstractmethod - def __floordiv__(self, other: Any) -> int: ... + def __floordiv__(self, other) -> int: ... @abstractmethod - def __rfloordiv__(self, other: Any) -> int: ... + def __rfloordiv__(self, other) -> int: ... @abstractmethod - def __mod__(self, other: Any) -> Any: ... + def __mod__(self, other): ... @abstractmethod - def __rmod__(self, other: Any) -> Any: ... + def __rmod__(self, other): ... @abstractmethod - def __lt__(self, other: Any) -> bool: ... + def __lt__(self, other) -> bool: ... @abstractmethod - def __le__(self, other: Any) -> bool: ... + def __le__(self, other) -> bool: ... def __complex__(self) -> complex: ... @property - def real(self) -> Any: ... + def real(self): ... @property - def imag(self) -> Any: ... - def conjugate(self) -> Any: ... + def imag(self): ... + def conjugate(self): ... class Rational(Real): @property @@ -99,29 +100,29 @@ class Integral(Rational): def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent: Any, modulus: Any | None = None) -> Any: ... + def __pow__(self, exponent, modulus: Incomplete | None = None): ... @abstractmethod - def __lshift__(self, other: Any) -> Any: ... + def __lshift__(self, other): ... @abstractmethod - def __rlshift__(self, other: Any) -> Any: ... + def __rlshift__(self, other): ... @abstractmethod - def __rshift__(self, other: Any) -> Any: ... + def __rshift__(self, other): ... @abstractmethod - def __rrshift__(self, other: Any) -> Any: ... + def __rrshift__(self, other): ... @abstractmethod - def __and__(self, other: Any) -> Any: ... + def __and__(self, other): ... @abstractmethod - def __rand__(self, other: Any) -> Any: ... + def __rand__(self, other): ... @abstractmethod - def __xor__(self, other: Any) -> Any: ... + def __xor__(self, other): ... @abstractmethod - def __rxor__(self, other: Any) -> Any: ... + def __rxor__(self, other): ... @abstractmethod - def __or__(self, other: Any) -> Any: ... + def __or__(self, other): ... @abstractmethod - def __ror__(self, other: Any) -> Any: ... + def __ror__(self, other): ... @abstractmethod - def __invert__(self) -> Any: ... + def __invert__(self): ... def __float__(self) -> float: ... @property def numerator(self) -> int: ... diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi index f852489ffacf..02da0c9f954a 100644 --- a/mypy/typeshed/stdlib/opcode.pyi +++ b/mypy/typeshed/stdlib/opcode.pyi @@ -1,5 +1,5 @@ import sys -from typing_extensions import Literal +from typing import Literal __all__ = [ "cmp_op", @@ -56,8 +56,4 @@ opmap: dict[str, int] HAVE_ARGUMENT: Literal[90] EXTENDED_ARG: Literal[144] -if sys.version_info >= (3, 8): - def stack_effect(__opcode: int, __oparg: int | None = None, *, jump: bool | None = None) -> int: ... - -else: - def stack_effect(__opcode: int, __oparg: int | None = None) -> int: ... +def stack_effect(__opcode: int, __oparg: int | None = None, *, jump: bool | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index e92dd7a095d6..3b277460d8f6 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -25,17 +25,28 @@ from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMappin from contextlib import AbstractContextManager from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper as _TextIOWrapper from subprocess import Popen -from typing import IO, Any, AnyStr, BinaryIO, Generic, NoReturn, Protocol, TypeVar, overload, runtime_checkable -from typing_extensions import Final, Literal, Self, TypeAlias, Unpack, final +from typing import ( + IO, + Any, + AnyStr, + BinaryIO, + Final, + Generic, + Literal, + NoReturn, + Protocol, + TypeVar, + final, + overload, + runtime_checkable, +) +from typing_extensions import Self, TypeAlias, Unpack from . import path as _path if sys.version_info >= (3, 9): from types import GenericAlias -if sys.platform != "win32": - from resource import struct_rusage - # This unnecessary alias is to work around various errors path = _path @@ -361,9 +372,8 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo if sys.platform == "win32": @property def st_file_attributes(self) -> int: ... - if sys.version_info >= (3, 8): - @property - def st_reparse_tag(self) -> int: ... + @property + def st_reparse_tag(self) -> int: ... if sys.version_info >= (3, 12): @property def st_birthtime(self) -> float: ... # time of file creation in seconds @@ -965,6 +975,8 @@ else: def waitid(__idtype: int, __ident: int, __options: int) -> waitid_result | None: ... + from resource import struct_rusage + def wait3(options: int) -> tuple[int, int, struct_rusage]: ... def wait4(pid: int, options: int) -> tuple[int, int, struct_rusage]: ... def WCOREDUMP(__status: int) -> bool: ... @@ -975,36 +987,35 @@ else: def WEXITSTATUS(status: int) -> int: ... def WSTOPSIG(status: int) -> int: ... def WTERMSIG(status: int) -> int: ... - if sys.version_info >= (3, 8): - def posix_spawn( - __path: StrOrBytesPath, - __argv: _ExecVArgs, - __env: _ExecEnv, - *, - file_actions: Sequence[tuple[Any, ...]] | None = ..., - setpgroup: int | None = ..., - resetids: bool = ..., - setsid: bool = ..., - setsigmask: Iterable[int] = ..., - setsigdef: Iterable[int] = ..., - scheduler: tuple[Any, sched_param] | None = ..., - ) -> int: ... - def posix_spawnp( - __path: StrOrBytesPath, - __argv: _ExecVArgs, - __env: _ExecEnv, - *, - file_actions: Sequence[tuple[Any, ...]] | None = ..., - setpgroup: int | None = ..., - resetids: bool = ..., - setsid: bool = ..., - setsigmask: Iterable[int] = ..., - setsigdef: Iterable[int] = ..., - scheduler: tuple[Any, sched_param] | None = ..., - ) -> int: ... - POSIX_SPAWN_OPEN: int - POSIX_SPAWN_CLOSE: int - POSIX_SPAWN_DUP2: int + def posix_spawn( + __path: StrOrBytesPath, + __argv: _ExecVArgs, + __env: _ExecEnv, + *, + file_actions: Sequence[tuple[Any, ...]] | None = ..., + setpgroup: int | None = ..., + resetids: bool = ..., + setsid: bool = ..., + setsigmask: Iterable[int] = ..., + setsigdef: Iterable[int] = ..., + scheduler: tuple[Any, sched_param] | None = ..., + ) -> int: ... + def posix_spawnp( + __path: StrOrBytesPath, + __argv: _ExecVArgs, + __env: _ExecEnv, + *, + file_actions: Sequence[tuple[Any, ...]] | None = ..., + setpgroup: int | None = ..., + resetids: bool = ..., + setsid: bool = ..., + setsigmask: Iterable[int] = ..., + setsigdef: Iterable[int] = ..., + scheduler: tuple[Any, sched_param] | None = ..., + ) -> int: ... + POSIX_SPAWN_OPEN: int + POSIX_SPAWN_CLOSE: int + POSIX_SPAWN_DUP2: int if sys.platform != "win32": @final @@ -1048,38 +1059,36 @@ if sys.platform != "win32": after_in_child: Callable[..., Any] | None = ..., ) -> None: ... -if sys.version_info >= (3, 8): - if sys.platform == "win32": - class _AddedDllDirectory: - path: str | None - def __init__(self, path: str | None, cookie: _T, remove_dll_directory: Callable[[_T], object]) -> None: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__(self, *args: Unused) -> None: ... - - def add_dll_directory(path: str) -> _AddedDllDirectory: ... - if sys.platform == "linux": - MFD_CLOEXEC: int - MFD_ALLOW_SEALING: int - MFD_HUGETLB: int - MFD_HUGE_SHIFT: int - MFD_HUGE_MASK: int - MFD_HUGE_64KB: int - MFD_HUGE_512KB: int - MFD_HUGE_1MB: int - MFD_HUGE_2MB: int - MFD_HUGE_8MB: int - MFD_HUGE_16MB: int - MFD_HUGE_32MB: int - MFD_HUGE_256MB: int - MFD_HUGE_512MB: int - MFD_HUGE_1GB: int - MFD_HUGE_2GB: int - MFD_HUGE_16GB: int - def memfd_create(name: str, flags: int = ...) -> int: ... - def copy_file_range( - src: int, dst: int, count: int, offset_src: int | None = ..., offset_dst: int | None = ... - ) -> int: ... +if sys.platform == "win32": + class _AddedDllDirectory: + path: str | None + def __init__(self, path: str | None, cookie: _T, remove_dll_directory: Callable[[_T], object]) -> None: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, *args: Unused) -> None: ... + + def add_dll_directory(path: str) -> _AddedDllDirectory: ... + +if sys.platform == "linux": + MFD_CLOEXEC: int + MFD_ALLOW_SEALING: int + MFD_HUGETLB: int + MFD_HUGE_SHIFT: int + MFD_HUGE_MASK: int + MFD_HUGE_64KB: int + MFD_HUGE_512KB: int + MFD_HUGE_1MB: int + MFD_HUGE_2MB: int + MFD_HUGE_8MB: int + MFD_HUGE_16MB: int + MFD_HUGE_32MB: int + MFD_HUGE_256MB: int + MFD_HUGE_512MB: int + MFD_HUGE_1GB: int + MFD_HUGE_2GB: int + MFD_HUGE_16GB: int + def memfd_create(name: str, flags: int = ...) -> int: ... + def copy_file_range(src: int, dst: int, count: int, offset_src: int | None = ..., offset_dst: int | None = ...) -> int: ... if sys.version_info >= (3, 9): def waitstatus_to_exitcode(status: int) -> int: ... diff --git a/mypy/typeshed/stdlib/ossaudiodev.pyi b/mypy/typeshed/stdlib/ossaudiodev.pyi index d956a89729fd..b9ee3edab033 100644 --- a/mypy/typeshed/stdlib/ossaudiodev.pyi +++ b/mypy/typeshed/stdlib/ossaudiodev.pyi @@ -1,6 +1,5 @@ import sys -from typing import Any, overload -from typing_extensions import Literal +from typing import Any, Literal, overload if sys.platform != "win32" and sys.platform != "darwin": AFMT_AC3: int diff --git a/mypy/typeshed/stdlib/parser.pyi b/mypy/typeshed/stdlib/parser.pyi index cce8594eac58..bafc8015fed9 100644 --- a/mypy/typeshed/stdlib/parser.pyi +++ b/mypy/typeshed/stdlib/parser.pyi @@ -1,8 +1,7 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from types import CodeType -from typing import Any -from typing_extensions import final +from typing import Any, final def expr(source: str) -> STType: ... def suite(source: str) -> STType: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 10ffa4a778e8..c3b0b7ad6337 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -14,8 +14,8 @@ from collections.abc import Callable, Generator, Iterator, Sequence from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from os import PathLike, stat_result from types import TracebackType -from typing import IO, Any, BinaryIO, overload -from typing_extensions import Literal, Self +from typing import IO, Any, BinaryIO, Literal, overload +from typing_extensions import Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -196,13 +196,9 @@ class Path(PurePath): if sys.version_info >= (3, 9): def readlink(self) -> Self: ... - if sys.version_info >= (3, 8): - def rename(self, target: str | PurePath) -> Self: ... - def replace(self, target: str | PurePath) -> Self: ... - else: - def rename(self, target: str | PurePath) -> None: ... - def replace(self, target: str | PurePath) -> None: ... + def rename(self, target: str | PurePath) -> Self: ... + def replace(self, target: str | PurePath) -> Self: ... def resolve(self, strict: bool = False) -> Self: ... def rmdir(self) -> None: ... def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ... @@ -210,11 +206,7 @@ class Path(PurePath): def hardlink_to(self, target: StrOrBytesPath) -> None: ... def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: ... - if sys.version_info >= (3, 8): - def unlink(self, missing_ok: bool = False) -> None: ... - else: - def unlink(self) -> None: ... - + def unlink(self, missing_ok: bool = False) -> None: ... @classmethod def home(cls) -> Self: ... def absolute(self) -> Self: ... @@ -229,7 +221,7 @@ class Path(PurePath): ) -> int: ... else: def write_text(self, data: str, encoding: str | None = None, errors: str | None = None) -> int: ... - if sys.version_info >= (3, 8) and sys.version_info < (3, 12): + if sys.version_info < (3, 12): def link_to(self, target: StrOrBytesPath) -> None: ... if sys.version_info >= (3, 12): def walk( diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index cf3995d74f5d..0a4d439976ff 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -1,10 +1,10 @@ -import sys from _typeshed import ReadableBuffer, SupportsWrite from collections.abc import Callable, Iterable, Iterator, Mapping -from typing import Any, ClassVar, Protocol, SupportsBytes -from typing_extensions import SupportsIndex, TypeAlias, final +from typing import Any, ClassVar, Protocol, SupportsBytes, SupportsIndex, final +from typing_extensions import TypeAlias __all__ = [ + "PickleBuffer", "PickleError", "PicklingError", "UnpicklingError", @@ -30,6 +30,7 @@ __all__ = [ "BINUNICODE", "BINUNICODE8", "BUILD", + "BYTEARRAY8", "DEFAULT_PROTOCOL", "DICT", "DUP", @@ -61,6 +62,7 @@ __all__ = [ "NEWOBJ", "NEWOBJ_EX", "NEWTRUE", + "NEXT_BUFFER", "NONE", "OBJ", "PERSID", @@ -68,6 +70,7 @@ __all__ = [ "POP_MARK", "PROTO", "PUT", + "READONLY_BUFFER", "REDUCE", "SETITEM", "SETITEMS", @@ -85,9 +88,6 @@ __all__ = [ "UNICODE", ] -if sys.version_info >= (3, 8): - __all__ += ["BYTEARRAY8", "NEXT_BUFFER", "PickleBuffer", "READONLY_BUFFER"] - HIGHEST_PROTOCOL: int DEFAULT_PROTOCOL: int @@ -97,48 +97,43 @@ class _ReadableFileobj(Protocol): def read(self, __n: int) -> bytes: ... def readline(self) -> bytes: ... -if sys.version_info >= (3, 8): - @final - class PickleBuffer: - def __init__(self, buffer: ReadableBuffer) -> None: ... - def raw(self) -> memoryview: ... - def release(self) -> None: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... - _BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None - def dump( - obj: Any, - file: SupportsWrite[bytes], - protocol: int | None = None, - *, - fix_imports: bool = True, - buffer_callback: _BufferCallback = None, - ) -> None: ... - def dumps( - obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None - ) -> bytes: ... - def load( - file: _ReadableFileobj, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), - ) -> Any: ... - def loads( - __data: ReadableBuffer, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), - ) -> Any: ... +@final +class PickleBuffer: + def __init__(self, buffer: ReadableBuffer) -> None: ... + def raw(self) -> memoryview: ... + def release(self) -> None: ... + def __buffer__(self, __flags: int) -> memoryview: ... + def __release_buffer__(self, __buffer: memoryview) -> None: ... + +_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None -else: - def dump(obj: Any, file: SupportsWrite[bytes], protocol: int | None = None, *, fix_imports: bool = True) -> None: ... - def dumps(obj: Any, protocol: int | None = None, *, fix_imports: bool = True) -> bytes: ... - def load(file: _ReadableFileobj, *, fix_imports: bool = True, encoding: str = "ASCII", errors: str = "strict") -> Any: ... - def loads(data: ReadableBuffer, *, fix_imports: bool = True, encoding: str = "ASCII", errors: str = "strict") -> Any: ... +def dump( + obj: Any, + file: SupportsWrite[bytes], + protocol: int | None = None, + *, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, +) -> None: ... +def dumps( + obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None +) -> bytes: ... +def load( + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... +def loads( + __data: ReadableBuffer, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... class PickleError(Exception): ... class PicklingError(PickleError): ... @@ -158,19 +153,15 @@ class Pickler: bin: bool # undocumented dispatch: ClassVar[dict[type, Callable[[Unpickler, Any], None]]] # undocumented, _Pickler only - if sys.version_info >= (3, 8): - def __init__( - self, - file: SupportsWrite[bytes], - protocol: int | None = ..., - *, - fix_imports: bool = ..., - buffer_callback: _BufferCallback = ..., - ) -> None: ... - def reducer_override(self, obj: Any) -> Any: ... - else: - def __init__(self, file: SupportsWrite[bytes], protocol: int | None = ..., *, fix_imports: bool = ...) -> None: ... - + def __init__( + self, + file: SupportsWrite[bytes], + protocol: int | None = ..., + *, + fix_imports: bool = ..., + buffer_callback: _BufferCallback = ..., + ) -> None: ... + def reducer_override(self, obj: Any) -> Any: ... def dump(self, __obj: Any) -> None: ... def clear_memo(self) -> None: ... def persistent_id(self, obj: Any) -> Any: ... @@ -178,21 +169,15 @@ class Pickler: class Unpickler: dispatch: ClassVar[dict[int, Callable[[Unpickler], None]]] # undocumented, _Unpickler only - if sys.version_info >= (3, 8): - def __init__( - self, - file: _ReadableFileobj, - *, - fix_imports: bool = ..., - encoding: str = ..., - errors: str = ..., - buffers: Iterable[Any] | None = ..., - ) -> None: ... - else: - def __init__( - self, file: _ReadableFileobj, *, fix_imports: bool = ..., encoding: str = ..., errors: str = ... - ) -> None: ... - + def __init__( + self, + file: _ReadableFileobj, + *, + fix_imports: bool = ..., + encoding: str = ..., + errors: str = ..., + buffers: Iterable[Any] | None = ..., + ) -> None: ... def load(self) -> Any: ... def find_class(self, __module_name: str, __global_name: str) -> Any: ... def persistent_load(self, pid: Any) -> Any: ... @@ -272,11 +257,10 @@ STACK_GLOBAL: bytes MEMOIZE: bytes FRAME: bytes -if sys.version_info >= (3, 8): - # Protocol 5 - BYTEARRAY8: bytes - NEXT_BUFFER: bytes - READONLY_BUFFER: bytes +# protocol 5 +BYTEARRAY8: bytes +NEXT_BUFFER: bytes +READONLY_BUFFER: bytes def encode_long(x: int) -> bytes: ... # undocumented def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ... # undocumented diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi index 483d6c454c2e..f0e6d4123e1d 100644 --- a/mypy/typeshed/stdlib/platform.pyi +++ b/mypy/typeshed/stdlib/platform.pyi @@ -1,37 +1,10 @@ import sys - -if sys.version_info < (3, 8): - import os - - DEV_NULL = os.devnull from typing import NamedTuple -if sys.version_info >= (3, 8): - def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ... - -else: - def libc_ver( - executable: str = sys.executable, lib: str = "", version: str = "", chunksize: int = 16384 - ) -> tuple[str, str]: ... - -if sys.version_info < (3, 8): - def linux_distribution( - distname: str = "", - version: str = "", - id: str = "", - supported_dists: tuple[str, ...] = ..., - full_distribution_name: bool = ..., - ) -> tuple[str, str, str]: ... - def dist( - distname: str = "", version: str = "", id: str = "", supported_dists: tuple[str, ...] = ... - ) -> tuple[str, str, str]: ... - +def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ... def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ... - -if sys.version_info >= (3, 8): - def win32_edition() -> str: ... - def win32_is_iot() -> bool: ... - +def win32_edition() -> str: ... +def win32_is_iot() -> bool: ... def mac_ver( release: str = "", versioninfo: tuple[str, str, str] = ("", "", ""), machine: str = "" ) -> tuple[str, tuple[str, str, str], str]: ... diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index bd5525484514..f7912a784987 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -6,39 +6,9 @@ from enum import Enum from typing import IO, Any from typing_extensions import Self -if sys.version_info >= (3, 9): - __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"] -elif sys.version_info >= (3, 8): - __all__ = [ - "readPlist", - "writePlist", - "readPlistFromBytes", - "writePlistToBytes", - "Data", - "InvalidFileException", - "FMT_XML", - "FMT_BINARY", - "load", - "dump", - "loads", - "dumps", - "UID", - ] -else: - __all__ = [ - "readPlist", - "writePlist", - "readPlistFromBytes", - "writePlistToBytes", - "Data", - "InvalidFileException", - "FMT_XML", - "FMT_BINARY", - "load", - "dump", - "loads", - "dumps", - ] +__all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"] +if sys.version_info < (3, 9): + __all__ += ["readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "Data"] class PlistFormat(Enum): FMT_XML: int @@ -46,8 +16,23 @@ class PlistFormat(Enum): FMT_XML = PlistFormat.FMT_XML FMT_BINARY = PlistFormat.FMT_BINARY +if sys.version_info >= (3, 13): + def load( + fp: IO[bytes], + *, + fmt: PlistFormat | None = None, + dict_type: type[MutableMapping[str, Any]] = ..., + aware_datetime: bool = False, + ) -> Any: ... + def loads( + value: ReadableBuffer | str, + *, + fmt: PlistFormat | None = None, + dict_type: type[MutableMapping[str, Any]] = ..., + aware_datetime: bool = False, + ) -> Any: ... -if sys.version_info >= (3, 9): +elif sys.version_info >= (3, 9): def load(fp: IO[bytes], *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ...) -> Any: ... def loads( value: ReadableBuffer, *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ... @@ -69,21 +54,41 @@ else: dict_type: type[MutableMapping[str, Any]] = ..., ) -> Any: ... -def dump( - value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, - fp: IO[bytes], - *, - fmt: PlistFormat = ..., - sort_keys: bool = True, - skipkeys: bool = False, -) -> None: ... -def dumps( - value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, - *, - fmt: PlistFormat = ..., - skipkeys: bool = False, - sort_keys: bool = True, -) -> bytes: ... +if sys.version_info >= (3, 13): + def dump( + value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, + fp: IO[bytes], + *, + fmt: PlistFormat = ..., + sort_keys: bool = True, + skipkeys: bool = False, + aware_datetime: bool = False, + ) -> None: ... + def dumps( + value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, + *, + fmt: PlistFormat = ..., + skipkeys: bool = False, + sort_keys: bool = True, + aware_datetime: bool = False, + ) -> bytes: ... + +else: + def dump( + value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, + fp: IO[bytes], + *, + fmt: PlistFormat = ..., + sort_keys: bool = True, + skipkeys: bool = False, + ) -> None: ... + def dumps( + value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, + *, + fmt: PlistFormat = ..., + skipkeys: bool = False, + sort_keys: bool = True, + ) -> bytes: ... if sys.version_info < (3, 9): def readPlist(pathOrFile: str | IO[bytes]) -> Any: ... @@ -96,14 +101,13 @@ if sys.version_info < (3, 9): data: bytes def __init__(self, data: bytes) -> None: ... -if sys.version_info >= (3, 8): - class UID: - data: int - def __init__(self, data: int) -> None: ... - def __index__(self) -> int: ... - def __reduce__(self) -> tuple[type[Self], tuple[int]]: ... - def __hash__(self) -> int: ... - def __eq__(self, other: object) -> bool: ... +class UID: + data: int + def __init__(self, data: int) -> None: ... + def __index__(self) -> int: ... + def __reduce__(self) -> tuple[type[Self], tuple[int]]: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... class InvalidFileException(ValueError): def __init__(self, message: str = "Invalid file") -> None: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 808e7e5222af..12f1d16a0d6f 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -3,8 +3,8 @@ import ssl import sys from builtins import list as _list # conflicts with a method named "list" from re import Pattern -from typing import Any, BinaryIO, NoReturn, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, BinaryIO, Literal, NoReturn, overload +from typing_extensions import TypeAlias __all__ = ["POP3", "error_proto", "POP3_SSL"] diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 81cc93c5aa66..6cba003bbd5f 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -51,6 +51,9 @@ if sys.platform != "win32": P_ALL as P_ALL, P_PGID as P_PGID, P_PID as P_PID, + POSIX_SPAWN_CLOSE as POSIX_SPAWN_CLOSE, + POSIX_SPAWN_DUP2 as POSIX_SPAWN_DUP2, + POSIX_SPAWN_OPEN as POSIX_SPAWN_OPEN, PRIO_PGRP as PRIO_PGRP, PRIO_PROCESS as PRIO_PROCESS, PRIO_USER as PRIO_USER, @@ -161,12 +164,17 @@ if sys.platform != "win32": pathconf as pathconf, pathconf_names as pathconf_names, pipe as pipe, + posix_spawn as posix_spawn, + posix_spawnp as posix_spawnp, pread as pread, + preadv as preadv, putenv as putenv, pwrite as pwrite, + pwritev as pwritev, read as read, readlink as readlink, readv as readv, + register_at_fork as register_at_fork, remove as remove, rename as rename, replace as replace, @@ -222,48 +230,28 @@ if sys.platform != "win32": writev as writev, ) - if sys.platform != "darwin": - from os import EX_NOTFOUND as EX_NOTFOUND + if sys.version_info >= (3, 9): + from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode - if sys.platform == "linux": - from os import ( - GRND_NONBLOCK as GRND_NONBLOCK, - GRND_RANDOM as GRND_RANDOM, - RTLD_DEEPBIND as RTLD_DEEPBIND, - XATTR_CREATE as XATTR_CREATE, - XATTR_REPLACE as XATTR_REPLACE, - XATTR_SIZE_MAX as XATTR_SIZE_MAX, - getrandom as getrandom, - getxattr as getxattr, - listxattr as listxattr, - removexattr as removexattr, - setxattr as setxattr, - ) + if sys.version_info >= (3, 11): + from os import login_tty as login_tty - if sys.version_info >= (3, 10): - from os import ( - EFD_CLOEXEC as EFD_CLOEXEC, - EFD_NONBLOCK as EFD_NONBLOCK, - EFD_SEMAPHORE as EFD_SEMAPHORE, - SPLICE_F_MORE as SPLICE_F_MORE, - SPLICE_F_MOVE as SPLICE_F_MOVE, - SPLICE_F_NONBLOCK as SPLICE_F_NONBLOCK, - eventfd as eventfd, - eventfd_read as eventfd_read, - eventfd_write as eventfd_write, - splice as splice, - ) - else: + if sys.platform != "linux": from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod if sys.platform != "darwin": from os import ( + EX_NOTFOUND as EX_NOTFOUND, POSIX_FADV_DONTNEED as POSIX_FADV_DONTNEED, POSIX_FADV_NOREUSE as POSIX_FADV_NOREUSE, POSIX_FADV_NORMAL as POSIX_FADV_NORMAL, POSIX_FADV_RANDOM as POSIX_FADV_RANDOM, POSIX_FADV_SEQUENTIAL as POSIX_FADV_SEQUENTIAL, POSIX_FADV_WILLNEED as POSIX_FADV_WILLNEED, + RWF_DSYNC as RWF_DSYNC, + RWF_HIPRI as RWF_HIPRI, + RWF_NOWAIT as RWF_NOWAIT, + RWF_SYNC as RWF_SYNC, fdatasync as fdatasync, getresgid as getresgid, getresuid as getresuid, @@ -286,78 +274,85 @@ if sys.platform != "win32": if sys.version_info >= (3, 10): from os import RWF_APPEND as RWF_APPEND - if sys.version_info >= (3, 11): - from os import login_tty as login_tty - - if sys.version_info >= (3, 9): - from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode - - if sys.platform == "linux": - from os import P_PIDFD as P_PIDFD, pidfd_open as pidfd_open - - if sys.version_info >= (3, 8): + if sys.platform == "linux": from os import ( - POSIX_SPAWN_CLOSE as POSIX_SPAWN_CLOSE, - POSIX_SPAWN_DUP2 as POSIX_SPAWN_DUP2, - POSIX_SPAWN_OPEN as POSIX_SPAWN_OPEN, - posix_spawn as posix_spawn, - posix_spawnp as posix_spawnp, + GRND_NONBLOCK as GRND_NONBLOCK, + GRND_RANDOM as GRND_RANDOM, + MFD_ALLOW_SEALING as MFD_ALLOW_SEALING, + MFD_CLOEXEC as MFD_CLOEXEC, + MFD_HUGE_1GB as MFD_HUGE_1GB, + MFD_HUGE_1MB as MFD_HUGE_1MB, + MFD_HUGE_2GB as MFD_HUGE_2GB, + MFD_HUGE_2MB as MFD_HUGE_2MB, + MFD_HUGE_8MB as MFD_HUGE_8MB, + MFD_HUGE_16GB as MFD_HUGE_16GB, + MFD_HUGE_16MB as MFD_HUGE_16MB, + MFD_HUGE_32MB as MFD_HUGE_32MB, + MFD_HUGE_64KB as MFD_HUGE_64KB, + MFD_HUGE_256MB as MFD_HUGE_256MB, + MFD_HUGE_512KB as MFD_HUGE_512KB, + MFD_HUGE_512MB as MFD_HUGE_512MB, + MFD_HUGE_MASK as MFD_HUGE_MASK, + MFD_HUGE_SHIFT as MFD_HUGE_SHIFT, + MFD_HUGETLB as MFD_HUGETLB, + RTLD_DEEPBIND as RTLD_DEEPBIND, + XATTR_CREATE as XATTR_CREATE, + XATTR_REPLACE as XATTR_REPLACE, + XATTR_SIZE_MAX as XATTR_SIZE_MAX, + copy_file_range as copy_file_range, + getrandom as getrandom, + getxattr as getxattr, + listxattr as listxattr, + memfd_create as memfd_create, + removexattr as removexattr, + setxattr as setxattr, ) - if sys.platform == "linux": + if sys.version_info >= (3, 9): + from os import P_PIDFD as P_PIDFD, pidfd_open as pidfd_open + + if sys.version_info >= (3, 10): from os import ( - MFD_ALLOW_SEALING as MFD_ALLOW_SEALING, - MFD_CLOEXEC as MFD_CLOEXEC, - MFD_HUGE_1GB as MFD_HUGE_1GB, - MFD_HUGE_1MB as MFD_HUGE_1MB, - MFD_HUGE_2GB as MFD_HUGE_2GB, - MFD_HUGE_2MB as MFD_HUGE_2MB, - MFD_HUGE_8MB as MFD_HUGE_8MB, - MFD_HUGE_16GB as MFD_HUGE_16GB, - MFD_HUGE_16MB as MFD_HUGE_16MB, - MFD_HUGE_32MB as MFD_HUGE_32MB, - MFD_HUGE_64KB as MFD_HUGE_64KB, - MFD_HUGE_256MB as MFD_HUGE_256MB, - MFD_HUGE_512KB as MFD_HUGE_512KB, - MFD_HUGE_512MB as MFD_HUGE_512MB, - MFD_HUGE_MASK as MFD_HUGE_MASK, - MFD_HUGE_SHIFT as MFD_HUGE_SHIFT, - MFD_HUGETLB as MFD_HUGETLB, - copy_file_range as copy_file_range, - memfd_create as memfd_create, + EFD_CLOEXEC as EFD_CLOEXEC, + EFD_NONBLOCK as EFD_NONBLOCK, + EFD_SEMAPHORE as EFD_SEMAPHORE, + SPLICE_F_MORE as SPLICE_F_MORE, + SPLICE_F_MOVE as SPLICE_F_MOVE, + SPLICE_F_NONBLOCK as SPLICE_F_NONBLOCK, + eventfd as eventfd, + eventfd_read as eventfd_read, + eventfd_write as eventfd_write, + splice as splice, ) - from os import preadv as preadv, pwritev as pwritev, register_at_fork as register_at_fork - - if sys.platform != "darwin": - from os import RWF_DSYNC as RWF_DSYNC, RWF_HIPRI as RWF_HIPRI, RWF_NOWAIT as RWF_NOWAIT, RWF_SYNC as RWF_SYNC - if sys.version_info >= (3, 12) and sys.platform == "linux": - from os import ( - CLONE_FILES as CLONE_FILES, - CLONE_FS as CLONE_FS, - CLONE_NEWCGROUP as CLONE_NEWCGROUP, - CLONE_NEWIPC as CLONE_NEWIPC, - CLONE_NEWNET as CLONE_NEWNET, - CLONE_NEWNS as CLONE_NEWNS, - CLONE_NEWPID as CLONE_NEWPID, - CLONE_NEWTIME as CLONE_NEWTIME, - CLONE_NEWUSER as CLONE_NEWUSER, - CLONE_NEWUTS as CLONE_NEWUTS, - CLONE_SIGHAND as CLONE_SIGHAND, - CLONE_SYSVSEM as CLONE_SYSVSEM, - CLONE_THREAD as CLONE_THREAD, - CLONE_VM as CLONE_VM, - setns as setns, - unshare as unshare, - ) + if sys.version_info >= (3, 12): + from os import ( + CLONE_FILES as CLONE_FILES, + CLONE_FS as CLONE_FS, + CLONE_NEWCGROUP as CLONE_NEWCGROUP, + CLONE_NEWIPC as CLONE_NEWIPC, + CLONE_NEWNET as CLONE_NEWNET, + CLONE_NEWNS as CLONE_NEWNS, + CLONE_NEWPID as CLONE_NEWPID, + CLONE_NEWTIME as CLONE_NEWTIME, + CLONE_NEWUSER as CLONE_NEWUSER, + CLONE_NEWUTS as CLONE_NEWUTS, + CLONE_SIGHAND as CLONE_SIGHAND, + CLONE_SYSVSEM as CLONE_SYSVSEM, + CLONE_THREAD as CLONE_THREAD, + CLONE_VM as CLONE_VM, + setns as setns, + unshare as unshare, + ) - if sys.version_info >= (3, 12) and sys.platform == "darwin": - from os import ( - PRIO_DARWIN_BG as PRIO_DARWIN_BG, - PRIO_DARWIN_NONUI as PRIO_DARWIN_NONUI, - PRIO_DARWIN_PROCESS as PRIO_DARWIN_PROCESS, - PRIO_DARWIN_THREAD as PRIO_DARWIN_THREAD, - ) + if sys.platform == "darwin": + if sys.version_info >= (3, 12): + from os import ( + PRIO_DARWIN_BG as PRIO_DARWIN_BG, + PRIO_DARWIN_NONUI as PRIO_DARWIN_NONUI, + PRIO_DARWIN_PROCESS as PRIO_DARWIN_PROCESS, + PRIO_DARWIN_THREAD as PRIO_DARWIN_THREAD, + ) # Not same as os.environ or os.environb # Because of this variable, we can't do "from posix import *" in os/__init__.pyi diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index 45a8ad7ec6a4..29e7c0f01017 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import AnyOrLiteralStr, BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath -from collections.abc import Sequence +from collections.abc import Iterable from genericpath import ( commonprefix as commonprefix, exists as exists, @@ -102,11 +102,11 @@ def normpath(path: PathLike[AnyStr]) -> AnyStr: ... @overload def normpath(path: AnyOrLiteralStr) -> AnyOrLiteralStr: ... @overload -def commonpath(paths: Sequence[LiteralString]) -> LiteralString: ... +def commonpath(paths: Iterable[LiteralString]) -> LiteralString: ... @overload -def commonpath(paths: Sequence[StrPath]) -> str: ... +def commonpath(paths: Iterable[StrPath]) -> str: ... @overload -def commonpath(paths: Sequence[BytesPath]) -> bytes: ... +def commonpath(paths: Iterable[BytesPath]) -> bytes: ... # First parameter is not actually pos-only, # but must be defined as pos-only in the stub or cross-platform code doesn't type-check, diff --git a/mypy/typeshed/stdlib/pprint.pyi b/mypy/typeshed/stdlib/pprint.pyi index 5a909c69b077..171878f4165d 100644 --- a/mypy/typeshed/stdlib/pprint.pyi +++ b/mypy/typeshed/stdlib/pprint.pyi @@ -1,10 +1,7 @@ import sys from typing import IO -if sys.version_info >= (3, 8): - __all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter", "pp"] -else: - __all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter"] +__all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter", "pp"] if sys.version_info >= (3, 10): def pformat( @@ -18,7 +15,7 @@ if sys.version_info >= (3, 10): underscore_numbers: bool = False, ) -> str: ... -elif sys.version_info >= (3, 8): +else: def pformat( object: object, indent: int = 1, @@ -29,9 +26,6 @@ elif sys.version_info >= (3, 8): sort_dicts: bool = True, ) -> str: ... -else: - def pformat(object: object, indent: int = 1, width: int = 80, depth: int | None = None, *, compact: bool = False) -> str: ... - if sys.version_info >= (3, 10): def pp( object: object, @@ -45,7 +39,7 @@ if sys.version_info >= (3, 10): underscore_numbers: bool = ..., ) -> None: ... -elif sys.version_info >= (3, 8): +else: def pp( object: object, stream: IO[str] | None = ..., @@ -70,18 +64,6 @@ if sys.version_info >= (3, 10): underscore_numbers: bool = False, ) -> None: ... -elif sys.version_info >= (3, 8): - def pprint( - object: object, - stream: IO[str] | None = None, - indent: int = 1, - width: int = 80, - depth: int | None = None, - *, - compact: bool = False, - sort_dicts: bool = True, - ) -> None: ... - else: def pprint( object: object, @@ -91,6 +73,7 @@ else: depth: int | None = None, *, compact: bool = False, + sort_dicts: bool = True, ) -> None: ... def isreadable(object: object) -> bool: ... @@ -110,17 +93,6 @@ class PrettyPrinter: sort_dicts: bool = True, underscore_numbers: bool = False, ) -> None: ... - elif sys.version_info >= (3, 8): - def __init__( - self, - indent: int = 1, - width: int = 80, - depth: int | None = None, - stream: IO[str] | None = None, - *, - compact: bool = False, - sort_dicts: bool = True, - ) -> None: ... else: def __init__( self, @@ -130,6 +102,7 @@ class PrettyPrinter: stream: IO[str] | None = None, *, compact: bool = False, + sort_dicts: bool = True, ) -> None: ... def pformat(self, object: object) -> str: ... diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 44ce33469ff9..a6ffd54de005 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -3,8 +3,8 @@ from _typeshed import StrEnum, StrOrBytesPath from collections.abc import Iterable from cProfile import Profile as _cProfile from profile import Profile -from typing import IO, Any, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Literal, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index a6a2d8fabb69..022b08046c54 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -1,6 +1,7 @@ import sys from collections.abc import Callable, Iterable -from typing_extensions import Literal, TypeAlias +from typing import Literal +from typing_extensions import TypeAlias if sys.platform != "win32": __all__ = ["openpty", "fork", "spawn"] diff --git a/mypy/typeshed/stdlib/pwd.pyi b/mypy/typeshed/stdlib/pwd.pyi index 80813479d7af..64e831bcecce 100644 --- a/mypy/typeshed/stdlib/pwd.pyi +++ b/mypy/typeshed/stdlib/pwd.pyi @@ -1,7 +1,6 @@ import sys from _typeshed import structseq -from typing import Any -from typing_extensions import Final, final +from typing import Any, Final, final if sys.platform != "win32": @final diff --git a/mypy/typeshed/stdlib/py_compile.pyi b/mypy/typeshed/stdlib/py_compile.pyi index 48f1d7dc3e70..81561a202883 100644 --- a/mypy/typeshed/stdlib/py_compile.pyi +++ b/mypy/typeshed/stdlib/py_compile.pyi @@ -17,27 +17,15 @@ class PycInvalidationMode(enum.Enum): UNCHECKED_HASH: int def _get_default_invalidation_mode() -> PycInvalidationMode: ... - -if sys.version_info >= (3, 8): - def compile( - file: AnyStr, - cfile: AnyStr | None = None, - dfile: AnyStr | None = None, - doraise: bool = False, - optimize: int = -1, - invalidation_mode: PycInvalidationMode | None = None, - quiet: int = 0, - ) -> AnyStr | None: ... - -else: - def compile( - file: AnyStr, - cfile: AnyStr | None = None, - dfile: AnyStr | None = None, - doraise: bool = False, - optimize: int = -1, - invalidation_mode: PycInvalidationMode | None = None, - ) -> AnyStr | None: ... +def compile( + file: AnyStr, + cfile: AnyStr | None = None, + dfile: AnyStr | None = None, + doraise: bool = False, + optimize: int = -1, + invalidation_mode: PycInvalidationMode | None = None, + quiet: int = 0, +) -> AnyStr | None: ... if sys.version_info >= (3, 10): def main() -> None: ... diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index 1b09bcb059e4..3134de79352d 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -5,8 +5,8 @@ from builtins import list as _list # "list" conflicts with method name from collections.abc import Callable, Container, Mapping, MutableMapping from reprlib import Repr from types import MethodType, ModuleType, TracebackType -from typing import IO, Any, AnyStr, NoReturn, TypeVar -from typing_extensions import Final, TypeGuard +from typing import IO, Any, AnyStr, Final, NoReturn, TypeVar +from typing_extensions import TypeGuard __all__ = ["help"] diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 9e1eea08be54..92d926ebd332 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -1,8 +1,8 @@ from _typeshed import ReadableBuffer, SupportsRead from collections.abc import Callable from pyexpat import errors as errors, model as model -from typing import Any -from typing_extensions import TypeAlias, final +from typing import Any, final +from typing_extensions import TypeAlias EXPAT_VERSION: str # undocumented version_info: tuple[int, int, int] # undocumented diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index ec532ca3cffe..84c6cfceb1de 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -4,8 +4,8 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Callable, Iterator, Mapping from sre_constants import error as error -from typing import Any, AnyStr, Generic, TypeVar, overload -from typing_extensions import Literal, TypeAlias, final +from typing import Any, AnyStr, Generic, Literal, TypeVar, final, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi index 57cefb4681ac..31c55111360a 100644 --- a/mypy/typeshed/stdlib/resource.pyi +++ b/mypy/typeshed/stdlib/resource.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import structseq -from typing_extensions import Final, final +from typing import Final, final if sys.platform != "win32": RLIMIT_AS: int diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index 5e2828e42c30..f2cfc881c1da 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import FileDescriptorLike from collections.abc import Iterable from types import TracebackType -from typing import Any -from typing_extensions import Self, final +from typing import Any, final +from typing_extensions import Self if sys.platform != "win32": PIPE_BUF: int diff --git a/mypy/typeshed/stdlib/shlex.pyi b/mypy/typeshed/stdlib/shlex.pyi index c4fd23d60666..3fda03b5694a 100644 --- a/mypy/typeshed/stdlib/shlex.pyi +++ b/mypy/typeshed/stdlib/shlex.pyi @@ -1,18 +1,11 @@ -import sys from collections.abc import Iterable from typing import TextIO from typing_extensions import Self -if sys.version_info >= (3, 8): - __all__ = ["shlex", "split", "quote", "join"] -else: - __all__ = ["shlex", "split", "quote"] +__all__ = ["shlex", "split", "quote", "join"] def split(s: str, comments: bool = False, posix: bool = True) -> list[str]: ... - -if sys.version_info >= (3, 8): - def join(split_command: Iterable[str]) -> str: ... - +def join(split_command: Iterable[str]) -> str: ... def quote(s: str) -> str: ... class shlex(Iterable[str]): diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index 78e930920073..f6440aa27513 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -48,12 +48,7 @@ class ExecError(OSError): ... class ReadError(OSError): ... class RegistryError(Exception): ... -if sys.version_info >= (3, 8): - def copyfileobj(fsrc: SupportsRead[AnyStr], fdst: SupportsWrite[AnyStr], length: int = 0) -> None: ... - -else: - def copyfileobj(fsrc: SupportsRead[AnyStr], fdst: SupportsWrite[AnyStr], length: int = 16384) -> None: ... - +def copyfileobj(fsrc: SupportsRead[AnyStr], fdst: SupportsWrite[AnyStr], length: int = 0) -> None: ... def copyfile(src: StrOrBytesPath, dst: _StrOrBytesPathT, *, follow_symlinks: bool = True) -> _StrOrBytesPathT: ... def copymode(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ... def copystat(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ... @@ -66,27 +61,15 @@ def copy2(src: StrPath, dst: StrPath, *, follow_symlinks: bool = True) -> _PathR @overload def copy2(src: BytesPath, dst: BytesPath, *, follow_symlinks: bool = True) -> _PathReturn: ... def ignore_patterns(*patterns: StrPath) -> Callable[[Any, list[str]], set[str]]: ... - -if sys.version_info >= (3, 8): - def copytree( - src: StrPath, - dst: StrPath, - symlinks: bool = False, - ignore: None | Callable[[str, list[str]], Iterable[str]] | Callable[[StrPath, list[str]], Iterable[str]] = None, - copy_function: Callable[[str, str], object] = ..., - ignore_dangling_symlinks: bool = False, - dirs_exist_ok: bool = False, - ) -> _PathReturn: ... - -else: - def copytree( - src: StrPath, - dst: StrPath, - symlinks: bool = False, - ignore: None | Callable[[str, list[str]], Iterable[str]] | Callable[[StrPath, list[str]], Iterable[str]] = None, - copy_function: Callable[[str, str], object] = ..., - ignore_dangling_symlinks: bool = False, - ) -> _PathReturn: ... +def copytree( + src: StrPath, + dst: StrPath, + symlinks: bool = False, + ignore: None | Callable[[str, list[str]], Iterable[str]] | Callable[[StrPath, list[str]], Iterable[str]] = None, + copy_function: Callable[[str, str], object] = ..., + ignore_dangling_symlinks: bool = False, + dirs_exist_ok: bool = False, +) -> _PathReturn: ... _OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, Any], object] _OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, Exception], object] @@ -161,16 +144,10 @@ def chown(path: FileDescriptorOrPath, user: None = None, *, group: str | int) -> def chown(path: FileDescriptorOrPath, user: None, group: str | int) -> None: ... @overload def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... - -if sys.version_info >= (3, 8): - @overload - def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... - @overload - def which(cmd: bytes, mode: int = 1, path: StrPath | None = None) -> bytes | None: ... - -else: - def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... - +@overload +def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... +@overload +def which(cmd: bytes, mode: int = 1, path: StrPath | None = None) -> bytes | None: ... def make_archive( base_name: str, format: str, @@ -192,15 +169,9 @@ def register_archive_format( name: str, function: Callable[[str, str], object], extra_args: None = None, description: str = "" ) -> None: ... def unregister_archive_format(name: str) -> None: ... - -if sys.version_info >= (3, 8): - def unpack_archive( - filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: _TarfileFilter | None = None - ) -> None: ... - -else: - def unpack_archive(filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None) -> None: ... - +def unpack_archive( + filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: _TarfileFilter | None = None +) -> None: ... @overload def register_unpack_format( name: str, diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 906a6dabe192..910424c01c31 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -3,8 +3,8 @@ from _typeshed import structseq from collections.abc import Callable, Iterable from enum import IntEnum from types import FrameType -from typing import Any -from typing_extensions import Final, Never, TypeAlias, final +from typing import Any, Final, final +from typing_extensions import Never, TypeAlias NSIG: int @@ -179,11 +179,9 @@ else: def sigtimedwait(sigset: Iterable[int], timeout: float) -> struct_siginfo | None: ... def sigwaitinfo(sigset: Iterable[int]) -> struct_siginfo: ... -if sys.version_info >= (3, 8): - def strsignal(__signalnum: _SIGNUM) -> str | None: ... - def valid_signals() -> set[Signals]: ... - def raise_signal(__signalnum: _SIGNUM) -> None: ... - +def strsignal(__signalnum: _SIGNUM) -> str | None: ... +def valid_signals() -> set[Signals]: ... +def raise_signal(__signalnum: _SIGNUM) -> None: ... def set_wakeup_fd(fd: int, *, warn_on_full_buffer: bool = ...) -> int: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index cc0cbe3709af..ce5e35228fe4 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -4,7 +4,7 @@ import _socket import sys from _socket import ( - _FD, + CAPI as CAPI, EAI_AGAIN as EAI_AGAIN, EAI_BADFLAGS as EAI_BADFLAGS, EAI_FAIL as EAI_FAIL, @@ -33,9 +33,28 @@ from _socket import ( IP_TTL as IP_TTL, IPPORT_RESERVED as IPPORT_RESERVED, IPPORT_USERRESERVED as IPPORT_USERRESERVED, + IPPROTO_AH as IPPROTO_AH, + IPPROTO_DSTOPTS as IPPROTO_DSTOPTS, + IPPROTO_EGP as IPPROTO_EGP, + IPPROTO_ESP as IPPROTO_ESP, + IPPROTO_FRAGMENT as IPPROTO_FRAGMENT, + IPPROTO_GGP as IPPROTO_GGP, + IPPROTO_HOPOPTS as IPPROTO_HOPOPTS, IPPROTO_ICMP as IPPROTO_ICMP, + IPPROTO_ICMPV6 as IPPROTO_ICMPV6, + IPPROTO_IDP as IPPROTO_IDP, + IPPROTO_IGMP as IPPROTO_IGMP, IPPROTO_IP as IPPROTO_IP, + IPPROTO_IPV4 as IPPROTO_IPV4, + IPPROTO_IPV6 as IPPROTO_IPV6, + IPPROTO_MAX as IPPROTO_MAX, + IPPROTO_ND as IPPROTO_ND, + IPPROTO_NONE as IPPROTO_NONE, + IPPROTO_PIM as IPPROTO_PIM, + IPPROTO_PUP as IPPROTO_PUP, IPPROTO_RAW as IPPROTO_RAW, + IPPROTO_ROUTING as IPPROTO_ROUTING, + IPPROTO_SCTP as IPPROTO_SCTP, IPPROTO_TCP as IPPROTO_TCP, IPPROTO_UDP as IPPROTO_UDP, IPV6_CHECKSUM as IPV6_CHECKSUM, @@ -82,11 +101,13 @@ from _socket import ( SOMAXCONN as SOMAXCONN, TCP_FASTOPEN as TCP_FASTOPEN, TCP_KEEPCNT as TCP_KEEPCNT, + TCP_KEEPINTVL as TCP_KEEPINTVL, TCP_MAXSEG as TCP_MAXSEG, TCP_NODELAY as TCP_NODELAY, SocketType as SocketType, _Address as _Address, _RetAddress as _RetAddress, + close as close, dup as dup, error as error, gaierror as gaierror, @@ -103,6 +124,9 @@ from _socket import ( herror as herror, htonl as htonl, htons as htons, + if_indextoname as if_indextoname, + if_nameindex as if_nameindex, + if_nametoindex as if_nametoindex, inet_aton as inet_aton, inet_ntoa as inet_ntoa, inet_ntop as inet_ntop, @@ -116,8 +140,20 @@ from _typeshed import ReadableBuffer, Unused, WriteableBuffer from collections.abc import Iterable from enum import IntEnum, IntFlag from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase, TextIOWrapper -from typing import Any, Protocol, overload -from typing_extensions import Literal, Self +from typing import Any, Literal, Protocol, SupportsIndex, overload +from typing_extensions import Self + +if sys.platform == "win32": + from _socket import ( + RCVALL_MAX as RCVALL_MAX, + RCVALL_OFF as RCVALL_OFF, + RCVALL_ON as RCVALL_ON, + RCVALL_SOCKETLEVELONLY as RCVALL_SOCKETLEVELONLY, + SIO_KEEPALIVE_VALS as SIO_KEEPALIVE_VALS, + SIO_LOOPBACK_FAST_PATH as SIO_LOOPBACK_FAST_PATH, + SIO_RCVALL as SIO_RCVALL, + SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE, + ) if sys.platform != "darwin" or sys.version_info >= (3, 9): from _socket import ( @@ -131,54 +167,27 @@ if sys.platform != "darwin" or sys.version_info >= (3, 9): if sys.platform == "darwin": from _socket import PF_SYSTEM as PF_SYSTEM, SYSPROTO_CONTROL as SYSPROTO_CONTROL -else: - from _socket import SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE - -if sys.version_info >= (3, 10): - from _socket import IP_RECVTOS as IP_RECVTOS -elif sys.platform != "darwin" and sys.platform != "win32": - from _socket import IP_RECVTOS as IP_RECVTOS - -from _socket import TCP_KEEPINTVL as TCP_KEEPINTVL, close as close if sys.platform != "darwin": - from _socket import TCP_KEEPIDLE as TCP_KEEPIDLE - -if sys.platform != "win32" or sys.version_info >= (3, 8): from _socket import ( - IPPROTO_AH as IPPROTO_AH, - IPPROTO_DSTOPTS as IPPROTO_DSTOPTS, - IPPROTO_EGP as IPPROTO_EGP, - IPPROTO_ESP as IPPROTO_ESP, - IPPROTO_FRAGMENT as IPPROTO_FRAGMENT, - IPPROTO_GGP as IPPROTO_GGP, - IPPROTO_HOPOPTS as IPPROTO_HOPOPTS, - IPPROTO_ICMPV6 as IPPROTO_ICMPV6, - IPPROTO_IDP as IPPROTO_IDP, - IPPROTO_IGMP as IPPROTO_IGMP, - IPPROTO_IPV4 as IPPROTO_IPV4, - IPPROTO_IPV6 as IPPROTO_IPV6, - IPPROTO_MAX as IPPROTO_MAX, - IPPROTO_ND as IPPROTO_ND, - IPPROTO_NONE as IPPROTO_NONE, - IPPROTO_PIM as IPPROTO_PIM, - IPPROTO_PUP as IPPROTO_PUP, - IPPROTO_ROUTING as IPPROTO_ROUTING, - IPPROTO_SCTP as IPPROTO_SCTP, + IPPROTO_CBT as IPPROTO_CBT, + IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, + IPPROTO_IGP as IPPROTO_IGP, + IPPROTO_L2TP as IPPROTO_L2TP, + IPPROTO_PGM as IPPROTO_PGM, + IPPROTO_RDP as IPPROTO_RDP, + IPPROTO_ST as IPPROTO_ST, + TCP_KEEPIDLE as TCP_KEEPIDLE, ) - if sys.platform != "darwin": - from _socket import ( - IPPROTO_CBT as IPPROTO_CBT, - IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, - IPPROTO_IGP as IPPROTO_IGP, - IPPROTO_L2TP as IPPROTO_L2TP, - IPPROTO_PGM as IPPROTO_PGM, - IPPROTO_RDP as IPPROTO_RDP, - IPPROTO_ST as IPPROTO_ST, - ) +if sys.version_info >= (3, 10): + from _socket import IP_RECVTOS as IP_RECVTOS +elif sys.platform != "win32" and sys.platform != "darwin": + from _socket import IP_RECVTOS as IP_RECVTOS + if sys.platform != "win32" and sys.platform != "darwin": from _socket import ( + IP_BIND_ADDRESS_NO_PORT as IP_BIND_ADDRESS_NO_PORT, IP_TRANSPARENT as IP_TRANSPARENT, IPPROTO_BIP as IPPROTO_BIP, IPPROTO_MOBILE as IPPROTO_MOBILE, @@ -186,10 +195,14 @@ if sys.platform != "win32" and sys.platform != "darwin": IPX_TYPE as IPX_TYPE, SCM_CREDENTIALS as SCM_CREDENTIALS, SO_BINDTODEVICE as SO_BINDTODEVICE, + SO_DOMAIN as SO_DOMAIN, SO_MARK as SO_MARK, SO_PASSCRED as SO_PASSCRED, + SO_PASSSEC as SO_PASSSEC, SO_PEERCRED as SO_PEERCRED, + SO_PEERSEC as SO_PEERSEC, SO_PRIORITY as SO_PRIORITY, + SO_PROTOCOL as SO_PROTOCOL, SO_SETFIB as SO_SETFIB, SOL_ATALK as SOL_ATALK, SOL_AX25 as SOL_AX25, @@ -197,6 +210,7 @@ if sys.platform != "win32" and sys.platform != "darwin": SOL_IPX as SOL_IPX, SOL_NETROM as SOL_NETROM, SOL_ROSE as SOL_ROSE, + TCP_CONGESTION as TCP_CONGESTION, TCP_CORK as TCP_CORK, TCP_DEFER_ACCEPT as TCP_DEFER_ACCEPT, TCP_INFO as TCP_INFO, @@ -206,6 +220,7 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_USER_TIMEOUT as TCP_USER_TIMEOUT, TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP, ) + if sys.platform != "win32": from _socket import ( CMSG_LEN as CMSG_LEN, @@ -235,6 +250,7 @@ if sys.platform != "win32": SCM_CREDS as SCM_CREDS, SCM_RIGHTS as SCM_RIGHTS, SO_REUSEPORT as SO_REUSEPORT, + TCP_NOTSENT_LOWAT as TCP_NOTSENT_LOWAT, sethostname as sethostname, ) @@ -252,9 +268,6 @@ if sys.platform != "win32": IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU, ) -if sys.platform != "win32" or sys.version_info >= (3, 8): - from _socket import if_indextoname as if_indextoname, if_nameindex as if_nameindex, if_nametoindex as if_nametoindex - if sys.platform != "darwin": if sys.platform != "win32" or sys.version_info >= (3, 9): from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM @@ -262,6 +275,9 @@ if sys.platform != "darwin": if sys.platform == "darwin" and sys.version_info >= (3, 10): from _socket import TCP_KEEPALIVE as TCP_KEEPALIVE +if sys.platform == "darwin" and sys.version_info >= (3, 11): + from _socket import TCP_CONNECTION_INFO as TCP_CONNECTION_INFO + if sys.platform == "linux": from _socket import ( ALG_OP_DECRYPT as ALG_OP_DECRYPT, @@ -275,15 +291,27 @@ if sys.platform == "linux": ALG_SET_OP as ALG_SET_OP, ALG_SET_PUBKEY as ALG_SET_PUBKEY, CAN_BCM as CAN_BCM, + CAN_BCM_CAN_FD_FRAME as CAN_BCM_CAN_FD_FRAME, + CAN_BCM_RX_ANNOUNCE_RESUME as CAN_BCM_RX_ANNOUNCE_RESUME, CAN_BCM_RX_CHANGED as CAN_BCM_RX_CHANGED, + CAN_BCM_RX_CHECK_DLC as CAN_BCM_RX_CHECK_DLC, CAN_BCM_RX_DELETE as CAN_BCM_RX_DELETE, + CAN_BCM_RX_FILTER_ID as CAN_BCM_RX_FILTER_ID, + CAN_BCM_RX_NO_AUTOTIMER as CAN_BCM_RX_NO_AUTOTIMER, CAN_BCM_RX_READ as CAN_BCM_RX_READ, + CAN_BCM_RX_RTR_FRAME as CAN_BCM_RX_RTR_FRAME, CAN_BCM_RX_SETUP as CAN_BCM_RX_SETUP, CAN_BCM_RX_STATUS as CAN_BCM_RX_STATUS, CAN_BCM_RX_TIMEOUT as CAN_BCM_RX_TIMEOUT, + CAN_BCM_SETTIMER as CAN_BCM_SETTIMER, + CAN_BCM_STARTTIMER as CAN_BCM_STARTTIMER, + CAN_BCM_TX_ANNOUNCE as CAN_BCM_TX_ANNOUNCE, + CAN_BCM_TX_COUNTEVT as CAN_BCM_TX_COUNTEVT, + CAN_BCM_TX_CP_CAN_ID as CAN_BCM_TX_CP_CAN_ID, CAN_BCM_TX_DELETE as CAN_BCM_TX_DELETE, CAN_BCM_TX_EXPIRED as CAN_BCM_TX_EXPIRED, CAN_BCM_TX_READ as CAN_BCM_TX_READ, + CAN_BCM_TX_RESET_MULTI_IDX as CAN_BCM_TX_RESET_MULTI_IDX, CAN_BCM_TX_SEND as CAN_BCM_TX_SEND, CAN_BCM_TX_SETUP as CAN_BCM_TX_SETUP, CAN_BCM_TX_STATUS as CAN_BCM_TX_STATUS, @@ -291,6 +319,7 @@ if sys.platform == "linux": CAN_EFF_MASK as CAN_EFF_MASK, CAN_ERR_FLAG as CAN_ERR_FLAG, CAN_ERR_MASK as CAN_ERR_MASK, + CAN_ISOTP as CAN_ISOTP, CAN_RAW as CAN_RAW, CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES as CAN_RAW_FD_FRAMES, @@ -299,6 +328,7 @@ if sys.platform == "linux": CAN_RAW_RECV_OWN_MSGS as CAN_RAW_RECV_OWN_MSGS, CAN_RTR_FLAG as CAN_RTR_FLAG, CAN_SFF_MASK as CAN_SFF_MASK, + IOCTL_VM_SOCKETS_GET_LOCAL_CID as IOCTL_VM_SOCKETS_GET_LOCAL_CID, NETLINK_ARPD as NETLINK_ARPD, NETLINK_CRYPTO as NETLINK_CRYPTO, NETLINK_DNRTMSG as NETLINK_DNRTMSG, @@ -341,6 +371,9 @@ if sys.platform == "linux": RDS_RDMA_SILENT as RDS_RDMA_SILENT, RDS_RDMA_USE_ONCE as RDS_RDMA_USE_ONCE, RDS_RECVERR as RDS_RECVERR, + SO_VM_SOCKETS_BUFFER_MAX_SIZE as SO_VM_SOCKETS_BUFFER_MAX_SIZE, + SO_VM_SOCKETS_BUFFER_MIN_SIZE as SO_VM_SOCKETS_BUFFER_MIN_SIZE, + SO_VM_SOCKETS_BUFFER_SIZE as SO_VM_SOCKETS_BUFFER_SIZE, SOL_ALG as SOL_ALG, SOL_CAN_BASE as SOL_CAN_BASE, SOL_CAN_RAW as SOL_CAN_RAW, @@ -369,36 +402,12 @@ if sys.platform == "linux": TIPC_WAIT_FOREVER as TIPC_WAIT_FOREVER, TIPC_WITHDRAWN as TIPC_WITHDRAWN, TIPC_ZONE_SCOPE as TIPC_ZONE_SCOPE, - ) -if sys.platform == "linux": - from _socket import ( - CAN_ISOTP as CAN_ISOTP, - IOCTL_VM_SOCKETS_GET_LOCAL_CID as IOCTL_VM_SOCKETS_GET_LOCAL_CID, - SO_VM_SOCKETS_BUFFER_MAX_SIZE as SO_VM_SOCKETS_BUFFER_MAX_SIZE, - SO_VM_SOCKETS_BUFFER_MIN_SIZE as SO_VM_SOCKETS_BUFFER_MIN_SIZE, - SO_VM_SOCKETS_BUFFER_SIZE as SO_VM_SOCKETS_BUFFER_SIZE, VM_SOCKETS_INVALID_VERSION as VM_SOCKETS_INVALID_VERSION, VMADDR_CID_ANY as VMADDR_CID_ANY, VMADDR_CID_HOST as VMADDR_CID_HOST, VMADDR_PORT_ANY as VMADDR_PORT_ANY, ) -if sys.platform != "win32": - from _socket import TCP_NOTSENT_LOWAT as TCP_NOTSENT_LOWAT -if sys.platform == "linux" and sys.version_info >= (3, 8): - from _socket import ( - CAN_BCM_CAN_FD_FRAME as CAN_BCM_CAN_FD_FRAME, - CAN_BCM_RX_ANNOUNCE_RESUME as CAN_BCM_RX_ANNOUNCE_RESUME, - CAN_BCM_RX_CHECK_DLC as CAN_BCM_RX_CHECK_DLC, - CAN_BCM_RX_FILTER_ID as CAN_BCM_RX_FILTER_ID, - CAN_BCM_RX_NO_AUTOTIMER as CAN_BCM_RX_NO_AUTOTIMER, - CAN_BCM_RX_RTR_FRAME as CAN_BCM_RX_RTR_FRAME, - CAN_BCM_SETTIMER as CAN_BCM_SETTIMER, - CAN_BCM_STARTTIMER as CAN_BCM_STARTTIMER, - CAN_BCM_TX_ANNOUNCE as CAN_BCM_TX_ANNOUNCE, - CAN_BCM_TX_COUNTEVT as CAN_BCM_TX_COUNTEVT, - CAN_BCM_TX_CP_CAN_ID as CAN_BCM_TX_CP_CAN_ID, - CAN_BCM_TX_RESET_MULTI_IDX as CAN_BCM_TX_RESET_MULTI_IDX, - ) + if sys.platform == "linux" and sys.version_info >= (3, 9): from _socket import ( CAN_J1939 as CAN_J1939, @@ -426,21 +435,14 @@ if sys.platform == "linux" and sys.version_info >= (3, 9): SO_J1939_FILTER as SO_J1939_FILTER, SO_J1939_PROMISC as SO_J1939_PROMISC, SO_J1939_SEND_PRIO as SO_J1939_SEND_PRIO, + UDPLITE_RECV_CSCOV as UDPLITE_RECV_CSCOV, + UDPLITE_SEND_CSCOV as UDPLITE_SEND_CSCOV, ) if sys.platform == "linux" and sys.version_info >= (3, 10): from _socket import IPPROTO_MPTCP as IPPROTO_MPTCP if sys.platform == "linux" and sys.version_info >= (3, 11): from _socket import SO_INCOMING_CPU as SO_INCOMING_CPU -if sys.platform == "win32": - from _socket import ( - RCVALL_MAX as RCVALL_MAX, - RCVALL_OFF as RCVALL_OFF, - RCVALL_ON as RCVALL_ON, - RCVALL_SOCKETLEVELONLY as RCVALL_SOCKETLEVELONLY, - SIO_KEEPALIVE_VALS as SIO_KEEPALIVE_VALS, - SIO_LOOPBACK_FAST_PATH as SIO_LOOPBACK_FAST_PATH, - SIO_RCVALL as SIO_RCVALL, - ) + if sys.version_info >= (3, 12): from _socket import ( IP_ADD_SOURCE_MEMBERSHIP as IP_ADD_SOURCE_MEMBERSHIP, @@ -471,8 +473,6 @@ if sys.version_info >= (3, 12): ETHERTYPE_IPV6 as ETHERTYPE_IPV6, ETHERTYPE_VLAN as ETHERTYPE_VLAN, ) -if sys.version_info >= (3, 11) and sys.platform == "darwin": - from _socket import TCP_CONNECTION_INFO as TCP_CONNECTION_INFO # Re-exported from errno EBADF: int @@ -493,7 +493,7 @@ class AddressFamily(IntEnum): AF_ROUTE: int AF_SYSTEM: int AF_UNIX: int - if sys.platform != "darwin" and sys.platform != "win32": + if sys.platform != "win32" and sys.platform != "darwin": AF_AAL5: int AF_ASH: int AF_ATMPVC: int @@ -518,8 +518,7 @@ class AddressFamily(IntEnum): AF_ALG: int AF_NETLINK: int AF_VSOCK: int - if sys.version_info >= (3, 8): - AF_QIPCRTR: int + AF_QIPCRTR: int if sys.platform != "win32" or sys.version_info >= (3, 9): AF_LINK: int if sys.platform != "darwin": @@ -569,8 +568,7 @@ if sys.platform == "linux": AF_ALG = AddressFamily.AF_ALG AF_NETLINK = AddressFamily.AF_NETLINK AF_VSOCK = AddressFamily.AF_VSOCK - if sys.version_info >= (3, 8): - AF_QIPCRTR = AddressFamily.AF_QIPCRTR + AF_QIPCRTR = AddressFamily.AF_QIPCRTR if sys.platform != "win32" or sys.version_info >= (3, 9): AF_LINK = AddressFamily.AF_LINK @@ -771,7 +769,7 @@ class socket(_socket.socket): def get_inheritable(self) -> bool: ... def set_inheritable(self, inheritable: bool) -> None: ... -def fromfd(fd: _FD, family: AddressFamily | int, type: SocketKind | int, proto: int = 0) -> socket: ... +def fromfd(fd: SupportsIndex, family: AddressFamily | int, type: SocketKind | int, proto: int = 0) -> socket: ... if sys.platform != "win32": if sys.version_info >= (3, 9): @@ -816,16 +814,10 @@ else: address: tuple[str | None, int], timeout: float | None = ..., source_address: _Address | None = None # noqa: F811 ) -> socket: ... -if sys.version_info >= (3, 8): - def has_dualstack_ipv6() -> bool: ... - def create_server( - address: _Address, - *, - family: int = ..., - backlog: int | None = None, - reuse_port: bool = False, - dualstack_ipv6: bool = False, - ) -> socket: ... +def has_dualstack_ipv6() -> bool: ... +def create_server( + address: _Address, *, family: int = ..., backlog: int | None = None, reuse_port: bool = False, dualstack_ipv6: bool = False +) -> socket: ... # the 5th tuple item is an address def getaddrinfo( diff --git a/mypy/typeshed/stdlib/spwd.pyi b/mypy/typeshed/stdlib/spwd.pyi index 27b1061e1b0e..93dfad3b38cc 100644 --- a/mypy/typeshed/stdlib/spwd.pyi +++ b/mypy/typeshed/stdlib/spwd.pyi @@ -1,7 +1,6 @@ import sys from _typeshed import structseq -from typing import Any -from typing_extensions import Final, final +from typing import Any, Final, final if sys.platform != "win32": @final diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 236e093c9909..659545c50b41 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -1,11 +1,11 @@ import sqlite3 import sys -from _typeshed import Incomplete, ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused +from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused from collections.abc import Callable, Generator, Iterable, Iterator, Mapping from datetime import date, datetime, time from types import TracebackType -from typing import Any, Protocol, TypeVar, overload -from typing_extensions import Literal, Self, SupportsIndex, TypeAlias, final +from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload +from typing_extensions import Self, TypeAlias _T = TypeVar("_T") _CursorT = TypeVar("_CursorT", bound=Cursor) @@ -245,12 +245,6 @@ else: def register_adapter(__type: type[_T], __caster: _Adapter[_T]) -> None: ... def register_converter(__name: str, __converter: _Converter) -> None: ... -if sys.version_info < (3, 8): - class Cache: - def __init__(self, *args: Incomplete, **kwargs: Unused) -> None: ... - def display(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... - def get(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... - class _AggregateProtocol(Protocol): def step(self, __value: int) -> object: ... def finalize(self) -> int: ... @@ -341,13 +335,9 @@ class Connection: ) -> None: ... def create_collation(self, __name: str, __callback: Callable[[str, str], int | SupportsIndex] | None) -> None: ... - if sys.version_info >= (3, 8): - def create_function( - self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False - ) -> None: ... - else: - def create_function(self, name: str, num_params: int, func: Callable[..., _SqliteData] | None) -> None: ... - + def create_function( + self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False + ) -> None: ... @overload def cursor(self, factory: None = None) -> Cursor: ... @overload @@ -458,15 +448,8 @@ class Row: def __lt__(self, __value: object) -> bool: ... def __ne__(self, __value: object) -> bool: ... -if sys.version_info >= (3, 8): - @final - class _Statement: ... - -else: - @final - class Statement: - def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... - _Statement: TypeAlias = Statement +@final +class _Statement: ... class Warning(Exception): ... diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi index 2c10bf7e7c3b..c242bd2a065f 100644 --- a/mypy/typeshed/stdlib/sre_parse.pyi +++ b/mypy/typeshed/stdlib/sre_parse.pyi @@ -25,7 +25,14 @@ if sys.version_info >= (3, 11): if sys.version_info < (3, 11): class Verbose(Exception): ... -class _State: +_OpSubpatternType: TypeAlias = tuple[int | None, int, int, SubPattern] +_OpGroupRefExistsType: TypeAlias = tuple[int, SubPattern, SubPattern] +_OpInType: TypeAlias = list[tuple[_NIC, int]] +_OpBranchType: TypeAlias = tuple[None, list[SubPattern]] +_AvType: TypeAlias = _OpInType | _OpBranchType | Iterable[SubPattern] | _OpGroupRefExistsType | _OpSubpatternType +_CodeType: TypeAlias = tuple[_NIC, _AvType] + +class State: flags: int groupdict: dict[str, int] groupwidths: list[int | None] @@ -37,29 +44,12 @@ class _State: def checkgroup(self, gid: int) -> bool: ... def checklookbehindgroup(self, gid: int, source: Tokenizer) -> None: ... -if sys.version_info >= (3, 8): - State: TypeAlias = _State -else: - Pattern: TypeAlias = _State - -_OpSubpatternType: TypeAlias = tuple[int | None, int, int, SubPattern] -_OpGroupRefExistsType: TypeAlias = tuple[int, SubPattern, SubPattern] -_OpInType: TypeAlias = list[tuple[_NIC, int]] -_OpBranchType: TypeAlias = tuple[None, list[SubPattern]] -_AvType: TypeAlias = _OpInType | _OpBranchType | Iterable[SubPattern] | _OpGroupRefExistsType | _OpSubpatternType -_CodeType: TypeAlias = tuple[_NIC, _AvType] - class SubPattern: data: list[_CodeType] width: int | None + state: State - if sys.version_info >= (3, 8): - state: State - def __init__(self, state: State, data: list[_CodeType] | None = None) -> None: ... - else: - pattern: Pattern - def __init__(self, pattern: Pattern, data: list[_CodeType] | None = None) -> None: ... - + def __init__(self, state: State, data: list[_CodeType] | None = None) -> None: ... def dump(self, level: int = 0) -> None: ... def __len__(self) -> int: ... def __delitem__(self, index: int | slice) -> None: ... @@ -79,11 +69,7 @@ class Tokenizer: def match(self, char: str) -> bool: ... def get(self) -> str | None: ... def getwhile(self, n: int, charset: Iterable[str]) -> str: ... - if sys.version_info >= (3, 8): - def getuntil(self, terminator: str, name: str) -> str: ... - else: - def getuntil(self, terminator: str) -> str: ... - + def getuntil(self, terminator: str, name: str) -> str: ... @property def pos(self) -> int: ... def tell(self) -> int: ... @@ -106,23 +92,13 @@ if sys.version_info >= (3, 12): @overload def parse_template(source: bytes, pattern: _Pattern[Any]) -> _TemplateByteType: ... -elif sys.version_info >= (3, 8): +else: @overload def parse_template(source: str, state: _Pattern[Any]) -> _TemplateType: ... @overload def parse_template(source: bytes, state: _Pattern[Any]) -> _TemplateByteType: ... -else: - @overload - def parse_template(source: str, pattern: _Pattern[Any]) -> _TemplateType: ... - @overload - def parse_template(source: bytes, pattern: _Pattern[Any]) -> _TemplateByteType: ... - -if sys.version_info >= (3, 8): - def parse(str: str, flags: int = 0, state: State | None = None) -> SubPattern: ... - -else: - def parse(str: str, flags: int = 0, pattern: Pattern | None = None) -> SubPattern: ... +def parse(str: str, flags: int = 0, state: State | None = None) -> SubPattern: ... if sys.version_info < (3, 12): def expand_template(template: _TemplateType, match: Match[Any]) -> str: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index d7f256d031ac..583ac82750ac 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -3,8 +3,8 @@ import socket import sys from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Callable, Iterable -from typing import Any, NamedTuple, overload -from typing_extensions import Literal, Never, Self, TypeAlias, TypedDict, final +from typing import Any, Literal, NamedTuple, TypedDict, final, overload +from typing_extensions import Never, Self, TypeAlias _PCTRTT: TypeAlias = tuple[tuple[str, str], ...] _PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] @@ -199,14 +199,11 @@ class Options(enum.IntFlag): OP_NO_COMPRESSION: int OP_NO_TICKET: int OP_NO_RENEGOTIATION: int - if sys.version_info >= (3, 8): - OP_ENABLE_MIDDLEBOX_COMPAT: int + OP_ENABLE_MIDDLEBOX_COMPAT: int if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: int OP_ENABLE_KTLS: int - if sys.version_info >= (3, 11): - OP_IGNORE_UNEXPECTED_EOF: int - elif sys.version_info >= (3, 8) and sys.platform == "linux": + if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: int OP_ALL: Options @@ -222,14 +219,11 @@ OP_SINGLE_ECDH_USE: Options OP_NO_COMPRESSION: Options OP_NO_TICKET: Options OP_NO_RENEGOTIATION: Options -if sys.version_info >= (3, 8): - OP_ENABLE_MIDDLEBOX_COMPAT: Options +OP_ENABLE_MIDDLEBOX_COMPAT: Options if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: Options OP_ENABLE_KTLS: Options -if sys.version_info >= (3, 11): - OP_IGNORE_UNEXPECTED_EOF: Options -elif sys.version_info >= (3, 8) and sys.platform == "linux": +if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: Options HAS_NEVER_CHECK_COMMON_NAME: bool @@ -365,8 +359,7 @@ class SSLSocket(socket.socket): def unwrap(self) -> socket.socket: ... def version(self) -> str | None: ... def pending(self) -> int: ... - if sys.version_info >= (3, 8): - def verify_client_post_handshake(self) -> None: ... + def verify_client_post_handshake(self) -> None: ... # These methods always raise `NotImplementedError`: def recvmsg(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] def recvmsg_into(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] @@ -397,9 +390,8 @@ class SSLContext: # so making these ClassVars wouldn't be appropriate sslobject_class: type[SSLObject] sslsocket_class: type[SSLSocket] - if sys.version_info >= (3, 8): - keylog_filename: str - post_handshake_auth: bool + keylog_filename: str + post_handshake_auth: bool if sys.version_info >= (3, 10): security_level: int if sys.version_info >= (3, 10): @@ -481,8 +473,7 @@ class SSLObject: def unwrap(self) -> None: ... def version(self) -> str | None: ... def get_channel_binding(self, cb_type: str = "tls-unique") -> bytes | None: ... - if sys.version_info >= (3, 8): - def verify_client_post_handshake(self) -> None: ... + def verify_client_post_handshake(self) -> None: ... @final class MemoryBIO: diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index 07174f4531b9..f3f013fc93e7 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -3,11 +3,15 @@ from _typeshed import SupportsRichComparisonT from collections.abc import Hashable, Iterable, Sequence from decimal import Decimal from fractions import Fraction -from typing import Any, NamedTuple, SupportsFloat, TypeVar -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Literal, NamedTuple, SupportsFloat, TypeVar +from typing_extensions import Self, TypeAlias __all__ = [ "StatisticsError", + "fmean", + "geometric_mean", + "mean", + "harmonic_mean", "pstdev", "pvariance", "stdev", @@ -16,14 +20,12 @@ __all__ = [ "median_low", "median_high", "median_grouped", - "mean", "mode", - "harmonic_mean", + "multimode", + "NormalDist", + "quantiles", ] -if sys.version_info >= (3, 8): - __all__ += ["geometric_mean", "multimode", "NormalDist", "fmean", "quantiles"] - if sys.version_info >= (3, 10): __all__ += ["covariance", "correlation", "linear_regression"] @@ -39,12 +41,10 @@ class StatisticsError(ValueError): ... if sys.version_info >= (3, 11): def fmean(data: Iterable[SupportsFloat], weights: Iterable[SupportsFloat] | None = None) -> float: ... -elif sys.version_info >= (3, 8): +else: def fmean(data: Iterable[SupportsFloat]) -> float: ... -if sys.version_info >= (3, 8): - def geometric_mean(data: Iterable[SupportsFloat]) -> float: ... - +def geometric_mean(data: Iterable[SupportsFloat]) -> float: ... def mean(data: Iterable[_NumberT]) -> _NumberT: ... if sys.version_info >= (3, 10): @@ -64,56 +64,49 @@ else: def median_grouped(data: Iterable[_NumberT], interval: _NumberT | float = 1) -> _NumberT | float: ... def mode(data: Iterable[_HashableT]) -> _HashableT: ... - -if sys.version_info >= (3, 8): - def multimode(data: Iterable[_HashableT]) -> list[_HashableT]: ... - +def multimode(data: Iterable[_HashableT]) -> list[_HashableT]: ... def pstdev(data: Iterable[_NumberT], mu: _NumberT | None = None) -> _NumberT: ... def pvariance(data: Iterable[_NumberT], mu: _NumberT | None = None) -> _NumberT: ... - -if sys.version_info >= (3, 8): - def quantiles( - data: Iterable[_NumberT], *, n: int = 4, method: Literal["inclusive", "exclusive"] = "exclusive" - ) -> list[_NumberT]: ... - +def quantiles( + data: Iterable[_NumberT], *, n: int = 4, method: Literal["inclusive", "exclusive"] = "exclusive" +) -> list[_NumberT]: ... def stdev(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: ... def variance(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: ... -if sys.version_info >= (3, 8): - class NormalDist: - def __init__(self, mu: float = 0.0, sigma: float = 1.0) -> None: ... - @property - def mean(self) -> float: ... - @property - def median(self) -> float: ... - @property - def mode(self) -> float: ... - @property - def stdev(self) -> float: ... - @property - def variance(self) -> float: ... - @classmethod - def from_samples(cls, data: Iterable[SupportsFloat]) -> Self: ... - def samples(self, n: int, *, seed: Any | None = None) -> list[float]: ... - def pdf(self, x: float) -> float: ... - def cdf(self, x: float) -> float: ... - def inv_cdf(self, p: float) -> float: ... - def overlap(self, other: NormalDist) -> float: ... - def quantiles(self, n: int = 4) -> list[float]: ... - if sys.version_info >= (3, 9): - def zscore(self, x: float) -> float: ... - - def __eq__(self, x2: object) -> bool: ... - def __add__(self, x2: float | NormalDist) -> NormalDist: ... - def __sub__(self, x2: float | NormalDist) -> NormalDist: ... - def __mul__(self, x2: float) -> NormalDist: ... - def __truediv__(self, x2: float) -> NormalDist: ... - def __pos__(self) -> NormalDist: ... - def __neg__(self) -> NormalDist: ... - __radd__ = __add__ - def __rsub__(self, x2: float | NormalDist) -> NormalDist: ... - __rmul__ = __mul__ - def __hash__(self) -> int: ... +class NormalDist: + def __init__(self, mu: float = 0.0, sigma: float = 1.0) -> None: ... + @property + def mean(self) -> float: ... + @property + def median(self) -> float: ... + @property + def mode(self) -> float: ... + @property + def stdev(self) -> float: ... + @property + def variance(self) -> float: ... + @classmethod + def from_samples(cls, data: Iterable[SupportsFloat]) -> Self: ... + def samples(self, n: int, *, seed: Any | None = None) -> list[float]: ... + def pdf(self, x: float) -> float: ... + def cdf(self, x: float) -> float: ... + def inv_cdf(self, p: float) -> float: ... + def overlap(self, other: NormalDist) -> float: ... + def quantiles(self, n: int = 4) -> list[float]: ... + if sys.version_info >= (3, 9): + def zscore(self, x: float) -> float: ... + + def __eq__(self, x2: object) -> bool: ... + def __add__(self, x2: float | NormalDist) -> NormalDist: ... + def __sub__(self, x2: float | NormalDist) -> NormalDist: ... + def __mul__(self, x2: float) -> NormalDist: ... + def __truediv__(self, x2: float) -> NormalDist: ... + def __pos__(self) -> NormalDist: ... + def __neg__(self) -> NormalDist: ... + __radd__ = __add__ + def __rsub__(self, x2: float | NormalDist) -> NormalDist: ... + __rmul__ = __mul__ + def __hash__(self) -> int: ... if sys.version_info >= (3, 12): def correlation( diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index b89623f05c99..df1db5c82eea 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Callable, Collection, Iterable, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Generic, TypeVar, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, AnyStr, Generic, Literal, TypeVar, overload +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -64,12 +64,7 @@ if sys.platform == "win32": # reveal_type(e.cmd) # Any, but morally is _CMD _FILE: TypeAlias = None | int | IO[Any] _InputString: TypeAlias = ReadableBuffer | str -if sys.version_info >= (3, 8): - _CMD: TypeAlias = StrOrBytesPath | Sequence[StrOrBytesPath] -else: - # Python 3.7 doesn't support _CMD being a single PathLike. - # See: https://bugs.python.org/issue31961 - _CMD: TypeAlias = str | bytes | Sequence[StrOrBytesPath] +_CMD: TypeAlias = StrOrBytesPath | Sequence[StrOrBytesPath] if sys.platform == "win32": _ENV: TypeAlias = Mapping[str, str] else: @@ -80,8 +75,7 @@ _T = TypeVar("_T") # These two are private but documented if sys.version_info >= (3, 11): _USE_VFORK: bool -if sys.version_info >= (3, 8): - _USE_POSIX_SPAWN: bool +_USE_POSIX_SPAWN: bool class CompletedProcess(Generic[_T]): # morally: _CMD @@ -2577,11 +2571,7 @@ else: def getstatusoutput(cmd: str | bytes) -> tuple[int, str]: ... def getoutput(cmd: str | bytes) -> str: ... -if sys.version_info >= (3, 8): - def list2cmdline(seq: Iterable[StrOrBytesPath]) -> str: ... # undocumented - -else: - def list2cmdline(seq: Iterable[str]) -> str: ... # undocumented +def list2cmdline(seq: Iterable[StrOrBytesPath]) -> str: ... # undocumented if sys.platform == "win32": class STARTUPINFO: diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi index b508a1ea8e20..9b051e82b64b 100644 --- a/mypy/typeshed/stdlib/sunau.pyi +++ b/mypy/typeshed/stdlib/sunau.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import Unused -from typing import IO, Any, NamedTuple, NoReturn, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Any, Literal, NamedTuple, NoReturn, overload +from typing_extensions import Self, TypeAlias _File: TypeAlias = str | IO[bytes] diff --git a/mypy/typeshed/stdlib/symbol.pyi b/mypy/typeshed/stdlib/symbol.pyi index bb6660374d16..48ae3567a1a5 100644 --- a/mypy/typeshed/stdlib/symbol.pyi +++ b/mypy/typeshed/stdlib/symbol.pyi @@ -1,5 +1,3 @@ -import sys - single_input: int file_input: int eval_input: int @@ -87,11 +85,9 @@ encoding_decl: int yield_expr: int yield_arg: int sync_comp_for: int -if sys.version_info >= (3, 8): - func_body_suite: int - func_type: int - func_type_input: int - namedexpr_test: int - typelist: int - +func_body_suite: int +func_type: int +func_type_input: int +namedexpr_test: int +typelist: int sym_name: dict[int, str] diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index 304ae8bf8126..0f080954ba2c 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -29,21 +29,16 @@ class Function(SymbolTable): def get_locals(self) -> tuple[str, ...]: ... def get_globals(self) -> tuple[str, ...]: ... def get_frees(self) -> tuple[str, ...]: ... - if sys.version_info >= (3, 8): - def get_nonlocals(self) -> tuple[str, ...]: ... + def get_nonlocals(self) -> tuple[str, ...]: ... class Class(SymbolTable): def get_methods(self) -> tuple[str, ...]: ... class Symbol: - if sys.version_info >= (3, 8): - def __init__( - self, name: str, flags: int, namespaces: Sequence[SymbolTable] | None = None, *, module_scope: bool = False - ) -> None: ... - def is_nonlocal(self) -> bool: ... - else: - def __init__(self, name: str, flags: int, namespaces: Sequence[SymbolTable] | None = None) -> None: ... - + def __init__( + self, name: str, flags: int, namespaces: Sequence[SymbolTable] | None = None, *, module_scope: bool = False + ) -> None: ... + def is_nonlocal(self) -> bool: ... def get_name(self) -> str: ... def is_referenced(self) -> bool: ... def is_parameter(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 1d4111af3a49..2f847498214b 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -1,13 +1,13 @@ import sys from _typeshed import OptExcInfo, ProfileFunction, TraceFunction, structseq from builtins import object as _object -from collections.abc import AsyncGenerator, Callable, Coroutine, Sequence +from collections.abc import AsyncGenerator, Callable, Sequence from importlib.abc import PathEntryFinder from importlib.machinery import ModuleSpec from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType -from typing import Any, NoReturn, Protocol, TextIO, TypeVar -from typing_extensions import Final, Literal, TypeAlias, final +from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final +from typing_extensions import TypeAlias _T = TypeVar("_T") @@ -55,8 +55,7 @@ platform: str if sys.version_info >= (3, 9): platlibdir: str prefix: str -if sys.version_info >= (3, 8): - pycache_prefix: str | None +pycache_prefix: str | None ps1: object ps2: object @@ -322,18 +321,19 @@ if sys.version_info < (3, 9): # An 11-tuple or None def callstats() -> tuple[int, int, int, int, int, int, int, int, int, int, int] | None: ... -if sys.version_info >= (3, 8): - # Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily. - class UnraisableHookArgs(Protocol): - exc_type: type[BaseException] - exc_value: BaseException | None - exc_traceback: TracebackType | None - err_msg: str | None - object: _object - unraisablehook: Callable[[UnraisableHookArgs], Any] - def __unraisablehook__(__unraisable: UnraisableHookArgs) -> Any: ... - def addaudithook(hook: Callable[[str, tuple[Any, ...]], Any]) -> None: ... - def audit(__event: str, *args: Any) -> None: ... +# Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily. +class UnraisableHookArgs(Protocol): + exc_type: type[BaseException] + exc_value: BaseException | None + exc_traceback: TracebackType | None + err_msg: str | None + object: _object + +unraisablehook: Callable[[UnraisableHookArgs], Any] + +def __unraisablehook__(__unraisable: UnraisableHookArgs) -> Any: ... +def addaudithook(hook: Callable[[str, tuple[Any, ...]], Any]) -> None: ... +def audit(__event: str, *args: Any) -> None: ... _AsyncgenHook: TypeAlias = Callable[[AsyncGenerator[Any, Any]], None] | None @@ -353,12 +353,7 @@ if sys.platform == "win32": def get_coroutine_origin_tracking_depth() -> int: ... def set_coroutine_origin_tracking_depth(depth: int) -> None: ... -if sys.version_info < (3, 8): - _CoroWrapper: TypeAlias = Callable[[Coroutine[Any, Any, Any]], Any] - def set_coroutine_wrapper(__wrapper: _CoroWrapper) -> None: ... - def get_coroutine_wrapper() -> _CoroWrapper: ... - -# The following two functions were added in 3.11.0, 3.10.7, 3.9.14, 3.8.14, & 3.7.14, +# The following two functions were added in 3.11.0, 3.10.7, 3.9.14, and 3.8.14, # as part of the response to CVE-2020-10735 def set_int_max_str_digits(maxdigits: int) -> None: ... def get_int_max_str_digits() -> int: ... diff --git a/mypy/typeshed/stdlib/sysconfig.pyi b/mypy/typeshed/stdlib/sysconfig.pyi index 7e29cf1326d6..2edb71d78cdd 100644 --- a/mypy/typeshed/stdlib/sysconfig.pyi +++ b/mypy/typeshed/stdlib/sysconfig.pyi @@ -1,6 +1,5 @@ import sys -from typing import IO, Any, overload -from typing_extensions import Literal +from typing import IO, Any, Literal, overload __all__ = [ "get_config_h_filename", diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index 0b769301a482..164334f60a6f 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -1,6 +1,5 @@ import sys -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload if sys.platform != "win32": LOG_ALERT: Literal[1] diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index d9d9641ac698..0bfd91ce2161 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -6,8 +6,8 @@ from builtins import list as _list # aliases to avoid name clashes with fields from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj from types import TracebackType -from typing import IO, ClassVar, Protocol, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, ClassVar, Literal, Protocol, overload +from typing_extensions import Self, TypeAlias __all__ = [ "TarFile", @@ -119,6 +119,7 @@ def open( debug: int | None = ..., errorlevel: int | None = ..., compresslevel: int | None = ..., + preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., ) -> TarFile: ... class ExFileObject(io.BufferedReader): @@ -291,32 +292,23 @@ class TarFile: def getnames(self) -> _list[str]: ... def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ... def next(self) -> TarInfo | None: ... - if sys.version_info >= (3, 8): - def extractall( - self, - path: StrOrBytesPath = ".", - members: Iterable[TarInfo] | None = None, - *, - numeric_owner: bool = False, - filter: _TarfileFilter | None = ..., - ) -> None: ... - def extract( - self, - member: str | TarInfo, - path: StrOrBytesPath = "", - set_attrs: bool = True, - *, - numeric_owner: bool = False, - filter: _TarfileFilter | None = ..., - ) -> None: ... - else: - def extractall( - self, path: StrOrBytesPath = ".", members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False - ) -> None: ... - def extract( - self, member: str | TarInfo, path: StrOrBytesPath = "", set_attrs: bool = True, *, numeric_owner: bool = False - ) -> None: ... - + def extractall( + self, + path: StrOrBytesPath = ".", + members: Iterable[TarInfo] | None = None, + *, + numeric_owner: bool = False, + filter: _TarfileFilter | None = ..., + ) -> None: ... + def extract( + self, + member: str | TarInfo, + path: StrOrBytesPath = "", + set_attrs: bool = True, + *, + numeric_owner: bool = False, + filter: _TarfileFilter | None = ..., + ) -> None: ... def _extract_member( self, tarinfo: TarInfo, targetpath: str, set_attrs: bool = True, numeric_owner: bool = False ) -> None: ... # undocumented @@ -350,9 +342,6 @@ if sys.version_info >= (3, 9): else: def is_tarfile(name: StrOrBytesPath) -> bool: ... -if sys.version_info < (3, 8): - def filemode(mode: int) -> str: ... # undocumented - class TarError(Exception): ... class ReadError(TarError): ... class CompressionError(TarError): ... @@ -360,30 +349,29 @@ class StreamError(TarError): ... class ExtractError(TarError): ... class HeaderError(TarError): ... -if sys.version_info >= (3, 8): - class FilterError(TarError): - # This attribute is only set directly on the subclasses, but the documentation guarantees - # that it is always present on FilterError. - tarinfo: TarInfo +class FilterError(TarError): + # This attribute is only set directly on the subclasses, but the documentation guarantees + # that it is always present on FilterError. + tarinfo: TarInfo - class AbsolutePathError(FilterError): - def __init__(self, tarinfo: TarInfo) -> None: ... +class AbsolutePathError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... - class OutsideDestinationError(FilterError): - def __init__(self, tarinfo: TarInfo, path: str) -> None: ... +class OutsideDestinationError(FilterError): + def __init__(self, tarinfo: TarInfo, path: str) -> None: ... - class SpecialFileError(FilterError): - def __init__(self, tarinfo: TarInfo) -> None: ... +class SpecialFileError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... - class AbsoluteLinkError(FilterError): - def __init__(self, tarinfo: TarInfo) -> None: ... +class AbsoluteLinkError(FilterError): + def __init__(self, tarinfo: TarInfo) -> None: ... - class LinkOutsideDestinationError(FilterError): - def __init__(self, tarinfo: TarInfo, path: str) -> None: ... +class LinkOutsideDestinationError(FilterError): + def __init__(self, tarinfo: TarInfo, path: str) -> None: ... - def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ... - def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ... - def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ... +def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ... +def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ... +def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ... class TarInfo: name: str @@ -414,27 +402,21 @@ class TarInfo: def linkpath(self) -> str: ... @linkpath.setter def linkpath(self, linkname: str) -> None: ... - if sys.version_info >= (3, 8): - def replace( - self, - *, - name: str = ..., - mtime: int = ..., - mode: int = ..., - linkname: str = ..., - uid: int = ..., - gid: int = ..., - uname: str = ..., - gname: str = ..., - deep: bool = True, - ) -> Self: ... - + def replace( + self, + *, + name: str = ..., + mtime: int = ..., + mode: int = ..., + linkname: str = ..., + uid: int = ..., + gid: int = ..., + uname: str = ..., + gname: str = ..., + deep: bool = True, + ) -> Self: ... def get_info(self) -> Mapping[str, str | int | bytes | Mapping[str, str]]: ... - if sys.version_info >= (3, 8): - def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... - else: - def tobuf(self, format: int | None = 1, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... - + def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... def create_ustar_header( self, info: Mapping[str, str | int | bytes | Mapping[str, str]], encoding: str, errors: str ) -> bytes: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 628f99410732..2c4b548458ea 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -14,8 +14,8 @@ from _typeshed import ( ) from collections.abc import Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Generic, overload -from typing_extensions import Literal, Self +from typing import IO, Any, AnyStr, Generic, Literal, overload +from typing_extensions import Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -85,7 +85,7 @@ if sys.version_info >= (3, 12): delete_on_close: bool = True, ) -> _TemporaryFileWrapper[Any]: ... -elif sys.version_info >= (3, 8): +else: @overload def NamedTemporaryFile( mode: OpenTextMode, @@ -126,9 +126,12 @@ elif sys.version_info >= (3, 8): errors: str | None = None, ) -> _TemporaryFileWrapper[Any]: ... +if sys.platform == "win32": + TemporaryFile = NamedTemporaryFile else: + # See the comments for builtins.open() for an explanation of the overloads. @overload - def NamedTemporaryFile( + def TemporaryFile( mode: OpenTextMode, buffering: int = -1, encoding: str | None = None, @@ -136,21 +139,70 @@ else: suffix: AnyStr | None = None, prefix: AnyStr | None = None, dir: GenericPath[AnyStr] | None = None, - delete: bool = True, - ) -> _TemporaryFileWrapper[str]: ... + *, + errors: str | None = None, + ) -> io.TextIOWrapper: ... @overload - def NamedTemporaryFile( - mode: OpenBinaryMode = "w+b", - buffering: int = -1, + def TemporaryFile( + mode: OpenBinaryMode, + buffering: Literal[0], encoding: str | None = None, newline: str | None = None, suffix: AnyStr | None = None, prefix: AnyStr | None = None, dir: GenericPath[AnyStr] | None = None, - delete: bool = True, - ) -> _TemporaryFileWrapper[bytes]: ... + *, + errors: str | None = None, + ) -> io.FileIO: ... @overload - def NamedTemporaryFile( + def TemporaryFile( + *, + buffering: Literal[0], + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + errors: str | None = None, + ) -> io.FileIO: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeWriting, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedWriter: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeReading, + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedReader: ... + @overload + def TemporaryFile( + mode: OpenBinaryModeUpdating = "w+b", + buffering: Literal[-1, 1] = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + *, + errors: str | None = None, + ) -> io.BufferedRandom: ... + @overload + def TemporaryFile( mode: str = "w+b", buffering: int = -1, encoding: str | None = None, @@ -158,168 +210,9 @@ else: suffix: AnyStr | None = None, prefix: AnyStr | None = None, dir: GenericPath[AnyStr] | None = None, - delete: bool = True, - ) -> _TemporaryFileWrapper[Any]: ... - -if sys.platform == "win32": - TemporaryFile = NamedTemporaryFile -else: - # See the comments for builtins.open() for an explanation of the overloads. - if sys.version_info >= (3, 8): - @overload - def TemporaryFile( - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> io.TextIOWrapper: ... - @overload - def TemporaryFile( - mode: OpenBinaryMode, - buffering: Literal[0], - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> io.FileIO: ... - @overload - def TemporaryFile( - *, - buffering: Literal[0], - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - errors: str | None = None, - ) -> io.FileIO: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeWriting, - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> io.BufferedWriter: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeReading, - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> io.BufferedReader: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeUpdating = "w+b", - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> io.BufferedRandom: ... - @overload - def TemporaryFile( - mode: str = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - *, - errors: str | None = None, - ) -> IO[Any]: ... - else: - @overload - def TemporaryFile( - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.TextIOWrapper: ... - @overload - def TemporaryFile( - mode: OpenBinaryMode, - buffering: Literal[0], - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.FileIO: ... - @overload - def TemporaryFile( - *, - buffering: Literal[0], - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.FileIO: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeUpdating = "w+b", - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.BufferedRandom: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeWriting, - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.BufferedWriter: ... - @overload - def TemporaryFile( - mode: OpenBinaryModeReading, - buffering: Literal[-1, 1] = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> io.BufferedReader: ... - @overload - def TemporaryFile( - mode: str = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: GenericPath[AnyStr] | None = None, - ) -> IO[Any]: ... + *, + errors: str | None = None, + ) -> IO[Any]: ... class _TemporaryFileWrapper(IO[AnyStr]): file: IO[AnyStr] # io.TextIOWrapper, io.BufferedReader or io.BufferedWriter @@ -386,143 +279,78 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): @property def newlines(self) -> str | tuple[str, ...] | None: ... # undocumented # bytes needs to go first, as default mode is to open as bytes - if sys.version_info >= (3, 8): - @overload - def __init__( - self: SpooledTemporaryFile[bytes], - max_size: int = 0, - mode: OpenBinaryMode = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - *, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self: SpooledTemporaryFile[str], - max_size: int, - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - *, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self: SpooledTemporaryFile[str], - max_size: int = 0, - *, - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self, - max_size: int, - mode: str, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - *, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self, - max_size: int = 0, - *, - mode: str, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - errors: str | None = None, - ) -> None: ... - @property - def errors(self) -> str | None: ... - else: - @overload - def __init__( - self: SpooledTemporaryFile[bytes], - max_size: int = 0, - mode: OpenBinaryMode = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - ) -> None: ... - @overload - def __init__( - self: SpooledTemporaryFile[str], - max_size: int, - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - ) -> None: ... - @overload - def __init__( - self: SpooledTemporaryFile[str], - max_size: int = 0, - *, - mode: OpenTextMode, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - ) -> None: ... - @overload - def __init__( - self, - max_size: int, - mode: str, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - ) -> None: ... - @overload - def __init__( - self, - max_size: int = 0, - *, - mode: str, - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - ) -> None: ... - + @overload + def __init__( + self: SpooledTemporaryFile[bytes], + max_size: int = 0, + mode: OpenBinaryMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: ... + @overload + def __init__( + self: SpooledTemporaryFile[str], + max_size: int, + mode: OpenTextMode, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: ... + @overload + def __init__( + self: SpooledTemporaryFile[str], + max_size: int = 0, + *, + mode: OpenTextMode, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + errors: str | None = None, + ) -> None: ... + @overload + def __init__( + self, + max_size: int, + mode: str, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: ... + @overload + def __init__( + self, + max_size: int = 0, + *, + mode: str, + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + errors: str | None = None, + ) -> None: ... + @property + def errors(self) -> str | None: ... def rollover(self) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, exc: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ... diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index bcd73823c63f..90b6cabb5237 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -1,10 +1,10 @@ import _thread import sys +from _thread import _excepthook, _ExceptHookArgs, get_native_id as get_native_id from _typeshed import ProfileFunction, TraceFunction from collections.abc import Callable, Iterable, Mapping from types import TracebackType -from typing import Any, TypeVar -from typing_extensions import final +from typing import Any, TypeVar, final _T = TypeVar("_T") @@ -26,15 +26,15 @@ __all__ = [ "BrokenBarrierError", "Timer", "ThreadError", + "ExceptHookArgs", "setprofile", "settrace", "local", "stack_size", + "excepthook", + "get_native_id", ] -if sys.version_info >= (3, 8): - __all__ += ["ExceptHookArgs", "excepthook", "get_native_id"] - if sys.version_info >= (3, 10): __all__ += ["getprofile", "gettrace"] @@ -50,10 +50,6 @@ def currentThread() -> Thread: ... # deprecated alias for current_thread() def get_ident() -> int: ... def enumerate() -> list[Thread]: ... def main_thread() -> Thread: ... - -if sys.version_info >= (3, 8): - from _thread import get_native_id as get_native_id - def settrace(func: TraceFunction) -> None: ... def setprofile(func: ProfileFunction | None) -> None: ... @@ -90,10 +86,8 @@ class Thread: def start(self) -> None: ... def run(self) -> None: ... def join(self, timeout: float | None = None) -> None: ... - if sys.version_info >= (3, 8): - @property - def native_id(self) -> int | None: ... # only available on some platforms - + @property + def native_id(self) -> int | None: ... # only available on some platforms def is_alive(self) -> bool: ... if sys.version_info < (3, 9): def isAlive(self) -> bool: ... @@ -159,11 +153,8 @@ class Event: def clear(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... -if sys.version_info >= (3, 8): - from _thread import _excepthook, _ExceptHookArgs - - excepthook = _excepthook - ExceptHookArgs = _ExceptHookArgs +excepthook = _excepthook +ExceptHookArgs = _ExceptHookArgs class Timer(Thread): args: Iterable[Any] # undocumented diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi index 035d78934f3a..28752bddc4dd 100644 --- a/mypy/typeshed/stdlib/time.pyi +++ b/mypy/typeshed/stdlib/time.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import structseq -from typing import Any, Protocol -from typing_extensions import Final, Literal, TypeAlias, final +from typing import Any, Final, Literal, Protocol, final +from typing_extensions import TypeAlias _TimeTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int] @@ -25,7 +25,7 @@ if sys.platform != "win32": if sys.platform != "linux" and sys.platform != "darwin": CLOCK_HIGHRES: int # Solaris only -if sys.version_info >= (3, 8) and sys.platform == "darwin": +if sys.platform == "darwin": CLOCK_UPTIME_RAW: int if sys.version_info >= (3, 9) and sys.platform == "linux": @@ -63,18 +63,14 @@ class struct_time(structseq[Any | int], _TimeTuple): @property def tm_gmtoff(self) -> int: ... -def asctime(t: _TimeTuple | struct_time = ...) -> str: ... - -if sys.version_info < (3, 8): - def clock() -> float: ... - -def ctime(secs: float | None = ...) -> str: ... -def gmtime(secs: float | None = ...) -> struct_time: ... -def localtime(secs: float | None = ...) -> struct_time: ... -def mktime(t: _TimeTuple | struct_time) -> float: ... -def sleep(secs: float) -> None: ... -def strftime(format: str, t: _TimeTuple | struct_time = ...) -> str: ... -def strptime(string: str, format: str = ...) -> struct_time: ... +def asctime(time_tuple: _TimeTuple | struct_time = ..., /) -> str: ... +def ctime(seconds: float | None = None, /) -> str: ... +def gmtime(seconds: float | None = None, /) -> struct_time: ... +def localtime(seconds: float | None = None, /) -> struct_time: ... +def mktime(time_tuple: _TimeTuple | struct_time, /) -> float: ... +def sleep(seconds: float, /) -> None: ... +def strftime(format: str, time_tuple: _TimeTuple | struct_time = ..., /) -> str: ... +def strptime(data_string: str, format: str = "%a %b %d %H:%M:%S %Y", /) -> struct_time: ... def time() -> float: ... if sys.platform != "win32": @@ -86,22 +82,22 @@ class _ClockInfo(Protocol): monotonic: bool resolution: float -def get_clock_info(name: Literal["monotonic", "perf_counter", "process_time", "time", "thread_time"]) -> _ClockInfo: ... +def get_clock_info(name: Literal["monotonic", "perf_counter", "process_time", "time", "thread_time"], /) -> _ClockInfo: ... def monotonic() -> float: ... def perf_counter() -> float: ... def process_time() -> float: ... if sys.platform != "win32": - def clock_getres(clk_id: int) -> float: ... # Unix only - def clock_gettime(clk_id: int) -> float: ... # Unix only - def clock_settime(clk_id: int, time: float) -> None: ... # Unix only + def clock_getres(clk_id: int, /) -> float: ... # Unix only + def clock_gettime(clk_id: int, /) -> float: ... # Unix only + def clock_settime(clk_id: int, time: float, /) -> None: ... # Unix only if sys.platform != "win32": - def clock_gettime_ns(clock_id: int) -> int: ... - def clock_settime_ns(clock_id: int, time: int) -> int: ... + def clock_gettime_ns(clock_id: int, /) -> int: ... + def clock_settime_ns(clock_id: int, time: int, /) -> int: ... if sys.platform == "linux": - def pthread_getcpuclockid(thread_id: int) -> int: ... + def pthread_getcpuclockid(thread_id: int, /) -> int: ... def monotonic_ns() -> int: ... def perf_counter_ns() -> int: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 5f7c3cb4527d..ff876d0bb88c 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -5,8 +5,8 @@ from collections.abc import Callable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, NamedTuple, TypeVar, overload, type_check_only -from typing_extensions import Literal, TypeAlias, TypedDict, TypeVarTuple, Unpack, deprecated +from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, overload, type_check_only +from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated if sys.version_info >= (3, 9): __all__ = [ @@ -1254,7 +1254,7 @@ class Canvas(Widget, XView, YView): offset: _ScreenUnits = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1283,7 +1283,7 @@ class Canvas(Widget, XView, YView): offset: _ScreenUnits = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1318,7 +1318,7 @@ class Canvas(Widget, XView, YView): offset: _ScreenUnits = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1350,7 +1350,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1380,7 +1380,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1416,7 +1416,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1451,7 +1451,7 @@ class Canvas(Widget, XView, YView): outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1484,7 +1484,7 @@ class Canvas(Widget, XView, YView): outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1523,7 +1523,7 @@ class Canvas(Widget, XView, YView): outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1555,7 +1555,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1585,7 +1585,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1621,7 +1621,7 @@ class Canvas(Widget, XView, YView): outline: str = ..., outlineoffset: _ScreenUnits = ..., outlinestipple: str = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., @@ -1642,7 +1642,7 @@ class Canvas(Widget, XView, YView): font: _FontDescription = ..., justify: Literal["left", "center", "right"] = ..., offset: _ScreenUnits = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., text: float | str = ..., @@ -1663,7 +1663,7 @@ class Canvas(Widget, XView, YView): font: _FontDescription = ..., justify: Literal["left", "center", "right"] = ..., offset: _ScreenUnits = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., text: float | str = ..., @@ -1677,7 +1677,7 @@ class Canvas(Widget, XView, YView): *, anchor: _Anchor = ..., height: _ScreenUnits = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., window: Widget = ..., @@ -1689,7 +1689,7 @@ class Canvas(Widget, XView, YView): *, anchor: _Anchor = ..., height: _ScreenUnits = ..., - state: Literal["normal", "active", "disabled"] = ..., + state: Literal["normal", "hidden", "disabled"] = ..., tags: str | list[str] | tuple[str, ...] = ..., width: _ScreenUnits = ..., window: Widget = ..., @@ -1712,9 +1712,7 @@ class Canvas(Widget, XView, YView): ) -> dict[str, tuple[str, str, str, str, str]] | None: ... itemconfig = itemconfigure def move(self, *args) -> None: ... - if sys.version_info >= (3, 8): - def moveto(self, tagOrId: str | int, x: Literal[""] | float = "", y: Literal[""] | float = "") -> None: ... - + def moveto(self, tagOrId: str | int, x: Literal[""] | float = "", y: Literal[""] | float = "") -> None: ... def postscript(self, cnf={}, **kw): ... # tkinter does: # lower = tag_lower @@ -3338,9 +3336,8 @@ class PhotoImage(Image, _PhotoImageLike): to: tuple[int, int] | None = None, ) -> None: ... def write(self, filename: StrOrBytesPath, format: str | None = None, from_coords: tuple[int, int] | None = None) -> None: ... - if sys.version_info >= (3, 8): - def transparency_get(self, x: int, y: int) -> bool: ... - def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... + def transparency_get(self, x: int, y: int) -> bool: ... + def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... class BitmapImage(Image, _BitmapImageLike): # This should be kept in sync with PIL.ImageTK.BitmapImage.__init__() @@ -3495,11 +3492,10 @@ class Spinbox(Widget, XView): def selection_adjust(self, index): ... def selection_clear(self): ... def selection_element(self, element: Incomplete | None = None): ... - if sys.version_info >= (3, 8): - def selection_from(self, index: int) -> None: ... - def selection_present(self) -> None: ... - def selection_range(self, start: int, end: int) -> None: ... - def selection_to(self, index: int) -> None: ... + def selection_from(self, index: int) -> None: ... + def selection_present(self) -> None: ... + def selection_range(self, start: int, end: int) -> None: ... + def selection_to(self, index: int) -> None: ... class LabelFrame(Widget): def __init__( diff --git a/mypy/typeshed/stdlib/tkinter/constants.pyi b/mypy/typeshed/stdlib/tkinter/constants.pyi index 1383b0f9bf4b..74fa72acb0bf 100644 --- a/mypy/typeshed/stdlib/tkinter/constants.pyi +++ b/mypy/typeshed/stdlib/tkinter/constants.pyi @@ -1,4 +1,4 @@ -from typing_extensions import Literal +from typing import Literal # These are not actually bools. See #4669 NO: bool diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi index 10b36e4d3c06..3d62f079178e 100644 --- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi @@ -2,8 +2,7 @@ import sys from _typeshed import Incomplete, StrOrBytesPath from collections.abc import Iterable from tkinter import Button, Entry, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog -from typing import IO, ClassVar -from typing_extensions import Literal +from typing import IO, ClassVar, Literal if sys.version_info >= (3, 9): __all__ = [ diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 9dffcd1ba0c6..448e2b0054a5 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -1,8 +1,8 @@ import _tkinter import sys import tkinter -from typing import Any, overload -from typing_extensions import Literal, TypeAlias, TypedDict +from typing import Any, Literal, TypedDict, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] @@ -15,8 +15,11 @@ ITALIC: Literal["italic"] _FontDescription: TypeAlias = ( str # "Helvetica 12" | Font # A font object constructed in Python - | list[Any] # ("Helvetica", 12, BOLD) - | tuple[Any, ...] + | list[Any] # ["Helvetica", 12, BOLD] + | tuple[str] # ("Liberation Sans",) needs wrapping in tuple/list to handle spaces + | tuple[str, int] # ("Liberation Sans", 12) + | tuple[str, int, str] # ("Liberation Sans", 12, "bold") + | tuple[str, int, list[str] | tuple[str, ...]] # e.g. bold and italic | _tkinter.Tcl_Obj # A font object constructed in Tcl ) diff --git a/mypy/typeshed/stdlib/tkinter/tix.pyi b/mypy/typeshed/stdlib/tkinter/tix.pyi index 672c5ab67403..73649de427e8 100644 --- a/mypy/typeshed/stdlib/tkinter/tix.pyi +++ b/mypy/typeshed/stdlib/tkinter/tix.pyi @@ -1,7 +1,6 @@ import tkinter from _typeshed import Incomplete -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal WINDOW: Literal["window"] TEXT: Literal["text"] diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index 2bbbafbcb945..ac5accb73d9f 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1,11 +1,10 @@ import _tkinter -import sys import tkinter from _typeshed import Incomplete from collections.abc import Callable from tkinter.font import _FontDescription -from typing import Any, overload -from typing_extensions import Literal, TypeAlias, TypedDict +from typing import Any, Literal, TypedDict, overload +from typing_extensions import TypeAlias __all__ = [ "Button", @@ -1102,11 +1101,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): def parent(self, item: str | int) -> str: ... def prev(self, item: str | int) -> str: ... # returning empty string means first item def see(self, item: str | int) -> None: ... - if sys.version_info >= (3, 8): - def selection(self) -> tuple[str, ...]: ... - else: - def selection(self, selop: Incomplete | None = ..., items: Incomplete | None = None) -> tuple[str, ...]: ... - + def selection(self) -> tuple[str, ...]: ... @overload def selection_set(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi index 85867a2b9744..f1fec7698043 100644 --- a/mypy/typeshed/stdlib/token.pyi +++ b/mypy/typeshed/stdlib/token.pyi @@ -3,11 +3,14 @@ import sys __all__ = [ "AMPER", "AMPEREQUAL", + "ASYNC", "AT", "ATEQUAL", + "AWAIT", "CIRCUMFLEX", "CIRCUMFLEXEQUAL", "COLON", + "COLONEQUAL", "COMMA", "DEDENT", "DOT", @@ -59,6 +62,8 @@ __all__ = [ "STAREQUAL", "STRING", "TILDE", + "TYPE_COMMENT", + "TYPE_IGNORE", "VBAR", "VBAREQUAL", "tok_name", @@ -67,9 +72,6 @@ __all__ = [ "COMMENT", ] -if sys.version_info >= (3, 8): - __all__ += ["ASYNC", "AWAIT", "COLONEQUAL", "TYPE_COMMENT", "TYPE_IGNORE"] - if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] @@ -129,9 +131,8 @@ AT: int RARROW: int ELLIPSIS: int ATEQUAL: int -if sys.version_info >= (3, 8): - AWAIT: int - ASYNC: int +AWAIT: int +ASYNC: int OP: int ERRORTOKEN: int N_TOKENS: int @@ -140,11 +141,10 @@ tok_name: dict[int, str] COMMENT: int NL: int ENCODING: int -if sys.version_info >= (3, 8): - TYPE_COMMENT: int - TYPE_IGNORE: int - COLONEQUAL: int - EXACT_TOKEN_TYPES: dict[str, int] +TYPE_COMMENT: int +TYPE_IGNORE: int +COLONEQUAL: int +EXACT_TOKEN_TYPES: dict[str, int] if sys.version_info >= (3, 10): SOFT_KEYWORD: int diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 0028ed034ae6..3cd9ab8f87ce 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -3,17 +3,21 @@ from _typeshed import FileDescriptorOrPath from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from token import * +from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES from typing import Any, NamedTuple, TextIO from typing_extensions import TypeAlias __all__ = [ "AMPER", "AMPEREQUAL", + "ASYNC", "AT", "ATEQUAL", + "AWAIT", "CIRCUMFLEX", "CIRCUMFLEXEQUAL", "COLON", + "COLONEQUAL", "COMMA", "COMMENT", "DEDENT", @@ -68,29 +72,24 @@ __all__ = [ "STAREQUAL", "STRING", "TILDE", + "TYPE_COMMENT", + "TYPE_IGNORE", "TokenInfo", "VBAR", "VBAREQUAL", "detect_encoding", + "generate_tokens", "tok_name", "tokenize", "untokenize", ] -if sys.version_info >= (3, 8): - __all__ += ["ASYNC", "AWAIT", "COLONEQUAL", "generate_tokens", "TYPE_COMMENT", "TYPE_IGNORE"] - if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] if sys.version_info >= (3, 12): __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] -if sys.version_info >= (3, 8): - from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES -else: - EXACT_TOKEN_TYPES: dict[str, int] - cookie_re: Pattern[str] blank_re: Pattern[bytes] diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 47449dfe8143..f6720155936f 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import SupportsWrite, Unused from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType -from typing import Any, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Literal, overload +from typing_extensions import Self, TypeAlias __all__ = [ "extract_stack", @@ -240,8 +240,7 @@ class FrameSummary(Iterable[Any]): def __getitem__(self, pos: int) -> Any: ... def __iter__(self) -> Iterator[Any]: ... def __eq__(self, other: object) -> bool: ... - if sys.version_info >= (3, 8): - def __len__(self) -> Literal[4]: ... + def __len__(self) -> Literal[4]: ... class StackSummary(list[FrameSummary]): @classmethod diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi index 6448a16ce11a..e721e414138b 100644 --- a/mypy/typeshed/stdlib/tracemalloc.pyi +++ b/mypy/typeshed/stdlib/tracemalloc.pyi @@ -1,8 +1,8 @@ import sys from _tracemalloc import * from collections.abc import Sequence -from typing import Any, overload -from typing_extensions import SupportsIndex, TypeAlias +from typing import Any, SupportsIndex, overload +from typing_extensions import TypeAlias def get_object_traceback(obj: object) -> Traceback | None: ... def take_snapshot() -> Snapshot: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index b26a668d273b..05ffc2143233 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -16,8 +16,8 @@ from collections.abc import ( from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping -from typing import Any, ClassVar, Mapping, Protocol, TypeVar, overload # noqa: Y022 -from typing_extensions import Literal, ParamSpec, Self, TypeVarTuple, final +from typing import Any, ClassVar, Literal, Mapping, Protocol, TypeVar, final, overload # noqa: Y022 +from typing_extensions import ParamSpec, Self, TypeVarTuple __all__ = [ "FunctionType", @@ -45,11 +45,9 @@ __all__ = [ "MethodWrapperType", "WrapperDescriptorType", "resolve_bases", + "CellType", ] -if sys.version_info >= (3, 8): - __all__ += ["CellType"] - if sys.version_info >= (3, 9): __all__ += ["GenericAlias"] @@ -68,9 +66,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) @final class _Cell: - if sys.version_info >= (3, 8): - def __new__(cls, __contents: object = ...) -> Self: ... - + def __new__(cls, __contents: object = ...) -> Self: ... def __eq__(self, __value: object) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] cell_contents: Any @@ -118,10 +114,8 @@ class CodeType: def __hash__(self) -> int: ... @property def co_argcount(self) -> int: ... - if sys.version_info >= (3, 8): - @property - def co_posonlyargcount(self) -> int: ... - + @property + def co_posonlyargcount(self) -> int: ... @property def co_kwonlyargcount(self) -> int: ... @property @@ -203,30 +197,11 @@ class CodeType: __freevars: tuple[str, ...] = ..., __cellvars: tuple[str, ...] = ..., ) -> Self: ... - elif sys.version_info >= (3, 8): - def __new__( - cls, - __argcount: int, - __posonlyargcount: int, - __kwonlyargcount: int, - __nlocals: int, - __stacksize: int, - __flags: int, - __codestring: bytes, - __constants: tuple[object, ...], - __names: tuple[str, ...], - __varnames: tuple[str, ...], - __filename: str, - __name: str, - __firstlineno: int, - __lnotab: bytes, - __freevars: tuple[str, ...] = ..., - __cellvars: tuple[str, ...] = ..., - ) -> Self: ... else: def __new__( cls, __argcount: int, + __posonlyargcount: int, __kwonlyargcount: int, __nlocals: int, __stacksize: int, @@ -286,7 +261,7 @@ class CodeType: co_name: str = ..., co_linetable: bytes = ..., ) -> CodeType: ... - elif sys.version_info >= (3, 8): + else: def replace( self, *, @@ -418,18 +393,6 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): @overload def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... -class _StaticFunctionType: - # Fictional type to correct the type of MethodType.__func__. - # FunctionType is a descriptor, so mypy follows the descriptor protocol and - # converts MethodType.__func__ back to MethodType (the return type of - # FunctionType.__get__). But this is actually a special case; MethodType is - # implemented in C and its attribute access doesn't go through - # __getattribute__. - # By wrapping FunctionType in _StaticFunctionType, we get the right result; - # similar to wrapping a function in staticmethod() at runtime to prevent it - # being bound as a method. - def __get__(self, obj: object, type: type | None) -> FunctionType: ... - @final class MethodType: @property @@ -437,7 +400,7 @@ class MethodType: @property def __defaults__(self) -> tuple[Any, ...] | None: ... # inherited from the added function @property - def __func__(self) -> _StaticFunctionType: ... + def __func__(self) -> Callable[..., Any]: ... @property def __self__(self) -> object: ... @property @@ -515,7 +478,7 @@ class ClassMethodDescriptorType: class TracebackType: def __new__(cls, tb_next: TracebackType | None, tb_frame: FrameType, tb_lasti: int, tb_lineno: int) -> Self: ... tb_next: TracebackType | None - # the rest are read-only even in 3.7 + # the rest are read-only @property def tb_frame(self) -> FrameType: ... @property @@ -598,8 +561,7 @@ def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Await @overload def coroutine(func: _Fn) -> _Fn: ... -if sys.version_info >= (3, 8): - CellType = _Cell +CellType = _Cell if sys.version_info >= (3, 9): class GenericAlias: @@ -626,7 +588,10 @@ if sys.version_info >= (3, 10): @final class NoneType: def __bool__(self) -> Literal[False]: ... - EllipsisType = ellipsis # noqa: F821 from builtins + + @final + class EllipsisType: ... + from builtins import _NotImplementedType NotImplementedType = _NotImplementedType diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 555df0ea47c8..9fef9d3922f5 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -1,4 +1,6 @@ -import collections # Needed by aliases like DefaultDict, see mypy issue 2986 +# TODO: The collections import is required, otherwise mypy crashes. +# https://github.com/python/mypy/issues/16744 +import collections # noqa: F401 # pyright: ignore import sys import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values @@ -18,7 +20,7 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing_extensions import Never as _Never, ParamSpec as _ParamSpec, final as _final +from typing_extensions import Never as _Never, ParamSpec as _ParamSpec if sys.version_info >= (3, 10): from types import UnionType @@ -46,6 +48,7 @@ __all__ = [ "DefaultDict", "Deque", "Dict", + "Final", "FrozenSet", "Generator", "Generic", @@ -55,6 +58,7 @@ __all__ = [ "Iterator", "KeysView", "List", + "Literal", "Mapping", "MappingView", "MutableMapping", @@ -63,6 +67,7 @@ __all__ = [ "NamedTuple", "NewType", "Optional", + "Protocol", "Reversible", "Sequence", "Set", @@ -71,38 +76,31 @@ __all__ = [ "SupportsBytes", "SupportsComplex", "SupportsFloat", + "SupportsIndex", "SupportsInt", "SupportsRound", "Text", "Tuple", "Type", "TypeVar", + "TypedDict", "Union", "ValuesView", "TYPE_CHECKING", "cast", + "final", + "get_args", + "get_origin", "get_type_hints", "no_type_check", "no_type_check_decorator", "overload", + "runtime_checkable", "ForwardRef", "NoReturn", "OrderedDict", ] -if sys.version_info >= (3, 8): - __all__ += [ - "Final", - "Literal", - "Protocol", - "SupportsIndex", - "TypedDict", - "final", - "get_args", - "get_origin", - "runtime_checkable", - ] - if sys.version_info >= (3, 9): __all__ += ["Annotated", "BinaryIO", "IO", "Match", "Pattern", "TextIO"] @@ -137,7 +135,8 @@ def type_check_only(func_or_cls: _F) -> _F: ... Any = object() -@_final +def final(f: _T) -> _T: ... +@final class TypeVar: @property def __name__(self) -> str: ... @@ -175,7 +174,7 @@ class TypeVar: _promote = object() # N.B. Keep this definition in sync with typing_extensions._SpecialForm -@_final +@final class _SpecialForm: def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): @@ -199,12 +198,11 @@ ClassVar: _SpecialForm Optional: _SpecialForm Tuple: _SpecialForm -if sys.version_info >= (3, 8): - Final: _SpecialForm - def final(f: _T) -> _T: ... - Literal: _SpecialForm - # TypedDict is a (non-subscriptable) special form. - TypedDict: object +Final: _SpecialForm + +Literal: _SpecialForm +# TypedDict is a (non-subscriptable) special form. +TypedDict: object if sys.version_info >= (3, 11): Self: _SpecialForm @@ -214,7 +212,7 @@ if sys.version_info >= (3, 11): NotRequired: _SpecialForm LiteralString: _SpecialForm - @_final + @final class TypeVarTuple: @property def __name__(self) -> str: ... @@ -224,21 +222,21 @@ if sys.version_info >= (3, 11): def __typing_prepare_subst__(self, alias: Incomplete, args: Incomplete) -> Incomplete: ... if sys.version_info >= (3, 10): - @_final + @final class ParamSpecArgs: @property def __origin__(self) -> ParamSpec: ... def __init__(self, origin: ParamSpec) -> None: ... def __eq__(self, other: object) -> bool: ... - @_final + @final class ParamSpecKwargs: @property def __origin__(self) -> ParamSpec: ... def __init__(self, origin: ParamSpec) -> None: ... def __eq__(self, other: object) -> bool: ... - @_final + @final class ParamSpec: @property def __name__(self) -> str: ... @@ -301,7 +299,7 @@ _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. -_TC = TypeVar("_TC", bound=Type[object]) +_TC = TypeVar("_TC", bound=type[object]) def no_type_check(arg: _F) -> _F: ... def no_type_check_decorator(decorator: Callable[_P, _T]) -> Callable[_P, _T]: ... @@ -329,8 +327,6 @@ if sys.version_info >= (3, 9): # Predefined type variables. AnyStr = TypeVar("AnyStr", str, bytes) # noqa: Y001 -# Technically in 3.7 this inherited from GenericMeta. But let's not reflect that, since -# type checkers tend to assume that Protocols all have the ABCMeta metaclass. class _ProtocolMeta(ABCMeta): if sys.version_info >= (3, 12): def __init__(cls, *args: Any, **kwargs: Any) -> None: ... @@ -358,11 +354,10 @@ class SupportsBytes(Protocol, metaclass=ABCMeta): @abstractmethod def __bytes__(self) -> bytes: ... -if sys.version_info >= (3, 8): - @runtime_checkable - class SupportsIndex(Protocol, metaclass=ABCMeta): - @abstractmethod - def __index__(self) -> int: ... +@runtime_checkable +class SupportsIndex(Protocol, metaclass=ABCMeta): + @abstractmethod + def __index__(self) -> int: ... @runtime_checkable class SupportsAbs(Protocol[_T_co]): @@ -418,7 +413,7 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return @overload @abstractmethod def throw( - self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None ) -> _YieldT_co: ... @overload @abstractmethod @@ -455,7 +450,7 @@ class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _Retu @overload @abstractmethod def throw( - self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None ) -> _YieldT_co: ... @overload @abstractmethod @@ -491,7 +486,7 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contr @overload @abstractmethod def athrow( - self, __typ: Type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None ) -> Awaitable[_YieldT_co]: ... @overload @abstractmethod @@ -602,9 +597,7 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, item: object) -> bool: ... def __iter__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... - if sys.version_info >= (3, 8): - def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... - + def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... def __or__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... @@ -618,9 +611,7 @@ class KeysView(MappingView, AbstractSet[_KT_co]): def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, key: object) -> bool: ... def __iter__(self) -> Iterator[_KT_co]: ... - if sys.version_info >= (3, 8): - def __reversed__(self) -> Iterator[_KT_co]: ... - + def __reversed__(self) -> Iterator[_KT_co]: ... def __or__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[_KT_co]: ... @@ -632,8 +623,7 @@ class ValuesView(MappingView, Collection[_VT_co]): def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ... # undocumented def __contains__(self, value: object) -> bool: ... def __iter__(self) -> Iterator[_VT_co]: ... - if sys.version_info >= (3, 8): - def __reversed__(self) -> Iterator[_VT_co]: ... + def __reversed__(self) -> Iterator[_VT_co]: ... class Mapping(Collection[_KT], Generic[_KT, _VT_co]): # TODO: We wish the key type could also be covariant, but that doesn't work, @@ -772,7 +762,7 @@ class IO(Iterator[AnyStr]): def __enter__(self) -> IO[AnyStr]: ... @abstractmethod def __exit__( - self, __type: Type[BaseException] | None, __value: BaseException | None, __traceback: TracebackType | None + self, __type: type[BaseException] | None, __value: BaseException | None, __traceback: TracebackType | None ) -> None: ... class BinaryIO(IO[bytes]): @@ -823,24 +813,25 @@ else: obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None ) -> dict[str, Any]: ... -if sys.version_info >= (3, 8): - def get_args(tp: Any) -> tuple[Any, ...]: ... +def get_args(tp: Any) -> tuple[Any, ...]: ... - if sys.version_info >= (3, 10): - @overload - def get_origin(tp: ParamSpecArgs | ParamSpecKwargs) -> ParamSpec: ... - @overload - def get_origin(tp: UnionType) -> type[UnionType]: ... - if sys.version_info >= (3, 9): - @overload - def get_origin(tp: GenericAlias) -> type: ... - @overload - def get_origin(tp: Any) -> Any | None: ... - else: - def get_origin(tp: Any) -> Any | None: ... +if sys.version_info >= (3, 10): + @overload + def get_origin(tp: ParamSpecArgs | ParamSpecKwargs) -> ParamSpec: ... + @overload + def get_origin(tp: UnionType) -> type[UnionType]: ... + +if sys.version_info >= (3, 9): + @overload + def get_origin(tp: GenericAlias) -> type: ... + @overload + def get_origin(tp: Any) -> Any | None: ... + +else: + def get_origin(tp: Any) -> Any | None: ... @overload -def cast(typ: Type[_T], val: Any) -> _T: ... +def cast(typ: type[_T], val: Any) -> _T: ... @overload def cast(typ: str, val: Any) -> Any: ... @overload @@ -865,9 +856,7 @@ if sys.version_info >= (3, 11): # Type constructors class NamedTuple(tuple[Any, ...]): - if sys.version_info < (3, 8): - _field_types: ClassVar[collections.OrderedDict[str, type]] - elif sys.version_info < (3, 9): + if sys.version_info < (3, 9): _field_types: ClassVar[dict[str, type]] _field_defaults: ClassVar[dict[str, Any]] _fields: ClassVar[tuple[str, ...]] @@ -881,11 +870,7 @@ class NamedTuple(tuple[Any, ...]): def __init__(self, __typename: str, __fields: None = None, **kwargs: Any) -> None: ... @classmethod def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ... - if sys.version_info >= (3, 8): - def _asdict(self) -> dict[str, Any]: ... - else: - def _asdict(self) -> collections.OrderedDict[str, Any]: ... - + def _asdict(self) -> dict[str, Any]: ... def _replace(self, **kwargs: Any) -> typing_extensions.Self: ... # Internal mypy fallback type for all typed dicts (does not exist at runtime) @@ -923,7 +908,7 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): # supposedly incompatible definitions of __or__ and __ior__ def __ior__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... # type: ignore[misc] -@_final +@final class ForwardRef: __forward_arg__: str __forward_code__: CodeType @@ -958,7 +943,7 @@ def _type_repr(obj: object) -> str: ... if sys.version_info >= (3, 12): def override(__method: _F) -> _F: ... - @_final + @final class TypeAliasType: def __init__( self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 5c5b756f5256..ea5c7b21aa87 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -1,5 +1,4 @@ import abc -import collections import sys import typing from _collections_abc import dict_items, dict_keys, dict_values @@ -353,9 +352,7 @@ else: ) -> IdentityFunction: ... class NamedTuple(tuple[Any, ...]): - if sys.version_info < (3, 8): - _field_types: ClassVar[collections.OrderedDict[str, type]] - elif sys.version_info < (3, 9): + if sys.version_info < (3, 9): _field_types: ClassVar[dict[str, type]] _field_defaults: ClassVar[dict[str, Any]] _fields: ClassVar[tuple[str, ...]] @@ -366,11 +363,7 @@ else: def __init__(self, typename: str, fields: None = None, **kwargs: Any) -> None: ... @classmethod def _make(cls, iterable: Iterable[Any]) -> Self: ... - if sys.version_info >= (3, 8): - def _asdict(self) -> dict[str, Any]: ... - else: - def _asdict(self) -> collections.OrderedDict[str, Any]: ... - + def _asdict(self) -> dict[str, Any]: ... def _replace(self, **kwargs: Any) -> Self: ... class NewType: diff --git a/mypy/typeshed/stdlib/unicodedata.pyi b/mypy/typeshed/stdlib/unicodedata.pyi index 5a1f7fe6638d..35ab6d609a19 100644 --- a/mypy/typeshed/stdlib/unicodedata.pyi +++ b/mypy/typeshed/stdlib/unicodedata.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadOnlyBuffer -from typing import Any, TypeVar, overload -from typing_extensions import Literal, TypeAlias, final +from typing import Any, Literal, TypeVar, final, overload +from typing_extensions import TypeAlias ucd_3_2_0: UCD unidata_version: str @@ -27,10 +27,7 @@ def digit(__chr: str, __default: _T) -> int | _T: ... _EastAsianWidth: TypeAlias = Literal["F", "H", "W", "Na", "A", "N"] def east_asian_width(__chr: str) -> _EastAsianWidth: ... - -if sys.version_info >= (3, 8): - def is_normalized(__form: str, __unistr: str) -> bool: ... - +def is_normalized(__form: str, __unistr: str) -> bool: ... def lookup(__name: str | ReadOnlyBuffer) -> str: ... def mirrored(__chr: str) -> int: ... @overload @@ -60,9 +57,7 @@ class UCD: @overload def digit(self, __chr: str, __default: _T) -> int | _T: ... def east_asian_width(self, __chr: str) -> _EastAsianWidth: ... - if sys.version_info >= (3, 8): - def is_normalized(self, __form: str, __unistr: str) -> bool: ... - + def is_normalized(self, __form: str, __unistr: str) -> bool: ... def lookup(self, __name: str | ReadOnlyBuffer) -> str: ... def mirrored(self, __chr: str) -> int: ... @overload diff --git a/mypy/typeshed/stdlib/unittest/__init__.pyi b/mypy/typeshed/stdlib/unittest/__init__.pyi index f96d6fb185c5..f2532ccf7fd8 100644 --- a/mypy/typeshed/stdlib/unittest/__init__.pyi +++ b/mypy/typeshed/stdlib/unittest/__init__.pyi @@ -1,9 +1,11 @@ import sys +from unittest.async_case import * from .case import ( FunctionTestCase as FunctionTestCase, SkipTest as SkipTest, TestCase as TestCase, + addModuleCleanup as addModuleCleanup, expectedFailure as expectedFailure, skip as skip, skipIf as skipIf, @@ -27,15 +29,11 @@ from .signals import ( ) from .suite import BaseTestSuite as BaseTestSuite, TestSuite as TestSuite -if sys.version_info >= (3, 8): - from unittest.async_case import * - - from .case import addModuleCleanup as addModuleCleanup - if sys.version_info >= (3, 11): from .case import doModuleCleanups as doModuleCleanups, enterModuleContext as enterModuleContext __all__ = [ + "IsolatedAsyncioTestCase", "TestResult", "TestCase", "TestSuite", @@ -57,11 +55,9 @@ __all__ = [ "getTestCaseNames", "makeSuite", "findTestCases", + "addModuleCleanup", ] -if sys.version_info >= (3, 8): - __all__ += ["addModuleCleanup", "IsolatedAsyncioTestCase"] - if sys.version_info >= (3, 11): __all__ += ["enterModuleContext", "doModuleCleanups"] diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index d66f027324f1..120bb96d761b 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -68,9 +68,8 @@ else: self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None ) -> bool | None: ... -if sys.version_info >= (3, 8): - def addModuleCleanup(__function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... - def doModuleCleanups() -> None: ... +def addModuleCleanup(__function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... +def doModuleCleanups() -> None: ... if sys.version_info >= (3, 11): def enterModuleContext(cm: AbstractContextManager[_T]) -> _T: ... @@ -274,20 +273,16 @@ class TestCase: def defaultTestResult(self) -> unittest.result.TestResult: ... def id(self) -> str: ... def shortDescription(self) -> str | None: ... - if sys.version_info >= (3, 8): - def addCleanup(self, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... - else: - def addCleanup(self, function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addCleanup(self, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): def enterContext(self, cm: AbstractContextManager[_T]) -> _T: ... def doCleanups(self) -> None: ... - if sys.version_info >= (3, 8): - @classmethod - def addClassCleanup(cls, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... - @classmethod - def doClassCleanups(cls) -> None: ... + @classmethod + def addClassCleanup(cls, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + @classmethod + def doClassCleanups(cls) -> None: ... if sys.version_info >= (3, 11): @classmethod diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 8e96b23ce959..c6014d4bb886 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -2,8 +2,8 @@ import sys from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, Sequence from contextlib import _GeneratorContextManager from types import TracebackType -from typing import Any, Generic, TypeVar, overload -from typing_extensions import Final, Literal, ParamSpec, Self, TypeAlias +from typing import Any, Final, Generic, Literal, TypeVar, overload +from typing_extensions import ParamSpec, Self, TypeAlias _T = TypeVar("_T") _TT = TypeVar("_TT", bound=type[Any]) @@ -12,41 +12,23 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _AF = TypeVar("_AF", bound=Callable[..., Coroutine[Any, Any, Any]]) _P = ParamSpec("_P") -if sys.version_info >= (3, 8): - __all__ = ( - "Mock", - "MagicMock", - "patch", - "sentinel", - "DEFAULT", - "ANY", - "call", - "create_autospec", - "AsyncMock", - "FILTER_DIR", - "NonCallableMock", - "NonCallableMagicMock", - "mock_open", - "PropertyMock", - "seal", - ) -else: - __all__ = ( - "Mock", - "MagicMock", - "patch", - "sentinel", - "DEFAULT", - "ANY", - "call", - "create_autospec", - "FILTER_DIR", - "NonCallableMock", - "NonCallableMagicMock", - "mock_open", - "PropertyMock", - "seal", - ) +__all__ = ( + "Mock", + "MagicMock", + "patch", + "sentinel", + "DEFAULT", + "ANY", + "call", + "create_autospec", + "AsyncMock", + "FILTER_DIR", + "NonCallableMock", + "NonCallableMagicMock", + "mock_open", + "PropertyMock", + "seal", +) if sys.version_info < (3, 9): __version__: Final[str] @@ -87,12 +69,10 @@ class _Call(tuple[Any, ...]): def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... def __getattr__(self, attr: str) -> Any: ... def __getattribute__(self, attr: str) -> Any: ... - if sys.version_info >= (3, 8): - @property - def args(self) -> tuple[Any, ...]: ... - @property - def kwargs(self) -> Mapping[str, Any]: ... - + @property + def args(self) -> tuple[Any, ...]: ... + @property + def kwargs(self) -> Mapping[str, Any]: ... def call_list(self) -> Any: ... call: _Call @@ -144,24 +124,13 @@ class NonCallableMock(Base, Any): def __delattr__(self, name: str) -> None: ... def __setattr__(self, name: str, value: Any) -> None: ... def __dir__(self) -> list[str]: ... - if sys.version_info >= (3, 8): - def _calls_repr(self, prefix: str = "Calls") -> str: ... - def assert_called_with(self, *args: Any, **kwargs: Any) -> None: ... - def assert_not_called(self) -> None: ... - def assert_called_once_with(self, *args: Any, **kwargs: Any) -> None: ... - def _format_mock_failure_message(self, args: Any, kwargs: Any, action: str = "call") -> str: ... - else: - def assert_called_with(_mock_self, *args: Any, **kwargs: Any) -> None: ... - def assert_not_called(_mock_self) -> None: ... - def assert_called_once_with(_mock_self, *args: Any, **kwargs: Any) -> None: ... - def _format_mock_failure_message(self, args: Any, kwargs: Any) -> str: ... - if sys.version_info >= (3, 8): - def assert_called(self) -> None: ... - def assert_called_once(self) -> None: ... - else: - def assert_called(_mock_self) -> None: ... - def assert_called_once(_mock_self) -> None: ... - + def _calls_repr(self, prefix: str = "Calls") -> str: ... + def assert_called_with(self, *args: Any, **kwargs: Any) -> None: ... + def assert_not_called(self) -> None: ... + def assert_called_once_with(self, *args: Any, **kwargs: Any) -> None: ... + def _format_mock_failure_message(self, args: Any, kwargs: Any, action: str = "call") -> str: ... + def assert_called(self) -> None: ... + def assert_called_once(self) -> None: ... def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... def _extract_mock_name(self) -> str: ... def _get_call_signature_from_name(self, name: str) -> Any: ... @@ -198,10 +167,7 @@ class CallableMixin(Base): _new_parent: Any | None = None, **kwargs: Any, ) -> None: ... - if sys.version_info >= (3, 8): - def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - else: - def __call__(_mock_self, *args: Any, **kwargs: Any) -> Any: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... class Mock(CallableMixin, NonCallableMock): ... @@ -256,16 +222,12 @@ class _patch(Generic[_T]): # arguments. See the _patch_default_new class below for this functionality. @overload def __call__(self, func: Callable[_P, _R]) -> Callable[_P, _R]: ... - if sys.version_info >= (3, 8): - def decoration_helper( - self, patched: _patch[Any], args: Sequence[Any], keywargs: Any - ) -> _GeneratorContextManager[tuple[Sequence[Any], Any]]: ... - + def decoration_helper( + self, patched: _patch[Any], args: Sequence[Any], keywargs: Any + ) -> _GeneratorContextManager[tuple[Sequence[Any], Any]]: ... def decorate_class(self, klass: _TT) -> _TT: ... def decorate_callable(self, func: Callable[..., _R]) -> Callable[..., _R]: ... - if sys.version_info >= (3, 8): - def decorate_async_callable(self, func: Callable[..., Awaitable[_R]]) -> Callable[..., Awaitable[_R]]: ... - + def decorate_async_callable(self, func: Callable[..., Awaitable[_R]]) -> Callable[..., Awaitable[_R]]: ... def get_original(self) -> tuple[Any, bool]: ... target: Any temp_original: Any @@ -277,15 +239,10 @@ class _patch(Generic[_T]): def start(self) -> _T: ... def stop(self) -> None: ... -if sys.version_info >= (3, 8): - _Mock: TypeAlias = MagicMock | AsyncMock -else: - _Mock: TypeAlias = MagicMock - # This class does not exist at runtime, it's a hack to make this work: # @patch("foo") # def bar(..., mock: MagicMock) -> None: ... -class _patch_default_new(_patch[_Mock]): +class _patch_default_new(_patch[MagicMock | AsyncMock]): @overload def __call__(self, func: _TT) -> _TT: ... # Can't use the following as ParamSpec is only allowed as last parameter: @@ -366,7 +323,7 @@ class _patcher: autospec: Any | None = ..., new_callable: Any | None = ..., **kwargs: Any, - ) -> _patch[_Mock]: ... + ) -> _patch[MagicMock | AsyncMock]: ... @staticmethod def multiple( target: Any, @@ -388,38 +345,34 @@ class MagicMixin: class NonCallableMagicMock(MagicMixin, NonCallableMock): ... class MagicMock(MagicMixin, Mock): ... -if sys.version_info >= (3, 8): - class AsyncMockMixin(Base): - def __init__(self, *args: Any, **kwargs: Any) -> None: ... - async def _execute_mock_call(self, *args: Any, **kwargs: Any) -> Any: ... - def assert_awaited(self) -> None: ... - def assert_awaited_once(self) -> None: ... - def assert_awaited_with(self, *args: Any, **kwargs: Any) -> None: ... - def assert_awaited_once_with(self, *args: Any, **kwargs: Any) -> None: ... - def assert_any_await(self, *args: Any, **kwargs: Any) -> None: ... - def assert_has_awaits(self, calls: Iterable[_Call], any_order: bool = False) -> None: ... - def assert_not_awaited(self) -> None: ... - def reset_mock(self, *args: Any, **kwargs: Any) -> None: ... - await_count: int - await_args: _Call | None - await_args_list: _CallList - - class AsyncMagicMixin(MagicMixin): - def __init__(self, *args: Any, **kw: Any) -> None: ... - - class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): - # Improving the `reset_mock` signature. - # It is defined on `AsyncMockMixin` with `*args, **kwargs`, which is not ideal. - # But, `NonCallableMock` super-class has the better version. - def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... +class AsyncMockMixin(Base): + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + async def _execute_mock_call(self, *args: Any, **kwargs: Any) -> Any: ... + def assert_awaited(self) -> None: ... + def assert_awaited_once(self) -> None: ... + def assert_awaited_with(self, *args: Any, **kwargs: Any) -> None: ... + def assert_awaited_once_with(self, *args: Any, **kwargs: Any) -> None: ... + def assert_any_await(self, *args: Any, **kwargs: Any) -> None: ... + def assert_has_awaits(self, calls: Iterable[_Call], any_order: bool = False) -> None: ... + def assert_not_awaited(self) -> None: ... + def reset_mock(self, *args: Any, **kwargs: Any) -> None: ... + await_count: int + await_args: _Call | None + await_args_list: _CallList + +class AsyncMagicMixin(MagicMixin): + def __init__(self, *args: Any, **kw: Any) -> None: ... + +class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): + # Improving the `reset_mock` signature. + # It is defined on `AsyncMockMixin` with `*args, **kwargs`, which is not ideal. + # But, `NonCallableMock` super-class has the better version. + def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... class MagicProxy: name: str parent: Any def __init__(self, name: str, parent: Any) -> None: ... - if sys.version_info < (3, 8): - def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def create_mock(self) -> Any: ... def __get__(self, obj: Any, _type: Any | None = None) -> Any: ... @@ -471,11 +424,7 @@ class _SpecState: def mock_open(mock: Any | None = None, read_data: Any = "") -> Any: ... class PropertyMock(Mock): - if sys.version_info >= (3, 8): - def __get__(self, obj: _T, obj_type: type[_T] | None = None) -> Self: ... - else: - def __get__(self, obj: _T, obj_type: type[_T] | None) -> Self: ... - + def __get__(self, obj: _T, obj_type: type[_T] | None = None) -> Self: ... def __set__(self, obj: Any, val: Any) -> None: ... def seal(mock: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index 116754091d1a..ed1929b26501 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import Any, AnyStr, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/urllib/robotparser.pyi b/mypy/typeshed/stdlib/urllib/robotparser.pyi index d218c3dc6c0f..14ceef550dab 100644 --- a/mypy/typeshed/stdlib/urllib/robotparser.pyi +++ b/mypy/typeshed/stdlib/urllib/robotparser.pyi @@ -1,4 +1,3 @@ -import sys from collections.abc import Iterable from typing import NamedTuple @@ -18,5 +17,4 @@ class RobotFileParser: def modified(self) -> None: ... def crawl_delay(self, useragent: str) -> str | None: ... def request_rate(self, useragent: str) -> RequestRate | None: ... - if sys.version_info >= (3, 8): - def site_maps(self) -> list[str] | None: ... + def site_maps(self) -> list[str] | None: ... diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index 6222eb65918a..12afea9337e7 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -2,8 +2,8 @@ import sys from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence from types import ModuleType, TracebackType -from typing import Any, Generic, TextIO, TypeVar, overload -from typing_extensions import Literal, TypeAlias +from typing import Any, Generic, Literal, TextIO, TypeVar, overload +from typing_extensions import TypeAlias __all__ = [ "warn", diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index 6b7af2f79da1..9137f1e47643 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadableBuffer, Unused -from typing import IO, Any, BinaryIO, NamedTuple, NoReturn, overload -from typing_extensions import Literal, Self, TypeAlias, deprecated +from typing import IO, Any, BinaryIO, Literal, NamedTuple, NoReturn, overload +from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 9): __all__ = ["open", "Error", "Wave_read", "Wave_write"] diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index 99c7bb5846b6..2b3f978c814b 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from collections.abc import Callable, Sequence -from typing_extensions import Literal +from typing import Literal __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index 613b239ff663..897177547c71 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -1,7 +1,7 @@ import sys from types import TracebackType -from typing import Any -from typing_extensions import Literal, Self, TypeAlias, final +from typing import Any, Literal, final +from typing_extensions import Self, TypeAlias if sys.platform == "win32": _KeyType: TypeAlias = HKEYType | int diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi index aa04fdc27a01..bacc5302826f 100644 --- a/mypy/typeshed/stdlib/winsound.pyi +++ b/mypy/typeshed/stdlib/winsound.pyi @@ -1,7 +1,6 @@ import sys from _typeshed import ReadableBuffer -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload if sys.platform == "win32": SND_APPLICATION: Literal[128] diff --git a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi index 4d83bef025d9..162f60254a58 100644 --- a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi @@ -1,6 +1,5 @@ from collections.abc import Iterable -from typing import Any, TypeVar -from typing_extensions import Literal +from typing import Any, Literal, TypeVar __all__ = ["NodeList", "EmptyNodeList", "StringTypes", "defproperty"] diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index ec17f0a41497..28bfa1b36924 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -1,8 +1,8 @@ import sys import xml.dom from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite -from typing import NoReturn, TypeVar, overload -from typing_extensions import Literal, Self +from typing import Literal, NoReturn, TypeVar, overload +from typing_extensions import Self from xml.dom.minicompat import NodeList from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS from xml.sax.xmlreader import XMLReader diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index 920905160e43..95436ab5dd38 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Incomplete, SupportsRead from collections.abc import Sequence -from typing_extensions import Literal, TypeAlias +from typing import Literal +from typing_extensions import TypeAlias from xml.dom.minidom import Document, DOMImplementation, Element, Text from xml.sax.handler import ContentHandler from xml.sax.xmlreader import XMLReader diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index c07e4ba2465e..480dd7ce732c 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete, Unused -from typing import Any, NoReturn -from typing_extensions import Literal, TypeAlias +from typing import Any, Literal, NoReturn +from typing_extensions import TypeAlias from urllib.request import OpenerDirector from xml.dom.expatbuilder import ExpatBuilder, ExpatBuilderNS from xml.dom.minidom import Node diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index b08ca88e7e97..c508f72892c1 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -2,14 +2,16 @@ import sys from _collections_abc import dict_keys from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence -from typing import Any, TypeVar, overload -from typing_extensions import Literal, SupportsIndex, TypeAlias, TypeGuard +from typing import Any, Literal, SupportsIndex, TypeVar, overload +from typing_extensions import TypeAlias, TypeGuard __all__ = [ + "C14NWriterTarget", "Comment", "dump", "Element", "ElementTree", + "canonicalize", "fromstring", "fromstringlist", "iselement", @@ -31,9 +33,6 @@ __all__ = [ "register_namespace", ] -if sys.version_info >= (3, 8): - __all__ += ["C14NWriterTarget", "canonicalize"] - if sys.version_info >= (3, 9): __all__ += ["indent"] @@ -50,36 +49,34 @@ class ParseError(SyntaxError): # In reality it works based on `.tag` attribute duck typing. def iselement(element: object) -> TypeGuard[Element]: ... - -if sys.version_info >= (3, 8): - @overload - def canonicalize( - xml_data: str | ReadableBuffer | None = None, - *, - out: None = None, - from_file: _FileRead | None = None, - with_comments: bool = False, - strip_text: bool = False, - rewrite_prefixes: bool = False, - qname_aware_tags: Iterable[str] | None = None, - qname_aware_attrs: Iterable[str] | None = None, - exclude_attrs: Iterable[str] | None = None, - exclude_tags: Iterable[str] | None = None, - ) -> str: ... - @overload - def canonicalize( - xml_data: str | ReadableBuffer | None = None, - *, - out: SupportsWrite[str], - from_file: _FileRead | None = None, - with_comments: bool = False, - strip_text: bool = False, - rewrite_prefixes: bool = False, - qname_aware_tags: Iterable[str] | None = None, - qname_aware_attrs: Iterable[str] | None = None, - exclude_attrs: Iterable[str] | None = None, - exclude_tags: Iterable[str] | None = None, - ) -> None: ... +@overload +def canonicalize( + xml_data: str | ReadableBuffer | None = None, + *, + out: None = None, + from_file: _FileRead | None = None, + with_comments: bool = False, + strip_text: bool = False, + rewrite_prefixes: bool = False, + qname_aware_tags: Iterable[str] | None = None, + qname_aware_attrs: Iterable[str] | None = None, + exclude_attrs: Iterable[str] | None = None, + exclude_tags: Iterable[str] | None = None, +) -> str: ... +@overload +def canonicalize( + xml_data: str | ReadableBuffer | None = None, + *, + out: SupportsWrite[str], + from_file: _FileRead | None = None, + with_comments: bool = False, + strip_text: bool = False, + rewrite_prefixes: bool = False, + qname_aware_tags: Iterable[str] | None = None, + qname_aware_attrs: Iterable[str] | None = None, + exclude_attrs: Iterable[str] | None = None, + exclude_tags: Iterable[str] | None = None, +) -> None: ... class Element: tag: str @@ -172,93 +169,66 @@ class ElementTree: def write_c14n(self, file: _FileWriteC14N) -> None: ... def register_namespace(prefix: str, uri: str) -> None: ... - -if sys.version_info >= (3, 8): - @overload - def tostring( - element: Element, - encoding: None = None, - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> bytes: ... - @overload - def tostring( - element: Element, - encoding: Literal["unicode"], - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> str: ... - @overload - def tostring( - element: Element, - encoding: str, - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> Any: ... - @overload - def tostringlist( - element: Element, - encoding: None = None, - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> list[bytes]: ... - @overload - def tostringlist( - element: Element, - encoding: Literal["unicode"], - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> list[str]: ... - @overload - def tostringlist( - element: Element, - encoding: str, - method: str | None = None, - *, - xml_declaration: bool | None = None, - default_namespace: str | None = None, - short_empty_elements: bool = True, - ) -> list[Any]: ... - -else: - @overload - def tostring( - element: Element, encoding: None = None, method: str | None = None, *, short_empty_elements: bool = True - ) -> bytes: ... - @overload - def tostring( - element: Element, encoding: Literal["unicode"], method: str | None = None, *, short_empty_elements: bool = True - ) -> str: ... - @overload - def tostring(element: Element, encoding: str, method: str | None = None, *, short_empty_elements: bool = True) -> Any: ... - @overload - def tostringlist( - element: Element, encoding: None = None, method: str | None = None, *, short_empty_elements: bool = True - ) -> list[bytes]: ... - @overload - def tostringlist( - element: Element, encoding: Literal["unicode"], method: str | None = None, *, short_empty_elements: bool = True - ) -> list[str]: ... - @overload - def tostringlist( - element: Element, encoding: str, method: str | None = None, *, short_empty_elements: bool = True - ) -> list[Any]: ... - +@overload +def tostring( + element: Element, + encoding: None = None, + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> bytes: ... +@overload +def tostring( + element: Element, + encoding: Literal["unicode"], + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> str: ... +@overload +def tostring( + element: Element, + encoding: str, + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> Any: ... +@overload +def tostringlist( + element: Element, + encoding: None = None, + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> list[bytes]: ... +@overload +def tostringlist( + element: Element, + encoding: Literal["unicode"], + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> list[str]: ... +@overload +def tostringlist( + element: Element, + encoding: str, + method: str | None = None, + *, + xml_declaration: bool | None = None, + default_namespace: str | None = None, + short_empty_elements: bool = True, +) -> list[Any]: ... def dump(elem: Element) -> None: ... if sys.version_info >= (3, 9): @@ -297,21 +267,18 @@ def fromstringlist(sequence: Sequence[str | ReadableBuffer], parser: XMLParser | _ElementFactory: TypeAlias = Callable[[Any, dict[Any, Any]], Element] class TreeBuilder: - if sys.version_info >= (3, 8): - # comment_factory can take None because passing None to Comment is not an error - def __init__( - self, - element_factory: _ElementFactory | None = ..., - *, - comment_factory: Callable[[str | None], Element] | None = ..., - pi_factory: Callable[[str, str | None], Element] | None = ..., - insert_comments: bool = ..., - insert_pis: bool = ..., - ) -> None: ... - insert_comments: bool - insert_pis: bool - else: - def __init__(self, element_factory: _ElementFactory | None = ...) -> None: ... + # comment_factory can take None because passing None to Comment is not an error + def __init__( + self, + element_factory: _ElementFactory | None = ..., + *, + comment_factory: Callable[[str | None], Element] | None = ..., + pi_factory: Callable[[str, str | None], Element] | None = ..., + insert_comments: bool = ..., + insert_pis: bool = ..., + ) -> None: ... + insert_comments: bool + insert_pis: bool def close(self) -> Element: ... def data(self, __data: str) -> None: ... @@ -319,31 +286,29 @@ class TreeBuilder: # depending on what the particular factory supports. def start(self, __tag: Any, __attrs: dict[Any, Any]) -> Element: ... def end(self, __tag: str) -> Element: ... - if sys.version_info >= (3, 8): - # These two methods have pos-only parameters in the C implementation - def comment(self, __text: str | None) -> Element: ... - def pi(self, __target: str, __text: str | None = None) -> Element: ... + # These two methods have pos-only parameters in the C implementation + def comment(self, __text: str | None) -> Element: ... + def pi(self, __target: str, __text: str | None = None) -> Element: ... -if sys.version_info >= (3, 8): - class C14NWriterTarget: - def __init__( - self, - write: Callable[[str], object], - *, - with_comments: bool = False, - strip_text: bool = False, - rewrite_prefixes: bool = False, - qname_aware_tags: Iterable[str] | None = None, - qname_aware_attrs: Iterable[str] | None = None, - exclude_attrs: Iterable[str] | None = None, - exclude_tags: Iterable[str] | None = None, - ) -> None: ... - def data(self, data: str) -> None: ... - def start_ns(self, prefix: str, uri: str) -> None: ... - def start(self, tag: str, attrs: Mapping[str, str]) -> None: ... - def end(self, tag: str) -> None: ... - def comment(self, text: str) -> None: ... - def pi(self, target: str, data: str) -> None: ... +class C14NWriterTarget: + def __init__( + self, + write: Callable[[str], object], + *, + with_comments: bool = False, + strip_text: bool = False, + rewrite_prefixes: bool = False, + qname_aware_tags: Iterable[str] | None = None, + qname_aware_attrs: Iterable[str] | None = None, + exclude_attrs: Iterable[str] | None = None, + exclude_tags: Iterable[str] | None = None, + ) -> None: ... + def data(self, data: str) -> None: ... + def start_ns(self, prefix: str, uri: str) -> None: ... + def start(self, tag: str, attrs: Mapping[str, str]) -> None: ... + def end(self, tag: str) -> None: ... + def comment(self, text: str) -> None: ... + def pi(self, target: str, data: str) -> None: ... class XMLParser: parser: Any @@ -351,11 +316,6 @@ class XMLParser: # TODO-what is entity used for??? entity: Any version: str - if sys.version_info >= (3, 8): - def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... - else: - def __init__(self, html: int = ..., target: Any = ..., encoding: str | None = ...) -> None: ... - def doctype(self, __name: str, __pubid: str, __system: str) -> None: ... - + def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... def close(self) -> Any: ... def feed(self, __data: str | ReadableBuffer) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi index d2d6f12d474a..a2eecc5a7864 100644 --- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi @@ -1,4 +1,3 @@ -import sys from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co from collections.abc import Iterable from typing import Protocol @@ -16,21 +15,11 @@ from xml.sax.xmlreader import XMLReader class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]): def close(self) -> None: ... -if sys.version_info >= (3, 8): - _Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str] -else: - _Source: TypeAlias = str | _SupportsReadClose[bytes] | _SupportsReadClose[str] +_Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str] default_parser_list: list[str] -if sys.version_info >= (3, 8): - - def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ... - -else: - - def make_parser(parser_list: list[str] = []) -> XMLReader: ... - +def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ... def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ... def parseString(string: ReadableBuffer | str, handler: ContentHandler, errorHandler: ErrorHandler | None = ...) -> None: ... def _create_parser(parser_name: str) -> XMLReader: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index 79969359f091..2be5f7df2d7d 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -1,14 +1,13 @@ import gzip import http.client -import sys import time from _typeshed import ReadableBuffer, SizedBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Protocol, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import Any, Literal, Protocol, overload +from typing_extensions import Self, TypeAlias class _SupportsTimeTuple(Protocol): def timetuple(self) -> time.struct_time: ... @@ -228,13 +227,9 @@ class Transport: _headers: list[tuple[str, str]] _extra_headers: list[tuple[str, str]] - if sys.version_info >= (3, 8): - def __init__( - self, use_datetime: bool = False, use_builtin_types: bool = False, *, headers: Iterable[tuple[str, str]] = () - ) -> None: ... - else: - def __init__(self, use_datetime: bool = False, use_builtin_types: bool = False) -> None: ... - + def __init__( + self, use_datetime: bool = False, use_builtin_types: bool = False, *, headers: Iterable[tuple[str, str]] = () + ) -> None: ... def request( self, host: _HostType, handler: str, request_body: SizedBuffer, verbose: bool = False ) -> tuple[_Marshallable, ...]: ... @@ -253,20 +248,14 @@ class Transport: def parse_response(self, response: http.client.HTTPResponse) -> tuple[_Marshallable, ...]: ... class SafeTransport(Transport): - if sys.version_info >= (3, 8): - def __init__( - self, - use_datetime: bool = False, - use_builtin_types: bool = False, - *, - headers: Iterable[tuple[str, str]] = (), - context: Any | None = None, - ) -> None: ... - else: - def __init__( - self, use_datetime: bool = False, use_builtin_types: bool = False, *, context: Any | None = None - ) -> None: ... - + def __init__( + self, + use_datetime: bool = False, + use_builtin_types: bool = False, + *, + headers: Iterable[tuple[str, str]] = (), + context: Any | None = None, + ) -> None: ... def make_connection(self, host: _HostType) -> http.client.HTTPSConnection: ... class ServerProxy: @@ -277,34 +266,19 @@ class ServerProxy: __verbose: bool __allow_none: bool - if sys.version_info >= (3, 8): - def __init__( - self, - uri: str, - transport: Transport | None = None, - encoding: str | None = None, - verbose: bool = False, - allow_none: bool = False, - use_datetime: bool = False, - use_builtin_types: bool = False, - *, - headers: Iterable[tuple[str, str]] = (), - context: Any | None = None, - ) -> None: ... - else: - def __init__( - self, - uri: str, - transport: Transport | None = None, - encoding: str | None = None, - verbose: bool = False, - allow_none: bool = False, - use_datetime: bool = False, - use_builtin_types: bool = False, - *, - context: Any | None = None, - ) -> None: ... - + def __init__( + self, + uri: str, + transport: Transport | None = None, + encoding: str | None = None, + verbose: bool = False, + allow_none: bool = False, + use_datetime: bool = False, + use_builtin_types: bool = False, + *, + headers: Iterable[tuple[str, str]] = (), + context: Any | None = None, + ) -> None: ... def __getattr__(self, name: str) -> _Method: ... @overload def __call__(self, attr: Literal["close"]) -> Callable[[], None]: ... diff --git a/mypy/typeshed/stdlib/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index 7fc39138fec5..3e6e78de3f70 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -1,6 +1,5 @@ import sys -from typing import Any -from typing_extensions import final +from typing import Any, final class Str(str): ... diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index 83982be3be08..be0cdf12a4a9 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -5,12 +5,13 @@ from collections.abc import Callable, Iterable, Iterator from io import TextIOWrapper from os import PathLike from types import TracebackType -from typing import IO, Protocol, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Literal, Protocol, overload +from typing_extensions import Self, TypeAlias __all__ = [ "BadZipFile", "BadZipfile", + "Path", "error", "ZIP_STORED", "ZIP_DEFLATED", @@ -23,15 +24,13 @@ __all__ = [ "LargeZipFile", ] -if sys.version_info >= (3, 8): - __all__ += ["Path"] - -# TODO: use TypeAlias when mypy bugs are fixed +# TODO: use TypeAlias for these two when mypy bugs are fixed # https://github.com/python/mypy/issues/16581 _DateTuple = tuple[int, int, int, int, int, int] # noqa: Y026 +_ZipFileMode = Literal["r", "w", "x", "a"] # noqa: Y026 + _ReadWriteMode: TypeAlias = Literal["r", "w"] _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] -_ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"] class BadZipFile(Exception): ... @@ -132,7 +131,7 @@ class ZipFile: strict_timestamps: bool = True, metadata_encoding: None = None, ) -> None: ... - elif sys.version_info >= (3, 8): + else: def __init__( self, file: StrPath | IO[bytes], @@ -143,15 +142,6 @@ class ZipFile: *, strict_timestamps: bool = True, ) -> None: ... - else: - def __init__( - self, - file: StrPath | IO[bytes], - mode: _ZipFileMode = "r", - compression: int = 0, - allowZip64: bool = True, - compresslevel: int | None = None, - ) -> None: ... def __enter__(self) -> Self: ... def __exit__( @@ -217,20 +207,15 @@ class ZipInfo: file_size: int orig_filename: str # undocumented def __init__(self, filename: str = "NoName", date_time: _DateTuple = (1980, 1, 1, 0, 0, 0)) -> None: ... - if sys.version_info >= (3, 8): - @classmethod - def from_file(cls, filename: StrPath, arcname: StrPath | None = None, *, strict_timestamps: bool = True) -> Self: ... - else: - @classmethod - def from_file(cls, filename: StrPath, arcname: StrPath | None = None) -> Self: ... - + @classmethod + def from_file(cls, filename: StrPath, arcname: StrPath | None = None, *, strict_timestamps: bool = True) -> Self: ... def is_dir(self) -> bool: ... def FileHeader(self, zip64: bool | None = None) -> bytes: ... if sys.version_info >= (3, 12): from zipfile._path import CompleteDirs as CompleteDirs, Path as Path -elif sys.version_info >= (3, 8): +else: class CompleteDirs(ZipFile): def resolve_dir(self, name: str) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi index 3cf034aec8f4..0398824e1fd2 100644 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -3,8 +3,8 @@ from _typeshed import StrPath from collections.abc import Iterator, Sequence from io import TextIOWrapper from os import PathLike -from typing import IO, overload -from typing_extensions import Literal, Self, TypeAlias +from typing import IO, Literal, overload +from typing_extensions import Self, TypeAlias from zipfile import ZipFile _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index 0189bfe712b5..158d573cac74 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -4,8 +4,7 @@ from importlib.abc import ResourceReader from importlib.machinery import ModuleSpec from types import CodeType, ModuleType -if sys.version_info >= (3, 8): - __all__ = ["ZipImportError", "zipimporter"] +__all__ = ["ZipImportError", "zipimporter"] class ZipImportError(ImportError): ... diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index c3419af0de3f..efeb5a88a76f 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import ReadableBuffer -from typing_extensions import Literal +from typing import Literal DEFLATED: Literal[8] DEF_MEM_LEVEL: int # can change diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 9f6f5d3838a4..b51a965c95da 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -866,30 +866,6 @@ _program.py:10: error: Incompatible types in assignment (expression has type "in _program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[Never]]"; expected "defaultdict[int, List[Never]]" _program.py:24: error: Invalid index type "str" for "MyDDict[Dict[Never, Never]]"; expected type "int" -[case testNoSubcriptionOfStdlibCollections] -# flags: --python-version 3.7 -import collections -from collections import Counter -from typing import TypeVar - -collections.defaultdict[int, str]() -Counter[int]() - -T = TypeVar('T') -DDint = collections.defaultdict[T, int] - -d = DDint[str]() -d[0] = 1 - -def f(d: collections.defaultdict[int, str]) -> None: - ... -[out] -_program.py:6: error: "defaultdict" is not subscriptable -_program.py:7: error: "Counter" is not subscriptable -_program.py:10: error: "defaultdict" is not subscriptable -_program.py:13: error: Invalid index type "int" for "defaultdict[str, int]"; expected type "str" -_program.py:15: error: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead - [case testCollectionsAliases] import typing as t import collections as c @@ -1590,12 +1566,11 @@ _testNoPython3StubAvailable.py:3: note: (or run "mypy --install-types" to instal [case testTypingOrderedDictAlias] -# flags: --python-version 3.7 from typing import OrderedDict x: OrderedDict[str, int] = OrderedDict({}) reveal_type(x) [out] -_testTypingOrderedDictAlias.py:4: note: Revealed type is "collections.OrderedDict[builtins.str, builtins.int]" +_testTypingOrderedDictAlias.py:3: note: Revealed type is "collections.OrderedDict[builtins.str, builtins.int]" [case testTypingExtensionsOrderedDictAlias] from typing_extensions import OrderedDict @@ -1711,7 +1686,6 @@ _testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[Type[builti _testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[Type[builtins.int], builtins.str]" [case testTypeAliasWithNewStyleUnionInStub] -# flags: --python-version 3.7 import m a: m.A reveal_type(a) @@ -1762,12 +1736,12 @@ CU4: TypeAlias = int | Callable[[str | bool], str] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" m.pyi:22: note: Revealed type is "typing._SpecialForm" -_testTypeAliasWithNewStyleUnionInStub.py:4: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:6: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:8: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:10: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:12: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" -_testTypeAliasWithNewStyleUnionInStub.py:14: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" +_testTypeAliasWithNewStyleUnionInStub.py:3: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:5: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:7: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:9: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:11: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" +_testTypeAliasWithNewStyleUnionInStub.py:13: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" [case testEnumNameWorkCorrectlyOn311] # flags: --python-version 3.11 From 3f58c2d940ac92afe8cf4c80cf6508b048dd272e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:56:02 +0100 Subject: [PATCH 0479/1617] Fix TypeVar defaults with None (PEP 696) (#16859) Ref: https://github.com/python/mypy/issues/14851 --- mypy/typeanal.py | 4 +++- test-data/unit/check-typevar-defaults.test | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 2b37d10e2aff..530793730f35 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -39,6 +39,7 @@ from mypy.options import Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs +from mypy.state import state from mypy.tvar_scope import TypeVarLikeScope from mypy.types import ( ANNOTATED_TYPE_NAMES, @@ -1893,7 +1894,8 @@ def fix_instance( t.args = tuple(args) fix_type_var_tuple_argument(t) if not t.type.has_type_var_tuple_type: - fixed = expand_type(t, env) + with state.strict_optional_set(options.strict_optional): + fixed = expand_type(t, env) assert isinstance(fixed, Instance) t.args = fixed.args diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 6136746cbd0a..75453a3a4f80 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -124,6 +124,7 @@ from typing import Generic, TypeVar, Union, overload T1 = TypeVar("T1") T2 = TypeVar("T2", default=int) T3 = TypeVar("T3", default=str) +T4 = TypeVar("T4", default=Union[int, None]) class ClassA1(Generic[T2, T3]): ... @@ -200,6 +201,20 @@ def func_a3( n = ClassA3[float, float, float]() # E: Type application has too many types (expected between 1 and 2) reveal_type(n) # N: Revealed type is "Any" +class ClassA4(Generic[T4]): ... + +def func_a4( + a: ClassA4, + b: ClassA4[float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA4[Union[builtins.int, None]]" + reveal_type(b) # N: Revealed type is "__main__.ClassA4[builtins.float]" + + k = ClassA4() + reveal_type(k) # N: Revealed type is "__main__.ClassA4[Union[builtins.int, None]]" + l = ClassA4[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassA4[builtins.float]" + [case testTypeVarDefaultsClass2] # flags: --disallow-any-generics from typing import Generic, ParamSpec From 3804f7e320288e625bbedc916a6c26d635bf8e3f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 3 Feb 2024 05:19:03 +0100 Subject: [PATCH 0480/1617] Expand TypeVarTuple default (PEP 696) (#16851) Small change to fix an error when expanding a TypeVarTuple default. ``` RuntimeError: Invalid type replacement to expand: Unpack[tuple[builtins.int, builtins.str]] ``` Ref: #14851 --- mypy/expandtype.py | 2 ++ mypy/test/testtypes.py | 2 +- test-data/unit/check-typevar-defaults.test | 12 ++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index b4bc1aa9b9a5..d2d294fb77f3 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -277,6 +277,8 @@ def visit_unpack_type(self, t: UnpackType) -> Type: def expand_unpack(self, t: UnpackType) -> list[Type]: assert isinstance(t.type, TypeVarTupleType) repl = get_proper_type(self.variables.get(t.type.id, t.type)) + if isinstance(repl, UnpackType): + repl = get_proper_type(repl.type) if isinstance(repl, TupleType): return repl.items elif ( diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index e8dd623bec53..b3f84905c47e 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1541,7 +1541,7 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 8 + ALLOWED_GET_PROPER_TYPES = 9 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 75453a3a4f80..544bc59494b3 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -289,8 +289,8 @@ def func_c1( # reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]" - # k = ClassC1() # TODO - # reveal_type(k) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO + k = ClassC1() + reveal_type(k) # N: Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" l = ClassC1[float]() reveal_type(l) # N: Revealed type is "__main__.ClassC1[builtins.float]" @@ -305,8 +305,8 @@ def func_c2( # reveal_type(b) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO reveal_type(c) # N: Revealed type is "__main__.ClassC2[builtins.int]" - # k = ClassC2() # TODO - # reveal_type(k) # Revealed type is "__main__.ClassC2[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO + k = ClassC2() + reveal_type(k) # N: Revealed type is "__main__.ClassC2[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" l = ClassC2[int]() # reveal_type(l) # Revealed type is "__main__.ClassC2[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO m = ClassC2[int, Unpack[Tuple[()]]]() @@ -323,8 +323,8 @@ def func_c3( reveal_type(b) # N: Revealed type is "__main__.ClassC3[builtins.int]" reveal_type(c) # N: Revealed type is "__main__.ClassC3[builtins.int, builtins.float]" - # k = ClassC3() # TODO - # reveal_type(k) # Revealed type is "__main__.ClassC3[builtins.str]" # TODO + k = ClassC3() + reveal_type(k) # N: Revealed type is "__main__.ClassC3[builtins.str]" l = ClassC3[int]() reveal_type(l) # N: Revealed type is "__main__.ClassC3[builtins.int]" m = ClassC3[int, Unpack[Tuple[float]]]() From 9bb102aafab24c59c42a8b0c071a467c123bd22e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 3 Feb 2024 10:25:56 +0100 Subject: [PATCH 0481/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/5ce34dc09606aa3fcd09254e6e51c9d68afefe49 --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/builtins.pyi | 91 +++++++++++++++++++++- mypy/typeshed/stdlib/functools.pyi | 40 ++++++---- mypy/typeshed/stdlib/numbers.pyi | 118 +++++++++++++++++------------ 4 files changed, 191 insertions(+), 64 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index ec3d86e41687..36540172ab95 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -167,7 +167,11 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 1ae1ba03e1ad..b8c807dd388d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -61,6 +61,7 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, + LiteralString, ParamSpec, Self, TypeAlias, @@ -434,8 +435,17 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] + @overload + def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... @@ -443,10 +453,13 @@ class str(Sequence[str]): self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... @overload - def expandtabs(self: str, tabsize: SupportsIndex = 8) -> str: ... + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -462,32 +475,91 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... + @overload + def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... + @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace( + self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 + ) -> LiteralString: ... + @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): + @overload + def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... + @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... + @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... + @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... + @overload + def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... + @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... + @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -498,6 +570,9 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... + @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -506,13 +581,25 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... + @overload + def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... + @overload def __mod__(self, __value: Any) -> str: ... + @overload + def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... + @overload + def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... + @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... @@ -1593,7 +1680,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 0f1666024f84..8c48e77c16cf 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import ParamSpec, Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -27,10 +27,12 @@ __all__ = [ if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWrapper = TypeVar("_RWrapper") @overload def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... @@ -80,31 +82,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi index c2a0301d5636..9f507d8335cf 100644 --- a/mypy/typeshed/stdlib/numbers.pyi +++ b/mypy/typeshed/stdlib/numbers.pyi @@ -1,9 +1,25 @@ # Note: these stubs are incomplete. The more complex type # signatures are currently omitted. +# +# Use SupportsComplex, SupportsFloat and SupportsIndex for return types in this module +# rather than `numbers.Complex`, `numbers.Real` and `numbers.Integral`, +# to avoid an excessive number of `type: ignore`s in subclasses of these ABCs +# (since type checkers don't see `complex` as a subtype of `numbers.Complex`, +# nor `float` as a subtype of `numbers.Real`, etc.) +import sys from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import SupportsFloat, overload +from typing import Literal, SupportsFloat, SupportsIndex, overload +from typing_extensions import TypeAlias + +if sys.version_info >= (3, 11): + from typing import SupportsComplex as _SupportsComplex +else: + # builtins.complex didn't have a __complex__ method on older Pythons + import typing + + _SupportsComplex: TypeAlias = typing.SupportsComplex | complex __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -11,120 +27,128 @@ class Number(metaclass=ABCMeta): @abstractmethod def __hash__(self) -> int: ... +# See comment at the top of the file +# for why some of these return types are purposefully vague class Complex(Number): @abstractmethod def __complex__(self) -> complex: ... def __bool__(self) -> bool: ... @property @abstractmethod - def real(self): ... + def real(self) -> SupportsFloat: ... @property @abstractmethod - def imag(self): ... + def imag(self) -> SupportsFloat: ... @abstractmethod - def __add__(self, other): ... + def __add__(self, other) -> _SupportsComplex: ... @abstractmethod - def __radd__(self, other): ... + def __radd__(self, other) -> _SupportsComplex: ... @abstractmethod - def __neg__(self): ... + def __neg__(self) -> _SupportsComplex: ... @abstractmethod - def __pos__(self): ... - def __sub__(self, other): ... - def __rsub__(self, other): ... + def __pos__(self) -> _SupportsComplex: ... + def __sub__(self, other) -> _SupportsComplex: ... + def __rsub__(self, other) -> _SupportsComplex: ... @abstractmethod - def __mul__(self, other): ... + def __mul__(self, other) -> _SupportsComplex: ... @abstractmethod - def __rmul__(self, other): ... + def __rmul__(self, other) -> _SupportsComplex: ... @abstractmethod - def __truediv__(self, other): ... + def __truediv__(self, other) -> _SupportsComplex: ... @abstractmethod - def __rtruediv__(self, other): ... + def __rtruediv__(self, other) -> _SupportsComplex: ... @abstractmethod - def __pow__(self, exponent): ... + def __pow__(self, exponent) -> _SupportsComplex: ... @abstractmethod - def __rpow__(self, base): ... + def __rpow__(self, base) -> _SupportsComplex: ... @abstractmethod - def __abs__(self) -> Real: ... + def __abs__(self) -> SupportsFloat: ... @abstractmethod - def conjugate(self): ... + def conjugate(self) -> _SupportsComplex: ... @abstractmethod def __eq__(self, other: object) -> bool: ... +# See comment at the top of the file +# for why some of these return types are purposefully vague class Real(Complex, SupportsFloat): @abstractmethod def __float__(self) -> float: ... @abstractmethod - def __trunc__(self) -> int: ... + def __trunc__(self) -> SupportsIndex: ... @abstractmethod - def __floor__(self) -> int: ... + def __floor__(self) -> SupportsIndex: ... @abstractmethod - def __ceil__(self) -> int: ... + def __ceil__(self) -> SupportsIndex: ... @abstractmethod @overload - def __round__(self, ndigits: None = None) -> int: ... + def __round__(self, ndigits: None = None) -> SupportsIndex: ... @abstractmethod @overload - def __round__(self, ndigits: int): ... - def __divmod__(self, other): ... - def __rdivmod__(self, other): ... + def __round__(self, ndigits: int) -> SupportsFloat: ... + def __divmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... + def __rdivmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... @abstractmethod - def __floordiv__(self, other) -> int: ... + def __floordiv__(self, other) -> SupportsFloat: ... @abstractmethod - def __rfloordiv__(self, other) -> int: ... + def __rfloordiv__(self, other) -> SupportsFloat: ... @abstractmethod - def __mod__(self, other): ... + def __mod__(self, other) -> SupportsFloat: ... @abstractmethod - def __rmod__(self, other): ... + def __rmod__(self, other) -> SupportsFloat: ... @abstractmethod def __lt__(self, other) -> bool: ... @abstractmethod def __le__(self, other) -> bool: ... def __complex__(self) -> complex: ... @property - def real(self): ... + def real(self) -> SupportsFloat: ... @property - def imag(self): ... - def conjugate(self): ... + def imag(self) -> Literal[0]: ... + def conjugate(self) -> SupportsFloat: ... # type: ignore[override] +# See comment at the top of the file +# for why some of these return types are purposefully vague class Rational(Real): @property @abstractmethod - def numerator(self) -> int: ... + def numerator(self) -> SupportsIndex: ... @property @abstractmethod - def denominator(self) -> int: ... + def denominator(self) -> SupportsIndex: ... def __float__(self) -> float: ... +# See comment at the top of the file +# for why some of these return types are purposefully vague class Integral(Rational): @abstractmethod def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent, modulus: Incomplete | None = None): ... + def __pow__(self, exponent, modulus: Incomplete | None = None) -> SupportsIndex: ... # type: ignore[override] @abstractmethod - def __lshift__(self, other): ... + def __lshift__(self, other) -> SupportsIndex: ... @abstractmethod - def __rlshift__(self, other): ... + def __rlshift__(self, other) -> SupportsIndex: ... @abstractmethod - def __rshift__(self, other): ... + def __rshift__(self, other) -> SupportsIndex: ... @abstractmethod - def __rrshift__(self, other): ... + def __rrshift__(self, other) -> SupportsIndex: ... @abstractmethod - def __and__(self, other): ... + def __and__(self, other) -> SupportsIndex: ... @abstractmethod - def __rand__(self, other): ... + def __rand__(self, other) -> SupportsIndex: ... @abstractmethod - def __xor__(self, other): ... + def __xor__(self, other) -> SupportsIndex: ... @abstractmethod - def __rxor__(self, other): ... + def __rxor__(self, other) -> SupportsIndex: ... @abstractmethod - def __or__(self, other): ... + def __or__(self, other) -> SupportsIndex: ... @abstractmethod - def __ror__(self, other): ... + def __ror__(self, other) -> SupportsIndex: ... @abstractmethod - def __invert__(self): ... + def __invert__(self) -> SupportsIndex: ... def __float__(self) -> float: ... @property - def numerator(self) -> int: ... + def numerator(self) -> SupportsIndex: ... @property - def denominator(self) -> int: ... + def denominator(self) -> Literal[1]: ... From d25e4a9ebb83d8fb2c49135be1e0c090196ee3b3 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0482/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 90 ------------------------------- 1 file changed, 90 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index b8c807dd388d..df78cec18226 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, - LiteralString, ParamSpec, Self, TypeAlias, @@ -435,31 +434,16 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... @@ -475,91 +459,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString: ... - @overload def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace( - self: LiteralString, __old: LiteralString, __new: LiteralString, __count: SupportsIndex = -1 - ) -> LiteralString: ... - @overload def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, __prefix: LiteralString) -> LiteralString: ... - @overload def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, __suffix: LiteralString) -> LiteralString: ... - @overload def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - @overload - def rjust(self: LiteralString, __width: SupportsIndex, __fillchar: LiteralString = " ") -> LiteralString: ... - @overload def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, __sep: LiteralString) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> bool: ... - @overload - def strip(self: LiteralString, __chars: LiteralString | None = None) -> LiteralString: ... - @overload def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, __table: _TranslateTable) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, __width: SupportsIndex) -> LiteralString: ... - @overload def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -570,9 +495,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, __value: LiteralString) -> LiteralString: ... - @overload def __add__(self, __value: str) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, __key: str) -> bool: ... # type: ignore[override] @@ -581,25 +503,13 @@ class str(Sequence[str]): def __getitem__(self, __key: SupportsIndex | slice) -> str: ... def __gt__(self, __value: str) -> bool: ... def __hash__(self) -> int: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, __value: str) -> bool: ... def __len__(self) -> int: ... def __lt__(self, __value: str) -> bool: ... - @overload - def __mod__(self: LiteralString, __value: LiteralString | tuple[LiteralString, ...]) -> LiteralString: ... - @overload def __mod__(self, __value: Any) -> str: ... - @overload - def __mul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __ne__(self, __value: object) -> bool: ... - @overload - def __rmul__(self: LiteralString, __value: SupportsIndex) -> LiteralString: ... - @overload def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From d132999ba631b332d0684173897e5947591f4acc Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0483/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index df78cec18226..09f082f2fe48 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1590,7 +1590,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(__iterable: Iterable[bool | _LiteralInteger], start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload From dd12a2d810f2bbe7a8686674397043b18575480f Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0484/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 36540172ab95..ec3d86e41687 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -167,11 +167,7 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From 0dd4b6f7576be3d3857fecefb298decdf0711ac7 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 4 Mar 2023 13:14:11 +0000 Subject: [PATCH 0485/1617] Revert use of `ParamSpec` for `functools.wraps` --- mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 8c48e77c16cf..0f1666024f84 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import SupportsAllComparisons, SupportsItems +from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload -from typing_extensions import ParamSpec, Self, TypeAlias +from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -27,12 +27,10 @@ __all__ = [ if sys.version_info >= (3, 9): __all__ += ["cache"] +_AnyCallable: TypeAlias = Callable[..., object] + _T = TypeVar("_T") _S = TypeVar("_S") -_PWrapped = ParamSpec("_PWrapped") -_RWrapped = TypeVar("_RWrapped") -_PWrapper = ParamSpec("_PWrapper") -_RWrapper = TypeVar("_RWrapper") @overload def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... @@ -82,41 +80,31 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] -class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): - __wrapped__: Callable[_PWrapped, _RWrapped] - def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... - # as with ``Callable``, we'll assume that these attributes exist - __name__: str - __qualname__: str - -class _Wrapper(Generic[_PWrapped, _RWrapped]): - def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... - if sys.version_info >= (3, 12): def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... else: def update_wrapper( - wrapper: Callable[_PWrapper, _RWrapper], - wrapped: Callable[_PWrapped, _RWrapped], + wrapper: _T, + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + ) -> _T: ... def wraps( - wrapped: Callable[_PWrapped, _RWrapped], + wrapped: _AnyCallable, assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _Wrapper[_PWrapped, _RWrapped]: ... + ) -> IdentityFunction: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From 8c2ef9dde8aa803e04038427ad84f09664d9d93f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:05:10 +0100 Subject: [PATCH 0486/1617] Update hashes in sync-typeshed.py following recent typeshed sync --- misc/sync-typeshed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 9d6fd92270a5..ee6414ab7b19 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,10 +179,10 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "588623ff2", # LiteralString reverts - "bdcc90e85", # sum reverts - "3e5d81337", # ctypes reverts - "344298e3a", # ParamSpec for functools.wraps + "d25e4a9eb", # LiteralString reverts + "d132999ba", # sum reverts + "dd12a2d81", # ctypes reverts + "0dd4b6f75", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: From 7bdd61f2d89ecd2cee4ebe6eb2375a72b29f0b10 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 5 Feb 2024 01:41:24 +0100 Subject: [PATCH 0487/1617] stubgen: Fix crash on star unpack of TypeVarTuple (#16869) Fixes #16864 --- mypy/stubgen.py | 4 +++ mypy/test/teststubgen.py | 4 +++ test-data/unit/stubgen.test | 50 +++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 1f7a01e6ae3c..c314fabc882d 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -100,6 +100,7 @@ OpExpr, OverloadedFuncDef, SetExpr, + StarExpr, Statement, StrExpr, TempNode, @@ -339,6 +340,9 @@ def visit_ellipsis(self, node: EllipsisExpr) -> str: def visit_op_expr(self, o: OpExpr) -> str: return f"{o.left.accept(self)} {o.op} {o.right.accept(self)}" + def visit_star_expr(self, o: StarExpr) -> str: + return f"*{o.expr.accept(self)}" + def find_defined_names(file: MypyFile) -> set[str]: finder = DefinitionFinder() diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index c138f9953918..3669772854cb 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -10,6 +10,8 @@ from types import ModuleType from typing import Any +import pytest + from mypy.errors import CompileError from mypy.moduleinspect import InspectError, ModuleInspect from mypy.stubdoc import ( @@ -698,6 +700,8 @@ def run_case_inner(self, testcase: DataDrivenTestCase) -> None: f.write(content) options = self.parse_flags(source, extra) + if sys.version_info < options.pyversion: + pytest.skip() modules = self.parse_modules(source) out_dir = "out" try: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 751a1a7fbbcf..3b3bc658a14a 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1211,6 +1211,56 @@ T = TypeVar('T') class D(Generic[T]): ... +[case testGenericClassTypeVarTuple] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack +Ts = TypeVarTuple('Ts') +class D(Generic[Unpack[Ts]]): ... +[out] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple('Ts') + +class D(Generic[Unpack[Ts]]): ... + +[case testGenericClassTypeVarTuple_semanal] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack +Ts = TypeVarTuple('Ts') +class D(Generic[Unpack[Ts]]): ... +[out] +from typing import Generic +from typing_extensions import TypeVarTuple, Unpack + +Ts = TypeVarTuple('Ts') + +class D(Generic[Unpack[Ts]]): ... + +[case testGenericClassTypeVarTuplePy311] +# flags: --python-version=3.11 +from typing import Generic, TypeVarTuple +Ts = TypeVarTuple('Ts') +class D(Generic[*Ts]): ... +[out] +from typing import Generic, TypeVarTuple + +Ts = TypeVarTuple('Ts') + +class D(Generic[*Ts]): ... + +[case testGenericClassTypeVarTuplePy311_semanal] +# flags: --python-version=3.11 +from typing import Generic, TypeVarTuple +Ts = TypeVarTuple('Ts') +class D(Generic[*Ts]): ... +[out] +from typing import Generic, TypeVarTuple + +Ts = TypeVarTuple('Ts') + +class D(Generic[*Ts]): ... + [case testObjectBaseClass] class A(object): ... [out] From ede0b200a10186a095378516d840389f8da4edd4 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 5 Feb 2024 00:42:13 +0000 Subject: [PATCH 0488/1617] Bump ruff to 0.2.0 (#16870) Fix this deprecation warning when running ruff on the repository directly: ``` warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `pyproject.toml`: - 'ignore' -> 'lint.ignore' - 'select' -> 'lint.select' - 'unfixable' -> 'lint.unfixable' - 'isort' -> 'lint.isort' ``` Note that you don't see the deprecation warnings if you run ruff via pre-commit (only if you run ruff directly, e.g. via ruff .), since pre-commit [swallows all output from a tool if the tool exits with code 0](https://stackoverflow.com/questions/72895720/pre-commit-hook-does-not-echo-on-terminal/72898524#72898524). --- .pre-commit-config.yaml | 2 +- pyproject.toml | 29 +++++++++++++++-------------- test-requirements.in | 2 +- test-requirements.txt | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eec410457641..4cfb8297a66a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.15 # must match test-requirements.txt + rev: v0.2.0 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/pyproject.toml b/pyproject.toml index 5fc0cee3f65c..fa6cf876b647 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,20 @@ line-length = 99 target-version = "py38" fix = true +extend-exclude = [ + "@*", + # Sphinx configuration is irrelevant + "docs/source/conf.py", + "mypyc/doc/conf.py", + # tests have more relaxed styling requirements + # fixtures have their own .pyi-specific configuration + "test-data/*", + "mypyc/test-data/*", + # typeshed has its own .pyi-specific configuration + "mypy/typeshed/*", +] + +[tool.ruff.lint] select = [ "E", # pycodestyle (error) "F", # pyflakes @@ -66,20 +80,7 @@ unfixable = [ "UP036", # sometimes it's better to just noqa this ] -extend-exclude = [ - "@*", - # Sphinx configuration is irrelevant - "docs/source/conf.py", - "mypyc/doc/conf.py", - # tests have more relaxed styling requirements - # fixtures have their own .pyi-specific configuration - "test-data/*", - "mypyc/test-data/*", - # typeshed has its own .pyi-specific configuration - "mypy/typeshed/*", -] - -[tool.ruff.isort] +[tool.ruff.lint.isort] combine-as-imports = true extra-standard-library = ["typing_extensions"] diff --git a/test-requirements.in b/test-requirements.in index a158f5c05ee1..8eeef206018e 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -14,6 +14,6 @@ psutil>=4.0 pytest>=7.4.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.1.15 # must match version in .pre-commit-config.yaml +ruff==0.2.0 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 diff --git a/test-requirements.txt b/test-requirements.txt index dcd250970a15..525edbc252d8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -67,7 +67,7 @@ ruamel-yaml==0.17.40 # via pre-commit-hooks ruamel-yaml-clib==0.2.8 # via ruamel-yaml -ruff==0.1.15 +ruff==0.2.0 # via -r test-requirements.in tomli==2.0.1 # via -r test-requirements.in From b956e6a57c4dd36d670097a3eccf7dc092348fec Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Mon, 5 Feb 2024 14:17:43 +0100 Subject: [PATCH 0489/1617] stubtest: Private parameters can be omitted (#16507) Fixes #16443 --- CHANGELOG.md | 3 ++ mypy/stubtest.py | 14 ++++++-- mypy/test/teststubtest.py | 72 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d79b415f5d..bae881656865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Next release +Stubtest will ignore private function/method parameters when they are missing from the stub. Private parameters +names start with a single underscore and have a default (PR [16507](https://github.com/python/mypy/pull/16507)). + ## Mypy 1.8 We’ve just uploaded mypy 1.8 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 225c1c35c1be..0e8a1c3ceac2 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -940,7 +940,8 @@ def _verify_signature( elif len(stub.pos) < len(runtime.pos): for runtime_arg in runtime.pos[len(stub.pos) :]: if runtime_arg.name not in stub.kwonly: - yield f'stub does not have argument "{runtime_arg.name}"' + if not _is_private_parameter(runtime_arg): + yield f'stub does not have argument "{runtime_arg.name}"' else: yield f'runtime argument "{runtime_arg.name}" is not keyword-only' @@ -980,7 +981,8 @@ def _verify_signature( ): yield f'stub argument "{arg}" is not keyword-only' else: - yield f'stub does not have argument "{arg}"' + if not _is_private_parameter(runtime.kwonly[arg]): + yield f'stub does not have argument "{arg}"' # Checks involving **kwargs if stub.varkw is None and runtime.varkw is not None: @@ -995,6 +997,14 @@ def _verify_signature( yield f'runtime does not have **kwargs argument "{stub.varkw.variable.name}"' +def _is_private_parameter(arg: inspect.Parameter) -> bool: + return ( + arg.name.startswith("_") + and not arg.name.startswith("__") + and arg.default is not inspect.Parameter.empty + ) + + @verify.register(nodes.FuncItem) def verify_funcitem( stub: nodes.FuncItem, runtime: MaybeMissing[Any], object_path: list[str] diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 55f35200a7f5..418308e2e65e 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -346,6 +346,78 @@ def test_arg_kind(self) -> Iterator[Case]: error="stub_posonly_570", ) + @collect_cases + def test_private_parameters(self) -> Iterator[Case]: + # Private parameters can optionally be omitted. + yield Case( + stub="def priv_pos_arg_missing() -> None: ...", + runtime="def priv_pos_arg_missing(_p1=None): pass", + error=None, + ) + yield Case( + stub="def multi_priv_args() -> None: ...", + runtime="def multi_priv_args(_p='', _q=''): pass", + error=None, + ) + yield Case( + stub="def priv_kwarg_missing() -> None: ...", + runtime="def priv_kwarg_missing(*, _p2=''): pass", + error=None, + ) + # But if they are included, they must be correct. + yield Case( + stub="def priv_pos_arg_wrong(_p: int = ...) -> None: ...", + runtime="def priv_pos_arg_wrong(_p=None): pass", + error="priv_pos_arg_wrong", + ) + yield Case( + stub="def priv_kwarg_wrong(*, _p: int = ...) -> None: ...", + runtime="def priv_kwarg_wrong(*, _p=None): pass", + error="priv_kwarg_wrong", + ) + # Private parameters must have a default and start with exactly one + # underscore. + yield Case( + stub="def pos_arg_no_default() -> None: ...", + runtime="def pos_arg_no_default(_np): pass", + error="pos_arg_no_default", + ) + yield Case( + stub="def kwarg_no_default() -> None: ...", + runtime="def kwarg_no_default(*, _np): pass", + error="kwarg_no_default", + ) + yield Case( + stub="def double_underscore_pos_arg() -> None: ...", + runtime="def double_underscore_pos_arg(__np = None): pass", + error="double_underscore_pos_arg", + ) + yield Case( + stub="def double_underscore_kwarg() -> None: ...", + runtime="def double_underscore_kwarg(*, __np = None): pass", + error="double_underscore_kwarg", + ) + # But spot parameters that are accidentally not marked kw-only and + # vice-versa. + yield Case( + stub="def priv_arg_is_kwonly(_p=...) -> None: ...", + runtime="def priv_arg_is_kwonly(*, _p=''): pass", + error="priv_arg_is_kwonly", + ) + yield Case( + stub="def priv_arg_is_positional(*, _p=...) -> None: ...", + runtime="def priv_arg_is_positional(_p=''): pass", + error="priv_arg_is_positional", + ) + # Private parameters not at the end of the parameter list must be + # included so that users can pass the following arguments using + # positional syntax. + yield Case( + stub="def priv_args_not_at_end(*, q='') -> None: ...", + runtime="def priv_args_not_at_end(_p='', q=''): pass", + error="priv_args_not_at_end", + ) + @collect_cases def test_default_presence(self) -> Iterator[Case]: yield Case( From 5c5f3ad412bcd0ea510f5918578ed24325829033 Mon Sep 17 00:00:00 2001 From: James Braza Date: Tue, 6 Feb 2024 16:49:52 -0800 Subject: [PATCH 0490/1617] Docs: adding missing `mutable-override` to section title (#16886) Closes https://github.com/python/mypy/issues/16880 Supercedes https://github.com/python/mypy/pull/16881 --- docs/source/error_code_list2.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 60f870c57db9..c966fe1f7ea6 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -484,11 +484,11 @@ Example: .. _code-mutable-override: -Check that overrides of mutable attributes are safe ---------------------------------------------------- +Check that overrides of mutable attributes are safe [mutable-override] +---------------------------------------------------------------------- -This will enable the check for unsafe overrides of mutable attributes. For -historical reasons, and because this is a relatively common pattern in Python, +`mutable-override` will enable the check for unsafe overrides of mutable attributes. +For historical reasons, and because this is a relatively common pattern in Python, this check is not enabled by default. The example below is unsafe, and will be flagged when this error code is enabled: From 780a29d0b1b93848d7ce3faf8e819a6cc140b2e5 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:28:03 +0100 Subject: [PATCH 0491/1617] Bump version to 1.10.0+dev (#16888) The release branch has been cut: https://github.com/python/mypy/tree/release-1.9.0 Increase the dev version. --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 74e80839308c..93ab6463c573 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.9.0+dev" +__version__ = "1.10.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 517f5aee23ba218f615bcd4427bca62f120bc222 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 9 Feb 2024 13:27:33 +0000 Subject: [PATCH 0492/1617] Stubtest: ignore a new protocol dunder (#16895) This is added to all protocol classes on Python 3.12.2+ (it was added in a patch release of 3.12 as part of a bugfix). There's no reason why you'd want to explicitly include it in a stub (and doing so would lead the type checker to incorrectly conclude that you wanted a member literally called `__non_callable_proto_members__`) Cf. https://github.com/python/typeshed/pull/11384 and https://github.com/python/typeshed/issues/11383 --- mypy/stubtest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 0e8a1c3ceac2..c2f82c98d089 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1486,6 +1486,7 @@ def verify_typealias( # Added to all protocol classes on 3.12+ (or if using typing_extensions.Protocol) "__protocol_attrs__", "__callable_proto_members_only__", + "__non_callable_proto_members__", # typing implementation details, consider removing some of these: "__parameters__", "__origin__", From d8e3d591048cfe16dbc9cfa2ff88db38c587e3d0 Mon Sep 17 00:00:00 2001 From: jhance Date: Fri, 9 Feb 2024 18:28:10 -0800 Subject: [PATCH 0493/1617] Unsupport targetting 3.7. (#16883) This syncs up this constant to the actual minimum version that typeshed is now targetting. --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- mypy/defaults.py | 2 +- test-data/unit/cmdline.test | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mypy/defaults.py b/mypy/defaults.py index 6a09a61a461e..2bbae23d7e2d 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -10,7 +10,7 @@ # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. -PYTHON3_VERSION_MIN: Final = (3, 7) # Keep in sync with typeshed's python support +PYTHON3_VERSION_MIN: Final = (3, 8) # Keep in sync with typeshed's python support CACHE_DIR: Final = ".mypy_cache" CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index f286f4781ed5..2262b7e7280c 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -592,7 +592,7 @@ main.py:1: error: Cannot find implementation or library stub for module named "a \[tool.mypy] python_version = 3.10 [out] -pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.7 or higher). You may need to put quotes around your Python version +pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.8 or higher). You may need to put quotes around your Python version == Return code: 0 [case testPythonVersionTooOld10] @@ -604,13 +604,13 @@ python_version = 1.0 mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 3) == Return code: 0 -[case testPythonVersionTooOld36] +[case testPythonVersionTooOld37] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.6 +python_version = 3.7 [out] -mypy.ini: [mypy]: python_version: Python 3.6 is not supported (must be 3.7 or higher) +mypy.ini: [mypy]: python_version: Python 3.7 is not supported (must be 3.8 or higher) == Return code: 0 [case testPythonVersionTooNew40] @@ -633,11 +633,11 @@ usage: mypy [-h] [-v] [-V] [more options; see below] mypy: error: Mypy no longer supports checking Python 2 code. Consider pinning to mypy<0.980 if you need to check Python 2 code. == Return code: 2 -[case testPythonVersionAccepted37] +[case testPythonVersionAccepted38] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.7 +python_version = 3.8 [out] [case testPythonVersionAccepted311] From 996544fe21aa21ee29d1ed5a178e2026edbe6bce Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:53:39 +0100 Subject: [PATCH 0494/1617] Update CI actions (#16901) Update `actions/setup-python` from `v4` to `v5` and `actions/github-script` from `v6` to `v7`. https://github.com/actions/setup-python/releases/tag/v5.0.0 https://github.com/actions/github-script/releases/tag/v7.0.0 --- .github/workflows/build_wheels.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/mypy_primer.yml | 2 +- .github/workflows/mypy_primer_comment.yml | 2 +- .github/workflows/sync_typeshed.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/test_stubgenc.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index f1438279673d..8055cfd24180 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Trigger script diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ad6b57c53fd9..f13a3de1f2e3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: VERIFY_MYPY_ERROR_CODES: 1 steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.8' - name: Install tox diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index f8991e27970a..07a1d0863eb2 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -37,7 +37,7 @@ jobs: with: path: mypy_to_test fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install dependencies diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 6e3bb590364f..492e03aff16e 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -44,7 +44,7 @@ jobs: - name: Post comment id: post-comment - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index de9e0aad599f..b545e7b0662b 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 # TODO: use whatever solution ends up working for # https://github.com/python/typeshed/issues/8434 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: git config diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4613605425c3..e4e44c671287 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -120,7 +120,7 @@ jobs: PYTEST_ADDOPTS: --color=yes steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.arch }} diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 7bdcfdb305bb..519f63ac2bd7 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup 🐍 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 From 837f7e0ed4f87869f314ec102c0d6e47ec3272ec Mon Sep 17 00:00:00 2001 From: Sam Xifaras Date: Sun, 11 Feb 2024 04:04:28 -0500 Subject: [PATCH 0495/1617] stubtest: correct type annotations in _Arguments (#16897) Two fields in the `_Arguments` class, `mypy_config_file` and `custom_typeshed_dir`, can take on a None value, but they are not marked as such. Calling `stubtest.parse_options` on an empty list of arguments reproduces the situation where these two fields are None. --- mypy/stubtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index c2f82c98d089..dd43c472d67f 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1878,8 +1878,8 @@ class _Arguments: allowlist: list[str] generate_allowlist: bool ignore_unused_allowlist: bool - mypy_config_file: str - custom_typeshed_dir: str + mypy_config_file: str | None + custom_typeshed_dir: str | None check_typeshed: bool version: str @@ -1922,7 +1922,7 @@ def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: options.incremental = False options.custom_typeshed_dir = args.custom_typeshed_dir if options.custom_typeshed_dir: - options.abs_custom_typeshed_dir = os.path.abspath(args.custom_typeshed_dir) + options.abs_custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) options.config_file = args.mypy_config_file options.use_builtins_fixtures = use_builtins_fixtures From 4a9c1e95457f253f87ef5db970ad8d59209c4715 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 11 Feb 2024 16:55:59 +0100 Subject: [PATCH 0496/1617] stubgen: Add support for PEP 570 positional-only parameters (#16904) This only adds support for Python modules (x-ref #14138) --- mypy/stubgen.py | 9 +++++++++ test-data/unit/stubgen.test | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index c314fabc882d..36e8bd2acfb4 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -110,6 +110,7 @@ Var, ) from mypy.options import Options as MypyOptions +from mypy.sharedparse import MAGIC_METHODS_POS_ARGS_ONLY from mypy.stubdoc import ArgSig, FunctionSig from mypy.stubgenc import InspectionStubGenerator, generate_stub_for_c_module from mypy.stubutil import ( @@ -480,6 +481,9 @@ def get_default_function_sig(self, func_def: FuncDef, ctx: FunctionContext) -> F def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: args: list[ArgSig] = [] + # Ignore pos-only status of magic methods whose args names are elided by mypy at parse + actually_pos_only_args = o.name not in MAGIC_METHODS_POS_ARGS_ONLY + pos_only_marker_position = 0 # Where to insert "/", if any for i, arg_ in enumerate(o.arguments): var = arg_.variable kind = arg_.kind @@ -500,6 +504,9 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: if not isinstance(get_proper_type(annotated_type), AnyType): typename = self.print_annotation(annotated_type) + if actually_pos_only_args and arg_.pos_only: + pos_only_marker_position += 1 + if kind.is_named() and not any(arg.name.startswith("*") for arg in args): args.append(ArgSig("*")) @@ -518,6 +525,8 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: args.append( ArgSig(name, typename, default=bool(arg_.initializer), default_value=default) ) + if pos_only_marker_position: + args.insert(pos_only_marker_position, ArgSig("/")) if ctx.class_info is not None and all( arg.type is None and arg.default is False for arg in args diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 3b3bc658a14a..b5bccaa4cdbd 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4231,3 +4231,25 @@ o = int | None def f1(a: int | tuple[int, int | None] | None) -> int: ... def f2(a: int | x.Union[int, int] | float | None) -> int: ... + +[case testPEP570PosOnlyParams] +def f(x=0, /): ... +def f1(x: int, /): ... +def f2(x: int, y: float = 1, /): ... +def f3(x: int, /, y: float): ... +def f4(x: int, /, y: float = 1): ... +def f5(x: int, /, *, y: float): ... +def f6(x: int = 0, /, *, y: float): ... +def f7(x: int, /, *, y: float = 1): ... +def f8(x: int = 0, /, *, y: float = 1): ... + +[out] +def f(x: int = 0, /) -> None: ... +def f1(x: int, /): ... +def f2(x: int, y: float = 1, /): ... +def f3(x: int, /, y: float): ... +def f4(x: int, /, y: float = 1): ... +def f5(x: int, /, *, y: float): ... +def f6(x: int = 0, /, *, y: float): ... +def f7(x: int, /, *, y: float = 1): ... +def f8(x: int = 0, /, *, y: float = 1): ... From b6e91d46b299bfd0af36b37586d3337a20e14b0e Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 11 Feb 2024 23:09:22 +0100 Subject: [PATCH 0497/1617] stubgen: Preserve empty tuple annotation (#16907) --- mypy/stubutil.py | 2 ++ test-data/unit/stubgen.test | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 1a9c2357c58e..69af643efab2 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -250,6 +250,8 @@ def visit_unbound_type(self, t: UnboundType) -> str: self.stubgen.import_tracker.require_name(s) if t.args: s += f"[{self.args_str(t.args)}]" + elif t.empty_tuple_index: + s += "[()]" return s def visit_none_type(self, t: NoneType) -> str: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index b5bccaa4cdbd..c56f6b40b74d 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4253,3 +4253,17 @@ def f5(x: int, /, *, y: float): ... def f6(x: int = 0, /, *, y: float): ... def f7(x: int, /, *, y: float = 1): ... def f8(x: int = 0, /, *, y: float = 1): ... + +[case testPreserveEmptyTuple] +ann: tuple[()] +alias = tuple[()] +def f(x: tuple[()]): ... +class C(tuple[()]): ... + +[out] +ann: tuple[()] +alias = tuple[()] + +def f(x: tuple[()]): ... + +class C(tuple[()]): ... From c26f1297d4f19d2d1124a30efc97caebb8c28616 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 13 Feb 2024 00:19:09 +1000 Subject: [PATCH 0498/1617] Allow inferring +int to be a Literal (#16910) This makes unary positive on integers preserve the literal value of the integer, allowing `var: Literal[1] = +1` to be accepted. Basically I looked for code handling `__neg__` and added a branch for `__pos__` as well. Fixes #16728. --- mypy/checkexpr.py | 4 ++++ mypy/exprtotype.py | 9 ++++++--- mypy/plugins/default.py | 25 +++++++++++++++++++------ test-data/unit/check-literal.test | 13 ++++++++++++- test-data/unit/check-tuples.test | 4 +++- test-data/unit/fixtures/tuple.pyi | 1 + 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ff7b7fa2ff58..2842606b7b18 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4437,6 +4437,10 @@ def try_getting_int_literals(self, index: Expression) -> list[int] | None: operand = index.expr if isinstance(operand, IntExpr): return [-1 * operand.value] + if index.op == "+": + operand = index.expr + if isinstance(operand, IntExpr): + return [operand.value] typ = get_proper_type(self.accept(index)) if isinstance(typ, Instance) and typ.last_known_value is not None: typ = typ.last_known_value diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index 7a50429b81d1..2218a950788c 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -183,9 +183,12 @@ def expr_to_unanalyzed_type( elif isinstance(expr, UnaryExpr): typ = expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax) if isinstance(typ, RawExpressionType): - if isinstance(typ.literal_value, int) and expr.op == "-": - typ.literal_value *= -1 - return typ + if isinstance(typ.literal_value, int): + if expr.op == "-": + typ.literal_value *= -1 + return typ + elif expr.op == "+": + return typ raise TypeTranslationError() elif isinstance(expr, IntExpr): return RawExpressionType(expr.value, "builtins.int", line=expr.line, column=expr.column) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index ddcc37f465fe..93fff5320cd5 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -100,6 +100,8 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No return int_pow_callback elif fullname == "builtins.int.__neg__": return int_neg_callback + elif fullname == "builtins.int.__pos__": + return int_pos_callback elif fullname in ("builtins.tuple.__mul__", "builtins.tuple.__rmul__"): return tuple_mul_callback elif fullname in {n + ".setdefault" for n in TPDICT_FB_NAMES}: @@ -471,32 +473,43 @@ def int_pow_callback(ctx: MethodContext) -> Type: return ctx.default_return_type -def int_neg_callback(ctx: MethodContext) -> Type: - """Infer a more precise return type for int.__neg__. +def int_neg_callback(ctx: MethodContext, multiplier: int = -1) -> Type: + """Infer a more precise return type for int.__neg__ and int.__pos__. This is mainly used to infer the return type as LiteralType - if the original underlying object is a LiteralType object + if the original underlying object is a LiteralType object. """ if isinstance(ctx.type, Instance) and ctx.type.last_known_value is not None: value = ctx.type.last_known_value.value fallback = ctx.type.last_known_value.fallback if isinstance(value, int): if is_literal_type_like(ctx.api.type_context[-1]): - return LiteralType(value=-value, fallback=fallback) + return LiteralType(value=multiplier * value, fallback=fallback) else: return ctx.type.copy_modified( last_known_value=LiteralType( - value=-value, fallback=ctx.type, line=ctx.type.line, column=ctx.type.column + value=multiplier * value, + fallback=ctx.type, + line=ctx.type.line, + column=ctx.type.column, ) ) elif isinstance(ctx.type, LiteralType): value = ctx.type.value fallback = ctx.type.fallback if isinstance(value, int): - return LiteralType(value=-value, fallback=fallback) + return LiteralType(value=multiplier * value, fallback=fallback) return ctx.default_return_type +def int_pos_callback(ctx: MethodContext) -> Type: + """Infer a more precise return type for int.__pos__. + + This is identical to __neg__, except the value is not inverted. + """ + return int_neg_callback(ctx, +1) + + def tuple_mul_callback(ctx: MethodContext) -> Type: """Infer a more precise return type for tuple.__mul__ and tuple.__rmul__. diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index de4440ce7f49..5604cc4b5893 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -397,29 +397,36 @@ from typing_extensions import Literal a1: Literal[4] b1: Literal[0x2a] c1: Literal[-300] +d1: Literal[+8] reveal_type(a1) # N: Revealed type is "Literal[4]" reveal_type(b1) # N: Revealed type is "Literal[42]" reveal_type(c1) # N: Revealed type is "Literal[-300]" +reveal_type(d1) # N: Revealed type is "Literal[8]" a2t = Literal[4] b2t = Literal[0x2a] c2t = Literal[-300] +d2t = Literal[+8] a2: a2t b2: b2t c2: c2t +d2: d2t reveal_type(a2) # N: Revealed type is "Literal[4]" reveal_type(b2) # N: Revealed type is "Literal[42]" reveal_type(c2) # N: Revealed type is "Literal[-300]" +reveal_type(d2) # N: Revealed type is "Literal[8]" def f1(x: Literal[4]) -> Literal[4]: pass def f2(x: Literal[0x2a]) -> Literal[0x2a]: pass def f3(x: Literal[-300]) -> Literal[-300]: pass +def f4(x: Literal[+8]) -> Literal[+8]: pass reveal_type(f1) # N: Revealed type is "def (x: Literal[4]) -> Literal[4]" reveal_type(f2) # N: Revealed type is "def (x: Literal[42]) -> Literal[42]" reveal_type(f3) # N: Revealed type is "def (x: Literal[-300]) -> Literal[-300]" +reveal_type(f4) # N: Revealed type is "def (x: Literal[8]) -> Literal[8]" [builtins fixtures/tuple.pyi] [out] @@ -2747,6 +2754,9 @@ d: Literal[1] = 1 e: Literal[2] = 2 f: Literal[+1] = 1 g: Literal[+2] = 2 +h: Literal[1] = +1 +i: Literal[+2] = 2 +j: Literal[+3] = +3 x: Literal[+True] = True # E: Invalid type: Literal[...] cannot contain arbitrary expressions y: Literal[-True] = -1 # E: Invalid type: Literal[...] cannot contain arbitrary expressions @@ -2759,6 +2769,7 @@ from typing_extensions import Literal, Final ONE: Final = 1 x: Literal[-1] = -ONE +y: Literal[+1] = +ONE TWO: Final = 2 THREE: Final = 3 @@ -2766,7 +2777,7 @@ THREE: Final = 3 err_code = -TWO if bool(): err_code = -THREE -[builtins fixtures/float.pyi] +[builtins fixtures/ops.pyi] [case testAliasForEnumTypeAsLiteral] from typing_extensions import Literal diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 66115ca0c30d..ad4893c2890a 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -337,10 +337,12 @@ if int(): b = t1[-1] if int(): a = t1[(0)] +if int(): + b = t1[+1] if int(): x = t3[0:3] # type (A, B, C) if int(): - y = t3[0:5:2] # type (A, C, E) + y = t3[0:+5:2] # type (A, C, E) if int(): x = t3[:-2] # type (A, B, C) diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index cb6347e9f2fd..eb89de8c86ef 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -32,6 +32,7 @@ class classmethod: pass # We need int and slice for indexing tuples. class int: def __neg__(self) -> 'int': pass + def __pos__(self) -> 'int': pass class float: pass class slice: pass class bool(int): pass From 5ffa6dde6e295c7cd1bc237dcc252672a39c625e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 22:09:41 -0800 Subject: [PATCH 0499/1617] Sync typeshed (#16918) Source commit: https://github.com/python/typeshed/commit/48a0497b2310e8e4bcb81c72aed7517b2a3a3bfd --- mypy/typeshed/stdlib/_ast.pyi | 6 +- mypy/typeshed/stdlib/_curses.pyi | 1 + mypy/typeshed/stdlib/_msi.pyi | 1 + mypy/typeshed/stdlib/_thread.pyi | 1 + mypy/typeshed/stdlib/asyncio/sslproto.pyi | 4 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 3 + .../stdlib/asyncio/windows_events.pyi | 2 + mypy/typeshed/stdlib/calendar.pyi | 4 +- mypy/typeshed/stdlib/contextlib.pyi | 1 + mypy/typeshed/stdlib/dbm/gnu.pyi | 1 + mypy/typeshed/stdlib/dbm/ndbm.pyi | 1 + mypy/typeshed/stdlib/enum.pyi | 3 + mypy/typeshed/stdlib/ftplib.pyi | 12 +- mypy/typeshed/stdlib/grp.pyi | 1 + .../stdlib/importlib/metadata/__init__.pyi | 1 + mypy/typeshed/stdlib/inspect.pyi | 1 + mypy/typeshed/stdlib/io.pyi | 42 +- mypy/typeshed/stdlib/msilib/__init__.pyi | 1 + .../stdlib/multiprocessing/reduction.pyi | 1 + mypy/typeshed/stdlib/os/__init__.pyi | 7 + mypy/typeshed/stdlib/pstats.pyi | 1 + mypy/typeshed/stdlib/pwd.pyi | 1 + mypy/typeshed/stdlib/pyexpat/__init__.pyi | 9 +- mypy/typeshed/stdlib/resource.pyi | 2 + mypy/typeshed/stdlib/select.pyi | 3 + mypy/typeshed/stdlib/signal.pyi | 3 + mypy/typeshed/stdlib/spwd.pyi | 1 + mypy/typeshed/stdlib/string.pyi | 2 +- mypy/typeshed/stdlib/subprocess.pyi | 9 +- mypy/typeshed/stdlib/sys/__init__.pyi | 2 + mypy/typeshed/stdlib/time.pyi | 1 + mypy/typeshed/stdlib/tkinter/__init__.pyi | 707 +++++++++--------- mypy/typeshed/stdlib/tkinter/ttk.pyi | 357 ++++----- mypy/typeshed/stdlib/typing.pyi | 3 + mypy/typeshed/stdlib/unicodedata.pyi | 10 +- mypy/typeshed/stdlib/unittest/result.pyi | 1 + 36 files changed, 653 insertions(+), 553 deletions(-) diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index fc3f035cc779..0758450dfa7c 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -6,6 +6,10 @@ PyCF_ONLY_AST: Literal[1024] PyCF_TYPE_COMMENTS: Literal[4096] PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +# Alias used for fields that must always be valid identifiers +# A string `x` counts as a valid identifier if both the following are True +# (1) `x.isidentifier()` evaluates to `True` +# (2) `keyword.iskeyword(x)` evaluates to `False` _Identifier: typing_extensions.TypeAlias = str class AST: @@ -499,7 +503,7 @@ class keyword(AST): class alias(AST): if sys.version_info >= (3, 10): __match_args__ = ("name", "asname") - name: _Identifier + name: str asname: _Identifier | None class withitem(AST): diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index adb09a50f47c..20189cb285c5 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -553,5 +553,6 @@ if sys.platform != "win32": major: int minor: int patch: int + ncurses_version: _ncurses_version window = _CursesWindow # undocumented diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi index 160406a6d8d5..22239cbfff04 100644 --- a/mypy/typeshed/stdlib/_msi.pyi +++ b/mypy/typeshed/stdlib/_msi.pyi @@ -45,6 +45,7 @@ if sys.platform == "win32": # Don't exist at runtime __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] + def UuidCreate() -> str: ... def FCICreate(__cabname: str, __files: list[str]) -> None: ... def OpenDatabase(__path: str, __persist: int) -> _Database: ... diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 8b43a81cac8a..ff9bd1a12eb1 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -32,6 +32,7 @@ def get_native_id() -> int: ... # only available on some platforms class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]): if sys.version_info >= (3, 10): __match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread") + @property def exc_type(self) -> type[BaseException]: ... @property diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index 5dcca950e819..04197c8d2978 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -25,6 +25,7 @@ if sys.version_info >= (3, 11): STATE_CON_MADE: str STATE_EOF: str STATE_CON_LOST: str + def add_flowcontrol_defaults(high: int | None, low: int | None, kb: int) -> tuple[int, int]: ... else: @@ -155,9 +156,10 @@ class SSLProtocol(_SSLProtocolBase): def _check_handshake_timeout(self) -> None: ... def _on_handshake_complete(self, handshake_exc: BaseException | None) -> None: ... def _fatal_error(self, exc: BaseException, message: str = "Fatal error on transport") -> None: ... - def _abort(self) -> None: ... if sys.version_info >= (3, 11): + def _abort(self, exc: BaseException | None) -> None: ... def get_buffer(self, n: int) -> memoryview: ... else: + def _abort(self) -> None: ... def _finalize(self) -> None: ... def _process_write_backlog(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index d2a2fef5c33b..2fbc0a4e6049 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -96,6 +96,7 @@ if sys.platform != "win32": def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... + else: class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... @@ -120,6 +121,7 @@ if sys.platform != "win32": else: def get_child_watcher(self) -> AbstractChildWatcher: ... def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + SelectorEventLoop = _UnixSelectorEventLoop DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy @@ -136,6 +138,7 @@ if sys.platform != "win32": def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + else: class MultiLoopChildWatcher(AbstractChildWatcher): def is_active(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index fdf43d3ea91c..9c150ee16beb 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -69,6 +69,7 @@ if sys.platform == "win32": def recvfrom_into( self, conn: socket.socket, buf: WriteableBuffer, flags: int = 0 ) -> futures.Future[tuple[int, socket._RetAddress]]: ... + SelectorEventLoop = _WindowsSelectorEventLoop class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): @@ -80,4 +81,5 @@ if sys.platform == "win32": _loop_factory: ClassVar[type[ProactorEventLoop]] def get_child_watcher(self) -> NoReturn: ... def set_child_watcher(self, watcher: Any) -> NoReturn: ... + DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index cac39a498ac9..5cc49e102fdf 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -124,7 +124,7 @@ class HTMLCalendar(Calendar): def formatyear(self, theyear: int, width: int = 3) -> str: ... def formatyearpage( self, theyear: int, width: int = 3, css: str | None = "calendar.css", encoding: str | None = None - ) -> str: ... + ) -> bytes: ... class different_locale: def __init__(self, locale: _LocaleType) -> None: ... @@ -166,6 +166,7 @@ if sys.version_info >= (3, 12): OCTOBER: Literal[10] NOVEMBER: Literal[11] DECEMBER: Literal[12] + JANUARY = Month.JANUARY FEBRUARY = Month.FEBRUARY MARCH = Month.MARCH @@ -187,6 +188,7 @@ if sys.version_info >= (3, 12): FRIDAY: Literal[4] SATURDAY: Literal[5] SUNDAY: Literal[6] + MONDAY = Day.MONDAY TUESDAY = Day.TUESDAY WEDNESDAY = Day.WEDNESDAY diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index ce46d0d39830..eb4e95b33509 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -118,6 +118,7 @@ class closing(AbstractContextManager[_SupportsCloseT]): if sys.version_info >= (3, 10): class _SupportsAclose(Protocol): def aclose(self) -> Awaitable[object]: ... + _SupportsAcloseT = TypeVar("_SupportsAcloseT", bound=_SupportsAclose) class aclosing(AbstractAsyncContextManager[_SupportsAcloseT]): diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index 3dc66a30c370..0f818ed5e7f5 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -37,4 +37,5 @@ if sys.platform != "win32": # Don't exist at runtime __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] + def open(__filename: str, __flags: str = "r", __mode: int = 0o666) -> _gdbm: ... diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 1106fb2a8e7e..a7a6d52d8f19 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -33,4 +33,5 @@ if sys.platform != "win32": # Don't exist at runtime __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] + def open(__filename: str, __flags: str = "r", __mode: int = 0o666) -> _dbm: ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 42d0c19d39e7..96cb2264ea20 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -175,6 +175,7 @@ if sys.version_info >= (3, 11): name: str clsname: str member: Enum | None + _magic_enum_attr = property else: _magic_enum_attr = types.DynamicClassAttribute @@ -261,6 +262,7 @@ if sys.version_info >= (3, 11): CONTINUOUS: str NAMED_FLAGS: str UNIQUE: str + CONTINUOUS = EnumCheck.CONTINUOUS NAMED_FLAGS = EnumCheck.NAMED_FLAGS UNIQUE = EnumCheck.UNIQUE @@ -274,6 +276,7 @@ if sys.version_info >= (3, 11): CONFORM: str EJECT: str KEEP: str + STRICT = FlagBoundary.STRICT CONFORM = FlagBoundary.CONFORM EJECT = FlagBoundary.EJECT diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 3bc03a0ff121..9e7097ddc56e 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -31,7 +31,7 @@ class FTP: sock: socket | None welcome: str | None passiveserver: int - timeout: int + timeout: float | None af: int lastresp: str file: TextIO | None @@ -48,7 +48,7 @@ class FTP: user: str = "", passwd: str = "", acct: str = "", - timeout: float = ..., + timeout: float | None = ..., source_address: tuple[str, int] | None = None, *, encoding: str = "utf-8", @@ -60,7 +60,7 @@ class FTP: user: str = "", passwd: str = "", acct: str = "", - timeout: float = ..., + timeout: float | None = ..., source_address: tuple[str, int] | None = None, ) -> None: ... @@ -127,7 +127,7 @@ class FTP_TLS(FTP): acct: str = "", *, context: SSLContext | None = None, - timeout: float = ..., + timeout: float | None = ..., source_address: tuple[str, int] | None = None, encoding: str = "utf-8", ) -> None: ... @@ -141,7 +141,7 @@ class FTP_TLS(FTP): keyfile: str | None = None, certfile: str | None = None, context: SSLContext | None = None, - timeout: float = ..., + timeout: float | None = ..., source_address: tuple[str, int] | None = None, *, encoding: str = "utf-8", @@ -156,7 +156,7 @@ class FTP_TLS(FTP): keyfile: str | None = None, certfile: str | None = None, context: SSLContext | None = None, - timeout: float = ..., + timeout: float | None = ..., source_address: tuple[str, int] | None = None, ) -> None: ... ssl_version: int diff --git a/mypy/typeshed/stdlib/grp.pyi b/mypy/typeshed/stdlib/grp.pyi index bb0d65180918..965ecece2a56 100644 --- a/mypy/typeshed/stdlib/grp.pyi +++ b/mypy/typeshed/stdlib/grp.pyi @@ -7,6 +7,7 @@ if sys.platform != "win32": class struct_group(structseq[Any], tuple[str, str | None, int, list[str]]): if sys.version_info >= (3, 10): __match_args__: Final = ("gr_name", "gr_passwd", "gr_gid", "gr_mem") + @property def gr_name(self) -> str: ... @property diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index a936eece1d3f..eb4db39ebf40 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -43,6 +43,7 @@ class PackageNotFoundError(ModuleNotFoundError): if sys.version_info >= (3, 11): class DeprecatedTuple: def __getitem__(self, item: int) -> str: ... + _EntryPointBase = DeprecatedTuple else: class _EntryPointBase(NamedTuple): diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index a26dc67f9945..06a8ff6a3462 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -430,6 +430,7 @@ if sys.version_info < (3, 11): varargs: str | None keywords: str | None defaults: tuple[Any, ...] + def getargspec(func: object) -> ArgSpec: ... class FullArgSpec(NamedTuple): diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index d949971048b0..659b216c43dc 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -6,7 +6,7 @@ from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer from collections.abc import Callable, Iterable, Iterator from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, Literal, TextIO, TypeVar, overload +from typing import IO, Any, BinaryIO, Literal, Protocol, TextIO, TypeVar, overload, type_check_only from typing_extensions import Self __all__ = [ @@ -94,7 +94,10 @@ class BufferedIOBase(IOBase): class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes mode: str - name: FileDescriptorOrPath + # The type of "name" equals the argument passed in to the constructor, + # but that can make FileIO incompatible with other I/O types that assume + # "name" is a str. In the future, making FileIO generic might help. + name: Any def __init__( self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... ) -> None: ... @@ -146,16 +149,43 @@ class TextIOBase(IOBase): def readlines(self, __hint: int = -1) -> list[str]: ... # type: ignore[override] def read(self, __size: int | None = ...) -> str: ... +@type_check_only +class _WrappedBuffer(Protocol): + # "name" is wrapped by TextIOWrapper. Its type is inconsistent between + # the various I/O types, see the comments on TextIOWrapper.name and + # TextIO.name. + @property + def name(self) -> Any: ... + @property + def closed(self) -> bool: ... + def read(self, size: int = ..., /) -> ReadableBuffer: ... + # Optional: def read1(self, size: int, /) -> ReadableBuffer: ... + def write(self, b: bytes, /) -> object: ... + def flush(self) -> object: ... + def close(self) -> object: ... + def seekable(self) -> bool: ... + def readable(self) -> bool: ... + def writable(self) -> bool: ... + def truncate(self, size: int, /) -> int: ... + def fileno(self) -> int: ... + def isatty(self) -> int: ... + # Optional: Only needs to be present if seekable() returns True. + # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... + # def tell(self) -> int: ... + +# TODO: Should be generic over the buffer type, but needs to wait for +# TypeVar defaults. class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes def __init__( self, - buffer: IO[bytes], + buffer: _WrappedBuffer, encoding: str | None = ..., errors: str | None = ..., newline: str | None = ..., line_buffering: bool = ..., write_through: bool = ..., ) -> None: ... + # Equals the "buffer" argument passed in to the constructor. @property def buffer(self) -> BinaryIO: ... @property @@ -180,7 +210,11 @@ class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible d def writelines(self, __lines: Iterable[str]) -> None: ... # type: ignore[override] def readline(self, __size: int = -1) -> str: ... # type: ignore[override] def readlines(self, __hint: int = -1) -> list[str]: ... # type: ignore[override] - def seek(self, __cookie: int, __whence: int = 0) -> int: ... # stubtest needs this + # Equals the "buffer" argument passed in to the constructor. + def detach(self) -> BinaryIO: ... + # TextIOWrapper's version of seek only supports a limited subset of + # operations. + def seek(self, __cookie: int, __whence: int = 0) -> int: ... class StringIO(TextIOWrapper): def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/msilib/__init__.pyi b/mypy/typeshed/stdlib/msilib/__init__.pyi index 106805dab931..3e43cbc44f52 100644 --- a/mypy/typeshed/stdlib/msilib/__init__.pyi +++ b/mypy/typeshed/stdlib/msilib/__init__.pyi @@ -56,6 +56,7 @@ if sys.platform == "win32": def gen_id(self, file: str) -> str: ... def append(self, full: str, file: str, logical: str) -> tuple[int, str]: ... def commit(self, db: _Database) -> None: ... + _directories: set[str] class Directory: diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index ad80169b463c..91532633e1b9 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -86,4 +86,5 @@ class AbstractReducer(metaclass=ABCMeta): sendfds = _sendfds recvfds = _recvfds DupFd = _DupFd + def __init__(self, *args: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 3b277460d8f6..b57678635c07 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -341,6 +341,7 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo # More items may be added at the end by some implementations. if sys.version_info >= (3, 10): __match_args__: Final = ("st_mode", "st_ino", "st_dev", "st_nlink", "st_uid", "st_gid", "st_size") + @property def st_mode(self) -> int: ... # protection bits, @property @@ -446,6 +447,7 @@ class statvfs_result(structseq[int], tuple[int, int, int, int, int, int, int, in "f_flag", "f_namemax", ) + @property def f_bsize(self) -> int: ... @property @@ -488,6 +490,7 @@ def umask(__mask: int) -> int: ... class uname_result(structseq[str], tuple[str, str, str, str, str]): if sys.version_info >= (3, 10): __match_args__: Final = ("sysname", "nodename", "release", "version", "machine") + @property def sysname(self) -> str: ... @property @@ -704,6 +707,7 @@ if sys.platform != "win32": class terminal_size(structseq[int], tuple[int, int]): if sys.version_info >= (3, 10): __match_args__: Final = ("columns", "lines") + @property def columns(self) -> int: ... @property @@ -925,6 +929,7 @@ def system(command: StrOrBytesPath) -> int: ... class times_result(structseq[float], tuple[float, float, float, float, float]): if sys.version_info >= (3, 10): __match_args__: Final = ("user", "system", "children_user", "children_system", "elapsed") + @property def user(self) -> float: ... @property @@ -962,6 +967,7 @@ else: class waitid_result(structseq[int], tuple[int, int, int, int, int]): if sys.version_info >= (3, 10): __match_args__: Final = ("si_pid", "si_uid", "si_signo", "si_status", "si_code") + @property def si_pid(self) -> int: ... @property @@ -1022,6 +1028,7 @@ if sys.platform != "win32": class sched_param(structseq[int], tuple[int]): if sys.version_info >= (3, 10): __match_args__: Final = ("sched_priority",) + def __new__(cls, sched_priority: int) -> Self: ... @property def sched_priority(self) -> int: ... diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index a6ffd54de005..86f88da9e712 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -36,6 +36,7 @@ if sys.version_info >= (3, 9): percall_cumtime: float file_name: str line_number: int + @dataclass(unsafe_hash=True) class StatsProfile: total_tt: float diff --git a/mypy/typeshed/stdlib/pwd.pyi b/mypy/typeshed/stdlib/pwd.pyi index 64e831bcecce..9a8e1036e550 100644 --- a/mypy/typeshed/stdlib/pwd.pyi +++ b/mypy/typeshed/stdlib/pwd.pyi @@ -7,6 +7,7 @@ if sys.platform != "win32": class struct_passwd(structseq[Any], tuple[str, str, int, int, str, str, str]): if sys.version_info >= (3, 10): __match_args__: Final = ("pw_name", "pw_passwd", "pw_uid", "pw_gid", "pw_gecos", "pw_dir", "pw_shell") + @property def pw_name(self) -> str: ... @property diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 92d926ebd332..2188e458474c 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -52,9 +52,12 @@ class XMLParserType: EndDoctypeDeclHandler: Callable[[], Any] | None ElementDeclHandler: Callable[[str, _Model], Any] | None AttlistDeclHandler: Callable[[str, str, str, str | None, bool], Any] | None - StartElementHandler: Callable[[str, dict[str, str]], Any] | Callable[[str, list[str]], Any] | Callable[ - [str, dict[str, str], list[str]], Any - ] | None + StartElementHandler: ( + Callable[[str, dict[str, str]], Any] + | Callable[[str, list[str]], Any] + | Callable[[str, dict[str, str], list[str]], Any] + | None + ) EndElementHandler: Callable[[str], Any] | None ProcessingInstructionHandler: Callable[[str, str], Any] | None CharacterDataHandler: Callable[[str], Any] | None diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi index 31c55111360a..f40e5ec1ea55 100644 --- a/mypy/typeshed/stdlib/resource.pyi +++ b/mypy/typeshed/stdlib/resource.pyi @@ -24,6 +24,7 @@ if sys.platform != "win32": RLIMIT_RTTIME: int RLIMIT_SIGPENDING: int RUSAGE_THREAD: int + @final class struct_rusage( structseq[float], tuple[float, float, int, int, int, int, int, int, int, int, int, int, int, int, int, int] @@ -47,6 +48,7 @@ if sys.platform != "win32": "ru_nvcsw", "ru_nivcsw", ) + @property def ru_utime(self) -> float: ... @property diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index f2cfc881c1da..afab88e18453 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -52,6 +52,7 @@ if sys.platform != "linux" and sys.platform != "win32": data: Any = ..., udata: Any = ..., ) -> None: ... + # BSD only @final class kqueue: @@ -64,6 +65,7 @@ if sys.platform != "linux" and sys.platform != "win32": def fileno(self) -> int: ... @classmethod def fromfd(cls, __fd: FileDescriptorLike) -> kqueue: ... + KQ_EV_ADD: int KQ_EV_CLEAR: int KQ_EV_DELETE: int @@ -123,6 +125,7 @@ if sys.platform == "linux": def poll(self, timeout: float | None = None, maxevents: int = -1) -> list[tuple[int, int]]: ... @classmethod def fromfd(cls, __fd: FileDescriptorLike) -> epoll: ... + EPOLLERR: int EPOLLEXCLUSIVE: int EPOLLET: int diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 910424c01c31..544473df9932 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -126,6 +126,7 @@ else: SIG_BLOCK: int SIG_UNBLOCK: int SIG_SETMASK: int + SIG_BLOCK = Sigmasks.SIG_BLOCK SIG_UNBLOCK = Sigmasks.SIG_UNBLOCK SIG_SETMASK = Sigmasks.SIG_SETMASK @@ -153,10 +154,12 @@ else: SIGRTMIN: Signals if sys.version_info >= (3, 11): SIGSTKFLT: Signals + @final class struct_siginfo(structseq[int], tuple[int, int, int, int, int, int, int]): if sys.version_info >= (3, 10): __match_args__: Final = ("si_signo", "si_code", "si_errno", "si_pid", "si_uid", "si_status", "si_band") + @property def si_signo(self) -> int: ... @property diff --git a/mypy/typeshed/stdlib/spwd.pyi b/mypy/typeshed/stdlib/spwd.pyi index 93dfad3b38cc..d362a0b77573 100644 --- a/mypy/typeshed/stdlib/spwd.pyi +++ b/mypy/typeshed/stdlib/spwd.pyi @@ -17,6 +17,7 @@ if sys.platform != "win32": "sp_expire", "sp_flag", ) + @property def sp_namp(self) -> str: ... @property diff --git a/mypy/typeshed/stdlib/string.pyi b/mypy/typeshed/stdlib/string.pyi index 1a875a071bf5..8b60243f2333 100644 --- a/mypy/typeshed/stdlib/string.pyi +++ b/mypy/typeshed/stdlib/string.pyi @@ -80,4 +80,4 @@ class Formatter: def get_value(self, key: int | str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> Any: ... def check_unused_args(self, used_args: set[int | str], args: Sequence[Any], kwargs: Mapping[str, Any]) -> None: ... def format_field(self, value: Any, format_spec: str) -> Any: ... - def convert_field(self, value: Any, conversion: str) -> Any: ... + def convert_field(self, value: Any, conversion: str | None) -> Any: ... diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index df1db5c82eea..d3302aba5e10 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2564,12 +2564,12 @@ class Popen(Generic[AnyStr]): # The result really is always a str. if sys.version_info >= (3, 11): - def getstatusoutput(cmd: str | bytes, *, encoding: str | None = None, errors: str | None = None) -> tuple[int, str]: ... - def getoutput(cmd: str | bytes, *, encoding: str | None = None, errors: str | None = None) -> str: ... + def getstatusoutput(cmd: _CMD, *, encoding: str | None = None, errors: str | None = None) -> tuple[int, str]: ... + def getoutput(cmd: _CMD, *, encoding: str | None = None, errors: str | None = None) -> str: ... else: - def getstatusoutput(cmd: str | bytes) -> tuple[int, str]: ... - def getoutput(cmd: str | bytes) -> str: ... + def getstatusoutput(cmd: _CMD) -> tuple[int, str]: ... + def getoutput(cmd: _CMD) -> str: ... def list2cmdline(seq: Iterable[StrOrBytesPath]) -> str: ... # undocumented @@ -2592,6 +2592,7 @@ if sys.platform == "win32": wShowWindow: int lpAttributeList: Mapping[str, Any] def copy(self) -> STARTUPINFO: ... + from _winapi import ( ABOVE_NORMAL_PRIORITY_CLASS as ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS as BELOW_NORMAL_PRIORITY_CLASS, diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 2f847498214b..bb1d244bdac9 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -42,6 +42,8 @@ hexversion: int last_type: type[BaseException] | None last_value: BaseException | None last_traceback: TracebackType | None +if sys.version_info >= (3, 12): + last_exc: BaseException # or undefined. maxsize: int maxunicode: int meta_path: list[_MetaPathFinder] diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi index 28752bddc4dd..b7962f0751d6 100644 --- a/mypy/typeshed/stdlib/time.pyi +++ b/mypy/typeshed/stdlib/time.pyi @@ -39,6 +39,7 @@ if sys.version_info >= (3, 9) and sys.platform == "linux": class struct_time(structseq[Any | int], _TimeTuple): if sys.version_info >= (3, 10): __match_args__: Final = ("tm_year", "tm_mon", "tm_mday", "tm_hour", "tm_min", "tm_sec", "tm_wday", "tm_yday", "tm_isdst") + @property def tm_year(self) -> int: ... @property diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index ff876d0bb88c..4733c31b5bae 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -178,13 +178,12 @@ _Compound: TypeAlias = Literal["top", "left", "center", "right", "bottom", "none _Cursor: TypeAlias = str | tuple[str] | tuple[str, str] | tuple[str, str, str] | tuple[str, str, str, str] # example when it's sequence: entry['invalidcommand'] = [entry.register(print), '%P'] _EntryValidateCommand: TypeAlias = str | list[str] | tuple[str, ...] | Callable[[], bool] -_GridIndex: TypeAlias = int | str _ImageSpec: TypeAlias = _Image | str # str can be from e.g. tkinter.image_names() _Relief: TypeAlias = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] # manual page: Tk_GetRelief _ScreenUnits: TypeAlias = str | float # Often the right type instead of int. Manual page: Tk_GetPixels # -xscrollcommand and -yscrollcommand in 'options' manual page _XYScrollCommand: TypeAlias = str | Callable[[float, float], object] -_TakeFocusValue: TypeAlias = int | Literal[""] | Callable[[str], bool | None] # -takefocus in manual page named 'options' +_TakeFocusValue: TypeAlias = bool | Literal[0, 1, ""] | Callable[[str], bool | None] # -takefocus in manual page named 'options' if sys.version_info >= (3, 11): class _VersionInfoType(NamedTuple): @@ -262,16 +261,14 @@ class Event(Generic[_W_co]): def NoDefaultRoot() -> None: ... -_TraceMode: TypeAlias = Literal["array", "read", "write", "unset"] - class Variable: def __init__(self, master: Misc | None = None, value: Incomplete | None = None, name: str | None = None) -> None: ... def set(self, value) -> None: ... initialize = set def get(self): ... - def trace_add(self, mode: _TraceMode, callback: Callable[[str, str, str], object]) -> str: ... - def trace_remove(self, mode: _TraceMode, cbname: str) -> None: ... - def trace_info(self) -> list[tuple[tuple[_TraceMode, ...], str]]: ... + def trace_add(self, mode: Literal["array", "read", "write", "unset"], callback: Callable[[str, str, str], object]) -> str: ... + def trace_remove(self, mode: Literal["array", "read", "write", "unset"], cbname: str) -> None: ... + def trace_info(self) -> list[tuple[tuple[Literal["array", "read", "write", "unset"], ...], str]]: ... @deprecated("use trace_add() instead of trace()") def trace(self, mode, callback): ... @deprecated("use trace_add() instead of trace_variable()") @@ -505,7 +502,7 @@ class Misc: bbox = grid_bbox def grid_columnconfigure( self, - index: _GridIndex | list[int] | tuple[int, ...], + index: int | str | list[int] | tuple[int, ...], cnf: _GridIndexInfo = {}, *, minsize: _ScreenUnits = ..., @@ -515,7 +512,7 @@ class Misc: ) -> _GridIndexInfo | Any: ... # can be None but annoying to check def grid_rowconfigure( self, - index: _GridIndex | list[int] | tuple[int, ...], + index: int | str | list[int] | tuple[int, ...], cnf: _GridIndexInfo = {}, *, minsize: _ScreenUnits = ..., @@ -829,7 +826,7 @@ class Pack: after: Misc = ..., anchor: _Anchor = ..., before: Misc = ..., - expand: int = ..., + expand: bool | Literal[0, 1] = 0, fill: Literal["none", "x", "y", "both"] = ..., side: Literal["left", "right", "top", "bottom"] = ..., ipadx: _ScreenUnits = ..., @@ -949,28 +946,28 @@ class Toplevel(BaseWidget, Wm): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 0, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - class_: str = ..., - colormap: Literal["new", ""] | Misc = ..., - container: bool = ..., - cursor: _Cursor = ..., - height: _ScreenUnits = ..., + border: _ScreenUnits = 0, + borderwidth: _ScreenUnits = 0, + class_: str = "Toplevel", + colormap: Literal["new", ""] | Misc = "", + container: bool = False, + cursor: _Cursor = "", + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: _ScreenUnits = 0, menu: Menu = ..., name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - screen: str = ..., # can't be changed after creating widget - takefocus: _TakeFocusValue = ..., + padx: _ScreenUnits = 0, + pady: _ScreenUnits = 0, + relief: _Relief = "flat", + screen: str = "", # can't be changed after creating widget + takefocus: _TakeFocusValue = 0, use: int = ..., - visual: str | tuple[str, int] = ..., - width: _ScreenUnits = ..., + visual: str | tuple[str, int] = "", + width: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -1006,46 +1003,46 @@ class Button(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: _Anchor = "center", background: str = ..., bd: _ScreenUnits = ..., # same as borderwidth bg: str = ..., # same as background - bitmap: str = ..., + bitmap: str = "", border: _ScreenUnits = ..., # same as borderwidth borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., - default: Literal["normal", "active", "disabled"] = ..., + command: _ButtonCommand = "", + compound: _Compound = "none", + cursor: _Cursor = "", + default: Literal["normal", "active", "disabled"] = "disabled", disabledforeground: str = ..., fg: str = ..., # same as foreground - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., # width and height must be int for buttons containing just text, but # ints are also valid _ScreenUnits - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., - justify: Literal["left", "center", "right"] = ..., + highlightthickness: _ScreenUnits = 1, + image: _ImageSpec = "", + justify: Literal["left", "center", "right"] = "center", name: str = ..., - overrelief: _Relief = ..., + overrelief: _Relief | Literal[""] = "", padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., relief: _Relief = ..., repeatdelay: int = ..., repeatinterval: int = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", + text: float | str = "", # We allow the textvariable to be any Variable, not necessarily # StringVar. This is useful for e.g. a button that displays the value # of an IntVar. textvariable: Variable = ..., - underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + underline: int = -1, + width: _ScreenUnits = 0, + wraplength: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -1075,7 +1072,7 @@ class Button(Widget): highlightthickness: _ScreenUnits = ..., image: _ImageSpec = ..., justify: Literal["left", "center", "right"] = ..., - overrelief: _Relief = ..., + overrelief: _Relief | Literal[""] = ..., padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., relief: _Relief = ..., @@ -1102,13 +1099,13 @@ class Canvas(Widget, XView, YView): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 0, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - closeenough: float = ..., - confine: bool = ..., - cursor: _Cursor = ..., + border: _ScreenUnits = 0, + borderwidth: _ScreenUnits = 0, + closeenough: float = 1.0, + confine: bool = True, + cursor: _Cursor = "", # canvas manual page has a section named COORDINATES, and the first # part of it describes _ScreenUnits. height: _ScreenUnits = ..., @@ -1116,27 +1113,27 @@ class Canvas(Widget, XView, YView): highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., - insertofftime: int = ..., - insertontime: int = ..., - insertwidth: _ScreenUnits = ..., + insertborderwidth: _ScreenUnits = 0, + insertofftime: int = 300, + insertontime: int = 600, + insertwidth: _ScreenUnits = 2, name: str = ..., offset=..., # undocumented - relief: _Relief = ..., + relief: _Relief = "flat", # Setting scrollregion to None doesn't reset it back to empty, # but setting it to () does. - scrollregion: tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits] | tuple[()] = ..., + scrollregion: tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits] | tuple[()] = (), selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: _ScreenUnits = 1, selectforeground: str = ..., # man page says that state can be 'hidden', but it can't - state: Literal["normal", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + state: Literal["normal", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", width: _ScreenUnits = ..., - xscrollcommand: _XYScrollCommand = ..., - xscrollincrement: _ScreenUnits = ..., - yscrollcommand: _XYScrollCommand = ..., - yscrollincrement: _ScreenUnits = ..., + xscrollcommand: _XYScrollCommand = "", + xscrollincrement: _ScreenUnits = 0, + yscrollcommand: _XYScrollCommand = "", + yscrollincrement: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -1732,7 +1729,7 @@ class Canvas(Widget, XView, YView): def select_from(self, tagOrId, index) -> None: ... def select_item(self): ... def select_to(self, tagOrId, index) -> None: ... - def type(self, tagOrId): ... + def type(self, tagOrId: str | int) -> int | None: ... class Checkbutton(Widget): def __init__( @@ -1742,27 +1739,27 @@ class Checkbutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: _Anchor = "center", background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., - bitmap: str = ..., + bitmap: str = "", border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., + command: _ButtonCommand = "", + compound: _Compound = "none", + cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., - indicatoron: bool = ..., - justify: Literal["left", "center", "right"] = ..., + highlightthickness: _ScreenUnits = 1, + image: _ImageSpec = "", + indicatoron: bool = True, + justify: Literal["left", "center", "right"] = "center", name: str = ..., offrelief: _Relief = ..., # The checkbutton puts a value to its variable when it's checked or @@ -1775,24 +1772,24 @@ class Checkbutton(Widget): # and list[int] are incompatible. Also, we would need a way to # specify "Checkbutton not associated with any variable", which is # done by setting variable to empty string (the default). - offvalue: Any = ..., - onvalue: Any = ..., - overrelief: _Relief = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + offvalue: Any = 0, + onvalue: Any = 1, + overrelief: _Relief | Literal[""] = "", + padx: _ScreenUnits = 1, + pady: _ScreenUnits = 1, + relief: _Relief = "flat", selectcolor: str = ..., - selectimage: _ImageSpec = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + selectimage: _ImageSpec = "", + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", + text: float | str = "", textvariable: Variable = ..., - tristateimage: _ImageSpec = ..., - tristatevalue: Any = ..., - underline: int = ..., + tristateimage: _ImageSpec = "", + tristatevalue: Any = "", + underline: int = -1, variable: Variable | Literal[""] = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: _ScreenUnits = 0, + wraplength: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -1825,7 +1822,7 @@ class Checkbutton(Widget): offrelief: _Relief = ..., offvalue: Any = ..., onvalue: Any = ..., - overrelief: _Relief = ..., + overrelief: _Relief | Literal[""] = ..., padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., relief: _Relief = ..., @@ -1851,8 +1848,6 @@ class Checkbutton(Widget): def select(self) -> None: ... def toggle(self) -> None: ... -_EntryIndex: TypeAlias = str | int # "INDICES" in manual page - class Entry(Widget, XView): def __init__( self, @@ -1864,39 +1859,39 @@ class Entry(Widget, XView): bg: str = ..., border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., + cursor: _Cursor = "xterm", disabledbackground: str = ..., disabledforeground: str = ..., - exportselection: bool = ..., + exportselection: bool = True, fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkTextFont", foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., - insertofftime: int = ..., - insertontime: int = ..., + insertborderwidth: _ScreenUnits = 0, + insertofftime: int = 300, + insertontime: int = 600, insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = ..., - invcmd: _EntryValidateCommand = ..., # same as invalidcommand - justify: Literal["left", "center", "right"] = ..., + invalidcommand: _EntryValidateCommand = "", + invcmd: _EntryValidateCommand = "", # same as invalidcommand + justify: Literal["left", "center", "right"] = "left", name: str = ..., readonlybackground: str = ..., - relief: _Relief = ..., + relief: _Relief = "sunken", selectbackground: str = ..., selectborderwidth: _ScreenUnits = ..., selectforeground: str = ..., - show: str = ..., - state: Literal["normal", "disabled", "readonly"] = ..., - takefocus: _TakeFocusValue = ..., + show: str = "", + state: Literal["normal", "disabled", "readonly"] = "normal", + takefocus: _TakeFocusValue = "", textvariable: Variable = ..., - validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: _EntryValidateCommand = ..., - vcmd: _EntryValidateCommand = ..., # same as validatecommand - width: int = ..., - xscrollcommand: _XYScrollCommand = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", + validatecommand: _EntryValidateCommand = "", + vcmd: _EntryValidateCommand = "", # same as validatecommand + width: int = 20, + xscrollcommand: _XYScrollCommand = "", ) -> None: ... @overload def configure( @@ -1944,19 +1939,19 @@ class Entry(Widget, XView): @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure - def delete(self, first: _EntryIndex, last: _EntryIndex | None = None) -> None: ... + def delete(self, first: str | int, last: str | int | None = None) -> None: ... def get(self) -> str: ... - def icursor(self, index: _EntryIndex) -> None: ... - def index(self, index: _EntryIndex) -> int: ... - def insert(self, index: _EntryIndex, string: str) -> None: ... + def icursor(self, index: str | int) -> None: ... + def index(self, index: str | int) -> int: ... + def insert(self, index: str | int, string: str) -> None: ... def scan_mark(self, x) -> None: ... def scan_dragto(self, x) -> None: ... - def selection_adjust(self, index: _EntryIndex) -> None: ... + def selection_adjust(self, index: str | int) -> None: ... def selection_clear(self) -> None: ... # type: ignore[override] - def selection_from(self, index: _EntryIndex) -> None: ... + def selection_from(self, index: str | int) -> None: ... def selection_present(self) -> bool: ... - def selection_range(self, start: _EntryIndex, end: _EntryIndex) -> None: ... - def selection_to(self, index: _EntryIndex) -> None: ... + def selection_range(self, start: str | int, end: str | int) -> None: ... + def selection_to(self, index: str | int) -> None: ... select_adjust = selection_adjust select_clear = selection_clear select_from = selection_from @@ -1971,25 +1966,25 @@ class Frame(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 0, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - class_: str = ..., # can't be changed with configure() - colormap: Literal["new", ""] | Misc = ..., # can't be changed with configure() - container: bool = ..., # can't be changed with configure() - cursor: _Cursor = ..., - height: _ScreenUnits = ..., + border: _ScreenUnits = 0, + borderwidth: _ScreenUnits = 0, + class_: str = "Frame", # can't be changed with configure() + colormap: Literal["new", ""] | Misc = "", # can't be changed with configure() + container: bool = False, # can't be changed with configure() + cursor: _Cursor = "", + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: _ScreenUnits = 0, name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - visual: str | tuple[str, int] = ..., # can't be changed with configure() - width: _ScreenUnits = ..., + padx: _ScreenUnits = 0, + pady: _ScreenUnits = 0, + relief: _Relief = "flat", + takefocus: _TakeFocusValue = 0, + visual: str | tuple[str, int] = "", # can't be changed with configure() + width: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -2024,36 +2019,36 @@ class Label(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: _Anchor = "center", background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., - bitmap: str = ..., + bitmap: str = "", border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., + compound: _Compound = "none", + cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., - justify: Literal["left", "center", "right"] = ..., + highlightthickness: _ScreenUnits = 0, + image: _ImageSpec = "", + justify: Literal["left", "center", "right"] = "center", name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + padx: _ScreenUnits = 1, + pady: _ScreenUnits = 1, + relief: _Relief = "flat", + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = 0, + text: float | str = "", textvariable: Variable = ..., - underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + underline: int = -1, + width: _ScreenUnits = 0, + wraplength: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -2104,21 +2099,21 @@ class Listbox(Widget, XView, YView): *, activestyle: Literal["dotbox", "none", "underline"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 1, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., + border: _ScreenUnits = 1, + borderwidth: _ScreenUnits = 1, + cursor: _Cursor = "", disabledforeground: str = ..., - exportselection: int = ..., + exportselection: bool | Literal[0, 1] = 1, fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: int = ..., + height: int = 10, highlightbackground: str = ..., highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., - justify: Literal["left", "center", "right"] = ..., + justify: Literal["left", "center", "right"] = "left", # There's no tkinter.ListVar, but seems like bare tkinter.Variable # actually works for this: # @@ -2132,20 +2127,20 @@ class Listbox(Widget, XView, YView): name: str = ..., relief: _Relief = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: _ScreenUnits = 0, selectforeground: str = ..., # from listbox man page: "The value of the [selectmode] option may be # arbitrary, but the default bindings expect it to be ..." # # I have never seen anyone setting this to something else than what # "the default bindings expect", but let's support it anyway. - selectmode: str = ..., - setgrid: bool = ..., - state: Literal["normal", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - width: int = ..., - xscrollcommand: _XYScrollCommand = ..., - yscrollcommand: _XYScrollCommand = ..., + selectmode: str = "browse", + setgrid: bool = False, + state: Literal["normal", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", + width: int = 20, + xscrollcommand: _XYScrollCommand = "", + yscrollcommand: _XYScrollCommand = "", ) -> None: ... @overload def configure( @@ -2223,23 +2218,23 @@ class Menu(Widget): bg: str = ..., border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., + cursor: _Cursor = "arrow", disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., name: str = ..., - postcommand: Callable[[], object] | str = ..., + postcommand: Callable[[], object] | str = "", relief: _Relief = ..., selectcolor: str = ..., - takefocus: _TakeFocusValue = ..., - tearoff: int = ..., + takefocus: _TakeFocusValue = 0, + tearoff: bool | Literal[0, 1] = 1, # I guess tearoffcommand arguments are supposed to be widget objects, # but they are widget name strings. Use nametowidget() to handle the # arguments of tearoffcommand. - tearoffcommand: Callable[[str, str], object] | str = ..., - title: str = ..., - type: Literal["menubar", "tearoff", "normal"] = ..., + tearoffcommand: Callable[[str, str], object] | str = "", + title: str = "", + type: Literal["menubar", "tearoff", "normal"] = "normal", ) -> None: ... @overload def configure( @@ -2491,35 +2486,35 @@ class Menubutton(Widget): background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., - bitmap: str = ..., + bitmap: str = "", border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., - direction: Literal["above", "below", "left", "right", "flush"] = ..., + compound: _Compound = "none", + cursor: _Cursor = "", + direction: Literal["above", "below", "left", "right", "flush"] = "below", disabledforeground: str = ..., fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: _ScreenUnits = 0, + image: _ImageSpec = "", indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., menu: Menu = ..., name: str = ..., padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., - relief: _Relief = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + relief: _Relief = "flat", + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = 0, + text: float | str = "", textvariable: Variable = ..., - underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + underline: int = -1, + width: _ScreenUnits = 0, + wraplength: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -2571,30 +2566,30 @@ class Message(Widget): master: Misc | None = None, cnf: dict[str, Any] | None = {}, *, - anchor: _Anchor = ..., - aspect: int = ..., + anchor: _Anchor = "center", + aspect: int = 150, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 1, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., + border: _ScreenUnits = 1, + borderwidth: _ScreenUnits = 1, + cursor: _Cursor = "", fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - justify: Literal["left", "center", "right"] = ..., + highlightthickness: _ScreenUnits = 0, + justify: Literal["left", "center", "right"] = "left", name: str = ..., padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + relief: _Relief = "flat", + takefocus: _TakeFocusValue = 0, + text: float | str = "", textvariable: Variable = ..., # there's width but no height - width: _ScreenUnits = ..., + width: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -2636,46 +2631,46 @@ class Radiobutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: _Anchor = "center", background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., - bitmap: str = ..., + bitmap: str = "", border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., + command: _ButtonCommand = "", + compound: _Compound = "none", + cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., - indicatoron: bool = ..., - justify: Literal["left", "center", "right"] = ..., + highlightthickness: _ScreenUnits = 1, + image: _ImageSpec = "", + indicatoron: bool = True, + justify: Literal["left", "center", "right"] = "center", name: str = ..., offrelief: _Relief = ..., - overrelief: _Relief = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + overrelief: _Relief | Literal[""] = "", + padx: _ScreenUnits = 1, + pady: _ScreenUnits = 1, + relief: _Relief = "flat", selectcolor: str = ..., - selectimage: _ImageSpec = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., + selectimage: _ImageSpec = "", + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", + text: float | str = "", textvariable: Variable = ..., - tristateimage: _ImageSpec = ..., - tristatevalue: Any = ..., - underline: int = ..., - value: Any = ..., + tristateimage: _ImageSpec = "", + tristatevalue: Any = "", + underline: int = -1, + value: Any = "", variable: Variable | Literal[""] = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: _ScreenUnits = 0, + wraplength: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -2706,7 +2701,7 @@ class Radiobutton(Widget): indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., offrelief: _Relief = ..., - overrelief: _Relief = ..., + overrelief: _Relief | Literal[""] = ..., padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., relief: _Relief = ..., @@ -2740,40 +2735,40 @@ class Scale(Widget): *, activebackground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 1, bg: str = ..., - bigincrement: float = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + bigincrement: float = 0.0, + border: _ScreenUnits = 1, + borderwidth: _ScreenUnits = 1, # don't know why the callback gets string instead of float - command: str | Callable[[str], object] = ..., - cursor: _Cursor = ..., - digits: int = ..., + command: str | Callable[[str], object] = "", + cursor: _Cursor = "", + digits: int = 0, fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - from_: float = ..., + from_: float = 0.0, highlightbackground: str = ..., highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., - label: str = ..., - length: _ScreenUnits = ..., + label: str = "", + length: _ScreenUnits = 100, name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., - relief: _Relief = ..., - repeatdelay: int = ..., - repeatinterval: int = ..., - resolution: float = ..., - showvalue: bool = ..., - sliderlength: _ScreenUnits = ..., - sliderrelief: _Relief = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - tickinterval: float = ..., - to: float = ..., + orient: Literal["horizontal", "vertical"] = "vertical", + relief: _Relief = "flat", + repeatdelay: int = 300, + repeatinterval: int = 100, + resolution: float = 1.0, + showvalue: bool = True, + sliderlength: _ScreenUnits = 30, + sliderrelief: _Relief = "raised", + state: Literal["normal", "active", "disabled"] = "normal", + takefocus: _TakeFocusValue = "", + tickinterval: float = 0.0, + to: float = 100.0, troughcolor: str = ..., variable: IntVar | DoubleVar = ..., - width: _ScreenUnits = ..., + width: _ScreenUnits = 15, ) -> None: ... @overload def configure( @@ -2830,7 +2825,7 @@ class Scrollbar(Widget): cnf: dict[str, Any] | None = {}, *, activebackground: str = ..., - activerelief: _Relief = ..., + activerelief: _Relief = "raised", background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., @@ -2840,19 +2835,19 @@ class Scrollbar(Widget): # 'SCROLLING COMMANDS' in scrollbar man page. There doesn't seem to # be any way to specify an overloaded callback function, so we say # that it can take any args while it can't in reality. - command: Callable[..., tuple[float, float] | None] | str = ..., - cursor: _Cursor = ..., - elementborderwidth: _ScreenUnits = ..., + command: Callable[..., tuple[float, float] | None] | str = "", + cursor: _Cursor = "", + elementborderwidth: _ScreenUnits = -1, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - jump: bool = ..., + highlightthickness: _ScreenUnits = 0, + jump: bool = False, name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., + orient: Literal["horizontal", "vertical"] = "vertical", relief: _Relief = ..., - repeatdelay: int = ..., - repeatinterval: int = ..., - takefocus: _TakeFocusValue = ..., + repeatdelay: int = 300, + repeatinterval: int = 100, + takefocus: _TakeFocusValue = "", troughcolor: str = ..., width: _ScreenUnits = ..., ) -> None: ... @@ -2901,56 +2896,56 @@ class Text(Widget, XView, YView): master: Misc | None = None, cnf: dict[str, Any] | None = {}, *, - autoseparators: bool = ..., + autoseparators: bool = True, background: str = ..., bd: _ScreenUnits = ..., bg: str = ..., - blockcursor: bool = ..., + blockcursor: bool = False, border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., - endline: int | Literal[""] = ..., - exportselection: bool = ..., + cursor: _Cursor = "xterm", + endline: int | Literal[""] = "", + exportselection: bool = True, fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkFixedFont", foreground: str = ..., # width is always int, but height is allowed to be ScreenUnits. # This doesn't make any sense to me, and this isn't documented. # The docs seem to say that both should be integers. - height: _ScreenUnits = ..., + height: _ScreenUnits = 24, highlightbackground: str = ..., highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., inactiveselectbackground: str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., - insertofftime: int = ..., - insertontime: int = ..., - insertunfocussed: Literal["none", "hollow", "solid"] = ..., + insertborderwidth: _ScreenUnits = 0, + insertofftime: int = 300, + insertontime: int = 600, + insertunfocussed: Literal["none", "hollow", "solid"] = "none", insertwidth: _ScreenUnits = ..., - maxundo: int = ..., + maxundo: int = 0, name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., + padx: _ScreenUnits = 1, + pady: _ScreenUnits = 1, relief: _Relief = ..., selectbackground: str = ..., selectborderwidth: _ScreenUnits = ..., selectforeground: str = ..., - setgrid: bool = ..., - spacing1: _ScreenUnits = ..., - spacing2: _ScreenUnits = ..., - spacing3: _ScreenUnits = ..., - startline: int | Literal[""] = ..., - state: Literal["normal", "disabled"] = ..., + setgrid: bool = False, + spacing1: _ScreenUnits = 0, + spacing2: _ScreenUnits = 0, + spacing3: _ScreenUnits = 0, + startline: int | Literal[""] = "", + state: Literal["normal", "disabled"] = "normal", # Literal inside Tuple doesn't actually work - tabs: _ScreenUnits | str | tuple[_ScreenUnits | str, ...] = ..., - tabstyle: Literal["tabular", "wordprocessor"] = ..., - takefocus: _TakeFocusValue = ..., - undo: bool = ..., - width: int = ..., - wrap: Literal["none", "char", "word"] = ..., - xscrollcommand: _XYScrollCommand = ..., - yscrollcommand: _XYScrollCommand = ..., + tabs: _ScreenUnits | str | tuple[_ScreenUnits | str, ...] = "", + tabstyle: Literal["tabular", "wordprocessor"] = "tabular", + takefocus: _TakeFocusValue = "", + undo: bool = False, + width: int = 80, + wrap: Literal["none", "char", "word"] = "char", + xscrollcommand: _XYScrollCommand = "", + yscrollcommand: _XYScrollCommand = "", ) -> None: ... @overload def configure( @@ -3371,51 +3366,51 @@ class Spinbox(Widget, XView): border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., buttonbackground: str = ..., - buttoncursor: _Cursor = ..., + buttoncursor: _Cursor = "", buttondownrelief: _Relief = ..., buttonuprelief: _Relief = ..., # percent substitutions don't seem to be supported, it's similar to Entry's validation stuff - command: Callable[[], object] | str | list[str] | tuple[str, ...] = ..., - cursor: _Cursor = ..., + command: Callable[[], object] | str | list[str] | tuple[str, ...] = "", + cursor: _Cursor = "xterm", disabledbackground: str = ..., disabledforeground: str = ..., - exportselection: bool = ..., + exportselection: bool = True, fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkTextFont", foreground: str = ..., - format: str = ..., - from_: float = ..., + format: str = "", + from_: float = 0.0, highlightbackground: str = ..., highlightcolor: str = ..., highlightthickness: _ScreenUnits = ..., - increment: float = ..., + increment: float = 1.0, insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., - insertofftime: int = ..., - insertontime: int = ..., + insertborderwidth: _ScreenUnits = 0, + insertofftime: int = 300, + insertontime: int = 600, insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = ..., - invcmd: _EntryValidateCommand = ..., - justify: Literal["left", "center", "right"] = ..., + invalidcommand: _EntryValidateCommand = "", + invcmd: _EntryValidateCommand = "", + justify: Literal["left", "center", "right"] = "left", name: str = ..., readonlybackground: str = ..., - relief: _Relief = ..., - repeatdelay: int = ..., - repeatinterval: int = ..., + relief: _Relief = "sunken", + repeatdelay: int = 400, + repeatinterval: int = 100, selectbackground: str = ..., selectborderwidth: _ScreenUnits = ..., selectforeground: str = ..., - state: Literal["normal", "disabled", "readonly"] = ..., - takefocus: _TakeFocusValue = ..., + state: Literal["normal", "disabled", "readonly"] = "normal", + takefocus: _TakeFocusValue = "", textvariable: Variable = ..., - to: float = ..., - validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: _EntryValidateCommand = ..., - vcmd: _EntryValidateCommand = ..., + to: float = 0.0, + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", + validatecommand: _EntryValidateCommand = "", + vcmd: _EntryValidateCommand = "", values: list[str] | tuple[str, ...] = ..., - width: int = ..., - wrap: bool = ..., - xscrollcommand: _XYScrollCommand = ..., + width: int = 20, + wrap: bool = False, + xscrollcommand: _XYScrollCommand = "", ) -> None: ... @overload def configure( @@ -3481,8 +3476,8 @@ class Spinbox(Widget, XView): def get(self) -> str: ... def icursor(self, index): ... def identify(self, x: int, y: int) -> Literal["", "buttondown", "buttonup", "entry"]: ... - def index(self, index: _EntryIndex) -> int: ... - def insert(self, index: _EntryIndex, s: str) -> Literal[""]: ... + def index(self, index: str | int) -> int: ... + def insert(self, index: str | int, s: str) -> Literal[""]: ... # spinbox.invoke("asdf") gives error mentioning .invoke("none"), but it's not documented def invoke(self, element: Literal["none", "buttonup", "buttondown"]) -> Literal[""]: ... def scan(self, *args): ... @@ -3504,32 +3499,32 @@ class LabelFrame(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 2, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - class_: str = ..., # can't be changed with configure() - colormap: Literal["new", ""] | Misc = ..., # can't be changed with configure() - container: bool = ..., # undocumented, can't be changed with configure() - cursor: _Cursor = ..., + border: _ScreenUnits = 2, + borderwidth: _ScreenUnits = 2, + class_: str = "Labelframe", # can't be changed with configure() + colormap: Literal["new", ""] | Misc = "", # can't be changed with configure() + container: bool = False, # undocumented, can't be changed with configure() + cursor: _Cursor = "", fg: str = ..., - font: _FontDescription = ..., + font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = ..., + height: _ScreenUnits = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: _ScreenUnits = 0, # 'ne' and 'en' are valid labelanchors, but only 'ne' is a valid _Anchor. - labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., + labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = "nw", labelwidget: Misc = ..., name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - text: float | str = ..., - visual: str | tuple[str, int] = ..., # can't be changed with configure() - width: _ScreenUnits = ..., + padx: _ScreenUnits = 0, + pady: _ScreenUnits = 0, + relief: _Relief = "groove", + takefocus: _TakeFocusValue = 0, + text: float | str = "", + visual: str | tuple[str, int] = "", # can't be changed with configure() + width: _ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -3569,27 +3564,27 @@ class PanedWindow(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: _ScreenUnits = 1, bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - cursor: _Cursor = ..., - handlepad: _ScreenUnits = ..., - handlesize: _ScreenUnits = ..., - height: _ScreenUnits = ..., + border: _ScreenUnits = 1, + borderwidth: _ScreenUnits = 1, + cursor: _Cursor = "", + handlepad: _ScreenUnits = 8, + handlesize: _ScreenUnits = 8, + height: _ScreenUnits = "", name: str = ..., - opaqueresize: bool = ..., - orient: Literal["horizontal", "vertical"] = ..., - proxybackground: str = ..., - proxyborderwidth: _ScreenUnits = ..., - proxyrelief: _Relief = ..., - relief: _Relief = ..., - sashcursor: _Cursor = ..., - sashpad: _ScreenUnits = ..., - sashrelief: _Relief = ..., - sashwidth: _ScreenUnits = ..., - showhandle: bool = ..., - width: _ScreenUnits = ..., + opaqueresize: bool = True, + orient: Literal["horizontal", "vertical"] = "horizontal", + proxybackground: str = "", + proxyborderwidth: _ScreenUnits = 2, + proxyrelief: _Relief = "flat", + relief: _Relief = "flat", + sashcursor: _Cursor = "", + sashpad: _ScreenUnits = 0, + sashrelief: _Relief = "flat", + sashwidth: _ScreenUnits = 3, + showhandle: bool = False, + width: _ScreenUnits = "", ) -> None: ... @overload def configure( diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index ac5accb73d9f..f1b132b33657 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -46,7 +46,7 @@ _Padding: TypeAlias = ( ) # from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound -_TtkCompound: TypeAlias = Literal["text", "image", tkinter._Compound] +_TtkCompound: TypeAlias = Literal["", "text", "image", tkinter._Compound] class Style: master: Incomplete @@ -78,21 +78,21 @@ class Button(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., - default: Literal["normal", "active", "disabled"] = ..., - image: tkinter._ImageSpec = ..., + class_: str = "", + command: tkinter._ButtonCommand = "", + compound: _TtkCompound = "", + cursor: tkinter._Cursor = "", + default: Literal["normal", "active", "disabled"] = "normal", + image: tkinter._ImageSpec = "", name: str = ..., padding=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., + text: float | str = "", textvariable: tkinter.Variable = ..., - underline: int = ..., - width: int | Literal[""] = ..., + underline: int = -1, + width: int | Literal[""] = "", ) -> None: ... @overload def configure( @@ -123,26 +123,26 @@ class Checkbutton(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., - image: tkinter._ImageSpec = ..., + class_: str = "", + command: tkinter._ButtonCommand = "", + compound: _TtkCompound = "", + cursor: tkinter._Cursor = "", + image: tkinter._ImageSpec = "", name: str = ..., - offvalue: Any = ..., - onvalue: Any = ..., + offvalue: Any = 0, + onvalue: Any = 1, padding=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., + text: float | str = "", textvariable: tkinter.Variable = ..., - underline: int = ..., + underline: int = -1, # Seems like variable can be empty string, but actually setting it to # empty string segfaults before Tcl 8.6.9. Search for ttk::checkbutton # here: https://sourceforge.net/projects/tcl/files/Tcl/8.6.9/tcltk-release-notes-8.6.9.txt/view variable: tkinter.Variable = ..., - width: int | Literal[""] = ..., + width: int | Literal[""] = "", ) -> None: ... @overload def configure( @@ -177,23 +177,23 @@ class Entry(Widget, tkinter.Entry): widget: str | None = None, *, background: str = ..., # undocumented - class_: str = ..., + class_: str = "", cursor: tkinter._Cursor = ..., - exportselection: bool = ..., - font: _FontDescription = ..., - foreground: str = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., - justify: Literal["left", "center", "right"] = ..., + exportselection: bool = True, + font: _FontDescription = "TkTextFont", + foreground: str = "", + invalidcommand: tkinter._EntryValidateCommand = "", + justify: Literal["left", "center", "right"] = "left", name: str = ..., - show: str = ..., - state: str = ..., - style: str = ..., + show: str = "", + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., - validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., - width: int = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", + validatecommand: tkinter._EntryValidateCommand = "", + width: int = 20, + xscrollcommand: tkinter._XYScrollCommand = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -254,25 +254,25 @@ class Combobox(Entry): master: tkinter.Misc | None = None, *, background: str = ..., # undocumented - class_: str = ..., - cursor: tkinter._Cursor = ..., - exportselection: bool = ..., + class_: str = "", + cursor: tkinter._Cursor = "", + exportselection: bool = True, font: _FontDescription = ..., # undocumented foreground: str = ..., # undocumented - height: int = ..., + height: int = 10, invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented - justify: Literal["left", "center", "right"] = ..., + justify: Literal["left", "center", "right"] = "left", name: str = ..., - postcommand: Callable[[], object] | str = ..., + postcommand: Callable[[], object] | str = "", show=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., # undocumented validatecommand: tkinter._EntryValidateCommand = ..., # undocumented values: list[str] | tuple[str, ...] = ..., - width: int = ..., + width: int = 20, xscrollcommand: tkinter._XYScrollCommand = ..., # undocumented ) -> None: ... @overload # type: ignore[override] @@ -334,21 +334,23 @@ class Combobox(Entry): def set(self, value: Any) -> None: ... class Frame(Widget): + # This should be kept in sync with tkinter.ttk.LabeledScale.__init__() + # (all of these keyword-only arguments are also present there) def __init__( self, master: tkinter.Misc | None = None, *, border: tkinter._ScreenUnits = ..., borderwidth: tkinter._ScreenUnits = ..., - class_: str = ..., - cursor: tkinter._Cursor = ..., - height: tkinter._ScreenUnits = ..., + class_: str = "", + cursor: tkinter._Cursor = "", + height: tkinter._ScreenUnits = 0, name: str = ..., padding: _Padding = ..., relief: tkinter._Relief = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - width: tkinter._ScreenUnits = ..., + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + width: tkinter._ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -375,26 +377,26 @@ class Label(Widget): master: tkinter.Misc | None = None, *, anchor: tkinter._Anchor = ..., - background: str = ..., + background: str = "", border: tkinter._ScreenUnits = ..., # alias for borderwidth borderwidth: tkinter._ScreenUnits = ..., # undocumented - class_: str = ..., - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., + class_: str = "", + compound: _TtkCompound = "", + cursor: tkinter._Cursor = "", font: _FontDescription = ..., - foreground: str = ..., - image: tkinter._ImageSpec = ..., + foreground: str = "", + image: tkinter._ImageSpec = "", justify: Literal["left", "center", "right"] = ..., name: str = ..., padding: _Padding = ..., relief: tkinter._Relief = ..., - state: str = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., + state: str = "normal", + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + text: float | str = "", textvariable: tkinter.Variable = ..., - underline: int = ..., - width: int | Literal[""] = ..., + underline: int = -1, + width: int | Literal[""] = "", wraplength: tkinter._ScreenUnits = ..., ) -> None: ... @overload @@ -434,19 +436,19 @@ class Labelframe(Widget): *, border: tkinter._ScreenUnits = ..., borderwidth: tkinter._ScreenUnits = ..., # undocumented - class_: str = ..., - cursor: tkinter._Cursor = ..., - height: tkinter._ScreenUnits = ..., + class_: str = "", + cursor: tkinter._Cursor = "", + height: tkinter._ScreenUnits = 0, labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: tkinter.Misc = ..., name: str = ..., padding: _Padding = ..., relief: tkinter._Relief = ..., # undocumented - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., - underline: int = ..., - width: tkinter._ScreenUnits = ..., + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + text: float | str = "", + underline: int = -1, + width: tkinter._ScreenUnits = 0, ) -> None: ... @overload def configure( @@ -478,21 +480,21 @@ class Menubutton(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., - direction: Literal["above", "below", "left", "right", "flush"] = ..., - image: tkinter._ImageSpec = ..., + class_: str = "", + compound: _TtkCompound = "", + cursor: tkinter._Cursor = "", + direction: Literal["above", "below", "left", "right", "flush"] = "below", + image: tkinter._ImageSpec = "", menu: tkinter.Menu = ..., name: str = ..., padding=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., + text: float | str = "", textvariable: tkinter.Variable = ..., - underline: int = ..., - width: int | Literal[""] = ..., + underline: int = -1, + width: int | Literal[""] = "", ) -> None: ... @overload def configure( @@ -522,14 +524,14 @@ class Notebook(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - cursor: tkinter._Cursor = ..., - height: int = ..., + class_: str = "", + cursor: tkinter._Cursor = "", + height: int = 0, name: str = ..., padding: _Padding = ..., - style: str = ..., + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - width: int = ..., + width: int = 0, ) -> None: ... @overload def configure( @@ -573,15 +575,15 @@ class Panedwindow(Widget, tkinter.PanedWindow): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - cursor: tkinter._Cursor = ..., + class_: str = "", + cursor: tkinter._Cursor = "", # width and height for tkinter.ttk.Panedwindow are int but for tkinter.PanedWindow they are screen units - height: int = ..., + height: int = 0, name: str = ..., - orient: Literal["vertical", "horizontal"] = ..., # can't be changed with configure() - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - width: int = ..., + orient: Literal["vertical", "horizontal"] = "vertical", # can't be changed with configure() + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + width: int = 0, ) -> None: ... def add(self, child: tkinter.Widget, *, weight: int = ..., **kw) -> None: ... @overload # type: ignore[override] @@ -623,17 +625,17 @@ class Progressbar(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - cursor: tkinter._Cursor = ..., - length: tkinter._ScreenUnits = ..., - maximum: float = ..., - mode: Literal["determinate", "indeterminate"] = ..., + class_: str = "", + cursor: tkinter._Cursor = "", + length: tkinter._ScreenUnits = 100, + maximum: float = 100, + mode: Literal["determinate", "indeterminate"] = "determinate", name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., - phase: int = ..., # docs say read-only but assigning int to this works - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - value: float = ..., + orient: Literal["horizontal", "vertical"] = "horizontal", + phase: int = 0, # docs say read-only but assigning int to this works + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + value: float = 0.0, variable: tkinter.IntVar | tkinter.DoubleVar = ..., ) -> None: ... @overload @@ -664,22 +666,22 @@ class Radiobutton(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., - image: tkinter._ImageSpec = ..., + class_: str = "", + command: tkinter._ButtonCommand = "", + compound: _TtkCompound = "", + cursor: tkinter._Cursor = "", + image: tkinter._ImageSpec = "", name: str = ..., padding=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - text: float | str = ..., + text: float | str = "", textvariable: tkinter.Variable = ..., - underline: int = ..., - value: Any = ..., + underline: int = -1, + value: Any = "1", variable: tkinter.Variable | Literal[""] = ..., - width: int | Literal[""] = ..., + width: int | Literal[""] = "", ) -> None: ... @overload def configure( @@ -712,18 +714,18 @@ class Scale(Widget, tkinter.Scale): # type: ignore[misc] self, master: tkinter.Misc | None = None, *, - class_: str = ..., - command: str | Callable[[str], object] = ..., - cursor: tkinter._Cursor = ..., - from_: float = ..., - length: tkinter._ScreenUnits = ..., + class_: str = "", + command: str | Callable[[str], object] = "", + cursor: tkinter._Cursor = "", + from_: float = 0, + length: tkinter._ScreenUnits = 100, name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., + orient: Literal["horizontal", "vertical"] = "horizontal", state: str = ..., # undocumented - style: str = ..., + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - to: float = ..., - value: float = ..., + to: float = 1.0, + value: float = 0, variable: tkinter.IntVar | tkinter.DoubleVar = ..., ) -> None: ... @overload # type: ignore[override] @@ -773,13 +775,13 @@ class Scrollbar(Widget, tkinter.Scrollbar): # type: ignore[misc] self, master: tkinter.Misc | None = None, *, - class_: str = ..., - command: Callable[..., tuple[float, float] | None] | str = ..., - cursor: tkinter._Cursor = ..., + class_: str = "", + command: Callable[..., tuple[float, float] | None] | str = "", + cursor: tkinter._Cursor = "", name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + orient: Literal["horizontal", "vertical"] = "vertical", + style: str = "", + takefocus: tkinter._TakeFocusValue = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -814,12 +816,12 @@ class Separator(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - cursor: tkinter._Cursor = ..., + class_: str = "", + cursor: tkinter._Cursor = "", name: str = ..., - orient: Literal["horizontal", "vertical"] = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + orient: Literal["horizontal", "vertical"] = "horizontal", + style: str = "", + takefocus: tkinter._TakeFocusValue = "", ) -> None: ... @overload def configure( @@ -840,11 +842,11 @@ class Sizegrip(Widget): self, master: tkinter.Misc | None = None, *, - class_: str = ..., + class_: str = "", cursor: tkinter._Cursor = ..., name: str = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + style: str = "", + takefocus: tkinter._TakeFocusValue = "", ) -> None: ... @overload def configure( @@ -865,30 +867,30 @@ class Spinbox(Entry): master: tkinter.Misc | None = None, *, background: str = ..., # undocumented - class_: str = ..., - command: Callable[[], object] | str | list[str] | tuple[str, ...] = ..., - cursor: tkinter._Cursor = ..., + class_: str = "", + command: Callable[[], object] | str | list[str] | tuple[str, ...] = "", + cursor: tkinter._Cursor = "", exportselection: bool = ..., # undocumented font: _FontDescription = ..., # undocumented foreground: str = ..., # undocumented - format: str = ..., - from_: float = ..., - increment: float = ..., + format: str = "", + from_: float = 0, + increment: float = 1, invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented justify: Literal["left", "center", "right"] = ..., # undocumented name: str = ..., show=..., # undocumented - state: str = ..., - style: str = ..., + state: str = "normal", + style: str = "", takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., # undocumented - to: float = ..., - validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + to: float = 0, + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", + validatecommand: tkinter._EntryValidateCommand = "", values: list[str] | tuple[str, ...] = ..., width: int = ..., # undocumented - wrap: bool = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + wrap: bool = False, + xscrollcommand: tkinter._XYScrollCommand = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -957,23 +959,23 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): self, master: tkinter.Misc | None = None, *, - class_: str = ..., - columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = ..., - cursor: tkinter._Cursor = ..., - displaycolumns: str | int | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ..., - height: int = ..., + class_: str = "", + columns: str | list[str] | list[int] | list[str | int] | tuple[str | int, ...] = "", + cursor: tkinter._Cursor = "", + displaycolumns: str | int | list[str] | tuple[str, ...] | list[int] | tuple[int, ...] = ("#all",), + height: int = 10, name: str = ..., padding: _Padding = ..., - selectmode: Literal["extended", "browse", "none"] = ..., + selectmode: Literal["extended", "browse", "none"] = "extended", # list/tuple of Literal don't actually work in mypy # # 'tree headings' is same as ['tree', 'headings'], and I wouldn't be # surprised if someone is using it. - show: Literal["tree", "headings", "tree headings", ""] | list[str] | tuple[str, ...] = ..., - style: str = ..., + show: Literal["tree", "headings", "tree headings", ""] | list[str] | tuple[str, ...] = ("tree", "headings"), + style: str = "", takefocus: tkinter._TakeFocusValue = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., - yscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: tkinter._XYScrollCommand = "", + yscrollcommand: tkinter._XYScrollCommand = "", ) -> None: ... @overload def configure( @@ -1158,9 +1160,10 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): def tag_has(self, tagname: str, item: str | int) -> bool: ... class LabeledScale(Frame): - label: Incomplete - scale: Incomplete - # TODO: don't any-type **kw. That goes to Frame.__init__. + label: Label + scale: Scale + # This should be kept in sync with tkinter.ttk.Frame.__init__() + # (all the keyword-only args except compound are from there) def __init__( self, master: tkinter.Misc | None = None, @@ -1168,8 +1171,18 @@ class LabeledScale(Frame): from_: float = 0, to: float = 10, *, - compound: Literal["top", "bottom"] = ..., - **kw, + border: tkinter._ScreenUnits = ..., + borderwidth: tkinter._ScreenUnits = ..., + class_: str = "", + compound: Literal["top", "bottom"] = "top", + cursor: tkinter._Cursor = "", + height: tkinter._ScreenUnits = 0, + name: str = ..., + padding: _Padding = ..., + relief: tkinter._Relief = ..., + style: str = "", + takefocus: tkinter._TakeFocusValue = "", + width: tkinter._ScreenUnits = 0, ) -> None: ... # destroy is overridden, signature does not change value: Any @@ -1177,15 +1190,15 @@ class LabeledScale(Frame): class OptionMenu(Menubutton): def __init__( self, - master, - variable, + master: tkinter.Misc | None, + variable: tkinter.StringVar, default: str | None = None, *values: str, # rest of these are keyword-only because *args syntax used above - style: str = ..., - direction: Literal["above", "below", "left", "right", "flush"] = ..., - command: Callable[[tkinter.StringVar], object] | None = ..., + style: str = "", + direction: Literal["above", "below", "left", "right", "flush"] = "below", + command: Callable[[tkinter.StringVar], object] | None = None, ) -> None: ... # configure, config, cget, destroy are inherited from Menubutton # destroy and __setitem__ are overridden, signature does not change - def set_menu(self, default: Incomplete | None = None, *values) -> None: ... + def set_menu(self, default: str | None = None, *values: str) -> None: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 9fef9d3922f5..5d01be539016 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -273,6 +273,7 @@ if sys.version_info >= (3, 10): def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... + Concatenate: _SpecialForm TypeAlias: _SpecialForm TypeGuard: _SpecialForm @@ -864,6 +865,7 @@ class NamedTuple(tuple[Any, ...]): # So we only add it to the stub on 3.12+. if sys.version_info >= (3, 12): __orig_bases__: ClassVar[tuple[Any, ...]] + @overload def __init__(self, __typename: str, __fields: Iterable[tuple[str, Any]]) -> None: ... @overload @@ -885,6 +887,7 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): # so we only add it to the stub on 3.12+ if sys.version_info >= (3, 12): __orig_bases__: ClassVar[tuple[Any, ...]] + def copy(self) -> typing_extensions.Self: ... # Using Never so that only calls using mypy plugin hook that specialize the signature # can go through. diff --git a/mypy/typeshed/stdlib/unicodedata.pyi b/mypy/typeshed/stdlib/unicodedata.pyi index 35ab6d609a19..5c6749c8a1ae 100644 --- a/mypy/typeshed/stdlib/unicodedata.pyi +++ b/mypy/typeshed/stdlib/unicodedata.pyi @@ -11,6 +11,8 @@ if sys.version_info < (3, 10): _T = TypeVar("_T") +_NormalizationForm: TypeAlias = Literal["NFC", "NFD", "NFKC", "NFKD"] + def bidirectional(__chr: str) -> str: ... def category(__chr: str) -> str: ... def combining(__chr: str) -> int: ... @@ -27,14 +29,14 @@ def digit(__chr: str, __default: _T) -> int | _T: ... _EastAsianWidth: TypeAlias = Literal["F", "H", "W", "Na", "A", "N"] def east_asian_width(__chr: str) -> _EastAsianWidth: ... -def is_normalized(__form: str, __unistr: str) -> bool: ... +def is_normalized(__form: _NormalizationForm, __unistr: str) -> bool: ... def lookup(__name: str | ReadOnlyBuffer) -> str: ... def mirrored(__chr: str) -> int: ... @overload def name(__chr: str) -> str: ... @overload def name(__chr: str, __default: _T) -> str | _T: ... -def normalize(__form: str, __unistr: str) -> str: ... +def normalize(__form: _NormalizationForm, __unistr: str) -> str: ... @overload def numeric(__chr: str) -> float: ... @overload @@ -57,14 +59,14 @@ class UCD: @overload def digit(self, __chr: str, __default: _T) -> int | _T: ... def east_asian_width(self, __chr: str) -> _EastAsianWidth: ... - def is_normalized(self, __form: str, __unistr: str) -> bool: ... + def is_normalized(self, __form: _NormalizationForm, __unistr: str) -> bool: ... def lookup(self, __name: str | ReadOnlyBuffer) -> str: ... def mirrored(self, __chr: str) -> int: ... @overload def name(self, __chr: str) -> str: ... @overload def name(self, __chr: str, __default: _T) -> str | _T: ... - def normalize(self, __form: str, __unistr: str) -> str: ... + def normalize(self, __form: _NormalizationForm, __unistr: str) -> str: ... @overload def numeric(self, __chr: str) -> float: ... @overload diff --git a/mypy/typeshed/stdlib/unittest/result.pyi b/mypy/typeshed/stdlib/unittest/result.pyi index dfc505936f59..436fabf20c65 100644 --- a/mypy/typeshed/stdlib/unittest/result.pyi +++ b/mypy/typeshed/stdlib/unittest/result.pyi @@ -27,6 +27,7 @@ class TestResult: tb_locals: bool if sys.version_info >= (3, 12): collectedDurations: _DurationsType + def __init__(self, stream: TextIO | None = None, descriptions: bool | None = None, verbosity: int | None = None) -> None: ... def printErrors(self) -> None: ... def wasSuccessful(self) -> bool: ... From 2e5174c82317068645ec888c36902bd19ffcd49d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:01:15 +0100 Subject: [PATCH 0500/1617] Add basic support for recursive TypeVar defaults (PEP 696) (#16878) Ref: https://github.com/python/mypy/issues/14851 --- mypy/applytype.py | 13 +++- mypy/expandtype.py | 9 +++ mypy/semanal.py | 9 +++ mypy/tvar_scope.py | 22 ++++++ mypy/typetraverser.py | 6 +- test-data/unit/check-typevar-defaults.test | 78 ++++++++++++++++++++++ 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index b00372855d9c..e14906fa2772 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -147,7 +147,18 @@ def apply_generic_arguments( # TODO: move apply_poly() logic from checkexpr.py here when new inference # becomes universally used (i.e. in all passes + in unification). # With this new logic we can actually *add* some new free variables. - remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] + remaining_tvars: list[TypeVarLikeType] = [] + for tv in tvars: + if tv.id in id_to_type: + continue + if not tv.has_default(): + remaining_tvars.append(tv) + continue + # TypeVarLike isn't in id_to_type mapping. + # Only expand the TypeVar default here. + typ = expand_type(tv, id_to_type) + assert isinstance(typ, TypeVarLikeType) + remaining_tvars.append(typ) return callable.copy_modified( ret_type=expand_type(callable.ret_type, id_to_type), diff --git a/mypy/expandtype.py b/mypy/expandtype.py index d2d294fb77f3..3bf45854b2a0 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -179,6 +179,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator): def __init__(self, variables: Mapping[TypeVarId, Type]) -> None: self.variables = variables + self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {} def visit_unbound_type(self, t: UnboundType) -> Type: return t @@ -226,6 +227,14 @@ def visit_type_var(self, t: TypeVarType) -> Type: # TODO: do we really need to do this? # If I try to remove this special-casing ~40 tests fail on reveal_type(). return repl.copy_modified(last_known_value=None) + if isinstance(repl, TypeVarType) and repl.has_default(): + if (tvar_id := repl.id) in self.recursive_tvar_guard: + return self.recursive_tvar_guard[tvar_id] or repl + self.recursive_tvar_guard[tvar_id] = None + repl = repl.accept(self) + if isinstance(repl, TypeVarType): + repl.default = repl.default.accept(self) + self.recursive_tvar_guard[tvar_id] = repl return repl def visit_param_spec(self, t: ParamSpecType) -> Type: diff --git a/mypy/semanal.py b/mypy/semanal.py index 4bf9f0c3eabb..aeceb644fe52 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1954,6 +1954,15 @@ class Foo(Bar, Generic[T]): ... del base_type_exprs[i] tvar_defs: list[TypeVarLikeType] = [] for name, tvar_expr in declared_tvars: + tvar_expr_default = tvar_expr.default + if isinstance(tvar_expr_default, UnboundType): + # TODO: - detect out of order and self-referencing TypeVars + # - nested default types, e.g. list[T1] + n = self.lookup_qualified( + tvar_expr_default.name, tvar_expr_default, suppress_errors=True + ) + if n is not None and (default := self.tvar_scope.get_binding(n)) is not None: + tvar_expr.default = default tvar_def = self.tvar_scope.bind_new(name, tvar_expr) tvar_defs.append(tvar_def) return base_type_exprs, tvar_defs, is_protocol diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index c7a653a1552d..4dc663df0399 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -15,6 +15,26 @@ TypeVarTupleType, TypeVarType, ) +from mypy.typetraverser import TypeTraverserVisitor + + +class TypeVarLikeNamespaceSetter(TypeTraverserVisitor): + """Set namespace for all TypeVarLikeTypes types.""" + + def __init__(self, namespace: str) -> None: + self.namespace = namespace + + def visit_type_var(self, t: TypeVarType) -> None: + t.id.namespace = self.namespace + super().visit_type_var(t) + + def visit_param_spec(self, t: ParamSpecType) -> None: + t.id.namespace = self.namespace + return super().visit_param_spec(t) + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + t.id.namespace = self.namespace + super().visit_type_var_tuple(t) class TypeVarLikeScope: @@ -88,6 +108,8 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: i = self.func_id # TODO: Consider also using namespaces for functions namespace = "" + tvar_expr.default.accept(TypeVarLikeNamespaceSetter(namespace)) + if isinstance(tvar_expr, TypeVarExpr): tvar_def: TypeVarLikeType = TypeVarType( name=name, diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index d9ab54871f4a..1ff5f6685eb8 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -61,16 +61,16 @@ def visit_type_var(self, t: TypeVarType) -> None: # Note that type variable values and upper bound aren't treated as # components, since they are components of the type variable # definition. We want to traverse everything just once. - pass + t.default.accept(self) def visit_param_spec(self, t: ParamSpecType) -> None: - pass + t.default.accept(self) def visit_parameters(self, t: Parameters) -> None: self.traverse_types(t.arg_types) def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: - pass + t.default.accept(self) def visit_literal_type(self, t: LiteralType) -> None: t.fallback.accept(self) diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 544bc59494b3..1a08823cb692 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -349,6 +349,84 @@ def func_c4( reveal_type(m) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] +[case testTypeVarDefaultsClassRecursive1] +# flags: --disallow-any-generics +from typing import Generic, TypeVar + +T1 = TypeVar("T1", default=str) +T2 = TypeVar("T2", default=T1) +T3 = TypeVar("T3", default=T2) + +class ClassD1(Generic[T1, T2]): ... + +def func_d1( + a: ClassD1, + b: ClassD1[int], + c: ClassD1[int, float] +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassD1[builtins.str, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassD1[builtins.int, builtins.int]" + reveal_type(c) # N: Revealed type is "__main__.ClassD1[builtins.int, builtins.float]" + + k = ClassD1() + reveal_type(k) # N: Revealed type is "__main__.ClassD1[builtins.str, builtins.str]" + l = ClassD1[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassD1[builtins.int, builtins.int]" + m = ClassD1[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassD1[builtins.int, builtins.float]" + +class ClassD2(Generic[T1, T2, T3]): ... + +def func_d2( + a: ClassD2, + b: ClassD2[int], + c: ClassD2[int, float], + d: ClassD2[int, float, str], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassD2[builtins.str, builtins.str, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.int, builtins.int]" + reveal_type(c) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.float, builtins.str]" + + k = ClassD2() + reveal_type(k) # N: Revealed type is "__main__.ClassD2[builtins.str, builtins.str, builtins.str]" + l = ClassD2[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.int, builtins.int]" + m = ClassD2[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.float, builtins.float]" + n = ClassD2[int, float, str]() + reveal_type(n) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.float, builtins.str]" + +[case testTypeVarDefaultsClassRecursiveMultipleFiles] +# flags: --disallow-any-generics +from typing import Generic, TypeVar +from file2 import T as T2 + +T = TypeVar('T', default=T2) + +class ClassG1(Generic[T2, T]): + pass + +def func( + a: ClassG1, + b: ClassG1[str], + c: ClassG1[str, float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassG1[builtins.int, builtins.int]" + reveal_type(b) # N: Revealed type is "__main__.ClassG1[builtins.str, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassG1[builtins.str, builtins.float]" + + k = ClassG1() + reveal_type(k) # N: Revealed type is "__main__.ClassG1[builtins.int, builtins.int]" + l = ClassG1[str]() + reveal_type(l) # N: Revealed type is "__main__.ClassG1[builtins.str, builtins.str]" + m = ClassG1[str, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassG1[builtins.str, builtins.float]" + +[file file2.py] +from typing import TypeVar +T = TypeVar('T', default=int) + [case testTypeVarDefaultsTypeAlias1] # flags: --disallow-any-generics from typing import Any, Dict, List, Tuple, TypeVar, Union From bfbac5efbad32305b2948b82f95c9a8c34d44d34 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Fri, 16 Feb 2024 23:34:44 +0100 Subject: [PATCH 0501/1617] stubgen: Fix generated dataclass `__init__` signature (#16906) Fixes #16811 stubgen was swallowing default values for `__init__` methods generated by the dataclass plugin making their signature incorrect. This is because the plugin does not include the argument's initializer in the generated signature. I changed it to include a dummy ellipsis so that stubgen can generate correct code. I also fixed arguments added by the dataclass plugin with the invalid names `*` and `**` to have the valid and unique names `*generated_args` and `**generated_kwargs` (with extra underscores to make them unique if necessary). This removes the need for the hack to special case them in stubgen and is less confusing for someone looking at them in a stub file. --- mypy/plugins/dataclasses.py | 17 +++++++--- mypy/stubgen.py | 11 ------- test-data/unit/check-dataclasses.test | 6 ++++ test-data/unit/stubgen.test | 47 ++++++++++++++++++--------- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 685d1b342055..dead512a2202 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -24,6 +24,7 @@ Context, DataclassTransformSpec, Decorator, + EllipsisExpr, Expression, FuncDef, FuncItem, @@ -149,13 +150,13 @@ def to_argument( return Argument( variable=self.to_var(current_info), type_annotation=self.expand_type(current_info), - initializer=None, + initializer=EllipsisExpr() if self.has_default else None, # Only used by stubgen kind=arg_kind, ) def expand_type(self, current_info: TypeInfo) -> Type | None: if self.type is not None and self.info.self_type is not None: - # In general, it is not safe to call `expand_type()` during semantic analyzis, + # In general, it is not safe to call `expand_type()` during semantic analysis, # however this plugin is called very late, so all types should be fully ready. # Also, it is tricky to avoid eager expansion of Self types here (e.g. because # we serialize attributes). @@ -269,11 +270,17 @@ def transform(self) -> bool: if arg.kind == ARG_POS: arg.kind = ARG_OPT - nameless_var = Var("") + existing_args_names = {arg.variable.name for arg in args} + gen_args_name = "generated_args" + while gen_args_name in existing_args_names: + gen_args_name += "_" + gen_kwargs_name = "generated_kwargs" + while gen_kwargs_name in existing_args_names: + gen_kwargs_name += "_" args = [ - Argument(nameless_var, AnyType(TypeOfAny.explicit), None, ARG_STAR), + Argument(Var(gen_args_name), AnyType(TypeOfAny.explicit), None, ARG_STAR), *args, - Argument(nameless_var, AnyType(TypeOfAny.explicit), None, ARG_STAR2), + Argument(Var(gen_kwargs_name), AnyType(TypeOfAny.explicit), None, ARG_STAR2), ] add_method_to_class( diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 36e8bd2acfb4..279f0569174a 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -537,17 +537,6 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: if new_args is not None: args = new_args - is_dataclass_generated = ( - self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated - ) - if o.name == "__init__" and is_dataclass_generated and "**" in [a.name for a in args]: - # The dataclass plugin generates invalid nameless "*" and "**" arguments - new_name = "".join(a.name.strip("*") for a in args) - for arg in args: - if arg.name == "*": - arg.name = f"*{new_name}_" # this name is guaranteed to be unique - elif arg.name == "**": - arg.name = f"**{new_name}__" # same here return args def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index b57fe8f548c4..a055507cdd78 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1610,10 +1610,16 @@ B: Any @dataclass class A(B): a: int +@dataclass +class C(B): + generated_args: int + generated_kwargs: int A(a=1, b=2) A(1) A(a="foo") # E: Argument "a" to "A" has incompatible type "str"; expected "int" +C(generated_args="foo", generated_kwargs="bar") # E: Argument "generated_args" to "C" has incompatible type "str"; expected "int" \ + # E: Argument "generated_kwargs" to "C" has incompatible type "str"; expected "int" [builtins fixtures/dataclasses.pyi] [case testDataclassesCallableFrozen] diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index c56f6b40b74d..3503fd4ad808 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4083,20 +4083,21 @@ class W: ... class V: ... [case testDataclass_semanal] -from dataclasses import dataclass, InitVar +from dataclasses import InitVar, dataclass, field from typing import ClassVar @dataclass class X: a: int - b: str = "hello" - c: ClassVar - d: ClassVar = 200 + b: InitVar[str] + c: str = "hello" + d: ClassVar + e: ClassVar = 200 f: list[int] = field(init=False, default_factory=list) g: int = field(default=2, kw_only=True) h: int = 1 - i: InitVar[str] - j: InitVar = 100 + i: InitVar = 100 + j: list[int] = field(default_factory=list) non_field = None @dataclass(init=False, repr=False, frozen=True) @@ -4109,23 +4110,24 @@ from typing import ClassVar @dataclass class X: a: int - b: str = ... - c: ClassVar - d: ClassVar = ... + b: InitVar[str] + c: str = ... + d: ClassVar + e: ClassVar = ... f: list[int] = ... g: int = ... h: int = ... - i: InitVar[str] - j: InitVar = ... + i: InitVar = ... + j: list[int] = ... non_field = ... - def __init__(self, a, b, f, g, h, i, j) -> None: ... + def __init__(self, a, b, c=..., *, g=..., h=..., i=..., j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... [case testDataclassWithKwOnlyField_semanal] # flags: --python-version=3.10 -from dataclasses import dataclass, InitVar, KW_ONLY +from dataclasses import dataclass, field, InitVar, KW_ONLY from typing import ClassVar @dataclass @@ -4162,7 +4164,7 @@ class X: i: InitVar[str] j: InitVar = ... non_field = ... - def __init__(self, a, b, f, g, *, h, i, j) -> None: ... + def __init__(self, a, b=..., *, g=..., h=..., i, j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... @@ -4193,6 +4195,13 @@ import missing class X(missing.Base): a: int +@dataclass +class Y(missing.Base): + generated_args: str + generated_args_: str + generated_kwargs: float + generated_kwargs_: float + [out] import missing from dataclasses import dataclass @@ -4200,7 +4209,15 @@ from dataclasses import dataclass @dataclass class X(missing.Base): a: int - def __init__(self, *selfa_, a, **selfa__) -> None: ... + def __init__(self, *generated_args, a, **generated_kwargs) -> None: ... + +@dataclass +class Y(missing.Base): + generated_args: str + generated_args_: str + generated_kwargs: float + generated_kwargs_: float + def __init__(self, *generated_args__, generated_args, generated_args_, generated_kwargs, generated_kwargs_, **generated_kwargs__) -> None: ... [case testAlwaysUsePEP604Union] import typing From 17271e57cfce5c441c3e481d20b28ca7484db231 Mon Sep 17 00:00:00 2001 From: Edward Paget Date: Sat, 17 Feb 2024 17:32:11 -0600 Subject: [PATCH 0502/1617] Fix narrowing on match with function subject (#16503) Fixes #12998 mypy can't narrow match statements with functions subjects because the callexpr node is not a literal node. This adds a 'dummy' literal node that the match statement visitor can use to do the type narrowing. The python grammar describes the the match subject as a named expression so this uses that nameexpr node as it's literal. --------- Co-authored-by: hauntsaninja --- mypy/checker.py | 19 ++++++++++++++++--- test-data/unit/check-python310.test | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 391f28e93b1d..56be3db3f9e7 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5053,6 +5053,19 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None: return def visit_match_stmt(self, s: MatchStmt) -> None: + named_subject: Expression + if isinstance(s.subject, CallExpr): + # Create a dummy subject expression to handle cases where a match statement's subject + # is not a literal value. This lets us correctly narrow types and check exhaustivity + # This is hack! + id = s.subject.callee.fullname if isinstance(s.subject.callee, RefExpr) else "" + name = "dummy-match-" + id + v = Var(name) + named_subject = NameExpr(name) + named_subject.node = v + else: + named_subject = s.subject + with self.binder.frame_context(can_skip=False, fall_through=0): subject_type = get_proper_type(self.expr_checker.accept(s.subject)) @@ -5071,7 +5084,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: # The second pass narrows down the types and type checks bodies. for p, g, b in zip(s.patterns, s.guards, s.bodies): current_subject_type = self.expr_checker.narrow_type_from_binder( - s.subject, subject_type + named_subject, subject_type ) pattern_type = self.pattern_checker.accept(p, current_subject_type) with self.binder.frame_context(can_skip=True, fall_through=2): @@ -5082,7 +5095,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: else_map: TypeMap = {} else: pattern_map, else_map = conditional_types_to_typemaps( - s.subject, pattern_type.type, pattern_type.rest_type + named_subject, pattern_type.type, pattern_type.rest_type ) self.remove_capture_conflicts(pattern_type.captures, inferred_types) self.push_type_map(pattern_map) @@ -5110,7 +5123,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: and expr.fullname == case_target.fullname ): continue - type_map[s.subject] = type_map[expr] + type_map[named_subject] = type_map[expr] self.push_type_map(guard_map) self.accept(b) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index cbb26a130738..b0e27fe1e3a0 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1139,6 +1139,21 @@ match m: reveal_type(a) # N: Revealed type is "builtins.str" +[case testMatchCapturePatternFromFunctionReturningUnion] +def func1(arg: bool) -> str | int: ... +def func2(arg: bool) -> bytes | int: ... + +def main() -> None: + match func1(True): + case str(a): + match func2(True): + case c: + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(c) # N: Revealed type is "Union[builtins.bytes, builtins.int]" + reveal_type(a) # N: Revealed type is "builtins.str" + case a: + reveal_type(a) # N: Revealed type is "builtins.int" + -- Guards -- [case testMatchSimplePatternGuard] From eb84794bd02b20e051103f91b6d1dcb01c0e342c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 18 Feb 2024 13:33:59 +0000 Subject: [PATCH 0503/1617] FIx stubtest's tests to work with the latest version of `typing_extensions` (#16928) Stubtest's tests will start failing when `typing_extensions==4.10.0` comes out, due to some new `ClassVar`s on `typing_extensions.TypedDict`. This PR fixes that. Fixes https://github.com/python/typing_extensions/issues/339. Note: there's no need to cherry-pick this to the `release-1.9.0` branch, since the daily workflow `typing_extensions` uses runs mypy's tests using the mypy `master` branch. --- test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 7aca6fad1b42..68dd985cfe2a 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -61,6 +61,8 @@ class _TypedDict(Mapping[str, object]): __optional_keys__: frozenset[str] __readonly_keys__: frozenset[str] __mutable_keys__: frozenset[str] + __closed__: bool + __extra_items__: Any __total__: bool def TypedDict(typename: str, fields: Dict[str, Type[_T]], *, total: Any = ...) -> Type[dict]: ... From 46ebacae0ca5b464a7d422ac1e3370cae32c135a Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 18 Feb 2024 22:32:19 +0100 Subject: [PATCH 0504/1617] stubgen: Replace obsolete typing aliases with builtin containers (#16780) Addresses part of #16737 This only replaces typing symbols that have equivalents in the `builtins` module. Replacing other symbols, like those from the `collections.abc` module, are a bit more complicated so I suggest we handle them separately. I also changed the default `TypedDict` module from `typing_extensions` to `typing` as typeshed dropped support for Python 3.7. --- mypy/stubgen.py | 51 ++++++---- mypy/stubutil.py | 33 ++++++- .../pybind11_fixtures/__init__.pyi | 6 +- .../pybind11_fixtures/demo.pyi | 4 +- .../pybind11_fixtures/__init__.pyi | 6 +- .../pybind11_fixtures/demo.pyi | 4 +- test-data/unit/stubgen.test | 94 +++++++++++++++---- 7 files changed, 148 insertions(+), 50 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 279f0569174a..7721366f5c0c 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -47,7 +47,7 @@ import os.path import sys import traceback -from typing import Final, Iterable +from typing import Final, Iterable, Iterator import mypy.build import mypy.mixedtraverser @@ -114,6 +114,7 @@ from mypy.stubdoc import ArgSig, FunctionSig from mypy.stubgenc import InspectionStubGenerator, generate_stub_for_c_module from mypy.stubutil import ( + TYPING_BUILTIN_REPLACEMENTS, BaseStubGenerator, CantImport, ClassInfo, @@ -289,20 +290,19 @@ def visit_call_expr(self, node: CallExpr) -> str: raise ValueError(f"Unknown argument kind {kind} in call") return f"{callee}({', '.join(args)})" + def _visit_ref_expr(self, node: NameExpr | MemberExpr) -> str: + fullname = self.stubgen.get_fullname(node) + if fullname in TYPING_BUILTIN_REPLACEMENTS: + return self.stubgen.add_name(TYPING_BUILTIN_REPLACEMENTS[fullname], require=False) + qualname = get_qualified_name(node) + self.stubgen.import_tracker.require_name(qualname) + return qualname + def visit_name_expr(self, node: NameExpr) -> str: - self.stubgen.import_tracker.require_name(node.name) - return node.name + return self._visit_ref_expr(node) def visit_member_expr(self, o: MemberExpr) -> str: - node: Expression = o - trailer = "" - while isinstance(node, MemberExpr): - trailer = "." + node.name + trailer - node = node.expr - if not isinstance(node, NameExpr): - return ERROR_MARKER - self.stubgen.import_tracker.require_name(node.name) - return node.name + trailer + return self._visit_ref_expr(o) def visit_str_expr(self, node: StrExpr) -> str: return repr(node.value) @@ -351,11 +351,17 @@ def find_defined_names(file: MypyFile) -> set[str]: return finder.names +def get_assigned_names(lvalues: Iterable[Expression]) -> Iterator[str]: + for lvalue in lvalues: + if isinstance(lvalue, NameExpr): + yield lvalue.name + elif isinstance(lvalue, TupleExpr): + yield from get_assigned_names(lvalue.items) + + class DefinitionFinder(mypy.traverser.TraverserVisitor): """Find names of things defined at the top level of a module.""" - # TODO: Assignment statements etc. - def __init__(self) -> None: # Short names of things defined at the top level. self.names: set[str] = set() @@ -368,6 +374,10 @@ def visit_func_def(self, o: FuncDef) -> None: # Don't recurse, as we only keep track of top-level definitions. self.names.add(o.name) + def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + for name in get_assigned_names(o.lvalues): + self.names.add(name) + def find_referenced_names(file: MypyFile) -> set[str]: finder = ReferenceFinder() @@ -1023,10 +1033,15 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: and isinstance(expr.node, (FuncDef, Decorator, MypyFile)) or isinstance(expr.node, TypeInfo) ) and not self.is_private_member(expr.node.fullname) - elif ( - isinstance(expr, IndexExpr) - and isinstance(expr.base, NameExpr) - and not self.is_private_name(expr.base.name) + elif isinstance(expr, IndexExpr) and ( + (isinstance(expr.base, NameExpr) and not self.is_private_name(expr.base.name)) + or ( # Also some known aliases that could be member expression + isinstance(expr.base, MemberExpr) + and not self.is_private_member(get_qualified_name(expr.base)) + and self.get_fullname(expr.base).startswith( + ("builtins.", "typing.", "typing_extensions.", "collections.abc.") + ) + ) ): if isinstance(expr.index, TupleExpr): indices = expr.index.items diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 69af643efab2..410672f89d09 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -22,6 +22,26 @@ # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () +# Typing constructs to be replaced by their builtin equivalents. +TYPING_BUILTIN_REPLACEMENTS: Final = { + # From typing + "typing.Text": "builtins.str", + "typing.Tuple": "builtins.tuple", + "typing.List": "builtins.list", + "typing.Dict": "builtins.dict", + "typing.Set": "builtins.set", + "typing.FrozenSet": "builtins.frozenset", + "typing.Type": "builtins.type", + # From typing_extensions + "typing_extensions.Text": "builtins.str", + "typing_extensions.Tuple": "builtins.tuple", + "typing_extensions.List": "builtins.list", + "typing_extensions.Dict": "builtins.dict", + "typing_extensions.Set": "builtins.set", + "typing_extensions.FrozenSet": "builtins.frozenset", + "typing_extensions.Type": "builtins.type", +} + class CantImport(Exception): def __init__(self, module: str, message: str) -> None: @@ -229,6 +249,8 @@ def visit_unbound_type(self, t: UnboundType) -> str: return " | ".join([item.accept(self) for item in t.args]) if fullname == "typing.Optional": return f"{t.args[0].accept(self)} | None" + if fullname in TYPING_BUILTIN_REPLACEMENTS: + s = self.stubgen.add_name(TYPING_BUILTIN_REPLACEMENTS[fullname], require=True) if self.known_modules is not None and "." in s: # see if this object is from any of the modules that we're currently processing. # reverse sort so that subpackages come before parents: e.g. "foo.bar" before "foo". @@ -476,7 +498,7 @@ def reexport(self, name: str) -> None: def import_lines(self) -> list[str]: """The list of required import lines (as strings with python code). - In order for a module be included in this output, an indentifier must be both + In order for a module be included in this output, an identifier must be both 'required' via require_name() and 'imported' via add_import_from() or add_import() """ @@ -585,9 +607,9 @@ def __init__( # a corresponding import statement. self.known_imports = { "_typeshed": ["Incomplete"], - "typing": ["Any", "TypeVar", "NamedTuple"], + "typing": ["Any", "TypeVar", "NamedTuple", "TypedDict"], "collections.abc": ["Generator"], - "typing_extensions": ["TypedDict", "ParamSpec", "TypeVarTuple"], + "typing_extensions": ["ParamSpec", "TypeVarTuple"], } def get_sig_generators(self) -> list[SignatureGenerator]: @@ -613,7 +635,10 @@ def add_name(self, fullname: str, require: bool = True) -> str: """ module, name = fullname.rsplit(".", 1) alias = "_" + name if name in self.defined_names else None - self.import_tracker.add_import_from(module, [(name, alias)], require=require) + while alias in self.defined_names: + alias = "_" + alias + if module != "builtins" or alias: # don't import from builtins unless needed + self.import_tracker.add_import_from(module, [(name, alias)], require=require) return alias or name def add_import_line(self, line: str) -> None: diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi index bb939aa5a5e7..90afb46d6d94 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi @@ -1,6 +1,6 @@ import os from . import demo as demo -from typing import List, Tuple, overload +from typing import overload class StaticMethods: def __init__(self, *args, **kwargs) -> None: ... @@ -22,6 +22,6 @@ class TestStruct: def func_incomplete_signature(*args, **kwargs): ... def func_returning_optional() -> int | None: ... -def func_returning_pair() -> Tuple[int, float]: ... +def func_returning_pair() -> tuple[int, float]: ... def func_returning_path() -> os.PathLike: ... -def func_returning_vector() -> List[float]: ... +def func_returning_vector() -> list[float]: ... diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi index 6f164a03edcc..87b8ec0e4ad6 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi @@ -1,4 +1,4 @@ -from typing import ClassVar, List, overload +from typing import ClassVar, overload PI: float __version__: str @@ -47,7 +47,7 @@ class Point: def __init__(self) -> None: ... @overload def __init__(self, x: float, y: float) -> None: ... - def as_list(self) -> List[float]: ... + def as_list(self) -> list[float]: ... @overload def distance_to(self, x: float, y: float) -> float: ... @overload diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi index 622e5881a147..db04bccab028 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi @@ -1,6 +1,6 @@ import os from . import demo as demo -from typing import List, Tuple, overload +from typing import overload class StaticMethods: def __init__(self, *args, **kwargs) -> None: @@ -44,9 +44,9 @@ def func_incomplete_signature(*args, **kwargs): """func_incomplete_signature() -> dummy_sub_namespace::HasNoBinding""" def func_returning_optional() -> int | None: """func_returning_optional() -> Optional[int]""" -def func_returning_pair() -> Tuple[int, float]: +def func_returning_pair() -> tuple[int, float]: """func_returning_pair() -> Tuple[int, float]""" def func_returning_path() -> os.PathLike: """func_returning_path() -> os.PathLike""" -def func_returning_vector() -> List[float]: +def func_returning_vector() -> list[float]: """func_returning_vector() -> List[float]""" diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi index 1527225ed009..1be0bc905a43 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi @@ -1,4 +1,4 @@ -from typing import ClassVar, List, overload +from typing import ClassVar, overload PI: float __version__: str @@ -73,7 +73,7 @@ class Point: 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None """ - def as_list(self) -> List[float]: + def as_list(self) -> list[float]: """as_list(self: pybind11_fixtures.demo.Point) -> List[float]""" @overload def distance_to(self, x: float, y: float) -> float: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 3503fd4ad808..53baa2c0ca06 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1376,9 +1376,8 @@ x: List[collections.defaultdict] [out] import collections -from typing import List -x: List[collections.defaultdict] +x: list[collections.defaultdict] [case testAnnotationFwRefs] @@ -2216,9 +2215,9 @@ funcs: Dict[Any, Any] f = funcs[a.f] [out] from _typeshed import Incomplete -from typing import Any, Dict +from typing import Any -funcs: Dict[Any, Any] +funcs: dict[Any, Any] f: Incomplete [case testAbstractMethodNameExpr] @@ -3290,18 +3289,18 @@ def f(*args: Union[int, Tuple[int, int]]) -> int: [out] -from typing import Tuple, overload +from typing import overload class A: @overload def f(self, x: int, y: int) -> int: ... @overload - def f(self, x: Tuple[int, int]) -> int: ... + def f(self, x: tuple[int, int]) -> int: ... @overload def f(x: int, y: int) -> int: ... @overload -def f(x: Tuple[int, int]) -> int: ... +def f(x: tuple[int, int]) -> int: ... [case testOverload_fromTypingExtensionsImport] from typing import Tuple, Union @@ -3332,19 +3331,18 @@ def f(*args: Union[int, Tuple[int, int]]) -> int: [out] -from typing import Tuple from typing_extensions import overload class A: @overload def f(self, x: int, y: int) -> int: ... @overload - def f(self, x: Tuple[int, int]) -> int: ... + def f(self, x: tuple[int, int]) -> int: ... @overload def f(x: int, y: int) -> int: ... @overload -def f(x: Tuple[int, int]) -> int: ... +def f(x: tuple[int, int]) -> int: ... [case testOverload_importTyping] import typing @@ -3407,22 +3405,22 @@ class A: @typing.overload def f(self, x: int, y: int) -> int: ... @typing.overload - def f(self, x: typing.Tuple[int, int]) -> int: ... + def f(self, x: tuple[int, int]) -> int: ... @typing.overload @classmethod def g(cls, x: int, y: int) -> int: ... @typing.overload @classmethod - def g(cls, x: typing.Tuple[int, int]) -> int: ... + def g(cls, x: tuple[int, int]) -> int: ... @typing.overload def f(x: int, y: int) -> int: ... @typing.overload -def f(x: typing.Tuple[int, int]) -> int: ... +def f(x: tuple[int, int]) -> int: ... @typing_extensions.overload def g(x: int, y: int) -> int: ... @typing_extensions.overload -def g(x: typing.Tuple[int, int]) -> int: ... +def g(x: tuple[int, int]) -> int: ... [case testOverload_importTypingAs] import typing as t @@ -3485,22 +3483,22 @@ class A: @t.overload def f(self, x: int, y: int) -> int: ... @t.overload - def f(self, x: t.Tuple[int, int]) -> int: ... + def f(self, x: tuple[int, int]) -> int: ... @t.overload @classmethod def g(cls, x: int, y: int) -> int: ... @t.overload @classmethod - def g(cls, x: t.Tuple[int, int]) -> int: ... + def g(cls, x: tuple[int, int]) -> int: ... @t.overload def f(x: int, y: int) -> int: ... @t.overload -def f(x: t.Tuple[int, int]) -> int: ... +def f(x: tuple[int, int]) -> int: ... @te.overload def g(x: int, y: int) -> int: ... @te.overload -def g(x: t.Tuple[int, int]) -> int: ... +def g(x: tuple[int, int]) -> int: ... [case testOverloadFromImportAlias] from typing import overload as t_overload @@ -4249,6 +4247,66 @@ o = int | None def f1(a: int | tuple[int, int | None] | None) -> int: ... def f2(a: int | x.Union[int, int] | float | None) -> int: ... +[case testTypingBuiltinReplacements] +import typing +import typing as t +from typing import Tuple +import typing_extensions +import typing_extensions as te +from typing_extensions import List, Type + +# builtins are not builtins +tuple = int +[list,] = float +dict, set, frozenset = str, float, int + +x: Tuple[t.Text, t.FrozenSet[typing.Type[float]]] +y: typing.List[int] +z: t.Dict[str, float] +v: typing.Set[int] +w: List[typing_extensions.Dict[te.FrozenSet[Type[int]], te.Tuple[te.Set[te.Text], ...]]] + +x_alias = Tuple[str, ...] +y_alias = typing.List[int] +z_alias = t.Dict[str, float] +v_alias = typing.Set[int] +w_alias = List[typing_extensions.Dict[str, te.Tuple[int, ...]]] + +[out] +from _typeshed import Incomplete +from builtins import dict as _dict, frozenset as _frozenset, list as _list, set as _set, tuple as _tuple + +tuple = int +list: Incomplete +dict: Incomplete +set: Incomplete +frozenset: Incomplete +x: _tuple[str, _frozenset[type[float]]] +y: _list[int] +z: _dict[str, float] +v: _set[int] +w: _list[_dict[_frozenset[type[int]], _tuple[_set[str], ...]]] +x_alias = _tuple[str, ...] +y_alias = _list[int] +z_alias = _dict[str, float] +v_alias = _set[int] +w_alias = _list[_dict[str, _tuple[int, ...]]] + +[case testHandlingNameCollisions] +# flags: --include-private +from typing import Tuple +tuple = int +_tuple = range +__tuple = map +x: Tuple[int, str] +[out] +from builtins import tuple as ___tuple + +tuple = int +_tuple = range +__tuple = map +x: ___tuple[int, str] + [case testPEP570PosOnlyParams] def f(x=0, /): ... def f1(x: int, /): ... From 790e8a73d8671a41cae419b4ea07579bfb2bc292 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:02:49 +0100 Subject: [PATCH 0505/1617] Error handling for recursive TypeVar defaults (PEP 696) (#16925) This PR adds some additional error handling for recursive TypeVar defaults. Open issue for future PRs: - Expanding nested recursive defaults, e.g. `T2 = list[T1 = str]` - Scope binding, especially for TypeAliasTypes Ref: https://github.com/python/mypy/issues/14851 --- mypy/messages.py | 9 ++ mypy/semanal.py | 47 ++++++-- mypy/typeanal.py | 36 +++++- mypy/types.py | 9 ++ test-data/unit/check-typevar-defaults.test | 134 ++++++++++++++++++++- 5 files changed, 223 insertions(+), 12 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index c107e874f4fc..db6c91ba9008 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2059,6 +2059,15 @@ def impossible_intersection( template.format(formatted_base_class_list, reason), context, code=codes.UNREACHABLE ) + def tvar_without_default_type( + self, tvar_name: str, last_tvar_name_with_default: str, context: Context + ) -> None: + self.fail( + f'"{tvar_name}" cannot appear after "{last_tvar_name_with_default}" ' + "in type parameter list because it has no default type", + context, + ) + def report_protocol_problems( self, subtype: Instance | TupleType | TypedDictType | TypeType | CallableType, diff --git a/mypy/semanal.py b/mypy/semanal.py index aeceb644fe52..38d5ddec0818 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -226,6 +226,7 @@ SELF_TYPE_NAMES, FindTypeVarVisitor, TypeAnalyser, + TypeVarDefaultTranslator, TypeVarLikeList, analyze_type_alias, check_for_explicit_any, @@ -252,6 +253,7 @@ TPDICT_NAMES, TYPE_ALIAS_NAMES, TYPE_CHECK_ONLY_NAMES, + TYPE_VAR_LIKE_NAMES, TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, @@ -1953,17 +1955,19 @@ class Foo(Bar, Generic[T]): ... defn.removed_base_type_exprs.append(defn.base_type_exprs[i]) del base_type_exprs[i] tvar_defs: list[TypeVarLikeType] = [] + last_tvar_name_with_default: str | None = None for name, tvar_expr in declared_tvars: - tvar_expr_default = tvar_expr.default - if isinstance(tvar_expr_default, UnboundType): - # TODO: - detect out of order and self-referencing TypeVars - # - nested default types, e.g. list[T1] - n = self.lookup_qualified( - tvar_expr_default.name, tvar_expr_default, suppress_errors=True - ) - if n is not None and (default := self.tvar_scope.get_binding(n)) is not None: - tvar_expr.default = default + tvar_expr.default = tvar_expr.default.accept( + TypeVarDefaultTranslator(self, tvar_expr.name, context) + ) tvar_def = self.tvar_scope.bind_new(name, tvar_expr) + if last_tvar_name_with_default is not None and not tvar_def.has_default(): + self.msg.tvar_without_default_type( + tvar_def.name, last_tvar_name_with_default, context + ) + tvar_def.default = AnyType(TypeOfAny.from_error) + elif tvar_def.has_default(): + last_tvar_name_with_default = tvar_def.name tvar_defs.append(tvar_def) return base_type_exprs, tvar_defs, is_protocol @@ -2855,6 +2859,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: with self.allow_unbound_tvars_set(): s.rvalue.accept(self) self.basic_type_applications = old_basic_type_applications + elif self.can_possibly_be_typevarlike_declaration(s): + # Allow unbound tvars inside TypeVarLike defaults to be evaluated later + with self.allow_unbound_tvars_set(): + s.rvalue.accept(self) else: s.rvalue.accept(self) @@ -3031,6 +3039,16 @@ def can_possibly_be_type_form(self, s: AssignmentStmt) -> bool: # Something that looks like Foo = Bar[Baz, ...] return True + def can_possibly_be_typevarlike_declaration(self, s: AssignmentStmt) -> bool: + """Check if r.h.s. can be a TypeVarLike declaration.""" + if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr): + return False + if not isinstance(s.rvalue, CallExpr) or not isinstance(s.rvalue.callee, NameExpr): + return False + ref = s.rvalue.callee + ref.accept(self) + return ref.fullname in TYPE_VAR_LIKE_NAMES + def is_type_ref(self, rv: Expression, bare: bool = False) -> bool: """Does this expression refer to a type? @@ -3515,9 +3533,20 @@ def analyze_alias( found_type_vars = self.find_type_var_likes(typ) tvar_defs: list[TypeVarLikeType] = [] namespace = self.qualified_name(name) + last_tvar_name_with_default: str | None = None with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): for name, tvar_expr in found_type_vars: + tvar_expr.default = tvar_expr.default.accept( + TypeVarDefaultTranslator(self, tvar_expr.name, typ) + ) tvar_def = self.tvar_scope.bind_new(name, tvar_expr) + if last_tvar_name_with_default is not None and not tvar_def.has_default(): + self.msg.tvar_without_default_type( + tvar_def.name, last_tvar_name_with_default, typ + ) + tvar_def.default = AnyType(TypeOfAny.from_error) + elif tvar_def.has_default(): + last_tvar_name_with_default = tvar_def.name tvar_defs.append(tvar_def) analyzed, depends_on = analyze_type_alias( diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 530793730f35..9cc0114df333 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -38,7 +38,12 @@ ) from mypy.options import Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface -from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs +from mypy.semanal_shared import ( + SemanticAnalyzerCoreInterface, + SemanticAnalyzerInterface, + paramspec_args, + paramspec_kwargs, +) from mypy.state import state from mypy.tvar_scope import TypeVarLikeScope from mypy.types import ( @@ -2508,3 +2513,32 @@ def process_types(self, types: list[Type] | tuple[Type, ...]) -> None: else: for t in types: t.accept(self) + + +class TypeVarDefaultTranslator(TrivialSyntheticTypeTranslator): + """Type translate visitor that replaces UnboundTypes with in-scope TypeVars.""" + + def __init__( + self, api: SemanticAnalyzerInterface, tvar_expr_name: str, context: Context + ) -> None: + self.api = api + self.tvar_expr_name = tvar_expr_name + self.context = context + + def visit_unbound_type(self, t: UnboundType) -> Type: + sym = self.api.lookup_qualified(t.name, t, suppress_errors=True) + if sym is not None: + if type_var := self.api.tvar_scope.get_binding(sym): + return type_var + if isinstance(sym.node, TypeVarLikeExpr): + self.api.fail( + f'Type parameter "{self.tvar_expr_name}" has a default type ' + "that refers to one or more type variables that are out of scope", + self.context, + ) + return AnyType(TypeOfAny.from_error) + return super().visit_unbound_type(t) + + def visit_type_alias_type(self, t: TypeAliasType) -> Type: + # TypeAliasTypes are analyzed separately already, just return it + return t diff --git a/mypy/types.py b/mypy/types.py index b1119c9447e2..f76e35784d8f 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -85,6 +85,15 @@ TypeVisitor as TypeVisitor, ) +TYPE_VAR_LIKE_NAMES: Final = ( + "typing.TypeVar", + "typing_extensions.TypeVar", + "typing.ParamSpec", + "typing_extensions.ParamSpec", + "typing.TypeVarTuple", + "typing_extensions.TypeVarTuple", +) + TYPED_NAMEDTUPLE_NAMES: Final = ("typing.NamedTuple", "typing_extensions.NamedTuple") # Supported names of TypedDict type constructors. diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 1a08823cb692..9ca67376da26 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -82,6 +82,74 @@ T3 = TypeVar("T3", int, str, default=bytes) # E: TypeVar default must be one of T4 = TypeVar("T4", int, str, default=Union[int, str]) # E: TypeVar default must be one of the constraint types T5 = TypeVar("T5", float, str, default=int) # E: TypeVar default must be one of the constraint types +[case testTypeVarDefaultsInvalid3] +from typing import Dict, Generic, TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2", default=T3) # E: Name "T3" is used before definition +T3 = TypeVar("T3", default=str) +T4 = TypeVar("T4", default=T3) + +class ClassError1(Generic[T3, T1]): ... # E: "T1" cannot appear after "T3" in type parameter list because it has no default type + +def func_error1( + a: ClassError1, + b: ClassError1[int], + c: ClassError1[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassError1[builtins.str, Any]" + reveal_type(b) # N: Revealed type is "__main__.ClassError1[builtins.int, Any]" + reveal_type(c) # N: Revealed type is "__main__.ClassError1[builtins.int, builtins.float]" + + k = ClassError1() + reveal_type(k) # N: Revealed type is "__main__.ClassError1[builtins.str, Any]" + l = ClassError1[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassError1[builtins.int, Any]" + m = ClassError1[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassError1[builtins.int, builtins.float]" + +class ClassError2(Generic[T4, T3]): ... # E: Type parameter "T4" has a default type that refers to one or more type variables that are out of scope + +def func_error2( + a: ClassError2, + b: ClassError2[int], + c: ClassError2[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassError2[Any, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassError2[builtins.int, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassError2[builtins.int, builtins.float]" + + k = ClassError2() + reveal_type(k) # N: Revealed type is "__main__.ClassError2[Any, builtins.str]" + l = ClassError2[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassError2[builtins.int, builtins.str]" + m = ClassError2[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassError2[builtins.int, builtins.float]" + +TERR1 = Dict[T3, T1] # E: "T1" cannot appear after "T3" in type parameter list because it has no default type + +def func_error_alias1( + a: TERR1, + b: TERR1[int], + c: TERR1[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[builtins.str, Any]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, Any]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.float]" + +TERR2 = Dict[T4, T3] # TODO should be an error \ + # Type parameter "T4" has a default type that refers to one or more type variables that are out of scope + +def func_error_alias2( + a: TERR2, + b: TERR2[int], + c: TERR2[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[Any, builtins.str]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.float]" +[builtins fixtures/dict.pyi] + [case testTypeVarDefaultsFunctions] from typing import TypeVar, ParamSpec, List, Union, Callable, Tuple from typing_extensions import TypeVarTuple, Unpack @@ -351,11 +419,12 @@ def func_c4( [case testTypeVarDefaultsClassRecursive1] # flags: --disallow-any-generics -from typing import Generic, TypeVar +from typing import Generic, TypeVar, List T1 = TypeVar("T1", default=str) T2 = TypeVar("T2", default=T1) T3 = TypeVar("T3", default=T2) +T4 = TypeVar("T4", default=List[T1]) class ClassD1(Generic[T1, T2]): ... @@ -397,12 +466,30 @@ def func_d2( n = ClassD2[int, float, str]() reveal_type(n) # N: Revealed type is "__main__.ClassD2[builtins.int, builtins.float, builtins.str]" +class ClassD3(Generic[T1, T4]): ... + +def func_d3( + a: ClassD3, + b: ClassD3[int], + c: ClassD3[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassD3[builtins.str, builtins.list[builtins.str]]" + reveal_type(b) # N: Revealed type is "__main__.ClassD3[builtins.int, builtins.list[builtins.int]]" + reveal_type(c) # N: Revealed type is "__main__.ClassD3[builtins.int, builtins.float]" + + # k = ClassD3() + # reveal_type(k) # Revealed type is "__main__.ClassD3[builtins.str, builtins.list[builtins.str]]" # TODO + l = ClassD3[int]() + reveal_type(l) # N: Revealed type is "__main__.ClassD3[builtins.int, builtins.list[builtins.int]]" + m = ClassD3[int, float]() + reveal_type(m) # N: Revealed type is "__main__.ClassD3[builtins.int, builtins.float]" + [case testTypeVarDefaultsClassRecursiveMultipleFiles] # flags: --disallow-any-generics from typing import Generic, TypeVar from file2 import T as T2 -T = TypeVar('T', default=T2) +T = TypeVar("T", default=T2) class ClassG1(Generic[T2, T]): pass @@ -587,3 +674,46 @@ def func_c4( # reveal_type(b) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultsTypeAliasRecursive1] +# flags: --disallow-any-generics +from typing import Dict, List, TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2", default=T1) + +TD1 = Dict[T1, T2] + +def func_d1( + a: TD1, # E: Missing type parameters for generic type "TD1" + b: TD1[int], + c: TD1[int, float], +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.int]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.float]" +[builtins fixtures/dict.pyi] + +[case testTypeVarDefaultsTypeAliasRecursive2] +from typing import Any, Dict, Generic, TypeVar + +T1 = TypeVar("T1", default=str) +T2 = TypeVar("T2", default=T1) +Alias1 = Dict[T1, T2] +T3 = TypeVar("T3") +class A(Generic[T3]): ... + +T4 = TypeVar("T4", default=A[Alias1]) +class B(Generic[T4]): ... + +def func_d3( + a: B, + b: B[A[Alias1[int]]], + c: B[A[Alias1[int, float]]], + d: B[int], +) -> None: + reveal_type(a) # N: Revealed type is "__main__.B[__main__.A[builtins.dict[builtins.str, builtins.str]]]" + reveal_type(b) # N: Revealed type is "__main__.B[__main__.A[builtins.dict[builtins.int, builtins.int]]]" + reveal_type(c) # N: Revealed type is "__main__.B[__main__.A[builtins.dict[builtins.int, builtins.float]]]" + reveal_type(d) # N: Revealed type is "__main__.B[builtins.int]" +[builtins fixtures/dict.pyi] From 2037e4a068df6e1dcc8f76c37f53e04d62d64e80 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 25 Feb 2024 14:44:49 -0800 Subject: [PATCH 0506/1617] Workaround parenthesised context manager issue (#16949) Fixes #16945 --- mypy/checker.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 56be3db3f9e7..9f987cb5ccdf 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4,7 +4,7 @@ import itertools from collections import defaultdict -from contextlib import contextmanager, nullcontext +from contextlib import ExitStack, contextmanager from typing import ( AbstractSet, Callable, @@ -526,17 +526,11 @@ def check_second_pass( # print("XXX in pass %d, class %s, function %s" % # (self.pass_num, type_name, node.fullname or node.name)) done.add(node) - with ( - self.tscope.class_scope(active_typeinfo) - if active_typeinfo - else nullcontext() - ): - with ( - self.scope.push_class(active_typeinfo) - if active_typeinfo - else nullcontext() - ): - self.check_partial(node) + with ExitStack() as stack: + if active_typeinfo: + stack.enter_context(self.tscope.class_scope(active_typeinfo)) + stack.enter_context(self.scope.push_class(active_typeinfo)) + self.check_partial(node) return True def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> None: From a91151c46fb0407c9220db9630826634793b9697 Mon Sep 17 00:00:00 2001 From: hesam <97763520+hesam-ghamary@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:28:41 +0330 Subject: [PATCH 0507/1617] Fix duplicate word in protocols.rst (#16950) --- docs/source/protocols.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 3336d77cb397..067f4d9dcfac 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -9,7 +9,7 @@ compatible as types: nominal subtyping and structural subtyping. *Nominal* subtyping is strictly based on the class hierarchy. If class ``Dog`` inherits class ``Animal``, it's a subtype of ``Animal``. Instances of ``Dog`` can be used when ``Animal`` instances are expected. This form of subtyping -subtyping is what Python's type system predominantly uses: it's easy to +is what Python's type system predominantly uses: it's easy to understand and produces clear and concise error messages, and matches how the native :py:func:`isinstance ` check works -- based on class hierarchy. From 5a8cd80857fd7a1771d5f714c3e3ad69af4070c8 Mon Sep 17 00:00:00 2001 From: Srinivas Lade Date: Wed, 28 Feb 2024 11:16:23 -0500 Subject: [PATCH 0508/1617] [mypyc] Optimize TYPE_CHECKING to False at Runtime (#16263) Fixes [mypyc/mypyc#902](https://github.com/mypyc/mypyc/issues/902) This PR finds references of `typing.TYPE_CHECKING` or `typing_extensions.TYPE_CHECKING` and optimizes them to `False` in mypyc. --- mypyc/irbuild/expression.py | 6 ++++++ mypyc/test-data/irbuild-basic.test | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 8d205b432d2d..5cdd9a432a12 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -126,6 +126,8 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: return builder.true() if fullname == "builtins.False": return builder.false() + if fullname in ("typing.TYPE_CHECKING", "typing_extensions.TYPE_CHECKING"): + return builder.false() math_literal = transform_math_literal(builder, fullname) if math_literal is not None: @@ -185,6 +187,10 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value: + # Special Cases + if expr.fullname in ("typing.TYPE_CHECKING", "typing_extensions.TYPE_CHECKING"): + return builder.false() + # First check if this is maybe a final attribute. final = builder.get_final_ref(expr) if final is not None: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index bf608abb87ad..d6c47814cb7f 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3688,3 +3688,33 @@ def f(arg): arg :: __main__.A L0: return arg + +[case testTypeCheckingFlag] +from typing import TYPE_CHECKING, List + +def f(arg: List[int]) -> int: + if TYPE_CHECKING: + from collections.abc import Sized + s: Sized = arg + return len(s) + +[out] +def f(arg): + arg :: list + r0 :: bool + r1 :: int + r2 :: bit + s :: object + r3 :: int +L0: + r0 = 0 << 1 + r1 = extend r0: builtins.bool to builtins.int + r2 = r1 != 0 + if r2 goto L1 else goto L2 :: bool +L1: + goto L3 +L2: +L3: + s = arg + r3 = CPyObject_Size(s) + return r3 From 162c74d2af77070c6983e0f97fcb593eda4d29d1 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Wed, 28 Feb 2024 11:52:42 -0500 Subject: [PATCH 0509/1617] [mypyc] Remangle redefined names produced by async with (#16408) Fixes mypyc/mypyc#1001. --------- Co-authored-by: Jelle Zijlstra --- mypyc/irbuild/builder.py | 7 ++++--- mypyc/test-data/run-async.test | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 9d160b08505d..69f3ad9c495f 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -1246,14 +1246,15 @@ def add_var_to_env_class( ) -> AssignmentTarget: # First, define the variable name as an attribute of the environment class, and then # construct a target for that attribute. - self.fn_info.env_class.attributes[var.name] = rtype - attr_target = AssignmentTargetAttr(base.curr_env_reg, var.name) + name = remangle_redefinition_name(var.name) + self.fn_info.env_class.attributes[name] = rtype + attr_target = AssignmentTargetAttr(base.curr_env_reg, name) if reassign: # Read the local definition of the variable, and set the corresponding attribute of # the environment class' variable to be that value. reg = self.read(self.lookup(var), self.fn_info.fitem.line) - self.add(SetAttr(base.curr_env_reg, var.name, reg, self.fn_info.fitem.line)) + self.add(SetAttr(base.curr_env_reg, name, reg, self.fn_info.fitem.line)) # Override the local definition of the variable to instead point at the variable in # the environment class. diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index 85ad172d61df..8488632e6574 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -143,3 +143,31 @@ async def foo() -> AsyncIterable[int]: yields, val = run_generator(async_iter(foo())) assert yields == (0,1,2), yields assert val == 'lol no', val + +[case testAsyncWithVarReuse] +class ConMan: + async def __aenter__(self) -> int: + return 1 + async def __aexit__(self, *exc: object): + pass + +class ConManB: + async def __aenter__(self) -> int: + return 2 + async def __aexit__(self, *exc: object): + pass + +async def x() -> None: + value = 2 + async with ConMan() as f: + value += f + assert value == 3, value + async with ConManB() as f: + value += f + assert value == 5, value + +[typing fixtures/typing-full.pyi] +[file driver.py] +import asyncio +import native +asyncio.run(native.x()) From 9f1c90a072c1bbbbb8260ad7181fc1f1acb99137 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Wed, 28 Feb 2024 12:06:35 -0500 Subject: [PATCH 0510/1617] [mypyc] Don't crash on non-inlinable final local reads (#15719) Fixes mypyc/mypyc#852. Fixes mypyc/mypyc#990. --- mypyc/irbuild/builder.py | 4 ++-- mypyc/test-data/irbuild-basic.test | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 69f3ad9c495f..f201a4737f89 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -60,7 +60,7 @@ UnionType, get_proper_type, ) -from mypy.util import split_target +from mypy.util import module_prefix, split_target from mypy.visitor import ExpressionVisitor, StatementVisitor from mypyc.common import BITMAP_BITS, SELF_NAME, TEMP_ATTR_NAME from mypyc.crash import catch_errors @@ -1023,7 +1023,7 @@ def emit_load_final( """ if final_var.final_value is not None: # this is safe even for non-native names return self.load_literal_value(final_var.final_value) - elif native: + elif native and module_prefix(self.graph, fullname): return self.load_final_static(fullname, self.mapper.type_to_rtype(typ), line, name) else: return None diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index d6c47814cb7f..cd952ef2ebfd 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3337,6 +3337,32 @@ def foo(z): L0: return 1 +[case testFinalLocals] +from typing import Final + +def inlined() -> str: + # XXX: the final type must be declared explicitly for Var.final_value to be set. + const: Final[str] = "Oppenheimer" + return const + +def local() -> str: + const: Final[str] = inlined() + return const +[out] +def inlined(): + r0, const, r1 :: str +L0: + r0 = 'Oppenheimer' + const = r0 + r1 = 'Oppenheimer' + return r1 +def local(): + r0, const :: str +L0: + r0 = inlined() + const = r0 + return const + [case testDirectlyCall__bool__] class A: def __bool__(self) -> bool: From f19b5d3a026319687dd81a5c7c976698bbe948a8 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Wed, 28 Feb 2024 13:01:10 -0500 Subject: [PATCH 0511/1617] [mypyc] Fix compilation of unreachable comprehensions (#15721) Fixes mypyc/mypyc#816. Admittedly hacky. --- mypyc/irbuild/expression.py | 4 ++++ mypyc/irbuild/for_helpers.py | 29 +++++++++++++++++++++++++++++ mypyc/test-data/run-misc.test | 6 ++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 5cdd9a432a12..81e37953809f 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -80,6 +80,7 @@ from mypyc.irbuild.constant_fold import constant_fold_expr from mypyc.irbuild.for_helpers import ( comprehension_helper, + raise_error_if_contains_unreachable_names, translate_list_comprehension, translate_set_comprehension, ) @@ -1020,6 +1021,9 @@ def transform_set_comprehension(builder: IRBuilder, o: SetComprehension) -> Valu def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value: + if raise_error_if_contains_unreachable_names(builder, o): + return builder.none() + d = builder.maybe_spill(builder.call_c(dict_new_op, [], o.line)) loop_params = list(zip(o.indices, o.sequences, o.condlists, o.is_async)) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 61dbbe960eb2..5d8315e88f72 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -12,10 +12,12 @@ from mypy.nodes import ( ARG_POS, CallExpr, + DictionaryComprehension, Expression, GeneratorExpr, Lvalue, MemberExpr, + NameExpr, RefExpr, SetExpr, TupleExpr, @@ -28,6 +30,7 @@ IntOp, LoadAddress, LoadMem, + RaiseStandardError, Register, TupleGet, TupleSet, @@ -229,6 +232,9 @@ def set_item(item_index: Value) -> None: def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value: + if raise_error_if_contains_unreachable_names(builder, gen): + return builder.none() + # Try simplest list comprehension, otherwise fall back to general one val = sequence_from_generator_preallocate_helper( builder, @@ -251,7 +257,30 @@ def gen_inner_stmts() -> None: return builder.read(list_ops) +def raise_error_if_contains_unreachable_names( + builder: IRBuilder, gen: GeneratorExpr | DictionaryComprehension +) -> bool: + """Raise a runtime error and return True if generator contains unreachable names. + + False is returned if the generator can be safely transformed without crashing. + (It may still be unreachable!) + """ + if any(isinstance(s, NameExpr) and s.node is None for s in gen.indices): + error = RaiseStandardError( + RaiseStandardError.RUNTIME_ERROR, + "mypyc internal error: should be unreachable", + gen.line, + ) + builder.add(error) + return True + + return False + + def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value: + if raise_error_if_contains_unreachable_names(builder, gen): + return builder.none() + set_ops = builder.maybe_spill(builder.new_set_op([], gen.line)) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists, gen.is_async)) diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index f77ba3a1302b..14bb5be979ae 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -1097,8 +1097,10 @@ B = sys.platform == 'x' and sys.foobar C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)] C = sys.platform == 'x' and cast(a, b[c]) C = sys.platform == 'x' and (lambda x: y + x) -# TODO: This still doesn't work -# C = sys.platform == 'x' and (x for y in z) +C = sys.platform == 'x' and (x for y in z) +C = sys.platform == 'x' and [x for y in z] +C = sys.platform == 'x' and {x: x for y in z} +C = sys.platform == 'x' and {x for y in z} assert not A assert not B From 02c50bcbc0ee26ec682c7356a1b3b9ecd9c11a3c Mon Sep 17 00:00:00 2001 From: Riccardo Di Maio <35903974+rdimaio@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:18:33 +0100 Subject: [PATCH 0512/1617] Docs: Update `TypedDict` import statements (#16958) Since Python 3.8, `TypedDict` has been available from the `typing` module. As Python 3.8+ is needed to use mypy (https://github.com/python/mypy/blob/master/setup.py#L12), then it's best for the docs to reflect Python 3.8+ usage. For previous versions, there's already a disclaimer on the page that explains that `typing_extensions` must be used: https://github.com/python/mypy/blob/master/docs/source/typed_dict.rst?plain=1#L102-L110 Co-authored-by: Alex Waygood --- docs/source/common_issues.rst | 4 ++-- docs/source/error_code_list.rst | 8 ++++---- docs/source/generics.rst | 3 +-- docs/source/protocols.rst | 14 +++++--------- docs/source/stubs.rst | 2 +- docs/source/typed_dict.rst | 4 ++-- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 8cc18c863e45..4a1d1b437153 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -541,7 +541,7 @@ Consider this example: .. code-block:: python - from typing_extensions import Protocol + from typing import Protocol class P(Protocol): x: float @@ -561,7 +561,7 @@ the protocol definition: .. code-block:: python - from typing_extensions import Protocol + from typing import Protocol class P(Protocol): @property diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 4decd37e6e8a..48b3b689884f 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -537,7 +537,7 @@ Example: .. code-block:: python - from typing_extensions import TypedDict + from typing import TypedDict class Point(TypedDict): x: int @@ -562,7 +562,7 @@ to have been validated at the point of construction. Example: .. code-block:: python - from typing_extensions import TypedDict + from typing import TypedDict class Point(TypedDict): x: int @@ -868,7 +868,7 @@ the return type affects which lines mypy thinks are reachable after a ``True`` may swallow exceptions. An imprecise return type can result in mysterious errors reported near ``with`` statements. -To fix this, use either ``typing_extensions.Literal[False]`` or +To fix this, use either ``typing.Literal[False]`` or ``None`` as the return type. Returning ``None`` is equivalent to returning ``False`` in this context, since both are treated as false values. @@ -897,7 +897,7 @@ You can use ``Literal[False]`` to fix the error: .. code-block:: python - from typing_extensions import Literal + from typing import Literal class MyContext: ... diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 9ac79f90121d..01ae7534ba93 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -770,8 +770,7 @@ protocols mostly follow the normal rules for generic classes. Example: .. code-block:: python - from typing import TypeVar - from typing_extensions import Protocol + from typing import Protocol, TypeVar T = TypeVar('T') diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 067f4d9dcfac..731562867691 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -68,8 +68,7 @@ class: .. code-block:: python - from typing import Iterable - from typing_extensions import Protocol + from typing import Iterable, Protocol class SupportsClose(Protocol): # Empty method body (explicit '...') @@ -226,8 +225,7 @@ such as trees and linked lists: .. code-block:: python - from typing import TypeVar, Optional - from typing_extensions import Protocol + from typing import TypeVar, Optional, Protocol class TreeLike(Protocol): value: int @@ -255,7 +253,7 @@ rudimentary support for runtime structural checks: .. code-block:: python - from typing_extensions import Protocol, runtime_checkable + from typing import Protocol, runtime_checkable @runtime_checkable class Portable(Protocol): @@ -298,8 +296,7 @@ member: .. code-block:: python - from typing import Optional, Iterable - from typing_extensions import Protocol + from typing import Optional, Iterable, Protocol class Combiner(Protocol): def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ... @@ -323,8 +320,7 @@ a double underscore prefix is used. For example: .. code-block:: python - from typing import Callable, TypeVar - from typing_extensions import Protocol + from typing import Callable, Protocol, TypeVar T = TypeVar('T') diff --git a/docs/source/stubs.rst b/docs/source/stubs.rst index 7c84a9718b3e..c0a3f8b88111 100644 --- a/docs/source/stubs.rst +++ b/docs/source/stubs.rst @@ -114,7 +114,7 @@ For example: .. code-block:: python - from typing_extensions import Protocol + from typing import Protocol class Resource(Protocol): def ok_1(self, foo: list[str] = ...) -> None: ... diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index 19a717d7feb7..e5ce2927db4d 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -25,7 +25,7 @@ dictionary value depends on the key: .. code-block:: python - from typing_extensions import TypedDict + from typing import TypedDict Movie = TypedDict('Movie', {'name': str, 'year': int}) @@ -189,7 +189,7 @@ in Python 3.6 and later: .. code-block:: python - from typing_extensions import TypedDict + from typing import TypedDict # "from typing_extensions" in Python 3.7 and earlier class Movie(TypedDict): name: str From ab0bd8cac3f4183ca0c67ab7f9235c6b515ac2f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:47:00 -0800 Subject: [PATCH 0513/1617] Sync typeshed (#16969) Source commit: https://github.com/python/typeshed/commit/e05098681f326b98c635853a40287ac21f771fa2 --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_ctypes.pyi | 6 +- mypy/typeshed/stdlib/_dummy_thread.pyi | 10 +- mypy/typeshed/stdlib/_lsprof.pyi | 35 ++++ mypy/typeshed/stdlib/_operator.pyi | 12 +- mypy/typeshed/stdlib/_thread.pyi | 10 +- mypy/typeshed/stdlib/abc.pyi | 6 +- mypy/typeshed/stdlib/argparse.pyi | 30 ++- mypy/typeshed/stdlib/asyncio/events.pyi | 16 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 8 +- mypy/typeshed/stdlib/cProfile.pyi | 8 +- mypy/typeshed/stdlib/datetime.pyi | 18 +- mypy/typeshed/stdlib/difflib.pyi | 4 +- mypy/typeshed/stdlib/distutils/sysconfig.pyi | 11 +- mypy/typeshed/stdlib/email/utils.pyi | 6 +- mypy/typeshed/stdlib/functools.pyi | 11 +- .../stdlib/importlib/metadata/__init__.pyi | 24 ++- .../stdlib/importlib/metadata/_meta.pyi | 23 ++- mypy/typeshed/stdlib/itertools.pyi | 16 +- mypy/typeshed/stdlib/numbers.pyi | 171 ++++++++++++------ mypy/typeshed/stdlib/os/__init__.pyi | 21 ++- mypy/typeshed/stdlib/posix.pyi | 4 +- mypy/typeshed/stdlib/queue.pyi | 8 + mypy/typeshed/stdlib/shlex.pyi | 34 +++- mypy/typeshed/stdlib/shutil.pyi | 14 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 73 +++++--- mypy/typeshed/stdlib/sysconfig.pyi | 7 +- mypy/typeshed/stdlib/tarfile.pyi | 3 + mypy/typeshed/stdlib/tkinter/__init__.pyi | 6 +- mypy/typeshed/stdlib/types.pyi | 12 +- mypy/typeshed/stdlib/typing_extensions.pyi | 10 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 6 +- 33 files changed, 437 insertions(+), 191 deletions(-) create mode 100644 mypy/typeshed/stdlib/_lsprof.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index da395f797881..deb940395e1e 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -36,6 +36,7 @@ _heapq: 3.0- _imp: 3.0- _json: 3.0- _locale: 3.0- +_lsprof: 3.0- _markupbase: 3.0- _msi: 3.0- _operator: 3.4- diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index ec3d86e41687..e0cc87814609 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -64,6 +64,7 @@ class _CData(metaclass=_CDataMeta): # Structure.from_buffer(...) # valid at runtime # Structure(...).from_buffer(...) # invalid at runtime # + @classmethod def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... @classmethod @@ -106,14 +107,15 @@ class _CArgObject: ... def byref(obj: _CData, offset: int = ...) -> _CArgObject: ... -_ECT: TypeAlias = Callable[[type[_CData] | None, CFuncPtr, tuple[_CData, ...]], _CData] +_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData] _PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any] class CFuncPtr(_PointerLike, _CData): restype: type[_CData] | Callable[[int], Any] | None argtypes: Sequence[type[_CData]] errcheck: _ECT - _flags_: ClassVar[int] # Abstract attribute that must be defined on subclasses + # Abstract attribute that must be defined on subclasses + _flags_: ClassVar[int] @overload def __init__(self) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/_dummy_thread.pyi b/mypy/typeshed/stdlib/_dummy_thread.pyi index 541096734a91..1182e53c66c3 100644 --- a/mypy/typeshed/stdlib/_dummy_thread.pyi +++ b/mypy/typeshed/stdlib/_dummy_thread.pyi @@ -1,13 +1,19 @@ from collections.abc import Callable from types import TracebackType -from typing import Any, NoReturn +from typing import Any, NoReturn, overload +from typing_extensions import TypeVarTuple, Unpack __all__ = ["error", "start_new_thread", "exit", "get_ident", "allocate_lock", "interrupt_main", "LockType", "RLock"] +_Ts = TypeVarTuple("_Ts") + TIMEOUT_MAX: int error = RuntimeError -def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any] = {}) -> None: ... +@overload +def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> None: ... +@overload +def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> None: ... def exit() -> NoReturn: ... def get_ident() -> int: ... def allocate_lock() -> LockType: ... diff --git a/mypy/typeshed/stdlib/_lsprof.pyi b/mypy/typeshed/stdlib/_lsprof.pyi new file mode 100644 index 000000000000..8a6934162c92 --- /dev/null +++ b/mypy/typeshed/stdlib/_lsprof.pyi @@ -0,0 +1,35 @@ +import sys +from _typeshed import structseq +from collections.abc import Callable +from types import CodeType +from typing import Any, Final, final + +class Profiler: + def __init__( + self, timer: Callable[[], float] | None = None, timeunit: float = 0.0, subcalls: bool = True, builtins: bool = True + ) -> None: ... + def getstats(self) -> list[profiler_entry]: ... + def enable(self, subcalls: bool = True, builtins: bool = True) -> None: ... + def disable(self) -> None: ... + def clear(self) -> None: ... + +@final +class profiler_entry(structseq[Any], tuple[CodeType | str, int, int, float, float, list[profiler_subentry]]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("code", "callcount", "reccallcount", "totaltime", "inlinetime", "calls") + code: CodeType | str + callcount: int + reccallcount: int + totaltime: float + inlinetime: float + calls: list[profiler_subentry] + +@final +class profiler_subentry(structseq[Any], tuple[CodeType | str, int, int, float, float]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("code", "callcount", "reccallcount", "totaltime", "inlinetime") + code: CodeType | str + callcount: int + reccallcount: int + totaltime: float + inlinetime: float diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index acc4a6fb59ca..9b24e086adff 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -95,16 +95,16 @@ def length_hint(__obj: object, __default: int = 0) -> int: ... @final class attrgetter(Generic[_T_co]): @overload - def __new__(cls, attr: str) -> attrgetter[Any]: ... + def __new__(cls, attr: str, /) -> attrgetter[Any]: ... @overload - def __new__(cls, attr: str, __attr2: str) -> attrgetter[tuple[Any, Any]]: ... + def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ... @overload - def __new__(cls, attr: str, __attr2: str, __attr3: str) -> attrgetter[tuple[Any, Any, Any]]: ... + def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ... @overload - def __new__(cls, attr: str, __attr2: str, __attr3: str, __attr4: str) -> attrgetter[tuple[Any, Any, Any, Any]]: ... + def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ... @overload - def __new__(cls, attr: str, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... - def __call__(self, obj: Any) -> _T_co: ... + def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... + def __call__(self, obj: Any, /) -> _T_co: ... @final class itemgetter(Generic[_T_co]): diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index ff9bd1a12eb1..e69f9d2359aa 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -3,7 +3,10 @@ from _typeshed import structseq from collections.abc import Callable from threading import Thread from types import TracebackType -from typing import Any, Final, NoReturn, final +from typing import Any, Final, NoReturn, final, overload +from typing_extensions import TypeVarTuple, Unpack + +_Ts = TypeVarTuple("_Ts") error = RuntimeError @@ -18,7 +21,10 @@ class LockType: self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... -def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any] = ...) -> int: ... +@overload +def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> int: ... +@overload +def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> int: ... def interrupt_main() -> None: ... def exit() -> NoReturn: ... def allocate_lock() -> LockType: ... diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index c642f8b9f123..e4e7f59b58ca 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -3,7 +3,7 @@ import sys from _typeshed import SupportsWrite from collections.abc import Callable from typing import Any, Literal, TypeVar -from typing_extensions import Concatenate, ParamSpec +from typing_extensions import Concatenate, ParamSpec, deprecated _T = TypeVar("_T") _R_co = TypeVar("_R_co", covariant=True) @@ -28,15 +28,17 @@ class ABCMeta(type): def register(cls: ABCMeta, subclass: type[_T]) -> type[_T]: ... def abstractmethod(funcobj: _FuncT) -> _FuncT: ... - +@deprecated("Deprecated, use 'classmethod' with 'abstractmethod' instead") class abstractclassmethod(classmethod[_T, _P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... +@deprecated("Deprecated, use 'staticmethod' with 'abstractmethod' instead") class abstractstaticmethod(staticmethod[_P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[_P, _R_co]) -> None: ... +@deprecated("Deprecated, use 'property' with 'abstractmethod' instead") class abstractproperty(property): __isabstractmethod__: Literal[True] diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 489cc6b16634..c34aca1f8c20 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -3,7 +3,7 @@ from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import Self, TypeAlias, deprecated __all__ = [ "ArgumentParser", @@ -339,11 +339,23 @@ class Action(_AttributeHolder): if sys.version_info >= (3, 12): class BooleanOptionalAction(Action): + @overload def __init__( self, option_strings: Sequence[str], dest: str, - default: _T | str | None = None, + default: bool | None = None, + *, + required: bool = False, + help: str | None = None, + ) -> None: ... + @overload + @deprecated("The `type`, `choices`, and `metavar` parameters are ignored and will be removed in Python 3.14.") + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: _T | bool | None = None, type: Callable[[str], _T] | FileType | None = sentinel, choices: Iterable[_T] | None = sentinel, required: bool = False, @@ -353,11 +365,23 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 9): class BooleanOptionalAction(Action): + @overload + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool | None = None, + *, + required: bool = False, + help: str | None = None, + ) -> None: ... + @overload + @deprecated("The `type`, `choices`, and `metavar` parameters are ignored and will be removed in Python 3.14.") def __init__( self, option_strings: Sequence[str], dest: str, - default: _T | str | None = None, + default: _T | bool | None = None, type: Callable[[str], _T] | FileType | None = None, choices: Iterable[_T] | None = None, required: bool = False, diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 649771df8bf1..16f5296e2125 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -543,10 +543,18 @@ class AbstractEventLoopPolicy: @abstractmethod def new_event_loop(self) -> AbstractEventLoop: ... # Child processes handling (Unix only). - @abstractmethod - def get_child_watcher(self) -> AbstractChildWatcher: ... - @abstractmethod - def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + if sys.version_info >= (3, 12): + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + else: + @abstractmethod + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): def get_event_loop(self) -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 23447ba27aa5..028a7571bb79 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -375,6 +375,8 @@ else: if sys.version_info >= (3, 12): _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co] +elif sys.version_info >= (3, 9): + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co] else: _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co] @@ -382,7 +384,7 @@ else: # While this is true in general, here it's sort-of okay to have a covariant subclass, # since the only reason why `asyncio.Future` is invariant is the `set_result()` method, # and `asyncio.Task.set_result()` always raises. -class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues] +class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] if sys.version_info >= (3, 12): def __init__( self, diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 09f082f2fe48..02e128234dc1 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -437,7 +437,7 @@ class str(Sequence[str]): def capitalize(self) -> str: ... # type: ignore[misc] def casefold(self) -> str: ... # type: ignore[misc] def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - def count(self, x: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + def count(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... @@ -1130,7 +1130,7 @@ class property: class _NotImplementedType(Any): # A little weird, but typing the __call__ as NotImplemented makes the error message # for NotImplemented() much better - __call__: NotImplemented # type: ignore[valid-type] # pyright: ignore[reportGeneralTypeIssues] + __call__: NotImplemented # type: ignore[valid-type] # pyright: ignore[reportInvalidTypeForm] NotImplemented: _NotImplementedType @@ -1544,9 +1544,9 @@ def quit(code: sys._ExitCode = None) -> NoReturn: ... class reversed(Iterator[_T]): @overload - def __init__(self, __sequence: Reversible[_T]) -> None: ... + def __new__(cls, __sequence: Reversible[_T]) -> Iterator[_T]: ... # type: ignore[misc] @overload - def __init__(self, __sequence: SupportsLenAndGetItem[_T]) -> None: ... + def __new__(cls, __sequence: SupportsLenAndGetItem[_T]) -> Iterator[_T]: ... # type: ignore[misc] def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def __length_hint__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index 7d97fa22c394..c212f0383eaf 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -1,3 +1,4 @@ +import _lsprof from _typeshed import StrOrBytesPath, Unused from collections.abc import Callable from types import CodeType @@ -15,13 +16,8 @@ _T = TypeVar("_T") _P = ParamSpec("_P") _Label: TypeAlias = tuple[str, int, str] -class Profile: +class Profile(_lsprof.Profiler): stats: dict[_Label, tuple[int, int, int, int, dict[_Label, tuple[int, int, int, int]]]] # undocumented - def __init__( - self, timer: Callable[[], float] = ..., timeunit: float = ..., subcalls: bool = ..., builtins: bool = ... - ) -> None: ... - def enable(self) -> None: ... - def disable(self) -> None: ... def print_stats(self, sort: str | int = -1) -> None: ... def dump_stats(self, file: StrOrBytesPath) -> None: ... def create_stats(self) -> None: ... diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 54ecddec3a9a..852208cd83a1 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,16 +1,14 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, TypeVar, final, overload -from typing_extensions import Self, TypeAlias +from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, final, overload +from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") elif sys.version_info >= (3, 9): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") -_D = TypeVar("_D", bound=date) - MINYEAR: Literal[1] MAXYEAR: Literal[9999] @@ -90,11 +88,11 @@ class date: def __add__(self, __value: timedelta) -> Self: ... def __radd__(self, __value: timedelta) -> Self: ... @overload - def __sub__(self, __value: timedelta) -> Self: ... - @overload def __sub__(self, __value: datetime) -> NoReturn: ... @overload - def __sub__(self: _D, __value: _D) -> timedelta: ... + def __sub__(self, __value: Self) -> timedelta: ... + @overload + def __sub__(self, __value: timedelta) -> Self: ... def __hash__(self) -> int: ... def weekday(self) -> int: ... def isoweekday(self) -> int: ... @@ -251,10 +249,12 @@ class datetime(date): def fromtimestamp(cls, __timestamp: float, tz: _TzInfo | None = ...) -> Self: ... @classmethod + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.UTC)") def utcfromtimestamp(cls, __t: float) -> Self: ... @classmethod def now(cls, tz: _TzInfo | None = None) -> Self: ... @classmethod + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.UTC)") def utcnow(cls) -> Self: ... @classmethod def combine(cls, date: _Date, time: _Time, tzinfo: _TzInfo | None = ...) -> Self: ... @@ -290,6 +290,6 @@ class datetime(date): def __eq__(self, __value: object) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] - def __sub__(self, __value: timedelta) -> Self: ... + def __sub__(self, __value: Self) -> timedelta: ... @overload - def __sub__(self: _D, __value: _D) -> timedelta: ... + def __sub__(self, __value: timedelta) -> Self: ... diff --git a/mypy/typeshed/stdlib/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi index 894ebaaeca98..d5b77b8f0e2c 100644 --- a/mypy/typeshed/stdlib/difflib.pyi +++ b/mypy/typeshed/stdlib/difflib.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable, Iterable, Iterator, Sequence -from typing import Any, AnyStr, Generic, NamedTuple, TypeVar, overload +from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload if sys.version_info >= (3, 9): from types import GenericAlias @@ -49,7 +49,7 @@ class SequenceMatcher(Generic[_T]): def find_longest_match(self, alo: int, ahi: int, blo: int, bhi: int) -> Match: ... def get_matching_blocks(self) -> list[Match]: ... - def get_opcodes(self) -> list[tuple[str, int, int, int, int]]: ... + def get_opcodes(self) -> list[tuple[Literal["replace", "delete", "insert", "equal"], int, int, int, int]]: ... def get_grouped_opcodes(self, n: int = 3) -> Iterable[list[tuple[str, int, int, int, int]]]: ... def ratio(self) -> float: ... def quick_ratio(self) -> float: ... diff --git a/mypy/typeshed/stdlib/distutils/sysconfig.pyi b/mypy/typeshed/stdlib/distutils/sysconfig.pyi index 464cfb639c6d..e2399a6cf36b 100644 --- a/mypy/typeshed/stdlib/distutils/sysconfig.pyi +++ b/mypy/typeshed/stdlib/distutils/sysconfig.pyi @@ -1,6 +1,8 @@ import sys from collections.abc import Mapping from distutils.ccompiler import CCompiler +from typing import Literal, overload +from typing_extensions import deprecated PREFIX: str EXEC_PREFIX: str @@ -10,8 +12,15 @@ project_base: str python_build: bool def expand_makefile_vars(s: str, vars: Mapping[str, str]) -> str: ... +@overload +@deprecated("SO is deprecated, use EXT_SUFFIX. Support is removed in Python 3.11") +def get_config_var(name: Literal["SO"]) -> int | str | None: ... +@overload def get_config_var(name: str) -> int | str | None: ... -def get_config_vars(*args: str) -> Mapping[str, int | str]: ... +@overload +def get_config_vars() -> dict[str, str | int]: ... +@overload +def get_config_vars(arg: str, /, *args: str) -> list[str | int]: ... def get_config_h_filename() -> str: ... def get_makefile_filename() -> str: ... def get_python_inc(plat_specific: bool = ..., prefix: str | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 186e768050be..0b62647532db 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -4,7 +4,7 @@ from _typeshed import Unused from email import _ParamType from email.charset import Charset from typing import overload -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated __all__ = [ "collapse_rfc2231_value", @@ -54,6 +54,10 @@ def formatdate(timeval: float | None = None, localtime: bool = False, usegmt: bo def format_datetime(dt: datetime.datetime, usegmt: bool = False) -> str: ... if sys.version_info >= (3, 12): + @overload + def localtime(dt: datetime.datetime | None = None) -> datetime.datetime: ... + @overload + @deprecated("The `isdst` parameter does nothing and will be removed in Python 3.14.") def localtime(dt: datetime.datetime | None = None, isdst: Unused = None) -> datetime.datetime: ... else: diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 0f1666024f84..991182486113 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -30,6 +30,7 @@ if sys.version_info >= (3, 9): _AnyCallable: TypeAlias = Callable[..., object] _T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) _S = TypeVar("_S") @overload @@ -171,17 +172,17 @@ class singledispatchmethod(Generic[_T]): def register(self, cls: type[Any], method: Callable[..., _T]) -> Callable[..., _T]: ... def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... -class cached_property(Generic[_T]): - func: Callable[[Any], _T] +class cached_property(Generic[_T_co]): + func: Callable[[Any], _T_co] attrname: str | None - def __init__(self, func: Callable[[Any], _T]) -> None: ... + def __init__(self, func: Callable[[Any], _T_co]) -> None: ... @overload def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T_co: ... def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: ... + def __set__(self, instance: object, value: _T_co) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index eb4db39ebf40..b2fe14777056 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -10,7 +10,7 @@ from os import PathLike from pathlib import Path from re import Pattern from typing import Any, ClassVar, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Self +from typing_extensions import Self, TypeAlias _T = TypeVar("_T") _KT = TypeVar("_KT") @@ -33,9 +33,17 @@ if sys.version_info >= (3, 10): __all__ += ["PackageMetadata", "packages_distributions"] if sys.version_info >= (3, 10): - from importlib.metadata._meta import PackageMetadata as PackageMetadata + from importlib.metadata._meta import PackageMetadata as PackageMetadata, SimplePath def packages_distributions() -> Mapping[str, list[str]]: ... + if sys.version_info >= (3, 12): + # It's generic but shouldn't be + _SimplePath: TypeAlias = SimplePath[Any] + else: + _SimplePath: TypeAlias = SimplePath +else: + _SimplePath: TypeAlias = Path + class PackageNotFoundError(ModuleNotFoundError): @property def name(self) -> str: ... # type: ignore[override] @@ -184,7 +192,7 @@ class Distribution(_distribution_parent): @abc.abstractmethod def read_text(self, filename: str) -> str | None: ... @abc.abstractmethod - def locate_file(self, path: StrPath) -> PathLike[str]: ... + def locate_file(self, path: StrPath) -> _SimplePath: ... @classmethod def from_name(cls, name: str) -> Distribution: ... @overload @@ -233,14 +241,14 @@ class MetadataPathFinder(DistributionFinder): @classmethod def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... if sys.version_info >= (3, 10): - # Yes, this is an instance method that has argumend named "cls" + # Yes, this is an instance method that has a parameter named "cls" def invalidate_caches(cls) -> None: ... class PathDistribution(Distribution): - _path: Path - def __init__(self, path: Path) -> None: ... - def read_text(self, filename: StrPath) -> str: ... - def locate_file(self, path: StrPath) -> PathLike[str]: ... + _path: _SimplePath + def __init__(self, path: _SimplePath) -> None: ... + def read_text(self, filename: StrPath) -> str | None: ... + def locate_file(self, path: StrPath) -> _SimplePath: ... def distribution(distribution_name: str) -> Distribution: ... @overload diff --git a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi index 64fefa9a84e2..3eac226b7065 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi @@ -3,6 +3,7 @@ from collections.abc import Iterator from typing import Any, Protocol, TypeVar, overload _T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) class PackageMetadata(Protocol): def __len__(self) -> int: ... @@ -22,19 +23,27 @@ class PackageMetadata(Protocol): def get(self, name: str, failobj: _T) -> _T | str: ... if sys.version_info >= (3, 12): - class SimplePath(Protocol[_T]): - def joinpath(self) -> _T: ... + class SimplePath(Protocol[_T_co]): + # At runtime this is defined as taking `str | _T`, but that causes trouble. + # See #11436. + def joinpath(self, other: str, /) -> _T_co: ... @property - def parent(self) -> _T: ... + def parent(self) -> _T_co: ... def read_text(self) -> str: ... - def __truediv__(self, other: _T | str) -> _T: ... + # As with joinpath(), this is annotated as taking `str | _T` at runtime. + def __truediv__(self, other: str, /) -> _T_co: ... else: class SimplePath(Protocol): - def joinpath(self) -> SimplePath: ... - def parent(self) -> SimplePath: ... + # Actually takes only self at runtime, but that's clearly wrong + def joinpath(self, other: Any, /) -> SimplePath: ... + # Not defined as a property at runtime, but it should be + @property + def parent(self) -> Any: ... def read_text(self) -> str: ... # There was a bug in `SimplePath` definition in cpython, see #8451 # Strictly speaking `__div__` was defined in 3.10, not __truediv__, # but it should have always been `__truediv__`. - def __truediv__(self) -> SimplePath: ... + # Also, the runtime defines this method as taking no arguments, + # which is obviously wrong. + def __truediv__(self, other: Any, /) -> SimplePath: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 1fa76399444a..0e501e1ade4d 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -220,21 +220,7 @@ class product(Iterator[_T_co]): __iter6: Iterable[_T6], ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload - def __new__( - cls, - __iter1: Iterable[Any], - __iter2: Iterable[Any], - __iter3: Iterable[Any], - __iter4: Iterable[Any], - __iter5: Iterable[Any], - __iter6: Iterable[Any], - __iter7: Iterable[Any], - *iterables: Iterable[Any], - ) -> product[tuple[Any, ...]]: ... - @overload - def __new__(cls, *iterables: Iterable[_T1], repeat: int) -> product[tuple[_T1, ...]]: ... - @overload - def __new__(cls, *iterables: Iterable[Any], repeat: int = ...) -> product[tuple[Any, ...]]: ... + def __new__(cls, *iterables: Iterable[_T1], repeat: int = 1) -> product[tuple[_T1, ...]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi index 9f507d8335cf..e129de2cdc67 100644 --- a/mypy/typeshed/stdlib/numbers.pyi +++ b/mypy/typeshed/stdlib/numbers.pyi @@ -1,27 +1,62 @@ # Note: these stubs are incomplete. The more complex type # signatures are currently omitted. # -# Use SupportsComplex, SupportsFloat and SupportsIndex for return types in this module +# Use _ComplexLike, _RealLike and _IntegralLike for return types in this module # rather than `numbers.Complex`, `numbers.Real` and `numbers.Integral`, # to avoid an excessive number of `type: ignore`s in subclasses of these ABCs # (since type checkers don't see `complex` as a subtype of `numbers.Complex`, # nor `float` as a subtype of `numbers.Real`, etc.) -import sys from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Literal, SupportsFloat, SupportsIndex, overload -from typing_extensions import TypeAlias +from typing import Literal, Protocol, overload -if sys.version_info >= (3, 11): - from typing import SupportsComplex as _SupportsComplex -else: - # builtins.complex didn't have a __complex__ method on older Pythons - import typing +__all__ = ["Number", "Complex", "Real", "Rational", "Integral"] - _SupportsComplex: TypeAlias = typing.SupportsComplex | complex +############################ +# Protocols for return types +############################ -__all__ = ["Number", "Complex", "Real", "Rational", "Integral"] +# `_ComplexLike` is a structural-typing approximation +# of the `Complex` ABC, which is not (and cannot be) a protocol +# +# NOTE: We can't include `__complex__` here, +# as we want `int` to be seen as a subtype of `_ComplexLike`, +# and `int.__complex__` does not exist :( +class _ComplexLike(Protocol): + def __neg__(self) -> _ComplexLike: ... + def __pos__(self) -> _ComplexLike: ... + def __abs__(self) -> _RealLike: ... + +# _RealLike is a structural-typing approximation +# of the `Real` ABC, which is not (and cannot be) a protocol +class _RealLike(_ComplexLike, Protocol): + def __trunc__(self) -> _IntegralLike: ... + def __floor__(self) -> _IntegralLike: ... + def __ceil__(self) -> _IntegralLike: ... + def __float__(self) -> float: ... + # Overridden from `_ComplexLike` + # for a more precise return type: + def __neg__(self) -> _RealLike: ... + def __pos__(self) -> _RealLike: ... + +# _IntegralLike is a structural-typing approximation +# of the `Integral` ABC, which is not (and cannot be) a protocol +class _IntegralLike(_RealLike, Protocol): + def __invert__(self) -> _IntegralLike: ... + def __int__(self) -> int: ... + def __index__(self) -> int: ... + # Overridden from `_ComplexLike` + # for a more precise return type: + def __abs__(self) -> _IntegralLike: ... + # Overridden from `RealLike` + # for a more precise return type: + def __neg__(self) -> _IntegralLike: ... + def __pos__(self) -> _IntegralLike: ... + +################# +# Module "proper" +################# class Number(metaclass=ABCMeta): @abstractmethod @@ -29,126 +64,146 @@ class Number(metaclass=ABCMeta): # See comment at the top of the file # for why some of these return types are purposefully vague -class Complex(Number): +class Complex(Number, _ComplexLike): @abstractmethod def __complex__(self) -> complex: ... def __bool__(self) -> bool: ... @property @abstractmethod - def real(self) -> SupportsFloat: ... + def real(self) -> _RealLike: ... @property @abstractmethod - def imag(self) -> SupportsFloat: ... + def imag(self) -> _RealLike: ... @abstractmethod - def __add__(self, other) -> _SupportsComplex: ... + def __add__(self, other) -> _ComplexLike: ... @abstractmethod - def __radd__(self, other) -> _SupportsComplex: ... + def __radd__(self, other) -> _ComplexLike: ... @abstractmethod - def __neg__(self) -> _SupportsComplex: ... + def __neg__(self) -> _ComplexLike: ... @abstractmethod - def __pos__(self) -> _SupportsComplex: ... - def __sub__(self, other) -> _SupportsComplex: ... - def __rsub__(self, other) -> _SupportsComplex: ... + def __pos__(self) -> _ComplexLike: ... + def __sub__(self, other) -> _ComplexLike: ... + def __rsub__(self, other) -> _ComplexLike: ... @abstractmethod - def __mul__(self, other) -> _SupportsComplex: ... + def __mul__(self, other) -> _ComplexLike: ... @abstractmethod - def __rmul__(self, other) -> _SupportsComplex: ... + def __rmul__(self, other) -> _ComplexLike: ... @abstractmethod - def __truediv__(self, other) -> _SupportsComplex: ... + def __truediv__(self, other) -> _ComplexLike: ... @abstractmethod - def __rtruediv__(self, other) -> _SupportsComplex: ... + def __rtruediv__(self, other) -> _ComplexLike: ... @abstractmethod - def __pow__(self, exponent) -> _SupportsComplex: ... + def __pow__(self, exponent) -> _ComplexLike: ... @abstractmethod - def __rpow__(self, base) -> _SupportsComplex: ... + def __rpow__(self, base) -> _ComplexLike: ... @abstractmethod - def __abs__(self) -> SupportsFloat: ... + def __abs__(self) -> _RealLike: ... @abstractmethod - def conjugate(self) -> _SupportsComplex: ... + def conjugate(self) -> _ComplexLike: ... @abstractmethod def __eq__(self, other: object) -> bool: ... # See comment at the top of the file # for why some of these return types are purposefully vague -class Real(Complex, SupportsFloat): +class Real(Complex, _RealLike): @abstractmethod def __float__(self) -> float: ... @abstractmethod - def __trunc__(self) -> SupportsIndex: ... + def __trunc__(self) -> _IntegralLike: ... @abstractmethod - def __floor__(self) -> SupportsIndex: ... + def __floor__(self) -> _IntegralLike: ... @abstractmethod - def __ceil__(self) -> SupportsIndex: ... + def __ceil__(self) -> _IntegralLike: ... @abstractmethod @overload - def __round__(self, ndigits: None = None) -> SupportsIndex: ... + def __round__(self, ndigits: None = None) -> _IntegralLike: ... @abstractmethod @overload - def __round__(self, ndigits: int) -> SupportsFloat: ... - def __divmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... - def __rdivmod__(self, other) -> tuple[SupportsFloat, SupportsFloat]: ... + def __round__(self, ndigits: int) -> _RealLike: ... + def __divmod__(self, other) -> tuple[_RealLike, _RealLike]: ... + def __rdivmod__(self, other) -> tuple[_RealLike, _RealLike]: ... @abstractmethod - def __floordiv__(self, other) -> SupportsFloat: ... + def __floordiv__(self, other) -> _RealLike: ... @abstractmethod - def __rfloordiv__(self, other) -> SupportsFloat: ... + def __rfloordiv__(self, other) -> _RealLike: ... @abstractmethod - def __mod__(self, other) -> SupportsFloat: ... + def __mod__(self, other) -> _RealLike: ... @abstractmethod - def __rmod__(self, other) -> SupportsFloat: ... + def __rmod__(self, other) -> _RealLike: ... @abstractmethod def __lt__(self, other) -> bool: ... @abstractmethod def __le__(self, other) -> bool: ... def __complex__(self) -> complex: ... @property - def real(self) -> SupportsFloat: ... + def real(self) -> _RealLike: ... @property def imag(self) -> Literal[0]: ... - def conjugate(self) -> SupportsFloat: ... # type: ignore[override] + def conjugate(self) -> _RealLike: ... + # Not actually overridden at runtime, + # but we override these in the stub to give them more precise return types: + @abstractmethod + def __pos__(self) -> _RealLike: ... + @abstractmethod + def __neg__(self) -> _RealLike: ... # See comment at the top of the file # for why some of these return types are purposefully vague class Rational(Real): @property @abstractmethod - def numerator(self) -> SupportsIndex: ... + def numerator(self) -> _IntegralLike: ... @property @abstractmethod - def denominator(self) -> SupportsIndex: ... + def denominator(self) -> _IntegralLike: ... def __float__(self) -> float: ... # See comment at the top of the file # for why some of these return types are purposefully vague -class Integral(Rational): +class Integral(Rational, _IntegralLike): @abstractmethod def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent, modulus: Incomplete | None = None) -> SupportsIndex: ... # type: ignore[override] + def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ... @abstractmethod - def __lshift__(self, other) -> SupportsIndex: ... + def __lshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rlshift__(self, other) -> SupportsIndex: ... + def __rlshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rshift__(self, other) -> SupportsIndex: ... + def __rshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __rrshift__(self, other) -> SupportsIndex: ... + def __rrshift__(self, other) -> _IntegralLike: ... @abstractmethod - def __and__(self, other) -> SupportsIndex: ... + def __and__(self, other) -> _IntegralLike: ... @abstractmethod - def __rand__(self, other) -> SupportsIndex: ... + def __rand__(self, other) -> _IntegralLike: ... @abstractmethod - def __xor__(self, other) -> SupportsIndex: ... + def __xor__(self, other) -> _IntegralLike: ... @abstractmethod - def __rxor__(self, other) -> SupportsIndex: ... + def __rxor__(self, other) -> _IntegralLike: ... @abstractmethod - def __or__(self, other) -> SupportsIndex: ... + def __or__(self, other) -> _IntegralLike: ... @abstractmethod - def __ror__(self, other) -> SupportsIndex: ... + def __ror__(self, other) -> _IntegralLike: ... @abstractmethod - def __invert__(self) -> SupportsIndex: ... + def __invert__(self) -> _IntegralLike: ... def __float__(self) -> float: ... @property - def numerator(self) -> SupportsIndex: ... + def numerator(self) -> _IntegralLike: ... @property def denominator(self) -> Literal[1]: ... + # Not actually overridden at runtime, + # but we override these in the stub to give them more precise return types: + @abstractmethod + def __pos__(self) -> _IntegralLike: ... + @abstractmethod + def __neg__(self) -> _IntegralLike: ... + @abstractmethod + def __abs__(self) -> _IntegralLike: ... + @abstractmethod + @overload + def __round__(self, ndigits: None = None) -> _IntegralLike: ... + @abstractmethod + @overload + def __round__(self, ndigits: int) -> _IntegralLike: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index b57678635c07..eef52e7a8b3b 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -40,7 +40,7 @@ from typing import ( overload, runtime_checkable, ) -from typing_extensions import Self, TypeAlias, Unpack +from typing_extensions import Self, TypeAlias, Unpack, deprecated from . import path as _path @@ -308,7 +308,8 @@ if sys.platform != "win32": EX_NOPERM: int EX_CONFIG: int -if sys.platform != "win32" and sys.platform != "darwin": +# Exists on some Unix platforms, e.g. Solaris. +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": EX_NOTFOUND: int P_NOWAIT: int @@ -361,8 +362,16 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo @property def st_mtime(self) -> float: ... # time of most recent content modification, # platform dependent (time of most recent metadata change on Unix, or the time of creation on Windows) - @property - def st_ctime(self) -> float: ... + if sys.version_info >= (3, 12) and sys.platform == "win32": + @property + @deprecated( + "Use st_birthtime instead to retrieve the file creation time. In the future, this property will contain the last metadata change time." + ) + def st_ctime(self) -> float: ... + else: + @property + def st_ctime(self) -> float: ... + @property def st_atime_ns(self) -> int: ... # time of most recent access, in nanoseconds @property @@ -860,8 +869,8 @@ if sys.platform != "win32": def abort() -> NoReturn: ... # These are defined as execl(file, *args) but the first *arg is mandatory. -def execl(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: StrOrBytesPath) -> NoReturn: ... -def execlp(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: StrOrBytesPath) -> NoReturn: ... +def execl(file: StrOrBytesPath, *args: Unpack[tuple[StrOrBytesPath, Unpack[tuple[StrOrBytesPath, ...]]]]) -> NoReturn: ... +def execlp(file: StrOrBytesPath, *args: Unpack[tuple[StrOrBytesPath, Unpack[tuple[StrOrBytesPath, ...]]]]) -> NoReturn: ... # These are: execle(file, *args, env) but env is pulled from the last element of the args. def execle( diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 6cba003bbd5f..b31b8f3d3524 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -239,9 +239,11 @@ if sys.platform != "win32": if sys.platform != "linux": from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod + if sys.platform != "linux" and sys.platform != "darwin": + from os import EX_NOTFOUND as EX_NOTFOUND + if sys.platform != "darwin": from os import ( - EX_NOTFOUND as EX_NOTFOUND, POSIX_FADV_DONTNEED as POSIX_FADV_DONTNEED, POSIX_FADV_NOREUSE as POSIX_FADV_NOREUSE, POSIX_FADV_NORMAL as POSIX_FADV_NORMAL, diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi index 3537e445ed97..d7cae5f2ac79 100644 --- a/mypy/typeshed/stdlib/queue.pyi +++ b/mypy/typeshed/stdlib/queue.pyi @@ -12,6 +12,9 @@ _T = TypeVar("_T") class Empty(Exception): ... class Full(Exception): ... +if sys.version_info >= (3, 13): + class ShutDown(Exception): ... + class Queue(Generic[_T]): maxsize: int @@ -20,6 +23,8 @@ class Queue(Generic[_T]): not_full: Condition # undocumented all_tasks_done: Condition # undocumented unfinished_tasks: int # undocumented + if sys.version_info >= (3, 13): + is_shutdown: bool # undocumented # Despite the fact that `queue` has `deque` type, # we treat it as `Any` to allow different implementations in subtypes. queue: Any # undocumented @@ -29,6 +34,9 @@ class Queue(Generic[_T]): def full(self) -> bool: ... def get(self, block: bool = True, timeout: float | None = None) -> _T: ... def get_nowait(self) -> _T: ... + if sys.version_info >= (3, 13): + def shutdown(self, immediate: bool = False) -> None: ... + def _get(self) -> _T: ... def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ... def put_nowait(self, item: _T) -> None: ... diff --git a/mypy/typeshed/stdlib/shlex.pyi b/mypy/typeshed/stdlib/shlex.pyi index 3fda03b5694a..daa8df439b26 100644 --- a/mypy/typeshed/stdlib/shlex.pyi +++ b/mypy/typeshed/stdlib/shlex.pyi @@ -1,13 +1,32 @@ +import sys +from collections import deque from collections.abc import Iterable -from typing import TextIO -from typing_extensions import Self +from io import TextIOWrapper +from typing import Literal, Protocol, overload, type_check_only +from typing_extensions import Self, deprecated __all__ = ["shlex", "split", "quote", "join"] -def split(s: str, comments: bool = False, posix: bool = True) -> list[str]: ... +@type_check_only +class _ShlexInstream(Protocol): + def read(self, size: Literal[1], /) -> str: ... + def readline(self) -> object: ... + def close(self) -> object: ... + +if sys.version_info >= (3, 12): + def split(s: str | _ShlexInstream, comments: bool = False, posix: bool = True) -> list[str]: ... + +else: + @overload + def split(s: str | _ShlexInstream, comments: bool = False, posix: bool = True) -> list[str]: ... + @overload + @deprecated("Passing None for 's' to shlex.split() is deprecated and will raise an error in Python 3.12.") + def split(s: None, comments: bool = False, posix: bool = True) -> list[str]: ... + def join(split_command: Iterable[str]) -> str: ... def quote(s: str) -> str: ... +# TODO: Make generic over infile once PEP 696 is implemented. class shlex(Iterable[str]): commenters: str wordchars: str @@ -17,17 +36,18 @@ class shlex(Iterable[str]): escapedquotes: str whitespace_split: bool infile: str | None - instream: TextIO + instream: _ShlexInstream source: str debug: int lineno: int token: str + filestack: deque[tuple[str | None, _ShlexInstream, int]] eof: str | None @property def punctuation_chars(self) -> str: ... def __init__( self, - instream: str | TextIO | None = None, + instream: str | _ShlexInstream | None = None, infile: str | None = None, posix: bool = False, punctuation_chars: bool | str = False, @@ -35,8 +55,8 @@ class shlex(Iterable[str]): def get_token(self) -> str | None: ... def push_token(self, tok: str) -> None: ... def read_token(self) -> str | None: ... - def sourcehook(self, newfile: str) -> tuple[str, TextIO] | None: ... - def push_source(self, newstream: str | TextIO, newfile: str | None = None) -> None: ... + def sourcehook(self, newfile: str) -> tuple[str, TextIOWrapper] | None: ... + def push_source(self, newstream: str | _ShlexInstream, newfile: str | None = None) -> None: ... def pop_source(self) -> None: ... def error_leader(self, infile: str | None = None, lineno: int | None = None) -> str: ... def __iter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index f6440aa27513..a06181ce876d 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -4,7 +4,7 @@ from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, from collections.abc import Callable, Iterable, Sequence from tarfile import _TarfileFilter from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated __all__ = [ "copyfileobj", @@ -78,24 +78,20 @@ class _RmtreeType(Protocol): avoids_symlink_attacks: bool if sys.version_info >= (3, 12): @overload + def __call__(self, path: StrOrBytesPath, ignore_errors: bool = False, *, dir_fd: int | None = None) -> None: ... + @overload + @deprecated("The `onerror` parameter is deprecated and will be removed in Python 3.14. Use `onexc` instead.") def __call__( self, path: StrOrBytesPath, ignore_errors: bool = False, onerror: _OnErrorCallback | None = None, *, - onexc: None = None, dir_fd: int | None = None, ) -> None: ... @overload def __call__( - self, - path: StrOrBytesPath, - ignore_errors: bool = False, - onerror: None = None, - *, - onexc: _OnExcCallback, - dir_fd: int | None = None, + self, path: StrOrBytesPath, ignore_errors: bool = False, *, onexc: _OnExcCallback, dir_fd: int | None = None ) -> None: ... elif sys.version_info >= (3, 11): def __call__( diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 659545c50b41..7cf75bbc33c5 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -221,16 +221,32 @@ def adapt(__obj: Any, __proto: Any) -> Any: ... @overload def adapt(__obj: Any, __proto: Any, __alt: _T) -> Any | _T: ... def complete_statement(statement: str) -> bool: ... -def connect( - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., -) -> Connection: ... + +if sys.version_info >= (3, 12): + def connect( + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + autocommit: bool = ..., + ) -> Connection: ... + +else: + def connect( + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + ) -> Connection: ... + def enable_callback_tracebacks(__enable: bool) -> None: ... if sys.version_info < (3, 12): @@ -300,17 +316,32 @@ class Connection: def autocommit(self, val: int) -> None: ... row_factory: Any text_factory: Any - def __init__( - self, - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - ) -> None: ... + if sys.version_info >= (3, 12): + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + autocommit: bool = ..., + ) -> None: ... + else: + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + ) -> None: ... + def close(self) -> None: ... if sys.version_info >= (3, 11): def blobopen(self, __table: str, __column: str, __row: int, *, readonly: bool = False, name: str = "main") -> Blob: ... diff --git a/mypy/typeshed/stdlib/sysconfig.pyi b/mypy/typeshed/stdlib/sysconfig.pyi index 2edb71d78cdd..807a979050e8 100644 --- a/mypy/typeshed/stdlib/sysconfig.pyi +++ b/mypy/typeshed/stdlib/sysconfig.pyi @@ -1,5 +1,6 @@ import sys from typing import IO, Any, Literal, overload +from typing_extensions import deprecated __all__ = [ "get_config_h_filename", @@ -15,11 +16,15 @@ __all__ = [ "parse_config_h", ] +@overload +@deprecated("SO is deprecated, use EXT_SUFFIX. Support is removed in Python 3.11") +def get_config_var(name: Literal["SO"]) -> Any: ... +@overload def get_config_var(name: str) -> Any: ... @overload def get_config_vars() -> dict[str, Any]: ... @overload -def get_config_vars(arg: str, *args: str) -> list[Any]: ... +def get_config_vars(arg: str, /, *args: str) -> list[Any]: ... def get_scheme_names() -> tuple[str, ...]: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 0bfd91ce2161..47c831190286 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -292,6 +292,8 @@ class TarFile: def getnames(self) -> _list[str]: ... def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ... def next(self) -> TarInfo | None: ... + # Calling this method without `filter` is deprecated, but it may be set either on the class or in an + # individual call, so we can't mark it as @deprecated here. def extractall( self, path: StrOrBytesPath = ".", @@ -300,6 +302,7 @@ class TarFile: numeric_owner: bool = False, filter: _TarfileFilter | None = ..., ) -> None: ... + # Same situation as for `extractall`. def extract( self, member: str | TarInfo, diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 4733c31b5bae..3f65eb2c8fe4 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -665,7 +665,7 @@ class Wm: iconmask = wm_iconmask def wm_iconname(self, newName: Incomplete | None = None) -> str: ... iconname = wm_iconname - def wm_iconphoto(self, default: bool, __image1: _PhotoImageLike | str, *args: _PhotoImageLike | str) -> None: ... + def wm_iconphoto(self, default: bool, image1: _PhotoImageLike | str, /, *args: _PhotoImageLike | str) -> None: ... iconphoto = wm_iconphoto def wm_iconposition(self, x: int | None = None, y: int | None = None) -> tuple[int, int] | None: ... iconposition = wm_iconposition @@ -1721,7 +1721,9 @@ class Canvas(Widget, XView, YView): def tag_raise(self, __first: str | int, __second: str | int | None = ...) -> None: ... def tkraise(self, __first: str | int, __second: str | int | None = ...) -> None: ... # type: ignore[override] def lift(self, __first: str | int, __second: str | int | None = ...) -> None: ... # type: ignore[override] - def scale(self, *args) -> None: ... + def scale( + self, __tagOrId: str | int, __xOrigin: _ScreenUnits, __yOrigin: _ScreenUnits, __xScale: float, __yScale: float + ) -> None: ... def scan_mark(self, x, y) -> None: ... def scan_dragto(self, x, y, gain: int = 10) -> None: ... def select_adjust(self, tagOrId, index) -> None: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 05ffc2143233..05c5e85e4a9e 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -17,7 +17,7 @@ from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping from typing import Any, ClassVar, Literal, Mapping, Protocol, TypeVar, final, overload # noqa: Y022 -from typing_extensions import ParamSpec, Self, TypeVarTuple +from typing_extensions import ParamSpec, Self, TypeVarTuple, deprecated __all__ = [ "FunctionType", @@ -138,8 +138,14 @@ class CodeType: def co_name(self) -> str: ... @property def co_firstlineno(self) -> int: ... - @property - def co_lnotab(self) -> bytes: ... + if sys.version_info >= (3, 10): + @property + @deprecated("Will be removed in Python 3.14. Use the co_lines() method instead.") + def co_lnotab(self) -> bytes: ... + else: + @property + def co_lnotab(self) -> bytes: ... + @property def co_freevars(self) -> tuple[str, ...]: ... @property diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index ea5c7b21aa87..921c1334cfe4 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -182,6 +182,7 @@ __all__ = [ "no_type_check", "no_type_check_decorator", "ReadOnly", + "TypeIs", ] _T = typing.TypeVar("_T") @@ -220,10 +221,14 @@ def IntVar(name: str) -> Any: ... # returns a new TypeVar class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): __required_keys__: ClassVar[frozenset[str]] __optional_keys__: ClassVar[frozenset[str]] - __readonly_keys__: ClassVar[frozenset[str]] - __mutable_keys__: ClassVar[frozenset[str]] __total__: ClassVar[bool] __orig_bases__: ClassVar[tuple[Any, ...]] + # PEP 705 + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] + # PEP 728 + __closed__: ClassVar[bool] + __extra_items__: ClassVar[Any] def copy(self) -> Self: ... # Using Never so that only calls using mypy plugin hook that specialize the signature # can go through. @@ -501,3 +506,4 @@ class Doc: def __eq__(self, other: object) -> bool: ... ReadOnly: _SpecialForm +TypeIs: _SpecialForm diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index c508f72892c1..2a363a504dec 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -3,7 +3,7 @@ from _collections_abc import dict_keys from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence from typing import Any, Literal, SupportsIndex, TypeVar, overload -from typing_extensions import TypeAlias, TypeGuard +from typing_extensions import TypeAlias, TypeGuard, deprecated __all__ = [ "C14NWriterTarget", @@ -121,6 +121,10 @@ class Element: def __setitem__(self, __key: SupportsIndex, __value: Element) -> None: ... @overload def __setitem__(self, __key: slice, __value: Iterable[Element]) -> None: ... + + # Doesn't really exist in earlier versions, where __len__ is called implicitly instead + @deprecated("Testing an element's truth value is deprecated.") + def __bool__(self) -> bool: ... if sys.version_info < (3, 9): def getchildren(self) -> list[Element]: ... def getiterator(self, tag: str | None = None) -> list[Element]: ... From 055184f28dd83f20ba36cadbb7a75e5f30cf36d8 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 29 Feb 2024 22:02:54 -0800 Subject: [PATCH 0514/1617] Fix override checking for decorated property (#16856) Fixes #16855 --- mypy/checker.py | 39 +++++++++++++++----------- test-data/unit/check-functions.test | 43 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9f987cb5ccdf..a272a3aaac3d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2020,22 +2020,29 @@ def check_method_override_for_base_with_name( if original_node and is_property(original_node): original_type = get_property_type(original_type) - if isinstance(typ, FunctionLike) and is_property(defn): - typ = get_property_type(typ) - if ( - isinstance(original_node, Var) - and not original_node.is_final - and (not original_node.is_property or original_node.is_settable_property) - and isinstance(defn, Decorator) - ): - # We only give an error where no other similar errors will be given. - if not isinstance(original_type, AnyType): - self.msg.fail( - "Cannot override writeable attribute with read-only property", - # Give an error on function line to match old behaviour. - defn.func, - code=codes.OVERRIDE, - ) + if is_property(defn): + inner: FunctionLike | None + if isinstance(typ, FunctionLike): + inner = typ + else: + inner = self.extract_callable_type(typ, context) + if inner is not None: + typ = inner + typ = get_property_type(typ) + if ( + isinstance(original_node, Var) + and not original_node.is_final + and (not original_node.is_property or original_node.is_settable_property) + and isinstance(defn, Decorator) + ): + # We only give an error where no other similar errors will be given. + if not isinstance(original_type, AnyType): + self.msg.fail( + "Cannot override writeable attribute with read-only property", + # Give an error on function line to match old behaviour. + defn.func, + code=codes.OVERRIDE, + ) if isinstance(original_type, AnyType) or isinstance(typ, AnyType): pass diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index b3df5fddafba..3aecbe065c27 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2730,6 +2730,49 @@ f: Callable[[Sequence[TI]], None] g: Callable[[Union[Sequence[TI], Sequence[TS]]], None] f = g +[case testOverrideDecoratedProperty] +class Base: + @property + def foo(self) -> int: ... + +class decorator: + def __init__(self, fn): + self.fn = fn + def __call__(self, decorated_self) -> int: + return self.fn(decorated_self) + +class Child(Base): + @property + @decorator + def foo(self) -> int: + return 42 + +reveal_type(Child().foo) # N: Revealed type is "builtins.int" + +class BadChild1(Base): + @decorator + def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "Base" \ + # N: Superclass: \ + # N: int \ + # N: Subclass: \ + # N: decorator + return 42 + +class not_a_decorator: + def __init__(self, fn): ... + +class BadChild2(Base): + @property + @not_a_decorator + def foo(self) -> int: # E: "not_a_decorator" not callable \ + # E: Signature of "foo" incompatible with supertype "Base" \ + # N: Superclass: \ + # N: int \ + # N: Subclass: \ + # N: not_a_decorator + return 42 +[builtins fixtures/property.pyi] + [case explicitOverride] # flags: --python-version 3.12 from typing import override From 3c87af272cbf7c49699b7508c7f51365da139c05 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:08:22 +0100 Subject: [PATCH 0515/1617] Allow TypedDict initialization from Type (#16963) Fixes #11644 --- mypy/checkexpr.py | 2 ++ test-data/unit/check-typeddict.test | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2842606b7b18..e893410e2b7d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1855,6 +1855,8 @@ def analyze_type_type_callee(self, item: ProperType, context: Context) -> Type: # We support Type of namedtuples but not of tuples in general if isinstance(item, TupleType) and tuple_fallback(item).type.fullname != "builtins.tuple": return self.analyze_type_type_callee(tuple_fallback(item), context) + if isinstance(item, TypedDictType): + return self.typeddict_callable_from_context(item) self.msg.unsupported_type_type(item, context) return AnyType(TypeOfAny.from_error) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index adf4d210ed0c..639be7bde8d8 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3447,3 +3447,19 @@ class Params(TypedDict("Params", {'x': int})): p: Params = {'x': 2} reveal_type(p) # N: Revealed type is "TypedDict('__main__.Params', {'x': builtins.int})" [builtins fixtures/dict.pyi] + +[case testInitTypedDictFromType] +from typing import TypedDict, Type + +class Point(TypedDict): + x: int + y: int + +def func(cls: Type[Point]) -> None: + reveal_type(cls) # N: Revealed type is "Type[TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})]" + cls(x=1, y=2) + cls(1, 2) # E: Too many positional arguments + cls(x=1) # E: Missing named argument "y" + cls(x=1, y=2, error="") # E: Unexpected keyword argument "error" +[typing fixtures/typing-full.pyi] +[builtins fixtures/tuple.pyi] From bcb3747f277745f7a0035bb92cc95ee83f6778dc Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 1 Mar 2024 06:01:34 -0800 Subject: [PATCH 0516/1617] Implement TypeIs (PEP 742) (#16898) Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- docs/source/error_code_list2.rst | 16 + mypy/applytype.py | 7 +- mypy/checker.py | 42 +- mypy/checkexpr.py | 13 +- mypy/constraints.py | 16 +- mypy/errorcodes.py | 6 + mypy/expandtype.py | 2 + mypy/fixup.py | 2 + mypy/message_registry.py | 5 +- mypy/messages.py | 4 + mypy/nodes.py | 3 + mypy/semanal.py | 7 + mypy/subtypes.py | 13 + mypy/typeanal.py | 27 +- mypy/types.py | 9 + test-data/unit/check-errorcodes.test | 8 + test-data/unit/check-typeguard.test | 2 +- test-data/unit/check-typeis.test | 798 ++++++++++++++++++ test-data/unit/lib-stub/typing_extensions.pyi | 1 + 19 files changed, 962 insertions(+), 19 deletions(-) create mode 100644 test-data/unit/check-typeis.test diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index c966fe1f7ea6..465d1c7a6583 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -555,3 +555,19 @@ Correct usage: When this code is enabled, using ``reveal_locals`` is always an error, because there's no way one can import it. + +.. _code-narrowed-type-not-subtype: + +Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] +--------------------------------------------------------------- + +:pep:`742` requires that when ``TypeIs`` is used, the narrowed +type must be a subtype of the original type:: + + from typing_extensions import TypeIs + + def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int + ... + + def g(x: object) -> TypeIs[str]: # OK + ... diff --git a/mypy/applytype.py b/mypy/applytype.py index e14906fa2772..eecd555bf90d 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -137,11 +137,15 @@ def apply_generic_arguments( arg_types=[expand_type(at, id_to_type) for at in callable.arg_types] ) - # Apply arguments to TypeGuard if any. + # Apply arguments to TypeGuard and TypeIs if any. if callable.type_guard is not None: type_guard = expand_type(callable.type_guard, id_to_type) else: type_guard = None + if callable.type_is is not None: + type_is = expand_type(callable.type_is, id_to_type) + else: + type_is = None # The callable may retain some type vars if only some were applied. # TODO: move apply_poly() logic from checkexpr.py here when new inference @@ -164,4 +168,5 @@ def apply_generic_arguments( ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, type_guard=type_guard, + type_is=type_is, ) diff --git a/mypy/checker.py b/mypy/checker.py index a272a3aaac3d..941dc06f1c71 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1203,6 +1203,22 @@ def check_func_def( # visible from *inside* of this function/method. ref_type: Type | None = self.scope.active_self_type() + if typ.type_is: + arg_index = 0 + # For methods and classmethods, we want the second parameter + if ref_type is not None and (not defn.is_static or defn.name == "__new__"): + arg_index = 1 + if arg_index < len(typ.arg_types) and not is_subtype( + typ.type_is, typ.arg_types[arg_index] + ): + self.fail( + message_registry.NARROWED_TYPE_NOT_SUBTYPE.format( + format_type(typ.type_is, self.options), + format_type(typ.arg_types[arg_index], self.options), + ), + item, + ) + # Store argument types. for i in range(len(typ.arg_types)): arg_type = typ.arg_types[i] @@ -2178,6 +2194,8 @@ def check_override( elif isinstance(original, CallableType) and isinstance(override, CallableType): if original.type_guard is not None and override.type_guard is None: fail = True + if original.type_is is not None and override.type_is is None: + fail = True if is_private(name): fail = False @@ -5643,7 +5661,7 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap: def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]: """Find any isinstance checks (within a chain of ands). Includes implicit and explicit checks for None and calls to callable. - Also includes TypeGuard functions. + Also includes TypeGuard and TypeIs functions. Return value is a map of variables to their types if the condition is true and a map of variables to their types if the condition is false. @@ -5695,7 +5713,7 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1: return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0]) elif isinstance(node.callee, RefExpr): - if node.callee.type_guard is not None: + if node.callee.type_guard is not None or node.callee.type_is is not None: # TODO: Follow *args, **kwargs if node.arg_kinds[0] != nodes.ARG_POS: # the first argument might be used as a kwarg @@ -5721,7 +5739,12 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM # we want the idx-th variable to be narrowed expr = collapse_walrus(node.args[idx]) else: - self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node) + kind = ( + "guard" if node.callee.type_guard is not None else "narrower" + ) + self.fail( + message_registry.TYPE_GUARD_POS_ARG_REQUIRED.format(kind), node + ) return {}, {} if literal(expr) == LITERAL_TYPE: # Note: we wrap the target type, so that we can special case later. @@ -5729,7 +5752,18 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM # considered "always right" (i.e. even if the types are not overlapping). # Also note that a care must be taken to unwrap this back at read places # where we use this to narrow down declared type. - return {expr: TypeGuardedType(node.callee.type_guard)}, {} + if node.callee.type_guard is not None: + return {expr: TypeGuardedType(node.callee.type_guard)}, {} + else: + assert node.callee.type_is is not None + return conditional_types_to_typemaps( + expr, + *self.conditional_types_with_intersection( + self.lookup_type(expr), + [TypeRange(node.callee.type_is, is_upper_bound=False)], + expr, + ), + ) elif isinstance(node, ComparisonExpr): # Step 1: Obtain the types of each operand and whether or not we can # narrow their types. (For example, we shouldn't try narrowing the diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e893410e2b7d..37a90ce55b9e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1451,13 +1451,12 @@ def check_call_expr_with_callee_type( object_type=object_type, ) proper_callee = get_proper_type(callee_type) - if ( - isinstance(e.callee, RefExpr) - and isinstance(proper_callee, CallableType) - and proper_callee.type_guard is not None - ): + if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType): # Cache it for find_isinstance_check() - e.callee.type_guard = proper_callee.type_guard + if proper_callee.type_guard is not None: + e.callee.type_guard = proper_callee.type_guard + if proper_callee.type_is is not None: + e.callee.type_is = proper_callee.type_is return ret_type def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str) -> Type: @@ -5283,7 +5282,7 @@ def infer_lambda_type_using_context( # is a constructor -- but this fallback doesn't make sense for lambdas. callable_ctx = callable_ctx.copy_modified(fallback=self.named_type("builtins.function")) - if callable_ctx.type_guard is not None: + if callable_ctx.type_guard is not None or callable_ctx.type_is is not None: # Lambda's return type cannot be treated as a `TypeGuard`, # because it is implicit. And `TypeGuard`s must be explicit. # See https://github.com/python/mypy/issues/9927 diff --git a/mypy/constraints.py b/mypy/constraints.py index c4eba2ca1ede..cdfa39ac45f3 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1018,10 +1018,22 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: param_spec = template.param_spec() template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type - if template.type_guard is not None: + if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard - if cactual.type_guard is not None: cactual_ret_type = cactual.type_guard + elif template.type_guard is not None: + template_ret_type = AnyType(TypeOfAny.special_form) + elif cactual.type_guard is not None: + cactual_ret_type = AnyType(TypeOfAny.special_form) + + if template.type_is is not None and cactual.type_is is not None: + template_ret_type = template.type_is + cactual_ret_type = cactual.type_is + elif template.type_is is not None: + template_ret_type = AnyType(TypeOfAny.special_form) + elif cactual.type_is is not None: + cactual_ret_type = AnyType(TypeOfAny.special_form) + res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) if param_spec is None: diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 72ee63a6a897..688bd6a4ddd5 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -281,5 +281,11 @@ def __hash__(self) -> int: sub_code_of=MISC, ) +NARROWED_TYPE_NOT_SUBTYPE: Final[ErrorCode] = ErrorCode( + "narrowed-type-not-subtype", + "Warn if a TypeIs function's narrowed type is not a subtype of the original type", + "General", +) + # This copy will not include any error codes defined later in the plugins. mypy_error_codes = error_codes.copy() diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 3bf45854b2a0..ec6a2ecfd0d2 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -351,6 +351,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: arg_names=t.arg_names[:-2] + repl.arg_names, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), + type_is=(t.type_is.accept(self) if t.type_is is not None else None), imprecise_arg_kinds=(t.imprecise_arg_kinds or repl.imprecise_arg_kinds), variables=[*repl.variables, *t.variables], ) @@ -384,6 +385,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: arg_types=arg_types, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None), + type_is=(t.type_is.accept(self) if t.type_is is not None else None), ) if needs_normalization: return expanded.with_normalized_var_args() diff --git a/mypy/fixup.py b/mypy/fixup.py index 02c6ab93f29e..849a6483d724 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -270,6 +270,8 @@ def visit_callable_type(self, ct: CallableType) -> None: arg.accept(self) if ct.type_guard is not None: ct.type_guard.accept(self) + if ct.type_is is not None: + ct.type_is.accept(self) def visit_overloaded(self, t: Overloaded) -> None: for ct in t.items: diff --git a/mypy/message_registry.py b/mypy/message_registry.py index fb430b63c74b..ccc1443dacf0 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -262,7 +262,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: CONTIGUOUS_ITERABLE_EXPECTED: Final = ErrorMessage("Contiguous iterable with same type expected") ITERABLE_TYPE_EXPECTED: Final = ErrorMessage("Invalid type '{}' for *expr (iterable expected)") -TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type guard requires positional argument") +TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type {} requires positional argument") # Match Statement MISSING_MATCH_ARGS: Final = 'Class "{}" doesn\'t define "__match_args__"' @@ -324,3 +324,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: ARG_NAME_EXPECTED_STRING_LITERAL: Final = ErrorMessage( "Expected string literal for argument name, got {}", codes.SYNTAX ) +NARROWED_TYPE_NOT_SUBTYPE: Final = ErrorMessage( + "Narrowed type {} is not a subtype of input type {}", codes.NARROWED_TYPE_NOT_SUBTYPE +) diff --git a/mypy/messages.py b/mypy/messages.py index db6c91ba9008..92b57ef781a2 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2643,6 +2643,8 @@ def format_literal_value(typ: LiteralType) -> str: elif isinstance(func, CallableType): if func.type_guard is not None: return_type = f"TypeGuard[{format(func.type_guard)}]" + elif func.type_is is not None: + return_type = f"TypeIs[{format(func.type_is)}]" else: return_type = format(func.ret_type) if func.is_ellipsis_args: @@ -2859,6 +2861,8 @@ 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, options)}]" + elif tp.type_is is not None: + s += f"TypeIs[{format_type_bare(tp.type_is, options)}]" else: s += format_type_bare(tp.ret_type, options) diff --git a/mypy/nodes.py b/mypy/nodes.py index 1c781320580a..bb278d92392d 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1755,6 +1755,7 @@ class RefExpr(Expression): "is_inferred_def", "is_alias_rvalue", "type_guard", + "type_is", ) def __init__(self) -> None: @@ -1776,6 +1777,8 @@ def __init__(self) -> None: self.is_alias_rvalue = False # Cache type guard from callable_type.type_guard self.type_guard: mypy.types.Type | None = None + # And same for TypeIs + self.type_is: mypy.types.Type | None = None @property def fullname(self) -> str: diff --git a/mypy/semanal.py b/mypy/semanal.py index 38d5ddec0818..6bf02382a036 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -881,6 +881,13 @@ def analyze_func_def(self, defn: FuncDef) -> None: ) # in this case, we just kind of just ... remove the type guard. result = result.copy_modified(type_guard=None) + if result.type_is and ARG_POS not in result.arg_kinds[skip_self:]: + self.fail( + '"TypeIs" functions must have a positional argument', + result, + code=codes.VALID_TYPE, + ) + result = result.copy_modified(type_is=None) result = self.remove_unpack_kwargs(defn, result) if has_self_type and self.type is not None: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 2d536f892a2a..4d5e7335b14f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -683,10 +683,23 @@ def visit_callable_type(self, left: CallableType) -> bool: if left.type_guard is not None and right.type_guard is not None: if not self._is_subtype(left.type_guard, right.type_guard): return False + elif left.type_is is not None and right.type_is is not None: + # For TypeIs we have to check both ways; it is unsafe to pass + # a TypeIs[Child] when a TypeIs[Parent] is expected, because + # if the narrower returns False, we assume that the narrowed value is + # *not* a Parent. + if not self._is_subtype(left.type_is, right.type_is) or not self._is_subtype( + right.type_is, left.type_is + ): + return False elif right.type_guard is not None and left.type_guard is None: # This means that one function has `TypeGuard` and other does not. # They are not compatible. See https://github.com/python/mypy/issues/11307 return False + elif right.type_is is not None and left.type_is is None: + # Similarly, if one function has `TypeIs` and the other does not, + # they are not compatible. + return False return is_callable_compatible( left, right, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 9cc0114df333..8a9ac8f4ac31 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -668,7 +668,10 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ ) return AnyType(TypeOfAny.from_error) return RequiredType(self.anal_type(t.args[0]), required=False) - elif self.anal_type_guard_arg(t, fullname) is not None: + elif ( + self.anal_type_guard_arg(t, fullname) is not None + or self.anal_type_is_arg(t, fullname) is not None + ): # In most contexts, TypeGuard[...] acts as an alias for bool (ignoring its args) return self.named_type("builtins.bool") elif fullname in ("typing.Unpack", "typing_extensions.Unpack"): @@ -986,7 +989,8 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: variables = t.variables else: variables, _ = self.bind_function_type_variables(t, t) - special = self.anal_type_guard(t.ret_type) + type_guard = self.anal_type_guard(t.ret_type) + type_is = self.anal_type_is(t.ret_type) arg_kinds = t.arg_kinds if len(arg_kinds) >= 2 and arg_kinds[-2] == ARG_STAR and arg_kinds[-1] == ARG_STAR2: arg_types = self.anal_array(t.arg_types[:-2], nested=nested) + [ @@ -1041,7 +1045,8 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: # its type will be the falsey FakeInfo fallback=(t.fallback if t.fallback.type else self.named_type("builtins.function")), variables=self.anal_var_defs(variables), - type_guard=special, + type_guard=type_guard, + type_is=type_is, unpack_kwargs=unpacked_kwargs, ) return ret @@ -1064,6 +1069,22 @@ def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None: return self.anal_type(t.args[0]) return None + def anal_type_is(self, t: Type) -> Type | None: + if isinstance(t, UnboundType): + sym = self.lookup_qualified(t.name, t) + if sym is not None and sym.node is not None: + return self.anal_type_is_arg(t, sym.node.fullname) + # TODO: What if it's an Instance? Then use t.type.fullname? + return None + + def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: + if fullname in ("typing_extensions.TypeIs", "typing.TypeIs"): + if len(t.args) != 1: + self.fail("TypeIs must have exactly one type argument", t, code=codes.VALID_TYPE) + return AnyType(TypeOfAny.from_error) + return self.anal_type(t.args[0]) + return None + def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: """Analyze signature argument type for *args and **kwargs argument.""" if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: diff --git a/mypy/types.py b/mypy/types.py index f76e35784d8f..b34efde15b31 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1800,6 +1800,7 @@ class CallableType(FunctionLike): "def_extras", # Information about original definition we want to serialize. # This is used for more detailed error messages. "type_guard", # T, if -> TypeGuard[T] (ret_type is bool in this case). + "type_is", # T, if -> TypeIs[T] (ret_type is bool in this case). "from_concatenate", # whether this callable is from a concatenate object # (this is used for error messages) "imprecise_arg_kinds", @@ -1826,6 +1827,7 @@ def __init__( bound_args: Sequence[Type | None] = (), def_extras: dict[str, Any] | None = None, type_guard: Type | None = None, + type_is: Type | None = None, from_concatenate: bool = False, imprecise_arg_kinds: bool = False, unpack_kwargs: bool = False, @@ -1875,6 +1877,7 @@ def __init__( else: self.def_extras = {} self.type_guard = type_guard + self.type_is = type_is self.unpack_kwargs = unpack_kwargs def copy_modified( @@ -1896,6 +1899,7 @@ def copy_modified( bound_args: Bogus[list[Type | None]] = _dummy, def_extras: Bogus[dict[str, Any]] = _dummy, type_guard: Bogus[Type | None] = _dummy, + type_is: Bogus[Type | None] = _dummy, from_concatenate: Bogus[bool] = _dummy, imprecise_arg_kinds: Bogus[bool] = _dummy, unpack_kwargs: Bogus[bool] = _dummy, @@ -1920,6 +1924,7 @@ def copy_modified( bound_args=bound_args if bound_args is not _dummy else self.bound_args, def_extras=def_extras if def_extras is not _dummy else dict(self.def_extras), type_guard=type_guard if type_guard is not _dummy else self.type_guard, + type_is=type_is if type_is is not _dummy else self.type_is, from_concatenate=( from_concatenate if from_concatenate is not _dummy else self.from_concatenate ), @@ -2233,6 +2238,7 @@ def serialize(self) -> JsonDict: "bound_args": [(None if t is None else t.serialize()) for t in self.bound_args], "def_extras": dict(self.def_extras), "type_guard": self.type_guard.serialize() if self.type_guard is not None else None, + "type_is": (self.type_is.serialize() if self.type_is is not None else None), "from_concatenate": self.from_concatenate, "imprecise_arg_kinds": self.imprecise_arg_kinds, "unpack_kwargs": self.unpack_kwargs, @@ -2257,6 +2263,7 @@ def deserialize(cls, data: JsonDict) -> CallableType: type_guard=( deserialize_type(data["type_guard"]) if data["type_guard"] is not None else None ), + type_is=(deserialize_type(data["type_is"]) if data["type_is"] is not None else None), from_concatenate=data["from_concatenate"], imprecise_arg_kinds=data["imprecise_arg_kinds"], unpack_kwargs=data["unpack_kwargs"], @@ -3315,6 +3322,8 @@ def visit_callable_type(self, t: CallableType) -> str: if not isinstance(get_proper_type(t.ret_type), NoneType): if t.type_guard is not None: s += f" -> TypeGuard[{t.type_guard.accept(self)}]" + elif t.type_is is not None: + s += f" -> TypeIs[{t.type_is.accept(self)}]" else: s += f" -> {t.ret_type.accept(self)}" diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 7f5f05d37595..9d49480539e0 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1182,3 +1182,11 @@ class D(C): def other(self) -> None: self.bad2: int = 5 # E: Covariant override of a mutable attribute (base class "C" defined the type as "float", expression has type "int") [mutable-override] [builtins fixtures/property.pyi] + +[case testNarrowedTypeNotSubtype] +from typing_extensions import TypeIs + +def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of input type "str" [narrowed-type-not-subtype] + pass + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index c48887bb016a..66c21bf3abe1 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -504,7 +504,7 @@ def with_bool(o: object) -> bool: pass accepts_typeguard(with_bool_typeguard) accepts_typeguard(with_str_typeguard) -accepts_typeguard(with_bool) # E: Argument 1 to "accepts_typeguard" has incompatible type "Callable[[object], bool]"; expected "Callable[[object], TypeGuard[bool]]" +accepts_typeguard(with_bool) # E: Argument 1 to "accepts_typeguard" has incompatible type "Callable[[object], bool]"; expected "Callable[[object], TypeGuard[Never]]" [builtins fixtures/tuple.pyi] [case testTypeGuardAsOverloadedFunctionArg] diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test new file mode 100644 index 000000000000..04b64a45c8c1 --- /dev/null +++ b/test-data/unit/check-typeis.test @@ -0,0 +1,798 @@ +[case testTypeIsBasic] +from typing_extensions import TypeIs +class Point: pass +def is_point(a: object) -> TypeIs[Point]: pass +def main(a: object) -> None: + if is_point(a): + reveal_type(a) # N: Revealed type is "__main__.Point" + else: + reveal_type(a) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] + +[case testTypeIsElif] +from typing_extensions import TypeIs +from typing import Union +class Point: pass +def is_point(a: object) -> TypeIs[Point]: pass +class Line: pass +def is_line(a: object) -> TypeIs[Line]: pass +def main(a: Union[Point, Line, int]) -> None: + if is_point(a): + reveal_type(a) # N: Revealed type is "__main__.Point" + elif is_line(a): + reveal_type(a) # N: Revealed type is "__main__.Line" + else: + reveal_type(a) # N: Revealed type is "builtins.int" + +[builtins fixtures/tuple.pyi] + +[case testTypeIsTypeArgsNone] +from typing_extensions import TypeIs +def foo(a: object) -> TypeIs: # E: TypeIs must have exactly one type argument + pass +[builtins fixtures/tuple.pyi] + +[case testTypeIsTypeArgsTooMany] +from typing_extensions import TypeIs +def foo(a: object) -> TypeIs[int, int]: # E: TypeIs must have exactly one type argument + pass +[builtins fixtures/tuple.pyi] + +[case testTypeIsTypeArgType] +from typing_extensions import TypeIs +def foo(a: object) -> TypeIs[42]: # E: Invalid type: try using Literal[42] instead? + pass +[builtins fixtures/tuple.pyi] + +[case testTypeIsRepr] +from typing_extensions import TypeIs +def foo(a: object) -> TypeIs[int]: + pass +reveal_type(foo) # N: Revealed type is "def (a: builtins.object) -> TypeIs[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsCallArgsNone] +from typing_extensions import TypeIs +class Point: pass + +def is_point() -> TypeIs[Point]: pass # E: "TypeIs" functions must have a positional argument +def main(a: object) -> None: + if is_point(): + reveal_type(a) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] + +[case testTypeIsCallArgsMultiple] +from typing_extensions import TypeIs +class Point: pass +def is_point(a: object, b: object) -> TypeIs[Point]: pass +def main(a: object, b: object) -> None: + if is_point(a, b): + reveal_type(a) # N: Revealed type is "__main__.Point" + reveal_type(b) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] + +[case testTypeIsIsBool] +from typing_extensions import TypeIs +def f(a: TypeIs[int]) -> None: pass +reveal_type(f) # N: Revealed type is "def (a: builtins.bool)" +a: TypeIs[int] +reveal_type(a) # N: Revealed type is "builtins.bool" +class C: + a: TypeIs[int] +reveal_type(C().a) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithTypeVar] +from typing import TypeVar, Tuple, Type +from typing_extensions import TypeIs +T = TypeVar('T') +def is_tuple_of_type(a: Tuple[object, ...], typ: Type[T]) -> TypeIs[Tuple[T, ...]]: pass +def main(a: Tuple[object, ...]): + if is_tuple_of_type(a, int): + reveal_type(a) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsUnionIn] +from typing import Union +from typing_extensions import TypeIs +def is_foo(a: Union[int, str]) -> TypeIs[str]: pass +def main(a: Union[str, int]) -> None: + if is_foo(a): + reveal_type(a) # N: Revealed type is "builtins.str" + else: + reveal_type(a) # N: Revealed type is "builtins.int" + reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsUnionOut] +from typing import Union +from typing_extensions import TypeIs +def is_foo(a: object) -> TypeIs[Union[int, str]]: pass +def main(a: object) -> None: + if is_foo(a): + reveal_type(a) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsNonzeroFloat] +from typing_extensions import TypeIs +def is_nonzero(a: object) -> TypeIs[float]: pass +def main(a: int): + if is_nonzero(a): + reveal_type(a) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeIsHigherOrder] +from typing import Callable, TypeVar, Iterable, List +from typing_extensions import TypeIs +T = TypeVar('T') +R = TypeVar('R') +def filter(f: Callable[[T], TypeIs[R]], it: Iterable[T]) -> Iterable[R]: pass +def is_float(a: object) -> TypeIs[float]: pass +a: List[object] = ["a", 0, 0.0] +b = filter(is_float, a) +reveal_type(b) # N: Revealed type is "typing.Iterable[builtins.float]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsMethod] +from typing_extensions import TypeIs +class C: + def main(self, a: object) -> None: + if self.is_float(a): + reveal_type(self) # N: Revealed type is "__main__.C" + reveal_type(a) # N: Revealed type is "builtins.float" + def is_float(self, a: object) -> TypeIs[float]: pass +[builtins fixtures/tuple.pyi] + +[case testTypeIsCrossModule] +import guard +from points import Point +def main(a: object) -> None: + if guard.is_point(a): + reveal_type(a) # N: Revealed type is "points.Point" +[file guard.py] +from typing_extensions import TypeIs +import points +def is_point(a: object) -> TypeIs[points.Point]: pass +[file points.py] +class Point: pass +[builtins fixtures/tuple.pyi] + +[case testTypeIsBodyRequiresBool] +from typing_extensions import TypeIs +def is_float(a: object) -> TypeIs[float]: + return "not a bool" # E: Incompatible return value type (got "str", expected "bool") +[builtins fixtures/tuple.pyi] + +[case testTypeIsNarrowToTypedDict] +from typing import Mapping, TypedDict +from typing_extensions import TypeIs +class User(TypedDict): + name: str + id: int +def is_user(a: Mapping[str, object]) -> TypeIs[User]: + return isinstance(a.get("name"), str) and isinstance(a.get("id"), int) +def main(a: Mapping[str, object]) -> None: + if is_user(a): + reveal_type(a) # N: Revealed type is "TypedDict('__main__.User', {'name': builtins.str, 'id': builtins.int})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypeIsInAssert] +from typing_extensions import TypeIs +def is_float(a: object) -> TypeIs[float]: pass +def main(a: object) -> None: + assert is_float(a) + reveal_type(a) # N: Revealed type is "builtins.float" +[builtins fixtures/tuple.pyi] + +[case testTypeIsFromAny] +from typing import Any +from typing_extensions import TypeIs +def is_objfloat(a: object) -> TypeIs[float]: pass +def is_anyfloat(a: Any) -> TypeIs[float]: pass +def objmain(a: object) -> None: + if is_objfloat(a): + reveal_type(a) # N: Revealed type is "builtins.float" + if is_anyfloat(a): + reveal_type(a) # N: Revealed type is "builtins.float" +def anymain(a: Any) -> None: + if is_objfloat(a): + reveal_type(a) # N: Revealed type is "builtins.float" + if is_anyfloat(a): + reveal_type(a) # N: Revealed type is "builtins.float" +[builtins fixtures/tuple.pyi] + +[case testTypeIsNegatedAndElse] +from typing import Union +from typing_extensions import TypeIs +def is_int(a: object) -> TypeIs[int]: pass +def is_str(a: object) -> TypeIs[str]: pass +def intmain(a: Union[int, str]) -> None: + if not is_int(a): + reveal_type(a) # N: Revealed type is "builtins.str" + else: + reveal_type(a) # N: Revealed type is "builtins.int" +def strmain(a: Union[int, str]) -> None: + if is_str(a): + reveal_type(a) # N: Revealed type is "builtins.str" + else: + reveal_type(a) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeIsClassMethod] +from typing_extensions import TypeIs +class C: + @classmethod + def is_float(cls, a: object) -> TypeIs[float]: pass + def method(self, a: object) -> None: + if self.is_float(a): + reveal_type(a) # N: Revealed type is "builtins.float" +def main(a: object) -> None: + if C.is_float(a): + reveal_type(a) # N: Revealed type is "builtins.float" +[builtins fixtures/classmethod.pyi] + +[case testTypeIsRequiresPositionalArgs] +from typing_extensions import TypeIs +def is_float(a: object, b: object = 0) -> TypeIs[float]: pass +def main1(a: object) -> None: + if is_float(a=a, b=1): + reveal_type(a) # N: Revealed type is "builtins.float" + + if is_float(b=1, a=a): + reveal_type(a) # N: Revealed type is "builtins.float" + +[builtins fixtures/tuple.pyi] + +[case testTypeIsOverload] +from typing import overload, Any, Callable, Iterable, Iterator, List, Optional, TypeVar +from typing_extensions import TypeIs + +T = TypeVar("T") +R = TypeVar("R") + +@overload +def filter(f: Callable[[T], TypeIs[R]], it: Iterable[T]) -> Iterator[R]: ... +@overload +def filter(f: Callable[[T], bool], it: Iterable[T]) -> Iterator[T]: ... +def filter(*args): pass + +def is_int_typeis(a: object) -> TypeIs[int]: pass +def is_int_bool(a: object) -> bool: pass + +def main(a: List[Optional[int]]) -> None: + bb = filter(lambda x: x is not None, a) + reveal_type(bb) # N: Revealed type is "typing.Iterator[Union[builtins.int, None]]" + # Also, if you replace 'bool' with 'Any' in the second overload, bb is Iterator[Any] + cc = filter(is_int_typeis, a) + reveal_type(cc) # N: Revealed type is "typing.Iterator[builtins.int]" + dd = filter(is_int_bool, a) + reveal_type(dd) # N: Revealed type is "typing.Iterator[Union[builtins.int, None]]" + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeIsDecorated] +from typing import TypeVar +from typing_extensions import TypeIs +T = TypeVar("T") +def decorator(f: T) -> T: pass +@decorator +def is_float(a: object) -> TypeIs[float]: + pass +def main(a: object) -> None: + if is_float(a): + reveal_type(a) # N: Revealed type is "builtins.float" +[builtins fixtures/tuple.pyi] + +[case testTypeIsMethodOverride] +from typing_extensions import TypeIs +class C: + def is_float(self, a: object) -> TypeIs[float]: pass +class D(C): + def is_float(self, a: object) -> bool: pass # Fail +[builtins fixtures/tuple.pyi] +[out] +main:5: error: Signature of "is_float" incompatible with supertype "C" +main:5: note: Superclass: +main:5: note: def is_float(self, a: object) -> TypeIs[float] +main:5: note: Subclass: +main:5: note: def is_float(self, a: object) -> bool + +[case testTypeIsInAnd] +from typing import Any +from typing_extensions import TypeIs +def isclass(a: object) -> bool: + pass +def isfloat(a: object) -> TypeIs[float]: + pass +def isstr(a: object) -> TypeIs[str]: + pass + +def coverage1(obj: Any) -> bool: + if isfloat(obj) and obj.__self__ is not None and isclass(obj.__self__): # E: "float" has no attribute "__self__" + reveal_type(obj) # N: Revealed type is "builtins.float" + return True + reveal_type(obj) # N: Revealed type is "Any" + return False + +def coverage2(obj: Any) -> bool: + if not (isfloat(obj) or isstr(obj)): + reveal_type(obj) # N: Revealed type is "Any" + return True + reveal_type(obj) # N: Revealed type is "Union[builtins.float, builtins.str]" + return False +[builtins fixtures/classmethod.pyi] + +[case testAssignToTypeIsedVariable1] +from typing_extensions import TypeIs + +class A: pass +class B(A): pass + +def guard(a: A) -> TypeIs[B]: + pass + +a = A() +if not guard(a): + a = A() +[builtins fixtures/tuple.pyi] + +[case testAssignToTypeIsedVariable2] +from typing_extensions import TypeIs + +class A: pass +class B: pass + +def guard(a: object) -> TypeIs[B]: + pass + +a = A() +if not guard(a): + a = A() +[builtins fixtures/tuple.pyi] + +[case testAssignToTypeIsedVariable3] +from typing_extensions import TypeIs + +class A: pass +class B: pass + +def guard(a: object) -> TypeIs[B]: + pass + +a = A() +if guard(a): + reveal_type(a) # N: Revealed type is "__main__." + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + reveal_type(a) # N: Revealed type is "__main__." + a = A() + reveal_type(a) # N: Revealed type is "__main__.A" +reveal_type(a) # N: Revealed type is "__main__.A" +[builtins fixtures/tuple.pyi] + +[case testTypeIsNestedRestrictionAny] +from typing_extensions import TypeIs +from typing import Any + +class A: ... +def f(x: object) -> TypeIs[A]: ... +def g(x: object) -> None: ... + +def test(x: Any) -> None: + if not(f(x) or x): + return + g(reveal_type(x)) # N: Revealed type is "Union[__main__.A, Any]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsNestedRestrictionUnionOther] +from typing_extensions import TypeIs +from typing import Any + +class A: ... +class B: ... +def f(x: object) -> TypeIs[A]: ... +def f2(x: object) -> TypeIs[B]: ... +def g(x: object) -> None: ... + +def test(x: object) -> None: + if not(f(x) or f2(x)): + return + g(reveal_type(x)) # N: Revealed type is "Union[__main__.A, __main__.B]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsComprehensionSubtype] +from typing import List +from typing_extensions import TypeIs + +class Base: ... +class Foo(Base): ... +class Bar(Base): ... + +def is_foo(item: object) -> TypeIs[Foo]: + return isinstance(item, Foo) + +def is_bar(item: object) -> TypeIs[Bar]: + return isinstance(item, Bar) + +def foobar(items: List[object]): + a: List[Base] = [x for x in items if is_foo(x) or is_bar(x)] + b: List[Base] = [x for x in items if is_foo(x)] + c: List[Foo] = [x for x in items if is_foo(x)] + d: List[Bar] = [x for x in items if is_foo(x)] # E: List comprehension has incompatible type List[Foo]; expected List[Bar] +[builtins fixtures/tuple.pyi] + +[case testTypeIsNestedRestrictionUnionIsInstance] +from typing_extensions import TypeIs +from typing import Any, List + +class A: ... +def f(x: List[Any]) -> TypeIs[List[str]]: ... +def g(x: object) -> None: ... + +def test(x: List[Any]) -> None: + if not(f(x) or isinstance(x, A)): + return + g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsMultipleCondition] +from typing_extensions import TypeIs +from typing import Any, List + +class Foo: ... +class Bar: ... + +def is_foo(item: object) -> TypeIs[Foo]: + return isinstance(item, Foo) + +def is_bar(item: object) -> TypeIs[Bar]: + return isinstance(item, Bar) + +def foobar(x: object): + if not isinstance(x, Foo) or not isinstance(x, Bar): + return + reveal_type(x) # N: Revealed type is "__main__." + +def foobar_typeis(x: object): + if not is_foo(x) or not is_bar(x): + return + # Looks like a typo but this is what our unique name generation produces + reveal_type(x) # N: Revealed type is "__main__.1" +[builtins fixtures/tuple.pyi] + +[case testTypeIsAsFunctionArgAsBoolSubtype] +from typing import Callable +from typing_extensions import TypeIs + +def accepts_bool(f: Callable[[object], bool]): pass + +def with_bool_typeis(o: object) -> TypeIs[bool]: pass +def with_str_typeis(o: object) -> TypeIs[str]: pass +def with_bool(o: object) -> bool: pass + +accepts_bool(with_bool_typeis) +accepts_bool(with_str_typeis) +accepts_bool(with_bool) +[builtins fixtures/tuple.pyi] + +[case testTypeIsAsFunctionArg] +from typing import Callable +from typing_extensions import TypeIs + +def accepts_typeis(f: Callable[[object], TypeIs[bool]]): pass +def different_typeis(f: Callable[[object], TypeIs[str]]): pass + +def with_typeis(o: object) -> TypeIs[bool]: pass +def with_bool(o: object) -> bool: pass + +accepts_typeis(with_typeis) +accepts_typeis(with_bool) # E: Argument 1 to "accepts_typeis" has incompatible type "Callable[[object], bool]"; expected "Callable[[object], TypeIs[bool]]" + +different_typeis(with_typeis) # E: Argument 1 to "different_typeis" has incompatible type "Callable[[object], TypeIs[bool]]"; expected "Callable[[object], TypeIs[str]]" +different_typeis(with_bool) # E: Argument 1 to "different_typeis" has incompatible type "Callable[[object], bool]"; expected "Callable[[object], TypeIs[str]]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsAsGenericFunctionArg] +from typing import Callable, TypeVar +from typing_extensions import TypeIs + +T = TypeVar('T') + +def accepts_typeis(f: Callable[[object], TypeIs[T]]): pass + +def with_bool_typeis(o: object) -> TypeIs[bool]: pass +def with_str_typeis(o: object) -> TypeIs[str]: pass +def with_bool(o: object) -> bool: pass + +accepts_typeis(with_bool_typeis) +accepts_typeis(with_str_typeis) +accepts_typeis(with_bool) # E: Argument 1 to "accepts_typeis" has incompatible type "Callable[[object], bool]"; expected "Callable[[object], TypeIs[Never]]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsAsOverloadedFunctionArg] +# https://github.com/python/mypy/issues/11307 +from typing import Callable, TypeVar, Generic, Any, overload +from typing_extensions import TypeIs + +_T = TypeVar('_T') + +class filter(Generic[_T]): + @overload + def __init__(self, function: Callable[[object], TypeIs[_T]]) -> None: pass + @overload + def __init__(self, function: Callable[[_T], Any]) -> None: pass + def __init__(self, function): pass + +def is_int_typeis(a: object) -> TypeIs[int]: pass +def returns_bool(a: object) -> bool: pass + +reveal_type(filter(is_int_typeis)) # N: Revealed type is "__main__.filter[builtins.int]" +reveal_type(filter(returns_bool)) # N: Revealed type is "__main__.filter[builtins.object]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsSubtypingVariance] +from typing import Callable +from typing_extensions import TypeIs + +class A: pass +class B(A): pass +class C(B): pass + +def accepts_typeis(f: Callable[[object], TypeIs[B]]): pass + +def with_typeis_a(o: object) -> TypeIs[A]: pass +def with_typeis_b(o: object) -> TypeIs[B]: pass +def with_typeis_c(o: object) -> TypeIs[C]: pass + +accepts_typeis(with_typeis_a) # E: Argument 1 to "accepts_typeis" has incompatible type "Callable[[object], TypeIs[A]]"; expected "Callable[[object], TypeIs[B]]" +accepts_typeis(with_typeis_b) +accepts_typeis(with_typeis_c) # E: Argument 1 to "accepts_typeis" has incompatible type "Callable[[object], TypeIs[C]]"; expected "Callable[[object], TypeIs[B]]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithIdentityGeneric] +from typing import TypeVar +from typing_extensions import TypeIs + +_T = TypeVar("_T") + +def identity(val: _T) -> TypeIs[_T]: + pass + +def func1(name: _T): + reveal_type(name) # N: Revealed type is "_T`-1" + if identity(name): + reveal_type(name) # N: Revealed type is "_T`-1" + +def func2(name: str): + reveal_type(name) # N: Revealed type is "builtins.str" + if identity(name): + reveal_type(name) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithGenericOnSecondParam] +from typing import TypeVar +from typing_extensions import TypeIs + +_R = TypeVar("_R") + +def guard(val: object, param: _R) -> TypeIs[_R]: + pass + +def func1(name: object): + reveal_type(name) # N: Revealed type is "builtins.object" + if guard(name, name): + reveal_type(name) # N: Revealed type is "builtins.object" + if guard(name, 1): + reveal_type(name) # N: Revealed type is "builtins.int" + +def func2(name: int): + reveal_type(name) # N: Revealed type is "builtins.int" + if guard(name, True): + reveal_type(name) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithGenericInstance] +from typing import TypeVar, List, Iterable +from typing_extensions import TypeIs + +_T = TypeVar("_T") + +def is_list_of_str(val: Iterable[_T]) -> TypeIs[List[_T]]: + pass + +def func(name: Iterable[str]): + reveal_type(name) # N: Revealed type is "typing.Iterable[builtins.str]" + if is_list_of_str(name): + reveal_type(name) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithTupleGeneric] +from typing import TypeVar, Tuple +from typing_extensions import TypeIs + +_T = TypeVar("_T") + +def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeIs[Tuple[_T, _T]]: + pass + +def func(names: Tuple[str, ...]): + reveal_type(names) # N: Revealed type is "builtins.tuple[builtins.str, ...]" + if is_two_element_tuple(names): + reveal_type(names) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsErroneousDefinitionFails] +from typing_extensions import TypeIs + +class Z: + def typeis1(self, *, x: object) -> TypeIs[int]: # E: "TypeIs" functions must have a positional argument + ... + + @staticmethod + def typeis2(x: object) -> TypeIs[int]: + ... + + @staticmethod + def typeis3(*, x: object) -> TypeIs[int]: # E: "TypeIs" functions must have a positional argument + ... + +def bad_typeis(*, x: object) -> TypeIs[int]: # E: "TypeIs" functions must have a positional argument + ... + +[builtins fixtures/classmethod.pyi] + +[case testTypeIsWithKeywordArg] +from typing_extensions import TypeIs + +class Z: + def typeis(self, x: object) -> TypeIs[int]: + ... + +def typeis(x: object) -> TypeIs[int]: + ... + +n: object +if typeis(x=n): + reveal_type(n) # N: Revealed type is "builtins.int" + +if Z().typeis(x=n): + reveal_type(n) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testStaticMethodTypeIs] +from typing_extensions import TypeIs + +class Y: + @staticmethod + def typeis(h: object) -> TypeIs[int]: + ... + +x: object +if Y().typeis(x): + reveal_type(x) # N: Revealed type is "builtins.int" +if Y.typeis(x): + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/classmethod.pyi] + +[case testTypeIsKwargFollowingThroughOverloaded] +from typing import overload, Union +from typing_extensions import TypeIs + +@overload +def typeis(x: object, y: str) -> TypeIs[str]: + ... + +@overload +def typeis(x: object, y: int) -> TypeIs[int]: + ... + +def typeis(x: object, y: Union[int, str]) -> Union[TypeIs[int], TypeIs[str]]: + ... + +x: object +if typeis(x=x, y=42): + reveal_type(x) # N: Revealed type is "builtins.int" + +if typeis(y=42, x=x): + reveal_type(x) # N: Revealed type is "builtins.int" + +if typeis(x=x, y="42"): + reveal_type(x) # N: Revealed type is "builtins.str" + +if typeis(y="42", x=x): + reveal_type(x) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + +[case testGenericAliasWithTypeIs] +from typing import Callable, List, TypeVar +from typing_extensions import TypeIs + +T = TypeVar('T') +A = Callable[[object], TypeIs[List[T]]] +def foo(x: object) -> TypeIs[List[str]]: ... + +def test(f: A[T]) -> T: ... +reveal_type(test(foo)) # N: Revealed type is "builtins.str" +[builtins fixtures/list.pyi] + +[case testNoCrashOnDunderCallTypeIs] +from typing_extensions import TypeIs + +class A: + def __call__(self, x) -> TypeIs[int]: + return True + +a: A +assert a(x=1) + +x: object +assert a(x=x) +reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeIsMustBeSubtypeFunctions] +from typing_extensions import TypeIs +from typing import List, Sequence, TypeVar + +def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of input type "str" + pass + +T = TypeVar('T') + +def g(x: List[T]) -> TypeIs[Sequence[T]]: # E: Narrowed type "Sequence[T]" is not a subtype of input type "List[T]" + pass + +[builtins fixtures/tuple.pyi] + +[case testTypeIsMustBeSubtypeMethods] +from typing_extensions import TypeIs + +class NarrowHolder: + @classmethod + def cls_narrower_good(cls, x: object) -> TypeIs[int]: + pass + + @classmethod + def cls_narrower_bad(cls, x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of input type "str" + pass + + @staticmethod + def static_narrower_good(x: object) -> TypeIs[int]: + pass + + @staticmethod + def static_narrower_bad(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of input type "str" + pass + + def inst_narrower_good(self, x: object) -> TypeIs[int]: + pass + + def inst_narrower_bad(self, x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of input type "str" + pass + + +[builtins fixtures/classmethod.pyi] + +[case testTypeIsTypeGuardNoSubtyping] +from typing_extensions import TypeGuard, TypeIs +from typing import Callable + +def accept_typeis(x: Callable[[object], TypeIs[str]]): + pass + +def accept_typeguard(x: Callable[[object], TypeGuard[str]]): + pass + +def typeis(x: object) -> TypeIs[str]: + pass + +def typeguard(x: object) -> TypeGuard[str]: + pass + +accept_typeis(typeis) +accept_typeis(typeguard) # E: Argument 1 to "accept_typeis" has incompatible type "Callable[[object], TypeGuard[str]]"; expected "Callable[[object], TypeIs[str]]" +accept_typeguard(typeis) # E: Argument 1 to "accept_typeguard" has incompatible type "Callable[[object], TypeIs[str]]"; expected "Callable[[object], TypeGuard[str]]" +accept_typeguard(typeguard) + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 68dd985cfe2a..18b6c8fc477c 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -34,6 +34,7 @@ Concatenate: _SpecialForm TypeAlias: _SpecialForm TypeGuard: _SpecialForm +TypeIs: _SpecialForm Never: _SpecialForm TypeVarTuple: _SpecialForm From 87437b86129a2d6f88137c014241d3610b39a77a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 1 Mar 2024 10:50:28 -0800 Subject: [PATCH 0517/1617] Add incremental tests for TypeGuard/TypeIs (#16976) --- test-data/unit/check-incremental.test | 76 +++++++++++++++++++ test-data/unit/lib-stub/typing_extensions.pyi | 5 +- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 69381227ca8e..42faa8c627ba 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6574,3 +6574,79 @@ class TheClass: [out] [out2] tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" + +[case testStartUsingTypeGuard] +import a +[file a.py] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] + +[file a.py.2] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] +if guard(x): + assert_type(x, int) +else: + assert_type(x, Union[int, str]) +[file lib.py] +from typing_extensions import TypeGuard +def guard(x: object) -> TypeGuard[int]: + pass +[builtins fixtures/tuple.pyi] + +[case testStartUsingTypeIs] +import a +[file a.py] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] + +[file a.py.2] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] +if guard(x): + assert_type(x, int) +else: + assert_type(x, str) +[file lib.py] +from typing_extensions import TypeIs +def guard(x: object) -> TypeIs[int]: + pass +[builtins fixtures/tuple.pyi] + +[case testTypeGuardToTypeIs] +import a +[file a.py] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] +if guard(x): + assert_type(x, int) +else: + assert_type(x, Union[int, str]) +[file a.py.2] +from lib import guard +from typing import Union +from typing_extensions import assert_type +x: Union[int, str] +if guard(x): + assert_type(x, int) +else: + assert_type(x, str) +[file lib.py] +from typing_extensions import TypeGuard +def guard(x: object) -> TypeGuard[int]: + pass +[file lib.py.2] +from typing_extensions import TypeIs +def guard(x: object) -> TypeIs[int]: + pass +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 18b6c8fc477c..ff55f1b54c7d 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -68,7 +68,8 @@ class _TypedDict(Mapping[str, object]): def TypedDict(typename: str, fields: Dict[str, Type[_T]], *, total: Any = ...) -> Type[dict]: ... -def reveal_type(__obj: T) -> T: pass +def reveal_type(__obj: _T) -> _T: pass +def assert_type(__val: _T, __typ: Any) -> _T: pass def dataclass_transform( *, @@ -77,7 +78,7 @@ def dataclass_transform( kw_only_default: bool = ..., field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = ..., **kwargs: Any, -) -> Callable[[T], T]: ... +) -> Callable[[_T], _T]: ... def override(__arg: _T) -> _T: ... def deprecated(__msg: str) -> Callable[[_T], _T]: ... From 9f36d7c07d251c37f281a6a62d3b96dc12c09e44 Mon Sep 17 00:00:00 2001 From: youkaichao Date: Sat, 2 Mar 2024 03:04:56 +0800 Subject: [PATCH 0518/1617] docs: Add missing ClassVar import (#16962) --- docs/source/cheat_sheet_py3.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index 7ae8eeb59d66..b8e43960fd09 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -152,6 +152,8 @@ Classes .. code-block:: python + from typing import ClassVar + class BankAccount: # The "__init__" method doesn't return anything, so it gets return # type "None" just like any other method that doesn't return anything From 42afe6715f9487d1776158bebecf5bb235034719 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 4 Mar 2024 12:13:28 +0300 Subject: [PATCH 0519/1617] Add `py312` target version to `black` config (#16983) I won't have any effect, but will reflect our target versions better. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fa6cf876b647..ef8acda3f95d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 99 -target-version = ["py38", "py39", "py310", "py311"] +target-version = ["py38", "py39", "py310", "py311", "py312"] skip-magic-trailing-comma = true force-exclude = ''' ^/mypy/typeshed| From 2c66b4821369494e50aa1487e477790d014f8fcc Mon Sep 17 00:00:00 2001 From: jhance Date: Mon, 4 Mar 2024 07:57:27 -0800 Subject: [PATCH 0520/1617] Add changelog for 1.9.0 (#16978) I did my best to sort them out in a way I thought was reasonable, but feel free to suggest improvements. I also removed a few that were linter version updates etc. --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Jelle Zijlstra --- CHANGELOG.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bae881656865..86e4dc91aaec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,64 @@ # Mypy Release Notes -## Next release +## Mypy 1.9 + +We’ve just uploaded mypy 1.9 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Breaking Changes + +Because the version of typeshed we use in mypy 1.9 doesn't support 3.7, neither does mypy 1.9. (Jared Hance, PR [16883](https://github.com/python/mypy/pull/16883)) + +#### Basic PEP 696 Support + +This release contains new support for PEP 696 (https://peps.python.org/pep-0696). Please try it out! (Contributed by Marc Mueller). + +#### Type-checking improvements + * Fix duplicated TypeVarTuple test (Jelle Zijlstra, PR [16853](https://github.com/python/mypy/pull/16853)) + * Fix missing type store for overloads (Marc Mueller, PR [16803](https://github.com/python/mypy/pull/16803)) + * Fix `'WriteToConn' object has no attribute 'flush'` (Charlie Denton, PR [16801](https://github.com/python/mypy/pull/16801)) + * Update TypeAlias error messages to remove colon (Marc Mueller, PR [16831](https://github.com/python/mypy/pull/16831)) + * Support narrowing unions that include type[None] (Christoph Tyralla, PR [16315](https://github.com/python/mypy/pull/16315)) + * Support TypedDict functional syntax as class base type (anniel-stripe, PR [16703](https://github.com/python/mypy/pull/16703)) + * Accept multiline quoted annotations (Shantanu, PR [16765](https://github.com/python/mypy/pull/16765)) + * Allow unary + in Literal (Jelle Zijlstra, PR [16729](https://github.com/python/mypy/pull/16729)) + * Speed up finding function type variables (Jukka Lehtosalo, PR [16562](https://github.com/python/mypy/pull/16562)) + * Substitute type variables in return type of static methods (Kouroche Bouchiat, PR [16670](https://github.com/python/mypy/pull/16670)) + * Consider TypeVarTuple to be invariant (Marc Mueller, PR [16759](https://github.com/python/mypy/pull/16759)) + * Add `alias` support to `field()` in `attrs` plugin (Nikita Sobolev, PR [16610](https://github.com/python/mypy/pull/16610)) + * Improve attrs hashability detection (Tin Tvrtković, PR [16556](https://github.com/python/mypy/pull/16556)) + +#### Documentation Updates + * Document --enable-incomplete-feature possible values in "mypy --help" (Froger David, PR [16661](https://github.com/python/mypy/pull/16661)) + * Update new type system discussion links (thomaswhaley, PR [16841](https://github.com/python/mypy/pull/16841)) + * Docs: Add missing class instantiation to cheat sheet (Aleksi Tarvainen, PR [16817](https://github.com/python/mypy/pull/16817)) + * Fix typo in getting_started.rst (zipperer, PR [16700](https://github.com/python/mypy/pull/16700)) + * Document how evil `--no-strict-optional` is (Shantanu, PR [16731](https://github.com/python/mypy/pull/16731)) + * Improve mypy daemon documentation note about local partial types (Makonnen Makonnen, PR [16782](https://github.com/python/mypy/pull/16782)) + * Fix numbering error in docs (Stefanie Molin, PR [16838](https://github.com/python/mypy/pull/16838)) + * Various docs improvements (Shantanu, PR [16836](https://github.com/python/mypy/pull/16836)) + +#### Stubtest Improvements + * Stubtest will ignore private function/method parameters when they are missing from the stub. +Private parameters names start with a single underscore and have a default +(PR [16507](https://github.com/python/mypy/pull/16507)). + * Stubtest: ignore a new protocol dunder (Alex Waygood, PR [16895](https://github.com/python/mypy/pull/16895)) + * stubtest: Private parameters can be omitted (Sebastian Rittau, PR [16507](https://github.com/python/mypy/pull/16507)) + * stubtest: Add support for setting enum members to "..." (Jelle Zijlstra, PR [16807](https://github.com/python/mypy/pull/16807)) + * stubtest: adjust symtable logic (Shantanu, PR [16823](https://github.com/python/mypy/pull/16823)) + * stubtest: fix pos-only handling in overload resolution (Shantanu, PR [16750](https://github.com/python/mypy/pull/16750)) + +#### Stubgen Improvements + * stubgen: Fix crash on star unpack of TypeVarTuple (Ali Hamdan, PR [16869](https://github.com/python/mypy/pull/16869)) + * Fix failing stubgen tests (Ali Hamdan, PR [16779](https://github.com/python/mypy/pull/16779)) + * stubgen: use PEP 604 unions everywhere (Ali Hamdan, PR [16519](https://github.com/python/mypy/pull/16519)) + * Improve stubgen tests (Fabian Keller, PR [16760](https://github.com/python/mypy/pull/16760)) + * stubgen: Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) + * Support type stub generation for `staticmethod` (WeilerMarcel, PR [14934](https://github.com/python/mypy/pull/14934)) -Stubtest will ignore private function/method parameters when they are missing from the stub. Private parameters -names start with a single underscore and have a default (PR [16507](https://github.com/python/mypy/pull/16507)). ## Mypy 1.8 From d354e763084e3890972c0c912a8290e440959f26 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 4 Mar 2024 20:18:27 +0000 Subject: [PATCH 0521/1617] Various 1.9 CHANGELOG updates (#16984) Mostly minor, but also announce that `--local-partial-types` will be enabled by default soon and explain type parameter defaults in more detail. --- CHANGELOG.md | 94 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86e4dc91aaec..59085dea4d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,51 +12,93 @@ You can read the full documentation for this release on [Read the Docs](http://m Because the version of typeshed we use in mypy 1.9 doesn't support 3.7, neither does mypy 1.9. (Jared Hance, PR [16883](https://github.com/python/mypy/pull/16883)) -#### Basic PEP 696 Support +We are planning to enable +[local partial types](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-local-partial-types) (enabled via the +`--local-partial-types` flag) later this year by default. This change +was announced years ago, but now it's finally happening. This is a +major backward-incompatible change, so we'll probably include it as +part of the upcoming mypy 2.0 release. This makes daemon and +non-daemon mypy runs have the same behavior by default. + +Local partial types can also be enabled in the mypy config file: +``` +local_partial_types = True +``` + +We are looking at providing a tool to make it easier to migrate +projects to use `--local-partial-types`, but it's not yet clear whether +this is practical. The migration usually involves adding some +explicit type annotations to module-level and class-level variables. + +#### Basic Support for Type Parameter Defaults (PEP 696) + +This release contains new experimental support for type parameter +defaults ([PEP 696](https://peps.python.org/pep-0696)). Please try it +out! This feature was contributed by Marc Mueller. + +Since this feature will be officially introduced in the next Python +feature release (3.13), you will need to import `TypeVar`, `ParamSpec` +or `TypeVarTuple` from `typing_extensions` to use defaults for now. + +This example adapted from the PEP defines a default for `BotT`: +```python +from typing import Generic +from typing_extensions import TypeVar + +class Bot: ... + +BotT = TypeVar("BotT", bound=Bot, default=Bot) -This release contains new support for PEP 696 (https://peps.python.org/pep-0696). Please try it out! (Contributed by Marc Mueller). +class Context(Generic[BotT]): + bot: BotT -#### Type-checking improvements - * Fix duplicated TypeVarTuple test (Jelle Zijlstra, PR [16853](https://github.com/python/mypy/pull/16853)) +class MyBot(Bot): ... + +# type is Bot (the default) +reveal_type(Context().bot) +# type is MyBot +reveal_type(Context[MyBot]().bot) +``` + +#### Type-checking Improvements * Fix missing type store for overloads (Marc Mueller, PR [16803](https://github.com/python/mypy/pull/16803)) * Fix `'WriteToConn' object has no attribute 'flush'` (Charlie Denton, PR [16801](https://github.com/python/mypy/pull/16801)) - * Update TypeAlias error messages to remove colon (Marc Mueller, PR [16831](https://github.com/python/mypy/pull/16831)) - * Support narrowing unions that include type[None] (Christoph Tyralla, PR [16315](https://github.com/python/mypy/pull/16315)) + * Improve TypeAlias error messages (Marc Mueller, PR [16831](https://github.com/python/mypy/pull/16831)) + * Support narrowing unions that include `type[None]` (Christoph Tyralla, PR [16315](https://github.com/python/mypy/pull/16315)) * Support TypedDict functional syntax as class base type (anniel-stripe, PR [16703](https://github.com/python/mypy/pull/16703)) * Accept multiline quoted annotations (Shantanu, PR [16765](https://github.com/python/mypy/pull/16765)) - * Allow unary + in Literal (Jelle Zijlstra, PR [16729](https://github.com/python/mypy/pull/16729)) - * Speed up finding function type variables (Jukka Lehtosalo, PR [16562](https://github.com/python/mypy/pull/16562)) + * Allow unary + in `Literal` (Jelle Zijlstra, PR [16729](https://github.com/python/mypy/pull/16729)) * Substitute type variables in return type of static methods (Kouroche Bouchiat, PR [16670](https://github.com/python/mypy/pull/16670)) * Consider TypeVarTuple to be invariant (Marc Mueller, PR [16759](https://github.com/python/mypy/pull/16759)) * Add `alias` support to `field()` in `attrs` plugin (Nikita Sobolev, PR [16610](https://github.com/python/mypy/pull/16610)) * Improve attrs hashability detection (Tin Tvrtković, PR [16556](https://github.com/python/mypy/pull/16556)) +#### Performance Improvements + + * Speed up finding function type variables (Jukka Lehtosalo, PR [16562](https://github.com/python/mypy/pull/16562)) + #### Documentation Updates - * Document --enable-incomplete-feature possible values in "mypy --help" (Froger David, PR [16661](https://github.com/python/mypy/pull/16661)) + + * Document supported values for `--enable-incomplete-feature` in "mypy --help" (Froger David, PR [16661](https://github.com/python/mypy/pull/16661)) * Update new type system discussion links (thomaswhaley, PR [16841](https://github.com/python/mypy/pull/16841)) - * Docs: Add missing class instantiation to cheat sheet (Aleksi Tarvainen, PR [16817](https://github.com/python/mypy/pull/16817)) - * Fix typo in getting_started.rst (zipperer, PR [16700](https://github.com/python/mypy/pull/16700)) + * Add missing class instantiation to cheat sheet (Aleksi Tarvainen, PR [16817](https://github.com/python/mypy/pull/16817)) * Document how evil `--no-strict-optional` is (Shantanu, PR [16731](https://github.com/python/mypy/pull/16731)) * Improve mypy daemon documentation note about local partial types (Makonnen Makonnen, PR [16782](https://github.com/python/mypy/pull/16782)) - * Fix numbering error in docs (Stefanie Molin, PR [16838](https://github.com/python/mypy/pull/16838)) - * Various docs improvements (Shantanu, PR [16836](https://github.com/python/mypy/pull/16836)) + * Fix numbering error (Stefanie Molin, PR [16838](https://github.com/python/mypy/pull/16838)) + * Various documentation improvements (Shantanu, PR [16836](https://github.com/python/mypy/pull/16836)) #### Stubtest Improvements - * Stubtest will ignore private function/method parameters when they are missing from the stub. -Private parameters names start with a single underscore and have a default -(PR [16507](https://github.com/python/mypy/pull/16507)). - * Stubtest: ignore a new protocol dunder (Alex Waygood, PR [16895](https://github.com/python/mypy/pull/16895)) - * stubtest: Private parameters can be omitted (Sebastian Rittau, PR [16507](https://github.com/python/mypy/pull/16507)) - * stubtest: Add support for setting enum members to "..." (Jelle Zijlstra, PR [16807](https://github.com/python/mypy/pull/16807)) - * stubtest: adjust symtable logic (Shantanu, PR [16823](https://github.com/python/mypy/pull/16823)) - * stubtest: fix pos-only handling in overload resolution (Shantanu, PR [16750](https://github.com/python/mypy/pull/16750)) + * Ignore private function/method parameters when they are missing from the stub (private parameter names start with a single underscore and have a default) (PR [16507](https://github.com/python/mypy/pull/16507)) + * Ignore a new protocol dunder (Alex Waygood, PR [16895](https://github.com/python/mypy/pull/16895)) + * Private parameters can be omitted (Sebastian Rittau, PR [16507](https://github.com/python/mypy/pull/16507)) + * Add support for setting enum members to "..." (Jelle Zijlstra, PR [16807](https://github.com/python/mypy/pull/16807)) + * Adjust symbol table logic (Shantanu, PR [16823](https://github.com/python/mypy/pull/16823)) + * Fix posisitional-only handling in overload resolution (Shantanu, PR [16750](https://github.com/python/mypy/pull/16750)) #### Stubgen Improvements - * stubgen: Fix crash on star unpack of TypeVarTuple (Ali Hamdan, PR [16869](https://github.com/python/mypy/pull/16869)) - * Fix failing stubgen tests (Ali Hamdan, PR [16779](https://github.com/python/mypy/pull/16779)) - * stubgen: use PEP 604 unions everywhere (Ali Hamdan, PR [16519](https://github.com/python/mypy/pull/16519)) - * Improve stubgen tests (Fabian Keller, PR [16760](https://github.com/python/mypy/pull/16760)) - * stubgen: Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) + * Fix crash on star unpack of TypeVarTuple (Ali Hamdan, PR [16869](https://github.com/python/mypy/pull/16869)) + * Use PEP 604 unions everywhere (Ali Hamdan, PR [16519](https://github.com/python/mypy/pull/16519)) + * Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) * Support type stub generation for `staticmethod` (WeilerMarcel, PR [14934](https://github.com/python/mypy/pull/14934)) From 2f0f8f26d7aa3dab3c44a621bbed58c7816db2a4 Mon Sep 17 00:00:00 2001 From: jhance Date: Tue, 5 Mar 2024 13:58:22 -0800 Subject: [PATCH 0522/1617] Update changelog for 1.9 with acknowledgements. (#16989) Co-authored-by: Jukka Lehtosalo Co-authored-by: Alex Waygood --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59085dea4d1f..8bd537d46e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,41 @@ reveal_type(Context[MyBot]().bot) * Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) * Support type stub generation for `staticmethod` (WeilerMarcel, PR [14934](https://github.com/python/mypy/pull/14934)) +#### Acknowledgements + +​Thanks to all mypy contributors who contributed to this release: + +- Aleksi Tarvainen +- Alex Waygood +- Ali Hamdan +- anniel-stripe +- Charlie Denton +- Christoph Tyralla +- Dheeraj +- Fabian Keller +- Fabian Lewis +- Froger David +- Ihor +- Jared Hance +- Jelle Zijlstra +- Jukka Lehtosalo +- Kouroche Bouchiat +- Lukas Geiger +- Maarten Huijsmans +- Makonnen Makonnen +- Marc Mueller +- Nikita Sobolev +- Sebastian Rittau +- Shantanu +- Stefanie Molin +- Stephen Morton +- thomaswhaley +- Tin Tvrtković +- WeilerMarcel +- Wesley Collin Wright +- zipperer + +I’d also like to thank my employer, Dropbox, for supporting mypy development. ## Mypy 1.8 From 2fbfb6060a4549a1837d5eed4ad7ef1e8da256b9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 7 Mar 2024 02:25:06 +0100 Subject: [PATCH 0523/1617] Fix inference with UninhabitedType (#16994) At the moment, inference fails if an empty dict is used (without annotation) as one of the types. It's because the constraint solver can't resolve `dict[str, int]` and `dict[Never, Never]`. However in this case it's more reasonable to interpret the empty dict as `dict[Any, Any]` and just using the first type instead. That matches the behavior of pyright. ```py T = TypeVar("T") class A(Generic[T]): ... def func1(a: A[T], b: T) -> T: ... def a1(a: A[Dict[str, int]]) -> None: reveal_type(func1(a, {})) ``` ``` # before main: error: Cannot infer type argument 1 of "func1" (diff) main: note: Revealed type is "Any" (diff) # after main: note: Revealed type is "builtins.dict[builtins.str, builtins.int]" ``` --- mypy/join.py | 13 +++++++---- test-data/unit/check-inference.test | 34 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index bf88f43d88fe..3603e9fefb7a 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -108,12 +108,17 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # TODO: contravariant case should use meet but pass seen instances as # an argument to keep track of recursive checks. elif type_var.variance in (INVARIANT, CONTRAVARIANT): - if not is_equivalent(ta, sa): + if isinstance(ta_proper, UninhabitedType) and not ta_proper.is_noreturn: + new_type = sa + elif isinstance(sa_proper, UninhabitedType) and not sa_proper.is_noreturn: + new_type = ta + elif not is_equivalent(ta, sa): self.seen_instances.pop() return object_from_instance(t) - # If the types are different but equivalent, then an Any is involved - # so using a join in the contravariant case is also OK. - new_type = join_types(ta, sa, self) + else: + # If the types are different but equivalent, then an Any is involved + # so using a join in the contravariant case is also OK. + new_type = join_types(ta, sa, self) elif isinstance(type_var, TypeVarTupleType): new_type = get_proper_type(join_types(ta, sa, self)) # Put the joined arguments back into instance in the normal form: diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 953855e502d6..1b1ce607bf28 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3813,3 +3813,37 @@ def m1() -> float: ... def m2() -> float: ... reveal_type(Combine(m1, m2)) # N: Revealed type is "builtins.float" [builtins fixtures/list.pyi] + +[case testInferenceWithUninhabitedType] +from typing import Dict, Generic, List, Never, TypeVar + +T = TypeVar("T") + +class A(Generic[T]): ... +class B(Dict[T, T]): ... + +def func1(a: A[T], b: T) -> T: ... +def func2(a: T, b: A[T]) -> T: ... + +def a1(a: A[Dict[str, int]]) -> None: + reveal_type(func1(a, {})) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + reveal_type(func2({}, a)) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +def a2(check: bool, a: B[str]) -> None: + reveal_type(a if check else {}) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]" + +def a3() -> None: + a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") + b = {1: {}} # E: Need type annotation for "b" + c = {1: {}, 2: {"key": {}}} # E: Need type annotation for "c" + reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[Any, Any]]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[builtins.str, builtins.dict[Any, Any]]]" + +def a4(x: List[str], y: List[Never]) -> None: + z1 = [x, y] + z2 = [y, x] + reveal_type(z1) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]" + z1[1].append("asdf") # E: "object" has no attribute "append" +[builtins fixtures/dict.pyi] From 0b8fed526de75284349afbd4b448172b61148931 Mon Sep 17 00:00:00 2001 From: Oskari Lehto Date: Thu, 7 Mar 2024 05:40:10 +0200 Subject: [PATCH 0524/1617] Fix single item enum match type exhaustion (#16966) Fixes #14109 --------- Co-authored-by: Shantanu Jain --- mypy/checkpattern.py | 2 +- test-data/unit/check-python310.test | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 7b6a55324741..a23be464b825 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -202,7 +202,7 @@ def visit_value_pattern(self, o: ValuePattern) -> PatternType: typ = self.chk.expr_checker.accept(o.expr) typ = coerce_to_literal(typ) narrowed_type, rest_type = self.chk.conditional_types_with_intersection( - current_type, [get_type_range(typ)], o, default=current_type + current_type, [get_type_range(typ)], o, default=get_proper_type(typ) ) if not isinstance(get_proper_type(narrowed_type), (LiteralType, UninhabitedType)): return PatternType(narrowed_type, UnionType.make_union([narrowed_type, rest_type]), {}) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index b0e27fe1e3a0..3a040d94d7ba 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1369,6 +1369,27 @@ match m3: reveal_type(m3) # N: Revealed type is "Tuple[Union[builtins.int, builtins.str]]" [builtins fixtures/tuple.pyi] +[case testMatchEnumSingleChoice] +from enum import Enum +from typing import NoReturn + +def assert_never(x: NoReturn) -> None: ... + +class Medal(Enum): + gold = 1 + +def f(m: Medal) -> None: + always_assigned: int | None = None + match m: + case Medal.gold: + always_assigned = 1 + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case _: + assert_never(m) + + reveal_type(always_assigned) # N: Revealed type is "builtins.int" +[builtins fixtures/bool.pyi] + [case testMatchLiteralPatternEnumNegativeNarrowing] from enum import Enum class Medal(Enum): @@ -1388,10 +1409,13 @@ def f(m: Medal) -> int: def g(m: Medal) -> int: match m: case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" return 0 case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" return 1 case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" return 2 [case testMatchLiteralPatternEnumCustomEquals-skip] From e0ad95296037446fccb398b8dadc54ae0751df46 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 8 Mar 2024 07:05:05 -0800 Subject: [PATCH 0525/1617] Disallow all super calls to methods with trivial bodies (#16756) Relates to: https://discuss.python.org/t/calling-abstract-methods/42576 I think this makes mypy's behaviour more predictable --- mypy/checkmember.py | 8 +------- test-data/unit/check-abstract.test | 12 ++++++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index c24edacf0ee1..afa8f37ff7d5 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -359,13 +359,7 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None: impl = node.impl if isinstance(node.impl, FuncDef) else node.impl.func unsafe_super = impl.is_trivial_body if unsafe_super: - ret_type = ( - impl.type.ret_type - if isinstance(impl.type, CallableType) - else AnyType(TypeOfAny.unannotated) - ) - if not subtypes.is_subtype(NoneType(), ret_type): - mx.msg.unsafe_super(node.name, node.info.name, mx.context) + mx.msg.unsafe_super(node.name, node.info.name, mx.context) def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: MemberContext) -> Type: diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 7f91eb8e7145..3b0b9c520b75 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -896,9 +896,9 @@ class A(metaclass=ABCMeta): class B(A): @property def x(self) -> int: - return super().x.y # E: "int" has no attribute "y" + return super().x.y # E: Call to abstract method "x" of "A" with trivial body via super() is unsafe \ + # E: "int" has no attribute "y" [builtins fixtures/property.pyi] -[out] [case testSuperWithReadWriteAbstractProperty] from abc import abstractproperty, ABCMeta @@ -1659,10 +1659,10 @@ class Abstract: class SubProto(Proto): def meth(self) -> int: - return super().meth() + return super().meth() # E: Call to abstract method "meth" of "Proto" with trivial body via super() is unsafe class SubAbstract(Abstract): def meth(self) -> int: - return super().meth() + return super().meth() # E: Call to abstract method "meth" of "Abstract" with trivial body via super() is unsafe [case testEmptyBodyNoSuperWarningOptionalReturn] from typing import Protocol, Optional @@ -1676,10 +1676,10 @@ class Abstract: class SubProto(Proto): def meth(self) -> Optional[int]: - return super().meth() + return super().meth() # E: Call to abstract method "meth" of "Proto" with trivial body via super() is unsafe class SubAbstract(Abstract): def meth(self) -> Optional[int]: - return super().meth() + return super().meth() # E: Call to abstract method "meth" of "Abstract" with trivial body via super() is unsafe [case testEmptyBodyTypeCheckingOnly] from typing import TYPE_CHECKING From c94d8e3cd02e8be5e4594d84ec77c84b3faf7948 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 9 Mar 2024 09:16:41 +0000 Subject: [PATCH 0526/1617] [mypyc] Provide an easier way to define IR-to-IR transforms (#16998) This makes it easy to define simple IR-to-IR transforms by subclassing `IRTansform` and overriding some visit methods. Add an implementation of a simple copy propagation optimization as an example. This will be used by the implementation of mypyc/mypyc#854, and this can also be used for various optimizations. The IR transform preserves the identities of ops that are not modified. This means that the old IR is no longer valid after the transform, but the transform can be fast since we don't need to allocate many objects if only a small subset of ops will be modified by a transform. --- mypyc/codegen/emitmodule.py | 13 +- mypyc/irbuild/builder.py | 6 +- mypyc/irbuild/ll_builder.py | 8 +- mypyc/test-data/opt-copy-propagation.test | 400 ++++++++++++++++++++++ mypyc/test/test_copy_propagation.py | 47 +++ mypyc/transform/copy_propagation.py | 94 +++++ mypyc/transform/ir_transform.py | 353 +++++++++++++++++++ 7 files changed, 904 insertions(+), 17 deletions(-) create mode 100644 mypyc/test-data/opt-copy-propagation.test create mode 100644 mypyc/test/test_copy_propagation.py create mode 100644 mypyc/transform/copy_propagation.py create mode 100644 mypyc/transform/ir_transform.py diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 6c0dfd43b9af..0035bd53188b 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -56,6 +56,7 @@ from mypyc.irbuild.prepare import load_type_map from mypyc.namegen import NameGenerator, exported_name from mypyc.options import CompilerOptions +from mypyc.transform.copy_propagation import do_copy_propagation from mypyc.transform.exceptions import insert_exception_handling from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.transform.uninit import insert_uninit_checks @@ -225,18 +226,16 @@ def compile_scc_to_ir( if errors.num_errors > 0: return modules - # Insert uninit checks. for module in modules.values(): for fn in module.functions: + # Insert uninit checks. insert_uninit_checks(fn) - # Insert exception handling. - for module in modules.values(): - for fn in module.functions: + # Insert exception handling. insert_exception_handling(fn) - # Insert refcount handling. - for module in modules.values(): - for fn in module.functions: + # Insert refcount handling. insert_ref_count_opcodes(fn) + # Perform copy propagation optimization. + do_copy_propagation(fn, compiler_options) return modules diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index f201a4737f89..52891d68e3b2 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -160,7 +160,7 @@ def __init__( options: CompilerOptions, singledispatch_impls: dict[FuncDef, list[RegisterImplInfo]], ) -> None: - self.builder = LowLevelIRBuilder(current_module, errors, mapper, options) + self.builder = LowLevelIRBuilder(errors, options) self.builders = [self.builder] self.symtables: list[dict[SymbolNode, SymbolTarget]] = [{}] self.runtime_args: list[list[RuntimeArg]] = [[]] @@ -1111,9 +1111,7 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None: def enter(self, fn_info: FuncInfo | str = "") -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) - self.builder = LowLevelIRBuilder( - self.current_module, self.errors, self.mapper, self.options - ) + self.builder = LowLevelIRBuilder(self.errors, self.options) self.builder.set_module(self.module_name, self.module_path) self.builders.append(self.builder) self.symtables.append({}) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index d1ea91476a66..45c06e11befd 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -126,7 +126,6 @@ short_int_rprimitive, str_rprimitive, ) -from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.util import concrete_arg_kind from mypyc.options import CompilerOptions from mypyc.primitives.bytes_ops import bytes_compare @@ -220,12 +219,8 @@ class LowLevelIRBuilder: - def __init__( - self, current_module: str, errors: Errors, mapper: Mapper, options: CompilerOptions - ) -> None: - self.current_module = current_module + def __init__(self, errors: Errors | None, options: CompilerOptions) -> None: self.errors = errors - self.mapper = mapper self.options = options self.args: list[Register] = [] self.blocks: list[BasicBlock] = [] @@ -2394,6 +2389,7 @@ def _create_dict(self, keys: list[Value], values: list[Value], line: int) -> Val return self.call_c(dict_new_op, [], line) def error(self, msg: str, line: int) -> None: + assert self.errors is not None, "cannot generate errors in this compiler phase" self.errors.error(msg, self.module_path, line) diff --git a/mypyc/test-data/opt-copy-propagation.test b/mypyc/test-data/opt-copy-propagation.test new file mode 100644 index 000000000000..49b80f4385fc --- /dev/null +++ b/mypyc/test-data/opt-copy-propagation.test @@ -0,0 +1,400 @@ +-- Test cases for copy propagation optimization. This also tests IR transforms in general, +-- as copy propagation was the first IR transform that was implemented. + +[case testCopyPropagationSimple] +def g() -> int: + return 1 + +def f() -> int: + y = g() + return y +[out] +def g(): +L0: + return 2 +def f(): + r0 :: int +L0: + r0 = g() + return r0 + +[case testCopyPropagationChain] +def f(x: int) -> int: + y = x + z = y + return z +[out] +def f(x): + x :: int +L0: + return x + +[case testCopyPropagationChainPartial] +def f(x: int) -> int: + y = x + z = y + x = 2 + return z +[out] +def f(x): + x, y :: int +L0: + y = x + x = 4 + return y + +[case testCopyPropagationChainBad] +def f(x: int) -> int: + y = x + z = y + y = 2 + return z +[out] +def f(x): + x, y, z :: int +L0: + y = x + z = y + y = 4 + return z + +[case testCopyPropagationMutatedSource1] +def f(x: int) -> int: + y = x + x = 1 + return y +[out] +def f(x): + x, y :: int +L0: + y = x + x = 2 + return y + +[case testCopyPropagationMutatedSource2] +def f() -> int: + z = 1 + y = z + z = 2 + return y +[out] +def f(): + z, y :: int +L0: + z = 2 + y = z + z = 4 + return y + +[case testCopyPropagationTooComplex] +def f(b: bool, x: int) -> int: + if b: + y = x + return y + else: + y = 1 + return y +[out] +def f(b, x): + b :: bool + x, y :: int +L0: + if b goto L1 else goto L2 :: bool +L1: + y = x + return y +L2: + y = 2 + return y + +[case testCopyPropagationArg] +def f(x: int) -> int: + x = 2 + return x +[out] +def f(x): + x :: int +L0: + x = 4 + return x + +[case testCopyPropagationPartiallyDefined1] +def f(b: bool) -> int: + if b: + x = 1 + y = x + return y +[out] +def f(b): + b :: bool + r0, x :: int + r1 :: bool + y :: int +L0: + r0 = :: int + x = r0 + if b goto L1 else goto L2 :: bool +L1: + x = 2 +L2: + if is_error(x) goto L3 else goto L4 +L3: + r1 = raise UnboundLocalError('local variable "x" referenced before assignment') + unreachable +L4: + y = x + return y + +-- The remaining test cases test basic IRTransform functionality and are not +-- all needed for testing copy propagation as such. + +[case testIRTransformBranch] +from mypy_extensions import i64 + +def f(x: bool) -> int: + y = x + if y: + return 1 + else: + return 2 +[out] +def f(x): + x :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + return 2 +L2: + return 4 + +[case testIRTransformAssignment] +def f(b: bool, x: int) -> int: + y = x + if b: + return y + else: + return 1 +[out] +def f(b, x): + b :: bool + x :: int +L0: + if b goto L1 else goto L2 :: bool +L1: + return x +L2: + return 2 + +[case testIRTransformRegisterOps1] +from __future__ import annotations +from typing import cast + +class C: + a: int + + def m(self, x: int) -> None: pass + +def get_attr(x: C) -> int: + y = x + return y.a + +def set_attr(x: C) -> None: + y = x + y.a = 1 + +def tuple_get(x: tuple[int, int]) -> int: + y = x + return y[0] + +def tuple_set(x: int, xx: int) -> tuple[int, int]: + y = x + z = xx + return y, z + +def call(x: int) -> int: + y = x + return call(y) + +def method_call(c: C, x: int) -> None: + y = x + c.m(y) + +def cast_op(x: object) -> str: + y = x + return cast(str, y) + +def box(x: int) -> object: + y = x + return y + +def unbox(x: object) -> int: + y = x + return cast(int, y) + +def call_c(x: list[str]) -> None: + y = x + y.append("x") + +def keep_alive(x: C) -> int: + y = x + return y.a + 1 +[out] +def C.m(self, x): + self :: __main__.C + x :: int +L0: + return 1 +def get_attr(x): + x :: __main__.C + r0 :: int +L0: + r0 = x.a + return r0 +def set_attr(x): + x :: __main__.C + r0 :: bool +L0: + x.a = 2; r0 = is_error + return 1 +def tuple_get(x): + x :: tuple[int, int] + r0 :: int +L0: + r0 = x[0] + return r0 +def tuple_set(x, xx): + x, xx :: int + r0 :: tuple[int, int] +L0: + r0 = (x, xx) + return r0 +def call(x): + x, r0 :: int +L0: + r0 = call(x) + return r0 +def method_call(c, x): + c :: __main__.C + x :: int + r0 :: None +L0: + r0 = c.m(x) + return 1 +def cast_op(x): + x :: object + r0 :: str +L0: + r0 = cast(str, x) + return r0 +def box(x): + x :: int + r0 :: object +L0: + r0 = box(int, x) + return r0 +def unbox(x): + x :: object + r0 :: int +L0: + r0 = unbox(int, x) + return r0 +def call_c(x): + x :: list + r0 :: str + r1 :: i32 + r2 :: bit +L0: + r0 = 'x' + r1 = PyList_Append(x, r0) + r2 = r1 >= 0 :: signed + return 1 +def keep_alive(x): + x :: __main__.C + r0, r1 :: int +L0: + r0 = borrow x.a + r1 = CPyTagged_Add(r0, 2) + keep_alive x + return r1 + +[case testIRTransformRegisterOps2] +from mypy_extensions import i32, i64 + +def truncate(x: i64) -> i32: + y = x + return i32(y) + +def extend(x: i32) -> i64: + y = x + return i64(y) + +def int_op(x: i64, xx: i64) -> i64: + y = x + z = xx + return y + z + +def comparison_op(x: i64, xx: i64) -> bool: + y = x + z = xx + return y == z + +def float_op(x: float, xx: float) -> float: + y = x + z = xx + return y + z + +def float_neg(x: float) -> float: + y = x + return -y + +def float_comparison_op(x: float, xx: float) -> bool: + y = x + z = xx + return y == z +[out] +def truncate(x): + x :: i64 + r0 :: i32 +L0: + r0 = truncate x: i64 to i32 + return r0 +def extend(x): + x :: i32 + r0 :: i64 +L0: + r0 = extend signed x: i32 to i64 + return r0 +def int_op(x, xx): + x, xx, r0 :: i64 +L0: + r0 = x + xx + return r0 +def comparison_op(x, xx): + x, xx :: i64 + r0 :: bit +L0: + r0 = x == xx + return r0 +def float_op(x, xx): + x, xx, r0 :: float +L0: + r0 = x + xx + return r0 +def float_neg(x): + x, r0 :: float +L0: + r0 = -x + return r0 +def float_comparison_op(x, xx): + x, xx :: float + r0 :: bit +L0: + r0 = x == xx + return r0 + +-- Note that transforms of these ops aren't tested here: +-- * LoadMem +-- * SetMem +-- * GetElementPtr +-- * LoadAddress +-- * Unborrow diff --git a/mypyc/test/test_copy_propagation.py b/mypyc/test/test_copy_propagation.py new file mode 100644 index 000000000000..c729e3d186c3 --- /dev/null +++ b/mypyc/test/test_copy_propagation.py @@ -0,0 +1,47 @@ +"""Runner for copy propagation optimization tests.""" + +from __future__ import annotations + +import os.path + +from mypy.errors import CompileError +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase +from mypyc.common import TOP_LEVEL_NAME +from mypyc.ir.pprint import format_func +from mypyc.options import CompilerOptions +from mypyc.test.testutil import ( + ICODE_GEN_BUILTINS, + MypycDataSuite, + assert_test_output, + build_ir_for_single_file, + remove_comment_lines, + use_custom_builtins, +) +from mypyc.transform.copy_propagation import do_copy_propagation +from mypyc.transform.uninit import insert_uninit_checks + +files = ["opt-copy-propagation.test"] + + +class TestCopyPropagation(MypycDataSuite): + files = files + base_path = test_temp_dir + + def run_case(self, testcase: DataDrivenTestCase) -> None: + with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): + expected_output = remove_comment_lines(testcase.output) + try: + ir = build_ir_for_single_file(testcase.input) + except CompileError as e: + actual = e.messages + else: + actual = [] + for fn in ir: + if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"): + continue + insert_uninit_checks(fn) + do_copy_propagation(fn, CompilerOptions()) + actual.extend(format_func(fn)) + + assert_test_output(testcase, actual, "Invalid source code output", expected_output) diff --git a/mypyc/transform/copy_propagation.py b/mypyc/transform/copy_propagation.py new file mode 100644 index 000000000000..49de616f85a3 --- /dev/null +++ b/mypyc/transform/copy_propagation.py @@ -0,0 +1,94 @@ +"""Simple copy propagation optimization. + +Example input: + + x = f() + y = x + +The register x is redundant and we can directly assign its value to y: + + y = f() + +This can optimize away registers that are assigned to once. +""" + +from __future__ import annotations + +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import Assign, AssignMulti, LoadAddress, LoadErrorValue, Register, Value +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.options import CompilerOptions +from mypyc.sametype import is_same_type +from mypyc.transform.ir_transform import IRTransform + + +def do_copy_propagation(fn: FuncIR, options: CompilerOptions) -> None: + """Perform copy propagation optimization for fn.""" + + # Anything with an assignment count >1 will not be optimized + # here, as it would be require data flow analysis and we want to + # keep this simple and fast, at least until we've made data flow + # analysis much faster. + counts: dict[Value, int] = {} + replacements: dict[Value, Value] = {} + for arg in fn.arg_regs: + # Arguments are always assigned to initially + counts[arg] = 1 + + for block in fn.blocks: + for op in block.ops: + if isinstance(op, Assign): + c = counts.get(op.dest, 0) + counts[op.dest] = c + 1 + # Does this look like a supported assignment? + # TODO: Something needs LoadErrorValue assignments to be preserved? + if ( + c == 0 + and is_same_type(op.dest.type, op.src.type) + and not isinstance(op.src, LoadErrorValue) + ): + replacements[op.dest] = op.src + elif c == 1: + # Too many assignments -- don't replace this one + replacements.pop(op.dest, 0) + elif isinstance(op, AssignMulti): + # Copy propagation not supported for AssignMulti destinations + counts[op.dest] = 2 + replacements.pop(op.dest, 0) + elif isinstance(op, LoadAddress): + # We don't support taking the address of an arbitrary Value, + # so we'll need to preserve the operands of LoadAddress. + if isinstance(op.src, Register): + counts[op.src] = 2 + replacements.pop(op.src, 0) + + # Follow chains of propagation with more than one assignment. + for src, dst in list(replacements.items()): + if counts.get(dst, 0) > 1: + # Not supported + del replacements[src] + else: + while dst in replacements: + dst = replacements[dst] + if counts.get(dst, 0) > 1: + # Not supported + del replacements[src] + if src in replacements: + replacements[src] = dst + + builder = LowLevelIRBuilder(None, options) + transform = CopyPropagationTransform(builder, replacements) + transform.transform_blocks(fn.blocks) + fn.blocks = builder.blocks + + +class CopyPropagationTransform(IRTransform): + def __init__(self, builder: LowLevelIRBuilder, map: dict[Value, Value]) -> None: + super().__init__(builder) + self.op_map.update(map) + self.removed = set(map) + + def visit_assign(self, op: Assign) -> Value | None: + if op.dest in self.removed: + return None + return self.add(op) diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py new file mode 100644 index 000000000000..1bcfc8fb5feb --- /dev/null +++ b/mypyc/transform/ir_transform.py @@ -0,0 +1,353 @@ +"""Helpers for implementing generic IR to IR transforms.""" + +from __future__ import annotations + +from typing import Final, Optional + +from mypyc.ir.ops import ( + Assign, + AssignMulti, + BasicBlock, + Box, + Branch, + Call, + CallC, + Cast, + ComparisonOp, + DecRef, + Extend, + FloatComparisonOp, + FloatNeg, + FloatOp, + GetAttr, + GetElementPtr, + Goto, + IncRef, + InitStatic, + IntOp, + KeepAlive, + LoadAddress, + LoadErrorValue, + LoadGlobal, + LoadLiteral, + LoadMem, + LoadStatic, + MethodCall, + Op, + OpVisitor, + RaiseStandardError, + Return, + SetAttr, + SetMem, + Truncate, + TupleGet, + TupleSet, + Unborrow, + Unbox, + Unreachable, + Value, +) +from mypyc.irbuild.ll_builder import LowLevelIRBuilder + + +class IRTransform(OpVisitor[Optional[Value]]): + """Identity transform. + + Subclass and override to perform changes to IR. + + Subclass IRTransform and override any OpVisitor visit_* methods + that perform any IR changes. The default implementations implement + an identity transform. + + A visit method can return None to remove ops. In this case the + transform must ensure that no op uses the original removed op + as a source after the transform. + + You can retain old BasicBlock and op references in ops. The transform + will automatically patch these for you as needed. + """ + + def __init__(self, builder: LowLevelIRBuilder) -> None: + self.builder = builder + # Subclasses add additional op mappings here. A None value indicates + # that the op/register is deleted. + self.op_map: dict[Value, Value | None] = {} + + def transform_blocks(self, blocks: list[BasicBlock]) -> None: + """Transform basic blocks that represent a single function. + + The result of the transform will be collected at self.builder.blocks. + """ + block_map: dict[BasicBlock, BasicBlock] = {} + op_map = self.op_map + for block in blocks: + new_block = BasicBlock() + block_map[block] = new_block + self.builder.activate_block(new_block) + new_block.error_handler = block.error_handler + for op in block.ops: + new_op = op.accept(self) + if new_op is not op: + op_map[op] = new_op + + # Update all op/block references to point to the transformed ones. + patcher = PatchVisitor(op_map, block_map) + for block in self.builder.blocks: + for op in block.ops: + op.accept(patcher) + if block.error_handler is not None: + block.error_handler = block_map.get(block.error_handler, block.error_handler) + + def add(self, op: Op) -> Value: + return self.builder.add(op) + + def visit_goto(self, op: Goto) -> Value: + return self.add(op) + + def visit_branch(self, op: Branch) -> Value: + return self.add(op) + + def visit_return(self, op: Return) -> Value: + return self.add(op) + + def visit_unreachable(self, op: Unreachable) -> Value: + return self.add(op) + + def visit_assign(self, op: Assign) -> Value | None: + return self.add(op) + + def visit_assign_multi(self, op: AssignMulti) -> Value | None: + return self.add(op) + + def visit_load_error_value(self, op: LoadErrorValue) -> Value | None: + return self.add(op) + + def visit_load_literal(self, op: LoadLiteral) -> Value | None: + return self.add(op) + + def visit_get_attr(self, op: GetAttr) -> Value | None: + return self.add(op) + + def visit_set_attr(self, op: SetAttr) -> Value | None: + return self.add(op) + + def visit_load_static(self, op: LoadStatic) -> Value | None: + return self.add(op) + + def visit_init_static(self, op: InitStatic) -> Value | None: + return self.add(op) + + def visit_tuple_get(self, op: TupleGet) -> Value | None: + return self.add(op) + + def visit_tuple_set(self, op: TupleSet) -> Value | None: + return self.add(op) + + def visit_inc_ref(self, op: IncRef) -> Value | None: + return self.add(op) + + def visit_dec_ref(self, op: DecRef) -> Value | None: + return self.add(op) + + def visit_call(self, op: Call) -> Value | None: + return self.add(op) + + def visit_method_call(self, op: MethodCall) -> Value | None: + return self.add(op) + + def visit_cast(self, op: Cast) -> Value | None: + return self.add(op) + + def visit_box(self, op: Box) -> Value | None: + return self.add(op) + + def visit_unbox(self, op: Unbox) -> Value | None: + return self.add(op) + + def visit_raise_standard_error(self, op: RaiseStandardError) -> Value | None: + return self.add(op) + + def visit_call_c(self, op: CallC) -> Value | None: + return self.add(op) + + def visit_truncate(self, op: Truncate) -> Value | None: + return self.add(op) + + def visit_extend(self, op: Extend) -> Value | None: + return self.add(op) + + def visit_load_global(self, op: LoadGlobal) -> Value | None: + return self.add(op) + + def visit_int_op(self, op: IntOp) -> Value | None: + return self.add(op) + + def visit_comparison_op(self, op: ComparisonOp) -> Value | None: + return self.add(op) + + def visit_float_op(self, op: FloatOp) -> Value | None: + return self.add(op) + + def visit_float_neg(self, op: FloatNeg) -> Value | None: + return self.add(op) + + def visit_float_comparison_op(self, op: FloatComparisonOp) -> Value | None: + return self.add(op) + + def visit_load_mem(self, op: LoadMem) -> Value | None: + return self.add(op) + + def visit_set_mem(self, op: SetMem) -> Value | None: + return self.add(op) + + def visit_get_element_ptr(self, op: GetElementPtr) -> Value | None: + return self.add(op) + + def visit_load_address(self, op: LoadAddress) -> Value | None: + return self.add(op) + + def visit_keep_alive(self, op: KeepAlive) -> Value | None: + return self.add(op) + + def visit_unborrow(self, op: Unborrow) -> Value | None: + return self.add(op) + + +class PatchVisitor(OpVisitor[None]): + def __init__( + self, op_map: dict[Value, Value | None], block_map: dict[BasicBlock, BasicBlock] + ) -> None: + self.op_map: Final = op_map + self.block_map: Final = block_map + + def fix_op(self, op: Value) -> Value: + new = self.op_map.get(op, op) + assert new is not None, "use of removed op" + return new + + def fix_block(self, block: BasicBlock) -> BasicBlock: + return self.block_map.get(block, block) + + def visit_goto(self, op: Goto) -> None: + op.label = self.fix_block(op.label) + + def visit_branch(self, op: Branch) -> None: + op.value = self.fix_op(op.value) + op.true = self.fix_block(op.true) + op.false = self.fix_block(op.false) + + def visit_return(self, op: Return) -> None: + op.value = self.fix_op(op.value) + + def visit_unreachable(self, op: Unreachable) -> None: + pass + + def visit_assign(self, op: Assign) -> None: + op.src = self.fix_op(op.src) + + def visit_assign_multi(self, op: AssignMulti) -> None: + op.src = [self.fix_op(s) for s in op.src] + + def visit_load_error_value(self, op: LoadErrorValue) -> None: + pass + + def visit_load_literal(self, op: LoadLiteral) -> None: + pass + + def visit_get_attr(self, op: GetAttr) -> None: + op.obj = self.fix_op(op.obj) + + def visit_set_attr(self, op: SetAttr) -> None: + op.obj = self.fix_op(op.obj) + op.src = self.fix_op(op.src) + + def visit_load_static(self, op: LoadStatic) -> None: + pass + + def visit_init_static(self, op: InitStatic) -> None: + op.value = self.fix_op(op.value) + + def visit_tuple_get(self, op: TupleGet) -> None: + op.src = self.fix_op(op.src) + + def visit_tuple_set(self, op: TupleSet) -> None: + op.items = [self.fix_op(item) for item in op.items] + + def visit_inc_ref(self, op: IncRef) -> None: + op.src = self.fix_op(op.src) + + def visit_dec_ref(self, op: DecRef) -> None: + op.src = self.fix_op(op.src) + + def visit_call(self, op: Call) -> None: + op.args = [self.fix_op(arg) for arg in op.args] + + def visit_method_call(self, op: MethodCall) -> None: + op.obj = self.fix_op(op.obj) + op.args = [self.fix_op(arg) for arg in op.args] + + def visit_cast(self, op: Cast) -> None: + op.src = self.fix_op(op.src) + + def visit_box(self, op: Box) -> None: + op.src = self.fix_op(op.src) + + def visit_unbox(self, op: Unbox) -> None: + op.src = self.fix_op(op.src) + + def visit_raise_standard_error(self, op: RaiseStandardError) -> None: + if isinstance(op.value, Value): + op.value = self.fix_op(op.value) + + def visit_call_c(self, op: CallC) -> None: + op.args = [self.fix_op(arg) for arg in op.args] + + def visit_truncate(self, op: Truncate) -> None: + op.src = self.fix_op(op.src) + + def visit_extend(self, op: Extend) -> None: + op.src = self.fix_op(op.src) + + def visit_load_global(self, op: LoadGlobal) -> None: + pass + + def visit_int_op(self, op: IntOp) -> None: + op.lhs = self.fix_op(op.lhs) + op.rhs = self.fix_op(op.rhs) + + def visit_comparison_op(self, op: ComparisonOp) -> None: + op.lhs = self.fix_op(op.lhs) + op.rhs = self.fix_op(op.rhs) + + def visit_float_op(self, op: FloatOp) -> None: + op.lhs = self.fix_op(op.lhs) + op.rhs = self.fix_op(op.rhs) + + def visit_float_neg(self, op: FloatNeg) -> None: + op.src = self.fix_op(op.src) + + def visit_float_comparison_op(self, op: FloatComparisonOp) -> None: + op.lhs = self.fix_op(op.lhs) + op.rhs = self.fix_op(op.rhs) + + def visit_load_mem(self, op: LoadMem) -> None: + op.src = self.fix_op(op.src) + + def visit_set_mem(self, op: SetMem) -> None: + op.dest = self.fix_op(op.dest) + op.src = self.fix_op(op.src) + + def visit_get_element_ptr(self, op: GetElementPtr) -> None: + op.src = self.fix_op(op.src) + + def visit_load_address(self, op: LoadAddress) -> None: + if isinstance(op.src, LoadStatic): + new = self.fix_op(op.src) + assert isinstance(new, LoadStatic) + op.src = new + + def visit_keep_alive(self, op: KeepAlive) -> None: + op.src = [self.fix_op(s) for s in op.src] + + def visit_unborrow(self, op: Unborrow) -> None: + op.src = self.fix_op(op.src) From 4259e37875219d30427a66304033f661f8b47f8f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:31:16 +0200 Subject: [PATCH 0527/1617] Remove redundant Python 3.7 code (#17004) Some follow on from https://github.com/python/mypy/pull/15566. Also add 3.12 to tox.ini. --- mypy/modulefinder.py | 4 ++-- mypy/stubgenc.py | 2 +- mypy/util.py | 2 +- mypyc/lib-rt/CPy.h | 5 ----- mypyc/lib-rt/pythonsupport.h | 15 --------------- tox.ini | 1 + 6 files changed, 5 insertions(+), 24 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 455aa40e5975..452cfef20f4c 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -870,6 +870,6 @@ def parse_version(version: str) -> tuple[int, int]: def typeshed_py_version(options: Options) -> tuple[int, int]: """Return Python version used for checking whether module supports typeshed.""" - # Typeshed no longer covers Python 3.x versions before 3.7, so 3.7 is + # Typeshed no longer covers Python 3.x versions before 3.8, so 3.8 is # the earliest we can support. - return max(options.python_version, (3, 7)) + return max(options.python_version, (3, 8)) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 3bec0c246d9a..29b2636d39cc 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -495,7 +495,7 @@ def get_type_annotation(self, obj: object) -> str: if obj is None or obj is type(None): return "None" elif inspect.isclass(obj): - return "type[{}]".format(self.get_type_fullname(obj)) + return f"type[{self.get_type_fullname(obj)}]" elif isinstance(obj, FunctionType): return self.add_name("typing.Callable") elif isinstance(obj, ModuleType): diff --git a/mypy/util.py b/mypy/util.py index 968774ee7c98..bbb5a8610f7f 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -298,7 +298,7 @@ def _generate_junit_contents( text=escape("\n".join(messages)), filename="mypy", time=dt, - name="mypy-py{ver}-{platform}".format(ver=version, platform=platform), + name=f"mypy-py{version}-{platform}", ) xml += JUNIT_FOOTER diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 64b716945b94..1a03f049ecb0 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -544,13 +544,8 @@ void CPy_AttributeError(const char *filename, const char *funcname, const char * // Misc operations -#if PY_VERSION_HEX >= 0x03080000 #define CPy_TRASHCAN_BEGIN(op, dealloc) Py_TRASHCAN_BEGIN(op, dealloc) #define CPy_TRASHCAN_END(op) Py_TRASHCAN_END -#else -#define CPy_TRASHCAN_BEGIN(op, dealloc) Py_TRASHCAN_SAFE_BEGIN(op) -#define CPy_TRASHCAN_END(op) Py_TRASHCAN_SAFE_END(op) -#endif // Tweaked version of _PyArg_Parser in CPython typedef struct CPyArg_Parser { diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 1d493b45b89d..f7d501f44a27 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -354,21 +354,6 @@ list_count(PyListObject *self, PyObject *value) return CPyTagged_ShortFromSsize_t(count); } -#if PY_VERSION_HEX < 0x03080000 -static PyObject * -_PyDict_GetItemStringWithError(PyObject *v, const char *key) -{ - PyObject *kv, *rv; - kv = PyUnicode_FromString(key); - if (kv == NULL) { - return NULL; - } - rv = PyDict_GetItemWithError(v, kv); - Py_DECREF(kv); - return rv; -} -#endif - #define CPyUnicode_EqualToASCIIString(x, y) _PyUnicode_EqualToASCIIString(x, y) // Adapted from genobject.c in Python 3.7.2 diff --git a/tox.ini b/tox.ini index 31aed1a1ef48..c2abd05d7b6c 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ envlist = py39, py310, py311, + py312, docs, lint, type, From 16abf5cbe08c8b399381fc38220586cf2e49c2bc Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 9 Mar 2024 19:40:22 -0800 Subject: [PATCH 0528/1617] Fix type narrowing for types.EllipsisType (#17003) Fixes #17002 --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index b34efde15b31..d3c4df8b3b09 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1524,7 +1524,7 @@ def is_singleton_type(self) -> bool: return ( self.type.is_enum and len(self.get_enum_values()) == 1 - or self.type.fullname == "builtins.ellipsis" + or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"} ) def get_enum_values(self) -> list[str]: From ea49e1fa488810997d192a36d85357dadb4a7f14 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 11 Mar 2024 15:18:39 +0100 Subject: [PATCH 0529/1617] Support `TypeAliasType` (#16926) Builds on top of and supersedes #16644 --------- Co-authored-by: sobolevn --- mypy/semanal.py | 132 +++++++++++++++-- mypy/typeanal.py | 49 ++++-- test-data/unit/check-generics.test | 54 +++---- test-data/unit/check-inference.test | 2 +- .../unit/check-parameter-specification.test | 18 +-- test-data/unit/check-python312.test | 23 +++ test-data/unit/check-type-aliases.test | 139 ++++++++++++++++++ test-data/unit/fixtures/typing-full.pyi | 17 ++- test-data/unit/lib-stub/typing_extensions.pyi | 8 +- 9 files changed, 373 insertions(+), 69 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6bf02382a036..93e84ced4639 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -52,7 +52,7 @@ from contextlib import contextmanager from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, TypeVar, cast -from typing_extensions import TypeAlias as _TypeAlias +from typing_extensions import TypeAlias as _TypeAlias, TypeGuard from mypy import errorcodes as codes, message_registry from mypy.constant_fold import constant_fold_expr @@ -2018,34 +2018,35 @@ def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList def analyze_unbound_tvar(self, t: Type) -> tuple[str, TypeVarLikeExpr] | None: if isinstance(t, UnpackType) and isinstance(t.type, UnboundType): - return self.analyze_unbound_tvar_impl(t.type, allow_tvt=True) + return self.analyze_unbound_tvar_impl(t.type, is_unpacked=True) if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): inner_t = t.args[0] if isinstance(inner_t, UnboundType): - return self.analyze_unbound_tvar_impl(inner_t, allow_tvt=True) + return self.analyze_unbound_tvar_impl(inner_t, is_unpacked=True) return None return self.analyze_unbound_tvar_impl(t) return None def analyze_unbound_tvar_impl( - self, t: UnboundType, allow_tvt: bool = False + self, t: UnboundType, is_unpacked: bool = False, is_typealias_param: bool = False ) -> tuple[str, TypeVarLikeExpr] | None: + assert not is_unpacked or not is_typealias_param, "Mutually exclusive conditions" sym = self.lookup_qualified(t.name, t) if sym and isinstance(sym.node, PlaceholderNode): self.record_incomplete_ref() - if not allow_tvt and sym and isinstance(sym.node, ParamSpecExpr): + if not is_unpacked and sym and isinstance(sym.node, ParamSpecExpr): if sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope return None return t.name, sym.node - if allow_tvt and sym and isinstance(sym.node, TypeVarTupleExpr): + if (is_unpacked or is_typealias_param) and sym and isinstance(sym.node, TypeVarTupleExpr): if sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope return None return t.name, sym.node - if sym is None or not isinstance(sym.node, TypeVarExpr) or allow_tvt: + if sym is None or not isinstance(sym.node, TypeVarExpr) or is_unpacked: return None elif sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope @@ -3515,7 +3516,11 @@ def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Typ return typ def analyze_alias( - self, name: str, rvalue: Expression, allow_placeholder: bool = False + self, + name: str, + rvalue: Expression, + allow_placeholder: bool = False, + declared_type_vars: TypeVarLikeList | None = None, ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str], bool]: """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable). @@ -3540,9 +3545,10 @@ def analyze_alias( found_type_vars = self.find_type_var_likes(typ) tvar_defs: list[TypeVarLikeType] = [] namespace = self.qualified_name(name) + alias_type_vars = found_type_vars if declared_type_vars is None else declared_type_vars last_tvar_name_with_default: str | None = None with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): - for name, tvar_expr in found_type_vars: + for name, tvar_expr in alias_type_vars: tvar_expr.default = tvar_expr.default.accept( TypeVarDefaultTranslator(self, tvar_expr.name, typ) ) @@ -3567,6 +3573,7 @@ def analyze_alias( in_dynamic_func=dynamic, global_scope=global_scope, allowed_alias_tvars=tvar_defs, + has_type_params=declared_type_vars is not None, ) # There can be only one variadic variable at most, the error is reported elsewhere. @@ -3579,7 +3586,7 @@ def analyze_alias( variadic = True new_tvar_defs.append(td) - qualified_tvars = [node.fullname for _name, node in found_type_vars] + qualified_tvars = [node.fullname for _name, node in alias_type_vars] empty_tuple_index = typ.empty_tuple_index if isinstance(typ, UnboundType) else False return analyzed, new_tvar_defs, depends_on, qualified_tvars, empty_tuple_index @@ -3612,7 +3619,19 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # unless using PEP 613 `cls: TypeAlias = A` return False - if isinstance(s.rvalue, CallExpr) and s.rvalue.analyzed: + # It can be `A = TypeAliasType('A', ...)` call, in this case, + # we just take the second argument and analyze it: + type_params: TypeVarLikeList | None + if self.check_type_alias_type_call(s.rvalue, name=lvalue.name): + rvalue = s.rvalue.args[1] + pep_695 = True + type_params = self.analyze_type_alias_type_params(s.rvalue) + else: + rvalue = s.rvalue + pep_695 = False + type_params = None + + if isinstance(rvalue, CallExpr) and rvalue.analyzed: return False existing = self.current_symbol_table().get(lvalue.name) @@ -3638,7 +3657,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: return False non_global_scope = self.type or self.is_func_scope() - if not pep_613 and isinstance(s.rvalue, RefExpr) and non_global_scope: + if not pep_613 and isinstance(rvalue, RefExpr) and non_global_scope: # Fourth rule (special case): Non-subscripted right hand side creates a variable # at class and function scopes. For example: # @@ -3650,8 +3669,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # without this rule, this typical use case will require a lot of explicit # annotations (see the second rule). return False - rvalue = s.rvalue - if not pep_613 and not self.can_be_type_alias(rvalue): + if not pep_613 and not pep_695 and not self.can_be_type_alias(rvalue): return False if existing and not isinstance(existing.node, (PlaceholderNode, TypeAlias)): @@ -3668,7 +3686,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: else: tag = self.track_incomplete_refs() res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( - lvalue.name, rvalue, allow_placeholder=True + lvalue.name, rvalue, allow_placeholder=True, declared_type_vars=type_params ) if not res: return False @@ -3698,13 +3716,15 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) # Note: with the new (lazy) type alias representation we only need to set no_args to True - # if the expected number of arguments is non-zero, so that aliases like A = List work. + # if the expected number of arguments is non-zero, so that aliases like `A = List` work + # but not aliases like `A = TypeAliasType("A", List)` as these need explicit type params. # However, eagerly expanding aliases like Text = str is a nice performance optimization. no_args = ( isinstance(res, ProperType) and isinstance(res, Instance) and not res.args and not empty_tuple_index + and not pep_695 ) if isinstance(res, ProperType) and isinstance(res, Instance): if not validate_instance(res, self.fail, empty_tuple_index): @@ -3771,6 +3791,80 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: self.note("Use variable annotation syntax to define protocol members", s) return True + def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGuard[CallExpr]: + if not isinstance(rvalue, CallExpr): + return False + + names = ["typing_extensions.TypeAliasType"] + if self.options.python_version >= (3, 12): + names.append("typing.TypeAliasType") + if not refers_to_fullname(rvalue.callee, tuple(names)): + return False + + return self.check_typevarlike_name(rvalue, name, rvalue) + + def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList: + if "type_params" in rvalue.arg_names: + type_params_arg = rvalue.args[rvalue.arg_names.index("type_params")] + if not isinstance(type_params_arg, TupleExpr): + self.fail( + "Tuple literal expected as the type_params argument to TypeAliasType", + type_params_arg, + ) + return [] + type_params = type_params_arg.items + else: + type_params = [] + + declared_tvars: TypeVarLikeList = [] + have_type_var_tuple = False + for tp_expr in type_params: + if isinstance(tp_expr, StarExpr): + tp_expr.valid = False + self.analyze_type_expr(tp_expr) + try: + base = self.expr_to_unanalyzed_type(tp_expr) + except TypeTranslationError: + continue + if not isinstance(base, UnboundType): + continue + + tag = self.track_incomplete_refs() + tvar = self.analyze_unbound_tvar_impl(base, is_typealias_param=True) + if tvar: + if isinstance(tvar[1], TypeVarTupleExpr): + if have_type_var_tuple: + self.fail( + "Can only use one TypeVarTuple in type_params argument to TypeAliasType", + base, + code=codes.TYPE_VAR, + ) + have_type_var_tuple = True + continue + have_type_var_tuple = True + elif not self.found_incomplete_ref(tag): + self.fail( + "Free type variable expected in type_params argument to TypeAliasType", + base, + code=codes.TYPE_VAR, + ) + sym = self.lookup_qualified(base.name, base) + if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + self.note( + "Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR + ) + continue + if tvar in declared_tvars: + self.fail( + f'Duplicate type variable "{tvar[0]}" in type_params argument to TypeAliasType', + base, + code=codes.TYPE_VAR, + ) + continue + if tvar: + declared_tvars.append(tvar) + return declared_tvars + def disable_invalid_recursive_aliases( self, s: AssignmentStmt, current_node: TypeAlias ) -> None: @@ -5187,6 +5281,12 @@ def visit_call_expr(self, expr: CallExpr) -> None: expr.analyzed = OpExpr("divmod", expr.args[0], expr.args[1]) expr.analyzed.line = expr.line expr.analyzed.accept(self) + elif refers_to_fullname( + expr.callee, ("typing.TypeAliasType", "typing_extensions.TypeAliasType") + ): + with self.allow_unbound_tvars_set(): + for a in expr.args: + a.accept(self) else: # Normal call expression. for a in expr.args: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 8a9ac8f4ac31..470b07948535 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -141,6 +141,7 @@ def analyze_type_alias( in_dynamic_func: bool = False, global_scope: bool = True, allowed_alias_tvars: list[TypeVarLikeType] | None = None, + has_type_params: bool = False, ) -> tuple[Type, set[str]]: """Analyze r.h.s. of a (potential) type alias definition. @@ -158,6 +159,7 @@ def analyze_type_alias( allow_placeholder=allow_placeholder, prohibit_self_type="type alias target", allowed_alias_tvars=allowed_alias_tvars, + has_type_params=has_type_params, ) analyzer.in_dynamic_func = in_dynamic_func analyzer.global_scope = global_scope @@ -210,6 +212,7 @@ def __init__( prohibit_self_type: str | None = None, allowed_alias_tvars: list[TypeVarLikeType] | None = None, allow_type_any: bool = False, + has_type_params: bool = False, ) -> None: self.api = api self.fail_func = api.fail @@ -231,6 +234,7 @@ def __init__( if allowed_alias_tvars is None: allowed_alias_tvars = [] self.allowed_alias_tvars = allowed_alias_tvars + self.has_type_params = has_type_params # If false, record incomplete ref if we generate PlaceholderType. self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? @@ -325,7 +329,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if tvar_def is None: if self.allow_unbound_tvars: return t - self.fail(f'ParamSpec "{t.name}" is unbound', t, code=codes.VALID_TYPE) + if self.defining_alias and self.has_type_params: + msg = f'ParamSpec "{t.name}" is not included in type_params' + else: + msg = f'ParamSpec "{t.name}" is unbound' + self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, ParamSpecType) if len(t.args) > 0: @@ -349,11 +357,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) and not defining_literal and (tvar_def is None or tvar_def not in self.allowed_alias_tvars) ): - self.fail( - f'Can\'t use bound type variable "{t.name}" to define generic alias', - t, - code=codes.VALID_TYPE, - ) + if self.has_type_params: + msg = f'Type variable "{t.name}" is not included in type_params' + else: + msg = f'Can\'t use bound type variable "{t.name}" to define generic alias' + self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) if isinstance(sym.node, TypeVarExpr) and tvar_def is not None: assert isinstance(tvar_def, TypeVarType) @@ -368,17 +376,21 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) and self.defining_alias and tvar_def not in self.allowed_alias_tvars ): - self.fail( - f'Can\'t use bound type variable "{t.name}" to define generic alias', - t, - code=codes.VALID_TYPE, - ) + if self.has_type_params: + msg = f'Type variable "{t.name}" is not included in type_params' + else: + msg = f'Can\'t use bound type variable "{t.name}" to define generic alias' + self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) if isinstance(sym.node, TypeVarTupleExpr): if tvar_def is None: if self.allow_unbound_tvars: return t - self.fail(f'TypeVarTuple "{t.name}" is unbound', t, code=codes.VALID_TYPE) + if self.defining_alias and self.has_type_params: + msg = f'TypeVarTuple "{t.name}" is not included in type_params' + else: + msg = f'TypeVarTuple "{t.name}" is unbound' + self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, TypeVarTupleType) if not self.allow_type_var_tuple: @@ -1267,6 +1279,19 @@ def analyze_callable_args_for_paramspec( AnyType(TypeOfAny.explicit), ret_type=ret_type, fallback=fallback ) return None + elif ( + self.defining_alias + and self.has_type_params + and tvar_def not in self.allowed_alias_tvars + ): + self.fail( + f'ParamSpec "{callable_args.name}" is not included in type_params', + callable_args, + code=codes.VALID_TYPE, + ) + return callable_with_ellipsis( + AnyType(TypeOfAny.special_form), ret_type=ret_type, fallback=fallback + ) return CallableType( [ diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index f4b7c14bd053..b1d1ff3f46a1 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3124,8 +3124,8 @@ def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (x: T`2) -> builtins.list[T`2]" -reveal_type(dec(either)) # N: Revealed type is "def [T] (x: T`4, y: T`4) -> builtins.list[T`4]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (x: T`5, y: T`5) -> builtins.list[T`5]" reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" [builtins fixtures/list.pyi] @@ -3142,8 +3142,8 @@ V = TypeVar('V') def dec(f: Callable[P, List[T]]) -> Callable[P, T]: ... def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (x: builtins.list[T`2]) -> T`2" -reveal_type(dec(either)) # N: Revealed type is "def [T] (x: builtins.list[T`4], y: builtins.list[T`4]) -> T`4" +reveal_type(dec(id)) # N: Revealed type is "def [T] (x: builtins.list[T`3]) -> T`3" +reveal_type(dec(either)) # N: Revealed type is "def [T] (x: builtins.list[T`5], y: builtins.list[T`5]) -> T`5" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecPopOff] @@ -3161,9 +3161,9 @@ def dec(f: Callable[Concatenate[T, P], S]) -> Callable[P, Callable[[T], S]]: ... def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" -reveal_type(dec(either)) # N: Revealed type is "def [T] (y: T`4) -> def (T`4) -> T`4" -reveal_type(dec(pair)) # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`7) -> Tuple[T`7, V`-2]" +reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`2) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (y: T`5) -> def (T`5) -> T`5" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]" reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, P, S] (def (T`-1, *P.args, **P.kwargs) -> S`-3) -> def (*P.args, **P.kwargs) -> def (T`-1) -> S`-3" [builtins fixtures/list.pyi] @@ -3182,11 +3182,11 @@ def dec(f: Callable[P, Callable[[T], S]]) -> Callable[Concatenate[T, P], S]: ... def id() -> Callable[[U], U]: ... def either(x: U) -> Callable[[U], U]: ... def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> T`2" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, x: T`5) -> T`5" -reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`8, x: U`-1) -> Tuple[T`8, U`-1]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, x: T`6) -> T`6" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> Tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if P can be empty -reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`11, f: def () -> def (T`11) -> S`12) -> S`12" +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`12, f: def () -> def (T`12) -> S`13) -> S`13" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecVsParamSpec] @@ -3203,7 +3203,7 @@ class Bar(Generic[P, T]): ... def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def f(*args: Q.args, **kwargs: Q.kwargs) -> Foo[Q]: ... -reveal_type(dec(f)) # N: Revealed type is "def [P] (*P.args, **P.kwargs) -> builtins.list[__main__.Foo[P`1]]" +reveal_type(dec(f)) # N: Revealed type is "def [P] (*P.args, **P.kwargs) -> builtins.list[__main__.Foo[P`2]]" g: Callable[Concatenate[int, Q], Foo[Q]] reveal_type(dec(g)) # N: Revealed type is "def [Q] (builtins.int, *Q.args, **Q.kwargs) -> builtins.list[__main__.Foo[Q`-1]]" h: Callable[Concatenate[T, Q], Bar[Q, T]] @@ -3264,8 +3264,8 @@ def transform( def dec(f: Callable[W, U]) -> Callable[W, U]: ... def dec2(f: Callable[Concatenate[str, W], U]) -> Callable[Concatenate[bytes, W], U]: ... -reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`2) -> def (builtins.int, *P.args, **P.kwargs) -> T`2" -reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`6) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`6" +reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`3) -> def (builtins.int, *P.args, **P.kwargs) -> T`3" +reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`7) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`7" [builtins fixtures/tuple.pyi] [case testNoAccidentalVariableClashInNestedGeneric] @@ -3319,8 +3319,8 @@ def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> builtins.list[T`2]" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`4, T`4) -> builtins.list[T`4]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> builtins.list[T`5]" reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" [builtins fixtures/tuple.pyi] @@ -3338,8 +3338,8 @@ V = TypeVar("V") def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`2]) -> T`2" -reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`4], builtins.list[T`4]) -> T`4" +reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`3]) -> T`3" +reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`5], builtins.list[T`5]) -> T`5" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicPopOff] @@ -3358,9 +3358,9 @@ def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`1) -> T`1" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`4) -> def (T`4) -> T`4" -reveal_type(dec(pair)) # N: Revealed type is "def [V] (V`-2) -> def [T] (T`7) -> Tuple[T`7, V`-2]" +reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`2) -> T`2" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5) -> def (T`5) -> T`5" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]" reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, Ts, S] (def (T`-1, *Unpack[Ts`-2]) -> S`-3) -> def (*Unpack[Ts`-2]) -> def (T`-1) -> S`-3" [builtins fixtures/list.pyi] @@ -3380,11 +3380,11 @@ def id() -> Callable[[U], U]: ... def either(x: U) -> Callable[[U], U]: ... def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (T`2) -> T`2" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> T`5" -reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`8, U`-1) -> Tuple[T`8, U`-1]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, T`6) -> T`6" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, U`-1) -> Tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if Ts is empty -reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`11, def () -> def (T`11) -> S`12) -> S`12" +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`12, def () -> def (T`12) -> S`13) -> S`13" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicVsVariadic] @@ -3402,9 +3402,9 @@ class Bar(Generic[Unpack[Ts], T]): ... def dec(f: Callable[[Unpack[Ts]], T]) -> Callable[[Unpack[Ts]], List[T]]: ... def f(*args: Unpack[Us]) -> Foo[Unpack[Us]]: ... -reveal_type(dec(f)) # N: Revealed type is "def [Ts] (*Unpack[Ts`1]) -> builtins.list[__main__.Foo[Unpack[Ts`1]]]" +reveal_type(dec(f)) # N: Revealed type is "def [Ts] (*Unpack[Ts`2]) -> builtins.list[__main__.Foo[Unpack[Ts`2]]]" g: Callable[[Unpack[Us]], Foo[Unpack[Us]]] -reveal_type(dec(g)) # N: Revealed type is "def [Ts] (*Unpack[Ts`3]) -> builtins.list[__main__.Foo[Unpack[Ts`3]]]" +reveal_type(dec(g)) # N: Revealed type is "def [Ts] (*Unpack[Ts`4]) -> builtins.list[__main__.Foo[Unpack[Ts`4]]]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicVsVariadicConcatenate] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 1b1ce607bf28..08b53ab16972 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3807,7 +3807,7 @@ def Negate(count: int, /, metric: Metric[float]) -> float: ... def Combine(count: int, m1: Metric[T], m2: Metric[T], /, *more: Metric[T]) -> T: ... reveal_type(Negate) # N: Revealed type is "def (metric: __main__.Metric[builtins.float]) -> builtins.float" -reveal_type(Combine) # N: Revealed type is "def [T] (def () -> T`4, def () -> T`4, *more: def () -> T`4) -> T`4" +reveal_type(Combine) # N: Revealed type is "def [T] (def () -> T`5, def () -> T`5, *more: def () -> T`5) -> T`5" def m1() -> float: ... def m2() -> float: ... diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index b212c7585993..8fd9abcb9752 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -901,8 +901,8 @@ class A: def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`3, *_P.args, **_P.kwargs) -> _R`3" -reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`7, *_P.args, **_P.kwargs) -> _R`7" +reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`4, *_P.args, **_P.kwargs) -> _R`4" +reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`8, *_P.args, **_P.kwargs) -> _R`8" def f(x: int) -> int: ... @@ -933,8 +933,8 @@ class A: def func(self, action: Job[_P, None]) -> Job[_P, None]: ... -reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`2, None]) -> __main__.Job[_P`2, None]" -reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`4, None]) -> __main__.Job[_P`4, None]" +reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]" +reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`5, None]) -> __main__.Job[_P`5, None]" reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" def f(x: int, y: int) -> None: ... @@ -1096,7 +1096,7 @@ j = Job(generic_f) reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`2)" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`3)" reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] @@ -1115,10 +1115,10 @@ class Job(Generic[_P, _T]): def generic_f(x: _T) -> _T: ... j = Job(generic_f) -reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`2], _T`2]" +reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`3], _T`3]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`3) -> _T`3" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`4) -> _T`4" reveal_type(jf(1)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] @@ -1619,13 +1619,13 @@ U = TypeVar("U") def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def test(x: U) -> U: ... reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]" -reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`2) -> builtins.list[T`2]" +reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]" class A: ... TA = TypeVar("TA", bound=A) def test_with_bound(x: TA) -> TA: ... -reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`4) -> builtins.list[T`4]" +reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`5) -> builtins.list[T`5]" dec(test_with_bound)(0) # E: Value of type variable "T" of function cannot be "int" dec(test_with_bound)(A()) # OK [builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 285563c19991..188c51f98185 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -60,3 +60,26 @@ def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 gener def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported \ # E: Name "Ts" is not defined [builtins fixtures/tuple.pyi] + +[case test695TypeAliasType] +from typing import Callable, TypeAliasType, TypeVar, TypeVarTuple + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +TestType = TypeAliasType("TestType", int | str) +x: TestType = 42 +y: TestType = 'a' +z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") + +BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is not included in type_params +ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]" + +# TODO this should report errors on the two following lines +#BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) +#ba2: BadAlias2[int] +#reveal_type(ba2) + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index a43233eed973..79a443dbeedc 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1065,3 +1065,142 @@ def eval(e: Expr) -> int: elif e[0] == 456: return -eval(e[1]) [builtins fixtures/dict-full.pyi] + +[case testTypeAliasType] +from typing import Union +from typing_extensions import TypeAliasType + +TestType = TypeAliasType("TestType", Union[int, str]) +x: TestType = 42 +y: TestType = 'a' +z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") +[builtins fixtures/tuple.pyi] + +[case testTypeAliasTypeInvalid] +from typing_extensions import TypeAliasType + +TestType = TypeAliasType("T", int) # E: String argument 1 "T" to TypeAliasType(...) does not match variable name "TestType" + +T1 = T2 = TypeAliasType("T", int) +t1: T1 # E: Variable "__main__.T1" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases + +T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead? +t3: T3 +reveal_type(t3) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testTypeAliasTypeGeneric] +from typing import Callable, Dict, Generic, TypeVar, Tuple +from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec, Unpack + +K = TypeVar('K') +V = TypeVar('V') +T = TypeVar('T') +Ts = TypeVarTuple("Ts") +Ts1 = TypeVarTuple("Ts1") +P = ParamSpec("P") + +TestType = TypeAliasType("TestType", Dict[K, V], type_params=(K, V)) +x: TestType[int, str] = {1: 'a'} +y: TestType[str, int] = {'a': 1} +z: TestType[str, int] = {1: 'a'} # E: Dict entry 0 has incompatible type "int": "str"; expected "str": "int" +w: TestType[int] # E: Bad number of arguments for type alias, expected 2, given 1 + +InvertedDict = TypeAliasType("InvertedDict", Dict[K, V], type_params=(V, K)) +xi: InvertedDict[str, int] = {1: 'a'} +yi: InvertedDict[str, int] = {'a': 1} # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "str" +zi: InvertedDict[int, str] = {1: 'a'} # E: Dict entry 0 has incompatible type "int": "str"; expected "str": "int" +reveal_type(xi) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + +VariadicAlias1 = TypeAliasType("VariadicAlias1", Tuple[Unpack[Ts]], type_params=(Ts,)) +VariadicAlias2 = TypeAliasType("VariadicAlias2", Tuple[Unpack[Ts], K], type_params=(Ts, K)) +VariadicAlias3 = TypeAliasType("VariadicAlias3", Callable[[Unpack[Ts]], int], type_params=(Ts,)) +xv: VariadicAlias1[int, str] = (1, 'a') +yv: VariadicAlias1[str, int] = (1, 'a') # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "Tuple[str, int]") +zv: VariadicAlias2[int, str] = (1, 'a') +def int_in_int_out(x: int) -> int: return x +wv: VariadicAlias3[int] = int_in_int_out +reveal_type(wv) # N: Revealed type is "def (builtins.int) -> builtins.int" + +ParamAlias = TypeAliasType("ParamAlias", Callable[P, int], type_params=(P,)) +def f(x: str, y: float) -> int: return 1 +def g(x: int, y: float) -> int: return 1 +xp1: ParamAlias[str, float] = f +xp2: ParamAlias[str, float] = g # E: Incompatible types in assignment (expression has type "Callable[[int, float], int]", variable has type "Callable[[str, float], int]") +xp3: ParamAlias[str, float] = lambda x, y: 1 + +class G(Generic[P, T]): ... +ParamAlias2 = TypeAliasType("ParamAlias2", G[P, T], type_params=(P, T)) +xp: ParamAlias2[[int], str] +reveal_type(xp) # N: Revealed type is "__main__.G[[builtins.int], builtins.str]" +[builtins fixtures/dict.pyi] + +[case testTypeAliasTypeInvalidGeneric] +from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec +from typing import Callable, Dict, Generic, TypeVar, Tuple, Unpack + +K = TypeVar('K') +V = TypeVar('V') +T = TypeVar('T') +Ts = TypeVarTuple("Ts") +Ts1 = TypeVarTuple("Ts1") +P = ParamSpec("P") + +Ta0 = TypeAliasType("Ta0", int, type_params=(T, T)) # E: Duplicate type variable "T" in type_params argument to TypeAliasType + +Ta1 = TypeAliasType("Ta1", int, type_params=K) # E: Tuple literal expected as the type_params argument to TypeAliasType + +Ta2 = TypeAliasType("Ta2", int, type_params=(None,)) # E: Free type variable expected in type_params argument to TypeAliasType + +Ta3 = TypeAliasType("Ta3", Dict[K, V], type_params=(V,)) # E: Type variable "K" is not included in type_params +partially_generic1: Ta3[int] = {"a": 1} +reveal_type(partially_generic1) # N: Revealed type is "builtins.dict[Any, builtins.int]" +partially_generic2: Ta3[int] = {1: "a"} # E: Dict entry 0 has incompatible type "int": "str"; expected "Any": "int" + +Ta4 = TypeAliasType("Ta4", Tuple[Unpack[Ts]], type_params=(Ts, Ts1)) # E: Can only use one TypeVarTuple in type_params argument to TypeAliasType + +Ta5 = TypeAliasType("Ta5", Dict) # Unlike old style aliases, this is not generic +non_generic_dict: Ta5[int, str] # E: Bad number of arguments for type alias, expected 0, given 2 +reveal_type(non_generic_dict) # N: Revealed type is "builtins.dict[Any, Any]" + +Ta6 = TypeAliasType("Ta6", Tuple[Unpack[Ts]]) # E: TypeVarTuple "Ts" is not included in type_params +unbound_tvt_alias: Ta6[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(unbound_tvt_alias) # N: Revealed type is "builtins.tuple[Any, ...]" + +class G(Generic[P, T]): ... +Ta7 = TypeAliasType("Ta7", G[P, T]) # E: ParamSpec "P" is not included in type_params \ + # E: Type variable "T" is not included in type_params +unbound_ps_alias: Ta7[[int], str] # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: Bad number of arguments for type alias, expected 0, given 2 +reveal_type(unbound_ps_alias) # N: Revealed type is "__main__.G[Any, Any]" + +Ta8 = TypeAliasType("Ta8", Callable[P, int]) # E: ParamSpec "P" is not included in type_params +unbound_ps_alias2: Ta8[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(unbound_ps_alias2) # N: Revealed type is "def [P] (*Any, **Any) -> builtins.int" + +Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included in type_params \ + # E: Type variable "T" is not included in type_params +unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2 +reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any" + +# TODO this should report errors on the two following lines +#Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) +#unbound_tvt_alias2: Ta10[int] +#reveal_type(unbound_tvt_alias2) + +[builtins fixtures/dict.pyi] + +[case testTypeAliasTypeNoUnpackInTypeParams311] +# flags: --python-version 3.11 +from typing_extensions import TypeAliasType, TypeVar, TypeVarTuple, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +Ta1 = TypeAliasType("Ta1", None, type_params=(*Ts,)) # E: can't use starred expression here +Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type variable expected in type_params argument to TypeAliasType \ + # N: Don't Unpack type variables in type_params + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index ca8a2413f05f..f7da75fa4cd0 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -10,13 +10,17 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass +class _SpecialForm: ... +class TypeVar: ... +class ParamSpec: ... +class TypeVarTuple: ... + def cast(t, o): ... def assert_type(o, t): ... overload = 0 Any = 0 Union = 0 Optional = 0 -TypeVar = 0 Generic = 0 Protocol = 0 Tuple = 0 @@ -39,6 +43,8 @@ U = TypeVar('U') V = TypeVar('V') S = TypeVar('S') +def final(x: T) -> T: ... + class NamedTuple(tuple[Any, ...]): ... # Note: definitions below are different from typeshed, variances are declared @@ -182,8 +188,6 @@ class _TypedDict(Mapping[str, object]): def update(self: T, __m: T) -> None: ... def __delitem__(self, k: NoReturn) -> None: ... -class _SpecialForm: pass - def dataclass_transform( *, eq_default: bool = ..., @@ -199,3 +203,10 @@ def reveal_type(__obj: T) -> T: ... # Only exists in type checking time: def type_check_only(__func_or_class: T) -> T: ... + +# Was added in 3.12 +@final +class TypeAliasType: + def __init__( + self, name: str, value: Any, *, type_params: Tuple[Union[TypeVar, ParamSpec, TypeVarTuple], ...] = () + ) -> None: ... diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index ff55f1b54c7d..b7b738f63d92 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -1,5 +1,5 @@ import typing -from typing import Any, Callable, Mapping, Iterable, Iterator, NoReturn as NoReturn, Dict, Tuple, Type +from typing import Any, Callable, Mapping, Iterable, Iterator, NoReturn as NoReturn, Dict, Tuple, Type, Union from typing import TYPE_CHECKING as TYPE_CHECKING from typing import NewType as NewType, overload as overload @@ -40,6 +40,12 @@ Never: _SpecialForm TypeVarTuple: _SpecialForm Unpack: _SpecialForm +@final +class TypeAliasType: + def __init__( + self, name: str, value: Any, *, type_params: Tuple[Union[TypeVar, ParamSpec, TypeVarTuple], ...] = () + ) -> None: ... + # Fallback type for all typed dicts (does not exist at runtime). class _TypedDict(Mapping[str, object]): # Needed to make this class non-abstract. It is explicitly declared abstract in From a00fcba1e77ac944276b8c4ad0a31b7b05ded59f Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 13 Mar 2024 16:42:20 +0000 Subject: [PATCH 0530/1617] Revert "Revert use of `ParamSpec` for `functools.wraps`" (#16942) ParamSpec support has improved so it doesn't seem necessary to revert the changes any more. --- misc/generate_changelog.py | 1 - misc/sync-typeshed.py | 1 - mypy/typeshed/stdlib/functools.pyi | 40 +++++++++++++++++++----------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/misc/generate_changelog.py b/misc/generate_changelog.py index 7c7f28b6eeb7..ebab6c569152 100644 --- a/misc/generate_changelog.py +++ b/misc/generate_changelog.py @@ -79,7 +79,6 @@ def filter_omitted_commits(commits: list[CommitInfo]) -> list[CommitInfo]: "Revert sum literal integer change", "Remove use of LiteralString in builtins", "Revert typeshed ctypes change", - "Revert use of `ParamSpec` for `functools.wraps`", ) ): # These are generated by a typeshed sync. diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index ee6414ab7b19..56bc1624d5d0 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -182,7 +182,6 @@ def main() -> None: "d25e4a9eb", # LiteralString reverts "d132999ba", # sum reverts "dd12a2d81", # ctypes reverts - "0dd4b6f75", # ParamSpec for functools.wraps ] for commit in commits_to_cherry_pick: try: diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 991182486113..d3f702bcef4f 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,9 +1,9 @@ import sys import types -from _typeshed import IdentityFunction, SupportsAllComparisons, SupportsItems +from _typeshed import SupportsAllComparisons, SupportsItems from collections.abc import Callable, Hashable, Iterable, Sequence, Sized from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import ParamSpec, Self, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -27,11 +27,13 @@ __all__ = [ if sys.version_info >= (3, 9): __all__ += ["cache"] -_AnyCallable: TypeAlias = Callable[..., object] - _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _S = TypeVar("_S") +_PWrapped = ParamSpec("_PWrapped") +_RWrapped = TypeVar("_RWrapped") +_PWrapper = ParamSpec("_PWrapper") +_RWrapper = TypeVar("_RWrapper") @overload def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... @@ -81,31 +83,41 @@ else: ] WRAPPER_UPDATES: tuple[Literal["__dict__"]] +class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): + __wrapped__: Callable[_PWrapped, _RWrapped] + def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... + # as with ``Callable``, we'll assume that these attributes exist + __name__: str + __qualname__: str + +class _Wrapper(Generic[_PWrapped, _RWrapped]): + def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + if sys.version_info >= (3, 12): def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( - wrapper: _T, - wrapped: _AnyCallable, + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> _T: ... + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( - wrapped: _AnyCallable, + wrapped: Callable[_PWrapped, _RWrapped], assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), updated: Sequence[str] = ("__dict__",), - ) -> IdentityFunction: ... + ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... From a18a0db0c77e71050aaf31a53ad1fba8c663fd1a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 14 Mar 2024 17:14:59 +0000 Subject: [PATCH 0531/1617] [mypyc] Optimize away some bool/bit registers (#17022) If a register is always used in a branch immediately after assignment, and it isn't used for anything else, we can replace the assignment with a branch op. This avoids some assignment ops and gotos. This is not a very interesting optimization in general, but it will help a lot with tagged integer operations once I refactor them to be generated in the lowering pass (in follow-up PRs). --- mypyc/codegen/emitmodule.py | 4 +- mypyc/test-data/opt-flag-elimination.test | 300 ++++++++++++++++++ ...y_propagation.py => test_optimizations.py} | 31 +- mypyc/transform/flag_elimination.py | 108 +++++++ mypyc/transform/ir_transform.py | 16 +- 5 files changed, 445 insertions(+), 14 deletions(-) create mode 100644 mypyc/test-data/opt-flag-elimination.test rename mypyc/test/{test_copy_propagation.py => test_optimizations.py} (62%) create mode 100644 mypyc/transform/flag_elimination.py diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 0035bd53188b..9466bc2cea79 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -58,6 +58,7 @@ from mypyc.options import CompilerOptions from mypyc.transform.copy_propagation import do_copy_propagation from mypyc.transform.exceptions import insert_exception_handling +from mypyc.transform.flag_elimination import do_flag_elimination from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.transform.uninit import insert_uninit_checks @@ -234,8 +235,9 @@ def compile_scc_to_ir( insert_exception_handling(fn) # Insert refcount handling. insert_ref_count_opcodes(fn) - # Perform copy propagation optimization. + # Perform optimizations. do_copy_propagation(fn, compiler_options) + do_flag_elimination(fn, compiler_options) return modules diff --git a/mypyc/test-data/opt-flag-elimination.test b/mypyc/test-data/opt-flag-elimination.test new file mode 100644 index 000000000000..f047a87dc3fa --- /dev/null +++ b/mypyc/test-data/opt-flag-elimination.test @@ -0,0 +1,300 @@ +-- Test cases for "flag elimination" optimization. Used to optimize away +-- registers that are always used immediately after assignment as branch conditions. + +[case testFlagEliminationSimple] +def c() -> bool: + return True +def d() -> bool: + return True + +def f(x: bool) -> int: + if x: + b = c() + else: + b = d() + if b: + return 1 + else: + return 2 +[out] +def c(): +L0: + return 1 +def d(): +L0: + return 1 +def f(x): + x, r0, r1 :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + r0 = c() + if r0 goto L4 else goto L5 :: bool +L2: + r1 = d() + if r1 goto L4 else goto L5 :: bool +L3: + unreachable +L4: + return 2 +L5: + return 4 + +[case testFlagEliminationOneAssignment] +def c() -> bool: + return True + +def f(x: bool) -> int: + # Not applied here + b = c() + if b: + return 1 + else: + return 2 +[out] +def c(): +L0: + return 1 +def f(x): + x, r0, b :: bool +L0: + r0 = c() + b = r0 + if b goto L1 else goto L2 :: bool +L1: + return 2 +L2: + return 4 + +[case testFlagEliminationThreeCases] +def c(x: int) -> bool: + return True + +def f(x: bool, y: bool) -> int: + if x: + b = c(1) + elif y: + b = c(2) + else: + b = c(3) + if b: + return 1 + else: + return 2 +[out] +def c(x): + x :: int +L0: + return 1 +def f(x, y): + x, y, r0, r1, r2 :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + r0 = c(2) + if r0 goto L6 else goto L7 :: bool +L2: + if y goto L3 else goto L4 :: bool +L3: + r1 = c(4) + if r1 goto L6 else goto L7 :: bool +L4: + r2 = c(6) + if r2 goto L6 else goto L7 :: bool +L5: + unreachable +L6: + return 2 +L7: + return 4 + +[case testFlagEliminationAssignmentNotLastOp] +def f(x: bool) -> int: + y = 0 + if x: + b = True + y = 1 + else: + b = False + if b: + return 1 + else: + return 2 +[out] +def f(x): + x :: bool + y :: int + b :: bool +L0: + y = 0 + if x goto L1 else goto L2 :: bool +L1: + b = 1 + y = 2 + goto L3 +L2: + b = 0 +L3: + if b goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testFlagEliminationAssignmentNoDirectGoto] +def f(x: bool) -> int: + if x: + b = True + else: + b = False + if x: + if b: + return 1 + else: + return 2 + return 4 +[out] +def f(x): + x, b :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + b = 1 + goto L3 +L2: + b = 0 +L3: + if x goto L4 else goto L7 :: bool +L4: + if b goto L5 else goto L6 :: bool +L5: + return 2 +L6: + return 4 +L7: + return 8 + +[case testFlagEliminationBranchNotNextOpAfterGoto] +def f(x: bool) -> int: + if x: + b = True + else: + b = False + y = 1 # Prevents the optimization + if b: + return 1 + else: + return 2 +[out] +def f(x): + x, b :: bool + y :: int +L0: + if x goto L1 else goto L2 :: bool +L1: + b = 1 + goto L3 +L2: + b = 0 +L3: + y = 2 + if b goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testFlagEliminationFlagReadTwice] +def f(x: bool) -> bool: + if x: + b = True + else: + b = False + if b: + return b # Prevents the optimization + else: + return False +[out] +def f(x): + x, b :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + b = 1 + goto L3 +L2: + b = 0 +L3: + if b goto L4 else goto L5 :: bool +L4: + return b +L5: + return 0 + +[case testFlagEliminationArgumentNotEligible] +def f(x: bool, b: bool) -> bool: + if x: + b = True + else: + b = False + if b: + return True + else: + return False +[out] +def f(x, b): + x, b :: bool +L0: + if x goto L1 else goto L2 :: bool +L1: + b = 1 + goto L3 +L2: + b = 0 +L3: + if b goto L4 else goto L5 :: bool +L4: + return 1 +L5: + return 0 + +[case testFlagEliminationFlagNotAlwaysDefined] +def f(x: bool, y: bool) -> bool: + if x: + b = True + elif y: + b = False + else: + bb = False # b not assigned here -> can't optimize + if b: + return True + else: + return False +[out] +def f(x, y): + x, y, r0, b, bb, r1 :: bool +L0: + r0 = :: bool + b = r0 + if x goto L1 else goto L2 :: bool +L1: + b = 1 + goto L5 +L2: + if y goto L3 else goto L4 :: bool +L3: + b = 0 + goto L5 +L4: + bb = 0 +L5: + if is_error(b) goto L6 else goto L7 +L6: + r1 = raise UnboundLocalError('local variable "b" referenced before assignment') + unreachable +L7: + if b goto L8 else goto L9 :: bool +L8: + return 1 +L9: + return 0 diff --git a/mypyc/test/test_copy_propagation.py b/mypyc/test/test_optimizations.py similarity index 62% rename from mypyc/test/test_copy_propagation.py rename to mypyc/test/test_optimizations.py index c729e3d186c3..3f1f46ac1dd7 100644 --- a/mypyc/test/test_copy_propagation.py +++ b/mypyc/test/test_optimizations.py @@ -1,4 +1,4 @@ -"""Runner for copy propagation optimization tests.""" +"""Runner for IR optimization tests.""" from __future__ import annotations @@ -8,6 +8,7 @@ from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase from mypyc.common import TOP_LEVEL_NAME +from mypyc.ir.func_ir import FuncIR from mypyc.ir.pprint import format_func from mypyc.options import CompilerOptions from mypyc.test.testutil import ( @@ -19,13 +20,16 @@ use_custom_builtins, ) from mypyc.transform.copy_propagation import do_copy_propagation +from mypyc.transform.flag_elimination import do_flag_elimination from mypyc.transform.uninit import insert_uninit_checks -files = ["opt-copy-propagation.test"] +class OptimizationSuite(MypycDataSuite): + """Base class for IR optimization test suites. + + To use this, add a base class and define "files" and "do_optimizations". + """ -class TestCopyPropagation(MypycDataSuite): - files = files base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: @@ -41,7 +45,24 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"): continue insert_uninit_checks(fn) - do_copy_propagation(fn, CompilerOptions()) + self.do_optimizations(fn) actual.extend(format_func(fn)) assert_test_output(testcase, actual, "Invalid source code output", expected_output) + + def do_optimizations(self, fn: FuncIR) -> None: + raise NotImplementedError + + +class TestCopyPropagation(OptimizationSuite): + files = ["opt-copy-propagation.test"] + + def do_optimizations(self, fn: FuncIR) -> None: + do_copy_propagation(fn, CompilerOptions()) + + +class TestFlagElimination(OptimizationSuite): + files = ["opt-flag-elimination.test"] + + def do_optimizations(self, fn: FuncIR) -> None: + do_flag_elimination(fn, CompilerOptions()) diff --git a/mypyc/transform/flag_elimination.py b/mypyc/transform/flag_elimination.py new file mode 100644 index 000000000000..605e5bc46ae4 --- /dev/null +++ b/mypyc/transform/flag_elimination.py @@ -0,0 +1,108 @@ +"""Bool register elimination optimization. + +Example input: + + L1: + r0 = f() + b = r0 + goto L3 + L2: + r1 = g() + b = r1 + goto L3 + L3: + if b goto L4 else goto L5 + +The register b is redundant and we replace the assignments with two copies of +the branch in L3: + + L1: + r0 = f() + if r0 goto L4 else goto L5 + L2: + r1 = g() + if r1 goto L4 else goto L5 + +This helps generate simpler IR for tagged integers comparisons, for example. +""" + +from __future__ import annotations + +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import Assign, BasicBlock, Branch, Goto, Register, Unreachable +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.options import CompilerOptions +from mypyc.transform.ir_transform import IRTransform + + +def do_flag_elimination(fn: FuncIR, options: CompilerOptions) -> None: + # Find registers that are used exactly once as source, and in a branch. + counts: dict[Register, int] = {} + branches: dict[Register, Branch] = {} + labels: dict[Register, BasicBlock] = {} + for block in fn.blocks: + for i, op in enumerate(block.ops): + for src in op.sources(): + if isinstance(src, Register): + counts[src] = counts.get(src, 0) + 1 + if i == 0 and isinstance(op, Branch) and isinstance(op.value, Register): + branches[op.value] = op + labels[op.value] = block + + # Based on these we can find the candidate registers. + candidates: set[Register] = { + r for r in branches if counts.get(r, 0) == 1 and r not in fn.arg_regs + } + + # Remove candidates with invalid assignments. + for block in fn.blocks: + for i, op in enumerate(block.ops): + if isinstance(op, Assign) and op.dest in candidates: + next_op = block.ops[i + 1] + if not (isinstance(next_op, Goto) and next_op.label is labels[op.dest]): + # Not right + candidates.remove(op.dest) + + builder = LowLevelIRBuilder(None, options) + transform = FlagEliminationTransform( + builder, {x: y for x, y in branches.items() if x in candidates} + ) + transform.transform_blocks(fn.blocks) + fn.blocks = builder.blocks + + +class FlagEliminationTransform(IRTransform): + def __init__(self, builder: LowLevelIRBuilder, branch_map: dict[Register, Branch]) -> None: + super().__init__(builder) + self.branch_map = branch_map + self.branches = set(branch_map.values()) + + def visit_assign(self, op: Assign) -> None: + old_branch = self.branch_map.get(op.dest) + if old_branch: + # Replace assignment with a copy of the old branch, which is in a + # separate basic block. The old branch will be deletecd in visit_branch. + new_branch = Branch( + op.src, + old_branch.true, + old_branch.false, + old_branch.op, + old_branch.line, + rare=old_branch.rare, + ) + new_branch.negated = old_branch.negated + new_branch.traceback_entry = old_branch.traceback_entry + self.add(new_branch) + else: + self.add(op) + + def visit_goto(self, op: Goto) -> None: + # This is a no-op if basic block already terminated + self.builder.goto(op.label) + + def visit_branch(self, op: Branch) -> None: + if op in self.branches: + # This branch is optimized away + self.add(Unreachable()) + else: + self.add(op) diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py index 1bcfc8fb5feb..254fe3f7771d 100644 --- a/mypyc/transform/ir_transform.py +++ b/mypyc/transform/ir_transform.py @@ -101,17 +101,17 @@ def transform_blocks(self, blocks: list[BasicBlock]) -> None: def add(self, op: Op) -> Value: return self.builder.add(op) - def visit_goto(self, op: Goto) -> Value: - return self.add(op) + def visit_goto(self, op: Goto) -> None: + self.add(op) - def visit_branch(self, op: Branch) -> Value: - return self.add(op) + def visit_branch(self, op: Branch) -> None: + self.add(op) - def visit_return(self, op: Return) -> Value: - return self.add(op) + def visit_return(self, op: Return) -> None: + self.add(op) - def visit_unreachable(self, op: Unreachable) -> Value: - return self.add(op) + def visit_unreachable(self, op: Unreachable) -> None: + self.add(op) def visit_assign(self, op: Assign) -> Value | None: return self.add(op) From 1741c16b73bac748e17e6515f65e08afa7b250c3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 15 Mar 2024 15:05:34 +0000 Subject: [PATCH 0532/1617] Use lower-case generics more consistently in error messages (#17035) Suggest `list[x]` instead of `List[x]` on Python 3.9 and later in hints. We already suggest `x | None` instead of `Optional[x]` on 3.10+, so this makes the error messages more consistent. Use lower-case `type[x]` when using `reveal_type` on Python 3.9 and later. --- mypy/messages.py | 2 ++ mypy/types.py | 6 +++++- test-data/unit/check-lowercase.test | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index 92b57ef781a2..199b7c42b11b 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1779,6 +1779,8 @@ def need_annotation_for_var( alias = alias.split(".")[-1] if alias == "Dict": type_dec = f"{type_dec}, {type_dec}" + if self.options.use_lowercase_names(): + alias = alias.lower() recommended_type = f"{alias}[{type_dec}]" if recommended_type is not None: hint = f' (hint: "{node.name}: {recommended_type} = ...")' diff --git a/mypy/types.py b/mypy/types.py index d3c4df8b3b09..b4209e9debf4 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3405,7 +3405,11 @@ def visit_ellipsis_type(self, t: EllipsisType) -> str: return "..." def visit_type_type(self, t: TypeType) -> str: - return f"Type[{t.item.accept(self)}]" + if self.options.use_lowercase_names(): + type_name = "type" + else: + type_name = "Type" + return f"{type_name}[{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 d1ebbdd282fa..ab6d68929f8e 100644 --- a/test-data/unit/check-lowercase.test +++ b/test-data/unit/check-lowercase.test @@ -49,3 +49,17 @@ x: type[type] y: int y = x # E: Incompatible types in assignment (expression has type "type[type]", variable has type "int") + +[case testLowercaseSettingOnTypeAnnotationHint] +# flags: --python-version 3.9 --no-force-uppercase-builtins +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: --python-version 3.9 --no-force-uppercase-builtins +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])" +[builtins fixtures/primitives.pyi] From 31dc50371aaa267f258f74275b4bc07f27d70001 Mon Sep 17 00:00:00 2001 From: Hashem Date: Fri, 15 Mar 2024 11:29:48 -0400 Subject: [PATCH 0533/1617] attrs: Fix emulating hash method logic (#17016) This commit fixes a couple regressions in 1.9.0 from 91be285. Attrs' logic for hashability is slightly complex: * https://www.attrs.org/en/stable/hashing.html * https://github.com/python-attrs/attrs/blob/9e443b18527dc96b194e92805fa751cbf8434ba9/src/attr/_make.py#L1660-L1689 Mypy now properly emulates attrs' logic so that custom `__hash__` implementations are preserved, `@frozen` subclasses are always hashable, and classes are only made unhashable based on the values of `eq` and `unsafe_hash`. Fixes #17015 Fixes https://github.com/python/mypy/pull/16556#issuecomment-1987116488 Based on a patch in #17012 Co-Authored-By: Tin Tvrtkovic Co-authored-by: Hashem Nasarat --- mypy/plugins/attrs.py | 22 ++++- test-data/unit/check-incremental.test | 2 +- test-data/unit/check-plugin-attrs.test | 113 +++++++++++++++++++++-- test-data/unit/fixtures/plugin_attrs.pyi | 2 + 4 files changed, 125 insertions(+), 14 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 345ea822ed94..83f685f57a16 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -325,9 +325,6 @@ def attr_class_maker_callback( frozen = _get_frozen(ctx, frozen_default) order = _determine_eq_order(ctx) slots = _get_decorator_bool_argument(ctx, "slots", slots_default) - hashable = _get_decorator_bool_argument(ctx, "hash", False) or _get_decorator_bool_argument( - ctx, "unsafe_hash", False - ) auto_attribs = _get_decorator_optional_bool_argument(ctx, "auto_attribs", auto_attribs_default) kw_only = _get_decorator_bool_argument(ctx, "kw_only", False) @@ -371,7 +368,24 @@ def attr_class_maker_callback( _add_order(ctx, adder) if frozen: _make_frozen(ctx, attributes) - elif not hashable: + # Frozen classes are hashable by default, even if inheriting from non-frozen ones. + hashable: bool | None = _get_decorator_bool_argument( + ctx, "hash", True + ) and _get_decorator_bool_argument(ctx, "unsafe_hash", True) + else: + hashable = _get_decorator_optional_bool_argument(ctx, "unsafe_hash") + if hashable is None: # unspecified + hashable = _get_decorator_optional_bool_argument(ctx, "hash") + + eq = _get_decorator_optional_bool_argument(ctx, "eq") + has_own_hash = "__hash__" in ctx.cls.info.names + + if has_own_hash or (hashable is None and eq is False): + pass # Do nothing. + elif hashable: + # We copy the `__hash__` signature from `object` to make them hashable. + ctx.cls.info.names["__hash__"] = ctx.cls.info.mro[-1].names["__hash__"] + else: _remove_hashability(ctx) return True diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 42faa8c627ba..a7f4fafc579e 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3015,7 +3015,7 @@ class NoInit: class NoCmp: x: int -[builtins fixtures/list.pyi] +[builtins fixtures/plugin_attrs.pyi] [rechecked] [stale] [out1] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 0f379724553a..39b266dba50e 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -360,7 +360,8 @@ class A: a = A(5) a.a = 16 # E: Property "a" defined in "A" is read-only -[builtins fixtures/bool.pyi] +[builtins fixtures/plugin_attrs.pyi] + [case testAttrsNextGenFrozen] from attr import frozen, field @@ -370,7 +371,7 @@ class A: a = A(5) a.a = 16 # E: Property "a" defined in "A" is read-only -[builtins fixtures/bool.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testAttrsNextGenDetect] from attr import define, field @@ -420,7 +421,7 @@ reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.bool) - reveal_type(B) # N: Revealed type is "def (a: builtins.bool, b: builtins.int) -> __main__.B" reveal_type(C) # N: Revealed type is "def (a: builtins.int) -> __main__.C" -[builtins fixtures/bool.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testAttrsDataClass] import attr @@ -1155,7 +1156,7 @@ c = NonFrozenFrozen(1, 2) c.a = 17 # E: Property "a" defined in "NonFrozenFrozen" is read-only c.b = 17 # E: Property "b" defined in "NonFrozenFrozen" is read-only -[builtins fixtures/bool.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testAttrsCallableAttributes] from typing import Callable import attr @@ -1178,7 +1179,7 @@ class G: class FFrozen(F): def bar(self) -> bool: return self._cb(5, 6) -[builtins fixtures/callable.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testAttrsWithFactory] from typing import List @@ -1450,7 +1451,7 @@ class C: total = attr.ib(type=Bad) # E: Name "Bad" is not defined C(0).total = 1 # E: Property "total" defined in "C" is read-only -[builtins fixtures/bool.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testTypeInAttrDeferredStar] import lib @@ -1941,7 +1942,7 @@ class C: default=None, converter=default_if_none(factory=dict) \ # E: Unsupported converter, only named functions, types and lambdas are currently supported ) -[builtins fixtures/dict.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testAttrsUnannotatedConverter] import attr @@ -2012,7 +2013,7 @@ class Sub(Base): @property def name(self) -> str: ... -[builtins fixtures/property.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testOverrideWithPropertyInFrozenClassChecked] from attrs import frozen @@ -2035,7 +2036,7 @@ class Sub(Base): # This matches runtime semantics reveal_type(Sub) # N: Revealed type is "def (*, name: builtins.str, first_name: builtins.str, last_name: builtins.str) -> __main__.Sub" -[builtins fixtures/property.pyi] +[builtins fixtures/plugin_attrs.pyi] [case testFinalInstanceAttribute] from attrs import define @@ -2380,3 +2381,97 @@ class B(A): reveal_type(B.__hash__) # N: Revealed type is "None" [builtins fixtures/plugin_attrs.pyi] + +[case testManualOwnHashability] +from attrs import define, frozen + +@define +class A: + a: int + def __hash__(self) -> int: + ... + +reveal_type(A.__hash__) # N: Revealed type is "def (self: __main__.A) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testSubclassDefaultLosesHashability] +from attrs import define, frozen + +@define +class A: + a: int + def __hash__(self) -> int: + ... + +@define +class B(A): + pass + +reveal_type(B.__hash__) # N: Revealed type is "None" + +[builtins fixtures/plugin_attrs.pyi] + +[case testSubclassEqFalseKeepsHashability] +from attrs import define, frozen + +@define +class A: + a: int + def __hash__(self) -> int: + ... + +@define(eq=False) +class B(A): + pass + +reveal_type(B.__hash__) # N: Revealed type is "def (self: __main__.A) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testSubclassingFrozenHashability] +from attrs import define, frozen + +@define +class A: + a: int + +@frozen +class B(A): + pass + +reveal_type(B.__hash__) # N: Revealed type is "def (self: builtins.object) -> builtins.int" + +[builtins fixtures/plugin_attrs.pyi] + +[case testSubclassingFrozenHashOffHashability] +from attrs import define, frozen + +@define +class A: + a: int + def __hash__(self) -> int: + ... + +@frozen(unsafe_hash=False) +class B(A): + pass + +reveal_type(B.__hash__) # N: Revealed type is "None" + +[builtins fixtures/plugin_attrs.pyi] + +[case testUnsafeHashPrecedence] +from attrs import define, frozen + +@define(unsafe_hash=True, hash=False) +class A: + pass +reveal_type(A.__hash__) # N: Revealed type is "def (self: builtins.object) -> builtins.int" + +@define(unsafe_hash=False, hash=True) +class B: + pass +reveal_type(B.__hash__) # N: Revealed type is "None" + +[builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/fixtures/plugin_attrs.pyi b/test-data/unit/fixtures/plugin_attrs.pyi index 5b87c47b5bc8..7fd641727253 100644 --- a/test-data/unit/fixtures/plugin_attrs.pyi +++ b/test-data/unit/fixtures/plugin_attrs.pyi @@ -35,3 +35,5 @@ class tuple(Sequence[Tco], Generic[Tco]): def __iter__(self) -> Iterator[Tco]: pass def __contains__(self, item: object) -> bool: pass def __getitem__(self, x: int) -> Tco: pass + +property = object() # Dummy definition From c591c891f7c5c35c3546ae6b4709ee97ef9e1136 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 16 Mar 2024 12:54:54 +0000 Subject: [PATCH 0534/1617] [mypyc] Implement lowering pass and add primitives for int (in)equality (#17027) Add a new `PrimitiveOp` op which can be transformed into lower-level ops in a lowering pass after reference counting op insertion pass. Higher-level ops in IR make it easier to implement various optimizations, and the output of irbuild test cases will be more compact and readable. Implement the lowering pass. Currently it's pretty minimal, and I will add additional primitives and the direct transformation of various primitives to `CallC` ops in follow-up PRs. Currently primitives that map to C calls generate `CallC` ops in the main irbuild pass, but the long-term goal is to only/mostly generate `PrimitiveOp`s instead of `CallC` ops during the main irbuild pass. Also implement primitives for tagged integer equality and inequality as examples. Lowering of primitives is implemented using decorated handler functions in `mypyc.lower` that are found based on the name of the primitive. The name has no other significance, though it's also used in pretty-printed IR output. Work on mypyc/mypyc#854. The issue describes the motivation in more detail. --- mypyc/analysis/dataflow.py | 4 + mypyc/analysis/ircheck.py | 4 + mypyc/analysis/selfleaks.py | 4 + mypyc/codegen/emitfunc.py | 6 + mypyc/codegen/emitmodule.py | 10 +- mypyc/ir/ops.py | 79 ++++++++- mypyc/ir/pprint.py | 17 ++ mypyc/irbuild/ast_helpers.py | 7 +- mypyc/irbuild/expression.py | 4 +- mypyc/irbuild/ll_builder.py | 126 +++++++++++-- mypyc/lower/__init__.py | 0 mypyc/lower/int_ops.py | 15 ++ mypyc/lower/registry.py | 26 +++ mypyc/primitives/int_ops.py | 25 ++- mypyc/primitives/registry.py | 40 +++-- mypyc/test-data/analysis.test | 70 +++----- mypyc/test-data/irbuild-basic.test | 206 +++++++--------------- mypyc/test-data/irbuild-bool.test | 6 +- mypyc/test-data/irbuild-classes.test | 2 +- mypyc/test-data/irbuild-int.test | 28 +-- mypyc/test-data/irbuild-match.test | 45 +++-- mypyc/test-data/irbuild-nested.test | 4 +- mypyc/test-data/irbuild-optional.test | 2 +- mypyc/test-data/irbuild-tuple.test | 87 ++------- mypyc/test-data/lowering-int.test | 126 +++++++++++++ mypyc/test-data/opt-flag-elimination.test | 18 +- mypyc/test-data/refcount.test | 172 ++++++------------ mypyc/test/test_cheader.py | 16 +- mypyc/test/test_emitfunc.py | 2 + mypyc/test/test_lowering.py | 54 ++++++ mypyc/transform/ir_transform.py | 17 +- mypyc/transform/lower.py | 33 ++++ 32 files changed, 772 insertions(+), 483 deletions(-) create mode 100644 mypyc/lower/__init__.py create mode 100644 mypyc/lower/int_ops.py create mode 100644 mypyc/lower/registry.py create mode 100644 mypyc/test-data/lowering-int.test create mode 100644 mypyc/test/test_lowering.py create mode 100644 mypyc/transform/lower.py diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 57ad2b17fcc5..9babf860fb31 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -38,6 +38,7 @@ MethodCall, Op, OpVisitor, + PrimitiveOp, RaiseStandardError, RegisterOp, Return, @@ -234,6 +235,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill[T]: def visit_call_c(self, op: CallC) -> GenAndKill[T]: return self.visit_register_op(op) + def visit_primitive_op(self, op: PrimitiveOp) -> GenAndKill[T]: + return self.visit_register_op(op) + def visit_truncate(self, op: Truncate) -> GenAndKill[T]: return self.visit_register_op(op) diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index 127047e02ff5..88737ac208de 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -37,6 +37,7 @@ MethodCall, Op, OpVisitor, + PrimitiveOp, RaiseStandardError, Register, Return, @@ -381,6 +382,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None: def visit_call_c(self, op: CallC) -> None: pass + def visit_primitive_op(self, op: PrimitiveOp) -> None: + pass + def visit_truncate(self, op: Truncate) -> None: pass diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py index 80c2bc348bc2..5d89a9bfc7c6 100644 --- a/mypyc/analysis/selfleaks.py +++ b/mypyc/analysis/selfleaks.py @@ -31,6 +31,7 @@ LoadStatic, MethodCall, OpVisitor, + PrimitiveOp, RaiseStandardError, Register, RegisterOp, @@ -149,6 +150,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill: def visit_call_c(self, op: CallC) -> GenAndKill: return self.check_register_op(op) + def visit_primitive_op(self, op: PrimitiveOp) -> GenAndKill: + return self.check_register_op(op) + def visit_truncate(self, op: Truncate) -> GenAndKill: return CLEAN diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index c08f1f840fa4..12f57b9cee6f 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -47,6 +47,7 @@ MethodCall, Op, OpVisitor, + PrimitiveOp, RaiseStandardError, Register, Return, @@ -629,6 +630,11 @@ def visit_call_c(self, op: CallC) -> None: args = ", ".join(self.reg(arg) for arg in op.args) self.emitter.emit_line(f"{dest}{op.function_name}({args});") + def visit_primitive_op(self, op: PrimitiveOp) -> None: + raise RuntimeError( + f"unexpected PrimitiveOp {op.desc.name}: they must be lowered before codegen" + ) + def visit_truncate(self, op: Truncate) -> None: dest = self.reg(op) value = self.reg(op.src) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 9466bc2cea79..6c8f5ac91335 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -59,6 +59,7 @@ from mypyc.transform.copy_propagation import do_copy_propagation from mypyc.transform.exceptions import insert_exception_handling from mypyc.transform.flag_elimination import do_flag_elimination +from mypyc.transform.lower import lower_ir from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.transform.uninit import insert_uninit_checks @@ -235,6 +236,8 @@ def compile_scc_to_ir( insert_exception_handling(fn) # Insert refcount handling. insert_ref_count_opcodes(fn) + # Switch to lower abstraction level IR. + lower_ir(fn, compiler_options) # Perform optimizations. do_copy_propagation(fn, compiler_options) do_flag_elimination(fn, compiler_options) @@ -423,10 +426,11 @@ def compile_modules_to_c( ) modules = compile_modules_to_ir(result, mapper, compiler_options, errors) - ctext = compile_ir_to_c(groups, modules, result, mapper, compiler_options) + if errors.num_errors > 0: + return {}, [] - if errors.num_errors == 0: - write_cache(modules, result, group_map, ctext) + ctext = compile_ir_to_c(groups, modules, result, mapper, compiler_options) + write_cache(modules, result, group_map, ctext) return modules, [ctext[name] for _, name in groups] diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 04c50d1e2841..3acfb0933e5a 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -576,6 +576,78 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_method_call(self) +class PrimitiveDescription: + """Description of a primitive op. + + Primitives get lowered into lower-level ops before code generation. + + If c_function_name is provided, a primitive will be lowered into a CallC op. + Otherwise custom logic will need to be implemented to transform the + primitive into lower-level ops. + """ + + def __init__( + self, + name: str, + arg_types: list[RType], + return_type: RType, # TODO: What about generic? + var_arg_type: RType | None, + truncated_type: RType | None, + c_function_name: str | None, + error_kind: int, + steals: StealsDescription, + is_borrowed: bool, + ordering: list[int] | None, + extra_int_constants: list[tuple[int, RType]], + priority: int, + ) -> None: + # Each primitive much have a distinct name, but otherwise they are arbitrary. + self.name: Final = name + self.arg_types: Final = arg_types + self.return_type: Final = return_type + self.var_arg_type: Final = var_arg_type + self.truncated_type: Final = truncated_type + # If non-None, this will map to a call of a C helper function; if None, + # there must be a custom handler function that gets invoked during the lowering + # pass to generate low-level IR for the primitive (in the mypyc.lower package) + self.c_function_name: Final = c_function_name + self.error_kind: Final = error_kind + self.steals: Final = steals + self.is_borrowed: Final = is_borrowed + self.ordering: Final = ordering + self.extra_int_constants: Final = extra_int_constants + self.priority: Final = priority + + def __repr__(self) -> str: + return f"" + + +class PrimitiveOp(RegisterOp): + """A higher-level primitive operation. + + Some of these have special compiler support. These will be lowered + (transformed) into lower-level IR ops before code generation, and after + reference counting op insertion. Others will be transformed into CallC + ops. + + Tagged integer equality is a typical primitive op with non-trivial + lowering. It gets transformed into a tag check, followed by different + code paths for short and long representations. + """ + + def __init__(self, args: list[Value], desc: PrimitiveDescription, line: int = -1) -> None: + self.args = args + self.type = desc.return_type + self.error_kind = desc.error_kind + self.desc = desc + + def sources(self) -> list[Value]: + return self.args + + def accept(self, visitor: OpVisitor[T]) -> T: + return visitor.visit_primitive_op(self) + + class LoadErrorValue(RegisterOp): """Load an error value. @@ -1446,7 +1518,8 @@ class Unborrow(RegisterOp): error_kind = ERR_NEVER - def __init__(self, src: Value) -> None: + def __init__(self, src: Value, line: int = -1) -> None: + super().__init__(line) assert src.is_borrowed self.src = src self.type = src.type @@ -1555,6 +1628,10 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> T: def visit_call_c(self, op: CallC) -> T: raise NotImplementedError + @abstractmethod + def visit_primitive_op(self, op: PrimitiveOp) -> T: + raise NotImplementedError + @abstractmethod def visit_truncate(self, op: Truncate) -> T: raise NotImplementedError diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 5578049256f1..8d6723917ea0 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -43,6 +43,7 @@ MethodCall, Op, OpVisitor, + PrimitiveOp, RaiseStandardError, Register, Return, @@ -217,6 +218,22 @@ def visit_call_c(self, op: CallC) -> str: else: return self.format("%r = %s(%s)", op, op.function_name, args_str) + def visit_primitive_op(self, op: PrimitiveOp) -> str: + args = [] + arg_index = 0 + type_arg_index = 0 + for arg_type in zip(op.desc.arg_types): + if arg_type: + args.append(self.format("%r", op.args[arg_index])) + arg_index += 1 + else: + assert op.type_args + args.append(self.format("%r", op.type_args[type_arg_index])) + type_arg_index += 1 + + args_str = ", ".join(args) + return self.format("%r = %s %s ", op, op.desc.name, args_str) + def visit_truncate(self, op: Truncate) -> str: return self.format("%r = truncate %r: %t to %t", op, op.src, op.src_type, op.type) diff --git a/mypyc/irbuild/ast_helpers.py b/mypyc/irbuild/ast_helpers.py index 1af1ad611a89..8490eaa03477 100644 --- a/mypyc/irbuild/ast_helpers.py +++ b/mypyc/irbuild/ast_helpers.py @@ -93,7 +93,12 @@ def maybe_process_conditional_comparison( self.add_bool_branch(reg, true, false) else: # "left op right" for two tagged integers - self.builder.compare_tagged_condition(left, right, op, true, false, e.line) + if op in ("==", "!="): + reg = self.builder.binary_op(left, right, op, e.line) + self.flush_keep_alives() + self.add_bool_branch(reg, true, false) + else: + self.builder.compare_tagged_condition(left, right, op, true, false, e.line) return True diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 81e37953809f..021b7a1dbe90 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -756,7 +756,7 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value: set_literal = precompute_set_literal(builder, e.operands[1]) if set_literal is not None: lhs = e.operands[0] - result = builder.builder.call_c( + result = builder.builder.primitive_op( set_in_op, [builder.accept(lhs), set_literal], e.line, bool_rprimitive ) if first_op == "not in": @@ -778,7 +778,7 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value: borrow_left = is_borrow_friendly_expr(builder, right_expr) left = builder.accept(left_expr, can_borrow=borrow_left) right = builder.accept(right_expr, can_borrow=True) - return builder.compare_tagged(left, right, first_op, e.line) + return builder.binary_op(left, right, first_op, e.line) # TODO: Don't produce an expression when used in conditional context # All of the trickiness here is due to support for chained conditionals diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 45c06e11befd..f9bacb43bc3e 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -63,6 +63,8 @@ LoadStatic, MethodCall, Op, + PrimitiveDescription, + PrimitiveOp, RaiseStandardError, Register, SetMem, @@ -1313,7 +1315,12 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.compare_strings(lreg, rreg, op, line) if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype) and op in ("==", "!="): return self.compare_bytes(lreg, rreg, op, line) - if is_tagged(ltype) and is_tagged(rtype) and op in int_comparison_op_mapping: + if ( + is_tagged(ltype) + and is_tagged(rtype) + and op in int_comparison_op_mapping + and op not in ("==", "!=") + ): return self.compare_tagged(lreg, rreg, op, line) if is_bool_rprimitive(ltype) and is_bool_rprimitive(rtype) and op in BOOL_BINARY_OPS: if op in ComparisonOp.signed_ops: @@ -1379,13 +1386,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: # Mixed int comparisons if op in ("==", "!="): - op_id = ComparisonOp.signed_ops[op] - if is_tagged(ltype) and is_subtype(rtype, ltype): - rreg = self.coerce(rreg, int_rprimitive, line) - return self.comparison_op(lreg, rreg, op_id, line) - if is_tagged(rtype) and is_subtype(ltype, rtype): - lreg = self.coerce(lreg, int_rprimitive, line) - return self.comparison_op(lreg, rreg, op_id, line) + pass # TODO: Do we need anything here? elif op in op in int_comparison_op_mapping: if is_tagged(ltype) and is_subtype(rtype, ltype): rreg = self.coerce(rreg, short_int_rprimitive, line) @@ -1412,8 +1413,8 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if base_op in float_op_to_id: return self.float_op(lreg, rreg, base_op, line) - call_c_ops_candidates = binary_ops.get(op, []) - target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) + primitive_ops_candidates = binary_ops.get(op, []) + target = self.matching_primitive_op(primitive_ops_candidates, [lreg, rreg], line) assert target, "Unsupported binary operation: %s" % op return target @@ -1432,7 +1433,14 @@ def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) - def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given operator (value context).""" # generate fast binary logic ops on short ints - if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type): + if (is_short_int_rprimitive(lhs.type) or is_short_int_rprimitive(rhs.type)) and op in ( + "==", + "!=", + ): + quick = True + else: + quick = is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type) + if quick: return self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] result = Register(bool_rprimitive) @@ -1986,6 +1994,102 @@ def matching_call_c( return target return None + def primitive_op( + self, + desc: PrimitiveDescription, + args: list[Value], + line: int, + result_type: RType | None = None, + ) -> Value: + """Add a primitive op.""" + # Does this primitive map into calling a Python C API + # or an internal mypyc C API function? + if desc.c_function_name: + # TODO: Generate PrimitiOps here and transform them into CallC + # ops only later in the lowering pass + c_desc = CFunctionDescription( + desc.name, + desc.arg_types, + desc.return_type, + desc.var_arg_type, + desc.truncated_type, + desc.c_function_name, + desc.error_kind, + desc.steals, + desc.is_borrowed, + desc.ordering, + desc.extra_int_constants, + desc.priority, + ) + return self.call_c(c_desc, args, line, result_type) + + # This primitve gets transformed in a lowering pass to + # lower-level IR ops using a custom transform function. + + coerced = [] + # Coerce fixed number arguments + for i in range(min(len(args), len(desc.arg_types))): + formal_type = desc.arg_types[i] + arg = args[i] + assert formal_type is not None # TODO + arg = self.coerce(arg, formal_type, line) + coerced.append(arg) + assert desc.ordering is None + assert desc.var_arg_type is None + assert not desc.extra_int_constants + target = self.add(PrimitiveOp(coerced, desc, line=line)) + if desc.is_borrowed: + # If the result is borrowed, force the arguments to be + # kept alive afterwards, as otherwise the result might be + # immediately freed, at the risk of a dangling pointer. + for arg in coerced: + if not isinstance(arg, (Integer, LoadLiteral)): + self.keep_alives.append(arg) + if desc.error_kind == ERR_NEG_INT: + comp = ComparisonOp(target, Integer(0, desc.return_type, line), ComparisonOp.SGE, line) + comp.error_kind = ERR_FALSE + self.add(comp) + + assert desc.truncated_type is None + result = target + if result_type and not is_runtime_subtype(result.type, result_type): + if is_none_rprimitive(result_type): + # Special case None return. The actual result may actually be a bool + # and so we can't just coerce it. + result = self.none() + else: + result = self.coerce(result, result_type, line, can_borrow=desc.is_borrowed) + return result + + def matching_primitive_op( + self, + candidates: list[PrimitiveDescription], + args: list[Value], + line: int, + result_type: RType | None = None, + can_borrow: bool = False, + ) -> Value | None: + matching: PrimitiveDescription | None = None + for desc in candidates: + if len(desc.arg_types) != len(args): + continue + if all( + # formal is not None and # TODO + is_subtype(actual.type, formal) + for actual, formal in zip(args, desc.arg_types) + ) and (not desc.is_borrowed or can_borrow): + if matching: + assert matching.priority != desc.priority, "Ambiguous:\n1) {}\n2) {}".format( + matching, desc + ) + if desc.priority > matching.priority: + matching = desc + else: + matching = desc + if matching: + return self.primitive_op(matching, args, line=line) + return None + def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> Value: """Generate a native integer binary op. diff --git a/mypyc/lower/__init__.py b/mypyc/lower/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/mypyc/lower/int_ops.py b/mypyc/lower/int_ops.py new file mode 100644 index 000000000000..40fba7af4f4d --- /dev/null +++ b/mypyc/lower/int_ops.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from mypyc.ir.ops import Value +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.lower.registry import lower_binary_op + + +@lower_binary_op("int_eq") +def lower_int_eq(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], "==", line) + + +@lower_binary_op("int_ne") +def lower_int_ne(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], "!=", line) diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py new file mode 100644 index 000000000000..cc53eb93f4dd --- /dev/null +++ b/mypyc/lower/registry.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import Callable, Final, List + +from mypyc.ir.ops import Value +from mypyc.irbuild.ll_builder import LowLevelIRBuilder + +LowerFunc = Callable[[LowLevelIRBuilder, List[Value], int], Value] + + +lowering_registry: Final[dict[str, LowerFunc]] = {} + + +def lower_binary_op(name: str) -> Callable[[LowerFunc], LowerFunc]: + """Register a handler that generates low-level IR for a primitive binary op.""" + + def wrapper(f: LowerFunc) -> LowerFunc: + assert name not in lowering_registry + lowering_registry[name] = f + return f + + return wrapper + + +# Import various modules that set up global state. +import mypyc.lower.int_ops # noqa: F401 diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 95f9cc5ff43f..4103fe349a74 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -12,7 +12,14 @@ from typing import NamedTuple -from mypyc.ir.ops import ERR_ALWAYS, ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER, ComparisonOp +from mypyc.ir.ops import ( + ERR_ALWAYS, + ERR_MAGIC, + ERR_MAGIC_OVERLAPPING, + ERR_NEVER, + ComparisonOp, + PrimitiveDescription, +) from mypyc.ir.rtypes import ( RType, bit_rprimitive, @@ -101,6 +108,22 @@ ) +def int_binary_primitive( + op: str, primitive_name: str, return_type: RType = int_rprimitive, error_kind: int = ERR_NEVER +) -> PrimitiveDescription: + return binary_op( + name=op, + arg_types=[int_rprimitive, int_rprimitive], + return_type=return_type, + primitive_name=primitive_name, + error_kind=error_kind, + ) + + +int_eq = int_binary_primitive(op="==", primitive_name="int_eq", return_type=bit_rprimitive) +int_ne = int_binary_primitive(op="!=", primitive_name="int_ne", return_type=bit_rprimitive) + + def int_binary_op( name: str, c_function_name: str, diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 11fca7dc2c70..d4768b4df532 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -39,7 +39,7 @@ from typing import Final, NamedTuple -from mypyc.ir.ops import StealsDescription +from mypyc.ir.ops import PrimitiveDescription, StealsDescription from mypyc.ir.rtypes import RType # Error kind for functions that return negative integer on exception. This @@ -76,7 +76,7 @@ class LoadAddressDescription(NamedTuple): function_ops: dict[str, list[CFunctionDescription]] = {} # CallC op for binary ops -binary_ops: dict[str, list[CFunctionDescription]] = {} +binary_ops: dict[str, list[PrimitiveDescription]] = {} # CallC op for unary ops unary_ops: dict[str, list[CFunctionDescription]] = {} @@ -192,8 +192,9 @@ def binary_op( name: str, arg_types: list[RType], return_type: RType, - c_function_name: str, error_kind: int, + c_function_name: str | None = None, + primitive_name: str | None = None, var_arg_type: RType | None = None, truncated_type: RType | None = None, ordering: list[int] | None = None, @@ -201,7 +202,7 @@ def binary_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, -) -> CFunctionDescription: +) -> PrimitiveDescription: """Define a c function call op for a binary operation. This will be automatically generated by matching against the AST. @@ -209,22 +210,24 @@ def binary_op( Most arguments are similar to method_op(), but exactly two argument types are expected. """ + assert c_function_name is not None or primitive_name is not None + assert not (c_function_name is not None and primitive_name is not None) if extra_int_constants is None: extra_int_constants = [] ops = binary_ops.setdefault(name, []) - desc = CFunctionDescription( - name, - arg_types, - return_type, - var_arg_type, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + desc = PrimitiveDescription( + name=primitive_name or name, + arg_types=arg_types, + return_type=return_type, + var_arg_type=var_arg_type, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, ) ops.append(desc) return desc @@ -311,11 +314,10 @@ def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription: return LoadAddressDescription(name, type, src) +# Import various modules that set up global state. import mypyc.primitives.bytes_ops import mypyc.primitives.dict_ops import mypyc.primitives.float_ops - -# Import various modules that set up global state. import mypyc.primitives.int_ops import mypyc.primitives.list_ops import mypyc.primitives.misc_ops diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index efd219cc222a..8e067aed4d79 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -10,40 +10,27 @@ def f(a: int) -> None: [out] def f(a): a, x :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit y, z :: int L0: x = 2 - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq x, a + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsEq_(x, a) - if r2 goto L3 else goto L4 :: bool -L2: - r3 = x == a - if r3 goto L3 else goto L4 :: bool -L3: y = 2 - goto L5 -L4: + goto L3 +L2: z = 2 -L5: +L3: return 1 (0, 0) {a} {a, x} (0, 1) {a, x} {a, x} (0, 2) {a, x} {a, x} -(0, 3) {a, x} {a, x} -(1, 0) {a, x} {a, x} -(1, 1) {a, x} {a, x} -(2, 0) {a, x} {a, x} -(2, 1) {a, x} {a, x} -(3, 0) {a, x} {a, x, y} -(3, 1) {a, x, y} {a, x, y} -(4, 0) {a, x} {a, x, z} -(4, 1) {a, x, z} {a, x, z} -(5, 0) {a, x, y, z} {a, x, y, z} +(1, 0) {a, x} {a, x, y} +(1, 1) {a, x, y} {a, x, y} +(2, 0) {a, x} {a, x, z} +(2, 1) {a, x, z} {a, x, z} +(3, 0) {a, x, y, z} {a, x, y, z} [case testSimple_Liveness] def f(a: int) -> int: @@ -58,7 +45,7 @@ def f(a): r0 :: bit L0: x = 2 - r0 = x == 2 + r0 = int_eq x, 2 if r0 goto L1 else goto L2 :: bool L1: return a @@ -124,7 +111,7 @@ def f(a): r0 :: bit y, x :: int L0: - r0 = a == 2 + r0 = int_eq a, 2 if r0 goto L1 else goto L2 :: bool L1: y = 2 @@ -421,40 +408,27 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit x :: int L0: - r0 = a & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq a, a + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsEq_(a, a) - if r2 goto L3 else goto L4 :: bool -L2: - r3 = a == a - if r3 goto L3 else goto L4 :: bool -L3: x = 4 a = 2 - goto L5 -L4: + goto L3 +L2: x = 2 -L5: +L3: return x (0, 0) {a} {a} (0, 1) {a} {a} -(0, 2) {a} {a} (1, 0) {a} {a} -(1, 1) {a} {a} +(1, 1) {a} {} +(1, 2) {} {} (2, 0) {a} {a} (2, 1) {a} {a} -(3, 0) {a} {a} -(3, 1) {a} {} -(3, 2) {} {} -(4, 0) {a} {a} -(4, 1) {a} {a} -(5, 0) {} {} +(3, 0) {} {} [case testLoop_BorrowedArgument] def f(a: int) -> int: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index cd952ef2ebfd..981460dae371 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -568,7 +568,7 @@ L3: x = 2 goto L8 L4: - r4 = n == 0 + r4 = int_eq n, 0 if r4 goto L5 else goto L6 :: bool L5: x = 2 @@ -598,7 +598,7 @@ def f(n): r0 :: bit r1 :: int L0: - r0 = n == 0 + r0 = int_eq n, 0 if r0 goto L1 else goto L2 :: bool L1: r1 = 0 @@ -1462,7 +1462,7 @@ L0: r1 = load_mem r0 :: native_int* keep_alive x r2 = r1 << 1 - r3 = r2 != 0 + r3 = int_ne r2, 0 if r3 goto L1 else goto L2 :: bool L1: return 2 @@ -2052,19 +2052,12 @@ def f(): r13 :: bit r14 :: object r15, x :: int - r16 :: native_int - r17, r18 :: bit - r19 :: bool - r20, r21 :: bit - r22 :: native_int - r23, r24 :: bit - r25 :: bool - r26, r27 :: bit - r28 :: int - r29 :: object - r30 :: i32 - r31 :: bit - r32 :: short_int + r16, r17 :: bit + r18 :: int + r19 :: object + r20 :: i32 + r21 :: bit + r22 :: short_int L0: r0 = PyList_New(0) r1 = PyList_New(3) @@ -2086,52 +2079,30 @@ L1: keep_alive r1 r12 = r11 << 1 r13 = r9 < r12 :: signed - if r13 goto L2 else goto L14 :: bool + if r13 goto L2 else goto L8 :: bool L2: r14 = CPyList_GetItemUnsafe(r1, r9) r15 = unbox(int, r14) x = r15 - r16 = x & 1 - r17 = r16 == 0 - if r17 goto L3 else goto L4 :: bool + r16 = int_ne x, 4 + if r16 goto L4 else goto L3 :: bool L3: - r18 = x != 4 - r19 = r18 - goto L5 + goto L7 L4: - r20 = CPyTagged_IsEq_(x, 4) - r21 = r20 ^ 1 - r19 = r21 + r17 = int_ne x, 6 + if r17 goto L6 else goto L5 :: bool L5: - if r19 goto L7 else goto L6 :: bool + goto L7 L6: - goto L13 + r18 = CPyTagged_Multiply(x, x) + r19 = box(int, r18) + r20 = PyList_Append(r0, r19) + r21 = r20 >= 0 :: signed L7: - r22 = x & 1 - r23 = r22 == 0 - if r23 goto L8 else goto L9 :: bool -L8: - r24 = x != 6 - r25 = r24 - goto L10 -L9: - r26 = CPyTagged_IsEq_(x, 6) - r27 = r26 ^ 1 - r25 = r27 -L10: - if r25 goto L12 else goto L11 :: bool -L11: - goto L13 -L12: - r28 = CPyTagged_Multiply(x, x) - r29 = box(int, r28) - r30 = PyList_Append(r0, r29) - r31 = r30 >= 0 :: signed -L13: - r32 = r9 + 2 - r9 = r32 + r22 = r9 + 2 + r9 = r22 goto L1 -L14: +L8: return r0 [case testDictComprehension] @@ -2151,19 +2122,12 @@ def f(): r13 :: bit r14 :: object r15, x :: int - r16 :: native_int - r17, r18 :: bit - r19 :: bool - r20, r21 :: bit - r22 :: native_int - r23, r24 :: bit - r25 :: bool - r26, r27 :: bit - r28 :: int - r29, r30 :: object - r31 :: i32 - r32 :: bit - r33 :: short_int + r16, r17 :: bit + r18 :: int + r19, r20 :: object + r21 :: i32 + r22 :: bit + r23 :: short_int L0: r0 = PyDict_New() r1 = PyList_New(3) @@ -2185,53 +2149,31 @@ L1: keep_alive r1 r12 = r11 << 1 r13 = r9 < r12 :: signed - if r13 goto L2 else goto L14 :: bool + if r13 goto L2 else goto L8 :: bool L2: r14 = CPyList_GetItemUnsafe(r1, r9) r15 = unbox(int, r14) x = r15 - r16 = x & 1 - r17 = r16 == 0 - if r17 goto L3 else goto L4 :: bool + r16 = int_ne x, 4 + if r16 goto L4 else goto L3 :: bool L3: - r18 = x != 4 - r19 = r18 - goto L5 + goto L7 L4: - r20 = CPyTagged_IsEq_(x, 4) - r21 = r20 ^ 1 - r19 = r21 + r17 = int_ne x, 6 + if r17 goto L6 else goto L5 :: bool L5: - if r19 goto L7 else goto L6 :: bool + goto L7 L6: - goto L13 + r18 = CPyTagged_Multiply(x, x) + r19 = box(int, x) + r20 = box(int, r18) + r21 = CPyDict_SetItem(r0, r19, r20) + r22 = r21 >= 0 :: signed L7: - r22 = x & 1 - r23 = r22 == 0 - if r23 goto L8 else goto L9 :: bool -L8: - r24 = x != 6 - r25 = r24 - goto L10 -L9: - r26 = CPyTagged_IsEq_(x, 6) - r27 = r26 ^ 1 - r25 = r27 -L10: - if r25 goto L12 else goto L11 :: bool -L11: - goto L13 -L12: - r28 = CPyTagged_Multiply(x, x) - r29 = box(int, x) - r30 = box(int, r28) - r31 = CPyDict_SetItem(r0, r29, r30) - r32 = r31 >= 0 :: signed -L13: - r33 = r9 + 2 - r9 = r33 + r23 = r9 + 2 + r9 = r23 goto L1 -L14: +L8: return r0 [case testLoopsMultipleAssign] @@ -3011,85 +2953,57 @@ def call_any(l): r0 :: bool r1, r2 :: object r3, i :: int - r4 :: native_int - r5, r6 :: bit - r7 :: bool - r8, r9 :: bit + r4, r5 :: bit L0: r0 = 0 r1 = PyObject_GetIter(l) L1: r2 = PyIter_Next(r1) - if is_error(r2) goto L9 else goto L2 + if is_error(r2) goto L6 else goto L2 L2: r3 = unbox(int, r2) i = r3 - r4 = i & 1 - r5 = r4 == 0 - if r5 goto L3 else goto L4 :: bool + r4 = int_eq i, 0 + if r4 goto L3 else goto L4 :: bool L3: - r6 = i == 0 - r7 = r6 - goto L5 + r0 = 1 + goto L8 L4: - r8 = CPyTagged_IsEq_(i, 0) - r7 = r8 L5: - if r7 goto L6 else goto L7 :: bool + goto L1 L6: - r0 = 1 - goto L11 + r5 = CPy_NoErrOccured() L7: L8: - goto L1 -L9: - r9 = CPy_NoErrOccured() -L10: -L11: return r0 def call_all(l): l :: object r0 :: bool r1, r2 :: object r3, i :: int - r4 :: native_int - r5, r6 :: bit - r7 :: bool - r8 :: bit - r9 :: bool - r10 :: bit + r4, r5, r6 :: bit L0: r0 = 1 r1 = PyObject_GetIter(l) L1: r2 = PyIter_Next(r1) - if is_error(r2) goto L9 else goto L2 + if is_error(r2) goto L6 else goto L2 L2: r3 = unbox(int, r2) i = r3 - r4 = i & 1 - r5 = r4 == 0 + r4 = int_eq i, 0 + r5 = r4 ^ 1 if r5 goto L3 else goto L4 :: bool L3: - r6 = i == 0 - r7 = r6 - goto L5 + r0 = 0 + goto L8 L4: - r8 = CPyTagged_IsEq_(i, 0) - r7 = r8 L5: - r9 = r7 ^ 1 - if r9 goto L6 else goto L7 :: bool + goto L1 L6: - r0 = 0 - goto L11 + r6 = CPy_NoErrOccured() L7: L8: - goto L1 -L9: - r10 = CPy_NoErrOccured() -L10: -L11: return r0 [case testSum] diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index 731d393d69ab..f0b0b480bc0d 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -96,7 +96,7 @@ L0: r1 = load_mem r0 :: native_int* keep_alive l r2 = r1 << 1 - r3 = r2 != 0 + r3 = int_ne r2, 0 return r3 def always_truthy_instance_to_bool(o): o :: __main__.C @@ -222,7 +222,7 @@ def eq1(x, y): L0: r0 = y << 1 r1 = extend r0: builtins.bool to builtins.int - r2 = x == r1 + r2 = int_eq x, r1 return r2 def eq2(x, y): x :: bool @@ -233,7 +233,7 @@ def eq2(x, y): L0: r0 = x << 1 r1 = extend r0: builtins.bool to builtins.int - r2 = r1 == y + r2 = int_eq r1, y return r2 def neq1(x, y): x :: i64 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 55e55dbf3286..8c4743c6a47f 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1249,7 +1249,7 @@ L0: r0 = x.__getitem__(2) r1 = CPyList_GetItemShortBorrow(r0, 0) r2 = unbox(int, r1) - r3 = r2 == 4 + r3 = int_eq r2, 4 keep_alive r0 if r3 goto L1 else goto L2 :: bool L1: diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index fbe00aff4040..1489f2f470dd 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -4,24 +4,10 @@ def f(x: int, y: int) -> bool: [out] def f(x, y): x, y :: int - r0 :: native_int - r1, r2 :: bit - r3 :: bool - r4, r5 :: bit + r0 :: bit L0: - r0 = x & 1 - r1 = r0 == 0 - if r1 goto L1 else goto L2 :: bool -L1: - r2 = x != y - r3 = r2 - goto L3 -L2: - r4 = CPyTagged_IsEq_(x, y) - r5 = r4 ^ 1 - r3 = r5 -L3: - return r3 + r0 = int_ne x, y + return r0 [case testShortIntComparisons] def f(x: int) -> int: @@ -43,22 +29,22 @@ def f(x): r4 :: native_int r5, r6, r7 :: bit L0: - r0 = x == 6 + r0 = int_eq x, 6 if r0 goto L1 else goto L2 :: bool L1: return 2 L2: - r1 = x != 8 + r1 = int_ne x, 8 if r1 goto L3 else goto L4 :: bool L3: return 4 L4: - r2 = 10 == x + r2 = int_eq 10, x if r2 goto L5 else goto L6 :: bool L5: return 6 L6: - r3 = 12 != x + r3 = int_ne 12, x if r3 goto L7 else goto L8 :: bool L7: return 8 diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index a078ae0defdb..ab5a19624ba6 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -14,7 +14,7 @@ def f(): r6 :: object_ptr r7, r8 :: object L0: - r0 = 246 == 246 + r0 = int_eq 246, 246 if r0 goto L1 else goto L2 :: bool L1: r1 = 'matched' @@ -30,6 +30,7 @@ L2: L3: r8 = box(None, 1) return r8 + [case testMatchOrPattern_python3_10] def f(): match 123: @@ -46,10 +47,10 @@ def f(): r7 :: object_ptr r8, r9 :: object L0: - r0 = 246 == 246 + r0 = int_eq 246, 246 if r0 goto L3 else goto L1 :: bool L1: - r1 = 246 == 912 + r1 = int_eq 246, 912 if r1 goto L3 else goto L2 :: bool L2: goto L4 @@ -67,6 +68,7 @@ L4: L5: r9 = box(None, 1) return r9 + [case testMatchOrPatternManyPatterns_python3_10] def f(): match 1: @@ -83,16 +85,16 @@ def f(): r9 :: object_ptr r10, r11 :: object L0: - r0 = 2 == 2 + r0 = int_eq 2, 2 if r0 goto L5 else goto L1 :: bool L1: - r1 = 2 == 4 + r1 = int_eq 2, 4 if r1 goto L5 else goto L2 :: bool L2: - r2 = 2 == 6 + r2 = int_eq 2, 6 if r2 goto L5 else goto L3 :: bool L3: - r3 = 2 == 8 + r3 = int_eq 2, 8 if r3 goto L5 else goto L4 :: bool L4: goto L6 @@ -110,6 +112,7 @@ L6: L7: r11 = box(None, 1) return r11 + [case testMatchClassPattern_python3_10] def f(): match 123: @@ -200,7 +203,7 @@ def f(): r14 :: object_ptr r15, r16 :: object L0: - r0 = 246 == 246 + r0 = int_eq 246, 246 if r0 goto L1 else goto L2 :: bool L1: r1 = 'matched' @@ -213,7 +216,7 @@ L1: keep_alive r1 goto L5 L2: - r8 = 246 == 912 + r8 = int_eq 246, 912 if r8 goto L3 else goto L4 :: bool L3: r9 = 'no match' @@ -229,6 +232,7 @@ L4: L5: r16 = box(None, 1) return r16 + [case testMatchMultiBodyAndComplexOr_python3_10] def f(): match 123: @@ -265,7 +269,7 @@ def f(): r23 :: object_ptr r24, r25 :: object L0: - r0 = 246 == 2 + r0 = int_eq 246, 2 if r0 goto L1 else goto L2 :: bool L1: r1 = 'here 1' @@ -278,10 +282,10 @@ L1: keep_alive r1 goto L9 L2: - r8 = 246 == 4 + r8 = int_eq 246, 4 if r8 goto L5 else goto L3 :: bool L3: - r9 = 246 == 6 + r9 = int_eq 246, 6 if r9 goto L5 else goto L4 :: bool L4: goto L6 @@ -296,7 +300,7 @@ L5: keep_alive r10 goto L9 L6: - r17 = 246 == 246 + r17 = int_eq 246, 246 if r17 goto L7 else goto L8 :: bool L7: r18 = 'here 123' @@ -312,6 +316,7 @@ L8: L9: r25 = box(None, 1) return r25 + [case testMatchWithGuard_python3_10] def f(): match 123: @@ -328,7 +333,7 @@ def f(): r6 :: object_ptr r7, r8 :: object L0: - r0 = 246 == 246 + r0 = int_eq 246, 246 if r0 goto L1 else goto L3 :: bool L1: if 1 goto L2 else goto L3 :: bool @@ -346,6 +351,7 @@ L3: L4: r8 = box(None, 1) return r8 + [case testMatchSingleton_python3_10] def f(): match 123: @@ -449,7 +455,7 @@ def f(): r9 :: object_ptr r10, r11 :: object L0: - r0 = 2 == 2 + r0 = int_eq 2, 2 if r0 goto L3 else goto L1 :: bool L1: r1 = load_address PyLong_Type @@ -472,6 +478,7 @@ L4: L5: r11 = box(None, 1) return r11 + [case testMatchAsPattern_python3_10] def f(): match 123: @@ -487,7 +494,7 @@ def f(): r6 :: object_ptr r7, r8 :: object L0: - r0 = 246 == 246 + r0 = int_eq 246, 246 r1 = object 123 x = r1 if r0 goto L1 else goto L2 :: bool @@ -504,6 +511,7 @@ L2: L3: r8 = box(None, 1) return r8 + [case testMatchAsPatternOnOrPattern_python3_10] def f(): match 1: @@ -521,12 +529,12 @@ def f(): r8 :: object_ptr r9, r10 :: object L0: - r0 = 2 == 2 + r0 = int_eq 2, 2 r1 = object 1 x = r1 if r0 goto L3 else goto L1 :: bool L1: - r2 = 2 == 4 + r2 = int_eq 2, 4 r3 = object 2 x = r3 if r2 goto L3 else goto L2 :: bool @@ -545,6 +553,7 @@ L4: L5: r10 = box(None, 1) return r10 + [case testMatchAsPatternOnClassPattern_python3_10] def f(): match 123: diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index b2b884705366..62ae6eb9ee35 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -658,7 +658,7 @@ def baz_f_obj.__call__(__mypyc_self__, n): r6, r7 :: int L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = n == 0 + r1 = int_eq n, 0 if r1 goto L1 else goto L2 :: bool L1: return 0 @@ -796,7 +796,7 @@ def baz(n): r0 :: bit r1, r2, r3 :: int L0: - r0 = n == 0 + r0 = int_eq n, 0 if r0 goto L1 else goto L2 :: bool L1: return 0 diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index e89018a727da..75c008586999 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -222,7 +222,7 @@ def f(y): L0: r0 = box(None, 1) x = r0 - r1 = y == 2 + r1 = int_eq y, 2 if r1 goto L1 else goto L2 :: bool L1: r2 = box(int, y) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index a47f3db6a725..ab0e2fa09a9d 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -195,70 +195,30 @@ def f(i: int) -> bool: [out] def f(i): i :: int - r0 :: native_int - r1, r2 :: bit + r0 :: bit + r1 :: bool + r2 :: bit r3 :: bool r4 :: bit - r5 :: bool - r6 :: native_int - r7, r8 :: bit - r9 :: bool - r10 :: bit - r11 :: bool - r12 :: native_int - r13, r14 :: bit - r15 :: bool - r16 :: bit L0: - r0 = i & 1 - r1 = r0 == 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq i, 2 + if r0 goto L1 else goto L2 :: bool L1: - r2 = i == 2 - r3 = r2 + r1 = r0 goto L3 L2: - r4 = CPyTagged_IsEq_(i, 2) - r3 = r4 + r2 = int_eq i, 4 + r1 = r2 L3: - if r3 goto L4 else goto L5 :: bool + if r1 goto L4 else goto L5 :: bool L4: - r5 = r3 - goto L9 + r3 = r1 + goto L6 L5: - r6 = i & 1 - r7 = r6 == 0 - if r7 goto L6 else goto L7 :: bool + r4 = int_eq i, 6 + r3 = r4 L6: - r8 = i == 4 - r9 = r8 - goto L8 -L7: - r10 = CPyTagged_IsEq_(i, 4) - r9 = r10 -L8: - r5 = r9 -L9: - if r5 goto L10 else goto L11 :: bool -L10: - r11 = r5 - goto L15 -L11: - r12 = i & 1 - r13 = r12 == 0 - if r13 goto L12 else goto L13 :: bool -L12: - r14 = i == 6 - r15 = r14 - goto L14 -L13: - r16 = CPyTagged_IsEq_(i, 6) - r15 = r16 -L14: - r11 = r15 -L15: - return r11 - + return r3 [case testTupleBuiltFromList] def f(val: int) -> bool: @@ -270,24 +230,11 @@ def test() -> None: [out] def f(val): val, r0 :: int - r1 :: native_int - r2, r3 :: bit - r4 :: bool - r5 :: bit + r1 :: bit L0: r0 = CPyTagged_Remainder(val, 4) - r1 = r0 & 1 - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool -L1: - r3 = r0 == 0 - r4 = r3 - goto L3 -L2: - r5 = CPyTagged_IsEq_(r0, 0) - r4 = r5 -L3: - return r4 + r1 = int_eq r0, 0 + return r1 def test(): r0 :: list r1, r2, r3 :: object diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test new file mode 100644 index 000000000000..8c813563d0e6 --- /dev/null +++ b/mypyc/test-data/lowering-int.test @@ -0,0 +1,126 @@ +-- Test cases for converting high-level IR to lower-level IR (lowering). + +[case testLowerIntEq] +def f(x: int, y: int) -> int: + if x == y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1, r2, r3 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = x == y + if r2 goto L3 else goto L4 :: bool +L2: + r3 = CPyTagged_IsEq_(x, y) + if r3 goto L3 else goto L4 :: bool +L3: + return 2 +L4: + return 4 + +[case testLowerIntNe] +def f(x: int, y: int) -> int: + if x != y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1, r2, r3, r4 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = x != y + if r2 goto L3 else goto L4 :: bool +L2: + r3 = CPyTagged_IsEq_(x, y) + r4 = r3 ^ 1 + if r4 goto L3 else goto L4 :: bool +L3: + return 2 +L4: + return 4 + +[case testLowerIntEqWithConstant] +def f(x: int, y: int) -> int: + if x == 2: + return 1 + elif -1 == x: + return 2 + return 3 +[out] +def f(x, y): + x, y :: int + r0, r1 :: bit +L0: + r0 = x == 4 + if r0 goto L1 else goto L2 :: bool +L1: + return 2 +L2: + r1 = -2 == x + if r1 goto L3 else goto L4 :: bool +L3: + return 4 +L4: + return 6 + +[case testLowerIntNeWithConstant] +def f(x: int, y: int) -> int: + if x != 2: + return 1 + elif -1 != x: + return 2 + return 3 +[out] +def f(x, y): + x, y :: int + r0, r1 :: bit +L0: + r0 = x != 4 + if r0 goto L1 else goto L2 :: bool +L1: + return 2 +L2: + r1 = -2 != x + if r1 goto L3 else goto L4 :: bool +L3: + return 4 +L4: + return 6 + +[case testLowerIntEqValueContext] +def f(x: int, y: int) -> bool: + return x == y +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1, r2 :: bit + r3 :: bool + r4 :: bit +L0: + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = x == y + r3 = r2 + goto L3 +L2: + r4 = CPyTagged_IsEq_(x, y) + r3 = r4 +L3: + return r3 diff --git a/mypyc/test-data/opt-flag-elimination.test b/mypyc/test-data/opt-flag-elimination.test index f047a87dc3fa..337ced70a355 100644 --- a/mypyc/test-data/opt-flag-elimination.test +++ b/mypyc/test-data/opt-flag-elimination.test @@ -29,15 +29,13 @@ L0: if x goto L1 else goto L2 :: bool L1: r0 = c() - if r0 goto L4 else goto L5 :: bool + if r0 goto L3 else goto L4 :: bool L2: r1 = d() - if r1 goto L4 else goto L5 :: bool + if r1 goto L3 else goto L4 :: bool L3: - unreachable -L4: return 2 -L5: +L4: return 4 [case testFlagEliminationOneAssignment] @@ -92,20 +90,18 @@ L0: if x goto L1 else goto L2 :: bool L1: r0 = c(2) - if r0 goto L6 else goto L7 :: bool + if r0 goto L5 else goto L6 :: bool L2: if y goto L3 else goto L4 :: bool L3: r1 = c(4) - if r1 goto L6 else goto L7 :: bool + if r1 goto L5 else goto L6 :: bool L4: r2 = c(6) - if r2 goto L6 else goto L7 :: bool + if r2 goto L5 else goto L6 :: bool L5: - unreachable -L6: return 2 -L7: +L6: return 4 [case testFlagEliminationAssignmentNotLastOp] diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 0f2c134ae21e..df980af8a7c7 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -67,7 +67,7 @@ def f(): L0: x = 2 y = 4 - r0 = x == 2 + r0 = int_eq x, 2 if r0 goto L3 else goto L4 :: bool L1: return x @@ -185,34 +185,26 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: native_int - r1, r2, r3 :: bit - x, r4, y :: int + r0 :: bit + x, r1, y :: int L0: - r0 = a & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq a, a + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsEq_(a, a) - if r2 goto L3 else goto L4 :: bool -L2: - r3 = a == a - if r3 goto L3 else goto L4 :: bool -L3: a = 2 - goto L5 -L4: + goto L3 +L2: x = 4 dec_ref x :: int - goto L6 -L5: - r4 = CPyTagged_Add(a, 2) + goto L4 +L3: + r1 = CPyTagged_Add(a, 2) dec_ref a :: int - y = r4 + y = r1 return y -L6: +L4: inc_ref a :: int - goto L5 + goto L3 [case testConditionalAssignToArgument2] def f(a: int) -> int: @@ -225,33 +217,25 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: native_int - r1, r2, r3 :: bit - x, r4, y :: int + r0 :: bit + x, r1, y :: int L0: - r0 = a & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq a, a + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsEq_(a, a) - if r2 goto L3 else goto L4 :: bool -L2: - r3 = a == a - if r3 goto L3 else goto L4 :: bool -L3: x = 4 dec_ref x :: int - goto L6 -L4: + goto L4 +L2: a = 2 -L5: - r4 = CPyTagged_Add(a, 2) +L3: + r1 = CPyTagged_Add(a, 2) dec_ref a :: int - y = r4 + y = r1 return y -L6: +L4: inc_ref a :: int - goto L5 + goto L3 [case testConditionalAssignToArgument3] def f(a: int) -> int: @@ -261,25 +245,17 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit L0: - r0 = a & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq a, a + if r0 goto L1 else goto L3 :: bool L1: - r2 = CPyTagged_IsEq_(a, a) - if r2 goto L3 else goto L5 :: bool -L2: - r3 = a == a - if r3 goto L3 else goto L5 :: bool -L3: a = 2 -L4: +L2: return a -L5: +L3: inc_ref a :: int - goto L4 + goto L2 [case testAssignRegisterToItself] def f(a: int) -> int: @@ -438,40 +414,32 @@ def f() -> int: [out] def f(): x, y, z :: int - r0 :: native_int - r1, r2, r3 :: bit - a, r4, r5 :: int + r0 :: bit + a, r1, r2 :: int L0: x = 2 y = 4 z = 6 - r0 = z & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_eq z, z + if r0 goto L3 else goto L4 :: bool L1: - r2 = CPyTagged_IsEq_(z, z) - if r2 goto L5 else goto L6 :: bool -L2: - r3 = z == z - if r3 goto L5 else goto L6 :: bool -L3: return z -L4: +L2: a = 2 - r4 = CPyTagged_Add(x, y) + r1 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int - r5 = CPyTagged_Subtract(r4, a) - dec_ref r4 :: int + r2 = CPyTagged_Subtract(r1, a) + dec_ref r1 :: int dec_ref a :: int - return r5 -L5: + return r2 +L3: dec_ref x :: int dec_ref y :: int - goto L3 -L6: + goto L1 +L4: dec_ref z :: int - goto L4 + goto L2 [case testLoop] def f(a: int) -> int: @@ -1371,25 +1339,12 @@ class C: def add(c): c :: __main__.C r0, r1 :: int - r2 :: native_int - r3, r4 :: bit - r5 :: bool - r6 :: bit + r2 :: bit L0: r0 = borrow c.x r1 = borrow c.y - r2 = r0 & 1 - r3 = r2 == 0 - if r3 goto L1 else goto L2 :: bool -L1: - r4 = r0 == r1 - r5 = r4 - goto L3 -L2: - r6 = CPyTagged_IsEq_(r0, r1) - r5 = r6 -L3: - return r5 + r2 = int_eq r0, r1 + return r2 [case testBorrowIntLessThan] def add(c: C) -> bool: @@ -1441,24 +1396,11 @@ class C: def add(c): c :: __main__.C r0 :: int - r1 :: native_int - r2, r3 :: bit - r4 :: bool - r5 :: bit + r1 :: bit L0: r0 = borrow c.x - r1 = r0 & 1 - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool -L1: - r3 = r0 == 20 - r4 = r3 - goto L3 -L2: - r5 = CPyTagged_IsEq_(r0, 20) - r4 = r5 -L3: - return r4 + r1 = int_eq r0, 20 + return r1 [case testBorrowIntArithmetic] def add(c: C) -> int: @@ -1501,23 +1443,15 @@ class C: def add(c, n): c :: __main__.C n, r0, r1 :: int - r2 :: native_int - r3, r4, r5 :: bit + r2 :: bit L0: r0 = borrow c.x r1 = borrow c.y - r2 = r0 & 1 - r3 = r2 != 0 - if r3 goto L1 else goto L2 :: bool + r2 = int_eq r0, r1 + if r2 goto L1 else goto L2 :: bool L1: - r4 = CPyTagged_IsEq_(r0, r1) - if r4 goto L3 else goto L4 :: bool -L2: - r5 = r0 == r1 - if r5 goto L3 else goto L4 :: bool -L3: return 1 -L4: +L2: return 0 [case testBorrowIntInPlaceOp] diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index cc0fd9df2b34..f2af41c22ea9 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -7,6 +7,7 @@ import re import unittest +from mypyc.ir.ops import PrimitiveDescription from mypyc.primitives import registry from mypyc.primitives.registry import CFunctionDescription @@ -25,17 +26,24 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for values in [ + for old_values in [ registry.method_call_ops.values(), registry.function_ops.values(), - registry.binary_ops.values(), registry.unary_ops.values(), ]: + for old_ops in old_values: + if isinstance(old_ops, CFunctionDescription): + old_ops = [old_ops] + for old_op in old_ops: + check_name(old_op.c_function_name) + + for values in [registry.binary_ops.values()]: for ops in values: - if isinstance(ops, CFunctionDescription): + if isinstance(ops, PrimitiveDescription): ops = [ops] for op in ops: - check_name(op.c_function_name) + if op.c_function_name is not None: + check_name(op.c_function_name) primitives_path = os.path.join(os.path.dirname(__file__), "..", "primitives") for fnam in glob.glob(f"{primitives_path}/*.py"): diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index ab1586bb22a8..b16387aa40af 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -859,6 +859,8 @@ def assert_emit_binary_op( args = [left, right] if desc.ordering is not None: args = [args[i] for i in desc.ordering] + # This only supports primitives that map to C calls + assert desc.c_function_name is not None self.assert_emit( CallC( desc.c_function_name, diff --git a/mypyc/test/test_lowering.py b/mypyc/test/test_lowering.py new file mode 100644 index 000000000000..e32dba2e1021 --- /dev/null +++ b/mypyc/test/test_lowering.py @@ -0,0 +1,54 @@ +"""Runner for lowering transform tests.""" + +from __future__ import annotations + +import os.path + +from mypy.errors import CompileError +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase +from mypyc.common import TOP_LEVEL_NAME +from mypyc.ir.pprint import format_func +from mypyc.options import CompilerOptions +from mypyc.test.testutil import ( + ICODE_GEN_BUILTINS, + MypycDataSuite, + assert_test_output, + build_ir_for_single_file, + remove_comment_lines, + use_custom_builtins, +) +from mypyc.transform.exceptions import insert_exception_handling +from mypyc.transform.flag_elimination import do_flag_elimination +from mypyc.transform.lower import lower_ir +from mypyc.transform.refcount import insert_ref_count_opcodes +from mypyc.transform.uninit import insert_uninit_checks + + +class TestLowering(MypycDataSuite): + files = ["lowering-int.test"] + base_path = test_temp_dir + + def run_case(self, testcase: DataDrivenTestCase) -> None: + with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): + expected_output = remove_comment_lines(testcase.output) + try: + ir = build_ir_for_single_file(testcase.input) + except CompileError as e: + actual = e.messages + else: + actual = [] + for fn in ir: + if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"): + continue + options = CompilerOptions() + # Lowering happens after exception handling and ref count opcodes have + # been added. Any changes must maintain reference counting semantics. + insert_uninit_checks(fn) + insert_exception_handling(fn) + insert_ref_count_opcodes(fn) + lower_ir(fn, options) + do_flag_elimination(fn, options) + actual.extend(format_func(fn)) + + assert_test_output(testcase, actual, "Invalid source code output", expected_output) diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py index 254fe3f7771d..a631bd7352b5 100644 --- a/mypyc/transform/ir_transform.py +++ b/mypyc/transform/ir_transform.py @@ -35,6 +35,7 @@ MethodCall, Op, OpVisitor, + PrimitiveOp, RaiseStandardError, Return, SetAttr, @@ -80,6 +81,7 @@ def transform_blocks(self, blocks: list[BasicBlock]) -> None: """ block_map: dict[BasicBlock, BasicBlock] = {} op_map = self.op_map + empties = set() for block in blocks: new_block = BasicBlock() block_map[block] = new_block @@ -89,7 +91,10 @@ def transform_blocks(self, blocks: list[BasicBlock]) -> None: new_op = op.accept(self) if new_op is not op: op_map[op] = new_op - + # A transform can produce empty blocks which can be removed. + if is_empty_block(new_block) and not is_empty_block(block): + empties.add(new_block) + self.builder.blocks = [block for block in self.builder.blocks if block not in empties] # Update all op/block references to point to the transformed ones. patcher = PatchVisitor(op_map, block_map) for block in self.builder.blocks: @@ -170,6 +175,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> Value | None: def visit_call_c(self, op: CallC) -> Value | None: return self.add(op) + def visit_primitive_op(self, op: PrimitiveOp) -> Value | None: + return self.add(op) + def visit_truncate(self, op: Truncate) -> Value | None: return self.add(op) @@ -302,6 +310,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None: def visit_call_c(self, op: CallC) -> None: op.args = [self.fix_op(arg) for arg in op.args] + def visit_primitive_op(self, op: PrimitiveOp) -> None: + op.args = [self.fix_op(arg) for arg in op.args] + def visit_truncate(self, op: Truncate) -> None: op.src = self.fix_op(op.src) @@ -351,3 +362,7 @@ def visit_keep_alive(self, op: KeepAlive) -> None: def visit_unborrow(self, op: Unborrow) -> None: op.src = self.fix_op(op.src) + + +def is_empty_block(block: BasicBlock) -> bool: + return len(block.ops) == 1 and isinstance(block.ops[0], Unreachable) diff --git a/mypyc/transform/lower.py b/mypyc/transform/lower.py new file mode 100644 index 000000000000..b717657095f9 --- /dev/null +++ b/mypyc/transform/lower.py @@ -0,0 +1,33 @@ +"""Transform IR to lower-level ops. + +Higher-level ops are used in earlier compiler passes, as they make +various analyses, optimizations and transforms easier to implement. +Later passes use lower-level ops, as they are easier to generate code +from, and they help with lower-level optimizations. + +Lowering of various primitive ops is implemented in the mypyc.lower +package. +""" + +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import PrimitiveOp, Value +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.lower.registry import lowering_registry +from mypyc.options import CompilerOptions +from mypyc.transform.ir_transform import IRTransform + + +def lower_ir(ir: FuncIR, options: CompilerOptions) -> None: + builder = LowLevelIRBuilder(None, options) + visitor = LoweringVisitor(builder) + visitor.transform_blocks(ir.blocks) + ir.blocks = builder.blocks + + +class LoweringVisitor(IRTransform): + def visit_primitive_op(self, op: PrimitiveOp) -> Value: + # The lowering implementation functions of various primitive ops are stored + # in a registry, which is populated using function decorators. The name + # of op (such as "int_eq") is used as the key. + lower_fn = lowering_registry[op.desc.name] + return lower_fn(self.builder, op.args, op.line) From cf221bdc2cefc539a0b278c10ed6bde0350f370e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 16 Mar 2024 12:40:30 +0000 Subject: [PATCH 0535/1617] Sync typeshed Source commit: https://github.com/python/typeshed/commit/ff7caa30e29d9c9467b1ba0007764c65c0b44a5c --- mypy/typeshed/stdlib/_codecs.pyi | 90 +- mypy/typeshed/stdlib/_collections_abc.pyi | 6 +- mypy/typeshed/stdlib/_compression.pyi | 4 +- mypy/typeshed/stdlib/_ctypes.pyi | 44 +- mypy/typeshed/stdlib/_curses.pyi | 123 +- mypy/typeshed/stdlib/_decimal.pyi | 176 +-- mypy/typeshed/stdlib/_heapq.pyi | 10 +- mypy/typeshed/stdlib/_imp.pyi | 22 +- mypy/typeshed/stdlib/_locale.pyi | 20 +- mypy/typeshed/stdlib/_msi.pyi | 6 +- mypy/typeshed/stdlib/_operator.pyi | 130 +- mypy/typeshed/stdlib/_posixsubprocess.pyi | 47 +- mypy/typeshed/stdlib/_py_abc.pyi | 2 +- mypy/typeshed/stdlib/_random.pyi | 6 +- mypy/typeshed/stdlib/_socket.pyi | 99 +- mypy/typeshed/stdlib/_thread.pyi | 6 +- mypy/typeshed/stdlib/_tkinter.pyi | 73 +- mypy/typeshed/stdlib/_tracemalloc.pyi | 4 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 50 +- mypy/typeshed/stdlib/_typeshed/dbapi.pyi | 12 +- mypy/typeshed/stdlib/_typeshed/wsgi.pyi | 16 +- mypy/typeshed/stdlib/_typeshed/xml.pyi | 6 +- mypy/typeshed/stdlib/_weakref.pyi | 16 +- mypy/typeshed/stdlib/_winapi.pyi | 98 +- mypy/typeshed/stdlib/abc.pyi | 2 +- mypy/typeshed/stdlib/argparse.pyi | 2 +- mypy/typeshed/stdlib/array.pyi | 74 +- mypy/typeshed/stdlib/asyncio/events.pyi | 6 +- mypy/typeshed/stdlib/asyncio/futures.pyi | 8 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 176 +-- mypy/typeshed/stdlib/asyncio/threads.pyi | 2 +- mypy/typeshed/stdlib/asyncio/trsock.pyi | 12 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 40 +- mypy/typeshed/stdlib/audioop.pyi | 67 +- mypy/typeshed/stdlib/bdb.pyi | 2 +- mypy/typeshed/stdlib/binascii.pyi | 26 +- mypy/typeshed/stdlib/builtins.pyi | 1192 +++++++++-------- mypy/typeshed/stdlib/bz2.pyi | 4 +- mypy/typeshed/stdlib/cProfile.pyi | 2 +- mypy/typeshed/stdlib/cgi.pyi | 2 +- mypy/typeshed/stdlib/cmath.pyi | 44 +- mypy/typeshed/stdlib/codecs.pyi | 16 +- mypy/typeshed/stdlib/collections/__init__.pyi | 138 +- mypy/typeshed/stdlib/compileall.pyi | 2 +- .../stdlib/concurrent/futures/_base.pyi | 16 +- mypy/typeshed/stdlib/contextlib.pyi | 14 +- mypy/typeshed/stdlib/contextvars.pyi | 18 +- mypy/typeshed/stdlib/curses/__init__.pyi | 2 +- mypy/typeshed/stdlib/curses/panel.pyi | 2 +- mypy/typeshed/stdlib/dataclasses.pyi | 14 +- mypy/typeshed/stdlib/datetime.pyi | 116 +- mypy/typeshed/stdlib/dbm/gnu.pyi | 2 +- mypy/typeshed/stdlib/dbm/ndbm.pyi | 2 +- mypy/typeshed/stdlib/distutils/core.pyi | 2 +- mypy/typeshed/stdlib/email/charset.pyi | 2 +- mypy/typeshed/stdlib/email/header.pyi | 2 +- mypy/typeshed/stdlib/email/headerregistry.pyi | 4 +- mypy/typeshed/stdlib/email/message.pyi | 12 +- mypy/typeshed/stdlib/encodings/utf_8.pyi | 8 +- mypy/typeshed/stdlib/fcntl.pyi | 16 +- mypy/typeshed/stdlib/fractions.pyi | 2 +- mypy/typeshed/stdlib/functools.pyi | 18 +- mypy/typeshed/stdlib/gc.pyi | 6 +- mypy/typeshed/stdlib/gzip.pyi | 6 +- mypy/typeshed/stdlib/hashlib.pyi | 18 +- mypy/typeshed/stdlib/heapq.pyi | 2 +- mypy/typeshed/stdlib/hmac.pyi | 4 +- mypy/typeshed/stdlib/imghdr.pyi | 4 +- mypy/typeshed/stdlib/imp.pyi | 2 +- mypy/typeshed/stdlib/importlib/abc.pyi | 14 +- mypy/typeshed/stdlib/inspect.pyi | 6 +- mypy/typeshed/stdlib/io.pyi | 64 +- mypy/typeshed/stdlib/itertools.pyi | 108 +- mypy/typeshed/stdlib/json/encoder.pyi | 2 + mypy/typeshed/stdlib/lib2to3/fixer_base.pyi | 2 +- mypy/typeshed/stdlib/logging/__init__.pyi | 9 +- mypy/typeshed/stdlib/logging/handlers.pyi | 2 +- mypy/typeshed/stdlib/lzma.pyi | 4 +- mypy/typeshed/stdlib/marshal.pyi | 8 +- mypy/typeshed/stdlib/math.pyi | 112 +- mypy/typeshed/stdlib/mmap.pyi | 16 +- mypy/typeshed/stdlib/msvcrt.pyi | 18 +- .../stdlib/multiprocessing/context.pyi | 12 +- .../stdlib/multiprocessing/dummy/__init__.pyi | 4 +- .../stdlib/multiprocessing/managers.pyi | 70 +- .../stdlib/multiprocessing/queues.pyi | 2 +- .../stdlib/multiprocessing/sharedctypes.pyi | 10 +- .../stdlib/multiprocessing/synchronize.pyi | 4 +- mypy/typeshed/stdlib/ntpath.pyi | 6 +- mypy/typeshed/stdlib/opcode.pyi | 2 +- mypy/typeshed/stdlib/optparse.pyi | 113 +- mypy/typeshed/stdlib/os/__init__.pyi | 160 +-- mypy/typeshed/stdlib/pathlib.pyi | 4 +- mypy/typeshed/stdlib/pickle.pyi | 13 +- mypy/typeshed/stdlib/posixpath.pyi | 6 +- mypy/typeshed/stdlib/profile.pyi | 2 +- mypy/typeshed/stdlib/pstats.pyi | 3 +- mypy/typeshed/stdlib/pwd.pyi | 4 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 14 +- mypy/typeshed/stdlib/re.pyi | 22 +- mypy/typeshed/stdlib/readline.pyi | 34 +- mypy/typeshed/stdlib/resource.pyi | 10 +- mypy/typeshed/stdlib/select.pyi | 15 +- mypy/typeshed/stdlib/signal.pyi | 30 +- mypy/typeshed/stdlib/smtplib.pyi | 4 +- mypy/typeshed/stdlib/socket.pyi | 8 +- mypy/typeshed/stdlib/spwd.pyi | 2 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 175 ++- mypy/typeshed/stdlib/ssl.pyi | 20 +- mypy/typeshed/stdlib/statistics.pyi | 10 +- mypy/typeshed/stdlib/string.pyi | 8 +- mypy/typeshed/stdlib/struct.pyi | 16 +- mypy/typeshed/stdlib/sys/__init__.pyi | 34 +- mypy/typeshed/stdlib/sys/_monitoring.pyi | 16 +- mypy/typeshed/stdlib/syslog.pyi | 6 +- mypy/typeshed/stdlib/tarfile.pyi | 6 +- mypy/typeshed/stdlib/tempfile.pyi | 8 +- mypy/typeshed/stdlib/termios.pyi | 16 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 152 ++- mypy/typeshed/stdlib/tkinter/dnd.pyi | 2 +- mypy/typeshed/stdlib/tkinter/font.pyi | 4 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 8 +- mypy/typeshed/stdlib/tomllib.pyi | 4 +- mypy/typeshed/stdlib/trace.pyi | 2 +- mypy/typeshed/stdlib/traceback.pyi | 14 +- mypy/typeshed/stdlib/types.pyi | 191 +-- mypy/typeshed/stdlib/typing.pyi | 109 +- mypy/typeshed/stdlib/typing_extensions.pyi | 36 +- mypy/typeshed/stdlib/unicodedata.pyi | 68 +- mypy/typeshed/stdlib/unittest/async_case.pyi | 2 +- mypy/typeshed/stdlib/unittest/case.pyi | 6 +- mypy/typeshed/stdlib/unittest/main.pyi | 2 +- mypy/typeshed/stdlib/unittest/mock.pyi | 6 +- mypy/typeshed/stdlib/urllib/request.pyi | 3 +- mypy/typeshed/stdlib/weakref.pyi | 7 +- mypy/typeshed/stdlib/winreg.pyi | 38 +- mypy/typeshed/stdlib/wsgiref/types.pyi | 16 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 36 +- mypy/typeshed/stdlib/xmlrpc/server.pyi | 8 +- mypy/typeshed/stdlib/xxlimited.pyi | 4 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 10 +- mypy/typeshed/stdlib/zlib.pyi | 10 +- mypy/typeshed/stdlib/zoneinfo/__init__.pyi | 12 +- 143 files changed, 2703 insertions(+), 2458 deletions(-) diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index 6de4666e0776..ecf874d33ddd 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -13,13 +13,13 @@ _CharMap: TypeAlias = dict[int, int] | _EncodingMap _Handler: TypeAlias = Callable[[UnicodeError], tuple[str | bytes, int]] _SearchFunction: TypeAlias = Callable[[str], codecs.CodecInfo | None] -def register(__search_function: _SearchFunction) -> None: ... +def register(search_function: _SearchFunction, /) -> None: ... if sys.version_info >= (3, 10): - def unregister(__search_function: _SearchFunction) -> None: ... + def unregister(search_function: _SearchFunction, /) -> None: ... -def register_error(__errors: str, __handler: _Handler) -> None: ... -def lookup_error(__name: str) -> _Handler: ... +def register_error(errors: str, handler: _Handler, /) -> None: ... +def lookup_error(name: str, /) -> _Handler: ... # The type ignore on `encode` and `decode` is to avoid issues with overlapping overloads, for more details, see #300 # https://docs.python.org/3/library/codecs.html#binary-transforms @@ -68,66 +68,66 @@ def decode( def decode(obj: str, encoding: Literal["hex", "hex_codec"], errors: str = "strict") -> bytes: ... @overload def decode(obj: ReadableBuffer, encoding: str = "utf-8", errors: str = "strict") -> str: ... -def lookup(__encoding: str) -> codecs.CodecInfo: ... -def charmap_build(__map: str) -> _CharMap: ... -def ascii_decode(__data: ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... -def ascii_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def charmap_decode(__data: ReadableBuffer, __errors: str | None = None, __mapping: _CharMap | None = None) -> tuple[str, int]: ... -def charmap_encode(__str: str, __errors: str | None = None, __mapping: _CharMap | None = None) -> tuple[bytes, int]: ... -def escape_decode(__data: str | ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... -def escape_encode(__data: bytes, __errors: str | None = None) -> tuple[bytes, int]: ... -def latin_1_decode(__data: ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... -def latin_1_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... +def lookup(encoding: str, /) -> codecs.CodecInfo: ... +def charmap_build(map: str, /) -> _CharMap: ... +def ascii_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... +def ascii_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def charmap_decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ... +def charmap_encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ... +def escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... +def escape_encode(data: bytes, errors: str | None = None, /) -> tuple[bytes, int]: ... +def latin_1_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... +def latin_1_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... if sys.version_info >= (3, 9): def raw_unicode_escape_decode( - __data: str | ReadableBuffer, __errors: str | None = None, __final: bool = True + data: str | ReadableBuffer, errors: str | None = None, final: bool = True, / ) -> tuple[str, int]: ... else: - def raw_unicode_escape_decode(__data: str | ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... + def raw_unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... -def raw_unicode_escape_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def readbuffer_encode(__data: str | ReadableBuffer, __errors: str | None = None) -> tuple[bytes, int]: ... +def raw_unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def readbuffer_encode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[bytes, int]: ... if sys.version_info >= (3, 9): def unicode_escape_decode( - __data: str | ReadableBuffer, __errors: str | None = None, __final: bool = True + data: str | ReadableBuffer, errors: str | None = None, final: bool = True, / ) -> tuple[str, int]: ... else: - def unicode_escape_decode(__data: str | ReadableBuffer, __errors: str | None = None) -> tuple[str, int]: ... + def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... -def unicode_escape_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_16_be_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_16_be_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_16_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_16_encode(__str: str, __errors: str | None = None, __byteorder: int = 0) -> tuple[bytes, int]: ... +def unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_16_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_16_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_16_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_16_encode(str: str, errors: str | None = None, byteorder: int = 0, /) -> tuple[bytes, int]: ... def utf_16_ex_decode( - __data: ReadableBuffer, __errors: str | None = None, __byteorder: int = 0, __final: bool = False + data: ReadableBuffer, errors: str | None = None, byteorder: int = 0, final: bool = False, / ) -> tuple[str, int, int]: ... -def utf_16_le_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_16_le_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_32_be_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_32_be_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_32_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_32_encode(__str: str, __errors: str | None = None, __byteorder: int = 0) -> tuple[bytes, int]: ... +def utf_16_le_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_16_le_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_32_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_32_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_32_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_32_encode(str: str, errors: str | None = None, byteorder: int = 0, /) -> tuple[bytes, int]: ... def utf_32_ex_decode( - __data: ReadableBuffer, __errors: str | None = None, __byteorder: int = 0, __final: bool = False + data: ReadableBuffer, errors: str | None = None, byteorder: int = 0, final: bool = False, / ) -> tuple[str, int, int]: ... -def utf_32_le_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_32_le_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_7_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_7_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... -def utf_8_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... -def utf_8_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... +def utf_32_le_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_32_le_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_7_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_7_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... +def utf_8_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... +def utf_8_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... if sys.platform == "win32": - def mbcs_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... - def mbcs_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... + def mbcs_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + def mbcs_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... def code_page_decode( - __codepage: int, __data: ReadableBuffer, __errors: str | None = None, __final: bool = False + codepage: int, data: ReadableBuffer, errors: str | None = None, final: bool = False, / ) -> tuple[str, int]: ... - def code_page_encode(__code_page: int, __str: str, __errors: str | None = None) -> tuple[bytes, int]: ... - def oem_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... - def oem_encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... + def code_page_encode(code_page: int, str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + def oem_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + def oem_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 0aa09967a895..e467d626e8a8 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -69,7 +69,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -82,7 +82,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -91,4 +91,4 @@ if sys.version_info >= (3, 12): @runtime_checkable class Buffer(Protocol): @abstractmethod - def __buffer__(self, __flags: int) -> memoryview: ... + def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/_compression.pyi b/mypy/typeshed/stdlib/_compression.pyi index 24e11261140b..a41a8142cc3a 100644 --- a/mypy/typeshed/stdlib/_compression.pyi +++ b/mypy/typeshed/stdlib/_compression.pyi @@ -6,9 +6,9 @@ from typing import Any, Protocol BUFFER_SIZE = DEFAULT_BUFFER_SIZE class _Reader(Protocol): - def read(self, __n: int) -> bytes: ... + def read(self, n: int, /) -> bytes: ... def seekable(self) -> bool: ... - def seek(self, __n: int) -> Any: ... + def seek(self, n: int, /) -> Any: ... class BaseStream(BufferedIOBase): ... diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index e0cc87814609..60bbc51d9411 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -44,8 +44,8 @@ if sys.platform == "win32": def FormatError(code: int = ...) -> str: ... def get_last_error() -> int: ... def set_last_error(value: int) -> int: ... - def LoadLibrary(__name: str, __load_flags: int = 0) -> int: ... - def FreeLibrary(__handle: int) -> None: ... + def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ... + def FreeLibrary(handle: int, /) -> None: ... class _CDataMeta(type): # By default mypy complains about the following two methods, because strictly speaking cls @@ -75,8 +75,8 @@ class _CData(metaclass=_CDataMeta): def from_param(cls, obj: Any) -> Self | _CArgObject: ... @classmethod def in_dll(cls, library: CDLL, name: str) -> Self: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... class _SimpleCData(_CData, Generic[_T]): value: _T @@ -95,13 +95,13 @@ class _Pointer(_PointerLike, _CData, Generic[_CT]): @overload def __init__(self, arg: _CT) -> None: ... @overload - def __getitem__(self, __key: int) -> Any: ... + def __getitem__(self, key: int, /) -> Any: ... @overload - def __getitem__(self, __key: slice) -> list[Any]: ... - def __setitem__(self, __key: int, __value: Any) -> None: ... + def __getitem__(self, key: slice, /) -> list[Any]: ... + def __setitem__(self, key: int, value: Any, /) -> None: ... def POINTER(type: type[_CT]) -> type[_Pointer[_CT]]: ... -def pointer(__arg: _CT) -> _Pointer[_CT]: ... +def pointer(arg: _CT, /) -> _Pointer[_CT]: ... class _CArgObject: ... @@ -119,15 +119,15 @@ class CFuncPtr(_PointerLike, _CData): @overload def __init__(self) -> None: ... @overload - def __init__(self, __address: int) -> None: ... + def __init__(self, address: int, /) -> None: ... @overload - def __init__(self, __callable: Callable[..., Any]) -> None: ... + def __init__(self, callable: Callable[..., Any], /) -> None: ... @overload - def __init__(self, __func_spec: tuple[str | int, CDLL], __paramflags: tuple[_PF, ...] | None = ...) -> None: ... + def __init__(self, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> None: ... if sys.platform == "win32": @overload def __init__( - self, __vtbl_index: int, __name: str, __paramflags: tuple[_PF, ...] | None = ..., __iid: _CData | None = ... + self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., / ) -> None: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @@ -139,10 +139,10 @@ class _CField(Generic[_CT, _GetT, _SetT]): offset: int size: int @overload - def __get__(self, __instance: None, __owner: type[Any] | None) -> Self: ... + def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... @overload - def __get__(self, __instance: Any, __owner: type[Any] | None) -> _GetT: ... - def __set__(self, __instance: Any, __value: _SetT) -> None: ... + def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + def __set__(self, instance: Any, value: _SetT, /) -> None: ... class _StructUnionMeta(_CDataMeta): _fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]] @@ -169,7 +169,11 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - raw: bytes # Note: only available if _CT == c_char + # Note: only available if _CT == c_char + @property + def raw(self) -> bytes: ... + @raw.setter + def raw(self, value: ReadableBuffer) -> None: ... value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT @@ -185,13 +189,13 @@ class Array(_CData, Generic[_CT]): # the array element type would belong are annotated with Any instead. def __init__(self, *args: Any) -> None: ... @overload - def __getitem__(self, __key: int) -> Any: ... + def __getitem__(self, key: int, /) -> Any: ... @overload - def __getitem__(self, __key: slice) -> list[Any]: ... + def __getitem__(self, key: slice, /) -> list[Any]: ... @overload - def __setitem__(self, __key: int, __value: Any) -> None: ... + def __setitem__(self, key: int, value: Any, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: Iterable[Any]) -> None: ... + def __setitem__(self, key: slice, value: Iterable[Any], /) -> None: ... def __iter__(self) -> Iterator[Any]: ... # Can't inherit from Sized because the metaclass conflict between # Sized and _CData prevents using _CDataMeta. diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 20189cb285c5..929c6f8f3bc8 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -275,15 +275,15 @@ if sys.platform != "win32": def baudrate() -> int: ... def beep() -> None: ... def can_change_color() -> bool: ... - def cbreak(__flag: bool = True) -> None: ... - def color_content(__color_number: int) -> tuple[int, int, int]: ... - def color_pair(__pair_number: int) -> int: ... - def curs_set(__visibility: int) -> int: ... + def cbreak(flag: bool = True, /) -> None: ... + def color_content(color_number: int, /) -> tuple[int, int, int]: ... + def color_pair(pair_number: int, /) -> int: ... + def curs_set(visibility: int, /) -> int: ... def def_prog_mode() -> None: ... def def_shell_mode() -> None: ... - def delay_output(__ms: int) -> None: ... + def delay_output(ms: int, /) -> None: ... def doupdate() -> None: ... - def echo(__flag: bool = True) -> None: ... + def echo(flag: bool = True, /) -> None: ... def endwin() -> None: ... def erasechar() -> bytes: ... def filter() -> None: ... @@ -295,82 +295,83 @@ if sys.platform != "win32": def getmouse() -> tuple[int, int, int, int, int]: ... def getsyx() -> tuple[int, int]: ... - def getwin(__file: SupportsRead[bytes]) -> _CursesWindow: ... - def halfdelay(__tenths: int) -> None: ... + def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... + def halfdelay(tenths: int, /) -> None: ... def has_colors() -> bool: ... if sys.version_info >= (3, 10): def has_extended_color_support() -> bool: ... def has_ic() -> bool: ... def has_il() -> bool: ... - def has_key(__key: int) -> bool: ... - def init_color(__color_number: int, __r: int, __g: int, __b: int) -> None: ... - def init_pair(__pair_number: int, __fg: int, __bg: int) -> None: ... + def has_key(key: int, /) -> bool: ... + def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... + def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... def initscr() -> _CursesWindow: ... - def intrflush(__flag: bool) -> None: ... - def is_term_resized(__nlines: int, __ncols: int) -> bool: ... + def intrflush(flag: bool, /) -> None: ... + def is_term_resized(nlines: int, ncols: int, /) -> bool: ... def isendwin() -> bool: ... - def keyname(__key: int) -> bytes: ... + def keyname(key: int, /) -> bytes: ... def killchar() -> bytes: ... def longname() -> bytes: ... - def meta(__yes: bool) -> None: ... - def mouseinterval(__interval: int) -> None: ... - def mousemask(__newmask: int) -> tuple[int, int]: ... - def napms(__ms: int) -> int: ... - def newpad(__nlines: int, __ncols: int) -> _CursesWindow: ... - def newwin(__nlines: int, __ncols: int, __begin_y: int = ..., __begin_x: int = ...) -> _CursesWindow: ... - def nl(__flag: bool = True) -> None: ... + def meta(yes: bool, /) -> None: ... + def mouseinterval(interval: int, /) -> None: ... + def mousemask(newmask: int, /) -> tuple[int, int]: ... + def napms(ms: int, /) -> int: ... + def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ... + def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ... + def nl(flag: bool = True, /) -> None: ... def nocbreak() -> None: ... def noecho() -> None: ... def nonl() -> None: ... def noqiflush() -> None: ... def noraw() -> None: ... - def pair_content(__pair_number: int) -> tuple[int, int]: ... - def pair_number(__attr: int) -> int: ... - def putp(__string: ReadOnlyBuffer) -> None: ... - def qiflush(__flag: bool = True) -> None: ... - def raw(__flag: bool = True) -> None: ... + def pair_content(pair_number: int, /) -> tuple[int, int]: ... + def pair_number(attr: int, /) -> int: ... + def putp(string: ReadOnlyBuffer, /) -> None: ... + def qiflush(flag: bool = True, /) -> None: ... + def raw(flag: bool = True, /) -> None: ... def reset_prog_mode() -> None: ... def reset_shell_mode() -> None: ... def resetty() -> None: ... - def resize_term(__nlines: int, __ncols: int) -> None: ... - def resizeterm(__nlines: int, __ncols: int) -> None: ... + def resize_term(nlines: int, ncols: int, /) -> None: ... + def resizeterm(nlines: int, ncols: int, /) -> None: ... def savetty() -> None: ... if sys.version_info >= (3, 9): - def set_escdelay(__ms: int) -> None: ... - def set_tabsize(__size: int) -> None: ... + def set_escdelay(ms: int, /) -> None: ... + def set_tabsize(size: int, /) -> None: ... - def setsyx(__y: int, __x: int) -> None: ... + def setsyx(y: int, x: int, /) -> None: ... def setupterm(term: str | None = None, fd: int = -1) -> None: ... def start_color() -> None: ... def termattrs() -> int: ... def termname() -> bytes: ... - def tigetflag(__capname: str) -> int: ... - def tigetnum(__capname: str) -> int: ... - def tigetstr(__capname: str) -> bytes | None: ... + def tigetflag(capname: str, /) -> int: ... + def tigetnum(capname: str, /) -> int: ... + def tigetstr(capname: str, /) -> bytes | None: ... def tparm( - __str: ReadOnlyBuffer, - __i1: int = 0, - __i2: int = 0, - __i3: int = 0, - __i4: int = 0, - __i5: int = 0, - __i6: int = 0, - __i7: int = 0, - __i8: int = 0, - __i9: int = 0, + str: ReadOnlyBuffer, + i1: int = 0, + i2: int = 0, + i3: int = 0, + i4: int = 0, + i5: int = 0, + i6: int = 0, + i7: int = 0, + i8: int = 0, + i9: int = 0, + /, ) -> bytes: ... - def typeahead(__fd: int) -> None: ... - def unctrl(__ch: _ChType) -> bytes: ... + def typeahead(fd: int, /) -> None: ... + def unctrl(ch: _ChType, /) -> bytes: ... if sys.version_info < (3, 12) or sys.platform != "darwin": # The support for macos was dropped in 3.12 - def unget_wch(__ch: int | str) -> None: ... + def unget_wch(ch: int | str, /) -> None: ... - def ungetch(__ch: _ChType) -> None: ... - def ungetmouse(__id: int, __x: int, __y: int, __z: int, __bstate: int) -> None: ... + def ungetch(ch: _ChType, /) -> None: ... + def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ... def update_lines_cols() -> None: ... def use_default_colors() -> None: ... - def use_env(__flag: bool) -> None: ... + def use_env(flag: bool, /) -> None: ... class error(Exception): ... @@ -389,11 +390,11 @@ if sys.platform != "win32": def addstr(self, str: str, attr: int = ...) -> None: ... @overload def addstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... - def attroff(self, __attr: int) -> None: ... - def attron(self, __attr: int) -> None: ... - def attrset(self, __attr: int) -> None: ... - def bkgd(self, __ch: _ChType, __attr: int = ...) -> None: ... - def bkgdset(self, __ch: _ChType, __attr: int = ...) -> None: ... + def attroff(self, attr: int, /) -> None: ... + def attron(self, attr: int, /) -> None: ... + def attrset(self, attr: int, /) -> None: ... + def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ... + def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ... def border( self, ls: _ChType = ..., @@ -431,8 +432,8 @@ if sys.platform != "win32": def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... @overload def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... - def echochar(self, __ch: _ChType, __attr: int = ...) -> None: ... - def enclose(self, __y: int, __x: int) -> bool: ... + def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... + def enclose(self, y: int, x: int, /) -> bool: ... def erase(self) -> None: ... def getbegyx(self) -> tuple[int, int]: ... def getbkgd(self) -> tuple[int, int]: ... @@ -491,7 +492,7 @@ if sys.platform != "win32": def instr(self, n: int = ...) -> bytes: ... @overload def instr(self, y: int, x: int, n: int = ...) -> bytes: ... - def is_linetouched(self, __line: int) -> bool: ... + def is_linetouched(self, line: int, /) -> bool: ... def is_wintouched(self) -> bool: ... def keypad(self, yes: bool) -> None: ... def leaveok(self, yes: bool) -> None: ... @@ -516,8 +517,8 @@ if sys.platform != "win32": def overwrite( self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... - def putwin(self, __file: IO[Any]) -> None: ... - def redrawln(self, __beg: int, __num: int) -> None: ... + def putwin(self, file: IO[Any], /) -> None: ... + def redrawln(self, beg: int, num: int, /) -> None: ... def redrawwin(self) -> None: ... @overload def refresh(self) -> None: ... @@ -526,7 +527,7 @@ if sys.platform != "win32": def resize(self, nlines: int, ncols: int) -> None: ... def scroll(self, lines: int = ...) -> None: ... def scrollok(self, flag: bool) -> None: ... - def setscrreg(self, __top: int, __bottom: int) -> None: ... + def setscrreg(self, top: int, bottom: int, /) -> None: ... def standend(self) -> None: ... def standout(self) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 369d04cd2d5d..90d16215c280 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -47,7 +47,7 @@ class Overflow(Inexact, Rounded): ... class Underflow(Inexact, Rounded, Subnormal): ... class FloatOperation(DecimalException, TypeError): ... -def setcontext(__context: Context) -> None: ... +def setcontext(context: Context, /) -> None: ... def getcontext() -> Context: ... if sys.version_info >= (3, 11): @@ -70,7 +70,7 @@ else: class Decimal: def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... @classmethod - def from_float(cls, __f: float) -> Self: ... + def from_float(cls, f: float, /) -> Self: ... def __bool__(self) -> bool: ... def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... def __hash__(self) -> int: ... @@ -78,28 +78,28 @@ class Decimal: def as_integer_ratio(self) -> tuple[int, int]: ... def to_eng_string(self, context: Context | None = None) -> str: ... def __abs__(self) -> Decimal: ... - def __add__(self, __value: _Decimal) -> Decimal: ... - def __divmod__(self, __value: _Decimal) -> tuple[Decimal, Decimal]: ... - def __eq__(self, __value: object) -> bool: ... - def __floordiv__(self, __value: _Decimal) -> Decimal: ... - def __ge__(self, __value: _ComparableNum) -> bool: ... - def __gt__(self, __value: _ComparableNum) -> bool: ... - def __le__(self, __value: _ComparableNum) -> bool: ... - def __lt__(self, __value: _ComparableNum) -> bool: ... - def __mod__(self, __value: _Decimal) -> Decimal: ... - def __mul__(self, __value: _Decimal) -> Decimal: ... + def __add__(self, value: _Decimal, /) -> Decimal: ... + def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __eq__(self, value: object, /) -> bool: ... + def __floordiv__(self, value: _Decimal, /) -> Decimal: ... + def __ge__(self, value: _ComparableNum, /) -> bool: ... + def __gt__(self, value: _ComparableNum, /) -> bool: ... + def __le__(self, value: _ComparableNum, /) -> bool: ... + def __lt__(self, value: _ComparableNum, /) -> bool: ... + def __mod__(self, value: _Decimal, /) -> Decimal: ... + def __mul__(self, value: _Decimal, /) -> Decimal: ... def __neg__(self) -> Decimal: ... def __pos__(self) -> Decimal: ... - def __pow__(self, __value: _Decimal, __mod: _Decimal | None = None) -> Decimal: ... - def __radd__(self, __value: _Decimal) -> Decimal: ... - def __rdivmod__(self, __value: _Decimal) -> tuple[Decimal, Decimal]: ... - def __rfloordiv__(self, __value: _Decimal) -> Decimal: ... - def __rmod__(self, __value: _Decimal) -> Decimal: ... - def __rmul__(self, __value: _Decimal) -> Decimal: ... - def __rsub__(self, __value: _Decimal) -> Decimal: ... - def __rtruediv__(self, __value: _Decimal) -> Decimal: ... - def __sub__(self, __value: _Decimal) -> Decimal: ... - def __truediv__(self, __value: _Decimal) -> Decimal: ... + def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... + def __radd__(self, value: _Decimal, /) -> Decimal: ... + def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... + def __rmod__(self, value: _Decimal, /) -> Decimal: ... + def __rmul__(self, value: _Decimal, /) -> Decimal: ... + def __rsub__(self, value: _Decimal, /) -> Decimal: ... + def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... + def __sub__(self, value: _Decimal, /) -> Decimal: ... + def __truediv__(self, value: _Decimal, /) -> Decimal: ... def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... def __float__(self) -> float: ... def __int__(self) -> int: ... @@ -113,11 +113,11 @@ class Decimal: @overload def __round__(self) -> int: ... @overload - def __round__(self, __ndigits: int) -> Decimal: ... + def __round__(self, ndigits: int, /) -> Decimal: ... def __floor__(self) -> int: ... def __ceil__(self) -> int: ... def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... - def __rpow__(self, __value: _Decimal, __mod: Context | None = None) -> Decimal: ... + def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... def normalize(self, context: Context | None = None) -> Decimal: ... def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... @@ -165,8 +165,8 @@ class Decimal: def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... def __copy__(self) -> Self: ... - def __deepcopy__(self, __memo: Any) -> Self: ... - def __format__(self, __specifier: str, __context: Context | None = ...) -> str: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... class _ContextManager: new_context: Context @@ -182,7 +182,7 @@ class Context: # even settable attributes like `prec` and `rounding`, # but that's inexpressable in the stub. # Type checkers either ignore it or misinterpret it - # if you add a `def __delattr__(self, __name: str) -> NoReturn` method to the stub + # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub prec: int rounding: str Emin: int @@ -212,69 +212,69 @@ class Context: __hash__: ClassVar[None] # type: ignore[assignment] def Etiny(self) -> int: ... def Etop(self) -> int: ... - def create_decimal(self, __num: _DecimalNew = "0") -> Decimal: ... - def create_decimal_from_float(self, __f: float) -> Decimal: ... - def abs(self, __x: _Decimal) -> Decimal: ... - def add(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def canonical(self, __x: Decimal) -> Decimal: ... - def compare(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_signal(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_total(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_total_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def copy_abs(self, __x: _Decimal) -> Decimal: ... - def copy_decimal(self, __x: _Decimal) -> Decimal: ... - def copy_negate(self, __x: _Decimal) -> Decimal: ... - def copy_sign(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divide(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divide_int(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divmod(self, __x: _Decimal, __y: _Decimal) -> tuple[Decimal, Decimal]: ... - def exp(self, __x: _Decimal) -> Decimal: ... - def fma(self, __x: _Decimal, __y: _Decimal, __z: _Decimal) -> Decimal: ... - def is_canonical(self, __x: _Decimal) -> bool: ... - def is_finite(self, __x: _Decimal) -> bool: ... - def is_infinite(self, __x: _Decimal) -> bool: ... - def is_nan(self, __x: _Decimal) -> bool: ... - def is_normal(self, __x: _Decimal) -> bool: ... - def is_qnan(self, __x: _Decimal) -> bool: ... - def is_signed(self, __x: _Decimal) -> bool: ... - def is_snan(self, __x: _Decimal) -> bool: ... - def is_subnormal(self, __x: _Decimal) -> bool: ... - def is_zero(self, __x: _Decimal) -> bool: ... - def ln(self, __x: _Decimal) -> Decimal: ... - def log10(self, __x: _Decimal) -> Decimal: ... - def logb(self, __x: _Decimal) -> Decimal: ... - def logical_and(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def logical_invert(self, __x: _Decimal) -> Decimal: ... - def logical_or(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def logical_xor(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def max(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def max_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def min(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def min_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def minus(self, __x: _Decimal) -> Decimal: ... - def multiply(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def next_minus(self, __x: _Decimal) -> Decimal: ... - def next_plus(self, __x: _Decimal) -> Decimal: ... - def next_toward(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def normalize(self, __x: _Decimal) -> Decimal: ... - def number_class(self, __x: _Decimal) -> str: ... - def plus(self, __x: _Decimal) -> Decimal: ... + def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... + def create_decimal_from_float(self, f: float, /) -> Decimal: ... + def abs(self, x: _Decimal, /) -> Decimal: ... + def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def canonical(self, x: Decimal, /) -> Decimal: ... + def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def copy_abs(self, x: _Decimal, /) -> Decimal: ... + def copy_decimal(self, x: _Decimal, /) -> Decimal: ... + def copy_negate(self, x: _Decimal, /) -> Decimal: ... + def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def exp(self, x: _Decimal, /) -> Decimal: ... + def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... + def is_canonical(self, x: _Decimal, /) -> bool: ... + def is_finite(self, x: _Decimal, /) -> bool: ... + def is_infinite(self, x: _Decimal, /) -> bool: ... + def is_nan(self, x: _Decimal, /) -> bool: ... + def is_normal(self, x: _Decimal, /) -> bool: ... + def is_qnan(self, x: _Decimal, /) -> bool: ... + def is_signed(self, x: _Decimal, /) -> bool: ... + def is_snan(self, x: _Decimal, /) -> bool: ... + def is_subnormal(self, x: _Decimal, /) -> bool: ... + def is_zero(self, x: _Decimal, /) -> bool: ... + def ln(self, x: _Decimal, /) -> Decimal: ... + def log10(self, x: _Decimal, /) -> Decimal: ... + def logb(self, x: _Decimal, /) -> Decimal: ... + def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_invert(self, x: _Decimal, /) -> Decimal: ... + def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def minus(self, x: _Decimal, /) -> Decimal: ... + def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def next_minus(self, x: _Decimal, /) -> Decimal: ... + def next_plus(self, x: _Decimal, /) -> Decimal: ... + def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def normalize(self, x: _Decimal, /) -> Decimal: ... + def number_class(self, x: _Decimal, /) -> str: ... + def plus(self, x: _Decimal, /) -> Decimal: ... def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... - def quantize(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... def radix(self) -> Decimal: ... - def remainder(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def remainder_near(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def rotate(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def same_quantum(self, __x: _Decimal, __y: _Decimal) -> bool: ... - def scaleb(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def shift(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def sqrt(self, __x: _Decimal) -> Decimal: ... - def subtract(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def to_eng_string(self, __x: _Decimal) -> str: ... - def to_sci_string(self, __x: _Decimal) -> str: ... - def to_integral_exact(self, __x: _Decimal) -> Decimal: ... - def to_integral_value(self, __x: _Decimal) -> Decimal: ... - def to_integral(self, __x: _Decimal) -> Decimal: ... + def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... + def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def sqrt(self, x: _Decimal, /) -> Decimal: ... + def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def to_eng_string(self, x: _Decimal, /) -> str: ... + def to_sci_string(self, x: _Decimal, /) -> str: ... + def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... + def to_integral_value(self, x: _Decimal, /) -> Decimal: ... + def to_integral(self, x: _Decimal, /) -> Decimal: ... DefaultContext: Context BasicContext: Context diff --git a/mypy/typeshed/stdlib/_heapq.pyi b/mypy/typeshed/stdlib/_heapq.pyi index 28b03a75d4c7..9f731bf91eef 100644 --- a/mypy/typeshed/stdlib/_heapq.pyi +++ b/mypy/typeshed/stdlib/_heapq.pyi @@ -4,8 +4,8 @@ _T = TypeVar("_T") __about__: Final[str] -def heapify(__heap: list[Any]) -> None: ... -def heappop(__heap: list[_T]) -> _T: ... -def heappush(__heap: list[_T], __item: _T) -> None: ... -def heappushpop(__heap: list[_T], __item: _T) -> _T: ... -def heapreplace(__heap: list[_T], __item: _T) -> _T: ... +def heapify(heap: list[Any], /) -> None: ... +def heappop(heap: list[_T], /) -> _T: ... +def heappush(heap: list[_T], item: _T, /) -> None: ... +def heappushpop(heap: list[_T], item: _T, /) -> _T: ... +def heapreplace(heap: list[_T], item: _T, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/_imp.pyi b/mypy/typeshed/stdlib/_imp.pyi index adab2e803efe..de3549a91da5 100644 --- a/mypy/typeshed/stdlib/_imp.pyi +++ b/mypy/typeshed/stdlib/_imp.pyi @@ -7,22 +7,22 @@ from typing import Any check_hash_based_pycs: str def source_hash(key: int, source: ReadableBuffer) -> bytes: ... -def create_builtin(__spec: ModuleSpec) -> types.ModuleType: ... -def create_dynamic(__spec: ModuleSpec, __file: Any = None) -> types.ModuleType: ... +def create_builtin(spec: ModuleSpec, /) -> types.ModuleType: ... +def create_dynamic(spec: ModuleSpec, file: Any = None, /) -> types.ModuleType: ... def acquire_lock() -> None: ... -def exec_builtin(__mod: types.ModuleType) -> int: ... -def exec_dynamic(__mod: types.ModuleType) -> int: ... +def exec_builtin(mod: types.ModuleType, /) -> int: ... +def exec_dynamic(mod: types.ModuleType, /) -> int: ... def extension_suffixes() -> list[str]: ... -def init_frozen(__name: str) -> types.ModuleType: ... -def is_builtin(__name: str) -> int: ... -def is_frozen(__name: str) -> bool: ... -def is_frozen_package(__name: str) -> bool: ... +def init_frozen(name: str, /) -> types.ModuleType: ... +def is_builtin(name: str, /) -> int: ... +def is_frozen(name: str, /) -> bool: ... +def is_frozen_package(name: str, /) -> bool: ... def lock_held() -> bool: ... def release_lock() -> None: ... if sys.version_info >= (3, 11): - def find_frozen(__name: str, *, withdata: bool = False) -> tuple[memoryview | None, bool, str | None] | None: ... - def get_frozen_object(__name: str, __data: ReadableBuffer | None = None) -> types.CodeType: ... + def find_frozen(name: str, /, *, withdata: bool = False) -> tuple[memoryview | None, bool, str | None] | None: ... + def get_frozen_object(name: str, data: ReadableBuffer | None = None, /) -> types.CodeType: ... else: - def get_frozen_object(__name: str) -> types.CodeType: ... + def get_frozen_object(name: str, /) -> types.CodeType: ... diff --git a/mypy/typeshed/stdlib/_locale.pyi b/mypy/typeshed/stdlib/_locale.pyi index d7399f15e1a3..0825e12034f4 100644 --- a/mypy/typeshed/stdlib/_locale.pyi +++ b/mypy/typeshed/stdlib/_locale.pyi @@ -10,14 +10,14 @@ LC_NUMERIC: int LC_ALL: int CHAR_MAX: int -def setlocale(__category: int, __locale: str | None = None) -> str: ... +def setlocale(category: int, locale: str | None = None, /) -> str: ... def localeconv() -> Mapping[str, int | str | list[int]]: ... if sys.version_info >= (3, 11): def getencoding() -> str: ... -def strcoll(__os1: str, __os2: str) -> int: ... -def strxfrm(__string: str) -> str: ... +def strcoll(os1: str, os2: str, /) -> int: ... +def strxfrm(string: str, /) -> str: ... # native gettext functions # https://docs.python.org/3/library/locale.html#access-to-message-catalogs @@ -87,14 +87,14 @@ if sys.platform != "win32": CRNCYSTR: int ALT_DIGITS: int - def nl_langinfo(__key: int) -> str: ... + def nl_langinfo(key: int, /) -> str: ... # This is dependent on `libintl.h` which is a part of `gettext` # system dependency. These functions might be missing. # But, we always say that they are present. - def gettext(__msg: str) -> str: ... - def dgettext(__domain: str | None, __msg: str) -> str: ... - def dcgettext(__domain: str | None, __msg: str, __category: int) -> str: ... - def textdomain(__domain: str | None) -> str: ... - def bindtextdomain(__domain: str, __dir: StrPath | None) -> str: ... - def bind_textdomain_codeset(__domain: str, __codeset: str | None) -> str | None: ... + def gettext(msg: str, /) -> str: ... + def dgettext(domain: str | None, msg: str, /) -> str: ... + def dcgettext(domain: str | None, msg: str, category: int, /) -> str: ... + def textdomain(domain: str | None, /) -> str: ... + def bindtextdomain(domain: str, dir: StrPath | None, /) -> str: ... + def bind_textdomain_codeset(domain: str, codeset: str | None, /) -> str | None: ... diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi index 22239cbfff04..779fda3b67fe 100644 --- a/mypy/typeshed/stdlib/_msi.pyi +++ b/mypy/typeshed/stdlib/_msi.pyi @@ -47,9 +47,9 @@ if sys.platform == "win32": __init__: None # type: ignore[assignment] def UuidCreate() -> str: ... - def FCICreate(__cabname: str, __files: list[str]) -> None: ... - def OpenDatabase(__path: str, __persist: int) -> _Database: ... - def CreateRecord(__count: int) -> _Record: ... + def FCICreate(cabname: str, files: list[str], /) -> None: ... + def OpenDatabase(path: str, persist: int, /) -> _Database: ... + def CreateRecord(count: int, /) -> _Record: ... MSICOLINFO_NAMES: int MSICOLINFO_TYPES: int diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 9b24e086adff..69ee563b5cf4 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -19,16 +19,16 @@ _Ts = TypeVarTuple("_Ts") # the numpy.array comparison dunders return another numpy.array. class _SupportsDunderLT(Protocol): - def __lt__(self, __other: Any) -> Any: ... + def __lt__(self, other: Any, /) -> Any: ... class _SupportsDunderGT(Protocol): - def __gt__(self, __other: Any) -> Any: ... + def __gt__(self, other: Any, /) -> Any: ... class _SupportsDunderLE(Protocol): - def __le__(self, __other: Any) -> Any: ... + def __le__(self, other: Any, /) -> Any: ... class _SupportsDunderGE(Protocol): - def __ge__(self, __other: Any) -> Any: ... + def __ge__(self, other: Any, /) -> Any: ... _SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT @@ -42,56 +42,56 @@ class _SupportsPos(Protocol[_T_co]): def __pos__(self) -> _T_co: ... # All four comparison functions must have the same signature, or we get false-positive errors -def lt(__a: _SupportsComparison, __b: _SupportsComparison) -> Any: ... -def le(__a: _SupportsComparison, __b: _SupportsComparison) -> Any: ... -def eq(__a: object, __b: object) -> Any: ... -def ne(__a: object, __b: object) -> Any: ... -def ge(__a: _SupportsComparison, __b: _SupportsComparison) -> Any: ... -def gt(__a: _SupportsComparison, __b: _SupportsComparison) -> Any: ... -def not_(__a: object) -> bool: ... -def truth(__a: object) -> bool: ... -def is_(__a: object, __b: object) -> bool: ... -def is_not(__a: object, __b: object) -> bool: ... -def abs(__a: SupportsAbs[_T]) -> _T: ... -def add(__a: Any, __b: Any) -> Any: ... -def and_(__a: Any, __b: Any) -> Any: ... -def floordiv(__a: Any, __b: Any) -> Any: ... -def index(__a: SupportsIndex) -> int: ... -def inv(__a: _SupportsInversion[_T_co]) -> _T_co: ... -def invert(__a: _SupportsInversion[_T_co]) -> _T_co: ... -def lshift(__a: Any, __b: Any) -> Any: ... -def mod(__a: Any, __b: Any) -> Any: ... -def mul(__a: Any, __b: Any) -> Any: ... -def matmul(__a: Any, __b: Any) -> Any: ... -def neg(__a: _SupportsNeg[_T_co]) -> _T_co: ... -def or_(__a: Any, __b: Any) -> Any: ... -def pos(__a: _SupportsPos[_T_co]) -> _T_co: ... -def pow(__a: Any, __b: Any) -> Any: ... -def rshift(__a: Any, __b: Any) -> Any: ... -def sub(__a: Any, __b: Any) -> Any: ... -def truediv(__a: Any, __b: Any) -> Any: ... -def xor(__a: Any, __b: Any) -> Any: ... -def concat(__a: Sequence[_T], __b: Sequence[_T]) -> Sequence[_T]: ... -def contains(__a: Container[object], __b: object) -> bool: ... -def countOf(__a: Iterable[object], __b: object) -> int: ... +def lt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... +def le(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... +def eq(a: object, b: object, /) -> Any: ... +def ne(a: object, b: object, /) -> Any: ... +def ge(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... +def gt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... +def not_(a: object, /) -> bool: ... +def truth(a: object, /) -> bool: ... +def is_(a: object, b: object, /) -> bool: ... +def is_not(a: object, b: object, /) -> bool: ... +def abs(a: SupportsAbs[_T], /) -> _T: ... +def add(a: Any, b: Any, /) -> Any: ... +def and_(a: Any, b: Any, /) -> Any: ... +def floordiv(a: Any, b: Any, /) -> Any: ... +def index(a: SupportsIndex, /) -> int: ... +def inv(a: _SupportsInversion[_T_co], /) -> _T_co: ... +def invert(a: _SupportsInversion[_T_co], /) -> _T_co: ... +def lshift(a: Any, b: Any, /) -> Any: ... +def mod(a: Any, b: Any, /) -> Any: ... +def mul(a: Any, b: Any, /) -> Any: ... +def matmul(a: Any, b: Any, /) -> Any: ... +def neg(a: _SupportsNeg[_T_co], /) -> _T_co: ... +def or_(a: Any, b: Any, /) -> Any: ... +def pos(a: _SupportsPos[_T_co], /) -> _T_co: ... +def pow(a: Any, b: Any, /) -> Any: ... +def rshift(a: Any, b: Any, /) -> Any: ... +def sub(a: Any, b: Any, /) -> Any: ... +def truediv(a: Any, b: Any, /) -> Any: ... +def xor(a: Any, b: Any, /) -> Any: ... +def concat(a: Sequence[_T], b: Sequence[_T], /) -> Sequence[_T]: ... +def contains(a: Container[object], b: object, /) -> bool: ... +def countOf(a: Iterable[object], b: object, /) -> int: ... @overload -def delitem(__a: MutableSequence[Any], __b: SupportsIndex) -> None: ... +def delitem(a: MutableSequence[Any], b: SupportsIndex, /) -> None: ... @overload -def delitem(__a: MutableSequence[Any], __b: slice) -> None: ... +def delitem(a: MutableSequence[Any], b: slice, /) -> None: ... @overload -def delitem(__a: MutableMapping[_K, Any], __b: _K) -> None: ... +def delitem(a: MutableMapping[_K, Any], b: _K, /) -> None: ... @overload -def getitem(__a: Sequence[_T], __b: slice) -> Sequence[_T]: ... +def getitem(a: Sequence[_T], b: slice, /) -> Sequence[_T]: ... @overload -def getitem(__a: SupportsGetItem[_K, _V], __b: _K) -> _V: ... -def indexOf(__a: Iterable[_T], __b: _T) -> int: ... +def getitem(a: SupportsGetItem[_K, _V], b: _K, /) -> _V: ... +def indexOf(a: Iterable[_T], b: _T, /) -> int: ... @overload -def setitem(__a: MutableSequence[_T], __b: SupportsIndex, __c: _T) -> None: ... +def setitem(a: MutableSequence[_T], b: SupportsIndex, c: _T, /) -> None: ... @overload -def setitem(__a: MutableSequence[_T], __b: slice, __c: Sequence[_T]) -> None: ... +def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ... @overload -def setitem(__a: MutableMapping[_K, _V], __b: _K, __c: _V) -> None: ... -def length_hint(__obj: object, __default: int = 0) -> int: ... +def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ... +def length_hint(obj: object, default: int = 0, /) -> int: ... @final class attrgetter(Generic[_T_co]): @overload @@ -109,9 +109,9 @@ class attrgetter(Generic[_T_co]): @final class itemgetter(Generic[_T_co]): @overload - def __new__(cls, __item: _T) -> itemgetter[_T]: ... + def __new__(cls, item: _T, /) -> itemgetter[_T]: ... @overload - def __new__(cls, __item1: _T1, __item2: _T2, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... + def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: # TypeVar "_KT_contra@SupportsGetItem" is contravariant # "tuple[int, int]" is incompatible with protocol "SupportsIndex" @@ -123,25 +123,25 @@ class itemgetter(Generic[_T_co]): @final class methodcaller: - def __init__(self, __name: str, *args: Any, **kwargs: Any) -> None: ... + def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... def __call__(self, obj: Any) -> Any: ... -def iadd(__a: Any, __b: Any) -> Any: ... -def iand(__a: Any, __b: Any) -> Any: ... -def iconcat(__a: Any, __b: Any) -> Any: ... -def ifloordiv(__a: Any, __b: Any) -> Any: ... -def ilshift(__a: Any, __b: Any) -> Any: ... -def imod(__a: Any, __b: Any) -> Any: ... -def imul(__a: Any, __b: Any) -> Any: ... -def imatmul(__a: Any, __b: Any) -> Any: ... -def ior(__a: Any, __b: Any) -> Any: ... -def ipow(__a: Any, __b: Any) -> Any: ... -def irshift(__a: Any, __b: Any) -> Any: ... -def isub(__a: Any, __b: Any) -> Any: ... -def itruediv(__a: Any, __b: Any) -> Any: ... -def ixor(__a: Any, __b: Any) -> Any: ... +def iadd(a: Any, b: Any, /) -> Any: ... +def iand(a: Any, b: Any, /) -> Any: ... +def iconcat(a: Any, b: Any, /) -> Any: ... +def ifloordiv(a: Any, b: Any, /) -> Any: ... +def ilshift(a: Any, b: Any, /) -> Any: ... +def imod(a: Any, b: Any, /) -> Any: ... +def imul(a: Any, b: Any, /) -> Any: ... +def imatmul(a: Any, b: Any, /) -> Any: ... +def ior(a: Any, b: Any, /) -> Any: ... +def ipow(a: Any, b: Any, /) -> Any: ... +def irshift(a: Any, b: Any, /) -> Any: ... +def isub(a: Any, b: Any, /) -> Any: ... +def itruediv(a: Any, b: Any, /) -> Any: ... +def ixor(a: Any, b: Any, /) -> Any: ... if sys.version_info >= (3, 11): - def call(__obj: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + def call(obj: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -def _compare_digest(__a: AnyStr, __b: AnyStr) -> bool: ... +def _compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 6c1782433e45..3df56d9a3d03 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -6,27 +6,28 @@ from typing import SupportsIndex if sys.platform != "win32": def cloexec_pipe() -> tuple[int, int]: ... def fork_exec( - __args: Sequence[StrOrBytesPath] | None, - __executable_list: Sequence[bytes], - __close_fds: bool, - __pass_fds: tuple[int, ...], - __cwd: str, - __env: Sequence[bytes] | None, - __p2cread: int, - __p2cwrite: int, - __c2pread: int, - __c2pwrite: int, - __errread: int, - __errwrite: int, - __errpipe_read: int, - __errpipe_write: int, - __restore_signals: int, - __call_setsid: int, - __pgid_to_set: int, - __gid: SupportsIndex | None, - __extra_groups: list[int] | None, - __uid: SupportsIndex | None, - __child_umask: int, - __preexec_fn: Callable[[], None], - __allow_vfork: bool, + args: Sequence[StrOrBytesPath] | None, + executable_list: Sequence[bytes], + close_fds: bool, + pass_fds: tuple[int, ...], + cwd: str, + env: Sequence[bytes] | None, + p2cread: int, + p2cwrite: int, + c2pread: int, + c2pwrite: int, + errread: int, + errwrite: int, + errpipe_read: int, + errpipe_write: int, + restore_signals: int, + call_setsid: int, + pgid_to_set: int, + gid: SupportsIndex | None, + extra_groups: list[int] | None, + uid: SupportsIndex | None, + child_umask: int, + preexec_fn: Callable[[], None], + allow_vfork: bool, + /, ) -> int: ... diff --git a/mypy/typeshed/stdlib/_py_abc.pyi b/mypy/typeshed/stdlib/_py_abc.pyi index cc45c6ad3814..1260717489e4 100644 --- a/mypy/typeshed/stdlib/_py_abc.pyi +++ b/mypy/typeshed/stdlib/_py_abc.pyi @@ -9,6 +9,6 @@ def get_cache_token() -> _CacheToken: ... class ABCMeta(type): def __new__( - __mcls: type[_typeshed.Self], __name: str, __bases: tuple[type[Any], ...], __namespace: dict[str, Any] + mcls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], / ) -> _typeshed.Self: ... def register(cls, subclass: type[_T]) -> type[_T]: ... diff --git a/mypy/typeshed/stdlib/_random.pyi b/mypy/typeshed/stdlib/_random.pyi index 7c5803ede781..4082344ade8e 100644 --- a/mypy/typeshed/stdlib/_random.pyi +++ b/mypy/typeshed/stdlib/_random.pyi @@ -5,8 +5,8 @@ _State: TypeAlias = tuple[int, ...] class Random: def __init__(self, seed: object = ...) -> None: ... - def seed(self, __n: object = None) -> None: ... + def seed(self, n: object = None, /) -> None: ... def getstate(self) -> _State: ... - def setstate(self, __state: _State) -> None: ... + def setstate(self, state: _State, /) -> None: ... def random(self) -> float: ... - def getrandbits(self, __k: int) -> int: ... + def getrandbits(self, k: int, /) -> int: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 6471cae2e72d..2a48349d4f7d 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -696,70 +696,71 @@ class socket: else: def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = ...) -> None: ... - def bind(self, __address: _Address) -> None: ... + def bind(self, address: _Address, /) -> None: ... def close(self) -> None: ... - def connect(self, __address: _Address) -> None: ... - def connect_ex(self, __address: _Address) -> int: ... + def connect(self, address: _Address, /) -> None: ... + def connect_ex(self, address: _Address, /) -> int: ... def detach(self) -> int: ... def fileno(self) -> int: ... def getpeername(self) -> _RetAddress: ... def getsockname(self) -> _RetAddress: ... @overload - def getsockopt(self, __level: int, __optname: int) -> int: ... + def getsockopt(self, level: int, optname: int, /) -> int: ... @overload - def getsockopt(self, __level: int, __optname: int, __buflen: int) -> bytes: ... + def getsockopt(self, level: int, optname: int, buflen: int, /) -> bytes: ... def getblocking(self) -> bool: ... def gettimeout(self) -> float | None: ... if sys.platform == "win32": - def ioctl(self, __control: int, __option: int | tuple[int, int, int] | bool) -> None: ... + def ioctl(self, control: int, option: int | tuple[int, int, int] | bool, /) -> None: ... - def listen(self, __backlog: int = ...) -> None: ... - def recv(self, __bufsize: int, __flags: int = ...) -> bytes: ... - def recvfrom(self, __bufsize: int, __flags: int = ...) -> tuple[bytes, _RetAddress]: ... + def listen(self, backlog: int = ..., /) -> None: ... + def recv(self, bufsize: int, flags: int = ..., /) -> bytes: ... + def recvfrom(self, bufsize: int, flags: int = ..., /) -> tuple[bytes, _RetAddress]: ... if sys.platform != "win32": - def recvmsg(self, __bufsize: int, __ancbufsize: int = ..., __flags: int = ...) -> tuple[bytes, list[_CMSG], int, Any]: ... + def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ... def recvmsg_into( - self, __buffers: Iterable[WriteableBuffer], __ancbufsize: int = ..., __flags: int = ... + self, buffers: Iterable[WriteableBuffer], ancbufsize: int = ..., flags: int = ..., / ) -> tuple[int, list[_CMSG], int, Any]: ... def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ... def recv_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> int: ... - def send(self, __data: ReadableBuffer, __flags: int = ...) -> int: ... - def sendall(self, __data: ReadableBuffer, __flags: int = ...) -> None: ... + def send(self, data: ReadableBuffer, flags: int = ..., /) -> int: ... + def sendall(self, data: ReadableBuffer, flags: int = ..., /) -> None: ... @overload - def sendto(self, __data: ReadableBuffer, __address: _Address) -> int: ... + def sendto(self, data: ReadableBuffer, address: _Address, /) -> int: ... @overload - def sendto(self, __data: ReadableBuffer, __flags: int, __address: _Address) -> int: ... + def sendto(self, data: ReadableBuffer, flags: int, address: _Address, /) -> int: ... if sys.platform != "win32": def sendmsg( self, - __buffers: Iterable[ReadableBuffer], - __ancdata: Iterable[_CMSGArg] = ..., - __flags: int = ..., - __address: _Address | None = ..., + buffers: Iterable[ReadableBuffer], + ancdata: Iterable[_CMSGArg] = ..., + flags: int = ..., + address: _Address | None = ..., + /, ) -> int: ... if sys.platform == "linux": def sendmsg_afalg( self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ... ) -> int: ... - def setblocking(self, __flag: bool) -> None: ... - def settimeout(self, __value: float | None) -> None: ... + def setblocking(self, flag: bool, /) -> None: ... + def settimeout(self, value: float | None, /) -> None: ... @overload - def setsockopt(self, __level: int, __optname: int, __value: int | ReadableBuffer) -> None: ... + def setsockopt(self, level: int, optname: int, value: int | ReadableBuffer, /) -> None: ... @overload - def setsockopt(self, __level: int, __optname: int, __value: None, __optlen: int) -> None: ... + def setsockopt(self, level: int, optname: int, value: None, optlen: int, /) -> None: ... if sys.platform == "win32": - def share(self, __process_id: int) -> bytes: ... + def share(self, process_id: int, /) -> bytes: ... - def shutdown(self, __how: int) -> None: ... + def shutdown(self, how: int, /) -> None: ... SocketType = socket # ===== Functions ===== -def close(__fd: SupportsIndex) -> None: ... -def dup(__fd: SupportsIndex) -> int: ... +def close(fd: SupportsIndex, /) -> None: ... +def dup(fd: SupportsIndex, /) -> int: ... # the 5th tuple item is an address def getaddrinfo( @@ -770,33 +771,33 @@ def getaddrinfo( proto: int = ..., flags: int = ..., ) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... -def gethostbyname(__hostname: str) -> str: ... -def gethostbyname_ex(__hostname: str) -> tuple[str, list[str], list[str]]: ... +def gethostbyname(hostname: str, /) -> str: ... +def gethostbyname_ex(hostname: str, /) -> tuple[str, list[str], list[str]]: ... def gethostname() -> str: ... -def gethostbyaddr(__ip_address: str) -> tuple[str, list[str], list[str]]: ... -def getnameinfo(__sockaddr: tuple[str, int] | tuple[str, int, int, int], __flags: int) -> tuple[str, str]: ... -def getprotobyname(__protocolname: str) -> int: ... -def getservbyname(__servicename: str, __protocolname: str = ...) -> int: ... -def getservbyport(__port: int, __protocolname: str = ...) -> str: ... -def ntohl(__x: int) -> int: ... # param & ret val are 32-bit ints -def ntohs(__x: int) -> int: ... # param & ret val are 16-bit ints -def htonl(__x: int) -> int: ... # param & ret val are 32-bit ints -def htons(__x: int) -> int: ... # param & ret val are 16-bit ints -def inet_aton(__ip_string: str) -> bytes: ... # ret val 4 bytes in length -def inet_ntoa(__packed_ip: ReadableBuffer) -> str: ... -def inet_pton(__address_family: int, __ip_string: str) -> bytes: ... -def inet_ntop(__address_family: int, __packed_ip: ReadableBuffer) -> str: ... +def gethostbyaddr(ip_address: str, /) -> tuple[str, list[str], list[str]]: ... +def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int, /) -> tuple[str, str]: ... +def getprotobyname(protocolname: str, /) -> int: ... +def getservbyname(servicename: str, protocolname: str = ..., /) -> int: ... +def getservbyport(port: int, protocolname: str = ..., /) -> str: ... +def ntohl(x: int, /) -> int: ... # param & ret val are 32-bit ints +def ntohs(x: int, /) -> int: ... # param & ret val are 16-bit ints +def htonl(x: int, /) -> int: ... # param & ret val are 32-bit ints +def htons(x: int, /) -> int: ... # param & ret val are 16-bit ints +def inet_aton(ip_string: str, /) -> bytes: ... # ret val 4 bytes in length +def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ... +def inet_pton(address_family: int, ip_string: str, /) -> bytes: ... +def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ... def getdefaulttimeout() -> float | None: ... -def setdefaulttimeout(__timeout: float | None) -> None: ... +def setdefaulttimeout(timeout: float | None, /) -> None: ... if sys.platform != "win32": - def sethostname(__name: str) -> None: ... - def CMSG_LEN(__length: int) -> int: ... - def CMSG_SPACE(__length: int) -> int: ... - def socketpair(__family: int = ..., __type: int = ..., __proto: int = ...) -> tuple[socket, socket]: ... + def sethostname(name: str, /) -> None: ... + def CMSG_LEN(length: int, /) -> int: ... + def CMSG_SPACE(length: int, /) -> int: ... + def socketpair(family: int = ..., type: int = ..., proto: int = ..., /) -> tuple[socket, socket]: ... def if_nameindex() -> list[tuple[int, str]]: ... -def if_nametoindex(__name: str) -> int: ... -def if_indextoname(__index: int) -> str: ... +def if_nametoindex(name: str, /) -> int: ... +def if_indextoname(index: int, /) -> str: ... CAPI: object diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index e69f9d2359aa..4ea9aa0609e5 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -54,6 +54,6 @@ if sys.version_info >= (3, 12): def daemon_threads_allowed() -> bool: ... class _local: - def __getattribute__(self, __name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... - def __delattr__(self, __name: str) -> None: ... + def __getattribute__(self, name: str, /) -> Any: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... + def __delattr__(self, name: str, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index 67e7e3f696e2..3340df424163 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -21,12 +21,12 @@ class Tcl_Obj: @property def typename(self) -> str: ... __hash__: ClassVar[None] # type: ignore[assignment] - def __eq__(self, __value): ... - def __ge__(self, __value): ... - def __gt__(self, __value): ... - def __le__(self, __value): ... - def __lt__(self, __value): ... - def __ne__(self, __value): ... + def __eq__(self, value, /): ... + def __ge__(self, value, /): ... + def __gt__(self, value, /): ... + def __le__(self, value, /): ... + def __lt__(self, value, /): ... + def __ne__(self, value, /): ... class TclError(Exception): ... @@ -50,39 +50,39 @@ class TclError(Exception): ... @final class TkappType: # Please keep in sync with tkinter.Tk - def adderrorinfo(self, __msg): ... - def call(self, __command: Any, *args: Any) -> Any: ... - def createcommand(self, __name, __func): ... + def adderrorinfo(self, msg, /): ... + def call(self, command: Any, /, *args: Any) -> Any: ... + def createcommand(self, name, func, /): ... if sys.platform != "win32": - def createfilehandler(self, __file, __mask, __func): ... - def deletefilehandler(self, __file): ... + def createfilehandler(self, file, mask, func, /): ... + def deletefilehandler(self, file, /): ... - def createtimerhandler(self, __milliseconds, __func): ... - def deletecommand(self, __name): ... - def dooneevent(self, __flags: int = 0): ... - def eval(self, __script: str) -> str: ... - def evalfile(self, __fileName): ... - def exprboolean(self, __s): ... - def exprdouble(self, __s): ... - def exprlong(self, __s): ... - def exprstring(self, __s): ... - def getboolean(self, __arg): ... - def getdouble(self, __arg): ... - def getint(self, __arg): ... + def createtimerhandler(self, milliseconds, func, /): ... + def deletecommand(self, name, /): ... + def dooneevent(self, flags: int = 0, /): ... + def eval(self, script: str, /) -> str: ... + def evalfile(self, fileName, /): ... + def exprboolean(self, s, /): ... + def exprdouble(self, s, /): ... + def exprlong(self, s, /): ... + def exprstring(self, s, /): ... + def getboolean(self, arg, /): ... + def getdouble(self, arg, /): ... + def getint(self, arg, /): ... def getvar(self, *args, **kwargs): ... def globalgetvar(self, *args, **kwargs): ... def globalsetvar(self, *args, **kwargs): ... def globalunsetvar(self, *args, **kwargs): ... def interpaddr(self): ... def loadtk(self) -> None: ... - def mainloop(self, __threshold: int = 0): ... + def mainloop(self, threshold: int = 0, /): ... def quit(self): ... - def record(self, __script): ... + def record(self, script, /): ... def setvar(self, *ags, **kwargs): ... if sys.version_info < (3, 11): - def split(self, __arg): ... + def split(self, arg, /): ... - def splitlist(self, __arg): ... + def splitlist(self, arg, /): ... def unsetvar(self, *args, **kwargs): ... def wantobjects(self, *args, **kwargs): ... def willdispatch(self): ... @@ -107,14 +107,15 @@ class TkttType: def deletetimerhandler(self): ... def create( - __screenName: str | None = None, - __baseName: str = "", - __className: str = "Tk", - __interactive: bool = False, - __wantobjects: bool = False, - __wantTk: bool = True, - __sync: bool = False, - __use: str | None = None, + screenName: str | None = None, + baseName: str = "", + className: str = "Tk", + interactive: bool = False, + wantobjects: bool = False, + wantTk: bool = True, + sync: bool = False, + use: str | None = None, + /, ): ... def getbusywaitinterval(): ... -def setbusywaitinterval(__new_val): ... +def setbusywaitinterval(new_val, /): ... diff --git a/mypy/typeshed/stdlib/_tracemalloc.pyi b/mypy/typeshed/stdlib/_tracemalloc.pyi index 1b79d9dc5785..b1aeb710233e 100644 --- a/mypy/typeshed/stdlib/_tracemalloc.pyi +++ b/mypy/typeshed/stdlib/_tracemalloc.pyi @@ -2,7 +2,7 @@ import sys from collections.abc import Sequence from tracemalloc import _FrameTuple, _TraceTuple -def _get_object_traceback(__obj: object) -> Sequence[_FrameTuple] | None: ... +def _get_object_traceback(obj: object, /) -> Sequence[_FrameTuple] | None: ... def _get_traces() -> Sequence[_TraceTuple]: ... def clear_traces() -> None: ... def get_traceback_limit() -> int: ... @@ -13,5 +13,5 @@ def is_tracing() -> bool: ... if sys.version_info >= (3, 9): def reset_peak() -> None: ... -def start(__nframe: int = 1) -> None: ... +def start(nframe: int = 1, /) -> None: ... def stop() -> None: ... diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index e9a24bab28a9..9469081ae5d6 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -67,7 +67,7 @@ sentinel: Any # stable class IdentityFunction(Protocol): - def __call__(self, __x: _T) -> _T: ... + def __call__(self, x: _T, /) -> _T: ... # stable class SupportsNext(Protocol[_T_co]): @@ -80,16 +80,16 @@ class SupportsAnext(Protocol[_T_co]): # Comparison protocols class SupportsDunderLT(Protocol[_T_contra]): - def __lt__(self, __other: _T_contra) -> bool: ... + def __lt__(self, other: _T_contra, /) -> bool: ... class SupportsDunderGT(Protocol[_T_contra]): - def __gt__(self, __other: _T_contra) -> bool: ... + def __gt__(self, other: _T_contra, /) -> bool: ... class SupportsDunderLE(Protocol[_T_contra]): - def __le__(self, __other: _T_contra) -> bool: ... + def __le__(self, other: _T_contra, /) -> bool: ... class SupportsDunderGE(Protocol[_T_contra]): - def __ge__(self, __other: _T_contra) -> bool: ... + def __ge__(self, other: _T_contra, /) -> bool: ... class SupportsAllComparisons( SupportsDunderLT[Any], SupportsDunderGT[Any], SupportsDunderLE[Any], SupportsDunderGE[Any], Protocol @@ -101,22 +101,22 @@ SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichC # Dunder protocols class SupportsAdd(Protocol[_T_contra, _T_co]): - def __add__(self, __x: _T_contra) -> _T_co: ... + def __add__(self, x: _T_contra, /) -> _T_co: ... class SupportsRAdd(Protocol[_T_contra, _T_co]): - def __radd__(self, __x: _T_contra) -> _T_co: ... + def __radd__(self, x: _T_contra, /) -> _T_co: ... class SupportsSub(Protocol[_T_contra, _T_co]): - def __sub__(self, __x: _T_contra) -> _T_co: ... + def __sub__(self, x: _T_contra, /) -> _T_co: ... class SupportsRSub(Protocol[_T_contra, _T_co]): - def __rsub__(self, __x: _T_contra) -> _T_co: ... + def __rsub__(self, x: _T_contra, /) -> _T_co: ... class SupportsDivMod(Protocol[_T_contra, _T_co]): - def __divmod__(self, __other: _T_contra) -> _T_co: ... + def __divmod__(self, other: _T_contra, /) -> _T_co: ... class SupportsRDivMod(Protocol[_T_contra, _T_co]): - def __rdivmod__(self, __other: _T_contra) -> _T_co: ... + def __rdivmod__(self, other: _T_contra, /) -> _T_co: ... # This protocol is generic over the iterator type, while Iterable is # generic over the type that is iterated over. @@ -130,7 +130,7 @@ class SupportsAiter(Protocol[_T_co]): class SupportsLenAndGetItem(Protocol[_T_co]): def __len__(self) -> int: ... - def __getitem__(self, __k: int) -> _T_co: ... + def __getitem__(self, k: int, /) -> _T_co: ... class SupportsTrunc(Protocol): def __trunc__(self) -> int: ... @@ -144,17 +144,17 @@ class SupportsItems(Protocol[_KT_co, _VT_co]): # stable class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): def keys(self) -> Iterable[_KT]: ... - def __getitem__(self, __key: _KT) -> _VT_co: ... + def __getitem__(self, key: _KT, /) -> _VT_co: ... # stable class SupportsGetItem(Protocol[_KT_contra, _VT_co]): - def __contains__(self, __x: Any) -> bool: ... - def __getitem__(self, __key: _KT_contra) -> _VT_co: ... + def __contains__(self, x: Any, /) -> bool: ... + def __getitem__(self, key: _KT_contra, /) -> _VT_co: ... # stable class SupportsItemAccess(SupportsGetItem[_KT_contra, _VT], Protocol[_KT_contra, _VT]): - def __setitem__(self, __key: _KT_contra, __value: _VT) -> None: ... - def __delitem__(self, __key: _KT_contra) -> None: ... + def __setitem__(self, key: _KT_contra, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT_contra, /) -> None: ... StrPath: TypeAlias = str | PathLike[str] # stable BytesPath: TypeAlias = bytes | PathLike[bytes] # stable @@ -238,11 +238,11 @@ FileDescriptorOrPath: TypeAlias = int | StrOrBytesPath # stable class SupportsRead(Protocol[_T_co]): - def read(self, __length: int = ...) -> _T_co: ... + def read(self, length: int = ..., /) -> _T_co: ... # stable class SupportsReadline(Protocol[_T_co]): - def readline(self, __length: int = ...) -> _T_co: ... + def readline(self, length: int = ..., /) -> _T_co: ... # stable class SupportsNoArgReadline(Protocol[_T_co]): @@ -250,7 +250,7 @@ class SupportsNoArgReadline(Protocol[_T_co]): # stable class SupportsWrite(Protocol[_T_contra]): - def write(self, __s: _T_contra) -> object: ... + def write(self, s: _T_contra, /) -> object: ... # stable class SupportsFlush(Protocol): @@ -267,17 +267,17 @@ WriteableBuffer: TypeAlias = Buffer ReadableBuffer: TypeAlias = Buffer # stable class SliceableBuffer(Buffer, Protocol): - def __getitem__(self, __slice: slice) -> Sequence[int]: ... + def __getitem__(self, slice: slice, /) -> Sequence[int]: ... class IndexableBuffer(Buffer, Protocol): - def __getitem__(self, __i: int) -> int: ... + def __getitem__(self, i: int, /) -> int: ... class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol): - def __contains__(self, __x: Any) -> bool: ... + def __contains__(self, x: Any, /) -> bool: ... @overload - def __getitem__(self, __slice: slice) -> Sequence[int]: ... + def __getitem__(self, slice: slice, /) -> Sequence[int]: ... @overload - def __getitem__(self, __i: int) -> int: ... + def __getitem__(self, i: int, /) -> int: ... class SizedBuffer(Sized, Buffer, Protocol): ... diff --git a/mypy/typeshed/stdlib/_typeshed/dbapi.pyi b/mypy/typeshed/stdlib/_typeshed/dbapi.pyi index 022e95996bb3..d54fbee57042 100644 --- a/mypy/typeshed/stdlib/_typeshed/dbapi.pyi +++ b/mypy/typeshed/stdlib/_typeshed/dbapi.pyi @@ -23,15 +23,15 @@ class DBAPICursor(Protocol): @property def rowcount(self) -> int: ... # optional: - # def callproc(self, __procname: str, __parameters: Sequence[Any] = ...) -> Sequence[Any]: ... + # def callproc(self, procname: str, parameters: Sequence[Any] = ..., /) -> Sequence[Any]: ... def close(self) -> object: ... - def execute(self, __operation: str, __parameters: Sequence[Any] | Mapping[str, Any] = ...) -> object: ... - def executemany(self, __operation: str, __seq_of_parameters: Sequence[Sequence[Any]]) -> object: ... + def execute(self, operation: str, parameters: Sequence[Any] | Mapping[str, Any] = ..., /) -> object: ... + def executemany(self, operation: str, seq_of_parameters: Sequence[Sequence[Any]], /) -> object: ... def fetchone(self) -> Sequence[Any] | None: ... - def fetchmany(self, __size: int = ...) -> Sequence[Sequence[Any]]: ... + def fetchmany(self, size: int = ..., /) -> Sequence[Sequence[Any]]: ... def fetchall(self) -> Sequence[Sequence[Any]]: ... # optional: # def nextset(self) -> None | Literal[True]: ... arraysize: int - def setinputsizes(self, __sizes: Sequence[DBAPITypeCode | int | None]) -> object: ... - def setoutputsize(self, __size: int, __column: int = ...) -> object: ... + def setinputsizes(self, sizes: Sequence[DBAPITypeCode | int | None], /) -> object: ... + def setoutputsize(self, size: int, column: int = ..., /) -> object: ... diff --git a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi index e8ebf6409e7f..63f204eb889b 100644 --- a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi +++ b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi @@ -11,7 +11,7 @@ from typing import Any, Protocol from typing_extensions import TypeAlias class _Readable(Protocol): - def read(self, __size: int = ...) -> bytes: ... + def read(self, size: int = ..., /) -> bytes: ... # Optional: def close(self) -> object: ... if sys.version_info >= (3, 11): @@ -20,7 +20,7 @@ else: # stable class StartResponse(Protocol): def __call__( - self, __status: str, __headers: list[tuple[str, str]], __exc_info: OptExcInfo | None = ... + self, status: str, headers: list[tuple[str, str]], exc_info: OptExcInfo | None = ..., / ) -> Callable[[bytes], object]: ... WSGIEnvironment: TypeAlias = dict[str, Any] # stable @@ -28,17 +28,17 @@ else: # WSGI input streams per PEP 3333, stable class InputStream(Protocol): - def read(self, __size: int = ...) -> bytes: ... - def readline(self, __size: int = ...) -> bytes: ... - def readlines(self, __hint: int = ...) -> list[bytes]: ... + def read(self, size: int = ..., /) -> bytes: ... + def readline(self, size: int = ..., /) -> bytes: ... + def readlines(self, hint: int = ..., /) -> list[bytes]: ... def __iter__(self) -> Iterator[bytes]: ... # WSGI error streams per PEP 3333, stable class ErrorStream(Protocol): def flush(self) -> object: ... - def write(self, __s: str) -> object: ... - def writelines(self, __seq: list[str]) -> object: ... + def write(self, s: str, /) -> object: ... + def writelines(self, seq: list[str], /) -> object: ... # Optional file wrapper in wsgi.file_wrapper class FileWrapper(Protocol): - def __call__(self, __file: _Readable, __block_size: int = ...) -> Iterable[bytes]: ... + def __call__(self, file: _Readable, block_size: int = ..., /) -> Iterable[bytes]: ... diff --git a/mypy/typeshed/stdlib/_typeshed/xml.pyi b/mypy/typeshed/stdlib/_typeshed/xml.pyi index 46c5fab097c4..6cd1b39af628 100644 --- a/mypy/typeshed/stdlib/_typeshed/xml.pyi +++ b/mypy/typeshed/stdlib/_typeshed/xml.pyi @@ -4,6 +4,6 @@ from typing import Any, Protocol # As defined https://docs.python.org/3/library/xml.dom.html#domimplementation-objects class DOMImplementation(Protocol): - def hasFeature(self, __feature: str, __version: str | None) -> bool: ... - def createDocument(self, __namespaceUri: str, __qualifiedName: str, __doctype: Any | None) -> Any: ... - def createDocumentType(self, __qualifiedName: str, __publicId: str, __systemId: str) -> Any: ... + def hasFeature(self, feature: str, version: str | None, /) -> bool: ... + def createDocument(self, namespaceUri: str, qualifiedName: str, doctype: Any | None, /) -> Any: ... + def createDocumentType(self, qualifiedName: str, publicId: str, systemId: str, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index f939aa815bd2..e395143cc027 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -11,31 +11,31 @@ _T = TypeVar("_T") @final class CallableProxyType(Generic[_C]): # "weakcallableproxy" - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __getattr__(self, attr: str) -> Any: ... __call__: _C @final class ProxyType(Generic[_T]): # "weakproxy" - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __getattr__(self, attr: str) -> Any: ... class ReferenceType(Generic[_T]): __callback__: Callable[[ReferenceType[_T]], Any] - def __new__(cls, __o: _T, __callback: Callable[[ReferenceType[_T]], Any] | None = ...) -> Self: ... + def __new__(cls, o: _T, callback: Callable[[ReferenceType[_T]], Any] | None = ..., /) -> Self: ... def __call__(self) -> _T | None: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... ref = ReferenceType -def getweakrefcount(__object: Any) -> int: ... -def getweakrefs(__object: Any) -> list[Any]: ... +def getweakrefcount(object: Any, /) -> int: ... +def getweakrefs(object: Any, /) -> list[Any]: ... # Return CallableProxyType if object is callable, ProxyType otherwise @overload -def proxy(__object: _C, __callback: Callable[[_C], Any] | None = None) -> CallableProxyType[_C]: ... +def proxy(object: _C, callback: Callable[[_C], Any] | None = None, /) -> CallableProxyType[_C]: ... @overload -def proxy(__object: _T, __callback: Callable[[_T], Any] | None = None) -> Any: ... +def proxy(object: _T, callback: Callable[[_T], Any] | None = None, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index 21ae149e186e..c6fb0484df8e 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -158,7 +158,7 @@ if sys.platform == "win32": ERROR_ACCESS_DENIED: Literal[5] ERROR_PRIVILEGE_NOT_HELD: Literal[1314] - def CloseHandle(__handle: int) -> None: ... + def CloseHandle(handle: int, /) -> None: ... @overload def ConnectNamedPipe(handle: int, overlapped: Literal[True]) -> Overlapped: ... @overload @@ -166,59 +166,63 @@ if sys.platform == "win32": @overload def ConnectNamedPipe(handle: int, overlapped: bool) -> Overlapped | None: ... def CreateFile( - __file_name: str, - __desired_access: int, - __share_mode: int, - __security_attributes: int, - __creation_disposition: int, - __flags_and_attributes: int, - __template_file: int, + file_name: str, + desired_access: int, + share_mode: int, + security_attributes: int, + creation_disposition: int, + flags_and_attributes: int, + template_file: int, + /, ) -> int: ... - def CreateJunction(__src_path: str, __dst_path: str) -> None: ... + def CreateJunction(src_path: str, dst_path: str, /) -> None: ... def CreateNamedPipe( - __name: str, - __open_mode: int, - __pipe_mode: int, - __max_instances: int, - __out_buffer_size: int, - __in_buffer_size: int, - __default_timeout: int, - __security_attributes: int, + name: str, + open_mode: int, + pipe_mode: int, + max_instances: int, + out_buffer_size: int, + in_buffer_size: int, + default_timeout: int, + security_attributes: int, + /, ) -> int: ... - def CreatePipe(__pipe_attrs: Any, __size: int) -> tuple[int, int]: ... + def CreatePipe(pipe_attrs: Any, size: int, /) -> tuple[int, int]: ... def CreateProcess( - __application_name: str | None, - __command_line: str | None, - __proc_attrs: Any, - __thread_attrs: Any, - __inherit_handles: bool, - __creation_flags: int, - __env_mapping: dict[str, str], - __current_directory: str | None, - __startup_info: Any, + application_name: str | None, + command_line: str | None, + proc_attrs: Any, + thread_attrs: Any, + inherit_handles: bool, + creation_flags: int, + env_mapping: dict[str, str], + current_directory: str | None, + startup_info: Any, + /, ) -> tuple[int, int, int, int]: ... def DuplicateHandle( - __source_process_handle: int, - __source_handle: int, - __target_process_handle: int, - __desired_access: int, - __inherit_handle: bool, - __options: int = 0, + source_process_handle: int, + source_handle: int, + target_process_handle: int, + desired_access: int, + inherit_handle: bool, + options: int = 0, + /, ) -> int: ... - def ExitProcess(__ExitCode: int) -> NoReturn: ... + def ExitProcess(ExitCode: int, /) -> NoReturn: ... def GetACP() -> int: ... def GetFileType(handle: int) -> int: ... def GetCurrentProcess() -> int: ... - def GetExitCodeProcess(__process: int) -> int: ... + def GetExitCodeProcess(process: int, /) -> int: ... def GetLastError() -> int: ... - def GetModuleFileName(__module_handle: int) -> str: ... - def GetStdHandle(__std_handle: int) -> int: ... + def GetModuleFileName(module_handle: int, /) -> str: ... + def GetStdHandle(std_handle: int, /) -> int: ... def GetVersion() -> int: ... - def OpenProcess(__desired_access: int, __inherit_handle: bool, __process_id: int) -> int: ... - def PeekNamedPipe(__handle: int, __size: int = 0) -> tuple[int, int] | tuple[bytes, int, int]: ... + def OpenProcess(desired_access: int, inherit_handle: bool, process_id: int, /) -> int: ... + def PeekNamedPipe(handle: int, size: int = 0, /) -> tuple[int, int] | tuple[bytes, int, int]: ... if sys.version_info >= (3, 10): def LCMapStringEx(locale: str, flags: int, src: str) -> str: ... - def UnmapViewOfFile(__address: int) -> None: ... + def UnmapViewOfFile(address: int, /) -> None: ... @overload def ReadFile(handle: int, size: int, overlapped: Literal[True]) -> tuple[Overlapped, int]: ... @@ -227,12 +231,12 @@ if sys.platform == "win32": @overload def ReadFile(handle: int, size: int, overlapped: int | bool) -> tuple[Any, int]: ... def SetNamedPipeHandleState( - __named_pipe: int, __mode: int | None, __max_collection_count: int | None, __collect_data_timeout: int | None + named_pipe: int, mode: int | None, max_collection_count: int | None, collect_data_timeout: int | None, / ) -> None: ... - def TerminateProcess(__handle: int, __exit_code: int) -> None: ... - def WaitForMultipleObjects(__handle_seq: Sequence[int], __wait_flag: bool, __milliseconds: int = 0xFFFFFFFF) -> int: ... - def WaitForSingleObject(__handle: int, __milliseconds: int) -> int: ... - def WaitNamedPipe(__name: str, __timeout: int) -> None: ... + def TerminateProcess(handle: int, exit_code: int, /) -> None: ... + def WaitForMultipleObjects(handle_seq: Sequence[int], wait_flag: bool, milliseconds: int = 0xFFFFFFFF, /) -> int: ... + def WaitForSingleObject(handle: int, milliseconds: int, /) -> int: ... + def WaitNamedPipe(name: str, timeout: int, /) -> None: ... @overload def WriteFile(handle: int, buffer: ReadableBuffer, overlapped: Literal[True]) -> tuple[Overlapped, int]: ... @overload @@ -242,10 +246,10 @@ if sys.platform == "win32": @final class Overlapped: event: int - def GetOverlappedResult(self, __wait: bool) -> tuple[int, int]: ... + def GetOverlappedResult(self, wait: bool, /) -> tuple[int, int]: ... def cancel(self) -> None: ... def getbuffer(self) -> bytes | None: ... if sys.version_info >= (3, 12): def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ... - def NeedCurrentDirectoryForExePath(__exe_name: str) -> bool: ... + def NeedCurrentDirectoryForExePath(exe_name: str, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index e4e7f59b58ca..6bf7821f1c1b 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -15,7 +15,7 @@ class ABCMeta(type): __abstractmethods__: frozenset[str] if sys.version_info >= (3, 11): def __new__( - __mcls: type[_typeshed.Self], __name: str, __bases: tuple[type, ...], __namespace: dict[str, Any], **kwargs: Any + mcls: type[_typeshed.Self], name: str, bases: tuple[type, ...], namespace: dict[str, Any], /, **kwargs: Any ) -> _typeshed.Self: ... else: def __new__( diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index c34aca1f8c20..0701654734a4 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -392,7 +392,7 @@ elif sys.version_info >= (3, 9): class Namespace(_AttributeHolder): def __init__(self, **kwargs: Any) -> None: ... def __getattr__(self, name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... def __contains__(self, key: str) -> bool: ... def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 4b5675d2a76e..1b7de1c7882d 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -24,68 +24,68 @@ class array(MutableSequence[_T]): @property def itemsize(self) -> int: ... @overload - def __init__(self: array[int], __typecode: _IntTypeCode, __initializer: bytes | bytearray | Iterable[int] = ...) -> None: ... + def __init__(self: array[int], typecode: _IntTypeCode, initializer: bytes | bytearray | Iterable[int] = ..., /) -> None: ... @overload def __init__( - self: array[float], __typecode: _FloatTypeCode, __initializer: bytes | bytearray | Iterable[float] = ... + self: array[float], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., / ) -> None: ... @overload def __init__( - self: array[str], __typecode: _UnicodeTypeCode, __initializer: bytes | bytearray | Iterable[str] = ... + self: array[str], typecode: _UnicodeTypeCode, initializer: bytes | bytearray | Iterable[str] = ..., / ) -> None: ... @overload - def __init__(self, __typecode: str, __initializer: Iterable[_T]) -> None: ... + def __init__(self, typecode: str, initializer: Iterable[_T], /) -> None: ... @overload - def __init__(self, __typecode: str, __initializer: bytes | bytearray = ...) -> None: ... - def append(self, __v: _T) -> None: ... + def __init__(self, typecode: str, initializer: bytes | bytearray = ..., /) -> None: ... + def append(self, v: _T, /) -> None: ... def buffer_info(self) -> tuple[int, int]: ... def byteswap(self) -> None: ... - def count(self, __v: _T) -> int: ... - def extend(self, __bb: Iterable[_T]) -> None: ... - def frombytes(self, __buffer: ReadableBuffer) -> None: ... - def fromfile(self, __f: SupportsRead[bytes], __n: int) -> None: ... - def fromlist(self, __list: list[_T]) -> None: ... - def fromunicode(self, __ustr: str) -> None: ... + def count(self, v: _T, /) -> int: ... + def extend(self, bb: Iterable[_T], /) -> None: ... + def frombytes(self, buffer: ReadableBuffer, /) -> None: ... + def fromfile(self, f: SupportsRead[bytes], n: int, /) -> None: ... + def fromlist(self, list: list[_T], /) -> None: ... + def fromunicode(self, ustr: str, /) -> None: ... if sys.version_info >= (3, 10): - def index(self, __v: _T, __start: int = 0, __stop: int = sys.maxsize) -> int: ... + def index(self, v: _T, start: int = 0, stop: int = sys.maxsize, /) -> int: ... else: - def index(self, __v: _T) -> int: ... # type: ignore[override] + def index(self, v: _T, /) -> int: ... # type: ignore[override] - def insert(self, __i: int, __v: _T) -> None: ... - def pop(self, __i: int = -1) -> _T: ... - def remove(self, __v: _T) -> None: ... + def insert(self, i: int, v: _T, /) -> None: ... + def pop(self, i: int = -1, /) -> _T: ... + def remove(self, v: _T, /) -> None: ... def tobytes(self) -> bytes: ... - def tofile(self, __f: SupportsWrite[bytes]) -> None: ... + def tofile(self, f: SupportsWrite[bytes], /) -> None: ... def tolist(self) -> list[_T]: ... def tounicode(self) -> str: ... if sys.version_info < (3, 9): - def fromstring(self, __buffer: str | ReadableBuffer) -> None: ... + def fromstring(self, buffer: str | ReadableBuffer, /) -> None: ... def tostring(self) -> bytes: ... def __len__(self) -> int: ... @overload - def __getitem__(self, __key: SupportsIndex) -> _T: ... + def __getitem__(self, key: SupportsIndex, /) -> _T: ... @overload - def __getitem__(self, __key: slice) -> array[_T]: ... + def __getitem__(self, key: slice, /) -> array[_T]: ... @overload # type: ignore[override] - def __setitem__(self, __key: SupportsIndex, __value: _T) -> None: ... + def __setitem__(self, key: SupportsIndex, value: _T, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: array[_T]) -> None: ... - def __delitem__(self, __key: SupportsIndex | slice) -> None: ... - def __add__(self, __value: array[_T]) -> array[_T]: ... - def __eq__(self, __value: object) -> bool: ... - def __ge__(self, __value: array[_T]) -> bool: ... - def __gt__(self, __value: array[_T]) -> bool: ... - def __iadd__(self, __value: array[_T]) -> Self: ... # type: ignore[override] - def __imul__(self, __value: int) -> Self: ... - def __le__(self, __value: array[_T]) -> bool: ... - def __lt__(self, __value: array[_T]) -> bool: ... - def __mul__(self, __value: int) -> array[_T]: ... - def __rmul__(self, __value: int) -> array[_T]: ... + def __setitem__(self, key: slice, value: array[_T], /) -> None: ... + def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... + def __add__(self, value: array[_T], /) -> array[_T]: ... + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: array[_T], /) -> bool: ... + def __gt__(self, value: array[_T], /) -> bool: ... + def __iadd__(self, value: array[_T], /) -> Self: ... # type: ignore[override] + def __imul__(self, value: int, /) -> Self: ... + def __le__(self, value: array[_T], /) -> bool: ... + def __lt__(self, value: array[_T], /) -> bool: ... + def __mul__(self, value: int, /) -> array[_T]: ... + def __rmul__(self, value: int, /) -> array[_T]: ... def __copy__(self) -> array[_T]: ... - def __deepcopy__(self, __unused: Any) -> array[_T]: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __deepcopy__(self, unused: Any, /) -> array[_T]: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... if sys.version_info >= (3, 12): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 16f5296e2125..95de28c5021e 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -43,9 +43,7 @@ _ProtocolFactory: TypeAlias = Callable[[], BaseProtocol] _SSLContext: TypeAlias = bool | None | ssl.SSLContext class _TaskFactory(Protocol): - def __call__( - self, __loop: AbstractEventLoop, __factory: Coroutine[Any, Any, _T] | Generator[Any, None, _T] - ) -> Future[_T]: ... + def __call__(self, loop: AbstractEventLoop, factory: Coroutine[Any, Any, _T] | Generator[Any, None, _T], /) -> Future[_T]: ... class Handle: _cancelled: bool @@ -577,6 +575,6 @@ else: def get_child_watcher() -> AbstractChildWatcher: ... def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... -def _set_running_loop(__loop: AbstractEventLoop | None) -> None: ... +def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... def _get_running_loop() -> AbstractEventLoop: ... def get_running_loop() -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index 44b9528705a5..560dcc1d5712 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -34,7 +34,7 @@ class Future(Awaitable[_T], Iterable[_T]): def get_loop(self) -> AbstractEventLoop: ... @property def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... - def add_done_callback(self, __fn: Callable[[Self], object], *, context: Context | None = None) -> None: ... + def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... if sys.version_info >= (3, 9): def cancel(self, msg: Any | None = None) -> bool: ... else: @@ -44,9 +44,9 @@ class Future(Awaitable[_T], Iterable[_T]): def done(self) -> bool: ... def result(self) -> _T: ... def exception(self) -> BaseException | None: ... - def remove_done_callback(self, __fn: Callable[[Self], object]) -> int: ... - def set_result(self, __result: _T) -> None: ... - def set_exception(self, __exception: type | BaseException) -> None: ... + def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... + def set_result(self, result: _T, /) -> None: ... + def set_exception(self, exception: type | BaseException, /) -> None: ... def __iter__(self) -> Generator[Any, None, _T]: ... def __await__(self) -> Generator[Any, None, _T]: ... @property diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 028a7571bb79..67291071d512 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -98,81 +98,88 @@ def ensure_future(coro_or_future: Awaitable[_T], *, loop: AbstractEventLoop | No # N.B. Having overlapping overloads is the only way to get acceptable type inference in all edge cases. if sys.version_info >= (3, 10): @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = False) -> Future[tuple[_T1]]: ... # type: ignore[overload-overlap] + def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: Literal[False] = False) -> Future[tuple[_T1]]: ... # type: ignore[overload-overlap] @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: Literal[False] = False + coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], /, *, return_exceptions: Literal[False] = False ) -> Future[tuple[_T1, _T2]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + /, *, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + /, *, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + /, *, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], - __coro_or_future6: _FutureLike[_T6], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + coro_or_future6: _FutureLike[_T6], + /, *, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[overload-overlap] @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[overload-overlap] + def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[overload-overlap] @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: bool + coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], /, *, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + /, *, return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + /, *, return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + /, *, return_exceptions: bool, ) -> Future[ @@ -180,12 +187,13 @@ if sys.version_info >= (3, 10): ]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], - __coro_or_future6: _FutureLike[_T6], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + coro_or_future6: _FutureLike[_T6], + /, *, return_exceptions: bool, ) -> Future[ @@ -204,54 +212,59 @@ if sys.version_info >= (3, 10): else: @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False + coro_or_future1: _FutureLike[_T1], /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False ) -> Future[tuple[_T1]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False, ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], - __coro_or_future6: _FutureLike[_T6], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + coro_or_future6: _FutureLike[_T6], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: Literal[False] = False, @@ -262,43 +275,47 @@ else: ) -> Future[list[_T]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = None, return_exceptions: bool + coro_or_future1: _FutureLike[_T1], /, *, loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload def gather( # type: ignore[overload-overlap] - __coro_or_future1: _FutureLike[_T1], - __coro_or_future2: _FutureLike[_T2], - __coro_or_future3: _FutureLike[_T3], - __coro_or_future4: _FutureLike[_T4], - __coro_or_future5: _FutureLike[_T5], - __coro_or_future6: _FutureLike[_T6], + coro_or_future1: _FutureLike[_T1], + coro_or_future2: _FutureLike[_T2], + coro_or_future3: _FutureLike[_T3], + coro_or_future4: _FutureLike[_T4], + coro_or_future5: _FutureLike[_T5], + coro_or_future6: _FutureLike[_T6], + /, *, loop: AbstractEventLoop | None = None, return_exceptions: bool, @@ -411,7 +428,7 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... def get_name(self) -> str: ... - def set_name(self, __value: object) -> None: ... + def set_name(self, value: object, /) -> None: ... if sys.version_info >= (3, 12): def get_context(self) -> Context: ... @@ -446,7 +463,8 @@ if sys.version_info >= (3, 12): class _CustomTaskConstructor(Protocol[_TaskT_co]): def __call__( self, - __coro: _TaskCompatibleCoro[Any], + coro: _TaskCompatibleCoro[Any], + /, *, loop: AbstractEventLoop, name: str | None, diff --git a/mypy/typeshed/stdlib/asyncio/threads.pyi b/mypy/typeshed/stdlib/asyncio/threads.pyi index 88c4fddcaa3f..799efd25fea4 100644 --- a/mypy/typeshed/stdlib/asyncio/threads.pyi +++ b/mypy/typeshed/stdlib/asyncio/threads.pyi @@ -6,4 +6,4 @@ __all__ = ("to_thread",) _P = ParamSpec("_P") _R = TypeVar("_R") -async def to_thread(__func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... +async def to_thread(func: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... diff --git a/mypy/typeshed/stdlib/asyncio/trsock.pyi b/mypy/typeshed/stdlib/asyncio/trsock.pyi index 742216a84ccd..e74cf6fd4e05 100644 --- a/mypy/typeshed/stdlib/asyncio/trsock.pyi +++ b/mypy/typeshed/stdlib/asyncio/trsock.pyi @@ -51,7 +51,7 @@ class TransportSocket: else: def ioctl(self, control: int, option: int | tuple[int, int, int] | bool) -> NoReturn: ... - def listen(self, __backlog: int = ...) -> None: ... + def listen(self, backlog: int = ..., /) -> None: ... def makefile(self) -> BinaryIO: ... def sendfile(self, file: BinaryIO, offset: int = ..., count: int | None = ...) -> int: ... def close(self) -> None: ... @@ -66,11 +66,7 @@ class TransportSocket: ) -> NoReturn: ... def sendmsg( - self, - __buffers: Iterable[ReadableBuffer], - __ancdata: Iterable[_CMSG] = ..., - __flags: int = ..., - __address: _Address = ..., + self, buffers: Iterable[ReadableBuffer], ancdata: Iterable[_CMSG] = ..., flags: int = ..., address: _Address = ..., / ) -> int: ... @overload def sendto(self, data: ReadableBuffer, address: _Address) -> int: ... @@ -87,9 +83,9 @@ class TransportSocket: def recv_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> int: ... def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ... def recvmsg_into( - self, __buffers: Iterable[_WriteBuffer], __ancbufsize: int = ..., __flags: int = ... + self, buffers: Iterable[_WriteBuffer], ancbufsize: int = ..., flags: int = ..., / ) -> tuple[int, list[_CMSG], int, Any]: ... - def recvmsg(self, __bufsize: int, __ancbufsize: int = ..., __flags: int = ...) -> tuple[bytes, list[_CMSG], int, Any]: ... + def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ... def recvfrom(self, bufsize: int, flags: int = ...) -> tuple[bytes, _RetAddress]: ... def recv(self, bufsize: int, flags: int = ...) -> bytes: ... def __enter__(self) -> socket.socket: ... diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index 2fbc0a4e6049..e9274b853290 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -17,7 +17,9 @@ if sys.version_info >= (3, 12): @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") class AbstractChildWatcher: @abstractmethod - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod @@ -36,7 +38,9 @@ if sys.version_info >= (3, 12): else: class AbstractChildWatcher: @abstractmethod - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod @@ -87,27 +91,35 @@ if sys.platform != "win32": class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") class FastChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... else: class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... class FastChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... @@ -135,7 +147,9 @@ if sys.platform != "win32": def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... @@ -147,7 +161,9 @@ if sys.platform != "win32": def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... @@ -159,7 +175,9 @@ if sys.platform != "win32": self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... def __del__(self) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... @@ -172,5 +190,7 @@ if sys.platform != "win32": def is_active(self) -> bool: ... def close(self) -> None: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - def add_child_handler(self, pid: int, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/audioop.pyi b/mypy/typeshed/stdlib/audioop.pyi index b5934516e40f..830d6f83a273 100644 --- a/mypy/typeshed/stdlib/audioop.pyi +++ b/mypy/typeshed/stdlib/audioop.pyi @@ -5,38 +5,39 @@ _RatecvState: TypeAlias = tuple[int, tuple[tuple[int, int], ...]] class error(Exception): ... -def add(__fragment1: bytes, __fragment2: bytes, __width: int) -> bytes: ... -def adpcm2lin(__fragment: bytes, __width: int, __state: _AdpcmState | None) -> tuple[bytes, _AdpcmState]: ... -def alaw2lin(__fragment: bytes, __width: int) -> bytes: ... -def avg(__fragment: bytes, __width: int) -> int: ... -def avgpp(__fragment: bytes, __width: int) -> int: ... -def bias(__fragment: bytes, __width: int, __bias: int) -> bytes: ... -def byteswap(__fragment: bytes, __width: int) -> bytes: ... -def cross(__fragment: bytes, __width: int) -> int: ... -def findfactor(__fragment: bytes, __reference: bytes) -> float: ... -def findfit(__fragment: bytes, __reference: bytes) -> tuple[int, float]: ... -def findmax(__fragment: bytes, __length: int) -> int: ... -def getsample(__fragment: bytes, __width: int, __index: int) -> int: ... -def lin2adpcm(__fragment: bytes, __width: int, __state: _AdpcmState | None) -> tuple[bytes, _AdpcmState]: ... -def lin2alaw(__fragment: bytes, __width: int) -> bytes: ... -def lin2lin(__fragment: bytes, __width: int, __newwidth: int) -> bytes: ... -def lin2ulaw(__fragment: bytes, __width: int) -> bytes: ... -def max(__fragment: bytes, __width: int) -> int: ... -def maxpp(__fragment: bytes, __width: int) -> int: ... -def minmax(__fragment: bytes, __width: int) -> tuple[int, int]: ... -def mul(__fragment: bytes, __width: int, __factor: float) -> bytes: ... +def add(fragment1: bytes, fragment2: bytes, width: int, /) -> bytes: ... +def adpcm2lin(fragment: bytes, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... +def alaw2lin(fragment: bytes, width: int, /) -> bytes: ... +def avg(fragment: bytes, width: int, /) -> int: ... +def avgpp(fragment: bytes, width: int, /) -> int: ... +def bias(fragment: bytes, width: int, bias: int, /) -> bytes: ... +def byteswap(fragment: bytes, width: int, /) -> bytes: ... +def cross(fragment: bytes, width: int, /) -> int: ... +def findfactor(fragment: bytes, reference: bytes, /) -> float: ... +def findfit(fragment: bytes, reference: bytes, /) -> tuple[int, float]: ... +def findmax(fragment: bytes, length: int, /) -> int: ... +def getsample(fragment: bytes, width: int, index: int, /) -> int: ... +def lin2adpcm(fragment: bytes, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... +def lin2alaw(fragment: bytes, width: int, /) -> bytes: ... +def lin2lin(fragment: bytes, width: int, newwidth: int, /) -> bytes: ... +def lin2ulaw(fragment: bytes, width: int, /) -> bytes: ... +def max(fragment: bytes, width: int, /) -> int: ... +def maxpp(fragment: bytes, width: int, /) -> int: ... +def minmax(fragment: bytes, width: int, /) -> tuple[int, int]: ... +def mul(fragment: bytes, width: int, factor: float, /) -> bytes: ... def ratecv( - __fragment: bytes, - __width: int, - __nchannels: int, - __inrate: int, - __outrate: int, - __state: _RatecvState | None, - __weightA: int = 1, - __weightB: int = 0, + fragment: bytes, + width: int, + nchannels: int, + inrate: int, + outrate: int, + state: _RatecvState | None, + weightA: int = 1, + weightB: int = 0, + /, ) -> tuple[bytes, _RatecvState]: ... -def reverse(__fragment: bytes, __width: int) -> bytes: ... -def rms(__fragment: bytes, __width: int) -> int: ... -def tomono(__fragment: bytes, __width: int, __lfactor: float, __rfactor: float) -> bytes: ... -def tostereo(__fragment: bytes, __width: int, __lfactor: float, __rfactor: float) -> bytes: ... -def ulaw2lin(__fragment: bytes, __width: int) -> bytes: ... +def reverse(fragment: bytes, width: int, /) -> bytes: ... +def rms(fragment: bytes, width: int, /) -> int: ... +def tomono(fragment: bytes, width: int, lfactor: float, rfactor: float, /) -> bytes: ... +def tostereo(fragment: bytes, width: int, lfactor: float, rfactor: float, /) -> bytes: ... +def ulaw2lin(fragment: bytes, width: int, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index 43012a253164..a72e986728a7 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -67,7 +67,7 @@ class Bdb: ) -> None: ... def runeval(self, expr: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> None: ... def runctx(self, cmd: str | CodeType, globals: dict[str, Any] | None, locals: Mapping[str, Any] | None) -> None: ... - def runcall(self, __func: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> _T | None: ... + def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> _T | None: ... class Breakpoint: next: int diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi index d48507b90694..32e018c653cb 100644 --- a/mypy/typeshed/stdlib/binascii.pyi +++ b/mypy/typeshed/stdlib/binascii.pyi @@ -6,31 +6,31 @@ from typing_extensions import TypeAlias # or ASCII-only strings. _AsciiBuffer: TypeAlias = str | ReadableBuffer -def a2b_uu(__data: _AsciiBuffer) -> bytes: ... -def b2a_uu(__data: ReadableBuffer, *, backtick: bool = False) -> bytes: ... +def a2b_uu(data: _AsciiBuffer, /) -> bytes: ... +def b2a_uu(data: ReadableBuffer, /, *, backtick: bool = False) -> bytes: ... if sys.version_info >= (3, 11): - def a2b_base64(__data: _AsciiBuffer, *, strict_mode: bool = False) -> bytes: ... + def a2b_base64(data: _AsciiBuffer, /, *, strict_mode: bool = False) -> bytes: ... else: - def a2b_base64(__data: _AsciiBuffer) -> bytes: ... + def a2b_base64(data: _AsciiBuffer, /) -> bytes: ... -def b2a_base64(__data: ReadableBuffer, *, newline: bool = True) -> bytes: ... +def b2a_base64(data: ReadableBuffer, /, *, newline: bool = True) -> bytes: ... def a2b_qp(data: _AsciiBuffer, header: bool = False) -> bytes: ... def b2a_qp(data: ReadableBuffer, quotetabs: bool = False, istext: bool = True, header: bool = False) -> bytes: ... if sys.version_info < (3, 11): - def a2b_hqx(__data: _AsciiBuffer) -> bytes: ... - def rledecode_hqx(__data: ReadableBuffer) -> bytes: ... - def rlecode_hqx(__data: ReadableBuffer) -> bytes: ... - def b2a_hqx(__data: ReadableBuffer) -> bytes: ... + def a2b_hqx(data: _AsciiBuffer, /) -> bytes: ... + def rledecode_hqx(data: ReadableBuffer, /) -> bytes: ... + def rlecode_hqx(data: ReadableBuffer, /) -> bytes: ... + def b2a_hqx(data: ReadableBuffer, /) -> bytes: ... -def crc_hqx(__data: ReadableBuffer, __crc: int) -> int: ... -def crc32(__data: ReadableBuffer, __crc: int = 0) -> int: ... +def crc_hqx(data: ReadableBuffer, crc: int, /) -> int: ... +def crc32(data: ReadableBuffer, crc: int = 0, /) -> int: ... def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... -def a2b_hex(__hexstr: _AsciiBuffer) -> bytes: ... -def unhexlify(__hexstr: _AsciiBuffer) -> bytes: ... +def a2b_hex(hexstr: _AsciiBuffer, /) -> bytes: ... +def unhexlify(hexstr: _AsciiBuffer, /) -> bytes: ... class Error(ValueError): ... class Incomplete(Exception): ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 02e128234dc1..b4765b26c8e5 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -61,6 +61,7 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, + LiteralString, ParamSpec, Self, TypeAlias, @@ -97,45 +98,44 @@ class object: __annotations__: dict[str, Any] @property def __class__(self) -> type[Self]: ... - # Ignore errors about type mismatch between property getter and setter @__class__.setter - def __class__(self, __type: type[object]) -> None: ... # noqa: F811 + def __class__(self, type: type[object], /) -> None: ... def __init__(self) -> None: ... def __new__(cls) -> Self: ... # N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers. # Overriding them in subclasses has different semantics, even if the override has an identical signature. - def __setattr__(self, __name: str, __value: Any) -> None: ... - def __delattr__(self, __name: str) -> None: ... - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... + def __delattr__(self, name: str, /) -> None: ... + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... def __str__(self) -> str: ... # noqa: Y029 def __repr__(self) -> str: ... # noqa: Y029 def __hash__(self) -> int: ... - def __format__(self, __format_spec: str) -> str: ... - def __getattribute__(self, __name: str) -> Any: ... + def __format__(self, format_spec: str, /) -> str: ... + def __getattribute__(self, name: str, /) -> Any: ... def __sizeof__(self) -> int: ... # return type of pickle methods is rather hard to express in the current type system # see #6661 and https://docs.python.org/3/library/pickle.html#object.__reduce__ def __reduce__(self) -> str | tuple[Any, ...]: ... - def __reduce_ex__(self, __protocol: SupportsIndex) -> str | tuple[Any, ...]: ... + def __reduce_ex__(self, protocol: SupportsIndex, /) -> str | tuple[Any, ...]: ... if sys.version_info >= (3, 11): def __getstate__(self) -> object: ... def __dir__(self) -> Iterable[str]: ... def __init_subclass__(cls) -> None: ... @classmethod - def __subclasshook__(cls, __subclass: type) -> bool: ... + def __subclasshook__(cls, subclass: type, /) -> bool: ... class staticmethod(Generic[_P, _R_co]): @property def __func__(self) -> Callable[_P, _R_co]: ... @property def __isabstractmethod__(self) -> bool: ... - def __init__(self, __f: Callable[_P, _R_co]) -> None: ... + def __init__(self, f: Callable[_P, _R_co], /) -> None: ... @overload - def __get__(self, __instance: None, __owner: type) -> Callable[_P, _R_co]: ... + def __get__(self, instance: None, owner: type, /) -> Callable[_P, _R_co]: ... @overload - def __get__(self, __instance: _T, __owner: type[_T] | None = None) -> Callable[_P, _R_co]: ... + def __get__(self, instance: _T, owner: type[_T] | None = None, /) -> Callable[_P, _R_co]: ... if sys.version_info >= (3, 10): __name__: str __qualname__: str @@ -148,11 +148,11 @@ class classmethod(Generic[_T, _P, _R_co]): def __func__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... @property def __isabstractmethod__(self) -> bool: ... - def __init__(self, __f: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... + def __init__(self, f: Callable[Concatenate[type[_T], _P], _R_co], /) -> None: ... @overload - def __get__(self, __instance: _T, __owner: type[_T] | None = None) -> Callable[_P, _R_co]: ... + def __get__(self, instance: _T, owner: type[_T] | None = None, /) -> Callable[_P, _R_co]: ... @overload - def __get__(self, __instance: None, __owner: type[_T]) -> Callable[_P, _R_co]: ... + def __get__(self, instance: None, owner: type[_T], /) -> Callable[_P, _R_co]: ... if sys.version_info >= (3, 10): __name__: str __qualname__: str @@ -184,35 +184,35 @@ class type: @property def __weakrefoffset__(self) -> int: ... @overload - def __init__(self, __o: object) -> None: ... + def __init__(self, o: object, /) -> None: ... @overload - def __init__(self, __name: str, __bases: tuple[type, ...], __dict: dict[str, Any], **kwds: Any) -> None: ... + def __init__(self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None: ... @overload - def __new__(cls, __o: object) -> type: ... + def __new__(cls, o: object, /) -> type: ... @overload def __new__( - cls: type[_typeshed.Self], __name: str, __bases: tuple[type, ...], __namespace: dict[str, Any], **kwds: Any + cls: type[_typeshed.Self], name: str, bases: tuple[type, ...], namespace: dict[str, Any], /, **kwds: Any ) -> _typeshed.Self: ... def __call__(self, *args: Any, **kwds: Any) -> Any: ... def __subclasses__(self: _typeshed.Self) -> list[_typeshed.Self]: ... # Note: the documentation doesn't specify what the return type is, the standard # implementation seems to be returning a list. def mro(self) -> list[type]: ... - def __instancecheck__(self, __instance: Any) -> bool: ... - def __subclasscheck__(self, __subclass: type) -> bool: ... + def __instancecheck__(self, instance: Any, /) -> bool: ... + def __subclasscheck__(self, subclass: type, /) -> bool: ... @classmethod - def __prepare__(metacls, __name: str, __bases: tuple[type, ...], **kwds: Any) -> MutableMapping[str, object]: ... + def __prepare__(metacls, name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]: ... if sys.version_info >= (3, 10): - def __or__(self, __value: Any) -> types.UnionType: ... - def __ror__(self, __value: Any) -> types.UnionType: ... + def __or__(self, value: Any, /) -> types.UnionType: ... + def __ror__(self, value: Any, /) -> types.UnionType: ... if sys.version_info >= (3, 12): __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] class super: @overload - def __init__(self, __t: Any, __obj: Any) -> None: ... + def __init__(self, t: Any, obj: Any, /) -> None: ... @overload - def __init__(self, __t: Any) -> None: ... + def __init__(self, t: Any, /) -> None: ... @overload def __init__(self) -> None: ... @@ -222,9 +222,9 @@ _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0] # noqa: Y026 class int: @overload - def __new__(cls, __x: ConvertibleToInt = ...) -> Self: ... + def __new__(cls, x: ConvertibleToInt = ..., /) -> Self: ... @overload - def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> Self: ... + def __new__(cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self: ... def as_integer_ratio(self) -> tuple[int, Literal[1]]: ... @property def real(self) -> int: ... @@ -265,59 +265,59 @@ class int: if sys.version_info >= (3, 12): def is_integer(self) -> Literal[True]: ... - def __add__(self, __value: int) -> int: ... - def __sub__(self, __value: int) -> int: ... - def __mul__(self, __value: int) -> int: ... - def __floordiv__(self, __value: int) -> int: ... - def __truediv__(self, __value: int) -> float: ... - def __mod__(self, __value: int) -> int: ... - def __divmod__(self, __value: int) -> tuple[int, int]: ... - def __radd__(self, __value: int) -> int: ... - def __rsub__(self, __value: int) -> int: ... - def __rmul__(self, __value: int) -> int: ... - def __rfloordiv__(self, __value: int) -> int: ... - def __rtruediv__(self, __value: int) -> float: ... - def __rmod__(self, __value: int) -> int: ... - def __rdivmod__(self, __value: int) -> tuple[int, int]: ... + def __add__(self, value: int, /) -> int: ... + def __sub__(self, value: int, /) -> int: ... + def __mul__(self, value: int, /) -> int: ... + def __floordiv__(self, value: int, /) -> int: ... + def __truediv__(self, value: int, /) -> float: ... + def __mod__(self, value: int, /) -> int: ... + def __divmod__(self, value: int, /) -> tuple[int, int]: ... + def __radd__(self, value: int, /) -> int: ... + def __rsub__(self, value: int, /) -> int: ... + def __rmul__(self, value: int, /) -> int: ... + def __rfloordiv__(self, value: int, /) -> int: ... + def __rtruediv__(self, value: int, /) -> float: ... + def __rmod__(self, value: int, /) -> int: ... + def __rdivmod__(self, value: int, /) -> tuple[int, int]: ... @overload - def __pow__(self, __x: Literal[0]) -> Literal[1]: ... + def __pow__(self, x: Literal[0], /) -> Literal[1]: ... @overload - def __pow__(self, __value: Literal[0], __mod: None) -> Literal[1]: ... + def __pow__(self, value: Literal[0], mod: None, /) -> Literal[1]: ... @overload - def __pow__(self, __value: _PositiveInteger, __mod: None = None) -> int: ... + def __pow__(self, value: _PositiveInteger, mod: None = None, /) -> int: ... @overload - def __pow__(self, __value: _NegativeInteger, __mod: None = None) -> float: ... + def __pow__(self, value: _NegativeInteger, mod: None = None, /) -> float: ... # positive __value -> int; negative __value -> float # return type must be Any as `int | float` causes too many false-positive errors @overload - def __pow__(self, __value: int, __mod: None = None) -> Any: ... - @overload - def __pow__(self, __value: int, __mod: int) -> int: ... - def __rpow__(self, __value: int, __mod: int | None = None) -> Any: ... - def __and__(self, __value: int) -> int: ... - def __or__(self, __value: int) -> int: ... - def __xor__(self, __value: int) -> int: ... - def __lshift__(self, __value: int) -> int: ... - def __rshift__(self, __value: int) -> int: ... - def __rand__(self, __value: int) -> int: ... - def __ror__(self, __value: int) -> int: ... - def __rxor__(self, __value: int) -> int: ... - def __rlshift__(self, __value: int) -> int: ... - def __rrshift__(self, __value: int) -> int: ... + def __pow__(self, value: int, mod: None = None, /) -> Any: ... + @overload + def __pow__(self, value: int, mod: int, /) -> int: ... + def __rpow__(self, value: int, mod: int | None = None, /) -> Any: ... + def __and__(self, value: int, /) -> int: ... + def __or__(self, value: int, /) -> int: ... + def __xor__(self, value: int, /) -> int: ... + def __lshift__(self, value: int, /) -> int: ... + def __rshift__(self, value: int, /) -> int: ... + def __rand__(self, value: int, /) -> int: ... + def __ror__(self, value: int, /) -> int: ... + def __rxor__(self, value: int, /) -> int: ... + def __rlshift__(self, value: int, /) -> int: ... + def __rrshift__(self, value: int, /) -> int: ... def __neg__(self) -> int: ... def __pos__(self) -> int: ... def __invert__(self) -> int: ... def __trunc__(self) -> int: ... def __ceil__(self) -> int: ... def __floor__(self) -> int: ... - def __round__(self, __ndigits: SupportsIndex = ...) -> int: ... + def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ... def __getnewargs__(self) -> tuple[int]: ... - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... - def __lt__(self, __value: int) -> bool: ... - def __le__(self, __value: int) -> bool: ... - def __gt__(self, __value: int) -> bool: ... - def __ge__(self, __value: int) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __lt__(self, value: int, /) -> bool: ... + def __le__(self, value: int, /) -> bool: ... + def __gt__(self, value: int, /) -> bool: ... + def __ge__(self, value: int, /) -> bool: ... def __float__(self) -> float: ... def __int__(self) -> int: ... def __abs__(self) -> int: ... @@ -326,44 +326,44 @@ class int: def __index__(self) -> int: ... class float: - def __new__(cls, __x: ConvertibleToFloat = ...) -> Self: ... + def __new__(cls, x: ConvertibleToFloat = ..., /) -> Self: ... def as_integer_ratio(self) -> tuple[int, int]: ... def hex(self) -> str: ... def is_integer(self) -> bool: ... @classmethod - def fromhex(cls, __string: str) -> Self: ... + def fromhex(cls, string: str, /) -> Self: ... @property def real(self) -> float: ... @property def imag(self) -> float: ... def conjugate(self) -> float: ... - def __add__(self, __value: float) -> float: ... - def __sub__(self, __value: float) -> float: ... - def __mul__(self, __value: float) -> float: ... - def __floordiv__(self, __value: float) -> float: ... - def __truediv__(self, __value: float) -> float: ... - def __mod__(self, __value: float) -> float: ... - def __divmod__(self, __value: float) -> tuple[float, float]: ... - @overload - def __pow__(self, __value: int, __mod: None = None) -> float: ... + def __add__(self, value: float, /) -> float: ... + def __sub__(self, value: float, /) -> float: ... + def __mul__(self, value: float, /) -> float: ... + def __floordiv__(self, value: float, /) -> float: ... + def __truediv__(self, value: float, /) -> float: ... + def __mod__(self, value: float, /) -> float: ... + def __divmod__(self, value: float, /) -> tuple[float, float]: ... + @overload + def __pow__(self, value: int, mod: None = None, /) -> float: ... # positive __value -> float; negative __value -> complex # return type must be Any as `float | complex` causes too many false-positive errors @overload - def __pow__(self, __value: float, __mod: None = None) -> Any: ... - def __radd__(self, __value: float) -> float: ... - def __rsub__(self, __value: float) -> float: ... - def __rmul__(self, __value: float) -> float: ... - def __rfloordiv__(self, __value: float) -> float: ... - def __rtruediv__(self, __value: float) -> float: ... - def __rmod__(self, __value: float) -> float: ... - def __rdivmod__(self, __value: float) -> tuple[float, float]: ... + def __pow__(self, value: float, mod: None = None, /) -> Any: ... + def __radd__(self, value: float, /) -> float: ... + def __rsub__(self, value: float, /) -> float: ... + def __rmul__(self, value: float, /) -> float: ... + def __rfloordiv__(self, value: float, /) -> float: ... + def __rtruediv__(self, value: float, /) -> float: ... + def __rmod__(self, value: float, /) -> float: ... + def __rdivmod__(self, value: float, /) -> tuple[float, float]: ... @overload - def __rpow__(self, __value: _PositiveInteger, __mod: None = None) -> float: ... + def __rpow__(self, value: _PositiveInteger, mod: None = None, /) -> float: ... @overload - def __rpow__(self, __value: _NegativeInteger, __mod: None = None) -> complex: ... + def __rpow__(self, value: _NegativeInteger, mod: None = None, /) -> complex: ... # Returning `complex` for the general case gives too many false-positive errors. @overload - def __rpow__(self, __value: float, __mod: None = None) -> Any: ... + def __rpow__(self, value: float, mod: None = None, /) -> Any: ... def __getnewargs__(self) -> tuple[float]: ... def __trunc__(self) -> int: ... if sys.version_info >= (3, 9): @@ -371,15 +371,15 @@ class float: def __floor__(self) -> int: ... @overload - def __round__(self, __ndigits: None = None) -> int: ... + def __round__(self, ndigits: None = None, /) -> int: ... @overload - def __round__(self, __ndigits: SupportsIndex) -> float: ... - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... - def __lt__(self, __value: float) -> bool: ... - def __le__(self, __value: float) -> bool: ... - def __gt__(self, __value: float) -> bool: ... - def __ge__(self, __value: float) -> bool: ... + def __round__(self, ndigits: SupportsIndex, /) -> float: ... + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __lt__(self, value: float, /) -> bool: ... + def __le__(self, value: float, /) -> bool: ... + def __gt__(self, value: float, /) -> bool: ... + def __ge__(self, value: float, /) -> bool: ... def __neg__(self) -> float: ... def __pos__(self) -> float: ... def __int__(self) -> int: ... @@ -403,18 +403,18 @@ class complex: @property def imag(self) -> float: ... def conjugate(self) -> complex: ... - def __add__(self, __value: complex) -> complex: ... - def __sub__(self, __value: complex) -> complex: ... - def __mul__(self, __value: complex) -> complex: ... - def __pow__(self, __value: complex, __mod: None = None) -> complex: ... - def __truediv__(self, __value: complex) -> complex: ... - def __radd__(self, __value: complex) -> complex: ... - def __rsub__(self, __value: complex) -> complex: ... - def __rmul__(self, __value: complex) -> complex: ... - def __rpow__(self, __value: complex, __mod: None = None) -> complex: ... - def __rtruediv__(self, __value: complex) -> complex: ... - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __add__(self, value: complex, /) -> complex: ... + def __sub__(self, value: complex, /) -> complex: ... + def __mul__(self, value: complex, /) -> complex: ... + def __pow__(self, value: complex, mod: None = None, /) -> complex: ... + def __truediv__(self, value: complex, /) -> complex: ... + def __radd__(self, value: complex, /) -> complex: ... + def __rsub__(self, value: complex, /) -> complex: ... + def __rmul__(self, value: complex, /) -> complex: ... + def __rpow__(self, value: complex, mod: None = None, /) -> complex: ... + def __rtruediv__(self, value: complex, /) -> complex: ... + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... def __neg__(self) -> complex: ... def __pos__(self) -> complex: ... def __abs__(self) -> float: ... @@ -424,29 +424,44 @@ class complex: def __complex__(self) -> complex: ... class _FormatMapMapping(Protocol): - def __getitem__(self, __key: str) -> Any: ... + def __getitem__(self, key: str, /) -> Any: ... class _TranslateTable(Protocol): - def __getitem__(self, __key: int) -> str | int | None: ... + def __getitem__(self, key: int, /) -> str | int | None: ... class str(Sequence[str]): @overload def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + @overload + def capitalize(self: LiteralString) -> LiteralString: ... + @overload def capitalize(self) -> str: ... # type: ignore[misc] + @overload + def casefold(self: LiteralString) -> LiteralString: ... + @overload def casefold(self) -> str: ... # type: ignore[misc] - def center(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - def count(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + @overload + def center(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... + @overload + def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] + def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( - self, __suffix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> bool: ... + @overload + def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... + @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] - def find(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + @overload + def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... - def index(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... + def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... def isascii(self) -> bool: ... @@ -459,86 +474,159 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - def join(self, __iterable: Iterable[str]) -> str: ... # type: ignore[misc] - def ljust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] + @overload + def join(self: LiteralString, iterable: Iterable[LiteralString], /) -> LiteralString: ... + @overload + def join(self, iterable: Iterable[str], /) -> str: ... # type: ignore[misc] + @overload + def ljust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... + @overload + def ljust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] + @overload + def lower(self: LiteralString) -> LiteralString: ... + @overload def lower(self) -> str: ... # type: ignore[misc] - def lstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] - def partition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] - def replace(self, __old: str, __new: str, __count: SupportsIndex = -1) -> str: ... # type: ignore[misc] + @overload + def lstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... + @overload + def lstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] + @overload + def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload + def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def replace(self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /) -> LiteralString: ... + @overload + def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: str) -> str: ... # type: ignore[misc] - def removesuffix(self, __suffix: str) -> str: ... # type: ignore[misc] + @overload + def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... + @overload + def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] + @overload + def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ... + @overload + def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] - def rfind(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - def rindex(self, __sub: str, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ...) -> int: ... - def rjust(self, __width: SupportsIndex, __fillchar: str = " ") -> str: ... # type: ignore[misc] - def rpartition(self, __sep: str) -> tuple[str, str, str]: ... # type: ignore[misc] + def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + @overload + def rjust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... + @overload + def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] + @overload + def rpartition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... + @overload + def rpartition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] + @overload + def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - def rstrip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def rstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... + @overload + def rstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] + @overload + def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... + @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] + @overload + def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... + @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( - self, __prefix: str | tuple[str, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> bool: ... - def strip(self, __chars: str | None = None) -> str: ... # type: ignore[misc] + @overload + def strip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... + @overload + def strip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] + @overload + def swapcase(self: LiteralString) -> LiteralString: ... + @overload def swapcase(self) -> str: ... # type: ignore[misc] + @overload + def title(self: LiteralString) -> LiteralString: ... + @overload def title(self) -> str: ... # type: ignore[misc] - def translate(self, __table: _TranslateTable) -> str: ... + def translate(self, table: _TranslateTable, /) -> str: ... + @overload + def upper(self: LiteralString) -> LiteralString: ... + @overload def upper(self) -> str: ... # type: ignore[misc] - def zfill(self, __width: SupportsIndex) -> str: ... # type: ignore[misc] + @overload + def zfill(self: LiteralString, width: SupportsIndex, /) -> LiteralString: ... + @overload + def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload - def maketrans(__x: dict[int, _T] | dict[str, _T] | dict[str | int, _T]) -> dict[int, _T]: ... + def maketrans(x: dict[int, _T] | dict[str, _T] | dict[str | int, _T], /) -> dict[int, _T]: ... @staticmethod @overload - def maketrans(__x: str, __y: str) -> dict[int, int]: ... + def maketrans(x: str, y: str, /) -> dict[int, int]: ... @staticmethod @overload - def maketrans(__x: str, __y: str, __z: str) -> dict[int, int | None]: ... - def __add__(self, __value: str) -> str: ... # type: ignore[misc] + def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... + @overload + def __add__(self: LiteralString, value: LiteralString, /) -> LiteralString: ... + @overload + def __add__(self, value: str, /) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ - def __contains__(self, __key: str) -> bool: ... # type: ignore[override] - def __eq__(self, __value: object) -> bool: ... - def __ge__(self, __value: str) -> bool: ... - def __getitem__(self, __key: SupportsIndex | slice) -> str: ... - def __gt__(self, __value: str) -> bool: ... + def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: str, /) -> bool: ... + def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... + def __gt__(self, value: str, /) -> bool: ... def __hash__(self) -> int: ... + @overload + def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... + @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] - def __le__(self, __value: str) -> bool: ... + def __le__(self, value: str, /) -> bool: ... def __len__(self) -> int: ... - def __lt__(self, __value: str) -> bool: ... - def __mod__(self, __value: Any) -> str: ... - def __mul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] - def __ne__(self, __value: object) -> bool: ... - def __rmul__(self, __value: SupportsIndex) -> str: ... # type: ignore[misc] + def __lt__(self, value: str, /) -> bool: ... + @overload + def __mod__(self: LiteralString, value: LiteralString | tuple[LiteralString, ...], /) -> LiteralString: ... + @overload + def __mod__(self, value: Any, /) -> str: ... + @overload + def __mul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... + @overload + def __mul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] + def __ne__(self, value: object, /) -> bool: ... + @overload + def __rmul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... + @overload + def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... class bytes(Sequence[int]): @overload - def __new__(cls, __o: Iterable[SupportsIndex] | SupportsIndex | SupportsBytes | ReadableBuffer) -> Self: ... + def __new__(cls, o: Iterable[SupportsIndex] | SupportsIndex | SupportsBytes | ReadableBuffer, /) -> Self: ... @overload - def __new__(cls, __string: str, encoding: str, errors: str = ...) -> Self: ... + def __new__(cls, string: str, /, encoding: str, errors: str = ...) -> Self: ... @overload def __new__(cls) -> Self: ... def capitalize(self) -> bytes: ... - def center(self, __width: SupportsIndex, __fillchar: bytes = b" ") -> bytes: ... + def center(self, width: SupportsIndex, fillchar: bytes = b" ", /) -> bytes: ... def count( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: ... def endswith( self, - __suffix: ReadableBuffer | tuple[ReadableBuffer, ...], - __start: SupportsIndex | None = ..., - __end: SupportsIndex | None = ..., + suffix: ReadableBuffer | tuple[ReadableBuffer, ...], + start: SupportsIndex | None = ..., + end: SupportsIndex | None = ..., + /, ) -> bool: ... def expandtabs(self, tabsize: SupportsIndex = 8) -> bytes: ... def find( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... def index( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... @@ -548,100 +636,102 @@ class bytes(Sequence[int]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - def join(self, __iterable_of_bytes: Iterable[ReadableBuffer]) -> bytes: ... - def ljust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = b" ") -> bytes: ... + def join(self, iterable_of_bytes: Iterable[ReadableBuffer], /) -> bytes: ... + def ljust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytes: ... def lower(self) -> bytes: ... - def lstrip(self, __bytes: ReadableBuffer | None = None) -> bytes: ... - def partition(self, __sep: ReadableBuffer) -> tuple[bytes, bytes, bytes]: ... - def replace(self, __old: ReadableBuffer, __new: ReadableBuffer, __count: SupportsIndex = -1) -> bytes: ... + def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... + def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ... + def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ... if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: ReadableBuffer) -> bytes: ... - def removesuffix(self, __suffix: ReadableBuffer) -> bytes: ... + def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ... + def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ... def rfind( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def rindex( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... - def rjust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = b" ") -> bytes: ... - def rpartition(self, __sep: ReadableBuffer) -> tuple[bytes, bytes, bytes]: ... + def rjust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytes: ... + def rpartition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ... def rsplit(self, sep: ReadableBuffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytes]: ... - def rstrip(self, __bytes: ReadableBuffer | None = None) -> bytes: ... + def rstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... def split(self, sep: ReadableBuffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytes]: ... def splitlines(self, keepends: bool = False) -> list[bytes]: ... def startswith( self, - __prefix: ReadableBuffer | tuple[ReadableBuffer, ...], - __start: SupportsIndex | None = ..., - __end: SupportsIndex | None = ..., + prefix: ReadableBuffer | tuple[ReadableBuffer, ...], + start: SupportsIndex | None = ..., + end: SupportsIndex | None = ..., + /, ) -> bool: ... - def strip(self, __bytes: ReadableBuffer | None = None) -> bytes: ... + def strip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... def swapcase(self) -> bytes: ... def title(self) -> bytes: ... - def translate(self, __table: ReadableBuffer | None, delete: bytes = b"") -> bytes: ... + def translate(self, table: ReadableBuffer | None, /, delete: bytes = b"") -> bytes: ... def upper(self) -> bytes: ... - def zfill(self, __width: SupportsIndex) -> bytes: ... + def zfill(self, width: SupportsIndex, /) -> bytes: ... @classmethod - def fromhex(cls, __string: str) -> Self: ... + def fromhex(cls, string: str, /) -> Self: ... @staticmethod - def maketrans(__frm: ReadableBuffer, __to: ReadableBuffer) -> bytes: ... + def maketrans(frm: ReadableBuffer, to: ReadableBuffer, /) -> bytes: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... def __hash__(self) -> int: ... @overload - def __getitem__(self, __key: SupportsIndex) -> int: ... + def __getitem__(self, key: SupportsIndex, /) -> int: ... @overload - def __getitem__(self, __key: slice) -> bytes: ... - def __add__(self, __value: ReadableBuffer) -> bytes: ... - def __mul__(self, __value: SupportsIndex) -> bytes: ... - def __rmul__(self, __value: SupportsIndex) -> bytes: ... - def __mod__(self, __value: Any) -> bytes: ... + def __getitem__(self, key: slice, /) -> bytes: ... + def __add__(self, value: ReadableBuffer, /) -> bytes: ... + def __mul__(self, value: SupportsIndex, /) -> bytes: ... + def __rmul__(self, value: SupportsIndex, /) -> bytes: ... + def __mod__(self, value: Any, /) -> bytes: ... # Incompatible with Sequence.__contains__ - def __contains__(self, __key: SupportsIndex | ReadableBuffer) -> bool: ... # type: ignore[override] - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... - def __lt__(self, __value: bytes) -> bool: ... - def __le__(self, __value: bytes) -> bool: ... - def __gt__(self, __value: bytes) -> bool: ... - def __ge__(self, __value: bytes) -> bool: ... + def __contains__(self, key: SupportsIndex | ReadableBuffer, /) -> bool: ... # type: ignore[override] + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __lt__(self, value: bytes, /) -> bool: ... + def __le__(self, value: bytes, /) -> bool: ... + def __gt__(self, value: bytes, /) -> bool: ... + def __ge__(self, value: bytes, /) -> bool: ... def __getnewargs__(self) -> tuple[bytes]: ... if sys.version_info >= (3, 11): def __bytes__(self) -> bytes: ... - def __buffer__(self, __flags: int) -> memoryview: ... + def __buffer__(self, flags: int, /) -> memoryview: ... class bytearray(MutableSequence[int]): @overload def __init__(self) -> None: ... @overload - def __init__(self, __ints: Iterable[SupportsIndex] | SupportsIndex | ReadableBuffer) -> None: ... + def __init__(self, ints: Iterable[SupportsIndex] | SupportsIndex | ReadableBuffer, /) -> None: ... @overload - def __init__(self, __string: str, encoding: str, errors: str = ...) -> None: ... - def append(self, __item: SupportsIndex) -> None: ... + def __init__(self, string: str, /, encoding: str, errors: str = ...) -> None: ... + def append(self, item: SupportsIndex, /) -> None: ... def capitalize(self) -> bytearray: ... - def center(self, __width: SupportsIndex, __fillchar: bytes = b" ") -> bytearray: ... + def center(self, width: SupportsIndex, fillchar: bytes = b" ", /) -> bytearray: ... def count( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def copy(self) -> bytearray: ... def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: ... def endswith( self, - __suffix: ReadableBuffer | tuple[ReadableBuffer, ...], - __start: SupportsIndex | None = ..., - __end: SupportsIndex | None = ..., + suffix: ReadableBuffer | tuple[ReadableBuffer, ...], + start: SupportsIndex | None = ..., + end: SupportsIndex | None = ..., + /, ) -> bool: ... def expandtabs(self, tabsize: SupportsIndex = 8) -> bytearray: ... - def extend(self, __iterable_of_ints: Iterable[SupportsIndex]) -> None: ... + def extend(self, iterable_of_ints: Iterable[SupportsIndex], /) -> None: ... def find( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... def index( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... - def insert(self, __index: SupportsIndex, __item: SupportsIndex) -> None: ... + def insert(self, index: SupportsIndex, item: SupportsIndex, /) -> None: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... def isascii(self) -> bool: ... @@ -650,76 +740,77 @@ class bytearray(MutableSequence[int]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - def join(self, __iterable_of_bytes: Iterable[ReadableBuffer]) -> bytearray: ... - def ljust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = b" ") -> bytearray: ... + def join(self, iterable_of_bytes: Iterable[ReadableBuffer], /) -> bytearray: ... + def ljust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytearray: ... def lower(self) -> bytearray: ... - def lstrip(self, __bytes: ReadableBuffer | None = None) -> bytearray: ... - def partition(self, __sep: ReadableBuffer) -> tuple[bytearray, bytearray, bytearray]: ... - def pop(self, __index: int = -1) -> int: ... - def remove(self, __value: int) -> None: ... + def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytearray: ... + def partition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ... + def pop(self, index: int = -1, /) -> int: ... + def remove(self, value: int, /) -> None: ... if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: ReadableBuffer) -> bytearray: ... - def removesuffix(self, __suffix: ReadableBuffer) -> bytearray: ... + def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ... + def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ... - def replace(self, __old: ReadableBuffer, __new: ReadableBuffer, __count: SupportsIndex = -1) -> bytearray: ... + def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ... def rfind( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... def rindex( - self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> int: ... - def rjust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = b" ") -> bytearray: ... - def rpartition(self, __sep: ReadableBuffer) -> tuple[bytearray, bytearray, bytearray]: ... + def rjust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytearray: ... + def rpartition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ... def rsplit(self, sep: ReadableBuffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytearray]: ... - def rstrip(self, __bytes: ReadableBuffer | None = None) -> bytearray: ... + def rstrip(self, bytes: ReadableBuffer | None = None, /) -> bytearray: ... def split(self, sep: ReadableBuffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytearray]: ... def splitlines(self, keepends: bool = False) -> list[bytearray]: ... def startswith( self, - __prefix: ReadableBuffer | tuple[ReadableBuffer, ...], - __start: SupportsIndex | None = ..., - __end: SupportsIndex | None = ..., + prefix: ReadableBuffer | tuple[ReadableBuffer, ...], + start: SupportsIndex | None = ..., + end: SupportsIndex | None = ..., + /, ) -> bool: ... - def strip(self, __bytes: ReadableBuffer | None = None) -> bytearray: ... + def strip(self, bytes: ReadableBuffer | None = None, /) -> bytearray: ... def swapcase(self) -> bytearray: ... def title(self) -> bytearray: ... - def translate(self, __table: ReadableBuffer | None, delete: bytes = b"") -> bytearray: ... + def translate(self, table: ReadableBuffer | None, /, delete: bytes = b"") -> bytearray: ... def upper(self) -> bytearray: ... - def zfill(self, __width: SupportsIndex) -> bytearray: ... + def zfill(self, width: SupportsIndex, /) -> bytearray: ... @classmethod - def fromhex(cls, __string: str) -> Self: ... + def fromhex(cls, string: str, /) -> Self: ... @staticmethod - def maketrans(__frm: ReadableBuffer, __to: ReadableBuffer) -> bytes: ... + def maketrans(frm: ReadableBuffer, to: ReadableBuffer, /) -> bytes: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... __hash__: ClassVar[None] # type: ignore[assignment] @overload - def __getitem__(self, __key: SupportsIndex) -> int: ... + def __getitem__(self, key: SupportsIndex, /) -> int: ... @overload - def __getitem__(self, __key: slice) -> bytearray: ... + def __getitem__(self, key: slice, /) -> bytearray: ... @overload - def __setitem__(self, __key: SupportsIndex, __value: SupportsIndex) -> None: ... + def __setitem__(self, key: SupportsIndex, value: SupportsIndex, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: Iterable[SupportsIndex] | bytes) -> None: ... - def __delitem__(self, __key: SupportsIndex | slice) -> None: ... - def __add__(self, __value: ReadableBuffer) -> bytearray: ... + def __setitem__(self, key: slice, value: Iterable[SupportsIndex] | bytes, /) -> None: ... + def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... + def __add__(self, value: ReadableBuffer, /) -> bytearray: ... # The superclass wants us to accept Iterable[int], but that fails at runtime. - def __iadd__(self, __value: ReadableBuffer) -> Self: ... # type: ignore[override] - def __mul__(self, __value: SupportsIndex) -> bytearray: ... - def __rmul__(self, __value: SupportsIndex) -> bytearray: ... - def __imul__(self, __value: SupportsIndex) -> Self: ... - def __mod__(self, __value: Any) -> bytes: ... + def __iadd__(self, value: ReadableBuffer, /) -> Self: ... # type: ignore[override] + def __mul__(self, value: SupportsIndex, /) -> bytearray: ... + def __rmul__(self, value: SupportsIndex, /) -> bytearray: ... + def __imul__(self, value: SupportsIndex, /) -> Self: ... + def __mod__(self, value: Any, /) -> bytes: ... # Incompatible with Sequence.__contains__ - def __contains__(self, __key: SupportsIndex | ReadableBuffer) -> bool: ... # type: ignore[override] - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... - def __lt__(self, __value: ReadableBuffer) -> bool: ... - def __le__(self, __value: ReadableBuffer) -> bool: ... - def __gt__(self, __value: ReadableBuffer) -> bool: ... - def __ge__(self, __value: ReadableBuffer) -> bool: ... + def __contains__(self, key: SupportsIndex | ReadableBuffer, /) -> bool: ... # type: ignore[override] + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __lt__(self, value: ReadableBuffer, /) -> bool: ... + def __le__(self, value: ReadableBuffer, /) -> bool: ... + def __gt__(self, value: ReadableBuffer, /) -> bool: ... + def __ge__(self, value: ReadableBuffer, /) -> bool: ... def __alloc__(self) -> int: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... @final class memoryview(Sequence[int]): @@ -750,22 +841,22 @@ class memoryview(Sequence[int]): def __new__(cls, obj: ReadableBuffer) -> Self: ... def __enter__(self) -> Self: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_val: BaseException | None, __exc_tb: TracebackType | None + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... def cast(self, format: str, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... @overload - def __getitem__(self, __key: SupportsIndex | tuple[SupportsIndex, ...]) -> int: ... + def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> int: ... @overload - def __getitem__(self, __key: slice) -> memoryview: ... - def __contains__(self, __x: object) -> bool: ... + def __getitem__(self, key: slice, /) -> memoryview: ... + def __contains__(self, x: object, /) -> bool: ... def __iter__(self) -> Iterator[int]: ... def __len__(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload - def __setitem__(self, __key: slice, __value: ReadableBuffer) -> None: ... + def __setitem__(self, key: slice, value: ReadableBuffer, /) -> None: ... @overload - def __setitem__(self, __key: SupportsIndex | tuple[SupportsIndex, ...], __value: SupportsIndex) -> None: ... + def __setitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], value: SupportsIndex, /) -> None: ... if sys.version_info >= (3, 10): def tobytes(self, order: Literal["C", "F", "A"] | None = "C") -> bytes: ... else: @@ -775,38 +866,38 @@ class memoryview(Sequence[int]): def toreadonly(self) -> memoryview: ... def release(self) -> None: ... def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... @final class bool(int): - def __new__(cls, __o: object = ...) -> Self: ... + def __new__(cls, o: object = ..., /) -> Self: ... # The following overloads could be represented more elegantly with a TypeVar("_B", bool, int), # however mypy has a bug regarding TypeVar constraints (https://github.com/python/mypy/issues/11880). @overload - def __and__(self, __value: bool) -> bool: ... + def __and__(self, value: bool, /) -> bool: ... @overload - def __and__(self, __value: int) -> int: ... + def __and__(self, value: int, /) -> int: ... @overload - def __or__(self, __value: bool) -> bool: ... + def __or__(self, value: bool, /) -> bool: ... @overload - def __or__(self, __value: int) -> int: ... + def __or__(self, value: int, /) -> int: ... @overload - def __xor__(self, __value: bool) -> bool: ... + def __xor__(self, value: bool, /) -> bool: ... @overload - def __xor__(self, __value: int) -> int: ... + def __xor__(self, value: int, /) -> int: ... @overload - def __rand__(self, __value: bool) -> bool: ... + def __rand__(self, value: bool, /) -> bool: ... @overload - def __rand__(self, __value: int) -> int: ... + def __rand__(self, value: int, /) -> int: ... @overload - def __ror__(self, __value: bool) -> bool: ... + def __ror__(self, value: bool, /) -> bool: ... @overload - def __ror__(self, __value: int) -> int: ... + def __ror__(self, value: int, /) -> int: ... @overload - def __rxor__(self, __value: bool) -> bool: ... + def __rxor__(self, value: bool, /) -> bool: ... @overload - def __rxor__(self, __value: int) -> int: ... + def __rxor__(self, value: int, /) -> int: ... def __getnewargs__(self) -> tuple[int]: ... @deprecated("Will throw an error in Python 3.14. Use `not` for logical negation of bools instead.") def __invert__(self) -> int: ... @@ -820,38 +911,38 @@ class slice: @property def stop(self) -> Any: ... @overload - def __new__(cls, __stop: Any) -> Self: ... + def __new__(cls, stop: Any, /) -> Self: ... @overload - def __new__(cls, __start: Any, __stop: Any, __step: Any = ...) -> Self: ... - def __eq__(self, __value: object) -> bool: ... + def __new__(cls, start: Any, stop: Any, step: Any = ..., /) -> Self: ... + def __eq__(self, value: object, /) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] - def indices(self, __len: SupportsIndex) -> tuple[int, int, int]: ... + def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ... class tuple(Sequence[_T_co]): - def __new__(cls, __iterable: Iterable[_T_co] = ...) -> Self: ... + def __new__(cls, iterable: Iterable[_T_co] = ..., /) -> Self: ... def __len__(self) -> int: ... - def __contains__(self, __key: object) -> bool: ... + def __contains__(self, key: object, /) -> bool: ... @overload - def __getitem__(self, __key: SupportsIndex) -> _T_co: ... + def __getitem__(self, key: SupportsIndex, /) -> _T_co: ... @overload - def __getitem__(self, __key: slice) -> tuple[_T_co, ...]: ... + def __getitem__(self, key: slice, /) -> tuple[_T_co, ...]: ... def __iter__(self) -> Iterator[_T_co]: ... - def __lt__(self, __value: tuple[_T_co, ...]) -> bool: ... - def __le__(self, __value: tuple[_T_co, ...]) -> bool: ... - def __gt__(self, __value: tuple[_T_co, ...]) -> bool: ... - def __ge__(self, __value: tuple[_T_co, ...]) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __lt__(self, value: tuple[_T_co, ...], /) -> bool: ... + def __le__(self, value: tuple[_T_co, ...], /) -> bool: ... + def __gt__(self, value: tuple[_T_co, ...], /) -> bool: ... + def __ge__(self, value: tuple[_T_co, ...], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload - def __add__(self, __value: tuple[_T_co, ...]) -> tuple[_T_co, ...]: ... + def __add__(self, value: tuple[_T_co, ...], /) -> tuple[_T_co, ...]: ... @overload - def __add__(self, __value: tuple[_T, ...]) -> tuple[_T_co | _T, ...]: ... - def __mul__(self, __value: SupportsIndex) -> tuple[_T_co, ...]: ... - def __rmul__(self, __value: SupportsIndex) -> tuple[_T_co, ...]: ... - def count(self, __value: Any) -> int: ... - def index(self, __value: Any, __start: SupportsIndex = 0, __stop: SupportsIndex = sys.maxsize) -> int: ... + def __add__(self, value: tuple[_T, ...], /) -> tuple[_T_co | _T, ...]: ... + def __mul__(self, value: SupportsIndex, /) -> tuple[_T_co, ...]: ... + def __rmul__(self, value: SupportsIndex, /) -> tuple[_T_co, ...]: ... + def count(self, value: Any, /) -> int: ... + def index(self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # Doesn't exist at runtime, but deleting this breaks mypy. See #2999 @final @@ -877,23 +968,23 @@ class function: __module__: str # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any. - def __get__(self, __instance: object, __owner: type | None = None) -> Any: ... + def __get__(self, instance: object, owner: type | None = None, /) -> Any: ... class list(MutableSequence[_T]): @overload def __init__(self) -> None: ... @overload - def __init__(self, __iterable: Iterable[_T]) -> None: ... + def __init__(self, iterable: Iterable[_T], /) -> None: ... def copy(self) -> list[_T]: ... - def append(self, __object: _T) -> None: ... - def extend(self, __iterable: Iterable[_T]) -> None: ... - def pop(self, __index: SupportsIndex = -1) -> _T: ... + def append(self, object: _T, /) -> None: ... + def extend(self, iterable: Iterable[_T], /) -> None: ... + def pop(self, index: SupportsIndex = -1, /) -> _T: ... # Signature of `list.index` should be kept in line with `collections.UserList.index()` # and multiprocessing.managers.ListProxy.index() - def index(self, __value: _T, __start: SupportsIndex = 0, __stop: SupportsIndex = sys.maxsize) -> int: ... - def count(self, __value: _T) -> int: ... - def insert(self, __index: SupportsIndex, __object: _T) -> None: ... - def remove(self, __value: _T) -> None: ... + def index(self, value: _T, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ... + def count(self, value: _T, /) -> int: ... + def insert(self, index: SupportsIndex, object: _T, /) -> None: ... + def remove(self, value: _T, /) -> None: ... # Signature of `list.sort` should be kept inline with `collections.UserList.sort()` # and multiprocessing.managers.ListProxy.sort() # @@ -907,32 +998,32 @@ class list(MutableSequence[_T]): def __iter__(self) -> Iterator[_T]: ... __hash__: ClassVar[None] # type: ignore[assignment] @overload - def __getitem__(self, __i: SupportsIndex) -> _T: ... + def __getitem__(self, i: SupportsIndex, /) -> _T: ... @overload - def __getitem__(self, __s: slice) -> list[_T]: ... + def __getitem__(self, s: slice, /) -> list[_T]: ... @overload - def __setitem__(self, __key: SupportsIndex, __value: _T) -> None: ... + def __setitem__(self, key: SupportsIndex, value: _T, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: Iterable[_T]) -> None: ... - def __delitem__(self, __key: SupportsIndex | slice) -> None: ... + def __setitem__(self, key: slice, value: Iterable[_T], /) -> None: ... + def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... # Overloading looks unnecessary, but is needed to work around complex mypy problems @overload - def __add__(self, __value: list[_T]) -> list[_T]: ... + def __add__(self, value: list[_T], /) -> list[_T]: ... @overload - def __add__(self, __value: list[_S]) -> list[_S | _T]: ... - def __iadd__(self, __value: Iterable[_T]) -> Self: ... # type: ignore[misc] - def __mul__(self, __value: SupportsIndex) -> list[_T]: ... - def __rmul__(self, __value: SupportsIndex) -> list[_T]: ... - def __imul__(self, __value: SupportsIndex) -> Self: ... - def __contains__(self, __key: object) -> bool: ... + def __add__(self, value: list[_S], /) -> list[_S | _T]: ... + def __iadd__(self, value: Iterable[_T], /) -> Self: ... # type: ignore[misc] + def __mul__(self, value: SupportsIndex, /) -> list[_T]: ... + def __rmul__(self, value: SupportsIndex, /) -> list[_T]: ... + def __imul__(self, value: SupportsIndex, /) -> Self: ... + def __contains__(self, key: object, /) -> bool: ... def __reversed__(self) -> Iterator[_T]: ... - def __gt__(self, __value: list[_T]) -> bool: ... - def __ge__(self, __value: list[_T]) -> bool: ... - def __lt__(self, __value: list[_T]) -> bool: ... - def __le__(self, __value: list[_T]) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __gt__(self, value: list[_T], /) -> bool: ... + def __ge__(self, value: list[_T], /) -> bool: ... + def __lt__(self, value: list[_T], /) -> bool: ... + def __le__(self, value: list[_T], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class dict(MutableMapping[_KT, _VT]): # __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics @@ -942,19 +1033,19 @@ class dict(MutableMapping[_KT, _VT]): @overload def __init__(self: dict[str, _VT], **kwargs: _VT) -> None: ... @overload - def __init__(self, __map: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... + def __init__(self, map: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload - def __init__(self: dict[str, _VT], __map: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> None: ... + def __init__(self: dict[str, _VT], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ... @overload - def __init__(self, __iterable: Iterable[tuple[_KT, _VT]]) -> None: ... + def __init__(self, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload - def __init__(self: dict[str, _VT], __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> None: ... + def __init__(self: dict[str, _VT], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... # Next two overloads are for dict(string.split(sep) for string in iterable) # Cannot be Iterable[Sequence[_T]] or otherwise dict(["foo", "bar", "baz"]) is not an error @overload - def __init__(self: dict[str, str], __iterable: Iterable[list[str]]) -> None: ... + def __init__(self: dict[str, str], iterable: Iterable[list[str]], /) -> None: ... @overload - def __init__(self: dict[bytes, bytes], __iterable: Iterable[list[bytes]]) -> None: ... + def __init__(self: dict[bytes, bytes], iterable: Iterable[list[bytes]], /) -> None: ... def __new__(cls, *args: Any, **kwargs: Any) -> Self: ... def copy(self) -> dict[_KT, _VT]: ... def keys(self) -> dict_keys[_KT, _VT]: ... @@ -965,122 +1056,122 @@ class dict(MutableMapping[_KT, _VT]): # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload - def fromkeys(cls, __iterable: Iterable[_T], __value: None = None) -> dict[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_T], value: None = None, /) -> dict[_T, Any | None]: ... @classmethod @overload - def fromkeys(cls, __iterable: Iterable[_T], __value: _S) -> dict[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ... # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] - def get(self, __key: _KT) -> _VT | None: ... + def get(self, key: _KT, /) -> _VT | None: ... @overload - def get(self, __key: _KT, __default: _VT) -> _VT: ... + def get(self, key: _KT, default: _VT, /) -> _VT: ... @overload - def get(self, __key: _KT, __default: _T) -> _VT | _T: ... + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... @overload - def pop(self, __key: _KT) -> _VT: ... + def pop(self, key: _KT, /) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _VT) -> _VT: ... + def pop(self, key: _KT, default: _VT, /) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _T) -> _VT | _T: ... + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... def __len__(self) -> int: ... - def __getitem__(self, __key: _KT) -> _VT: ... - def __setitem__(self, __key: _KT, __value: _VT) -> None: ... - def __delitem__(self, __key: _KT) -> None: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... def __iter__(self) -> Iterator[_KT]: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __reversed__(self) -> Iterator[_KT]: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @overload - def __or__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ... + def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... @overload - def __or__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... + def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... @overload - def __ror__(self, __value: dict[_KT, _VT]) -> dict[_KT, _VT]: ... + def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... @overload - def __ror__(self, __value: dict[_T1, _T2]) -> dict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] - def __ior__(self, __value: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... + def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... @overload - def __ior__(self, __value: Iterable[tuple[_KT, _VT]]) -> Self: ... + def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ... class set(MutableSet[_T]): @overload def __init__(self) -> None: ... @overload - def __init__(self, __iterable: Iterable[_T]) -> None: ... - def add(self, __element: _T) -> None: ... + def __init__(self, iterable: Iterable[_T], /) -> None: ... + def add(self, element: _T, /) -> None: ... def copy(self) -> set[_T]: ... def difference(self, *s: Iterable[Any]) -> set[_T]: ... def difference_update(self, *s: Iterable[Any]) -> None: ... - def discard(self, __element: _T) -> None: ... + def discard(self, element: _T, /) -> None: ... def intersection(self, *s: Iterable[Any]) -> set[_T]: ... def intersection_update(self, *s: Iterable[Any]) -> None: ... - def isdisjoint(self, __s: Iterable[Any]) -> bool: ... - def issubset(self, __s: Iterable[Any]) -> bool: ... - def issuperset(self, __s: Iterable[Any]) -> bool: ... - def remove(self, __element: _T) -> None: ... - def symmetric_difference(self, __s: Iterable[_T]) -> set[_T]: ... - def symmetric_difference_update(self, __s: Iterable[_T]) -> None: ... + def isdisjoint(self, s: Iterable[Any], /) -> bool: ... + def issubset(self, s: Iterable[Any], /) -> bool: ... + def issuperset(self, s: Iterable[Any], /) -> bool: ... + def remove(self, element: _T, /) -> None: ... + def symmetric_difference(self, s: Iterable[_T], /) -> set[_T]: ... + def symmetric_difference_update(self, s: Iterable[_T], /) -> None: ... def union(self, *s: Iterable[_S]) -> set[_T | _S]: ... def update(self, *s: Iterable[_T]) -> None: ... def __len__(self) -> int: ... - def __contains__(self, __o: object) -> bool: ... + def __contains__(self, o: object, /) -> bool: ... def __iter__(self) -> Iterator[_T]: ... - def __and__(self, __value: AbstractSet[object]) -> set[_T]: ... - def __iand__(self, __value: AbstractSet[object]) -> Self: ... - def __or__(self, __value: AbstractSet[_S]) -> set[_T | _S]: ... - def __ior__(self, __value: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] - def __sub__(self, __value: AbstractSet[_T | None]) -> set[_T]: ... - def __isub__(self, __value: AbstractSet[object]) -> Self: ... - def __xor__(self, __value: AbstractSet[_S]) -> set[_T | _S]: ... - def __ixor__(self, __value: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] - def __le__(self, __value: AbstractSet[object]) -> bool: ... - def __lt__(self, __value: AbstractSet[object]) -> bool: ... - def __ge__(self, __value: AbstractSet[object]) -> bool: ... - def __gt__(self, __value: AbstractSet[object]) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __and__(self, value: AbstractSet[object], /) -> set[_T]: ... + def __iand__(self, value: AbstractSet[object], /) -> Self: ... + def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... + def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] + def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... + def __isub__(self, value: AbstractSet[object], /) -> Self: ... + def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... + def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] + def __le__(self, value: AbstractSet[object], /) -> bool: ... + def __lt__(self, value: AbstractSet[object], /) -> bool: ... + def __ge__(self, value: AbstractSet[object], /) -> bool: ... + def __gt__(self, value: AbstractSet[object], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class frozenset(AbstractSet[_T_co]): @overload def __new__(cls) -> Self: ... @overload - def __new__(cls, __iterable: Iterable[_T_co]) -> Self: ... + def __new__(cls, iterable: Iterable[_T_co], /) -> Self: ... def copy(self) -> frozenset[_T_co]: ... def difference(self, *s: Iterable[object]) -> frozenset[_T_co]: ... def intersection(self, *s: Iterable[object]) -> frozenset[_T_co]: ... - def isdisjoint(self, __s: Iterable[_T_co]) -> bool: ... - def issubset(self, __s: Iterable[object]) -> bool: ... - def issuperset(self, __s: Iterable[object]) -> bool: ... - def symmetric_difference(self, __s: Iterable[_T_co]) -> frozenset[_T_co]: ... + def isdisjoint(self, s: Iterable[_T_co], /) -> bool: ... + def issubset(self, s: Iterable[object], /) -> bool: ... + def issuperset(self, s: Iterable[object], /) -> bool: ... + def symmetric_difference(self, s: Iterable[_T_co], /) -> frozenset[_T_co]: ... def union(self, *s: Iterable[_S]) -> frozenset[_T_co | _S]: ... def __len__(self) -> int: ... - def __contains__(self, __o: object) -> bool: ... + def __contains__(self, o: object, /) -> bool: ... def __iter__(self) -> Iterator[_T_co]: ... - def __and__(self, __value: AbstractSet[_T_co]) -> frozenset[_T_co]: ... - def __or__(self, __value: AbstractSet[_S]) -> frozenset[_T_co | _S]: ... - def __sub__(self, __value: AbstractSet[_T_co]) -> frozenset[_T_co]: ... - def __xor__(self, __value: AbstractSet[_S]) -> frozenset[_T_co | _S]: ... - def __le__(self, __value: AbstractSet[object]) -> bool: ... - def __lt__(self, __value: AbstractSet[object]) -> bool: ... - def __ge__(self, __value: AbstractSet[object]) -> bool: ... - def __gt__(self, __value: AbstractSet[object]) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __and__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ... + def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... + def __sub__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ... + def __xor__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... + def __le__(self, value: AbstractSet[object], /) -> bool: ... + def __lt__(self, value: AbstractSet[object], /) -> bool: ... + def __ge__(self, value: AbstractSet[object], /) -> bool: ... + def __gt__(self, value: AbstractSet[object], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class enumerate(Iterator[tuple[int, _T]]): def __new__(cls, iterable: Iterable[_T], start: int = ...) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class range(Sequence[int]): @@ -1091,20 +1182,20 @@ class range(Sequence[int]): @property def step(self) -> int: ... @overload - def __new__(cls, __stop: SupportsIndex) -> Self: ... + def __new__(cls, stop: SupportsIndex, /) -> Self: ... @overload - def __new__(cls, __start: SupportsIndex, __stop: SupportsIndex, __step: SupportsIndex = ...) -> Self: ... - def count(self, __value: int) -> int: ... - def index(self, __value: int) -> int: ... # type: ignore[override] + def __new__(cls, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex = ..., /) -> Self: ... + def count(self, value: int, /) -> int: ... + def index(self, value: int, /) -> int: ... # type: ignore[override] def __len__(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... - def __contains__(self, __key: object) -> bool: ... + def __contains__(self, key: object, /) -> bool: ... def __iter__(self) -> Iterator[int]: ... @overload - def __getitem__(self, __key: SupportsIndex) -> int: ... + def __getitem__(self, key: SupportsIndex, /) -> int: ... @overload - def __getitem__(self, __key: slice) -> range: ... + def __getitem__(self, key: slice, /) -> range: ... def __reversed__(self) -> Iterator[int]: ... class property: @@ -1119,12 +1210,12 @@ class property: fdel: Callable[[Any], None] | None = ..., doc: str | None = ..., ) -> None: ... - def getter(self, __fget: Callable[[Any], Any]) -> property: ... - def setter(self, __fset: Callable[[Any, Any], None]) -> property: ... - def deleter(self, __fdel: Callable[[Any], None]) -> property: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... - def __set__(self, __instance: Any, __value: Any) -> None: ... - def __delete__(self, __instance: Any) -> None: ... + def getter(self, fget: Callable[[Any], Any], /) -> property: ... + def setter(self, fset: Callable[[Any, Any], None], /) -> property: ... + def deleter(self, fdel: Callable[[Any], None], /) -> property: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... + def __set__(self, instance: Any, value: Any, /) -> None: ... + def __delete__(self, instance: Any, /) -> None: ... @final class _NotImplementedType(Any): @@ -1134,14 +1225,14 @@ class _NotImplementedType(Any): NotImplemented: _NotImplementedType -def abs(__x: SupportsAbs[_T]) -> _T: ... -def all(__iterable: Iterable[object]) -> bool: ... -def any(__iterable: Iterable[object]) -> bool: ... -def ascii(__obj: object) -> str: ... -def bin(__number: int | SupportsIndex) -> str: ... +def abs(x: SupportsAbs[_T], /) -> _T: ... +def all(iterable: Iterable[object], /) -> bool: ... +def any(iterable: Iterable[object], /) -> bool: ... +def ascii(obj: object, /) -> str: ... +def bin(number: int | SupportsIndex, /) -> str: ... def breakpoint(*args: Any, **kws: Any) -> None: ... -def callable(__obj: object) -> TypeGuard[Callable[..., object]]: ... -def chr(__i: int) -> str: ... +def callable(obj: object, /) -> TypeGuard[Callable[..., object]]: ... +def chr(i: int, /) -> str: ... # We define this here instead of using os.PathLike to avoid import cycle issues. # See https://github.com/python/typeshed/pull/991#issuecomment-288160993 @@ -1149,7 +1240,7 @@ class _PathLike(Protocol[AnyStr_co]): def __fspath__(self) -> AnyStr_co: ... if sys.version_info >= (3, 10): - def aiter(__async_iterable: SupportsAiter[_SupportsAnextT]) -> _SupportsAnextT: ... + def aiter(async_iterable: SupportsAiter[_SupportsAnextT], /) -> _SupportsAnextT: ... class _SupportsSynchronousAnext(Protocol[_AwaitableT_co]): def __anext__(self) -> _AwaitableT_co: ... @@ -1158,9 +1249,9 @@ if sys.version_info >= (3, 10): # `anext` is not, in fact, an async function. When default is not provided # `anext` is just a passthrough for `obj.__anext__` # See discussion in #7491 and pure-Python implementation of `anext` at https://github.com/python/cpython/blob/ea786a882b9ed4261eafabad6011bc7ef3b5bf94/Lib/test/test_asyncgen.py#L52-L80 - def anext(__i: _SupportsSynchronousAnext[_AwaitableT]) -> _AwaitableT: ... + def anext(i: _SupportsSynchronousAnext[_AwaitableT], /) -> _AwaitableT: ... @overload - async def anext(__i: SupportsAnext[_T], __default: _VT) -> _T | _VT: ... + async def anext(i: SupportsAnext[_T], default: _VT, /) -> _T | _VT: ... # compile() returns a CodeType, unless the flags argument includes PyCF_ONLY_AST (=1024), # in which case it returns ast.AST. We have overloads for flag 0 (the default) and for @@ -1210,86 +1301,86 @@ def compile( ) -> Any: ... def copyright() -> None: ... def credits() -> None: ... -def delattr(__obj: object, __name: str) -> None: ... -def dir(__o: object = ...) -> list[str]: ... +def delattr(obj: object, name: str, /) -> None: ... +def dir(o: object = ..., /) -> list[str]: ... @overload -def divmod(__x: SupportsDivMod[_T_contra, _T_co], __y: _T_contra) -> _T_co: ... +def divmod(x: SupportsDivMod[_T_contra, _T_co], y: _T_contra, /) -> _T_co: ... @overload -def divmod(__x: _T_contra, __y: SupportsRDivMod[_T_contra, _T_co]) -> _T_co: ... +def divmod(x: _T_contra, y: SupportsRDivMod[_T_contra, _T_co], /) -> _T_co: ... # The `globals` argument to `eval` has to be `dict[str, Any]` rather than `dict[str, object]` due to invariance. # (The `globals` argument has to be a "real dict", rather than any old mapping, unlike the `locals` argument.) def eval( - __source: str | ReadableBuffer | CodeType, - __globals: dict[str, Any] | None = None, - __locals: Mapping[str, object] | None = None, + source: str | ReadableBuffer | CodeType, globals: dict[str, Any] | None = None, locals: Mapping[str, object] | None = None, / ) -> Any: ... # Comment above regarding `eval` applies to `exec` as well if sys.version_info >= (3, 11): def exec( - __source: str | ReadableBuffer | CodeType, - __globals: dict[str, Any] | None = None, - __locals: Mapping[str, object] | None = None, + source: str | ReadableBuffer | CodeType, + globals: dict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + /, *, closure: tuple[_Cell, ...] | None = None, ) -> None: ... else: def exec( - __source: str | ReadableBuffer | CodeType, - __globals: dict[str, Any] | None = None, - __locals: Mapping[str, object] | None = None, + source: str | ReadableBuffer | CodeType, + globals: dict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + /, ) -> None: ... def exit(code: sys._ExitCode = None) -> NoReturn: ... class filter(Iterator[_T]): @overload - def __new__(cls, __function: None, __iterable: Iterable[_T | None]) -> Self: ... + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... @overload - def __new__(cls, __function: Callable[[_S], TypeGuard[_T]], __iterable: Iterable[_S]) -> Self: ... + def __new__(cls, function: Callable[[_S], TypeGuard[_T]], iterable: Iterable[_S], /) -> Self: ... @overload - def __new__(cls, __function: Callable[[_T], Any], __iterable: Iterable[_T]) -> Self: ... + def __new__(cls, function: Callable[[_T], Any], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -def format(__value: object, __format_spec: str = "") -> str: ... +def format(value: object, format_spec: str = "", /) -> str: ... @overload -def getattr(__o: object, __name: str) -> Any: ... +def getattr(o: object, name: str, /) -> Any: ... # While technically covered by the last overload, spelling out the types for None, bool # and basic containers help mypy out in some tricky situations involving type context # (aka bidirectional inference) @overload -def getattr(__o: object, __name: str, __default: None) -> Any | None: ... +def getattr(o: object, name: str, default: None, /) -> Any | None: ... @overload -def getattr(__o: object, __name: str, __default: bool) -> Any | bool: ... +def getattr(o: object, name: str, default: bool, /) -> Any | bool: ... @overload -def getattr(__o: object, __name: str, __default: list[Any]) -> Any | list[Any]: ... +def getattr(o: object, name: str, default: list[Any], /) -> Any | list[Any]: ... @overload -def getattr(__o: object, __name: str, __default: dict[Any, Any]) -> Any | dict[Any, Any]: ... +def getattr(o: object, name: str, default: dict[Any, Any], /) -> Any | dict[Any, Any]: ... @overload -def getattr(__o: object, __name: str, __default: _T) -> Any | _T: ... +def getattr(o: object, name: str, default: _T, /) -> Any | _T: ... def globals() -> dict[str, Any]: ... -def hasattr(__obj: object, __name: str) -> bool: ... -def hash(__obj: object) -> int: ... +def hasattr(obj: object, name: str, /) -> bool: ... +def hash(obj: object, /) -> int: ... def help(request: object = ...) -> None: ... -def hex(__number: int | SupportsIndex) -> str: ... -def id(__obj: object) -> int: ... -def input(__prompt: object = "") -> str: ... +def hex(number: int | SupportsIndex, /) -> str: ... +def id(obj: object, /) -> int: ... +def input(prompt: object = "", /) -> str: ... class _GetItemIterable(Protocol[_T_co]): - def __getitem__(self, __i: int) -> _T_co: ... + def __getitem__(self, i: int, /) -> _T_co: ... @overload -def iter(__object: SupportsIter[_SupportsNextT]) -> _SupportsNextT: ... +def iter(object: SupportsIter[_SupportsNextT], /) -> _SupportsNextT: ... @overload -def iter(__object: _GetItemIterable[_T]) -> Iterator[_T]: ... +def iter(object: _GetItemIterable[_T], /) -> Iterator[_T]: ... @overload -def iter(__object: Callable[[], _T | None], __sentinel: None) -> Iterator[_T]: ... +def iter(object: Callable[[], _T | None], sentinel: None, /) -> Iterator[_T]: ... @overload -def iter(__object: Callable[[], _T], __sentinel: object) -> Iterator[_T]: ... +def iter(object: Callable[[], _T], sentinel: object, /) -> Iterator[_T]: ... # Keep this alias in sync with unittest.case._ClassInfo if sys.version_info >= (3, 10): @@ -1297,50 +1388,53 @@ if sys.version_info >= (3, 10): else: _ClassInfo: TypeAlias = type | tuple[_ClassInfo, ...] -def isinstance(__obj: object, __class_or_tuple: _ClassInfo) -> bool: ... -def issubclass(__cls: type, __class_or_tuple: _ClassInfo) -> bool: ... -def len(__obj: Sized) -> int: ... +def isinstance(obj: object, class_or_tuple: _ClassInfo, /) -> bool: ... +def issubclass(cls: type, class_or_tuple: _ClassInfo, /) -> bool: ... +def len(obj: Sized, /) -> int: ... def license() -> None: ... def locals() -> dict[str, Any]: ... class map(Iterator[_S]): @overload - def __new__(cls, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> Self: ... + def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... @overload - def __new__(cls, __func: Callable[[_T1, _T2], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> Self: ... + def __new__(cls, func: Callable[[_T1, _T2], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... @overload def __new__( - cls, __func: Callable[[_T1, _T2, _T3], _S], __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] + cls, func: Callable[[_T1, _T2, _T3], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / ) -> Self: ... @overload def __new__( cls, - __func: Callable[[_T1, _T2, _T3, _T4], _S], - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], + func: Callable[[_T1, _T2, _T3, _T4], _S], + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + /, ) -> Self: ... @overload def __new__( cls, - __func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + /, ) -> Self: ... @overload def __new__( cls, - __func: Callable[..., _S], - __iter1: Iterable[Any], - __iter2: Iterable[Any], - __iter3: Iterable[Any], - __iter4: Iterable[Any], - __iter5: Iterable[Any], - __iter6: Iterable[Any], + func: Callable[..., _S], + iter1: Iterable[Any], + iter2: Iterable[Any], + iter3: Iterable[Any], + iter4: Iterable[Any], + iter5: Iterable[Any], + iter6: Iterable[Any], + /, *iterables: Iterable[Any], ) -> Self: ... def __iter__(self) -> Self: ... @@ -1348,37 +1442,37 @@ class map(Iterator[_S]): @overload def max( - __arg1: SupportsRichComparisonT, __arg2: SupportsRichComparisonT, *_args: SupportsRichComparisonT, key: None = None + arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, key: None = None ) -> SupportsRichComparisonT: ... @overload -def max(__arg1: _T, __arg2: _T, *_args: _T, key: Callable[[_T], SupportsRichComparison]) -> _T: ... +def max(arg1: _T, arg2: _T, /, *_args: _T, key: Callable[[_T], SupportsRichComparison]) -> _T: ... @overload -def max(__iterable: Iterable[SupportsRichComparisonT], *, key: None = None) -> SupportsRichComparisonT: ... +def max(iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT: ... @overload -def max(__iterable: Iterable[_T], *, key: Callable[[_T], SupportsRichComparison]) -> _T: ... +def max(iterable: Iterable[_T], /, *, key: Callable[[_T], SupportsRichComparison]) -> _T: ... @overload -def max(__iterable: Iterable[SupportsRichComparisonT], *, key: None = None, default: _T) -> SupportsRichComparisonT | _T: ... +def max(iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T: ... @overload -def max(__iterable: Iterable[_T1], *, key: Callable[[_T1], SupportsRichComparison], default: _T2) -> _T1 | _T2: ... +def max(iterable: Iterable[_T1], /, *, key: Callable[[_T1], SupportsRichComparison], default: _T2) -> _T1 | _T2: ... @overload def min( - __arg1: SupportsRichComparisonT, __arg2: SupportsRichComparisonT, *_args: SupportsRichComparisonT, key: None = None + arg1: SupportsRichComparisonT, arg2: SupportsRichComparisonT, /, *_args: SupportsRichComparisonT, key: None = None ) -> SupportsRichComparisonT: ... @overload -def min(__arg1: _T, __arg2: _T, *_args: _T, key: Callable[[_T], SupportsRichComparison]) -> _T: ... +def min(arg1: _T, arg2: _T, /, *_args: _T, key: Callable[[_T], SupportsRichComparison]) -> _T: ... @overload -def min(__iterable: Iterable[SupportsRichComparisonT], *, key: None = None) -> SupportsRichComparisonT: ... +def min(iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None) -> SupportsRichComparisonT: ... @overload -def min(__iterable: Iterable[_T], *, key: Callable[[_T], SupportsRichComparison]) -> _T: ... +def min(iterable: Iterable[_T], /, *, key: Callable[[_T], SupportsRichComparison]) -> _T: ... @overload -def min(__iterable: Iterable[SupportsRichComparisonT], *, key: None = None, default: _T) -> SupportsRichComparisonT | _T: ... +def min(iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, default: _T) -> SupportsRichComparisonT | _T: ... @overload -def min(__iterable: Iterable[_T1], *, key: Callable[[_T1], SupportsRichComparison], default: _T2) -> _T1 | _T2: ... +def min(iterable: Iterable[_T1], /, *, key: Callable[[_T1], SupportsRichComparison], default: _T2) -> _T1 | _T2: ... @overload -def next(__i: SupportsNext[_T]) -> _T: ... +def next(i: SupportsNext[_T], /) -> _T: ... @overload -def next(__i: SupportsNext[_T], __default: _VT) -> _T | _VT: ... -def oct(__number: int | SupportsIndex) -> str: ... +def next(i: SupportsNext[_T], default: _VT, /) -> _T | _VT: ... +def oct(number: int | SupportsIndex, /) -> str: ... _Opener: TypeAlias = Callable[[str, int], int] @@ -1468,7 +1562,7 @@ def open( closefd: bool = True, opener: _Opener | None = None, ) -> IO[Any]: ... -def ord(__c: str | bytes | bytearray) -> int: ... +def ord(c: str | bytes | bytearray, /) -> int: ... class _SupportsWriteAndFlush(SupportsWrite[_T_contra], SupportsFlush, Protocol[_T_contra]): ... @@ -1489,13 +1583,13 @@ _E = TypeVar("_E", contravariant=True) _M = TypeVar("_M", contravariant=True) class _SupportsPow2(Protocol[_E, _T_co]): - def __pow__(self, __other: _E) -> _T_co: ... + def __pow__(self, other: _E, /) -> _T_co: ... class _SupportsPow3NoneOnly(Protocol[_E, _T_co]): - def __pow__(self, __other: _E, __modulo: None = None) -> _T_co: ... + def __pow__(self, other: _E, modulo: None = None, /) -> _T_co: ... class _SupportsPow3(Protocol[_E, _M, _T_co]): - def __pow__(self, __other: _E, __modulo: _M) -> _T_co: ... + def __pow__(self, other: _E, modulo: _M, /) -> _T_co: ... _SupportsSomeKindOfPow = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] @@ -1544,14 +1638,14 @@ def quit(code: sys._ExitCode = None) -> NoReturn: ... class reversed(Iterator[_T]): @overload - def __new__(cls, __sequence: Reversible[_T]) -> Iterator[_T]: ... # type: ignore[misc] + def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] @overload - def __new__(cls, __sequence: SupportsLenAndGetItem[_T]) -> Iterator[_T]: ... # type: ignore[misc] + def __new__(cls, sequence: SupportsLenAndGetItem[_T], /) -> Iterator[_T]: ... # type: ignore[misc] def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def __length_hint__(self) -> int: ... -def repr(__obj: object) -> str: ... +def repr(obj: object, /) -> str: ... # See https://github.com/python/typeshed/pull/9141 # and https://github.com/python/typeshed/pull/9151 @@ -1561,7 +1655,7 @@ class _SupportsRound1(Protocol[_T_co]): def __round__(self) -> _T_co: ... class _SupportsRound2(Protocol[_T_co]): - def __round__(self, __ndigits: int) -> _T_co: ... + def __round__(self, ndigits: int, /) -> _T_co: ... @overload def round(number: _SupportsRound1[_T], ndigits: None = None) -> _T: ... @@ -1570,13 +1664,13 @@ def round(number: _SupportsRound2[_T], ndigits: SupportsIndex) -> _T: ... # See https://github.com/python/typeshed/pull/6292#discussion_r748875189 # for why arg 3 of `setattr` should be annotated with `Any` and not `object` -def setattr(__obj: object, __name: str, __value: Any) -> None: ... +def setattr(obj: object, name: str, value: Any, /) -> None: ... @overload def sorted( - __iterable: Iterable[SupportsRichComparisonT], *, key: None = None, reverse: bool = False + iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, reverse: bool = False ) -> list[SupportsRichComparisonT]: ... @overload -def sorted(__iterable: Iterable[_T], *, key: Callable[[_T], SupportsRichComparison], reverse: bool = False) -> list[_T]: ... +def sorted(iterable: Iterable[_T], /, *, key: Callable[[_T], SupportsRichComparison], reverse: bool = False) -> list[_T]: ... _AddableT1 = TypeVar("_AddableT1", bound=SupportsAdd[Any, Any]) _AddableT2 = TypeVar("_AddableT2", bound=SupportsAdd[Any, Any]) @@ -1590,62 +1684,58 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(__iterable: Iterable[bool], start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload -def sum(__iterable: Iterable[_SupportsSumNoDefaultT]) -> _SupportsSumNoDefaultT | Literal[0]: ... +def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload -def sum(__iterable: Iterable[_AddableT1], start: _AddableT2) -> _AddableT1 | _AddableT2: ... +def sum(iterable: Iterable[_AddableT1], /, start: _AddableT2) -> _AddableT1 | _AddableT2: ... # The argument to `vars()` has to have a `__dict__` attribute, so the second overload can't be annotated with `object` # (A "SupportsDunderDict" protocol doesn't work) # Use a type: ignore to make complaints about overlapping overloads go away @overload -def vars(__object: type) -> types.MappingProxyType[str, Any]: ... # type: ignore[overload-overlap] +def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... # type: ignore[overload-overlap] @overload -def vars(__object: Any = ...) -> dict[str, Any]: ... +def vars(object: Any = ..., /) -> dict[str, Any]: ... class zip(Iterator[_T_co]): if sys.version_info >= (3, 10): @overload def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], *, strict: bool = ...) -> zip[tuple[_T1]]: ... + def __new__(cls, iter1: Iterable[_T1], /, *, strict: bool = ...) -> zip[tuple[_T1]]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], *, strict: bool = ...) -> zip[tuple[_T1, _T2]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = ...) -> zip[tuple[_T1, _T2]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], *, strict: bool = ... + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, strict: bool = ... ) -> zip[tuple[_T1, _T2, _T3]]: ... @overload def __new__( - cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - *, - strict: bool = ..., + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, strict: bool = ... ) -> zip[tuple[_T1, _T2, _T3, _T4]]: ... @overload def __new__( cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + /, *, strict: bool = ..., ) -> zip[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def __new__( cls, - __iter1: Iterable[Any], - __iter2: Iterable[Any], - __iter3: Iterable[Any], - __iter4: Iterable[Any], - __iter5: Iterable[Any], - __iter6: Iterable[Any], + iter1: Iterable[Any], + iter2: Iterable[Any], + iter3: Iterable[Any], + iter4: Iterable[Any], + iter5: Iterable[Any], + iter6: Iterable[Any], + /, *iterables: Iterable[Any], strict: bool = ..., ) -> zip[tuple[Any, ...]]: ... @@ -1653,33 +1743,29 @@ class zip(Iterator[_T_co]): @overload def __new__(cls) -> zip[Any]: ... @overload - def __new__(cls, __iter1: Iterable[_T1]) -> zip[tuple[_T1]]: ... + def __new__(cls, iter1: Iterable[_T1], /) -> zip[tuple[_T1]]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> zip[tuple[_T1, _T2]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip[tuple[_T1, _T2]]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3]) -> zip[tuple[_T1, _T2, _T3]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> zip[tuple[_T1, _T2, _T3]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4] + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], / ) -> zip[tuple[_T1, _T2, _T3, _T4]]: ... @overload def __new__( - cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], / ) -> zip[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def __new__( cls, - __iter1: Iterable[Any], - __iter2: Iterable[Any], - __iter3: Iterable[Any], - __iter4: Iterable[Any], - __iter5: Iterable[Any], - __iter6: Iterable[Any], + iter1: Iterable[Any], + iter2: Iterable[Any], + iter3: Iterable[Any], + iter4: Iterable[Any], + iter5: Iterable[Any], + iter6: Iterable[Any], + /, *iterables: Iterable[Any], ) -> zip[tuple[Any, ...]]: ... @@ -1695,7 +1781,7 @@ def __import__( fromlist: Sequence[str] = (), level: int = 0, ) -> types.ModuleType: ... -def __build_class__(__func: Callable[[], _Cell | Any], __name: str, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... +def __build_class__(func: Callable[[], _Cell | Any], name: str, /, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... if sys.version_info >= (3, 10): from types import EllipsisType @@ -1722,12 +1808,12 @@ class BaseException: __suppress_context__: bool __traceback__: TracebackType | None def __init__(self, *args: object) -> None: ... - def __setstate__(self, __state: dict[str, Any] | None) -> None: ... - def with_traceback(self, __tb: TracebackType | None) -> Self: ... + def __setstate__(self, state: dict[str, Any] | None, /) -> None: ... + def with_traceback(self, tb: TracebackType | None, /) -> Self: ... if sys.version_info >= (3, 11): # only present after add_note() is called __notes__: list[str] - def add_note(self, __note: str) -> None: ... + def add_note(self, note: str, /) -> None: ... class GeneratorExit(BaseException): ... class KeyboardInterrupt(BaseException): ... @@ -1837,7 +1923,7 @@ class UnicodeDecodeError(UnicodeError): start: int end: int reason: str - def __init__(self, __encoding: str, __object: ReadableBuffer, __start: int, __end: int, __reason: str) -> None: ... + def __init__(self, encoding: str, object: ReadableBuffer, start: int, end: int, reason: str, /) -> None: ... class UnicodeEncodeError(UnicodeError): encoding: str @@ -1845,7 +1931,7 @@ class UnicodeEncodeError(UnicodeError): start: int end: int reason: str - def __init__(self, __encoding: str, __object: str, __start: int, __end: int, __reason: str) -> None: ... + def __init__(self, encoding: str, object: str, start: int, end: int, reason: str, /) -> None: ... class UnicodeTranslateError(UnicodeError): encoding: None @@ -1853,7 +1939,7 @@ class UnicodeTranslateError(UnicodeError): start: int end: int reason: str - def __init__(self, __object: str, __start: int, __end: int, __reason: str) -> None: ... + def __init__(self, object: str, start: int, end: int, reason: str, /) -> None: ... class Warning(Exception): ... class UserWarning(Warning): ... @@ -1878,60 +1964,60 @@ if sys.version_info >= (3, 11): # See `check_exception_group.py` for use-cases and comments. class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): - def __new__(cls, __message: str, __exceptions: Sequence[_BaseExceptionT_co]) -> Self: ... - def __init__(self, __message: str, __exceptions: Sequence[_BaseExceptionT_co]) -> None: ... + def __new__(cls, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> Self: ... + def __init__(self, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> None: ... @property def message(self) -> str: ... @property def exceptions(self) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]: ... @overload def subgroup( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> ExceptionGroup[_ExceptionT] | None: ... @overload def subgroup( - self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / ) -> BaseExceptionGroup[_BaseExceptionT] | None: ... @overload def subgroup( - self, __condition: Callable[[_BaseExceptionT_co | Self], bool] + self, condition: Callable[[_BaseExceptionT_co | Self], bool], / ) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ... @overload def split( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> tuple[ExceptionGroup[_ExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... @overload def split( - self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / ) -> tuple[BaseExceptionGroup[_BaseExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... @overload def split( - self, __condition: Callable[[_BaseExceptionT_co | Self], bool] + self, condition: Callable[[_BaseExceptionT_co | Self], bool], / ) -> tuple[BaseExceptionGroup[_BaseExceptionT_co] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... # In reality it is `NonEmptySequence`: @overload - def derive(self, __excs: Sequence[_ExceptionT]) -> ExceptionGroup[_ExceptionT]: ... + def derive(self, excs: Sequence[_ExceptionT], /) -> ExceptionGroup[_ExceptionT]: ... @overload - def derive(self, __excs: Sequence[_BaseExceptionT]) -> BaseExceptionGroup[_BaseExceptionT]: ... - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def derive(self, excs: Sequence[_BaseExceptionT], /) -> BaseExceptionGroup[_BaseExceptionT]: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): - def __new__(cls, __message: str, __exceptions: Sequence[_ExceptionT_co]) -> Self: ... - def __init__(self, __message: str, __exceptions: Sequence[_ExceptionT_co]) -> None: ... + def __new__(cls, message: str, exceptions: Sequence[_ExceptionT_co], /) -> Self: ... + def __init__(self, message: str, exceptions: Sequence[_ExceptionT_co], /) -> None: ... @property def exceptions(self) -> tuple[_ExceptionT_co | ExceptionGroup[_ExceptionT_co], ...]: ... # We accept a narrower type, but that's OK. @overload # type: ignore[override] def subgroup( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> ExceptionGroup[_ExceptionT] | None: ... @overload - def subgroup(self, __condition: Callable[[_ExceptionT_co | Self], bool]) -> ExceptionGroup[_ExceptionT_co] | None: ... + def subgroup(self, condition: Callable[[_ExceptionT_co | Self], bool], /) -> ExceptionGroup[_ExceptionT_co] | None: ... @overload # type: ignore[override] def split( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> tuple[ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None]: ... @overload def split( - self, __condition: Callable[[_ExceptionT_co | Self], bool] + self, condition: Callable[[_ExceptionT_co | Self], bool], / ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ... diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index 620d59a6010c..a7837e1b9ff8 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -14,7 +14,7 @@ __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "d class _ReadableFileobj(_compression._Reader, Protocol): ... class _WritableFileobj(Protocol): - def write(self, __b: bytes) -> object: ... + def write(self, b: bytes, /) -> object: ... # The following attributes and methods are optional: # def fileno(self) -> int: ... # def close(self) -> object: ... @@ -132,7 +132,7 @@ class BZ2File(BaseStream, IO[bytes]): @final class BZ2Compressor: def __init__(self, compresslevel: int = ...) -> None: ... - def compress(self, __data: ReadableBuffer) -> bytes: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... def flush(self) -> bytes: ... @final diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index c212f0383eaf..0cf6e34ec99e 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -24,7 +24,7 @@ class Profile(_lsprof.Profiler): def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... - def runcall(self, __func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... + def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def __enter__(self) -> Self: ... def __exit__(self, *exc_info: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index 91179c2ed8d5..d20be33e3d76 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -33,7 +33,7 @@ def parse_multipart( ) -> dict[str, list[Any]]: ... class _Environ(Protocol): - def __getitem__(self, __k: str) -> str: ... + def __getitem__(self, k: str, /) -> str: ... def keys(self) -> Iterable[str]: ... def parse_header(line: str) -> tuple[str, dict[str, str]]: ... diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index 8aad19dafcfb..fab9d10230f8 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -11,26 +11,26 @@ tau: float _C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex -def acos(__z: _C) -> complex: ... -def acosh(__z: _C) -> complex: ... -def asin(__z: _C) -> complex: ... -def asinh(__z: _C) -> complex: ... -def atan(__z: _C) -> complex: ... -def atanh(__z: _C) -> complex: ... -def cos(__z: _C) -> complex: ... -def cosh(__z: _C) -> complex: ... -def exp(__z: _C) -> complex: ... +def acos(z: _C, /) -> complex: ... +def acosh(z: _C, /) -> complex: ... +def asin(z: _C, /) -> complex: ... +def asinh(z: _C, /) -> complex: ... +def atan(z: _C, /) -> complex: ... +def atanh(z: _C, /) -> complex: ... +def cos(z: _C, /) -> complex: ... +def cosh(z: _C, /) -> complex: ... +def exp(z: _C, /) -> complex: ... def isclose(a: _C, b: _C, *, rel_tol: SupportsFloat = 1e-09, abs_tol: SupportsFloat = 0.0) -> bool: ... -def isinf(__z: _C) -> bool: ... -def isnan(__z: _C) -> bool: ... -def log(__x: _C, __base: _C = ...) -> complex: ... -def log10(__z: _C) -> complex: ... -def phase(__z: _C) -> float: ... -def polar(__z: _C) -> tuple[float, float]: ... -def rect(__r: float, __phi: float) -> complex: ... -def sin(__z: _C) -> complex: ... -def sinh(__z: _C) -> complex: ... -def sqrt(__z: _C) -> complex: ... -def tan(__z: _C) -> complex: ... -def tanh(__z: _C) -> complex: ... -def isfinite(__z: _C) -> bool: ... +def isinf(z: _C, /) -> bool: ... +def isnan(z: _C, /) -> bool: ... +def log(x: _C, base: _C = ..., /) -> complex: ... +def log10(z: _C, /) -> complex: ... +def phase(z: _C, /) -> float: ... +def polar(z: _C, /) -> tuple[float, float]: ... +def rect(r: float, phi: float, /) -> complex: ... +def sin(z: _C, /) -> complex: ... +def sinh(z: _C, /) -> complex: ... +def sqrt(z: _C, /) -> complex: ... +def tan(z: _C, /) -> complex: ... +def tanh(z: _C, /) -> complex: ... +def isfinite(z: _C, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 7e192d91ddc5..6e53b780c473 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -59,13 +59,13 @@ BOM64_BE: Literal[b"\x00\x00\xfe\xff"] BOM64_LE: Literal[b"\xff\xfe\x00\x00"] class _WritableStream(Protocol): - def write(self, __data: bytes) -> object: ... - def seek(self, __offset: int, __whence: int) -> object: ... + def write(self, data: bytes, /) -> object: ... + def seek(self, offset: int, whence: int, /) -> object: ... def close(self) -> object: ... class _ReadableStream(Protocol): - def read(self, __size: int = ...) -> bytes: ... - def seek(self, __offset: int, __whence: int) -> object: ... + def read(self, size: int = ..., /) -> bytes: ... + def seek(self, offset: int, whence: int, /) -> object: ... def close(self) -> object: ... class _Stream(_WritableStream, _ReadableStream, Protocol): ... @@ -77,16 +77,16 @@ class _Stream(_WritableStream, _ReadableStream, Protocol): ... # They were much more common in Python 2 than in Python 3. class _Encoder(Protocol): - def __call__(self, __input: str, __errors: str = ...) -> tuple[bytes, int]: ... # signature of Codec().encode + def __call__(self, input: str, errors: str = ..., /) -> tuple[bytes, int]: ... # signature of Codec().encode class _Decoder(Protocol): - def __call__(self, __input: bytes, __errors: str = ...) -> tuple[str, int]: ... # signature of Codec().decode + def __call__(self, input: bytes, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode class _StreamReader(Protocol): - def __call__(self, __stream: _ReadableStream, __errors: str = ...) -> StreamReader: ... + def __call__(self, stream: _ReadableStream, errors: str = ..., /) -> StreamReader: ... class _StreamWriter(Protocol): - def __call__(self, __stream: _WritableStream, __errors: str = ...) -> StreamWriter: ... + def __call__(self, stream: _WritableStream, errors: str = ..., /) -> StreamWriter: ... class _IncrementalEncoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalEncoder: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 0df800a4a3be..1d23ecd66a8d 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -49,21 +49,21 @@ class UserDict(MutableMapping[_KT, _VT]): data: dict[_KT, _VT] # __init__ should be kept roughly in line with `dict.__init__`, which has the same semantics @overload - def __init__(self, __dict: None = None) -> None: ... + def __init__(self, dict: None = None, /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], __dict: None = None, **kwargs: _VT) -> None: ... + def __init__(self: UserDict[str, _VT], dict: None = None, /, **kwargs: _VT) -> None: ... @overload - def __init__(self, __dict: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... + def __init__(self, dict: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], __dict: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> None: ... + def __init__(self: UserDict[str, _VT], dict: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ... @overload - def __init__(self, __iterable: Iterable[tuple[_KT, _VT]]) -> None: ... + def __init__(self, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> None: ... + def __init__(self: UserDict[str, _VT], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... @overload - def __init__(self: UserDict[str, str], __iterable: Iterable[list[str]]) -> None: ... + def __init__(self: UserDict[str, str], iterable: Iterable[list[str]], /) -> None: ... @overload - def __init__(self: UserDict[bytes, bytes], __iterable: Iterable[list[bytes]]) -> None: ... + def __init__(self: UserDict[bytes, bytes], iterable: Iterable[list[bytes]], /) -> None: ... def __len__(self) -> int: ... def __getitem__(self, key: _KT) -> _VT: ... def __setitem__(self, key: _KT, item: _VT) -> None: ... @@ -137,8 +137,10 @@ class UserList(MutableSequence[_T]): def copy(self) -> Self: ... def __copy__(self) -> Self: ... def count(self, item: _T) -> int: ... - # All arguments are passed to `list.index` at runtime, so the signature should be kept in line with `list.index`. - def index(self, item: _T, __start: SupportsIndex = 0, __stop: SupportsIndex = sys.maxsize) -> int: ... + # The runtime signature is "item, *args", and the arguments are then passed + # to `list.index`. In order to give more precise types, we pretend that the + # `item` argument is positional-only. + def index(self, item: _T, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ... # All arguments are passed to `list.sort` at runtime, so the signature should be kept in line with `list.sort`. @overload def sort(self: UserList[SupportsRichComparisonT], *, key: None = None, reverse: bool = False) -> None: ... @@ -200,8 +202,8 @@ class UserString(Sequence[UserString]): maketrans = str.maketrans def partition(self, sep: str) -> tuple[str, str, str]: ... if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: str | UserString) -> Self: ... - def removesuffix(self, __suffix: str | UserString) -> Self: ... + def removeprefix(self, prefix: str | UserString, /) -> Self: ... + def removesuffix(self, suffix: str | UserString, /) -> Self: ... def replace(self, old: str | UserString, new: str | UserString, maxsplit: int = -1) -> Self: ... def rfind(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ... @@ -227,58 +229,58 @@ class deque(MutableSequence[_T]): def __init__(self, *, maxlen: int | None = None) -> None: ... @overload def __init__(self, iterable: Iterable[_T], maxlen: int | None = None) -> None: ... - def append(self, __x: _T) -> None: ... - def appendleft(self, __x: _T) -> None: ... + def append(self, x: _T, /) -> None: ... + def appendleft(self, x: _T, /) -> None: ... def copy(self) -> Self: ... - def count(self, __x: _T) -> int: ... - def extend(self, __iterable: Iterable[_T]) -> None: ... - def extendleft(self, __iterable: Iterable[_T]) -> None: ... - def insert(self, __i: int, __x: _T) -> None: ... - def index(self, __x: _T, __start: int = 0, __stop: int = ...) -> int: ... + def count(self, x: _T, /) -> int: ... + def extend(self, iterable: Iterable[_T], /) -> None: ... + def extendleft(self, iterable: Iterable[_T], /) -> None: ... + def insert(self, i: int, x: _T, /) -> None: ... + def index(self, x: _T, start: int = 0, stop: int = ..., /) -> int: ... def pop(self) -> _T: ... # type: ignore[override] def popleft(self) -> _T: ... - def remove(self, __value: _T) -> None: ... - def rotate(self, __n: int = 1) -> None: ... + def remove(self, value: _T, /) -> None: ... + def rotate(self, n: int = 1, /) -> None: ... def __copy__(self) -> Self: ... def __len__(self) -> int: ... # These methods of deque don't take slices, unlike MutableSequence, hence the type: ignores - def __getitem__(self, __key: SupportsIndex) -> _T: ... # type: ignore[override] - def __setitem__(self, __key: SupportsIndex, __value: _T) -> None: ... # type: ignore[override] - def __delitem__(self, __key: SupportsIndex) -> None: ... # type: ignore[override] - def __contains__(self, __key: object) -> bool: ... + def __getitem__(self, key: SupportsIndex, /) -> _T: ... # type: ignore[override] + def __setitem__(self, key: SupportsIndex, value: _T, /) -> None: ... # type: ignore[override] + def __delitem__(self, key: SupportsIndex, /) -> None: ... # type: ignore[override] + def __contains__(self, key: object, /) -> bool: ... def __reduce__(self) -> tuple[type[Self], tuple[()], None, Iterator[_T]]: ... - def __iadd__(self, __value: Iterable[_T]) -> Self: ... - def __add__(self, __value: Self) -> Self: ... - def __mul__(self, __value: int) -> Self: ... - def __imul__(self, __value: int) -> Self: ... - def __lt__(self, __value: deque[_T]) -> bool: ... - def __le__(self, __value: deque[_T]) -> bool: ... - def __gt__(self, __value: deque[_T]) -> bool: ... - def __ge__(self, __value: deque[_T]) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __iadd__(self, value: Iterable[_T], /) -> Self: ... + def __add__(self, value: Self, /) -> Self: ... + def __mul__(self, value: int, /) -> Self: ... + def __imul__(self, value: int, /) -> Self: ... + def __lt__(self, value: deque[_T], /) -> bool: ... + def __le__(self, value: deque[_T], /) -> bool: ... + def __gt__(self, value: deque[_T], /) -> bool: ... + def __ge__(self, value: deque[_T], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Counter(dict[_T, int], Generic[_T]): @overload - def __init__(self, __iterable: None = None) -> None: ... + def __init__(self, iterable: None = None, /) -> None: ... @overload - def __init__(self: Counter[str], __iterable: None = None, **kwargs: int) -> None: ... + def __init__(self: Counter[str], iterable: None = None, /, **kwargs: int) -> None: ... @overload - def __init__(self, __mapping: SupportsKeysAndGetItem[_T, int]) -> None: ... + def __init__(self, mapping: SupportsKeysAndGetItem[_T, int], /) -> None: ... @overload - def __init__(self, __iterable: Iterable[_T]) -> None: ... + def __init__(self, iterable: Iterable[_T], /) -> None: ... def copy(self) -> Self: ... def elements(self) -> Iterator[_T]: ... def most_common(self, n: int | None = None) -> list[tuple[_T, int]]: ... @classmethod def fromkeys(cls, iterable: Any, v: int | None = None) -> NoReturn: ... # type: ignore[override] @overload - def subtract(self, __iterable: None = None) -> None: ... + def subtract(self, iterable: None = None, /) -> None: ... @overload - def subtract(self, __mapping: Mapping[_T, int]) -> None: ... + def subtract(self, mapping: Mapping[_T, int], /) -> None: ... @overload - def subtract(self, __iterable: Iterable[_T]) -> None: ... + def subtract(self, iterable: Iterable[_T], /) -> None: ... # Unlike dict.update(), use Mapping instead of SupportsKeysAndGetItem for the first overload # (source code does an `isinstance(other, Mapping)` check) # @@ -286,11 +288,11 @@ class Counter(dict[_T, int], Generic[_T]): # (if it were `Iterable[_T] | Iterable[tuple[_T, int]]`, # the tuples would be added as keys, breaking type safety) @overload # type: ignore[override] - def update(self, __m: Mapping[_T, int], **kwargs: int) -> None: ... + def update(self, m: Mapping[_T, int], /, **kwargs: int) -> None: ... @overload - def update(self, __iterable: Iterable[_T], **kwargs: int) -> None: ... + def update(self, iterable: Iterable[_T], /, **kwargs: int) -> None: ... @overload - def update(self, __iterable: None = None, **kwargs: int) -> None: ... + def update(self, iterable: None = None, /, **kwargs: int) -> None: ... def __missing__(self, key: _T) -> int: ... def __delitem__(self, elem: object) -> None: ... if sys.version_info >= (3, 10): @@ -371,16 +373,16 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): def pop(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 9): @overload - def __or__(self, __value: dict[_KT, _VT]) -> Self: ... + def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload - def __or__(self, __value: dict[_T1, _T2]) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... @overload - def __ror__(self, __value: dict[_KT, _VT]) -> Self: ... + def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... @overload - def __ror__(self, __value: dict[_T1, _T2]) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] class defaultdict(dict[_KT, _VT]): default_factory: Callable[[], _VT] | None @@ -389,39 +391,41 @@ class defaultdict(dict[_KT, _VT]): @overload def __init__(self: defaultdict[str, _VT], **kwargs: _VT) -> None: ... @overload - def __init__(self, __default_factory: Callable[[], _VT] | None) -> None: ... + def __init__(self, default_factory: Callable[[], _VT] | None, /) -> None: ... @overload - def __init__(self: defaultdict[str, _VT], __default_factory: Callable[[], _VT] | None, **kwargs: _VT) -> None: ... + def __init__(self: defaultdict[str, _VT], default_factory: Callable[[], _VT] | None, /, **kwargs: _VT) -> None: ... @overload - def __init__(self, __default_factory: Callable[[], _VT] | None, __map: SupportsKeysAndGetItem[_KT, _VT]) -> None: ... + def __init__(self, default_factory: Callable[[], _VT] | None, map: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload def __init__( self: defaultdict[str, _VT], - __default_factory: Callable[[], _VT] | None, - __map: SupportsKeysAndGetItem[str, _VT], + default_factory: Callable[[], _VT] | None, + map: SupportsKeysAndGetItem[str, _VT], + /, **kwargs: _VT, ) -> None: ... @overload - def __init__(self, __default_factory: Callable[[], _VT] | None, __iterable: Iterable[tuple[_KT, _VT]]) -> None: ... + def __init__(self, default_factory: Callable[[], _VT] | None, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload def __init__( self: defaultdict[str, _VT], - __default_factory: Callable[[], _VT] | None, - __iterable: Iterable[tuple[str, _VT]], + default_factory: Callable[[], _VT] | None, + iterable: Iterable[tuple[str, _VT]], + /, **kwargs: _VT, ) -> None: ... - def __missing__(self, __key: _KT) -> _VT: ... + def __missing__(self, key: _KT, /) -> _VT: ... def __copy__(self) -> Self: ... def copy(self) -> Self: ... if sys.version_info >= (3, 9): @overload - def __or__(self, __value: dict[_KT, _VT]) -> Self: ... + def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload - def __or__(self, __value: dict[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... + def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... @overload - def __ror__(self, __value: dict[_KT, _VT]) -> Self: ... + def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... @overload - def __ror__(self, __value: dict[_T1, _T2]) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] class ChainMap(MutableMapping[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] @@ -457,10 +461,14 @@ class ChainMap(MutableMapping[_KT, _VT]): # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, so the signature should be kept in line with `dict.fromkeys`. @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], __value: None = None) -> ChainMap[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ... @classmethod @overload - def fromkeys(cls, __iterable: Iterable[_T], __value: _S) -> ChainMap[_T, _S]: ... + # Special-case None: the user probably wants to add non-None values later. + def fromkeys(cls, iterable: Iterable[_T], value: None, /) -> ChainMap[_T, Any | None]: ... + @classmethod + @overload + def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> ChainMap[_T, _S]: ... if sys.version_info >= (3, 9): @overload def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi index 7f101bf79f6d..9fb3608f2979 100644 --- a/mypy/typeshed/stdlib/compileall.pyi +++ b/mypy/typeshed/stdlib/compileall.pyi @@ -6,7 +6,7 @@ from typing import Any, Protocol __all__ = ["compile_dir", "compile_file", "compile_path"] class _SupportsSearch(Protocol): - def search(self, __string: str) -> Any: ... + def search(self, string: str, /) -> Any: ... if sys.version_info >= (3, 10): def compile_dir( diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 9ea4d5dff6fb..7dfdda224013 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -1,7 +1,7 @@ import sys import threading from _typeshed import Unused -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Callable, Collection, Iterable, Iterator from logging import Logger from types import TracebackType from typing import Any, Generic, Literal, NamedTuple, Protocol, TypeVar @@ -58,7 +58,7 @@ class Future(Generic[_T]): class Executor: if sys.version_info >= (3, 9): - def submit(self, __fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... + def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... else: def submit(self, fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... @@ -91,9 +91,15 @@ class DoneAndNotDoneFutures(NamedTuple, Generic[_T]): done: set[Future[_T]] not_done: set[Future[_T]] -def wait( - fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" -) -> DoneAndNotDoneFutures[_T]: ... +if sys.version_info >= (3, 9): + def wait( + fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> DoneAndNotDoneFutures[_T]: ... + +else: + def wait( + fs: Collection[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" + ) -> DoneAndNotDoneFutures[_T]: ... class _Waiter: event: threading.Event diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index eb4e95b33509..f82bb4b7b6ad 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -42,7 +42,7 @@ class AbstractContextManager(Protocol[_T_co]): def __enter__(self) -> _T_co: ... @abstractmethod def __exit__( - self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> bool | None: ... @runtime_checkable @@ -50,7 +50,7 @@ class AbstractAsyncContextManager(Protocol[_T_co]): async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( - self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> bool | None: ... class ContextDecorator: @@ -145,12 +145,12 @@ class redirect_stderr(_RedirectStream[_T_io]): ... class ExitStack(metaclass=abc.ABCMeta): def enter_context(self, cm: AbstractContextManager[_T]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... - def callback(self, __callback: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... + def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def pop_all(self) -> Self: ... def close(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> bool: ... _ExitCoroFunc: TypeAlias = Callable[ @@ -165,15 +165,15 @@ class AsyncExitStack(metaclass=abc.ABCMeta): async def enter_async_context(self, cm: AbstractAsyncContextManager[_T]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... def push_async_exit(self, exit: _ACM_EF) -> _ACM_EF: ... - def callback(self, __callback: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... + def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def push_async_callback( - self, __callback: Callable[_P, Awaitable[_T]], *args: _P.args, **kwds: _P.kwargs + self, callback: Callable[_P, Awaitable[_T]], /, *args: _P.args, **kwds: _P.kwargs ) -> Callable[_P, Awaitable[_T]]: ... def pop_all(self) -> Self: ... async def aclose(self) -> None: ... async def __aenter__(self) -> Self: ... async def __aexit__( - self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> bool: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index 3245cdd5dad2..ceb9085fa187 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -24,11 +24,11 @@ class ContextVar(Generic[_T]): @overload def get(self) -> _T: ... @overload - def get(self, __default: _T) -> _T: ... + def get(self, default: _T, /) -> _T: ... @overload - def get(self, __default: _D) -> _D | _T: ... - def set(self, __value: _T) -> Token[_T]: ... - def reset(self, __token: Token[_T]) -> None: ... + def get(self, default: _D, /) -> _D | _T: ... + def set(self, value: _T, /) -> Token[_T]: ... + def reset(self, token: Token[_T], /) -> None: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... @@ -50,14 +50,14 @@ def copy_context() -> Context: ... class Context(Mapping[ContextVar[Any], Any]): def __init__(self) -> None: ... @overload - def get(self, __key: ContextVar[_T], __default: None = None) -> _T | None: ... + def get(self, key: ContextVar[_T], default: None = None, /) -> _T | None: ... @overload - def get(self, __key: ContextVar[_T], __default: _T) -> _T: ... + def get(self, key: ContextVar[_T], default: _T, /) -> _T: ... @overload - def get(self, __key: ContextVar[_T], __default: _D) -> _T | _D: ... + def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... def copy(self) -> Context: ... - def __getitem__(self, __key: ContextVar[_T]) -> _T: ... + def __getitem__(self, key: ContextVar[_T], /) -> _T: ... def __iter__(self) -> Iterator[ContextVar[Any]]: ... def __len__(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index db44fa6a6be7..2a82ae9bda22 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -18,4 +18,4 @@ if sys.platform != "win32": COLORS: int COLOR_PAIRS: int - def wrapper(__func: Callable[Concatenate[_CursesWindow, _P], _T], *arg: _P.args, **kwds: _P.kwargs) -> _T: ... + def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... diff --git a/mypy/typeshed/stdlib/curses/panel.pyi b/mypy/typeshed/stdlib/curses/panel.pyi index 30803791f039..403ae9b50019 100644 --- a/mypy/typeshed/stdlib/curses/panel.pyi +++ b/mypy/typeshed/stdlib/curses/panel.pyi @@ -20,6 +20,6 @@ if sys.platform != "win32": def window(self) -> _CursesWindow: ... def bottom_panel() -> _Curses_Panel: ... - def new_panel(__win: _CursesWindow) -> _Curses_Panel: ... + def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ... def top_panel() -> _Curses_Panel: ... def update_panels() -> _Curses_Panel: ... diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 389a159a915f..00e0d31d092a 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -55,9 +55,9 @@ def astuple(obj: DataclassInstance) -> tuple[Any, ...]: ... @overload def astuple(obj: DataclassInstance, *, tuple_factory: Callable[[list[Any]], _T]) -> _T: ... @overload -def dataclass(__cls: None) -> Callable[[type[_T]], type[_T]]: ... +def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ... @overload -def dataclass(__cls: type[_T]) -> type[_T]: ... +def dataclass(cls: type[_T], /) -> type[_T]: ... if sys.version_info >= (3, 11): @overload @@ -227,16 +227,18 @@ if sys.version_info >= (3, 9): else: class _InitVarMeta(type): # Not used, instead `InitVar.__class_getitem__` is called. - def __getitem__(self, params: Any) -> InitVar[Any]: ... + # pyright ignore is needed because pyright (not unreasonably) thinks this + # is an invalid use of InitVar. + def __getitem__(self, params: Any) -> InitVar[Any]: ... # pyright: ignore class InitVar(Generic[_T], metaclass=_InitVarMeta): type: Type[_T] def __init__(self, type: Type[_T]) -> None: ... if sys.version_info >= (3, 9): @overload - def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... + def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore @overload - def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... + def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore if sys.version_info >= (3, 12): def make_dataclass( @@ -310,4 +312,4 @@ else: frozen: bool = False, ) -> type: ... -def replace(__obj: _DataclassT, **changes: Any) -> _DataclassT: ... +def replace(obj: _DataclassT, /, **changes: Any) -> _DataclassT: ... diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 852208cd83a1..7b890ca010dc 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -14,12 +14,12 @@ MAXYEAR: Literal[9999] class tzinfo: @abstractmethod - def tzname(self, __dt: datetime | None) -> str | None: ... + def tzname(self, dt: datetime | None, /) -> str | None: ... @abstractmethod - def utcoffset(self, __dt: datetime | None) -> timedelta | None: ... + def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... @abstractmethod - def dst(self, __dt: datetime | None) -> timedelta | None: ... - def fromutc(self, __dt: datetime) -> datetime: ... + def dst(self, dt: datetime | None, /) -> timedelta | None: ... + def fromutc(self, dt: datetime, /) -> datetime: ... # Alias required to avoid name conflicts with date(time).tzinfo. _TzInfo: TypeAlias = tzinfo @@ -30,11 +30,11 @@ class timezone(tzinfo): min: ClassVar[timezone] max: ClassVar[timezone] def __init__(self, offset: timedelta, name: str = ...) -> None: ... - def tzname(self, __dt: datetime | None) -> str: ... - def utcoffset(self, __dt: datetime | None) -> timedelta: ... - def dst(self, __dt: datetime | None) -> None: ... + def tzname(self, dt: datetime | None, /) -> str: ... + def utcoffset(self, dt: datetime | None, /) -> timedelta: ... + def dst(self, dt: datetime | None, /) -> None: ... def __hash__(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 11): UTC: timezone @@ -51,13 +51,13 @@ class date: resolution: ClassVar[timedelta] def __new__(cls, year: SupportsIndex, month: SupportsIndex, day: SupportsIndex) -> Self: ... @classmethod - def fromtimestamp(cls, __timestamp: float) -> Self: ... + def fromtimestamp(cls, timestamp: float, /) -> Self: ... @classmethod def today(cls) -> Self: ... @classmethod - def fromordinal(cls, __n: int) -> Self: ... + def fromordinal(cls, n: int, /) -> Self: ... @classmethod - def fromisoformat(cls, __date_string: str) -> Self: ... + def fromisoformat(cls, date_string: str, /) -> Self: ... @classmethod def fromisocalendar(cls, year: int, week: int, day: int) -> Self: ... @property @@ -73,26 +73,26 @@ class date: if sys.version_info >= (3, 12): def strftime(self, format: str) -> str: ... else: - def strftime(self, __format: str) -> str: ... + def strftime(self, format: str, /) -> str: ... - def __format__(self, __fmt: str) -> str: ... + def __format__(self, fmt: str, /) -> str: ... def isoformat(self) -> str: ... def timetuple(self) -> struct_time: ... def toordinal(self) -> int: ... def replace(self, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ... - def __le__(self, __value: date) -> bool: ... - def __lt__(self, __value: date) -> bool: ... - def __ge__(self, __value: date) -> bool: ... - def __gt__(self, __value: date) -> bool: ... - def __eq__(self, __value: object) -> bool: ... - def __add__(self, __value: timedelta) -> Self: ... - def __radd__(self, __value: timedelta) -> Self: ... + def __le__(self, value: date, /) -> bool: ... + def __lt__(self, value: date, /) -> bool: ... + def __ge__(self, value: date, /) -> bool: ... + def __gt__(self, value: date, /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... + def __add__(self, value: timedelta, /) -> Self: ... + def __radd__(self, value: timedelta, /) -> Self: ... @overload - def __sub__(self, __value: datetime) -> NoReturn: ... + def __sub__(self, value: datetime, /) -> NoReturn: ... @overload - def __sub__(self, __value: Self) -> timedelta: ... + def __sub__(self, value: Self, /) -> timedelta: ... @overload - def __sub__(self, __value: timedelta) -> Self: ... + def __sub__(self, value: timedelta, /) -> Self: ... def __hash__(self) -> int: ... def weekday(self) -> int: ... def isoweekday(self) -> int: ... @@ -127,24 +127,24 @@ class time: def tzinfo(self) -> _TzInfo | None: ... @property def fold(self) -> int: ... - def __le__(self, __value: time) -> bool: ... - def __lt__(self, __value: time) -> bool: ... - def __ge__(self, __value: time) -> bool: ... - def __gt__(self, __value: time) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __le__(self, value: time, /) -> bool: ... + def __lt__(self, value: time, /) -> bool: ... + def __ge__(self, value: time, /) -> bool: ... + def __gt__(self, value: time, /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... def isoformat(self, timespec: str = ...) -> str: ... @classmethod - def fromisoformat(cls, __time_string: str) -> Self: ... + def fromisoformat(cls, time_string: str, /) -> Self: ... # On <3.12, the name of the parameter in the pure-Python implementation # didn't match the name in the C implementation, # meaning it is only *safe* to pass it as a keyword argument on 3.12+ if sys.version_info >= (3, 12): def strftime(self, format: str) -> str: ... else: - def strftime(self, __format: str) -> str: ... + def strftime(self, format: str, /) -> str: ... - def __format__(self, __fmt: str) -> str: ... + def __format__(self, fmt: str, /) -> str: ... def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... @@ -183,30 +183,30 @@ class timedelta: @property def microseconds(self) -> int: ... def total_seconds(self) -> float: ... - def __add__(self, __value: timedelta) -> timedelta: ... - def __radd__(self, __value: timedelta) -> timedelta: ... - def __sub__(self, __value: timedelta) -> timedelta: ... - def __rsub__(self, __value: timedelta) -> timedelta: ... + def __add__(self, value: timedelta, /) -> timedelta: ... + def __radd__(self, value: timedelta, /) -> timedelta: ... + def __sub__(self, value: timedelta, /) -> timedelta: ... + def __rsub__(self, value: timedelta, /) -> timedelta: ... def __neg__(self) -> timedelta: ... def __pos__(self) -> timedelta: ... def __abs__(self) -> timedelta: ... - def __mul__(self, __value: float) -> timedelta: ... - def __rmul__(self, __value: float) -> timedelta: ... + def __mul__(self, value: float, /) -> timedelta: ... + def __rmul__(self, value: float, /) -> timedelta: ... @overload - def __floordiv__(self, __value: timedelta) -> int: ... + def __floordiv__(self, value: timedelta, /) -> int: ... @overload - def __floordiv__(self, __value: int) -> timedelta: ... + def __floordiv__(self, value: int, /) -> timedelta: ... @overload - def __truediv__(self, __value: timedelta) -> float: ... + def __truediv__(self, value: timedelta, /) -> float: ... @overload - def __truediv__(self, __value: float) -> timedelta: ... - def __mod__(self, __value: timedelta) -> timedelta: ... - def __divmod__(self, __value: timedelta) -> tuple[int, timedelta]: ... - def __le__(self, __value: timedelta) -> bool: ... - def __lt__(self, __value: timedelta) -> bool: ... - def __ge__(self, __value: timedelta) -> bool: ... - def __gt__(self, __value: timedelta) -> bool: ... - def __eq__(self, __value: object) -> bool: ... + def __truediv__(self, value: float, /) -> timedelta: ... + def __mod__(self, value: timedelta, /) -> timedelta: ... + def __divmod__(self, value: timedelta, /) -> tuple[int, timedelta]: ... + def __le__(self, value: timedelta, /) -> bool: ... + def __lt__(self, value: timedelta, /) -> bool: ... + def __ge__(self, value: timedelta, /) -> bool: ... + def __gt__(self, value: timedelta, /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __bool__(self) -> bool: ... def __hash__(self) -> int: ... @@ -246,11 +246,11 @@ class datetime(date): def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = ...) -> Self: ... else: @classmethod - def fromtimestamp(cls, __timestamp: float, tz: _TzInfo | None = ...) -> Self: ... + def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ... @classmethod @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.UTC)") - def utcfromtimestamp(cls, __t: float) -> Self: ... + def utcfromtimestamp(cls, t: float, /) -> Self: ... @classmethod def now(cls, tz: _TzInfo | None = None) -> Self: ... @classmethod @@ -279,17 +279,17 @@ class datetime(date): def astimezone(self, tz: _TzInfo | None = ...) -> Self: ... def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... @classmethod - def strptime(cls, __date_string: str, __format: str) -> Self: ... + def strptime(cls, date_string: str, format: str, /) -> Self: ... def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... - def __le__(self, __value: datetime) -> bool: ... # type: ignore[override] - def __lt__(self, __value: datetime) -> bool: ... # type: ignore[override] - def __ge__(self, __value: datetime) -> bool: ... # type: ignore[override] - def __gt__(self, __value: datetime) -> bool: ... # type: ignore[override] - def __eq__(self, __value: object) -> bool: ... + def __le__(self, value: datetime, /) -> bool: ... # type: ignore[override] + def __lt__(self, value: datetime, /) -> bool: ... # type: ignore[override] + def __ge__(self, value: datetime, /) -> bool: ... # type: ignore[override] + def __gt__(self, value: datetime, /) -> bool: ... # type: ignore[override] + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload # type: ignore[override] - def __sub__(self, __value: Self) -> timedelta: ... + def __sub__(self, value: Self, /) -> timedelta: ... @overload - def __sub__(self, __value: timedelta) -> Self: ... + def __sub__(self, value: timedelta, /) -> Self: ... diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index 0f818ed5e7f5..8b562019fcfb 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -38,4 +38,4 @@ if sys.platform != "win32": __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] - def open(__filename: str, __flags: str = "r", __mode: int = 0o666) -> _gdbm: ... + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index a7a6d52d8f19..5eb84e6949fc 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -34,4 +34,4 @@ if sys.platform != "win32": __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] - def open(__filename: str, __flags: str = "r", __mode: int = 0o666) -> _dbm: ... + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index 7b0bdd1b35bd..c41c8ba19a8b 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -53,5 +53,5 @@ def setup( password: str = ..., fullname: str = ..., **attrs: Any, -) -> None: ... +) -> Distribution: ... def run_setup(script_name: str, script_args: list[str] | None = None, stop_after: str = "run") -> Distribution: ... diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index f8de016ab8bf..2d12df337207 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -25,7 +25,7 @@ class Charset: @overload def body_encode(self, string: str | bytes) -> str: ... def __eq__(self, other: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... def add_charset( charset: str, header_enc: int | None = None, body_enc: int | None = None, output_charset: str | None = None diff --git a/mypy/typeshed/stdlib/email/header.pyi b/mypy/typeshed/stdlib/email/header.pyi index fc9d73331bae..212132c6be18 100644 --- a/mypy/typeshed/stdlib/email/header.pyi +++ b/mypy/typeshed/stdlib/email/header.pyi @@ -17,7 +17,7 @@ class Header: def append(self, s: bytes | bytearray | str, charset: Charset | str | None = None, errors: str = "strict") -> None: ... def encode(self, splitchars: str = ";, \t", maxlinelen: int | None = None, linesep: str = "\n") -> str: ... def __eq__(self, other: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... # decode_header() either returns list[tuple[str, None]] if the header # contains no encoded parts, or list[tuple[bytes, str | None]] if the header diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi index 93a2b3ee72b5..2ffdca9b2f22 100644 --- a/mypy/typeshed/stdlib/email/headerregistry.pyi +++ b/mypy/typeshed/stdlib/email/headerregistry.pyi @@ -140,9 +140,9 @@ class MessageIDHeader: class _HeaderParser(Protocol): max_count: ClassVar[Literal[1] | None] @staticmethod - def value_parser(__value: str) -> TokenList: ... + def value_parser(value: str, /) -> TokenList: ... @classmethod - def parse(cls, __value: str, __kwds: dict[str, Any]) -> None: ... + def parse(cls, value: str, kwds: dict[str, Any], /) -> None: ... class HeaderRegistry: registry: dict[str, type[_HeaderParser]] diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 7384f3146a8e..d7d7e8c8e908 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -15,16 +15,20 @@ _PayloadType: TypeAlias = Message | str _EncodedPayloadType: TypeAlias = Message | bytes _MultipartPayloadType: TypeAlias = list[_PayloadType] _CharsetType: TypeAlias = Charset | str | None -# Type returned by Policy.header_fetch_parse, AnyOf[str | Header] +# Type returned by Policy.header_fetch_parse, often str or Header. _HeaderType: TypeAlias = Any -_HeaderTypeParam: TypeAlias = str | Header +# Type accepted by Policy.header_store_parse. +_HeaderTypeParam: TypeAlias = str | Header | Any class _SupportsEncodeToPayload(Protocol): - def encode(self, __encoding: str) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ... + def encode(self, encoding: str, /) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ... class _SupportsDecodeToPayload(Protocol): - def decode(self, __encoding: str, __errors: str) -> _PayloadType | _MultipartPayloadType: ... + def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ... +# TODO: This class should be generic over the header policy and/or the header +# value types allowed by the policy. This depends on PEP 696 support +# (https://github.com/python/typeshed/issues/11422). class Message: policy: Policy # undocumented preamble: str | None diff --git a/mypy/typeshed/stdlib/encodings/utf_8.pyi b/mypy/typeshed/stdlib/encodings/utf_8.pyi index 0de51026f9f5..bb745399eb8c 100644 --- a/mypy/typeshed/stdlib/encodings/utf_8.pyi +++ b/mypy/typeshed/stdlib/encodings/utf_8.pyi @@ -6,16 +6,16 @@ class IncrementalEncoder(codecs.IncrementalEncoder): class IncrementalDecoder(codecs.BufferedIncrementalDecoder): @staticmethod - def _buffer_decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... class StreamWriter(codecs.StreamWriter): @staticmethod - def encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... class StreamReader(codecs.StreamReader): @staticmethod - def decode(__data: ReadableBuffer, __errors: str | None = None, __final: bool = False) -> tuple[str, int]: ... + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... def getregentry() -> codecs.CodecInfo: ... -def encode(__str: str, __errors: str | None = None) -> tuple[bytes, int]: ... +def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 7e5af76c2aae..ccf638205bbe 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -106,22 +106,22 @@ if sys.platform != "win32": FICLONERANGE: int @overload - def fcntl(__fd: FileDescriptorLike, __cmd: int, __arg: int = 0) -> int: ... + def fcntl(fd: FileDescriptorLike, cmd: int, arg: int = 0, /) -> int: ... @overload - def fcntl(__fd: FileDescriptorLike, __cmd: int, __arg: str | ReadOnlyBuffer) -> bytes: ... + def fcntl(fd: FileDescriptorLike, cmd: int, arg: str | ReadOnlyBuffer, /) -> bytes: ... # If arg is an int, return int @overload - def ioctl(__fd: FileDescriptorLike, __request: int, __arg: int = 0, __mutate_flag: bool = True) -> int: ... + def ioctl(fd: FileDescriptorLike, request: int, arg: int = 0, mutate_flag: bool = True, /) -> int: ... # The return type works as follows: # - If arg is a read-write buffer, return int if mutate_flag is True, otherwise bytes # - If arg is a read-only buffer, return bytes (and ignore the value of mutate_flag) # We can't represent that precisely as we can't distinguish between read-write and read-only # buffers, so we add overloads for a few unambiguous cases and use Any for the rest. @overload - def ioctl(__fd: FileDescriptorLike, __request: int, __arg: bytes, __mutate_flag: bool = True) -> bytes: ... + def ioctl(fd: FileDescriptorLike, request: int, arg: bytes, mutate_flag: bool = True, /) -> bytes: ... @overload - def ioctl(__fd: FileDescriptorLike, __request: int, __arg: WriteableBuffer, __mutate_flag: Literal[False]) -> bytes: ... + def ioctl(fd: FileDescriptorLike, request: int, arg: WriteableBuffer, mutate_flag: Literal[False], /) -> bytes: ... @overload - def ioctl(__fd: FileDescriptorLike, __request: int, __arg: Buffer, __mutate_flag: bool = True) -> Any: ... - def flock(__fd: FileDescriptorLike, __operation: int) -> None: ... - def lockf(__fd: FileDescriptorLike, __cmd: int, __len: int = 0, __start: int = 0, __whence: int = 0) -> Any: ... + def ioctl(fd: FileDescriptorLike, request: int, arg: Buffer, mutate_flag: bool = True, /) -> Any: ... + def flock(fd: FileDescriptorLike, operation: int, /) -> None: ... + def lockf(fd: FileDescriptorLike, cmd: int, len: int = 0, start: int = 0, whence: int = 0, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 43eaa21bd039..086aff50344c 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -24,7 +24,7 @@ class Fraction(Rational): @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload - def __new__(cls, __value: float | Decimal | str) -> Self: ... + def __new__(cls, value: float | Decimal | str, /) -> Self: ... @classmethod def from_float(cls, f: float) -> Self: ... @classmethod diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index d3f702bcef4f..27550cfe08e6 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -36,9 +36,9 @@ _PWrapper = ParamSpec("_PWrapper") _RWrapper = TypeVar("_RWrapper") @overload -def reduce(__function: Callable[[_T, _S], _T], __sequence: Iterable[_S], __initial: _T) -> _T: ... +def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T, /) -> _T: ... @overload -def reduce(__function: Callable[[_T, _T], _T], __sequence: Iterable[_T]) -> _T: ... +def reduce(function: Callable[[_T, _T], _T], sequence: Iterable[_T], /) -> _T: ... class _CacheInfo(NamedTuple): hits: int @@ -61,7 +61,7 @@ class _lru_cache_wrapper(Generic[_T]): def cache_parameters(self) -> _CacheParameters: ... def __copy__(self) -> _lru_cache_wrapper[_T]: ... - def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_T]: ... + def __deepcopy__(self, memo: Any, /) -> _lru_cache_wrapper[_T]: ... @overload def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ... @@ -129,8 +129,8 @@ class partial(Generic[_T]): def args(self) -> tuple[Any, ...]: ... @property def keywords(self) -> dict[str, Any]: ... - def __new__(cls, __func: Callable[..., _T], *args: Any, **kwargs: Any) -> Self: ... - def __call__(__self, *args: Any, **kwargs: Any) -> _T: ... + def __new__(cls, func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> Self: ... + def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... @@ -142,9 +142,9 @@ class partialmethod(Generic[_T]): args: tuple[Any, ...] keywords: dict[str, Any] @overload - def __init__(self, __func: Callable[..., _T], *args: Any, **keywords: Any) -> None: ... + def __init__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> None: ... @overload - def __init__(self, __func: _Descriptor, *args: Any, **keywords: Any) -> None: ... + def __init__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> None: ... def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ... @property def __isabstractmethod__(self) -> bool: ... @@ -166,7 +166,7 @@ class _SingleDispatchCallable(Generic[_T]): @overload def register(self, cls: type[Any], func: Callable[..., _T]) -> Callable[..., _T]: ... def _clear_cache(self) -> None: ... - def __call__(__self, *args: Any, **kwargs: Any) -> _T: ... + def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... def singledispatch(func: Callable[..., _T]) -> _SingleDispatchCallable[_T]: ... @@ -199,7 +199,7 @@ class cached_property(Generic[_T_co]): def __class_getitem__(cls, item: Any) -> GenericAlias: ... if sys.version_info >= (3, 9): - def cache(__user_function: Callable[..., _T]) -> _lru_cache_wrapper[_T]: ... + def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ... def _make_key( args: tuple[Hashable, ...], diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 914c41434791..31179add314c 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -27,11 +27,11 @@ def get_referents(*objs: Any) -> list[Any]: ... def get_referrers(*objs: Any) -> list[Any]: ... def get_stats() -> list[dict[str, Any]]: ... def get_threshold() -> tuple[int, int, int]: ... -def is_tracked(__obj: Any) -> bool: ... +def is_tracked(obj: Any, /) -> bool: ... if sys.version_info >= (3, 9): - def is_finalized(__obj: Any) -> bool: ... + def is_finalized(obj: Any, /) -> bool: ... def isenabled() -> bool: ... -def set_debug(__flags: int) -> None: ... +def set_debug(flags: int, /) -> None: ... def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 2c33d7cf5810..7f43795dd01f 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -22,15 +22,15 @@ FNAME: int # actually Literal[8] # undocumented FCOMMENT: int # actually Literal[16] # undocumented class _ReadableFileobj(Protocol): - def read(self, __n: int) -> bytes: ... - def seek(self, __n: int) -> object: ... + def read(self, n: int, /) -> bytes: ... + def seek(self, n: int, /) -> object: ... # The following attributes and methods are optional: # name: str # mode: str # def fileno() -> int: ... class _WritableFileobj(Protocol): - def write(self, __b: bytes) -> object: ... + def write(self, b: bytes, /) -> object: ... def flush(self) -> object: ... # The following attributes and methods are optional: # name: str diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 38e846ab7eb3..93bd986c9d31 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -59,7 +59,7 @@ class _Hash: def copy(self) -> Self: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... - def update(self, __data: ReadableBuffer) -> None: ... + def update(self, data: ReadableBuffer, /) -> None: ... if sys.version_info >= (3, 9): def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> _Hash: ... @@ -92,9 +92,9 @@ class _VarLenHash: name: str def __init__(self, data: ReadableBuffer = ...) -> None: ... def copy(self) -> _VarLenHash: ... - def digest(self, __length: int) -> bytes: ... - def hexdigest(self, __length: int) -> str: ... - def update(self, __data: ReadableBuffer) -> None: ... + def digest(self, length: int, /) -> bytes: ... + def hexdigest(self, length: int, /) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... sha3_224 = _Hash sha3_256 = _Hash @@ -116,7 +116,8 @@ class _BlakeHash(_Hash): if sys.version_info >= (3, 9): def __init__( self, - __data: ReadableBuffer = ..., + data: ReadableBuffer = ..., + /, *, digest_size: int = ..., key: ReadableBuffer = ..., @@ -134,7 +135,8 @@ class _BlakeHash(_Hash): else: def __init__( self, - __data: ReadableBuffer = ..., + data: ReadableBuffer = ..., + /, *, digest_size: int = ..., key: ReadableBuffer = ..., @@ -157,9 +159,9 @@ if sys.version_info >= (3, 11): def getbuffer(self) -> ReadableBuffer: ... class _FileDigestFileObj(Protocol): - def readinto(self, __buf: bytearray) -> int: ... + def readinto(self, buf: bytearray, /) -> int: ... def readable(self) -> bool: ... def file_digest( - __fileobj: _BytesIOLike | _FileDigestFileObj, __digest: str | Callable[[], _Hash], *, _bufsize: int = 262144 + fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _Hash], /, *, _bufsize: int = 262144 ) -> _Hash: ... diff --git a/mypy/typeshed/stdlib/heapq.pyi b/mypy/typeshed/stdlib/heapq.pyi index 9198febd3cfa..7a3aa8b442a5 100644 --- a/mypy/typeshed/stdlib/heapq.pyi +++ b/mypy/typeshed/stdlib/heapq.pyi @@ -14,4 +14,4 @@ def merge( ) -> Iterable[_S]: ... def nlargest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ... def nsmallest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ... -def _heapify_max(__heap: list[Any]) -> None: ... # undocumented +def _heapify_max(heap: list[Any], /) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index 614121529416..ac1372dd1e9c 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -32,7 +32,7 @@ class HMAC: def copy(self) -> HMAC: ... @overload -def compare_digest(__a: ReadableBuffer, __b: ReadableBuffer) -> bool: ... +def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... @overload -def compare_digest(__a: AnyStr, __b: AnyStr) -> bool: ... +def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... def digest(key: SizedBuffer, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ... diff --git a/mypy/typeshed/stdlib/imghdr.pyi b/mypy/typeshed/stdlib/imghdr.pyi index d0960a5a1c5c..6e1b858b8f32 100644 --- a/mypy/typeshed/stdlib/imghdr.pyi +++ b/mypy/typeshed/stdlib/imghdr.pyi @@ -6,8 +6,8 @@ __all__ = ["what"] class _ReadableBinary(Protocol): def tell(self) -> int: ... - def read(self, __size: int) -> bytes: ... - def seek(self, __offset: int) -> Any: ... + def read(self, size: int, /) -> bytes: ... + def seek(self, offset: int, /) -> Any: ... @overload def what(file: StrPath | _ReadableBinary, h: None = None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/imp.pyi b/mypy/typeshed/stdlib/imp.pyi index b532f480fa13..ee5a0cd7bc72 100644 --- a/mypy/typeshed/stdlib/imp.pyi +++ b/mypy/typeshed/stdlib/imp.pyi @@ -45,7 +45,7 @@ class _FileLike(Protocol): def read(self) -> str | bytes: ... def close(self) -> Any: ... def __enter__(self) -> Any: ... - def __exit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: TracebackType | None) -> Any: ... + def __exit__(self, typ: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None, /) -> Any: ... # PathLike doesn't work for the pathname argument here def load_source(name: str, pathname: str, file: _FileLike | None = None) -> types.ModuleType: ... diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 825eab7ffde2..75e78ed59172 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -72,7 +72,7 @@ if sys.version_info >= (3, 10): def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec( - self, __fullname: str, __path: Sequence[str] | None, __target: types.ModuleType | None = ... + self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ..., / ) -> ModuleSpec | None: ... class PathEntryFinder(metaclass=ABCMeta): @@ -91,7 +91,7 @@ else: def invalidate_caches(self) -> None: ... # Not defined on the actual class, but expected to exist. def find_spec( - self, __fullname: str, __path: Sequence[str] | None, __target: types.ModuleType | None = ... + self, fullname: str, path: Sequence[str] | None, target: types.ModuleType | None = ..., / ) -> ModuleSpec | None: ... class PathEntryFinder(Finder): @@ -138,25 +138,25 @@ if sys.version_info >= (3, 9): def joinpath(self, *descendants: str) -> Traversable: ... else: @abstractmethod - def joinpath(self, __child: str) -> Traversable: ... + def joinpath(self, child: str, /) -> Traversable: ... # The documentation and runtime protocol allows *args, **kwargs arguments, # but this would mean that all implementers would have to support them, # which is not the case. @overload @abstractmethod - def open(self, __mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... + def open(self, mode: Literal["r"] = "r", /, *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open(self, __mode: Literal["rb"]) -> IO[bytes]: ... + def open(self, mode: Literal["rb"], /) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... if sys.version_info >= (3, 10): - def __truediv__(self, __child: str) -> Traversable: ... + def __truediv__(self, child: str, /) -> Traversable: ... else: @abstractmethod - def __truediv__(self, __child: str) -> Traversable: ... + def __truediv__(self, child: str, /) -> Traversable: ... @abstractmethod def read_bytes(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 06a8ff6a3462..bb5ddc37c603 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -225,10 +225,10 @@ def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGe def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... class _SupportsSet(Protocol[_T_cont, _V_cont]): - def __set__(self, __instance: _T_cont, __value: _V_cont) -> None: ... + def __set__(self, instance: _T_cont, value: _V_cont, /) -> None: ... class _SupportsDelete(Protocol[_T_cont]): - def __delete__(self, __instance: _T_cont) -> None: ... + def __delete__(self, instance: _T_cont, /) -> None: ... def isasyncgen(object: object) -> TypeGuard[AsyncGeneratorType[Any, Any]]: ... def istraceback(object: object) -> TypeGuard[TracebackType]: ... @@ -482,7 +482,7 @@ def formatargvalues( formatvalue: Callable[[Any], str] | None = ..., ) -> str: ... def getmro(cls: type) -> tuple[type, ...]: ... -def getcallargs(__func: Callable[_P, Any], *args: _P.args, **kwds: _P.kwargs) -> dict[str, Any]: ... +def getcallargs(func: Callable[_P, Any], /, *args: _P.args, **kwds: _P.kwargs) -> dict[str, Any]: ... class ClosureVars(NamedTuple): nonlocals: Mapping[str, Any] diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 659b216c43dc..e7ed1b0b5ee5 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -63,15 +63,15 @@ class IOBase(metaclass=abc.ABCMeta): def isatty(self) -> bool: ... def readable(self) -> bool: ... read: Callable[..., Any] - def readlines(self, __hint: int = -1) -> list[bytes]: ... - def seek(self, __offset: int, __whence: int = ...) -> int: ... + def readlines(self, hint: int = -1, /) -> list[bytes]: ... + def seek(self, offset: int, whence: int = ..., /) -> int: ... def seekable(self) -> bool: ... def tell(self) -> int: ... - def truncate(self, __size: int | None = ...) -> int: ... + def truncate(self, size: int | None = ..., /) -> int: ... def writable(self) -> bool: ... write: Callable[..., Any] - def writelines(self, __lines: Iterable[ReadableBuffer]) -> None: ... - def readline(self, __size: int | None = -1) -> bytes: ... + def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... + def readline(self, size: int | None = -1, /) -> bytes: ... def __del__(self) -> None: ... @property def closed(self) -> bool: ... @@ -79,18 +79,18 @@ class IOBase(metaclass=abc.ABCMeta): class RawIOBase(IOBase): def readall(self) -> bytes: ... - def readinto(self, __buffer: WriteableBuffer) -> int | None: ... - def write(self, __b: ReadableBuffer) -> int | None: ... - def read(self, __size: int = -1) -> bytes | None: ... + def readinto(self, buffer: WriteableBuffer, /) -> int | None: ... + def write(self, b: ReadableBuffer, /) -> int | None: ... + def read(self, size: int = -1, /) -> bytes | None: ... class BufferedIOBase(IOBase): raw: RawIOBase # This is not part of the BufferedIOBase API and may not exist on some implementations. def detach(self) -> RawIOBase: ... - def readinto(self, __buffer: WriteableBuffer) -> int: ... - def write(self, __buffer: ReadableBuffer) -> int: ... - def readinto1(self, __buffer: WriteableBuffer) -> int: ... - def read(self, __size: int | None = ...) -> bytes: ... - def read1(self, __size: int = ...) -> bytes: ... + def readinto(self, buffer: WriteableBuffer, /) -> int: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... + def readinto1(self, buffer: WriteableBuffer, /) -> int: ... + def read(self, size: int | None = ..., /) -> bytes: ... + def read1(self, size: int = ..., /) -> bytes: ... class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes mode: str @@ -103,8 +103,8 @@ class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definit ) -> None: ... @property def closefd(self) -> bool: ... - def write(self, __b: ReadableBuffer) -> int: ... - def read(self, __size: int = -1) -> bytes: ... + def write(self, b: ReadableBuffer, /) -> int: ... + def read(self, size: int = -1, /) -> bytes: ... def __enter__(self) -> Self: ... class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes @@ -116,25 +116,25 @@ class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible d def __enter__(self) -> Self: ... def getvalue(self) -> bytes: ... def getbuffer(self) -> memoryview: ... - def read1(self, __size: int | None = -1) -> bytes: ... + def read1(self, size: int | None = -1, /) -> bytes: ... class BufferedReader(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes def __enter__(self) -> Self: ... def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, __size: int = 0) -> bytes: ... + def peek(self, size: int = 0, /) -> bytes: ... class BufferedWriter(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes def __enter__(self) -> Self: ... def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def write(self, __buffer: ReadableBuffer) -> int: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... class BufferedRandom(BufferedReader, BufferedWriter): # type: ignore[misc] # incompatible definitions of methods in the base classes def __enter__(self) -> Self: ... - def seek(self, __target: int, __whence: int = 0) -> int: ... # stubtest needs this + def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this class BufferedRWPair(BufferedIOBase): def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, __size: int = ...) -> bytes: ... + def peek(self, size: int = ..., /) -> bytes: ... class TextIOBase(IOBase): encoding: str @@ -143,11 +143,11 @@ class TextIOBase(IOBase): def __iter__(self) -> Iterator[str]: ... # type: ignore[override] def __next__(self) -> str: ... # type: ignore[override] def detach(self) -> BinaryIO: ... - def write(self, __s: str) -> int: ... - def writelines(self, __lines: Iterable[str]) -> None: ... # type: ignore[override] - def readline(self, __size: int = ...) -> str: ... # type: ignore[override] - def readlines(self, __hint: int = -1) -> list[str]: ... # type: ignore[override] - def read(self, __size: int | None = ...) -> str: ... + def write(self, s: str, /) -> int: ... + def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] + def readline(self, size: int = ..., /) -> str: ... # type: ignore[override] + def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] + def read(self, size: int | None = ..., /) -> str: ... @type_check_only class _WrappedBuffer(Protocol): @@ -207,14 +207,14 @@ class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible d def __enter__(self) -> Self: ... def __iter__(self) -> Iterator[str]: ... # type: ignore[override] def __next__(self) -> str: ... # type: ignore[override] - def writelines(self, __lines: Iterable[str]) -> None: ... # type: ignore[override] - def readline(self, __size: int = -1) -> str: ... # type: ignore[override] - def readlines(self, __hint: int = -1) -> list[str]: ... # type: ignore[override] + def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] + def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] + def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] # Equals the "buffer" argument passed in to the constructor. def detach(self) -> BinaryIO: ... # TextIOWrapper's version of seek only supports a limited subset of # operations. - def seek(self, __cookie: int, __whence: int = 0) -> int: ... + def seek(self, cookie: int, whence: int = 0, /) -> int: ... class StringIO(TextIOWrapper): def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... @@ -229,10 +229,10 @@ class IncrementalNewlineDecoder(codecs.IncrementalDecoder): def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... @property def newlines(self) -> str | tuple[str, ...] | None: ... - def setstate(self, __state: tuple[bytes, int]) -> None: ... + def setstate(self, state: tuple[bytes, int], /) -> None: ... if sys.version_info >= (3, 10): @overload - def text_encoding(__encoding: None, __stacklevel: int = 2) -> Literal["locale", "utf-8"]: ... + def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ... @overload - def text_encoding(__encoding: _T, __stacklevel: int = 2) -> _T: ... + def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 0e501e1ade4d..264064dcd682 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -35,7 +35,7 @@ class count(Iterator[_N]): def __iter__(self) -> Self: ... class cycle(Iterator[_T]): - def __init__(self, __iterable: Iterable[_T]) -> None: ... + def __init__(self, iterable: Iterable[_T], /) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -62,9 +62,9 @@ class chain(Iterator[_T]): def __iter__(self) -> Self: ... @classmethod # We use type[Any] and not type[_S] to not lose the type inference from __iterable - def from_iterable(cls: type[Any], __iterable: Iterable[Iterable[_S]]) -> chain[_S]: ... + def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class compress(Iterator[_T]): def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... @@ -72,12 +72,12 @@ class compress(Iterator[_T]): def __next__(self) -> _T: ... class dropwhile(Iterator[_T]): - def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... class filterfalse(Iterator[_T]): - def __init__(self, __predicate: _Predicate[_T] | None, __iterable: Iterable[_T]) -> None: ... + def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -91,74 +91,70 @@ class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): class islice(Iterator[_T]): @overload - def __init__(self, __iterable: Iterable[_T], __stop: int | None) -> None: ... + def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... @overload - def __init__(self, __iterable: Iterable[_T], __start: int | None, __stop: int | None, __step: int | None = ...) -> None: ... + def __init__(self, iterable: Iterable[_T], start: int | None, stop: int | None, step: int | None = ..., /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... class starmap(Iterator[_T_co]): - def __new__(cls, __function: Callable[..., _T], __iterable: Iterable[Iterable[Any]]) -> starmap[_T]: ... + def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... class takewhile(Iterator[_T]): - def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -def tee(__iterable: Iterable[_T], __n: int = 2) -> tuple[Iterator[_T], ...]: ... +def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... class zip_longest(Iterator[_T_co]): # one iterable (fillvalue doesn't matter) @overload - def __new__(cls, __iter1: Iterable[_T1], *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... + def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... # two iterables @overload # In the overloads without fillvalue, all of the tuple members could theoretically be None, # but we return Any instead to avoid false positives for code where we know one of the iterables # is longer. - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> zip_longest[tuple[_T1 | Any, _T2 | Any]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip_longest[tuple[_T1 | Any, _T2 | Any]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], *, fillvalue: _T + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, fillvalue: _T ) -> zip_longest[tuple[_T1 | _T, _T2 | _T]]: ... # three iterables @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], *, fillvalue: _T + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, fillvalue: _T ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T]]: ... # four iterables @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4] + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], / ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4], *, fillvalue: _T + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, fillvalue: _T ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T, _T4 | _T]]: ... # five iterables @overload def __new__( - cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], / ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any, _T5 | Any]]: ... @overload def __new__( cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + /, *, fillvalue: _T, ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T, _T4 | _T, _T5 | _T]]: ... @@ -166,23 +162,25 @@ class zip_longest(Iterator[_T_co]): @overload def __new__( cls, - __iter1: Iterable[_T], - __iter2: Iterable[_T], - __iter3: Iterable[_T], - __iter4: Iterable[_T], - __iter5: Iterable[_T], - __iter6: Iterable[_T], + iter1: Iterable[_T], + iter2: Iterable[_T], + iter3: Iterable[_T], + iter4: Iterable[_T], + iter5: Iterable[_T], + iter6: Iterable[_T], + /, *iterables: Iterable[_T], ) -> zip_longest[tuple[_T | Any, ...]]: ... @overload def __new__( cls, - __iter1: Iterable[_T], - __iter2: Iterable[_T], - __iter3: Iterable[_T], - __iter4: Iterable[_T], - __iter5: Iterable[_T], - __iter6: Iterable[_T], + iter1: Iterable[_T], + iter2: Iterable[_T], + iter3: Iterable[_T], + iter4: Iterable[_T], + iter5: Iterable[_T], + iter6: Iterable[_T], + /, *iterables: Iterable[_T], fillvalue: _T, ) -> zip_longest[tuple[_T, ...]]: ... @@ -191,33 +189,29 @@ class zip_longest(Iterator[_T_co]): class product(Iterator[_T_co]): @overload - def __new__(cls, __iter1: Iterable[_T1]) -> product[tuple[_T1]]: ... + def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> product[tuple[_T1, _T2]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> product[tuple[_T1, _T2]]: ... @overload - def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3]) -> product[tuple[_T1, _T2, _T3]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /) -> product[tuple[_T1, _T2, _T3]]: ... @overload def __new__( - cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4] + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], / ) -> product[tuple[_T1, _T2, _T3, _T4]]: ... @overload def __new__( - cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], / ) -> product[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def __new__( cls, - __iter1: Iterable[_T1], - __iter2: Iterable[_T2], - __iter3: Iterable[_T3], - __iter4: Iterable[_T4], - __iter5: Iterable[_T5], - __iter6: Iterable[_T6], + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + iter6: Iterable[_T6], + /, ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload def __new__(cls, *iterables: Iterable[_T1], repeat: int = 1) -> product[tuple[_T1, ...]]: ... @@ -268,7 +262,7 @@ class combinations_with_replacement(Iterator[_T_co]): if sys.version_info >= (3, 10): class pairwise(Iterator[_T_co]): - def __new__(cls, __iterable: Iterable[_T]) -> pairwise[tuple[_T, _T]]: ... + def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index 0c0d366eb7a2..c1062688bd93 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -10,6 +10,8 @@ INFINITY: float def py_encode_basestring(s: str) -> str: ... # undocumented def py_encode_basestring_ascii(s: str) -> str: ... # undocumented +def encode_basestring(s: str) -> str: ... # undocumented +def encode_basestring_ascii(s: str) -> str: ... # undocumented class JSONEncoder: item_separator: str diff --git a/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi b/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi index 5468ab1db5c3..06813c94308a 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixer_base.pyi @@ -38,5 +38,5 @@ class BaseFix: class ConditionalFix(BaseFix, metaclass=ABCMeta): skip_on: ClassVar[str | None] - def start_tree(self, __tree: Node, __filename: StrPath) -> None: ... + def start_tree(self, tree: Node, filename: StrPath, /) -> None: ... def should_skip(self, node: Base) -> bool: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index eae2bcd3e96c..a62d0674df4c 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -71,12 +71,12 @@ _FormatStyle: TypeAlias = Literal["%", "{", "$"] if sys.version_info >= (3, 12): class _SupportsFilter(Protocol): - def filter(self, __record: LogRecord) -> bool | LogRecord: ... + def filter(self, record: LogRecord, /) -> bool | LogRecord: ... _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool | LogRecord] | _SupportsFilter else: class _SupportsFilter(Protocol): - def filter(self, __record: LogRecord) -> bool: ... + def filter(self, record: LogRecord, /) -> bool: ... _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool] | _SupportsFilter @@ -341,6 +341,9 @@ class LogRecord: stack_info: str | None thread: int | None threadName: str | None + if sys.version_info >= (3, 12): + taskName: str | None + def __init__( self, name: str, @@ -355,7 +358,7 @@ class LogRecord: ) -> None: ... def getMessage(self) -> str: ... # Allows setting contextual information on LogRecord objects as per the docs, see #7833 - def __setattr__(self, __name: str, __value: Any) -> None: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... _L = TypeVar("_L", bound=Logger | LoggerAdapter[Any]) diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index 2280dbad4c5d..4c3dc913308c 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -253,7 +253,7 @@ class HTTPHandler(Handler): class _QueueLike(Protocol[_T]): def get(self) -> _T: ... - def put_nowait(self, __item: _T) -> None: ... + def put_nowait(self, item: _T, /) -> None: ... class QueueHandler(Handler): queue: _QueueLike[Any] diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index 05248ee0e710..c05e46a02aeb 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -99,7 +99,7 @@ class LZMACompressor: def __init__( self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... ) -> None: ... - def compress(self, __data: ReadableBuffer) -> bytes: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... def flush(self) -> bytes: ... class LZMAError(Exception): ... @@ -194,4 +194,4 @@ def compress( def decompress( data: ReadableBuffer, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None ) -> bytes: ... -def is_check_supported(__check_id: int) -> bool: ... +def is_check_supported(check_id: int, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/marshal.pyi b/mypy/typeshed/stdlib/marshal.pyi index 21f05c908479..69546344f5bf 100644 --- a/mypy/typeshed/stdlib/marshal.pyi +++ b/mypy/typeshed/stdlib/marshal.pyi @@ -27,7 +27,7 @@ _Marshallable: TypeAlias = ( | ReadableBuffer ) -def dump(__value: _Marshallable, __file: SupportsWrite[bytes], __version: int = 4) -> None: ... -def load(__file: SupportsRead[bytes]) -> Any: ... -def dumps(__value: _Marshallable, __version: int = 4) -> bytes: ... -def loads(__bytes: ReadableBuffer) -> Any: ... +def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /) -> None: ... +def load(file: SupportsRead[bytes], /) -> Any: ... +def dumps(value: _Marshallable, version: int = 4, /) -> bytes: ... +def loads(bytes: ReadableBuffer, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index ee0693912a8b..0c2fd4aba719 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -14,58 +14,58 @@ inf: float nan: float tau: float -def acos(__x: _SupportsFloatOrIndex) -> float: ... -def acosh(__x: _SupportsFloatOrIndex) -> float: ... -def asin(__x: _SupportsFloatOrIndex) -> float: ... -def asinh(__x: _SupportsFloatOrIndex) -> float: ... -def atan(__x: _SupportsFloatOrIndex) -> float: ... -def atan2(__y: _SupportsFloatOrIndex, __x: _SupportsFloatOrIndex) -> float: ... -def atanh(__x: _SupportsFloatOrIndex) -> float: ... +def acos(x: _SupportsFloatOrIndex, /) -> float: ... +def acosh(x: _SupportsFloatOrIndex, /) -> float: ... +def asin(x: _SupportsFloatOrIndex, /) -> float: ... +def asinh(x: _SupportsFloatOrIndex, /) -> float: ... +def atan(x: _SupportsFloatOrIndex, /) -> float: ... +def atan2(y: _SupportsFloatOrIndex, x: _SupportsFloatOrIndex, /) -> float: ... +def atanh(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 11): - def cbrt(__x: _SupportsFloatOrIndex) -> float: ... + def cbrt(x: _SupportsFloatOrIndex, /) -> float: ... class _SupportsCeil(Protocol[_T_co]): def __ceil__(self) -> _T_co: ... @overload -def ceil(__x: _SupportsCeil[_T]) -> _T: ... +def ceil(x: _SupportsCeil[_T], /) -> _T: ... @overload -def ceil(__x: _SupportsFloatOrIndex) -> int: ... -def comb(__n: SupportsIndex, __k: SupportsIndex) -> int: ... -def copysign(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... -def cos(__x: _SupportsFloatOrIndex) -> float: ... -def cosh(__x: _SupportsFloatOrIndex) -> float: ... -def degrees(__x: _SupportsFloatOrIndex) -> float: ... -def dist(__p: Iterable[_SupportsFloatOrIndex], __q: Iterable[_SupportsFloatOrIndex]) -> float: ... -def erf(__x: _SupportsFloatOrIndex) -> float: ... -def erfc(__x: _SupportsFloatOrIndex) -> float: ... -def exp(__x: _SupportsFloatOrIndex) -> float: ... +def ceil(x: _SupportsFloatOrIndex, /) -> int: ... +def comb(n: SupportsIndex, k: SupportsIndex, /) -> int: ... +def copysign(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... +def cos(x: _SupportsFloatOrIndex, /) -> float: ... +def cosh(x: _SupportsFloatOrIndex, /) -> float: ... +def degrees(x: _SupportsFloatOrIndex, /) -> float: ... +def dist(p: Iterable[_SupportsFloatOrIndex], q: Iterable[_SupportsFloatOrIndex], /) -> float: ... +def erf(x: _SupportsFloatOrIndex, /) -> float: ... +def erfc(x: _SupportsFloatOrIndex, /) -> float: ... +def exp(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 11): - def exp2(__x: _SupportsFloatOrIndex) -> float: ... + def exp2(x: _SupportsFloatOrIndex, /) -> float: ... -def expm1(__x: _SupportsFloatOrIndex) -> float: ... -def fabs(__x: _SupportsFloatOrIndex) -> float: ... -def factorial(__x: SupportsIndex) -> int: ... +def expm1(x: _SupportsFloatOrIndex, /) -> float: ... +def fabs(x: _SupportsFloatOrIndex, /) -> float: ... +def factorial(x: SupportsIndex, /) -> int: ... class _SupportsFloor(Protocol[_T_co]): def __floor__(self) -> _T_co: ... @overload -def floor(__x: _SupportsFloor[_T]) -> _T: ... +def floor(x: _SupportsFloor[_T], /) -> _T: ... @overload -def floor(__x: _SupportsFloatOrIndex) -> int: ... -def fmod(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... -def frexp(__x: _SupportsFloatOrIndex) -> tuple[float, int]: ... -def fsum(__seq: Iterable[_SupportsFloatOrIndex]) -> float: ... -def gamma(__x: _SupportsFloatOrIndex) -> float: ... +def floor(x: _SupportsFloatOrIndex, /) -> int: ... +def fmod(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... +def frexp(x: _SupportsFloatOrIndex, /) -> tuple[float, int]: ... +def fsum(seq: Iterable[_SupportsFloatOrIndex], /) -> float: ... +def gamma(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 9): def gcd(*integers: SupportsIndex) -> int: ... else: - def gcd(__x: SupportsIndex, __y: SupportsIndex) -> int: ... + def gcd(x: SupportsIndex, y: SupportsIndex, /) -> int: ... def hypot(*coordinates: _SupportsFloatOrIndex) -> float: ... def isclose( @@ -75,51 +75,51 @@ def isclose( rel_tol: _SupportsFloatOrIndex = 1e-09, abs_tol: _SupportsFloatOrIndex = 0.0, ) -> bool: ... -def isinf(__x: _SupportsFloatOrIndex) -> bool: ... -def isfinite(__x: _SupportsFloatOrIndex) -> bool: ... -def isnan(__x: _SupportsFloatOrIndex) -> bool: ... -def isqrt(__n: SupportsIndex) -> int: ... +def isinf(x: _SupportsFloatOrIndex, /) -> bool: ... +def isfinite(x: _SupportsFloatOrIndex, /) -> bool: ... +def isnan(x: _SupportsFloatOrIndex, /) -> bool: ... +def isqrt(n: SupportsIndex, /) -> int: ... if sys.version_info >= (3, 9): def lcm(*integers: SupportsIndex) -> int: ... -def ldexp(__x: _SupportsFloatOrIndex, __i: int) -> float: ... -def lgamma(__x: _SupportsFloatOrIndex) -> float: ... +def ldexp(x: _SupportsFloatOrIndex, i: int, /) -> float: ... +def lgamma(x: _SupportsFloatOrIndex, /) -> float: ... def log(x: _SupportsFloatOrIndex, base: _SupportsFloatOrIndex = ...) -> float: ... -def log10(__x: _SupportsFloatOrIndex) -> float: ... -def log1p(__x: _SupportsFloatOrIndex) -> float: ... -def log2(__x: _SupportsFloatOrIndex) -> float: ... -def modf(__x: _SupportsFloatOrIndex) -> tuple[float, float]: ... +def log10(x: _SupportsFloatOrIndex, /) -> float: ... +def log1p(x: _SupportsFloatOrIndex, /) -> float: ... +def log2(x: _SupportsFloatOrIndex, /) -> float: ... +def modf(x: _SupportsFloatOrIndex, /) -> tuple[float, float]: ... if sys.version_info >= (3, 12): - def nextafter(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex, *, steps: SupportsIndex | None = None) -> float: ... + def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /, *, steps: SupportsIndex | None = None) -> float: ... elif sys.version_info >= (3, 9): - def nextafter(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... + def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... -def perm(__n: SupportsIndex, __k: SupportsIndex | None = None) -> int: ... -def pow(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... +def perm(n: SupportsIndex, k: SupportsIndex | None = None, /) -> int: ... +def pow(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... @overload -def prod(__iterable: Iterable[SupportsIndex], *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] +def prod(iterable: Iterable[SupportsIndex], /, *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] @overload -def prod(__iterable: Iterable[_SupportsFloatOrIndex], *, start: _SupportsFloatOrIndex = 1) -> float: ... -def radians(__x: _SupportsFloatOrIndex) -> float: ... -def remainder(__x: _SupportsFloatOrIndex, __y: _SupportsFloatOrIndex) -> float: ... -def sin(__x: _SupportsFloatOrIndex) -> float: ... -def sinh(__x: _SupportsFloatOrIndex) -> float: ... +def prod(iterable: Iterable[_SupportsFloatOrIndex], /, *, start: _SupportsFloatOrIndex = 1) -> float: ... +def radians(x: _SupportsFloatOrIndex, /) -> float: ... +def remainder(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... +def sin(x: _SupportsFloatOrIndex, /) -> float: ... +def sinh(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 12): - def sumprod(__p: Iterable[float], __q: Iterable[float]) -> float: ... + def sumprod(p: Iterable[float], q: Iterable[float], /) -> float: ... -def sqrt(__x: _SupportsFloatOrIndex) -> float: ... -def tan(__x: _SupportsFloatOrIndex) -> float: ... -def tanh(__x: _SupportsFloatOrIndex) -> float: ... +def sqrt(x: _SupportsFloatOrIndex, /) -> float: ... +def tan(x: _SupportsFloatOrIndex, /) -> float: ... +def tanh(x: _SupportsFloatOrIndex, /) -> float: ... # Is different from `_typeshed.SupportsTrunc`, which is not generic class _SupportsTrunc(Protocol[_T_co]): def __trunc__(self) -> _T_co: ... -def trunc(__x: _SupportsTrunc[_T]) -> _T: ... +def trunc(x: _SupportsTrunc[_T], /) -> _T: ... if sys.version_info >= (3, 9): - def ulp(__x: _SupportsFloatOrIndex) -> float: ... + def ulp(x: _SupportsFloatOrIndex, /) -> float: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 6bbb797f054d..93c4f408e5b6 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -58,24 +58,24 @@ class mmap(Iterable[int], Sized): def read(self, n: int | None = ...) -> bytes: ... def write(self, bytes: ReadableBuffer) -> int: ... @overload - def __getitem__(self, __key: int) -> int: ... + def __getitem__(self, key: int, /) -> int: ... @overload - def __getitem__(self, __key: slice) -> bytes: ... - def __delitem__(self, __key: int | slice) -> NoReturn: ... + def __getitem__(self, key: slice, /) -> bytes: ... + def __delitem__(self, key: int | slice, /) -> NoReturn: ... @overload - def __setitem__(self, __key: int, __value: int) -> None: ... + def __setitem__(self, key: int, value: int, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: ReadableBuffer) -> None: ... + def __setitem__(self, key: slice, value: ReadableBuffer, /) -> None: ... # Doesn't actually exist, but the object actually supports "in" because it has __getitem__, # so we claim that there is also a __contains__ to help type checkers. - def __contains__(self, __o: object) -> bool: ... + def __contains__(self, o: object, /) -> bool: ... # Doesn't actually exist, but the object is actually iterable because it has __getitem__ and __len__, # so we claim that there is also an __iter__ to help type checkers. def __iter__(self) -> Iterator[int]: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... if sys.platform != "win32": MADV_NORMAL: int diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index bfd7ec62a9be..54b3674a3a46 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -13,20 +13,20 @@ if sys.platform == "win32": SEM_NOALIGNMENTFAULTEXCEPT: int SEM_NOGPFAULTERRORBOX: int SEM_NOOPENFILEERRORBOX: int - def locking(__fd: int, __mode: int, __nbytes: int) -> None: ... - def setmode(__fd: int, __mode: int) -> int: ... - def open_osfhandle(__handle: int, __flags: int) -> int: ... - def get_osfhandle(__fd: int) -> int: ... + def locking(fd: int, mode: int, nbytes: int, /) -> None: ... + def setmode(fd: int, mode: int, /) -> int: ... + def open_osfhandle(handle: int, flags: int, /) -> int: ... + def get_osfhandle(fd: int, /) -> int: ... def kbhit() -> bool: ... def getch() -> bytes: ... def getwch() -> str: ... def getche() -> bytes: ... def getwche() -> str: ... - def putch(__char: bytes | bytearray) -> None: ... - def putwch(__unicode_char: str) -> None: ... - def ungetch(__char: bytes | bytearray) -> None: ... - def ungetwch(__unicode_char: str) -> None: ... + def putch(char: bytes | bytearray, /) -> None: ... + def putwch(unicode_char: str, /) -> None: ... + def ungetch(char: bytes | bytearray, /) -> None: ... + def ungetwch(unicode_char: str, /) -> None: ... def heapmin() -> None: ... - def SetErrorMode(__mode: int) -> int: ... + def SetErrorMode(mode: int, /) -> int: ... if sys.version_info >= (3, 10): def GetErrorMode() -> int: ... # undocumented diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 1cc8d03ea436..a3edaa463818 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -7,7 +7,7 @@ from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, pop from multiprocessing.managers import SyncManager from multiprocessing.pool import Pool as _Pool from multiprocessing.process import BaseProcess -from multiprocessing.sharedctypes import SynchronizedArray, SynchronizedBase +from multiprocessing.sharedctypes import Synchronized, SynchronizedArray from typing import Any, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias @@ -79,15 +79,17 @@ class BaseContext: @overload def RawArray(self, typecode_or_type: str, size_or_initializer: int | Sequence[Any]) -> Any: ... @overload - def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[False]) -> _CT: ... + def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[False]) -> Synchronized[_CT]: ... @overload - def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True) -> SynchronizedBase[_CT]: ... + def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True) -> Synchronized[_CT]: ... @overload - def Value(self, typecode_or_type: str, *args: Any, lock: Literal[True] | _LockLike = True) -> SynchronizedBase[Any]: ... + def Value(self, typecode_or_type: str, *args: Any, lock: Literal[True] | _LockLike = True) -> Synchronized[Any]: ... @overload def Value(self, typecode_or_type: str | type[_CData], *args: Any, lock: bool | _LockLike = True) -> Any: ... @overload - def Array(self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False]) -> _CT: ... + def Array( + self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] + ) -> SynchronizedArray[_CT]: ... @overload def Array( self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True diff --git a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi index 804a56e9cbcf..3cbeeb057791 100644 --- a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi @@ -57,8 +57,8 @@ Process = DummyProcess class Namespace: def __init__(self, **kwds: Any) -> None: ... - def __getattr__(self, __name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... + def __getattr__(self, name: str, /) -> Any: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... class Value: _typecode: Any diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index eb3ac29b1449..02b5c4bc8c67 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -22,8 +22,8 @@ _VT = TypeVar("_VT") class Namespace: def __init__(self, **kwds: Any) -> None: ... - def __getattr__(self, __name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... + def __getattr__(self, name: str, /) -> Any: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... _Namespace: TypeAlias = Namespace @@ -63,23 +63,23 @@ class ValueProxy(BaseProxy, Generic[_T]): class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): __builtins__: ClassVar[dict[str, Any]] def __len__(self) -> int: ... - def __getitem__(self, __key: _KT) -> _VT: ... - def __setitem__(self, __key: _KT, __value: _VT) -> None: ... - def __delitem__(self, __key: _KT) -> None: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... def __iter__(self) -> Iterator[_KT]: ... def copy(self) -> dict[_KT, _VT]: ... @overload # type: ignore[override] - def get(self, __key: _KT) -> _VT | None: ... + def get(self, key: _KT, /) -> _VT | None: ... @overload - def get(self, __key: _KT, __default: _VT) -> _VT: ... + def get(self, key: _KT, default: _VT, /) -> _VT: ... @overload - def get(self, __key: _KT, __default: _T) -> _VT | _T: ... + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... @overload - def pop(self, __key: _KT) -> _VT: ... + def pop(self, key: _KT, /) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _VT) -> _VT: ... + def pop(self, key: _KT, default: _VT, /) -> _VT: ... @overload - def pop(self, __key: _KT, __default: _T) -> _VT | _T: ... + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... def keys(self) -> list[_KT]: ... # type: ignore[override] def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] def values(self) -> list[_VT]: ... # type: ignore[override] @@ -87,26 +87,26 @@ class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): class BaseListProxy(BaseProxy, MutableSequence[_T]): __builtins__: ClassVar[dict[str, Any]] def __len__(self) -> int: ... - def __add__(self, __x: list[_T]) -> list[_T]: ... - def __delitem__(self, __i: SupportsIndex | slice) -> None: ... + def __add__(self, x: list[_T], /) -> list[_T]: ... + def __delitem__(self, i: SupportsIndex | slice, /) -> None: ... @overload - def __getitem__(self, __i: SupportsIndex) -> _T: ... + def __getitem__(self, i: SupportsIndex, /) -> _T: ... @overload - def __getitem__(self, __s: slice) -> list[_T]: ... + def __getitem__(self, s: slice, /) -> list[_T]: ... @overload - def __setitem__(self, __i: SupportsIndex, __o: _T) -> None: ... + def __setitem__(self, i: SupportsIndex, o: _T, /) -> None: ... @overload - def __setitem__(self, __s: slice, __o: Iterable[_T]) -> None: ... - def __mul__(self, __n: SupportsIndex) -> list[_T]: ... - def __rmul__(self, __n: SupportsIndex) -> list[_T]: ... + def __setitem__(self, s: slice, o: Iterable[_T], /) -> None: ... + def __mul__(self, n: SupportsIndex, /) -> list[_T]: ... + def __rmul__(self, n: SupportsIndex, /) -> list[_T]: ... def __reversed__(self) -> Iterator[_T]: ... - def append(self, __object: _T) -> None: ... - def extend(self, __iterable: Iterable[_T]) -> None: ... - def pop(self, __index: SupportsIndex = ...) -> _T: ... - def index(self, __value: _T, __start: SupportsIndex = ..., __stop: SupportsIndex = ...) -> int: ... - def count(self, __value: _T) -> int: ... - def insert(self, __index: SupportsIndex, __object: _T) -> None: ... - def remove(self, __value: _T) -> None: ... + def append(self, object: _T, /) -> None: ... + def extend(self, iterable: Iterable[_T], /) -> None: ... + def pop(self, index: SupportsIndex = ..., /) -> _T: ... + def index(self, value: _T, start: SupportsIndex = ..., stop: SupportsIndex = ..., /) -> int: ... + def count(self, value: _T, /) -> int: ... + def insert(self, index: SupportsIndex, object: _T, /) -> None: ... + def remove(self, value: _T, /) -> None: ... # Use BaseListProxy[SupportsRichComparisonT] for the first overload rather than [SupportsRichComparison] # to work around invariance @overload @@ -115,8 +115,8 @@ class BaseListProxy(BaseProxy, MutableSequence[_T]): def sort(self, *, key: Callable[[_T], SupportsRichComparison], reverse: bool = ...) -> None: ... class ListProxy(BaseListProxy[_T]): - def __iadd__(self, __value: Iterable[_T]) -> Self: ... # type: ignore[override] - def __imul__(self, __value: SupportsIndex) -> Self: ... # type: ignore[override] + def __iadd__(self, value: Iterable[_T], /) -> Self: ... # type: ignore[override] + def __imul__(self, value: SupportsIndex, /) -> Self: ... # type: ignore[override] # Returned by BaseManager.get_server() class Server: @@ -186,19 +186,19 @@ class SyncManager(BaseManager): @overload def dict(self, **kwargs: _VT) -> DictProxy[str, _VT]: ... @overload - def dict(self, __map: SupportsKeysAndGetItem[_KT, _VT]) -> DictProxy[_KT, _VT]: ... + def dict(self, map: SupportsKeysAndGetItem[_KT, _VT], /) -> DictProxy[_KT, _VT]: ... @overload - def dict(self, __map: SupportsKeysAndGetItem[str, _VT], **kwargs: _VT) -> DictProxy[str, _VT]: ... + def dict(self, map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> DictProxy[str, _VT]: ... @overload - def dict(self, __iterable: Iterable[tuple[_KT, _VT]]) -> DictProxy[_KT, _VT]: ... + def dict(self, iterable: Iterable[tuple[_KT, _VT]], /) -> DictProxy[_KT, _VT]: ... @overload - def dict(self, __iterable: Iterable[tuple[str, _VT]], **kwargs: _VT) -> DictProxy[str, _VT]: ... + def dict(self, iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> DictProxy[str, _VT]: ... @overload - def dict(self, __iterable: Iterable[list[str]]) -> DictProxy[str, str]: ... + def dict(self, iterable: Iterable[list[str]], /) -> DictProxy[str, str]: ... @overload - def dict(self, __iterable: Iterable[list[bytes]]) -> DictProxy[bytes, bytes]: ... + def dict(self, iterable: Iterable[list[bytes]], /) -> DictProxy[bytes, bytes]: ... @overload - def list(self, __sequence: Sequence[_T]) -> ListProxy[_T]: ... + def list(self, sequence: Sequence[_T], /) -> ListProxy[_T]: ... @overload def list(self) -> ListProxy[Any]: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/queues.pyi b/mypy/typeshed/stdlib/multiprocessing/queues.pyi index 8e72d15f25f6..4cedd665552a 100644 --- a/mypy/typeshed/stdlib/multiprocessing/queues.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/queues.pyi @@ -23,7 +23,7 @@ class Queue(Generic[_T]): def join_thread(self) -> None: ... def cancel_join_thread(self) -> None: ... if sys.version_info >= (3, 12): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class JoinableQueue(Queue[_T]): def task_done(self) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 3979f14cf636..4093a97e6ca3 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -72,7 +72,7 @@ def synchronized(obj: ctypes.Array[_CT], lock: _LockLike | None = None, ctx: Any def synchronized(obj: _CT, lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedBase[_CT]: ... class _AcquireFunc(Protocol): - def __call__(self, __block: bool = ..., __timeout: float | None = ...) -> bool: ... + def __call__(self, block: bool = ..., timeout: float | None = ..., /) -> bool: ... class SynchronizedBase(Generic[_CT]): acquire: _AcquireFunc @@ -83,7 +83,7 @@ class SynchronizedBase(Generic[_CT]): def get_lock(self) -> _LockLike: ... def __enter__(self) -> bool: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_val: BaseException | None, __exc_tb: TracebackType | None + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... class Synchronized(SynchronizedBase[_SimpleCData[_T]], Generic[_T]): @@ -91,7 +91,13 @@ class Synchronized(SynchronizedBase[_SimpleCData[_T]], Generic[_T]): class SynchronizedArray(SynchronizedBase[ctypes.Array[_CT]], Generic[_CT]): def __len__(self) -> int: ... + @overload + def __getitem__(self, i: slice) -> list[_CT]: ... + @overload def __getitem__(self, i: int) -> _CT: ... + @overload + def __setitem__(self, i: slice, value: Iterable[_CT]) -> None: ... + @overload def __setitem__(self, i: int, value: _CT) -> None: ... def __getslice__(self, start: int, stop: int) -> list[_CT]: ... def __setslice__(self, start: int, stop: int, values: Iterable[_CT]) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index a4e36cfa0b6e..048c6fe8d891 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -23,7 +23,7 @@ class Condition(AbstractContextManager[bool]): def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... def release(self) -> None: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_val: BaseException | None, __exc_tb: TracebackType | None + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... class Event: @@ -38,7 +38,7 @@ class SemLock(AbstractContextManager[bool]): def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... def release(self) -> None: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_val: BaseException | None, __exc_tb: TracebackType | None + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... class Lock(SemLock): diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi index bfa880ee03a8..079366018bf5 100644 --- a/mypy/typeshed/stdlib/ntpath.pyi +++ b/mypy/typeshed/stdlib/ntpath.pyi @@ -97,11 +97,11 @@ altsep: LiteralString # but must be defined as pos-only in the stub or cross-platform code doesn't type-check, # as the parameter name is different in posixpath.join() @overload -def join(__path: LiteralString, *paths: LiteralString) -> LiteralString: ... +def join(path: LiteralString, /, *paths: LiteralString) -> LiteralString: ... @overload -def join(__path: StrPath, *paths: StrPath) -> str: ... +def join(path: StrPath, /, *paths: StrPath) -> str: ... @overload -def join(__path: BytesPath, *paths: BytesPath) -> bytes: ... +def join(path: BytesPath, /, *paths: BytesPath) -> bytes: ... if sys.platform == "win32": if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi index 02da0c9f954a..14bdb7622142 100644 --- a/mypy/typeshed/stdlib/opcode.pyi +++ b/mypy/typeshed/stdlib/opcode.pyi @@ -56,4 +56,4 @@ opmap: dict[str, int] HAVE_ARGUMENT: Literal[90] EXTENDED_ARG: Literal[144] -def stack_effect(__opcode: int, __oparg: int | None = None, *, jump: bool | None = None) -> int: ... +def stack_effect(opcode: int, oparg: int | None = None, /, *, jump: bool | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index a8c1c4cfb93e..3474648617c2 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,6 +1,7 @@ +from _typeshed import Incomplete from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, overload +from typing import IO, Any, AnyStr, Literal, overload __all__ = [ "Option", @@ -26,8 +27,8 @@ NO_DEFAULT: tuple[str, ...] SUPPRESS_HELP: str SUPPRESS_USAGE: str -def check_builtin(option: Option, opt: Any, value: str) -> Any: ... -def check_choice(option: Option, opt: Any, value: str) -> str: ... +def check_builtin(option: Option, opt, value: str): ... +def check_choice(option: Option, opt, value: str) -> str: ... class OptParseError(Exception): msg: str @@ -54,26 +55,26 @@ class HelpFormatter: _short_opt_fmt: str current_indent: int default_tag: str - help_position: Any - help_width: Any + help_position: int + help_width: int | Any # initialized as None and computed later as int when storing option strings indent_increment: int level: int max_help_position: int option_strings: dict[Option, str] parser: OptionParser - short_first: Any + short_first: Incomplete width: int def __init__(self, indent_increment: int, max_help_position: int, width: int | None, short_first: int) -> None: ... def dedent(self) -> None: ... def expand_default(self, option: Option) -> str: ... - def format_description(self, description: str) -> str: ... - def format_epilog(self, epilog: str) -> str: ... + def format_description(self, description: str | None) -> str: ... + def format_epilog(self, epilog: str | None) -> str: ... @abstractmethod - def format_heading(self, heading: Any) -> str: ... + def format_heading(self, heading: str) -> str: ... def format_option(self, option: Option) -> str: ... def format_option_strings(self, option: Option) -> str: ... @abstractmethod - def format_usage(self, usage: Any) -> str: ... + def format_usage(self, usage: str) -> str: ... def indent(self) -> None: ... def set_long_opt_delimiter(self, delim: str) -> None: ... def set_parser(self, parser: OptionParser) -> None: ... @@ -98,25 +99,25 @@ class Option: ACTIONS: tuple[str, ...] ALWAYS_TYPED_ACTIONS: tuple[str, ...] ATTRS: list[str] - CHECK_METHODS: list[Callable[..., Any]] | None + CHECK_METHODS: list[Callable[..., Incomplete]] | None CONST_ACTIONS: tuple[str, ...] STORE_ACTIONS: tuple[str, ...] TYPED_ACTIONS: tuple[str, ...] TYPES: tuple[str, ...] - TYPE_CHECKER: dict[str, Callable[..., Any]] + TYPE_CHECKER: dict[str, Callable[[Option, str, Incomplete], Any]] _long_opts: list[str] _short_opts: list[str] action: str dest: str | None - default: Any + default: Incomplete nargs: int - type: Any - callback: Callable[..., Any] | None - callback_args: tuple[Any, ...] | None - callback_kwargs: dict[str, Any] | None + type: Incomplete + callback: Callable[..., Incomplete] | None + callback_args: tuple[Incomplete, ...] | None + callback_kwargs: dict[str, Incomplete] | None help: str | None metavar: str | None - def __init__(self, *opts: str | None, **attrs: Any) -> None: ... + def __init__(self, *opts: str | None, **attrs) -> None: ... def _check_action(self) -> None: ... def _check_callback(self) -> None: ... def _check_choice(self) -> None: ... @@ -125,13 +126,13 @@ class Option: def _check_nargs(self) -> None: ... def _check_opt_strings(self, opts: Iterable[str | None]) -> list[str]: ... def _check_type(self) -> None: ... - def _set_attrs(self, attrs: dict[str, Any]) -> None: ... + def _set_attrs(self, attrs: dict[str, Incomplete]) -> None: ... def _set_opt_strings(self, opts: Iterable[str]) -> None: ... - def check_value(self, opt: str, value: Any) -> Any: ... - def convert_value(self, opt: str, value: Any) -> Any: ... + def check_value(self, opt: str, value): ... + def convert_value(self, opt: str, value): ... def get_opt_string(self) -> str: ... - def process(self, opt: Any, value: Any, values: Any, parser: OptionParser) -> int: ... - def take_action(self, action: str, dest: str, opt: Any, value: Any, values: Any, parser: OptionParser) -> int: ... + def process(self, opt, value, values, parser: OptionParser) -> int: ... + def take_action(self, action: str, dest: str, opt, value, values, parser: OptionParser) -> int: ... def takes_value(self) -> bool: ... make_option = Option @@ -140,28 +141,30 @@ class OptionContainer: _long_opt: dict[str, Option] _short_opt: dict[str, Option] conflict_handler: str - defaults: dict[str, Any] - description: Any + defaults: dict[str, Incomplete] + description: str | None option_class: type[Option] - def __init__(self, option_class: type[Option], conflict_handler: Any, description: Any) -> None: ... - def _check_conflict(self, option: Any) -> None: ... + def __init__( + self, option_class: type[Option], conflict_handler: Literal["error", "resolve"], description: str | None + ) -> None: ... + def _check_conflict(self, option: Option) -> None: ... def _create_option_mappings(self) -> None: ... def _share_option_mappings(self, parser: OptionParser) -> None: ... @overload def add_option(self, opt: Option) -> Option: ... @overload - def add_option(self, *args: str | None, **kwargs: Any) -> Any: ... + def add_option(self, arg: str, /, *args: str | None, **kwargs) -> Option: ... def add_options(self, option_list: Iterable[Option]) -> None: ... def destroy(self) -> None: ... - def format_description(self, formatter: HelpFormatter | None) -> Any: ... - def format_help(self, formatter: HelpFormatter | None) -> str: ... - def format_option_help(self, formatter: HelpFormatter | None) -> str: ... - def get_description(self) -> Any: ... + def format_option_help(self, formatter: HelpFormatter) -> str: ... + def format_description(self, formatter: HelpFormatter) -> str: ... + def format_help(self, formatter: HelpFormatter) -> str: ... + def get_description(self) -> str | None: ... def get_option(self, opt_str: str) -> Option | None: ... def has_option(self, opt_str: str) -> bool: ... def remove_option(self, opt_str: str) -> None: ... - def set_conflict_handler(self, handler: Any) -> None: ... - def set_description(self, description: Any) -> None: ... + def set_conflict_handler(self, handler: Literal["error", "resolve"]) -> None: ... + def set_description(self, description: str | None) -> None: ... class OptionGroup(OptionContainer): option_list: list[Option] @@ -172,15 +175,15 @@ class OptionGroup(OptionContainer): def set_title(self, title: str) -> None: ... class Values: - def __init__(self, defaults: Mapping[str, Any] | None = None) -> None: ... - def _update(self, dict: Mapping[str, Any], mode: Any) -> None: ... - def _update_careful(self, dict: Mapping[str, Any]) -> None: ... - def _update_loose(self, dict: Mapping[str, Any]) -> None: ... - def ensure_value(self, attr: str, value: Any) -> Any: ... + def __init__(self, defaults: Mapping[str, Incomplete] | None = None) -> None: ... + def _update(self, dict: Mapping[str, Incomplete], mode) -> None: ... + def _update_careful(self, dict: Mapping[str, Incomplete]) -> None: ... + def _update_loose(self, dict: Mapping[str, Incomplete]) -> None: ... + def ensure_value(self, attr: str, value): ... def read_file(self, filename: str, mode: str = "careful") -> None: ... def read_module(self, modname: str, mode: str = "careful") -> None: ... - def __getattr__(self, name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... + def __getattr__(self, name: str): ... + def __setattr__(self, name: str, value, /) -> None: ... def __eq__(self, other: object) -> bool: ... class OptionParser(OptionContainer): @@ -190,9 +193,9 @@ class OptionParser(OptionContainer): largs: list[str] | None option_groups: list[OptionGroup] option_list: list[Option] - process_default_values: Any + process_default_values: bool prog: str | None - rargs: list[Any] | None + rargs: list[str] | None standard_option_list: list[Option] usage: str | None values: Values | None @@ -214,28 +217,28 @@ class OptionParser(OptionContainer): def _add_version_option(self) -> None: ... def _create_option_list(self) -> None: ... def _get_all_options(self) -> list[Option]: ... - def _get_args(self, args: Iterable[Any]) -> list[Any]: ... + def _get_args(self, args: Iterable[Incomplete]) -> list[Incomplete]: ... def _init_parsing_state(self) -> None: ... def _match_long_opt(self, opt: str) -> str: ... def _populate_option_list(self, option_list: Iterable[Option], add_help: bool = True) -> None: ... - def _process_args(self, largs: list[Any], rargs: list[Any], values: Values) -> None: ... - def _process_long_opt(self, rargs: list[Any], values: Any) -> None: ... - def _process_short_opts(self, rargs: list[Any], values: Any) -> None: ... + def _process_args(self, largs: list[Incomplete], rargs: list[Incomplete], values: Values) -> None: ... + def _process_long_opt(self, rargs: list[Incomplete], values) -> None: ... + def _process_short_opts(self, rargs: list[Incomplete], values) -> None: ... @overload - def add_option_group(self, __opt_group: OptionGroup) -> OptionGroup: ... + def add_option_group(self, opt_group: OptionGroup, /) -> OptionGroup: ... @overload - def add_option_group(self, *args: Any, **kwargs: Any) -> OptionGroup: ... + def add_option_group(self, *args, **kwargs) -> OptionGroup: ... def check_values(self, values: Values, args: list[str]) -> tuple[Values, list[str]]: ... def disable_interspersed_args(self) -> None: ... def enable_interspersed_args(self) -> None: ... def error(self, msg: str) -> None: ... def exit(self, status: int = 0, msg: str | None = None) -> None: ... - def expand_prog_name(self, s: str | None) -> Any: ... - def format_epilog(self, formatter: HelpFormatter) -> Any: ... + def expand_prog_name(self, s: str) -> str: ... + def format_epilog(self, formatter: HelpFormatter) -> str: ... def format_help(self, formatter: HelpFormatter | None = None) -> str: ... def format_option_help(self, formatter: HelpFormatter | None = None) -> str: ... def get_default_values(self) -> Values: ... - def get_option_group(self, opt_str: str) -> Any: ... + def get_option_group(self, opt_str: str) -> OptionGroup | None: ... def get_prog_name(self) -> str: ... def get_usage(self) -> str: ... def get_version(self) -> str: ... @@ -246,7 +249,7 @@ class OptionParser(OptionContainer): def print_usage(self, file: IO[str] | None = None) -> None: ... def print_help(self, file: IO[str] | None = None) -> None: ... def print_version(self, file: IO[str] | None = None) -> None: ... - def set_default(self, dest: Any, value: Any) -> None: ... - def set_defaults(self, **kwargs: Any) -> None: ... - def set_process_default_values(self, process: Any) -> None: ... + def set_default(self, dest, value) -> None: ... + def set_defaults(self, **kwargs) -> None: ... + def set_process_default_values(self, process) -> None: ... def set_usage(self, usage: str) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index eef52e7a8b3b..89d906d4edfc 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -493,8 +493,8 @@ def get_exec_path(env: Mapping[str, str] | None = None) -> list[str]: ... def getlogin() -> str: ... def getpid() -> int: ... def getppid() -> int: ... -def strerror(__code: int) -> str: ... -def umask(__mask: int) -> int: ... +def strerror(code: int, /) -> str: ... +def umask(mask: int, /) -> int: ... @final class uname_result(structseq[str], tuple[str, str, str, str, str]): if sys.version_info >= (3, 10): @@ -516,9 +516,9 @@ if sys.platform != "win32": def getegid() -> int: ... def geteuid() -> int: ... def getgid() -> int: ... - def getgrouplist(__user: str, __group: int) -> list[int]: ... + def getgrouplist(user: str, group: int, /) -> list[int]: ... def getgroups() -> list[int]: ... # Unix only, behaves differently on Mac - def initgroups(__username: str, __gid: int) -> None: ... + def initgroups(username: str, gid: int, /) -> None: ... def getpgid(pid: int) -> int: ... def getpgrp() -> int: ... def getpriority(which: int, who: int) -> int: ... @@ -528,21 +528,21 @@ if sys.platform != "win32": def getresgid() -> tuple[int, int, int]: ... def getuid() -> int: ... - def setegid(__egid: int) -> None: ... - def seteuid(__euid: int) -> None: ... - def setgid(__gid: int) -> None: ... - def setgroups(__groups: Sequence[int]) -> None: ... + def setegid(egid: int, /) -> None: ... + def seteuid(euid: int, /) -> None: ... + def setgid(gid: int, /) -> None: ... + def setgroups(groups: Sequence[int], /) -> None: ... def setpgrp() -> None: ... - def setpgid(__pid: int, __pgrp: int) -> None: ... - def setregid(__rgid: int, __egid: int) -> None: ... + def setpgid(pid: int, pgrp: int, /) -> None: ... + def setregid(rgid: int, egid: int, /) -> None: ... if sys.platform != "darwin": - def setresgid(__rgid: int, __egid: int, __sgid: int) -> None: ... - def setresuid(__ruid: int, __euid: int, __suid: int) -> None: ... + def setresgid(rgid: int, egid: int, sgid: int, /) -> None: ... + def setresuid(ruid: int, euid: int, suid: int, /) -> None: ... - def setreuid(__ruid: int, __euid: int) -> None: ... - def getsid(__pid: int) -> int: ... + def setreuid(ruid: int, euid: int, /) -> None: ... + def getsid(pid: int, /) -> int: ... def setsid() -> None: ... - def setuid(__uid: int) -> None: ... + def setuid(uid: int, /) -> None: ... def uname() -> uname_result: ... @overload @@ -555,14 +555,14 @@ if sys.platform != "win32": def getenvb(key: bytes) -> bytes | None: ... @overload def getenvb(key: bytes, default: _T) -> bytes | _T: ... - def putenv(__name: StrOrBytesPath, __value: StrOrBytesPath) -> None: ... - def unsetenv(__name: StrOrBytesPath) -> None: ... + def putenv(name: StrOrBytesPath, value: StrOrBytesPath, /) -> None: ... + def unsetenv(name: StrOrBytesPath, /) -> None: ... else: - def putenv(__name: str, __value: str) -> None: ... + def putenv(name: str, value: str, /) -> None: ... if sys.version_info >= (3, 9): - def unsetenv(__name: str) -> None: ... + def unsetenv(name: str, /) -> None: ... _Opener: TypeAlias = Callable[[str, int], int] @@ -644,50 +644,50 @@ def fdopen( opener: _Opener | None = ..., ) -> IO[Any]: ... def close(fd: int) -> None: ... -def closerange(__fd_low: int, __fd_high: int) -> None: ... +def closerange(fd_low: int, fd_high: int, /) -> None: ... def device_encoding(fd: int) -> str | None: ... -def dup(__fd: int) -> int: ... +def dup(fd: int, /) -> int: ... def dup2(fd: int, fd2: int, inheritable: bool = True) -> int: ... def fstat(fd: int) -> stat_result: ... -def ftruncate(__fd: int, __length: int) -> None: ... +def ftruncate(fd: int, length: int, /) -> None: ... def fsync(fd: FileDescriptorLike) -> None: ... -def isatty(__fd: int) -> bool: ... +def isatty(fd: int, /) -> bool: ... if sys.platform != "win32" and sys.version_info >= (3, 11): - def login_tty(__fd: int) -> None: ... + def login_tty(fd: int, /) -> None: ... if sys.version_info >= (3, 11): - def lseek(__fd: int, __position: int, __whence: int) -> int: ... + def lseek(fd: int, position: int, whence: int, /) -> int: ... else: - def lseek(__fd: int, __position: int, __how: int) -> int: ... + def lseek(fd: int, position: int, how: int, /) -> int: ... def open(path: StrOrBytesPath, flags: int, mode: int = 0o777, *, dir_fd: int | None = None) -> int: ... def pipe() -> tuple[int, int]: ... -def read(__fd: int, __length: int) -> bytes: ... +def read(fd: int, length: int, /) -> bytes: ... if sys.version_info >= (3, 12) or sys.platform != "win32": - def get_blocking(__fd: int) -> bool: ... - def set_blocking(__fd: int, __blocking: bool) -> None: ... + def get_blocking(fd: int, /) -> bool: ... + def set_blocking(fd: int, blocking: bool, /) -> None: ... if sys.platform != "win32": def fchmod(fd: int, mode: int) -> None: ... def fchown(fd: int, uid: int, gid: int) -> None: ... - def fpathconf(__fd: int, __name: str | int) -> int: ... - def fstatvfs(__fd: int) -> statvfs_result: ... - def lockf(__fd: int, __command: int, __length: int) -> None: ... + def fpathconf(fd: int, name: str | int, /) -> int: ... + def fstatvfs(fd: int, /) -> statvfs_result: ... + def lockf(fd: int, command: int, length: int, /) -> None: ... def openpty() -> tuple[int, int]: ... # some flavors of Unix if sys.platform != "darwin": def fdatasync(fd: FileDescriptorLike) -> None: ... - def pipe2(__flags: int) -> tuple[int, int]: ... # some flavors of Unix - def posix_fallocate(__fd: int, __offset: int, __length: int) -> None: ... - def posix_fadvise(__fd: int, __offset: int, __length: int, __advice: int) -> None: ... + def pipe2(flags: int, /) -> tuple[int, int]: ... # some flavors of Unix + def posix_fallocate(fd: int, offset: int, length: int, /) -> None: ... + def posix_fadvise(fd: int, offset: int, length: int, advice: int, /) -> None: ... - def pread(__fd: int, __length: int, __offset: int) -> bytes: ... - def pwrite(__fd: int, __buffer: ReadableBuffer, __offset: int) -> int: ... + def pread(fd: int, length: int, offset: int, /) -> bytes: ... + def pwrite(fd: int, buffer: ReadableBuffer, offset: int, /) -> int: ... # In CI, stubtest sometimes reports that these are available on MacOS, sometimes not - def preadv(__fd: int, __buffers: SupportsLenAndGetItem[WriteableBuffer], __offset: int, __flags: int = 0) -> int: ... - def pwritev(__fd: int, __buffers: SupportsLenAndGetItem[ReadableBuffer], __offset: int, __flags: int = 0) -> int: ... + def preadv(fd: int, buffers: SupportsLenAndGetItem[WriteableBuffer], offset: int, flags: int = 0, /) -> int: ... + def pwritev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], offset: int, flags: int = 0, /) -> int: ... if sys.platform != "darwin": if sys.version_info >= (3, 10): RWF_APPEND: int # docs say available on 3.7+, stubtest says otherwise @@ -709,8 +709,8 @@ if sys.platform != "win32": flags: int = 0, ) -> int: ... # FreeBSD and Mac OS X only - def readv(__fd: int, __buffers: SupportsLenAndGetItem[WriteableBuffer]) -> int: ... - def writev(__fd: int, __buffers: SupportsLenAndGetItem[ReadableBuffer]) -> int: ... + def readv(fd: int, buffers: SupportsLenAndGetItem[WriteableBuffer], /) -> int: ... + def writev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], /) -> int: ... @final class terminal_size(structseq[int], tuple[int, int]): @@ -722,21 +722,21 @@ class terminal_size(structseq[int], tuple[int, int]): @property def lines(self) -> int: ... -def get_terminal_size(__fd: int = ...) -> terminal_size: ... -def get_inheritable(__fd: int) -> bool: ... -def set_inheritable(__fd: int, __inheritable: bool) -> None: ... +def get_terminal_size(fd: int = ..., /) -> terminal_size: ... +def get_inheritable(fd: int, /) -> bool: ... +def set_inheritable(fd: int, inheritable: bool, /) -> None: ... if sys.platform == "win32": - def get_handle_inheritable(__handle: int) -> bool: ... - def set_handle_inheritable(__handle: int, __inheritable: bool) -> None: ... + def get_handle_inheritable(handle: int, /) -> bool: ... + def set_handle_inheritable(handle: int, inheritable: bool, /) -> None: ... if sys.platform != "win32": # Unix only - def tcgetpgrp(__fd: int) -> int: ... - def tcsetpgrp(__fd: int, __pgid: int) -> None: ... - def ttyname(__fd: int) -> str: ... + def tcgetpgrp(fd: int, /) -> int: ... + def tcsetpgrp(fd: int, pgid: int, /) -> None: ... + def ttyname(fd: int, /) -> str: ... -def write(__fd: int, __data: ReadableBuffer) -> int: ... +def write(fd: int, data: ReadableBuffer, /) -> int: ... def access( path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, effective_ids: bool = False, follow_symlinks: bool = True ) -> bool: ... @@ -779,9 +779,9 @@ def makedirs(name: StrOrBytesPath, mode: int = 0o777, exist_ok: bool = False) -> if sys.platform != "win32": def mknod(path: StrOrBytesPath, mode: int = 0o600, device: int = 0, *, dir_fd: int | None = None) -> None: ... - def major(__device: int) -> int: ... - def minor(__device: int) -> int: ... - def makedev(__major: int, __minor: int) -> int: ... + def major(device: int, /) -> int: ... + def minor(device: int, /) -> int: ... + def makedev(major: int, minor: int, /) -> int: ... def pathconf(path: FileDescriptorOrPath, name: str | int) -> int: ... # Unix only def readlink(path: GenericPath[AnyStr], *, dir_fd: int | None = None) -> AnyStr: ... @@ -901,21 +901,21 @@ _ExecVArgs: TypeAlias = ( # we limit to str | bytes. _ExecEnv: TypeAlias = Mapping[bytes, bytes | str] | Mapping[str, bytes | str] -def execv(__path: StrOrBytesPath, __argv: _ExecVArgs) -> NoReturn: ... +def execv(path: StrOrBytesPath, argv: _ExecVArgs, /) -> NoReturn: ... def execve(path: FileDescriptorOrPath, argv: _ExecVArgs, env: _ExecEnv) -> NoReturn: ... def execvp(file: StrOrBytesPath, args: _ExecVArgs) -> NoReturn: ... def execvpe(file: StrOrBytesPath, args: _ExecVArgs, env: _ExecEnv) -> NoReturn: ... def _exit(status: int) -> NoReturn: ... -def kill(__pid: int, __signal: int) -> None: ... +def kill(pid: int, signal: int, /) -> None: ... if sys.platform != "win32": # Unix only def fork() -> int: ... def forkpty() -> tuple[int, int]: ... # some flavors of Unix - def killpg(__pgid: int, __signal: int) -> None: ... - def nice(__increment: int) -> int: ... + def killpg(pgid: int, signal: int, /) -> None: ... + def nice(increment: int, /) -> int: ... if sys.platform != "darwin": - def plock(__op: int) -> None: ... # ???op is int? + def plock(op: int, /) -> None: ... # ???op is int? class _wrap_close(_TextIOWrapper): def __init__(self, stream: _TextIOWrapper, proc: Popen[str]) -> None: ... @@ -930,8 +930,8 @@ if sys.platform != "win32": def spawnve(mode: int, file: StrOrBytesPath, args: _ExecVArgs, env: _ExecEnv) -> int: ... else: - def spawnv(__mode: int, __path: StrOrBytesPath, __argv: _ExecVArgs) -> int: ... - def spawnve(__mode: int, __path: StrOrBytesPath, __argv: _ExecVArgs, __env: _ExecEnv) -> int: ... + def spawnv(mode: int, path: StrOrBytesPath, argv: _ExecVArgs, /) -> int: ... + def spawnve(mode: int, path: StrOrBytesPath, argv: _ExecVArgs, env: _ExecEnv, /) -> int: ... def system(command: StrOrBytesPath) -> int: ... @final @@ -951,7 +951,7 @@ class times_result(structseq[float], tuple[float, float, float, float, float]): def elapsed(self) -> float: ... def times() -> times_result: ... -def waitpid(__pid: int, __options: int) -> tuple[int, int]: ... +def waitpid(pid: int, options: int, /) -> tuple[int, int]: ... if sys.platform == "win32": if sys.version_info >= (3, 10): @@ -988,13 +988,13 @@ else: @property def si_code(self) -> int: ... - def waitid(__idtype: int, __ident: int, __options: int) -> waitid_result | None: ... + def waitid(idtype: int, ident: int, options: int, /) -> waitid_result | None: ... from resource import struct_rusage def wait3(options: int) -> tuple[int, int, struct_rusage]: ... def wait4(pid: int, options: int) -> tuple[int, int, struct_rusage]: ... - def WCOREDUMP(__status: int) -> bool: ... + def WCOREDUMP(status: int, /) -> bool: ... def WIFCONTINUED(status: int) -> bool: ... def WIFSTOPPED(status: int) -> bool: ... def WIFSIGNALED(status: int) -> bool: ... @@ -1003,9 +1003,10 @@ else: def WSTOPSIG(status: int) -> int: ... def WTERMSIG(status: int) -> int: ... def posix_spawn( - __path: StrOrBytesPath, - __argv: _ExecVArgs, - __env: _ExecEnv, + path: StrOrBytesPath, + argv: _ExecVArgs, + env: _ExecEnv, + /, *, file_actions: Sequence[tuple[Any, ...]] | None = ..., setpgroup: int | None = ..., @@ -1016,9 +1017,10 @@ else: scheduler: tuple[Any, sched_param] | None = ..., ) -> int: ... def posix_spawnp( - __path: StrOrBytesPath, - __argv: _ExecVArgs, - __env: _ExecEnv, + path: StrOrBytesPath, + argv: _ExecVArgs, + env: _ExecEnv, + /, *, file_actions: Sequence[tuple[Any, ...]] | None = ..., setpgroup: int | None = ..., @@ -1046,26 +1048,26 @@ if sys.platform != "win32": def sched_get_priority_max(policy: int) -> int: ... # some flavors of Unix def sched_yield() -> None: ... # some flavors of Unix if sys.platform != "darwin": - def sched_setscheduler(__pid: int, __policy: int, __param: sched_param) -> None: ... # some flavors of Unix - def sched_getscheduler(__pid: int) -> int: ... # some flavors of Unix - def sched_rr_get_interval(__pid: int) -> float: ... # some flavors of Unix - def sched_setparam(__pid: int, __param: sched_param) -> None: ... # some flavors of Unix - def sched_getparam(__pid: int) -> sched_param: ... # some flavors of Unix - def sched_setaffinity(__pid: int, __mask: Iterable[int]) -> None: ... # some flavors of Unix - def sched_getaffinity(__pid: int) -> set[int]: ... # some flavors of Unix + def sched_setscheduler(pid: int, policy: int, param: sched_param, /) -> None: ... # some flavors of Unix + def sched_getscheduler(pid: int, /) -> int: ... # some flavors of Unix + def sched_rr_get_interval(pid: int, /) -> float: ... # some flavors of Unix + def sched_setparam(pid: int, param: sched_param, /) -> None: ... # some flavors of Unix + def sched_getparam(pid: int, /) -> sched_param: ... # some flavors of Unix + def sched_setaffinity(pid: int, mask: Iterable[int], /) -> None: ... # some flavors of Unix + def sched_getaffinity(pid: int, /) -> set[int]: ... # some flavors of Unix def cpu_count() -> int | None: ... if sys.platform != "win32": # Unix only - def confstr(__name: str | int) -> str | None: ... + def confstr(name: str | int, /) -> str | None: ... def getloadavg() -> tuple[float, float, float]: ... - def sysconf(__name: str | int) -> int: ... + def sysconf(name: str | int, /) -> int: ... if sys.platform == "linux": def getrandom(size: int, flags: int = 0) -> bytes: ... -def urandom(__size: int) -> bytes: ... +def urandom(size: int, /) -> bytes: ... if sys.platform != "win32": def register_at_fork( diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index c3b0b7ad6337..5ea025095f68 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -59,7 +59,7 @@ class PurePath(PathLike[str]): def is_absolute(self) -> bool: ... def is_reserved(self) -> bool: ... if sys.version_info >= (3, 12): - def is_relative_to(self, __other: StrPath, *_deprecated: StrPath) -> bool: ... + def is_relative_to(self, other: StrPath, /, *_deprecated: StrPath) -> bool: ... elif sys.version_info >= (3, 9): def is_relative_to(self, *other: StrPath) -> bool: ... @@ -69,7 +69,7 @@ class PurePath(PathLike[str]): def match(self, path_pattern: str) -> bool: ... if sys.version_info >= (3, 12): - def relative_to(self, __other: StrPath, *_deprecated: StrPath, walk_up: bool = False) -> Self: ... + def relative_to(self, other: StrPath, /, *_deprecated: StrPath, walk_up: bool = False) -> Self: ... else: def relative_to(self, *other: StrPath) -> Self: ... diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 0a4d439976ff..98ec80b0f14e 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -94,7 +94,7 @@ DEFAULT_PROTOCOL: int bytes_types: tuple[type[Any], ...] # undocumented class _ReadableFileobj(Protocol): - def read(self, __n: int) -> bytes: ... + def read(self, n: int, /) -> bytes: ... def readline(self) -> bytes: ... @final @@ -102,8 +102,8 @@ class PickleBuffer: def __init__(self, buffer: ReadableBuffer) -> None: ... def raw(self) -> memoryview: ... def release(self) -> None: ... - def __buffer__(self, __flags: int) -> memoryview: ... - def __release_buffer__(self, __buffer: memoryview) -> None: ... + def __buffer__(self, flags: int, /) -> memoryview: ... + def __release_buffer__(self, buffer: memoryview, /) -> None: ... _BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None @@ -127,7 +127,8 @@ def load( buffers: Iterable[Any] | None = (), ) -> Any: ... def loads( - __data: ReadableBuffer, + data: ReadableBuffer, + /, *, fix_imports: bool = True, encoding: str = "ASCII", @@ -162,7 +163,7 @@ class Pickler: buffer_callback: _BufferCallback = ..., ) -> None: ... def reducer_override(self, obj: Any) -> Any: ... - def dump(self, __obj: Any) -> None: ... + def dump(self, obj: Any, /) -> None: ... def clear_memo(self) -> None: ... def persistent_id(self, obj: Any) -> Any: ... @@ -179,7 +180,7 @@ class Unpickler: buffers: Iterable[Any] | None = ..., ) -> None: ... def load(self) -> Any: ... - def find_class(self, __module_name: str, __global_name: str) -> Any: ... + def find_class(self, module_name: str, global_name: str, /) -> Any: ... def persistent_load(self, pid: Any) -> Any: ... MARK: bytes diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index 29e7c0f01017..1fc471ac7d0b 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -112,11 +112,11 @@ def commonpath(paths: Iterable[BytesPath]) -> bytes: ... # but must be defined as pos-only in the stub or cross-platform code doesn't type-check, # as the parameter name is different in ntpath.join() @overload -def join(__a: LiteralString, *paths: LiteralString) -> LiteralString: ... +def join(a: LiteralString, /, *paths: LiteralString) -> LiteralString: ... @overload -def join(__a: StrPath, *paths: StrPath) -> str: ... +def join(a: StrPath, /, *paths: StrPath) -> str: ... @overload -def join(__a: BytesPath, *paths: BytesPath) -> bytes: ... +def join(a: BytesPath, /, *paths: BytesPath) -> bytes: ... if sys.version_info >= (3, 10): @overload diff --git a/mypy/typeshed/stdlib/profile.pyi b/mypy/typeshed/stdlib/profile.pyi index 6ae375004158..73eba36344fe 100644 --- a/mypy/typeshed/stdlib/profile.pyi +++ b/mypy/typeshed/stdlib/profile.pyi @@ -27,5 +27,5 @@ class Profile: def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... - def runcall(self, __func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... + def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def calibrate(self, m: int, verbose: int = 0) -> float: ... diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 86f88da9e712..d1571fd94be5 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -48,7 +48,8 @@ class Stats: sort_arg_dict_default: _SortArgDict def __init__( self, - __arg: None | str | Profile | _cProfile = ..., + arg: None | str | Profile | _cProfile = ..., + /, *args: None | str | Profile | _cProfile | Self, stream: IO[Any] | None = None, ) -> None: ... diff --git a/mypy/typeshed/stdlib/pwd.pyi b/mypy/typeshed/stdlib/pwd.pyi index 9a8e1036e550..a84ba324718a 100644 --- a/mypy/typeshed/stdlib/pwd.pyi +++ b/mypy/typeshed/stdlib/pwd.pyi @@ -24,5 +24,5 @@ if sys.platform != "win32": def pw_shell(self) -> str: ... def getpwall() -> list[struct_passwd]: ... - def getpwuid(__uid: int) -> struct_passwd: ... - def getpwnam(__name: str) -> struct_passwd: ... + def getpwuid(uid: int, /) -> struct_passwd: ... + def getpwnam(name: str, /) -> struct_passwd: ... diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 2188e458474c..10011b437b6a 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -24,14 +24,14 @@ _Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] @final class XMLParserType: - def Parse(self, __data: str | ReadableBuffer, __isfinal: bool = False) -> int: ... - def ParseFile(self, __file: SupportsRead[bytes]) -> int: ... - def SetBase(self, __base: str) -> None: ... + def Parse(self, data: str | ReadableBuffer, isfinal: bool = False, /) -> int: ... + def ParseFile(self, file: SupportsRead[bytes], /) -> int: ... + def SetBase(self, base: str, /) -> None: ... def GetBase(self) -> str | None: ... def GetInputContext(self) -> bytes | None: ... - def ExternalEntityParserCreate(self, __context: str | None, __encoding: str = ...) -> XMLParserType: ... - def SetParamEntityParsing(self, __flag: int) -> int: ... - def UseForeignDTD(self, __flag: bool = True) -> None: ... + def ExternalEntityParserCreate(self, context: str | None, encoding: str = ..., /) -> XMLParserType: ... + def SetParamEntityParsing(self, flag: int, /) -> int: ... + def UseForeignDTD(self, flag: bool = True, /) -> None: ... @property def intern(self) -> dict[str, str]: ... buffer_size: int @@ -75,7 +75,7 @@ class XMLParserType: ExternalEntityRefHandler: Callable[[str, str | None, str | None, str | None], int] | None SkippedEntityHandler: Callable[[str, bool], Any] | None -def ErrorString(__code: int) -> str: ... +def ErrorString(code: int, /) -> str: ... # intern is undocumented def ParserCreate( diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 84c6cfceb1de..7945c5f46cdc 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -72,11 +72,11 @@ class Match(Generic[AnyStr]): def expand(self, template: AnyStr) -> AnyStr: ... # group() returns "AnyStr" or "AnyStr | None", depending on the pattern. @overload - def group(self, __group: Literal[0] = 0) -> AnyStr: ... + def group(self, group: Literal[0] = 0, /) -> AnyStr: ... @overload - def group(self, __group: str | int) -> AnyStr | Any: ... + def group(self, group: str | int, /) -> AnyStr | Any: ... @overload - def group(self, __group1: str | int, __group2: str | int, *groups: str | int) -> tuple[AnyStr | Any, ...]: ... + def group(self, group1: str | int, group2: str | int, /, *groups: str | int) -> tuple[AnyStr | Any, ...]: ... # Each item of groups()'s return tuple is either "AnyStr" or # "AnyStr | None", depending on the pattern. @overload @@ -89,18 +89,18 @@ class Match(Generic[AnyStr]): def groupdict(self) -> dict[str, AnyStr | Any]: ... @overload def groupdict(self, default: _T) -> dict[str, AnyStr | _T]: ... - def start(self, __group: int | str = 0) -> int: ... - def end(self, __group: int | str = 0) -> int: ... - def span(self, __group: int | str = 0) -> tuple[int, int]: ... + def start(self, group: int | str = 0, /) -> int: ... + def end(self, group: int | str = 0, /) -> int: ... + def span(self, group: int | str = 0, /) -> tuple[int, int]: ... @property def regs(self) -> tuple[tuple[int, int], ...]: ... # undocumented # __getitem__() returns "AnyStr" or "AnyStr | None", depending on the pattern. @overload - def __getitem__(self, __key: Literal[0]) -> AnyStr: ... + def __getitem__(self, key: Literal[0], /) -> AnyStr: ... @overload - def __getitem__(self, __key: int | str) -> AnyStr | Any: ... + def __getitem__(self, key: int | str, /) -> AnyStr | Any: ... def __copy__(self) -> Match[AnyStr]: ... - def __deepcopy__(self, __memo: Any) -> Match[AnyStr]: ... + def __deepcopy__(self, memo: Any, /) -> Match[AnyStr]: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... @@ -174,8 +174,8 @@ class Pattern(Generic[AnyStr]): @overload def subn(self, repl: AnyStr | Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = 0) -> tuple[AnyStr, int]: ... def __copy__(self) -> Pattern[AnyStr]: ... - def __deepcopy__(self, __memo: Any) -> Pattern[AnyStr]: ... - def __eq__(self, __value: object) -> bool: ... + def __deepcopy__(self, memo: Any, /) -> Pattern[AnyStr]: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/readline.pyi b/mypy/typeshed/stdlib/readline.pyi index 14c01a986351..688ae48d9f92 100644 --- a/mypy/typeshed/stdlib/readline.pyi +++ b/mypy/typeshed/stdlib/readline.pyi @@ -7,30 +7,30 @@ if sys.platform != "win32": _Completer: TypeAlias = Callable[[str, int], str | None] _CompDisp: TypeAlias = Callable[[str, Sequence[str], int], None] - def parse_and_bind(__string: str) -> None: ... - def read_init_file(__filename: StrOrBytesPath | None = None) -> None: ... + def parse_and_bind(string: str, /) -> None: ... + def read_init_file(filename: StrOrBytesPath | None = None, /) -> None: ... def get_line_buffer() -> str: ... - def insert_text(__string: str) -> None: ... + def insert_text(string: str, /) -> None: ... def redisplay() -> None: ... - def read_history_file(__filename: StrOrBytesPath | None = None) -> None: ... - def write_history_file(__filename: StrOrBytesPath | None = None) -> None: ... - def append_history_file(__nelements: int, __filename: StrOrBytesPath | None = None) -> None: ... + def read_history_file(filename: StrOrBytesPath | None = None, /) -> None: ... + def write_history_file(filename: StrOrBytesPath | None = None, /) -> None: ... + def append_history_file(nelements: int, filename: StrOrBytesPath | None = None, /) -> None: ... def get_history_length() -> int: ... - def set_history_length(__length: int) -> None: ... + def set_history_length(length: int, /) -> None: ... def clear_history() -> None: ... def get_current_history_length() -> int: ... - def get_history_item(__index: int) -> str: ... - def remove_history_item(__pos: int) -> None: ... - def replace_history_item(__pos: int, __line: str) -> None: ... - def add_history(__string: str) -> None: ... - def set_auto_history(__enabled: bool) -> None: ... - def set_startup_hook(__function: Callable[[], object] | None = None) -> None: ... - def set_pre_input_hook(__function: Callable[[], object] | None = None) -> None: ... - def set_completer(__function: _Completer | None = None) -> None: ... + def get_history_item(index: int, /) -> str: ... + def remove_history_item(pos: int, /) -> None: ... + def replace_history_item(pos: int, line: str, /) -> None: ... + def add_history(string: str, /) -> None: ... + def set_auto_history(enabled: bool, /) -> None: ... + def set_startup_hook(function: Callable[[], object] | None = None, /) -> None: ... + def set_pre_input_hook(function: Callable[[], object] | None = None, /) -> None: ... + def set_completer(function: _Completer | None = None, /) -> None: ... def get_completer() -> _Completer | None: ... def get_completion_type() -> int: ... def get_begidx() -> int: ... def get_endidx() -> int: ... - def set_completer_delims(__string: str) -> None: ... + def set_completer_delims(string: str, /) -> None: ... def get_completer_delims() -> str: ... - def set_completion_display_matches_hook(__function: _CompDisp | None = None) -> None: ... + def set_completion_display_matches_hook(function: _CompDisp | None = None, /) -> None: ... diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi index f40e5ec1ea55..5e468c2cead5 100644 --- a/mypy/typeshed/stdlib/resource.pyi +++ b/mypy/typeshed/stdlib/resource.pyi @@ -83,12 +83,12 @@ if sys.platform != "win32": def ru_nivcsw(self) -> int: ... def getpagesize() -> int: ... - def getrlimit(__resource: int) -> tuple[int, int]: ... - def getrusage(__who: int) -> struct_rusage: ... - def setrlimit(__resource: int, __limits: tuple[int, int]) -> None: ... + def getrlimit(resource: int, /) -> tuple[int, int]: ... + def getrusage(who: int, /) -> struct_rusage: ... + def setrlimit(resource: int, limits: tuple[int, int], /) -> None: ... if sys.platform == "linux": if sys.version_info >= (3, 12): - def prlimit(__pid: int, __resource: int, __limits: tuple[int, int] | None = None) -> tuple[int, int]: ... + def prlimit(pid: int, resource: int, limits: tuple[int, int] | None = None, /) -> tuple[int, int]: ... else: - def prlimit(__pid: int, __resource: int, __limits: tuple[int, int] = ...) -> tuple[int, int]: ... + def prlimit(pid: int, resource: int, limits: tuple[int, int] = ..., /) -> tuple[int, int]: ... error = OSError diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index afab88e18453..6d4c8d8f4c15 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -28,7 +28,7 @@ class poll: def poll(self, timeout: float | None = ...) -> list[tuple[int, int]]: ... def select( - __rlist: Iterable[Any], __wlist: Iterable[Any], __xlist: Iterable[Any], __timeout: float | None = None + rlist: Iterable[Any], wlist: Iterable[Any], xlist: Iterable[Any], timeout: float | None = None, / ) -> tuple[list[Any], list[Any], list[Any]]: ... error = OSError @@ -60,11 +60,11 @@ if sys.platform != "linux" and sys.platform != "win32": def __init__(self) -> None: ... def close(self) -> None: ... def control( - self, __changelist: Iterable[kevent] | None, __maxevents: int, __timeout: float | None = None + self, changelist: Iterable[kevent] | None, maxevents: int, timeout: float | None = None, / ) -> list[kevent]: ... def fileno(self) -> int: ... @classmethod - def fromfd(cls, __fd: FileDescriptorLike) -> kqueue: ... + def fromfd(cls, fd: FileDescriptorLike, /) -> kqueue: ... KQ_EV_ADD: int KQ_EV_CLEAR: int @@ -112,9 +112,10 @@ if sys.platform == "linux": def __enter__(self) -> Self: ... def __exit__( self, - __exc_type: type[BaseException] | None = None, - __exc_value: BaseException | None = ..., - __exc_tb: TracebackType | None = None, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = ..., + exc_tb: TracebackType | None = None, + /, ) -> None: ... def close(self) -> None: ... closed: bool @@ -124,7 +125,7 @@ if sys.platform == "linux": def unregister(self, fd: FileDescriptorLike) -> None: ... def poll(self, timeout: float | None = None, maxevents: int = -1) -> list[tuple[int, int]]: ... @classmethod - def fromfd(cls, __fd: FileDescriptorLike) -> epoll: ... + def fromfd(cls, fd: FileDescriptorLike, /) -> epoll: ... EPOLLERR: int EPOLLEXCLUSIVE: int diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 544473df9932..d1fb3ba963d4 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -67,15 +67,15 @@ SIG_IGN: Handlers _SIGNUM: TypeAlias = int | Signals _HANDLER: TypeAlias = Callable[[int, FrameType | None], Any] | int | Handlers | None -def default_int_handler(__signalnum: int, __frame: FrameType | None) -> Never: ... +def default_int_handler(signalnum: int, frame: FrameType | None, /) -> Never: ... if sys.version_info >= (3, 10): # arguments changed in 3.10.2 def getsignal(signalnum: _SIGNUM) -> _HANDLER: ... def signal(signalnum: _SIGNUM, handler: _HANDLER) -> _HANDLER: ... else: - def getsignal(__signalnum: _SIGNUM) -> _HANDLER: ... - def signal(__signalnum: _SIGNUM, __handler: _HANDLER) -> _HANDLER: ... + def getsignal(signalnum: _SIGNUM, /) -> _HANDLER: ... + def signal(signalnum: _SIGNUM, handler: _HANDLER, /) -> _HANDLER: ... SIGABRT: Signals SIGFPE: Signals @@ -130,22 +130,22 @@ else: SIG_BLOCK = Sigmasks.SIG_BLOCK SIG_UNBLOCK = Sigmasks.SIG_UNBLOCK SIG_SETMASK = Sigmasks.SIG_SETMASK - def alarm(__seconds: int) -> int: ... - def getitimer(__which: int) -> tuple[float, float]: ... + def alarm(seconds: int, /) -> int: ... + def getitimer(which: int, /) -> tuple[float, float]: ... def pause() -> None: ... - def pthread_kill(__thread_id: int, __signalnum: int) -> None: ... + def pthread_kill(thread_id: int, signalnum: int, /) -> None: ... if sys.version_info >= (3, 10): # arguments changed in 3.10.2 def pthread_sigmask(how: int, mask: Iterable[int]) -> set[_SIGNUM]: ... else: - def pthread_sigmask(__how: int, __mask: Iterable[int]) -> set[_SIGNUM]: ... + def pthread_sigmask(how: int, mask: Iterable[int], /) -> set[_SIGNUM]: ... - def setitimer(__which: int, __seconds: float, __interval: float = 0.0) -> tuple[float, float]: ... - def siginterrupt(__signalnum: int, __flag: bool) -> None: ... + def setitimer(which: int, seconds: float, interval: float = 0.0, /) -> tuple[float, float]: ... + def siginterrupt(signalnum: int, flag: bool, /) -> None: ... def sigpending() -> Any: ... if sys.version_info >= (3, 10): # argument changed in 3.10.2 def sigwait(sigset: Iterable[int]) -> _SIGNUM: ... else: - def sigwait(__sigset: Iterable[int]) -> _SIGNUM: ... + def sigwait(sigset: Iterable[int], /) -> _SIGNUM: ... if sys.platform != "darwin": SIGCLD: Signals SIGPOLL: Signals @@ -176,17 +176,17 @@ else: def si_band(self) -> int: ... if sys.version_info >= (3, 10): - def sigtimedwait(__sigset: Iterable[int], __timeout: float) -> struct_siginfo | None: ... - def sigwaitinfo(__sigset: Iterable[int]) -> struct_siginfo: ... + def sigtimedwait(sigset: Iterable[int], timeout: float, /) -> struct_siginfo | None: ... + def sigwaitinfo(sigset: Iterable[int], /) -> struct_siginfo: ... else: def sigtimedwait(sigset: Iterable[int], timeout: float) -> struct_siginfo | None: ... def sigwaitinfo(sigset: Iterable[int]) -> struct_siginfo: ... -def strsignal(__signalnum: _SIGNUM) -> str | None: ... +def strsignal(signalnum: _SIGNUM, /) -> str | None: ... def valid_signals() -> set[Signals]: ... -def raise_signal(__signalnum: _SIGNUM) -> None: ... +def raise_signal(signalnum: _SIGNUM, /) -> None: ... def set_wakeup_fd(fd: int, *, warn_on_full_buffer: bool = ...) -> int: ... if sys.version_info >= (3, 9): if sys.platform == "linux": - def pidfd_send_signal(__pidfd: int, __sig: int, __siginfo: None = None, __flags: int = ...) -> None: ... + def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = ..., /) -> None: ... diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi index 6db7daebbb41..a762427bcab3 100644 --- a/mypy/typeshed/stdlib/smtplib.pyi +++ b/mypy/typeshed/stdlib/smtplib.pyi @@ -68,9 +68,9 @@ def quotedata(data: str) -> str: ... class _AuthObject(Protocol): @overload - def __call__(self, __challenge: None = None) -> str | None: ... + def __call__(self, challenge: None = None, /) -> str | None: ... @overload - def __call__(self, __challenge: bytes) -> str: ... + def __call__(self, challenge: bytes, /) -> str: ... class SMTP: debuglevel: int diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index ce5e35228fe4..cdbd70533714 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -682,8 +682,8 @@ if sys.platform == "win32": errorTab: dict[int, str] # undocumented class _SendableFile(Protocol): - def read(self, __size: int) -> bytes: ... - def seek(self, __offset: int) -> object: ... + def read(self, size: int, /) -> bytes: ... + def seek(self, offset: int, /) -> object: ... # optional fields: # @@ -803,7 +803,7 @@ def getfqdn(name: str = "") -> str: ... if sys.version_info >= (3, 11): def create_connection( address: tuple[str | None, int], - timeout: float | None = ..., # noqa: F811 + timeout: float | None = ..., source_address: _Address | None = None, *, all_errors: bool = False, @@ -811,7 +811,7 @@ if sys.version_info >= (3, 11): else: def create_connection( - address: tuple[str | None, int], timeout: float | None = ..., source_address: _Address | None = None # noqa: F811 + address: tuple[str | None, int], timeout: float | None = ..., source_address: _Address | None = None ) -> socket: ... def has_dualstack_ipv6() -> bool: ... diff --git a/mypy/typeshed/stdlib/spwd.pyi b/mypy/typeshed/stdlib/spwd.pyi index d362a0b77573..67ad3bfc751b 100644 --- a/mypy/typeshed/stdlib/spwd.pyi +++ b/mypy/typeshed/stdlib/spwd.pyi @@ -38,4 +38,4 @@ if sys.platform != "win32": def sp_flag(self) -> int: ... def getspall() -> list[struct_spwd]: ... - def getspnam(__arg: str) -> struct_spwd: ... + def getspnam(arg: str, /) -> struct_spwd: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 7cf75bbc33c5..068ce1514c3c 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -8,6 +8,7 @@ from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overlo from typing_extensions import Self, TypeAlias _T = TypeVar("_T") +_ConnectionT = TypeVar("_ConnectionT", bound=Connection) _CursorT = TypeVar("_CursorT", bound=Cursor) _SqliteData: TypeAlias = str | ReadableBuffer | int | float | None # Data that is passed through adapters can be of any type accepted by an adapter. @@ -217,57 +218,107 @@ if sys.version_info >= (3, 12): # Can take or return anything depending on what's in the registry. @overload -def adapt(__obj: Any, __proto: Any) -> Any: ... +def adapt(obj: Any, proto: Any, /) -> Any: ... @overload -def adapt(__obj: Any, __proto: Any, __alt: _T) -> Any | _T: ... +def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ... def complete_statement(statement: str) -> bool: ... if sys.version_info >= (3, 12): + @overload def connect( database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, + *, autocommit: bool = ..., ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + *, + autocommit: bool = ..., + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + autocommit: bool = ..., + ) -> _ConnectionT: ... else: + @overload def connect( database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... -def enable_callback_tracebacks(__enable: bool) -> None: ... +def enable_callback_tracebacks(enable: bool, /) -> None: ... if sys.version_info < (3, 12): # takes a pos-or-keyword argument because there is a C wrapper def enable_shared_cache(enable: int) -> None: ... if sys.version_info >= (3, 10): - def register_adapter(__type: type[_T], __adapter: _Adapter[_T]) -> None: ... - def register_converter(__typename: str, __converter: _Converter) -> None: ... + def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ... + def register_converter(typename: str, converter: _Converter, /) -> None: ... else: - def register_adapter(__type: type[_T], __caster: _Adapter[_T]) -> None: ... - def register_converter(__name: str, __converter: _Converter) -> None: ... + def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ... + def register_converter(name: str, converter: _Converter, /) -> None: ... class _AggregateProtocol(Protocol): - def step(self, __value: int) -> object: ... + def step(self, value: int, /) -> object: ... def finalize(self) -> int: ... class _SingleParamWindowAggregateClass(Protocol): - def step(self, __param: Any) -> object: ... - def inverse(self, __param: Any) -> object: ... + def step(self, param: Any, /) -> object: ... + def inverse(self, param: Any, /) -> object: ... def value(self) -> _SqliteData: ... def finalize(self) -> _SqliteData: ... @@ -344,7 +395,7 @@ class Connection: def close(self) -> None: ... if sys.version_info >= (3, 11): - def blobopen(self, __table: str, __column: str, __row: int, *, readonly: bool = False, name: str = "main") -> Blob: ... + def blobopen(self, table: str, column: str, row: int, /, *, readonly: bool = False, name: str = "main") -> Blob: ... def commit(self) -> None: ... def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... @@ -353,19 +404,19 @@ class Connection: # for the case where num_params = 1, which is expected to be the common case. @overload def create_window_function( - self, __name: str, __num_params: Literal[1], __aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None + self, name: str, num_params: Literal[1], aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None, / ) -> None: ... # And for num_params = -1, which means the aggregate must accept any number of parameters. @overload def create_window_function( - self, __name: str, __num_params: Literal[-1], __aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None + self, name: str, num_params: Literal[-1], aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None, / ) -> None: ... @overload def create_window_function( - self, __name: str, __num_params: int, __aggregate_class: Callable[[], _WindowAggregateClass] | None + self, name: str, num_params: int, aggregate_class: Callable[[], _WindowAggregateClass] | None, / ) -> None: ... - def create_collation(self, __name: str, __callback: Callable[[str, str], int | SupportsIndex] | None) -> None: ... + def create_collation(self, name: str, callback: Callable[[str, str], int | SupportsIndex] | None, /) -> None: ... def create_function( self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False ) -> None: ... @@ -373,9 +424,9 @@ class Connection: def cursor(self, factory: None = None) -> Cursor: ... @overload def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... - def execute(self, __sql: str, __parameters: _Parameters = ...) -> Cursor: ... - def executemany(self, __sql: str, __parameters: Iterable[_Parameters]) -> Cursor: ... - def executescript(self, __sql_script: str) -> Cursor: ... + def execute(self, sql: str, parameters: _Parameters = ..., /) -> Cursor: ... + def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... + def executescript(self, sql_script: str, /) -> Cursor: ... def interrupt(self) -> None: ... def iterdump(self) -> Generator[str, None, None]: ... def rollback(self) -> None: ... @@ -386,8 +437,8 @@ class Connection: def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... # enable_load_extension and load_extension is not available on python distributions compiled # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 - def enable_load_extension(self, __enable: bool) -> None: ... - def load_extension(self, __name: str) -> None: ... + def enable_load_extension(self, enable: bool, /) -> None: ... + def load_extension(self, name: str, /) -> None: ... def backup( self, target: Connection, @@ -398,18 +449,18 @@ class Connection: sleep: float = 0.25, ) -> None: ... if sys.version_info >= (3, 11): - def setlimit(self, __category: int, __limit: int) -> int: ... - def getlimit(self, __category: int) -> int: ... + def setlimit(self, category: int, limit: int, /) -> int: ... + def getlimit(self, category: int, /) -> int: ... def serialize(self, *, name: str = "main") -> bytes: ... - def deserialize(self, __data: ReadableBuffer, *, name: str = "main") -> None: ... + def deserialize(self, data: ReadableBuffer, /, *, name: str = "main") -> None: ... if sys.version_info >= (3, 12): - def getconfig(self, __op: int) -> bool: ... - def setconfig(self, __op: int, __enable: bool = True) -> bool: ... + def getconfig(self, op: int, /) -> bool: ... + def setconfig(self, op: int, enable: bool = True, /) -> bool: ... - def __call__(self, __sql: str) -> _Statement: ... + def __call__(self, sql: str, /) -> _Statement: ... def __enter__(self) -> Self: ... def __exit__( - self, __type: type[BaseException] | None, __value: BaseException | None, __traceback: TracebackType | None + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / ) -> Literal[False]: ... class Cursor(Iterator[Any]): @@ -424,18 +475,18 @@ class Cursor(Iterator[Any]): row_factory: Callable[[Cursor, Row], object] | None @property def rowcount(self) -> int: ... - def __init__(self, __cursor: Connection) -> None: ... + def __init__(self, cursor: Connection, /) -> None: ... def close(self) -> None: ... - def execute(self, __sql: str, __parameters: _Parameters = ()) -> Self: ... - def executemany(self, __sql: str, __seq_of_parameters: Iterable[_Parameters]) -> Self: ... - def executescript(self, __sql_script: str) -> Cursor: ... + def execute(self, sql: str, parameters: _Parameters = (), /) -> Self: ... + def executemany(self, sql: str, seq_of_parameters: Iterable[_Parameters], /) -> Self: ... + def executescript(self, sql_script: str, /) -> Cursor: ... def fetchall(self) -> list[Any]: ... def fetchmany(self, size: int | None = 1) -> list[Any]: ... # Returns either a row (as created by the row_factory) or None, but # putting None in the return annotation causes annoying false positives. def fetchone(self) -> Any: ... - def setinputsizes(self, __sizes: Unused) -> None: ... # does nothing - def setoutputsize(self, __size: Unused, __column: Unused = None) -> None: ... # does nothing + def setinputsizes(self, sizes: Unused, /) -> None: ... # does nothing + def setoutputsize(self, size: Unused, column: Unused = None, /) -> None: ... # does nothing def __iter__(self) -> Self: ... def __next__(self) -> Any: ... @@ -462,22 +513,22 @@ class PrepareProtocol: class ProgrammingError(DatabaseError): ... class Row: - def __init__(self, __cursor: Cursor, __data: tuple[Any, ...]) -> None: ... + def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... def keys(self) -> list[str]: ... @overload - def __getitem__(self, __key: int | str) -> Any: ... + def __getitem__(self, key: int | str, /) -> Any: ... @overload - def __getitem__(self, __key: slice) -> tuple[Any, ...]: ... + def __getitem__(self, key: slice, /) -> tuple[Any, ...]: ... def __hash__(self) -> int: ... def __iter__(self) -> Iterator[Any]: ... def __len__(self) -> int: ... # These return NotImplemented for anything that is not a Row. - def __eq__(self, __value: object) -> bool: ... - def __ge__(self, __value: object) -> bool: ... - def __gt__(self, __value: object) -> bool: ... - def __le__(self, __value: object) -> bool: ... - def __lt__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: object, /) -> bool: ... + def __gt__(self, value: object, /) -> bool: ... + def __le__(self, value: object, /) -> bool: ... + def __lt__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... @final class _Statement: ... @@ -488,13 +539,13 @@ if sys.version_info >= (3, 11): @final class Blob: def close(self) -> None: ... - def read(self, __length: int = -1) -> bytes: ... - def write(self, __data: ReadableBuffer) -> None: ... + def read(self, length: int = -1, /) -> bytes: ... + def write(self, data: ReadableBuffer, /) -> None: ... def tell(self) -> int: ... # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END - def seek(self, __offset: int, __origin: int = 0) -> None: ... + def seek(self, offset: int, origin: int = 0, /) -> None: ... def __len__(self) -> int: ... def __enter__(self) -> Self: ... - def __exit__(self, __type: object, __val: object, __tb: object) -> Literal[False]: ... - def __getitem__(self, __key: SupportsIndex | slice) -> int: ... - def __setitem__(self, __key: SupportsIndex | slice, __value: int) -> None: ... + def __exit__(self, type: object, val: object, tb: object, /) -> Literal[False]: ... + def __getitem__(self, key: SupportsIndex | slice, /) -> int: ... + def __setitem__(self, key: SupportsIndex | slice, value: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 583ac82750ac..b2263df1337d 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -15,6 +15,8 @@ _PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | _SrvnmeCbType: TypeAlias = Callable[[SSLSocket | SSLObject, str | None, SSLSocket], int | None] +socket_error = OSError + class _Cipher(TypedDict): aead: bool alg_bits: int @@ -96,14 +98,14 @@ else: _create_default_https_context: Callable[..., SSLContext] -def RAND_bytes(__n: int) -> bytes: ... +def RAND_bytes(n: int, /) -> bytes: ... if sys.version_info < (3, 12): - def RAND_pseudo_bytes(__n: int) -> tuple[bytes, bool]: ... + def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ... def RAND_status() -> bool: ... def RAND_egd(path: str) -> None: ... -def RAND_add(__string: str | ReadableBuffer, __entropy: float) -> None: ... +def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ... if sys.version_info < (3, 12): def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ... @@ -420,12 +422,12 @@ class SSLContext: def get_ca_certs(self, binary_form: bool = False) -> Any: ... def get_ciphers(self) -> list[_Cipher]: ... def set_default_verify_paths(self) -> None: ... - def set_ciphers(self, __cipherlist: str) -> None: ... + def set_ciphers(self, cipherlist: str, /) -> None: ... def set_alpn_protocols(self, alpn_protocols: Iterable[str]) -> None: ... def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ... def set_servername_callback(self, server_name_callback: _SrvnmeCbType | None) -> None: ... - def load_dh_params(self, __path: str) -> None: ... - def set_ecdh_curve(self, __name: str) -> None: ... + def load_dh_params(self, path: str, /) -> None: ... + def set_ecdh_curve(self, name: str, /) -> None: ... def wrap_socket( self, sock: socket.socket, @@ -479,8 +481,8 @@ class SSLObject: class MemoryBIO: pending: int eof: bool - def read(self, __size: int = -1) -> bytes: ... - def write(self, __b: ReadableBuffer) -> int: ... + def read(self, size: int = -1, /) -> bytes: ... + def write(self, b: ReadableBuffer, /) -> int: ... def write_eof(self) -> None: ... @final @@ -495,7 +497,7 @@ class SSLSession: def time(self) -> int: ... @property def timeout(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... class SSLErrorNumber(enum.IntEnum): SSL_ERROR_EOF: int diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index f3f013fc93e7..c5f5ed64b328 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -110,14 +110,14 @@ class NormalDist: if sys.version_info >= (3, 12): def correlation( - __x: Sequence[_Number], __y: Sequence[_Number], *, method: Literal["linear", "ranked"] = "linear" + x: Sequence[_Number], y: Sequence[_Number], /, *, method: Literal["linear", "ranked"] = "linear" ) -> float: ... elif sys.version_info >= (3, 10): - def correlation(__x: Sequence[_Number], __y: Sequence[_Number]) -> float: ... + def correlation(x: Sequence[_Number], y: Sequence[_Number], /) -> float: ... if sys.version_info >= (3, 10): - def covariance(__x: Sequence[_Number], __y: Sequence[_Number]) -> float: ... + def covariance(x: Sequence[_Number], y: Sequence[_Number], /) -> float: ... class LinearRegression(NamedTuple): slope: float @@ -125,8 +125,8 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 11): def linear_regression( - __regressor: Sequence[_Number], __dependent_variable: Sequence[_Number], *, proportional: bool = False + regressor: Sequence[_Number], dependent_variable: Sequence[_Number], /, *, proportional: bool = False ) -> LinearRegression: ... elif sys.version_info >= (3, 10): - def linear_regression(__regressor: Sequence[_Number], __dependent_variable: Sequence[_Number]) -> LinearRegression: ... + def linear_regression(regressor: Sequence[_Number], dependent_variable: Sequence[_Number], /) -> LinearRegression: ... diff --git a/mypy/typeshed/stdlib/string.pyi b/mypy/typeshed/stdlib/string.pyi index 8b60243f2333..35a76e9c8628 100644 --- a/mypy/typeshed/stdlib/string.pyi +++ b/mypy/typeshed/stdlib/string.pyi @@ -47,17 +47,17 @@ class Template(metaclass=_TemplateMetaclass): flags: ClassVar[RegexFlag] pattern: ClassVar[Pattern[str]] def __init__(self, template: str) -> None: ... - def substitute(self, __mapping: Mapping[str, object] = {}, **kwds: object) -> str: ... - def safe_substitute(self, __mapping: Mapping[str, object] = {}, **kwds: object) -> str: ... + def substitute(self, mapping: Mapping[str, object] = {}, /, **kwds: object) -> str: ... + def safe_substitute(self, mapping: Mapping[str, object] = {}, /, **kwds: object) -> str: ... if sys.version_info >= (3, 11): def get_identifiers(self) -> list[str]: ... def is_valid(self) -> bool: ... class Formatter: @overload - def format(self, __format_string: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... + def format(self, format_string: LiteralString, /, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... @overload - def format(self, __format_string: str, *args: Any, **kwargs: Any) -> str: ... + def format(self, format_string: str, /, *args: Any, **kwargs: Any) -> str: ... @overload def vformat( self, format_string: LiteralString, args: Sequence[LiteralString], kwargs: Mapping[LiteralString, LiteralString] diff --git a/mypy/typeshed/stdlib/struct.pyi b/mypy/typeshed/stdlib/struct.pyi index 4220cd825b76..e684632489ea 100644 --- a/mypy/typeshed/stdlib/struct.pyi +++ b/mypy/typeshed/stdlib/struct.pyi @@ -6,12 +6,12 @@ __all__ = ["calcsize", "pack", "pack_into", "unpack", "unpack_from", "iter_unpac class error(Exception): ... -def pack(__fmt: str | bytes, *v: Any) -> bytes: ... -def pack_into(__fmt: str | bytes, __buffer: WriteableBuffer, __offset: int, *v: Any) -> None: ... -def unpack(__format: str | bytes, __buffer: ReadableBuffer) -> tuple[Any, ...]: ... -def unpack_from(__format: str | bytes, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... -def iter_unpack(__format: str | bytes, __buffer: ReadableBuffer) -> Iterator[tuple[Any, ...]]: ... -def calcsize(__format: str | bytes) -> int: ... +def pack(fmt: str | bytes, /, *v: Any) -> bytes: ... +def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ... +def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... +def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... +def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... +def calcsize(format: str | bytes, /) -> int: ... class Struct: @property @@ -21,6 +21,6 @@ class Struct: def __init__(self, format: str | bytes) -> None: ... def pack(self, *v: Any) -> bytes: ... def pack_into(self, buffer: WriteableBuffer, offset: int, *v: Any) -> None: ... - def unpack(self, __buffer: ReadableBuffer) -> tuple[Any, ...]: ... + def unpack(self, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... def unpack_from(self, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... - def iter_unpack(self, __buffer: ReadableBuffer) -> Iterator[tuple[Any, ...]]: ... + def iter_unpack(self, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index bb1d244bdac9..353e20c4b2e1 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -17,9 +17,7 @@ _OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall # Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder` class _MetaPathFinder(Protocol): - def find_spec( - self, __fullname: str, __path: Sequence[str] | None, __target: ModuleType | None = ... - ) -> ModuleSpec | None: ... + def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ... # ----- sys variables ----- if sys.platform != "win32": @@ -245,19 +243,19 @@ class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel version_info: _version_info -def call_tracing(__func: Callable[..., _T], __args: Any) -> _T: ... +def call_tracing(func: Callable[..., _T], args: Any, /) -> _T: ... def _clear_type_cache() -> None: ... def _current_frames() -> dict[int, FrameType]: ... -def _getframe(__depth: int = 0) -> FrameType: ... +def _getframe(depth: int = 0, /) -> FrameType: ... def _debugmallocstats() -> None: ... -def __displayhook__(__object: object) -> None: ... -def __excepthook__(__exctype: type[BaseException], __value: BaseException, __traceback: TracebackType | None) -> None: ... +def __displayhook__(object: object, /) -> None: ... +def __excepthook__(exctype: type[BaseException], value: BaseException, traceback: TracebackType | None, /) -> None: ... def exc_info() -> OptExcInfo: ... if sys.version_info >= (3, 11): def exception() -> BaseException | None: ... -def exit(__status: _ExitCode = None) -> NoReturn: ... +def exit(status: _ExitCode = None, /) -> NoReturn: ... def getallocatedblocks() -> int: ... def getdefaultencoding() -> str: ... @@ -266,7 +264,7 @@ if sys.platform != "win32": def getfilesystemencoding() -> str: ... def getfilesystemencodeerrors() -> str: ... -def getrefcount(__object: Any) -> int: ... +def getrefcount(object: Any, /) -> int: ... def getrecursionlimit() -> int: ... def getsizeof(obj: object, default: int = ...) -> int: ... def getswitchinterval() -> float: ... @@ -302,22 +300,22 @@ if sys.platform == "win32": def getwindowsversion() -> _WinVersion: ... -def intern(__string: str) -> str: ... +def intern(string: str, /) -> str: ... def is_finalizing() -> bool: ... def breakpointhook(*args: Any, **kwargs: Any) -> Any: ... __breakpointhook__ = breakpointhook # Contains the original value of breakpointhook if sys.platform != "win32": - def setdlopenflags(__flags: int) -> None: ... + def setdlopenflags(flags: int, /) -> None: ... -def setrecursionlimit(__limit: int) -> None: ... -def setswitchinterval(__interval: float) -> None: ... +def setrecursionlimit(limit: int, /) -> None: ... +def setswitchinterval(interval: float, /) -> None: ... def gettotalrefcount() -> int: ... # Debug builds only if sys.version_info < (3, 9): def getcheckinterval() -> int: ... # deprecated - def setcheckinterval(__n: int) -> None: ... # deprecated + def setcheckinterval(n: int, /) -> None: ... # deprecated if sys.version_info < (3, 9): # An 11-tuple or None @@ -333,9 +331,9 @@ class UnraisableHookArgs(Protocol): unraisablehook: Callable[[UnraisableHookArgs], Any] -def __unraisablehook__(__unraisable: UnraisableHookArgs) -> Any: ... +def __unraisablehook__(unraisable: UnraisableHookArgs, /) -> Any: ... def addaudithook(hook: Callable[[str, tuple[Any, ...]], Any]) -> None: ... -def audit(__event: str, *args: Any) -> None: ... +def audit(event: str, /, *args: Any) -> None: ... _AsyncgenHook: TypeAlias = Callable[[AsyncGenerator[Any, Any]], None] | None @@ -366,9 +364,9 @@ if sys.version_info >= (3, 12): def is_stack_trampoline_active() -> bool: ... # It always exists, but raises on non-linux platforms: if sys.platform == "linux": - def activate_stack_trampoline(__backend: str) -> None: ... + def activate_stack_trampoline(backend: str, /) -> None: ... else: - def activate_stack_trampoline(__backend: str) -> NoReturn: ... + def activate_stack_trampoline(backend: str, /) -> NoReturn: ... from . import _monitoring diff --git a/mypy/typeshed/stdlib/sys/_monitoring.pyi b/mypy/typeshed/stdlib/sys/_monitoring.pyi index 40aeb9cb5bdb..0507eeedc26d 100644 --- a/mypy/typeshed/stdlib/sys/_monitoring.pyi +++ b/mypy/typeshed/stdlib/sys/_monitoring.pyi @@ -14,9 +14,9 @@ COVERAGE_ID: int PROFILER_ID: int OPTIMIZER_ID: int -def use_tool_id(__tool_id: int, __name: str) -> None: ... -def free_tool_id(__tool_id: int) -> None: ... -def get_tool(__tool_id: int) -> str | None: ... +def use_tool_id(tool_id: int, name: str, /) -> None: ... +def free_tool_id(tool_id: int, /) -> None: ... +def get_tool(tool_id: int, /) -> str | None: ... events: _events @@ -40,13 +40,13 @@ class _events: RERAISE: int STOP_ITERATION: int -def get_events(__tool_id: int) -> int: ... -def set_events(__tool_id: int, __event_set: int) -> None: ... -def get_local_events(__tool_id: int, __code: CodeType) -> int: ... -def set_local_events(__tool_id: int, __code: CodeType, __event_set: int) -> int: ... +def get_events(tool_id: int, /) -> int: ... +def set_events(tool_id: int, event_set: int, /) -> None: ... +def get_local_events(tool_id: int, code: CodeType, /) -> int: ... +def set_local_events(tool_id: int, code: CodeType, event_set: int, /) -> int: ... def restart_events() -> None: ... DISABLE: object MISSING: object -def register_callback(__tool_id: int, __event: int, __func: Callable[..., Any] | None) -> Callable[..., Any] | None: ... +def register_callback(tool_id: int, event: int, func: Callable[..., Any] | None, /) -> Callable[..., Any] | None: ... diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index 164334f60a6f..02876e0b7e85 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -35,11 +35,11 @@ if sys.platform != "win32": LOG_USER: Literal[8] LOG_UUCP: Literal[64] LOG_WARNING: Literal[4] - def LOG_MASK(__pri: int) -> int: ... - def LOG_UPTO(__pri: int) -> int: ... + def LOG_MASK(pri: int, /) -> int: ... + def LOG_UPTO(pri: int, /) -> int: ... def closelog() -> None: ... def openlog(ident: str = ..., logoption: int = ..., facility: int = ...) -> None: ... - def setlogmask(__maskpri: int) -> int: ... + def setlogmask(maskpri: int, /) -> int: ... @overload def syslog(priority: int, message: str) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 47c831190286..b6fe454eff78 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -43,10 +43,10 @@ _FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None] _TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction class _Fileobj(Protocol): - def read(self, __size: int) -> bytes: ... - def write(self, __b: bytes) -> object: ... + def read(self, size: int, /) -> bytes: ... + def write(self, b: bytes, /) -> object: ... def tell(self) -> int: ... - def seek(self, __pos: int) -> object: ... + def seek(self, pos: int, /) -> object: ... def close(self) -> object: ... # Optional fields: # name: str | bytes diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 2c4b548458ea..ce8f2f1f5929 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -364,14 +364,14 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): if sys.version_info >= (3, 11): # These three work only if the SpooledTemporaryFile is opened in binary mode, # because the underlying object in text mode does not have these methods. - def read1(self, __size: int = ...) -> AnyStr: ... + def read1(self, size: int = ..., /) -> AnyStr: ... def readinto(self, b: WriteableBuffer) -> int: ... def readinto1(self, b: WriteableBuffer) -> int: ... def detach(self) -> io.RawIOBase: ... - def read(self, __n: int = ...) -> AnyStr: ... - def readline(self, __limit: int | None = ...) -> AnyStr: ... # type: ignore[override] - def readlines(self, __hint: int = ...) -> list[AnyStr]: ... # type: ignore[override] + def read(self, n: int = ..., /) -> AnyStr: ... + def readline(self, limit: int | None = ..., /) -> AnyStr: ... # type: ignore[override] + def readlines(self, hint: int = ..., /) -> list[AnyStr]: ... # type: ignore[override] def seek(self, offset: int, whence: int = ...) -> int: ... def tell(self) -> int: ... def truncate(self, size: int | None = None) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index 776396cce407..a5378e40fdf2 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -254,14 +254,14 @@ if sys.platform != "win32": XCASE: int XTABS: int - def tcgetattr(__fd: FileDescriptorLike) -> _AttrReturn: ... - def tcsetattr(__fd: FileDescriptorLike, __when: int, __attributes: _Attr) -> None: ... - def tcsendbreak(__fd: FileDescriptorLike, __duration: int) -> None: ... - def tcdrain(__fd: FileDescriptorLike) -> None: ... - def tcflush(__fd: FileDescriptorLike, __queue: int) -> None: ... - def tcflow(__fd: FileDescriptorLike, __action: int) -> None: ... + def tcgetattr(fd: FileDescriptorLike, /) -> _AttrReturn: ... + def tcsetattr(fd: FileDescriptorLike, when: int, attributes: _Attr, /) -> None: ... + def tcsendbreak(fd: FileDescriptorLike, duration: int, /) -> None: ... + def tcdrain(fd: FileDescriptorLike, /) -> None: ... + def tcflush(fd: FileDescriptorLike, queue: int, /) -> None: ... + def tcflow(fd: FileDescriptorLike, action: int, /) -> None: ... if sys.version_info >= (3, 11): - def tcgetwinsize(__fd: FileDescriptorLike) -> tuple[int, int]: ... - def tcsetwinsize(__fd: FileDescriptorLike, __winsize: tuple[int, int]) -> None: ... + def tcgetwinsize(fd: FileDescriptorLike, /) -> tuple[int, int]: ... + def tcsetwinsize(fd: FileDescriptorLike, winsize: tuple[int, int], /) -> None: ... class error(Exception): ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 3f65eb2c8fe4..80bc56ef53f3 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -618,18 +618,18 @@ class Wm: @overload def wm_attributes(self) -> tuple[Any, ...]: ... @overload - def wm_attributes(self, __option: str): ... + def wm_attributes(self, option: str, /): ... @overload - def wm_attributes(self, __option: str, __value, *__other_option_value_pairs: Any) -> None: ... + def wm_attributes(self, option: str, value, /, *__other_option_value_pairs: Any) -> None: ... attributes = wm_attributes def wm_client(self, name: str | None = None) -> str: ... client = wm_client @overload def wm_colormapwindows(self) -> list[Misc]: ... @overload - def wm_colormapwindows(self, __wlist: list[Misc] | tuple[Misc, ...]) -> None: ... + def wm_colormapwindows(self, wlist: list[Misc] | tuple[Misc, ...], /) -> None: ... @overload - def wm_colormapwindows(self, __first_wlist_item: Misc, *other_wlist_items: Misc) -> None: ... + def wm_colormapwindows(self, first_wlist_item: Misc, /, *other_wlist_items: Misc) -> None: ... colormapwindows = wm_colormapwindows def wm_command(self, value: str | None = None) -> str: ... command = wm_command @@ -767,31 +767,31 @@ class Tk(Misc, Wm): # Tk has __getattr__ so that tk_instance.foo falls back to tk_instance.tk.foo # Please keep in sync with _tkinter.TkappType. # Some methods are intentionally missing because they are inherited from Misc instead. - def adderrorinfo(self, __msg): ... - def call(self, __command: Any, *args: Any) -> Any: ... - def createcommand(self, __name, __func): ... + def adderrorinfo(self, msg, /): ... + def call(self, command: Any, /, *args: Any) -> Any: ... + def createcommand(self, name, func, /): ... if sys.platform != "win32": - def createfilehandler(self, __file, __mask, __func): ... - def deletefilehandler(self, __file): ... - - def createtimerhandler(self, __milliseconds, __func): ... - def dooneevent(self, __flags: int = ...): ... - def eval(self, __script: str) -> str: ... - def evalfile(self, __fileName): ... - def exprboolean(self, __s): ... - def exprdouble(self, __s): ... - def exprlong(self, __s): ... - def exprstring(self, __s): ... + def createfilehandler(self, file, mask, func, /): ... + def deletefilehandler(self, file, /): ... + + def createtimerhandler(self, milliseconds, func, /): ... + def dooneevent(self, flags: int = ..., /): ... + def eval(self, script: str, /) -> str: ... + def evalfile(self, fileName, /): ... + def exprboolean(self, s, /): ... + def exprdouble(self, s, /): ... + def exprlong(self, s, /): ... + def exprstring(self, s, /): ... def globalgetvar(self, *args, **kwargs): ... def globalsetvar(self, *args, **kwargs): ... def globalunsetvar(self, *args, **kwargs): ... def interpaddr(self): ... def loadtk(self) -> None: ... - def record(self, __script): ... + def record(self, script, /): ... if sys.version_info < (3, 11): - def split(self, __arg): ... + def split(self, arg, /): ... - def splitlist(self, __arg): ... + def splitlist(self, arg, /): ... def unsetvar(self, *args, **kwargs): ... def wantobjects(self, *args, **kwargs): ... def willdispatch(self): ... @@ -1214,11 +1214,11 @@ class Canvas(Widget, XView, YView): def canvasx(self, screenx, gridspacing: Incomplete | None = None): ... def canvasy(self, screeny, gridspacing: Incomplete | None = None): ... @overload - def coords(self, __tagOrId: str | int) -> list[float]: ... + def coords(self, tagOrId: str | int, /) -> list[float]: ... @overload - def coords(self, __tagOrId: str | int, __args: list[int] | list[float] | tuple[float, ...]) -> None: ... + def coords(self, tagOrId: str | int, args: list[int] | list[float] | tuple[float, ...], /) -> None: ... @overload - def coords(self, __tagOrId: str | int, __x1: float, __y1: float, *args: float) -> None: ... + def coords(self, tagOrId: str | int, x1: float, y1: float, /, *args: float) -> None: ... # create_foo() methods accept coords as a list or tuple, or as separate arguments. # Lists and tuples can be flat as in [1, 2, 3, 4], or nested as in [(1, 2), (3, 4)]. # Keyword arguments should be the same in all overloads of each method. @@ -1228,10 +1228,11 @@ class Canvas(Widget, XView, YView): @overload def create_line( self, - __x0: float, - __y0: float, - __x1: float, - __y1: float, + x0: float, + y0: float, + x1: float, + y1: float, + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1259,8 +1260,9 @@ class Canvas(Widget, XView, YView): @overload def create_line( self, - __xy_pair_0: tuple[float, float], - __xy_pair_1: tuple[float, float], + xy_pair_0: tuple[float, float], + xy_pair_1: tuple[float, float], + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1288,7 +1290,7 @@ class Canvas(Widget, XView, YView): @overload def create_line( self, - __coords: ( + coords: ( tuple[float, float, float, float] | tuple[tuple[float, float], tuple[float, float]] | list[int] @@ -1296,6 +1298,7 @@ class Canvas(Widget, XView, YView): | list[tuple[int, int]] | list[tuple[float, float]] ), + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1323,10 +1326,11 @@ class Canvas(Widget, XView, YView): @overload def create_oval( self, - __x0: float, - __y0: float, - __x1: float, - __y1: float, + x0: float, + y0: float, + x1: float, + y1: float, + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1355,8 +1359,9 @@ class Canvas(Widget, XView, YView): @overload def create_oval( self, - __xy_pair_0: tuple[float, float], - __xy_pair_1: tuple[float, float], + xy_pair_0: tuple[float, float], + xy_pair_1: tuple[float, float], + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1385,7 +1390,7 @@ class Canvas(Widget, XView, YView): @overload def create_oval( self, - __coords: ( + coords: ( tuple[float, float, float, float] | tuple[tuple[float, float], tuple[float, float]] | list[int] @@ -1393,6 +1398,7 @@ class Canvas(Widget, XView, YView): | list[tuple[int, int]] | list[tuple[float, float]] ), + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1421,10 +1427,11 @@ class Canvas(Widget, XView, YView): @overload def create_polygon( self, - __x0: float, - __y0: float, - __x1: float, - __y1: float, + x0: float, + y0: float, + x1: float, + y1: float, + /, *xy_pairs: float, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1456,8 +1463,9 @@ class Canvas(Widget, XView, YView): @overload def create_polygon( self, - __xy_pair_0: tuple[float, float], - __xy_pair_1: tuple[float, float], + xy_pair_0: tuple[float, float], + xy_pair_1: tuple[float, float], + /, *xy_pairs: tuple[float, float], activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1489,7 +1497,7 @@ class Canvas(Widget, XView, YView): @overload def create_polygon( self, - __coords: ( + coords: ( tuple[float, ...] | tuple[tuple[float, float], ...] | list[int] @@ -1497,6 +1505,7 @@ class Canvas(Widget, XView, YView): | list[tuple[int, int]] | list[tuple[float, float]] ), + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1528,10 +1537,11 @@ class Canvas(Widget, XView, YView): @overload def create_rectangle( self, - __x0: float, - __y0: float, - __x1: float, - __y1: float, + x0: float, + y0: float, + x1: float, + y1: float, + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1560,8 +1570,9 @@ class Canvas(Widget, XView, YView): @overload def create_rectangle( self, - __xy_pair_0: tuple[float, float], - __xy_pair_1: tuple[float, float], + xy_pair_0: tuple[float, float], + xy_pair_1: tuple[float, float], + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1590,7 +1601,7 @@ class Canvas(Widget, XView, YView): @overload def create_rectangle( self, - __coords: ( + coords: ( tuple[float, float, float, float] | tuple[tuple[float, float], tuple[float, float]] | list[int] @@ -1598,6 +1609,7 @@ class Canvas(Widget, XView, YView): | list[tuple[int, int]] | list[tuple[float, float]] ), + /, *, activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., @@ -1626,8 +1638,9 @@ class Canvas(Widget, XView, YView): @overload def create_text( self, - __x: float, - __y: float, + x: float, + y: float, + /, *, activefill: str = ..., activestipple: str = ..., @@ -1648,7 +1661,8 @@ class Canvas(Widget, XView, YView): @overload def create_text( self, - __coords: tuple[float, float] | list[int] | list[float], + coords: tuple[float, float] | list[int] | list[float], + /, *, activefill: str = ..., activestipple: str = ..., @@ -1669,8 +1683,9 @@ class Canvas(Widget, XView, YView): @overload def create_window( self, - __x: float, - __y: float, + x: float, + y: float, + /, *, anchor: _Anchor = ..., height: _ScreenUnits = ..., @@ -1682,7 +1697,8 @@ class Canvas(Widget, XView, YView): @overload def create_window( self, - __coords: tuple[float, float] | list[int] | list[float], + coords: tuple[float, float] | list[int] | list[float], + /, *, anchor: _Anchor = ..., height: _ScreenUnits = ..., @@ -1694,11 +1710,11 @@ class Canvas(Widget, XView, YView): def dchars(self, *args) -> None: ... def delete(self, *tagsOrCanvasIds: str | int) -> None: ... @overload - def dtag(self, __tag: str, __tag_to_delete: str | None = ...) -> None: ... + def dtag(self, tag: str, tag_to_delete: str | None = ..., /) -> None: ... @overload - def dtag(self, __id: int, __tag_to_delete: str) -> None: ... + def dtag(self, id: int, tag_to_delete: str, /) -> None: ... def focus(self, *args): ... - def gettags(self, __tagOrId: str | int) -> tuple[str, ...]: ... + def gettags(self, tagOrId: str | int, /) -> tuple[str, ...]: ... def icursor(self, *args) -> None: ... def index(self, *args): ... def insert(self, *args) -> None: ... @@ -1716,13 +1732,13 @@ class Canvas(Widget, XView, YView): # lift = tkraise = tag_raise # # But mypy doesn't like aliasing here (maybe because Misc defines the same names) - def tag_lower(self, __first: str | int, __second: str | int | None = ...) -> None: ... - def lower(self, __first: str | int, __second: str | int | None = ...) -> None: ... # type: ignore[override] - def tag_raise(self, __first: str | int, __second: str | int | None = ...) -> None: ... - def tkraise(self, __first: str | int, __second: str | int | None = ...) -> None: ... # type: ignore[override] - def lift(self, __first: str | int, __second: str | int | None = ...) -> None: ... # type: ignore[override] + def tag_lower(self, first: str | int, second: str | int | None = ..., /) -> None: ... + def lower(self, first: str | int, second: str | int | None = ..., /) -> None: ... # type: ignore[override] + def tag_raise(self, first: str | int, second: str | int | None = ..., /) -> None: ... + def tkraise(self, first: str | int, second: str | int | None = ..., /) -> None: ... # type: ignore[override] + def lift(self, first: str | int, second: str | int | None = ..., /) -> None: ... # type: ignore[override] def scale( - self, __tagOrId: str | int, __xOrigin: _ScreenUnits, __yOrigin: _ScreenUnits, __xScale: float, __yScale: float + self, tagOrId: str | int, xOrigin: _ScreenUnits, yOrigin: _ScreenUnits, xScale: float, yScale: float, / ) -> None: ... def scan_mark(self, x, y) -> None: ... def scan_dragto(self, x, y, gain: int = 10) -> None: ... @@ -3182,7 +3198,7 @@ class Text(Widget, XView, YView): @overload def tag_configure(self, tagName: str, cnf: str) -> tuple[str, str, str, Any, Any]: ... tag_config = tag_configure - def tag_delete(self, __first_tag_name: str, *tagNames: str) -> None: ... # error if no tag names given + def tag_delete(self, first_tag_name: str, /, *tagNames: str) -> None: ... # error if no tag names given def tag_lower(self, tagName: str, belowThis: str | None = None) -> None: ... def tag_names(self, index: _TextIndex | None = None) -> tuple[str, ...]: ... def tag_nextrange( diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi index 5a83bb56679f..d806be74068e 100644 --- a/mypy/typeshed/stdlib/tkinter/dnd.pyi +++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi @@ -6,7 +6,7 @@ if sys.version_info >= (3, 9): __all__ = ["dnd_start", "DndHandler"] class _DndSource(Protocol): - def dnd_end(self, __target: Widget | None, __event: Event[Misc] | None) -> None: ... + def dnd_end(self, target: Widget | None, event: Event[Misc] | None, /) -> None: ... class DndHandler: root: ClassVar[Tk | None] diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 448e2b0054a5..46625014d4ac 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -97,9 +97,9 @@ class Font: configure = config def copy(self) -> Font: ... @overload - def metrics(self, __option: Literal["ascent", "descent", "linespace"], *, displayof: tkinter.Misc | None = ...) -> int: ... + def metrics(self, option: Literal["ascent", "descent", "linespace"], /, *, displayof: tkinter.Misc | None = ...) -> int: ... @overload - def metrics(self, __option: Literal["fixed"], *, displayof: tkinter.Misc | None = ...) -> bool: ... + def metrics(self, option: Literal["fixed"], /, *, displayof: tkinter.Misc | None = ...) -> bool: ... @overload def metrics(self, *, displayof: tkinter.Misc | None = ...) -> _MetricsDict: ... def measure(self, text: str, displayof: tkinter.Misc | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index f1b132b33657..86a23ce82211 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1105,19 +1105,19 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): def see(self, item: str | int) -> None: ... def selection(self) -> tuple[str, ...]: ... @overload - def selection_set(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + def selection_set(self, items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...], /) -> None: ... @overload def selection_set(self, *items: str | int) -> None: ... @overload - def selection_add(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + def selection_add(self, items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...], /) -> None: ... @overload def selection_add(self, *items: str | int) -> None: ... @overload - def selection_remove(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + def selection_remove(self, items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...], /) -> None: ... @overload def selection_remove(self, *items: str | int) -> None: ... @overload - def selection_toggle(self, __items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...]) -> None: ... + def selection_toggle(self, items: list[str] | tuple[str, ...] | list[int] | tuple[int, ...], /) -> None: ... @overload def selection_toggle(self, *items: str | int) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/tomllib.pyi b/mypy/typeshed/stdlib/tomllib.pyi index 3a6ce93f87e1..d559568b912b 100644 --- a/mypy/typeshed/stdlib/tomllib.pyi +++ b/mypy/typeshed/stdlib/tomllib.pyi @@ -6,5 +6,5 @@ __all__ = ("loads", "load", "TOMLDecodeError") class TOMLDecodeError(ValueError): ... -def load(__fp: SupportsRead[bytes], *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... -def loads(__s: str, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... +def load(fp: SupportsRead[bytes], /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... +def loads(s: str, /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index 14a921c5e6cc..d32647a55cb5 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -65,7 +65,7 @@ class Trace: self, cmd: str | types.CodeType, globals: Mapping[str, Any] | None = None, locals: Mapping[str, Any] | None = None ) -> None: ... if sys.version_info >= (3, 9): - def runfunc(self, __func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... + def runfunc(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... else: def runfunc(self, func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index f6720155936f..928858f81d1c 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -34,7 +34,8 @@ def print_tb(tb: TracebackType | None, limit: int | None = None, file: SupportsW if sys.version_info >= (3, 10): @overload def print_exception( - __exc: type[BaseException] | None, + exc: type[BaseException] | None, + /, value: BaseException | None = ..., tb: TracebackType | None = ..., limit: int | None = None, @@ -43,18 +44,19 @@ if sys.version_info >= (3, 10): ) -> None: ... @overload def print_exception( - __exc: BaseException, *, limit: int | None = None, file: SupportsWrite[str] | None = None, chain: bool = True + exc: BaseException, /, *, limit: int | None = None, file: SupportsWrite[str] | None = None, chain: bool = True ) -> None: ... @overload def format_exception( - __exc: type[BaseException] | None, + exc: type[BaseException] | None, + /, value: BaseException | None = ..., tb: TracebackType | None = ..., limit: int | None = None, chain: bool = True, ) -> list[str]: ... @overload - def format_exception(__exc: BaseException, *, limit: int | None = None, chain: bool = True) -> list[str]: ... + def format_exception(exc: BaseException, /, *, limit: int | None = None, chain: bool = True) -> list[str]: ... else: def print_exception( @@ -85,9 +87,9 @@ def print_list(extracted_list: list[FrameSummary], file: SupportsWrite[str] | No if sys.version_info >= (3, 10): @overload - def format_exception_only(__exc: BaseException | None) -> list[str]: ... + def format_exception_only(exc: BaseException | None, /) -> list[str]: ... @overload - def format_exception_only(__exc: Unused, value: BaseException | None) -> list[str]: ... + def format_exception_only(exc: Unused, /, value: BaseException | None) -> list[str]: ... else: def format_exception_only(etype: type[BaseException] | None, value: BaseException | None) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 05c5e85e4a9e..f2d79b7f3ade 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -66,8 +66,8 @@ _VT_co = TypeVar("_VT_co", covariant=True) @final class _Cell: - def __new__(cls, __contents: object = ...) -> Self: ... - def __eq__(self, __value: object) -> bool: ... + def __new__(cls, contents: object = ..., /) -> Self: ... + def __eq__(self, value: object, /) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] cell_contents: Any @@ -102,15 +102,15 @@ class FunctionType: ) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @overload - def __get__(self, __instance: None, __owner: type) -> FunctionType: ... + def __get__(self, instance: None, owner: type, /) -> FunctionType: ... @overload - def __get__(self, __instance: object, __owner: type | None = None) -> MethodType: ... + def __get__(self, instance: object, owner: type | None = None, /) -> MethodType: ... LambdaType = FunctionType @final class CodeType: - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @property def co_argcount(self) -> int: ... @@ -164,64 +164,67 @@ class CodeType: if sys.version_info >= (3, 11): def __new__( cls, - __argcount: int, - __posonlyargcount: int, - __kwonlyargcount: int, - __nlocals: int, - __stacksize: int, - __flags: int, - __codestring: bytes, - __constants: tuple[object, ...], - __names: tuple[str, ...], - __varnames: tuple[str, ...], - __filename: str, - __name: str, - __qualname: str, - __firstlineno: int, - __linetable: bytes, - __exceptiontable: bytes, - __freevars: tuple[str, ...] = ..., - __cellvars: tuple[str, ...] = ..., + argcount: int, + posonlyargcount: int, + kwonlyargcount: int, + nlocals: int, + stacksize: int, + flags: int, + codestring: bytes, + constants: tuple[object, ...], + names: tuple[str, ...], + varnames: tuple[str, ...], + filename: str, + name: str, + qualname: str, + firstlineno: int, + linetable: bytes, + exceptiontable: bytes, + freevars: tuple[str, ...] = ..., + cellvars: tuple[str, ...] = ..., + /, ) -> Self: ... elif sys.version_info >= (3, 10): def __new__( cls, - __argcount: int, - __posonlyargcount: int, - __kwonlyargcount: int, - __nlocals: int, - __stacksize: int, - __flags: int, - __codestring: bytes, - __constants: tuple[object, ...], - __names: tuple[str, ...], - __varnames: tuple[str, ...], - __filename: str, - __name: str, - __firstlineno: int, - __linetable: bytes, - __freevars: tuple[str, ...] = ..., - __cellvars: tuple[str, ...] = ..., + argcount: int, + posonlyargcount: int, + kwonlyargcount: int, + nlocals: int, + stacksize: int, + flags: int, + codestring: bytes, + constants: tuple[object, ...], + names: tuple[str, ...], + varnames: tuple[str, ...], + filename: str, + name: str, + firstlineno: int, + linetable: bytes, + freevars: tuple[str, ...] = ..., + cellvars: tuple[str, ...] = ..., + /, ) -> Self: ... else: def __new__( cls, - __argcount: int, - __posonlyargcount: int, - __kwonlyargcount: int, - __nlocals: int, - __stacksize: int, - __flags: int, - __codestring: bytes, - __constants: tuple[object, ...], - __names: tuple[str, ...], - __varnames: tuple[str, ...], - __filename: str, - __name: str, - __firstlineno: int, - __lnotab: bytes, - __freevars: tuple[str, ...] = ..., - __cellvars: tuple[str, ...] = ..., + argcount: int, + posonlyargcount: int, + kwonlyargcount: int, + nlocals: int, + stacksize: int, + flags: int, + codestring: bytes, + constants: tuple[object, ...], + names: tuple[str, ...], + varnames: tuple[str, ...], + filename: str, + name: str, + firstlineno: int, + lnotab: bytes, + freevars: tuple[str, ...] = ..., + cellvars: tuple[str, ...] = ..., + /, ) -> Self: ... if sys.version_info >= (3, 11): def replace( @@ -293,10 +296,10 @@ class CodeType: class MappingProxyType(Mapping[_KT, _VT_co]): __hash__: ClassVar[None] # type: ignore[assignment] def __new__(cls, mapping: SupportsKeysAndGetItem[_KT, _VT_co]) -> Self: ... - def __getitem__(self, __key: _KT) -> _VT_co: ... + def __getitem__(self, key: _KT, /) -> _VT_co: ... def __iter__(self) -> Iterator[_KT]: ... def __len__(self) -> int: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def copy(self) -> dict[_KT, _VT_co]: ... def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... @@ -304,19 +307,19 @@ class MappingProxyType(Mapping[_KT, _VT_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT]: ... - def __or__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT_co | _T2]: ... - def __ror__(self, __value: Mapping[_T1, _T2]) -> dict[_KT | _T1, _VT_co | _T2]: ... + def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... + def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... class SimpleNamespace: __hash__: ClassVar[None] # type: ignore[assignment] def __init__(self, **kwargs: Any) -> None: ... - def __eq__(self, __value: object) -> bool: ... - def __getattribute__(self, __name: str) -> Any: ... - def __setattr__(self, __name: str, __value: Any) -> None: ... - def __delattr__(self, __name: str) -> None: ... + def __eq__(self, value: object, /) -> bool: ... + def __getattribute__(self, name: str, /) -> Any: ... + def __setattr__(self, name: str, value: Any, /) -> None: ... + def __delattr__(self, name: str, /) -> None: ... class _LoaderProtocol(Protocol): - def load_module(self, __fullname: str) -> ModuleType: ... + def load_module(self, fullname: str, /) -> ModuleType: ... class ModuleType: __name__: str @@ -348,13 +351,13 @@ class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): __qualname__: str def __iter__(self) -> Self: ... def __next__(self) -> _YieldT_co: ... - def send(self, __arg: _SendT_contra) -> _YieldT_co: ... + def send(self, arg: _SendT_contra, /) -> _YieldT_co: ... @overload def throw( - self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... + self, typ: type[BaseException], val: BaseException | object = ..., tb: TracebackType | None = ..., / ) -> _YieldT_co: ... @overload - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... + def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... @final class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @@ -368,16 +371,16 @@ class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): def __aiter__(self) -> Self: ... def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... - def asend(self, __val: _SendT_contra) -> Coroutine[Any, Any, _YieldT_co]: ... + def asend(self, val: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @overload async def athrow( - self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... + self, typ: type[BaseException], val: BaseException | object = ..., tb: TracebackType | None = ..., / ) -> _YieldT_co: ... @overload - async def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... + async def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... def aclose(self) -> Coroutine[Any, Any, None]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, __item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): @@ -391,13 +394,13 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): def close(self) -> None: ... def __await__(self) -> Generator[Any, None, _ReturnT_co]: ... - def send(self, __arg: _SendT_contra) -> _YieldT_co: ... + def send(self, arg: _SendT_contra, /) -> _YieldT_co: ... @overload def throw( - self, __typ: type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ... + self, typ: type[BaseException], val: BaseException | object = ..., tb: TracebackType | None = ..., / ) -> _YieldT_co: ... @overload - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = ...) -> _YieldT_co: ... + def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... @final class MethodType: @@ -413,9 +416,9 @@ class MethodType: def __name__(self) -> str: ... # inherited from the added function @property def __qualname__(self) -> str: ... # inherited from the added function - def __new__(cls, __func: Callable[..., Any], __obj: object) -> Self: ... + def __new__(cls, func: Callable[..., Any], obj: object, /) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @final @@ -427,7 +430,7 @@ class BuiltinFunctionType: @property def __qualname__(self) -> str: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __eq__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... BuiltinMethodType = BuiltinFunctionType @@ -441,7 +444,7 @@ class WrapperDescriptorType: @property def __objclass__(self) -> type: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... @final class MethodWrapperType: @@ -454,8 +457,8 @@ class MethodWrapperType: @property def __objclass__(self) -> type: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __eq__(self, __value: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @final @@ -467,7 +470,7 @@ class MethodDescriptorType: @property def __objclass__(self) -> type: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... @final class ClassMethodDescriptorType: @@ -478,7 +481,7 @@ class ClassMethodDescriptorType: @property def __objclass__(self) -> type: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... @final class TracebackType: @@ -524,9 +527,9 @@ class GetSetDescriptorType: def __qualname__(self) -> str: ... @property def __objclass__(self) -> type: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... - def __set__(self, __instance: Any, __value: Any) -> None: ... - def __delete__(self, __instance: Any) -> None: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... + def __set__(self, instance: Any, value: Any, /) -> None: ... + def __delete__(self, instance: Any, /) -> None: ... @final class MemberDescriptorType: @@ -536,9 +539,9 @@ class MemberDescriptorType: def __qualname__(self) -> str: ... @property def __objclass__(self) -> type: ... - def __get__(self, __instance: Any, __owner: type | None = None) -> Any: ... - def __set__(self, __instance: Any, __value: Any) -> None: ... - def __delete__(self, __instance: Any) -> None: ... + def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... + def __set__(self, instance: Any, value: Any, /) -> None: ... + def __delete__(self, instance: Any, /) -> None: ... def new_class( name: str, @@ -552,7 +555,7 @@ def prepare_class( ) -> tuple[type, dict[str, Any], dict[str, Any]]: ... if sys.version_info >= (3, 12): - def get_original_bases(__cls: type) -> tuple[Any, ...]: ... + def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... # Actually a different type, but `property` is special and we want that too. DynamicClassAttribute = property @@ -578,8 +581,8 @@ if sys.version_info >= (3, 9): @property def __parameters__(self) -> tuple[Any, ...]: ... def __new__(cls, origin: type, args: Any) -> Self: ... - def __getitem__(self, __typeargs: Any) -> GenericAlias: ... - def __eq__(self, __value: object) -> bool: ... + def __getitem__(self, typeargs: Any, /) -> GenericAlias: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 11): @property @@ -605,7 +608,7 @@ if sys.version_info >= (3, 10): class UnionType: @property def __args__(self) -> tuple[Any, ...]: ... - def __or__(self, __value: Any) -> UnionType: ... - def __ror__(self, __value: Any) -> UnionType: ... - def __eq__(self, __value: object) -> bool: ... + def __or__(self, value: Any, /) -> UnionType: ... + def __ror__(self, value: Any, /) -> UnionType: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 5d01be539016..be0c29c89f8d 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -1,3 +1,5 @@ +# Since this module defines "overload" it is not recognized by Ruff as typing.overload +# ruff: noqa: F811 # TODO: The collections import is required, otherwise mypy crashes. # https://github.com/python/mypy/issues/16744 import collections # noqa: F401 # pyright: ignore @@ -282,7 +284,7 @@ if sys.version_info >= (3, 10): def __init__(self, name: str, tp: Any) -> None: ... if sys.version_info >= (3, 11): @staticmethod - def __call__(__x: _T) -> _T: ... + def __call__(x: _T, /) -> _T: ... else: def __call__(self, x: _T) -> _T: ... @@ -372,7 +374,7 @@ class SupportsRound(Protocol[_T_co]): def __round__(self) -> int: ... @overload @abstractmethod - def __round__(self, __ndigits: int) -> _T_co: ... + def __round__(self, ndigits: int, /) -> _T_co: ... @runtime_checkable class Sized(Protocol, metaclass=ABCMeta): @@ -410,15 +412,15 @@ _ReturnT_co = TypeVar("_ReturnT_co", covariant=True) class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): def __next__(self) -> _YieldT_co: ... @abstractmethod - def send(self, __value: _SendT_contra) -> _YieldT_co: ... + def send(self, value: _SendT_contra, /) -> _YieldT_co: ... @overload @abstractmethod def throw( - self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, typ: type[BaseException], val: BaseException | object = None, tb: TracebackType | None = None, / ) -> _YieldT_co: ... @overload @abstractmethod - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _YieldT_co: ... + def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> _YieldT_co: ... def close(self) -> None: ... def __iter__(self) -> Generator[_YieldT_co, _SendT_contra, _ReturnT_co]: ... @property @@ -447,15 +449,15 @@ class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _Retu @property def cr_running(self) -> bool: ... @abstractmethod - def send(self, __value: _SendT_contra) -> _YieldT_co: ... + def send(self, value: _SendT_contra, /) -> _YieldT_co: ... @overload @abstractmethod def throw( - self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, typ: type[BaseException], val: BaseException | object = None, tb: TracebackType | None = None, / ) -> _YieldT_co: ... @overload @abstractmethod - def throw(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> _YieldT_co: ... + def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> _YieldT_co: ... @abstractmethod def close(self) -> None: ... @@ -483,15 +485,15 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): def __anext__(self) -> Awaitable[_YieldT_co]: ... @abstractmethod - def asend(self, __value: _SendT_contra) -> Awaitable[_YieldT_co]: ... + def asend(self, value: _SendT_contra, /) -> Awaitable[_YieldT_co]: ... @overload @abstractmethod def athrow( - self, __typ: type[BaseException], __val: BaseException | object = None, __tb: TracebackType | None = None + self, typ: type[BaseException], val: BaseException | object = None, tb: TracebackType | None = None, / ) -> Awaitable[_YieldT_co]: ... @overload @abstractmethod - def athrow(self, __typ: BaseException, __val: None = None, __tb: TracebackType | None = None) -> Awaitable[_YieldT_co]: ... + def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> Awaitable[_YieldT_co]: ... def aclose(self) -> Awaitable[None]: ... @property def ag_await(self) -> Any: ... @@ -506,7 +508,7 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contr class Container(Protocol[_T_co]): # This is generic more on vibes than anything else @abstractmethod - def __contains__(self, __x: object) -> bool: ... + def __contains__(self, x: object, /) -> bool: ... @runtime_checkable class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): @@ -630,30 +632,30 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]): # TODO: We wish the key type could also be covariant, but that doesn't work, # see discussion in https://github.com/python/typing/pull/273. @abstractmethod - def __getitem__(self, __key: _KT) -> _VT_co: ... + def __getitem__(self, key: _KT, /) -> _VT_co: ... # Mixin methods @overload - def get(self, __key: _KT) -> _VT_co | None: ... + def get(self, key: _KT, /) -> _VT_co | None: ... @overload - def get(self, __key: _KT, default: _VT_co | _T) -> _VT_co | _T: ... + def get(self, key: _KT, /, default: _VT_co | _T) -> _VT_co | _T: ... def items(self) -> ItemsView[_KT, _VT_co]: ... def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... - def __contains__(self, __key: object) -> bool: ... - def __eq__(self, __other: object) -> bool: ... + def __contains__(self, key: object, /) -> bool: ... + def __eq__(self, other: object, /) -> bool: ... class MutableMapping(Mapping[_KT, _VT]): @abstractmethod - def __setitem__(self, __key: _KT, __value: _VT) -> None: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... @abstractmethod - def __delitem__(self, __key: _KT) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... def clear(self) -> None: ... @overload - def pop(self, __key: _KT) -> _VT: ... + def pop(self, key: _KT, /) -> _VT: ... @overload - def pop(self, __key: _KT, default: _VT) -> _VT: ... + def pop(self, key: _KT, /, default: _VT) -> _VT: ... @overload - def pop(self, __key: _KT, default: _T) -> _VT | _T: ... + def pop(self, key: _KT, /, default: _T) -> _VT | _T: ... def popitem(self) -> tuple[_KT, _VT]: ... # This overload should be allowed only if the value type is compatible with None. # @@ -662,9 +664,9 @@ class MutableMapping(Mapping[_KT, _VT]): # -- collections.ChainMap.setdefault # -- weakref.WeakKeyDictionary.setdefault @overload - def setdefault(self: MutableMapping[_KT, _T | None], __key: _KT, __default: None = None) -> _T | None: ... + def setdefault(self: MutableMapping[_KT, _T | None], key: _KT, default: None = None, /) -> _T | None: ... @overload - def setdefault(self, __key: _KT, __default: _VT) -> _VT: ... + def setdefault(self, key: _KT, default: _VT, /) -> _VT: ... # 'update' used to take a Union, but using overloading is better. # The second overloaded type here is a bit too general, because # Mapping[tuple[_KT, _VT], W] is a subclass of Iterable[tuple[_KT, _VT]], @@ -686,9 +688,9 @@ class MutableMapping(Mapping[_KT, _VT]): # -- weakref.WeakValueDictionary.__ior__ # -- weakref.WeakKeyDictionary.__ior__ @overload - def update(self, __m: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ... + def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... @overload - def update(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ... + def update(self, m: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... @overload def update(self, **kwargs: _VT) -> None: ... @@ -720,41 +722,41 @@ class IO(Iterator[AnyStr]): @abstractmethod def isatty(self) -> bool: ... @abstractmethod - def read(self, __n: int = -1) -> AnyStr: ... + def read(self, n: int = -1, /) -> AnyStr: ... @abstractmethod def readable(self) -> bool: ... @abstractmethod - def readline(self, __limit: int = -1) -> AnyStr: ... + def readline(self, limit: int = -1, /) -> AnyStr: ... @abstractmethod - def readlines(self, __hint: int = -1) -> list[AnyStr]: ... + def readlines(self, hint: int = -1, /) -> list[AnyStr]: ... @abstractmethod - def seek(self, __offset: int, __whence: int = 0) -> int: ... + def seek(self, offset: int, whence: int = 0, /) -> int: ... @abstractmethod def seekable(self) -> bool: ... @abstractmethod def tell(self) -> int: ... @abstractmethod - def truncate(self, __size: int | None = None) -> int: ... + def truncate(self, size: int | None = None, /) -> int: ... @abstractmethod def writable(self) -> bool: ... @abstractmethod @overload - def write(self: IO[str], __s: str) -> int: ... + def write(self: IO[str], s: str, /) -> int: ... @abstractmethod @overload - def write(self: IO[bytes], __s: ReadableBuffer) -> int: ... + def write(self: IO[bytes], s: ReadableBuffer, /) -> int: ... @abstractmethod @overload - def write(self, __s: AnyStr) -> int: ... + def write(self, s: AnyStr, /) -> int: ... @abstractmethod @overload - def writelines(self: IO[str], __lines: Iterable[str]) -> None: ... + def writelines(self: IO[str], lines: Iterable[str], /) -> None: ... @abstractmethod @overload - def writelines(self: IO[bytes], __lines: Iterable[ReadableBuffer]) -> None: ... + def writelines(self: IO[bytes], lines: Iterable[ReadableBuffer], /) -> None: ... @abstractmethod @overload - def writelines(self, __lines: Iterable[AnyStr]) -> None: ... + def writelines(self, lines: Iterable[AnyStr], /) -> None: ... @abstractmethod def __next__(self) -> AnyStr: ... @abstractmethod @@ -763,7 +765,7 @@ class IO(Iterator[AnyStr]): def __enter__(self) -> IO[AnyStr]: ... @abstractmethod def __exit__( - self, __type: type[BaseException] | None, __value: BaseException | None, __traceback: TracebackType | None + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / ) -> None: ... class BinaryIO(IO[bytes]): @@ -839,9 +841,9 @@ def cast(typ: str, val: Any) -> Any: ... def cast(typ: object, val: Any) -> Any: ... if sys.version_info >= (3, 11): - def reveal_type(__obj: _T) -> _T: ... - def assert_never(__arg: Never) -> Never: ... - def assert_type(__val: _T, __typ: Any) -> _T: ... + def reveal_type(obj: _T, /) -> _T: ... + def assert_never(arg: Never, /) -> Never: ... + def assert_type(val: _T, typ: Any, /) -> _T: ... def clear_overloads() -> None: ... def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... def dataclass_transform( @@ -867,9 +869,12 @@ class NamedTuple(tuple[Any, ...]): __orig_bases__: ClassVar[tuple[Any, ...]] @overload - def __init__(self, __typename: str, __fields: Iterable[tuple[str, Any]]) -> None: ... + def __init__(self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ... @overload - def __init__(self, __typename: str, __fields: None = None, **kwargs: Any) -> None: ... + @typing_extensions.deprecated( + "Creating a typing.NamedTuple using keyword arguments is deprecated and support will be removed in Python 3.15" + ) + def __init__(self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ... @classmethod def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ... def _asdict(self) -> dict[str, Any]: ... @@ -894,22 +899,22 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): def setdefault(self, k: _Never, default: object) -> object: ... # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. def pop(self, k: _Never, default: _T = ...) -> object: ... # pyright: ignore[reportInvalidTypeVarUse] - def update(self: _T, __m: _T) -> None: ... + def update(self: _T, m: _T, /) -> None: ... def __delitem__(self, k: _Never) -> None: ... def items(self) -> dict_items[str, object]: ... def keys(self) -> dict_keys[str, object]: ... def values(self) -> dict_values[str, object]: ... if sys.version_info >= (3, 9): @overload - def __or__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... + def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... @overload - def __or__(self, __value: dict[str, Any]) -> dict[str, object]: ... + def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... @overload - def __ror__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... + def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... @overload - def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... + def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... # supposedly incompatible definitions of __or__ and __ior__ - def __ior__(self, __value: typing_extensions.Self) -> typing_extensions.Self: ... # type: ignore[misc] + def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] @final class ForwardRef: @@ -945,7 +950,7 @@ if sys.version_info >= (3, 10): def _type_repr(obj: object) -> str: ... if sys.version_info >= (3, 12): - def override(__method: _F) -> _F: ... + def override(method: _F, /) -> _F: ... @final class TypeAliasType: def __init__( @@ -967,5 +972,5 @@ if sys.version_info >= (3, 12): def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 13): - def is_protocol(__tp: type) -> bool: ... - def get_protocol_members(__tp: type) -> frozenset[str]: ... + def is_protocol(tp: type, /) -> bool: ... + def get_protocol_members(tp: type, /) -> frozenset[str]: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 921c1334cfe4..f9e94ca683d6 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -235,22 +235,22 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): def setdefault(self, k: Never, default: object) -> object: ... # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. def pop(self, k: Never, default: _T = ...) -> object: ... # pyright: ignore[reportInvalidTypeVarUse] - def update(self: _T, __m: _T) -> None: ... + def update(self: _T, m: _T, /) -> None: ... def items(self) -> dict_items[str, object]: ... def keys(self) -> dict_keys[str, object]: ... def values(self) -> dict_values[str, object]: ... def __delitem__(self, k: Never) -> None: ... if sys.version_info >= (3, 9): @overload - def __or__(self, __value: Self) -> Self: ... + def __or__(self, value: Self, /) -> Self: ... @overload - def __or__(self, __value: dict[str, Any]) -> dict[str, object]: ... + def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... @overload - def __ror__(self, __value: Self) -> Self: ... + def __ror__(self, value: Self, /) -> Self: ... @overload - def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... + def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... # supposedly incompatible definitions of `__ior__` and `__or__`: - def __ior__(self, __value: Self) -> Self: ... # type: ignore[misc] + def __ior__(self, value: Self, /) -> Self: ... # type: ignore[misc] # TypedDict is a (non-subscriptable) special form. TypedDict: object @@ -335,9 +335,9 @@ if sys.version_info >= (3, 11): else: Self: _SpecialForm Never: _SpecialForm - def reveal_type(__obj: _T) -> _T: ... - def assert_never(__arg: Never) -> Never: ... - def assert_type(__val: _T, __typ: Any) -> _T: ... + def reveal_type(obj: _T, /) -> _T: ... + def assert_never(arg: Never, /) -> Never: ... + def assert_type(val: _T, typ: Any, /) -> _T: ... def clear_overloads() -> None: ... def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... @@ -373,7 +373,7 @@ else: class NewType: def __init__(self, name: str, tp: Any) -> None: ... - def __call__(self, __obj: _T) -> _T: ... + def __call__(self, obj: _T, /) -> _T: ... __supertype__: type if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -456,16 +456,16 @@ class deprecated: message: str category: type[Warning] | None stacklevel: int - def __init__(self, __message: str, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... - def __call__(self, __arg: _T) -> _T: ... + def __init__(self, message: str, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... + def __call__(self, arg: _T, /) -> _T: ... if sys.version_info >= (3, 12): from collections.abc import Buffer as Buffer from types import get_original_bases as get_original_bases from typing import TypeAliasType as TypeAliasType, override as override else: - def override(__arg: _F) -> _F: ... - def get_original_bases(__cls: type) -> tuple[Any, ...]: ... + def override(arg: _F, /) -> _F: ... + def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... @final class TypeAliasType: def __init__( @@ -491,17 +491,17 @@ else: class Buffer(Protocol): # Not actually a Protocol at runtime; see # https://github.com/python/typeshed/issues/10224 for why we're defining it this way - def __buffer__(self, __flags: int) -> memoryview: ... + def __buffer__(self, flags: int, /) -> memoryview: ... if sys.version_info >= (3, 13): from typing import get_protocol_members as get_protocol_members, is_protocol as is_protocol else: - def is_protocol(__tp: type) -> bool: ... - def get_protocol_members(__tp: type) -> frozenset[str]: ... + def is_protocol(tp: type, /) -> bool: ... + def get_protocol_members(tp: type, /) -> frozenset[str]: ... class Doc: documentation: str - def __init__(self, __documentation: str) -> None: ... + def __init__(self, documentation: str, /) -> None: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/unicodedata.pyi b/mypy/typeshed/stdlib/unicodedata.pyi index 5c6749c8a1ae..77d69edf06af 100644 --- a/mypy/typeshed/stdlib/unicodedata.pyi +++ b/mypy/typeshed/stdlib/unicodedata.pyi @@ -13,61 +13,61 @@ _T = TypeVar("_T") _NormalizationForm: TypeAlias = Literal["NFC", "NFD", "NFKC", "NFKD"] -def bidirectional(__chr: str) -> str: ... -def category(__chr: str) -> str: ... -def combining(__chr: str) -> int: ... +def bidirectional(chr: str, /) -> str: ... +def category(chr: str, /) -> str: ... +def combining(chr: str, /) -> int: ... @overload -def decimal(__chr: str) -> int: ... +def decimal(chr: str, /) -> int: ... @overload -def decimal(__chr: str, __default: _T) -> int | _T: ... -def decomposition(__chr: str) -> str: ... +def decimal(chr: str, default: _T, /) -> int | _T: ... +def decomposition(chr: str, /) -> str: ... @overload -def digit(__chr: str) -> int: ... +def digit(chr: str, /) -> int: ... @overload -def digit(__chr: str, __default: _T) -> int | _T: ... +def digit(chr: str, default: _T, /) -> int | _T: ... _EastAsianWidth: TypeAlias = Literal["F", "H", "W", "Na", "A", "N"] -def east_asian_width(__chr: str) -> _EastAsianWidth: ... -def is_normalized(__form: _NormalizationForm, __unistr: str) -> bool: ... -def lookup(__name: str | ReadOnlyBuffer) -> str: ... -def mirrored(__chr: str) -> int: ... +def east_asian_width(chr: str, /) -> _EastAsianWidth: ... +def is_normalized(form: _NormalizationForm, unistr: str, /) -> bool: ... +def lookup(name: str | ReadOnlyBuffer, /) -> str: ... +def mirrored(chr: str, /) -> int: ... @overload -def name(__chr: str) -> str: ... +def name(chr: str, /) -> str: ... @overload -def name(__chr: str, __default: _T) -> str | _T: ... -def normalize(__form: _NormalizationForm, __unistr: str) -> str: ... +def name(chr: str, default: _T, /) -> str | _T: ... +def normalize(form: _NormalizationForm, unistr: str, /) -> str: ... @overload -def numeric(__chr: str) -> float: ... +def numeric(chr: str, /) -> float: ... @overload -def numeric(__chr: str, __default: _T) -> float | _T: ... +def numeric(chr: str, default: _T, /) -> float | _T: ... @final class UCD: # The methods below are constructed from the same array in C # (unicodedata_functions) and hence identical to the functions above. unidata_version: str - def bidirectional(self, __chr: str) -> str: ... - def category(self, __chr: str) -> str: ... - def combining(self, __chr: str) -> int: ... + def bidirectional(self, chr: str, /) -> str: ... + def category(self, chr: str, /) -> str: ... + def combining(self, chr: str, /) -> int: ... @overload - def decimal(self, __chr: str) -> int: ... + def decimal(self, chr: str, /) -> int: ... @overload - def decimal(self, __chr: str, __default: _T) -> int | _T: ... - def decomposition(self, __chr: str) -> str: ... + def decimal(self, chr: str, default: _T, /) -> int | _T: ... + def decomposition(self, chr: str, /) -> str: ... @overload - def digit(self, __chr: str) -> int: ... + def digit(self, chr: str, /) -> int: ... @overload - def digit(self, __chr: str, __default: _T) -> int | _T: ... - def east_asian_width(self, __chr: str) -> _EastAsianWidth: ... - def is_normalized(self, __form: _NormalizationForm, __unistr: str) -> bool: ... - def lookup(self, __name: str | ReadOnlyBuffer) -> str: ... - def mirrored(self, __chr: str) -> int: ... + def digit(self, chr: str, default: _T, /) -> int | _T: ... + def east_asian_width(self, chr: str, /) -> _EastAsianWidth: ... + def is_normalized(self, form: _NormalizationForm, unistr: str, /) -> bool: ... + def lookup(self, name: str | ReadOnlyBuffer, /) -> str: ... + def mirrored(self, chr: str, /) -> int: ... @overload - def name(self, __chr: str) -> str: ... + def name(self, chr: str, /) -> str: ... @overload - def name(self, __chr: str, __default: _T) -> str | _T: ... - def normalize(self, __form: _NormalizationForm, __unistr: str) -> str: ... + def name(self, chr: str, default: _T, /) -> str | _T: ... + def normalize(self, form: _NormalizationForm, unistr: str, /) -> str: ... @overload - def numeric(self, __chr: str) -> float: ... + def numeric(self, chr: str, /) -> float: ... @overload - def numeric(self, __chr: str, __default: _T) -> float | _T: ... + def numeric(self, chr: str, default: _T, /) -> float | _T: ... diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index b71eec2e0644..12d6ef49e828 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -14,7 +14,7 @@ _P = ParamSpec("_P") class IsolatedAsyncioTestCase(TestCase): async def asyncSetUp(self) -> None: ... async def asyncTearDown(self) -> None: ... - def addAsyncCleanup(self, __func: Callable[_P, Awaitable[object]], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addAsyncCleanup(self, func: Callable[_P, Awaitable[object]], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): async def enterAsyncContext(self, cm: AbstractAsyncContextManager[_T]) -> _T: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index 120bb96d761b..bd1c064f0270 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -68,7 +68,7 @@ else: self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None ) -> bool | None: ... -def addModuleCleanup(__function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... +def addModuleCleanup(function: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def doModuleCleanups() -> None: ... if sys.version_info >= (3, 11): @@ -273,14 +273,14 @@ class TestCase: def defaultTestResult(self) -> unittest.result.TestResult: ... def id(self) -> str: ... def shortDescription(self) -> str | None: ... - def addCleanup(self, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addCleanup(self, function: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): def enterContext(self, cm: AbstractContextManager[_T]) -> _T: ... def doCleanups(self) -> None: ... @classmethod - def addClassCleanup(cls, __function: Callable[_P, object], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def addClassCleanup(cls, function: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... @classmethod def doClassCleanups(cls) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 3e8cb7b764c2..55bc1ec741db 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -11,7 +11,7 @@ MAIN_EXAMPLES: str MODULE_EXAMPLES: str class _TestRunner(Protocol): - def run(self, __test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... + def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase, /) -> unittest.result.TestResult: ... # not really documented class TestProgram: diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index c6014d4bb886..6e64e7a85560 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -65,7 +65,7 @@ class _Call(tuple[Any, ...]): from_kall: bool = True, ) -> None: ... def __eq__(self, other: object) -> bool: ... - def __ne__(self, __value: object) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... def __getattr__(self, attr: str) -> Any: ... def __getattribute__(self, attr: str) -> Any: ... @@ -103,7 +103,7 @@ class NonCallableMock(Base, Any): **kwargs: Any, ) -> Self: ... else: - def __new__(__cls, *args: Any, **kw: Any) -> Self: ... + def __new__(cls, /, *args: Any, **kw: Any) -> Self: ... def __init__( self, @@ -234,7 +234,7 @@ class _patch(Generic[_T]): is_local: bool def __enter__(self) -> _T: ... def __exit__( - self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> None: ... def start(self) -> _T: ... def stop(self) -> None: ... diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index ca3feaea262a..3442be8b8ea4 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -227,7 +227,8 @@ class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): class _HTTPConnectionProtocol(Protocol): def __call__( self, - __host: str, + host: str, + /, *, port: int | None = ..., timeout: float = ..., diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 1bb2eacfb46a..8f3ad0631c10 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -51,10 +51,10 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): @overload def __init__(self) -> None: ... @overload - def __init__(self: WeakValueDictionary[_KT, _VT], __other: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]]) -> None: ... + def __init__(self: WeakValueDictionary[_KT, _VT], other: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]], /) -> None: ... @overload def __init__( - self: WeakValueDictionary[str, _VT], __other: Mapping[str, _VT] | Iterable[tuple[str, _VT]] = (), **kwargs: _VT + self: WeakValueDictionary[str, _VT], other: Mapping[str, _VT] | Iterable[tuple[str, _VT]] = (), /, **kwargs: _VT ) -> None: ... def __len__(self) -> int: ... def __getitem__(self, key: _KT) -> _VT: ... @@ -93,7 +93,6 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): class KeyedRef(ref[_T], Generic[_KT, _T]): key: _KT - # This __new__ method uses a non-standard name for the "cls" parameter def __new__(type, ob: _T, callback: Callable[[_T], Any], key: _KT) -> Self: ... def __init__(self, ob: _T, callback: Callable[[_T], Any], key: _KT) -> None: ... @@ -141,7 +140,7 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... class finalize: # TODO: This is a good candidate for to be a `Generic[_P, _T]` class - def __init__(self, __obj: object, __func: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ... + def __init__(self, obj: object, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def __call__(self, _: Any = None) -> Any | None: ... def detach(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... def peek(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index 897177547c71..ffb0a4cb8094 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -5,31 +5,31 @@ from typing_extensions import Self, TypeAlias if sys.platform == "win32": _KeyType: TypeAlias = HKEYType | int - def CloseKey(__hkey: _KeyType) -> None: ... - def ConnectRegistry(__computer_name: str | None, __key: _KeyType) -> HKEYType: ... - def CreateKey(__key: _KeyType, __sub_key: str | None) -> HKEYType: ... + def CloseKey(hkey: _KeyType, /) -> None: ... + def ConnectRegistry(computer_name: str | None, key: _KeyType, /) -> HKEYType: ... + def CreateKey(key: _KeyType, sub_key: str | None, /) -> HKEYType: ... def CreateKeyEx(key: _KeyType, sub_key: str | None, reserved: int = 0, access: int = 131078) -> HKEYType: ... - def DeleteKey(__key: _KeyType, __sub_key: str) -> None: ... + def DeleteKey(key: _KeyType, sub_key: str, /) -> None: ... def DeleteKeyEx(key: _KeyType, sub_key: str, access: int = 256, reserved: int = 0) -> None: ... - def DeleteValue(__key: _KeyType, __value: str) -> None: ... - def EnumKey(__key: _KeyType, __index: int) -> str: ... - def EnumValue(__key: _KeyType, __index: int) -> tuple[str, Any, int]: ... - def ExpandEnvironmentStrings(__string: str) -> str: ... - def FlushKey(__key: _KeyType) -> None: ... - def LoadKey(__key: _KeyType, __sub_key: str, __file_name: str) -> None: ... + def DeleteValue(key: _KeyType, value: str, /) -> None: ... + def EnumKey(key: _KeyType, index: int, /) -> str: ... + def EnumValue(key: _KeyType, index: int, /) -> tuple[str, Any, int]: ... + def ExpandEnvironmentStrings(string: str, /) -> str: ... + def FlushKey(key: _KeyType, /) -> None: ... + def LoadKey(key: _KeyType, sub_key: str, file_name: str, /) -> None: ... def OpenKey(key: _KeyType, sub_key: str, reserved: int = 0, access: int = 131097) -> HKEYType: ... def OpenKeyEx(key: _KeyType, sub_key: str, reserved: int = 0, access: int = 131097) -> HKEYType: ... - def QueryInfoKey(__key: _KeyType) -> tuple[int, int, int]: ... - def QueryValue(__key: _KeyType, __sub_key: str | None) -> str: ... - def QueryValueEx(__key: _KeyType, __name: str) -> tuple[Any, int]: ... - def SaveKey(__key: _KeyType, __file_name: str) -> None: ... - def SetValue(__key: _KeyType, __sub_key: str, __type: int, __value: str) -> None: ... + def QueryInfoKey(key: _KeyType, /) -> tuple[int, int, int]: ... + def QueryValue(key: _KeyType, sub_key: str | None, /) -> str: ... + def QueryValueEx(key: _KeyType, name: str, /) -> tuple[Any, int]: ... + def SaveKey(key: _KeyType, file_name: str, /) -> None: ... + def SetValue(key: _KeyType, sub_key: str, type: int, value: str, /) -> None: ... def SetValueEx( - __key: _KeyType, __value_name: str | None, __reserved: Any, __type: int, __value: str | int + key: _KeyType, value_name: str | None, reserved: Any, type: int, value: str | int, / ) -> None: ... # reserved is ignored - def DisableReflectionKey(__key: _KeyType) -> None: ... - def EnableReflectionKey(__key: _KeyType) -> None: ... - def QueryReflectionKey(__key: _KeyType) -> bool: ... + def DisableReflectionKey(key: _KeyType, /) -> None: ... + def EnableReflectionKey(key: _KeyType, /) -> None: ... + def QueryReflectionKey(key: _KeyType, /) -> bool: ... HKEY_CLASSES_ROOT: int HKEY_CURRENT_USER: int HKEY_LOCAL_MACHINE: int diff --git a/mypy/typeshed/stdlib/wsgiref/types.pyi b/mypy/typeshed/stdlib/wsgiref/types.pyi index 4e8f47264f3a..86212df8ccdc 100644 --- a/mypy/typeshed/stdlib/wsgiref/types.pyi +++ b/mypy/typeshed/stdlib/wsgiref/types.pyi @@ -7,26 +7,26 @@ __all__ = ["StartResponse", "WSGIEnvironment", "WSGIApplication", "InputStream", class StartResponse(Protocol): def __call__( - self, __status: str, __headers: list[tuple[str, str]], __exc_info: _OptExcInfo | None = ... + self, status: str, headers: list[tuple[str, str]], exc_info: _OptExcInfo | None = ..., / ) -> Callable[[bytes], object]: ... WSGIEnvironment: TypeAlias = dict[str, Any] WSGIApplication: TypeAlias = Callable[[WSGIEnvironment, StartResponse], Iterable[bytes]] class InputStream(Protocol): - def read(self, __size: int = ...) -> bytes: ... - def readline(self, __size: int = ...) -> bytes: ... - def readlines(self, __hint: int = ...) -> list[bytes]: ... + def read(self, size: int = ..., /) -> bytes: ... + def readline(self, size: int = ..., /) -> bytes: ... + def readlines(self, hint: int = ..., /) -> list[bytes]: ... def __iter__(self) -> Iterator[bytes]: ... class ErrorStream(Protocol): def flush(self) -> object: ... - def write(self, __s: str) -> object: ... - def writelines(self, __seq: list[str]) -> object: ... + def write(self, s: str, /) -> object: ... + def writelines(self, seq: list[str], /) -> object: ... class _Readable(Protocol): - def read(self, __size: int = ...) -> bytes: ... + def read(self, size: int = ..., /) -> bytes: ... # Optional: def close(self) -> object: ... class FileWrapper(Protocol): - def __call__(self, __file: _Readable, __block_size: int = ...) -> Iterable[bytes]: ... + def __call__(self, file: _Readable, block_size: int = ..., /) -> Iterable[bytes]: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 2a363a504dec..a8af66938344 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -84,9 +84,9 @@ class Element: text: str | None tail: str | None def __init__(self, tag: str, attrib: dict[str, str] = ..., **extra: str) -> None: ... - def append(self, __subelement: Element) -> None: ... + def append(self, subelement: Element, /) -> None: ... def clear(self) -> None: ... - def extend(self, __elements: Iterable[Element]) -> None: ... + def extend(self, elements: Iterable[Element], /) -> None: ... def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... def findall(self, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... @overload @@ -97,30 +97,30 @@ class Element: def get(self, key: str, default: None = None) -> str | None: ... @overload def get(self, key: str, default: _T) -> str | _T: ... - def insert(self, __index: int, __subelement: Element) -> None: ... + def insert(self, index: int, subelement: Element, /) -> None: ... def items(self) -> ItemsView[str, str]: ... def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ... def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... def itertext(self) -> Generator[str, None, None]: ... def keys(self) -> dict_keys[str, str]: ... # makeelement returns the type of self in Python impl, but not in C impl - def makeelement(self, __tag: str, __attrib: dict[str, str]) -> Element: ... - def remove(self, __subelement: Element) -> None: ... - def set(self, __key: str, __value: str) -> None: ... + def makeelement(self, tag: str, attrib: dict[str, str], /) -> Element: ... + def remove(self, subelement: Element, /) -> None: ... + def set(self, key: str, value: str, /) -> None: ... def __copy__(self) -> Element: ... # returns the type of self in Python impl, but not in C impl - def __deepcopy__(self, __memo: Any) -> Element: ... # Only exists in C impl - def __delitem__(self, __key: SupportsIndex | slice) -> None: ... + def __deepcopy__(self, memo: Any, /) -> Element: ... # Only exists in C impl + def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... @overload - def __getitem__(self, __key: SupportsIndex) -> Element: ... + def __getitem__(self, key: SupportsIndex, /) -> Element: ... @overload - def __getitem__(self, __key: slice) -> list[Element]: ... + def __getitem__(self, key: slice, /) -> list[Element]: ... def __len__(self) -> int: ... # Doesn't actually exist at runtime, but instance of the class are indeed iterable due to __getitem__. def __iter__(self) -> Iterator[Element]: ... @overload - def __setitem__(self, __key: SupportsIndex, __value: Element) -> None: ... + def __setitem__(self, key: SupportsIndex, value: Element, /) -> None: ... @overload - def __setitem__(self, __key: slice, __value: Iterable[Element]) -> None: ... + def __setitem__(self, key: slice, value: Iterable[Element], /) -> None: ... # Doesn't really exist in earlier versions, where __len__ is called implicitly instead @deprecated("Testing an element's truth value is deprecated.") @@ -285,14 +285,14 @@ class TreeBuilder: insert_pis: bool def close(self) -> Element: ... - def data(self, __data: str) -> None: ... + def data(self, data: str, /) -> None: ... # tag and attrs are passed to the element_factory, so they could be anything # depending on what the particular factory supports. - def start(self, __tag: Any, __attrs: dict[Any, Any]) -> Element: ... - def end(self, __tag: str) -> Element: ... + def start(self, tag: Any, attrs: dict[Any, Any], /) -> Element: ... + def end(self, tag: str, /) -> Element: ... # These two methods have pos-only parameters in the C implementation - def comment(self, __text: str | None) -> Element: ... - def pi(self, __target: str, __text: str | None = None) -> Element: ... + def comment(self, text: str | None, /) -> Element: ... + def pi(self, target: str, text: str | None = None, /) -> Element: ... class C14NWriterTarget: def __init__( @@ -322,4 +322,4 @@ class XMLParser: version: str def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... def close(self) -> Any: ... - def feed(self, __data: str | ReadableBuffer) -> None: ... + def feed(self, data: str | ReadableBuffer, /) -> None: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/server.pyi b/mypy/typeshed/stdlib/xmlrpc/server.pyi index 832fe265e0a5..8ca3a4d1a33c 100644 --- a/mypy/typeshed/stdlib/xmlrpc/server.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi @@ -12,17 +12,17 @@ class _DispatchArity0(Protocol): def __call__(self) -> _Marshallable: ... class _DispatchArity1(Protocol): - def __call__(self, __arg1: _Marshallable) -> _Marshallable: ... + def __call__(self, arg1: _Marshallable, /) -> _Marshallable: ... class _DispatchArity2(Protocol): - def __call__(self, __arg1: _Marshallable, __arg2: _Marshallable) -> _Marshallable: ... + def __call__(self, arg1: _Marshallable, arg2: _Marshallable, /) -> _Marshallable: ... class _DispatchArity3(Protocol): - def __call__(self, __arg1: _Marshallable, __arg2: _Marshallable, __arg3: _Marshallable) -> _Marshallable: ... + def __call__(self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, /) -> _Marshallable: ... class _DispatchArity4(Protocol): def __call__( - self, __arg1: _Marshallable, __arg2: _Marshallable, __arg3: _Marshallable, __arg4: _Marshallable + self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, arg4: _Marshallable, / ) -> _Marshallable: ... class _DispatchArityN(Protocol): diff --git a/mypy/typeshed/stdlib/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index 3e6e78de3f70..6bae87a8db2a 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -9,7 +9,7 @@ class Xxo: if sys.version_info >= (3, 11) and sys.platform != "win32": x_exports: int -def foo(__i: int, __j: int) -> Any: ... +def foo(i: int, j: int, /) -> Any: ... def new() -> Xxo: ... if sys.version_info >= (3, 10): @@ -19,4 +19,4 @@ else: class error(Exception): ... class Null: ... - def roj(__b: Any) -> None: ... + def roj(b: Any, /) -> None: ... diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index be0cdf12a4a9..b61e07f8b90d 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -40,16 +40,16 @@ error = BadZipfile class LargeZipFile(Exception): ... class _ZipStream(Protocol): - def read(self, __n: int) -> bytes: ... + def read(self, n: int, /) -> bytes: ... # The following methods are optional: # def seekable(self) -> bool: ... # def tell(self) -> int: ... - # def seek(self, __n: int) -> object: ... + # def seek(self, n: int, /) -> object: ... # Stream shape as required by _EndRecData() and _EndRecData64(). class _SupportsReadSeekTell(Protocol): - def read(self, __n: int = ...) -> bytes: ... - def seek(self, __cookie: int, __whence: int) -> object: ... + def read(self, n: int = ..., /) -> bytes: ... + def seek(self, cookie: int, whence: int, /) -> object: ... def tell(self) -> int: ... class _ClosableZipStream(_ZipStream, Protocol): @@ -92,7 +92,7 @@ class ZipExtFile(io.BufferedIOBase): def seek(self, offset: int, whence: int = 0) -> int: ... class _Writer(Protocol): - def write(self, __s: str) -> object: ... + def write(self, s: str, /) -> object: ... class ZipFile: filename: str | None diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index efeb5a88a76f..234770172d40 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -40,17 +40,17 @@ class _Decompress: def flush(self, length: int = ...) -> bytes: ... def copy(self) -> _Decompress: ... -def adler32(__data: ReadableBuffer, __value: int = 1) -> int: ... +def adler32(data: ReadableBuffer, value: int = 1, /) -> int: ... if sys.version_info >= (3, 11): - def compress(__data: ReadableBuffer, level: int = -1, wbits: int = 15) -> bytes: ... + def compress(data: ReadableBuffer, /, level: int = -1, wbits: int = 15) -> bytes: ... else: - def compress(__data: ReadableBuffer, level: int = -1) -> bytes: ... + def compress(data: ReadableBuffer, /, level: int = -1) -> bytes: ... def compressobj( level: int = -1, method: int = 8, wbits: int = 15, memLevel: int = 8, strategy: int = 0, zdict: ReadableBuffer | None = None ) -> _Compress: ... -def crc32(__data: ReadableBuffer, __value: int = 0) -> int: ... -def decompress(__data: ReadableBuffer, wbits: int = 15, bufsize: int = 16384) -> bytes: ... +def crc32(data: ReadableBuffer, value: int = 0, /) -> int: ... +def decompress(data: ReadableBuffer, /, wbits: int = 15, bufsize: int = 16384) -> bytes: ... def decompressobj(wbits: int = 15, zdict: ReadableBuffer = b"") -> _Decompress: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index a95530ed461a..77930ac79dd5 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -7,8 +7,8 @@ from typing_extensions import Self __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] class _IOBytes(Protocol): - def read(self, __size: int) -> bytes: ... - def seek(self, __size: int, __whence: int = ...) -> Any: ... + def read(self, size: int, /) -> bytes: ... + def seek(self, size: int, whence: int = ..., /) -> Any: ... class ZoneInfo(tzinfo): @property @@ -17,12 +17,12 @@ class ZoneInfo(tzinfo): @classmethod def no_cache(cls, key: str) -> Self: ... @classmethod - def from_file(cls, __fobj: _IOBytes, key: str | None = None) -> Self: ... + def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... @classmethod def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... - def tzname(self, __dt: datetime | None) -> str | None: ... - def utcoffset(self, __dt: datetime | None) -> timedelta | None: ... - def dst(self, __dt: datetime | None) -> timedelta | None: ... + def tzname(self, dt: datetime | None, /) -> str | None: ... + def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... + def dst(self, dt: datetime | None, /) -> timedelta | None: ... # Note: Both here and in clear_cache, the types allow the use of `str` where # a sequence of strings is required. This should be remedied if a solution From 5c00e362d40aa26e0a22a740f05a52d05edf0f91 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH 0536/1617] Remove use of LiteralString in builtins (#13743) --- mypy/typeshed/stdlib/builtins.pyi | 88 ------------------------------- 1 file changed, 88 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index b4765b26c8e5..99919c64c50d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, - LiteralString, ParamSpec, Self, TypeAlias, @@ -434,31 +433,16 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload def capitalize(self) -> str: ... # type: ignore[misc] - @overload - def casefold(self: LiteralString) -> LiteralString: ... - @overload def casefold(self) -> str: ... # type: ignore[misc] - @overload - def center(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... - @overload def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> bool: ... - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, map: _FormatMapMapping) -> str: ... def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... @@ -474,89 +458,32 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - @overload - def join(self: LiteralString, iterable: Iterable[LiteralString], /) -> LiteralString: ... - @overload def join(self, iterable: Iterable[str], /) -> str: ... # type: ignore[misc] - @overload - def ljust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... - @overload def ljust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] - @overload - def lower(self: LiteralString) -> LiteralString: ... - @overload def lower(self) -> str: ... # type: ignore[misc] - @overload - def lstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... - @overload def lstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] - @overload - def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def replace(self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /) -> LiteralString: ... - @overload def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... - @overload def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] - @overload - def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ... - @overload def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... - @overload - def rjust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... - @overload def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] - @overload - def rpartition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def rpartition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] - @overload - def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def rstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... - @overload def rstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] - @overload - def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... - @overload def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] - @overload - def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / ) -> bool: ... - @overload - def strip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... - @overload def strip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] - @overload - def swapcase(self: LiteralString) -> LiteralString: ... - @overload def swapcase(self) -> str: ... # type: ignore[misc] - @overload - def title(self: LiteralString) -> LiteralString: ... - @overload def title(self) -> str: ... # type: ignore[misc] def translate(self, table: _TranslateTable, /) -> str: ... - @overload - def upper(self: LiteralString) -> LiteralString: ... - @overload def upper(self) -> str: ... # type: ignore[misc] - @overload - def zfill(self: LiteralString, width: SupportsIndex, /) -> LiteralString: ... - @overload def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload @@ -567,9 +494,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... - @overload - def __add__(self: LiteralString, value: LiteralString, /) -> LiteralString: ... - @overload def __add__(self, value: str, /) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] @@ -578,25 +502,13 @@ class str(Sequence[str]): def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... def __gt__(self, value: str, /) -> bool: ... def __hash__(self) -> int: ... - @overload - def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... - @overload def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] def __le__(self, value: str, /) -> bool: ... def __len__(self) -> int: ... def __lt__(self, value: str, /) -> bool: ... - @overload - def __mod__(self: LiteralString, value: LiteralString | tuple[LiteralString, ...], /) -> LiteralString: ... - @overload def __mod__(self, value: Any, /) -> str: ... - @overload - def __mul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... - @overload def __mul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] def __ne__(self, value: object, /) -> bool: ... - @overload - def __rmul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... - @overload def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... From 44bc98bd50e7170887f0740b53ed95a8eb04f00e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH 0537/1617] Revert sum literal integer change (#13961) This is allegedly causing large performance problems, see 13821 typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing to undo. Patching this in typeshed also feels weird, since there's a more general soundness issue. If a typevar has a bound or constraint, we might not want to solve it to a Literal. If we can confirm the performance regression or fix the unsoundness within mypy, I might pursue upstreaming this in typeshed. (Reminder: add this to the sync_typeshed script once merged) --- mypy/typeshed/stdlib/builtins.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 99919c64c50d..680cd556172f 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1596,7 +1596,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] @overload def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload From 61a490091d7c941780919660dc4fdfa88ae6474a Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH 0538/1617] Revert typeshed ctypes change Since the plugin provides superior type checking: https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual cherry-pick of e437cdf. --- mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 60bbc51d9411..cf9cb81a44a3 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -169,11 +169,7 @@ class Array(_CData, Generic[_CT]): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... - # Note: only available if _CT == c_char - @property - def raw(self) -> bytes: ... - @raw.setter - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise # TODO These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT From b013cc016a3e3c8c7caa6a27bdf7b5e22998dd41 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 16 Mar 2024 14:38:05 +0100 Subject: [PATCH 0539/1617] Support `TypeAliasType` in a class scope (#17038) Fixes https://github.com/python/mypy/issues/16614#issuecomment-2000428700 --- mypy/semanal.py | 2 +- test-data/unit/check-type-aliases.test | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 93e84ced4639..5aaf2bc6f433 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3657,7 +3657,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: return False non_global_scope = self.type or self.is_func_scope() - if not pep_613 and isinstance(rvalue, RefExpr) and non_global_scope: + if not pep_613 and not pep_695 and isinstance(rvalue, RefExpr) and non_global_scope: # Fourth rule (special case): Non-subscripted right hand side creates a variable # at class and function scopes. For example: # diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 79a443dbeedc..7330a04c3647 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1074,6 +1074,11 @@ TestType = TypeAliasType("TestType", Union[int, str]) x: TestType = 42 y: TestType = 'a' z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") + +class A: + ClassAlias = TypeAliasType("ClassAlias", int) +xc: A.ClassAlias = 1 +yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/tuple.pyi] [case testTypeAliasTypeInvalid] From 00220bd095f42b080e0844bb2b0c11f37afc35a5 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 16 Mar 2024 15:39:13 +0000 Subject: [PATCH 0540/1617] Update commit hashes in sync-typeshed.py (#17042) Followup to #17039 --- misc/sync-typeshed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 56bc1624d5d0..2dc6e230df00 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -179,9 +179,9 @@ def main() -> None: print("Created typeshed sync commit.") commits_to_cherry_pick = [ - "d25e4a9eb", # LiteralString reverts - "d132999ba", # sum reverts - "dd12a2d81", # ctypes reverts + "5c00e362d", # LiteralString reverts + "44bc98bd5", # sum reverts + "61a490091", # ctypes reverts ] for commit in commits_to_cherry_pick: try: From 7d0a8e79dfa3219135baf4d227ab4975aef710b7 Mon Sep 17 00:00:00 2001 From: Roman Solomatin <36135455+Samoed@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:19:27 +0300 Subject: [PATCH 0541/1617] Update running_mypy.rst add closing bracket (#17046) --- docs/source/running_mypy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 25b34b247b4b..42474ae94c48 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -405,7 +405,7 @@ this error, try: For example, suppose you are trying to add the module ``foo.bar.baz`` which is located at ``~/foo-project/src/foo/bar/baz.py``. In this case, you must run ``mypy ~/foo-project/src`` (or set the ``MYPYPATH`` to - ``~/foo-project/src``. + ``~/foo-project/src``). .. _finding-imports: From afdd9d5b2bb8a9ed9819571b266903396f47c5d9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 19 Mar 2024 17:08:51 +0000 Subject: [PATCH 0542/1617] [mypyc] Implement lowering for remaining tagged integer comparisons (#17040) Support lowering of tagged integer `<`, `<=`, `>` and `>=` operations. Previously we had separate code paths for integer comparisons in values vs conditions. Unify these and remove the duplicate code path. The different code paths produced subtly different code, but now they are identical. The generated code is now sometimes slightly more verbose in the slow path (big integer). I may look into simplifying it in a follow-up PR. This also makes the output of many irbuild test cases significantly more compact. Follow-up to #17027. Work on mypyc/mypyc#854. --- mypyc/ir/pprint.py | 2 +- mypyc/irbuild/ast_helpers.py | 9 +- mypyc/irbuild/builder.py | 3 - mypyc/irbuild/expression.py | 6 - mypyc/irbuild/function.py | 5 +- mypyc/irbuild/ll_builder.py | 94 +---- mypyc/lower/int_ops.py | 20 + mypyc/primitives/int_ops.py | 4 + mypyc/test-data/analysis.test | 205 +++------ mypyc/test-data/exceptions.test | 58 +-- mypyc/test-data/irbuild-basic.test | 435 +++++--------------- mypyc/test-data/irbuild-bool.test | 56 +-- mypyc/test-data/irbuild-dunders.test | 20 +- mypyc/test-data/irbuild-int.test | 51 +-- mypyc/test-data/irbuild-lists.test | 13 +- mypyc/test-data/irbuild-set.test | 126 +++--- mypyc/test-data/irbuild-singledispatch.test | 2 +- mypyc/test-data/irbuild-statements.test | 140 ++----- mypyc/test-data/irbuild-tuple.test | 8 +- mypyc/test-data/lowering-int.test | 273 +++++++++++- mypyc/test-data/refcount.test | 60 +-- 21 files changed, 622 insertions(+), 968 deletions(-) diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 8d6723917ea0..2ca6a47921fc 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -232,7 +232,7 @@ def visit_primitive_op(self, op: PrimitiveOp) -> str: type_arg_index += 1 args_str = ", ".join(args) - return self.format("%r = %s %s ", op, op.desc.name, args_str) + return self.format("%r = %s %s", op, op.desc.name, args_str) def visit_truncate(self, op: Truncate) -> str: return self.format("%r = truncate %r: %t to %t", op, op.src, op.src_type, op.type) diff --git a/mypyc/irbuild/ast_helpers.py b/mypyc/irbuild/ast_helpers.py index 8490eaa03477..bc976647675d 100644 --- a/mypyc/irbuild/ast_helpers.py +++ b/mypyc/irbuild/ast_helpers.py @@ -93,12 +93,9 @@ def maybe_process_conditional_comparison( self.add_bool_branch(reg, true, false) else: # "left op right" for two tagged integers - if op in ("==", "!="): - reg = self.builder.binary_op(left, right, op, e.line) - self.flush_keep_alives() - self.add_bool_branch(reg, true, false) - else: - self.builder.compare_tagged_condition(left, right, op, true, false, e.line) + reg = self.builder.binary_op(left, right, op, e.line) + self.flush_keep_alives() + self.add_bool_branch(reg, true, false) return True diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 52891d68e3b2..cca771e82c83 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -378,9 +378,6 @@ def call_c(self, desc: CFunctionDescription, args: list[Value], line: int) -> Va def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.builder.int_op(type, lhs, rhs, op, line) - def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: - return self.builder.compare_tagged(lhs, rhs, op, line) - def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: return self.builder.compare_tuples(lhs, rhs, op, line) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 021b7a1dbe90..ba62d71d0ad3 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -814,12 +814,6 @@ def translate_is_none(builder: IRBuilder, expr: Expression, negated: bool) -> Va def transform_basic_comparison( builder: IRBuilder, op: str, left: Value, right: Value, line: int ) -> Value: - if ( - is_int_rprimitive(left.type) - and is_int_rprimitive(right.type) - and op in int_comparison_op_mapping - ): - return builder.compare_tagged(left, right, op, line) if is_fixed_width_rtype(left.type) and op in int_comparison_op_mapping: if right.type == left.type: if left.type.is_signed: diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index b1785f40550e..c985e88b0e0c 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -889,9 +889,8 @@ def gen_native_func_call_and_return(fdef: FuncDef) -> None: call_impl, next_impl = BasicBlock(), BasicBlock() current_id = builder.load_int(i) - builder.builder.compare_tagged_condition( - passed_id, current_id, "==", call_impl, next_impl, line - ) + cond = builder.binary_op(passed_id, current_id, "==", line) + builder.add_bool_branch(cond, call_impl, next_impl) # Call the registered implementation builder.activate_block(call_impl) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index f9bacb43bc3e..548b391030fe 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1315,13 +1315,6 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.compare_strings(lreg, rreg, op, line) if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype) and op in ("==", "!="): return self.compare_bytes(lreg, rreg, op, line) - if ( - is_tagged(ltype) - and is_tagged(rtype) - and op in int_comparison_op_mapping - and op not in ("==", "!=") - ): - return self.compare_tagged(lreg, rreg, op, line) if is_bool_rprimitive(ltype) and is_bool_rprimitive(rtype) and op in BOOL_BINARY_OPS: if op in ComparisonOp.signed_ops: return self.bool_comparison_op(lreg, rreg, op, line) @@ -1384,16 +1377,6 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if is_fixed_width_rtype(lreg.type): return self.comparison_op(lreg, rreg, op_id, line) - # Mixed int comparisons - if op in ("==", "!="): - pass # TODO: Do we need anything here? - elif op in op in int_comparison_op_mapping: - if is_tagged(ltype) and is_subtype(rtype, ltype): - rreg = self.coerce(rreg, short_int_rprimitive, line) - return self.compare_tagged(lreg, rreg, op, line) - if is_tagged(rtype) and is_subtype(ltype, rtype): - lreg = self.coerce(lreg, short_int_rprimitive, line) - return self.compare_tagged(lreg, rreg, op, line) if is_float_rprimitive(ltype) or is_float_rprimitive(rtype): if isinstance(lreg, Integer): lreg = Float(float(lreg.numeric_value())) @@ -1445,18 +1428,16 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] result = Register(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() - check_lhs = self.check_tagged_short_int(lhs, line) + check_lhs = self.check_tagged_short_int(lhs, line, negated=True) if op in ("==", "!="): - check = check_lhs + self.add(Branch(check_lhs, int_block, short_int_block, Branch.BOOL)) else: # for non-equality logical ops (less/greater than, etc.), need to check both sides - check_rhs = self.check_tagged_short_int(rhs, line) - check = self.int_op(bit_rprimitive, check_lhs, check_rhs, IntOp.AND, line) - self.add(Branch(check, short_int_block, int_block, Branch.BOOL)) - self.activate_block(short_int_block) - eq = self.comparison_op(lhs, rhs, op_type, line) - self.add(Assign(result, eq, line)) - self.goto(out) + short_lhs = BasicBlock() + self.add(Branch(check_lhs, int_block, short_lhs, Branch.BOOL)) + self.activate_block(short_lhs) + check_rhs = self.check_tagged_short_int(rhs, line, negated=True) + self.add(Branch(check_rhs, int_block, short_int_block, Branch.BOOL)) self.activate_block(int_block) if swap_op: args = [rhs, lhs] @@ -1469,62 +1450,12 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: else: call_result = call self.add(Assign(result, call_result, line)) - self.goto_and_activate(out) - return result - - def compare_tagged_condition( - self, lhs: Value, rhs: Value, op: str, true: BasicBlock, false: BasicBlock, line: int - ) -> None: - """Compare two tagged integers using given operator (conditional context). - - Assume lhs and rhs are tagged integers. - - Args: - lhs: Left operand - rhs: Right operand - op: Operation, one of '==', '!=', '<', '<=', '>', '<=' - true: Branch target if comparison is true - false: Branch target if comparison is false - """ - is_eq = op in ("==", "!=") - if (is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type)) or ( - is_eq and (is_short_int_rprimitive(lhs.type) or is_short_int_rprimitive(rhs.type)) - ): - # We can skip the tag check - check = self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) - self.flush_keep_alives() - self.add(Branch(check, true, false, Branch.BOOL)) - return - op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] - int_block, short_int_block = BasicBlock(), BasicBlock() - check_lhs = self.check_tagged_short_int(lhs, line, negated=True) - if is_eq or is_short_int_rprimitive(rhs.type): - self.flush_keep_alives() - self.add(Branch(check_lhs, int_block, short_int_block, Branch.BOOL)) - else: - # For non-equality logical ops (less/greater than, etc.), need to check both sides - rhs_block = BasicBlock() - self.add(Branch(check_lhs, int_block, rhs_block, Branch.BOOL)) - self.activate_block(rhs_block) - check_rhs = self.check_tagged_short_int(rhs, line, negated=True) - self.flush_keep_alives() - self.add(Branch(check_rhs, int_block, short_int_block, Branch.BOOL)) - # Arbitrary integers (slow path) - self.activate_block(int_block) - if swap_op: - args = [rhs, lhs] - else: - args = [lhs, rhs] - call = self.call_c(c_func_desc, args, line) - if negate_result: - self.add(Branch(call, false, true, Branch.BOOL)) - else: - self.flush_keep_alives() - self.add(Branch(call, true, false, Branch.BOOL)) - # Short integers (fast path) + self.goto(out) self.activate_block(short_int_block) eq = self.comparison_op(lhs, rhs, op_type, line) - self.add(Branch(eq, true, false, Branch.BOOL)) + self.add(Assign(result, eq, line)) + self.goto_and_activate(out) + return result def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two strings""" @@ -2309,7 +2240,8 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val length = self.gen_method_call(val, "__len__", [], int_rprimitive, line) length = self.coerce(length, int_rprimitive, line) ok, fail = BasicBlock(), BasicBlock() - self.compare_tagged_condition(length, Integer(0), ">=", ok, fail, line) + cond = self.binary_op(length, Integer(0), ">=", line) + self.add_bool_branch(cond, ok, fail) self.activate_block(fail) self.add( RaiseStandardError( diff --git a/mypyc/lower/int_ops.py b/mypyc/lower/int_ops.py index 40fba7af4f4d..5255a64b647d 100644 --- a/mypyc/lower/int_ops.py +++ b/mypyc/lower/int_ops.py @@ -13,3 +13,23 @@ def lower_int_eq(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Va @lower_binary_op("int_ne") def lower_int_ne(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return builder.compare_tagged(args[0], args[1], "!=", line) + + +@lower_binary_op("int_lt") +def lower_int_lt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], "<", line) + + +@lower_binary_op("int_le") +def lower_int_le(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], "<=", line) + + +@lower_binary_op("int_gt") +def lower_int_gt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], ">", line) + + +@lower_binary_op("int_ge") +def lower_int_ge(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + return builder.compare_tagged(args[0], args[1], ">=", line) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 4103fe349a74..029d71606886 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -122,6 +122,10 @@ def int_binary_primitive( int_eq = int_binary_primitive(op="==", primitive_name="int_eq", return_type=bit_rprimitive) int_ne = int_binary_primitive(op="!=", primitive_name="int_ne", return_type=bit_rprimitive) +int_lt = int_binary_primitive(op="<", primitive_name="int_lt", return_type=bit_rprimitive) +int_le = int_binary_primitive(op="<=", primitive_name="int_le", return_type=bit_rprimitive) +int_gt = int_binary_primitive(op=">", primitive_name="int_gt", return_type=bit_rprimitive) +int_ge = int_binary_primitive(op=">=", primitive_name="int_ge", return_type=bit_rprimitive) def int_binary_op( diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 8e067aed4d79..35677b8ea56d 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -148,40 +148,27 @@ def f(n: int) -> None: [out] def f(n): n :: int - r0 :: native_int - r1, r2, r3 :: bit - r4, m :: int + r0 :: bit + r1, m :: int L0: L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 10 + if r0 goto L2 else goto L3 :: bool L2: - r2 = CPyTagged_IsLt_(n, 10) - if r2 goto L4 else goto L5 :: bool -L3: - r3 = n < 10 :: signed - if r3 goto L4 else goto L5 :: bool -L4: - r4 = CPyTagged_Add(n, 2) - n = r4 + r1 = CPyTagged_Add(n, 2) + n = r1 m = n goto L1 -L5: +L3: return 1 (0, 0) {n} {n} (1, 0) {n} {n} (1, 1) {n} {n} -(1, 2) {n} {n} (2, 0) {n} {n} (2, 1) {n} {n} +(2, 2) {n} {m, n} +(2, 3) {m, n} {m, n} (3, 0) {n} {n} -(3, 1) {n} {n} -(4, 0) {n} {n} -(4, 1) {n} {n} -(4, 2) {n} {m, n} -(4, 3) {m, n} {m, n} -(5, 0) {n} {n} [case testMultiPass_Liveness] def f(n: int) -> None: @@ -195,67 +182,40 @@ def f(n: int) -> None: [out] def f(n): n, x, y :: int - r0 :: native_int - r1, r2, r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit + r0, r1 :: bit L0: x = 2 y = 2 L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 2 + if r0 goto L2 else goto L6 :: bool L2: - r2 = CPyTagged_IsLt_(n, 2) - if r2 goto L4 else goto L10 :: bool + n = y L3: - r3 = n < 2 :: signed - if r3 goto L4 else goto L10 :: bool + r1 = int_lt n, 4 + if r1 goto L4 else goto L5 :: bool L4: - n = y -L5: - r4 = n & 1 - r5 = r4 != 0 - if r5 goto L6 else goto L7 :: bool -L6: - r6 = CPyTagged_IsLt_(n, 4) - if r6 goto L8 else goto L9 :: bool -L7: - r7 = n < 4 :: signed - if r7 goto L8 else goto L9 :: bool -L8: n = 2 n = x - goto L5 -L9: + goto L3 +L5: goto L1 -L10: +L6: return 1 (0, 0) {n} {n, x} (0, 1) {n, x} {n, x, y} (0, 2) {n, x, y} {n, x, y} -(1, 0) {n, x, y} {n, r0, x, y} -(1, 1) {n, r0, x, y} {n, r1, x, y} -(1, 2) {n, r1, x, y} {n, x, y} -(2, 0) {n, x, y} {r2, x, y} -(2, 1) {r2, x, y} {x, y} -(3, 0) {n, x, y} {r3, x, y} -(3, 1) {r3, x, y} {x, y} -(4, 0) {x, y} {n, x, y} -(4, 1) {n, x, y} {n, x, y} -(5, 0) {n, x, y} {n, r4, x, y} -(5, 1) {n, r4, x, y} {n, r5, x, y} -(5, 2) {n, r5, x, y} {n, x, y} -(6, 0) {n, x, y} {n, r6, x, y} -(6, 1) {n, r6, x, y} {n, x, y} -(7, 0) {n, x, y} {n, r7, x, y} -(7, 1) {n, r7, x, y} {n, x, y} -(8, 0) {x, y} {x, y} -(8, 1) {x, y} {n, x, y} -(8, 2) {n, x, y} {n, x, y} -(9, 0) {n, x, y} {n, x, y} -(10, 0) {} {} +(1, 0) {n, x, y} {r0, x, y} +(1, 1) {r0, x, y} {x, y} +(2, 0) {x, y} {n, x, y} +(2, 1) {n, x, y} {n, x, y} +(3, 0) {n, x, y} {n, r1, x, y} +(3, 1) {n, r1, x, y} {n, x, y} +(4, 0) {x, y} {x, y} +(4, 1) {x, y} {n, x, y} +(4, 2) {n, x, y} {n, x, y} +(5, 0) {n, x, y} {n, x, y} +(6, 0) {} {} [case testCall_Liveness] def f(x: int) -> int: @@ -296,80 +256,35 @@ def f(a: int) -> None: [out] def f(a): a :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: native_int - r7 :: bit - r8 :: native_int - r9, r10, r11 :: bit + r0, r1 :: bit y, x :: int L0: L1: - r0 = a & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_lt a, a + if r0 goto L2 else goto L6 :: bool L2: - r2 = a & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool L3: - r4 = CPyTagged_IsLt_(a, a) - if r4 goto L5 else goto L12 :: bool + r1 = int_lt a, a + if r1 goto L4 else goto L5 :: bool L4: - r5 = a < a :: signed - if r5 goto L5 else goto L12 :: bool -L5: -L6: - r6 = a & 1 - r7 = r6 != 0 - if r7 goto L8 else goto L7 :: bool -L7: - r8 = a & 1 - r9 = r8 != 0 - if r9 goto L8 else goto L9 :: bool -L8: - r10 = CPyTagged_IsLt_(a, a) - if r10 goto L10 else goto L11 :: bool -L9: - r11 = a < a :: signed - if r11 goto L10 else goto L11 :: bool -L10: y = a - goto L6 -L11: + goto L3 +L5: x = a goto L1 -L12: +L6: return 1 (0, 0) {a} {a} (1, 0) {a, x, y} {a, x, y} (1, 1) {a, x, y} {a, x, y} -(1, 2) {a, x, y} {a, x, y} (2, 0) {a, x, y} {a, x, y} -(2, 1) {a, x, y} {a, x, y} -(2, 2) {a, x, y} {a, x, y} (3, 0) {a, x, y} {a, x, y} (3, 1) {a, x, y} {a, x, y} (4, 0) {a, x, y} {a, x, y} (4, 1) {a, x, y} {a, x, y} (5, 0) {a, x, y} {a, x, y} +(5, 1) {a, x, y} {a, x, y} (6, 0) {a, x, y} {a, x, y} -(6, 1) {a, x, y} {a, x, y} -(6, 2) {a, x, y} {a, x, y} -(7, 0) {a, x, y} {a, x, y} -(7, 1) {a, x, y} {a, x, y} -(7, 2) {a, x, y} {a, x, y} -(8, 0) {a, x, y} {a, x, y} -(8, 1) {a, x, y} {a, x, y} -(9, 0) {a, x, y} {a, x, y} -(9, 1) {a, x, y} {a, x, y} -(10, 0) {a, x, y} {a, x, y} -(10, 1) {a, x, y} {a, x, y} -(11, 0) {a, x, y} {a, x, y} -(11, 1) {a, x, y} {a, x, y} -(12, 0) {a, x, y} {a, x, y} [case testTrivial_BorrowedArgument] def f(a: int, b: int) -> int: @@ -441,55 +356,33 @@ def f(a: int) -> int: [out] def f(a): a, sum, i :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6, r7 :: int + r0 :: bit + r1, r2 :: int L0: sum = 0 i = 0 L1: - r0 = i & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_le i, a + if r0 goto L2 else goto L3 :: bool L2: - r2 = a & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool -L3: - r4 = CPyTagged_IsLt_(a, i) - if r4 goto L6 else goto L5 :: bool -L4: - r5 = i <= a :: signed - if r5 goto L5 else goto L6 :: bool -L5: - r6 = CPyTagged_Add(sum, i) - sum = r6 - r7 = CPyTagged_Add(i, 2) - i = r7 + r1 = CPyTagged_Add(sum, i) + sum = r1 + r2 = CPyTagged_Add(i, 2) + i = r2 goto L1 -L6: +L3: return sum (0, 0) {a} {a} (0, 1) {a} {a} (0, 2) {a} {a} (1, 0) {a} {a} (1, 1) {a} {a} -(1, 2) {a} {a} (2, 0) {a} {a} (2, 1) {a} {a} (2, 2) {a} {a} +(2, 3) {a} {a} +(2, 4) {a} {a} (3, 0) {a} {a} -(3, 1) {a} {a} -(4, 0) {a} {a} -(4, 1) {a} {a} -(5, 0) {a} {a} -(5, 1) {a} {a} -(5, 2) {a} {a} -(5, 3) {a} {a} -(5, 4) {a} {a} -(6, 0) {a} {a} [case testError] def f(x: List[int]) -> None: pass # E: Name "List" is not defined \ diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index ed43b86ebdb4..1ec03dd9a671 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -111,56 +111,42 @@ def sum(a: List[int], l: int) -> int: def sum(a, l): a :: list l, sum, i :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: object - r7, r8, r9, r10 :: int + r0 :: bit + r1 :: object + r2, r3, r4, r5 :: int L0: sum = 0 i = 0 L1: - r0 = i & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_lt i, l + if r0 goto L2 else goto L7 :: bool L2: - r2 = l & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool + r1 = CPyList_GetItemBorrow(a, i) + if is_error(r1) goto L8 (error at sum:6) else goto L3 L3: - r4 = CPyTagged_IsLt_(i, l) - if r4 goto L5 else goto L10 :: bool + r2 = unbox(int, r1) + if is_error(r2) goto L8 (error at sum:6) else goto L4 L4: - r5 = i < l :: signed - if r5 goto L5 else goto L10 :: bool -L5: - r6 = CPyList_GetItemBorrow(a, i) - if is_error(r6) goto L11 (error at sum:6) else goto L6 -L6: - r7 = unbox(int, r6) - if is_error(r7) goto L11 (error at sum:6) else goto L7 -L7: - r8 = CPyTagged_Add(sum, r7) + r3 = CPyTagged_Add(sum, r2) dec_ref sum :: int - dec_ref r7 :: int - sum = r8 - r9 = CPyTagged_Add(i, 2) + dec_ref r2 :: int + sum = r3 + r4 = CPyTagged_Add(i, 2) dec_ref i :: int - i = r9 + i = r4 goto L1 -L8: +L5: return sum -L9: - r10 = :: int - return r10 -L10: +L6: + r5 = :: int + return r5 +L7: dec_ref i :: int - goto L8 -L11: + goto L5 +L8: dec_ref sum :: int dec_ref i :: int - goto L9 + goto L6 [case testTryExcept] def g() -> None: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 981460dae371..164fc213a8a2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -76,27 +76,13 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit + r0 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L1 else goto L2 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L4 else goto L5 :: bool -L3: - r5 = x < y :: signed - if r5 goto L4 else goto L5 :: bool -L4: x = 2 -L5: +L2: return x [case testIfElse] @@ -109,30 +95,16 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit + r0 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L1 else goto L2 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L4 else goto L5 :: bool -L3: - r5 = x < y :: signed - if r5 goto L4 else goto L5 :: bool -L4: x = 2 - goto L6 -L5: + goto L3 +L2: x = 4 -L6: +L3: return x [case testAnd1] @@ -145,48 +117,19 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: native_int - r7 :: bit - r8 :: native_int - r9, r10, r11 :: bit + r0, r1 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L1 else goto L3 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool + r1 = int_gt x, y + if r1 goto L2 else goto L3 :: bool L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L4 else goto L9 :: bool -L3: - r5 = x < y :: signed - if r5 goto L4 else goto L9 :: bool -L4: - r6 = x & 1 - r7 = r6 != 0 - if r7 goto L6 else goto L5 :: bool -L5: - r8 = y & 1 - r9 = r8 != 0 - if r9 goto L6 else goto L7 :: bool -L6: - r10 = CPyTagged_IsLt_(y, x) - if r10 goto L8 else goto L9 :: bool -L7: - r11 = x > y :: signed - if r11 goto L8 else goto L9 :: bool -L8: x = 2 - goto L10 -L9: + goto L4 +L3: x = 4 -L10: +L4: return x [case testAnd2] @@ -221,48 +164,19 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: native_int - r7 :: bit - r8 :: native_int - r9, r10, r11 :: bit + r0, r1 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L2 else goto L1 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool + r1 = int_gt x, y + if r1 goto L2 else goto L3 :: bool L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L8 else goto L4 :: bool -L3: - r5 = x < y :: signed - if r5 goto L8 else goto L4 :: bool -L4: - r6 = x & 1 - r7 = r6 != 0 - if r7 goto L6 else goto L5 :: bool -L5: - r8 = y & 1 - r9 = r8 != 0 - if r9 goto L6 else goto L7 :: bool -L6: - r10 = CPyTagged_IsLt_(y, x) - if r10 goto L8 else goto L9 :: bool -L7: - r11 = x > y :: signed - if r11 goto L8 else goto L9 :: bool -L8: x = 2 - goto L10 -L9: + goto L4 +L3: x = 4 -L10: +L4: return x [case testOr2] @@ -295,27 +209,13 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit + r0 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L2 else goto L1 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L5 else goto L4 :: bool -L3: - r5 = x < y :: signed - if r5 goto L5 else goto L4 :: bool -L4: x = 2 -L5: +L2: return x [case testNotAnd] @@ -326,45 +226,16 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: native_int - r7 :: bit - r8 :: native_int - r9, r10, r11 :: bit + r0, r1 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L1 else goto L2 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool + r1 = int_gt x, y + if r1 goto L3 else goto L2 :: bool L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L4 else goto L8 :: bool -L3: - r5 = x < y :: signed - if r5 goto L4 else goto L8 :: bool -L4: - r6 = x & 1 - r7 = r6 != 0 - if r7 goto L6 else goto L5 :: bool -L5: - r8 = y & 1 - r9 = r8 != 0 - if r9 goto L6 else goto L7 :: bool -L6: - r10 = CPyTagged_IsLt_(y, x) - if r10 goto L9 else goto L8 :: bool -L7: - r11 = x > y :: signed - if r11 goto L9 else goto L8 :: bool -L8: x = 2 -L9: +L3: return x [case testWhile] @@ -375,31 +246,17 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: int + r0 :: bit + r1 :: int L0: L1: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_gt x, y + if r0 goto L2 else goto L3 :: bool L2: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool -L3: - r4 = CPyTagged_IsLt_(y, x) - if r4 goto L5 else goto L6 :: bool -L4: - r5 = x > y :: signed - if r5 goto L5 else goto L6 :: bool -L5: - r6 = CPyTagged_Subtract(x, y) - x = r6 + r1 = CPyTagged_Subtract(x, y) + x = r1 goto L1 -L6: +L3: return x [case testWhile2] @@ -411,32 +268,18 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: int + r0 :: bit + r1 :: int L0: x = 2 L1: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_gt x, y + if r0 goto L2 else goto L3 :: bool L2: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool -L3: - r4 = CPyTagged_IsLt_(y, x) - if r4 goto L5 else goto L6 :: bool -L4: - r5 = x > y :: signed - if r5 goto L5 else goto L6 :: bool -L5: - r6 = CPyTagged_Subtract(x, y) - x = r6 + r1 = CPyTagged_Subtract(x, y) + x = r1 goto L1 -L6: +L3: return x [case testImplicitNoneReturn] @@ -466,30 +309,16 @@ def f(x: int, y: int) -> None: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit + r0 :: bit L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L1 :: bool + r0 = int_lt x, y + if r0 goto L1 else goto L2 :: bool L1: - r2 = y & 1 - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPyTagged_IsLt_(x, y) - if r4 goto L4 else goto L5 :: bool -L3: - r5 = x < y :: signed - if r5 goto L4 else goto L5 :: bool -L4: x = 2 - goto L6 -L5: + goto L3 +L2: y = 4 -L6: +L3: return 1 [case testRecursion] @@ -501,29 +330,21 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: native_int - r1, r2, r3 :: bit - r4, r5, r6, r7, r8 :: int + r0 :: bit + r1, r2, r3, r4, r5 :: int L0: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_le n, 2 + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsLt_(2, n) - if r2 goto L4 else goto L3 :: bool + return 2 L2: - r3 = n <= 2 :: signed - if r3 goto L3 else goto L4 :: bool + r1 = CPyTagged_Subtract(n, 2) + r2 = f(r1) + r3 = CPyTagged_Subtract(n, 4) + r4 = f(r3) + r5 = CPyTagged_Add(r2, r4) + return r5 L3: - return 2 -L4: - r4 = CPyTagged_Subtract(n, 2) - r5 = f(r4) - r6 = CPyTagged_Subtract(n, 4) - r7 = f(r6) - r8 = CPyTagged_Add(r5, r7) - return r8 -L5: unreachable [case testReportTypeCheckError] @@ -550,33 +371,25 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit x :: int - r4 :: bit + r1 :: bit L0: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_lt n, 0 + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsLt_(n, 0) - if r2 goto L3 else goto L4 :: bool + x = 2 + goto L6 L2: - r3 = n < 0 :: signed - if r3 goto L3 else goto L4 :: bool + r1 = int_eq n, 0 + if r1 goto L3 else goto L4 :: bool L3: x = 2 - goto L8 + goto L5 L4: - r4 = int_eq n, 0 - if r4 goto L5 else goto L6 :: bool + x = 4 L5: - x = 2 - goto L7 L6: - x = 4 -L7: -L8: return x [case testUnaryMinus] @@ -1272,27 +1085,19 @@ def f(x: int) -> int: [out] def absolute_value(x): x :: int - r0 :: native_int - r1, r2, r3 :: bit - r4, r5 :: int + r0 :: bit + r1, r2 :: int L0: - r0 = x & 1 - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = int_gt x, 0 + if r0 goto L1 else goto L2 :: bool L1: - r2 = CPyTagged_IsLt_(0, x) - if r2 goto L3 else goto L4 :: bool + r1 = x + goto L3 L2: - r3 = x > 0 :: signed - if r3 goto L3 else goto L4 :: bool + r2 = CPyTagged_Negate(x) + r1 = r2 L3: - r4 = x - goto L5 -L4: - r5 = CPyTagged_Negate(x) - r4 = r5 -L5: - return r4 + return r1 def call_native_function(x): x, r0 :: int L0: @@ -2078,7 +1883,7 @@ L1: r11 = load_mem r10 :: native_int* keep_alive r1 r12 = r11 << 1 - r13 = r9 < r12 :: signed + r13 = int_lt r9, r12 if r13 goto L2 else goto L8 :: bool L2: r14 = CPyList_GetItemUnsafe(r1, r9) @@ -2148,7 +1953,7 @@ L1: r11 = load_mem r10 :: native_int* keep_alive r1 r12 = r11 << 1 - r13 = r9 < r12 :: signed + r13 = int_lt r9, r12 if r13 goto L2 else goto L8 :: bool L2: r14 = CPyList_GetItemUnsafe(r1, r9) @@ -2215,7 +2020,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive l r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItemUnsafe(l, r0) @@ -2241,7 +2046,7 @@ L5: r16 = load_mem r15 :: native_int* keep_alive l r17 = r16 << 1 - r18 = r14 < r17 :: signed + r18 = int_lt r14, r17 if r18 goto L6 else goto L8 :: bool L6: r19 = CPyList_GetItemUnsafe(l, r14) @@ -2504,60 +2309,24 @@ L0: return x def f(x, y, z): x, y, z, r0, r1 :: int - r2 :: native_int - r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit - r8 :: bool - r9 :: bit - r10 :: bool - r11 :: int - r12 :: native_int - r13 :: bit - r14 :: native_int - r15, r16, r17 :: bit - r18 :: bool - r19 :: bit + r2 :: bit + r3 :: bool + r4 :: int + r5 :: bit L0: r0 = g(x) r1 = g(y) - r2 = r0 & 1 - r3 = r2 == 0 - r4 = r1 & 1 - r5 = r4 == 0 - r6 = r3 & r5 - if r6 goto L1 else goto L2 :: bool + r2 = int_lt r0, r1 + if r2 goto L2 else goto L1 :: bool L1: - r7 = r0 < r1 :: signed - r8 = r7 + r3 = r2 goto L3 L2: - r9 = CPyTagged_IsLt_(r0, r1) - r8 = r9 + r4 = g(z) + r5 = int_gt r1, r4 + r3 = r5 L3: - if r8 goto L5 else goto L4 :: bool -L4: - r10 = r8 - goto L9 -L5: - r11 = g(z) - r12 = r1 & 1 - r13 = r12 == 0 - r14 = r11 & 1 - r15 = r14 == 0 - r16 = r13 & r15 - if r16 goto L6 else goto L7 :: bool -L6: - r17 = r1 > r11 :: signed - r18 = r17 - goto L8 -L7: - r19 = CPyTagged_IsLt_(r11, r1) - r18 = r19 -L8: - r10 = r18 -L9: - return r10 + return r3 [case testEq] class A: @@ -3577,7 +3346,7 @@ L0: r0 = 8 i = r0 L1: - r1 = r0 < 24 :: signed + r1 = int_lt r0, 24 if r1 goto L2 else goto L4 :: bool L2: r2 = CPyTagged_Add(sum, i) diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index f0b0b480bc0d..795a3360fcd2 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -272,59 +272,23 @@ def lt1(x, y): x :: bool y :: int r0 :: bool - r1 :: short_int - r2 :: native_int - r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit - r8 :: bool - r9 :: bit + r1 :: int + r2 :: bit L0: r0 = x << 1 - r1 = extend r0: builtins.bool to short_int - r2 = r1 & 1 - r3 = r2 == 0 - r4 = y & 1 - r5 = r4 == 0 - r6 = r3 & r5 - if r6 goto L1 else goto L2 :: bool -L1: - r7 = r1 < y :: signed - r8 = r7 - goto L3 -L2: - r9 = CPyTagged_IsLt_(r1, y) - r8 = r9 -L3: - return r8 + r1 = extend r0: builtins.bool to builtins.int + r2 = int_lt r1, y + return r2 def lt2(x, y): x :: int y, r0 :: bool - r1 :: short_int - r2 :: native_int - r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit - r8 :: bool - r9 :: bit + r1 :: int + r2 :: bit L0: r0 = y << 1 - r1 = extend r0: builtins.bool to short_int - r2 = x & 1 - r3 = r2 == 0 - r4 = r1 & 1 - r5 = r4 == 0 - r6 = r3 & r5 - if r6 goto L1 else goto L2 :: bool -L1: - r7 = x < r1 :: signed - r8 = r7 - goto L3 -L2: - r9 = CPyTagged_IsLt_(x, r1) - r8 = r9 -L3: - return r8 + r1 = extend r0: builtins.bool to builtins.int + r2 = int_lt x, r1 + return r2 def gt1(x, y): x :: bool y, r0 :: i64 diff --git a/mypyc/test-data/irbuild-dunders.test b/mypyc/test-data/irbuild-dunders.test index b50b6eeae162..1796a7e2160e 100644 --- a/mypyc/test-data/irbuild-dunders.test +++ b/mypyc/test-data/irbuild-dunders.test @@ -15,24 +15,16 @@ L0: def f(c): c :: __main__.C r0 :: int - r1 :: native_int - r2, r3, r4 :: bit - r5 :: bool + r1 :: bit + r2 :: bool L0: r0 = c.__len__() - r1 = r0 & 1 - r2 = r1 != 0 - if r2 goto L1 else goto L2 :: bool + r1 = int_ge r0, 0 + if r1 goto L2 else goto L1 :: bool L1: - r3 = CPyTagged_IsLt_(r0, 0) - if r3 goto L3 else goto L4 :: bool -L2: - r4 = r0 >= 0 :: signed - if r4 goto L4 else goto L3 :: bool -L3: - r5 = raise ValueError('__len__() should return >= 0') + r2 = raise ValueError('__len__() should return >= 0') unreachable -L4: +L2: return r0 [case testDundersSetItem] diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 1489f2f470dd..b1a712103e70 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -25,9 +25,7 @@ def f(x: int) -> int: [out] def f(x): x :: int - r0, r1, r2, r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit + r0, r1, r2, r3, r4 :: bit L0: r0 = int_eq x, 6 if r0 goto L1 else goto L2 :: bool @@ -49,22 +47,15 @@ L6: L7: return 8 L8: - r4 = x & 1 - r5 = r4 != 0 - if r5 goto L9 else goto L10 :: bool + r4 = int_lt x, 8 + if r4 goto L9 else goto L10 :: bool L9: - r6 = CPyTagged_IsLt_(x, 8) - if r6 goto L11 else goto L12 :: bool + return 10 L10: - r7 = x < 8 :: signed - if r7 goto L11 else goto L12 :: bool L11: - return 10 L12: L13: L14: -L15: -L16: return 12 [case testIntMin] @@ -73,36 +64,18 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6 :: bool - r7 :: bit - r8 :: int -L0: - r0 = y & 1 - r1 = r0 == 0 - r2 = x & 1 - r3 = r2 == 0 - r4 = r1 & r3 - if r4 goto L1 else goto L2 :: bool + r0 :: bit + r1 :: int +L0: + r0 = int_lt y, x + if r0 goto L1 else goto L2 :: bool L1: - r5 = y < x :: signed - r6 = r5 + r1 = y goto L3 L2: - r7 = CPyTagged_IsLt_(y, x) - r6 = r7 + r1 = x L3: - if r6 goto L4 else goto L5 :: bool -L4: - r8 = y - goto L6 -L5: - r8 = x -L6: - return r8 + return r1 [case testIntFloorDivideByPowerOfTwo] def divby1(x: int) -> int: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 80c4fe5fcd5e..ced4646922a3 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -230,7 +230,7 @@ L0: r3 = 0 i = r3 L1: - r4 = r3 < r2 :: signed + r4 = int_lt r3, r2 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItem(l, i) @@ -357,7 +357,7 @@ L1: r5 = load_mem r4 :: native_int* keep_alive source r6 = r5 << 1 - r7 = r3 < r6 :: signed + r7 = int_lt r3, r6 if r7 goto L2 else goto L4 :: bool L2: r8 = CPyList_GetItemUnsafe(source, r3) @@ -382,7 +382,7 @@ L5: r19 = load_mem r18 :: native_int* keep_alive source r20 = r19 << 1 - r21 = r17 < r20 :: signed + r21 = int_lt r17, r20 if r21 goto L6 else goto L8 :: bool L6: r22 = CPyList_GetItemUnsafe(source, r17) @@ -398,6 +398,7 @@ L7: L8: b = r16 return 1 + [case testGeneratorNext] from typing import List, Optional @@ -425,7 +426,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive x r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItemUnsafe(x, r0) @@ -504,7 +505,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive a r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItemUnsafe(a, r0) @@ -533,7 +534,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive a r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItemUnsafe(a, r0) diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index a56ebe3438fa..51feab332593 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -115,7 +115,7 @@ L1: r11 = load_mem r10 :: native_int* keep_alive tmp_list r12 = r11 << 1 - r13 = r9 < r12 :: signed + r13 = int_lt r9, r12 if r13 goto L2 else goto L4 :: bool L2: r14 = CPyList_GetItemUnsafe(tmp_list, r9) @@ -234,7 +234,7 @@ L0: r1 = 2 x = r1 L1: - r2 = r1 < 12 :: signed + r2 = int_lt r1, 12 if r2 goto L2 else goto L4 :: bool L2: r3 = f(x) @@ -265,7 +265,7 @@ L0: r1 = 2 x = r1 L1: - r2 = r1 < 12 :: signed + r2 = int_lt r1, 12 if r2 goto L2 else goto L4 :: bool L2: r3 = f(x) @@ -323,27 +323,22 @@ def test(): r19 :: bit r20 :: object r21, z :: int - r22 :: native_int - r23 :: bit - r24 :: native_int - r25, r26, r27 :: bit - r28 :: bool - r29 :: bit - r30 :: int - r31 :: object - r32 :: i32 - r33 :: bit - r34 :: short_int - r35, r36, r37 :: object - r38, y, r39 :: int - r40 :: object - r41 :: i32 - r42, r43 :: bit - r44, r45, r46 :: object - r47, x, r48 :: int - r49 :: object - r50 :: i32 - r51, r52 :: bit + r22 :: bit + r23 :: int + r24 :: object + r25 :: i32 + r26 :: bit + r27 :: short_int + r28, r29, r30 :: object + r31, y, r32 :: int + r33 :: object + r34 :: i32 + r35, r36 :: bit + r37, r38, r39 :: object + r40, x, r41 :: int + r42 :: object + r43 :: i32 + r44, r45 :: bit a :: set L0: r0 = PyList_New(5) @@ -374,73 +369,60 @@ L1: r17 = load_mem r16 :: native_int* keep_alive tmp_list r18 = r17 << 1 - r19 = r15 < r18 :: signed - if r19 goto L2 else goto L9 :: bool + r19 = int_lt r15, r18 + if r19 goto L2 else goto L6 :: bool L2: r20 = CPyList_GetItemUnsafe(tmp_list, r15) r21 = unbox(int, r20) z = r21 - r22 = z & 1 - r23 = r22 == 0 - r24 = 8 & 1 - r25 = r24 == 0 - r26 = r23 & r25 - if r26 goto L3 else goto L4 :: bool + r22 = int_lt z, 8 + if r22 goto L4 else goto L3 :: bool L3: - r27 = z < 8 :: signed - r28 = r27 goto L5 L4: - r29 = CPyTagged_IsLt_(z, 8) - r28 = r29 + r23 = f1(z) + r24 = box(int, r23) + r25 = PyList_Append(r14, r24) + r26 = r25 >= 0 :: signed L5: - if r28 goto L7 else goto L6 :: bool + r27 = r15 + 2 + r15 = r27 + goto L1 L6: - goto L8 + r28 = PyObject_GetIter(r14) + r29 = PyObject_GetIter(r28) L7: - r30 = f1(z) - r31 = box(int, r30) - r32 = PyList_Append(r14, r31) - r33 = r32 >= 0 :: signed + r30 = PyIter_Next(r29) + if is_error(r30) goto L10 else goto L8 L8: - r34 = r15 + 2 - r15 = r34 - goto L1 + r31 = unbox(int, r30) + y = r31 + r32 = f2(y) + r33 = box(int, r32) + r34 = PyList_Append(r13, r33) + r35 = r34 >= 0 :: signed L9: - r35 = PyObject_GetIter(r14) - r36 = PyObject_GetIter(r35) + goto L7 L10: - r37 = PyIter_Next(r36) - if is_error(r37) goto L13 else goto L11 + r36 = CPy_NoErrOccured() L11: - r38 = unbox(int, r37) - y = r38 - r39 = f2(y) - r40 = box(int, r39) - r41 = PyList_Append(r13, r40) - r42 = r41 >= 0 :: signed + r37 = PyObject_GetIter(r13) + r38 = PyObject_GetIter(r37) L12: - goto L10 + r39 = PyIter_Next(r38) + if is_error(r39) goto L15 else goto L13 L13: - r43 = CPy_NoErrOccured() + r40 = unbox(int, r39) + x = r40 + r41 = f3(x) + r42 = box(int, r41) + r43 = PySet_Add(r12, r42) + r44 = r43 >= 0 :: signed L14: - r44 = PyObject_GetIter(r13) - r45 = PyObject_GetIter(r44) + goto L12 L15: - r46 = PyIter_Next(r45) - if is_error(r46) goto L18 else goto L16 + r45 = CPy_NoErrOccured() L16: - r47 = unbox(int, r46) - x = r47 - r48 = f3(x) - r49 = box(int, r48) - r50 = PySet_Add(r12, r49) - r51 = r50 >= 0 :: signed -L17: - goto L15 -L18: - r52 = CPy_NoErrOccured() -L19: a = r12 return 1 diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test index 10970a385966..e1053397546f 100644 --- a/mypyc/test-data/irbuild-singledispatch.test +++ b/mypyc/test-data/irbuild-singledispatch.test @@ -81,7 +81,7 @@ L3: if r17 goto L4 else goto L7 :: bool L4: r18 = unbox(int, r6) - r19 = r18 == 0 + r19 = int_eq r18, 0 if r19 goto L5 else goto L6 :: bool L5: r20 = unbox(int, arg) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index b7c67730a05f..ed97c4cd4138 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -16,7 +16,7 @@ L0: r0 = 0 i = r0 L1: - r1 = r0 < 10 :: signed + r1 = int_lt r0, 10 if r1 goto L2 else goto L4 :: bool L2: r2 = CPyTagged_Add(x, i) @@ -36,39 +36,21 @@ def f(a: int) -> None: [out] def f(a): a, r0, i :: int - r1 :: native_int - r2 :: bit - r3 :: native_int - r4, r5, r6 :: bit - r7 :: bool - r8 :: bit - r9 :: int + r1 :: bit + r2 :: int L0: r0 = 0 i = r0 L1: - r1 = r0 & 1 - r2 = r1 == 0 - r3 = a & 1 - r4 = r3 == 0 - r5 = r2 & r4 - if r5 goto L2 else goto L3 :: bool + r1 = int_lt r0, a + if r1 goto L2 else goto L4 :: bool L2: - r6 = r0 < a :: signed - r7 = r6 - goto L4 L3: - r8 = CPyTagged_IsLt_(r0, a) - r7 = r8 -L4: - if r7 goto L5 else goto L7 :: bool -L5: -L6: - r9 = CPyTagged_Add(r0, 2) - r0 = r9 - i = r9 + r2 = CPyTagged_Add(r0, 2) + r0 = r2 + i = r2 goto L1 -L7: +L4: return 1 [case testForInNegativeRange] @@ -85,7 +67,7 @@ L0: r0 = 20 i = r0 L1: - r1 = r0 > 0 :: signed + r1 = int_gt r0, 0 if r1 goto L2 else goto L4 :: bool L2: L3: @@ -104,22 +86,14 @@ def f() -> None: [out] def f(): n :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit L0: n = 0 L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 10 + if r0 goto L2 else goto L3 :: bool L2: - r2 = CPyTagged_IsLt_(n, 10) - if r2 goto L4 else goto L5 :: bool L3: - r3 = n < 10 :: signed - if r3 goto L4 else goto L5 :: bool -L4: -L5: return 1 [case testBreakFor] @@ -136,7 +110,7 @@ L0: r0 = 0 n = r0 L1: - r1 = r0 < 10 :: signed + r1 = int_lt r0, 10 if r1 goto L2 else goto L4 :: bool L2: goto L4 @@ -158,36 +132,19 @@ def f() -> None: [out] def f(): n :: int - r0 :: native_int - r1, r2, r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit + r0, r1 :: bit L0: n = 0 L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 10 + if r0 goto L2 else goto L6 :: bool L2: - r2 = CPyTagged_IsLt_(n, 10) - if r2 goto L4 else goto L10 :: bool L3: - r3 = n < 10 :: signed - if r3 goto L4 else goto L10 :: bool + r1 = int_lt n, 8 + if r1 goto L4 else goto L5 :: bool L4: L5: - r4 = n & 1 - r5 = r4 != 0 - if r5 goto L6 else goto L7 :: bool L6: - r6 = CPyTagged_IsLt_(n, 8) - if r6 goto L8 else goto L9 :: bool -L7: - r7 = n < 8 :: signed - if r7 goto L8 else goto L9 :: bool -L8: -L9: -L10: return 1 [case testContinue] @@ -198,23 +155,15 @@ def f() -> None: [out] def f(): n :: int - r0 :: native_int - r1, r2, r3 :: bit + r0 :: bit L0: n = 0 L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 10 + if r0 goto L2 else goto L3 :: bool L2: - r2 = CPyTagged_IsLt_(n, 10) - if r2 goto L4 else goto L5 :: bool -L3: - r3 = n < 10 :: signed - if r3 goto L4 else goto L5 :: bool -L4: goto L1 -L5: +L3: return 1 [case testContinueFor] @@ -231,7 +180,7 @@ L0: r0 = 0 n = r0 L1: - r1 = r0 < 10 :: signed + r1 = int_lt r0, 10 if r1 goto L2 else goto L4 :: bool L2: L3: @@ -252,38 +201,21 @@ def f() -> None: [out] def f(): n :: int - r0 :: native_int - r1, r2, r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit + r0, r1 :: bit L0: n = 0 L1: - r0 = n & 1 - r1 = r0 != 0 - if r1 goto L2 else goto L3 :: bool + r0 = int_lt n, 10 + if r0 goto L2 else goto L6 :: bool L2: - r2 = CPyTagged_IsLt_(n, 10) - if r2 goto L4 else goto L10 :: bool L3: - r3 = n < 10 :: signed - if r3 goto L4 else goto L10 :: bool + r1 = int_lt n, 8 + if r1 goto L4 else goto L5 :: bool L4: + goto L3 L5: - r4 = n & 1 - r5 = r4 != 0 - if r5 goto L6 else goto L7 :: bool -L6: - r6 = CPyTagged_IsLt_(n, 8) - if r6 goto L8 else goto L9 :: bool -L7: - r7 = n < 8 :: signed - if r7 goto L8 else goto L9 :: bool -L8: - goto L5 -L9: goto L1 -L10: +L6: return 1 [case testForList] @@ -314,7 +246,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive ls r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyList_GetItemUnsafe(ls, r0) @@ -963,7 +895,7 @@ L1: r3 = load_mem r2 :: native_int* keep_alive a r4 = r3 << 1 - r5 = r1 < r4 :: signed + r5 = int_lt r1, r4 if r5 goto L2 else goto L4 :: bool L2: r6 = CPyList_GetItemUnsafe(a, r1) @@ -1045,7 +977,7 @@ L1: r3 = load_mem r2 :: native_int* keep_alive a r4 = r3 << 1 - r5 = r0 < r4 :: signed + r5 = int_lt r0, r4 if r5 goto L2 else goto L7 :: bool L2: r6 = PyIter_Next(r1) @@ -1100,10 +1032,10 @@ L2: r5 = load_mem r4 :: native_int* keep_alive b r6 = r5 << 1 - r7 = r1 < r6 :: signed + r7 = int_lt r1, r6 if r7 goto L3 else goto L6 :: bool L3: - r8 = r2 < 10 :: signed + r8 = int_lt r2, 10 if r8 goto L4 else goto L6 :: bool L4: r9 = unbox(bool, r3) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index ab0e2fa09a9d..342bb19b5360 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -147,7 +147,7 @@ L1: r2 = load_mem r1 :: native_int* keep_alive xs r3 = r2 << 1 - r4 = r0 < r3 :: signed + r4 = int_lt r0, r3 if r4 goto L2 else goto L4 :: bool L2: r5 = CPySequenceTuple_GetItem(xs, r0) @@ -279,7 +279,7 @@ L1: r13 = load_mem r12 :: native_int* keep_alive source r14 = r13 << 1 - r15 = r11 < r14 :: signed + r15 = int_lt r11, r14 if r15 goto L2 else goto L4 :: bool L2: r16 = CPyList_GetItemUnsafe(source, r11) @@ -335,7 +335,7 @@ L1: r5 = CPyStr_Size_size_t(source) r6 = r5 >= 0 :: signed r7 = r5 << 1 - r8 = r4 < r7 :: signed + r8 = int_lt r4, r7 if r8 goto L2 else goto L4 :: bool L2: r9 = CPyStr_GetItem(source, r4) @@ -391,7 +391,7 @@ L1: r5 = load_mem r4 :: native_int* keep_alive source r6 = r5 << 1 - r7 = r3 < r6 :: signed + r7 = int_lt r3, r6 if r7 goto L2 else goto L4 :: bool L2: r8 = CPySequenceTuple_GetItem(source, r3) diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test index 8c813563d0e6..e7df944c4458 100644 --- a/mypyc/test-data/lowering-int.test +++ b/mypyc/test-data/lowering-int.test @@ -13,13 +13,13 @@ def f(x, y): r1, r2, r3 :: bit L0: r0 = x & 1 - r1 = r0 == 0 + r1 = r0 != 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = x == y + r2 = CPyTagged_IsEq_(x, y) if r2 goto L3 else goto L4 :: bool L2: - r3 = CPyTagged_IsEq_(x, y) + r3 = x == y if r3 goto L3 else goto L4 :: bool L3: return 2 @@ -39,14 +39,14 @@ def f(x, y): r1, r2, r3, r4 :: bit L0: r0 = x & 1 - r1 = r0 == 0 + r1 = r0 != 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = x != y - if r2 goto L3 else goto L4 :: bool + r2 = CPyTagged_IsEq_(x, y) + r3 = r2 ^ 1 + if r3 goto L3 else goto L4 :: bool L2: - r3 = CPyTagged_IsEq_(x, y) - r4 = r3 ^ 1 + r4 = x != y if r4 goto L3 else goto L4 :: bool L3: return 2 @@ -113,14 +113,265 @@ def f(x, y): r4 :: bit L0: r0 = x & 1 - r1 = r0 == 0 + r1 = r0 != 0 if r1 goto L1 else goto L2 :: bool L1: - r2 = x == y + r2 = CPyTagged_IsEq_(x, y) r3 = r2 goto L3 L2: - r4 = CPyTagged_IsEq_(x, y) + r4 = x == y r3 = r4 L3: return r3 + +[case testLowerIntLt] +def f(x: int, y: int) -> int: + if x < y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5 :: bit +L0: + r0 = x & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = y & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(x, y) + if r4 goto L4 else goto L5 :: bool +L3: + r5 = x < y :: signed + if r5 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testLowerIntLe] +def f(x: int, y: int) -> int: + if x <= y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5, r6 :: bit +L0: + r0 = x & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = y & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(y, x) + r5 = r4 ^ 1 + if r5 goto L4 else goto L5 :: bool +L3: + r6 = x <= y :: signed + if r6 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testLowerIntGt] +def f(x: int, y: int) -> int: + if x > y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5 :: bit +L0: + r0 = x & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = y & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(y, x) + if r4 goto L4 else goto L5 :: bool +L3: + r5 = x > y :: signed + if r5 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testLowerIntGe] +def f(x: int, y: int) -> int: + if x >= y: + return 1 + else: + return 2 +[out] +def f(x, y): + x, y :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5, r6 :: bit +L0: + r0 = x & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = y & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(x, y) + r5 = r4 ^ 1 + if r5 goto L4 else goto L5 :: bool +L3: + r6 = x >= y :: signed + if r6 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testLowerIntLtShort] +def both() -> int: + if 3 < 5: + return 1 + else: + return 2 + +def rhs_only(x: int) -> int: + if x < 5: + return 1 + else: + return 2 + +def lhs_only(x: int) -> int: + if 5 < x: + return 1 + else: + return 2 +[out] +def both(): + r0 :: bit +L0: + r0 = 6 < 10 :: signed + if r0 goto L1 else goto L2 :: bool +L1: + return 2 +L2: + return 4 +def rhs_only(x): + x :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5 :: bit +L0: + r0 = x & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = 10 & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(x, 10) + if r4 goto L4 else goto L5 :: bool +L3: + r5 = x < 10 :: signed + if r5 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 +def lhs_only(x): + x :: int + r0 :: native_int + r1 :: bit + r2 :: native_int + r3, r4, r5 :: bit +L0: + r0 = 10 & 1 + r1 = r0 != 0 + if r1 goto L2 else goto L1 :: bool +L1: + r2 = x & 1 + r3 = r2 != 0 + if r3 goto L2 else goto L3 :: bool +L2: + r4 = CPyTagged_IsLt_(10, x) + if r4 goto L4 else goto L5 :: bool +L3: + r5 = 10 < x :: signed + if r5 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 4 + +[case testLowerIntForLoop] +from __future__ import annotations + +def f(l: list[int]) -> None: + for x in l: + pass +[out] +def f(l): + l :: list + r0 :: short_int + r1 :: ptr + r2 :: native_int + r3 :: short_int + r4 :: bit + r5 :: object + r6, x :: int + r7 :: short_int + r8 :: None +L0: + r0 = 0 +L1: + r1 = get_element_ptr l ob_size :: PyVarObject + r2 = load_mem r1 :: native_int* + r3 = r2 << 1 + r4 = r0 < r3 :: signed + if r4 goto L2 else goto L5 :: bool +L2: + r5 = CPyList_GetItemUnsafe(l, r0) + r6 = unbox(int, r5) + dec_ref r5 + if is_error(r6) goto L6 (error at f:4) else goto L3 +L3: + x = r6 + dec_ref x :: int +L4: + r7 = r0 + 2 + r0 = r7 + goto L1 +L5: + return 1 +L6: + r8 = :: None + return r8 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index df980af8a7c7..3021381abded 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -452,41 +452,27 @@ def f(a: int) -> int: [out] def f(a): a, sum, i :: int - r0 :: native_int - r1 :: bit - r2 :: native_int - r3, r4, r5 :: bit - r6, r7 :: int + r0 :: bit + r1, r2 :: int L0: sum = 0 i = 0 L1: - r0 = i & 1 - r1 = r0 != 0 - if r1 goto L3 else goto L2 :: bool + r0 = int_le i, a + if r0 goto L2 else goto L4 :: bool L2: - r2 = a & 1 - r3 = r2 != 0 - if r3 goto L3 else goto L4 :: bool -L3: - r4 = CPyTagged_IsLt_(a, i) - if r4 goto L7 else goto L5 :: bool -L4: - r5 = i <= a :: signed - if r5 goto L5 else goto L7 :: bool -L5: - r6 = CPyTagged_Add(sum, i) + r1 = CPyTagged_Add(sum, i) dec_ref sum :: int - sum = r6 - r7 = CPyTagged_Add(i, 2) + sum = r1 + r2 = CPyTagged_Add(i, 2) dec_ref i :: int - i = r7 + i = r2 goto L1 -L6: +L3: return sum -L7: +L4: dec_ref i :: int - goto L6 + goto L3 [case testCall] def f(a: int) -> int: @@ -1357,30 +1343,12 @@ class C: def add(c): c :: __main__.C r0, r1 :: int - r2 :: native_int - r3 :: bit - r4 :: native_int - r5, r6, r7 :: bit - r8 :: bool - r9 :: bit + r2 :: bit L0: r0 = borrow c.x r1 = borrow c.y - r2 = r0 & 1 - r3 = r2 == 0 - r4 = r1 & 1 - r5 = r4 == 0 - r6 = r3 & r5 - if r6 goto L1 else goto L2 :: bool -L1: - r7 = r0 < r1 :: signed - r8 = r7 - goto L3 -L2: - r9 = CPyTagged_IsLt_(r0, r1) - r8 = r9 -L3: - return r8 + r2 = int_lt r0, r1 + return r2 [case testBorrowIntCompareFinal] from typing_extensions import Final From 952c6162bff63ef0cde9d0944471351130bcc107 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 20 Mar 2024 19:38:06 +0000 Subject: [PATCH 0543/1617] [mypyc] Refactor: move tagged int related code to mypyc.lower.int_ops (#17052) --- mypyc/irbuild/expression.py | 5 +- mypyc/irbuild/ll_builder.py | 45 ------------------ mypyc/lower/int_ops.py | 92 ++++++++++++++++++++++++++++++++++--- mypyc/primitives/int_ops.py | 28 ----------- 4 files changed, 87 insertions(+), 83 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index ba62d71d0ad3..a16faf6cd7d7 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -95,7 +95,6 @@ from mypyc.primitives.bytes_ops import bytes_slice_op from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, dict_set_item_op from mypyc.primitives.generic_ops import iter_op -from mypyc.primitives.int_ops import int_comparison_op_mapping from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op from mypyc.primitives.registry import CFunctionDescription, builtin_names @@ -814,7 +813,7 @@ def translate_is_none(builder: IRBuilder, expr: Expression, negated: bool) -> Va def transform_basic_comparison( builder: IRBuilder, op: str, left: Value, right: Value, line: int ) -> Value: - if is_fixed_width_rtype(left.type) and op in int_comparison_op_mapping: + if is_fixed_width_rtype(left.type) and op in ComparisonOp.signed_ops: if right.type == left.type: if left.type.is_signed: op_id = ComparisonOp.signed_ops[op] @@ -831,7 +830,7 @@ def transform_basic_comparison( ) elif ( is_fixed_width_rtype(right.type) - and op in int_comparison_op_mapping + and op in ComparisonOp.signed_ops and isinstance(left, Integer) ): if right.type.is_signed: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 548b391030fe..e989471ceb59 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -159,7 +159,6 @@ int64_divide_op, int64_mod_op, int64_to_int_op, - int_comparison_op_mapping, int_to_int32_op, int_to_int64_op, ssize_t_to_int_op, @@ -1413,50 +1412,6 @@ def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) - check = self.comparison_op(bitwise_and, zero, op, line) return check - def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: - """Compare two tagged integers using given operator (value context).""" - # generate fast binary logic ops on short ints - if (is_short_int_rprimitive(lhs.type) or is_short_int_rprimitive(rhs.type)) and op in ( - "==", - "!=", - ): - quick = True - else: - quick = is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type) - if quick: - return self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) - op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] - result = Register(bool_rprimitive) - short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() - check_lhs = self.check_tagged_short_int(lhs, line, negated=True) - if op in ("==", "!="): - self.add(Branch(check_lhs, int_block, short_int_block, Branch.BOOL)) - else: - # for non-equality logical ops (less/greater than, etc.), need to check both sides - short_lhs = BasicBlock() - self.add(Branch(check_lhs, int_block, short_lhs, Branch.BOOL)) - self.activate_block(short_lhs) - check_rhs = self.check_tagged_short_int(rhs, line, negated=True) - self.add(Branch(check_rhs, int_block, short_int_block, Branch.BOOL)) - self.activate_block(int_block) - if swap_op: - args = [rhs, lhs] - else: - args = [lhs, rhs] - call = self.call_c(c_func_desc, args, line) - if negate_result: - # TODO: introduce UnaryIntOp? - call_result = self.unary_op(call, "not", line) - else: - call_result = call - self.add(Assign(result, call_result, line)) - self.goto(out) - self.activate_block(short_int_block) - eq = self.comparison_op(lhs, rhs, op_type, line) - self.add(Assign(result, eq, line)) - self.goto_and_activate(out) - return result - def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two strings""" compare_result = self.call_c(unicode_compare, [lhs, rhs], line) diff --git a/mypyc/lower/int_ops.py b/mypyc/lower/int_ops.py index 5255a64b647d..90a3253ba093 100644 --- a/mypyc/lower/int_ops.py +++ b/mypyc/lower/int_ops.py @@ -1,35 +1,113 @@ +"""Convert tagged int primitive ops to lower-level ops.""" + from __future__ import annotations -from mypyc.ir.ops import Value +from typing import NamedTuple + +from mypyc.ir.ops import Assign, BasicBlock, Branch, ComparisonOp, Register, Value +from mypyc.ir.rtypes import bool_rprimitive, is_short_int_rprimitive from mypyc.irbuild.ll_builder import LowLevelIRBuilder from mypyc.lower.registry import lower_binary_op +from mypyc.primitives.int_ops import int_equal_, int_less_than_ +from mypyc.primitives.registry import CFunctionDescription + + +# Description for building int comparison ops +# +# Fields: +# binary_op_variant: identify which IntOp to use when operands are short integers +# c_func_description: the C function to call when operands are tagged integers +# c_func_negated: whether to negate the C function call's result +# c_func_swap_operands: whether to swap lhs and rhs when call the function +class IntComparisonOpDescription(NamedTuple): + binary_op_variant: int + c_func_description: CFunctionDescription + c_func_negated: bool + c_func_swap_operands: bool + + +# Provide mapping from textual op to short int's op variant and boxed int's description. +# Note that these are not complete implementations and require extra IR. +int_comparison_op_mapping: dict[str, IntComparisonOpDescription] = { + "==": IntComparisonOpDescription(ComparisonOp.EQ, int_equal_, False, False), + "!=": IntComparisonOpDescription(ComparisonOp.NEQ, int_equal_, True, False), + "<": IntComparisonOpDescription(ComparisonOp.SLT, int_less_than_, False, False), + "<=": IntComparisonOpDescription(ComparisonOp.SLE, int_less_than_, True, True), + ">": IntComparisonOpDescription(ComparisonOp.SGT, int_less_than_, False, True), + ">=": IntComparisonOpDescription(ComparisonOp.SGE, int_less_than_, True, False), +} + + +def compare_tagged(self: LowLevelIRBuilder, lhs: Value, rhs: Value, op: str, line: int) -> Value: + """Compare two tagged integers using given operator (value context).""" + # generate fast binary logic ops on short ints + if (is_short_int_rprimitive(lhs.type) or is_short_int_rprimitive(rhs.type)) and op in ( + "==", + "!=", + ): + quick = True + else: + quick = is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type) + if quick: + return self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) + op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] + result = Register(bool_rprimitive) + short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() + check_lhs = self.check_tagged_short_int(lhs, line, negated=True) + if op in ("==", "!="): + self.add(Branch(check_lhs, int_block, short_int_block, Branch.BOOL)) + else: + # for non-equality logical ops (less/greater than, etc.), need to check both sides + short_lhs = BasicBlock() + self.add(Branch(check_lhs, int_block, short_lhs, Branch.BOOL)) + self.activate_block(short_lhs) + check_rhs = self.check_tagged_short_int(rhs, line, negated=True) + self.add(Branch(check_rhs, int_block, short_int_block, Branch.BOOL)) + self.activate_block(int_block) + if swap_op: + args = [rhs, lhs] + else: + args = [lhs, rhs] + call = self.call_c(c_func_desc, args, line) + if negate_result: + # TODO: introduce UnaryIntOp? + call_result = self.unary_op(call, "not", line) + else: + call_result = call + self.add(Assign(result, call_result, line)) + self.goto(out) + self.activate_block(short_int_block) + eq = self.comparison_op(lhs, rhs, op_type, line) + self.add(Assign(result, eq, line)) + self.goto_and_activate(out) + return result @lower_binary_op("int_eq") def lower_int_eq(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], "==", line) + return compare_tagged(builder, args[0], args[1], "==", line) @lower_binary_op("int_ne") def lower_int_ne(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], "!=", line) + return compare_tagged(builder, args[0], args[1], "!=", line) @lower_binary_op("int_lt") def lower_int_lt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], "<", line) + return compare_tagged(builder, args[0], args[1], "<", line) @lower_binary_op("int_le") def lower_int_le(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], "<=", line) + return compare_tagged(builder, args[0], args[1], "<=", line) @lower_binary_op("int_gt") def lower_int_gt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], ">", line) + return compare_tagged(builder, args[0], args[1], ">", line) @lower_binary_op("int_ge") def lower_int_ge(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: - return builder.compare_tagged(args[0], args[1], ">=", line) + return compare_tagged(builder, args[0], args[1], ">=", line) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 029d71606886..4413028a0e83 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -10,14 +10,11 @@ from __future__ import annotations -from typing import NamedTuple - from mypyc.ir.ops import ( ERR_ALWAYS, ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER, - ComparisonOp, PrimitiveDescription, ) from mypyc.ir.rtypes import ( @@ -196,20 +193,6 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: # Primitives related to integer comparison operations: -# Description for building int comparison ops -# -# Fields: -# binary_op_variant: identify which IntOp to use when operands are short integers -# c_func_description: the C function to call when operands are tagged integers -# c_func_negated: whether to negate the C function call's result -# c_func_swap_operands: whether to swap lhs and rhs when call the function -class IntComparisonOpDescription(NamedTuple): - binary_op_variant: int - c_func_description: CFunctionDescription - c_func_negated: bool - c_func_swap_operands: bool - - # Equals operation on two boxed tagged integers int_equal_ = custom_op( arg_types=[int_rprimitive, int_rprimitive], @@ -226,17 +209,6 @@ class IntComparisonOpDescription(NamedTuple): error_kind=ERR_NEVER, ) -# Provide mapping from textual op to short int's op variant and boxed int's description. -# Note that these are not complete implementations and require extra IR. -int_comparison_op_mapping: dict[str, IntComparisonOpDescription] = { - "==": IntComparisonOpDescription(ComparisonOp.EQ, int_equal_, False, False), - "!=": IntComparisonOpDescription(ComparisonOp.NEQ, int_equal_, True, False), - "<": IntComparisonOpDescription(ComparisonOp.SLT, int_less_than_, False, False), - "<=": IntComparisonOpDescription(ComparisonOp.SLE, int_less_than_, True, True), - ">": IntComparisonOpDescription(ComparisonOp.SGT, int_less_than_, False, True), - ">=": IntComparisonOpDescription(ComparisonOp.SGE, int_less_than_, True, False), -} - int64_divide_op = custom_op( arg_types=[int64_rprimitive, int64_rprimitive], return_type=int64_rprimitive, From a505e5fb018c673644d6b1c044fe8df0c836895f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:08:35 -0700 Subject: [PATCH 0544/1617] Bump black from 24.1.1 to 24.3.0 (#17051) --- test-requirements.in | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.in b/test-requirements.in index 8eeef206018e..166bdf934d47 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,7 +4,7 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==24.1.1 # must match version in .pre-commit-config.yaml +black==24.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' diff --git a/test-requirements.txt b/test-requirements.txt index 525edbc252d8..f105b753799f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ # attrs==23.1.0 # via -r test-requirements.in -black==24.1.1 +black==24.3.0 # via -r test-requirements.in cfgv==3.4.0 # via pre-commit From 394d17b758bae6c95cbe91b84c5cccf0f4d73c28 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:08:56 -0700 Subject: [PATCH 0545/1617] Improve yield from inference for unions of generators (#16717) Fixes #15141, closes #15168 --- mypy/checker.py | 5 +-- mypy/checkexpr.py | 12 +------ mypyc/test-data/run-generators.test | 9 +++--- test-data/unit/check-statements.test | 47 +++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 941dc06f1c71..5d243195d50f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -989,8 +989,9 @@ def get_generator_return_type(self, return_type: Type, is_coroutine: bool) -> Ty # AwaitableGenerator, Generator: tr is args[2]. return return_type.args[2] else: - # Supertype of Generator (Iterator, Iterable, object): tr is any. - return AnyType(TypeOfAny.special_form) + # We have a supertype of Generator (Iterator, Iterable, object) + # Treat `Iterator[X]` as a shorthand for `Generator[X, Any, None]`. + return NoneType() def visit_func_def(self, defn: FuncDef) -> None: if not self.recurse_into_functions: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 37a90ce55b9e..e7567eafb8fe 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5963,17 +5963,7 @@ def visit_yield_from_expr(self, e: YieldFromExpr, allow_none_return: bool = Fals # Determine the type of the entire yield from expression. iter_type = get_proper_type(iter_type) - if isinstance(iter_type, Instance) and iter_type.type.fullname == "typing.Generator": - expr_type = self.chk.get_generator_return_type(iter_type, False) - else: - # Non-Generators don't return anything from `yield from` expressions. - # However special-case Any (which might be produced by an error). - actual_item_type = get_proper_type(actual_item_type) - if isinstance(actual_item_type, AnyType): - expr_type = AnyType(TypeOfAny.from_another_any, source_any=actual_item_type) - else: - # Treat `Iterator[X]` as a shorthand for `Generator[X, None, Any]`. - expr_type = NoneType() + expr_type = self.chk.get_generator_return_type(iter_type, is_coroutine=False) if not allow_none_return and isinstance(get_proper_type(expr_type), NoneType): self.chk.msg.does_not_return_value(None, e) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index bcf9da1846ae..7e9804c49582 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -246,12 +246,12 @@ assert run_generator(another_triple()()) == ((1,), None) assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) [case testYieldThrow] -from typing import Generator, Iterable, Any +from typing import Generator, Iterable, Any, Union from traceback import print_tb from contextlib import contextmanager import wrapsys -def generator() -> Iterable[int]: +def generator() -> Generator[int, None, Union[int, None]]: try: yield 1 yield 2 @@ -264,6 +264,7 @@ def generator() -> Iterable[int]: else: print('caught exception without value') return 0 + return None def no_except() -> Iterable[int]: yield 1 @@ -355,11 +356,11 @@ with ctx_manager() as c: raise Exception File "native.py", line 10, in generator yield 3 - File "native.py", line 30, in wrapper + File "native.py", line 31, in wrapper return (yield from x) File "native.py", line 9, in generator yield 2 - File "native.py", line 30, in wrapper + File "native.py", line 31, in wrapper return (yield from x) caught exception without value caught exception with value some string diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index f5b47e7ab97f..71cc80719779 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -85,7 +85,7 @@ def f() -> Generator[int, None, None]: from typing import Iterator def f() -> Iterator[int]: yield 1 - return "foo" + return "foo" # E: No return value expected [out] @@ -2231,6 +2231,51 @@ class B: pass def foo(x: int) -> Union[Generator[A, None, None], Generator[B, None, None]]: yield x # E: Incompatible types in "yield" (actual type "int", expected type "Union[A, B]") +[case testYieldFromUnionOfGenerators] +from typing import Generator, Union + +class T: pass + +def foo(arg: Union[Generator[int, None, T], Generator[str, None, T]]) -> Generator[Union[int, str], None, T]: + return (yield from arg) + +[case testYieldFromInvalidUnionReturn] +from typing import Generator, Union + +class A: pass +class B: pass + +def foo(arg: Union[A, B]) -> Generator[Union[int, str], None, A]: + return (yield from arg) # E: "yield from" can't be applied to "Union[A, B]" + +[case testYieldFromUnionOfGeneratorWithIterableStr] +from typing import Generator, Union, Iterable, Optional + +def foo(arg: Union[Generator[int, None, bytes], Iterable[str]]) -> Generator[Union[int, str], None, Optional[bytes]]: + return (yield from arg) + +def bar(arg: Generator[str, None, str]) -> Generator[str, None, str]: + return foo(arg) # E: Incompatible return value type (got "Generator[Union[int, str], None, Optional[bytes]]", expected "Generator[str, None, str]") + +def launder(arg: Iterable[str]) -> Generator[Union[int, str], None, Optional[bytes]]: + return foo(arg) + +def baz(arg: Generator[str, None, str]) -> Generator[Union[int, str], None, Optional[bytes]]: + # this is unsound, the Generator return type will actually be str + return launder(arg) +[builtins fixtures/tuple.pyi] + +[case testYieldIteratorReturn] +from typing import Iterator + +def get_strings(foo: bool) -> Iterator[str]: + if foo: + return ["foo1", "foo2"] # E: No return value expected + else: + yield "bar1" + yield "bar2" +[builtins fixtures/tuple.pyi] + [case testNoCrashOnStarRightHandSide] x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi] From a0a0ada29905b786faf05770b13501fd6a20c891 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 22 Mar 2024 10:01:25 +0000 Subject: [PATCH 0546/1617] [mypyc] Refactor: use primitive op for initializing list item (#17056) Add a new primitive op for initializing list items. Also add support for primitive ops that steal operands (reference counting wise). This will also remove most instances of `WORD_SIZE` in irbuild tests, which were a bit painful, since running tests with `--update-data` removed these and they had to be manually added back for 32-bit tests to pass. --- mypyc/ir/ops.py | 8 + mypyc/ir/pprint.py | 5 +- mypyc/irbuild/ll_builder.py | 14 +- mypyc/lower/int_ops.py | 14 +- mypyc/lower/list_ops.py | 34 ++++ mypyc/lower/registry.py | 7 +- mypyc/primitives/misc_ops.py | 22 ++- mypyc/primitives/registry.py | 35 ++++ mypyc/test-data/irbuild-any.test | 7 +- mypyc/test-data/irbuild-basic.test | 182 ++++++++++---------- mypyc/test-data/irbuild-classes.test | 2 +- mypyc/test-data/irbuild-dict.test | 2 +- mypyc/test-data/irbuild-generics.test | 2 +- mypyc/test-data/irbuild-i64.test | 4 +- mypyc/test-data/irbuild-lists.test | 32 ++-- mypyc/test-data/irbuild-set.test | 216 ++++++++++++------------ mypyc/test-data/irbuild-statements.test | 67 ++++---- mypyc/test-data/irbuild-str.test | 21 ++- mypyc/test-data/irbuild-tuple.test | 103 ++++++----- mypyc/test-data/lowering-list.test | 33 ++++ mypyc/test-data/refcount.test | 11 +- mypyc/test/test_lowering.py | 4 +- 22 files changed, 463 insertions(+), 362 deletions(-) create mode 100644 mypyc/lower/list_ops.py create mode 100644 mypyc/test-data/lowering-list.test diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 3acfb0933e5a..7df4347171da 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -644,6 +644,14 @@ def __init__(self, args: list[Value], desc: PrimitiveDescription, line: int = -1 def sources(self) -> list[Value]: return self.args + def stolen(self) -> list[Value]: + steals = self.desc.steals + if isinstance(steals, list): + assert len(steals) == len(self.args) + return [arg for arg, steal in zip(self.args, steals) if steal] + else: + return [] if not steals else self.sources() + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_primitive_op(self) diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 2ca6a47921fc..59ee994f012d 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -232,7 +232,10 @@ def visit_primitive_op(self, op: PrimitiveOp) -> str: type_arg_index += 1 args_str = ", ".join(args) - return self.format("%r = %s %s", op, op.desc.name, args_str) + if op.is_void: + return self.format("%s %s", op.desc.name, args_str) + else: + return self.format("%r = %s %s", op, op.desc.name, args_str) def visit_truncate(self, op: Truncate) -> str: return self.format("%r = truncate %r: %t to %t", op, op.src, op.src_type, op.type) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index e989471ceb59..134265852b2f 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -67,7 +67,6 @@ PrimitiveOp, RaiseStandardError, Register, - SetMem, Truncate, TupleGet, TupleSet, @@ -165,7 +164,7 @@ uint8_overflow, ) from mypyc.primitives.list_ops import list_build_op, list_extend_op, new_list_op -from mypyc.primitives.misc_ops import bool_op, fast_isinstance_op, none_object_op +from mypyc.primitives.misc_ops import bool_op, buf_init_item, fast_isinstance_op, none_object_op from mypyc.primitives.registry import ( ERR_NEG_INT, CFunctionDescription, @@ -1627,14 +1626,9 @@ def new_list_op(self, values: list[Value], line: int) -> Value: ob_item_ptr = self.add(GetElementPtr(result_list, PyListObject, "ob_item", line)) ob_item_base = self.add(LoadMem(pointer_rprimitive, ob_item_ptr, line)) for i in range(len(values)): - if i == 0: - item_address = ob_item_base - else: - offset = Integer(PLATFORM_SIZE * i, c_pyssize_t_rprimitive, line) - item_address = self.add( - IntOp(pointer_rprimitive, ob_item_base, offset, IntOp.ADD, line) - ) - self.add(SetMem(object_rprimitive, item_address, args[i], line)) + self.primitive_op( + buf_init_item, [ob_item_base, Integer(i, c_pyssize_t_rprimitive), args[i]], line + ) self.add(KeepAlive([result_list])) return result_list diff --git a/mypyc/lower/int_ops.py b/mypyc/lower/int_ops.py index 90a3253ba093..adfb4c21e2de 100644 --- a/mypyc/lower/int_ops.py +++ b/mypyc/lower/int_ops.py @@ -7,7 +7,7 @@ from mypyc.ir.ops import Assign, BasicBlock, Branch, ComparisonOp, Register, Value from mypyc.ir.rtypes import bool_rprimitive, is_short_int_rprimitive from mypyc.irbuild.ll_builder import LowLevelIRBuilder -from mypyc.lower.registry import lower_binary_op +from mypyc.lower.registry import lower_primitive_op from mypyc.primitives.int_ops import int_equal_, int_less_than_ from mypyc.primitives.registry import CFunctionDescription @@ -83,31 +83,31 @@ def compare_tagged(self: LowLevelIRBuilder, lhs: Value, rhs: Value, op: str, lin return result -@lower_binary_op("int_eq") +@lower_primitive_op("int_eq") def lower_int_eq(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], "==", line) -@lower_binary_op("int_ne") +@lower_primitive_op("int_ne") def lower_int_ne(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], "!=", line) -@lower_binary_op("int_lt") +@lower_primitive_op("int_lt") def lower_int_lt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], "<", line) -@lower_binary_op("int_le") +@lower_primitive_op("int_le") def lower_int_le(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], "<=", line) -@lower_binary_op("int_gt") +@lower_primitive_op("int_gt") def lower_int_gt(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], ">", line) -@lower_binary_op("int_ge") +@lower_primitive_op("int_ge") def lower_int_ge(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: return compare_tagged(builder, args[0], args[1], ">=", line) diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py new file mode 100644 index 000000000000..f4619e07dc7e --- /dev/null +++ b/mypyc/lower/list_ops.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from mypyc.common import PLATFORM_SIZE +from mypyc.ir.ops import Integer, IntOp, SetMem, Value +from mypyc.ir.rtypes import c_pyssize_t_rprimitive, object_rprimitive, pointer_rprimitive +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.lower.registry import lower_primitive_op + + +@lower_primitive_op("buf_init_item") +def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + """Initialize an item in a buffer of "PyObject *" values at given index. + + This can be used to initialize the data buffer of a freshly allocated list + object. + """ + base = args[0] + index_value = args[1] + value = args[2] + assert isinstance(index_value, Integer) + index = index_value.numeric_value() + if index == 0: + ptr = base + else: + ptr = builder.add( + IntOp( + pointer_rprimitive, + base, + Integer(index * PLATFORM_SIZE, c_pyssize_t_rprimitive), + IntOp.ADD, + line, + ) + ) + return builder.add(SetMem(object_rprimitive, ptr, value, line)) diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py index cc53eb93f4dd..d1599dc98cf4 100644 --- a/mypyc/lower/registry.py +++ b/mypyc/lower/registry.py @@ -11,8 +11,8 @@ lowering_registry: Final[dict[str, LowerFunc]] = {} -def lower_binary_op(name: str) -> Callable[[LowerFunc], LowerFunc]: - """Register a handler that generates low-level IR for a primitive binary op.""" +def lower_primitive_op(name: str) -> Callable[[LowerFunc], LowerFunc]: + """Register a handler that generates low-level IR for a primitive op.""" def wrapper(f: LowerFunc) -> LowerFunc: assert name not in lowering_registry @@ -23,4 +23,5 @@ def wrapper(f: LowerFunc) -> LowerFunc: # Import various modules that set up global state. -import mypyc.lower.int_ops # noqa: F401 +import mypyc.lower.int_ops +import mypyc.lower.list_ops # noqa: F401 diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 5a8cc111ebc2..87d009f7bbab 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -13,9 +13,17 @@ int_rprimitive, object_pointer_rprimitive, object_rprimitive, + pointer_rprimitive, str_rprimitive, + void_rtype, +) +from mypyc.primitives.registry import ( + ERR_NEG_INT, + custom_op, + custom_primitive_op, + function_op, + load_address_op, ) -from mypyc.primitives.registry import ERR_NEG_INT, custom_op, function_op, load_address_op # Get the 'bool' type object. load_address_op(name="builtins.bool", type=object_rprimitive, src="PyBool_Type") @@ -232,10 +240,20 @@ ) -# register an implementation for a singledispatch function +# Register an implementation for a singledispatch function register_function = custom_op( arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], return_type=object_rprimitive, c_function_name="CPySingledispatch_RegisterFunction", error_kind=ERR_MAGIC, ) + + +# Initialize a PyObject * item in a memory buffer (steal the value) +buf_init_item = custom_primitive_op( + name="buf_init_item", + arg_types=[pointer_rprimitive, c_pyssize_t_rprimitive, object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, + steals=[False, False, True], +) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index d4768b4df532..1472885a4829 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -267,6 +267,41 @@ def custom_op( ) +def custom_primitive_op( + name: str, + arg_types: list[RType], + return_type: RType, + error_kind: int, + c_function_name: str | None = None, + var_arg_type: RType | None = None, + truncated_type: RType | None = None, + ordering: list[int] | None = None, + extra_int_constants: list[tuple[int, RType]] | None = None, + steals: StealsDescription = False, + is_borrowed: bool = False, +) -> PrimitiveDescription: + """Define a primitive op that can't be automatically generated based on the AST. + + Most arguments are similar to method_op(). + """ + if extra_int_constants is None: + extra_int_constants = [] + return PrimitiveDescription( + name=name, + arg_types=arg_types, + return_type=return_type, + var_arg_type=var_arg_type, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=0, + ) + + def unary_op( name: str, arg_type: RType, diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 98f3dae9ee88..dd1931ba40f3 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -106,7 +106,7 @@ def f2(a, n, l): r9, r10 :: bit r11 :: list r12 :: object - r13, r14, r15 :: ptr + r13, r14 :: ptr L0: r0 = box(int, n) r1 = PyObject_GetItem(a, r0) @@ -123,9 +123,8 @@ L0: r12 = box(int, n) r13 = get_element_ptr r11 ob_item :: PyListObject r14 = load_mem r13 :: ptr* - set_mem r14, a :: builtins.object* - r15 = r14 + WORD_SIZE*1 - set_mem r15, r12 :: builtins.object* + buf_init_item r14, 0, a + buf_init_item r14, 1, r12 keep_alive r11 return 1 def f3(a, n): diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 164fc213a8a2..766e584d4149 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -771,7 +771,7 @@ L0: r2 = object 1 r3 = get_element_ptr r1 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r2 :: builtins.object* + buf_init_item r4, 0, r2 keep_alive r1 r5 = g(r1) r6 = box(None, 1) @@ -801,7 +801,7 @@ L0: r2 = PyList_New(1) r3 = get_element_ptr r2 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, y :: builtins.object* + buf_init_item r4, 0, y keep_alive r2 a = r2 r5 = (2, 4) @@ -1676,7 +1676,7 @@ L0: r5 = object 1 r6 = get_element_ptr r4 ob_item :: PyListObject r7 = load_mem r6 :: ptr* - set_mem r7, r5 :: builtins.object* + buf_init_item r7, 0, r5 keep_alive r4 r8 = box(tuple[int, int], r0) r9 = CPyList_Extend(r4, r8) @@ -1849,20 +1849,20 @@ def f() -> List[int]: def f(): r0, r1 :: list r2, r3, r4 :: object - r5, r6, r7, r8 :: ptr - r9 :: short_int - r10 :: ptr - r11 :: native_int - r12 :: short_int - r13 :: bit - r14 :: object - r15, x :: int - r16, r17 :: bit - r18 :: int - r19 :: object - r20 :: i32 - r21 :: bit - r22 :: short_int + r5, r6 :: ptr + r7 :: short_int + r8 :: ptr + r9 :: native_int + r10 :: short_int + r11 :: bit + r12 :: object + r13, x :: int + r14, r15 :: bit + r16 :: int + r17 :: object + r18 :: i32 + r19 :: bit + r20 :: short_int L0: r0 = PyList_New(0) r1 = PyList_New(3) @@ -1871,41 +1871,39 @@ L0: r4 = object 3 r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5 :: ptr* - set_mem r6, r2 :: builtins.object* - r7 = r6 + WORD_SIZE*1 - set_mem r7, r3 :: builtins.object* - r8 = r6 + WORD_SIZE*2 - set_mem r8, r4 :: builtins.object* + buf_init_item r6, 0, r2 + buf_init_item r6, 1, r3 + buf_init_item r6, 2, r4 keep_alive r1 - r9 = 0 + r7 = 0 L1: - r10 = get_element_ptr r1 ob_size :: PyVarObject - r11 = load_mem r10 :: native_int* + r8 = get_element_ptr r1 ob_size :: PyVarObject + r9 = load_mem r8 :: native_int* keep_alive r1 - r12 = r11 << 1 - r13 = int_lt r9, r12 - if r13 goto L2 else goto L8 :: bool + r10 = r9 << 1 + r11 = int_lt r7, r10 + if r11 goto L2 else goto L8 :: bool L2: - r14 = CPyList_GetItemUnsafe(r1, r9) - r15 = unbox(int, r14) - x = r15 - r16 = int_ne x, 4 - if r16 goto L4 else goto L3 :: bool + r12 = CPyList_GetItemUnsafe(r1, r7) + r13 = unbox(int, r12) + x = r13 + r14 = int_ne x, 4 + if r14 goto L4 else goto L3 :: bool L3: goto L7 L4: - r17 = int_ne x, 6 - if r17 goto L6 else goto L5 :: bool + r15 = int_ne x, 6 + if r15 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = CPyTagged_Multiply(x, x) - r19 = box(int, r18) - r20 = PyList_Append(r0, r19) - r21 = r20 >= 0 :: signed + r16 = CPyTagged_Multiply(x, x) + r17 = box(int, r16) + r18 = PyList_Append(r0, r17) + r19 = r18 >= 0 :: signed L7: - r22 = r9 + 2 - r9 = r22 + r20 = r7 + 2 + r7 = r20 goto L1 L8: return r0 @@ -1919,20 +1917,20 @@ def f(): r0 :: dict r1 :: list r2, r3, r4 :: object - r5, r6, r7, r8 :: ptr - r9 :: short_int - r10 :: ptr - r11 :: native_int - r12 :: short_int - r13 :: bit - r14 :: object - r15, x :: int - r16, r17 :: bit - r18 :: int - r19, r20 :: object - r21 :: i32 - r22 :: bit - r23 :: short_int + r5, r6 :: ptr + r7 :: short_int + r8 :: ptr + r9 :: native_int + r10 :: short_int + r11 :: bit + r12 :: object + r13, x :: int + r14, r15 :: bit + r16 :: int + r17, r18 :: object + r19 :: i32 + r20 :: bit + r21 :: short_int L0: r0 = PyDict_New() r1 = PyList_New(3) @@ -1941,42 +1939,40 @@ L0: r4 = object 3 r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5 :: ptr* - set_mem r6, r2 :: builtins.object* - r7 = r6 + WORD_SIZE*1 - set_mem r7, r3 :: builtins.object* - r8 = r6 + WORD_SIZE*2 - set_mem r8, r4 :: builtins.object* + buf_init_item r6, 0, r2 + buf_init_item r6, 1, r3 + buf_init_item r6, 2, r4 keep_alive r1 - r9 = 0 + r7 = 0 L1: - r10 = get_element_ptr r1 ob_size :: PyVarObject - r11 = load_mem r10 :: native_int* + r8 = get_element_ptr r1 ob_size :: PyVarObject + r9 = load_mem r8 :: native_int* keep_alive r1 - r12 = r11 << 1 - r13 = int_lt r9, r12 - if r13 goto L2 else goto L8 :: bool + r10 = r9 << 1 + r11 = int_lt r7, r10 + if r11 goto L2 else goto L8 :: bool L2: - r14 = CPyList_GetItemUnsafe(r1, r9) - r15 = unbox(int, r14) - x = r15 - r16 = int_ne x, 4 - if r16 goto L4 else goto L3 :: bool + r12 = CPyList_GetItemUnsafe(r1, r7) + r13 = unbox(int, r12) + x = r13 + r14 = int_ne x, 4 + if r14 goto L4 else goto L3 :: bool L3: goto L7 L4: - r17 = int_ne x, 6 - if r17 goto L6 else goto L5 :: bool + r15 = int_ne x, 6 + if r15 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = CPyTagged_Multiply(x, x) - r19 = box(int, x) - r20 = box(int, r18) - r21 = CPyDict_SetItem(r0, r19, r20) - r22 = r21 >= 0 :: signed + r16 = CPyTagged_Multiply(x, x) + r17 = box(int, x) + r18 = box(int, r16) + r19 = CPyDict_SetItem(r0, r17, r18) + r20 = r19 >= 0 :: signed L7: - r23 = r9 + 2 - r9 = r23 + r21 = r7 + 2 + r7 = r21 goto L1 L8: return r0 @@ -2208,11 +2204,11 @@ def __top_level__(): r59 :: bit r60 :: list r61, r62, r63 :: object - r64, r65, r66, r67 :: ptr - r68 :: dict - r69 :: str - r70 :: i32 - r71 :: bit + r64, r65 :: ptr + r66 :: dict + r67 :: str + r68 :: i32 + r69 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2285,16 +2281,14 @@ L2: r63 = object 3 r64 = get_element_ptr r60 ob_item :: PyListObject r65 = load_mem r64 :: ptr* - set_mem r65, r61 :: builtins.object* - r66 = r65 + WORD_SIZE*1 - set_mem r66, r62 :: builtins.object* - r67 = r65 + WORD_SIZE*2 - set_mem r67, r63 :: builtins.object* + buf_init_item r65, 0, r61 + buf_init_item r65, 1, r62 + buf_init_item r65, 2, r63 keep_alive r60 - r68 = __main__.globals :: static - r69 = 'y' - r70 = CPyDict_SetItem(r68, r69, r60) - r71 = r70 >= 0 :: signed + r66 = __main__.globals :: static + r67 = 'y' + r68 = CPyDict_SetItem(r66, r67, r60) + r69 = r68 >= 0 :: signed return 1 [case testChainedConditional] diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 8c4743c6a47f..cbed51ebcfb0 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -53,7 +53,7 @@ L0: r2 = PyList_New(1) r3 = get_element_ptr r2 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, c :: builtins.object* + buf_init_item r4, 0, c keep_alive r2 a = r2 r5 = CPyList_GetItemShort(a, 0) diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 1a84f3fe3098..9445219a08ce 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -551,7 +551,7 @@ L2: r4 = object 1 r5 = get_element_ptr r3 ob_item :: PyListObject r6 = load_mem r5 :: ptr* - set_mem r6, r4 :: builtins.object* + buf_init_item r6, 0, r4 keep_alive r3 r7 = CPyDict_SetDefault(d, r2, r3) return r7 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 35920889e596..50f6ed6cda1e 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -23,7 +23,7 @@ L0: r1 = PyList_New(1) r2 = get_element_ptr r1 ob_item :: PyListObject r3 = load_mem r2 :: ptr* - set_mem r3, r0 :: builtins.object* + buf_init_item r3, 0, r0 keep_alive r1 return r1 def h(x, y): diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index 07f549c9fcc2..ad2a97e6eeff 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1150,7 +1150,7 @@ L0: r1 = PyList_New(1) r2 = get_element_ptr r1 ob_item :: PyListObject r3 = load_mem r2 :: ptr* - set_mem r3, r0 :: builtins.object* + buf_init_item r3, 0, r0 keep_alive r1 a = r1 r4 = CPyList_GetItemInt64Borrow(a, n) @@ -1260,7 +1260,7 @@ L0: r1 = box(i64, n) r2 = get_element_ptr r0 ob_item :: PyListObject r3 = load_mem r2 :: ptr* - set_mem r3, r1 :: builtins.object* + buf_init_item r3, 0, r1 keep_alive r0 r4 = n <= 4611686018427387903 :: signed if r4 goto L1 else goto L2 :: bool diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index ced4646922a3..66aa1dc748be 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -108,7 +108,7 @@ def f() -> None: def f(): r0 :: list r1, r2 :: object - r3, r4, r5 :: ptr + r3, r4 :: ptr x :: list L0: r0 = PyList_New(2) @@ -116,9 +116,8 @@ L0: r2 = object 2 r3 = get_element_ptr r0 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r1 :: builtins.object* - r5 = r4 + WORD_SIZE*1 - set_mem r5, r2 :: builtins.object* + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 keep_alive r0 x = r0 return 1 @@ -165,7 +164,7 @@ L0: r2 = object 4 r3 = get_element_ptr r1 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r2 :: builtins.object* + buf_init_item r4, 0, r2 keep_alive r1 r5 = CPySequence_RMultiply(6, r1) b = r5 @@ -253,25 +252,24 @@ def f(x: List[int], y: List[int]) -> List[int]: def f(x, y): x, y, r0 :: list r1, r2 :: object - r3, r4, r5 :: ptr - r6, r7, r8 :: object - r9 :: i32 - r10 :: bit + r3, r4 :: ptr + r5, r6, r7 :: object + r8 :: i32 + r9 :: bit L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 r3 = get_element_ptr r0 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r1 :: builtins.object* - r5 = r4 + WORD_SIZE*1 - set_mem r5, r2 :: builtins.object* + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 keep_alive r0 - r6 = CPyList_Extend(r0, x) - r7 = CPyList_Extend(r0, y) - r8 = object 3 - r9 = PyList_Append(r0, r8) - r10 = r9 >= 0 :: signed + r5 = CPyList_Extend(r0, x) + r6 = CPyList_Extend(r0, y) + r7 = object 3 + r8 = PyList_Append(r0, r7) + r9 = r8 >= 0 :: signed return r0 [case testListIn] diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 51feab332593..ea900f2e4789 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -79,20 +79,20 @@ L0: def test1(): r0 :: list r1, r2, r3 :: object - r4, r5, r6, r7 :: ptr + r4, r5 :: ptr tmp_list :: list - r8 :: set - r9 :: short_int - r10 :: ptr - r11 :: native_int - r12 :: short_int - r13 :: bit - r14 :: object - r15, x, r16 :: int - r17 :: object - r18 :: i32 - r19 :: bit - r20 :: short_int + r6 :: set + r7 :: short_int + r8 :: ptr + r9 :: native_int + r10 :: short_int + r11 :: bit + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: i32 + r17 :: bit + r18 :: short_int a :: set L0: r0 = PyList_New(3) @@ -101,36 +101,34 @@ L0: r3 = object 5 r4 = get_element_ptr r0 ob_item :: PyListObject r5 = load_mem r4 :: ptr* - set_mem r5, r1 :: builtins.object* - r6 = r5 + WORD_SIZE*1 - set_mem r6, r2 :: builtins.object* - r7 = r5 + WORD_SIZE*2 - set_mem r7, r3 :: builtins.object* + buf_init_item r5, 0, r1 + buf_init_item r5, 1, r2 + buf_init_item r5, 2, r3 keep_alive r0 tmp_list = r0 - r8 = PySet_New(0) - r9 = 0 + r6 = PySet_New(0) + r7 = 0 L1: - r10 = get_element_ptr tmp_list ob_size :: PyVarObject - r11 = load_mem r10 :: native_int* + r8 = get_element_ptr tmp_list ob_size :: PyVarObject + r9 = load_mem r8 :: native_int* keep_alive tmp_list - r12 = r11 << 1 - r13 = int_lt r9, r12 - if r13 goto L2 else goto L4 :: bool + r10 = r9 << 1 + r11 = int_lt r7, r10 + if r11 goto L2 else goto L4 :: bool L2: - r14 = CPyList_GetItemUnsafe(tmp_list, r9) - r15 = unbox(int, r14) - x = r15 - r16 = f(x) - r17 = box(int, r16) - r18 = PySet_Add(r8, r17) - r19 = r18 >= 0 :: signed + r12 = CPyList_GetItemUnsafe(tmp_list, r7) + r13 = unbox(int, r12) + x = r13 + r14 = f(x) + r15 = box(int, r14) + r16 = PySet_Add(r6, r15) + r17 = r16 >= 0 :: signed L3: - r20 = r9 + 2 - r9 = r20 + r18 = r7 + 2 + r7 = r18 goto L1 L4: - a = r8 + a = r6 return 1 def test2(): r0, tmp_tuple :: tuple[int, int, int] @@ -312,33 +310,33 @@ L0: def test(): r0 :: list r1, r2, r3, r4, r5 :: object - r6, r7, r8, r9, r10, r11 :: ptr + r6, r7 :: ptr tmp_list :: list - r12 :: set - r13, r14 :: list - r15 :: short_int - r16 :: ptr - r17 :: native_int - r18 :: short_int - r19 :: bit + r8 :: set + r9, r10 :: list + r11 :: short_int + r12 :: ptr + r13 :: native_int + r14 :: short_int + r15 :: bit + r16 :: object + r17, z :: int + r18 :: bit + r19 :: int r20 :: object - r21, z :: int + r21 :: i32 r22 :: bit - r23 :: int - r24 :: object - r25 :: i32 - r26 :: bit - r27 :: short_int - r28, r29, r30 :: object - r31, y, r32 :: int - r33 :: object - r34 :: i32 - r35, r36 :: bit - r37, r38, r39 :: object - r40, x, r41 :: int - r42 :: object - r43 :: i32 - r44, r45 :: bit + r23 :: short_int + r24, r25, r26 :: object + r27, y, r28 :: int + r29 :: object + r30 :: i32 + r31, r32 :: bit + r33, r34, r35 :: object + r36, x, r37 :: int + r38 :: object + r39 :: i32 + r40, r41 :: bit a :: set L0: r0 = PyList_New(5) @@ -349,81 +347,77 @@ L0: r5 = object 5 r6 = get_element_ptr r0 ob_item :: PyListObject r7 = load_mem r6 :: ptr* - set_mem r7, r1 :: builtins.object* - r8 = r7 + WORD_SIZE*1 - set_mem r8, r2 :: builtins.object* - r9 = r7 + WORD_SIZE*2 - set_mem r9, r3 :: builtins.object* - r10 = r7 + WORD_SIZE*3 - set_mem r10, r4 :: builtins.object* - r11 = r7 + WORD_SIZE*4 - set_mem r11, r5 :: builtins.object* + buf_init_item r7, 0, r1 + buf_init_item r7, 1, r2 + buf_init_item r7, 2, r3 + buf_init_item r7, 3, r4 + buf_init_item r7, 4, r5 keep_alive r0 tmp_list = r0 - r12 = PySet_New(0) - r13 = PyList_New(0) - r14 = PyList_New(0) - r15 = 0 + r8 = PySet_New(0) + r9 = PyList_New(0) + r10 = PyList_New(0) + r11 = 0 L1: - r16 = get_element_ptr tmp_list ob_size :: PyVarObject - r17 = load_mem r16 :: native_int* + r12 = get_element_ptr tmp_list ob_size :: PyVarObject + r13 = load_mem r12 :: native_int* keep_alive tmp_list - r18 = r17 << 1 - r19 = int_lt r15, r18 - if r19 goto L2 else goto L6 :: bool + r14 = r13 << 1 + r15 = int_lt r11, r14 + if r15 goto L2 else goto L6 :: bool L2: - r20 = CPyList_GetItemUnsafe(tmp_list, r15) - r21 = unbox(int, r20) - z = r21 - r22 = int_lt z, 8 - if r22 goto L4 else goto L3 :: bool + r16 = CPyList_GetItemUnsafe(tmp_list, r11) + r17 = unbox(int, r16) + z = r17 + r18 = int_lt z, 8 + if r18 goto L4 else goto L3 :: bool L3: goto L5 L4: - r23 = f1(z) - r24 = box(int, r23) - r25 = PyList_Append(r14, r24) - r26 = r25 >= 0 :: signed + r19 = f1(z) + r20 = box(int, r19) + r21 = PyList_Append(r10, r20) + r22 = r21 >= 0 :: signed L5: - r27 = r15 + 2 - r15 = r27 + r23 = r11 + 2 + r11 = r23 goto L1 L6: - r28 = PyObject_GetIter(r14) - r29 = PyObject_GetIter(r28) + r24 = PyObject_GetIter(r10) + r25 = PyObject_GetIter(r24) L7: - r30 = PyIter_Next(r29) - if is_error(r30) goto L10 else goto L8 + r26 = PyIter_Next(r25) + if is_error(r26) goto L10 else goto L8 L8: - r31 = unbox(int, r30) - y = r31 - r32 = f2(y) - r33 = box(int, r32) - r34 = PyList_Append(r13, r33) - r35 = r34 >= 0 :: signed + r27 = unbox(int, r26) + y = r27 + r28 = f2(y) + r29 = box(int, r28) + r30 = PyList_Append(r9, r29) + r31 = r30 >= 0 :: signed L9: goto L7 L10: - r36 = CPy_NoErrOccured() + r32 = CPy_NoErrOccured() L11: - r37 = PyObject_GetIter(r13) - r38 = PyObject_GetIter(r37) + r33 = PyObject_GetIter(r9) + r34 = PyObject_GetIter(r33) L12: - r39 = PyIter_Next(r38) - if is_error(r39) goto L15 else goto L13 + r35 = PyIter_Next(r34) + if is_error(r35) goto L15 else goto L13 L13: - r40 = unbox(int, r39) - x = r40 - r41 = f3(x) - r42 = box(int, r41) - r43 = PySet_Add(r12, r42) - r44 = r43 >= 0 :: signed + r36 = unbox(int, r35) + x = r36 + r37 = f3(x) + r38 = box(int, r37) + r39 = PySet_Add(r8, r38) + r40 = r39 >= 0 :: signed L14: goto L12 L15: - r45 = CPy_NoErrOccured() + r41 = CPy_NoErrOccured() L16: - a = r12 + a = r8 return 1 [case testSetSize] diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index ed97c4cd4138..628d692c85c1 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -688,40 +688,39 @@ def delListMultiple() -> None: def delList(): r0 :: list r1, r2 :: object - r3, r4, r5 :: ptr + r3, r4 :: ptr l :: list - r6 :: object - r7 :: i32 - r8 :: bit + r5 :: object + r6 :: i32 + r7 :: bit L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 r3 = get_element_ptr r0 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r1 :: builtins.object* - r5 = r4 + WORD_SIZE*1 - set_mem r5, r2 :: builtins.object* + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 keep_alive r0 l = r0 - r6 = object 1 - r7 = PyObject_DelItem(l, r6) - r8 = r7 >= 0 :: signed + r5 = object 1 + r6 = PyObject_DelItem(l, r5) + r7 = r6 >= 0 :: signed return 1 def delListMultiple(): r0 :: list r1, r2, r3, r4, r5, r6, r7 :: object - r8, r9, r10, r11, r12, r13, r14, r15 :: ptr + r8, r9 :: ptr l :: list + r10 :: object + r11 :: i32 + r12 :: bit + r13 :: object + r14 :: i32 + r15 :: bit r16 :: object r17 :: i32 r18 :: bit - r19 :: object - r20 :: i32 - r21 :: bit - r22 :: object - r23 :: i32 - r24 :: bit L0: r0 = PyList_New(7) r1 = object 1 @@ -733,30 +732,24 @@ L0: r7 = object 7 r8 = get_element_ptr r0 ob_item :: PyListObject r9 = load_mem r8 :: ptr* - set_mem r9, r1 :: builtins.object* - r10 = r9 + WORD_SIZE*1 - set_mem r10, r2 :: builtins.object* - r11 = r9 + WORD_SIZE*2 - set_mem r11, r3 :: builtins.object* - r12 = r9 + WORD_SIZE*3 - set_mem r12, r4 :: builtins.object* - r13 = r9 + WORD_SIZE*4 - set_mem r13, r5 :: builtins.object* - r14 = r9 + WORD_SIZE*5 - set_mem r14, r6 :: builtins.object* - r15 = r9 + WORD_SIZE*6 - set_mem r15, r7 :: builtins.object* + buf_init_item r9, 0, r1 + buf_init_item r9, 1, r2 + buf_init_item r9, 2, r3 + buf_init_item r9, 3, r4 + buf_init_item r9, 4, r5 + buf_init_item r9, 5, r6 + buf_init_item r9, 6, r7 keep_alive r0 l = r0 - r16 = object 1 + r10 = object 1 + r11 = PyObject_DelItem(l, r10) + r12 = r11 >= 0 :: signed + r13 = object 2 + r14 = PyObject_DelItem(l, r13) + r15 = r14 >= 0 :: signed + r16 = object 3 r17 = PyObject_DelItem(l, r16) r18 = r17 >= 0 :: signed - r19 = object 2 - r20 = PyObject_DelItem(l, r19) - r21 = r20 >= 0 :: signed - r22 = object 3 - r23 = PyObject_DelItem(l, r22) - r24 = r23 >= 0 :: signed return 1 [case testDelDict] diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 9851e0f4fb24..dfaa50520364 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -203,8 +203,8 @@ def f(var, num): r12 :: object r13 :: str r14 :: list - r15, r16, r17 :: ptr - r18, s2, r19, s3, r20, s4 :: str + r15, r16 :: ptr + r17, s2, r18, s3, r19, s4 :: str L0: r0 = "Hi! I'm " r1 = '. I am ' @@ -224,16 +224,15 @@ L0: r14 = PyList_New(2) r15 = get_element_ptr r14 ob_item :: PyListObject r16 = load_mem r15 :: ptr* - set_mem r16, r6 :: builtins.object* - r17 = r16 + WORD_SIZE*1 - set_mem r17, r13 :: builtins.object* + buf_init_item r16, 0, r6 + buf_init_item r16, 1, r13 keep_alive r14 - r18 = PyUnicode_Join(r5, r14) - s2 = r18 - r19 = '' - s3 = r19 - r20 = 'abc' - s4 = r20 + r17 = PyUnicode_Join(r5, r14) + s2 = r17 + r18 = '' + s3 = r18 + r19 = 'abc' + s4 = r19 return 1 [case testStringFormattingCStyle] diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 342bb19b5360..0a26d8aa1d3d 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -101,28 +101,27 @@ def f(x, y): x, y :: object r0 :: list r1, r2 :: object - r3, r4, r5 :: ptr - r6, r7, r8 :: object - r9 :: i32 - r10 :: bit - r11 :: tuple + r3, r4 :: ptr + r5, r6, r7 :: object + r8 :: i32 + r9 :: bit + r10 :: tuple L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 r3 = get_element_ptr r0 ob_item :: PyListObject r4 = load_mem r3 :: ptr* - set_mem r4, r1 :: builtins.object* - r5 = r4 + WORD_SIZE*1 - set_mem r5, r2 :: builtins.object* + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 keep_alive r0 - r6 = CPyList_Extend(r0, x) - r7 = CPyList_Extend(r0, y) - r8 = object 3 - r9 = PyList_Append(r0, r8) - r10 = r9 >= 0 :: signed - r11 = PyList_AsTuple(r0) - return r11 + r5 = CPyList_Extend(r0, x) + r6 = CPyList_Extend(r0, y) + r7 = object 3 + r8 = PyList_Append(r0, r7) + r9 = r8 >= 0 :: signed + r10 = PyList_AsTuple(r0) + return r10 [case testTupleFor] from typing import Tuple, List @@ -238,22 +237,22 @@ L0: def test(): r0 :: list r1, r2, r3 :: object - r4, r5, r6, r7 :: ptr + r4, r5 :: ptr source :: list - r8 :: ptr - r9 :: native_int - r10 :: tuple - r11 :: short_int - r12 :: ptr - r13 :: native_int - r14 :: short_int - r15 :: bit - r16 :: object - r17, x :: int - r18 :: bool - r19 :: object - r20 :: bit - r21 :: short_int + r6 :: ptr + r7 :: native_int + r8 :: tuple + r9 :: short_int + r10 :: ptr + r11 :: native_int + r12 :: short_int + r13 :: bit + r14 :: object + r15, x :: int + r16 :: bool + r17 :: object + r18 :: bit + r19 :: short_int a :: tuple L0: r0 = PyList_New(3) @@ -262,38 +261,36 @@ L0: r3 = object 3 r4 = get_element_ptr r0 ob_item :: PyListObject r5 = load_mem r4 :: ptr* - set_mem r5, r1 :: builtins.object* - r6 = r5 + WORD_SIZE*1 - set_mem r6, r2 :: builtins.object* - r7 = r5 + WORD_SIZE*2 - set_mem r7, r3 :: builtins.object* + buf_init_item r5, 0, r1 + buf_init_item r5, 1, r2 + buf_init_item r5, 2, r3 keep_alive r0 source = r0 - r8 = get_element_ptr source ob_size :: PyVarObject - r9 = load_mem r8 :: native_int* + r6 = get_element_ptr source ob_size :: PyVarObject + r7 = load_mem r6 :: native_int* keep_alive source - r10 = PyTuple_New(r9) - r11 = 0 + r8 = PyTuple_New(r7) + r9 = 0 L1: - r12 = get_element_ptr source ob_size :: PyVarObject - r13 = load_mem r12 :: native_int* + r10 = get_element_ptr source ob_size :: PyVarObject + r11 = load_mem r10 :: native_int* keep_alive source - r14 = r13 << 1 - r15 = int_lt r11, r14 - if r15 goto L2 else goto L4 :: bool + r12 = r11 << 1 + r13 = int_lt r9, r12 + if r13 goto L2 else goto L4 :: bool L2: - r16 = CPyList_GetItemUnsafe(source, r11) - r17 = unbox(int, r16) - x = r17 - r18 = f(x) - r19 = box(bool, r18) - r20 = CPySequenceTuple_SetItemUnsafe(r10, r11, r19) + r14 = CPyList_GetItemUnsafe(source, r9) + r15 = unbox(int, r14) + x = r15 + r16 = f(x) + r17 = box(bool, r16) + r18 = CPySequenceTuple_SetItemUnsafe(r8, r9, r17) L3: - r21 = r11 + 2 - r11 = r21 + r19 = r9 + 2 + r9 = r19 goto L1 L4: - a = r10 + a = r8 return 1 [case testTupleBuiltFromStr] diff --git a/mypyc/test-data/lowering-list.test b/mypyc/test-data/lowering-list.test new file mode 100644 index 000000000000..c8438d869970 --- /dev/null +++ b/mypyc/test-data/lowering-list.test @@ -0,0 +1,33 @@ +[case testLowerListDisplay] +def f() -> None: + a = [4, 6, 7] +[out] +def f(): + r0 :: list + r1, r2, r3 :: object + r4, r5, r6, r7 :: ptr + a :: list + r8 :: None +L0: + r0 = PyList_New(3) + if is_error(r0) goto L2 (error at f:2) else goto L1 +L1: + r1 = object 4 + r2 = object 6 + r3 = object 7 + r4 = get_element_ptr r0 ob_item :: PyListObject + r5 = load_mem r4 :: ptr* + inc_ref r1 + set_mem r5, r1 :: builtins.object* + inc_ref r2 + r6 = r5 + WORD_SIZE*1 + set_mem r6, r2 :: builtins.object* + inc_ref r3 + r7 = r5 + WORD_SIZE*2 + set_mem r7, r3 :: builtins.object* + a = r0 + dec_ref a + return 1 +L2: + r8 = :: None + return r8 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 3021381abded..b8d598e3b533 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -498,7 +498,7 @@ def f() -> int: def f(): r0 :: list r1, r2 :: object - r3, r4, r5 :: ptr + r3, r4 :: ptr a :: list L0: r0 = PyList_New(2) @@ -507,10 +507,9 @@ L0: r3 = get_element_ptr r0 ob_item :: PyListObject r4 = load_mem r3 :: ptr* inc_ref r1 - set_mem r4, r1 :: builtins.object* - r5 = r4 + WORD_SIZE*1 + buf_init_item r4, 0, r1 inc_ref r2 - set_mem r5, r2 :: builtins.object* + buf_init_item r4, 1, r2 a = r0 dec_ref a return 0 @@ -586,7 +585,7 @@ L0: r1 = PyList_New(1) r2 = get_element_ptr r1 ob_item :: PyListObject r3 = load_mem r2 :: ptr* - set_mem r3, r0 :: builtins.object* + buf_init_item r3, 0, r0 a = r1 r4 = CPyList_GetItemShort(a, 0) dec_ref a @@ -1266,7 +1265,7 @@ L0: r1 = PyList_New(1) r2 = get_element_ptr r1 ob_item :: PyListObject r3 = load_mem r2 :: ptr* - set_mem r3, r0 :: builtins.object* + buf_init_item r3, 0, r0 a = r1 r4 = CPyList_GetItemShortBorrow(a, 0) r5 = borrow cast(__main__.C, r4) diff --git a/mypyc/test/test_lowering.py b/mypyc/test/test_lowering.py index e32dba2e1021..50a9a7390855 100644 --- a/mypyc/test/test_lowering.py +++ b/mypyc/test/test_lowering.py @@ -16,6 +16,7 @@ assert_test_output, build_ir_for_single_file, remove_comment_lines, + replace_word_size, use_custom_builtins, ) from mypyc.transform.exceptions import insert_exception_handling @@ -26,12 +27,13 @@ class TestLowering(MypycDataSuite): - files = ["lowering-int.test"] + files = ["lowering-int.test", "lowering-list.test"] base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) + expected_output = replace_word_size(expected_output) try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: From 5db161ffbfd7d9cdd837410cd5d581c0ba597623 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 22 Mar 2024 12:55:06 -0700 Subject: [PATCH 0547/1617] Store typeshed patches in repo (#17054) This way, we don't have to do the two step rebase and merge + update hashes dance. We also get a cleaner commit history --- .pre-commit-config.yaml | 2 +- misc/sync-typeshed.py | 101 +++++----- ...e-of-LiteralString-in-builtins-13743.patch | 182 ++++++++++++++++++ ...ert-sum-literal-integer-change-13961.patch | 36 ++++ .../0001-Revert-typeshed-ctypes-change.patch | 32 +++ 5 files changed, 306 insertions(+), 47 deletions(-) create mode 100644 misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch create mode 100644 misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch create mode 100644 misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4cfb8297a66a..a7ff48051aad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: '^(mypyc/external/)|(mypy/typeshed/)' # Exclude all vendored code from lints +exclude: '^(mypyc/external/)|(mypy/typeshed/)|misc/typeshed_patches' # Exclude all vendored code from lints repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 # must match test-requirements.txt diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 2dc6e230df00..3101b4bfa72a 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -11,6 +11,7 @@ import argparse import functools +import glob import os import re import shutil @@ -148,58 +149,66 @@ def main() -> None: if os.environ.get("GITHUB_TOKEN") is None: raise ValueError("GITHUB_TOKEN environment variable must be set") - branch_name = "mypybot/sync-typeshed" - subprocess.run(["git", "checkout", "-B", branch_name, "origin/master"], check=True) - - if not args.typeshed_dir: - # Clone typeshed repo if no directory given. - with tempfile.TemporaryDirectory() as tempdir: - print(f"Cloning typeshed in {tempdir}...") + with tempfile.TemporaryDirectory() as tmpdir: + # Stash patches before checking out a new branch + typeshed_patches = os.path.join("misc", "typeshed_patches") + tmp_patches = os.path.join(tmpdir, "typeshed_patches") + shutil.copytree(typeshed_patches, tmp_patches) + + branch_name = "mypybot/sync-typeshed" + subprocess.run(["git", "checkout", "-B", branch_name, "origin/master"], check=True) + + # Copy the stashed patches back + shutil.rmtree(typeshed_patches, ignore_errors=True) + shutil.copytree(tmp_patches, typeshed_patches) + if subprocess.run(["git", "diff", "--quiet", "--exit-code"], check=False).returncode != 0: + subprocess.run(["git", "commit", "-am", "Update typeshed patches"], check=True) + + if not args.typeshed_dir: + tmp_typeshed = os.path.join(tmpdir, "typeshed") + os.makedirs(tmp_typeshed) + # Clone typeshed repo if no directory given. + print(f"Cloning typeshed in {tmp_typeshed}...") subprocess.run( - ["git", "clone", "https://github.com/python/typeshed.git"], check=True, cwd=tempdir + ["git", "clone", "https://github.com/python/typeshed.git"], + check=True, + cwd=tmp_typeshed, ) - repo = os.path.join(tempdir, "typeshed") + repo = os.path.join(tmp_typeshed, "typeshed") commit = update_typeshed(repo, args.commit) - else: - commit = update_typeshed(args.typeshed_dir, args.commit) + else: + commit = update_typeshed(args.typeshed_dir, args.commit) - assert commit + assert commit - # Create a commit - message = textwrap.dedent( - f"""\ - Sync typeshed + # Create a commit + message = textwrap.dedent( + f"""\ + Sync typeshed - Source commit: - https://github.com/python/typeshed/commit/{commit} - """ - ) - subprocess.run(["git", "add", "--all", os.path.join("mypy", "typeshed")], check=True) - subprocess.run(["git", "commit", "-m", message], check=True) - print("Created typeshed sync commit.") - - commits_to_cherry_pick = [ - "5c00e362d", # LiteralString reverts - "44bc98bd5", # sum reverts - "61a490091", # ctypes reverts - ] - for commit in commits_to_cherry_pick: - try: - subprocess.run(["git", "cherry-pick", commit], check=True) - except subprocess.CalledProcessError: - if not sys.__stdin__.isatty(): - # We're in an automated context - raise - - # Allow the option to merge manually - print( - f"Commit {commit} failed to cherry pick." - " In a separate shell, please manually merge and continue cherry pick." - ) - rsp = input("Did you finish the cherry pick? [y/N]: ") - if rsp.lower() not in {"y", "yes"}: - raise - print(f"Cherry-picked {commit}.") + Source commit: + https://github.com/python/typeshed/commit/{commit} + """ + ) + subprocess.run(["git", "add", "--all", os.path.join("mypy", "typeshed")], check=True) + subprocess.run(["git", "commit", "-m", message], check=True) + print("Created typeshed sync commit.") + + patches = sorted(glob.glob(os.path.join(typeshed_patches, "*.patch"))) + for patch in patches: + cmd = ["git", "am", "--3way", patch] + try: + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"\n\nFailed to apply patch {patch}\n" + "1. Resolve the conflict, `git add --update`, then run `git am --continue`\n" + "2. Run `git format-patch -1 -o misc/typeshed_patches ` " + "to update the patch file.\n" + "3. Re-run sync-typeshed.py" + ) from e + + print(f"Applied patch {patch}") if args.make_pr: subprocess.run(["git", "push", "--force", "origin", branch_name], check=True) diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch new file mode 100644 index 000000000000..6a0977dfc489 --- /dev/null +++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch @@ -0,0 +1,182 @@ +From 5c00e362d40aa26e0a22a740f05a52d05edf0f91 Mon Sep 17 00:00:00 2001 +From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> +Date: Mon, 26 Sep 2022 12:55:07 -0700 +Subject: [PATCH] Remove use of LiteralString in builtins (#13743) + +--- + mypy/typeshed/stdlib/builtins.pyi | 88 ------------------------------- + 1 file changed, 88 deletions(-) + +diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi +index b4765b26c..99919c64c 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 + from typing_extensions import ( # noqa: Y023 + Concatenate, + Literal, +- LiteralString, + ParamSpec, + Self, + TypeAlias, +@@ -434,31 +433,16 @@ class str(Sequence[str]): + def __new__(cls, object: object = ...) -> Self: ... + @overload + def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... +- @overload +- def capitalize(self: LiteralString) -> LiteralString: ... +- @overload + def capitalize(self) -> str: ... # type: ignore[misc] +- @overload +- def casefold(self: LiteralString) -> LiteralString: ... +- @overload + def casefold(self) -> str: ... # type: ignore[misc] +- @overload +- def center(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... +- @overload + def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] + def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... + def endswith( + self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + ) -> bool: ... +- @overload +- def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... +- @overload + def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] + def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... +- @overload +- def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... +- @overload + def format(self, *args: object, **kwargs: object) -> str: ... + def format_map(self, map: _FormatMapMapping) -> str: ... + def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... +@@ -474,89 +458,32 @@ class str(Sequence[str]): + def isspace(self) -> bool: ... + def istitle(self) -> bool: ... + def isupper(self) -> bool: ... +- @overload +- def join(self: LiteralString, iterable: Iterable[LiteralString], /) -> LiteralString: ... +- @overload + def join(self, iterable: Iterable[str], /) -> str: ... # type: ignore[misc] +- @overload +- def ljust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... +- @overload + def ljust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] +- @overload +- def lower(self: LiteralString) -> LiteralString: ... +- @overload + def lower(self) -> str: ... # type: ignore[misc] +- @overload +- def lstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... +- @overload + def lstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] +- @overload +- def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... +- @overload + def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] +- @overload +- def replace(self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /) -> LiteralString: ... +- @overload + def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] + if sys.version_info >= (3, 9): +- @overload +- def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... +- @overload + def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] +- @overload +- def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ... +- @overload + def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] + + def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... +- @overload +- def rjust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... +- @overload + def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] +- @overload +- def rpartition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... +- @overload + def rpartition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] +- @overload +- def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... +- @overload + def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] +- @overload +- def rstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... +- @overload + def rstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] +- @overload +- def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ... +- @overload + def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] +- @overload +- def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ... +- @overload + def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] + def startswith( + self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + ) -> bool: ... +- @overload +- def strip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... +- @overload + def strip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] +- @overload +- def swapcase(self: LiteralString) -> LiteralString: ... +- @overload + def swapcase(self) -> str: ... # type: ignore[misc] +- @overload +- def title(self: LiteralString) -> LiteralString: ... +- @overload + def title(self) -> str: ... # type: ignore[misc] + def translate(self, table: _TranslateTable, /) -> str: ... +- @overload +- def upper(self: LiteralString) -> LiteralString: ... +- @overload + def upper(self) -> str: ... # type: ignore[misc] +- @overload +- def zfill(self: LiteralString, width: SupportsIndex, /) -> LiteralString: ... +- @overload + def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] + @staticmethod + @overload +@@ -567,9 +494,6 @@ class str(Sequence[str]): + @staticmethod + @overload + def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... +- @overload +- def __add__(self: LiteralString, value: LiteralString, /) -> LiteralString: ... +- @overload + def __add__(self, value: str, /) -> str: ... # type: ignore[misc] + # Incompatible with Sequence.__contains__ + def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] +@@ -578,25 +502,13 @@ class str(Sequence[str]): + def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... + def __gt__(self, value: str, /) -> bool: ... + def __hash__(self) -> int: ... +- @overload +- def __iter__(self: LiteralString) -> Iterator[LiteralString]: ... +- @overload + def __iter__(self) -> Iterator[str]: ... # type: ignore[misc] + def __le__(self, value: str, /) -> bool: ... + def __len__(self) -> int: ... + def __lt__(self, value: str, /) -> bool: ... +- @overload +- def __mod__(self: LiteralString, value: LiteralString | tuple[LiteralString, ...], /) -> LiteralString: ... +- @overload + def __mod__(self, value: Any, /) -> str: ... +- @overload +- def __mul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... +- @overload + def __mul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] + def __ne__(self, value: object, /) -> bool: ... +- @overload +- def __rmul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ... +- @overload + def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] + def __getnewargs__(self) -> tuple[str]: ... + +-- +2.39.3 (Apple Git-146) + diff --git a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch new file mode 100644 index 000000000000..044e672bfda5 --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch @@ -0,0 +1,36 @@ +From 44bc98bd50e7170887f0740b53ed95a8eb04f00e Mon Sep 17 00:00:00 2001 +From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> +Date: Sat, 29 Oct 2022 12:47:21 -0700 +Subject: [PATCH] Revert sum literal integer change (#13961) + +This is allegedly causing large performance problems, see 13821 + +typeshed/8231 had zero hits on mypy_primer, so it's not the worst thing +to undo. Patching this in typeshed also feels weird, since there's a +more general soundness issue. If a typevar has a bound or constraint, we +might not want to solve it to a Literal. + +If we can confirm the performance regression or fix the unsoundness +within mypy, I might pursue upstreaming this in typeshed. + +(Reminder: add this to the sync_typeshed script once merged) +--- + mypy/typeshed/stdlib/builtins.pyi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi +index 99919c64c..680cd5561 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -1596,7 +1596,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit + # without creating many false-positive errors (see #7578). + # Instead, we special-case the most common examples of this: bool and literal integers. + @overload +-def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] ++def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] + @overload + def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... + @overload +-- +2.39.3 (Apple Git-146) + diff --git a/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch new file mode 100644 index 000000000000..27066bf3c25b --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch @@ -0,0 +1,32 @@ +From 61a490091d7c941780919660dc4fdfa88ae6474a Mon Sep 17 00:00:00 2001 +From: AlexWaygood +Date: Mon, 1 May 2023 20:34:55 +0100 +Subject: [PATCH] Revert typeshed ctypes change Since the plugin provides + superior type checking: + https://github.com/python/mypy/pull/13987#issuecomment-1310863427 A manual + cherry-pick of e437cdf. + +--- + mypy/typeshed/stdlib/_ctypes.pyi | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi +index 60bbc51d9..cf9cb81a4 100644 +--- a/mypy/typeshed/stdlib/_ctypes.pyi ++++ b/mypy/typeshed/stdlib/_ctypes.pyi +@@ -169,11 +169,7 @@ class Array(_CData, Generic[_CT]): + def _type_(self) -> type[_CT]: ... + @_type_.setter + def _type_(self, value: type[_CT]) -> None: ... +- # Note: only available if _CT == c_char +- @property +- def raw(self) -> bytes: ... +- @raw.setter +- def raw(self, value: ReadableBuffer) -> None: ... ++ raw: bytes # Note: only available if _CT == c_char + value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise + # TODO These methods cannot be annotated correctly at the moment. + # All of these "Any"s stand for the array's element type, but it's not possible to use _CT +-- +2.39.3 (Apple Git-146) + From 433e8c92947fc2673084561b4c0d76db4d4a4aa4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 25 Mar 2024 12:47:46 +0000 Subject: [PATCH 0548/1617] [mypyc] Refactor: add two list primitive ops (#17058) Add ops for getting list size and a pointer to list item data. This simplifies the IR generated in the main irbuild pass. Continue work on mypyc/mypyc#854. --- mypyc/irbuild/ll_builder.py | 19 +- mypyc/lower/list_ops.py | 15 +- mypyc/lower/misc_ops.py | 12 + mypyc/lower/registry.py | 3 +- mypyc/primitives/list_ops.py | 10 + mypyc/primitives/misc_ops.py | 8 + mypyc/test-data/irbuild-any.test | 9 +- mypyc/test-data/irbuild-basic.test | 390 +++++++++++------------- mypyc/test-data/irbuild-bool.test | 19 +- mypyc/test-data/irbuild-bytes.test | 13 +- mypyc/test-data/irbuild-classes.test | 25 +- mypyc/test-data/irbuild-dict.test | 17 +- mypyc/test-data/irbuild-generics.test | 7 +- mypyc/test-data/irbuild-i64.test | 178 +++++------ mypyc/test-data/irbuild-lists.test | 353 ++++++++++----------- mypyc/test-data/irbuild-set.test | 216 +++++++------ mypyc/test-data/irbuild-statements.test | 268 ++++++++-------- mypyc/test-data/irbuild-str.test | 23 +- mypyc/test-data/irbuild-tuple.test | 206 ++++++------- mypyc/test-data/refcount.test | 71 ++--- 20 files changed, 883 insertions(+), 979 deletions(-) create mode 100644 mypyc/lower/misc_ops.py diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 134265852b2f..a05040e25f76 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -78,10 +78,8 @@ int_op_to_id, ) from mypyc.ir.rtypes import ( - PyListObject, PyObject, PySetObject, - PyVarObject, RArray, RInstance, RPrimitive, @@ -163,8 +161,14 @@ ssize_t_to_int_op, uint8_overflow, ) -from mypyc.primitives.list_ops import list_build_op, list_extend_op, new_list_op -from mypyc.primitives.misc_ops import bool_op, buf_init_item, fast_isinstance_op, none_object_op +from mypyc.primitives.list_ops import list_build_op, list_extend_op, list_items, new_list_op +from mypyc.primitives.misc_ops import ( + bool_op, + buf_init_item, + fast_isinstance_op, + none_object_op, + var_object_size, +) from mypyc.primitives.registry import ( ERR_NEG_INT, CFunctionDescription, @@ -1623,8 +1627,7 @@ def new_list_op(self, values: list[Value], line: int) -> Value: if not values: return result_list args = [self.coerce(item, object_rprimitive, line) for item in values] - ob_item_ptr = self.add(GetElementPtr(result_list, PyListObject, "ob_item", line)) - ob_item_base = self.add(LoadMem(pointer_rprimitive, ob_item_ptr, line)) + ob_item_base = self.add(PrimitiveOp([result_list], list_items, line)) for i in range(len(values)): self.primitive_op( buf_init_item, [ob_item_base, Integer(i, c_pyssize_t_rprimitive), args[i]], line @@ -2165,9 +2168,7 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val typ = val.type size_value = None if is_list_rprimitive(typ) or is_tuple_rprimitive(typ) or is_bytes_rprimitive(typ): - elem_address = self.add(GetElementPtr(val, PyVarObject, "ob_size")) - size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) - self.add(KeepAlive([val])) + size_value = self.primitive_op(var_object_size, [val], line) elif is_set_rprimitive(typ): elem_address = self.add(GetElementPtr(val, PySetObject, "used")) size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py index f4619e07dc7e..0d2e3e7169d8 100644 --- a/mypyc/lower/list_ops.py +++ b/mypyc/lower/list_ops.py @@ -1,8 +1,13 @@ from __future__ import annotations from mypyc.common import PLATFORM_SIZE -from mypyc.ir.ops import Integer, IntOp, SetMem, Value -from mypyc.ir.rtypes import c_pyssize_t_rprimitive, object_rprimitive, pointer_rprimitive +from mypyc.ir.ops import GetElementPtr, Integer, IntOp, LoadMem, SetMem, Value +from mypyc.ir.rtypes import ( + PyListObject, + c_pyssize_t_rprimitive, + object_rprimitive, + pointer_rprimitive, +) from mypyc.irbuild.ll_builder import LowLevelIRBuilder from mypyc.lower.registry import lower_primitive_op @@ -32,3 +37,9 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V ) ) return builder.add(SetMem(object_rprimitive, ptr, value, line)) + + +@lower_primitive_op("list_items") +def list_items(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + ob_item_ptr = builder.add(GetElementPtr(args[0], PyListObject, "ob_item", line)) + return builder.add(LoadMem(pointer_rprimitive, ob_item_ptr, line)) diff --git a/mypyc/lower/misc_ops.py b/mypyc/lower/misc_ops.py new file mode 100644 index 000000000000..1effcd4f42ac --- /dev/null +++ b/mypyc/lower/misc_ops.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from mypyc.ir.ops import GetElementPtr, LoadMem, Value +from mypyc.ir.rtypes import PyVarObject, c_pyssize_t_rprimitive +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.lower.registry import lower_primitive_op + + +@lower_primitive_op("var_object_size") +def var_object_size(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + elem_address = builder.add(GetElementPtr(args[0], PyVarObject, "ob_size")) + return builder.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py index d1599dc98cf4..084d57df4608 100644 --- a/mypyc/lower/registry.py +++ b/mypyc/lower/registry.py @@ -23,5 +23,4 @@ def wrapper(f: LowerFunc) -> LowerFunc: # Import various modules that set up global state. -import mypyc.lower.int_ops -import mypyc.lower.list_ops # noqa: F401 +from mypyc.lower import int_ops, list_ops, misc_ops # noqa: F401 diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 7fe3157f3a38..cb75e19a8dea 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -11,12 +11,14 @@ int_rprimitive, list_rprimitive, object_rprimitive, + pointer_rprimitive, short_int_rprimitive, ) from mypyc.primitives.registry import ( ERR_NEG_INT, binary_op, custom_op, + custom_primitive_op, function_op, load_address_op, method_op, @@ -60,6 +62,14 @@ steals=True, ) +# Get pointer to list items (ob_item PyListObject field) +list_items = custom_primitive_op( + name="list_items", + arg_types=[list_rprimitive], + return_type=pointer_rprimitive, + error_kind=ERR_NEVER, +) + # list[index] (for an integer index) list_get_item_op = method_op( name="__getitem__", diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 87d009f7bbab..fea62bbb19c4 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -257,3 +257,11 @@ error_kind=ERR_NEVER, steals=[False, False, True], ) + +# Get length of PyVarObject instance (e.g. list or tuple) +var_object_size = custom_primitive_op( + name="var_object_size", + arg_types=[object_rprimitive], + return_type=c_pyssize_t_rprimitive, + error_kind=ERR_NEVER, +) diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index dd1931ba40f3..0d14e1a5dfc8 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -106,7 +106,7 @@ def f2(a, n, l): r9, r10 :: bit r11 :: list r12 :: object - r13, r14 :: ptr + r13 :: ptr L0: r0 = box(int, n) r1 = PyObject_GetItem(a, r0) @@ -121,10 +121,9 @@ L0: r10 = CPyList_SetItem(l, n, a) r11 = PyList_New(2) r12 = box(int, n) - r13 = get_element_ptr r11 ob_item :: PyListObject - r14 = load_mem r13 :: ptr* - buf_init_item r14, 0, a - buf_init_item r14, 1, r12 + r13 = list_items r11 + buf_init_item r13, 0, a + buf_init_item r13, 1, r12 keep_alive r11 return 1 def f3(a, n): diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 766e584d4149..11df241b5074 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -761,21 +761,20 @@ def g(y): r0 :: None r1 :: list r2 :: object - r3, r4 :: ptr - r5 :: None - r6 :: object - r7 :: None + r3 :: ptr + r4 :: None + r5 :: object + r6 :: None L0: r0 = g(y) r1 = PyList_New(1) r2 = object 1 - r3 = get_element_ptr r1 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r2 + r3 = list_items r1 + buf_init_item r3, 0, r2 keep_alive r1 - r5 = g(r1) - r6 = box(None, 1) - r7 = g(r6) + r4 = g(r1) + r5 = box(None, 1) + r6 = g(r5) return 1 [case testCoerceToObject1] @@ -789,28 +788,27 @@ def g(y: object) -> object: def g(y): y, r0, r1 :: object r2 :: list - r3, r4 :: ptr + r3 :: ptr a :: list - r5 :: tuple[int, int] - r6 :: object - r7 :: bit - r8, r9 :: object + r4 :: tuple[int, int] + r5 :: object + r6 :: bit + r7, r8 :: object L0: r0 = object 1 r1 = g(r0) r2 = PyList_New(1) - r3 = get_element_ptr r2 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, y + r3 = list_items r2 + buf_init_item r3, 0, y keep_alive r2 a = r2 - r5 = (2, 4) - r6 = box(tuple[int, int], r5) - r7 = CPyList_SetItem(a, 0, r6) - r8 = box(bool, 1) - y = r8 - r9 = object 3 - return r9 + r4 = (2, 4) + r5 = box(tuple[int, int], r4) + r6 = CPyList_SetItem(a, 0, r5) + r7 = box(bool, 1) + y = r7 + r8 = object 3 + return r8 [case testCoerceToObject2] class A: @@ -1258,17 +1256,14 @@ L3: unreachable def lst(x): x :: list - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: bit + r0 :: native_int + r1 :: short_int + r2 :: bit L0: - r0 = get_element_ptr x ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive x - r2 = r1 << 1 - r3 = int_ne r2, 0 - if r3 goto L1 else goto L2 :: bool + r0 = var_object_size x + r1 = r0 << 1 + r2 = int_ne r1, 0 + if r2 goto L1 else goto L2 :: bool L1: return 2 L2: @@ -1661,12 +1656,12 @@ def h(): r3 :: object r4 :: list r5 :: object - r6, r7 :: ptr - r8, r9 :: object - r10 :: tuple - r11 :: dict - r12 :: object - r13 :: tuple[int, int, int] + r6 :: ptr + r7, r8 :: object + r9 :: tuple + r10 :: dict + r11 :: object + r12 :: tuple[int, int, int] L0: r0 = (4, 6) r1 = __main__.globals :: static @@ -1674,17 +1669,16 @@ L0: r3 = CPyDict_GetItem(r1, r2) r4 = PyList_New(1) r5 = object 1 - r6 = get_element_ptr r4 ob_item :: PyListObject - r7 = load_mem r6 :: ptr* - buf_init_item r7, 0, r5 + r6 = list_items r4 + buf_init_item r6, 0, r5 keep_alive r4 - r8 = box(tuple[int, int], r0) - r9 = CPyList_Extend(r4, r8) - r10 = PyList_AsTuple(r4) - r11 = PyDict_New() - r12 = PyObject_Call(r3, r10, r11) - r13 = unbox(tuple[int, int, int], r12) - return r13 + r7 = box(tuple[int, int], r0) + r8 = CPyList_Extend(r4, r7) + r9 = PyList_AsTuple(r4) + r10 = PyDict_New() + r11 = PyObject_Call(r3, r9, r10) + r12 = unbox(tuple[int, int, int], r11) + return r12 [case testStar2Args] from typing import Tuple @@ -1849,61 +1843,57 @@ def f() -> List[int]: def f(): r0, r1 :: list r2, r3, r4 :: object - r5, r6 :: ptr - r7 :: short_int - r8 :: ptr - r9 :: native_int - r10 :: short_int - r11 :: bit - r12 :: object - r13, x :: int - r14, r15 :: bit - r16 :: int - r17 :: object - r18 :: i32 - r19 :: bit - r20 :: short_int + r5 :: ptr + r6 :: short_int + r7 :: native_int + r8 :: short_int + r9 :: bit + r10 :: object + r11, x :: int + r12, r13 :: bit + r14 :: int + r15 :: object + r16 :: i32 + r17 :: bit + r18 :: short_int L0: r0 = PyList_New(0) r1 = PyList_New(3) r2 = object 1 r3 = object 2 r4 = object 3 - r5 = get_element_ptr r1 ob_item :: PyListObject - r6 = load_mem r5 :: ptr* - buf_init_item r6, 0, r2 - buf_init_item r6, 1, r3 - buf_init_item r6, 2, r4 + r5 = list_items r1 + buf_init_item r5, 0, r2 + buf_init_item r5, 1, r3 + buf_init_item r5, 2, r4 keep_alive r1 - r7 = 0 + r6 = 0 L1: - r8 = get_element_ptr r1 ob_size :: PyVarObject - r9 = load_mem r8 :: native_int* - keep_alive r1 - r10 = r9 << 1 - r11 = int_lt r7, r10 - if r11 goto L2 else goto L8 :: bool + r7 = var_object_size r1 + r8 = r7 << 1 + r9 = int_lt r6, r8 + if r9 goto L2 else goto L8 :: bool L2: - r12 = CPyList_GetItemUnsafe(r1, r7) - r13 = unbox(int, r12) - x = r13 - r14 = int_ne x, 4 - if r14 goto L4 else goto L3 :: bool + r10 = CPyList_GetItemUnsafe(r1, r6) + r11 = unbox(int, r10) + x = r11 + r12 = int_ne x, 4 + if r12 goto L4 else goto L3 :: bool L3: goto L7 L4: - r15 = int_ne x, 6 - if r15 goto L6 else goto L5 :: bool + r13 = int_ne x, 6 + if r13 goto L6 else goto L5 :: bool L5: goto L7 L6: - r16 = CPyTagged_Multiply(x, x) - r17 = box(int, r16) - r18 = PyList_Append(r0, r17) - r19 = r18 >= 0 :: signed + r14 = CPyTagged_Multiply(x, x) + r15 = box(int, r14) + r16 = PyList_Append(r0, r15) + r17 = r16 >= 0 :: signed L7: - r20 = r7 + 2 - r7 = r20 + r18 = r6 + 2 + r6 = r18 goto L1 L8: return r0 @@ -1917,62 +1907,58 @@ def f(): r0 :: dict r1 :: list r2, r3, r4 :: object - r5, r6 :: ptr - r7 :: short_int - r8 :: ptr - r9 :: native_int - r10 :: short_int - r11 :: bit - r12 :: object - r13, x :: int - r14, r15 :: bit - r16 :: int - r17, r18 :: object - r19 :: i32 - r20 :: bit - r21 :: short_int + r5 :: ptr + r6 :: short_int + r7 :: native_int + r8 :: short_int + r9 :: bit + r10 :: object + r11, x :: int + r12, r13 :: bit + r14 :: int + r15, r16 :: object + r17 :: i32 + r18 :: bit + r19 :: short_int L0: r0 = PyDict_New() r1 = PyList_New(3) r2 = object 1 r3 = object 2 r4 = object 3 - r5 = get_element_ptr r1 ob_item :: PyListObject - r6 = load_mem r5 :: ptr* - buf_init_item r6, 0, r2 - buf_init_item r6, 1, r3 - buf_init_item r6, 2, r4 + r5 = list_items r1 + buf_init_item r5, 0, r2 + buf_init_item r5, 1, r3 + buf_init_item r5, 2, r4 keep_alive r1 - r7 = 0 + r6 = 0 L1: - r8 = get_element_ptr r1 ob_size :: PyVarObject - r9 = load_mem r8 :: native_int* - keep_alive r1 - r10 = r9 << 1 - r11 = int_lt r7, r10 - if r11 goto L2 else goto L8 :: bool + r7 = var_object_size r1 + r8 = r7 << 1 + r9 = int_lt r6, r8 + if r9 goto L2 else goto L8 :: bool L2: - r12 = CPyList_GetItemUnsafe(r1, r7) - r13 = unbox(int, r12) - x = r13 - r14 = int_ne x, 4 - if r14 goto L4 else goto L3 :: bool + r10 = CPyList_GetItemUnsafe(r1, r6) + r11 = unbox(int, r10) + x = r11 + r12 = int_ne x, 4 + if r12 goto L4 else goto L3 :: bool L3: goto L7 L4: - r15 = int_ne x, 6 - if r15 goto L6 else goto L5 :: bool + r13 = int_ne x, 6 + if r13 goto L6 else goto L5 :: bool L5: goto L7 L6: - r16 = CPyTagged_Multiply(x, x) - r17 = box(int, x) - r18 = box(int, r16) - r19 = CPyDict_SetItem(r0, r17, r18) - r20 = r19 >= 0 :: signed + r14 = CPyTagged_Multiply(x, x) + r15 = box(int, x) + r16 = box(int, r14) + r17 = CPyDict_SetItem(r0, r15, r16) + r18 = r17 >= 0 :: signed L7: - r21 = r7 + 2 - r7 = r21 + r19 = r6 + 2 + r6 = r19 goto L1 L8: return r0 @@ -1987,82 +1973,73 @@ def f(l: List[Tuple[int, int, int]]) -> List[int]: def f(l): l :: list r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6 :: tuple[int, int, int] - r7, x, r8, y, r9, z :: int - r10 :: short_int - r11 :: ptr - r12 :: native_int - r13 :: list + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5 :: tuple[int, int, int] + r6, x, r7, y, r8, z :: int + r9 :: short_int + r10 :: native_int + r11 :: list + r12 :: short_int + r13 :: native_int r14 :: short_int - r15 :: ptr - r16 :: native_int - r17 :: short_int - r18 :: bit - r19 :: object - r20 :: tuple[int, int, int] - r21, x_2, r22, y_2, r23, z_2, r24, r25 :: int - r26 :: object - r27 :: bit - r28 :: short_int + r15 :: bit + r16 :: object + r17 :: tuple[int, int, int] + r18, x_2, r19, y_2, r20, z_2, r21, r22 :: int + r23 :: object + r24 :: bit + r25 :: short_int L0: r0 = 0 L1: - r1 = get_element_ptr l ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive l - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size l + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(l, r0) - r6 = unbox(tuple[int, int, int], r5) - r7 = r6[0] - x = r7 - r8 = r6[1] - y = r8 - r9 = r6[2] - z = r9 + r4 = CPyList_GetItemUnsafe(l, r0) + r5 = unbox(tuple[int, int, int], r4) + r6 = r5[0] + x = r6 + r7 = r5[1] + y = r7 + r8 = r5[2] + z = r8 L3: - r10 = r0 + 2 - r0 = r10 + r9 = r0 + 2 + r0 = r9 goto L1 L4: - r11 = get_element_ptr l ob_size :: PyVarObject - r12 = load_mem r11 :: native_int* - keep_alive l - r13 = PyList_New(r12) - r14 = 0 + r10 = var_object_size l + r11 = PyList_New(r10) + r12 = 0 L5: - r15 = get_element_ptr l ob_size :: PyVarObject - r16 = load_mem r15 :: native_int* - keep_alive l - r17 = r16 << 1 - r18 = int_lt r14, r17 - if r18 goto L6 else goto L8 :: bool + r13 = var_object_size l + r14 = r13 << 1 + r15 = int_lt r12, r14 + if r15 goto L6 else goto L8 :: bool L6: - r19 = CPyList_GetItemUnsafe(l, r14) - r20 = unbox(tuple[int, int, int], r19) - r21 = r20[0] - x_2 = r21 - r22 = r20[1] - y_2 = r22 - r23 = r20[2] - z_2 = r23 - r24 = CPyTagged_Add(x_2, y_2) - r25 = CPyTagged_Add(r24, z_2) - r26 = box(int, r25) - r27 = CPyList_SetItemUnsafe(r13, r14, r26) + r16 = CPyList_GetItemUnsafe(l, r12) + r17 = unbox(tuple[int, int, int], r16) + r18 = r17[0] + x_2 = r18 + r19 = r17[1] + y_2 = r19 + r20 = r17[2] + z_2 = r20 + r21 = CPyTagged_Add(x_2, y_2) + r22 = CPyTagged_Add(r21, z_2) + r23 = box(int, r22) + r24 = CPyList_SetItemUnsafe(r11, r12, r23) L7: - r28 = r14 + 2 - r14 = r28 + r25 = r12 + 2 + r12 = r25 goto L5 L8: - return r13 + return r11 [case testProperty] class PropertyHolder: @@ -2204,11 +2181,11 @@ def __top_level__(): r59 :: bit r60 :: list r61, r62, r63 :: object - r64, r65 :: ptr - r66 :: dict - r67 :: str - r68 :: i32 - r69 :: bit + r64 :: ptr + r65 :: dict + r66 :: str + r67 :: i32 + r68 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2279,16 +2256,15 @@ L2: r61 = object 1 r62 = object 2 r63 = object 3 - r64 = get_element_ptr r60 ob_item :: PyListObject - r65 = load_mem r64 :: ptr* - buf_init_item r65, 0, r61 - buf_init_item r65, 1, r62 - buf_init_item r65, 2, r63 + r64 = list_items r60 + buf_init_item r64, 0, r61 + buf_init_item r64, 1, r62 + buf_init_item r64, 2, r63 keep_alive r60 - r66 = __main__.globals :: static - r67 = 'y' - r68 = CPyDict_SetItem(r66, r67, r60) - r69 = r68 >= 0 :: signed + r65 = __main__.globals :: static + r66 = 'y' + r67 = CPyDict_SetItem(r65, r66, r60) + r68 = r67 >= 0 :: signed return 1 [case testChainedConditional] diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index 795a3360fcd2..128266e6b1d7 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -87,17 +87,14 @@ L0: return 1 def list_to_bool(l): l :: list - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: bit -L0: - r0 = get_element_ptr l ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive l - r2 = r1 << 1 - r3 = int_ne r2, 0 - return r3 + r0 :: native_int + r1 :: short_int + r2 :: bit +L0: + r0 = var_object_size l + r1 = r0 << 1 + r2 = int_ne r1, 0 + return r2 def always_truthy_instance_to_bool(o): o :: __main__.C r0 :: i32 diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index 8e97a7f4a569..b41836d8829f 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -140,15 +140,12 @@ def f(b: bytes) -> int: [out] def f(b): b :: bytes - r0 :: ptr - r1 :: native_int - r2 :: short_int + r0 :: native_int + r1 :: short_int L0: - r0 = get_element_ptr b ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive b - r2 = r1 << 1 - return r2 + r0 = var_object_size b + r1 = r0 << 1 + return r1 [case testBytesFormatting] def f(var: bytes, num: int) -> None: diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index cbed51ebcfb0..2c15f09c9c34 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -41,28 +41,27 @@ def f(): r0, c :: __main__.C r1 :: bool r2 :: list - r3, r4 :: ptr + r3 :: ptr a :: list - r5 :: object - r6, d :: __main__.C - r7, r8 :: int + r4 :: object + r5, d :: __main__.C + r6, r7 :: int L0: r0 = C() c = r0 c.x = 10; r1 = is_error r2 = PyList_New(1) - r3 = get_element_ptr r2 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, c + r3 = list_items r2 + buf_init_item r3, 0, c keep_alive r2 a = r2 - r5 = CPyList_GetItemShort(a, 0) - r6 = cast(__main__.C, r5) - d = r6 - r7 = borrow d.x - r8 = CPyTagged_Add(r7, 2) + r4 = CPyList_GetItemShort(a, 0) + r5 = cast(__main__.C, r4) + d = r5 + r6 = borrow d.x + r7 = CPyTagged_Add(r6, 2) keep_alive d - return r8 + return r7 [case testMethodCall] class A: diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 9445219a08ce..6139a02029b9 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -537,8 +537,8 @@ def f3(d, flag): r2 :: str r3 :: list r4 :: object - r5, r6 :: ptr - r7, r8 :: object + r5 :: ptr + r6, r7 :: object L0: if flag goto L1 else goto L2 :: bool L1: @@ -549,15 +549,14 @@ L2: r2 = 'a' r3 = PyList_New(1) r4 = object 1 - r5 = get_element_ptr r3 ob_item :: PyListObject - r6 = load_mem r5 :: ptr* - buf_init_item r6, 0, r4 + r5 = list_items r3 + buf_init_item r5, 0, r4 keep_alive r3 - r7 = CPyDict_SetDefault(d, r2, r3) - return r7 + r6 = CPyDict_SetDefault(d, r2, r3) + return r6 L3: - r8 = box(None, 1) - return r8 + r7 = box(None, 1) + return r7 def f4(d, flag): d :: dict flag :: bool diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 50f6ed6cda1e..4f9d0ab83a16 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -17,13 +17,12 @@ def g(x): x :: list r0 :: object r1 :: list - r2, r3 :: ptr + r2 :: ptr L0: r0 = CPyList_GetItemShort(x, 0) r1 = PyList_New(1) - r2 = get_element_ptr r1 ob_item :: PyListObject - r3 = load_mem r2 :: ptr* - buf_init_item r3, 0, r0 + r2 = list_items r1 + buf_init_item r2, 0, r0 keep_alive r1 return r1 def h(x, y): diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index ad2a97e6eeff..a52de16f3a6c 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1140,24 +1140,23 @@ def f(n): n :: i64 r0 :: __main__.C r1 :: list - r2, r3 :: ptr + r2 :: ptr a :: list - r4 :: object - r5 :: __main__.C - r6 :: str + r3 :: object + r4 :: __main__.C + r5 :: str L0: r0 = C() r1 = PyList_New(1) - r2 = get_element_ptr r1 ob_item :: PyListObject - r3 = load_mem r2 :: ptr* - buf_init_item r3, 0, r0 + r2 = list_items r1 + buf_init_item r2, 0, r0 keep_alive r1 a = r1 - r4 = CPyList_GetItemInt64Borrow(a, n) - r5 = borrow cast(__main__.C, r4) - r6 = r5.s - keep_alive a, n, r4 - return r6 + r3 = CPyList_GetItemInt64Borrow(a, n) + r4 = borrow cast(__main__.C, r3) + r5 = r4.s + keep_alive a, n, r3 + return r5 [case testBorrowOverI64ListGetItem2] from typing import List @@ -1202,19 +1201,16 @@ def g(a: List[i64], y: i64) -> bool: def f(a, y): a :: list y :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: i64 - r4 :: bit + r0 :: native_int + r1 :: short_int + r2 :: i64 + r3 :: bit L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = r3 < y :: signed - if r4 goto L1 else goto L2 :: bool + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = r2 < y :: signed + if r3 goto L1 else goto L2 :: bool L1: return 1 L2: @@ -1222,19 +1218,16 @@ L2: def g(a, y): a :: list y :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: i64 - r4 :: bit + r0 :: native_int + r1 :: short_int + r2 :: i64 + r3 :: bit L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = y < r3 :: signed - if r4 goto L1 else goto L2 :: bool + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = y < r2 :: signed + if r3 goto L1 else goto L2 :: bool L1: return 1 L2: @@ -1251,32 +1244,31 @@ def f(n): n :: i64 r0 :: list r1 :: object - r2, r3 :: ptr - r4, r5 :: bit - r6, r7, r8 :: int - r9 :: list + r2 :: ptr + r3, r4 :: bit + r5, r6, r7 :: int + r8 :: list L0: r0 = PyList_New(1) r1 = box(i64, n) - r2 = get_element_ptr r0 ob_item :: PyListObject - r3 = load_mem r2 :: ptr* - buf_init_item r3, 0, r1 + r2 = list_items r0 + buf_init_item r2, 0, r1 keep_alive r0 - r4 = n <= 4611686018427387903 :: signed - if r4 goto L1 else goto L2 :: bool + r3 = n <= 4611686018427387903 :: signed + if r3 goto L1 else goto L2 :: bool L1: - r5 = n >= -4611686018427387904 :: signed - if r5 goto L3 else goto L2 :: bool + r4 = n >= -4611686018427387904 :: signed + if r4 goto L3 else goto L2 :: bool L2: - r6 = CPyTagged_FromInt64(n) - r7 = r6 + r5 = CPyTagged_FromInt64(n) + r6 = r5 goto L4 L3: - r8 = n << 1 - r7 = r8 + r7 = n << 1 + r6 = r7 L4: - r9 = CPySequence_Multiply(r0, r7) - return r9 + r8 = CPySequence_Multiply(r0, r6) + return r8 [case testShortIntAndI64Op] from mypy_extensions import i64 @@ -1298,49 +1290,40 @@ def lt_i64(a: List[i64], n: i64) -> bool: def add_i64(a, n): a :: list n :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3, r4 :: i64 + r0 :: native_int + r1 :: short_int + r2, r3 :: i64 L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = r3 + n - return r4 + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = r2 + n + return r3 def add_i64_2(a, n): a :: list n :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3, r4 :: i64 + r0 :: native_int + r1 :: short_int + r2, r3 :: i64 L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = n + r3 - return r4 + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = n + r2 + return r3 def eq_i64(a, n): a :: list n :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: i64 - r4 :: bit + r0 :: native_int + r1 :: short_int + r2 :: i64 + r3 :: bit L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = r3 == n - if r4 goto L1 else goto L2 :: bool + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = r2 == n + if r3 goto L1 else goto L2 :: bool L1: return 1 L2: @@ -1348,19 +1331,16 @@ L2: def lt_i64(a, n): a :: list n :: i64 - r0 :: ptr - r1 :: native_int - r2 :: short_int - r3 :: i64 - r4 :: bit + r0 :: native_int + r1 :: short_int + r2 :: i64 + r3 :: bit L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - r3 = r2 >> 1 - r4 = n < r3 :: signed - if r4 goto L1 else goto L2 :: bool + r0 = var_object_size a + r1 = r0 << 1 + r2 = r1 >> 1 + r3 = n < r2 :: signed + if r3 goto L1 else goto L2 :: bool L1: return 1 L2: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 66aa1dc748be..725f218b686a 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -108,16 +108,15 @@ def f() -> None: def f(): r0 :: list r1, r2 :: object - r3, r4 :: ptr + r3 :: ptr x :: list L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 - r3 = get_element_ptr r0 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r1 - buf_init_item r4, 1, r2 + r3 = list_items r0 + buf_init_item r3, 0, r1 + buf_init_item r3, 1, r2 keep_alive r0 x = r0 return 1 @@ -155,19 +154,18 @@ def f(a: List[int]) -> None: def f(a): a, r0, b, r1 :: list r2 :: object - r3, r4 :: ptr - r5 :: list + r3 :: ptr + r4 :: list L0: r0 = CPySequence_Multiply(a, 4) b = r0 r1 = PyList_New(1) r2 = object 4 - r3 = get_element_ptr r1 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r2 + r3 = list_items r1 + buf_init_item r3, 0, r2 keep_alive r1 - r5 = CPySequence_RMultiply(6, r1) - b = r5 + r4 = CPySequence_RMultiply(6, r1) + b = r4 return 1 [case testListLen] @@ -177,15 +175,12 @@ def f(a: List[int]) -> int: [out] def f(a): a :: list - r0 :: ptr - r1 :: native_int - r2 :: short_int + r0 :: native_int + r1 :: short_int L0: - r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive a - r2 = r1 << 1 - return r2 + r0 = var_object_size a + r1 = r0 << 1 + return r1 [case testListAppend] from typing import List @@ -213,33 +208,30 @@ def increment(l: List[int]) -> List[int]: [out] def increment(l): l :: list - r0 :: ptr - r1 :: native_int - r2, r3 :: short_int + r0 :: native_int + r1, r2 :: short_int i :: int - r4 :: bit - r5, r6, r7 :: object - r8 :: bit - r9 :: short_int + r3 :: bit + r4, r5, r6 :: object + r7 :: bit + r8 :: short_int L0: - r0 = get_element_ptr l ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive l - r2 = r1 << 1 - r3 = 0 - i = r3 + r0 = var_object_size l + r1 = r0 << 1 + r2 = 0 + i = r2 L1: - r4 = int_lt r3, r2 - if r4 goto L2 else goto L4 :: bool + r3 = int_lt r2, r1 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItem(l, i) - r6 = object 1 - r7 = PyNumber_InPlaceAdd(r5, r6) - r8 = CPyList_SetItem(l, i, r7) + r4 = CPyList_GetItem(l, i) + r5 = object 1 + r6 = PyNumber_InPlaceAdd(r4, r5) + r7 = CPyList_SetItem(l, i, r6) L3: - r9 = r3 + 2 - r3 = r9 - i = r9 + r8 = r2 + 2 + r2 = r8 + i = r8 goto L1 L4: return l @@ -252,24 +244,23 @@ def f(x: List[int], y: List[int]) -> List[int]: def f(x, y): x, y, r0 :: list r1, r2 :: object - r3, r4 :: ptr - r5, r6, r7 :: object - r8 :: i32 - r9 :: bit + r3 :: ptr + r4, r5, r6 :: object + r7 :: i32 + r8 :: bit L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 - r3 = get_element_ptr r0 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r1 - buf_init_item r4, 1, r2 + r3 = list_items r0 + buf_init_item r3, 0, r1 + buf_init_item r3, 1, r2 keep_alive r0 - r5 = CPyList_Extend(r0, x) - r6 = CPyList_Extend(r0, y) - r7 = object 3 - r8 = PyList_Append(r0, r7) - r9 = r8 >= 0 :: signed + r4 = CPyList_Extend(r0, x) + r5 = CPyList_Extend(r0, y) + r6 = object 3 + r7 = PyList_Append(r0, r6) + r8 = r7 >= 0 :: signed return r0 [case testListIn] @@ -316,85 +307,73 @@ def f(source: List[int]) -> None: [out] def f(source): source :: list - r0 :: ptr - r1 :: native_int - r2 :: list - r3 :: short_int - r4 :: ptr - r5 :: native_int - r6 :: short_int - r7 :: bit - r8 :: object - r9, x, r10 :: int - r11 :: object - r12 :: bit - r13 :: short_int + r0 :: native_int + r1 :: list + r2 :: short_int + r3 :: native_int + r4 :: short_int + r5 :: bit + r6 :: object + r7, x, r8 :: int + r9 :: object + r10 :: bit + r11 :: short_int a :: list - r14 :: ptr + r12 :: native_int + r13 :: list + r14 :: short_int r15 :: native_int - r16 :: list - r17 :: short_int - r18 :: ptr - r19 :: native_int - r20 :: short_int - r21 :: bit - r22 :: object - r23, x_2, r24 :: int - r25 :: object - r26 :: bit - r27 :: short_int + r16 :: short_int + r17 :: bit + r18 :: object + r19, x_2, r20 :: int + r21 :: object + r22 :: bit + r23 :: short_int b :: list L0: - r0 = get_element_ptr source ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive source - r2 = PyList_New(r1) - r3 = 0 + r0 = var_object_size source + r1 = PyList_New(r0) + r2 = 0 L1: - r4 = get_element_ptr source ob_size :: PyVarObject - r5 = load_mem r4 :: native_int* - keep_alive source - r6 = r5 << 1 - r7 = int_lt r3, r6 - if r7 goto L2 else goto L4 :: bool + r3 = var_object_size source + r4 = r3 << 1 + r5 = int_lt r2, r4 + if r5 goto L2 else goto L4 :: bool L2: - r8 = CPyList_GetItemUnsafe(source, r3) - r9 = unbox(int, r8) - x = r9 - r10 = CPyTagged_Add(x, 2) - r11 = box(int, r10) - r12 = CPyList_SetItemUnsafe(r2, r3, r11) + r6 = CPyList_GetItemUnsafe(source, r2) + r7 = unbox(int, r6) + x = r7 + r8 = CPyTagged_Add(x, 2) + r9 = box(int, r8) + r10 = CPyList_SetItemUnsafe(r1, r2, r9) L3: - r13 = r3 + 2 - r3 = r13 + r11 = r2 + 2 + r2 = r11 goto L1 L4: - a = r2 - r14 = get_element_ptr source ob_size :: PyVarObject - r15 = load_mem r14 :: native_int* - keep_alive source - r16 = PyList_New(r15) - r17 = 0 + a = r1 + r12 = var_object_size source + r13 = PyList_New(r12) + r14 = 0 L5: - r18 = get_element_ptr source ob_size :: PyVarObject - r19 = load_mem r18 :: native_int* - keep_alive source - r20 = r19 << 1 - r21 = int_lt r17, r20 - if r21 goto L6 else goto L8 :: bool + r15 = var_object_size source + r16 = r15 << 1 + r17 = int_lt r14, r16 + if r17 goto L6 else goto L8 :: bool L6: - r22 = CPyList_GetItemUnsafe(source, r17) - r23 = unbox(int, r22) - x_2 = r23 - r24 = CPyTagged_Add(x_2, 2) - r25 = box(int, r24) - r26 = CPyList_SetItemUnsafe(r16, r17, r25) + r18 = CPyList_GetItemUnsafe(source, r14) + r19 = unbox(int, r18) + x_2 = r19 + r20 = CPyTagged_Add(x_2, 2) + r21 = box(int, r20) + r22 = CPyList_SetItemUnsafe(r13, r14, r21) L7: - r27 = r17 + 2 - r17 = r27 + r23 = r14 + 2 + r14 = r23 goto L5 L8: - b = r16 + b = r13 return 1 [case testGeneratorNext] @@ -406,42 +385,39 @@ def test(x: List[int]) -> None: def test(x): x :: list r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, i :: int - r7 :: object - r8 :: union[int, None] - r9 :: short_int - r10 :: object + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5, i :: int + r6 :: object + r7 :: union[int, None] + r8 :: short_int + r9 :: object res :: union[int, None] L0: r0 = 0 L1: - r1 = get_element_ptr x ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive x - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size x + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(x, r0) - r6 = unbox(int, r5) - i = r6 - r7 = box(int, i) - r8 = r7 + r4 = CPyList_GetItemUnsafe(x, r0) + r5 = unbox(int, r4) + i = r5 + r6 = box(int, i) + r7 = r6 goto L5 L3: - r9 = r0 + 2 - r0 = r9 + r8 = r0 + 2 + r0 = r8 goto L1 L4: - r10 = box(None, 1) - r8 = r10 + r9 = box(None, 1) + r7 = r9 L5: - res = r8 + res = r7 return 1 [case testSimplifyListUnion] @@ -465,10 +441,9 @@ def narrow(a): r2 :: bit r3 :: bool r4 :: list - r5 :: ptr - r6 :: native_int - r7 :: short_int - r8 :: int + r5 :: native_int + r6 :: short_int + r7 :: int L0: r0 = load_address PyList_Type r1 = PyObject_IsInstance(a, r0) @@ -477,70 +452,62 @@ L0: if r3 goto L1 else goto L2 :: bool L1: r4 = borrow cast(list, a) - r5 = get_element_ptr r4 ob_size :: PyVarObject - r6 = load_mem r5 :: native_int* - keep_alive r4 - r7 = r6 << 1 + r5 = var_object_size r4 + r6 = r5 << 1 keep_alive a - return r7 + return r6 L2: - r8 = unbox(int, a) - return r8 + r7 = unbox(int, a) + return r7 def loop(a): a :: list r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x :: union[str, bytes] - r7 :: short_int + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5, x :: union[str, bytes] + r6 :: short_int L0: r0 = 0 L1: - r1 = get_element_ptr a ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive a - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size a + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(a, r0) - r6 = cast(union[str, bytes], r5) - x = r6 + r4 = CPyList_GetItemUnsafe(a, r0) + r5 = cast(union[str, bytes], r4) + x = r5 L3: - r7 = r0 + 2 - r0 = r7 + r6 = r0 + 2 + r0 = r6 goto L1 L4: return 1 def nested_union(a): a :: list r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x :: union[str, None] - r7 :: short_int + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5, x :: union[str, None] + r6 :: short_int L0: r0 = 0 L1: - r1 = get_element_ptr a ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive a - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size a + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(a, r0) - r6 = cast(union[str, None], r5) - x = r6 + r4 = CPyList_GetItemUnsafe(a, r0) + r5 = cast(union[str, None], r4) + x = r5 L3: - r7 = r0 + 2 - r0 = r7 + r6 = r0 + 2 + r0 = r6 goto L1 L4: return 1 diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index ea900f2e4789..1ac638754a8b 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -79,56 +79,52 @@ L0: def test1(): r0 :: list r1, r2, r3 :: object - r4, r5 :: ptr + r4 :: ptr tmp_list :: list - r6 :: set - r7 :: short_int - r8 :: ptr - r9 :: native_int - r10 :: short_int - r11 :: bit - r12 :: object - r13, x, r14 :: int - r15 :: object - r16 :: i32 - r17 :: bit - r18 :: short_int + r5 :: set + r6 :: short_int + r7 :: native_int + r8 :: short_int + r9 :: bit + r10 :: object + r11, x, r12 :: int + r13 :: object + r14 :: i32 + r15 :: bit + r16 :: short_int a :: set L0: r0 = PyList_New(3) r1 = object 1 r2 = object 3 r3 = object 5 - r4 = get_element_ptr r0 ob_item :: PyListObject - r5 = load_mem r4 :: ptr* - buf_init_item r5, 0, r1 - buf_init_item r5, 1, r2 - buf_init_item r5, 2, r3 + r4 = list_items r0 + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 + buf_init_item r4, 2, r3 keep_alive r0 tmp_list = r0 - r6 = PySet_New(0) - r7 = 0 + r5 = PySet_New(0) + r6 = 0 L1: - r8 = get_element_ptr tmp_list ob_size :: PyVarObject - r9 = load_mem r8 :: native_int* - keep_alive tmp_list - r10 = r9 << 1 - r11 = int_lt r7, r10 - if r11 goto L2 else goto L4 :: bool + r7 = var_object_size tmp_list + r8 = r7 << 1 + r9 = int_lt r6, r8 + if r9 goto L2 else goto L4 :: bool L2: - r12 = CPyList_GetItemUnsafe(tmp_list, r7) - r13 = unbox(int, r12) - x = r13 - r14 = f(x) - r15 = box(int, r14) - r16 = PySet_Add(r6, r15) - r17 = r16 >= 0 :: signed + r10 = CPyList_GetItemUnsafe(tmp_list, r6) + r11 = unbox(int, r10) + x = r11 + r12 = f(x) + r13 = box(int, r12) + r14 = PySet_Add(r5, r13) + r15 = r14 >= 0 :: signed L3: - r18 = r7 + 2 - r7 = r18 + r16 = r6 + 2 + r6 = r16 goto L1 L4: - a = r6 + a = r5 return 1 def test2(): r0, tmp_tuple :: tuple[int, int, int] @@ -310,33 +306,32 @@ L0: def test(): r0 :: list r1, r2, r3, r4, r5 :: object - r6, r7 :: ptr + r6 :: ptr tmp_list :: list - r8 :: set - r9, r10 :: list - r11 :: short_int - r12 :: ptr - r13 :: native_int - r14 :: short_int - r15 :: bit - r16 :: object - r17, z :: int - r18 :: bit - r19 :: int - r20 :: object - r21 :: i32 - r22 :: bit - r23 :: short_int - r24, r25, r26 :: object - r27, y, r28 :: int - r29 :: object - r30 :: i32 - r31, r32 :: bit - r33, r34, r35 :: object - r36, x, r37 :: int - r38 :: object - r39 :: i32 - r40, r41 :: bit + r7 :: set + r8, r9 :: list + r10 :: short_int + r11 :: native_int + r12 :: short_int + r13 :: bit + r14 :: object + r15, z :: int + r16 :: bit + r17 :: int + r18 :: object + r19 :: i32 + r20 :: bit + r21 :: short_int + r22, r23, r24 :: object + r25, y, r26 :: int + r27 :: object + r28 :: i32 + r29, r30 :: bit + r31, r32, r33 :: object + r34, x, r35 :: int + r36 :: object + r37 :: i32 + r38, r39 :: bit a :: set L0: r0 = PyList_New(5) @@ -345,79 +340,76 @@ L0: r3 = object 3 r4 = object 4 r5 = object 5 - r6 = get_element_ptr r0 ob_item :: PyListObject - r7 = load_mem r6 :: ptr* - buf_init_item r7, 0, r1 - buf_init_item r7, 1, r2 - buf_init_item r7, 2, r3 - buf_init_item r7, 3, r4 - buf_init_item r7, 4, r5 + r6 = list_items r0 + buf_init_item r6, 0, r1 + buf_init_item r6, 1, r2 + buf_init_item r6, 2, r3 + buf_init_item r6, 3, r4 + buf_init_item r6, 4, r5 keep_alive r0 tmp_list = r0 - r8 = PySet_New(0) + r7 = PySet_New(0) + r8 = PyList_New(0) r9 = PyList_New(0) - r10 = PyList_New(0) - r11 = 0 + r10 = 0 L1: - r12 = get_element_ptr tmp_list ob_size :: PyVarObject - r13 = load_mem r12 :: native_int* - keep_alive tmp_list - r14 = r13 << 1 - r15 = int_lt r11, r14 - if r15 goto L2 else goto L6 :: bool + r11 = var_object_size tmp_list + r12 = r11 << 1 + r13 = int_lt r10, r12 + if r13 goto L2 else goto L6 :: bool L2: - r16 = CPyList_GetItemUnsafe(tmp_list, r11) - r17 = unbox(int, r16) - z = r17 - r18 = int_lt z, 8 - if r18 goto L4 else goto L3 :: bool + r14 = CPyList_GetItemUnsafe(tmp_list, r10) + r15 = unbox(int, r14) + z = r15 + r16 = int_lt z, 8 + if r16 goto L4 else goto L3 :: bool L3: goto L5 L4: - r19 = f1(z) - r20 = box(int, r19) - r21 = PyList_Append(r10, r20) - r22 = r21 >= 0 :: signed + r17 = f1(z) + r18 = box(int, r17) + r19 = PyList_Append(r9, r18) + r20 = r19 >= 0 :: signed L5: - r23 = r11 + 2 - r11 = r23 + r21 = r10 + 2 + r10 = r21 goto L1 L6: - r24 = PyObject_GetIter(r10) - r25 = PyObject_GetIter(r24) + r22 = PyObject_GetIter(r9) + r23 = PyObject_GetIter(r22) L7: - r26 = PyIter_Next(r25) - if is_error(r26) goto L10 else goto L8 + r24 = PyIter_Next(r23) + if is_error(r24) goto L10 else goto L8 L8: - r27 = unbox(int, r26) - y = r27 - r28 = f2(y) - r29 = box(int, r28) - r30 = PyList_Append(r9, r29) - r31 = r30 >= 0 :: signed + r25 = unbox(int, r24) + y = r25 + r26 = f2(y) + r27 = box(int, r26) + r28 = PyList_Append(r8, r27) + r29 = r28 >= 0 :: signed L9: goto L7 L10: - r32 = CPy_NoErrOccured() + r30 = CPy_NoErrOccured() L11: - r33 = PyObject_GetIter(r9) - r34 = PyObject_GetIter(r33) + r31 = PyObject_GetIter(r8) + r32 = PyObject_GetIter(r31) L12: - r35 = PyIter_Next(r34) - if is_error(r35) goto L15 else goto L13 + r33 = PyIter_Next(r32) + if is_error(r33) goto L15 else goto L13 L13: - r36 = unbox(int, r35) - x = r36 - r37 = f3(x) - r38 = box(int, r37) - r39 = PySet_Add(r8, r38) - r40 = r39 >= 0 :: signed + r34 = unbox(int, r33) + x = r34 + r35 = f3(x) + r36 = box(int, r35) + r37 = PySet_Add(r7, r36) + r38 = r37 >= 0 :: signed L14: goto L12 L15: - r41 = CPy_NoErrOccured() + r39 = CPy_NoErrOccured() L16: - a = r8 + a = r7 return 1 [case testSetSize] diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 628d692c85c1..f9d3354b317c 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -231,32 +231,29 @@ def f(ls): ls :: list y :: int r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x, r7 :: int - r8 :: short_int + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5, x, r6 :: int + r7 :: short_int L0: y = 0 r0 = 0 L1: - r1 = get_element_ptr ls ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive ls - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size ls + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(ls, r0) - r6 = unbox(int, r5) - x = r6 - r7 = CPyTagged_Add(y, x) - y = r7 + r4 = CPyList_GetItemUnsafe(ls, r0) + r5 = unbox(int, r4) + x = r5 + r6 = CPyTagged_Add(y, x) + y = r6 L3: - r8 = r0 + 2 - r0 = r8 + r7 = r0 + 2 + r0 = r7 goto L1 L4: return y @@ -688,39 +685,38 @@ def delListMultiple() -> None: def delList(): r0 :: list r1, r2 :: object - r3, r4 :: ptr + r3 :: ptr l :: list - r5 :: object - r6 :: i32 - r7 :: bit + r4 :: object + r5 :: i32 + r6 :: bit L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 - r3 = get_element_ptr r0 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r1 - buf_init_item r4, 1, r2 + r3 = list_items r0 + buf_init_item r3, 0, r1 + buf_init_item r3, 1, r2 keep_alive r0 l = r0 - r5 = object 1 - r6 = PyObject_DelItem(l, r5) - r7 = r6 >= 0 :: signed + r4 = object 1 + r5 = PyObject_DelItem(l, r4) + r6 = r5 >= 0 :: signed return 1 def delListMultiple(): r0 :: list r1, r2, r3, r4, r5, r6, r7 :: object - r8, r9 :: ptr + r8 :: ptr l :: list - r10 :: object - r11 :: i32 - r12 :: bit - r13 :: object - r14 :: i32 - r15 :: bit - r16 :: object - r17 :: i32 - r18 :: bit + r9 :: object + r10 :: i32 + r11 :: bit + r12 :: object + r13 :: i32 + r14 :: bit + r15 :: object + r16 :: i32 + r17 :: bit L0: r0 = PyList_New(7) r1 = object 1 @@ -730,26 +726,25 @@ L0: r5 = object 5 r6 = object 6 r7 = object 7 - r8 = get_element_ptr r0 ob_item :: PyListObject - r9 = load_mem r8 :: ptr* - buf_init_item r9, 0, r1 - buf_init_item r9, 1, r2 - buf_init_item r9, 2, r3 - buf_init_item r9, 3, r4 - buf_init_item r9, 4, r5 - buf_init_item r9, 5, r6 - buf_init_item r9, 6, r7 + r8 = list_items r0 + buf_init_item r8, 0, r1 + buf_init_item r8, 1, r2 + buf_init_item r8, 2, r3 + buf_init_item r8, 3, r4 + buf_init_item r8, 4, r5 + buf_init_item r8, 5, r6 + buf_init_item r8, 6, r7 keep_alive r0 l = r0 - r10 = object 1 - r11 = PyObject_DelItem(l, r10) - r12 = r11 >= 0 :: signed - r13 = object 2 - r14 = PyObject_DelItem(l, r13) - r15 = r14 >= 0 :: signed - r16 = object 3 - r17 = PyObject_DelItem(l, r16) - r18 = r17 >= 0 :: signed + r9 = object 1 + r10 = PyObject_DelItem(l, r9) + r11 = r10 >= 0 :: signed + r12 = object 2 + r13 = PyObject_DelItem(l, r12) + r14 = r13 >= 0 :: signed + r15 = object 3 + r16 = PyObject_DelItem(l, r15) + r17 = r16 >= 0 :: signed return 1 [case testDelDict] @@ -872,35 +867,32 @@ def f(a): r0 :: short_int i :: int r1 :: short_int - r2 :: ptr - r3 :: native_int - r4 :: short_int - r5 :: bit - r6 :: object - r7, x, r8 :: int - r9, r10 :: short_int + r2 :: native_int + r3 :: short_int + r4 :: bit + r5 :: object + r6, x, r7 :: int + r8, r9 :: short_int L0: r0 = 0 i = 0 r1 = 0 L1: - r2 = get_element_ptr a ob_size :: PyVarObject - r3 = load_mem r2 :: native_int* - keep_alive a - r4 = r3 << 1 - r5 = int_lt r1, r4 - if r5 goto L2 else goto L4 :: bool + r2 = var_object_size a + r3 = r2 << 1 + r4 = int_lt r1, r3 + if r4 goto L2 else goto L4 :: bool L2: - r6 = CPyList_GetItemUnsafe(a, r1) - r7 = unbox(int, r6) - x = r7 - r8 = CPyTagged_Add(i, x) + r5 = CPyList_GetItemUnsafe(a, r1) + r6 = unbox(int, r5) + x = r6 + r7 = CPyTagged_Add(i, x) L3: - r9 = r0 + 2 - r0 = r9 - i = r9 - r10 = r1 + 2 - r1 = r10 + r8 = r0 + 2 + r0 = r8 + i = r8 + r9 = r1 + 2 + r1 = r9 goto L1 L4: L5: @@ -950,50 +942,47 @@ def f(a, b): b :: object r0 :: short_int r1 :: object - r2 :: ptr - r3 :: native_int - r4 :: short_int - r5 :: bit - r6, r7 :: object - r8, x :: int - r9, y :: bool - r10 :: i32 - r11 :: bit - r12 :: bool - r13 :: short_int - r14 :: bit + r2 :: native_int + r3 :: short_int + r4 :: bit + r5, r6 :: object + r7, x :: int + r8, y :: bool + r9 :: i32 + r10 :: bit + r11 :: bool + r12 :: short_int + r13 :: bit L0: r0 = 0 r1 = PyObject_GetIter(b) L1: - r2 = get_element_ptr a ob_size :: PyVarObject - r3 = load_mem r2 :: native_int* - keep_alive a - r4 = r3 << 1 - r5 = int_lt r0, r4 - if r5 goto L2 else goto L7 :: bool + r2 = var_object_size a + r3 = r2 << 1 + r4 = int_lt r0, r3 + if r4 goto L2 else goto L7 :: bool L2: - r6 = PyIter_Next(r1) - if is_error(r6) goto L7 else goto L3 + r5 = PyIter_Next(r1) + if is_error(r5) goto L7 else goto L3 L3: - r7 = CPyList_GetItemUnsafe(a, r0) - r8 = unbox(int, r7) - x = r8 - r9 = unbox(bool, r6) - y = r9 - r10 = PyObject_IsTrue(b) - r11 = r10 >= 0 :: signed - r12 = truncate r10: i32 to builtins.bool - if r12 goto L4 else goto L5 :: bool + r6 = CPyList_GetItemUnsafe(a, r0) + r7 = unbox(int, r6) + x = r7 + r8 = unbox(bool, r5) + y = r8 + r9 = PyObject_IsTrue(b) + r10 = r9 >= 0 :: signed + r11 = truncate r9: i32 to builtins.bool + if r11 goto L4 else goto L5 :: bool L4: x = 2 L5: L6: - r13 = r0 + 2 - r0 = r13 + r12 = r0 + 2 + r0 = r12 goto L1 L7: - r14 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L8: return 1 def g(a, b): @@ -1003,15 +992,14 @@ def g(a, b): r1, r2 :: short_int z :: int r3 :: object - r4 :: ptr - r5 :: native_int - r6 :: short_int - r7, r8 :: bit - r9, x :: bool - r10 :: object - r11, y :: int - r12, r13 :: short_int - r14 :: bit + r4 :: native_int + r5 :: short_int + r6, r7 :: bit + r8, x :: bool + r9 :: object + r10, y :: int + r11, r12 :: short_int + r13 :: bit L0: r0 = PyObject_GetIter(a) r1 = 0 @@ -1021,31 +1009,29 @@ L1: r3 = PyIter_Next(r0) if is_error(r3) goto L6 else goto L2 L2: - r4 = get_element_ptr b ob_size :: PyVarObject - r5 = load_mem r4 :: native_int* - keep_alive b - r6 = r5 << 1 - r7 = int_lt r1, r6 - if r7 goto L3 else goto L6 :: bool + r4 = var_object_size b + r5 = r4 << 1 + r6 = int_lt r1, r5 + if r6 goto L3 else goto L6 :: bool L3: - r8 = int_lt r2, 10 - if r8 goto L4 else goto L6 :: bool + r7 = int_lt r2, 10 + if r7 goto L4 else goto L6 :: bool L4: - r9 = unbox(bool, r3) - x = r9 - r10 = CPyList_GetItemUnsafe(b, r1) - r11 = unbox(int, r10) - y = r11 + r8 = unbox(bool, r3) + x = r8 + r9 = CPyList_GetItemUnsafe(b, r1) + r10 = unbox(int, r9) + y = r10 x = 0 L5: - r12 = r1 + 2 - r1 = r12 - r13 = r2 + 2 - r2 = r13 - z = r13 + r11 = r1 + 2 + r1 = r11 + r12 = r2 + 2 + r2 = r12 + z = r12 goto L1 L6: - r14 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L7: return 1 diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index dfaa50520364..771dcc4c0e68 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -203,8 +203,8 @@ def f(var, num): r12 :: object r13 :: str r14 :: list - r15, r16 :: ptr - r17, s2, r18, s3, r19, s4 :: str + r15 :: ptr + r16, s2, r17, s3, r18, s4 :: str L0: r0 = "Hi! I'm " r1 = '. I am ' @@ -222,17 +222,16 @@ L0: r12 = CPyObject_CallMethodObjArgs(r7, r11, var, r10, 0) r13 = cast(str, r12) r14 = PyList_New(2) - r15 = get_element_ptr r14 ob_item :: PyListObject - r16 = load_mem r15 :: ptr* - buf_init_item r16, 0, r6 - buf_init_item r16, 1, r13 + r15 = list_items r14 + buf_init_item r15, 0, r6 + buf_init_item r15, 1, r13 keep_alive r14 - r17 = PyUnicode_Join(r5, r14) - s2 = r17 - r18 = '' - s3 = r18 - r19 = 'abc' - s4 = r19 + r16 = PyUnicode_Join(r5, r14) + s2 = r16 + r17 = '' + s3 = r17 + r18 = 'abc' + s4 = r18 return 1 [case testStringFormattingCStyle] diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 0a26d8aa1d3d..a6813de4ee44 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -62,15 +62,12 @@ def f(x: Tuple[int, ...]) -> int: [out] def f(x): x :: tuple - r0 :: ptr - r1 :: native_int - r2 :: short_int + r0 :: native_int + r1 :: short_int L0: - r0 = get_element_ptr x ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive x - r2 = r1 << 1 - return r2 + r0 = var_object_size x + r1 = r0 << 1 + return r1 [case testSequenceTupleForced] from typing import Tuple @@ -101,27 +98,26 @@ def f(x, y): x, y :: object r0 :: list r1, r2 :: object - r3, r4 :: ptr - r5, r6, r7 :: object - r8 :: i32 - r9 :: bit - r10 :: tuple + r3 :: ptr + r4, r5, r6 :: object + r7 :: i32 + r8 :: bit + r9 :: tuple L0: r0 = PyList_New(2) r1 = object 1 r2 = object 2 - r3 = get_element_ptr r0 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* - buf_init_item r4, 0, r1 - buf_init_item r4, 1, r2 + r3 = list_items r0 + buf_init_item r3, 0, r1 + buf_init_item r3, 1, r2 keep_alive r0 - r5 = CPyList_Extend(r0, x) - r6 = CPyList_Extend(r0, y) - r7 = object 3 - r8 = PyList_Append(r0, r7) - r9 = r8 >= 0 :: signed - r10 = PyList_AsTuple(r0) - return r10 + r4 = CPyList_Extend(r0, x) + r5 = CPyList_Extend(r0, y) + r6 = object 3 + r7 = PyList_Append(r0, r6) + r8 = r7 >= 0 :: signed + r9 = PyList_AsTuple(r0) + return r9 [case testTupleFor] from typing import Tuple, List @@ -132,29 +128,26 @@ def f(xs: Tuple[str, ...]) -> None: def f(xs): xs :: tuple r0 :: short_int - r1 :: ptr - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x :: str - r7 :: short_int + r1 :: native_int + r2 :: short_int + r3 :: bit + r4 :: object + r5, x :: str + r6 :: short_int L0: r0 = 0 L1: - r1 = get_element_ptr xs ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - keep_alive xs - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L4 :: bool + r1 = var_object_size xs + r2 = r1 << 1 + r3 = int_lt r0, r2 + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPySequenceTuple_GetItem(xs, r0) - r6 = cast(str, r5) - x = r6 + r4 = CPySequenceTuple_GetItem(xs, r0) + r5 = cast(str, r4) + x = r5 L3: - r7 = r0 + 2 - r0 = r7 + r6 = r0 + 2 + r0 = r6 goto L1 L4: return 1 @@ -237,60 +230,53 @@ L0: def test(): r0 :: list r1, r2, r3 :: object - r4, r5 :: ptr + r4 :: ptr source :: list - r6 :: ptr - r7 :: native_int - r8 :: tuple + r5 :: native_int + r6 :: tuple + r7 :: short_int + r8 :: native_int r9 :: short_int - r10 :: ptr - r11 :: native_int - r12 :: short_int - r13 :: bit + r10 :: bit + r11 :: object + r12, x :: int + r13 :: bool r14 :: object - r15, x :: int - r16 :: bool - r17 :: object - r18 :: bit - r19 :: short_int + r15 :: bit + r16 :: short_int a :: tuple L0: r0 = PyList_New(3) r1 = object 1 r2 = object 2 r3 = object 3 - r4 = get_element_ptr r0 ob_item :: PyListObject - r5 = load_mem r4 :: ptr* - buf_init_item r5, 0, r1 - buf_init_item r5, 1, r2 - buf_init_item r5, 2, r3 + r4 = list_items r0 + buf_init_item r4, 0, r1 + buf_init_item r4, 1, r2 + buf_init_item r4, 2, r3 keep_alive r0 source = r0 - r6 = get_element_ptr source ob_size :: PyVarObject - r7 = load_mem r6 :: native_int* - keep_alive source - r8 = PyTuple_New(r7) - r9 = 0 + r5 = var_object_size source + r6 = PyTuple_New(r5) + r7 = 0 L1: - r10 = get_element_ptr source ob_size :: PyVarObject - r11 = load_mem r10 :: native_int* - keep_alive source - r12 = r11 << 1 - r13 = int_lt r9, r12 - if r13 goto L2 else goto L4 :: bool + r8 = var_object_size source + r9 = r8 << 1 + r10 = int_lt r7, r9 + if r10 goto L2 else goto L4 :: bool L2: - r14 = CPyList_GetItemUnsafe(source, r9) - r15 = unbox(int, r14) - x = r15 - r16 = f(x) - r17 = box(bool, r16) - r18 = CPySequenceTuple_SetItemUnsafe(r8, r9, r17) + r11 = CPyList_GetItemUnsafe(source, r7) + r12 = unbox(int, r11) + x = r12 + r13 = f(x) + r14 = box(bool, r13) + r15 = CPySequenceTuple_SetItemUnsafe(r6, r7, r14) L3: - r19 = r9 + 2 - r9 = r19 + r16 = r7 + 2 + r7 = r16 goto L1 L4: - a = r8 + a = r6 return 1 [case testTupleBuiltFromStr] @@ -363,44 +349,38 @@ L0: return r0 def test(source): source :: tuple - r0 :: ptr - r1 :: native_int - r2 :: tuple - r3 :: short_int - r4 :: ptr - r5 :: native_int - r6 :: short_int - r7 :: bit - r8 :: object - r9, x, r10 :: bool - r11 :: object - r12 :: bit - r13 :: short_int + r0 :: native_int + r1 :: tuple + r2 :: short_int + r3 :: native_int + r4 :: short_int + r5 :: bit + r6 :: object + r7, x, r8 :: bool + r9 :: object + r10 :: bit + r11 :: short_int a :: tuple L0: - r0 = get_element_ptr source ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* - keep_alive source - r2 = PyTuple_New(r1) - r3 = 0 + r0 = var_object_size source + r1 = PyTuple_New(r0) + r2 = 0 L1: - r4 = get_element_ptr source ob_size :: PyVarObject - r5 = load_mem r4 :: native_int* - keep_alive source - r6 = r5 << 1 - r7 = int_lt r3, r6 - if r7 goto L2 else goto L4 :: bool + r3 = var_object_size source + r4 = r3 << 1 + r5 = int_lt r2, r4 + if r5 goto L2 else goto L4 :: bool L2: - r8 = CPySequenceTuple_GetItem(source, r3) - r9 = unbox(bool, r8) - x = r9 - r10 = f(x) - r11 = box(bool, r10) - r12 = CPySequenceTuple_SetItemUnsafe(r2, r3, r11) + r6 = CPySequenceTuple_GetItem(source, r2) + r7 = unbox(bool, r6) + x = r7 + r8 = f(x) + r9 = box(bool, r8) + r10 = CPySequenceTuple_SetItemUnsafe(r1, r2, r9) L3: - r13 = r3 + 2 - r3 = r13 + r11 = r2 + 2 + r2 = r11 goto L1 L4: - a = r2 + a = r1 return 1 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index b8d598e3b533..e719ecb2afe1 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -498,18 +498,17 @@ def f() -> int: def f(): r0 :: list r1, r2 :: object - r3, r4 :: ptr + r3 :: ptr a :: list L0: r0 = PyList_New(2) r1 = object 0 r2 = object 1 - r3 = get_element_ptr r0 ob_item :: PyListObject - r4 = load_mem r3 :: ptr* + r3 = list_items r0 inc_ref r1 - buf_init_item r4, 0, r1 + buf_init_item r3, 0, r1 inc_ref r2 - buf_init_item r4, 1, r2 + buf_init_item r3, 1, r2 a = r0 dec_ref a return 0 @@ -576,21 +575,20 @@ def f() -> None: def f(): r0 :: __main__.C r1 :: list - r2, r3 :: ptr + r2 :: ptr a :: list - r4 :: object - r5, d :: __main__.C + r3 :: object + r4, d :: __main__.C L0: r0 = C() r1 = PyList_New(1) - r2 = get_element_ptr r1 ob_item :: PyListObject - r3 = load_mem r2 :: ptr* - buf_init_item r3, 0, r0 + r2 = list_items r1 + buf_init_item r2, 0, r0 a = r1 - r4 = CPyList_GetItemShort(a, 0) + r3 = CPyList_GetItemShort(a, 0) dec_ref a - r5 = cast(__main__.C, r4) - d = r5 + r4 = cast(__main__.C, r3) + d = r4 dec_ref d return 1 @@ -815,17 +813,15 @@ def f() -> int: [out] def f(): r0, x :: list - r1 :: ptr - r2 :: native_int - r3 :: short_int + r1 :: native_int + r2 :: short_int L0: r0 = PyList_New(0) x = r0 - r1 = get_element_ptr x ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* + r1 = var_object_size x dec_ref x - r3 = r2 << 1 - return r3 + r2 = r1 << 1 + return r2 [case testSometimesUninitializedVariable] def f(x: bool) -> int: @@ -1066,15 +1062,13 @@ class C: def f(x): x :: __main__.C r0 :: list - r1 :: ptr - r2 :: native_int - r3 :: short_int + r1 :: native_int + r2 :: short_int L0: r0 = borrow x.a - r1 = get_element_ptr r0 ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* - r3 = r2 << 1 - return r3 + r1 = var_object_size r0 + r2 = r1 << 1 + return r2 [case testBorrowIsinstanceArgument] from typing import List @@ -1255,23 +1249,22 @@ class C: def f(): r0 :: __main__.C r1 :: list - r2, r3 :: ptr + r2 :: ptr a :: list - r4 :: object - r5 :: __main__.C - r6 :: str + r3 :: object + r4 :: __main__.C + r5 :: str L0: r0 = C() r1 = PyList_New(1) - r2 = get_element_ptr r1 ob_item :: PyListObject - r3 = load_mem r2 :: ptr* - buf_init_item r3, 0, r0 + r2 = list_items r1 + buf_init_item r2, 0, r0 a = r1 - r4 = CPyList_GetItemShortBorrow(a, 0) - r5 = borrow cast(__main__.C, r4) - r6 = r5.s + r3 = CPyList_GetItemShortBorrow(a, 0) + r4 = borrow cast(__main__.C, r3) + r5 = r4.s dec_ref a - return r6 + return r5 [case testBorrowSetAttrObject] from typing import Optional From 99f4b8138467c9a77003369b01242c202a6599c0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:17:46 +0100 Subject: [PATCH 0549/1617] Add classifier for 3.12 (#17065) --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 140020b18bc4..a17ee562eb39 100644 --- a/setup.py +++ b/setup.py @@ -189,6 +189,7 @@ def run(self): "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Software Development", "Typing :: Typed", ] From bebd278092031fc465de00838ef304349e188398 Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:06:46 +0100 Subject: [PATCH 0550/1617] fix for Pytest 8 compat (#17066) In Debian, we upgraded to pytest version 8.1.1+ for the next release. pytest deprecated the name for the first positional argument to `pytest.fail` from `msg` to `reason` in Pytest 7.0 and removed `msg` in Pytest 8.0 https://docs.pytest.org/en/7.0.x/reference/reference.html#pytest-fail https://docs.pytest.org/en/8.0.x/changelog.html#old-deprecations-are-now-errors --- mypy/test/data.py | 4 +--- mypy/test/helpers.py | 2 +- pyproject.toml | 2 +- test-requirements.in | 2 +- test-requirements.txt | 4 ++-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index de0267daf918..32f6354cc162 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -640,9 +640,7 @@ def pytest_pycollect_makeitem(collector: Any, name: str, obj: object) -> Any | N # Non-None result means this obj is a test case. # The collect method of the returned DataSuiteCollector instance will be called later, # with self.obj being obj. - return DataSuiteCollector.from_parent( # type: ignore[no-untyped-call] - parent=collector, name=name - ) + return DataSuiteCollector.from_parent(parent=collector, name=name) return None diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index bae4f6e81ad1..50de50e60004 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -41,7 +41,7 @@ def run_mypy(args: list[str]) -> None: if status != 0: sys.stdout.write(outval) sys.stderr.write(errval) - pytest.fail(msg="Sample check failed", pytrace=False) + pytest.fail(reason="Sample check failed", pytrace=False) def diff_ranges( diff --git a/pyproject.toml b/pyproject.toml index ef8acda3f95d..35f1592ca83c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ extra-standard-library = ["typing_extensions"] ignore = ["**/.readthedocs.yaml"] [tool.pytest.ini_options] -minversion = "6.0.0" +minversion = "7.0.0" testpaths = ["mypy/test", "mypyc/test"] python_files = 'test*.py' diff --git a/test-requirements.in b/test-requirements.in index 166bdf934d47..637f5b948055 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -11,7 +11,7 @@ lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_ pre-commit pre-commit-hooks==4.5.0 psutil>=4.0 -pytest>=7.4.0 +pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.2.0 # must match version in .pre-commit-config.yaml diff --git a/test-requirements.txt b/test-requirements.txt index f105b753799f..9005daab2876 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -44,7 +44,7 @@ platformdirs==3.11.0 # via # black # virtualenv -pluggy==1.3.0 +pluggy==1.4.0 # via pytest pre-commit==3.5.0 # via -r test-requirements.in @@ -52,7 +52,7 @@ pre-commit-hooks==4.5.0 # via -r test-requirements.in psutil==5.9.6 # via -r test-requirements.in -pytest==7.4.2 +pytest==8.1.1 # via # -r test-requirements.in # pytest-cov From 4310586460e0af07fa8994a0b4f03cb323e352f0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 27 Mar 2024 01:37:09 +0100 Subject: [PATCH 0551/1617] Fix TypedDict init from Type with optional keys (#17068) Followup to #16963 Correctly set optional and required keys for the TypedDict init callable. Ref: https://github.com/python/mypy/issues/11644 --- mypy/checkexpr.py | 5 ++- test-data/unit/check-typeddict.test | 32 ++++++++++++++++--- test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e7567eafb8fe..24d8447cdf3e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -949,7 +949,10 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType: def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType: return CallableType( list(callee.items.values()), - [ArgKind.ARG_NAMED] * len(callee.items), + [ + ArgKind.ARG_NAMED if name in callee.required_keys else ArgKind.ARG_NAMED_OPT + for name in callee.items + ], list(callee.items.keys()), callee, self.named_type("builtins.type"), diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 639be7bde8d8..bd1fbe3f2667 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3450,16 +3450,40 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Params', {'x': builtin [case testInitTypedDictFromType] from typing import TypedDict, Type +from typing_extensions import Required -class Point(TypedDict): - x: int +class Point(TypedDict, total=False): + x: Required[int] y: int def func(cls: Type[Point]) -> None: - reveal_type(cls) # N: Revealed type is "Type[TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})]" + reveal_type(cls) # N: Revealed type is "Type[TypedDict('__main__.Point', {'x': builtins.int, 'y'?: builtins.int})]" cls(x=1, y=2) cls(1, 2) # E: Too many positional arguments - cls(x=1) # E: Missing named argument "y" + cls(x=1) + cls(y=2) # E: Missing named argument "x" cls(x=1, y=2, error="") # E: Unexpected keyword argument "error" [typing fixtures/typing-full.pyi] [builtins fixtures/tuple.pyi] + +[case testInitTypedDictFromTypeGeneric] +from typing import Generic, TypedDict, Type, TypeVar +from typing_extensions import Required + +class Point(TypedDict, total=False): + x: Required[int] + y: int + +T = TypeVar("T", bound=Point) + +class A(Generic[T]): + def __init__(self, a: Type[T]) -> None: + self.a = a + + def func(self) -> T: + reveal_type(self.a) # N: Revealed type is "Type[T`1]" + self.a(x=1, y=2) + self.a(y=2) # E: Missing named argument "x" + return self.a(x=1) +[typing fixtures/typing-full.pyi] +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index b7b738f63d92..b5bfc1ab3f20 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -39,6 +39,8 @@ Never: _SpecialForm TypeVarTuple: _SpecialForm Unpack: _SpecialForm +Required: _SpecialForm +NotRequired: _SpecialForm @final class TypeAliasType: From 337bcf9ec3de40ec195d52d5615b875c3fb8ea26 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Fri, 29 Mar 2024 00:49:33 +0100 Subject: [PATCH 0552/1617] Improve error message for bound typevar in TypeAliasType (#17053) Follow up to #17038 When a type variable is bound to a class, it cannot be reused in a type alias. Previously in `TypeAliasType`, this error was reported as "not included in type_params". However in the following example, the error is misleading: ```python from typing import Dict, Generic, TypeVar from typing_extensions import TypeAliasType T = TypeVar("T") class A(Generic[T]): Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) x: A.Ta11 = {"a": 1} reveal_type(x) ``` On the master branch: ``` main.py:8: error: Type variable "T" is not included in type_params [valid-type] main.py:8: error: "T" is a type variable and only valid in type context [misc] main.py:8: error: Free type variable expected in type_params argument to TypeAliasType [type-var] main.py:12: note: Revealed type is "builtins.dict[builtins.str, Any]" Found 3 errors in 1 file (checked 1 source file) ``` With this PR: ``` typealiastype.py:8: error: Can't use bound type variable "T" to define generic alias [valid-type] typealiastype.py:8: error: "T" is a type variable and only valid in type context [misc] typealiastype.py:12: note: Revealed type is "builtins.dict[builtins.str, Any]" Found 2 errors in 1 file (checked 1 source file) ``` This is possible by storing the names of all the declared type_params, even those that are invalid, and checking if the offending type variables are in the list. --- mypy/semanal.py | 49 ++++++++++++++++++-------- mypy/typeanal.py | 26 ++++++++------ test-data/unit/check-type-aliases.test | 5 +++ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 5aaf2bc6f433..6832e767c3a4 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3521,6 +3521,7 @@ def analyze_alias( rvalue: Expression, allow_placeholder: bool = False, declared_type_vars: TypeVarLikeList | None = None, + all_declared_type_params_names: list[str] | None = None, ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str], bool]: """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable). @@ -3573,7 +3574,7 @@ def analyze_alias( in_dynamic_func=dynamic, global_scope=global_scope, allowed_alias_tvars=tvar_defs, - has_type_params=declared_type_vars is not None, + alias_type_params_names=all_declared_type_params_names, ) # There can be only one variadic variable at most, the error is reported elsewhere. @@ -3622,14 +3623,16 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # It can be `A = TypeAliasType('A', ...)` call, in this case, # we just take the second argument and analyze it: type_params: TypeVarLikeList | None + all_type_params_names: list[str] | None if self.check_type_alias_type_call(s.rvalue, name=lvalue.name): rvalue = s.rvalue.args[1] pep_695 = True - type_params = self.analyze_type_alias_type_params(s.rvalue) + type_params, all_type_params_names = self.analyze_type_alias_type_params(s.rvalue) else: rvalue = s.rvalue pep_695 = False type_params = None + all_type_params_names = None if isinstance(rvalue, CallExpr) and rvalue.analyzed: return False @@ -3686,7 +3689,11 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: else: tag = self.track_incomplete_refs() res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( - lvalue.name, rvalue, allow_placeholder=True, declared_type_vars=type_params + lvalue.name, + rvalue, + allow_placeholder=True, + declared_type_vars=type_params, + all_declared_type_params_names=all_type_params_names, ) if not res: return False @@ -3803,7 +3810,14 @@ def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGu return self.check_typevarlike_name(rvalue, name, rvalue) - def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList: + def analyze_type_alias_type_params( + self, rvalue: CallExpr + ) -> tuple[TypeVarLikeList, list[str]]: + """Analyze type_params of TypeAliasType. + + Returns declared unbound type variable expressions and a list of all decalred type + variable names for error reporting. + """ if "type_params" in rvalue.arg_names: type_params_arg = rvalue.args[rvalue.arg_names.index("type_params")] if not isinstance(type_params_arg, TupleExpr): @@ -3811,12 +3825,13 @@ def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList: "Tuple literal expected as the type_params argument to TypeAliasType", type_params_arg, ) - return [] + return [], [] type_params = type_params_arg.items else: - type_params = [] + return [], [] declared_tvars: TypeVarLikeList = [] + all_declared_tvar_names: list[str] = [] # includes bound type variables have_type_var_tuple = False for tp_expr in type_params: if isinstance(tp_expr, StarExpr): @@ -3843,16 +3858,19 @@ def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList: continue have_type_var_tuple = True elif not self.found_incomplete_ref(tag): - self.fail( - "Free type variable expected in type_params argument to TypeAliasType", - base, - code=codes.TYPE_VAR, - ) sym = self.lookup_qualified(base.name, base) - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): - self.note( - "Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR + if sym and isinstance(sym.node, TypeVarLikeExpr): + all_declared_tvar_names.append(sym.node.name) # Error will be reported later + else: + self.fail( + "Free type variable expected in type_params argument to TypeAliasType", + base, + code=codes.TYPE_VAR, ) + if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + self.note( + "Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR + ) continue if tvar in declared_tvars: self.fail( @@ -3862,8 +3880,9 @@ def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList: ) continue if tvar: + all_declared_tvar_names.append(tvar[0]) declared_tvars.append(tvar) - return declared_tvars + return declared_tvars, all_declared_tvar_names def disable_invalid_recursive_aliases( self, s: AssignmentStmt, current_node: TypeAlias diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 470b07948535..3f4b86185f2d 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -141,7 +141,7 @@ def analyze_type_alias( in_dynamic_func: bool = False, global_scope: bool = True, allowed_alias_tvars: list[TypeVarLikeType] | None = None, - has_type_params: bool = False, + alias_type_params_names: list[str] | None = None, ) -> tuple[Type, set[str]]: """Analyze r.h.s. of a (potential) type alias definition. @@ -159,7 +159,7 @@ def analyze_type_alias( allow_placeholder=allow_placeholder, prohibit_self_type="type alias target", allowed_alias_tvars=allowed_alias_tvars, - has_type_params=has_type_params, + alias_type_params_names=alias_type_params_names, ) analyzer.in_dynamic_func = in_dynamic_func analyzer.global_scope = global_scope @@ -212,7 +212,7 @@ def __init__( prohibit_self_type: str | None = None, allowed_alias_tvars: list[TypeVarLikeType] | None = None, allow_type_any: bool = False, - has_type_params: bool = False, + alias_type_params_names: list[str] | None = None, ) -> None: self.api = api self.fail_func = api.fail @@ -234,7 +234,7 @@ def __init__( if allowed_alias_tvars is None: allowed_alias_tvars = [] self.allowed_alias_tvars = allowed_alias_tvars - self.has_type_params = has_type_params + self.alias_type_params_names = alias_type_params_names # If false, record incomplete ref if we generate PlaceholderType. self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? @@ -275,6 +275,12 @@ def visit_unbound_type(self, t: UnboundType, defining_literal: bool = False) -> return make_optional_type(typ) return typ + def not_declared_in_type_params(self, tvar_name: str) -> bool: + return ( + self.alias_type_params_names is not None + and tvar_name not in self.alias_type_params_names + ) + def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) -> Type: sym = self.lookup_qualified(t.name, t) if sym is not None: @@ -329,7 +335,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if tvar_def is None: if self.allow_unbound_tvars: return t - if self.defining_alias and self.has_type_params: + if self.defining_alias and self.not_declared_in_type_params(t.name): msg = f'ParamSpec "{t.name}" is not included in type_params' else: msg = f'ParamSpec "{t.name}" is unbound' @@ -357,7 +363,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) and not defining_literal and (tvar_def is None or tvar_def not in self.allowed_alias_tvars) ): - if self.has_type_params: + if self.not_declared_in_type_params(t.name): msg = f'Type variable "{t.name}" is not included in type_params' else: msg = f'Can\'t use bound type variable "{t.name}" to define generic alias' @@ -376,7 +382,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) and self.defining_alias and tvar_def not in self.allowed_alias_tvars ): - if self.has_type_params: + if self.not_declared_in_type_params(t.name): msg = f'Type variable "{t.name}" is not included in type_params' else: msg = f'Can\'t use bound type variable "{t.name}" to define generic alias' @@ -386,7 +392,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if tvar_def is None: if self.allow_unbound_tvars: return t - if self.defining_alias and self.has_type_params: + if self.defining_alias and self.not_declared_in_type_params(t.name): msg = f'TypeVarTuple "{t.name}" is not included in type_params' else: msg = f'TypeVarTuple "{t.name}" is unbound' @@ -1281,11 +1287,11 @@ def analyze_callable_args_for_paramspec( return None elif ( self.defining_alias - and self.has_type_params + and self.not_declared_in_type_params(tvar_def.name) and tvar_def not in self.allowed_alias_tvars ): self.fail( - f'ParamSpec "{callable_args.name}" is not included in type_params', + f'ParamSpec "{tvar_def.name}" is not included in type_params', callable_args, code=codes.VALID_TYPE, ) diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 7330a04c3647..a9c57d46ad22 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1195,6 +1195,11 @@ reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> #unbound_tvt_alias2: Ta10[int] #reveal_type(unbound_tvt_alias2) +class A(Generic[T]): + Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \ + # E: "T" is a type variable and only valid in type context +x: A.Ta11 = {"a": 1} +reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] [case testTypeAliasTypeNoUnpackInTypeParams311] From 4a7e5d3aa4e434e45746365a24638ecfd8b42b50 Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 29 Mar 2024 03:38:28 +0300 Subject: [PATCH 0553/1617] Add TypeGuard and TypeIs traversing in TypeTraverserVisitor (#17071) Fixes #17029. --- mypy/typetraverser.py | 6 ++++++ test-data/unit/check-typeguard.test | 12 ++++++++++++ test-data/unit/check-typeis.test | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index 1ff5f6685eb8..a28bbf422b61 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -86,6 +86,12 @@ def visit_callable_type(self, t: CallableType) -> None: t.ret_type.accept(self) t.fallback.accept(self) + if t.type_guard is not None: + t.type_guard.accept(self) + + if t.type_is is not None: + t.type_is.accept(self) + def visit_tuple_type(self, t: TupleType) -> None: self.traverse_types(t.items) t.partial_fallback.accept(self) diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 66c21bf3abe1..27b88553fb43 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -54,6 +54,18 @@ def main(a: object, b: object) -> None: reveal_type(b) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] +[case testTypeGuardTypeVarReturn] +from typing import Callable, Optional, TypeVar +from typing_extensions import TypeGuard +T = TypeVar('T') +def is_str(x: object) -> TypeGuard[str]: pass +def main(x: object, type_check_func: Callable[[object], TypeGuard[T]]) -> T: + if not type_check_func(x): + raise Exception() + return x +reveal_type(main("a", is_str)) # N: Revealed type is "builtins.str" +[builtins fixtures/exception.pyi] + [case testTypeGuardIsBool] from typing_extensions import TypeGuard def f(a: TypeGuard[int]) -> None: pass diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 04b64a45c8c1..6b96845504ab 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -92,6 +92,18 @@ def main(a: Tuple[object, ...]): reveal_type(a) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] +[case testTypeIsTypeVarReturn] +from typing import Callable, Optional, TypeVar +from typing_extensions import TypeIs +T = TypeVar('T') +def is_str(x: object) -> TypeIs[str]: pass +def main(x: object, type_check_func: Callable[[object], TypeIs[T]]) -> T: + if not type_check_func(x): + raise Exception() + return x +reveal_type(main("a", is_str)) # N: Revealed type is "builtins.str" +[builtins fixtures/exception.pyi] + [case testTypeIsUnionIn] from typing import Union from typing_extensions import TypeIs From ec440155ca4eecb7d083d581448abe8f048ded36 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:47:16 -0700 Subject: [PATCH 0554/1617] Sync typeshed (#17081) Sync typeshed Source commit: https://github.com/python/typeshed/commit/d3c831ce7d305a97ab4d3acf61aa22591fc8364a Note that you will need to close and re-open the PR in order to trigger CI. --------- Co-authored-by: mypybot <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/builtins.pyi | 1 + mypy/typeshed/stdlib/dataclasses.pyi | 8 ++++---- mypy/typeshed/stdlib/importlib/resources/simple.pyi | 4 ++-- .../stdlib/multiprocessing/resource_tracker.pyi | 6 +++--- mypy/typeshed/stdlib/multiprocessing/util.pyi | 4 ++-- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 2 ++ mypy/typeshed/stdlib/signal.pyi | 8 ++------ mypy/typeshed/stdlib/tkinter/commondialog.pyi | 4 ++-- mypy/typeshed/stdlib/tkinter/dialog.pyi | 2 +- mypy/typeshed/stdlib/tkinter/scrolledtext.pyi | 3 +-- mypy/typeshed/stdlib/traceback.pyi | 8 ++++---- mypy/typeshed/stdlib/typing.pyi | 10 +++++----- mypy/typeshed/stdlib/typing_extensions.pyi | 4 ++-- mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi | 2 +- mypy/typeshed/stdlib/xml/etree/ElementTree.pyi | 2 ++ 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 680cd556172f..47dddcadf36d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1777,6 +1777,7 @@ class MemoryError(Exception): ... class NameError(Exception): if sys.version_info >= (3, 10): + def __init__(self, *args: object, name: str | None = ...) -> None: ... name: str class ReferenceError(Exception): ... diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 00e0d31d092a..c361122704a5 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -243,7 +243,7 @@ class InitVar(Generic[_T], metaclass=_InitVarMeta): if sys.version_info >= (3, 12): def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], + fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], *, bases: tuple[type, ...] = (), namespace: dict[str, Any] | None = None, @@ -263,7 +263,7 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 11): def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], + fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], *, bases: tuple[type, ...] = (), namespace: dict[str, Any] | None = None, @@ -282,7 +282,7 @@ elif sys.version_info >= (3, 11): elif sys.version_info >= (3, 10): def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], + fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], *, bases: tuple[type, ...] = (), namespace: dict[str, Any] | None = None, @@ -300,7 +300,7 @@ elif sys.version_info >= (3, 10): else: def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], + fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], *, bases: tuple[type, ...] = (), namespace: dict[str, Any] | None = None, diff --git a/mypy/typeshed/stdlib/importlib/resources/simple.pyi b/mypy/typeshed/stdlib/importlib/resources/simple.pyi index 9ff415156365..c360da96d856 100644 --- a/mypy/typeshed/stdlib/importlib/resources/simple.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/simple.pyi @@ -28,11 +28,11 @@ if sys.version_info >= (3, 11): def is_file(self) -> Literal[True]: ... def is_dir(self) -> Literal[False]: ... @overload - def open(self, mode: OpenTextMode = "r", *args: Incomplete, **kwargs: Incomplete) -> TextIOWrapper: ... + def open(self, mode: OpenTextMode = "r", *args, **kwargs) -> TextIOWrapper: ... @overload def open(self, mode: OpenBinaryMode, *args: Unused, **kwargs: Unused) -> BinaryIO: ... @overload - def open(self, mode: str, *args: Incomplete, **kwargs: Incomplete) -> IO[Any]: ... + def open(self, mode: str, *args: Incomplete, **kwargs) -> IO[Any]: ... def joinpath(self, name: Never) -> NoReturn: ... # type: ignore[override] class ResourceContainer(Traversable, metaclass=abc.ABCMeta): diff --git a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi index 7f726a00d73a..78ad79cf925f 100644 --- a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi @@ -1,4 +1,4 @@ -from _typeshed import FileDescriptorOrPath, Incomplete +from _typeshed import FileDescriptorOrPath from collections.abc import Sized __all__ = ["ensure_running", "register", "unregister"] @@ -6,8 +6,8 @@ __all__ = ["ensure_running", "register", "unregister"] class ResourceTracker: def getfd(self) -> int | None: ... def ensure_running(self) -> None: ... - def register(self, name: Sized, rtype: Incomplete) -> None: ... - def unregister(self, name: Sized, rtype: Incomplete) -> None: ... + def register(self, name: Sized, rtype) -> None: ... + def unregister(self, name: Sized, rtype) -> None: ... _resource_tracker: ResourceTracker ensure_running = _resource_tracker.ensure_running diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index aeb46f85a327..8b900996f9eb 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -42,7 +42,7 @@ def is_abstract_socket_namespace(address: str | bytes | None) -> bool: ... abstract_sockets_supported: bool def get_temp_dir() -> str: ... -def register_after_fork(obj: Incomplete, func: Callable[[Incomplete], object]) -> None: ... +def register_after_fork(obj, func: Callable[[Incomplete], object]) -> None: ... class Finalize: def __init__( @@ -59,7 +59,7 @@ class Finalize: _finalizer_registry: MutableMapping[Incomplete, Incomplete] = {}, sub_debug: Callable[..., object] = ..., getpid: Callable[[], int] = ..., - ) -> Incomplete: ... + ): ... def cancel(self) -> None: ... def still_active(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 10011b437b6a..88bf9464d130 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -32,6 +32,8 @@ class XMLParserType: def ExternalEntityParserCreate(self, context: str | None, encoding: str = ..., /) -> XMLParserType: ... def SetParamEntityParsing(self, flag: int, /) -> int: ... def UseForeignDTD(self, flag: bool = True, /) -> None: ... + def GetReparseDeferralEnabled(self) -> bool: ... + def SetReparseDeferralEnabled(self, enabled: bool, /) -> None: ... @property def intern(self) -> dict[str, str]: ... buffer_size: int diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index d1fb3ba963d4..663ee2fe7430 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -175,12 +175,8 @@ else: @property def si_band(self) -> int: ... - if sys.version_info >= (3, 10): - def sigtimedwait(sigset: Iterable[int], timeout: float, /) -> struct_siginfo | None: ... - def sigwaitinfo(sigset: Iterable[int], /) -> struct_siginfo: ... - else: - def sigtimedwait(sigset: Iterable[int], timeout: float) -> struct_siginfo | None: ... - def sigwaitinfo(sigset: Iterable[int]) -> struct_siginfo: ... + def sigtimedwait(sigset: Iterable[int], timeout: float, /) -> struct_siginfo | None: ... + def sigwaitinfo(sigset: Iterable[int], /) -> struct_siginfo: ... def strsignal(signalnum: _SIGNUM, /) -> str | None: ... def valid_signals() -> set[Signals]: ... diff --git a/mypy/typeshed/stdlib/tkinter/commondialog.pyi b/mypy/typeshed/stdlib/tkinter/commondialog.pyi index eba3ab5be3bd..d06c08df5b76 100644 --- a/mypy/typeshed/stdlib/tkinter/commondialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/commondialog.pyi @@ -10,5 +10,5 @@ class Dialog: command: ClassVar[str | None] master: Incomplete | None options: Mapping[str, Incomplete] - def __init__(self, master: Incomplete | None = None, **options: Incomplete) -> None: ... - def show(self, **options: Incomplete) -> Incomplete: ... + def __init__(self, master: Incomplete | None = None, **options) -> None: ... + def show(self, **options): ... diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi index 7bc77ac6d8b5..f76732a25460 100644 --- a/mypy/typeshed/stdlib/tkinter/dialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi @@ -12,5 +12,5 @@ DIALOG_ICON: str class Dialog(Widget): widgetName: str num: int - def __init__(self, master: Incomplete | None = None, cnf: Mapping[str, Any] = {}, **kw: Incomplete) -> None: ... + def __init__(self, master: Incomplete | None = None, cnf: Mapping[str, Any] = {}, **kw) -> None: ... def destroy(self) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/scrolledtext.pyi b/mypy/typeshed/stdlib/tkinter/scrolledtext.pyi index 114f8c3de3ea..6f1abc714487 100644 --- a/mypy/typeshed/stdlib/tkinter/scrolledtext.pyi +++ b/mypy/typeshed/stdlib/tkinter/scrolledtext.pyi @@ -1,4 +1,3 @@ -from _typeshed import Incomplete from tkinter import Frame, Misc, Scrollbar, Text __all__ = ["ScrolledText"] @@ -7,4 +6,4 @@ __all__ = ["ScrolledText"] class ScrolledText(Text): frame: Frame vbar: Scrollbar - def __init__(self, master: Misc | None = None, **kwargs: Incomplete) -> None: ... + def __init__(self, master: Misc | None = None, **kwargs) -> None: ... diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 928858f81d1c..39803003cfe5 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -27,7 +27,7 @@ __all__ = [ "walk_tb", ] -_PT: TypeAlias = tuple[str, int, str, str | None] +_FrameSummaryTuple: TypeAlias = tuple[str, int, str, str | None] def print_tb(tb: TracebackType | None, limit: int | None = None, file: SupportsWrite[str] | None = None) -> None: ... @@ -80,10 +80,10 @@ def print_last(limit: int | None = None, file: SupportsWrite[str] | None = None, def print_stack(f: FrameType | None = None, limit: int | None = None, file: SupportsWrite[str] | None = None) -> None: ... def extract_tb(tb: TracebackType | None, limit: int | None = None) -> StackSummary: ... def extract_stack(f: FrameType | None = None, limit: int | None = None) -> StackSummary: ... -def format_list(extracted_list: list[FrameSummary]) -> list[str]: ... +def format_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple]) -> list[str]: ... # undocumented -def print_list(extracted_list: list[FrameSummary], file: SupportsWrite[str] | None = None) -> None: ... +def print_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple], file: SupportsWrite[str] | None = None) -> None: ... if sys.version_info >= (3, 10): @overload @@ -255,7 +255,7 @@ class StackSummary(list[FrameSummary]): capture_locals: bool = False, ) -> StackSummary: ... @classmethod - def from_list(cls, a_list: Iterable[FrameSummary | _PT]) -> StackSummary: ... + def from_list(cls, a_list: Iterable[FrameSummary | _FrameSummaryTuple]) -> StackSummary: ... if sys.version_info >= (3, 11): def format_frame_summary(self, frame_summary: FrameSummary) -> str: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index be0c29c89f8d..a2294f2f579f 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -6,7 +6,7 @@ import collections # noqa: F401 # pyright: ignore import sys import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values -from _typeshed import IdentityFunction, Incomplete, ReadableBuffer, SupportsKeysAndGetItem +from _typeshed import IdentityFunction, ReadableBuffer, SupportsKeysAndGetItem from abc import ABCMeta, abstractmethod from contextlib import AbstractAsyncContextManager, AbstractContextManager from re import Match as Match, Pattern as Pattern @@ -170,7 +170,7 @@ class TypeVar: def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Incomplete) -> Incomplete: ... + def __typing_subst__(self, arg): ... # Used for an undocumented mypy feature. Does not exist at runtime. _promote = object() @@ -221,7 +221,7 @@ if sys.version_info >= (3, 11): def __init__(self, name: str) -> None: ... def __iter__(self) -> Any: ... def __typing_subst__(self, arg: Never) -> Never: ... - def __typing_prepare_subst__(self, alias: Incomplete, args: Incomplete) -> Incomplete: ... + def __typing_prepare_subst__(self, alias, args): ... if sys.version_info >= (3, 10): @final @@ -270,8 +270,8 @@ if sys.version_info >= (3, 10): @property def kwargs(self) -> ParamSpecKwargs: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Incomplete) -> Incomplete: ... - def __typing_prepare_subst__(self, alias: Incomplete, args: Incomplete) -> Incomplete: ... + def __typing_subst__(self, arg): ... + def __typing_prepare_subst__(self, alias, args): ... def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index f9e94ca683d6..cb67eb612a71 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -2,7 +2,7 @@ import abc import sys import typing from _collections_abc import dict_items, dict_keys, dict_values -from _typeshed import IdentityFunction, Incomplete +from _typeshed import IdentityFunction from typing import ( # noqa: Y022,Y037,Y038,Y039 IO as IO, TYPE_CHECKING as TYPE_CHECKING, @@ -413,7 +413,7 @@ class TypeVar: def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Incomplete) -> Incomplete: ... + def __typing_subst__(self, arg): ... @final class ParamSpec: diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index 480dd7ce732c..62ca7dd9fc45 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -60,7 +60,7 @@ class DOMBuilder: def supportsFeature(self, name: str) -> bool: ... def canSetFeature(self, name: str, state: int) -> bool: ... # getFeature could return any attribute from an instance of `Options` - def getFeature(self, name: str) -> Incomplete: ... + def getFeature(self, name: str): ... def parseURI(self, uri: str) -> ExpatBuilder | ExpatBuilderNS: ... def parse(self, input: DOMInputSource) -> ExpatBuilder | ExpatBuilderNS: ... # `input` and `cnode` argtypes for `parseWithContext` are unknowable diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index a8af66938344..9198bd3322d9 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -250,6 +250,7 @@ class XMLPullParser: # Second element in the tuple could be `Element`, `tuple[str, str]` or `None`. # Use `Any` to avoid false-positive errors. def read_events(self) -> Iterator[tuple[str, Any]]: ... + def flush(self) -> None: ... def XML(text: str | ReadableBuffer, parser: XMLParser | None = None) -> Element: ... def XMLID(text: str | ReadableBuffer, parser: XMLParser | None = None) -> tuple[Element, dict[str, Element]]: ... @@ -323,3 +324,4 @@ class XMLParser: def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... def close(self) -> Any: ... def feed(self, data: str | ReadableBuffer, /) -> None: ... + def flush(self) -> None: ... From 80190101f68b52e960c22572ed6cc814de078b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Simon?= Date: Thu, 4 Apr 2024 12:34:00 +0200 Subject: [PATCH 0555/1617] Narrow individual items when matching a tuple to a sequence pattern (#16905) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #12364 When matching a tuple to a sequence pattern, this change narrows the type of tuple items inside the matched case: ```py def test(a: bool, b: bool) -> None: match a, b: case True, True: reveal_type(a) # before: "builtins.bool", after: "Literal[True]" ``` This also works with nested tuples, recursively: ```py def test(a: bool, b: bool, c: bool) -> None: match a, (b, c): case _, [True, False]: reveal_type(c) # before: "builtins.bool", after: "Literal[False]" ``` This only partially fixes issue #12364; see [my comment there](https://github.com/python/mypy/issues/12364#issuecomment-1937375271) for more context. --- This is my first contribution to mypy, so I may miss some context or conventions; I'm eager for any feedback! --------- Co-authored-by: Loïc Simon --- mypy/checker.py | 17 ++++++++ test-data/unit/check-python310.test | 66 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 5d243195d50f..af7535581091 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5119,6 +5119,9 @@ def visit_match_stmt(self, s: MatchStmt) -> None: ) self.remove_capture_conflicts(pattern_type.captures, inferred_types) self.push_type_map(pattern_map) + if pattern_map: + for expr, typ in pattern_map.items(): + self.push_type_map(self._get_recursive_sub_patterns_map(expr, typ)) self.push_type_map(pattern_type.captures) if g is not None: with self.binder.frame_context(can_skip=False, fall_through=3): @@ -5156,6 +5159,20 @@ def visit_match_stmt(self, s: MatchStmt) -> None: with self.binder.frame_context(can_skip=False, fall_through=2): pass + def _get_recursive_sub_patterns_map( + self, expr: Expression, typ: Type + ) -> dict[Expression, Type]: + sub_patterns_map: dict[Expression, Type] = {} + typ_ = get_proper_type(typ) + if isinstance(expr, TupleExpr) and isinstance(typ_, TupleType): + # When matching a tuple expression with a sequence pattern, narrow individual tuple items + assert len(expr.items) == len(typ_.items) + for item_expr, item_typ in zip(expr.items, typ_.items): + sub_patterns_map[item_expr] = item_typ + sub_patterns_map.update(self._get_recursive_sub_patterns_map(item_expr, item_typ)) + + return sub_patterns_map + def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[Var, Type]: all_captures: dict[Var, list[tuple[NameExpr, Type]]] = defaultdict(list) for tm in type_maps: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 3a040d94d7ba..2b56d2db07a9 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -341,6 +341,72 @@ match m: reveal_type(m) # N: Revealed type is "builtins.list[builtins.list[builtins.str]]" [builtins fixtures/list.pyi] +[case testMatchSequencePatternNarrowSubjectItems] +m: int +n: str +o: bool + +match m, n, o: + case [3, "foo", True]: + reveal_type(m) # N: Revealed type is "Literal[3]" + reveal_type(n) # N: Revealed type is "Literal['foo']" + reveal_type(o) # N: Revealed type is "Literal[True]" + case [a, b, c]: + reveal_type(m) # N: Revealed type is "builtins.int" + reveal_type(n) # N: Revealed type is "builtins.str" + reveal_type(o) # N: Revealed type is "builtins.bool" + +reveal_type(m) # N: Revealed type is "builtins.int" +reveal_type(n) # N: Revealed type is "builtins.str" +reveal_type(o) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternNarrowSubjectItemsRecursive] +m: int +n: int +o: int +p: int +q: int +r: int + +match m, (n, o), (p, (q, r)): + case [0, [1, 2], [3, [4, 5]]]: + reveal_type(m) # N: Revealed type is "Literal[0]" + reveal_type(n) # N: Revealed type is "Literal[1]" + reveal_type(o) # N: Revealed type is "Literal[2]" + reveal_type(p) # N: Revealed type is "Literal[3]" + reveal_type(q) # N: Revealed type is "Literal[4]" + reveal_type(r) # N: Revealed type is "Literal[5]" +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternSequencesLengthMismatchNoNarrowing] +m: int +n: str +o: bool + +match m, n, o: + case [3, "foo"]: + pass + case [3, "foo", True, True]: + pass +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternSequencesLengthMismatchNoNarrowingRecursive] +m: int +n: int +o: int + +match m, (n, o): + case [0]: + pass + case [0, 1, [2]]: + pass + case [0, [1]]: + pass + case [0, [1, 2, 3]]: + pass +[builtins fixtures/tuple.pyi] + -- Mapping Pattern -- [case testMatchMappingPatternCaptures] From 732d98ecb2a98e4eaea14aba1ed8ac9c1f5ccdb6 Mon Sep 17 00:00:00 2001 From: roberfi Date: Mon, 8 Apr 2024 08:40:02 +0200 Subject: [PATCH 0556/1617] Fix string formatting for string enums (#16555) Fixes #7563 Inside `check_str_format_call` method, it checks if expression of `format` method call is an Enum member and it takes Literal value of that Enum member to check the `format` call arguments, if so. --- mypy/checkexpr.py | 12 +++++++- test-data/unit/check-formatting.test | 42 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 24d8447cdf3e..e8a2e501a452 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -636,7 +636,17 @@ def check_str_format_call(self, e: CallExpr) -> None: if isinstance(e.callee.expr, StrExpr): format_value = e.callee.expr.value elif self.chk.has_type(e.callee.expr): - base_typ = try_getting_literal(self.chk.lookup_type(e.callee.expr)) + typ = get_proper_type(self.chk.lookup_type(e.callee.expr)) + if ( + isinstance(typ, Instance) + and typ.type.is_enum + and isinstance(typ.last_known_value, LiteralType) + and isinstance(typ.last_known_value.value, str) + ): + value_type = typ.type.names[typ.last_known_value.value].type + if isinstance(value_type, Type): + typ = get_proper_type(value_type) + base_typ = try_getting_literal(typ) if isinstance(base_typ, LiteralType) and isinstance(base_typ.value, str): format_value = base_typ.value if format_value is not None: diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index 75651124b76f..83ae9b526f22 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -588,3 +588,45 @@ class S: '{:%}'.format(0.001) [builtins fixtures/primitives.pyi] [typing fixtures/typing-medium.pyi] + +[case testEnumWithStringToFormatValue] +from enum import Enum + +class Responses(str, Enum): + TEMPLATED = 'insert {} here' + TEMPLATED_WITH_KW = 'insert {value} here' + NORMAL = 'something' + +Responses.TEMPLATED.format(42) +Responses.TEMPLATED_WITH_KW.format(value=42) +Responses.TEMPLATED.format() # E: Cannot find replacement for positional format specifier 0 +Responses.TEMPLATED_WITH_KW.format() # E: Cannot find replacement for named format specifier "value" +Responses.NORMAL.format(42) # E: Not all arguments converted during string formatting +Responses.NORMAL.format(value=42) # E: Not all arguments converted during string formatting +[builtins fixtures/primitives.pyi] + +[case testNonStringEnumToFormatValue] +from enum import Enum + +class Responses(Enum): + TEMPLATED = 'insert {value} here' + +Responses.TEMPLATED.format(value=42) # E: "Responses" has no attribute "format" +[builtins fixtures/primitives.pyi] + +[case testStrEnumWithStringToFormatValue] +# flags: --python-version 3.11 +from enum import StrEnum + +class Responses(StrEnum): + TEMPLATED = 'insert {} here' + TEMPLATED_WITH_KW = 'insert {value} here' + NORMAL = 'something' + +Responses.TEMPLATED.format(42) +Responses.TEMPLATED_WITH_KW.format(value=42) +Responses.TEMPLATED.format() # E: Cannot find replacement for positional format specifier 0 +Responses.TEMPLATED_WITH_KW.format() # E: Cannot find replacement for named format specifier "value" +Responses.NORMAL.format(42) # E: Not all arguments converted during string formatting +Responses.NORMAL.format(value=42) # E: Not all arguments converted during string formatting +[builtins fixtures/primitives.pyi] From 3ff6e47c57a67e807e0b4579a816b4f66ab16824 Mon Sep 17 00:00:00 2001 From: Ihor <31508183+nautics889@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:59:51 +0300 Subject: [PATCH 0557/1617] Docs: docstrings in checker.py, ast_helpers.py (#16908) --- mypy/checker.py | 17 ++++++++++++----- mypyc/irbuild/ast_helpers.py | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index af7535581091..9c10cd2fc30d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2166,11 +2166,18 @@ def check_override( """Check a method override with given signatures. Arguments: - override: The signature of the overriding method. - original: The signature of the original supertype method. - name: The name of the subtype. This and the next argument are - only used for generating error messages. - supertype: The name of the supertype. + override: The signature of the overriding method. + original: The signature of the original supertype method. + name: The name of the overriding method. + Used primarily for generating error messages. + name_in_super: The name of the overridden in the superclass. + Used for generating error messages only. + supertype: The name of the supertype. + original_class_or_static: Indicates whether the original method (from the superclass) + is either a class method or a static method. + override_class_or_static: Indicates whether the overriding method (from the subclass) + is either a class method or a static method. + node: Context node. """ # Use boolean variable to clarify code. fail = False diff --git a/mypyc/irbuild/ast_helpers.py b/mypyc/irbuild/ast_helpers.py index bc976647675d..3b0f50514594 100644 --- a/mypyc/irbuild/ast_helpers.py +++ b/mypyc/irbuild/ast_helpers.py @@ -62,6 +62,7 @@ def maybe_process_conditional_comparison( do nothing and return False. Args: + self: IR form Builder e: Arbitrary expression true: Branch target if comparison is true false: Branch target if comparison is false From e2fc1f28935806ca04b18fab277217f583b51594 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 8 Apr 2024 20:38:03 +0200 Subject: [PATCH 0558/1617] Fix crash when expanding invalid Unpack in a `Callable` alias (#17028) Fixes #16937 --- mypy/expandtype.py | 31 ++++++++++++++----------- test-data/unit/check-python311.test | 31 +++++++++++++++++++++++++ test-data/unit/check-python312.test | 7 +++--- test-data/unit/check-type-aliases.test | 7 +++--- test-data/unit/check-typevar-tuple.test | 16 +++++++++++++ 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index ec6a2ecfd0d2..f7fa0258f588 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -26,6 +26,7 @@ Type, TypeAliasType, TypedDictType, + TypeOfAny, TypeType, TypeVarId, TypeVarLikeType, @@ -312,24 +313,26 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) + new_unpack: Type if isinstance(var_arg_type, Instance): # we have something like Unpack[Tuple[Any, ...]] new_unpack = var_arg - else: - if isinstance(var_arg_type, TupleType): - # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] - expanded_tuple = var_arg_type.accept(self) - assert isinstance(expanded_tuple, ProperType) and isinstance( - expanded_tuple, TupleType - ) - expanded_items = expanded_tuple.items - fallback = var_arg_type.partial_fallback - else: - # We have plain Unpack[Ts] - assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) - fallback = var_arg_type.tuple_fallback - expanded_items = self.expand_unpack(var_arg) + elif isinstance(var_arg_type, TupleType): + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] + expanded_tuple = var_arg_type.accept(self) + assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) + expanded_items = expanded_tuple.items + fallback = var_arg_type.partial_fallback new_unpack = UnpackType(TupleType(expanded_items, fallback)) + elif isinstance(var_arg_type, TypeVarTupleType): + # We have plain Unpack[Ts] + fallback = var_arg_type.tuple_fallback + expanded_items = self.expand_unpack(var_arg) + new_unpack = UnpackType(TupleType(expanded_items, fallback)) + else: + # We have invalid type in Unpack. This can happen when expanding aliases + # to Callable[[*Invalid], Ret] + new_unpack = AnyType(TypeOfAny.from_error, line=var_arg.line, column=var_arg.column) return prefix + [new_unpack] + suffix def visit_callable_type(self, t: CallableType) -> CallableType: diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 37dc3ca0f5b4..2d1a09ef3336 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -142,3 +142,34 @@ myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" m # N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2] reveal_type(myclass3) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] + +[case testUnpackNewSyntaxInvalidCallableAlias] +from typing import Any, Callable, List, Tuple, TypeVar, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined + +def good(*x: int) -> int: ... +def bad(*x: int, y: int) -> int: ... + +Alias1 = Callable[[*Ts], int] # E: Variable "__main__.Ts" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +x1: Alias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x1) # N: Revealed type is "def (*Any) -> builtins.int" +x1 = good +x1 = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") + +Alias2 = Callable[[*T], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple) +x2: Alias2[int] +reveal_type(x2) # N: Revealed type is "def (*Any) -> builtins.int" + +Unknown = Any +Alias3 = Callable[[*Unknown], int] +x3: Alias3[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int" + +IntList = List[int] +Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple) +x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 188c51f98185..2b99a42628b1 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -76,10 +76,9 @@ BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is no ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]" -# TODO this should report errors on the two following lines -#BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) -#ba2: BadAlias2[int] -#reveal_type(ba2) +BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) # E: TypeVarTuple "Ts" is not included in type_params +ba2: BadAlias2[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index a9c57d46ad22..aebb0381d962 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1190,10 +1190,9 @@ Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included i unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2 reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any" -# TODO this should report errors on the two following lines -#Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) -#unbound_tvt_alias2: Ta10[int] -#reveal_type(unbound_tvt_alias2) +Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) # E: TypeVarTuple "Ts" is not included in type_params +unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str" class A(Generic[T]): Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \ diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index cc3dc4ed9f39..f704e3c5c713 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2278,6 +2278,22 @@ higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Cal higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" [builtins fixtures/tuple.pyi] +[case testAliasToCallableWithUnpackInvalid] +from typing import Any, Callable, List, Tuple, TypeVar, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined + +def good(*x: int) -> int: ... +def bad(*x: int, y: int) -> int: ... + +Alias = Callable[[Unpack[T]], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple) +x: Alias[int] +reveal_type(x) # N: Revealed type is "def (*Any) -> builtins.int" +x = good +x = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleInvariant] from typing import Generic, Tuple from typing_extensions import Unpack, TypeVarTuple From 5161ac2e5b73dc7597536eb4444219868317e5d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 19:50:28 -0700 Subject: [PATCH 0559/1617] Sync typeshed (#17124) Source commit: https://github.com/python/typeshed/commit/7c8e82fe483a40ec4cb0a2505cfdb0f3e7cc81d9 Co-authored-by: mypybot <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/_curses.pyi | 1091 +++++++++-------- mypy/typeshed/stdlib/asyncio/streams.pyi | 15 +- mypy/typeshed/stdlib/curses/__init__.pyi | 27 +- mypy/typeshed/stdlib/curses/ascii.pyi | 117 +- mypy/typeshed/stdlib/curses/has_key.pyi | 5 +- mypy/typeshed/stdlib/curses/panel.pyi | 41 +- mypy/typeshed/stdlib/curses/textpad.pyi | 18 +- .../stdlib/importlib/resources/simple.pyi | 15 +- mypy/typeshed/stdlib/io.pyi | 10 +- .../multiprocessing/resource_tracker.pyi | 4 +- mypy/typeshed/stdlib/multiprocessing/util.pyi | 31 +- mypy/typeshed/stdlib/ssl.pyi | 6 + mypy/typeshed/stdlib/typing.pyi | 10 +- mypy/typeshed/stdlib/typing_extensions.pyi | 8 +- mypy/typeshed/stdlib/winreg.pyi | 121 +- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 4 +- mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi | 2 +- 17 files changed, 795 insertions(+), 730 deletions(-) diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 929c6f8f3bc8..6f3fbd807fcc 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -3,557 +3,564 @@ from _typeshed import ReadOnlyBuffer, SupportsRead from typing import IO, Any, NamedTuple, final, overload from typing_extensions import TypeAlias -if sys.platform != "win32": - # Handled by PyCurses_ConvertToChtype in _cursesmodule.c. - _ChType: TypeAlias = str | bytes | int +# NOTE: This module is ordinarily only available on Unix, but the windows-curses +# package makes it available on Windows as well with the same contents. - # ACS codes are only initialized after initscr is called - ACS_BBSS: int - ACS_BLOCK: int - ACS_BOARD: int - ACS_BSBS: int - ACS_BSSB: int - ACS_BSSS: int - ACS_BTEE: int - ACS_BULLET: int - ACS_CKBOARD: int - ACS_DARROW: int - ACS_DEGREE: int - ACS_DIAMOND: int - ACS_GEQUAL: int - ACS_HLINE: int - ACS_LANTERN: int - ACS_LARROW: int - ACS_LEQUAL: int - ACS_LLCORNER: int - ACS_LRCORNER: int - ACS_LTEE: int - ACS_NEQUAL: int - ACS_PI: int - ACS_PLMINUS: int - ACS_PLUS: int - ACS_RARROW: int - ACS_RTEE: int - ACS_S1: int - ACS_S3: int - ACS_S7: int - ACS_S9: int - ACS_SBBS: int - ACS_SBSB: int - ACS_SBSS: int - ACS_SSBB: int - ACS_SSBS: int - ACS_SSSB: int - ACS_SSSS: int - ACS_STERLING: int - ACS_TTEE: int - ACS_UARROW: int - ACS_ULCORNER: int - ACS_URCORNER: int - ACS_VLINE: int - ALL_MOUSE_EVENTS: int - A_ALTCHARSET: int - A_ATTRIBUTES: int - A_BLINK: int - A_BOLD: int - A_CHARTEXT: int - A_COLOR: int - A_DIM: int - A_HORIZONTAL: int - A_INVIS: int - if sys.platform != "darwin": - A_ITALIC: int - A_LEFT: int - A_LOW: int - A_NORMAL: int - A_PROTECT: int - A_REVERSE: int - A_RIGHT: int - A_STANDOUT: int - A_TOP: int - A_UNDERLINE: int - A_VERTICAL: int - BUTTON1_CLICKED: int - BUTTON1_DOUBLE_CLICKED: int - BUTTON1_PRESSED: int - BUTTON1_RELEASED: int - BUTTON1_TRIPLE_CLICKED: int - BUTTON2_CLICKED: int - BUTTON2_DOUBLE_CLICKED: int - BUTTON2_PRESSED: int - BUTTON2_RELEASED: int - BUTTON2_TRIPLE_CLICKED: int - BUTTON3_CLICKED: int - BUTTON3_DOUBLE_CLICKED: int - BUTTON3_PRESSED: int - BUTTON3_RELEASED: int - BUTTON3_TRIPLE_CLICKED: int - BUTTON4_CLICKED: int - BUTTON4_DOUBLE_CLICKED: int - BUTTON4_PRESSED: int - BUTTON4_RELEASED: int - BUTTON4_TRIPLE_CLICKED: int - # Darwin ncurses doesn't provide BUTTON5_* constants - if sys.version_info >= (3, 10) and sys.platform != "darwin": - BUTTON5_PRESSED: int - BUTTON5_RELEASED: int - BUTTON5_CLICKED: int - BUTTON5_DOUBLE_CLICKED: int - BUTTON5_TRIPLE_CLICKED: int - BUTTON_ALT: int - BUTTON_CTRL: int - BUTTON_SHIFT: int - COLOR_BLACK: int - COLOR_BLUE: int - COLOR_CYAN: int - COLOR_GREEN: int - COLOR_MAGENTA: int - COLOR_RED: int - COLOR_WHITE: int - COLOR_YELLOW: int - ERR: int - KEY_A1: int - KEY_A3: int - KEY_B2: int - KEY_BACKSPACE: int - KEY_BEG: int - KEY_BREAK: int - KEY_BTAB: int - KEY_C1: int - KEY_C3: int - KEY_CANCEL: int - KEY_CATAB: int - KEY_CLEAR: int - KEY_CLOSE: int - KEY_COMMAND: int - KEY_COPY: int - KEY_CREATE: int - KEY_CTAB: int - KEY_DC: int - KEY_DL: int - KEY_DOWN: int - KEY_EIC: int - KEY_END: int - KEY_ENTER: int - KEY_EOL: int - KEY_EOS: int - KEY_EXIT: int - KEY_F0: int - KEY_F1: int - KEY_F10: int - KEY_F11: int - KEY_F12: int - KEY_F13: int - KEY_F14: int - KEY_F15: int - KEY_F16: int - KEY_F17: int - KEY_F18: int - KEY_F19: int - KEY_F2: int - KEY_F20: int - KEY_F21: int - KEY_F22: int - KEY_F23: int - KEY_F24: int - KEY_F25: int - KEY_F26: int - KEY_F27: int - KEY_F28: int - KEY_F29: int - KEY_F3: int - KEY_F30: int - KEY_F31: int - KEY_F32: int - KEY_F33: int - KEY_F34: int - KEY_F35: int - KEY_F36: int - KEY_F37: int - KEY_F38: int - KEY_F39: int - KEY_F4: int - KEY_F40: int - KEY_F41: int - KEY_F42: int - KEY_F43: int - KEY_F44: int - KEY_F45: int - KEY_F46: int - KEY_F47: int - KEY_F48: int - KEY_F49: int - KEY_F5: int - KEY_F50: int - KEY_F51: int - KEY_F52: int - KEY_F53: int - KEY_F54: int - KEY_F55: int - KEY_F56: int - KEY_F57: int - KEY_F58: int - KEY_F59: int - KEY_F6: int - KEY_F60: int - KEY_F61: int - KEY_F62: int - KEY_F63: int - KEY_F7: int - KEY_F8: int - KEY_F9: int - KEY_FIND: int - KEY_HELP: int - KEY_HOME: int - KEY_IC: int - KEY_IL: int - KEY_LEFT: int - KEY_LL: int - KEY_MARK: int - KEY_MAX: int - KEY_MESSAGE: int - KEY_MIN: int - KEY_MOUSE: int - KEY_MOVE: int - KEY_NEXT: int - KEY_NPAGE: int - KEY_OPEN: int - KEY_OPTIONS: int - KEY_PPAGE: int - KEY_PREVIOUS: int - KEY_PRINT: int - KEY_REDO: int - KEY_REFERENCE: int - KEY_REFRESH: int - KEY_REPLACE: int - KEY_RESET: int - KEY_RESIZE: int - KEY_RESTART: int - KEY_RESUME: int - KEY_RIGHT: int - KEY_SAVE: int - KEY_SBEG: int - KEY_SCANCEL: int - KEY_SCOMMAND: int - KEY_SCOPY: int - KEY_SCREATE: int - KEY_SDC: int - KEY_SDL: int - KEY_SELECT: int - KEY_SEND: int - KEY_SEOL: int - KEY_SEXIT: int - KEY_SF: int - KEY_SFIND: int - KEY_SHELP: int - KEY_SHOME: int - KEY_SIC: int - KEY_SLEFT: int - KEY_SMESSAGE: int - KEY_SMOVE: int - KEY_SNEXT: int - KEY_SOPTIONS: int - KEY_SPREVIOUS: int - KEY_SPRINT: int - KEY_SR: int - KEY_SREDO: int - KEY_SREPLACE: int - KEY_SRESET: int - KEY_SRIGHT: int - KEY_SRSUME: int - KEY_SSAVE: int - KEY_SSUSPEND: int - KEY_STAB: int - KEY_SUNDO: int - KEY_SUSPEND: int - KEY_UNDO: int - KEY_UP: int - OK: int - REPORT_MOUSE_POSITION: int - _C_API: Any - version: bytes - def baudrate() -> int: ... - def beep() -> None: ... - def can_change_color() -> bool: ... - def cbreak(flag: bool = True, /) -> None: ... - def color_content(color_number: int, /) -> tuple[int, int, int]: ... - def color_pair(pair_number: int, /) -> int: ... - def curs_set(visibility: int, /) -> int: ... - def def_prog_mode() -> None: ... - def def_shell_mode() -> None: ... - def delay_output(ms: int, /) -> None: ... - def doupdate() -> None: ... - def echo(flag: bool = True, /) -> None: ... - def endwin() -> None: ... - def erasechar() -> bytes: ... - def filter() -> None: ... - def flash() -> None: ... - def flushinp() -> None: ... - if sys.version_info >= (3, 9): - def get_escdelay() -> int: ... - def get_tabsize() -> int: ... +# Handled by PyCurses_ConvertToChtype in _cursesmodule.c. +_ChType: TypeAlias = str | bytes | int - def getmouse() -> tuple[int, int, int, int, int]: ... - def getsyx() -> tuple[int, int]: ... - def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... - def halfdelay(tenths: int, /) -> None: ... - def has_colors() -> bool: ... - if sys.version_info >= (3, 10): - def has_extended_color_support() -> bool: ... +# ACS codes are only initialized after initscr is called +ACS_BBSS: int +ACS_BLOCK: int +ACS_BOARD: int +ACS_BSBS: int +ACS_BSSB: int +ACS_BSSS: int +ACS_BTEE: int +ACS_BULLET: int +ACS_CKBOARD: int +ACS_DARROW: int +ACS_DEGREE: int +ACS_DIAMOND: int +ACS_GEQUAL: int +ACS_HLINE: int +ACS_LANTERN: int +ACS_LARROW: int +ACS_LEQUAL: int +ACS_LLCORNER: int +ACS_LRCORNER: int +ACS_LTEE: int +ACS_NEQUAL: int +ACS_PI: int +ACS_PLMINUS: int +ACS_PLUS: int +ACS_RARROW: int +ACS_RTEE: int +ACS_S1: int +ACS_S3: int +ACS_S7: int +ACS_S9: int +ACS_SBBS: int +ACS_SBSB: int +ACS_SBSS: int +ACS_SSBB: int +ACS_SSBS: int +ACS_SSSB: int +ACS_SSSS: int +ACS_STERLING: int +ACS_TTEE: int +ACS_UARROW: int +ACS_ULCORNER: int +ACS_URCORNER: int +ACS_VLINE: int +ALL_MOUSE_EVENTS: int +A_ALTCHARSET: int +A_ATTRIBUTES: int +A_BLINK: int +A_BOLD: int +A_CHARTEXT: int +A_COLOR: int +A_DIM: int +A_HORIZONTAL: int +A_INVIS: int +if sys.platform != "darwin": + A_ITALIC: int +A_LEFT: int +A_LOW: int +A_NORMAL: int +A_PROTECT: int +A_REVERSE: int +A_RIGHT: int +A_STANDOUT: int +A_TOP: int +A_UNDERLINE: int +A_VERTICAL: int +BUTTON1_CLICKED: int +BUTTON1_DOUBLE_CLICKED: int +BUTTON1_PRESSED: int +BUTTON1_RELEASED: int +BUTTON1_TRIPLE_CLICKED: int +BUTTON2_CLICKED: int +BUTTON2_DOUBLE_CLICKED: int +BUTTON2_PRESSED: int +BUTTON2_RELEASED: int +BUTTON2_TRIPLE_CLICKED: int +BUTTON3_CLICKED: int +BUTTON3_DOUBLE_CLICKED: int +BUTTON3_PRESSED: int +BUTTON3_RELEASED: int +BUTTON3_TRIPLE_CLICKED: int +BUTTON4_CLICKED: int +BUTTON4_DOUBLE_CLICKED: int +BUTTON4_PRESSED: int +BUTTON4_RELEASED: int +BUTTON4_TRIPLE_CLICKED: int +# Darwin ncurses doesn't provide BUTTON5_* constants +if sys.version_info >= (3, 10) and sys.platform != "darwin": + BUTTON5_PRESSED: int + BUTTON5_RELEASED: int + BUTTON5_CLICKED: int + BUTTON5_DOUBLE_CLICKED: int + BUTTON5_TRIPLE_CLICKED: int +BUTTON_ALT: int +BUTTON_CTRL: int +BUTTON_SHIFT: int +COLOR_BLACK: int +COLOR_BLUE: int +COLOR_CYAN: int +COLOR_GREEN: int +COLOR_MAGENTA: int +COLOR_RED: int +COLOR_WHITE: int +COLOR_YELLOW: int +ERR: int +KEY_A1: int +KEY_A3: int +KEY_B2: int +KEY_BACKSPACE: int +KEY_BEG: int +KEY_BREAK: int +KEY_BTAB: int +KEY_C1: int +KEY_C3: int +KEY_CANCEL: int +KEY_CATAB: int +KEY_CLEAR: int +KEY_CLOSE: int +KEY_COMMAND: int +KEY_COPY: int +KEY_CREATE: int +KEY_CTAB: int +KEY_DC: int +KEY_DL: int +KEY_DOWN: int +KEY_EIC: int +KEY_END: int +KEY_ENTER: int +KEY_EOL: int +KEY_EOS: int +KEY_EXIT: int +KEY_F0: int +KEY_F1: int +KEY_F10: int +KEY_F11: int +KEY_F12: int +KEY_F13: int +KEY_F14: int +KEY_F15: int +KEY_F16: int +KEY_F17: int +KEY_F18: int +KEY_F19: int +KEY_F2: int +KEY_F20: int +KEY_F21: int +KEY_F22: int +KEY_F23: int +KEY_F24: int +KEY_F25: int +KEY_F26: int +KEY_F27: int +KEY_F28: int +KEY_F29: int +KEY_F3: int +KEY_F30: int +KEY_F31: int +KEY_F32: int +KEY_F33: int +KEY_F34: int +KEY_F35: int +KEY_F36: int +KEY_F37: int +KEY_F38: int +KEY_F39: int +KEY_F4: int +KEY_F40: int +KEY_F41: int +KEY_F42: int +KEY_F43: int +KEY_F44: int +KEY_F45: int +KEY_F46: int +KEY_F47: int +KEY_F48: int +KEY_F49: int +KEY_F5: int +KEY_F50: int +KEY_F51: int +KEY_F52: int +KEY_F53: int +KEY_F54: int +KEY_F55: int +KEY_F56: int +KEY_F57: int +KEY_F58: int +KEY_F59: int +KEY_F6: int +KEY_F60: int +KEY_F61: int +KEY_F62: int +KEY_F63: int +KEY_F7: int +KEY_F8: int +KEY_F9: int +KEY_FIND: int +KEY_HELP: int +KEY_HOME: int +KEY_IC: int +KEY_IL: int +KEY_LEFT: int +KEY_LL: int +KEY_MARK: int +KEY_MAX: int +KEY_MESSAGE: int +KEY_MIN: int +KEY_MOUSE: int +KEY_MOVE: int +KEY_NEXT: int +KEY_NPAGE: int +KEY_OPEN: int +KEY_OPTIONS: int +KEY_PPAGE: int +KEY_PREVIOUS: int +KEY_PRINT: int +KEY_REDO: int +KEY_REFERENCE: int +KEY_REFRESH: int +KEY_REPLACE: int +KEY_RESET: int +KEY_RESIZE: int +KEY_RESTART: int +KEY_RESUME: int +KEY_RIGHT: int +KEY_SAVE: int +KEY_SBEG: int +KEY_SCANCEL: int +KEY_SCOMMAND: int +KEY_SCOPY: int +KEY_SCREATE: int +KEY_SDC: int +KEY_SDL: int +KEY_SELECT: int +KEY_SEND: int +KEY_SEOL: int +KEY_SEXIT: int +KEY_SF: int +KEY_SFIND: int +KEY_SHELP: int +KEY_SHOME: int +KEY_SIC: int +KEY_SLEFT: int +KEY_SMESSAGE: int +KEY_SMOVE: int +KEY_SNEXT: int +KEY_SOPTIONS: int +KEY_SPREVIOUS: int +KEY_SPRINT: int +KEY_SR: int +KEY_SREDO: int +KEY_SREPLACE: int +KEY_SRESET: int +KEY_SRIGHT: int +KEY_SRSUME: int +KEY_SSAVE: int +KEY_SSUSPEND: int +KEY_STAB: int +KEY_SUNDO: int +KEY_SUSPEND: int +KEY_UNDO: int +KEY_UP: int +OK: int +REPORT_MOUSE_POSITION: int +_C_API: Any +version: bytes - def has_ic() -> bool: ... - def has_il() -> bool: ... - def has_key(key: int, /) -> bool: ... - def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... - def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... - def initscr() -> _CursesWindow: ... - def intrflush(flag: bool, /) -> None: ... - def is_term_resized(nlines: int, ncols: int, /) -> bool: ... - def isendwin() -> bool: ... - def keyname(key: int, /) -> bytes: ... - def killchar() -> bytes: ... - def longname() -> bytes: ... - def meta(yes: bool, /) -> None: ... - def mouseinterval(interval: int, /) -> None: ... - def mousemask(newmask: int, /) -> tuple[int, int]: ... - def napms(ms: int, /) -> int: ... - def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ... - def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ... - def nl(flag: bool = True, /) -> None: ... - def nocbreak() -> None: ... - def noecho() -> None: ... - def nonl() -> None: ... - def noqiflush() -> None: ... - def noraw() -> None: ... - def pair_content(pair_number: int, /) -> tuple[int, int]: ... - def pair_number(attr: int, /) -> int: ... - def putp(string: ReadOnlyBuffer, /) -> None: ... - def qiflush(flag: bool = True, /) -> None: ... - def raw(flag: bool = True, /) -> None: ... - def reset_prog_mode() -> None: ... - def reset_shell_mode() -> None: ... - def resetty() -> None: ... - def resize_term(nlines: int, ncols: int, /) -> None: ... - def resizeterm(nlines: int, ncols: int, /) -> None: ... - def savetty() -> None: ... - if sys.version_info >= (3, 9): - def set_escdelay(ms: int, /) -> None: ... - def set_tabsize(size: int, /) -> None: ... +def baudrate() -> int: ... +def beep() -> None: ... +def can_change_color() -> bool: ... +def cbreak(flag: bool = True, /) -> None: ... +def color_content(color_number: int, /) -> tuple[int, int, int]: ... +def color_pair(pair_number: int, /) -> int: ... +def curs_set(visibility: int, /) -> int: ... +def def_prog_mode() -> None: ... +def def_shell_mode() -> None: ... +def delay_output(ms: int, /) -> None: ... +def doupdate() -> None: ... +def echo(flag: bool = True, /) -> None: ... +def endwin() -> None: ... +def erasechar() -> bytes: ... +def filter() -> None: ... +def flash() -> None: ... +def flushinp() -> None: ... - def setsyx(y: int, x: int, /) -> None: ... - def setupterm(term: str | None = None, fd: int = -1) -> None: ... - def start_color() -> None: ... - def termattrs() -> int: ... - def termname() -> bytes: ... - def tigetflag(capname: str, /) -> int: ... - def tigetnum(capname: str, /) -> int: ... - def tigetstr(capname: str, /) -> bytes | None: ... - def tparm( - str: ReadOnlyBuffer, - i1: int = 0, - i2: int = 0, - i3: int = 0, - i4: int = 0, - i5: int = 0, - i6: int = 0, - i7: int = 0, - i8: int = 0, - i9: int = 0, - /, - ) -> bytes: ... - def typeahead(fd: int, /) -> None: ... - def unctrl(ch: _ChType, /) -> bytes: ... - if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - def unget_wch(ch: int | str, /) -> None: ... +if sys.version_info >= (3, 9): + def get_escdelay() -> int: ... + def get_tabsize() -> int: ... - def ungetch(ch: _ChType, /) -> None: ... - def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ... - def update_lines_cols() -> None: ... - def use_default_colors() -> None: ... - def use_env(flag: bool, /) -> None: ... +def getmouse() -> tuple[int, int, int, int, int]: ... +def getsyx() -> tuple[int, int]: ... +def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... +def halfdelay(tenths: int, /) -> None: ... +def has_colors() -> bool: ... - class error(Exception): ... +if sys.version_info >= (3, 10): + def has_extended_color_support() -> bool: ... - @final - class _CursesWindow: - encoding: str - @overload - def addch(self, ch: _ChType, attr: int = ...) -> None: ... - @overload - def addch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ... - @overload - def addnstr(self, str: str, n: int, attr: int = ...) -> None: ... - @overload - def addnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ... - @overload - def addstr(self, str: str, attr: int = ...) -> None: ... - @overload - def addstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... - def attroff(self, attr: int, /) -> None: ... - def attron(self, attr: int, /) -> None: ... - def attrset(self, attr: int, /) -> None: ... - def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ... - def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ... - def border( - self, - ls: _ChType = ..., - rs: _ChType = ..., - ts: _ChType = ..., - bs: _ChType = ..., - tl: _ChType = ..., - tr: _ChType = ..., - bl: _ChType = ..., - br: _ChType = ..., - ) -> None: ... - @overload - def box(self) -> None: ... - @overload - def box(self, vertch: _ChType = ..., horch: _ChType = ...) -> None: ... - @overload - def chgat(self, attr: int) -> None: ... - @overload - def chgat(self, num: int, attr: int) -> None: ... - @overload - def chgat(self, y: int, x: int, attr: int) -> None: ... - @overload - def chgat(self, y: int, x: int, num: int, attr: int) -> None: ... - def clear(self) -> None: ... - def clearok(self, yes: int) -> None: ... - def clrtobot(self) -> None: ... - def clrtoeol(self) -> None: ... - def cursyncup(self) -> None: ... - @overload - def delch(self) -> None: ... - @overload - def delch(self, y: int, x: int) -> None: ... - def deleteln(self) -> None: ... - @overload - def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... - @overload - def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... - def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... - def enclose(self, y: int, x: int, /) -> bool: ... - def erase(self) -> None: ... - def getbegyx(self) -> tuple[int, int]: ... - def getbkgd(self) -> tuple[int, int]: ... - @overload - def getch(self) -> int: ... - @overload - def getch(self, y: int, x: int) -> int: ... - if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - @overload - def get_wch(self) -> int | str: ... - @overload - def get_wch(self, y: int, x: int) -> int | str: ... +def has_ic() -> bool: ... +def has_il() -> bool: ... +def has_key(key: int, /) -> bool: ... +def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... +def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... +def initscr() -> _CursesWindow: ... +def intrflush(flag: bool, /) -> None: ... +def is_term_resized(nlines: int, ncols: int, /) -> bool: ... +def isendwin() -> bool: ... +def keyname(key: int, /) -> bytes: ... +def killchar() -> bytes: ... +def longname() -> bytes: ... +def meta(yes: bool, /) -> None: ... +def mouseinterval(interval: int, /) -> None: ... +def mousemask(newmask: int, /) -> tuple[int, int]: ... +def napms(ms: int, /) -> int: ... +def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ... +def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ... +def nl(flag: bool = True, /) -> None: ... +def nocbreak() -> None: ... +def noecho() -> None: ... +def nonl() -> None: ... +def noqiflush() -> None: ... +def noraw() -> None: ... +def pair_content(pair_number: int, /) -> tuple[int, int]: ... +def pair_number(attr: int, /) -> int: ... +def putp(string: ReadOnlyBuffer, /) -> None: ... +def qiflush(flag: bool = True, /) -> None: ... +def raw(flag: bool = True, /) -> None: ... +def reset_prog_mode() -> None: ... +def reset_shell_mode() -> None: ... +def resetty() -> None: ... +def resize_term(nlines: int, ncols: int, /) -> None: ... +def resizeterm(nlines: int, ncols: int, /) -> None: ... +def savetty() -> None: ... +if sys.version_info >= (3, 9): + def set_escdelay(ms: int, /) -> None: ... + def set_tabsize(size: int, /) -> None: ... + +def setsyx(y: int, x: int, /) -> None: ... +def setupterm(term: str | None = None, fd: int = -1) -> None: ... +def start_color() -> None: ... +def termattrs() -> int: ... +def termname() -> bytes: ... +def tigetflag(capname: str, /) -> int: ... +def tigetnum(capname: str, /) -> int: ... +def tigetstr(capname: str, /) -> bytes | None: ... +def tparm( + str: ReadOnlyBuffer, + i1: int = 0, + i2: int = 0, + i3: int = 0, + i4: int = 0, + i5: int = 0, + i6: int = 0, + i7: int = 0, + i8: int = 0, + i9: int = 0, + /, +) -> bytes: ... +def typeahead(fd: int, /) -> None: ... +def unctrl(ch: _ChType, /) -> bytes: ... + +if sys.version_info < (3, 12) or sys.platform != "darwin": + # The support for macos was dropped in 3.12 + def unget_wch(ch: int | str, /) -> None: ... + +def ungetch(ch: _ChType, /) -> None: ... +def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ... +def update_lines_cols() -> None: ... +def use_default_colors() -> None: ... +def use_env(flag: bool, /) -> None: ... + +class error(Exception): ... + +@final +class _CursesWindow: + encoding: str + @overload + def addch(self, ch: _ChType, attr: int = ...) -> None: ... + @overload + def addch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ... + @overload + def addnstr(self, str: str, n: int, attr: int = ...) -> None: ... + @overload + def addnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ... + @overload + def addstr(self, str: str, attr: int = ...) -> None: ... + @overload + def addstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... + def attroff(self, attr: int, /) -> None: ... + def attron(self, attr: int, /) -> None: ... + def attrset(self, attr: int, /) -> None: ... + def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ... + def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ... + def border( + self, + ls: _ChType = ..., + rs: _ChType = ..., + ts: _ChType = ..., + bs: _ChType = ..., + tl: _ChType = ..., + tr: _ChType = ..., + bl: _ChType = ..., + br: _ChType = ..., + ) -> None: ... + @overload + def box(self) -> None: ... + @overload + def box(self, vertch: _ChType = ..., horch: _ChType = ...) -> None: ... + @overload + def chgat(self, attr: int) -> None: ... + @overload + def chgat(self, num: int, attr: int) -> None: ... + @overload + def chgat(self, y: int, x: int, attr: int) -> None: ... + @overload + def chgat(self, y: int, x: int, num: int, attr: int) -> None: ... + def clear(self) -> None: ... + def clearok(self, yes: int) -> None: ... + def clrtobot(self) -> None: ... + def clrtoeol(self) -> None: ... + def cursyncup(self) -> None: ... + @overload + def delch(self) -> None: ... + @overload + def delch(self, y: int, x: int) -> None: ... + def deleteln(self) -> None: ... + @overload + def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + @overload + def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... + def enclose(self, y: int, x: int, /) -> bool: ... + def erase(self) -> None: ... + def getbegyx(self) -> tuple[int, int]: ... + def getbkgd(self) -> tuple[int, int]: ... + @overload + def getch(self) -> int: ... + @overload + def getch(self, y: int, x: int) -> int: ... + if sys.version_info < (3, 12) or sys.platform != "darwin": + # The support for macos was dropped in 3.12 @overload - def getkey(self) -> str: ... - @overload - def getkey(self, y: int, x: int) -> str: ... - def getmaxyx(self) -> tuple[int, int]: ... - def getparyx(self) -> tuple[int, int]: ... - @overload - def getstr(self) -> bytes: ... - @overload - def getstr(self, n: int) -> bytes: ... - @overload - def getstr(self, y: int, x: int) -> bytes: ... - @overload - def getstr(self, y: int, x: int, n: int) -> bytes: ... - def getyx(self) -> tuple[int, int]: ... - @overload - def hline(self, ch: _ChType, n: int) -> None: ... - @overload - def hline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... - def idcok(self, flag: bool) -> None: ... - def idlok(self, yes: bool) -> None: ... - def immedok(self, flag: bool) -> None: ... - @overload - def inch(self) -> int: ... - @overload - def inch(self, y: int, x: int) -> int: ... - @overload - def insch(self, ch: _ChType, attr: int = ...) -> None: ... - @overload - def insch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ... - def insdelln(self, nlines: int) -> None: ... - def insertln(self) -> None: ... - @overload - def insnstr(self, str: str, n: int, attr: int = ...) -> None: ... - @overload - def insnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ... - @overload - def insstr(self, str: str, attr: int = ...) -> None: ... - @overload - def insstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... - @overload - def instr(self, n: int = ...) -> bytes: ... - @overload - def instr(self, y: int, x: int, n: int = ...) -> bytes: ... - def is_linetouched(self, line: int, /) -> bool: ... - def is_wintouched(self) -> bool: ... - def keypad(self, yes: bool) -> None: ... - def leaveok(self, yes: bool) -> None: ... - def move(self, new_y: int, new_x: int) -> None: ... - def mvderwin(self, y: int, x: int) -> None: ... - def mvwin(self, new_y: int, new_x: int) -> None: ... - def nodelay(self, yes: bool) -> None: ... - def notimeout(self, yes: bool) -> None: ... - @overload - def noutrefresh(self) -> None: ... - @overload - def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... - @overload - def overlay(self, destwin: _CursesWindow) -> None: ... - @overload - def overlay( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int - ) -> None: ... - @overload - def overwrite(self, destwin: _CursesWindow) -> None: ... - @overload - def overwrite( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int - ) -> None: ... - def putwin(self, file: IO[Any], /) -> None: ... - def redrawln(self, beg: int, num: int, /) -> None: ... - def redrawwin(self) -> None: ... - @overload - def refresh(self) -> None: ... - @overload - def refresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... - def resize(self, nlines: int, ncols: int) -> None: ... - def scroll(self, lines: int = ...) -> None: ... - def scrollok(self, flag: bool) -> None: ... - def setscrreg(self, top: int, bottom: int, /) -> None: ... - def standend(self) -> None: ... - def standout(self) -> None: ... - @overload - def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ... - @overload - def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... - @overload - def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... - @overload - def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... - def syncdown(self) -> None: ... - def syncok(self, flag: bool) -> None: ... - def syncup(self) -> None: ... - def timeout(self, delay: int) -> None: ... - def touchline(self, start: int, count: int, changed: bool = ...) -> None: ... - def touchwin(self) -> None: ... - def untouchwin(self) -> None: ... - @overload - def vline(self, ch: _ChType, n: int) -> None: ... + def get_wch(self) -> int | str: ... @overload - def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... + def get_wch(self, y: int, x: int) -> int | str: ... + + @overload + def getkey(self) -> str: ... + @overload + def getkey(self, y: int, x: int) -> str: ... + def getmaxyx(self) -> tuple[int, int]: ... + def getparyx(self) -> tuple[int, int]: ... + @overload + def getstr(self) -> bytes: ... + @overload + def getstr(self, n: int) -> bytes: ... + @overload + def getstr(self, y: int, x: int) -> bytes: ... + @overload + def getstr(self, y: int, x: int, n: int) -> bytes: ... + def getyx(self) -> tuple[int, int]: ... + @overload + def hline(self, ch: _ChType, n: int) -> None: ... + @overload + def hline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... + def idcok(self, flag: bool) -> None: ... + def idlok(self, yes: bool) -> None: ... + def immedok(self, flag: bool) -> None: ... + @overload + def inch(self) -> int: ... + @overload + def inch(self, y: int, x: int) -> int: ... + @overload + def insch(self, ch: _ChType, attr: int = ...) -> None: ... + @overload + def insch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ... + def insdelln(self, nlines: int) -> None: ... + def insertln(self) -> None: ... + @overload + def insnstr(self, str: str, n: int, attr: int = ...) -> None: ... + @overload + def insnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ... + @overload + def insstr(self, str: str, attr: int = ...) -> None: ... + @overload + def insstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... + @overload + def instr(self, n: int = ...) -> bytes: ... + @overload + def instr(self, y: int, x: int, n: int = ...) -> bytes: ... + def is_linetouched(self, line: int, /) -> bool: ... + def is_wintouched(self) -> bool: ... + def keypad(self, yes: bool) -> None: ... + def leaveok(self, yes: bool) -> None: ... + def move(self, new_y: int, new_x: int) -> None: ... + def mvderwin(self, y: int, x: int) -> None: ... + def mvwin(self, new_y: int, new_x: int) -> None: ... + def nodelay(self, yes: bool) -> None: ... + def notimeout(self, yes: bool) -> None: ... + @overload + def noutrefresh(self) -> None: ... + @overload + def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... + @overload + def overlay(self, destwin: _CursesWindow) -> None: ... + @overload + def overlay( + self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + ) -> None: ... + @overload + def overwrite(self, destwin: _CursesWindow) -> None: ... + @overload + def overwrite( + self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + ) -> None: ... + def putwin(self, file: IO[Any], /) -> None: ... + def redrawln(self, beg: int, num: int, /) -> None: ... + def redrawwin(self) -> None: ... + @overload + def refresh(self) -> None: ... + @overload + def refresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... + def resize(self, nlines: int, ncols: int) -> None: ... + def scroll(self, lines: int = ...) -> None: ... + def scrollok(self, flag: bool) -> None: ... + def setscrreg(self, top: int, bottom: int, /) -> None: ... + def standend(self) -> None: ... + def standout(self) -> None: ... + @overload + def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + @overload + def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + @overload + def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + @overload + def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def syncdown(self) -> None: ... + def syncok(self, flag: bool) -> None: ... + def syncup(self) -> None: ... + def timeout(self, delay: int) -> None: ... + def touchline(self, start: int, count: int, changed: bool = ...) -> None: ... + def touchwin(self) -> None: ... + def untouchwin(self) -> None: ... + @overload + def vline(self, ch: _ChType, n: int) -> None: ... + @overload + def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... - class _ncurses_version(NamedTuple): - major: int - minor: int - patch: int +class _ncurses_version(NamedTuple): + major: int + minor: int + patch: int - ncurses_version: _ncurses_version - window = _CursesWindow # undocumented +ncurses_version: _ncurses_version +window = _CursesWindow # undocumented diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 4dff8d28b616..c3cc7b8c9e5a 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -1,8 +1,8 @@ import ssl import sys -from _typeshed import StrPath -from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence -from typing import Any, SupportsIndex +from _typeshed import ReadableBuffer, StrPath +from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence, Sized +from typing import Any, Protocol, SupportsIndex from typing_extensions import Self, TypeAlias from . import events, protocols, transports @@ -23,6 +23,8 @@ else: _ClientConnectedCallback: TypeAlias = Callable[[StreamReader, StreamWriter], Awaitable[None] | None] +class _ReaduntilBuffer(ReadableBuffer, Sized, Protocol): ... + if sys.version_info >= (3, 10): async def open_connection( host: str | None = None, @@ -140,8 +142,11 @@ class StreamReader(AsyncIterator[bytes]): def at_eof(self) -> bool: ... def feed_data(self, data: Iterable[SupportsIndex]) -> None: ... async def readline(self) -> bytes: ... - # Can be any buffer that supports len(); consider changing to a Protocol if PEP 688 is accepted - async def readuntil(self, separator: bytes | bytearray | memoryview = b"\n") -> bytes: ... + if sys.version_info >= (3, 13): + async def readuntil(self, separator: _ReaduntilBuffer | tuple[_ReaduntilBuffer, ...] = b"\n") -> bytes: ... + else: + async def readuntil(self, separator: _ReaduntilBuffer = b"\n") -> bytes: ... + async def read(self, n: int = -1) -> bytes: ... async def readexactly(self, n: int) -> bytes: ... def __aiter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index 2a82ae9bda22..1df184dbaa60 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -1,21 +1,22 @@ -import sys +from _curses import * +from _curses import _CursesWindow as _CursesWindow from collections.abc import Callable from typing import TypeVar from typing_extensions import Concatenate, ParamSpec -if sys.platform != "win32": - from _curses import * - from _curses import _CursesWindow as _CursesWindow +# NOTE: The _curses module is ordinarily only available on Unix, but the +# windows-curses package makes it available on Windows as well with the same +# contents. - _T = TypeVar("_T") - _P = ParamSpec("_P") +_T = TypeVar("_T") +_P = ParamSpec("_P") - # available after calling `curses.initscr()` - LINES: int - COLS: int +# available after calling `curses.initscr()` +LINES: int +COLS: int - # available after calling `curses.start_color()` - COLORS: int - COLOR_PAIRS: int +# available after calling `curses.start_color()` +COLORS: int +COLOR_PAIRS: int - def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... +def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... diff --git a/mypy/typeshed/stdlib/curses/ascii.pyi b/mypy/typeshed/stdlib/curses/ascii.pyi index 25de8f605bda..66efbe36a7df 100644 --- a/mypy/typeshed/stdlib/curses/ascii.pyi +++ b/mypy/typeshed/stdlib/curses/ascii.pyi @@ -1,63 +1,62 @@ -import sys from typing import TypeVar -if sys.platform != "win32": - _CharT = TypeVar("_CharT", str, int) +_CharT = TypeVar("_CharT", str, int) - NUL: int - SOH: int - STX: int - ETX: int - EOT: int - ENQ: int - ACK: int - BEL: int - BS: int - TAB: int - HT: int - LF: int - NL: int - VT: int - FF: int - CR: int - SO: int - SI: int - DLE: int - DC1: int - DC2: int - DC3: int - DC4: int - NAK: int - SYN: int - ETB: int - CAN: int - EM: int - SUB: int - ESC: int - FS: int - GS: int - RS: int - US: int - SP: int - DEL: int +NUL: int +SOH: int +STX: int +ETX: int +EOT: int +ENQ: int +ACK: int +BEL: int +BS: int +TAB: int +HT: int +LF: int +NL: int +VT: int +FF: int +CR: int +SO: int +SI: int +DLE: int +DC1: int +DC2: int +DC3: int +DC4: int +NAK: int +SYN: int +ETB: int +CAN: int +EM: int +SUB: int +ESC: int +FS: int +GS: int +RS: int +US: int +SP: int +DEL: int - controlnames: list[int] - def isalnum(c: str | int) -> bool: ... - def isalpha(c: str | int) -> bool: ... - def isascii(c: str | int) -> bool: ... - def isblank(c: str | int) -> bool: ... - def iscntrl(c: str | int) -> bool: ... - def isdigit(c: str | int) -> bool: ... - def isgraph(c: str | int) -> bool: ... - def islower(c: str | int) -> bool: ... - def isprint(c: str | int) -> bool: ... - def ispunct(c: str | int) -> bool: ... - def isspace(c: str | int) -> bool: ... - def isupper(c: str | int) -> bool: ... - def isxdigit(c: str | int) -> bool: ... - def isctrl(c: str | int) -> bool: ... - def ismeta(c: str | int) -> bool: ... - def ascii(c: _CharT) -> _CharT: ... - def ctrl(c: _CharT) -> _CharT: ... - def alt(c: _CharT) -> _CharT: ... - def unctrl(c: str | int) -> str: ... +controlnames: list[int] + +def isalnum(c: str | int) -> bool: ... +def isalpha(c: str | int) -> bool: ... +def isascii(c: str | int) -> bool: ... +def isblank(c: str | int) -> bool: ... +def iscntrl(c: str | int) -> bool: ... +def isdigit(c: str | int) -> bool: ... +def isgraph(c: str | int) -> bool: ... +def islower(c: str | int) -> bool: ... +def isprint(c: str | int) -> bool: ... +def ispunct(c: str | int) -> bool: ... +def isspace(c: str | int) -> bool: ... +def isupper(c: str | int) -> bool: ... +def isxdigit(c: str | int) -> bool: ... +def isctrl(c: str | int) -> bool: ... +def ismeta(c: str | int) -> bool: ... +def ascii(c: _CharT) -> _CharT: ... +def ctrl(c: _CharT) -> _CharT: ... +def alt(c: _CharT) -> _CharT: ... +def unctrl(c: str | int) -> str: ... diff --git a/mypy/typeshed/stdlib/curses/has_key.pyi b/mypy/typeshed/stdlib/curses/has_key.pyi index ff728aedf84b..3811060b916a 100644 --- a/mypy/typeshed/stdlib/curses/has_key.pyi +++ b/mypy/typeshed/stdlib/curses/has_key.pyi @@ -1,4 +1 @@ -import sys - -if sys.platform != "win32": - def has_key(ch: int | str) -> bool: ... +def has_key(ch: int | str) -> bool: ... diff --git a/mypy/typeshed/stdlib/curses/panel.pyi b/mypy/typeshed/stdlib/curses/panel.pyi index 403ae9b50019..3d3448bd9584 100644 --- a/mypy/typeshed/stdlib/curses/panel.pyi +++ b/mypy/typeshed/stdlib/curses/panel.pyi @@ -1,25 +1,22 @@ -import sys +from _curses import _CursesWindow -if sys.platform != "win32": - from _curses import _CursesWindow +version: str - version: str +class _Curses_Panel: # type is (note the space in the class name) + def above(self) -> _Curses_Panel: ... + def below(self) -> _Curses_Panel: ... + def bottom(self) -> None: ... + def hidden(self) -> bool: ... + def hide(self) -> None: ... + def move(self, y: int, x: int) -> None: ... + def replace(self, win: _CursesWindow) -> None: ... + def set_userptr(self, obj: object) -> None: ... + def show(self) -> None: ... + def top(self) -> None: ... + def userptr(self) -> object: ... + def window(self) -> _CursesWindow: ... - class _Curses_Panel: # type is (note the space in the class name) - def above(self) -> _Curses_Panel: ... - def below(self) -> _Curses_Panel: ... - def bottom(self) -> None: ... - def hidden(self) -> bool: ... - def hide(self) -> None: ... - def move(self, y: int, x: int) -> None: ... - def replace(self, win: _CursesWindow) -> None: ... - def set_userptr(self, obj: object) -> None: ... - def show(self) -> None: ... - def top(self) -> None: ... - def userptr(self) -> object: ... - def window(self) -> _CursesWindow: ... - - def bottom_panel() -> _Curses_Panel: ... - def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ... - def top_panel() -> _Curses_Panel: ... - def update_panels() -> _Curses_Panel: ... +def bottom_panel() -> _Curses_Panel: ... +def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ... +def top_panel() -> _Curses_Panel: ... +def update_panels() -> _Curses_Panel: ... diff --git a/mypy/typeshed/stdlib/curses/textpad.pyi b/mypy/typeshed/stdlib/curses/textpad.pyi index 4d28b4dfbcdc..ce6eed09b289 100644 --- a/mypy/typeshed/stdlib/curses/textpad.pyi +++ b/mypy/typeshed/stdlib/curses/textpad.pyi @@ -1,13 +1,11 @@ -import sys +from _curses import _CursesWindow from collections.abc import Callable -if sys.platform != "win32": - from _curses import _CursesWindow - def rectangle(win: _CursesWindow, uly: int, ulx: int, lry: int, lrx: int) -> None: ... +def rectangle(win: _CursesWindow, uly: int, ulx: int, lry: int, lrx: int) -> None: ... - class Textbox: - stripspaces: bool - def __init__(self, win: _CursesWindow, insert_mode: bool = False) -> None: ... - def edit(self, validate: Callable[[int], int] | None = None) -> str: ... - def do_command(self, ch: str | int) -> None: ... - def gather(self) -> str: ... +class Textbox: + stripspaces: bool + def __init__(self, win: _CursesWindow, insert_mode: bool = False) -> None: ... + def edit(self, validate: Callable[[int], int] | None = None) -> str: ... + def do_command(self, ch: str | int) -> None: ... + def gather(self) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/simple.pyi b/mypy/typeshed/stdlib/importlib/resources/simple.pyi index c360da96d856..c4c758111c2d 100644 --- a/mypy/typeshed/stdlib/importlib/resources/simple.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/simple.pyi @@ -1,6 +1,5 @@ import abc import sys -from _typeshed import Incomplete, OpenBinaryMode, OpenTextMode, Unused from collections.abc import Iterator from io import TextIOWrapper from typing import IO, Any, BinaryIO, Literal, NoReturn, overload @@ -28,11 +27,19 @@ if sys.version_info >= (3, 11): def is_file(self) -> Literal[True]: ... def is_dir(self) -> Literal[False]: ... @overload - def open(self, mode: OpenTextMode = "r", *args, **kwargs) -> TextIOWrapper: ... + def open( + self, + mode: Literal["r"] = "r", + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = False, + write_through: bool = False, + ) -> TextIOWrapper: ... @overload - def open(self, mode: OpenBinaryMode, *args: Unused, **kwargs: Unused) -> BinaryIO: ... + def open(self, mode: Literal["rb"]) -> BinaryIO: ... @overload - def open(self, mode: str, *args: Incomplete, **kwargs) -> IO[Any]: ... + def open(self, mode: str) -> IO[Any]: ... def joinpath(self, name: Never) -> NoReturn: ... # type: ignore[override] class ResourceContainer(Traversable, metaclass=abc.ABCMeta): diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index e7ed1b0b5ee5..fdbbc8dddce9 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -179,11 +179,11 @@ class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible d def __init__( self, buffer: _WrappedBuffer, - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - line_buffering: bool = ..., - write_through: bool = ..., + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = False, + write_through: bool = False, ) -> None: ... # Equals the "buffer" argument passed in to the constructor. @property diff --git a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi index 78ad79cf925f..61da7fdf1ceb 100644 --- a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi @@ -6,8 +6,8 @@ __all__ = ["ensure_running", "register", "unregister"] class ResourceTracker: def getfd(self) -> int | None: ... def ensure_running(self) -> None: ... - def register(self, name: Sized, rtype) -> None: ... - def unregister(self, name: Sized, rtype) -> None: ... + def register(self, name: Sized, rtype: str) -> None: ... + def unregister(self, name: Sized, rtype: str) -> None: ... _resource_tracker: ResourceTracker ensure_running = _resource_tracker.ensure_running diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index 8b900996f9eb..790d6c7467f0 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -2,7 +2,7 @@ import threading from _typeshed import ConvertibleToInt, Incomplete, Unused from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence from logging import Logger, _Level as _LoggingLevel -from typing import Any +from typing import Any, Generic, TypeVar, overload __all__ = [ "sub_debug", @@ -22,6 +22,9 @@ __all__ = [ "SUBWARNING", ] +_T = TypeVar("_T") +_R_co = TypeVar("_R_co", default=Any, covariant=True) + NOTSET: int SUBDEBUG: int DEBUG: int @@ -42,13 +45,29 @@ def is_abstract_socket_namespace(address: str | bytes | None) -> bool: ... abstract_sockets_supported: bool def get_temp_dir() -> str: ... -def register_after_fork(obj, func: Callable[[Incomplete], object]) -> None: ... +def register_after_fork(obj: _T, func: Callable[[_T], object]) -> None: ... -class Finalize: +class Finalize(Generic[_R_co]): + # "args" and "kwargs" are passed as arguments to "callback". + @overload + def __init__( + self, + obj: None, + callback: Callable[..., _R_co], + *, + args: Sequence[Any] = (), + kwargs: Mapping[str, Any] | None = None, + exitpriority: int, + ) -> None: ... + @overload + def __init__( + self, obj: None, callback: Callable[..., _R_co], args: Sequence[Any], kwargs: Mapping[str, Any] | None, exitpriority: int + ) -> None: ... + @overload def __init__( self, - obj: Incomplete | None, - callback: Callable[..., Incomplete], + obj: Any, + callback: Callable[..., _R_co], args: Sequence[Any] = (), kwargs: Mapping[str, Any] | None = None, exitpriority: int | None = None, @@ -59,7 +78,7 @@ class Finalize: _finalizer_registry: MutableMapping[Incomplete, Incomplete] = {}, sub_debug: Callable[..., object] = ..., getpid: Callable[[], int] = ..., - ): ... + ) -> _R_co: ... def cancel(self) -> None: ... def still_active(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index b2263df1337d..15d86372531a 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -366,6 +366,9 @@ class SSLSocket(socket.socket): def recvmsg(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] def recvmsg_into(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] def sendmsg(self, *args: Never, **kwargs: Never) -> Never: ... # type: ignore[override] + if sys.version_info >= (3, 13): + def get_verified_chain(self) -> list[bytes]: ... + def get_unverified_chain(self) -> list[bytes]: ... class TLSVersion(enum.IntEnum): MINIMUM_SUPPORTED: int @@ -476,6 +479,9 @@ class SSLObject: def version(self) -> str | None: ... def get_channel_binding(self, cb_type: str = "tls-unique") -> bytes | None: ... def verify_client_post_handshake(self) -> None: ... + if sys.version_info >= (3, 13): + def get_verified_chain(self) -> list[bytes]: ... + def get_unverified_chain(self) -> list[bytes]: ... @final class MemoryBIO: diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index a2294f2f579f..580322b653b4 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -170,7 +170,7 @@ class TypeVar: def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg): ... + def __typing_subst__(self, arg: Any) -> Any: ... # Used for an undocumented mypy feature. Does not exist at runtime. _promote = object() @@ -221,7 +221,7 @@ if sys.version_info >= (3, 11): def __init__(self, name: str) -> None: ... def __iter__(self) -> Any: ... def __typing_subst__(self, arg: Never) -> Never: ... - def __typing_prepare_subst__(self, alias, args): ... + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... if sys.version_info >= (3, 10): @final @@ -270,8 +270,8 @@ if sys.version_info >= (3, 10): @property def kwargs(self) -> ParamSpecKwargs: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg): ... - def __typing_prepare_subst__(self, alias, args): ... + def __typing_subst__(self, arg: Any) -> Any: ... + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... @@ -290,7 +290,7 @@ if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... - __supertype__: type + __supertype__: type | NewType else: def NewType(name: str, tp: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index cb67eb612a71..48a398ba4095 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -374,7 +374,7 @@ else: class NewType: def __init__(self, name: str, tp: Any) -> None: ... def __call__(self, obj: _T, /) -> _T: ... - __supertype__: type + __supertype__: type | NewType if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... @@ -413,7 +413,7 @@ class TypeVar: def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg): ... + def __typing_subst__(self, arg: Any) -> Any: ... @final class ParamSpec: @@ -453,10 +453,10 @@ class TypeVarTuple: def __iter__(self) -> Any: ... # Unpack[Self] class deprecated: - message: str + message: LiteralString category: type[Warning] | None stacklevel: int - def __init__(self, message: str, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... + def __init__(self, message: LiteralString, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... def __call__(self, arg: _T, /) -> _T: ... if sys.version_info >= (3, 12): diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index ffb0a4cb8094..d4d04817d7e0 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -1,6 +1,7 @@ import sys +from _typeshed import ReadableBuffer, Unused from types import TracebackType -from typing import Any, Literal, final +from typing import Any, Final, Literal, final, overload from typing_extensions import Self, TypeAlias if sys.platform == "win32": @@ -24,12 +25,40 @@ if sys.platform == "win32": def QueryValueEx(key: _KeyType, name: str, /) -> tuple[Any, int]: ... def SaveKey(key: _KeyType, file_name: str, /) -> None: ... def SetValue(key: _KeyType, sub_key: str, type: int, value: str, /) -> None: ... + @overload # type=REG_DWORD|REG_QWORD def SetValueEx( - key: _KeyType, value_name: str | None, reserved: Any, type: int, value: str | int, / - ) -> None: ... # reserved is ignored + key: _KeyType, value_name: str | None, reserved: Unused, type: Literal[4, 5], value: int | None, / + ) -> None: ... + @overload # type=REG_SZ|REG_EXPAND_SZ + def SetValueEx( + key: _KeyType, value_name: str | None, reserved: Unused, type: Literal[1, 2], value: str | None, / + ) -> None: ... + @overload # type=REG_MULTI_SZ + def SetValueEx( + key: _KeyType, value_name: str | None, reserved: Unused, type: Literal[7], value: list[str] | None, / + ) -> None: ... + @overload # type=REG_BINARY and everything else + def SetValueEx( + key: _KeyType, + value_name: str | None, + reserved: Unused, + type: Literal[0, 3, 8, 9, 10, 11], + value: ReadableBuffer | None, + /, + ) -> None: ... + @overload # Unknown or undocumented + def SetValueEx( + key: _KeyType, + value_name: str | None, + reserved: Unused, + type: int, + value: int | str | list[str] | ReadableBuffer | None, + /, + ) -> None: ... def DisableReflectionKey(key: _KeyType, /) -> None: ... def EnableReflectionKey(key: _KeyType, /) -> None: ... def QueryReflectionKey(key: _KeyType, /) -> bool: ... + HKEY_CLASSES_ROOT: int HKEY_CURRENT_USER: int HKEY_LOCAL_MACHINE: int @@ -38,52 +67,52 @@ if sys.platform == "win32": HKEY_CURRENT_CONFIG: int HKEY_DYN_DATA: int - KEY_ALL_ACCESS: Literal[983103] - KEY_WRITE: Literal[131078] - KEY_READ: Literal[131097] - KEY_EXECUTE: Literal[131097] - KEY_QUERY_VALUE: Literal[1] - KEY_SET_VALUE: Literal[2] - KEY_CREATE_SUB_KEY: Literal[4] - KEY_ENUMERATE_SUB_KEYS: Literal[8] - KEY_NOTIFY: Literal[16] - KEY_CREATE_LINK: Literal[32] + KEY_ALL_ACCESS: Final = 983103 + KEY_WRITE: Final = 131078 + KEY_READ: Final = 131097 + KEY_EXECUTE: Final = 131097 + KEY_QUERY_VALUE: Final = 1 + KEY_SET_VALUE: Final = 2 + KEY_CREATE_SUB_KEY: Final = 4 + KEY_ENUMERATE_SUB_KEYS: Final = 8 + KEY_NOTIFY: Final = 16 + KEY_CREATE_LINK: Final = 32 - KEY_WOW64_64KEY: Literal[256] - KEY_WOW64_32KEY: Literal[512] + KEY_WOW64_64KEY: Final = 256 + KEY_WOW64_32KEY: Final = 512 - REG_BINARY: Literal[3] - REG_DWORD: Literal[4] - REG_DWORD_LITTLE_ENDIAN: Literal[4] - REG_DWORD_BIG_ENDIAN: Literal[5] - REG_EXPAND_SZ: Literal[2] - REG_LINK: Literal[6] - REG_MULTI_SZ: Literal[7] - REG_NONE: Literal[0] - REG_QWORD: Literal[11] - REG_QWORD_LITTLE_ENDIAN: Literal[11] - REG_RESOURCE_LIST: Literal[8] - REG_FULL_RESOURCE_DESCRIPTOR: Literal[9] - REG_RESOURCE_REQUIREMENTS_LIST: Literal[10] - REG_SZ: Literal[1] + REG_BINARY: Final = 3 + REG_DWORD: Final = 4 + REG_DWORD_LITTLE_ENDIAN: Final = 4 + REG_DWORD_BIG_ENDIAN: Final = 5 + REG_EXPAND_SZ: Final = 2 + REG_LINK: Final = 6 + REG_MULTI_SZ: Final = 7 + REG_NONE: Final = 0 + REG_QWORD: Final = 11 + REG_QWORD_LITTLE_ENDIAN: Final = 11 + REG_RESOURCE_LIST: Final = 8 + REG_FULL_RESOURCE_DESCRIPTOR: Final = 9 + REG_RESOURCE_REQUIREMENTS_LIST: Final = 10 + REG_SZ: Final = 1 - REG_CREATED_NEW_KEY: int # undocumented - REG_LEGAL_CHANGE_FILTER: int # undocumented - REG_LEGAL_OPTION: int # undocumented - REG_NOTIFY_CHANGE_ATTRIBUTES: int # undocumented - REG_NOTIFY_CHANGE_LAST_SET: int # undocumented - REG_NOTIFY_CHANGE_NAME: int # undocumented - REG_NOTIFY_CHANGE_SECURITY: int # undocumented - REG_NO_LAZY_FLUSH: int # undocumented - REG_OPENED_EXISTING_KEY: int # undocumented - REG_OPTION_BACKUP_RESTORE: int # undocumented - REG_OPTION_CREATE_LINK: int # undocumented - REG_OPTION_NON_VOLATILE: int # undocumented - REG_OPTION_OPEN_LINK: int # undocumented - REG_OPTION_RESERVED: int # undocumented - REG_OPTION_VOLATILE: int # undocumented - REG_REFRESH_HIVE: int # undocumented - REG_WHOLE_HIVE_VOLATILE: int # undocumented + REG_CREATED_NEW_KEY: Final = 1 # undocumented + REG_LEGAL_CHANGE_FILTER: Final = 268435471 # undocumented + REG_LEGAL_OPTION: Final = 31 # undocumented + REG_NOTIFY_CHANGE_ATTRIBUTES: Final = 2 # undocumented + REG_NOTIFY_CHANGE_LAST_SET: Final = 4 # undocumented + REG_NOTIFY_CHANGE_NAME: Final = 1 # undocumented + REG_NOTIFY_CHANGE_SECURITY: Final = 8 # undocumented + REG_NO_LAZY_FLUSH: Final = 4 # undocumented + REG_OPENED_EXISTING_KEY: Final = 2 # undocumented + REG_OPTION_BACKUP_RESTORE: Final = 4 # undocumented + REG_OPTION_CREATE_LINK: Final = 2 # undocumented + REG_OPTION_NON_VOLATILE: Final = 0 # undocumented + REG_OPTION_OPEN_LINK: Final = 8 # undocumented + REG_OPTION_RESERVED: Final = 0 # undocumented + REG_OPTION_VOLATILE: Final = 1 # undocumented + REG_REFRESH_HIVE: Final = 2 # undocumented + REG_WHOLE_HIVE_VOLATILE: Final = 1 # undocumented error = OSError diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index 28bfa1b36924..fae2c4d98714 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -256,9 +256,9 @@ class Text(CharacterData): nodeName: str attributes: Incomplete data: Incomplete - def splitText(self, offset): ... + def splitText(self, offset: int) -> Self: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... - def replaceWholeText(self, content): ... + def replaceWholeText(self, content) -> Self | None: ... @property def isWhitespaceInElementContent(self) -> bool: ... @property diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index 62ca7dd9fc45..ab76d362e23f 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -60,7 +60,7 @@ class DOMBuilder: def supportsFeature(self, name: str) -> bool: ... def canSetFeature(self, name: str, state: int) -> bool: ... # getFeature could return any attribute from an instance of `Options` - def getFeature(self, name: str): ... + def getFeature(self, name: str) -> Any: ... def parseURI(self, uri: str) -> ExpatBuilder | ExpatBuilderNS: ... def parse(self, input: DOMInputSource) -> ExpatBuilder | ExpatBuilderNS: ... # `input` and `cnode` argtypes for `parseWithContext` are unknowable From d6d9d8cd4f27c52edac1f537e236ec48a01e54cb Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:39:02 +0100 Subject: [PATCH 0560/1617] Bump version to 1.11.0+dev (#17129) The release branch has been cut: https://github.com/python/mypy/tree/release-1.10 Increase the dev version. --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 93ab6463c573..f2615b77109d 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.10.0+dev" +__version__ = "1.11.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 0570f71f000489c94021d956662ab3373f7296bc Mon Sep 17 00:00:00 2001 From: Matthieu Devlin Date: Mon, 15 Apr 2024 21:16:51 -0700 Subject: [PATCH 0561/1617] fix: incorrect returned type of access descriptors on unions of types (#16604) Fixes https://github.com/python/mypy/issues/16603 This change maps over union types when determining the types of access descriptors. Previously, the because [this conditional](https://github.com/md384/mypy/blob/c2a55afcef32ecb11a4c76c4c79539f6ba36d55c/mypy/checkmember.py#L697-L701) would fall through to the `else` case because instance type was not a singular `TypeType` (it was a Union), so we'd end up with an instance value being passed to `__get__` instead of `None`. --- mypy/checkmember.py | 13 +++++++++++ test-data/unit/check-unions.test | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index afa8f37ff7d5..64d6733f5309 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -123,6 +123,7 @@ def copy_modified( messages: MessageBuilder | None = None, self_type: Type | None = None, is_lvalue: bool | None = None, + original_type: Type | None = None, ) -> MemberContext: mx = MemberContext( self.is_lvalue, @@ -142,6 +143,8 @@ def copy_modified( mx.self_type = self_type if is_lvalue is not None: mx.is_lvalue = is_lvalue + if original_type is not None: + mx.original_type = original_type return mx @@ -644,6 +647,16 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: return make_simplified_union( [analyze_descriptor_access(typ, mx) for typ in descriptor_type.items] ) + elif isinstance(instance_type, UnionType): + # map over the instance types + return make_simplified_union( + [ + analyze_descriptor_access( + descriptor_type, mx.copy_modified(original_type=original_type) + ) + for original_type in instance_type.items + ] + ) elif not isinstance(descriptor_type, Instance): return orig_descriptor_type diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index d79ab14184c6..2e69a96f0c78 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1220,3 +1220,41 @@ nc: Union[Container[str], int] 'x' in nc # E: Unsupported right operand type for in ("Union[Container[str], int]") [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testDescriptorAccessForUnionOfTypes] +from typing import overload, Generic, Any, TypeVar, List, Optional, Union, Type + +_T_co = TypeVar("_T_co", bound=Any, covariant=True) + +class Mapped(Generic[_T_co]): + def __init__(self, value: _T_co): + self.value = value + + @overload + def __get__( + self, instance: None, owner: Any + ) -> List[_T_co]: + ... + + @overload + def __get__(self, instance: object, owner: Any) -> _T_co: + ... + + def __get__( + self, instance: Optional[object], owner: Any + ) -> Union[List[_T_co], _T_co]: + return self.value + +class A: + field_1: Mapped[int] = Mapped(1) + field_2: Mapped[str] = Mapped('1') + +class B: + field_1: Mapped[int] = Mapped(2) + field_2: Mapped[str] = Mapped('2') + +mix: Union[Type[A], Type[B]] = A +reveal_type(mix) # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]" +reveal_type(mix.field_1) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(mix().field_1) # N: Revealed type is "builtins.int" +[builtins fixtures/list.pyi] From df35dcf020b3b03a8e3280edf8ada8c6ad8e0da5 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 17 Apr 2024 02:27:55 -0700 Subject: [PATCH 0562/1617] Error for assignment of functional Enum to variable of different name (#16805) Relates to discussion in https://discuss.python.org/t/draft-of-typing-spec-chapter-for-enums/43496/11 --- mypy/semanal_enum.py | 19 ++++++--- test-data/unit/check-enum.test | 72 +++++++++++++--------------------- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 21576ab47a84..30e0bd56c312 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -103,7 +103,10 @@ class A(enum.Enum): fullname = callee.fullname if fullname not in ENUM_BASES: return None - items, values, ok = self.parse_enum_call_args(call, fullname.split(".")[-1]) + + new_class_name, items, values, ok = self.parse_enum_call_args( + call, fullname.split(".")[-1] + ) if not ok: # Error. Construct dummy return value. name = var_name @@ -111,6 +114,10 @@ class A(enum.Enum): name += "@" + str(call.line) info = self.build_enum_call_typeinfo(name, [], fullname, node.line) else: + if new_class_name != var_name: + msg = f'String argument 1 "{new_class_name}" to {fullname}(...) does not match variable name "{var_name}"' + self.fail(msg, call) + name = cast(StrExpr, call.args[0]).value if name != var_name or is_func_scope: # Give it a unique name derived from the line number. @@ -142,7 +149,7 @@ def build_enum_call_typeinfo( def parse_enum_call_args( self, call: CallExpr, class_name: str - ) -> tuple[list[str], list[Expression | None], bool]: + ) -> tuple[str, list[str], list[Expression | None], bool]: """Parse arguments of an Enum call. Return a tuple of fields, values, was there an error. @@ -172,6 +179,8 @@ def parse_enum_call_args( return self.fail_enum_call_arg( f"{class_name}() expects a string literal as the first argument", call ) + new_class_name = value.value + items = [] values: list[Expression | None] = [] if isinstance(names, StrExpr): @@ -239,13 +248,13 @@ def parse_enum_call_args( if not values: values = [None] * len(items) assert len(items) == len(values) - return items, values, True + return new_class_name, items, values, True def fail_enum_call_arg( self, message: str, context: Context - ) -> tuple[list[str], list[Expression | None], bool]: + ) -> tuple[str, list[str], list[Expression | None], bool]: self.fail(message, context) - return [], [], False + return "", [], [], False # Helpers diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 6779ae266454..b4e8795859c3 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -452,55 +452,39 @@ from enum import Enum, IntEnum PictureSize = Enum('PictureSize', 'P0 P1 P2 P3 P4 P5 P6 P7 P8', type=str, module=__name__) fake_enum1 = Enum('fake_enum1', ['a', 'b']) -fake_enum2 = Enum('fake_enum1', names=['a', 'b']) -fake_enum3 = Enum(value='fake_enum1', names=['a', 'b']) -fake_enum4 = Enum(value='fake_enum1', names=['a', 'b'] , module=__name__) +fake_enum2 = Enum('fake_enum2', names=['a', 'b']) +fake_enum3 = Enum(value='fake_enum3', names=['a', 'b']) +fake_enum4 = Enum(value='fake_enum4', names=['a', 'b'] , module=__name__) [case testFunctionalEnumErrors] from enum import Enum, IntEnum -A = Enum('A') -B = Enum('B', 42) -C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q') -D = Enum('D', foo) +A = Enum('A') # E: Too few arguments for Enum() +B = Enum('B', 42) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members +C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q') # E: Too many arguments for Enum() +D = Enum('D', foo) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members \ + # E: Name "foo" is not defined bar = 'x y z' -E = Enum('E', bar) -I = IntEnum('I') -J = IntEnum('I', 42) -K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q') -L = Enum('L', ' ') -M = Enum('M', ()) -N = IntEnum('M', []) -P = Enum('P', [42]) -Q = Enum('Q', [('a', 42, 0)]) -R = IntEnum('R', [[0, 42]]) -S = Enum('S', {1: 1}) -T = Enum('T', keyword='a b') -U = Enum('U', *['a']) -V = Enum('U', **{'a': 1}) +E = Enum('E', bar) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members +I = IntEnum('I') # E: Too few arguments for IntEnum() +J = IntEnum('I', 42) # E: Second argument of IntEnum() must be string, tuple, list or dict literal for mypy to determine Enum members +K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q') # E: Too many arguments for IntEnum() +L = Enum('L', ' ') # E: Enum() needs at least one item +M = Enum('M', ()) # E: Enum() needs at least one item +N = IntEnum('M', []) # E: IntEnum() needs at least one item +P = Enum('P', [42]) # E: Enum() with tuple or list expects strings or (name, value) pairs +Q = Enum('Q', [('a', 42, 0)]) # E: Enum() with tuple or list expects strings or (name, value) pairs +R = IntEnum('R', [[0, 42]]) # E: IntEnum() with tuple or list expects strings or (name, value) pairs +S = Enum('S', {1: 1}) # E: Enum() with dict literal requires string literals +T = Enum('T', keyword='a b') # E: Unexpected keyword argument "keyword" +U = Enum('U', *['a']) # E: Unexpected arguments to Enum() +V = Enum('U', **{'a': 1}) # E: Unexpected arguments to Enum() W = Enum('W', 'a b') -W.c +W.c # E: "Type[W]" has no attribute "c" +X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(...) does not match variable name "X" +reveal_type(X.a) # N: Revealed type is "Literal[__main__.Something@23.a]?" +X.asdf # E: "Type[Something@23]" has no attribute "asdf" + [typing fixtures/typing-medium.pyi] -[out] -main:2: error: Too few arguments for Enum() -main:3: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members -main:4: error: Too many arguments for Enum() -main:5: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members -main:5: error: Name "foo" is not defined -main:7: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members -main:8: error: Too few arguments for IntEnum() -main:9: error: Second argument of IntEnum() must be string, tuple, list or dict literal for mypy to determine Enum members -main:10: error: Too many arguments for IntEnum() -main:11: error: Enum() needs at least one item -main:12: error: Enum() needs at least one item -main:13: error: IntEnum() needs at least one item -main:14: error: Enum() with tuple or list expects strings or (name, value) pairs -main:15: error: Enum() with tuple or list expects strings or (name, value) pairs -main:16: error: IntEnum() with tuple or list expects strings or (name, value) pairs -main:17: error: Enum() with dict literal requires string literals -main:18: error: Unexpected keyword argument "keyword" -main:19: error: Unexpected arguments to Enum() -main:20: error: Unexpected arguments to Enum() -main:22: error: "Type[W]" has no attribute "c" [case testFunctionalEnumFlag] from enum import Flag, IntFlag @@ -1117,7 +1101,7 @@ from enum import Enum class A: def __init__(self) -> None: - self.b = Enum("x", [("foo", "bar")]) # E: Enum type as attribute is not supported + self.b = Enum("b", [("foo", "bar")]) # E: Enum type as attribute is not supported reveal_type(A().b) # N: Revealed type is "Any" From 1072c78ad375b7f0511549287f54432050396717 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 21 Apr 2024 19:31:46 -0700 Subject: [PATCH 0563/1617] Fix Literal strings containing pipe characters (#17148) Fixes #16367 During semantic analysis, we try to parse all strings as types, including those inside Literal[]. Previously, we preserved the original string in the `UnboundType.original_str_expr` attribute, but if a type is parsed as a Union, we didn't have a place to put the value. This PR instead always wraps string types in a RawExpressionType node, which now optionally includes a `.node` attribute containing the parsed type. This way, we don't need to worry about preserving the original string as a custom attribute on different kinds of types that can appear in this context. The downside is that more code needs to be aware of RawExpressionType. --- mypy/fastparse.py | 11 +-- mypy/semanal.py | 31 ++++---- mypy/server/astmerge.py | 3 +- mypy/stubutil.py | 16 +++- mypy/type_visitor.py | 4 + mypy/typeanal.py | 21 ++---- mypy/types.py | 75 ++++++++----------- mypy/typetraverser.py | 3 +- mypyc/irbuild/classdef.py | 9 +-- test-data/unit/check-final.test | 2 + test-data/unit/check-literal.test | 4 + test-data/unit/check-namedtuple.test | 8 +- .../unit/check-parameter-specification.test | 23 +++++- test-data/unit/check-typeguard.test | 11 +++ test-data/unit/check-typeis.test | 11 +++ 15 files changed, 142 insertions(+), 90 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index a155187992ec..e208e4d0b7d9 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -319,14 +319,7 @@ def parse_type_string( """ try: _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None) - if isinstance(node, UnboundType) and node.original_str_expr is None: - node.original_str_expr = expr_string - node.original_str_fallback = expr_fallback_name - return node - elif isinstance(node, UnionType): - return node - else: - return RawExpressionType(expr_string, expr_fallback_name, line, column) + return RawExpressionType(expr_string, expr_fallback_name, line, column, node=node) except (SyntaxError, ValueError): # Note: the parser will raise a `ValueError` instead of a SyntaxError if # the string happens to contain things like \x00. @@ -1034,6 +1027,8 @@ def set_type_optional(self, type: Type | None, initializer: Expression | None) - return # Indicate that type should be wrapped in an Optional if arg is initialized to None. optional = isinstance(initializer, NameExpr) and initializer.name == "None" + if isinstance(type, RawExpressionType) and type.node is not None: + type = type.node if isinstance(type, UnboundType): type.optional = optional diff --git a/mypy/semanal.py b/mypy/semanal.py index 6832e767c3a4..1fc58a6c11f1 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3231,10 +3231,10 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: def analyze_lvalues(self, s: AssignmentStmt) -> None: # We cannot use s.type, because analyze_simple_literal_type() will set it. explicit = s.unanalyzed_type is not None - if self.is_final_type(s.unanalyzed_type): + final_type = self.unwrap_final_type(s.unanalyzed_type) + if final_type is not None: # We need to exclude bare Final. - assert isinstance(s.unanalyzed_type, UnboundType) - if not s.unanalyzed_type.args: + if not final_type.args: explicit = False if s.rvalue: @@ -3300,19 +3300,19 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: Returns True if Final[...] was present. """ - if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type): + final_type = self.unwrap_final_type(s.unanalyzed_type) + if final_type is None: return False - assert isinstance(s.unanalyzed_type, UnboundType) - if len(s.unanalyzed_type.args) > 1: - self.fail("Final[...] takes at most one type argument", s.unanalyzed_type) + if len(final_type.args) > 1: + self.fail("Final[...] takes at most one type argument", final_type) invalid_bare_final = False - if not s.unanalyzed_type.args: + if not final_type.args: s.type = None if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: invalid_bare_final = True self.fail("Type in Final[...] can only be omitted if there is an initializer", s) else: - s.type = s.unanalyzed_type.args[0] + s.type = final_type.args[0] if s.type is not None and self.is_classvar(s.type): self.fail("Variable should not be annotated with both ClassVar and Final", s) @@ -4713,13 +4713,18 @@ def is_classvar(self, typ: Type) -> bool: return False return sym.node.fullname == "typing.ClassVar" - def is_final_type(self, typ: Type | None) -> bool: + def unwrap_final_type(self, typ: Type | None) -> UnboundType | None: + if typ is None: + return None + typ = typ.resolve_string_annotation() if not isinstance(typ, UnboundType): - return False + return None sym = self.lookup_qualified(typ.name, typ) if not sym or not sym.node: - return False - return sym.node.fullname in FINAL_TYPE_NAMES + return None + if sym.node.fullname in FINAL_TYPE_NAMES: + return typ + return None def fail_invalid_classvar(self, context: Context) -> None: self.fail(message_registry.CLASS_VAR_OUTSIDE_OF_CLASS, context) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 174c2922c767..e6648fbb4be7 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -507,7 +507,8 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None: typ.fallback.accept(self) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - pass + if t.node is not None: + t.node.accept(self) def visit_literal_type(self, typ: LiteralType) -> None: typ.fallback.accept(self) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 410672f89d09..8e41d6862531 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -17,7 +17,16 @@ from mypy.modulefinder import ModuleNotFoundReason from mypy.moduleinspect import InspectError, ModuleInspect from mypy.stubdoc import ArgSig, FunctionSig -from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType +from mypy.types import ( + AnyType, + NoneType, + RawExpressionType, + Type, + TypeList, + TypeStrVisitor, + UnboundType, + UnionType, +) # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () @@ -291,12 +300,11 @@ def args_str(self, args: Iterable[Type]) -> str: The main difference from list_str is the preservation of quotes for string arguments """ - types = ["builtins.bytes", "builtins.str"] res = [] for arg in args: arg_str = arg.accept(self) - if isinstance(arg, UnboundType) and arg.original_str_fallback in types: - res.append(f"'{arg_str}'") + if isinstance(arg, RawExpressionType): + res.append(repr(arg.literal_value)) else: res.append(arg_str) return ", ".join(res) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 1860a43eb14f..a6ae77832ceb 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -376,6 +376,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> T: return self.query_types(t.items.values()) def visit_raw_expression_type(self, t: RawExpressionType) -> T: + if t.node is not None: + return t.node.accept(self) return self.strategy([]) def visit_literal_type(self, t: LiteralType) -> T: @@ -516,6 +518,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> bool: return self.query_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> bool: + if t.node is not None: + return t.node.accept(self) return self.default def visit_literal_type(self, t: LiteralType) -> bool: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 3f4b86185f2d..c2c578045297 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1070,6 +1070,7 @@ def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: return ret def anal_type_guard(self, t: Type) -> Type | None: + t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1088,6 +1089,7 @@ def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None: return None def anal_type_is(self, t: Type) -> Type | None: + t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1105,6 +1107,7 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: """Analyze signature argument type for *args and **kwargs argument.""" + t = t.resolve_string_annotation() if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: components = t.name.split(".") tvar_name = ".".join(components[:-1]) @@ -1195,6 +1198,8 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # make signatures like "foo(x: 20) -> None" legal, we can change # this method so it generates and returns an actual LiteralType # instead. + if t.node is not None: + return t.node.accept(self) if self.report_invalid_types: if t.base_type_name in ("builtins.int", "builtins.bool"): @@ -1455,6 +1460,7 @@ def analyze_callable_args( invalid_unpacks: list[Type] = [] second_unpack_last = False for i, arg in enumerate(arglist.items): + arg = arg.resolve_string_annotation() if isinstance(arg, CallableArgument): args.append(arg.typ) names.append(arg.name) @@ -1535,18 +1541,6 @@ def analyze_literal_type(self, t: UnboundType) -> Type: return UnionType.make_union(output, line=t.line) def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None: - # This UnboundType was originally defined as a string. - if isinstance(arg, UnboundType) and arg.original_str_expr is not None: - assert arg.original_str_fallback is not None - return [ - LiteralType( - value=arg.original_str_expr, - fallback=self.named_type(arg.original_str_fallback), - line=arg.line, - column=arg.column, - ) - ] - # If arg is an UnboundType that was *not* originally defined as # a string, try expanding it in case it's a type alias or something. if isinstance(arg, UnboundType): @@ -2528,7 +2522,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> None: self.process_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - pass + if t.node is not None: + t.node.accept(self) def visit_literal_type(self, t: LiteralType) -> None: pass diff --git a/mypy/types.py b/mypy/types.py index b4209e9debf4..5573dc9efe0e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -271,6 +271,9 @@ def can_be_true_default(self) -> bool: def can_be_false_default(self) -> bool: return True + def resolve_string_annotation(self) -> Type: + return self + def accept(self, visitor: TypeVisitor[T]) -> T: raise RuntimeError("Not implemented", type(self)) @@ -900,14 +903,7 @@ def copy_modified( class UnboundType(ProperType): """Instance type that has not been bound during semantic analysis.""" - __slots__ = ( - "name", - "args", - "optional", - "empty_tuple_index", - "original_str_expr", - "original_str_fallback", - ) + __slots__ = ("name", "args", "optional", "empty_tuple_index") def __init__( self, @@ -917,8 +913,6 @@ def __init__( column: int = -1, optional: bool = False, empty_tuple_index: bool = False, - original_str_expr: str | None = None, - original_str_fallback: str | None = None, ) -> None: super().__init__(line, column) if not args: @@ -930,21 +924,6 @@ def __init__( self.optional = optional # Special case for X[()] self.empty_tuple_index = empty_tuple_index - # If this UnboundType was originally defined as a str or bytes, keep track of - # the original contents of that string-like thing. This way, if this UnboundExpr - # ever shows up inside of a LiteralType, we can determine whether that - # Literal[...] is valid or not. E.g. Literal[foo] is most likely invalid - # (unless 'foo' is an alias for another literal or something) and - # Literal["foo"] most likely is. - # - # We keep track of the entire string instead of just using a boolean flag - # so we can distinguish between things like Literal["foo"] vs - # Literal[" foo "]. - # - # We also keep track of what the original base fallback type was supposed to be - # so we don't have to try and recompute it later - self.original_str_expr = original_str_expr - self.original_str_fallback = original_str_fallback def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundType: if args is _dummy: @@ -956,25 +935,19 @@ def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundT column=self.column, optional=self.optional, empty_tuple_index=self.empty_tuple_index, - original_str_expr=self.original_str_expr, - original_str_fallback=self.original_str_fallback, ) def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_unbound_type(self) def __hash__(self) -> int: - return hash((self.name, self.optional, tuple(self.args), self.original_str_expr)) + return hash((self.name, self.optional, tuple(self.args))) def __eq__(self, other: object) -> bool: if not isinstance(other, UnboundType): return NotImplemented return ( - self.name == other.name - and self.optional == other.optional - and self.args == other.args - and self.original_str_expr == other.original_str_expr - and self.original_str_fallback == other.original_str_fallback + self.name == other.name and self.optional == other.optional and self.args == other.args ) def serialize(self) -> JsonDict: @@ -982,19 +955,12 @@ def serialize(self) -> JsonDict: ".class": "UnboundType", "name": self.name, "args": [a.serialize() for a in self.args], - "expr": self.original_str_expr, - "expr_fallback": self.original_str_fallback, } @classmethod def deserialize(cls, data: JsonDict) -> UnboundType: assert data[".class"] == "UnboundType" - return UnboundType( - data["name"], - [deserialize_type(a) for a in data["args"]], - original_str_expr=data["expr"], - original_str_fallback=data["expr_fallback"], - ) + return UnboundType(data["name"], [deserialize_type(a) for a in data["args"]]) class CallableArgument(ProperType): @@ -2646,7 +2612,7 @@ class RawExpressionType(ProperType): This synthetic type is only used at the beginning stages of semantic analysis and should be completely removing during the process for mapping UnboundTypes to - actual types: we either turn it into a LiteralType or an AnyType. + actual types: we turn it into its "node" argument, a LiteralType, or an AnyType. For example, suppose `Foo[1]` is initially represented as the following: @@ -2684,7 +2650,7 @@ class RawExpressionType(ProperType): ) """ - __slots__ = ("literal_value", "base_type_name", "note") + __slots__ = ("literal_value", "base_type_name", "note", "node") def __init__( self, @@ -2693,11 +2659,13 @@ def __init__( line: int = -1, column: int = -1, note: str | None = None, + node: Type | None = None, ) -> None: super().__init__(line, column) self.literal_value = literal_value self.base_type_name = base_type_name self.note = note + self.node = node def simple_name(self) -> str: return self.base_type_name.replace("builtins.", "") @@ -2707,6 +2675,21 @@ def accept(self, visitor: TypeVisitor[T]) -> T: ret: T = visitor.visit_raw_expression_type(self) return ret + def copy_modified(self, node: Type | None) -> RawExpressionType: + return RawExpressionType( + literal_value=self.literal_value, + base_type_name=self.base_type_name, + line=self.line, + column=self.column, + note=self.note, + node=node, + ) + + def resolve_string_annotation(self) -> Type: + if self.node is not None: + return self.node.resolve_string_annotation() + return self + def serialize(self) -> JsonDict: assert False, "Synthetic types don't serialize" @@ -2718,6 +2701,7 @@ def __eq__(self, other: object) -> bool: return ( self.base_type_name == other.base_type_name and self.literal_value == other.literal_value + and self.node == other.node ) else: return NotImplemented @@ -3386,6 +3370,8 @@ def item_str(name: str, typ: str) -> str: return f"TypedDict({prefix}{s})" def visit_raw_expression_type(self, t: RawExpressionType) -> str: + if t.node is not None: + return t.node.accept(self) return repr(t.literal_value) def visit_literal_type(self, t: LiteralType) -> str: @@ -3449,6 +3435,9 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return t def visit_raw_expression_type(self, t: RawExpressionType) -> Type: + if t.node is not None: + node = t.node.accept(self) + return t.copy_modified(node=node) return t def visit_type_list(self, t: TypeList) -> Type: diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index a28bbf422b61..4d740a802b55 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -130,7 +130,8 @@ def visit_partial_type(self, t: PartialType) -> None: pass def visit_raw_expression_type(self, t: RawExpressionType) -> None: - pass + if t.node is not None: + t.node.accept(self) def visit_type_alias_type(self, t: TypeAliasType) -> None: # TODO: sometimes we want to traverse target as well diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index fc2bb4a1fc2f..3f6ec0f33822 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -24,7 +24,7 @@ TypeInfo, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, get_proper_type +from mypy.types import ENUM_REMOVED_PROPS, Instance, RawExpressionType, get_proper_type from mypyc.common import PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature @@ -601,16 +601,15 @@ def add_non_ext_class_attr_ann( if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? ann_type = get_proper_type(stmt.type) - if ( - isinstance(stmt.unanalyzed_type, UnboundType) - and stmt.unanalyzed_type.original_str_expr is not None + if isinstance(stmt.unanalyzed_type, RawExpressionType) and isinstance( + stmt.unanalyzed_type.literal_value, str ): # Annotation is a forward reference, so don't attempt to load the actual # type and load the string instead. # # TODO: is it possible to determine whether a non-string annotation is # actually a forward reference due to the __annotations__ future? - typ = builder.load_str(stmt.unanalyzed_type.original_str_expr) + typ = builder.load_str(stmt.unanalyzed_type.literal_value) elif isinstance(ann_type, Instance): typ = load_type(builder, ann_type.type, stmt.line) else: diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index b1378a47b1b1..26a0d0782503 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -6,11 +6,13 @@ [case testFinalDefiningModuleVar] from typing import Final +w: 'Final' = int() x: Final = int() y: Final[float] = int() z: Final[int] = int() bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") +reveal_type(w) # N: Revealed type is "builtins.int" reveal_type(x) # N: Revealed type is "builtins.int" reveal_type(y) # N: Revealed type is "builtins.float" reveal_type(z) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 5604cc4b5893..3cf6e8ff17e9 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -12,8 +12,12 @@ reveal_type(g1) # N: Revealed type is "def (x: Literal['A['])" def f2(x: 'A B') -> None: pass # E: Invalid type comment or annotation def g2(x: Literal['A B']) -> None: pass +def h2(x: 'A|int') -> None: pass # E: Name "A" is not defined +def i2(x: Literal['A|B']) -> None: pass reveal_type(f2) # N: Revealed type is "def (x: Any)" reveal_type(g2) # N: Revealed type is "def (x: Literal['A B'])" +reveal_type(h2) # N: Revealed type is "def (x: Union[Any, builtins.int])" +reveal_type(i2) # N: Revealed type is "def (x: Literal['A|B'])" [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 0ce8630e51d9..23e109e1af78 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -802,14 +802,20 @@ class Fraction(Real): [builtins fixtures/tuple.pyi] [case testForwardReferenceInNamedTuple] -from typing import NamedTuple +from typing import List, NamedTuple class A(NamedTuple): b: 'B' x: int + y: List['B'] class B: pass + +def f(a: A): + reveal_type(a.b) # N: Revealed type is "__main__.B" + reveal_type(a.x) # N: Revealed type is "builtins.int" + reveal_type(a.y) # N: Revealed type is "builtins.list[__main__.B]" [builtins fixtures/tuple.pyi] [case testTypeNamedTupleClassmethod] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 8fd9abcb9752..cab7d2bf6819 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1193,7 +1193,28 @@ def func(callback: Callable[P, str]) -> Callable[P, str]: return inner [builtins fixtures/paramspec.pyi] -[case testParamSpecArgsAndKwargsMissmatch] +[case testParamSpecArgsAndKwargsStringified] +from typing import Callable +from typing_extensions import ParamSpec + +P1 = ParamSpec("P1") + +def func(callback: Callable[P1, str]) -> Callable[P1, str]: + def inner(*args: "P1.args", **kwargs: "P1.kwargs") -> str: + return "foo" + return inner + +@func +def outer(a: int) -> str: + return "" + +outer(1) # OK +outer("x") # E: Argument 1 to "outer" has incompatible type "str"; expected "int" +outer(a=1) # OK +outer(b=1) # E: Unexpected keyword argument "b" for "outer" +[builtins fixtures/paramspec.pyi] + +[case testParamSpecArgsAndKwargsMismatch] from typing import Callable from typing_extensions import ParamSpec diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 27b88553fb43..e1b7a86aba63 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -9,6 +9,17 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] +[case testTypeGuardStringified] +from typing_extensions import TypeGuard +class Point: pass +def is_point(a: object) -> "TypeGuard[Point]": pass +def main(a: object) -> None: + if is_point(a): + reveal_type(a) # N: Revealed type is "__main__.Point" + else: + reveal_type(a) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] + [case testTypeGuardTypeArgsNone] from typing_extensions import TypeGuard def foo(a: object) -> TypeGuard: # E: TypeGuard must have exactly one type argument diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 6b96845504ab..83467d5e3683 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -9,6 +9,17 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] +[case testTypeIsStringified] +from typing_extensions import TypeIs +class Point: pass +def is_point(a: object) -> "TypeIs[Point]": pass +def main(a: object) -> None: + if is_point(a): + reveal_type(a) # N: Revealed type is "__main__.Point" + else: + reveal_type(a) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] + [case testTypeIsElif] from typing_extensions import TypeIs from typing import Union From f7687d30440564e0582755194872f7fa1b915fdd Mon Sep 17 00:00:00 2001 From: GiorgosPapoutsakis <116210016+GiorgosPapoutsakis@users.noreply.github.com> Date: Mon, 22 Apr 2024 08:58:49 +0300 Subject: [PATCH 0564/1617] Update CONTRIBUTING.md to include commands for Windows (#17142) Add command about how to activate virtual environment on Windows. --- CONTRIBUTING.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46292c301406..a5d339330a75 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,17 +30,23 @@ cd mypy #### (3) Create then activate a virtual environment ```bash -# On Windows, the commands may be slightly different. For more details, see -# https://docs.python.org/3/library/venv.html#creating-virtual-environments python3 -m venv venv source venv/bin/activate ``` +```bash +# For Windows use +python -m venv venv +. venv/Scripts/activate + +# For more details, see https://docs.python.org/3/library/venv.html#creating-virtual-environments +``` + #### (4) Install the test requirements and the project ```bash -python3 -m pip install -r test-requirements.txt -python3 -m pip install -e . +python -m pip install -r test-requirements.txt +python -m pip install -e . hash -r # This resets shell PATH cache, not necessary on Windows ``` From 810a019c535ec0366cf9b2359129dec884b06a3e Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:45:56 +0100 Subject: [PATCH 0565/1617] Update CHANGELOG.md with draft for release 1.10 (#17150) Initial pass at blog post for Release 1.10. Still need to add some information about the major changes. Pulled up 3 commits that seemed like we might want to write something about (under TODO), but they can move them to "Other Notable Changes and Fixes" if that's not the case. --- CHANGELOG.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bd537d46e9c..66b7cea86fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,111 @@ # Mypy Release Notes +## Next release + + + +## Mypy 1.10 (Unreleased) + +We’ve just uploaded mypy 1.10 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +**TODO** +- Implement TypeIs (PEP 742) (Jelle Zijlstra, PR [16898](https://github.com/python/mypy/pull/16898)) +- Error handling for recursive TypeVar defaults (PEP 696) (Marc Mueller, PR [16925](https://github.com/python/mypy/pull/16925)) +- Add basic support for recursive TypeVar defaults (PEP 696) (Marc Mueller, PR [16878](https://github.com/python/mypy/pull/16878)) + +#### Other Notable Changes and Fixes +- fix: incorrect returned type of access descriptors on unions of types (Matthieu Devlin, PR [16604](https://github.com/python/mypy/pull/16604)) +- Fix crash when expanding invalid Unpack in a `Callable` alias (Ali Hamdan, PR [17028](https://github.com/python/mypy/pull/17028)) +- Fix string formatting for string enums (roberfi, PR [16555](https://github.com/python/mypy/pull/16555)) +- Narrow individual items when matching a tuple to a sequence pattern (Loïc Simon, PR [16905](https://github.com/python/mypy/pull/16905)) +- Add TypeGuard and TypeIs traversing in TypeTraverserVisitor (Evgeniy Slobodkin, PR [17071](https://github.com/python/mypy/pull/17071)) +- Improve error message for bound typevar in TypeAliasType (Ali Hamdan, PR [17053](https://github.com/python/mypy/pull/17053)) +- Fix TypedDict init from Type with optional keys (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) +- Improve yield from inference for unions of generators (Shantanu, PR [16717](https://github.com/python/mypy/pull/16717)) +- Support `TypeAliasType` in a class scope (Ali Hamdan, PR [17038](https://github.com/python/mypy/pull/17038)) +- attrs: Fix emulating hash method logic (Hashem, PR [17016](https://github.com/python/mypy/pull/17016)) +- Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) +- Revert "Revert use of `ParamSpec` for `functools.wraps`" (Tamir Duberstein, PR [16942](https://github.com/python/mypy/pull/16942)) +- Support `TypeAliasType` (Ali Hamdan, PR [16926](https://github.com/python/mypy/pull/16926)) +- Fix type narrowing for types.EllipsisType (Shantanu, PR [17003](https://github.com/python/mypy/pull/17003)) +- Disallow all super calls to methods with trivial bodies (Shantanu, PR [16756](https://github.com/python/mypy/pull/16756)) +- Fix single item enum match type exhaustion (Oskari Lehto, PR [16966](https://github.com/python/mypy/pull/16966)) +- Fix inference with UninhabitedType (Marc Mueller, PR [16994](https://github.com/python/mypy/pull/16994)) +- Allow TypedDict initialization from Type (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) +- Fix override checking for decorated property (Shantanu, PR [16856](https://github.com/python/mypy/pull/16856)) +- Fix duplicate word in protocols.rst (hesam, PR [16950](https://github.com/python/mypy/pull/16950)) +- Workaround parenthesised context manager issue (Shantanu, PR [16949](https://github.com/python/mypy/pull/16949)) +- Fix narrowing on match with function subject (Edward Paget, PR [16503](https://github.com/python/mypy/pull/16503)) +- Allow inferring +int to be a Literal (Spencer Brown, PR [16910](https://github.com/python/mypy/pull/16910)) + +#### Stubgen Improvements +- stubgen: Preserve empty tuple annotation (Ali Hamdan, PR [16907](https://github.com/python/mypy/pull/16907)) +- stubgen: Add support for PEP 570 positional-only parameters (Ali Hamdan, PR [16904](https://github.com/python/mypy/pull/16904)) +- stubgen: Replace obsolete typing aliases with builtin containers (Ali Hamdan, PR [16780](https://github.com/python/mypy/pull/16780)) +- stubgen: Fix generated dataclass `__init__` signature (Ali Hamdan, PR [16906](https://github.com/python/mypy/pull/16906)) + +#### Stubtest Improvements +- stubtest: correct type annotations in _Arguments (Sam Xifaras, PR [16897](https://github.com/python/mypy/pull/16897)) + +#### Mypyc Improvements +- [mypyc] Refactor: add two list primitive ops (Jukka Lehtosalo, PR [17058](https://github.com/python/mypy/pull/17058)) +- [mypyc] Refactor: use primitive op for initializing list item (Jukka Lehtosalo, PR [17056](https://github.com/python/mypy/pull/17056)) +- [mypyc] Refactor: move tagged int related code to mypyc.lower.int_ops (Jukka Lehtosalo, PR [17052](https://github.com/python/mypy/pull/17052)) +- [mypyc] Implement lowering for remaining tagged integer comparisons (Jukka Lehtosalo, PR [17040](https://github.com/python/mypy/pull/17040)) +- [mypyc] Implement lowering pass and add primitives for int (in)equality (Jukka Lehtosalo, PR [17027](https://github.com/python/mypy/pull/17027)) +- [mypyc] Optimize away some bool/bit registers (Jukka Lehtosalo, PR [17022](https://github.com/python/mypy/pull/17022)) +- [mypyc] Provide an easier way to define IR-to-IR transforms (Jukka Lehtosalo, PR [16998](https://github.com/python/mypy/pull/16998)) +- [mypyc] Remangle redefined names produced by async with (Richard Si, PR [16408](https://github.com/python/mypy/pull/16408)) +- [mypyc] Optimize TYPE_CHECKING to False at Runtime (Srinivas Lade, PR [16263](https://github.com/python/mypy/pull/16263)) +- [mypyc] Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) +- [mypyc] Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) + +#### Documentation Improvements +- Update running_mypy.rst add closing bracket (Roman Solomatin, PR [17046](https://github.com/python/mypy/pull/17046)) +- Docs: docstrings in checker.py, ast_helpers.py (Ihor, PR [16908](https://github.com/python/mypy/pull/16908)) +- docs: Add missing ClassVar import (youkaichao, PR [16962](https://github.com/python/mypy/pull/16962)) +- Docs: Update `TypedDict` import statements (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) +- Docs: adding missing `mutable-override` to section title (James Braza, PR [16886](https://github.com/python/mypy/pull/16886)) + +#### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Alex Waygood +- Ali Hamdan +- Edward Paget +- Evgeniy Slobodkin +- Hashem +- hesam +- Hugo van Kemenade +- Ihor +- James Braza +- Jelle Zijlstra +- jhance +- Jukka Lehtosalo +- Loïc Simon +- Marc Mueller +- Matthieu Devlin +- Michael R. Crusoe +- Nikita Sobolev +- Oskari Lehto +- Riccardo Di Maio +- Richard Si +- roberfi +- Roman Solomatin +- Sam Xifaras +- Shantanu +- Spencer Brown +- Srinivas Lade +- Tamir Duberstein +- youkaichao + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + + ## Mypy 1.9 We’ve just uploaded mypy 1.9 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: From c1460f85e8db919a2f7c32c979d3b25aedc38a78 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 23 Apr 2024 17:32:11 +0100 Subject: [PATCH 0566/1617] Various updates to changelog for 1.10 (#17158) --- CHANGELOG.md | 159 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b7cea86fb5..243d46946326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,64 +12,121 @@ We’ve just uploaded mypy 1.10 to the Python Package Index ([PyPI](https://pypi You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -**TODO** -- Implement TypeIs (PEP 742) (Jelle Zijlstra, PR [16898](https://github.com/python/mypy/pull/16898)) -- Error handling for recursive TypeVar defaults (PEP 696) (Marc Mueller, PR [16925](https://github.com/python/mypy/pull/16925)) -- Add basic support for recursive TypeVar defaults (PEP 696) (Marc Mueller, PR [16878](https://github.com/python/mypy/pull/16878)) +#### Support TypeIs (PEP 742) -#### Other Notable Changes and Fixes -- fix: incorrect returned type of access descriptors on unions of types (Matthieu Devlin, PR [16604](https://github.com/python/mypy/pull/16604)) -- Fix crash when expanding invalid Unpack in a `Callable` alias (Ali Hamdan, PR [17028](https://github.com/python/mypy/pull/17028)) -- Fix string formatting for string enums (roberfi, PR [16555](https://github.com/python/mypy/pull/16555)) -- Narrow individual items when matching a tuple to a sequence pattern (Loïc Simon, PR [16905](https://github.com/python/mypy/pull/16905)) -- Add TypeGuard and TypeIs traversing in TypeTraverserVisitor (Evgeniy Slobodkin, PR [17071](https://github.com/python/mypy/pull/17071)) -- Improve error message for bound typevar in TypeAliasType (Ali Hamdan, PR [17053](https://github.com/python/mypy/pull/17053)) -- Fix TypedDict init from Type with optional keys (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) -- Improve yield from inference for unions of generators (Shantanu, PR [16717](https://github.com/python/mypy/pull/16717)) -- Support `TypeAliasType` in a class scope (Ali Hamdan, PR [17038](https://github.com/python/mypy/pull/17038)) -- attrs: Fix emulating hash method logic (Hashem, PR [17016](https://github.com/python/mypy/pull/17016)) -- Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) -- Revert "Revert use of `ParamSpec` for `functools.wraps`" (Tamir Duberstein, PR [16942](https://github.com/python/mypy/pull/16942)) -- Support `TypeAliasType` (Ali Hamdan, PR [16926](https://github.com/python/mypy/pull/16926)) -- Fix type narrowing for types.EllipsisType (Shantanu, PR [17003](https://github.com/python/mypy/pull/17003)) -- Disallow all super calls to methods with trivial bodies (Shantanu, PR [16756](https://github.com/python/mypy/pull/16756)) -- Fix single item enum match type exhaustion (Oskari Lehto, PR [16966](https://github.com/python/mypy/pull/16966)) -- Fix inference with UninhabitedType (Marc Mueller, PR [16994](https://github.com/python/mypy/pull/16994)) -- Allow TypedDict initialization from Type (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) -- Fix override checking for decorated property (Shantanu, PR [16856](https://github.com/python/mypy/pull/16856)) -- Fix duplicate word in protocols.rst (hesam, PR [16950](https://github.com/python/mypy/pull/16950)) -- Workaround parenthesised context manager issue (Shantanu, PR [16949](https://github.com/python/mypy/pull/16949)) -- Fix narrowing on match with function subject (Edward Paget, PR [16503](https://github.com/python/mypy/pull/16503)) -- Allow inferring +int to be a Literal (Spencer Brown, PR [16910](https://github.com/python/mypy/pull/16910)) +Mypy now supports `TypeIs` ([PEP 742](https://peps.python.org/pep-0742/)), which allows +functions to narrow the type of a value, similar to `isinstance()`. Unlike `TypeGuard`, +`TypeIs` can narrow in both the `if` and `else` branches of an if statement: -#### Stubgen Improvements -- stubgen: Preserve empty tuple annotation (Ali Hamdan, PR [16907](https://github.com/python/mypy/pull/16907)) -- stubgen: Add support for PEP 570 positional-only parameters (Ali Hamdan, PR [16904](https://github.com/python/mypy/pull/16904)) -- stubgen: Replace obsolete typing aliases with builtin containers (Ali Hamdan, PR [16780](https://github.com/python/mypy/pull/16780)) -- stubgen: Fix generated dataclass `__init__` signature (Ali Hamdan, PR [16906](https://github.com/python/mypy/pull/16906)) +```python +from typing_extensions import TypeIs -#### Stubtest Improvements -- stubtest: correct type annotations in _Arguments (Sam Xifaras, PR [16897](https://github.com/python/mypy/pull/16897)) +def is_str(s: object) -> TypeIs[str]: + return isinstance(s, str) + +def f(o: str | int) -> None: + if is_str(o): + # Type of o is 'str' + ... + else: + # Type of o is 'int' + ... +``` + +`TypeIs` will be added to the `typing` module in Python 3.13, but it +can be used on earlier Python versions by importing it from +`typing_extensions`. + +This feature was contributed by Jelle Zijlstra (PR [16898](https://github.com/python/mypy/pull/16898)). + +#### Support TypeVar Defaults (PEP 696) + +[PEP 696](https://peps.python.org/pep-0696/) adds support for type parameter defaults. +Example: + +```python +from typing import Generic +from typing_extensions import TypeVar + +T = TypeVar("T", default=int) + +class C(Generic[T]): + ... + +x: C = ... +y: C[str] = ... +reveal_type(x) # C[int], because of the default +reveal_type(y) # C[str] +``` + +TypeVar defaults will be added to the `typing` module in Python 3.13, but they +can be used with earlier Python releases by importing `TypeVar` from +`typing_extensions`. + +This feature was contributed by Marc Mueller (PR [16878](https://github.com/python/mypy/pull/16878) +and PR [16925](https://github.com/python/mypy/pull/16925)). + +#### Detect Additional Unsafe Uses of super() + +Mypy will reject unsafe uses of `super()` more consistently, when the target has a +trivial (empty) body. Example: + +```python +class Proto(Protocol): + def method(self) -> int: ... + +class Sub(Proto): + def method(self) -> int: + return super().meth() # Error (unsafe) +``` + +This feature was contributed by Shantanu (PR [16756](https://github.com/python/mypy/pull/16756)). + +#### Stubgen Improvements +- Preserve empty tuple annotation (Ali Hamdan, PR [16907](https://github.com/python/mypy/pull/16907)) +- Add support for PEP 570 positional-only parameters (Ali Hamdan, PR [16904](https://github.com/python/mypy/pull/16904)) +- Replace obsolete typing aliases with builtin containers (Ali Hamdan, PR [16780](https://github.com/python/mypy/pull/16780)) +- Fix generated dataclass `__init__` signature (Ali Hamdan, PR [16906](https://github.com/python/mypy/pull/16906)) #### Mypyc Improvements -- [mypyc] Refactor: add two list primitive ops (Jukka Lehtosalo, PR [17058](https://github.com/python/mypy/pull/17058)) -- [mypyc] Refactor: use primitive op for initializing list item (Jukka Lehtosalo, PR [17056](https://github.com/python/mypy/pull/17056)) -- [mypyc] Refactor: move tagged int related code to mypyc.lower.int_ops (Jukka Lehtosalo, PR [17052](https://github.com/python/mypy/pull/17052)) -- [mypyc] Implement lowering for remaining tagged integer comparisons (Jukka Lehtosalo, PR [17040](https://github.com/python/mypy/pull/17040)) -- [mypyc] Implement lowering pass and add primitives for int (in)equality (Jukka Lehtosalo, PR [17027](https://github.com/python/mypy/pull/17027)) -- [mypyc] Optimize away some bool/bit registers (Jukka Lehtosalo, PR [17022](https://github.com/python/mypy/pull/17022)) -- [mypyc] Provide an easier way to define IR-to-IR transforms (Jukka Lehtosalo, PR [16998](https://github.com/python/mypy/pull/16998)) -- [mypyc] Remangle redefined names produced by async with (Richard Si, PR [16408](https://github.com/python/mypy/pull/16408)) -- [mypyc] Optimize TYPE_CHECKING to False at Runtime (Srinivas Lade, PR [16263](https://github.com/python/mypy/pull/16263)) -- [mypyc] Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) -- [mypyc] Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) + +- Provide an easier way to define IR-to-IR transforms (Jukka Lehtosalo, PR [16998](https://github.com/python/mypy/pull/16998)) +- Implement lowering pass and add primitives for int (in)equality (Jukka Lehtosalo, PR [17027](https://github.com/python/mypy/pull/17027)) +- Implement lowering for remaining tagged integer comparisons (Jukka Lehtosalo, PR [17040](https://github.com/python/mypy/pull/17040)) +- Optimize away some bool/bit registers (Jukka Lehtosalo, PR [17022](https://github.com/python/mypy/pull/17022)) +- Remangle redefined names produced by async with (Richard Si, PR [16408](https://github.com/python/mypy/pull/16408)) +- Optimize TYPE_CHECKING to False at Runtime (Srinivas Lade, PR [16263](https://github.com/python/mypy/pull/16263)) +- Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) +- Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) +- Support `TypeAliasType` (Ali Hamdan, PR [16926](https://github.com/python/mypy/pull/16926)) #### Documentation Improvements -- Update running_mypy.rst add closing bracket (Roman Solomatin, PR [17046](https://github.com/python/mypy/pull/17046)) -- Docs: docstrings in checker.py, ast_helpers.py (Ihor, PR [16908](https://github.com/python/mypy/pull/16908)) -- docs: Add missing ClassVar import (youkaichao, PR [16962](https://github.com/python/mypy/pull/16962)) -- Docs: Update `TypedDict` import statements (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) -- Docs: adding missing `mutable-override` to section title (James Braza, PR [16886](https://github.com/python/mypy/pull/16886)) +- Import `TypedDict` from `typing` instead of `typing_extensions` (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) +- Add missing `mutable-override` to section title (James Braza, PR [16886](https://github.com/python/mypy/pull/16886)) + +#### Error Reporting Improvements + +- Improve error message for bound TypeVar in TypeAliasType (Ali Hamdan, PR [17053](https://github.com/python/mypy/pull/17053)) +- Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) + +#### Other Notable Changes and Fixes +- Fix incorrect inferred type when accessing descriptor on union type (Matthieu Devlin, PR [16604](https://github.com/python/mypy/pull/16604)) +- Fix crash when expanding invalid `Unpack` in a `Callable` alias (Ali Hamdan, PR [17028](https://github.com/python/mypy/pull/17028)) +- Fix false positive when string formatting with string enum (roberfi, PR [16555](https://github.com/python/mypy/pull/16555)) +- Narrow individual items when matching a tuple to a sequence pattern (Loïc Simon, PR [16905](https://github.com/python/mypy/pull/16905)) +- Fix false positive from type variable within TypeGuard or TypeIs (Evgeniy Slobodkin, PR [17071](https://github.com/python/mypy/pull/17071)) +- Improve `yield from` inference for unions of generators (Shantanu, PR [16717](https://github.com/python/mypy/pull/16717)) +- Support `TypeAliasType` in a class scope (Ali Hamdan, PR [17038](https://github.com/python/mypy/pull/17038)) +- Fix emulating hash method logic in `attrs` classes (Hashem, PR [17016](https://github.com/python/mypy/pull/17016)) +- Add reverted typeshed commit that uses `ParamSpec` for `functools.wraps` (Tamir Duberstein, PR [16942](https://github.com/python/mypy/pull/16942)) +- Fix type narrowing for `types.EllipsisType` (Shantanu, PR [17003](https://github.com/python/mypy/pull/17003)) +- Fix single item enum match type exhaustion (Oskari Lehto, PR [16966](https://github.com/python/mypy/pull/16966)) +- Improve type inference with empty collections (Marc Mueller, PR [16994](https://github.com/python/mypy/pull/16994)) +- Fix override checking for decorated property (Shantanu, PR [16856](https://github.com/python/mypy/pull/16856)) +- Fix narrowing on match with function subject (Edward Paget, PR [16503](https://github.com/python/mypy/pull/16503)) +- Allow `+N` within `Literal[...]` (Spencer Brown, PR [16910](https://github.com/python/mypy/pull/16910)) +- Experimental: Support TypedDict within `type[...]` (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) +- Experimtental: Fix issue with TypedDict with optional keys in `type[...]` (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) #### Acknowledgements Thanks to all mypy contributors who contributed to this release: From 400eece0731772b8c584bc335f13b08eee4e3b61 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:58:18 +0100 Subject: [PATCH 0567/1617] Update CHANGELOG.md (#17159) - add typeshed updates note - remove unreleased --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 243d46946326..a90997f6cc3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ -## Mypy 1.10 (Unreleased) +## Mypy 1.10 We’ve just uploaded mypy 1.10 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: @@ -128,6 +128,11 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Experimental: Support TypedDict within `type[...]` (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) - Experimtental: Fix issue with TypedDict with optional keys in `type[...]` (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) +#### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=7c8e82fe483a40ec4cb0a2505cfdb0f3e7cc81d9+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + + #### Acknowledgements Thanks to all mypy contributors who contributed to this release: From 43e130b1bb2f912e7b51354570da041772d33767 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:26:48 +0100 Subject: [PATCH 0568/1617] Update CHANGELOG.md to point out PEP 695 initial support (#17164) --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a90997f6cc3a..d0ea19866892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,42 @@ can be used with earlier Python releases by importing `TypeVar` from This feature was contributed by Marc Mueller (PR [16878](https://github.com/python/mypy/pull/16878) and PR [16925](https://github.com/python/mypy/pull/16925)). +#### Support TypeAliasType (PEP 695) +As part of the initial steps towards implementing [PEP 695](https://peps.python.org/pep-0695/), mypy now supports `TypeAliasType`. +`TypeAliasType` provides a backport of the new `type` statement in Python 3.12. + +```python +type ListOrSet[T] = list[T] | set[T] +``` + +is equivalent to: + +```python +T = TypeVar("T") +ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) +``` + +Example of use in mypy: + +```python +from typing_extensions import TypeAliasType, TypeVar + +NewUnionType = TypeAliasType("NewUnionType", int | str) +x: NewUnionType = 42 +y: NewUnionType = 'a' +z: NewUnionType = object() # error: Incompatible types in assignment (expression has type "object", variable has type "int | str") [assignment] + +T = TypeVar("T") +ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) +a: ListOrSet[int] = [1, 2] +b: ListOrSet[str] = {'a', 'b'} +c: ListOrSet[str] = 'test' # error: Incompatible types in assignment (expression has type "str", variable has type "list[str] | set[str]") [assignment] +``` + +`TypeAliasType` was added to the `typing` module in Python 3.12, but it can be used with earlier Python releases by importing from `typing_extensions`. + +This feature was contributed by Ali Hamdan (PR [16926](https://github.com/python/mypy/pull/16926), PR [17038](https://github.com/python/mypy/pull/17038) and PR [17053](https://github.com/python/mypy/pull/17053)) + #### Detect Additional Unsafe Uses of super() Mypy will reject unsafe uses of `super()` more consistently, when the target has a @@ -98,7 +134,6 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Optimize TYPE_CHECKING to False at Runtime (Srinivas Lade, PR [16263](https://github.com/python/mypy/pull/16263)) - Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) - Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) -- Support `TypeAliasType` (Ali Hamdan, PR [16926](https://github.com/python/mypy/pull/16926)) #### Documentation Improvements - Import `TypedDict` from `typing` instead of `typing_extensions` (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) @@ -106,7 +141,6 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m #### Error Reporting Improvements -- Improve error message for bound TypeVar in TypeAliasType (Ali Hamdan, PR [17053](https://github.com/python/mypy/pull/17053)) - Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) #### Other Notable Changes and Fixes @@ -116,7 +150,6 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Narrow individual items when matching a tuple to a sequence pattern (Loïc Simon, PR [16905](https://github.com/python/mypy/pull/16905)) - Fix false positive from type variable within TypeGuard or TypeIs (Evgeniy Slobodkin, PR [17071](https://github.com/python/mypy/pull/17071)) - Improve `yield from` inference for unions of generators (Shantanu, PR [16717](https://github.com/python/mypy/pull/16717)) -- Support `TypeAliasType` in a class scope (Ali Hamdan, PR [17038](https://github.com/python/mypy/pull/17038)) - Fix emulating hash method logic in `attrs` classes (Hashem, PR [17016](https://github.com/python/mypy/pull/17016)) - Add reverted typeshed commit that uses `ParamSpec` for `functools.wraps` (Tamir Duberstein, PR [16942](https://github.com/python/mypy/pull/16942)) - Fix type narrowing for `types.EllipsisType` (Shantanu, PR [17003](https://github.com/python/mypy/pull/17003)) From 82ebd866830cd79e25bf9d59e9f9474bd280c4f5 Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:17:59 +0200 Subject: [PATCH 0569/1617] docs: remove six from the intersphinx mappings (#17165) 002f77cedc9a5c772ebcfb0c5d245a98044c8b21 removed the last reference to six, so there is no need to pull down the six docs inventory --- docs/source/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 683b2a6785b3..fa76734054ac 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -266,7 +266,6 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), - "six": ("https://six.readthedocs.io", None), "attrs": ("https://www.attrs.org/en/stable/", None), "cython": ("https://docs.cython.org/en/latest", None), "monkeytype": ("https://monkeytype.readthedocs.io/en/latest", None), From 6ebce43143c898db3de83f31b2b9e5f34e3000fa Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Fri, 26 Apr 2024 23:02:17 +0900 Subject: [PATCH 0570/1617] docs: Use lower-case generics (#17176) Use lower-case `list`, `tuple`, `type` instead of `List`, `Tuple`, `Type` in documentation. --- docs/source/common_issues.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 4a1d1b437153..cfe82e19e77b 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -41,7 +41,7 @@ once you add annotations: def foo(a: str) -> str: return '(' + a.split() + ')' - # error: Unsupported operand types for + ("str" and List[str]) + # error: Unsupported operand types for + ("str" and "list[str]") If you don't know what types to add, you can use ``Any``, but beware: @@ -226,7 +226,7 @@ dict to a new variable, as mentioned earlier: .. code-block:: python - a: List[int] = [] + a: list[int] = [] Without the annotation mypy can't always figure out the precise type of ``a``. @@ -238,7 +238,7 @@ modification operation in the same scope (such as ``append`` for a list): .. code-block:: python - a = [] # Okay because followed by append, inferred type List[int] + a = [] # Okay because followed by append, inferred type list[int] for i in range(n): a.append(i * i) @@ -276,7 +276,7 @@ not support ``sort()``) as a list and sort it in-place: def f(x: Sequence[int]) -> None: # Type of x is Sequence[int] here; we don't know the concrete type. x = list(x) - # Type of x is List[int] here. + # Type of x is list[int] here. x.sort() # Okay! See :ref:`type-narrowing` for more information. @@ -296,8 +296,8 @@ unexpected errors when combined with type inference. For example: class A: ... class B(A): ... - lst = [A(), A()] # Inferred type is List[A] - new_lst = [B(), B()] # inferred type is List[B] + lst = [A(), A()] # Inferred type is list[A] + new_lst = [B(), B()] # inferred type is list[B] lst = new_lst # mypy will complain about this, because List is invariant Possible strategies in such situations are: @@ -306,7 +306,7 @@ Possible strategies in such situations are: .. code-block:: python - new_lst: List[A] = [B(), B()] + new_lst: list[A] = [B(), B()] lst = new_lst # OK * Make a copy of the right hand side: @@ -319,7 +319,7 @@ Possible strategies in such situations are: .. code-block:: python - def f_bad(x: List[A]) -> A: + def f_bad(x: list[A]) -> A: return x[0] f_bad(new_lst) # Fails @@ -489,7 +489,7 @@ understand how mypy handles a particular piece of code. Example: .. code-block:: python - reveal_type((1, 'hello')) # Revealed type is "Tuple[builtins.int, builtins.str]" + reveal_type((1, 'hello')) # Revealed type is "tuple[builtins.int, builtins.str]" You can also use ``reveal_locals()`` at any line in a file to see the types of all local variables at once. Example: @@ -622,16 +622,16 @@ instructions at the `mypyc wheels repo 0.5: tp = A else: From a1900c2c94aebc0a23a1238fde078d5c4020c954 Mon Sep 17 00:00:00 2001 From: dexterkennedy <104945997+dexterkennedy@users.noreply.github.com> Date: Sat, 27 Apr 2024 05:35:55 -0400 Subject: [PATCH 0571/1617] Log full path to config file in verbose output (#17180) Contributes to #6544 --- mypy/build.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index 65a06211c87e..84c85e66bd49 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2850,10 +2850,14 @@ def skipping_ancestor(manager: BuildManager, id: str, path: str, ancestor_for: S def log_configuration(manager: BuildManager, sources: list[BuildSource]) -> None: """Output useful configuration information to LOG and TRACE""" + config_file = manager.options.config_file + if config_file: + config_file = os.path.abspath(config_file) + manager.log() configuration_vars = [ ("Mypy Version", __version__), - ("Config File", (manager.options.config_file or "Default")), + ("Config File", (config_file or "Default")), ("Configured Executable", manager.options.python_executable or "None"), ("Current Executable", sys.executable), ("Cache Dir", manager.options.cache_dir), From 8bc79660734aa06572f51ba71da97f1b38f9efbf Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 28 Apr 2024 00:06:15 +0300 Subject: [PATCH 0572/1617] Pin MacOS version in GH actions (#17183) Fix failing MacOS tests in CI Python 3.9 is not available on the latest MacOS images https://github.com/actions/setup-python/issues/850 --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4e44c671287..4593e79e728c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,8 @@ jobs: - name: mypyc runtime tests with py39-macos python: '3.9.18' arch: x64 - os: macos-latest + # TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version + os: macos-13 toxenv: py tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" - name: mypyc runtime tests with py38-debug-build-ubuntu From ba6febc903776491ea445cef2ef5375b95e178cd Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 28 Apr 2024 00:46:32 +0300 Subject: [PATCH 0573/1617] Enum private attributes are not enum members (#17182) Fixes #17098 --- mypy/checkmember.py | 4 ++-- mypy/semanal.py | 7 ++++++- mypy/typeanal.py | 7 ++++++- mypy/typeops.py | 3 +++ test-data/unit/check-enum.test | 33 +++++++++++++++++++++++++++++++ test-data/unit/check-literal.test | 4 +++- 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 64d6733f5309..5824b00a37f6 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1139,8 +1139,8 @@ def analyze_enum_class_attribute_access( # Skip these since Enum will remove it if name in ENUM_REMOVED_PROPS: 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("_", "") != "": + # Dunders and private names are not Enum members + if name.startswith("__") and name.replace("_", "") != "": return None enum_literal = LiteralType(name, fallback=itype) diff --git a/mypy/semanal.py b/mypy/semanal.py index 1fc58a6c11f1..91a6b1808987 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3979,7 +3979,12 @@ def analyze_name_lvalue( existing = names.get(name) outer = self.is_global_or_nonlocal(name) - if kind == MDEF and isinstance(self.type, TypeInfo) and self.type.is_enum: + if ( + kind == MDEF + and isinstance(self.type, TypeInfo) + and self.type.is_enum + and not name.startswith("__") + ): # Special case: we need to be sure that `Enum` keys are unique. if existing is not None and not isinstance(existing.node, PlaceholderNode): self.fail( diff --git a/mypy/typeanal.py b/mypy/typeanal.py index c2c578045297..5cde7da721ec 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -868,7 +868,12 @@ def analyze_unbound_type_without_type_info( # If, in the distant future, we decide to permit things like # `def foo(x: Color.RED) -> None: ...`, we can remove that # check entirely. - if isinstance(sym.node, Var) and sym.node.info and sym.node.info.is_enum: + if ( + isinstance(sym.node, Var) + and sym.node.info + and sym.node.info.is_enum + and not sym.node.name.startswith("__") + ): value = sym.node.name base_enum_short_name = sym.node.info.name if not defining_literal: diff --git a/mypy/typeops.py b/mypy/typeops.py index 5b396308d955..a59bd3739562 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -885,6 +885,9 @@ class Status(Enum): # Skip these since Enum will remove it if name in ENUM_REMOVED_PROPS: continue + # Skip private attributes + if name.startswith("__"): + continue new_items.append(LiteralType(name, typ)) return make_simplified_union(new_items, contract_literals=False) elif typ.type.fullname == "builtins.bool": diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index b4e8795859c3..e8e65f464eaf 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1425,6 +1425,10 @@ from enum import Enum class Correct(Enum): x = 'y' y = 'x' +class Correct2(Enum): + x = 'y' + __z = 'y' + __z = 'x' class Foo(Enum): A = 1 A = 'a' # E: Attempted to reuse member name "A" in Enum definition "Foo" \ @@ -2105,3 +2109,32 @@ class AllPartialList(Enum): def check(self) -> None: reveal_type(self.value) # N: Revealed type is "builtins.list[Any]" + +[case testEnumPrivateAttributeNotMember] +from enum import Enum + +class MyEnum(Enum): + A = 1 + B = 2 + __my_dict = {A: "ham", B: "spam"} + +# TODO: change the next line to use MyEnum._MyEnum__my_dict when mypy implements name mangling +x: MyEnum = MyEnum.__my_dict # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum") + +[case testEnumWithPrivateAttributeReachability] +# flags: --warn-unreachable +from enum import Enum + +class MyEnum(Enum): + A = 1 + B = 2 + __my_dict = {A: "ham", B: "spam"} + +e: MyEnum +if e == MyEnum.A: + reveal_type(e) # N: Revealed type is "Literal[__main__.MyEnum.A]" +elif e == MyEnum.B: + reveal_type(e) # N: Revealed type is "Literal[__main__.MyEnum.B]" +else: + reveal_type(e) # E: Statement is unreachable +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 3cf6e8ff17e9..423ba74eba72 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2503,7 +2503,7 @@ class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 - + __ROUGE = RED def func(self) -> int: pass r: Literal[Color.RED] @@ -2512,6 +2512,8 @@ b: Literal[Color.BLUE] bad1: Literal[Color] # E: Parameter 1 of Literal[...] is invalid bad2: Literal[Color.func] # E: Parameter 1 of Literal[...] is invalid bad3: Literal[Color.func()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions +# TODO: change the next line to use Color._Color__ROUGE when mypy implements name mangling +bad4: Literal[Color.__ROUGE] # E: Parameter 1 of Literal[...] is invalid def expects_color(x: Color) -> None: pass def expects_red(x: Literal[Color.RED]) -> None: pass From cd895ce3d356acba5c88c67c853d3671768f0c8d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:13:42 -0700 Subject: [PATCH 0574/1617] Sync typeshed (#17201) Source commit: https://github.com/python/typeshed/commit/f244be921e4a3dfb8f7d84ae404e1515815df2ce --- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 22 ++- mypy/typeshed/stdlib/asyncio/constants.pyi | 6 +- mypy/typeshed/stdlib/asyncio/coroutines.pyi | 4 +- mypy/typeshed/stdlib/asyncio/futures.pyi | 4 +- mypy/typeshed/stdlib/asyncio/locks.pyi | 8 +- mypy/typeshed/stdlib/asyncio/sslproto.pyi | 18 +- mypy/typeshed/stdlib/audioop.pyi | 54 +++--- mypy/typeshed/stdlib/builtins.pyi | 21 ++- mypy/typeshed/stdlib/cgi.pyi | 4 +- mypy/typeshed/stdlib/collections/__init__.pyi | 31 +++- mypy/typeshed/stdlib/contextlib.pyi | 49 +++--- mypy/typeshed/stdlib/email/message.pyi | 53 +++--- mypy/typeshed/stdlib/email/parser.pyi | 40 +++-- mypy/typeshed/stdlib/http/__init__.pyi | 136 +++++++-------- mypy/typeshed/stdlib/http/client.pyi | 28 +-- mypy/typeshed/stdlib/inspect.pyi | 86 +++++----- mypy/typeshed/stdlib/logging/__init__.pyi | 2 +- .../stdlib/multiprocessing/context.pyi | 13 +- .../stdlib/multiprocessing/synchronize.pyi | 4 +- mypy/typeshed/stdlib/os/__init__.pyi | 4 +- mypy/typeshed/stdlib/plistlib.pyi | 4 +- mypy/typeshed/stdlib/pstats.pyi | 18 +- mypy/typeshed/stdlib/py_compile.pyi | 6 +- mypy/typeshed/stdlib/signal.pyi | 92 +++++----- mypy/typeshed/stdlib/socket.pyi | 150 ++++++++-------- mypy/typeshed/stdlib/ssl.pyi | 162 +++++++++--------- mypy/typeshed/stdlib/tkinter/__init__.pyi | 76 ++++---- mypy/typeshed/stdlib/typing.pyi | 36 ++-- mypy/typeshed/stdlib/unittest/mock.pyi | 4 +- mypy/typeshed/stdlib/uuid.pyi | 6 +- mypy/typeshed/stdlib/weakref.pyi | 11 +- 31 files changed, 608 insertions(+), 544 deletions(-) diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 9469081ae5d6..6937d97b87ea 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -47,10 +47,15 @@ AnyStr_co = TypeVar("AnyStr_co", str, bytes, covariant=True) # noqa: Y001 # isn't possible or a type is already partially known. In cases like these, # use Incomplete instead of Any as a marker. For example, use # "Incomplete | None" instead of "Any | None". -Incomplete: TypeAlias = Any +Incomplete: TypeAlias = Any # stable # To describe a function parameter that is unused and will work with anything. -Unused: TypeAlias = object +Unused: TypeAlias = object # stable + +# Marker for return types that include None, but where forcing the user to +# check for None can be detrimental. Sometimes called "the Any trick". See +# CONTRIBUTING.md for more information. +MaybeNone: TypeAlias = Any # stable # Used to mark arguments that default to a sentinel value. This prevents # stubtest from complaining about the default value not matching. @@ -146,13 +151,22 @@ class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): def keys(self) -> Iterable[_KT]: ... def __getitem__(self, key: _KT, /) -> _VT_co: ... -# stable +# This protocol is currently under discussion. Use SupportsContainsAndGetItem +# instead, if you require the __contains__ method. +# See https://github.com/python/typeshed/issues/11822. class SupportsGetItem(Protocol[_KT_contra, _VT_co]): def __contains__(self, x: Any, /) -> bool: ... def __getitem__(self, key: _KT_contra, /) -> _VT_co: ... # stable -class SupportsItemAccess(SupportsGetItem[_KT_contra, _VT], Protocol[_KT_contra, _VT]): +class SupportsContainsAndGetItem(Protocol[_KT_contra, _VT_co]): + def __contains__(self, x: Any, /) -> bool: ... + def __getitem__(self, key: _KT_contra, /) -> _VT_co: ... + +# stable +class SupportsItemAccess(Protocol[_KT_contra, _VT]): + def __contains__(self, x: Any, /) -> bool: ... + def __getitem__(self, key: _KT_contra, /) -> _VT: ... def __setitem__(self, key: _KT_contra, value: _VT, /) -> None: ... def __delitem__(self, key: _KT_contra, /) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/constants.pyi b/mypy/typeshed/stdlib/asyncio/constants.pyi index 559cc02a0faa..7759a2844953 100644 --- a/mypy/typeshed/stdlib/asyncio/constants.pyi +++ b/mypy/typeshed/stdlib/asyncio/constants.pyi @@ -15,6 +15,6 @@ if sys.version_info >= (3, 12): THREAD_JOIN_TIMEOUT: Literal[300] class _SendfileMode(enum.Enum): - UNSUPPORTED: int - TRY_NATIVE: int - FALLBACK: int + UNSUPPORTED = 1 + TRY_NATIVE = 2 + FALLBACK = 3 diff --git a/mypy/typeshed/stdlib/asyncio/coroutines.pyi b/mypy/typeshed/stdlib/asyncio/coroutines.pyi index e92b150875f6..bc797de7fd51 100644 --- a/mypy/typeshed/stdlib/asyncio/coroutines.pyi +++ b/mypy/typeshed/stdlib/asyncio/coroutines.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Awaitable, Callable, Coroutine from typing import Any, TypeVar, overload -from typing_extensions import ParamSpec, TypeGuard +from typing_extensions import ParamSpec, TypeGuard, TypeIs if sys.version_info >= (3, 11): __all__ = ("iscoroutinefunction", "iscoroutine") @@ -23,4 +23,4 @@ def iscoroutinefunction(func: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable def iscoroutinefunction(func: Callable[_P, object]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... @overload def iscoroutinefunction(func: object) -> TypeGuard[Callable[..., Coroutine[Any, Any, Any]]]: ... -def iscoroutine(obj: object) -> TypeGuard[Coroutine[Any, Any, Any]]: ... +def iscoroutine(obj: object) -> TypeIs[Coroutine[Any, Any, Any]]: ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index 560dcc1d5712..a3953cdaf8c7 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -3,7 +3,7 @@ from collections.abc import Awaitable, Callable, Generator, Iterable from concurrent.futures._base import Future as _ConcurrentFuture from contextvars import Context from typing import Any, Literal, TypeVar -from typing_extensions import Self, TypeGuard +from typing_extensions import Self, TypeIs from .events import AbstractEventLoop @@ -17,7 +17,7 @@ _T = TypeVar("_T") # asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py # but it leads to circular import error in pytype tool. # That's why the import order is reversed. -def isfuture(obj: object) -> TypeGuard[Future[Any]]: ... +def isfuture(obj: object) -> TypeIs[Future[Any]]: ... class Future(Awaitable[_T], Iterable[_T]): _state: str diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index 3aac34b6934f..0114aeb23329 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -101,10 +101,10 @@ class BoundedSemaphore(Semaphore): ... if sys.version_info >= (3, 11): class _BarrierState(enum.Enum): # undocumented - FILLING: str - DRAINING: str - RESETTING: str - BROKEN: str + FILLING = "filling" + DRAINING = "draining" + RESETTING = "resetting" + BROKEN = "broken" class Barrier(_LoopBoundMixin): def __init__(self, parties: int) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index 04197c8d2978..e904d7395cdc 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -14,17 +14,17 @@ if sys.version_info >= (3, 11): SSLAgainErrors: tuple[type[ssl.SSLWantReadError], type[ssl.SSLSyscallError]] class SSLProtocolState(Enum): - UNWRAPPED: str - DO_HANDSHAKE: str - WRAPPED: str - FLUSHING: str - SHUTDOWN: str + UNWRAPPED = "UNWRAPPED" + DO_HANDSHAKE = "DO_HANDSHAKE" + WRAPPED = "WRAPPED" + FLUSHING = "FLUSHING" + SHUTDOWN = "SHUTDOWN" class AppProtocolState(Enum): - STATE_INIT: str - STATE_CON_MADE: str - STATE_EOF: str - STATE_CON_LOST: str + STATE_INIT = "STATE_INIT" + STATE_CON_MADE = "STATE_CON_MADE" + STATE_EOF = "STATE_EOF" + STATE_CON_LOST = "STATE_CON_LOST" def add_flowcontrol_defaults(high: int | None, low: int | None, kb: int) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/audioop.pyi b/mypy/typeshed/stdlib/audioop.pyi index 830d6f83a273..f3ce78ccb7fa 100644 --- a/mypy/typeshed/stdlib/audioop.pyi +++ b/mypy/typeshed/stdlib/audioop.pyi @@ -1,32 +1,32 @@ -from typing_extensions import TypeAlias +from typing_extensions import Buffer, TypeAlias _AdpcmState: TypeAlias = tuple[int, int] _RatecvState: TypeAlias = tuple[int, tuple[tuple[int, int], ...]] class error(Exception): ... -def add(fragment1: bytes, fragment2: bytes, width: int, /) -> bytes: ... -def adpcm2lin(fragment: bytes, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... -def alaw2lin(fragment: bytes, width: int, /) -> bytes: ... -def avg(fragment: bytes, width: int, /) -> int: ... -def avgpp(fragment: bytes, width: int, /) -> int: ... -def bias(fragment: bytes, width: int, bias: int, /) -> bytes: ... -def byteswap(fragment: bytes, width: int, /) -> bytes: ... -def cross(fragment: bytes, width: int, /) -> int: ... -def findfactor(fragment: bytes, reference: bytes, /) -> float: ... -def findfit(fragment: bytes, reference: bytes, /) -> tuple[int, float]: ... -def findmax(fragment: bytes, length: int, /) -> int: ... -def getsample(fragment: bytes, width: int, index: int, /) -> int: ... -def lin2adpcm(fragment: bytes, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... -def lin2alaw(fragment: bytes, width: int, /) -> bytes: ... -def lin2lin(fragment: bytes, width: int, newwidth: int, /) -> bytes: ... -def lin2ulaw(fragment: bytes, width: int, /) -> bytes: ... -def max(fragment: bytes, width: int, /) -> int: ... -def maxpp(fragment: bytes, width: int, /) -> int: ... -def minmax(fragment: bytes, width: int, /) -> tuple[int, int]: ... -def mul(fragment: bytes, width: int, factor: float, /) -> bytes: ... +def add(fragment1: Buffer, fragment2: Buffer, width: int, /) -> bytes: ... +def adpcm2lin(fragment: Buffer, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... +def alaw2lin(fragment: Buffer, width: int, /) -> bytes: ... +def avg(fragment: Buffer, width: int, /) -> int: ... +def avgpp(fragment: Buffer, width: int, /) -> int: ... +def bias(fragment: Buffer, width: int, bias: int, /) -> bytes: ... +def byteswap(fragment: Buffer, width: int, /) -> bytes: ... +def cross(fragment: Buffer, width: int, /) -> int: ... +def findfactor(fragment: Buffer, reference: Buffer, /) -> float: ... +def findfit(fragment: Buffer, reference: Buffer, /) -> tuple[int, float]: ... +def findmax(fragment: Buffer, length: int, /) -> int: ... +def getsample(fragment: Buffer, width: int, index: int, /) -> int: ... +def lin2adpcm(fragment: Buffer, width: int, state: _AdpcmState | None, /) -> tuple[bytes, _AdpcmState]: ... +def lin2alaw(fragment: Buffer, width: int, /) -> bytes: ... +def lin2lin(fragment: Buffer, width: int, newwidth: int, /) -> bytes: ... +def lin2ulaw(fragment: Buffer, width: int, /) -> bytes: ... +def max(fragment: Buffer, width: int, /) -> int: ... +def maxpp(fragment: Buffer, width: int, /) -> int: ... +def minmax(fragment: Buffer, width: int, /) -> tuple[int, int]: ... +def mul(fragment: Buffer, width: int, factor: float, /) -> bytes: ... def ratecv( - fragment: bytes, + fragment: Buffer, width: int, nchannels: int, inrate: int, @@ -36,8 +36,8 @@ def ratecv( weightB: int = 0, /, ) -> tuple[bytes, _RatecvState]: ... -def reverse(fragment: bytes, width: int, /) -> bytes: ... -def rms(fragment: bytes, width: int, /) -> int: ... -def tomono(fragment: bytes, width: int, lfactor: float, rfactor: float, /) -> bytes: ... -def tostereo(fragment: bytes, width: int, lfactor: float, rfactor: float, /) -> bytes: ... -def ulaw2lin(fragment: bytes, width: int, /) -> bytes: ... +def reverse(fragment: Buffer, width: int, /) -> bytes: ... +def rms(fragment: Buffer, width: int, /) -> int: ... +def tomono(fragment: Buffer, width: int, lfactor: float, rfactor: float, /) -> bytes: ... +def tostereo(fragment: Buffer, width: int, lfactor: float, rfactor: float, /) -> bytes: ... +def ulaw2lin(fragment: Buffer, width: int, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 47dddcadf36d..9e56c5430c52 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -65,6 +65,7 @@ from typing_extensions import ( # noqa: Y023 Self, TypeAlias, TypeGuard, + TypeIs, TypeVarTuple, deprecated, ) @@ -943,15 +944,25 @@ class dict(MutableMapping[_KT, _VT]): @overload def __init__(self) -> None: ... @overload - def __init__(self: dict[str, _VT], **kwargs: _VT) -> None: ... + def __init__(self: dict[str, _VT], **kwargs: _VT) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 @overload def __init__(self, map: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload - def __init__(self: dict[str, _VT], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ... + def __init__( + self: dict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + map: SupportsKeysAndGetItem[str, _VT], + /, + **kwargs: _VT, + ) -> None: ... @overload def __init__(self, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload - def __init__(self: dict[str, _VT], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... + def __init__( + self: dict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + iterable: Iterable[tuple[str, _VT]], + /, + **kwargs: _VT, + ) -> None: ... # Next two overloads are for dict(string.split(sep) for string in iterable) # Cannot be Iterable[Sequence[_T]] or otherwise dict(["foo", "bar", "baz"]) is not an error @overload @@ -1143,7 +1154,7 @@ def any(iterable: Iterable[object], /) -> bool: ... def ascii(obj: object, /) -> str: ... def bin(number: int | SupportsIndex, /) -> str: ... def breakpoint(*args: Any, **kws: Any) -> None: ... -def callable(obj: object, /) -> TypeGuard[Callable[..., object]]: ... +def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ... def chr(i: int, /) -> str: ... # We define this here instead of using os.PathLike to avoid import cycle issues. @@ -1253,6 +1264,8 @@ class filter(Iterator[_T]): @overload def __new__(cls, function: Callable[[_S], TypeGuard[_T]], iterable: Iterable[_S], /) -> Self: ... @overload + def __new__(cls, function: Callable[[_S], TypeIs[_T]], iterable: Iterable[_S], /) -> Self: ... + @overload def __new__(cls, function: Callable[[_T], Any], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index d20be33e3d76..3a2e2a91b241 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -1,4 +1,4 @@ -from _typeshed import SupportsGetItem, SupportsItemAccess, Unused +from _typeshed import SupportsContainsAndGetItem, SupportsGetItem, SupportsItemAccess, Unused from builtins import list as _list, type as _type from collections.abc import Iterable, Iterator, Mapping from email.message import Message @@ -85,7 +85,7 @@ class FieldStorage: fp: IO[Any] | None = None, headers: Mapping[str, str] | Message | None = None, outerboundary: bytes = b"", - environ: SupportsGetItem[str, str] = ..., + environ: SupportsContainsAndGetItem[str, str] = ..., keep_blank_values: int = 0, strict_parsing: int = 0, limit: int | None = None, diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 1d23ecd66a8d..71e3c564dd57 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -51,15 +51,27 @@ class UserDict(MutableMapping[_KT, _VT]): @overload def __init__(self, dict: None = None, /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], dict: None = None, /, **kwargs: _VT) -> None: ... + def __init__( + self: UserDict[str, _VT], dict: None = None, /, **kwargs: _VT # pyright: ignore[reportInvalidTypeVarUse] #11780 + ) -> None: ... @overload def __init__(self, dict: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], dict: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ... + def __init__( + self: UserDict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + dict: SupportsKeysAndGetItem[str, _VT], + /, + **kwargs: _VT, + ) -> None: ... @overload def __init__(self, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload - def __init__(self: UserDict[str, _VT], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... + def __init__( + self: UserDict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + iterable: Iterable[tuple[str, _VT]], + /, + **kwargs: _VT, + ) -> None: ... @overload def __init__(self: UserDict[str, str], iterable: Iterable[list[str]], /) -> None: ... @overload @@ -389,16 +401,21 @@ class defaultdict(dict[_KT, _VT]): @overload def __init__(self) -> None: ... @overload - def __init__(self: defaultdict[str, _VT], **kwargs: _VT) -> None: ... + def __init__(self: defaultdict[str, _VT], **kwargs: _VT) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 @overload def __init__(self, default_factory: Callable[[], _VT] | None, /) -> None: ... @overload - def __init__(self: defaultdict[str, _VT], default_factory: Callable[[], _VT] | None, /, **kwargs: _VT) -> None: ... + def __init__( + self: defaultdict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + default_factory: Callable[[], _VT] | None, + /, + **kwargs: _VT, + ) -> None: ... @overload def __init__(self, default_factory: Callable[[], _VT] | None, map: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... @overload def __init__( - self: defaultdict[str, _VT], + self: defaultdict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 default_factory: Callable[[], _VT] | None, map: SupportsKeysAndGetItem[str, _VT], /, @@ -408,7 +425,7 @@ class defaultdict(dict[_KT, _VT]): def __init__(self, default_factory: Callable[[], _VT] | None, iterable: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload def __init__( - self: defaultdict[str, _VT], + self: defaultdict[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 default_factory: Callable[[], _VT] | None, iterable: Iterable[tuple[str, _VT]], /, diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index f82bb4b7b6ad..29ac7cde561a 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -31,32 +31,33 @@ if sys.version_info >= (3, 11): _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _T_io = TypeVar("_T_io", bound=IO[str] | None) +_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound=bool | None, default=bool | None) _F = TypeVar("_F", bound=Callable[..., Any]) _P = ParamSpec("_P") _ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] -_CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any] | _ExitFunc) +_CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) @runtime_checkable -class AbstractContextManager(Protocol[_T_co]): +class AbstractContextManager(Protocol[_T_co, _ExitT_co]): def __enter__(self) -> _T_co: ... @abstractmethod def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / - ) -> bool | None: ... + ) -> _ExitT_co: ... @runtime_checkable -class AbstractAsyncContextManager(Protocol[_T_co]): +class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]): async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / - ) -> bool | None: ... + ) -> _ExitT_co: ... class ContextDecorator: def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManager(AbstractContextManager[_T_co], ContextDecorator): +class _GeneratorContextManager(AbstractContextManager[_T_co, bool | None], ContextDecorator): # __init__ and all instance attributes are actually inherited from _GeneratorContextManagerBase # _GeneratorContextManagerBase is more trouble than it's worth to include in the stub; see #6676 def __init__(self, func: Callable[..., Iterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... @@ -81,7 +82,7 @@ if sys.version_info >= (3, 10): class AsyncContextDecorator: def __call__(self, func: _AF) -> _AF: ... - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co], AsyncContextDecorator): + class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator): # __init__ and these attributes are actually defined in the base class _GeneratorContextManagerBase, # which is more trouble than it's worth to include in the stub (see #6676) def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... @@ -94,7 +95,7 @@ if sys.version_info >= (3, 10): ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co]): + class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None]): def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] @@ -111,7 +112,7 @@ class _SupportsClose(Protocol): _SupportsCloseT = TypeVar("_SupportsCloseT", bound=_SupportsClose) -class closing(AbstractContextManager[_SupportsCloseT]): +class closing(AbstractContextManager[_SupportsCloseT, None]): def __init__(self, thing: _SupportsCloseT) -> None: ... def __exit__(self, *exc_info: Unused) -> None: ... @@ -121,17 +122,17 @@ if sys.version_info >= (3, 10): _SupportsAcloseT = TypeVar("_SupportsAcloseT", bound=_SupportsAclose) - class aclosing(AbstractAsyncContextManager[_SupportsAcloseT]): + class aclosing(AbstractAsyncContextManager[_SupportsAcloseT, None]): def __init__(self, thing: _SupportsAcloseT) -> None: ... async def __aexit__(self, *exc_info: Unused) -> None: ... -class suppress(AbstractContextManager[None]): +class suppress(AbstractContextManager[None, bool]): def __init__(self, *exceptions: type[BaseException]) -> None: ... def __exit__( self, exctype: type[BaseException] | None, excinst: BaseException | None, exctb: TracebackType | None ) -> bool: ... -class _RedirectStream(AbstractContextManager[_T_io]): +class _RedirectStream(AbstractContextManager[_T_io, None]): def __init__(self, new_target: _T_io) -> None: ... def __exit__( self, exctype: type[BaseException] | None, excinst: BaseException | None, exctb: TracebackType | None @@ -142,8 +143,8 @@ class redirect_stderr(_RedirectStream[_T_io]): ... # In reality this is a subclass of `AbstractContextManager`; # see #7961 for why we don't do that in the stub -class ExitStack(metaclass=abc.ABCMeta): - def enter_context(self, cm: AbstractContextManager[_T]) -> _T: ... +class ExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): + def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def pop_all(self) -> Self: ... @@ -151,18 +152,18 @@ class ExitStack(metaclass=abc.ABCMeta): def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / - ) -> bool: ... + ) -> _ExitT_co: ... _ExitCoroFunc: TypeAlias = Callable[ [type[BaseException] | None, BaseException | None, TracebackType | None], Awaitable[bool | None] ] -_ACM_EF = TypeVar("_ACM_EF", bound=AbstractAsyncContextManager[Any] | _ExitCoroFunc) +_ACM_EF = TypeVar("_ACM_EF", bound=AbstractAsyncContextManager[Any, Any] | _ExitCoroFunc) # In reality this is a subclass of `AbstractAsyncContextManager`; # see #7961 for why we don't do that in the stub -class AsyncExitStack(metaclass=abc.ABCMeta): - def enter_context(self, cm: AbstractContextManager[_T]) -> _T: ... - async def enter_async_context(self, cm: AbstractAsyncContextManager[_T]) -> _T: ... +class AsyncExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): + def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... + async def enter_async_context(self, cm: AbstractAsyncContextManager[_T, _ExitT_co]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... def push_async_exit(self, exit: _ACM_EF) -> _ACM_EF: ... def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... @@ -177,31 +178,31 @@ class AsyncExitStack(metaclass=abc.ABCMeta): ) -> bool: ... if sys.version_info >= (3, 10): - class nullcontext(AbstractContextManager[_T], AbstractAsyncContextManager[_T]): + class nullcontext(AbstractContextManager[_T, None], AbstractAsyncContextManager[_T, None]): enter_result: _T @overload def __init__(self: nullcontext[None], enter_result: None = None) -> None: ... @overload - def __init__(self: nullcontext[_T], enter_result: _T) -> None: ... + def __init__(self: nullcontext[_T], enter_result: _T) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 def __enter__(self) -> _T: ... def __exit__(self, *exctype: Unused) -> None: ... async def __aenter__(self) -> _T: ... async def __aexit__(self, *exctype: Unused) -> None: ... else: - class nullcontext(AbstractContextManager[_T]): + class nullcontext(AbstractContextManager[_T, None]): enter_result: _T @overload def __init__(self: nullcontext[None], enter_result: None = None) -> None: ... @overload - def __init__(self: nullcontext[_T], enter_result: _T) -> None: ... + def __init__(self: nullcontext[_T], enter_result: _T) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 def __enter__(self) -> _T: ... def __exit__(self, *exctype: Unused) -> None: ... if sys.version_info >= (3, 11): _T_fd_or_any_path = TypeVar("_T_fd_or_any_path", bound=FileDescriptorOrPath) - class chdir(AbstractContextManager[None], Generic[_T_fd_or_any_path]): + class chdir(AbstractContextManager[None, None], Generic[_T_fd_or_any_path]): path: _T_fd_or_any_path def __init__(self, path: _T_fd_or_any_path) -> None: ... def __enter__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index d7d7e8c8e908..4032bc6136d4 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -3,22 +3,25 @@ from email import _ParamsType, _ParamType from email.charset import Charset from email.contentmanager import ContentManager from email.errors import MessageDefect -from email.header import Header from email.policy import Policy -from typing import Any, Literal, Protocol, TypeVar, overload +from typing import Any, Generic, Literal, Protocol, TypeVar, overload from typing_extensions import Self, TypeAlias __all__ = ["Message", "EmailMessage"] _T = TypeVar("_T") +# Type returned by Policy.header_fetch_parse, often str or Header. +_HeaderT = TypeVar("_HeaderT", default=str) +_HeaderParamT = TypeVar("_HeaderParamT", default=str) +# Represents headers constructed by HeaderRegistry. Those are sub-classes +# of BaseHeader and another header type. +_HeaderRegistryT = TypeVar("_HeaderRegistryT", default=Any) +_HeaderRegistryParamT = TypeVar("_HeaderRegistryParamT", default=Any) + _PayloadType: TypeAlias = Message | str _EncodedPayloadType: TypeAlias = Message | bytes _MultipartPayloadType: TypeAlias = list[_PayloadType] _CharsetType: TypeAlias = Charset | str | None -# Type returned by Policy.header_fetch_parse, often str or Header. -_HeaderType: TypeAlias = Any -# Type accepted by Policy.header_store_parse. -_HeaderTypeParam: TypeAlias = str | Header | Any class _SupportsEncodeToPayload(Protocol): def encode(self, encoding: str, /) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ... @@ -26,10 +29,7 @@ class _SupportsEncodeToPayload(Protocol): class _SupportsDecodeToPayload(Protocol): def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ... -# TODO: This class should be generic over the header policy and/or the header -# value types allowed by the policy. This depends on PEP 696 support -# (https://github.com/python/typeshed/issues/11422). -class Message: +class Message(Generic[_HeaderT, _HeaderParamT]): policy: Policy # undocumented preamble: str | None epilogue: str | None @@ -70,24 +70,23 @@ class Message: # Same as `get` with `failobj=None`, but with the expectation that it won't return None in most scenarios # This is important for protocols using __getitem__, like SupportsKeysAndGetItem # Morally, the return type should be `AnyOf[_HeaderType, None]`, - # which we could spell as `_HeaderType | Any`, - # *but* `_HeaderType` itself is currently an alias to `Any`... - def __getitem__(self, name: str) -> _HeaderType: ... - def __setitem__(self, name: str, val: _HeaderTypeParam) -> None: ... + # so using "the Any trick" instead. + def __getitem__(self, name: str) -> _HeaderT | Any: ... + def __setitem__(self, name: str, val: _HeaderParamT) -> None: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> list[str]: ... - def values(self) -> list[_HeaderType]: ... - def items(self) -> list[tuple[str, _HeaderType]]: ... + def values(self) -> list[_HeaderT]: ... + def items(self) -> list[tuple[str, _HeaderT]]: ... @overload - def get(self, name: str, failobj: None = None) -> _HeaderType | None: ... + def get(self, name: str, failobj: None = None) -> _HeaderT | None: ... @overload - def get(self, name: str, failobj: _T) -> _HeaderType | _T: ... + def get(self, name: str, failobj: _T) -> _HeaderT | _T: ... @overload - def get_all(self, name: str, failobj: None = None) -> list[_HeaderType] | None: ... + def get_all(self, name: str, failobj: None = None) -> list[_HeaderT] | None: ... @overload - def get_all(self, name: str, failobj: _T) -> list[_HeaderType] | _T: ... + def get_all(self, name: str, failobj: _T) -> list[_HeaderT] | _T: ... def add_header(self, _name: str, _value: str, **_params: _ParamsType) -> None: ... - def replace_header(self, _name: str, _value: _HeaderTypeParam) -> None: ... + def replace_header(self, _name: str, _value: _HeaderParamT) -> None: ... def get_content_type(self) -> str: ... def get_content_maintype(self) -> str: ... def get_content_subtype(self) -> str: ... @@ -141,14 +140,14 @@ class Message: ) -> None: ... def __init__(self, policy: Policy = ...) -> None: ... # The following two methods are undocumented, but a source code comment states that they are public API - def set_raw(self, name: str, value: _HeaderTypeParam) -> None: ... - def raw_items(self) -> Iterator[tuple[str, _HeaderType]]: ... + def set_raw(self, name: str, value: _HeaderParamT) -> None: ... + def raw_items(self) -> Iterator[tuple[str, _HeaderT]]: ... -class MIMEPart(Message): +class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def __init__(self, policy: Policy | None = None) -> None: ... - def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> Message | None: ... - def iter_attachments(self) -> Iterator[Message]: ... - def iter_parts(self) -> Iterator[Message]: ... + def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ... + def iter_attachments(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... + def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ... def set_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> None: ... def make_related(self, boundary: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi index 28b6aca856ca..fecb29d90b2e 100644 --- a/mypy/typeshed/stdlib/email/parser.pyi +++ b/mypy/typeshed/stdlib/email/parser.pyi @@ -3,24 +3,34 @@ from collections.abc import Callable from email.feedparser import BytesFeedParser as BytesFeedParser, FeedParser as FeedParser from email.message import Message from email.policy import Policy -from typing import IO +from io import _WrappedBuffer +from typing import Generic, TypeVar, overload __all__ = ["Parser", "HeaderParser", "BytesParser", "BytesHeaderParser", "FeedParser", "BytesFeedParser"] -class Parser: - def __init__(self, _class: Callable[[], Message] | None = None, *, policy: Policy = ...) -> None: ... - def parse(self, fp: SupportsRead[str], headersonly: bool = False) -> Message: ... - def parsestr(self, text: str, headersonly: bool = False) -> Message: ... +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) -class HeaderParser(Parser): - def parse(self, fp: SupportsRead[str], headersonly: bool = True) -> Message: ... - def parsestr(self, text: str, headersonly: bool = True) -> Message: ... +class Parser(Generic[_MessageT]): + @overload + def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + @overload + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def parse(self, fp: SupportsRead[str], headersonly: bool = False) -> _MessageT: ... + def parsestr(self, text: str, headersonly: bool = False) -> _MessageT: ... -class BytesParser: - def __init__(self, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> None: ... - def parse(self, fp: IO[bytes], headersonly: bool = False) -> Message: ... - def parsebytes(self, text: bytes | bytearray, headersonly: bool = False) -> Message: ... +class HeaderParser(Parser[_MessageT]): + def parse(self, fp: SupportsRead[str], headersonly: bool = True) -> _MessageT: ... + def parsestr(self, text: str, headersonly: bool = True) -> _MessageT: ... -class BytesHeaderParser(BytesParser): - def parse(self, fp: IO[bytes], headersonly: bool = True) -> Message: ... - def parsebytes(self, text: bytes | bytearray, headersonly: bool = True) -> Message: ... +class BytesParser(Generic[_MessageT]): + parser: Parser[_MessageT] + @overload + def __init__(self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + @overload + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def parse(self, fp: _WrappedBuffer, headersonly: bool = False) -> _MessageT: ... + def parsebytes(self, text: bytes | bytearray, headersonly: bool = False) -> _MessageT: ... + +class BytesHeaderParser(BytesParser[_MessageT]): + def parse(self, fp: _WrappedBuffer, headersonly: bool = True) -> _MessageT: ... + def parsebytes(self, text: bytes | bytearray, headersonly: bool = True) -> _MessageT: ... diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index 2eee06fdaaa9..bb5737cc0481 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -15,65 +15,65 @@ class HTTPStatus(IntEnum): def phrase(self) -> str: ... @property def description(self) -> str: ... - CONTINUE: int - SWITCHING_PROTOCOLS: int - PROCESSING: int - OK: int - CREATED: int - ACCEPTED: int - NON_AUTHORITATIVE_INFORMATION: int - NO_CONTENT: int - RESET_CONTENT: int - PARTIAL_CONTENT: int - MULTI_STATUS: int - ALREADY_REPORTED: int - IM_USED: int - MULTIPLE_CHOICES: int - MOVED_PERMANENTLY: int - FOUND: int - SEE_OTHER: int - NOT_MODIFIED: int - USE_PROXY: int - TEMPORARY_REDIRECT: int - PERMANENT_REDIRECT: int - BAD_REQUEST: int - UNAUTHORIZED: int - PAYMENT_REQUIRED: int - FORBIDDEN: int - NOT_FOUND: int - METHOD_NOT_ALLOWED: int - NOT_ACCEPTABLE: int - PROXY_AUTHENTICATION_REQUIRED: int - REQUEST_TIMEOUT: int - CONFLICT: int - GONE: int - LENGTH_REQUIRED: int - PRECONDITION_FAILED: int - REQUEST_ENTITY_TOO_LARGE: int - REQUEST_URI_TOO_LONG: int - UNSUPPORTED_MEDIA_TYPE: int - REQUESTED_RANGE_NOT_SATISFIABLE: int - EXPECTATION_FAILED: int - UNPROCESSABLE_ENTITY: int - LOCKED: int - FAILED_DEPENDENCY: int - UPGRADE_REQUIRED: int - PRECONDITION_REQUIRED: int - TOO_MANY_REQUESTS: int - REQUEST_HEADER_FIELDS_TOO_LARGE: int - INTERNAL_SERVER_ERROR: int - NOT_IMPLEMENTED: int - BAD_GATEWAY: int - SERVICE_UNAVAILABLE: int - GATEWAY_TIMEOUT: int - HTTP_VERSION_NOT_SUPPORTED: int - VARIANT_ALSO_NEGOTIATES: int - INSUFFICIENT_STORAGE: int - LOOP_DETECTED: int - NOT_EXTENDED: int - NETWORK_AUTHENTICATION_REQUIRED: int - MISDIRECTED_REQUEST: int - UNAVAILABLE_FOR_LEGAL_REASONS: int + CONTINUE = 100 + SWITCHING_PROTOCOLS = 101 + PROCESSING = 102 + OK = 200 + CREATED = 201 + ACCEPTED = 202 + NON_AUTHORITATIVE_INFORMATION = 203 + NO_CONTENT = 204 + RESET_CONTENT = 205 + PARTIAL_CONTENT = 206 + MULTI_STATUS = 207 + ALREADY_REPORTED = 208 + IM_USED = 226 + MULTIPLE_CHOICES = 300 + MOVED_PERMANENTLY = 301 + FOUND = 302 + SEE_OTHER = 303 + NOT_MODIFIED = 304 + USE_PROXY = 305 + TEMPORARY_REDIRECT = 307 + PERMANENT_REDIRECT = 308 + BAD_REQUEST = 400 + UNAUTHORIZED = 401 + PAYMENT_REQUIRED = 402 + FORBIDDEN = 403 + NOT_FOUND = 404 + METHOD_NOT_ALLOWED = 405 + NOT_ACCEPTABLE = 406 + PROXY_AUTHENTICATION_REQUIRED = 407 + REQUEST_TIMEOUT = 408 + CONFLICT = 409 + GONE = 410 + LENGTH_REQUIRED = 411 + PRECONDITION_FAILED = 412 + REQUEST_ENTITY_TOO_LARGE = 413 + REQUEST_URI_TOO_LONG = 414 + UNSUPPORTED_MEDIA_TYPE = 415 + REQUESTED_RANGE_NOT_SATISFIABLE = 416 + EXPECTATION_FAILED = 417 + UNPROCESSABLE_ENTITY = 422 + LOCKED = 423 + FAILED_DEPENDENCY = 424 + UPGRADE_REQUIRED = 426 + PRECONDITION_REQUIRED = 428 + TOO_MANY_REQUESTS = 429 + REQUEST_HEADER_FIELDS_TOO_LARGE = 431 + INTERNAL_SERVER_ERROR = 500 + NOT_IMPLEMENTED = 501 + BAD_GATEWAY = 502 + SERVICE_UNAVAILABLE = 503 + GATEWAY_TIMEOUT = 504 + HTTP_VERSION_NOT_SUPPORTED = 505 + VARIANT_ALSO_NEGOTIATES = 506 + INSUFFICIENT_STORAGE = 507 + LOOP_DETECTED = 508 + NOT_EXTENDED = 510 + NETWORK_AUTHENTICATION_REQUIRED = 511 + MISDIRECTED_REQUEST = 421 + UNAVAILABLE_FOR_LEGAL_REASONS = 451 if sys.version_info >= (3, 9): EARLY_HINTS: Literal[103] IM_A_TEAPOT: Literal[418] @@ -94,12 +94,12 @@ if sys.version_info >= (3, 11): class HTTPMethod(StrEnum): @property def description(self) -> str: ... - CONNECT: str - DELETE: str - GET: str - HEAD: str - OPTIONS: str - PATCH: str - POST: str - PUT: str - TRACE: str + CONNECT = "CONNECT" + DELETE = "DELETE" + GET = "GET" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + POST = "POST" + PUT = "PUT" + TRACE = "TRACE" diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index fb5450730f60..f68d9d0ca7d7 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -3,7 +3,7 @@ import io import ssl import sys import types -from _typeshed import ReadableBuffer, SupportsRead, WriteableBuffer +from _typeshed import ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer from collections.abc import Callable, Iterable, Iterator, Mapping from socket import socket from typing import Any, BinaryIO, TypeVar, overload @@ -33,6 +33,7 @@ __all__ = [ _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | ReadableBuffer _T = TypeVar("_T") +_MessageT = TypeVar("_MessageT", bound=email.message.Message) HTTP_PORT: int HTTPS_PORT: int @@ -97,28 +98,13 @@ NETWORK_AUTHENTICATION_REQUIRED: int responses: dict[int, str] -class HTTPMessage(email.message.Message): +class HTTPMessage(email.message.Message[str, str]): def getallmatchingheaders(self, name: str) -> list[str]: ... # undocumented - # override below all of Message's methods that use `_HeaderType` / `_HeaderTypeParam` with `str` - # `HTTPMessage` breaks the Liskov substitution principle by only intending for `str` headers - # This is easier than making `Message` generic - def __getitem__(self, name: str) -> str | None: ... - def __setitem__(self, name: str, val: str) -> None: ... # type: ignore[override] - def values(self) -> list[str]: ... - def items(self) -> list[tuple[str, str]]: ... - @overload - def get(self, name: str, failobj: None = None) -> str | None: ... - @overload - def get(self, name: str, failobj: _T) -> str | _T: ... - @overload - def get_all(self, name: str, failobj: None = None) -> list[str] | None: ... - @overload - def get_all(self, name: str, failobj: _T) -> list[str] | _T: ... - def replace_header(self, _name: str, _value: str) -> None: ... # type: ignore[override] - def set_raw(self, name: str, value: str) -> None: ... # type: ignore[override] - def raw_items(self) -> Iterator[tuple[str, str]]: ... -def parse_headers(fp: io.BufferedIOBase, _class: Callable[[], email.message.Message] = ...) -> HTTPMessage: ... +@overload +def parse_headers(fp: SupportsReadline[bytes], _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def parse_headers(fp: SupportsReadline[bytes]) -> HTTPMessage: ... class HTTPResponse(io.BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible method definitions in the base classes msg: HTTPMessage diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index bb5ddc37c603..0abf16d9d0ab 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -26,7 +26,7 @@ from types import ( WrapperDescriptorType, ) from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload -from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard +from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs if sys.version_info >= (3, 11): __all__ = [ @@ -192,10 +192,10 @@ if sys.version_info >= (3, 11): def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... def getmodulename(path: StrPath) -> str | None: ... -def ismodule(object: object) -> TypeGuard[ModuleType]: ... -def isclass(object: object) -> TypeGuard[type[Any]]: ... -def ismethod(object: object) -> TypeGuard[MethodType]: ... -def isfunction(object: object) -> TypeGuard[FunctionType]: ... +def ismodule(object: object) -> TypeIs[ModuleType]: ... +def isclass(object: object) -> TypeIs[type[Any]]: ... +def ismethod(object: object) -> TypeIs[MethodType]: ... +def isfunction(object: object) -> TypeIs[FunctionType]: ... if sys.version_info >= (3, 12): def markcoroutinefunction(func: _F) -> _F: ... @@ -214,9 +214,9 @@ def iscoroutinefunction(obj: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[ def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... @overload def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... -def isgenerator(object: object) -> TypeGuard[GeneratorType[Any, Any, Any]]: ... -def iscoroutine(object: object) -> TypeGuard[CoroutineType[Any, Any, Any]]: ... -def isawaitable(object: object) -> TypeGuard[Awaitable[Any]]: ... +def isgenerator(object: object) -> TypeIs[GeneratorType[Any, Any, Any]]: ... +def iscoroutine(object: object) -> TypeIs[CoroutineType[Any, Any, Any]]: ... +def isawaitable(object: object) -> TypeIs[Awaitable[Any]]: ... @overload def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... @overload @@ -230,18 +230,18 @@ class _SupportsSet(Protocol[_T_cont, _V_cont]): class _SupportsDelete(Protocol[_T_cont]): def __delete__(self, instance: _T_cont, /) -> None: ... -def isasyncgen(object: object) -> TypeGuard[AsyncGeneratorType[Any, Any]]: ... -def istraceback(object: object) -> TypeGuard[TracebackType]: ... -def isframe(object: object) -> TypeGuard[FrameType]: ... -def iscode(object: object) -> TypeGuard[CodeType]: ... -def isbuiltin(object: object) -> TypeGuard[BuiltinFunctionType]: ... +def isasyncgen(object: object) -> TypeIs[AsyncGeneratorType[Any, Any]]: ... +def istraceback(object: object) -> TypeIs[TracebackType]: ... +def isframe(object: object) -> TypeIs[FrameType]: ... +def iscode(object: object) -> TypeIs[CodeType]: ... +def isbuiltin(object: object) -> TypeIs[BuiltinFunctionType]: ... if sys.version_info >= (3, 11): - def ismethodwrapper(object: object) -> TypeGuard[MethodWrapperType]: ... + def ismethodwrapper(object: object) -> TypeIs[MethodWrapperType]: ... def isroutine( object: object, -) -> TypeGuard[ +) -> TypeIs[ FunctionType | LambdaType | MethodType @@ -251,11 +251,11 @@ def isroutine( | MethodDescriptorType | ClassMethodDescriptorType ]: ... -def ismethoddescriptor(object: object) -> TypeGuard[MethodDescriptorType]: ... -def ismemberdescriptor(object: object) -> TypeGuard[MemberDescriptorType]: ... +def ismethoddescriptor(object: object) -> TypeIs[MethodDescriptorType]: ... +def ismemberdescriptor(object: object) -> TypeIs[MemberDescriptorType]: ... def isabstract(object: object) -> bool: ... -def isgetsetdescriptor(object: object) -> TypeGuard[GetSetDescriptorType]: ... -def isdatadescriptor(object: object) -> TypeGuard[_SupportsSet[Any, Any] | _SupportsDelete[Any]]: ... +def isgetsetdescriptor(object: object) -> TypeIs[GetSetDescriptorType]: ... +def isdatadescriptor(object: object) -> TypeIs[_SupportsSet[Any, Any] | _SupportsDelete[Any]]: ... # # Retrieving source code @@ -347,11 +347,11 @@ if sys.version_info >= (3, 10): # The name is the same as the enum's name in CPython class _ParameterKind(enum.IntEnum): - POSITIONAL_ONLY: int - POSITIONAL_OR_KEYWORD: int - VAR_POSITIONAL: int - KEYWORD_ONLY: int - VAR_KEYWORD: int + POSITIONAL_ONLY = 0 + POSITIONAL_OR_KEYWORD = 1 + VAR_POSITIONAL = 2 + KEYWORD_ONLY = 3 + VAR_KEYWORD = 4 @property def description(self) -> str: ... @@ -611,22 +611,22 @@ if sys.version_info >= (3, 9): if sys.version_info >= (3, 12): class BufferFlags(enum.IntFlag): - SIMPLE: int - WRITABLE: int - FORMAT: int - ND: int - STRIDES: int - C_CONTIGUOUS: int - F_CONTIGUOUS: int - ANY_CONTIGUOUS: int - INDIRECT: int - CONTIG: int - CONTIG_RO: int - STRIDED: int - STRIDED_RO: int - RECORDS: int - RECORDS_RO: int - FULL: int - FULL_RO: int - READ: int - WRITE: int + SIMPLE = 0 + WRITABLE = 1 + FORMAT = 4 + ND = 8 + STRIDES = 24 + C_CONTIGUOUS = 56 + F_CONTIGUOUS = 88 + ANY_CONTIGUOUS = 152 + INDIRECT = 280 + CONTIG = 9 + CONTIG_RO = 8 + STRIDED = 25 + STRIDED_RO = 24 + RECORDS = 29 + RECORDS_RO = 28 + FULL = 285 + FULL_RO = 284 + READ = 256 + WRITE = 512 diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index a62d0674df4c..f5f7f91ece61 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -597,7 +597,7 @@ class StreamHandler(Handler, Generic[_StreamT]): @overload def __init__(self: StreamHandler[TextIO], stream: None = None) -> None: ... @overload - def __init__(self: StreamHandler[_StreamT], stream: _StreamT) -> None: ... + def __init__(self: StreamHandler[_StreamT], stream: _StreamT) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 def setStream(self, stream: _StreamT) -> _StreamT | None: ... if sys.version_info >= (3, 11): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index a3edaa463818..9a45a81559c0 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -1,13 +1,13 @@ import ctypes import sys from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData +from ctypes import _CData, _SimpleCData, c_char from logging import Logger, _Level as _LoggingLevel from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, popen_spawn_win32, queues, synchronize from multiprocessing.managers import SyncManager from multiprocessing.pool import Pool as _Pool from multiprocessing.process import BaseProcess -from multiprocessing.sharedctypes import Synchronized, SynchronizedArray +from multiprocessing.sharedctypes import Synchronized, SynchronizedArray, SynchronizedString from typing import Any, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias @@ -19,6 +19,7 @@ else: __all__ = () _LockLike: TypeAlias = synchronize.Lock | synchronize.RLock +_T = TypeVar("_T") _CT = TypeVar("_CT", bound=_CData) class ProcessError(Exception): ... @@ -79,6 +80,10 @@ class BaseContext: @overload def RawArray(self, typecode_or_type: str, size_or_initializer: int | Sequence[Any]) -> Any: ... @overload + def Value( + self, typecode_or_type: type[_SimpleCData[_T]], *args: Any, lock: Literal[True] | _LockLike = True + ) -> Synchronized[_T]: ... + @overload def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[False]) -> Synchronized[_CT]: ... @overload def Value(self, typecode_or_type: type[_CT], *args: Any, lock: Literal[True] | _LockLike = True) -> Synchronized[_CT]: ... @@ -87,6 +92,10 @@ class BaseContext: @overload def Value(self, typecode_or_type: str | type[_CData], *args: Any, lock: bool | _LockLike = True) -> Any: ... @overload + def Array( + self, typecode_or_type: type[c_char], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True + ) -> SynchronizedString: ... + @overload def Array( self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] ) -> SynchronizedArray[_CT]: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index 048c6fe8d891..b417925fb17b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -14,7 +14,7 @@ class Barrier(threading.Barrier): self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *ctx: BaseContext ) -> None: ... -class Condition(AbstractContextManager[bool]): +class Condition(AbstractContextManager[bool, None]): def __init__(self, lock: _LockLike | None = None, *, ctx: BaseContext) -> None: ... def notify(self, n: int = 1) -> None: ... def notify_all(self) -> None: ... @@ -34,7 +34,7 @@ class Event: def wait(self, timeout: float | None = None) -> bool: ... # Not part of public API -class SemLock(AbstractContextManager[bool]): +class SemLock(AbstractContextManager[bool, None]): def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... def release(self) -> None: ... def __exit__( diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 89d906d4edfc..e1c7855c0bb6 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -747,7 +747,7 @@ if sys.platform != "win32": def getcwd() -> str: ... def getcwdb() -> bytes: ... -def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = True) -> None: ... +def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = ...) -> None: ... if sys.platform != "win32" and sys.platform != "linux": def chflags(path: StrOrBytesPath, flags: int, follow_symlinks: bool = True) -> None: ... # some flavors of Unix @@ -794,7 +794,7 @@ def replace( ) -> None: ... def rmdir(path: StrOrBytesPath, *, dir_fd: int | None = None) -> None: ... -class _ScandirIterator(Iterator[DirEntry[AnyStr]], AbstractContextManager[_ScandirIterator[AnyStr]]): +class _ScandirIterator(Iterator[DirEntry[AnyStr]], AbstractContextManager[_ScandirIterator[AnyStr], None]): def __next__(self) -> DirEntry[AnyStr]: ... def __exit__(self, *args: Unused) -> None: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index f7912a784987..09637673ce21 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -11,8 +11,8 @@ if sys.version_info < (3, 9): __all__ += ["readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "Data"] class PlistFormat(Enum): - FMT_XML: int - FMT_BINARY: int + FMT_XML = 1 + FMT_BINARY = 2 FMT_XML = PlistFormat.FMT_XML FMT_BINARY = PlistFormat.FMT_BINARY diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index d1571fd94be5..83256b433035 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -14,15 +14,15 @@ else: _Selector: TypeAlias = str | float | int class SortKey(StrEnum): - CALLS: str - CUMULATIVE: str - FILENAME: str - LINE: str - NAME: str - NFL: str - PCALLS: str - STDNAME: str - TIME: str + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" if sys.version_info >= (3, 9): from dataclasses import dataclass diff --git a/mypy/typeshed/stdlib/py_compile.pyi b/mypy/typeshed/stdlib/py_compile.pyi index 81561a202883..334ce79b5dd0 100644 --- a/mypy/typeshed/stdlib/py_compile.pyi +++ b/mypy/typeshed/stdlib/py_compile.pyi @@ -12,9 +12,9 @@ class PyCompileError(Exception): def __init__(self, exc_type: type[BaseException], exc_value: BaseException, file: str, msg: str = "") -> None: ... class PycInvalidationMode(enum.Enum): - TIMESTAMP: int - CHECKED_HASH: int - UNCHECKED_HASH: int + TIMESTAMP = 1 + CHECKED_HASH = 2 + UNCHECKED_HASH = 3 def _get_default_invalidation_mode() -> PycInvalidationMode: ... def compile( diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 663ee2fe7430..cbb7440b9147 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -9,57 +9,57 @@ from typing_extensions import Never, TypeAlias NSIG: int class Signals(IntEnum): - SIGABRT: int - SIGFPE: int - SIGILL: int - SIGINT: int - SIGSEGV: int - SIGTERM: int + SIGABRT = 6 + SIGFPE = 8 + SIGILL = 4 + SIGINT = 2 + SIGSEGV = 11 + SIGTERM = 15 if sys.platform == "win32": - SIGBREAK: int - CTRL_C_EVENT: int - CTRL_BREAK_EVENT: int + SIGBREAK = 21 + CTRL_C_EVENT = 0 + CTRL_BREAK_EVENT = 1 else: - SIGALRM: int - SIGBUS: int - SIGCHLD: int - SIGCONT: int - SIGHUP: int - SIGIO: int - SIGIOT: int - SIGKILL: int - SIGPIPE: int - SIGPROF: int - SIGQUIT: int - SIGSTOP: int - SIGSYS: int - SIGTRAP: int - SIGTSTP: int - SIGTTIN: int - SIGTTOU: int - SIGURG: int - SIGUSR1: int - SIGUSR2: int - SIGVTALRM: int - SIGWINCH: int - SIGXCPU: int - SIGXFSZ: int + SIGALRM = 14 + SIGBUS = 7 + SIGCHLD = 17 + SIGCONT = 18 + SIGHUP = 1 + SIGIO = 29 + SIGIOT = 6 + SIGKILL = 9 + SIGPIPE = 13 + SIGPROF = 27 + SIGQUIT = 3 + SIGSTOP = 19 + SIGSYS = 31 + SIGTRAP = 5 + SIGTSTP = 20 + SIGTTIN = 21 + SIGTTOU = 22 + SIGURG = 23 + SIGUSR1 = 10 + SIGUSR2 = 12 + SIGVTALRM = 26 + SIGWINCH = 28 + SIGXCPU = 24 + SIGXFSZ = 25 if sys.platform != "linux": - SIGEMT: int - SIGINFO: int + SIGEMT = 7 + SIGINFO = 29 if sys.platform != "darwin": - SIGCLD: int - SIGPOLL: int - SIGPWR: int - SIGRTMAX: int - SIGRTMIN: int + SIGCLD = 17 + SIGPOLL = 29 + SIGPWR = 30 + SIGRTMAX = 64 + SIGRTMIN = 34 if sys.version_info >= (3, 11): - SIGSTKFLT: int + SIGSTKFLT = 16 class Handlers(IntEnum): - SIG_DFL: int - SIG_IGN: int + SIG_DFL = 0 + SIG_IGN = 1 SIG_DFL: Handlers SIG_IGN: Handlers @@ -123,9 +123,9 @@ else: ITIMER_VIRTUAL: int class Sigmasks(IntEnum): - SIG_BLOCK: int - SIG_UNBLOCK: int - SIG_SETMASK: int + SIG_BLOCK = 0 + SIG_UNBLOCK = 1 + SIG_SETMASK = 2 SIG_BLOCK = Sigmasks.SIG_BLOCK SIG_UNBLOCK = Sigmasks.SIG_UNBLOCK diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index cdbd70533714..a309bac9370a 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -480,51 +480,51 @@ EAGAIN: int EWOULDBLOCK: int class AddressFamily(IntEnum): - AF_INET: int - AF_INET6: int - AF_APPLETALK: int - AF_DECnet: int - AF_IPX: int - AF_SNA: int - AF_UNSPEC: int + AF_INET = 2 + AF_INET6 = 10 + AF_APPLETALK = 5 + AF_DECnet = ... + AF_IPX = 4 + AF_SNA = 22 + AF_UNSPEC = 0 if sys.platform != "darwin": - AF_IRDA: int + AF_IRDA = 23 if sys.platform != "win32": - AF_ROUTE: int - AF_SYSTEM: int - AF_UNIX: int + AF_ROUTE = 16 + AF_SYSTEM = 32 + AF_UNIX = 1 if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5: int - AF_ASH: int - AF_ATMPVC: int - AF_ATMSVC: int - AF_AX25: int - AF_BRIDGE: int - AF_ECONET: int - AF_KEY: int - AF_LLC: int - AF_NETBEUI: int - AF_NETROM: int - AF_PPPOX: int - AF_ROSE: int - AF_SECURITY: int - AF_WANPIPE: int - AF_X25: int + AF_AAL5 = ... + AF_ASH = 18 + AF_ATMPVC = 8 + AF_ATMSVC = 20 + AF_AX25 = 3 + AF_BRIDGE = 7 + AF_ECONET = 19 + AF_KEY = 15 + AF_LLC = 26 + AF_NETBEUI = 13 + AF_NETROM = 6 + AF_PPPOX = 24 + AF_ROSE = 11 + AF_SECURITY = 14 + AF_WANPIPE = 25 + AF_X25 = 9 if sys.platform == "linux": - AF_CAN: int - AF_PACKET: int - AF_RDS: int - AF_TIPC: int - AF_ALG: int - AF_NETLINK: int - AF_VSOCK: int - AF_QIPCRTR: int + AF_CAN = 29 + AF_PACKET = 17 + AF_RDS = 21 + AF_TIPC = 30 + AF_ALG = 38 + AF_NETLINK = 16 + AF_VSOCK = 40 + AF_QIPCRTR = 42 if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK: int + AF_LINK = 33 if sys.platform != "darwin": - AF_BLUETOOTH: int + AF_BLUETOOTH = 32 if sys.platform == "win32" and sys.version_info >= (3, 12): - AF_HYPERV: int + AF_HYPERV = 34 AF_INET = AddressFamily.AF_INET AF_INET6 = AddressFamily.AF_INET6 @@ -579,14 +579,14 @@ if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = AddressFamily.AF_HYPERV class SocketKind(IntEnum): - SOCK_STREAM: int - SOCK_DGRAM: int - SOCK_RAW: int - SOCK_RDM: int - SOCK_SEQPACKET: int + SOCK_STREAM = 1 + SOCK_DGRAM = 2 + SOCK_RAW = 3 + SOCK_RDM = 4 + SOCK_SEQPACKET = 5 if sys.platform == "linux": - SOCK_CLOEXEC: int - SOCK_NONBLOCK: int + SOCK_CLOEXEC = 524288 + SOCK_NONBLOCK = 2048 SOCK_STREAM = SocketKind.SOCK_STREAM SOCK_DGRAM = SocketKind.SOCK_DGRAM @@ -598,32 +598,32 @@ if sys.platform == "linux": SOCK_NONBLOCK = SocketKind.SOCK_NONBLOCK class MsgFlag(IntFlag): - MSG_CTRUNC: int - MSG_DONTROUTE: int - MSG_OOB: int - MSG_PEEK: int - MSG_TRUNC: int - MSG_WAITALL: int + MSG_CTRUNC = 8 + MSG_DONTROUTE = 4 + MSG_OOB = 1 + MSG_PEEK = 2 + MSG_TRUNC = 32 + MSG_WAITALL = 256 if sys.platform != "darwin": - MSG_BCAST: int - MSG_MCAST: int - MSG_ERRQUEUE: int + MSG_BCAST = 1024 + MSG_MCAST = 2048 + MSG_ERRQUEUE = 8192 if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG: int - MSG_CMSG_CLOEXEC: int - MSG_CONFIRM: int - MSG_ETAG: int - MSG_FASTOPEN: int - MSG_MORE: int - MSG_NOTIFICATION: int + MSG_BTAG = ... + MSG_CMSG_CLOEXEC = 1073741821 + MSG_CONFIRM = 2048 + MSG_ETAG = ... + MSG_FASTOPEN = 536870912 + MSG_MORE = 32768 + MSG_NOTIFICATION = ... if sys.platform != "win32": - MSG_DONTWAIT: int - MSG_EOF: int - MSG_EOR: int - MSG_NOSIGNAL: int # sometimes this exists on darwin, sometimes not + MSG_DONTWAIT = 64 + MSG_EOF = 256 + MSG_EOR = 128 + MSG_NOSIGNAL = 16384 # sometimes this exists on darwin, sometimes not MSG_CTRUNC = MsgFlag.MSG_CTRUNC MSG_DONTROUTE = MsgFlag.MSG_DONTROUTE @@ -653,17 +653,17 @@ if sys.platform != "win32" and sys.platform != "darwin": MSG_NOTIFICATION = MsgFlag.MSG_NOTIFICATION class AddressInfo(IntFlag): - AI_ADDRCONFIG: int - AI_ALL: int - AI_CANONNAME: int - AI_NUMERICHOST: int - AI_NUMERICSERV: int - AI_PASSIVE: int - AI_V4MAPPED: int + AI_ADDRCONFIG = 32 + AI_ALL = 16 + AI_CANONNAME = 2 + AI_NUMERICHOST = 4 + AI_NUMERICSERV = 1024 + AI_PASSIVE = 1 + AI_V4MAPPED = 8 if sys.platform != "win32": - AI_DEFAULT: int - AI_MASK: int - AI_V4MAPPED_CFG: int + AI_DEFAULT = 1536 + AI_MASK = 5127 + AI_V4MAPPED_CFG = 512 AI_ADDRCONFIG = AddressInfo.AI_ADDRCONFIG AI_ALL = AddressInfo.AI_ALL diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 15d86372531a..81c68c69ec4e 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -138,23 +138,23 @@ if sys.platform == "win32": def enum_crls(store_name: str) -> _EnumRetType: ... class VerifyMode(enum.IntEnum): - CERT_NONE: int - CERT_OPTIONAL: int - CERT_REQUIRED: int + CERT_NONE = 0 + CERT_OPTIONAL = 1 + CERT_REQUIRED = 2 CERT_NONE: VerifyMode CERT_OPTIONAL: VerifyMode CERT_REQUIRED: VerifyMode class VerifyFlags(enum.IntFlag): - VERIFY_DEFAULT: int - VERIFY_CRL_CHECK_LEAF: int - VERIFY_CRL_CHECK_CHAIN: int - VERIFY_X509_STRICT: int - VERIFY_X509_TRUSTED_FIRST: int + VERIFY_DEFAULT = 0 + VERIFY_CRL_CHECK_LEAF = 4 + VERIFY_CRL_CHECK_CHAIN = 12 + VERIFY_X509_STRICT = 32 + VERIFY_X509_TRUSTED_FIRST = 32768 if sys.version_info >= (3, 10): - VERIFY_ALLOW_PROXY_CERTS: int - VERIFY_X509_PARTIAL_CHAIN: int + VERIFY_ALLOW_PROXY_CERTS = 64 + VERIFY_X509_PARTIAL_CHAIN = 524288 VERIFY_DEFAULT: VerifyFlags VERIFY_CRL_CHECK_LEAF: VerifyFlags @@ -167,15 +167,15 @@ if sys.version_info >= (3, 10): VERIFY_X509_PARTIAL_CHAIN: VerifyFlags class _SSLMethod(enum.IntEnum): - PROTOCOL_SSLv23: int - PROTOCOL_SSLv2: int - PROTOCOL_SSLv3: int - PROTOCOL_TLSv1: int - PROTOCOL_TLSv1_1: int - PROTOCOL_TLSv1_2: int - PROTOCOL_TLS: int - PROTOCOL_TLS_CLIENT: int - PROTOCOL_TLS_SERVER: int + PROTOCOL_SSLv23 = 2 + PROTOCOL_SSLv2 = ... + PROTOCOL_SSLv3 = ... + PROTOCOL_TLSv1 = 3 + PROTOCOL_TLSv1_1 = 4 + PROTOCOL_TLSv1_2 = 5 + PROTOCOL_TLS = 2 + PROTOCOL_TLS_CLIENT = 16 + PROTOCOL_TLS_SERVER = 17 PROTOCOL_SSLv23: _SSLMethod PROTOCOL_SSLv2: _SSLMethod @@ -188,25 +188,25 @@ PROTOCOL_TLS_CLIENT: _SSLMethod PROTOCOL_TLS_SERVER: _SSLMethod class Options(enum.IntFlag): - OP_ALL: int - OP_NO_SSLv2: int - OP_NO_SSLv3: int - OP_NO_TLSv1: int - OP_NO_TLSv1_1: int - OP_NO_TLSv1_2: int - OP_NO_TLSv1_3: int - OP_CIPHER_SERVER_PREFERENCE: int - OP_SINGLE_DH_USE: int - OP_SINGLE_ECDH_USE: int - OP_NO_COMPRESSION: int - OP_NO_TICKET: int - OP_NO_RENEGOTIATION: int - OP_ENABLE_MIDDLEBOX_COMPAT: int + OP_ALL = 2147483728 + OP_NO_SSLv2 = 0 + OP_NO_SSLv3 = 33554432 + OP_NO_TLSv1 = 67108864 + OP_NO_TLSv1_1 = 268435456 + OP_NO_TLSv1_2 = 134217728 + OP_NO_TLSv1_3 = 536870912 + OP_CIPHER_SERVER_PREFERENCE = 4194304 + OP_SINGLE_DH_USE = 0 + OP_SINGLE_ECDH_USE = 0 + OP_NO_COMPRESSION = 131072 + OP_NO_TICKET = 16384 + OP_NO_RENEGOTIATION = 1073741824 + OP_ENABLE_MIDDLEBOX_COMPAT = 1048576 if sys.version_info >= (3, 12): - OP_LEGACY_SERVER_CONNECT: int - OP_ENABLE_KTLS: int + OP_LEGACY_SERVER_CONNECT = 4 + OP_ENABLE_KTLS = 8 if sys.version_info >= (3, 11) or sys.platform == "linux": - OP_IGNORE_UNEXPECTED_EOF: int + OP_IGNORE_UNEXPECTED_EOF = 128 OP_ALL: Options OP_NO_SSLv2: Options @@ -246,33 +246,33 @@ OPENSSL_VERSION_INFO: tuple[int, int, int, int, int] OPENSSL_VERSION_NUMBER: int class AlertDescription(enum.IntEnum): - ALERT_DESCRIPTION_ACCESS_DENIED: int - ALERT_DESCRIPTION_BAD_CERTIFICATE: int - ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: int - ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: int - ALERT_DESCRIPTION_BAD_RECORD_MAC: int - ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: int - ALERT_DESCRIPTION_CERTIFICATE_REVOKED: int - ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: int - ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: int - ALERT_DESCRIPTION_CLOSE_NOTIFY: int - ALERT_DESCRIPTION_DECODE_ERROR: int - ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: int - ALERT_DESCRIPTION_DECRYPT_ERROR: int - ALERT_DESCRIPTION_HANDSHAKE_FAILURE: int - ALERT_DESCRIPTION_ILLEGAL_PARAMETER: int - ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: int - ALERT_DESCRIPTION_INTERNAL_ERROR: int - ALERT_DESCRIPTION_NO_RENEGOTIATION: int - ALERT_DESCRIPTION_PROTOCOL_VERSION: int - ALERT_DESCRIPTION_RECORD_OVERFLOW: int - ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: int - ALERT_DESCRIPTION_UNKNOWN_CA: int - ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: int - ALERT_DESCRIPTION_UNRECOGNIZED_NAME: int - ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: int - ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: int - ALERT_DESCRIPTION_USER_CANCELLED: int + ALERT_DESCRIPTION_ACCESS_DENIED = 49 + ALERT_DESCRIPTION_BAD_CERTIFICATE = 42 + ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE = 114 + ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE = 113 + ALERT_DESCRIPTION_BAD_RECORD_MAC = 20 + ALERT_DESCRIPTION_CERTIFICATE_EXPIRED = 45 + ALERT_DESCRIPTION_CERTIFICATE_REVOKED = 44 + ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN = 46 + ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE = 111 + ALERT_DESCRIPTION_CLOSE_NOTIFY = 0 + ALERT_DESCRIPTION_DECODE_ERROR = 50 + ALERT_DESCRIPTION_DECOMPRESSION_FAILURE = 30 + ALERT_DESCRIPTION_DECRYPT_ERROR = 51 + ALERT_DESCRIPTION_HANDSHAKE_FAILURE = 40 + ALERT_DESCRIPTION_ILLEGAL_PARAMETER = 47 + ALERT_DESCRIPTION_INSUFFICIENT_SECURITY = 71 + ALERT_DESCRIPTION_INTERNAL_ERROR = 80 + ALERT_DESCRIPTION_NO_RENEGOTIATION = 100 + ALERT_DESCRIPTION_PROTOCOL_VERSION = 70 + ALERT_DESCRIPTION_RECORD_OVERFLOW = 22 + ALERT_DESCRIPTION_UNEXPECTED_MESSAGE = 10 + ALERT_DESCRIPTION_UNKNOWN_CA = 48 + ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY = 115 + ALERT_DESCRIPTION_UNRECOGNIZED_NAME = 112 + ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE = 43 + ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION = 110 + ALERT_DESCRIPTION_USER_CANCELLED = 90 ALERT_DESCRIPTION_HANDSHAKE_FAILURE: AlertDescription ALERT_DESCRIPTION_INTERNAL_ERROR: AlertDescription @@ -316,8 +316,8 @@ class _ASN1Object(_ASN1ObjectBase): def fromname(cls, name: str) -> Self: ... class Purpose(_ASN1Object, enum.Enum): - SERVER_AUTH: _ASN1Object - CLIENT_AUTH: _ASN1Object + SERVER_AUTH = (129, "serverAuth", "TLS Web Server Authentication", "1.3.6.1.5.5.7.3.2") # pyright: ignore[reportCallIssue] + CLIENT_AUTH = (130, "clientAuth", "TLS Web Client Authentication", "1.3.6.1.5.5.7.3.1") # pyright: ignore[reportCallIssue] class SSLSocket(socket.socket): context: SSLContext @@ -371,13 +371,13 @@ class SSLSocket(socket.socket): def get_unverified_chain(self) -> list[bytes]: ... class TLSVersion(enum.IntEnum): - MINIMUM_SUPPORTED: int - MAXIMUM_SUPPORTED: int - SSLv3: int - TLSv1: int - TLSv1_1: int - TLSv1_2: int - TLSv1_3: int + MINIMUM_SUPPORTED = -2 + MAXIMUM_SUPPORTED = -1 + SSLv3 = 768 + TLSv1 = 769 + TLSv1_1 = 770 + TLSv1_2 = 771 + TLSv1_3 = 772 class SSLContext: check_hostname: bool @@ -506,15 +506,15 @@ class SSLSession: def __eq__(self, value: object, /) -> bool: ... class SSLErrorNumber(enum.IntEnum): - SSL_ERROR_EOF: int - SSL_ERROR_INVALID_ERROR_CODE: int - SSL_ERROR_SSL: int - SSL_ERROR_SYSCALL: int - SSL_ERROR_WANT_CONNECT: int - SSL_ERROR_WANT_READ: int - SSL_ERROR_WANT_WRITE: int - SSL_ERROR_WANT_X509_LOOKUP: int - SSL_ERROR_ZERO_RETURN: int + SSL_ERROR_EOF = 8 + SSL_ERROR_INVALID_ERROR_CODE = 10 + SSL_ERROR_SSL = 1 + SSL_ERROR_SYSCALL = 5 + SSL_ERROR_WANT_CONNECT = 7 + SSL_ERROR_WANT_READ = 2 + SSL_ERROR_WANT_WRITE = 3 + SSL_ERROR_WANT_X509_LOOKUP = 4 + SSL_ERROR_ZERO_RETURN = 6 SSL_ERROR_EOF: SSLErrorNumber # undocumented SSL_ERROR_INVALID_ERROR_CODE: SSLErrorNumber # undocumented diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 80bc56ef53f3..d8ce17535eab 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -194,45 +194,45 @@ if sys.version_info >= (3, 11): serial: int class EventType(StrEnum): - Activate: str - ButtonPress: str + Activate = "36" + ButtonPress = "4" Button = ButtonPress - ButtonRelease: str - Circulate: str - CirculateRequest: str - ClientMessage: str - Colormap: str - Configure: str - ConfigureRequest: str - Create: str - Deactivate: str - Destroy: str - Enter: str - Expose: str - FocusIn: str - FocusOut: str - GraphicsExpose: str - Gravity: str - KeyPress: str - Key = KeyPress - KeyRelease: str - Keymap: str - Leave: str - Map: str - MapRequest: str - Mapping: str - Motion: str - MouseWheel: str - NoExpose: str - Property: str - Reparent: str - ResizeRequest: str - Selection: str - SelectionClear: str - SelectionRequest: str - Unmap: str - VirtualEvent: str - Visibility: str + ButtonRelease = "5" + Circulate = "26" + CirculateRequest = "27" + ClientMessage = "33" + Colormap = "32" + Configure = "22" + ConfigureRequest = "23" + Create = "16" + Deactivate = "37" + Destroy = "17" + Enter = "7" + Expose = "12" + FocusIn = "9" + FocusOut = "10" + GraphicsExpose = "13" + Gravity = "24" + KeyPress = "2" + Key = "2" + KeyRelease = "3" + Keymap = "11" + Leave = "8" + Map = "19" + MapRequest = "20" + Mapping = "34" + Motion = "6" + MouseWheel = "38" + NoExpose = "14" + Property = "28" + Reparent = "21" + ResizeRequest = "25" + Selection = "31" + SelectionClear = "29" + SelectionRequest = "30" + Unmap = "18" + VirtualEvent = "35" + Visibility = "15" _W = TypeVar("_W", bound=Misc) # Events considered covariant because you should never assign to event.widget. diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 580322b653b4..4b80397bdd7a 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -129,12 +129,6 @@ if sys.version_info >= (3, 11): if sys.version_info >= (3, 12): __all__ += ["TypeAliasType", "override"] -ContextManager = AbstractContextManager -AsyncContextManager = AbstractAsyncContextManager - -# This itself is only available during type checking -def type_check_only(func_or_cls: _F) -> _F: ... - Any = object() def final(f: _T) -> _T: ... @@ -183,12 +177,6 @@ class _SpecialForm: def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... -_F = TypeVar("_F", bound=Callable[..., Any]) -_P = _ParamSpec("_P") -_T = TypeVar("_T") - -def overload(func: _F) -> _F: ... - Union: _SpecialForm Generic: _SpecialForm # Protocol is only present in 3.8 and later, but mypy needs it unconditionally @@ -295,6 +283,10 @@ if sys.version_info >= (3, 10): else: def NewType(name: str, tp: Any) -> Any: ... +_F = TypeVar("_F", bound=Callable[..., Any]) +_P = _ParamSpec("_P") +_T = TypeVar("_T") + # These type variables are used by the container types. _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. @@ -304,9 +296,13 @@ _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. _TC = TypeVar("_TC", bound=type[object]) +def overload(func: _F) -> _F: ... def no_type_check(arg: _F) -> _F: ... def no_type_check_decorator(decorator: Callable[_P, _T]) -> Callable[_P, _T]: ... +# This itself is only available during type checking +def type_check_only(func_or_cls: _F) -> _F: ... + # Type aliases and type constructors class _Alias: @@ -432,10 +428,22 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return @property def gi_yieldfrom(self) -> Generator[Any, Any, Any] | None: ... +# NOTE: Technically we would like this to be able to accept a second parameter as well, just +# like it's counterpart in contextlib, however `typing._SpecialGenericAlias` enforces the +# correct number of arguments at runtime, so we would be hiding runtime errors. +@runtime_checkable +class ContextManager(AbstractContextManager[_T_co, bool | None], Protocol[_T_co]): ... + +# NOTE: Technically we would like this to be able to accept a second parameter as well, just +# like it's counterpart in contextlib, however `typing._SpecialGenericAlias` enforces the +# correct number of arguments at runtime, so we would be hiding runtime errors. +@runtime_checkable +class AsyncContextManager(AbstractAsyncContextManager[_T_co, bool | None], Protocol[_T_co]): ... + @runtime_checkable class Awaitable(Protocol[_T_co]): @abstractmethod - def __await__(self) -> Generator[Any, None, _T_co]: ... + def __await__(self) -> Generator[Any, Any, _T_co]: ... class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): __name__: str @@ -445,7 +453,7 @@ class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _Retu @property def cr_code(self) -> CodeType: ... @property - def cr_frame(self) -> FrameType: ... + def cr_frame(self) -> FrameType | None: ... @property def cr_running(self) -> bool: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 6e64e7a85560..dd61b83a658a 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -188,7 +188,7 @@ class _patch(Generic[_T]): # but that's impossible with the current type system. if sys.version_info >= (3, 10): def __init__( - self: _patch[_T], + self: _patch[_T], # pyright: ignore[reportInvalidTypeVarUse] #11780 getter: Callable[[], Any], attribute: str, new: _T, @@ -203,7 +203,7 @@ class _patch(Generic[_T]): ) -> None: ... else: def __init__( - self: _patch[_T], + self: _patch[_T], # pyright: ignore[reportInvalidTypeVarUse] #11780 getter: Callable[[], Any], attribute: str, new: _T, diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index e1ea424f9680..1be7a5ef009f 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -7,9 +7,9 @@ from typing_extensions import TypeAlias _FieldsType: TypeAlias = tuple[int, int, int, int, int, int] class SafeUUID(Enum): - safe: int - unsafe: int - unknown: None + safe = 0 + unsafe = -1 + unknown = None class UUID: def __init__( diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 8f3ad0631c10..e345124237da 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -51,10 +51,17 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): @overload def __init__(self) -> None: ... @overload - def __init__(self: WeakValueDictionary[_KT, _VT], other: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]], /) -> None: ... + def __init__( + self: WeakValueDictionary[_KT, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + other: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]], + /, + ) -> None: ... @overload def __init__( - self: WeakValueDictionary[str, _VT], other: Mapping[str, _VT] | Iterable[tuple[str, _VT]] = (), /, **kwargs: _VT + self: WeakValueDictionary[str, _VT], # pyright: ignore[reportInvalidTypeVarUse] #11780 + other: Mapping[str, _VT] | Iterable[tuple[str, _VT]] = (), + /, + **kwargs: _VT, ) -> None: ... def __len__(self) -> int: ... def __getitem__(self, key: _KT) -> _VT: ... From fb31409b392c5533b25173705d62ed385ee39cfb Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 1 May 2024 03:15:32 -0700 Subject: [PATCH 0575/1617] Run more tests in parallel (#17185) At some point, Github Actions runners started having more cores: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories --- .github/workflows/test.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4593e79e728c..98a737a78b3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,39 +36,39 @@ jobs: arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py38-windows-64 python: '3.8' arch: x64 os: windows-latest toxenv: py38 - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" - name: Test suite with py39-ubuntu python: '3.9' arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" - name: Test suite with py310-ubuntu python: '3.10' arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" - name: Test suite with py311-ubuntu, mypyc-compiled python: '3.11' arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled python: '3.12' arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2" + tox_extra_args: "-n 4" test_mypyc: true - name: mypyc runtime tests with py39-macos @@ -77,13 +77,13 @@ jobs: # TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version os: macos-13 toxenv: py - tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" + tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py" - name: mypyc runtime tests with py38-debug-build-ubuntu python: '3.8.17' arch: x64 os: ubuntu-latest toxenv: py - tox_extra_args: "-n 2 mypyc/test/test_run.py mypyc/test/test_external.py" + tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" debug_build: true - name: Type check our own code (py38-ubuntu) @@ -141,7 +141,9 @@ jobs: pip install -r test-requirements.txt CC=clang MYPYC_OPT_LEVEL=0 MYPY_USE_MYPYC=1 pip install -e . - name: Setup tox environment - run: tox run -e ${{ matrix.toxenv }} --notest + run: | + tox run -e ${{ matrix.toxenv }} --notest + python -c 'import os; print("os.cpu_count", os.cpu_count(), "os.sched_getaffinity", len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} @@ -190,4 +192,4 @@ jobs: - name: Setup tox environment run: tox run -e py --notest - name: Test - run: tox run -e py --skip-pkg-install -- -n 2 mypyc/test/ + run: tox run -e py --skip-pkg-install -- -n 4 mypyc/test/ From 35fbd2a852be2c47e8006429afe9b3ad4b1bfac2 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Sat, 11 May 2024 05:11:02 +0530 Subject: [PATCH 0576/1617] Add Error format support, and JSON output option (#11396) ### Description Resolves #10816 The changes this PR makes are relatively small. It currently: - Adds an `--output` option to mypy CLI - Adds a `ErrorFormatter` abstract base class, which can be subclassed to create new output formats - Adds a `MypyError` class that represents the external format of a mypy error. - Adds a check for `--output` being `'json'`, in which case the `JSONFormatter` is used to produce the reported output. #### Demo: ```console $ mypy mytest.py mytest.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") mytest.py:3: error: Name "z" is not defined Found 2 errors in 1 file (checked 1 source file) $ mypy mytest.py --output=json {"file": "mytest.py", "line": 2, "column": 4, "severity": "error", "message": "Incompatible types in assignment (expression has type \"str\", variable has type \"int\")", "code": "assignment"} {"file": "mytest.py", "line": 3, "column": 4, "severity": "error", "message": "Name \"z\" is not defined", "code": "name-defined"} ``` --- A few notes regarding the changes: - I chose to re-use the intermediate `ErrorTuple`s created during error reporting, instead of using the more general `ErrorInfo` class, because a lot of machinery already exists in mypy for sorting and removing duplicate error reports, which produces `ErrorTuple`s at the end. The error sorting and duplicate removal logic could perhaps be separated out from the rest of the code, to be able to use `ErrorInfo` objects more freely. - `ErrorFormatter` doesn't really need to be an abstract class, but I think it would be better this way. If there's a different method that would be preferred, I'd be happy to know. - The `--output` CLI option is, most probably, not added in the correct place. Any help in how to do it properly would be appreciated, the mypy option parsing code seems very complex. - The ability to add custom output formats can be simply added by subclassing the `ErrorFormatter` class inside a mypy plugin, and adding a `name` field to the formatters. The mypy runtime can then check through the `__subclasses__` of the formatter and determine if such a formatter is present. The "checking for the `name` field" part of this code might be appropriate to add within this PR itself, instead of hard-coding `JSONFormatter`. Does that sound like a good idea? --------- Co-authored-by: Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com> Co-authored-by: Tushar Sadhwani --- mypy/build.py | 11 ++--- mypy/error_formatter.py | 37 +++++++++++++++++ mypy/errors.py | 75 ++++++++++++++++++++++++++++++---- mypy/main.py | 17 +++++++- mypy/options.py | 4 +- mypy/test/testoutput.py | 58 ++++++++++++++++++++++++++ mypy/util.py | 9 +++- test-data/unit/outputjson.test | 44 ++++++++++++++++++++ 8 files changed, 239 insertions(+), 16 deletions(-) create mode 100644 mypy/error_formatter.py create mode 100644 mypy/test/testoutput.py create mode 100644 test-data/unit/outputjson.test diff --git a/mypy/build.py b/mypy/build.py index 84c85e66bd49..3ceb473f0948 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -44,6 +44,7 @@ import mypy.semanal_main from mypy.checker import TypeChecker +from mypy.error_formatter import OUTPUT_CHOICES, ErrorFormatter from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.indirection import TypeIndirectionVisitor @@ -253,6 +254,7 @@ def _build( plugin=plugin, plugins_snapshot=snapshot, errors=errors, + error_formatter=None if options.output is None else OUTPUT_CHOICES.get(options.output), flush_errors=flush_errors, fscache=fscache, stdout=stdout, @@ -607,6 +609,7 @@ def __init__( fscache: FileSystemCache, stdout: TextIO, stderr: TextIO, + error_formatter: ErrorFormatter | None = None, ) -> None: self.stats: dict[str, Any] = {} # Values are ints or floats self.stdout = stdout @@ -615,6 +618,7 @@ def __init__( self.data_dir = data_dir self.errors = errors self.errors.set_ignore_prefix(ignore_prefix) + self.error_formatter = error_formatter self.search_paths = search_paths self.source_set = source_set self.reports = reports @@ -3463,11 +3467,8 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No for id in stale: graph[id].transitive_error = True for id in stale: - manager.flush_errors( - manager.errors.simplify_path(graph[id].xpath), - manager.errors.file_messages(graph[id].xpath), - False, - ) + errors = manager.errors.file_messages(graph[id].xpath, formatter=manager.error_formatter) + manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False) graph[id].write_cache() graph[id].mark_as_rechecked() diff --git a/mypy/error_formatter.py b/mypy/error_formatter.py new file mode 100644 index 000000000000..ffc6b6747596 --- /dev/null +++ b/mypy/error_formatter.py @@ -0,0 +1,37 @@ +"""Defines the different custom formats in which mypy can output.""" + +import json +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from mypy.errors import MypyError + + +class ErrorFormatter(ABC): + """Base class to define how errors are formatted before being printed.""" + + @abstractmethod + def report_error(self, error: "MypyError") -> str: + raise NotImplementedError + + +class JSONFormatter(ErrorFormatter): + """Formatter for basic JSON output format.""" + + def report_error(self, error: "MypyError") -> str: + """Prints out the errors as simple, static JSON lines.""" + return json.dumps( + { + "file": error.file_path, + "line": error.line, + "column": error.column, + "message": error.message, + "hint": None if len(error.hints) == 0 else "\n".join(error.hints), + "code": None if error.errorcode is None else error.errorcode.code, + "severity": error.severity, + } + ) + + +OUTPUT_CHOICES = {"json": JSONFormatter()} diff --git a/mypy/errors.py b/mypy/errors.py index eabe96a2dc73..7a937da39c20 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -8,6 +8,7 @@ from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes +from mypy.error_formatter import ErrorFormatter from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes from mypy.message_registry import ErrorMessage from mypy.options import Options @@ -834,7 +835,7 @@ def raise_error(self, use_stdout: bool = True) -> NoReturn: ) def format_messages( - self, error_info: list[ErrorInfo], source_lines: list[str] | None + self, error_tuples: list[ErrorTuple], source_lines: list[str] | None ) -> list[str]: """Return a string list that represents the error messages. @@ -843,9 +844,6 @@ def format_messages( severity 'error'). """ a: list[str] = [] - error_info = [info for info in error_info if not info.hidden] - errors = self.render_messages(self.sort_messages(error_info)) - errors = self.remove_duplicates(errors) for ( file, line, @@ -856,7 +854,7 @@ def format_messages( message, allow_dups, code, - ) in errors: + ) in error_tuples: s = "" if file is not None: if self.options.show_column_numbers and line >= 0 and column >= 0: @@ -901,18 +899,28 @@ def format_messages( a.append(" " * (DEFAULT_SOURCE_OFFSET + column) + marker) return a - def file_messages(self, path: str) -> list[str]: + def file_messages(self, path: str, formatter: ErrorFormatter | None = None) -> list[str]: """Return a string list of new error messages from a given file. Use a form suitable for displaying to the user. """ if path not in self.error_info_map: return [] + + error_info = self.error_info_map[path] + error_info = [info for info in error_info if not info.hidden] + error_tuples = self.render_messages(self.sort_messages(error_info)) + error_tuples = self.remove_duplicates(error_tuples) + + if formatter is not None: + errors = create_errors(error_tuples) + return [formatter.report_error(err) for err in errors] + self.flushed_files.add(path) source_lines = None if self.options.pretty and self.read_source: source_lines = self.read_source(path) - return self.format_messages(self.error_info_map[path], source_lines) + return self.format_messages(error_tuples, source_lines) def new_messages(self) -> list[str]: """Return a string list of new error messages. @@ -1278,3 +1286,56 @@ def report_internal_error( # Exit. The caller has nothing more to say. # We use exit code 2 to signal that this is no ordinary error. raise SystemExit(2) + + +class MypyError: + def __init__( + self, + file_path: str, + line: int, + column: int, + message: str, + errorcode: ErrorCode | None, + severity: Literal["error", "note"], + ) -> None: + self.file_path = file_path + self.line = line + self.column = column + self.message = message + self.errorcode = errorcode + self.severity = severity + self.hints: list[str] = [] + + +# (file_path, line, column) +_ErrorLocation = Tuple[str, int, int] + + +def create_errors(error_tuples: list[ErrorTuple]) -> list[MypyError]: + errors: list[MypyError] = [] + latest_error_at_location: dict[_ErrorLocation, MypyError] = {} + + for error_tuple in error_tuples: + file_path, line, column, _, _, severity, message, _, errorcode = error_tuple + if file_path is None: + continue + + assert severity in ("error", "note") + if severity == "note": + error_location = (file_path, line, column) + error = latest_error_at_location.get(error_location) + if error is None: + # This is purely a note, with no error correlated to it + error = MypyError(file_path, line, column, message, errorcode, severity="note") + errors.append(error) + continue + + error.hints.append(message) + + else: + error = MypyError(file_path, line, column, message, errorcode, severity="error") + errors.append(error) + error_location = (file_path, line, column) + latest_error_at_location[error_location] = error + + return errors diff --git a/mypy/main.py b/mypy/main.py index c2df79d51e83..489ef8fd9a7b 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -18,6 +18,7 @@ parse_version, validate_package_allow_list, ) +from mypy.error_formatter import OUTPUT_CHOICES from mypy.errorcodes import error_codes from mypy.errors import CompileError from mypy.find_sources import InvalidSourceList, create_source_list @@ -72,7 +73,9 @@ def main( if clean_exit: options.fast_exit = False - formatter = util.FancyFormatter(stdout, stderr, options.hide_error_codes) + formatter = util.FancyFormatter( + stdout, stderr, options.hide_error_codes, hide_success=bool(options.output) + ) if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr): # Since --install-types performs user input, we want regular stdout and stderr. @@ -156,7 +159,9 @@ def run_build( stdout: TextIO, stderr: TextIO, ) -> tuple[build.BuildResult | None, list[str], bool]: - formatter = util.FancyFormatter(stdout, stderr, options.hide_error_codes) + formatter = util.FancyFormatter( + stdout, stderr, options.hide_error_codes, hide_success=bool(options.output) + ) messages = [] messages_by_file = defaultdict(list) @@ -525,6 +530,14 @@ def add_invertible_flag( stdout=stdout, ) + general_group.add_argument( + "-O", + "--output", + metavar="FORMAT", + help="Set a custom output format", + choices=OUTPUT_CHOICES, + ) + config_group = parser.add_argument_group( title="Config file", description="Use a config file instead of command line arguments. " diff --git a/mypy/options.py b/mypy/options.py index bf9c09f1bf4b..91639828801e 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -376,10 +376,12 @@ def __init__(self) -> None: self.disable_bytearray_promotion = False self.disable_memoryview_promotion = False - self.force_uppercase_builtins = False self.force_union_syntax = False + # Sets custom output format + self.output: str | None = None + def use_lowercase_names(self) -> bool: if self.python_version >= (3, 9): return not self.force_uppercase_builtins diff --git a/mypy/test/testoutput.py b/mypy/test/testoutput.py new file mode 100644 index 000000000000..41f6881658c8 --- /dev/null +++ b/mypy/test/testoutput.py @@ -0,0 +1,58 @@ +"""Test cases for `--output=json`. + +These cannot be run by the usual unit test runner because of the backslashes in +the output, which get normalized to forward slashes by the test suite on Windows. +""" + +from __future__ import annotations + +import os +import os.path + +from mypy import api +from mypy.defaults import PYTHON3_VERSION +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite + + +class OutputJSONsuite(DataSuite): + files = ["outputjson.test"] + + def run_case(self, testcase: DataDrivenTestCase) -> None: + test_output_json(testcase) + + +def test_output_json(testcase: DataDrivenTestCase) -> None: + """Runs Mypy in a subprocess, and ensures that `--output=json` works as intended.""" + mypy_cmdline = ["--output=json"] + mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}") + + # Write the program to a file. + program_path = os.path.join(test_temp_dir, "main") + mypy_cmdline.append(program_path) + with open(program_path, "w", encoding="utf8") as file: + for s in testcase.input: + file.write(f"{s}\n") + + output = [] + # Type check the program. + out, err, returncode = api.run(mypy_cmdline) + # split lines, remove newlines, and remove directory of test case + for line in (out + err).rstrip("\n").splitlines(): + if line.startswith(test_temp_dir + os.sep): + output.append(line[len(test_temp_dir + os.sep) :].rstrip("\r\n")) + else: + output.append(line.rstrip("\r\n")) + + if returncode > 1: + output.append("!!! Mypy crashed !!!") + + # Remove temp file. + os.remove(program_path) + + # JSON encodes every `\` character into `\\`, so we need to remove `\\` from windows paths + # and `/` from POSIX paths + json_os_separator = os.sep.replace("\\", "\\\\") + normalized_output = [line.replace(test_temp_dir + json_os_separator, "") for line in output] + + assert normalized_output == testcase.output diff --git a/mypy/util.py b/mypy/util.py index bbb5a8610f7f..4b1b918b92e6 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -563,8 +563,12 @@ class FancyFormatter: This currently only works on Linux and Mac. """ - def __init__(self, f_out: IO[str], f_err: IO[str], hide_error_codes: bool) -> None: + def __init__( + self, f_out: IO[str], f_err: IO[str], hide_error_codes: bool, hide_success: bool = False + ) -> None: self.hide_error_codes = hide_error_codes + self.hide_success = hide_success + # Check if we are in a human-facing terminal on a supported platform. if sys.platform not in ("linux", "darwin", "win32", "emscripten"): self.dummy_term = True @@ -793,6 +797,9 @@ def format_success(self, n_sources: int, use_color: bool = True) -> str: n_sources is total number of files passed directly on command line, i.e. excluding stubs and followed imports. """ + if self.hide_success: + return "" + msg = f"Success: no issues found in {n_sources} source file{plural_s(n_sources)}" if not use_color: return msg diff --git a/test-data/unit/outputjson.test b/test-data/unit/outputjson.test new file mode 100644 index 000000000000..43649b7b781d --- /dev/null +++ b/test-data/unit/outputjson.test @@ -0,0 +1,44 @@ +-- Test cases for `--output=json`. +-- These cannot be run by the usual unit test runner because of the backslashes +-- in the output, which get normalized to forward slashes by the test suite on +-- Windows. + +[case testOutputJsonNoIssues] +# flags: --output=json +def foo() -> None: + pass + +foo() +[out] + +[case testOutputJsonSimple] +# flags: --output=json +def foo() -> None: + pass + +foo(1) +[out] +{"file": "main", "line": 5, "column": 0, "message": "Too many arguments for \"foo\"", "hint": null, "code": "call-arg", "severity": "error"} + +[case testOutputJsonWithHint] +# flags: --output=json +from typing import Optional, overload + +@overload +def foo() -> None: ... +@overload +def foo(x: int) -> None: ... + +def foo(x: Optional[int] = None) -> None: + ... + +reveal_type(foo) + +foo('42') + +def bar() -> None: ... +bar('42') +[out] +{"file": "main", "line": 12, "column": 12, "message": "Revealed type is \"Overload(def (), def (x: builtins.int))\"", "hint": null, "code": "misc", "severity": "note"} +{"file": "main", "line": 14, "column": 0, "message": "No overload variant of \"foo\" matches argument type \"str\"", "hint": "Possible overload variants:\n def foo() -> None\n def foo(x: int) -> None", "code": "call-overload", "severity": "error"} +{"file": "main", "line": 17, "column": 0, "message": "Too many arguments for \"bar\"", "hint": null, "code": "call-arg", "severity": "error"} From b4f98698d83d2601b97cbead4503066e492bf8a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 21:11:50 -0400 Subject: [PATCH 0577/1617] Sync typeshed (#17246) Source commit: https://github.com/python/typeshed/commit/a9d7e861f7a46ae7acd56569326adef302e10f29 --- .../test_cases/asyncio/check_coroutines.py | 25 ++ .../@tests/test_cases/asyncio/check_gather.py | 38 ++ .../@tests/test_cases/asyncio/check_task.py | 28 ++ .../test_cases/builtins/check_dict-py39.py | 67 +++ .../@tests/test_cases/builtins/check_dict.py | 58 +++ .../builtins/check_exception_group-py311.py | 323 ++++++++++++++ .../test_cases/builtins/check_iteration.py | 16 + .../@tests/test_cases/builtins/check_list.py | 21 + .../test_cases/builtins/check_object.py | 13 + .../@tests/test_cases/builtins/check_pow.py | 91 ++++ .../test_cases/builtins/check_reversed.py | 34 ++ .../@tests/test_cases/builtins/check_round.py | 68 +++ .../@tests/test_cases/builtins/check_sum.py | 55 +++ .../@tests/test_cases/builtins/check_tuple.py | 13 + .../stdlib/@tests/test_cases/check_codecs.py | 13 + .../test_cases/check_concurrent_futures.py | 30 ++ .../@tests/test_cases/check_contextlib.py | 20 + .../@tests/test_cases/check_dataclasses.py | 101 +++++ .../stdlib/@tests/test_cases/check_enum.py | 38 ++ .../@tests/test_cases/check_functools.py | 67 +++ .../@tests/test_cases/check_importlib.py | 47 ++ .../test_cases/check_importlib_metadata.py | 33 ++ .../stdlib/@tests/test_cases/check_io.py | 6 + .../stdlib/@tests/test_cases/check_logging.py | 30 ++ .../test_cases/check_multiprocessing.py | 14 + .../stdlib/@tests/test_cases/check_pathlib.py | 20 + .../stdlib/@tests/test_cases/check_re.py | 26 ++ .../stdlib/@tests/test_cases/check_sqlite3.py | 26 ++ .../stdlib/@tests/test_cases/check_tarfile.py | 13 + .../@tests/test_cases/check_tempfile.py | 31 ++ .../@tests/test_cases/check_threading.py | 14 + .../stdlib/@tests/test_cases/check_tkinter.py | 30 ++ .../@tests/test_cases/check_unittest.py | 173 ++++++++ .../stdlib/@tests/test_cases/check_xml.py | 35 ++ .../collections/check_defaultdict-py39.py | 69 +++ .../@tests/test_cases/email/check_message.py | 6 + .../itertools/check_itertools_recipes.py | 410 ++++++++++++++++++ .../test_cases/typing/check_MutableMapping.py | 18 + .../@tests/test_cases/typing/check_all.py | 14 + .../typing/check_regression_issue_9296.py | 16 + .../test_cases/typing/check_typing_io.py | 21 + mypy/typeshed/stdlib/_typeshed/importlib.pyi | 18 + mypy/typeshed/stdlib/ast.pyi | 230 +++++++--- mypy/typeshed/stdlib/builtins.pyi | 8 +- mypy/typeshed/stdlib/dbm/__init__.pyi | 11 +- mypy/typeshed/stdlib/dbm/dumb.pyi | 8 +- mypy/typeshed/stdlib/dbm/gnu.pyi | 7 +- mypy/typeshed/stdlib/dbm/ndbm.pyi | 7 +- mypy/typeshed/stdlib/importlib/abc.pyi | 4 +- mypy/typeshed/stdlib/importlib/util.pyi | 5 +- mypy/typeshed/stdlib/logging/__init__.pyi | 2 +- mypy/typeshed/stdlib/pathlib.pyi | 8 +- mypy/typeshed/stdlib/pkgutil.pyi | 12 +- mypy/typeshed/stdlib/shelve.pyi | 17 +- mypy/typeshed/stdlib/socket.pyi | 13 + mypy/typeshed/stdlib/sys/__init__.pyi | 13 +- mypy/typeshed/stdlib/tempfile.pyi | 6 +- mypy/typeshed/stdlib/types.pyi | 29 +- mypy/typeshed/stdlib/typing.pyi | 43 +- 59 files changed, 2469 insertions(+), 143 deletions(-) create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_enum.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_functools.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_io.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_logging.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_re.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_threading.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_xml.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py create mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py create mode 100644 mypy/typeshed/stdlib/_typeshed/importlib.pyi diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py new file mode 100644 index 000000000000..160bd896469e --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from asyncio import iscoroutinefunction +from collections.abc import Awaitable, Callable, Coroutine +from typing import Any +from typing_extensions import assert_type + + +def test_iscoroutinefunction( + x: Callable[[str, int], Coroutine[str, int, bytes]], + y: Callable[[str, int], Awaitable[bytes]], + z: Callable[[str, int], str | Awaitable[bytes]], + xx: object, +) -> None: + if iscoroutinefunction(x): + assert_type(x, Callable[[str, int], Coroutine[str, int, bytes]]) + + if iscoroutinefunction(y): + assert_type(y, Callable[[str, int], Coroutine[Any, Any, bytes]]) + + if iscoroutinefunction(z): + assert_type(z, Callable[[str, int], Coroutine[Any, Any, Any]]) + + if iscoroutinefunction(xx): + assert_type(xx, Callable[..., Coroutine[Any, Any, Any]]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py new file mode 100644 index 000000000000..02a01e39731a --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import asyncio +from typing import Awaitable, List, Tuple, Union +from typing_extensions import assert_type + + +async def coro1() -> int: + return 42 + + +async def coro2() -> str: + return "spam" + + +async def test_gather(awaitable1: Awaitable[int], awaitable2: Awaitable[str]) -> None: + a = await asyncio.gather(awaitable1) + assert_type(a, Tuple[int]) + + b = await asyncio.gather(awaitable1, awaitable2, return_exceptions=True) + assert_type(b, Tuple[Union[int, BaseException], Union[str, BaseException]]) + + c = await asyncio.gather(awaitable1, awaitable2, awaitable1, awaitable1, awaitable1, awaitable1) + assert_type(c, Tuple[int, str, int, int, int, int]) + + d = await asyncio.gather(awaitable1, awaitable1, awaitable1, awaitable1, awaitable1, awaitable1, awaitable1) + assert_type(d, List[int]) + + awaitables_list: list[Awaitable[int]] = [awaitable1] + e = await asyncio.gather(*awaitables_list) + assert_type(e, List[int]) + + # this case isn't reliable between typecheckers, no one would ever call it with no args anyway + # f = await asyncio.gather() + # assert_type(f, list[Any]) + + +asyncio.run(test_gather(coro1(), coro2())) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py new file mode 100644 index 000000000000..69bcf8f782aa --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +import asyncio + + +class Waiter: + def __init__(self) -> None: + self.tasks: list[asyncio.Task[object]] = [] + + def add(self, t: asyncio.Task[object]) -> None: + self.tasks.append(t) + + async def join(self) -> None: + await asyncio.wait(self.tasks) + + +async def foo() -> int: + return 42 + + +async def main() -> None: + # asyncio.Task is covariant in its type argument, which is unusual since its parent class + # asyncio.Future is invariant in its type argument. This is only sound because asyncio.Task + # is not actually Liskov substitutable for asyncio.Future: it does not implement set_result. + w = Waiter() + t: asyncio.Task[int] = asyncio.create_task(foo()) + w.add(t) + await w.join() diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py new file mode 100644 index 000000000000..d707cfed222e --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py @@ -0,0 +1,67 @@ +""" +Tests for `dict.__(r)or__`. + +`dict.__or__` and `dict.__ror__` were only added in py39, +hence why these are in a separate file to the other test cases for `dict`. +""" + +from __future__ import annotations + +import os +import sys +from typing import Mapping, TypeVar, Union +from typing_extensions import Self, assert_type + +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + +if sys.version_info >= (3, 9): + + class CustomDictSubclass(dict[_KT, _VT]): + pass + + class CustomMappingWithDunderOr(Mapping[_KT, _VT]): + def __or__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: + return {} + + def __ror__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: + return {} + + def __ior__(self, other: Mapping[_KT, _VT]) -> Self: + return self + + def test_dict_dot_or( + a: dict[int, int], + b: CustomDictSubclass[int, int], + c: dict[str, str], + d: Mapping[int, int], + e: CustomMappingWithDunderOr[str, str], + ) -> None: + # dict.__(r)or__ always returns a dict, even if called on a subclass of dict: + assert_type(a | b, dict[int, int]) + assert_type(b | a, dict[int, int]) + + assert_type(a | c, dict[Union[int, str], Union[int, str]]) + + # arbitrary mappings are not accepted by `dict.__or__`; + # it has to be a subclass of `dict` + a | d # type: ignore + + # but Mappings such as `os._Environ` or `CustomMappingWithDunderOr`, + # which define `__ror__` methods that accept `dict`, are fine: + assert_type(a | os.environ, dict[Union[str, int], Union[str, int]]) + assert_type(os.environ | a, dict[Union[str, int], Union[str, int]]) + + assert_type(c | os.environ, dict[str, str]) + assert_type(c | e, dict[str, str]) + + assert_type(os.environ | c, dict[str, str]) + assert_type(e | c, dict[str, str]) + + e |= c + e |= a # type: ignore + + # TODO: this test passes mypy, but fails pyright for some reason: + # c |= e + + c |= a # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py new file mode 100644 index 000000000000..aa920d045cbc --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import Dict, Generic, Iterable, TypeVar +from typing_extensions import assert_type + +# These do follow `__init__` overloads order: +# mypy and pyright have different opinions about this one: +# mypy raises: 'Need type annotation for "bad"' +# pyright is fine with it. +# bad = dict() +good: dict[str, str] = dict() +assert_type(good, Dict[str, str]) + +assert_type(dict(arg=1), Dict[str, int]) + +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + + +class KeysAndGetItem(Generic[_KT, _VT]): + data: dict[_KT, _VT] + + def __init__(self, data: dict[_KT, _VT]) -> None: + self.data = data + + def keys(self) -> Iterable[_KT]: + return self.data.keys() + + def __getitem__(self, __k: _KT) -> _VT: + return self.data[__k] + + +kt1: KeysAndGetItem[int, str] = KeysAndGetItem({0: ""}) +assert_type(dict(kt1), Dict[int, str]) +dict(kt1, arg="a") # type: ignore + +kt2: KeysAndGetItem[str, int] = KeysAndGetItem({"": 0}) +assert_type(dict(kt2, arg=1), Dict[str, int]) + + +def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str]: + return dict(x) + + +i1: Iterable[tuple[int, str]] = [(1, "a"), (2, "b")] +test_iterable_tuple_overload(i1) +dict(i1, arg="a") # type: ignore + +i2: Iterable[tuple[str, int]] = [("a", 1), ("b", 2)] +assert_type(dict(i2, arg=1), Dict[str, int]) + +i3: Iterable[str] = ["a.b"] +i4: Iterable[bytes] = [b"a.b"] +assert_type(dict(string.split(".") for string in i3), Dict[str, str]) +assert_type(dict(string.split(b".") for string in i4), Dict[bytes, bytes]) + +dict(["foo", "bar", "baz"]) # type: ignore +dict([b"foo", b"bar", b"baz"]) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py new file mode 100644 index 000000000000..e53cd12288a4 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py @@ -0,0 +1,323 @@ +from __future__ import annotations + +import sys +from typing import TypeVar +from typing_extensions import assert_type + +if sys.version_info >= (3, 11): + # This can be removed later, but right now Flake8 does not know + # about these two classes: + from builtins import BaseExceptionGroup, ExceptionGroup + + # BaseExceptionGroup + # ================== + # `BaseExceptionGroup` can work with `BaseException`: + beg = BaseExceptionGroup("x", [SystemExit(), SystemExit()]) + assert_type(beg, BaseExceptionGroup[SystemExit]) + assert_type(beg.exceptions, tuple[SystemExit | BaseExceptionGroup[SystemExit], ...]) + + # Covariance works: + _beg1: BaseExceptionGroup[BaseException] = beg + + # `BaseExceptionGroup` can work with `Exception`: + beg2 = BaseExceptionGroup("x", [ValueError()]) + # FIXME: this is not right, runtime returns `ExceptionGroup` instance instead, + # but I am unable to represent this with types right now. + assert_type(beg2, BaseExceptionGroup[ValueError]) + + # .subgroup() + # ----------- + + assert_type(beg.subgroup(KeyboardInterrupt), BaseExceptionGroup[KeyboardInterrupt] | None) + assert_type(beg.subgroup((KeyboardInterrupt,)), BaseExceptionGroup[KeyboardInterrupt] | None) + + def is_base_exc(exc: BaseException) -> bool: + return isinstance(exc, BaseException) + + def is_specific(exc: SystemExit | BaseExceptionGroup[SystemExit]) -> bool: + return isinstance(exc, SystemExit) + + # This one does not have `BaseExceptionGroup` part, + # this is why we treat as an error. + def is_system_exit(exc: SystemExit) -> bool: + return isinstance(exc, SystemExit) + + def unrelated_subgroup(exc: KeyboardInterrupt) -> bool: + return False + + assert_type(beg.subgroup(is_base_exc), BaseExceptionGroup[SystemExit] | None) + assert_type(beg.subgroup(is_specific), BaseExceptionGroup[SystemExit] | None) + beg.subgroup(is_system_exit) # type: ignore + beg.subgroup(unrelated_subgroup) # type: ignore + + # `Exception`` subgroup returns `ExceptionGroup`: + assert_type(beg.subgroup(ValueError), ExceptionGroup[ValueError] | None) + assert_type(beg.subgroup((ValueError,)), ExceptionGroup[ValueError] | None) + + # Callable are harder, we don't support cast to `ExceptionGroup` here. + # Because callables might return `True` the first time. And `BaseExceptionGroup` + # will stick, no matter what arguments are. + + def is_exception(exc: Exception) -> bool: + return isinstance(exc, Exception) + + def is_exception_or_beg(exc: Exception | BaseExceptionGroup[SystemExit]) -> bool: + return isinstance(exc, Exception) + + # This is an error because of the `Exception` argument type, + # while `SystemExit` is needed instead. + beg.subgroup(is_exception_or_beg) # type: ignore + + # This is an error, because `BaseExceptionGroup` is not an `Exception` + # subclass. It is required. + beg.subgroup(is_exception) # type: ignore + + # .split() + # -------- + + assert_type( + beg.split(KeyboardInterrupt), tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None] + ) + assert_type( + beg.split((KeyboardInterrupt,)), + tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None], + ) + assert_type( + beg.split(ValueError), # there are no `ValueError` items in there, but anyway + tuple[ExceptionGroup[ValueError] | None, BaseExceptionGroup[SystemExit] | None], + ) + + excs_to_split: list[ValueError | KeyError | SystemExit] = [ValueError(), KeyError(), SystemExit()] + to_split = BaseExceptionGroup("x", excs_to_split) + assert_type(to_split, BaseExceptionGroup[ValueError | KeyError | SystemExit]) + + # Ideally the first part should be `ExceptionGroup[ValueError]` (done) + # and the second part should be `BaseExceptionGroup[KeyError | SystemExit]`, + # but we cannot subtract type from a union. + # We also cannot change `BaseExceptionGroup` to `ExceptionGroup` even if needed + # in the second part here because of that. + assert_type( + to_split.split(ValueError), + tuple[ExceptionGroup[ValueError] | None, BaseExceptionGroup[ValueError | KeyError | SystemExit] | None], + ) + + def split_callable1(exc: ValueError | KeyError | SystemExit | BaseExceptionGroup[ValueError | KeyError | SystemExit]) -> bool: + return True + + assert_type( + to_split.split(split_callable1), # Concrete type is ok + tuple[ + BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, + BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, + ], + ) + assert_type( + to_split.split(is_base_exc), # Base class is ok + tuple[ + BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, + BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, + ], + ) + # `Exception` cannot be used: `BaseExceptionGroup` is not a subtype of it. + to_split.split(is_exception) # type: ignore + + # .derive() + # --------- + + assert_type(beg.derive([ValueError()]), ExceptionGroup[ValueError]) + assert_type(beg.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) + + # ExceptionGroup + # ============== + + # `ExceptionGroup` can work with `Exception`: + excs: list[ValueError | KeyError] = [ValueError(), KeyError()] + eg = ExceptionGroup("x", excs) + assert_type(eg, ExceptionGroup[ValueError | KeyError]) + assert_type(eg.exceptions, tuple[ValueError | KeyError | ExceptionGroup[ValueError | KeyError], ...]) + + # Covariance works: + _eg1: ExceptionGroup[Exception] = eg + + # `ExceptionGroup` cannot work with `BaseException`: + ExceptionGroup("x", [SystemExit()]) # type: ignore + + # .subgroup() + # ----------- + + # Our decision is to ban cases like:: + # + # >>> eg = ExceptionGroup('x', [ValueError()]) + # >>> eg.subgroup(BaseException) + # ExceptionGroup('e', [ValueError()]) + # + # are possible in runtime. + # We do it because, it does not make sense for all other base exception types. + # Supporting just `BaseException` looks like an overkill. + eg.subgroup(BaseException) # type: ignore + eg.subgroup((KeyboardInterrupt, SystemExit)) # type: ignore + + assert_type(eg.subgroup(Exception), ExceptionGroup[Exception] | None) + assert_type(eg.subgroup(ValueError), ExceptionGroup[ValueError] | None) + assert_type(eg.subgroup((ValueError,)), ExceptionGroup[ValueError] | None) + + def subgroup_eg1(exc: ValueError | KeyError | ExceptionGroup[ValueError | KeyError]) -> bool: + return True + + def subgroup_eg2(exc: ValueError | KeyError) -> bool: + return True + + assert_type(eg.subgroup(subgroup_eg1), ExceptionGroup[ValueError | KeyError] | None) + assert_type(eg.subgroup(is_exception), ExceptionGroup[ValueError | KeyError] | None) + assert_type(eg.subgroup(is_base_exc), ExceptionGroup[ValueError | KeyError] | None) + assert_type(eg.subgroup(is_base_exc), ExceptionGroup[ValueError | KeyError] | None) + + # Does not have `ExceptionGroup` part: + eg.subgroup(subgroup_eg2) # type: ignore + + # .split() + # -------- + + assert_type(eg.split(TypeError), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError | KeyError] | None]) + assert_type(eg.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError | KeyError] | None]) + assert_type( + eg.split(is_exception), tuple[ExceptionGroup[ValueError | KeyError] | None, ExceptionGroup[ValueError | KeyError] | None] + ) + assert_type( + eg.split(is_base_exc), + # is not converted, because `ExceptionGroup` cannot have + # direct `BaseException` subclasses inside. + tuple[ExceptionGroup[ValueError | KeyError] | None, ExceptionGroup[ValueError | KeyError] | None], + ) + + # It does not include `ExceptionGroup` itself, so it will fail: + def value_or_key_error(exc: ValueError | KeyError) -> bool: + return isinstance(exc, (ValueError, KeyError)) + + eg.split(value_or_key_error) # type: ignore + + # `ExceptionGroup` cannot have direct `BaseException` subclasses inside. + eg.split(BaseException) # type: ignore + eg.split((SystemExit, GeneratorExit)) # type: ignore + + # .derive() + # --------- + + assert_type(eg.derive([ValueError()]), ExceptionGroup[ValueError]) + assert_type(eg.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) + + # BaseExceptionGroup Custom Subclass + # ================================== + # In some cases `Self` type can be preserved in runtime, + # but it is impossible to express. That's why we always fallback to + # `BaseExceptionGroup` and `ExceptionGroup`. + + _BE = TypeVar("_BE", bound=BaseException) + + class CustomBaseGroup(BaseExceptionGroup[_BE]): ... + + cb1 = CustomBaseGroup("x", [SystemExit()]) + assert_type(cb1, CustomBaseGroup[SystemExit]) + cb2 = CustomBaseGroup("x", [ValueError()]) + assert_type(cb2, CustomBaseGroup[ValueError]) + + # .subgroup() + # ----------- + + assert_type(cb1.subgroup(KeyboardInterrupt), BaseExceptionGroup[KeyboardInterrupt] | None) + assert_type(cb2.subgroup((KeyboardInterrupt,)), BaseExceptionGroup[KeyboardInterrupt] | None) + + assert_type(cb1.subgroup(ValueError), ExceptionGroup[ValueError] | None) + assert_type(cb2.subgroup((KeyError,)), ExceptionGroup[KeyError] | None) + + def cb_subgroup1(exc: SystemExit | CustomBaseGroup[SystemExit]) -> bool: + return True + + def cb_subgroup2(exc: ValueError | CustomBaseGroup[ValueError]) -> bool: + return True + + assert_type(cb1.subgroup(cb_subgroup1), BaseExceptionGroup[SystemExit] | None) + assert_type(cb2.subgroup(cb_subgroup2), BaseExceptionGroup[ValueError] | None) + cb1.subgroup(cb_subgroup2) # type: ignore + cb2.subgroup(cb_subgroup1) # type: ignore + + # .split() + # -------- + + assert_type( + cb1.split(KeyboardInterrupt), tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None] + ) + assert_type(cb1.split(TypeError), tuple[ExceptionGroup[TypeError] | None, BaseExceptionGroup[SystemExit] | None]) + assert_type(cb2.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, BaseExceptionGroup[ValueError] | None]) + + def cb_split1(exc: SystemExit | CustomBaseGroup[SystemExit]) -> bool: + return True + + def cb_split2(exc: ValueError | CustomBaseGroup[ValueError]) -> bool: + return True + + assert_type(cb1.split(cb_split1), tuple[BaseExceptionGroup[SystemExit] | None, BaseExceptionGroup[SystemExit] | None]) + assert_type(cb2.split(cb_split2), tuple[BaseExceptionGroup[ValueError] | None, BaseExceptionGroup[ValueError] | None]) + cb1.split(cb_split2) # type: ignore + cb2.split(cb_split1) # type: ignore + + # .derive() + # --------- + + # Note, that `Self` type is not preserved in runtime. + assert_type(cb1.derive([ValueError()]), ExceptionGroup[ValueError]) + assert_type(cb1.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) + assert_type(cb2.derive([ValueError()]), ExceptionGroup[ValueError]) + assert_type(cb2.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) + + # ExceptionGroup Custom Subclass + # ============================== + + _E = TypeVar("_E", bound=Exception) + + class CustomGroup(ExceptionGroup[_E]): ... + + CustomGroup("x", [SystemExit()]) # type: ignore + cg1 = CustomGroup("x", [ValueError()]) + assert_type(cg1, CustomGroup[ValueError]) + + # .subgroup() + # ----------- + + cg1.subgroup(BaseException) # type: ignore + cg1.subgroup((KeyboardInterrupt, SystemExit)) # type: ignore + + assert_type(cg1.subgroup(ValueError), ExceptionGroup[ValueError] | None) + assert_type(cg1.subgroup((KeyError,)), ExceptionGroup[KeyError] | None) + + def cg_subgroup1(exc: ValueError | CustomGroup[ValueError]) -> bool: + return True + + def cg_subgroup2(exc: ValueError) -> bool: + return True + + assert_type(cg1.subgroup(cg_subgroup1), ExceptionGroup[ValueError] | None) + cg1.subgroup(cb_subgroup2) # type: ignore + + # .split() + # -------- + + assert_type(cg1.split(TypeError), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError] | None]) + assert_type(cg1.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError] | None]) + cg1.split(BaseException) # type: ignore + + def cg_split1(exc: ValueError | CustomGroup[ValueError]) -> bool: + return True + + def cg_split2(exc: ValueError) -> bool: + return True + + assert_type(cg1.split(cg_split1), tuple[ExceptionGroup[ValueError] | None, ExceptionGroup[ValueError] | None]) + cg1.split(cg_split2) # type: ignore + + # .derive() + # --------- + + # Note, that `Self` type is not preserved in runtime. + assert_type(cg1.derive([ValueError()]), ExceptionGroup[ValueError]) + assert_type(cg1.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py new file mode 100644 index 000000000000..3d609635377e --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from typing import Iterator +from typing_extensions import assert_type + + +class OldStyleIter: + def __getitem__(self, index: int) -> str: + return str(index) + + +for x in iter(OldStyleIter()): + assert_type(x, str) + +assert_type(iter(OldStyleIter()), Iterator[str]) +assert_type(next(iter(OldStyleIter())), str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py new file mode 100644 index 000000000000..4113f5c66182 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import List, Union +from typing_extensions import assert_type + + +# list.__add__ example from #8292 +class Foo: + def asd(self) -> int: + return 1 + + +class Bar: + def asd(self) -> int: + return 2 + + +combined = [Foo()] + [Bar()] +assert_type(combined, List[Union[Foo, Bar]]) +for item in combined: + assert_type(item.asd(), int) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py new file mode 100644 index 000000000000..60df1143f727 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing import Any + + +# The following should pass without error (see #6661): +class Diagnostic: + def __reduce__(self) -> str | tuple[Any, ...]: + res = super().__reduce__() + if isinstance(res, tuple) and len(res) >= 3: + res[2]["_info"] = 42 + + return res diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py new file mode 100644 index 000000000000..1f38710d6bea --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +from decimal import Decimal +from fractions import Fraction +from typing import Any, Literal +from typing_extensions import assert_type + +# See #7163 +assert_type(pow(1, 0), Literal[1]) +assert_type(1**0, Literal[1]) +assert_type(pow(1, 0, None), Literal[1]) + +# TODO: We don't have a good way of expressing the fact +# that passing 0 for the third argument will lead to an exception being raised +# (see discussion in #8566) +# +# assert_type(pow(2, 4, 0), NoReturn) + +assert_type(pow(2, 4), int) +assert_type(2**4, int) +assert_type(pow(4, 6, None), int) + +assert_type(pow(5, -7), float) +assert_type(5**-7, float) + +assert_type(pow(2, 4, 5), int) # pow(, , ) +assert_type(pow(2, 35, 3), int) # pow(, , ) + +assert_type(pow(2, 8.5), float) +assert_type(2**8.6, float) +assert_type(pow(2, 8.6, None), float) + +# TODO: Why does this pass pyright but not mypy?? +# assert_type((-2) ** 0.5, complex) + +assert_type(pow((-5), 8.42, None), complex) + +assert_type(pow(4.6, 8), float) +assert_type(4.6**8, float) +assert_type(pow(5.1, 4, None), float) + +assert_type(pow(complex(6), 6.2), complex) +assert_type(complex(6) ** 6.2, complex) +assert_type(pow(complex(9), 7.3, None), complex) + +assert_type(pow(Fraction(), 4, None), Fraction) +assert_type(Fraction() ** 4, Fraction) + +assert_type(pow(Fraction(3, 7), complex(1, 8)), complex) +assert_type(Fraction(3, 7) ** complex(1, 8), complex) + +assert_type(pow(complex(4, -8), Fraction(2, 3)), complex) +assert_type(complex(4, -8) ** Fraction(2, 3), complex) + +assert_type(pow(Decimal("1.0"), Decimal("1.6")), Decimal) +assert_type(Decimal("1.0") ** Decimal("1.6"), Decimal) + +assert_type(pow(Decimal("1.0"), Decimal("1.0"), Decimal("1.0")), Decimal) +assert_type(pow(Decimal("4.6"), 7, None), Decimal) +assert_type(Decimal("4.6") ** 7, Decimal) + +# These would ideally be more precise, but `Any` is acceptable +# They have to be `Any` due to the fact that type-checkers can't distinguish +# between positive and negative numbers for the second argument to `pow()` +# +# int for positive 2nd-arg, float otherwise +assert_type(pow(4, 65), Any) +assert_type(pow(2, -45), Any) +assert_type(pow(3, 57, None), Any) +assert_type(pow(67, 0.98, None), Any) +assert_type(87**7.32, Any) +# pow(, ) -> float +# pow(, ) -> complex +assert_type(pow(4.7, 7.4), Any) +assert_type(pow(-9.8, 8.3), Any) +assert_type(pow(-9.3, -88.2), Any) +assert_type(pow(8.2, -9.8), Any) +assert_type(pow(4.7, 9.2, None), Any) +# See #7046 -- float for a positive 1st arg, complex otherwise +assert_type((-95) ** 8.42, Any) + +# All of the following cases should fail a type-checker. +pow(1.9, 4, 6) # type: ignore +pow(4, 7, 4.32) # type: ignore +pow(6.2, 5.9, 73) # type: ignore +pow(complex(6), 6.2, 7) # type: ignore +pow(Fraction(), 5, 8) # type: ignore +Decimal("8.7") ** 3.14 # type: ignore + +# TODO: This fails at runtime, but currently passes mypy and pyright: +pow(Decimal("8.5"), 3.21) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py new file mode 100644 index 000000000000..2a43a57deb4e --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from collections.abc import Iterator +from typing import Generic, TypeVar +from typing_extensions import assert_type + +x: list[int] = [] +assert_type(list(reversed(x)), "list[int]") + + +class MyReversible: + def __iter__(self) -> Iterator[str]: + yield "blah" + + def __reversed__(self) -> Iterator[str]: + yield "blah" + + +assert_type(list(reversed(MyReversible())), "list[str]") + + +_T = TypeVar("_T") + + +class MyLenAndGetItem(Generic[_T]): + def __len__(self) -> int: + return 0 + + def __getitem__(self, item: int) -> _T: + raise KeyError + + +len_and_get_item: MyLenAndGetItem[int] = MyLenAndGetItem() +assert_type(list(reversed(len_and_get_item)), "list[int]") diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py new file mode 100644 index 000000000000..84081f3665b9 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from typing import overload +from typing_extensions import assert_type + + +class CustomIndex: + def __index__(self) -> int: + return 1 + + +# float: + +assert_type(round(5.5), int) +assert_type(round(5.5, None), int) +assert_type(round(5.5, 0), float) +assert_type(round(5.5, 1), float) +assert_type(round(5.5, 5), float) +assert_type(round(5.5, CustomIndex()), float) + +# int: + +assert_type(round(1), int) +assert_type(round(1, 1), int) +assert_type(round(1, None), int) +assert_type(round(1, CustomIndex()), int) + +# Protocols: + + +class WithCustomRound1: + def __round__(self) -> str: + return "a" + + +assert_type(round(WithCustomRound1()), str) +assert_type(round(WithCustomRound1(), None), str) +# Errors: +round(WithCustomRound1(), 1) # type: ignore +round(WithCustomRound1(), CustomIndex()) # type: ignore + + +class WithCustomRound2: + def __round__(self, digits: int) -> str: + return "a" + + +assert_type(round(WithCustomRound2(), 1), str) +assert_type(round(WithCustomRound2(), CustomIndex()), str) +# Errors: +round(WithCustomRound2(), None) # type: ignore +round(WithCustomRound2()) # type: ignore + + +class WithOverloadedRound: + @overload + def __round__(self, ndigits: None = ...) -> str: ... + + @overload + def __round__(self, ndigits: int) -> bytes: ... + + def __round__(self, ndigits: int | None = None) -> str | bytes: + return b"" if ndigits is None else "" + + +assert_type(round(WithOverloadedRound()), str) +assert_type(round(WithOverloadedRound(), None), str) +assert_type(round(WithOverloadedRound(), 1), bytes) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py new file mode 100644 index 000000000000..cda7eadbbe41 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from typing import Any, List, Literal, Union +from typing_extensions import assert_type + + +class Foo: + def __add__(self, other: Any) -> Foo: + return Foo() + + +class Bar: + def __radd__(self, other: Any) -> Bar: + return Bar() + + +class Baz: + def __add__(self, other: Any) -> Baz: + return Baz() + + def __radd__(self, other: Any) -> Baz: + return Baz() + + +literal_list: list[Literal[0, 1]] = [0, 1, 1] + +assert_type(sum([2, 4]), int) +assert_type(sum([3, 5], 4), int) + +assert_type(sum([True, False]), int) +assert_type(sum([True, False], True), int) +assert_type(sum(literal_list), int) + +assert_type(sum([["foo"], ["bar"]], ["baz"]), List[str]) + +assert_type(sum([Foo(), Foo()], Foo()), Foo) +assert_type(sum([Baz(), Baz()]), Union[Baz, Literal[0]]) + +# mypy and pyright infer the types differently for these, so we can't use assert_type +# Just test that no error is emitted for any of these +sum([("foo",), ("bar", "baz")], ()) # mypy: `tuple[str, ...]`; pyright: `tuple[()] | tuple[str] | tuple[str, str]` +sum([5.6, 3.2]) # mypy: `float`; pyright: `float | Literal[0]` +sum([2.5, 5.8], 5) # mypy: `float`; pyright: `float | int` + +# These all fail at runtime +sum("abcde") # type: ignore +sum([["foo"], ["bar"]]) # type: ignore +sum([("foo",), ("bar", "baz")]) # type: ignore +sum([Foo(), Foo()]) # type: ignore +sum([Bar(), Bar()], Bar()) # type: ignore +sum([Bar(), Bar()]) # type: ignore + +# TODO: these pass pyright with the current stubs, but mypy erroneously emits an error: +# sum([3, Fraction(7, 22), complex(8, 0), 9.83]) +# sum([3, Decimal('0.98')]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py new file mode 100644 index 000000000000..bc0d8db28389 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing import Tuple +from typing_extensions import assert_type + + +# Empty tuples, see #8275 +class TupleSub(Tuple[int, ...]): + pass + + +assert_type(TupleSub(), TupleSub) +assert_type(TupleSub([1, 2, 3]), TupleSub) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py b/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py new file mode 100644 index 000000000000..19e663ceeaaf --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +import codecs +from typing_extensions import assert_type + +assert_type(codecs.decode("x", "unicode-escape"), str) +assert_type(codecs.decode(b"x", "unicode-escape"), str) + +assert_type(codecs.decode(b"x", "utf-8"), str) +codecs.decode("x", "utf-8") # type: ignore + +assert_type(codecs.decode("ab", "hex"), bytes) +assert_type(codecs.decode(b"ab", "hex"), bytes) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py b/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py new file mode 100644 index 000000000000..962ec23c6b48 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from collections.abc import Callable, Iterator +from concurrent.futures import Future, ThreadPoolExecutor, as_completed +from typing_extensions import assert_type + + +class Parent: ... + + +class Child(Parent): ... + + +def check_as_completed_covariance() -> None: + with ThreadPoolExecutor() as executor: + f1 = executor.submit(lambda: Parent()) + f2 = executor.submit(lambda: Child()) + fs: list[Future[Parent] | Future[Child]] = [f1, f2] + assert_type(as_completed(fs), Iterator[Future[Parent]]) + for future in as_completed(fs): + assert_type(future.result(), Parent) + + +def check_future_invariance() -> None: + def execute_callback(callback: Callable[[], Parent], future: Future[Parent]) -> None: + future.set_result(callback()) + + fut: Future[Child] = Future() + execute_callback(lambda: Parent(), fut) # type: ignore + assert isinstance(fut.result(), Child) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py new file mode 100644 index 000000000000..648661bca856 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from contextlib import ExitStack +from typing_extensions import assert_type + + +# See issue #7961 +class Thing(ExitStack): + pass + + +stack = ExitStack() +thing = Thing() +assert_type(stack.enter_context(Thing()), Thing) +assert_type(thing.enter_context(ExitStack()), ExitStack) + +with stack as cm: + assert_type(cm, ExitStack) +with thing as cm2: + assert_type(cm2, Thing) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py b/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py new file mode 100644 index 000000000000..76ce8e1bd260 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import dataclasses as dc +from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Tuple, Type, Union +from typing_extensions import Annotated, assert_type + +if TYPE_CHECKING: + from _typeshed import DataclassInstance + + +@dc.dataclass +class Foo: + attr: str + + +assert_type(dc.fields(Foo), Tuple[dc.Field[Any], ...]) + +# Mypy correctly emits errors on these +# due to the fact it's a dataclass class, not an instance. +# Pyright, however, handles ClassVar members in protocols differently. +# See https://github.com/microsoft/pyright/issues/4339 +# +# dc.asdict(Foo) +# dc.astuple(Foo) +# dc.replace(Foo) + +# See #9723 for why we can't make this assertion +# if dc.is_dataclass(Foo): +# assert_type(Foo, Type[Foo]) + +f = Foo(attr="attr") + +assert_type(dc.fields(f), Tuple[dc.Field[Any], ...]) +assert_type(dc.asdict(f), Dict[str, Any]) +assert_type(dc.astuple(f), Tuple[Any, ...]) +assert_type(dc.replace(f, attr="new"), Foo) + +if dc.is_dataclass(f): + # The inferred type doesn't change + # if it's already known to be a subtype of _DataclassInstance + assert_type(f, Foo) + + +def check_other_isdataclass_overloads(x: type, y: object) -> None: + # TODO: pyright correctly emits an error on this, but mypy does not -- why? + # dc.fields(x) + + dc.fields(y) # type: ignore + + dc.asdict(x) # type: ignore + dc.asdict(y) # type: ignore + + dc.astuple(x) # type: ignore + dc.astuple(y) # type: ignore + + dc.replace(x) # type: ignore + dc.replace(y) # type: ignore + + if dc.is_dataclass(x): + assert_type(x, Type["DataclassInstance"]) + assert_type(dc.fields(x), Tuple[dc.Field[Any], ...]) + + # Mypy correctly emits an error on these due to the fact + # that it's a dataclass class, not a dataclass instance. + # Pyright, however, handles ClassVar members in protocols differently. + # See https://github.com/microsoft/pyright/issues/4339 + # + # dc.asdict(x) + # dc.astuple(x) + # dc.replace(x) + + if dc.is_dataclass(y): + assert_type(y, Union["DataclassInstance", Type["DataclassInstance"]]) + assert_type(dc.fields(y), Tuple[dc.Field[Any], ...]) + + # Mypy correctly emits an error on these due to the fact we don't know + # whether it's a dataclass class or a dataclass instance. + # Pyright, however, handles ClassVar members in protocols differently. + # See https://github.com/microsoft/pyright/issues/4339 + # + # dc.asdict(y) + # dc.astuple(y) + # dc.replace(y) + + if dc.is_dataclass(y) and not isinstance(y, type): + assert_type(y, "DataclassInstance") + assert_type(dc.fields(y), Tuple[dc.Field[Any], ...]) + assert_type(dc.asdict(y), Dict[str, Any]) + assert_type(dc.astuple(y), Tuple[Any, ...]) + dc.replace(y) + + +# Regression test for #11653 +D = dc.make_dataclass( + "D", [("a", Union[int, None]), "y", ("z", Annotated[FrozenSet[bytes], "metadata"], dc.field(default=frozenset({b"foo"})))] +) +# Check that it's inferred by the type checker as a class object of some kind +# (but don't assert the exact type that `D` is inferred as, +# in case a type checker decides to add some special-casing for +# `make_dataclass` in the future) +assert_type(D.__mro__, Tuple[type, ...]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py b/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py new file mode 100644 index 000000000000..4ea4947c811d --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import enum +import sys +from typing import Literal, Type +from typing_extensions import assert_type + +A = enum.Enum("A", "spam eggs bacon") +B = enum.Enum("B", ["spam", "eggs", "bacon"]) +C = enum.Enum("Bar", [("spam", 1), ("eggs", 2), ("bacon", 3)]) +D = enum.Enum("Bar", {"spam": 1, "eggs": 2}) + +assert_type(A, Type[A]) +assert_type(B, Type[B]) +assert_type(C, Type[C]) +assert_type(D, Type[D]) + + +class EnumOfTuples(enum.Enum): + X = 1, 2, 3 + Y = 4, 5, 6 + + +assert_type(EnumOfTuples((1, 2, 3)), EnumOfTuples) + +# TODO: ideally this test would pass: +# +# if sys.version_info >= (3, 12): +# assert_type(EnumOfTuples(1, 2, 3), EnumOfTuples) + + +if sys.version_info >= (3, 11): + + class Foo(enum.StrEnum): + X = enum.auto() + + assert_type(Foo.X, Literal[Foo.X]) + assert_type(Foo.X.value, str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py b/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py new file mode 100644 index 000000000000..dca572683f8d --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from functools import cached_property, wraps +from typing import Callable, TypeVar +from typing_extensions import ParamSpec, assert_type + +P = ParamSpec("P") +T_co = TypeVar("T_co", covariant=True) + + +def my_decorator(func: Callable[P, T_co]) -> Callable[P, T_co]: + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T_co: + print(args) + return func(*args, **kwargs) + + # verify that the wrapped function has all these attributes + wrapper.__annotations__ = func.__annotations__ + wrapper.__doc__ = func.__doc__ + wrapper.__module__ = func.__module__ + wrapper.__name__ = func.__name__ + wrapper.__qualname__ = func.__qualname__ + return wrapper + + +class A: + def __init__(self, x: int): + self.x = x + + @cached_property + def x(self) -> int: + return 0 + + +assert_type(A(x=1).x, int) + + +class B: + @cached_property + def x(self) -> int: + return 0 + + +def check_cached_property_settable(x: int) -> None: + b = B() + assert_type(b.x, int) + b.x = x + assert_type(b.x, int) + + +# https://github.com/python/typeshed/issues/10048 +class Parent: ... + + +class Child(Parent): ... + + +class X: + @cached_property + def some(self) -> Parent: + return Parent() + + +class Y(X): + @cached_property + def some(self) -> Child: # safe override + return Child() diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py new file mode 100644 index 000000000000..17eefdafc971 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import importlib.abc +import importlib.util +import pathlib +import sys +import zipfile +from collections.abc import Sequence +from importlib.machinery import ModuleSpec +from types import ModuleType +from typing_extensions import Self + +# Assert that some Path classes are Traversable. +if sys.version_info >= (3, 9): + + def traverse(t: importlib.abc.Traversable) -> None: + pass + + traverse(pathlib.Path()) + traverse(zipfile.Path("")) + + +class MetaFinder: + @classmethod + def find_spec(cls, fullname: str, path: Sequence[str] | None, target: ModuleType | None = None) -> ModuleSpec | None: + return None # simplified mock for demonstration purposes only + + +class PathFinder: + @classmethod + def path_hook(cls, path_entry: str) -> type[Self]: + return cls # simplified mock for demonstration purposes only + + @classmethod + def find_spec(cls, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: + return None # simplified mock for demonstration purposes only + + +class Loader: + @classmethod + def load_module(cls, fullname: str) -> ModuleType: + return ModuleType(fullname) + + +sys.meta_path.append(MetaFinder) +sys.path_hooks.append(PathFinder.path_hook) +importlib.util.spec_from_loader("xxxx42xxxx", Loader) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py new file mode 100644 index 000000000000..f1322e16c54f --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import sys +from _typeshed import StrPath +from os import PathLike +from pathlib import Path +from typing import Any +from zipfile import Path as ZipPath + +if sys.version_info >= (3, 10): + from importlib.metadata._meta import SimplePath + + # Simplified version of zipfile.Path + class MyPath: + @property + def parent(self) -> PathLike[str]: ... # undocumented + + def read_text(self, encoding: str | None = ..., errors: str | None = ...) -> str: ... + def joinpath(self, *other: StrPath) -> MyPath: ... + def __truediv__(self, add: StrPath) -> MyPath: ... + + if sys.version_info >= (3, 12): + + def takes_simple_path(p: SimplePath[Any]) -> None: ... + + else: + + def takes_simple_path(p: SimplePath) -> None: ... + + takes_simple_path(Path()) + takes_simple_path(ZipPath("")) + takes_simple_path(MyPath()) + takes_simple_path("some string") # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_io.py b/mypy/typeshed/stdlib/@tests/test_cases/check_io.py new file mode 100644 index 000000000000..abf84dd5a103 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_io.py @@ -0,0 +1,6 @@ +from gzip import GzipFile +from io import FileIO, TextIOWrapper + +TextIOWrapper(FileIO("")) +TextIOWrapper(FileIO(13)) +TextIOWrapper(GzipFile("")) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py b/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py new file mode 100644 index 000000000000..fe3d8eb16fd0 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import logging +import logging.handlers +import multiprocessing +import queue +from typing import Any + +# This pattern comes from the logging docs, and should therefore pass a type checker +# See https://docs.python.org/3/library/logging.html#logrecord-objects + +old_factory = logging.getLogRecordFactory() + + +def record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord: + record = old_factory(*args, **kwargs) + record.custom_attribute = 0xDECAFBAD + return record + + +logging.setLogRecordFactory(record_factory) + +# The logging docs say that QueueHandler and QueueListener can take "any queue-like object" +# We test that here (regression test for #10168) +logging.handlers.QueueHandler(queue.Queue()) +logging.handlers.QueueHandler(queue.SimpleQueue()) +logging.handlers.QueueHandler(multiprocessing.Queue()) +logging.handlers.QueueListener(queue.Queue()) +logging.handlers.QueueListener(queue.SimpleQueue()) +logging.handlers.QueueListener(multiprocessing.Queue()) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py b/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py new file mode 100644 index 000000000000..201f96c0c4c8 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from ctypes import c_char, c_float +from multiprocessing import Array, Value +from multiprocessing.sharedctypes import Synchronized, SynchronizedString +from typing_extensions import assert_type + +string = Array(c_char, 12) +assert_type(string, SynchronizedString) +assert_type(string.value, bytes) + +field = Value(c_float, 0.0) +assert_type(field, Synchronized[float]) +field.value = 1.2 diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py new file mode 100644 index 000000000000..0b52c3669d07 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from pathlib import Path, PureWindowsPath + +if Path("asdf") == Path("asdf"): + ... + +# https://github.com/python/typeshed/issues/10661 +# Provide a true positive error when comparing Path to str +# mypy should report a comparison-overlap error with --strict-equality, +# and pyright should report a reportUnnecessaryComparison error +if Path("asdf") == "asdf": # type: ignore + ... + +# Errors on comparison here are technically false positives. However, this comparison is a little +# interesting: it can never hold true on Posix, but could hold true on Windows. We should experiment +# with more accurate __new__, such that we only get an error for such comparisons on platforms +# where they can never hold true. +if PureWindowsPath("asdf") == Path("asdf"): # type: ignore + ... diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_re.py b/mypy/typeshed/stdlib/@tests/test_cases/check_re.py new file mode 100644 index 000000000000..b6ab2b0d59d2 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_re.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import mmap +import re +import typing as t +from typing_extensions import assert_type + + +def check_search(str_pat: re.Pattern[str], bytes_pat: re.Pattern[bytes]) -> None: + assert_type(str_pat.search("x"), t.Optional[t.Match[str]]) + assert_type(bytes_pat.search(b"x"), t.Optional[t.Match[bytes]]) + assert_type(bytes_pat.search(bytearray(b"x")), t.Optional[t.Match[bytes]]) + assert_type(bytes_pat.search(mmap.mmap(0, 10)), t.Optional[t.Match[bytes]]) + + +def check_search_with_AnyStr(pattern: re.Pattern[t.AnyStr], string: t.AnyStr) -> re.Match[t.AnyStr]: + """See issue #9591""" + match = pattern.search(string) + if match is None: + raise ValueError(f"'{string!r}' does not match {pattern!r}") + return match + + +def check_no_ReadableBuffer_false_negatives() -> None: + re.compile("foo").search(bytearray(b"foo")) # type: ignore + re.compile("foo").search(mmap.mmap(0, 10)) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py b/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py new file mode 100644 index 000000000000..3ec47ceccb90 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import sqlite3 +from typing_extensions import assert_type + + +class MyConnection(sqlite3.Connection): + pass + + +# Default return-type is Connection. +assert_type(sqlite3.connect(":memory:"), sqlite3.Connection) + +# Providing an alternate factory changes the return-type. +assert_type(sqlite3.connect(":memory:", factory=MyConnection), MyConnection) + +# Provides a true positive error. When checking the connect() function, +# mypy should report an arg-type error for the factory argument. +with sqlite3.connect(":memory:", factory=None) as con: # type: ignore + pass + +# The Connection class also accepts a `factory` arg but it does not affect +# the return-type. This use case is not idiomatic--connections should be +# established using the `connect()` function, not directly (as shown here). +assert_type(sqlite3.Connection(":memory:", factory=None), sqlite3.Connection) +assert_type(sqlite3.Connection(":memory:", factory=MyConnection), sqlite3.Connection) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py new file mode 100644 index 000000000000..54510a3d7626 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py @@ -0,0 +1,13 @@ +import tarfile + +with tarfile.open("test.tar.xz", "w:xz") as tar: + pass + +# Test with valid preset values +tarfile.open("test.tar.xz", "w:xz", preset=0) +tarfile.open("test.tar.xz", "w:xz", preset=5) +tarfile.open("test.tar.xz", "w:xz", preset=9) + +# Test with invalid preset values +tarfile.open("test.tar.xz", "w:xz", preset=-1) # type: ignore +tarfile.open("test.tar.xz", "w:xz", preset=10) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py new file mode 100644 index 000000000000..c259c192a140 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import io +import sys +from tempfile import TemporaryFile, _TemporaryFileWrapper +from typing_extensions import assert_type + +if sys.platform == "win32": + assert_type(TemporaryFile(), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile("w+"), _TemporaryFileWrapper[str]) + assert_type(TemporaryFile("w+b"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile("wb"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile("rb"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile("wb", 0), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile(mode="w+"), _TemporaryFileWrapper[str]) + assert_type(TemporaryFile(mode="w+b"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile(mode="wb"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile(mode="rb"), _TemporaryFileWrapper[bytes]) + assert_type(TemporaryFile(buffering=0), _TemporaryFileWrapper[bytes]) +else: + assert_type(TemporaryFile(), io.BufferedRandom) + assert_type(TemporaryFile("w+"), io.TextIOWrapper) + assert_type(TemporaryFile("w+b"), io.BufferedRandom) + assert_type(TemporaryFile("wb"), io.BufferedWriter) + assert_type(TemporaryFile("rb"), io.BufferedReader) + assert_type(TemporaryFile("wb", 0), io.FileIO) + assert_type(TemporaryFile(mode="w+"), io.TextIOWrapper) + assert_type(TemporaryFile(mode="w+b"), io.BufferedRandom) + assert_type(TemporaryFile(mode="wb"), io.BufferedWriter) + assert_type(TemporaryFile(mode="rb"), io.BufferedReader) + assert_type(TemporaryFile(buffering=0), io.FileIO) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py b/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py new file mode 100644 index 000000000000..eddfc2549a64 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +import _threading_local +import threading + +loc = threading.local() +loc.foo = 42 +del loc.foo +loc.baz = ["spam", "eggs"] +del loc.baz + +l2 = _threading_local.local() +l2.asdfasdf = 56 +del l2.asdfasdf diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py new file mode 100644 index 000000000000..befac6697519 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import tkinter +import traceback +import types + + +def custom_handler(exc: type[BaseException], val: BaseException, tb: types.TracebackType | None) -> None: + print("oh no") + + +root = tkinter.Tk() +root.report_callback_exception = traceback.print_exception +root.report_callback_exception = custom_handler + + +def foo(x: int, y: str) -> None: + pass + + +root.after(1000, foo, 10, "lol") +root.after(1000, foo, 10, 10) # type: ignore + + +# Font size must be integer +label = tkinter.Label() +label.config(font=("", 12)) +label.config(font=("", 12.34)) # type: ignore +label.config(font=("", 12, "bold")) +label.config(font=("", 12.34, "bold")) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py b/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py new file mode 100644 index 000000000000..40c6efaa8ca0 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import unittest +from collections.abc import Iterator, Mapping +from datetime import datetime, timedelta +from decimal import Decimal +from fractions import Fraction +from typing import TypedDict +from typing_extensions import assert_type +from unittest.mock import MagicMock, Mock, patch + +case = unittest.TestCase() + +### +# Tests for assertAlmostEqual +### + +case.assertAlmostEqual(1, 2.4) +case.assertAlmostEqual(2.4, 2.41) +case.assertAlmostEqual(Fraction(49, 50), Fraction(48, 50)) +case.assertAlmostEqual(3.14, complex(5, 6)) +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) +case.assertAlmostEqual(Decimal("1.1"), Decimal("1.11")) +case.assertAlmostEqual(2.4, 2.41, places=8) +case.assertAlmostEqual(2.4, 2.41, delta=0.02) +case.assertAlmostEqual(2.4, 2.41, None, "foo", 0.02) + +case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore +case.assertAlmostEqual("foo", "bar") # type: ignore +case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore +case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore + +### +# Tests for assertNotAlmostEqual +### + +case.assertAlmostEqual(1, 2.4) +case.assertNotAlmostEqual(Fraction(49, 50), Fraction(48, 50)) +case.assertAlmostEqual(3.14, complex(5, 6)) +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) + +case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore +case.assertNotAlmostEqual("foo", "bar") # type: ignore +case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore +case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore +case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore + +### +# Tests for assertGreater +### + + +class Spam: + def __lt__(self, other: object) -> bool: + return True + + +class Eggs: + def __gt__(self, other: object) -> bool: + return True + + +class Ham: + def __lt__(self, other: Ham) -> bool: + if not isinstance(other, Ham): + return NotImplemented + return True + + +class Bacon: + def __gt__(self, other: Bacon) -> bool: + if not isinstance(other, Bacon): + return NotImplemented + return True + + +case.assertGreater(5.8, 3) +case.assertGreater(Decimal("4.5"), Fraction(3, 2)) +case.assertGreater(Fraction(3, 2), 0.9) +case.assertGreater(Eggs(), object()) +case.assertGreater(object(), Spam()) +case.assertGreater(Ham(), Ham()) +case.assertGreater(Bacon(), Bacon()) + +case.assertGreater(object(), object()) # type: ignore +case.assertGreater(datetime(1999, 1, 2), 1) # type: ignore +case.assertGreater(Spam(), Eggs()) # type: ignore +case.assertGreater(Ham(), Bacon()) # type: ignore +case.assertGreater(Bacon(), Ham()) # type: ignore + + +### +# Tests for assertDictEqual +### + + +class TD1(TypedDict): + x: int + y: str + + +class TD2(TypedDict): + a: bool + b: bool + + +class MyMapping(Mapping[str, int]): + def __getitem__(self, __key: str) -> int: + return 42 + + def __iter__(self) -> Iterator[str]: + return iter([]) + + def __len__(self) -> int: + return 0 + + +td1: TD1 = {"x": 1, "y": "foo"} +td2: TD2 = {"a": True, "b": False} +m = MyMapping() + +case.assertDictEqual({}, {}) +case.assertDictEqual({"x": 1, "y": 2}, {"x": 1, "y": 2}) +case.assertDictEqual({"x": 1, "y": "foo"}, {"y": "foo", "x": 1}) +case.assertDictEqual({"x": 1}, {}) +case.assertDictEqual({}, {"x": 1}) +case.assertDictEqual({1: "x"}, {"y": 222}) +case.assertDictEqual({1: "x"}, td1) +case.assertDictEqual(td1, {1: "x"}) +case.assertDictEqual(td1, td2) + +case.assertDictEqual(1, {}) # type: ignore +case.assertDictEqual({}, 1) # type: ignore + +# These should fail, but don't due to TypedDict limitations: +# case.assertDictEqual(m, {"": 0}) # xtype: ignore +# case.assertDictEqual({"": 0}, m) # xtype: ignore + +### +# Tests for mock.patch +### + + +@patch("sys.exit") +def f_default_new(i: int, mock: MagicMock) -> str: + return "asdf" + + +@patch("sys.exit", new=42) +def f_explicit_new(i: int) -> str: + return "asdf" + + +assert_type(f_default_new(1), str) +f_default_new("a") # Not an error due to ParamSpec limitations +assert_type(f_explicit_new(1), str) +f_explicit_new("a") # type: ignore[arg-type] + + +@patch("sys.exit", new=Mock()) +class TestXYZ(unittest.TestCase): + attr: int = 5 + + @staticmethod + def method() -> int: + return 123 + + +assert_type(TestXYZ.attr, int) +assert_type(TestXYZ.method(), int) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py b/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py new file mode 100644 index 000000000000..b485dac8dc29 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import sys +from typing_extensions import assert_type +from xml.dom.minidom import Document + +document = Document() + +assert_type(document.toxml(), str) +assert_type(document.toxml(encoding=None), str) +assert_type(document.toxml(encoding="UTF8"), bytes) +assert_type(document.toxml("UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toxml(standalone=True), str) + assert_type(document.toxml("UTF8", True), bytes) + assert_type(document.toxml(encoding="UTF8", standalone=True), bytes) + + +# Because toprettyxml can mix positional and keyword variants of the "encoding" argument, which +# determines the return type, the proper stub typing isn't immediately obvious. This is a basic +# brute-force sanity check. +# Test cases like toxml +assert_type(document.toprettyxml(), str) +assert_type(document.toprettyxml(encoding=None), str) +assert_type(document.toprettyxml(encoding="UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toprettyxml(standalone=True), str) + assert_type(document.toprettyxml(encoding="UTF8", standalone=True), bytes) +# Test cases unique to toprettyxml +assert_type(document.toprettyxml(" "), str) +assert_type(document.toprettyxml(" ", "\r\n"), str) +assert_type(document.toprettyxml(" ", "\r\n", "UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toprettyxml(" ", "\r\n", "UTF8", True), bytes) + assert_type(document.toprettyxml(" ", "\r\n", standalone=True), str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py b/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py new file mode 100644 index 000000000000..9fe5ec8076ce --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py @@ -0,0 +1,69 @@ +""" +Tests for `defaultdict.__or__` and `defaultdict.__ror__`. +These methods were only added in py39. +""" + +from __future__ import annotations + +import os +import sys +from collections import defaultdict +from typing import Mapping, TypeVar, Union +from typing_extensions import Self, assert_type + +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") + + +if sys.version_info >= (3, 9): + + class CustomDefaultDictSubclass(defaultdict[_KT, _VT]): + pass + + class CustomMappingWithDunderOr(Mapping[_KT, _VT]): + def __or__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: + return {} + + def __ror__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: + return {} + + def __ior__(self, other: Mapping[_KT, _VT]) -> Self: + return self + + def test_defaultdict_dot_or( + a: defaultdict[int, int], + b: CustomDefaultDictSubclass[int, int], + c: defaultdict[str, str], + d: Mapping[int, int], + e: CustomMappingWithDunderOr[str, str], + ) -> None: + assert_type(a | b, defaultdict[int, int]) + + # In contrast to `dict.__or__`, `defaultdict.__or__` returns `Self` if called on a subclass of `defaultdict`: + assert_type(b | a, CustomDefaultDictSubclass[int, int]) + + assert_type(a | c, defaultdict[Union[int, str], Union[int, str]]) + + # arbitrary mappings are not accepted by `defaultdict.__or__`; + # it has to be a subclass of `dict` + a | d # type: ignore + + # but Mappings such as `os._Environ` or `CustomMappingWithDunderOr`, + # which define `__ror__` methods that accept `dict`, are fine + # (`os._Environ.__(r)or__` always returns `dict`, even if a `defaultdict` is passed): + assert_type(a | os.environ, dict[Union[str, int], Union[str, int]]) + assert_type(os.environ | a, dict[Union[str, int], Union[str, int]]) + + assert_type(c | os.environ, dict[str, str]) + assert_type(c | e, dict[str, str]) + + assert_type(os.environ | c, dict[str, str]) + assert_type(e | c, dict[str, str]) + + e |= c + e |= a # type: ignore + + # TODO: this test passes mypy, but fails pyright for some reason: + # c |= e + + c |= a # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py b/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py new file mode 100644 index 000000000000..a9b43e23fb27 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py @@ -0,0 +1,6 @@ +from email.headerregistry import Address +from email.message import EmailMessage + +msg = EmailMessage() +msg["To"] = "receiver@example.com" +msg["From"] = Address("Sender Name", "sender", "example.com") diff --git a/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py b/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py new file mode 100644 index 000000000000..c45ffee28cee --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py @@ -0,0 +1,410 @@ +"""Type-annotated versions of the recipes from the itertools docs. + +These are all meant to be examples of idiomatic itertools usage, +so they should all type-check without error. +""" + +from __future__ import annotations + +import collections +import math +import operator +import sys +from itertools import chain, combinations, count, cycle, filterfalse, groupby, islice, product, repeat, starmap, tee, zip_longest +from typing import ( + Any, + Callable, + Collection, + Hashable, + Iterable, + Iterator, + Literal, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) +from typing_extensions import TypeAlias, TypeVarTuple, Unpack + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_HashableT = TypeVar("_HashableT", bound=Hashable) +_Ts = TypeVarTuple("_Ts") + + +def take(n: int, iterable: Iterable[_T]) -> list[_T]: + "Return first n items of the iterable as a list" + return list(islice(iterable, n)) + + +# Note: the itertools docs uses the parameter name "iterator", +# but the function actually accepts any iterable +# as its second argument +def prepend(value: _T1, iterator: Iterable[_T2]) -> Iterator[_T1 | _T2]: + "Prepend a single value in front of an iterator" + # prepend(1, [2, 3, 4]) --> 1 2 3 4 + return chain([value], iterator) + + +def tabulate(function: Callable[[int], _T], start: int = 0) -> Iterator[_T]: + "Return function(0), function(1), ..." + return map(function, count(start)) + + +def repeatfunc(func: Callable[[Unpack[_Ts]], _T], times: int | None = None, *args: Unpack[_Ts]) -> Iterator[_T]: + """Repeat calls to func with specified arguments. + + Example: repeatfunc(random.random) + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def flatten(list_of_lists: Iterable[Iterable[_T]]) -> Iterator[_T]: + "Flatten one level of nesting" + return chain.from_iterable(list_of_lists) + + +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: + "Returns the sequence elements n times" + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: + "Return an iterator over the last n items" + # tail(3, 'ABCDEFG') --> E F G + return iter(collections.deque(iterable, maxlen=n)) + + +# This function *accepts* any iterable, +# but it only *makes sense* to use it with an iterator +def consume(iterator: Iterator[object], n: int | None = None) -> None: + "Advance the iterator n-steps ahead. If n is None, consume entirely." + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + collections.deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +@overload +def nth(iterable: Iterable[_T], n: int, default: None = None) -> _T | None: ... + + +@overload +def nth(iterable: Iterable[_T], n: int, default: _T1) -> _T | _T1: ... + + +def nth(iterable: Iterable[object], n: int, default: object = None) -> object: + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +@overload +def quantify(iterable: Iterable[object]) -> int: ... + + +@overload +def quantify(iterable: Iterable[_T], pred: Callable[[_T], bool]) -> int: ... + + +def quantify(iterable: Iterable[object], pred: Callable[[Any], bool] = bool) -> int: + "Given a predicate that returns True or False, count the True results." + return sum(map(pred, iterable)) + + +@overload +def first_true( + iterable: Iterable[_T], default: Literal[False] = False, pred: Callable[[_T], bool] | None = None +) -> _T | Literal[False]: ... + + +@overload +def first_true(iterable: Iterable[_T], default: _T1, pred: Callable[[_T], bool] | None = None) -> _T | _T1: ... + + +def first_true(iterable: Iterable[object], default: object = False, pred: Callable[[Any], bool] | None = None) -> object: + """Returns the first true value in the iterable. + If no true value is found, returns *default* + If *pred* is not None, returns the first item + for which pred(item) is true. + """ + # first_true([a,b,c], x) --> a or b or c or x + # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x + return next(filter(pred, iterable), default) + + +_ExceptionOrExceptionTuple: TypeAlias = Union[Type[BaseException], Tuple[Type[BaseException], ...]] + + +@overload +def iter_except(func: Callable[[], _T], exception: _ExceptionOrExceptionTuple, first: None = None) -> Iterator[_T]: ... + + +@overload +def iter_except( + func: Callable[[], _T], exception: _ExceptionOrExceptionTuple, first: Callable[[], _T1] +) -> Iterator[_T | _T1]: ... + + +def iter_except( + func: Callable[[], object], exception: _ExceptionOrExceptionTuple, first: Callable[[], object] | None = None +) -> Iterator[object]: + """Call a function repeatedly until an exception is raised. + Converts a call-until-exception interface to an iterator interface. + Like builtins.iter(func, sentinel) but uses an exception instead + of a sentinel to end the loop. + Examples: + iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator + iter_except(d.popitem, KeyError) # non-blocking dict iterator + iter_except(d.popleft, IndexError) # non-blocking deque iterator + iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue + iter_except(s.pop, KeyError) # non-blocking set iterator + """ + try: + if first is not None: + yield first() # For database APIs needing an initial cast to db.first() + while True: + yield func() + except exception: + pass + + +def sliding_window(iterable: Iterable[_T], n: int) -> Iterator[tuple[_T, ...]]: + # sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG + it = iter(iterable) + window = collections.deque(islice(it, n - 1), maxlen=n) + for x in it: + window.append(x) + yield tuple(window) + + +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: + "roundrobin('ABC', 'D', 'EF') --> A D E B F C" + # Recipe credited to George Sakkis + num_active = len(iterables) + nexts: Iterator[Callable[[], _T]] = cycle(iter(it).__next__ for it in iterables) + while num_active: + try: + for next in nexts: + yield next() + except StopIteration: + # Remove the iterator we just exhausted from the cycle. + num_active -= 1 + nexts = cycle(islice(nexts, num_active)) + + +def partition(pred: Callable[[_T], bool], iterable: Iterable[_T]) -> tuple[Iterator[_T], Iterator[_T]]: + """Partition entries into false entries and true entries. + If *pred* is slow, consider wrapping it with functools.lru_cache(). + """ + # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) + + +def subslices(seq: Sequence[_T]) -> Iterator[Sequence[_T]]: + "Return all contiguous non-empty subslices of a sequence" + # subslices('ABCD') --> A AB ABC ABCD B BC BCD C CD D + slices = starmap(slice, combinations(range(len(seq) + 1), 2)) + return map(operator.getitem, repeat(seq), slices) + + +def before_and_after(predicate: Callable[[_T], bool], it: Iterable[_T]) -> tuple[Iterator[_T], Iterator[_T]]: + """Variant of takewhile() that allows complete + access to the remainder of the iterator. + >>> it = iter('ABCdEfGhI') + >>> all_upper, remainder = before_and_after(str.isupper, it) + >>> ''.join(all_upper) + 'ABC' + >>> ''.join(remainder) # takewhile() would lose the 'd' + 'dEfGhI' + Note that the first iterator must be fully + consumed before the second iterator can + generate valid results. + """ + it = iter(it) + transition: list[_T] = [] + + def true_iterator() -> Iterator[_T]: + for elem in it: + if predicate(elem): + yield elem + else: + transition.append(elem) + return + + def remainder_iterator() -> Iterator[_T]: + yield from transition + yield from it + + return true_iterator(), remainder_iterator() + + +@overload +def unique_everseen(iterable: Iterable[_HashableT], key: None = None) -> Iterator[_HashableT]: ... + + +@overload +def unique_everseen(iterable: Iterable[_T], key: Callable[[_T], Hashable]) -> Iterator[_T]: ... + + +def unique_everseen(iterable: Iterable[_T], key: Callable[[_T], Hashable] | None = None) -> Iterator[_T]: + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBcCAD', str.lower) --> A B c D + seen: set[Hashable] = set() + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen.add(element) + yield element + # For order preserving deduplication, + # a faster but non-lazy solution is: + # yield from dict.fromkeys(iterable) + else: + for element in iterable: + k = key(element) + if k not in seen: + seen.add(k) + yield element + # For use cases that allow the last matching element to be returned, + # a faster but non-lazy solution is: + # t1, t2 = tee(iterable) + # yield from dict(zip(map(key, t1), t2)).values() + + +# Slightly adapted from the docs recipe; a one-liner was a bit much for pyright +def unique_justseen(iterable: Iterable[_T], key: Callable[[_T], bool] | None = None) -> Iterator[_T]: + "List unique elements, preserving order. Remember only the element just seen." + # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B + # unique_justseen('ABBcCAD', str.lower) --> A B c A D + g: groupby[_T | bool, _T] = groupby(iterable, key) + return map(next, map(operator.itemgetter(1), g)) + + +def powerset(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: + "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def polynomial_derivative(coefficients: Sequence[float]) -> list[float]: + """Compute the first derivative of a polynomial. + f(x) = x³ -4x² -17x + 60 + f'(x) = 3x² -8x -17 + """ + # polynomial_derivative([1, -4, -17, 60]) -> [3, -8, -17] + n = len(coefficients) + powers = reversed(range(1, n)) + return list(map(operator.mul, coefficients, powers)) + + +def nth_combination(iterable: Iterable[_T], r: int, index: int) -> tuple[_T, ...]: + "Equivalent to list(combinations(iterable, r))[index]" + pool = tuple(iterable) + n = len(pool) + c = math.comb(n, r) + if index < 0: + index += c + if index < 0 or index >= c: + raise IndexError + result: list[_T] = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + return tuple(result) + + +if sys.version_info >= (3, 10): + + @overload + def grouper( + iterable: Iterable[_T], n: int, *, incomplete: Literal["fill"] = "fill", fillvalue: None = None + ) -> Iterator[tuple[_T | None, ...]]: ... + + @overload + def grouper( + iterable: Iterable[_T], n: int, *, incomplete: Literal["fill"] = "fill", fillvalue: _T1 + ) -> Iterator[tuple[_T | _T1, ...]]: ... + + @overload + def grouper( + iterable: Iterable[_T], n: int, *, incomplete: Literal["strict", "ignore"], fillvalue: None = None + ) -> Iterator[tuple[_T, ...]]: ... + + def grouper( + iterable: Iterable[object], n: int, *, incomplete: Literal["fill", "strict", "ignore"] = "fill", fillvalue: object = None + ) -> Iterator[tuple[object, ...]]: + "Collect data into non-overlapping fixed-length chunks or blocks" + # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx + # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError + # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF + args = [iter(iterable)] * n + if incomplete == "fill": + return zip_longest(*args, fillvalue=fillvalue) + if incomplete == "strict": + return zip(*args, strict=True) + if incomplete == "ignore": + return zip(*args) + else: + raise ValueError("Expected fill, strict, or ignore") + + def transpose(it: Iterable[Iterable[_T]]) -> Iterator[tuple[_T, ...]]: + "Swap the rows and columns of the input." + # transpose([(1, 2, 3), (11, 22, 33)]) --> (1, 11) (2, 22) (3, 33) + return zip(*it, strict=True) + + +if sys.version_info >= (3, 12): + from itertools import batched + + def sum_of_squares(it: Iterable[float]) -> float: + "Add up the squares of the input values." + # sum_of_squares([10, 20, 30]) -> 1400 + return math.sumprod(*tee(it)) + + def convolve(signal: Iterable[float], kernel: Iterable[float]) -> Iterator[float]: + """Discrete linear convolution of two iterables. + The kernel is fully consumed before the calculations begin. + The signal is consumed lazily and can be infinite. + Convolutions are mathematically commutative. + If the signal and kernel are swapped, + the output will be the same. + Article: https://betterexplained.com/articles/intuitive-convolution/ + Video: https://www.youtube.com/watch?v=KuXjwB4LzSA + """ + # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) + # convolve(data, [1/2, 0, -1/2]) --> 1st derivative estimate + # convolve(data, [1, -2, 1]) --> 2nd derivative estimate + kernel = tuple(kernel)[::-1] + n = len(kernel) + padded_signal = chain(repeat(0, n - 1), signal, repeat(0, n - 1)) + windowed_signal = sliding_window(padded_signal, n) + return map(math.sumprod, repeat(kernel), windowed_signal) + + def polynomial_eval(coefficients: Sequence[float], x: float) -> float: + """Evaluate a polynomial at a specific value. + Computes with better numeric stability than Horner's method. + """ + # Evaluate x³ -4x² -17x + 60 at x = 2.5 + # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125 + n = len(coefficients) + if not n: + return type(x)(0) + powers = map(pow, repeat(x), reversed(range(n))) + return math.sumprod(coefficients, powers) + + def matmul(m1: Sequence[Collection[float]], m2: Sequence[Collection[float]]) -> Iterator[tuple[float, ...]]: + "Multiply two matrices." + # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) --> (49, 80), (41, 60) + n = len(m2[0]) + return batched(starmap(math.sumprod, product(m1, transpose(m2))), n) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py new file mode 100644 index 000000000000..10a33ffb83d5 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from typing import Any, Union +from typing_extensions import assert_type + + +def check_setdefault_method() -> None: + d: dict[int, str] = {} + d2: dict[int, str | None] = {} + d3: dict[int, Any] = {} + + d.setdefault(1) # type: ignore + assert_type(d.setdefault(1, "x"), str) + assert_type(d2.setdefault(1), Union[str, None]) + assert_type(d2.setdefault(1, None), Union[str, None]) + assert_type(d2.setdefault(1, "x"), Union[str, None]) + assert_type(d3.setdefault(1), Union[Any, None]) + assert_type(d3.setdefault(1, "x"), Any) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py new file mode 100644 index 000000000000..44eb548e04a9 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py @@ -0,0 +1,14 @@ +# pyright: reportWildcardImportFromLibrary=false +""" +This tests that star imports work when using "all += " syntax. +""" +from __future__ import annotations + +import sys +from typing import * +from zipfile import * + +if sys.version_info >= (3, 9): + x: Annotated[int, 42] + +p: Path diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py new file mode 100644 index 000000000000..34c5631aeb1a --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import typing as t + +KT = t.TypeVar("KT") + + +class MyKeysView(t.KeysView[KT]): + pass + + +d: dict[t.Any, t.Any] = {} +dict_keys = type(d.keys()) + +# This should not cause an error like `Member "register" is unknown`: +MyKeysView.register(dict_keys) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py new file mode 100644 index 000000000000..67f16dc91765 --- /dev/null +++ b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import mmap +from typing import IO, AnyStr + + +def check_write(io_bytes: IO[bytes], io_str: IO[str], io_anystr: IO[AnyStr], any_str: AnyStr, buf: mmap.mmap) -> None: + io_bytes.write(b"") + io_bytes.write(buf) + io_bytes.write("") # type: ignore + io_bytes.write(any_str) # type: ignore + + io_str.write(b"") # type: ignore + io_str.write(buf) # type: ignore + io_str.write("") + io_str.write(any_str) # type: ignore + + io_anystr.write(b"") # type: ignore + io_anystr.write(buf) # type: ignore + io_anystr.write("") # type: ignore + io_anystr.write(any_str) diff --git a/mypy/typeshed/stdlib/_typeshed/importlib.pyi b/mypy/typeshed/stdlib/_typeshed/importlib.pyi new file mode 100644 index 000000000000..a4e56cdaff62 --- /dev/null +++ b/mypy/typeshed/stdlib/_typeshed/importlib.pyi @@ -0,0 +1,18 @@ +# Implicit protocols used in importlib. +# We intentionally omit deprecated and optional methods. + +from collections.abc import Sequence +from importlib.machinery import ModuleSpec +from types import ModuleType +from typing import Protocol + +__all__ = ["LoaderProtocol", "MetaPathFinderProtocol", "PathEntryFinderProtocol"] + +class LoaderProtocol(Protocol): + def load_module(self, fullname: str, /) -> ModuleType: ... + +class MetaPathFinderProtocol(Protocol): + def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ... + +class PathEntryFinderProtocol(Protocol): + def find_spec(self, fullname: str, target: ModuleType | None = ..., /) -> ModuleSpec | None: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 4ab325b5baa7..2525c3642a6f 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -181,82 +181,172 @@ class NodeTransformer(NodeVisitor): _T = _TypeVar("_T", bound=AST) -@overload -def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", - mode: Literal["exec"] = "exec", - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> Module: ... -@overload -def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["eval"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> Expression: ... -@overload -def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["func_type"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> FunctionType: ... -@overload -def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], - mode: Literal["single"], - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> Interactive: ... -@overload -def parse( - source: str | ReadableBuffer, - *, - mode: Literal["eval"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> Expression: ... -@overload -def parse( - source: str | ReadableBuffer, - *, - mode: Literal["func_type"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> FunctionType: ... -@overload -def parse( - source: str | ReadableBuffer, - *, - mode: Literal["single"], - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> Interactive: ... -@overload -def parse( - source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", - mode: str = "exec", - *, - type_comments: bool = False, - feature_version: None | int | tuple[int, int] = None, -) -> AST: ... +if sys.version_info >= (3, 13): + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: Literal["exec"] = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> Module: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["eval"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> Expression: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["func_type"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> FunctionType: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["single"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> Interactive: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["eval"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> Expression: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["func_type"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> FunctionType: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["single"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> Interactive: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: str = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> AST: ... + +else: + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: Literal["exec"] = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> Module: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["eval"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> Expression: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["func_type"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> FunctionType: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any], + mode: Literal["single"], + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> Interactive: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["eval"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> Expression: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["func_type"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> FunctionType: ... + @overload + def parse( + source: str | ReadableBuffer, + *, + mode: Literal["single"], + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> Interactive: ... + @overload + def parse( + source: str | ReadableBuffer, + filename: str | ReadableBuffer | os.PathLike[Any] = "", + mode: str = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> AST: ... if sys.version_info >= (3, 9): def unparse(ast_obj: AST) -> str: ... def copy_location(new_node: _T, old_node: AST) -> _T: ... -if sys.version_info >= (3, 9): +if sys.version_info >= (3, 13): + def dump( + node: AST, + annotate_fields: bool = True, + include_attributes: bool = False, + *, + indent: int | str | None = None, + show_empty: bool = False, + ) -> str: ... + +elif sys.version_info >= (3, 9): def dump( node: AST, annotate_fields: bool = True, include_attributes: bool = False, *, indent: int | str | None = None ) -> str: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 9e56c5430c52..4c47a0736e2e 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -31,7 +31,7 @@ from _typeshed import ( ) from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper -from types import CodeType, TracebackType, _Cell +from types import CellType, CodeType, TracebackType # mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} are imported from collections.abc in builtins.pyi from typing import ( # noqa: Y022 @@ -863,7 +863,7 @@ class tuple(Sequence[_T_co]): class function: # Make sure this class definition stays roughly in line with `types.FunctionType` @property - def __closure__(self) -> tuple[_Cell, ...] | None: ... + def __closure__(self) -> tuple[CellType, ...] | None: ... __code__: CodeType __defaults__: tuple[Any, ...] | None __dict__: dict[str, Any] @@ -1245,7 +1245,7 @@ if sys.version_info >= (3, 11): locals: Mapping[str, object] | None = None, /, *, - closure: tuple[_Cell, ...] | None = None, + closure: tuple[CellType, ...] | None = None, ) -> None: ... else: @@ -1706,7 +1706,7 @@ def __import__( fromlist: Sequence[str] = (), level: int = 0, ) -> types.ModuleType: ... -def __build_class__(func: Callable[[], _Cell | Any], name: str, /, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... +def __build_class__(func: Callable[[], CellType | Any], name: str, /, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... if sys.version_info >= (3, 10): from types import EllipsisType diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index 9c6989a1c151..f414763e02a6 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -1,3 +1,5 @@ +import sys +from _typeshed import StrOrBytesPath from collections.abc import Iterator, MutableMapping from types import TracebackType from typing import Literal @@ -91,5 +93,10 @@ class _error(Exception): ... error: tuple[type[_error], type[OSError]] -def whichdb(filename: str) -> str | None: ... -def open(file: str, flag: _TFlags = "r", mode: int = 0o666) -> _Database: ... +if sys.version_info >= (3, 11): + def whichdb(filename: StrOrBytesPath) -> str | None: ... + def open(file: StrOrBytesPath, flag: _TFlags = "r", mode: int = 0o666) -> _Database: ... + +else: + def whichdb(filename: str) -> str | None: ... + def open(file: str, flag: _TFlags = "r", mode: int = 0o666) -> _Database: ... diff --git a/mypy/typeshed/stdlib/dbm/dumb.pyi b/mypy/typeshed/stdlib/dbm/dumb.pyi index 1fc68cf71f9d..1c0b7756f292 100644 --- a/mypy/typeshed/stdlib/dbm/dumb.pyi +++ b/mypy/typeshed/stdlib/dbm/dumb.pyi @@ -1,3 +1,5 @@ +import sys +from _typeshed import StrOrBytesPath from collections.abc import Iterator, MutableMapping from types import TracebackType from typing_extensions import Self, TypeAlias @@ -28,4 +30,8 @@ class _Database(MutableMapping[_KeyType, bytes]): self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... -def open(file: str, flag: str = "c", mode: int = 0o666) -> _Database: ... +if sys.version_info >= (3, 11): + def open(file: StrOrBytesPath, flag: str = "c", mode: int = 0o666) -> _Database: ... + +else: + def open(file: str, flag: str = "c", mode: int = 0o666) -> _Database: ... diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index 8b562019fcfb..e80441cbb25b 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import ReadOnlyBuffer +from _typeshed import ReadOnlyBuffer, StrOrBytesPath from types import TracebackType from typing import TypeVar, overload from typing_extensions import Self, TypeAlias @@ -38,4 +38,7 @@ if sys.platform != "win32": __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 5eb84e6949fc..02bf23ec181c 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import ReadOnlyBuffer +from _typeshed import ReadOnlyBuffer, StrOrBytesPath from types import TracebackType from typing import TypeVar, overload from typing_extensions import Self, TypeAlias @@ -34,4 +34,7 @@ if sys.platform != "win32": __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 75e78ed59172..3937481159dc 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -64,7 +64,7 @@ class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): # The base classes differ starting in 3.10: if sys.version_info >= (3, 10): - # Please keep in sync with sys._MetaPathFinder + # Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol class MetaPathFinder(metaclass=ABCMeta): if sys.version_info < (3, 12): def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... @@ -85,7 +85,7 @@ if sys.version_info >= (3, 10): def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... else: - # Please keep in sync with sys._MetaPathFinder + # Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol class MetaPathFinder(Finder): def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... def invalidate_caches(self) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index 6608f70d4469..2492c76d5c6c 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -3,6 +3,7 @@ import importlib.machinery import sys import types from _typeshed import ReadableBuffer, StrOrBytesPath +from _typeshed.importlib import LoaderProtocol from collections.abc import Callable from typing import Any from typing_extensions import ParamSpec @@ -23,13 +24,13 @@ def source_from_cache(path: str) -> str: ... def decode_source(source_bytes: ReadableBuffer) -> str: ... def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None: ... def spec_from_loader( - name: str, loader: importlib.abc.Loader | None, *, origin: str | None = None, is_package: bool | None = None + name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None ) -> importlib.machinery.ModuleSpec | None: ... def spec_from_file_location( name: str, location: StrOrBytesPath | None = None, *, - loader: importlib.abc.Loader | None = None, + loader: LoaderProtocol | None = None, submodule_search_locations: list[str] | None = ..., ) -> importlib.machinery.ModuleSpec | None: ... def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index f5f7f91ece61..7ceddfa7ff28 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -587,7 +587,7 @@ def setLoggerClass(klass: type[Logger]) -> None: ... def captureWarnings(capture: bool) -> None: ... def setLogRecordFactory(factory: Callable[..., LogRecord]) -> None: ... -lastResort: StreamHandler[Any] | None +lastResort: Handler | None _StreamT = TypeVar("_StreamT", bound=SupportsWrite[str]) diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 5ea025095f68..0013e221f2e1 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -15,7 +15,7 @@ from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWra from os import PathLike, stat_result from types import TracebackType from typing import IO, Any, BinaryIO, Literal, overload -from typing_extensions import Self +from typing_extensions import Self, deprecated if sys.version_info >= (3, 9): from types import GenericAlias @@ -222,7 +222,11 @@ class Path(PurePath): else: def write_text(self, data: str, encoding: str | None = None, errors: str | None = None) -> int: ... if sys.version_info < (3, 12): - def link_to(self, target: StrOrBytesPath) -> None: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated as of Python 3.10 and removed in Python 3.12. Use hardlink_to() instead.") + def link_to(self, target: StrOrBytesPath) -> None: ... + else: + def link_to(self, target: StrOrBytesPath) -> None: ... if sys.version_info >= (3, 12): def walk( self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ... diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi index 4a0c8d101b7a..7e7fa4fda9a1 100644 --- a/mypy/typeshed/stdlib/pkgutil.pyi +++ b/mypy/typeshed/stdlib/pkgutil.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import SupportsRead +from _typeshed.importlib import LoaderProtocol, MetaPathFinderProtocol, PathEntryFinderProtocol from collections.abc import Callable, Iterable, Iterator -from importlib.abc import Loader, MetaPathFinder, PathEntryFinder from typing import IO, Any, NamedTuple, TypeVar from typing_extensions import deprecated @@ -23,7 +23,7 @@ if sys.version_info < (3, 12): _PathT = TypeVar("_PathT", bound=Iterable[str]) class ModuleInfo(NamedTuple): - module_finder: MetaPathFinder | PathEntryFinder + module_finder: MetaPathFinderProtocol | PathEntryFinderProtocol name: str ispkg: bool @@ -37,11 +37,11 @@ if sys.version_info < (3, 12): def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ... @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") -def find_loader(fullname: str) -> Loader | None: ... -def get_importer(path_item: str) -> PathEntryFinder | None: ... +def find_loader(fullname: str) -> LoaderProtocol | None: ... +def get_importer(path_item: str) -> PathEntryFinderProtocol | None: ... @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") -def get_loader(module_or_name: str) -> Loader | None: ... -def iter_importers(fullname: str = "") -> Iterator[MetaPathFinder | PathEntryFinder]: ... +def get_loader(module_or_name: str) -> LoaderProtocol | None: ... +def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ... def iter_modules(path: Iterable[str] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ... def read_code(stream: SupportsRead[bytes]) -> Any: ... # undocumented def walk_packages( diff --git a/mypy/typeshed/stdlib/shelve.pyi b/mypy/typeshed/stdlib/shelve.pyi index 59abeafe6fca..654c2ea097f7 100644 --- a/mypy/typeshed/stdlib/shelve.pyi +++ b/mypy/typeshed/stdlib/shelve.pyi @@ -1,3 +1,5 @@ +import sys +from _typeshed import StrOrBytesPath from collections.abc import Iterator, MutableMapping from dbm import _TFlags from types import TracebackType @@ -41,6 +43,17 @@ class BsdDbShelf(Shelf[_VT]): def last(self) -> tuple[str, _VT]: ... class DbfilenameShelf(Shelf[_VT]): - def __init__(self, filename: str, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False) -> None: ... + if sys.version_info >= (3, 11): + def __init__( + self, filename: StrOrBytesPath, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False + ) -> None: ... + else: + def __init__(self, filename: str, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False) -> None: ... -def open(filename: str, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False) -> Shelf[Any]: ... +if sys.version_info >= (3, 11): + def open( + filename: StrOrBytesPath, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False + ) -> Shelf[Any]: ... + +else: + def open(filename: str, flag: _TFlags = "c", protocol: int | None = None, writeback: bool = False) -> Shelf[Any]: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index a309bac9370a..b626409d2dde 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -474,6 +474,13 @@ if sys.version_info >= (3, 12): ETHERTYPE_VLAN as ETHERTYPE_VLAN, ) + if sys.platform == "linux": + from _socket import ETH_P_ALL as ETH_P_ALL + + if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": + # FreeBSD >= 14.0 + from _socket import PF_DIVERT as PF_DIVERT + # Re-exported from errno EBADF: int EAGAIN: int @@ -525,6 +532,9 @@ class AddressFamily(IntEnum): AF_BLUETOOTH = 32 if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = 34 + if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12): + # FreeBSD >= 14.0 + AF_DIVERT = 44 AF_INET = AddressFamily.AF_INET AF_INET6 = AddressFamily.AF_INET6 @@ -577,6 +587,9 @@ if sys.platform != "win32" or sys.version_info >= (3, 9): if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = AddressFamily.AF_HYPERV +if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12): + # FreeBSD >= 14.0 + AF_DIVERT = AddressFamily.AF_DIVERT class SocketKind(IntEnum): SOCK_STREAM = 1 diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 353e20c4b2e1..5867c9a9d510 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -1,9 +1,8 @@ import sys from _typeshed import OptExcInfo, ProfileFunction, TraceFunction, structseq +from _typeshed.importlib import MetaPathFinderProtocol, PathEntryFinderProtocol from builtins import object as _object from collections.abc import AsyncGenerator, Callable, Sequence -from importlib.abc import PathEntryFinder -from importlib.machinery import ModuleSpec from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final @@ -15,10 +14,6 @@ _T = TypeVar("_T") _ExitCode: TypeAlias = str | int | None _OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall 2022 or later -# Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder` -class _MetaPathFinder(Protocol): - def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ... - # ----- sys variables ----- if sys.platform != "win32": abiflags: str @@ -44,13 +39,13 @@ if sys.version_info >= (3, 12): last_exc: BaseException # or undefined. maxsize: int maxunicode: int -meta_path: list[_MetaPathFinder] +meta_path: list[MetaPathFinderProtocol] modules: dict[str, ModuleType] if sys.version_info >= (3, 10): orig_argv: list[str] path: list[str] -path_hooks: list[Callable[[str], PathEntryFinder]] -path_importer_cache: dict[str, PathEntryFinder | None] +path_hooks: list[Callable[[str], PathEntryFinderProtocol]] +path_importer_cache: dict[str, PathEntryFinderProtocol | None] platform: str if sys.version_info >= (3, 9): platlibdir: str diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index ce8f2f1f5929..b66369926404 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -374,7 +374,11 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def readlines(self, hint: int = ..., /) -> list[AnyStr]: ... # type: ignore[override] def seek(self, offset: int, whence: int = ...) -> int: ... def tell(self) -> int: ... - def truncate(self, size: int | None = None) -> None: ... # type: ignore[override] + if sys.version_info >= (3, 11): + def truncate(self, size: int | None = None) -> int: ... + else: + def truncate(self, size: int | None = None) -> None: ... # type: ignore[override] + @overload def write(self: SpooledTemporaryFile[str], s: str) -> int: ... @overload diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index f2d79b7f3ade..38940b4345c8 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -1,5 +1,6 @@ import sys from _typeshed import SupportsKeysAndGetItem +from _typeshed.importlib import LoaderProtocol from collections.abc import ( AsyncGenerator, Awaitable, @@ -16,7 +17,7 @@ from collections.abc import ( from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping -from typing import Any, ClassVar, Literal, Mapping, Protocol, TypeVar, final, overload # noqa: Y022 +from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload # noqa: Y022 from typing_extensions import ParamSpec, Self, TypeVarTuple, deprecated __all__ = [ @@ -64,18 +65,11 @@ _T2 = TypeVar("_T2") _KT = TypeVar("_KT") _VT_co = TypeVar("_VT_co", covariant=True) -@final -class _Cell: - def __new__(cls, contents: object = ..., /) -> Self: ... - def __eq__(self, value: object, /) -> bool: ... - __hash__: ClassVar[None] # type: ignore[assignment] - cell_contents: Any - # Make sure this class definition stays roughly in line with `builtins.function` @final class FunctionType: @property - def __closure__(self) -> tuple[_Cell, ...] | None: ... + def __closure__(self) -> tuple[CellType, ...] | None: ... __code__: CodeType __defaults__: tuple[Any, ...] | None __dict__: dict[str, Any] @@ -98,7 +92,7 @@ class FunctionType: globals: dict[str, Any], name: str | None = ..., argdefs: tuple[object, ...] | None = ..., - closure: tuple[_Cell, ...] | None = ..., + closure: tuple[CellType, ...] | None = ..., ) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @overload @@ -318,15 +312,12 @@ class SimpleNamespace: def __setattr__(self, name: str, value: Any, /) -> None: ... def __delattr__(self, name: str, /) -> None: ... -class _LoaderProtocol(Protocol): - def load_module(self, fullname: str, /) -> ModuleType: ... - class ModuleType: __name__: str __file__: str | None @property def __dict__(self) -> dict[str, Any]: ... # type: ignore[override] - __loader__: _LoaderProtocol | None + __loader__: LoaderProtocol | None __package__: str | None __path__: MutableSequence[str] __spec__: ModuleSpec | None @@ -336,6 +327,12 @@ class ModuleType: # using `builtins.__import__` or `importlib.import_module` less painful def __getattr__(self, name: str) -> Any: ... +@final +class CellType: + def __new__(cls, contents: object = ..., /) -> Self: ... + __hash__: ClassVar[None] # type: ignore[assignment] + cell_contents: Any + _YieldT_co = TypeVar("_YieldT_co", covariant=True) _SendT_contra = TypeVar("_SendT_contra", contravariant=True) _ReturnT_co = TypeVar("_ReturnT_co", covariant=True) @@ -405,7 +402,7 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): @final class MethodType: @property - def __closure__(self) -> tuple[_Cell, ...] | None: ... # inherited from the added function + def __closure__(self) -> tuple[CellType, ...] | None: ... # inherited from the added function @property def __defaults__(self) -> tuple[Any, ...] | None: ... # inherited from the added function @property @@ -570,8 +567,6 @@ def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Await @overload def coroutine(func: _Fn) -> _Fn: ... -CellType = _Cell - if sys.version_info >= (3, 9): class GenericAlias: @property diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 4b80397bdd7a..d047f1c87621 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -8,7 +8,6 @@ import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import IdentityFunction, ReadableBuffer, SupportsKeysAndGetItem from abc import ABCMeta, abstractmethod -from contextlib import AbstractAsyncContextManager, AbstractContextManager from re import Match as Match, Pattern as Pattern from types import ( BuiltinFunctionType, @@ -24,10 +23,10 @@ from types import ( ) from typing_extensions import Never as _Never, ParamSpec as _ParamSpec -if sys.version_info >= (3, 10): - from types import UnionType if sys.version_info >= (3, 9): from types import GenericAlias +if sys.version_info >= (3, 10): + from types import UnionType __all__ = [ "AbstractSet", @@ -402,8 +401,8 @@ class Reversible(Iterable[_T_co], Protocol[_T_co]): def __reversed__(self) -> Iterator[_T_co]: ... _YieldT_co = TypeVar("_YieldT_co", covariant=True) -_SendT_contra = TypeVar("_SendT_contra", contravariant=True) -_ReturnT_co = TypeVar("_ReturnT_co", covariant=True) +_SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): def __next__(self) -> _YieldT_co: ... @@ -428,24 +427,28 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return @property def gi_yieldfrom(self) -> Generator[Any, Any, Any] | None: ... -# NOTE: Technically we would like this to be able to accept a second parameter as well, just -# like it's counterpart in contextlib, however `typing._SpecialGenericAlias` enforces the -# correct number of arguments at runtime, so we would be hiding runtime errors. -@runtime_checkable -class ContextManager(AbstractContextManager[_T_co, bool | None], Protocol[_T_co]): ... +# NOTE: Prior to Python 3.13 these aliases are lacking the second _ExitT_co parameter +if sys.version_info >= (3, 13): + from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager +else: + from contextlib import AbstractAsyncContextManager, AbstractContextManager -# NOTE: Technically we would like this to be able to accept a second parameter as well, just -# like it's counterpart in contextlib, however `typing._SpecialGenericAlias` enforces the -# correct number of arguments at runtime, so we would be hiding runtime errors. -@runtime_checkable -class AsyncContextManager(AbstractAsyncContextManager[_T_co, bool | None], Protocol[_T_co]): ... + @runtime_checkable + class ContextManager(AbstractContextManager[_T_co, bool | None], Protocol[_T_co]): ... + + @runtime_checkable + class AsyncContextManager(AbstractAsyncContextManager[_T_co, bool | None], Protocol[_T_co]): ... @runtime_checkable class Awaitable(Protocol[_T_co]): @abstractmethod def __await__(self) -> Generator[Any, Any, _T_co]: ... -class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): +# Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter. +_SendT_contra_nd = TypeVar("_SendT_contra_nd", contravariant=True) +_ReturnT_co_nd = TypeVar("_ReturnT_co_nd", covariant=True) + +class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd]): __name__: str __qualname__: str @property @@ -457,7 +460,7 @@ class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _Retu @property def cr_running(self) -> bool: ... @abstractmethod - def send(self, value: _SendT_contra, /) -> _YieldT_co: ... + def send(self, value: _SendT_contra_nd, /) -> _YieldT_co: ... @overload @abstractmethod def throw( @@ -473,9 +476,9 @@ class Coroutine(Awaitable[_ReturnT_co], Generic[_YieldT_co, _SendT_contra, _Retu # The parameters correspond to Generator, but the 4th is the original type. @type_check_only class AwaitableGenerator( - Awaitable[_ReturnT_co], - Generator[_YieldT_co, _SendT_contra, _ReturnT_co], - Generic[_YieldT_co, _SendT_contra, _ReturnT_co, _S], + Awaitable[_ReturnT_co_nd], + Generator[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd], + Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd, _S], metaclass=ABCMeta, ): ... From 0a2225b0754d7bd0714291820d56833d5284b2a4 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Wed, 15 May 2024 10:48:15 +0100 Subject: [PATCH 0578/1617] [dmypy] sort list of files for update by extension (#17245) dmypy receives the list of updated files via `--update` flag. If this list contains both `foo.py` and `foo.pyi`, the order matters. It seems to process the first file in the list first. But if we have a `.pyi` file, we want this to be processed first since this one contains the typing information. Let's reverse sort the list of updated files by the extension. This should be a simple enough fix to resolve this. Though there might be some edge cases where the list of files to update contains just pyi files, but we might need to recheck the equivalent py files even if not explicitly updated. --- mypy/dmypy_server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 3d337eedbf1c..f8a0f91f87d9 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -383,6 +383,9 @@ def cmd_recheck( removals = set(remove) sources = [s for s in sources if s.path and s.path not in removals] if update: + # Sort list of file updates by extension, so *.pyi files are first. + update.sort(key=lambda f: os.path.splitext(f)[1], reverse=True) + known = {s.path for s in sources if s.path} added = [p for p in update if p not in known] try: From cdc956bd209285b43cfca712902be2da04d133f9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 15 May 2024 14:14:41 +0200 Subject: [PATCH 0579/1617] Ignore typeshed test files (#17249) During the last typehed update, we included the `@tests` folder which is unnecessary for mypy. Update the `sync-typeshed.py` script to exclude it in the future. Refs: - #17246 - https://github.com/python/typeshed/issues/11762 --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- misc/sync-typeshed.py | 4 +- .../test_cases/asyncio/check_coroutines.py | 25 -- .../@tests/test_cases/asyncio/check_gather.py | 38 -- .../@tests/test_cases/asyncio/check_task.py | 28 -- .../test_cases/builtins/check_dict-py39.py | 67 --- .../@tests/test_cases/builtins/check_dict.py | 58 --- .../builtins/check_exception_group-py311.py | 323 -------------- .../test_cases/builtins/check_iteration.py | 16 - .../@tests/test_cases/builtins/check_list.py | 21 - .../test_cases/builtins/check_object.py | 13 - .../@tests/test_cases/builtins/check_pow.py | 91 ---- .../test_cases/builtins/check_reversed.py | 34 -- .../@tests/test_cases/builtins/check_round.py | 68 --- .../@tests/test_cases/builtins/check_sum.py | 55 --- .../@tests/test_cases/builtins/check_tuple.py | 13 - .../stdlib/@tests/test_cases/check_codecs.py | 13 - .../test_cases/check_concurrent_futures.py | 30 -- .../@tests/test_cases/check_contextlib.py | 20 - .../@tests/test_cases/check_dataclasses.py | 101 ----- .../stdlib/@tests/test_cases/check_enum.py | 38 -- .../@tests/test_cases/check_functools.py | 67 --- .../@tests/test_cases/check_importlib.py | 47 -- .../test_cases/check_importlib_metadata.py | 33 -- .../stdlib/@tests/test_cases/check_io.py | 6 - .../stdlib/@tests/test_cases/check_logging.py | 30 -- .../test_cases/check_multiprocessing.py | 14 - .../stdlib/@tests/test_cases/check_pathlib.py | 20 - .../stdlib/@tests/test_cases/check_re.py | 26 -- .../stdlib/@tests/test_cases/check_sqlite3.py | 26 -- .../stdlib/@tests/test_cases/check_tarfile.py | 13 - .../@tests/test_cases/check_tempfile.py | 31 -- .../@tests/test_cases/check_threading.py | 14 - .../stdlib/@tests/test_cases/check_tkinter.py | 30 -- .../@tests/test_cases/check_unittest.py | 173 -------- .../stdlib/@tests/test_cases/check_xml.py | 35 -- .../collections/check_defaultdict-py39.py | 69 --- .../@tests/test_cases/email/check_message.py | 6 - .../itertools/check_itertools_recipes.py | 410 ------------------ .../test_cases/typing/check_MutableMapping.py | 18 - .../@tests/test_cases/typing/check_all.py | 14 - .../typing/check_regression_issue_9296.py | 16 - .../test_cases/typing/check_typing_io.py | 21 - 42 files changed, 3 insertions(+), 2172 deletions(-) delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_enum.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_functools.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_io.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_logging.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_re.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_threading.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/check_xml.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py delete mode 100644 mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 3101b4bfa72a..22023234710e 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -51,7 +51,9 @@ def update_typeshed(typeshed_dir: str, commit: str | None) -> str: # Remove existing stubs. shutil.rmtree(stdlib_dir) # Copy new stdlib stubs. - shutil.copytree(os.path.join(typeshed_dir, "stdlib"), stdlib_dir) + shutil.copytree( + os.path.join(typeshed_dir, "stdlib"), stdlib_dir, ignore=shutil.ignore_patterns("@tests") + ) shutil.copy(os.path.join(typeshed_dir, "LICENSE"), os.path.join("mypy", "typeshed")) return commit diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py deleted file mode 100644 index 160bd896469e..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_coroutines.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from asyncio import iscoroutinefunction -from collections.abc import Awaitable, Callable, Coroutine -from typing import Any -from typing_extensions import assert_type - - -def test_iscoroutinefunction( - x: Callable[[str, int], Coroutine[str, int, bytes]], - y: Callable[[str, int], Awaitable[bytes]], - z: Callable[[str, int], str | Awaitable[bytes]], - xx: object, -) -> None: - if iscoroutinefunction(x): - assert_type(x, Callable[[str, int], Coroutine[str, int, bytes]]) - - if iscoroutinefunction(y): - assert_type(y, Callable[[str, int], Coroutine[Any, Any, bytes]]) - - if iscoroutinefunction(z): - assert_type(z, Callable[[str, int], Coroutine[Any, Any, Any]]) - - if iscoroutinefunction(xx): - assert_type(xx, Callable[..., Coroutine[Any, Any, Any]]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py deleted file mode 100644 index 02a01e39731a..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_gather.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -import asyncio -from typing import Awaitable, List, Tuple, Union -from typing_extensions import assert_type - - -async def coro1() -> int: - return 42 - - -async def coro2() -> str: - return "spam" - - -async def test_gather(awaitable1: Awaitable[int], awaitable2: Awaitable[str]) -> None: - a = await asyncio.gather(awaitable1) - assert_type(a, Tuple[int]) - - b = await asyncio.gather(awaitable1, awaitable2, return_exceptions=True) - assert_type(b, Tuple[Union[int, BaseException], Union[str, BaseException]]) - - c = await asyncio.gather(awaitable1, awaitable2, awaitable1, awaitable1, awaitable1, awaitable1) - assert_type(c, Tuple[int, str, int, int, int, int]) - - d = await asyncio.gather(awaitable1, awaitable1, awaitable1, awaitable1, awaitable1, awaitable1, awaitable1) - assert_type(d, List[int]) - - awaitables_list: list[Awaitable[int]] = [awaitable1] - e = await asyncio.gather(*awaitables_list) - assert_type(e, List[int]) - - # this case isn't reliable between typecheckers, no one would ever call it with no args anyway - # f = await asyncio.gather() - # assert_type(f, list[Any]) - - -asyncio.run(test_gather(coro1(), coro2())) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py b/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py deleted file mode 100644 index 69bcf8f782aa..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/asyncio/check_task.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import annotations - -import asyncio - - -class Waiter: - def __init__(self) -> None: - self.tasks: list[asyncio.Task[object]] = [] - - def add(self, t: asyncio.Task[object]) -> None: - self.tasks.append(t) - - async def join(self) -> None: - await asyncio.wait(self.tasks) - - -async def foo() -> int: - return 42 - - -async def main() -> None: - # asyncio.Task is covariant in its type argument, which is unusual since its parent class - # asyncio.Future is invariant in its type argument. This is only sound because asyncio.Task - # is not actually Liskov substitutable for asyncio.Future: it does not implement set_result. - w = Waiter() - t: asyncio.Task[int] = asyncio.create_task(foo()) - w.add(t) - await w.join() diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py deleted file mode 100644 index d707cfed222e..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict-py39.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Tests for `dict.__(r)or__`. - -`dict.__or__` and `dict.__ror__` were only added in py39, -hence why these are in a separate file to the other test cases for `dict`. -""" - -from __future__ import annotations - -import os -import sys -from typing import Mapping, TypeVar, Union -from typing_extensions import Self, assert_type - -_KT = TypeVar("_KT") -_VT = TypeVar("_VT") - -if sys.version_info >= (3, 9): - - class CustomDictSubclass(dict[_KT, _VT]): - pass - - class CustomMappingWithDunderOr(Mapping[_KT, _VT]): - def __or__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: - return {} - - def __ror__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: - return {} - - def __ior__(self, other: Mapping[_KT, _VT]) -> Self: - return self - - def test_dict_dot_or( - a: dict[int, int], - b: CustomDictSubclass[int, int], - c: dict[str, str], - d: Mapping[int, int], - e: CustomMappingWithDunderOr[str, str], - ) -> None: - # dict.__(r)or__ always returns a dict, even if called on a subclass of dict: - assert_type(a | b, dict[int, int]) - assert_type(b | a, dict[int, int]) - - assert_type(a | c, dict[Union[int, str], Union[int, str]]) - - # arbitrary mappings are not accepted by `dict.__or__`; - # it has to be a subclass of `dict` - a | d # type: ignore - - # but Mappings such as `os._Environ` or `CustomMappingWithDunderOr`, - # which define `__ror__` methods that accept `dict`, are fine: - assert_type(a | os.environ, dict[Union[str, int], Union[str, int]]) - assert_type(os.environ | a, dict[Union[str, int], Union[str, int]]) - - assert_type(c | os.environ, dict[str, str]) - assert_type(c | e, dict[str, str]) - - assert_type(os.environ | c, dict[str, str]) - assert_type(e | c, dict[str, str]) - - e |= c - e |= a # type: ignore - - # TODO: this test passes mypy, but fails pyright for some reason: - # c |= e - - c |= a # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py deleted file mode 100644 index aa920d045cbc..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_dict.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -from typing import Dict, Generic, Iterable, TypeVar -from typing_extensions import assert_type - -# These do follow `__init__` overloads order: -# mypy and pyright have different opinions about this one: -# mypy raises: 'Need type annotation for "bad"' -# pyright is fine with it. -# bad = dict() -good: dict[str, str] = dict() -assert_type(good, Dict[str, str]) - -assert_type(dict(arg=1), Dict[str, int]) - -_KT = TypeVar("_KT") -_VT = TypeVar("_VT") - - -class KeysAndGetItem(Generic[_KT, _VT]): - data: dict[_KT, _VT] - - def __init__(self, data: dict[_KT, _VT]) -> None: - self.data = data - - def keys(self) -> Iterable[_KT]: - return self.data.keys() - - def __getitem__(self, __k: _KT) -> _VT: - return self.data[__k] - - -kt1: KeysAndGetItem[int, str] = KeysAndGetItem({0: ""}) -assert_type(dict(kt1), Dict[int, str]) -dict(kt1, arg="a") # type: ignore - -kt2: KeysAndGetItem[str, int] = KeysAndGetItem({"": 0}) -assert_type(dict(kt2, arg=1), Dict[str, int]) - - -def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str]: - return dict(x) - - -i1: Iterable[tuple[int, str]] = [(1, "a"), (2, "b")] -test_iterable_tuple_overload(i1) -dict(i1, arg="a") # type: ignore - -i2: Iterable[tuple[str, int]] = [("a", 1), ("b", 2)] -assert_type(dict(i2, arg=1), Dict[str, int]) - -i3: Iterable[str] = ["a.b"] -i4: Iterable[bytes] = [b"a.b"] -assert_type(dict(string.split(".") for string in i3), Dict[str, str]) -assert_type(dict(string.split(b".") for string in i4), Dict[bytes, bytes]) - -dict(["foo", "bar", "baz"]) # type: ignore -dict([b"foo", b"bar", b"baz"]) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py deleted file mode 100644 index e53cd12288a4..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py +++ /dev/null @@ -1,323 +0,0 @@ -from __future__ import annotations - -import sys -from typing import TypeVar -from typing_extensions import assert_type - -if sys.version_info >= (3, 11): - # This can be removed later, but right now Flake8 does not know - # about these two classes: - from builtins import BaseExceptionGroup, ExceptionGroup - - # BaseExceptionGroup - # ================== - # `BaseExceptionGroup` can work with `BaseException`: - beg = BaseExceptionGroup("x", [SystemExit(), SystemExit()]) - assert_type(beg, BaseExceptionGroup[SystemExit]) - assert_type(beg.exceptions, tuple[SystemExit | BaseExceptionGroup[SystemExit], ...]) - - # Covariance works: - _beg1: BaseExceptionGroup[BaseException] = beg - - # `BaseExceptionGroup` can work with `Exception`: - beg2 = BaseExceptionGroup("x", [ValueError()]) - # FIXME: this is not right, runtime returns `ExceptionGroup` instance instead, - # but I am unable to represent this with types right now. - assert_type(beg2, BaseExceptionGroup[ValueError]) - - # .subgroup() - # ----------- - - assert_type(beg.subgroup(KeyboardInterrupt), BaseExceptionGroup[KeyboardInterrupt] | None) - assert_type(beg.subgroup((KeyboardInterrupt,)), BaseExceptionGroup[KeyboardInterrupt] | None) - - def is_base_exc(exc: BaseException) -> bool: - return isinstance(exc, BaseException) - - def is_specific(exc: SystemExit | BaseExceptionGroup[SystemExit]) -> bool: - return isinstance(exc, SystemExit) - - # This one does not have `BaseExceptionGroup` part, - # this is why we treat as an error. - def is_system_exit(exc: SystemExit) -> bool: - return isinstance(exc, SystemExit) - - def unrelated_subgroup(exc: KeyboardInterrupt) -> bool: - return False - - assert_type(beg.subgroup(is_base_exc), BaseExceptionGroup[SystemExit] | None) - assert_type(beg.subgroup(is_specific), BaseExceptionGroup[SystemExit] | None) - beg.subgroup(is_system_exit) # type: ignore - beg.subgroup(unrelated_subgroup) # type: ignore - - # `Exception`` subgroup returns `ExceptionGroup`: - assert_type(beg.subgroup(ValueError), ExceptionGroup[ValueError] | None) - assert_type(beg.subgroup((ValueError,)), ExceptionGroup[ValueError] | None) - - # Callable are harder, we don't support cast to `ExceptionGroup` here. - # Because callables might return `True` the first time. And `BaseExceptionGroup` - # will stick, no matter what arguments are. - - def is_exception(exc: Exception) -> bool: - return isinstance(exc, Exception) - - def is_exception_or_beg(exc: Exception | BaseExceptionGroup[SystemExit]) -> bool: - return isinstance(exc, Exception) - - # This is an error because of the `Exception` argument type, - # while `SystemExit` is needed instead. - beg.subgroup(is_exception_or_beg) # type: ignore - - # This is an error, because `BaseExceptionGroup` is not an `Exception` - # subclass. It is required. - beg.subgroup(is_exception) # type: ignore - - # .split() - # -------- - - assert_type( - beg.split(KeyboardInterrupt), tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None] - ) - assert_type( - beg.split((KeyboardInterrupt,)), - tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None], - ) - assert_type( - beg.split(ValueError), # there are no `ValueError` items in there, but anyway - tuple[ExceptionGroup[ValueError] | None, BaseExceptionGroup[SystemExit] | None], - ) - - excs_to_split: list[ValueError | KeyError | SystemExit] = [ValueError(), KeyError(), SystemExit()] - to_split = BaseExceptionGroup("x", excs_to_split) - assert_type(to_split, BaseExceptionGroup[ValueError | KeyError | SystemExit]) - - # Ideally the first part should be `ExceptionGroup[ValueError]` (done) - # and the second part should be `BaseExceptionGroup[KeyError | SystemExit]`, - # but we cannot subtract type from a union. - # We also cannot change `BaseExceptionGroup` to `ExceptionGroup` even if needed - # in the second part here because of that. - assert_type( - to_split.split(ValueError), - tuple[ExceptionGroup[ValueError] | None, BaseExceptionGroup[ValueError | KeyError | SystemExit] | None], - ) - - def split_callable1(exc: ValueError | KeyError | SystemExit | BaseExceptionGroup[ValueError | KeyError | SystemExit]) -> bool: - return True - - assert_type( - to_split.split(split_callable1), # Concrete type is ok - tuple[ - BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, - BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, - ], - ) - assert_type( - to_split.split(is_base_exc), # Base class is ok - tuple[ - BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, - BaseExceptionGroup[ValueError | KeyError | SystemExit] | None, - ], - ) - # `Exception` cannot be used: `BaseExceptionGroup` is not a subtype of it. - to_split.split(is_exception) # type: ignore - - # .derive() - # --------- - - assert_type(beg.derive([ValueError()]), ExceptionGroup[ValueError]) - assert_type(beg.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) - - # ExceptionGroup - # ============== - - # `ExceptionGroup` can work with `Exception`: - excs: list[ValueError | KeyError] = [ValueError(), KeyError()] - eg = ExceptionGroup("x", excs) - assert_type(eg, ExceptionGroup[ValueError | KeyError]) - assert_type(eg.exceptions, tuple[ValueError | KeyError | ExceptionGroup[ValueError | KeyError], ...]) - - # Covariance works: - _eg1: ExceptionGroup[Exception] = eg - - # `ExceptionGroup` cannot work with `BaseException`: - ExceptionGroup("x", [SystemExit()]) # type: ignore - - # .subgroup() - # ----------- - - # Our decision is to ban cases like:: - # - # >>> eg = ExceptionGroup('x', [ValueError()]) - # >>> eg.subgroup(BaseException) - # ExceptionGroup('e', [ValueError()]) - # - # are possible in runtime. - # We do it because, it does not make sense for all other base exception types. - # Supporting just `BaseException` looks like an overkill. - eg.subgroup(BaseException) # type: ignore - eg.subgroup((KeyboardInterrupt, SystemExit)) # type: ignore - - assert_type(eg.subgroup(Exception), ExceptionGroup[Exception] | None) - assert_type(eg.subgroup(ValueError), ExceptionGroup[ValueError] | None) - assert_type(eg.subgroup((ValueError,)), ExceptionGroup[ValueError] | None) - - def subgroup_eg1(exc: ValueError | KeyError | ExceptionGroup[ValueError | KeyError]) -> bool: - return True - - def subgroup_eg2(exc: ValueError | KeyError) -> bool: - return True - - assert_type(eg.subgroup(subgroup_eg1), ExceptionGroup[ValueError | KeyError] | None) - assert_type(eg.subgroup(is_exception), ExceptionGroup[ValueError | KeyError] | None) - assert_type(eg.subgroup(is_base_exc), ExceptionGroup[ValueError | KeyError] | None) - assert_type(eg.subgroup(is_base_exc), ExceptionGroup[ValueError | KeyError] | None) - - # Does not have `ExceptionGroup` part: - eg.subgroup(subgroup_eg2) # type: ignore - - # .split() - # -------- - - assert_type(eg.split(TypeError), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError | KeyError] | None]) - assert_type(eg.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError | KeyError] | None]) - assert_type( - eg.split(is_exception), tuple[ExceptionGroup[ValueError | KeyError] | None, ExceptionGroup[ValueError | KeyError] | None] - ) - assert_type( - eg.split(is_base_exc), - # is not converted, because `ExceptionGroup` cannot have - # direct `BaseException` subclasses inside. - tuple[ExceptionGroup[ValueError | KeyError] | None, ExceptionGroup[ValueError | KeyError] | None], - ) - - # It does not include `ExceptionGroup` itself, so it will fail: - def value_or_key_error(exc: ValueError | KeyError) -> bool: - return isinstance(exc, (ValueError, KeyError)) - - eg.split(value_or_key_error) # type: ignore - - # `ExceptionGroup` cannot have direct `BaseException` subclasses inside. - eg.split(BaseException) # type: ignore - eg.split((SystemExit, GeneratorExit)) # type: ignore - - # .derive() - # --------- - - assert_type(eg.derive([ValueError()]), ExceptionGroup[ValueError]) - assert_type(eg.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) - - # BaseExceptionGroup Custom Subclass - # ================================== - # In some cases `Self` type can be preserved in runtime, - # but it is impossible to express. That's why we always fallback to - # `BaseExceptionGroup` and `ExceptionGroup`. - - _BE = TypeVar("_BE", bound=BaseException) - - class CustomBaseGroup(BaseExceptionGroup[_BE]): ... - - cb1 = CustomBaseGroup("x", [SystemExit()]) - assert_type(cb1, CustomBaseGroup[SystemExit]) - cb2 = CustomBaseGroup("x", [ValueError()]) - assert_type(cb2, CustomBaseGroup[ValueError]) - - # .subgroup() - # ----------- - - assert_type(cb1.subgroup(KeyboardInterrupt), BaseExceptionGroup[KeyboardInterrupt] | None) - assert_type(cb2.subgroup((KeyboardInterrupt,)), BaseExceptionGroup[KeyboardInterrupt] | None) - - assert_type(cb1.subgroup(ValueError), ExceptionGroup[ValueError] | None) - assert_type(cb2.subgroup((KeyError,)), ExceptionGroup[KeyError] | None) - - def cb_subgroup1(exc: SystemExit | CustomBaseGroup[SystemExit]) -> bool: - return True - - def cb_subgroup2(exc: ValueError | CustomBaseGroup[ValueError]) -> bool: - return True - - assert_type(cb1.subgroup(cb_subgroup1), BaseExceptionGroup[SystemExit] | None) - assert_type(cb2.subgroup(cb_subgroup2), BaseExceptionGroup[ValueError] | None) - cb1.subgroup(cb_subgroup2) # type: ignore - cb2.subgroup(cb_subgroup1) # type: ignore - - # .split() - # -------- - - assert_type( - cb1.split(KeyboardInterrupt), tuple[BaseExceptionGroup[KeyboardInterrupt] | None, BaseExceptionGroup[SystemExit] | None] - ) - assert_type(cb1.split(TypeError), tuple[ExceptionGroup[TypeError] | None, BaseExceptionGroup[SystemExit] | None]) - assert_type(cb2.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, BaseExceptionGroup[ValueError] | None]) - - def cb_split1(exc: SystemExit | CustomBaseGroup[SystemExit]) -> bool: - return True - - def cb_split2(exc: ValueError | CustomBaseGroup[ValueError]) -> bool: - return True - - assert_type(cb1.split(cb_split1), tuple[BaseExceptionGroup[SystemExit] | None, BaseExceptionGroup[SystemExit] | None]) - assert_type(cb2.split(cb_split2), tuple[BaseExceptionGroup[ValueError] | None, BaseExceptionGroup[ValueError] | None]) - cb1.split(cb_split2) # type: ignore - cb2.split(cb_split1) # type: ignore - - # .derive() - # --------- - - # Note, that `Self` type is not preserved in runtime. - assert_type(cb1.derive([ValueError()]), ExceptionGroup[ValueError]) - assert_type(cb1.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) - assert_type(cb2.derive([ValueError()]), ExceptionGroup[ValueError]) - assert_type(cb2.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) - - # ExceptionGroup Custom Subclass - # ============================== - - _E = TypeVar("_E", bound=Exception) - - class CustomGroup(ExceptionGroup[_E]): ... - - CustomGroup("x", [SystemExit()]) # type: ignore - cg1 = CustomGroup("x", [ValueError()]) - assert_type(cg1, CustomGroup[ValueError]) - - # .subgroup() - # ----------- - - cg1.subgroup(BaseException) # type: ignore - cg1.subgroup((KeyboardInterrupt, SystemExit)) # type: ignore - - assert_type(cg1.subgroup(ValueError), ExceptionGroup[ValueError] | None) - assert_type(cg1.subgroup((KeyError,)), ExceptionGroup[KeyError] | None) - - def cg_subgroup1(exc: ValueError | CustomGroup[ValueError]) -> bool: - return True - - def cg_subgroup2(exc: ValueError) -> bool: - return True - - assert_type(cg1.subgroup(cg_subgroup1), ExceptionGroup[ValueError] | None) - cg1.subgroup(cb_subgroup2) # type: ignore - - # .split() - # -------- - - assert_type(cg1.split(TypeError), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError] | None]) - assert_type(cg1.split((TypeError,)), tuple[ExceptionGroup[TypeError] | None, ExceptionGroup[ValueError] | None]) - cg1.split(BaseException) # type: ignore - - def cg_split1(exc: ValueError | CustomGroup[ValueError]) -> bool: - return True - - def cg_split2(exc: ValueError) -> bool: - return True - - assert_type(cg1.split(cg_split1), tuple[ExceptionGroup[ValueError] | None, ExceptionGroup[ValueError] | None]) - cg1.split(cg_split2) # type: ignore - - # .derive() - # --------- - - # Note, that `Self` type is not preserved in runtime. - assert_type(cg1.derive([ValueError()]), ExceptionGroup[ValueError]) - assert_type(cg1.derive([KeyboardInterrupt()]), BaseExceptionGroup[KeyboardInterrupt]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py deleted file mode 100644 index 3d609635377e..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_iteration.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from typing import Iterator -from typing_extensions import assert_type - - -class OldStyleIter: - def __getitem__(self, index: int) -> str: - return str(index) - - -for x in iter(OldStyleIter()): - assert_type(x, str) - -assert_type(iter(OldStyleIter()), Iterator[str]) -assert_type(next(iter(OldStyleIter())), str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py deleted file mode 100644 index 4113f5c66182..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_list.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from typing import List, Union -from typing_extensions import assert_type - - -# list.__add__ example from #8292 -class Foo: - def asd(self) -> int: - return 1 - - -class Bar: - def asd(self) -> int: - return 2 - - -combined = [Foo()] + [Bar()] -assert_type(combined, List[Union[Foo, Bar]]) -for item in combined: - assert_type(item.asd(), int) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py deleted file mode 100644 index 60df1143f727..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_object.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from typing import Any - - -# The following should pass without error (see #6661): -class Diagnostic: - def __reduce__(self) -> str | tuple[Any, ...]: - res = super().__reduce__() - if isinstance(res, tuple) and len(res) >= 3: - res[2]["_info"] = 42 - - return res diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py deleted file mode 100644 index 1f38710d6bea..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_pow.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import annotations - -from decimal import Decimal -from fractions import Fraction -from typing import Any, Literal -from typing_extensions import assert_type - -# See #7163 -assert_type(pow(1, 0), Literal[1]) -assert_type(1**0, Literal[1]) -assert_type(pow(1, 0, None), Literal[1]) - -# TODO: We don't have a good way of expressing the fact -# that passing 0 for the third argument will lead to an exception being raised -# (see discussion in #8566) -# -# assert_type(pow(2, 4, 0), NoReturn) - -assert_type(pow(2, 4), int) -assert_type(2**4, int) -assert_type(pow(4, 6, None), int) - -assert_type(pow(5, -7), float) -assert_type(5**-7, float) - -assert_type(pow(2, 4, 5), int) # pow(, , ) -assert_type(pow(2, 35, 3), int) # pow(, , ) - -assert_type(pow(2, 8.5), float) -assert_type(2**8.6, float) -assert_type(pow(2, 8.6, None), float) - -# TODO: Why does this pass pyright but not mypy?? -# assert_type((-2) ** 0.5, complex) - -assert_type(pow((-5), 8.42, None), complex) - -assert_type(pow(4.6, 8), float) -assert_type(4.6**8, float) -assert_type(pow(5.1, 4, None), float) - -assert_type(pow(complex(6), 6.2), complex) -assert_type(complex(6) ** 6.2, complex) -assert_type(pow(complex(9), 7.3, None), complex) - -assert_type(pow(Fraction(), 4, None), Fraction) -assert_type(Fraction() ** 4, Fraction) - -assert_type(pow(Fraction(3, 7), complex(1, 8)), complex) -assert_type(Fraction(3, 7) ** complex(1, 8), complex) - -assert_type(pow(complex(4, -8), Fraction(2, 3)), complex) -assert_type(complex(4, -8) ** Fraction(2, 3), complex) - -assert_type(pow(Decimal("1.0"), Decimal("1.6")), Decimal) -assert_type(Decimal("1.0") ** Decimal("1.6"), Decimal) - -assert_type(pow(Decimal("1.0"), Decimal("1.0"), Decimal("1.0")), Decimal) -assert_type(pow(Decimal("4.6"), 7, None), Decimal) -assert_type(Decimal("4.6") ** 7, Decimal) - -# These would ideally be more precise, but `Any` is acceptable -# They have to be `Any` due to the fact that type-checkers can't distinguish -# between positive and negative numbers for the second argument to `pow()` -# -# int for positive 2nd-arg, float otherwise -assert_type(pow(4, 65), Any) -assert_type(pow(2, -45), Any) -assert_type(pow(3, 57, None), Any) -assert_type(pow(67, 0.98, None), Any) -assert_type(87**7.32, Any) -# pow(, ) -> float -# pow(, ) -> complex -assert_type(pow(4.7, 7.4), Any) -assert_type(pow(-9.8, 8.3), Any) -assert_type(pow(-9.3, -88.2), Any) -assert_type(pow(8.2, -9.8), Any) -assert_type(pow(4.7, 9.2, None), Any) -# See #7046 -- float for a positive 1st arg, complex otherwise -assert_type((-95) ** 8.42, Any) - -# All of the following cases should fail a type-checker. -pow(1.9, 4, 6) # type: ignore -pow(4, 7, 4.32) # type: ignore -pow(6.2, 5.9, 73) # type: ignore -pow(complex(6), 6.2, 7) # type: ignore -pow(Fraction(), 5, 8) # type: ignore -Decimal("8.7") ** 3.14 # type: ignore - -# TODO: This fails at runtime, but currently passes mypy and pyright: -pow(Decimal("8.5"), 3.21) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py deleted file mode 100644 index 2a43a57deb4e..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_reversed.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from collections.abc import Iterator -from typing import Generic, TypeVar -from typing_extensions import assert_type - -x: list[int] = [] -assert_type(list(reversed(x)), "list[int]") - - -class MyReversible: - def __iter__(self) -> Iterator[str]: - yield "blah" - - def __reversed__(self) -> Iterator[str]: - yield "blah" - - -assert_type(list(reversed(MyReversible())), "list[str]") - - -_T = TypeVar("_T") - - -class MyLenAndGetItem(Generic[_T]): - def __len__(self) -> int: - return 0 - - def __getitem__(self, item: int) -> _T: - raise KeyError - - -len_and_get_item: MyLenAndGetItem[int] = MyLenAndGetItem() -assert_type(list(reversed(len_and_get_item)), "list[int]") diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py deleted file mode 100644 index 84081f3665b9..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_round.py +++ /dev/null @@ -1,68 +0,0 @@ -from __future__ import annotations - -from typing import overload -from typing_extensions import assert_type - - -class CustomIndex: - def __index__(self) -> int: - return 1 - - -# float: - -assert_type(round(5.5), int) -assert_type(round(5.5, None), int) -assert_type(round(5.5, 0), float) -assert_type(round(5.5, 1), float) -assert_type(round(5.5, 5), float) -assert_type(round(5.5, CustomIndex()), float) - -# int: - -assert_type(round(1), int) -assert_type(round(1, 1), int) -assert_type(round(1, None), int) -assert_type(round(1, CustomIndex()), int) - -# Protocols: - - -class WithCustomRound1: - def __round__(self) -> str: - return "a" - - -assert_type(round(WithCustomRound1()), str) -assert_type(round(WithCustomRound1(), None), str) -# Errors: -round(WithCustomRound1(), 1) # type: ignore -round(WithCustomRound1(), CustomIndex()) # type: ignore - - -class WithCustomRound2: - def __round__(self, digits: int) -> str: - return "a" - - -assert_type(round(WithCustomRound2(), 1), str) -assert_type(round(WithCustomRound2(), CustomIndex()), str) -# Errors: -round(WithCustomRound2(), None) # type: ignore -round(WithCustomRound2()) # type: ignore - - -class WithOverloadedRound: - @overload - def __round__(self, ndigits: None = ...) -> str: ... - - @overload - def __round__(self, ndigits: int) -> bytes: ... - - def __round__(self, ndigits: int | None = None) -> str | bytes: - return b"" if ndigits is None else "" - - -assert_type(round(WithOverloadedRound()), str) -assert_type(round(WithOverloadedRound(), None), str) -assert_type(round(WithOverloadedRound(), 1), bytes) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py deleted file mode 100644 index cda7eadbbe41..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_sum.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations - -from typing import Any, List, Literal, Union -from typing_extensions import assert_type - - -class Foo: - def __add__(self, other: Any) -> Foo: - return Foo() - - -class Bar: - def __radd__(self, other: Any) -> Bar: - return Bar() - - -class Baz: - def __add__(self, other: Any) -> Baz: - return Baz() - - def __radd__(self, other: Any) -> Baz: - return Baz() - - -literal_list: list[Literal[0, 1]] = [0, 1, 1] - -assert_type(sum([2, 4]), int) -assert_type(sum([3, 5], 4), int) - -assert_type(sum([True, False]), int) -assert_type(sum([True, False], True), int) -assert_type(sum(literal_list), int) - -assert_type(sum([["foo"], ["bar"]], ["baz"]), List[str]) - -assert_type(sum([Foo(), Foo()], Foo()), Foo) -assert_type(sum([Baz(), Baz()]), Union[Baz, Literal[0]]) - -# mypy and pyright infer the types differently for these, so we can't use assert_type -# Just test that no error is emitted for any of these -sum([("foo",), ("bar", "baz")], ()) # mypy: `tuple[str, ...]`; pyright: `tuple[()] | tuple[str] | tuple[str, str]` -sum([5.6, 3.2]) # mypy: `float`; pyright: `float | Literal[0]` -sum([2.5, 5.8], 5) # mypy: `float`; pyright: `float | int` - -# These all fail at runtime -sum("abcde") # type: ignore -sum([["foo"], ["bar"]]) # type: ignore -sum([("foo",), ("bar", "baz")]) # type: ignore -sum([Foo(), Foo()]) # type: ignore -sum([Bar(), Bar()], Bar()) # type: ignore -sum([Bar(), Bar()]) # type: ignore - -# TODO: these pass pyright with the current stubs, but mypy erroneously emits an error: -# sum([3, Fraction(7, 22), complex(8, 0), 9.83]) -# sum([3, Decimal('0.98')]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py b/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py deleted file mode 100644 index bc0d8db28389..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/builtins/check_tuple.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from typing import Tuple -from typing_extensions import assert_type - - -# Empty tuples, see #8275 -class TupleSub(Tuple[int, ...]): - pass - - -assert_type(TupleSub(), TupleSub) -assert_type(TupleSub([1, 2, 3]), TupleSub) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py b/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py deleted file mode 100644 index 19e663ceeaaf..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_codecs.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -import codecs -from typing_extensions import assert_type - -assert_type(codecs.decode("x", "unicode-escape"), str) -assert_type(codecs.decode(b"x", "unicode-escape"), str) - -assert_type(codecs.decode(b"x", "utf-8"), str) -codecs.decode("x", "utf-8") # type: ignore - -assert_type(codecs.decode("ab", "hex"), bytes) -assert_type(codecs.decode(b"ab", "hex"), bytes) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py b/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py deleted file mode 100644 index 962ec23c6b48..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_concurrent_futures.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Iterator -from concurrent.futures import Future, ThreadPoolExecutor, as_completed -from typing_extensions import assert_type - - -class Parent: ... - - -class Child(Parent): ... - - -def check_as_completed_covariance() -> None: - with ThreadPoolExecutor() as executor: - f1 = executor.submit(lambda: Parent()) - f2 = executor.submit(lambda: Child()) - fs: list[Future[Parent] | Future[Child]] = [f1, f2] - assert_type(as_completed(fs), Iterator[Future[Parent]]) - for future in as_completed(fs): - assert_type(future.result(), Parent) - - -def check_future_invariance() -> None: - def execute_callback(callback: Callable[[], Parent], future: Future[Parent]) -> None: - future.set_result(callback()) - - fut: Future[Child] = Future() - execute_callback(lambda: Parent(), fut) # type: ignore - assert isinstance(fut.result(), Child) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py deleted file mode 100644 index 648661bca856..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_contextlib.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from contextlib import ExitStack -from typing_extensions import assert_type - - -# See issue #7961 -class Thing(ExitStack): - pass - - -stack = ExitStack() -thing = Thing() -assert_type(stack.enter_context(Thing()), Thing) -assert_type(thing.enter_context(ExitStack()), ExitStack) - -with stack as cm: - assert_type(cm, ExitStack) -with thing as cm2: - assert_type(cm2, Thing) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py b/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py deleted file mode 100644 index 76ce8e1bd260..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_dataclasses.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -import dataclasses as dc -from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Tuple, Type, Union -from typing_extensions import Annotated, assert_type - -if TYPE_CHECKING: - from _typeshed import DataclassInstance - - -@dc.dataclass -class Foo: - attr: str - - -assert_type(dc.fields(Foo), Tuple[dc.Field[Any], ...]) - -# Mypy correctly emits errors on these -# due to the fact it's a dataclass class, not an instance. -# Pyright, however, handles ClassVar members in protocols differently. -# See https://github.com/microsoft/pyright/issues/4339 -# -# dc.asdict(Foo) -# dc.astuple(Foo) -# dc.replace(Foo) - -# See #9723 for why we can't make this assertion -# if dc.is_dataclass(Foo): -# assert_type(Foo, Type[Foo]) - -f = Foo(attr="attr") - -assert_type(dc.fields(f), Tuple[dc.Field[Any], ...]) -assert_type(dc.asdict(f), Dict[str, Any]) -assert_type(dc.astuple(f), Tuple[Any, ...]) -assert_type(dc.replace(f, attr="new"), Foo) - -if dc.is_dataclass(f): - # The inferred type doesn't change - # if it's already known to be a subtype of _DataclassInstance - assert_type(f, Foo) - - -def check_other_isdataclass_overloads(x: type, y: object) -> None: - # TODO: pyright correctly emits an error on this, but mypy does not -- why? - # dc.fields(x) - - dc.fields(y) # type: ignore - - dc.asdict(x) # type: ignore - dc.asdict(y) # type: ignore - - dc.astuple(x) # type: ignore - dc.astuple(y) # type: ignore - - dc.replace(x) # type: ignore - dc.replace(y) # type: ignore - - if dc.is_dataclass(x): - assert_type(x, Type["DataclassInstance"]) - assert_type(dc.fields(x), Tuple[dc.Field[Any], ...]) - - # Mypy correctly emits an error on these due to the fact - # that it's a dataclass class, not a dataclass instance. - # Pyright, however, handles ClassVar members in protocols differently. - # See https://github.com/microsoft/pyright/issues/4339 - # - # dc.asdict(x) - # dc.astuple(x) - # dc.replace(x) - - if dc.is_dataclass(y): - assert_type(y, Union["DataclassInstance", Type["DataclassInstance"]]) - assert_type(dc.fields(y), Tuple[dc.Field[Any], ...]) - - # Mypy correctly emits an error on these due to the fact we don't know - # whether it's a dataclass class or a dataclass instance. - # Pyright, however, handles ClassVar members in protocols differently. - # See https://github.com/microsoft/pyright/issues/4339 - # - # dc.asdict(y) - # dc.astuple(y) - # dc.replace(y) - - if dc.is_dataclass(y) and not isinstance(y, type): - assert_type(y, "DataclassInstance") - assert_type(dc.fields(y), Tuple[dc.Field[Any], ...]) - assert_type(dc.asdict(y), Dict[str, Any]) - assert_type(dc.astuple(y), Tuple[Any, ...]) - dc.replace(y) - - -# Regression test for #11653 -D = dc.make_dataclass( - "D", [("a", Union[int, None]), "y", ("z", Annotated[FrozenSet[bytes], "metadata"], dc.field(default=frozenset({b"foo"})))] -) -# Check that it's inferred by the type checker as a class object of some kind -# (but don't assert the exact type that `D` is inferred as, -# in case a type checker decides to add some special-casing for -# `make_dataclass` in the future) -assert_type(D.__mro__, Tuple[type, ...]) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py b/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py deleted file mode 100644 index 4ea4947c811d..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_enum.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -import enum -import sys -from typing import Literal, Type -from typing_extensions import assert_type - -A = enum.Enum("A", "spam eggs bacon") -B = enum.Enum("B", ["spam", "eggs", "bacon"]) -C = enum.Enum("Bar", [("spam", 1), ("eggs", 2), ("bacon", 3)]) -D = enum.Enum("Bar", {"spam": 1, "eggs": 2}) - -assert_type(A, Type[A]) -assert_type(B, Type[B]) -assert_type(C, Type[C]) -assert_type(D, Type[D]) - - -class EnumOfTuples(enum.Enum): - X = 1, 2, 3 - Y = 4, 5, 6 - - -assert_type(EnumOfTuples((1, 2, 3)), EnumOfTuples) - -# TODO: ideally this test would pass: -# -# if sys.version_info >= (3, 12): -# assert_type(EnumOfTuples(1, 2, 3), EnumOfTuples) - - -if sys.version_info >= (3, 11): - - class Foo(enum.StrEnum): - X = enum.auto() - - assert_type(Foo.X, Literal[Foo.X]) - assert_type(Foo.X.value, str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py b/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py deleted file mode 100644 index dca572683f8d..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_functools.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -from functools import cached_property, wraps -from typing import Callable, TypeVar -from typing_extensions import ParamSpec, assert_type - -P = ParamSpec("P") -T_co = TypeVar("T_co", covariant=True) - - -def my_decorator(func: Callable[P, T_co]) -> Callable[P, T_co]: - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> T_co: - print(args) - return func(*args, **kwargs) - - # verify that the wrapped function has all these attributes - wrapper.__annotations__ = func.__annotations__ - wrapper.__doc__ = func.__doc__ - wrapper.__module__ = func.__module__ - wrapper.__name__ = func.__name__ - wrapper.__qualname__ = func.__qualname__ - return wrapper - - -class A: - def __init__(self, x: int): - self.x = x - - @cached_property - def x(self) -> int: - return 0 - - -assert_type(A(x=1).x, int) - - -class B: - @cached_property - def x(self) -> int: - return 0 - - -def check_cached_property_settable(x: int) -> None: - b = B() - assert_type(b.x, int) - b.x = x - assert_type(b.x, int) - - -# https://github.com/python/typeshed/issues/10048 -class Parent: ... - - -class Child(Parent): ... - - -class X: - @cached_property - def some(self) -> Parent: - return Parent() - - -class Y(X): - @cached_property - def some(self) -> Child: # safe override - return Child() diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py deleted file mode 100644 index 17eefdafc971..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - -import importlib.abc -import importlib.util -import pathlib -import sys -import zipfile -from collections.abc import Sequence -from importlib.machinery import ModuleSpec -from types import ModuleType -from typing_extensions import Self - -# Assert that some Path classes are Traversable. -if sys.version_info >= (3, 9): - - def traverse(t: importlib.abc.Traversable) -> None: - pass - - traverse(pathlib.Path()) - traverse(zipfile.Path("")) - - -class MetaFinder: - @classmethod - def find_spec(cls, fullname: str, path: Sequence[str] | None, target: ModuleType | None = None) -> ModuleSpec | None: - return None # simplified mock for demonstration purposes only - - -class PathFinder: - @classmethod - def path_hook(cls, path_entry: str) -> type[Self]: - return cls # simplified mock for demonstration purposes only - - @classmethod - def find_spec(cls, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: - return None # simplified mock for demonstration purposes only - - -class Loader: - @classmethod - def load_module(cls, fullname: str) -> ModuleType: - return ModuleType(fullname) - - -sys.meta_path.append(MetaFinder) -sys.path_hooks.append(PathFinder.path_hook) -importlib.util.spec_from_loader("xxxx42xxxx", Loader) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py b/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py deleted file mode 100644 index f1322e16c54f..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_importlib_metadata.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -import sys -from _typeshed import StrPath -from os import PathLike -from pathlib import Path -from typing import Any -from zipfile import Path as ZipPath - -if sys.version_info >= (3, 10): - from importlib.metadata._meta import SimplePath - - # Simplified version of zipfile.Path - class MyPath: - @property - def parent(self) -> PathLike[str]: ... # undocumented - - def read_text(self, encoding: str | None = ..., errors: str | None = ...) -> str: ... - def joinpath(self, *other: StrPath) -> MyPath: ... - def __truediv__(self, add: StrPath) -> MyPath: ... - - if sys.version_info >= (3, 12): - - def takes_simple_path(p: SimplePath[Any]) -> None: ... - - else: - - def takes_simple_path(p: SimplePath) -> None: ... - - takes_simple_path(Path()) - takes_simple_path(ZipPath("")) - takes_simple_path(MyPath()) - takes_simple_path("some string") # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_io.py b/mypy/typeshed/stdlib/@tests/test_cases/check_io.py deleted file mode 100644 index abf84dd5a103..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_io.py +++ /dev/null @@ -1,6 +0,0 @@ -from gzip import GzipFile -from io import FileIO, TextIOWrapper - -TextIOWrapper(FileIO("")) -TextIOWrapper(FileIO(13)) -TextIOWrapper(GzipFile("")) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py b/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py deleted file mode 100644 index fe3d8eb16fd0..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_logging.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import logging -import logging.handlers -import multiprocessing -import queue -from typing import Any - -# This pattern comes from the logging docs, and should therefore pass a type checker -# See https://docs.python.org/3/library/logging.html#logrecord-objects - -old_factory = logging.getLogRecordFactory() - - -def record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord: - record = old_factory(*args, **kwargs) - record.custom_attribute = 0xDECAFBAD - return record - - -logging.setLogRecordFactory(record_factory) - -# The logging docs say that QueueHandler and QueueListener can take "any queue-like object" -# We test that here (regression test for #10168) -logging.handlers.QueueHandler(queue.Queue()) -logging.handlers.QueueHandler(queue.SimpleQueue()) -logging.handlers.QueueHandler(multiprocessing.Queue()) -logging.handlers.QueueListener(queue.Queue()) -logging.handlers.QueueListener(queue.SimpleQueue()) -logging.handlers.QueueListener(multiprocessing.Queue()) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py b/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py deleted file mode 100644 index 201f96c0c4c8..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_multiprocessing.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import annotations - -from ctypes import c_char, c_float -from multiprocessing import Array, Value -from multiprocessing.sharedctypes import Synchronized, SynchronizedString -from typing_extensions import assert_type - -string = Array(c_char, 12) -assert_type(string, SynchronizedString) -assert_type(string.value, bytes) - -field = Value(c_float, 0.0) -assert_type(field, Synchronized[float]) -field.value = 1.2 diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py b/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py deleted file mode 100644 index 0b52c3669d07..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_pathlib.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from pathlib import Path, PureWindowsPath - -if Path("asdf") == Path("asdf"): - ... - -# https://github.com/python/typeshed/issues/10661 -# Provide a true positive error when comparing Path to str -# mypy should report a comparison-overlap error with --strict-equality, -# and pyright should report a reportUnnecessaryComparison error -if Path("asdf") == "asdf": # type: ignore - ... - -# Errors on comparison here are technically false positives. However, this comparison is a little -# interesting: it can never hold true on Posix, but could hold true on Windows. We should experiment -# with more accurate __new__, such that we only get an error for such comparisons on platforms -# where they can never hold true. -if PureWindowsPath("asdf") == Path("asdf"): # type: ignore - ... diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_re.py b/mypy/typeshed/stdlib/@tests/test_cases/check_re.py deleted file mode 100644 index b6ab2b0d59d2..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_re.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import mmap -import re -import typing as t -from typing_extensions import assert_type - - -def check_search(str_pat: re.Pattern[str], bytes_pat: re.Pattern[bytes]) -> None: - assert_type(str_pat.search("x"), t.Optional[t.Match[str]]) - assert_type(bytes_pat.search(b"x"), t.Optional[t.Match[bytes]]) - assert_type(bytes_pat.search(bytearray(b"x")), t.Optional[t.Match[bytes]]) - assert_type(bytes_pat.search(mmap.mmap(0, 10)), t.Optional[t.Match[bytes]]) - - -def check_search_with_AnyStr(pattern: re.Pattern[t.AnyStr], string: t.AnyStr) -> re.Match[t.AnyStr]: - """See issue #9591""" - match = pattern.search(string) - if match is None: - raise ValueError(f"'{string!r}' does not match {pattern!r}") - return match - - -def check_no_ReadableBuffer_false_negatives() -> None: - re.compile("foo").search(bytearray(b"foo")) # type: ignore - re.compile("foo").search(mmap.mmap(0, 10)) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py b/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py deleted file mode 100644 index 3ec47ceccb90..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_sqlite3.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import sqlite3 -from typing_extensions import assert_type - - -class MyConnection(sqlite3.Connection): - pass - - -# Default return-type is Connection. -assert_type(sqlite3.connect(":memory:"), sqlite3.Connection) - -# Providing an alternate factory changes the return-type. -assert_type(sqlite3.connect(":memory:", factory=MyConnection), MyConnection) - -# Provides a true positive error. When checking the connect() function, -# mypy should report an arg-type error for the factory argument. -with sqlite3.connect(":memory:", factory=None) as con: # type: ignore - pass - -# The Connection class also accepts a `factory` arg but it does not affect -# the return-type. This use case is not idiomatic--connections should be -# established using the `connect()` function, not directly (as shown here). -assert_type(sqlite3.Connection(":memory:", factory=None), sqlite3.Connection) -assert_type(sqlite3.Connection(":memory:", factory=MyConnection), sqlite3.Connection) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py deleted file mode 100644 index 54510a3d7626..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_tarfile.py +++ /dev/null @@ -1,13 +0,0 @@ -import tarfile - -with tarfile.open("test.tar.xz", "w:xz") as tar: - pass - -# Test with valid preset values -tarfile.open("test.tar.xz", "w:xz", preset=0) -tarfile.open("test.tar.xz", "w:xz", preset=5) -tarfile.open("test.tar.xz", "w:xz", preset=9) - -# Test with invalid preset values -tarfile.open("test.tar.xz", "w:xz", preset=-1) # type: ignore -tarfile.open("test.tar.xz", "w:xz", preset=10) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py deleted file mode 100644 index c259c192a140..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_tempfile.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -import io -import sys -from tempfile import TemporaryFile, _TemporaryFileWrapper -from typing_extensions import assert_type - -if sys.platform == "win32": - assert_type(TemporaryFile(), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile("w+"), _TemporaryFileWrapper[str]) - assert_type(TemporaryFile("w+b"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile("wb"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile("rb"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile("wb", 0), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile(mode="w+"), _TemporaryFileWrapper[str]) - assert_type(TemporaryFile(mode="w+b"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile(mode="wb"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile(mode="rb"), _TemporaryFileWrapper[bytes]) - assert_type(TemporaryFile(buffering=0), _TemporaryFileWrapper[bytes]) -else: - assert_type(TemporaryFile(), io.BufferedRandom) - assert_type(TemporaryFile("w+"), io.TextIOWrapper) - assert_type(TemporaryFile("w+b"), io.BufferedRandom) - assert_type(TemporaryFile("wb"), io.BufferedWriter) - assert_type(TemporaryFile("rb"), io.BufferedReader) - assert_type(TemporaryFile("wb", 0), io.FileIO) - assert_type(TemporaryFile(mode="w+"), io.TextIOWrapper) - assert_type(TemporaryFile(mode="w+b"), io.BufferedRandom) - assert_type(TemporaryFile(mode="wb"), io.BufferedWriter) - assert_type(TemporaryFile(mode="rb"), io.BufferedReader) - assert_type(TemporaryFile(buffering=0), io.FileIO) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py b/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py deleted file mode 100644 index eddfc2549a64..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_threading.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import annotations - -import _threading_local -import threading - -loc = threading.local() -loc.foo = 42 -del loc.foo -loc.baz = ["spam", "eggs"] -del loc.baz - -l2 = _threading_local.local() -l2.asdfasdf = 56 -del l2.asdfasdf diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py b/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py deleted file mode 100644 index befac6697519..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_tkinter.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import tkinter -import traceback -import types - - -def custom_handler(exc: type[BaseException], val: BaseException, tb: types.TracebackType | None) -> None: - print("oh no") - - -root = tkinter.Tk() -root.report_callback_exception = traceback.print_exception -root.report_callback_exception = custom_handler - - -def foo(x: int, y: str) -> None: - pass - - -root.after(1000, foo, 10, "lol") -root.after(1000, foo, 10, 10) # type: ignore - - -# Font size must be integer -label = tkinter.Label() -label.config(font=("", 12)) -label.config(font=("", 12.34)) # type: ignore -label.config(font=("", 12, "bold")) -label.config(font=("", 12.34, "bold")) # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py b/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py deleted file mode 100644 index 40c6efaa8ca0..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_unittest.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import unittest -from collections.abc import Iterator, Mapping -from datetime import datetime, timedelta -from decimal import Decimal -from fractions import Fraction -from typing import TypedDict -from typing_extensions import assert_type -from unittest.mock import MagicMock, Mock, patch - -case = unittest.TestCase() - -### -# Tests for assertAlmostEqual -### - -case.assertAlmostEqual(1, 2.4) -case.assertAlmostEqual(2.4, 2.41) -case.assertAlmostEqual(Fraction(49, 50), Fraction(48, 50)) -case.assertAlmostEqual(3.14, complex(5, 6)) -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) -case.assertAlmostEqual(Decimal("1.1"), Decimal("1.11")) -case.assertAlmostEqual(2.4, 2.41, places=8) -case.assertAlmostEqual(2.4, 2.41, delta=0.02) -case.assertAlmostEqual(2.4, 2.41, None, "foo", 0.02) - -case.assertAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore -case.assertAlmostEqual("foo", "bar") # type: ignore -case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore -case.assertAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore -case.assertAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore - -### -# Tests for assertNotAlmostEqual -### - -case.assertAlmostEqual(1, 2.4) -case.assertNotAlmostEqual(Fraction(49, 50), Fraction(48, 50)) -case.assertAlmostEqual(3.14, complex(5, 6)) -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1)) -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1)) - -case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore -case.assertNotAlmostEqual("foo", "bar") # type: ignore -case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore -case.assertNotAlmostEqual(Decimal("0.4"), Fraction(1, 2)) # type: ignore -case.assertNotAlmostEqual(complex(2, 3), Decimal("0.9")) # type: ignore - -### -# Tests for assertGreater -### - - -class Spam: - def __lt__(self, other: object) -> bool: - return True - - -class Eggs: - def __gt__(self, other: object) -> bool: - return True - - -class Ham: - def __lt__(self, other: Ham) -> bool: - if not isinstance(other, Ham): - return NotImplemented - return True - - -class Bacon: - def __gt__(self, other: Bacon) -> bool: - if not isinstance(other, Bacon): - return NotImplemented - return True - - -case.assertGreater(5.8, 3) -case.assertGreater(Decimal("4.5"), Fraction(3, 2)) -case.assertGreater(Fraction(3, 2), 0.9) -case.assertGreater(Eggs(), object()) -case.assertGreater(object(), Spam()) -case.assertGreater(Ham(), Ham()) -case.assertGreater(Bacon(), Bacon()) - -case.assertGreater(object(), object()) # type: ignore -case.assertGreater(datetime(1999, 1, 2), 1) # type: ignore -case.assertGreater(Spam(), Eggs()) # type: ignore -case.assertGreater(Ham(), Bacon()) # type: ignore -case.assertGreater(Bacon(), Ham()) # type: ignore - - -### -# Tests for assertDictEqual -### - - -class TD1(TypedDict): - x: int - y: str - - -class TD2(TypedDict): - a: bool - b: bool - - -class MyMapping(Mapping[str, int]): - def __getitem__(self, __key: str) -> int: - return 42 - - def __iter__(self) -> Iterator[str]: - return iter([]) - - def __len__(self) -> int: - return 0 - - -td1: TD1 = {"x": 1, "y": "foo"} -td2: TD2 = {"a": True, "b": False} -m = MyMapping() - -case.assertDictEqual({}, {}) -case.assertDictEqual({"x": 1, "y": 2}, {"x": 1, "y": 2}) -case.assertDictEqual({"x": 1, "y": "foo"}, {"y": "foo", "x": 1}) -case.assertDictEqual({"x": 1}, {}) -case.assertDictEqual({}, {"x": 1}) -case.assertDictEqual({1: "x"}, {"y": 222}) -case.assertDictEqual({1: "x"}, td1) -case.assertDictEqual(td1, {1: "x"}) -case.assertDictEqual(td1, td2) - -case.assertDictEqual(1, {}) # type: ignore -case.assertDictEqual({}, 1) # type: ignore - -# These should fail, but don't due to TypedDict limitations: -# case.assertDictEqual(m, {"": 0}) # xtype: ignore -# case.assertDictEqual({"": 0}, m) # xtype: ignore - -### -# Tests for mock.patch -### - - -@patch("sys.exit") -def f_default_new(i: int, mock: MagicMock) -> str: - return "asdf" - - -@patch("sys.exit", new=42) -def f_explicit_new(i: int) -> str: - return "asdf" - - -assert_type(f_default_new(1), str) -f_default_new("a") # Not an error due to ParamSpec limitations -assert_type(f_explicit_new(1), str) -f_explicit_new("a") # type: ignore[arg-type] - - -@patch("sys.exit", new=Mock()) -class TestXYZ(unittest.TestCase): - attr: int = 5 - - @staticmethod - def method() -> int: - return 123 - - -assert_type(TestXYZ.attr, int) -assert_type(TestXYZ.method(), int) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py b/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py deleted file mode 100644 index b485dac8dc29..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/check_xml.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import sys -from typing_extensions import assert_type -from xml.dom.minidom import Document - -document = Document() - -assert_type(document.toxml(), str) -assert_type(document.toxml(encoding=None), str) -assert_type(document.toxml(encoding="UTF8"), bytes) -assert_type(document.toxml("UTF8"), bytes) -if sys.version_info >= (3, 9): - assert_type(document.toxml(standalone=True), str) - assert_type(document.toxml("UTF8", True), bytes) - assert_type(document.toxml(encoding="UTF8", standalone=True), bytes) - - -# Because toprettyxml can mix positional and keyword variants of the "encoding" argument, which -# determines the return type, the proper stub typing isn't immediately obvious. This is a basic -# brute-force sanity check. -# Test cases like toxml -assert_type(document.toprettyxml(), str) -assert_type(document.toprettyxml(encoding=None), str) -assert_type(document.toprettyxml(encoding="UTF8"), bytes) -if sys.version_info >= (3, 9): - assert_type(document.toprettyxml(standalone=True), str) - assert_type(document.toprettyxml(encoding="UTF8", standalone=True), bytes) -# Test cases unique to toprettyxml -assert_type(document.toprettyxml(" "), str) -assert_type(document.toprettyxml(" ", "\r\n"), str) -assert_type(document.toprettyxml(" ", "\r\n", "UTF8"), bytes) -if sys.version_info >= (3, 9): - assert_type(document.toprettyxml(" ", "\r\n", "UTF8", True), bytes) - assert_type(document.toprettyxml(" ", "\r\n", standalone=True), str) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py b/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py deleted file mode 100644 index 9fe5ec8076ce..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/collections/check_defaultdict-py39.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Tests for `defaultdict.__or__` and `defaultdict.__ror__`. -These methods were only added in py39. -""" - -from __future__ import annotations - -import os -import sys -from collections import defaultdict -from typing import Mapping, TypeVar, Union -from typing_extensions import Self, assert_type - -_KT = TypeVar("_KT") -_VT = TypeVar("_VT") - - -if sys.version_info >= (3, 9): - - class CustomDefaultDictSubclass(defaultdict[_KT, _VT]): - pass - - class CustomMappingWithDunderOr(Mapping[_KT, _VT]): - def __or__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: - return {} - - def __ror__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]: - return {} - - def __ior__(self, other: Mapping[_KT, _VT]) -> Self: - return self - - def test_defaultdict_dot_or( - a: defaultdict[int, int], - b: CustomDefaultDictSubclass[int, int], - c: defaultdict[str, str], - d: Mapping[int, int], - e: CustomMappingWithDunderOr[str, str], - ) -> None: - assert_type(a | b, defaultdict[int, int]) - - # In contrast to `dict.__or__`, `defaultdict.__or__` returns `Self` if called on a subclass of `defaultdict`: - assert_type(b | a, CustomDefaultDictSubclass[int, int]) - - assert_type(a | c, defaultdict[Union[int, str], Union[int, str]]) - - # arbitrary mappings are not accepted by `defaultdict.__or__`; - # it has to be a subclass of `dict` - a | d # type: ignore - - # but Mappings such as `os._Environ` or `CustomMappingWithDunderOr`, - # which define `__ror__` methods that accept `dict`, are fine - # (`os._Environ.__(r)or__` always returns `dict`, even if a `defaultdict` is passed): - assert_type(a | os.environ, dict[Union[str, int], Union[str, int]]) - assert_type(os.environ | a, dict[Union[str, int], Union[str, int]]) - - assert_type(c | os.environ, dict[str, str]) - assert_type(c | e, dict[str, str]) - - assert_type(os.environ | c, dict[str, str]) - assert_type(e | c, dict[str, str]) - - e |= c - e |= a # type: ignore - - # TODO: this test passes mypy, but fails pyright for some reason: - # c |= e - - c |= a # type: ignore diff --git a/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py b/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py deleted file mode 100644 index a9b43e23fb27..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/email/check_message.py +++ /dev/null @@ -1,6 +0,0 @@ -from email.headerregistry import Address -from email.message import EmailMessage - -msg = EmailMessage() -msg["To"] = "receiver@example.com" -msg["From"] = Address("Sender Name", "sender", "example.com") diff --git a/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py b/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py deleted file mode 100644 index c45ffee28cee..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/itertools/check_itertools_recipes.py +++ /dev/null @@ -1,410 +0,0 @@ -"""Type-annotated versions of the recipes from the itertools docs. - -These are all meant to be examples of idiomatic itertools usage, -so they should all type-check without error. -""" - -from __future__ import annotations - -import collections -import math -import operator -import sys -from itertools import chain, combinations, count, cycle, filterfalse, groupby, islice, product, repeat, starmap, tee, zip_longest -from typing import ( - Any, - Callable, - Collection, - Hashable, - Iterable, - Iterator, - Literal, - Sequence, - Tuple, - Type, - TypeVar, - Union, - overload, -) -from typing_extensions import TypeAlias, TypeVarTuple, Unpack - -_T = TypeVar("_T") -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") -_HashableT = TypeVar("_HashableT", bound=Hashable) -_Ts = TypeVarTuple("_Ts") - - -def take(n: int, iterable: Iterable[_T]) -> list[_T]: - "Return first n items of the iterable as a list" - return list(islice(iterable, n)) - - -# Note: the itertools docs uses the parameter name "iterator", -# but the function actually accepts any iterable -# as its second argument -def prepend(value: _T1, iterator: Iterable[_T2]) -> Iterator[_T1 | _T2]: - "Prepend a single value in front of an iterator" - # prepend(1, [2, 3, 4]) --> 1 2 3 4 - return chain([value], iterator) - - -def tabulate(function: Callable[[int], _T], start: int = 0) -> Iterator[_T]: - "Return function(0), function(1), ..." - return map(function, count(start)) - - -def repeatfunc(func: Callable[[Unpack[_Ts]], _T], times: int | None = None, *args: Unpack[_Ts]) -> Iterator[_T]: - """Repeat calls to func with specified arguments. - - Example: repeatfunc(random.random) - """ - if times is None: - return starmap(func, repeat(args)) - return starmap(func, repeat(args, times)) - - -def flatten(list_of_lists: Iterable[Iterable[_T]]) -> Iterator[_T]: - "Flatten one level of nesting" - return chain.from_iterable(list_of_lists) - - -def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: - "Returns the sequence elements n times" - return chain.from_iterable(repeat(tuple(iterable), n)) - - -def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: - "Return an iterator over the last n items" - # tail(3, 'ABCDEFG') --> E F G - return iter(collections.deque(iterable, maxlen=n)) - - -# This function *accepts* any iterable, -# but it only *makes sense* to use it with an iterator -def consume(iterator: Iterator[object], n: int | None = None) -> None: - "Advance the iterator n-steps ahead. If n is None, consume entirely." - # Use functions that consume iterators at C speed. - if n is None: - # feed the entire iterator into a zero-length deque - collections.deque(iterator, maxlen=0) - else: - # advance to the empty slice starting at position n - next(islice(iterator, n, n), None) - - -@overload -def nth(iterable: Iterable[_T], n: int, default: None = None) -> _T | None: ... - - -@overload -def nth(iterable: Iterable[_T], n: int, default: _T1) -> _T | _T1: ... - - -def nth(iterable: Iterable[object], n: int, default: object = None) -> object: - "Returns the nth item or a default value" - return next(islice(iterable, n, None), default) - - -@overload -def quantify(iterable: Iterable[object]) -> int: ... - - -@overload -def quantify(iterable: Iterable[_T], pred: Callable[[_T], bool]) -> int: ... - - -def quantify(iterable: Iterable[object], pred: Callable[[Any], bool] = bool) -> int: - "Given a predicate that returns True or False, count the True results." - return sum(map(pred, iterable)) - - -@overload -def first_true( - iterable: Iterable[_T], default: Literal[False] = False, pred: Callable[[_T], bool] | None = None -) -> _T | Literal[False]: ... - - -@overload -def first_true(iterable: Iterable[_T], default: _T1, pred: Callable[[_T], bool] | None = None) -> _T | _T1: ... - - -def first_true(iterable: Iterable[object], default: object = False, pred: Callable[[Any], bool] | None = None) -> object: - """Returns the first true value in the iterable. - If no true value is found, returns *default* - If *pred* is not None, returns the first item - for which pred(item) is true. - """ - # first_true([a,b,c], x) --> a or b or c or x - # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x - return next(filter(pred, iterable), default) - - -_ExceptionOrExceptionTuple: TypeAlias = Union[Type[BaseException], Tuple[Type[BaseException], ...]] - - -@overload -def iter_except(func: Callable[[], _T], exception: _ExceptionOrExceptionTuple, first: None = None) -> Iterator[_T]: ... - - -@overload -def iter_except( - func: Callable[[], _T], exception: _ExceptionOrExceptionTuple, first: Callable[[], _T1] -) -> Iterator[_T | _T1]: ... - - -def iter_except( - func: Callable[[], object], exception: _ExceptionOrExceptionTuple, first: Callable[[], object] | None = None -) -> Iterator[object]: - """Call a function repeatedly until an exception is raised. - Converts a call-until-exception interface to an iterator interface. - Like builtins.iter(func, sentinel) but uses an exception instead - of a sentinel to end the loop. - Examples: - iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator - iter_except(d.popitem, KeyError) # non-blocking dict iterator - iter_except(d.popleft, IndexError) # non-blocking deque iterator - iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue - iter_except(s.pop, KeyError) # non-blocking set iterator - """ - try: - if first is not None: - yield first() # For database APIs needing an initial cast to db.first() - while True: - yield func() - except exception: - pass - - -def sliding_window(iterable: Iterable[_T], n: int) -> Iterator[tuple[_T, ...]]: - # sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG - it = iter(iterable) - window = collections.deque(islice(it, n - 1), maxlen=n) - for x in it: - window.append(x) - yield tuple(window) - - -def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: - "roundrobin('ABC', 'D', 'EF') --> A D E B F C" - # Recipe credited to George Sakkis - num_active = len(iterables) - nexts: Iterator[Callable[[], _T]] = cycle(iter(it).__next__ for it in iterables) - while num_active: - try: - for next in nexts: - yield next() - except StopIteration: - # Remove the iterator we just exhausted from the cycle. - num_active -= 1 - nexts = cycle(islice(nexts, num_active)) - - -def partition(pred: Callable[[_T], bool], iterable: Iterable[_T]) -> tuple[Iterator[_T], Iterator[_T]]: - """Partition entries into false entries and true entries. - If *pred* is slow, consider wrapping it with functools.lru_cache(). - """ - # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 - t1, t2 = tee(iterable) - return filterfalse(pred, t1), filter(pred, t2) - - -def subslices(seq: Sequence[_T]) -> Iterator[Sequence[_T]]: - "Return all contiguous non-empty subslices of a sequence" - # subslices('ABCD') --> A AB ABC ABCD B BC BCD C CD D - slices = starmap(slice, combinations(range(len(seq) + 1), 2)) - return map(operator.getitem, repeat(seq), slices) - - -def before_and_after(predicate: Callable[[_T], bool], it: Iterable[_T]) -> tuple[Iterator[_T], Iterator[_T]]: - """Variant of takewhile() that allows complete - access to the remainder of the iterator. - >>> it = iter('ABCdEfGhI') - >>> all_upper, remainder = before_and_after(str.isupper, it) - >>> ''.join(all_upper) - 'ABC' - >>> ''.join(remainder) # takewhile() would lose the 'd' - 'dEfGhI' - Note that the first iterator must be fully - consumed before the second iterator can - generate valid results. - """ - it = iter(it) - transition: list[_T] = [] - - def true_iterator() -> Iterator[_T]: - for elem in it: - if predicate(elem): - yield elem - else: - transition.append(elem) - return - - def remainder_iterator() -> Iterator[_T]: - yield from transition - yield from it - - return true_iterator(), remainder_iterator() - - -@overload -def unique_everseen(iterable: Iterable[_HashableT], key: None = None) -> Iterator[_HashableT]: ... - - -@overload -def unique_everseen(iterable: Iterable[_T], key: Callable[[_T], Hashable]) -> Iterator[_T]: ... - - -def unique_everseen(iterable: Iterable[_T], key: Callable[[_T], Hashable] | None = None) -> Iterator[_T]: - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBcCAD', str.lower) --> A B c D - seen: set[Hashable] = set() - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen.add(element) - yield element - # For order preserving deduplication, - # a faster but non-lazy solution is: - # yield from dict.fromkeys(iterable) - else: - for element in iterable: - k = key(element) - if k not in seen: - seen.add(k) - yield element - # For use cases that allow the last matching element to be returned, - # a faster but non-lazy solution is: - # t1, t2 = tee(iterable) - # yield from dict(zip(map(key, t1), t2)).values() - - -# Slightly adapted from the docs recipe; a one-liner was a bit much for pyright -def unique_justseen(iterable: Iterable[_T], key: Callable[[_T], bool] | None = None) -> Iterator[_T]: - "List unique elements, preserving order. Remember only the element just seen." - # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B - # unique_justseen('ABBcCAD', str.lower) --> A B c A D - g: groupby[_T | bool, _T] = groupby(iterable, key) - return map(next, map(operator.itemgetter(1), g)) - - -def powerset(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: - "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" - s = list(iterable) - return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) - - -def polynomial_derivative(coefficients: Sequence[float]) -> list[float]: - """Compute the first derivative of a polynomial. - f(x) = x³ -4x² -17x + 60 - f'(x) = 3x² -8x -17 - """ - # polynomial_derivative([1, -4, -17, 60]) -> [3, -8, -17] - n = len(coefficients) - powers = reversed(range(1, n)) - return list(map(operator.mul, coefficients, powers)) - - -def nth_combination(iterable: Iterable[_T], r: int, index: int) -> tuple[_T, ...]: - "Equivalent to list(combinations(iterable, r))[index]" - pool = tuple(iterable) - n = len(pool) - c = math.comb(n, r) - if index < 0: - index += c - if index < 0 or index >= c: - raise IndexError - result: list[_T] = [] - while r: - c, n, r = c * r // n, n - 1, r - 1 - while index >= c: - index -= c - c, n = c * (n - r) // n, n - 1 - result.append(pool[-1 - n]) - return tuple(result) - - -if sys.version_info >= (3, 10): - - @overload - def grouper( - iterable: Iterable[_T], n: int, *, incomplete: Literal["fill"] = "fill", fillvalue: None = None - ) -> Iterator[tuple[_T | None, ...]]: ... - - @overload - def grouper( - iterable: Iterable[_T], n: int, *, incomplete: Literal["fill"] = "fill", fillvalue: _T1 - ) -> Iterator[tuple[_T | _T1, ...]]: ... - - @overload - def grouper( - iterable: Iterable[_T], n: int, *, incomplete: Literal["strict", "ignore"], fillvalue: None = None - ) -> Iterator[tuple[_T, ...]]: ... - - def grouper( - iterable: Iterable[object], n: int, *, incomplete: Literal["fill", "strict", "ignore"] = "fill", fillvalue: object = None - ) -> Iterator[tuple[object, ...]]: - "Collect data into non-overlapping fixed-length chunks or blocks" - # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx - # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError - # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF - args = [iter(iterable)] * n - if incomplete == "fill": - return zip_longest(*args, fillvalue=fillvalue) - if incomplete == "strict": - return zip(*args, strict=True) - if incomplete == "ignore": - return zip(*args) - else: - raise ValueError("Expected fill, strict, or ignore") - - def transpose(it: Iterable[Iterable[_T]]) -> Iterator[tuple[_T, ...]]: - "Swap the rows and columns of the input." - # transpose([(1, 2, 3), (11, 22, 33)]) --> (1, 11) (2, 22) (3, 33) - return zip(*it, strict=True) - - -if sys.version_info >= (3, 12): - from itertools import batched - - def sum_of_squares(it: Iterable[float]) -> float: - "Add up the squares of the input values." - # sum_of_squares([10, 20, 30]) -> 1400 - return math.sumprod(*tee(it)) - - def convolve(signal: Iterable[float], kernel: Iterable[float]) -> Iterator[float]: - """Discrete linear convolution of two iterables. - The kernel is fully consumed before the calculations begin. - The signal is consumed lazily and can be infinite. - Convolutions are mathematically commutative. - If the signal and kernel are swapped, - the output will be the same. - Article: https://betterexplained.com/articles/intuitive-convolution/ - Video: https://www.youtube.com/watch?v=KuXjwB4LzSA - """ - # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) - # convolve(data, [1/2, 0, -1/2]) --> 1st derivative estimate - # convolve(data, [1, -2, 1]) --> 2nd derivative estimate - kernel = tuple(kernel)[::-1] - n = len(kernel) - padded_signal = chain(repeat(0, n - 1), signal, repeat(0, n - 1)) - windowed_signal = sliding_window(padded_signal, n) - return map(math.sumprod, repeat(kernel), windowed_signal) - - def polynomial_eval(coefficients: Sequence[float], x: float) -> float: - """Evaluate a polynomial at a specific value. - Computes with better numeric stability than Horner's method. - """ - # Evaluate x³ -4x² -17x + 60 at x = 2.5 - # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125 - n = len(coefficients) - if not n: - return type(x)(0) - powers = map(pow, repeat(x), reversed(range(n))) - return math.sumprod(coefficients, powers) - - def matmul(m1: Sequence[Collection[float]], m2: Sequence[Collection[float]]) -> Iterator[tuple[float, ...]]: - "Multiply two matrices." - # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) --> (49, 80), (41, 60) - n = len(m2[0]) - return batched(starmap(math.sumprod, product(m1, transpose(m2))), n) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py deleted file mode 100644 index 10a33ffb83d5..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_MutableMapping.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from typing import Any, Union -from typing_extensions import assert_type - - -def check_setdefault_method() -> None: - d: dict[int, str] = {} - d2: dict[int, str | None] = {} - d3: dict[int, Any] = {} - - d.setdefault(1) # type: ignore - assert_type(d.setdefault(1, "x"), str) - assert_type(d2.setdefault(1), Union[str, None]) - assert_type(d2.setdefault(1, None), Union[str, None]) - assert_type(d2.setdefault(1, "x"), Union[str, None]) - assert_type(d3.setdefault(1), Union[Any, None]) - assert_type(d3.setdefault(1, "x"), Any) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py deleted file mode 100644 index 44eb548e04a9..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_all.py +++ /dev/null @@ -1,14 +0,0 @@ -# pyright: reportWildcardImportFromLibrary=false -""" -This tests that star imports work when using "all += " syntax. -""" -from __future__ import annotations - -import sys -from typing import * -from zipfile import * - -if sys.version_info >= (3, 9): - x: Annotated[int, 42] - -p: Path diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py deleted file mode 100644 index 34c5631aeb1a..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_regression_issue_9296.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -import typing as t - -KT = t.TypeVar("KT") - - -class MyKeysView(t.KeysView[KT]): - pass - - -d: dict[t.Any, t.Any] = {} -dict_keys = type(d.keys()) - -# This should not cause an error like `Member "register" is unknown`: -MyKeysView.register(dict_keys) diff --git a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py b/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py deleted file mode 100644 index 67f16dc91765..000000000000 --- a/mypy/typeshed/stdlib/@tests/test_cases/typing/check_typing_io.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -import mmap -from typing import IO, AnyStr - - -def check_write(io_bytes: IO[bytes], io_str: IO[str], io_anystr: IO[AnyStr], any_str: AnyStr, buf: mmap.mmap) -> None: - io_bytes.write(b"") - io_bytes.write(buf) - io_bytes.write("") # type: ignore - io_bytes.write(any_str) # type: ignore - - io_str.write(b"") # type: ignore - io_str.write(buf) # type: ignore - io_str.write("") - io_str.write(any_str) # type: ignore - - io_anystr.write(b"") # type: ignore - io_anystr.write(buf) # type: ignore - io_anystr.write("") # type: ignore - io_anystr.write(any_str) From b74829e1c6fbbfd376fe1043a7ce37e3f120f799 Mon Sep 17 00:00:00 2001 From: gilesgc <50000301+gilesgc@users.noreply.github.com> Date: Thu, 16 May 2024 21:45:57 -0400 Subject: [PATCH 0580/1617] Fix for type narrowing of negative integer literals (#17256) Fixes #10514 Fixes #17118 Negative integer literals were not being correctly handled in the type narrowing process, causing mypy errors such as "Statement is unreachable" despite the checked code being valid. This fix ensures that negative integer literals are properly considered in if-statements. --- mypy/plugins/default.py | 2 +- test-data/unit/check-narrowing.test | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 93fff5320cd5..170d3c85b5f9 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -489,7 +489,7 @@ def int_neg_callback(ctx: MethodContext, multiplier: int = -1) -> Type: return ctx.type.copy_modified( last_known_value=LiteralType( value=multiplier * value, - fallback=ctx.type, + fallback=fallback, line=ctx.type.line, column=ctx.type.column, ) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 4d117687554e..8612df9bc663 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2089,3 +2089,28 @@ if isinstance(x, (Z, NoneType)): # E: Subclass of "X" and "Z" cannot exist: "Z" reveal_type(x) # E: Statement is unreachable [builtins fixtures/isinstance.pyi] + +[case testTypeNarrowingReachableNegative] +# flags: --warn-unreachable +from typing import Literal + +x: Literal[-1] + +if x == -1: + assert True + +[typing fixtures/typing-medium.pyi] +[builtins fixtures/ops.pyi] + +[case testTypeNarrowingReachableNegativeUnion] +from typing import Literal + +x: Literal[-1, 1] + +if x == -1: + reveal_type(x) # N: Revealed type is "Literal[-1]" +else: + reveal_type(x) # N: Revealed type is "Literal[1]" + +[typing fixtures/typing-medium.pyi] +[builtins fixtures/ops.pyi] From 5fb8d6262f2ade83234e46334eb3fb8a4bbaedc0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 17 May 2024 11:23:20 +0100 Subject: [PATCH 0581/1617] [PEP 695] Partial support for new type parameter syntax in Python 3.12 (#17233) Add basic support for most features of PEP 695. It's still not generally useful, but it should be enough for experimentation and testing. I will continue working on follow-up PRs after this has been merged. This is currently behind a feature flag: `--enable-incomplete-feature=NewGenericSyntax` These features, among other things, are unimplemented (or at least untested): * Recursive type aliases * Checking for various errors * Inference of variance in complex cases * Dealing with unknown variance consistently * Scoping * Mypy daemon * Compilation using mypyc The trickiest remaining thing is probably variance inference in cases where some types aren't ready (i.e. not inferred) when we need variance. I have some ideas about how to tackle this, but it might need significant work. Currently the idea is to infer variance on demand when we need it, but we may need to defer if variance can't be calculated, for example if a type of an attribute is not yet ready. The current approach is to fall back to covariance in some cases, which is not ideal. Work on #15238. --- mypy/checker.py | 11 +- mypy/fastparse.py | 106 +++- mypy/join.py | 4 +- mypy/nodes.py | 54 +- mypy/options.py | 3 +- mypy/partially_defined.py | 5 + mypy/semanal.py | 174 +++++- mypy/strconv.py | 28 + mypy/subtypes.py | 83 ++- mypy/test/testparse.py | 4 + mypy/traverser.py | 6 + mypy/typestate.py | 10 +- mypy/visitor.py | 7 + mypyc/irbuild/visitor.py | 4 + test-data/unit/check-python312.test | 876 ++++++++++++++++++++++++++++ test-data/unit/parse-python312.test | 87 +++ test-data/unit/pythoneval.test | 21 + 17 files changed, 1441 insertions(+), 42 deletions(-) create mode 100644 test-data/unit/parse-python312.test diff --git a/mypy/checker.py b/mypy/checker.py index 9c10cd2fc30d..3daf64daaac4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -146,6 +146,7 @@ from mypy.state import state from mypy.subtypes import ( find_member, + infer_class_variances, is_callable_compatible, is_equivalent, is_more_precise, @@ -2374,7 +2375,7 @@ def visit_class_def(self, defn: ClassDef) -> None: self.allow_abstract_call = old_allow_abstract_call # TODO: Apply the sig to the actual TypeInfo so we can handle decorators # that completely swap out the type. (e.g. Callable[[Type[A]], Type[B]]) - if typ.defn.type_vars: + if typ.defn.type_vars and typ.defn.type_args is None: for base_inst in typ.bases: for base_tvar, base_decl_tvar in zip( base_inst.args, base_inst.type.defn.type_vars @@ -2396,6 +2397,7 @@ def visit_class_def(self, defn: ClassDef) -> None: self.check_protocol_variance(defn) if not defn.has_incompatible_baseclass and defn.info.is_enum: self.check_enum(defn) + infer_class_variances(defn.info) def check_final_deletable(self, typ: TypeInfo) -> None: # These checks are only for mypyc. Only perform some checks that are easier @@ -2566,6 +2568,9 @@ def check_protocol_variance(self, defn: ClassDef) -> None: if they are actually covariant/contravariant, since this may break transitivity of subtyping, see PEP 544. """ + if defn.type_args is not None: + # Using new-style syntax (PEP 695), so variance will be inferred + return info = defn.info object_type = Instance(info.mro[-1], []) tvars = info.defn.type_vars @@ -3412,8 +3417,8 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp if ( lv.node.final_unset_in_class and not lv.node.final_set_in_init - and not self.is_stub - and # It is OK to skip initializer in stub files. + and not self.is_stub # It is OK to skip initializer in stub files. + and # Avoid extra error messages, if there is no type in Final[...], # then we already reported the error about missing r.h.s. isinstance(s, AssignmentStmt) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index e208e4d0b7d9..ee042b96339f 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -17,6 +17,9 @@ ARG_POS, ARG_STAR, ARG_STAR2, + PARAM_SPEC_KIND, + TYPE_VAR_KIND, + TYPE_VAR_TUPLE_KIND, ArgKind, Argument, AssertStmt, @@ -79,6 +82,8 @@ TempNode, TryStmt, TupleExpr, + TypeAliasStmt, + TypeParam, UnaryExpr, Var, WhileStmt, @@ -87,7 +92,7 @@ YieldFromExpr, check_arg_names, ) -from mypy.options import Options +from mypy.options import NEW_GENERIC_SYNTAX, Options from mypy.patterns import ( AsPattern, ClassPattern, @@ -144,11 +149,6 @@ def ast3_parse( NamedExpr = ast3.NamedExpr Constant = ast3.Constant -if sys.version_info >= (3, 12): - ast_TypeAlias = ast3.TypeAlias -else: - ast_TypeAlias = Any - if sys.version_info >= (3, 10): Match = ast3.Match MatchValue = ast3.MatchValue @@ -171,11 +171,21 @@ def ast3_parse( MatchAs = Any MatchOr = Any AstNode = Union[ast3.expr, ast3.stmt, ast3.ExceptHandler] + if sys.version_info >= (3, 11): TryStar = ast3.TryStar else: TryStar = Any +if sys.version_info >= (3, 12): + ast_TypeAlias = ast3.TypeAlias + ast_ParamSpec = ast3.ParamSpec + ast_TypeVarTuple = ast3.TypeVarTuple +else: + ast_TypeAlias = Any + ast_ParamSpec = Any + ast_TypeVarTuple = Any + N = TypeVar("N", bound=Node) # There is no way to create reasonable fallbacks at this stage, @@ -884,6 +894,8 @@ def do_func_def( arg_kinds = [arg.kind for arg in args] arg_names = [None if arg.pos_only else arg.variable.name for arg in args] + # Type parameters, if using new syntax for generics (PEP 695) + explicit_type_params: list[TypeParam] | None = None arg_types: list[Type | None] = [] if no_type_check: @@ -937,12 +949,17 @@ def do_func_def( return_type = AnyType(TypeOfAny.from_error) else: if sys.version_info >= (3, 12) and n.type_params: - self.fail( - ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: + explicit_type_params = self.translate_type_params(n.type_params) + else: + self.fail( + ErrorMessage( + "PEP 695 generics are not yet supported", code=codes.VALID_TYPE + ), + n.type_params[0].lineno, + n.type_params[0].col_offset, + blocker=False, + ) arg_types = [a.type_annotation for a in args] return_type = TypeConverter( @@ -986,7 +1003,7 @@ def do_func_def( self.class_and_function_stack.pop() self.class_and_function_stack.append("F") body = self.as_required_block(n.body, can_strip=True, is_coroutine=is_coroutine) - func_def = FuncDef(n.name, args, body, func_type) + func_def = FuncDef(n.name, args, body, func_type, explicit_type_params) if isinstance(func_def.type, CallableType): # semanal.py does some in-place modifications we want to avoid func_def.unanalyzed_type = func_def.type.copy_modified() @@ -1120,13 +1137,19 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.class_and_function_stack.append("C") keywords = [(kw.arg, self.visit(kw.value)) for kw in n.keywords if kw.arg] + # Type parameters, if using new syntax for generics (PEP 695) + explicit_type_params: list[TypeParam] | None = None + if sys.version_info >= (3, 12) and n.type_params: - self.fail( - ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: + explicit_type_params = self.translate_type_params(n.type_params) + else: + self.fail( + ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), + n.type_params[0].lineno, + n.type_params[0].col_offset, + blocker=False, + ) cdef = ClassDef( n.name, @@ -1135,6 +1158,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.translate_expr_list(n.bases), metaclass=dict(keywords).get("metaclass"), keywords=keywords, + type_args=explicit_type_params, ) cdef.decorators = self.translate_expr_list(n.decorator_list) # Set lines to match the old mypy 0.700 lines, in order to keep @@ -1150,6 +1174,24 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.class_and_function_stack.pop() return cdef + def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: + explicit_type_params = [] + for p in type_params: + bound = None + values: list[Type] = [] + if isinstance(p, ast_ParamSpec): # type: ignore[misc] + explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [])) + elif isinstance(p, ast_TypeVarTuple): # type: ignore[misc] + explicit_type_params.append(TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [])) + else: + if isinstance(p.bound, ast3.Tuple): + conv = TypeConverter(self.errors, line=p.lineno) + values = [conv.visit(t) for t in p.bound.elts] + elif p.bound is not None: + bound = TypeConverter(self.errors, line=p.lineno).visit(p.bound) + explicit_type_params.append(TypeParam(p.name, TYPE_VAR_KIND, bound, values)) + return explicit_type_params + # Return(expr? value) def visit_Return(self, n: ast3.Return) -> ReturnStmt: node = ReturnStmt(self.visit(n.value)) @@ -1735,15 +1777,23 @@ def visit_MatchOr(self, n: MatchOr) -> OrPattern: node = OrPattern([self.visit(pattern) for pattern in n.patterns]) return self.set_line(node, n) - def visit_TypeAlias(self, n: ast_TypeAlias) -> AssignmentStmt: - self.fail( - ErrorMessage("PEP 695 type aliases are not yet supported", code=codes.VALID_TYPE), - n.lineno, - n.col_offset, - blocker=False, - ) - node = AssignmentStmt([NameExpr(n.name.id)], self.visit(n.value)) - return self.set_line(node, n) + # TypeAlias(identifier name, type_param* type_params, expr value) + def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: + node: TypeAliasStmt | AssignmentStmt + if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: + type_params = self.translate_type_params(n.type_params) + value = self.visit(n.value) + node = TypeAliasStmt(self.visit_Name(n.name), type_params, value) + return self.set_line(node, n) + else: + self.fail( + ErrorMessage("PEP 695 type aliases are not yet supported", code=codes.VALID_TYPE), + n.lineno, + n.col_offset, + blocker=False, + ) + node = AssignmentStmt([NameExpr(n.name.id)], self.visit(n.value)) + return self.set_line(node, n) class TypeConverter: diff --git a/mypy/join.py b/mypy/join.py index 3603e9fefb7a..7e0ff301ebf8 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -6,7 +6,7 @@ import mypy.typeops from mypy.maptype import map_instance_to_supertype -from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT +from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY from mypy.state import state from mypy.subtypes import ( SubtypeContext, @@ -97,7 +97,7 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: elif isinstance(sa_proper, AnyType): new_type = AnyType(TypeOfAny.from_another_any, sa_proper) elif isinstance(type_var, TypeVarType): - if type_var.variance == COVARIANT: + if type_var.variance in (COVARIANT, VARIANCE_NOT_READY): new_type = join_types(ta, sa, self) if len(type_var.values) != 0 and new_type not in type_var.values: self.seen_instances.pop() diff --git a/mypy/nodes.py b/mypy/nodes.py index bb278d92392d..4c83d8081f6c 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -653,6 +653,28 @@ def set_line( self.variable.set_line(self.line, self.column, self.end_line, self.end_column) +# These specify the kind of a TypeParam +TYPE_VAR_KIND: Final = 0 +PARAM_SPEC_KIND: Final = 1 +TYPE_VAR_TUPLE_KIND: Final = 2 + + +class TypeParam: + __slots__ = ("name", "kind", "upper_bound", "values") + + def __init__( + self, + name: str, + kind: int, + upper_bound: mypy.types.Type | None, + values: list[mypy.types.Type], + ) -> None: + self.name = name + self.kind = kind + self.upper_bound = upper_bound + self.values = values + + FUNCITEM_FLAGS: Final = FUNCBASE_FLAGS + [ "is_overload", "is_generator", @@ -672,6 +694,7 @@ class FuncItem(FuncBase): "min_args", # Minimum number of arguments "max_pos", # Maximum number of positional arguments, -1 if no explicit # limit (*args not included) + "type_args", # New-style type parameters (PEP 695) "body", # Body of the function "is_overload", # Is this an overload variant of function with more than # one overload variant? @@ -689,12 +712,14 @@ def __init__( arguments: list[Argument] | None = None, body: Block | None = None, typ: mypy.types.FunctionLike | None = None, + type_args: list[TypeParam] | None = None, ) -> None: super().__init__() self.arguments = arguments or [] self.arg_names = [None if arg.pos_only else arg.variable.name for arg in self.arguments] self.arg_kinds: list[ArgKind] = [arg.kind for arg in self.arguments] self.max_pos: int = self.arg_kinds.count(ARG_POS) + self.arg_kinds.count(ARG_OPT) + self.type_args: list[TypeParam] | None = type_args self.body: Block = body or Block([]) self.type = typ self.unanalyzed_type = typ @@ -761,8 +786,9 @@ def __init__( arguments: list[Argument] | None = None, body: Block | None = None, typ: mypy.types.FunctionLike | None = None, + type_args: list[TypeParam] | None = None, ) -> None: - super().__init__(arguments, body, typ) + super().__init__(arguments, body, typ, type_args) self._name = name self.is_decorated = False self.is_conditional = False # Defined conditionally (within block)? @@ -1070,6 +1096,7 @@ class ClassDef(Statement): "name", "_fullname", "defs", + "type_args", "type_vars", "base_type_exprs", "removed_base_type_exprs", @@ -1089,6 +1116,9 @@ class ClassDef(Statement): name: str # Name of the class without module prefix _fullname: str # Fully qualified name of the class defs: Block + # New-style type parameters (PEP 695), unanalyzed + type_args: list[TypeParam] | None + # Semantically analyzed type parameters (all syntax variants) type_vars: list[mypy.types.TypeVarLikeType] # Base class expressions (not semantically analyzed -- can be arbitrary expressions) base_type_exprs: list[Expression] @@ -1111,12 +1141,14 @@ def __init__( base_type_exprs: list[Expression] | None = None, metaclass: Expression | None = None, keywords: list[tuple[str, Expression]] | None = None, + type_args: list[TypeParam] | None = None, ) -> None: super().__init__() self.name = name self._fullname = "" self.defs = defs self.type_vars = type_vars or [] + self.type_args = type_args self.base_type_exprs = base_type_exprs or [] self.removed_base_type_exprs = [] self.info = CLASSDEF_NO_INFO @@ -1607,6 +1639,25 @@ def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_match_stmt(self) +class TypeAliasStmt(Statement): + __slots__ = ("name", "type_args", "value") + + __match_args__ = ("name", "type_args", "value") + + name: NameExpr + type_args: list[TypeParam] + value: Expression # Will get translated into a type + + def __init__(self, name: NameExpr, type_args: list[TypeParam], value: Expression) -> None: + super().__init__() + self.name = name + self.type_args = type_args + self.value = value + + def accept(self, visitor: StatementVisitor[T]) -> T: + return visitor.visit_type_alias_stmt(self) + + # Expressions @@ -2442,6 +2493,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: INVARIANT: Final = 0 COVARIANT: Final = 1 CONTRAVARIANT: Final = 2 +VARIANCE_NOT_READY: Final = 3 # Variance hasn't been inferred (using Python 3.12 syntax) class TypeVarLikeExpr(SymbolNode, Expression): diff --git a/mypy/options.py b/mypy/options.py index 91639828801e..5ef6bc2a35e7 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -73,7 +73,8 @@ class BuildType: TYPE_VAR_TUPLE: Final = "TypeVarTuple" UNPACK: Final = "Unpack" PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" -INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES,)) +NEW_GENERIC_SYNTAX: Final = "NewGenericSyntax" +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, NEW_GENERIC_SYNTAX)) COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index b7f577110fa8..da0bb517189a 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -36,6 +36,7 @@ SymbolTable, TryStmt, TupleExpr, + TypeAliasStmt, WhileStmt, WithStmt, implicit_module_attrs, @@ -673,3 +674,7 @@ def visit_import_from(self, o: ImportFrom) -> None: name = mod self.tracker.record_definition(name) super().visit_import_from(o) + + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + # Type alias target may contain forward references + self.tracker.record_definition(o.name.name) diff --git a/mypy/semanal.py b/mypy/semanal.py index 91a6b1808987..f92471c159de 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -81,9 +81,13 @@ LDEF, MDEF, NOT_ABSTRACT, + PARAM_SPEC_KIND, REVEAL_LOCALS, REVEAL_TYPE, RUNTIME_PROTOCOL_DECOS, + TYPE_VAR_KIND, + TYPE_VAR_TUPLE_KIND, + VARIANCE_NOT_READY, ArgKind, AssertStmt, AssertTypeExpr, @@ -159,9 +163,11 @@ TupleExpr, TypeAlias, TypeAliasExpr, + TypeAliasStmt, TypeApplication, TypedDictExpr, TypeInfo, + TypeParam, TypeVarExpr, TypeVarLikeExpr, TypeVarTupleExpr, @@ -787,6 +793,7 @@ def file_context( self.num_incomplete_refs = 0 if active_type: + self.push_type_args(active_type.defn.type_args, active_type.defn) self.incomplete_type_stack.append(False) scope.enter_class(active_type) self.enter_class(active_type.defn.info) @@ -800,6 +807,7 @@ def file_context( self.leave_class() self._type = None self.incomplete_type_stack.pop() + self.pop_type_args(active_type.defn.type_args) del self.options # @@ -835,6 +843,10 @@ def visit_func_def(self, defn: FuncDef) -> None: self.analyze_func_def(defn) def analyze_func_def(self, defn: FuncDef) -> None: + if self.push_type_args(defn.type_args, defn) is None: + self.defer(defn) + return + self.function_stack.append(defn) if defn.type: @@ -943,6 +955,8 @@ def analyze_func_def(self, defn: FuncDef) -> None: defn.type = defn.type.copy_modified(ret_type=ret_type) self.wrapped_coro_return_types[defn] = defn.type + self.pop_type_args(defn.type_args) + def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType: if not typ.arg_kinds or typ.arg_kinds[-1] is not ArgKind.ARG_STAR2: return typ @@ -1618,9 +1632,79 @@ def visit_class_def(self, defn: ClassDef) -> None: self.incomplete_type_stack.append(not defn.info) namespace = self.qualified_name(defn.name) with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): + if self.push_type_args(defn.type_args, defn) is None: + self.mark_incomplete(defn.name, defn) + return + self.analyze_class(defn) + self.pop_type_args(defn.type_args) self.incomplete_type_stack.pop() + def push_type_args( + self, type_args: list[TypeParam] | None, context: Context + ) -> list[tuple[str, TypeVarLikeExpr]] | None: + if not type_args: + return [] + tvs: list[tuple[str, TypeVarLikeExpr]] = [] + for p in type_args: + tv = self.analyze_type_param(p) + if tv is None: + return None + tvs.append((p.name, tv)) + + for name, tv in tvs: + self.add_symbol(name, tv, context, no_progress=True) + + return tvs + + def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: + fullname = self.qualified_name(type_param.name) + if type_param.upper_bound: + upper_bound = self.anal_type(type_param.upper_bound) + if upper_bound is None: + return None + else: + upper_bound = self.named_type("builtins.object") + default = AnyType(TypeOfAny.from_omitted_generics) + if type_param.kind == TYPE_VAR_KIND: + values = [] + if type_param.values: + for value in type_param.values: + analyzed = self.anal_type(value) + if analyzed is None: + return None + values.append(analyzed) + return TypeVarExpr( + name=type_param.name, + fullname=fullname, + values=values, + upper_bound=upper_bound, + default=default, + variance=VARIANCE_NOT_READY, + ) + elif type_param.kind == PARAM_SPEC_KIND: + return ParamSpecExpr( + name=type_param.name, fullname=fullname, upper_bound=upper_bound, default=default + ) + else: + assert type_param.kind == TYPE_VAR_TUPLE_KIND + tuple_fallback = self.named_type("builtins.tuple", [self.object_type()]) + return TypeVarTupleExpr( + name=type_param.name, + fullname=fullname, + # Upper bound for *Ts is *tuple[object, ...], it can never be object. + upper_bound=tuple_fallback.copy_modified(), + tuple_fallback=tuple_fallback, + default=default, + ) + + def pop_type_args(self, type_args: list[TypeParam] | None) -> None: + if not type_args: + return + for tv in type_args: + names = self.current_symbol_table() + del names[tv.name] + def analyze_class(self, defn: ClassDef) -> None: fullname = self.qualified_name(defn.name) if not defn.info and not self.is_core_builtin_class(defn): @@ -1914,6 +1998,13 @@ class Foo(Bar, Generic[T]): ... removed: list[int] = [] declared_tvars: TypeVarLikeList = [] is_protocol = False + if defn.type_args is not None: + for p in defn.type_args: + node = self.lookup(p.name, context) + assert node is not None + assert isinstance(node.node, TypeVarLikeExpr) + declared_tvars.append((p.name, node.node)) + for i, base_expr in enumerate(base_type_exprs): if isinstance(base_expr, StarExpr): base_expr.valid = True @@ -5125,6 +5216,79 @@ def visit_match_stmt(self, s: MatchStmt) -> None: guard.accept(self) self.visit_block(s.bodies[i]) + def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: + self.statement = s + type_params = self.push_type_args(s.type_args, s) + if type_params is None: + self.defer(s) + return + all_type_params_names = [p.name for p in s.type_args] + + try: + tag = self.track_incomplete_refs() + res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( + s.name.name, + s.value, + allow_placeholder=True, + declared_type_vars=type_params, + all_declared_type_params_names=all_type_params_names, + ) + if not res: + res = AnyType(TypeOfAny.from_error) + + if not self.is_func_scope(): + # Only marking incomplete for top-level placeholders makes recursive aliases like + # `A = Sequence[str | A]` valid here, similar to how we treat base classes in class + # definitions, allowing `class str(Sequence[str]): ...` + incomplete_target = isinstance(res, ProperType) and isinstance( + res, PlaceholderType + ) + else: + incomplete_target = has_placeholder(res) + + if self.found_incomplete_ref(tag) or incomplete_target: + # Since we have got here, we know this must be a type alias (incomplete refs + # may appear in nested positions), therefore use becomes_typeinfo=True. + self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True) + return + + self.add_type_alias_deps(depends_on) + # In addition to the aliases used, we add deps on unbound + # type variables, since they are erased from target type. + self.add_type_alias_deps(qualified_tvars) + # The above are only direct deps on other aliases. + # For subscripted aliases, type deps from expansion are added in deps.py + # (because the type is stored). + check_for_explicit_any( + res, self.options, self.is_typeshed_stub_file, self.msg, context=s + ) + # When this type alias gets "inlined", the Any is not explicit anymore, + # so we need to replace it with non-explicit Anys. + res = make_any_non_explicit(res) + eager = self.is_func_scope() + alias_node = TypeAlias( + res, + self.qualified_name(s.name.name), + s.line, + s.column, + alias_tvars=alias_tvars, + no_args=False, + eager=eager, + ) + + existing = self.current_symbol_table().get(s.name.name) + if ( + existing + and isinstance(existing.node, (PlaceholderNode, TypeAlias)) + and existing.node.line == s.line + ): + existing.node = alias_node + else: + self.add_symbol(s.name.name, alias_node, s) + + finally: + self.pop_type_args(s.type_args) + # # Expressions # @@ -5803,6 +5967,7 @@ def lookup( for table in reversed(self.locals): if table is not None and name in table: return table[name] + # 4. Current file global scope if name in self.globals: return self.globals[name] @@ -6115,6 +6280,7 @@ def add_symbol( module_hidden: bool = False, can_defer: bool = True, escape_comprehensions: bool = False, + no_progress: bool = False, ) -> bool: """Add symbol to the currently active symbol table. @@ -6136,7 +6302,9 @@ def add_symbol( symbol = SymbolTableNode( kind, node, module_public=module_public, module_hidden=module_hidden ) - return self.add_symbol_table_node(name, symbol, context, can_defer, escape_comprehensions) + return self.add_symbol_table_node( + name, symbol, context, can_defer, escape_comprehensions, no_progress + ) def add_symbol_skip_local(self, name: str, node: SymbolNode) -> None: """Same as above, but skipping the local namespace. @@ -6167,6 +6335,7 @@ def add_symbol_table_node( context: Context | None = None, can_defer: bool = True, escape_comprehensions: bool = False, + no_progress: bool = False, ) -> bool: """Add symbol table node to the currently active symbol table. @@ -6215,7 +6384,8 @@ def add_symbol_table_node( self.name_already_defined(name, context, existing) elif name not in self.missing_names[-1] and "*" not in self.missing_names[-1]: names[name] = symbol - self.progress = True + if not no_progress: + self.progress = True return True return False diff --git a/mypy/strconv.py b/mypy/strconv.py index 42a07c7f62fa..a96a27c45d75 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -86,6 +86,9 @@ def func_helper(self, o: mypy.nodes.FuncItem) -> list[object]: elif kind == mypy.nodes.ARG_STAR2: extra.append(("DictVarArg", [arg.variable])) a: list[Any] = [] + if o.type_args: + for p in o.type_args: + a.append(self.type_param(p)) if args: a.append(("Args", args)) if o.type: @@ -187,6 +190,9 @@ def visit_class_def(self, o: mypy.nodes.ClassDef) -> str: a.insert(1, ("TupleType", [o.info.tuple_type])) if o.info and o.info.fallback_to_any: a.insert(1, "FallbackToAny") + if o.type_args: + for p in reversed(o.type_args): + a.insert(1, self.type_param(p)) return self.dump(a, o) def visit_var(self, o: mypy.nodes.Var) -> str: @@ -323,6 +329,28 @@ def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> str: a.append(("Body", o.bodies[i].body)) return self.dump(a, o) + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> str: + a: list[Any] = [o.name] + for p in o.type_args: + a.append(self.type_param(p)) + a.append(o.value) + return self.dump(a, o) + + def type_param(self, p: mypy.nodes.TypeParam) -> list[Any]: + a: list[Any] = [] + if p.kind == mypy.nodes.PARAM_SPEC_KIND: + prefix = "**" + elif p.kind == mypy.nodes.TYPE_VAR_TUPLE_KIND: + prefix = "*" + else: + prefix = "" + a.append(prefix + p.name) + if p.upper_bound: + a.append(p.upper_bound) + if p.values: + a.append(("Values", p.values)) + return [("TypeParam", a)] + # Expressions # Simple expressions diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 4d5e7335b14f..a5523fbe0d45 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -8,7 +8,7 @@ import mypy.constraints import mypy.typeops from mypy.erasetype import erase_type -from mypy.expandtype import expand_self_type, expand_type_by_instance +from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance from mypy.maptype import map_instance_to_supertype # Circular import; done in the function instead. @@ -19,6 +19,7 @@ CONTRAVARIANT, COVARIANT, INVARIANT, + VARIANCE_NOT_READY, Decorator, FuncBase, OverloadedFuncDef, @@ -66,7 +67,7 @@ ) from mypy.types_utils import flatten_types from mypy.typestate import SubtypeKind, type_state -from mypy.typevars import fill_typevars_with_any +from mypy.typevars import fill_typevars, fill_typevars_with_any # Flags for detected protocol members IS_SETTABLE: Final = 1 @@ -361,7 +362,10 @@ def check_type_parameter( p_left = get_proper_type(left) if isinstance(p_left, UninhabitedType) and p_left.ambiguous: variance = COVARIANT - if variance == COVARIANT: + # If variance hasn't been inferred yet, we are lenient and default to + # covariance. This shouldn't happen often, but it's very difficult to + # avoid these cases altogether. + if variance == COVARIANT or variance == VARIANCE_NOT_READY: if proper_subtype: return is_proper_subtype(left, right, subtype_context=subtype_context) else: @@ -575,8 +579,12 @@ def visit_instance(self, left: Instance) -> bool: else: type_params = zip(t.args, right.args, right.type.defn.type_vars) if not self.subtype_context.ignore_type_params: + tried_infer = False for lefta, righta, tvar in type_params: if isinstance(tvar, TypeVarType): + if tvar.variance == VARIANCE_NOT_READY and not tried_infer: + infer_class_variances(right.type) + tried_infer = True if not check_type_parameter( lefta, righta, @@ -1978,3 +1986,72 @@ def is_more_precise(left: Type, right: Type, *, ignore_promotions: bool = False) if isinstance(right, AnyType): return True return is_proper_subtype(left, right, ignore_promotions=ignore_promotions) + + +def all_non_object_members(info: TypeInfo) -> set[str]: + members = set(info.names) + for base in info.mro[1:-1]: + members.update(base.names) + return members + + +def infer_variance(info: TypeInfo, i: int) -> bool: + """Infer the variance of the ith type variable of a generic class. + + Return True if successful. This can fail if some inferred types aren't ready. + """ + object_type = Instance(info.mro[-1], []) + + for variance in COVARIANT, CONTRAVARIANT, INVARIANT: + tv = info.defn.type_vars[i] + assert isinstance(tv, TypeVarType) + if tv.variance != VARIANCE_NOT_READY: + continue + tv.variance = variance + co = True + contra = True + tvar = info.defn.type_vars[i] + self_type = fill_typevars(info) + for member in all_non_object_members(info): + if member in ("__init__", "__new__"): + continue + node = info[member].node + if isinstance(node, Var) and node.type is None: + tv.variance = VARIANCE_NOT_READY + return False + if isinstance(self_type, TupleType): + self_type = mypy.typeops.tuple_fallback(self_type) + + flags = get_member_flags(member, self_type) + typ = find_member(member, self_type, self_type) + settable = IS_SETTABLE in flags + if typ: + typ2 = expand_type(typ, {tvar.id: object_type}) + if not is_subtype(typ, typ2): + co = False + if not is_subtype(typ2, typ): + contra = False + if settable: + co = False + if co: + v = COVARIANT + elif contra: + v = CONTRAVARIANT + else: + v = INVARIANT + if v == variance: + break + tv.variance = VARIANCE_NOT_READY + return True + + +def infer_class_variances(info: TypeInfo) -> bool: + if not info.defn.type_args: + return True + tvs = info.defn.type_vars + success = True + for i, tv in enumerate(tvs): + if isinstance(tv, TypeVarType) and tv.variance == VARIANCE_NOT_READY: + if not infer_variance(info, i): + success = False + return success diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index e33fa7e53ff0..e215920a6797 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -23,6 +23,8 @@ class ParserSuite(DataSuite): if sys.version_info < (3, 10): files.remove("parse-python310.test") + if sys.version_info < (3, 12): + files.remove("parse-python312.test") def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -39,6 +41,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None: if testcase.file.endswith("python310.test"): options.python_version = (3, 10) + elif testcase.file.endswith("python312.test"): + options.python_version = (3, 12) else: options.python_version = defaults.PYTHON3_VERSION diff --git a/mypy/traverser.py b/mypy/traverser.py index d11dd395f978..225de27e7002 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -71,6 +71,7 @@ TupleExpr, TypeAlias, TypeAliasExpr, + TypeAliasStmt, TypeApplication, TypedDictExpr, TypeVarExpr, @@ -243,6 +244,11 @@ def visit_match_stmt(self, o: MatchStmt) -> None: guard.accept(self) o.bodies[i].accept(self) + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + o.name.accept(self) + # TODO: params + o.value.accept(self) + def visit_member_expr(self, o: MemberExpr) -> None: o.expr.accept(self) diff --git a/mypy/typestate.py b/mypy/typestate.py index c5a5da03eae5..0082c5564705 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -8,9 +8,9 @@ from typing import Dict, Final, Set, Tuple from typing_extensions import TypeAlias as _TypeAlias -from mypy.nodes import TypeInfo +from mypy.nodes import VARIANCE_NOT_READY, TypeInfo from mypy.server.trigger import make_trigger -from mypy.types import Instance, Type, TypeVarId, get_proper_type +from mypy.types import Instance, Type, TypeVarId, TypeVarType, get_proper_type MAX_NEGATIVE_CACHE_TYPES: Final = 1000 MAX_NEGATIVE_CACHE_ENTRIES: Final = 10000 @@ -192,6 +192,12 @@ def record_subtype_cache_entry( # These are unlikely to match, due to the large space of # possible values. Avoid uselessly increasing cache sizes. return + if any( + (isinstance(tv, TypeVarType) and tv.variance == VARIANCE_NOT_READY) + for tv in right.type.defn.type_vars + ): + # Variance indeterminate -- don't know the result + return cache = self._subtype_caches.setdefault(right.type, {}) cache.setdefault(kind, set()).add((left, right)) diff --git a/mypy/visitor.py b/mypy/visitor.py index c5aa3caa8295..340e1af64e00 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -309,6 +309,10 @@ def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> T: def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: pass + @abstractmethod + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: + pass + @trait @mypyc_attr(allow_interpreted_subclasses=True) @@ -460,6 +464,9 @@ def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> T: def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: pass + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: + pass + # Expressions (default no-op implementation) def visit_int_expr(self, o: mypy.nodes.IntExpr) -> T: diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 12e186fd40d8..e7256f036e4c 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -70,6 +70,7 @@ TryStmt, TupleExpr, TypeAliasExpr, + TypeAliasStmt, TypeApplication, TypedDictExpr, TypeVarExpr, @@ -249,6 +250,9 @@ def visit_nonlocal_decl(self, stmt: NonlocalDecl) -> None: def visit_match_stmt(self, stmt: MatchStmt) -> None: transform_match_stmt(self.builder, stmt) + def visit_type_alias_stmt(self, stmt: TypeAliasStmt) -> None: + self.bail('The "type" statement is not yet supported by mypyc', stmt.line) + # Expressions def visit_name_expr(self, expr: NameExpr) -> Value: diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 2b99a42628b1..53656ae5e3fb 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -82,3 +82,879 @@ reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695GenericFunctionSyntax] +# flags: --enable-incomplete-feature=NewGenericSyntax + +def ident[TV](x: TV) -> TV: + y: TV = x + y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "TV") + return x + +reveal_type(ident(1)) # N: Revealed type is "builtins.int" +reveal_type(ident('x')) # N: Revealed type is "builtins.str" + +a: TV # E: Name "TV" is not defined + +def tup[T, S](x: T, y: S) -> tuple[T, S]: + reveal_type((x, y)) # N: Revealed type is "Tuple[T`-1, S`-2]" + return (x, y) + +reveal_type(tup(1, 'x')) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testPEP695GenericClassSyntax] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T]: + x: T + + def __init__(self, x: T) -> None: + self.x = x + + def ident(self, x: T) -> T: + y: T = x + if int(): + return self.x + else: + return y + +reveal_type(C("x")) # N: Revealed type is "__main__.C[builtins.str]" +c: C[int] = C(1) +reveal_type(c.x) # N: Revealed type is "builtins.int" +reveal_type(c.ident(1)) # N: Revealed type is "builtins.int" + +[case testPEP695GenericMethodInGenericClass] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T]: + def m[S](self, x: S) -> T | S: ... + +a: C[int] = C[object]() # E: Incompatible types in assignment (expression has type "C[object]", variable has type "C[int]") +b: C[object] = C[int]() + +reveal_type(C[str]().m(1)) # N: Revealed type is "Union[builtins.str, builtins.int]" + +[case testPEP695InferVarianceSimpleFromMethod] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Invariant[T]: + def f(self, x: T) -> None: + pass + + def g(self) -> T | None: + return None + +a: Invariant[object] +b: Invariant[int] +if int(): + a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") + +class Covariant[T]: + def g(self) -> T | None: + return None + +c: Covariant[object] +d: Covariant[int] +if int(): + c = d +if int(): + d = c # E: Incompatible types in assignment (expression has type "Covariant[object]", variable has type "Covariant[int]") + +class Contravariant[T]: + def f(self, x: T) -> None: + pass + +e: Contravariant[object] +f: Contravariant[int] +if int(): + e = f # E: Incompatible types in assignment (expression has type "Contravariant[int]", variable has type "Contravariant[object]") +if int(): + f = e + +[case testPEP695InferVarianceSimpleFromAttribute] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Invariant1[T]: + def __init__(self, x: T) -> None: + self.x = x + +a: Invariant1[object] +b: Invariant1[int] +if int(): + a = b # E: Incompatible types in assignment (expression has type "Invariant1[int]", variable has type "Invariant1[object]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "Invariant1[object]", variable has type "Invariant1[int]") + +class Invariant2[T]: + def __init__(self) -> None: + self.x: list[T] = [] + +a2: Invariant2[object] +b2: Invariant2[int] +if int(): + a2 = b2 # E: Incompatible types in assignment (expression has type "Invariant2[int]", variable has type "Invariant2[object]") +if int(): + b2 = a2 # E: Incompatible types in assignment (expression has type "Invariant2[object]", variable has type "Invariant2[int]") + +class Invariant3[T]: + def __init__(self) -> None: + self.x: T | None = None + +a3: Invariant3[object] +b3: Invariant3[int] +if int(): + a3 = b3 # E: Incompatible types in assignment (expression has type "Invariant3[int]", variable has type "Invariant3[object]") +if int(): + b3 = a3 # E: Incompatible types in assignment (expression has type "Invariant3[object]", variable has type "Invariant3[int]") + +[case testPEP695InferVarianceRecursive] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Invariant[T]: + def f(self, x: Invariant[T]) -> Invariant[T]: + return x + +class Covariant[T]: + def f(self) -> Covariant[T]: + return self + +class Contravariant[T]: + def f(self, x: Contravariant[T]) -> None: + pass + +a: Invariant[object] +b: Invariant[int] +if int(): + a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") + +c: Covariant[object] +d: Covariant[int] +if int(): + c = d +if int(): + d = c # E: Incompatible types in assignment (expression has type "Covariant[object]", variable has type "Covariant[int]") + +e: Contravariant[object] +f: Contravariant[int] +if int(): + e = f # E: Incompatible types in assignment (expression has type "Contravariant[int]", variable has type "Contravariant[object]") +if int(): + f = e + +[case testPEP695InferVarianceCalculateOnDemand] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Covariant[T]: + def __init__(self) -> None: + self.x = [1] + + def f(self) -> None: + c = Covariant[int]() + # We need to know that T is covariant here + self.g(c) + c2 = Covariant[object]() + self.h(c2) # E: Argument 1 to "h" of "Covariant" has incompatible type "Covariant[object]"; expected "Covariant[int]" + + def g(self, x: Covariant[object]) -> None: pass + def h(self, x: Covariant[int]) -> None: pass + +[case testPEP695InferVarianceNotReadyWhenNeeded] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Covariant[T]: + def f(self) -> None: + c = Covariant[int]() + # We need to know that T is covariant here + self.g(c) + c2 = Covariant[object]() + self.h(c2) # E: Argument 1 to "h" of "Covariant" has incompatible type "Covariant[object]"; expected "Covariant[int]" + + def g(self, x: Covariant[object]) -> None: pass + def h(self, x: Covariant[int]) -> None: pass + + def __init__(self) -> None: + self.x = [1] + +class Invariant[T]: + def f(self) -> None: + c = Invariant(1) + # We need to know that T is invariant here, and for this we need the type + # of self.x, which won't be available on the first type checking pass, + # since __init__ is defined later in the file. In this case we fall back + # covariance. + self.g(c) + c2 = Invariant(object()) + self.h(c2) # E: Argument 1 to "h" of "Invariant" has incompatible type "Invariant[object]"; expected "Invariant[int]" + + def g(self, x: Invariant[object]) -> None: pass + def h(self, x: Invariant[int]) -> None: pass + + def __init__(self, x: T) -> None: + self.x = x + +# Now we should have the variance correct. +a: Invariant[object] +b: Invariant[int] +if int(): + a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") + +[case testPEP695InferVarianceNotReadyForJoin] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Invariant[T]: + def f(self) -> None: + # Assume covariance if variance us not ready + reveal_type([Invariant(1), Invariant(object())]) \ + # N: Revealed type is "builtins.list[__main__.Invariant[builtins.object]]" + + def __init__(self, x: T) -> None: + self.x = x + +reveal_type([Invariant(1), Invariant(object())]) # N: Revealed type is "builtins.list[builtins.object]" + +[case testPEP695InferVarianceNotReadyForMeet] +# flags: --enable-incomplete-feature=NewGenericSyntax + +from typing import TypeVar, Callable + +S = TypeVar("S") +def c(a: Callable[[S], None], b: Callable[[S], None]) -> S: ... + +def a1(x: Invariant[int]) -> None: pass +def a2(x: Invariant[object]) -> None: pass + +class Invariant[T]: + def f(self) -> None: + reveal_type(c(a1, a2)) # N: Revealed type is "__main__.Invariant[builtins.int]" + + def __init__(self, x: T) -> None: + self.x = x + +reveal_type(c(a1, a2)) # N: Revealed type is "Never" + +[case testPEP695InheritInvariant] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Invariant[T]: + x: T + +class Subclass[T](Invariant[T]): + pass + +x: Invariant[int] +y: Invariant[object] +if int(): + x = y # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") +if int(): + y = x # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") + +a: Subclass[int] +b: Subclass[object] +if int(): + a = b # E: Incompatible types in assignment (expression has type "Subclass[object]", variable has type "Subclass[int]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") + +[case testPEP695InheritanceMakesInvariant] +# flags: --enable-incomplete-feature=NewGenericSyntax +class Covariant[T]: + def f(self) -> T: + ... + +class Subclass[T](Covariant[list[T]]): + pass + +x: Covariant[int] = Covariant[object]() # E: Incompatible types in assignment (expression has type "Covariant[object]", variable has type "Covariant[int]") +y: Covariant[object] = Covariant[int]() + +a: Subclass[int] = Subclass[object]() # E: Incompatible types in assignment (expression has type "Subclass[object]", variable has type "Subclass[int]") +b: Subclass[object] = Subclass[int]() # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") + +[case testPEP695InheritCoOrContravariant] +# flags: --enable-incomplete-feature=NewGenericSyntax +class Contravariant[T]: + def f(self, x: T) -> None: pass + +class CovSubclass[T](Contravariant[T]): + pass + +a: CovSubclass[int] = CovSubclass[object]() +b: CovSubclass[object] = CovSubclass[int]() # E: Incompatible types in assignment (expression has type "CovSubclass[int]", variable has type "CovSubclass[object]") + +class Covariant[T]: + def f(self) -> T: ... + +class CoSubclass[T](Covariant[T]): + pass + +c: CoSubclass[int] = CoSubclass[object]() # E: Incompatible types in assignment (expression has type "CoSubclass[object]", variable has type "CoSubclass[int]") +d: CoSubclass[object] = CoSubclass[int]() + +class InvSubclass[T](Covariant[T]): + def g(self, x: T) -> None: pass + +e: InvSubclass[int] = InvSubclass[object]() # E: Incompatible types in assignment (expression has type "InvSubclass[object]", variable has type "InvSubclass[int]") +f: InvSubclass[object] = InvSubclass[int]() # E: Incompatible types in assignment (expression has type "InvSubclass[int]", variable has type "InvSubclass[object]") + +[case testPEP695FinalAttribute] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Final + +class C[T]: + def __init__(self, x: T) -> None: + self.x: Final = x + +a: C[int] = C[object](1) # E: Incompatible types in assignment (expression has type "C[object]", variable has type "C[int]") +b: C[object] = C[int](1) + +[case testPEP695TwoTypeVariables] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T, S]: + def f(self, x: T) -> None: ... + def g(self) -> S: ... + +a: C[int, int] = C[object, int]() +b: C[object, int] = C[int, int]() # E: Incompatible types in assignment (expression has type "C[int, int]", variable has type "C[object, int]") +c: C[int, int] = C[int, object]() # E: Incompatible types in assignment (expression has type "C[int, object]", variable has type "C[int, int]") +d: C[int, object] = C[int, int]() + +[case testPEP695Properties] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class R[T]: + @property + def p(self) -> T: ... + +class RW[T]: + @property + def p(self) -> T: ... + @p.setter + def p(self, x: T) -> None: ... + +a: R[int] = R[object]() # E: Incompatible types in assignment (expression has type "R[object]", variable has type "R[int]") +b: R[object] = R[int]() +c: RW[int] = RW[object]() # E: Incompatible types in assignment (expression has type "RW[object]", variable has type "RW[int]") +d: RW[object] = RW[int]() # E: Incompatible types in assignment (expression has type "RW[int]", variable has type "RW[object]") +[builtins fixtures/property.pyi] + +[case testPEP695Protocol] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Protocol + +class PContra[T](Protocol): + def f(self, x: T) -> None: ... + +PContra() # E: Cannot instantiate protocol class "PContra" +a: PContra[int] +b: PContra[object] +if int(): + a = b +if int(): + b = a # E: Incompatible types in assignment (expression has type "PContra[int]", variable has type "PContra[object]") + +class PCov[T](Protocol): + def f(self) -> T: ... + +PCov() # E: Cannot instantiate protocol class "PCov" +c: PCov[int] +d: PCov[object] +if int(): + c = d # E: Incompatible types in assignment (expression has type "PCov[object]", variable has type "PCov[int]") +if int(): + d = c + +class PInv[T](Protocol): + def f(self, x: T) -> T: ... + +PInv() # E: Cannot instantiate protocol class "PInv" +e: PInv[int] +f: PInv[object] +if int(): + e = f # E: Incompatible types in assignment (expression has type "PInv[object]", variable has type "PInv[int]") +if int(): + f = e # E: Incompatible types in assignment (expression has type "PInv[int]", variable has type "PInv[object]") + +[case testPEP695TypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T]: pass +class D[T, S]: pass + +type A[S] = C[S] + +a: A[int] +reveal_type(a) # N: Revealed type is "__main__.C[builtins.int]" + +type A2[T] = C[C[T]] +a2: A2[str] +reveal_type(a2) # N: Revealed type is "__main__.C[__main__.C[builtins.str]]" + +type A3[T, S] = D[S, C[T]] +a3: A3[int, str] +reveal_type(a3) # N: Revealed type is "__main__.D[builtins.str, __main__.C[builtins.int]]" + +type A4 = int | str +a4: A4 +reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testPEP695TypeAliasWithUnusedTypeParams] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A[T] = int +a: A[str] +reveal_type(a) # N: Revealed type is "builtins.int" + +[case testPEP695TypeAliasForwardReference1] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type A[T] = C[T] + +a: A[int] +reveal_type(a) # N: Revealed type is "__main__.C[builtins.int]" + +class C[T]: pass + +[case testPEP695TypeAliasForwardReference2] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type X = C +type A = X + +a: A +reveal_type(a) # N: Revealed type is "__main__.C" + +class C: pass + +[case testPEP695TypeAliasForwardReference3] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type X = D +type A = C[X] + +a: A +reveal_type(a) # N: Revealed type is "__main__.C[__main__.D]" + +class C[T]: pass +class D: pass + +[case testPEP695TypeAliasForwardReference4] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type A = C + +# Note that this doesn't actually work at runtime, but we currently don't +# keep track whether a type alias is valid in various runtime type contexts. +class D(A): + pass + +class C: pass + +x: C = D() +y: D = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") + +[case testPEP695TypeAliasForwardReference5] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A = str +type B[T] = C[T] +class C[T]: pass +a: A +b: B[int] +c: C[str] +reveal_type(a) # N: Revealed type is "builtins.str" +reveal_type(b) # N: Revealed type is "__main__.C[builtins.int]" +reveal_type(c) # N: Revealed type is "__main__.C[builtins.str]" + +[case testPEP695TypeAliasWithUndefineName] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A[T] = XXX # E: Name "XXX" is not defined +a: A[int] +reveal_type(a) # N: Revealed type is "Any" + +[case testPEP695TypeAliasInvalidType] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A = int | 1 # E: Invalid type: try using Literal[1] instead? +a: A +reveal_type(a) # N: Revealed type is "Union[builtins.int, Any]" +type B = int + str # E: Invalid type alias: expression is not a valid type +b: B +reveal_type(b) # N: Revealed type is "Any" + +[case testPEP695TypeAliasBoundForwardReference] +# mypy: enable-incomplete-feature=NewGenericSyntax +type B[T: Foo] = list[T] +class Foo: pass + +[case testPEP695UpperBound] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class D: + x: int +class E(D): pass + +class C[T: D]: pass + +a: C[D] +b: C[E] +reveal_type(a) # N: Revealed type is "__main__.C[__main__.D]" +reveal_type(b) # N: Revealed type is "__main__.C[__main__.E]" + +c: C[int] # E: Type argument "int" of "C" must be a subtype of "D" + +def f[T: D](a: T) -> T: + reveal_type(a.x) # N: Revealed type is "builtins.int" + return a + +reveal_type(f(D())) # N: Revealed type is "__main__.D" +reveal_type(f(E())) # N: Revealed type is "__main__.E" +f(1) # E: Value of type variable "T" of "f" cannot be "int" + +[case testPEP695UpperBoundForwardReference1] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T: D]: pass + +a: C[D] +b: C[E] +reveal_type(a) # N: Revealed type is "__main__.C[__main__.D]" +reveal_type(b) # N: Revealed type is "__main__.C[__main__.E]" + +c: C[int] # E: Type argument "int" of "C" must be a subtype of "D" + +class D: pass +class E(D): pass + +[case testPEP695UpperBoundForwardReference2] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type A = D +class C[T: A]: pass + +class D: pass +class E(D): pass + +a: C[D] +b: C[E] +reveal_type(a) # N: Revealed type is "__main__.C[__main__.D]" +reveal_type(b) # N: Revealed type is "__main__.C[__main__.E]" + +c: C[int] # E: Type argument "int" of "C" must be a subtype of "D" + +[case testPEP695UpperBoundForwardReference3] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class D[T]: pass +class E[T](D[T]): pass + +type A = D[X] + +class C[T: A]: pass + +class X: pass + +a: C[D[X]] +b: C[E[X]] +reveal_type(a) # N: Revealed type is "__main__.C[__main__.D[__main__.X]]" +reveal_type(b) # N: Revealed type is "__main__.C[__main__.E[__main__.X]]" + +c: C[D[int]] # E: Type argument "D[int]" of "C" must be a subtype of "D[X]" + +[case testPEP695UpperBoundForwardReference4] +# flags: --enable-incomplete-feature=NewGenericSyntax + +def f[T: D](a: T) -> T: + reveal_type(a.x) # N: Revealed type is "builtins.int" + return a + +class D: + x: int +class E(D): pass + +reveal_type(f(D())) # N: Revealed type is "__main__.D" +reveal_type(f(E())) # N: Revealed type is "__main__.E" +f(1) # E: Value of type variable "T" of "f" cannot be "int" + +[case testPEP695UpperBoundUndefinedName] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T: XX]: # E: Name "XX" is not defined + pass + +a: C[int] + +def f[T: YY](x: T) -> T: # E: Name "YY" is not defined + return x +reveal_type(f) # N: Revealed type is "def [T <: Any] (x: T`-1) -> T`-1" + +[case testPEP695UpperBoundWithMultipleParams] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T, S: int]: pass +class D[A: int, B]: pass + +def f[T: int, S: int | str](x: T, y: S) -> T | S: + return x + +C[str, int]() +C[str, str]() # E: Value of type variable "S" of "C" cannot be "str" +D[int, str]() +D[str, str]() # E: Value of type variable "A" of "D" cannot be "str" +f(1, 1) +u: int | str +f(1, u) +f('x', None) # E: Value of type variable "T" of "f" cannot be "str" \ + # E: Value of type variable "S" of "f" cannot be "None" + +[case testPEP695InferVarianceOfTupleType] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Cov[T](tuple[int, str]): + def f(self) -> T: pass + +class Cov2[T](tuple[T, T]): + pass + +class Contra[T](tuple[int, str]): + def f(self, x: T) -> None: pass + +a: Cov[object] = Cov[int]() +b: Cov[int] = Cov[object]() # E: Incompatible types in assignment (expression has type "Cov[object]", variable has type "Cov[int]") + +c: Cov2[object] = Cov2[int]() +d: Cov2[int] = Cov2[object]() # E: Incompatible types in assignment (expression has type "Cov2[object]", variable has type "Cov2[int]") + +e: Contra[int] = Contra[object]() +f: Contra[object] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[object]") +[builtins fixtures/tuple-simple.pyi] + +[case testPEP695ValueRestiction] +# flags: --enable-incomplete-feature=NewGenericSyntax + +def f[T: (int, str)](x: T) -> T: + reveal_type(x) # N: Revealed type is "builtins.int" \ + # N: Revealed type is "builtins.str" + return x + +reveal_type(f(1)) # N: Revealed type is "builtins.int" +reveal_type(f('x')) # N: Revealed type is "builtins.str" +f(None) # E: Value of type variable "T" of "f" cannot be "None" + +class C[T: (object, None)]: pass + +a: C[object] +b: C[None] +c: C[int] # E: Value of type variable "T" of "C" cannot be "int" + +[case testPEP695ValueRestictionForwardReference] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T: (int, D)]: + def __init__(self, x: T) -> None: + a = x + if int(): + a = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") \ + # E: Incompatible types in assignment (expression has type "str", variable has type "D") + self.x: T = x + +reveal_type(C(1).x) # N: Revealed type is "builtins.int" +C(None) # E: Value of type variable "T" of "C" cannot be "None" + +class D: pass + +C(D()) + +[case testPEP695ValueRestictionUndefinedName] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C[T: (int, XX)]: # E: Name "XX" is not defined + pass + +def f[S: (int, YY)](x: S) -> S: # E: Name "YY" is not defined + return x + +[case testPEP695ParamSpec] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Callable + +def g[**P](f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: + f(*args, **kwargs) + f(1, *args, **kwargs) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def h(x: int, y: str) -> None: pass + +g(h, 1, y='x') +g(h, 1, x=1) # E: "g" gets multiple values for keyword argument "x" \ + # E: Missing positional argument "y" in call to "g" + +class C[**P, T]: + def m(self, *args: P.args, **kwargs: P.kwargs) -> T: ... + +a: C[[int, str], None] +reveal_type(a) # N: Revealed type is "__main__.C[[builtins.int, builtins.str], None]" +reveal_type(a.m) # N: Revealed type is "def (builtins.int, builtins.str)" +[builtins fixtures/tuple.pyi] + +[case testPEP695ParamSpecTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Callable + +type C[**P] = Callable[P, int] + +f: C[[str, int | None]] +reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, None]) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeVarTuple] +# flags: --enable-incomplete-feature=NewGenericSyntax + +def f[*Ts](t: tuple[*Ts]) -> tuple[*Ts]: + reveal_type(t) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" + return t + +reveal_type(f((1, 'x'))) # N: Revealed type is "Tuple[Literal[1]?, Literal['x']?]" +a: tuple[int, ...] +reveal_type(f(a)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" + +class C[T, *Ts]: + pass + +b: C[int, str, None] +reveal_type(b) # N: Revealed type is "__main__.C[builtins.int, builtins.str, None]" +c: C[str] +reveal_type(c) # N: Revealed type is "__main__.C[builtins.str]" +b = c # E: Incompatible types in assignment (expression has type "C[str]", variable has type "C[int, str, None]") +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeVarTupleAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Callable + +type C[*Ts] = tuple[*Ts, int] + +a: C[str, None] +reveal_type(a) # N: Revealed type is "Tuple[builtins.str, None, builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testPEP695IncrementalFunction] +# flags: --enable-incomplete-feature=NewGenericSyntax +import a + +[file a.py] +import b + +[file a.py.2] +import b +reveal_type(b.f(1)) +reveal_type(b.g(1, 'x')) +b.g('x', 'x') +b.g(1, 2) + +[file b.py] +def f[T](x: T) -> T: + return x + +def g[T: int, S: (str, None)](x: T, y: S) -> T | S: + return x + +[out2] +tmp/a.py:2: note: Revealed type is "builtins.int" +tmp/a.py:3: note: Revealed type is "Union[builtins.int, builtins.str]" +tmp/a.py:4: error: Value of type variable "T" of "g" cannot be "str" +tmp/a.py:5: error: Value of type variable "S" of "g" cannot be "int" + +[case testPEP695IncrementalClass] +# flags: --enable-incomplete-feature=NewGenericSyntax +import a + +[file a.py] +import b + +[file a.py.2] +from b import C, D +x: C[int] +reveal_type(x) + +class N(int): pass +class SS(str): pass + +y1: D[int, str] +y2: D[N, str] +y3: D[int, None] +y4: D[int, None] +y5: D[int, SS] # Error +y6: D[object, str] # Error + +[file b.py] +class C[T]: pass + +class D[T: int, S: (str, None)]: + pass + +[out2] +tmp/a.py:3: note: Revealed type is "b.C[builtins.int]" +tmp/a.py:12: error: Value of type variable "S" of "D" cannot be "SS" +tmp/a.py:13: error: Type argument "object" of "D" must be a subtype of "int" + +[case testPEP695IncrementalParamSpecAndTypeVarTuple] +# flags: --enable-incomplete-feature=NewGenericSyntax +import a + +[file a.py] +import b + +[file a.py.2] +from b import C, D +x1: C[()] +x2: C[int] +x3: C[int, str] +y: D[[int, str]] +reveal_type(y.m) + +[file b.py] +class C[*Ts]: pass +class D[**P]: + def m(self, *args: P.args, **kwargs: P.kwargs) -> None: pass + +[builtins fixtures/tuple.pyi] +[out2] +tmp/a.py:6: note: Revealed type is "def (builtins.int, builtins.str)" + +[case testPEP695IncrementalTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +import a + +[file a.py] +import b + +[file a.py.2] +from b import A, B +a: A +reveal_type(a) +b: B[int] +reveal_type(b) + +[file b.py] +type A = str +class Foo[T]: pass +type B[T] = Foo[T] + +[builtins fixtures/tuple.pyi] +[out2] +tmp/a.py:3: note: Revealed type is "builtins.str" +tmp/a.py:5: note: Revealed type is "b.Foo[builtins.int]" + +[case testPEP695UndefinedNameInGenericFunction] +# mypy: enable-incomplete-feature=NewGenericSyntax + +def f[T](x: T) -> T: + return unknown() # E: Name "unknown" is not defined + +class C: + def m[T](self, x: T) -> T: + return unknown() # E: Name "unknown" is not defined diff --git a/test-data/unit/parse-python312.test b/test-data/unit/parse-python312.test new file mode 100644 index 000000000000..28204ccd647b --- /dev/null +++ b/test-data/unit/parse-python312.test @@ -0,0 +1,87 @@ +[case testPEP695TypeAlias] +# mypy: enable-incomplete-feature=NewGenericSyntax +type A[T] = C[T] +[out] +MypyFile:1( + TypeAliasStmt:2( + NameExpr(A) + TypeParam( + T) + IndexExpr:2( + NameExpr(C) + NameExpr(T)))) + +[case testPEP695GenericFunction] +# mypy: enable-incomplete-feature=NewGenericSyntax + +def f[T](): pass +def g[T: str](): pass +def h[T: (int, str)](): pass +[out] +MypyFile:1( + FuncDef:3( + f + TypeParam( + T) + Block:3( + PassStmt:3())) + FuncDef:4( + g + TypeParam( + T + str?) + Block:4( + PassStmt:4())) + FuncDef:5( + h + TypeParam( + T + Values( + int? + str?)) + Block:5( + PassStmt:5()))) + +[case testPEP695ParamSpec] +# mypy: enable-incomplete-feature=NewGenericSyntax + +def f[**P](): pass +class C[T: int, **P]: pass +[out] +MypyFile:1( + FuncDef:3( + f + TypeParam( + **P) + Block:3( + PassStmt:3())) + ClassDef:4( + C + TypeParam( + T + int?) + TypeParam( + **P) + PassStmt:4())) + +[case testPEP695TypeVarTuple] +# mypy: enable-incomplete-feature=NewGenericSyntax + +def f[*Ts](): pass +class C[T: int, *Ts]: pass +[out] +MypyFile:1( + FuncDef:3( + f + TypeParam( + *Ts) + Block:3( + PassStmt:3())) + ClassDef:4( + C + TypeParam( + T + int?) + TypeParam( + *Ts) + PassStmt:4())) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index b51a965c95da..0ed3540b6bb9 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2091,3 +2091,24 @@ def f(d: Description) -> None: reveal_type(d.name_fn) [out] _testDataclassStrictOptionalAlwaysSet.py:9: note: Revealed type is "def (Union[builtins.int, None]) -> Union[builtins.str, None]" + +[case testPEP695VarianceInference] +# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +from typing import Callable, Final + +class Job[_R_co]: + def __init__(self, target: Callable[[], _R_co]) -> None: + self.target: Final = target + +def func( + action: Job[int | None], + a1: Job[int | None], + a2: Job[int], + a3: Job[None], +) -> None: + action = a1 + action = a2 + action = a3 + a2 = action # Error +[out] +_testPEP695VarianceInference.py:17: error: Incompatible types in assignment (expression has type "Job[None]", variable has type "Job[int]") From 3b97e6e60b561b18ef23bfd98a4296b23f60a10a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 17 May 2024 15:02:45 +0100 Subject: [PATCH 0582/1617] [PEP 695] Implement new scoping rules for type parameters (#17258) Type parameters get a separate scope with some special features. Work on #15238. --- mypy/nodes.py | 10 +- mypy/semanal.py | 118 ++++++++++++---- mypy/typeanal.py | 37 +++-- test-data/unit/check-python312.test | 203 ++++++++++++++++++++++++++++ 4 files changed, 323 insertions(+), 45 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 4c83d8081f6c..6657ab8cb65f 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2502,7 +2502,7 @@ class TypeVarLikeExpr(SymbolNode, Expression): Note that they are constructed by the semantic analyzer. """ - __slots__ = ("_name", "_fullname", "upper_bound", "default", "variance") + __slots__ = ("_name", "_fullname", "upper_bound", "default", "variance", "is_new_style") _name: str _fullname: str @@ -2525,6 +2525,7 @@ def __init__( upper_bound: mypy.types.Type, default: mypy.types.Type, variance: int = INVARIANT, + is_new_style: bool = False, ) -> None: super().__init__() self._name = name @@ -2532,6 +2533,7 @@ def __init__( self.upper_bound = upper_bound self.default = default self.variance = variance + self.is_new_style = is_new_style @property def name(self) -> str: @@ -2570,8 +2572,9 @@ def __init__( upper_bound: mypy.types.Type, default: mypy.types.Type, variance: int = INVARIANT, + is_new_style: bool = False, ) -> None: - super().__init__(name, fullname, upper_bound, default, variance) + super().__init__(name, fullname, upper_bound, default, variance, is_new_style) self.values = values def accept(self, visitor: ExpressionVisitor[T]) -> T: @@ -2648,8 +2651,9 @@ def __init__( tuple_fallback: mypy.types.Instance, default: mypy.types.Type, variance: int = INVARIANT, + is_new_style: bool = False, ) -> None: - super().__init__(name, fullname, upper_bound, default, variance) + super().__init__(name, fullname, upper_bound, default, variance, is_new_style) self.tuple_fallback = tuple_fallback def accept(self, visitor: ExpressionVisitor[T]) -> T: diff --git a/mypy/semanal.py b/mypy/semanal.py index f92471c159de..a66f43e17dd2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -317,6 +317,14 @@ CORE_BUILTIN_CLASSES: Final = ["object", "bool", "function"] +# Python has several different scope/namespace kinds with subtly different semantics. +SCOPE_GLOBAL: Final = 0 # Module top level +SCOPE_CLASS: Final = 1 # Class body +SCOPE_FUNC: Final = 2 # Function or lambda +SCOPE_COMPREHENSION: Final = 3 # Comprehension or generator expression +SCOPE_ANNOTATION: Final = 4 # Annotation scopes for type parameters and aliases (PEP 695) + + # Used for tracking incomplete references Tag: _TypeAlias = int @@ -342,8 +350,8 @@ class SemanticAnalyzer( nonlocal_decls: list[set[str]] # Local names of function scopes; None for non-function scopes. locals: list[SymbolTable | None] - # Whether each scope is a comprehension scope. - is_comprehension_stack: list[bool] + # Type of each scope (SCOPE_*, indexes match locals) + scope_stack: list[int] # Nested block depths of scopes block_depth: list[int] # TypeInfo of directly enclosing class (or None) @@ -417,7 +425,7 @@ def __init__( errors: Report analysis errors using this instance """ self.locals = [None] - self.is_comprehension_stack = [False] + self.scope_stack = [SCOPE_GLOBAL] # Saved namespaces from previous iteration. Every top-level function/method body is # analyzed in several iterations until all names are resolved. We need to save # the local namespaces for the top level function and all nested functions between @@ -880,6 +888,7 @@ def analyze_func_def(self, defn: FuncDef) -> None: # Don't store not ready types (including placeholders). if self.found_incomplete_ref(tag) or has_placeholder(result): self.defer(defn) + # TODO: pop type args return assert isinstance(result, ProperType) if isinstance(result, CallableType): @@ -1645,6 +1654,8 @@ def push_type_args( ) -> list[tuple[str, TypeVarLikeExpr]] | None: if not type_args: return [] + self.locals.append(SymbolTable()) + self.scope_stack.append(SCOPE_ANNOTATION) tvs: list[tuple[str, TypeVarLikeExpr]] = [] for p in type_args: tv = self.analyze_type_param(p) @@ -1653,10 +1664,23 @@ def push_type_args( tvs.append((p.name, tv)) for name, tv in tvs: - self.add_symbol(name, tv, context, no_progress=True) + if self.is_defined_type_param(name): + self.fail(f'"{name}" already defined as a type parameter', context) + else: + self.add_symbol(name, tv, context, no_progress=True, type_param=True) return tvs + def is_defined_type_param(self, name: str) -> bool: + for names in self.locals: + if names is None: + continue + if name in names: + node = names[name].node + if isinstance(node, TypeVarLikeExpr): + return True + return False + def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: fullname = self.qualified_name(type_param.name) if type_param.upper_bound: @@ -1681,10 +1705,15 @@ def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: upper_bound=upper_bound, default=default, variance=VARIANCE_NOT_READY, + is_new_style=True, ) elif type_param.kind == PARAM_SPEC_KIND: return ParamSpecExpr( - name=type_param.name, fullname=fullname, upper_bound=upper_bound, default=default + name=type_param.name, + fullname=fullname, + upper_bound=upper_bound, + default=default, + is_new_style=True, ) else: assert type_param.kind == TYPE_VAR_TUPLE_KIND @@ -1696,14 +1725,14 @@ def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: upper_bound=tuple_fallback.copy_modified(), tuple_fallback=tuple_fallback, default=default, + is_new_style=True, ) def pop_type_args(self, type_args: list[TypeParam] | None) -> None: if not type_args: return - for tv in type_args: - names = self.current_symbol_table() - del names[tv.name] + self.locals.pop() + self.scope_stack.pop() def analyze_class(self, defn: ClassDef) -> None: fullname = self.qualified_name(defn.name) @@ -1785,8 +1814,18 @@ def analyze_class(self, defn: ClassDef) -> None: defn.info.is_protocol = is_protocol self.recalculate_metaclass(defn, declared_metaclass) defn.info.runtime_protocol = False + + if defn.type_args: + # PEP 695 type parameters are not in scope in class decorators, so + # temporarily disable type parameter namespace. + type_params_names = self.locals.pop() + self.scope_stack.pop() for decorator in defn.decorators: self.analyze_class_decorator(defn, decorator) + if defn.type_args: + self.locals.append(type_params_names) + self.scope_stack.append(SCOPE_ANNOTATION) + self.analyze_class_body_common(defn) def setup_type_vars(self, defn: ClassDef, tvar_defs: list[TypeVarLikeType]) -> None: @@ -1938,7 +1977,7 @@ def enter_class(self, info: TypeInfo) -> None: # Remember previous active class self.type_stack.append(self.type) self.locals.append(None) # Add class scope - self.is_comprehension_stack.append(False) + self.scope_stack.append(SCOPE_CLASS) self.block_depth.append(-1) # The class body increments this to 0 self.loop_depth.append(0) self._type = info @@ -1949,7 +1988,7 @@ def leave_class(self) -> None: self.block_depth.pop() self.loop_depth.pop() self.locals.pop() - self.is_comprehension_stack.pop() + self.scope_stack.pop() self._type = self.type_stack.pop() self.missing_names.pop() @@ -2923,8 +2962,8 @@ class C: [(j := i) for i in [1, 2, 3]] is a syntax error that is not enforced by Python parser, but at later steps. """ - for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)): - if not is_comprehension and i < len(self.locals) - 1: + for i, scope_type in enumerate(reversed(self.scope_stack)): + if scope_type != SCOPE_COMPREHENSION and i < len(self.locals) - 1: if self.locals[-1 - i] is None: self.fail( "Assignment expression within a comprehension" @@ -5188,8 +5227,14 @@ def visit_nonlocal_decl(self, d: NonlocalDecl) -> None: self.fail("nonlocal declaration not allowed at module level", d) else: for name in d.names: - for table in reversed(self.locals[:-1]): + for table, scope_type in zip( + reversed(self.locals[:-1]), reversed(self.scope_stack[:-1]) + ): if table is not None and name in table: + if scope_type == SCOPE_ANNOTATION: + self.fail( + f'nonlocal binding not allowed for type parameter "{name}"', d + ) break else: self.fail(f'No binding for nonlocal "{name}" found', d) @@ -5350,7 +5395,7 @@ def visit_star_expr(self, expr: StarExpr) -> None: def visit_yield_from_expr(self, e: YieldFromExpr) -> None: if not self.is_func_scope(): self.fail('"yield from" outside function', e, serious=True, blocker=True) - elif self.is_comprehension_stack[-1]: + elif self.scope_stack[-1] == SCOPE_COMPREHENSION: self.fail( '"yield from" inside comprehension or generator expression', e, @@ -5848,7 +5893,7 @@ def visit__promote_expr(self, expr: PromoteExpr) -> None: def visit_yield_expr(self, e: YieldExpr) -> None: if not self.is_func_scope(): self.fail('"yield" outside function', e, serious=True, blocker=True) - elif self.is_comprehension_stack[-1]: + elif self.scope_stack[-1] == SCOPE_COMPREHENSION: self.fail( '"yield" inside comprehension or generator expression', e, @@ -6281,6 +6326,7 @@ def add_symbol( can_defer: bool = True, escape_comprehensions: bool = False, no_progress: bool = False, + type_param: bool = False, ) -> bool: """Add symbol to the currently active symbol table. @@ -6303,7 +6349,7 @@ def add_symbol( kind, node, module_public=module_public, module_hidden=module_hidden ) return self.add_symbol_table_node( - name, symbol, context, can_defer, escape_comprehensions, no_progress + name, symbol, context, can_defer, escape_comprehensions, no_progress, type_param ) def add_symbol_skip_local(self, name: str, node: SymbolNode) -> None: @@ -6336,6 +6382,7 @@ def add_symbol_table_node( can_defer: bool = True, escape_comprehensions: bool = False, no_progress: bool = False, + type_param: bool = False, ) -> bool: """Add symbol table node to the currently active symbol table. @@ -6355,7 +6402,9 @@ def add_symbol_table_node( can_defer: if True, defer current target if adding a placeholder context: error context (see above about None value) """ - names = self.current_symbol_table(escape_comprehensions=escape_comprehensions) + names = self.current_symbol_table( + escape_comprehensions=escape_comprehensions, type_param=type_param + ) existing = names.get(name) if isinstance(symbol.node, PlaceholderNode) and can_defer: if context is not None: @@ -6673,7 +6722,7 @@ def enter( names = self.saved_locals.setdefault(function, SymbolTable()) self.locals.append(names) is_comprehension = isinstance(function, (GeneratorExpr, DictionaryComprehension)) - self.is_comprehension_stack.append(is_comprehension) + self.scope_stack.append(SCOPE_FUNC if not is_comprehension else SCOPE_COMPREHENSION) self.global_decls.append(set()) self.nonlocal_decls.append(set()) # -1 since entering block will increment this to 0. @@ -6684,7 +6733,7 @@ def enter( yield finally: self.locals.pop() - self.is_comprehension_stack.pop() + self.scope_stack.pop() self.global_decls.pop() self.nonlocal_decls.pop() self.block_depth.pop() @@ -6692,11 +6741,14 @@ def enter( self.missing_names.pop() def is_func_scope(self) -> bool: - return self.locals[-1] is not None + scope_type = self.scope_stack[-1] + if scope_type == SCOPE_ANNOTATION: + scope_type = self.scope_stack[-2] + return scope_type in (SCOPE_FUNC, SCOPE_COMPREHENSION) def is_nested_within_func_scope(self) -> bool: """Are we underneath a function scope, even if we are in a nested class also?""" - return any(l is not None for l in self.locals) + return any(s in (SCOPE_FUNC, SCOPE_COMPREHENSION) for s in self.scope_stack) def is_class_scope(self) -> bool: return self.type is not None and not self.is_func_scope() @@ -6713,14 +6765,24 @@ def current_symbol_kind(self) -> int: kind = GDEF return kind - def current_symbol_table(self, escape_comprehensions: bool = False) -> SymbolTable: - if self.is_func_scope(): - assert self.locals[-1] is not None + def current_symbol_table( + self, escape_comprehensions: bool = False, type_param: bool = False + ) -> SymbolTable: + if type_param and self.scope_stack[-1] == SCOPE_ANNOTATION: + n = self.locals[-1] + assert n is not None + return n + elif self.is_func_scope(): + if self.scope_stack[-1] == SCOPE_ANNOTATION: + n = self.locals[-2] + else: + n = self.locals[-1] + assert n is not None if escape_comprehensions: - assert len(self.locals) == len(self.is_comprehension_stack) + assert len(self.locals) == len(self.scope_stack) # Retrieve the symbol table from the enclosing non-comprehension scope. - for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)): - if not is_comprehension: + for i, scope_type in enumerate(reversed(self.scope_stack)): + if scope_type != SCOPE_COMPREHENSION: if i == len(self.locals) - 1: # The last iteration. # The caller of the comprehension is in the global space. names = self.globals @@ -6734,7 +6796,7 @@ def current_symbol_table(self, escape_comprehensions: bool = False) -> SymbolTab else: assert False, "Should have at least one non-comprehension scope" else: - names = self.locals[-1] + names = n assert names is not None elif self.type is not None: names = self.type.names diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 5cde7da721ec..31d451b0831a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -894,6 +894,7 @@ def analyze_unbound_type_without_type_info( t = t.copy_modified(args=self.anal_array(t.args)) # TODO: Move this message building logic to messages.py. notes: list[str] = [] + error_code = codes.VALID_TYPE if isinstance(sym.node, Var): notes.append( "See https://mypy.readthedocs.io/en/" @@ -912,25 +913,33 @@ def analyze_unbound_type_without_type_info( message = 'Module "{}" is not valid as a type' notes.append("Perhaps you meant to use a protocol matching the module structure?") elif unbound_tvar: - message = 'Type variable "{}" is unbound' - short = name.split(".")[-1] - notes.append( - ( - '(Hint: Use "Generic[{}]" or "Protocol[{}]" base class' - ' to bind "{}" inside a class)' - ).format(short, short, short) - ) - notes.append( - '(Hint: Use "{}" in function signature to bind "{}"' - " inside a function)".format(short, short) - ) + assert isinstance(sym.node, TypeVarLikeExpr) + if sym.node.is_new_style: + # PEP 695 type paramaters are never considered unbound -- they are undefined + # in contexts where they aren't valid, such as in argument default values. + message = 'Name "{}" is not defined' + name = name.split(".")[-1] + error_code = codes.NAME_DEFINED + else: + message = 'Type variable "{}" is unbound' + short = name.split(".")[-1] + notes.append( + ( + '(Hint: Use "Generic[{}]" or "Protocol[{}]" base class' + ' to bind "{}" inside a class)' + ).format(short, short, short) + ) + notes.append( + '(Hint: Use "{}" in function signature to bind "{}"' + " inside a function)".format(short, short) + ) else: message = 'Cannot interpret reference "{}" as a type' if not defining_literal: # Literal check already gives a custom error. Avoid duplicating errors. - self.fail(message.format(name), t, code=codes.VALID_TYPE) + self.fail(message.format(name), t, code=error_code) for note in notes: - self.note(note, t, code=codes.VALID_TYPE) + self.note(note, t, code=error_code) # TODO: Would it be better to always return Any instead of UnboundType # in case of an error? On one hand, UnboundType has a name so error messages diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 53656ae5e3fb..cce22634df6d 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -958,3 +958,206 @@ def f[T](x: T) -> T: class C: def m[T](self, x: T) -> T: return unknown() # E: Name "unknown" is not defined + +[case testPEP695FunctionTypeVarAccessInFunction] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import cast + +class C: + def m[T](self, x: T) -> T: + y: T = x + reveal_type(y) # N: Revealed type is "T`-1" + return cast(T, y) + +reveal_type(C().m(1)) # N: Revealed type is "builtins.int" + +[case testPEP695ScopingBasics] +# mypy: enable-incomplete-feature=NewGenericSyntax + +T = 1 + +def f[T](x: T) -> T: + T = 'a' + reveal_type(T) # N: Revealed type is "builtins.str" + return x + +reveal_type(T) # N: Revealed type is "builtins.int" + +class C[T]: + T = 1.2 + reveal_type(T) # N: Revealed type is "builtins.float" + +reveal_type(T) # N: Revealed type is "builtins.int" + +[case testPEP695ClassScoping] +# mypy: enable-incomplete-feature=NewGenericSyntax + +class C: + class D: pass + + def m[T: D](self, x: T, y: D) -> T: + return x + +C().m(C.D(), C.D()) +C().m(1, C.D()) # E: Value of type variable "T" of "m" of "C" cannot be "int" + +[case testPEP695NestedGenericFunction] +# mypy: enable-incomplete-feature=NewGenericSyntax +def f[T](x: T) -> T: + reveal_type(f(x)) # N: Revealed type is "T`-1" + reveal_type(f(1)) # N: Revealed type is "builtins.int" + + def ff(x: T) -> T: + y: T = x + return y + reveal_type(ff(x)) # N: Revealed type is "T`-1" + ff(1) # E: Argument 1 to "ff" has incompatible type "int"; expected "T" + + def g[S](a: S) -> S: + ff(a) # E: Argument 1 to "ff" has incompatible type "S"; expected "T" + return a + reveal_type(g(1)) # N: Revealed type is "builtins.int" + reveal_type(g(x)) # N: Revealed type is "T`-1" + + def h[S](a: S) -> S: + return a + reveal_type(h(1)) # N: Revealed type is "builtins.int" + reveal_type(h(x)) # N: Revealed type is "T`-1" + return x + +[case testPEP695NonLocalAndGlobal] +# mypy: enable-incomplete-feature=NewGenericSyntax +def f() -> None: + T = 1 + def g[T](x: T) -> T: + nonlocal T # E: nonlocal binding not allowed for type parameter "T" + T = 'x' # E: "T" is a type variable and only valid in type context + return x + reveal_type(T) # N: Revealed type is "builtins.int" + +def g() -> None: + a = 1 + def g[T](x: T) -> T: + nonlocal a + a = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + return x + +x = 1 + +def h[T](a: T) -> T: + global x + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + return a + +class C[T]: + def m[S](self, a: S) -> S: + global x + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + return a + +[case testPEP695ArgumentDefault] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import cast + +def f[T]( + x: T = + T # E: Name "T" is not defined \ + # E: Incompatible default for argument "x" (default has type "object", argument has type "T") +) -> T: + return x + +def g[T](x: T = cast(T, None)) -> T: # E: Name "T" is not defined + return x + +class C: + def m[T](self, x: T = cast(T, None)) -> T: # E: Name "T" is not defined + return x + +[case testPEP695ListComprehension] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import cast + +def f[T](x: T) -> T: + b = [cast(T, a) for a in [1, 2]] + reveal_type(b) # N: Revealed type is "builtins.list[T`-1]" + return x + +[case testPEP695ReuseNameInSameScope] +# mypy: enable-incomplete-feature=NewGenericSyntax + +class C[T]: + def m[S](self, x: S, y: T) -> S | T: + return x + + def m2[S](self, x: S, y: T) -> S | T: + return x + +class D[T]: + pass + +def f[T](x: T) -> T: + return x + +def g[T](x: T) -> T: + def nested[S](y: S) -> S: + return y + def nested2[S](y: S) -> S: + return y + return x + +[case testPEP695NestedScopingSpecialCases] +# mypy: enable-incomplete-feature=NewGenericSyntax +# This is adapted from PEP 695 +S = 0 + +def outer1[S]() -> None: + S = 1 + T = 1 + + def outer2[T]() -> None: + def inner1() -> None: + nonlocal S + nonlocal T # E: nonlocal binding not allowed for type parameter "T" + + def inner2() -> None: + global S + +[case testPEP695ScopingWithBaseClasses] +# mypy: enable-incomplete-feature=NewGenericSyntax +# This is adapted from PEP 695 +class Outer: + class Private: + pass + + # If the type parameter scope was like a traditional scope, + # the base class 'Private' would not be accessible here. + class Inner[T](Private, list[T]): + pass + + # Likewise, 'Inner' would not be available in these type annotations. + def method1[T](self, a: Inner[T]) -> Inner[T]: + return a + +[case testPEP695RedefineTypeParameterInScope] +# mypy: enable-incomplete-feature=NewGenericSyntax +class C[T]: + def m[T](self, x: T) -> T: # E: "T" already defined as a type parameter + return x + def m2(self) -> None: + def nested[T](x: T) -> T: # E: "T" already defined as a type parameter + return x + +def f[S, S](x: S) -> S: # E: "S" already defined as a type parameter + return x + +[case testPEP695ClassDecorator] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import Any + +T = 0 + +def decorator(x: str) -> Any: ... + +@decorator(T) # E: Argument 1 to "decorator" has incompatible type "int"; expected "str" +class C[T]: + pass From dfab362f2ad44d3f7e512be14ecb3e1de768fa5a Mon Sep 17 00:00:00 2001 From: Christopher Barber Date: Sat, 18 May 2024 06:15:43 -0400 Subject: [PATCH 0583/1617] Added [prop-decorator] code for unsupported property decorators (#14461) (#16571) Using a decorator before a @property now results in the narrower `prop-decorator` code, which is a subcode of `misc` for backward compatibility. I would have preferred to add a more general Unsupported error code and have this be a subcode of that, but this has to be a subcode of misc for backward compatibility. Fixes #14461 --- docs/source/error_code_list.rst | 23 +++++++++++++++++++++++ mypy/errorcodes.py | 8 +++++++- mypy/semanal.py | 6 ++++-- test-data/unit/semanal-errors.test | 6 ++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 48b3b689884f..64d9a1d03287 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1171,6 +1171,29 @@ annotations in an unchecked function: Note that mypy will still exit with return code ``0``, since such behaviour is specified by :pep:`484`. +.. _code-prop-decorator: + +Decorator preceding property not supported [prop-decorator] +----------------------------------------------------------- + +Mypy does not yet support analysis of decorators that precede the property +decorator. If the decorator does not preserve the declared type of the property, +mypy will not infer the correct type for the declaration. If the decorator cannot +be moved after the ``@property`` decorator, then you must use a type ignore +comment: + +.. code-block:: python + + class MyClass + @special # type: ignore[prop-decorator] + @property + def magic(self) -> str: + return "xyzzy" + +.. note:: + + For backward compatibility, this error code is a subcode of the generic ``[misc]`` code. + .. _code-syntax: Report syntax errors [syntax] diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 688bd6a4ddd5..7de796a70c8d 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -262,7 +262,6 @@ def __hash__(self) -> int: default_enabled=False, ) - # Syntax errors are often blocking. SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General") @@ -281,6 +280,13 @@ def __hash__(self) -> int: sub_code_of=MISC, ) +PROPERTY_DECORATOR = ErrorCode( + "prop-decorator", + "Decorators on top of @property are not supported", + "General", + sub_code_of=MISC, +) + NARROWED_TYPE_NOT_SUBTYPE: Final[ErrorCode] = ErrorCode( "narrowed-type-not-subtype", "Warn if a TypeIs function's narrowed type is not a subtype of the original type", diff --git a/mypy/semanal.py b/mypy/semanal.py index a66f43e17dd2..7d6c75b274ee 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -56,7 +56,7 @@ from mypy import errorcodes as codes, message_registry from mypy.constant_fold import constant_fold_expr -from mypy.errorcodes import ErrorCode +from mypy.errorcodes import PROPERTY_DECORATOR, ErrorCode from mypy.errors import Errors, report_internal_error from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.messages import ( @@ -1620,7 +1620,9 @@ def visit_decorator(self, dec: Decorator) -> None: if not no_type_check and self.recurse_into_functions: dec.func.accept(self) if could_be_decorated_property and dec.decorators and dec.var.is_property: - self.fail("Decorators on top of @property are not supported", dec) + self.fail( + "Decorators on top of @property are not supported", dec, code=PROPERTY_DECORATOR + ) if (dec.func.is_static or dec.func.is_class) and dec.var.is_property: self.fail("Only instance methods can be decorated with @property", dec) if dec.func.abstract_status == IS_ABSTRACT and dec.func.is_final: diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 82307f30877e..269536f868a4 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1241,6 +1241,12 @@ class A: @property # OK @dec def g(self) -> int: pass + @dec # type: ignore[misc] + @property + def h(self) -> int: pass + @dec # type: ignore[prop-decorator] + @property + def i(self) -> int: pass [builtins fixtures/property.pyi] [out] From 12837eaedcab5352dd2e3df925b21373597762e8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 18 May 2024 13:31:36 +0100 Subject: [PATCH 0584/1617] [mypyc] Show traceback when emitfunc unit test fails (#17262) This makes debugging test failures easier. --- mypy/test/helpers.py | 6 ++++-- mypyc/test/test_emitfunc.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 50de50e60004..f26c3b042e8c 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -104,7 +104,9 @@ def render_diff_range( output.write("\n") -def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) -> None: +def assert_string_arrays_equal( + expected: list[str], actual: list[str], msg: str, *, traceback: bool = False +) -> None: """Assert that two string arrays are equal. Display any differences in a human-readable form. @@ -136,7 +138,7 @@ def assert_string_arrays_equal(expected: list[str], actual: list[str], msg: str) "Update the test output using --update-data -n0 " "(you can additionally use the -k selector to update only specific tests)\n" ) - pytest.fail(msg, pytrace=False) + pytest.fail(msg, pytrace=traceback) def assert_module_equivalence(name: str, expected: Iterable[str], actual: Iterable[str]) -> None: diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index b16387aa40af..317427afac5a 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -841,7 +841,9 @@ def assert_emit( else: expected_lines = expected.rstrip().split("\n") expected_lines = [line.strip(" ") for line in expected_lines] - assert_string_arrays_equal(expected_lines, actual_lines, msg="Generated code unexpected") + assert_string_arrays_equal( + expected_lines, actual_lines, msg="Generated code unexpected", traceback=True + ) if skip_next: assert visitor.op_index == 1 else: From 828c0befb4b416bc668b994e719581f55d3d2275 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 18 May 2024 17:14:31 -0400 Subject: [PATCH 0585/1617] Support rename=True in collections.namedtuple (#17247) A pretty marginal feature but it's now tested in the typing conformance suite, and it was easy to add support. For reference: https://github.com/python/typing/blob/9f7f400bb7c4c79f1fb938402e0bb3198dac0054/conformance/tests/namedtuples_define_functional.py#L46, https://github.com/python/cpython/blob/7d8725ac6f3304677d71dabdb7c184e98a62d864/Lib/collections/__init__.py#L389 --- mypy/semanal_namedtuple.py | 60 ++++++++++++++++++++------ test-data/unit/check-namedtuple.test | 26 ++++++++++- test-data/unit/semanal-namedtuple.test | 4 +- 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 9a0be9d9c14c..f051c4ee36e9 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -5,9 +5,11 @@ from __future__ import annotations +import keyword from contextlib import contextmanager -from typing import Final, Iterator, List, Mapping, cast +from typing import Container, Final, Iterator, List, Mapping, cast +from mypy.errorcodes import ARG_TYPE, ErrorCode from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.messages import MessageBuilder from mypy.nodes import ( @@ -352,6 +354,7 @@ def parse_namedtuple_args( self.fail(f'Too few arguments for "{type_name}()"', call) return None defaults: list[Expression] = [] + rename = False if len(args) > 2: # Typed namedtuple doesn't support additional arguments. if fullname in TYPED_NAMEDTUPLE_NAMES: @@ -370,7 +373,17 @@ def parse_namedtuple_args( "{}()".format(type_name), arg, ) - break + elif arg_name == "rename": + arg = args[i] + if isinstance(arg, NameExpr) and arg.name in ("True", "False"): + rename = arg.name == "True" + else: + self.fail( + 'Boolean literal expected as the "rename" argument to ' + f"{type_name}()", + arg, + code=ARG_TYPE, + ) if call.arg_kinds[:2] != [ARG_POS, ARG_POS]: self.fail(f'Unexpected arguments to "{type_name}()"', call) return None @@ -417,17 +430,28 @@ def parse_namedtuple_args( return [], [], [], typename, [], False if not types: types = [AnyType(TypeOfAny.unannotated) for _ in items] - underscore = [item for item in items if item.startswith("_")] - if underscore: - self.fail( - f'"{type_name}()" field names cannot start with an underscore: ' - + ", ".join(underscore), - call, - ) + processed_items = [] + seen_names: set[str] = set() + for i, item in enumerate(items): + problem = self.check_namedtuple_field_name(item, seen_names) + if problem is None: + processed_items.append(item) + seen_names.add(item) + else: + if not rename: + self.fail(f'"{type_name}()" {problem}', call) + # Even if rename=False, we pretend that it is True. + # At runtime namedtuple creation would throw an error; + # applying the rename logic means we create a more sensible + # namedtuple. + new_name = f"_{i}" + processed_items.append(new_name) + seen_names.add(new_name) + if len(defaults) > len(items): self.fail(f'Too many defaults given in call to "{type_name}()"', call) defaults = defaults[: len(items)] - return items, types, defaults, typename, tvar_defs, True + return processed_items, types, defaults, typename, tvar_defs, True def parse_namedtuple_fields_with_types( self, nodes: list[Expression], context: Context @@ -666,5 +690,17 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]: # Helpers - def fail(self, msg: str, ctx: Context) -> None: - self.api.fail(msg, ctx) + def check_namedtuple_field_name(self, field: str, seen_names: Container[str]) -> str | None: + """Return None for valid fields, a string description for invalid ones.""" + if field in seen_names: + return f'has duplicate field name "{field}"' + elif not field.isidentifier(): + return f'field name "{field}" is not a valid identifier' + elif field.startswith("_"): + return f'field name "{field}" starts with an underscore' + elif keyword.iskeyword(field): + return f'field name "{field}" is a keyword' + return None + + def fail(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: + self.api.fail(msg, ctx, code=code) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 23e109e1af78..5e7c730162d8 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -22,10 +22,13 @@ a, b, c = x # E: Need more than 2 values to unpack (3 expected) x[2] # E: Tuple index out of range [builtins fixtures/tuple.pyi] -[case testNamedTupleNoUnderscoreFields] +[case testNamedTupleInvalidFields] from collections import namedtuple -X = namedtuple('X', 'x, _y, _z') # E: "namedtuple()" field names cannot start with an underscore: _y, _z +X = namedtuple('X', 'x, _y') # E: "namedtuple()" field name "_y" starts with an underscore +Y = namedtuple('Y', ['x', '1']) # E: "namedtuple()" field name "1" is not a valid identifier +Z = namedtuple('Z', ['x', 'def']) # E: "namedtuple()" field name "def" is a keyword +A = namedtuple('A', ['x', 'x']) # E: "namedtuple()" has duplicate field name "x" [builtins fixtures/tuple.pyi] [case testNamedTupleAccessingAttributes] @@ -125,6 +128,8 @@ E = namedtuple('E', 'a b', 0) [builtins fixtures/bool.pyi] [out] +main:4: error: Boolean literal expected as the "rename" argument to namedtuple() +main:5: error: Boolean literal expected as the "rename" argument to namedtuple() main:5: error: Argument "rename" to "namedtuple" has incompatible type "str"; expected "int" main:6: error: Unexpected keyword argument "unrecognized_arg" for "namedtuple" /test-data/unit/lib-stub/collections.pyi:3: note: "namedtuple" defined here @@ -145,6 +150,23 @@ Z = namedtuple('Z', ['x', 'y'], defaults='not a tuple') # E: List or tuple lite [builtins fixtures/list.pyi] +[case testNamedTupleRename] +from collections import namedtuple + +X = namedtuple('X', ['abc', 'def'], rename=False) # E: "namedtuple()" field name "def" is a keyword +Y = namedtuple('Y', ['x', 'x', 'def', '42', '_x'], rename=True) +y = Y(x=0, _1=1, _2=2, _3=3, _4=4) +reveal_type(y.x) # N: Revealed type is "Any" +reveal_type(y._1) # N: Revealed type is "Any" +reveal_type(y._2) # N: Revealed type is "Any" +reveal_type(y._3) # N: Revealed type is "Any" +reveal_type(y._4) # N: Revealed type is "Any" +y._0 # E: "Y" has no attribute "_0" +y._5 # E: "Y" has no attribute "_5" +y._x # E: "Y" has no attribute "_x" + +[builtins fixtures/list.pyi] + [case testNamedTupleWithItemTypes] from typing import NamedTuple N = NamedTuple('N', [('a', int), diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test index f396f799028f..16944391da86 100644 --- a/test-data/unit/semanal-namedtuple.test +++ b/test-data/unit/semanal-namedtuple.test @@ -165,7 +165,7 @@ N = namedtuple('N', ['x', 1]) # E: String literal expected as "namedtuple()" ite [case testNamedTupleWithUnderscoreItemName] from collections import namedtuple -N = namedtuple('N', ['_fallback']) # E: "namedtuple()" field names cannot start with an underscore: _fallback +N = namedtuple('N', ['_fallback']) # E: "namedtuple()" field name "_fallback" starts with an underscore [builtins fixtures/tuple.pyi] -- NOTE: The following code works at runtime but is not yet supported by mypy. @@ -197,7 +197,7 @@ N = NamedTuple('N', 1) # E: List or tuple literal expected as the second argumen [case testTypingNamedTupleWithUnderscoreItemName] from typing import NamedTuple -N = NamedTuple('N', [('_fallback', int)]) # E: "NamedTuple()" field names cannot start with an underscore: _fallback +N = NamedTuple('N', [('_fallback', int)]) # E: "NamedTuple()" field name "_fallback" starts with an underscore [builtins fixtures/tuple.pyi] [case testTypingNamedTupleWithUnexpectedNames] From 1c8346316fb8476bc12a6ba990228c96c241d619 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 18 May 2024 15:51:00 -0700 Subject: [PATCH 0586/1617] stubtest: changes for py313 (#17261) Technically it feels like we should be able to put the new dunders on `type` or something, but that wasn't enough to make false positives go away. But also we might not want to do that because it only applies to pure Python types --- mypy/stubtest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index dd43c472d67f..d78b71715159 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -634,6 +634,10 @@ def strip_prefix(s: str, prefix: str) -> str: if strip_prefix(stub_arg.variable.name, "__") == runtime_arg.name: return + nonspecific_names = {"object", "args"} + if runtime_arg.name in nonspecific_names: + return + def names_approx_match(a: str, b: str) -> bool: a = a.strip("_") b = b.strip("_") @@ -1455,6 +1459,8 @@ def verify_typealias( "__getattr__", # resulting behaviour might be typed explicitly "__setattr__", # defining this on a class can cause worse type checking "__vectorcalloffset__", # undocumented implementation detail of the vectorcall protocol + "__firstlineno__", + "__static_attributes__", # isinstance/issubclass hooks that type-checkers don't usually care about "__instancecheck__", "__subclasshook__", From c27f4f5858035a61a32e7149c95abdb5a4660d7e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 18 May 2024 15:56:40 -0700 Subject: [PATCH 0587/1617] Support namedtuple.__replace__ in Python 3.13 (#17259) --- mypy/semanal_namedtuple.py | 6 ++++++ test-data/unit/check-namedtuple.test | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index f051c4ee36e9..753deafe103b 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -623,6 +623,12 @@ def add_method( ret=selftype, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) + if self.options.python_version >= (3, 13): + add_method( + "__replace__", + ret=selftype, + args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], + ) def make_init_arg(var: Var) -> Argument: default = default_items.get(var.name, None) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 5e7c730162d8..a0d984b30279 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1398,3 +1398,17 @@ class Test3(NamedTuple, metaclass=type): # E: Unexpected keyword argument "meta ... [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + + +[case testNamedTupleDunderReplace] +# flags: --python-version 3.13 +from typing import NamedTuple + +class A(NamedTuple): + x: int + +A(x=0).__replace__(x=1) +A(x=0).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "A" has incompatible type "str"; expected "int" +A(x=0).__replace__(y=1) # E: Unexpected keyword argument "y" for "__replace__" of "A" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] From ac8a5a76d4944890b14da427b75d93c329c68003 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 19 May 2024 09:58:58 +0100 Subject: [PATCH 0588/1617] [mypyc] Allow specifying primitives as pure (#17263) Pure primitives have no side effects, take only immutable arguments, and never fail. These properties will enable additional optimizations. For example, it doesn't matter in which order these primitives are evaluated, and we can perform common subexpression elimination on them. Only mark a few primitives as pure for now, but we can generalize this later. --- mypyc/ir/ops.py | 14 ++++++++++++++ mypyc/irbuild/ll_builder.py | 2 ++ mypyc/primitives/int_ops.py | 2 ++ mypyc/primitives/registry.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 7df4347171da..377266e797d9 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -600,6 +600,7 @@ def __init__( ordering: list[int] | None, extra_int_constants: list[tuple[int, RType]], priority: int, + is_pure: bool, ) -> None: # Each primitive much have a distinct name, but otherwise they are arbitrary. self.name: Final = name @@ -617,6 +618,11 @@ def __init__( self.ordering: Final = ordering self.extra_int_constants: Final = extra_int_constants self.priority: Final = priority + # Pure primitives have no side effects, take immutable arguments, and + # never fail. They support additional optimizations. + self.is_pure: Final = is_pure + if is_pure: + assert error_kind == ERR_NEVER def __repr__(self) -> str: return f"" @@ -1036,6 +1042,8 @@ def __init__( error_kind: int, line: int, var_arg_idx: int = -1, + *, + is_pure: bool = False, ) -> None: self.error_kind = error_kind super().__init__(line) @@ -1046,6 +1054,12 @@ def __init__( self.is_borrowed = is_borrowed # The position of the first variable argument in args (if >= 0) self.var_arg_idx = var_arg_idx + # Is the function pure? Pure functions have no side effects + # and all the arguments are immutable. Pure functions support + # additional optimizations. Pure functions never fail. + self.is_pure = is_pure + if is_pure: + assert error_kind == ERR_NEVER def sources(self) -> list[Value]: return self.args diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a05040e25f76..0c9310e6a5ca 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1821,6 +1821,7 @@ def call_c( error_kind, line, var_arg_idx, + is_pure=desc.is_pure, ) ) if desc.is_borrowed: @@ -1903,6 +1904,7 @@ def primitive_op( desc.ordering, desc.extra_int_constants, desc.priority, + is_pure=desc.is_pure, ) return self.call_c(c_desc, args, line, result_type) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 4413028a0e83..2eff233403f4 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -199,6 +199,7 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: return_type=bit_rprimitive, c_function_name="CPyTagged_IsEq_", error_kind=ERR_NEVER, + is_pure=True, ) # Less than operation on two boxed tagged integers @@ -207,6 +208,7 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: return_type=bit_rprimitive, c_function_name="CPyTagged_IsLt_", error_kind=ERR_NEVER, + is_pure=True, ) int64_divide_op = custom_op( diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 1472885a4829..5190b01adf4a 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -60,6 +60,7 @@ class CFunctionDescription(NamedTuple): ordering: list[int] | None extra_int_constants: list[tuple[int, RType]] priority: int + is_pure: bool # A description for C load operations including LoadGlobal and LoadAddress @@ -97,6 +98,7 @@ def method_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, + is_pure: bool = False, ) -> CFunctionDescription: """Define a c function call op that replaces a method call. @@ -121,6 +123,8 @@ def method_op( steals: description of arguments that this steals (ref count wise) is_borrowed: if True, returned value is borrowed (no need to decrease refcount) priority: if multiple ops match, the one with the highest priority is picked + is_pure: if True, declare that the C function has no side effects, takes immutable + arguments, and never raises an exception """ if extra_int_constants is None: extra_int_constants = [] @@ -138,6 +142,7 @@ def method_op( ordering, extra_int_constants, priority, + is_pure=is_pure, ) ops.append(desc) return desc @@ -183,6 +188,7 @@ def function_op( ordering, extra_int_constants, priority, + is_pure=False, ) ops.append(desc) return desc @@ -228,6 +234,7 @@ def binary_op( ordering=ordering, extra_int_constants=extra_int_constants, priority=priority, + is_pure=False, ) ops.append(desc) return desc @@ -244,6 +251,8 @@ def custom_op( extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, + *, + is_pure: bool = False, ) -> CFunctionDescription: """Create a one-off CallC op that can't be automatically generated from the AST. @@ -264,6 +273,7 @@ def custom_op( ordering, extra_int_constants, 0, + is_pure=is_pure, ) @@ -279,6 +289,7 @@ def custom_primitive_op( extra_int_constants: list[tuple[int, RType]] | None = None, steals: StealsDescription = False, is_borrowed: bool = False, + is_pure: bool = False, ) -> PrimitiveDescription: """Define a primitive op that can't be automatically generated based on the AST. @@ -299,6 +310,7 @@ def custom_primitive_op( ordering=ordering, extra_int_constants=extra_int_constants, priority=0, + is_pure=is_pure, ) @@ -314,6 +326,7 @@ def unary_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, + is_pure: bool = False, ) -> CFunctionDescription: """Define a c function call op for an unary operation. @@ -338,6 +351,7 @@ def unary_op( ordering, extra_int_constants, priority, + is_pure=is_pure, ) ops.append(desc) return desc From e8a26308d5d06925cf769b62f41ef2e4bc546ada Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 20 May 2024 15:54:27 -0400 Subject: [PATCH 0589/1617] Stubtest: ignore `_ios_support` (#17270) Trying to import this module on py313 raises RuntimeError on Windows, and it doesn't seem important --- mypy/stubtest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d78b71715159..a7cde8b8fe6c 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1891,7 +1891,9 @@ class _Arguments: # typeshed added a stub for __main__, but that causes stubtest to check itself -ANNOYING_STDLIB_MODULES: typing_extensions.Final = frozenset({"antigravity", "this", "__main__"}) +ANNOYING_STDLIB_MODULES: typing_extensions.Final = frozenset( + {"antigravity", "this", "__main__", "_ios_support"} +) def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: From 3579c6149b74bee4717fb5fcac9e4351d36fe1b5 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 20 May 2024 22:33:13 -0400 Subject: [PATCH 0590/1617] Add test documenting #17230 (#17199) --- test-data/unit/check-python310.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 2b56d2db07a9..8991b65f67b5 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1220,6 +1220,21 @@ def main() -> None: case a: reveal_type(a) # N: Revealed type is "builtins.int" +[case testMatchCapturePatternFromAsyncFunctionReturningUnion-xfail] +async def func1(arg: bool) -> str | int: ... +async def func2(arg: bool) -> bytes | int: ... + +async def main() -> None: + match await func1(True): + case str(a): + match await func2(True): + case c: + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(c) # N: Revealed type is "Union[builtins.bytes, builtins.int]" + reveal_type(a) # N: Revealed type is "builtins.str" + case a: + reveal_type(a) # N: Revealed type is "builtins.int" + -- Guards -- [case testMatchSimplePatternGuard] From f5afdcd01adfe2b082d9f61f467920845f9d1176 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 20 May 2024 20:08:36 -0700 Subject: [PATCH 0591/1617] Add support for __spec__ (#14739) Fixes #4145 Co-authored-by: Joongi Kim --- mypy/nodes.py | 1 + mypy/semanal.py | 18 ++++++++++++++++++ test-data/unit/check-basic.test | 8 ++++++++ test-data/unit/fine-grained-inspect.test | 2 +- test-data/unit/pythoneval.test | 6 ++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 6657ab8cb65f..21051ffa4d0b 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -117,6 +117,7 @@ def set_line( "__file__": "__builtins__.str", "__package__": "__builtins__.str", "__annotations__": None, # dict[str, Any] bounded in add_implicit_module_attrs() + "__spec__": None, # importlib.machinery.ModuleSpec bounded in add_implicit_module_attrs() } diff --git a/mypy/semanal.py b/mypy/semanal.py index 7d6c75b274ee..61c4eb737fb9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -283,6 +283,7 @@ TypeVarTupleType, TypeVarType, UnboundType, + UnionType, UnpackType, get_proper_type, get_proper_types, @@ -635,6 +636,7 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None: str_type: Type | None = self.named_type_or_none("builtins.str") if str_type is None: str_type = UnboundType("builtins.str") + inst: Type | None for name, t in implicit_module_attrs.items(): if name == "__doc__": typ: Type = str_type @@ -660,6 +662,22 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None: self.defer() return typ = inst + elif name == "__spec__": + if self.options.use_builtins_fixtures: + inst = self.named_type_or_none("builtins.object") + else: + inst = self.named_type_or_none("importlib.machinery.ModuleSpec") + if inst is None: + if self.final_iteration: + inst = self.named_type_or_none("builtins.object") + assert inst is not None, "Cannot find builtins.object" + else: + self.defer() + return + if file_node.name == "__main__": + # https://docs.python.org/3/reference/import.html#main-spec + inst = UnionType.make_union([inst, NoneType()]) + typ = inst else: assert t is not None, f"type should be specified for {name}" typ = UnboundType(t) diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 7a426c3eca9f..959d80cb2104 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -219,6 +219,14 @@ reveal_type(__doc__) # N: Revealed type is "builtins.str" reveal_type(__file__) # N: Revealed type is "builtins.str" reveal_type(__package__) # N: Revealed type is "builtins.str" reveal_type(__annotations__) # N: Revealed type is "builtins.dict[builtins.str, Any]" +# This will actually reveal Union[importlib.machinery.ModuleSpec, None] +reveal_type(__spec__) # N: Revealed type is "Union[builtins.object, None]" + +import module +reveal_type(module.__name__) # N: Revealed type is "builtins.str" +# This will actually reveal importlib.machinery.ModuleSpec +reveal_type(module.__spec__) # N: Revealed type is "builtins.object" +[file module.py] [builtins fixtures/primitives.pyi] diff --git a/test-data/unit/fine-grained-inspect.test b/test-data/unit/fine-grained-inspect.test index f8ce35585c10..ed89f2f099f9 100644 --- a/test-data/unit/fine-grained-inspect.test +++ b/test-data/unit/fine-grained-inspect.test @@ -236,7 +236,7 @@ class C: ... [builtins fixtures/module.pyi] [out] == -{"": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]} +{"": ["C", "__annotations__", "__doc__", "__file__", "__name__", "__package__", "__spec__", "bar", "x"], "ModuleType": ["__file__", "__getattr__"]} [case testInspectModuleDef] # inspect2: --show=definition --include-kind tmp/foo.py:2:1 diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 0ed3540b6bb9..acb0ff88ad04 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -107,14 +107,20 @@ f [case testModuleAttributes] import math import typing +print(type(__spec__)) print(math.__name__) +print(math.__spec__.name) print(type(math.__dict__)) print(type(math.__doc__ or '')) +print(type(math.__spec__).__name__) print(math.__class__) [out] + +math math +ModuleSpec [case testSpecialAttributes] From 2892ed4d0e91e7b715a246e6d4530a4685daea1e Mon Sep 17 00:00:00 2001 From: Alexander Leopold Shon <46231621+alexlshon@users.noreply.github.com> Date: Tue, 21 May 2024 15:38:10 -0500 Subject: [PATCH 0592/1617] Fix case involving non-ASCII chars on Windows (#17275) Fixes #16669 One can replicate this error in Windows using Python3.8 just by calling the mypy/pyinfo.py module using a slightly modified code of the `get_search_dirs` function where the python executable doesn't match the value of sys.executable. The only modification made to this code from `get_search_dirs` is the adding of a non-ascii-path to the env parameter --- mypy/pyinfo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index f262ac8b2132..f5f35800d44e 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -71,6 +71,7 @@ def getsearchdirs() -> tuple[list[str], list[str]]: if __name__ == "__main__": + sys.stdout.reconfigure(encoding="utf-8") # type: ignore [attr-defined] if sys.argv[-1] == "getsearchdirs": print(repr(getsearchdirs())) else: From 42157ba5a3ebe7117ee5e4952d3cd7696305bdd4 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 21 May 2024 13:40:02 -0700 Subject: [PATCH 0593/1617] stubgen: preserve enum value initialisers (#17125) See https://github.com/python/typing-council/issues/11 --- mypy/stubgen.py | 7 +++++++ test-data/unit/stubgen.test | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 7721366f5c0c..22028694ad6b 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -453,6 +453,7 @@ def __init__( self.analyzed = analyzed # Short names of methods defined in the body of the current class self.method_names: set[str] = set() + self.processing_enum = False self.processing_dataclass = False def visit_mypy_file(self, o: MypyFile) -> None: @@ -727,6 +728,8 @@ def visit_class_def(self, o: ClassDef) -> None: if base_types: for base in base_types: self.import_tracker.require_name(base) + if self.analyzed and o.info.is_enum: + self.processing_enum = True if isinstance(o.metaclass, (NameExpr, MemberExpr)): meta = o.metaclass.accept(AliasPrinter(self)) base_types.append("metaclass=" + meta) @@ -756,6 +759,7 @@ def visit_class_def(self, o: ClassDef) -> None: self._state = CLASS self.method_names = set() self.processing_dataclass = False + self.processing_enum = False self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: @@ -1153,6 +1157,9 @@ def get_init( # Final without type argument is invalid in stubs. final_arg = self.get_str_type_of_node(rvalue) typename += f"[{final_arg}]" + elif self.processing_enum: + initializer, _ = self.get_str_default_of_node(rvalue) + return f"{self._indent}{lvalue} = {initializer}\n" elif self.processing_dataclass: # attribute without annotation is not a dataclass field, don't add annotation. return f"{self._indent}{lvalue} = ...\n" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 53baa2c0ca06..916e2e3a8e17 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4342,3 +4342,27 @@ alias = tuple[()] def f(x: tuple[()]): ... class C(tuple[()]): ... + +[case testPreserveEnumValue_semanal] +from enum import Enum + +class Foo(Enum): + A = 1 + B = 2 + C = 3 + +class Bar(Enum): + A = object() + B = "a" + "b" + +[out] +from enum import Enum + +class Foo(Enum): + A = 1 + B = 2 + C = 3 + +class Bar(Enum): + A = ... + B = ... From 99dd3145ec1506415d3d2c7cf0bcb15735acac00 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Tue, 21 May 2024 22:55:31 +0200 Subject: [PATCH 0594/1617] Automatically set -n=0 when running tests with --update-data (#17204) Unless there is a reason to have the error, I think this improves the developer experience. --- mypy/test/data.py | 12 +++++++----- mypy/test/helpers.py | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 32f6354cc162..ee567afe2125 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -620,11 +620,13 @@ def pytest_addoption(parser: Any) -> None: ) -def pytest_configure(config: pytest.Config) -> None: - if config.getoption("--update-data") and config.getoption("--numprocesses", default=1) > 1: - raise pytest.UsageError( - "--update-data incompatible with parallelized tests; re-run with -n 1" - ) +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: pytest.Config) -> None: + if config.getoption("--collectonly"): + return + # --update-data is not compatible with parallelized tests, disable parallelization + if config.getoption("--update-data"): + config.option.numprocesses = 0 # This function name is special to pytest. See diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f26c3b042e8c..f532e77b82d3 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -135,8 +135,8 @@ def assert_string_arrays_equal( show_align_message(expected[first_diff], actual[first_diff]) sys.stderr.write( - "Update the test output using --update-data -n0 " - "(you can additionally use the -k selector to update only specific tests)\n" + "Update the test output using --update-data " + "(implies -n0; you can additionally use the -k selector to update only specific tests)\n" ) pytest.fail(msg, pytrace=traceback) From ca393dd07ab729a860215b1ff0257bc599bf1068 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Wed, 22 May 2024 11:35:22 +1200 Subject: [PATCH 0595/1617] fix: annotated argument's `var` node type is explicit, not inferred (#17217) Fixes #17216 During conversion from a standard library AST to the mypy AST, `Var` nodes were being created inside `Argument` nodes without acknowledging the presence of a type annotation, leading to the `Var` node's type as being always set as *inferred*: https://github.com/python/mypy/blob/fb31409b392c5533b25173705d62ed385ee39cfb/mypy/nodes.py#L988 This causes an error at https://github.com/python/mypy/blob/fb31409b392c5533b25173705d62ed385ee39cfb/mypyc/irbuild/expression.py#L161-L164 The fix simply acknowledges any presence of a type annotation, so the type of the relevant `Var` node is no longer considered inferred if an annotation is present. --- mypy/fastparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index ee042b96339f..a32e7d8f9978 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1116,7 +1116,7 @@ def make_argument( if argument_elide_name(arg.arg): pos_only = True - argument = Argument(Var(arg.arg), arg_type, self.visit(default), kind, pos_only) + argument = Argument(Var(arg.arg, arg_type), arg_type, self.visit(default), kind, pos_only) argument.set_line( arg.lineno, arg.col_offset, From 0871c93334738d2d4429056f19223d92ffb094ce Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 22 May 2024 22:54:08 -0700 Subject: [PATCH 0596/1617] Add support for functools.partial (#16939) Fixes #1484 Turns out that this is currently the second most popular mypy issue (and first most popular is a type system feature request that would need a PEP). I'm sure there's stuff missing, but this should handle most cases. --- mypy/checkexpr.py | 34 ++--- mypy/fixup.py | 3 + mypy/plugins/default.py | 15 ++- mypy/plugins/functools.py | 144 ++++++++++++++++++++- mypy/server/astdiff.py | 9 ++ mypy/types.py | 29 ++++- test-data/unit/check-functools.test | 180 ++++++++++++++++++++++++++ test-data/unit/check-incremental.test | 61 +++++++++ test-data/unit/lib-stub/functools.pyi | 6 +- 9 files changed, 454 insertions(+), 27 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index e8a2e501a452..4b0f5fe533d8 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1229,14 +1229,14 @@ def apply_function_plugin( assert callback is not None # Assume that caller ensures this return callback( FunctionContext( - formal_arg_types, - formal_arg_kinds, - callee.arg_names, - formal_arg_names, - callee.ret_type, - formal_arg_exprs, - context, - self.chk, + arg_types=formal_arg_types, + arg_kinds=formal_arg_kinds, + callee_arg_names=callee.arg_names, + arg_names=formal_arg_names, + default_return_type=callee.ret_type, + args=formal_arg_exprs, + context=context, + api=self.chk, ) ) else: @@ -1246,15 +1246,15 @@ def apply_function_plugin( object_type = get_proper_type(object_type) return method_callback( MethodContext( - object_type, - formal_arg_types, - formal_arg_kinds, - callee.arg_names, - formal_arg_names, - callee.ret_type, - formal_arg_exprs, - context, - self.chk, + type=object_type, + arg_types=formal_arg_types, + arg_kinds=formal_arg_kinds, + callee_arg_names=callee.arg_names, + arg_names=formal_arg_names, + default_return_type=callee.ret_type, + args=formal_arg_exprs, + context=context, + api=self.chk, ) ) diff --git a/mypy/fixup.py b/mypy/fixup.py index 849a6483d724..f2b5bc17d32e 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -239,6 +239,9 @@ def visit_instance(self, inst: Instance) -> None: a.accept(self) if inst.last_known_value is not None: inst.last_known_value.accept(self) + if inst.extra_attrs: + for v in inst.extra_attrs.attrs.values(): + v.accept(self) def visit_type_alias_type(self, t: TypeAliasType) -> None: type_ref = t.type_ref diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 170d3c85b5f9..3ad301a15f6c 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -47,6 +47,10 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] return ctypes.array_constructor_callback elif fullname == "functools.singledispatch": return singledispatch.create_singledispatch_function_callback + elif fullname == "functools.partial": + import mypy.plugins.functools + + return mypy.plugins.functools.partial_new_callback return None @@ -118,6 +122,10 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No return singledispatch.singledispatch_register_callback elif fullname == singledispatch.REGISTER_CALLABLE_CALL_METHOD: return singledispatch.call_singledispatch_function_after_register_argument + elif fullname == "functools.partial.__call__": + import mypy.plugins.functools + + return mypy.plugins.functools.partial_call_callback return None def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: @@ -155,12 +163,13 @@ def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], def get_class_decorator_hook_2( self, fullname: str ) -> Callable[[ClassDefContext], bool] | None: - from mypy.plugins import attrs, dataclasses, functools + import mypy.plugins.functools + from mypy.plugins import attrs, dataclasses if fullname in dataclasses.dataclass_makers: return dataclasses.dataclass_class_maker_callback - elif fullname in functools.functools_total_ordering_makers: - return functools.functools_total_ordering_maker_callback + elif fullname in mypy.plugins.functools.functools_total_ordering_makers: + return mypy.plugins.functools.functools_total_ordering_maker_callback elif fullname in attrs.attr_class_makers: return attrs.attr_class_maker_callback elif fullname in attrs.attr_dataclass_makers: diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 792ed6669503..81a3b4d96ef3 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -4,10 +4,22 @@ from typing import Final, NamedTuple +import mypy.checker import mypy.plugin -from mypy.nodes import ARG_POS, ARG_STAR2, Argument, FuncItem, Var +from mypy.argmap import map_actuals_to_formals +from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, FuncItem, Var from mypy.plugins.common import add_method_to_class -from mypy.types import AnyType, CallableType, Type, TypeOfAny, UnboundType, get_proper_type +from mypy.types import ( + AnyType, + CallableType, + Instance, + Overloaded, + Type, + TypeOfAny, + UnboundType, + UninhabitedType, + get_proper_type, +) functools_total_ordering_makers: Final = {"functools.total_ordering"} @@ -102,3 +114,131 @@ def _analyze_class(ctx: mypy.plugin.ClassDefContext) -> dict[str, _MethodInfo | comparison_methods[name] = None return comparison_methods + + +def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: + """Infer a more precise return type for functools.partial""" + if not isinstance(ctx.api, mypy.checker.TypeChecker): # use internals + return ctx.default_return_type + if len(ctx.arg_types) != 3: # fn, *args, **kwargs + return ctx.default_return_type + if len(ctx.arg_types[0]) != 1: + return ctx.default_return_type + + if isinstance(get_proper_type(ctx.arg_types[0][0]), Overloaded): + # TODO: handle overloads, just fall back to whatever the non-plugin code does + return ctx.default_return_type + fn_type = ctx.api.extract_callable_type(ctx.arg_types[0][0], ctx=ctx.default_return_type) + if fn_type is None: + return ctx.default_return_type + + defaulted = fn_type.copy_modified( + arg_kinds=[ + ( + ArgKind.ARG_OPT + if k == ArgKind.ARG_POS + else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) + ) + for k in fn_type.arg_kinds + ] + ) + if defaulted.line < 0: + # Make up a line number if we don't have one + defaulted.set_line(ctx.default_return_type) + + actual_args = [a for param in ctx.args[1:] for a in param] + actual_arg_kinds = [a for param in ctx.arg_kinds[1:] for a in param] + actual_arg_names = [a for param in ctx.arg_names[1:] for a in param] + actual_types = [a for param in ctx.arg_types[1:] for a in param] + + _, bound = ctx.api.expr_checker.check_call( + callee=defaulted, + args=actual_args, + arg_kinds=actual_arg_kinds, + arg_names=actual_arg_names, + context=defaulted, + ) + bound = get_proper_type(bound) + if not isinstance(bound, CallableType): + return ctx.default_return_type + + formal_to_actual = map_actuals_to_formals( + actual_kinds=actual_arg_kinds, + actual_names=actual_arg_names, + formal_kinds=fn_type.arg_kinds, + formal_names=fn_type.arg_names, + actual_arg_type=lambda i: actual_types[i], + ) + + partial_kinds = [] + partial_types = [] + partial_names = [] + # We need to fully apply any positional arguments (they cannot be respecified) + # However, keyword arguments can be respecified, so just give them a default + for i, actuals in enumerate(formal_to_actual): + if len(bound.arg_types) == len(fn_type.arg_types): + arg_type = bound.arg_types[i] + if isinstance(get_proper_type(arg_type), UninhabitedType): + arg_type = fn_type.arg_types[i] # bit of a hack + else: + # TODO: I assume that bound and fn_type have the same arguments. It appears this isn't + # true when PEP 646 things are happening. See testFunctoolsPartialTypeVarTuple + arg_type = fn_type.arg_types[i] + + if not actuals or fn_type.arg_kinds[i] in (ArgKind.ARG_STAR, ArgKind.ARG_STAR2): + partial_kinds.append(fn_type.arg_kinds[i]) + partial_types.append(arg_type) + partial_names.append(fn_type.arg_names[i]) + elif actuals: + if any(actual_arg_kinds[j] == ArgKind.ARG_POS for j in actuals): + continue + kind = actual_arg_kinds[actuals[0]] + if kind == ArgKind.ARG_NAMED: + kind = ArgKind.ARG_NAMED_OPT + partial_kinds.append(kind) + partial_types.append(arg_type) + partial_names.append(fn_type.arg_names[i]) + + ret_type = bound.ret_type + if isinstance(get_proper_type(ret_type), UninhabitedType): + ret_type = fn_type.ret_type # same kind of hack as above + + partially_applied = fn_type.copy_modified( + arg_types=partial_types, + arg_kinds=partial_kinds, + arg_names=partial_names, + ret_type=ret_type, + ) + + ret = ctx.api.named_generic_type("functools.partial", [ret_type]) + ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied) + return ret + + +def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: + """Infer a more precise return type for functools.partial.__call__.""" + if ( + not isinstance(ctx.api, mypy.checker.TypeChecker) # use internals + or not isinstance(ctx.type, Instance) + or ctx.type.type.fullname != "functools.partial" + or not ctx.type.extra_attrs + or "__mypy_partial" not in ctx.type.extra_attrs.attrs + ): + return ctx.default_return_type + + partial_type = ctx.type.extra_attrs.attrs["__mypy_partial"] + if len(ctx.arg_types) != 2: # *args, **kwargs + return ctx.default_return_type + + args = [a for param in ctx.args for a in param] + arg_kinds = [a for param in ctx.arg_kinds for a in param] + arg_names = [a for param in ctx.arg_names for a in param] + + result = ctx.api.expr_checker.check_call( + callee=partial_type, + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=ctx.context, + ) + return result[0] diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 5323bf2c57cb..f8a874005adb 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -378,11 +378,20 @@ def visit_deleted_type(self, typ: DeletedType) -> SnapshotItem: return snapshot_simple_type(typ) def visit_instance(self, typ: Instance) -> SnapshotItem: + extra_attrs: SnapshotItem + if typ.extra_attrs: + extra_attrs = ( + tuple(sorted((k, v.accept(self)) for k, v in typ.extra_attrs.attrs.items())), + tuple(typ.extra_attrs.immutable), + ) + else: + extra_attrs = () return ( "Instance", encode_optional_str(typ.type.fullname), snapshot_types(typ.args), ("None",) if typ.last_known_value is None else snapshot_type(typ.last_known_value), + extra_attrs, ) def visit_type_var(self, typ: TypeVarType) -> SnapshotItem: diff --git a/mypy/types.py b/mypy/types.py index 5573dc9efe0e..0ef3803c5687 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1322,6 +1322,23 @@ def copy(self) -> ExtraAttrs: def __repr__(self) -> str: return f"ExtraAttrs({self.attrs!r}, {self.immutable!r}, {self.mod_name!r})" + def serialize(self) -> JsonDict: + return { + ".class": "ExtraAttrs", + "attrs": {k: v.serialize() for k, v in self.attrs.items()}, + "immutable": list(self.immutable), + "mod_name": self.mod_name, + } + + @classmethod + def deserialize(cls, data: JsonDict) -> ExtraAttrs: + assert data[".class"] == "ExtraAttrs" + return ExtraAttrs( + {k: deserialize_type(v) for k, v in data["attrs"].items()}, + set(data["immutable"]), + data["mod_name"], + ) + class Instance(ProperType): """An instance type of form C[T1, ..., Tn]. @@ -1434,6 +1451,7 @@ def serialize(self) -> JsonDict | str: data["args"] = [arg.serialize() for arg in self.args] if self.last_known_value is not None: data["last_known_value"] = self.last_known_value.serialize() + data["extra_attrs"] = self.extra_attrs.serialize() if self.extra_attrs else None return data @classmethod @@ -1452,6 +1470,8 @@ def deserialize(cls, data: JsonDict | str) -> Instance: inst.type_ref = data["type_ref"] # Will be fixed up by fixup.py later. if "last_known_value" in data: inst.last_known_value = LiteralType.deserialize(data["last_known_value"]) + if data.get("extra_attrs") is not None: + inst.extra_attrs = ExtraAttrs.deserialize(data["extra_attrs"]) return inst def copy_modified( @@ -1461,13 +1481,14 @@ def copy_modified( last_known_value: Bogus[LiteralType | None] = _dummy, ) -> Instance: new = Instance( - self.type, - args if args is not _dummy else self.args, - self.line, - self.column, + typ=self.type, + args=args if args is not _dummy else self.args, + line=self.line, + column=self.column, last_known_value=( last_known_value if last_known_value is not _dummy else self.last_known_value ), + extra_attrs=self.extra_attrs, ) # We intentionally don't copy the extra_attrs here, so they will be erased. new.can_be_true = self.can_be_true diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index e721a56850e1..5af5dfc8e469 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -144,3 +144,183 @@ def f(d: D[C]) -> None: d: D[int] # E: Type argument "int" of "D" must be a subtype of "C" [builtins fixtures/dict.pyi] + +[case testFunctoolsPartialBasic] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... # N: "foo" defined here + +p1 = functools.partial(foo) +p1(1, "a", 3) # OK +p1(1, "a", c=3) # OK +p1(1, b="a", c=3) # OK + +reveal_type(p1) # N: Revealed type is "functools.partial[builtins.int]" + +def takes_callable_int(f: Callable[..., int]) -> None: ... +def takes_callable_str(f: Callable[..., str]) -> None: ... +takes_callable_int(p1) +takes_callable_str(p1) # E: Argument 1 to "takes_callable_str" has incompatible type "partial[int]"; expected "Callable[..., str]" \ + # N: "partial[int].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], int]" + +p2 = functools.partial(foo, 1) +p2("a") # OK +p2("a", 3) # OK +p2("a", c=3) # OK +p2(1, 3) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" +p2(1, "a", 3) # E: Too many arguments for "foo" \ + # E: Argument 1 to "foo" has incompatible type "int"; expected "str" \ + # E: Argument 2 to "foo" has incompatible type "str"; expected "int" +p2(a=1, b="a", c=3) # E: Unexpected keyword argument "a" for "foo" + +p3 = functools.partial(foo, b="a") +p3(1) # OK +p3(1, c=3) # OK +p3(a=1) # OK +p3(1, b="a", c=3) # OK, keywords can be clobbered +p3(1, 3) # E: Too many positional arguments for "foo" \ + # E: Argument 2 to "foo" has incompatible type "int"; expected "str" + +functools.partial(foo, "a") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" +functools.partial(foo, b=1) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" +functools.partial(foo, a=1, b=2, c=3) # E: Argument 2 to "foo" has incompatible type "int"; expected "str" +functools.partial(1) # E: "int" not callable \ + # E: Argument 1 to "partial" has incompatible type "int"; expected "Callable[..., Never]" +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialStar] +import functools + +def foo(a: int, b: str, *args: int, d: str, **kwargs: int) -> int: ... + +p1 = functools.partial(foo, 1, d="a", x=9) +p1("a", 2, 3, 4) # OK +p1("a", 2, 3, 4, d="a") # OK +p1("a", 2, 3, 4, "a") # E: Argument 5 to "foo" has incompatible type "str"; expected "int" +p1("a", 2, 3, 4, x="a") # E: Argument "x" to "foo" has incompatible type "str"; expected "int" + +p2 = functools.partial(foo, 1, "a") +p2(2, 3, 4, d="a") # OK +p2("a") # E: Missing named argument "d" for "foo" \ + # E: Argument 1 to "foo" has incompatible type "str"; expected "int" +p2(2, 3, 4) # E: Missing named argument "d" for "foo" + +functools.partial(foo, 1, "a", "b", "c", d="a") # E: Argument 3 to "foo" has incompatible type "str"; expected "int" \ + # E: Argument 4 to "foo" has incompatible type "str"; expected "int" + +def bar(*a: bytes, **k: int): + p1("a", 2, 3, 4, d="a", **k) + p1("a", d="a", **k) + p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str" + p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str" + p1(*a) # E: List or tuple expected as variadic arguments +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialGeneric] +from typing import TypeVar +import functools + +T = TypeVar("T") +U = TypeVar("U") + +def foo(a: T, b: T) -> T: ... + +p1 = functools.partial(foo, 1) +reveal_type(p1(2)) # N: Revealed type is "builtins.int" +p1("a") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" + +p2 = functools.partial(foo, "a") +p2(1) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" +reveal_type(p2("a")) # N: Revealed type is "builtins.str" + +def bar(a: T, b: U) -> U: ... + +p3 = functools.partial(bar, 1) +reveal_type(p3(2)) # N: Revealed type is "builtins.int" +reveal_type(p3("a")) # N: Revealed type is "builtins.str" +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialCallable] +from typing import Callable +import functools + +def main1(f: Callable[[int, str], int]) -> None: + p = functools.partial(f, 1) + p("a") # OK + p(1) # E: Argument 1 has incompatible type "int"; expected "str" + + functools.partial(f, a=1) # E: Unexpected keyword argument "a" + +class CallbackProto: + def __call__(self, a: int, b: str) -> int: ... + +def main2(f: CallbackProto) -> None: + p = functools.partial(f, b="a") + p(1) # OK + p("a") # E: Argument 1 to "__call__" of "CallbackProto" has incompatible type "str"; expected "int" +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialOverload] +from typing import overload +import functools + +@overload +def foo(a: int, b: str) -> int: ... +@overload +def foo(a: str, b: int) -> str: ... +def foo(*a, **k): ... + +p1 = functools.partial(foo) +reveal_type(p1(1, "a")) # N: Revealed type is "builtins.int" +reveal_type(p1("a", 1)) # N: Revealed type is "builtins.int" +p1(1, 2) # TODO: false negative +p1("a", "b") # TODO: false negative +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialTypeGuard] +import functools +from typing_extensions import TypeGuard + +def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ... # E: "list" is not subscriptable, use "typing.List" instead + +reveal_type(functools.partial(is_str_list, [1, 2, 3])) # N: Revealed type is "functools.partial[builtins.bool]" +reveal_type(functools.partial(is_str_list, [1, 2, 3])()) # N: Revealed type is "builtins.bool" +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialType] +import functools +from typing import Type + +class A: + def __init__(self, a: int, b: str) -> None: ... # N: "A" defined here + +p = functools.partial(A, 1) +reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]" + +p("a") # OK +p(1) # E: Argument 1 to "A" has incompatible type "int"; expected "str" +p(z=1) # E: Unexpected keyword argument "z" for "A" + +def main(t: Type[A]) -> None: + p = functools.partial(t, 1) # E: "Type[A]" not callable + reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]" + + p("a") # OK + p(1) # False negative + p(z=1) # False negative + +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialTypeVarTuple] +import functools +import typing +Ts = typing.TypeVarTuple("Ts") +def foo(fn: typing.Callable[[typing.Unpack[Ts]], None], /, *arg: typing.Unpack[Ts], kwarg: str) -> None: ... +p = functools.partial(foo, kwarg="asdf") + +def bar(a: int, b: str, c: float) -> None: ... +p(bar, 1, "a", 3.0) # OK +p(bar, 1, "a", 3.0, kwarg="asdf") # OK +p(bar, 1, "a", "b") # E: Argument 1 to "foo" has incompatible type "Callable[[int, str, float], None]"; expected "Callable[[int, str, str], None]" +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index a7f4fafc579e..ead896b8e458 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6575,6 +6575,67 @@ class TheClass: [out2] tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" + +[case testIncrementalFunctoolsPartial] +import a + +[file a.py] +from typing import Callable +from partial import p1, p2 + +p1(1, "a", 3) # OK +p1(1, "a", c=3) # OK +p1(1, b="a", c=3) # OK + +reveal_type(p1) + +def takes_callable_int(f: Callable[..., int]) -> None: ... +def takes_callable_str(f: Callable[..., str]) -> None: ... +takes_callable_int(p1) +takes_callable_str(p1) + +p2("a") # OK +p2("a", 3) # OK +p2("a", c=3) # OK +p2(1, 3) +p2(1, "a", 3) +p2(a=1, b="a", c=3) + +[file a.py.2] +from typing import Callable +from partial import p3 + +p3(1) # OK +p3(1, c=3) # OK +p3(a=1) # OK +p3(1, b="a", c=3) # OK, keywords can be clobbered +p3(1, 3) + +[file partial.py] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... + +p1 = functools.partial(foo) +p2 = functools.partial(foo, 1) +p3 = functools.partial(foo, b="a") +[builtins fixtures/dict.pyi] +[out] +tmp/a.py:8: note: Revealed type is "functools.partial[builtins.int]" +tmp/a.py:13: error: Argument 1 to "takes_callable_str" has incompatible type "partial[int]"; expected "Callable[..., str]" +tmp/a.py:13: note: "partial[int].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], int]" +tmp/a.py:18: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +tmp/a.py:19: error: Too many arguments for "foo" +tmp/a.py:19: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +tmp/a.py:19: error: Argument 2 to "foo" has incompatible type "str"; expected "int" +tmp/a.py:20: error: Unexpected keyword argument "a" for "foo" +tmp/partial.py:4: note: "foo" defined here +[out2] +tmp/a.py:8: error: Too many positional arguments for "foo" +tmp/a.py:8: error: Argument 2 to "foo" has incompatible type "int"; expected "str" + + [case testStartUsingTypeGuard] import a [file a.py] diff --git a/test-data/unit/lib-stub/functools.pyi b/test-data/unit/lib-stub/functools.pyi index e665b2bad0c2..b8d47e1da2b5 100644 --- a/test-data/unit/lib-stub/functools.pyi +++ b/test-data/unit/lib-stub/functools.pyi @@ -1,4 +1,4 @@ -from typing import Generic, TypeVar, Callable, Any, Mapping, overload +from typing import Generic, TypeVar, Callable, Any, Mapping, Self, overload _T = TypeVar("_T") @@ -33,3 +33,7 @@ class cached_property(Generic[_T]): def __get__(self, instance: object, owner: type[Any] | None = ...) -> _T: ... def __set_name__(self, owner: type[Any], name: str) -> None: ... def __class_getitem__(cls, item: Any) -> Any: ... + +class partial(Generic[_T]): + def __new__(cls, __func: Callable[..., _T], *args: Any, **kwargs: Any) -> Self: ... + def __call__(__self, *args: Any, **kwargs: Any) -> _T: ... From 25087fdbb72d1495e6903d171dee999c47ba09fd Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Thu, 23 May 2024 01:59:06 -0400 Subject: [PATCH 0597/1617] Validate more about overrides on untyped methods (#17276) This commit fixes #9618 by making MyPy always complain if a method overrides a base class method marked as `@final`. In the process, it also adds a few additional validations: - Always verify the `@override` decorator, which ought to be pretty backward-compatible for most projects assuming that strict override checks aren't enabled by default (and it appears to me that `--enable-error-code explicit-override` is off by default) - Verify that the method signature is compatible (which in practice means only arity and argument name checks) *if* the `--check-untyped-defs` flag is set; it seems unlikely that a user would want mypy to validate the bodies of untyped functions but wouldn't want to be alerted about incompatible overrides. Note: I did also explore enabling the signature compatibility check for all code, which in principle makes sense. But the mypy_primer results indicated that there would be backward compability issues because too many libraries rely on us not validating this: https://github.com/python/mypy/pull/17274 --- mypy/checker.py | 23 ++++++++++++++++------- mypy/nodes.py | 6 ++++++ test-data/unit/check-dynamic-typing.test | 15 +++++++++++++++ test-data/unit/check-functions.test | 12 ++++++++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3daf64daaac4..6da537fad5cb 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1004,7 +1004,7 @@ def _visit_func_def(self, defn: FuncDef) -> None: """Type check a function definition.""" self.check_func_item(defn, name=defn.name) if defn.info: - if not defn.is_dynamic() and not defn.is_overload and not defn.is_decorated: + if not defn.is_overload and not defn.is_decorated: # If the definition is the implementation for an # overload, the legality of the override has already # been typechecked, and decorated methods will be @@ -1913,9 +1913,17 @@ def check_method_override( Return a list of base classes which contain an attribute with the method name. """ # Check against definitions in base classes. + check_override_compatibility = defn.name not in ( + "__init__", + "__new__", + "__init_subclass__", + "__post_init__", + ) and (self.options.check_untyped_defs or not defn.is_dynamic()) found_method_base_classes: list[TypeInfo] = [] for base in defn.info.mro[1:]: - result = self.check_method_or_accessor_override_for_base(defn, base) + result = self.check_method_or_accessor_override_for_base( + defn, base, check_override_compatibility + ) if result is None: # Node was deferred, we will have another attempt later. return None @@ -1924,7 +1932,10 @@ def check_method_override( return found_method_base_classes def check_method_or_accessor_override_for_base( - self, defn: FuncDef | OverloadedFuncDef | Decorator, base: TypeInfo + self, + defn: FuncDef | OverloadedFuncDef | Decorator, + base: TypeInfo, + check_override_compatibility: bool, ) -> bool | None: """Check if method definition is compatible with a base class. @@ -1945,10 +1956,8 @@ def check_method_or_accessor_override_for_base( if defn.is_final: self.check_if_final_var_override_writable(name, base_attr.node, defn) found_base_method = True - - # Check the type of override. - if name not in ("__init__", "__new__", "__init_subclass__", "__post_init__"): - # Check method override + if check_override_compatibility: + # Check compatibility of the override signature # (__init__, __new__, __init_subclass__ are special). if self.check_method_override_for_base_with_name(defn, name, base): return None diff --git a/mypy/nodes.py b/mypy/nodes.py index 21051ffa4d0b..e52618fcdae6 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -616,6 +616,9 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef: # NOTE: res.info will be set in the fixup phase. return res + def is_dynamic(self) -> bool: + return all(item.is_dynamic() for item in self.items) + class Argument(Node): """A single argument in a FuncItem.""" @@ -938,6 +941,9 @@ def deserialize(cls, data: JsonDict) -> Decorator: dec.is_overload = data["is_overload"] return dec + def is_dynamic(self) -> bool: + return self.func.is_dynamic() + VAR_FLAGS: Final = [ "is_self", diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index 0dc05a7a0ea1..21fd52169ff5 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -756,6 +756,21 @@ main:5: note: def f(self, x: A) -> None main:5: note: Subclass: main:5: note: def f(self, x: Any, y: Any) -> None +[case testInvalidOverrideArgumentCountWithImplicitSignature4] +# flags: --check-untyped-defs +import typing +class B: + def f(self, x: A) -> None: pass +class A(B): + def f(self, x, y): + x() +[out] +main:6: error: Signature of "f" incompatible with supertype "B" +main:6: note: Superclass: +main:6: note: def f(self, x: A) -> None +main:6: note: Subclass: +main:6: note: def f(self, x: Any, y: Any) -> Any + [case testInvalidOverrideWithImplicitSignatureAndClassMethod1] class B: @classmethod diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 3aecbe065c27..fe01590c6c71 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3228,3 +3228,15 @@ class A: reveal_type(A.f) # N: Revealed type is "__main__.something_callable" reveal_type(A().f) # N: Revealed type is "builtins.str" [builtins fixtures/property.pyi] + +[case testFinalOverrideOnUntypedDef] +from typing import final + +class Base: + @final + def foo(self): + pass + +class Derived(Base): + def foo(self): # E: Cannot override final attribute "foo" (previously declared in base class "Base") + pass From 43a605f742bd554acbdff9bea74c764621e3aa44 Mon Sep 17 00:00:00 2001 From: Max Murin Date: Thu, 23 May 2024 18:21:07 -0700 Subject: [PATCH 0598/1617] Mypybot/sync typeshed (#17280) Sync typeshed before 1.11 release. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood Co-authored-by: Jelle Zijlstra --- mypy/fastparse.py | 4 +- mypy/pyinfo.py | 2 +- mypy/typeshed/stdlib/VERSIONS | 2 +- mypy/typeshed/stdlib/_ast.pyi | 683 +++++++++++++++++- mypy/typeshed/stdlib/_ctypes.pyi | 2 +- mypy/typeshed/stdlib/_socket.pyi | 4 +- mypy/typeshed/stdlib/_stat.pyi | 40 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 2 + mypy/typeshed/stdlib/_weakref.pyi | 2 +- mypy/typeshed/stdlib/_weakrefset.pyi | 2 +- mypy/typeshed/stdlib/argparse.pyi | 264 +++++-- mypy/typeshed/stdlib/array.pyi | 2 +- mypy/typeshed/stdlib/asyncio/__init__.pyi | 10 +- mypy/typeshed/stdlib/asyncio/events.pyi | 4 +- mypy/typeshed/stdlib/asyncio/futures.pyi | 2 +- mypy/typeshed/stdlib/asyncio/queues.pyi | 2 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 2 +- mypy/typeshed/stdlib/atexit.pyi | 4 +- mypy/typeshed/stdlib/base64.pyi | 6 + mypy/typeshed/stdlib/builtins.pyi | 30 +- mypy/typeshed/stdlib/calendar.pyi | 58 +- mypy/typeshed/stdlib/code.pyi | 35 +- .../stdlib/concurrent/futures/_base.pyi | 2 +- .../stdlib/concurrent/futures/thread.pyi | 2 +- mypy/typeshed/stdlib/contextvars.pyi | 4 +- mypy/typeshed/stdlib/csv.pyi | 8 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 2 +- mypy/typeshed/stdlib/dataclasses.pyi | 2 +- mypy/typeshed/stdlib/datetime.pyi | 32 + mypy/typeshed/stdlib/difflib.pyi | 2 +- mypy/typeshed/stdlib/dis.pyi | 17 +- .../stdlib/distutils/archive_util.pyi | 29 +- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 53 +- mypy/typeshed/stdlib/distutils/cmd.pyi | 71 +- .../stdlib/distutils/command/bdist_msi.pyi | 8 +- .../stdlib/distutils/command/build.pyi | 6 +- .../stdlib/distutils/command/build_py.pyi | 4 +- .../stdlib/distutils/command/check.pyi | 4 +- .../stdlib/distutils/command/config.pyi | 9 +- .../stdlib/distutils/command/install.pyi | 6 +- .../stdlib/distutils/command/register.pyi | 6 +- .../stdlib/distutils/command/sdist.pyi | 6 +- mypy/typeshed/stdlib/distutils/core.pyi | 4 +- mypy/typeshed/stdlib/distutils/dep_util.pyi | 17 +- mypy/typeshed/stdlib/distutils/dir_util.pyi | 30 +- mypy/typeshed/stdlib/distutils/dist.pyi | 13 +- mypy/typeshed/stdlib/distutils/file_util.pyi | 46 +- mypy/typeshed/stdlib/distutils/filelist.pyi | 14 +- mypy/typeshed/stdlib/distutils/spawn.pyi | 6 +- mypy/typeshed/stdlib/distutils/sysconfig.pyi | 6 +- mypy/typeshed/stdlib/distutils/text_file.pyi | 14 +- mypy/typeshed/stdlib/distutils/util.pyi | 14 +- mypy/typeshed/stdlib/faulthandler.pyi | 2 +- mypy/typeshed/stdlib/filecmp.pyi | 2 +- mypy/typeshed/stdlib/fileinput.pyi | 2 +- mypy/typeshed/stdlib/functools.pyi | 6 +- mypy/typeshed/stdlib/genericpath.pyi | 7 + mypy/typeshed/stdlib/graphlib.pyi | 2 +- mypy/typeshed/stdlib/gzip.pyi | 6 +- mypy/typeshed/stdlib/http/__init__.pyi | 15 +- mypy/typeshed/stdlib/http/cookies.pyi | 2 +- .../stdlib/importlib/metadata/__init__.pyi | 5 +- mypy/typeshed/stdlib/inspect.pyi | 6 + mypy/typeshed/stdlib/keyword.pyi | 4 +- mypy/typeshed/stdlib/logging/__init__.pyi | 109 +-- mypy/typeshed/stdlib/mailbox.pyi | 4 +- mypy/typeshed/stdlib/marshal.pyi | 16 +- mypy/typeshed/stdlib/math.pyi | 3 + .../stdlib/multiprocessing/managers.pyi | 2 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 2 +- .../stdlib/multiprocessing/queues.pyi | 2 +- .../stdlib/multiprocessing/shared_memory.pyi | 2 +- mypy/typeshed/stdlib/ntpath.pyi | 9 +- mypy/typeshed/stdlib/opcode.pyi | 8 +- mypy/typeshed/stdlib/optparse.pyi | 2 +- mypy/typeshed/stdlib/os/__init__.pyi | 2 +- mypy/typeshed/stdlib/pdb.pyi | 4 +- mypy/typeshed/stdlib/posixpath.pyi | 5 + mypy/typeshed/stdlib/pydoc.pyi | 109 ++- mypy/typeshed/stdlib/queue.pyi | 6 +- mypy/typeshed/stdlib/random.pyi | 5 +- mypy/typeshed/stdlib/re.pyi | 29 +- mypy/typeshed/stdlib/shutil.pyi | 61 +- mypy/typeshed/stdlib/signal.pyi | 2 +- mypy/typeshed/stdlib/stat.pyi | 6 + mypy/typeshed/stdlib/statistics.pyi | 31 +- mypy/typeshed/stdlib/subprocess.pyi | 4 +- mypy/typeshed/stdlib/sys/__init__.pyi | 21 +- mypy/typeshed/stdlib/syslog.pyi | 9 + mypy/typeshed/stdlib/tempfile.pyi | 4 +- mypy/typeshed/stdlib/threading.pyi | 3 + mypy/typeshed/stdlib/token.pyi | 9 +- mypy/typeshed/stdlib/tokenize.pyi | 13 +- mypy/typeshed/stdlib/types.pyi | 9 +- mypy/typeshed/stdlib/typing.pyi | 53 +- mypy/typeshed/stdlib/typing_extensions.pyi | 187 ++--- mypy/typeshed/stdlib/unittest/case.pyi | 2 +- mypy/typeshed/stdlib/urllib/parse.pyi | 2 +- mypy/typeshed/stdlib/urllib/request.pyi | 27 +- mypy/typeshed/stdlib/venv/__init__.pyi | 34 +- mypy/typeshed/stdlib/warnings.pyi | 14 +- mypy/typeshed/stdlib/wsgiref/util.pyi | 2 + test-data/unit/pythoneval.test | 2 +- 103 files changed, 1943 insertions(+), 529 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index a32e7d8f9978..49f0a938b750 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2041,8 +2041,10 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: sliceval.col_offset = sliceval.lower.col_offset else: assert isinstance(n.slice, ast3.ExtSlice) - dims = copy.deepcopy(n.slice.dims) + dims = cast(List[ast3.expr], copy.deepcopy(n.slice.dims)) for s in dims: + # These fields don't actually have a col_offset attribute but we add + # it manually. if getattr(s, "col_offset", None) is None: if isinstance(s, ast3.Index): s.col_offset = s.value.col_offset diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index f5f35800d44e..ee5307cfaebb 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -71,7 +71,7 @@ def getsearchdirs() -> tuple[list[str], list[str]]: if __name__ == "__main__": - sys.stdout.reconfigure(encoding="utf-8") # type: ignore [attr-defined] + sys.stdout.reconfigure(encoding="utf-8") # type: ignore[union-attr] if sys.argv[-1] == "getsearchdirs": print(repr(getsearchdirs())) else: diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index deb940395e1e..a8526aab9422 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -166,7 +166,7 @@ ipaddress: 3.3- itertools: 3.0- json: 3.0- keyword: 3.0- -lib2to3: 3.0- +lib2to3: 3.0-3.12 linecache: 3.0- locale: 3.0- logging: 3.0- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 0758450dfa7c..51791b4099d5 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,29 +1,35 @@ import sys import typing_extensions -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Generic, Literal, TypedDict, overload +from typing_extensions import Unpack PyCF_ONLY_AST: Literal[1024] PyCF_TYPE_COMMENTS: Literal[4096] PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +# Used for node end positions in constructor keyword arguments +_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) # noqa: Y023 + # Alias used for fields that must always be valid identifiers # A string `x` counts as a valid identifier if both the following are True # (1) `x.isidentifier()` evaluates to `True` # (2) `keyword.iskeyword(x)` evaluates to `False` _Identifier: typing_extensions.TypeAlias = str +# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes +class _Attributes(TypedDict, Generic[_EndPositionT], total=False): + lineno: int + col_offset: int + end_lineno: _EndPositionT + end_col_offset: _EndPositionT + class AST: if sys.version_info >= (3, 10): __match_args__ = () _attributes: ClassVar[tuple[str, ...]] _fields: ClassVar[tuple[str, ...]] - def __init__(self, *args: Any, **kwargs: Any) -> None: ... - # TODO: Not all nodes have all of the following attributes - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - type_comment: str | None + if sys.version_info >= (3, 13): + _field_types: ClassVar[dict[str, Any]] class mod(AST): ... class type_ignore(AST): ... @@ -31,31 +37,54 @@ class type_ignore(AST): ... class TypeIgnore(type_ignore): if sys.version_info >= (3, 10): __match_args__ = ("lineno", "tag") + lineno: int tag: str + def __init__(self, lineno: int, tag: str) -> None: ... class FunctionType(mod): if sys.version_info >= (3, 10): __match_args__ = ("argtypes", "returns") argtypes: list[expr] returns: expr + if sys.version_info >= (3, 13): + @overload + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + @overload + def __init__(self, argtypes: list[expr] = ..., *, returns: expr) -> None: ... + else: + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... class Module(mod): if sys.version_info >= (3, 10): __match_args__ = ("body", "type_ignores") body: list[stmt] type_ignores: list[TypeIgnore] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> None: ... + else: + def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... class Interactive(mod): if sys.version_info >= (3, 10): __match_args__ = ("body",) body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ...) -> None: ... + else: + def __init__(self, body: list[stmt]) -> None: ... class Expression(mod): if sys.version_info >= (3, 10): __match_args__ = ("body",) body: expr + def __init__(self, body: expr) -> None: ... -class stmt(AST): ... +class stmt(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... class FunctionDef(stmt): if sys.version_info >= (3, 12): @@ -67,8 +96,58 @@ class FunctionDef(stmt): body: list[stmt] decorator_list: list[expr] returns: expr | None + type_comment: str | None if sys.version_info >= (3, 12): type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... class AsyncFunctionDef(stmt): if sys.version_info >= (3, 12): @@ -80,8 +159,58 @@ class AsyncFunctionDef(stmt): body: list[stmt] decorator_list: list[expr] returns: expr | None + type_comment: str | None if sys.version_info >= (3, 12): type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... class ClassDef(stmt): if sys.version_info >= (3, 12): @@ -95,22 +224,73 @@ class ClassDef(stmt): decorator_list: list[expr] if sys.version_info >= (3, 12): type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + bases: list[expr] = ..., + keywords: list[keyword] = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + **kwargs: Unpack[_Attributes], + ) -> None: ... class Return(stmt): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class Delete(stmt): if sys.version_info >= (3, 10): __match_args__ = ("targets",) targets: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class Assign(stmt): if sys.version_info >= (3, 10): __match_args__ = ("targets", "value", "type_comment") targets: list[expr] value: expr + type_comment: str | None + if sys.version_info >= (3, 13): + @overload + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, targets: list[expr] = ..., *, value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... class AugAssign(stmt): if sys.version_info >= (3, 10): @@ -118,6 +298,9 @@ class AugAssign(stmt): target: Name | Attribute | Subscript op: operator value: expr + def __init__( + self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] + ) -> None: ... class AnnAssign(stmt): if sys.version_info >= (3, 10): @@ -126,6 +309,25 @@ class AnnAssign(stmt): annotation: expr value: expr | None simple: int + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None = None, + *, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... class For(stmt): if sys.version_info >= (3, 10): @@ -134,6 +336,27 @@ class For(stmt): iter: expr body: list[stmt] orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... class AsyncFor(stmt): if sys.version_info >= (3, 10): @@ -142,6 +365,27 @@ class AsyncFor(stmt): iter: expr body: list[stmt] orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... class While(stmt): if sys.version_info >= (3, 10): @@ -149,6 +393,12 @@ class While(stmt): test: expr body: list[stmt] orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... class If(stmt): if sys.version_info >= (3, 10): @@ -156,24 +406,57 @@ class If(stmt): test: expr body: list[stmt] orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... class With(stmt): if sys.version_info >= (3, 10): __match_args__ = ("items", "body", "type_comment") items: list[withitem] body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... class AsyncWith(stmt): if sys.version_info >= (3, 10): __match_args__ = ("items", "body", "type_comment") items: list[withitem] body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... class Raise(stmt): if sys.version_info >= (3, 10): __match_args__ = ("exc", "cause") exc: expr | None cause: expr | None + def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class Try(stmt): if sys.version_info >= (3, 10): @@ -182,6 +465,24 @@ class Try(stmt): handlers: list[ExceptHandler] orelse: list[stmt] finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... if sys.version_info >= (3, 11): class TryStar(stmt): @@ -190,17 +491,40 @@ if sys.version_info >= (3, 11): handlers: list[ExceptHandler] orelse: list[stmt] finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... class Assert(stmt): if sys.version_info >= (3, 10): __match_args__ = ("test", "msg") test: expr msg: expr | None + def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class Import(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) names: list[alias] + if sys.version_info >= (3, 13): + def __init__(self, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... class ImportFrom(stmt): if sys.version_info >= (3, 10): @@ -208,32 +532,65 @@ class ImportFrom(stmt): module: str | None names: list[alias] level: int + if sys.version_info >= (3, 13): + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... class Global(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... class Nonlocal(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... class Expr(stmt): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Pass(stmt): ... class Break(stmt): ... class Continue(stmt): ... -class expr(AST): ... + +class expr(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... class BoolOp(expr): if sys.version_info >= (3, 10): __match_args__ = ("op", "values") op: boolop values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, op: boolop, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class BinOp(expr): if sys.version_info >= (3, 10): @@ -241,18 +598,21 @@ class BinOp(expr): left: expr op: operator right: expr + def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... class UnaryOp(expr): if sys.version_info >= (3, 10): __match_args__ = ("op", "operand") op: unaryop operand: expr + def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Lambda(expr): if sys.version_info >= (3, 10): __match_args__ = ("args", "body") args: arguments body: expr + def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... class IfExp(expr): if sys.version_info >= (3, 10): @@ -260,29 +620,46 @@ class IfExp(expr): test: expr body: expr orelse: expr + def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Dict(expr): if sys.version_info >= (3, 10): __match_args__ = ("keys", "values") keys: list[expr | None] values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class Set(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts",) elts: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class ListComp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") elt: expr generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... class SetComp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") elt: expr generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... class DictComp(expr): if sys.version_info >= (3, 10): @@ -290,27 +667,40 @@ class DictComp(expr): key: expr value: expr generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__( + self, key: expr, value: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... class GeneratorExp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") elt: expr generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... class Await(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Yield(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class YieldFrom(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Compare(expr): if sys.version_info >= (3, 10): @@ -318,6 +708,12 @@ class Compare(expr): left: expr ops: list[cmpop] comparators: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, left: expr, ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class Call(expr): if sys.version_info >= (3, 10): @@ -325,6 +721,12 @@ class Call(expr): func: expr args: list[expr] keywords: list[keyword] + if sys.version_info >= (3, 13): + def __init__( + self, func: expr, args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... class FormattedValue(expr): if sys.version_info >= (3, 10): @@ -332,11 +734,16 @@ class FormattedValue(expr): value: expr conversion: int format_spec: expr | None + def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class JoinedStr(expr): if sys.version_info >= (3, 10): __match_args__ = ("values",) values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... class Constant(expr): if sys.version_info >= (3, 10): @@ -346,72 +753,94 @@ class Constant(expr): # Aliases for value, for backwards compatibility s: Any n: int | float | complex + def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class NamedExpr(expr): if sys.version_info >= (3, 10): __match_args__ = ("target", "value") target: Name value: expr + def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... class Attribute(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "attr", "ctx") value: expr attr: _Identifier - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... if sys.version_info >= (3, 9): _Slice: typing_extensions.TypeAlias = expr + _SliceAttributes: typing_extensions.TypeAlias = _Attributes else: class slice(AST): ... _Slice: typing_extensions.TypeAlias = slice + class _SliceAttributes(TypedDict): ... + class Slice(_Slice): if sys.version_info >= (3, 10): __match_args__ = ("lower", "upper", "step") lower: expr | None upper: expr | None step: expr | None + def __init__( + self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] + ) -> None: ... if sys.version_info < (3, 9): class ExtSlice(slice): dims: list[slice] + def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... class Index(slice): value: expr + def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... class Subscript(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "slice", "ctx") value: expr slice: _Slice - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... class Starred(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "ctx") value: expr - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... class Name(expr): if sys.version_info >= (3, 10): __match_args__ = ("id", "ctx") id: _Identifier - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... class List(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts", "ctx") elts: list[expr] - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... class Tuple(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts", "ctx") elts: list[expr] - ctx: expr_context + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` if sys.version_info >= (3, 9): dims: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... class expr_context(AST): ... @@ -422,6 +851,7 @@ if sys.version_info < (3, 9): class Suite(mod): body: list[stmt] + def __init__(self, body: list[stmt]) -> None: ... class Del(expr_context): ... class Load(expr_context): ... @@ -467,8 +897,20 @@ class comprehension(AST): iter: expr ifs: list[expr] is_async: int - -class excepthandler(AST): ... + if sys.version_info >= (3, 13): + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr] = ..., *, is_async: int) -> None: ... + else: + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + +class excepthandler(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... class ExceptHandler(excepthandler): if sys.version_info >= (3, 10): @@ -476,6 +918,19 @@ class ExceptHandler(excepthandler): type: expr | None name: _Identifier | None body: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__( + self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... class arguments(AST): if sys.version_info >= (3, 10): @@ -487,38 +942,117 @@ class arguments(AST): kw_defaults: list[expr | None] kwarg: arg | None defaults: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = None, + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = None, + defaults: list[expr] = ..., + ) -> None: ... + else: + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + *, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None = None, + *, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + defaults: list[expr], + ) -> None: ... class arg(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None if sys.version_info >= (3, 10): __match_args__ = ("arg", "annotation", "type_comment") arg: _Identifier annotation: expr | None + type_comment: str | None + def __init__( + self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... class keyword(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None if sys.version_info >= (3, 10): __match_args__ = ("arg", "value") arg: _Identifier | None value: expr + @overload + def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... class alias(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None if sys.version_info >= (3, 10): __match_args__ = ("name", "asname") name: str asname: _Identifier | None + def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... class withitem(AST): if sys.version_info >= (3, 10): __match_args__ = ("context_expr", "optional_vars") context_expr: expr optional_vars: expr | None + def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... if sys.version_info >= (3, 10): class Match(stmt): __match_args__ = ("subject", "cases") subject: expr cases: list[match_case] + if sys.version_info >= (3, 13): + def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... + + class pattern(AST): + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... - class pattern(AST): ... # Without the alias, Pyright complains variables named pattern are recursively defined _Pattern: typing_extensions.TypeAlias = pattern @@ -527,28 +1061,58 @@ if sys.version_info >= (3, 10): pattern: _Pattern guard: expr | None body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... + else: + @overload + def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ... + @overload + def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... class MatchValue(pattern): __match_args__ = ("value",) value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... class MatchSingleton(pattern): __match_args__ = ("value",) value: Literal[True, False] | None + def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... class MatchSequence(pattern): __match_args__ = ("patterns",) patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... class MatchStar(pattern): __match_args__ = ("name",) name: _Identifier | None + def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... class MatchMapping(pattern): __match_args__ = ("keys", "patterns", "rest") keys: list[expr] patterns: list[pattern] rest: _Identifier | None + if sys.version_info >= (3, 13): + def __init__( + self, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + keys: list[expr], + patterns: list[pattern], + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... class MatchClass(pattern): __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") @@ -556,36 +1120,111 @@ if sys.version_info >= (3, 10): patterns: list[pattern] kwd_attrs: list[_Identifier] kwd_patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__( + self, + cls: expr, + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + cls: expr, + patterns: list[pattern], + kwd_attrs: list[_Identifier], + kwd_patterns: list[pattern], + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... class MatchAs(pattern): __match_args__ = ("pattern", "name") pattern: _Pattern | None name: _Identifier | None + def __init__( + self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... class MatchOr(pattern): __match_args__ = ("patterns",) patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... if sys.version_info >= (3, 12): class type_param(AST): + lineno: int + col_offset: int end_lineno: int end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... class TypeVar(type_param): - __match_args__ = ("name", "bound") + if sys.version_info >= (3, 13): + __match_args__ = ("name", "bound", "default_value") + else: + __match_args__ = ("name", "bound") name: _Identifier bound: expr | None + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, + name: _Identifier, + bound: expr | None = None, + default_value: expr | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... class ParamSpec(type_param): - __match_args__ = ("name",) + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... class TypeVarTuple(type_param): - __match_args__ = ("name",) + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... class TypeAlias(stmt): __match_args__ = ("name", "type_params", "value") name: Name type_params: list[type_param] value: expr + if sys.version_info >= (3, 13): + @overload + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + @overload + def __init__( + self, name: Name, type_params: list[type_param] = ..., *, value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index cf9cb81a44a3..a5f20dfd30e7 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -197,7 +197,7 @@ class Array(_CData, Generic[_CT]): # Sized and _CData prevents using _CDataMeta. def __len__(self) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def addressof(obj: _CData) -> int: ... def alignment(obj_or_type: _CData | type[_CData]) -> int: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 2a48349d4f7d..affa8d63ecfa 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -783,7 +783,7 @@ def ntohl(x: int, /) -> int: ... # param & ret val are 32-bit ints def ntohs(x: int, /) -> int: ... # param & ret val are 16-bit ints def htonl(x: int, /) -> int: ... # param & ret val are 32-bit ints def htons(x: int, /) -> int: ... # param & ret val are 16-bit ints -def inet_aton(ip_string: str, /) -> bytes: ... # ret val 4 bytes in length +def inet_aton(ip_addr: str, /) -> bytes: ... # ret val 4 bytes in length def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ... def inet_pton(address_family: int, ip_string: str, /) -> bytes: ... def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ... @@ -797,7 +797,7 @@ if sys.platform != "win32": def socketpair(family: int = ..., type: int = ..., proto: int = ..., /) -> tuple[socket, socket]: ... def if_nameindex() -> list[tuple[int, str]]: ... -def if_nametoindex(name: str, /) -> int: ... +def if_nametoindex(oname: str, /) -> int: ... def if_indextoname(index: int, /) -> str: ... CAPI: object diff --git a/mypy/typeshed/stdlib/_stat.pyi b/mypy/typeshed/stdlib/_stat.pyi index 347897404edc..c4e918d8b57f 100644 --- a/mypy/typeshed/stdlib/_stat.pyi +++ b/mypy/typeshed/stdlib/_stat.pyi @@ -64,19 +64,19 @@ UF_NODUMP: Literal[0x00000001] UF_NOUNLINK: Literal[0x00000010] UF_OPAQUE: Literal[0x00000008] -def S_IMODE(mode: int) -> int: ... -def S_IFMT(mode: int) -> int: ... -def S_ISBLK(mode: int) -> bool: ... -def S_ISCHR(mode: int) -> bool: ... -def S_ISDIR(mode: int) -> bool: ... -def S_ISDOOR(mode: int) -> bool: ... -def S_ISFIFO(mode: int) -> bool: ... -def S_ISLNK(mode: int) -> bool: ... -def S_ISPORT(mode: int) -> bool: ... -def S_ISREG(mode: int) -> bool: ... -def S_ISSOCK(mode: int) -> bool: ... -def S_ISWHT(mode: int) -> bool: ... -def filemode(mode: int) -> str: ... +def S_IMODE(mode: int, /) -> int: ... +def S_IFMT(mode: int, /) -> int: ... +def S_ISBLK(mode: int, /) -> bool: ... +def S_ISCHR(mode: int, /) -> bool: ... +def S_ISDIR(mode: int, /) -> bool: ... +def S_ISDOOR(mode: int, /) -> bool: ... +def S_ISFIFO(mode: int, /) -> bool: ... +def S_ISLNK(mode: int, /) -> bool: ... +def S_ISPORT(mode: int, /) -> bool: ... +def S_ISREG(mode: int, /) -> bool: ... +def S_ISSOCK(mode: int, /) -> bool: ... +def S_ISWHT(mode: int, /) -> bool: ... +def filemode(mode: int, /) -> str: ... if sys.platform == "win32": IO_REPARSE_TAG_SYMLINK: int @@ -101,3 +101,17 @@ if sys.platform == "win32": FILE_ATTRIBUTE_SYSTEM: Literal[4] FILE_ATTRIBUTE_TEMPORARY: Literal[256] FILE_ATTRIBUTE_VIRTUAL: Literal[65536] + +if sys.version_info >= (3, 13): + SF_SETTABLE: Literal[0x3FFF0000] + # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 + # SF_RESTRICTED: Literal[0x00080000] + SF_FIRMLINK: Literal[0x00800000] + SF_DATALESS: Literal[0x40000000] + + SF_SUPPORTED: Literal[0x9F0000] + SF_SYNTHETIC: Literal[0xC0000000] + + UF_TRACKED: Literal[0x00000040] + UF_DATAVAULT: Literal[0x00000080] + UF_SETTABLE: Literal[0x0000FFFF] diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 6937d97b87ea..7201819b25ed 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -326,6 +326,8 @@ class structseq(Generic[_T_co]): # but only has any meaning if you supply it a dict where the keys are strings. # https://github.com/python/typeshed/pull/6560#discussion_r767149830 def __new__(cls: type[Self], sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> Self: ... + if sys.version_info >= (3, 13): + def __replace__(self: Self, **kwargs: Any) -> Self: ... # Superset of typing.AnyStr that also includes LiteralString AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString) # noqa: Y001 diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index e395143cc027..61365645d768 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -27,7 +27,7 @@ class ReferenceType(Generic[_T]): def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... ref = ReferenceType diff --git a/mypy/typeshed/stdlib/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi index 6482ade1271e..2a4e682f64ed 100644 --- a/mypy/typeshed/stdlib/_weakrefset.pyi +++ b/mypy/typeshed/stdlib/_weakrefset.pyi @@ -48,4 +48,4 @@ class WeakSet(MutableSet[_T]): def __or__(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... def isdisjoint(self, other: Iterable[_T]) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 0701654734a4..1956d08c9933 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -318,51 +318,95 @@ class Action(_AttributeHolder): required: bool help: str | None metavar: str | tuple[str, ...] | None - def __init__( - self, - option_strings: Sequence[str], - dest: str, - nargs: int | str | None = None, - const: _T | None = None, - default: _T | str | None = None, - type: Callable[[str], _T] | FileType | None = None, - choices: Iterable[_T] | None = None, - required: bool = False, - help: str | None = None, - metavar: str | tuple[str, ...] | None = None, - ) -> None: ... - def __call__( - self, parser: ArgumentParser, namespace: Namespace, values: str | Sequence[Any] | None, option_string: str | None = None - ) -> None: ... - if sys.version_info >= (3, 9): - def format_usage(self) -> str: ... - -if sys.version_info >= (3, 12): - class BooleanOptionalAction(Action): - @overload + if sys.version_info >= (3, 13): def __init__( self, option_strings: Sequence[str], dest: str, - default: bool | None = None, - *, + nargs: int | str | None = None, + const: _T | None = None, + default: _T | str | None = None, + type: Callable[[str], _T] | FileType | None = None, + choices: Iterable[_T] | None = None, required: bool = False, help: str | None = None, + metavar: str | tuple[str, ...] | None = None, + deprecated: bool = False, ) -> None: ... - @overload - @deprecated("The `type`, `choices`, and `metavar` parameters are ignored and will be removed in Python 3.14.") + else: def __init__( self, option_strings: Sequence[str], dest: str, - default: _T | bool | None = None, - type: Callable[[str], _T] | FileType | None = sentinel, - choices: Iterable[_T] | None = sentinel, + nargs: int | str | None = None, + const: _T | None = None, + default: _T | str | None = None, + type: Callable[[str], _T] | FileType | None = None, + choices: Iterable[_T] | None = None, required: bool = False, help: str | None = None, - metavar: str | tuple[str, ...] | None = sentinel, + metavar: str | tuple[str, ...] | None = None, ) -> None: ... + def __call__( + self, parser: ArgumentParser, namespace: Namespace, values: str | Sequence[Any] | None, option_string: str | None = None + ) -> None: ... + if sys.version_info >= (3, 9): + def format_usage(self) -> str: ... + +if sys.version_info >= (3, 12): + class BooleanOptionalAction(Action): + if sys.version_info >= (3, 13): + @overload + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool | None = None, + *, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + @overload + @deprecated("The `type`, `choices`, and `metavar` parameters are ignored and will be removed in Python 3.14.") + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: _T | bool | None = None, + type: Callable[[str], _T] | FileType | None = sentinel, + choices: Iterable[_T] | None = sentinel, + required: bool = False, + help: str | None = None, + metavar: str | tuple[str, ...] | None = sentinel, + deprecated: bool = False, + ) -> None: ... + else: + @overload + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool | None = None, + *, + required: bool = False, + help: str | None = None, + ) -> None: ... + @overload + @deprecated("The `type`, `choices`, and `metavar` parameters are ignored and will be removed in Python 3.14.") + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: _T | bool | None = None, + type: Callable[[str], _T] | FileType | None = sentinel, + choices: Iterable[_T] | None = sentinel, + required: bool = False, + help: str | None = None, + metavar: str | tuple[str, ...] | None = sentinel, + ) -> None: ... + elif sys.version_info >= (3, 9): class BooleanOptionalAction(Action): @overload @@ -431,7 +475,19 @@ class _StoreAction(Action): ... # undocumented class _StoreConstAction(Action): - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + const: Any | None = None, + default: Any = None, + required: bool = False, + help: str | None = None, + metavar: str | tuple[str, ...] | None = None, + deprecated: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): def __init__( self, option_strings: Sequence[str], @@ -456,15 +512,37 @@ class _StoreConstAction(Action): # undocumented class _StoreTrueAction(_StoreConstAction): - def __init__( - self, option_strings: Sequence[str], dest: str, default: bool = False, required: bool = False, help: str | None = None - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool = False, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + else: + def __init__( + self, option_strings: Sequence[str], dest: str, default: bool = False, required: bool = False, help: str | None = None + ) -> None: ... # undocumented class _StoreFalseAction(_StoreConstAction): - def __init__( - self, option_strings: Sequence[str], dest: str, default: bool = True, required: bool = False, help: str | None = None - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool = True, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + else: + def __init__( + self, option_strings: Sequence[str], dest: str, default: bool = True, required: bool = False, help: str | None = None + ) -> None: ... # undocumented class _AppendAction(Action): ... @@ -474,7 +552,19 @@ class _ExtendAction(_AppendAction): ... # undocumented class _AppendConstAction(Action): - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + const: Any | None = None, + default: Any = None, + required: bool = False, + help: str | None = None, + metavar: str | tuple[str, ...] | None = None, + deprecated: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): def __init__( self, option_strings: Sequence[str], @@ -499,27 +589,72 @@ class _AppendConstAction(Action): # undocumented class _CountAction(Action): - def __init__( - self, option_strings: Sequence[str], dest: str, default: Any = None, required: bool = False, help: str | None = None - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: Any = None, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + else: + def __init__( + self, option_strings: Sequence[str], dest: str, default: Any = None, required: bool = False, help: str | None = None + ) -> None: ... # undocumented class _HelpAction(Action): - def __init__( - self, option_strings: Sequence[str], dest: str = "==SUPPRESS==", default: str = "==SUPPRESS==", help: str | None = None - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + dest: str = "==SUPPRESS==", + default: str = "==SUPPRESS==", + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + else: + def __init__( + self, + option_strings: Sequence[str], + dest: str = "==SUPPRESS==", + default: str = "==SUPPRESS==", + help: str | None = None, + ) -> None: ... # undocumented class _VersionAction(Action): version: str | None - def __init__( - self, - option_strings: Sequence[str], - version: str | None = None, - dest: str = "==SUPPRESS==", - default: str = "==SUPPRESS==", - help: str = "show program's version number and exit", - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + option_strings: Sequence[str], + version: str | None = None, + dest: str = "==SUPPRESS==", + default: str = "==SUPPRESS==", + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): + def __init__( + self, + option_strings: Sequence[str], + version: str | None = None, + dest: str = "==SUPPRESS==", + default: str = "==SUPPRESS==", + help: str | None = None, + ) -> None: ... + else: + def __init__( + self, + option_strings: Sequence[str], + version: str | None = None, + dest: str = "==SUPPRESS==", + default: str = "==SUPPRESS==", + help: str = "show program's version number and exit", + ) -> None: ... # undocumented class _SubParsersAction(Action, Generic[_ArgumentParserT]): @@ -542,7 +677,30 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): # Note: `add_parser` accepts all kwargs of `ArgumentParser.__init__`. It also # accepts its own `help` and `aliases` kwargs. - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 13): + def add_parser( + self, + name: str, + *, + deprecated: bool = False, + help: str | None = ..., + aliases: Sequence[str] = ..., + # Kwargs from ArgumentParser constructor + prog: str | None = ..., + usage: str | None = ..., + description: str | None = ..., + epilog: str | None = ..., + parents: Sequence[_ArgumentParserT] = ..., + formatter_class: _FormatterClass = ..., + prefix_chars: str = ..., + fromfile_prefix_chars: str | None = ..., + argument_default: Any = ..., + conflict_handler: str = ..., + add_help: bool = ..., + allow_abbrev: bool = ..., + exit_on_error: bool = ..., + ) -> _ArgumentParserT: ... + elif sys.version_info >= (3, 9): def add_parser( self, name: str, diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 1b7de1c7882d..878d8d8cb808 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -87,6 +87,6 @@ class array(MutableSequence[_T]): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... if sys.version_info >= (3, 12): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... ArrayType = array diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index d5bbe8cb0642..daf28862aa6a 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -30,12 +30,12 @@ if sys.platform == "win32": else: from .unix_events import * -_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) # Aliases imported by multiple submodules in typeshed if sys.version_info >= (3, 12): - _AwaitableLike: TypeAlias = Awaitable[_T] # noqa: Y047 - _CoroutineLike: TypeAlias = Coroutine[Any, Any, _T] # noqa: Y047 + _AwaitableLike: TypeAlias = Awaitable[_T_co] # noqa: Y047 + _CoroutineLike: TypeAlias = Coroutine[Any, Any, _T_co] # noqa: Y047 else: - _AwaitableLike: TypeAlias = Generator[Any, None, _T] | Awaitable[_T] - _CoroutineLike: TypeAlias = Generator[Any, None, _T] | Coroutine[Any, Any, _T] + _AwaitableLike: TypeAlias = Generator[Any, None, _T_co] | Awaitable[_T_co] + _CoroutineLike: TypeAlias = Generator[Any, None, _T_co] | Coroutine[Any, Any, _T_co] diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 95de28c5021e..c0345eb1b5b5 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -2,7 +2,7 @@ import ssl import sys from _typeshed import FileDescriptorLike, ReadableBuffer, StrPath, Unused, WriteableBuffer from abc import ABCMeta, abstractmethod -from collections.abc import Callable, Coroutine, Generator, Sequence +from collections.abc import Callable, Sequence from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Literal, Protocol, TypeVar, overload @@ -43,7 +43,7 @@ _ProtocolFactory: TypeAlias = Callable[[], BaseProtocol] _SSLContext: TypeAlias = bool | None | ssl.SSLContext class _TaskFactory(Protocol): - def __call__(self, loop: AbstractEventLoop, factory: Coroutine[Any, Any, _T] | Generator[Any, None, _T], /) -> Future[_T]: ... + def __call__(self, loop: AbstractEventLoop, factory: _CoroutineLike[_T], /) -> Future[_T]: ... class Handle: _cancelled: bool diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index a3953cdaf8c7..e19fd53f3311 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -52,6 +52,6 @@ class Future(Awaitable[_T], Iterable[_T]): @property def _loop(self) -> AbstractEventLoop: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ... diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index bb4ee71f9267..1d8f80f4c388 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -41,7 +41,7 @@ class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 async def join(self) -> None: ... def task_done(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, type: Any) -> GenericAlias: ... + def __class_getitem__(cls, type: Any, /) -> GenericAlias: ... class PriorityQueue(Queue[_T]): ... class LifoQueue(Queue[_T]): ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 67291071d512..c16a1919b7c8 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -443,7 +443,7 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn @classmethod def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... diff --git a/mypy/typeshed/stdlib/atexit.pyi b/mypy/typeshed/stdlib/atexit.pyi index ea041d7b5e46..7f7b05ccc0a3 100644 --- a/mypy/typeshed/stdlib/atexit.pyi +++ b/mypy/typeshed/stdlib/atexit.pyi @@ -8,5 +8,5 @@ _P = ParamSpec("_P") def _clear() -> None: ... def _ncallbacks() -> int: ... def _run_exitfuncs() -> None: ... -def register(func: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Callable[_P, _T]: ... -def unregister(func: Callable[..., object]) -> None: ... +def register(func: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Callable[_P, _T]: ... +def unregister(func: Callable[..., object], /) -> None: ... diff --git a/mypy/typeshed/stdlib/base64.pyi b/mypy/typeshed/stdlib/base64.pyi index 4629c95d0949..8be4cfe69de0 100644 --- a/mypy/typeshed/stdlib/base64.pyi +++ b/mypy/typeshed/stdlib/base64.pyi @@ -25,6 +25,8 @@ __all__ = [ if sys.version_info >= (3, 10): __all__ += ["b32hexencode", "b32hexdecode"] +if sys.version_info >= (3, 13): + __all__ += ["z85decode", "z85encode"] def b64encode(s: ReadableBuffer, altchars: ReadableBuffer | None = None) -> bytes: ... def b64decode(s: str | ReadableBuffer, altchars: str | ReadableBuffer | None = None, validate: bool = False) -> bytes: ... @@ -57,3 +59,7 @@ def decodebytes(s: ReadableBuffer) -> bytes: ... if sys.version_info < (3, 9): def encodestring(s: ReadableBuffer) -> bytes: ... def decodestring(s: ReadableBuffer) -> bytes: ... + +if sys.version_info >= (3, 13): + def z85encode(s: ReadableBuffer) -> bytes: ... + def z85decode(s: str | ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 4c47a0736e2e..4a6c4bbcae45 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1233,12 +1233,34 @@ def divmod(x: _T_contra, y: SupportsRDivMod[_T_contra, _T_co], /) -> _T_co: ... # The `globals` argument to `eval` has to be `dict[str, Any]` rather than `dict[str, object]` due to invariance. # (The `globals` argument has to be a "real dict", rather than any old mapping, unlike the `locals` argument.) -def eval( - source: str | ReadableBuffer | CodeType, globals: dict[str, Any] | None = None, locals: Mapping[str, object] | None = None, / -) -> Any: ... +if sys.version_info >= (3, 13): + def eval( + source: str | ReadableBuffer | CodeType, + /, + globals: dict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + ) -> Any: ... + +else: + def eval( + source: str | ReadableBuffer | CodeType, + globals: dict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + /, + ) -> Any: ... # Comment above regarding `eval` applies to `exec` as well -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + def exec( + source: str | ReadableBuffer | CodeType, + /, + globals: dict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + *, + closure: tuple[CellType, ...] | None = None, + ) -> None: ... + +elif sys.version_info >= (3, 11): def exec( source: str | ReadableBuffer | CodeType, globals: dict[str, Any] | None = None, diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 5cc49e102fdf..39312d0b2523 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -4,7 +4,7 @@ import sys from _typeshed import Unused from collections.abc import Iterable, Sequence from time import struct_time -from typing import ClassVar, Literal +from typing import ClassVar, Final from typing_extensions import TypeAlias __all__ = [ @@ -154,18 +154,18 @@ month_abbr: Sequence[str] if sys.version_info >= (3, 12): class Month(enum.IntEnum): - JANUARY: Literal[1] - FEBRUARY: Literal[2] - MARCH: Literal[3] - APRIL: Literal[4] - MAY: Literal[5] - JUNE: Literal[6] - JULY: Literal[7] - AUGUST: Literal[8] - SEPTEMBER: Literal[9] - OCTOBER: Literal[10] - NOVEMBER: Literal[11] - DECEMBER: Literal[12] + JANUARY = 1 + FEBRUARY = 2 + MARCH = 3 + APRIL = 4 + MAY = 5 + JUNE = 6 + JULY = 7 + AUGUST = 8 + SEPTEMBER = 9 + OCTOBER = 10 + NOVEMBER = 11 + DECEMBER = 12 JANUARY = Month.JANUARY FEBRUARY = Month.FEBRUARY @@ -181,13 +181,13 @@ if sys.version_info >= (3, 12): DECEMBER = Month.DECEMBER class Day(enum.IntEnum): - MONDAY: Literal[0] - TUESDAY: Literal[1] - WEDNESDAY: Literal[2] - THURSDAY: Literal[3] - FRIDAY: Literal[4] - SATURDAY: Literal[5] - SUNDAY: Literal[6] + MONDAY = 0 + TUESDAY = 1 + WEDNESDAY = 2 + THURSDAY = 3 + FRIDAY = 4 + SATURDAY = 5 + SUNDAY = 6 MONDAY = Day.MONDAY TUESDAY = Day.TUESDAY @@ -197,12 +197,12 @@ if sys.version_info >= (3, 12): SATURDAY = Day.SATURDAY SUNDAY = Day.SUNDAY else: - MONDAY: Literal[0] - TUESDAY: Literal[1] - WEDNESDAY: Literal[2] - THURSDAY: Literal[3] - FRIDAY: Literal[4] - SATURDAY: Literal[5] - SUNDAY: Literal[6] - -EPOCH: Literal[1970] + MONDAY: Final = 0 + TUESDAY: Final = 1 + WEDNESDAY: Final = 2 + THURSDAY: Final = 3 + FRIDAY: Final = 4 + SATURDAY: Final = 5 + SUNDAY: Final = 6 + +EPOCH: Final = 1970 diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi index 4715bd866ddc..02689238a9a5 100644 --- a/mypy/typeshed/stdlib/code.pyi +++ b/mypy/typeshed/stdlib/code.pyi @@ -1,3 +1,4 @@ +import sys from codeop import CommandCompiler from collections.abc import Callable, Mapping from types import CodeType @@ -18,16 +19,34 @@ class InteractiveInterpreter: class InteractiveConsole(InteractiveInterpreter): buffer: list[str] # undocumented filename: str # undocumented - def __init__(self, locals: Mapping[str, Any] | None = None, filename: str = "") -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, locals: Mapping[str, Any] | None = None, filename: str = "", *, local_exit: bool = False + ) -> None: ... + def push(self, line: str, filename: str | None = None) -> bool: ... + else: + def __init__(self, locals: Mapping[str, Any] | None = None, filename: str = "") -> None: ... + def push(self, line: str) -> bool: ... + def interact(self, banner: str | None = None, exitmsg: str | None = None) -> None: ... - def push(self, line: str) -> bool: ... def resetbuffer(self) -> None: ... def raw_input(self, prompt: str = "") -> str: ... -def interact( - banner: str | None = None, - readfunc: Callable[[str], str] | None = None, - local: Mapping[str, Any] | None = None, - exitmsg: str | None = None, -) -> None: ... +if sys.version_info >= (3, 13): + def interact( + banner: str | None = None, + readfunc: Callable[[str], str] | None = None, + local: Mapping[str, Any] | None = None, + exitmsg: str | None = None, + local_exit: bool = False, + ) -> None: ... + +else: + def interact( + banner: str | None = None, + readfunc: Callable[[str], str] | None = None, + local: Mapping[str, Any] | None = None, + exitmsg: str | None = None, + ) -> None: ... + def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 7dfdda224013..3d5eccfc048d 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -54,7 +54,7 @@ class Future(Generic[_T]): def exception(self, timeout: float | None = None) -> BaseException | None: ... def set_exception(self, exception: BaseException | None) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Executor: if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi index f38cf2c57963..d1b7858eae02 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi @@ -29,7 +29,7 @@ class _WorkItem(Generic[_S]): def __init__(self, future: Future[_S], fn: Callable[..., _S], args: Iterable[Any], kwargs: Mapping[str, Any]) -> None: ... def run(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def _worker( executor_reference: ref[Any], diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index ceb9085fa187..dd5ea0acbe2c 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -30,7 +30,7 @@ class ContextVar(Generic[_T]): def set(self, value: _T, /) -> Token[_T]: ... def reset(self, token: Token[_T], /) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class Token(Generic[_T]): @@ -40,7 +40,7 @@ class Token(Generic[_T]): def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express MISSING: ClassVar[object] if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def copy_context() -> Context: ... diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 56f8bf029b12..24f0db332165 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -40,7 +40,6 @@ __all__ = [ "QUOTE_NONE", "Error", "Dialect", - "__doc__", "excel", "excel_tab", "field_size_limit", @@ -51,13 +50,14 @@ __all__ = [ "list_dialects", "Sniffer", "unregister_dialect", - "__version__", "DictReader", "DictWriter", "unix_dialect", ] if sys.version_info >= (3, 12): __all__ += ["QUOTE_STRINGS", "QUOTE_NOTNULL"] +if sys.version_info < (3, 13): + __all__ += ["__doc__", "__version__"] _T = TypeVar("_T") @@ -111,7 +111,7 @@ class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> dict[_T | Any, str | Any]: ... if sys.version_info >= (3, 12): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class DictWriter(Generic[_T]): fieldnames: Collection[_T] @@ -139,7 +139,7 @@ class DictWriter(Generic[_T]): def writerow(self, rowdict: Mapping[_T, Any]) -> Any: ... def writerows(self, rowdicts: Iterable[Mapping[_T, Any]]) -> None: ... if sys.version_info >= (3, 12): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Sniffer: preferred: list[str] diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 2fe551fa9dc2..dfd61c8f8ffc 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -76,7 +76,7 @@ class LibraryLoader(Generic[_DLLT]): def __getitem__(self, name: str) -> _DLLT: ... def LoadLibrary(self, name: str) -> _DLLT: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... cdll: LibraryLoader[CDLL] if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index c361122704a5..18c7e7b5a467 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -143,7 +143,7 @@ class Field(Generic[_T]): def __set_name__(self, owner: Type[Any], name: str) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # NOTE: Actual return type is 'Field[_T]', but we want to help type checkers # to understand the magic that happens at runtime. diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 7b890ca010dc..71522a59d4df 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -79,6 +79,9 @@ class date: def isoformat(self) -> str: ... def timetuple(self) -> struct_time: ... def toordinal(self) -> int: ... + if sys.version_info >= (3, 13): + def __replace__(self, /, *, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ... + def replace(self, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ... def __le__(self, value: date, /) -> bool: ... def __lt__(self, value: date, /) -> bool: ... @@ -148,6 +151,19 @@ class time: def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... + if sys.version_info >= (3, 13): + def __replace__( + self, + /, + *, + hour: SupportsIndex = ..., + minute: SupportsIndex = ..., + second: SupportsIndex = ..., + microsecond: SupportsIndex = ..., + tzinfo: _TzInfo | None = ..., + fold: int = ..., + ) -> Self: ... + def replace( self, hour: SupportsIndex = ..., @@ -263,6 +279,22 @@ class datetime(date): def date(self) -> _Date: ... def time(self) -> _Time: ... def timetz(self) -> _Time: ... + if sys.version_info >= (3, 13): + def __replace__( + self, + /, + *, + year: SupportsIndex = ..., + month: SupportsIndex = ..., + day: SupportsIndex = ..., + hour: SupportsIndex = ..., + minute: SupportsIndex = ..., + second: SupportsIndex = ..., + microsecond: SupportsIndex = ..., + tzinfo: _TzInfo | None = ..., + fold: int = ..., + ) -> Self: ... + def replace( self, year: SupportsIndex = ..., diff --git a/mypy/typeshed/stdlib/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi index d5b77b8f0e2c..50154d785c2f 100644 --- a/mypy/typeshed/stdlib/difflib.pyi +++ b/mypy/typeshed/stdlib/difflib.pyi @@ -55,7 +55,7 @@ class SequenceMatcher(Generic[_T]): def quick_ratio(self) -> float: ... def real_quick_ratio(self) -> float: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @overload def get_close_matches(word: AnyStr, possibilities: Iterable[AnyStr], n: int = 3, cutoff: float = 0.6) -> list[AnyStr]: ... diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index 796d81d8bf70..47c63cc8b3d3 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -47,7 +47,22 @@ if sys.version_info >= (3, 11): col_offset: int | None = None end_col_offset: int | None = None -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + class _Instruction(NamedTuple): + opname: str + opcode: int + arg: int | None + argval: Any + argrepr: str + offset: int + start_offset: int + starts_line: bool + line_number: int | None + label: int | None = None + positions: Positions | None = None + cache_info: list[tuple[str, int, Any]] | None = None + +elif sys.version_info >= (3, 11): class _Instruction(NamedTuple): opname: str opcode: int diff --git a/mypy/typeshed/stdlib/distutils/archive_util.pyi b/mypy/typeshed/stdlib/distutils/archive_util.pyi index a8947ce35c60..16684ff06956 100644 --- a/mypy/typeshed/stdlib/distutils/archive_util.pyi +++ b/mypy/typeshed/stdlib/distutils/archive_util.pyi @@ -1,20 +1,35 @@ +from _typeshed import StrOrBytesPath, StrPath +from typing import Literal, overload + +@overload def make_archive( base_name: str, format: str, - root_dir: str | None = None, + root_dir: StrOrBytesPath | None = None, base_dir: str | None = None, - verbose: int = 0, - dry_run: int = 0, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, + owner: str | None = None, + group: str | None = None, +) -> str: ... +@overload +def make_archive( + base_name: StrPath, + format: str, + root_dir: StrOrBytesPath, + base_dir: str | None = None, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, owner: str | None = None, group: str | None = None, ) -> str: ... def make_tarball( base_name: str, - base_dir: str, + base_dir: StrPath, compress: str | None = "gzip", - verbose: int = 0, - dry_run: int = 0, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, owner: str | None = None, group: str | None = None, ) -> str: ... -def make_zipfile(base_name: str, base_dir: str, verbose: int = 0, dry_run: int = 0) -> str: ... +def make_zipfile(base_name: str, base_dir: str, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0) -> str: ... diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index cc097728f77c..cd6efee0a210 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,5 +1,7 @@ -from collections.abc import Callable -from typing import Any +from _typeshed import BytesPath, StrPath +from collections.abc import Callable, Iterable +from distutils.file_util import _BytesPathT, _StrPathT +from typing import Any, Literal, overload from typing_extensions import TypeAlias _Macro: TypeAlias = tuple[str] | tuple[str, str | None] @@ -10,7 +12,11 @@ def gen_lib_options( def gen_preprocess_options(macros: list[_Macro], include_dirs: list[str]) -> list[str]: ... def get_default_compiler(osname: str | None = None, platform: str | None = None) -> str: ... def new_compiler( - plat: str | None = None, compiler: str | None = None, verbose: int = 0, dry_run: int = 0, force: int = 0 + plat: str | None = None, + compiler: str | None = None, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, + force: bool | Literal[0, 1] = 0, ) -> CCompiler: ... def show_compilers() -> None: ... @@ -25,7 +31,9 @@ class CCompiler: library_dirs: list[str] runtime_library_dirs: list[str] objects: list[str] - def __init__(self, verbose: int = 0, dry_run: int = 0, force: int = 0) -> None: ... + def __init__( + self, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0, force: bool | Literal[0, 1] = 0 + ) -> None: ... def add_include_dir(self, dir: str) -> None: ... def set_include_dirs(self, dirs: list[str]) -> None: ... def add_library(self, libname: str) -> None: ... @@ -39,7 +47,7 @@ class CCompiler: def add_link_object(self, object: str) -> None: ... def set_link_objects(self, objects: list[str]) -> None: ... def detect_language(self, sources: str | list[str]) -> str | None: ... - def find_library_file(self, dirs: list[str], lib: str, debug: bool = ...) -> str | None: ... + def find_library_file(self, dirs: list[str], lib: str, debug: bool | Literal[0, 1] = 0) -> str | None: ... def has_function( self, funcname: str, @@ -58,7 +66,7 @@ class CCompiler: output_dir: str | None = None, macros: list[_Macro] | None = None, include_dirs: list[str] | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, depends: list[str] | None = None, @@ -68,7 +76,7 @@ class CCompiler: objects: list[str], output_libname: str, output_dir: str | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, target_lang: str | None = None, ) -> None: ... def link( @@ -81,7 +89,7 @@ class CCompiler: library_dirs: list[str] | None = None, runtime_library_dirs: list[str] | None = None, export_symbols: list[str] | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, build_temp: str | None = None, @@ -95,7 +103,7 @@ class CCompiler: libraries: list[str] | None = None, library_dirs: list[str] | None = None, runtime_library_dirs: list[str] | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, target_lang: str | None = None, @@ -109,7 +117,7 @@ class CCompiler: library_dirs: list[str] | None = None, runtime_library_dirs: list[str] | None = None, export_symbols: list[str] | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, build_temp: str | None = None, @@ -124,7 +132,7 @@ class CCompiler: library_dirs: list[str] | None = None, runtime_library_dirs: list[str] | None = None, export_symbols: list[str] | None = None, - debug: bool = ..., + debug: bool | Literal[0, 1] = 0, extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, build_temp: str | None = None, @@ -139,14 +147,27 @@ class CCompiler: extra_preargs: list[str] | None = None, extra_postargs: list[str] | None = None, ) -> None: ... - def executable_filename(self, basename: str, strip_dir: int = 0, output_dir: str = "") -> str: ... - def library_filename(self, libname: str, lib_type: str = "static", strip_dir: int = 0, output_dir: str = "") -> str: ... - def object_filenames(self, source_filenames: list[str], strip_dir: int = 0, output_dir: str = "") -> list[str]: ... - def shared_object_filename(self, basename: str, strip_dir: int = 0, output_dir: str = "") -> str: ... + @overload + def executable_filename(self, basename: str, strip_dir: Literal[0, False] = 0, output_dir: StrPath = "") -> str: ... + @overload + def executable_filename(self, basename: StrPath, strip_dir: Literal[1, True], output_dir: StrPath = "") -> str: ... + def library_filename( + self, libname: str, lib_type: str = "static", strip_dir: bool | Literal[0, 1] = 0, output_dir: StrPath = "" + ) -> str: ... + def object_filenames( + self, source_filenames: Iterable[StrPath], strip_dir: bool | Literal[0, 1] = 0, output_dir: StrPath | None = "" + ) -> list[str]: ... + @overload + def shared_object_filename(self, basename: str, strip_dir: Literal[0, False] = 0, output_dir: StrPath = "") -> str: ... + @overload + def shared_object_filename(self, basename: StrPath, strip_dir: Literal[1, True], output_dir: StrPath = "") -> str: ... def execute(self, func: Callable[..., object], args: tuple[Any, ...], msg: str | None = None, level: int = 1) -> None: ... def spawn(self, cmd: list[str]) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... - def move_file(self, src: str, dst: str) -> str: ... + @overload + def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ... + @overload + def move_file(self, src: BytesPath, dst: _BytesPathT) -> _BytesPathT | bytes: ... def announce(self, msg: str, level: int = 1) -> None: ... def warn(self, msg: str) -> None: ... def debug_print(self, msg: str) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index 61fce37b80bc..defea50e78dc 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -1,12 +1,14 @@ -from _typeshed import Incomplete +from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused from abc import abstractmethod from collections.abc import Callable, Iterable from distutils.dist import Distribution -from typing import Any +from distutils.file_util import _BytesPathT, _StrPathT +from typing import Any, ClassVar, Literal, overload class Command: distribution: Distribution - sub_commands: list[tuple[str, Callable[[Command], bool] | None]] + # Any to work around variance issues + sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] def __init__(self, dist: Distribution) -> None: ... @abstractmethod def initialize_options(self) -> None: ... @@ -22,32 +24,63 @@ class Command: def ensure_dirname(self, option: str) -> None: ... def get_command_name(self) -> str: ... def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ... - def get_finalized_command(self, command: str, create: int = 1) -> Command: ... - def reinitialize_command(self, command: Command | str, reinit_subcommands: int = 0) -> Command: ... + def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ... + def reinitialize_command(self, command: Command | str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... def run_command(self, command: str) -> None: ... def get_sub_commands(self) -> list[str]: ... def warn(self, msg: str) -> None: ... def execute(self, func: Callable[..., object], args: Iterable[Any], msg: str | None = None, level: int = 1) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... + @overload def copy_file( - self, infile: str, outfile: str, preserve_mode: int = 1, preserve_times: int = 1, link: str | None = None, level: Any = 1 - ) -> tuple[str, bool]: ... # level is not used + self, + infile: StrPath, + outfile: _StrPathT, + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + link: str | None = None, + level: Unused = 1, + ) -> tuple[_StrPathT | str, bool]: ... + @overload + def copy_file( + self, + infile: BytesPath, + outfile: _BytesPathT, + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + link: str | None = None, + level: Unused = 1, + ) -> tuple[_BytesPathT | bytes, bool]: ... def copy_tree( self, - infile: str, + infile: StrPath, outfile: str, - preserve_mode: int = 1, - preserve_times: int = 1, - preserve_symlinks: int = 0, - level: Any = 1, - ) -> list[str]: ... # level is not used - def move_file(self, src: str, dst: str, level: Any = 1) -> str: ... # level is not used - def spawn(self, cmd: Iterable[str], search_path: int = 1, level: Any = 1) -> None: ... # level is not used + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + preserve_symlinks: bool | Literal[0, 1] = 0, + level: Unused = 1, + ) -> list[str]: ... + @overload + def move_file(self, src: StrPath, dst: _StrPathT, level: Unused = 1) -> _StrPathT | str: ... + @overload + def move_file(self, src: BytesPath, dst: _BytesPathT, level: Unused = 1) -> _BytesPathT | bytes: ... + def spawn(self, cmd: Iterable[str], search_path: bool | Literal[0, 1] = 1, level: Unused = 1) -> None: ... + @overload def make_archive( self, base_name: str, format: str, - root_dir: str | None = None, + root_dir: StrOrBytesPath | None = None, + base_dir: str | None = None, + owner: str | None = None, + group: str | None = None, + ) -> str: ... + @overload + def make_archive( + self, + base_name: StrPath, + format: str, + root_dir: StrOrBytesPath, base_dir: str | None = None, owner: str | None = None, group: str | None = None, @@ -55,12 +88,12 @@ class Command: def make_file( self, infiles: str | list[str] | tuple[str, ...], - outfile: str, + outfile: StrOrBytesPath, func: Callable[..., object], args: list[Any], exec_msg: str | None = None, skip_msg: str | None = None, - level: Any = 1, - ) -> None: ... # level is not used + level: Unused = 1, + ) -> None: ... def ensure_finalized(self) -> None: ... def dump_options(self, header: Incomplete | None = None, indent: str = "") -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi index fa98e86d592a..d1eb374ff52b 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi @@ -1,5 +1,5 @@ import sys -from typing import Any +from typing import Any, Literal from ..cmd import Command @@ -9,9 +9,9 @@ if sys.platform == "win32": class PyDialog(Dialog): def __init__(self, *args, **kw) -> None: ... def title(self, title) -> None: ... - def back(self, title, next, name: str = "Back", active: int = 1): ... - def cancel(self, title, next, name: str = "Cancel", active: int = 1): ... - def next(self, title, next, name: str = "Next", active: int = 1): ... + def back(self, title, next, name: str = "Back", active: bool | Literal[0, 1] = 1): ... + def cancel(self, title, next, name: str = "Cancel", active: bool | Literal[0, 1] = 1): ... + def next(self, title, next, name: str = "Next", active: bool | Literal[0, 1] = 1): ... def xbutton(self, name, title, next, xpos): ... class bdist_msi(Command): diff --git a/mypy/typeshed/stdlib/distutils/command/build.pyi b/mypy/typeshed/stdlib/distutils/command/build.pyi index cf3c8a562ff3..31fc036d4f97 100644 --- a/mypy/typeshed/stdlib/distutils/command/build.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build.pyi @@ -1,4 +1,5 @@ -from typing import Any +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -28,4 +29,5 @@ class build(Command): def has_c_libraries(self): ... def has_ext_modules(self): ... def has_scripts(self): ... - sub_commands: Any + # Any to work around variance issues + sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] diff --git a/mypy/typeshed/stdlib/distutils/command/build_py.pyi b/mypy/typeshed/stdlib/distutils/command/build_py.pyi index ca4e4ed7e797..4c607c6dabe9 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_py.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_py.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Literal from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 @@ -32,7 +32,7 @@ class build_py(Command): def find_all_modules(self): ... def get_source_files(self): ... def get_module_outfile(self, build_dir, package, module): ... - def get_outputs(self, include_bytecode: int = 1): ... + def get_outputs(self, include_bytecode: bool | Literal[0, 1] = 1): ... def build_module(self, module, module_file, package): ... def build_modules(self) -> None: ... def build_packages(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/check.pyi b/mypy/typeshed/stdlib/distutils/command/check.pyi index 9cbcc6c87f21..da041d82587d 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Literal from typing_extensions import TypeAlias from ..cmd import Command @@ -16,7 +16,7 @@ class SilentReporter(_Reporter): report_level, halt_level, stream: Any | None = ..., - debug: int = ..., + debug: bool | Literal[0, 1] = 0, encoding: str = ..., error_handler: str = ..., ) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi index 7077c9a4c158..391f5a862038 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,6 +1,7 @@ +from _typeshed import StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any +from typing import Any, Literal from ..ccompiler import CCompiler from ..cmd import Command @@ -65,8 +66,8 @@ class config(Command): include_dirs: Sequence[str] | None = None, libraries: Sequence[str] | None = None, library_dirs: Sequence[str] | None = None, - decl: int = 0, - call: int = 0, + decl: bool | Literal[0, 1] = 0, + call: bool | Literal[0, 1] = 0, ) -> bool: ... def check_lib( self, @@ -80,4 +81,4 @@ class config(Command): self, header: str, include_dirs: Sequence[str] | None = None, library_dirs: Sequence[str] | None = None, lang: str = "c" ) -> bool: ... -def dump_file(filename: str, head: Any | None = None) -> None: ... +def dump_file(filename: StrOrBytesPath, head: Any | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index 661d256e6f07..8b2295d7a3c7 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -1,4 +1,5 @@ -from typing import Any +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -60,4 +61,5 @@ class install(Command): def has_headers(self): ... def has_scripts(self): ... def has_data(self): ... - sub_commands: Any + # Any to work around variance issues + sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] diff --git a/mypy/typeshed/stdlib/distutils/command/register.pyi b/mypy/typeshed/stdlib/distutils/command/register.pyi index f88b94113ff4..a5e251d2d01e 100644 --- a/mypy/typeshed/stdlib/distutils/command/register.pyi +++ b/mypy/typeshed/stdlib/distutils/command/register.pyi @@ -1,10 +1,12 @@ -from typing import Any +from collections.abc import Callable +from typing import Any, ClassVar from ..config import PyPIRCCommand class register(PyPIRCCommand): description: str - sub_commands: Any + # Any to work around variance issues + sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] list_classifiers: int strict: int def initialize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/sdist.pyi b/mypy/typeshed/stdlib/distutils/command/sdist.pyi index 636c4a351d19..db303f77a463 100644 --- a/mypy/typeshed/stdlib/distutils/command/sdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/sdist.pyi @@ -1,4 +1,5 @@ -from typing import Any +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -11,7 +12,8 @@ class sdist(Command): boolean_options: Any help_options: Any negative_opt: Any - sub_commands: Any + # Any to work around variance issues + sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] READMES: Any template: Any manifest: Any diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index c41c8ba19a8b..f3c434df0b1a 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -3,7 +3,7 @@ from collections.abc import Mapping from distutils.cmd import Command as Command from distutils.dist import Distribution as Distribution from distutils.extension import Extension as Extension -from typing import Any +from typing import Any, Literal USAGE: str @@ -45,7 +45,7 @@ def setup( command_packages: list[str] = ..., command_options: Mapping[str, Mapping[str, tuple[Any, Any]]] = ..., package_data: Mapping[str, list[str]] = ..., - include_package_data: bool = ..., + include_package_data: bool | Literal[0, 1] = ..., libraries: list[str] = ..., headers: list[str] = ..., ext_package: str = ..., diff --git a/mypy/typeshed/stdlib/distutils/dep_util.pyi b/mypy/typeshed/stdlib/distutils/dep_util.pyi index 096ce19d4859..058377accabc 100644 --- a/mypy/typeshed/stdlib/distutils/dep_util.pyi +++ b/mypy/typeshed/stdlib/distutils/dep_util.pyi @@ -1,3 +1,14 @@ -def newer(source: str, target: str) -> bool: ... -def newer_pairwise(sources: list[str], targets: list[str]) -> list[tuple[str, str]]: ... -def newer_group(sources: list[str], target: str, missing: str = "error") -> bool: ... +from _typeshed import StrOrBytesPath, SupportsLenAndGetItem +from collections.abc import Iterable +from typing import Literal, TypeVar + +_SourcesT = TypeVar("_SourcesT", bound=StrOrBytesPath) +_TargetsT = TypeVar("_TargetsT", bound=StrOrBytesPath) + +def newer(source: StrOrBytesPath, target: StrOrBytesPath) -> bool | Literal[1]: ... +def newer_pairwise( + sources: SupportsLenAndGetItem[_SourcesT], targets: SupportsLenAndGetItem[_TargetsT] +) -> tuple[list[_SourcesT], list[_TargetsT]]: ... +def newer_group( + sources: Iterable[StrOrBytesPath], target: StrOrBytesPath, missing: Literal["error", "ignore", "newer"] = "error" +) -> Literal[0, 1]: ... diff --git a/mypy/typeshed/stdlib/distutils/dir_util.pyi b/mypy/typeshed/stdlib/distutils/dir_util.pyi index 2324a2d50caa..23e2c3bc28b9 100644 --- a/mypy/typeshed/stdlib/distutils/dir_util.pyi +++ b/mypy/typeshed/stdlib/distutils/dir_util.pyi @@ -1,13 +1,23 @@ -def mkpath(name: str, mode: int = 0o777, verbose: int = 1, dry_run: int = 0) -> list[str]: ... -def create_tree(base_dir: str, files: list[str], mode: int = 0o777, verbose: int = 1, dry_run: int = 0) -> None: ... +from _typeshed import StrOrBytesPath, StrPath +from collections.abc import Iterable +from typing import Literal + +def mkpath(name: str, mode: int = 0o777, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0) -> list[str]: ... +def create_tree( + base_dir: StrPath, + files: Iterable[StrPath], + mode: int = 0o777, + verbose: bool | Literal[0, 1] = 1, + dry_run: bool | Literal[0, 1] = 0, +) -> None: ... def copy_tree( - src: str, + src: StrPath, dst: str, - preserve_mode: int = 1, - preserve_times: int = 1, - preserve_symlinks: int = 0, - update: int = 0, - verbose: int = 1, - dry_run: int = 0, + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + preserve_symlinks: bool | Literal[0, 1] = 0, + update: bool | Literal[0, 1] = 0, + verbose: bool | Literal[0, 1] = 1, + dry_run: bool | Literal[0, 1] = 0, ) -> list[str]: ... -def remove_tree(directory: str, verbose: int = 1, dry_run: int = 0) -> None: ... +def remove_tree(directory: StrOrBytesPath, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index b296b11f73ba..4094df903325 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -1,8 +1,8 @@ -from _typeshed import FileDescriptorOrPath, Incomplete, SupportsWrite +from _typeshed import Incomplete, StrOrBytesPath, StrPath, SupportsWrite from collections.abc import Iterable, Mapping from distutils.cmd import Command from re import Pattern -from typing import IO, Any, ClassVar, TypeVar, overload +from typing import IO, Any, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias command_re: Pattern[str] @@ -11,7 +11,7 @@ _OptionsList: TypeAlias = list[tuple[str, str | None, str, int] | tuple[str, str _CommandT = TypeVar("_CommandT", bound=Command) class DistributionMetadata: - def __init__(self, path: FileDescriptorOrPath | None = None) -> None: ... + def __init__(self, path: StrOrBytesPath | None = None) -> None: ... name: str | None version: str | None author: str | None @@ -30,7 +30,7 @@ class DistributionMetadata: requires: list[str] | None obsoletes: list[str] | None def read_pkg_file(self, file: IO[str]) -> None: ... - def write_pkg_info(self, base_dir: str) -> None: ... + def write_pkg_info(self, base_dir: StrPath) -> None: ... def write_pkg_file(self, file: SupportsWrite[str]) -> None: ... def get_name(self) -> str: ... def get_version(self) -> str: ... @@ -63,7 +63,10 @@ class Distribution: def __init__(self, attrs: Mapping[str, Any] | None = None) -> None: ... def get_option_dict(self, command: str) -> dict[str, tuple[str, str]]: ... def parse_config_files(self, filenames: Iterable[str] | None = None) -> None: ... - def get_command_obj(self, command: str, create: bool = True) -> Command | None: ... + @overload + def get_command_obj(self, command: str, create: Literal[1, True] = 1) -> Command: ... + @overload + def get_command_obj(self, command: str, create: Literal[0, False]) -> Command | None: ... global_options: ClassVar[_OptionsList] common_usage: ClassVar[str] display_options: ClassVar[_OptionsList] diff --git a/mypy/typeshed/stdlib/distutils/file_util.pyi b/mypy/typeshed/stdlib/distutils/file_util.pyi index a97dfca60007..873d23ea7e50 100644 --- a/mypy/typeshed/stdlib/distutils/file_util.pyi +++ b/mypy/typeshed/stdlib/distutils/file_util.pyi @@ -1,14 +1,38 @@ -from collections.abc import Sequence +from _typeshed import BytesPath, StrOrBytesPath, StrPath +from collections.abc import Iterable +from typing import Literal, TypeVar, overload +_StrPathT = TypeVar("_StrPathT", bound=StrPath) +_BytesPathT = TypeVar("_BytesPathT", bound=BytesPath) + +@overload +def copy_file( + src: StrPath, + dst: _StrPathT, + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + update: bool | Literal[0, 1] = 0, + link: str | None = None, + verbose: bool | Literal[0, 1] = 1, + dry_run: bool | Literal[0, 1] = 0, +) -> tuple[_StrPathT | str, bool]: ... +@overload def copy_file( - src: str, - dst: str, - preserve_mode: bool = ..., - preserve_times: bool = ..., - update: bool = ..., + src: BytesPath, + dst: _BytesPathT, + preserve_mode: bool | Literal[0, 1] = 1, + preserve_times: bool | Literal[0, 1] = 1, + update: bool | Literal[0, 1] = 0, link: str | None = None, - verbose: bool = ..., - dry_run: bool = ..., -) -> tuple[str, str]: ... -def move_file(src: str, dst: str, verbose: bool = ..., dry_run: bool = ...) -> str: ... -def write_file(filename: str, contents: Sequence[str]) -> None: ... + verbose: bool | Literal[0, 1] = 1, + dry_run: bool | Literal[0, 1] = 0, +) -> tuple[_BytesPathT | bytes, bool]: ... +@overload +def move_file( + src: StrPath, dst: _StrPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 +) -> _StrPathT | str: ... +@overload +def move_file( + src: BytesPath, dst: _BytesPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 +) -> _BytesPathT | bytes: ... +def write_file(filename: StrOrBytesPath, contents: Iterable[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/filelist.pyi b/mypy/typeshed/stdlib/distutils/filelist.pyi index 25db2f3cb6cc..607a78a1fbac 100644 --- a/mypy/typeshed/stdlib/distutils/filelist.pyi +++ b/mypy/typeshed/stdlib/distutils/filelist.pyi @@ -23,7 +23,11 @@ class FileList: def include_pattern(self, pattern: str | Pattern[str], *, is_regex: Literal[True, 1]) -> bool: ... @overload def include_pattern( - self, pattern: str | Pattern[str], anchor: bool | Literal[0, 1] = 1, prefix: str | None = None, is_regex: int = 0 + self, + pattern: str | Pattern[str], + anchor: bool | Literal[0, 1] = 1, + prefix: str | None = None, + is_regex: bool | Literal[0, 1] = 0, ) -> bool: ... @overload def exclude_pattern( @@ -33,7 +37,11 @@ class FileList: def exclude_pattern(self, pattern: str | Pattern[str], *, is_regex: Literal[True, 1]) -> bool: ... @overload def exclude_pattern( - self, pattern: str | Pattern[str], anchor: bool | Literal[0, 1] = 1, prefix: str | None = None, is_regex: int = 0 + self, + pattern: str | Pattern[str], + anchor: bool | Literal[0, 1] = 1, + prefix: str | None = None, + is_regex: bool | Literal[0, 1] = 0, ) -> bool: ... def findall(dir: str = ".") -> list[str]: ... @@ -46,5 +54,5 @@ def translate_pattern( def translate_pattern(pattern: str | Pattern[str], *, is_regex: Literal[True, 1]) -> Pattern[str]: ... @overload def translate_pattern( - pattern: str | Pattern[str], anchor: bool | Literal[0, 1] = 1, prefix: str | None = None, is_regex: int = 0 + pattern: str | Pattern[str], anchor: bool | Literal[0, 1] = 1, prefix: str | None = None, is_regex: bool | Literal[0, 1] = 0 ) -> Pattern[str]: ... diff --git a/mypy/typeshed/stdlib/distutils/spawn.pyi b/mypy/typeshed/stdlib/distutils/spawn.pyi index a8a2c4140b2d..50d89aeb9e5f 100644 --- a/mypy/typeshed/stdlib/distutils/spawn.pyi +++ b/mypy/typeshed/stdlib/distutils/spawn.pyi @@ -1,2 +1,6 @@ -def spawn(cmd: list[str], search_path: bool = ..., verbose: bool = ..., dry_run: bool = ...) -> None: ... +from typing import Literal + +def spawn( + cmd: list[str], search_path: bool | Literal[0, 1] = 1, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 +) -> None: ... def find_executable(executable: str, path: str | None = None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/distutils/sysconfig.pyi b/mypy/typeshed/stdlib/distutils/sysconfig.pyi index e2399a6cf36b..da72e3275fe3 100644 --- a/mypy/typeshed/stdlib/distutils/sysconfig.pyi +++ b/mypy/typeshed/stdlib/distutils/sysconfig.pyi @@ -23,8 +23,10 @@ def get_config_vars() -> dict[str, str | int]: ... def get_config_vars(arg: str, /, *args: str) -> list[str | int]: ... def get_config_h_filename() -> str: ... def get_makefile_filename() -> str: ... -def get_python_inc(plat_specific: bool = ..., prefix: str | None = None) -> str: ... -def get_python_lib(plat_specific: bool = ..., standard_lib: bool = ..., prefix: str | None = None) -> str: ... +def get_python_inc(plat_specific: bool | Literal[0, 1] = 0, prefix: str | None = None) -> str: ... +def get_python_lib( + plat_specific: bool | Literal[0, 1] = 0, standard_lib: bool | Literal[0, 1] = 0, prefix: str | None = None +) -> str: ... def customize_compiler(compiler: CCompiler) -> None: ... if sys.version_info < (3, 10): diff --git a/mypy/typeshed/stdlib/distutils/text_file.pyi b/mypy/typeshed/stdlib/distutils/text_file.pyi index 4a6cf1db77c6..54951af7e55d 100644 --- a/mypy/typeshed/stdlib/distutils/text_file.pyi +++ b/mypy/typeshed/stdlib/distutils/text_file.pyi @@ -1,4 +1,4 @@ -from typing import IO +from typing import IO, Literal class TextFile: def __init__( @@ -6,12 +6,12 @@ class TextFile: filename: str | None = None, file: IO[str] | None = None, *, - strip_comments: bool = ..., - lstrip_ws: bool = ..., - rstrip_ws: bool = ..., - skip_blanks: bool = ..., - join_lines: bool = ..., - collapse_join: bool = ..., + strip_comments: bool | Literal[0, 1] = ..., + lstrip_ws: bool | Literal[0, 1] = ..., + rstrip_ws: bool | Literal[0, 1] = ..., + skip_blanks: bool | Literal[0, 1] = ..., + join_lines: bool | Literal[0, 1] = ..., + collapse_join: bool | Literal[0, 1] = ..., ) -> None: ... def open(self, filename: str) -> None: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/util.pyi b/mypy/typeshed/stdlib/distutils/util.pyi index 835266edde59..515b5b2b86d9 100644 --- a/mypy/typeshed/stdlib/distutils/util.pyi +++ b/mypy/typeshed/stdlib/distutils/util.pyi @@ -5,22 +5,26 @@ from typing import Any, Literal def get_host_platform() -> str: ... def get_platform() -> str: ... def convert_path(pathname: str) -> str: ... -def change_root(new_root: str, pathname: str) -> str: ... +def change_root(new_root: StrPath, pathname: StrPath) -> str: ... def check_environ() -> None: ... def subst_vars(s: str, local_vars: Mapping[str, str]) -> None: ... def split_quoted(s: str) -> list[str]: ... def execute( - func: Callable[..., object], args: tuple[Any, ...], msg: str | None = None, verbose: bool = ..., dry_run: bool = ... + func: Callable[..., object], + args: tuple[Any, ...], + msg: str | None = None, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, ) -> None: ... def strtobool(val: str) -> Literal[0, 1]: ... def byte_compile( py_files: list[str], optimize: int = 0, - force: bool = ..., + force: bool | Literal[0, 1] = 0, prefix: str | None = None, base_dir: str | None = None, - verbose: bool = ..., - dry_run: bool = ..., + verbose: bool | Literal[0, 1] = 1, + dry_run: bool | Literal[0, 1] = 0, direct: bool | None = None, ) -> None: ... def rfc822_escape(header: str) -> str: ... diff --git a/mypy/typeshed/stdlib/faulthandler.pyi b/mypy/typeshed/stdlib/faulthandler.pyi index 7b42b8ec8444..320a8b6fad15 100644 --- a/mypy/typeshed/stdlib/faulthandler.pyi +++ b/mypy/typeshed/stdlib/faulthandler.pyi @@ -10,4 +10,4 @@ def is_enabled() -> bool: ... if sys.platform != "win32": def register(signum: int, file: FileDescriptorLike = ..., all_threads: bool = ..., chain: bool = ...) -> None: ... - def unregister(signum: int) -> None: ... + def unregister(signum: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index 4f54a9bff6ee..5c8232d800d5 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -52,6 +52,6 @@ class dircmp(Generic[AnyStr]): def phase4(self) -> None: ... def phase4_closure(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def clear_cache() -> None: ... diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index e8d5dd8d2d5b..1e6aa78e2607 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -200,7 +200,7 @@ class FileInput(Iterator[AnyStr]): def isfirstline(self) -> bool: ... def isstdin(self) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 10): def hook_compressed( diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 27550cfe08e6..9957fa8f1634 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -132,7 +132,7 @@ class partial(Generic[_T]): def __new__(cls, func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> Self: ... def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # With protocols, this could change into a generic protocol that defines __get__ and returns _T _Descriptor: TypeAlias = Any @@ -149,7 +149,7 @@ class partialmethod(Generic[_T]): @property def __isabstractmethod__(self) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _SingleDispatchCallable(Generic[_T]): registry: types.MappingProxyType[Any, Callable[..., _T]] @@ -196,7 +196,7 @@ class cached_property(Generic[_T_co]): # __set__ is not defined at runtime, but @cached_property is designed to be settable def __set__(self, instance: object, value: _T_co) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 9): def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ... diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi index 0dd5dec4b2ec..9d87c48fd520 100644 --- a/mypy/typeshed/stdlib/genericpath.pyi +++ b/mypy/typeshed/stdlib/genericpath.pyi @@ -20,6 +20,8 @@ __all__ = [ ] if sys.version_info >= (3, 12): __all__ += ["islink"] +if sys.version_info >= (3, 13): + __all__ += ["isjunction", "isdevdrive", "lexists"] # All overloads can return empty string. Ideally, Literal[""] would be a valid # Iterable[T], so that list[T] | Literal[""] could be used as a return @@ -50,3 +52,8 @@ def getctime(filename: FileDescriptorOrPath) -> float: ... def samefile(f1: FileDescriptorOrPath, f2: FileDescriptorOrPath) -> bool: ... def sameopenfile(fp1: int, fp2: int) -> bool: ... def samestat(s1: os.stat_result, s2: os.stat_result) -> bool: ... + +if sys.version_info >= (3, 13): + def isjunction(path: StrOrBytesPath) -> bool: ... + def isdevdrive(path: StrOrBytesPath) -> bool: ... + def lexists(path: StrOrBytesPath) -> bool: ... diff --git a/mypy/typeshed/stdlib/graphlib.pyi b/mypy/typeshed/stdlib/graphlib.pyi index c02d447ad501..1ca8cbe12b08 100644 --- a/mypy/typeshed/stdlib/graphlib.pyi +++ b/mypy/typeshed/stdlib/graphlib.pyi @@ -23,6 +23,6 @@ class TopologicalSorter(Generic[_T]): def get_ready(self) -> tuple[_T, ...]: ... def static_order(self) -> Iterable[_T]: ... if sys.version_info >= (3, 11): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class CycleError(ValueError): ... diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 7f43795dd01f..542945698bba 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -12,8 +12,8 @@ _ReadBinaryMode: TypeAlias = Literal["r", "rb"] _WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"] _OpenTextMode: TypeAlias = Literal["rt", "at", "wt", "xt"] -READ: Literal[1] # undocumented -WRITE: Literal[2] # undocumented +READ: object # undocumented +WRITE: object # undocumented FTEXT: int # actually Literal[1] # undocumented FHCRC: int # actually Literal[2] # undocumented @@ -86,7 +86,7 @@ class BadGzipFile(OSError): ... class GzipFile(_compression.BaseStream): myfileobj: FileIO | None - mode: Literal[1, 2] + mode: object name: str compress: zlib._Compress fileobj: _ReadableFileobj | _WritableFileobj diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index bb5737cc0481..d455283948d1 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -1,6 +1,5 @@ import sys from enum import IntEnum -from typing import Literal if sys.version_info >= (3, 11): from enum import StrEnum @@ -49,11 +48,19 @@ class HTTPStatus(IntEnum): GONE = 410 LENGTH_REQUIRED = 411 PRECONDITION_FAILED = 412 + if sys.version_info >= (3, 13): + CONTENT_TOO_LARGE = 413 REQUEST_ENTITY_TOO_LARGE = 413 + if sys.version_info >= (3, 13): + URI_TOO_LONG = 414 REQUEST_URI_TOO_LONG = 414 UNSUPPORTED_MEDIA_TYPE = 415 + if sys.version_info >= (3, 13): + RANGE_NOT_SATISFIABLE = 416 REQUESTED_RANGE_NOT_SATISFIABLE = 416 EXPECTATION_FAILED = 417 + if sys.version_info >= (3, 13): + UNPROCESSABLE_CONTENT = 422 UNPROCESSABLE_ENTITY = 422 LOCKED = 423 FAILED_DEPENDENCY = 424 @@ -75,9 +82,9 @@ class HTTPStatus(IntEnum): MISDIRECTED_REQUEST = 421 UNAVAILABLE_FOR_LEGAL_REASONS = 451 if sys.version_info >= (3, 9): - EARLY_HINTS: Literal[103] - IM_A_TEAPOT: Literal[418] - TOO_EARLY: Literal[425] + EARLY_HINTS = 103 + IM_A_TEAPOT = 418 + TOO_EARLY = 425 if sys.version_info >= (3, 12): @property def is_informational(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/cookies.pyi b/mypy/typeshed/stdlib/http/cookies.pyi index 3d19bb108c2d..c4af5256b5d8 100644 --- a/mypy/typeshed/stdlib/http/cookies.pyi +++ b/mypy/typeshed/stdlib/http/cookies.pyi @@ -45,7 +45,7 @@ class Morsel(dict[str, Any], Generic[_T]): def __eq__(self, morsel: object) -> bool: ... def __setitem__(self, K: str, V: Any) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class BaseCookie(dict[str, Morsel[_T]], Generic[_T]): def __init__(self, input: _DataType | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index b2fe14777056..56ee20523950 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -240,7 +240,10 @@ class DistributionFinder(MetaPathFinder): class MetadataPathFinder(DistributionFinder): @classmethod def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 11): + @classmethod + def invalidate_caches(cls) -> None: ... + elif sys.version_info >= (3, 10): # Yes, this is an instance method that has a parameter named "cls" def invalidate_caches(cls) -> None: ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 0abf16d9d0ab..23e0663d0d60 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -318,6 +318,7 @@ class Signature: def bind(self, *args: Any, **kwargs: Any) -> BoundArguments: ... def bind_partial(self, *args: Any, **kwargs: Any) -> BoundArguments: ... def replace(self, *, parameters: Sequence[Parameter] | type[_void] | None = ..., return_annotation: Any = ...) -> Self: ... + __replace__ = replace if sys.version_info >= (3, 10): @classmethod def from_callable( @@ -332,6 +333,8 @@ class Signature: else: @classmethod def from_callable(cls, obj: _IntrospectableCallable, *, follow_wrapped: bool = True) -> Self: ... + if sys.version_info >= (3, 13): + def format(self, *, max_width: int | None = None) -> str: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... @@ -392,6 +395,9 @@ class Parameter: default: Any = ..., annotation: Any = ..., ) -> Self: ... + if sys.version_info >= (3, 13): + __replace__ = replace + def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/keyword.pyi b/mypy/typeshed/stdlib/keyword.pyi index 5eb7aab85317..960dfd2fa155 100644 --- a/mypy/typeshed/stdlib/keyword.pyi +++ b/mypy/typeshed/stdlib/keyword.pyi @@ -7,14 +7,14 @@ if sys.version_info >= (3, 9): else: __all__ = ["iskeyword", "kwlist"] -def iskeyword(s: str) -> bool: ... +def iskeyword(s: str, /) -> bool: ... # a list at runtime, but you're not meant to mutate it; # type it as a sequence kwlist: Final[Sequence[str]] if sys.version_info >= (3, 9): - def issoftkeyword(s: str) -> bool: ... + def issoftkeyword(s: str, /) -> bool: ... # a list at runtime, but you're not meant to mutate it; # type it as a sequence diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 7ceddfa7ff28..8b19444a5d01 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -50,7 +50,6 @@ __all__ = [ "makeLogRecord", "setLoggerClass", "shutdown", - "warn", "warning", "getLogRecordFactory", "setLogRecordFactory", @@ -58,6 +57,8 @@ __all__ = [ "raiseExceptions", ] +if sys.version_info < (3, 13): + __all__ += ["warn"] if sys.version_info >= (3, 11): __all__ += ["getLevelNamesMapping"] if sys.version_info >= (3, 12): @@ -156,15 +157,17 @@ class Logger(Filterer): stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... + if sys.version_info < (3, 13): + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def error( self, msg: object, @@ -365,12 +368,18 @@ _L = TypeVar("_L", bound=Logger | LoggerAdapter[Any]) class LoggerAdapter(Generic[_L]): logger: _L manager: Manager # undocumented + + if sys.version_info >= (3, 13): + def __init__(self, logger: _L, extra: Mapping[str, object] | None = None, merge_extra: bool = False) -> None: ... + elif sys.version_info >= (3, 10): + def __init__(self, logger: _L, extra: Mapping[str, object] | None = None) -> None: ... + else: + def __init__(self, logger: _L, extra: Mapping[str, object]) -> None: ... + if sys.version_info >= (3, 10): extra: Mapping[str, object] | None - def __init__(self, logger: _L, extra: Mapping[str, object] | None = None) -> None: ... else: extra: Mapping[str, object] - def __init__(self, logger: _L, extra: Mapping[str, object]) -> None: ... def process(self, msg: Any, kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]: ... def debug( @@ -403,16 +412,18 @@ class LoggerAdapter(Generic[_L]): extra: Mapping[str, object] | None = None, **kwargs: object, ) -> None: ... - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... + if sys.version_info < (3, 13): + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + **kwargs: object, + ) -> None: ... + def error( self, msg: object, @@ -458,19 +469,32 @@ class LoggerAdapter(Generic[_L]): def getEffectiveLevel(self) -> int: ... def setLevel(self, level: _Level) -> None: ... def hasHandlers(self) -> bool: ... - def _log( - self, - level: int, - msg: object, - args: _ArgsType, - exc_info: _ExcInfoType | None = None, - extra: Mapping[str, object] | None = None, - stack_info: bool = False, - ) -> None: ... # undocumented + if sys.version_info >= (3, 11): + def _log( + self, + level: int, + msg: object, + args: _ArgsType, + *, + exc_info: _ExcInfoType | None = None, + extra: Mapping[str, object] | None = None, + stack_info: bool = False, + ) -> None: ... # undocumented + else: + def _log( + self, + level: int, + msg: object, + args: _ArgsType, + exc_info: _ExcInfoType | None = None, + extra: Mapping[str, object] | None = None, + stack_info: bool = False, + ) -> None: ... # undocumented + @property def name(self) -> str: ... # undocumented if sys.version_info >= (3, 11): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def getLogger(name: str | None = None) -> Logger: ... def getLoggerClass() -> type[Logger]: ... @@ -499,14 +523,17 @@ def warning( stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... -def warn( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, -) -> None: ... + +if sys.version_info < (3, 13): + def warn( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... + def error( msg: object, *args: object, @@ -600,7 +627,7 @@ class StreamHandler(Handler, Generic[_StreamT]): def __init__(self: StreamHandler[_StreamT], stream: _StreamT) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] #11780 def setStream(self, stream: _StreamT) -> _StreamT | None: ... if sys.version_info >= (3, 11): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class FileHandler(StreamHandler[TextIOWrapper]): baseFilename: str # undocumented diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi index 1059bfe917e8..2f43f9552652 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -102,7 +102,7 @@ class Mailbox(Generic[_MessageT]): @abstractmethod def close(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Maildir(Mailbox[MaildirMessage]): colon: str @@ -244,7 +244,7 @@ class _ProxyFile(Generic[AnyStr]): @property def closed(self) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _PartialFile(_ProxyFile[AnyStr]): def __init__(self, f: IO[AnyStr], start: int | None = None, stop: int | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/marshal.pyi b/mypy/typeshed/stdlib/marshal.pyi index 69546344f5bf..6ab202637dda 100644 --- a/mypy/typeshed/stdlib/marshal.pyi +++ b/mypy/typeshed/stdlib/marshal.pyi @@ -1,4 +1,5 @@ import builtins +import sys import types from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite from typing import Any @@ -27,7 +28,14 @@ _Marshallable: TypeAlias = ( | ReadableBuffer ) -def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /) -> None: ... -def load(file: SupportsRead[bytes], /) -> Any: ... -def dumps(value: _Marshallable, version: int = 4, /) -> bytes: ... -def loads(bytes: ReadableBuffer, /) -> Any: ... +if sys.version_info >= (3, 13): + def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /, *, allow_code: bool = True) -> None: ... + def load(file: SupportsRead[bytes], /, *, allow_code: bool = True) -> Any: ... + def dumps(value: _Marshallable, version: int = 4, /, *, allow_code: bool = True) -> bytes: ... + def loads(bytes: ReadableBuffer, /, *, allow_code: bool = True) -> Any: ... + +else: + def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /) -> None: ... + def load(file: SupportsRead[bytes], /) -> Any: ... + def dumps(value: _Marshallable, version: int = 4, /) -> bytes: ... + def loads(bytes: ReadableBuffer, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 0c2fd4aba719..0e6565fcf588 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -123,3 +123,6 @@ def trunc(x: _SupportsTrunc[_T], /) -> _T: ... if sys.version_info >= (3, 9): def ulp(x: _SupportsFloatOrIndex, /) -> float: ... + +if sys.version_info >= (3, 13): + def fma(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, z: _SupportsFloatOrIndex) -> float: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 02b5c4bc8c67..9b2d2970112e 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -58,7 +58,7 @@ class ValueProxy(BaseProxy, Generic[_T]): def set(self, value: _T) -> None: ... value: _T if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): __builtins__: ClassVar[dict[str, Any]] diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 465c8e08c134..d2d611e3ca62 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -21,7 +21,7 @@ class ApplyResult(Generic[_T]): def ready(self) -> bool: ... def successful(self) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # alias created during issue #17805 AsyncResult = ApplyResult diff --git a/mypy/typeshed/stdlib/multiprocessing/queues.pyi b/mypy/typeshed/stdlib/multiprocessing/queues.pyi index 4cedd665552a..581a46ea0bc8 100644 --- a/mypy/typeshed/stdlib/multiprocessing/queues.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/queues.pyi @@ -38,4 +38,4 @@ class SimpleQueue(Generic[_T]): def get(self) -> _T: ... def put(self, obj: _T) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi index adbe8b943de6..0a6b113b194f 100644 --- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi @@ -37,4 +37,4 @@ class ShareableList(Generic[_SLT]): def count(self, value: _SLT) -> int: ... def index(self, value: _SLT) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi index 079366018bf5..ebe305ef708c 100644 --- a/mypy/typeshed/stdlib/ntpath.pyi +++ b/mypy/typeshed/stdlib/ntpath.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import BytesPath, StrPath +from _typeshed import BytesPath, StrOrBytesPath, StrPath from genericpath import ( commonprefix as commonprefix, exists as exists, @@ -47,6 +47,8 @@ from typing_extensions import LiteralString if sys.version_info >= (3, 12): from posixpath import isjunction as isjunction, splitroot as splitroot +if sys.version_info >= (3, 13): + from genericpath import isdevdrive as isdevdrive __all__ = [ "normcase", @@ -90,6 +92,8 @@ __all__ = [ ] if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] +if sys.version_info >= (3, 13): + __all__ += ["isdevdrive", "isreserved"] altsep: LiteralString @@ -117,3 +121,6 @@ if sys.platform == "win32": else: realpath = abspath + +if sys.version_info >= (3, 13): + def isreserved(path: StrOrBytesPath) -> bool: ... diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi index 14bdb7622142..f9f76962f876 100644 --- a/mypy/typeshed/stdlib/opcode.pyi +++ b/mypy/typeshed/stdlib/opcode.pyi @@ -20,6 +20,8 @@ if sys.version_info >= (3, 12): __all__ += ["hasarg", "hasexc"] else: __all__ += ["hasnargs"] +if sys.version_info >= (3, 13): + __all__ += ["hasjump"] if sys.version_info >= (3, 9): cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]] @@ -50,10 +52,12 @@ if sys.version_info >= (3, 12): hasexc: list[int] else: hasnargs: list[int] +if sys.version_info >= (3, 13): + hasjump: list[int] opname: list[str] opmap: dict[str, int] -HAVE_ARGUMENT: Literal[90] -EXTENDED_ARG: Literal[144] +HAVE_ARGUMENT: int +EXTENDED_ARG: int def stack_effect(opcode: int, oparg: int | None = None, /, *, jump: bool | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index 3474648617c2..a179c2d1bb3c 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -151,7 +151,7 @@ class OptionContainer: def _create_option_mappings(self) -> None: ... def _share_option_mappings(self, parser: OptionParser) -> None: ... @overload - def add_option(self, opt: Option) -> Option: ... + def add_option(self, opt: Option, /) -> Option: ... @overload def add_option(self, arg: str, /, *args: str | None, **kwargs) -> Option: ... def add_options(self, option_list: Iterable[Option]) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index e1c7855c0bb6..31c5d2aa3ee6 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -437,7 +437,7 @@ class DirEntry(Generic[AnyStr]): def stat(self, *, follow_symlinks: bool = True) -> stat_result: ... def __fspath__(self) -> AnyStr: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 12): def is_junction(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index 4cc708d9d5fe..487adddd04bf 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -55,7 +55,9 @@ class Pdb(Bdb, Cmd): ) -> None: ... def forget(self) -> None: ... def setup(self, f: FrameType | None, tb: TracebackType | None) -> None: ... - def execRcLines(self) -> None: ... + if sys.version_info < (3, 11): + def execRcLines(self) -> None: ... + def bp_commands(self, frame: FrameType) -> bool: ... def interaction(self, frame: FrameType | None, traceback: TracebackType | None) -> None: ... def displayhook(self, obj: object) -> None: ... diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index 1fc471ac7d0b..e5f5fa0d813c 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -14,6 +14,9 @@ from genericpath import ( sameopenfile as sameopenfile, samestat as samestat, ) + +if sys.version_info >= (3, 13): + from genericpath import isdevdrive as isdevdrive from os import PathLike from typing import AnyStr, overload from typing_extensions import LiteralString @@ -60,6 +63,8 @@ __all__ = [ ] if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] +if sys.version_info >= (3, 13): + __all__ += ["isdevdrive"] supports_unicode_filenames: bool # aliases (also in os) diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index 3134de79352d..1a90eb30efca 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import OptExcInfo, SupportsWrite +from _typeshed import OptExcInfo, SupportsWrite, Unused from abc import abstractmethod from builtins import list as _list # "list" conflicts with method name from collections.abc import Callable, Container, Mapping, MutableMapping @@ -121,7 +121,7 @@ class HTMLDoc(Doc): def formattree( self, tree: list[tuple[type, tuple[type, ...]] | list[Any]], modname: str, parent: type | None = None ) -> str: ... - def docmodule(self, object: object, name: str | None = None, mod: str | None = None, *ignored: Any) -> str: ... + def docmodule(self, object: object, name: str | None = None, mod: str | None = None, *ignored: Unused) -> str: ... def docclass( self, object: object, @@ -129,22 +129,44 @@ class HTMLDoc(Doc): mod: str | None = None, funcs: Mapping[str, str] = {}, classes: Mapping[str, str] = {}, - *ignored: Any, + *ignored: Unused, ) -> str: ... def formatvalue(self, object: object) -> str: ... - def docroutine( # type: ignore[override] - self, - object: object, - name: str | None = None, - mod: str | None = None, - funcs: Mapping[str, str] = {}, - classes: Mapping[str, str] = {}, - methods: Mapping[str, str] = {}, - cl: type | None = None, - ) -> str: ... - def docproperty(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] - def docother(self, object: object, name: str | None = None, mod: Any | None = None, *ignored: Any) -> str: ... - def docdata(self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + def docother(self, object: object, name: str | None = None, mod: Any | None = None, *ignored: Unused) -> str: ... + if sys.version_info >= (3, 11): + def docroutine( # type: ignore[override] + self, + object: object, + name: str | None = None, + mod: str | None = None, + funcs: Mapping[str, str] = {}, + classes: Mapping[str, str] = {}, + methods: Mapping[str, str] = {}, + cl: type | None = None, + homecls: type | None = None, + ) -> str: ... + def docproperty( + self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None, *ignored: Unused + ) -> str: ... + def docdata( + self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None, *ignored: Unused + ) -> str: ... + else: + def docroutine( # type: ignore[override] + self, + object: object, + name: str | None = None, + mod: str | None = None, + funcs: Mapping[str, str] = {}, + classes: Mapping[str, str] = {}, + methods: Mapping[str, str] = {}, + cl: type | None = None, + ) -> str: ... + def docproperty(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + def docdata(self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + if sys.version_info >= (3, 11): + def parentlink(self, object: type | ModuleType, modname: str) -> str: ... + def index(self, dir: str, shadowed: MutableMapping[str, bool] | None = None) -> str: ... def filelink(self, url: str, path: str) -> str: ... @@ -164,21 +186,48 @@ class TextDoc(Doc): def formattree( self, tree: list[tuple[type, tuple[type, ...]] | list[Any]], modname: str, parent: type | None = None, prefix: str = "" ) -> str: ... - def docmodule(self, object: object, name: str | None = None, mod: Any | None = None) -> str: ... # type: ignore[override] - def docclass(self, object: object, name: str | None = None, mod: str | None = None, *ignored: Any) -> str: ... + def docclass(self, object: object, name: str | None = None, mod: str | None = None, *ignored: Unused) -> str: ... def formatvalue(self, object: object) -> str: ... - def docroutine(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] - def docproperty(self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] - def docdata(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] - def docother( # type: ignore[override] - self, - object: object, - name: str | None = None, - mod: str | None = None, - parent: str | None = None, - maxlen: int | None = None, - doc: Any | None = None, - ) -> str: ... + if sys.version_info >= (3, 11): + def docroutine( # type: ignore[override] + self, + object: object, + name: str | None = None, + mod: str | None = None, + cl: Any | None = None, + homecls: Any | None = None, + ) -> str: ... + def docmodule(self, object: object, name: str | None = None, mod: Any | None = None, *ignored: Unused) -> str: ... + def docproperty( + self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None, *ignored: Unused + ) -> str: ... + def docdata( + self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None, *ignored: Unused + ) -> str: ... + def docother( + self, + object: object, + name: str | None = None, + mod: str | None = None, + parent: str | None = None, + *ignored: Unused, + maxlen: int | None = None, + doc: Any | None = None, + ) -> str: ... + else: + def docroutine(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + def docmodule(self, object: object, name: str | None = None, mod: Any | None = None) -> str: ... # type: ignore[override] + def docproperty(self, object: object, name: str | None = None, mod: Any | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + def docdata(self, object: object, name: str | None = None, mod: str | None = None, cl: Any | None = None) -> str: ... # type: ignore[override] + def docother( # type: ignore[override] + self, + object: object, + name: str | None = None, + mod: str | None = None, + parent: str | None = None, + maxlen: int | None = None, + doc: Any | None = None, + ) -> str: ... def pager(text: str) -> None: ... def getpager() -> Callable[[str], None]: ... diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi index d7cae5f2ac79..16643c99d08d 100644 --- a/mypy/typeshed/stdlib/queue.pyi +++ b/mypy/typeshed/stdlib/queue.pyi @@ -6,6 +6,8 @@ if sys.version_info >= (3, 9): from types import GenericAlias __all__ = ["Empty", "Full", "Queue", "PriorityQueue", "LifoQueue", "SimpleQueue"] +if sys.version_info >= (3, 13): + __all__ += ["ShutDown"] _T = TypeVar("_T") @@ -46,7 +48,7 @@ class Queue(Generic[_T]): def _qsize(self) -> int: ... def task_done(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class PriorityQueue(Queue[_T]): queue: list[_T] @@ -63,4 +65,4 @@ class SimpleQueue(Generic[_T]): def put_nowait(self, item: _T) -> None: ... def qsize(self) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/random.pyi b/mypy/typeshed/stdlib/random.pyi index 9fd1c64f2bba..e7320369c377 100644 --- a/mypy/typeshed/stdlib/random.pyi +++ b/mypy/typeshed/stdlib/random.pyi @@ -41,7 +41,10 @@ _T = TypeVar("_T") class Random(_random.Random): VERSION: ClassVar[int] - def __init__(self, x: Any = None) -> None: ... + if sys.version_info >= (3, 9): + def __init__(self, x: int | float | str | bytes | bytearray | None = None) -> None: ... # noqa: Y041 + else: + def __init__(self, x: Any = None) -> None: ... # Using other `seed` types is deprecated since 3.9 and removed in 3.11 # Ignore Y041, since random.seed doesn't treat int like a float subtype. Having an explicit # int better documents conventional usage of random.seed. diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 7945c5f46cdc..b06f494c0b7d 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -1,5 +1,6 @@ import enum import sre_compile +import sre_constants import sys from _typeshed import ReadableBuffer from collections.abc import Callable, Iterator, Mapping @@ -21,7 +22,6 @@ __all__ = [ "finditer", "compile", "purge", - "template", "escape", "error", "A", @@ -41,10 +41,17 @@ __all__ = [ "Match", "Pattern", ] +if sys.version_info < (3, 13): + __all__ += ["template"] if sys.version_info >= (3, 11): __all__ += ["NOFLAG", "RegexFlag"] +if sys.version_info >= (3, 13): + __all__ += ["PatternError"] + + PatternError = sre_constants.error + _T = TypeVar("_T") @final @@ -102,7 +109,7 @@ class Match(Generic[AnyStr]): def __copy__(self) -> Match[AnyStr]: ... def __deepcopy__(self, memo: Any, /) -> Match[AnyStr]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class Pattern(Generic[AnyStr]): @@ -178,7 +185,7 @@ class Pattern(Generic[AnyStr]): def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # ----- re variables and constants ----- @@ -198,10 +205,11 @@ class RegexFlag(enum.IntFlag): VERBOSE = X U = sre_compile.SRE_FLAG_UNICODE UNICODE = U - T = sre_compile.SRE_FLAG_TEMPLATE - TEMPLATE = T + if sys.version_info < (3, 13): + T = sre_compile.SRE_FLAG_TEMPLATE + TEMPLATE = T if sys.version_info >= (3, 11): - NOFLAG: int + NOFLAG = 0 A = RegexFlag.A ASCII = RegexFlag.ASCII @@ -218,8 +226,9 @@ X = RegexFlag.X VERBOSE = RegexFlag.VERBOSE U = RegexFlag.U UNICODE = RegexFlag.UNICODE -T = RegexFlag.T -TEMPLATE = RegexFlag.TEMPLATE +if sys.version_info < (3, 13): + T = RegexFlag.T + TEMPLATE = RegexFlag.TEMPLATE if sys.version_info >= (3, 11): NOFLAG = RegexFlag.NOFLAG _FlagsType: TypeAlias = int | RegexFlag @@ -287,4 +296,6 @@ def subn( ) -> tuple[bytes, int]: ... def escape(pattern: AnyStr) -> AnyStr: ... def purge() -> None: ... -def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ... + +if sys.version_info < (3, 13): + def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ... diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index a06181ce876d..f6c8a390d85f 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -1,6 +1,6 @@ import os import sys -from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite +from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite from collections.abc import Callable, Iterable, Sequence from tarfile import _TarfileFilter from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload @@ -71,14 +71,12 @@ def copytree( dirs_exist_ok: bool = False, ) -> _PathReturn: ... -_OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, Any], object] -_OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, Exception], object] +_OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, ExcInfo], object] +_OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object] class _RmtreeType(Protocol): avoids_symlink_attacks: bool if sys.version_info >= (3, 12): - @overload - def __call__(self, path: StrOrBytesPath, ignore_errors: bool = False, *, dir_fd: int | None = None) -> None: ... @overload @deprecated("The `onerror` parameter is deprecated and will be removed in Python 3.14. Use `onexc` instead.") def __call__( @@ -91,7 +89,12 @@ class _RmtreeType(Protocol): ) -> None: ... @overload def __call__( - self, path: StrOrBytesPath, ignore_errors: bool = False, *, onexc: _OnExcCallback, dir_fd: int | None = None + self, + path: StrOrBytesPath, + ignore_errors: bool = False, + *, + onexc: _OnExcCallback | None = None, + dir_fd: int | None = None, ) -> None: ... elif sys.version_info >= (3, 11): def __call__( @@ -132,14 +135,44 @@ def disk_usage(path: FileDescriptorOrPath) -> _ntuple_diskusage: ... # While chown can be imported on Windows, it doesn't actually work; # see https://bugs.python.org/issue33140. We keep it here because it's # in __all__. -@overload -def chown(path: FileDescriptorOrPath, user: str | int, group: None = None) -> None: ... -@overload -def chown(path: FileDescriptorOrPath, user: None = None, *, group: str | int) -> None: ... -@overload -def chown(path: FileDescriptorOrPath, user: None, group: str | int) -> None: ... -@overload -def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... +if sys.version_info >= (3, 13): + @overload + def chown( + path: FileDescriptorOrPath, + user: str | int, + group: None = None, + *, + dir_fd: int | None = None, + follow_symlinks: bool = True, + ) -> None: ... + @overload + def chown( + path: FileDescriptorOrPath, + user: None = None, + *, + group: str | int, + dir_fd: int | None = None, + follow_symlinks: bool = True, + ) -> None: ... + @overload + def chown( + path: FileDescriptorOrPath, user: None, group: str | int, *, dir_fd: int | None = None, follow_symlinks: bool = True + ) -> None: ... + @overload + def chown( + path: FileDescriptorOrPath, user: str | int, group: str | int, *, dir_fd: int | None = None, follow_symlinks: bool = True + ) -> None: ... + +else: + @overload + def chown(path: FileDescriptorOrPath, user: str | int, group: None = None) -> None: ... + @overload + def chown(path: FileDescriptorOrPath, user: None = None, *, group: str | int) -> None: ... + @overload + def chown(path: FileDescriptorOrPath, user: None, group: str | int) -> None: ... + @overload + def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... + @overload def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... @overload diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index cbb7440b9147..2e3ac5bf24c3 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -181,7 +181,7 @@ else: def strsignal(signalnum: _SIGNUM, /) -> str | None: ... def valid_signals() -> set[Signals]: ... def raise_signal(signalnum: _SIGNUM, /) -> None: ... -def set_wakeup_fd(fd: int, *, warn_on_full_buffer: bool = ...) -> int: ... +def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = ...) -> int: ... if sys.version_info >= (3, 9): if sys.platform == "linux": diff --git a/mypy/typeshed/stdlib/stat.pyi b/mypy/typeshed/stdlib/stat.pyi index 4518acb5a162..f3bdd92c1068 100644 --- a/mypy/typeshed/stdlib/stat.pyi +++ b/mypy/typeshed/stdlib/stat.pyi @@ -1 +1,7 @@ +import sys from _stat import * +from typing import Literal + +if sys.version_info >= (3, 13): + # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 + SF_RESTRICTED: Literal[0x00080000] diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index c5f5ed64b328..c8ecbbceab1a 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import SupportsRichComparisonT -from collections.abc import Hashable, Iterable, Sequence +from collections.abc import Callable, Hashable, Iterable, Sequence from decimal import Decimal from fractions import Fraction from typing import Any, Literal, NamedTuple, SupportsFloat, TypeVar @@ -28,6 +28,8 @@ __all__ = [ if sys.version_info >= (3, 10): __all__ += ["covariance", "correlation", "linear_regression"] +if sys.version_info >= (3, 13): + __all__ += ["kde", "kde_random"] # Most functions in this module accept homogeneous collections of one of these types _Number: TypeAlias = float | Decimal | Fraction @@ -130,3 +132,30 @@ if sys.version_info >= (3, 11): elif sys.version_info >= (3, 10): def linear_regression(regressor: Sequence[_Number], dependent_variable: Sequence[_Number], /) -> LinearRegression: ... + +if sys.version_info >= (3, 13): + _Kernel: TypeAlias = Literal[ + "normal", + "gauss", + "logistic", + "sigmoid", + "rectangular", + "uniform", + "triangular", + "parabolic", + "epanechnikov", + "quartic", + "biweight", + "triweight", + "cosine", + ] + def kde( + data: Sequence[float], h: float, kernel: _Kernel = "normal", *, cumulative: bool = False + ) -> Callable[[float], float]: ... + def kde_random( + data: Sequence[float], + h: float, + kernel: _Kernel = "normal", + *, + seed: int | float | str | bytes | bytearray | None = None, # noqa: Y041 + ) -> Callable[[], float]: ... diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index d3302aba5e10..6234ecc02b48 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -88,7 +88,7 @@ class CompletedProcess(Generic[_T]): def __init__(self, args: _CMD, returncode: int, stdout: _T | None = None, stderr: _T | None = None) -> None: ... def check_returncode(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 11): # 3.11 adds "process_group" argument @@ -2560,7 +2560,7 @@ class Popen(Generic[AnyStr]): ) -> None: ... def __del__(self) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # The result really is always a str. if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 5867c9a9d510..9989a27b2bc1 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import OptExcInfo, ProfileFunction, TraceFunction, structseq +from _typeshed import MaybeNone, OptExcInfo, ProfileFunction, TraceFunction, structseq from _typeshed.importlib import MetaPathFinderProtocol, PathEntryFinderProtocol from builtins import object as _object from collections.abc import AsyncGenerator, Callable, Sequence @@ -56,23 +56,24 @@ ps2: object # TextIO is used instead of more specific types for the standard streams, # since they are often monkeypatched at runtime. At startup, the objects -# are initialized to instances of TextIOWrapper. +# are initialized to instances of TextIOWrapper, but can also be None under +# some circumstances. # # To use methods from TextIOWrapper, use an isinstance check to ensure that # the streams have not been overridden: # # if isinstance(sys.stdout, io.TextIOWrapper): # sys.stdout.reconfigure(...) -stdin: TextIO -stdout: TextIO -stderr: TextIO +stdin: TextIO | MaybeNone +stdout: TextIO | MaybeNone +stderr: TextIO | MaybeNone if sys.version_info >= (3, 10): stdlib_module_names: frozenset[str] -__stdin__: Final[TextIOWrapper] # Contains the original value of stdin -__stdout__: Final[TextIOWrapper] # Contains the original value of stdout -__stderr__: Final[TextIOWrapper] # Contains the original value of stderr +__stdin__: Final[TextIOWrapper | None] # Contains the original value of stdin +__stdout__: Final[TextIOWrapper | None] # Contains the original value of stdout +__stderr__: Final[TextIOWrapper | None] # Contains the original value of stderr tracebacklimit: int version: str api_version: int @@ -264,9 +265,9 @@ def getrecursionlimit() -> int: ... def getsizeof(obj: object, default: int = ...) -> int: ... def getswitchinterval() -> float: ... def getprofile() -> ProfileFunction | None: ... -def setprofile(profilefunc: ProfileFunction | None) -> None: ... +def setprofile(function: ProfileFunction | None, /) -> None: ... def gettrace() -> TraceFunction | None: ... -def settrace(tracefunc: TraceFunction | None) -> None: ... +def settrace(function: TraceFunction | None, /) -> None: ... if sys.platform == "win32": # A tuple of length 5, even though it has more than 5 attributes. diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index 02876e0b7e85..d539dd5e4579 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -35,6 +35,15 @@ if sys.platform != "win32": LOG_USER: Literal[8] LOG_UUCP: Literal[64] LOG_WARNING: Literal[4] + + if sys.version_info >= (3, 13): + LOG_FTP: Literal[88] + LOG_INSTALL: Literal[112] + LOG_LAUNCHD: Literal[192] + LOG_NETINFO: Literal[96] + LOG_RAS: Literal[120] + LOG_REMOTEAUTH: Literal[104] + def LOG_MASK(pri: int, /) -> int: ... def LOG_UPTO(pri: int, /) -> int: ... def closelog() -> None: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index b66369926404..3ae8cca39f77 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -398,7 +398,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def writable(self) -> bool: ... def __next__(self) -> AnyStr: ... # type: ignore[override] if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class TemporaryDirectory(Generic[AnyStr]): name: AnyStr @@ -457,7 +457,7 @@ class TemporaryDirectory(Generic[AnyStr]): def __enter__(self) -> AnyStr: ... def __exit__(self, exc: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # The overloads overlap, but they should still work fine. @overload diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index 90b6cabb5237..1ecadef508d0 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -109,6 +109,9 @@ class Lock: def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... def release(self) -> None: ... def locked(self) -> bool: ... + def acquire_lock(self, blocking: bool = ..., timeout: float = ...) -> bool: ... # undocumented + def release_lock(self) -> None: ... # undocumented + def locked_lock(self) -> bool: ... # undocumented @final class _RLock: diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi index f1fec7698043..668987d7c2bf 100644 --- a/mypy/typeshed/stdlib/token.pyi +++ b/mypy/typeshed/stdlib/token.pyi @@ -3,10 +3,8 @@ import sys __all__ = [ "AMPER", "AMPEREQUAL", - "ASYNC", "AT", "ATEQUAL", - "AWAIT", "CIRCUMFLEX", "CIRCUMFLEXEQUAL", "COLON", @@ -71,6 +69,8 @@ __all__ = [ "NL", "COMMENT", ] +if sys.version_info < (3, 13): + __all__ += ["ASYNC", "AWAIT"] if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] @@ -131,8 +131,9 @@ AT: int RARROW: int ELLIPSIS: int ATEQUAL: int -AWAIT: int -ASYNC: int +if sys.version_info < (3, 13): + AWAIT: int + ASYNC: int OP: int ERRORTOKEN: int N_TOKENS: int diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 3cd9ab8f87ce..3d2a93865df8 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -10,10 +10,8 @@ from typing_extensions import TypeAlias __all__ = [ "AMPER", "AMPEREQUAL", - "ASYNC", "AT", "ATEQUAL", - "AWAIT", "CIRCUMFLEX", "CIRCUMFLEXEQUAL", "COLON", @@ -83,6 +81,8 @@ __all__ = [ "tokenize", "untokenize", ] +if sys.version_info < (3, 13): + __all__ += ["ASYNC", "AWAIT"] if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] @@ -90,6 +90,9 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 12): __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] +if sys.version_info >= (3, 13): + __all__ += ["TokenError", "open"] + cookie_re: Pattern[str] blank_re: Pattern[bytes] @@ -110,7 +113,9 @@ class TokenInfo(_TokenInfo): _Token: TypeAlias = TokenInfo | Sequence[int | str | _Position] class TokenError(Exception): ... -class StopTokenizing(Exception): ... # undocumented + +if sys.version_info < (3, 13): + class StopTokenizing(Exception): ... # undocumented class Untokenizer: tokens: list[str] @@ -120,6 +125,8 @@ class Untokenizer: def add_whitespace(self, start: _Position) -> None: ... def untokenize(self, iterable: Iterable[_Token]) -> str: ... def compat(self, token: Sequence[int | str], iterable: Iterable[_Token]) -> None: ... + if sys.version_info >= (3, 12): + def escape_brackets(self, token: str) -> str: ... # the docstring says "returns bytes" but is incorrect -- # if the ENCODING token is missing, it skips the encode diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 38940b4345c8..93cb89046366 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -58,6 +58,9 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 12): __all__ += ["get_original_bases"] +if sys.version_info >= (3, 13): + __all__ += ["CapsuleType"] + # Note, all classes "defined" here require special handling. _T1 = TypeVar("_T1") @@ -299,7 +302,7 @@ class MappingProxyType(Mapping[_KT, _VT_co]): def values(self) -> ValuesView[_VT_co]: ... def items(self) -> ItemsView[_KT, _VT_co]: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT]: ... def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... @@ -607,3 +610,7 @@ if sys.version_info >= (3, 10): def __ror__(self, value: Any, /) -> UnionType: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... + +if sys.version_info >= (3, 13): + @final + class CapsuleType: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index d047f1c87621..1b021d1eecbd 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -128,6 +128,9 @@ if sys.version_info >= (3, 11): if sys.version_info >= (3, 12): __all__ += ["TypeAliasType", "override"] +if sys.version_info >= (3, 13): + __all__ += ["get_protocol_members", "is_protocol", "NoDefault"] + Any = object() def final(f: _T) -> _T: ... @@ -146,6 +149,21 @@ class TypeVar: if sys.version_info >= (3, 12): @property def __infer_variance__(self) -> bool: ... + if sys.version_info >= (3, 13): + @property + def __default__(self) -> Any: ... + if sys.version_info >= (3, 13): + def __init__( + self, + name: str, + *constraints: Any, + bound: Any | None = None, + contravariant: bool = False, + covariant: bool = False, + infer_variance: bool = False, + default: Any = ..., + ) -> None: ... + elif sys.version_info >= (3, 12): def __init__( self, name: str, @@ -164,6 +182,8 @@ class TypeVar: def __ror__(self, left: Any) -> _SpecialForm: ... if sys.version_info >= (3, 11): def __typing_subst__(self, arg: Any) -> Any: ... + if sys.version_info >= (3, 13): + def has_default(self) -> bool: ... # Used for an undocumented mypy feature. Does not exist at runtime. _promote = object() @@ -205,7 +225,15 @@ if sys.version_info >= (3, 11): class TypeVarTuple: @property def __name__(self) -> str: ... - def __init__(self, name: str) -> None: ... + if sys.version_info >= (3, 13): + @property + def __default__(self) -> Any: ... + def has_default(self) -> bool: ... + if sys.version_info >= (3, 13): + def __init__(self, name: str, *, default: Any = ...) -> None: ... + else: + def __init__(self, name: str) -> None: ... + def __iter__(self) -> Any: ... def __typing_subst__(self, arg: Never) -> Never: ... def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... @@ -238,6 +266,21 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 12): @property def __infer_variance__(self) -> bool: ... + if sys.version_info >= (3, 13): + @property + def __default__(self) -> Any: ... + if sys.version_info >= (3, 13): + def __init__( + self, + name: str, + *, + bound: Any | None = None, + contravariant: bool = False, + covariant: bool = False, + infer_variance: bool = False, + default: Any = ..., + ) -> None: ... + elif sys.version_info >= (3, 12): def __init__( self, name: str, @@ -262,6 +305,8 @@ if sys.version_info >= (3, 10): def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... + if sys.version_info >= (3, 13): + def has_default(self) -> bool: ... Concatenate: _SpecialForm TypeAlias: _SpecialForm @@ -890,6 +935,8 @@ class NamedTuple(tuple[Any, ...]): def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ... def _asdict(self) -> dict[str, Any]: ... def _replace(self, **kwargs: Any) -> typing_extensions.Self: ... + if sys.version_info >= (3, 13): + def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ... # Internal mypy fallback type for all typed dicts (does not exist at runtime) # N.B. Keep this mostly in sync with typing_extensions._TypedDict/mypy_extensions._TypedDict @@ -985,3 +1032,7 @@ if sys.version_info >= (3, 12): if sys.version_info >= (3, 13): def is_protocol(tp: type, /) -> bool: ... def get_protocol_members(tp: type, /) -> frozenset[str]: ... + @final + class _NoDefaultType: ... + + NoDefault: _NoDefaultType diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 48a398ba4095..73fd2dc8cbb3 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -3,13 +3,13 @@ import sys import typing from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import IdentityFunction +from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager from typing import ( # noqa: Y022,Y037,Y038,Y039 IO as IO, TYPE_CHECKING as TYPE_CHECKING, AbstractSet as AbstractSet, Any as Any, AnyStr as AnyStr, - AsyncContextManager as AsyncContextManager, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, @@ -20,7 +20,6 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 ClassVar as ClassVar, Collection as Collection, Container as Container, - ContextManager as ContextManager, Coroutine as Coroutine, Counter as Counter, DefaultDict as DefaultDict, @@ -95,6 +94,7 @@ __all__ = [ "Coroutine", "AsyncGenerator", "AsyncContextManager", + "CapsuleType", "ChainMap", "ContextManager", "Counter", @@ -166,6 +166,7 @@ __all__ = [ "MutableMapping", "MutableSequence", "MutableSet", + "NoDefault", "Optional", "Pattern", "Reversible", @@ -379,86 +380,6 @@ else: def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... -# New things in 3.xx -# The `default` parameter was added to TypeVar, ParamSpec, and TypeVarTuple (PEP 696) -# The `infer_variance` parameter was added to TypeVar in 3.12 (PEP 695) -# typing_extensions.override (PEP 698) -@final -class TypeVar: - @property - def __name__(self) -> str: ... - @property - def __bound__(self) -> Any | None: ... - @property - def __constraints__(self) -> tuple[Any, ...]: ... - @property - def __covariant__(self) -> bool: ... - @property - def __contravariant__(self) -> bool: ... - @property - def __infer_variance__(self) -> bool: ... - @property - def __default__(self) -> Any | None: ... - def __init__( - self, - name: str, - *constraints: Any, - bound: Any | None = None, - covariant: bool = False, - contravariant: bool = False, - default: Any | None = None, - infer_variance: bool = False, - ) -> None: ... - if sys.version_info >= (3, 10): - def __or__(self, right: Any) -> _SpecialForm: ... - def __ror__(self, left: Any) -> _SpecialForm: ... - if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Any) -> Any: ... - -@final -class ParamSpec: - @property - def __name__(self) -> str: ... - @property - def __bound__(self) -> Any | None: ... - @property - def __covariant__(self) -> bool: ... - @property - def __contravariant__(self) -> bool: ... - @property - def __infer_variance__(self) -> bool: ... - @property - def __default__(self) -> Any | None: ... - def __init__( - self, - name: str, - *, - bound: None | type[Any] | str = None, - contravariant: bool = False, - covariant: bool = False, - default: type[Any] | str | None = None, - ) -> None: ... - @property - def args(self) -> ParamSpecArgs: ... - @property - def kwargs(self) -> ParamSpecKwargs: ... - -@final -class TypeVarTuple: - @property - def __name__(self) -> str: ... - @property - def __default__(self) -> Any | None: ... - def __init__(self, name: str, *, default: Any | None = None) -> None: ... - def __iter__(self) -> Any: ... # Unpack[Self] - -class deprecated: - message: LiteralString - category: type[Warning] | None - stacklevel: int - def __init__(self, message: LiteralString, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... - def __call__(self, arg: _T, /) -> _T: ... - if sys.version_info >= (3, 12): from collections.abc import Buffer as Buffer from types import get_original_bases as get_original_bases @@ -494,10 +415,110 @@ else: def __buffer__(self, flags: int, /) -> memoryview: ... if sys.version_info >= (3, 13): - from typing import get_protocol_members as get_protocol_members, is_protocol as is_protocol + from types import CapsuleType as CapsuleType + from typing import ( + NoDefault as NoDefault, + ParamSpec as ParamSpec, + TypeVar as TypeVar, + TypeVarTuple as TypeVarTuple, + get_protocol_members as get_protocol_members, + is_protocol as is_protocol, + ) + from warnings import deprecated as deprecated else: def is_protocol(tp: type, /) -> bool: ... def get_protocol_members(tp: type, /) -> frozenset[str]: ... + @final + class _NoDefaultType: ... + + NoDefault: _NoDefaultType + @final + class CapsuleType: ... + + class deprecated: + message: LiteralString + category: type[Warning] | None + stacklevel: int + def __init__(self, message: LiteralString, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... + def __call__(self, arg: _T, /) -> _T: ... + + @final + class TypeVar: + @property + def __name__(self) -> str: ... + @property + def __bound__(self) -> Any | None: ... + @property + def __constraints__(self) -> tuple[Any, ...]: ... + @property + def __covariant__(self) -> bool: ... + @property + def __contravariant__(self) -> bool: ... + @property + def __infer_variance__(self) -> bool: ... + @property + def __default__(self) -> Any: ... + def __init__( + self, + name: str, + *constraints: Any, + bound: Any | None = None, + covariant: bool = False, + contravariant: bool = False, + default: Any = ..., + infer_variance: bool = False, + ) -> None: ... + def has_default(self) -> bool: ... + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + if sys.version_info >= (3, 10): + def __or__(self, right: Any) -> _SpecialForm: ... + def __ror__(self, left: Any) -> _SpecialForm: ... + if sys.version_info >= (3, 11): + def __typing_subst__(self, arg: Any) -> Any: ... + + @final + class ParamSpec: + @property + def __name__(self) -> str: ... + @property + def __bound__(self) -> Any | None: ... + @property + def __covariant__(self) -> bool: ... + @property + def __contravariant__(self) -> bool: ... + @property + def __infer_variance__(self) -> bool: ... + @property + def __default__(self) -> Any: ... + def __init__( + self, + name: str, + *, + bound: None | type[Any] | str = None, + contravariant: bool = False, + covariant: bool = False, + default: Any = ..., + ) -> None: ... + @property + def args(self) -> ParamSpecArgs: ... + @property + def kwargs(self) -> ParamSpecKwargs: ... + def has_default(self) -> bool: ... + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + if sys.version_info >= (3, 10): + def __or__(self, right: Any) -> _SpecialForm: ... + def __ror__(self, left: Any) -> _SpecialForm: ... + + @final + class TypeVarTuple: + @property + def __name__(self) -> str: ... + @property + def __default__(self) -> Any: ... + def __init__(self, name: str, *, default: Any = ...) -> None: ... + def __iter__(self) -> Any: ... # Unpack[Self] + def has_default(self) -> bool: ... + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... class Doc: documentation: str diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index bd1c064f0270..b63292604ecc 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -329,7 +329,7 @@ class _AssertRaisesContext(_AssertRaisesBaseContext, Generic[_E]): self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None ) -> bool: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _AssertWarnsContext(_AssertRaisesBaseContext): warning: WarningMessage diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index ed1929b26501..89a50995d553 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -56,7 +56,7 @@ class _NetlocResultMixinBase(Generic[AnyStr]): @property def port(self) -> int | None: ... if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): ... class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): ... diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 3442be8b8ea4..2a6476f9e6d8 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -52,16 +52,23 @@ _T = TypeVar("_T") _UrlopenRet: TypeAlias = Any _DataType: TypeAlias = ReadableBuffer | SupportsRead[bytes] | Iterable[bytes] | None -def urlopen( - url: str | Request, - data: _DataType | None = None, - timeout: float | None = ..., - *, - cafile: str | None = None, - capath: str | None = None, - cadefault: bool = False, - context: ssl.SSLContext | None = None, -) -> _UrlopenRet: ... +if sys.version_info >= (3, 13): + def urlopen( + url: str | Request, data: _DataType | None = None, timeout: float | None = ..., *, context: ssl.SSLContext | None = None + ) -> _UrlopenRet: ... + +else: + def urlopen( + url: str | Request, + data: _DataType | None = None, + timeout: float | None = ..., + *, + cafile: str | None = None, + capath: str | None = None, + cadefault: bool = False, + context: ssl.SSLContext | None = None, + ) -> _UrlopenRet: ... + def install_opener(opener: OpenerDirector) -> None: ... def build_opener(*handlers: BaseHandler | Callable[[], BaseHandler]) -> OpenerDirector: ... diff --git a/mypy/typeshed/stdlib/venv/__init__.pyi b/mypy/typeshed/stdlib/venv/__init__.pyi index f184649f10f0..0490c35b44f2 100644 --- a/mypy/typeshed/stdlib/venv/__init__.pyi +++ b/mypy/typeshed/stdlib/venv/__init__.pyi @@ -1,7 +1,7 @@ import logging import sys from _typeshed import StrOrBytesPath -from collections.abc import Sequence +from collections.abc import Iterable, Sequence from types import SimpleNamespace logger: logging.Logger @@ -17,7 +17,20 @@ class EnvBuilder: with_pip: bool prompt: str | None - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 13): + def __init__( + self, + system_site_packages: bool = False, + clear: bool = False, + symlinks: bool = False, + upgrade: bool = False, + with_pip: bool = False, + prompt: str | None = None, + upgrade_deps: bool = False, + *, + scm_ignore_files: Iterable[str] = ..., + ) -> None: ... + elif sys.version_info >= (3, 9): def __init__( self, system_site_packages: bool = False, @@ -54,8 +67,23 @@ class EnvBuilder: def install_scripts(self, context: SimpleNamespace, path: str) -> None: ... if sys.version_info >= (3, 9): def upgrade_dependencies(self, context: SimpleNamespace) -> None: ... + if sys.version_info >= (3, 13): + def create_git_ignore_file(self, context: SimpleNamespace) -> None: ... -if sys.version_info >= (3, 9): +if sys.version_info >= (3, 13): + def create( + env_dir: StrOrBytesPath, + system_site_packages: bool = False, + clear: bool = False, + symlinks: bool = False, + with_pip: bool = False, + prompt: str | None = None, + upgrade_deps: bool = False, + *, + scm_ignore_files: Iterable[str] = ..., + ) -> None: ... + +elif sys.version_info >= (3, 9): def create( env_dir: StrOrBytesPath, system_site_packages: bool = False, diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index 12afea9337e7..539a8f2379c1 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -3,7 +3,7 @@ from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence from types import ModuleType, TracebackType from typing import Any, Generic, Literal, TextIO, TypeVar, overload -from typing_extensions import TypeAlias +from typing_extensions import LiteralString, TypeAlias __all__ = [ "warn", @@ -16,6 +16,10 @@ __all__ = [ "catch_warnings", ] +if sys.version_info >= (3, 13): + __all__ += ["deprecated"] + +_T = TypeVar("_T") _W = TypeVar("_W", bound=list[WarningMessage] | None) _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] @@ -110,3 +114,11 @@ class catch_warnings(Generic[_W]): def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... + +if sys.version_info >= (3, 13): + class deprecated: + message: LiteralString + category: type[Warning] | None + stacklevel: int + def __init__(self, message: LiteralString, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ... + def __call__(self, arg: _T, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/wsgiref/util.pyi b/mypy/typeshed/stdlib/wsgiref/util.pyi index 962fac2c5a22..3966e17b0d28 100644 --- a/mypy/typeshed/stdlib/wsgiref/util.pyi +++ b/mypy/typeshed/stdlib/wsgiref/util.pyi @@ -4,6 +4,8 @@ from collections.abc import Callable from typing import IO, Any __all__ = ["FileWrapper", "guess_scheme", "application_uri", "request_uri", "shift_path_info", "setup_testing_defaults"] +if sys.version_info >= (3, 13): + __all__ += ["is_hop_by_hop"] class FileWrapper: filelike: IO[bytes] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index acb0ff88ad04..0cf6d6b5aa38 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -277,7 +277,7 @@ bin(sys.stdout) _program.py:5: error: No overload variant of "write" of "IO" matches argument type "bytes" _program.py:5: note: Possible overload variants: _program.py:5: note: def write(self, str, /) -> int -_program.py:10: error: Argument 1 to "bin" has incompatible type "TextIO"; expected "IO[bytes]" +_program.py:10: error: Argument 1 to "bin" has incompatible type "Union[TextIO, Any]"; expected "IO[bytes]" [case testBuiltinOpen] f = open('x') From 3ddc0094dcfe5523832b7ddcf87fb67b7b61d550 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 24 May 2024 17:19:33 -0700 Subject: [PATCH 0599/1617] Support unions in functools.partial (#17284) Co-authored-by: cdce8p --- mypy/checker.py | 17 ++++++++++++++++- mypy/join.py | 4 ++-- test-data/unit/check-functools.test | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6da537fad5cb..b8a1e9813071 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -26,7 +26,7 @@ from typing_extensions import TypeAlias as _TypeAlias import mypy.checkexpr -from mypy import errorcodes as codes, message_registry, nodes, operators +from mypy import errorcodes as codes, join, message_registry, nodes, operators from mypy.binder import ConditionalTypeBinder, Frame, get_declaration from mypy.checkmember import ( MemberContext, @@ -699,6 +699,21 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab ) if isinstance(inner_call, CallableType): outer_type = inner_call + elif isinstance(inner_type, UnionType): + union_type = make_simplified_union(inner_type.items) + if isinstance(union_type, UnionType): + items = [] + for item in union_type.items: + callable_item = self.extract_callable_type(item, ctx) + if callable_item is None: + break + items.append(callable_item) + else: + joined_type = get_proper_type(join.join_type_list(items)) + if isinstance(joined_type, CallableType): + outer_type = joined_type + else: + return self.extract_callable_type(union_type, ctx) if outer_type is None: self.msg.not_callable(inner_type, ctx) return outer_type diff --git a/mypy/join.py b/mypy/join.py index 7e0ff301ebf8..782b4fbebd7b 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import overload +from typing import Sequence, overload import mypy.typeops from mypy.maptype import map_instance_to_supertype @@ -853,7 +853,7 @@ def object_or_any_from_type(typ: ProperType) -> ProperType: return AnyType(TypeOfAny.implementation_artifact) -def join_type_list(types: list[Type]) -> Type: +def join_type_list(types: Sequence[Type]) -> Type: if not types: # This is a little arbitrary but reasonable. Any empty tuple should be compatible # with all variable length tuples, and this makes it possible. diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 5af5dfc8e469..30ab36abef01 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -324,3 +324,25 @@ p(bar, 1, "a", 3.0) # OK p(bar, 1, "a", 3.0, kwarg="asdf") # OK p(bar, 1, "a", "b") # E: Argument 1 to "foo" has incompatible type "Callable[[int, str, float], None]"; expected "Callable[[int, str, str], None]" [builtins fixtures/dict.pyi] + +[case testFunctoolsPartialUnion] +import functools +from typing import Any, Callable, Union + +cls1: Any +cls2: Union[Any, Any] +reveal_type(functools.partial(cls1, 2)()) # N: Revealed type is "Any" +reveal_type(functools.partial(cls2, 2)()) # N: Revealed type is "Any" + +fn1: Union[Callable[[int], int], Callable[[int], int]] +reveal_type(functools.partial(fn1, 2)()) # N: Revealed type is "builtins.int" + +fn2: Union[Callable[[int], int], Callable[[int], str]] +reveal_type(functools.partial(fn2, 2)()) # N: Revealed type is "builtins.object" + +fn3: Union[Callable[[int], int], str] +reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \ + # E: "Union[Callable[[int], int], str]" not callable \ + # N: Revealed type is "builtins.int" \ + # E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]" +[builtins fixtures/tuple.pyi] From 66b48cbe97bf9c7660525766afe6d7089a984769 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 24 May 2024 23:02:45 -0700 Subject: [PATCH 0600/1617] Fix stubgen for Python 3.13 (#17290) __firstlineno__ and __static_attributes__ are new in 3.13. __annotate__ will be new in 3.14, so we might as well add it now. I tried to run the test suite on 3.13. There are a ton of compilation failures from mypyc, and a number of stubgen failures that this PR will fix. --- mypy/stubgenc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 29b2636d39cc..7e3ef49c6e9a 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -466,6 +466,9 @@ def is_skipped_attribute(self, attr: str) -> bool: "__module__", "__weakref__", "__annotations__", + "__firstlineno__", + "__static_attributes__", + "__annotate__", ) or attr in self.IGNORED_DUNDERS or is_pybind_skipped_attribute(attr) # For pickling From fa2aefc3f50479a0d9ef3295a90913435b5b4ad2 Mon Sep 17 00:00:00 2001 From: Max Murin Date: Sat, 25 May 2024 06:14:57 -0700 Subject: [PATCH 0601/1617] Fix for bug with descriptors in non-strict-optional (#17293) Fixes #17289. --- mypy/checkmember.py | 2 +- test-data/unit/check-unions.test | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 5824b00a37f6..fa847de2e4a0 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -654,7 +654,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: analyze_descriptor_access( descriptor_type, mx.copy_modified(original_type=original_type) ) - for original_type in instance_type.items + for original_type in instance_type.relevant_items() ] ) elif not isinstance(descriptor_type, Instance): diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 2e69a96f0c78..2ca2f1ba9eb3 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1258,3 +1258,34 @@ reveal_type(mix) # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B reveal_type(mix.field_1) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(mix().field_1) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] + + +[case testDescriptorAccessForUnionOfTypesWithNoStrictOptional] +# mypy: no-strict-optional +from typing import overload, Generic, Any, TypeVar, List, Optional, Union, Type + +class Descriptor: + @overload + def __get__( + self, instance: None, owner: type + ) -> str: + ... + + @overload + def __get__(self, instance: object, owner: type) -> int: + ... + + def __get__( + self, instance: Optional[object], owner: type + ) -> Union[str, int]: + ... + +class A: + field = Descriptor() + +a_class_or_none: Optional[Type[A]] +x: str = a_class_or_none.field + +a_or_none: Optional[A] +y: int = a_or_none.field +[builtins fixtures/list.pyi] From 9315d629920c8e2ab09f789e362c9d5b7f84a871 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 25 May 2024 16:38:14 -0700 Subject: [PATCH 0602/1617] Support type objects in functools.partial (#17292) --- mypy/checker.py | 5 +++++ test-data/unit/check-functools.test | 32 ++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b8a1e9813071..72bbc3d284ef 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -681,6 +681,11 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab inner_type = get_proper_type(inner_type) outer_type: CallableType | None = None if inner_type is not None and not isinstance(inner_type, AnyType): + if isinstance(inner_type, TypeType): + if isinstance(inner_type.item, Instance): + inner_type = expand_type_by_instance( + type_object_type(inner_type.item.type, self.named_type), inner_type.item + ) if isinstance(inner_type, CallableType): outer_type = inner_type elif isinstance(inner_type, Instance): diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 30ab36abef01..38083ad98f21 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -303,12 +303,12 @@ p(1) # E: Argument 1 to "A" has incompatible type "int"; expected "str" p(z=1) # E: Unexpected keyword argument "z" for "A" def main(t: Type[A]) -> None: - p = functools.partial(t, 1) # E: "Type[A]" not callable + p = functools.partial(t, 1) reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]" p("a") # OK - p(1) # False negative - p(z=1) # False negative + p(1) # E: Argument 1 to "A" has incompatible type "int"; expected "str" + p(z=1) # E: Unexpected keyword argument "z" for "A" [builtins fixtures/dict.pyi] @@ -346,3 +346,29 @@ reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \ # N: Revealed type is "builtins.int" \ # E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]" [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeObject] +import functools +from typing import Type, Generic, TypeVar + +class A: + def __init__(self, val: int) -> None: ... + +cls1: Type[A] +reveal_type(functools.partial(cls1, 2)()) # N: Revealed type is "__main__.A" +functools.partial(cls1, "asdf") # E: Argument 1 to "A" has incompatible type "str"; expected "int" + +T = TypeVar("T") +class B(Generic[T]): + def __init__(self, val: T) -> None: ... + +cls2: Type[B[int]] +reveal_type(functools.partial(cls2, 2)()) # N: Revealed type is "__main__.B[builtins.int]" +functools.partial(cls2, "asdf") # E: Argument 1 to "B" has incompatible type "str"; expected "int" + +def foo(cls3: Type[B[T]]): + reveal_type(functools.partial(cls3, "asdf")) # N: Revealed type is "functools.partial[__main__.B[T`-1]]" \ + # E: Argument 1 to "B" has incompatible type "str"; expected "T" + reveal_type(functools.partial(cls3, 2)()) # N: Revealed type is "__main__.B[T`-1]" \ + # E: Argument 1 to "B" has incompatible type "int"; expected "T" +[builtins fixtures/tuple.pyi] From 5059ffdd5df4702ae5b690a6dfd5f1a70c7964e1 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Sat, 25 May 2024 22:19:51 -0700 Subject: [PATCH 0603/1617] =?UTF-8?q?Don=E2=80=99t=20leak=20unreachability?= =?UTF-8?q?=20from=20lambda=20body=20to=20surrounding=20scope=20(#17287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #17254 Signed-off-by: Anders Kaseorg --- mypy/checkexpr.py | 10 ++++++---- test-data/unit/check-unreachable-code.test | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4b0f5fe533d8..479ef228b038 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5223,15 +5223,16 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: self.chk.return_types.append(AnyType(TypeOfAny.special_form)) # Type check everything in the body except for the final return # statement (it can contain tuple unpacking before return). - with self.chk.scope.push_function(e): + with self.chk.binder.frame_context( + can_skip=True, fall_through=0 + ), self.chk.scope.push_function(e): # Lambdas can have more than one element in body, # when we add "fictional" AssigmentStatement nodes, like in: # `lambda (a, b): a` for stmt in e.body.body[:-1]: stmt.accept(self.chk) # Only type check the return expression, not the return statement. - # This is important as otherwise the following statements would be - # considered unreachable. There's no useful type context. + # There's no useful type context. ret_type = self.accept(e.expr(), allow_none_return=True) fallback = self.named_type("builtins.function") self.chk.return_types.pop() @@ -5243,7 +5244,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: self.chk.check_func_item(e, type_override=type_override) if not self.chk.has_type(e.expr()): # TODO: return expression must be accepted before exiting function scope. - self.accept(e.expr(), allow_none_return=True) + with self.chk.binder.frame_context(can_skip=True, fall_through=0): + self.accept(e.expr(), allow_none_return=True) ret_type = self.chk.lookup_type(e.expr()) self.chk.return_types.pop() return replace_callable_return_type(inferred_type, ret_type) diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index b8b438b979c6..81777f4c0e2b 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1494,3 +1494,23 @@ from typing import Generator def f() -> Generator[None, None, None]: return None yield None + +[case testLambdaNoReturn] +# flags: --warn-unreachable +from typing import Callable, NoReturn + +def foo() -> NoReturn: + raise + +f = lambda: foo() +x = 0 # not unreachable + +[case testLambdaNoReturnAnnotated] +# flags: --warn-unreachable +from typing import Callable, NoReturn + +def foo() -> NoReturn: + raise + +f: Callable[[], NoReturn] = lambda: foo() # E: Return statement in function which does not return # (false positive: https://github.com/python/mypy/issues/17254) +x = 0 # not unreachable From f60f458bf0e75e93a7b23a6ae31afd18f3d201e3 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 27 May 2024 18:07:49 -0700 Subject: [PATCH 0604/1617] Avoid does not return error in lambda (#17294) Fixes #10520, fixes #15142 --- mypy/checker.py | 2 +- test-data/unit/check-unreachable-code.test | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 72bbc3d284ef..179ff6e0b4b6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4494,7 +4494,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: is_lambda = isinstance(self.scope.top_function(), LambdaExpr) if isinstance(return_type, UninhabitedType): # Avoid extra error messages for failed inference in lambdas - if not is_lambda or not return_type.ambiguous: + if not is_lambda and not return_type.ambiguous: self.fail(message_registry.NO_RETURN_EXPECTED, s) return diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 81777f4c0e2b..cbad1bd5449e 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1502,15 +1502,8 @@ from typing import Callable, NoReturn def foo() -> NoReturn: raise -f = lambda: foo() +f1 = lambda: foo() x = 0 # not unreachable -[case testLambdaNoReturnAnnotated] -# flags: --warn-unreachable -from typing import Callable, NoReturn - -def foo() -> NoReturn: - raise - -f: Callable[[], NoReturn] = lambda: foo() # E: Return statement in function which does not return # (false positive: https://github.com/python/mypy/issues/17254) +f2: Callable[[], NoReturn] = lambda: foo() x = 0 # not unreachable From 7032f8c729a1c06a2521978750b62f2dd4d261d9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 May 2024 15:22:56 +0100 Subject: [PATCH 0605/1617] [PEP 695] Detect errors related to mixing old and new style features (#17269) `Generic[...]` or `Protocol[...]` shouldn't be used with new-style syntax. Generic functions and classes using the new syntax shouldn't mix new-style and old-style type parameters. Work on #15238. --- mypy/messages.py | 4 +++ mypy/semanal.py | 40 ++++++++++++++++++++++------- test-data/unit/check-python312.test | 40 +++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 199b7c42b11b..8f923462c789 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2421,6 +2421,10 @@ def annotation_in_unchecked_function(self, context: Context) -> None: code=codes.ANNOTATION_UNCHECKED, ) + def type_parameters_should_be_declared(self, undeclared: list[str], context: Context) -> None: + names = ", ".join('"' + n + '"' for n in undeclared) + self.fail(f"All type parameters should be declared ({names} not declared)", context) + def quote_type_string(type_string: str) -> str: """Quotes a type representation for use in messages.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 61c4eb737fb9..320ae72d99f9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1119,6 +1119,14 @@ def update_function_type_variables(self, fun_type: CallableType, defn: FuncItem) fun_type.variables, has_self_type = a.bind_function_type_variables(fun_type, defn) if has_self_type and self.type is not None: self.setup_self_type() + if defn.type_args: + bound_fullnames = {v.fullname for v in fun_type.variables} + declared_fullnames = {self.qualified_name(p.name) for p in defn.type_args} + extra = sorted(bound_fullnames - declared_fullnames) + if extra: + self.msg.type_parameters_should_be_declared( + [n.split(".")[-1] for n in extra], defn + ) return has_self_type def setup_self_type(self) -> None: @@ -2076,11 +2084,19 @@ class Foo(Bar, Generic[T]): ... continue result = self.analyze_class_typevar_declaration(base) if result is not None: - if declared_tvars: - self.fail("Only single Generic[...] or Protocol[...] can be in bases", context) - removed.append(i) tvars = result[0] is_protocol |= result[1] + if declared_tvars: + if defn.type_args: + if is_protocol: + self.fail('No arguments expected for "Protocol" base class', context) + else: + self.fail("Generic[...] base class is redundant", context) + else: + self.fail( + "Only single Generic[...] or Protocol[...] can be in bases", context + ) + removed.append(i) declared_tvars.extend(tvars) if isinstance(base, UnboundType): sym = self.lookup_qualified(base.name, base) @@ -2092,15 +2108,21 @@ class Foo(Bar, Generic[T]): ... all_tvars = self.get_all_bases_tvars(base_type_exprs, removed) if declared_tvars: - if len(remove_dups(declared_tvars)) < len(declared_tvars): + if len(remove_dups(declared_tvars)) < len(declared_tvars) and not defn.type_args: self.fail("Duplicate type variables in Generic[...] or Protocol[...]", context) declared_tvars = remove_dups(declared_tvars) if not set(all_tvars).issubset(set(declared_tvars)): - self.fail( - "If Generic[...] or Protocol[...] is present" - " it should list all type variables", - context, - ) + if defn.type_args: + undeclared = sorted(set(all_tvars) - set(declared_tvars)) + self.msg.type_parameters_should_be_declared( + [tv[0] for tv in undeclared], context + ) + else: + self.fail( + "If Generic[...] or Protocol[...] is present" + " it should list all type variables", + context, + ) # In case of error, Generic tvars will go first declared_tvars = remove_dups(declared_tvars + all_tvars) else: diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index cce22634df6d..f5d9fd195f04 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1161,3 +1161,43 @@ def decorator(x: str) -> Any: ... @decorator(T) # E: Argument 1 to "decorator" has incompatible type "int"; expected "str" class C[T]: pass + +[case testPEP695InvalidGenericOrProtocolBaseClass] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import Generic, Protocol, TypeVar + +S = TypeVar("S") + +class C[T](Generic[T]): # E: Generic[...] base class is redundant + pass +class C2[T](Generic[S]): # E: Generic[...] base class is redundant + pass + +a: C[int] +b: C2[int, str] + +class P[T](Protocol[T]): # E: No arguments expected for "Protocol" base class + pass +class P2[T](Protocol[S]): # E: No arguments expected for "Protocol" base class + pass + +[case testPEP695MixNewAndOldStyleGenerics] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import TypeVar + +S = TypeVar("S") +U = TypeVar("U") + +def f[T](x: T, y: S) -> T | S: ... # E: All type parameters should be declared ("S" not declared) +def g[T](x: S, y: U) -> T | S | U: ... # E: All type parameters should be declared ("S", "U" not declared) + +def h[S: int](x: S) -> S: + a: int = x + return x + +class C[T]: + def m[X, S](self, x: S, y: U) -> X | S | U: ... # E: All type parameters should be declared ("U" not declared) + def m2(self, x: T, y: S) -> T | S: ... + +class D[T](C[S]): # E: All type parameters should be declared ("S" not declared) + pass From 0820e95a809c950db6c8995097b043ddd102a98f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 May 2024 15:48:01 +0100 Subject: [PATCH 0606/1617] [PEP 695] Support recursive type aliases (#17268) The implementation follows the approach used for old-style type aliases. Work on #15238. --- mypy/nodes.py | 4 ++- mypy/semanal.py | 32 ++++++++++++++++++--- test-data/unit/check-python312.test | 43 +++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index e52618fcdae6..dbde3ddf4f1b 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1647,19 +1647,21 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class TypeAliasStmt(Statement): - __slots__ = ("name", "type_args", "value") + __slots__ = ("name", "type_args", "value", "invalid_recursive_alias") __match_args__ = ("name", "type_args", "value") name: NameExpr type_args: list[TypeParam] value: Expression # Will get translated into a type + invalid_recursive_alias: bool def __init__(self, name: NameExpr, type_args: list[TypeParam], value: Expression) -> None: super().__init__() self.name = name self.type_args = type_args self.value = value + self.invalid_recursive_alias = False def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_type_alias_stmt(self) diff --git a/mypy/semanal.py b/mypy/semanal.py index 320ae72d99f9..0689d5416efe 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3961,7 +3961,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: alias_node.normalized = rvalue.node.normalized current_node = existing.node if existing else alias_node assert isinstance(current_node, TypeAlias) - self.disable_invalid_recursive_aliases(s, current_node) + self.disable_invalid_recursive_aliases(s, current_node, s.rvalue) if self.is_class_scope(): assert self.type is not None if self.type.is_protocol: @@ -4057,7 +4057,7 @@ def analyze_type_alias_type_params( return declared_tvars, all_declared_tvar_names def disable_invalid_recursive_aliases( - self, s: AssignmentStmt, current_node: TypeAlias + self, s: AssignmentStmt | TypeAliasStmt, current_node: TypeAlias, ctx: Context ) -> None: """Prohibit and fix recursive type aliases that are invalid/unsupported.""" messages = [] @@ -4074,7 +4074,7 @@ def disable_invalid_recursive_aliases( current_node.target = AnyType(TypeOfAny.from_error) s.invalid_recursive_alias = True for msg in messages: - self.fail(msg, s.rvalue) + self.fail(msg, ctx) def analyze_lvalue( self, @@ -5304,6 +5304,8 @@ def visit_match_stmt(self, s: MatchStmt) -> None: self.visit_block(s.bodies[i]) def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: + if s.invalid_recursive_alias: + return self.statement = s type_params = self.push_type_args(s.type_args, s) if type_params is None: @@ -5369,10 +5371,32 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: and isinstance(existing.node, (PlaceholderNode, TypeAlias)) and existing.node.line == s.line ): - existing.node = alias_node + updated = False + if isinstance(existing.node, TypeAlias): + if existing.node.target != res: + # Copy expansion to the existing alias, this matches how we update base classes + # for a TypeInfo _in place_ if there are nested placeholders. + existing.node.target = res + existing.node.alias_tvars = alias_tvars + updated = True + else: + # Otherwise just replace existing placeholder with type alias. + existing.node = alias_node + updated = True + + if updated: + if self.final_iteration: + self.cannot_resolve_name(s.name.name, "name", s) + return + else: + # We need to defer so that this change can get propagated to base classes. + self.defer(s, force_progress=True) else: self.add_symbol(s.name.name, alias_node, s) + current_node = existing.node if existing else alias_node + assert isinstance(current_node, TypeAlias) + self.disable_invalid_recursive_aliases(s, current_node, s.value) finally: self.pop_type_args(s.type_args) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index f5d9fd195f04..6dd61351d7a8 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1162,6 +1162,49 @@ def decorator(x: str) -> Any: ... class C[T]: pass +[case testPEP695RecursiceTypeAlias] +# mypy: enable-incomplete-feature=NewGenericSyntax + +type A = str | list[A] +a: A +reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.list[...]]" + +class C[T]: pass + +type B[T] = C[T] | list[B[T]] +b: B[int] +reveal_type(b) # N: Revealed type is "Union[__main__.C[builtins.int], builtins.list[...]]" + +[case testPEP695BadRecursiveTypeAlias] +# mypy: enable-incomplete-feature=NewGenericSyntax + +type A = A # E: Cannot resolve name "A" (possible cyclic definition) +type B = B | int # E: Invalid recursive alias: a union item of itself +a: A +reveal_type(a) # N: Revealed type is "Any" +b: B +reveal_type(b) # N: Revealed type is "Any" + +[case testPEP695RecursiveTypeAliasForwardReference] +# mypy: enable-incomplete-feature=NewGenericSyntax + +def f(a: A) -> None: + if isinstance(a, str): + reveal_type(a) # N: Revealed type is "builtins.str" + else: + reveal_type(a) # N: Revealed type is "__main__.C[Union[builtins.str, __main__.C[...]]]" + +type A = str | C[A] + +class C[T]: pass + +f('x') +f(C[str]()) +f(C[C[str]]()) +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "A" +f(C[int]()) # E: Argument 1 to "f" has incompatible type "C[int]"; expected "A" +[builtins fixtures/isinstance.pyi] + [case testPEP695InvalidGenericOrProtocolBaseClass] # mypy: enable-incomplete-feature=NewGenericSyntax from typing import Generic, Protocol, TypeVar From 77cfb9887c8f2bba2443196d7462a027f435450f Mon Sep 17 00:00:00 2001 From: GiorgosPapoutsakis <116210016+GiorgosPapoutsakis@users.noreply.github.com> Date: Thu, 30 May 2024 23:37:14 +0300 Subject: [PATCH 0607/1617] Add documentation for show-error-code-links (#17144) This PR closes issue https://github.com/python/mypy/issues/16693 and a part of issue https://github.com/python/mypy/issues/17083 Propositional documentation updates for show-error-code-links, which update files command_line.rst and config_file.rst. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/command_line.rst | 11 +++++++++++ docs/source/config_file.rst | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 4a7ead3e8724..50a6ef65f4d0 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -747,6 +747,17 @@ in error messages. main.py:12:9: error: Unsupported operand types for / ("int" and "str") +.. option:: --show-error-code-links + + This flag will also display a link to error code documentation, anchored to the error code reported by mypy. + The corresponding error code will be highlighted within the documentation page. + If we enable this flag, the error message now looks like this:: + + main.py:3: error: Unsupported operand types for - ("int" and "str") [operator] + main.py:3: note: See 'https://mypy.rtfd.io/en/stable/_refs.html#code-operator' for more info + + + .. option:: --show-error-end This flag will make mypy show not just that start position where diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index ac110cbed9f1..b0e82a33255a 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -787,6 +787,13 @@ These options may only be set in the global section (``[mypy]``). Shows column numbers in error messages. +.. confval:: show_error_code_links + + :type: boolean + :default: False + + Shows documentation link to corresponding error code. + .. confval:: hide_error_codes :type: boolean From c3bbd1cdeca02e63e1102a3274415f056e8d1e43 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 31 May 2024 11:29:55 -0700 Subject: [PATCH 0608/1617] Use Never in more messages, use ambiguous in join (#17304) Switches the logic from https://github.com/python/mypy/pull/16994 to use ambiguous (since is_noreturn was only meant for error messages) See also https://github.com/python/mypy/pull/15996 --- mypy/copytype.py | 2 +- mypy/join.py | 4 ++-- mypy/messages.py | 7 ++----- mypy/typeanal.py | 2 +- mypy/types.py | 13 ++++--------- test-data/unit/check-dataclasses.test | 4 ++-- test-data/unit/check-flags.test | 4 ++-- test-data/unit/check-literal.test | 6 ++---- test-data/unit/check-plugin-attrs.test | 4 ++-- test-data/unit/check-python310.test | 2 +- test-data/unit/check-type-aliases.test | 2 +- test-data/unit/check-typeddict.test | 6 +++--- 12 files changed, 23 insertions(+), 33 deletions(-) diff --git a/mypy/copytype.py b/mypy/copytype.py index 4ca381c4a8c4..465f06566f54 100644 --- a/mypy/copytype.py +++ b/mypy/copytype.py @@ -53,7 +53,7 @@ def visit_none_type(self, t: NoneType) -> ProperType: return self.copy_common(t, NoneType()) def visit_uninhabited_type(self, t: UninhabitedType) -> ProperType: - dup = UninhabitedType(t.is_noreturn) + dup = UninhabitedType() dup.ambiguous = t.ambiguous return self.copy_common(t, dup) diff --git a/mypy/join.py b/mypy/join.py index 782b4fbebd7b..c711697ec46d 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -108,9 +108,9 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # TODO: contravariant case should use meet but pass seen instances as # an argument to keep track of recursive checks. elif type_var.variance in (INVARIANT, CONTRAVARIANT): - if isinstance(ta_proper, UninhabitedType) and not ta_proper.is_noreturn: + if isinstance(ta_proper, UninhabitedType) and ta_proper.ambiguous: new_type = sa - elif isinstance(sa_proper, UninhabitedType) and not sa_proper.is_noreturn: + elif isinstance(sa_proper, UninhabitedType) and sa_proper.ambiguous: new_type = ta elif not is_equivalent(ta, sa): self.seen_instances.pop() diff --git a/mypy/messages.py b/mypy/messages.py index 8f923462c789..53a7f7d97774 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2430,7 +2430,7 @@ def quote_type_string(type_string: str) -> str: """Quotes a type representation for use in messages.""" no_quote_regex = r"^<(tuple|union): \d+ items>$" if ( - type_string in ["Module", "overloaded function", "Never", ""] + type_string in ["Module", "overloaded function", ""] or type_string.startswith("Module ") or re.match(no_quote_regex, type_string) is not None or type_string.endswith("?") @@ -2633,10 +2633,7 @@ def format_literal_value(typ: LiteralType) -> str: elif isinstance(typ, DeletedType): return "" elif isinstance(typ, UninhabitedType): - if typ.is_noreturn: - return "NoReturn" - else: - return "Never" + return "Never" elif isinstance(typ, TypeType): type_name = "type" if options.use_lowercase_names() else "Type" return f"{type_name}[{format(typ.item)}]" diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 31d451b0831a..8f138ab5698f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -645,7 +645,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ return AnyType(TypeOfAny.from_error) return self.anal_type(t.args[0]) elif fullname in NEVER_NAMES: - return UninhabitedType(is_noreturn=True) + return UninhabitedType() elif fullname in LITERAL_TYPE_NAMES: return self.analyze_literal_type(t) elif fullname in ANNOTATED_TYPE_NAMES: diff --git a/mypy/types.py b/mypy/types.py index 0ef3803c5687..2cacc3e44085 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1169,17 +1169,12 @@ class UninhabitedType(ProperType): is_subtype(UninhabitedType, T) = True """ - __slots__ = ("ambiguous", "is_noreturn") + __slots__ = ("ambiguous",) - is_noreturn: bool # Does this come from a NoReturn? Purely for error messages. - # It is important to track whether this is an actual NoReturn type, or just a result - # of ambiguous type inference, in the latter case we don't want to mark a branch as - # unreachable in binder. ambiguous: bool # Is this a result of inference for a variable without constraints? - def __init__(self, is_noreturn: bool = False, line: int = -1, column: int = -1) -> None: + def __init__(self, line: int = -1, column: int = -1) -> None: super().__init__(line, column) - self.is_noreturn = is_noreturn self.ambiguous = False def can_be_true_default(self) -> bool: @@ -1198,12 +1193,12 @@ def __eq__(self, other: object) -> bool: return isinstance(other, UninhabitedType) def serialize(self) -> JsonDict: - return {".class": "UninhabitedType", "is_noreturn": self.is_noreturn} + return {".class": "UninhabitedType"} @classmethod def deserialize(cls, data: JsonDict) -> UninhabitedType: assert data[".class"] == "UninhabitedType" - return UninhabitedType(is_noreturn=data["is_noreturn"]) + return UninhabitedType() class NoneType(ProperType): diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index a055507cdd78..924f9c7bb5be 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2080,8 +2080,8 @@ class B: a_or_b: Union[A[int], B] _ = replace(a_or_b, x=42, y=True, init_var=42) _ = replace(a_or_b, x=42, y=True) # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]" -_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected Never -_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never +_ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected "Never" +_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never" _ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index c90c773e320f..62711d5f0071 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -408,7 +408,7 @@ reveal_type(f() or no_return()) # N: Revealed type is "builtins.int" # flags: --warn-no-return from mypy_extensions import NoReturn -x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "NoReturn") +x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "Never") [builtins fixtures/dict.pyi] [case testNoReturnAsync] @@ -477,7 +477,7 @@ def no_return() -> NoReturn: pass def f() -> NoReturn: no_return() -x: NoReturn = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "NoReturn") +x: NoReturn = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Never") [builtins fixtures/dict.pyi] [case testShowErrorContextFunction] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 423ba74eba72..8f8aaf6a3982 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -839,14 +839,13 @@ b: NoReturn c: None fa(lit) -fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "NoReturn" +fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "Never" fc(lit) # E: Argument 1 to "fc" has incompatible type "Literal[1]"; expected "None" f_lit(a) f_lit(b) f_lit(c) # E: Argument 1 to "f_lit" has incompatible type "None"; expected "Literal[1]" [builtins fixtures/tuple.pyi] -[out] [case testLiteralCheckSubtypingNoStrictOptional] # flags: --no-strict-optional @@ -865,14 +864,13 @@ b: NoReturn c: None fa(lit) -fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "NoReturn" +fb(lit) # E: Argument 1 to "fb" has incompatible type "Literal[1]"; expected "Never" fc(lit) # E: Argument 1 to "fc" has incompatible type "Literal[1]"; expected "None" f_lit(a) f_lit(b) f_lit(c) [builtins fixtures/tuple.pyi] -[out] [case testLiteralCallingOverloadedFunction] from typing import overload, Generic, TypeVar, Any diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 39b266dba50e..b96c00730a74 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2170,8 +2170,8 @@ class B: a_or_b: A[int] | B a2 = attrs.evolve(a_or_b, x=42, y=True) -a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected Never -a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected Never +a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected "Never" +a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never" [builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 8991b65f67b5..5ecc69dc7c32 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1406,7 +1406,7 @@ def f(value: int) -> int: # E: Missing return statement case 2: return 1 case o: - assert_never(o) # E: Argument 1 to "assert_never" has incompatible type "int"; expected "NoReturn" + assert_never(o) # E: Argument 1 to "assert_never" has incompatible type "int"; expected "Never" [case testMatchExhaustiveNoError] from typing import NoReturn, Union, Literal diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index aebb0381d962..f77c3c1c34e2 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -57,7 +57,7 @@ Never = NoReturn a: Never # Used to be an error here def f(a: Never): ... -f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "NoReturn" +f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "Never" [case testImportUnionAlias] import typing from _m import U diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index bd1fbe3f2667..09b86e4afd2d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1648,7 +1648,7 @@ a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompa x = '' a.setdefault(x, 1) # E: Expected TypedDict key to be string literal alias = a.setdefault -alias(x, 1) # E: Argument 1 has incompatible type "str"; expected "NoReturn" +alias(x, 1) # E: Argument 1 has incompatible type "str"; expected "Never" a.update({}) a.update({'x': 1}) @@ -1680,8 +1680,8 @@ b.pop('x') # E: Key "x" of TypedDict "B" cannot be deleted x = '' b.pop(x) # E: Expected TypedDict key to be string literal pop = b.pop -pop('x') # E: Argument 1 has incompatible type "str"; expected "NoReturn" -pop('invalid') # E: Argument 1 has incompatible type "str"; expected "NoReturn" +pop('x') # E: Argument 1 has incompatible type "str"; expected "Never" +pop('invalid') # E: Argument 1 has incompatible type "str"; expected "Never" [builtins fixtures/dict.pyi] [case testTypedDictDel] From 2116386c7752a5c78425419df8e28f654c893045 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Sun, 2 Jun 2024 14:51:56 +0100 Subject: [PATCH 0609/1617] Update 'typing_extensions' to >=4.6.0 to fix python 3.12 error (#17312) With earlier versions of typing_extensions, the following traceback is seen: ``` Traceback (most recent call last): File ".../bin/mypy", line 5, in from mypy.__main__ import console_entry File ".../lib/python3.12/site-packages/mypy/__main__.py", line 9, in from mypy.main import main, process_options File ".../lib/python3.12/site-packages/mypy/main.py", line 12, in from typing_extensions import Final File ".../lib/python3.12/site-packages/typing_extensions.py", line 1174, in class TypeVar(typing.TypeVar, _DefaultMixin, _root=True): TypeError: type 'typing.TypeVar' is not an acceptable base type ``` The error is addressed in typing_extensions in https://github.com/python/typing_extensions/pull/162, which is included in the 4.6.0 release. --- mypy-requirements.txt | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy-requirements.txt b/mypy-requirements.txt index f81412be761e..341052822f25 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -1,4 +1,4 @@ # NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml -typing_extensions>=4.1.0 +typing_extensions>=4.6.0 mypy_extensions>=1.0.0 tomli>=1.1.0; python_version<'3.11' diff --git a/pyproject.toml b/pyproject.toml index 35f1592ca83c..33d4ec094f50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = [ "setuptools >= 40.6.2", "wheel >= 0.30.0", # the following is from mypy-requirements.txt - "typing_extensions>=4.1.0", + "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", "tomli>=1.1.0; python_version<'3.11'", # the following is from build-requirements.txt From b207550318fc5d58372abe3dac0d34f895d3ead9 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 2 Jun 2024 15:23:26 +0100 Subject: [PATCH 0610/1617] Sync typing_extensions pin in setup.py with the pin in the other two places (#17313) Followup to #17312. (Can't say I fully understand why we have to have this pin in three places :) --- mypy-requirements.txt | 1 + pyproject.toml | 2 +- setup.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 341052822f25..8d41a3fc7003 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -1,4 +1,5 @@ # NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml +# and the pins in setup.py typing_extensions>=4.6.0 mypy_extensions>=1.0.0 tomli>=1.1.0; python_version<'3.11' diff --git a/pyproject.toml b/pyproject.toml index 33d4ec094f50..12a0dc109cd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = [ # self-typechecking :/ "setuptools >= 40.6.2", "wheel >= 0.30.0", - # the following is from mypy-requirements.txt + # the following is from mypy-requirements.txt/setup.py "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", "tomli>=1.1.0; python_version<'3.11'", diff --git a/setup.py b/setup.py index a17ee562eb39..160e2b054b0e 100644 --- a/setup.py +++ b/setup.py @@ -218,9 +218,9 @@ def run(self): }, classifiers=classifiers, cmdclass=cmdclass, - # When changing this, also update mypy-requirements.txt. + # When changing this, also update mypy-requirements.txt and pyproject.toml install_requires=[ - "typing_extensions>=4.1.0", + "typing_extensions>=4.6.0", "mypy_extensions >= 1.0.0", "tomli>=1.1.0; python_version<'3.11'", ], From 6c24ea66e20166964aa5d42e28fda5b4b69f44b1 Mon Sep 17 00:00:00 2001 From: urnest Date: Mon, 3 Jun 2024 14:38:44 +1000 Subject: [PATCH 0611/1617] fix #16935 fix type of tuple[X,Y] expression (#17235) implement the mypy/checkexpr.py TODO: Specialize the callable for the type arguments ... so e.g. reveal_type(tuple[int, int]) gives expected def (p0: tuple[builtins.int, builtins.int]) -> tuple[builtins.int, builtins.int] ... rather than def [_T_co] (typing.Iterable[_T_co`1] =) -> builtins.tuple[_T_co`1, ...] Fixes #16935 --- mypy/checkexpr.py | 17 +++++++- .../check-type-object-type-inference.test | 41 +++++++++++++++++++ test-data/unit/pythoneval.test | 3 +- 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test-data/unit/check-type-object-type-inference.test diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 479ef228b038..0a4af069ea17 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4836,8 +4836,21 @@ def apply_type_arguments_to_callable( len(args) < min_arg_count or len(args) > len(tp.variables) ) and not has_type_var_tuple: if tp.is_type_obj() and tp.type_object().fullname == "builtins.tuple": - # TODO: Specialize the callable for the type arguments - return tp + # e.g. expression tuple[X, Y] + # - want the type of the expression i.e. a function with that as its return type + # - tp is type of tuple (note it won't have params as we are only called + # with generic callable type) + # - tuple[X, Y]() takes a single arg that is a tuple containing an X and a Y + return CallableType( + [TupleType(list(args), self.chk.named_type("tuple"))], + [ARG_POS], + [None], + TupleType(list(args), self.chk.named_type("tuple")), + tp.fallback, + name="tuple", + definition=tp.definition, + bound_args=tp.bound_args, + ) self.msg.incompatible_type_application( min_arg_count, len(tp.variables), len(args), ctx ) diff --git a/test-data/unit/check-type-object-type-inference.test b/test-data/unit/check-type-object-type-inference.test new file mode 100644 index 000000000000..baeca1e22ac7 --- /dev/null +++ b/test-data/unit/check-type-object-type-inference.test @@ -0,0 +1,41 @@ +[case testInferTupleType] +# flags: --python-version 3.9 +from typing import TypeVar, Generic, Type +from abc import abstractmethod + +T = TypeVar('T') +class E(Generic[T]): + @abstractmethod + def e(self, t: T) -> str: + ... + +class F: + @abstractmethod + def f(self, tp: Type[T]) -> E[T]: + ... + +def g(f: F): + f.f(int).e(7) + f.f(tuple[int,str]) + f.f(tuple[int,str]).e('x') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, str]" + f.f(tuple[int,str]).e( (7,8) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, str]" + f.f(tuple[int,str]).e( (7,'x') ) # OK + reveal_type(f.f(tuple[int,str]).e) # N: Revealed type is "def (t: Tuple[builtins.int, builtins.str]) -> builtins.str" + +def h(f: F): + f.f(int).e(7) + f.f(tuple) + f.f(tuple).e('y') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[Any, ...]" + f.f(tuple).e( (8,'y') ) # OK + reveal_type(f.f(tuple).e) # N: Revealed type is "def (t: builtins.tuple[Any, ...]) -> builtins.str" + +def i(f: F): + f.f(tuple[int,tuple[int,str]]) + f.f(tuple[int,tuple[int,str]]).e('z') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, Tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e( (8,9) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, Tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e( (17, (28, 29)) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, Tuple[int, int]]"; expected "Tuple[int, Tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e( (27,(28,'z')) ) # OK + reveal_type(f.f(tuple[int,tuple[int,str]]).e) # N: Revealed type is "def (t: Tuple[builtins.int, Tuple[builtins.int, builtins.str]]) -> builtins.str" + +x = tuple[int,str][str] # E: The type "Type[Tuple[Any, ...]]" is not generic and not indexable +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 0cf6d6b5aa38..a76d3abd7114 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1834,7 +1834,6 @@ RHSAlias3: type = tuple[int, ...] WrongTypeElement = str | tuple[float, 1] # Error WrongEllipsis = tuple[float, float, ...] | str # Error -# TODO: This should produce a fixed-length tuple reveal_type(tuple[int, str]((1, "x"))) [out] _testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "Union[builtins.str, Tuple[builtins.float, builtins.float, builtins.str]]" @@ -1845,7 +1844,7 @@ _testTupleWithDifferentArgsPy310.py:19: note: Revealed type is "builtins.tuple[b _testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" _testTupleWithDifferentArgsPy310.py:26: error: Invalid type: try using Literal[1] instead? _testTupleWithDifferentArgsPy310.py:27: error: Unexpected "..." -_testTupleWithDifferentArgsPy310.py:30: note: Revealed type is "builtins.tuple[builtins.object, ...]" +_testTupleWithDifferentArgsPy310.py:29: note: Revealed type is "Tuple[builtins.int, builtins.str]" [case testEnumIterMetaInference] import socket From aa4410ff7806425f143c3c4a21324d8f10b3f76d Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:49:12 -0700 Subject: [PATCH 0612/1617] [mypyc] Fix ParamSpec (#17309) Fixes https://github.com/mypyc/mypyc/issues/1051 --- mypyc/irbuild/mapper.py | 4 +-- mypyc/test-data/irbuild-generics.test | 41 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index a3abbb1f84fb..90ce0e16c741 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -15,7 +15,7 @@ Type, TypedDictType, TypeType, - TypeVarType, + TypeVarLikeType, UnboundType, UninhabitedType, UnionType, @@ -131,7 +131,7 @@ def type_to_rtype(self, typ: Type | None) -> RType: return object_rprimitive elif isinstance(typ, TypeType): return object_rprimitive - elif isinstance(typ, TypeVarType): + elif isinstance(typ, TypeVarLikeType): # Erase type variable to upper bound. # TODO: Erase to union if object has value restriction? return self.type_to_rtype(typ.upper_bound) diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 4f9d0ab83a16..910148f80dda 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -148,3 +148,44 @@ L2: r4 = x L3: return r4 + + +[case testParamSpec] +from typing import Callable, ParamSpec, TypeVar + +P = ParamSpec("P") + +def execute(func: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> int: + return func(*args, **kwargs) + +def f(x: int) -> int: + return x + +execute(f, 1) +[out] +def execute(func, args, kwargs): + func :: object + args :: tuple + kwargs :: dict + r0 :: list + r1 :: object + r2 :: dict + r3 :: i32 + r4 :: bit + r5 :: tuple + r6 :: object + r7 :: int +L0: + r0 = PyList_New(0) + r1 = CPyList_Extend(r0, args) + r2 = PyDict_New() + r3 = CPyDict_UpdateInDisplay(r2, kwargs) + r4 = r3 >= 0 :: signed + r5 = PyList_AsTuple(r0) + r6 = PyObject_Call(func, r5, r2) + r7 = unbox(int, r6) + return r7 +def f(x): + x :: int +L0: + return x From 93dac05cc8461f13c2031dff48711eecbe2595af Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 3 Jun 2024 17:10:38 +0100 Subject: [PATCH 0613/1617] [PEP 695] Fix a few issues and add tests (#17318) Fix badly formed types that could be created when using aliases like `type A = list`. Improve some error messages when using PEP 695 syntax. Add a few PEP 695 tests. Work on #15238. --- mypy/message_registry.py | 3 ++ mypy/messages.py | 6 ++- mypy/semanal.py | 5 +++ mypy/typeanal.py | 30 ++++++++++--- test-data/unit/check-python312.test | 69 +++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 8 deletions(-) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index ccc1443dacf0..3852431f2290 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -194,6 +194,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage: "A function returning TypeVar should receive at least " "one argument containing the same TypeVar" ) +TYPE_PARAMETERS_SHOULD_BE_DECLARED: Final = ( + "All type parameters should be declared ({} not declared)" +) # Super TOO_MANY_ARGS_FOR_SUPER: Final = ErrorMessage('Too many arguments for "super"') diff --git a/mypy/messages.py b/mypy/messages.py index 53a7f7d97774..de079feda048 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2423,7 +2423,11 @@ def annotation_in_unchecked_function(self, context: Context) -> None: def type_parameters_should_be_declared(self, undeclared: list[str], context: Context) -> None: names = ", ".join('"' + n + '"' for n in undeclared) - self.fail(f"All type parameters should be declared ({names} not declared)", context) + self.fail( + message_registry.TYPE_PARAMETERS_SHOULD_BE_DECLARED.format(names), + context, + code=codes.VALID_TYPE, + ) def quote_type_string(type_string: str) -> str: diff --git a/mypy/semanal.py b/mypy/semanal.py index 0689d5416efe..44db7ddf5618 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3694,6 +3694,7 @@ def analyze_alias( allow_placeholder: bool = False, declared_type_vars: TypeVarLikeList | None = None, all_declared_type_params_names: list[str] | None = None, + python_3_12_type_alias: bool = False, ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str], bool]: """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable). @@ -3747,6 +3748,7 @@ def analyze_alias( global_scope=global_scope, allowed_alias_tvars=tvar_defs, alias_type_params_names=all_declared_type_params_names, + python_3_12_type_alias=python_3_12_type_alias, ) # There can be only one variadic variable at most, the error is reported elsewhere. @@ -5321,6 +5323,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: allow_placeholder=True, declared_type_vars=type_params, all_declared_type_params_names=all_type_params_names, + python_3_12_type_alias=True, ) if not res: res = AnyType(TypeOfAny.from_error) @@ -5355,6 +5358,8 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) eager = self.is_func_scope() + if isinstance(res, ProperType) and isinstance(res, Instance) and not res.args: + fix_instance(res, self.fail, self.note, disallow_any=False, options=self.options) alias_node = TypeAlias( res, self.qualified_name(s.name.name), diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 8f138ab5698f..bf53204ffce9 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -142,6 +142,7 @@ def analyze_type_alias( global_scope: bool = True, allowed_alias_tvars: list[TypeVarLikeType] | None = None, alias_type_params_names: list[str] | None = None, + python_3_12_type_alias: bool = False, ) -> tuple[Type, set[str]]: """Analyze r.h.s. of a (potential) type alias definition. @@ -160,6 +161,7 @@ def analyze_type_alias( prohibit_self_type="type alias target", allowed_alias_tvars=allowed_alias_tvars, alias_type_params_names=alias_type_params_names, + python_3_12_type_alias=python_3_12_type_alias, ) analyzer.in_dynamic_func = in_dynamic_func analyzer.global_scope = global_scope @@ -202,6 +204,7 @@ def __init__( is_typeshed_stub: bool, *, defining_alias: bool = False, + python_3_12_type_alias: bool = False, allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, @@ -220,6 +223,7 @@ def __init__( self.tvar_scope = tvar_scope # Are we analysing a type alias definition rvalue? self.defining_alias = defining_alias + self.python_3_12_type_alias = python_3_12_type_alias self.allow_tuple_literal = allow_tuple_literal # Positive if we are analyzing arguments of another (outer) type self.nesting_level = 0 @@ -364,7 +368,12 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) and (tvar_def is None or tvar_def not in self.allowed_alias_tvars) ): if self.not_declared_in_type_params(t.name): - msg = f'Type variable "{t.name}" is not included in type_params' + if self.python_3_12_type_alias: + msg = message_registry.TYPE_PARAMETERS_SHOULD_BE_DECLARED.format( + f'"{t.name}"' + ) + else: + msg = f'Type variable "{t.name}" is not included in type_params' else: msg = f'Can\'t use bound type variable "{t.name}" to define generic alias' self.fail(msg, t, code=codes.VALID_TYPE) @@ -393,7 +402,12 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if self.allow_unbound_tvars: return t if self.defining_alias and self.not_declared_in_type_params(t.name): - msg = f'TypeVarTuple "{t.name}" is not included in type_params' + if self.python_3_12_type_alias: + msg = message_registry.TYPE_PARAMETERS_SHOULD_BE_DECLARED.format( + f'"{t.name}"' + ) + else: + msg = f'TypeVarTuple "{t.name}" is not included in type_params' else: msg = f'TypeVarTuple "{t.name}" is unbound' self.fail(msg, t, code=codes.VALID_TYPE) @@ -1309,11 +1323,13 @@ def analyze_callable_args_for_paramspec( and self.not_declared_in_type_params(tvar_def.name) and tvar_def not in self.allowed_alias_tvars ): - self.fail( - f'ParamSpec "{tvar_def.name}" is not included in type_params', - callable_args, - code=codes.VALID_TYPE, - ) + if self.python_3_12_type_alias: + msg = message_registry.TYPE_PARAMETERS_SHOULD_BE_DECLARED.format( + f'"{tvar_def.name}"' + ) + else: + msg = f'ParamSpec "{tvar_def.name}" is not included in type_params' + self.fail(msg, callable_args, code=codes.VALID_TYPE) return callable_with_ellipsis( AnyType(TypeOfAny.special_form), ret_type=ret_type, fallback=fallback ) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 6dd61351d7a8..8443aadb6905 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1244,3 +1244,72 @@ class C[T]: class D[T](C[S]): # E: All type parameters should be declared ("S" not declared) pass + +[case testPEP695MixNewAndOldStyleTypeVarTupleAndParamSpec] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import TypeVarTuple, ParamSpec, Callable +Ts = TypeVarTuple("Ts") +P = ParamSpec("P") + +def f[T](x: T, f: Callable[P, None] # E: All type parameters should be declared ("P" not declared) + ) -> Callable[P, T]: ... +def g[T](x: T, f: tuple[*Ts] # E: All type parameters should be declared ("Ts" not declared) + ) -> tuple[T, *Ts]: ... +[builtins fixtures/tuple.pyi] + +[case testPEP695MixNewAndOldStyleGenericsInTypeAlias] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import TypeVar, ParamSpec, TypeVarTuple, Callable + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +P = ParamSpec("P") + +type A = list[T] # E: All type parameters should be declared ("T" not declared) +a: A[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(a) # N: Revealed type is "builtins.list[Any]" + +type B = tuple[*Ts] # E: All type parameters should be declared ("Ts" not declared) +type C = Callable[P, None] # E: All type parameters should be declared ("P" not declared) +[builtins fixtures/tuple.pyi] + +[case testPEP695NonGenericAliasToGenericClass] +# mypy: enable-incomplete-feature=NewGenericSyntax +class C[T]: pass +type A = C +x: C +y: A +reveal_type(x) # N: Revealed type is "__main__.C[Any]" +reveal_type(y) # N: Revealed type is "__main__.C[Any]" +z: A[int] # E: Bad number of arguments for type alias, expected 0, given 1 + +[case testPEP695SelfType] +# mypy: enable-incomplete-feature=NewGenericSyntax +from typing import Self + +class C: + @classmethod + def m[T](cls, x: T) -> tuple[Self, T]: + return cls(), x + +class D(C): + pass + +reveal_type(C.m(1)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" +reveal_type(D.m(1)) # N: Revealed type is "Tuple[__main__.D, builtins.int]" + +class E[T]: + def m(self) -> Self: + return self + + def mm[S](self, x: S) -> tuple[Self, S]: + return self, x + +class F[T](E[T]): + pass + +reveal_type(E[int]().m()) # N: Revealed type is "__main__.E[builtins.int]" +reveal_type(E[int]().mm(b'x')) # N: Revealed type is "Tuple[__main__.E[builtins.int], builtins.bytes]" +reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]" +reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]" +[builtins fixtures/tuple.pyi] From c7621912e2451f7135169f7d458f91c6c947ddba Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 4 Jun 2024 13:32:15 +0100 Subject: [PATCH 0614/1617] [PEP 695] Generate error if 3.12 type alias is called (#17320) PEP 695 type aliases raise an exception at runtime if called. Work on #15238. --- mypy/checkexpr.py | 4 +++ mypy/nodes.py | 6 ++++ mypy/semanal.py | 2 ++ test-data/unit/check-python312.test | 47 +++++++++++++++++++++++++ test-data/unit/check-type-aliases.test | 7 ++++ test-data/unit/fixtures/typing-full.pyi | 1 + 6 files changed, 67 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0a4af069ea17..f826d4c11dd3 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4659,6 +4659,8 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: is due to slight differences in how type arguments are applied and checked. """ if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias): + if tapp.expr.node.python_3_12_type_alias: + return self.named_type("typing.TypeAliasType") # Subscription of a (generic) alias in runtime context, expand the alias. item = instantiate_type_alias( tapp.expr.node, @@ -4721,6 +4723,8 @@ class LongName(Generic[T]): ... x = A() y = cast(A, ...) """ + if alias.python_3_12_type_alias: + return self.named_type("typing.TypeAliasType") if isinstance(alias.target, Instance) and alias.target.invalid: # type: ignore[misc] # An invalid alias, error already has been reported return AnyType(TypeOfAny.from_error) diff --git a/mypy/nodes.py b/mypy/nodes.py index dbde3ddf4f1b..90561779051d 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3578,6 +3578,7 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here "_is_recursive", "eager", "tvar_tuple_index", + "python_3_12_type_alias", ) __match_args__ = ("name", "target", "alias_tvars", "no_args") @@ -3593,6 +3594,7 @@ def __init__( no_args: bool = False, normalized: bool = False, eager: bool = False, + python_3_12_type_alias: bool = False, ) -> None: self._fullname = fullname self.target = target @@ -3605,6 +3607,7 @@ def __init__( # it is the cached value. self._is_recursive: bool | None = None self.eager = eager + self.python_3_12_type_alias = python_3_12_type_alias self.tvar_tuple_index = None for i, t in enumerate(alias_tvars): if isinstance(t, mypy.types.TypeVarTupleType): @@ -3675,6 +3678,7 @@ def serialize(self) -> JsonDict: "normalized": self.normalized, "line": self.line, "column": self.column, + "python_3_12_type_alias": self.python_3_12_type_alias, } return data @@ -3692,6 +3696,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias: normalized = data["normalized"] line = data["line"] column = data["column"] + python_3_12_type_alias = data["python_3_12_type_alias"] return cls( target, fullname, @@ -3700,6 +3705,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias: alias_tvars=cast(List[mypy.types.TypeVarLikeType], alias_tvars), no_args=no_args, normalized=normalized, + python_3_12_type_alias=python_3_12_type_alias, ) diff --git a/mypy/semanal.py b/mypy/semanal.py index 44db7ddf5618..e4f123cbfc20 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3922,6 +3922,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: alias_tvars=alias_tvars, no_args=no_args, eager=eager, + python_3_12_type_alias=pep_695, ) if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)) and ( not isinstance(rvalue, OpExpr) @@ -5368,6 +5369,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: alias_tvars=alias_tvars, no_args=False, eager=eager, + python_3_12_type_alias=True, ) existing = self.current_symbol_table().get(s.name.name) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 8443aadb6905..77276d9ee079 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1313,3 +1313,50 @@ reveal_type(E[int]().mm(b'x')) # N: Revealed type is "Tuple[__main__.E[builtins reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]" reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]" [builtins fixtures/tuple.pyi] + +[case testPEP695CallAlias] +# mypy: enable-incomplete-feature=NewGenericSyntax + +class C: + def __init__(self, x: str) -> None: ... +type A = C + +class D[T]: pass +type B[T] = D[T] + +reveal_type(A) # N: Revealed type is "typing.TypeAliasType" +reveal_type(B) # N: Revealed type is "typing.TypeAliasType" +reveal_type(B[int]) # N: Revealed type is "typing.TypeAliasType" + +A(1) # E: "TypeAliasType" not callable +B[int]() # E: "TypeAliasType" not callable + +A2 = C +B2 = D +A2(1) # E: Argument 1 to "C" has incompatible type "int"; expected "str" +B2[int]() +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695IncrementalTypeAliasKinds] +# flags: --enable-incomplete-feature=NewGenericSyntax +import a + +[file a.py] +from b import A + +[file a.py.2] +from b import A, B, C +A() +B() +C() + +[file b.py] +from typing_extensions import TypeAlias +type A = int +B = int +C: TypeAlias = int +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] +[out2] +tmp/a.py:2: error: "TypeAliasType" not callable diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index f77c3c1c34e2..c13331e0a61b 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1075,11 +1075,15 @@ x: TestType = 42 y: TestType = 'a' z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") +reveal_type(TestType) # N: Revealed type is "typing.TypeAliasType" +TestType() # E: "TypeAliasType" not callable + class A: ClassAlias = TypeAliasType("ClassAlias", int) xc: A.ClassAlias = 1 yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testTypeAliasTypeInvalid] from typing_extensions import TypeAliasType @@ -1094,6 +1098,7 @@ T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead? t3: T3 reveal_type(t3) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testTypeAliasTypeGeneric] from typing import Callable, Dict, Generic, TypeVar, Tuple @@ -1140,6 +1145,7 @@ ParamAlias2 = TypeAliasType("ParamAlias2", G[P, T], type_params=(P, T)) xp: ParamAlias2[[int], str] reveal_type(xp) # N: Revealed type is "__main__.G[[builtins.int], builtins.str]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testTypeAliasTypeInvalidGeneric] from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec @@ -1200,6 +1206,7 @@ class A(Generic[T]): x: A.Ta11 = {"a": 1} reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testTypeAliasTypeNoUnpackInTypeParams311] # flags: --python-version 3.11 diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index f7da75fa4cd0..71d4dcb58853 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -35,6 +35,7 @@ TypedDict = 0 NoReturn = 0 NewType = 0 Self = 0 +Unpack = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) From 16d5aaf7f7f1e7f267c0a51924f6ca53ac5abe99 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 4 Jun 2024 15:31:39 +0100 Subject: [PATCH 0615/1617] [PEP 695] Add daemon tests that use new type parameter syntax (#17327) Add some basic mypy daemon test coverage, and make it possible to write daemon tests that only work on recent Python versions. Everything tested seems to work already. Work on #15238. --- mypy/test/testdeps.py | 5 ++ mypy/test/testdiff.py | 7 +- mypy/test/testfinegrained.py | 4 + mypy/traverser.py | 1 - test-data/unit/deps.test | 16 ++++ test-data/unit/diff.test | 99 ++++++++++++++++++++++ test-data/unit/fine-grained-python312.test | 81 ++++++++++++++++++ 7 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 test-data/unit/fine-grained-python312.test diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index f9a059672de8..7c845eab8b57 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -3,8 +3,11 @@ from __future__ import annotations import os +import sys from collections import defaultdict +import pytest + from mypy import build from mypy.errors import CompileError from mypy.modulefinder import BuildSource @@ -28,6 +31,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: src = "\n".join(testcase.input) dump_all = "# __dump_all__" in src options = parse_options(src, testcase, incremental_step=1) + if options.python_version > sys.version_info: + pytest.skip("Test case requires a newer Python version") options.use_builtins_fixtures = True options.show_traceback = True options.cache_dir = os.devnull diff --git a/mypy/test/testdiff.py b/mypy/test/testdiff.py index 5e2e0bc2ca5a..0559b33c33e2 100644 --- a/mypy/test/testdiff.py +++ b/mypy/test/testdiff.py @@ -3,9 +3,11 @@ from __future__ import annotations import os +import sys + +import pytest from mypy import build -from mypy.defaults import PYTHON3_VERSION from mypy.errors import CompileError from mypy.modulefinder import BuildSource from mypy.nodes import MypyFile @@ -24,6 +26,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: files_dict = dict(testcase.files) second_src = files_dict["tmp/next.py"] options = parse_options(first_src, testcase, 1) + if options.python_version > sys.version_info: + pytest.skip("Test case requires a newer Python version") messages1, files1 = self.build(first_src, options) messages2, files2 = self.build(second_src, options) @@ -53,7 +57,6 @@ def build(self, source: str, options: Options) -> tuple[list[str], dict[str, Myp options.use_builtins_fixtures = True options.show_traceback = True options.cache_dir = os.devnull - options.python_version = PYTHON3_VERSION options.allow_empty_bodies = True try: result = build.build( diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index f61a58c425fc..800ba2dff087 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -16,6 +16,7 @@ import os import re +import sys import unittest from typing import Any @@ -82,6 +83,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: f.write(main_src) options = self.get_options(main_src, testcase, build_cache=False) + if options.python_version > sys.version_info: + pytest.skip("Test case requires a newer Python version") + build_options = self.get_options(main_src, testcase, build_cache=True) server = Server(options, DEFAULT_STATUS_FILE) diff --git a/mypy/traverser.py b/mypy/traverser.py index 225de27e7002..6f162c9ec576 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -246,7 +246,6 @@ def visit_match_stmt(self, o: MatchStmt) -> None: def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: o.name.accept(self) - # TODO: params o.value.accept(self) def visit_member_expr(self, o: MemberExpr) -> None: diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index d18b4aae963b..f46cfebb113f 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1431,3 +1431,19 @@ class B(A): -> m -> m -> m + +[case testPEP695TypeAliasDeps] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +from a import C, E +type A = C +type A2 = A +type A3 = E +[file a.py] +class C: pass +class D: pass +type E = D +[out] + -> m + -> m + -> m + -> m diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 8fc74868123e..9212d902e8b2 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1530,3 +1530,102 @@ class C: [out] __main__.C.get_by_team_and_id __main__.Optional + +[case testPEP695TypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +from typing_extensions import TypeAlias, TypeAliasType +type A = int +type B = str +type C = int +D = int +E: TypeAlias = int +F = TypeAliasType("F", int) +G = TypeAliasType("G", int) +type H = int + +[file next.py] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +from typing_extensions import TypeAlias, TypeAliasType +type A = str +type B = str +type C[T] = int +type D = int +type E = int +type F = int +type G = str +type H[T] = int + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] +[out] +__main__.A +__main__.C +__main__.D +__main__.E +__main__.G +__main__.H + +[case testPEP695TypeAlias2] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +type A[T: int] = list[T] +type B[T: int] = list[T] +type C[T: (int, str)] = list[T] +type D[T: (int, str)] = list[T] +type E[T: int] = list[T] +type F[T: (int, str)] = list[T] + +[file next.py] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +type A[T] = list[T] +type B[T: str] = list[T] +type C[T: (int, None)] = list[T] +type D[T] = list[T] +type E[T: int] = list[T] +type F[T: (int, str)] = list[T] + +[out] +__main__.A +__main__.B +__main__.C +__main__.D + +[case testPEP695GenericFunction] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +def f[T](x: T) -> T: + return x +def g[T](x: T, y: T) -> T: + return x +[file next.py] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +def f[T](x: T) -> T: + return x +def g[T, S](x: T, y: S) -> S: + return y +[out] +__main__.g + +[case testPEP695GenericClass] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +class C[T]: + pass +class D[T]: + pass +class E[T]: + pass +class F[T]: + def f(self, x: object) -> T: ... +[file next.py] +# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +class C[T]: + pass +class D[T: int]: + pass +class E: + pass +class F[T]: + def f(self, x: T) -> T: ... +[out] +__main__.D +__main__.E +__main__.F +__main__.F.f diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test new file mode 100644 index 000000000000..70cf427d6798 --- /dev/null +++ b/test-data/unit/fine-grained-python312.test @@ -0,0 +1,81 @@ +[case testPEP695TypeAliasDep] +# flags: --enable-incomplete-feature=NewGenericSyntax +import m +def g() -> m.C: + return m.f() +[file m.py] +type C = int + +def f() -> int: + pass +[file m.py.2] +type C = str + +def f() -> int: + pass +[out] +== +main:4: error: Incompatible return value type (got "int", expected "str") + +[case testPEP695ChangeOldStyleToNewStyleTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +from m import A +A() + +[file m.py] +A = int + +[file m.py.2] +type A = int +[typing fixtures/typing-full.pyi] +[builtins fixtures/tuple.pyi] +[out] +== +main:3: error: "TypeAliasType" not callable + +[case testPEP695VarianceChangesDueToDependency] +# flags: --enable-incomplete-feature=NewGenericSyntax +from a import C + +x: C[object] = C[int]() + +[file a.py] +from b import A + +class C[T]: + def f(self) -> A[T]: ... + +[file b.py] +class A[T]: + def f(self) -> T: ... + +[file b.py.2] +class A[T]: + def f(self) -> list[T]: ... + +[out] +== +main:4: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]") + +[case testPEP695TypeAliasChangesDueToDependency] +# flags: --enable-incomplete-feature=NewGenericSyntax +from a import A +x: A +x = 0 +x = '' + +[file a.py] +from b import B +type A = B[int, str] + +[file b.py] +from typing import Union as B + +[file b.py.2] +from builtins import tuple as B + +[builtins fixtures/tuple.pyi] +[out] +== +main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") +main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]") From ad0e180659f8bb1ef0c270045c655888b3b1223a Mon Sep 17 00:00:00 2001 From: GiorgosPapoutsakis <116210016+GiorgosPapoutsakis@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:38:17 +0300 Subject: [PATCH 0616/1617] Fix false positive for Final local scope variable in Protocol (#17308) This PR fixes and closes #17281 ,which reported a false positive when using Final within the local function scope of a protocol method. With these changes: - Local variables within protocol methods can be marked as Final. - Protocol members still cannot be marked as Final Modified ``semanal.py`` file and added a unit test in ``test-data/unit/check.final.test`` --------- Co-authored-by: Jelle Zijlstra Co-authored-by: Stanislav Terliakov --- mypy/semanal.py | 3 ++- test-data/unit/check-final.test | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e4f123cbfc20..2448ea8485f7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3505,7 +3505,8 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: if self.loop_depth[-1] > 0: self.fail("Cannot use Final inside a loop", s) if self.type and self.type.is_protocol: - self.msg.protocol_members_cant_be_final(s) + if self.is_class_scope(): + self.msg.protocol_members_cant_be_final(s) if ( isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 26a0d0782503..dadf76a283b0 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -301,6 +301,50 @@ class P(Protocol): pass [out] +[case testFinalInProtocol] +from typing import Final, Protocol, final + +class P(Protocol): + var1 : Final[int] = 0 # E: Protocol member cannot be final + + @final # E: Protocol member cannot be final + def meth1(self) -> None: + var2: Final = 0 + + def meth2(self) -> None: + var3: Final = 0 + + def meth3(self) -> None: + class Inner: + var3: Final = 0 # OK + + @final + def inner(self) -> None: ... + + class Inner: + var3: Final = 0 # OK + + @final + def inner(self) -> None: ... + +[out] + +[case testFinalWithClassVarInProtocol] +from typing import Protocol, Final, final, ClassVar + +class P(Protocol): + var1 : Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final + var2: ClassVar[int] = 1 + + @final # E: Protocol member cannot be final + def meth1(self) -> None: + ... + + def meth2(self) -> None: + var3: Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final # E: ClassVar can only be used for assignments in class body + +[out] + [case testFinalNotInLoops] from typing import Final From fbaa7e0121a180051c28d72bf1ed73f5c3c3b947 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 5 Jun 2024 10:49:13 +0100 Subject: [PATCH 0617/1617] [PEP 695] Add tests for type aliases with bounds and value restrictions (#17330) The functionality already works, but there was missing test coverage. Work on #15238. --- test-data/unit/check-python312.test | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 77276d9ee079..905012d9099c 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1360,3 +1360,42 @@ C: TypeAlias = int [typing fixtures/typing-full.pyi] [out2] tmp/a.py:2: error: "TypeAliasType" not callable + +[case testPEP695TypeAliasBoundAndValueChecking] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Any, cast + +class C: pass +class D(C): pass + +type A[T: C] = list[T] +a1: A +reveal_type(a1) # N: Revealed type is "builtins.list[Any]" +a2: A[Any] +a3: A[C] +a4: A[D] +a5: A[object] # E: Type argument "object" of "A" must be a subtype of "C" +a6: A[int] # E: Type argument "int" of "A" must be a subtype of "C" + +x1 = cast(A[C], a1) +x2 = cast(A[None], a1) # E: Type argument "None" of "A" must be a subtype of "C" + +type A2[T: (int, C)] = list[T] +b1: A2 +reveal_type(b1) # N: Revealed type is "builtins.list[Any]" +b2: A2[Any] +b3: A2[int] +b4: A2[C] +b5: A2[D] # E: Value of type variable "T" of "A2" cannot be "D" +b6: A2[object] # E: Value of type variable "T" of "A2" cannot be "object" + +list[A2[int]]() +list[A2[None]]() # E: Invalid type argument value for "A2" + +class N(int): pass + +type A3[T: C, S: (int, str)] = T | S +c1: A3[C, int] +c2: A3[D, str] +c3: A3[C, N] # E: Value of type variable "S" of "A3" cannot be "N" +c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" From ddcf90d1c43b078b8acc5a341074a1c9ab260569 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 5 Jun 2024 16:16:19 +0100 Subject: [PATCH 0618/1617] [PEP 695] Add tests for type alias in class body or function (#17334) Work on #15238. --- test-data/unit/check-python312.test | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 905012d9099c..2b67f56e679c 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1399,3 +1399,57 @@ c1: A3[C, int] c2: A3[D, str] c3: A3[C, N] # E: Value of type variable "S" of "A3" cannot be "N" c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" + +[case testPEP695TypeAliasInClassBodyOrFunction] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class C: + type A = int + type B[T] = list[T] | None + a: A + b: B[str] + + def method(self) -> None: + v: C.A + reveal_type(v) # N: Revealed type is "builtins.int" + +reveal_type(C.a) # N: Revealed type is "builtins.int" +reveal_type(C.b) # N: Revealed type is "Union[builtins.list[builtins.str], None]" + +C.A = str # E: Incompatible types in assignment (expression has type "Type[str]", variable has type "TypeAliasType") + +x: C.A +y: C.B[int] +reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(y) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + +def f() -> None: + type A = int + type B[T] = list[T] | None + a: A + reveal_type(a) # N: Revealed type is "builtins.int" + + def g() -> None: + b: B[int] + reveal_type(b) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + +class D: + def __init__(self) -> None: + type A = int + self.a: A = 0 + type B[T] = list[T] + self.b: B[int] = [1] + +reveal_type(D().a) # N: Revealed type is "builtins.int" +reveal_type(D().b) # N: Revealed type is "builtins.list[builtins.int]" + +class E[T]: + type X = list[T] # E: All type parameters should be declared ("T" not declared) + + def __init__(self) -> None: + type A = list[T] # E: All type parameters should be declared ("T" not declared) + self.a: A + +reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] From 6bdd854083604e4416123edf87a8f549356f783f Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Wed, 5 Jun 2024 23:32:42 +0200 Subject: [PATCH 0619/1617] Do not forget that a `TypedDict` was wrapped in `Unpack` after a `name-defined` error occurred. (#17226) Do not set the `unpacked_kwargs` attribute of `CallableType` to False when visiting a callable of which the `Unpack` wrapper of a `TypedDict` has already been removed. Fixes #17225 --- mypy/typeanal.py | 2 +- test-data/unit/check-typeddict.test | 38 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index bf53204ffce9..ded8b8412a9a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1030,7 +1030,7 @@ def visit_parameters(self, t: Parameters) -> Type: def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope with self.tvar_scope_frame(): - unpacked_kwargs = False + unpacked_kwargs = t.unpack_kwargs if self.defining_alias: variables = t.variables else: diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 09b86e4afd2d..5fb74f66dd89 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3487,3 +3487,41 @@ class A(Generic[T]): return self.a(x=1) [typing fixtures/typing-full.pyi] [builtins fixtures/tuple.pyi] + +[case testNameUndefinedErrorDoesNotLoseUnpackedKWArgsInformation] +from typing import overload +from typing_extensions import TypedDict, Unpack + +class TD(TypedDict, total=False): + x: int + y: str + +@overload +def f(self, *, x: int) -> None: ... +@overload +def f(self, *, y: str) -> None: ... +def f(self, **kwargs: Unpack[TD]) -> None: + z # E: Name "z" is not defined + +@overload +def g(self, *, x: float) -> None: ... +@overload +def g(self, *, y: str) -> None: ... +def g(self, **kwargs: Unpack[TD]) -> None: # E: Overloaded function implementation does not accept all possible arguments of signature 1 + z # E: Name "z" is not defined + +class A: + def f(self, *, x: int) -> None: ... + def g(self, *, x: float) -> None: ... +class B(A): + def f(self, **kwargs: Unpack[TD]) -> None: + z # E: Name "z" is not defined + def g(self, **kwargs: Unpack[TD]) -> None: # E: Signature of "g" incompatible with supertype "A" \ + # N: Superclass: \ + # N: def g(self, *, x: float) -> None \ + # N: Subclass: \ + # N: def g(*, x: int = ..., y: str = ...) -> None + z # E: Name "z" is not defined +reveal_type(B.f) # N: Revealed type is "def (self: __main__.B, **kwargs: Unpack[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: builtins.str})])" +B().f(x=1.0) # E: Argument "x" to "f" of "B" has incompatible type "float"; expected "int" +[builtins fixtures/primitives.pyi] From 668256364bc320a371e0a705436f1a700a724edc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 5 Jun 2024 23:11:41 +0100 Subject: [PATCH 0620/1617] Use namespaces for function type variables (#17311) Fixes https://github.com/python/mypy/issues/16582 IMO this is long overdue. Currently, type variable IDs are 99% unique, but when they accidentally clash, it causes hard to debug issues. The implementation is generally straightforward, but it uncovered a whole bunch of unrelated bugs. Few notes: * This still doesn't fix the type variables in nested generic callable types (those that appear in return types of another generic callable). It is non-trivial to put namespace there, and luckily this situation is already special-cased in `checkexpr.py` to avoid ID clashes. * This uncovered a bug in overloaded dunder overrides handling, fix is simple. * This also uncovered a deeper problem in unsafe overload overlap logic (w.r.t. partial parameters overlap). Here proper fix would be hard, so instead I tweak current logic so it will not cause false positives, at a cost of possible false negatives. * This makes explicit that we use a somewhat ad-hoc logic for join/meet of generic callables. FWIW I decided to keep it, since it seems to work reasonably well. * This accidentally highlighted two bugs in error message locations. One very old one related to type aliases, I fixed newly discovered cases by extending a previous partial fix. Second, the error locations generated by `partial` plugin were completely off (you can see examples in `mypy_primer` where there were errors on empty lines etc). * This PR (naturally) causes a significant amount of new valid errors (fixed false negatives). To improve the error messages, I extend the name disambiguation logic to include type variables (and also type aliases, while I am at it), previously it only applied to `Instance`s. Note that I use a notation `TypeVar@namespace`, which is a semantic equivalent of qualified name for type variables. For now, I shorten the namespace to only the last component, to make errors less verbose. We can reconsider this if it causes confusion. * Finally, this PR will hopefully allow a more principled implementation of https://github.com/python/mypy/issues/15907 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 4 +- mypy/checkexpr.py | 13 +++-- mypy/expandtype.py | 2 +- mypy/join.py | 31 ++++++++++ mypy/meet.py | 3 +- mypy/messages.py | 76 ++++++++++++++++++------- mypy/plugins/attrs.py | 17 +++--- mypy/plugins/dataclasses.py | 5 +- mypy/plugins/functools.py | 14 ++++- mypy/semanal.py | 19 ++++--- mypy/semanal_namedtuple.py | 31 ++++++---- mypy/semanal_shared.py | 4 +- mypy/subtypes.py | 16 ++++-- mypy/test/testtypes.py | 33 ++++++++--- mypy/test/typefixture.py | 9 +-- mypy/tvar_scope.py | 24 ++++---- mypy/typeanal.py | 15 +++-- mypy/types.py | 32 ++++++----- mypyc/test-data/fixtures/testutil.py | 4 +- test-data/unit/check-functions.test | 62 ++++++++++++++++++++ test-data/unit/check-functools.test | 4 +- test-data/unit/check-generics.test | 29 +++++----- test-data/unit/check-inference.test | 2 +- test-data/unit/check-python311.test | 2 +- test-data/unit/check-type-aliases.test | 11 ++++ test-data/unit/check-typevar-tuple.test | 3 +- 26 files changed, 332 insertions(+), 133 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 179ff6e0b4b6..38976d4ce15e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2171,7 +2171,9 @@ def bind_and_map_method( def get_op_other_domain(self, tp: FunctionLike) -> Type | None: if isinstance(tp, CallableType): if tp.arg_kinds and tp.arg_kinds[0] == ARG_POS: - return tp.arg_types[0] + # For generic methods, domain comparison is tricky, as a first + # approximation erase all remaining type variables to bounds. + return erase_typevars(tp.arg_types[0], {v.id for v in tp.variables}) return None elif isinstance(tp, Overloaded): raw_items = [self.get_op_other_domain(it) for it in tp.items] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f826d4c11dd3..8e6af0218c32 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -167,6 +167,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -4933,7 +4934,7 @@ def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: tv = TypeVarType( "T", "T", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5164,7 +5165,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: kt = TypeVarType( "KT", "KT", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5172,7 +5173,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: vt = TypeVarType( "VT", "VT", - id=-2, + id=TypeVarId(-2, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5564,7 +5565,7 @@ def check_generator_or_comprehension( tv = TypeVarType( "T", "T", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5591,7 +5592,7 @@ def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type: ktdef = TypeVarType( "KT", "KT", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5599,7 +5600,7 @@ def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type: vtdef = TypeVarType( "VT", "VT", - id=-2, + id=TypeVarId(-2, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/expandtype.py b/mypy/expandtype.py index f7fa0258f588..86875bc6079a 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -316,7 +316,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l new_unpack: Type if isinstance(var_arg_type, Instance): # we have something like Unpack[Tuple[Any, ...]] - new_unpack = var_arg + new_unpack = UnpackType(var_arg.type.accept(self)) elif isinstance(var_arg_type, TupleType): # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] expanded_tuple = var_arg_type.accept(self) diff --git a/mypy/join.py b/mypy/join.py index c711697ec46d..5284be7dd2a1 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -5,6 +5,7 @@ from typing import Sequence, overload import mypy.typeops +from mypy.expandtype import expand_type from mypy.maptype import map_instance_to_supertype from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY from mypy.state import state @@ -36,6 +37,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -718,7 +720,35 @@ def is_similar_callables(t: CallableType, s: CallableType) -> bool: ) +def update_callable_ids(c: CallableType, ids: list[TypeVarId]) -> CallableType: + tv_map = {} + tvs = [] + for tv, new_id in zip(c.variables, ids): + new_tv = tv.copy_modified(id=new_id) + tvs.append(new_tv) + tv_map[tv.id] = new_tv + return expand_type(c, tv_map).copy_modified(variables=tvs) + + +def match_generic_callables(t: CallableType, s: CallableType) -> tuple[CallableType, CallableType]: + # The case where we combine/join/meet similar callables, situation where both are generic + # requires special care. A more principled solution may involve unify_generic_callable(), + # but it would have two problems: + # * This adds risk of infinite recursion: e.g. join -> unification -> solver -> join + # * Using unification is an incorrect thing for meets, as it "widens" the types + # Finally, this effectively falls back to an old behaviour before namespaces were added to + # type variables, and it worked relatively well. + max_len = max(len(t.variables), len(s.variables)) + min_len = min(len(t.variables), len(s.variables)) + if min_len == 0: + return t, s + new_ids = [TypeVarId.new(meta_level=0) for _ in range(max_len)] + # Note: this relies on variables being in order they appear in function definition. + return update_callable_ids(t, new_ids), update_callable_ids(s, new_ids) + + def join_similar_callables(t: CallableType, s: CallableType) -> CallableType: + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_meet(t.arg_types[i], s.arg_types[i])) @@ -771,6 +801,7 @@ def safe_meet(t: Type, s: Type) -> Type: def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) diff --git a/mypy/meet.py b/mypy/meet.py index df8b960cdf3f..2d44cafb23b3 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1024,8 +1024,9 @@ def default(self, typ: Type) -> ProperType: def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType: - from mypy.join import safe_join + from mypy.join import match_generic_callables, safe_join + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) diff --git a/mypy/messages.py b/mypy/messages.py index de079feda048..f01b0a726584 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -83,6 +83,7 @@ TypeOfAny, TypeStrVisitor, TypeType, + TypeVarLikeType, TypeVarTupleType, TypeVarType, UnboundType, @@ -2502,14 +2503,16 @@ def format_literal_value(typ: LiteralType) -> str: return typ.value_repr() if isinstance(typ, TypeAliasType) and typ.is_recursive: - # TODO: find balance here, str(typ) doesn't support custom verbosity, and may be - # too verbose for user messages, OTOH it nicely shows structure of recursive types. - if verbosity < 2: - type_str = typ.alias.name if typ.alias else "" + if typ.alias is None: + type_str = "" + else: + if verbosity >= 2 or (fullnames and typ.alias.fullname in fullnames): + type_str = typ.alias.fullname + else: + type_str = typ.alias.name if typ.args: type_str += f"[{format_list(typ.args)}]" - return type_str - return str(typ) + return type_str # TODO: always mention type alias names in errors. typ = get_proper_type(typ) @@ -2550,9 +2553,15 @@ def format_literal_value(typ: LiteralType) -> str: return f"Unpack[{format(typ.type)}]" elif isinstance(typ, TypeVarType): # This is similar to non-generic instance types. + fullname = scoped_type_var_name(typ) + if verbosity >= 2 or (fullnames and fullname in fullnames): + return fullname return typ.name elif isinstance(typ, TypeVarTupleType): # This is similar to non-generic instance types. + fullname = scoped_type_var_name(typ) + if verbosity >= 2 or (fullnames and fullname in fullnames): + return fullname return typ.name elif isinstance(typ, ParamSpecType): # Concatenate[..., P] @@ -2563,6 +2572,7 @@ def format_literal_value(typ: LiteralType) -> str: return f"[{args}, **{typ.name_with_suffix()}]" else: + # TODO: better disambiguate ParamSpec name clashes. return typ.name_with_suffix() elif isinstance(typ, TupleType): # Prefer the name of the fallback class (if not tuple), as it's more informative. @@ -2680,29 +2690,51 @@ def format_literal_value(typ: LiteralType) -> str: return "object" -def collect_all_instances(t: Type) -> list[Instance]: - """Return all instances that `t` contains (including `t`). +def collect_all_named_types(t: Type) -> list[Type]: + """Return all instances/aliases/type variables that `t` contains (including `t`). This is similar to collect_all_inner_types from typeanal but only returns instances and will recurse into fallbacks. """ - visitor = CollectAllInstancesQuery() + visitor = CollectAllNamedTypesQuery() t.accept(visitor) - return visitor.instances + return visitor.types -class CollectAllInstancesQuery(TypeTraverserVisitor): +class CollectAllNamedTypesQuery(TypeTraverserVisitor): def __init__(self) -> None: - self.instances: list[Instance] = [] + self.types: list[Type] = [] def visit_instance(self, t: Instance) -> None: - self.instances.append(t) + self.types.append(t) super().visit_instance(t) def visit_type_alias_type(self, t: TypeAliasType) -> None: if t.alias and not t.is_recursive: - t.alias.target.accept(self) - super().visit_type_alias_type(t) + get_proper_type(t).accept(self) + else: + self.types.append(t) + super().visit_type_alias_type(t) + + def visit_type_var(self, t: TypeVarType) -> None: + self.types.append(t) + super().visit_type_var(t) + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + self.types.append(t) + super().visit_type_var_tuple(t) + + def visit_param_spec(self, t: ParamSpecType) -> None: + self.types.append(t) + super().visit_param_spec(t) + + +def scoped_type_var_name(t: TypeVarLikeType) -> str: + if not t.id.namespace: + return t.name + # TODO: support rare cases when both TypeVar name and namespace suffix coincide. + *_, suffix = t.id.namespace.split(".") + return f"{t.name}@{suffix}" def find_type_overlaps(*types: Type) -> set[str]: @@ -2713,8 +2745,14 @@ def find_type_overlaps(*types: Type) -> set[str]: """ d: dict[str, set[str]] = {} for type in types: - for inst in collect_all_instances(type): - d.setdefault(inst.type.name, set()).add(inst.type.fullname) + for t in collect_all_named_types(type): + if isinstance(t, ProperType) and isinstance(t, Instance): + d.setdefault(t.type.name, set()).add(t.type.fullname) + elif isinstance(t, TypeAliasType) and t.alias: + d.setdefault(t.alias.name, set()).add(t.alias.fullname) + else: + assert isinstance(t, TypeVarLikeType) + d.setdefault(t.name, set()).add(scoped_type_var_name(t)) for shortname in d.keys(): if f"typing.{shortname}" in TYPES_FOR_UNIMPORTED_HINTS: d[shortname].add(f"typing.{shortname}") @@ -2732,7 +2770,7 @@ def format_type( """ Convert a type to a relatively short string suitable for error messages. - `verbosity` is a coarse grained control on the verbosity of the type + `verbosity` is a coarse-grained control on the verbosity of the type This function returns a string appropriate for unmodified use in error messages; this means that it will be quoted in most cases. If @@ -2748,7 +2786,7 @@ def format_type_bare( """ Convert a type to a relatively short string suitable for error messages. - `verbosity` is a coarse grained control on the verbosity of the type + `verbosity` is a coarse-grained control on the verbosity of the type `fullnames` specifies a set of names that should be printed in full This function will return an unquoted string. If a caller doesn't need to diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 83f685f57a16..db976385ee56 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -69,6 +69,7 @@ Type, TypeOfAny, TypeType, + TypeVarId, TypeVarType, UninhabitedType, UnionType, @@ -807,25 +808,25 @@ def _add_order(ctx: mypy.plugin.ClassDefContext, adder: MethodAdder) -> None: # AT = TypeVar('AT') # def __lt__(self: AT, other: AT) -> bool # This way comparisons with subclasses will work correctly. + fullname = f"{ctx.cls.info.fullname}.{SELF_TVAR_NAME}" tvd = TypeVarType( SELF_TVAR_NAME, - ctx.cls.info.fullname + "." + SELF_TVAR_NAME, - id=-1, + fullname, + # Namespace is patched per-method below. + id=TypeVarId(-1, namespace=""), values=[], upper_bound=object_type, default=AnyType(TypeOfAny.from_omitted_generics), ) self_tvar_expr = TypeVarExpr( - SELF_TVAR_NAME, - ctx.cls.info.fullname + "." + SELF_TVAR_NAME, - [], - object_type, - AnyType(TypeOfAny.from_omitted_generics), + SELF_TVAR_NAME, fullname, [], object_type, AnyType(TypeOfAny.from_omitted_generics) ) ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) - args = [Argument(Var("other", tvd), tvd, None, ARG_POS)] for method in ["__lt__", "__le__", "__gt__", "__ge__"]: + namespace = f"{ctx.cls.info.fullname}.{method}" + tvd = tvd.copy_modified(id=TypeVarId(tvd.id.raw_id, namespace=namespace)) + args = [Argument(Var("other", tvd), tvd, None, ARG_POS)] adder.add_method(method, args, bool_type, self_type=tvd, tvd=tvd) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dead512a2202..dd2eceab217f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -65,6 +65,7 @@ TupleType, Type, TypeOfAny, + TypeVarId, TypeVarType, UninhabitedType, UnionType, @@ -314,8 +315,8 @@ def transform(self) -> bool: obj_type = self._api.named_type("builtins.object") order_tvar_def = TypeVarType( SELF_TVAR_NAME, - info.fullname + "." + SELF_TVAR_NAME, - id=-1, + f"{info.fullname}.{SELF_TVAR_NAME}", + id=TypeVarId(-1, namespace=f"{info.fullname}.{method_name}"), values=[], upper_bound=obj_type, default=AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 81a3b4d96ef3..335123a4a108 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -7,7 +7,7 @@ import mypy.checker import mypy.plugin from mypy.argmap import map_actuals_to_formals -from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, FuncItem, Var +from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var from mypy.plugins.common import add_method_to_class from mypy.types import ( AnyType, @@ -151,12 +151,22 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: actual_arg_names = [a for param in ctx.arg_names[1:] for a in param] actual_types = [a for param in ctx.arg_types[1:] for a in param] + # Create a valid context for various ad-hoc inspections in check_call(). + call_expr = CallExpr( + callee=ctx.args[0][0], + args=actual_args, + arg_kinds=actual_arg_kinds, + arg_names=actual_arg_names, + analyzed=ctx.context.analyzed if isinstance(ctx.context, CallExpr) else None, + ) + call_expr.set_line(ctx.context) + _, bound = ctx.api.expr_checker.check_call( callee=defaulted, args=actual_args, arg_kinds=actual_arg_kinds, arg_names=actual_arg_names, - context=defaulted, + context=call_expr, ) bound = get_proper_type(bound) if not isinstance(bound, CallableType): diff --git a/mypy/semanal.py b/mypy/semanal.py index 2448ea8485f7..8505b3a9ccac 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -279,6 +279,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -894,7 +895,7 @@ def analyze_func_def(self, defn: FuncDef) -> None: self.prepare_method_signature(defn, self.type, has_self_type) # Analyze function signature - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): if defn.type: self.check_classvar_in_signature(defn.type) assert isinstance(defn.type, CallableType) @@ -902,7 +903,9 @@ def analyze_func_def(self, defn: FuncDef) -> None: # class-level imported names and type variables are in scope. analyzer = self.type_analyzer() tag = self.track_incomplete_refs() - result = analyzer.visit_callable_type(defn.type, nested=False) + result = analyzer.visit_callable_type( + defn.type, nested=False, namespace=defn.fullname + ) # Don't store not ready types (including placeholders). if self.found_incomplete_ref(tag) or has_placeholder(result): self.defer(defn) @@ -1114,7 +1117,7 @@ def update_function_type_variables(self, fun_type: CallableType, defn: FuncItem) if defn is generic. Return True, if the signature contains typing.Self type, or False otherwise. """ - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): a = self.type_analyzer() fun_type.variables, has_self_type = a.bind_function_type_variables(fun_type, defn) if has_self_type and self.type is not None: @@ -1152,7 +1155,7 @@ def setup_self_type(self) -> None: info.self_type = TypeVarType( "Self", f"{info.fullname}.Self", - id=0, + id=TypeVarId(0), # 0 is a special value for self-types. values=[], upper_bound=fill_typevars(info), default=AnyType(TypeOfAny.from_omitted_generics), @@ -1441,7 +1444,7 @@ def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> Non self.add_symbol(func.name, func, func) def analyze_arg_initializers(self, defn: FuncItem) -> None: - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): # Analyze default arguments for arg in defn.arguments: if arg.initializer: @@ -1449,7 +1452,7 @@ def analyze_arg_initializers(self, defn: FuncItem) -> None: def analyze_function_body(self, defn: FuncItem) -> None: is_method = self.is_class_scope() - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): # Bind the type variables again to visit the body. if defn.type: a = self.type_analyzer() @@ -3930,7 +3933,9 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: or (self.options.python_version >= (3, 10) or self.is_stub_file) ): # Note: CallExpr is for "void = type(None)" and OpExpr is for "X | Y" union syntax. - s.rvalue.analyzed = TypeAliasExpr(alias_node) + if not isinstance(s.rvalue.analyzed, TypeAliasExpr): + # Any existing node will be updated in-place below. + s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors s.rvalue.analyzed.column = res.column diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 753deafe103b..768dd265b338 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -62,6 +62,7 @@ Type, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarType, UnboundType, @@ -569,27 +570,33 @@ def add_field( add_field(Var("__match_args__", match_args_type), is_initialized_in_class=True) assert info.tuple_type is not None # Set by update_tuple_type() above. - tvd = TypeVarType( + shared_self_type = TypeVarType( name=SELF_TVAR_NAME, - fullname=info.fullname + "." + SELF_TVAR_NAME, + fullname=f"{info.fullname}.{SELF_TVAR_NAME}", + # Namespace is patched per-method below. id=self.api.tvar_scope.new_unique_func_id(), values=[], upper_bound=info.tuple_type, default=AnyType(TypeOfAny.from_omitted_generics), ) - selftype = tvd def add_method( funcname: str, - ret: Type, + ret: Type | None, # None means use (patched) self-type args: list[Argument], is_classmethod: bool = False, is_new: bool = False, ) -> None: + fullname = f"{info.fullname}.{funcname}" + self_type = shared_self_type.copy_modified( + id=TypeVarId(shared_self_type.id.raw_id, namespace=fullname) + ) + if ret is None: + ret = self_type if is_classmethod or is_new: - first = [Argument(Var("_cls"), TypeType.make_normalized(selftype), None, ARG_POS)] + first = [Argument(Var("_cls"), TypeType.make_normalized(self_type), None, ARG_POS)] else: - first = [Argument(Var("_self"), selftype, None, ARG_POS)] + first = [Argument(Var("_self"), self_type, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] @@ -597,12 +604,12 @@ def add_method( arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) - signature.variables = [tvd] + signature.variables = [self_type] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) - func._fullname = info.fullname + "." + funcname + func._fullname = fullname func.line = line if is_classmethod: v = Var(funcname, func.type) @@ -620,13 +627,13 @@ def add_method( add_method( "_replace", - ret=selftype, + ret=None, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) if self.options.python_version >= (3, 13): add_method( "__replace__", - ret=selftype, + ret=None, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) @@ -635,11 +642,11 @@ def make_init_arg(var: Var) -> Argument: kind = ARG_POS if default is None else ARG_OPT return Argument(var, var.type, default, kind) - add_method("__new__", ret=selftype, args=[make_init_arg(var) for var in vars], is_new=True) + add_method("__new__", ret=None, args=[make_init_arg(var) for var in vars], is_new=True) add_method("_asdict", args=[], ret=ordereddictype) add_method( "_make", - ret=selftype, + ret=None, is_classmethod=True, args=[Argument(Var("iterable", iterable_type), iterable_type, None, ARG_POS)], ) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index b5ec2bb52a0d..01d8e9aafffb 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -314,7 +314,7 @@ def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> def paramspec_args( name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, *, named_type_func: _NamedTypeCallback, line: int = -1, @@ -337,7 +337,7 @@ def paramspec_args( def paramspec_kwargs( name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, *, named_type_func: _NamedTypeCallback, line: int = -1, diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a5523fbe0d45..971caa3991ae 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -944,7 +944,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: # When it is the same overload, then the types are equal. return True - # Ensure each overload in the right side (the supertype) is accounted for. + # Ensure each overload on the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() @@ -1792,7 +1792,9 @@ def are_args_compatible( # If both arguments are required allow_partial_overlap has no effect. allow_partial_overlap = False - def is_different(left_item: object | None, right_item: object | None) -> bool: + def is_different( + left_item: object | None, right_item: object | None, allow_overlap: bool + ) -> bool: """Checks if the left and right items are different. If the right item is unspecified (e.g. if the right callable doesn't care @@ -1802,19 +1804,21 @@ def is_different(left_item: object | None, right_item: object | None) -> bool: if the left callable also doesn't care.""" if right_item is None: return False - if allow_partial_overlap and left_item is None: + if allow_overlap and left_item is None: return False return left_item != right_item # If right has a specific name it wants this argument to be, left must # have the same. - if is_different(left.name, right.name): + if is_different(left.name, right.name, allow_partial_overlap): # But pay attention to whether we're ignoring positional arg names if not ignore_pos_arg_names or right.pos is None: return False - # If right is at a specific position, left must have the same: - if is_different(left.pos, right.pos) and not allow_imprecise_kinds: + # If right is at a specific position, left must have the same. + # TODO: partial overlap logic is flawed for positions. + # We disable it to avoid false positives at a cost of few false negatives. + if is_different(left.pos, right.pos, allow_overlap=False) and not allow_imprecise_kinds: return False # If right's argument is optional, left's must also be diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index b3f84905c47e..0218d33cc124 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -144,7 +144,11 @@ def test_tuple_type_upper(self) -> None: def test_type_variable_binding(self) -> None: assert_equal( - str(TypeVarType("X", "X", 1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics))), + str( + TypeVarType( + "X", "X", TypeVarId(1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) + ), "X`1", ) assert_equal( @@ -152,7 +156,7 @@ def test_type_variable_binding(self) -> None: TypeVarType( "X", "X", - 1, + TypeVarId(1), [self.x, self.y], self.fx.o, AnyType(TypeOfAny.from_omitted_generics), @@ -170,14 +174,25 @@ def test_generic_function_type(self) -> None: self.function, name=None, variables=[ - TypeVarType("X", "X", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + TypeVarType( + "X", + "X", + TypeVarId(-1), + [], + self.fx.o, + AnyType(TypeOfAny.from_omitted_generics), + ) ], ) assert_equal(str(c), "def [X] (X?, Y?) -> Y?") v = [ - TypeVarType("Y", "Y", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)), - TypeVarType("X", "X", -2, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)), + TypeVarType( + "Y", "Y", TypeVarId(-1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ), + TypeVarType( + "X", "X", TypeVarId(-2), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ), ] c2 = CallableType([], [], [], NoneType(), self.function, name=None, variables=v) assert_equal(str(c2), "def [Y, X] ()") @@ -205,7 +220,9 @@ def test_type_alias_expand_all(self) -> None: def test_recursive_nested_in_non_recursive(self) -> None: A, _ = self.fx.def_alias_1(self.fx.a) - T = TypeVarType("T", "T", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + T = TypeVarType( + "T", "T", TypeVarId(-1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) NA = self.fx.non_rec_alias(Instance(self.fx.gi, [T]), [T], [A]) assert not NA.is_recursive assert has_recursive_types(NA) @@ -657,7 +674,9 @@ def callable(self, vars: list[str], *a: Type) -> CallableType: n = -1 for v in vars: tv.append( - TypeVarType(v, v, n, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + TypeVarType( + v, v, TypeVarId(n), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) ) n -= 1 return CallableType( diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index b7bde16e6be2..5a813f70117c 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -30,6 +30,7 @@ TypeAliasType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -57,7 +58,7 @@ def make_type_var( return TypeVarType( name, name, - id, + TypeVarId(id), values, upper_bound, AnyType(TypeOfAny.from_omitted_generics), @@ -227,7 +228,7 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy return TypeVarTupleType( name, name, - id, + TypeVarId(id), upper_bound, self.std_tuple, AnyType(TypeOfAny.from_omitted_generics), @@ -325,7 +326,7 @@ def make_type_info( TypeVarTupleType( n, n, - id, + TypeVarId(id), self.std_tuple.copy_modified(args=[self.o]), self.std_tuple.copy_modified(args=[self.o]), AnyType(TypeOfAny.from_omitted_generics), @@ -340,7 +341,7 @@ def make_type_info( TypeVarType( n, n, - id, + TypeVarId(id), [], self.o, AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index 4dc663df0399..fe97a8359287 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -85,29 +85,27 @@ def allow_binding(self, fullname: str) -> bool: return False return True - def method_frame(self) -> TypeVarLikeScope: + def method_frame(self, namespace: str) -> TypeVarLikeScope: """A new scope frame for binding a method""" - return TypeVarLikeScope(self, False, None) + return TypeVarLikeScope(self, False, None, namespace=namespace) def class_frame(self, namespace: str) -> TypeVarLikeScope: """A new scope frame for binding a class. Prohibits *this* class's tvars""" return TypeVarLikeScope(self.get_function_scope(), True, self, namespace=namespace) - def new_unique_func_id(self) -> int: + def new_unique_func_id(self) -> TypeVarId: """Used by plugin-like code that needs to make synthetic generic functions.""" self.func_id -= 1 - return self.func_id + return TypeVarId(self.func_id) def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: if self.is_class_scope: self.class_id += 1 i = self.class_id - namespace = self.namespace else: self.func_id -= 1 i = self.func_id - # TODO: Consider also using namespaces for functions - namespace = "" + namespace = self.namespace tvar_expr.default.accept(TypeVarLikeNamespaceSetter(namespace)) if isinstance(tvar_expr, TypeVarExpr): @@ -124,9 +122,9 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: ) elif isinstance(tvar_expr, ParamSpecExpr): tvar_def = ParamSpecType( - name, - tvar_expr.fullname, - i, + name=name, + fullname=tvar_expr.fullname, + id=TypeVarId(i, namespace=namespace), flavor=ParamSpecFlavor.BARE, upper_bound=tvar_expr.upper_bound, default=tvar_expr.default, @@ -135,9 +133,9 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: ) elif isinstance(tvar_expr, TypeVarTupleExpr): tvar_def = TypeVarTupleType( - name, - tvar_expr.fullname, - i, + name=name, + fullname=tvar_expr.fullname, + id=TypeVarId(i, namespace=namespace), upper_bound=tvar_expr.upper_bound, tuple_fallback=tvar_expr.tuple_fallback, default=tvar_expr.default, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index ded8b8412a9a..a513b0716a01 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1027,9 +1027,12 @@ def visit_unpack_type(self, t: UnpackType) -> Type: def visit_parameters(self, t: Parameters) -> Type: raise NotImplementedError("ParamSpec literals cannot have unbound TypeVars") - def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: + def visit_callable_type( + self, t: CallableType, nested: bool = True, namespace: str = "" + ) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope - with self.tvar_scope_frame(): + # TODO: attach namespace for nested free type variables (these appear in return type only). + with self.tvar_scope_frame(namespace=namespace): unpacked_kwargs = t.unpack_kwargs if self.defining_alias: variables = t.variables @@ -1432,7 +1435,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type: ) else: # Callable[P, RET] (where P is ParamSpec) - with self.tvar_scope_frame(): + with self.tvar_scope_frame(namespace=""): # Temporarily bind ParamSpecs to allow code like this: # my_fun: Callable[Q, Foo[Q]] # We usually do this later in visit_callable_type(), but the analysis @@ -1648,9 +1651,9 @@ def note(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None self.note_func(msg, ctx, code=code) @contextmanager - def tvar_scope_frame(self) -> Iterator[None]: + def tvar_scope_frame(self, namespace: str) -> Iterator[None]: old_scope = self.tvar_scope - self.tvar_scope = self.tvar_scope.method_frame() + self.tvar_scope = self.tvar_scope.method_frame(namespace) yield self.tvar_scope = old_scope @@ -1795,7 +1798,7 @@ def anal_var_def(self, var_def: TypeVarLikeType) -> TypeVarLikeType: return TypeVarType( name=var_def.name, fullname=var_def.fullname, - id=var_def.id.raw_id, + id=var_def.id, values=self.anal_array(var_def.values), upper_bound=var_def.upper_bound.accept(self), default=var_def.default.accept(self), diff --git a/mypy/types.py b/mypy/types.py index 2cacc3e44085..cdcb26f435b8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -510,9 +510,8 @@ class TypeVarId: # Class variable used for allocating fresh ids for metavariables. next_raw_id: ClassVar[int] = 1 - # Fullname of class (or potentially function in the future) which - # declares this type variable (not the fullname of the TypeVar - # definition!), or '' + # Fullname of class or function/method which declares this type + # variable (not the fullname of the TypeVar definition!), or '' namespace: str def __init__(self, raw_id: int, meta_level: int = 0, *, namespace: str = "") -> None: @@ -560,7 +559,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, upper_bound: Type, default: Type, line: int = -1, @@ -569,8 +568,6 @@ def __init__( super().__init__(line, column) self.name = name self.fullname = fullname - if isinstance(id, int): - id = TypeVarId(id) self.id = id self.upper_bound = upper_bound self.default = default @@ -607,7 +604,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, values: list[Type], upper_bound: Type, default: Type, @@ -626,7 +623,7 @@ def copy_modified( values: Bogus[list[Type]] = _dummy, upper_bound: Bogus[Type] = _dummy, default: Bogus[Type] = _dummy, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, line: int = _dummy_int, column: int = _dummy_int, **kwargs: Any, @@ -722,7 +719,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, flavor: int, upper_bound: Type, default: Type, @@ -749,7 +746,7 @@ def with_flavor(self, flavor: int) -> ParamSpecType: def copy_modified( self, *, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, flavor: int = _dummy_int, prefix: Bogus[Parameters] = _dummy, default: Bogus[Type] = _dummy, @@ -794,6 +791,7 @@ def serialize(self) -> JsonDict: "name": self.name, "fullname": self.fullname, "id": self.id.raw_id, + "namespace": self.id.namespace, "flavor": self.flavor, "upper_bound": self.upper_bound.serialize(), "default": self.default.serialize(), @@ -806,7 +804,7 @@ def deserialize(cls, data: JsonDict) -> ParamSpecType: return ParamSpecType( data["name"], data["fullname"], - data["id"], + TypeVarId(data["id"], namespace=data["namespace"]), data["flavor"], deserialize_type(data["upper_bound"]), deserialize_type(data["default"]), @@ -826,7 +824,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, upper_bound: Type, tuple_fallback: Instance, default: Type, @@ -848,6 +846,7 @@ def serialize(self) -> JsonDict: "name": self.name, "fullname": self.fullname, "id": self.id.raw_id, + "namespace": self.id.namespace, "upper_bound": self.upper_bound.serialize(), "tuple_fallback": self.tuple_fallback.serialize(), "default": self.default.serialize(), @@ -860,7 +859,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType: return TypeVarTupleType( data["name"], data["fullname"], - data["id"], + TypeVarId(data["id"], namespace=data["namespace"]), deserialize_type(data["upper_bound"]), Instance.deserialize(data["tuple_fallback"]), deserialize_type(data["default"]), @@ -881,7 +880,7 @@ def __eq__(self, other: object) -> bool: def copy_modified( self, *, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, upper_bound: Bogus[Type] = _dummy, default: Bogus[Type] = _dummy, min_len: Bogus[int] = _dummy, @@ -3499,6 +3498,11 @@ def visit_instance(self, typ: Instance) -> None: typ.column = self.column super().visit_instance(typ) + def visit_type_alias_type(self, typ: TypeAliasType) -> None: + typ.line = self.line + typ.column = self.column + super().visit_type_alias_type(typ) + class HasTypeVars(BoolTypeQuery): def __init__(self) -> None: diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index 7f00ee5aea00..f210faf71109 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -5,7 +5,7 @@ import math from typing import ( Any, Iterator, TypeVar, Generator, Optional, List, Tuple, Sequence, - Union, Callable, Awaitable, + Union, Callable, Awaitable, Generic ) from typing import Final @@ -86,7 +86,7 @@ def run_generator(gen: Generator[T, V, U], F = TypeVar('F', bound=Callable) -class async_val(Awaitable[V]): +class async_val(Awaitable[V], Generic[T, V]): def __init__(self, val: T) -> None: self.val = val diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index fe01590c6c71..917b74fd2147 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3240,3 +3240,65 @@ class Base: class Derived(Base): def foo(self): # E: Cannot override final attribute "foo" (previously declared in base class "Base") pass + +[case testTypeVarIdClashPolymorphic] +from typing import Callable, Generic, TypeVar + +A = TypeVar("A") +B = TypeVar("B") + +class Gen(Generic[A]): ... + +def id_(x: A) -> A: ... +def f(x: Gen[A], y: A) -> Gen[Gen[A]]: ... +def g(x: Gen[A], id_: Callable[[B], B], f: Callable[[A, B], Gen[A]]) -> A: ... + +def test(x: Gen[Gen[A]]) -> Gen[A]: + return g(x, id_, f) # Technically OK + +x: Gen[Gen[int]] +reveal_type(g(x, id_, f)) # N: Revealed type is "__main__.Gen[builtins.int]" + +def h(x: A, y: A) -> A: ... +def gn(id_: Callable[[B], B], step: Callable[[A, B], A]) -> A: ... + +def fn(x: A) -> A: + return gn(id_, h) # Technically OK + +[case testTypeVarIdsNested] +from typing import Callable, TypeVar + +A = TypeVar("A") +B = TypeVar("B") + +def f(x: Callable[[A], A]) -> Callable[[B], B]: + def g(x: B) -> B: ... + return g + +reveal_type(f(f)) # N: Revealed type is "def [B] (B`1) -> B`1" +reveal_type(f(f)(f)) # N: Revealed type is "def [A] (x: def (A`-1) -> A`-1) -> def [B] (B`-2) -> B`-2" + +[case testGenericUnionFunctionJoin] +from typing import TypeVar, Union + +T = TypeVar("T") +S = TypeVar("S") + +def f(x: T, y: S) -> Union[T, S]: ... +def g(x: T, y: S) -> Union[T, S]: ... + +x = [f, g] +reveal_type(x) # N: Revealed type is "builtins.list[def [T, S] (x: T`4, y: S`5) -> Union[T`4, S`5]]" +[builtins fixtures/list.pyi] + +[case testTypeVariableClashErrorMessage] +from typing import TypeVar + +T = TypeVar("T") + +class C: # Note: Generic[T] missing + def bad_idea(self, x: T) -> None: + self.x = x + + def nope(self, x: T) -> None: + self.x = x # E: Incompatible types in assignment (expression has type "T@nope", variable has type "T@bad_idea") diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 38083ad98f21..283500f25a7d 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -183,8 +183,8 @@ p3(1, 3) # E: Too many positional arguments for "foo" \ # E: Argument 2 to "foo" has incompatible type "int"; expected "str" functools.partial(foo, "a") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" -functools.partial(foo, b=1) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" -functools.partial(foo, a=1, b=2, c=3) # E: Argument 2 to "foo" has incompatible type "int"; expected "str" +functools.partial(foo, b=1) # E: Argument "b" to "foo" has incompatible type "int"; expected "str" +functools.partial(foo, a=1, b=2, c=3) # E: Argument "b" to "foo" has incompatible type "int"; expected "str" functools.partial(1) # E: "int" not callable \ # E: Argument 1 to "partial" has incompatible type "int"; expected "Callable[..., Never]" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b1d1ff3f46a1..bd327745e2ed 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1608,17 +1608,17 @@ if int(): if int(): y1 = f3 if int(): - y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], A]") + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A@f4]", variable has type "Callable[[A@f1], A@f1]") y2 = f2 if int(): y2 = f2 if int(): - y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f2], B]") if int(): - y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], B]") + y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B@f3], B@f3]", variable has type "Callable[[A], B@f2]") if int(): - y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], B]") + y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A@f4]", variable has type "Callable[[A@f2], B]") y3 = f3 if int(): @@ -1634,7 +1634,7 @@ y4 = f4 if int(): y4 = f4 if int(): - y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[int], A]") + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[int], A@f4]") if int(): y4 = f2 if int(): @@ -1655,26 +1655,26 @@ def outer(t: T) -> None: y1 = f1 if int(): y1 = f2 - y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], A]") - y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[A], A]") + y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A@f3]", variable has type "Callable[[A@f1], A@f1]") + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A@f4], T]", variable has type "Callable[[A@f1], A@f1]") y1 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], A]") y2 = f2 if int(): - y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f2], B]") y3 = f3 if int(): - y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[T], A]") + y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[T], A@f3]") y3 = f2 - y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[T], A]") + y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A@f4], T]", variable has type "Callable[[T], A@f3]") y3 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[T], A]") y4 = f4 if int(): - y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], T]") + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f4], T]") y4 = f2 - y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], T]") + y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A@f3]", variable has type "Callable[[A@f4], T]") y4 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], T]") y5 = f5 @@ -1683,7 +1683,6 @@ def outer(t: T) -> None: y5 = f2 y5 = f3 y5 = f4 -[out] [case testSubtypingWithGenericFunctionUsingTypevarWithValues] from typing import TypeVar, Callable @@ -2928,8 +2927,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]: def id(__x: U) -> U: ... fs = [id, id, id] -reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`3) -> builtins.list[S`3]" -reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]" +reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]" +reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCurry] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 08b53ab16972..fcd03f8efe01 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -988,7 +988,7 @@ a = k2 if int(): a = k2 if int(): - a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T]], List[Union[T, int]]]", variable has type "Callable[[S, List[T]], List[Union[T, int]]]") + a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T@k1]], List[Union[T@k1, int]]]", variable has type "Callable[[S, List[T@k2]], List[Union[T@k2, int]]]") b = k1 if int(): b = k1 diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 2d1a09ef3336..28951824999f 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -171,5 +171,5 @@ reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int" IntList = List[int] Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple) x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 -reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +reveal_type(x4) # N: Revealed type is "def (*Any) -> builtins.int" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index c13331e0a61b..86bd4422003b 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1220,3 +1220,14 @@ Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type vari # N: Don't Unpack type variables in type_params [builtins fixtures/tuple.pyi] + +[case testAliasInstanceNameClash] +from lib import func +class A: ... +func(A()) # E: Argument 1 to "func" has incompatible type "__main__.A"; expected "lib.A" +[file lib.py] +from typing import List, Union + +A = Union[int, List[A]] +def func(x: A) -> int: ... +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index f704e3c5c713..21415abb9c28 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -881,7 +881,8 @@ y: B z: C reveal_type(x) # N: Revealed type is "Any" reveal_type(y) # N: Revealed type is "Any" -reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[Any]]" +reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" + [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicWithBadType] From f989414d9db8c94268e593200ab94b4aec2cc3d3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Jun 2024 10:34:06 +0100 Subject: [PATCH 0621/1617] [PEP 695] Don't crash when redefining something as a type alias (#17335) Generate an error instead. Work on #15238. --- mypy/semanal.py | 9 +++++++- test-data/unit/check-python312.test | 36 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8505b3a9ccac..8592a6f05e1f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5323,6 +5323,14 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: all_type_params_names = [p.name for p in s.type_args] try: + existing = self.current_symbol_table().get(s.name.name) + if existing and not ( + isinstance(existing.node, TypeAlias) + or (isinstance(existing.node, PlaceholderNode) and existing.node.line == s.line) + ): + self.already_defined(s.name.name, s, existing, "Name") + return + tag = self.track_incomplete_refs() res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( s.name.name, @@ -5378,7 +5386,6 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: python_3_12_type_alias=True, ) - existing = self.current_symbol_table().get(s.name.name) if ( existing and isinstance(existing.node, (PlaceholderNode, TypeAlias)) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 2b67f56e679c..52f77243fd0a 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1453,3 +1453,39 @@ class E[T]: reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695RedefineAsTypeAlias1] +# flags: --enable-incomplete-feature=NewGenericSyntax +class C: pass +type C = int # E: Name "C" already defined on line 2 + +A = 0 +type A = str # E: Name "A" already defined on line 5 +reveal_type(A) # N: Revealed type is "builtins.int" + +[case testPEP695RedefineAsTypeAlias2] +# flags: --enable-incomplete-feature=NewGenericSyntax +from m import D +type D = int # E: Name "D" already defined (possibly by an import) +a: D +reveal_type(a) # N: Revealed type is "m.D" +[file m.py] +class D: pass + +[case testPEP695RedefineAsTypeAlias3] +# flags: --enable-incomplete-feature=NewGenericSyntax +D = list["Forward"] +type D = int # E: Name "D" already defined on line 2 +Forward = str +x: D +reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" + +[case testPEP695MultiDefinitionsForTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +if int(): + type A[T] = list[T] +else: + type A[T] = str # E: Name "A" already defined on line 3 +x: T # E: Name "T" is not defined +a: A[int] +reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" From 09c48a4d9da2b0d8bf5ce116ca35d51a46c415f2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Jun 2024 10:35:37 +0100 Subject: [PATCH 0622/1617] [mypyc] Inline tagged integer arithmetic and bitwise operations (#17265) Inline the fast path of various tagged integer operations by using C inline functions. Most of these operations are very quick, so getting rid of the overhead of a C call improves performance significantly. This also enables the C compiler to optimize things more, if there are constant operands, for example. This speeds up an older version of the richards benchmark, which didn't use native integers, by 10% (on CPython 3.12). Even bigger improvements are possible in some microbenchmarks. We didn't do this in the past because of worries about compilation time. However, I couldn't measure an impact to self-compilation speed, and the binary size is only increased by about 0.1%. Work on mypyc/mypyc#757. --- mypyc/lib-rt/CPy.h | 162 +++++++++++++++++++++++++++++++++++++--- mypyc/lib-rt/int_ops.c | 165 +++++++++-------------------------------- 2 files changed, 185 insertions(+), 142 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 1a03f049ecb0..9e85647226fe 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -129,20 +129,20 @@ Py_ssize_t CPyTagged_AsSsize_t(CPyTagged x); void CPyTagged_IncRef(CPyTagged x); void CPyTagged_DecRef(CPyTagged x); void CPyTagged_XDecRef(CPyTagged x); -CPyTagged CPyTagged_Negate(CPyTagged num); -CPyTagged CPyTagged_Invert(CPyTagged num); -CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_And(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Or(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Xor(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Rshift(CPyTagged left, CPyTagged right); -CPyTagged CPyTagged_Lshift(CPyTagged left, CPyTagged right); + bool CPyTagged_IsEq_(CPyTagged left, CPyTagged right); bool CPyTagged_IsLt_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Negate_(CPyTagged num); +CPyTagged CPyTagged_Invert_(CPyTagged num); +CPyTagged CPyTagged_Add_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Subtract_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Multiply_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_FloorDivide_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Remainder_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); +CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); + PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); PyObject *CPyLong_FromStrWithBase(PyObject *o, CPyTagged base); @@ -286,6 +286,144 @@ static inline bool CPyTagged_IsLe(CPyTagged left, CPyTagged right) { } } +static inline CPyTagged CPyTagged_Negate(CPyTagged num) { + if (likely(CPyTagged_CheckShort(num) + && num != (CPyTagged) ((Py_ssize_t)1 << (CPY_INT_BITS - 1)))) { + // The only possibility of an overflow error happening when negating a short is if we + // attempt to negate the most negative number. + return -num; + } + return CPyTagged_Negate_(num); +} + +static inline CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right) { + // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. + if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { + CPyTagged sum = left + right; + if (likely(!CPyTagged_IsAddOverflow(sum, left, right))) { + return sum; + } + } + return CPyTagged_Add_(left, right); +} + +static inline CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right) { + // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. + if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { + CPyTagged diff = left - right; + if (likely(!CPyTagged_IsSubtractOverflow(diff, left, right))) { + return diff; + } + } + return CPyTagged_Subtract_(left, right); +} + +static inline CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right) { + // TODO: Consider using some clang/gcc extension to check for overflow + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { + if (!CPyTagged_IsMultiplyOverflow(left, right)) { + return left * CPyTagged_ShortAsSsize_t(right); + } + } + return CPyTagged_Multiply_(left, right); +} + +static inline CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right) { + if (CPyTagged_CheckShort(left) + && CPyTagged_CheckShort(right) + && !CPyTagged_MaybeFloorDivideFault(left, right)) { + Py_ssize_t result = CPyTagged_ShortAsSsize_t(left) / CPyTagged_ShortAsSsize_t(right); + if (((Py_ssize_t)left < 0) != (((Py_ssize_t)right) < 0)) { + if (result * right != left) { + // Round down + result--; + } + } + return result << 1; + } + return CPyTagged_FloorDivide_(left, right); +} + +static inline CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right) { + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) + && !CPyTagged_MaybeRemainderFault(left, right)) { + Py_ssize_t result = (Py_ssize_t)left % (Py_ssize_t)right; + if (((Py_ssize_t)right < 0) != ((Py_ssize_t)left < 0) && result != 0) { + result += right; + } + return result; + } + return CPyTagged_Remainder_(left, right); +} + +// Bitwise '~' +static inline CPyTagged CPyTagged_Invert(CPyTagged num) { + if (likely(CPyTagged_CheckShort(num) && num != CPY_TAGGED_ABS_MIN)) { + return ~num & ~CPY_INT_TAG; + } + return CPyTagged_Invert_(num); +} + +// Bitwise '&' +static inline CPyTagged CPyTagged_And(CPyTagged left, CPyTagged right) { + if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { + return left & right; + } + return CPyTagged_BitwiseLongOp_(left, right, '&'); +} + +// Bitwise '|' +static inline CPyTagged CPyTagged_Or(CPyTagged left, CPyTagged right) { + if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { + return left | right; + } + return CPyTagged_BitwiseLongOp_(left, right, '|'); +} + +// Bitwise '^' +static inline CPyTagged CPyTagged_Xor(CPyTagged left, CPyTagged right) { + if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { + return left ^ right; + } + return CPyTagged_BitwiseLongOp_(left, right, '^'); +} + +// Bitwise '>>' +static inline CPyTagged CPyTagged_Rshift(CPyTagged left, CPyTagged right) { + if (likely(CPyTagged_CheckShort(left) + && CPyTagged_CheckShort(right) + && (Py_ssize_t)right >= 0)) { + CPyTagged count = CPyTagged_ShortAsSsize_t(right); + if (unlikely(count >= CPY_INT_BITS)) { + if ((Py_ssize_t)left >= 0) { + return 0; + } else { + return CPyTagged_ShortFromInt(-1); + } + } + return ((Py_ssize_t)left >> count) & ~CPY_INT_TAG; + } + return CPyTagged_Rshift_(left, right); +} + +static inline bool IsShortLshiftOverflow(Py_ssize_t short_int, Py_ssize_t shift) { + return ((Py_ssize_t)(short_int << shift) >> shift) != short_int; +} + +// Bitwise '<<' +static inline CPyTagged CPyTagged_Lshift(CPyTagged left, CPyTagged right) { + if (likely(CPyTagged_CheckShort(left) + && CPyTagged_CheckShort(right) + && (Py_ssize_t)right >= 0 + && right < CPY_INT_BITS * 2)) { + CPyTagged shift = CPyTagged_ShortAsSsize_t(right); + if (!IsShortLshiftOverflow(left, shift)) + // Short integers, no overflow + return left << shift; + } + return CPyTagged_Lshift_(left, right); +} + // Float operations diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index b57d88c6ac93..b1b3d6e125f3 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -135,13 +135,8 @@ void CPyTagged_XDecRef(CPyTagged x) { } } -CPyTagged CPyTagged_Negate(CPyTagged num) { - if (CPyTagged_CheckShort(num) - && num != (CPyTagged) ((Py_ssize_t)1 << (CPY_INT_BITS - 1))) { - // The only possibility of an overflow error happening when negating a short is if we - // attempt to negate the most negative number. - return -num; - } +// Tagged int negation slow path, where the result may be a long integer +CPyTagged CPyTagged_Negate_(CPyTagged num) { PyObject *num_obj = CPyTagged_AsObject(num); PyObject *result = PyNumber_Negative(num_obj); if (result == NULL) { @@ -151,14 +146,8 @@ CPyTagged CPyTagged_Negate(CPyTagged num) { return CPyTagged_StealFromObject(result); } -CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right) { - // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. - if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { - CPyTagged sum = left + right; - if (likely(!CPyTagged_IsAddOverflow(sum, left, right))) { - return sum; - } - } +// Tagged int addition slow path, where the result may be a long integer +CPyTagged CPyTagged_Add_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); PyObject *result = PyNumber_Add(left_obj, right_obj); @@ -170,14 +159,8 @@ CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right) { - // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. - if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { - CPyTagged diff = left - right; - if (likely(!CPyTagged_IsSubtractOverflow(diff, left, right))) { - return diff; - } - } +// Tagged int subraction slow path, where the result may be a long integer +CPyTagged CPyTagged_Subtract_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); PyObject *result = PyNumber_Subtract(left_obj, right_obj); @@ -189,13 +172,8 @@ CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right) { - // TODO: Consider using some clang/gcc extension - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { - if (!CPyTagged_IsMultiplyOverflow(left, right)) { - return left * CPyTagged_ShortAsSsize_t(right); - } - } +// Tagged int multiplication slow path, where the result may be a long integer +CPyTagged CPyTagged_Multiply_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); PyObject *result = PyNumber_Multiply(left_obj, right_obj); @@ -207,19 +185,8 @@ CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right) { - if (CPyTagged_CheckShort(left) - && CPyTagged_CheckShort(right) - && !CPyTagged_MaybeFloorDivideFault(left, right)) { - Py_ssize_t result = CPyTagged_ShortAsSsize_t(left) / CPyTagged_ShortAsSsize_t(right); - if (((Py_ssize_t)left < 0) != (((Py_ssize_t)right) < 0)) { - if (result * right != left) { - // Round down - result--; - } - } - return result << 1; - } +// Tagged int // slow path, where the result may be a long integer (or raise) +CPyTagged CPyTagged_FloorDivide_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); PyObject *result = PyNumber_FloorDivide(left_obj, right_obj); @@ -233,15 +200,8 @@ CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right) { } } -CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right) { - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) - && !CPyTagged_MaybeRemainderFault(left, right)) { - Py_ssize_t result = (Py_ssize_t)left % (Py_ssize_t)right; - if (((Py_ssize_t)right < 0) != ((Py_ssize_t)left < 0) && result != 0) { - result += right; - } - return result; - } +// Tagged int % slow path, where the result may be a long integer (or raise) +CPyTagged CPyTagged_Remainder_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); PyObject *result = PyNumber_Remainder(left_obj, right_obj); @@ -368,7 +328,7 @@ static digit *GetIntDigits(CPyTagged n, Py_ssize_t *size, digit *buf) { // Shared implementation of bitwise '&', '|' and '^' (specified by op) for at least // one long operand. This is somewhat optimized for performance. -static CPyTagged BitwiseLongOp(CPyTagged a, CPyTagged b, char op) { +CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op) { // Directly access the digits, as there is no fast C API function for this. digit abuf[3]; digit bbuf[3]; @@ -419,89 +379,34 @@ static CPyTagged BitwiseLongOp(CPyTagged a, CPyTagged b, char op) { return CPyTagged_StealFromObject((PyObject *)r); } -// Bitwise '&' -CPyTagged CPyTagged_And(CPyTagged left, CPyTagged right) { - if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { - return left & right; - } - return BitwiseLongOp(left, right, '&'); -} - -// Bitwise '|' -CPyTagged CPyTagged_Or(CPyTagged left, CPyTagged right) { - if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { - return left | right; - } - return BitwiseLongOp(left, right, '|'); -} - -// Bitwise '^' -CPyTagged CPyTagged_Xor(CPyTagged left, CPyTagged right) { - if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) { - return left ^ right; - } - return BitwiseLongOp(left, right, '^'); -} - -// Bitwise '~' -CPyTagged CPyTagged_Invert(CPyTagged num) { - if (likely(CPyTagged_CheckShort(num) && num != CPY_TAGGED_ABS_MIN)) { - return ~num & ~CPY_INT_TAG; - } else { - PyObject *obj = CPyTagged_AsObject(num); - PyObject *result = PyNumber_Invert(obj); - if (unlikely(result == NULL)) { - CPyError_OutOfMemory(); - } - Py_DECREF(obj); - return CPyTagged_StealFromObject(result); +// Bitwise '~' slow path +CPyTagged CPyTagged_Invert_(CPyTagged num) { + PyObject *obj = CPyTagged_AsObject(num); + PyObject *result = PyNumber_Invert(obj); + if (unlikely(result == NULL)) { + CPyError_OutOfMemory(); } + Py_DECREF(obj); + return CPyTagged_StealFromObject(result); } -// Bitwise '>>' -CPyTagged CPyTagged_Rshift(CPyTagged left, CPyTagged right) { - if (likely(CPyTagged_CheckShort(left) - && CPyTagged_CheckShort(right) - && (Py_ssize_t)right >= 0)) { - CPyTagged count = CPyTagged_ShortAsSsize_t(right); - if (unlikely(count >= CPY_INT_BITS)) { - if ((Py_ssize_t)left >= 0) { - return 0; - } else { - return CPyTagged_ShortFromInt(-1); - } - } - return ((Py_ssize_t)left >> count) & ~CPY_INT_TAG; - } else { - // Long integer or negative shift -- use generic op - PyObject *lobj = CPyTagged_AsObject(left); - PyObject *robj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_Rshift(lobj, robj); - Py_DECREF(lobj); - Py_DECREF(robj); - if (result == NULL) { - // Propagate error (could be negative shift count) - return CPY_INT_TAG; - } - return CPyTagged_StealFromObject(result); +// Bitwise '>>' slow path +CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right) { + // Long integer or negative shift -- use generic op + PyObject *lobj = CPyTagged_AsObject(left); + PyObject *robj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_Rshift(lobj, robj); + Py_DECREF(lobj); + Py_DECREF(robj); + if (result == NULL) { + // Propagate error (could be negative shift count) + return CPY_INT_TAG; } + return CPyTagged_StealFromObject(result); } -static inline bool IsShortLshiftOverflow(Py_ssize_t short_int, Py_ssize_t shift) { - return ((Py_ssize_t)(short_int << shift) >> shift) != short_int; -} - -// Bitwise '<<' -CPyTagged CPyTagged_Lshift(CPyTagged left, CPyTagged right) { - if (likely(CPyTagged_CheckShort(left) - && CPyTagged_CheckShort(right) - && (Py_ssize_t)right >= 0 - && right < CPY_INT_BITS * 2)) { - CPyTagged shift = CPyTagged_ShortAsSsize_t(right); - if (!IsShortLshiftOverflow(left, shift)) - // Short integers, no overflow - return left << shift; - } +// Bitwise '<<' slow path +CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right) { // Long integer or out of range shift -- use generic op PyObject *lobj = CPyTagged_AsObject(left); PyObject *robj = CPyTagged_AsObject(right); From 8dd268ffd84ccf549b3aa9105dd35766a899b2bd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Jun 2024 11:43:11 +0100 Subject: [PATCH 0623/1617] [PEP 695] Fix handling of undefined name in generic function annotation (#17338) This was generating a false positive. Work on #15238. --- mypy/semanal.py | 2 +- test-data/unit/check-python312.test | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8592a6f05e1f..98184ab41dd7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -909,7 +909,7 @@ def analyze_func_def(self, defn: FuncDef) -> None: # Don't store not ready types (including placeholders). if self.found_incomplete_ref(tag) or has_placeholder(result): self.defer(defn) - # TODO: pop type args + self.pop_type_args(defn.type_args) return assert isinstance(result, ProperType) if isinstance(result, CallableType): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 52f77243fd0a..a1c819667087 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1489,3 +1489,8 @@ else: x: T # E: Name "T" is not defined a: A[int] reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +[case testPEP695UndefinedNameInAnnotation] +# flags: --enable-incomplete-feature=NewGenericSyntax +def f[T](x: foobar, y: T) -> T: ... # E: Name "foobar" is not defined +reveal_type(f) # N: Revealed type is "def [T] (x: Any, y: T`-1) -> T`-1" From 3518f2499f5677792888bc97484cc53404472fca Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 8 Jun 2024 20:01:27 +0100 Subject: [PATCH 0624/1617] Move apply_type() to applytype.py (#17346) Moving towards https://github.com/python/mypy/issues/15907 This is a pure refactoring. It was surprisingly easy, this didn't add new import cycles, because there is already (somewhat fundamental) cycle `applytype.py` <-> `subtypes.py`. --- mypy/applytype.py | 135 +++++++++++++++++++++++++++++++++++++++++++++- mypy/checkexpr.py | 128 +------------------------------------------ 2 files changed, 134 insertions(+), 129 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index eecd555bf90d..4847570b1712 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -1,17 +1,24 @@ from __future__ import annotations -from typing import Callable, Sequence +from typing import Callable, Iterable, Sequence import mypy.subtypes from mypy.erasetype import erase_typevars from mypy.expandtype import expand_type -from mypy.nodes import Context +from mypy.nodes import Context, TypeInfo +from mypy.type_visitor import TypeTranslator +from mypy.typeops import get_all_type_vars from mypy.types import ( AnyType, CallableType, + Instance, + Parameters, + ParamSpecFlavor, ParamSpecType, PartialType, + ProperType, Type, + TypeAliasType, TypeVarId, TypeVarLikeType, TypeVarTupleType, @@ -19,6 +26,7 @@ UninhabitedType, UnpackType, get_proper_type, + remove_dups, ) @@ -170,3 +178,126 @@ def apply_generic_arguments( type_guard=type_guard, type_is=type_is, ) + + +def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> CallableType | None: + """Make free type variables generic in the type if possible. + + This will translate the type `tp` while trying to create valid bindings for + type variables `poly_tvars` while traversing the type. This follows the same rules + as we do during semantic analysis phase, examples: + * Callable[Callable[[T], T], T] -> def [T] (def (T) -> T) -> T + * Callable[[], Callable[[T], T]] -> def () -> def [T] (T -> T) + * List[T] -> None (not possible) + """ + try: + return tp.copy_modified( + arg_types=[t.accept(PolyTranslator(poly_tvars)) for t in tp.arg_types], + ret_type=tp.ret_type.accept(PolyTranslator(poly_tvars)), + variables=[], + ) + except PolyTranslationError: + return None + + +class PolyTranslationError(Exception): + pass + + +class PolyTranslator(TypeTranslator): + """Make free type variables generic in the type if possible. + + See docstring for apply_poly() for details. + """ + + def __init__( + self, + poly_tvars: Iterable[TypeVarLikeType], + bound_tvars: frozenset[TypeVarLikeType] = frozenset(), + seen_aliases: frozenset[TypeInfo] = frozenset(), + ) -> None: + self.poly_tvars = set(poly_tvars) + # This is a simplified version of TypeVarScope used during semantic analysis. + self.bound_tvars = bound_tvars + self.seen_aliases = seen_aliases + + def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]: + found_vars = [] + for arg in t.arg_types: + for tv in get_all_type_vars(arg): + if isinstance(tv, ParamSpecType): + normalized: TypeVarLikeType = tv.copy_modified( + flavor=ParamSpecFlavor.BARE, prefix=Parameters([], [], []) + ) + else: + normalized = tv + if normalized in self.poly_tvars and normalized not in self.bound_tvars: + found_vars.append(normalized) + return remove_dups(found_vars) + + def visit_callable_type(self, t: CallableType) -> Type: + found_vars = self.collect_vars(t) + self.bound_tvars |= set(found_vars) + result = super().visit_callable_type(t) + self.bound_tvars -= set(found_vars) + + assert isinstance(result, ProperType) and isinstance(result, CallableType) + result.variables = list(result.variables) + found_vars + return result + + def visit_type_var(self, t: TypeVarType) -> Type: + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_type_var(t) + + def visit_param_spec(self, t: ParamSpecType) -> Type: + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_param_spec(t) + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + if t in self.poly_tvars and t not in self.bound_tvars: + raise PolyTranslationError() + return super().visit_type_var_tuple(t) + + def visit_type_alias_type(self, t: TypeAliasType) -> Type: + if not t.args: + return t.copy_modified() + if not t.is_recursive: + return get_proper_type(t).accept(self) + # We can't handle polymorphic application for recursive generic aliases + # without risking an infinite recursion, just give up for now. + raise PolyTranslationError() + + def visit_instance(self, t: Instance) -> Type: + if t.type.has_param_spec_type: + # We need this special-casing to preserve the possibility to store a + # generic function in an instance type. Things like + # forall T . Foo[[x: T], T] + # are not really expressible in current type system, but this looks like + # a useful feature, so let's keep it. + param_spec_index = next( + i for (i, tv) in enumerate(t.type.defn.type_vars) if isinstance(tv, ParamSpecType) + ) + p = get_proper_type(t.args[param_spec_index]) + if isinstance(p, Parameters): + found_vars = self.collect_vars(p) + self.bound_tvars |= set(found_vars) + new_args = [a.accept(self) for a in t.args] + self.bound_tvars -= set(found_vars) + + repl = new_args[param_spec_index] + assert isinstance(repl, ProperType) and isinstance(repl, Parameters) + repl.variables = list(repl.variables) + list(found_vars) + return t.copy_modified(args=new_args) + # There is the same problem with callback protocols as with aliases + # (callback protocols are essentially more flexible aliases to callables). + if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]: + if t.type in self.seen_aliases: + raise PolyTranslationError() + call = mypy.subtypes.find_member("__call__", t, t, is_operator=True) + assert call is not None + return call.accept( + PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type}) + ) + return super().visit_instance(t) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 8e6af0218c32..779d63c8d385 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -115,7 +115,6 @@ non_method_protocol_members, ) from mypy.traverser import has_await_expression -from mypy.type_visitor import TypeTranslator from mypy.typeanal import ( check_for_explicit_any, fix_instance, @@ -168,7 +167,6 @@ TypeOfAny, TypeType, TypeVarId, - TypeVarLikeType, TypeVarTupleType, TypeVarType, UnboundType, @@ -182,7 +180,6 @@ get_proper_types, has_recursive_types, is_named_instance, - remove_dups, split_with_prefix_and_suffix, ) from mypy.types_utils import ( @@ -2136,7 +2133,7 @@ def infer_function_type_arguments( ) # Try applying inferred polymorphic type if possible, e.g. Callable[[T], T] can # be interpreted as def [T] (T) -> T, but dict[T, T] cannot be expressed. - applied = apply_poly(poly_callee_type, free_vars) + applied = applytype.apply_poly(poly_callee_type, free_vars) if applied is not None and all( a is not None and not isinstance(get_proper_type(a), UninhabitedType) for a in poly_inferred_args @@ -6220,129 +6217,6 @@ def replace_callable_return_type(c: CallableType, new_ret_type: Type) -> Callabl return c.copy_modified(ret_type=new_ret_type) -def apply_poly(tp: CallableType, poly_tvars: Sequence[TypeVarLikeType]) -> CallableType | None: - """Make free type variables generic in the type if possible. - - This will translate the type `tp` while trying to create valid bindings for - type variables `poly_tvars` while traversing the type. This follows the same rules - as we do during semantic analysis phase, examples: - * Callable[Callable[[T], T], T] -> def [T] (def (T) -> T) -> T - * Callable[[], Callable[[T], T]] -> def () -> def [T] (T -> T) - * List[T] -> None (not possible) - """ - try: - return tp.copy_modified( - arg_types=[t.accept(PolyTranslator(poly_tvars)) for t in tp.arg_types], - ret_type=tp.ret_type.accept(PolyTranslator(poly_tvars)), - variables=[], - ) - except PolyTranslationError: - return None - - -class PolyTranslationError(Exception): - pass - - -class PolyTranslator(TypeTranslator): - """Make free type variables generic in the type if possible. - - See docstring for apply_poly() for details. - """ - - def __init__( - self, - poly_tvars: Iterable[TypeVarLikeType], - bound_tvars: frozenset[TypeVarLikeType] = frozenset(), - seen_aliases: frozenset[TypeInfo] = frozenset(), - ) -> None: - self.poly_tvars = set(poly_tvars) - # This is a simplified version of TypeVarScope used during semantic analysis. - self.bound_tvars = bound_tvars - self.seen_aliases = seen_aliases - - def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]: - found_vars = [] - for arg in t.arg_types: - for tv in get_all_type_vars(arg): - if isinstance(tv, ParamSpecType): - normalized: TypeVarLikeType = tv.copy_modified( - flavor=ParamSpecFlavor.BARE, prefix=Parameters([], [], []) - ) - else: - normalized = tv - if normalized in self.poly_tvars and normalized not in self.bound_tvars: - found_vars.append(normalized) - return remove_dups(found_vars) - - def visit_callable_type(self, t: CallableType) -> Type: - found_vars = self.collect_vars(t) - self.bound_tvars |= set(found_vars) - result = super().visit_callable_type(t) - self.bound_tvars -= set(found_vars) - - assert isinstance(result, ProperType) and isinstance(result, CallableType) - result.variables = list(result.variables) + found_vars - return result - - def visit_type_var(self, t: TypeVarType) -> Type: - if t in self.poly_tvars and t not in self.bound_tvars: - raise PolyTranslationError() - return super().visit_type_var(t) - - def visit_param_spec(self, t: ParamSpecType) -> Type: - if t in self.poly_tvars and t not in self.bound_tvars: - raise PolyTranslationError() - return super().visit_param_spec(t) - - def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: - if t in self.poly_tvars and t not in self.bound_tvars: - raise PolyTranslationError() - return super().visit_type_var_tuple(t) - - def visit_type_alias_type(self, t: TypeAliasType) -> Type: - if not t.args: - return t.copy_modified() - if not t.is_recursive: - return get_proper_type(t).accept(self) - # We can't handle polymorphic application for recursive generic aliases - # without risking an infinite recursion, just give up for now. - raise PolyTranslationError() - - def visit_instance(self, t: Instance) -> Type: - if t.type.has_param_spec_type: - # We need this special-casing to preserve the possibility to store a - # generic function in an instance type. Things like - # forall T . Foo[[x: T], T] - # are not really expressible in current type system, but this looks like - # a useful feature, so let's keep it. - param_spec_index = next( - i for (i, tv) in enumerate(t.type.defn.type_vars) if isinstance(tv, ParamSpecType) - ) - p = get_proper_type(t.args[param_spec_index]) - if isinstance(p, Parameters): - found_vars = self.collect_vars(p) - self.bound_tvars |= set(found_vars) - new_args = [a.accept(self) for a in t.args] - self.bound_tvars -= set(found_vars) - - repl = new_args[param_spec_index] - assert isinstance(repl, ProperType) and isinstance(repl, Parameters) - repl.variables = list(repl.variables) + list(found_vars) - return t.copy_modified(args=new_args) - # There is the same problem with callback protocols as with aliases - # (callback protocols are essentially more flexible aliases to callables). - if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]: - if t.type in self.seen_aliases: - raise PolyTranslationError() - call = find_member("__call__", t, t, is_operator=True) - assert call is not None - return call.accept( - PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type}) - ) - return super().visit_instance(t) - - class ArgInferSecondPassQuery(types.BoolTypeQuery): """Query whether an argument type should be inferred in the second pass. From 428a0354867911ef9666266bb060ce6d1d203e5a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 9 Jun 2024 20:50:43 +0100 Subject: [PATCH 0625/1617] Fix crash on recursive alias with an optional type (#17350) Fixes https://github.com/python/mypy/issues/17132 Fix is trivial, we don't need that extra `get_proper_type()`. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/typeanal.py | 12 +++--------- test-data/unit/check-recursive-types.test | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index a513b0716a01..a9b4576c8f42 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -93,7 +93,6 @@ callable_with_ellipsis, find_unpack_in_list, flatten_nested_tuples, - flatten_nested_unions, get_proper_type, has_type_vars, ) @@ -2337,16 +2336,11 @@ def make_optional_type(t: Type) -> Type: is called during semantic analysis and simplification only works during type checking. """ - p_t = get_proper_type(t) - if isinstance(p_t, NoneType): + if isinstance(t, ProperType) and isinstance(t, NoneType): return t - elif isinstance(p_t, UnionType): + elif isinstance(t, ProperType) and isinstance(t, UnionType): # Eagerly expanding aliases is not safe during semantic analysis. - items = [ - item - for item in flatten_nested_unions(p_t.items, handle_type_alias_type=False) - if not isinstance(get_proper_type(item), NoneType) - ] + items = [item for item in t.items if not isinstance(get_proper_type(item), NoneType)] return UnionType(items + [NoneType()], t.line, t.column) else: return UnionType([t, NoneType()], t.line, t.column) diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 84593933a2de..b67818e169b1 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -942,3 +942,19 @@ NotFilter = Tuple[Literal["not"], "NotFilter"] n: NotFilter reveal_type(n[1][1][0]) # N: Revealed type is "Literal['not']" [builtins fixtures/tuple.pyi] + +[case testNoCrashOnRecursiveAliasWithNone] +# flags: --strict-optional +from typing import Union, Generic, TypeVar, Optional + +T = TypeVar("T") +class A(Generic[T]): ... +class B(Generic[T]): ... + +Z = Union[A[Z], B[Optional[Z]]] +X = Union[A[Optional[X]], B[Optional[X]]] + +z: Z +x: X +reveal_type(z) # N: Revealed type is "Union[__main__.A[...], __main__.B[Union[..., None]]]" +reveal_type(x) # N: Revealed type is "Union[__main__.A[Union[..., None]], __main__.B[Union[..., None]]]" From 09e6a2bea4cb4a523791220b37b5c664db895760 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 9 Jun 2024 21:04:42 +0100 Subject: [PATCH 0626/1617] Fix crash on unpacking self in NamedTuple (#17351) Fixes https://github.com/python/mypy/issues/17010 Fix is trivial: replicate the `TypeVar` handling logic in the caller. --- mypy/checker.py | 2 ++ test-data/unit/check-namedtuple.test | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 38976d4ce15e..42fcc05c5976 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3839,6 +3839,8 @@ def check_multi_assignment_from_tuple( self.expr_checker.accept(rvalue, lvalue_type) ) + if isinstance(reinferred_rvalue_type, TypeVarLikeType): + reinferred_rvalue_type = get_proper_type(reinferred_rvalue_type.upper_bound) if isinstance(reinferred_rvalue_type, UnionType): # If this is an Optional type in non-strict Optional code, unwrap it. relevant_items = reinferred_rvalue_type.relevant_items() diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index a0d984b30279..2007d574f922 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1412,3 +1412,14 @@ A(x=0).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "A" has inco A(x=0).__replace__(y=1) # E: Unexpected keyword argument "y" for "__replace__" of "A" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testUnpackSelfNamedTuple] +import typing + +class Foo(typing.NamedTuple): + bar: int + def baz(self: typing.Self) -> None: + x, = self + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] From 7c391ddb2f72833822309e5baef1ab533b149e1b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 9 Jun 2024 21:33:13 +0100 Subject: [PATCH 0627/1617] Fix crash on invalid callable property override (#17352) Fixes https://github.com/python/mypy/issues/16896 Fix is simple, do not assume that an error context given by the caller of the override check for callable type is a method defining such type, because it may be a property. --- mypy/checker.py | 2 +- test-data/unit/check-functions.test | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 42fcc05c5976..38f6f5f44816 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2279,7 +2279,7 @@ def erase_override(t: Type) -> Type: ): arg_type_in_super = original.arg_types[i] - if isinstance(node, FuncDef): + if isinstance(node, FuncDef) and not node.is_property: context: Context = node.arguments[i + len(override.bound_args)] else: context = node diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 917b74fd2147..4b04a3b96ae4 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3302,3 +3302,25 @@ class C: # Note: Generic[T] missing def nope(self, x: T) -> None: self.x = x # E: Incompatible types in assignment (expression has type "T@nope", variable has type "T@bad_idea") + +[case testNoCrashOnBadCallablePropertyOverride] +from typing import Callable, Union + +class C: ... +class D: ... + +A = Callable[[C], None] +B = Callable[[D], None] + +class Foo: + @property + def method(self) -> Callable[[int, Union[A, B]], None]: + ... + +class Bar(Foo): + @property + def method(self) -> Callable[[int, A], None]: # E: Argument 2 of "method" is incompatible with supertype "Foo"; supertype defines the argument type as "Union[Callable[[C], None], Callable[[D], None]]" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + ... +[builtins fixtures/property.pyi] From 5ae9e69480985d5eba423b718c675ea8714ac66c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 9 Jun 2024 23:32:04 +0100 Subject: [PATCH 0628/1617] Fix crash involving recursive union of tuples (#17353) Fixes https://github.com/python/mypy/issues/17236 It turns out we were calculating tuple fallbacks where we don't really need to. We can rely on the fact that tuple fallback is trivial for non-trivial partial fallbacks to simplify the logic and avoid the infinite recursion. --- mypy/subtypes.py | 11 ++++--- test-data/unit/check-recursive-types.test | 36 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 971caa3991ae..63f5137ef8ae 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -794,15 +794,18 @@ def visit_tuple_type(self, left: TupleType) -> bool: return False if any(not self._is_subtype(l, r) for l, r in zip(left.items, right.items)): return False - rfallback = mypy.typeops.tuple_fallback(right) - if is_named_instance(rfallback, "builtins.tuple"): + if is_named_instance(right.partial_fallback, "builtins.tuple"): # No need to verify fallback. This is useful since the calculated fallback # may be inconsistent due to how we calculate joins between unions vs. # non-unions. For example, join(int, str) == object, whereas # join(Union[int, C], Union[str, C]) == Union[int, str, C]. return True - lfallback = mypy.typeops.tuple_fallback(left) - return self._is_subtype(lfallback, rfallback) + if is_named_instance(left.partial_fallback, "builtins.tuple"): + # Again, no need to verify. At this point we know the right fallback + # is a subclass of tuple, so if left is plain tuple, it cannot be a subtype. + return False + # At this point we know both fallbacks are non-tuple. + return self._is_subtype(left.partial_fallback, right.partial_fallback) else: return False diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index b67818e169b1..33cb9ccad9af 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -958,3 +958,39 @@ z: Z x: X reveal_type(z) # N: Revealed type is "Union[__main__.A[...], __main__.B[Union[..., None]]]" reveal_type(x) # N: Revealed type is "Union[__main__.A[Union[..., None]], __main__.B[Union[..., None]]]" + +[case testRecursiveTupleFallback1] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +T2 = Tuple[T1, "T4", "T4"] +T3 = Tuple[str, "T4", "T4"] +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback2] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +class T2(Tuple[T1, "T4", "T4"]): ... +T3 = Tuple[str, "T4", "T4"] +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback3] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +T2 = Tuple[T1, "T4", "T4"] +class T3(Tuple[str, "T4", "T4"]): ... +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback4] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +class T2(Tuple[T1, "T4", "T4"]): ... +class T3(Tuple[str, "T4", "T4"]): ... +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] From 83d54ffb01af9cba76a36d2ec0938acc7dfa2197 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Jun 2024 20:11:38 +0100 Subject: [PATCH 0629/1617] Use polymorphic inference in unification (#17348) Moving towards https://github.com/python/mypy/issues/15907 Fixes https://github.com/python/mypy/issues/17206 This PR enables polymorphic inference during unification. This will allow us to handle even more tricky situations involving generic higher-order functions (see a random example I added in tests). Implementation is mostly straightforward, few notes: * This uncovered another issue with unions in solver, unfortunately current constraint inference algorithm can sometimes infer weird constraints like `T <: Union[T, int]`, that later confuse the solver. * This uncovered another possible type variable clash scenario that was not handled properly. In overloaded generic function, each overload should have a different namespace for type variables (currently they all just get function name). I use `module.some_func#0` etc. for overloads namespaces instead. * Another thing with overloads is that the switch caused unsafe overlap check to change: after some back and forth I am keeping it mostly the same to avoid possible regressions (unfortunately this requires some extra refreshing of type variables). * This makes another `ParamSpec` crash to happen more often so I fix it in this same PR. * Finally this uncovered a bug in handling of overloaded `__init__()` that I am fixing here as well. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 16 ++- mypy/constraints.py | 9 +- mypy/message_registry.py | 4 + mypy/semanal.py | 38 ++++-- mypy/semanal_typeargs.py | 22 +++- mypy/solve.py | 9 +- mypy/subtypes.py | 16 ++- mypy/typeanal.py | 17 ++- mypy/typeops.py | 33 +++-- test-data/unit/check-generics.test | 120 +++++++++++++++--- .../unit/check-parameter-specification.test | 28 ++-- test-data/unit/check-selftype.test | 6 +- 12 files changed, 253 insertions(+), 65 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 38f6f5f44816..04e90c3e94cd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -791,9 +791,21 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: if impl_type is not None: assert defn.impl is not None + # This is what we want from implementation, it should accept all arguments + # of an overload, but the return types should go the opposite way. + if is_callable_compatible( + impl_type, + sig1, + is_compat=is_subtype, + is_proper_subtype=False, + is_compat_return=lambda l, r: is_subtype(r, l), + ): + continue + # If the above check didn't work, we repeat some key steps in + # is_callable_compatible() to give a better error message. + # We perform a unification step that's very similar to what - # 'is_callable_compatible' would have done if we had set - # 'unify_generics' to True -- the only difference is that + # 'is_callable_compatible' does -- the only difference is that # we check and see if the impl_type's return value is a # *supertype* of the overload alternative, not a *subtype*. # diff --git a/mypy/constraints.py b/mypy/constraints.py index cdfa39ac45f3..46221bd82628 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -688,14 +688,19 @@ def visit_unpack_type(self, template: UnpackType) -> list[Constraint]: def visit_parameters(self, template: Parameters) -> list[Constraint]: # Constraining Any against C[P] turns into infer_against_any([P], Any) - # ... which seems like the only case this can happen. Better to fail loudly otherwise. if isinstance(self.actual, AnyType): return self.infer_against_any(template.arg_types, self.actual) if type_state.infer_polymorphic and isinstance(self.actual, Parameters): # For polymorphic inference we need to be able to infer secondary constraints # in situations like [x: T] <: P <: [x: int]. return infer_callable_arguments_constraints(template, self.actual, self.direction) - raise RuntimeError("Parameters cannot be constrained to") + if type_state.infer_polymorphic and isinstance(self.actual, ParamSpecType): + # Similar for [x: T] <: Q <: Concatenate[int, P]. + return infer_callable_arguments_constraints( + template, self.actual.prefix, self.direction + ) + # There also may be unpatched types after a user error, simply ignore them. + return [] # Non-leaf types diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 3852431f2290..52bd9a1ce00c 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -180,6 +180,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage: ) INVALID_UNPACK: Final = "{} cannot be unpacked (must be tuple or TypeVarTuple)" INVALID_UNPACK_POSITION: Final = "Unpack is only valid in a variadic position" +INVALID_PARAM_SPEC_LOCATION: Final = "Invalid location for ParamSpec {}" +INVALID_PARAM_SPEC_LOCATION_NOTE: Final = ( + 'You can use ParamSpec as the first argument to Callable, e.g., "Callable[{}, int]"' +) # TypeVar INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}' diff --git a/mypy/semanal.py b/mypy/semanal.py index 98184ab41dd7..903af80fe404 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -479,6 +479,9 @@ def __init__( # new uses of this, as this may cause leaking `UnboundType`s to type checking. self.allow_unbound_tvars = False + # Used to pass information about current overload index to visit_func_def(). + self.current_overload_item: int | None = None + # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties @property @@ -869,6 +872,11 @@ def visit_func_def(self, defn: FuncDef) -> None: with self.scope.function_scope(defn): self.analyze_func_def(defn) + def function_fullname(self, fullname: str) -> str: + if self.current_overload_item is None: + return fullname + return f"{fullname}#{self.current_overload_item}" + def analyze_func_def(self, defn: FuncDef) -> None: if self.push_type_args(defn.type_args, defn) is None: self.defer(defn) @@ -895,7 +903,8 @@ def analyze_func_def(self, defn: FuncDef) -> None: self.prepare_method_signature(defn, self.type, has_self_type) # Analyze function signature - with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): + fullname = self.function_fullname(defn.fullname) + with self.tvar_scope_frame(self.tvar_scope.method_frame(fullname)): if defn.type: self.check_classvar_in_signature(defn.type) assert isinstance(defn.type, CallableType) @@ -903,9 +912,7 @@ def analyze_func_def(self, defn: FuncDef) -> None: # class-level imported names and type variables are in scope. analyzer = self.type_analyzer() tag = self.track_incomplete_refs() - result = analyzer.visit_callable_type( - defn.type, nested=False, namespace=defn.fullname - ) + result = analyzer.visit_callable_type(defn.type, nested=False, namespace=fullname) # Don't store not ready types (including placeholders). if self.found_incomplete_ref(tag) or has_placeholder(result): self.defer(defn) @@ -1117,7 +1124,8 @@ def update_function_type_variables(self, fun_type: CallableType, defn: FuncItem) if defn is generic. Return True, if the signature contains typing.Self type, or False otherwise. """ - with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): + fullname = self.function_fullname(defn.fullname) + with self.tvar_scope_frame(self.tvar_scope.method_frame(fullname)): a = self.type_analyzer() fun_type.variables, has_self_type = a.bind_function_type_variables(fun_type, defn) if has_self_type and self.type is not None: @@ -1175,6 +1183,14 @@ def visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: with self.scope.function_scope(defn): self.analyze_overloaded_func_def(defn) + @contextmanager + def overload_item_set(self, item: int | None) -> Iterator[None]: + self.current_overload_item = item + try: + yield + finally: + self.current_overload_item = None + def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # OverloadedFuncDef refers to any legitimate situation where you have # more than one declaration for the same function in a row. This occurs @@ -1187,7 +1203,8 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: first_item = defn.items[0] first_item.is_overload = True - first_item.accept(self) + with self.overload_item_set(0): + first_item.accept(self) if isinstance(first_item, Decorator) and first_item.func.is_property: # This is a property. @@ -1272,7 +1289,8 @@ def analyze_overload_sigs_and_impl( if i != 0: # Assume that the first item was already visited item.is_overload = True - item.accept(self) + with self.overload_item_set(i if i < len(defn.items) - 1 else None): + item.accept(self) # TODO: support decorated overloaded functions properly if isinstance(item, Decorator): callable = function_type(item.func, self.named_type("builtins.function")) @@ -1444,7 +1462,8 @@ def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> Non self.add_symbol(func.name, func, func) def analyze_arg_initializers(self, defn: FuncItem) -> None: - with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): + fullname = self.function_fullname(defn.fullname) + with self.tvar_scope_frame(self.tvar_scope.method_frame(fullname)): # Analyze default arguments for arg in defn.arguments: if arg.initializer: @@ -1452,7 +1471,8 @@ def analyze_arg_initializers(self, defn: FuncItem) -> None: def analyze_function_body(self, defn: FuncItem) -> None: is_method = self.is_class_scope() - with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): + fullname = self.function_fullname(defn.fullname) + with self.tvar_scope_frame(self.tvar_scope.method_frame(fullname)): # Bind the type variables again to visit the body. if defn.type: a = self.type_analyzer() diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 15ea15d612c0..02cb1b1f6128 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -12,6 +12,7 @@ from mypy import errorcodes as codes, message_registry from mypy.errorcodes import ErrorCode from mypy.errors import Errors +from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE from mypy.messages import format_type from mypy.mixedtraverser import MixedTraverserVisitor from mypy.nodes import ARG_STAR, Block, ClassDef, Context, FakeInfo, FuncItem, MypyFile @@ -146,13 +147,25 @@ def validate_args( for (i, arg), tvar in zip(enumerate(args), type_vars): if isinstance(tvar, TypeVarType): if isinstance(arg, ParamSpecType): - # TODO: Better message is_error = True - self.fail(f'Invalid location for ParamSpec "{arg.name}"', ctx) + self.fail( + INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)), + ctx, + code=codes.VALID_TYPE, + ) self.note( - "You can use ParamSpec as the first argument to Callable, e.g., " - "'Callable[{}, int]'".format(arg.name), + INVALID_PARAM_SPEC_LOCATION_NOTE.format(arg.name), + ctx, + code=codes.VALID_TYPE, + ) + continue + if isinstance(arg, Parameters): + is_error = True + self.fail( + f"Cannot use {format_type(arg, self.options)} for regular type variable," + " only for ParamSpec", ctx, + code=codes.VALID_TYPE, ) continue if tvar.values: @@ -204,6 +217,7 @@ def validate_args( "Can only replace ParamSpec with a parameter types list or" f" another ParamSpec, got {format_type(arg, self.options)}", ctx, + code=codes.VALID_TYPE, ) return is_error diff --git a/mypy/solve.py b/mypy/solve.py index 9770364bf892..bb87b6576ada 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -514,7 +514,8 @@ def skip_reverse_union_constraints(cs: list[Constraint]) -> list[Constraint]: is a linear constraint. This is however not true in presence of union types, for example T :> Union[S, int] vs S <: T. Trying to solve such constraints would be detected ambiguous as (T, S) form a non-linear SCC. However, simply removing the linear part results in a valid - solution T = Union[S, int], S = . + solution T = Union[S, int], S = . A similar scenario is when we get T <: Union[T, int], + such constraints carry no information, and will equally confuse linearity check. TODO: a cleaner solution may be to avoid inferring such constraints in first place, but this would require passing around a flag through all infer_constraints() calls. @@ -525,7 +526,13 @@ def skip_reverse_union_constraints(cs: list[Constraint]) -> list[Constraint]: if isinstance(p_target, UnionType): for item in p_target.items: if isinstance(item, TypeVarType): + if item == c.origin_type_var and c.op == SUBTYPE_OF: + reverse_union_cs.add(c) + continue + # These two forms are semantically identical, but are different from + # the point of view of Constraint.__eq__(). reverse_union_cs.add(Constraint(item, neg_op(c.op), c.origin_type_var)) + reverse_union_cs.add(Constraint(c.origin_type_var, c.op, item)) return [c for c in cs if c not in reverse_union_cs] diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 63f5137ef8ae..a5d1d5d8194a 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -8,7 +8,12 @@ import mypy.constraints import mypy.typeops from mypy.erasetype import erase_type -from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance +from mypy.expandtype import ( + expand_self_type, + expand_type, + expand_type_by_instance, + freshen_function_type_vars, +) from mypy.maptype import map_instance_to_supertype # Circular import; done in the function instead. @@ -1860,6 +1865,11 @@ def unify_generic_callable( """ import mypy.solve + if set(type.type_var_ids()) & {v.id for v in mypy.typeops.get_all_type_vars(target)}: + # Overload overlap check does nasty things like unifying in opposite direction. + # This can easily create type variable clashes, so we need to refresh. + type = freshen_function_type_vars(type) + if return_constraint_direction is None: return_constraint_direction = mypy.constraints.SUBTYPE_OF @@ -1882,7 +1892,9 @@ def unify_generic_callable( constraints = [ c for c in constraints if not isinstance(get_proper_type(c.target), NoneType) ] - inferred_vars, _ = mypy.solve.solve_constraints(type.variables, constraints) + inferred_vars, _ = mypy.solve.solve_constraints( + type.variables, constraints, allow_polymorphic=True + ) if None in inferred_vars: return None non_none_inferred_vars = cast(List[Type], inferred_vars) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index a9b4576c8f42..28abd24149e6 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -10,7 +10,14 @@ from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode from mypy.expandtype import expand_type -from mypy.messages import MessageBuilder, format_type_bare, quote_type_string, wrong_type_arg_count +from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE +from mypy.messages import ( + MessageBuilder, + format_type, + format_type_bare, + quote_type_string, + wrong_type_arg_count, +) from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, @@ -1782,12 +1789,14 @@ def anal_type( analyzed = AnyType(TypeOfAny.from_error) else: self.fail( - f'Invalid location for ParamSpec "{analyzed.name}"', t, code=codes.VALID_TYPE + INVALID_PARAM_SPEC_LOCATION.format(format_type(analyzed, self.options)), + t, + code=codes.VALID_TYPE, ) self.note( - "You can use ParamSpec as the first argument to Callable, e.g., " - "'Callable[{}, int]'".format(analyzed.name), + INVALID_PARAM_SPEC_LOCATION_NOTE.format(analyzed.name), t, + code=codes.VALID_TYPE, ) analyzed = AnyType(TypeOfAny.from_error) return analyzed diff --git a/mypy/typeops.py b/mypy/typeops.py index a59bd3739562..62c850452516 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -152,7 +152,14 @@ def type_object_type_from_function( # ... # # We need to map B's __init__ to the type (List[T]) -> None. - signature = bind_self(signature, original_type=default_self, is_classmethod=is_new) + signature = bind_self( + signature, + original_type=default_self, + is_classmethod=is_new, + # Explicit instance self annotations have special handling in class_callable(), + # we don't need to bind any type variables in them if they are generic. + ignore_instances=True, + ) signature = cast(FunctionLike, map_type_from_supertype(signature, info, def_info)) special_sig: str | None = None @@ -244,7 +251,9 @@ class C(D[E[T]], Generic[T]): ... return expand_type_by_instance(typ, inst_type) -def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool: +def supported_self_type( + typ: ProperType, allow_callable: bool = True, allow_instances: bool = True +) -> bool: """Is this a supported kind of explicit self-types? Currently, this means an X or Type[X], where X is an instance or @@ -257,14 +266,19 @@ def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool: # as well as callable self for callback protocols. return True return isinstance(typ, TypeVarType) or ( - isinstance(typ, Instance) and typ != fill_typevars(typ.type) + allow_instances and isinstance(typ, Instance) and typ != fill_typevars(typ.type) ) F = TypeVar("F", bound=FunctionLike) -def bind_self(method: F, original_type: Type | None = None, is_classmethod: bool = False) -> F: +def bind_self( + method: F, + original_type: Type | None = None, + is_classmethod: bool = False, + ignore_instances: bool = False, +) -> F: """Return a copy of `method`, with the type of its first parameter (usually self or cls) bound to original_type. @@ -288,9 +302,10 @@ class B(A): pass """ if isinstance(method, Overloaded): - return cast( - F, Overloaded([bind_self(c, original_type, is_classmethod) for c in method.items]) - ) + items = [ + bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items + ] + return cast(F, Overloaded(items)) assert isinstance(method, CallableType) func = method if not func.arg_types: @@ -310,7 +325,9 @@ class B(A): pass # this special-casing looks not very principled, there is nothing meaningful we can infer # from such definition, since it is inherently indefinitely recursive. allow_callable = func.name is None or not func.name.startswith("__call__ of") - if func.variables and supported_self_type(self_param_type, allow_callable=allow_callable): + if func.variables and supported_self_type( + self_param_type, allow_callable=allow_callable, allow_instances=not ignore_instances + ): from mypy.infer import infer_type_arguments if original_type is None: diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index bd327745e2ed..b4b075694bb4 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2927,8 +2927,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]: def id(__x: U) -> U: ... fs = [id, id, id] -reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]" -reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]" +reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`11) -> builtins.list[S`11]" +reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`13) -> builtins.list[S`13]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCurry] @@ -3027,7 +3027,7 @@ def dec(f: Callable[[T], S], g: Callable[[T], U]) -> Callable[[T], Tuple[S, U]]: def id(x: V) -> V: ... -reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]" +reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`7) -> Tuple[T`7, T`7]" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericEllipsisSelfSpecialCase] @@ -3099,13 +3099,13 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]: ... reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" -reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]" -reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`6) -> S`6" -reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`9) -> S`9" +reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]" +reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`8) -> S`8" +reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`12) -> S`12" reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" -reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`16) -> builtins.list[S`16]" -reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`19]) -> T`19" +reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]" +reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24" dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "List[T]" [builtins fixtures/list.pyi] @@ -3185,7 +3185,7 @@ reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, x: T`6) -> T`6" reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> Tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if P can be empty -reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`12, f: def () -> def (T`12) -> S`13) -> S`13" +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`13, f: def () -> def (T`13) -> S`14) -> S`14" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecVsParamSpec] @@ -3263,8 +3263,8 @@ def transform( def dec(f: Callable[W, U]) -> Callable[W, U]: ... def dec2(f: Callable[Concatenate[str, W], U]) -> Callable[Concatenate[bytes, W], U]: ... -reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`3) -> def (builtins.int, *P.args, **P.kwargs) -> T`3" -reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`7) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`7" +reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`9) -> def (builtins.int, *P.args, **P.kwargs) -> T`9" +reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`13) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`13" [builtins fixtures/tuple.pyi] [case testNoAccidentalVariableClashInNestedGeneric] @@ -3318,8 +3318,8 @@ def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> builtins.list[T`5]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`9) -> builtins.list[T`9]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`11, T`11) -> builtins.list[T`11]" reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" [builtins fixtures/tuple.pyi] @@ -3337,8 +3337,8 @@ V = TypeVar("V") def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`3]) -> T`3" -reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`5], builtins.list[T`5]) -> T`5" +reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`9]) -> T`9" +reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`11], builtins.list[T`11]) -> T`11" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicPopOff] @@ -3383,7 +3383,7 @@ reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, T`6) -> T`6" reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, U`-1) -> Tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if Ts is empty -reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`12, def () -> def (T`12) -> S`13) -> S`13" +reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`13, def () -> def (T`13) -> S`14) -> S`14" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicVsVariadic] @@ -3442,3 +3442,91 @@ reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[b h: Callable[[Unpack[Us]], Foo[int]] reveal_type(dec(h)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" [builtins fixtures/list.pyi] + +[case testHigherOrderGenericPartial] +from typing import TypeVar, Callable + +T = TypeVar("T") +S = TypeVar("S") +U = TypeVar("U") +def apply(f: Callable[[T], S], x: T) -> S: ... +def id(x: U) -> U: ... + +A1 = TypeVar("A1") +A2 = TypeVar("A2") +R = TypeVar("R") +def fake_partial(fun: Callable[[A1, A2], R], arg: A1) -> Callable[[A2], R]: ... + +f_pid = fake_partial(apply, id) +reveal_type(f_pid) # N: Revealed type is "def [A2] (A2`2) -> A2`2" +reveal_type(f_pid(1)) # N: Revealed type is "builtins.int" + +[case testInvalidTypeVarParametersConcrete] +from typing import Callable, Generic, ParamSpec, Protocol, TypeVar, overload + +P = ParamSpec('P') +P2 = ParamSpec('P2') +R = TypeVar('R') +R2 = TypeVar('R2') + +class C(Generic[P, R, P2, R2]): ... + +class Proto(Protocol[P, R]): + @overload + def __call__(self, f: Callable[P2, R2]) -> C[P2, R2, ..., R]: ... + @overload + def __call__(self, **kwargs) -> C[P, R, ..., [int, str]]: ... # E: Cannot use "[int, str]" for regular type variable, only for ParamSpec +[builtins fixtures/tuple.pyi] + +[case testInvalidTypeVarParametersArbitrary] +from typing import Callable, Generic, ParamSpec, Protocol, TypeVar, overload + +P = ParamSpec('P') +P2 = ParamSpec('P2') +R = TypeVar('R') +R2 = TypeVar('R2') + +class C(Generic[P, R, P2, R2]): ... + +class Proto(Protocol[P, R]): + @overload + def __call__(self, f: Callable[P2, R2]) -> C[P2, R2, ..., R]: ... + @overload + def __call__(self, **kwargs) -> C[P, R, ..., ...]: ... # E: Cannot use "[VarArg(Any), KwArg(Any)]" for regular type variable, only for ParamSpec +[builtins fixtures/tuple.pyi] + +[case testGenericOverloadOverlapUnion] +from typing import TypeVar, overload, Union, Generic + +K = TypeVar("K") +V = TypeVar("V") +T = TypeVar("T") + +class C(Generic[K, V]): + @overload + def pop(self, key: K) -> V: ... + @overload + def pop(self, key: K, default: Union[V, T] = ...) -> Union[V, T]: ... + def pop(self, key: K, default: Union[V, T] = ...) -> Union[V, T]: + ... + +[case testOverloadedGenericInit] +from typing import TypeVar, overload, Union, Generic + +T = TypeVar("T") +S = TypeVar("S") + +class Int(Generic[T]): ... +class Str(Generic[T]): ... + +class C(Generic[T]): + @overload + def __init__(self: C[Int[S]], x: int, y: S) -> None: ... + @overload + def __init__(self: C[Str[S]], x: str, y: S) -> None: ... + def __init__(self, x, y) -> None: ... + +def foo(x: T): + reveal_type(C) # N: Revealed type is "Overload(def [T, S] (x: builtins.int, y: S`-1) -> __main__.C[__main__.Int[S`-1]], def [T, S] (x: builtins.str, y: S`-1) -> __main__.C[__main__.Str[S`-1]])" + reveal_type(C(0, x)) # N: Revealed type is "__main__.C[__main__.Int[T`-1]]" + reveal_type(C("yes", x)) # N: Revealed type is "__main__.C[__main__.Str[T`-1]]" diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index cab7d2bf6819..37916c2155fe 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -23,19 +23,19 @@ x: P # E: ParamSpec "P" is unbound def foo1(x: Callable[P, int]) -> Callable[P, str]: ... def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \ - # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate \ # N: You can use Concatenate as the first argument to Callable def foo4(x: List[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ - # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" def foo5(x: Callable[[int, str], P]) -> None: ... # E: Invalid location for ParamSpec "P" \ - # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpec "P" \ - # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" [builtins fixtures/paramspec.pyi] [case testParamSpecImports] @@ -901,8 +901,8 @@ class A: def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`4, *_P.args, **_P.kwargs) -> _R`4" -reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`8, *_P.args, **_P.kwargs) -> _R`8" +reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`15, *_P.args, **_P.kwargs) -> _R`15" +reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`19, *_P.args, **_P.kwargs) -> _R`19" def f(x: int) -> int: ... @@ -933,8 +933,8 @@ class A: def func(self, action: Job[_P, None]) -> Job[_P, None]: ... -reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]" -reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`5, None]) -> __main__.Job[_P`5, None]" +reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`13, None]) -> __main__.Job[_P`13, None]" +reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`15, None]) -> __main__.Job[_P`15, None]" reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" def f(x: int, y: int) -> None: ... @@ -1096,7 +1096,7 @@ j = Job(generic_f) reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`3)" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`13)" reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] @@ -1115,10 +1115,10 @@ class Job(Generic[_P, _T]): def generic_f(x: _T) -> _T: ... j = Job(generic_f) -reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`3], _T`3]" +reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`12], _T`12]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`4) -> _T`4" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`13) -> _T`13" reveal_type(jf(1)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] @@ -1520,7 +1520,7 @@ T = TypeVar("T") A = List[T] def f(x: A[[int, str]]) -> None: ... # E: Bracketed expression "[...]" is not valid as a type def g(x: A[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ - # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" C = Callable[P, T] x: C[int] # E: Bad number of arguments for type alias, expected 2, given 1 @@ -1640,13 +1640,13 @@ U = TypeVar("U") def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def test(x: U) -> U: ... reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]" -reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]" +reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`12) -> builtins.list[T`12]" class A: ... TA = TypeVar("TA", bound=A) def test_with_bound(x: TA) -> TA: ... -reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`5) -> builtins.list[T`5]" +reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`14) -> builtins.list[T`14]" dec(test_with_bound)(0) # E: Value of type variable "T" of function cannot be "int" dec(test_with_bound)(A()) # OK [builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index e49a7a0e2e2f..e99b859bbcd0 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1793,7 +1793,7 @@ class C: def bar(self) -> Self: ... def foo(self, x: S) -> Tuple[Self, S]: ... -reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> Tuple[Self`1, S`2]" +reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`7, x: S`8) -> Tuple[Self`7, S`8]" reveal_type(C().foo(42)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" [builtins fixtures/tuple.pyi] @@ -1807,7 +1807,7 @@ class C: def bar(self) -> Self: ... foo: Callable[[S, Self], Tuple[Self, S]] -reveal_type(C().foo) # N: Revealed type is "def [S] (S`1, __main__.C) -> Tuple[__main__.C, S`1]" +reveal_type(C().foo) # N: Revealed type is "def [S] (S`7, __main__.C) -> Tuple[__main__.C, S`7]" reveal_type(C().foo(42, C())) # N: Revealed type is "Tuple[__main__.C, builtins.int]" class This: ... [builtins fixtures/tuple.pyi] @@ -2032,7 +2032,7 @@ class Ben(Object): } @classmethod def doit(cls) -> Foo: - reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]" + reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`10) -> Self`10]" foo_method = cls.MY_MAP["foo"] return foo_method(Foo()) [builtins fixtures/isinstancelist.pyi] From 6427da62add428d452f690aabbb6c272ff713710 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Jun 2024 20:12:15 +0100 Subject: [PATCH 0630/1617] Properly handle unpacks in overlap checks (#17356) Fixes https://github.com/python/mypy/issues/17319 This is still not 100% robust, but at least it should not crash, and should cover correctly vast majority of cases. --- mypy/meet.py | 34 +++++++++++++++ test-data/unit/check-typevar-tuple.test | 57 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/mypy/meet.py b/mypy/meet.py index 2d44cafb23b3..48e5dfaa18ee 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -611,6 +611,19 @@ def are_tuples_overlapping( right = adjust_tuple(right, left) or right assert isinstance(left, TupleType), f"Type {left} is not a tuple" assert isinstance(right, TupleType), f"Type {right} is not a tuple" + + # This algorithm works well if only one tuple is variadic, if both are + # variadic we may get rare false negatives for overlapping prefix/suffix. + # Also, this ignores empty unpack case, but it is probably consistent with + # how we handle e.g. empty lists in overload overlaps. + # TODO: write a more robust algorithm for cases where both types are variadic. + left_unpack = find_unpack_in_list(left.items) + right_unpack = find_unpack_in_list(right.items) + if left_unpack is not None: + left = expand_tuple_if_possible(left, len(right.items)) + if right_unpack is not None: + right = expand_tuple_if_possible(right, len(left.items)) + if len(left.items) != len(right.items): return False return all( @@ -624,6 +637,27 @@ def are_tuples_overlapping( ) +def expand_tuple_if_possible(tup: TupleType, target: int) -> TupleType: + if len(tup.items) > target + 1: + return tup + extra = target + 1 - len(tup.items) + new_items = [] + for it in tup.items: + if not isinstance(it, UnpackType): + new_items.append(it) + continue + unpacked = get_proper_type(it.type) + if isinstance(unpacked, TypeVarTupleType): + instance = unpacked.tuple_fallback + else: + # Nested non-variadic tuples should be normalized at this point. + assert isinstance(unpacked, Instance) + instance = unpacked + assert instance.type.fullname == "builtins.tuple" + new_items.extend([instance.args[0]] * extra) + return tup.copy_modified(items=new_items) + + def adjust_tuple(left: ProperType, r: ProperType) -> TupleType | None: """Find out if `left` is a Tuple[A, ...], and adjust its length to `right`""" if isinstance(left, Instance) and left.type.fullname == "builtins.tuple": diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 21415abb9c28..2751e01aa21a 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1808,6 +1808,63 @@ def test(a: Tuple[int, str], b: Tuple[bool], c: Tuple[bool, ...]): reveal_type(add(b, c)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" [builtins fixtures/tuple.pyi] +[case testTypeVarTupleOverloadOverlap] +from typing import Union, overload, Tuple +from typing_extensions import Unpack + +class Int(int): ... + +A = Tuple[int, Unpack[Tuple[int, ...]]] +B = Tuple[int, Unpack[Tuple[str, ...]]] + +@overload +def f(arg: A) -> int: ... +@overload +def f(arg: B) -> str: ... +def f(arg: Union[A, B]) -> Union[int, str]: + ... + +A1 = Tuple[int, Unpack[Tuple[Int, ...]]] +B1 = Tuple[Unpack[Tuple[Int, ...]], int] + +@overload +def f1(arg: A1) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +@overload +def f1(arg: B1) -> str: ... +def f1(arg: Union[A1, B1]) -> Union[int, str]: + ... + +A2 = Tuple[int, int, int] +B2 = Tuple[int, Unpack[Tuple[int, ...]]] + +@overload +def f2(arg: A2) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +@overload +def f2(arg: B2) -> str: ... +def f2(arg: Union[A2, B2]) -> Union[int, str]: + ... + +A3 = Tuple[int, int, int] +B3 = Tuple[int, Unpack[Tuple[str, ...]]] + +@overload +def f3(arg: A3) -> int: ... +@overload +def f3(arg: B3) -> str: ... +def f3(arg: Union[A3, B3]) -> Union[int, str]: + ... + +A4 = Tuple[int, int, Unpack[Tuple[int, ...]]] +B4 = Tuple[int] + +@overload +def f4(arg: A4) -> int: ... +@overload +def f4(arg: B4) -> str: ... +def f4(arg: Union[A4, B4]) -> Union[int, str]: + ... +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleIndexOldStyleNonNormalizedAndNonLiteral] from typing import Any, Tuple from typing_extensions import Unpack From 4fa4657d7d9e41b5b3b7ae093ccdc360ce6e1b95 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Jun 2024 20:40:06 +0100 Subject: [PATCH 0631/1617] Fix type application for classes with generic constructors (#17354) Fixes https://github.com/python/mypy/issues/17212 Note I removed the problematic asset after all. It is hard to maintain it, since this function may be called from both explicit application, and from type inference code paths. And these two cases may have different min/max type argument count (see tests and comments for examples). --- mypy/applytype.py | 5 ++- mypy/checkexpr.py | 41 +++++++++++++++++++------ mypy/typeanal.py | 6 ++++ test-data/unit/check-generics.test | 13 ++++++++ test-data/unit/check-typevar-tuple.test | 27 ++++++++++++++++ 5 files changed, 80 insertions(+), 12 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 4847570b1712..783748cd8a5e 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -101,8 +101,7 @@ def apply_generic_arguments( bound or constraints, instead of giving an error. """ tvars = callable.variables - min_arg_count = sum(not tv.has_default() for tv in tvars) - assert min_arg_count <= len(orig_types) <= len(tvars) + assert len(orig_types) <= len(tvars) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. # Create a map from type variable id to target type. @@ -156,7 +155,7 @@ def apply_generic_arguments( type_is = None # The callable may retain some type vars if only some were applied. - # TODO: move apply_poly() logic from checkexpr.py here when new inference + # TODO: move apply_poly() logic here when new inference # becomes universally used (i.e. in all passes + in unification). # With this new logic we can actually *add* some new free variables. remaining_tvars: list[TypeVarLikeType] = [] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 779d63c8d385..c34952b084f9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4781,7 +4781,11 @@ class C(Generic[T, Unpack[Ts]]): ... We simply group the arguments that need to go into Ts variable into a TupleType, similar to how it is done in other places using split_with_prefix_and_suffix(). """ - vars = t.variables + if t.is_type_obj(): + # Type arguments must map to class type variables, ignoring constructor vars. + vars = t.type_object().defn.type_vars + else: + vars = list(t.variables) args = flatten_nested_tuples(args) # TODO: this logic is duplicated with semanal_typeargs. @@ -4799,6 +4803,7 @@ class C(Generic[T, Unpack[Ts]]): ... if not vars or not any(isinstance(v, TypeVarTupleType) for v in vars): return list(args) + # TODO: in future we may want to support type application to variadic functions. assert t.is_type_obj() info = t.type_object() # We reuse the logic from semanal phase to reduce code duplication. @@ -4832,10 +4837,23 @@ def apply_type_arguments_to_callable( tp = get_proper_type(tp) if isinstance(tp, CallableType): - min_arg_count = sum(not v.has_default() for v in tp.variables) - has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in tp.variables) + if tp.is_type_obj(): + # If we have a class object in runtime context, then the available type + # variables are those of the class, we don't include additional variables + # of the constructor. So that with + # class C(Generic[T]): + # def __init__(self, f: Callable[[S], T], x: S) -> None + # C[int] is valid + # C[int, str] is invalid (although C as a callable has 2 type variables) + # Note: various logic below and in applytype.py relies on the fact that + # class type variables appear *before* constructor variables. + type_vars = tp.type_object().defn.type_vars + else: + type_vars = list(tp.variables) + min_arg_count = sum(not v.has_default() for v in type_vars) + has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in type_vars) if ( - len(args) < min_arg_count or len(args) > len(tp.variables) + len(args) < min_arg_count or len(args) > len(type_vars) ) and not has_type_var_tuple: if tp.is_type_obj() and tp.type_object().fullname == "builtins.tuple": # e.g. expression tuple[X, Y] @@ -4854,19 +4872,24 @@ def apply_type_arguments_to_callable( bound_args=tp.bound_args, ) self.msg.incompatible_type_application( - min_arg_count, len(tp.variables), len(args), ctx + min_arg_count, len(type_vars), len(args), ctx ) return AnyType(TypeOfAny.from_error) return self.apply_generic_arguments(tp, self.split_for_callable(tp, args, ctx), ctx) if isinstance(tp, Overloaded): for it in tp.items: - min_arg_count = sum(not v.has_default() for v in it.variables) - has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in it.variables) + if tp.is_type_obj(): + # Same as above. + type_vars = tp.type_object().defn.type_vars + else: + type_vars = list(it.variables) + min_arg_count = sum(not v.has_default() for v in type_vars) + has_type_var_tuple = any(isinstance(v, TypeVarTupleType) for v in type_vars) if ( - len(args) < min_arg_count or len(args) > len(it.variables) + len(args) < min_arg_count or len(args) > len(type_vars) ) and not has_type_var_tuple: self.msg.incompatible_type_application( - min_arg_count, len(it.variables), len(args), ctx + min_arg_count, len(type_vars), len(args), ctx ) return AnyType(TypeOfAny.from_error) return Overloaded( diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 28abd24149e6..82c90272d6c2 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2376,6 +2376,12 @@ def validate_instance(t: Instance, fail: MsgCallback, empty_tuple_index: bool) - if not t.args: if not (empty_tuple_index and len(t.type.type_vars) == 1): # The Any arguments should be set by the caller. + if empty_tuple_index and min_tv_count: + fail( + f"At least {min_tv_count} type argument(s) expected, none given", + t, + code=codes.TYPE_ARG, + ) return False elif not correct: fail( diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b4b075694bb4..ea3f501fd949 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3443,6 +3443,19 @@ h: Callable[[Unpack[Us]], Foo[int]] reveal_type(dec(h)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[builtins.int]" [builtins fixtures/list.pyi] +[case testTypeApplicationGenericConstructor] +from typing import Generic, TypeVar, Callable + +T = TypeVar("T") +S = TypeVar("S") +class C(Generic[T]): + def __init__(self, f: Callable[[S], T], x: S) -> None: + self.x = f(x) + +reveal_type(C[int]) # N: Revealed type is "def [S] (f: def (S`-1) -> builtins.int, x: S`-1) -> __main__.C[builtins.int]" +Alias = C[int] +C[int, str] # E: Type application has too many types (1 expected) + [case testHigherOrderGenericPartial] from typing import TypeVar, Callable diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 2751e01aa21a..0aff702e1b22 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2378,3 +2378,30 @@ def a2(x: Array[int, str]) -> None: reveal_type(func(x, 2, "Hello", True)) # E: Cannot infer type argument 1 of "func" \ # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleTypeApplicationOverload] +from typing import Generic, TypeVar, TypeVarTuple, Unpack, overload, Callable + +T = TypeVar("T") +T1 = TypeVar("T1") +T2 = TypeVar("T2") +T3 = TypeVar("T3") +Ts = TypeVarTuple("Ts") + +class C(Generic[T, Unpack[Ts]]): + @overload + def __init__(self, f: Callable[[Unpack[Ts]], T]) -> None: ... + @overload + def __init__(self, f: Callable[[T1, T2, T3, Unpack[Ts]], T], a: T1, b: T2, c: T3) -> None: ... + def __init__(self, f, *args, **kwargs) -> None: + ... + +reveal_type(C[int, str]) # N: Revealed type is "Overload(def (f: def (builtins.str) -> builtins.int) -> __main__.C[builtins.int, builtins.str], def [T1, T2, T3] (f: def (T1`-1, T2`-2, T3`-3, builtins.str) -> builtins.int, a: T1`-1, b: T2`-2, c: T3`-3) -> __main__.C[builtins.int, builtins.str])" +Alias = C[int, str] + +def f(x: int, y: int, z: int, t: int) -> str: ... +x = C(f, 0, 0, "hm") # E: Argument 1 to "C" has incompatible type "Callable[[int, int, int, int], str]"; expected "Callable[[int, int, str, int], str]" +reveal_type(x) # N: Revealed type is "__main__.C[builtins.str, builtins.int]" +reveal_type(C(f)) # N: Revealed type is "__main__.C[builtins.str, builtins.int, builtins.int, builtins.int, builtins.int]" +C[()] # E: At least 1 type argument(s) expected, none given +[builtins fixtures/tuple.pyi] From 317533c5589c5778bad4dbf3b0205974491debac Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Jun 2024 23:09:51 +0100 Subject: [PATCH 0632/1617] Fix crash on TypedDict unpacking for ParamSpec (#17358) Fixes https://github.com/python/mypy/issues/17345 Fixes https://github.com/python/mypy/issues/17112 Fixes https://github.com/python/mypy/issues/16616 Oh well, I clearly remember I have put those lines before `if` only because otherwise the line would be 101 chars long, and I didn't want to wrap arguments. Now I see it was a bad idea, LOL. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/constraints.py | 9 ++++++--- test-data/unit/check-typeddict.test | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 46221bd82628..56ca51d19486 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -223,9 +223,6 @@ def infer_constraints_for_callable( if actual_arg_type is None: continue - actual_type = mapper.expand_actual_type( - actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i] - ) if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): # If actual arguments are mapped to ParamSpec type, we can't infer individual # constraints, instead store them and infer single constraint at the end. @@ -243,6 +240,12 @@ def infer_constraints_for_callable( ) param_spec_arg_names.append(arg_names[actual] if arg_names else None) else: + actual_type = mapper.expand_actual_type( + actual_arg_type, + arg_kinds[actual], + callee.arg_names[i], + callee.arg_kinds[i], + ) c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF) constraints.extend(c) if ( diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 5fb74f66dd89..fa77d98e4a34 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3525,3 +3525,28 @@ class B(A): reveal_type(B.f) # N: Revealed type is "def (self: __main__.B, **kwargs: Unpack[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: builtins.str})])" B().f(x=1.0) # E: Argument "x" to "f" of "B" has incompatible type "float"; expected "int" [builtins fixtures/primitives.pyi] + +[case testTypedDictUnpackWithParamSpecInference] +from typing import TypeVar, ParamSpec, Callable +from typing_extensions import TypedDict, Unpack + +P = ParamSpec("P") +R = TypeVar("R") + +def run(func: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: ... + +class Params(TypedDict): + temperature: float + +def test(temperature: int) -> None: ... +def test2(temperature: float, other: str) -> None: ... + +class Test: + def f(self, c: Callable[..., None], **params: Unpack[Params]) -> None: + run(c, **params) + def g(self, **params: Unpack[Params]) -> None: + run(test, **params) # E: Argument "temperature" to "run" has incompatible type "float"; expected "int" + def h(self, **params: Unpack[Params]) -> None: + run(test2, other="yes", **params) + run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str" +[builtins fixtures/tuple.pyi] From b8a026017de10969d35de9d1ea7951428b95dfbc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 11 Jun 2024 09:35:08 +0100 Subject: [PATCH 0633/1617] Fix crash when overriding with unpacked TypedDict (#17359) Fixes https://github.com/python/mypy/issues/17208 While writing the fix (that is trivial), I could not notice that the relevant code simply assumes functions can have nothing but positional parameters. This could lead really misleading error messages, so I decided to fix this as well. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 26 ++++++++++---- test-data/unit/check-functions.test | 53 ++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 04e90c3e94cd..70db31c9a94f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2255,6 +2255,7 @@ def check_override( if fail: emitted_msg = False + offset_arguments = isinstance(override, CallableType) and override.unpack_kwargs # Normalize signatures, so we get better diagnostics. if isinstance(override, (CallableType, Overloaded)): override = override.with_unpacked_kwargs() @@ -2285,12 +2286,23 @@ def check_override( def erase_override(t: Type) -> Type: return erase_typevars(t, ids_to_erase=override_ids) - for i in range(len(override.arg_types)): - if not is_subtype( - original.arg_types[i], erase_override(override.arg_types[i]) - ): - arg_type_in_super = original.arg_types[i] - + for i, (sub_kind, super_kind) in enumerate( + zip(override.arg_kinds, original.arg_kinds) + ): + if sub_kind.is_positional() and super_kind.is_positional(): + override_arg_type = override.arg_types[i] + original_arg_type = original.arg_types[i] + elif sub_kind.is_named() and super_kind.is_named() and not offset_arguments: + arg_name = override.arg_names[i] + if arg_name in original.arg_names: + override_arg_type = override.arg_types[i] + original_i = original.arg_names.index(arg_name) + original_arg_type = original.arg_types[original_i] + else: + continue + else: + continue + if not is_subtype(original_arg_type, erase_override(override_arg_type)): if isinstance(node, FuncDef) and not node.is_property: context: Context = node.arguments[i + len(override.bound_args)] else: @@ -2300,7 +2312,7 @@ def erase_override(t: Type) -> Type: name, type_name, name_in_super, - arg_type_in_super, + original_arg_type, supertype, context, secondary_context=node, diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 4b04a3b96ae4..ef6ca9f3b285 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -40,11 +40,10 @@ class B(A): class C(A): def f(self, *, b: int, a: str) -> None: pass # Fail [out] -main:10: error: Signature of "f" incompatible with supertype "A" -main:10: note: Superclass: -main:10: note: def f(self, *, a: int, b: str) -> None -main:10: note: Subclass: -main:10: note: def f(self, *, b: int, a: str) -> None +main:10: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str" +main:10: note: This violates the Liskov substitution principle +main:10: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +main:10: error: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" [case testPositionalOverridingArgumentNameInsensitivity] import typing @@ -3324,3 +3323,47 @@ class Bar(Foo): # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides ... [builtins fixtures/property.pyi] + +[case testNoCrashOnUnpackOverride] +from typing import Unpack +from typing_extensions import TypedDict + +class Params(TypedDict): + x: int + y: str + +class Other(TypedDict): + x: int + y: int + +class B: + def meth(self, **kwargs: Unpack[Params]) -> None: + ... +class C(B): + def meth(self, **kwargs: Unpack[Other]) -> None: # E: Signature of "meth" incompatible with supertype "B" \ + # N: Superclass: \ + # N: def meth(*, x: int, y: str) -> None \ + # N: Subclass: \ + # N: def meth(*, x: int, y: int) -> None + + ... +[builtins fixtures/tuple.pyi] + +[case testOverrideErrorLocationNamed] +class B: + def meth( + self, *, + x: int, + y: str, + ) -> None: + ... +class C(B): + def meth( + self, *, + y: int, # E: Argument 1 of "meth" is incompatible with supertype "B"; supertype defines the argument type as "str" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + x: int, + ) -> None: + ... +[builtins fixtures/tuple.pyi] From 415d49f25b6315cf1b7a04046a942246a033498d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 11 Jun 2024 17:13:46 +0100 Subject: [PATCH 0634/1617] [mypyc] Support new syntax for generic functions and classes (PEP 695) (#17357) Generate an implicit `Generic` base class for new-style generic classes. For this to work, also create C statics that can be used to access type variable objects (e.g. `T` or `Ts`) at runtime. These are needed when evaluating base classes. Import `TypeVar` and friends from the `_typing` C extension instead of `typing`, since the latter is pretty slow to import, and we don't want to add a hidden new runtime dependency in case the full `typing` module isn't needed. Generic functions don't need any changes, since they don't support indexing with a type, and type variable types aren't valid in runtime contexts. Type erasure seems sufficient, especially considering that mypyc doesn't support classes nested within functions. (I'm not 100% sure about this though, and we might need to put function type variables into statics eventually.) Update builtins test fixtures used in mypyc tests to not defined type variables such as `T`, since these leak into tests and can produce unexpected or unrealistic results. Ignore upper bounds and value restrictions. These are only used for type checking. This should only affect introspection of type variables, which isn't properly supported in compiled code anyway. New type alias syntax is not supported in this PR. --- mypy/nodes.py | 9 +- mypy/semanal.py | 9 +- mypyc/codegen/emitfunc.py | 11 +- mypyc/codegen/emitmodule.py | 11 ++ mypyc/common.py | 1 + mypyc/ir/module_ir.py | 6 + mypyc/ir/ops.py | 3 + mypyc/irbuild/builder.py | 17 +++ mypyc/irbuild/classdef.py | 62 +++++++- mypyc/irbuild/expression.py | 5 + mypyc/irbuild/main.py | 1 + mypyc/primitives/generic_ops.py | 2 +- mypyc/test-data/fixtures/ir.py | 132 ++++++++--------- mypyc/test-data/fixtures/typing-full.pyi | 3 + mypyc/test-data/irbuild-set.test | 5 +- mypyc/test-data/run-loops.test | 11 +- mypyc/test-data/run-python312.test | 172 +++++++++++++++++++++++ mypyc/test/test_run.py | 3 + 18 files changed, 382 insertions(+), 81 deletions(-) create mode 100644 mypyc/test-data/run-python312.test diff --git a/mypy/nodes.py b/mypy/nodes.py index 90561779051d..850b1db87556 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2535,8 +2535,9 @@ def __init__( default: mypy.types.Type, variance: int = INVARIANT, is_new_style: bool = False, + line: int = -1, ) -> None: - super().__init__() + super().__init__(line=line) self._name = name self._fullname = fullname self.upper_bound = upper_bound @@ -2582,8 +2583,9 @@ def __init__( default: mypy.types.Type, variance: int = INVARIANT, is_new_style: bool = False, + line: int = -1, ) -> None: - super().__init__(name, fullname, upper_bound, default, variance, is_new_style) + super().__init__(name, fullname, upper_bound, default, variance, is_new_style, line=line) self.values = values def accept(self, visitor: ExpressionVisitor[T]) -> T: @@ -2661,8 +2663,9 @@ def __init__( default: mypy.types.Type, variance: int = INVARIANT, is_new_style: bool = False, + line: int = -1, ) -> None: - super().__init__(name, fullname, upper_bound, default, variance, is_new_style) + super().__init__(name, fullname, upper_bound, default, variance, is_new_style, line=line) self.tuple_fallback = tuple_fallback def accept(self, visitor: ExpressionVisitor[T]) -> T: diff --git a/mypy/semanal.py b/mypy/semanal.py index 903af80fe404..8da5c68d562d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1709,7 +1709,7 @@ def push_type_args( self.scope_stack.append(SCOPE_ANNOTATION) tvs: list[tuple[str, TypeVarLikeExpr]] = [] for p in type_args: - tv = self.analyze_type_param(p) + tv = self.analyze_type_param(p, context) if tv is None: return None tvs.append((p.name, tv)) @@ -1732,7 +1732,9 @@ def is_defined_type_param(self, name: str) -> bool: return True return False - def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: + def analyze_type_param( + self, type_param: TypeParam, context: Context + ) -> TypeVarLikeExpr | None: fullname = self.qualified_name(type_param.name) if type_param.upper_bound: upper_bound = self.anal_type(type_param.upper_bound) @@ -1757,6 +1759,7 @@ def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: default=default, variance=VARIANCE_NOT_READY, is_new_style=True, + line=context.line, ) elif type_param.kind == PARAM_SPEC_KIND: return ParamSpecExpr( @@ -1765,6 +1768,7 @@ def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: upper_bound=upper_bound, default=default, is_new_style=True, + line=context.line, ) else: assert type_param.kind == TYPE_VAR_TUPLE_KIND @@ -1777,6 +1781,7 @@ def analyze_type_param(self, type_param: TypeParam) -> TypeVarLikeExpr | None: tuple_fallback=tuple_fallback, default=default, is_new_style=True, + line=context.line, ) def pop_type_args(self, type_args: list[TypeParam] | None) -> None: diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 12f57b9cee6f..d945a28d8481 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -6,7 +6,14 @@ from mypyc.analysis.blockfreq import frequently_executed_blocks from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer -from mypyc.common import MODULE_PREFIX, NATIVE_PREFIX, REG_PREFIX, STATIC_PREFIX, TYPE_PREFIX +from mypyc.common import ( + MODULE_PREFIX, + NATIVE_PREFIX, + REG_PREFIX, + STATIC_PREFIX, + TYPE_PREFIX, + TYPE_VAR_PREFIX, +) from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values from mypyc.ir.ops import ( @@ -14,6 +21,7 @@ NAMESPACE_MODULE, NAMESPACE_STATIC, NAMESPACE_TYPE, + NAMESPACE_TYPE_VAR, Assign, AssignMulti, BasicBlock, @@ -477,6 +485,7 @@ def visit_set_attr(self, op: SetAttr) -> None: NAMESPACE_STATIC: STATIC_PREFIX, NAMESPACE_TYPE: TYPE_PREFIX, NAMESPACE_MODULE: MODULE_PREFIX, + NAMESPACE_TYPE_VAR: TYPE_VAR_PREFIX, } def visit_load_static(self, op: LoadStatic) -> None: diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 6c8f5ac91335..1d8708912de5 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -41,6 +41,7 @@ PREFIX, RUNTIME_C_FILES, TOP_LEVEL_NAME, + TYPE_VAR_PREFIX, shared_lib_name, short_id_from_name, use_vectorcall, @@ -590,6 +591,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: self.declare_finals(module_name, module.final_names, declarations) for cl in module.classes: generate_class_type_decl(cl, emitter, ext_declarations, declarations) + self.declare_type_vars(module_name, module.type_var_names, declarations) for fn in module.functions: generate_function_declaration(fn, declarations) @@ -1063,6 +1065,15 @@ def declare_static_pyobject(self, identifier: str, emitter: Emitter) -> None: symbol = emitter.static_name(identifier, None) self.declare_global("PyObject *", symbol) + def declare_type_vars(self, module: str, type_var_names: list[str], emitter: Emitter) -> None: + for name in type_var_names: + static_name = emitter.static_name(name, module, prefix=TYPE_VAR_PREFIX) + emitter.context.declarations[static_name] = HeaderDeclaration( + f"PyObject *{static_name};", + [f"PyObject *{static_name} = NULL;"], + needs_export=False, + ) + def sort_classes(classes: list[tuple[str, ClassIR]]) -> list[tuple[str, ClassIR]]: mod_name = {ir: name for name, ir in classes} diff --git a/mypyc/common.py b/mypyc/common.py index 3d07f6c3d0d3..d7610fe15c41 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -13,6 +13,7 @@ STATIC_PREFIX: Final = "CPyStatic_" # Static variables (for literals etc.) TYPE_PREFIX: Final = "CPyType_" # Type object struct MODULE_PREFIX: Final = "CPyModule_" # Cached modules +TYPE_VAR_PREFIX: Final = "CPyTypeVar_" # Type variables when using new-style Python 3.12 syntax ATTR_PREFIX: Final = "_" # Attributes ENV_ATTR_NAME: Final = "__mypyc_env__" diff --git a/mypyc/ir/module_ir.py b/mypyc/ir/module_ir.py index dcf6f8768547..e3b240629eda 100644 --- a/mypyc/ir/module_ir.py +++ b/mypyc/ir/module_ir.py @@ -21,12 +21,17 @@ def __init__( functions: list[FuncIR], classes: list[ClassIR], final_names: list[tuple[str, RType]], + type_var_names: list[str], ) -> None: self.fullname = fullname self.imports = imports.copy() self.functions = functions self.classes = classes self.final_names = final_names + # Names of C statics used for Python 3.12 type variable objects. + # These are only visible in the module that defined them, so no need + # to serialize. + self.type_var_names = type_var_names def serialize(self) -> JsonDict: return { @@ -45,6 +50,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ModuleIR: [ctx.functions[FuncDecl.get_id_from_json(f)] for f in data["functions"]], [ClassIR.deserialize(c, ctx) for c in data["classes"]], [(k, deserialize_type(t, ctx)) for k, t in data["final_names"]], + [], ) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 377266e797d9..896ba3ac091c 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -789,6 +789,9 @@ def accept(self, visitor: OpVisitor[T]) -> T: # Namespace for modules NAMESPACE_MODULE: Final = "module" +# Namespace for Python 3.12 type variable objects (implicitly created TypeVar instances, etc.) +NAMESPACE_TYPE_VAR: Final = "typevar" + class LoadStatic(RegisterOp): """Load a static name (name :: static). diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index cca771e82c83..1b4f551d4a2a 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -69,6 +69,7 @@ from mypyc.ir.func_ir import INVALID_FUNC_DEF, FuncDecl, FuncIR, FuncSignature, RuntimeArg from mypyc.ir.ops import ( NAMESPACE_MODULE, + NAMESPACE_TYPE_VAR, Assign, BasicBlock, Branch, @@ -179,6 +180,7 @@ def __init__( self.function_names: set[tuple[str | None, str]] = set() self.classes: list[ClassIR] = [] self.final_names: list[tuple[str, RType]] = [] + self.type_var_names: list[str] = [] self.callable_class_names: set[str] = set() self.options = options @@ -541,6 +543,21 @@ def load_final_static( error_msg=f'value for final name "{error_name}" was not set', ) + def init_type_var(self, value: Value, name: str, line: int) -> None: + unique_name = name + "___" + str(line) + self.type_var_names.append(unique_name) + self.add(InitStatic(value, unique_name, self.module_name, namespace=NAMESPACE_TYPE_VAR)) + + def load_type_var(self, name: str, line: int) -> Value: + return self.add( + LoadStatic( + object_rprimitive, + name + "___" + str(line), + self.module_name, + namespace=NAMESPACE_TYPE_VAR, + ) + ) + def load_literal_value(self, val: int | str | bytes | float | complex | bool) -> Value: """Load value of a final name, class-level attribute, or constant folded expression.""" if isinstance(val, bool): diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 3f6ec0f33822..303ee8849244 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,6 +7,9 @@ from typing import Callable, Final from mypy.nodes import ( + PARAM_SPEC_KIND, + TYPE_VAR_KIND, + TYPE_VAR_TUPLE_KIND, AssignmentStmt, CallExpr, ClassDef, @@ -22,6 +25,7 @@ StrExpr, TempNode, TypeInfo, + TypeParam, is_class_var, ) from mypy.types import ENUM_REMOVED_PROPS, Instance, RawExpressionType, get_proper_type @@ -63,9 +67,16 @@ ) from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op -from mypyc.primitives.generic_ops import py_hasattr_op, py_setattr_op +from mypyc.primitives.generic_ops import ( + iter_op, + next_op, + py_get_item_op, + py_hasattr_op, + py_setattr_op, +) from mypyc.primitives.misc_ops import ( dataclass_sleight_of_hand, + import_op, not_implemented_op, py_calc_meta_op, pytype_from_template_op, @@ -405,8 +416,14 @@ def get_type_annotation(self, stmt: AssignmentStmt) -> TypeInfo | None: def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: # OK AND NOW THE FUN PART base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs - if base_exprs: - bases = [builder.accept(x) for x in base_exprs] + new_style_type_args = cdef.type_args + if new_style_type_args: + bases = [make_generic_base_class(builder, cdef.fullname, new_style_type_args, cdef.line)] + else: + bases = [] + + if base_exprs or new_style_type_args: + bases.extend([builder.accept(x) for x in base_exprs]) tp_bases = builder.new_tuple(bases, cdef.line) else: tp_bases = builder.add(LoadErrorValue(object_rprimitive, is_borrowed=True)) @@ -453,6 +470,45 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: return tp +def make_generic_base_class( + builder: IRBuilder, fullname: str, type_args: list[TypeParam], line: int +) -> Value: + """Construct Generic[...] base class object for a new-style generic class (Python 3.12).""" + mod = builder.call_c(import_op, [builder.load_str("_typing")], line) + tvs = [] + type_var_imported: Value | None = None + for type_param in type_args: + unpack = False + if type_param.kind == TYPE_VAR_KIND: + if type_var_imported: + # Reuse previously imported value as a minor optimization + tvt = type_var_imported + else: + tvt = builder.py_get_attr(mod, "TypeVar", line) + type_var_imported = tvt + elif type_param.kind == TYPE_VAR_TUPLE_KIND: + tvt = builder.py_get_attr(mod, "TypeVarTuple", line) + unpack = True + else: + assert type_param.kind == PARAM_SPEC_KIND + tvt = builder.py_get_attr(mod, "ParamSpec", line) + tv = builder.py_call(tvt, [builder.load_str(type_param.name)], line) + builder.init_type_var(tv, type_param.name, line) + if unpack: + # Evaluate *Ts for a TypeVarTuple + it = builder.call_c(iter_op, [tv], line) + tv = builder.call_c(next_op, [it], line) + tvs.append(tv) + gent = builder.py_get_attr(mod, "Generic", line) + if len(tvs) == 1: + arg = tvs[0] + else: + arg = builder.new_tuple(tvs, line) + + base = builder.call_c(py_get_item_op, [gent, arg], line) + return base + + # Mypy uses these internally as base classes of TypedDict classes. These are # lies and don't have any runtime equivalent. MAGIC_TYPED_DICT_CLASSES: Final[tuple[str, ...]] = ( diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index a16faf6cd7d7..8d7c089e20cd 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -44,6 +44,7 @@ TupleExpr, TypeApplication, TypeInfo, + TypeVarLikeExpr, UnaryExpr, Var, ) @@ -106,6 +107,10 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: + if isinstance(expr.node, TypeVarLikeExpr) and expr.node.is_new_style: + # Reference to Python 3.12 implicit TypeVar/TupleVarTuple/... object. + # These are stored in C statics and not visible in Python namespaces. + return builder.load_type_var(expr.node.name, expr.node.line) if expr.node is None: builder.add( RaiseStandardError( diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 85b905393af1..15928d939cbf 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -99,6 +99,7 @@ def build_ir( builder.functions, builder.classes, builder.final_names, + builder.type_var_names, ) result[module.fullname] = module_ir class_irs.extend(builder.classes) diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index 3caec0a9875e..fe42767db11e 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -178,7 +178,7 @@ ) # obj1[obj2] -method_op( +py_get_item_op = method_op( name="__getitem__", arg_types=[object_rprimitive, object_rprimitive], return_type=object_rprimitive, diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index bf06613ad2a8..6f0d8da90d57 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -7,12 +7,12 @@ overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol ) -T = TypeVar('T') +_T = TypeVar('_T') T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) -S = TypeVar('S') -K = TypeVar('K') # for keys in mapping -V = TypeVar('V') # for values in mapping +_S = TypeVar('_S') +_K = TypeVar('_K') # for keys in mapping +_V = TypeVar('_V') # for values in mapping class __SupportsAbs(Protocol[T_co]): def __abs__(self) -> T_co: pass @@ -199,76 +199,76 @@ def __contains__(self, item: object) -> int: ... class function: pass -class list(Generic[T], Sequence[T], Iterable[T]): - def __init__(self, i: Optional[Iterable[T]] = None) -> None: pass +class list(Generic[_T], Sequence[_T], Iterable[_T]): + def __init__(self, i: Optional[Iterable[_T]] = None) -> None: pass @overload - def __getitem__(self, i: int) -> T: ... + def __getitem__(self, i: int) -> _T: ... @overload - def __getitem__(self, s: slice) -> List[T]: ... - def __setitem__(self, i: int, o: T) -> None: pass + def __getitem__(self, s: slice) -> List[_T]: ... + def __setitem__(self, i: int, o: _T) -> None: pass def __delitem__(self, i: int) -> None: pass - def __mul__(self, i: int) -> List[T]: pass - def __rmul__(self, i: int) -> List[T]: pass - def __iter__(self) -> Iterator[T]: pass + def __mul__(self, i: int) -> List[_T]: pass + def __rmul__(self, i: int) -> List[_T]: pass + def __iter__(self) -> Iterator[_T]: pass def __len__(self) -> int: pass def __contains__(self, item: object) -> int: ... - def __add__(self, x: List[T]) -> List[T]: ... - def append(self, x: T) -> None: pass - def pop(self, i: int = -1) -> T: pass - def count(self, T) -> int: pass - def extend(self, l: Iterable[T]) -> None: pass - def insert(self, i: int, x: T) -> None: pass + def __add__(self, x: List[_T]) -> List[_T]: ... + def append(self, x: _T) -> None: pass + def pop(self, i: int = -1) -> _T: pass + def count(self, _T) -> int: pass + def extend(self, l: Iterable[_T]) -> None: pass + def insert(self, i: int, x: _T) -> None: pass def sort(self) -> None: pass def reverse(self) -> None: pass - def remove(self, o: T) -> None: pass - def index(self, o: T) -> int: pass + def remove(self, o: _T) -> None: pass + def index(self, o: _T) -> int: pass -class dict(Mapping[K, V]): +class dict(Mapping[_K, _V]): @overload - def __init__(self, **kwargs: K) -> None: ... + def __init__(self, **kwargs: _K) -> None: ... @overload - def __init__(self, map: Mapping[K, V], **kwargs: V) -> None: ... + def __init__(self, map: Mapping[_K, _V], **kwargs: _V) -> None: ... @overload - def __init__(self, iterable: Iterable[Tuple[K, V]], **kwargs: V) -> None: ... - def __getitem__(self, key: K) -> V: pass - def __setitem__(self, k: K, v: V) -> None: pass - def __delitem__(self, k: K) -> None: pass + def __init__(self, iterable: Iterable[Tuple[_K, _V]], **kwargs: _V) -> None: ... + def __getitem__(self, key: _K) -> _V: pass + def __setitem__(self, k: _K, v: _V) -> None: pass + def __delitem__(self, k: _K) -> None: pass def __contains__(self, item: object) -> int: pass - def __iter__(self) -> Iterator[K]: pass + def __iter__(self) -> Iterator[_K]: pass def __len__(self) -> int: pass @overload - def update(self, __m: Mapping[K, V], **kwargs: V) -> None: pass + def update(self, __m: Mapping[_K, _V], **kwargs: _V) -> None: pass @overload - def update(self, __m: Iterable[Tuple[K, V]], **kwargs: V) -> None: ... + def update(self, __m: Iterable[Tuple[_K, _V]], **kwargs: _V) -> None: ... @overload - def update(self, **kwargs: V) -> None: ... - def pop(self, x: int) -> K: pass - def keys(self) -> Iterable[K]: pass - def values(self) -> Iterable[V]: pass - def items(self) -> Iterable[Tuple[K, V]]: pass + def update(self, **kwargs: _V) -> None: ... + def pop(self, x: int) -> _K: pass + def keys(self) -> Iterable[_K]: pass + def values(self) -> Iterable[_V]: pass + def items(self) -> Iterable[Tuple[_K, _V]]: pass def clear(self) -> None: pass - def copy(self) -> Dict[K, V]: pass - def setdefault(self, key: K, val: V = ...) -> V: pass + def copy(self) -> Dict[_K, _V]: pass + def setdefault(self, key: _K, val: _V = ...) -> _V: pass -class set(Generic[T]): - def __init__(self, i: Optional[Iterable[T]] = None) -> None: pass - def __iter__(self) -> Iterator[T]: pass +class set(Generic[_T]): + def __init__(self, i: Optional[Iterable[_T]] = None) -> None: pass + def __iter__(self) -> Iterator[_T]: pass def __len__(self) -> int: pass - def add(self, x: T) -> None: pass - def remove(self, x: T) -> None: pass - def discard(self, x: T) -> None: pass + def add(self, x: _T) -> None: pass + def remove(self, x: _T) -> None: pass + def discard(self, x: _T) -> None: pass def clear(self) -> None: pass - def pop(self) -> T: pass - def update(self, x: Iterable[S]) -> None: pass - def __or__(self, s: Union[Set[S], FrozenSet[S]]) -> Set[Union[T, S]]: ... - def __xor__(self, s: Union[Set[S], FrozenSet[S]]) -> Set[Union[T, S]]: ... - -class frozenset(Generic[T]): - def __init__(self, i: Optional[Iterable[T]] = None) -> None: pass - def __iter__(self) -> Iterator[T]: pass + def pop(self) -> _T: pass + def update(self, x: Iterable[_S]) -> None: pass + def __or__(self, s: Union[Set[_S], FrozenSet[_S]]) -> Set[Union[_T, _S]]: ... + def __xor__(self, s: Union[Set[_S], FrozenSet[_S]]) -> Set[Union[_T, _S]]: ... + +class frozenset(Generic[_T]): + def __init__(self, i: Optional[Iterable[_T]] = None) -> None: pass + def __iter__(self) -> Iterator[_T]: pass def __len__(self) -> int: pass - def __or__(self, s: Union[Set[S], FrozenSet[S]]) -> FrozenSet[Union[T, S]]: ... - def __xor__(self, s: Union[Set[S], FrozenSet[S]]) -> FrozenSet[Union[T, S]]: ... + def __or__(self, s: Union[Set[_S], FrozenSet[_S]]) -> FrozenSet[Union[_T, _S]]: ... + def __xor__(self, s: Union[Set[_S], FrozenSet[_S]]) -> FrozenSet[Union[_T, _S]]: ... class slice: pass @@ -323,31 +323,31 @@ class OverflowError(ArithmeticError): pass class GeneratorExit(BaseException): pass -def any(i: Iterable[T]) -> bool: pass -def all(i: Iterable[T]) -> bool: pass -def sum(i: Iterable[T]) -> int: pass -def reversed(object: Sequence[T]) -> Iterator[T]: ... +def any(i: Iterable[_T]) -> bool: pass +def all(i: Iterable[_T]) -> bool: pass +def sum(i: Iterable[_T]) -> int: pass +def reversed(object: Sequence[_T]) -> Iterator[_T]: ... def id(o: object) -> int: pass # This type is obviously wrong but the test stubs don't have Sized anymore def len(o: object) -> int: pass def print(*object) -> None: pass def isinstance(x: object, t: object) -> bool: pass -def iter(i: Iterable[T]) -> Iterator[T]: pass +def iter(i: Iterable[_T]) -> Iterator[_T]: pass @overload -def next(i: Iterator[T]) -> T: pass +def next(i: Iterator[_T]) -> _T: pass @overload -def next(i: Iterator[T], default: T) -> T: pass +def next(i: Iterator[_T], default: _T) -> _T: pass def hash(o: object) -> int: ... def globals() -> Dict[str, Any]: ... def getattr(obj: object, name: str, default: Any = None) -> Any: ... def setattr(obj: object, name: str, value: Any) -> None: ... -def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ... +def enumerate(x: Iterable[_T]) -> Iterator[Tuple[int, _T]]: ... @overload -def zip(x: Iterable[T], y: Iterable[S]) -> Iterator[Tuple[T, S]]: ... +def zip(x: Iterable[_T], y: Iterable[_S]) -> Iterator[Tuple[_T, _S]]: ... @overload -def zip(x: Iterable[T], y: Iterable[S], z: Iterable[V]) -> Iterator[Tuple[T, S, V]]: ... +def zip(x: Iterable[_T], y: Iterable[_S], z: Iterable[_V]) -> Iterator[Tuple[_T, _S, _V]]: ... def eval(e: str) -> Any: ... -def abs(x: __SupportsAbs[T]) -> T: ... +def abs(x: __SupportsAbs[_T]) -> _T: ... @overload def divmod(x: __SupportsDivMod[T_contra, T_co], y: T_contra) -> T_co: ... @overload @@ -359,8 +359,8 @@ def pow(base: __SupportsPow3NoneOnly[T_contra, T_co], exp: T_contra, mod: None = @overload def pow(base: __SupportsPow3[T_contra, _M, T_co], exp: T_contra, mod: _M) -> T_co: ... def exit() -> None: ... -def min(x: T, y: T) -> T: ... -def max(x: T, y: T) -> T: ... +def min(x: _T, y: _T) -> _T: ... +def max(x: _T, y: _T) -> _T: ... def repr(o: object) -> str: ... def ascii(o: object) -> str: ... def ord(o: object) -> int: ... diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi index 52bca09a1dec..3ddc1f1bba08 100644 --- a/mypyc/test-data/fixtures/typing-full.pyi +++ b/mypyc/test-data/fixtures/typing-full.pyi @@ -167,3 +167,6 @@ class _TypedDict(Mapping[str, object]): def pop(self, k: NoReturn, default: T = ...) -> object: ... def update(self: T, __m: T) -> None: ... def __delitem__(self, k: NoReturn) -> None: ... + +class TypeAliasType: + pass diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 1ac638754a8b..110801b78a66 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -39,7 +39,10 @@ L0: return r0 [case testNewSetFromIterable] -from typing import Set, List +from typing import Set, List, TypeVar + +T = TypeVar("T") + def f(l: List[T]) -> Set[T]: return set(l) [out] diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 994b30b42347..6f7d79059a6d 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -276,7 +276,10 @@ for k in range(12): [out] [case testForIterable] -from typing import Iterable, Dict, Any, Tuple +from typing import Iterable, Dict, Any, Tuple, TypeVar + +T = TypeVar("T") + def iterate_over_any(a: Any) -> None: for element in a: print(element) @@ -350,13 +353,13 @@ iterate_over_tuple((1, 2, 3)) Traceback (most recent call last): File "driver.py", line 16, in iterate_over_any(5) - File "native.py", line 3, in iterate_over_any + File "native.py", line 6, in iterate_over_any for element in a: TypeError: 'int' object is not iterable Traceback (most recent call last): File "driver.py", line 20, in iterate_over_iterable(broken_generator(5)) - File "native.py", line 7, in iterate_over_iterable + File "native.py", line 10, in iterate_over_iterable for element in iterable: File "driver.py", line 8, in broken_generator raise Exception('Exception Manually Raised') @@ -364,7 +367,7 @@ Exception: Exception Manually Raised Traceback (most recent call last): File "driver.py", line 24, in iterate_and_delete(d) - File "native.py", line 11, in iterate_and_delete + File "native.py", line 14, in iterate_and_delete for key in d: RuntimeError: dictionary changed size during iteration 15 diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test new file mode 100644 index 000000000000..fbafeaf3e65f --- /dev/null +++ b/mypyc/test-data/run-python312.test @@ -0,0 +1,172 @@ +[case testPEP695Basics] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Any, TypeAliasType, cast + +from testutil import assertRaises + +def id[T](x: T) -> T: + return x + +def test_call_generic_function() -> None: + assert id(2) == 2 + assert id('x') == 'x' + +class C[T]: + x: T + + def __init__(self, x: T) -> None: + self.x = x + +class D[T, S]: + x: T + y: S + + def __init__(self, x: T, y: S) -> None: + self.x = x + self.y = y + + def set(self, x: object, y: object) -> None: + self.x = cast(T, x) + self.y = cast(S, y) + +def test_generic_class() -> None: + c = C(5) + assert c.x == 5 + c2 = C[str]('x') + assert c2.x == 'x' + d = D[str, int]('a', 5) + assert d.x == 'a' + assert d.y == 5 + d.set('b', 6) + assert d.x == 'b' + assert d.y == 6 + +def test_generic_class_via_any() -> None: + c_any: Any = C + c = c_any(2) + assert c.x == 2 + c2 = c_any[str]('y') + assert c2.x == 'y' + assert str(c_any[str]) == 'native.C[str]' + + d_any: Any = D + d = d_any(1, 'x') + assert d.x == 1 + assert d.y == 'x' + d2 = d_any[int, str](2, 'y') + assert d2.x == 2 + assert d2.y == 'y' + + with assertRaises(TypeError): + c_any[int, str] + with assertRaises(TypeError): + d_any[int] + +class E[*Ts]: pass + +def test_type_var_tuple() -> None: + e: E[int, str] = E() + e_any: Any = E + assert isinstance(e_any(), E) + assert isinstance(e_any[int](), E) + assert isinstance(e_any[int, str](), E) + +class F[**P]: pass + +def test_param_spec() -> None: + f: F[[int, str]] = F() + f_any: Any = F + assert isinstance(f_any(), F) + assert isinstance(f_any[[int, str]](), F) + +class SubC[S](C[S]): + def __init__(self, x: S) -> None: + super().__init__(x) + +def test_generic_subclass() -> None: + s = SubC(1) + assert s.x == 1 + s2 = SubC[str]('y') + assert s2.x == 'y' + sub_any: Any = SubC + assert sub_any(1).x == 1 + assert sub_any[str]('x').x == 'x' + assert isinstance(s, SubC) + assert isinstance(s, C) + +class SubD[ + T, # Put everything on separate lines + S]( + D[T, + S]): pass + +def test_generic_subclass_two_params() -> None: + s = SubD(3, 'y') + assert s.x == 3 + assert s.y == 'y' + s2 = SubD[str, int]('z', 4) + assert s2.x == 'z' + assert s2.y == 4 + sub_any: Any = SubD + assert sub_any(3, 'y').y == 'y' + assert sub_any[int, str](3, 'y').y == 'y' + assert isinstance(s, SubD) + assert isinstance(s, D) + +class SubE[*Ts](E[*Ts]): pass + +def test_type_var_tuple_subclass() -> None: + sub_any: Any = SubE + assert isinstance(sub_any(), SubE) + assert isinstance(sub_any(), E) + assert isinstance(sub_any[int](), SubE) + assert isinstance(sub_any[int, str](), SubE) + + +class SubF[**P](F[P]): pass + +def test_param_spec_subclass() -> None: + sub_any: Any = SubF + assert isinstance(sub_any(), SubF) + assert isinstance(sub_any(), F) + assert isinstance(sub_any[[int]](), SubF) + assert isinstance(sub_any[[int, str]](), SubF) + +# We test that upper bounds and restricted values can be used, but not that +# they are introspectable + +def bound[T: C](x: T) -> T: + return x + +def test_function_with_upper_bound() -> None: + c = C(1) + assert bound(c) is c + +def restriction[T: (int, str)](x: T) -> T: + return x + +def test_function_with_value_restriction() -> None: + assert restriction(1) == 1 + assert restriction('x') == 'x' + +class Bound[T: C]: + def __init__(self, x: T) -> None: + self.x = x + +def test_class_with_upper_bound() -> None: + c = C(1) + b = Bound(c) + assert b.x is c + b2 = Bound[C](c) + assert b2.x is c + +class Restriction[T: (int, str)]: + def __init__(self, x: T) -> None: + self.x = x + +def test_class_with_value_restriction() -> None: + r = Restriction(1) + assert r.x == 1 + r2 = Restriction[str]('a') + assert r2.x == 'a' +[typing fixtures/typing-full.pyi] diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 467ef8b87a92..37de192a9291 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -71,6 +71,8 @@ if sys.version_info >= (3, 10): files.append("run-match.test") +if sys.version_info >= (3, 12): + files.append("run-python312.test") setup_format = """\ from setuptools import setup @@ -194,6 +196,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> options.preserve_asts = True options.allow_empty_bodies = True options.incremental = self.separate + options.enable_incomplete_feature.append("NewGenericSyntax") # Avoid checking modules/packages named 'unchecked', to provide a way # to test interacting with code we don't have types for. From 8fb99695a01848f47a12a01c63ddd82cd354762d Mon Sep 17 00:00:00 2001 From: Tadeu Manoel Date: Wed, 12 Jun 2024 08:22:08 -0300 Subject: [PATCH 0635/1617] Write stubs for C module with utf-8 encoding (#17367) Avoid encoding errors if functions contain non-ASCII characters. --- mypy/stubgenc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 7e3ef49c6e9a..9acd3f171a41 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -178,7 +178,7 @@ def generate_stub_for_c_module( gen.generate_module() output = gen.output() - with open(target, "w") as file: + with open(target, "w", encoding="utf-8") as file: file.write(output) From 98a22c44c26ff436f1c343ad4727258ffd72e055 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:17:05 -0700 Subject: [PATCH 0636/1617] Fix isinstance with type aliases to PEP 604 unions (#17371) Fixes #12155, fixes #11673, seems pretty commonly reported issue --- mypy/checker.py | 2 ++ mypy/checkexpr.py | 10 ++++++++++ mypy/exprtotype.py | 3 ++- mypy/type_visitor.py | 7 ++++++- mypy/typeanal.py | 2 +- mypy/types.py | 1 + test-data/unit/check-type-aliases.test | 1 + test-data/unit/check-union-or-syntax.test | 19 +++++++++++++++++++ test-data/unit/fine-grained.test | 1 + test-data/unit/fixtures/type.pyi | 7 +++++++ test-data/unit/lib-stub/types.pyi | 3 +++ 11 files changed, 53 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 70db31c9a94f..119aa9f3cea2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7323,6 +7323,8 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: elif isinstance(typ, Instance) and typ.type.fullname == "builtins.type": object_type = Instance(typ.type.mro[-1], []) types.append(TypeRange(object_type, is_upper_bound=True)) + elif isinstance(typ, Instance) and typ.type.fullname == "types.UnionType" and typ.args: + types.append(TypeRange(UnionType(typ.args), is_upper_bound=False)) elif isinstance(typ, AnyType): types.append(TypeRange(typ, is_upper_bound=False)) else: # we didn't see an actual type, but rather a variable with unknown value diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c34952b084f9..861c28e5b54c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -527,6 +527,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> and node and isinstance(node.node, TypeAlias) and not node.node.no_args + and not ( + isinstance(union_target := get_proper_type(node.node.target), UnionType) + and union_target.uses_pep604_syntax + ) ): self.msg.type_arguments_not_allowed(e) if isinstance(typ, RefExpr) and isinstance(typ.node, TypeInfo): @@ -4762,6 +4766,12 @@ class LongName(Generic[T]): ... return TypeType(item, line=item.line, column=item.column) elif isinstance(item, AnyType): return AnyType(TypeOfAny.from_another_any, source_any=item) + elif ( + isinstance(item, UnionType) + and item.uses_pep604_syntax + and self.chk.options.python_version >= (3, 10) + ): + return self.chk.named_generic_type("types.UnionType", item.items) else: if alias_definition: return AnyType(TypeOfAny.special_form) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index 2218a950788c..d9bdf2e2b20b 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -122,7 +122,8 @@ def expr_to_unanalyzed_type( [ expr_to_unanalyzed_type(expr.left, options, allow_new_syntax), expr_to_unanalyzed_type(expr.right, options, allow_new_syntax), - ] + ], + uses_pep604_syntax=True, ) elif isinstance(expr, CallExpr) and isinstance(_parent, ListExpr): c = expr.callee diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index a6ae77832ceb..d0876629fc08 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -266,7 +266,12 @@ def visit_literal_type(self, t: LiteralType) -> Type: return LiteralType(value=t.value, fallback=fallback, line=t.line, column=t.column) def visit_union_type(self, t: UnionType) -> Type: - return UnionType(self.translate_types(t.items), t.line, t.column) + return UnionType( + self.translate_types(t.items), + t.line, + t.column, + uses_pep604_syntax=t.uses_pep604_syntax, + ) def translate_types(self, types: Iterable[Type]) -> list[Type]: return [t.accept(self) for t in types] diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 82c90272d6c2..6651af7dad4f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1271,7 +1271,7 @@ def visit_union_type(self, t: UnionType) -> Type: and not self.options.python_version >= (3, 10) ): self.fail("X | Y syntax for unions requires Python 3.10", t, code=codes.SYNTAX) - return UnionType(self.anal_array(t.items), t.line) + return UnionType(self.anal_array(t.items), t.line, uses_pep604_syntax=t.uses_pep604_syntax) def visit_partial_type(self, t: PartialType) -> Type: assert False, "Internal error: Unexpected partial type" diff --git a/mypy/types.py b/mypy/types.py index cdcb26f435b8..0f8c48c8cb7d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2821,6 +2821,7 @@ def __init__( items: Sequence[Type], line: int = -1, column: int = -1, + *, is_evaluated: bool = True, uses_pep604_syntax: bool = False, ) -> None: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 86bd4422003b..5eea1fb2b53e 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -967,6 +967,7 @@ a: A b: B reveal_type(a) # N: Revealed type is "Union[builtins.list[Any], builtins.int]" reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.list[Any]]" +[builtins fixtures/type.pyi] [case testValidTypeAliasValues] from typing import TypeVar, Generic, List diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index 85e268f348f0..b5fd85cb7ed8 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -207,6 +207,25 @@ foo: ReadableBuffer [file was_mmap.pyi] from was_builtins import * class mmap: ... +[builtins fixtures/type.pyi] + +[case testTypeAliasWithNewUnionIsInstance] +# flags: --python-version 3.10 +SimpleAlias = int | str + +def foo(x: int | str | tuple): + if isinstance(x, SimpleAlias): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(x) # N: Revealed type is "builtins.tuple[Any, ...]" + +ParameterizedAlias = str | list[str] + +# these are false negatives: +isinstance(5, str | list[str]) +isinstance(5, ParameterizedAlias) +[builtins fixtures/type.pyi] + # TODO: Get this test to pass [case testImplicit604TypeAliasWithCyclicImportNotInStub-xfail] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 9c379d8f60da..a87f8ceca15c 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10380,6 +10380,7 @@ from b import C, D A = C | D a: A reveal_type(a) +[builtins fixtures/type.pyi] [file b.py] C = int diff --git a/test-data/unit/fixtures/type.pyi b/test-data/unit/fixtures/type.pyi index 39357a693638..084b7f8388d8 100644 --- a/test-data/unit/fixtures/type.pyi +++ b/test-data/unit/fixtures/type.pyi @@ -1,6 +1,8 @@ # builtins stub used in type-related test cases. from typing import Any, Generic, TypeVar, List, Union +import sys +import types T = TypeVar("T") S = TypeVar("S") @@ -25,3 +27,8 @@ class bool: pass class int: pass class str: pass class ellipsis: pass + +if sys.version_info >= (3, 10): # type: ignore + def isinstance(obj: object, class_or_tuple: type | types.UnionType, /) -> bool: ... +else: + def isinstance(obj: object, class_or_tuple: type, /) -> bool: ... diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index 012fd8503377..e4869dbc3093 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -15,3 +15,6 @@ if sys.version_info >= (3, 10): class NoneType: ... + + class UnionType: + ... From 3d9256b3c508b69426dea9d672f517459f372fa8 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 14 Jun 2024 09:10:37 +0300 Subject: [PATCH 0637/1617] Support `enum.nonmember` for python3.11+ (#17376) This PR adds support for https://docs.python.org/3.11/library/enum.html#enum.nonmember Refs https://github.com/python/mypy/issues/12841 --- mypy/checkmember.py | 11 +++++++++++ mypy/plugins/enums.py | 15 ++++++++++++--- test-data/unit/check-enum.test | 28 ++++++++++++++++++++++++++++ test-data/unit/lib-stub/enum.pyi | 5 +++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index fa847de2e4a0..7525db25d9cd 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1143,6 +1143,17 @@ def analyze_enum_class_attribute_access( if name.startswith("__") and name.replace("_", "") != "": return None + node = itype.type.get(name) + if node and node.type: + proper = get_proper_type(node.type) + # Support `A = nonmember(1)` function call and decorator. + if ( + isinstance(proper, Instance) + and proper.type.fullname == "enum.nonmember" + and proper.args + ): + return proper.args[0] + enum_literal = LiteralType(name, fallback=itype) return itype.copy_modified(last_known_value=enum_literal) diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 83350fe2fe11..167b330f9b09 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -20,7 +20,15 @@ from mypy.semanal_enum import ENUM_BASES from mypy.subtypes import is_equivalent from mypy.typeops import fixup_partial_type, make_simplified_union -from mypy.types import CallableType, Instance, LiteralType, ProperType, Type, get_proper_type +from mypy.types import ( + CallableType, + Instance, + LiteralType, + ProperType, + Type, + get_proper_type, + is_named_instance, +) ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | { f"{prefix}._name_" for prefix in ENUM_BASES @@ -159,7 +167,7 @@ class SomeEnum: stnodes = (info.get(name) for name in info.names) - # Enums _can_ have methods and instance attributes. + # Enums _can_ have methods, instance attributes, and `nonmember`s. # Omit methods and attributes created by assigning to self.* # for our value inference. node_types = ( @@ -170,7 +178,8 @@ class SomeEnum: proper_types = [ _infer_value_type_with_auto_fallback(ctx, t) for t in node_types - if t is None or not isinstance(t, CallableType) + if t is None + or (not isinstance(t, CallableType) and not is_named_instance(t, "enum.nonmember")) ] underlying_type = _first(proper_types) if underlying_type is None: diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index e8e65f464eaf..183901416604 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -2138,3 +2138,31 @@ elif e == MyEnum.B: else: reveal_type(e) # E: Statement is unreachable [builtins fixtures/dict.pyi] + + +[case testEnumNonMemberSupport] +# flags: --python-version 3.11 +# This was added in 3.11 +from enum import Enum, nonmember + +class My(Enum): + a = 1 + b = 2 + c = nonmember(3) + +reveal_type(My.a) # N: Revealed type is "Literal[__main__.My.a]?" +reveal_type(My.b) # N: Revealed type is "Literal[__main__.My.b]?" +reveal_type(My.c) # N: Revealed type is "builtins.int" + +def accepts_my(my: My): + reveal_type(my.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" + +class Other(Enum): + a = 1 + @nonmember + class Support: + b = 2 + +reveal_type(Other.a) # N: Revealed type is "Literal[__main__.Other.a]?" +reveal_type(Other.Support.b) # N: Revealed type is "builtins.int" +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi index 11adfc597955..32dd7c38d251 100644 --- a/test-data/unit/lib-stub/enum.pyi +++ b/test-data/unit/lib-stub/enum.pyi @@ -48,3 +48,8 @@ class auto(IntFlag): # It is python-3.11+ only: class StrEnum(str, Enum): def __new__(cls: Type[_T], value: str | _T) -> _T: ... + +# It is python-3.11+ only: +class nonmember(Generic[_T]): + value: _T + def __init__(self, value: _T) -> None: ... From 5dd062a14b2d2c9b5c73fd266f166f4031e746a1 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 14 Jun 2024 02:35:22 -0700 Subject: [PATCH 0638/1617] Use inline config in the optional error codes docs (#17374) This page already says: > The examples in this section use inline configuration But some of these examples predate support for inline configuration of error codes that was added in #13502 --- docs/source/error_code_list2.rst | 20 ++++++++++---------- docs/source/error_codes.rst | 10 +++++++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 465d1c7a6583..2b765e412913 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -5,8 +5,8 @@ Error codes for optional checks This section documents various errors codes that mypy generates only if you enable certain options. See :ref:`error-codes` for general -documentation about error codes. :ref:`error-code-list` documents -error codes that are enabled by default. +documentation about error codes and their configuration. +:ref:`error-code-list` documents error codes that are enabled by default. .. note:: @@ -241,7 +241,7 @@ mypy generates an error if it thinks that an expression is redundant. .. code-block:: python - # Use "mypy --enable-error-code redundant-expr ..." + # mypy: enable-error-code="redundant-expr" def example(x: int) -> None: # Error: Left operand of "and" is always true [redundant-expr] @@ -268,7 +268,7 @@ example: .. code-block:: python - # Use "mypy --enable-error-code possibly-undefined ..." + # mypy: enable-error-code="possibly-undefined" from typing import Iterable @@ -297,7 +297,7 @@ Using an iterable value in a boolean context has a separate error code .. code-block:: python - # Use "mypy --enable-error-code truthy-bool ..." + # mypy: enable-error-code="truthy-bool" class Foo: pass @@ -347,7 +347,7 @@ Example: .. code-block:: python - # Use "mypy --enable-error-code ignore-without-code ..." + # mypy: enable-error-code="ignore-without-code" class Foo: def __init__(self, name: str) -> None: @@ -378,7 +378,7 @@ Example: .. code-block:: python - # Use "mypy --enable-error-code unused-awaitable ..." + # mypy: enable-error-code="unused-awaitable" import asyncio @@ -462,7 +462,7 @@ Example: .. code-block:: python - # Use "mypy --enable-error-code explicit-override ..." + # mypy: enable-error-code="explicit-override" from typing import override @@ -536,7 +536,7 @@ Now users can actually import ``reveal_type`` to make the runtime code safe. .. code-block:: python - # Use "mypy --enable-error-code unimported-reveal" + # mypy: enable-error-code="unimported-reveal" x = 1 reveal_type(x) # Note: Revealed type is "builtins.int" \ @@ -546,7 +546,7 @@ Correct usage: .. code-block:: python - # Use "mypy --enable-error-code unimported-reveal" + # mypy: enable-error-code="unimported-reveal" from typing import reveal_type # or `typing_extensions` x = 1 diff --git a/docs/source/error_codes.rst b/docs/source/error_codes.rst index 35fad161f8a2..485d70cb59bc 100644 --- a/docs/source/error_codes.rst +++ b/docs/source/error_codes.rst @@ -87,9 +87,13 @@ still keep the other two error codes enabled. The overall logic is following: * Individual config sections *adjust* them per glob/module -* Inline ``# mypy: disable-error-code="..."`` comments can further - *adjust* them for a specific module. - For example: ``# mypy: disable-error-code="truthy-bool, ignore-without-code"`` +* Inline ``# mypy: disable-error-code="..."`` and ``# mypy: enable-error-code="..."`` + comments can further *adjust* them for a specific file. + For example: + +.. code-block:: python + + # mypy: enable-error-code="truthy-bool, ignore-without-code" So one can e.g. enable some code globally, disable it for all tests in the corresponding config section, and then re-enable it with an inline From dac88f346cada58ae022599fffbbd961643b5d5b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 14 Jun 2024 17:35:19 +0300 Subject: [PATCH 0639/1617] Support `enum.member` for python3.11+ (#17382) There are no tests for `@enum.member` used as a decorator, because I can only decorate classes and functions, which are not supported right now: https://mypy-play.net/?mypy=latest&python=3.12&gist=449ee8c12eba9f807cfc7832f1ea2c49 ```python import enum class A(enum.Enum): class x: ... reveal_type(A.x) # Revealed type is "def () -> __main__.A.x" ``` This issue is separate and rather complex, so I would prefer to solve it independently. Refs https://github.com/python/mypy/pull/17376 --------- Co-authored-by: Alex Waygood --- mypy/plugins/default.py | 4 +++- mypy/plugins/enums.py | 18 ++++++++++++++++++ test-data/unit/check-enum.test | 19 +++++++++++++++++++ test-data/unit/lib-stub/enum.pyi | 5 +++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 3ad301a15f6c..5139b9b82289 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -41,7 +41,7 @@ class DefaultPlugin(Plugin): """Type checker plugin that is enabled by default.""" def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: - from mypy.plugins import ctypes, singledispatch + from mypy.plugins import ctypes, enums, singledispatch if fullname == "_ctypes.Array": return ctypes.array_constructor_callback @@ -51,6 +51,8 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] import mypy.plugins.functools return mypy.plugins.functools.partial_new_callback + elif fullname == "enum.member": + return enums.enum_member_callback return None diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 167b330f9b09..816241fa6e9a 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -87,6 +87,8 @@ def _infer_value_type_with_auto_fallback( return None proper_type = get_proper_type(fixup_partial_type(proper_type)) if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"): + if is_named_instance(proper_type, "enum.member") and proper_type.args: + return proper_type.args[0] return proper_type assert isinstance(ctx.type, Instance), "An incorrect ctx.type was passed." info = ctx.type.type @@ -126,6 +128,22 @@ def _implements_new(info: TypeInfo) -> bool: return type_with_new.fullname not in ("enum.Enum", "enum.IntEnum", "enum.StrEnum") +def enum_member_callback(ctx: mypy.plugin.FunctionContext) -> Type: + """By default `member(1)` will be infered as `member[int]`, + we want to improve the inference to be `Literal[1]` here.""" + if ctx.arg_types or ctx.arg_types[0]: + arg = get_proper_type(ctx.arg_types[0][0]) + proper_return = get_proper_type(ctx.default_return_type) + if ( + isinstance(arg, Instance) + and arg.last_known_value + and isinstance(proper_return, Instance) + and len(proper_return.args) == 1 + ): + return proper_return.copy_modified(args=[arg]) + return ctx.default_return_type + + def enum_value_callback(ctx: mypy.plugin.AttributeContext) -> Type: """This plugin refines the 'value' attribute in enums to refer to the original underlying value. For example, suppose we have the diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 183901416604..d53935085325 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -2166,3 +2166,22 @@ class Other(Enum): reveal_type(Other.a) # N: Revealed type is "Literal[__main__.Other.a]?" reveal_type(Other.Support.b) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] + + +[case testEnumMemberSupport] +# flags: --python-version 3.11 +# This was added in 3.11 +from enum import Enum, member + +class A(Enum): + x = member(1) + y = 2 + +reveal_type(A.x) # N: Revealed type is "Literal[__main__.A.x]?" +reveal_type(A.x.value) # N: Revealed type is "Literal[1]?" +reveal_type(A.y) # N: Revealed type is "Literal[__main__.A.y]?" +reveal_type(A.y.value) # N: Revealed type is "Literal[2]?" + +def some_a(a: A): + reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi index 32dd7c38d251..0e0b8e025d9f 100644 --- a/test-data/unit/lib-stub/enum.pyi +++ b/test-data/unit/lib-stub/enum.pyi @@ -53,3 +53,8 @@ class StrEnum(str, Enum): class nonmember(Generic[_T]): value: _T def __init__(self, value: _T) -> None: ... + +# It is python-3.11+ only: +class member(Generic[_T]): + value: _T + def __init__(self, value: _T) -> None: ... From 740d39ecc31c64675439e6796dcf46e6a9110896 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 14 Jun 2024 16:43:56 +0100 Subject: [PATCH 0640/1617] [PEP 695] Add more error checks and tests for error conditions (#17339) Detect invalid number of constrained types. At least two are required, according do PEP 695. Add tests for other simple errors. Work on #15238. --- mypy/errorcodes.py | 2 +- mypy/fastparse.py | 12 +++++++++-- mypy/message_registry.py | 3 +++ mypy/semanal.py | 9 +++++++-- test-data/unit/check-python312.test | 31 +++++++++++++++++++++++++++++ test-data/unit/semanal-errors.test | 2 +- 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 7de796a70c8d..6e8763264ddd 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -271,7 +271,7 @@ def __hash__(self) -> int: del error_codes[FILE.code] # This is a catch-all for remaining uncategorized errors. -MISC: Final = ErrorCode("misc", "Miscellaneous other checks", "General") +MISC: Final[ErrorCode] = ErrorCode("misc", "Miscellaneous other checks", "General") OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( "overload-overlap", diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 49f0a938b750..70afe9010583 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1185,8 +1185,16 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: explicit_type_params.append(TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [])) else: if isinstance(p.bound, ast3.Tuple): - conv = TypeConverter(self.errors, line=p.lineno) - values = [conv.visit(t) for t in p.bound.elts] + if len(p.bound.elts) < 2: + self.fail( + message_registry.TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES, + p.lineno, + p.col_offset, + blocker=False, + ) + else: + conv = TypeConverter(self.errors, line=p.lineno) + values = [conv.visit(t) for t in p.bound.elts] elif p.bound is not None: bound = TypeConverter(self.errors, line=p.lineno).visit(p.bound) explicit_type_params.append(TypeParam(p.name, TYPE_VAR_KIND, bound, values)) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 52bd9a1ce00c..befacc9e6182 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -334,3 +334,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: NARROWED_TYPE_NOT_SUBTYPE: Final = ErrorMessage( "Narrowed type {} is not a subtype of input type {}", codes.NARROWED_TYPE_NOT_SUBTYPE ) +TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES: Final = ErrorMessage( + "Type variable must have at least two constrained types", codes.MISC +) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8da5c68d562d..d2f02d4835e2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -59,6 +59,7 @@ from mypy.errorcodes import PROPERTY_DECORATOR, ErrorCode from mypy.errors import Errors, report_internal_error from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type +from mypy.message_registry import ErrorMessage from mypy.messages import ( SUGGESTED_TEST_FIXTURES, TYPES_FOR_UNIMPORTED_HINTS, @@ -4618,7 +4619,7 @@ def process_typevar_parameters( self.fail("TypeVar cannot be both covariant and contravariant", context) return None elif num_values == 1: - self.fail("TypeVar cannot have only a single constraint", context) + self.fail(message_registry.TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES, context) return None elif covariant: variance = COVARIANT @@ -7034,7 +7035,7 @@ def in_checked_function(self) -> bool: def fail( self, - msg: str, + msg: str | ErrorMessage, ctx: Context, serious: bool = False, *, @@ -7045,6 +7046,10 @@ def fail( return # In case it's a bug and we don't really have context assert ctx is not None, msg + if isinstance(msg, ErrorMessage): + if code is None: + code = msg.code + msg = msg.value self.errors.report(ctx.line, ctx.column, msg, blocker=blocker, code=code) def note(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index a1c819667087..06c5bada1e92 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1494,3 +1494,34 @@ reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" # flags: --enable-incomplete-feature=NewGenericSyntax def f[T](x: foobar, y: T) -> T: ... # E: Name "foobar" is not defined reveal_type(f) # N: Revealed type is "def [T] (x: Any, y: T`-1) -> T`-1" + +[case testPEP695WrongNumberOfConstrainedTypes] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A[T: ()] = list[T] # E: Type variable must have at least two constrained types +a: A[int] +reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +type B[T: (int,)] = list[T] # E: Type variable must have at least two constrained types +b: B[str] +reveal_type(b) # N: Revealed type is "builtins.list[builtins.str]" + +[case testPEP695UsingTypeVariableInOwnBoundOrConstraint] +# flags: --enable-incomplete-feature=NewGenericSyntax +type A[T: list[T]] = str # E: Name "T" is not defined +type B[S: (list[S], str)] = str # E: Name "S" is not defined +type C[T, S: list[T]] = str # E: Name "T" is not defined + +def f[T: T](x: T) -> T: ... # E: Name "T" is not defined +class D[T: T]: # E: Name "T" is not defined + pass + +[case testPEP695InvalidType] +# flags: --enable-incomplete-feature=NewGenericSyntax +def f[T: 1](x: T) -> T: ... # E: Invalid type: try using Literal[1] instead? +class C[T: (int, (1 + 2))]: pass # E: Invalid type comment or annotation +type A = list[1] # E: Invalid type: try using Literal[1] instead? +type B = (1 + 2) # E: Invalid type alias: expression is not a valid type +a: A +reveal_type(a) # N: Revealed type is "builtins.list[Any]" +b: B +reveal_type(b) # N: Revealed type is "Any" diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 269536f868a4..33c8f9b80aa0 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1046,7 +1046,7 @@ T = TypeVar(b'T') # E: TypeVar() expects a string literal as first argument d = TypeVar('D') # E: String argument 1 "D" to TypeVar(...) does not match variable name "d" e = TypeVar('e', int, str, x=1) # E: Unexpected argument to "TypeVar()": "x" f = TypeVar('f', (int, str), int) # E: Type expected -g = TypeVar('g', int) # E: TypeVar cannot have only a single constraint +g = TypeVar('g', int) # E: Type variable must have at least two constrained types h = TypeVar('h', x=(int, str)) # E: Unexpected argument to "TypeVar()": "x" i = TypeVar('i', bound=1) # E: TypeVar "bound" must be a type j = TypeVar('j', covariant=None) # E: TypeVar "covariant" may only be a literal bool From 693e1d8d74ba331c2bd36ff4774c9a4eefe03ab6 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 15 Jun 2024 15:28:25 +0100 Subject: [PATCH 0641/1617] Add a test case for no overload overlap with self-types (#17388) Fixes https://github.com/python/mypy/issues/14641 I don't think we have a test case for this, and although it is a bit in a grey area (because of `x: Any = None`), I think we should allow this. --- test-data/unit/check-overloading.test | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 7bca5cc7b508..bcb775ba5dac 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -4542,6 +4542,23 @@ reveal_type(Child().foo("...")) # N: Revealed type is "builtins.st reveal_type(Child().foo(x)) # N: Revealed type is "Union[__main__.Child, builtins.str]" reveal_type(Child().foo(3).child_only()) # N: Revealed type is "builtins.int" +[case testOverloadAndSelfTypesGenericNoOverlap] +from typing import Generic, TypeVar, Any, overload, Self, Union + +T = TypeVar("T", bound=Any) +class C(Generic[T]): + @overload + def get(self, obj: None) -> Self: ... + @overload + def get(self, obj: Any) -> T: ... + def get(self, obj: Union[Any, None]) -> Union[T, Self]: + return self + +class D(C[int]): ... +d: D +reveal_type(d.get(None)) # N: Revealed type is "__main__.D" +reveal_type(d.get("whatever")) # N: Revealed type is "builtins.int" + [case testOverloadAndClassTypes] from typing import overload, Union, TypeVar, Type From b81b9e0a230f135c9b8abdafba674922e37e1a88 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 16 Jun 2024 12:25:18 +0100 Subject: [PATCH 0642/1617] [mypyc] Sync pythoncapi_compat.h (#17390) This helps with Python 3.13 support. --- mypyc/lib-rt/pythoncapi_compat.h | 1071 +++++++++++++++++++++++++++--- 1 file changed, 967 insertions(+), 104 deletions(-) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index f22e92f7358f..1b59f93de7ec 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -19,34 +19,25 @@ extern "C" { #endif #include -#include "frameobject.h" // PyFrameObject, PyFrame_GetBack() - -// Compatibility with Visual Studio 2013 and older which don't support -// the inline keyword in C (only in C++): use __inline instead. -#if (defined(_MSC_VER) && _MSC_VER < 1900 \ - && !defined(__cplusplus) && !defined(inline)) -# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static __inline TYPE -#else -# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static inline TYPE +// Python 3.11.0b4 added PyFrame_Back() to Python.h +#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) +# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() #endif -// C++ compatibility: _Py_CAST() and _Py_NULL #ifndef _Py_CAST -# ifdef __cplusplus -# define _Py_CAST(type, expr) \ - const_cast(reinterpret_cast(expr)) -# else -# define _Py_CAST(type, expr) ((type)(expr)) -# endif +# define _Py_CAST(type, expr) ((type)(expr)) #endif -#ifndef _Py_NULL -# ifdef __cplusplus -# define _Py_NULL nullptr -# else -# define _Py_NULL NULL -# endif + +// Static inline functions should use _Py_NULL rather than using directly NULL +// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, +// _Py_NULL is defined as nullptr. +#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +#else +# define _Py_NULL NULL #endif // Cast argument to PyObject* type. @@ -57,8 +48,7 @@ extern "C" { // bpo-42262 added Py_NewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -_Py_NewRef(PyObject *obj) +static inline PyObject* _Py_NewRef(PyObject *obj) { Py_INCREF(obj); return obj; @@ -69,8 +59,7 @@ _Py_NewRef(PyObject *obj) // bpo-42262 added Py_XNewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -_Py_XNewRef(PyObject *obj) +static inline PyObject* _Py_XNewRef(PyObject *obj) { Py_XINCREF(obj); return obj; @@ -81,8 +70,7 @@ _Py_XNewRef(PyObject *obj) // bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) -PYCAPI_COMPAT_STATIC_INLINE(void) -_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { ob->ob_refcnt = refcnt; } @@ -93,18 +81,20 @@ _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) // Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. // It is excluded from the limited C API. #if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) -#define Py_SETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_DECREF(_py_tmp); \ +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ } while (0) -#define Py_XSETREF(op, op2) \ - do { \ - PyObject *_py_tmp = _PyObject_CAST(op); \ - (op) = (op2); \ - Py_XDECREF(_py_tmp); \ +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ } while (0) #endif @@ -117,18 +107,17 @@ _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) #if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) # define Py_IsNone(x) Py_Is(x, Py_None) #endif -#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsTrue) +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) # define Py_IsTrue(x) Py_Is(x, Py_True) #endif -#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsFalse) +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) # define Py_IsFalse(x) Py_Is(x, Py_False) #endif // bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) -PYCAPI_COMPAT_STATIC_INLINE(void) -_Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; } @@ -138,8 +127,7 @@ _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) // bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) -PYCAPI_COMPAT_STATIC_INLINE(void) -_Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { ob->ob_size = size; } @@ -148,9 +136,8 @@ _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) // bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 -#if PY_VERSION_HEX < 0x030900B1 -PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*) -PyFrame_GetCode(PyFrameObject *frame) +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) { assert(frame != _Py_NULL); assert(frame->f_code != _Py_NULL); @@ -158,8 +145,7 @@ PyFrame_GetCode(PyFrameObject *frame) } #endif -PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*) -_PyFrame_GetCodeBorrow(PyFrameObject *frame) +static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) { PyCodeObject *code = PyFrame_GetCode(frame); Py_DECREF(code); @@ -169,8 +155,7 @@ _PyFrame_GetCodeBorrow(PyFrameObject *frame) // bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) -PyFrame_GetBack(PyFrameObject *frame) +static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) { assert(frame != _Py_NULL); return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); @@ -178,8 +163,7 @@ PyFrame_GetBack(PyFrameObject *frame) #endif #if !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) -_PyFrame_GetBackBorrow(PyFrameObject *frame) +static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) { PyFrameObject *back = PyFrame_GetBack(frame); Py_XDECREF(back); @@ -190,8 +174,7 @@ _PyFrame_GetBackBorrow(PyFrameObject *frame) // bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyFrame_GetLocals(PyFrameObject *frame) +static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) { #if PY_VERSION_HEX >= 0x030400B1 if (PyFrame_FastToLocalsWithError(frame) < 0) { @@ -207,8 +190,7 @@ PyFrame_GetLocals(PyFrameObject *frame) // bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyFrame_GetGlobals(PyFrameObject *frame) +static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) { return Py_NewRef(frame->f_globals); } @@ -217,8 +199,7 @@ PyFrame_GetGlobals(PyFrameObject *frame) // bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyFrame_GetBuiltins(PyFrameObject *frame) +static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) { return Py_NewRef(frame->f_builtins); } @@ -227,8 +208,7 @@ PyFrame_GetBuiltins(PyFrameObject *frame) // bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(int) -PyFrame_GetLasti(PyFrameObject *frame) +static inline int PyFrame_GetLasti(PyFrameObject *frame) { #if PY_VERSION_HEX >= 0x030A00A7 // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, @@ -245,9 +225,63 @@ PyFrame_GetLasti(PyFrameObject *frame) #endif +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); +#else + value = _PyDict_GetItemWithError(locals, name); +#endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); +#else + PyErr_SetString(PyExc_NameError, "variable does not exist"); +#endif + return NULL; + } + return Py_NewRef(value); +} +#endif + + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* +PyFrame_GetVarString(PyFrameObject *frame, const char *name) +{ + PyObject *name_obj, *value; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(name); +#else + name_obj = PyString_FromString(name); +#endif + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + + // bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 -PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState *) +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState * PyThreadState_GetInterpreter(PyThreadState *tstate) { assert(tstate != _Py_NULL); @@ -258,8 +292,7 @@ PyThreadState_GetInterpreter(PyThreadState *tstate) // bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) -PyThreadState_GetFrame(PyThreadState *tstate) +static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != _Py_NULL); return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); @@ -267,7 +300,7 @@ PyThreadState_GetFrame(PyThreadState *tstate) #endif #if !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) +static inline PyFrameObject* _PyThreadState_GetFrameBorrow(PyThreadState *tstate) { PyFrameObject *frame = PyThreadState_GetFrame(tstate); @@ -278,9 +311,8 @@ _PyThreadState_GetFrameBorrow(PyThreadState *tstate) // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 -PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState*) -PyInterpreterState_Get(void) +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState* PyInterpreterState_Get(void) { PyThreadState *tstate; PyInterpreterState *interp; @@ -300,8 +332,7 @@ PyInterpreterState_Get(void) // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 #if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(uint64_t) -PyThreadState_GetID(PyThreadState *tstate) +static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) { assert(tstate != _Py_NULL); return tstate->id; @@ -310,8 +341,7 @@ PyThreadState_GetID(PyThreadState *tstate) // bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(void) -PyThreadState_EnterTracing(PyThreadState *tstate) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) { tstate->tracing++; #if PY_VERSION_HEX >= 0x030A00A1 @@ -324,8 +354,7 @@ PyThreadState_EnterTracing(PyThreadState *tstate) // bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(void) -PyThreadState_LeaveTracing(PyThreadState *tstate) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) { int use_tracing = (tstate->c_tracefunc != _Py_NULL || tstate->c_profilefunc != _Py_NULL); @@ -340,9 +369,9 @@ PyThreadState_LeaveTracing(PyThreadState *tstate) // bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 -#if PY_VERSION_HEX < 0x030900A1 -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyObject_CallNoArgs(PyObject *func) +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +static inline PyObject* PyObject_CallNoArgs(PyObject *func) { return PyObject_CallFunctionObjArgs(func, NULL); } @@ -351,9 +380,9 @@ PyObject_CallNoArgs(PyObject *func) // bpo-39245 made PyObject_CallOneArg() public (previously called // _PyObject_CallOneArg) in Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyObject_CallOneArg(PyObject *func, PyObject *arg) +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) { return PyObject_CallFunctionObjArgs(func, arg, NULL); } @@ -362,10 +391,19 @@ PyObject_CallOneArg(PyObject *func, PyObject *arg) // bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 -PYCAPI_COMPAT_STATIC_INLINE(int) +static inline int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) { int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + Py_XINCREF(value); res = PyModule_AddObject(module, name, value); if (res < 0) { @@ -378,8 +416,7 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) // bpo-40024 added PyModule_AddType() to Python 3.9.0a5 #if PY_VERSION_HEX < 0x030900A5 -PYCAPI_COMPAT_STATIC_INLINE(int) -PyModule_AddType(PyObject *module, PyTypeObject *type) +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) { const char *name, *dot; @@ -403,8 +440,7 @@ PyModule_AddType(PyObject *module, PyTypeObject *type) // bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. // bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. #if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(int) -PyObject_GC_IsTracked(PyObject* obj) +static inline int PyObject_GC_IsTracked(PyObject* obj) { return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); } @@ -413,8 +449,7 @@ PyObject_GC_IsTracked(PyObject* obj) // bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. // bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. #if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(int) -PyObject_GC_IsFinalized(PyObject *obj) +static inline int PyObject_GC_IsFinalized(PyObject *obj) { PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); @@ -424,8 +459,7 @@ PyObject_GC_IsFinalized(PyObject *obj) // bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) -PYCAPI_COMPAT_STATIC_INLINE(int) -_Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { +static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { return Py_TYPE(ob) == type; } #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) @@ -437,12 +471,10 @@ _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { // Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal // C API: Python 3.11a2-3.11a6 versions are not supported. #if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(int) -PyFloat_Pack2(double x, char *p, int le) +static inline int PyFloat_Pack2(double x, char *p, int le) { return _PyFloat_Pack2(x, (unsigned char*)p, le); } -PYCAPI_COMPAT_STATIC_INLINE(double) -PyFloat_Unpack2(const char *p, int le) +static inline double PyFloat_Unpack2(const char *p, int le) { return _PyFloat_Unpack2((const unsigned char *)p, le); } #endif @@ -453,34 +485,54 @@ PyFloat_Unpack2(const char *p, int le) // and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions // are not supported. #if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(int) -PyFloat_Pack4(double x, char *p, int le) +static inline int PyFloat_Pack4(double x, char *p, int le) { return _PyFloat_Pack4(x, (unsigned char*)p, le); } -PYCAPI_COMPAT_STATIC_INLINE(int) -PyFloat_Pack8(double x, char *p, int le) +static inline int PyFloat_Pack8(double x, char *p, int le) { return _PyFloat_Pack8(x, (unsigned char*)p, le); } -PYCAPI_COMPAT_STATIC_INLINE(double) -PyFloat_Unpack4(const char *p, int le) +static inline double PyFloat_Unpack4(const char *p, int le) { return _PyFloat_Unpack4((const unsigned char *)p, le); } -PYCAPI_COMPAT_STATIC_INLINE(double) -PyFloat_Unpack8(const char *p, int le) +static inline double PyFloat_Unpack8(const char *p, int le) { return _PyFloat_Unpack8((const unsigned char *)p, le); } #endif // gh-92154 added PyCode_GetCode() to Python 3.11.0b1 #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) -PYCAPI_COMPAT_STATIC_INLINE(PyObject*) -PyCode_GetCode(PyCodeObject *code) +static inline PyObject* PyCode_GetCode(PyCodeObject *code) { return Py_NewRef(code->co_code); } #endif +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + + // Py_UNUSED() was added to Python 3.4.0b2. #if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) # if defined(__GNUC__) || defined(__clang__) @@ -491,6 +543,817 @@ PyCode_GetCode(PyCodeObject *code) #endif +// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A0 +static inline PyObject* PyImport_AddModuleRef(const char *name) +{ + return Py_XNewRef(PyImport_AddModule(name)); +} +#endif + + +// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D0000 +static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +{ + PyObject *obj; + if (ref != NULL && !PyWeakref_Check(ref)) { + *pobj = NULL; + PyErr_SetString(PyExc_TypeError, "expected a weakref"); + return -1; + } + obj = PyWeakref_GetObject(ref); + if (obj == NULL) { + // SystemError if ref is NULL + *pobj = NULL; + return -1; + } + if (obj == Py_None) { + *pobj = NULL; + return 0; + } + *pobj = Py_NewRef(obj); + return (*pobj != NULL); +} +#endif + + +// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 +#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET +# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#endif + +// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 +#if PY_VERSION_HEX < 0x030800B1 +static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} +#endif + + +// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 +static inline PyObject* +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ +#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 + return _PyObject_Vectorcall(callable, args, nargsf, kwnames); +#else + PyObject *posargs = NULL, *kwargs = NULL; + PyObject *res; + Py_ssize_t nposargs, nkwargs, i; + + if (nargsf != 0 && args == NULL) { + PyErr_BadInternalCall(); + goto error; + } + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + goto error; + } + + nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); + if (kwnames) { + nkwargs = PyTuple_GET_SIZE(kwnames); + } + else { + nkwargs = 0; + } + + posargs = PyTuple_New(nposargs); + if (posargs == NULL) { + goto error; + } + if (nposargs) { + for (i=0; i < nposargs; i++) { + PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); + args++; + } + } + + if (nkwargs) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + goto error; + } + + for (i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = *args; + args++; + if (PyDict_SetItem(kwargs, key, value) < 0) { + goto error; + } + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(callable, posargs, kwargs); + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return res; + +error: + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return NULL; +#endif +} +#endif + + +// gh-106521 added PyObject_GetOptionalAttr() and +// PyObject_GetOptionalAttrString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) +{ + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 +#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + return _PyObject_LookupAttr(obj, attr_name, result); +#else + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; +#endif +} + +static inline int +PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +{ + PyObject *name_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(attr_name); +#else + name_obj = PyString_FromString(attr_name); +#endif + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} +#endif + + +// gh-106307 added PyObject_GetOptionalAttr() and +// PyMapping_GetOptionalItemString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) +{ + *result = PyObject_GetItem(obj, key); + if (*result) { + return 1; + } + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; +} + +static inline int +PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) +{ + PyObject *key_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + key_obj = PyUnicode_FromString(key); +#else + key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyMapping_GetOptionalItem(obj, key_obj, result); + Py_DECREF(key_obj); + return rc; +} +#endif + +// gh-108511 added PyMapping_HasKeyWithError() and +// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItem(obj, key, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItemString(obj, key, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-108511 added PyObject_HasAttrWithError() and +// PyObject_HasAttrStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttr(obj, attr, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttrString(obj, attr, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) +{ + int res; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); +#else + PyObject *key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + + +// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// bpo-1856 added _Py_Finalizing to Python 3.2.1b1. +// _Py_IsFinalizing() was added to PyPy 7.3.0. +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ + && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int Py_IsFinalizing(void) +{ +#if PY_VERSION_HEX >= 0x030700A1 + // _Py_IsFinalizing() was added to Python 3.7.0a1. + return _Py_IsFinalizing(); +#else + return (_Py_Finalizing != NULL); +#endif +} +#endif + + +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ +#ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; +#else + return _PyLong_AsInt(obj); +#endif +} +#endif + + +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return -1; + } + Py_VISIT(*dict); + return 0; +} + +static inline void +PyObject_ClearManagedDict(PyObject *obj) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return; + } + Py_CLEAR(*dict); +} +#endif + +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// Python 3.5.2 added _PyThreadState_UncheckedGet(). +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +static inline PyThreadState* +PyThreadState_GetUnchecked(void) +{ + return _PyThreadState_UncheckedGet(); +} +#endif + +// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) +{ + Py_ssize_t len; + const void *utf8; + PyObject *exc_type, *exc_value, *exc_tb; + int res; + + // API cannot report errors so save/restore the exception + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() +#if PY_VERSION_HEX >= 0x030300A1 + if (PyUnicode_IS_ASCII(unicode)) { + utf8 = PyUnicode_DATA(unicode); + len = PyUnicode_GET_LENGTH(unicode); + } + else { + utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); + if (utf8 == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + } + + if (len != str_len) { + res = 0; + goto done; + } + res = (memcmp(utf8, str, (size_t)len) == 0); +#else + PyObject *bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + +#if PY_VERSION_HEX >= 0x03000000 + len = PyBytes_GET_SIZE(bytes); + utf8 = PyBytes_AS_STRING(bytes); +#else + len = PyString_GET_SIZE(bytes); + utf8 = PyString_AS_STRING(bytes); +#endif + if (len != str_len) { + Py_DECREF(bytes); + res = 0; + goto done; + } + + res = (memcmp(utf8, str, (size_t)len) == 0); + Py_DECREF(bytes); +#endif + +done: + PyErr_Restore(exc_type, exc_value, exc_tb); + return res; +} + +static inline int +PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) +{ + return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); +} +#endif + + +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyList_Extend(PyObject *list, PyObject *iterable) +{ + return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); +} + +static inline int +PyList_Clear(PyObject *list) +{ + return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); +} +#endif + +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) +{ + PyObject *value; + + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + if (result) { + *result = NULL; + } + return -1; + } + + // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. + // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13.0a1 removed _PyDict_Pop(). +#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 + value = PyObject_CallMethod(dict, "pop", "O", key); +#elif PY_VERSION_HEX < 0x030600b3 + value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); +#else + value = _PyDict_Pop(dict, key, NULL); +#endif + if (value == NULL) { + if (result) { + *result = NULL; + } + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; + } + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; +} + +static inline int +PyDict_PopString(PyObject *dict, const char *key, PyObject **result) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + if (result != NULL) { + *result = NULL; + } + return -1; + } + + int res = PyDict_Pop(dict, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +#if PY_VERSION_HEX < 0x030200A4 +// Python 3.2.0a4 added Py_hash_t type +typedef Py_ssize_t Py_hash_t; +#endif + + +// gh-111545 added Py_HashPointer() to Python 3.13.0a3 +#if PY_VERSION_HEX < 0x030D00A3 +static inline Py_hash_t Py_HashPointer(const void *ptr) +{ +#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) + return _Py_HashPointer(ptr); +#else + return _Py_HashPointer(_Py_CAST(void*, ptr)); +#endif +} +#endif + + +// Python 3.13a4 added a PyTime API. +// Use the private API added to Python 3.5. +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +typedef _PyTime_t PyTime_t; +#define PyTime_MIN _PyTime_MIN +#define PyTime_MAX _PyTime_MAX + +static inline double PyTime_AsSecondsDouble(PyTime_t t) +{ return _PyTime_AsSecondsDouble(t); } + +static inline int PyTime_Monotonic(PyTime_t *result) +{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); } + +static inline int PyTime_Time(PyTime_t *result) +{ return _PyTime_GetSystemClockWithInfo(result, NULL); } + +static inline int PyTime_PerfCounter(PyTime_t *result) +{ +#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) + return _PyTime_GetPerfCounterWithInfo(result, NULL); +#elif PY_VERSION_HEX >= 0x03070000 + // Call time.perf_counter_ns() and convert Python int object to PyTime_t. + // Cache time.perf_counter_ns() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter_ns"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + long long value = PyLong_AsLongLong(res); + Py_DECREF(res); + + if (value == -1 && PyErr_Occurred()) { + return -1; + } + + Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); + *result = (PyTime_t)value; + return 0; +#else + // Call time.perf_counter() and convert C double to PyTime_t. + // Cache time.perf_counter() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + double d = PyFloat_AsDouble(res); + Py_DECREF(res); + + if (d == -1.0 && PyErr_Occurred()) { + return -1; + } + + // Avoid floor() to avoid having to link to libm + *result = (PyTime_t)(d * 1e9); + return 0; +#endif +} + +#endif + +// gh-111389 added hash constants to Python 3.13.0a5. These constants were +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +#if (!defined(PyHASH_BITS) \ + && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ + && PYPY_VERSION_NUM >= 0x07090000))) +# define PyHASH_BITS _PyHASH_BITS +# define PyHASH_MODULUS _PyHASH_MODULUS +# define PyHASH_INF _PyHASH_INF +# define PyHASH_IMAG _PyHASH_IMAG +#endif + + +// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() +// to Python 3.13.0a6 +#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) + +#define Py_CONSTANT_NONE 0 +#define Py_CONSTANT_FALSE 1 +#define Py_CONSTANT_TRUE 2 +#define Py_CONSTANT_ELLIPSIS 3 +#define Py_CONSTANT_NOT_IMPLEMENTED 4 +#define Py_CONSTANT_ZERO 5 +#define Py_CONSTANT_ONE 6 +#define Py_CONSTANT_EMPTY_STR 7 +#define Py_CONSTANT_EMPTY_BYTES 8 +#define Py_CONSTANT_EMPTY_TUPLE 9 + +static inline PyObject* Py_GetConstant(unsigned int constant_id) +{ + static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + + if (constants[Py_CONSTANT_NONE] == NULL) { + constants[Py_CONSTANT_NONE] = Py_None; + constants[Py_CONSTANT_FALSE] = Py_False; + constants[Py_CONSTANT_TRUE] = Py_True; + constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; + constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; + + constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); + if (constants[Py_CONSTANT_ZERO] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); + if (constants[Py_CONSTANT_ONE] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); + if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { + goto fatal_error; + } + // goto dance to avoid compiler warnings about Py_FatalError() + goto init_done; + +fatal_error: + // This case should never happen + Py_FatalError("Py_GetConstant() failed to get constants"); + } + +init_done: + if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { + return Py_NewRef(constants[constant_id]); + } + else { + PyErr_BadInternalCall(); + return NULL; + } +} + +static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) +{ + PyObject *obj = Py_GetConstant(constant_id); + Py_XDECREF(obj); + return obj; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline PyObject * +PyList_GetItemRef(PyObject *op, Py_ssize_t index) +{ + PyObject *item = PyList_GetItem(op, index); + Py_XINCREF(item); + return item; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline int +PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, + PyObject **result) +{ + PyObject *value; + if (PyDict_GetItemRef(d, key, &value) < 0) { + // get error + if (result) { + *result = NULL; + } + return -1; + } + if (value != NULL) { + // present + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; + } + + // missing: set the item + if (PyDict_SetItem(d, key, default_value) < 0) { + // set error + if (result) { + *result = NULL; + } + return -1; + } + if (result) { + *result = Py_NewRef(default_value); + } + return 0; +} +#endif + + +// gh-116560 added PyLong_GetSign() to Python 3.14a4 +#if PY_VERSION_HEX < 0x030E00A1 +static inline int PyLong_GetSign(PyObject *obj, int *sign) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + + *sign = _PyLong_Sign(obj); + return 0; +} +#endif + + #ifdef __cplusplus } #endif From 31faa43406b9b2a4edaacc55ad637a695f0094f7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 17 Jun 2024 10:55:22 +0100 Subject: [PATCH 0643/1617] [mypyc] Inline fast paths of integer unboxing operations (#17266) This applies to `int` and native integer types. This speeds up this micro-benchmark by up to 80% (it spends most of the time unboxing integers): ``` # a is list[int]/list[i64]/... for i in a: if i == 789: n += 1 ``` The impact to compile time when self-compiling is below the noise floor. The generated binary is about 0.1% larger. Since integer unboxing can be performance-critical, this seems like a decent win. Closes mypyc/mypyc#987. Work on mypyc/mypyc#757. --- mypyc/common.py | 1 + mypyc/lib-rt/CPy.h | 147 +++++++++++++++++++++++++++++++++-- mypyc/lib-rt/int_ops.c | 133 ++----------------------------- mypyc/lib-rt/pythonsupport.c | 106 +++++++++++++++++++++++++ mypyc/lib-rt/pythonsupport.h | 73 ++++------------- mypyc/lib-rt/setup.py | 1 + 6 files changed, 269 insertions(+), 192 deletions(-) create mode 100644 mypyc/lib-rt/pythonsupport.c diff --git a/mypyc/common.py b/mypyc/common.py index d7610fe15c41..31567c689c34 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -79,6 +79,7 @@ "exc_ops.c", "misc_ops.c", "generic_ops.c", + "pythonsupport.c", ] diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 9e85647226fe..8aa5a77c180c 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -120,9 +120,6 @@ static inline size_t CPy_FindAttrOffset(PyTypeObject *trait, CPyVTableItem *vtab CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value); CPyTagged CPyTagged_FromVoidPtr(void *ptr); CPyTagged CPyTagged_FromInt64(int64_t value); -CPyTagged CPyTagged_FromObject(PyObject *object); -CPyTagged CPyTagged_StealFromObject(PyObject *object); -CPyTagged CPyTagged_BorrowFromObject(PyObject *object); PyObject *CPyTagged_AsObject(CPyTagged x); PyObject *CPyTagged_StealAsObject(CPyTagged x); Py_ssize_t CPyTagged_AsSsize_t(CPyTagged x); @@ -148,18 +145,18 @@ CPyTagged CPyTagged_FromFloat(double f); PyObject *CPyLong_FromStrWithBase(PyObject *o, CPyTagged base); PyObject *CPyLong_FromStr(PyObject *o); PyObject *CPyBool_Str(bool b); -int64_t CPyLong_AsInt64(PyObject *o); +int64_t CPyLong_AsInt64_(PyObject *o); int64_t CPyInt64_Divide(int64_t x, int64_t y); int64_t CPyInt64_Remainder(int64_t x, int64_t y); -int32_t CPyLong_AsInt32(PyObject *o); +int32_t CPyLong_AsInt32_(PyObject *o); int32_t CPyInt32_Divide(int32_t x, int32_t y); int32_t CPyInt32_Remainder(int32_t x, int32_t y); void CPyInt32_Overflow(void); -int16_t CPyLong_AsInt16(PyObject *o); +int16_t CPyLong_AsInt16_(PyObject *o); int16_t CPyInt16_Divide(int16_t x, int16_t y); int16_t CPyInt16_Remainder(int16_t x, int16_t y); void CPyInt16_Overflow(void); -uint8_t CPyLong_AsUInt8(PyObject *o); +uint8_t CPyLong_AsUInt8_(PyObject *o); void CPyUInt8_Overflow(void); double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y); @@ -199,6 +196,41 @@ static inline PyObject *CPyTagged_LongAsObject(CPyTagged x) { return (PyObject *)(x & ~CPY_INT_TAG); } +static inline CPyTagged CPyTagged_FromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (unlikely(overflow != 0)) { + Py_INCREF(object); + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + return value << 1; + } +} + +static inline CPyTagged CPyTagged_StealFromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (unlikely(overflow != 0)) { + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + Py_DECREF(object); + return value << 1; + } +} + +static inline CPyTagged CPyTagged_BorrowFromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (unlikely(overflow != 0)) { + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + return value << 1; + } +} + static inline bool CPyTagged_TooBig(Py_ssize_t value) { // Micro-optimized for the common case where it fits. return (size_t)value > CPY_TAGGED_MAX @@ -286,6 +318,107 @@ static inline bool CPyTagged_IsLe(CPyTagged left, CPyTagged right) { } } +static inline int64_t CPyLong_AsInt64(PyObject *o) { + if (likely(PyLong_Check(o))) { + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = Py_SIZE(lobj); + if (likely(size == 1)) { + // Fast path + return CPY_LONG_DIGIT(lobj, 0); + } else if (likely(size == 0)) { + return 0; + } + } + // Slow path + return CPyLong_AsInt64_(o); +} + +static inline int32_t CPyLong_AsInt32(PyObject *o) { + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + return CPY_LONG_DIGIT(lobj, 0); + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + return CPY_LONG_DIGIT(lobj, 0); + } else if (likely(size == 0)) { + return 0; + } + #endif + } + // Slow path + return CPyLong_AsInt32_(o); +} + +static inline int16_t CPyLong_AsInt16(PyObject *o) { + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + digit x = CPY_LONG_DIGIT(lobj, 0); + if (x < 0x8000) + return x; + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + digit x = lobj->ob_digit[0]; + if (x < 0x8000) + return x; + } else if (likely(size == 0)) { + return 0; + } + #endif + } + // Slow path + return CPyLong_AsInt16_(o); +} + +static inline uint8_t CPyLong_AsUInt8(PyObject *o) { + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; + size_t tag = CPY_LONG_TAG(lobj); + if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { + // Fast path + digit x = CPY_LONG_DIGIT(lobj, 0); + if (x < 256) + return x; + } else if (likely(tag == CPY_SIGN_ZERO)) { + return 0; + } + #else + PyLongObject *lobj = (PyLongObject *)o; + Py_ssize_t size = lobj->ob_base.ob_size; + if (likely(size == 1)) { + // Fast path + digit x = lobj->ob_digit[0]; + if (x < 256) + return x; + } else if (likely(size == 0)) { + return 0; + } + #endif + } + // Slow path + return CPyLong_AsUInt8_(o); +} + static inline CPyTagged CPyTagged_Negate(CPyTagged num) { if (likely(CPyTagged_CheckShort(num) && num != (CPyTagged) ((Py_ssize_t)1 << (CPY_INT_BITS - 1)))) { diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index b1b3d6e125f3..9b5d4ef65fb1 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -44,41 +44,6 @@ CPyTagged CPyTagged_FromInt64(int64_t value) { } } -CPyTagged CPyTagged_FromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (unlikely(overflow != 0)) { - Py_INCREF(object); - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - return value << 1; - } -} - -CPyTagged CPyTagged_StealFromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (unlikely(overflow != 0)) { - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - Py_DECREF(object); - return value << 1; - } -} - -CPyTagged CPyTagged_BorrowFromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (unlikely(overflow != 0)) { - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - return value << 1; - } -} - PyObject *CPyTagged_AsObject(CPyTagged x) { PyObject *value; if (unlikely(CPyTagged_CheckLong(x))) { @@ -420,18 +385,8 @@ CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -int64_t CPyLong_AsInt64(PyObject *o) { - if (likely(PyLong_Check(o))) { - PyLongObject *lobj = (PyLongObject *)o; - Py_ssize_t size = Py_SIZE(lobj); - if (likely(size == 1)) { - // Fast path - return CPY_LONG_DIGIT(lobj, 0); - } else if (likely(size == 0)) { - return 0; - } - } - // Slow path +// i64 unboxing slow path +int64_t CPyLong_AsInt64_(PyObject *o) { int overflow; int64_t result = PyLong_AsLongLongAndOverflow(o, &overflow); if (result == -1) { @@ -479,29 +434,8 @@ int64_t CPyInt64_Remainder(int64_t x, int64_t y) { return d; } -int32_t CPyLong_AsInt32(PyObject *o) { - if (likely(PyLong_Check(o))) { - #if CPY_3_12_FEATURES - PyLongObject *lobj = (PyLongObject *)o; - size_t tag = CPY_LONG_TAG(lobj); - if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { - // Fast path - return CPY_LONG_DIGIT(lobj, 0); - } else if (likely(tag == CPY_SIGN_ZERO)) { - return 0; - } - #else - PyLongObject *lobj = (PyLongObject *)o; - Py_ssize_t size = lobj->ob_base.ob_size; - if (likely(size == 1)) { - // Fast path - return CPY_LONG_DIGIT(lobj, 0); - } else if (likely(size == 0)) { - return 0; - } - #endif - } - // Slow path +// i32 unboxing slow path +int32_t CPyLong_AsInt32_(PyObject *o) { int overflow; long result = PyLong_AsLongAndOverflow(o, &overflow); if (result > 0x7fffffffLL || result < -0x80000000LL) { @@ -557,33 +491,8 @@ void CPyInt32_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i32"); } -int16_t CPyLong_AsInt16(PyObject *o) { - if (likely(PyLong_Check(o))) { - #if CPY_3_12_FEATURES - PyLongObject *lobj = (PyLongObject *)o; - size_t tag = CPY_LONG_TAG(lobj); - if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { - // Fast path - digit x = CPY_LONG_DIGIT(lobj, 0); - if (x < 0x8000) - return x; - } else if (likely(tag == CPY_SIGN_ZERO)) { - return 0; - } - #else - PyLongObject *lobj = (PyLongObject *)o; - Py_ssize_t size = lobj->ob_base.ob_size; - if (likely(size == 1)) { - // Fast path - digit x = lobj->ob_digit[0]; - if (x < 0x8000) - return x; - } else if (likely(size == 0)) { - return 0; - } - #endif - } - // Slow path +// i16 unboxing slow path +int16_t CPyLong_AsInt16_(PyObject *o) { int overflow; long result = PyLong_AsLongAndOverflow(o, &overflow); if (result > 0x7fff || result < -0x8000) { @@ -639,34 +548,8 @@ void CPyInt16_Overflow() { PyErr_SetString(PyExc_OverflowError, "int too large to convert to i16"); } - -uint8_t CPyLong_AsUInt8(PyObject *o) { - if (likely(PyLong_Check(o))) { - #if CPY_3_12_FEATURES - PyLongObject *lobj = (PyLongObject *)o; - size_t tag = CPY_LONG_TAG(lobj); - if (likely(tag == (1 << CPY_NON_SIZE_BITS))) { - // Fast path - digit x = CPY_LONG_DIGIT(lobj, 0); - if (x < 256) - return x; - } else if (likely(tag == CPY_SIGN_ZERO)) { - return 0; - } - #else - PyLongObject *lobj = (PyLongObject *)o; - Py_ssize_t size = lobj->ob_base.ob_size; - if (likely(size == 1)) { - // Fast path - digit x = lobj->ob_digit[0]; - if (x < 256) - return x; - } else if (likely(size == 0)) { - return 0; - } - #endif - } - // Slow path +// u8 unboxing slow path +uint8_t CPyLong_AsUInt8_(PyObject *o) { int overflow; long result = PyLong_AsLongAndOverflow(o, &overflow); if (result < 0 || result >= 256) { diff --git a/mypyc/lib-rt/pythonsupport.c b/mypyc/lib-rt/pythonsupport.c new file mode 100644 index 000000000000..90fb69705a00 --- /dev/null +++ b/mypyc/lib-rt/pythonsupport.c @@ -0,0 +1,106 @@ +// Collects code that was copied in from cpython, for a couple of different reasons: +// * We wanted to modify it to produce a more efficient version for our uses +// * We needed to call it and it was static :( +// * We wanted to call it and needed to backport it + +#include "pythonsupport.h" + +#if CPY_3_12_FEATURES + +// Slow path of CPyLong_AsSsize_tAndOverflow (non-inlined) +Py_ssize_t +CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow) +{ + PyLongObject *v = (PyLongObject *)vv; + size_t x, prev; + Py_ssize_t res; + Py_ssize_t i; + int sign; + + *overflow = 0; + + res = -1; + i = CPY_LONG_TAG(v); + + sign = 1; + x = 0; + if (i & CPY_SIGN_NEGATIVE) { + sign = -1; + } + i >>= CPY_NON_SIZE_BITS; + while (--i >= 0) { + prev = x; + x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); + if ((x >> PyLong_SHIFT) != prev) { + *overflow = sign; + goto exit; + } + } + /* Haven't lost any bits, but casting to long requires extra + * care. + */ + if (x <= (size_t)CPY_TAGGED_MAX) { + res = (Py_ssize_t)x * sign; + } + else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { + res = CPY_TAGGED_MIN; + } + else { + *overflow = sign; + /* res is already set to -1 */ + } + exit: + return res; +} + +#else + +// Slow path of CPyLong_AsSsize_tAndOverflow (non-inlined, Python 3.11 and earlier) +Py_ssize_t +CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow) +{ + /* This version by Tim Peters */ + PyLongObject *v = (PyLongObject *)vv; + size_t x, prev; + Py_ssize_t res; + Py_ssize_t i; + int sign; + + *overflow = 0; + + res = -1; + i = Py_SIZE(v); + + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -(i); + } + while (--i >= 0) { + prev = x; + x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); + if ((x >> PyLong_SHIFT) != prev) { + *overflow = sign; + goto exit; + } + } + /* Haven't lost any bits, but casting to long requires extra + * care. + */ + if (x <= (size_t)CPY_TAGGED_MAX) { + res = (Py_ssize_t)x * sign; + } + else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { + res = CPY_TAGGED_MIN; + } + else { + *overflow = sign; + /* res is already set to -1 */ + } + exit: + return res; +} + + +#endif diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index f7d501f44a27..85f9ec64ac90 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -129,6 +129,9 @@ init_subclass(PyTypeObject *type, PyObject *kwds) return 0; } +Py_ssize_t +CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow); + #if CPY_3_12_FEATURES static inline Py_ssize_t @@ -136,10 +139,8 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) { /* This version by Tim Peters */ PyLongObject *v = (PyLongObject *)vv; - size_t x, prev; Py_ssize_t res; Py_ssize_t i; - int sign; *overflow = 0; @@ -154,35 +155,12 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) } else if (i == ((1 << CPY_NON_SIZE_BITS) | CPY_SIGN_NEGATIVE)) { res = -(sdigit)CPY_LONG_DIGIT(v, 0); } else { - sign = 1; - x = 0; - if (i & CPY_SIGN_NEGATIVE) { - sign = -1; - } - i >>= CPY_NON_SIZE_BITS; - while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); - if ((x >> PyLong_SHIFT) != prev) { - *overflow = sign; - goto exit; - } - } - /* Haven't lost any bits, but casting to long requires extra - * care (see comment above). - */ - if (x <= (size_t)CPY_TAGGED_MAX) { - res = (Py_ssize_t)x * sign; - } - else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { - res = CPY_TAGGED_MIN; - } - else { - *overflow = sign; - /* res is already set to -1 */ - } + // Slow path is moved to a non-inline helper function to + // limit size of generated code + int overflow_local; + res = CPyLong_AsSsize_tAndOverflow_(vv, &overflow_local); + *overflow = overflow_local; } - exit: return res; } @@ -204,10 +182,8 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) { /* This version by Tim Peters */ PyLongObject *v = (PyLongObject *)vv; - size_t x, prev; Py_ssize_t res; Py_ssize_t i; - int sign; *overflow = 0; @@ -221,35 +197,12 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) } else if (i == -1) { res = -(sdigit)CPY_LONG_DIGIT(v, 0); } else { - sign = 1; - x = 0; - if (i < 0) { - sign = -1; - i = -(i); - } - while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); - if ((x >> PyLong_SHIFT) != prev) { - *overflow = sign; - goto exit; - } - } - /* Haven't lost any bits, but casting to long requires extra - * care (see comment above). - */ - if (x <= (size_t)CPY_TAGGED_MAX) { - res = (Py_ssize_t)x * sign; - } - else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { - res = CPY_TAGGED_MIN; - } - else { - *overflow = sign; - /* res is already set to -1 */ - } + // Slow path is moved to a non-inline helper function to + // limit size of generated code + int overflow_local; + res = CPyLong_AsSsize_tAndOverflow_(vv, &overflow_local); + *overflow = overflow_local; } - exit: return res; } diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index ef81b794c9bd..66b130581cb3 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -58,6 +58,7 @@ def run(self): "list_ops.c", "exc_ops.c", "generic_ops.c", + "pythonsupport.c", ], depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"], extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args, From b20255276e72803da5c5f98cf6982b782cf5f4d7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 17 Jun 2024 12:03:01 +0100 Subject: [PATCH 0644/1617] [mypyc] Support Python 3.12 type alias syntax (PEP 695) (#17384) The main tricky bit is supporting uses of type alias objects at runtime. Python evaluates values of type aliases lazily, but there's no way to do this using public APIs, so we directly modify the `TypeAliasType` object that is used to represent a type alias at runtime in C. Unfortunately, this is fragile and will need to be updated each time CPython updates the internal representation of `TypeAliasType` objects. Wrap the target of the type alias within a lambda expression, so that we can easily create the lazy compute function in mypyc. This also reflects how this is implemented in CPython. Improve test stubs to avoid various false positives or confusing errors in tests when type checking runtime operations on types. This also makes some exisisting tests more realistic. Follow-up to #17357. --- mypy/checker.py | 4 ++ mypy/checkexpr.py | 4 +- mypy/fastparse.py | 8 +++- mypy/nodes.py | 4 +- mypy/semanal.py | 13 +++++- mypyc/irbuild/builder.py | 46 +++++++++++++++++++ mypyc/irbuild/classdef.py | 37 ++++----------- mypyc/irbuild/statement.py | 34 +++++++++++++- mypyc/irbuild/visitor.py | 3 +- mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/misc_ops.c | 31 +++++++++++++ mypyc/primitives/misc_ops.py | 12 +++++ mypyc/test-data/fixtures/ir.py | 1 + mypyc/test-data/fixtures/typing-full.pyi | 5 +- mypyc/test-data/run-python312.test | 53 ++++++++++++++++++++++ test-data/unit/check-class-namedtuple.test | 3 +- test-data/unit/check-expressions.test | 6 ++- test-data/unit/check-generics.test | 1 + test-data/unit/check-newsemanal.test | 8 ++-- test-data/unit/check-python312.test | 28 ++++++++++-- test-data/unit/check-redefine.test | 9 ++-- test-data/unit/check-type-aliases.test | 9 ++-- test-data/unit/check-typevar-tuple.test | 6 ++- test-data/unit/check-union-or-syntax.test | 2 +- test-data/unit/deps.test | 6 +++ test-data/unit/fine-grained-python312.test | 1 + test-data/unit/fixtures/isinstance.pyi | 3 +- test-data/unit/fixtures/tuple.pyi | 28 ++++++------ test-data/unit/fixtures/type.pyi | 1 + test-data/unit/fixtures/typing-full.pyi | 13 ++++-- test-data/unit/lib-stub/types.pyi | 2 + test-data/unit/parse-python312.test | 9 ++-- 32 files changed, 310 insertions(+), 81 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 119aa9f3cea2..bf739e7d1242 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -125,6 +125,7 @@ TryStmt, TupleExpr, TypeAlias, + TypeAliasStmt, TypeInfo, TypeVarExpr, UnaryExpr, @@ -5289,6 +5290,9 @@ def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, if node not in inferred_types or not is_subtype(typ, inferred_types[node]): del type_map[expr] + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + self.expr_checker.accept(o.value) + def make_fake_typeinfo( self, curr_module_fullname: str, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 861c28e5b54c..4fd1a308e560 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -411,7 +411,9 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = self.alias_type_in_runtime_context( node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue ) - elif isinstance(node, (TypeVarExpr, ParamSpecExpr, TypeVarTupleExpr)): + elif isinstance(node, TypeVarExpr): + return self.named_type("typing.TypeVar") + elif isinstance(node, (ParamSpecExpr, TypeVarTupleExpr)): result = self.object_type() else: if isinstance(node, PlaceholderNode): diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 70afe9010583..342cf36d69e8 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1791,7 +1791,13 @@ def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: type_params = self.translate_type_params(n.type_params) value = self.visit(n.value) - node = TypeAliasStmt(self.visit_Name(n.name), type_params, value) + # Since the value is evaluated lazily, wrap the value inside a lambda. + # This helps mypyc. + ret = ReturnStmt(value) + self.set_line(ret, n.value) + value_func = LambdaExpr(body=Block([ret])) + self.set_line(value_func, n.value) + node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) return self.set_line(node, n) else: self.fail( diff --git a/mypy/nodes.py b/mypy/nodes.py index 850b1db87556..5d3a1d31aece 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1653,10 +1653,10 @@ class TypeAliasStmt(Statement): name: NameExpr type_args: list[TypeParam] - value: Expression # Will get translated into a type + value: LambdaExpr # Return value will get translated into a type invalid_recursive_alias: bool - def __init__(self, name: NameExpr, type_args: list[TypeParam], value: Expression) -> None: + def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr) -> None: super().__init__() self.name = name self.type_args = type_args diff --git a/mypy/semanal.py b/mypy/semanal.py index d2f02d4835e2..03e6172bb325 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3766,6 +3766,10 @@ def analyze_alias( last_tvar_name_with_default = tvar_def.name tvar_defs.append(tvar_def) + if python_3_12_type_alias: + with self.allow_unbound_tvars_set(): + rvalue.accept(self) + analyzed, depends_on = analyze_type_alias( typ, self, @@ -5360,7 +5364,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: tag = self.track_incomplete_refs() res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( s.name.name, - s.value, + s.value.expr(), allow_placeholder=True, declared_type_vars=type_params, all_declared_type_params_names=all_type_params_names, @@ -5443,6 +5447,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: current_node = existing.node if existing else alias_node assert isinstance(current_node, TypeAlias) self.disable_invalid_recursive_aliases(s, current_node, s.value) + s.name.accept(self) finally: self.pop_type_args(s.type_args) @@ -5457,7 +5462,11 @@ def visit_name_expr(self, expr: NameExpr) -> None: def bind_name_expr(self, expr: NameExpr, sym: SymbolTableNode) -> None: """Bind name expression to a symbol table node.""" - if isinstance(sym.node, TypeVarExpr) and self.tvar_scope.get_binding(sym): + if ( + isinstance(sym.node, TypeVarExpr) + and self.tvar_scope.get_binding(sym) + and not self.allow_unbound_tvars + ): self.fail(f'"{expr.name}" is a type variable and only valid in type context', expr) elif isinstance(sym.node, PlaceholderNode): self.process_placeholder(expr.name, "name", expr) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 1b4f551d4a2a..a9e1ce471953 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -24,6 +24,9 @@ ARG_POS, GDEF, LDEF, + PARAM_SPEC_KIND, + TYPE_VAR_KIND, + TYPE_VAR_TUPLE_KIND, ArgKind, CallExpr, Decorator, @@ -44,6 +47,7 @@ TupleExpr, TypeAlias, TypeInfo, + TypeParam, UnaryExpr, Var, ) @@ -1409,3 +1413,45 @@ def get_call_target_fullname(ref: RefExpr) -> str: if isinstance(target, Instance): return target.type.fullname return ref.fullname + + +def create_type_params( + builder: IRBuilder, typing_mod: Value, type_args: list[TypeParam], line: int +) -> list[Value]: + """Create objects representing various kinds of Python 3.12 type parameters. + + The "typing_mod" argument is the "_typing" module object. The type objects + are looked up from it. + + The returned list has one item for each "type_args" item, in the same order. + Each item is either a TypeVar, TypeVarTuple or ParamSpec instance. + """ + tvs = [] + type_var_imported: Value | None = None + for type_param in type_args: + if type_param.kind == TYPE_VAR_KIND: + if type_var_imported: + # Reuse previously imported value as a minor optimization + tvt = type_var_imported + else: + tvt = builder.py_get_attr(typing_mod, "TypeVar", line) + type_var_imported = tvt + elif type_param.kind == TYPE_VAR_TUPLE_KIND: + tvt = builder.py_get_attr(typing_mod, "TypeVarTuple", line) + else: + assert type_param.kind == PARAM_SPEC_KIND + tvt = builder.py_get_attr(typing_mod, "ParamSpec", line) + if type_param.kind != TYPE_VAR_TUPLE_KIND: + # To match runtime semantics, pass infer_variance=True + tv = builder.py_call( + tvt, + [builder.load_str(type_param.name), builder.true()], + line, + arg_kinds=[ARG_POS, ARG_NAMED], + arg_names=[None, "infer_variance"], + ) + else: + tv = builder.py_call(tvt, [builder.load_str(type_param.name)], line) + builder.init_type_var(tv, type_param.name, line) + tvs.append(tv) + return tvs diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 303ee8849244..2152da099e81 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,8 +7,6 @@ from typing import Callable, Final from mypy.nodes import ( - PARAM_SPEC_KIND, - TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, AssignmentStmt, CallExpr, @@ -57,7 +55,7 @@ is_optional_type, object_rprimitive, ) -from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.builder import IRBuilder, create_type_params from mypyc.irbuild.function import ( gen_property_getter_ir, gen_property_setter_ir, @@ -475,35 +473,20 @@ def make_generic_base_class( ) -> Value: """Construct Generic[...] base class object for a new-style generic class (Python 3.12).""" mod = builder.call_c(import_op, [builder.load_str("_typing")], line) - tvs = [] - type_var_imported: Value | None = None - for type_param in type_args: - unpack = False - if type_param.kind == TYPE_VAR_KIND: - if type_var_imported: - # Reuse previously imported value as a minor optimization - tvt = type_var_imported - else: - tvt = builder.py_get_attr(mod, "TypeVar", line) - type_var_imported = tvt - elif type_param.kind == TYPE_VAR_TUPLE_KIND: - tvt = builder.py_get_attr(mod, "TypeVarTuple", line) - unpack = True - else: - assert type_param.kind == PARAM_SPEC_KIND - tvt = builder.py_get_attr(mod, "ParamSpec", line) - tv = builder.py_call(tvt, [builder.load_str(type_param.name)], line) - builder.init_type_var(tv, type_param.name, line) - if unpack: + tvs = create_type_params(builder, mod, type_args, line) + args = [] + for tv, type_param in zip(tvs, type_args): + if type_param.kind == TYPE_VAR_TUPLE_KIND: # Evaluate *Ts for a TypeVarTuple it = builder.call_c(iter_op, [tv], line) tv = builder.call_c(next_op, [it], line) - tvs.append(tv) + args.append(tv) + gent = builder.py_get_attr(mod, "Generic", line) - if len(tvs) == 1: - arg = tvs[0] + if len(args) == 1: + arg = args[0] else: - arg = builder.new_tuple(tvs, line) + arg = builder.new_tuple(args, line) base = builder.call_c(py_get_item_op, [gent, arg], line) return base diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 2c17eb2bb14d..4d828b1b9d82 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -12,6 +12,8 @@ from typing import Callable, Sequence from mypy.nodes import ( + ARG_NAMED, + ARG_POS, AssertStmt, AssignmentStmt, AwaitExpr, @@ -37,6 +39,7 @@ TempNode, TryStmt, TupleExpr, + TypeAliasStmt, WhileStmt, WithStmt, YieldExpr, @@ -74,7 +77,7 @@ object_rprimitive, ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional -from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op +from mypyc.irbuild.builder import IRBuilder, create_type_params, int_borrow_friendly_op from mypyc.irbuild.for_helpers import for_loop_helper from mypyc.irbuild.generator import add_raise_exception_blocks_to_generator_class from mypyc.irbuild.nonlocalcontrol import ( @@ -105,7 +108,9 @@ coro_op, import_from_many_op, import_many_op, + import_op, send_op, + set_type_alias_compute_function_op, type_op, yield_from_except_op, ) @@ -1015,3 +1020,30 @@ def transform_await_expr(builder: IRBuilder, o: AwaitExpr) -> Value: def transform_match_stmt(builder: IRBuilder, m: MatchStmt) -> None: m.accept(MatchVisitor(builder, m)) + + +def transform_type_alias_stmt(builder: IRBuilder, s: TypeAliasStmt) -> None: + line = s.line + # Use "_typing" to avoid importing "typing", as the latter can be expensive. + # "_typing" includes everything we need here. + mod = builder.call_c(import_op, [builder.load_str("_typing")], line) + type_params = create_type_params(builder, mod, s.type_args, s.line) + + type_alias_type = builder.py_get_attr(mod, "TypeAliasType", line) + args = [builder.load_str(s.name.name), builder.none()] + arg_names: list[str | None] = [None, None] + arg_kinds = [ARG_POS, ARG_POS] + if s.type_args: + args.append(builder.new_tuple(type_params, line)) + arg_names.append("type_params") + arg_kinds.append(ARG_NAMED) + alias = builder.py_call(type_alias_type, args, line, arg_names=arg_names, arg_kinds=arg_kinds) + + # Use primitive to set function used to lazily compute type alias type value. + # The value needs to be lazily computed to match Python runtime behavior, but + # Python public APIs don't support this, so we use a C primitive. + compute_fn = s.value.accept(builder.visitor) + builder.builder.primitive_op(set_type_alias_compute_function_op, [alias, compute_fn], line) + + target = builder.get_assignment_target(s.name) + builder.assign(target, alias, line) diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index e7256f036e4c..05a033c3e6ad 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -137,6 +137,7 @@ transform_raise_stmt, transform_return_stmt, transform_try_stmt, + transform_type_alias_stmt, transform_while_stmt, transform_with_stmt, transform_yield_expr, @@ -251,7 +252,7 @@ def visit_match_stmt(self, stmt: MatchStmt) -> None: transform_match_stmt(self.builder, stmt) def visit_type_alias_stmt(self, stmt: TypeAliasStmt) -> None: - self.bail('The "type" statement is not yet supported by mypyc', stmt.line) + transform_type_alias_stmt(self.builder, stmt) # Expressions diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 8aa5a77c180c..2ec04e4c5b5c 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -901,6 +901,7 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, PyOb PyObject *CPy_GetAIter(PyObject *obj); PyObject *CPy_GetANext(PyObject *aiter); +void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value); #ifdef __cplusplus } diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index f28eeb57e646..803123d436a2 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -940,3 +940,34 @@ PyObject *CPy_GetANext(PyObject *aiter) error: return NULL; } + +#ifdef CPY_3_12_FEATURES + +// Copied from Python 3.12.3, since this struct is internal to CPython. It defines +// the structure of typing.TypeAliasType objects. We need it since compute_value is +// not part of the public API, and we need to set it to match Python runtime semantics. +// +// IMPORTANT: This needs to be kept in sync with CPython! +typedef struct { + PyObject_HEAD + PyObject *name; + PyObject *type_params; + PyObject *compute_value; + PyObject *value; + PyObject *module; +} typealiasobject; + +void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value) { + typealiasobject *obj = (typealiasobject *)alias; + if (obj->value != NULL) { + Py_DECREF(obj->value); + } + obj->value = NULL; + Py_INCREF(compute_value); + if (obj->compute_value != NULL) { + Py_DECREF(obj->compute_value); + } + obj->compute_value = compute_value; +} + +#endif diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index fea62bbb19c4..e9016e24c46d 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -265,3 +265,15 @@ return_type=c_pyssize_t_rprimitive, error_kind=ERR_NEVER, ) + +# Set the lazy value compute function of an TypeAliasType instance (Python 3.12+). +# This must only be used as part of initializing the object. Any existing value +# will be cleared. +set_type_alias_compute_function_op = custom_primitive_op( + name="set_type_alias_compute_function", + c_function_name="CPy_SetTypeAliasTypeComputeFunction", + # (alias object, value compute function) + arg_types=[object_rprimitive, object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 6f0d8da90d57..ac95ffe2c047 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -45,6 +45,7 @@ def __ne__(self, x: object) -> bool: pass class type: def __init__(self, o: object) -> None: ... + def __or__(self, o: object) -> Any: ... __name__ : str __annotations__: Dict[str, Any] diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi index 3ddc1f1bba08..8bb3b1398f87 100644 --- a/mypyc/test-data/fixtures/typing-full.pyi +++ b/mypyc/test-data/fixtures/typing-full.pyi @@ -10,6 +10,9 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass +class _SpecialForm: + def __getitem__(self, index): ... + cast = 0 overload = 0 Any = 0 @@ -19,7 +22,6 @@ TypeVar = 0 Generic = 0 Protocol = 0 Tuple = 0 -Callable = 0 _promote = 0 NamedTuple = 0 Type = 0 @@ -30,6 +32,7 @@ Literal = 0 TypedDict = 0 NoReturn = 0 NewType = 0 +Callable: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test index fbafeaf3e65f..5e8a388fd8d3 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -169,4 +169,57 @@ def test_class_with_value_restriction() -> None: assert r.x == 1 r2 = Restriction[str]('a') assert r2.x == 'a' + +type A = int + +def test_simple_type_alias() -> None: + assert isinstance(A, TypeAliasType) + assert getattr(A, "__value__") is int + assert str(A) == "A" + +type B = Fwd[int] +Fwd = list + +def test_forward_reference_in_alias() -> None: + assert isinstance(B, TypeAliasType) + assert getattr(B, "__value__") == list[int] + +type R = int | list[R] + +def test_recursive_type_alias() -> None: + assert isinstance(R, TypeAliasType) + assert getattr(R, "__value__") == (int | list[R]) +[typing fixtures/typing-full.pyi] + +[case testPEP695GenericTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Callable +from types import GenericAlias + +from testutil import assertRaises + +type A[T] = list[T] + +def test_generic_alias() -> None: + assert type(A[str]) is GenericAlias + assert str(A[str]) == "A[str]" + assert str(getattr(A, "__value__")) == "list[T]" + +type B[T, S] = dict[S, T] + +def test_generic_alias_with_two_args() -> None: + assert str(B[str, int]) == "B[str, int]" + assert str(getattr(B, "__value__")) == "dict[S, T]" + +type C[*Ts] = tuple[*Ts] + +def test_type_var_tuple_type_alias() -> None: + assert str(C[int, str]) == "C[int, str]" + assert str(getattr(C, "__value__")) == "tuple[typing.Unpack[Ts]]" + +type D[**P] = Callable[P, int] + +def test_param_spec_type_alias() -> None: + assert str(D[[int, str]]) == "D[[int, str]]" + assert str(getattr(D, "__value__")) == "typing.Callable[P, int]" [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index f334b9011645..fd564c7e96cb 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -535,7 +535,7 @@ class Base(NamedTuple): self.x = 3 # E: Property "x" defined in "Base" is read-only self[1] # E: Tuple index out of range reveal_type(self[T]) # N: Revealed type is "builtins.int" \ - # E: No overload variant of "__getitem__" of "tuple" matches argument type "object" \ + # E: No overload variant of "__getitem__" of "tuple" matches argument type "TypeVar" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> int \ # N: def __getitem__(self, slice, /) -> Tuple[int, ...] @@ -568,6 +568,7 @@ reveal_type(Base(1).bad_override()) # N: Revealed type is "builtins.int" reveal_type(takes_base(Base(1))) # N: Revealed type is "builtins.int" reveal_type(takes_base(Child(1))) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testNewNamedTupleIllegalNames] from typing import Callable, NamedTuple diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 04b3f7a131cc..4fc6e9a75c83 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2449,5 +2449,7 @@ def f() -> int: # E: Missing return statement from typing import TypeVar T = TypeVar("T") x: int -x + T # E: Unsupported operand types for + ("int" and "object") -T() # E: "object" not callable +x + T # E: Unsupported left operand type for + ("int") +T() # E: "TypeVar" not callable +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index ea3f501fd949..abcb2a4bbc48 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -624,6 +624,7 @@ reveal_type(y) X = T # Error [builtins fixtures/list.pyi] +[typing fixtures/typing-full.pyi] [out] main:9:5: error: "Node" expects 2 type arguments, but 1 given main:11:5: error: "Node" expects 2 type arguments, but 3 given diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 7cbed5637c3a..47e508ee1a6b 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2184,8 +2184,7 @@ from typing import TypeVar, Generic, Any T = TypeVar('T', bound='B[Any]') # The "int" error is because of typing fixture. T = TypeVar('T', bound='C') # E: Cannot redefine "T" as a type variable \ - # E: Invalid assignment target \ - # E: "int" not callable + # E: Invalid assignment target class B(Generic[T]): x: T @@ -2194,6 +2193,8 @@ class C: ... x: B[int] # E: Type argument "int" of "B" must be a subtype of "B[Any]" y: B[B[Any]] reveal_type(y.x) # N: Revealed type is "__main__.B[Any]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testNewAnalyzerDuplicateTypeVarImportCycle] # flags: --disable-error-code used-before-def @@ -2216,12 +2217,13 @@ class C: ... x: B[int] y: B[B[Any]] reveal_type(y.x) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [out] tmp/b.py:8: error: Type argument "int" of "B" must be a subtype of "B[Any]" tmp/b.py:10: note: Revealed type is "b.B[Any]" tmp/a.py:5: error: Cannot redefine "T" as a type variable tmp/a.py:5: error: Invalid assignment target -tmp/a.py:5: error: "int" not callable [case testNewAnalyzerDuplicateTypeVarImportCycleWithAliases] # flags: --disable-error-code used-before-def diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 06c5bada1e92..b3a3645dc9f8 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -41,7 +41,8 @@ reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ [case test695TypeVar] from typing import Callable -type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported +type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported \ + # E: Name "T" is not defined type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported \ # E: Value of type "int" is not indexable \ # E: Name "P" is not defined @@ -52,7 +53,9 @@ class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported class Cls2[**P]: ... # E: PEP 695 generics are not yet supported class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported -def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported +def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported \ + # E: Name "T" is not defined + def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported \ # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ @@ -504,6 +507,7 @@ reveal_type(a3) # N: Revealed type is "__main__.D[builtins.str, __main__.C[buil type A4 = int | str a4: A4 reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/type.pyi] [case testPEP695TypeAliasWithUnusedTypeParams] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -531,6 +535,8 @@ a: A reveal_type(a) # N: Revealed type is "__main__.C" class C: pass +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeAliasForwardReference3] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -579,12 +585,15 @@ reveal_type(a) # N: Revealed type is "Any" [case testPEP695TypeAliasInvalidType] # flags: --enable-incomplete-feature=NewGenericSyntax -type A = int | 1 # E: Invalid type: try using Literal[1] instead? +type A = int | 1 # E: Invalid type: try using Literal[1] instead? \ + # E: Unsupported operand types for | ("Type[int]" and "int") + a: A reveal_type(a) # N: Revealed type is "Union[builtins.int, Any]" type B = int + str # E: Invalid type alias: expression is not a valid type b: B reveal_type(b) # N: Revealed type is "Any" +[builtins fixtures/type.pyi] [case testPEP695TypeAliasBoundForwardReference] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -809,6 +818,7 @@ type C[**P] = Callable[P, int] f: C[[str, int | None]] reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, None]) -> builtins.int" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeVarTuple] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -1062,7 +1072,7 @@ from typing import cast def f[T]( x: T = T # E: Name "T" is not defined \ - # E: Incompatible default for argument "x" (default has type "object", argument has type "T") + # E: Incompatible default for argument "x" (default has type "TypeVar", argument has type "T") ) -> T: return x @@ -1072,6 +1082,8 @@ def g[T](x: T = cast(T, None)) -> T: # E: Name "T" is not defined class C: def m[T](self, x: T = cast(T, None)) -> T: # E: Name "T" is not defined return x +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695ListComprehension] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1174,6 +1186,7 @@ class C[T]: pass type B[T] = C[T] | list[B[T]] b: B[int] reveal_type(b) # N: Revealed type is "Union[__main__.C[builtins.int], builtins.list[...]]" +[builtins fixtures/type.pyi] [case testPEP695BadRecursiveTypeAlias] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1184,6 +1197,8 @@ a: A reveal_type(a) # N: Revealed type is "Any" b: B reveal_type(b) # N: Revealed type is "Any" +[builtins fixtures/type.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695RecursiveTypeAliasForwardReference] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1272,6 +1287,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[Any]" type B = tuple[*Ts] # E: All type parameters should be declared ("Ts" not declared) type C = Callable[P, None] # E: All type parameters should be declared ("P" not declared) [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695NonGenericAliasToGenericClass] # mypy: enable-incomplete-feature=NewGenericSyntax @@ -1399,6 +1415,8 @@ c1: A3[C, int] c2: A3[D, str] c3: A3[C, N] # E: Value of type variable "S" of "A3" cannot be "N" c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" +[builtins fixtures/type.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695TypeAliasInClassBodyOrFunction] # flags: --enable-incomplete-feature=NewGenericSyntax @@ -1451,7 +1469,7 @@ class E[T]: self.a: A reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] [case testPEP695RedefineAsTypeAlias1] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index e3f1b976d4e9..b7642d30efc8 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -270,14 +270,17 @@ def f() -> None: from typing import TypeVar def f() -> None: x = TypeVar('x') - x = 1 # E: Invalid assignment target - reveal_type(x) # N: Revealed type is "builtins.int" + x = 1 # E: Invalid assignment target \ + # E: Incompatible types in assignment (expression has type "int", variable has type "TypeVar") + reveal_type(x) # N: Revealed type is "typing.TypeVar" y = 1 # NOTE: '"int" not callable' is due to test stubs y = TypeVar('y') # E: Cannot redefine "y" as a type variable \ - # E: "int" not callable + # E: Incompatible types in assignment (expression has type "TypeVar", variable has type "int") def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testCannotRedefineVarAsModule] # flags: --allow-redefinition diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 5eea1fb2b53e..6f9e9eda1d02 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -93,11 +93,9 @@ T = TypeVar('T') A = Tuple[T, T] if int(): - A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ - # E: Value of type "int" is not indexable - # the second error is because of `Union = 0` in lib-stub/typing.pyi + A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation [builtins fixtures/tuple.pyi] -[out] +[typing fixtures/typing-full.pyi] [case testProhibitUsingVariablesAsTypesAndAllowAliasesAsTypes] @@ -1202,8 +1200,7 @@ unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expe reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str" class A(Generic[T]): - Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \ - # E: "T" is a type variable and only valid in type context + Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias x: A.Ta11 = {"a": 1} reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 0aff702e1b22..8f7dd12d9cd4 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1100,9 +1100,10 @@ reveal_type(t.fn) # N: Revealed type is "def (builtins.int, builtins.int, built [builtins fixtures/tuple.pyi] [case testVariadicNamedTuple] -from typing import Tuple, Callable, NamedTuple, Generic +from typing import Tuple, Callable, NamedTuple, Generic, TypeVar from typing_extensions import TypeVarTuple, Unpack +T = TypeVar("T") Ts = TypeVarTuple("Ts") class A(NamedTuple, Generic[Unpack[Ts], T]): fn: Callable[[Unpack[Ts]], None] @@ -1129,9 +1130,10 @@ nt2 = A(fn=bad, val=42) # E: Argument "fn" to "A" has incompatible type "Callab [builtins fixtures/tuple.pyi] [case testVariadicTypedDict] -from typing import Tuple, Callable, Generic +from typing import Tuple, Callable, Generic, TypeVar from typing_extensions import TypeVarTuple, Unpack, TypedDict +T = TypeVar("T") Ts = TypeVarTuple("Ts") class A(TypedDict, Generic[Unpack[Ts], T]): fn: Callable[[Unpack[Ts]], None] diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index b5fd85cb7ed8..a1b63077eef9 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -189,7 +189,7 @@ def g(x: int | str | tuple[int, str] | C) -> None: # flags: --python-version 3.9 from typing import Union def f(x: Union[int, str, None]) -> None: - if isinstance(x, int | str): # E: Unsupported left operand type for | ("Type[int]") + if isinstance(x, int | str): reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" else: reveal_type(x) # N: Revealed type is "None" diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index f46cfebb113f..3364dee6c696 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1443,7 +1443,13 @@ class C: pass class D: pass type E = D [out] + -> m + -> m -> m + -> m + -> m -> m -> m -> m +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 70cf427d6798..3970c8cacfbf 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -75,6 +75,7 @@ from typing import Union as B from builtins import tuple as B [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [out] == main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") diff --git a/test-data/unit/fixtures/isinstance.pyi b/test-data/unit/fixtures/isinstance.pyi index c1125c24b941..c1446492af9b 100644 --- a/test-data/unit/fixtures/isinstance.pyi +++ b/test-data/unit/fixtures/isinstance.pyi @@ -5,8 +5,9 @@ T = TypeVar('T') class object: def __init__(self) -> None: pass -class type: +class type(Generic[T]): def __init__(self, x) -> None: pass + def __or__(self, other: type) -> type: pass class tuple(Generic[T]): pass diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index eb89de8c86ef..3b62d7fc1513 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -3,8 +3,8 @@ import _typeshed from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type -T = TypeVar("T") -Tco = TypeVar('Tco', covariant=True) +_T = TypeVar("_T") +_Tco = TypeVar('_Tco', covariant=True) class object: def __init__(self) -> None: pass @@ -12,17 +12,17 @@ class object: class type: def __init__(self, *a: object) -> None: pass def __call__(self, *a: object) -> object: pass -class tuple(Sequence[Tco], Generic[Tco]): - def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... - def __iter__(self) -> Iterator[Tco]: pass +class tuple(Sequence[_Tco], Generic[_Tco]): + def __new__(cls: Type[_T], iterable: Iterable[_Tco] = ...) -> _T: ... + def __iter__(self) -> Iterator[_Tco]: pass def __contains__(self, item: object) -> bool: pass @overload - def __getitem__(self, x: int) -> Tco: pass + def __getitem__(self, x: int) -> _Tco: pass @overload - def __getitem__(self, x: slice) -> Tuple[Tco, ...]: ... - def __mul__(self, n: int) -> Tuple[Tco, ...]: pass - def __rmul__(self, n: int) -> Tuple[Tco, ...]: pass - def __add__(self, x: Tuple[Tco, ...]) -> Tuple[Tco, ...]: pass + def __getitem__(self, x: slice) -> Tuple[_Tco, ...]: ... + def __mul__(self, n: int) -> Tuple[_Tco, ...]: pass + def __rmul__(self, n: int) -> Tuple[_Tco, ...]: pass + def __add__(self, x: Tuple[_Tco, ...]) -> Tuple[_Tco, ...]: pass def count(self, obj: object) -> int: pass class function: __name__: str @@ -40,13 +40,13 @@ class str: pass # For convenience class bytes: pass class bytearray: pass -class list(Sequence[T], Generic[T]): +class list(Sequence[_T], Generic[_T]): @overload - def __getitem__(self, i: int) -> T: ... + def __getitem__(self, i: int) -> _T: ... @overload - def __getitem__(self, s: slice) -> list[T]: ... + def __getitem__(self, s: slice) -> list[_T]: ... def __contains__(self, item: object) -> bool: ... - def __iter__(self) -> Iterator[T]: ... + def __iter__(self) -> Iterator[_T]: ... def isinstance(x: object, t: type) -> bool: pass diff --git a/test-data/unit/fixtures/type.pyi b/test-data/unit/fixtures/type.pyi index 084b7f8388d8..4ae8ed9ca6b1 100644 --- a/test-data/unit/fixtures/type.pyi +++ b/test-data/unit/fixtures/type.pyi @@ -27,6 +27,7 @@ class bool: pass class int: pass class str: pass class ellipsis: pass +class float: pass if sys.version_info >= (3, 10): # type: ignore def isinstance(obj: object, class_or_tuple: type | types.UnionType, /) -> bool: ... diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 71d4dcb58853..9d61361fc16e 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -10,8 +10,11 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass -class _SpecialForm: ... -class TypeVar: ... +class _SpecialForm: + def __getitem__(self, index: Any) -> Any: ... +class TypeVar: + def __init__(self, name, *args, bound=None): ... + def __or__(self, other): ... class ParamSpec: ... class TypeVarTuple: ... @@ -19,12 +22,10 @@ def cast(t, o): ... def assert_type(o, t): ... overload = 0 Any = 0 -Union = 0 Optional = 0 Generic = 0 Protocol = 0 Tuple = 0 -Callable = 0 _promote = 0 Type = 0 no_type_check = 0 @@ -36,6 +37,8 @@ NoReturn = 0 NewType = 0 Self = 0 Unpack = 0 +Callable: _SpecialForm +Union: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -211,3 +214,5 @@ class TypeAliasType: def __init__( self, name: str, value: Any, *, type_params: Tuple[Union[TypeVar, ParamSpec, TypeVarTuple], ...] = () ) -> None: ... + + def __or__(self, other: Any) -> Any: ... diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index e4869dbc3093..dded0ba6cd9a 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -9,6 +9,8 @@ class ModuleType: __file__: str def __getattr__(self, name: str) -> Any: pass +class GenericAlias: ... + if sys.version_info >= (3, 10): class Union: def __or__(self, x) -> Union: ... diff --git a/test-data/unit/parse-python312.test b/test-data/unit/parse-python312.test index 28204ccd647b..90ee96f38deb 100644 --- a/test-data/unit/parse-python312.test +++ b/test-data/unit/parse-python312.test @@ -7,9 +7,12 @@ MypyFile:1( NameExpr(A) TypeParam( T) - IndexExpr:2( - NameExpr(C) - NameExpr(T)))) + LambdaExpr:2( + Block:-1( + ReturnStmt:2( + IndexExpr:2( + NameExpr(C) + NameExpr(T))))))) [case testPEP695GenericFunction] # mypy: enable-incomplete-feature=NewGenericSyntax From 06c7d2613a3eebac6a09befcad2b2fae6374d262 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 17 Jun 2024 15:22:14 +0200 Subject: [PATCH 0645/1617] stubgen: Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (#17386) This Fixes 2 issues with invalid `Optional` (inspired by an error reported in #17197): - do not crash on empty `Optional` - treat `Optional` with more than one index as an unknown type instead of choosing the first type. It also fixes PEP 604 unions not being recognized as type aliases. --- mypy/stubgen.py | 6 ++++++ mypy/stubutil.py | 4 +++- test-data/unit/stubgen.test | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 22028694ad6b..8478bd2135e4 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -314,6 +314,8 @@ def visit_index_expr(self, node: IndexExpr) -> str: return " | ".join([item.accept(self) for item in node.index.items]) return node.index.accept(self) if base_fullname == "typing.Optional": + if isinstance(node.index, TupleExpr): + return self.stubgen.add_name("_typeshed.Incomplete") return f"{node.index.accept(self)} | None" base = node.base.accept(self) index = node.index.accept(self) @@ -1060,6 +1062,10 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: else: return False return all(self.is_alias_expression(i, top_level=False) for i in indices) + elif isinstance(expr, OpExpr) and expr.op == "|": + return self.is_alias_expression( + expr.left, top_level=False + ) and self.is_alias_expression(expr.right, top_level=False) else: return False diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 8e41d6862531..2f2db0dbbe53 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -257,7 +257,9 @@ def visit_unbound_type(self, t: UnboundType) -> str: if fullname == "typing.Union": return " | ".join([item.accept(self) for item in t.args]) if fullname == "typing.Optional": - return f"{t.args[0].accept(self)} | None" + if len(t.args) == 1: + return f"{t.args[0].accept(self)} | None" + return self.stubgen.add_name("_typeshed.Incomplete") if fullname in TYPING_BUILTIN_REPLACEMENTS: s = self.stubgen.add_name(TYPING_BUILTIN_REPLACEMENTS[fullname], require=True) if self.known_modules is not None and "." in s: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 916e2e3a8e17..5dcb0706a8cb 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4366,3 +4366,33 @@ class Foo(Enum): class Bar(Enum): A = ... B = ... + +[case testGracefullyHandleInvalidOptionalUsage] +from typing import Optional + +x: Optional # invalid +y: Optional[int] # valid +z: Optional[int, str] # invalid +w: Optional[int | str] # valid +r: Optional[type[int | str]] + +X = Optional +Y = Optional[int] +Z = Optional[int, str] +W = Optional[int | str] +R = Optional[type[int | str]] + +[out] +from _typeshed import Incomplete +from typing import Optional + +x: Incomplete +y: int | None +z: Incomplete +w: int | str | None +r: type[int | str] | None +X = Optional +Y = int | None +Z = Incomplete +W = int | str | None +R = type[int | str] | None From ba5c2793b1f9bd253c0415492dffb703eb523306 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 18 Jun 2024 00:45:00 +0100 Subject: [PATCH 0646/1617] Allow new-style self-types in classmethods (#17381) Fixes https://github.com/python/mypy/issues/16547 Fixes https://github.com/python/mypy/issues/16410 Fixes https://github.com/python/mypy/issues/5570 From the upvotes on the issue it looks like an important use case. From what I see this is an omission in the original implementation, I don't see any additional unsafety (except for the same that exists for instance methods/variables). I also incorporate a small refactoring and remove couple unused `get_proper_type()` calls. The fix uncovered an unrelated issue with unions in descriptors, so I fix that one as well. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checkexpr.py | 4 +- mypy/checkmember.py | 78 ++++++++++++++--------- test-data/unit/check-classes.test | 35 ++++++++++ test-data/unit/check-recursive-types.test | 2 +- test-data/unit/check-selftype.test | 61 ++++++++++++++++++ 5 files changed, 149 insertions(+), 31 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4fd1a308e560..1cea4f6c19e6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3261,7 +3261,9 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type if isinstance(base, RefExpr) and isinstance(base.node, MypyFile): module_symbol_table = base.node.names if isinstance(base, RefExpr) and isinstance(base.node, Var): - is_self = base.node.is_self + # This is needed to special case self-types, so we don't need to track + # these flags separately in checkmember.py. + is_self = base.node.is_self or base.node.is_cls else: is_self = False diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 7525db25d9cd..0f117f5475ed 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -638,7 +638,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: Return: The return type of the appropriate ``__get__`` overload for the descriptor. """ - instance_type = get_proper_type(mx.original_type) + instance_type = get_proper_type(mx.self_type) orig_descriptor_type = descriptor_type descriptor_type = get_proper_type(descriptor_type) @@ -647,16 +647,6 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: return make_simplified_union( [analyze_descriptor_access(typ, mx) for typ in descriptor_type.items] ) - elif isinstance(instance_type, UnionType): - # map over the instance types - return make_simplified_union( - [ - analyze_descriptor_access( - descriptor_type, mx.copy_modified(original_type=original_type) - ) - for original_type in instance_type.relevant_items() - ] - ) elif not isinstance(descriptor_type, Instance): return orig_descriptor_type @@ -777,23 +767,10 @@ def analyze_var( if mx.is_lvalue and var.is_classvar: mx.msg.cant_assign_to_classvar(name, mx.context) t = freshen_all_functions_type_vars(typ) - if not (mx.is_self or mx.is_super) or supported_self_type( - get_proper_type(mx.original_type) - ): - t = expand_self_type(var, t, mx.original_type) - elif ( - mx.is_self - and original_itype.type != var.info - # If an attribute with Self-type was defined in a supertype, we need to - # rebind the Self type variable to Self type variable of current class... - and original_itype.type.self_type is not None - # ...unless `self` has an explicit non-trivial annotation. - and original_itype == mx.chk.scope.active_self_type() - ): - t = expand_self_type(var, t, original_itype.type.self_type) - t = get_proper_type(expand_type_by_instance(t, itype)) + t = expand_self_type_if_needed(t, mx, var, original_itype) + t = expand_type_by_instance(t, itype) freeze_all_type_vars(t) - result: Type = t + result = t typ = get_proper_type(typ) call_type: ProperType | None = None @@ -857,6 +834,50 @@ def analyze_var( return result +def expand_self_type_if_needed( + t: Type, mx: MemberContext, var: Var, itype: Instance, is_class: bool = False +) -> Type: + """Expand special Self type in a backwards compatible manner. + + This should ensure that mixing old-style and new-style self-types work + seamlessly. Also, re-bind new style self-types in subclasses if needed. + """ + original = get_proper_type(mx.self_type) + if not (mx.is_self or mx.is_super): + repl = mx.self_type + if is_class: + if isinstance(original, TypeType): + repl = original.item + elif isinstance(original, CallableType): + # Problematic access errors should have been already reported. + repl = erase_typevars(original.ret_type) + else: + repl = itype + return expand_self_type(var, t, repl) + elif supported_self_type( + # Support compatibility with plain old style T -> T and Type[T] -> T only. + get_proper_type(mx.self_type), + allow_instances=False, + allow_callable=False, + ): + repl = mx.self_type + if is_class and isinstance(original, TypeType): + repl = original.item + return expand_self_type(var, t, repl) + elif ( + mx.is_self + and itype.type != var.info + # If an attribute with Self-type was defined in a supertype, we need to + # rebind the Self type variable to Self type variable of current class... + and itype.type.self_type is not None + # ...unless `self` has an explicit non-trivial annotation. + and itype == mx.chk.scope.active_self_type() + ): + return expand_self_type(var, t, itype.type.self_type) + else: + return t + + def freeze_all_type_vars(member_type: Type) -> None: member_type.accept(FreezeTypeVarsVisitor()) @@ -1059,12 +1080,11 @@ def analyze_class_attribute_access( else: message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS mx.msg.fail(message, mx.context) - + t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True) # Erase non-mapped variables, but keep mapped ones, even if there is an error. # In the above example this means that we infer following types: # C.x -> Any # C[int].x -> int - t = get_proper_type(expand_self_type(node.node, t, itype)) t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 983cb8454a05..f37b0dd1dc41 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -1950,6 +1950,41 @@ class B: def foo(x: Union[A, B]) -> None: reveal_type(x.attr) # N: Revealed type is "builtins.str" +[case testDescriptorGetUnionRestricted] +from typing import Any, Union + +class getter: + def __get__(self, instance: X1, owner: Any) -> str: ... + +class X1: + prop = getter() + +class X2: + prop: str + +def foo(x: Union[X1, X2]) -> None: + reveal_type(x.prop) # N: Revealed type is "builtins.str" + +[case testDescriptorGetUnionType] +from typing import Any, Union, Type, overload + +class getter: + @overload + def __get__(self, instance: None, owner: Any) -> getter: ... + @overload + def __get__(self, instance: object, owner: Any) -> str: ... + def __get__(self, instance, owner): + ... + +class X1: + prop = getter() +class X2: + prop = getter() + +def foo(x: Type[Union[X1, X2]]) -> None: + reveal_type(x.prop) # N: Revealed type is "__main__.getter" + + -- _promote decorators -- ------------------- diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 33cb9ccad9af..d5c8acd1bc15 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -440,7 +440,7 @@ from typing import NamedTuple, TypeVar, Tuple NT = NamedTuple("NT", [("x", NT), ("y", int)]) nt: NT reveal_type(nt) # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]" -reveal_type(nt.x) # N: Revealed type is "Tuple[Tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]" +reveal_type(nt.x) # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]" reveal_type(nt[0]) # N: Revealed type is "Tuple[Tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]" y: str if nt.x is not None: diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index e99b859bbcd0..fdd628b0271b 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2071,3 +2071,64 @@ p: Partial reveal_type(p()) # N: Revealed type is "Never" p2: Partial2 reveal_type(p2(42)) # N: Revealed type is "builtins.int" + +[case testAccessingSelfClassVarInClassMethod] +from typing import Self, ClassVar, Type, TypeVar + +T = TypeVar("T", bound="Foo") + +class Foo: + instance: ClassVar[Self] + @classmethod + def get_instance(cls) -> Self: + return reveal_type(cls.instance) # N: Revealed type is "Self`0" + @classmethod + def get_instance_old(cls: Type[T]) -> T: + return reveal_type(cls.instance) # N: Revealed type is "T`-1" + +class Bar(Foo): + extra: int + + @classmethod + def get_instance(cls) -> Self: + reveal_type(cls.instance.extra) # N: Revealed type is "builtins.int" + return cls.instance + + @classmethod + def other(cls) -> None: + reveal_type(cls.instance) # N: Revealed type is "Self`0" + reveal_type(cls.instance.extra) # N: Revealed type is "builtins.int" + +reveal_type(Bar.instance) # N: Revealed type is "__main__.Bar" +[builtins fixtures/classmethod.pyi] + +[case testAccessingSelfClassVarInClassMethodTuple] +from typing import Self, ClassVar, Tuple + +class C(Tuple[int, str]): + x: Self + y: ClassVar[Self] + + @classmethod + def bar(cls) -> None: + reveal_type(cls.y) # N: Revealed type is "Self`0" + @classmethod + def bar_self(self) -> Self: + return reveal_type(self.y) # N: Revealed type is "Self`0" + +c: C +reveal_type(c.x) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" +reveal_type(c.y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" +reveal_type(C.y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" +C.x # E: Access to generic instance variables via class is ambiguous +[builtins fixtures/classmethod.pyi] + +[case testAccessingTypingSelfUnion] +from typing import Self, Union + +class C: + x: Self +class D: + x: int +x: Union[C, D] +reveal_type(x.x) # N: Revealed type is "Union[__main__.C, builtins.int]" From 59b2df4865d435f98b56d0692c6542e8cc8425a1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 18 Jun 2024 10:48:49 +0100 Subject: [PATCH 0647/1617] [PEP 695] Add more tests (#17397) --- test-data/unit/check-python312.test | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index b3a3645dc9f8..348f2d11f9a7 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1543,3 +1543,51 @@ a: A reveal_type(a) # N: Revealed type is "builtins.list[Any]" b: B reveal_type(b) # N: Revealed type is "Any" + +[case testPEP695GenericNamedTuple] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import NamedTuple + +# Invariant because of the signature of the generated _replace method +class N[T](NamedTuple): + x: T + y: int + +a: N[object] +reveal_type(a.x) # N: Revealed type is "builtins.object" +b: N[int] +reveal_type(b.x) # N: Revealed type is "builtins.int" +if int(): + a = b # E: Incompatible types in assignment (expression has type "N[int]", variable has type "N[object]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "N[object]", variable has type "N[int]") + +class M[T: (int, str)](NamedTuple): + x: T + +c: M[int] +d: M[str] +e: M[bool] # E: Value of type variable "T" of "M" cannot be "bool" + +[builtins fixtures/tuple.pyi] + +[case testPEP695GenericTypedDict] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import TypedDict + +class D[T](TypedDict): + x: T + y: int + +class E[T: str](TypedDict): + x: T + y: int + +a: D[object] +reveal_type(a["x"]) # N: Revealed type is "builtins.object" +b: D[int] +reveal_type(b["x"]) # N: Revealed type is "builtins.int" +c: E[str] +d: E[int] # E: Type argument "int" of "E" must be a subtype of "str" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] From 10f18a82b612b6127659cd64aa60c10b9cc7a904 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 18 Jun 2024 12:34:19 +0100 Subject: [PATCH 0648/1617] Improve fixtures for builtins.type and types.UnionType (#17400) In typeshed `builtins.type` is not generic, so it shouldn't be generic in fixtures either. Also replace `types.Union` with `types.UnionType` in test stubs, as the former doesn't exist. --- test-data/unit/check-classes.test | 2 +- test-data/unit/fixtures/isinstance.pyi | 2 +- test-data/unit/fixtures/isinstance_python3_10.pyi | 6 +++--- test-data/unit/fixtures/type.pyi | 2 +- test-data/unit/lib-stub/types.pyi | 5 +---- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index f37b0dd1dc41..427133eca10b 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3466,7 +3466,7 @@ def foo(arg: Type[Any]): from typing import Type, Any def foo(arg: Type[Any]): reveal_type(arg.__str__) # N: Revealed type is "def () -> builtins.str" - reveal_type(arg.mro()) # N: Revealed type is "builtins.list[builtins.type[Any]]" + reveal_type(arg.mro()) # N: Revealed type is "builtins.list[builtins.type]" [builtins fixtures/type.pyi] [out] diff --git a/test-data/unit/fixtures/isinstance.pyi b/test-data/unit/fixtures/isinstance.pyi index c1446492af9b..12cef2035c2b 100644 --- a/test-data/unit/fixtures/isinstance.pyi +++ b/test-data/unit/fixtures/isinstance.pyi @@ -5,7 +5,7 @@ T = TypeVar('T') class object: def __init__(self) -> None: pass -class type(Generic[T]): +class type: def __init__(self, x) -> None: pass def __or__(self, other: type) -> type: pass diff --git a/test-data/unit/fixtures/isinstance_python3_10.pyi b/test-data/unit/fixtures/isinstance_python3_10.pyi index 7c919a216bfb..0918d10ab1ef 100644 --- a/test-data/unit/fixtures/isinstance_python3_10.pyi +++ b/test-data/unit/fixtures/isinstance_python3_10.pyi @@ -7,15 +7,15 @@ T = TypeVar('T') class object: def __init__(self) -> None: pass -class type(Generic[T]): +class type: def __init__(self, x) -> None: pass - def __or__(self, x) -> types.Union: pass + def __or__(self, x) -> types.UnionType: pass class tuple(Generic[T]): pass class function: pass -def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...], types.Union]) -> bool: pass +def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...], types.UnionType]) -> bool: pass def issubclass(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass class int: diff --git a/test-data/unit/fixtures/type.pyi b/test-data/unit/fixtures/type.pyi index 4ae8ed9ca6b1..0d93b2e1fcd6 100644 --- a/test-data/unit/fixtures/type.pyi +++ b/test-data/unit/fixtures/type.pyi @@ -13,7 +13,7 @@ class object: class list(Generic[T]): pass -class type(Generic[T]): +class type: __name__: str def __call__(self, *args: Any, **kwargs: Any) -> Any: pass def __or__(self, other: Union[type, None]) -> type: pass diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index dded0ba6cd9a..c3ac244c2a51 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -12,11 +12,8 @@ class ModuleType: class GenericAlias: ... if sys.version_info >= (3, 10): - class Union: - def __or__(self, x) -> Union: ... - class NoneType: ... class UnionType: - ... + def __or__(self, x) -> UnionType: ... From e1ff8aa30f291ec1613bc9893528067b269309bc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 19 Jun 2024 18:49:41 +0100 Subject: [PATCH 0649/1617] Consider overlap between instances and callables (#17389) Fixes https://github.com/python/mypy/issues/8869 The fix seems straightforward. --- mypy/meet.py | 18 ++++++++++++-- test-data/unit/check-statements.test | 35 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index 48e5dfaa18ee..401200a11cc1 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -7,6 +7,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.state import state from mypy.subtypes import ( + find_member, is_callable_compatible, is_equivalent, is_proper_subtype, @@ -477,9 +478,22 @@ def _type_object_overlap(left: Type, right: Type) -> bool: ignore_pos_arg_names=True, allow_partial_overlap=True, ) - elif isinstance(left, CallableType): + + call = None + other = None + if isinstance(left, CallableType) and isinstance(right, Instance): + call = find_member("__call__", right, right, is_operator=True) + other = left + if isinstance(right, CallableType) and isinstance(left, Instance): + call = find_member("__call__", left, left, is_operator=True) + other = right + if isinstance(get_proper_type(call), FunctionLike): + assert call is not None and other is not None + return _is_overlapping_types(call, other) + + if isinstance(left, CallableType): left = left.fallback - elif isinstance(right, CallableType): + if isinstance(right, CallableType): right = right.fallback if isinstance(left, LiteralType) and isinstance(right, LiteralType): diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 71cc80719779..34df5a8ab336 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2307,3 +2307,38 @@ class Outer: class Inner: break # E: "break" outside loop [builtins fixtures/list.pyi] + +[case testCallableInstanceOverlapAllowed] +# flags: --warn-unreachable +from typing import Any, Callable, List + +class CAny: + def __call__(self) -> Any: ... +class CNone: + def __call__(self) -> None: ... +class CWrong: + def __call__(self, x: int) -> None: ... + +def describe(func: Callable[[], None]) -> str: + if isinstance(func, CAny): + return "CAny" + elif isinstance(func, CNone): + return "CNone" + elif isinstance(func, CWrong): + return "CWrong" # E: Statement is unreachable + else: + return "other" + +class C(CAny): + def __call__(self) -> None: ... + +def f(): + pass + +describe(CAny()) +describe(C()) +describe(CNone()) +describe(CWrong()) # E: Argument 1 to "describe" has incompatible type "CWrong"; expected "Callable[[], None]" \ + # N: "CWrong.__call__" has type "Callable[[Arg(int, 'x')], None]" +describe(f) +[builtins fixtures/isinstancelist.pyi] From 7cb733ad42eccaccd29380d46d5c222ccc2788cb Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 19 Jun 2024 23:48:35 +0100 Subject: [PATCH 0650/1617] Re-work overload overlap logic (#17392) Fixes https://github.com/python/mypy/issues/5510 OK, so I noticed during last couple years, that every other time I change something about type variables, a few unsafe overload overlap errors either appears or disappears. At some point I almost stopped looking at them. The problem is that unsafe overload overlap detection for generic callables is currently ad-hoc. However, as I started working on it, I discovered a bunch of foundational problems (and few smaller issues), so I decided to re-work the unsafe overload overlap detection. Here is a detailed summary: * Currently return type compatibility is decided using regular subtype check. Although it is technically correct, in most cases there is nothing wrong if first overload returns `list[Subtype]` and second returns `list[Supertype]`. All the unsafe overload story is about runtime values, not static types, so we should use `is_subset()` instead of `is_subtype()`, which is IIUC easy to implement: we simply need to consider all invariant types covariant. * Current implementation only checks for overlap between parameters, i.e. it checks if there are some calls that are valid for both overloads. But we also need to check that those common calls will not be always caught by the first overload. I assume it was not checked because, naively, we already check elsewhere that first overload doesn't completely shadow the second one. But this is not the same: first overload may be not more general overall, but when narrowed to common calls, it may be more general. Example of such false-positive (this is an oversimplified version of what is often used in situations with many optional positional arguments): ```python @overload def foo(x: object) -> object: ... @overload def foo(x: int = ...) -> int: ... ``` * Currently overlap for generic callables is decided using some weird two-way unification procedure, where we actually keep going on (with non-unified variables, and/or ``) if the right to left unification fails. TBH I never understood this. What we need is to find some set of type variable values that makes two overloads unsafely overlapping. Constraint inference may be used as a (good) source of such guesses, but is not decisive in any way. So instead I simply try all combinations of upper bounds and values. The main benefit of such approach is that it is guaranteed false-positive free. If such algorithm finds an overlap it is definitely an overlap. There are however false negatives, but we can incrementally tighten them in the future. * I am making `Any` overlap nothing when considering overloads. Currently it overlaps everything (i.e. it is not different from `object`), but this violates the rule that replacing a precise type with `Any` should not generate an error. IOW I essentially treat `Any` as "too dynamic or not imported". * I extend `None` special-casing to be more uniform. Now essentially it only overlaps with explicitly optional types. This is important for descriptor-like signatures. * Finally, I did a cleanup in `is_overlapping_types()`, most notably flags were not passed down to various (recursive) helpers, and `ParamSpec`/`Parameters` were treated a bit arbitrary. Pros/cons of the outcome: * Pro: simple (even if not 100% accurate) mental model * Pro: all major classes of false positives eliminated * Pro: couple minor false negatives fixed * Con: two new false negatives added, more details below So here a two new false negatives and motivation on why I think they are OK. First example is ```python T = TypeVar("T") @overload def foo(x: str) -> int: ... @overload def foo(x: T) -> T: ... def foo(x): if isinstance(x, str): return 0 return x ``` This is obviously unsafe (consider `T = float`), but not flagged after this PR. I think this is ~fine for two reasons: * There is no good alternative for a user, the error is not very actionable. Using types like `(str | T) -> int | T` is a bad idea because unions with type variables are not only imprecise, but also highly problematic for inference. * The false negative is mostly affecting unbounded type variables, if a "suspicious" bound is used (like `bound=float` in this example), the error will be still reported. Second example is signatures like ```python @overload def foo(x: str, y: str) -> str: ... @overload def foo(*args: str) -> int: ... @overload def bar(*, x: str, y: str) -> str: ... @overload def bar(**kwds: str) -> int: ... ``` These are also unsafe because one can fool mypy with `x: tuple[str, ...] = ("x", "y"); foo(*x)` and `x: dict[str, str] = {"x": "x", "y": "y"}; bar(**x)`. I think this is OK because while such unsafe calls are quite rare, this kind of catch-all fallback as last overload is relatively common. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/checker.py | 195 +++++++++++------- mypy/constraints.py | 2 +- mypy/expandtype.py | 2 +- mypy/meet.py | 111 +++++----- mypy/messages.py | 10 +- mypy/semanal.py | 9 +- mypy/subtypes.py | 68 ++---- mypy/types.py | 9 +- mypy/typeshed/stdlib/builtins.pyi | 4 +- test-data/unit/check-async-await.test | 4 +- test-data/unit/check-classes.test | 18 +- test-data/unit/check-generics.test | 14 +- test-data/unit/check-overloading.test | 144 ++++++++----- .../unit/check-parameter-specification.test | 20 +- test-data/unit/check-selftype.test | 6 +- test-data/unit/pythoneval.test | 11 +- 16 files changed, 352 insertions(+), 275 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index bf739e7d1242..3a7f231ebf1d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -170,7 +170,6 @@ false_only, fixup_partial_type, function_type, - get_type_vars, is_literal_type_like, is_singleton_type, make_simplified_union, @@ -787,7 +786,16 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: type_vars = current_class.defn.type_vars if current_class else [] with state.strict_optional_set(True): if is_unsafe_overlapping_overload_signatures(sig1, sig2, type_vars): - self.msg.overloaded_signatures_overlap(i + 1, i + j + 2, item.func) + flip_note = ( + j == 0 + and not is_unsafe_overlapping_overload_signatures( + sig2, sig1, type_vars + ) + and not overload_can_never_match(sig2, sig1) + ) + self.msg.overloaded_signatures_overlap( + i + 1, i + j + 2, flip_note, item.func + ) if impl_type is not None: assert defn.impl is not None @@ -1764,6 +1772,8 @@ def is_unsafe_overlapping_op( # second operand is the right argument -- we switch the order of # the arguments of the reverse method. + # TODO: this manipulation is dangerous if callables are generic. + # Shuffling arguments between callables can create meaningless types. forward_tweaked = forward_item.copy_modified( arg_types=[forward_base_erased, forward_item.arg_types[0]], arg_kinds=[nodes.ARG_POS] * 2, @@ -1790,7 +1800,9 @@ def is_unsafe_overlapping_op( current_class = self.scope.active_class() type_vars = current_class.defn.type_vars if current_class else [] - return is_unsafe_overlapping_overload_signatures(first, second, type_vars) + return is_unsafe_overlapping_overload_signatures( + first, second, type_vars, partial_only=False + ) def check_inplace_operator_method(self, defn: FuncBase) -> None: """Check an inplace operator method such as __iadd__. @@ -2185,7 +2197,7 @@ def get_op_other_domain(self, tp: FunctionLike) -> Type | None: if isinstance(tp, CallableType): if tp.arg_kinds and tp.arg_kinds[0] == ARG_POS: # For generic methods, domain comparison is tricky, as a first - # approximation erase all remaining type variables to bounds. + # approximation erase all remaining type variables. return erase_typevars(tp.arg_types[0], {v.id for v in tp.variables}) return None elif isinstance(tp, Overloaded): @@ -7827,68 +7839,112 @@ def are_argument_counts_overlapping(t: CallableType, s: CallableType) -> bool: return min_args <= max_args +def expand_callable_variants(c: CallableType) -> list[CallableType]: + """Expand a generic callable using all combinations of type variables' values/bounds.""" + for tv in c.variables: + # We need to expand self-type before other variables, because this is the only + # type variable that can have other type variables in the upper bound. + if tv.id.is_self(): + c = expand_type(c, {tv.id: tv.upper_bound}).copy_modified( + variables=[v for v in c.variables if not v.id.is_self()] + ) + break + + if not c.is_generic(): + # Fast path. + return [c] + + tvar_values = [] + for tvar in c.variables: + if isinstance(tvar, TypeVarType) and tvar.values: + tvar_values.append(tvar.values) + else: + tvar_values.append([tvar.upper_bound]) + + variants = [] + for combination in itertools.product(*tvar_values): + tvar_map = {tv.id: subst for (tv, subst) in zip(c.variables, combination)} + variants.append(expand_type(c, tvar_map).copy_modified(variables=[])) + return variants + + def is_unsafe_overlapping_overload_signatures( - signature: CallableType, other: CallableType, class_type_vars: list[TypeVarLikeType] + signature: CallableType, + other: CallableType, + class_type_vars: list[TypeVarLikeType], + partial_only: bool = True, ) -> bool: """Check if two overloaded signatures are unsafely overlapping or partially overlapping. - We consider two functions 's' and 't' to be unsafely overlapping if both - of the following are true: + We consider two functions 's' and 't' to be unsafely overlapping if three + conditions hold: + + 1. s's parameters are partially overlapping with t's. i.e. there are calls that are + valid for both signatures. + 2. for these common calls, some of t's parameters types are wider that s's. + 3. s's return type is NOT a subset of t's. - 1. s's parameters are all more precise or partially overlapping with t's - 2. s's return type is NOT a subtype of t's. + Note that we use subset rather than subtype relationship in these checks because: + * Overload selection happens at runtime, not statically. + * This results in more lenient behavior. + This can cause false negatives (e.g. if overloaded function returns an externally + visible attribute with invariant type), but such situations are rare. In general, + overloads in Python are generally unsafe, so we intentionally try to avoid giving + non-actionable errors (see more details in comments below). Assumes that 'signature' appears earlier in the list of overload alternatives then 'other' and that their argument counts are overlapping. """ # Try detaching callables from the containing class so that all TypeVars - # are treated as being free. - # - # This lets us identify cases where the two signatures use completely - # incompatible types -- e.g. see the testOverloadingInferUnionReturnWithMixedTypevars - # test case. + # are treated as being free, i.e. the signature is as seen from inside the class, + # where "self" is not yet bound to anything. signature = detach_callable(signature, class_type_vars) other = detach_callable(other, class_type_vars) - # Note: We repeat this check twice in both directions due to a slight - # asymmetry in 'is_callable_compatible'. When checking for partial overlaps, - # we attempt to unify 'signature' and 'other' both against each other. - # - # If 'signature' cannot be unified with 'other', we end early. However, - # if 'other' cannot be modified with 'signature', the function continues - # using the older version of 'other'. - # - # This discrepancy is unfortunately difficult to get rid of, so we repeat the - # checks twice in both directions for now. - # - # Note that we ignore possible overlap between type variables and None. This - # is technically unsafe, but unsafety is tiny and this prevents some common - # use cases like: - # @overload - # def foo(x: None) -> None: .. - # @overload - # def foo(x: T) -> Foo[T]: ... - return is_callable_compatible( - signature, - other, - is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, - is_proper_subtype=False, - is_compat_return=lambda l, r: not is_subtype_no_promote(l, r), - ignore_return=False, - check_args_covariantly=True, - allow_partial_overlap=True, - no_unify_none=True, - ) or is_callable_compatible( - other, - signature, - is_compat=is_overlapping_types_no_promote_no_uninhabited_no_none, - is_proper_subtype=False, - is_compat_return=lambda l, r: not is_subtype_no_promote(r, l), - ignore_return=False, - check_args_covariantly=False, - allow_partial_overlap=True, - no_unify_none=True, - ) + # Note: We repeat this check twice in both directions compensate for slight + # asymmetries in 'is_callable_compatible'. + + for sig_variant in expand_callable_variants(signature): + for other_variant in expand_callable_variants(other): + # Using only expanded callables may cause false negatives, we can add + # more variants (e.g. using inference between callables) in the future. + if is_subset_no_promote(sig_variant.ret_type, other_variant.ret_type): + continue + if not ( + is_callable_compatible( + sig_variant, + other_variant, + is_compat=is_overlapping_types_for_overload, + check_args_covariantly=False, + is_proper_subtype=False, + is_compat_return=lambda l, r: not is_subset_no_promote(l, r), + allow_partial_overlap=True, + ) + or is_callable_compatible( + other_variant, + sig_variant, + is_compat=is_overlapping_types_for_overload, + check_args_covariantly=True, + is_proper_subtype=False, + is_compat_return=lambda l, r: not is_subset_no_promote(r, l), + allow_partial_overlap=True, + ) + ): + continue + # Using the same `allow_partial_overlap` flag as before, can cause false + # negatives in case where star argument is used in a catch-all fallback overload. + # But again, practicality beats purity here. + if not partial_only or not is_callable_compatible( + other_variant, + sig_variant, + is_compat=is_subset_no_promote, + check_args_covariantly=True, + is_proper_subtype=False, + ignore_return=True, + allow_partial_overlap=True, + ): + return True + return False def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) -> CallableType: @@ -7897,21 +7953,11 @@ def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) - A callable normally keeps track of the type variables it uses within its 'variables' field. However, if the callable is from a method and that method is using a class type variable, the callable will not keep track of that type variable since it belongs to the class. - - This function will traverse the callable and find all used type vars and add them to the - variables field if it isn't already present. - - The caller can then unify on all type variables whether the callable is originally from - the class or not.""" + """ if not class_type_vars: # Fast path, nothing to update. return typ - seen_type_vars = set() - for t in typ.arg_types + [typ.ret_type]: - seen_type_vars |= set(get_type_vars(t)) - return typ.copy_modified( - variables=list(typ.variables) + [tv for tv in class_type_vars if tv in seen_type_vars] - ) + return typ.copy_modified(variables=list(typ.variables) + class_type_vars) def overload_can_never_match(signature: CallableType, other: CallableType) -> bool: @@ -8388,21 +8434,24 @@ def get_property_type(t: ProperType) -> ProperType: return t -def is_subtype_no_promote(left: Type, right: Type) -> bool: - return is_subtype(left, right, ignore_promotions=True) +def is_subset_no_promote(left: Type, right: Type) -> bool: + return is_subtype(left, right, ignore_promotions=True, always_covariant=True) -def is_overlapping_types_no_promote_no_uninhabited_no_none(left: Type, right: Type) -> bool: - # For the purpose of unsafe overload checks we consider list[Never] and list[int] - # non-overlapping. This is consistent with how we treat list[int] and list[str] as - # non-overlapping, despite [] belongs to both. Also this will prevent false positives - # for failed type inference during unification. +def is_overlapping_types_for_overload(left: Type, right: Type) -> bool: + # Note that among other effects 'overlap_for_overloads' flag will effectively + # ignore possible overlap between type variables and None. This is technically + # unsafe, but unsafety is tiny and this prevents some common use cases like: + # @overload + # def foo(x: None) -> None: .. + # @overload + # def foo(x: T) -> Foo[T]: ... return is_overlapping_types( left, right, ignore_promotions=True, - ignore_uninhabited=True, prohibit_none_typevar_overlap=True, + overlap_for_overloads=True, ) diff --git a/mypy/constraints.py b/mypy/constraints.py index 56ca51d19486..316f481ac870 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1055,7 +1055,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # like U -> U, should be Callable[..., Any], but if U is a self-type, we can # allow it to leak, to be later bound to self. A bunch of existing code # depends on this old behaviour. - and not any(tv.id.raw_id == 0 for tv in cactual.variables) + and not any(tv.id.is_self() for tv in cactual.variables) ): # If the actual callable is generic, infer constraints in the opposite # direction, and indicate to the solver there are extra type variables diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 86875bc6079a..bff23c53defd 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -221,7 +221,7 @@ def visit_instance(self, t: Instance) -> Type: def visit_type_var(self, t: TypeVarType) -> Type: # Normally upper bounds can't contain other type variables, the only exception is # special type variable Self`0 <: C[T, S], where C is the class where Self is used. - if t.id.raw_id == 0: + if t.id.is_self(): t = t.copy_modified(upper_bound=t.upper_bound.accept(self)) repl = self.variables.get(t.id, t) if isinstance(repl, ProperType) and isinstance(repl, Instance): diff --git a/mypy/meet.py b/mypy/meet.py index 401200a11cc1..91abf43c0877 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -7,6 +7,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.state import state from mypy.subtypes import ( + are_parameters_compatible, find_member, is_callable_compatible, is_equivalent, @@ -257,12 +258,16 @@ def is_literal_in_union(x: ProperType, y: ProperType) -> bool: ) +def is_object(t: ProperType) -> bool: + return isinstance(t, Instance) and t.type.fullname == "builtins.object" + + def is_overlapping_types( left: Type, right: Type, ignore_promotions: bool = False, prohibit_none_typevar_overlap: bool = False, - ignore_uninhabited: bool = False, + overlap_for_overloads: bool = False, seen_types: set[tuple[Type, Type]] | None = None, ) -> bool: """Can a value of type 'left' also be of type 'right' or vice-versa? @@ -270,6 +275,9 @@ def is_overlapping_types( If 'ignore_promotions' is True, we ignore promotions while checking for overlaps. If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with TypeVars (in both strict-optional and non-strict-optional mode). + If 'overlap_for_overloads' is True, we check for overlaps more strictly (to avoid false + positives), for example: None only overlaps with explicitly optional types, Any + doesn't overlap with anything except object, we don't ignore positional argument names. """ if isinstance(left, TypeGuardedType) or isinstance( # type: ignore[misc] right, TypeGuardedType @@ -296,7 +304,7 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: right, ignore_promotions=ignore_promotions, prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, - ignore_uninhabited=ignore_uninhabited, + overlap_for_overloads=overlap_for_overloads, seen_types=seen_types.copy(), ) @@ -325,7 +333,7 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: # 'Any' may or may not be overlapping with the other type if isinstance(left, AnyType) or isinstance(right, AnyType): - return True + return not overlap_for_overloads or is_object(left) or is_object(right) # We check for complete overlaps next as a general-purpose failsafe. # If this check fails, we start checking to see if there exists a @@ -345,11 +353,25 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: ): return True - if is_proper_subtype( - left, right, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited - ) or is_proper_subtype( - right, left, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited - ): + def is_none_object_overlap(t1: Type, t2: Type) -> bool: + t1, t2 = get_proper_types((t1, t2)) + return ( + isinstance(t1, NoneType) + and isinstance(t2, Instance) + and t2.type.fullname == "builtins.object" + ) + + if overlap_for_overloads: + if is_none_object_overlap(left, right) or is_none_object_overlap(right, left): + return False + + def _is_subtype(left: Type, right: Type) -> bool: + if overlap_for_overloads: + return is_proper_subtype(left, right, ignore_promotions=ignore_promotions) + else: + return is_subtype(left, right, ignore_promotions=ignore_promotions) + + if _is_subtype(left, right) or _is_subtype(right, left): return True # See the docstring for 'get_possible_variants' for more info on what the @@ -358,21 +380,6 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: left_possible = get_possible_variants(left) right_possible = get_possible_variants(right) - # First handle special cases relating to PEP 612: - # - comparing a `Parameters` to a `Parameters` - # - comparing a `Parameters` to a `ParamSpecType` - # - comparing a `ParamSpecType` to a `ParamSpecType` - # - # These should all always be considered overlapping equality checks. - # These need to be done before we move on to other TypeVarLike comparisons. - if isinstance(left, (Parameters, ParamSpecType)) and isinstance( - right, (Parameters, ParamSpecType) - ): - return True - # A `Parameters` does not overlap with anything else, however - if isinstance(left, Parameters) or isinstance(right, Parameters): - return False - # Now move on to checking multi-variant types like Unions. We also perform # the same logic if either type happens to be a TypeVar/ParamSpec/TypeVarTuple. # @@ -422,7 +429,7 @@ def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool: # into their 'Instance' fallbacks. if isinstance(left, TypedDictType) and isinstance(right, TypedDictType): - return are_typed_dicts_overlapping(left, right, ignore_promotions=ignore_promotions) + return are_typed_dicts_overlapping(left, right, _is_overlapping_types) elif typed_dict_mapping_pair(left, right): # Overlaps between TypedDicts and Mappings require dedicated logic. return typed_dict_mapping_overlap(left, right, overlapping=_is_overlapping_types) @@ -432,7 +439,7 @@ def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool: right = right.fallback if is_tuple(left) and is_tuple(right): - return are_tuples_overlapping(left, right, ignore_promotions=ignore_promotions) + return are_tuples_overlapping(left, right, _is_overlapping_types) elif isinstance(left, TupleType): left = tuple_fallback(left) elif isinstance(right, TupleType): @@ -469,13 +476,26 @@ def _type_object_overlap(left: Type, right: Type) -> bool: if isinstance(left, TypeType) or isinstance(right, TypeType): return _type_object_overlap(left, right) or _type_object_overlap(right, left) + if isinstance(left, Parameters) and isinstance(right, Parameters): + return are_parameters_compatible( + left, + right, + is_compat=_is_overlapping_types, + is_proper_subtype=False, + ignore_pos_arg_names=not overlap_for_overloads, + allow_partial_overlap=True, + ) + # A `Parameters` does not overlap with anything else, however + if isinstance(left, Parameters) or isinstance(right, Parameters): + return False + if isinstance(left, CallableType) and isinstance(right, CallableType): return is_callable_compatible( left, right, is_compat=_is_overlapping_types, is_proper_subtype=False, - ignore_pos_arg_names=True, + ignore_pos_arg_names=not overlap_for_overloads, allow_partial_overlap=True, ) @@ -514,11 +534,7 @@ def _type_object_overlap(left: Type, right: Type) -> bool: if isinstance(left, Instance) and isinstance(right, Instance): # First we need to handle promotions and structural compatibility for instances # that came as fallbacks, so simply call is_subtype() to avoid code duplication. - if is_subtype( - left, right, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited - ) or is_subtype( - right, left, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited - ): + if _is_subtype(left, right) or _is_subtype(right, left): return True if right.type.fullname == "builtins.int" and left.type.fullname in MYPYC_NATIVE_INT_NAMES: @@ -578,32 +594,21 @@ def is_overlapping_erased_types( def are_typed_dicts_overlapping( - left: TypedDictType, - right: TypedDictType, - *, - ignore_promotions: bool = False, - prohibit_none_typevar_overlap: bool = False, + left: TypedDictType, right: TypedDictType, is_overlapping: Callable[[Type, Type], bool] ) -> bool: """Returns 'true' if left and right are overlapping TypeDictTypes.""" # All required keys in left are present and overlapping with something in right for key in left.required_keys: if key not in right.items: return False - if not is_overlapping_types( - left.items[key], - right.items[key], - ignore_promotions=ignore_promotions, - prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, - ): + if not is_overlapping(left.items[key], right.items[key]): return False # Repeat check in the other direction for key in right.required_keys: if key not in left.items: return False - if not is_overlapping_types( - left.items[key], right.items[key], ignore_promotions=ignore_promotions - ): + if not is_overlapping(left.items[key], right.items[key]): return False # The presence of any additional optional keys does not affect whether the two @@ -613,11 +618,7 @@ def are_typed_dicts_overlapping( def are_tuples_overlapping( - left: Type, - right: Type, - *, - ignore_promotions: bool = False, - prohibit_none_typevar_overlap: bool = False, + left: Type, right: Type, is_overlapping: Callable[[Type, Type], bool] ) -> bool: """Returns true if left and right are overlapping tuples.""" left, right = get_proper_types((left, right)) @@ -640,15 +641,7 @@ def are_tuples_overlapping( if len(left.items) != len(right.items): return False - return all( - is_overlapping_types( - l, - r, - ignore_promotions=ignore_promotions, - prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, - ) - for l, r in zip(left.items, right.items) - ) + return all(is_overlapping(l, r) for l, r in zip(left.items, right.items)) def expand_tuple_if_possible(tup: TupleType, target: int) -> TupleType: diff --git a/mypy/messages.py b/mypy/messages.py index f01b0a726584..c3a34bd41aba 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1624,13 +1624,21 @@ def overload_inconsistently_applies_decorator(self, decorator: str, context: Con context, ) - def overloaded_signatures_overlap(self, index1: int, index2: int, context: Context) -> None: + def overloaded_signatures_overlap( + self, index1: int, index2: int, flip_note: bool, context: Context + ) -> None: self.fail( "Overloaded function signatures {} and {} overlap with " "incompatible return types".format(index1, index2), context, code=codes.OVERLOAD_OVERLAP, ) + if flip_note: + self.note( + "Flipping the order of overloads will fix this error", + context, + code=codes.OVERLOAD_OVERLAP, + ) def overloaded_signature_will_never_match( self, index1: int, index2: int, context: Context diff --git a/mypy/semanal.py b/mypy/semanal.py index 03e6172bb325..c7a22d20aac6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1739,10 +1739,14 @@ def analyze_type_param( fullname = self.qualified_name(type_param.name) if type_param.upper_bound: upper_bound = self.anal_type(type_param.upper_bound) + # TODO: we should validate the upper bound is valid for a given kind. if upper_bound is None: return None else: - upper_bound = self.named_type("builtins.object") + if type_param.kind == TYPE_VAR_TUPLE_KIND: + upper_bound = self.named_type("builtins.tuple", [self.object_type()]) + else: + upper_bound = self.object_type() default = AnyType(TypeOfAny.from_omitted_generics) if type_param.kind == TYPE_VAR_KIND: values = [] @@ -1777,8 +1781,7 @@ def analyze_type_param( return TypeVarTupleExpr( name=type_param.name, fullname=fullname, - # Upper bound for *Ts is *tuple[object, ...], it can never be object. - upper_bound=tuple_fallback.copy_modified(), + upper_bound=upper_bound, tuple_fallback=tuple_fallback, default=default, is_new_style=True, diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a5d1d5d8194a..649cbae4c831 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -92,8 +92,8 @@ def __init__( ignore_pos_arg_names: bool = False, ignore_declared_variance: bool = False, # Supported for both proper and non-proper + always_covariant: bool = False, ignore_promotions: bool = False, - ignore_uninhabited: bool = False, # Proper subtype flags erase_instances: bool = False, keep_erased_types: bool = False, @@ -102,8 +102,8 @@ def __init__( self.ignore_type_params = ignore_type_params self.ignore_pos_arg_names = ignore_pos_arg_names self.ignore_declared_variance = ignore_declared_variance + self.always_covariant = always_covariant self.ignore_promotions = ignore_promotions - self.ignore_uninhabited = ignore_uninhabited self.erase_instances = erase_instances self.keep_erased_types = keep_erased_types self.options = options @@ -125,8 +125,8 @@ def is_subtype( ignore_type_params: bool = False, ignore_pos_arg_names: bool = False, ignore_declared_variance: bool = False, + always_covariant: bool = False, ignore_promotions: bool = False, - ignore_uninhabited: bool = False, options: Options | None = None, ) -> bool: """Is 'left' subtype of 'right'? @@ -145,8 +145,8 @@ def is_subtype( ignore_type_params=ignore_type_params, ignore_pos_arg_names=ignore_pos_arg_names, ignore_declared_variance=ignore_declared_variance, + always_covariant=always_covariant, ignore_promotions=ignore_promotions, - ignore_uninhabited=ignore_uninhabited, options=options, ) else: @@ -155,8 +155,8 @@ def is_subtype( ignore_type_params, ignore_pos_arg_names, ignore_declared_variance, + always_covariant, ignore_promotions, - ignore_uninhabited, options, } ), "Don't pass both context and individual flags" @@ -191,7 +191,6 @@ def is_proper_subtype( *, subtype_context: SubtypeContext | None = None, ignore_promotions: bool = False, - ignore_uninhabited: bool = False, erase_instances: bool = False, keep_erased_types: bool = False, ) -> bool: @@ -207,19 +206,12 @@ def is_proper_subtype( if subtype_context is None: subtype_context = SubtypeContext( ignore_promotions=ignore_promotions, - ignore_uninhabited=ignore_uninhabited, erase_instances=erase_instances, keep_erased_types=keep_erased_types, ) else: assert not any( - { - ignore_promotions, - ignore_uninhabited, - erase_instances, - keep_erased_types, - ignore_uninhabited, - } + {ignore_promotions, erase_instances, keep_erased_types} ), "Don't pass both context and individual flags" if type_state.is_assumed_proper_subtype(left, right): return True @@ -409,6 +401,7 @@ def build_subtype_kind(subtype_context: SubtypeContext, proper_subtype: bool) -> subtype_context.ignore_type_params, subtype_context.ignore_pos_arg_names, subtype_context.ignore_declared_variance, + subtype_context.always_covariant, subtype_context.ignore_promotions, subtype_context.erase_instances, subtype_context.keep_erased_types, @@ -447,11 +440,7 @@ def visit_none_type(self, left: NoneType) -> bool: return True def visit_uninhabited_type(self, left: UninhabitedType) -> bool: - # We ignore this for unsafe overload checks, so that and empty list and - # a list of int will be considered non-overlapping. - if isinstance(self.right, UninhabitedType): - return True - return not self.subtype_context.ignore_uninhabited + return True def visit_erased_type(self, left: ErasedType) -> bool: # This may be encountered during type inference. The result probably doesn't @@ -590,12 +579,15 @@ def visit_instance(self, left: Instance) -> bool: if tvar.variance == VARIANCE_NOT_READY and not tried_infer: infer_class_variances(right.type) tried_infer = True + if ( + self.subtype_context.always_covariant + and tvar.variance == INVARIANT + ): + variance = COVARIANT + else: + variance = tvar.variance if not check_type_parameter( - lefta, - righta, - tvar.variance, - self.proper_subtype, - self.subtype_context, + lefta, righta, variance, self.proper_subtype, self.subtype_context ): nominal = False else: @@ -687,6 +679,8 @@ def visit_parameters(self, left: Parameters) -> bool: is_proper_subtype=False, ignore_pos_arg_names=self.subtype_context.ignore_pos_arg_names, ) + elif isinstance(self.right, Instance): + return self.right.type.fullname == "builtins.object" else: return False @@ -1417,7 +1411,6 @@ def is_callable_compatible( check_args_covariantly: bool = False, allow_partial_overlap: bool = False, strict_concatenate: bool = False, - no_unify_none: bool = False, ) -> bool: """Is the left compatible with the right, using the provided compatibility check? @@ -1438,7 +1431,7 @@ def is_callable_compatible( configurable. For example, when checking the validity of overloads, it's useful to see if - the first overload alternative has more precise arguments then the second. + the first overload alternative has more precise arguments than the second. We would want to check the arguments covariantly in that case. Note! The following two function calls are NOT equivalent: @@ -1534,26 +1527,11 @@ def g(x: int) -> int: ... # (below) treats type variables on the two sides as independent. if left.variables: # Apply generic type variables away in left via type inference. - unified = unify_generic_callable( - left, right, ignore_return=ignore_return, no_unify_none=no_unify_none - ) + unified = unify_generic_callable(left, right, ignore_return=ignore_return) if unified is None: return False left = unified - # If we allow partial overlaps, we don't need to leave R generic: - # if we can find even just a single typevar assignment which - # would make these callables compatible, we should return True. - - # So, we repeat the above checks in the opposite direction. This also - # lets us preserve the 'symmetry' property of allow_partial_overlap. - if allow_partial_overlap and right.variables: - unified = unify_generic_callable( - right, left, ignore_return=ignore_return, no_unify_none=no_unify_none - ) - if unified is not None: - right = unified - # Check return types. if not ignore_return and not is_compat_return(left.ret_type, right.ret_type): return False @@ -1856,8 +1834,6 @@ def unify_generic_callable( target: NormalizedCallableType, ignore_return: bool, return_constraint_direction: int | None = None, - *, - no_unify_none: bool = False, ) -> NormalizedCallableType | None: """Try to unify a generic callable type with another callable type. @@ -1888,10 +1864,6 @@ def unify_generic_callable( type.ret_type, target.ret_type, return_constraint_direction ) constraints.extend(c) - if no_unify_none: - constraints = [ - c for c in constraints if not isinstance(get_proper_type(c.target), NoneType) - ] inferred_vars, _ = mypy.solve.solve_constraints( type.variables, constraints, allow_polymorphic=True ) diff --git a/mypy/types.py b/mypy/types.py index 0f8c48c8cb7d..3f764a5cc49e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -501,7 +501,7 @@ class TypeVarId: # function type variables. # Metavariables are allocated unique ids starting from 1. - raw_id: int = 0 + raw_id: int # Level of the variable in type inference. Currently either 0 for # declared types, or 1 for type inference metavariables. @@ -545,6 +545,10 @@ def __hash__(self) -> int: def is_meta_var(self) -> bool: return self.meta_level > 0 + def is_self(self) -> bool: + # This is a special value indicating typing.Self variable. + return self.raw_id == 0 + class TypeVarLikeType(ProperType): __slots__ = ("name", "fullname", "id", "upper_bound", "default") @@ -3095,8 +3099,7 @@ def get_proper_type(typ: Type | None) -> ProperType | None: @overload -def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[overload-overlap] - ... +def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: ... @overload diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 4a6c4bbcae45..42c0b27baf68 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1572,9 +1572,9 @@ def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> @overload def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> complex: ... @overload -def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... +def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload -def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... +def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... @overload diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 713c82c752ce..876fe0c6be15 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -765,7 +765,8 @@ class Task(Future[T]): @overload def wait(fs: Iterable[FT]) -> Future[Tuple[List[FT], List[FT]]]: ... \ - # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def wait(fs: Iterable[Awaitable[T]]) -> Future[Tuple[List[Task[T]], List[Task[T]]]]: ... def wait(fs: Any) -> Any: @@ -789,6 +790,7 @@ async def precise2(futures: Iterable[Awaitable[int]]) -> None: done, pending = await wait(futures) reveal_type(done) # N: Revealed type is "builtins.list[__main__.Task[builtins.int]]" + [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 427133eca10b..e66eab5e2927 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2427,10 +2427,10 @@ class B: [builtins fixtures/tuple.pyi] [case testReverseOperatorTypeVar1] -from typing import TypeVar, Any +from typing import TypeVar T = TypeVar("T", bound='Real') class Real: - def __add__(self, other: Any) -> str: ... + def __add__(self, other: object) -> str: ... class Fraction(Real): def __radd__(self, other: T) -> T: ... # E: Signatures of "__radd__" of "Fraction" and "__add__" of "T" are unsafely overlapping @@ -2465,7 +2465,7 @@ reveal_type(Real() + Fraction()) # N: Revealed type is "__main__.Real" reveal_type(Fraction() + Fraction()) # N: Revealed type is "builtins.str" [case testReverseOperatorTypeVar3] -from typing import TypeVar, Any +from typing import TypeVar T = TypeVar("T", bound='Real') class Real: def __add__(self, other: FractionChild) -> str: ... @@ -2701,14 +2701,12 @@ class X: [out] tmp/foo.pyi:6: error: Signatures of "__radd__" of "B" and "__add__" of "X" are unsafely overlapping -[case testUnsafeOverlappingWithLineNo] +[case testUnsafeOverlappingNotWithAny] from typing import TypeVar class Real: def __add__(self, other) -> str: ... class Fraction(Real): def __radd__(self, other: Real) -> Real: ... -[out] -main:5: error: Signatures of "__radd__" of "Fraction" and "__add__" of "Real" are unsafely overlapping [case testOverlappingNormalAndInplaceOperatorMethod] import typing @@ -4042,10 +4040,16 @@ def f(a: Type[User]) -> int: pass # E: Overloaded function signatures 1 and 2 o @overload def f(a: object) -> str: pass +# Note: plain type is equivalent to Type[Any] so no error here @overload -def g(a: Type[User]) -> int: pass # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def g(a: Type[User]) -> int: pass @overload def g(a: type) -> str: pass + +@overload +def h(a: Type[User]) -> int: pass # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +@overload +def h(a: Type[object]) -> str: pass [builtins fixtures/classmethod.pyi] [out] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index abcb2a4bbc48..d46d19946098 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3028,7 +3028,7 @@ def dec(f: Callable[[T], S], g: Callable[[T], U]) -> Callable[[T], Tuple[S, U]]: def id(x: V) -> V: ... -reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`7) -> Tuple[T`7, T`7]" +reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericEllipsisSelfSpecialCase] @@ -3264,8 +3264,8 @@ def transform( def dec(f: Callable[W, U]) -> Callable[W, U]: ... def dec2(f: Callable[Concatenate[str, W], U]) -> Callable[Concatenate[bytes, W], U]: ... -reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`9) -> def (builtins.int, *P.args, **P.kwargs) -> T`9" -reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`13) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`13" +reveal_type(transform(dec)) # N: Revealed type is "def [P, T] (def (builtins.int, *P.args, **P.kwargs) -> T`3) -> def (builtins.int, *P.args, **P.kwargs) -> T`3" +reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.int, builtins.str, *W.args, **W.kwargs) -> T`7) -> def (builtins.int, builtins.bytes, *W.args, **W.kwargs) -> T`7" [builtins fixtures/tuple.pyi] [case testNoAccidentalVariableClashInNestedGeneric] @@ -3319,8 +3319,8 @@ def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (T`9) -> builtins.list[T`9]" -reveal_type(dec(either)) # N: Revealed type is "def [T] (T`11, T`11) -> builtins.list[T`11]" +reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" +reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> builtins.list[T`5]" reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" [builtins fixtures/tuple.pyi] @@ -3338,8 +3338,8 @@ V = TypeVar("V") def id(x: U) -> U: ... def either(x: U, y: U) -> U: ... -reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`9]) -> T`9" -reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`11], builtins.list[T`11]) -> T`11" +reveal_type(dec(id)) # N: Revealed type is "def [T] (builtins.list[T`3]) -> T`3" +reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`5], builtins.list[T`5]) -> T`5" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicPopOff] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index bcb775ba5dac..03863b2978ba 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -373,7 +373,8 @@ def foo(t, s): pass class Wrapper(Generic[T]): @overload - def foo(self, t: List[T], s: T) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def foo(self, t: List[T], s: T) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def foo(self, t: T, s: T) -> str: ... def foo(self, t, s): pass @@ -384,7 +385,8 @@ class Dummy(Generic[T]): pass # cause the constraint solver to not infer T = object like it did in the # first example? @overload -def bar(d: Dummy[T], t: List[T], s: T) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def bar(d: Dummy[T], t: List[T], s: T) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def bar(d: Dummy[T], t: T, s: T) -> str: ... def bar(d: Dummy[T], t, s): pass @@ -1325,8 +1327,9 @@ def h(x: Sequence[str]) -> int: pass @overload def h(x: Sequence[T]) -> None: pass # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader +# Safety of this highly depends on the implementation, so we lean towards being silent. @overload -def i(x: List[str]) -> int: pass # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def i(x: List[str]) -> int: pass @overload def i(x: List[T]) -> None: pass [builtins fixtures/list.pyi] @@ -1752,14 +1755,11 @@ reveal_type(f(d)) # N: Revealed type is "builtins.list[builtins.int]" from typing import overload, Any @overload -def f(*, x: int = 3, y: int = 3) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(*, x: int = 3, y: int = 3) -> int: ... @overload def f(**kwargs: str) -> str: ... def f(*args, **kwargs): pass -# Checking an overload flagged as unsafe is a bit weird, but this is the -# cleanest way to make sure 'Any' ambiguity checks work correctly with -# keyword arguments. a: Any i: int reveal_type(f(x=a, y=i)) # N: Revealed type is "builtins.int" @@ -2163,8 +2163,9 @@ from wrapper import * [file wrapper.pyi] from typing import overload +# Safety of this highly depends on the implementation, so we lean towards being silent. @overload -def foo1(*x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(*x: int) -> int: ... @overload def foo1(x: int, y: int, z: int) -> str: ... @@ -2173,8 +2174,9 @@ def foo2(*x: int) -> int: ... @overload def foo2(x: int, y: str, z: int) -> str: ... +# Note: this is technically unsafe, but we don't report this for now. @overload -def bar1(x: int, y: int, z: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def bar1(x: int, y: int, z: int) -> str: ... @overload def bar1(*x: int) -> int: ... @@ -2248,7 +2250,7 @@ from wrapper import * from typing import overload @overload -def foo1(x: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(x: str) -> str: ... @overload def foo1(x: str, y: str = ...) -> int: ... @@ -2268,12 +2270,12 @@ from wrapper import * from typing import overload @overload -def foo1(*args: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(*args: int) -> int: ... @overload def foo1(**kwargs: int) -> str: ... @overload -def foo2(**kwargs: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo2(**kwargs: int) -> str: ... @overload def foo2(*args: int) -> int: ... [builtins fixtures/dict.pyi] @@ -2314,13 +2316,14 @@ def foo2(x: int, *args: int) -> str: ... @overload def foo2(*args2: str) -> int: ... +# The two examples are unsafe, but this is hard to detect. @overload -def foo3(*args: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo3(*args: int) -> int: ... @overload def foo3(x: int, *args2: int) -> str: ... @overload -def foo4(x: int, *args: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo4(x: int, *args: int) -> str: ... @overload def foo4(*args2: int) -> int: ... [builtins fixtures/tuple.pyi] @@ -2357,13 +2360,13 @@ def foo4(x: Other = ..., *args: str) -> int: ... from typing import overload @overload -def foo1(x: int = 0, y: int = 0) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(x: int = 0, y: int = 0) -> int: ... @overload def foo1(*xs: int) -> str: ... def foo1(*args): pass @overload -def foo2(*xs: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo2(*xs: int) -> str: ... @overload def foo2(x: int = 0, y: int = 0) -> int: ... def foo2(*args): pass @@ -2412,12 +2415,12 @@ from wrapper import * from typing import overload @overload -def foo1(x: str, y: str = ..., z: str = ...) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(x: str, y: str = ..., z: str = ...) -> str: ... @overload def foo1(*x: str) -> int: ... @overload -def foo2(*x: str) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo2(*x: str) -> int: ... @overload def foo2(x: str, y: str = ..., z: str = ...) -> str: ... @@ -2433,12 +2436,12 @@ from wrapper import * from typing import overload @overload -def foo1(x: str, y: str = ..., z: int = ...) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(x: str, y: str = ..., z: int = ...) -> str: ... @overload def foo1(*x: str) -> int: ... @overload -def foo2(x: str, y: str = ..., z: int = ...) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo2(x: str, y: str = ..., z: int = ...) -> str: ... @overload def foo2(*x: str) -> int: ... [builtins fixtures/tuple.pyi] @@ -2449,7 +2452,7 @@ from wrapper import * from typing import overload @overload -def foo1(*, x: str, y: str, z: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(*, x: str, y: str, z: str) -> str: ... @overload def foo1(**x: str) -> int: ... @@ -2481,12 +2484,12 @@ def foo2(**x: str) -> int: ... def foo2(*, x: str, y: str, z: int) -> str: ... @overload -def foo3(*, x: str, y: str, z: int = ...) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo3(*, x: str, y: str, z: int = ...) -> str: ... @overload def foo3(**x: str) -> int: ... @overload -def foo4(**x: str) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo4(**x: str) -> int: ... @overload def foo4(*, x: str, y: str, z: int = ...) -> str: ... [builtins fixtures/dict.pyi] @@ -2497,12 +2500,13 @@ from wrapper import * from typing import overload @overload -def foo1(x: str, *, y: str, z: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo1(x: str, *, y: str, z: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def foo1(**x: str) -> int: ... @overload -def foo2(**x: str) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def foo2(**x: str) -> int: ... @overload def foo2(x: str, *, y: str, z: str) -> str: ... @@ -2798,7 +2802,8 @@ def h(x: List[Union[C, D]]) -> str: ... def h(x): ... @overload -def i(x: List[Union[A, B]]) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def i(x: List[Union[A, B]]) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def i(x: List[Union[A, B, C]]) -> str: ... def i(x): ... @@ -2810,8 +2815,9 @@ from typing import TypeVar, overload T = TypeVar('T') +# Note: this is unsafe, but it is hard to detect. @overload -def f(x: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(x: int) -> str: ... @overload def f(x: T) -> T: ... def f(x): ... @@ -2827,14 +2833,15 @@ from typing import TypeVar, overload, List T = TypeVar('T') +# Note: first two examples are unsafe, but it is hard to detect. @overload -def f1(x: List[int]) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f1(x: List[int]) -> str: ... @overload def f1(x: List[T]) -> T: ... def f1(x): ... @overload -def f2(x: List[int]) -> List[str]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f2(x: List[int]) -> List[str]: ... @overload def f2(x: List[T]) -> List[T]: ... def f2(x): ... @@ -2859,17 +2866,15 @@ from typing import TypeVar, overload, Generic T = TypeVar('T') class Wrapper(Generic[T]): + # Similar to above: this is unsafe, but it is hard to detect. @overload - def f(self, x: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def f(self, x: int) -> str: ... @overload def f(self, x: T) -> T: ... def f(self, x): ... - # TODO: This shouldn't trigger an error message? - # Related to testTypeCheckOverloadImplementationTypeVarDifferingUsage2? - # See https://github.com/python/mypy/issues/5510 @overload - def g(self, x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def g(self, x: int) -> int: ... @overload def g(self, x: T) -> T: ... def g(self, x): ... @@ -2880,28 +2885,27 @@ from typing import TypeVar, overload, Generic, List T = TypeVar('T') class Wrapper(Generic[T]): + # Similar to above: first two examples are unsafe, but it is hard to detect. @overload - def f1(self, x: List[int]) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def f1(self, x: List[int]) -> str: ... @overload def f1(self, x: List[T]) -> T: ... def f1(self, x): ... @overload - def f2(self, x: List[int]) -> List[str]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def f2(self, x: List[int]) -> List[str]: ... @overload def f2(self, x: List[T]) -> List[T]: ... def f2(self, x): ... - # TODO: This shouldn't trigger an error message? - # See https://github.com/python/mypy/issues/5510 @overload - def g1(self, x: List[int]) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def g1(self, x: List[int]) -> int: ... @overload def g1(self, x: List[T]) -> T: ... def g1(self, x): ... @overload - def g2(self, x: List[int]) -> List[int]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def g2(self, x: List[int]) -> List[int]: ... @overload def g2(self, x: List[T]) -> List[T]: ... def g2(self, x): ... @@ -3078,13 +3082,14 @@ class C: pass S = TypeVar('S', A, B) @overload -def f(x: S) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(x: S) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types \ + # N: Flipping the order of overloads will fix this error @overload def f(x: Union[B, C]) -> str: ... def f(x): pass @overload -def g(x: Union[B, C]) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def g(x: Union[B, C]) -> int: ... @overload def g(x: S) -> str: ... def g(x): pass @@ -3607,7 +3612,7 @@ def test(x: T) -> T: from typing import overload, Optional @overload -def f(x: None) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(x: None) -> int: ... @overload def f(x: object) -> str: ... def f(x): ... @@ -3632,7 +3637,7 @@ reveal_type(g(c)) # N: Revealed type is "builtins.str" from typing import overload, Optional @overload -def f(x: None) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(x: None) -> int: ... @overload def f(x: object) -> str: ... def f(x): ... @@ -3978,7 +3983,7 @@ from typing import overload, Any, Optional, Union class FakeAttribute: @overload - def dummy(self, instance: None, owner: Any) -> 'FakeAttribute': ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def dummy(self, instance: None, owner: Any) -> 'FakeAttribute': ... @overload def dummy(self, instance: object, owner: Any) -> int: ... def dummy(self, instance: Optional[object], owner: Any) -> Union['FakeAttribute', int]: ... @@ -4545,7 +4550,7 @@ reveal_type(Child().foo(3).child_only()) # N: Revealed type is "builtins.in [case testOverloadAndSelfTypesGenericNoOverlap] from typing import Generic, TypeVar, Any, overload, Self, Union -T = TypeVar("T", bound=Any) +T = TypeVar("T") class C(Generic[T]): @overload def get(self, obj: None) -> Self: ... @@ -4903,7 +4908,7 @@ T = TypeVar('T') def f() -> None: @overload - def g(x: str) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + def g(x: str) -> int: ... @overload def g(x: T) -> T: ... def g(x): @@ -4944,7 +4949,7 @@ x: Any reveal_type(attr(x)) # N: Revealed type is "Any" attr("hi", 1) # E: No overload variant of "attr" matches argument types "str", "int" \ # N: Possible overload variants: \ - # N: def [T in (int, float)] attr(default: T = ..., blah: int = ...) -> T \ + # N: def [T in (int, float)] attr(default: T, blah: int = ...) -> T \ # N: def attr(default: Any = ...) -> int [file lib.pyi] from typing import overload, Any, TypeVar @@ -4952,7 +4957,7 @@ from typing import overload, Any, TypeVar T = TypeVar('T', int, float) @overload -def attr(default: T = ..., blah: int = ...) -> T: ... +def attr(default: T, blah: int = ...) -> T: ... @overload def attr(default: Any = ...) -> int: ... [out] @@ -5008,7 +5013,7 @@ children: List[Child] parents: List[Parent] @overload -def f(x: Child) -> List[Child]: pass # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f(x: Child) -> List[Child]: pass @overload def f(x: Parent) -> List[Parent]: pass def f(x: Union[Child, Parent]) -> Union[List[Child], List[Parent]]: @@ -5319,7 +5324,7 @@ def f1(g: G[A, B]) -> B: ... def f1(g: Any) -> Any: ... @overload -def f2(g: G[A, Any]) -> A: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def f2(g: G[A, Any]) -> A: ... @overload def f2(g: G[A, B], x: int = ...) -> B: ... def f2(g: Any, x: int = ...) -> Any: ... @@ -6500,7 +6505,7 @@ P = ParamSpec("P") R = TypeVar("R") @overload -def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def func(x: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... @overload def func(x: Callable[P, R]) -> Callable[Concatenate[str, P], R]: ... def func(x: Callable[..., R]) -> Callable[..., R]: ... @@ -6710,3 +6715,38 @@ class B: def f(self, *args, **kwargs): pass [builtins fixtures/tuple.pyi] + +[case testOverloadsSafeOverlapAllowed] +from lib import * +[file lib.pyi] +from typing import overload + +@overload +def bar(x: object) -> object: ... +@overload +def bar(x: int = ...) -> int: ... + +[case testOverloadsInvariantOverlapAllowed] +from lib import * +[file lib.pyi] +from typing import overload, List + +@overload +def bar(x: List[int]) -> List[int]: ... +@overload +def bar(x: List[object]) -> List[object]: ... + +[case testOverloadsNoneAnyOverlapAllowed] +from lib import * +[file lib.pyi] +from typing import overload, Any + +@overload +def foo(x: None) -> int: ... +@overload +def foo(x: object) -> str: ... + +@overload +def bar(x: int) -> int: ... +@overload +def bar(x: Any) -> str: ... diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 37916c2155fe..e6d8cec3f0b0 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -901,8 +901,8 @@ class A: def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`15, *_P.args, **_P.kwargs) -> _R`15" -reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`19, *_P.args, **_P.kwargs) -> _R`19" +reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`6, *_P.args, **_P.kwargs) -> _R`6" +reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`10, *_P.args, **_P.kwargs) -> _R`10" def f(x: int) -> int: ... @@ -933,8 +933,8 @@ class A: def func(self, action: Job[_P, None]) -> Job[_P, None]: ... -reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`13, None]) -> __main__.Job[_P`13, None]" -reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`15, None]) -> __main__.Job[_P`15, None]" +reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`4, None]) -> __main__.Job[_P`4, None]" +reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`6, None]) -> __main__.Job[_P`6, None]" reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" def f(x: int, y: int) -> None: ... @@ -1096,7 +1096,7 @@ j = Job(generic_f) reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`13)" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`4)" reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] @@ -1115,10 +1115,10 @@ class Job(Generic[_P, _T]): def generic_f(x: _T) -> _T: ... j = Job(generic_f) -reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`12], _T`12]" +reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`3], _T`3]" jf = j.into_callable() -reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`13) -> _T`13" +reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`4) -> _T`4" reveal_type(jf(1)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] @@ -1600,7 +1600,7 @@ from typing_extensions import Concatenate, ParamSpec P = ParamSpec("P") @overload -def command() -> Callable[[Callable[Concatenate[object, object, P], object]], None]: # E: Overloaded function signatures 1 and 2 overlap with incompatible return types +def command() -> Callable[[Callable[Concatenate[object, object, P], object]], None]: ... @overload @@ -1640,13 +1640,13 @@ U = TypeVar("U") def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... def test(x: U) -> U: ... reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]" -reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`12) -> builtins.list[T`12]" +reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]" class A: ... TA = TypeVar("TA", bound=A) def test_with_bound(x: TA) -> TA: ... -reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`14) -> builtins.list[T`14]" +reveal_type(dec(test_with_bound)) # N: Revealed type is "def [T <: __main__.A] (x: T`5) -> builtins.list[T`5]" dec(test_with_bound)(0) # E: Value of type variable "T" of function cannot be "int" dec(test_with_bound)(A()) # OK [builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index fdd628b0271b..1480c83b2272 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1793,7 +1793,7 @@ class C: def bar(self) -> Self: ... def foo(self, x: S) -> Tuple[Self, S]: ... -reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`7, x: S`8) -> Tuple[Self`7, S`8]" +reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> Tuple[Self`1, S`2]" reveal_type(C().foo(42)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" [builtins fixtures/tuple.pyi] @@ -1807,7 +1807,7 @@ class C: def bar(self) -> Self: ... foo: Callable[[S, Self], Tuple[Self, S]] -reveal_type(C().foo) # N: Revealed type is "def [S] (S`7, __main__.C) -> Tuple[__main__.C, S`7]" +reveal_type(C().foo) # N: Revealed type is "def [S] (S`1, __main__.C) -> Tuple[__main__.C, S`1]" reveal_type(C().foo(42, C())) # N: Revealed type is "Tuple[__main__.C, builtins.int]" class This: ... [builtins fixtures/tuple.pyi] @@ -2032,7 +2032,7 @@ class Ben(Object): } @classmethod def doit(cls) -> Foo: - reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`10) -> Self`10]" + reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]" foo_method = cls.MY_MAP["foo"] return foo_method(Foo()) [builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index a76d3abd7114..3bf8613d2478 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -444,7 +444,7 @@ False [case testOverlappingOperatorMethods] class X: pass class A: - def __add__(self, x) -> int: + def __add__(self, x: object) -> int: if isinstance(x, X): return 1 return NotImplemented @@ -1942,13 +1942,13 @@ class Bar(Generic[P]): ... def bad(foo: Foo[[int]], bar: Bar[[int]]) -> bool: return foo == bar -def good1(foo1: Foo[[int]], foo2: Foo[[str]]) -> bool: +def bad1(foo1: Foo[[int]], foo2: Foo[[str]]) -> bool: return foo1 == foo2 -def good2(foo1: Foo[[int, str]], foo2: Foo[[int, bytes]]) -> bool: +def bad2(foo1: Foo[[int, str]], foo2: Foo[[int, bytes]]) -> bool: return foo1 == foo2 -def good3(foo1: Foo[[int]], foo2: Foo[[int, int]]) -> bool: +def bad3(foo1: Foo[[int]], foo2: Foo[[int, int]]) -> bool: return foo1 == foo2 def good4(foo1: Foo[[int]], foo2: Foo[[int]]) -> bool: @@ -1971,6 +1971,9 @@ def good9(foo1: Foo[Concatenate[int, P]], foo2: Foo[[int, str, bytes]], *args: P [out] _testStrictEqualitywithParamSpec.py:11: error: Non-overlapping equality check (left operand type: "Foo[[int]]", right operand type: "Bar[[int]]") +_testStrictEqualitywithParamSpec.py:14: error: Non-overlapping equality check (left operand type: "Foo[[int]]", right operand type: "Foo[[str]]") +_testStrictEqualitywithParamSpec.py:17: error: Non-overlapping equality check (left operand type: "Foo[[int, str]]", right operand type: "Foo[[int, bytes]]") +_testStrictEqualitywithParamSpec.py:20: error: Non-overlapping equality check (left operand type: "Foo[[int]]", right operand type: "Foo[[int, int]]") [case testInferenceOfDunderDictOnClassObjects] class Foo: ... From 6877d6fb668c24d984268b937c3eeed8c4cad296 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Jun 2024 10:01:16 +0100 Subject: [PATCH 0651/1617] Make syntax of generic overload variants in messages close to PEP 695 (#17401) We used a custom syntax for type variable bounds and restrictions. Use PEP 695 syntax instead (or at least something closer to PEP 695 syntax). Co-authored-by: Ivan Levkivskyi --- mypy/messages.py | 4 ++-- test-data/unit/check-generic-subtyping.test | 2 +- test-data/unit/check-overloading.test | 12 ++++++------ test-data/unit/check-protocols.test | 4 ++-- test-data/unit/fine-grained.test | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index c3a34bd41aba..27f152413151 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2928,10 +2928,10 @@ 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, options)}") + tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}") elif tvar.values: tvars.append( - "{} in ({})".format( + "{}: ({})".format( tvar.name, ", ".join([format_type_bare(tp, options) for tp in tvar.values]), ) diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index fd40f128ff4a..90180e0f83f6 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -306,7 +306,7 @@ main:14: error: Signature of "f" incompatible with supertype "A" main:14: note: Superclass: main:14: note: def [S] f(self, x: int, y: S) -> None main:14: note: Subclass: -main:14: note: def [T1 <: str, S] f(self, x: T1, y: S) -> None +main:14: note: def [T1: str, S] f(self, x: T1, y: S) -> None -- Inheritance from generic types with implicit dynamic supertype -- -------------------------------------------------------------- diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 03863b2978ba..48d5996b226f 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1276,7 +1276,7 @@ f('x')() # E: "str" not callable f(1)() # E: "bool" not callable f(1.1) # E: No overload variant of "f" matches argument type "float" \ # N: Possible overload variants: \ - # N: def [T <: str] f(x: T) -> T \ + # N: def [T: str] f(x: T) -> T \ # N: def f(x: int) -> bool f(mystr())() # E: "mystr" not callable [builtins fixtures/primitives.pyi] @@ -1298,8 +1298,8 @@ def g(x: U, y: V) -> None: f(x)() # E: "mystr" not callable f(y) # E: No overload variant of "f" matches argument type "V" \ # N: Possible overload variants: \ - # N: def [T <: str] f(x: T) -> T \ - # N: def [T <: str] f(x: List[T]) -> None + # N: def [T: str] f(x: T) -> T \ + # N: def [T: str] f(x: List[T]) -> None a = f([x]) reveal_type(a) # N: Revealed type is "None" f([y]) # E: Value of type variable "T" of "f" cannot be "V" @@ -1351,7 +1351,7 @@ f(b'1')() # E: "str" not callable f(1.0) # E: No overload variant of "f" matches argument type "float" \ # N: Possible overload variants: \ # N: def f(x: int) -> int \ - # N: def [AnyStr in (bytes, str)] f(x: AnyStr) -> str + # N: def [AnyStr: (bytes, str)] f(x: AnyStr) -> str @overload def g(x: AnyStr, *a: AnyStr) -> None: pass @@ -4949,7 +4949,7 @@ x: Any reveal_type(attr(x)) # N: Revealed type is "Any" attr("hi", 1) # E: No overload variant of "attr" matches argument types "str", "int" \ # N: Possible overload variants: \ - # N: def [T in (int, float)] attr(default: T, blah: int = ...) -> T \ + # N: def [T: (int, float)] attr(default: T, blah: int = ...) -> T \ # N: def attr(default: Any = ...) -> int [file lib.pyi] from typing import overload, Any, TypeVar @@ -4972,7 +4972,7 @@ x: Any reveal_type(attr(x)) # N: Revealed type is "Any" attr("hi", 1) # E: No overload variant of "attr" matches argument types "str", "int" \ # N: Possible overload variants: \ - # N: def [T <: int] attr(default: T = ..., blah: int = ...) -> T \ + # N: def [T: int] attr(default: T = ..., blah: int = ...) -> T \ # N: def attr(default: Any = ...) -> int [file lib.pyi] from typing import overload, TypeVar, Any diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index e73add454a67..ee7556461fd3 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2182,7 +2182,7 @@ main:11: note: Following member(s) of "B" have conflicts: main:11: note: Expected: main:11: note: def [T] f(self, x: T) -> None main:11: note: Got: -main:11: note: def [S <: int, T] f(self, x: S, y: T) -> None +main:11: note: def [S: int, T] f(self, x: S, y: T) -> None [case testProtocolIncompatibilityWithGenericRestricted] from typing import Protocol, TypeVar @@ -2202,7 +2202,7 @@ main:11: note: Following member(s) of "B" have conflicts: main:11: note: Expected: main:11: note: def [T] f(self, x: T) -> None main:11: note: Got: -main:11: note: def [S in (int, str), T] f(self, x: S, y: T) -> None +main:11: note: def [S: (int, str), T] f(self, x: S, y: T) -> None [case testProtocolIncompatibilityWithManyOverloads] from typing import Protocol, overload diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index a87f8ceca15c..2a652e50b1e6 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -7138,7 +7138,7 @@ T = TypeVar('T', bound=str) a.py:2: error: No overload variant of "f" matches argument type "int" a.py:2: note: Possible overload variants: a.py:2: note: def f(x: C) -> None -a.py:2: note: def [c.T <: str] f(x: c.T) -> c.T +a.py:2: note: def [c.T: str] f(x: c.T) -> c.T [case testOverloadsGenericToNonGeneric] import a From 4ba2696466060435dbdac10c73ce94731370e252 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:45:37 +0200 Subject: [PATCH 0652/1617] Update typeshed (#17409) The automatic sync failed due to a merge conflict. Source commit: https://github.com/python/typeshed/commit/6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace --- ...e-of-LiteralString-in-builtins-13743.patch | 38 ++-- mypy/typeshed/stdlib/VERSIONS | 3 +- mypy/typeshed/stdlib/_ast.pyi | 5 +- mypy/typeshed/stdlib/_curses.pyi | 3 +- mypy/typeshed/stdlib/_json.pyi | 2 +- mypy/typeshed/stdlib/_tkinter.pyi | 7 + mypy/typeshed/stdlib/_weakref.pyi | 5 +- mypy/typeshed/stdlib/ast.pyi | 3 + mypy/typeshed/stdlib/builtins.pyi | 14 +- mypy/typeshed/stdlib/configparser.pyi | 195 +++++++++++++----- mypy/typeshed/stdlib/dataclasses.pyi | 8 +- mypy/typeshed/stdlib/enum.pyi | 26 ++- mypy/typeshed/stdlib/glob.pyi | 10 +- mypy/typeshed/stdlib/io.pyi | 2 +- mypy/typeshed/stdlib/ipaddress.pyi | 6 +- mypy/typeshed/stdlib/itertools.pyi | 58 ++++++ mypy/typeshed/stdlib/json/encoder.pyi | 4 +- mypy/typeshed/stdlib/locale.pyi | 8 +- mypy/typeshed/stdlib/logging/__init__.pyi | 8 +- mypy/typeshed/stdlib/logging/handlers.pyi | 2 +- mypy/typeshed/stdlib/math.pyi | 2 +- mypy/typeshed/stdlib/mimetypes.pyi | 9 + mypy/typeshed/stdlib/mmap.pyi | 10 +- .../stdlib/multiprocessing/context.pyi | 16 +- .../stdlib/multiprocessing/managers.pyi | 4 + .../stdlib/multiprocessing/shared_memory.pyi | 6 +- .../stdlib/multiprocessing/sharedctypes.pyi | 45 ++-- mypy/typeshed/stdlib/os/__init__.pyi | 20 +- mypy/typeshed/stdlib/pathlib.pyi | 55 ++++- mypy/typeshed/stdlib/platform.pyi | 25 +++ mypy/typeshed/stdlib/shutil.pyi | 16 +- mypy/typeshed/stdlib/spwd.pyi | 5 + mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 6 +- mypy/typeshed/stdlib/tarfile.pyi | 53 +++-- mypy/typeshed/stdlib/telnetlib.pyi | 1 + mypy/typeshed/stdlib/time.pyi | 5 +- mypy/typeshed/stdlib/traceback.pyi | 72 +++++-- mypy/typeshed/stdlib/types.pyi | 9 +- mypy/typeshed/stdlib/typing.pyi | 37 +++- mypy/typeshed/stdlib/typing_extensions.pyi | 8 +- mypy/typeshed/stdlib/weakref.pyi | 5 +- mypy/typeshed/stdlib/xml/sax/handler.pyi | 2 +- 42 files changed, 630 insertions(+), 188 deletions(-) diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch index 6a0977dfc489..683b0c322b71 100644 --- a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch +++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch @@ -1,14 +1,14 @@ -From 5c00e362d40aa26e0a22a740f05a52d05edf0f91 Mon Sep 17 00:00:00 2001 +From 3ec9b878d6bbe3fae64a508a62372f10a886406f Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH] Remove use of LiteralString in builtins (#13743) --- - mypy/typeshed/stdlib/builtins.pyi | 88 ------------------------------- - 1 file changed, 88 deletions(-) + mypy/typeshed/stdlib/builtins.pyi | 95 ------------------------------- + 1 file changed, 95 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index b4765b26c..99919c64c 100644 +index 53e00ec6a..bad3250ef 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 @@ -19,7 +19,7 @@ index b4765b26c..99919c64c 100644 ParamSpec, Self, TypeAlias, -@@ -434,31 +433,16 @@ class str(Sequence[str]): +@@ -435,31 +434,16 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... @@ -49,9 +49,9 @@ index b4765b26c..99919c64c 100644 - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload def format(self, *args: object, **kwargs: object) -> str: ... - def format_map(self, map: _FormatMapMapping) -> str: ... + def format_map(self, mapping: _FormatMapMapping, /) -> str: ... def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... -@@ -474,89 +458,32 @@ class str(Sequence[str]): +@@ -475,99 +459,35 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... @@ -75,10 +75,20 @@ index b4765b26c..99919c64c 100644 - def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ... - @overload def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] -- @overload -- def replace(self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /) -> LiteralString: ... -- @overload - def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] + if sys.version_info >= (3, 13): +- @overload +- def replace( +- self: LiteralString, old: LiteralString, new: LiteralString, /, count: SupportsIndex = -1 +- ) -> LiteralString: ... +- @overload + def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ... # type: ignore[misc] + else: +- @overload +- def replace( +- self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, / +- ) -> LiteralString: ... +- @overload + def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): - @overload - def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... @@ -141,7 +151,7 @@ index b4765b26c..99919c64c 100644 def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload -@@ -567,9 +494,6 @@ class str(Sequence[str]): +@@ -578,9 +498,6 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... @@ -151,7 +161,7 @@ index b4765b26c..99919c64c 100644 def __add__(self, value: str, /) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] -@@ -578,25 +502,13 @@ class str(Sequence[str]): +@@ -589,25 +506,13 @@ class str(Sequence[str]): def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... def __gt__(self, value: str, /) -> bool: ... def __hash__(self) -> int: ... @@ -178,5 +188,5 @@ index b4765b26c..99919c64c 100644 def __getnewargs__(self) -> tuple[str]: ... -- -2.39.3 (Apple Git-146) +2.45.2 diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index a8526aab9422..7b9ce2864484 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -65,9 +65,9 @@ array: 3.0- ast: 3.0- asynchat: 3.0-3.11 asyncio: 3.4- -asyncio.mixins: 3.10- asyncio.exceptions: 3.8- asyncio.format_helpers: 3.7- +asyncio.mixins: 3.10- asyncio.runners: 3.7- asyncio.staggered: 3.8- asyncio.taskgroups: 3.11- @@ -270,6 +270,7 @@ threading: 3.0- time: 3.0- timeit: 3.0- tkinter: 3.0- +tkinter.tix: 3.0-3.12 token: 3.0- tokenize: 3.0- tomllib: 3.11- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 51791b4099d5..d14c6d39a162 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -7,8 +7,11 @@ PyCF_ONLY_AST: Literal[1024] PyCF_TYPE_COMMENTS: Literal[4096] PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +if sys.version_info >= (3, 13): + PyCF_OPTIMIZED_AST: Literal[33792] + # Used for node end positions in constructor keyword arguments -_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) # noqa: Y023 +_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) # Alias used for fields that must always be valid identifiers # A string `x` counts as a valid identifier if both the following are True diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 6f3fbd807fcc..eb1d7b9bde9f 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -63,8 +63,7 @@ A_COLOR: int A_DIM: int A_HORIZONTAL: int A_INVIS: int -if sys.platform != "darwin": - A_ITALIC: int +A_ITALIC: int A_LEFT: int A_LOW: int A_NORMAL: int diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index a6a62be184d8..069fb6eac4bf 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -45,5 +45,5 @@ class make_scanner: def __init__(self, context: make_scanner) -> None: ... def __call__(self, string: str, index: int) -> tuple[Any, int]: ... -def encode_basestring_ascii(s: str) -> str: ... +def encode_basestring_ascii(s: str, /) -> str: ... def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index 3340df424163..aea74c8be279 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -1,5 +1,7 @@ import sys +from collections.abc import Callable from typing import Any, ClassVar, Literal, final +from typing_extensions import TypeAlias # _tkinter is meant to be only used internally by tkinter, but some tkinter # functions e.g. return _tkinter.Tcl_Obj objects. Tcl_Obj represents a Tcl @@ -30,6 +32,8 @@ class Tcl_Obj: class TclError(Exception): ... +_TkinterTraceFunc: TypeAlias = Callable[[tuple[str, ...]], object] + # This class allows running Tcl code. Tkinter uses it internally a lot, and # it's often handy to drop a piece of Tcl code into a tkinter program. Example: # @@ -86,6 +90,9 @@ class TkappType: def unsetvar(self, *args, **kwargs): ... def wantobjects(self, *args, **kwargs): ... def willdispatch(self): ... + if sys.version_info >= (3, 12): + def gettrace(self, /) -> _TkinterTraceFunc | None: ... + def settrace(self, func: _TkinterTraceFunc | None, /) -> None: ... # These should be kept in sync with tkinter.tix constants, except ALL_EVENTS which doesn't match TCL_ALL_EVENTS ALL_EVENTS: Literal[-3] diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index 61365645d768..f142820c56c7 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -21,8 +21,9 @@ class ProxyType(Generic[_T]): # "weakproxy" def __getattr__(self, attr: str) -> Any: ... class ReferenceType(Generic[_T]): - __callback__: Callable[[ReferenceType[_T]], Any] - def __new__(cls, o: _T, callback: Callable[[ReferenceType[_T]], Any] | None = ..., /) -> Self: ... + __callback__: Callable[[Self], Any] + def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... + def __init__(self, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> None: ... def __call__(self) -> _T | None: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 2525c3642a6f..90ede461fe3c 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -365,3 +365,6 @@ def walk(node: AST) -> Iterator[AST]: ... if sys.version_info >= (3, 9): def main() -> None: ... + +if sys.version_info >= (3, 14): + def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 42c0b27baf68..28b0b11a8e5c 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -445,7 +445,7 @@ class str(Sequence[str]): def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def format(self, *args: object, **kwargs: object) -> str: ... - def format_map(self, map: _FormatMapMapping) -> str: ... + def format_map(self, mapping: _FormatMapMapping, /) -> str: ... def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... @@ -464,7 +464,10 @@ class str(Sequence[str]): def lower(self) -> str: ... # type: ignore[misc] def lstrip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] def partition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] - def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] + if sys.version_info >= (3, 13): + def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ... # type: ignore[misc] + else: + def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] if sys.version_info >= (3, 9): def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] @@ -1126,6 +1129,9 @@ class property: fset: Callable[[Any, Any], None] | None fdel: Callable[[Any], None] | None __isabstractmethod__: bool + if sys.version_info >= (3, 13): + __name__: str + def __init__( self, fget: Callable[[Any], Any] | None = ..., @@ -1969,3 +1975,7 @@ if sys.version_info >= (3, 11): def split( self, condition: Callable[[_ExceptionT_co | Self], bool], / ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ... + +if sys.version_info >= (3, 13): + class IncompleteInputError(SyntaxError): ... + class PythonFinalizationError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index 07b57b17d56d..f38bb1de674d 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -5,7 +5,31 @@ from re import Pattern from typing import Any, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 13): + __all__ = ( + "NoSectionError", + "DuplicateOptionError", + "DuplicateSectionError", + "NoOptionError", + "InterpolationError", + "InterpolationDepthError", + "InterpolationMissingOptionError", + "InterpolationSyntaxError", + "ParsingError", + "MissingSectionHeaderError", + "ConfigParser", + "RawConfigParser", + "Interpolation", + "BasicInterpolation", + "ExtendedInterpolation", + "SectionProxy", + "ConverterMapping", + "DEFAULTSECT", + "MAX_INTERPOLATION_DEPTH", + "UNNAMED_SECTION", + "MultilineContinuationError", + ) +elif sys.version_info >= (3, 12): __all__ = ( "NoSectionError", "DuplicateOptionError", @@ -71,8 +95,9 @@ class Interpolation: class BasicInterpolation(Interpolation): ... class ExtendedInterpolation(Interpolation): ... -class LegacyInterpolation(Interpolation): - def before_get(self, parser: _Parser, section: str, option: str, value: str, vars: _Section) -> str: ... +if sys.version_info < (3, 13): + class LegacyInterpolation(Interpolation): + def before_get(self, parser: _Parser, section: str, option: str, value: str, vars: _Section) -> str: ... class RawConfigParser(_Parser): _SECT_TMPL: ClassVar[str] # undocumented @@ -86,54 +111,108 @@ class RawConfigParser(_Parser): BOOLEAN_STATES: ClassVar[Mapping[str, bool]] # undocumented default_section: str - @overload - def __init__( - self, - defaults: Mapping[str, str | None] | None = None, - dict_type: type[Mapping[str, str]] = ..., - *, - allow_no_value: Literal[True], - delimiters: Sequence[str] = ("=", ":"), - comment_prefixes: Sequence[str] = ("#", ";"), - inline_comment_prefixes: Sequence[str] | None = None, - strict: bool = True, - empty_lines_in_values: bool = True, - default_section: str = "DEFAULT", - interpolation: Interpolation | None = ..., - converters: _ConvertersMap = ..., - ) -> None: ... - @overload - def __init__( - self, - defaults: Mapping[str, str | None] | None, - dict_type: type[Mapping[str, str]], - allow_no_value: Literal[True], - *, - delimiters: Sequence[str] = ("=", ":"), - comment_prefixes: Sequence[str] = ("#", ";"), - inline_comment_prefixes: Sequence[str] | None = None, - strict: bool = True, - empty_lines_in_values: bool = True, - default_section: str = "DEFAULT", - interpolation: Interpolation | None = ..., - converters: _ConvertersMap = ..., - ) -> None: ... - @overload - def __init__( - self, - defaults: _Section | None = None, - dict_type: type[Mapping[str, str]] = ..., - allow_no_value: bool = False, - *, - delimiters: Sequence[str] = ("=", ":"), - comment_prefixes: Sequence[str] = ("#", ";"), - inline_comment_prefixes: Sequence[str] | None = None, - strict: bool = True, - empty_lines_in_values: bool = True, - default_section: str = "DEFAULT", - interpolation: Interpolation | None = ..., - converters: _ConvertersMap = ..., - ) -> None: ... + if sys.version_info >= (3, 13): + @overload + def __init__( + self, + defaults: Mapping[str, str | None] | None = None, + dict_type: type[Mapping[str, str]] = ..., + *, + allow_no_value: Literal[True], + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + allow_unnamed_section: bool = False, + ) -> None: ... + @overload + def __init__( + self, + defaults: Mapping[str, str | None] | None, + dict_type: type[Mapping[str, str]], + allow_no_value: Literal[True], + *, + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + allow_unnamed_section: bool = False, + ) -> None: ... + @overload + def __init__( + self, + defaults: _Section | None = None, + dict_type: type[Mapping[str, str]] = ..., + allow_no_value: bool = False, + *, + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + allow_unnamed_section: bool = False, + ) -> None: ... + else: + @overload + def __init__( + self, + defaults: Mapping[str, str | None] | None = None, + dict_type: type[Mapping[str, str]] = ..., + *, + allow_no_value: Literal[True], + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + ) -> None: ... + @overload + def __init__( + self, + defaults: Mapping[str, str | None] | None, + dict_type: type[Mapping[str, str]], + allow_no_value: Literal[True], + *, + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + ) -> None: ... + @overload + def __init__( + self, + defaults: _Section | None = None, + dict_type: type[Mapping[str, str]] = ..., + allow_no_value: bool = False, + *, + delimiters: Sequence[str] = ("=", ":"), + comment_prefixes: Sequence[str] = ("#", ";"), + inline_comment_prefixes: Sequence[str] | None = None, + strict: bool = True, + empty_lines_in_values: bool = True, + default_section: str = "DEFAULT", + interpolation: Interpolation | None = ..., + converters: _ConvertersMap = ..., + ) -> None: ... + def __len__(self) -> int: ... def __getitem__(self, key: str) -> SectionProxy: ... def __setitem__(self, key: str, value: _Section) -> None: ... @@ -300,7 +379,10 @@ class InterpolationSyntaxError(InterpolationError): ... class ParsingError(Error): source: str errors: list[tuple[int, str]] - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 13): + def __init__(self, source: str, *args: object) -> None: ... + def combine(self, others: Iterable[ParsingError]) -> ParsingError: ... + elif sys.version_info >= (3, 12): def __init__(self, source: str) -> None: ... else: def __init__(self, source: str | None = None, filename: str | None = None) -> None: ... @@ -311,3 +393,12 @@ class MissingSectionHeaderError(ParsingError): lineno: int line: str def __init__(self, filename: str, lineno: int, line: str) -> None: ... + +if sys.version_info >= (3, 13): + class _UNNAMED_SECTION: ... + UNNAMED_SECTION: _UNNAMED_SECTION + + class MultilineContinuationError(ParsingError): + lineno: int + line: str + def __init__(self, filename: str, lineno: int, line: str) -> None: ... diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 18c7e7b5a467..30489e6f8b3d 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -5,7 +5,7 @@ from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping from typing import Any, Generic, Literal, Protocol, TypeVar, overload -from typing_extensions import TypeAlias, TypeGuard +from typing_extensions import TypeAlias, TypeIs if sys.version_info >= (3, 9): from types import GenericAlias @@ -214,11 +214,9 @@ else: def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ... @overload -def is_dataclass(obj: DataclassInstance) -> Literal[True]: ... +def is_dataclass(obj: type) -> TypeIs[type[DataclassInstance]]: ... @overload -def is_dataclass(obj: type) -> TypeGuard[type[DataclassInstance]]: ... -@overload -def is_dataclass(obj: object) -> TypeGuard[DataclassInstance | type[DataclassInstance]]: ... +def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstance]]: ... class FrozenInstanceError(AttributeError): ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 96cb2264ea20..5c82b07c4185 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -31,10 +31,12 @@ if sys.version_info >= (3, 11): "nonmember", "property", "verify", + "pickle_by_enum_name", + "pickle_by_global_name", ] -if sys.version_info >= (3, 11): - __all__ += ["pickle_by_enum_name", "pickle_by_global_name"] +if sys.version_info >= (3, 13): + __all__ += ["EnumDict"] _EnumMemberT = TypeVar("_EnumMemberT") _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum]) @@ -74,6 +76,12 @@ class _EnumDict(dict[str, Any]): def update(self, members: SupportsKeysAndGetItem[str, Any], **more_members: Any) -> None: ... @overload def update(self, members: Iterable[tuple[str, Any]], **more_members: Any) -> None: ... + if sys.version_info >= (3, 13): + @property + def member_names(self) -> list[str]: ... + +if sys.version_info >= (3, 13): + EnumDict = _EnumDict # Structurally: Iterable[T], Reversible[T], Container[T] where T is the enum itself class EnumMeta(type): @@ -259,9 +267,9 @@ if sys.version_info >= (3, 11): def _generate_next_value_(name: str, start: int, count: int, last_values: list[str]) -> str: ... class EnumCheck(StrEnum): - CONTINUOUS: str - NAMED_FLAGS: str - UNIQUE: str + CONTINUOUS = "no skipped integer values" + NAMED_FLAGS = "multi-flag aliases may not contain unnamed flags" + UNIQUE = "one name per value" CONTINUOUS = EnumCheck.CONTINUOUS NAMED_FLAGS = EnumCheck.NAMED_FLAGS @@ -272,10 +280,10 @@ if sys.version_info >= (3, 11): def __call__(self, enumeration: _EnumerationT) -> _EnumerationT: ... class FlagBoundary(StrEnum): - STRICT: str - CONFORM: str - EJECT: str - KEEP: str + STRICT = "strict" + CONFORM = "conform" + EJECT = "eject" + KEEP = "keep" STRICT = FlagBoundary.STRICT CONFORM = FlagBoundary.CONFORM diff --git a/mypy/typeshed/stdlib/glob.pyi b/mypy/typeshed/stdlib/glob.pyi index 914ccc12ef1e..03cb5418e256 100644 --- a/mypy/typeshed/stdlib/glob.pyi +++ b/mypy/typeshed/stdlib/glob.pyi @@ -1,10 +1,13 @@ import sys from _typeshed import StrOrBytesPath -from collections.abc import Iterator +from collections.abc import Iterator, Sequence from typing import AnyStr __all__ = ["escape", "glob", "iglob"] +if sys.version_info >= (3, 13): + __all__ += ["translate"] + def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... @@ -40,3 +43,8 @@ else: def escape(pathname: AnyStr) -> AnyStr: ... def has_magic(s: str | bytes) -> bool: ... # undocumented + +if sys.version_info >= (3, 13): + def translate( + pat: str, *, recursive: bool = False, include_hidden: bool = False, seps: Sequence[str] | None = None + ) -> str: ... diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index fdbbc8dddce9..01f3bfc06a27 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -75,7 +75,7 @@ class IOBase(metaclass=abc.ABCMeta): def __del__(self) -> None: ... @property def closed(self) -> bool: ... - def _checkClosed(self, msg: str | None = ...) -> None: ... # undocumented + def _checkClosed(self) -> None: ... # undocumented class RawIOBase(IOBase): def readall(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 98b1893d2a8a..03decc74e65e 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -147,7 +147,11 @@ class _BaseV4: @property def max_prefixlen(self) -> Literal[32]: ... -class IPv4Address(_BaseV4, _BaseAddress): ... +class IPv4Address(_BaseV4, _BaseAddress): + if sys.version_info >= (3, 13): + @property + def ipv6_mapped(self) -> IPv6Address: ... + class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): ... class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 264064dcd682..16e04829c6cf 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -17,6 +17,10 @@ _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") _T6 = TypeVar("_T6") +_T7 = TypeVar("_T7") +_T8 = TypeVar("_T8") +_T9 = TypeVar("_T9") +_T10 = TypeVar("_T10") _Step: TypeAlias = SupportsFloat | SupportsInt | SupportsIndex | SupportsComplex @@ -214,6 +218,60 @@ class product(Iterator[_T_co]): /, ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6]]: ... @overload + def __new__( + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + iter6: Iterable[_T6], + iter7: Iterable[_T7], + /, + ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6, _T7]]: ... + @overload + def __new__( + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + iter6: Iterable[_T6], + iter7: Iterable[_T7], + iter8: Iterable[_T8], + /, + ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8]]: ... + @overload + def __new__( + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + iter6: Iterable[_T6], + iter7: Iterable[_T7], + iter8: Iterable[_T8], + iter9: Iterable[_T9], + /, + ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9]]: ... + @overload + def __new__( + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + iter6: Iterable[_T6], + iter7: Iterable[_T7], + iter8: Iterable[_T8], + iter9: Iterable[_T9], + iter10: Iterable[_T10], + /, + ) -> product[tuple[_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10]]: ... + @overload def __new__(cls, *iterables: Iterable[_T1], repeat: int = 1) -> product[tuple[_T1, ...]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index c1062688bd93..473398a60b2a 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -10,8 +10,8 @@ INFINITY: float def py_encode_basestring(s: str) -> str: ... # undocumented def py_encode_basestring_ascii(s: str) -> str: ... # undocumented -def encode_basestring(s: str) -> str: ... # undocumented -def encode_basestring_ascii(s: str) -> str: ... # undocumented +def encode_basestring(s: str, /) -> str: ... # undocumented +def encode_basestring_ascii(s: str, /) -> str: ... # undocumented class JSONEncoder: item_separator: str diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index c18523e04361..58de65449572 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -96,7 +96,6 @@ __all__ = [ "getpreferredencoding", "Error", "setlocale", - "resetlocale", "localeconv", "strcoll", "strxfrm", @@ -121,6 +120,9 @@ if sys.version_info >= (3, 11): if sys.version_info < (3, 12): __all__ += ["format"] +if sys.version_info < (3, 13): + __all__ += ["resetlocale"] + if sys.platform != "win32": __all__ += ["LC_MESSAGES"] @@ -133,7 +135,9 @@ def getlocale(category: int = ...) -> tuple[_str | None, _str | None]: ... def setlocale(category: int, locale: _str | Iterable[_str | None] | None = None) -> _str: ... def getpreferredencoding(do_setlocale: bool = True) -> _str: ... def normalize(localename: _str) -> _str: ... -def resetlocale(category: int = ...) -> None: ... + +if sys.version_info < (3, 13): + def resetlocale(category: int = ...) -> None: ... if sys.version_info < (3, 12): def format( diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 8b19444a5d01..4c6163257236 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -8,7 +8,7 @@ from string import Template from time import struct_time from types import FrameType, TracebackType from typing import Any, ClassVar, Generic, Literal, Protocol, TextIO, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): from types import GenericAlias @@ -572,7 +572,11 @@ fatal = critical def disable(level: int = 50) -> None: ... def addLevelName(level: int, levelName: str) -> None: ... -def getLevelName(level: _Level) -> Any: ... +@overload +def getLevelName(level: int) -> str: ... +@overload +@deprecated("The str -> int case is considered a mistake.") +def getLevelName(level: str) -> Any: ... if sys.version_info >= (3, 11): def getLevelNamesMapping() -> dict[str, int]: ... diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index 4c3dc913308c..4e97012abba1 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -46,7 +46,7 @@ class BaseRotatingHandler(FileHandler): def rotate(self, source: str, dest: str) -> None: ... class RotatingFileHandler(BaseRotatingHandler): - maxBytes: str # undocumented + maxBytes: int # undocumented backupCount: int # undocumented if sys.version_info >= (3, 9): def __init__( diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 0e6565fcf588..2bb61e0669b4 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -125,4 +125,4 @@ if sys.version_info >= (3, 9): def ulp(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 13): - def fma(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, z: _SupportsFloatOrIndex) -> float: ... + def fma(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, z: _SupportsFloatOrIndex, /) -> float: ... diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index e74b214d3ff1..517193e3516f 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import StrPath from collections.abc import Sequence from typing import IO @@ -18,6 +19,9 @@ __all__ = [ "common_types", ] +if sys.version_info >= (3, 13): + __all__ += ["guess_file_type"] + def guess_type(url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... def guess_all_extensions(type: str, strict: bool = True) -> list[str]: ... def guess_extension(type: str, strict: bool = True) -> str | None: ... @@ -25,6 +29,9 @@ def init(files: Sequence[str] | None = None) -> None: ... def read_mime_types(file: str) -> dict[str, str] | None: ... def add_type(type: str, ext: str, strict: bool = True) -> None: ... +if sys.version_info >= (3, 13): + def guess_file_type(path: StrPath, *, strict: bool = True) -> tuple[str | None, str | None]: ... + inited: bool knownfiles: list[str] suffix_map: dict[str, str] @@ -44,3 +51,5 @@ class MimeTypes: def read(self, filename: str, strict: bool = True) -> None: ... def readfp(self, fp: IO[str], strict: bool = True) -> None: ... def read_windows_registry(self, strict: bool = True) -> None: ... + if sys.version_info >= (3, 13): + def guess_file_type(self, path: StrPath, *, strict: bool = True) -> tuple[str | None, str | None]: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 93c4f408e5b6..7688970e5786 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadableBuffer, Unused from collections.abc import Iterable, Iterator, Sized -from typing import NoReturn, overload +from typing import Final, NoReturn, overload from typing_extensions import Self ACCESS_DEFAULT: int @@ -76,6 +76,8 @@ class mmap(Iterable[int], Sized): def __exit__(self, *args: Unused) -> None: ... def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... + if sys.version_info >= (3, 13): + def seekable(self) -> bool: ... if sys.platform != "win32": MADV_NORMAL: int @@ -111,3 +113,9 @@ if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win if sys.version_info >= (3, 10) and sys.platform == "darwin": MADV_FREE_REUSABLE: int MADV_FREE_REUSE: int + +if sys.version_info >= (3, 13) and sys.platform != "win32": + MAP_32BIT: Final = 32768 + +if sys.version_info >= (3, 13) and sys.platform == "darwin": + MAP_TPRO: Final = 524288 diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 9a45a81559c0..605be4686c1f 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -93,16 +93,20 @@ class BaseContext: def Value(self, typecode_or_type: str | type[_CData], *args: Any, lock: bool | _LockLike = True) -> Any: ... @overload def Array( - self, typecode_or_type: type[c_char], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True - ) -> SynchronizedString: ... + self, typecode_or_type: type[_SimpleCData[_T]], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] + ) -> SynchronizedArray[_T]: ... @overload def Array( - self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[False] - ) -> SynchronizedArray[_CT]: ... + self, typecode_or_type: type[c_char], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True + ) -> SynchronizedString: ... @overload def Array( - self, typecode_or_type: type[_CT], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True - ) -> SynchronizedArray[_CT]: ... + self, + typecode_or_type: type[_SimpleCData[_T]], + size_or_initializer: int | Sequence[Any], + *, + lock: Literal[True] | _LockLike = True, + ) -> SynchronizedArray[_T]: ... @overload def Array( self, typecode_or_type: str, size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 9b2d2970112e..5d5b9cdcb913 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -83,6 +83,8 @@ class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): def keys(self) -> list[_KT]: ... # type: ignore[override] def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] def values(self) -> list[_VT]: ... # type: ignore[override] + if sys.version_info >= (3, 13): + def __class_getitem__(cls, args: Any, /) -> Any: ... class BaseListProxy(BaseProxy, MutableSequence[_T]): __builtins__: ClassVar[dict[str, Any]] @@ -117,6 +119,8 @@ class BaseListProxy(BaseProxy, MutableSequence[_T]): class ListProxy(BaseListProxy[_T]): def __iadd__(self, value: Iterable[_T], /) -> Self: ... # type: ignore[override] def __imul__(self, value: SupportsIndex, /) -> Self: ... # type: ignore[override] + if sys.version_info >= (3, 13): + def __class_getitem__(cls, args: Any, /) -> Any: ... # Returned by BaseManager.get_server() class Server: diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi index 0a6b113b194f..b63cedf85867 100644 --- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi @@ -11,7 +11,11 @@ __all__ = ["SharedMemory", "ShareableList"] _SLT = TypeVar("_SLT", int, float, bool, str, bytes, None) class SharedMemory: - def __init__(self, name: str | None = None, create: bool = False, size: int = 0) -> None: ... + if sys.version_info >= (3, 13): + def __init__(self, name: str | None = None, create: bool = False, size: int = 0, *, track: bool = True) -> None: ... + else: + def __init__(self, name: str | None = None, create: bool = False, size: int = 0) -> None: ... + @property def buf(self) -> memoryview: ... @property diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 4093a97e6ca3..2b96ff047470 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -39,12 +39,20 @@ def Array( ) -> _CT: ... @overload def Array( - typecode_or_type: type[_CT], + typecode_or_type: type[c_char], size_or_initializer: int | Sequence[Any], *, lock: Literal[True] | _LockLike = True, ctx: BaseContext | None = None, -) -> SynchronizedArray[_CT]: ... +) -> SynchronizedString: ... +@overload +def Array( + typecode_or_type: type[_SimpleCData[_T]], + size_or_initializer: int | Sequence[Any], + *, + lock: Literal[True] | _LockLike = True, + ctx: BaseContext | None = None, +) -> SynchronizedArray[_T]: ... @overload def Array( typecode_or_type: str, @@ -65,9 +73,11 @@ def copy(obj: _CT) -> _CT: ... @overload def synchronized(obj: _SimpleCData[_T], lock: _LockLike | None = None, ctx: Any | None = None) -> Synchronized[_T]: ... @overload -def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... +def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... # type: ignore @overload -def synchronized(obj: ctypes.Array[_CT], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedArray[_CT]: ... +def synchronized( + obj: ctypes.Array[_SimpleCData[_T]], lock: _LockLike | None = None, ctx: Any | None = None +) -> SynchronizedArray[_T]: ... @overload def synchronized(obj: _CT, lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedBase[_CT]: ... @@ -89,19 +99,30 @@ class SynchronizedBase(Generic[_CT]): class Synchronized(SynchronizedBase[_SimpleCData[_T]], Generic[_T]): value: _T -class SynchronizedArray(SynchronizedBase[ctypes.Array[_CT]], Generic[_CT]): +class SynchronizedArray(SynchronizedBase[ctypes.Array[_SimpleCData[_T]]], Generic[_T]): def __len__(self) -> int: ... @overload - def __getitem__(self, i: slice) -> list[_CT]: ... + def __getitem__(self, i: slice) -> list[_T]: ... @overload - def __getitem__(self, i: int) -> _CT: ... + def __getitem__(self, i: int) -> _T: ... @overload - def __setitem__(self, i: slice, value: Iterable[_CT]) -> None: ... + def __setitem__(self, i: slice, value: Iterable[_T]) -> None: ... @overload - def __setitem__(self, i: int, value: _CT) -> None: ... - def __getslice__(self, start: int, stop: int) -> list[_CT]: ... - def __setslice__(self, start: int, stop: int, values: Iterable[_CT]) -> None: ... + def __setitem__(self, i: int, value: _T) -> None: ... + def __getslice__(self, start: int, stop: int) -> list[_T]: ... + def __setslice__(self, start: int, stop: int, values: Iterable[_T]) -> None: ... + +class SynchronizedString(SynchronizedArray[bytes]): + @overload # type: ignore[override] + def __getitem__(self, i: slice) -> bytes: ... + @overload # type: ignore[override] + def __getitem__(self, i: int) -> bytes: ... + @overload # type: ignore[override] + def __setitem__(self, i: slice, value: bytes) -> None: ... + @overload # type: ignore[override] + def __setitem__(self, i: int, value: bytes) -> None: ... # type: ignore[override] + def __getslice__(self, start: int, stop: int) -> bytes: ... # type: ignore[override] + def __setslice__(self, start: int, stop: int, values: bytes) -> None: ... # type: ignore[override] -class SynchronizedString(SynchronizedArray[c_char]): value: bytes raw: bytes diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 31c5d2aa3ee6..9b00117a5599 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -914,8 +914,8 @@ if sys.platform != "win32": def forkpty() -> tuple[int, int]: ... # some flavors of Unix def killpg(pgid: int, signal: int, /) -> None: ... def nice(increment: int, /) -> int: ... - if sys.platform != "darwin": - def plock(op: int, /) -> None: ... # ???op is int? + if sys.platform != "darwin" and sys.platform != "linux": + def plock(op: int, /) -> None: ... class _wrap_close(_TextIOWrapper): def __init__(self, stream: _TextIOWrapper, proc: Popen[str]) -> None: ... @@ -1141,16 +1141,16 @@ if sys.version_info >= (3, 10) and sys.platform == "linux": if sys.version_info >= (3, 12) and sys.platform == "linux": CLONE_FILES: int CLONE_FS: int - CLONE_NEWCGROUP: int - CLONE_NEWIPC: int - CLONE_NEWNET: int + CLONE_NEWCGROUP: int # Linux 4.6+ + CLONE_NEWIPC: int # Linux 2.6.19+ + CLONE_NEWNET: int # Linux 2.6.24+ CLONE_NEWNS: int - CLONE_NEWPID: int - CLONE_NEWTIME: int - CLONE_NEWUSER: int - CLONE_NEWUTS: int + CLONE_NEWPID: int # Linux 3.8+ + CLONE_NEWTIME: int # Linux 5.6+ + CLONE_NEWUSER: int # Linux 3.8+ + CLONE_NEWUTS: int # Linux 2.6.19+ CLONE_SIGHAND: int - CLONE_SYSVSEM: int + CLONE_SYSVSEM: int # Linux 2.6.26+ CLONE_THREAD: int CLONE_VM: int def unshare(flags: int) -> None: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 0013e221f2e1..c8c8dde0f33e 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -1,4 +1,5 @@ import sys +import types from _typeshed import ( OpenBinaryMode, OpenBinaryModeReading, @@ -14,7 +15,7 @@ from collections.abc import Callable, Generator, Iterator, Sequence from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from os import PathLike, stat_result from types import TracebackType -from typing import IO, Any, BinaryIO, Literal, overload +from typing import IO, Any, BinaryIO, ClassVar, Literal, overload from typing_extensions import Self, deprecated if sys.version_info >= (3, 9): @@ -22,7 +23,14 @@ if sys.version_info >= (3, 9): __all__ = ["PurePath", "PurePosixPath", "PureWindowsPath", "Path", "PosixPath", "WindowsPath"] +if sys.version_info >= (3, 13): + __all__ += ["UnsupportedOperation"] + class PurePath(PathLike[str]): + if sys.version_info >= (3, 13): + parser: ClassVar[types.ModuleType] + def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ... + @property def parts(self) -> tuple[str, ...]: ... @property @@ -94,8 +102,6 @@ class PureWindowsPath(PurePath): ... class Path(PurePath): def __new__(cls, *args: StrPath, **kwargs: Any) -> Self: ... - def __enter__(self) -> Self: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... @classmethod def cwd(cls) -> Self: ... if sys.version_info >= (3, 10): @@ -105,17 +111,38 @@ class Path(PurePath): def stat(self) -> stat_result: ... def chmod(self, mode: int) -> None: ... - if sys.version_info >= (3, 12): - def exists(self, *, follow_symlinks: bool = True) -> bool: ... + if sys.version_info >= (3, 13): + @classmethod + def from_uri(cls, uri: str) -> Path: ... + def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... + def is_file(self, *, follow_symlinks: bool = True) -> bool: ... + def read_text(self, encoding: str | None = None, errors: str | None = None, newline: str | None = None) -> str: ... + else: + def __enter__(self) -> Self: ... + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... + def is_dir(self) -> bool: ... + def is_file(self) -> bool: ... + def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ... + + if sys.version_info >= (3, 13): + def glob( + self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False + ) -> Generator[Self, None, None]: ... + def rglob( + self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False + ) -> Generator[Self, None, None]: ... + elif sys.version_info >= (3, 12): def glob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... def rglob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... else: - def exists(self) -> bool: ... def glob(self, pattern: str) -> Generator[Self, None, None]: ... def rglob(self, pattern: str) -> Generator[Self, None, None]: ... - def is_dir(self) -> bool: ... - def is_file(self) -> bool: ... + if sys.version_info >= (3, 12): + def exists(self, *, follow_symlinks: bool = True) -> bool: ... + else: + def exists(self) -> bool: ... + def is_symlink(self) -> bool: ... def is_socket(self) -> bool: ... def is_fifo(self) -> bool: ... @@ -186,8 +213,12 @@ class Path(PurePath): if sys.platform != "win32": # These methods do "exist" on Windows, but they always raise NotImplementedError, # so it's safer to pretend they don't exist - def owner(self) -> str: ... - def group(self) -> str: ... + if sys.version_info >= (3, 13): + def owner(self, *, follow_symlinks: bool = True) -> str: ... + def group(self, *, follow_symlinks: bool = True) -> str: ... + else: + def owner(self) -> str: ... + def group(self) -> str: ... # This method does "exist" on Windows on <3.12, but always raises NotImplementedError # On py312+, it works properly on Windows, as with all other platforms @@ -212,7 +243,6 @@ class Path(PurePath): def absolute(self) -> Self: ... def expanduser(self) -> Self: ... def read_bytes(self) -> bytes: ... - def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ... def samefile(self, other_path: StrPath) -> bool: ... def write_bytes(self, data: ReadableBuffer) -> int: ... if sys.version_info >= (3, 10): @@ -234,3 +264,6 @@ class Path(PurePath): class PosixPath(Path, PurePosixPath): ... class WindowsPath(Path, PureWindowsPath): ... + +if sys.version_info >= (3, 13): + class UnsupportedOperation(NotImplementedError): ... diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi index f0e6d4123e1d..c47ecdc51df4 100644 --- a/mypy/typeshed/stdlib/platform.pyi +++ b/mypy/typeshed/stdlib/platform.pyi @@ -40,3 +40,28 @@ def platform(aliased: bool = ..., terse: bool = ...) -> str: ... if sys.version_info >= (3, 10): def freedesktop_os_release() -> dict[str, str]: ... + +if sys.version_info >= (3, 13): + class AndroidVer(NamedTuple): + release: str + api_level: int + manufacturer: str + model: str + device: str + is_emulator: bool + + class IOSVersionInfo(NamedTuple): + system: str + release: str + model: str + is_simulator: bool + + def android_ver( + release: str = "", + api_level: int = 0, + manufacturer: str = "", + model: str = "", + device: str = "", + is_emulator: bool = False, + ) -> AndroidVer: ... + def ios_ver(system: str = "", release: str = "", model: str = "", is_simulator: bool = False) -> IOSVersionInfo: ... diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index f6c8a390d85f..dcff18d110bd 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -78,13 +78,25 @@ class _RmtreeType(Protocol): avoids_symlink_attacks: bool if sys.version_info >= (3, 12): @overload - @deprecated("The `onerror` parameter is deprecated and will be removed in Python 3.14. Use `onexc` instead.") + @deprecated("The `onerror` parameter is deprecated. Use `onexc` instead.") + def __call__( + self, + path: StrOrBytesPath, + ignore_errors: bool, + onerror: _OnErrorCallback, + *, + onexc: None = None, + dir_fd: int | None = None, + ) -> None: ... + @overload + @deprecated("The `onerror` parameter is deprecated. Use `onexc` instead.") def __call__( self, path: StrOrBytesPath, ignore_errors: bool = False, - onerror: _OnErrorCallback | None = None, *, + onerror: _OnErrorCallback, + onexc: None = None, dir_fd: int | None = None, ) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/spwd.pyi b/mypy/typeshed/stdlib/spwd.pyi index 67ad3bfc751b..3a5d39997dcc 100644 --- a/mypy/typeshed/stdlib/spwd.pyi +++ b/mypy/typeshed/stdlib/spwd.pyi @@ -36,6 +36,11 @@ if sys.platform != "win32": def sp_expire(self) -> int: ... @property def sp_flag(self) -> int: ... + # Deprecated aliases below. + @property + def sp_nam(self) -> str: ... + @property + def sp_pwd(self) -> str: ... def getspall() -> list[struct_spwd]: ... def getspnam(arg: str, /) -> struct_spwd: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 068ce1514c3c..3cb4b93e88fe 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -428,7 +428,11 @@ class Connection: def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... def executescript(self, sql_script: str, /) -> Cursor: ... def interrupt(self) -> None: ... - def iterdump(self) -> Generator[str, None, None]: ... + if sys.version_info >= (3, 13): + def iterdump(self, *, filter: str | None = None) -> Generator[str, None, None]: ... + else: + def iterdump(self) -> Generator[str, None, None]: ... + def rollback(self) -> None: ... def set_authorizer( self, authorizer_callback: Callable[[int, str | None, str | None, str | None, str | None], int] | None diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index b6fe454eff78..e52099464174 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -142,22 +142,43 @@ class TarFile: errorlevel: int | None offset: int # undocumented extraction_filter: _FilterFunction | None - def __init__( - self, - name: StrOrBytesPath | None = None, - mode: Literal["r", "a", "w", "x"] = "r", - fileobj: _Fileobj | None = None, - format: int | None = None, - tarinfo: type[TarInfo] | None = None, - dereference: bool | None = None, - ignore_zeros: bool | None = None, - encoding: str | None = None, - errors: str = "surrogateescape", - pax_headers: Mapping[str, str] | None = None, - debug: int | None = None, - errorlevel: int | None = None, - copybufsize: int | None = None, # undocumented - ) -> None: ... + if sys.version_info >= (3, 13): + stream: bool + def __init__( + self, + name: StrOrBytesPath | None = None, + mode: Literal["r", "a", "w", "x"] = "r", + fileobj: _Fileobj | None = None, + format: int | None = None, + tarinfo: type[TarInfo] | None = None, + dereference: bool | None = None, + ignore_zeros: bool | None = None, + encoding: str | None = None, + errors: str = "surrogateescape", + pax_headers: Mapping[str, str] | None = None, + debug: int | None = None, + errorlevel: int | None = None, + copybufsize: int | None = None, # undocumented + stream: bool = False, + ) -> None: ... + else: + def __init__( + self, + name: StrOrBytesPath | None = None, + mode: Literal["r", "a", "w", "x"] = "r", + fileobj: _Fileobj | None = None, + format: int | None = None, + tarinfo: type[TarInfo] | None = None, + dereference: bool | None = None, + ignore_zeros: bool | None = None, + encoding: str | None = None, + errors: str = "surrogateescape", + pax_headers: Mapping[str, str] | None = None, + debug: int | None = None, + errorlevel: int | None = None, + copybufsize: int | None = None, # undocumented + ) -> None: ... + def __enter__(self) -> Self: ... def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi index d244d54f2fbf..294a1cb12b63 100644 --- a/mypy/typeshed/stdlib/telnetlib.pyi +++ b/mypy/typeshed/stdlib/telnetlib.pyi @@ -88,6 +88,7 @@ NOOPT: bytes class Telnet: host: str | None # undocumented + sock: socket.socket | None # undocumented def __init__(self, host: str | None = None, port: int = 0, timeout: float = ...) -> None: ... def open(self, host: str, port: int = 0, timeout: float = ...) -> None: ... def msg(self, msg: str, *args: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi index b7962f0751d6..71cdc4d78fdc 100644 --- a/mypy/typeshed/stdlib/time.pyi +++ b/mypy/typeshed/stdlib/time.pyi @@ -27,6 +27,9 @@ if sys.platform != "win32": if sys.platform == "darwin": CLOCK_UPTIME_RAW: int + if sys.version_info >= (3, 13): + CLOCK_UPTIME_RAW_APPROX: int + CLOCK_MONOTONIC_RAW_APPROX: int if sys.version_info >= (3, 9) and sys.platform == "linux": CLOCK_TAI: int @@ -94,7 +97,7 @@ if sys.platform != "win32": def clock_settime(clk_id: int, time: float, /) -> None: ... # Unix only if sys.platform != "win32": - def clock_gettime_ns(clock_id: int, /) -> int: ... + def clock_gettime_ns(clk_id: int, /) -> int: ... def clock_settime_ns(clock_id: int, time: int, /) -> int: ... if sys.platform == "linux": diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 39803003cfe5..075c0f4b9de8 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -3,7 +3,7 @@ from _typeshed import SupportsWrite, Unused from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType from typing import Any, Literal, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import Self, TypeAlias, deprecated __all__ = [ "extract_stack", @@ -85,7 +85,13 @@ def format_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple]) -> # undocumented def print_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple], file: SupportsWrite[str] | None = None) -> None: ... -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 13): + @overload + def format_exception_only(exc: BaseException | None, /, *, show_group: bool = False) -> list[str]: ... + @overload + def format_exception_only(exc: Unused, /, value: BaseException | None, *, show_group: bool = False) -> list[str]: ... + +elif sys.version_info >= (3, 10): @overload def format_exception_only(exc: BaseException | None, /) -> list[str]: ... @overload @@ -111,13 +117,20 @@ class TracebackException: __context__: TracebackException __suppress_context__: bool stack: StackSummary - exc_type: type[BaseException] filename: str lineno: int text: str offset: int msg: str - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + @property + def exc_type_str(self) -> str: ... + @property + @deprecated("Deprecated in 3.13. Use exc_type_str instead.") + def exc_type(self) -> type[BaseException] | None: ... + else: + exc_type: type[BaseException] + if sys.version_info >= (3, 13): def __init__( self, exc_type: type[BaseException], @@ -130,12 +143,15 @@ class TracebackException: compact: bool = False, max_group_width: int = 15, max_group_depth: int = 10, + save_exc_type: bool = True, _seen: set[int] | None = None, ) -> None: ... - @classmethod - def from_exception( - cls, - exc: BaseException, + elif sys.version_info >= (3, 11): + def __init__( + self, + exc_type: type[BaseException], + exc_value: BaseException, + exc_traceback: TracebackType | None, *, limit: int | None = None, lookup_lines: bool = True, @@ -143,7 +159,8 @@ class TracebackException: compact: bool = False, max_group_width: int = 15, max_group_depth: int = 10, - ) -> Self: ... + _seen: set[int] | None = None, + ) -> None: ... elif sys.version_info >= (3, 10): def __init__( self, @@ -157,6 +174,20 @@ class TracebackException: compact: bool = False, _seen: set[int] | None = None, ) -> None: ... + else: + def __init__( + self, + exc_type: type[BaseException], + exc_value: BaseException, + exc_traceback: TracebackType | None, + *, + limit: int | None = None, + lookup_lines: bool = True, + capture_locals: bool = False, + _seen: set[int] | None = None, + ) -> None: ... + + if sys.version_info >= (3, 11): @classmethod def from_exception( cls, @@ -166,19 +197,21 @@ class TracebackException: lookup_lines: bool = True, capture_locals: bool = False, compact: bool = False, + max_group_width: int = 15, + max_group_depth: int = 10, ) -> Self: ... - else: - def __init__( - self, - exc_type: type[BaseException], - exc_value: BaseException, - exc_traceback: TracebackType | None, + elif sys.version_info >= (3, 10): + @classmethod + def from_exception( + cls, + exc: BaseException, *, limit: int | None = None, lookup_lines: bool = True, capture_locals: bool = False, - _seen: set[int] | None = None, - ) -> None: ... + compact: bool = False, + ) -> Self: ... + else: @classmethod def from_exception( cls, exc: BaseException, *, limit: int | None = None, lookup_lines: bool = True, capture_locals: bool = False @@ -190,7 +223,10 @@ class TracebackException: else: def format(self, *, chain: bool = True) -> Generator[str, None, None]: ... - def format_exception_only(self) -> Generator[str, None, None]: ... + if sys.version_info >= (3, 13): + def format_exception_only(self, *, show_group: bool = False, _depth: int = 0) -> Generator[str, None, None]: ... + else: + def format_exception_only(self) -> Generator[str, None, None]: ... if sys.version_info >= (3, 11): def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 93cb89046366..9e9dc56b8529 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -81,7 +81,7 @@ class FunctionType: __name__: str __qualname__: str __annotations__: dict[str, Any] - __kwdefaults__: dict[str, Any] + __kwdefaults__: dict[str, Any] | None if sys.version_info >= (3, 10): @property def __builtins__(self) -> dict[str, Any]: ... @@ -358,6 +358,8 @@ class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): ) -> _YieldT_co: ... @overload def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... + if sys.version_info >= (3, 13): + def __class_getitem__(cls, item: Any, /) -> Any: ... @final class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @@ -401,6 +403,8 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): ) -> _YieldT_co: ... @overload def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... + if sys.version_info >= (3, 13): + def __class_getitem__(cls, item: Any, /) -> Any: ... @final class MethodType: @@ -587,6 +591,9 @@ if sys.version_info >= (3, 9): def __unpacked__(self) -> bool: ... @property def __typing_unpacked_tuple_args__(self) -> tuple[Any, ...] | None: ... + if sys.version_info >= (3, 10): + def __or__(self, value: Any, /) -> UnionType: ... + def __ror__(self, value: Any, /) -> UnionType: ... # GenericAlias delegates attr access to `__origin__` def __getattr__(self, name: str) -> Any: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 1b021d1eecbd..92427f91f022 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -21,7 +21,7 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing_extensions import Never as _Never, ParamSpec as _ParamSpec +from typing_extensions import Never as _Never, ParamSpec as _ParamSpec, deprecated if sys.version_info >= (3, 9): from types import GenericAlias @@ -129,7 +129,7 @@ if sys.version_info >= (3, 12): __all__ += ["TypeAliasType", "override"] if sys.version_info >= (3, 13): - __all__ += ["get_protocol_members", "is_protocol", "NoDefault"] + __all__ += ["get_protocol_members", "is_protocol", "NoDefault", "TypeIs", "ReadOnly"] Any = object() @@ -183,6 +183,7 @@ class TypeVar: if sys.version_info >= (3, 11): def __typing_subst__(self, arg: Any) -> Any: ... if sys.version_info >= (3, 13): + def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... def has_default(self) -> bool: ... # Used for an undocumented mypy feature. Does not exist at runtime. @@ -989,7 +990,35 @@ class ForwardRef: else: def __init__(self, arg: str, is_argument: bool = True) -> None: ... - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 13): + @overload + @deprecated( + "Failing to pass a value to the 'type_params' parameter of ForwardRef._evaluate() is deprecated, " + "as it leads to incorrect behaviour when evaluating a stringified annotation " + "that references a PEP 695 type parameter. It will be disallowed in Python 3.15." + ) + def _evaluate( + self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, *, recursive_guard: frozenset[str] + ) -> Any | None: ... + @overload + def _evaluate( + self, + globalns: dict[str, Any] | None, + localns: dict[str, Any] | None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...], + *, + recursive_guard: frozenset[str], + ) -> Any | None: ... + elif sys.version_info >= (3, 12): + def _evaluate( + self, + globalns: dict[str, Any] | None, + localns: dict[str, Any] | None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, + *, + recursive_guard: frozenset[str], + ) -> Any | None: ... + elif sys.version_info >= (3, 9): def _evaluate( self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, recursive_guard: frozenset[str] ) -> Any | None: ... @@ -1036,3 +1065,5 @@ if sys.version_info >= (3, 13): class _NoDefaultType: ... NoDefault: _NoDefaultType + TypeIs: _SpecialForm + ReadOnly: _SpecialForm diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 73fd2dc8cbb3..a7d2b2c2e083 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -419,6 +419,8 @@ if sys.version_info >= (3, 13): from typing import ( NoDefault as NoDefault, ParamSpec as ParamSpec, + ReadOnly as ReadOnly, + TypeIs as TypeIs, TypeVar as TypeVar, TypeVarTuple as TypeVarTuple, get_protocol_members as get_protocol_members, @@ -520,11 +522,11 @@ else: def has_default(self) -> bool: ... def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + ReadOnly: _SpecialForm + TypeIs: _SpecialForm + class Doc: documentation: str def __init__(self, documentation: str, /) -> None: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... - -ReadOnly: _SpecialForm -TypeIs: _SpecialForm diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index e345124237da..aaba7ffc98d9 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -41,7 +41,10 @@ _P = ParamSpec("_P") ProxyTypes: tuple[type[Any], ...] class WeakMethod(ref[_CallableT]): - def __new__(cls, meth: _CallableT, callback: Callable[[Self], object] | None = None) -> Self: ... + # `ref` is implemented in `C` so positional-only arguments are enforced, but not in `WeakMethod`. + def __new__( # pyright: ignore[reportInconsistentConstructor] + cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None + ) -> Self: ... def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/xml/sax/handler.pyi b/mypy/typeshed/stdlib/xml/sax/handler.pyi index 30fe31d51374..7b7c69048efd 100644 --- a/mypy/typeshed/stdlib/xml/sax/handler.pyi +++ b/mypy/typeshed/stdlib/xml/sax/handler.pyi @@ -14,7 +14,7 @@ class ContentHandler: def startDocument(self) -> None: ... def endDocument(self) -> None: ... def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... - def endPrefixMapping(self, prefix) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... From c4470f1a5b52c01b09c116f28b7ee12b658f746b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Jun 2024 18:02:26 +0100 Subject: [PATCH 0653/1617] Make more type expressions valid in PEP 695 aliases and runtime contexts (#17404) Previously some type expressions, when used as the value of a PEP 695 type alias or in an expression context, generated errors, even if the code would work at runtime. Improve type inference of types in expression contexts (this includes PEP 695 type aliases) to better reflect runtime behavior. This is still not perfect, since we don't have precise types for everything in stubs. Use `typing._SpecialForm` as a fallback, as it supports indexing and `|` operations, which are supported for types. Also update stubs used in tests to better match typeshed stubs. In particular, provide `_SpecialForm` and define `Any = object()`, similar to typeshed. --- mypy/checkexpr.py | 26 ++++-- mypy/stubgenc.py | 2 +- mypyc/test-data/fixtures/typing-full.pyi | 6 +- test-data/unit/check-classes.test | 3 + test-data/unit/check-functions.test | 5 +- test-data/unit/check-generics.test | 7 +- test-data/unit/check-python312.test | 52 +++++++++++ .../check-type-object-type-inference.test | 3 +- test-data/unit/fixtures/typing-async.pyi | 4 +- test-data/unit/fixtures/typing-full.pyi | 7 +- test-data/unit/fixtures/typing-medium.pyi | 2 +- test-data/unit/fixtures/typing-namedtuple.pyi | 4 +- test-data/unit/fixtures/typing-override.pyi | 5 +- .../unit/fixtures/typing-typeddict-iror.pyi | 4 +- test-data/unit/fixtures/typing-typeddict.pyi | 4 +- test-data/unit/lib-stub/types.pyi | 4 +- test-data/unit/lib-stub/typing.pyi | 4 +- test-data/unit/pythoneval.test | 86 ++++++++++++++++++- 18 files changed, 196 insertions(+), 32 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 1cea4f6c19e6..734a9e1687bd 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -428,6 +428,9 @@ def analyze_var_ref(self, var: Var, context: Context) -> Type: if var.type: var_type = get_proper_type(var.type) if isinstance(var_type, Instance): + if var.fullname == "typing.Any": + # The typeshed type is 'object'; give a more useful type in runtime context + return self.named_type("typing._SpecialForm") if self.is_literal_context() and var_type.last_known_value is not None: return var_type.last_known_value if var.name in {"True", "False"}: @@ -4331,16 +4334,25 @@ def visit_index_with_type( return self.nonliteral_tuple_index_helper(left_type, index) elif isinstance(left_type, TypedDictType): return self.visit_typeddict_index_expr(left_type, e.index) - elif ( - isinstance(left_type, FunctionLike) - and left_type.is_type_obj() - and left_type.type_object().is_enum - ): - return self.visit_enum_index_expr(left_type.type_object(), e.index, e) - elif isinstance(left_type, TypeVarType) and not self.has_member( + elif isinstance(left_type, FunctionLike) and left_type.is_type_obj(): + if left_type.type_object().is_enum: + return self.visit_enum_index_expr(left_type.type_object(), e.index, e) + elif left_type.type_object().type_vars: + return self.named_type("types.GenericAlias") + elif ( + left_type.type_object().fullname == "builtins.type" + and self.chk.options.python_version >= (3, 9) + ): + # builtins.type is special: it's not generic in stubs, but it supports indexing + return self.named_type("typing._SpecialForm") + + if isinstance(left_type, TypeVarType) and not self.has_member( left_type.upper_bound, "__getitem__" ): return self.visit_index_with_type(left_type.upper_bound, e, original_type) + elif isinstance(left_type, Instance) and left_type.type.fullname == "typing._SpecialForm": + # Allow special forms to be indexed and used to create union types + return self.named_type("typing._SpecialForm") else: result, method_type = self.check_method_call_by_name( "__getitem__", left_type, [e.index], [ARG_POS], e, original_type=original_type diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 9acd3f171a41..bacb68f6d1c7 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -733,7 +733,7 @@ def generate_property_stub( def get_type_fullname(self, typ: type) -> str: """Given a type, return a string representation""" - if typ is Any: + if typ is Any: # type: ignore[comparison-overlap] return "Any" typename = getattr(typ, "__qualname__", typ.__name__) module_name = self.get_obj_module(typ) diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi index 8bb3b1398f87..6b6aba6802b1 100644 --- a/mypyc/test-data/fixtures/typing-full.pyi +++ b/mypyc/test-data/fixtures/typing-full.pyi @@ -15,8 +15,7 @@ class _SpecialForm: cast = 0 overload = 0 -Any = 0 -Union = 0 +Any = object() Optional = 0 TypeVar = 0 Generic = 0 @@ -28,11 +27,12 @@ Type = 0 no_type_check = 0 ClassVar = 0 Final = 0 -Literal = 0 TypedDict = 0 NoReturn = 0 NewType = 0 Callable: _SpecialForm +Union: _SpecialForm +Literal: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index e66eab5e2927..82208d27df41 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4790,12 +4790,15 @@ def g(x: Type[S]) -> str: return reveal_type(x * 0) # N: Revealed type is "builtins.str" [case testMetaclassGetitem] +import types + class M(type): def __getitem__(self, key) -> int: return 1 class A(metaclass=M): pass reveal_type(A[M]) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] [case testMetaclassSelfType] from typing import TypeVar, Type diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index ef6ca9f3b285..29cd977fe5d6 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1779,10 +1779,10 @@ def Arg(x, y): pass F = Callable[[Arg(int, 'x')], int] # E: Invalid argument constructor "__main__.Arg" [case testCallableParsingFromExpr] - from typing import Callable, List from mypy_extensions import Arg, VarArg, KwArg import mypy_extensions +import types # Needed for type checking def WrongArg(x, y): return y # Note that for this test, the 'Value of type "int" is not indexable' errors are silly, @@ -1799,11 +1799,10 @@ L = Callable[[Arg(name='x', type=int)], int] # ok # I have commented out the following test because I don't know how to expect the "defined here" note part of the error. # M = Callable[[Arg(gnome='x', type=int)], int] E: Invalid type alias: expression is not a valid type E: Unexpected keyword argument "gnome" for "Arg" N = Callable[[Arg(name=None, type=int)], int] # ok -O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: Type expected within [...] # E: The type "Type[List[Any]]" is not generic and not indexable +O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: Type expected within [...] P = Callable[[mypy_extensions.VarArg(int)], int] # ok Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "type" R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "name" - [builtins fixtures/dict.pyi] [case testCallableParsing] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index d46d19946098..b8cc0422b749 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -454,11 +454,13 @@ A[int, str, int]() # E: Type application has too many types (2 expected) [out] [case testInvalidTypeApplicationType] +import types a: A class A: pass a[A]() # E: Value of type "A" is not indexable A[A]() # E: The type "Type[A]" is not generic and not indexable -[out] +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testTypeApplicationArgTypes] from typing import TypeVar, Generic @@ -513,8 +515,9 @@ Alias[int]("a") # E: Argument 1 to "Node" has incompatible type "str"; expected [out] [case testTypeApplicationCrash] +import types type[int] # this was crashing, see #2302 (comment) # E: The type "Type[type]" is not generic and not indexable -[out] +[builtins fixtures/tuple.pyi] -- Generic type aliases diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 348f2d11f9a7..7c3d565b1b44 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1591,3 +1591,55 @@ c: E[str] d: E[int] # E: Type argument "int" of "E" must be a subtype of "str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasWithDifferentTargetTypes] +# flags: --enable-incomplete-feature=NewGenericSyntax +import types # We need GenericAlias from here, and test stubs don't bring in 'types' +from typing import Any, Callable, List, Literal, TypedDict + +# Test that various type expressions don't generate false positives as type alias +# values, as they are type checked as expressions. There is a similar test case in +# pythoneval.test that uses typeshed stubs. + +class C[T]: pass + +class TD(TypedDict): + x: int + +type A1 = type[int] +type A2 = type[int] | None +type A3 = None | type[int] +type A4 = type[Any] + +type B1[**P, R] = Callable[P, R] | None +type B2[**P, R] = None | Callable[P, R] +type B3 = Callable[[str], int] +type B4 = Callable[..., int] + +type C1 = A1 | None +type C2 = None | A1 + +type D1 = Any | None +type D2 = None | Any + +type E1 = List[int] +type E2 = List[int] | None +type E3 = None | List[int] + +type F1 = Literal[1] +type F2 = Literal['x'] | None +type F3 = None | Literal[True] + +type G1 = tuple[int, Any] +type G2 = tuple[int, Any] | None +type G3 = None | tuple[int, Any] + +type H1 = TD +type H2 = TD | None +type H3 = None | TD + +type I1 = C[int] +type I2 = C[Any] | None +type I3 = None | C[TD] +[builtins fixtures/type.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-type-object-type-inference.test b/test-data/unit/check-type-object-type-inference.test index baeca1e22ac7..5a4afa0c9248 100644 --- a/test-data/unit/check-type-object-type-inference.test +++ b/test-data/unit/check-type-object-type-inference.test @@ -2,6 +2,7 @@ # flags: --python-version 3.9 from typing import TypeVar, Generic, Type from abc import abstractmethod +import types # Explicitly bring in stubs for 'types' T = TypeVar('T') class E(Generic[T]): @@ -37,5 +38,5 @@ def i(f: F): f.f(tuple[int,tuple[int,str]]).e( (27,(28,'z')) ) # OK reveal_type(f.f(tuple[int,tuple[int,str]]).e) # N: Revealed type is "def (t: Tuple[builtins.int, Tuple[builtins.int, builtins.str]]) -> builtins.str" -x = tuple[int,str][str] # E: The type "Type[Tuple[Any, ...]]" is not generic and not indexable +x = tuple[int,str][str] # False negative [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/fixtures/typing-async.pyi b/test-data/unit/fixtures/typing-async.pyi index 9897dfd0b270..03728f822316 100644 --- a/test-data/unit/fixtures/typing-async.pyi +++ b/test-data/unit/fixtures/typing-async.pyi @@ -10,7 +10,7 @@ from abc import abstractmethod, ABCMeta cast = 0 overload = 0 -Any = 0 +Any = object() Union = 0 Optional = 0 TypeVar = 0 @@ -125,3 +125,5 @@ class AsyncContextManager(Generic[T]): def __aenter__(self) -> Awaitable[T]: pass # Use Any because not all the precise types are in the fixtures. def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Awaitable[Any]: pass + +class _SpecialForm: pass diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 9d61361fc16e..8e0116aab1c2 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -12,6 +12,8 @@ class GenericMeta(type): pass class _SpecialForm: def __getitem__(self, index: Any) -> Any: ... + def __or__(self, other): ... + def __ror__(self, other): ... class TypeVar: def __init__(self, name, *args, bound=None): ... def __or__(self, other): ... @@ -21,7 +23,7 @@ class TypeVarTuple: ... def cast(t, o): ... def assert_type(o, t): ... overload = 0 -Any = 0 +Any = object() Optional = 0 Generic = 0 Protocol = 0 @@ -31,7 +33,6 @@ Type = 0 no_type_check = 0 ClassVar = 0 Final = 0 -Literal = 0 TypedDict = 0 NoReturn = 0 NewType = 0 @@ -39,6 +40,7 @@ Self = 0 Unpack = 0 Callable: _SpecialForm Union: _SpecialForm +Literal: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -216,3 +218,4 @@ class TypeAliasType: ) -> None: ... def __or__(self, other: Any) -> Any: ... + def __ror__(self, other: Any) -> Any: ... diff --git a/test-data/unit/fixtures/typing-medium.pyi b/test-data/unit/fixtures/typing-medium.pyi index c19c5d5d96e2..c722a9ddb12c 100644 --- a/test-data/unit/fixtures/typing-medium.pyi +++ b/test-data/unit/fixtures/typing-medium.pyi @@ -8,7 +8,7 @@ cast = 0 overload = 0 -Any = 0 +Any = object() Union = 0 Optional = 0 TypeVar = 0 diff --git a/test-data/unit/fixtures/typing-namedtuple.pyi b/test-data/unit/fixtures/typing-namedtuple.pyi index f4744575fc09..bcdcfc44c3d2 100644 --- a/test-data/unit/fixtures/typing-namedtuple.pyi +++ b/test-data/unit/fixtures/typing-namedtuple.pyi @@ -1,6 +1,6 @@ TypeVar = 0 Generic = 0 -Any = 0 +Any = object() overload = 0 Type = 0 Literal = 0 @@ -26,3 +26,5 @@ class NamedTuple(tuple[Any, ...]): def __init__(self, typename: str, fields: Iterable[tuple[str, Any]] = ...) -> None: ... @overload def __init__(self, typename: str, fields: None = None, **kwargs: Any) -> None: ... + +class _SpecialForm: pass diff --git a/test-data/unit/fixtures/typing-override.pyi b/test-data/unit/fixtures/typing-override.pyi index 606ca63d4f0d..e9d2dfcf55c4 100644 --- a/test-data/unit/fixtures/typing-override.pyi +++ b/test-data/unit/fixtures/typing-override.pyi @@ -1,6 +1,6 @@ TypeVar = 0 Generic = 0 -Any = 0 +Any = object() overload = 0 Type = 0 Literal = 0 @@ -21,5 +21,6 @@ class Mapping(Iterable[KT], Generic[KT, T_co]): def keys(self) -> Iterable[T]: pass # Approximate return type def __getitem__(self, key: T) -> T_co: pass - def override(__arg: T) -> T: ... + +class _SpecialForm: pass diff --git a/test-data/unit/fixtures/typing-typeddict-iror.pyi b/test-data/unit/fixtures/typing-typeddict-iror.pyi index e452c8497109..845ac6cf208f 100644 --- a/test-data/unit/fixtures/typing-typeddict-iror.pyi +++ b/test-data/unit/fixtures/typing-typeddict-iror.pyi @@ -12,7 +12,7 @@ from abc import ABCMeta cast = 0 assert_type = 0 overload = 0 -Any = 0 +Any = object() Union = 0 Optional = 0 TypeVar = 0 @@ -64,3 +64,5 @@ class _TypedDict(Mapping[str, object]): def __ror__(self, __value: dict[str, Any]) -> dict[str, object]: ... # supposedly incompatible definitions of __or__ and __ior__ def __ior__(self, __value: Self) -> Self: ... # type: ignore[misc] + +class _SpecialForm: pass diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index 24a2f1328981..d136ac4ab8be 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -11,7 +11,7 @@ from abc import ABCMeta cast = 0 assert_type = 0 overload = 0 -Any = 0 +Any = object() Union = 0 Optional = 0 TypeVar = 0 @@ -71,3 +71,5 @@ class _TypedDict(Mapping[str, object]): def pop(self, k: NoReturn, default: T = ...) -> object: ... def update(self: T, __m: T) -> None: ... def __delitem__(self, k: NoReturn) -> None: ... + +class _SpecialForm: pass diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index c3ac244c2a51..3f713c31e417 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -9,7 +9,9 @@ class ModuleType: __file__: str def __getattr__(self, name: str) -> Any: pass -class GenericAlias: ... +class GenericAlias: + def __or__(self, o): ... + def __ror__(self, o): ... if sys.version_info >= (3, 10): class NoneType: diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index 5f458ca687c0..3cb164140883 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -11,7 +11,7 @@ cast = 0 assert_type = 0 overload = 0 -Any = 0 +Any = object() Union = 0 Optional = 0 TypeVar = 0 @@ -63,3 +63,5 @@ class Coroutine(Awaitable[V], Generic[T, U, V]): pass def final(meth: T) -> T: pass def reveal_type(__obj: T) -> T: pass + +class _SpecialForm: pass diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 3bf8613d2478..222430c3ef55 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1606,8 +1606,8 @@ class Foo(Enum): Bar: Foo = Callable[[str], None] Baz: Foo = Callable[[Dict[str, "Missing"]], None] [out] -_testEnumValueWithPlaceholderNodeType.py:5: error: Incompatible types in assignment (expression has type "object", variable has type "Foo") -_testEnumValueWithPlaceholderNodeType.py:6: error: Incompatible types in assignment (expression has type "object", variable has type "Foo") +_testEnumValueWithPlaceholderNodeType.py:5: error: Incompatible types in assignment (expression has type "", variable has type "Foo") +_testEnumValueWithPlaceholderNodeType.py:6: error: Incompatible types in assignment (expression has type "", variable has type "Foo") _testEnumValueWithPlaceholderNodeType.py:6: error: Name "Missing" is not defined [case testTypeshedRecursiveTypesExample] @@ -1781,9 +1781,9 @@ C = str | int D: TypeAlias = str | int [out] _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: The type "Type[type]" is not generic and not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("") _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: The type "Type[type]" is not generic and not indexable +_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Unsupported left operand type for | ("Type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type @@ -2120,3 +2120,81 @@ def func( a2 = action # Error [out] _testPEP695VarianceInference.py:17: error: Incompatible types in assignment (expression has type "Job[None]", variable has type "Job[int]") + +[case testPEP695TypeAliasWithDifferentTargetTypes] +# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +from typing import Any, Callable, List, Literal, TypedDict, overload, TypeAlias, TypeVar, Never + +class C[T]: pass + +class O[T]: + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, x: int) -> None: ... + def __init__(self, x: int = 0) -> None: + pass + +class TD(TypedDict): + x: int + +S = TypeVar("S") +A = list[S] +B: TypeAlias = list[S] + +type A1 = type[int] +type A2 = type[int] | None +type A3 = None | type[int] +type A4 = type[Any] +type A5 = type[C] | None +type A6 = None | type[C] +type A7 = type[O] | None +type A8 = None | type[O] + +type B1[**P, R] = Callable[P, R] | None +type B2[**P, R] = None | Callable[P, R] +type B3 = Callable[[str], int] +type B4 = Callable[..., int] + +type C1 = A1 | None +type C2 = None | A1 + +type D1 = Any | None +type D2 = None | Any + +type E1 = List[int] +type E2 = List[int] | None +type E3 = None | List[int] + +type F1 = Literal[1] +type F2 = Literal['x'] | None +type F3 = None | Literal[True] + +type G1 = tuple[int, Any] +type G2 = tuple[int, Any] | None +type G3 = None | tuple[int, Any] + +type H1 = TD +type H2 = TD | None +type H3 = None | TD + +type I1 = C[int] +type I2 = C[Any] | None +type I3 = None | C[TD] +type I4 = O[int] | None +type I5 = None | O[int] + +type J1[T] = T | None +type J2[T] = None | T +type J3[*Ts] = tuple[*Ts] +type J4[T] = J1[T] | None +type J5[T] = None | J1[T] +type J6[*Ts] = J3[*Ts] | None + +type K1 = A[int] | None +type K2 = None | A[int] +type K3 = B[int] | None +type K4 = None | B[int] + +type L1 = Never +type L2 = list[Never] From f9d8f3ae9e4454777e0dd44380ba57bff7ef8ca2 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 20 Jun 2024 18:57:31 +0100 Subject: [PATCH 0654/1617] Fix self-referential upper bound in new-style type variables (#17407) Fixes https://github.com/python/mypy/issues/17347 This copies old-style `TypeVar` logic 1:1 (I know it is ugly, but I don't think there is anything better now). Also while I am touching this code, I am removing `third_pass` argument (third pass is not a thing for ~5 years now). --- mypy/plugin.py | 1 - mypy/semanal.py | 13 ++++++------- test-data/unit/check-python312.test | 13 +++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/mypy/plugin.py b/mypy/plugin.py index 38016191de8f..858795addb7f 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -328,7 +328,6 @@ def anal_type( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, report_invalid_types: bool = True, - third_pass: bool = False, ) -> Type | None: """Analyze an unbound type. diff --git a/mypy/semanal.py b/mypy/semanal.py index c7a22d20aac6..f857c3e73381 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1738,10 +1738,12 @@ def analyze_type_param( ) -> TypeVarLikeExpr | None: fullname = self.qualified_name(type_param.name) if type_param.upper_bound: - upper_bound = self.anal_type(type_param.upper_bound) + upper_bound = self.anal_type(type_param.upper_bound, allow_placeholder=True) # TODO: we should validate the upper bound is valid for a given kind. if upper_bound is None: - return None + # This and below copies special-casing for old-style type variables, that + # is equally necessary for new-style classes to break a vicious circle. + upper_bound = PlaceholderType(None, [], context.line) else: if type_param.kind == TYPE_VAR_TUPLE_KIND: upper_bound = self.named_type("builtins.tuple", [self.object_type()]) @@ -1752,9 +1754,9 @@ def analyze_type_param( values = [] if type_param.values: for value in type_param.values: - analyzed = self.anal_type(value) + analyzed = self.anal_type(value, allow_placeholder=True) if analyzed is None: - return None + analyzed = PlaceholderType(None, [], context.line) values.append(analyzed) return TypeVarExpr( name=type_param.name, @@ -7192,7 +7194,6 @@ def anal_type( report_invalid_types: bool = True, prohibit_self_type: str | None = None, allow_type_any: bool = False, - third_pass: bool = False, ) -> Type | None: """Semantically analyze a type. @@ -7200,8 +7201,6 @@ def anal_type( typ: Type to analyze (if already analyzed, this is a no-op) allow_placeholder: If True, may return PlaceholderType if encountering an incomplete definition - third_pass: Unused; only for compatibility with old semantic - analyzer Return None only if some part of the type couldn't be bound *and* it referred to an incomplete namespace or definition. In this case also diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 7c3d565b1b44..27027d30a684 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1592,6 +1592,19 @@ d: E[int] # E: Type argument "int" of "E" must be a subtype of "str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] +[case testCurrentClassWorksAsBound] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import Protocol + +class Comparable[T: Comparable](Protocol): + def compare(self, other: T) -> bool: ... + +class Good: + def compare(self, other: Good) -> bool: ... + +x: Comparable[Good] +y: Comparable[int] # E: Type argument "int" of "Comparable" must be a subtype of "Comparable[Any]" + [case testPEP695TypeAliasWithDifferentTargetTypes] # flags: --enable-incomplete-feature=NewGenericSyntax import types # We need GenericAlias from here, and test stubs don't bring in 'types' From de4e9d612a7633b3a7d992ced5c15dbd47310296 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 21 Jun 2024 02:26:18 -0700 Subject: [PATCH 0655/1617] Fix isinstance checks with PEP 604 unions containing None (#17415) Fixes #17413 --- mypy/checker.py | 4 ++++ test-data/unit/check-union-or-syntax.test | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 3a7f231ebf1d..d2562d5dd722 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7316,7 +7316,11 @@ def is_writable_attribute(self, node: Node) -> bool: def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: if isinstance(expr, OpExpr) and expr.op == "|": left = self.get_isinstance_type(expr.left) + if left is None and is_literal_none(expr.left): + left = [TypeRange(NoneType(), is_upper_bound=False)] right = self.get_isinstance_type(expr.right) + if right is None and is_literal_none(expr.right): + right = [TypeRange(NoneType(), is_upper_bound=False)] if left is None or right is None: return None return left + right diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index a1b63077eef9..fcf679fff401 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -226,6 +226,17 @@ isinstance(5, str | list[str]) isinstance(5, ParameterizedAlias) [builtins fixtures/type.pyi] +[case testIsInstanceUnionNone] +# flags: --python-version 3.10 +def foo(value: str | bool | None): + assert not isinstance(value, str | None) + reveal_type(value) # N: Revealed type is "builtins.bool" + +def bar(value: object): + assert isinstance(value, str | None) + reveal_type(value) # N: Revealed type is "Union[builtins.str, None]" +[builtins fixtures/type.pyi] + # TODO: Get this test to pass [case testImplicit604TypeAliasWithCyclicImportNotInStub-xfail] From cc3492e45931d59666508ce81748dcdaa8ac436e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:38:30 -0700 Subject: [PATCH 0656/1617] Fix error reporting on cached run after uninstallation of third party library (#17420) Fixes https://github.com/python/mypy/issues/16766, fixes https://github.com/python/mypy/issues/17049 --- mypy/build.py | 7 +++++-- mypy/errors.py | 2 +- test-data/unit/check-incremental.test | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 3ceb473f0948..733f0685792e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -3467,8 +3467,11 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No for id in stale: graph[id].transitive_error = True for id in stale: - errors = manager.errors.file_messages(graph[id].xpath, formatter=manager.error_formatter) - manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False) + if graph[id].xpath not in manager.errors.ignored_files: + errors = manager.errors.file_messages( + graph[id].xpath, formatter=manager.error_formatter + ) + manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False) graph[id].write_cache() graph[id].mark_as_rechecked() diff --git a/mypy/errors.py b/mypy/errors.py index 7a937da39c20..d6dcd4e49e13 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -803,7 +803,7 @@ def blocker_module(self) -> str | None: def is_errors_for_file(self, file: str) -> bool: """Are there any errors for the given file?""" - return file in self.error_info_map + return file in self.error_info_map and file not in self.ignored_files def prefer_simple_messages(self) -> bool: """Should we generate simple/fast error messages? diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index ead896b8e458..24292bce3e21 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1833,6 +1833,21 @@ main:3: note: Revealed type is "builtins.int" main:3: note: Revealed type is "Any" +[case testIncrementalIgnoreErrors] +# flags: --config-file tmp/mypy.ini +import a +[file a.py] +import module_that_will_be_deleted +[file module_that_will_be_deleted.py] + +[file mypy.ini] +\[mypy] +\[mypy-a] +ignore_errors = True +[delete module_that_will_be_deleted.py.2] +[out1] +[out2] + [case testIncrementalNamedTupleInMethod] from ntcrash import nope [file ntcrash.py] From 9012fc9e954cb2ee0affd049f7c91b39c8fbc8e8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 22 Jun 2024 22:59:56 +0100 Subject: [PATCH 0657/1617] Some cleanup in partial plugin (#17423) Fixes https://github.com/python/mypy/issues/17405 Apart from fixing the crash I fix two obvious bugs I noticed while making this PR. --- mypy/checkexpr.py | 2 ++ mypy/plugins/functools.py | 31 +++++++++++++---- test-data/unit/check-functools.test | 52 +++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 734a9e1687bd..7ae23cfe516c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1228,6 +1228,8 @@ def apply_function_plugin( formal_arg_exprs[formal].append(args[actual]) if arg_names: formal_arg_names[formal].append(arg_names[actual]) + else: + formal_arg_names[formal].append(None) formal_arg_kinds[formal].append(arg_kinds[actual]) if object_type is None: diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 335123a4a108..4f2ed6f2361d 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -17,7 +17,6 @@ Type, TypeOfAny, UnboundType, - UninhabitedType, get_proper_type, ) @@ -132,6 +131,9 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: if fn_type is None: return ctx.default_return_type + # We must normalize from the start to have coherent view together with TypeChecker. + fn_type = fn_type.with_unpacked_kwargs().with_normalized_var_args() + defaulted = fn_type.copy_modified( arg_kinds=[ ( @@ -146,10 +148,25 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: # Make up a line number if we don't have one defaulted.set_line(ctx.default_return_type) - actual_args = [a for param in ctx.args[1:] for a in param] - actual_arg_kinds = [a for param in ctx.arg_kinds[1:] for a in param] - actual_arg_names = [a for param in ctx.arg_names[1:] for a in param] - actual_types = [a for param in ctx.arg_types[1:] for a in param] + # Flatten actual to formal mapping, since this is what check_call() expects. + actual_args = [] + actual_arg_kinds = [] + actual_arg_names = [] + actual_types = [] + seen_args = set() + for i, param in enumerate(ctx.args[1:], start=1): + for j, a in enumerate(param): + if a in seen_args: + # Same actual arg can map to multiple formals, but we need to include + # each one only once. + continue + # Here we rely on the fact that expressions are essentially immutable, so + # they can be compared by identity. + seen_args.add(a) + actual_args.append(a) + actual_arg_kinds.append(ctx.arg_kinds[i][j]) + actual_arg_names.append(ctx.arg_names[i][j]) + actual_types.append(ctx.arg_types[i][j]) # Create a valid context for various ad-hoc inspections in check_call(). call_expr = CallExpr( @@ -188,7 +205,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: for i, actuals in enumerate(formal_to_actual): if len(bound.arg_types) == len(fn_type.arg_types): arg_type = bound.arg_types[i] - if isinstance(get_proper_type(arg_type), UninhabitedType): + if not mypy.checker.is_valid_inferred_type(arg_type): arg_type = fn_type.arg_types[i] # bit of a hack else: # TODO: I assume that bound and fn_type have the same arguments. It appears this isn't @@ -210,7 +227,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: partial_names.append(fn_type.arg_names[i]) ret_type = bound.ret_type - if isinstance(get_proper_type(ret_type), UninhabitedType): + if not mypy.checker.is_valid_inferred_type(ret_type): ret_type = fn_type.ret_type # same kind of hack as above partially_applied = fn_type.copy_modified( diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 283500f25a7d..79ae962a73e0 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -372,3 +372,55 @@ def foo(cls3: Type[B[T]]): reveal_type(functools.partial(cls3, 2)()) # N: Revealed type is "__main__.B[T`-1]" \ # E: Argument 1 to "B" has incompatible type "int"; expected "T" [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypedDictUnpack] +from typing_extensions import TypedDict, Unpack +from functools import partial + +class Data(TypedDict, total=False): + x: int + +def f(**kwargs: Unpack[Data]) -> None: ... +def g(**kwargs: Unpack[Data]) -> None: + partial(f, **kwargs)() + +class MoreData(TypedDict, total=False): + x: int + y: int + +def f_more(**kwargs: Unpack[MoreData]) -> None: ... +def g_more(**kwargs: Unpack[MoreData]) -> None: + partial(f_more, **kwargs)() + +class Good(TypedDict, total=False): + y: int +class Bad(TypedDict, total=False): + y: str + +def h(**kwargs: Unpack[Data]) -> None: + bad: Bad + partial(f_more, **kwargs)(**bad) # E: Argument "y" to "f_more" has incompatible type "str"; expected "int" + good: Good + partial(f_more, **kwargs)(**good) +[builtins fixtures/dict.pyi] + +[case testFunctoolsPartialNestedGeneric] +from functools import partial +from typing import Generic, TypeVar, List + +T = TypeVar("T") +def get(n: int, args: List[T]) -> T: ... +first = partial(get, 0) + +x: List[str] +reveal_type(first(x)) # N: Revealed type is "builtins.str" +reveal_type(first([1])) # N: Revealed type is "builtins.int" + +first_kw = partial(get, n=0) +reveal_type(first_kw(args=[1])) # N: Revealed type is "builtins.int" + +# TODO: this is indeed invalid, but the error is incomprehensible. +first_kw([1]) # E: Too many positional arguments for "get" \ + # E: Too few arguments for "get" \ + # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int" +[builtins fixtures/list.pyi] From abdaf6a571a4a539d755db1d6dcfdc45b69d97c5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 22 Jun 2024 23:19:22 +0100 Subject: [PATCH 0658/1617] Use (simplified) unions instead of joins for tuple fallbacks (#17408) Ref https://github.com/python/mypy/issues/12056 If `mypy_primer` will look good, I will add some logic to shorted unions in error messages. cc @JukkaL --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alex Waygood --- mypy/checker.py | 3 ++ mypy/messages.py | 68 ++++++++++++++++++++++--- mypy/semanal_shared.py | 6 +-- mypy/typeops.py | 7 ++- test-data/unit/check-enum.test | 6 +-- test-data/unit/check-expressions.test | 2 +- test-data/unit/check-namedtuple.test | 6 +-- test-data/unit/check-newsemanal.test | 12 ++--- test-data/unit/check-statements.test | 2 +- test-data/unit/check-tuples.test | 4 +- test-data/unit/check-typevar-tuple.test | 6 +-- test-data/unit/check-unions.test | 57 +++++++++++++++++++++ test-data/unit/semanal-classes.test | 2 +- 13 files changed, 146 insertions(+), 35 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index d2562d5dd722..792e751691fd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -49,6 +49,7 @@ SUGGESTED_TEST_FIXTURES, MessageBuilder, append_invariance_notes, + append_union_note, format_type, format_type_bare, format_type_distinctly, @@ -6814,6 +6815,8 @@ def check_subtype( ) if isinstance(subtype, Instance) and isinstance(supertype, Instance): notes = append_invariance_notes(notes, subtype, supertype) + if isinstance(subtype, UnionType) and isinstance(supertype, UnionType): + notes = append_union_note(notes, subtype, supertype, self.options) if extra_info: msg = msg.with_additional_msg(" (" + ", ".join(extra_info) + ")") diff --git a/mypy/messages.py b/mypy/messages.py index 27f152413151..62846c536f3d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -90,6 +90,7 @@ UninhabitedType, UnionType, UnpackType, + flatten_nested_unions, get_proper_type, get_proper_types, ) @@ -145,6 +146,9 @@ "numbers.Integral", } +MAX_TUPLE_ITEMS = 10 +MAX_UNION_ITEMS = 10 + class MessageBuilder: """Helper class for reporting type checker error messages with parameters. @@ -2338,7 +2342,7 @@ def try_report_long_tuple_assignment_error( """ if isinstance(subtype, TupleType): if ( - len(subtype.items) > 10 + len(subtype.items) > MAX_TUPLE_ITEMS and isinstance(supertype, Instance) and supertype.type.fullname == "builtins.tuple" ): @@ -2347,7 +2351,7 @@ def try_report_long_tuple_assignment_error( self.generate_incompatible_tuple_error(lhs_types, subtype.items, context, msg) return True elif isinstance(supertype, TupleType) and ( - len(subtype.items) > 10 or len(supertype.items) > 10 + len(subtype.items) > MAX_TUPLE_ITEMS or len(supertype.items) > MAX_TUPLE_ITEMS ): if len(subtype.items) != len(supertype.items): if supertype_label is not None and subtype_label is not None: @@ -2370,7 +2374,7 @@ def try_report_long_tuple_assignment_error( 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: + if item_cnt > MAX_TUPLE_ITEMS: return "{}[{}, {}, ... <{} more items>]".format( "tuple" if self.options.use_lowercase_names() else "Tuple", format_type_bare(typ.items[0], self.options), @@ -2497,11 +2501,21 @@ def format(typ: Type) -> str: def format_list(types: Sequence[Type]) -> str: return ", ".join(format(typ) for typ in types) - def format_union(types: Sequence[Type]) -> str: + def format_union_items(types: Sequence[Type]) -> list[str]: formatted = [format(typ) for typ in types if format(typ) != "None"] + if len(formatted) > MAX_UNION_ITEMS and verbosity == 0: + more = len(formatted) - MAX_UNION_ITEMS // 2 + formatted = formatted[: MAX_UNION_ITEMS // 2] + else: + more = 0 + if more: + formatted.append(f"<{more} more items>") if any(format(typ) == "None" for typ in types): formatted.append("None") - return " | ".join(formatted) + return formatted + + def format_union(types: Sequence[Type]) -> str: + return " | ".join(format_union_items(types)) def format_literal_value(typ: LiteralType) -> str: if typ.is_enum_literal(): @@ -2605,6 +2619,9 @@ def format_literal_value(typ: LiteralType) -> str: elif isinstance(typ, LiteralType): return f"Literal[{format_literal_value(typ)}]" elif isinstance(typ, UnionType): + typ = get_proper_type(ignore_last_known_values(typ)) + if not isinstance(typ, UnionType): + return format(typ) literal_items, union_items = separate_union_literals(typ) # Coalesce multiple Literal[] members. This also changes output order. @@ -2624,7 +2641,7 @@ def format_literal_value(typ: LiteralType) -> str: return ( f"{literal_str} | {format_union(union_items)}" if options.use_or_syntax() - else f"Union[{format_list(union_items)}, {literal_str}]" + else f"Union[{', '.join(format_union_items(union_items))}, {literal_str}]" ) else: return literal_str @@ -2645,7 +2662,7 @@ def format_literal_value(typ: LiteralType) -> str: s = ( format_union(typ.items) if options.use_or_syntax() - else f"Union[{format_list(typ.items)}]" + else f"Union[{', '.join(format_union_items(typ.items))}]" ) return s elif isinstance(typ, NoneType): @@ -3182,6 +3199,23 @@ def append_invariance_notes( return notes +def append_union_note( + notes: list[str], arg_type: UnionType, expected_type: UnionType, options: Options +) -> list[str]: + """Point to specific union item(s) that may cause failure in subtype check.""" + non_matching = [] + items = flatten_nested_unions(arg_type.items) + if len(items) < MAX_UNION_ITEMS: + return notes + for item in items: + if not is_subtype(item, expected_type): + non_matching.append(item) + if non_matching: + types = ", ".join([format_type(typ, options) for typ in non_matching]) + notes.append(f"Item{plural_s(non_matching)} in the first union not in the second: {types}") + return notes + + def append_numbers_notes( notes: list[str], arg_type: Instance, expected_type: Instance ) -> list[str]: @@ -3235,3 +3269,23 @@ def format_key_list(keys: list[str], *, short: bool = False) -> str: return f"{td}key {formatted_keys[0]}" else: return f"{td}keys ({', '.join(formatted_keys)})" + + +def ignore_last_known_values(t: UnionType) -> Type: + """This will avoid types like str | str in error messages. + + last_known_values are kept during union simplification, but may cause + weird formatting for e.g. tuples of literals. + """ + union_items: list[Type] = [] + seen_instances = set() + for item in t.items: + if isinstance(item, ProperType) and isinstance(item, Instance): + erased = item.copy_modified(last_known_value=None) + if erased in seen_instances: + continue + seen_instances.add(erased) + union_items.append(erased) + else: + union_items.append(item) + return UnionType.make_union(union_items, t.line, t.column) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 01d8e9aafffb..db19f074911f 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -8,7 +8,6 @@ from mypy_extensions import trait -from mypy import join from mypy.errorcodes import LITERAL_REQ, ErrorCode from mypy.nodes import ( CallExpr, @@ -30,6 +29,7 @@ from mypy.plugin import SemanticAnalyzerPluginInterface from mypy.tvar_scope import TypeVarLikeScope from mypy.type_visitor import ANY_STRATEGY, BoolTypeQuery +from mypy.typeops import make_simplified_union from mypy.types import ( TPDICT_FB_NAMES, AnyType, @@ -58,7 +58,7 @@ # Priorities for ordering of patches within the "patch" phase of semantic analysis # (after the main pass): -# Fix fallbacks (does joins) +# Fix fallbacks (does subtype checks). PRIORITY_FALLBACKS: Final = 1 @@ -304,7 +304,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None: raise NotImplementedError else: items.append(item) - fallback.args = (join.join_type_list(items),) + fallback.args = (make_simplified_union(items),) class _NamedTypeCallback(Protocol): diff --git a/mypy/typeops.py b/mypy/typeops.py index 62c850452516..4fe187f811ca 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -95,8 +95,6 @@ def is_recursive_pair(s: Type, t: Type) -> bool: def tuple_fallback(typ: TupleType) -> Instance: """Return fallback type for a tuple.""" - from mypy.join import join_type_list - info = typ.partial_fallback.type if info.fullname != "builtins.tuple": return typ.partial_fallback @@ -115,8 +113,9 @@ def tuple_fallback(typ: TupleType) -> Instance: raise NotImplementedError else: items.append(item) - # TODO: we should really use a union here, tuple types are special. - return Instance(info, [join_type_list(items)], extra_attrs=typ.partial_fallback.extra_attrs) + return Instance( + info, [make_simplified_union(items)], extra_attrs=typ.partial_fallback.extra_attrs + ) def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Type | None: diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index d53935085325..78a114eda764 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1010,7 +1010,7 @@ _empty: Final = Empty.token def func(x: Union[int, None, Empty] = _empty) -> int: boom = x + 42 # E: Unsupported left operand type for + ("None") \ # E: Unsupported left operand type for + ("Empty") \ - # N: Left operand is of type "Union[int, None, Empty]" + # N: Left operand is of type "Union[int, Empty, None]" if x is _empty: reveal_type(x) # N: Revealed type is "Literal[__main__.Empty.token]" return 0 @@ -1056,7 +1056,7 @@ _empty = Empty.token def func(x: Union[int, None, Empty] = _empty) -> int: boom = x + 42 # E: Unsupported left operand type for + ("None") \ # E: Unsupported left operand type for + ("Empty") \ - # N: Left operand is of type "Union[int, None, Empty]" + # N: Left operand is of type "Union[int, Empty, None]" if x is _empty: reveal_type(x) # N: Revealed type is "Literal[__main__.Empty.token]" return 0 @@ -1084,7 +1084,7 @@ _empty = Empty.token def func(x: Union[int, None, Empty] = _empty) -> int: boom = x + 42 # E: Unsupported left operand type for + ("None") \ # E: Unsupported left operand type for + ("Empty") \ - # N: Left operand is of type "Union[int, None, Empty]" + # N: Left operand is of type "Union[int, Empty, None]" if x is _empty: reveal_type(x) # N: Revealed type is "Literal[__main__.Empty.token]" return 0 diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 4fc6e9a75c83..f9bd60f4dcc8 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1640,7 +1640,7 @@ from typing import Generator def g() -> Generator[int, None, None]: x = yield from () # E: Function does not return a value (it only ever returns None) x = yield from (0, 1, 2) # E: Function does not return a value (it only ever returns None) - x = yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "object", expected type "int") \ + x = yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "Union[int, str]", expected type "int") \ # E: Function does not return a value (it only ever returns None) x = yield from ("ERROR",) # E: Incompatible types in "yield from" (actual type "str", expected type "int") \ # E: Function does not return a value (it only ever returns None) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 2007d574f922..e9d156754d9c 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1249,7 +1249,7 @@ nti: NT[int] reveal_type(nti * x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" nts: NT[str] -reveal_type(nts * x) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(nts * x) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1310,9 +1310,9 @@ reveal_type(foo(nti, nts)) # N: Revealed type is "Tuple[builtins.int, builtins. reveal_type(foo(nts, nti)) # N: Revealed type is "Tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]" reveal_type(foo(nti, x)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(foo(nts, x)) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(foo(nts, x)) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" reveal_type(foo(x, nti)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(foo(x, nts)) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(foo(x, nts)) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 47e508ee1a6b..511c7b003015 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -1947,7 +1947,7 @@ class NTStr(NamedTuple): y: str t1: T -reveal_type(t1.__iter__) # N: Revealed type is "def () -> typing.Iterator[__main__.A]" +reveal_type(t1.__iter__) # N: Revealed type is "def () -> typing.Iterator[Union[__main__.B, __main__.C]]" t2: NTInt reveal_type(t2.__iter__) # N: Revealed type is "def () -> typing.Iterator[builtins.int]" @@ -1960,7 +1960,6 @@ t: Union[Tuple[int, int], Tuple[str, str]] for x in t: reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/for.pyi] -[out] [case testNewAnalyzerFallbackUpperBoundCheckAndFallbacks] from typing import TypeVar, Generic, Tuple @@ -1973,10 +1972,9 @@ S = TypeVar('S', bound='Tuple[G[A], ...]') class GG(Generic[S]): pass -g: GG[Tuple[G[B], G[C]]] \ - # E: Type argument "Tuple[G[B], G[C]]" of "GG" must be a subtype of "Tuple[G[A], ...]" \ - # E: Type argument "B" of "G" must be a subtype of "A" \ - # E: Type argument "C" of "G" must be a subtype of "A" +g: GG[Tuple[G[B], G[C]]] # E: Type argument "Tuple[G[B], G[C]]" of "GG" must be a subtype of "Tuple[G[A], ...]" \ + # E: Type argument "B" of "G" must be a subtype of "A" \ + # E: Type argument "C" of "G" must be a subtype of "A" T = TypeVar('T', bound=A, covariant=True) @@ -1984,7 +1982,7 @@ class G(Generic[T]): pass t: Tuple[G[B], G[C]] # E: Type argument "B" of "G" must be a subtype of "A" \ # E: Type argument "C" of "G" must be a subtype of "A" -reveal_type(t.__iter__) # N: Revealed type is "def () -> typing.Iterator[builtins.object]" +reveal_type(t.__iter__) # N: Revealed type is "def () -> typing.Iterator[__main__.G[__main__.B]]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerClassKeywordsForward] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 34df5a8ab336..d1464423e90f 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -1339,7 +1339,7 @@ from typing import Generator def g() -> Generator[int, None, None]: yield from () yield from (0, 1, 2) - yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "object", expected type "int") + yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "Union[int, str]", expected type "int") yield from ("ERROR",) # E: Incompatible types in "yield from" (actual type "str", expected type "int") [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index ad4893c2890a..bf36977b56e3 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1408,8 +1408,8 @@ y = "" reveal_type(t[x]) # N: Revealed type is "Union[builtins.int, builtins.str]" t[y] # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ # N: Possible overload variants: \ - # N: def __getitem__(self, int, /) -> object \ - # N: def __getitem__(self, slice, /) -> Tuple[object, ...] + # N: def __getitem__(self, int, /) -> Union[int, str] \ + # N: def __getitem__(self, slice, /) -> Tuple[Union[int, str], ...] [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 8f7dd12d9cd4..49298114e069 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -24,7 +24,7 @@ def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: reveal_type(g(args, args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" reveal_type(g(args, args2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" -reveal_type(g(args, args3)) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(g(args, args3)) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" reveal_type(g(any, any)) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] @@ -989,7 +989,7 @@ from typing_extensions import Unpack def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[float, ...]], bool]]) -> None: for x in xs: - reveal_type(x) # N: Revealed type is "builtins.float" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] [case testFixedUnpackItemInInstanceArguments] @@ -1715,7 +1715,7 @@ vt: Tuple[int, Unpack[Tuple[float, ...]], int] reveal_type(vt + (1, 2)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]" reveal_type((1, 2) + vt) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" -reveal_type(vt + vt) # N: Revealed type is "builtins.tuple[builtins.float, ...]" +reveal_type(vt + vt) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.float], ...]" reveal_type(vtf + (1, 2)) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]" reveal_type((1, 2) + vtf) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]" diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 2ca2f1ba9eb3..329896f7a1a7 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1289,3 +1289,60 @@ x: str = a_class_or_none.field a_or_none: Optional[A] y: int = a_or_none.field [builtins fixtures/list.pyi] + +[case testLargeUnionsShort] +from typing import Union + +class C1: ... +class C2: ... +class C3: ... +class C4: ... +class C5: ... +class C6: ... +class C7: ... +class C8: ... +class C9: ... +class C10: ... +class C11: ... + +u: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11] +x: int = u # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, <6 more items>]", variable has type "int") + +[case testLargeUnionsLongIfNeeded] +from typing import Union + +class C1: ... +class C2: ... +class C3: ... +class C4: ... +class C5: ... +class C6: ... +class C7: ... +class C8: ... +class C9: ... +class C10: ... + +x: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, int] +y: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, str] +x = y # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, str]", variable has type "Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, int]") \ + # N: Item in the first union not in the second: "str" + +[case testLargeUnionsNoneShown] +from typing import Union + +class C1: ... +class C2: ... +class C3: ... +class C4: ... +class C5: ... +class C6: ... +class C7: ... +class C8: ... +class C9: ... +class C10: ... +class C11: ... + +x: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11] +y: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, None] +x = y # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, <6 more items>, None]", variable has type "Union[C1, C2, C3, C4, C5, <6 more items>]") \ + # N: Item in the first union not in the second: "None" diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index 951791e23490..b14358509f85 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -585,7 +585,7 @@ MypyFile:1( TupleType( Tuple[builtins.int, builtins.str]) BaseType( - builtins.tuple[builtins.object, ...]) + builtins.tuple[Union[builtins.int, builtins.str], ...]) PassStmt:2())) [case testBaseClassFromIgnoredModule] From 1b116dfbe37a4503e0541d6bd6f5dd5c815ab36d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 23 Jun 2024 08:47:52 +0100 Subject: [PATCH 0659/1617] Fix explicit type for partial (#17424) Fixes https://github.com/python/mypy/issues/17301 --- mypy/plugins/functools.py | 17 +++++++++++++--- test-data/unit/check-functools.test | 31 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 4f2ed6f2361d..e41afe2fde02 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -6,6 +6,7 @@ import mypy.checker import mypy.plugin +import mypy.semanal from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var from mypy.plugins.common import add_method_to_class @@ -24,6 +25,8 @@ _ORDERING_METHODS: Final = {"__lt__", "__le__", "__gt__", "__ge__"} +PARTIAL = "functools.partial" + class _MethodInfo(NamedTuple): is_static: bool @@ -142,7 +145,8 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) ) for k in fn_type.arg_kinds - ] + ], + ret_type=ctx.api.named_generic_type(PARTIAL, [fn_type.ret_type]), ) if defaulted.line < 0: # Make up a line number if we don't have one @@ -188,6 +192,13 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: bound = get_proper_type(bound) if not isinstance(bound, CallableType): return ctx.default_return_type + wrapped_ret_type = get_proper_type(bound.ret_type) + if not isinstance(wrapped_ret_type, Instance) or wrapped_ret_type.type.fullname != PARTIAL: + return ctx.default_return_type + if not mypy.semanal.refers_to_fullname(ctx.args[0][0], PARTIAL): + # If the first argument is partial, above call will trigger the plugin + # again, in between the wrapping above an unwrapping here. + bound = bound.copy_modified(ret_type=wrapped_ret_type.args[0]) formal_to_actual = map_actuals_to_formals( actual_kinds=actual_arg_kinds, @@ -237,7 +248,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: ret_type=ret_type, ) - ret = ctx.api.named_generic_type("functools.partial", [ret_type]) + ret = ctx.api.named_generic_type(PARTIAL, [ret_type]) ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied) return ret @@ -247,7 +258,7 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: if ( not isinstance(ctx.api, mypy.checker.TypeChecker) # use internals or not isinstance(ctx.type, Instance) - or ctx.type.type.fullname != "functools.partial" + or ctx.type.type.fullname != PARTIAL or not ctx.type.extra_attrs or "__mypy_partial" not in ctx.type.extra_attrs.attrs ): diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 79ae962a73e0..997f5bc70c7d 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -347,6 +347,37 @@ reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \ # E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]" [builtins fixtures/tuple.pyi] +[case testFunctoolsPartialExplicitType] +from functools import partial +from typing import Type, TypeVar, Callable + +T = TypeVar("T") +def generic(string: str, integer: int, resulting_type: Type[T]) -> T: ... + +p: partial[str] = partial(generic, resulting_type=str) +q: partial[bool] = partial(generic, resulting_type=str) # E: Argument "resulting_type" to "generic" has incompatible type "Type[str]"; expected "Type[bool]" + +pc: Callable[..., str] = partial(generic, resulting_type=str) +qc: Callable[..., bool] = partial(generic, resulting_type=str) # E: Incompatible types in assignment (expression has type "partial[str]", variable has type "Callable[..., bool]") \ + # N: "partial[str].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], str]" +[builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialNestedPartial] +from functools import partial +from typing import Any + +def foo(x: int) -> int: ... +p = partial(partial, foo) +reveal_type(p()(1)) # N: Revealed type is "builtins.int" +p()("no") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" + +q = partial(partial, partial, foo) +q()()("no") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" + +r = partial(partial, foo, 1) +reveal_type(r()()) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + [case testFunctoolsPartialTypeObject] import functools from typing import Type, Generic, TypeVar From 79b1c8d6a467cd829bf6b9e3919fbcef7b50eb19 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 23 Jun 2024 15:12:41 +0100 Subject: [PATCH 0660/1617] Fix previous partial fix (#17429) This is a bit unfortunate, but the best we can probably do. cc @hauntsaninja --- mypy/plugins/functools.py | 34 ++++++++++++++++++++++------- test-data/unit/check-functools.test | 13 +++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index e41afe2fde02..19be71ca36df 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -137,6 +137,20 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: # We must normalize from the start to have coherent view together with TypeChecker. fn_type = fn_type.with_unpacked_kwargs().with_normalized_var_args() + last_context = ctx.api.type_context[-1] + if not fn_type.is_type_obj(): + # We wrap the return type to get use of a possible type context provided by caller. + # We cannot do this in case of class objects, since otherwise the plugin may get + # falsely triggered when evaluating the constructed call itself. + ret_type: Type = ctx.api.named_generic_type(PARTIAL, [fn_type.ret_type]) + wrapped_return = True + else: + ret_type = fn_type.ret_type + # Instead, for class objects we ignore any type context to avoid spurious errors, + # since the type context will be partial[X] etc., not X. + ctx.api.type_context[-1] = None + wrapped_return = False + defaulted = fn_type.copy_modified( arg_kinds=[ ( @@ -146,7 +160,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: ) for k in fn_type.arg_kinds ], - ret_type=ctx.api.named_generic_type(PARTIAL, [fn_type.ret_type]), + ret_type=ret_type, ) if defaulted.line < 0: # Make up a line number if we don't have one @@ -189,16 +203,20 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: arg_names=actual_arg_names, context=call_expr, ) + if not wrapped_return: + # Restore previously ignored context. + ctx.api.type_context[-1] = last_context + bound = get_proper_type(bound) if not isinstance(bound, CallableType): return ctx.default_return_type - wrapped_ret_type = get_proper_type(bound.ret_type) - if not isinstance(wrapped_ret_type, Instance) or wrapped_ret_type.type.fullname != PARTIAL: - return ctx.default_return_type - if not mypy.semanal.refers_to_fullname(ctx.args[0][0], PARTIAL): - # If the first argument is partial, above call will trigger the plugin - # again, in between the wrapping above an unwrapping here. - bound = bound.copy_modified(ret_type=wrapped_ret_type.args[0]) + + if wrapped_return: + # Reverse the wrapping we did above. + ret_type = get_proper_type(bound.ret_type) + if not isinstance(ret_type, Instance) or ret_type.type.fullname != PARTIAL: + return ctx.default_return_type + bound = bound.copy_modified(ret_type=ret_type.args[0]) formal_to_actual = map_actuals_to_formals( actual_kinds=actual_arg_kinds, diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 997f5bc70c7d..e4b3e4cffdc1 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -455,3 +455,16 @@ first_kw([1]) # E: Too many positional arguments for "get" \ # E: Too few arguments for "get" \ # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int" [builtins fixtures/list.pyi] + +[case testFunctoolsPartialClassObjectMatchingPartial] +from functools import partial + +class A: + def __init__(self, var: int, b: int, c: int) -> None: ... + +p = partial(A, 1) +reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]" +p(1, "no") # E: Argument 2 to "A" has incompatible type "str"; expected "int" + +q: partial[A] = partial(A, 1) # OK +[builtins fixtures/tuple.pyi] From 39b9b899178e6a30e7e8664c12f0eb610b8a44a5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 23 Jun 2024 22:09:22 +0100 Subject: [PATCH 0661/1617] Always allow lambda calls (#17430) See https://github.com/python/mypy/pull/17408 for context. --- mypy/checkexpr.py | 2 ++ mypy/nodes.py | 4 +++- mypy/plugins/functools.py | 2 +- test-data/unit/check-functions.test | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 7ae23cfe516c..fdc0f94b3997 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -36,6 +36,7 @@ ARG_STAR, ARG_STAR2, IMPLICITLY_ABSTRACT, + LAMBDA_NAME, LITERAL_TYPE, REVEAL_LOCALS, REVEAL_TYPE, @@ -599,6 +600,7 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> and self.chk.in_checked_function() and isinstance(callee_type, CallableType) and callee_type.implicit + and callee_type.name != LAMBDA_NAME ): if fullname is None and member is not None: assert object_type is not None diff --git a/mypy/nodes.py b/mypy/nodes.py index 5d3a1d31aece..d215bcfce098 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -175,6 +175,8 @@ def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]: "typing_extensions.runtime_checkable", ) +LAMBDA_NAME: Final = "" + class Node(Context): """Common base class for all non-type parse tree nodes.""" @@ -2262,7 +2264,7 @@ class LambdaExpr(FuncItem, Expression): @property def name(self) -> str: - return "" + return LAMBDA_NAME def expr(self) -> Expression: """Return the expression (the body) of the lambda.""" diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 19be71ca36df..9589c6aeca8b 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -25,7 +25,7 @@ _ORDERING_METHODS: Final = {"__lt__", "__le__", "__gt__", "__ge__"} -PARTIAL = "functools.partial" +PARTIAL: Final = "functools.partial" class _MethodInfo(NamedTuple): diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 29cd977fe5d6..93540e203c36 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3366,3 +3366,19 @@ class C(B): ) -> None: ... [builtins fixtures/tuple.pyi] + +[case testLambdaAlwaysAllowed] +# flags: --disallow-untyped-calls +from typing import Callable, Optional + +def func() -> Optional[str]: ... +var: Optional[str] + +factory: Callable[[], Optional[str]] +for factory in ( + lambda: var, + func, +): + reveal_type(factory) # N: Revealed type is "def () -> Union[builtins.str, None]" + var = factory() +[builtins fixtures/tuple.pyi] From 18945af2a86af79ae9317fc716a034549682728d Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 24 Jun 2024 12:34:58 +0300 Subject: [PATCH 0662/1617] Suppress second error message with `:=` and `[truthy-bool]` (#15941) Closes https://github.com/python/mypy/issues/15685 CC @ikonst Co-authored-by: Ilya Priven --- mypy/checker.py | 26 +++++++++++++++++++++----- test-data/unit/check-errorcodes.test | 6 ++++++ test-data/unit/check-python38.test | 3 +-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 792e751691fd..4f20c6ee8493 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5762,7 +5762,9 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap: else_map = {} return if_map, else_map - def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]: + def find_isinstance_check( + self, node: Expression, *, in_boolean_context: bool = True + ) -> tuple[TypeMap, TypeMap]: """Find any isinstance checks (within a chain of ands). Includes implicit and explicit checks for None and calls to callable. Also includes TypeGuard and TypeIs functions. @@ -5773,15 +5775,24 @@ def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]: If either of the values in the tuple is None, then that particular branch can never occur. + If `in_boolean_context=True` is passed, it means that we handle + a walrus expression. We treat rhs values + in expressions like `(a := A())` specially: + for example, some errors are suppressed. + May return {}, {}. Can return None, None in situations involving NoReturn. """ - if_map, else_map = self.find_isinstance_check_helper(node) + if_map, else_map = self.find_isinstance_check_helper( + node, in_boolean_context=in_boolean_context + ) new_if_map = self.propagate_up_typemap_info(if_map) new_else_map = self.propagate_up_typemap_info(else_map) return new_if_map, new_else_map - def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeMap]: + def find_isinstance_check_helper( + self, node: Expression, *, in_boolean_context: bool = True + ) -> tuple[TypeMap, TypeMap]: if is_true_literal(node): return {}, None if is_false_literal(node): @@ -6050,7 +6061,9 @@ def has_no_custom_eq_checks(t: Type) -> bool: if else_assignment_map is not None: else_map.update(else_assignment_map) - if_condition_map, else_condition_map = self.find_isinstance_check(node.value) + if_condition_map, else_condition_map = self.find_isinstance_check( + node.value, in_boolean_context=False + ) if if_condition_map is not None: if_map.update(if_condition_map) @@ -6112,7 +6125,10 @@ def has_no_custom_eq_checks(t: Type) -> bool: # Restrict the type of the variable to True-ish/False-ish in the if and else branches # respectively original_vartype = self.lookup_type(node) - self._check_for_truthy_type(original_vartype, node) + if in_boolean_context: + # We don't check `:=` values in expressions like `(a := A())`, + # because they produce two error messages. + self._check_for_truthy_type(original_vartype, node) vartype = try_expanding_sum_type_to_union(original_vartype, "builtins.bool") if_type = true_only(vartype) diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 9d49480539e0..961815b11817 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -892,6 +892,12 @@ if a: any_or_object: Union[object, Any] if any_or_object: pass + +if (my_foo := Foo()): # E: "__main__.my_foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + pass + +if my_a := (a or Foo()): # E: "__main__.Foo" returns "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + pass [builtins fixtures/list.pyi] [case testTruthyFunctions] diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 0f1cbb6e81c4..dfb918defb0a 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -297,8 +297,7 @@ def f(x: int = (c := 4)) -> int: z2: NT # E: Variable "NT" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases - if Alias := int: # E: Function "Alias" could always be true in boolean context \ - # E: Function "int" could always be true in boolean context + if Alias := int: # E: Function "Alias" could always be true in boolean context z3: Alias # E: Variable "Alias" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases From 620e28148afb4c8c04fbc0255e0c04769431c6b2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 24 Jun 2024 18:38:37 +0300 Subject: [PATCH 0663/1617] Do not report plugin-generated methods with `explicit-override` (#17433) Closes https://github.com/typeddjango/django-stubs/issues/2226 Closes https://github.com/python/mypy/issues/17417 Closes https://github.com/python/mypy/pull/17370 Closes https://github.com/python/mypy/issues/17224 This is an alternative to https://github.com/python/mypy/pull/17418 Thanks a lot to @sterliakov, I took a dataclasses test case from #17370 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 9 ++++++- test-data/unit/check-custom-plugin.test | 33 +++++++++++++++++++++++++ test-data/unit/check-dataclasses.test | 14 +++++++++++ test-data/unit/plugins/add_method.py | 23 +++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 test-data/unit/plugins/add_method.py diff --git a/mypy/checker.py b/mypy/checker.py index 4f20c6ee8493..2df74cf7be8d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1938,8 +1938,15 @@ def check_explicit_override_decorator( found_method_base_classes: list[TypeInfo] | None, context: Context | None = None, ) -> None: + plugin_generated = False + if defn.info and (node := defn.info.get(defn.name)) and node.plugin_generated: + # Do not report issues for plugin generated nodes, + # they can't realistically use `@override` for their methods. + plugin_generated = True + if ( - found_method_base_classes + not plugin_generated + and found_method_base_classes and not defn.is_explicit_override and defn.name not in ("__init__", "__new__") and not is_private(defn.name) diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 63529cf165ce..2b3b3f4a8695 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1050,6 +1050,39 @@ reveal_type(my_class.stmethod) # N: Revealed type is "Overload(def (arg: builti \[mypy] plugins=/test-data/unit/plugins/add_overloaded_method.py +[case testAddMethodPluginExplicitOverride] +# flags: --python-version 3.12 --config-file tmp/mypy.ini +from typing import override, TypeVar + +T = TypeVar('T', bound=type) + +def inject_foo(t: T) -> T: + # Imitates: + # t.foo_implicit = some_method + return t + +class BaseWithoutFoo: pass + +@inject_foo +class ChildWithFoo(BaseWithoutFoo): pass +reveal_type(ChildWithFoo.foo_implicit) # N: Revealed type is "def (self: __main__.ChildWithFoo)" + +@inject_foo +class SomeWithFoo(ChildWithFoo): pass +reveal_type(SomeWithFoo.foo_implicit) # N: Revealed type is "def (self: __main__.SomeWithFoo)" + +class ExplicitOverride(SomeWithFoo): + @override + def foo_implicit(self) -> None: pass + +class ImplicitOverride(SomeWithFoo): + def foo_implicit(self) -> None: pass # E: Method "foo_implicit" is not using @override but is overriding a method in class "__main__.SomeWithFoo" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/add_method.py +enable_error_code = explicit-override +[typing fixtures/typing-override.pyi] + [case testCustomErrorCodePlugin] # flags: --config-file tmp/mypy.ini --show-error-codes def main() -> int: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 924f9c7bb5be..f26ccd9a4854 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2475,3 +2475,17 @@ class Base: class Child(Base): y: int [builtins fixtures/dataclasses.pyi] + + +[case testDataclassInheritanceWorksWithExplicitOverridesAndOrdering] +# flags: --enable-error-code explicit-override +from dataclasses import dataclass + +@dataclass(order=True) +class Base: + x: int + +@dataclass(order=True) +class Child(Base): + y: int +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/plugins/add_method.py b/test-data/unit/plugins/add_method.py new file mode 100644 index 000000000000..f3a7ebdb95ed --- /dev/null +++ b/test-data/unit/plugins/add_method.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import ClassDefContext, Plugin +from mypy.plugins.common import add_method +from mypy.types import NoneType + + +class AddOverrideMethodPlugin(Plugin): + def get_class_decorator_hook_2(self, fullname: str) -> Callable[[ClassDefContext], bool] | None: + if fullname == "__main__.inject_foo": + return add_extra_methods_hook + return None + + +def add_extra_methods_hook(ctx: ClassDefContext) -> bool: + add_method(ctx, "foo_implicit", [], NoneType()) + return True + + +def plugin(version: str) -> type[AddOverrideMethodPlugin]: + return AddOverrideMethodPlugin From 6c1d8671ce6eaf2c955fa986cbad51d6e6726d5d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 24 Jun 2024 20:57:29 +0100 Subject: [PATCH 0664/1617] Fix ParamSpec inference against TypeVarTuple (#17431) Fixes https://github.com/python/mypy/issues/17278 Fixes https://github.com/python/mypy/issues/17127 --- mypy/constraints.py | 6 ++- mypy/expandtype.py | 14 ++++++- mypy/semanal_typeargs.py | 12 +----- mypy/types.py | 13 +++++- test-data/unit/check-typevar-tuple.test | 53 +++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 13 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 316f481ac870..49a2aea8fa05 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1071,7 +1071,11 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # (with literal '...'). if not template.is_ellipsis_args: unpack_present = find_unpack_in_list(template.arg_types) - if unpack_present is not None: + # When both ParamSpec and TypeVarTuple are present, things become messy + # quickly. For now, we only allow ParamSpec to "capture" TypeVarTuple, + # but not vice versa. + # TODO: infer more from prefixes when possible. + if unpack_present is not None and not cactual.param_spec(): # We need to re-normalize args to the form they appear in tuples, # for callables we always pack the suffix inside another tuple. unpack = template.arg_types[unpack_present] diff --git a/mypy/expandtype.py b/mypy/expandtype.py index bff23c53defd..5c4d6af9458e 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -270,6 +270,13 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: repl = self.variables.get(t.id, t) if isinstance(repl, TypeVarTupleType): return repl + elif isinstance(repl, ProperType) and isinstance(repl, (AnyType, UninhabitedType)): + # Some failed inference scenarios will try to set all type variables to Never. + # Instead of being picky and require all the callers to wrap them, + # do this here instead. + # Note: most cases when this happens are handled in expand unpack below, but + # in rare cases (e.g. ParamSpec containing Unpack star args) it may be skipped. + return t.tuple_fallback.copy_modified(args=[repl]) raise NotImplementedError def visit_unpack_type(self, t: UnpackType) -> Type: @@ -348,7 +355,7 @@ def visit_callable_type(self, t: CallableType) -> CallableType: # the replacement is ignored. if isinstance(repl, Parameters): # We need to expand both the types in the prefix and the ParamSpec itself - return t.copy_modified( + expanded = t.copy_modified( arg_types=self.expand_types(t.arg_types[:-2]) + repl.arg_types, arg_kinds=t.arg_kinds[:-2] + repl.arg_kinds, arg_names=t.arg_names[:-2] + repl.arg_names, @@ -358,6 +365,11 @@ def visit_callable_type(self, t: CallableType) -> CallableType: imprecise_arg_kinds=(t.imprecise_arg_kinds or repl.imprecise_arg_kinds), variables=[*repl.variables, *t.variables], ) + var_arg = expanded.var_arg() + if var_arg is not None and isinstance(var_arg.typ, UnpackType): + # Sometimes we get new unpacks after expanding ParamSpec. + expanded.normalize_trivial_unpack() + return expanded elif isinstance(repl, ParamSpecType): # We're substituting one ParamSpec for another; this can mean that the prefix # changes, e.g. substitute Concatenate[int, P] in place of Q. diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 02cb1b1f6128..dbf5136afa1b 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -15,7 +15,7 @@ from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE from mypy.messages import format_type from mypy.mixedtraverser import MixedTraverserVisitor -from mypy.nodes import ARG_STAR, Block, ClassDef, Context, FakeInfo, FuncItem, MypyFile +from mypy.nodes import Block, ClassDef, Context, FakeInfo, FuncItem, MypyFile from mypy.options import Options from mypy.scope import Scope from mypy.subtypes import is_same_type, is_subtype @@ -104,15 +104,7 @@ def visit_tuple_type(self, t: TupleType) -> None: def visit_callable_type(self, t: CallableType) -> None: super().visit_callable_type(t) - # Normalize trivial unpack in var args as *args: *tuple[X, ...] -> *args: X - if t.is_var_arg: - star_index = t.arg_kinds.index(ARG_STAR) - star_type = t.arg_types[star_index] - if isinstance(star_type, UnpackType): - p_type = get_proper_type(star_type.type) - if isinstance(p_type, Instance): - assert p_type.type.fullname == "builtins.tuple" - t.arg_types[star_index] = p_type.args[0] + t.normalize_trivial_unpack() def visit_instance(self, t: Instance) -> None: super().visit_instance(t) diff --git a/mypy/types.py b/mypy/types.py index 3f764a5cc49e..52f8a8d63f09 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2084,6 +2084,17 @@ def param_spec(self) -> ParamSpecType | None: prefix = Parameters(self.arg_types[:-2], self.arg_kinds[:-2], self.arg_names[:-2]) return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix) + def normalize_trivial_unpack(self) -> None: + # Normalize trivial unpack in var args as *args: *tuple[X, ...] -> *args: X in place. + if self.is_var_arg: + star_index = self.arg_kinds.index(ARG_STAR) + star_type = self.arg_types[star_index] + if isinstance(star_type, UnpackType): + p_type = get_proper_type(star_type.type) + if isinstance(p_type, Instance): + assert p_type.type.fullname == "builtins.tuple" + self.arg_types[star_index] = p_type.args[0] + def with_unpacked_kwargs(self) -> NormalizedCallableType: if not self.unpack_kwargs: return cast(NormalizedCallableType, self) @@ -2113,7 +2124,7 @@ def with_normalized_var_args(self) -> Self: if not isinstance(unpacked, TupleType): # Note that we don't normalize *args: *tuple[X, ...] -> *args: X, # this should be done once in semanal_typeargs.py for user-defined types, - # and we ourselves should never construct such type. + # and we ourselves rarely construct such type. return self unpack_index = find_unpack_in_list(unpacked.items) if unpack_index == 0 and len(unpacked.items) > 1: diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 49298114e069..ea692244597c 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2407,3 +2407,56 @@ reveal_type(x) # N: Revealed type is "__main__.C[builtins.str, builtins.int]" reveal_type(C(f)) # N: Revealed type is "__main__.C[builtins.str, builtins.int, builtins.int, builtins.int, builtins.int]" C[()] # E: At least 1 type argument(s) expected, none given [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleAgainstParamSpecActualSuccess] +from typing import Generic, TypeVar, TypeVarTuple, Unpack, Callable, Tuple, List +from typing_extensions import ParamSpec + +R = TypeVar("R") +P = ParamSpec("P") + +class CM(Generic[R]): ... +def cm(fn: Callable[P, R]) -> Callable[P, CM[R]]: ... + +Ts = TypeVarTuple("Ts") +@cm +def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ... + +reveal_type(test) # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __main__.CM[Tuple[Unpack[Ts`-1]]]" +reveal_type(test(1, 2, 3)) # N: Revealed type is "__main__.CM[Tuple[Literal[1]?, Literal[2]?, Literal[3]?]]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleAgainstParamSpecActualFailedNoCrash] +from typing import Generic, TypeVar, TypeVarTuple, Unpack, Callable, Tuple, List +from typing_extensions import ParamSpec + +R = TypeVar("R") +P = ParamSpec("P") + +class CM(Generic[R]): ... +def cm(fn: Callable[P, List[R]]) -> Callable[P, CM[R]]: ... + +Ts = TypeVarTuple("Ts") +@cm # E: Argument 1 to "cm" has incompatible type "Callable[[VarArg(Unpack[Ts])], Tuple[Unpack[Ts]]]"; expected "Callable[[VarArg(Never)], List[Never]]" +def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ... + +reveal_type(test) # N: Revealed type is "def (*args: Never) -> __main__.CM[Never]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleAgainstParamSpecActualPrefix] +from typing import Generic, TypeVar, TypeVarTuple, Unpack, Callable, Tuple, List +from typing_extensions import ParamSpec, Concatenate + +R = TypeVar("R") +P = ParamSpec("P") +T = TypeVar("T") + +class CM(Generic[R]): ... +def cm(fn: Callable[Concatenate[T, P], R]) -> Callable[Concatenate[List[T], P], CM[R]]: ... + +Ts = TypeVarTuple("Ts") +@cm +def test(x: T, *args: Unpack[Ts]) -> Tuple[T, Unpack[Ts]]: ... + +reveal_type(test) # N: Revealed type is "def [T, Ts] (builtins.list[T`2], *args: Unpack[Ts`-2]) -> __main__.CM[Tuple[T`2, Unpack[Ts`-2]]]" +[builtins fixtures/tuple.pyi] From d39f0234a18762a9b261a28763c7bea706633ce7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:22:46 -0700 Subject: [PATCH 0665/1617] Add changelog entry for 1.10.1 (#17436) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0ea19866892..9d5919cafe33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -165,6 +165,9 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m Please see [git log](https://github.com/python/typeshed/commits/main?after=7c8e82fe483a40ec4cb0a2505cfdb0f3e7cc81d9+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +#### Mypy 1.10.1 + +- Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) #### Acknowledgements Thanks to all mypy contributors who contributed to this release: From b88fdbd32fe0a45d40531a6504317aa3fd48489e Mon Sep 17 00:00:00 2001 From: InSync Date: Wed, 26 Jun 2024 14:04:04 +0700 Subject: [PATCH 0666/1617] Fix typo in `error_code_list2.rst` (#17443) --- docs/source/error_code_list2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 2b765e412913..0655ef2d35d8 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -389,7 +389,7 @@ Example: # Are you missing an await? asyncio.create_task(f()) -You can assign the value to a temporary, otherwise unused to variable to +You can assign the value to a temporary, otherwise unused variable to silence the error: .. code-block:: python From 69042d3fca754380965fc63def0a21a346895667 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 29 Jun 2024 20:46:17 +0100 Subject: [PATCH 0667/1617] Allow mixing ParamSpec and TypeVarTuple in Generic (#17450) Fixes https://github.com/python/mypy/issues/16696 Fixes https://github.com/python/mypy/issues/16695 I think there are no good reasons to not allow this anymore. Also I am using this opportunity to tighten a bit invalid instances/aliases where a regular type variable is replaced with parameters and vice versa. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/erasetype.py | 13 ++---------- mypy/nodes.py | 3 --- mypy/semanal_typeargs.py | 26 ++++++++++++++++++------ mypy/typevars.py | 13 ++---------- mypy/typevartuples.py | 14 +++++++++++++ test-data/unit/check-typevar-tuple.test | 27 +++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 31 deletions(-) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index b41eefcd4821..5d95b221af15 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -34,6 +34,7 @@ get_proper_type, get_proper_types, ) +from mypy.typevartuples import erased_vars def erase_type(typ: Type) -> ProperType: @@ -77,17 +78,7 @@ def visit_deleted_type(self, t: DeletedType) -> ProperType: return t def visit_instance(self, t: Instance) -> ProperType: - args: list[Type] = [] - for tv in t.type.defn.type_vars: - # Valid erasure for *Ts is *tuple[Any, ...], not just Any. - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType( - tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) - ) - ) - else: - args.append(AnyType(TypeOfAny.special_form)) + args = erased_vars(t.type.defn.type_vars, TypeOfAny.special_form) return Instance(t.type, args, t.line) def visit_type_var(self, t: TypeVarType) -> ProperType: diff --git a/mypy/nodes.py b/mypy/nodes.py index d215bcfce098..2eb39d4baaf6 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3165,9 +3165,6 @@ def add_type_vars(self) -> None: self.type_var_tuple_prefix = i self.type_var_tuple_suffix = len(self.defn.type_vars) - i - 1 self.type_vars.append(vd.name) - assert not ( - self.has_param_spec_type and self.has_type_var_tuple_type - ), "Mixing type var tuples and param specs not supported yet" @property def name(self) -> str: diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index dbf5136afa1b..646bb28a3b6e 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -39,6 +39,7 @@ get_proper_types, split_with_prefix_and_suffix, ) +from mypy.typevartuples import erased_vars class TypeArgumentAnalyzer(MixedTraverserVisitor): @@ -89,7 +90,14 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: return self.seen_aliases.add(t) assert t.alias is not None, f"Unfixed type alias {t.type_ref}" - is_error = self.validate_args(t.alias.name, tuple(t.args), t.alias.alias_tvars, t) + is_error, is_invalid = self.validate_args( + t.alias.name, tuple(t.args), t.alias.alias_tvars, t + ) + if is_invalid: + # If there is an arity error (e.g. non-Parameters used for ParamSpec etc.), + # then it is safer to erase the arguments completely, to avoid crashes later. + # TODO: can we move this logic to typeanal.py? + t.args = erased_vars(t.alias.alias_tvars, TypeOfAny.from_error) if not is_error: # If there was already an error for the alias itself, there is no point in checking # the expansion, most likely it will result in the same kind of error. @@ -113,7 +121,9 @@ def visit_instance(self, t: Instance) -> None: info = t.type if isinstance(info, FakeInfo): return # https://github.com/python/mypy/issues/11079 - self.validate_args(info.name, t.args, info.defn.type_vars, t) + _, is_invalid = self.validate_args(info.name, t.args, info.defn.type_vars, t) + if is_invalid: + t.args = tuple(erased_vars(info.defn.type_vars, TypeOfAny.from_error)) if t.type.fullname == "builtins.tuple" and len(t.args) == 1: # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] arg = t.args[0] @@ -125,7 +135,7 @@ def visit_instance(self, t: Instance) -> None: def validate_args( self, name: str, args: tuple[Type, ...], type_vars: list[TypeVarLikeType], ctx: Context - ) -> bool: + ) -> tuple[bool, bool]: if any(isinstance(v, TypeVarTupleType) for v in type_vars): prefix = next(i for (i, v) in enumerate(type_vars) if isinstance(v, TypeVarTupleType)) tvt = type_vars[prefix] @@ -136,10 +146,11 @@ def validate_args( args = start + (TupleType(list(middle), tvt.tuple_fallback),) + end is_error = False + is_invalid = False for (i, arg), tvar in zip(enumerate(args), type_vars): if isinstance(tvar, TypeVarType): if isinstance(arg, ParamSpecType): - is_error = True + is_invalid = True self.fail( INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)), ctx, @@ -152,7 +163,7 @@ def validate_args( ) continue if isinstance(arg, Parameters): - is_error = True + is_invalid = True self.fail( f"Cannot use {format_type(arg, self.options)} for regular type variable," " only for ParamSpec", @@ -205,13 +216,16 @@ def validate_args( if not isinstance( get_proper_type(arg), (ParamSpecType, Parameters, AnyType, UnboundType) ): + is_invalid = True self.fail( "Can only replace ParamSpec with a parameter types list or" f" another ParamSpec, got {format_type(arg, self.options)}", ctx, code=codes.VALID_TYPE, ) - return is_error + if is_invalid: + is_error = True + return is_error, is_invalid def visit_unpack_type(self, typ: UnpackType) -> None: super().visit_unpack_type(typ) diff --git a/mypy/typevars.py b/mypy/typevars.py index 3d74a40c303f..e871973104a2 100644 --- a/mypy/typevars.py +++ b/mypy/typevars.py @@ -3,7 +3,6 @@ from mypy.erasetype import erase_typevars from mypy.nodes import TypeInfo from mypy.types import ( - AnyType, Instance, ParamSpecType, ProperType, @@ -15,6 +14,7 @@ TypeVarType, UnpackType, ) +from mypy.typevartuples import erased_vars def fill_typevars(typ: TypeInfo) -> Instance | TupleType: @@ -64,16 +64,7 @@ def fill_typevars(typ: TypeInfo) -> Instance | TupleType: def fill_typevars_with_any(typ: TypeInfo) -> Instance | TupleType: """Apply a correct number of Any's as type arguments to a type.""" - args: list[Type] = [] - for tv in typ.defn.type_vars: - # Valid erasure for *Ts is *tuple[Any, ...], not just Any. - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType(tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)])) - ) - else: - args.append(AnyType(TypeOfAny.special_form)) - inst = Instance(typ, args) + inst = Instance(typ, erased_vars(typ.defn.type_vars, TypeOfAny.special_form)) if typ.tuple_type is None: return inst erased_tuple_type = erase_typevars(typ.tuple_type, {tv.id for tv in typ.defn.type_vars}) diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index af2effbd4035..2a9998c10746 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -5,9 +5,12 @@ from typing import Sequence from mypy.types import ( + AnyType, Instance, ProperType, Type, + TypeVarLikeType, + TypeVarTupleType, UnpackType, get_proper_type, split_with_prefix_and_suffix, @@ -30,3 +33,14 @@ def extract_unpack(types: Sequence[Type]) -> ProperType | None: if isinstance(types[0], UnpackType): return get_proper_type(types[0].type) return None + + +def erased_vars(type_vars: Sequence[TypeVarLikeType], type_of_any: int) -> list[Type]: + args: list[Type] = [] + for tv in type_vars: + # Valid erasure for *Ts is *tuple[Any, ...], not just Any. + if isinstance(tv, TypeVarTupleType): + args.append(UnpackType(tv.tuple_fallback.copy_modified(args=[AnyType(type_of_any)]))) + else: + args.append(AnyType(type_of_any)) + return args diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index ea692244597c..f49e1b3c6613 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2460,3 +2460,30 @@ def test(x: T, *args: Unpack[Ts]) -> Tuple[T, Unpack[Ts]]: ... reveal_type(test) # N: Revealed type is "def [T, Ts] (builtins.list[T`2], *args: Unpack[Ts`-2]) -> __main__.CM[Tuple[T`2, Unpack[Ts`-2]]]" [builtins fixtures/tuple.pyi] + +[case testMixingTypeVarTupleAndParamSpec] +from typing import Generic, ParamSpec, TypeVarTuple, Unpack, Callable, TypeVar + +P = ParamSpec("P") +Ts = TypeVarTuple("Ts") + +class A(Generic[P, Unpack[Ts]]): ... +class B(Generic[Unpack[Ts], P]): ... + +a: A[[int, str], int, str] +reveal_type(a) # N: Revealed type is "__main__.A[[builtins.int, builtins.str], builtins.int, builtins.str]" +b: B[int, str, [int, str]] +reveal_type(b) # N: Revealed type is "__main__.B[builtins.int, builtins.str, [builtins.int, builtins.str]]" + +x: A[int, str, [int, str]] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" +reveal_type(x) # N: Revealed type is "__main__.A[Any, Unpack[builtins.tuple[Any, ...]]]" +y: B[[int, str], int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" +reveal_type(y) # N: Revealed type is "__main__.B[Unpack[builtins.tuple[Any, ...]], Any]" + +R = TypeVar("R") +class C(Generic[P, R]): + fn: Callable[P, None] + +c: C[int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" +reveal_type(c.fn) # N: Revealed type is "def (*Any, **Any)" +[builtins fixtures/tuple.pyi] From a27447d7cfb4ea2f31fd0dcce13dc4be23abb6eb Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Sat, 29 Jun 2024 19:27:59 -0400 Subject: [PATCH 0668/1617] Add Literal support for docstrings (#17441) (Explain how this PR changes mypy.) Updates the is_valid_type regex to include quotes and . --- mypy/stubdoc.py | 2 +- mypy/stubgenc.py | 1 + mypy/test/teststubgen.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 8c0a4dab696f..928d024514f3 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -20,7 +20,7 @@ Sig: _TypeAlias = Tuple[str, str] -_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], ]*(\.[a-zA-Z_][\w\[\], ]*)*$") +_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], .\"\']*(\.[a-zA-Z_][\w\[\], ]*)*$") _ARG_NAME_RE: Final = re.compile(r"\**[A-Za-z_][A-Za-z0-9_]*$") diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index bacb68f6d1c7..0aa6088a4e02 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -252,6 +252,7 @@ def __init__( "Iterable", "Iterator", "List", + "Literal", "NamedTuple", "Optional", "Tuple", diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 3669772854cb..05a3809179bd 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1357,6 +1357,17 @@ def test_is_valid_type(self) -> None: assert is_valid_type("List[int]") assert is_valid_type("Dict[str, int]") assert is_valid_type("None") + assert is_valid_type("Literal[26]") + assert is_valid_type("Literal[0x1A]") + assert is_valid_type('Literal["hello world"]') + assert is_valid_type('Literal[b"hello world"]') + assert is_valid_type('Literal[u"hello world"]') + assert is_valid_type("Literal[True]") + assert is_valid_type("Literal[Color.RED]") + assert is_valid_type("Literal[None]") + assert is_valid_type( + 'Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]' + ) assert not is_valid_type("foo-bar") assert not is_valid_type("x->y") assert not is_valid_type("True") From e4de4e32eeb87c33a35b5abe88676cf3f34f388f Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Sat, 29 Jun 2024 19:32:51 -0400 Subject: [PATCH 0669/1617] Include keyword only args when generating signatures in stubgenc (#17448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, signatures generated for callable by the `InspectionStubGenerator` won’t include keywords only arguments or their defaults. This change includes them in the generated signatures. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/stubgenc.py | 56 +++++++++++++++++++++++++++---------- mypy/test/teststubgen.py | 29 +++++++++++++++++++ test-data/unit/stubgen.test | 9 ++++++ 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 0aa6088a4e02..7ab500b4fe12 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -12,7 +12,7 @@ import keyword import os.path from types import FunctionType, ModuleType -from typing import Any, Mapping +from typing import Any, Callable, Mapping from mypy.fastparse import parse_type_comment from mypy.moduleinspect import is_c_module @@ -292,6 +292,8 @@ def get_default_function_sig(self, func: object, ctx: FunctionContext) -> Functi varargs = argspec.varargs kwargs = argspec.varkw annotations = argspec.annotations + kwonlyargs = argspec.kwonlyargs + kwonlydefaults = argspec.kwonlydefaults def get_annotation(key: str) -> str | None: if key not in annotations: @@ -304,27 +306,51 @@ def get_annotation(key: str) -> str | None: return argtype arglist: list[ArgSig] = [] + # Add the arguments to the signature - for i, arg in enumerate(args): - # Check if the argument has a default value - if defaults and i >= len(args) - len(defaults): - default_value = defaults[i - (len(args) - len(defaults))] - if arg in annotations: - argtype = annotations[arg] + def add_args( + args: list[str], get_default_value: Callable[[int, str], object | None] + ) -> None: + for i, arg in enumerate(args): + # Check if the argument has a default value + default_value = get_default_value(i, arg) + if default_value is not None: + if arg in annotations: + argtype = annotations[arg] + else: + argtype = self.get_type_annotation(default_value) + if argtype == "None": + # None is not a useful annotation, but we can infer that the arg + # is optional + incomplete = self.add_name("_typeshed.Incomplete") + argtype = f"{incomplete} | None" + + arglist.append(ArgSig(arg, argtype, default=True)) else: - argtype = self.get_type_annotation(default_value) - if argtype == "None": - # None is not a useful annotation, but we can infer that the arg - # is optional - incomplete = self.add_name("_typeshed.Incomplete") - argtype = f"{incomplete} | None" - arglist.append(ArgSig(arg, argtype, default=True)) + arglist.append(ArgSig(arg, get_annotation(arg), default=False)) + + def get_pos_default(i: int, _arg: str) -> Any | None: + if defaults and i >= len(args) - len(defaults): + return defaults[i - (len(args) - len(defaults))] else: - arglist.append(ArgSig(arg, get_annotation(arg), default=False)) + return None + + add_args(args, get_pos_default) # Add *args if present if varargs: arglist.append(ArgSig(f"*{varargs}", get_annotation(varargs))) + # if we have keyword only args, then wee need to add "*" + elif kwonlyargs: + arglist.append(ArgSig("*")) + + def get_kw_default(_i: int, arg: str) -> Any | None: + if kwonlydefaults: + return kwonlydefaults.get(arg) + else: + return None + + add_args(kwonlyargs, get_kw_default) # Add **kwargs if present if kwargs: diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 05a3809179bd..e65a16c8f395 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -845,6 +845,35 @@ class TestClassVariableCls: assert_equal(gen.get_imports().splitlines(), ["from typing import ClassVar"]) assert_equal(output, ["class C:", " x: ClassVar[int] = ..."]) + def test_non_c_generate_signature_with_kw_only_args(self) -> None: + class TestClass: + def test( + self, arg0: str, *, keyword_only: str, keyword_only_with_default: int = 7 + ) -> None: + pass + + output: list[str] = [] + mod = ModuleType(TestClass.__module__, "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.is_c_module = False + gen.generate_function_stub( + "test", + TestClass.test, + output=output, + class_info=ClassInfo( + self_var="self", + cls=TestClass, + name="TestClass", + docstring=getattr(TestClass, "__doc__", None), + ), + ) + assert_equal( + output, + [ + "def test(self, arg0: str, *, keyword_only: str, keyword_only_with_default: int = ...) -> None: ..." + ], + ) + def test_generate_c_type_inheritance(self) -> None: class TestClass(KeyError): pass diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 5dcb0706a8cb..94d0edb2ae37 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -361,6 +361,15 @@ def g(x, *, y=1, z=2): ... def f(x, *, y: int = 1) -> None: ... def g(x, *, y: int = 1, z: int = 2) -> None: ... +[case testKeywordOnlyArg_inspect] +def f(x, *, y=1): ... +def g(x, *, y=1, z=2): ... +def h(x, *, y, z=2): ... +[out] +def f(x, *, y: int = ...): ... +def g(x, *, y: int = ..., z: int = ...): ... +def h(x, *, y, z: int = ...): ... + [case testProperty] class A: @property From 177c8ee7b8166b3dcf89c034a676ef5818edbc38 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 30 Jun 2024 00:34:26 +0100 Subject: [PATCH 0670/1617] Fix strict optional handling in attrs plugin (#17451) Fixes https://github.com/python/mypy/issues/13794 Fix is trivial (but unlike for dataclasses, the calls to type ops are scattered, so I simply put the whole hook inside a single `with`). --- mypy/plugins/attrs.py | 17 ++++++++++++++++- test-data/unit/check-plugin-attrs.test | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index db976385ee56..b67a285af11d 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -55,6 +55,7 @@ deserialize_and_fixup_type, ) from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state from mypy.typeops import get_type_vars, make_simplified_union, map_type_from_supertype from mypy.types import ( AnyType, @@ -317,9 +318,23 @@ def attr_class_maker_callback( See https://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works. - If this returns False, some required metadata was not ready yet and we need another + If this returns False, some required metadata was not ready yet, and we need another pass. """ + with state.strict_optional_set(ctx.api.options.strict_optional): + # This hook is called during semantic analysis, but it uses a bunch of + # type-checking ops, so it needs the strict optional set properly. + return attr_class_maker_callback_impl( + ctx, auto_attribs_default, frozen_default, slots_default + ) + + +def attr_class_maker_callback_impl( + ctx: mypy.plugin.ClassDefContext, + auto_attribs_default: bool | None, + frozen_default: bool, + slots_default: bool, +) -> bool: info = ctx.cls.info init = _get_decorator_bool_argument(ctx, "init", True) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index b96c00730a74..0c653d608187 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2475,3 +2475,23 @@ class B: reveal_type(B.__hash__) # N: Revealed type is "None" [builtins fixtures/plugin_attrs.pyi] + +[case testAttrsStrictOptionalSetProperly] +from typing import Generic, Optional, TypeVar + +import attr + +T = TypeVar("T") + +@attr.mutable() +class Parent(Generic[T]): + run_type: Optional[int] = None + +@attr.mutable() +class Child(Parent[float]): + pass + +Parent(run_type = None) +c = Child(run_type = None) +reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/plugin_attrs.pyi] From 02d3667442e0c8e63989e0276d1e78754cc84b2a Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Sun, 30 Jun 2024 16:15:34 -0400 Subject: [PATCH 0671/1617] Fix typechecking for async generators (#17452) Fixes #10534 This PR fixes a bug in typechecking asynchronous generators. Mypy currently typechecks a generator/comprehension as `AsyncGenerator` if the leftmost expression contains `await`, or if it contains an `async for`. However, there are other situations where we should get async generator: If there is an `await` expression in any of the conditions or in any sequence except for the leftmost one, the generator/comprehension should also be typechecked as `AsyncGenerator`. I've implemented this change in Mypy and added a test case to assert this behavior. If I enter the test cases into a regular repl, I can confirm that the runtime representation is generator/async_generator as the test case expects. According to the [language reference](https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-comp_for): > If a comprehension contains either async for clauses or await expressions or other asynchronous comprehensions it is called an asynchronous comprehension. Confusingly, the documentation itself is actually not quite correct either, as pointed out in https://github.com/python/cpython/issues/114104 Alongside this change, I've made a PR to update the docs to be more precise: https://github.com/python/cpython/pull/121175 has more details. --- mypy/checkexpr.py | 9 +++++++-- test-data/unit/check-async-await.test | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fdc0f94b3997..c4ab8a081acc 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5585,8 +5585,13 @@ def visit_set_comprehension(self, e: SetComprehension) -> Type: def visit_generator_expr(self, e: GeneratorExpr) -> Type: # If any of the comprehensions use async for, the expression will return an async generator - # object, or if the left-side expression uses await. - if any(e.is_async) or has_await_expression(e.left_expr): + # object, or await is used anywhere but in the leftmost sequence. + if ( + any(e.is_async) + or has_await_expression(e.left_expr) + or any(has_await_expression(sequence) for sequence in e.sequences[1:]) + or any(has_await_expression(cond) for condlist in e.condlists for cond in condlist) + ): typ = "typing.AsyncGenerator" # received type is always None in async generator expressions additional_args: list[Type] = [NoneType()] diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 876fe0c6be15..0ef08e5a0775 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -573,6 +573,25 @@ async def return_f() -> AsyncGenerator[int, None]: [builtins fixtures/dict.pyi] [typing fixtures/typing-async.pyi] +[case testImplicitAsyncGenerator] +from typing import List + +async def get_list() -> List[int]: + return [1] + +async def predicate() -> bool: + return True + +async def test_implicit_generators() -> None: + reveal_type(await predicate() for _ in [1]) # N: Revealed type is "typing.AsyncGenerator[builtins.bool, None]" + reveal_type(x for x in [1] if await predicate()) # N: Revealed type is "typing.AsyncGenerator[builtins.int, None]" + reveal_type(x for x in await get_list()) # N: Revealed type is "typing.Generator[builtins.int, None, None]" + reveal_type(x for _ in [1] for x in await get_list()) # N: Revealed type is "typing.AsyncGenerator[builtins.int, None]" + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-async.pyi] + + -- The full matrix of coroutine compatibility -- ------------------------------------------ From c346c5425891c1eac5e4eba4c1eb4e84ceeb2a06 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 30 Jun 2024 17:14:04 -0700 Subject: [PATCH 0672/1617] Use Python 3.12 for mypy_primer (#17456) I'd like for us to cover projects that use Python 3.12 only syntax, like homeassistant --- .github/workflows/mypy_primer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 07a1d0863eb2..0c77d3a255d8 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -39,7 +39,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.12" - name: Install dependencies run: | python -m pip install -U pip From 98717718be97bf54a102f9d844ae7185aedaa7ac Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 30 Jun 2024 17:24:36 -0700 Subject: [PATCH 0673/1617] Sync typeshed (#17458) Source commit: https://github.com/python/typeshed/commit/dcab6e88883c629ede9637fb011958f8b4918f52 --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_interpchannels.pyi | 84 +++++ mypy/typeshed/stdlib/argparse.pyi | 8 +- mypy/typeshed/stdlib/asyncio/events.pyi | 93 +++--- mypy/typeshed/stdlib/asyncio/tasks.pyi | 5 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 290 ++++++++++-------- .../stdlib/asyncio/windows_events.pyi | 5 +- mypy/typeshed/stdlib/dataclasses.pyi | 2 +- mypy/typeshed/stdlib/posixpath.pyi | 16 +- mypy/typeshed/stdlib/subprocess.pyi | 8 + mypy/typeshed/stdlib/tarfile.pyi | 24 +- mypy/typeshed/stdlib/zipimport.pyi | 2 + 12 files changed, 347 insertions(+), 191 deletions(-) create mode 100644 mypy/typeshed/stdlib/_interpchannels.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 7b9ce2864484..89754f65f3fa 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -34,6 +34,7 @@ _dummy_thread: 3.0-3.8 _dummy_threading: 3.0-3.8 _heapq: 3.0- _imp: 3.0- +_interpchannels: 3.13- _json: 3.0- _locale: 3.0- _lsprof: 3.0- diff --git a/mypy/typeshed/stdlib/_interpchannels.pyi b/mypy/typeshed/stdlib/_interpchannels.pyi new file mode 100644 index 000000000000..b77fe321a071 --- /dev/null +++ b/mypy/typeshed/stdlib/_interpchannels.pyi @@ -0,0 +1,84 @@ +from _typeshed import structseq +from typing import Final, Literal, SupportsIndex, final +from typing_extensions import Buffer, Self + +class ChannelError(RuntimeError): ... +class ChannelClosedError(ChannelError): ... +class ChannelEmptyError(ChannelError): ... +class ChannelNotEmptyError(ChannelError): ... +class ChannelNotFoundError(ChannelError): ... + +# Mark as final, since instantiating ChannelID is not supported. +@final +class ChannelID: + @property + def end(self) -> Literal["send", "recv", "both"]: ... + @property + def send(self) -> Self: ... + @property + def recv(self) -> Self: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: ChannelID) -> bool: ... + def __gt__(self, other: ChannelID) -> bool: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __le__(self, other: ChannelID) -> bool: ... + def __lt__(self, other: ChannelID) -> bool: ... + def __ne__(self, other: object) -> bool: ... + +@final +class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, int]): + __match_args__: Final = ( + "open", + "closing", + "closed", + "count", + "num_interp_send", + "num_interp_send_released", + "num_interp_recv", + "num_interp_recv_released", + ) + @property + def open(self) -> bool: ... + @property + def closing(self) -> bool: ... + @property + def closed(self) -> bool: ... + @property + def count(self) -> int: ... # type: ignore[override] + @property + def num_interp_send(self) -> int: ... + @property + def num_interp_send_released(self) -> int: ... + @property + def num_interp_recv(self) -> int: ... + @property + def num_interp_recv_released(self) -> int: ... + @property + def num_interp_both(self) -> int: ... + @property + def num_interp_both_recv_released(self) -> int: ... + @property + def num_interp_both_send_released(self) -> int: ... + @property + def num_interp_both_released(self) -> int: ... + @property + def recv_associated(self) -> bool: ... + @property + def recv_released(self) -> bool: ... + @property + def send_associated(self) -> bool: ... + @property + def send_released(self) -> bool: ... + +def create() -> ChannelID: ... +def destroy(cid: SupportsIndex) -> None: ... +def list_all() -> list[ChannelID]: ... +def list_interpreters(cid: SupportsIndex, *, send: bool) -> list[int]: ... +def send(cid: SupportsIndex, obj: object, *, blocking: bool = True, timeout: float | None = None) -> None: ... +def send_buffer(cid: SupportsIndex, obj: Buffer, *, blocking: bool = True, timeout: float | None = None) -> None: ... +def recv(cid: SupportsIndex, default: object = ...) -> object: ... +def close(cid: SupportsIndex, *, send: bool = False, recv: bool = False) -> None: ... +def get_info(cid: SupportsIndex) -> ChannelInfo: ... +def release(cid: SupportsIndex, *, send: bool = False, recv: bool = False, force: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 1956d08c9933..bc781ec8e61d 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -32,6 +32,7 @@ _T = TypeVar("_T") _ActionT = TypeVar("_ActionT", bound=Action) _ArgumentParserT = TypeVar("_ArgumentParserT", bound=ArgumentParser) _N = TypeVar("_N") +_ActionType: TypeAlias = Callable[[str], Any] | FileType | str # more precisely, Literal["store", "store_const", "store_true", # "store_false", "append", "append_const", "count", "help", "version", # "extend"], but using this would make it hard to annotate callers @@ -89,7 +90,7 @@ class _ActionsContainer: nargs: int | _NArgsStr | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., - type: Callable[[str], _T] | FileType = ..., + type: _ActionType = ..., choices: Iterable[_T] | None = ..., required: bool = ..., help: str | None = ..., @@ -313,7 +314,7 @@ class Action(_AttributeHolder): nargs: int | str | None const: Any default: Any - type: Callable[[str], Any] | FileType | None + type: _ActionType | None choices: Iterable[Any] | None required: bool help: str | None @@ -699,6 +700,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): add_help: bool = ..., allow_abbrev: bool = ..., exit_on_error: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... elif sys.version_info >= (3, 9): def add_parser( @@ -721,6 +723,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): add_help: bool = ..., allow_abbrev: bool = ..., exit_on_error: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... else: def add_parser( @@ -742,6 +745,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): conflict_handler: str = ..., add_help: bool = ..., allow_abbrev: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... def _get_subactions(self) -> list[Action]: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index c0345eb1b5b5..8c2664666835 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -16,23 +16,40 @@ from .tasks import Task from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from .unix_events import AbstractChildWatcher -__all__ = ( - "AbstractEventLoopPolicy", - "AbstractEventLoop", - "AbstractServer", - "Handle", - "TimerHandle", - "get_event_loop_policy", - "set_event_loop_policy", - "get_event_loop", - "set_event_loop", - "new_event_loop", - "get_child_watcher", - "set_child_watcher", - "_set_running_loop", - "get_running_loop", - "_get_running_loop", -) +if sys.version_info >= (3, 14): + __all__ = ( + "AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "get_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "set_event_loop", + "new_event_loop", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", + ) +else: + __all__ = ( + "AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "get_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "set_event_loop", + "new_event_loop", + "get_child_watcher", + "set_child_watcher", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", + ) _T = TypeVar("_T") _Ts = TypeVarTuple("_Ts") @@ -541,18 +558,19 @@ class AbstractEventLoopPolicy: @abstractmethod def new_event_loop(self) -> AbstractEventLoop: ... # Child processes handling (Unix only). - if sys.version_info >= (3, 12): - @abstractmethod - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher(self) -> AbstractChildWatcher: ... - @abstractmethod - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... - else: - @abstractmethod - def get_child_watcher(self) -> AbstractChildWatcher: ... - @abstractmethod - def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + else: + @abstractmethod + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): def get_event_loop(self) -> AbstractEventLoop: ... @@ -565,15 +583,16 @@ def get_event_loop() -> AbstractEventLoop: ... def set_event_loop(loop: AbstractEventLoop | None) -> None: ... def new_event_loop() -> AbstractEventLoop: ... -if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher() -> AbstractChildWatcher: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... +if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher() -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... -else: - def get_child_watcher() -> AbstractChildWatcher: ... - def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... + else: + def get_child_watcher() -> AbstractChildWatcher: ... + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... def _get_running_loop() -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index c16a1919b7c8..4613bca70c1a 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -70,7 +70,10 @@ _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") _T6 = TypeVar("_T6") _FT = TypeVar("_FT", bound=Future[Any]) -_FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] +if sys.version_info >= (3, 12): + _FutureLike: TypeAlias = Future[_T] | Awaitable[_T] +else: + _FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] _TaskYieldType: TypeAlias = Future[object] | None FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index e9274b853290..3a2c62646121 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -13,51 +13,54 @@ _Ts = TypeVarTuple("_Ts") # This is also technically not available on Win, # but other parts of typeshed need this definition. # So, it is special cased. -if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class AbstractChildWatcher: - @abstractmethod - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - @abstractmethod - def remove_child_handler(self, pid: int) -> bool: ... - @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def close(self) -> None: ... - @abstractmethod - def __enter__(self) -> Self: ... - @abstractmethod - def __exit__( - self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None - ) -> None: ... - @abstractmethod - def is_active(self) -> bool: ... - -else: - class AbstractChildWatcher: - @abstractmethod - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - @abstractmethod - def remove_child_handler(self, pid: int) -> bool: ... - @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def close(self) -> None: ... - @abstractmethod - def __enter__(self) -> Self: ... - @abstractmethod - def __exit__( - self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None - ) -> None: ... - @abstractmethod - def is_active(self) -> bool: ... +if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class AbstractChildWatcher: + @abstractmethod + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + @abstractmethod + def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod + def __enter__(self) -> Self: ... + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None + ) -> None: ... + @abstractmethod + def is_active(self) -> bool: ... + + else: + class AbstractChildWatcher: + @abstractmethod + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + @abstractmethod + def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod + def __enter__(self) -> Self: ... + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None + ) -> None: ... + @abstractmethod + def is_active(self) -> bool: ... if sys.platform != "win32": - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 14): + __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy") + elif sys.version_info >= (3, 9): __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", @@ -79,118 +82,137 @@ if sys.platform != "win32": "DefaultEventLoopPolicy", ) - # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. - # See discussion in #7412 - class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): - def close(self) -> None: ... - def is_active(self) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - - if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class SafeChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. + # See discussion in #7412 + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): + def close(self) -> None: ... + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class FastChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... - else: - class SafeChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... - class FastChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + else: + # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. + # See discussion in #7412 + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): + def close(self) -> None: ... + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): - if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher(self) -> AbstractChildWatcher: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... - else: - def get_child_watcher(self) -> AbstractChildWatcher: ... - def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + else: + def get_child_watcher(self) -> AbstractChildWatcher: ... + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... SelectorEventLoop = _UnixSelectorEventLoop DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy - if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class MultiLoopChildWatcher(AbstractChildWatcher): - def is_active(self) -> bool: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class MultiLoopChildWatcher(AbstractChildWatcher): + def is_active(self) -> bool: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - else: - class MultiLoopChildWatcher(AbstractChildWatcher): - def is_active(self) -> bool: ... + else: + class MultiLoopChildWatcher(AbstractChildWatcher): + def is_active(self) -> bool: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + if sys.version_info < (3, 14): + class ThreadedChildWatcher(AbstractChildWatcher): + def is_active(self) -> Literal[True]: ... def close(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... + def __del__(self) -> None: ... def add_child_handler( self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - class ThreadedChildWatcher(AbstractChildWatcher): - def is_active(self) -> Literal[True]: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def __del__(self) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - - if sys.version_info >= (3, 9): - class PidfdChildWatcher(AbstractChildWatcher): - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def is_active(self) -> bool: ... - def close(self) -> None: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + if sys.version_info >= (3, 9): + class PidfdChildWatcher(AbstractChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def is_active(self) -> bool: ... + def close(self) -> None: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 9c150ee16beb..97aa52ff8b9a 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -74,8 +74,9 @@ if sys.platform == "win32": class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): _loop_factory: ClassVar[type[SelectorEventLoop]] - def get_child_watcher(self) -> NoReturn: ... - def set_child_watcher(self, watcher: Any) -> NoReturn: ... + if sys.version_info < (3, 14): + def get_child_watcher(self) -> NoReturn: ... + def set_child_watcher(self, watcher: Any) -> NoReturn: ... class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): _loop_factory: ClassVar[type[ProactorEventLoop]] diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 30489e6f8b3d..626608e8a59d 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -108,7 +108,7 @@ class _DefaultFactory(Protocol[_T_co]): class Field(Generic[_T]): name: str - type: Type[_T] + type: Type[_T] | str | Any default: _T | Literal[_MISSING_TYPE.MISSING] default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING] repr: bool diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index e5f5fa0d813c..31406f8df950 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -77,11 +77,7 @@ pathsep: LiteralString defpath: LiteralString devnull: LiteralString -# Overloads are necessary to work around python/mypy#3644. -@overload -def abspath(path: PathLike[AnyStr]) -> AnyStr: ... -@overload -def abspath(path: AnyStr) -> AnyStr: ... +def abspath(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... @overload def basename(p: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -90,14 +86,8 @@ def basename(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... def dirname(p: PathLike[AnyStr]) -> AnyStr: ... @overload def dirname(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... -@overload -def expanduser(path: PathLike[AnyStr]) -> AnyStr: ... -@overload -def expanduser(path: AnyStr) -> AnyStr: ... -@overload -def expandvars(path: PathLike[AnyStr]) -> AnyStr: ... -@overload -def expandvars(path: AnyStr) -> AnyStr: ... +def expanduser(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... +def expandvars(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... @overload def normcase(s: PathLike[AnyStr]) -> AnyStr: ... @overload diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 6234ecc02b48..b01bac2455ce 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -889,6 +889,7 @@ if sys.version_info >= (3, 11): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -920,6 +921,7 @@ elif sys.version_info >= (3, 10): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -950,6 +952,7 @@ elif sys.version_info >= (3, 9): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -978,6 +981,7 @@ else: start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, ) -> int: ... @@ -1005,6 +1009,7 @@ if sys.version_info >= (3, 11): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1036,6 +1041,7 @@ elif sys.version_info >= (3, 10): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1066,6 +1072,7 @@ elif sys.version_info >= (3, 9): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1094,6 +1101,7 @@ else: pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, ) -> int: ... diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index e52099464174..d6adf21c1900 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -103,10 +103,13 @@ PAX_NAME_FIELDS: set[str] ENCODING: str +_FileCreationModes: TypeAlias = Literal["a", "w", "x"] + +@overload def open( name: StrOrBytesPath | None = None, mode: str = "r", - fileobj: IO[bytes] | None = None, # depends on mode + fileobj: IO[bytes] | None = None, bufsize: int = 10240, *, format: int | None = ..., @@ -121,6 +124,25 @@ def open( compresslevel: int | None = ..., preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., ) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + mode: _FileCreationModes = ..., + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int | None = ..., + preset: int | None = ..., +) -> TarFile: ... class ExFileObject(io.BufferedReader): def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ... diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index 158d573cac74..f53b09e188eb 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -28,5 +28,7 @@ class zipimporter: def is_package(self, fullname: str) -> bool: ... def load_module(self, fullname: str) -> ModuleType: ... if sys.version_info >= (3, 10): + def exec_module(self, module: ModuleType) -> None: ... + def create_module(self, spec: ModuleSpec) -> None: ... def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... def invalidate_caches(self) -> None: ... From 4ae632b6f07b33dc5937a3b1dd28af98a080bc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 2 Jul 2024 01:15:20 +0200 Subject: [PATCH 0674/1617] Fix type comments crash inside generic definitions (#16849) Closes https://github.com/python/mypy/issues/16649 It's the first time I am contributing to mypy so I am not very familiar with how it works entirely behind the scene. The issue that I had is that a crash happens when using tuple type comments inside functions/classes that depend on a *constrained* type variable. After investigation, the reason is that the type checker generates all possible definitions (since constraints are known) and expands the functions definitions and bodies accordingly. However, by doing so, a tuple type comment ('# type: (int, float)') would have a FakeInfo, so `ExpandTypeVisitor` would fail since it queries `t.type.fullname`. By the way, feel free to change where my test should lie. --- mypy/expandtype.py | 12 ++++++++++- test-data/unit/check-typevar-values.test | 26 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 5c4d6af9458e..9336be54437b 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -2,7 +2,7 @@ from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload -from mypy.nodes import ARG_STAR, Var +from mypy.nodes import ARG_STAR, FakeInfo, Var from mypy.state import state from mypy.types import ( ANY_STRATEGY, @@ -208,6 +208,16 @@ def visit_erased_type(self, t: ErasedType) -> Type: def visit_instance(self, t: Instance) -> Type: args = self.expand_types_with_unpack(list(t.args)) + + if isinstance(t.type, FakeInfo): + # The type checker expands function definitions and bodies + # if they depend on constrained type variables but the body + # might contain a tuple type comment (e.g., # type: (int, float)), + # in which case 't.type' is not yet available. + # + # See: https://github.com/python/mypy/issues/16649 + return t.copy_modified(args=args) + if t.type.fullname == "builtins.tuple": # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] arg = args[0] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index effaf620f1f0..8b961d88d23d 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -706,3 +706,29 @@ Func = Callable[[], T] class A: ... class B: ... + +[case testTypeCommentInGenericTypeWithConstrainedTypeVar] +from typing import Generic, TypeVar + +NT = TypeVar("NT", int, float) + +class Foo1(Generic[NT]): + p = 1 # type: int + +class Foo2(Generic[NT]): + p, q = 1, 2.0 # type: (int, float) + +class Foo3(Generic[NT]): + def bar(self) -> None: + p = 1 # type: int + +class Foo4(Generic[NT]): + def bar(self) -> None: + p, q = 1, 2.0 # type: (int, float) + +def foo3(x: NT) -> None: + p = 1 # type: int + +def foo4(x: NT) -> None: + p, q = 1, 2.0 # type: (int, float) +[builtins fixtures/tuple.pyi] From d1d3c780c7a2d30b2a038903289ea7487303a218 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:28:56 -0700 Subject: [PATCH 0675/1617] Further improvements to functools.partial handling (#17425) - Fixes another crash case / type inference in that case - Fix a false positive when calling the partially applied function with kwargs - TypeTraverse / comment / daemon test follow up ilevkivskyi mentioned on the original PR See also https://github.com/python/mypy/pull/17423 --- mypy/plugins/functools.py | 31 ++++--- mypy/type_visitor.py | 1 + mypy/types.py | 3 +- test-data/unit/check-functools.test | 121 ++++++++++++++++++++++------ test-data/unit/fine-grained.test | 48 +++++++++++ 5 files changed, 169 insertions(+), 35 deletions(-) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 9589c6aeca8b..6650af637519 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -245,11 +245,14 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: partial_kinds.append(fn_type.arg_kinds[i]) partial_types.append(arg_type) partial_names.append(fn_type.arg_names[i]) - elif actuals: - if any(actual_arg_kinds[j] == ArgKind.ARG_POS for j in actuals): + else: + assert actuals + if any(actual_arg_kinds[j] in (ArgKind.ARG_POS, ArgKind.ARG_STAR) for j in actuals): + # Don't add params for arguments passed positionally continue + # Add defaulted params for arguments passed via keyword kind = actual_arg_kinds[actuals[0]] - if kind == ArgKind.ARG_NAMED: + if kind == ArgKind.ARG_NAMED or kind == ArgKind.ARG_STAR2: kind = ArgKind.ARG_NAMED_OPT partial_kinds.append(kind) partial_types.append(arg_type) @@ -286,15 +289,25 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: if len(ctx.arg_types) != 2: # *args, **kwargs return ctx.default_return_type - args = [a for param in ctx.args for a in param] - arg_kinds = [a for param in ctx.arg_kinds for a in param] - arg_names = [a for param in ctx.arg_names for a in param] + # See comments for similar actual to formal code above + actual_args = [] + actual_arg_kinds = [] + actual_arg_names = [] + seen_args = set() + for i, param in enumerate(ctx.args): + for j, a in enumerate(param): + if a in seen_args: + continue + seen_args.add(a) + actual_args.append(a) + actual_arg_kinds.append(ctx.arg_kinds[i][j]) + actual_arg_names.append(ctx.arg_names[i][j]) result = ctx.api.expr_checker.check_call( callee=partial_type, - args=args, - arg_kinds=arg_kinds, - arg_names=arg_names, + args=actual_args, + arg_kinds=actual_arg_kinds, + arg_names=actual_arg_names, context=ctx.context, ) return result[0] diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index d0876629fc08..e685c49904bc 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -213,6 +213,7 @@ def visit_instance(self, t: Instance) -> Type: line=t.line, column=t.column, last_known_value=last_known_value, + extra_attrs=t.extra_attrs, ) def visit_type_var(self, t: TypeVarType) -> Type: diff --git a/mypy/types.py b/mypy/types.py index 52f8a8d63f09..2e7cbfd4e733 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1417,8 +1417,7 @@ def __init__( self._hash = -1 # Additional attributes defined per instance of this type. For example modules - # have different attributes per instance of types.ModuleType. This is intended - # to be "short-lived", we don't serialize it, and even don't store as variable type. + # have different attributes per instance of types.ModuleType. self.extra_attrs = extra_attrs def accept(self, visitor: TypeVisitor[T]) -> T: diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index e4b3e4cffdc1..710d3e66dfad 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -191,6 +191,7 @@ functools.partial(1) # E: "int" not callable \ [case testFunctoolsPartialStar] import functools +from typing import List def foo(a: int, b: str, *args: int, d: str, **kwargs: int) -> int: ... @@ -215,6 +216,13 @@ def bar(*a: bytes, **k: int): p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str" p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str" p1(*a) # E: List or tuple expected as variadic arguments + + +def baz(a: int, b: int) -> int: ... +def test_baz(xs: List[int]): + p3 = functools.partial(baz, *xs) + p3() + p3(1) # E: Too many arguments for "baz" [builtins fixtures/dict.pyi] [case testFunctoolsPartialGeneric] @@ -408,33 +416,83 @@ def foo(cls3: Type[B[T]]): from typing_extensions import TypedDict, Unpack from functools import partial -class Data(TypedDict, total=False): - x: int - -def f(**kwargs: Unpack[Data]) -> None: ... -def g(**kwargs: Unpack[Data]) -> None: - partial(f, **kwargs)() - -class MoreData(TypedDict, total=False): - x: int - y: int +class D1(TypedDict, total=False): + a1: int + +def fn1(a1: int) -> None: ... # N: "fn1" defined here +def main1(**d1: Unpack[D1]) -> None: + partial(fn1, **d1)() + partial(fn1, **d1)(**d1) + partial(fn1, **d1)(a1=1) + partial(fn1, **d1)(a1="asdf") # E: Argument "a1" to "fn1" has incompatible type "str"; expected "int" + partial(fn1, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn1" + +def fn2(**kwargs: Unpack[D1]) -> None: ... # N: "fn2" defined here +def main2(**d1: Unpack[D1]) -> None: + partial(fn2, **d1)() + partial(fn2, **d1)(**d1) + partial(fn2, **d1)(a1=1) + partial(fn2, **d1)(a1="asdf") # E: Argument "a1" to "fn2" has incompatible type "str"; expected "int" + partial(fn2, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn2" + +class D2(TypedDict, total=False): + a1: int + a2: str + +class A2Good(TypedDict, total=False): + a2: str +class A2Bad(TypedDict, total=False): + a2: int + +def fn3(a1: int, a2: str) -> None: ... # N: "fn3" defined here +def main3(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None: + partial(fn3, **d2)() + partial(fn3, **d2)(a1=1, a2="asdf") + + partial(fn3, **d2)(**d2) + + partial(fn3, **d2)(a1="asdf") # E: Argument "a1" to "fn3" has incompatible type "str"; expected "int" + partial(fn3, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn3" + + partial(fn3, **d2)(**a2good) + partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + +def fn4(**kwargs: Unpack[D2]) -> None: ... # N: "fn4" defined here +def main4(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None: + partial(fn4, **d2)() + partial(fn4, **d2)(a1=1, a2="asdf") + + partial(fn4, **d2)(**d2) + + partial(fn4, **d2)(a1="asdf") # E: Argument "a1" to "fn4" has incompatible type "str"; expected "int" + partial(fn4, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn4" + + partial(fn3, **d2)(**a2good) + partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + +def main5(**d2: Unpack[D2]) -> None: + partial(fn1, **d2)() # E: Extra argument "a2" from **args for "fn1" + partial(fn2, **d2)() # E: Extra argument "a2" from **args for "fn2" + +def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None: + partial(fn3, **d1)() # E: Missing positional argument "a1" in call to "fn3" + partial(fn3, **d1)("asdf") # E: Too many positional arguments for "fn3" \ + # E: Too few arguments for "fn3" \ + # E: Argument 1 to "fn3" has incompatible type "str"; expected "int" + partial(fn3, **d1)(a2="asdf") + partial(fn3, **d1)(**a2good) + partial(fn3, **d1)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + + partial(fn4, **d1)() + partial(fn4, **d1)("asdf") # E: Too many positional arguments for "fn4" \ + # E: Argument 1 to "fn4" has incompatible type "str"; expected "int" + partial(fn4, **d1)(a2="asdf") + partial(fn4, **d1)(**a2good) + partial(fn4, **d1)(**a2bad) # E: Argument "a2" to "fn4" has incompatible type "int"; expected "str" -def f_more(**kwargs: Unpack[MoreData]) -> None: ... -def g_more(**kwargs: Unpack[MoreData]) -> None: - partial(f_more, **kwargs)() - -class Good(TypedDict, total=False): - y: int -class Bad(TypedDict, total=False): - y: str - -def h(**kwargs: Unpack[Data]) -> None: - bad: Bad - partial(f_more, **kwargs)(**bad) # E: Argument "y" to "f_more" has incompatible type "str"; expected "int" - good: Good - partial(f_more, **kwargs)(**good) [builtins fixtures/dict.pyi] + [case testFunctoolsPartialNestedGeneric] from functools import partial from typing import Generic, TypeVar, List @@ -456,6 +514,21 @@ first_kw([1]) # E: Too many positional arguments for "get" \ # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int" [builtins fixtures/list.pyi] +[case testFunctoolsPartialHigherOrder] +from functools import partial +from typing import Callable + +def fn(a: int, b: str, c: bytes) -> int: ... + +def callback1(fn: Callable[[str, bytes], int]) -> None: ... +def callback2(fn: Callable[[str, int], int]) -> None: ... + +callback1(partial(fn, 1)) +# TODO: false negative +# https://github.com/python/mypy/issues/17461 +callback2(partial(fn, 1)) +[builtins fixtures/tuple.pyi] + [case testFunctoolsPartialClassObjectMatchingPartial] from functools import partial diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 2a652e50b1e6..2ad31311a402 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10497,3 +10497,51 @@ from pkg.sub import modb [out] == + +[case testFineGrainedFunctoolsPartial] +import m + +[file m.py] +from typing import Callable +from partial import p1 + +reveal_type(p1) +p1("a") +p1("a", 3) +p1("a", c=3) +p1(1, 3) +p1(1, "a", 3) +p1(a=1, b="a", c=3) +[builtins fixtures/dict.pyi] + +[file partial.py] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... +p1 = foo + +[file partial.py.2] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... +p1 = functools.partial(foo, 1) + +[out] +m.py:4: note: Revealed type is "def (a: builtins.int, b: builtins.str, c: builtins.int =) -> builtins.int" +m.py:5: error: Too few arguments +m.py:5: error: Argument 1 has incompatible type "str"; expected "int" +m.py:6: error: Argument 1 has incompatible type "str"; expected "int" +m.py:6: error: Argument 2 has incompatible type "int"; expected "str" +m.py:7: error: Too few arguments +m.py:7: error: Argument 1 has incompatible type "str"; expected "int" +m.py:8: error: Argument 2 has incompatible type "int"; expected "str" +== +m.py:4: note: Revealed type is "functools.partial[builtins.int]" +m.py:8: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +m.py:9: error: Too many arguments for "foo" +m.py:9: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +m.py:9: error: Argument 2 to "foo" has incompatible type "str"; expected "int" +m.py:10: error: Unexpected keyword argument "a" for "foo" +partial.py:4: note: "foo" defined here From f297917fc9eba13cc2a5b7e2e276394d94c03c8d Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 2 Jul 2024 08:51:36 -0700 Subject: [PATCH 0676/1617] Mention --enable-incomplete-feature=NewGenericSyntax (#17462) --- mypy/fastparse.py | 16 +++++++++++++--- test-data/unit/check-python312.test | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 342cf36d69e8..01f6ed4733ae 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -954,7 +954,9 @@ def do_func_def( else: self.fail( ErrorMessage( - "PEP 695 generics are not yet supported", code=codes.VALID_TYPE + "PEP 695 generics are not yet supported. " + "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", + code=codes.VALID_TYPE, ), n.type_params[0].lineno, n.type_params[0].col_offset, @@ -1145,7 +1147,11 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: explicit_type_params = self.translate_type_params(n.type_params) else: self.fail( - ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), + ErrorMessage( + "PEP 695 generics are not yet supported. " + "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", + code=codes.VALID_TYPE, + ), n.type_params[0].lineno, n.type_params[0].col_offset, blocker=False, @@ -1801,7 +1807,11 @@ def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: return self.set_line(node, n) else: self.fail( - ErrorMessage("PEP 695 type aliases are not yet supported", code=codes.VALID_TYPE), + ErrorMessage( + "PEP 695 type aliases are not yet supported. " + "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", + code=codes.VALID_TYPE, + ), n.lineno, n.col_offset, blocker=False, diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 27027d30a684..5307f47d539a 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1,10 +1,10 @@ [case test695TypeAlias] -type MyInt = int # E: PEP 695 type aliases are not yet supported +type MyInt = int # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support def f(x: MyInt) -> MyInt: return reveal_type(x) # N: Revealed type is "builtins.int" -type MyList[T] = list[T] # E: PEP 695 type aliases are not yet supported \ +type MyList[T] = list[T] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "T" is not defined def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not valid as a type \ @@ -17,7 +17,7 @@ def h(x: MyInt2) -> MyInt2: return reveal_type(x) # N: Revealed type is "builtins.int" [case test695Class] -class MyGen[T]: # E: PEP 695 generics are not yet supported +class MyGen[T]: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support def __init__(self, x: T) -> None: # E: Name "T" is not defined self.x = x @@ -25,13 +25,13 @@ def f(x: MyGen[int]): # E: "MyGen" expects no type arguments, but 1 given reveal_type(x.x) # N: Revealed type is "Any" [case test695Function] -def f[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ +def f[T](x: T) -> T: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "T" is not defined return reveal_type(x) # N: Revealed type is "Any" reveal_type(f(1)) # N: Revealed type is "Any" -async def g[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ +async def g[T](x: T) -> T: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "T" is not defined return reveal_type(x) # N: Revealed type is "Any" @@ -41,26 +41,26 @@ reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ [case test695TypeVar] from typing import Callable -type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported \ +type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "T" is not defined -type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported \ +type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Value of type "int" is not indexable \ # E: Name "P" is not defined -type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported \ +type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "Ts" is not defined -class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported -class Cls2[**P]: ... # E: PEP 695 generics are not yet supported -class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported +class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support +class Cls2[**P]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support +class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support -def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported \ +def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "T" is not defined -def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported \ +def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ # E: Name "P" is not defined -def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported \ +def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ # E: Name "Ts" is not defined [builtins fixtures/tuple.pyi] From 55a08120b7be56e7d33cf2e9d240d30a8f608f83 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 Jul 2024 19:36:16 +0100 Subject: [PATCH 0677/1617] Infer unions for ternary expressions (#17427) Ref https://github.com/python/mypy/issues/12056 cc @JukkaL Again, let's check the primer... --- mypy/checkexpr.py | 19 +++-- mypyc/test-data/irbuild-any.test | 4 +- test-data/unit/check-errorcodes.test | 2 +- test-data/unit/check-expressions.test | 17 ++-- test-data/unit/check-functions.test | 17 +++- test-data/unit/check-inference-context.test | 6 +- test-data/unit/check-inference.test | 10 ++- test-data/unit/check-optional.test | 2 +- test-data/unit/check-tuples.test | 87 ++++++++++++--------- 9 files changed, 99 insertions(+), 65 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c4ab8a081acc..3532e18b93b2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5766,16 +5766,15 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F context=if_type_fallback, allow_none_return=allow_none_return, ) - - # Only create a union type if the type context is a union, to be mostly - # compatible with older mypy versions where we always did a join. - # - # TODO: Always create a union or at least in more cases? - if isinstance(get_proper_type(self.type_context[-1]), UnionType): - res: Type = make_simplified_union([if_type, full_context_else_type]) - else: - res = join.join_types(if_type, else_type) - + res: Type = make_simplified_union([if_type, else_type]) + if has_uninhabited_component(res) and not isinstance( + get_proper_type(self.type_context[-1]), UnionType + ): + # In rare cases with empty collections join may give a better result. + alternative = join.join_types(if_type, else_type) + p_alt = get_proper_type(alternative) + if not isinstance(p_alt, Instance) or p_alt.type.fullname != "builtins.object": + res = alternative return res def analyze_cond_branch( diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 0d14e1a5dfc8..3bfb1587fb3b 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -151,7 +151,9 @@ def f4(a, n, b): a :: object n :: int b :: bool - r0, r1, r2, r3 :: object + r0 :: union[object, int] + r1, r2 :: object + r3 :: union[int, object] r4 :: int L0: if b goto L1 else goto L2 :: bool diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 961815b11817..c4d72388fba9 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -460,7 +460,7 @@ a: D = {'x': ''} # E: Incompatible types (expression has type "str", TypedDict b: D = {'y': ''} # E: Missing key "x" for TypedDict "D" [typeddict-item] \ # E: Extra key "y" for TypedDict "D" [typeddict-unknown-key] c = D(x=0) if int() else E(x=0, y=0) -c = {} # E: Expected TypedDict key "x" but found no keys [typeddict-item] +c = {} # E: Missing key "x" for TypedDict "D" [typeddict-item] d: D = {'x': '', 'y': 1} # E: Extra key "y" for TypedDict "D" [typeddict-unknown-key] \ # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [typeddict-item] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index f9bd60f4dcc8..61cee1d00c58 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1470,10 +1470,9 @@ if int(): [case testConditionalExpressionUnion] from typing import Union -reveal_type(1 if bool() else 2) # N: Revealed type is "builtins.int" -reveal_type(1 if bool() else '') # N: Revealed type is "builtins.object" -x: Union[int, str] = reveal_type(1 if bool() else '') \ - # N: Revealed type is "Union[Literal[1]?, Literal['']?]" +reveal_type(1 if bool() else 2) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" +reveal_type(1 if bool() else '') # N: Revealed type is "Union[Literal[1]?, Literal['']?]" +x: Union[int, str] = reveal_type(1 if bool() else '') # N: Revealed type is "Union[Literal[1]?, Literal['']?]" class A: pass class B(A): @@ -1487,17 +1486,17 @@ b = B() c = C() d = D() reveal_type(a if bool() else b) # N: Revealed type is "__main__.A" -reveal_type(b if bool() else c) # N: Revealed type is "builtins.object" -reveal_type(c if bool() else b) # N: Revealed type is "builtins.object" -reveal_type(c if bool() else a) # N: Revealed type is "builtins.object" -reveal_type(d if bool() else b) # N: Revealed type is "__main__.A" +reveal_type(b if bool() else c) # N: Revealed type is "Union[__main__.B, __main__.C]" +reveal_type(c if bool() else b) # N: Revealed type is "Union[__main__.C, __main__.B]" +reveal_type(c if bool() else a) # N: Revealed type is "Union[__main__.C, __main__.A]" +reveal_type(d if bool() else b) # N: Revealed type is "Union[__main__.D, __main__.B]" [builtins fixtures/bool.pyi] [case testConditionalExpressionUnionWithAny] from typing import Union, Any a: Any x: Union[int, str] = reveal_type(a if int() else 1) # N: Revealed type is "Union[Any, Literal[1]?]" -reveal_type(a if int() else 1) # N: Revealed type is "Any" +reveal_type(a if int() else 1) # N: Revealed type is "Union[Any, Literal[1]?]" [case testConditionalExpressionStatementNoReturn] from typing import List, Union diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 93540e203c36..6c895c86e899 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2250,13 +2250,26 @@ def dec(f: Callable[[A, str], None]) -> Callable[[A, int], None]: pass [out] [case testUnknownFunctionNotCallable] +from typing import TypeVar + def f() -> None: pass def g(x: int) -> None: pass h = f if bool() else g -reveal_type(h) # N: Revealed type is "builtins.function" -h(7) # E: Cannot call function of unknown type +reveal_type(h) # N: Revealed type is "Union[def (), def (x: builtins.int)]" +h(7) # E: Too many arguments for "f" + +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +h2 = join(f, g) +reveal_type(h2) # N: Revealed type is "builtins.function" +h2(7) # E: Cannot call function of unknown type + +h3 = join(g, f) +reveal_type(h3) # N: Revealed type is "builtins.function" +h3(7) # E: Cannot call function of unknown type [builtins fixtures/bool.pyi] [case testFunctionWithNameUnderscore] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index afe6548df2d4..17ae6d9934b7 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -701,7 +701,7 @@ class A: pass class B(A): pass class C(A): pass def f(func: Callable[[T], S], *z: T, r: Optional[S] = None) -> S: pass -reveal_type(f(lambda x: 0 if isinstance(x, B) else 1)) # N: Revealed type is "builtins.int" +reveal_type(f(lambda x: 0 if isinstance(x, B) else 1)) # N: Revealed type is "Union[Literal[0]?, Literal[1]?]" f(lambda x: 0 if isinstance(x, B) else 1, A())() # E: "int" not callable f(lambda x: x if isinstance(x, B) else B(), A(), r=B())() # E: "B" not callable f( @@ -1391,7 +1391,7 @@ from typing import Union, List, Any def f(x: Union[List[str], Any]) -> None: a = x if x else [] - reveal_type(a) # N: Revealed type is "Union[builtins.list[Union[builtins.str, Any]], builtins.list[builtins.str], Any]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.str], Any, builtins.list[Union[builtins.str, Any]]]" [builtins fixtures/list.pyi] [case testConditionalExpressionWithEmptyIteableAndUnionWithAny] @@ -1399,7 +1399,7 @@ from typing import Union, Iterable, Any def f(x: Union[Iterable[str], Any]) -> None: a = x if x else [] - reveal_type(a) # N: Revealed type is "Union[builtins.list[Union[builtins.str, Any]], typing.Iterable[builtins.str], Any]" + reveal_type(a) # N: Revealed type is "Union[typing.Iterable[builtins.str], Any, builtins.list[Union[builtins.str, Any]]]" [builtins fixtures/list.pyi] [case testInferMultipleAnyUnionCovariant] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index fcd03f8efe01..0dbefbc774a3 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1438,18 +1438,22 @@ class Wrapper: def f(cond: bool) -> Any: f = Wrapper if cond else lambda x: x - reveal_type(f) # N: Revealed type is "def (x: Any) -> Any" + reveal_type(f) # N: Revealed type is "Union[def (x: Any) -> __main__.Wrapper, def (x: Any) -> Any]" return f(3) def g(cond: bool) -> Any: f = lambda x: x if cond else Wrapper - reveal_type(f) # N: Revealed type is "def (x: Any) -> Any" + reveal_type(f) # N: Revealed type is "def (x: Any) -> Union[Any, def (x: Any) -> __main__.Wrapper]" + return f(3) + +def h(cond: bool) -> Any: + f = (lambda x: x) if cond else Wrapper + reveal_type(f) # N: Revealed type is "Union[def (x: Any) -> Any, def (x: Any) -> __main__.Wrapper]" return f(3) -- Boolean operators -- ----------------- - [case testOrOperationWithGenericOperands] from typing import List a: List[A] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 70f3c4486e14..f80aa5115bc3 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -395,7 +395,7 @@ def lookup_field(name, obj): attr = None [case testTernaryWithNone] -reveal_type(None if bool() else 0) # N: Revealed type is "Union[Literal[0]?, None]" +reveal_type(None if bool() else 0) # N: Revealed type is "Union[None, Literal[0]?]" [builtins fixtures/bool.pyi] [case testListWithNone] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index bf36977b56e3..972bccf8c24b 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1228,68 +1228,76 @@ x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[ [out] [case testFixedTupleJoinVarTuple] -from typing import Tuple +from typing import Tuple, TypeVar class A: pass class B(A): pass fixtup: Tuple[B, B] +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + vartup_b: Tuple[B, ...] -reveal_type(fixtup if int() else vartup_b) # N: Revealed type is "builtins.tuple[__main__.B, ...]" -reveal_type(vartup_b if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.B, ...]" +reveal_type(join(fixtup, vartup_b)) # N: Revealed type is "builtins.tuple[__main__.B, ...]" +reveal_type(join(vartup_b, fixtup)) # N: Revealed type is "builtins.tuple[__main__.B, ...]" vartup_a: Tuple[A, ...] -reveal_type(fixtup if int() else vartup_a) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(vartup_a if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" - +reveal_type(join(fixtup, vartup_a)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(vartup_a, fixtup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" [builtins fixtures/tuple.pyi] [out] [case testFixedTupleJoinList] -from typing import Tuple, List +from typing import Tuple, List, TypeVar class A: pass class B(A): pass fixtup: Tuple[B, B] +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + lst_b: List[B] -reveal_type(fixtup if int() else lst_b) # N: Revealed type is "typing.Sequence[__main__.B]" -reveal_type(lst_b if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.B]" +reveal_type(join(fixtup, lst_b)) # N: Revealed type is "typing.Sequence[__main__.B]" +reveal_type(join(lst_b, fixtup)) # N: Revealed type is "typing.Sequence[__main__.B]" lst_a: List[A] -reveal_type(fixtup if int() else lst_a) # N: Revealed type is "typing.Sequence[__main__.A]" -reveal_type(lst_a if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(fixtup, lst_a)) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(lst_a, fixtup)) # N: Revealed type is "typing.Sequence[__main__.A]" [builtins fixtures/tuple.pyi] [out] [case testEmptyTupleJoin] -from typing import Tuple, List +from typing import Tuple, List, TypeVar class A: pass empty = () +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + fixtup: Tuple[A] -reveal_type(fixtup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(empty if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(fixtup, empty)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(empty, fixtup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" vartup: Tuple[A, ...] -reveal_type(empty if int() else vartup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(vartup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(vartup, empty)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(empty, vartup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" lst: List[A] -reveal_type(empty if int() else lst) # N: Revealed type is "typing.Sequence[__main__.A]" -reveal_type(lst if int() else empty) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(empty, lst)) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(lst, empty)) # N: Revealed type is "typing.Sequence[__main__.A]" [builtins fixtures/tuple.pyi] [out] [case testTupleSubclassJoin] -from typing import Tuple, NamedTuple +from typing import Tuple, NamedTuple, TypeVar class NTup(NamedTuple): a: bool @@ -1302,32 +1310,38 @@ ntup: NTup subtup: SubTuple vartup: SubVarTuple -reveal_type(ntup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(ntup, vartup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, vartup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] [out] [case testTupleJoinIrregular] -from typing import Tuple +from typing import Tuple, TypeVar tup1: Tuple[bool, int] tup2: Tuple[bool] -reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(tup1, tup2)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup2, tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup1 if int() else ()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(() if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup1, ())) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join((), tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else ()) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(() if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join(tup2, ())) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join((), tup2)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" [builtins fixtures/tuple.pyi] [out] [case testTupleSubclassJoinIrregular] -from typing import Tuple, NamedTuple +from typing import Tuple, NamedTuple, TypeVar class NTup1(NamedTuple): a: bool @@ -1342,14 +1356,17 @@ tup1: NTup1 tup2: NTup2 subtup: SubTuple -reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(tup1, tup2)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join(tup2, tup1)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(tup1 if int() else subtup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup1, subtup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else subtup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup2, subtup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, tup2)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] [out] From 45afd73974ef9c558f753d3a55e2997610b9827a Mon Sep 17 00:00:00 2001 From: Raphael Krupinski Date: Wed, 3 Jul 2024 14:56:52 +0200 Subject: [PATCH 0678/1617] Fix help message for --no-namespace-packages. (#17472) Fixes #17466 Change help message of '--no-namespace-packages' option Co-authored-by: Raphael Krupinski <10319569-mattesilver@users.noreply.gitlab.com> --- mypy/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/main.py b/mypy/main.py index 489ef8fd9a7b..05044335ecee 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -565,7 +565,7 @@ def add_invertible_flag( "--no-namespace-packages", dest="namespace_packages", default=True, - help="Support namespace packages (PEP 420, __init__.py-less)", + help="Disable support for namespace packages (PEP 420, __init__.py-less)", group=imports_group, ) imports_group.add_argument( From cb7b96d32d7ca2ba791106f61e1adf5c0d7b0ea4 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Wed, 3 Jul 2024 12:05:40 -0500 Subject: [PATCH 0679/1617] Add `__replace__` for dataclasses in 3.13 (#17469) Fixes https://github.com/python/mypy/issues/17471 --- mypy/plugins/dataclasses.py | 15 ++++++++++++ test-data/unit/check-dataclasses.test | 34 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dd2eceab217f..edfc6840fc37 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -385,6 +385,9 @@ def transform(self) -> bool: self._add_dataclass_fields_magic_attribute() self._add_internal_replace_method(attributes) + if self._api.options.python_version >= (3, 13): + self._add_dunder_replace(attributes) + if "__post_init__" in info.names: self._add_internal_post_init_method(attributes) @@ -395,6 +398,18 @@ def transform(self) -> bool: return True + def _add_dunder_replace(self, attributes: list[DataclassAttribute]) -> None: + """Add a `__replace__` method to the class, which is used to replace attributes in the `copy` module.""" + args = [attr.to_argument(self._cls.info, of="replace") for attr in attributes] + type_vars = [tv for tv in self._cls.type_vars] + add_method_to_class( + self._api, + self._cls, + "__replace__", + args=args, + return_type=Instance(self._cls.info, type_vars), + ) + def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> None: """ Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index f26ccd9a4854..0f726242b25b 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2489,3 +2489,37 @@ class Base: class Child(Base): y: int [builtins fixtures/dataclasses.pyi] + +[case testDunderReplacePresent] +# flags: --python-version 3.13 +from dataclasses import dataclass + +@dataclass +class Coords: + x: int + y: int + + +replaced = Coords(2, 4).__replace__(x=2, y=5) +reveal_type(replaced) # N: Revealed type is "__main__.Coords" + +replaced = Coords(2, 4).__replace__(x=2) +reveal_type(replaced) # N: Revealed type is "__main__.Coords" + +Coords(2, 4).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "Coords" has incompatible type "str"; expected "int" +Coords(2, 4).__replace__(23) # E: Too many positional arguments for "__replace__" of "Coords" +Coords(2, 4).__replace__(23, 25) # E: Too many positional arguments for "__replace__" of "Coords" +Coords(2, 4).__replace__(x=23, y=25, z=42) # E: Unexpected keyword argument "z" for "__replace__" of "Coords" + +from typing import Generic, TypeVar +T = TypeVar('T') + +@dataclass +class Gen(Generic[T]): + x: T + +replaced_2 = Gen(2).__replace__(x=2) +reveal_type(replaced_2) # N: Revealed type is "__main__.Gen[builtins.int]" +Gen(2).__replace__(x="not an int") # E: Argument "x" to "__replace__" of "Gen" has incompatible type "str"; expected "int" + +[builtins fixtures/tuple.pyi] From 1882ed78aed75da6882145bb6c4747688b72eec0 Mon Sep 17 00:00:00 2001 From: Max Muoto Date: Wed, 3 Jul 2024 12:37:14 -0500 Subject: [PATCH 0680/1617] Have namedtuple `__replace__` return `Self` (#17475) --- mypy/semanal_namedtuple.py | 4 +++- test-data/unit/check-namedtuple.test | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 768dd265b338..bf526a1ee990 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -57,6 +57,7 @@ TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, + Instance, LiteralType, TupleType, Type, @@ -631,9 +632,10 @@ def add_method( args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) if self.options.python_version >= (3, 13): + type_vars = [tv for tv in info.defn.type_vars] add_method( "__replace__", - ret=None, + ret=Instance(info, type_vars), args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index e9d156754d9c..f10217b9aa5f 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1407,9 +1407,23 @@ from typing import NamedTuple class A(NamedTuple): x: int -A(x=0).__replace__(x=1) +replaced = A(x=0).__replace__(x=1) +reveal_type(replaced) # N: Revealed type is "__main__.A" + A(x=0).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "A" has incompatible type "str"; expected "int" A(x=0).__replace__(y=1) # E: Unexpected keyword argument "y" for "__replace__" of "A" + +from typing import TypeVar, Generic + +T = TypeVar("T") + +class GenericA(NamedTuple, Generic[T]): + x: T + +replaced_2 = GenericA(x=0).__replace__(x=1) +reveal_type(replaced_2) # N: Revealed type is "__main__.GenericA" +GenericA(x=0).__replace__(x="abc") # E: Argument "x" to "__replace__" of "GenericA" has incompatible type "str"; expected "int" + [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] From 606971807fad1de26ebc575d327d4c1c33f71c0e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:17:14 +0200 Subject: [PATCH 0681/1617] Bump version to 1.12.0+dev (#17467) The release branch has been cut: https://github.com/python/mypy/tree/release-1.11 --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index f2615b77109d..8e00b4cce702 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.11.0+dev" +__version__ = "1.12.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From ec00fb8db7f5c72faccde55a7f48dc6023c65411 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 5 Jul 2024 19:03:16 +0300 Subject: [PATCH 0682/1617] Refactor: remove temporary `def` from `semanal_classprop.check_protocol_status` (#17486) There's no need to create and call a temporary function, when we can just call an existing method: faster and simplier. --- mypy/semanal_classprop.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mypy/semanal_classprop.py b/mypy/semanal_classprop.py index b5f1b2181761..c5ad34122f6c 100644 --- a/mypy/semanal_classprop.py +++ b/mypy/semanal_classprop.py @@ -122,11 +122,12 @@ def check_protocol_status(info: TypeInfo, errors: Errors) -> None: if info.is_protocol: for type in info.bases: if not type.type.is_protocol and type.type.fullname != "builtins.object": - - def report(message: str, severity: str) -> None: - errors.report(info.line, info.column, message, severity=severity) - - report("All bases of a protocol must be protocols", "error") + errors.report( + info.line, + info.column, + "All bases of a protocol must be protocols", + severity="error", + ) def calculate_class_vars(info: TypeInfo) -> None: From 2e0b8687599fa033e78978dc78c42afa86934913 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Jul 2024 21:05:46 +0100 Subject: [PATCH 0683/1617] Revert "Have namedtuple `__replace__` return `Self`" (#17496) Reverts python/mypy#17475 --- mypy/semanal_namedtuple.py | 4 +--- test-data/unit/check-namedtuple.test | 16 +--------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index bf526a1ee990..768dd265b338 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -57,7 +57,6 @@ TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, - Instance, LiteralType, TupleType, Type, @@ -632,10 +631,9 @@ def add_method( args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) if self.options.python_version >= (3, 13): - type_vars = [tv for tv in info.defn.type_vars] add_method( "__replace__", - ret=Instance(info, type_vars), + ret=None, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index f10217b9aa5f..e9d156754d9c 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1407,23 +1407,9 @@ from typing import NamedTuple class A(NamedTuple): x: int -replaced = A(x=0).__replace__(x=1) -reveal_type(replaced) # N: Revealed type is "__main__.A" - +A(x=0).__replace__(x=1) A(x=0).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "A" has incompatible type "str"; expected "int" A(x=0).__replace__(y=1) # E: Unexpected keyword argument "y" for "__replace__" of "A" - -from typing import TypeVar, Generic - -T = TypeVar("T") - -class GenericA(NamedTuple, Generic[T]): - x: T - -replaced_2 = GenericA(x=0).__replace__(x=1) -reveal_type(replaced_2) # N: Revealed type is "__main__.GenericA" -GenericA(x=0).__replace__(x="abc") # E: Argument "x" to "__replace__" of "GenericA" has incompatible type "str"; expected "int" - [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] From 1ea867624d392f90755c7ccd3b410690ad87e891 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 6 Jul 2024 13:44:04 -0700 Subject: [PATCH 0684/1617] Run Python 3.13 tests in CI (with failures allowed) (#17484) https://github.com/python/mypy/issues/17264 --- .github/workflows/docs.yml | 2 +- .github/workflows/test.yml | 18 ++++++++++++++++++ test-requirements.in | 2 -- test-requirements.txt | 34 ++++------------------------------ 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f13a3de1f2e3..8bded1d380aa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,7 @@ jobs: with: python-version: '3.8' - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.11.0 + run: pip install tox==4.11.0 - name: Setup tox environment run: tox run -e ${{ env.TOXENV }} --notest - name: Test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98a737a78b3b..01d5876635b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -147,6 +147,24 @@ jobs: - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} + python-nightly: + runs-on: ubuntu-latest + name: Test suite with Python nightly + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.13-dev' + - name: Install tox + run: pip install setuptools==68.2.2 tox==4.11.0 + - name: Setup tox environment + run: tox run -e py --notest + - name: Test + run: tox run -e py --skip-pkg-install -- "-n 4" + continue-on-error: true + - name: Mark as a success + run: exit 0 + python_32bits: runs-on: ubuntu-latest name: Test mypyc suite with 32-bit Python diff --git a/test-requirements.in b/test-requirements.in index 637f5b948055..5a888811bfcd 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -8,8 +8,6 @@ black==24.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' -pre-commit -pre-commit-hooks==4.5.0 psutil>=4.0 pytest>=8.1.0 pytest-xdist>=1.34.0 diff --git a/test-requirements.txt b/test-requirements.txt index 9005daab2876..75a970c5bf0e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,22 +8,14 @@ attrs==23.1.0 # via -r test-requirements.in black==24.3.0 # via -r test-requirements.in -cfgv==3.4.0 - # via pre-commit click==8.1.7 # via black coverage==7.3.2 # via pytest-cov -distlib==0.3.7 - # via virtualenv execnet==2.0.2 # via pytest-xdist filelock==3.12.4 - # via - # -r test-requirements.in - # virtualenv -identify==2.5.30 - # via pre-commit + # via -r test-requirements.in iniconfig==2.0.0 # via pytest lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" @@ -32,8 +24,6 @@ mypy-extensions==1.0.0 # via # -r mypy-requirements.txt # black -nodeenv==1.8.0 - # via pre-commit packaging==23.2 # via # black @@ -41,15 +31,9 @@ packaging==23.2 pathspec==0.11.2 # via black platformdirs==3.11.0 - # via - # black - # virtualenv + # via black pluggy==1.4.0 # via pytest -pre-commit==3.5.0 - # via -r test-requirements.in -pre-commit-hooks==4.5.0 - # via -r test-requirements.in psutil==5.9.6 # via -r test-requirements.in pytest==8.1.1 @@ -61,12 +45,6 @@ pytest-cov==4.1.0 # via -r test-requirements.in pytest-xdist==3.3.1 # via -r test-requirements.in -pyyaml==6.0.1 - # via pre-commit -ruamel-yaml==0.17.40 - # via pre-commit-hooks -ruamel-yaml-clib==0.2.8 - # via ruamel-yaml ruff==0.2.0 # via -r test-requirements.in tomli==2.0.1 @@ -75,13 +53,9 @@ types-psutil==5.9.5.17 # via -r build-requirements.txt types-setuptools==68.2.0.0 # via -r build-requirements.txt -typing-extensions==4.8.0 +typing-extensions==4.12.2 # via -r mypy-requirements.txt -virtualenv==20.24.5 - # via pre-commit # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 - # via - # -r test-requirements.in - # nodeenv + # via -r test-requirements.in From 4ccf216492392ef673f4847ed8779fb54e826bd7 Mon Sep 17 00:00:00 2001 From: InSync Date: Sun, 7 Jul 2024 03:49:05 +0700 Subject: [PATCH 0685/1617] Fix cross-variable type-narrowing example (#17488) From [Type narrowing § Limitations](https://github.com/python/mypy/blob/606971807fad1de26ebc575d327d4c1c33f71c0e/docs/source/type_narrowing.rst#limitations): ```python def f(a: str | None, b: str | None) -> str: if a is not None or b is not None: return a or b # Incompatible return value type (got "str | None", expected "str") return 'spam' ``` A trivial counter-example is `f('', None)`, which returns `None`. Ironically, this somewhat makes Mypy's diagnostic "correct". I propose that `str` be replaced with a custom class `C` whose `__bool__()` is not defined (does it have to be `@final` too?): ```python class C: pass def f(a: C | None, b: C | None) -> C: if a is not None or b is not None: return a or b # Incompatible return value type (got "C | None", expected "C") return C() ``` --- docs/source/type_narrowing.rst | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 4c5c2851edd0..d698f35c44bc 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -368,14 +368,18 @@ Limitations Mypy's analysis is limited to individual symbols and it will not track relationships between symbols. For example, in the following code it's easy to deduce that if :code:`a` is None then :code:`b` must not be, -therefore :code:`a or b` will always be a string, but Mypy will not be able to tell that: +therefore :code:`a or b` will always be an instance of :code:`C`, +but Mypy will not be able to tell that: .. code-block:: python - def f(a: str | None, b: str | None) -> str: + class C: + pass + + def f(a: C | None, b: C | None) -> C: if a is not None or b is not None: - return a or b # Incompatible return value type (got "str | None", expected "str") - return 'spam' + return a or b # Incompatible return value type (got "C | None", expected "C") + return C() Tracking these sort of cross-variable conditions in a type checker would add significant complexity and performance overhead. @@ -385,9 +389,9 @@ or rewrite the function to be slightly more verbose: .. code-block:: python - def f(a: str | None, b: str | None) -> str: + def f(a: C | None, b: C | None) -> C: if a is not None: return a elif b is not None: return b - return 'spam' + return C() From e5b3b563ea3f6d1f83d0fe3552965c8b05ea4c3d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Jul 2024 22:04:07 +0100 Subject: [PATCH 0686/1617] Fix daemon crash on invalid type in TypedDict (#17495) Fixes https://github.com/python/mypy/issues/10007 Fixes https://github.com/python/mypy/issues/17477 This fixes the crash as proposed in https://github.com/python/mypy/pull/13732, but also fixes some inconsistencies in `Any` types exposed by the fix. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/semanal.py | 21 +++++++++++++++++ mypy/semanal_typeddict.py | 6 +++-- mypy/stats.py | 4 ++++ mypy/types.py | 5 +++- test-data/unit/check-flags.test | 4 ++-- test-data/unit/check-semanal-error.test | 31 ++++++++++++++++++++++++- test-data/unit/check-typeddict.test | 20 ++++++++++++++++ test-data/unit/reports.test | 16 ++++++------- test-data/unit/semanal-typeddict.test | 2 +- 9 files changed, 94 insertions(+), 15 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index f857c3e73381..f36149076fe6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3935,6 +3935,9 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # When this type alias gets "inlined", the Any is not explicit anymore, # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) + if self.options.disallow_any_unimported and has_any_from_unimported_type(res): + self.msg.unimported_type_becomes_any("Type alias target", res, s) + res = make_any_non_unimported(res) # Note: with the new (lazy) type alias representation we only need to set no_args to True # if the expected number of arguments is non-zero, so that aliases like `A = List` work # but not aliases like `A = TypeAliasType("A", List)` as these need explicit type params. @@ -5407,6 +5410,9 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: # When this type alias gets "inlined", the Any is not explicit anymore, # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) + if self.options.disallow_any_unimported and has_any_from_unimported_type(res): + self.msg.unimported_type_becomes_any("Type alias target", res, s) + res = make_any_non_unimported(res) eager = self.is_func_scope() if isinstance(res, ProperType) and isinstance(res, Instance) and not res.args: fix_instance(res, self.fail, self.note, disallow_any=False, options=self.options) @@ -7433,6 +7439,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: return t.copy_modified(args=[a.accept(self) for a in t.args]) +def make_any_non_unimported(t: Type) -> Type: + """Replace all Any types that come from unimported types with special form Any.""" + return t.accept(MakeAnyNonUnimported()) + + +class MakeAnyNonUnimported(TrivialSyntheticTypeTranslator): + def visit_any(self, t: AnyType) -> Type: + if t.type_of_any == TypeOfAny.from_unimported_type: + return t.copy_modified(TypeOfAny.special_form, missing_import_name=None) + return t + + def visit_type_alias_type(self, t: TypeAliasType) -> Type: + return t.copy_modified(args=[a.accept(self) for a in t.args]) + + def apply_semantic_analyzer_patches(patches: list[tuple[int, Callable[[], None]]]) -> None: """Call patch callbacks in the right order. diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index eee98d4d20fa..7b8d874337a2 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -310,11 +310,11 @@ def analyze_typeddict_classdef_fields( # Append stmt, name, and type in this case... fields.append(name) statements.append(stmt) - if stmt.type is None: + if stmt.unanalyzed_type is None: types.append(AnyType(TypeOfAny.unannotated)) else: analyzed = self.api.anal_type( - stmt.type, + stmt.unanalyzed_type, allow_required=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", @@ -322,6 +322,8 @@ def analyze_typeddict_classdef_fields( if analyzed is None: return None, [], [], set() # Need to defer types.append(analyzed) + if not has_placeholder(analyzed): + stmt.type = analyzed # ...despite possible minor failures that allow further analysis. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax: self.fail(TPDICT_CLASS_ERROR, stmt) diff --git a/mypy/stats.py b/mypy/stats.py index b167a41b0e34..9c69a245741b 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -203,7 +203,11 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: # Type variable definition -- not a real assignment. return if o.type: + # If there is an explicit type, don't visit the l.h.s. as an expression + # to avoid double-counting and mishandling special forms. self.type(o.type) + o.rvalue.accept(self) + return elif self.inferred and not self.all_nodes: # if self.all_nodes is set, lvalues will be visited later for lvalue in o.lvalues: diff --git a/mypy/types.py b/mypy/types.py index 2e7cbfd4e733..89609e8d0546 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1120,15 +1120,18 @@ def copy_modified( # Mark with Bogus because _dummy is just an object (with type Any) type_of_any: int = _dummy_int, original_any: Bogus[AnyType | None] = _dummy, + missing_import_name: Bogus[str | None] = _dummy, ) -> AnyType: if type_of_any == _dummy_int: type_of_any = self.type_of_any if original_any is _dummy: original_any = self.source_any + if missing_import_name is _dummy: + missing_import_name = self.missing_import_name return AnyType( type_of_any=type_of_any, source_any=original_any, - missing_import_name=self.missing_import_name, + missing_import_name=missing_import_name, line=self.line, column=self.column, ) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 62711d5f0071..4f327a2f0edc 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -924,9 +924,9 @@ class A(List[Unchecked]): # E: Base type becomes "List[Any]" due to an unfollowe from missing import Unchecked from typing import List -X = List[Unchecked] +X = List[Unchecked] # E: Type alias target becomes "List[Any]" due to an unfollowed import -def f(x: X) -> None: # E: Argument 1 to "f" becomes "List[Any]" due to an unfollowed import +def f(x: X) -> None: pass [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index c6cf45d96691..d7ab272aed6c 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -151,4 +151,33 @@ class C: x: P[int] = C() [builtins fixtures/tuple.pyi] -[out] + +[case testSemanalDoesNotLeakSyntheticTypes] +# flags: --cache-fine-grained +from typing import Generic, NamedTuple, TypedDict, TypeVar +from dataclasses import dataclass + +T = TypeVar('T') +class Wrap(Generic[T]): pass + +invalid_1: 1 + 2 # E: Invalid type comment or annotation +invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class A: + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class B(NamedTuple): + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class C(TypedDict): + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +@dataclass +class D: + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index fa77d98e4a34..d35ec8ddd80e 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2362,6 +2362,26 @@ Foo = TypedDict('Foo', {'camelCaseKey': str}) value: Foo = {} # E: Missing key "camelCaseKey" for TypedDict "Foo" [builtins fixtures/dict.pyi] +[case testTypedDictWithDeferredFieldTypeEval] +from typing import Generic, TypeVar, TypedDict, NotRequired + +class Foo(TypedDict): + y: NotRequired[int] + x: Outer[Inner[ForceDeferredEval]] + +var: Foo +reveal_type(var) # N: Revealed type is "TypedDict('__main__.Foo', {'y'?: builtins.int, 'x': __main__.Outer[__main__.Inner[__main__.ForceDeferredEval]]})" + +T1 = TypeVar("T1") +class Outer(Generic[T1]): pass + +T2 = TypeVar("T2", bound="ForceDeferredEval") +class Inner(Generic[T2]): pass + +class ForceDeferredEval: pass +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + -- Required[] [case testDoesRecognizeRequiredInTypedDictWithClass] diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 16061d9c32bf..81e24240af2d 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -81,19 +81,19 @@ def foo(a: int) -> MyDict: return {"a": a} md: MyDict = MyDict(**foo(42)) [outfile build/cobertura.xml] - + $PWD - + - + - + @@ -155,9 +155,9 @@ z: NestedGen[Any] [outfile report/types-of-anys.txt] Name Unannotated Explicit Unimported Omitted Generics Error Special Form Implementation Artifact ----------------------------------------------------------------------------------------------------------------- - n 0 4 0 8 0 0 0 + n 0 2 0 8 0 0 0 ----------------------------------------------------------------------------------------------------------------- -Total 0 4 0 8 0 0 0 +Total 0 2 0 8 0 0 0 [case testTypeVarTreatedAsEmptyLine] # cmd: mypy --html-report report n.py @@ -371,9 +371,9 @@ z = g.does_not_exist() # type: ignore # Error [outfile report/types-of-anys.txt] Name Unannotated Explicit Unimported Omitted Generics Error Special Form Implementation Artifact ----------------------------------------------------------------------------------------------------------------- - n 2 4 2 1 3 0 0 + n 2 3 1 1 3 0 0 ----------------------------------------------------------------------------------------------------------------- -Total 2 4 2 1 3 0 0 +Total 2 3 1 1 3 0 0 [case testAnyExpressionsReportUnqualifiedError] # cmd: mypy --any-exprs-report report n.py diff --git a/test-data/unit/semanal-typeddict.test b/test-data/unit/semanal-typeddict.test index b9eb6e0c2b13..9ce89155c308 100644 --- a/test-data/unit/semanal-typeddict.test +++ b/test-data/unit/semanal-typeddict.test @@ -42,4 +42,4 @@ MypyFile:1( NameExpr(x) TempNode:4( Any) - str?))) + builtins.str))) From 1acdfd073010a13a7e3b982097a9a1a7a5d9a544 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Jul 2024 22:38:54 +0100 Subject: [PATCH 0687/1617] Fix crash on NamedTuple with method and error in function (#17498) Fixes https://github.com/python/mypy/issues/16814 This one is tricky and may expose some other bugs. But IMO this is strictly correct thing to do. --- mypy/semanal_main.py | 2 ++ test-data/unit/check-namedtuple.test | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 1185a3821553..09a1223be6aa 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -291,6 +291,8 @@ def process_top_level_function( deferred, incomplete, progress = semantic_analyze_target( target, module, state, node, active_type, final_iteration, patches ) + if not incomplete: + state.manager.incomplete_namespaces.discard(module) if final_iteration: assert not deferred, "Must not defer during final iteration" if not progress: diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index e9d156754d9c..147270dff72e 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1423,3 +1423,27 @@ class Foo(typing.NamedTuple): reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNameErrorInNamedTupleNestedInFunction1] +from typing import NamedTuple + +def bar() -> None: + class MyNamedTuple(NamedTuple): + a: int + def foo(self) -> None: + ... + int_set: Set[int] # E: Name "Set" is not defined \ + # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Set") +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] + +[case testNameErrorInNamedTupleNestedInFunction2] +from typing import NamedTuple + +def bar() -> None: + class MyNamedTuple(NamedTuple): + a: int + def foo(self) -> None: + misspelled_var_name # E: Name "misspelled_var_name" is not defined +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] From 9c0a6f9ba355b307cd87c861619040e4b4691af4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 6 Jul 2024 23:57:05 +0100 Subject: [PATCH 0688/1617] Fix crash on self-type in callable protocol (#17499) Fixes https://github.com/python/mypy/issues/16629 This is really ad-hoc, but a proper fix would be much more hard, and this currently we have a crash in a relatively common scenario. --- mypy/solve.py | 12 ++++++++++++ test-data/unit/check-selftype.test | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/mypy/solve.py b/mypy/solve.py index bb87b6576ada..8a1495a9a246 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -553,6 +553,11 @@ def pre_validate_solutions( """ new_solutions: list[Type | None] = [] for t, s in zip(original_vars, solutions): + if is_callable_protocol(t.upper_bound): + # This is really ad-hoc, but a proper fix would be much more complex, + # and otherwise this may cause crash in a relatively common scenario. + new_solutions.append(s) + continue if s is not None and not is_subtype(s, t.upper_bound): bound_satisfies_all = True for c in constraints: @@ -567,3 +572,10 @@ def pre_validate_solutions( continue new_solutions.append(s) return new_solutions + + +def is_callable_protocol(t: Type) -> bool: + proper_t = get_proper_type(t) + if isinstance(proper_t, Instance) and proper_t.type.is_protocol: + return "__call__" in proper_t.type.protocol_members + return False diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 1480c83b2272..9601852ef823 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2132,3 +2132,31 @@ class D: x: int x: Union[C, D] reveal_type(x.x) # N: Revealed type is "Union[__main__.C, builtins.int]" + +[case testCallableProtocolTypingSelf] +from typing import Protocol, Self + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test + +[case testCallableProtocolOldSelf] +from typing import Protocol, TypeVar + +Self = TypeVar("Self", bound="MyProtocol") + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test From 6d45f3cb58ce4781b7e7f47469358af9441ea48b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 7 Jul 2024 10:59:20 +0100 Subject: [PATCH 0689/1617] Experimental: allow inline/anonymous TypedDicts (#17457) Fixes https://github.com/python/mypy/issues/9884 I was always a bit skeptical about this thing, since it feels more like TypeScript than Python, but it is second most upvoted issue. Also (this specific) implementation is like 60 lines of code plus tests, so why not. I know there is no PEP etc., but IMO this syntax is obvious and it just works. cc @JukkaL --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/command_line.rst | 21 +++++++++++ docs/source/typed_dict.rst | 38 +++++++++++++++++++ mypy/checker.py | 6 ++- mypy/exprtotype.py | 31 ++++++++++++++- mypy/fastparse.py | 20 +++++++++- mypy/message_registry.py | 1 + mypy/nodes.py | 1 + mypy/options.py | 3 +- mypy/semanal_typeddict.py | 15 ++------ mypy/typeanal.py | 55 +++++++++++++++++++++++---- mypy/types.py | 4 +- test-data/unit/check-literal.test | 19 +++++----- test-data/unit/check-python312.test | 15 +++++++- test-data/unit/check-typeddict.test | 58 ++++++++++++++++++++++++++++- 14 files changed, 247 insertions(+), 40 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 50a6ef65f4d0..906231dc7e42 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -1055,6 +1055,27 @@ List of currently incomplete/experimental features: # Without PreciseTupleTypes: tuple[int, ...] # With PreciseTupleTypes: tuple[()] | tuple[int] | tuple[int, int] +* ``NewGenericSyntax``: this feature enables support for syntax defined + by :pep:`695`. For example: + + .. code-block:: python + + class Container[T]: # defines a generic class + content: T + + def first[T](items: list[T]) -> T: # defines a generic function + return items[0] + + type Items[T] = list[tuple[T, T]] # defines a generic type alias + +* ``InlineTypedDict``: this feature enables non-standard syntax for inline + :ref:`TypedDicts `, for example: + + .. code-block:: python + + def test_values() -> {"int": int, "str": str}: + return {"int": 42, "str": "test"} + Miscellaneous ************* diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index e5ce2927db4d..c379b5449eae 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -248,3 +248,41 @@ section of the docs has a full description with an example, but in short, you wi need to give each TypedDict the same key where each value has a unique :ref:`Literal type `. Then, check that key to distinguish between your TypedDicts. + +Inline TypedDict types +---------------------- + +.. note:: + + This is an experimental (non-standard) feature. Use + ``--enable-incomplete-feature=InlineTypedDict`` to enable. + +Sometimes you may want to define a complex nested JSON schema, or annotate +a one-off function that returns a TypedDict. In such cases it may be convenient +to use inline TypedDict syntax. For example: + +.. code-block:: python + + def test_values() -> {"int": int, "str": str}: + return {"int": 42, "str": "test"} + + class Response(TypedDict): + status: int + msg: str + # Using inline syntax here avoids defining two additional TypedDicts. + content: {"items": list[{"key": str, "value": str}]} + +Inline TypedDicts can also by used as targets of type aliases, but due to +ambiguity with a regular variables it is only allowed for (newer) explicit +type alias forms: + +.. code-block:: python + + from typing import TypeAlias + + X = {"a": int, "b": int} # creates a variable with type dict[str, type[int]] + Y: TypeAlias = {"a": int, "b": int} # creates a type alias + type Z = {"a": int, "b": int} # same as above (Python 3.12+ only) + +Also, due to incompatibility with runtime type-checking it is strongly recommended +to *not* use inline syntax in union types. diff --git a/mypy/checker.py b/mypy/checker.py index 2df74cf7be8d..0ae499916ec6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2971,7 +2971,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.annotation_in_unchecked_function(context=s) def check_type_alias_rvalue(self, s: AssignmentStmt) -> None: - alias_type = self.expr_checker.accept(s.rvalue) + with self.msg.filter_errors(): + alias_type = self.expr_checker.accept(s.rvalue) self.store_type(s.lvalues[-1], alias_type) def check_assignment( @@ -5311,7 +5312,8 @@ def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, del type_map[expr] def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: - self.expr_checker.accept(o.value) + with self.msg.filter_errors(): + self.expr_checker.accept(o.value) def make_fake_typeinfo( self, diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index d9bdf2e2b20b..92316d11926d 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -4,9 +4,11 @@ from mypy.fastparse import parse_type_string from mypy.nodes import ( + MISSING_FALLBACK, BytesExpr, CallExpr, ComplexExpr, + DictExpr, EllipsisExpr, Expression, FloatExpr, @@ -29,9 +31,11 @@ AnyType, CallableArgument, EllipsisType, + Instance, ProperType, RawExpressionType, Type, + TypedDictType, TypeList, TypeOfAny, UnboundType, @@ -55,7 +59,7 @@ def _extract_argument_name(expr: Expression) -> str | None: def expr_to_unanalyzed_type( expr: Expression, - options: Options | None = None, + options: Options, allow_new_syntax: bool = False, _parent: Expression | None = None, allow_unpack: bool = False, @@ -67,6 +71,8 @@ def expr_to_unanalyzed_type( If allow_new_syntax is True, allow all type syntax independent of the target Python version (used in stubs). + + # TODO: a lot of code here is duplicated in fastparse.py, refactor this. """ # The `parent` parameter is used in recursive calls to provide context for # understanding whether an CallableArgument is ok. @@ -116,7 +122,7 @@ def expr_to_unanalyzed_type( elif ( isinstance(expr, OpExpr) and expr.op == "|" - and ((options and options.python_version >= (3, 10)) or allow_new_syntax) + and ((options.python_version >= (3, 10)) or allow_new_syntax) ): return UnionType( [ @@ -206,5 +212,26 @@ def expr_to_unanalyzed_type( return UnpackType( expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax), from_star_syntax=True ) + elif isinstance(expr, DictExpr): + if not expr.items: + raise TypeTranslationError() + items: dict[str, Type] = {} + extra_items_from = [] + for item_name, value in expr.items: + if not isinstance(item_name, StrExpr): + if item_name is None: + extra_items_from.append( + expr_to_unanalyzed_type(value, options, allow_new_syntax, expr) + ) + continue + raise TypeTranslationError() + items[item_name.value] = expr_to_unanalyzed_type( + value, options, allow_new_syntax, expr + ) + result = TypedDictType( + items, set(), Instance(MISSING_FALLBACK, ()), expr.line, expr.column + ) + result.extra_items_from = extra_items_from + return result else: raise TypeTranslationError() diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 01f6ed4733ae..75c4bd46550c 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -17,6 +17,7 @@ ARG_POS, ARG_STAR, ARG_STAR2, + MISSING_FALLBACK, PARAM_SPEC_KIND, TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, @@ -42,7 +43,6 @@ EllipsisExpr, Expression, ExpressionStmt, - FakeInfo, FloatExpr, ForStmt, FuncDef, @@ -116,6 +116,7 @@ RawExpressionType, TupleType, Type, + TypedDictType, TypeList, TypeOfAny, UnboundType, @@ -190,7 +191,6 @@ def ast3_parse( # There is no way to create reasonable fallbacks at this stage, # they must be patched later. -MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal") _dummy_fallback: Final = Instance(MISSING_FALLBACK, [], -1) TYPE_IGNORE_PATTERN: Final = re.compile(r"[^#]*#\s*type:\s*ignore\s*(.*)") @@ -2106,6 +2106,22 @@ def visit_Tuple(self, n: ast3.Tuple) -> Type: column=self.convert_column(n.col_offset), ) + def visit_Dict(self, n: ast3.Dict) -> Type: + if not n.keys: + return self.invalid_type(n) + items: dict[str, Type] = {} + extra_items_from = [] + for item_name, value in zip(n.keys, n.values): + if not isinstance(item_name, ast3.Constant) or not isinstance(item_name.value, str): + if item_name is None: + extra_items_from.append(self.visit(value)) + continue + return self.invalid_type(n) + items[item_name.value] = self.visit(value) + result = TypedDictType(items, set(), _dummy_fallback, n.lineno, n.col_offset) + result.extra_items_from = extra_items_from + return result + # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index befacc9e6182..06199e70d6b4 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -138,6 +138,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = ErrorMessage( "Expected TypedDict key to be string literal" ) +TYPEDDICT_OVERRIDE_MERGE: Final = 'Overwriting TypedDict field "{}" while merging' MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?") DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures") DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable") diff --git a/mypy/nodes.py b/mypy/nodes.py index 2eb39d4baaf6..4a5c7240fa83 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3480,6 +3480,7 @@ def __getattribute__(self, attr: str) -> type: VAR_NO_INFO: Final[TypeInfo] = FakeInfo("Var is lacking info") CLASSDEF_NO_INFO: Final[TypeInfo] = FakeInfo("ClassDef is lacking info") FUNC_NO_INFO: Final[TypeInfo] = FakeInfo("FuncBase for non-methods lack info") +MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal") class TypeAlias(SymbolNode): diff --git a/mypy/options.py b/mypy/options.py index 5ef6bc2a35e7..bff096d82c15 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -74,7 +74,8 @@ class BuildType: UNPACK: Final = "Unpack" PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" NEW_GENERIC_SYNTAX: Final = "NewGenericSyntax" -INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, NEW_GENERIC_SYNTAX)) +INLINE_TYPEDDICT: Final = "InlineTypedDict" +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, NEW_GENERIC_SYNTAX, INLINE_TYPEDDICT)) COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 7b8d874337a2..e639871364ce 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -8,6 +8,7 @@ from mypy.errorcodes import ErrorCode from mypy.expandtype import expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type +from mypy.message_registry import TYPEDDICT_OVERRIDE_MERGE from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_NAMED, @@ -216,7 +217,7 @@ def add_keys_and_types_from_base( valid_items = self.map_items_to_base(valid_items, tvars, base_args) for key in base_items: if key in keys: - self.fail(f'Overwriting TypedDict field "{key}" while merging', ctx) + self.fail(TYPEDDICT_OVERRIDE_MERGE.format(key), ctx) keys.extend(valid_items.keys()) types.extend(valid_items.values()) required_keys.update(base_typed_dict.required_keys) @@ -507,17 +508,7 @@ def parse_typeddict_fields_with_types( field_type_expr, self.options, self.api.is_stub_file ) except TypeTranslationError: - if ( - isinstance(field_type_expr, CallExpr) - and isinstance(field_type_expr.callee, RefExpr) - and field_type_expr.callee.fullname in TPDICT_NAMES - ): - self.fail_typeddict_arg( - "Inline TypedDict types not supported; use assignment to define TypedDict", - field_type_expr, - ) - else: - self.fail_typeddict_arg("Invalid field type", field_type_expr) + self.fail_typeddict_arg("Use dict literal for nested TypedDict", field_type_expr) return [], [], False analyzed = self.api.anal_type( type, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 6651af7dad4f..f63aef30a09a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -10,7 +10,11 @@ from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode from mypy.expandtype import expand_type -from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE +from mypy.message_registry import ( + INVALID_PARAM_SPEC_LOCATION, + INVALID_PARAM_SPEC_LOCATION_NOTE, + TYPEDDICT_OVERRIDE_MERGE, +) from mypy.messages import ( MessageBuilder, format_type, @@ -25,6 +29,7 @@ ARG_POS, ARG_STAR, ARG_STAR2, + MISSING_FALLBACK, SYMBOL_FUNCBASE_TYPES, ArgKind, Context, @@ -43,7 +48,7 @@ check_arg_names, get_nongen_builtins, ) -from mypy.options import Options +from mypy.options import INLINE_TYPEDDICT, Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface from mypy.semanal_shared import ( SemanticAnalyzerCoreInterface, @@ -1220,10 +1225,45 @@ def visit_tuple_type(self, t: TupleType) -> Type: return TupleType(self.anal_array(t.items, allow_unpack=True), fallback, t.line) def visit_typeddict_type(self, t: TypedDictType) -> Type: - items = { - item_name: self.anal_type(item_type) for (item_name, item_type) in t.items.items() - } - return TypedDictType(items, set(t.required_keys), t.fallback) + req_keys = set() + items = {} + for item_name, item_type in t.items.items(): + analyzed = self.anal_type(item_type, allow_required=True) + if isinstance(analyzed, RequiredType): + if analyzed.required: + req_keys.add(item_name) + analyzed = analyzed.item + else: + # Keys are required by default. + req_keys.add(item_name) + items[item_name] = analyzed + if t.fallback.type is MISSING_FALLBACK: # anonymous/inline TypedDict + if INLINE_TYPEDDICT not in self.options.enable_incomplete_feature: + self.fail( + "Inline TypedDict is experimental," + " must be enabled with --enable-incomplete-feature=InlineTypedDict", + t, + ) + required_keys = req_keys + fallback = self.named_type("typing._TypedDict") + for typ in t.extra_items_from: + analyzed = self.analyze_type(typ) + p_analyzed = get_proper_type(analyzed) + if not isinstance(p_analyzed, TypedDictType): + if not isinstance(p_analyzed, (AnyType, PlaceholderType)): + self.fail("Can only merge-in other TypedDict", t, code=codes.VALID_TYPE) + continue + for sub_item_name, sub_item_type in p_analyzed.items.items(): + if sub_item_name in items: + self.fail(TYPEDDICT_OVERRIDE_MERGE.format(sub_item_name), t) + continue + items[sub_item_name] = sub_item_type + if sub_item_name in p_analyzed.required_keys: + req_keys.add(sub_item_name) + else: + required_keys = t.required_keys + fallback = t.fallback + return TypedDictType(items, required_keys, fallback, t.line, t.column) def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # We should never see a bare Literal. We synthesize these raw literals @@ -1761,11 +1801,12 @@ def anal_type( allow_param_spec: bool = False, allow_unpack: bool = False, allow_ellipsis: bool = False, + allow_required: bool = False, ) -> Type: if nested: self.nesting_level += 1 old_allow_required = self.allow_required - self.allow_required = False + self.allow_required = allow_required old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis old_allow_unpack = self.allow_unpack diff --git a/mypy/types.py b/mypy/types.py index 89609e8d0546..91b40536f1cf 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2521,11 +2521,12 @@ class TypedDictType(ProperType): TODO: The fallback structure is perhaps overly complicated. """ - __slots__ = ("items", "required_keys", "fallback") + __slots__ = ("items", "required_keys", "fallback", "extra_items_from") items: dict[str, Type] # item_name -> item_type required_keys: set[str] fallback: Instance + extra_items_from: list[ProperType] # only used during semantic analysis def __init__( self, @@ -2541,6 +2542,7 @@ def __init__( self.fallback = fallback self.can_be_true = len(self.items) > 0 self.can_be_false = len(self.required_keys) == 0 + self.extra_items_from = [] def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_typeddict_type(self) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 8f8aaf6a3982..6d76ce176aaf 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -608,36 +608,35 @@ e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain a [case testLiteralDisallowCollections] from typing_extensions import Literal -a: Literal[{"a": 1, "b": 2}] # E: Invalid type: Literal[...] cannot contain arbitrary expressions +a: Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid b: Literal[{1, 2, 3}] # E: Invalid type: Literal[...] cannot contain arbitrary expressions -c: {"a": 1, "b": 2} # E: Invalid type comment or annotation +c: {"a": 1, "b": 2} # E: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict \ + # E: Invalid type: try using Literal[1] instead? \ + # E: Invalid type: try using Literal[2] instead? d: {1, 2, 3} # E: Invalid type comment or annotation [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testLiteralDisallowCollections2] - from typing_extensions import Literal a: (1, 2, 3) # E: Syntax error in type annotation \ # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) b: Literal[[1, 2, 3]] # E: Parameter 1 of Literal[...] is invalid c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid as a type [builtins fixtures/tuple.pyi] -[out] [case testLiteralDisallowCollectionsTypeAlias] - from typing_extensions import Literal -at = Literal[{"a": 1, "b": 2}] # E: Invalid type alias: expression is not a valid type +at = Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid bt = {"a": 1, "b": 2} -a: at # E: Variable "__main__.at" is not valid as a type \ - # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +a: at +reveal_type(a) # N: Revealed type is "Any" b: bt # E: Variable "__main__.bt" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases [builtins fixtures/dict.pyi] -[out] +[typing fixtures/typing-typeddict.pyi] [case testLiteralDisallowCollectionsTypeAlias2] - from typing_extensions import Literal at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type bt = {1, 2, 3} diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 5307f47d539a..073ef7f4bdec 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -585,8 +585,7 @@ reveal_type(a) # N: Revealed type is "Any" [case testPEP695TypeAliasInvalidType] # flags: --enable-incomplete-feature=NewGenericSyntax -type A = int | 1 # E: Invalid type: try using Literal[1] instead? \ - # E: Unsupported operand types for | ("Type[int]" and "int") +type A = int | 1 # E: Invalid type: try using Literal[1] instead? a: A reveal_type(a) # N: Revealed type is "Union[builtins.int, Any]" @@ -1656,3 +1655,15 @@ type I2 = C[Any] | None type I3 = None | C[TD] [builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] + +[case testTypedDictInlineYesNewStyleAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax --enable-incomplete-feature=InlineTypedDict +type X[T] = {"item": T, "other": X[T] | None} +x: X[str] +reveal_type(x) # N: Revealed type is "TypedDict({'item': builtins.str, 'other': Union[..., None]})" +if x["other"] is not None: + reveal_type(x["other"]["item"]) # N: Revealed type is "builtins.str" + +type Y[T] = {"item": T, **Y[T]} # E: Overwriting TypedDict field "item" while merging +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index d35ec8ddd80e..6a5120159c2d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -78,7 +78,7 @@ p = Point(x='meaning_of_life', y=1337) # E: Incompatible types (expression has [case testCannotCreateTypedDictInstanceWithInlineTypedDict] from mypy_extensions import TypedDict D = TypedDict('D', { - 'x': TypedDict('E', { # E: Inline TypedDict types not supported; use assignment to define TypedDict + 'x': TypedDict('E', { # E: Use dict literal for nested TypedDict 'y': int }) }) @@ -3570,3 +3570,59 @@ class Test: run(test2, other="yes", **params) run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] + +[case testTypedDictInlineNoOldStyleAlias] +# flags: --enable-incomplete-feature=InlineTypedDict +X = {"int": int, "str": str} +reveal_type(X) # N: Revealed type is "builtins.dict[builtins.str, def () -> builtins.object]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineYesMidStyleAlias] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing_extensions import TypeAlias +X: TypeAlias = {"int": int, "str": str} +x: X +reveal_type(x) # N: # N: Revealed type is "TypedDict({'int': builtins.int, 'str': builtins.str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNoEmpty] +# flags: --enable-incomplete-feature=InlineTypedDict +x: {} # E: Invalid type comment or annotation +reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNotRequired] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import NotRequired + +x: {"one": int, "other": NotRequired[int]} +x = {"one": 1} # OK +y: {"one": int, "other": int} +y = {"one": 1} # E: Expected TypedDict keys ("one", "other") but found only key "one" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNestedSchema] +# flags: --enable-incomplete-feature=InlineTypedDict +def nested() -> {"one": str, "other": {"a": int, "b": int}}: + if bool(): + return {"one": "yes", "other": {"a": 1, "b": 2}} # OK + else: + return {"one": "no", "other": {"a": 1, "b": "2"}} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineMergeAnother] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import TypeVar +from typing_extensions import TypeAlias + +T = TypeVar("T") +X: TypeAlias = {"item": T} +x: {"a": int, **X[str], "b": int} +reveal_type(x) # N: Revealed type is "TypedDict({'a': builtins.int, 'b': builtins.int, 'item': builtins.str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] From 4c54801afefa545bee6ee205578f4f53c6afeedb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:00:14 +0200 Subject: [PATCH 0690/1617] Fix `_PyObject_FastCall` for Python 3.13 (#17502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_PyObject_FastCall` will be removed in 3.13. It can safely replaced by `PyObject_Vectorcall` (available since `3.9`) / `_PyObject_Vectorcall` (available since `3.8`). https://github.com/python/cpython/issues/106023#issuecomment-1613963489 https://peps.python.org/pep-0590/ Fixes ```cpp /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h: In function ‘update_bases’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:62:20: error: implicit declaration of function ‘_PyObject_FastCall’; did you mean ‘PyObject_Call’? [-Werror=implicit-function-declaration] (diff) 62 | new_base = _PyObject_FastCall(meth, stack, 1); (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:62:18: error: assignment to ‘PyObject *’ {aka ‘struct _object *’} from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] (diff) 62 | new_base = _PyObject_FastCall(meth, stack, 1); (diff) | ^ (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h: In function ‘init_subclass’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:111:11: error: assignment to ‘PyObject *’ {aka ‘struct _object *’} from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] (diff) 111 | super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2); (diff) | ^ (diff) ``` --- mypyc/lib-rt/pythonsupport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 85f9ec64ac90..87e034f1fc51 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -59,7 +59,7 @@ update_bases(PyObject *bases) } continue; } - new_base = _PyObject_FastCall(meth, stack, 1); + new_base = _PyObject_Vectorcall(meth, stack, 1, NULL); Py_DECREF(meth); if (!new_base) { goto error; @@ -108,7 +108,7 @@ init_subclass(PyTypeObject *type, PyObject *kwds) PyObject *super, *func, *result; PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; - super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2); + super = _PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); if (super == NULL) { return -1; } From d4f7e5cd67c83f08f1e2dab418725351ff15da1b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:06:02 +0200 Subject: [PATCH 0691/1617] Fix `gen_is_coroutine` for Python 3.13 (#17501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `_PyInterpreterFrame` struct was changed in https://github.com/python/cpython/pull/105727 to store the code object in `f_executable` instead of `f_code`. Fixes ```cpp /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h: In function ‘_PyGen_GetCode’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:403:17: error: ‘_PyInterpreterFrame’ has no member named ‘f_code’ (diff) 403 | return frame->f_code; (diff) | ^~ (diff) ``` --- mypyc/lib-rt/mypyc_util.h | 3 +++ mypyc/lib-rt/pythonsupport.h | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 3c888a581a33..9967f0a13b4f 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -115,4 +115,7 @@ static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { #endif +// Are we targeting Python 3.13 or newer? +#define CPY_3_13_FEATURES (PY_VERSION_HEX >= 0x030d0000) + #endif diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 87e034f1fc51..c5423ab0fab5 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -392,7 +392,30 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { _PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL) #endif -#if CPY_3_12_FEATURES +#if CPY_3_13_FEATURES + +// These are copied from genobject.c in Python 3.13 + +/* Returns a borrowed reference */ +static inline PyCodeObject * +_PyGen_GetCode(PyGenObject *gen) { + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); + return _PyFrame_GetCode(frame); +} + +static int +gen_is_coroutine(PyObject *o) +{ + if (PyGen_CheckExact(o)) { + PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); + if (code->co_flags & CO_ITERABLE_COROUTINE) { + return 1; + } + } + return 0; +} + +#elif CPY_3_12_FEATURES // These are copied from genobject.c in Python 3.12 From 9175ce59322759d3e02727fd804008ee2234bf65 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:10:09 +0200 Subject: [PATCH 0692/1617] Fix `_PyList_Extend` for Python 3.13 (#17503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `_PyList_Extend` with `PyList_Extend` from `pythoncapi_compat.h`. https://github.com/python/cpython/issues/111138 https://docs.python.org/dev/c-api/list.html#c.PyList_Extend Fixes ```cpp /home/runner/work/mypy/mypy/mypyc/lib-rt/list_ops.c: In function ‘CPyList_Extend’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/list_ops.c:259:12: error: implicit declaration of function ‘_PyList_Extend’; did you mean ‘CPyList_Extend’? [-Werror=implicit-function-declaration] (diff) 259 | return _PyList_Extend((PyListObject *)o1, o2); (diff) | ^~~~~~~~~~~~~~ (diff) | CPyList_Extend (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/list_ops.c:259:12: error: returning ‘int’ from a function with return type ‘PyObject *’ {aka ‘struct _object *’} makes pointer from integer without a cast [-Werror=int-conversion] (diff) 259 | return _PyList_Extend((PyListObject *)o1, o2); (diff) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c: In function ‘CPyDict_Keys’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c:233:21: error: initialization of ‘PyObject *’ {aka ‘struct _object *’} from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] (diff) 233 | PyObject *res = _PyList_Extend((PyListObject *)list, view); (diff) | ^~~~~~~~~~~~~~ (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c: In function ‘CPyDict_Values’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c:253:21: error: initialization of ‘PyObject *’ {aka ‘struct _object *’} from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] (diff) 253 | PyObject *res = _PyList_Extend((PyListObject *)list, view); (diff) | ^~~~~~~~~~~~~~ (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c: In function ‘CPyDict_Items’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/dict_ops.c:273:21: error: initialization of ‘PyObject *’ {aka ‘struct _object *’} from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion] (diff) 273 | PyObject *res = _PyList_Extend((PyListObject *)list, view); (diff) | ^~~~~~~~~~~~~~ (diff) ``` --- mypyc/lib-rt/dict_ops.c | 15 ++++++--------- mypyc/lib-rt/list_ops.c | 5 ++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mypyc/lib-rt/dict_ops.c b/mypyc/lib-rt/dict_ops.c index c0cc8d5a7f87..031df8f63c49 100644 --- a/mypyc/lib-rt/dict_ops.c +++ b/mypyc/lib-rt/dict_ops.c @@ -230,12 +230,11 @@ PyObject *CPyDict_Keys(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } @@ -250,12 +249,11 @@ PyObject *CPyDict_Values(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } @@ -270,12 +268,11 @@ PyObject *CPyDict_Items(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c index df87228a0d10..d297ece8f417 100644 --- a/mypyc/lib-rt/list_ops.c +++ b/mypyc/lib-rt/list_ops.c @@ -256,7 +256,10 @@ int CPyList_Insert(PyObject *list, CPyTagged index, PyObject *value) } PyObject *CPyList_Extend(PyObject *o1, PyObject *o2) { - return _PyList_Extend((PyListObject *)o1, o2); + if (PyList_Extend(o1, o2) < 0) { + return NULL; + } + Py_RETURN_NONE; } // Return -2 or error, -1 if not found, or index of first match otherwise. From 7f67090b10694657bc72f39bdeaa3b189c7be7fc Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:56:33 +0200 Subject: [PATCH 0693/1617] Fix `_PyObject_LookupAttrId` for Python 3.13 (#17505) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_PyObject_LookupAttrId` was removed / replaced with `PyObject_GetOptionalAttrString` in https://github.com/python/cpython/pull/106522. https://docs.python.org/dev/c-api/object.html#c.PyObject_GetOptionalAttrString Fixes ```cpp /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h: In function ‘update_bases’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:51:13: error: implicit declaration of function ‘_PyObject_LookupAttrId’; did you mean ‘_PyObject_GetAttrId’? [-Werror=implicit-function-declaration] (diff) 51 | if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) { (diff) | ^~~~~~~~~~~~~~~~~~~~~~ (diff) | _PyObject_GetAttrId (diff) ``` --- mypyc/lib-rt/pythonsupport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index c5423ab0fab5..2d18e19b2c56 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -48,7 +48,7 @@ update_bases(PyObject *bases) } continue; } - if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) { + if (PyObject_GetOptionalAttrString(base, PyId___mro_entries__.string, &meth) < 0) { goto error; } if (!meth) { @@ -374,7 +374,7 @@ _CPyDictView_New(PyObject *dict, PyTypeObject *type) static int _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { PyObject *tmp = NULL; - int result = _PyObject_LookupAttrId(v, name, &tmp); + int result = PyObject_GetOptionalAttrString(v, name->string, &tmp); if (tmp) { Py_DECREF(tmp); } From 966d6d36595f9b029afb04271dc90fa5fd106005 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Jul 2024 01:00:01 +0200 Subject: [PATCH 0694/1617] Fix `PyUnicode` functions for Python 3.13 (#17504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `_PyUnicode_EqualToASCIIString` with `PyUnicode_EqualToUTF8`. https://docs.python.org/dev/c-api/unicode.html#c.PyUnicode_EqualToUTF8 https://github.com/python/cpython/issues/110289 Fixes ```cpp /home/runner/work/mypy/mypy/mypyc/lib-rt/getargs.c: In function ‘vgetargskeywords’: (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/pythonsupport.h:310:45: error: implicit declaration of function ‘_PyUnicode_EqualToASCIIString’; did you mean ‘CPyUnicode_EqualToASCIIString’? [-Werror=implicit-function-declaration] (diff) 310 | #define CPyUnicode_EqualToASCIIString(x, y) _PyUnicode_EqualToASCIIString(x, y) (diff) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (diff) /home/runner/work/mypy/mypy/mypyc/lib-rt/getargs.c:398:21: note: in expansion of macro ‘CPyUnicode_EqualToASCIIString’ (diff) 398 | if (CPyUnicode_EqualToASCIIString(key, kwlist[i])) { (diff) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (diff) ``` --- mypyc/lib-rt/getargs.c | 2 +- mypyc/lib-rt/pythonsupport.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 3c8b528f8048..1bc2f5b02ba8 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -395,7 +395,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, goto latefail; } for (i = pos; i < len; i++) { - if (CPyUnicode_EqualToASCIIString(key, kwlist[i])) { + if (PyUnicode_EqualToUTF8(key, kwlist[i])) { match = 1; break; } diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 2d18e19b2c56..85eb6348eb81 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -307,8 +307,6 @@ list_count(PyListObject *self, PyObject *value) return CPyTagged_ShortFromSsize_t(count); } -#define CPyUnicode_EqualToASCIIString(x, y) _PyUnicode_EqualToASCIIString(x, y) - // Adapted from genobject.c in Python 3.7.2 // Copied because it wasn't in 3.5.2 and it is undocumented anyways. /* From 4e3346ee1dee83868adc2411c4a0f4050cf2f95a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Jul 2024 01:00:31 +0200 Subject: [PATCH 0695/1617] Add additional includes for Python 3.13 (#17506) Define `Py_BUILD_CORE` required by `internal/...` header files. Include additional headers for moved private functions. ```cpp /opt/hostedtoolcache/Python/3.13.0-beta.3/x64/include/python3.13/internal/pycore_frame.h:8:4: error: #error "this header requires Py_BUILD_CORE define" (diff) 8 | # error "this header requires Py_BUILD_CORE define" (diff) ``` --- mypyc/lib-rt/pythonsupport.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 85eb6348eb81..69ff120dd40d 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -13,6 +13,18 @@ #include #include "mypyc_util.h" +#if CPY_3_13_FEATURES +#ifndef Py_BUILD_CORE +#define Py_BUILD_CORE +#endif +#include "internal/pycore_bytesobject.h" // _PyBytes_Join +#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdObjArgs, _PyObject_CallMethodIdOneArg +#include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue +#include "internal/pycore_object.h" // _PyType_CalculateMetaclass +#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError +#include "internal/pycore_unicodeobject.h" // _PyUnicode_EQ, _PyUnicode_FastCopyCharacters +#endif + #if CPY_3_12_FEATURES #include "internal/pycore_frame.h" #endif From acc65b5a1ec6065ed06b5be5c2c55ef9ff90d610 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:07:33 +0200 Subject: [PATCH 0696/1617] Update mypyc test output for Python 3.13 (#17508) --- mypyc/test-data/run-exceptions.test | 86 ++++++++++++++++++++++++++++ mypyc/test-data/run-loops.test | 35 +++++++++++ mypyc/test-data/run-misc.test | 5 +- mypyc/test-data/run-multimodule.test | 17 ++++++ 4 files changed, 142 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/run-exceptions.test b/mypyc/test-data/run-exceptions.test index c591fc1d8c15..1b180b933197 100644 --- a/mypyc/test-data/run-exceptions.test +++ b/mypyc/test-data/run-exceptions.test @@ -80,6 +80,43 @@ Traceback (most recent call last): File "native.py", line 23, in __init__ raise Exception Exception +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 4, in + f([]) + ~^^^^ + File "native.py", line 3, in f + g(x) + File "native.py", line 6, in g + x[5] = 2 +IndexError: list assignment index out of range +Traceback (most recent call last): + File "driver.py", line 8, in + r1() + ~~^^ + File "native.py", line 10, in r1 + q1() + File "native.py", line 13, in q1 + raise Exception("test") +Exception: test +Traceback (most recent call last): + File "driver.py", line 12, in + r2() + ~~^^ + File "native.py", line 16, in r2 + q2() + File "native.py", line 19, in q2 + raise Exception +Exception +Traceback (most recent call last): + File "driver.py", line 16, in + hey() + ~~~^^ + File "native.py", line 26, in hey + A() + File "native.py", line 23, in __init__ + raise Exception +Exception [case testTryExcept] from typing import Any, Iterator @@ -264,6 +301,55 @@ attr! -- 'object' object has no attribute 'lol' out! == l == key! -- 0 +[out version>=3.13] +== i == + +Traceback (most recent call last): + File "driver.py", line 6, in + i() + ~^^ + File "native.py", line 44, in i + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== k == +Traceback (most recent call last): + File "native.py", line 59, in k + r(1) + File "native.py", line 17, in r + raise Exception('hi') +Exception: hi + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "driver.py", line 12, in + k() + ~^^ + File "native.py", line 61, in k + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== g == +caught! +caught! +== f == +hi +None +list index out of range +None +== h == +gonna break +None +== j == +lookup! +lookup! +attr! -- 'object' object has no attribute 'lol' +out! +== l == +key! -- 0 [case testTryFinally] from typing import Any diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 6f7d79059a6d..95b79af1a411 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -381,6 +381,41 @@ RuntimeError: dictionary changed size during iteration 1 2 3 +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 16, in + iterate_over_any(5) + ~~~~~~~~~~~~~~~~^^^ + File "native.py", line 6, in iterate_over_any + for element in a: +TypeError: 'int' object is not iterable +Traceback (most recent call last): + File "driver.py", line 20, in + iterate_over_iterable(broken_generator(5)) + ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^ + File "native.py", line 10, in iterate_over_iterable + for element in iterable: + File "driver.py", line 8, in broken_generator + raise Exception('Exception Manually Raised') +Exception: Exception Manually Raised +Traceback (most recent call last): + File "driver.py", line 24, in + iterate_and_delete(d) + ~~~~~~~~~~~~~~~~~~^^^ + File "native.py", line 14, in iterate_and_delete + for key in d: +RuntimeError: dictionary changed size during iteration +15 +6 +3 +0 +1 +2 +3 +4 +1 +2 +3 [case testContinueFor] def f() -> None: diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 14bb5be979ae..f07ac51dae6c 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -968,7 +968,10 @@ print(z) [case testCheckVersion] import sys -if sys.version_info[:2] == (3, 12): +if sys.version_info[:2] == (3, 13): + def version() -> int: + return 13 +elif sys.version_info[:2] == (3, 12): def version() -> int: return 12 elif sys.version_info[:2] == (3, 11): diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 70c73dc2088b..5edd5688140e 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -291,6 +291,23 @@ Traceback (most recent call last): File "other.py", line 3, in fail2 x[2] = 2 IndexError: list assignment index out of range +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 6, in + other.fail2() + ~~~~~~~~~~~^^ + File "other.py", line 3, in fail2 + x[2] = 2 +IndexError: list assignment index out of range +Traceback (most recent call last): + File "driver.py", line 12, in + native.fail() + ~~~~~~~~~~~^^ + File "native.py", line 4, in fail + fail2() + File "other.py", line 3, in fail2 + x[2] = 2 +IndexError: list assignment index out of range [case testMultiModuleCycle] if False: From 78d1dfe9db3e2385c66b80c5e88dfca1655ae0e3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:08:00 +0200 Subject: [PATCH 0697/1617] Fix ManagedDict functions for Python 3.13 (#17507) `PyObject_VisitManagedDict` and `PyObject_ClearManagedDict` were made public in https://github.com/python/cpython/pull/108763. Both are available from `pythoncapi_compat.h`. --- mypyc/codegen/emitclass.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 8dcf7212b694..ad95a1b0f323 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -734,7 +734,7 @@ def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) - for attr, rtype in base.attributes.items(): emitter.emit_gc_visit(f"self->{emitter.attr(attr)}", rtype) if has_managed_dict(cl, emitter): - emitter.emit_line("_PyObject_VisitManagedDict((PyObject *)self, visit, arg);") + emitter.emit_line("PyObject_VisitManagedDict((PyObject *)self, visit, arg);") elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that @@ -757,7 +757,7 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N for attr, rtype in base.attributes.items(): emitter.emit_gc_clear(f"self->{emitter.attr(attr)}", rtype) if has_managed_dict(cl, emitter): - emitter.emit_line("_PyObject_ClearManagedDict((PyObject *)self);") + emitter.emit_line("PyObject_ClearManagedDict((PyObject *)self);") elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that From 3c9f69487d3f01b7c24cf4dbda2460c99094d1f3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:08:22 +0200 Subject: [PATCH 0698/1617] Add another include for Python 3.13 (#17509) Include `internal/pycore_setobject.h` necessary for `_PySet_Update`. --- mypyc/lib-rt/pythonsupport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 69ff120dd40d..8edc9abcf9f8 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -22,6 +22,7 @@ #include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue #include "internal/pycore_object.h" // _PyType_CalculateMetaclass #include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError +#include "internal/pycore_setobject.h" // _PySet_Update #include "internal/pycore_unicodeobject.h" // _PyUnicode_EQ, _PyUnicode_FastCopyCharacters #endif From 45bb91ed37f94140132742ab63a9a0d01f4f1b07 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 14 Jul 2024 13:02:31 +0100 Subject: [PATCH 0699/1617] [mypyc] Don't use _PyUnicode_EQ on 3.13, as it's no longer exported (#17523) Work on mypyc/mypyc#1056. --- mypyc/lib-rt/getargsfast.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypyc/lib-rt/getargsfast.c b/mypyc/lib-rt/getargsfast.c index 387deed4399b..62d0dfed0a6d 100644 --- a/mypyc/lib-rt/getargsfast.c +++ b/mypyc/lib-rt/getargsfast.c @@ -271,9 +271,16 @@ find_keyword(PyObject *kwnames, PyObject *const *kwstack, PyObject *key) for (i = 0; i < nkwargs; i++) { PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); assert(PyUnicode_Check(kwname)); +#if CPY_3_13_FEATURES + if (_PyUnicode_Equal(kwname, key)) { + return kwstack[i]; + } +#else if (_PyUnicode_EQ(kwname, key)) { return kwstack[i]; } +#endif + } return NULL; } From 42337a0ca0923ce9cf7492c6d847a945a69ad7a6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 14 Jul 2024 13:02:43 +0100 Subject: [PATCH 0700/1617] [mypyc] Don't use _PyUnicode_FastCopyCharacters on 3.13 (#17524) Work on mypyc/mypyc#1056. --- mypyc/lib-rt/str_ops.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 90b19001f8f0..4ba181bcce85 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -117,7 +117,11 @@ PyObject *CPyStr_Build(Py_ssize_t len, ...) { PyObject *item = va_arg(args, PyObject *); Py_ssize_t itemlen = PyUnicode_GET_LENGTH(item); if (itemlen != 0) { +#if CPY_3_13_FEATURES + PyUnicode_CopyCharacters(res, res_offset, item, 0, itemlen); +#else _PyUnicode_FastCopyCharacters(res, res_offset, item, 0, itemlen); +#endif res_offset += itemlen; } } From 6a0657e5959ba1777c4d427f8f355d499035d145 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 14 Jul 2024 13:02:51 +0100 Subject: [PATCH 0701/1617] [mypyc] Don't rely on _PyType_CalculateMetaclass on 3.13 (#17525) Copy the implementation from CPython (with minor changes), as it's no longer exported. Work on mypyc/mypyc#1056. --- mypyc/lib-rt/CPy.h | 5 +---- mypyc/lib-rt/misc_ops.c | 48 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 2ec04e4c5b5c..833b1bd2e76a 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -846,10 +846,7 @@ static inline bool CPy_TypeCheck(PyObject *o, PyObject *type) { return PyObject_TypeCheck(o, (PyTypeObject *)type); } -static inline PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o) { - return (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)type, o); -} - +PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o); PyObject *CPy_GetCoro(PyObject *obj); PyObject *CPyIter_Send(PyObject *iter, PyObject *val); int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp); diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index 803123d436a2..1572c4496e30 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -131,6 +131,52 @@ static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) { return matches; } +#if CPY_3_13_FEATURES + +// Adapted from CPython 3.13.0b3 +/* Determine the most derived metatype. */ +PyObject *CPy_CalculateMetaclass(PyObject *metatype, PyObject *bases) +{ + Py_ssize_t i, nbases; + PyTypeObject *winner; + PyObject *tmp; + PyTypeObject *tmptype; + + /* Determine the proper metatype to deal with this, + and check for metatype conflicts while we're at it. + Note that if some other metatype wins to contract, + it's possible that its instances are not types. */ + + nbases = PyTuple_GET_SIZE(bases); + winner = (PyTypeObject *)metatype; + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + tmptype = Py_TYPE(tmp); + if (PyType_IsSubtype(winner, tmptype)) + continue; + if (PyType_IsSubtype(tmptype, winner)) { + winner = tmptype; + continue; + } + /* else: */ + PyErr_SetString(PyExc_TypeError, + "metaclass conflict: " + "the metaclass of a derived class " + "must be a (non-strict) subclass " + "of the metaclasses of all its bases"); + return NULL; + } + return (PyObject *)winner; +} + +#else + +PyObject *CPy_CalculateMetaclass(PyObject *metatype, PyObject *bases) { + return (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)metatype, bases); +} + +#endif + // Create a heap type based on a template non-heap type. // This is super hacky and maybe we should suck it up and use PyType_FromSpec instead. // We allow bases to be NULL to represent just inheriting from object. @@ -163,7 +209,7 @@ PyObject *CPyType_FromTemplate(PyObject *template, // Find the appropriate metaclass from our base classes. We // care about this because Generic uses a metaclass prior to // Python 3.7. - metaclass = _PyType_CalculateMetaclass(metaclass, bases); + metaclass = (PyTypeObject *)CPy_CalculateMetaclass((PyObject *)metaclass, bases); if (!metaclass) goto error; From e062793d17a67766e6a44fc4095eb72e55e50d7c Mon Sep 17 00:00:00 2001 From: Max Murin Date: Fri, 19 Jul 2024 05:07:41 -0700 Subject: [PATCH 0702/1617] CHANGELOG.md update for 1.11 (#17539) Add a changelog for the 1.11 release. --- CHANGELOG.md | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d5919cafe33..196a75992c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,174 @@ ## Next release +## Mypy 1.11 + +We’ve just uploaded mypy 1.11 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +#### Additional support for PEP 695 + +Mypy now has experimental support for the new type parameter syntax introduced in Python 3.12 ([PEP 695](https://peps.python.org/pep-0695/)). +This feature is still experimental and must be enabled with the `--enable-incomplete-feature=NewGenericSyntax` flag. + +This example demonstrates the new syntax: +```python + +def f[T](x: T) -> T: ... + +reveal_type(f(1)) # Revealed type is 'int' +``` + +This feature was contributed by Jukka Lehtosalo (PR [17233](https://github.com/python/mypy/pull/17233)). + + +#### Support for `functools.partial` + +Mypy now typechecks uses of `functools.partial`, which previous mypy would always accept. +This example would previously pass: + +```python +from functools import partial + +def f(a: int, b: str) -> None: ... + +g = partial(f, 1) +g(1) # error: Argument 1 to "f" has incompatible type "int"; expected "str" [arg-type] +``` + +This feature was contributed by Shantanu (PR [16939](https://github.com/python/mypy/pull/16939)). + + +#### Changes to stubtest + * Stubtest: ignore `_ios_support` (Alex Waygood, PR [17270](https://github.com/python/mypy/pull/17270)) + * stubtest: changes for py313 (Shantanu, PR [17261](https://github.com/python/mypy/pull/17261)) + + +#### Changes to stubgen + * stubgen: Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (Ali Hamdan, PR [17386](https://github.com/python/mypy/pull/17386)) + * Fix stubgen for Python 3.13 (Jelle Zijlstra, PR [17290](https://github.com/python/mypy/pull/17290)) + * stubgen: preserve enum value initialisers (Shantanu, PR [17125](https://github.com/python/mypy/pull/17125)) + + +#### Changes to mypyc + * [mypyc] Sync pythoncapi_compat.h (Jukka Lehtosalo, PR [17390](https://github.com/python/mypy/pull/17390)) + * [mypyc] Support Python 3.12 type alias syntax (PEP 695) (Jukka Lehtosalo, PR [17384](https://github.com/python/mypy/pull/17384)) + * [mypyc] Support new syntax for generic functions and classes (PEP 695) (Jukka Lehtosalo, PR [17357](https://github.com/python/mypy/pull/17357)) + * [mypyc] Fix ParamSpec (Shantanu, PR [17309](https://github.com/python/mypy/pull/17309)) + * [mypyc] Inline fast paths of integer unboxing operations (Jukka Lehtosalo, PR [17266](https://github.com/python/mypy/pull/17266)) + * [mypyc] Inline tagged integer arithmetic and bitwise operations (Jukka Lehtosalo, PR [17265](https://github.com/python/mypy/pull/17265)) + * [mypyc] Allow specifying primitives as pure (Jukka Lehtosalo, PR [17263](https://github.com/python/mypy/pull/17263)) + + +#### Changes to error reporting + * Do not report plugin-generated methods with `explicit-override` (sobolevn, PR [17433](https://github.com/python/mypy/pull/17433)) + * Fix explicit type for partial (Ivan Levkivskyi, PR [17424](https://github.com/python/mypy/pull/17424)) + * Re-work overload overlap logic (Ivan Levkivskyi, PR [17392](https://github.com/python/mypy/pull/17392)) + * Use namespaces for function type variables (Ivan Levkivskyi, PR [17311](https://github.com/python/mypy/pull/17311)) + * Fix false positive for Final local scope variable in Protocol (GiorgosPapoutsakis, PR [17308](https://github.com/python/mypy/pull/17308)) + * Use Never in more messages, use ambiguous in join (Shantanu, PR [17304](https://github.com/python/mypy/pull/17304)) + * Log full path to config file in verbose output (dexterkennedy, PR [17180](https://github.com/python/mypy/pull/17180)) + * Added [prop-decorator] code for unsupported property decorators (#14461) (Christopher Barber, PR [16571](https://github.com/python/mypy/pull/16571)) + * Suppress second error message with `:=` and `[truthy-bool]` (Nikita Sobolev, PR [15941](https://github.com/python/mypy/pull/15941)) + * Error for assignment of functional Enum to variable of different name (Shantanu, PR [16805](https://github.com/python/mypy/pull/16805)) + * Add Error format support, and JSON output option (Tushar Sadhwani, PR [11396](https://github.com/python/mypy/pull/11396)) + + +#### Fixes for crashes + * Fix daemon crash on invalid type in TypedDict (Ivan Levkivskyi, PR [17495](https://github.com/python/mypy/pull/17495)) + * Some cleanup in partial plugin (Ivan Levkivskyi, PR [17423](https://github.com/python/mypy/pull/17423)) + * Fix crash when overriding with unpacked TypedDict (Ivan Levkivskyi, PR [17359](https://github.com/python/mypy/pull/17359)) + * Fix crash on TypedDict unpacking for ParamSpec (Ivan Levkivskyi, PR [17358](https://github.com/python/mypy/pull/17358)) + * Fix crash involving recursive union of tuples (Ivan Levkivskyi, PR [17353](https://github.com/python/mypy/pull/17353)) + * Fix crash on invalid callable property override (Ivan Levkivskyi, PR [17352](https://github.com/python/mypy/pull/17352)) + * Fix crash on unpacking self in NamedTuple (Ivan Levkivskyi, PR [17351](https://github.com/python/mypy/pull/17351)) + * Fix crash on recursive alias with an optional type (Ivan Levkivskyi, PR [17350](https://github.com/python/mypy/pull/17350)) + * Fix type comments crash inside generic definitions (Bénédikt Tran, PR [16849](https://github.com/python/mypy/pull/16849)) + + +#### Changes to documentation + * Mention --enable-incomplete-feature=NewGenericSyntax (Shantanu, PR [17462](https://github.com/python/mypy/pull/17462)) + * Use inline config in the optional error codes docs (Shantanu, PR [17374](https://github.com/python/mypy/pull/17374)) + * docs: Use lower-case generics (Seo Sanghyeon, PR [17176](https://github.com/python/mypy/pull/17176)) + * Add documentation for show-error-code-links (GiorgosPapoutsakis, PR [17144](https://github.com/python/mypy/pull/17144)) + * Update CONTRIBUTING.md to include commands for Windows (GiorgosPapoutsakis, PR [17142](https://github.com/python/mypy/pull/17142)) + + +#### Other notable contributions + * Fix ParamSpec inference against TypeVarTuple (Ivan Levkivskyi, PR [17431](https://github.com/python/mypy/pull/17431)) + * Always allow lambda calls (Ivan Levkivskyi, PR [17430](https://github.com/python/mypy/pull/17430)) + * Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) + * Fix isinstance checks with PEP 604 unions containing None (Shantanu, PR [17415](https://github.com/python/mypy/pull/17415)) + * Use (simplified) unions instead of joins for tuple fallbacks (Ivan Levkivskyi, PR [17408](https://github.com/python/mypy/pull/17408)) + * Fix self-referential upper bound in new-style type variables (Ivan Levkivskyi, PR [17407](https://github.com/python/mypy/pull/17407)) + * Consider overlap between instances and callables (Ivan Levkivskyi, PR [17389](https://github.com/python/mypy/pull/17389)) + * Support `enum.member` for python3.11+ (Nikita Sobolev, PR [17382](https://github.com/python/mypy/pull/17382)) + * Allow new-style self-types in classmethods (Ivan Levkivskyi, PR [17381](https://github.com/python/mypy/pull/17381)) + * Support `enum.nonmember` for python3.11+ (Nikita Sobolev, PR [17376](https://github.com/python/mypy/pull/17376)) + * Fix isinstance with type aliases to PEP 604 unions (Shantanu, PR [17371](https://github.com/python/mypy/pull/17371)) + * Properly handle unpacks in overlap checks (Ivan Levkivskyi, PR [17356](https://github.com/python/mypy/pull/17356)) + * Fix type application for classes with generic constructors (Ivan Levkivskyi, PR [17354](https://github.com/python/mypy/pull/17354)) + * Use polymorphic inference in unification (Ivan Levkivskyi, PR [17348](https://github.com/python/mypy/pull/17348)) + * Update 'typing_extensions' to >=4.6.0 to fix python 3.12 error (Ben Brown, PR [17312](https://github.com/python/mypy/pull/17312)) + * Avoid does not return error in lambda (Shantanu, PR [17294](https://github.com/python/mypy/pull/17294)) + * Fix for bug with descriptors in non-strict-optional (Max Murin, PR [17293](https://github.com/python/mypy/pull/17293)) + * Don’t leak unreachability from lambda body to surrounding scope (Anders Kaseorg, PR [17287](https://github.com/python/mypy/pull/17287)) + * Validate more about overrides on untyped methods (Steven Troxler, PR [17276](https://github.com/python/mypy/pull/17276)) + * Fix case involving non-ASCII chars on Windows (Alexander Leopold Shon, PR [17275](https://github.com/python/mypy/pull/17275)) + * Support namedtuple.__replace__ in Python 3.13 (Shantanu, PR [17259](https://github.com/python/mypy/pull/17259)) + * Fix for type narrowing of negative integer literals (gilesgc, PR [17256](https://github.com/python/mypy/pull/17256)) + * Support rename=True in collections.namedtuple (Jelle Zijlstra, PR [17247](https://github.com/python/mypy/pull/17247)) + * [dmypy] sort list of files for update by extension (Valentin Stanciu, PR [17245](https://github.com/python/mypy/pull/17245)) + * fix #16935 fix type of tuple[X,Y] expression (urnest, PR [17235](https://github.com/python/mypy/pull/17235)) + * Do not forget that a `TypedDict` was wrapped in `Unpack` after a `name-defined` error occurred. (Christoph Tyralla, PR [17226](https://github.com/python/mypy/pull/17226)) + * fix: annotated argument's `var` node type is explicit, not inferred (bzoracler, PR [17217](https://github.com/python/mypy/pull/17217)) + * Enum private attributes are not enum members (Ali Hamdan, PR [17182](https://github.com/python/mypy/pull/17182)) + * Fix Literal strings containing pipe characters (Jelle Zijlstra, PR [17148](https://github.com/python/mypy/pull/17148)) + * Add support for __spec__ (Shantanu, PR [14739](https://github.com/python/mypy/pull/14739)) + + +#### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + + +#### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Alex Waygood +- Alexander Leopold Shon +- Ali Hamdan +- Anders Kaseorg +- Ben Brown +- Bénédikt Tran +- bzoracler +- Christoph Tyralla +- Christopher Barber +- dexterkennedy +- gilesgc +- GiorgosPapoutsakis +- Ivan Levkivskyi +- Jelle Zijlstra +- Jukka Lehtosalo +- Marc Mueller +- Matthieu Devlin +- Michael R. Crusoe +- Nikita Sobolev +- Seo Sanghyeon +- Shantanu +- sobolevn +- Steven Troxler +- Tadeu Manoel +- Tamir Duberstein +- Tushar Sadhwani +- urnest +- Valentin Stanciu + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + ## Mypy 1.10 From 0753e2a82dad35034e000609b6e8daa37238bfaa Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 19 Jul 2024 16:49:09 +0100 Subject: [PATCH 0703/1617] Update CHANGELOG for mypy 1.11 (#17540) Added additional sections for major features and did various other updates. --- CHANGELOG.md | 188 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196a75992c24..b544e05ee573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,26 +11,40 @@ We’ve just uploaded mypy 1.11 to the Python Package Index ([PyPI](https://pypi You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Additional support for PEP 695 +#### Support Python 3.12 Syntax for Generics (PEP 695) -Mypy now has experimental support for the new type parameter syntax introduced in Python 3.12 ([PEP 695](https://peps.python.org/pep-0695/)). -This feature is still experimental and must be enabled with the `--enable-incomplete-feature=NewGenericSyntax` flag. +Mypy now supports the new type parameter syntax introduced in Python 3.12 ([PEP 695](https://peps.python.org/pep-0695/)). +This feature is still experimental and must be enabled with the `--enable-incomplete-feature=NewGenericSyntax` flag, or with `enable_incomplete_feature = NewGenericSyntax` in the mypy configuration file. +We plan to enable this by default in the next mypy feature release. This example demonstrates the new syntax: -```python +```python +# Generic function def f[T](x: T) -> T: ... reveal_type(f(1)) # Revealed type is 'int' + +# Generic class +class C[T]: + def __init__(self, x: T) -> None: + self.x = x + +c = C('a') +reveal_type(c.x) # Revealed type is 'str' + +# Type alias +type A[T] = C[list[T]] ``` -This feature was contributed by Jukka Lehtosalo (PR [17233](https://github.com/python/mypy/pull/17233)). +This feature was contributed by Jukka Lehtosalo. #### Support for `functools.partial` -Mypy now typechecks uses of `functools.partial`, which previous mypy would always accept. -This example would previously pass: +Mypy now type checks uses of `functools.partial`. Previously mypy would accept arbitrary arguments. + +This example will now produce an error: ```python from functools import partial @@ -38,98 +52,158 @@ from functools import partial def f(a: int, b: str) -> None: ... g = partial(f, 1) -g(1) # error: Argument 1 to "f" has incompatible type "int"; expected "str" [arg-type] + +# Argument has incompatible type "int"; expected "str" +g(11) ``` This feature was contributed by Shantanu (PR [16939](https://github.com/python/mypy/pull/16939)). -#### Changes to stubtest - * Stubtest: ignore `_ios_support` (Alex Waygood, PR [17270](https://github.com/python/mypy/pull/17270)) - * stubtest: changes for py313 (Shantanu, PR [17261](https://github.com/python/mypy/pull/17261)) +#### Stricter Checks for Untyped Overrides + +Past mypy versions didn't check if untyped methods were compatible with overridden methods. This would result in false negatives. Now mypy performs these checks when using `--check-untyped-defs`. + +For example, this now generates an error if using `--check-untyped-defs`: + +```python +class Base: + def f(self, x: int = 0) -> None: ... + +class Derived(Base): + # Signature incompatible with "Base" + def f(self): ... +``` + +This feature was contributed by Steven Troxler (PR [17276](https://github.com/python/mypy/pull/17276)). + + +#### Type Inference Improvements + +The new polymorphic inference algorithm introduced in mypy 1.5 is now used in more situations. This improves type inference involving generic higher-order functions, in particular. + +This feature was contributed by Ivan Levkivskyi (PR [17348](https://github.com/python/mypy/pull/17348)). + +Mypy now uses unions of tuple item types in certain contexts to enable more precise inferred types. Example: + +```python +for x in (1, 'x'): + # Previously inferred as 'object' + reveal_type(x) # Revealed type is 'int | str' +``` + +This was also contributed by Ivan Levkivskyi (PR [17408](https://github.com/python/mypy/pull/17408)). + +#### Improvements to Detection of Overlapping Overloads -#### Changes to stubgen - * stubgen: Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (Ali Hamdan, PR [17386](https://github.com/python/mypy/pull/17386)) - * Fix stubgen for Python 3.13 (Jelle Zijlstra, PR [17290](https://github.com/python/mypy/pull/17290)) - * stubgen: preserve enum value initialisers (Shantanu, PR [17125](https://github.com/python/mypy/pull/17125)) +The details of how mypy checks if two `@overload` signatures are unsafely overlapping were overhauled. This both fixes some false positives, and allows mypy to detect additional unsafe signatures. +This feature was contributed by Ivan Levkivskyi (PR [17392](https://github.com/python/mypy/pull/17392)). -#### Changes to mypyc - * [mypyc] Sync pythoncapi_compat.h (Jukka Lehtosalo, PR [17390](https://github.com/python/mypy/pull/17390)) - * [mypyc] Support Python 3.12 type alias syntax (PEP 695) (Jukka Lehtosalo, PR [17384](https://github.com/python/mypy/pull/17384)) - * [mypyc] Support new syntax for generic functions and classes (PEP 695) (Jukka Lehtosalo, PR [17357](https://github.com/python/mypy/pull/17357)) - * [mypyc] Fix ParamSpec (Shantanu, PR [17309](https://github.com/python/mypy/pull/17309)) - * [mypyc] Inline fast paths of integer unboxing operations (Jukka Lehtosalo, PR [17266](https://github.com/python/mypy/pull/17266)) - * [mypyc] Inline tagged integer arithmetic and bitwise operations (Jukka Lehtosalo, PR [17265](https://github.com/python/mypy/pull/17265)) - * [mypyc] Allow specifying primitives as pure (Jukka Lehtosalo, PR [17263](https://github.com/python/mypy/pull/17263)) +#### Better Support for Type Hints in Expressions -#### Changes to error reporting +Mypy now allows more expressions that evaluate to valid type annotations in all expression contexts. The inferred types of these expressions are also sometimes more precise. Previously they were often `object`. + +This example uses a union type that includes a callable type as an expression, and it no longer generates an error: + +```python +from typing import Callable + +print(Callable[[], int] | None) # No error +``` + +This feature was contributed by Jukka Lehtosalo (PR [17404](https://github.com/python/mypy/pull/17404)). + + +#### Mypyc Improvements + +Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is signficantly faster basic operations on `int` values. + + * Support Python 3.12 syntax for generic functions and classes (Jukka Lehtosalo, PR [17357](https://github.com/python/mypy/pull/17357)) + * Support Python 3.12 type alias syntax (Jukka Lehtosalo, PR [17384](https://github.com/python/mypy/pull/17384)) + * Fix ParamSpec (Shantanu, PR [17309](https://github.com/python/mypy/pull/17309)) + * Inline fast paths of integer unboxing operations (Jukka Lehtosalo, PR [17266](https://github.com/python/mypy/pull/17266)) + * Inline tagged integer arithmetic and bitwise operations (Jukka Lehtosalo, PR [17265](https://github.com/python/mypy/pull/17265)) + * Allow specifying primitives as pure (Jukka Lehtosalo, PR [17263](https://github.com/python/mypy/pull/17263)) + + +#### Changes to Stubtest + * Ignore `_ios_support` (Alex Waygood, PR [17270](https://github.com/python/mypy/pull/17270)) + * Improve support for Python 3.13 (Shantanu, PR [17261](https://github.com/python/mypy/pull/17261)) + + +#### Changes to Stubgen + * Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (Ali Hamdan, PR [17386](https://github.com/python/mypy/pull/17386)) + * Fix for Python 3.13 (Jelle Zijlstra, PR [17290](https://github.com/python/mypy/pull/17290)) + * Preserve enum value initialisers (Shantanu, PR [17125](https://github.com/python/mypy/pull/17125)) + + +#### Miscellaneous New Features + * Add error format support and JSON output option via `--output json` (Tushar Sadhwani, PR [11396](https://github.com/python/mypy/pull/11396)) + * Support `enum.member` in Python 3.11+ (Nikita Sobolev, PR [17382](https://github.com/python/mypy/pull/17382)) + * Support `enum.nonmember` in Python 3.11+ (Nikita Sobolev, PR [17376](https://github.com/python/mypy/pull/17376)) + * Support `namedtuple.__replace__` in Python 3.13 (Shantanu, PR [17259](https://github.com/python/mypy/pull/17259)) + * Support `rename=True` in collections.namedtuple (Jelle Zijlstra, PR [17247](https://github.com/python/mypy/pull/17247)) + * Add support for `__spec__` (Shantanu, PR [14739](https://github.com/python/mypy/pull/14739)) + + +#### Changes to Error Reporting + * Mention `--enable-incomplete-feature=NewGenericSyntax` in messages (Shantanu, PR [17462](https://github.com/python/mypy/pull/17462)) * Do not report plugin-generated methods with `explicit-override` (sobolevn, PR [17433](https://github.com/python/mypy/pull/17433)) - * Fix explicit type for partial (Ivan Levkivskyi, PR [17424](https://github.com/python/mypy/pull/17424)) - * Re-work overload overlap logic (Ivan Levkivskyi, PR [17392](https://github.com/python/mypy/pull/17392)) - * Use namespaces for function type variables (Ivan Levkivskyi, PR [17311](https://github.com/python/mypy/pull/17311)) + * Use and display namespaces for function type variables (Ivan Levkivskyi, PR [17311](https://github.com/python/mypy/pull/17311)) * Fix false positive for Final local scope variable in Protocol (GiorgosPapoutsakis, PR [17308](https://github.com/python/mypy/pull/17308)) * Use Never in more messages, use ambiguous in join (Shantanu, PR [17304](https://github.com/python/mypy/pull/17304)) * Log full path to config file in verbose output (dexterkennedy, PR [17180](https://github.com/python/mypy/pull/17180)) - * Added [prop-decorator] code for unsupported property decorators (#14461) (Christopher Barber, PR [16571](https://github.com/python/mypy/pull/16571)) + * Added `[prop-decorator]` code for unsupported property decorators (#14461) (Christopher Barber, PR [16571](https://github.com/python/mypy/pull/16571)) * Suppress second error message with `:=` and `[truthy-bool]` (Nikita Sobolev, PR [15941](https://github.com/python/mypy/pull/15941)) - * Error for assignment of functional Enum to variable of different name (Shantanu, PR [16805](https://github.com/python/mypy/pull/16805)) - * Add Error format support, and JSON output option (Tushar Sadhwani, PR [11396](https://github.com/python/mypy/pull/11396)) + * Generate error for assignment of functional Enum to variable of different name (Shantanu, PR [16805](https://github.com/python/mypy/pull/16805)) + * Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) -#### Fixes for crashes +#### Fixes for Crashes * Fix daemon crash on invalid type in TypedDict (Ivan Levkivskyi, PR [17495](https://github.com/python/mypy/pull/17495)) - * Some cleanup in partial plugin (Ivan Levkivskyi, PR [17423](https://github.com/python/mypy/pull/17423)) + * Fix crash and bugs related to `partial()` (Ivan Levkivskyi, PR [17423](https://github.com/python/mypy/pull/17423)) * Fix crash when overriding with unpacked TypedDict (Ivan Levkivskyi, PR [17359](https://github.com/python/mypy/pull/17359)) * Fix crash on TypedDict unpacking for ParamSpec (Ivan Levkivskyi, PR [17358](https://github.com/python/mypy/pull/17358)) * Fix crash involving recursive union of tuples (Ivan Levkivskyi, PR [17353](https://github.com/python/mypy/pull/17353)) * Fix crash on invalid callable property override (Ivan Levkivskyi, PR [17352](https://github.com/python/mypy/pull/17352)) * Fix crash on unpacking self in NamedTuple (Ivan Levkivskyi, PR [17351](https://github.com/python/mypy/pull/17351)) * Fix crash on recursive alias with an optional type (Ivan Levkivskyi, PR [17350](https://github.com/python/mypy/pull/17350)) - * Fix type comments crash inside generic definitions (Bénédikt Tran, PR [16849](https://github.com/python/mypy/pull/16849)) + * Fix crash on type comment inside generic definitions (Bénédikt Tran, PR [16849](https://github.com/python/mypy/pull/16849)) -#### Changes to documentation - * Mention --enable-incomplete-feature=NewGenericSyntax (Shantanu, PR [17462](https://github.com/python/mypy/pull/17462)) - * Use inline config in the optional error codes docs (Shantanu, PR [17374](https://github.com/python/mypy/pull/17374)) - * docs: Use lower-case generics (Seo Sanghyeon, PR [17176](https://github.com/python/mypy/pull/17176)) +#### Changes to Documentation + * Use inline config in documentation for optional error codes (Shantanu, PR [17374](https://github.com/python/mypy/pull/17374)) + * Use lower-case generics in documentation (Seo Sanghyeon, PR [17176](https://github.com/python/mypy/pull/17176)) * Add documentation for show-error-code-links (GiorgosPapoutsakis, PR [17144](https://github.com/python/mypy/pull/17144)) * Update CONTRIBUTING.md to include commands for Windows (GiorgosPapoutsakis, PR [17142](https://github.com/python/mypy/pull/17142)) -#### Other notable contributions +#### Other Notable Improvements and Fixes * Fix ParamSpec inference against TypeVarTuple (Ivan Levkivskyi, PR [17431](https://github.com/python/mypy/pull/17431)) + * Fix explicit type for `partial` (Ivan Levkivskyi, PR [17424](https://github.com/python/mypy/pull/17424)) * Always allow lambda calls (Ivan Levkivskyi, PR [17430](https://github.com/python/mypy/pull/17430)) - * Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) * Fix isinstance checks with PEP 604 unions containing None (Shantanu, PR [17415](https://github.com/python/mypy/pull/17415)) - * Use (simplified) unions instead of joins for tuple fallbacks (Ivan Levkivskyi, PR [17408](https://github.com/python/mypy/pull/17408)) * Fix self-referential upper bound in new-style type variables (Ivan Levkivskyi, PR [17407](https://github.com/python/mypy/pull/17407)) * Consider overlap between instances and callables (Ivan Levkivskyi, PR [17389](https://github.com/python/mypy/pull/17389)) - * Support `enum.member` for python3.11+ (Nikita Sobolev, PR [17382](https://github.com/python/mypy/pull/17382)) * Allow new-style self-types in classmethods (Ivan Levkivskyi, PR [17381](https://github.com/python/mypy/pull/17381)) - * Support `enum.nonmember` for python3.11+ (Nikita Sobolev, PR [17376](https://github.com/python/mypy/pull/17376)) * Fix isinstance with type aliases to PEP 604 unions (Shantanu, PR [17371](https://github.com/python/mypy/pull/17371)) * Properly handle unpacks in overlap checks (Ivan Levkivskyi, PR [17356](https://github.com/python/mypy/pull/17356)) * Fix type application for classes with generic constructors (Ivan Levkivskyi, PR [17354](https://github.com/python/mypy/pull/17354)) - * Use polymorphic inference in unification (Ivan Levkivskyi, PR [17348](https://github.com/python/mypy/pull/17348)) - * Update 'typing_extensions' to >=4.6.0 to fix python 3.12 error (Ben Brown, PR [17312](https://github.com/python/mypy/pull/17312)) - * Avoid does not return error in lambda (Shantanu, PR [17294](https://github.com/python/mypy/pull/17294)) - * Fix for bug with descriptors in non-strict-optional (Max Murin, PR [17293](https://github.com/python/mypy/pull/17293)) + * Update `typing_extensions` to >=4.6.0 to fix Python 3.12 error (Ben Brown, PR [17312](https://github.com/python/mypy/pull/17312)) + * Avoid "does not return" error in lambda (Shantanu, PR [17294](https://github.com/python/mypy/pull/17294)) + * Fix bug with descriptors in non-strict-optional mode (Max Murin, PR [17293](https://github.com/python/mypy/pull/17293)) * Don’t leak unreachability from lambda body to surrounding scope (Anders Kaseorg, PR [17287](https://github.com/python/mypy/pull/17287)) - * Validate more about overrides on untyped methods (Steven Troxler, PR [17276](https://github.com/python/mypy/pull/17276)) - * Fix case involving non-ASCII chars on Windows (Alexander Leopold Shon, PR [17275](https://github.com/python/mypy/pull/17275)) - * Support namedtuple.__replace__ in Python 3.13 (Shantanu, PR [17259](https://github.com/python/mypy/pull/17259)) + * Fix issues with non-ASCII characters on Windows (Alexander Leopold Shon, PR [17275](https://github.com/python/mypy/pull/17275)) * Fix for type narrowing of negative integer literals (gilesgc, PR [17256](https://github.com/python/mypy/pull/17256)) - * Support rename=True in collections.namedtuple (Jelle Zijlstra, PR [17247](https://github.com/python/mypy/pull/17247)) - * [dmypy] sort list of files for update by extension (Valentin Stanciu, PR [17245](https://github.com/python/mypy/pull/17245)) - * fix #16935 fix type of tuple[X,Y] expression (urnest, PR [17235](https://github.com/python/mypy/pull/17235)) - * Do not forget that a `TypedDict` was wrapped in `Unpack` after a `name-defined` error occurred. (Christoph Tyralla, PR [17226](https://github.com/python/mypy/pull/17226)) - * fix: annotated argument's `var` node type is explicit, not inferred (bzoracler, PR [17217](https://github.com/python/mypy/pull/17217)) - * Enum private attributes are not enum members (Ali Hamdan, PR [17182](https://github.com/python/mypy/pull/17182)) + * Fix confusion between .py and .pyi files in mypy daemon (Valentin Stanciu, PR [17245](https://github.com/python/mypy/pull/17245)) + * Fix type of `tuple[X, Y]` expression (urnest, PR [17235](https://github.com/python/mypy/pull/17235)) + * Don't forget that a `TypedDict` was wrapped in `Unpack` after a `name-defined` error occurred (Christoph Tyralla, PR [17226](https://github.com/python/mypy/pull/17226)) + * Mark annotated argument as having an explicit, not inferred type (bzoracler, PR [17217](https://github.com/python/mypy/pull/17217)) + * Don't consider Enum private attributes as enum members (Ali Hamdan, PR [17182](https://github.com/python/mypy/pull/17182)) * Fix Literal strings containing pipe characters (Jelle Zijlstra, PR [17148](https://github.com/python/mypy/pull/17148)) - * Add support for __spec__ (Shantanu, PR [14739](https://github.com/python/mypy/pull/14739)) #### Typeshed Updates @@ -331,7 +405,7 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m #### Typeshed Updates -Please see [git log](https://github.com/python/typeshed/commits/main?after=7c8e82fe483a40ec4cb0a2505cfdb0f3e7cc81d9+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. #### Mypy 1.10.1 From 0a040dd25dc1d3847994574152932112369c0840 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 19 Jul 2024 23:22:53 -0400 Subject: [PATCH 0704/1617] Fix types.GenericAlias lookup crash (#17543) Fixes #17542 --- mypy/checkexpr.py | 2 +- test-data/unit/check-functions.test | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 3532e18b93b2..2e8e6e2db9d5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4341,7 +4341,7 @@ def visit_index_with_type( elif isinstance(left_type, FunctionLike) and left_type.is_type_obj(): if left_type.type_object().is_enum: return self.visit_enum_index_expr(left_type.type_object(), e.index, e) - elif left_type.type_object().type_vars: + elif left_type.type_object().type_vars and self.chk.options.python_version >= (3, 9): return self.named_type("types.GenericAlias") elif ( left_type.type_object().fullname == "builtins.type" diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 6c895c86e899..ef5a66b6ecde 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1779,6 +1779,7 @@ def Arg(x, y): pass F = Callable[[Arg(int, 'x')], int] # E: Invalid argument constructor "__main__.Arg" [case testCallableParsingFromExpr] +# flags: --python-version 3.9 from typing import Callable, List from mypy_extensions import Arg, VarArg, KwArg import mypy_extensions @@ -1799,10 +1800,23 @@ L = Callable[[Arg(name='x', type=int)], int] # ok # I have commented out the following test because I don't know how to expect the "defined here" note part of the error. # M = Callable[[Arg(gnome='x', type=int)], int] E: Invalid type alias: expression is not a valid type E: Unexpected keyword argument "gnome" for "Arg" N = Callable[[Arg(name=None, type=int)], int] # ok -O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: Type expected within [...] +O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: Type expected within [...] P = Callable[[mypy_extensions.VarArg(int)], int] # ok -Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "type" -R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "name" +Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: "Arg" gets multiple values for keyword argument "type" +R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: "Arg" gets multiple values for keyword argument "name" + + + + + + + [builtins fixtures/dict.pyi] [case testCallableParsing] From 0b09116baae650441e4038b9cd486c7583d47f2a Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 20 Jul 2024 02:39:32 -0400 Subject: [PATCH 0705/1617] Indexing a type also produces a GenericAlias (#17546) Mentioned by Jelle in https://github.com/python/mypy/pull/17543#issuecomment-2240829583 --- mypy/checkexpr.py | 11 ++++------- test-data/unit/pythoneval.test | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2e8e6e2db9d5..ad1b416a0227 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4341,14 +4341,11 @@ def visit_index_with_type( elif isinstance(left_type, FunctionLike) and left_type.is_type_obj(): if left_type.type_object().is_enum: return self.visit_enum_index_expr(left_type.type_object(), e.index, e) - elif left_type.type_object().type_vars and self.chk.options.python_version >= (3, 9): - return self.named_type("types.GenericAlias") - elif ( - left_type.type_object().fullname == "builtins.type" - and self.chk.options.python_version >= (3, 9) + elif self.chk.options.python_version >= (3, 9) and ( + left_type.type_object().type_vars + or left_type.type_object().fullname == "builtins.type" ): - # builtins.type is special: it's not generic in stubs, but it supports indexing - return self.named_type("typing._SpecialForm") + return self.named_type("types.GenericAlias") if isinstance(left_type, TypeVarType) and not self.has_member( left_type.upper_bound, "__getitem__" diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 222430c3ef55..832e55f333de 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1781,7 +1781,7 @@ C = str | int D: TypeAlias = str | int [out] _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("") +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("GenericAlias") _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Unsupported left operand type for | ("Type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type From 6aa46f097cf30f3143c5ee4bae7f0d2e7ce914bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:00:33 -0700 Subject: [PATCH 0706/1617] Bump setuptools from 68.2.2 to 70.0.0 (#17533) --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 75a970c5bf0e..f4fb4a20cce7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -57,5 +57,5 @@ typing-extensions==4.12.2 # via -r mypy-requirements.txt # The following packages are considered to be unsafe in a requirements file: -setuptools==68.2.2 +setuptools==70.0.0 # via -r test-requirements.in From b202f3069a7c8c2f64b6bcc6a1f63794878d6c9d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 22 Jul 2024 22:44:37 +0300 Subject: [PATCH 0707/1617] Fix `typing.TypeAliasType` being undefined on python < 3.12 (#17558) Closes #17554 CC @JukkaL Refs https://github.com/python/mypy/pull/17320 --- mypy/checkexpr.py | 10 ++++++++-- test-data/unit/check-type-aliases.test | 11 ++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ad1b416a0227..9dee743ad406 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4679,7 +4679,7 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: """ if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias): if tapp.expr.node.python_3_12_type_alias: - return self.named_type("typing.TypeAliasType") + return self.type_alias_type_type() # Subscription of a (generic) alias in runtime context, expand the alias. item = instantiate_type_alias( tapp.expr.node, @@ -4743,7 +4743,7 @@ class LongName(Generic[T]): ... y = cast(A, ...) """ if alias.python_3_12_type_alias: - return self.named_type("typing.TypeAliasType") + return self.type_alias_type_type() if isinstance(alias.target, Instance) and alias.target.invalid: # type: ignore[misc] # An invalid alias, error already has been reported return AnyType(TypeOfAny.from_error) @@ -5863,6 +5863,12 @@ def named_type(self, name: str) -> Instance: """ return self.chk.named_type(name) + def type_alias_type_type(self) -> Instance: + """Returns a `typing.TypeAliasType` or `typing_extensions.TypeAliasType`.""" + if self.chk.options.python_version >= (3, 12): + return self.named_type("typing.TypeAliasType") + return self.named_type("typing_extensions.TypeAliasType") + def is_valid_var_arg(self, typ: Type) -> bool: """Is a type valid as a *args argument?""" typ = get_proper_type(typ) diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 6f9e9eda1d02..c7b9694a9188 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1074,7 +1074,7 @@ x: TestType = 42 y: TestType = 'a' z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") -reveal_type(TestType) # N: Revealed type is "typing.TypeAliasType" +reveal_type(TestType) # N: Revealed type is "typing_extensions.TypeAliasType" TestType() # E: "TypeAliasType" not callable class A: @@ -1084,6 +1084,15 @@ yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has typ [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] +[case testTypeAliasTypePython311] +# flags: --python-version 3.11 +# Pinning to 3.11, because 3.12 has `TypeAliasType` +from typing_extensions import TypeAliasType + +TestType = TypeAliasType("TestType", int) +x: TestType = 1 +[builtins fixtures/tuple.pyi] + [case testTypeAliasTypeInvalid] from typing_extensions import TypeAliasType From f2cee90937e96603b7b28083de966f587fba757d Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 24 Jul 2024 00:27:56 +0300 Subject: [PATCH 0708/1617] PEP 695: Add detection and error reporting for the use of incorrect expressions within the scope of a type parameter and a type alias (#17560) This fixes part of #15238: > Add detection and error reporting for use of assignment expressions, yield, yield from, and await expressions within the type parameter scope; see [this section of PEP](https://peps.python.org/pep-0695/#type-parameter-scopes) for details --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: sobolevn --- mypy/fastparse.py | 47 +++++++++++++++++++++++++++++ mypy/message_registry.py | 24 +++++++++++++++ test-data/unit/check-python312.test | 46 ++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 75c4bd46550c..363fc8375259 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -181,10 +181,12 @@ def ast3_parse( if sys.version_info >= (3, 12): ast_TypeAlias = ast3.TypeAlias ast_ParamSpec = ast3.ParamSpec + ast_TypeVar = ast3.TypeVar ast_TypeVarTuple = ast3.TypeVarTuple else: ast_TypeAlias = Any ast_ParamSpec = Any + ast_TypeVar = Any ast_TypeVarTuple = Any N = TypeVar("N", bound=Node) @@ -345,6 +347,15 @@ def is_no_type_check_decorator(expr: ast3.expr) -> bool: return False +def find_disallowed_expression_in_annotation_scope(expr: ast3.expr | None) -> ast3.expr | None: + if expr is None: + return None + for node in ast3.walk(expr): + if isinstance(node, (ast3.Yield, ast3.YieldFrom, ast3.NamedExpr, ast3.Await)): + return node + return None + + class ASTConverter: def __init__( self, @@ -1180,6 +1191,29 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.class_and_function_stack.pop() return cdef + def validate_type_param(self, type_param: ast_TypeVar) -> None: + incorrect_expr = find_disallowed_expression_in_annotation_scope(type_param.bound) + if incorrect_expr is None: + return + if isinstance(incorrect_expr, (ast3.Yield, ast3.YieldFrom)): + self.fail( + message_registry.TYPE_VAR_YIELD_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + if isinstance(incorrect_expr, ast3.NamedExpr): + self.fail( + message_registry.TYPE_VAR_NAMED_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + if isinstance(incorrect_expr, ast3.Await): + self.fail( + message_registry.TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: explicit_type_params = [] for p in type_params: @@ -1202,6 +1236,7 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: conv = TypeConverter(self.errors, line=p.lineno) values = [conv.visit(t) for t in p.bound.elts] elif p.bound is not None: + self.validate_type_param(p) bound = TypeConverter(self.errors, line=p.lineno).visit(p.bound) explicit_type_params.append(TypeParam(p.name, TYPE_VAR_KIND, bound, values)) return explicit_type_params @@ -1791,11 +1826,23 @@ def visit_MatchOr(self, n: MatchOr) -> OrPattern: node = OrPattern([self.visit(pattern) for pattern in n.patterns]) return self.set_line(node, n) + def validate_type_alias(self, n: ast_TypeAlias) -> None: + incorrect_expr = find_disallowed_expression_in_annotation_scope(n.value) + if incorrect_expr is None: + return + if isinstance(incorrect_expr, (ast3.Yield, ast3.YieldFrom)): + self.fail(message_registry.TYPE_ALIAS_WITH_YIELD_EXPRESSION, n.lineno, n.col_offset) + if isinstance(incorrect_expr, ast3.NamedExpr): + self.fail(message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION, n.lineno, n.col_offset) + if isinstance(incorrect_expr, ast3.Await): + self.fail(message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION, n.lineno, n.col_offset) + # TypeAlias(identifier name, type_param* type_params, expr value) def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: node: TypeAliasStmt | AssignmentStmt if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: type_params = self.translate_type_params(n.type_params) + self.validate_type_alias(n) value = self.visit(n.value) # Since the value is evaluated lazily, wrap the value inside a lambda. # This helps mypyc. diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 06199e70d6b4..29d539faaed6 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -338,3 +338,27 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES: Final = ErrorMessage( "Type variable must have at least two constrained types", codes.MISC ) + +TYPE_VAR_YIELD_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Yield expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_VAR_NAMED_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Named expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Await expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage( + "Yield expression cannot be used within a type alias", codes.SYNTAX +) + +TYPE_ALIAS_WITH_NAMED_EXPRESSION: Final = ErrorMessage( + "Named expression cannot be used within a type alias", codes.SYNTAX +) + +TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage( + "Await expression cannot be used within a type alias", codes.SYNTAX +) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 073ef7f4bdec..a3f4c87120cd 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1667,3 +1667,49 @@ if x["other"] is not None: type Y[T] = {"item": T, **Y[T]} # E: Overwriting TypedDict field "item" while merging [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695UsingIncorrectExpressionsInTypeVariableBound] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type X[T: (yield 1)] = Any # E: Yield expression cannot be used as a type variable bound +type Y[T: (yield from [])] = Any # E: Yield expression cannot be used as a type variable bound +type Z[T: (a := 1)] = Any # E: Named expression cannot be used as a type variable bound +type K[T: (await 1)] = Any # E: Await expression cannot be used as a type variable bound + +type XNested[T: (1 + (yield 1))] = Any # E: Yield expression cannot be used as a type variable bound +type YNested[T: (1 + (yield from []))] = Any # E: Yield expression cannot be used as a type variable bound +type ZNested[T: (1 + (a := 1))] = Any # E: Named expression cannot be used as a type variable bound +type KNested[T: (1 + (await 1))] = Any # E: Await expression cannot be used as a type variable bound + +class FooX[T: (yield 1)]: pass # E: Yield expression cannot be used as a type variable bound +class FooY[T: (yield from [])]: pass # E: Yield expression cannot be used as a type variable bound +class FooZ[T: (a := 1)]: pass # E: Named expression cannot be used as a type variable bound +class FooK[T: (await 1)]: pass # E: Await expression cannot be used as a type variable bound + +class FooXNested[T: (1 + (yield 1))]: pass # E: Yield expression cannot be used as a type variable bound +class FooYNested[T: (1 + (yield from []))]: pass # E: Yield expression cannot be used as a type variable bound +class FooZNested[T: (1 + (a := 1))]: pass # E: Named expression cannot be used as a type variable bound +class FooKNested[T: (1 + (await 1))]: pass # E: Await expression cannot be used as a type variable bound + +def foox[T: (yield 1)](): pass # E: Yield expression cannot be used as a type variable bound +def fooy[T: (yield from [])](): pass # E: Yield expression cannot be used as a type variable bound +def fooz[T: (a := 1)](): pass # E: Named expression cannot be used as a type variable bound +def fook[T: (await 1)](): pass # E: Await expression cannot be used as a type variable bound + +def foox_nested[T: (1 + (yield 1))](): pass # E: Yield expression cannot be used as a type variable bound +def fooy_nested[T: (1 + (yield from []))](): pass # E: Yield expression cannot be used as a type variable bound +def fooz_nested[T: (1 + (a := 1))](): pass # E: Named expression cannot be used as a type variable bound +def fook_nested[T: (1 +(await 1))](): pass # E: Await expression cannot be used as a type variable bound + +[case testPEP695UsingIncorrectExpressionsInTypeAlias] +# flags: --enable-incomplete-feature=NewGenericSyntax + +type X = (yield 1) # E: Yield expression cannot be used within a type alias +type Y = (yield from []) # E: Yield expression cannot be used within a type alias +type Z = (a := 1) # E: Named expression cannot be used within a type alias +type K = (await 1) # E: Await expression cannot be used within a type alias + +type XNested = (1 + (yield 1)) # E: Yield expression cannot be used within a type alias +type YNested = (1 + (yield from [])) # E: Yield expression cannot be used within a type alias +type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a type alias +type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias From 18965d6315236e2a3e381ce8789638000661d40a Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 24 Jul 2024 01:09:24 +0100 Subject: [PATCH 0709/1617] Remove the explicit setting of a pygments theme (#17571) This allows the underlying theme being used to provide the pygments theme instead which looks better (subjectively). --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index fa76734054ac..5934c7474536 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -92,7 +92,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +# pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] From ed0cd4acba02ed19b1cf18ac0ac416dc251d7714 Mon Sep 17 00:00:00 2001 From: Jordandev678 Date: Wed, 24 Jul 2024 07:09:09 +0100 Subject: [PATCH 0710/1617] Narrow based on collection containment (#17344) Enables the narrowing of variable types when checking a variable is "in" a collection, and the collection type is a subtype of the variable type. Fixes #3229 This PR updates the type narrowing for the "in" operator and allows it to narrow the type of a variable to the type of the collection's items - if the collection item type is a subtype of the variable (as defined by is_subtype). Examples ```python def foobar(foo: Union[str, float]): if foo in ['a', 'b']: reveal_type(foo) # N: Revealed type is "builtins.str" else: reveal_type(foo) # N: Revealed type is "Union[builtins.str, builtins.float]" ``` ```python typ: List[Literal['a', 'b']] = ['a', 'b'] x: str = "hi!" if x in typ: reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" else: reveal_type(x) # N: Revealed type is "builtins.str" ``` One existing test was updated, which compared `Optional[A]` with "in" to `(None,)`. Piror to this change that resulted in `Union[__main__.A, None]`, which now narrows to `None`. Test cases have been added for "in", "not in", Sets, Lists, and Tuples. I did add to the existing narrowing.pyi fixture for the test cases. A search of the *.test files shows it was only used in the narrowing tests, so there shouldn't be any speed impact in other areas. --------- Co-authored-by: Jordandev678 <20153053+Jordandev678@users.noreply.github.com> --- mypy/checker.py | 15 ++-- test-data/unit/check-narrowing.test | 112 +++++++++++++++++++++++++- test-data/unit/fixtures/narrowing.pyi | 9 ++- 3 files changed, 128 insertions(+), 8 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0ae499916ec6..59de599006a8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6011,11 +6011,16 @@ def has_no_custom_eq_checks(t: Type) -> bool: if_map, else_map = {}, {} if left_index in narrowable_operand_index_to_hash: - # We only try and narrow away 'None' for now - if is_overlapping_none(item_type): - collection_item_type = get_proper_type( - builtin_item_type(iterable_type) - ) + collection_item_type = get_proper_type(builtin_item_type(iterable_type)) + # Narrow if the collection is a subtype + if ( + collection_item_type is not None + and collection_item_type != item_type + and is_subtype(collection_item_type, item_type) + ): + if_map[operands[left_index]] = collection_item_type + # Try and narrow away 'None' + elif is_overlapping_none(item_type): if ( collection_item_type is not None and not is_overlapping_none(collection_item_type) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 8612df9bc663..e142fdd5d060 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1376,13 +1376,13 @@ else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" if val in (None,): - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "None" else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" if val not in (None,): reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" else: - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "None" [builtins fixtures/primitives.pyi] [case testNarrowingWithTupleOfTypes] @@ -2114,3 +2114,111 @@ else: [typing fixtures/typing-medium.pyi] [builtins fixtures/ops.pyi] + + +[case testTypeNarrowingStringInLiteralUnion] +from typing import Literal, Tuple +typ: Tuple[Literal['a', 'b'], ...] = ('a', 'b') +x: str = "hi!" +if x in typ: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +else: + reveal_type(x) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTypeNarrowingStringInLiteralUnionSubset] +from typing import Literal, Tuple +typeAlpha: Tuple[Literal['a', 'b', 'c'], ...] = ('a', 'b') +strIn: str = "b" +strOut: str = "c" +if strIn in typeAlpha: + reveal_type(strIn) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" +else: + reveal_type(strIn) # N: Revealed type is "builtins.str" +if strOut in typeAlpha: + reveal_type(strOut) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" +else: + reveal_type(strOut) # N: Revealed type is "builtins.str" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-medium.pyi] + +[case testNarrowingStringNotInLiteralUnion] +from typing import Literal, Tuple +typeAlpha: Tuple[Literal['a', 'b', 'c'],...] = ('a', 'b', 'c') +strIn: str = "c" +strOut: str = "d" +if strIn not in typeAlpha: + reveal_type(strIn) # N: Revealed type is "builtins.str" +else: + reveal_type(strIn) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" +if strOut in typeAlpha: + reveal_type(strOut) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" +else: + reveal_type(strOut) # N: Revealed type is "builtins.str" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-medium.pyi] + +[case testNarrowingStringInLiteralUnionDontExpand] +from typing import Literal, Tuple +typeAlpha: Tuple[Literal['a', 'b', 'c'], ...] = ('a', 'b', 'c') +strIn: Literal['c'] = "c" +reveal_type(strIn) # N: Revealed type is "Literal['c']" +#Check we don't expand a Literal into the Union type +if strIn not in typeAlpha: + reveal_type(strIn) # N: Revealed type is "Literal['c']" +else: + reveal_type(strIn) # N: Revealed type is "Literal['c']" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTypeNarrowingStringInMixedUnion] +from typing import Literal, Tuple +typ: Tuple[Literal['a', 'b'], ...] = ('a', 'b') +x: str = "hi!" +if x in typ: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +else: + reveal_type(x) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTypeNarrowingStringInSet] +from typing import Literal, Set +typ: Set[Literal['a', 'b']] = {'a', 'b'} +x: str = "hi!" +if x in typ: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +else: + reveal_type(x) # N: Revealed type is "builtins.str" +if x not in typ: + reveal_type(x) # N: Revealed type is "builtins.str" +else: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +[builtins fixtures/narrowing.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTypeNarrowingStringInList] +from typing import Literal, List +typ: List[Literal['a', 'b']] = ['a', 'b'] +x: str = "hi!" +if x in typ: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +else: + reveal_type(x) # N: Revealed type is "builtins.str" +if x not in typ: + reveal_type(x) # N: Revealed type is "builtins.str" +else: + reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" +[builtins fixtures/narrowing.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTypeNarrowingUnionStringFloat] +from typing import Union +def foobar(foo: Union[str, float]): + if foo in ['a', 'b']: + reveal_type(foo) # N: Revealed type is "builtins.str" + else: + reveal_type(foo) # N: Revealed type is "Union[builtins.str, builtins.float]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/fixtures/narrowing.pyi b/test-data/unit/fixtures/narrowing.pyi index 89ee011c1c80..a36ac7f29bd2 100644 --- a/test-data/unit/fixtures/narrowing.pyi +++ b/test-data/unit/fixtures/narrowing.pyi @@ -1,5 +1,5 @@ # Builtins stub used in check-narrowing test cases. -from typing import Generic, Sequence, Tuple, Type, TypeVar, Union +from typing import Generic, Sequence, Tuple, Type, TypeVar, Union, Iterable Tco = TypeVar('Tco', covariant=True) @@ -15,6 +15,13 @@ class function: pass class ellipsis: pass class int: pass class str: pass +class float: pass class dict(Generic[KT, VT]): pass def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass + +class list(Sequence[Tco]): + def __contains__(self, other: object) -> bool: pass +class set(Iterable[Tco], Generic[Tco]): + def __init__(self, iterable: Iterable[Tco] = ...) -> None: ... + def __contains__(self, item: object) -> bool: pass From 312694fda959357802d6425397accd8cb4b58a73 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 24 Jul 2024 02:10:16 -0400 Subject: [PATCH 0711/1617] Fix PEP 604 isinstance caching (#17563) Mentioned by ngnpope --- mypy/types.py | 11 +++++++++-- test-data/unit/check-incremental.test | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 91b40536f1cf..52b3121f9fb3 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2900,12 +2900,19 @@ def relevant_items(self) -> list[Type]: return [i for i in self.items if not isinstance(get_proper_type(i), NoneType)] def serialize(self) -> JsonDict: - return {".class": "UnionType", "items": [t.serialize() for t in self.items]} + return { + ".class": "UnionType", + "items": [t.serialize() for t in self.items], + "uses_pep604_syntax": self.uses_pep604_syntax, + } @classmethod def deserialize(cls, data: JsonDict) -> UnionType: assert data[".class"] == "UnionType" - return UnionType([deserialize_type(t) for t in data["items"]]) + return UnionType( + [deserialize_type(t) for t in data["items"]], + uses_pep604_syntax=data["uses_pep604_syntax"], + ) class PartialType(ProperType): diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 24292bce3e21..173265e48e6f 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6726,3 +6726,20 @@ from typing_extensions import TypeIs def guard(x: object) -> TypeIs[int]: pass [builtins fixtures/tuple.pyi] + +[case testStartUsingPEP604Union] +# flags: --python-version 3.10 +import a +[file a.py] +import lib + +[file a.py.2] +from lib import IntOrStr +assert isinstance(1, IntOrStr) + +[file lib.py] +from typing_extensions import TypeAlias + +IntOrStr: TypeAlias = int | str +assert isinstance(1, IntOrStr) +[builtins fixtures/type.pyi] From db9837fa240918fa90f2d9a3952314bd3207ed20 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:10:42 +1200 Subject: [PATCH 0712/1617] fix: Mismatched signature between checker plugin API and implementation (#17343) This PR changes the `fail` method's signature to be positional-only for the first two parameters, due to a mismatch between the [`CheckerPluginInterface` signature](https://github.com/python/mypy/blob/8dd268ffd84ccf549b3aa9105dd35766a899b2bd/mypy/plugin.py#L242-L244) and the implementation class ([`TypeChecker`](https://github.com/python/mypy/blob/8dd268ffd84ccf549b3aa9105dd35766a899b2bd/mypy/checker.py#L7116-L7118)). ```python class CheckerPluginInterface: ... @abstractmethod def fail( self, msg: str | ErrorMessage, ctx: Context, *, code: ErrorCode | None = None ) -> None: ... ``` ```python class TypeChecker(NodeVisitor[None], CheckerPluginInterface): ... def fail( self, msg: str | ErrorMessage, context: Context, *, code: ErrorCode | None = None ) -> None: ``` An alternative fix would be to change `TypeChecker.fail`'s parameter name to `ctx`, but that has a greater disruption potential. Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/plugin.py b/mypy/plugin.py index 858795addb7f..a1af7fa76350 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -240,7 +240,7 @@ def type_context(self) -> list[Type | None]: @abstractmethod def fail( - self, msg: str | ErrorMessage, ctx: Context, *, code: ErrorCode | None = None + self, msg: str | ErrorMessage, ctx: Context, /, *, code: ErrorCode | None = None ) -> None: """Emit an error message at given location.""" raise NotImplementedError From e67decb91ea5aacbb1e126463b02d78ded680a90 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:55:23 +0200 Subject: [PATCH 0713/1617] Sync typeshed (#17586) Source commit: https://github.com/python/typeshed/commit/9eae1ae1a5b47745c7d289bb2092f2b4340e7622 --- mypy/typeshed/stdlib/VERSIONS | 4 + mypy/typeshed/stdlib/_collections_abc.pyi | 4 + mypy/typeshed/stdlib/_csv.pyi | 14 +- mypy/typeshed/stdlib/_ctypes.pyi | 17 +- mypy/typeshed/stdlib/_interpchannels.pyi | 8 +- mypy/typeshed/stdlib/_interpqueues.pyi | 16 ++ mypy/typeshed/stdlib/_interpreters.pyi | 50 ++++ mypy/typeshed/stdlib/_stat.pyi | 166 +++++------ mypy/typeshed/stdlib/_thread.pyi | 8 +- mypy/typeshed/stdlib/_tkinter.pyi | 20 +- mypy/typeshed/stdlib/_winapi.pyi | 264 +++++++++--------- mypy/typeshed/stdlib/abc.pyi | 6 +- mypy/typeshed/stdlib/argparse.pyi | 12 +- mypy/typeshed/stdlib/asyncio/base_events.pyi | 72 ++++- mypy/typeshed/stdlib/asyncio/base_futures.pyi | 8 +- mypy/typeshed/stdlib/asyncio/constants.pyi | 16 +- mypy/typeshed/stdlib/asyncio/events.pyi | 75 ++++- .../stdlib/asyncio/format_helpers.pyi | 17 +- mypy/typeshed/stdlib/asyncio/queues.pyi | 13 +- mypy/typeshed/stdlib/asyncio/sslproto.pyi | 10 +- mypy/typeshed/stdlib/asyncio/streams.pyi | 6 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 83 ++++-- .../stdlib/asyncio/windows_events.pyi | 40 ++- .../typeshed/stdlib/asyncio/windows_utils.pyi | 4 +- mypy/typeshed/stdlib/bdb.pyi | 18 +- mypy/typeshed/stdlib/binhex.pyi | 8 +- mypy/typeshed/stdlib/builtins.pyi | 28 +- mypy/typeshed/stdlib/cmd.pyi | 4 +- mypy/typeshed/stdlib/codecs.pyi | 42 +-- .../stdlib/concurrent/futures/__init__.pyi | 47 +++- .../stdlib/concurrent/futures/_base.pyi | 18 +- mypy/typeshed/stdlib/configparser.pyi | 6 +- mypy/typeshed/stdlib/copy.pyi | 14 +- mypy/typeshed/stdlib/datetime.pyi | 6 +- mypy/typeshed/stdlib/dbm/gnu.pyi | 3 + mypy/typeshed/stdlib/dbm/ndbm.pyi | 3 + mypy/typeshed/stdlib/dbm/sqlite3.pyi | 29 ++ mypy/typeshed/stdlib/dis.pyi | 89 +++++- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 11 +- mypy/typeshed/stdlib/distutils/cmd.pyi | 21 +- .../stdlib/distutils/command/bdist.pyi | 18 +- .../stdlib/distutils/command/bdist_dumb.pyi | 8 +- .../stdlib/distutils/command/bdist_msi.pyi | 6 +- .../stdlib/distutils/command/bdist_rpm.pyi | 8 +- .../distutils/command/bdist_wininst.pyi | 4 +- .../stdlib/distutils/command/build.pyi | 7 +- .../stdlib/distutils/command/build_clib.pyi | 10 +- .../stdlib/distutils/command/build_ext.pyi | 10 +- .../stdlib/distutils/command/build_py.pyi | 8 +- .../distutils/command/build_scripts.pyi | 6 +- .../stdlib/distutils/command/check.pyi | 6 +- .../stdlib/distutils/command/clean.pyi | 6 +- .../stdlib/distutils/command/config.pyi | 4 +- .../stdlib/distutils/command/install.pyi | 6 +- .../stdlib/distutils/command/install_data.pyi | 6 +- .../distutils/command/install_egg_info.pyi | 2 +- .../distutils/command/install_headers.pyi | 6 +- .../stdlib/distutils/command/install_lib.pyi | 8 +- .../distutils/command/install_scripts.pyi | 6 +- .../stdlib/distutils/command/sdist.pyi | 11 +- mypy/typeshed/stdlib/distutils/dist.pyi | 6 +- mypy/typeshed/stdlib/distutils/util.pyi | 7 +- mypy/typeshed/stdlib/email/utils.pyi | 17 +- mypy/typeshed/stdlib/fcntl.pyi | 39 ++- mypy/typeshed/stdlib/filecmp.pyi | 4 +- mypy/typeshed/stdlib/ftplib.pyi | 12 +- mypy/typeshed/stdlib/gc.pyi | 14 +- mypy/typeshed/stdlib/http/cookiejar.pyi | 2 +- .../stdlib/importlib/metadata/__init__.pyi | 15 +- .../stdlib/importlib/metadata/_meta.pyi | 20 +- .../stdlib/importlib/metadata/diagnose.pyi | 2 + mypy/typeshed/stdlib/inspect.pyi | 64 +++-- mypy/typeshed/stdlib/io.pyi | 22 +- mypy/typeshed/stdlib/ipaddress.pyi | 6 +- mypy/typeshed/stdlib/itertools.pyi | 6 +- mypy/typeshed/stdlib/lzma.pyi | 54 ++-- mypy/typeshed/stdlib/mailbox.pyi | 8 + mypy/typeshed/stdlib/mimetypes.pyi | 1 + mypy/typeshed/stdlib/mmap.pyi | 16 +- mypy/typeshed/stdlib/msvcrt.pyi | 12 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 10 +- .../stdlib/multiprocessing/reduction.pyi | 7 +- mypy/typeshed/stdlib/nntplib.pyi | 6 +- mypy/typeshed/stdlib/os/__init__.pyi | 24 +- mypy/typeshed/stdlib/pathlib.pyi | 10 +- mypy/typeshed/stdlib/pdb.pyi | 29 +- mypy/typeshed/stdlib/poplib.pyi | 12 +- mypy/typeshed/stdlib/posix.pyi | 22 +- mypy/typeshed/stdlib/pty.pyi | 10 +- mypy/typeshed/stdlib/pydoc.pyi | 37 ++- mypy/typeshed/stdlib/readline.pyi | 4 + mypy/typeshed/stdlib/site.pyi | 9 + mypy/typeshed/stdlib/sre_constants.pyi | 3 +- mypy/typeshed/stdlib/stat.pyi | 4 +- mypy/typeshed/stdlib/symtable.pyi | 31 +- mypy/typeshed/stdlib/sys/__init__.pyi | 6 +- mypy/typeshed/stdlib/syslog.pyi | 82 +++--- mypy/typeshed/stdlib/tempfile.pyi | 2 + mypy/typeshed/stdlib/threading.pyi | 2 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 71 ++++- mypy/typeshed/stdlib/tkinter/constants.pyi | 144 +++++----- mypy/typeshed/stdlib/tkinter/font.pyi | 10 +- mypy/typeshed/stdlib/tkinter/tix.pyi | 62 ++-- mypy/typeshed/stdlib/trace.pyi | 13 +- mypy/typeshed/stdlib/turtle.pyi | 14 +- mypy/typeshed/stdlib/types.pyi | 21 +- mypy/typeshed/stdlib/typing.pyi | 12 +- mypy/typeshed/stdlib/typing_extensions.pyi | 1 + mypy/typeshed/stdlib/unittest/__init__.pyi | 16 +- mypy/typeshed/stdlib/unittest/async_case.pyi | 4 + mypy/typeshed/stdlib/unittest/loader.pyi | 36 +-- mypy/typeshed/stdlib/unittest/main.pyi | 6 +- mypy/typeshed/stdlib/unittest/mock.pyi | 72 +++-- mypy/typeshed/stdlib/warnings.pyi | 6 +- mypy/typeshed/stdlib/wave.pyi | 4 +- mypy/typeshed/stdlib/webbrowser.pyi | 7 +- mypy/typeshed/stdlib/winsound.pyi | 32 +-- mypy/typeshed/stdlib/xml/dom/pulldom.pyi | 18 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 12 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 3 + mypy/typeshed/stdlib/zipfile/_path.pyi | 7 +- mypy/typeshed/stdlib/zlib.pyi | 38 +-- test-data/unit/pythoneval.test | 14 +- 123 files changed, 1750 insertions(+), 936 deletions(-) create mode 100644 mypy/typeshed/stdlib/_interpqueues.pyi create mode 100644 mypy/typeshed/stdlib/_interpreters.pyi create mode 100644 mypy/typeshed/stdlib/dbm/sqlite3.pyi create mode 100644 mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 89754f65f3fa..641f951ce3c0 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -35,6 +35,8 @@ _dummy_threading: 3.0-3.8 _heapq: 3.0- _imp: 3.0- _interpchannels: 3.13- +_interpqueues: 3.13- +_interpreters: 3.13- _json: 3.0- _locale: 3.0- _lsprof: 3.0- @@ -112,6 +114,7 @@ curses: 3.0- dataclasses: 3.7- datetime: 3.0- dbm: 3.0- +dbm.sqlite3: 3.13- decimal: 3.0- difflib: 3.0- dis: 3.0- @@ -155,6 +158,7 @@ importlib: 3.0- importlib._abc: 3.10- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- +importlib.metadata.diagnose: 3.13- importlib.readers: 3.10- importlib.resources: 3.7- importlib.resources.abc: 3.11- diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index e467d626e8a8..127488ee382c 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -70,6 +70,8 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + if sys.version_info >= (3, 13): + def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -83,6 +85,8 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + if sys.version_info >= (3, 13): + def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 19f2dc9664b1..9bb5d27f6e35 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,18 +1,18 @@ import sys from _typeshed import SupportsWrite from collections.abc import Iterable, Iterator -from typing import Any, Final, Literal +from typing import Any, Final from typing_extensions import TypeAlias __version__: Final[str] -QUOTE_ALL: Literal[1] -QUOTE_MINIMAL: Literal[0] -QUOTE_NONE: Literal[3] -QUOTE_NONNUMERIC: Literal[2] +QUOTE_ALL: Final = 1 +QUOTE_MINIMAL: Final = 0 +QUOTE_NONE: Final = 3 +QUOTE_NONNUMERIC: Final = 2 if sys.version_info >= (3, 12): - QUOTE_STRINGS: Literal[4] - QUOTE_NOTNULL: Literal[5] + QUOTE_STRINGS: Final = 4 + QUOTE_NOTNULL: Final = 5 # Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC` # However, using literals in situations like these can cause false-positives (see #7258) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index a5f20dfd30e7..f7da03a67ead 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -64,7 +64,6 @@ class _CData(metaclass=_CDataMeta): # Structure.from_buffer(...) # valid at runtime # Structure(...).from_buffer(...) # invalid at runtime # - @classmethod def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... @classmethod @@ -72,7 +71,7 @@ class _CData(metaclass=_CDataMeta): @classmethod def from_address(cls, address: int) -> Self: ... @classmethod - def from_param(cls, obj: Any) -> Self | _CArgObject: ... + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... @classmethod def in_dll(cls, library: CDLL, name: str) -> Self: ... def __buffer__(self, flags: int, /) -> memoryview: ... @@ -100,8 +99,8 @@ class _Pointer(_PointerLike, _CData, Generic[_CT]): def __getitem__(self, key: slice, /) -> list[Any]: ... def __setitem__(self, key: int, value: Any, /) -> None: ... -def POINTER(type: type[_CT]) -> type[_Pointer[_CT]]: ... -def pointer(arg: _CT, /) -> _Pointer[_CT]: ... +def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ... +def pointer(obj: _CT, /) -> _Pointer[_CT]: ... class _CArgObject: ... @@ -199,9 +198,9 @@ class Array(_CData, Generic[_CT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -def addressof(obj: _CData) -> int: ... -def alignment(obj_or_type: _CData | type[_CData]) -> int: ... +def addressof(obj: _CData, /) -> int: ... +def alignment(obj_or_type: _CData | type[_CData], /) -> int: ... def get_errno() -> int: ... -def resize(obj: _CData, size: int) -> None: ... -def set_errno(value: int) -> int: ... -def sizeof(obj_or_type: _CData | type[_CData]) -> int: ... +def resize(obj: _CData, size: int, /) -> None: ... +def set_errno(value: int, /) -> int: ... +def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ... diff --git a/mypy/typeshed/stdlib/_interpchannels.pyi b/mypy/typeshed/stdlib/_interpchannels.pyi index b77fe321a071..c03496044df0 100644 --- a/mypy/typeshed/stdlib/_interpchannels.pyi +++ b/mypy/typeshed/stdlib/_interpchannels.pyi @@ -1,5 +1,5 @@ from _typeshed import structseq -from typing import Final, Literal, SupportsIndex, final +from typing import Any, Final, Literal, SupportsIndex, final from typing_extensions import Buffer, Self class ChannelError(RuntimeError): ... @@ -72,13 +72,15 @@ class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, in @property def send_released(self) -> bool: ... -def create() -> ChannelID: ... +def create(unboundop: Literal[1, 2, 3]) -> ChannelID: ... def destroy(cid: SupportsIndex) -> None: ... def list_all() -> list[ChannelID]: ... def list_interpreters(cid: SupportsIndex, *, send: bool) -> list[int]: ... def send(cid: SupportsIndex, obj: object, *, blocking: bool = True, timeout: float | None = None) -> None: ... def send_buffer(cid: SupportsIndex, obj: Buffer, *, blocking: bool = True, timeout: float | None = None) -> None: ... -def recv(cid: SupportsIndex, default: object = ...) -> object: ... +def recv(cid: SupportsIndex, default: object = ...) -> tuple[Any, Literal[1, 2, 3]]: ... def close(cid: SupportsIndex, *, send: bool = False, recv: bool = False) -> None: ... +def get_count(cid: SupportsIndex) -> int: ... def get_info(cid: SupportsIndex) -> ChannelInfo: ... +def get_channel_defaults(cid: SupportsIndex) -> Literal[1, 2, 3]: ... def release(cid: SupportsIndex, *, send: bool = False, recv: bool = False, force: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpqueues.pyi b/mypy/typeshed/stdlib/_interpqueues.pyi new file mode 100644 index 000000000000..db5e4cff5068 --- /dev/null +++ b/mypy/typeshed/stdlib/_interpqueues.pyi @@ -0,0 +1,16 @@ +from typing import Any, SupportsIndex + +class QueueError(RuntimeError): ... +class QueueNotFoundError(QueueError): ... + +def bind(qid: SupportsIndex) -> None: ... +def create(maxsize: SupportsIndex, fmt: SupportsIndex) -> int: ... +def destroy(qid: SupportsIndex) -> None: ... +def get(qid: SupportsIndex) -> tuple[Any, int]: ... +def get_count(qid: SupportsIndex) -> int: ... +def get_maxsize(qid: SupportsIndex) -> int: ... +def get_queue_defaults(qid: SupportsIndex) -> tuple[int]: ... +def is_full(qid: SupportsIndex) -> bool: ... +def list_all() -> list[tuple[int, int]]: ... +def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex) -> None: ... +def release(qid: SupportsIndex) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi new file mode 100644 index 000000000000..75f661a7e8e1 --- /dev/null +++ b/mypy/typeshed/stdlib/_interpreters.pyi @@ -0,0 +1,50 @@ +import types +from collections.abc import Callable, Mapping +from typing import Final, Literal, SupportsIndex +from typing_extensions import TypeAlias + +_Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""] + +class InterpreterError(Exception): ... +class InterpreterNotFoundError(InterpreterError): ... +class NotShareableError(Exception): ... + +class CrossInterpreterBufferView: + def __buffer__(self, flags: int, /) -> memoryview: ... + +def new_config(name: _Configs = "isolated", /, **overides: object) -> types.SimpleNamespace: ... +def create(config: types.SimpleNamespace | _Configs | None = "isolated", *, reqrefs: bool = False) -> int: ... +def destroy(id: SupportsIndex, *, restrict: bool = False) -> None: ... +def list_all(*, require_ready: bool) -> list[tuple[int, int]]: ... +def get_current() -> tuple[int, int]: ... +def get_main() -> tuple[int, int]: ... +def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ... +def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ... +def whence(id: SupportsIndex) -> int: ... +def exec(id: SupportsIndex, code: str, shared: bool | None = None, *, restrict: bool = False) -> None: ... +def call( + id: SupportsIndex, + callable: Callable[..., object], + args: tuple[object, ...] | None = None, + kwargs: dict[str, object] | None = None, + *, + restrict: bool = False, +) -> object: ... +def run_string( + id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False +) -> None: ... +def run_func( + id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False +) -> None: ... +def set___main___attrs(id: SupportsIndex, updates: Mapping[str, object], *, restrict: bool = False) -> None: ... +def incref(id: SupportsIndex, *, implieslink: bool = False, restrict: bool = False) -> None: ... +def decref(id: SupportsIndex, *, restrict: bool = False) -> None: ... +def is_shareable(obj: object) -> bool: ... +def capture_exception(exc: BaseException | None = None) -> types.SimpleNamespace: ... + +WHENCE_UNKNOWN: Final = 0 +WHENCE_RUNTIME: Final = 1 +WHENCE_LEGACY_CAPI: Final = 2 +WHENCE_CAPI: Final = 3 +WHENCE_XI: Final = 4 +WHENCE_STDLIB: Final = 5 diff --git a/mypy/typeshed/stdlib/_stat.pyi b/mypy/typeshed/stdlib/_stat.pyi index c4e918d8b57f..903571a64bca 100644 --- a/mypy/typeshed/stdlib/_stat.pyi +++ b/mypy/typeshed/stdlib/_stat.pyi @@ -1,30 +1,30 @@ import sys -from typing import Literal - -SF_APPEND: Literal[0x00040000] -SF_ARCHIVED: Literal[0x00010000] -SF_IMMUTABLE: Literal[0x00020000] -SF_NOUNLINK: Literal[0x00100000] -SF_SNAPSHOT: Literal[0x00200000] - -ST_MODE: Literal[0] -ST_INO: Literal[1] -ST_DEV: Literal[2] -ST_NLINK: Literal[3] -ST_UID: Literal[4] -ST_GID: Literal[5] -ST_SIZE: Literal[6] -ST_ATIME: Literal[7] -ST_MTIME: Literal[8] -ST_CTIME: Literal[9] - -S_IFIFO: Literal[0o010000] -S_IFLNK: Literal[0o120000] -S_IFREG: Literal[0o100000] -S_IFSOCK: Literal[0o140000] -S_IFBLK: Literal[0o060000] -S_IFCHR: Literal[0o020000] -S_IFDIR: Literal[0o040000] +from typing import Final + +SF_APPEND: Final = 0x00040000 +SF_ARCHIVED: Final = 0x00010000 +SF_IMMUTABLE: Final = 0x00020000 +SF_NOUNLINK: Final = 0x00100000 +SF_SNAPSHOT: Final = 0x00200000 + +ST_MODE: Final = 0 +ST_INO: Final = 1 +ST_DEV: Final = 2 +ST_NLINK: Final = 3 +ST_UID: Final = 4 +ST_GID: Final = 5 +ST_SIZE: Final = 6 +ST_ATIME: Final = 7 +ST_MTIME: Final = 8 +ST_CTIME: Final = 9 + +S_IFIFO: Final = 0o010000 +S_IFLNK: Final = 0o120000 +S_IFREG: Final = 0o100000 +S_IFSOCK: Final = 0o140000 +S_IFBLK: Final = 0o060000 +S_IFCHR: Final = 0o020000 +S_IFDIR: Final = 0o040000 # These are 0 on systems that don't support the specific kind of file. # Example: Linux doesn't support door files, so S_IFDOOR is 0 on linux. @@ -32,37 +32,37 @@ S_IFDOOR: int S_IFPORT: int S_IFWHT: int -S_ISUID: Literal[0o4000] -S_ISGID: Literal[0o2000] -S_ISVTX: Literal[0o1000] - -S_IRWXU: Literal[0o0700] -S_IRUSR: Literal[0o0400] -S_IWUSR: Literal[0o0200] -S_IXUSR: Literal[0o0100] - -S_IRWXG: Literal[0o0070] -S_IRGRP: Literal[0o0040] -S_IWGRP: Literal[0o0020] -S_IXGRP: Literal[0o0010] - -S_IRWXO: Literal[0o0007] -S_IROTH: Literal[0o0004] -S_IWOTH: Literal[0o0002] -S_IXOTH: Literal[0o0001] - -S_ENFMT: Literal[0o2000] -S_IREAD: Literal[0o0400] -S_IWRITE: Literal[0o0200] -S_IEXEC: Literal[0o0100] - -UF_APPEND: Literal[0x00000004] -UF_COMPRESSED: Literal[0x00000020] # OS X 10.6+ only -UF_HIDDEN: Literal[0x00008000] # OX X 10.5+ only -UF_IMMUTABLE: Literal[0x00000002] -UF_NODUMP: Literal[0x00000001] -UF_NOUNLINK: Literal[0x00000010] -UF_OPAQUE: Literal[0x00000008] +S_ISUID: Final = 0o4000 +S_ISGID: Final = 0o2000 +S_ISVTX: Final = 0o1000 + +S_IRWXU: Final = 0o0700 +S_IRUSR: Final = 0o0400 +S_IWUSR: Final = 0o0200 +S_IXUSR: Final = 0o0100 + +S_IRWXG: Final = 0o0070 +S_IRGRP: Final = 0o0040 +S_IWGRP: Final = 0o0020 +S_IXGRP: Final = 0o0010 + +S_IRWXO: Final = 0o0007 +S_IROTH: Final = 0o0004 +S_IWOTH: Final = 0o0002 +S_IXOTH: Final = 0o0001 + +S_ENFMT: Final = 0o2000 +S_IREAD: Final = 0o0400 +S_IWRITE: Final = 0o0200 +S_IEXEC: Final = 0o0100 + +UF_APPEND: Final = 0x00000004 +UF_COMPRESSED: Final = 0x00000020 # OS X 10.6+ only +UF_HIDDEN: Final = 0x00008000 # OX X 10.5+ only +UF_IMMUTABLE: Final = 0x00000002 +UF_NODUMP: Final = 0x00000001 +UF_NOUNLINK: Final = 0x00000010 +UF_OPAQUE: Final = 0x00000008 def S_IMODE(mode: int, /) -> int: ... def S_IFMT(mode: int, /) -> int: ... @@ -84,34 +84,36 @@ if sys.platform == "win32": IO_REPARSE_TAG_APPEXECLINK: int if sys.platform == "win32": - FILE_ATTRIBUTE_ARCHIVE: Literal[32] - FILE_ATTRIBUTE_COMPRESSED: Literal[2048] - FILE_ATTRIBUTE_DEVICE: Literal[64] - FILE_ATTRIBUTE_DIRECTORY: Literal[16] - FILE_ATTRIBUTE_ENCRYPTED: Literal[16384] - FILE_ATTRIBUTE_HIDDEN: Literal[2] - FILE_ATTRIBUTE_INTEGRITY_STREAM: Literal[32768] - FILE_ATTRIBUTE_NORMAL: Literal[128] - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Literal[8192] - FILE_ATTRIBUTE_NO_SCRUB_DATA: Literal[131072] - FILE_ATTRIBUTE_OFFLINE: Literal[4096] - FILE_ATTRIBUTE_READONLY: Literal[1] - FILE_ATTRIBUTE_REPARSE_POINT: Literal[1024] - FILE_ATTRIBUTE_SPARSE_FILE: Literal[512] - FILE_ATTRIBUTE_SYSTEM: Literal[4] - FILE_ATTRIBUTE_TEMPORARY: Literal[256] - FILE_ATTRIBUTE_VIRTUAL: Literal[65536] + FILE_ATTRIBUTE_ARCHIVE: Final = 32 + FILE_ATTRIBUTE_COMPRESSED: Final = 2048 + FILE_ATTRIBUTE_DEVICE: Final = 64 + FILE_ATTRIBUTE_DIRECTORY: Final = 16 + FILE_ATTRIBUTE_ENCRYPTED: Final = 16384 + FILE_ATTRIBUTE_HIDDEN: Final = 2 + FILE_ATTRIBUTE_INTEGRITY_STREAM: Final = 32768 + FILE_ATTRIBUTE_NORMAL: Final = 128 + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Final = 8192 + FILE_ATTRIBUTE_NO_SCRUB_DATA: Final = 131072 + FILE_ATTRIBUTE_OFFLINE: Final = 4096 + FILE_ATTRIBUTE_READONLY: Final = 1 + FILE_ATTRIBUTE_REPARSE_POINT: Final = 1024 + FILE_ATTRIBUTE_SPARSE_FILE: Final = 512 + FILE_ATTRIBUTE_SYSTEM: Final = 4 + FILE_ATTRIBUTE_TEMPORARY: Final = 256 + FILE_ATTRIBUTE_VIRTUAL: Final = 65536 if sys.version_info >= (3, 13): - SF_SETTABLE: Literal[0x3FFF0000] + # Varies by platform. + SF_SETTABLE: Final[int] # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 # SF_RESTRICTED: Literal[0x00080000] - SF_FIRMLINK: Literal[0x00800000] - SF_DATALESS: Literal[0x40000000] + SF_FIRMLINK: Final = 0x00800000 + SF_DATALESS: Final = 0x40000000 - SF_SUPPORTED: Literal[0x9F0000] - SF_SYNTHETIC: Literal[0xC0000000] + if sys.platform == "darwin": + SF_SUPPORTED: Final = 0x9F0000 + SF_SYNTHETIC: Final = 0xC0000000 - UF_TRACKED: Literal[0x00000040] - UF_DATAVAULT: Literal[0x00000080] - UF_SETTABLE: Literal[0x0000FFFF] + UF_TRACKED: Final = 0x00000040 + UF_DATAVAULT: Final = 0x00000080 + UF_SETTABLE: Final = 0x0000FFFF diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 4ea9aa0609e5..304cb79ec96b 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -13,7 +13,7 @@ error = RuntimeError def _count() -> int: ... @final class LockType: - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... + def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... def locked(self) -> bool: ... def __enter__(self) -> bool: ... @@ -22,14 +22,14 @@ class LockType: ) -> None: ... @overload -def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> int: ... +def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... @overload -def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> int: ... +def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... def interrupt_main() -> None: ... def exit() -> NoReturn: ... def allocate_lock() -> LockType: ... def get_ident() -> int: ... -def stack_size(size: int = ...) -> int: ... +def stack_size(size: int = 0, /) -> int: ... TIMEOUT_MAX: float diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index aea74c8be279..a7293054d293 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from typing import Any, ClassVar, Literal, final +from typing import Any, ClassVar, Final, final from typing_extensions import TypeAlias # _tkinter is meant to be only used internally by tkinter, but some tkinter @@ -95,16 +95,16 @@ class TkappType: def settrace(self, func: _TkinterTraceFunc | None, /) -> None: ... # These should be kept in sync with tkinter.tix constants, except ALL_EVENTS which doesn't match TCL_ALL_EVENTS -ALL_EVENTS: Literal[-3] -FILE_EVENTS: Literal[8] -IDLE_EVENTS: Literal[32] -TIMER_EVENTS: Literal[16] -WINDOW_EVENTS: Literal[4] +ALL_EVENTS: Final = -3 +FILE_EVENTS: Final = 8 +IDLE_EVENTS: Final = 32 +TIMER_EVENTS: Final = 16 +WINDOW_EVENTS: Final = 4 -DONT_WAIT: Literal[2] -EXCEPTION: Literal[8] -READABLE: Literal[2] -WRITABLE: Literal[4] +DONT_WAIT: Final = 2 +EXCEPTION: Final = 8 +READABLE: Final = 2 +WRITABLE: Final = 4 TCL_VERSION: str TK_VERSION: str diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index c6fb0484df8e..62ea124045cc 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -1,117 +1,117 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Sequence -from typing import Any, Literal, NoReturn, final, overload +from typing import Any, Final, Literal, NoReturn, final, overload if sys.platform == "win32": - ABOVE_NORMAL_PRIORITY_CLASS: Literal[0x8000] - BELOW_NORMAL_PRIORITY_CLASS: Literal[0x4000] - - CREATE_BREAKAWAY_FROM_JOB: Literal[0x1000000] - CREATE_DEFAULT_ERROR_MODE: Literal[0x4000000] - CREATE_NO_WINDOW: Literal[0x8000000] - CREATE_NEW_CONSOLE: Literal[0x10] - CREATE_NEW_PROCESS_GROUP: Literal[0x200] - - DETACHED_PROCESS: Literal[8] - DUPLICATE_CLOSE_SOURCE: Literal[1] - DUPLICATE_SAME_ACCESS: Literal[2] - - ERROR_ALREADY_EXISTS: Literal[183] - ERROR_BROKEN_PIPE: Literal[109] - ERROR_IO_PENDING: Literal[997] - ERROR_MORE_DATA: Literal[234] - ERROR_NETNAME_DELETED: Literal[64] - ERROR_NO_DATA: Literal[232] - ERROR_NO_SYSTEM_RESOURCES: Literal[1450] - ERROR_OPERATION_ABORTED: Literal[995] - ERROR_PIPE_BUSY: Literal[231] - ERROR_PIPE_CONNECTED: Literal[535] - ERROR_SEM_TIMEOUT: Literal[121] - - FILE_FLAG_FIRST_PIPE_INSTANCE: Literal[0x80000] - FILE_FLAG_OVERLAPPED: Literal[0x40000000] - - FILE_GENERIC_READ: Literal[1179785] - FILE_GENERIC_WRITE: Literal[1179926] - - FILE_MAP_ALL_ACCESS: Literal[983071] - FILE_MAP_COPY: Literal[1] - FILE_MAP_EXECUTE: Literal[32] - FILE_MAP_READ: Literal[4] - FILE_MAP_WRITE: Literal[2] - - FILE_TYPE_CHAR: Literal[2] - FILE_TYPE_DISK: Literal[1] - FILE_TYPE_PIPE: Literal[3] - FILE_TYPE_REMOTE: Literal[32768] - FILE_TYPE_UNKNOWN: Literal[0] - - GENERIC_READ: Literal[0x80000000] - GENERIC_WRITE: Literal[0x40000000] - HIGH_PRIORITY_CLASS: Literal[0x80] - INFINITE: Literal[0xFFFFFFFF] + ABOVE_NORMAL_PRIORITY_CLASS: Final = 0x8000 + BELOW_NORMAL_PRIORITY_CLASS: Final = 0x4000 + + CREATE_BREAKAWAY_FROM_JOB: Final = 0x1000000 + CREATE_DEFAULT_ERROR_MODE: Final = 0x4000000 + CREATE_NO_WINDOW: Final = 0x8000000 + CREATE_NEW_CONSOLE: Final = 0x10 + CREATE_NEW_PROCESS_GROUP: Final = 0x200 + + DETACHED_PROCESS: Final = 8 + DUPLICATE_CLOSE_SOURCE: Final = 1 + DUPLICATE_SAME_ACCESS: Final = 2 + + ERROR_ALREADY_EXISTS: Final = 183 + ERROR_BROKEN_PIPE: Final = 109 + ERROR_IO_PENDING: Final = 997 + ERROR_MORE_DATA: Final = 234 + ERROR_NETNAME_DELETED: Final = 64 + ERROR_NO_DATA: Final = 232 + ERROR_NO_SYSTEM_RESOURCES: Final = 1450 + ERROR_OPERATION_ABORTED: Final = 995 + ERROR_PIPE_BUSY: Final = 231 + ERROR_PIPE_CONNECTED: Final = 535 + ERROR_SEM_TIMEOUT: Final = 121 + + FILE_FLAG_FIRST_PIPE_INSTANCE: Final = 0x80000 + FILE_FLAG_OVERLAPPED: Final = 0x40000000 + + FILE_GENERIC_READ: Final = 1179785 + FILE_GENERIC_WRITE: Final = 1179926 + + FILE_MAP_ALL_ACCESS: Final = 983071 + FILE_MAP_COPY: Final = 1 + FILE_MAP_EXECUTE: Final = 32 + FILE_MAP_READ: Final = 4 + FILE_MAP_WRITE: Final = 2 + + FILE_TYPE_CHAR: Final = 2 + FILE_TYPE_DISK: Final = 1 + FILE_TYPE_PIPE: Final = 3 + FILE_TYPE_REMOTE: Final = 32768 + FILE_TYPE_UNKNOWN: Final = 0 + + GENERIC_READ: Final = 0x80000000 + GENERIC_WRITE: Final = 0x40000000 + HIGH_PRIORITY_CLASS: Final = 0x80 + INFINITE: Final = 0xFFFFFFFF # Ignore the Flake8 error -- flake8-pyi assumes # most numbers this long will be implementation details, # but here we can see that it's a power of 2 - INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054 - IDLE_PRIORITY_CLASS: Literal[0x40] - NORMAL_PRIORITY_CLASS: Literal[0x20] - REALTIME_PRIORITY_CLASS: Literal[0x100] - NMPWAIT_WAIT_FOREVER: Literal[0xFFFFFFFF] - - MEM_COMMIT: Literal[0x1000] - MEM_FREE: Literal[0x10000] - MEM_IMAGE: Literal[0x1000000] - MEM_MAPPED: Literal[0x40000] - MEM_PRIVATE: Literal[0x20000] - MEM_RESERVE: Literal[0x2000] - - NULL: Literal[0] - OPEN_EXISTING: Literal[3] - - PIPE_ACCESS_DUPLEX: Literal[3] - PIPE_ACCESS_INBOUND: Literal[1] - PIPE_READMODE_MESSAGE: Literal[2] - PIPE_TYPE_MESSAGE: Literal[4] - PIPE_UNLIMITED_INSTANCES: Literal[255] - PIPE_WAIT: Literal[0] - - PAGE_EXECUTE: Literal[0x10] - PAGE_EXECUTE_READ: Literal[0x20] - PAGE_EXECUTE_READWRITE: Literal[0x40] - PAGE_EXECUTE_WRITECOPY: Literal[0x80] - PAGE_GUARD: Literal[0x100] - PAGE_NOACCESS: Literal[0x1] - PAGE_NOCACHE: Literal[0x200] - PAGE_READONLY: Literal[0x2] - PAGE_READWRITE: Literal[0x4] - PAGE_WRITECOMBINE: Literal[0x400] - PAGE_WRITECOPY: Literal[0x8] - - PROCESS_ALL_ACCESS: Literal[0x1FFFFF] - PROCESS_DUP_HANDLE: Literal[0x40] - - SEC_COMMIT: Literal[0x8000000] - SEC_IMAGE: Literal[0x1000000] - SEC_LARGE_PAGES: Literal[0x80000000] - SEC_NOCACHE: Literal[0x10000000] - SEC_RESERVE: Literal[0x4000000] - SEC_WRITECOMBINE: Literal[0x40000000] - - STARTF_USESHOWWINDOW: Literal[0x1] - STARTF_USESTDHANDLES: Literal[0x100] - - STD_ERROR_HANDLE: Literal[0xFFFFFFF4] - STD_OUTPUT_HANDLE: Literal[0xFFFFFFF5] - STD_INPUT_HANDLE: Literal[0xFFFFFFF6] - - STILL_ACTIVE: Literal[259] - SW_HIDE: Literal[0] - SYNCHRONIZE: Literal[0x100000] - WAIT_ABANDONED_0: Literal[128] - WAIT_OBJECT_0: Literal[0] - WAIT_TIMEOUT: Literal[258] + INVALID_HANDLE_VALUE: Final = 0xFFFFFFFFFFFFFFFF # noqa: Y054 + IDLE_PRIORITY_CLASS: Final = 0x40 + NORMAL_PRIORITY_CLASS: Final = 0x20 + REALTIME_PRIORITY_CLASS: Final = 0x100 + NMPWAIT_WAIT_FOREVER: Final = 0xFFFFFFFF + + MEM_COMMIT: Final = 0x1000 + MEM_FREE: Final = 0x10000 + MEM_IMAGE: Final = 0x1000000 + MEM_MAPPED: Final = 0x40000 + MEM_PRIVATE: Final = 0x20000 + MEM_RESERVE: Final = 0x2000 + + NULL: Final = 0 + OPEN_EXISTING: Final = 3 + + PIPE_ACCESS_DUPLEX: Final = 3 + PIPE_ACCESS_INBOUND: Final = 1 + PIPE_READMODE_MESSAGE: Final = 2 + PIPE_TYPE_MESSAGE: Final = 4 + PIPE_UNLIMITED_INSTANCES: Final = 255 + PIPE_WAIT: Final = 0 + + PAGE_EXECUTE: Final = 0x10 + PAGE_EXECUTE_READ: Final = 0x20 + PAGE_EXECUTE_READWRITE: Final = 0x40 + PAGE_EXECUTE_WRITECOPY: Final = 0x80 + PAGE_GUARD: Final = 0x100 + PAGE_NOACCESS: Final = 0x1 + PAGE_NOCACHE: Final = 0x200 + PAGE_READONLY: Final = 0x2 + PAGE_READWRITE: Final = 0x4 + PAGE_WRITECOMBINE: Final = 0x400 + PAGE_WRITECOPY: Final = 0x8 + + PROCESS_ALL_ACCESS: Final = 0x1FFFFF + PROCESS_DUP_HANDLE: Final = 0x40 + + SEC_COMMIT: Final = 0x8000000 + SEC_IMAGE: Final = 0x1000000 + SEC_LARGE_PAGES: Final = 0x80000000 + SEC_NOCACHE: Final = 0x10000000 + SEC_RESERVE: Final = 0x4000000 + SEC_WRITECOMBINE: Final = 0x40000000 + + STARTF_USESHOWWINDOW: Final = 0x1 + STARTF_USESTDHANDLES: Final = 0x100 + + STD_ERROR_HANDLE: Final = 0xFFFFFFF4 + STD_OUTPUT_HANDLE: Final = 0xFFFFFFF5 + STD_INPUT_HANDLE: Final = 0xFFFFFFF6 + + STILL_ACTIVE: Final = 259 + SW_HIDE: Final = 0 + SYNCHRONIZE: Final = 0x100000 + WAIT_ABANDONED_0: Final = 128 + WAIT_OBJECT_0: Final = 0 + WAIT_TIMEOUT: Final = 258 if sys.version_info >= (3, 10): LOCALE_NAME_INVARIANT: str @@ -131,32 +131,32 @@ if sys.platform == "win32": LCMAP_UPPERCASE: int if sys.version_info >= (3, 12): - COPYFILE2_CALLBACK_CHUNK_STARTED: Literal[1] - COPYFILE2_CALLBACK_CHUNK_FINISHED: Literal[2] - COPYFILE2_CALLBACK_STREAM_STARTED: Literal[3] - COPYFILE2_CALLBACK_STREAM_FINISHED: Literal[4] - COPYFILE2_CALLBACK_POLL_CONTINUE: Literal[5] - COPYFILE2_CALLBACK_ERROR: Literal[6] - - COPYFILE2_PROGRESS_CONTINUE: Literal[0] - COPYFILE2_PROGRESS_CANCEL: Literal[1] - COPYFILE2_PROGRESS_STOP: Literal[2] - COPYFILE2_PROGRESS_QUIET: Literal[3] - COPYFILE2_PROGRESS_PAUSE: Literal[4] - - COPY_FILE_FAIL_IF_EXISTS: Literal[0x1] - COPY_FILE_RESTARTABLE: Literal[0x2] - COPY_FILE_OPEN_SOURCE_FOR_WRITE: Literal[0x4] - COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Literal[0x8] - COPY_FILE_COPY_SYMLINK: Literal[0x800] - COPY_FILE_NO_BUFFERING: Literal[0x1000] - COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Literal[0x2000] - COPY_FILE_RESUME_FROM_PAUSE: Literal[0x4000] - COPY_FILE_NO_OFFLOAD: Literal[0x40000] - COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Literal[0x10000000] - - ERROR_ACCESS_DENIED: Literal[5] - ERROR_PRIVILEGE_NOT_HELD: Literal[1314] + COPYFILE2_CALLBACK_CHUNK_STARTED: Final = 1 + COPYFILE2_CALLBACK_CHUNK_FINISHED: Final = 2 + COPYFILE2_CALLBACK_STREAM_STARTED: Final = 3 + COPYFILE2_CALLBACK_STREAM_FINISHED: Final = 4 + COPYFILE2_CALLBACK_POLL_CONTINUE: Final = 5 + COPYFILE2_CALLBACK_ERROR: Final = 6 + + COPYFILE2_PROGRESS_CONTINUE: Final = 0 + COPYFILE2_PROGRESS_CANCEL: Final = 1 + COPYFILE2_PROGRESS_STOP: Final = 2 + COPYFILE2_PROGRESS_QUIET: Final = 3 + COPYFILE2_PROGRESS_PAUSE: Final = 4 + + COPY_FILE_FAIL_IF_EXISTS: Final = 0x1 + COPY_FILE_RESTARTABLE: Final = 0x2 + COPY_FILE_OPEN_SOURCE_FOR_WRITE: Final = 0x4 + COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Final = 0x8 + COPY_FILE_COPY_SYMLINK: Final = 0x800 + COPY_FILE_NO_BUFFERING: Final = 0x1000 + COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Final = 0x2000 + COPY_FILE_RESUME_FROM_PAUSE: Final = 0x4000 + COPY_FILE_NO_OFFLOAD: Final = 0x40000 + COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Final = 0x10000000 + + ERROR_ACCESS_DENIED: Final = 5 + ERROR_PRIVILEGE_NOT_HELD: Final = 1314 def CloseHandle(handle: int, /) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index 6bf7821f1c1b..fdca48ac7aaf 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -28,17 +28,17 @@ class ABCMeta(type): def register(cls: ABCMeta, subclass: type[_T]) -> type[_T]: ... def abstractmethod(funcobj: _FuncT) -> _FuncT: ... -@deprecated("Deprecated, use 'classmethod' with 'abstractmethod' instead") +@deprecated("Use 'classmethod' with 'abstractmethod' instead") class abstractclassmethod(classmethod[_T, _P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... -@deprecated("Deprecated, use 'staticmethod' with 'abstractmethod' instead") +@deprecated("Use 'staticmethod' with 'abstractmethod' instead") class abstractstaticmethod(staticmethod[_P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[_P, _R_co]) -> None: ... -@deprecated("Deprecated, use 'property' with 'abstractmethod' instead") +@deprecated("Use 'property' with 'abstractmethod' instead") class abstractproperty(property): __isabstractmethod__: Literal[True] diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index bc781ec8e61d..d878f3ebd6a2 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern -from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload +from typing import IO, Any, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -43,14 +43,14 @@ _ActionStr: TypeAlias = str # callers that don't use a literal argument _NArgsStr: TypeAlias = str -ONE_OR_MORE: Literal["+"] -OPTIONAL: Literal["?"] -PARSER: Literal["A..."] -REMAINDER: Literal["..."] +ONE_OR_MORE: Final = "+" +OPTIONAL: Final = "?" +PARSER: Final = "A..." +REMAINDER: Final = "..." _SUPPRESS_T = NewType("_SUPPRESS_T", str) SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is # the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy -ZERO_OR_MORE: Literal["*"] +ZERO_OR_MORE: Final = "*" _UNRECOGNIZED_ARGS_ATTR: str # undocumented class ArgumentError(Exception): diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index 112cfeefa8f2..cba2c7799528 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -49,6 +49,10 @@ class Server(AbstractServer): ssl_handshake_timeout: float | None, ) -> None: ... + if sys.version_info >= (3, 13): + def close_clients(self) -> None: ... + def abort_clients(self) -> None: ... + def get_loop(self) -> AbstractEventLoop: ... def is_serving(self) -> bool: ... async def start_serving(self) -> None: ... @@ -222,7 +226,9 @@ class BaseEventLoop(AbstractEventLoop): happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + # 3.13 added `keep_alive`. @overload async def create_server( self, @@ -237,6 +243,7 @@ class BaseEventLoop(AbstractEventLoop): ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -255,30 +262,48 @@ class BaseEventLoop(AbstractEventLoop): ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... - async def start_tls( + elif sys.version_info >= (3, 11): + @overload + async def create_server( self, - transport: BaseTransport, - protocol: BaseProtocol, - sslcontext: ssl.SSLContext, + protocol_factory: _ProtocolFactory, + host: str | Sequence[str] | None = None, + port: int = ..., *, - server_side: bool = False, - server_hostname: str | None = None, + family: int = ..., + flags: int = ..., + sock: None = None, + backlog: int = 100, + ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport | None: ... - async def connect_accepted_socket( + start_serving: bool = True, + ) -> Server: ... + @overload + async def create_server( self, - protocol_factory: Callable[[], _ProtocolT], - sock: socket, + protocol_factory: _ProtocolFactory, + host: None = None, + port: None = None, *, + family: int = ..., + flags: int = ..., + sock: socket = ..., + backlog: int = 100, ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... + start_serving: bool = True, + ) -> Server: ... else: @overload async def create_server( @@ -314,6 +339,29 @@ class BaseEventLoop(AbstractEventLoop): ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + + if sys.version_info >= (3, 11): + async def start_tls( + self, + transport: BaseTransport, + protocol: BaseProtocol, + sslcontext: ssl.SSLContext, + *, + server_side: bool = False, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> Transport | None: ... + async def connect_accepted_socket( + self, + protocol_factory: Callable[[], _ProtocolT], + sock: socket, + *, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> tuple[Transport, _ProtocolT]: ... + else: async def start_tls( self, transport: BaseTransport, diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi index 231766200934..55d2fbdbdb62 100644 --- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Sequence from contextvars import Context -from typing import Any, Literal +from typing import Any, Final from . import futures @@ -11,9 +11,9 @@ __all__ = () # That's why the import order is reversed. from .futures import isfuture as isfuture -_PENDING: Literal["PENDING"] # undocumented -_CANCELLED: Literal["CANCELLED"] # undocumented -_FINISHED: Literal["FINISHED"] # undocumented +_PENDING: Final = "PENDING" # undocumented +_CANCELLED: Final = "CANCELLED" # undocumented +_FINISHED: Final = "FINISHED" # undocumented def _format_callbacks(cb: Sequence[tuple[Callable[[futures.Future[Any]], None], Context]]) -> str: ... # undocumented def _future_repr_info(future: futures.Future[Any]) -> list[str]: ... # undocumented diff --git a/mypy/typeshed/stdlib/asyncio/constants.pyi b/mypy/typeshed/stdlib/asyncio/constants.pyi index 7759a2844953..5c6456b0e9c0 100644 --- a/mypy/typeshed/stdlib/asyncio/constants.pyi +++ b/mypy/typeshed/stdlib/asyncio/constants.pyi @@ -1,18 +1,18 @@ import enum import sys -from typing import Literal +from typing import Final -LOG_THRESHOLD_FOR_CONNLOST_WRITES: Literal[5] -ACCEPT_RETRY_DELAY: Literal[1] -DEBUG_STACK_DEPTH: Literal[10] +LOG_THRESHOLD_FOR_CONNLOST_WRITES: Final = 5 +ACCEPT_RETRY_DELAY: Final = 1 +DEBUG_STACK_DEPTH: Final = 10 SSL_HANDSHAKE_TIMEOUT: float -SENDFILE_FALLBACK_READBUFFER_SIZE: Literal[262144] +SENDFILE_FALLBACK_READBUFFER_SIZE: Final = 262144 if sys.version_info >= (3, 11): SSL_SHUTDOWN_TIMEOUT: float - FLOW_CONTROL_HIGH_WATER_SSL_READ: Literal[256] - FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Literal[512] + FLOW_CONTROL_HIGH_WATER_SSL_READ: Final = 256 + FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Final = 512 if sys.version_info >= (3, 12): - THREAD_JOIN_TIMEOUT: Literal[300] + THREAD_JOIN_TIMEOUT: Final = 300 class _SendfileMode(enum.Enum): UNSUPPORTED = 1 diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index 8c2664666835..eed688fc792a 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -94,6 +94,12 @@ class TimerHandle(Handle): class AbstractServer: @abstractmethod def close(self) -> None: ... + if sys.version_info >= (3, 13): + @abstractmethod + def close_clients(self) -> None: ... + @abstractmethod + def abort_clients(self) -> None: ... + async def __aenter__(self) -> Self: ... async def __aexit__(self, *exc: Unused) -> None: ... @abstractmethod @@ -272,7 +278,9 @@ class AbstractEventLoop: happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + # 3.13 added `keep_alive`. @overload @abstractmethod async def create_server( @@ -288,6 +296,7 @@ class AbstractEventLoop: ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -307,30 +316,46 @@ class AbstractEventLoop: ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + elif sys.version_info >= (3, 11): + @overload @abstractmethod - async def start_tls( + async def create_server( self, - transport: WriteTransport, - protocol: BaseProtocol, - sslcontext: ssl.SSLContext, + protocol_factory: _ProtocolFactory, + host: str | Sequence[str] | None = None, + port: int = ..., *, - server_side: bool = False, - server_hostname: str | None = None, + family: int = ..., + flags: int = ..., + sock: None = None, + backlog: int = 100, + ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport | None: ... - async def create_unix_server( + start_serving: bool = True, + ) -> Server: ... + @overload + @abstractmethod + async def create_server( self, protocol_factory: _ProtocolFactory, - path: StrPath | None = None, + host: None = None, + port: None = None, *, - sock: socket | None = None, + family: int = ..., + flags: int = ..., + sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -372,6 +397,33 @@ class AbstractEventLoop: ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + + if sys.version_info >= (3, 11): + @abstractmethod + async def start_tls( + self, + transport: WriteTransport, + protocol: BaseProtocol, + sslcontext: ssl.SSLContext, + *, + server_side: bool = False, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> Transport | None: ... + async def create_unix_server( + self, + protocol_factory: _ProtocolFactory, + path: StrPath | None = None, + *, + sock: socket | None = None, + backlog: int = 100, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + ) -> Server: ... + else: @abstractmethod async def start_tls( self, @@ -394,6 +446,7 @@ class AbstractEventLoop: ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + if sys.version_info >= (3, 11): async def connect_accepted_socket( self, diff --git a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi index 1c78dff3948a..41505b14cd08 100644 --- a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi +++ b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi @@ -1,4 +1,5 @@ import functools +import sys import traceback from collections.abc import Iterable from types import FrameType, FunctionType @@ -14,7 +15,17 @@ _FuncType: TypeAlias = FunctionType | _HasWrapper | functools.partial[Any] | fun def _get_function_source(func: _FuncType) -> tuple[str, int]: ... @overload def _get_function_source(func: object) -> tuple[str, int] | None: ... -def _format_callback_source(func: object, args: Iterable[Any]) -> str: ... -def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any]) -> str: ... -def _format_callback(func: object, args: Iterable[Any], kwargs: dict[str, Any], suffix: str = "") -> str: ... + +if sys.version_info >= (3, 13): + def _format_callback_source(func: object, args: Iterable[Any], *, debug: bool = False) -> str: ... + def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any], *, debug: bool = False) -> str: ... + def _format_callback( + func: object, args: Iterable[Any], kwargs: dict[str, Any], *, debug: bool = False, suffix: str = "" + ) -> str: ... + +else: + def _format_callback_source(func: object, args: Iterable[Any]) -> str: ... + def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any]) -> str: ... + def _format_callback(func: object, args: Iterable[Any], kwargs: dict[str, Any], suffix: str = "") -> str: ... + def extract_stack(f: FrameType | None = None, limit: int | None = None) -> traceback.StackSummary: ... diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index 1d8f80f4c388..895205aa9519 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -10,13 +10,20 @@ if sys.version_info >= (3, 10): else: _LoopBoundMixin = object -__all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty") - class QueueEmpty(Exception): ... class QueueFull(Exception): ... +if sys.version_info >= (3, 13): + __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty", "QueueShutDown") + +else: + __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty") + _T = TypeVar("_T") +if sys.version_info >= (3, 13): + class QueueShutDown(Exception): ... + # If Generic[_T] is last and _LoopBoundMixin is object, pyright is unhappy. # We can remove the noqa pragma when dropping 3.9 support. class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 @@ -42,6 +49,8 @@ class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 def task_done(self) -> None: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, type: Any, /) -> GenericAlias: ... + if sys.version_info >= (3, 13): + def shutdown(self, immediate: bool = False) -> None: ... class PriorityQueue(Queue[_T]): ... class LifoQueue(Queue[_T]): ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index e904d7395cdc..ded1933dd659 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -3,7 +3,7 @@ import sys from collections import deque from collections.abc import Callable from enum import Enum -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias from . import constants, events, futures, protocols, transports @@ -29,10 +29,10 @@ if sys.version_info >= (3, 11): def add_flowcontrol_defaults(high: int | None, low: int | None, kb: int) -> tuple[int, int]: ... else: - _UNWRAPPED: Literal["UNWRAPPED"] - _DO_HANDSHAKE: Literal["DO_HANDSHAKE"] - _WRAPPED: Literal["WRAPPED"] - _SHUTDOWN: Literal["SHUTDOWN"] + _UNWRAPPED: Final = "UNWRAPPED" + _DO_HANDSHAKE: Final = "DO_HANDSHAKE" + _WRAPPED: Final = "WRAPPED" + _SHUTDOWN: Final = "SHUTDOWN" if sys.version_info < (3, 11): class _SSLPipe: diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index c3cc7b8c9e5a..0be5249e2169 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -2,6 +2,7 @@ import ssl import sys from _typeshed import ReadableBuffer, StrPath from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence, Sized +from types import ModuleType from typing import Any, Protocol, SupportsIndex from typing_extensions import Self, TypeAlias @@ -130,7 +131,10 @@ class StreamWriter: async def start_tls( self, sslcontext: ssl.SSLContext, *, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None ) -> None: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + def __del__(self, warnings: ModuleType = ...) -> None: ... + elif sys.version_info >= (3, 11): def __del__(self) -> None: ... class StreamReader(AsyncIterator[bytes]): diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index 3a2c62646121..5dd3831f9a0a 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -1,15 +1,55 @@ import sys import types +from _typeshed import StrPath from abc import ABCMeta, abstractmethod from collections.abc import Callable +from socket import socket from typing import Literal from typing_extensions import Self, TypeVarTuple, Unpack, deprecated +from .base_events import Server, _ProtocolFactory, _SSLContext from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop _Ts = TypeVarTuple("_Ts") +if sys.platform != "win32": + if sys.version_info >= (3, 14): + __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy", "EventLoop") + elif sys.version_info >= (3, 13): + __all__ = ( + "SelectorEventLoop", + "AbstractChildWatcher", + "SafeChildWatcher", + "FastChildWatcher", + "PidfdChildWatcher", + "MultiLoopChildWatcher", + "ThreadedChildWatcher", + "DefaultEventLoopPolicy", + "EventLoop", + ) + elif sys.version_info >= (3, 9): + __all__ = ( + "SelectorEventLoop", + "AbstractChildWatcher", + "SafeChildWatcher", + "FastChildWatcher", + "PidfdChildWatcher", + "MultiLoopChildWatcher", + "ThreadedChildWatcher", + "DefaultEventLoopPolicy", + ) + else: + __all__ = ( + "SelectorEventLoop", + "AbstractChildWatcher", + "SafeChildWatcher", + "FastChildWatcher", + "MultiLoopChildWatcher", + "ThreadedChildWatcher", + "DefaultEventLoopPolicy", + ) + # This is also technically not available on Win, # but other parts of typeshed need this definition. # So, it is special cased. @@ -58,30 +98,6 @@ if sys.version_info < (3, 14): def is_active(self) -> bool: ... if sys.platform != "win32": - if sys.version_info >= (3, 14): - __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy") - elif sys.version_info >= (3, 9): - __all__ = ( - "SelectorEventLoop", - "AbstractChildWatcher", - "SafeChildWatcher", - "FastChildWatcher", - "PidfdChildWatcher", - "MultiLoopChildWatcher", - "ThreadedChildWatcher", - "DefaultEventLoopPolicy", - ) - else: - __all__ = ( - "SelectorEventLoop", - "AbstractChildWatcher", - "SafeChildWatcher", - "FastChildWatcher", - "MultiLoopChildWatcher", - "ThreadedChildWatcher", - "DefaultEventLoopPolicy", - ) - if sys.version_info < (3, 14): if sys.version_info >= (3, 12): # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. @@ -141,7 +157,21 @@ if sys.platform != "win32": ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... - class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... + class _UnixSelectorEventLoop(BaseSelectorEventLoop): + if sys.version_info >= (3, 13): + async def create_unix_server( # type: ignore[override] + self, + protocol_factory: _ProtocolFactory, + path: StrPath | None = None, + *, + sock: socket | None = None, + backlog: int = 100, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + cleanup_socket: bool = True, + ) -> Server: ... class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): if sys.version_info < (3, 14): @@ -158,6 +188,9 @@ if sys.platform != "win32": DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy + if sys.version_info >= (3, 13): + EventLoop = SelectorEventLoop + if sys.version_info < (3, 14): if sys.version_info >= (3, 12): @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 97aa52ff8b9a..e5205ba4dcb0 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -2,24 +2,36 @@ import socket import sys from _typeshed import Incomplete, ReadableBuffer, WriteableBuffer from collections.abc import Callable -from typing import IO, Any, ClassVar, Literal, NoReturn +from typing import IO, Any, ClassVar, Final, NoReturn from . import events, futures, proactor_events, selector_events, streams, windows_utils if sys.platform == "win32": - __all__ = ( - "SelectorEventLoop", - "ProactorEventLoop", - "IocpProactor", - "DefaultEventLoopPolicy", - "WindowsSelectorEventLoopPolicy", - "WindowsProactorEventLoopPolicy", - ) + if sys.version_info >= (3, 13): + # 3.13 added `EventLoop`. + __all__ = ( + "SelectorEventLoop", + "ProactorEventLoop", + "IocpProactor", + "DefaultEventLoopPolicy", + "WindowsSelectorEventLoopPolicy", + "WindowsProactorEventLoopPolicy", + "EventLoop", + ) + else: + __all__ = ( + "SelectorEventLoop", + "ProactorEventLoop", + "IocpProactor", + "DefaultEventLoopPolicy", + "WindowsSelectorEventLoopPolicy", + "WindowsProactorEventLoopPolicy", + ) - NULL: Literal[0] - INFINITE: Literal[0xFFFFFFFF] - ERROR_CONNECTION_REFUSED: Literal[1225] - ERROR_CONNECTION_ABORTED: Literal[1236] + NULL: Final = 0 + INFINITE: Final = 0xFFFFFFFF + ERROR_CONNECTION_REFUSED: Final = 1225 + ERROR_CONNECTION_ABORTED: Final = 1236 CONNECT_PIPE_INIT_DELAY: float CONNECT_PIPE_MAX_DELAY: float @@ -84,3 +96,5 @@ if sys.platform == "win32": def set_child_watcher(self, watcher: Any) -> NoReturn: ... DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy + if sys.version_info >= (3, 13): + EventLoop = ProactorEventLoop diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi index 6b3589adc3cb..4fa014532376 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -2,13 +2,13 @@ import subprocess import sys from collections.abc import Callable from types import TracebackType -from typing import Any, AnyStr, Literal +from typing import Any, AnyStr, Final from typing_extensions import Self if sys.platform == "win32": __all__ = ("pipe", "Popen", "PIPE", "PipeHandle") - BUFSIZE: Literal[8192] + BUFSIZE: Final = 8192 PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT def pipe(*, duplex: bool = False, overlapped: tuple[bool, bool] = (True, True), bufsize: int = 8192) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index a72e986728a7..75bfa91cc379 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import ExcInfo, TraceFunction +from _typeshed import ExcInfo, TraceFunction, Unused from collections.abc import Callable, Iterable, Mapping from types import CodeType, FrameType, TracebackType -from typing import IO, Any, Literal, SupportsInt, TypeVar +from typing import IO, Any, Final, SupportsInt, TypeVar from typing_extensions import ParamSpec __all__ = ["BdbQuit", "Bdb", "Breakpoint"] @@ -10,7 +10,10 @@ __all__ = ["BdbQuit", "Bdb", "Breakpoint"] _T = TypeVar("_T") _P = ParamSpec("_P") -GENERATOR_AND_COROUTINE_FLAGS: Literal[672] +# A union of code-object flags at runtime. +# The exact values of code-object flags are implementation details, +# so we don't include the value of this constant in the stubs. +GENERATOR_AND_COROUTINE_FLAGS: Final[int] class BdbQuit(Exception): ... @@ -32,6 +35,9 @@ class Bdb: def dispatch_call(self, frame: FrameType, arg: None) -> TraceFunction: ... def dispatch_return(self, frame: FrameType, arg: Any) -> TraceFunction: ... def dispatch_exception(self, frame: FrameType, arg: ExcInfo) -> TraceFunction: ... + if sys.version_info >= (3, 13): + def dispatch_opcode(self, frame: FrameType, arg: Unused) -> Callable[[FrameType, str, Any], TraceFunction]: ... + def is_skipped_module(self, module_name: str) -> bool: ... def stop_here(self, frame: FrameType) -> bool: ... def break_here(self, frame: FrameType) -> bool: ... @@ -42,7 +48,13 @@ class Bdb: def user_return(self, frame: FrameType, return_value: Any) -> None: ... def user_exception(self, frame: FrameType, exc_info: ExcInfo) -> None: ... def set_until(self, frame: FrameType, lineno: int | None = None) -> None: ... + if sys.version_info >= (3, 13): + def user_opcode(self, frame: FrameType) -> None: ... # undocumented + def set_step(self) -> None: ... + if sys.version_info >= (3, 13): + def set_stepinstr(self) -> None: ... # undocumented + def set_next(self, frame: FrameType) -> None: ... def set_return(self, frame: FrameType) -> None: ... def set_trace(self, frame: FrameType | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/binhex.pyi b/mypy/typeshed/stdlib/binhex.pyi index d514be3b9b26..bdead928468f 100644 --- a/mypy/typeshed/stdlib/binhex.pyi +++ b/mypy/typeshed/stdlib/binhex.pyi @@ -1,14 +1,14 @@ from _typeshed import SizedBuffer -from typing import IO, Any, Literal +from typing import IO, Any, Final from typing_extensions import TypeAlias __all__ = ["binhex", "hexbin", "Error"] class Error(Exception): ... -REASONABLY_LARGE: Literal[32768] -LINELEN: Literal[64] -RUNCHAR: Literal[b"\x90"] +REASONABLY_LARGE: Final = 32768 +LINELEN: Final = 64 +RUNCHAR: Final = b"\x90" class FInfo: Type: str diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 28b0b11a8e5c..07d2f1989558 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -74,6 +74,7 @@ if sys.version_info >= (3, 9): from types import GenericAlias _T = TypeVar("_T") +_I = TypeVar("_I", default=int) _T_co = TypeVar("_T_co", covariant=True) _T_contra = TypeVar("_T_contra", contravariant=True) _R_co = TypeVar("_R_co", covariant=True) @@ -728,8 +729,12 @@ class bytearray(MutableSequence[int]): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... +_IntegerFormats: TypeAlias = Literal[ + "b", "B", "@b", "@B", "h", "H", "@h", "@H", "i", "I", "@i", "@I", "l", "L", "@l", "@L", "q", "Q", "@q", "@Q", "P", "@P" +] + @final -class memoryview(Sequence[int]): +class memoryview(Sequence[_I]): @property def format(self) -> str: ... @property @@ -759,13 +764,20 @@ class memoryview(Sequence[int]): def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... - def cast(self, format: str, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... @overload - def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> int: ... + def cast(self, format: Literal["c", "@c"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bytes]: ... + @overload + def cast(self, format: Literal["f", "@f", "d", "@d"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[float]: ... + @overload + def cast(self, format: Literal["?"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bool]: ... @overload - def __getitem__(self, key: slice, /) -> memoryview: ... + def cast(self, format: _IntegerFormats, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... + @overload + def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> _I: ... + @overload + def __getitem__(self, key: slice, /) -> memoryview[_I]: ... def __contains__(self, x: object, /) -> bool: ... - def __iter__(self) -> Iterator[int]: ... + def __iter__(self) -> Iterator[_I]: ... def __len__(self) -> int: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @@ -1761,6 +1773,7 @@ class BaseException: __suppress_context__: bool __traceback__: TracebackType | None def __init__(self, *args: object) -> None: ... + def __new__(cls, *args: Any, **kwds: Any) -> Self: ... def __setstate__(self, state: dict[str, Any] | None, /) -> None: ... def with_traceback(self, tb: TracebackType | None, /) -> Self: ... if sys.version_info >= (3, 11): @@ -1911,9 +1924,9 @@ if sys.version_info >= (3, 10): class EncodingWarning(Warning): ... if sys.version_info >= (3, 11): - _BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True) + _BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True, default=BaseException) _BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException) - _ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True) + _ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True, default=Exception) _ExceptionT = TypeVar("_ExceptionT", bound=Exception) # See `check_exception_group.py` for use-cases and comments. @@ -1977,5 +1990,4 @@ if sys.version_info >= (3, 11): ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ... if sys.version_info >= (3, 13): - class IncompleteInputError(SyntaxError): ... class PythonFinalizationError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/cmd.pyi b/mypy/typeshed/stdlib/cmd.pyi index 9499847fb153..0733857433be 100644 --- a/mypy/typeshed/stdlib/cmd.pyi +++ b/mypy/typeshed/stdlib/cmd.pyi @@ -1,9 +1,9 @@ from collections.abc import Callable -from typing import IO, Any, Literal +from typing import IO, Any, Final __all__ = ["Cmd"] -PROMPT: Literal["(Cmd) "] +PROMPT: Final = "(Cmd) " IDENTCHARS: str # Too big to be `Literal` class Cmd: diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 6e53b780c473..9bc098dbc6d7 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -3,7 +3,7 @@ from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, Literal, Protocol, TextIO +from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO from typing_extensions import Self __all__ = [ @@ -53,10 +53,10 @@ __all__ = [ "lookup_error", ] -BOM32_BE: Literal[b"\xfe\xff"] -BOM32_LE: Literal[b"\xff\xfe"] -BOM64_BE: Literal[b"\x00\x00\xfe\xff"] -BOM64_LE: Literal[b"\xff\xfe\x00\x00"] +BOM32_BE: Final = b"\xfe\xff" +BOM32_LE: Final = b"\xff\xfe" +BOM64_BE: Final = b"\x00\x00\xfe\xff" +BOM64_LE: Final = b"\xff\xfe\x00\x00" class _WritableStream(Protocol): def write(self, data: bytes, /) -> object: ... @@ -135,23 +135,23 @@ def EncodedFile(file: _Stream, data_encoding: str, file_encoding: str | None = N def iterencode(iterator: Iterable[str], encoding: str, errors: str = "strict") -> Generator[bytes, None, None]: ... def iterdecode(iterator: Iterable[bytes], encoding: str, errors: str = "strict") -> Generator[str, None, None]: ... -BOM: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder` -BOM_BE: Literal[b"\xfe\xff"] -BOM_LE: Literal[b"\xff\xfe"] -BOM_UTF8: Literal[b"\xef\xbb\xbf"] -BOM_UTF16: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder` -BOM_UTF16_BE: Literal[b"\xfe\xff"] -BOM_UTF16_LE: Literal[b"\xff\xfe"] -BOM_UTF32: Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"] # depends on `sys.byteorder` -BOM_UTF32_BE: Literal[b"\x00\x00\xfe\xff"] -BOM_UTF32_LE: Literal[b"\xff\xfe\x00\x00"] +BOM: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder` +BOM_BE: Final = b"\xfe\xff" +BOM_LE: Final = b"\xff\xfe" +BOM_UTF8: Final = b"\xef\xbb\xbf" +BOM_UTF16: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder` +BOM_UTF16_BE: Final = b"\xfe\xff" +BOM_UTF16_LE: Final = b"\xff\xfe" +BOM_UTF32: Final[Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"]] # depends on `sys.byteorder` +BOM_UTF32_BE: Final = b"\x00\x00\xfe\xff" +BOM_UTF32_LE: Final = b"\xff\xfe\x00\x00" -def strict_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def replace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def ignore_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def xmlcharrefreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def backslashreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def namereplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... +def strict_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def replace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def ignore_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def xmlcharrefreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def backslashreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def namereplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... class Codec: # These are sort of @abstractmethod but sort of not. diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi index 07314ce9d402..68fd0bc5acb4 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi @@ -1,3 +1,5 @@ +import sys + from ._base import ( ALL_COMPLETED as ALL_COMPLETED, FIRST_COMPLETED as FIRST_COMPLETED, @@ -14,19 +16,36 @@ from ._base import ( from .process import ProcessPoolExecutor as ProcessPoolExecutor from .thread import ThreadPoolExecutor as ThreadPoolExecutor -__all__ = ( - "FIRST_COMPLETED", - "FIRST_EXCEPTION", - "ALL_COMPLETED", - "CancelledError", - "TimeoutError", - "BrokenExecutor", - "Future", - "Executor", - "wait", - "as_completed", - "ProcessPoolExecutor", - "ThreadPoolExecutor", -) +if sys.version_info >= (3, 13): + __all__ = ( + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "CancelledError", + "TimeoutError", + "InvalidStateError", + "BrokenExecutor", + "Future", + "Executor", + "wait", + "as_completed", + "ProcessPoolExecutor", + "ThreadPoolExecutor", + ) +else: + __all__ = ( + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "CancelledError", + "TimeoutError", + "BrokenExecutor", + "Future", + "Executor", + "wait", + "as_completed", + "ProcessPoolExecutor", + "ThreadPoolExecutor", + ) def __dir__() -> tuple[str, ...]: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 3d5eccfc048d..0c019457902b 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -4,20 +4,20 @@ from _typeshed import Unused from collections.abc import Callable, Collection, Iterable, Iterator from logging import Logger from types import TracebackType -from typing import Any, Generic, Literal, NamedTuple, Protocol, TypeVar +from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar from typing_extensions import ParamSpec, Self if sys.version_info >= (3, 9): from types import GenericAlias -FIRST_COMPLETED: Literal["FIRST_COMPLETED"] -FIRST_EXCEPTION: Literal["FIRST_EXCEPTION"] -ALL_COMPLETED: Literal["ALL_COMPLETED"] -PENDING: Literal["PENDING"] -RUNNING: Literal["RUNNING"] -CANCELLED: Literal["CANCELLED"] -CANCELLED_AND_NOTIFIED: Literal["CANCELLED_AND_NOTIFIED"] -FINISHED: Literal["FINISHED"] +FIRST_COMPLETED: Final = "FIRST_COMPLETED" +FIRST_EXCEPTION: Final = "FIRST_EXCEPTION" +ALL_COMPLETED: Final = "ALL_COMPLETED" +PENDING: Final = "PENDING" +RUNNING: Final = "RUNNING" +CANCELLED: Final = "CANCELLED" +CANCELLED_AND_NOTIFIED: Final = "CANCELLED_AND_NOTIFIED" +FINISHED: Final = "FINISHED" _FUTURE_STATES: list[str] _STATE_TO_DESCRIPTION_MAP: dict[str, str] LOGGER: Logger diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index f38bb1de674d..ee5000196e0e 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import StrOrBytesPath, SupportsWrite from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence from re import Pattern -from typing import Any, ClassVar, Literal, TypeVar, overload +from typing import Any, ClassVar, Final, Literal, TypeVar, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 13): @@ -83,8 +83,8 @@ _ConverterCallback: TypeAlias = Callable[[str], Any] _ConvertersMap: TypeAlias = dict[str, _ConverterCallback] _T = TypeVar("_T") -DEFAULTSECT: Literal["DEFAULT"] -MAX_INTERPOLATION_DEPTH: Literal[10] +DEFAULTSECT: Final = "DEFAULT" +MAX_INTERPOLATION_DEPTH: Final = 10 class Interpolation: def before_get(self, parser: _Parser, section: str, option: str, value: str, defaults: _Section) -> str: ... diff --git a/mypy/typeshed/stdlib/copy.pyi b/mypy/typeshed/stdlib/copy.pyi index 8a2dcc508e5d..020ce6c31b58 100644 --- a/mypy/typeshed/stdlib/copy.pyi +++ b/mypy/typeshed/stdlib/copy.pyi @@ -1,8 +1,16 @@ -from typing import Any, TypeVar +import sys +from typing import Any, Protocol, TypeVar +from typing_extensions import ParamSpec, Self __all__ = ["Error", "copy", "deepcopy"] _T = TypeVar("_T") +_SR = TypeVar("_SR", bound=_SupportsReplace[Any]) +_P = ParamSpec("_P") + +class _SupportsReplace(Protocol[_P]): + # In reality doesn't support args, but there's no other great way to express this. + def __replace__(self, *args: _P.args, **kwargs: _P.kwargs) -> Self: ... # None in CPython but non-None in Jython PyStringMap: Any @@ -11,6 +19,10 @@ PyStringMap: Any def deepcopy(x: _T, memo: dict[int, Any] | None = None, _nil: Any = []) -> _T: ... def copy(x: _T) -> _T: ... +if sys.version_info >= (3, 13): + __all__ += ["replace"] + def replace(obj: _SR, /, **changes: Any) -> _SR: ... + class Error(Exception): ... error = Error diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 71522a59d4df..38d5ac4c0819 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,7 +1,7 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, final, overload +from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): @@ -9,8 +9,8 @@ if sys.version_info >= (3, 11): elif sys.version_info >= (3, 9): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") -MINYEAR: Literal[1] -MAXYEAR: Literal[9999] +MINYEAR: Final = 1 +MAXYEAR: Final = 9999 class tzinfo: @abstractmethod diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index e80441cbb25b..1d1d541f5477 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -19,6 +19,9 @@ if sys.platform != "win32": def reorganize(self) -> None: ... def sync(self) -> None: ... def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + def __getitem__(self, item: _KeyType) -> bytes: ... def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... def __delitem__(self, key: _KeyType) -> None: ... diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 02bf23ec181c..4113a7e3ffb9 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -15,6 +15,9 @@ if sys.platform != "win32": # Actual typename dbm, not exposed by the implementation class _dbm: def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + def __getitem__(self, item: _KeyType) -> bytes: ... def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... def __delitem__(self, key: _KeyType) -> None: ... diff --git a/mypy/typeshed/stdlib/dbm/sqlite3.pyi b/mypy/typeshed/stdlib/dbm/sqlite3.pyi new file mode 100644 index 000000000000..446a0cf155fa --- /dev/null +++ b/mypy/typeshed/stdlib/dbm/sqlite3.pyi @@ -0,0 +1,29 @@ +from _typeshed import ReadableBuffer, StrOrBytesPath, Unused +from collections.abc import Generator, MutableMapping +from typing import Final, Literal +from typing_extensions import LiteralString, Self, TypeAlias + +BUILD_TABLE: Final[LiteralString] +GET_SIZE: Final[LiteralString] +LOOKUP_KEY: Final[LiteralString] +STORE_KV: Final[LiteralString] +DELETE_KEY: Final[LiteralString] +ITER_KEYS: Final[LiteralString] + +_SqliteData: TypeAlias = str | ReadableBuffer | int | float + +class error(OSError): ... + +class _Database(MutableMapping[bytes, bytes]): + def __init__(self, path: StrOrBytesPath, /, *, flag: Literal["r", "w", "c", "n"], mode: int) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: _SqliteData) -> bytes: ... + def __setitem__(self, key: _SqliteData, value: _SqliteData) -> None: ... + def __delitem__(self, key: _SqliteData) -> None: ... + def __iter__(self) -> Generator[bytes]: ... + def close(self) -> None: ... + def keys(self) -> list[bytes]: ... # type: ignore[override] + def __enter__(self) -> Self: ... + def __exit__(self, *args: Unused) -> None: ... + +def open(filename: StrOrBytesPath, /, flag: Literal["r", "w,", "c", "n"] = "r", mode: int = 0o666) -> _Database: ... diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index 47c63cc8b3d3..cb69eac89c92 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -31,6 +31,9 @@ __all__ = [ "EXTENDED_ARG", "stack_effect", ] +if sys.version_info >= (3, 13): + __all__ += ["hasjump"] + if sys.version_info >= (3, 12): __all__ += ["hasarg", "hasexc"] else: @@ -86,12 +89,41 @@ else: is_jump_target: bool class Instruction(_Instruction): - def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ... + if sys.version_info < (3, 13): + def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ... + if sys.version_info >= (3, 13): + @property + def oparg(self) -> int: ... + @property + def baseopcode(self) -> int: ... + @property + def baseopname(self) -> str: ... + @property + def cache_offset(self) -> int: ... + @property + def end_offset(self) -> int: ... + @property + def jump_target(self) -> int: ... + @property + def is_jump_target(self) -> bool: ... class Bytecode: codeobj: types.CodeType first_line: int - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + show_offsets: bool + # 3.13 added `show_offsets` + def __init__( + self, + x: _HaveCodeType | str, + *, + first_line: int | None = None, + current_offset: int | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): def __init__( self, x: _HaveCodeType | str, @@ -101,12 +133,15 @@ class Bytecode: show_caches: bool = False, adaptive: bool = False, ) -> None: ... - @classmethod - def from_traceback(cls, tb: types.TracebackType, *, show_caches: bool = False, adaptive: bool = False) -> Self: ... else: def __init__( self, x: _HaveCodeType | str, *, first_line: int | None = None, current_offset: int | None = None ) -> None: ... + + if sys.version_info >= (3, 11): + @classmethod + def from_traceback(cls, tb: types.TracebackType, *, show_caches: bool = False, adaptive: bool = False) -> Self: ... + else: @classmethod def from_traceback(cls, tb: types.TracebackType) -> Self: ... @@ -121,7 +156,8 @@ def findlinestarts(code: _HaveCodeType) -> Iterator[tuple[int, int]]: ... def pretty_flags(flags: int) -> str: ... def code_info(x: _HaveCodeType | str) -> str: ... -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + # 3.13 added `show_offsets` def dis( x: _HaveCodeType | str | bytes | bytearray | None = None, *, @@ -129,20 +165,43 @@ if sys.version_info >= (3, 11): depth: int | None = None, show_caches: bool = False, adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + def disassemble( + co: _HaveCodeType, + lasti: int = -1, + *, + file: IO[str] | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + def distb( + tb: types.TracebackType | None = None, + *, + file: IO[str] | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, ) -> None: ... + # 3.13 made `show_cache` `None` by default + def get_instructions( + x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool | None = None, adaptive: bool = False + ) -> Iterator[Instruction]: ... -else: +elif sys.version_info >= (3, 11): + # 3.11 added `show_caches` and `adaptive` def dis( - x: _HaveCodeType | str | bytes | bytearray | None = None, *, file: IO[str] | None = None, depth: int | None = None + x: _HaveCodeType | str | bytes | bytearray | None = None, + *, + file: IO[str] | None = None, + depth: int | None = None, + show_caches: bool = False, + adaptive: bool = False, ) -> None: ... - -if sys.version_info >= (3, 11): def disassemble( co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False ) -> None: ... - def disco( - co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False - ) -> None: ... def distb( tb: types.TracebackType | None = None, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False ) -> None: ... @@ -151,9 +210,13 @@ if sys.version_info >= (3, 11): ) -> Iterator[Instruction]: ... else: + def dis( + x: _HaveCodeType | str | bytes | bytearray | None = None, *, file: IO[str] | None = None, depth: int | None = None + ) -> None: ... def disassemble(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ... - def disco(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ... def distb(tb: types.TracebackType | None = None, *, file: IO[str] | None = None) -> None: ... def get_instructions(x: _HaveCodeType, *, first_line: int | None = None) -> Iterator[Instruction]: ... def show_code(co: _HaveCodeType, *, file: IO[str] | None = None) -> None: ... + +disco = disassemble diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index cd6efee0a210..e0f33f430e5a 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,10 +1,11 @@ -from _typeshed import BytesPath, StrPath +from _typeshed import BytesPath, StrPath, Unused from collections.abc import Callable, Iterable from distutils.file_util import _BytesPathT, _StrPathT -from typing import Any, Literal, overload -from typing_extensions import TypeAlias +from typing import Literal, overload +from typing_extensions import TypeAlias, TypeVarTuple, Unpack _Macro: TypeAlias = tuple[str] | tuple[str, str | None] +_Ts = TypeVarTuple("_Ts") def gen_lib_options( compiler: CCompiler, library_dirs: list[str], runtime_library_dirs: list[str], libraries: list[str] @@ -161,7 +162,9 @@ class CCompiler: def shared_object_filename(self, basename: str, strip_dir: Literal[0, False] = 0, output_dir: StrPath = "") -> str: ... @overload def shared_object_filename(self, basename: StrPath, strip_dir: Literal[1, True], output_dir: StrPath = "") -> str: ... - def execute(self, func: Callable[..., object], args: tuple[Any, ...], msg: str | None = None, level: int = 1) -> None: ... + def execute( + self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 + ) -> None: ... def spawn(self, cmd: list[str]) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index defea50e78dc..ca4fb3265324 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -3,7 +3,11 @@ from abc import abstractmethod from collections.abc import Callable, Iterable from distutils.dist import Distribution from distutils.file_util import _BytesPathT, _StrPathT -from typing import Any, ClassVar, Literal, overload +from typing import Any, ClassVar, Literal, TypeVar, overload +from typing_extensions import TypeVarTuple, Unpack + +_CommandT = TypeVar("_CommandT", bound=Command) +_Ts = TypeVarTuple("_Ts") class Command: distribution: Distribution @@ -19,17 +23,22 @@ class Command: def announce(self, msg: str, level: int = 1) -> None: ... def debug_print(self, msg: str) -> None: ... def ensure_string(self, option: str, default: str | None = None) -> None: ... - def ensure_string_list(self, option: str | list[str]) -> None: ... + def ensure_string_list(self, option: str) -> None: ... def ensure_filename(self, option: str) -> None: ... def ensure_dirname(self, option: str) -> None: ... def get_command_name(self) -> str: ... def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ... def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ... - def reinitialize_command(self, command: Command | str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... + @overload + def reinitialize_command(self, command: str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... + @overload + def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool | Literal[0, 1] = 0) -> _CommandT: ... def run_command(self, command: str) -> None: ... def get_sub_commands(self) -> list[str]: ... def warn(self, msg: str) -> None: ... - def execute(self, func: Callable[..., object], args: Iterable[Any], msg: str | None = None, level: int = 1) -> None: ... + def execute( + self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 + ) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... @overload def copy_file( @@ -89,8 +98,8 @@ class Command: self, infiles: str | list[str] | tuple[str, ...], outfile: StrOrBytesPath, - func: Callable[..., object], - args: list[Any], + func: Callable[[Unpack[_Ts]], Unused], + args: tuple[Unpack[_Ts]], exec_msg: str | None = None, skip_msg: str | None = None, level: Unused = 1, diff --git a/mypy/typeshed/stdlib/distutils/command/bdist.pyi b/mypy/typeshed/stdlib/distutils/command/bdist.pyi index e1f141d3a40f..43d77087f7d8 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist.pyi @@ -1,4 +1,6 @@ -from typing import Any +from _typeshed import Unused +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -6,13 +8,13 @@ def show_formats() -> None: ... class bdist(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any - no_format_option: Any - default_format: Any - format_commands: Any - format_command: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + no_format_option: ClassVar[tuple[str, ...]] + default_format: ClassVar[dict[str, str]] + format_commands: ClassVar[list[str]] + format_command: ClassVar[dict[str, tuple[str, str]]] bdist_base: Any plat_name: Any formats: Any diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi index 74cca4d13cd0..19997882dd53 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi @@ -1,12 +1,12 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class bdist_dumb(Command): description: str - user_options: Any - boolean_options: Any - default_format: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + default_format: ClassVar[dict[str, str]] bdist_dir: Any plat_name: Any format: Any diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi index d1eb374ff52b..d0eac1a3be5b 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi @@ -1,5 +1,5 @@ import sys -from typing import Any, Literal +from typing import Any, ClassVar, Literal from ..cmd import Command @@ -16,8 +16,8 @@ if sys.platform == "win32": class bdist_msi(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] all_versions: Any other_version: str if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi index 76691310b599..89c43e1b974c 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi @@ -1,12 +1,12 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class bdist_rpm(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] bdist_base: Any rpm_base: Any dist_dir: Any diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi index 8491d3126200..cf333bc5400d 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi @@ -1,10 +1,10 @@ from _typeshed import StrOrBytesPath from distutils.cmd import Command -from typing import Any, ClassVar +from typing import ClassVar class bdist_wininst(Command): description: ClassVar[str] - user_options: ClassVar[list[tuple[Any, ...]]] + user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] def initialize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build.pyi b/mypy/typeshed/stdlib/distutils/command/build.pyi index 31fc036d4f97..78ba6b7042dc 100644 --- a/mypy/typeshed/stdlib/distutils/command/build.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build.pyi @@ -1,3 +1,4 @@ +from _typeshed import Unused from collections.abc import Callable from typing import Any, ClassVar @@ -7,9 +8,9 @@ def show_compilers() -> None: ... class build(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] build_base: str build_purelib: Any build_platlib: Any diff --git a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi index 32ab182b30d0..1f66e2efc20c 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi @@ -1,4 +1,6 @@ -from typing import Any +from _typeshed import Unused +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -6,9 +8,9 @@ def show_compilers() -> None: ... class build_clib(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] build_clib: Any build_temp: Any libraries: Any diff --git a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi index 5eb541fb9101..a0813c314021 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi @@ -1,4 +1,6 @@ -from typing import Any +from _typeshed import Unused +from collections.abc import Callable +from typing import Any, ClassVar from ..cmd import Command @@ -9,9 +11,9 @@ def show_compilers() -> None: ... class build_ext(Command): description: str sep_by: Any - user_options: Any - boolean_options: Any - help_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] extensions: Any build_lib: Any plat_name: Any diff --git a/mypy/typeshed/stdlib/distutils/command/build_py.pyi b/mypy/typeshed/stdlib/distutils/command/build_py.pyi index 4c607c6dabe9..90f06751416a 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_py.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_py.pyi @@ -1,13 +1,13 @@ -from typing import Any, Literal +from typing import Any, ClassVar, Literal from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 class build_py(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] build_lib: Any py_modules: Any package: Any diff --git a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi index 42135eceafef..7871bb8a5719 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 @@ -7,8 +7,8 @@ first_line_re: Any class build_scripts(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] build_dir: Any scripts: Any force: Any diff --git a/mypy/typeshed/stdlib/distutils/command/check.pyi b/mypy/typeshed/stdlib/distutils/command/check.pyi index da041d82587d..c67e4cbfdfe0 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Any, ClassVar, Literal from typing_extensions import TypeAlias from ..cmd import Command @@ -26,8 +26,8 @@ HAS_DOCUTILS: bool class check(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] restructuredtext: int metadata: int strict: int diff --git a/mypy/typeshed/stdlib/distutils/command/clean.pyi b/mypy/typeshed/stdlib/distutils/command/clean.pyi index 99560aa8a716..55f0a0eeaf10 100644 --- a/mypy/typeshed/stdlib/distutils/command/clean.pyi +++ b/mypy/typeshed/stdlib/distutils/command/clean.pyi @@ -1,11 +1,11 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class clean(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] build_base: Any build_lib: Any build_temp: Any diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi index 391f5a862038..2f528c2c290b 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,7 +1,7 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any, Literal +from typing import Any, ClassVar, Literal from ..ccompiler import CCompiler from ..cmd import Command @@ -11,7 +11,7 @@ LANG_EXT: dict[str, str] class config(Command): description: str # Tuple is full name, short name, description - user_options: Sequence[tuple[str, str | None, str]] + user_options: ClassVar[list[tuple[str, str | None, str]]] compiler: str | CCompiler cc: str | None include_dirs: Sequence[str] | None diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index 8b2295d7a3c7..b0a5a82fc3f6 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -9,9 +9,9 @@ INSTALL_SCHEMES: dict[str, dict[Any, Any]] class install(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] prefix: str | None exec_prefix: Any home: str | None diff --git a/mypy/typeshed/stdlib/distutils/command/install_data.pyi b/mypy/typeshed/stdlib/distutils/command/install_data.pyi index 6cc9b528ac9d..342c7a7ccca4 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_data.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_data.pyi @@ -1,11 +1,11 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class install_data(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] install_dir: Any outfiles: Any root: Any diff --git a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi index 776eafc1de09..3fd54989d14f 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi @@ -4,7 +4,7 @@ from ..cmd import Command class install_egg_info(Command): description: ClassVar[str] - user_options: ClassVar[list[tuple[str, str | None, str]]] + user_options: ClassVar[list[tuple[str, str, str]]] install_dir: Any def initialize_options(self) -> None: ... target: Any diff --git a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi index 795bd1cf8356..7854d2393a98 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi @@ -1,11 +1,11 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class install_headers(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] install_dir: Any force: int outfiles: Any diff --git a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi index a6a5e4e73f4c..718d082b7b07 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command @@ -6,9 +6,9 @@ PYTHON_SOURCE_EXTENSION: str class install_lib(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] install_dir: Any build_dir: Any force: int diff --git a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi index 92728a16a747..5ee5589ad33d 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi @@ -1,11 +1,11 @@ -from typing import Any +from typing import Any, ClassVar from ..cmd import Command class install_scripts(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] install_dir: Any force: int build_dir: Any diff --git a/mypy/typeshed/stdlib/distutils/command/sdist.pyi b/mypy/typeshed/stdlib/distutils/command/sdist.pyi index db303f77a463..5b7fe2419551 100644 --- a/mypy/typeshed/stdlib/distutils/command/sdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/sdist.pyi @@ -1,3 +1,4 @@ +from _typeshed import Unused from collections.abc import Callable from typing import Any, ClassVar @@ -8,13 +9,13 @@ def show_formats() -> None: ... class sdist(Command): description: str def checking_metadata(self): ... - user_options: Any - boolean_options: Any - help_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + negative_opt: ClassVar[dict[str, str]] # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] - READMES: Any + READMES: ClassVar[tuple[str, ...]] template: Any manifest: Any use_defaults: int diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 4094df903325..21ddbc425918 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -1,8 +1,8 @@ from _typeshed import Incomplete, StrOrBytesPath, StrPath, SupportsWrite -from collections.abc import Iterable, Mapping +from collections.abc import Iterable, MutableMapping from distutils.cmd import Command from re import Pattern -from typing import IO, Any, ClassVar, Literal, TypeVar, overload +from typing import IO, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias command_re: Pattern[str] @@ -60,7 +60,7 @@ class DistributionMetadata: class Distribution: cmdclass: dict[str, type[Command]] metadata: DistributionMetadata - def __init__(self, attrs: Mapping[str, Any] | None = None) -> None: ... + def __init__(self, attrs: MutableMapping[str, Incomplete] | None = None) -> None: ... def get_option_dict(self, command: str) -> dict[str, tuple[str, str]]: ... def parse_config_files(self, filenames: Iterable[str] | None = None) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/distutils/util.pyi b/mypy/typeshed/stdlib/distutils/util.pyi index 515b5b2b86d9..0e1bb4165d99 100644 --- a/mypy/typeshed/stdlib/distutils/util.pyi +++ b/mypy/typeshed/stdlib/distutils/util.pyi @@ -1,6 +1,9 @@ from _typeshed import StrPath, Unused from collections.abc import Callable, Container, Iterable, Mapping from typing import Any, Literal +from typing_extensions import TypeVarTuple, Unpack + +_Ts = TypeVarTuple("_Ts") def get_host_platform() -> str: ... def get_platform() -> str: ... @@ -10,8 +13,8 @@ def check_environ() -> None: ... def subst_vars(s: str, local_vars: Mapping[str, str]) -> None: ... def split_quoted(s: str) -> list[str]: ... def execute( - func: Callable[..., object], - args: tuple[Any, ...], + func: Callable[[Unpack[_Ts]], Unused], + args: tuple[Unpack[_Ts]], msg: str | None = None, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0, diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 0b62647532db..2724dbf6ec2f 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -1,6 +1,7 @@ import datetime import sys from _typeshed import Unused +from collections.abc import Iterable from email import _ParamType from email.charset import Charset from typing import overload @@ -28,9 +29,21 @@ _PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None def quote(str: str) -> str: ... def unquote(str: str) -> str: ... -def parseaddr(addr: str | None) -> tuple[str, str]: ... + +if sys.version_info >= (3, 13): + def parseaddr(addr: str | list[str], *, strict: bool = True) -> tuple[str, str]: ... + +else: + def parseaddr(addr: str) -> tuple[str, str]: ... + def formataddr(pair: tuple[str | None, str], charset: str | Charset = "utf-8") -> str: ... -def getaddresses(fieldvalues: list[str]) -> list[tuple[str, str]]: ... + +if sys.version_info >= (3, 13): + def getaddresses(fieldvalues: Iterable[str], *, strict: bool = True) -> list[tuple[str, str]]: ... + +else: + def getaddresses(fieldvalues: Iterable[str]) -> list[tuple[str, str]]: ... + @overload def parsedate(data: None) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index ccf638205bbe..376611f166b8 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import FileDescriptorLike, ReadOnlyBuffer, WriteableBuffer -from typing import Any, Literal, overload +from typing import Any, Final, Literal, overload from typing_extensions import Buffer if sys.platform != "win32": @@ -44,9 +44,10 @@ if sys.platform != "win32": F_SEAL_SHRINK: int F_SEAL_WRITE: int if sys.version_info >= (3, 9): - F_OFD_GETLK: int - F_OFD_SETLK: int - F_OFD_SETLKW: int + F_OFD_GETLK: Final[int] + F_OFD_SETLK: Final[int] + F_OFD_SETLKW: Final[int] + if sys.version_info >= (3, 10): F_GETPIPE_SZ: int F_SETPIPE_SZ: int @@ -105,6 +106,36 @@ if sys.platform != "win32": FICLONE: int FICLONERANGE: int + if sys.version_info >= (3, 13) and sys.platform == "linux": + F_OWNER_TID: Final = 0 + F_OWNER_PID: Final = 1 + F_OWNER_PGRP: Final = 2 + F_SETOWN_EX: Final = 15 + F_GETOWN_EX: Final = 16 + F_SEAL_FUTURE_WRITE: Final = 16 + F_GET_RW_HINT: Final = 1035 + F_SET_RW_HINT: Final = 1036 + F_GET_FILE_RW_HINT: Final = 1037 + F_SET_FILE_RW_HINT: Final = 1038 + RWH_WRITE_LIFE_NOT_SET: Final = 0 + RWH_WRITE_LIFE_NONE: Final = 1 + RWH_WRITE_LIFE_SHORT: Final = 2 + RWH_WRITE_LIFE_MEDIUM: Final = 3 + RWH_WRITE_LIFE_LONG: Final = 4 + RWH_WRITE_LIFE_EXTREME: Final = 5 + + if sys.version_info >= (3, 11) and sys.platform == "darwin": + F_OFD_SETLK: Final = 90 + F_OFD_SETLKW: Final = 91 + F_OFD_GETLK: Final = 92 + + if sys.version_info >= (3, 13) and sys.platform != "linux": + # OSx and NetBSD + F_GETNOSIGPIPE: Final[int] + F_SETNOSIGPIPE: Final[int] + # OSx and FreeBSD + F_RDAHEAD: Final[int] + @overload def fcntl(fd: FileDescriptorLike, cmd: int, arg: int = 0, /) -> int: ... @overload diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index 5c8232d800d5..dfec2da72344 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import GenericPath, StrOrBytesPath from collections.abc import Callable, Iterable, Sequence -from typing import Any, AnyStr, Generic, Literal +from typing import Any, AnyStr, Final, Generic, Literal if sys.version_info >= (3, 9): from types import GenericAlias @@ -9,7 +9,7 @@ if sys.version_info >= (3, 9): __all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"] DEFAULT_IGNORES: list[str] -BUFSIZE: Literal[8192] +BUFSIZE: Final = 8192 def cmp(f1: StrOrBytesPath, f2: StrOrBytesPath, shallow: bool | Literal[0, 1] = True) -> bool: ... def cmpfiles( diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 9e7097ddc56e..1b96e0d504b7 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -4,16 +4,16 @@ from collections.abc import Callable, Iterable, Iterator from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, Literal, TextIO +from typing import Any, Final, Literal, TextIO from typing_extensions import Self __all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", "all_errors", "FTP_TLS"] -MSG_OOB: Literal[1] -FTP_PORT: Literal[21] -MAXLINE: Literal[8192] -CRLF: Literal["\r\n"] -B_CRLF: Literal[b"\r\n"] +MSG_OOB: Final = 1 +FTP_PORT: Final = 21 +MAXLINE: Final = 8192 +CRLF: Final = "\r\n" +B_CRLF: Final = b"\r\n" class Error(Exception): ... class error_reply(Error): ... diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 31179add314c..9d34e0d6213a 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -1,13 +1,13 @@ import sys from collections.abc import Callable -from typing import Any, Literal +from typing import Any, Final, Literal from typing_extensions import TypeAlias -DEBUG_COLLECTABLE: Literal[2] -DEBUG_LEAK: Literal[38] -DEBUG_SAVEALL: Literal[32] -DEBUG_STATS: Literal[1] -DEBUG_UNCOLLECTABLE: Literal[4] +DEBUG_COLLECTABLE: Final = 2 +DEBUG_LEAK: Final = 38 +DEBUG_SAVEALL: Final = 32 +DEBUG_STATS: Final = 1 +DEBUG_UNCOLLECTABLE: Final = 4 _CallbackType: TypeAlias = Callable[[Literal["start", "stop"], dict[str, int]], object] @@ -34,4 +34,4 @@ if sys.version_info >= (3, 9): def isenabled() -> bool: ... def set_debug(flags: int, /) -> None: ... -def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ...) -> None: ... +def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ..., /) -> None: ... diff --git a/mypy/typeshed/stdlib/http/cookiejar.pyi b/mypy/typeshed/stdlib/http/cookiejar.pyi index faac20d13125..56097f163afd 100644 --- a/mypy/typeshed/stdlib/http/cookiejar.pyi +++ b/mypy/typeshed/stdlib/http/cookiejar.pyi @@ -42,7 +42,7 @@ class CookieJar(Iterable[Cookie]): def __len__(self) -> int: ... class FileCookieJar(CookieJar): - filename: str + filename: str | None delayload: bool def __init__(self, filename: StrPath | None = None, delayload: bool = False, policy: CookiePolicy | None = None) -> None: ... def save(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 56ee20523950..37b9a3882179 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -1,6 +1,7 @@ import abc import pathlib import sys +import types from _collections_abc import dict_keys, dict_values from _typeshed import StrPath from collections.abc import Iterable, Iterator, Mapping @@ -36,11 +37,8 @@ if sys.version_info >= (3, 10): from importlib.metadata._meta import PackageMetadata as PackageMetadata, SimplePath def packages_distributions() -> Mapping[str, list[str]]: ... - if sys.version_info >= (3, 12): - # It's generic but shouldn't be - _SimplePath: TypeAlias = SimplePath[Any] - else: - _SimplePath: TypeAlias = SimplePath + _SimplePath: TypeAlias = SimplePath + else: _SimplePath: TypeAlias = Path @@ -48,7 +46,9 @@ class PackageNotFoundError(ModuleNotFoundError): @property def name(self) -> str: ... # type: ignore[override] -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + _EntryPointBase = object +elif sys.version_info >= (3, 11): class DeprecatedTuple: def __getitem__(self, item: int) -> str: ... @@ -226,6 +226,9 @@ class Distribution(_distribution_parent): if sys.version_info >= (3, 10): @property def name(self) -> str: ... + if sys.version_info >= (3, 13): + @property + def origin(self) -> types.SimpleNamespace: ... class DistributionFinder(MetaPathFinder): class Context: diff --git a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi index 3eac226b7065..9f791dab254f 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi @@ -1,9 +1,12 @@ import sys +from _typeshed import StrPath from collections.abc import Iterator -from typing import Any, Protocol, TypeVar, overload +from os import PathLike +from typing import Any, Protocol, overload +from typing_extensions import TypeVar _T = TypeVar("_T") -_T_co = TypeVar("_T_co", covariant=True) +_T_co = TypeVar("_T_co", covariant=True, default=Any) class PackageMetadata(Protocol): def __len__(self) -> int: ... @@ -22,7 +25,18 @@ class PackageMetadata(Protocol): @overload def get(self, name: str, failobj: _T) -> _T | str: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 13): + class SimplePath(Protocol): + def joinpath(self, other: StrPath, /) -> SimplePath: ... + def __truediv__(self, other: StrPath, /) -> SimplePath: ... + # Incorrect at runtime + @property + def parent(self) -> PathLike[str]: ... + def read_text(self, encoding: str | None = None) -> str: ... + def read_bytes(self) -> bytes: ... + def exists(self) -> bool: ... + +elif sys.version_info >= (3, 12): class SimplePath(Protocol[_T_co]): # At runtime this is defined as taking `str | _T`, but that causes trouble. # See #11436. diff --git a/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi b/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi new file mode 100644 index 000000000000..565872fd976f --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi @@ -0,0 +1,2 @@ +def inspect(path: str) -> None: ... +def run() -> None: ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 23e0663d0d60..1eb9fc502e12 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -25,7 +25,7 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload +from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs if sys.version_info >= (3, 11): @@ -161,35 +161,39 @@ class BlockFinder: last: int def tokeneater(self, type: int, token: str, srowcol: tuple[int, int], erowcol: tuple[int, int], line: str) -> None: ... -CO_OPTIMIZED: Literal[1] -CO_NEWLOCALS: Literal[2] -CO_VARARGS: Literal[4] -CO_VARKEYWORDS: Literal[8] -CO_NESTED: Literal[16] -CO_GENERATOR: Literal[32] -CO_NOFREE: Literal[64] -CO_COROUTINE: Literal[128] -CO_ITERABLE_COROUTINE: Literal[256] -CO_ASYNC_GENERATOR: Literal[512] -TPFLAGS_IS_ABSTRACT: Literal[1048576] +CO_OPTIMIZED: Final = 1 +CO_NEWLOCALS: Final = 2 +CO_VARARGS: Final = 4 +CO_VARKEYWORDS: Final = 8 +CO_NESTED: Final = 16 +CO_GENERATOR: Final = 32 +CO_NOFREE: Final = 64 +CO_COROUTINE: Final = 128 +CO_ITERABLE_COROUTINE: Final = 256 +CO_ASYNC_GENERATOR: Final = 512 +TPFLAGS_IS_ABSTRACT: Final = 1048576 modulesbyfile: dict[str, Any] _GetMembersPredicateTypeGuard: TypeAlias = Callable[[Any], TypeGuard[_T]] +_GetMembersPredicateTypeIs: TypeAlias = Callable[[Any], TypeIs[_T]] _GetMembersPredicate: TypeAlias = Callable[[Any], bool] -_GetMembersReturnTypeGuard: TypeAlias = list[tuple[str, _T]] -_GetMembersReturn: TypeAlias = list[tuple[str, Any]] +_GetMembersReturn: TypeAlias = list[tuple[str, _T]] @overload -def getmembers(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturnTypeGuard[_T]: ... +def getmembers(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturn[_T]: ... @overload -def getmembers(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... +def getmembers(object: object, predicate: _GetMembersPredicateTypeIs[_T]) -> _GetMembersReturn[_T]: ... +@overload +def getmembers(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn[Any]: ... if sys.version_info >= (3, 11): @overload - def getmembers_static(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturnTypeGuard[_T]: ... + def getmembers_static(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturn[_T]: ... + @overload + def getmembers_static(object: object, predicate: _GetMembersPredicateTypeIs[_T]) -> _GetMembersReturn[_T]: ... @overload - def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... + def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn[Any]: ... def getmodulename(path: StrPath) -> str | None: ... def ismodule(object: object) -> TypeIs[ModuleType]: ... @@ -360,10 +364,10 @@ class _ParameterKind(enum.IntEnum): def description(self) -> str: ... if sys.version_info >= (3, 12): - AGEN_CREATED: Literal["AGEN_CREATED"] - AGEN_RUNNING: Literal["AGEN_RUNNING"] - AGEN_SUSPENDED: Literal["AGEN_SUSPENDED"] - AGEN_CLOSED: Literal["AGEN_CLOSED"] + AGEN_CREATED: Final = "AGEN_CREATED" + AGEN_RUNNING: Final = "AGEN_RUNNING" + AGEN_SUSPENDED: Final = "AGEN_SUSPENDED" + AGEN_CLOSED: Final = "AGEN_CLOSED" def getasyncgenstate( agen: AsyncGenerator[Any, Any] @@ -580,19 +584,19 @@ def getattr_static(obj: object, attr: str, default: Any | None = ...) -> Any: .. # Current State of Generators and Coroutines # -GEN_CREATED: Literal["GEN_CREATED"] -GEN_RUNNING: Literal["GEN_RUNNING"] -GEN_SUSPENDED: Literal["GEN_SUSPENDED"] -GEN_CLOSED: Literal["GEN_CLOSED"] +GEN_CREATED: Final = "GEN_CREATED" +GEN_RUNNING: Final = "GEN_RUNNING" +GEN_SUSPENDED: Final = "GEN_SUSPENDED" +GEN_CLOSED: Final = "GEN_CLOSED" def getgeneratorstate( generator: Generator[Any, Any, Any] ) -> Literal["GEN_CREATED", "GEN_RUNNING", "GEN_SUSPENDED", "GEN_CLOSED"]: ... -CORO_CREATED: Literal["CORO_CREATED"] -CORO_RUNNING: Literal["CORO_RUNNING"] -CORO_SUSPENDED: Literal["CORO_SUSPENDED"] -CORO_CLOSED: Literal["CORO_CLOSED"] +CORO_CREATED: Final = "CORO_CREATED" +CORO_RUNNING: Final = "CORO_RUNNING" +CORO_SUSPENDED: Final = "CORO_SUSPENDED" +CORO_CLOSED: Final = "CORO_CLOSED" def getcoroutinestate( coroutine: Coroutine[Any, Any, Any] diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 01f3bfc06a27..a386a914ddcd 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -6,7 +6,7 @@ from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer from collections.abc import Callable, Iterable, Iterator from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, Literal, Protocol, TextIO, TypeVar, overload, type_check_only +from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only from typing_extensions import Self __all__ = [ @@ -36,11 +36,11 @@ if sys.version_info >= (3, 11): _T = TypeVar("_T") -DEFAULT_BUFFER_SIZE: Literal[8192] +DEFAULT_BUFFER_SIZE: Final = 8192 -SEEK_SET: Literal[0] -SEEK_CUR: Literal[1] -SEEK_END: Literal[2] +SEEK_SET: Final = 0 +SEEK_CUR: Final = 1 +SEEK_END: Final = 2 open = builtins.open @@ -173,12 +173,12 @@ class _WrappedBuffer(Protocol): # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... # def tell(self) -> int: ... -# TODO: Should be generic over the buffer type, but needs to wait for -# TypeVar defaults. -class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes +_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True) + +class TextIOWrapper(TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes def __init__( self, - buffer: _WrappedBuffer, + buffer: _BufferT_co, encoding: str | None = None, errors: str | None = None, newline: str | None = None, @@ -187,7 +187,7 @@ class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible d ) -> None: ... # Equals the "buffer" argument passed in to the constructor. @property - def buffer(self) -> BinaryIO: ... + def buffer(self) -> _BufferT_co: ... # type: ignore[override] @property def closed(self) -> bool: ... @property @@ -211,7 +211,7 @@ class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible d def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] # Equals the "buffer" argument passed in to the constructor. - def detach(self) -> BinaryIO: ... + def detach(self) -> _BufferT_co: ... # type: ignore[override] # TextIOWrapper's version of seek only supports a limited subset of # operations. def seek(self, cookie: int, whence: int = 0, /) -> int: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 03decc74e65e..f51ea87dcfcf 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,11 +1,11 @@ import sys from collections.abc import Iterable, Iterator -from typing import Any, Generic, Literal, SupportsInt, TypeVar, overload +from typing import Any, Final, Generic, Literal, SupportsInt, TypeVar, overload from typing_extensions import Self, TypeAlias # Undocumented length constants -IPV4LENGTH: Literal[32] -IPV6LENGTH: Literal[128] +IPV4LENGTH: Final = 32 +IPV6LENGTH: Final = 128 _A = TypeVar("_A", IPv4Address, IPv6Address) _N = TypeVar("_N", IPv4Network, IPv6Network) diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 16e04829c6cf..1635b6a0a072 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -326,6 +326,10 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 12): class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): - def __new__(cls, iterable: Iterable[_T_co], n: int) -> Self: ... + if sys.version_info >= (3, 13): + def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... + else: + def __new__(cls, iterable: Iterable[_T_co], n: int) -> Self: ... + def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, ...]: ... diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index c05e46a02aeb..2df2b9a8bd6a 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,7 +1,7 @@ from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Mapping, Sequence -from typing import IO, Any, Literal, TextIO, final, overload +from typing import IO, Any, Final, Literal, TextIO, final, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -50,33 +50,33 @@ _PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes] _FilterChain: TypeAlias = Sequence[Mapping[str, Any]] -FORMAT_AUTO: Literal[0] -FORMAT_XZ: Literal[1] -FORMAT_ALONE: Literal[2] -FORMAT_RAW: Literal[3] -CHECK_NONE: Literal[0] -CHECK_CRC32: Literal[1] -CHECK_CRC64: Literal[4] -CHECK_SHA256: Literal[10] -CHECK_ID_MAX: Literal[15] -CHECK_UNKNOWN: Literal[16] +FORMAT_AUTO: Final = 0 +FORMAT_XZ: Final = 1 +FORMAT_ALONE: Final = 2 +FORMAT_RAW: Final = 3 +CHECK_NONE: Final = 0 +CHECK_CRC32: Final = 1 +CHECK_CRC64: Final = 4 +CHECK_SHA256: Final = 10 +CHECK_ID_MAX: Final = 15 +CHECK_UNKNOWN: Final = 16 FILTER_LZMA1: int # v big number -FILTER_LZMA2: Literal[33] -FILTER_DELTA: Literal[3] -FILTER_X86: Literal[4] -FILTER_IA64: Literal[6] -FILTER_ARM: Literal[7] -FILTER_ARMTHUMB: Literal[8] -FILTER_SPARC: Literal[9] -FILTER_POWERPC: Literal[5] -MF_HC3: Literal[3] -MF_HC4: Literal[4] -MF_BT2: Literal[18] -MF_BT3: Literal[19] -MF_BT4: Literal[20] -MODE_FAST: Literal[1] -MODE_NORMAL: Literal[2] -PRESET_DEFAULT: Literal[6] +FILTER_LZMA2: Final = 33 +FILTER_DELTA: Final = 3 +FILTER_X86: Final = 4 +FILTER_IA64: Final = 6 +FILTER_ARM: Final = 7 +FILTER_ARMTHUMB: Final = 8 +FILTER_SPARC: Final = 9 +FILTER_POWERPC: Final = 5 +MF_HC3: Final = 3 +MF_HC4: Final = 4 +MF_BT2: Final = 18 +MF_BT3: Final = 19 +MF_BT4: Final = 20 +MODE_FAST: Final = 1 +MODE_NORMAL: Final = 2 +PRESET_DEFAULT: Final = 6 PRESET_EXTREME: int # v big number # from _lzma.c diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi index 2f43f9552652..a98a00a42853 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -115,6 +115,14 @@ class Maildir(Mailbox[MaildirMessage]): def get_message(self, key: str) -> MaildirMessage: ... def get_bytes(self, key: str) -> bytes: ... def get_file(self, key: str) -> _ProxyFile[bytes]: ... + if sys.version_info >= (3, 13): + def get_info(self, key: str) -> str: ... + def set_info(self, key: str, info: str) -> None: ... + def get_flags(self, key: str) -> str: ... + def set_flags(self, key: str, flags: str) -> None: ... + def add_flag(self, key: str, flag: str) -> None: ... + def remove_flag(self, key: str, flag: str) -> None: ... + def iterkeys(self) -> Iterator[str]: ... def __contains__(self, key: str) -> bool: ... def __len__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index 517193e3516f..9914a34a2d6a 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -45,6 +45,7 @@ class MimeTypes: types_map: tuple[dict[str, str], dict[str, str]] types_map_inv: tuple[dict[str, str], dict[str, str]] def __init__(self, filenames: tuple[str, ...] = (), strict: bool = True) -> None: ... + def add_type(self, type: str, ext: str, strict: bool = True) -> None: ... def guess_extension(self, type: str, strict: bool = True) -> str | None: ... def guess_type(self, url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... def guess_all_extensions(self, type: str, strict: bool = True) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 7688970e5786..a0c150d6e7e8 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadableBuffer, Unused from collections.abc import Iterable, Iterator, Sized -from typing import Final, NoReturn, overload +from typing import Final, Literal, NoReturn, overload from typing_extensions import Self ACCESS_DEFAULT: int @@ -77,7 +77,7 @@ class mmap(Iterable[int], Sized): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... if sys.version_info >= (3, 13): - def seekable(self) -> bool: ... + def seekable(self) -> Literal[True]: ... if sys.platform != "win32": MADV_NORMAL: int @@ -118,4 +118,16 @@ if sys.version_info >= (3, 13) and sys.platform != "win32": MAP_32BIT: Final = 32768 if sys.version_info >= (3, 13) and sys.platform == "darwin": + MAP_NORESERVE: Final = 64 + MAP_NOEXTEND: Final = 256 + MAP_HASSEMAPHORE: Final = 512 + MAP_NOCACHE: Final = 1024 + MAP_JIT: Final = 2048 + MAP_RESILIENT_CODESIGN: Final = 8192 + MAP_RESILIENT_MEDIA: Final = 16384 + MAP_TRANSLATED_ALLOW_EXECUTE: Final = 131072 + MAP_UNIX03: Final = 262144 MAP_TPRO: Final = 524288 + +if sys.version_info >= (3, 13) and sys.platform == "linux": + MAP_NORESERVE: Final = 16384 diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index 54b3674a3a46..403a5d933522 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -1,14 +1,14 @@ import sys -from typing import Final, Literal +from typing import Final # This module is only available on Windows if sys.platform == "win32": CRT_ASSEMBLY_VERSION: Final[str] - LK_UNLCK: Literal[0] - LK_LOCK: Literal[1] - LK_NBLCK: Literal[2] - LK_RLCK: Literal[3] - LK_NBRLCK: Literal[4] + LK_UNLCK: Final = 0 + LK_LOCK: Final = 1 + LK_NBLCK: Final = 2 + LK_RLCK: Final = 3 + LK_NBRLCK: Final = 4 SEM_FAILCRITICALERRORS: int SEM_NOALIGNMENTFAULTEXCEPT: int SEM_NOGPFAULTERRORBOX: int diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index d2d611e3ca62..950ed1d8c56b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType -from typing import Any, Generic, Literal, TypeVar +from typing import Any, Final, Generic, TypeVar from typing_extensions import Self if sys.version_info >= (3, 9): @@ -97,7 +97,7 @@ class ThreadPool(Pool): ) -> None: ... # undocumented -INIT: Literal["INIT"] -RUN: Literal["RUN"] -CLOSE: Literal["CLOSE"] -TERMINATE: Literal["TERMINATE"] +INIT: Final = "INIT" +RUN: Final = "RUN" +CLOSE: Final = "CLOSE" +TERMINATE: Final = "TERMINATE" diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index 91532633e1b9..6d23e20e6981 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -8,7 +8,7 @@ from copyreg import _DispatchTableType from multiprocessing import connection from pickle import _ReducedType from socket import socket -from typing import Any, Literal +from typing import Any, Final if sys.platform == "win32": __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"] @@ -43,10 +43,7 @@ if sys.platform == "win32": def detach(self) -> int: ... else: - if sys.platform == "darwin": - ACKNOWLEDGE: Literal[True] - else: - ACKNOWLEDGE: Literal[False] + ACKNOWLEDGE: Final[bool] def recvfds(sock: socket, size: int) -> list[int]: ... def send_handle(conn: HasFileno, handle: int, destination_pid: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi index 969c657e9aab..85dfbff1cb50 100644 --- a/mypy/typeshed/stdlib/nntplib.pyi +++ b/mypy/typeshed/stdlib/nntplib.pyi @@ -5,7 +5,7 @@ import sys from _typeshed import Unused from builtins import list as _list # conflicts with a method named "list" from collections.abc import Iterable -from typing import IO, Any, Literal, NamedTuple +from typing import IO, Any, Final, NamedTuple from typing_extensions import Self, TypeAlias __all__ = [ @@ -31,8 +31,8 @@ class NNTPPermanentError(NNTPError): ... class NNTPProtocolError(NNTPError): ... class NNTPDataError(NNTPError): ... -NNTP_PORT: Literal[119] -NNTP_SSL_PORT: Literal[563] +NNTP_PORT: Final = 119 +NNTP_SSL_PORT: Final = 563 class GroupInfo(NamedTuple): group: str diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 9b00117a5599..e2d272cb4112 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -971,7 +971,8 @@ else: def spawnvp(mode: int, file: StrOrBytesPath, args: _ExecVArgs) -> int: ... def spawnvpe(mode: int, file: StrOrBytesPath, args: _ExecVArgs, env: _ExecEnv) -> int: ... def wait() -> tuple[int, int]: ... # Unix only - if sys.platform != "darwin": + # Added to MacOS in 3.13 + if sys.platform != "darwin" or sys.version_info >= (3, 13): @final class waitid_result(structseq[int], tuple[int, int, int, int, int]): if sys.version_info >= (3, 10): @@ -1155,3 +1156,24 @@ if sys.version_info >= (3, 12) and sys.platform == "linux": CLONE_VM: int def unshare(flags: int) -> None: ... def setns(fd: FileDescriptorLike, nstype: int = 0) -> None: ... + +if sys.version_info >= (3, 13) and sys.platform != "win32": + def posix_openpt(oflag: int, /) -> int: ... + def grantpt(fd: FileDescriptorLike, /) -> None: ... + def unlockpt(fd: FileDescriptorLike, /) -> None: ... + def ptsname(fd: FileDescriptorLike, /) -> str: ... + +if sys.version_info >= (3, 13) and sys.platform == "linux": + TFD_TIMER_ABSTIME: Final = 1 + TFD_TIMER_CANCEL_ON_SET: Final = 2 + TFD_NONBLOCK: Final[int] + TFD_CLOEXEC: Final[int] + POSIX_SPAWN_CLOSEFROM: Final[int] + + def timerfd_create(clockid: int, /, *, flags: int = 0) -> int: ... + def timerfd_settime( + fd: FileDescriptor, /, *, flags: int = 0, initial: float = 0.0, interval: float = 0.0 + ) -> tuple[float, float]: ... + def timerfd_settime_ns(fd: FileDescriptor, /, *, flags: int = 0, initial: int = 0, interval: int = 0) -> tuple[int, int]: ... + def timerfd_gettime(fd: FileDescriptor, /) -> tuple[float, float]: ... + def timerfd_gettime_ns(fd: FileDescriptor, /) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index c8c8dde0f33e..116bf6431831 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -49,7 +49,7 @@ class PurePath(PathLike[str]): def stem(self) -> str: ... if sys.version_info >= (3, 12): def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... - def __init__(self, *args: StrPath) -> None: ... + def __init__(self, *args: StrPath) -> None: ... # pyright: ignore[reportInconsistentConstructor] else: def __new__(cls, *args: StrPath) -> Self: ... @@ -101,7 +101,11 @@ class PurePosixPath(PurePath): ... class PureWindowsPath(PurePath): ... class Path(PurePath): - def __new__(cls, *args: StrPath, **kwargs: Any) -> Self: ... + if sys.version_info >= (3, 12): + def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor] + else: + def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... + @classmethod def cwd(cls) -> Self: ... if sys.version_info >= (3, 10): @@ -113,7 +117,7 @@ class Path(PurePath): if sys.version_info >= (3, 13): @classmethod - def from_uri(cls, uri: str) -> Path: ... + def from_uri(cls, uri: str) -> Self: ... def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... def is_file(self, *, follow_symlinks: bool = True) -> bool: ... def read_text(self, encoding: str | None = None, errors: str | None = None, newline: str | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index 487adddd04bf..d49315427813 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -5,7 +5,7 @@ from cmd import Cmd from collections.abc import Callable, Iterable, Mapping, Sequence from inspect import _SourceObjectType from types import CodeType, FrameType, TracebackType -from typing import IO, Any, ClassVar, TypeVar +from typing import IO, Any, ClassVar, Final, TypeVar from typing_extensions import ParamSpec, Self __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"] @@ -30,6 +30,9 @@ class Pdb(Bdb, Cmd): commands_resuming: ClassVar[list[str]] + if sys.version_info >= (3, 13): + MAX_CHAINED_EXCEPTION_DEPTH: Final = 999 + aliases: dict[str, str] mainpyfile: str _wait_for_mainpyfile: bool @@ -58,8 +61,16 @@ class Pdb(Bdb, Cmd): if sys.version_info < (3, 11): def execRcLines(self) -> None: ... + if sys.version_info >= (3, 13): + user_opcode = Bdb.user_line + def bp_commands(self, frame: FrameType) -> bool: ... - def interaction(self, frame: FrameType | None, traceback: TracebackType | None) -> None: ... + + if sys.version_info >= (3, 13): + def interaction(self, frame: FrameType | None, tb_or_exc: TracebackType | BaseException | None) -> None: ... + else: + def interaction(self, frame: FrameType | None, traceback: TracebackType | None) -> None: ... + def displayhook(self, obj: object) -> None: ... def handle_command_def(self, line: str) -> bool: ... def defaultFile(self) -> str: ... @@ -72,6 +83,9 @@ class Pdb(Bdb, Cmd): if sys.version_info < (3, 11): def _runscript(self, filename: str) -> None: ... + if sys.version_info >= (3, 13): + def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... # type: ignore[override] + def do_commands(self, arg: str) -> bool | None: ... def do_break(self, arg: str, temporary: bool = ...) -> bool | None: ... def do_tbreak(self, arg: str) -> bool | None: ... @@ -81,6 +95,9 @@ class Pdb(Bdb, Cmd): def do_ignore(self, arg: str) -> bool | None: ... def do_clear(self, arg: str) -> bool | None: ... def do_where(self, arg: str) -> bool | None: ... + if sys.version_info >= (3, 13): + def do_exceptions(self, arg: str) -> bool | None: ... + def do_up(self, arg: str) -> bool | None: ... def do_down(self, arg: str) -> bool | None: ... def do_until(self, arg: str) -> bool | None: ... @@ -125,8 +142,14 @@ class Pdb(Bdb, Cmd): def help_exec(self) -> None: ... def help_pdb(self) -> None: ... def sigint_handler(self, signum: signal.Signals, frame: FrameType) -> None: ... - def message(self, msg: str) -> None: ... + if sys.version_info >= (3, 13): + def message(self, msg: str, end: str = "\n") -> None: ... + else: + def message(self, msg: str) -> None: ... + def error(self, msg: str) -> None: ... + if sys.version_info >= (3, 13): + def completenames(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... # type: ignore[override] if sys.version_info >= (3, 12): def set_convenience_variable(self, frame: FrameType, name: str, value: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 12f1d16a0d6f..7476f2991978 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -3,7 +3,7 @@ import ssl import sys from builtins import list as _list # conflicts with a method named "list" from re import Pattern -from typing import Any, BinaryIO, Literal, NoReturn, overload +from typing import Any, BinaryIO, Final, NoReturn, overload from typing_extensions import TypeAlias __all__ = ["POP3", "error_proto", "POP3_SSL"] @@ -12,11 +12,11 @@ _LongResp: TypeAlias = tuple[bytes, list[bytes], int] class error_proto(Exception): ... -POP3_PORT: Literal[110] -POP3_SSL_PORT: Literal[995] -CR: Literal[b"\r"] -LF: Literal[b"\n"] -CRLF: Literal[b"\r\n"] +POP3_PORT: Final = 110 +POP3_SSL_PORT: Final = 995 +CR: Final = b"\r" +LF: Final = b"\n" +CRLF: Final = b"\r\n" HAVE_SSL: bool class POP3: diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index b31b8f3d3524..1a4f22af82cf 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -236,6 +236,23 @@ if sys.platform != "win32": if sys.version_info >= (3, 11): from os import login_tty as login_tty + if sys.version_info >= (3, 13): + from os import grantpt as grantpt, posix_openpt as posix_openpt, ptsname as ptsname, unlockpt as unlockpt + + if sys.version_info >= (3, 13) and sys.platform == "linux": + from os import ( + POSIX_SPAWN_CLOSEFROM as POSIX_SPAWN_CLOSEFROM, + TFD_CLOEXEC as TFD_CLOEXEC, + TFD_NONBLOCK as TFD_NONBLOCK, + TFD_TIMER_ABSTIME as TFD_TIMER_ABSTIME, + TFD_TIMER_CANCEL_ON_SET as TFD_TIMER_CANCEL_ON_SET, + timerfd_create as timerfd_create, + timerfd_gettime as timerfd_gettime, + timerfd_gettime_ns as timerfd_gettime_ns, + timerfd_settime as timerfd_settime, + timerfd_settime_ns as timerfd_settime_ns, + ) + if sys.platform != "linux": from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod @@ -269,13 +286,14 @@ if sys.platform != "win32": sched_setscheduler as sched_setscheduler, setresgid as setresgid, setresuid as setresuid, - waitid as waitid, - waitid_result as waitid_result, ) if sys.version_info >= (3, 10): from os import RWF_APPEND as RWF_APPEND + if sys.platform != "darwin" or sys.version_info >= (3, 13): + from os import waitid as waitid, waitid_result as waitid_result + if sys.platform == "linux": from os import ( GRND_NONBLOCK as GRND_NONBLOCK, diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index 022b08046c54..4c9e42b4ec5e 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -1,17 +1,17 @@ import sys from collections.abc import Callable, Iterable -from typing import Literal +from typing import Final from typing_extensions import TypeAlias if sys.platform != "win32": __all__ = ["openpty", "fork", "spawn"] _Reader: TypeAlias = Callable[[int], bytes] - STDIN_FILENO: Literal[0] - STDOUT_FILENO: Literal[1] - STDERR_FILENO: Literal[2] + STDIN_FILENO: Final = 0 + STDOUT_FILENO: Final = 1 + STDERR_FILENO: Final = 2 - CHILD: Literal[0] + CHILD: Final = 0 def openpty() -> tuple[int, int]: ... def master_open() -> tuple[int, str]: ... # deprecated, use openpty() def slave_open(tty_name: str) -> int: ... # deprecated, use openpty() diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index 1a90eb30efca..144f782acad5 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -5,7 +5,7 @@ from builtins import list as _list # "list" conflicts with method name from collections.abc import Callable, Container, Mapping, MutableMapping from reprlib import Repr from types import MethodType, ModuleType, TracebackType -from typing import IO, Any, AnyStr, Final, NoReturn, TypeVar +from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar from typing_extensions import TypeGuard __all__ = ["help"] @@ -17,6 +17,9 @@ __date__: Final[str] __version__: Final[str] __credits__: Final[str] +class _Pager(Protocol): + def __call__(self, text: str, title: str = "") -> None: ... + def pathdirs() -> list[str]: ... def getdoc(object: object) -> str: ... def splitdoc(doc: AnyStr) -> tuple[AnyStr, AnyStr]: ... @@ -229,16 +232,36 @@ class TextDoc(Doc): doc: Any | None = None, ) -> str: ... -def pager(text: str) -> None: ... -def getpager() -> Callable[[str], None]: ... +if sys.version_info >= (3, 13): + def pager(text: str, title: str = "") -> None: ... + +else: + def pager(text: str) -> None: ... + def plain(text: str) -> str: ... -def pipepager(text: str, cmd: str) -> None: ... -def tempfilepager(text: str, cmd: str) -> None: ... -def ttypager(text: str) -> None: ... -def plainpager(text: str) -> None: ... def describe(thing: Any) -> str: ... def locate(path: str, forceload: bool = ...) -> object: ... +if sys.version_info >= (3, 13): + def get_pager() -> _Pager: ... + def pipe_pager(text: str, cmd: str, title: str = "") -> None: ... + def tempfile_pager(text: str, cmd: str, title: str = "") -> None: ... + def tty_pager(text: str, title: str = "") -> None: ... + def plain_pager(text: str, title: str = "") -> None: ... + + # For backwards compatibility. + getpager = get_pager + pipepager = pipe_pager + tempfilepager = tempfile_pager + ttypager = tty_pager + plainpager = plain_pager +else: + def getpager() -> Callable[[str], None]: ... + def pipepager(text: str, cmd: str) -> None: ... + def tempfilepager(text: str, cmd: str) -> None: ... + def ttypager(text: str) -> None: ... + def plainpager(text: str) -> None: ... + text: TextDoc html: HTMLDoc diff --git a/mypy/typeshed/stdlib/readline.pyi b/mypy/typeshed/stdlib/readline.pyi index 688ae48d9f92..7325c267b32c 100644 --- a/mypy/typeshed/stdlib/readline.pyi +++ b/mypy/typeshed/stdlib/readline.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Callable, Sequence +from typing import Literal from typing_extensions import TypeAlias if sys.platform != "win32": @@ -34,3 +35,6 @@ if sys.platform != "win32": def set_completer_delims(string: str, /) -> None: ... def get_completer_delims() -> str: ... def set_completion_display_matches_hook(function: _CompDisp | None = None, /) -> None: ... + + if sys.version_info >= (3, 13): + backend: Literal["readline", "editline"] diff --git a/mypy/typeshed/stdlib/site.pyi b/mypy/typeshed/stdlib/site.pyi index a8c6bcb417f4..6e39677aaea0 100644 --- a/mypy/typeshed/stdlib/site.pyi +++ b/mypy/typeshed/stdlib/site.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import StrPath from collections.abc import Iterable @@ -13,7 +14,15 @@ def addsitedir(sitedir: str, known_paths: set[str] | None = None) -> None: ... def addsitepackages(known_paths: set[str] | None, prefixes: Iterable[str] | None = None) -> set[str] | None: ... # undocumented def addusersitepackages(known_paths: set[str] | None) -> set[str] | None: ... # undocumented def check_enableusersite() -> bool | None: ... # undocumented + +if sys.version_info >= (3, 13): + def gethistoryfile() -> str: ... # undocumented + def enablerlcompleter() -> None: ... # undocumented + +if sys.version_info >= (3, 13): + def register_readline() -> None: ... # undocumented + def execsitecustomize() -> None: ... # undocumented def execusercustomize() -> None: ... # undocumented def getsitepackages(prefixes: Iterable[str] | None = None) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi index d522372c438c..0c1e484bb07e 100644 --- a/mypy/typeshed/stdlib/sre_constants.pyi +++ b/mypy/typeshed/stdlib/sre_constants.pyi @@ -30,7 +30,8 @@ AT_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] AT_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] CH_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] CH_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] -SRE_FLAG_TEMPLATE: int +if sys.version_info < (3, 13): + SRE_FLAG_TEMPLATE: int SRE_FLAG_IGNORECASE: int SRE_FLAG_LOCALE: int SRE_FLAG_MULTILINE: int diff --git a/mypy/typeshed/stdlib/stat.pyi b/mypy/typeshed/stdlib/stat.pyi index f3bdd92c1068..face28ab0cbb 100644 --- a/mypy/typeshed/stdlib/stat.pyi +++ b/mypy/typeshed/stdlib/stat.pyi @@ -1,7 +1,7 @@ import sys from _stat import * -from typing import Literal +from typing import Final if sys.version_info >= (3, 13): # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 - SF_RESTRICTED: Literal[0x00080000] + SF_RESTRICTED: Final = 0x00080000 diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index 0f080954ba2c..5481d4d1dd4a 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -5,11 +5,30 @@ from typing import Any __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] +if sys.version_info >= (3, 13): + __all__ += ["SymbolTableType"] + def symtable(code: str, filename: str, compile_type: str) -> SymbolTable: ... +if sys.version_info >= (3, 13): + from enum import StrEnum + + class SymbolTableType(StrEnum): + MODULE = "module" + FUNCTION = "function" + CLASS = "class" + ANNOTATION = "annotation" + TYPE_ALIAS = "type alias" + TYPE_PARAMETERS = "type parameters" + TYPE_VARIABLE = "type variable" + class SymbolTable: def __init__(self, raw_table: Any, filename: str) -> None: ... - def get_type(self) -> str: ... + if sys.version_info >= (3, 13): + def get_type(self) -> SymbolTableType: ... + else: + def get_type(self) -> str: ... + def get_id(self) -> int: ... def get_name(self) -> str: ... def get_lineno(self) -> int: ... @@ -42,13 +61,23 @@ class Symbol: def get_name(self) -> str: ... def is_referenced(self) -> bool: ... def is_parameter(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_type_parameter(self) -> bool: ... + def is_global(self) -> bool: ... def is_declared_global(self) -> bool: ... def is_local(self) -> bool: ... def is_annotated(self) -> bool: ... def is_free(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_free_class(self) -> bool: ... + def is_imported(self) -> bool: ... def is_assigned(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_comp_iter(self) -> bool: ... + def is_comp_cell(self) -> bool: ... + def is_namespace(self) -> bool: ... def get_namespaces(self) -> Sequence[SymbolTable]: ... def get_namespace(self) -> SymbolTable: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 9989a27b2bc1..d65ddfe3825d 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -355,7 +355,11 @@ def set_int_max_str_digits(maxdigits: int) -> None: ... def get_int_max_str_digits() -> int: ... if sys.version_info >= (3, 12): - def getunicodeinternedsize() -> int: ... + if sys.version_info >= (3, 13): + def getunicodeinternedsize(*, _only_immortal: bool = False) -> int: ... + else: + def getunicodeinternedsize() -> int: ... + def deactivate_stack_trampoline() -> None: ... def is_stack_trampoline_active() -> bool: ... # It always exists, but raises on non-linux platforms: diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index d539dd5e4579..1e0d0d383902 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -1,48 +1,50 @@ import sys -from typing import Literal, overload +from typing import Final, overload if sys.platform != "win32": - LOG_ALERT: Literal[1] - LOG_AUTH: Literal[32] - LOG_AUTHPRIV: Literal[80] - LOG_CONS: Literal[2] - LOG_CRIT: Literal[2] - LOG_CRON: Literal[72] - LOG_DAEMON: Literal[24] - LOG_DEBUG: Literal[7] - LOG_EMERG: Literal[0] - LOG_ERR: Literal[3] - LOG_INFO: Literal[6] - LOG_KERN: Literal[0] - LOG_LOCAL0: Literal[128] - LOG_LOCAL1: Literal[136] - LOG_LOCAL2: Literal[144] - LOG_LOCAL3: Literal[152] - LOG_LOCAL4: Literal[160] - LOG_LOCAL5: Literal[168] - LOG_LOCAL6: Literal[176] - LOG_LOCAL7: Literal[184] - LOG_LPR: Literal[48] - LOG_MAIL: Literal[16] - LOG_NDELAY: Literal[8] - LOG_NEWS: Literal[56] - LOG_NOTICE: Literal[5] - LOG_NOWAIT: Literal[16] - LOG_ODELAY: Literal[4] - LOG_PERROR: Literal[32] - LOG_PID: Literal[1] - LOG_SYSLOG: Literal[40] - LOG_USER: Literal[8] - LOG_UUCP: Literal[64] - LOG_WARNING: Literal[4] + LOG_ALERT: Final = 1 + LOG_AUTH: Final = 32 + LOG_AUTHPRIV: Final = 80 + LOG_CONS: Final = 2 + LOG_CRIT: Final = 2 + LOG_CRON: Final = 72 + LOG_DAEMON: Final = 24 + LOG_DEBUG: Final = 7 + LOG_EMERG: Final = 0 + LOG_ERR: Final = 3 + LOG_INFO: Final = 6 + LOG_KERN: Final = 0 + LOG_LOCAL0: Final = 128 + LOG_LOCAL1: Final = 136 + LOG_LOCAL2: Final = 144 + LOG_LOCAL3: Final = 152 + LOG_LOCAL4: Final = 160 + LOG_LOCAL5: Final = 168 + LOG_LOCAL6: Final = 176 + LOG_LOCAL7: Final = 184 + LOG_LPR: Final = 48 + LOG_MAIL: Final = 16 + LOG_NDELAY: Final = 8 + LOG_NEWS: Final = 56 + LOG_NOTICE: Final = 5 + LOG_NOWAIT: Final = 16 + LOG_ODELAY: Final = 4 + LOG_PERROR: Final = 32 + LOG_PID: Final = 1 + LOG_SYSLOG: Final = 40 + LOG_USER: Final = 8 + LOG_UUCP: Final = 64 + LOG_WARNING: Final = 4 if sys.version_info >= (3, 13): - LOG_FTP: Literal[88] - LOG_INSTALL: Literal[112] - LOG_LAUNCHD: Literal[192] - LOG_NETINFO: Literal[96] - LOG_RAS: Literal[120] - LOG_REMOTEAUTH: Literal[104] + LOG_FTP: Final = 88 + + if sys.platform == "darwin": + LOG_INSTALL: Final = 112 + LOG_LAUNCHD: Final = 192 + LOG_NETINFO: Final = 96 + LOG_RAS: Final = 120 + LOG_REMOTEAUTH: Final = 104 def LOG_MASK(pri: int, /) -> int: ... def LOG_UPTO(pri: int, /) -> int: ... diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 3ae8cca39f77..d31fd74d3482 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -264,6 +264,8 @@ class _TemporaryFileWrapper(IO[AnyStr]): def writelines(self: _TemporaryFileWrapper[bytes], lines: Iterable[ReadableBuffer]) -> None: ... @overload def writelines(self, lines: Iterable[AnyStr]) -> None: ... + @property + def closed(self) -> bool: ... if sys.version_info >= (3, 11): _SpooledTemporaryFileBase = io.IOBase diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index 1ecadef508d0..c441a04681e2 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -61,7 +61,7 @@ if sys.version_info >= (3, 10): def gettrace() -> TraceFunction | None: ... def getprofile() -> ProfileFunction | None: ... -def stack_size(size: int = ...) -> int: ... +def stack_size(size: int = 0, /) -> int: ... TIMEOUT_MAX: float diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index d8ce17535eab..77953525bebe 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,7 +1,7 @@ import _tkinter import sys from _typeshed import Incomplete, StrEnum, StrOrBytesPath -from collections.abc import Callable, Mapping, Sequence +from collections.abc import Callable, Iterable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType @@ -3331,9 +3331,33 @@ class PhotoImage(Image, _PhotoImageLike): def blank(self) -> None: ... def cget(self, option: str) -> str: ... def __getitem__(self, key: str) -> str: ... # always string: image['height'] can be '0' - def copy(self) -> PhotoImage: ... - def zoom(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... - def subsample(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + if sys.version_info >= (3, 13): + def copy( + self, + *, + from_coords: Iterable[int] | None = None, + zoom: int | tuple[int, int] | list[int] | None = None, + subsample: int | tuple[int, int] | list[int] | None = None, + ) -> PhotoImage: ... + def subsample(self, x: int, y: Literal[""] = "", *, from_coords: Iterable[int] | None = None) -> PhotoImage: ... + def zoom(self, x: int, y: Literal[""] = "", *, from_coords: Iterable[int] | None = None) -> PhotoImage: ... + def copy_replace( + self, + sourceImage: PhotoImage | str, + *, + from_coords: Iterable[int] | None = None, + to: Iterable[int] | None = None, + shrink: bool = False, + zoom: int | tuple[int, int] | list[int] | None = None, + subsample: int | tuple[int, int] | list[int] | None = None, + # `None` defaults to overlay. + compositingrule: Literal["overlay", "set"] | None = None, + ) -> None: ... + else: + def copy(self) -> PhotoImage: ... + def zoom(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + def subsample(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + def get(self, x: int, y: int) -> tuple[int, int, int]: ... def put( self, @@ -3348,7 +3372,44 @@ class PhotoImage(Image, _PhotoImageLike): ), to: tuple[int, int] | None = None, ) -> None: ... - def write(self, filename: StrOrBytesPath, format: str | None = None, from_coords: tuple[int, int] | None = None) -> None: ... + if sys.version_info >= (3, 13): + def read( + self, + filename: StrOrBytesPath, + format: str | None = None, + *, + from_coords: Iterable[int] | None = None, + to: Iterable[int] | None = None, + shrink: bool = False, + ) -> None: ... + def write( + self, + filename: StrOrBytesPath, + format: str | None = None, + from_coords: Iterable[int] | None = None, + *, + background: str | None = None, + grayscale: bool = False, + ) -> None: ... + @overload + def data( + self, format: str, *, from_coords: Iterable[int] | None = None, background: str | None = None, grayscale: bool = False + ) -> bytes: ... + @overload + def data( + self, + format: None = None, + *, + from_coords: Iterable[int] | None = None, + background: str | None = None, + grayscale: bool = False, + ) -> tuple[str, ...]: ... + + else: + def write( + self, filename: StrOrBytesPath, format: str | None = None, from_coords: tuple[int, int] | None = None + ) -> None: ... + def transparency_get(self, x: int, y: int) -> bool: ... def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/constants.pyi b/mypy/typeshed/stdlib/tkinter/constants.pyi index 74fa72acb0bf..0b497f3a42e4 100644 --- a/mypy/typeshed/stdlib/tkinter/constants.pyi +++ b/mypy/typeshed/stdlib/tkinter/constants.pyi @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Final # These are not actually bools. See #4669 NO: bool @@ -7,74 +7,74 @@ TRUE: bool FALSE: bool ON: bool OFF: bool -N: Literal["n"] -S: Literal["s"] -W: Literal["w"] -E: Literal["e"] -NW: Literal["nw"] -SW: Literal["sw"] -NE: Literal["ne"] -SE: Literal["se"] -NS: Literal["ns"] -EW: Literal["ew"] -NSEW: Literal["nsew"] -CENTER: Literal["center"] -NONE: Literal["none"] -X: Literal["x"] -Y: Literal["y"] -BOTH: Literal["both"] -LEFT: Literal["left"] -TOP: Literal["top"] -RIGHT: Literal["right"] -BOTTOM: Literal["bottom"] -RAISED: Literal["raised"] -SUNKEN: Literal["sunken"] -FLAT: Literal["flat"] -RIDGE: Literal["ridge"] -GROOVE: Literal["groove"] -SOLID: Literal["solid"] -HORIZONTAL: Literal["horizontal"] -VERTICAL: Literal["vertical"] -NUMERIC: Literal["numeric"] -CHAR: Literal["char"] -WORD: Literal["word"] -BASELINE: Literal["baseline"] -INSIDE: Literal["inside"] -OUTSIDE: Literal["outside"] -SEL: Literal["sel"] -SEL_FIRST: Literal["sel.first"] -SEL_LAST: Literal["sel.last"] -END: Literal["end"] -INSERT: Literal["insert"] -CURRENT: Literal["current"] -ANCHOR: Literal["anchor"] -ALL: Literal["all"] -NORMAL: Literal["normal"] -DISABLED: Literal["disabled"] -ACTIVE: Literal["active"] -HIDDEN: Literal["hidden"] -CASCADE: Literal["cascade"] -CHECKBUTTON: Literal["checkbutton"] -COMMAND: Literal["command"] -RADIOBUTTON: Literal["radiobutton"] -SEPARATOR: Literal["separator"] -SINGLE: Literal["single"] -BROWSE: Literal["browse"] -MULTIPLE: Literal["multiple"] -EXTENDED: Literal["extended"] -DOTBOX: Literal["dotbox"] -UNDERLINE: Literal["underline"] -PIESLICE: Literal["pieslice"] -CHORD: Literal["chord"] -ARC: Literal["arc"] -FIRST: Literal["first"] -LAST: Literal["last"] -BUTT: Literal["butt"] -PROJECTING: Literal["projecting"] -ROUND: Literal["round"] -BEVEL: Literal["bevel"] -MITER: Literal["miter"] -MOVETO: Literal["moveto"] -SCROLL: Literal["scroll"] -UNITS: Literal["units"] -PAGES: Literal["pages"] +N: Final = "n" +S: Final = "s" +W: Final = "w" +E: Final = "e" +NW: Final = "nw" +SW: Final = "sw" +NE: Final = "ne" +SE: Final = "se" +NS: Final = "ns" +EW: Final = "ew" +NSEW: Final = "nsew" +CENTER: Final = "center" +NONE: Final = "none" +X: Final = "x" +Y: Final = "y" +BOTH: Final = "both" +LEFT: Final = "left" +TOP: Final = "top" +RIGHT: Final = "right" +BOTTOM: Final = "bottom" +RAISED: Final = "raised" +SUNKEN: Final = "sunken" +FLAT: Final = "flat" +RIDGE: Final = "ridge" +GROOVE: Final = "groove" +SOLID: Final = "solid" +HORIZONTAL: Final = "horizontal" +VERTICAL: Final = "vertical" +NUMERIC: Final = "numeric" +CHAR: Final = "char" +WORD: Final = "word" +BASELINE: Final = "baseline" +INSIDE: Final = "inside" +OUTSIDE: Final = "outside" +SEL: Final = "sel" +SEL_FIRST: Final = "sel.first" +SEL_LAST: Final = "sel.last" +END: Final = "end" +INSERT: Final = "insert" +CURRENT: Final = "current" +ANCHOR: Final = "anchor" +ALL: Final = "all" +NORMAL: Final = "normal" +DISABLED: Final = "disabled" +ACTIVE: Final = "active" +HIDDEN: Final = "hidden" +CASCADE: Final = "cascade" +CHECKBUTTON: Final = "checkbutton" +COMMAND: Final = "command" +RADIOBUTTON: Final = "radiobutton" +SEPARATOR: Final = "separator" +SINGLE: Final = "single" +BROWSE: Final = "browse" +MULTIPLE: Final = "multiple" +EXTENDED: Final = "extended" +DOTBOX: Final = "dotbox" +UNDERLINE: Final = "underline" +PIESLICE: Final = "pieslice" +CHORD: Final = "chord" +ARC: Final = "arc" +FIRST: Final = "first" +LAST: Final = "last" +BUTT: Final = "butt" +PROJECTING: Final = "projecting" +ROUND: Final = "round" +BEVEL: Final = "bevel" +MITER: Final = "miter" +MOVETO: Final = "moveto" +SCROLL: Final = "scroll" +UNITS: Final = "units" +PAGES: Final = "pages" diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 46625014d4ac..317f3068be63 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -1,16 +1,16 @@ import _tkinter import sys import tkinter -from typing import Any, Literal, TypedDict, overload +from typing import Any, Final, Literal, TypedDict, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 9): __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] -NORMAL: Literal["normal"] -ROMAN: Literal["roman"] -BOLD: Literal["bold"] -ITALIC: Literal["italic"] +NORMAL: Final = "normal" +ROMAN: Final = "roman" +BOLD: Final = "bold" +ITALIC: Final = "italic" _FontDescription: TypeAlias = ( str # "Helvetica 12" diff --git a/mypy/typeshed/stdlib/tkinter/tix.pyi b/mypy/typeshed/stdlib/tkinter/tix.pyi index 73649de427e8..7891364fa02c 100644 --- a/mypy/typeshed/stdlib/tkinter/tix.pyi +++ b/mypy/typeshed/stdlib/tkinter/tix.pyi @@ -1,38 +1,38 @@ import tkinter from _typeshed import Incomplete -from typing import Any, Literal - -WINDOW: Literal["window"] -TEXT: Literal["text"] -STATUS: Literal["status"] -IMMEDIATE: Literal["immediate"] -IMAGE: Literal["image"] -IMAGETEXT: Literal["imagetext"] -BALLOON: Literal["balloon"] -AUTO: Literal["auto"] -ACROSSTOP: Literal["acrosstop"] - -ASCII: Literal["ascii"] -CELL: Literal["cell"] -COLUMN: Literal["column"] -DECREASING: Literal["decreasing"] -INCREASING: Literal["increasing"] -INTEGER: Literal["integer"] -MAIN: Literal["main"] -MAX: Literal["max"] -REAL: Literal["real"] -ROW: Literal["row"] -S_REGION: Literal["s-region"] -X_REGION: Literal["x-region"] -Y_REGION: Literal["y-region"] +from typing import Any, Final + +WINDOW: Final = "window" +TEXT: Final = "text" +STATUS: Final = "status" +IMMEDIATE: Final = "immediate" +IMAGE: Final = "image" +IMAGETEXT: Final = "imagetext" +BALLOON: Final = "balloon" +AUTO: Final = "auto" +ACROSSTOP: Final = "acrosstop" + +ASCII: Final = "ascii" +CELL: Final = "cell" +COLUMN: Final = "column" +DECREASING: Final = "decreasing" +INCREASING: Final = "increasing" +INTEGER: Final = "integer" +MAIN: Final = "main" +MAX: Final = "max" +REAL: Final = "real" +ROW: Final = "row" +S_REGION: Final = "s-region" +X_REGION: Final = "x-region" +Y_REGION: Final = "y-region" # These should be kept in sync with _tkinter constants, except TCL_ALL_EVENTS which doesn't match ALL_EVENTS -TCL_DONT_WAIT: Literal[2] -TCL_WINDOW_EVENTS: Literal[4] -TCL_FILE_EVENTS: Literal[8] -TCL_TIMER_EVENTS: Literal[16] -TCL_IDLE_EVENTS: Literal[32] -TCL_ALL_EVENTS: Literal[0] +TCL_DONT_WAIT: Final = 2 +TCL_WINDOW_EVENTS: Final = 4 +TCL_FILE_EVENTS: Final = 8 +TCL_TIMER_EVENTS: Final = 16 +TCL_IDLE_EVENTS: Final = 32 +TCL_ALL_EVENTS: Final = 0 class tixCommand: def tix_addbitmapdir(self, directory: str) -> None: ... diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index d32647a55cb5..04390f119195 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -27,7 +27,18 @@ class CoverageResults: outfile: StrPath | None = None, ) -> None: ... # undocumented def update(self, other: CoverageResults) -> None: ... - def write_results(self, show_missing: bool = True, summary: bool = False, coverdir: StrPath | None = None) -> None: ... + if sys.version_info >= (3, 13): + def write_results( + self, + show_missing: bool = True, + summary: bool = False, + coverdir: StrPath | None = None, + *, + ignore_missing_files: bool = False, + ) -> None: ... + else: + def write_results(self, show_missing: bool = True, summary: bool = False, coverdir: StrPath | None = None) -> None: ... + def write_results_file( self, path: StrPath, lines: Sequence[str], lnotab: Any, lines_hit: Mapping[int, int], encoding: str | None = None ) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index fd0723fd73ed..199feee746cb 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -101,7 +101,6 @@ __all__ = [ "setheading", "setpos", "setposition", - "settiltangle", "setundobuffer", "setx", "sety", @@ -132,6 +131,9 @@ __all__ = [ if sys.version_info >= (3, 12): __all__ += ["teleport"] +if sys.version_info < (3, 13): + __all__ += ["settiltangle"] + # Note: '_Color' is the alias we use for arguments and _AnyColor is the # alias we use for return types. Really, these two aliases should be the # same, but as per the "no union returns" typeshed policy, we'll return @@ -399,7 +401,10 @@ class RawTurtle(TPen, TNavigator): self, t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None ) -> None: ... def get_shapepoly(self) -> _PolygonCoords | None: ... - def settiltangle(self, angle: float) -> None: ... + + if sys.version_info < (3, 13): + def settiltangle(self, angle: float) -> None: ... + @overload def tiltangle(self, angle: None = None) -> float: ... @overload @@ -672,7 +677,10 @@ def shapetransform( t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None ) -> None: ... def get_shapepoly() -> _PolygonCoords | None: ... -def settiltangle(angle: float) -> None: ... + +if sys.version_info < (3, 13): + def settiltangle(angle: float) -> None: ... + @overload def tiltangle(angle: None = None) -> float: ... @overload diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 9e9dc56b8529..1e3eacd9f1fa 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -245,7 +245,7 @@ class CodeType: co_qualname: str = ..., co_linetable: bytes = ..., co_exceptiontable: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... elif sys.version_info >= (3, 10): def replace( self, @@ -266,7 +266,7 @@ class CodeType: co_filename: str = ..., co_name: str = ..., co_linetable: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... else: def replace( self, @@ -287,7 +287,10 @@ class CodeType: co_filename: str = ..., co_name: str = ..., co_lnotab: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... + + if sys.version_info >= (3, 13): + __replace__ = replace @final class MappingProxyType(Mapping[_KT, _VT_co]): @@ -301,6 +304,10 @@ class MappingProxyType(Mapping[_KT, _VT_co]): def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... def items(self) -> ItemsView[_KT, _VT_co]: ... + @overload + def get(self, key: _KT, /) -> _VT_co | None: ... # type: ignore[override] + @overload + def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ... # type: ignore[override] if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT]: ... @@ -309,11 +316,17 @@ class MappingProxyType(Mapping[_KT, _VT_co]): class SimpleNamespace: __hash__: ClassVar[None] # type: ignore[assignment] - def __init__(self, **kwargs: Any) -> None: ... + if sys.version_info >= (3, 13): + def __init__(self, mapping_or_iterable: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), /, **kwargs: Any) -> None: ... + else: + def __init__(self, **kwargs: Any) -> None: ... + def __eq__(self, value: object, /) -> bool: ... def __getattribute__(self, name: str, /) -> Any: ... def __setattr__(self, name: str, value: Any, /) -> None: ... def __delattr__(self, name: str, /) -> None: ... + if sys.version_info >= (3, 13): + def __replace__(self, **kwargs: Any) -> Self: ... class ModuleType: __name__: str diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 92427f91f022..f4de1fa86de5 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -542,16 +542,18 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): def __anext__(self) -> Awaitable[_YieldT_co]: ... @abstractmethod - def asend(self, value: _SendT_contra, /) -> Awaitable[_YieldT_co]: ... + def asend(self, value: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @overload @abstractmethod def athrow( self, typ: type[BaseException], val: BaseException | object = None, tb: TracebackType | None = None, / - ) -> Awaitable[_YieldT_co]: ... + ) -> Coroutine[Any, Any, _YieldT_co]: ... @overload @abstractmethod - def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> Awaitable[_YieldT_co]: ... - def aclose(self) -> Awaitable[None]: ... + def athrow( + self, typ: BaseException, val: None = None, tb: TracebackType | None = None, / + ) -> Coroutine[Any, Any, _YieldT_co]: ... + def aclose(self) -> Coroutine[Any, Any, None]: ... @property def ag_await(self) -> Any: ... @property @@ -1054,7 +1056,7 @@ if sys.version_info >= (3, 12): # It's writable on types, but not on instances of TypeAliasType. @property def __module__(self) -> str | None: ... # type: ignore[override] - def __getitem__(self, parameters: Any) -> Any: ... + def __getitem__(self, parameters: Any) -> GenericAlias: ... def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index a7d2b2c2e083..1e4f90a0a722 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -403,6 +403,7 @@ else: # It's writable on types, but not on instances of TypeAliasType. @property def __module__(self) -> str | None: ... # type: ignore[override] + # Returns typing._GenericAlias, which isn't stubbed. def __getitem__(self, parameters: Any) -> Any: ... if sys.version_info >= (3, 10): def __or__(self, right: Any) -> _SpecialForm: ... diff --git a/mypy/typeshed/stdlib/unittest/__init__.pyi b/mypy/typeshed/stdlib/unittest/__init__.pyi index f2532ccf7fd8..546ea77bb4ca 100644 --- a/mypy/typeshed/stdlib/unittest/__init__.pyi +++ b/mypy/typeshed/stdlib/unittest/__init__.pyi @@ -11,13 +11,7 @@ from .case import ( skipIf as skipIf, skipUnless as skipUnless, ) -from .loader import ( - TestLoader as TestLoader, - defaultTestLoader as defaultTestLoader, - findTestCases as findTestCases, - getTestCaseNames as getTestCaseNames, - makeSuite as makeSuite, -) +from .loader import TestLoader as TestLoader, defaultTestLoader as defaultTestLoader from .main import TestProgram as TestProgram, main as main from .result import TestResult as TestResult from .runner import TextTestResult as TextTestResult, TextTestRunner as TextTestRunner @@ -52,12 +46,14 @@ __all__ = [ "registerResult", "removeResult", "removeHandler", - "getTestCaseNames", - "makeSuite", - "findTestCases", "addModuleCleanup", ] +if sys.version_info < (3, 13): + from .loader import findTestCases as findTestCases, getTestCaseNames as getTestCaseNames, makeSuite as makeSuite + + __all__ += ["getTestCaseNames", "makeSuite", "findTestCases"] + if sys.version_info >= (3, 11): __all__ += ["enterModuleContext", "doModuleCleanups"] diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index 12d6ef49e828..565dd91c0fda 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -1,4 +1,5 @@ import sys +from asyncio.events import AbstractEventLoop from collections.abc import Awaitable, Callable from typing import TypeVar from typing_extensions import ParamSpec @@ -12,6 +13,9 @@ _T = TypeVar("_T") _P = ParamSpec("_P") class IsolatedAsyncioTestCase(TestCase): + if sys.version_info >= (3, 13): + loop_factory: Callable[[], AbstractEventLoop] | None = None + async def asyncSetUp(self) -> None: ... async def asyncTearDown(self) -> None: ... def addAsyncCleanup(self, func: Callable[_P, Awaitable[object]], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index 202309ac1d93..657f3d6dca71 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -5,7 +5,7 @@ from collections.abc import Callable, Sequence from re import Pattern from types import ModuleType from typing import Any -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated _SortComparisonMethod: TypeAlias = Callable[[str, str], int] _SuiteClass: TypeAlias = Callable[[list[unittest.case.TestCase]], unittest.suite.TestSuite] @@ -34,18 +34,22 @@ class TestLoader: defaultTestLoader: TestLoader -def getTestCaseNames( - testCaseClass: type[unittest.case.TestCase], - prefix: str, - sortUsing: _SortComparisonMethod = ..., - testNamePatterns: list[str] | None = None, -) -> Sequence[str]: ... -def makeSuite( - testCaseClass: type[unittest.case.TestCase], - prefix: str = "test", - sortUsing: _SortComparisonMethod = ..., - suiteClass: _SuiteClass = ..., -) -> unittest.suite.TestSuite: ... -def findTestCases( - module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ... -) -> unittest.suite.TestSuite: ... +if sys.version_info < (3, 13): + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def getTestCaseNames( + testCaseClass: type[unittest.case.TestCase], + prefix: str, + sortUsing: _SortComparisonMethod = ..., + testNamePatterns: list[str] | None = None, + ) -> Sequence[str]: ... + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def makeSuite( + testCaseClass: type[unittest.case.TestCase], + prefix: str = "test", + sortUsing: _SortComparisonMethod = ..., + suiteClass: _SuiteClass = ..., + ) -> unittest.suite.TestSuite: ... + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def findTestCases( + module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ... + ) -> unittest.suite.TestSuite: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 55bc1ec741db..3eb3d1612a3c 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -6,6 +6,7 @@ import unittest.suite from collections.abc import Iterable from types import ModuleType from typing import Any, Protocol +from typing_extensions import deprecated MAIN_EXAMPLES: str MODULE_EXAMPLES: str @@ -61,7 +62,10 @@ class TestProgram: tb_locals: bool = False, ) -> None: ... - def usageExit(self, msg: Any = None) -> None: ... + if sys.version_info < (3, 13): + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def usageExit(self, msg: Any = None) -> None: ... + def parseArgs(self, argv: list[str]) -> None: ... def createTests(self, from_discovery: bool = False, Loader: unittest.loader.TestLoader | None = None) -> None: ... def runTests(self) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index dd61b83a658a..84620b7f3889 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -12,23 +12,44 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _AF = TypeVar("_AF", bound=Callable[..., Coroutine[Any, Any, Any]]) _P = ParamSpec("_P") -__all__ = ( - "Mock", - "MagicMock", - "patch", - "sentinel", - "DEFAULT", - "ANY", - "call", - "create_autospec", - "AsyncMock", - "FILTER_DIR", - "NonCallableMock", - "NonCallableMagicMock", - "mock_open", - "PropertyMock", - "seal", -) +if sys.version_info >= (3, 13): + # ThreadingMock added in 3.13 + __all__ = ( + "Mock", + "MagicMock", + "patch", + "sentinel", + "DEFAULT", + "ANY", + "call", + "create_autospec", + "ThreadingMock", + "AsyncMock", + "FILTER_DIR", + "NonCallableMock", + "NonCallableMagicMock", + "mock_open", + "PropertyMock", + "seal", + ) +else: + __all__ = ( + "Mock", + "MagicMock", + "patch", + "sentinel", + "DEFAULT", + "ANY", + "call", + "create_autospec", + "AsyncMock", + "FILTER_DIR", + "NonCallableMock", + "NonCallableMagicMock", + "mock_open", + "PropertyMock", + "seal", + ) if sys.version_info < (3, 9): __version__: Final[str] @@ -124,7 +145,6 @@ class NonCallableMock(Base, Any): def __delattr__(self, name: str) -> None: ... def __setattr__(self, name: str, value: Any) -> None: ... def __dir__(self) -> list[str]: ... - def _calls_repr(self, prefix: str = "Calls") -> str: ... def assert_called_with(self, *args: Any, **kwargs: Any) -> None: ... def assert_not_called(self) -> None: ... def assert_called_once_with(self, *args: Any, **kwargs: Any) -> None: ... @@ -150,6 +170,10 @@ class NonCallableMock(Base, Any): def _format_mock_call_signature(self, args: Any, kwargs: Any) -> str: ... def _call_matcher(self, _call: tuple[_Call, ...]) -> _Call: ... def _get_child_mock(self, **kw: Any) -> NonCallableMock: ... + if sys.version_info >= (3, 13): + def _calls_repr(self) -> str: ... + else: + def _calls_repr(self, prefix: str = "Calls") -> str: ... class CallableMixin(Base): side_effect: Any @@ -427,4 +451,16 @@ class PropertyMock(Mock): def __get__(self, obj: _T, obj_type: type[_T] | None = None) -> Self: ... def __set__(self, obj: Any, val: Any) -> None: ... +if sys.version_info >= (3, 13): + class ThreadingMixin(Base): + DEFAULT_TIMEOUT: Final[float | None] = None + + def __init__(self, /, *args: Any, timeout: float | None | _SentinelObject = ..., **kwargs: Any) -> None: ... + # Same as `NonCallableMock.reset_mock.` + def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... + def wait_until_called(self, *, timeout: float | None | _SentinelObject = ...) -> None: ... + def wait_until_any_call_with(self, *args: Any, **kwargs: Any) -> None: ... + + class ThreadingMock(ThreadingMixin, MagicMixin, Mock): ... + def seal(mock: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index 539a8f2379c1..c7ab1cb091dd 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -21,8 +21,10 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") _W = TypeVar("_W", bound=list[WarningMessage] | None) -_ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] - +if sys.version_info >= (3, 14): + _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] +else: + _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "all", "module", "once"] filters: Sequence[tuple[str, str | None, type[Warning], str | None, int]] # undocumented, do not mutate def showwarning( diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index 9137f1e47643..9319d5347c79 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import ReadableBuffer, Unused -from typing import IO, Any, BinaryIO, Literal, NamedTuple, NoReturn, overload +from typing import IO, Any, BinaryIO, Final, Literal, NamedTuple, NoReturn, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 9): @@ -12,7 +12,7 @@ _File: TypeAlias = str | IO[bytes] class Error(Exception): ... -WAVE_FORMAT_PCM: Literal[1] +WAVE_FORMAT_PCM: Final = 1 class _wave_params(NamedTuple): nchannels: int diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index 2b3f978c814b..d7bf033172f6 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -2,6 +2,7 @@ import sys from abc import abstractmethod from collections.abc import Callable, Sequence from typing import Literal +from typing_extensions import deprecated __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] @@ -62,8 +63,10 @@ if sys.platform == "win32": def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... if sys.platform == "darwin": - class MacOSX(BaseBrowser): - def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... + if sys.version_info < (3, 13): + @deprecated("Deprecated in 3.11, to be removed in 3.13.") + class MacOSX(BaseBrowser): + def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... class MacOSXOSAScript(BaseBrowser): # In runtime this class does not have `name` and `basename` if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi index bacc5302826f..a20e81f94f98 100644 --- a/mypy/typeshed/stdlib/winsound.pyi +++ b/mypy/typeshed/stdlib/winsound.pyi @@ -1,24 +1,24 @@ import sys from _typeshed import ReadableBuffer -from typing import Literal, overload +from typing import Final, Literal, overload if sys.platform == "win32": - SND_APPLICATION: Literal[128] - SND_FILENAME: Literal[131072] - SND_ALIAS: Literal[65536] - SND_LOOP: Literal[8] - SND_MEMORY: Literal[4] - SND_PURGE: Literal[64] - SND_ASYNC: Literal[1] - SND_NODEFAULT: Literal[2] - SND_NOSTOP: Literal[16] - SND_NOWAIT: Literal[8192] + SND_APPLICATION: Final = 128 + SND_FILENAME: Final = 131072 + SND_ALIAS: Final = 65536 + SND_LOOP: Final = 8 + SND_MEMORY: Final = 4 + SND_PURGE: Final = 64 + SND_ASYNC: Final = 1 + SND_NODEFAULT: Final = 2 + SND_NOSTOP: Final = 16 + SND_NOWAIT: Final = 8192 - MB_ICONASTERISK: Literal[64] - MB_ICONEXCLAMATION: Literal[48] - MB_ICONHAND: Literal[16] - MB_ICONQUESTION: Literal[32] - MB_OK: Literal[0] + MB_ICONASTERISK: Final = 64 + MB_ICONEXCLAMATION: Final = 48 + MB_ICONHAND: Final = 16 + MB_ICONQUESTION: Final = 32 + MB_OK: Final = 0 def Beep(frequency: int, duration: int) -> None: ... # Can actually accept anything ORed with 4, and if not it's definitely str, but that's inexpressible @overload diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index 95436ab5dd38..50250de5cb2f 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,20 +1,20 @@ import sys from _typeshed import Incomplete, SupportsRead from collections.abc import Sequence -from typing import Literal +from typing import Final, Literal from typing_extensions import TypeAlias from xml.dom.minidom import Document, DOMImplementation, Element, Text from xml.sax.handler import ContentHandler from xml.sax.xmlreader import XMLReader -START_ELEMENT: Literal["START_ELEMENT"] -END_ELEMENT: Literal["END_ELEMENT"] -COMMENT: Literal["COMMENT"] -START_DOCUMENT: Literal["START_DOCUMENT"] -END_DOCUMENT: Literal["END_DOCUMENT"] -PROCESSING_INSTRUCTION: Literal["PROCESSING_INSTRUCTION"] -IGNORABLE_WHITESPACE: Literal["IGNORABLE_WHITESPACE"] -CHARACTERS: Literal["CHARACTERS"] +START_ELEMENT: Final = "START_ELEMENT" +END_ELEMENT: Final = "END_ELEMENT" +COMMENT: Final = "COMMENT" +START_DOCUMENT: Final = "START_DOCUMENT" +END_DOCUMENT: Final = "END_DOCUMENT" +PROCESSING_INSTRUCTION: Final = "PROCESSING_INSTRUCTION" +IGNORABLE_WHITESPACE: Final = "IGNORABLE_WHITESPACE" +CHARACTERS: Final = "CHARACTERS" _DocumentFactory: TypeAlias = DOMImplementation | None _Node: TypeAlias = Document | Element | Text diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 9198bd3322d9..4849b0ea1c35 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -239,9 +239,15 @@ if sys.version_info >= (3, 9): def indent(tree: Element | ElementTree, space: str = " ", level: int = 0) -> None: ... def parse(source: _FileRead, parser: XMLParser | None = None) -> ElementTree: ... -def iterparse( - source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None -) -> Iterator[tuple[str, Any]]: ... + +class _IterParseIterator(Iterator[tuple[str, Any]]): + def __next__(self) -> tuple[str, Any]: ... + if sys.version_info >= (3, 13): + def close(self) -> None: ... + if sys.version_info >= (3, 11): + def __del__(self) -> None: ... + +def iterparse(source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None) -> _IterParseIterator: ... class XMLPullParser: def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index b61e07f8b90d..aa52a0b56e41 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -206,6 +206,9 @@ class ZipInfo: compress_size: int file_size: int orig_filename: str # undocumented + if sys.version_info >= (3, 13): + compress_level: int | None + def __init__(self, filename: str = "NoName", date_time: _DateTuple = (1980, 1, 1, 0, 0, 0)) -> None: ... @classmethod def from_file(cls, filename: StrPath, arcname: StrPath | None = None, *, strict_timestamps: bool = True) -> Self: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi index 0398824e1fd2..bafbbeeb0d0b 100644 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -3,12 +3,14 @@ from _typeshed import StrPath from collections.abc import Iterator, Sequence from io import TextIOWrapper from os import PathLike -from typing import IO, Literal, overload +from typing import IO, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias from zipfile import ZipFile _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] +_ZF = TypeVar("_ZF", bound=ZipFile) + if sys.version_info >= (3, 12): class InitializedState: def __init__(self, *args: object, **kwargs: object) -> None: ... @@ -23,6 +25,9 @@ if sys.version_info >= (3, 12): @overload @classmethod def make(cls, source: StrPath | IO[bytes]) -> Self: ... + if sys.version_info >= (3, 13): + @classmethod + def inject(cls, zf: _ZF) -> _ZF: ... class Path: root: CompleteDirs diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index 234770172d40..2f6c40656038 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -1,29 +1,29 @@ import sys from _typeshed import ReadableBuffer -from typing import Literal +from typing import Final -DEFLATED: Literal[8] +DEFLATED: Final = 8 DEF_MEM_LEVEL: int # can change -DEF_BUF_SIZE: Literal[16384] +DEF_BUF_SIZE: Final = 16384 MAX_WBITS: int ZLIB_VERSION: str # can change ZLIB_RUNTIME_VERSION: str # can change -Z_NO_COMPRESSION: Literal[0] -Z_PARTIAL_FLUSH: Literal[1] -Z_BEST_COMPRESSION: Literal[9] -Z_BEST_SPEED: Literal[1] -Z_BLOCK: Literal[5] -Z_DEFAULT_COMPRESSION: Literal[-1] -Z_DEFAULT_STRATEGY: Literal[0] -Z_FILTERED: Literal[1] -Z_FINISH: Literal[4] -Z_FIXED: Literal[4] -Z_FULL_FLUSH: Literal[3] -Z_HUFFMAN_ONLY: Literal[2] -Z_NO_FLUSH: Literal[0] -Z_RLE: Literal[3] -Z_SYNC_FLUSH: Literal[2] -Z_TREES: Literal[6] +Z_NO_COMPRESSION: Final = 0 +Z_PARTIAL_FLUSH: Final = 1 +Z_BEST_COMPRESSION: Final = 9 +Z_BEST_SPEED: Final = 1 +Z_BLOCK: Final = 5 +Z_DEFAULT_COMPRESSION: Final = -1 +Z_DEFAULT_STRATEGY: Final = 0 +Z_FILTERED: Final = 1 +Z_FINISH: Final = 4 +Z_FIXED: Final = 4 +Z_FULL_FLUSH: Final = 3 +Z_HUFFMAN_ONLY: Final = 2 +Z_NO_FLUSH: Final = 0 +Z_RLE: Final = 3 +Z_SYNC_FLUSH: Final = 2 +Z_TREES: Final = 6 class error(Exception): ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 832e55f333de..b65a4cd59f79 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -286,7 +286,7 @@ f.write(b'x') f.foobar() [out] _program.py:3: error: Argument 1 to "write" of "TextIOBase" has incompatible type "bytes"; expected "str" -_program.py:4: error: "TextIOWrapper" has no attribute "foobar" +_program.py:4: error: "TextIOWrapper[_WrappedBuffer]" has no attribute "foobar" [case testOpenReturnTypeInference] reveal_type(open('x')) @@ -295,8 +295,8 @@ reveal_type(open('x', 'rb')) mode = 'rb' reveal_type(open('x', mode)) [out] -_program.py:1: note: Revealed type is "io.TextIOWrapper" -_program.py:2: note: Revealed type is "io.TextIOWrapper" +_program.py:1: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" +_program.py:2: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" _program.py:3: note: Revealed type is "io.BufferedReader" _program.py:5: note: Revealed type is "typing.IO[Any]" @@ -319,8 +319,8 @@ reveal_type(p.open('rb')) mode = 'rb' reveal_type(p.open(mode)) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper" -_program.py:4: note: Revealed type is "io.TextIOWrapper" +_program.py:3: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" +_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" _program.py:5: note: Revealed type is "io.BufferedReader" _program.py:7: note: Revealed type is "typing.IO[Any]" @@ -332,8 +332,8 @@ reveal_type(p.open(errors='replace', mode='r')) mode = 'r' reveal_type(p.open(mode=mode, errors='replace')) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper" -_program.py:4: note: Revealed type is "io.TextIOWrapper" +_program.py:3: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" +_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" _program.py:6: note: Revealed type is "typing.IO[Any]" [case testGenericPatterns] From 8b74b5ab03742ba86348dfe14d0468a995f74ee7 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Fri, 26 Jul 2024 16:08:45 -0700 Subject: [PATCH 0714/1617] Fix `RawExpressionType.accept` crash with `--cache-fine-grained` (#17588) Commit 1072c78ad375b7f0511549287f54432050396717 (#17148) converted all quoted types into `RawExpressionType`, which raised an `AssertionError` when `accept`ing a `TypeTriggersVisitor`. - Fixes #17574. - Fixes #17587. Signed-off-by: Anders Kaseorg --- mypy/types.py | 2 ++ test-data/unit/check-typeddict.test | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/mypy/types.py b/mypy/types.py index 52b3121f9fb3..c29d686ce5d8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2705,6 +2705,8 @@ def simple_name(self) -> str: return self.base_type_name.replace("builtins.", "") def accept(self, visitor: TypeVisitor[T]) -> T: + if self.node is not None: + return self.node.accept(visitor) assert isinstance(visitor, SyntheticTypeVisitor) ret: T = visitor.visit_raw_expression_type(self) return ret diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 6a5120159c2d..1ef08f825e7a 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1442,6 +1442,18 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'a': TypedDict('_ reveal_type(x['a']['b']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[case testTypedDictForwardReferenceCacheFineGrained] +# flags: --cache-fine-grained +from mypy_extensions import TypedDict +class A(TypedDict): + b: "B" +class B(TypedDict): + c: "C" +class C(TypedDict): + d: "D" +class D: + pass + [case testSelfRecursiveTypedDictInheriting] from mypy_extensions import TypedDict From a0dbbd5b462136914bb7a378221ae094b6844710 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:39:58 -0700 Subject: [PATCH 0715/1617] Sync typeshed (#17619) Source commit: https://github.com/python/typeshed/commit/4ef2d66663fc080fefa379e6ae5fc45d4f8b54eb --- mypy/typeshed/stdlib/_curses.pyi | 17 +- mypy/typeshed/stdlib/_decimal.pyi | 28 +- mypy/typeshed/stdlib/_osx_support.pyi | 10 +- mypy/typeshed/stdlib/argparse.pyi | 2 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 6 +- mypy/typeshed/stdlib/email/charset.pyi | 8 +- mypy/typeshed/stdlib/gzip.pyi | 16 +- mypy/typeshed/stdlib/io.pyi | 2 +- .../stdlib/lib2to3/fixes/fix_asserts.pyi | 4 +- .../stdlib/lib2to3/fixes/fix_idioms.pyi | 6 +- .../stdlib/lib2to3/fixes/fix_imports.pyi | 4 +- .../stdlib/lib2to3/fixes/fix_imports2.pyi | 4 +- .../stdlib/lib2to3/fixes/fix_methodattrs.pyi | 4 +- .../stdlib/lib2to3/fixes/fix_renames.pyi | 6 +- .../stdlib/lib2to3/fixes/fix_urllib.pyi | 4 +- mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi | 126 +++---- mypy/typeshed/stdlib/logging/__init__.pyi | 22 +- mypy/typeshed/stdlib/logging/config.pyi | 6 +- mypy/typeshed/stdlib/logging/handlers.pyi | 14 +- mypy/typeshed/stdlib/modulefinder.pyi | 14 +- .../stdlib/multiprocessing/forkserver.pyi | 6 +- .../multiprocessing/popen_spawn_win32.pyi | 10 +- .../stdlib/multiprocessing/reduction.pyi | 2 +- .../typeshed/stdlib/multiprocessing/spawn.pyi | 6 +- mypy/typeshed/stdlib/multiprocessing/util.pyi | 18 +- mypy/typeshed/stdlib/optparse.pyi | 6 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 5 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 344 +++++++++--------- mypy/typeshed/stdlib/subprocess.pyi | 12 +- mypy/typeshed/stdlib/tty.pyi | 16 +- mypy/typeshed/stdlib/unittest/case.pyi | 17 +- mypy/typeshed/stdlib/unittest/loader.pyi | 4 +- mypy/typeshed/stdlib/unittest/main.pyi | 6 +- mypy/typeshed/stdlib/unittest/result.pyi | 6 +- mypy/typeshed/stdlib/unittest/util.pyi | 14 +- mypy/typeshed/stdlib/xmlrpc/client.pyi | 32 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 16 +- 37 files changed, 418 insertions(+), 405 deletions(-) diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index eb1d7b9bde9f..505637574af1 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -368,11 +368,7 @@ def tparm( ) -> bytes: ... def typeahead(fd: int, /) -> None: ... def unctrl(ch: _ChType, /) -> bytes: ... - -if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - def unget_wch(ch: int | str, /) -> None: ... - +def unget_wch(ch: int | str, /) -> None: ... def ungetch(ch: _ChType, /) -> None: ... def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ... def update_lines_cols() -> None: ... @@ -447,13 +443,10 @@ class _CursesWindow: def getch(self) -> int: ... @overload def getch(self, y: int, x: int) -> int: ... - if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - @overload - def get_wch(self) -> int | str: ... - @overload - def get_wch(self, y: int, x: int) -> int | str: ... - + @overload + def get_wch(self) -> int | str: ... + @overload + def get_wch(self, y: int, x: int) -> int | str: ... @overload def getkey(self) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 90d16215c280..937a04ac3799 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -17,20 +17,20 @@ class DecimalTuple(NamedTuple): digits: tuple[int, ...] exponent: int | Literal["n", "N", "F"] -ROUND_DOWN: str -ROUND_HALF_UP: str -ROUND_HALF_EVEN: str -ROUND_CEILING: str -ROUND_FLOOR: str -ROUND_UP: str -ROUND_HALF_DOWN: str -ROUND_05UP: str -HAVE_CONTEXTVAR: bool -HAVE_THREADS: bool -MAX_EMAX: int -MAX_PREC: int -MIN_EMIN: int -MIN_ETINY: int +ROUND_DOWN: Final[str] +ROUND_HALF_UP: Final[str] +ROUND_HALF_EVEN: Final[str] +ROUND_CEILING: Final[str] +ROUND_FLOOR: Final[str] +ROUND_UP: Final[str] +ROUND_HALF_DOWN: Final[str] +ROUND_05UP: Final[str] +HAVE_CONTEXTVAR: Final[bool] +HAVE_THREADS: Final[bool] +MAX_EMAX: Final[int] +MAX_PREC: Final[int] +MIN_EMIN: Final[int] +MIN_ETINY: Final[int] class DecimalException(ArithmeticError): ... class Clamped(DecimalException): ... diff --git a/mypy/typeshed/stdlib/_osx_support.pyi b/mypy/typeshed/stdlib/_osx_support.pyi index 64dbdd24fd40..fb00e6986dd0 100644 --- a/mypy/typeshed/stdlib/_osx_support.pyi +++ b/mypy/typeshed/stdlib/_osx_support.pyi @@ -1,5 +1,5 @@ from collections.abc import Iterable, Sequence -from typing import TypeVar +from typing import Final, TypeVar _T = TypeVar("_T") _K = TypeVar("_K") @@ -7,15 +7,15 @@ _V = TypeVar("_V") __all__ = ["compiler_fixup", "customize_config_vars", "customize_compiler", "get_platform_osx"] -_UNIVERSAL_CONFIG_VARS: tuple[str, ...] # undocumented -_COMPILER_CONFIG_VARS: tuple[str, ...] # undocumented -_INITPRE: str # undocumented +_UNIVERSAL_CONFIG_VARS: Final[tuple[str, ...]] # undocumented +_COMPILER_CONFIG_VARS: Final[tuple[str, ...]] # undocumented +_INITPRE: Final[str] # undocumented def _find_executable(executable: str, path: str | None = None) -> str | None: ... # undocumented def _read_output(commandstring: str, capture_stderr: bool = False) -> str | None: ... # undocumented def _find_build_tool(toolname: str) -> str: ... # undocumented -_SYSTEM_VERSION: str | None # undocumented +_SYSTEM_VERSION: Final[str | None] # undocumented def _get_system_version() -> str: ... # undocumented def _remove_original_values(_config_vars: dict[str, str]) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index d878f3ebd6a2..66fa4e15291f 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -51,7 +51,7 @@ _SUPPRESS_T = NewType("_SUPPRESS_T", str) SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is # the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy ZERO_OR_MORE: Final = "*" -_UNRECOGNIZED_ARGS_ATTR: str # undocumented +_UNRECOGNIZED_ARGS_ATTR: Final[str] # undocumented class ArgumentError(Exception): argument_name: str | None diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 4613bca70c1a..f23ecef126d6 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -429,7 +429,11 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... ) -> None: ... - def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... + if sys.version_info >= (3, 12): + def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ... + else: + def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... + def get_name(self) -> str: ... def set_name(self, value: object, /) -> None: ... if sys.version_info >= (3, 12): diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index 2d12df337207..2939192c9526 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,12 +1,12 @@ from collections.abc import Callable, Iterator from email.message import Message -from typing import overload +from typing import Final, overload __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] -QP: int # undocumented -BASE64: int # undocumented -SHORTEST: int # undocumented +QP: Final[int] # undocumented +BASE64: Final[int] # undocumented +SHORTEST: Final[int] # undocumented class Charset: input_charset: str diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 542945698bba..9b32008dcbf6 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -3,7 +3,7 @@ import sys import zlib from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath from io import FileIO -from typing import Literal, Protocol, TextIO, overload +from typing import Final, Literal, Protocol, TextIO, overload from typing_extensions import TypeAlias __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] @@ -12,14 +12,14 @@ _ReadBinaryMode: TypeAlias = Literal["r", "rb"] _WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"] _OpenTextMode: TypeAlias = Literal["rt", "at", "wt", "xt"] -READ: object # undocumented -WRITE: object # undocumented +READ: Final[object] # undocumented +WRITE: Final[object] # undocumented -FTEXT: int # actually Literal[1] # undocumented -FHCRC: int # actually Literal[2] # undocumented -FEXTRA: int # actually Literal[4] # undocumented -FNAME: int # actually Literal[8] # undocumented -FCOMMENT: int # actually Literal[16] # undocumented +FTEXT: Final[int] # actually Literal[1] # undocumented +FHCRC: Final[int] # actually Literal[2] # undocumented +FEXTRA: Final[int] # actually Literal[4] # undocumented +FNAME: Final[int] # actually Literal[8] # undocumented +FCOMMENT: Final[int] # actually Literal[16] # undocumented class _ReadableFileobj(Protocol): def read(self, n: int, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index a386a914ddcd..2d64d261951d 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -168,7 +168,7 @@ class _WrappedBuffer(Protocol): def writable(self) -> bool: ... def truncate(self, size: int, /) -> int: ... def fileno(self) -> int: ... - def isatty(self) -> int: ... + def isatty(self) -> bool: ... # Optional: Only needs to be present if seekable() returns True. # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... # def tell(self) -> int: ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi index fb0b472aa12a..1bf7db2f76e9 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi @@ -1,8 +1,8 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from ..fixer_base import BaseFix -NAMES: dict[str, str] +NAMES: Final[dict[str, str]] class FixAsserts(BaseFix): BM_compatible: ClassVar[Literal[False]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi index 4595c57c7eb9..6b2723d09d43 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi @@ -1,9 +1,9 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -CMP: str -TYPE: str +CMP: Final[str] +TYPE: Final[str] class FixIdioms(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[False]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi index dd6f72dd88ac..c747af529f44 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi @@ -1,11 +1,11 @@ from _typeshed import StrPath from collections.abc import Generator -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base from ..pytree import Node -MAPPING: dict[str, str] +MAPPING: Final[dict[str, str]] def alternates(members): ... def build_pattern(mapping=...) -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi index 8d55433085dd..618ecd0424d8 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi @@ -1,6 +1,8 @@ +from typing import Final + from . import fix_imports -MAPPING: dict[str, str] +MAPPING: Final[dict[str, str]] class FixImports2(fix_imports.FixImports): mapping = MAPPING diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi index 594b5e2c95c9..ca9b71e43f85 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi @@ -1,8 +1,8 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -MAP: dict[str, str] +MAP: Final[dict[str, str]] class FixMethodattrs(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi index 6283f1ab7ce2..652d8f15ea1a 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi @@ -1,10 +1,10 @@ from collections.abc import Generator -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -MAPPING: dict[str, dict[str, str]] -LOOKUP: dict[tuple[str, str], str] +MAPPING: Final[dict[str, dict[str, str]]] +LOOKUP: Final[dict[tuple[str, str], str]] def alternates(members): ... def build_pattern() -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi index 625472f609ab..abdcc0f62970 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi @@ -1,9 +1,9 @@ from collections.abc import Generator -from typing import Literal +from typing import Final, Literal from .fix_imports import FixImports -MAPPING: dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]] +MAPPING: Final[dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]]] def build_pattern() -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi index debcb2193987..6898517acee6 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi @@ -1,65 +1,67 @@ -ENDMARKER: int -NAME: int -NUMBER: int -STRING: int -NEWLINE: int -INDENT: int -DEDENT: int -LPAR: int -RPAR: int -LSQB: int -RSQB: int -COLON: int -COMMA: int -SEMI: int -PLUS: int -MINUS: int -STAR: int -SLASH: int -VBAR: int -AMPER: int -LESS: int -GREATER: int -EQUAL: int -DOT: int -PERCENT: int -BACKQUOTE: int -LBRACE: int -RBRACE: int -EQEQUAL: int -NOTEQUAL: int -LESSEQUAL: int -GREATEREQUAL: int -TILDE: int -CIRCUMFLEX: int -LEFTSHIFT: int -RIGHTSHIFT: int -DOUBLESTAR: int -PLUSEQUAL: int -MINEQUAL: int -STAREQUAL: int -SLASHEQUAL: int -PERCENTEQUAL: int -AMPEREQUAL: int -VBAREQUAL: int -CIRCUMFLEXEQUAL: int -LEFTSHIFTEQUAL: int -RIGHTSHIFTEQUAL: int -DOUBLESTAREQUAL: int -DOUBLESLASH: int -DOUBLESLASHEQUAL: int -OP: int -COMMENT: int -NL: int -RARROW: int -AT: int -ATEQUAL: int -AWAIT: int -ASYNC: int -ERRORTOKEN: int -COLONEQUAL: int -N_TOKENS: int -NT_OFFSET: int +from typing import Final + +ENDMARKER: Final[int] +NAME: Final[int] +NUMBER: Final[int] +STRING: Final[int] +NEWLINE: Final[int] +INDENT: Final[int] +DEDENT: Final[int] +LPAR: Final[int] +RPAR: Final[int] +LSQB: Final[int] +RSQB: Final[int] +COLON: Final[int] +COMMA: Final[int] +SEMI: Final[int] +PLUS: Final[int] +MINUS: Final[int] +STAR: Final[int] +SLASH: Final[int] +VBAR: Final[int] +AMPER: Final[int] +LESS: Final[int] +GREATER: Final[int] +EQUAL: Final[int] +DOT: Final[int] +PERCENT: Final[int] +BACKQUOTE: Final[int] +LBRACE: Final[int] +RBRACE: Final[int] +EQEQUAL: Final[int] +NOTEQUAL: Final[int] +LESSEQUAL: Final[int] +GREATEREQUAL: Final[int] +TILDE: Final[int] +CIRCUMFLEX: Final[int] +LEFTSHIFT: Final[int] +RIGHTSHIFT: Final[int] +DOUBLESTAR: Final[int] +PLUSEQUAL: Final[int] +MINEQUAL: Final[int] +STAREQUAL: Final[int] +SLASHEQUAL: Final[int] +PERCENTEQUAL: Final[int] +AMPEREQUAL: Final[int] +VBAREQUAL: Final[int] +CIRCUMFLEXEQUAL: Final[int] +LEFTSHIFTEQUAL: Final[int] +RIGHTSHIFTEQUAL: Final[int] +DOUBLESTAREQUAL: Final[int] +DOUBLESLASH: Final[int] +DOUBLESLASHEQUAL: Final[int] +OP: Final[int] +COMMENT: Final[int] +NL: Final[int] +RARROW: Final[int] +AT: Final[int] +ATEQUAL: Final[int] +AWAIT: Final[int] +ASYNC: Final[int] +ERRORTOKEN: Final[int] +COLONEQUAL: Final[int] +N_TOKENS: Final[int] +NT_OFFSET: Final[int] tok_name: dict[int, str] def ISTERMINAL(x: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 4c6163257236..e6e6e8f645a0 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -7,7 +7,7 @@ from re import Pattern from string import Template from time import struct_time from types import FrameType, TracebackType -from typing import Any, ClassVar, Generic, Literal, Protocol, TextIO, TypeVar, overload +from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): @@ -236,14 +236,14 @@ class Logger(Filterer): def hasHandlers(self) -> bool: ... def callHandlers(self, record: LogRecord) -> None: ... # undocumented -CRITICAL: int -FATAL: int -ERROR: int -WARNING: int -WARN: int -INFO: int -DEBUG: int -NOTSET: int +CRITICAL: Final = 50 +FATAL: Final = CRITICAL +ERROR: Final = 40 +WARNING: Final = 30 +WARN: Final = WARNING +INFO: Final = 20 +DEBUG: Final = 10 +NOTSET: Final = 0 class Handler(Filterer): level: int # undocumented @@ -684,6 +684,6 @@ class StrFormatStyle(PercentStyle): # undocumented class StringTemplateStyle(PercentStyle): # undocumented _tpl: Template -_STYLES: dict[str, tuple[PercentStyle, str]] +_STYLES: Final[dict[str, tuple[PercentStyle, str]]] -BASIC_FORMAT: str +BASIC_FORMAT: Final[str] diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 7a26846addbb..83fe7461cb5c 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -4,14 +4,14 @@ from collections.abc import Callable, Hashable, Iterable, Sequence from configparser import RawConfigParser from re import Pattern from threading import Thread -from typing import IO, Any, Literal, SupportsIndex, TypedDict, overload +from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload from typing_extensions import Required, TypeAlias from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level DEFAULT_LOGGING_CONFIG_PORT: int -RESET_ERROR: int # undocumented -IDENTIFIER: Pattern[str] # undocumented +RESET_ERROR: Final[int] # undocumented +IDENTIFIER: Final[Pattern[str]] # undocumented if sys.version_info >= (3, 11): class _RootLoggerConfiguration(TypedDict, total=False): diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index 4e97012abba1..91f9fe57e46f 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -8,16 +8,16 @@ from logging import FileHandler, Handler, LogRecord from re import Pattern from socket import SocketKind, socket from threading import Thread -from typing import Any, ClassVar, Protocol, TypeVar +from typing import Any, ClassVar, Final, Protocol, TypeVar _T = TypeVar("_T") -DEFAULT_TCP_LOGGING_PORT: int -DEFAULT_UDP_LOGGING_PORT: int -DEFAULT_HTTP_LOGGING_PORT: int -DEFAULT_SOAP_LOGGING_PORT: int -SYSLOG_UDP_PORT: int -SYSLOG_TCP_PORT: int +DEFAULT_TCP_LOGGING_PORT: Final[int] +DEFAULT_UDP_LOGGING_PORT: Final[int] +DEFAULT_HTTP_LOGGING_PORT: Final[int] +DEFAULT_SOAP_LOGGING_PORT: Final[int] +SYSLOG_UDP_PORT: Final[int] +SYSLOG_TCP_PORT: Final[int] class WatchedFileHandler(FileHandler): dev: int # undocumented diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 132cac5f1878..2cf948ba898a 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -1,15 +1,15 @@ import sys from collections.abc import Container, Iterable, Iterator, Sequence from types import CodeType -from typing import IO, Any +from typing import IO, Any, Final if sys.version_info < (3, 11): - LOAD_CONST: int # undocumented - IMPORT_NAME: int # undocumented - STORE_NAME: int # undocumented - STORE_GLOBAL: int # undocumented - STORE_OPS: tuple[int, int] # undocumented - EXTENDED_ARG: int # undocumented + LOAD_CONST: Final[int] # undocumented + IMPORT_NAME: Final[int] # undocumented + STORE_NAME: Final[int] # undocumented + STORE_GLOBAL: Final[int] # undocumented + STORE_OPS: Final[tuple[int, int]] # undocumented + EXTENDED_ARG: Final[int] # undocumented packagePathMap: dict[str, list[str]] # undocumented diff --git a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi index 9a15f2683b7d..31b982856355 100644 --- a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi @@ -1,12 +1,12 @@ from _typeshed import FileDescriptorLike, Unused from collections.abc import Sequence from struct import Struct -from typing import Any +from typing import Any, Final __all__ = ["ensure_running", "get_inherited_fds", "connect_to_new_process", "set_forkserver_preload"] -MAXFDS_TO_SEND: int -SIGNED_STRUCT: Struct +MAXFDS_TO_SEND: Final = 256 +SIGNED_STRUCT: Final[Struct] class ForkServer: def set_forkserver_preload(self, modules_names: list[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi b/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi index 3dc9d5bd7332..481b9eec5a37 100644 --- a/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi @@ -1,16 +1,16 @@ import sys from multiprocessing.process import BaseProcess -from typing import ClassVar +from typing import ClassVar, Final from .util import Finalize if sys.platform == "win32": __all__ = ["Popen"] - TERMINATE: int - WINEXE: bool - WINSERVICE: bool - WINENV: bool + TERMINATE: Final[int] + WINEXE: Final[bool] + WINSERVICE: Final[bool] + WINENV: Final[bool] class Popen: finalizer: Finalize diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index 6d23e20e6981..a31987bcc3cb 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -15,7 +15,7 @@ if sys.platform == "win32": else: __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupFd", "sendfds", "recvfds"] -HAVE_SEND_HANDLE: bool +HAVE_SEND_HANDLE: Final[bool] class ForkingPickler(pickle.Pickler): dispatch_table: _DispatchTableType diff --git a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi index 26ff165756bf..43ce2f07d996 100644 --- a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi @@ -1,6 +1,6 @@ from collections.abc import Mapping, Sequence from types import ModuleType -from typing import Any +from typing import Any, Final __all__ = [ "_main", @@ -12,8 +12,8 @@ __all__ = [ "import_main_path", ] -WINEXE: bool -WINSERVICE: bool +WINEXE: Final[bool] +WINSERVICE: Final[bool] def set_executable(exe: str) -> None: ... def get_executable() -> str: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index 790d6c7467f0..d5b6384afd5e 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -2,7 +2,7 @@ import threading from _typeshed import ConvertibleToInt, Incomplete, Unused from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence from logging import Logger, _Level as _LoggingLevel -from typing import Any, Generic, TypeVar, overload +from typing import Any, Final, Generic, TypeVar, overload __all__ = [ "sub_debug", @@ -25,14 +25,14 @@ __all__ = [ _T = TypeVar("_T") _R_co = TypeVar("_R_co", default=Any, covariant=True) -NOTSET: int -SUBDEBUG: int -DEBUG: int -INFO: int -SUBWARNING: int +NOTSET: Final[int] +SUBDEBUG: Final[int] +DEBUG: Final[int] +INFO: Final[int] +SUBWARNING: Final[int] -LOGGER_NAME: str -DEFAULT_LOGGING_FORMAT: str +LOGGER_NAME: Final[str] +DEFAULT_LOGGING_FORMAT: Final[str] def sub_debug(msg: object, *args: object) -> None: ... def debug(msg: object, *args: object) -> None: ... @@ -92,7 +92,7 @@ class ForkAwareThreadLock: class ForkAwareLocal(threading.local): ... -MAXFD: int +MAXFD: Final[int] def close_all_fds_except(fds: Iterable[int]) -> None: ... def spawnv_passfds(path: bytes, args: Sequence[ConvertibleToInt], passfds: Sequence[int]) -> int: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index a179c2d1bb3c..b513bb647060 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,7 +1,7 @@ from _typeshed import Incomplete from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, Literal, overload +from typing import IO, Any, AnyStr, Literal, NoReturn, overload __all__ = [ "Option", @@ -231,8 +231,8 @@ class OptionParser(OptionContainer): def check_values(self, values: Values, args: list[str]) -> tuple[Values, list[str]]: ... def disable_interspersed_args(self) -> None: ... def enable_interspersed_args(self) -> None: ... - def error(self, msg: str) -> None: ... - def exit(self, status: int = 0, msg: str | None = None) -> None: ... + def error(self, msg: str) -> NoReturn: ... + def exit(self, status: int = 0, msg: str | None = None) -> NoReturn: ... def expand_prog_name(self, s: str) -> str: ... def format_epilog(self, formatter: HelpFormatter) -> str: ... def format_help(self, formatter: HelpFormatter | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 88bf9464d130..64decd56bee6 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -1,10 +1,10 @@ from _typeshed import ReadableBuffer, SupportsRead from collections.abc import Callable from pyexpat import errors as errors, model as model -from typing import Any, final +from typing import Any, Final, final from typing_extensions import TypeAlias -EXPAT_VERSION: str # undocumented +EXPAT_VERSION: Final[str] # undocumented version_info: tuple[int, int, int] # undocumented native_encoding: str # undocumented features: list[tuple[str, int]] # undocumented @@ -15,7 +15,6 @@ class ExpatError(Exception): offset: int error = ExpatError - XML_PARAM_ENTITY_PARSING_NEVER: int XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: int XML_PARAM_ENTITY_PARSING_ALWAYS: int diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 3cb4b93e88fe..9e46012ee777 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -4,7 +4,7 @@ from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unu from collections.abc import Callable, Generator, Iterable, Iterator, Mapping from datetime import date, datetime, time from types import TracebackType -from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload +from typing import Any, Final, Literal, Protocol, SupportsIndex, TypeVar, final, overload from typing_extensions import Self, TypeAlias _T = TypeVar("_T") @@ -35,186 +35,186 @@ Binary = memoryview # The remaining definitions are imported from _sqlite3. -PARSE_COLNAMES: int -PARSE_DECLTYPES: int -SQLITE_ALTER_TABLE: int -SQLITE_ANALYZE: int -SQLITE_ATTACH: int -SQLITE_CREATE_INDEX: int -SQLITE_CREATE_TABLE: int -SQLITE_CREATE_TEMP_INDEX: int -SQLITE_CREATE_TEMP_TABLE: int -SQLITE_CREATE_TEMP_TRIGGER: int -SQLITE_CREATE_TEMP_VIEW: int -SQLITE_CREATE_TRIGGER: int -SQLITE_CREATE_VIEW: int -SQLITE_CREATE_VTABLE: int -SQLITE_DELETE: int -SQLITE_DENY: int -SQLITE_DETACH: int -SQLITE_DONE: int -SQLITE_DROP_INDEX: int -SQLITE_DROP_TABLE: int -SQLITE_DROP_TEMP_INDEX: int -SQLITE_DROP_TEMP_TABLE: int -SQLITE_DROP_TEMP_TRIGGER: int -SQLITE_DROP_TEMP_VIEW: int -SQLITE_DROP_TRIGGER: int -SQLITE_DROP_VIEW: int -SQLITE_DROP_VTABLE: int -SQLITE_FUNCTION: int -SQLITE_IGNORE: int -SQLITE_INSERT: int -SQLITE_OK: int +PARSE_COLNAMES: Final[int] +PARSE_DECLTYPES: Final[int] +SQLITE_ALTER_TABLE: Final[int] +SQLITE_ANALYZE: Final[int] +SQLITE_ATTACH: Final[int] +SQLITE_CREATE_INDEX: Final[int] +SQLITE_CREATE_TABLE: Final[int] +SQLITE_CREATE_TEMP_INDEX: Final[int] +SQLITE_CREATE_TEMP_TABLE: Final[int] +SQLITE_CREATE_TEMP_TRIGGER: Final[int] +SQLITE_CREATE_TEMP_VIEW: Final[int] +SQLITE_CREATE_TRIGGER: Final[int] +SQLITE_CREATE_VIEW: Final[int] +SQLITE_CREATE_VTABLE: Final[int] +SQLITE_DELETE: Final[int] +SQLITE_DENY: Final[int] +SQLITE_DETACH: Final[int] +SQLITE_DONE: Final[int] +SQLITE_DROP_INDEX: Final[int] +SQLITE_DROP_TABLE: Final[int] +SQLITE_DROP_TEMP_INDEX: Final[int] +SQLITE_DROP_TEMP_TABLE: Final[int] +SQLITE_DROP_TEMP_TRIGGER: Final[int] +SQLITE_DROP_TEMP_VIEW: Final[int] +SQLITE_DROP_TRIGGER: Final[int] +SQLITE_DROP_VIEW: Final[int] +SQLITE_DROP_VTABLE: Final[int] +SQLITE_FUNCTION: Final[int] +SQLITE_IGNORE: Final[int] +SQLITE_INSERT: Final[int] +SQLITE_OK: Final[int] if sys.version_info >= (3, 11): - SQLITE_LIMIT_LENGTH: int - SQLITE_LIMIT_SQL_LENGTH: int - SQLITE_LIMIT_COLUMN: int - SQLITE_LIMIT_EXPR_DEPTH: int - SQLITE_LIMIT_COMPOUND_SELECT: int - SQLITE_LIMIT_VDBE_OP: int - SQLITE_LIMIT_FUNCTION_ARG: int - SQLITE_LIMIT_ATTACHED: int - SQLITE_LIMIT_LIKE_PATTERN_LENGTH: int - SQLITE_LIMIT_VARIABLE_NUMBER: int - SQLITE_LIMIT_TRIGGER_DEPTH: int - SQLITE_LIMIT_WORKER_THREADS: int -SQLITE_PRAGMA: int -SQLITE_READ: int -SQLITE_REINDEX: int -SQLITE_RECURSIVE: int -SQLITE_SAVEPOINT: int -SQLITE_SELECT: int -SQLITE_TRANSACTION: int -SQLITE_UPDATE: int + SQLITE_LIMIT_LENGTH: Final[int] + SQLITE_LIMIT_SQL_LENGTH: Final[int] + SQLITE_LIMIT_COLUMN: Final[int] + SQLITE_LIMIT_EXPR_DEPTH: Final[int] + SQLITE_LIMIT_COMPOUND_SELECT: Final[int] + SQLITE_LIMIT_VDBE_OP: Final[int] + SQLITE_LIMIT_FUNCTION_ARG: Final[int] + SQLITE_LIMIT_ATTACHED: Final[int] + SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int] + SQLITE_LIMIT_VARIABLE_NUMBER: Final[int] + SQLITE_LIMIT_TRIGGER_DEPTH: Final[int] + SQLITE_LIMIT_WORKER_THREADS: Final[int] +SQLITE_PRAGMA: Final[int] +SQLITE_READ: Final[int] +SQLITE_REINDEX: Final[int] +SQLITE_RECURSIVE: Final[int] +SQLITE_SAVEPOINT: Final[int] +SQLITE_SELECT: Final[int] +SQLITE_TRANSACTION: Final[int] +SQLITE_UPDATE: Final[int] adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] converters: dict[str, _Converter] sqlite_version: str version: str if sys.version_info >= (3, 11): - SQLITE_ABORT: int - SQLITE_ABORT_ROLLBACK: int - SQLITE_AUTH: int - SQLITE_AUTH_USER: int - SQLITE_BUSY: int - SQLITE_BUSY_RECOVERY: int - SQLITE_BUSY_SNAPSHOT: int - SQLITE_BUSY_TIMEOUT: int - SQLITE_CANTOPEN: int - SQLITE_CANTOPEN_CONVPATH: int - SQLITE_CANTOPEN_DIRTYWAL: int - SQLITE_CANTOPEN_FULLPATH: int - SQLITE_CANTOPEN_ISDIR: int - SQLITE_CANTOPEN_NOTEMPDIR: int - SQLITE_CANTOPEN_SYMLINK: int - SQLITE_CONSTRAINT: int - SQLITE_CONSTRAINT_CHECK: int - SQLITE_CONSTRAINT_COMMITHOOK: int - SQLITE_CONSTRAINT_FOREIGNKEY: int - SQLITE_CONSTRAINT_FUNCTION: int - SQLITE_CONSTRAINT_NOTNULL: int - SQLITE_CONSTRAINT_PINNED: int - SQLITE_CONSTRAINT_PRIMARYKEY: int - SQLITE_CONSTRAINT_ROWID: int - SQLITE_CONSTRAINT_TRIGGER: int - SQLITE_CONSTRAINT_UNIQUE: int - SQLITE_CONSTRAINT_VTAB: int - SQLITE_CORRUPT: int - SQLITE_CORRUPT_INDEX: int - SQLITE_CORRUPT_SEQUENCE: int - SQLITE_CORRUPT_VTAB: int - SQLITE_EMPTY: int - SQLITE_ERROR: int - SQLITE_ERROR_MISSING_COLLSEQ: int - SQLITE_ERROR_RETRY: int - SQLITE_ERROR_SNAPSHOT: int - SQLITE_FORMAT: int - SQLITE_FULL: int - SQLITE_INTERNAL: int - SQLITE_INTERRUPT: int - SQLITE_IOERR: int - SQLITE_IOERR_ACCESS: int - SQLITE_IOERR_AUTH: int - SQLITE_IOERR_BEGIN_ATOMIC: int - SQLITE_IOERR_BLOCKED: int - SQLITE_IOERR_CHECKRESERVEDLOCK: int - SQLITE_IOERR_CLOSE: int - SQLITE_IOERR_COMMIT_ATOMIC: int - SQLITE_IOERR_CONVPATH: int - SQLITE_IOERR_CORRUPTFS: int - SQLITE_IOERR_DATA: int - SQLITE_IOERR_DELETE: int - SQLITE_IOERR_DELETE_NOENT: int - SQLITE_IOERR_DIR_CLOSE: int - SQLITE_IOERR_DIR_FSYNC: int - SQLITE_IOERR_FSTAT: int - SQLITE_IOERR_FSYNC: int - SQLITE_IOERR_GETTEMPPATH: int - SQLITE_IOERR_LOCK: int - SQLITE_IOERR_MMAP: int - SQLITE_IOERR_NOMEM: int - SQLITE_IOERR_RDLOCK: int - SQLITE_IOERR_READ: int - SQLITE_IOERR_ROLLBACK_ATOMIC: int - SQLITE_IOERR_SEEK: int - SQLITE_IOERR_SHMLOCK: int - SQLITE_IOERR_SHMMAP: int - SQLITE_IOERR_SHMOPEN: int - SQLITE_IOERR_SHMSIZE: int - SQLITE_IOERR_SHORT_READ: int - SQLITE_IOERR_TRUNCATE: int - SQLITE_IOERR_UNLOCK: int - SQLITE_IOERR_VNODE: int - SQLITE_IOERR_WRITE: int - SQLITE_LOCKED: int - SQLITE_LOCKED_SHAREDCACHE: int - SQLITE_LOCKED_VTAB: int - SQLITE_MISMATCH: int - SQLITE_MISUSE: int - SQLITE_NOLFS: int - SQLITE_NOMEM: int - SQLITE_NOTADB: int - SQLITE_NOTFOUND: int - SQLITE_NOTICE: int - SQLITE_NOTICE_RECOVER_ROLLBACK: int - SQLITE_NOTICE_RECOVER_WAL: int - SQLITE_OK_LOAD_PERMANENTLY: int - SQLITE_OK_SYMLINK: int - SQLITE_PERM: int - SQLITE_PROTOCOL: int - SQLITE_RANGE: int - SQLITE_READONLY: int - SQLITE_READONLY_CANTINIT: int - SQLITE_READONLY_CANTLOCK: int - SQLITE_READONLY_DBMOVED: int - SQLITE_READONLY_DIRECTORY: int - SQLITE_READONLY_RECOVERY: int - SQLITE_READONLY_ROLLBACK: int - SQLITE_ROW: int - SQLITE_SCHEMA: int - SQLITE_TOOBIG: int - SQLITE_WARNING: int - SQLITE_WARNING_AUTOINDEX: int + SQLITE_ABORT: Final[int] + SQLITE_ABORT_ROLLBACK: Final[int] + SQLITE_AUTH: Final[int] + SQLITE_AUTH_USER: Final[int] + SQLITE_BUSY: Final[int] + SQLITE_BUSY_RECOVERY: Final[int] + SQLITE_BUSY_SNAPSHOT: Final[int] + SQLITE_BUSY_TIMEOUT: Final[int] + SQLITE_CANTOPEN: Final[int] + SQLITE_CANTOPEN_CONVPATH: Final[int] + SQLITE_CANTOPEN_DIRTYWAL: Final[int] + SQLITE_CANTOPEN_FULLPATH: Final[int] + SQLITE_CANTOPEN_ISDIR: Final[int] + SQLITE_CANTOPEN_NOTEMPDIR: Final[int] + SQLITE_CANTOPEN_SYMLINK: Final[int] + SQLITE_CONSTRAINT: Final[int] + SQLITE_CONSTRAINT_CHECK: Final[int] + SQLITE_CONSTRAINT_COMMITHOOK: Final[int] + SQLITE_CONSTRAINT_FOREIGNKEY: Final[int] + SQLITE_CONSTRAINT_FUNCTION: Final[int] + SQLITE_CONSTRAINT_NOTNULL: Final[int] + SQLITE_CONSTRAINT_PINNED: Final[int] + SQLITE_CONSTRAINT_PRIMARYKEY: Final[int] + SQLITE_CONSTRAINT_ROWID: Final[int] + SQLITE_CONSTRAINT_TRIGGER: Final[int] + SQLITE_CONSTRAINT_UNIQUE: Final[int] + SQLITE_CONSTRAINT_VTAB: Final[int] + SQLITE_CORRUPT: Final[int] + SQLITE_CORRUPT_INDEX: Final[int] + SQLITE_CORRUPT_SEQUENCE: Final[int] + SQLITE_CORRUPT_VTAB: Final[int] + SQLITE_EMPTY: Final[int] + SQLITE_ERROR: Final[int] + SQLITE_ERROR_MISSING_COLLSEQ: Final[int] + SQLITE_ERROR_RETRY: Final[int] + SQLITE_ERROR_SNAPSHOT: Final[int] + SQLITE_FORMAT: Final[int] + SQLITE_FULL: Final[int] + SQLITE_INTERNAL: Final[int] + SQLITE_INTERRUPT: Final[int] + SQLITE_IOERR: Final[int] + SQLITE_IOERR_ACCESS: Final[int] + SQLITE_IOERR_AUTH: Final[int] + SQLITE_IOERR_BEGIN_ATOMIC: Final[int] + SQLITE_IOERR_BLOCKED: Final[int] + SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int] + SQLITE_IOERR_CLOSE: Final[int] + SQLITE_IOERR_COMMIT_ATOMIC: Final[int] + SQLITE_IOERR_CONVPATH: Final[int] + SQLITE_IOERR_CORRUPTFS: Final[int] + SQLITE_IOERR_DATA: Final[int] + SQLITE_IOERR_DELETE: Final[int] + SQLITE_IOERR_DELETE_NOENT: Final[int] + SQLITE_IOERR_DIR_CLOSE: Final[int] + SQLITE_IOERR_DIR_FSYNC: Final[int] + SQLITE_IOERR_FSTAT: Final[int] + SQLITE_IOERR_FSYNC: Final[int] + SQLITE_IOERR_GETTEMPPATH: Final[int] + SQLITE_IOERR_LOCK: Final[int] + SQLITE_IOERR_MMAP: Final[int] + SQLITE_IOERR_NOMEM: Final[int] + SQLITE_IOERR_RDLOCK: Final[int] + SQLITE_IOERR_READ: Final[int] + SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int] + SQLITE_IOERR_SEEK: Final[int] + SQLITE_IOERR_SHMLOCK: Final[int] + SQLITE_IOERR_SHMMAP: Final[int] + SQLITE_IOERR_SHMOPEN: Final[int] + SQLITE_IOERR_SHMSIZE: Final[int] + SQLITE_IOERR_SHORT_READ: Final[int] + SQLITE_IOERR_TRUNCATE: Final[int] + SQLITE_IOERR_UNLOCK: Final[int] + SQLITE_IOERR_VNODE: Final[int] + SQLITE_IOERR_WRITE: Final[int] + SQLITE_LOCKED: Final[int] + SQLITE_LOCKED_SHAREDCACHE: Final[int] + SQLITE_LOCKED_VTAB: Final[int] + SQLITE_MISMATCH: Final[int] + SQLITE_MISUSE: Final[int] + SQLITE_NOLFS: Final[int] + SQLITE_NOMEM: Final[int] + SQLITE_NOTADB: Final[int] + SQLITE_NOTFOUND: Final[int] + SQLITE_NOTICE: Final[int] + SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int] + SQLITE_NOTICE_RECOVER_WAL: Final[int] + SQLITE_OK_LOAD_PERMANENTLY: Final[int] + SQLITE_OK_SYMLINK: Final[int] + SQLITE_PERM: Final[int] + SQLITE_PROTOCOL: Final[int] + SQLITE_RANGE: Final[int] + SQLITE_READONLY: Final[int] + SQLITE_READONLY_CANTINIT: Final[int] + SQLITE_READONLY_CANTLOCK: Final[int] + SQLITE_READONLY_DBMOVED: Final[int] + SQLITE_READONLY_DIRECTORY: Final[int] + SQLITE_READONLY_RECOVERY: Final[int] + SQLITE_READONLY_ROLLBACK: Final[int] + SQLITE_ROW: Final[int] + SQLITE_SCHEMA: Final[int] + SQLITE_TOOBIG: Final[int] + SQLITE_WARNING: Final[int] + SQLITE_WARNING_AUTOINDEX: Final[int] if sys.version_info >= (3, 12): - LEGACY_TRANSACTION_CONTROL: int - SQLITE_DBCONFIG_DEFENSIVE: int - SQLITE_DBCONFIG_DQS_DDL: int - SQLITE_DBCONFIG_DQS_DML: int - SQLITE_DBCONFIG_ENABLE_FKEY: int - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: int - SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: int - SQLITE_DBCONFIG_ENABLE_QPSG: int - SQLITE_DBCONFIG_ENABLE_TRIGGER: int - SQLITE_DBCONFIG_ENABLE_VIEW: int - SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: int - SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: int - SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: int - SQLITE_DBCONFIG_RESET_DATABASE: int - SQLITE_DBCONFIG_TRIGGER_EQP: int - SQLITE_DBCONFIG_TRUSTED_SCHEMA: int - SQLITE_DBCONFIG_WRITABLE_SCHEMA: int + LEGACY_TRANSACTION_CONTROL: Final[int] + SQLITE_DBCONFIG_DEFENSIVE: Final[int] + SQLITE_DBCONFIG_DQS_DDL: Final[int] + SQLITE_DBCONFIG_DQS_DML: Final[int] + SQLITE_DBCONFIG_ENABLE_FKEY: Final[int] + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int] + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int] + SQLITE_DBCONFIG_ENABLE_QPSG: Final[int] + SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int] + SQLITE_DBCONFIG_ENABLE_VIEW: Final[int] + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int] + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int] + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int] + SQLITE_DBCONFIG_RESET_DATABASE: Final[int] + SQLITE_DBCONFIG_TRIGGER_EQP: Final[int] + SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int] + SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int] # Can take or return anything depending on what's in the registry. @overload diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index b01bac2455ce..2a5859807b51 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import ReadableBuffer, StrOrBytesPath from collections.abc import Callable, Collection, Iterable, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, TypeVar, overload +from typing import IO, Any, AnyStr, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -74,8 +74,8 @@ _T = TypeVar("_T") # These two are private but documented if sys.version_info >= (3, 11): - _USE_VFORK: bool -_USE_POSIX_SPAWN: bool + _USE_VFORK: Final[bool] +_USE_POSIX_SPAWN: Final[bool] class CompletedProcess(Generic[_T]): # morally: _CMD @@ -1810,9 +1810,9 @@ else: text: bool | None = None, ) -> Any: ... # morally: -> str | bytes -PIPE: int -STDOUT: int -DEVNULL: int +PIPE: Final[int] +STDOUT: Final[int] +DEVNULL: Final[int] class SubprocessError(Exception): ... diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index add0d57a8d4b..0611879cf1b2 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -1,6 +1,6 @@ import sys import termios -from typing import IO +from typing import IO, Final from typing_extensions import TypeAlias if sys.platform != "win32": @@ -15,13 +15,13 @@ if sys.platform != "win32": _FD: TypeAlias = int | IO[str] # XXX: Undocumented integer constants - IFLAG: int - OFLAG: int - CFLAG: int - LFLAG: int - ISPEED: int - OSPEED: int - CC: int + IFLAG: Final[int] + OFLAG: Final[int] + CFLAG: Final[int] + LFLAG: Final[int] + ISPEED: Final[int] + OSPEED: Final[int] + CC: Final[int] def setraw(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... def setcbreak(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index b63292604ecc..a92f03f9745f 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -6,7 +6,20 @@ from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Se from contextlib import AbstractContextManager from re import Pattern from types import TracebackType -from typing import Any, AnyStr, ClassVar, Generic, NamedTuple, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload +from typing import ( + Any, + AnyStr, + ClassVar, + Final, + Generic, + NamedTuple, + NoReturn, + Protocol, + SupportsAbs, + SupportsRound, + TypeVar, + overload, +) from typing_extensions import ParamSpec, Self, TypeAlias from warnings import WarningMessage @@ -22,7 +35,7 @@ _E = TypeVar("_E", bound=BaseException) _FT = TypeVar("_FT", bound=Callable[..., Any]) _P = ParamSpec("_P") -DIFF_OMITTED: str +DIFF_OMITTED: Final[str] class _BaseTestCaseContext: test_case: TestCase diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index 657f3d6dca71..598e3cd84a5e 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -4,13 +4,13 @@ import unittest.suite from collections.abc import Callable, Sequence from re import Pattern from types import ModuleType -from typing import Any +from typing import Any, Final from typing_extensions import TypeAlias, deprecated _SortComparisonMethod: TypeAlias = Callable[[str, str], int] _SuiteClass: TypeAlias = Callable[[list[unittest.case.TestCase]], unittest.suite.TestSuite] -VALID_MODULE_NAME: Pattern[str] +VALID_MODULE_NAME: Final[Pattern[str]] class TestLoader: errors: list[type[BaseException]] diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 3eb3d1612a3c..22f2ec10634d 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -5,11 +5,11 @@ import unittest.result import unittest.suite from collections.abc import Iterable from types import ModuleType -from typing import Any, Protocol +from typing import Any, Final, Protocol from typing_extensions import deprecated -MAIN_EXAMPLES: str -MODULE_EXAMPLES: str +MAIN_EXAMPLES: Final[str] +MODULE_EXAMPLES: Final[str] class _TestRunner(Protocol): def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase, /) -> unittest.result.TestResult: ... diff --git a/mypy/typeshed/stdlib/unittest/result.pyi b/mypy/typeshed/stdlib/unittest/result.pyi index 436fabf20c65..0761baaa2830 100644 --- a/mypy/typeshed/stdlib/unittest/result.pyi +++ b/mypy/typeshed/stdlib/unittest/result.pyi @@ -2,14 +2,14 @@ import sys import unittest.case from _typeshed import OptExcInfo from collections.abc import Callable -from typing import Any, TextIO, TypeVar +from typing import Any, Final, TextIO, TypeVar from typing_extensions import TypeAlias _F = TypeVar("_F", bound=Callable[..., Any]) _DurationsType: TypeAlias = list[tuple[str, float]] -STDOUT_LINE: str -STDERR_LINE: str +STDOUT_LINE: Final[str] +STDERR_LINE: Final[str] # undocumented def failfast(method: _F) -> _F: ... diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi index c42d1346e4b7..945b0cecfed0 100644 --- a/mypy/typeshed/stdlib/unittest/util.pyi +++ b/mypy/typeshed/stdlib/unittest/util.pyi @@ -1,16 +1,16 @@ from collections.abc import MutableSequence, Sequence -from typing import Any, TypeVar +from typing import Any, Final, TypeVar from typing_extensions import TypeAlias _T = TypeVar("_T") _Mismatch: TypeAlias = tuple[_T, _T, int] -_MAX_LENGTH: int -_PLACEHOLDER_LEN: int -_MIN_BEGIN_LEN: int -_MIN_END_LEN: int -_MIN_COMMON_LEN: int -_MIN_DIFF_LEN: int +_MAX_LENGTH: Final[int] +_PLACEHOLDER_LEN: Final[int] +_MIN_BEGIN_LEN: Final[int] +_MIN_END_LEN: Final[int] +_MIN_COMMON_LEN: Final[int] +_MIN_DIFF_LEN: Final[int] def _shorten(s: str, prefixlen: int, suffixlen: int) -> str: ... def _common_shorten_repr(*args: str) -> tuple[str, ...]: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index 2be5f7df2d7d..d254102acc55 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Literal, Protocol, overload +from typing import Any, Final, Literal, Protocol, overload from typing_extensions import Self, TypeAlias class _SupportsTimeTuple(Protocol): @@ -34,22 +34,22 @@ _HostType: TypeAlias = tuple[str, dict[str, str]] | str def escape(s: str) -> str: ... # undocumented -MAXINT: int # undocumented -MININT: int # undocumented +MAXINT: Final[int] # undocumented +MININT: Final[int] # undocumented -PARSE_ERROR: int # undocumented -SERVER_ERROR: int # undocumented -APPLICATION_ERROR: int # undocumented -SYSTEM_ERROR: int # undocumented -TRANSPORT_ERROR: int # undocumented +PARSE_ERROR: Final[int] # undocumented +SERVER_ERROR: Final[int] # undocumented +APPLICATION_ERROR: Final[int] # undocumented +SYSTEM_ERROR: Final[int] # undocumented +TRANSPORT_ERROR: Final[int] # undocumented -NOT_WELLFORMED_ERROR: int # undocumented -UNSUPPORTED_ENCODING: int # undocumented -INVALID_ENCODING_CHAR: int # undocumented -INVALID_XMLRPC: int # undocumented -METHOD_NOT_FOUND: int # undocumented -INVALID_METHOD_PARAMS: int # undocumented -INTERNAL_ERROR: int # undocumented +NOT_WELLFORMED_ERROR: Final[int] # undocumented +UNSUPPORTED_ENCODING: Final[int] # undocumented +INVALID_ENCODING_CHAR: Final[int] # undocumented +INVALID_XMLRPC: Final[int] # undocumented +METHOD_NOT_FOUND: Final[int] # undocumented +INVALID_METHOD_PARAMS: Final[int] # undocumented +INTERNAL_ERROR: Final[int] # undocumented class Error(Exception): ... @@ -98,7 +98,7 @@ class Binary: def _binary(data: ReadableBuffer) -> Binary: ... # undocumented -WRAPPERS: tuple[type[DateTime], type[Binary]] # undocumented +WRAPPERS: Final[tuple[type[DateTime], type[Binary]]] # undocumented class ExpatParser: # undocumented def __init__(self, target: Unmarshaller) -> None: ... diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index aa52a0b56e41..57a8a6aaa40a 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable, Iterator from io import TextIOWrapper from os import PathLike from types import TracebackType -from typing import IO, Literal, Protocol, overload +from typing import IO, Final, Literal, Protocol, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -300,10 +300,10 @@ else: def is_zipfile(filename: StrOrBytesPath | _SupportsReadSeekTell) -> bool: ... -ZIP_STORED: int -ZIP_DEFLATED: int -ZIP64_LIMIT: int -ZIP_FILECOUNT_LIMIT: int -ZIP_MAX_COMMENT: int -ZIP_BZIP2: int -ZIP_LZMA: int +ZIP_STORED: Final[int] +ZIP_DEFLATED: Final[int] +ZIP64_LIMIT: Final[int] +ZIP_FILECOUNT_LIMIT: Final[int] +ZIP_MAX_COMMENT: Final[int] +ZIP_BZIP2: Final[int] +ZIP_LZMA: Final[int] From 6eb3ee55a2ea2cefccb41acf0fb520d6d404d24d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 3 Aug 2024 17:51:18 +0300 Subject: [PATCH 0716/1617] Fix error code handling in `stubtest` with `--mypy-config-file` (#17629) This is the first PR in the series of unifing configuration parsing for different mypy tools. I would like to have the simple and focused. --- mypy/main.py | 17 +---------------- mypy/options.py | 17 +++++++++++++++++ mypy/stubtest.py | 6 ++++++ mypy/test/teststubtest.py | 13 +++++++++++++ 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 05044335ecee..49a395b478b3 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -19,7 +19,6 @@ validate_package_allow_list, ) from mypy.error_formatter import OUTPUT_CHOICES -from mypy.errorcodes import error_codes from mypy.errors import CompileError from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache @@ -1336,21 +1335,7 @@ def set_strict_flags() -> None: validate_package_allow_list(options.untyped_calls_exclude) - # Process `--enable-error-code` and `--disable-error-code` flags - disabled_codes = set(options.disable_error_code) - enabled_codes = set(options.enable_error_code) - - valid_error_codes = set(error_codes.keys()) - - invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes - if invalid_codes: - parser.error(f"Invalid error code(s): {', '.join(sorted(invalid_codes))}") - - options.disabled_error_codes |= {error_codes[code] for code in disabled_codes} - options.enabled_error_codes |= {error_codes[code] for code in enabled_codes} - - # Enabling an error code always overrides disabling - options.disabled_error_codes -= options.enabled_error_codes + options.process_error_codes(error_callback=parser.error) # Validate incomplete features. for feature in options.enable_incomplete_feature: diff --git a/mypy/options.py b/mypy/options.py index bff096d82c15..5ab397e0e156 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -416,6 +416,23 @@ def snapshot(self) -> dict[str, object]: def __repr__(self) -> str: return f"Options({pprint.pformat(self.snapshot())})" + def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None: + # Process `--enable-error-code` and `--disable-error-code` flags + disabled_codes = set(self.disable_error_code) + enabled_codes = set(self.enable_error_code) + + valid_error_codes = set(error_codes.keys()) + + invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes + if invalid_codes: + error_callback(f"Invalid error code(s): {', '.join(sorted(invalid_codes))}") + + self.disabled_error_codes |= {error_codes[code] for code in disabled_codes} + self.enabled_error_codes |= {error_codes[code] for code in enabled_codes} + + # Enabling an error code always overrides disabling + self.disabled_error_codes -= self.enabled_error_codes + def apply_changes(self, changes: dict[str, object]) -> Options: # Note: effects of this method *must* be idempotent. new_options = Options() diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a7cde8b8fe6c..6299f21e48e9 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1941,6 +1941,12 @@ def set_strict_flags() -> None: # not needed yet parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr) + def error_callback(msg: str) -> typing.NoReturn: + print(_style("error:", color="red", bold=True), msg) + sys.exit(1) + + options.process_error_codes(error_callback=error_callback) + try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) except StubtestFailure as stubtest_failure: diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 418308e2e65e..1cc6c38e6e85 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -2477,6 +2477,19 @@ def test_config_file(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "Success: no issues found in 1 module\n" + def test_config_file_error_codes(self) -> None: + runtime = "temp = 5\n" + stub = "temp = SOME_GLOBAL_CONST" + output = run_stubtest(stub=stub, runtime=runtime, options=[]) + assert output == ( + "error: not checking stubs due to mypy build errors:\n" + 'test_module.pyi:1: error: Name "SOME_GLOBAL_CONST" is not defined [name-defined]\n' + ) + + config_file = "[mypy]\ndisable_error_code = name-defined\n" + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == "Success: no issues found in 1 module\n" + def test_no_modules(self) -> None: output = io.StringIO() with contextlib.redirect_stdout(output): From fe4df128e9cb76cc694494e9b0cd129d4aaa6ebd Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Aug 2024 12:18:47 +0300 Subject: [PATCH 0717/1617] Fix `stubgen --no-analysis/--parse-only` docs (#17632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The option is called `--inspect-mode`, not `--inspect` It used to be: ``` » stubgen --help usage: stubgen [-h] [more options, see -h] [-m MODULE] [-p PACKAGE] [files ...] Generate draft stubs for modules. Stubs are generated in directory ./out, to avoid overriding files with manual changes. This directory is assumed to exist. positional arguments: files generate stubs for given files or directories options: --no-analysis, --parse-only don't perform semantic analysis of sources, just parse them (only applies to Python modules, might affect quality of stubs. Not compatible with --inspect) --inspect-mode import and inspect modules instead of parsing source code.This is the default behavior for c modules and pyc-only packages, but it is also useful for pure python modules with dynamically generated members. ``` --- mypy/stubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 8478bd2135e4..4e6e3efc000b 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1772,7 +1772,7 @@ def parse_options(args: list[str]) -> Options: action="store_true", help="don't perform semantic analysis of sources, just parse them " "(only applies to Python modules, might affect quality of stubs. " - "Not compatible with --inspect)", + "Not compatible with --inspect-mode)", ) parser.add_argument( "--inspect-mode", From 2d55435606002f446e48884d165f1d2ffe2cb5c6 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 4 Aug 2024 14:15:53 +0300 Subject: [PATCH 0718/1617] List all incomplete features in `--enable-incomplete-feature` docs (#17633) It was strange to see only one feature: ```rst .. option:: --enable-incomplete-feature {PreciseTupleTypes} ``` --- docs/source/command_line.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 906231dc7e42..c085b63107b0 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -1008,7 +1008,7 @@ format into the specified directory. Enabling incomplete/experimental features ***************************************** -.. option:: --enable-incomplete-feature {PreciseTupleTypes} +.. option:: --enable-incomplete-feature {PreciseTupleTypes, NewGenericSyntax, InlineTypedDict} Some features may require several mypy releases to implement, for example due to their complexity, potential for backwards incompatibility, or From 41dcf1ac5be5d1d9b46d20b97bae101e30735f44 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Aug 2024 12:22:29 +0100 Subject: [PATCH 0719/1617] Revert "Fix `RawExpressionType.accept` crash with `--cache-fine-grained`" (#17637) Reverts python/mypy#17588 --- mypy/types.py | 2 -- test-data/unit/check-typeddict.test | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index c29d686ce5d8..52b3121f9fb3 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2705,8 +2705,6 @@ def simple_name(self) -> str: return self.base_type_name.replace("builtins.", "") def accept(self, visitor: TypeVisitor[T]) -> T: - if self.node is not None: - return self.node.accept(visitor) assert isinstance(visitor, SyntheticTypeVisitor) ret: T = visitor.visit_raw_expression_type(self) return ret diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 1ef08f825e7a..6a5120159c2d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1442,18 +1442,6 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'a': TypedDict('_ reveal_type(x['a']['b']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] -[case testTypedDictForwardReferenceCacheFineGrained] -# flags: --cache-fine-grained -from mypy_extensions import TypedDict -class A(TypedDict): - b: "B" -class B(TypedDict): - c: "C" -class C(TypedDict): - d: "D" -class D: - pass - [case testSelfRecursiveTypedDictInheriting] from mypy_extensions import TypedDict From bc39f1714e2e5f39eb90f05f74575a7c397f068b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Aug 2024 12:50:05 +0100 Subject: [PATCH 0720/1617] Revert "Fix Literal strings containing pipe characters" (#17638) Reverts python/mypy#17148 --- mypy/fastparse.py | 11 ++- mypy/semanal.py | 31 ++++---- mypy/server/astmerge.py | 3 +- mypy/stubutil.py | 16 +--- mypy/type_visitor.py | 4 - mypy/typeanal.py | 21 ++++-- mypy/types.py | 75 +++++++++++-------- mypy/typetraverser.py | 3 +- mypyc/irbuild/classdef.py | 9 ++- test-data/unit/check-final.test | 2 - test-data/unit/check-literal.test | 4 - test-data/unit/check-namedtuple.test | 8 +- .../unit/check-parameter-specification.test | 23 +----- test-data/unit/check-typeguard.test | 11 --- test-data/unit/check-typeis.test | 11 --- 15 files changed, 90 insertions(+), 142 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 363fc8375259..ab7eef924bd1 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -331,7 +331,14 @@ def parse_type_string( """ try: _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None) - return RawExpressionType(expr_string, expr_fallback_name, line, column, node=node) + if isinstance(node, UnboundType) and node.original_str_expr is None: + node.original_str_expr = expr_string + node.original_str_fallback = expr_fallback_name + return node + elif isinstance(node, UnionType): + return node + else: + return RawExpressionType(expr_string, expr_fallback_name, line, column) except (SyntaxError, ValueError): # Note: the parser will raise a `ValueError` instead of a SyntaxError if # the string happens to contain things like \x00. @@ -1057,8 +1064,6 @@ def set_type_optional(self, type: Type | None, initializer: Expression | None) - return # Indicate that type should be wrapped in an Optional if arg is initialized to None. optional = isinstance(initializer, NameExpr) and initializer.name == "None" - if isinstance(type, RawExpressionType) and type.node is not None: - type = type.node if isinstance(type, UnboundType): type.optional = optional diff --git a/mypy/semanal.py b/mypy/semanal.py index f36149076fe6..782985e3fbab 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3437,10 +3437,10 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: def analyze_lvalues(self, s: AssignmentStmt) -> None: # We cannot use s.type, because analyze_simple_literal_type() will set it. explicit = s.unanalyzed_type is not None - final_type = self.unwrap_final_type(s.unanalyzed_type) - if final_type is not None: + if self.is_final_type(s.unanalyzed_type): # We need to exclude bare Final. - if not final_type.args: + assert isinstance(s.unanalyzed_type, UnboundType) + if not s.unanalyzed_type.args: explicit = False if s.rvalue: @@ -3506,19 +3506,19 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: Returns True if Final[...] was present. """ - final_type = self.unwrap_final_type(s.unanalyzed_type) - if final_type is None: + if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type): return False - if len(final_type.args) > 1: - self.fail("Final[...] takes at most one type argument", final_type) + assert isinstance(s.unanalyzed_type, UnboundType) + if len(s.unanalyzed_type.args) > 1: + self.fail("Final[...] takes at most one type argument", s.unanalyzed_type) invalid_bare_final = False - if not final_type.args: + if not s.unanalyzed_type.args: s.type = None if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: invalid_bare_final = True self.fail("Type in Final[...] can only be omitted if there is an initializer", s) else: - s.type = final_type.args[0] + s.type = s.unanalyzed_type.args[0] if s.type is not None and self.is_classvar(s.type): self.fail("Variable should not be annotated with both ClassVar and Final", s) @@ -4937,18 +4937,13 @@ def is_classvar(self, typ: Type) -> bool: return False return sym.node.fullname == "typing.ClassVar" - def unwrap_final_type(self, typ: Type | None) -> UnboundType | None: - if typ is None: - return None - typ = typ.resolve_string_annotation() + def is_final_type(self, typ: Type | None) -> bool: if not isinstance(typ, UnboundType): - return None + return False sym = self.lookup_qualified(typ.name, typ) if not sym or not sym.node: - return None - if sym.node.fullname in FINAL_TYPE_NAMES: - return typ - return None + return False + return sym.node.fullname in FINAL_TYPE_NAMES def fail_invalid_classvar(self, context: Context) -> None: self.fail(message_registry.CLASS_VAR_OUTSIDE_OF_CLASS, context) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index e6648fbb4be7..174c2922c767 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -507,8 +507,7 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None: typ.fallback.accept(self) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_literal_type(self, typ: LiteralType) -> None: typ.fallback.accept(self) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 2f2db0dbbe53..04b36e149957 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -17,16 +17,7 @@ from mypy.modulefinder import ModuleNotFoundReason from mypy.moduleinspect import InspectError, ModuleInspect from mypy.stubdoc import ArgSig, FunctionSig -from mypy.types import ( - AnyType, - NoneType, - RawExpressionType, - Type, - TypeList, - TypeStrVisitor, - UnboundType, - UnionType, -) +from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () @@ -302,11 +293,12 @@ def args_str(self, args: Iterable[Type]) -> str: The main difference from list_str is the preservation of quotes for string arguments """ + types = ["builtins.bytes", "builtins.str"] res = [] for arg in args: arg_str = arg.accept(self) - if isinstance(arg, RawExpressionType): - res.append(repr(arg.literal_value)) + if isinstance(arg, UnboundType) and arg.original_str_fallback in types: + res.append(f"'{arg_str}'") else: res.append(arg_str) return ", ".join(res) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index e685c49904bc..59e13d12485c 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -382,8 +382,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> T: return self.query_types(t.items.values()) def visit_raw_expression_type(self, t: RawExpressionType) -> T: - if t.node is not None: - return t.node.accept(self) return self.strategy([]) def visit_literal_type(self, t: LiteralType) -> T: @@ -524,8 +522,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> bool: return self.query_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> bool: - if t.node is not None: - return t.node.accept(self) return self.default def visit_literal_type(self, t: LiteralType) -> bool: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index f63aef30a09a..f88c3f91d1c6 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1112,7 +1112,6 @@ def visit_callable_type( return ret def anal_type_guard(self, t: Type) -> Type | None: - t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1131,7 +1130,6 @@ def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None: return None def anal_type_is(self, t: Type) -> Type | None: - t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1149,7 +1147,6 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: """Analyze signature argument type for *args and **kwargs argument.""" - t = t.resolve_string_annotation() if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: components = t.name.split(".") tvar_name = ".".join(components[:-1]) @@ -1275,8 +1272,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # make signatures like "foo(x: 20) -> None" legal, we can change # this method so it generates and returns an actual LiteralType # instead. - if t.node is not None: - return t.node.accept(self) if self.report_invalid_types: if t.base_type_name in ("builtins.int", "builtins.bool"): @@ -1539,7 +1534,6 @@ def analyze_callable_args( invalid_unpacks: list[Type] = [] second_unpack_last = False for i, arg in enumerate(arglist.items): - arg = arg.resolve_string_annotation() if isinstance(arg, CallableArgument): args.append(arg.typ) names.append(arg.name) @@ -1620,6 +1614,18 @@ def analyze_literal_type(self, t: UnboundType) -> Type: return UnionType.make_union(output, line=t.line) def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None: + # This UnboundType was originally defined as a string. + if isinstance(arg, UnboundType) and arg.original_str_expr is not None: + assert arg.original_str_fallback is not None + return [ + LiteralType( + value=arg.original_str_expr, + fallback=self.named_type(arg.original_str_fallback), + line=arg.line, + column=arg.column, + ) + ] + # If arg is an UnboundType that was *not* originally defined as # a string, try expanding it in case it's a type alias or something. if isinstance(arg, UnboundType): @@ -2605,8 +2611,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> None: self.process_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_literal_type(self, t: LiteralType) -> None: pass diff --git a/mypy/types.py b/mypy/types.py index 52b3121f9fb3..72374c1555c9 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -271,9 +271,6 @@ def can_be_true_default(self) -> bool: def can_be_false_default(self) -> bool: return True - def resolve_string_annotation(self) -> Type: - return self - def accept(self, visitor: TypeVisitor[T]) -> T: raise RuntimeError("Not implemented", type(self)) @@ -906,7 +903,14 @@ def copy_modified( class UnboundType(ProperType): """Instance type that has not been bound during semantic analysis.""" - __slots__ = ("name", "args", "optional", "empty_tuple_index") + __slots__ = ( + "name", + "args", + "optional", + "empty_tuple_index", + "original_str_expr", + "original_str_fallback", + ) def __init__( self, @@ -916,6 +920,8 @@ def __init__( column: int = -1, optional: bool = False, empty_tuple_index: bool = False, + original_str_expr: str | None = None, + original_str_fallback: str | None = None, ) -> None: super().__init__(line, column) if not args: @@ -927,6 +933,21 @@ def __init__( self.optional = optional # Special case for X[()] self.empty_tuple_index = empty_tuple_index + # If this UnboundType was originally defined as a str or bytes, keep track of + # the original contents of that string-like thing. This way, if this UnboundExpr + # ever shows up inside of a LiteralType, we can determine whether that + # Literal[...] is valid or not. E.g. Literal[foo] is most likely invalid + # (unless 'foo' is an alias for another literal or something) and + # Literal["foo"] most likely is. + # + # We keep track of the entire string instead of just using a boolean flag + # so we can distinguish between things like Literal["foo"] vs + # Literal[" foo "]. + # + # We also keep track of what the original base fallback type was supposed to be + # so we don't have to try and recompute it later + self.original_str_expr = original_str_expr + self.original_str_fallback = original_str_fallback def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundType: if args is _dummy: @@ -938,19 +959,25 @@ def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundT column=self.column, optional=self.optional, empty_tuple_index=self.empty_tuple_index, + original_str_expr=self.original_str_expr, + original_str_fallback=self.original_str_fallback, ) def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_unbound_type(self) def __hash__(self) -> int: - return hash((self.name, self.optional, tuple(self.args))) + return hash((self.name, self.optional, tuple(self.args), self.original_str_expr)) def __eq__(self, other: object) -> bool: if not isinstance(other, UnboundType): return NotImplemented return ( - self.name == other.name and self.optional == other.optional and self.args == other.args + self.name == other.name + and self.optional == other.optional + and self.args == other.args + and self.original_str_expr == other.original_str_expr + and self.original_str_fallback == other.original_str_fallback ) def serialize(self) -> JsonDict: @@ -958,12 +985,19 @@ def serialize(self) -> JsonDict: ".class": "UnboundType", "name": self.name, "args": [a.serialize() for a in self.args], + "expr": self.original_str_expr, + "expr_fallback": self.original_str_fallback, } @classmethod def deserialize(cls, data: JsonDict) -> UnboundType: assert data[".class"] == "UnboundType" - return UnboundType(data["name"], [deserialize_type(a) for a in data["args"]]) + return UnboundType( + data["name"], + [deserialize_type(a) for a in data["args"]], + original_str_expr=data["expr"], + original_str_fallback=data["expr_fallback"], + ) class CallableArgument(ProperType): @@ -2646,7 +2680,7 @@ class RawExpressionType(ProperType): This synthetic type is only used at the beginning stages of semantic analysis and should be completely removing during the process for mapping UnboundTypes to - actual types: we turn it into its "node" argument, a LiteralType, or an AnyType. + actual types: we either turn it into a LiteralType or an AnyType. For example, suppose `Foo[1]` is initially represented as the following: @@ -2684,7 +2718,7 @@ class RawExpressionType(ProperType): ) """ - __slots__ = ("literal_value", "base_type_name", "note", "node") + __slots__ = ("literal_value", "base_type_name", "note") def __init__( self, @@ -2693,13 +2727,11 @@ def __init__( line: int = -1, column: int = -1, note: str | None = None, - node: Type | None = None, ) -> None: super().__init__(line, column) self.literal_value = literal_value self.base_type_name = base_type_name self.note = note - self.node = node def simple_name(self) -> str: return self.base_type_name.replace("builtins.", "") @@ -2709,21 +2741,6 @@ def accept(self, visitor: TypeVisitor[T]) -> T: ret: T = visitor.visit_raw_expression_type(self) return ret - def copy_modified(self, node: Type | None) -> RawExpressionType: - return RawExpressionType( - literal_value=self.literal_value, - base_type_name=self.base_type_name, - line=self.line, - column=self.column, - note=self.note, - node=node, - ) - - def resolve_string_annotation(self) -> Type: - if self.node is not None: - return self.node.resolve_string_annotation() - return self - def serialize(self) -> JsonDict: assert False, "Synthetic types don't serialize" @@ -2735,7 +2752,6 @@ def __eq__(self, other: object) -> bool: return ( self.base_type_name == other.base_type_name and self.literal_value == other.literal_value - and self.node == other.node ) else: return NotImplemented @@ -3411,8 +3427,6 @@ def item_str(name: str, typ: str) -> str: return f"TypedDict({prefix}{s})" def visit_raw_expression_type(self, t: RawExpressionType) -> str: - if t.node is not None: - return t.node.accept(self) return repr(t.literal_value) def visit_literal_type(self, t: LiteralType) -> str: @@ -3476,9 +3490,6 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return t def visit_raw_expression_type(self, t: RawExpressionType) -> Type: - if t.node is not None: - node = t.node.accept(self) - return t.copy_modified(node=node) return t def visit_type_list(self, t: TypeList) -> Type: diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index 4d740a802b55..a28bbf422b61 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -130,8 +130,7 @@ def visit_partial_type(self, t: PartialType) -> None: pass def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_type_alias_type(self, t: TypeAliasType) -> None: # TODO: sometimes we want to traverse target as well diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 2152da099e81..7e0a842b1b41 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -26,7 +26,7 @@ TypeParam, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, RawExpressionType, get_proper_type +from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, get_proper_type from mypyc.common import PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature @@ -640,15 +640,16 @@ def add_non_ext_class_attr_ann( if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? ann_type = get_proper_type(stmt.type) - if isinstance(stmt.unanalyzed_type, RawExpressionType) and isinstance( - stmt.unanalyzed_type.literal_value, str + if ( + isinstance(stmt.unanalyzed_type, UnboundType) + and stmt.unanalyzed_type.original_str_expr is not None ): # Annotation is a forward reference, so don't attempt to load the actual # type and load the string instead. # # TODO: is it possible to determine whether a non-string annotation is # actually a forward reference due to the __annotations__ future? - typ = builder.load_str(stmt.unanalyzed_type.literal_value) + typ = builder.load_str(stmt.unanalyzed_type.original_str_expr) elif isinstance(ann_type, Instance): typ = load_type(builder, ann_type.type, stmt.line) else: diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index dadf76a283b0..763183159e94 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -6,13 +6,11 @@ [case testFinalDefiningModuleVar] from typing import Final -w: 'Final' = int() x: Final = int() y: Final[float] = int() z: Final[int] = int() bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") -reveal_type(w) # N: Revealed type is "builtins.int" reveal_type(x) # N: Revealed type is "builtins.int" reveal_type(y) # N: Revealed type is "builtins.float" reveal_type(z) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 6d76ce176aaf..e8d942f5d74d 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -12,12 +12,8 @@ reveal_type(g1) # N: Revealed type is "def (x: Literal['A['])" def f2(x: 'A B') -> None: pass # E: Invalid type comment or annotation def g2(x: Literal['A B']) -> None: pass -def h2(x: 'A|int') -> None: pass # E: Name "A" is not defined -def i2(x: Literal['A|B']) -> None: pass reveal_type(f2) # N: Revealed type is "def (x: Any)" reveal_type(g2) # N: Revealed type is "def (x: Literal['A B'])" -reveal_type(h2) # N: Revealed type is "def (x: Union[Any, builtins.int])" -reveal_type(i2) # N: Revealed type is "def (x: Literal['A|B'])" [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 147270dff72e..df2c7ffc8067 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -824,20 +824,14 @@ class Fraction(Real): [builtins fixtures/tuple.pyi] [case testForwardReferenceInNamedTuple] -from typing import List, NamedTuple +from typing import NamedTuple class A(NamedTuple): b: 'B' x: int - y: List['B'] class B: pass - -def f(a: A): - reveal_type(a.b) # N: Revealed type is "__main__.B" - reveal_type(a.x) # N: Revealed type is "builtins.int" - reveal_type(a.y) # N: Revealed type is "builtins.list[__main__.B]" [builtins fixtures/tuple.pyi] [case testTypeNamedTupleClassmethod] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index e6d8cec3f0b0..c2afb61586a8 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1193,28 +1193,7 @@ def func(callback: Callable[P, str]) -> Callable[P, str]: return inner [builtins fixtures/paramspec.pyi] -[case testParamSpecArgsAndKwargsStringified] -from typing import Callable -from typing_extensions import ParamSpec - -P1 = ParamSpec("P1") - -def func(callback: Callable[P1, str]) -> Callable[P1, str]: - def inner(*args: "P1.args", **kwargs: "P1.kwargs") -> str: - return "foo" - return inner - -@func -def outer(a: int) -> str: - return "" - -outer(1) # OK -outer("x") # E: Argument 1 to "outer" has incompatible type "str"; expected "int" -outer(a=1) # OK -outer(b=1) # E: Unexpected keyword argument "b" for "outer" -[builtins fixtures/paramspec.pyi] - -[case testParamSpecArgsAndKwargsMismatch] +[case testParamSpecArgsAndKwargsMissmatch] from typing import Callable from typing_extensions import ParamSpec diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index e1b7a86aba63..27b88553fb43 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -9,17 +9,6 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] -[case testTypeGuardStringified] -from typing_extensions import TypeGuard -class Point: pass -def is_point(a: object) -> "TypeGuard[Point]": pass -def main(a: object) -> None: - if is_point(a): - reveal_type(a) # N: Revealed type is "__main__.Point" - else: - reveal_type(a) # N: Revealed type is "builtins.object" -[builtins fixtures/tuple.pyi] - [case testTypeGuardTypeArgsNone] from typing_extensions import TypeGuard def foo(a: object) -> TypeGuard: # E: TypeGuard must have exactly one type argument diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 83467d5e3683..6b96845504ab 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -9,17 +9,6 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] -[case testTypeIsStringified] -from typing_extensions import TypeIs -class Point: pass -def is_point(a: object) -> "TypeIs[Point]": pass -def main(a: object) -> None: - if is_point(a): - reveal_type(a) # N: Revealed type is "__main__.Point" - else: - reveal_type(a) # N: Revealed type is "builtins.object" -[builtins fixtures/tuple.pyi] - [case testTypeIsElif] from typing_extensions import TypeIs from typing import Union From b56f357a45838c204c951deeb6cbdbf143258e0c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Aug 2024 17:56:13 +0100 Subject: [PATCH 0721/1617] Unwrap TypedDict item types before storing (#17640) Fixes https://github.com/python/mypy/issues/17604 Fixes https://github.com/python/mypy/issues/17608 Fix is trivial, rectify an obvious omission in my original PR. --- mypy/semanal_typeddict.py | 4 +++- test-data/unit/check-typeddict.test | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index e639871364ce..832530df55e5 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -324,7 +324,9 @@ def analyze_typeddict_classdef_fields( return None, [], [], set() # Need to defer types.append(analyzed) if not has_placeholder(analyzed): - stmt.type = analyzed + stmt.type = ( + analyzed.item if isinstance(analyzed, RequiredType) else analyzed + ) # ...despite possible minor failures that allow further analysis. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax: self.fail(TPDICT_CLASS_ERROR, stmt) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 6a5120159c2d..bfc7b6394d52 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2382,6 +2382,14 @@ class ForceDeferredEval: pass [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictRequiredUnimportedAny] +# flags: --disallow-any-unimported +from typing import NotRequired, TypedDict +from nonexistent import Foo # type: ignore[import-not-found] +class Bar(TypedDict): + foo: NotRequired[Foo] # E: Type of variable becomes "Any" due to an unfollowed import +[typing fixtures/typing-typeddict.pyi] + -- Required[] [case testDoesRecognizeRequiredInTypedDictWithClass] From 6f20721ce2fa1c7d3bbef42e3eaf953b9f0868f9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Aug 2024 19:10:57 +0100 Subject: [PATCH 0722/1617] Fix crash on a callable attribute with single unpack (#17641) Fixes https://github.com/python/mypy/issues/17518 --- mypy/typeops.py | 6 ++++-- test-data/unit/check-typeddict.test | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 4fe187f811ca..036d782be89c 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -310,12 +310,14 @@ class B(A): pass if not func.arg_types: # Invalid method, return something. return cast(F, func) - if func.arg_kinds[0] == ARG_STAR: + if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2): # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. - # TODO: infer bounds on the type of *args? + + # In the case of **kwargs we should probably emit an error, but + # for now we simply skip it, to avoid crashes down the line. return cast(F, func) self_param_type = get_proper_type(func.arg_types[0]) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index bfc7b6394d52..dc1929751977 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3579,6 +3579,24 @@ class Test: run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] +[case testTypedDictUnpackSingleWithSubtypingNoCrash] +from typing import Callable +from typing_extensions import TypedDict, Unpack + +class Kwargs(TypedDict): + name: str + +def f(**kwargs: Unpack[Kwargs]) -> None: + pass + +class C: + d: Callable[[Unpack[Kwargs]], None] + +# TODO: it is an old question whether we should allow this, for now simply don't crash. +class D(C): + d = f +[builtins fixtures/tuple.pyi] + [case testTypedDictInlineNoOldStyleAlias] # flags: --enable-incomplete-feature=InlineTypedDict X = {"int": int, "str": str} From 50d0d047eb3dedf3b801c3731a37d56c320bad3e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Aug 2024 22:27:05 +0100 Subject: [PATCH 0723/1617] Always reset binder when checking deferred nodes (#17643) Fixes https://github.com/python/mypy/issues/17595 --- mypy/checker.py | 9 +++---- test-data/unit/check-functions.test | 37 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 59de599006a8..82633a5b6c0e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -540,10 +540,11 @@ def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> self.check_top_level(node) else: self.recurse_into_functions = True - if isinstance(node, LambdaExpr): - self.expr_checker.accept(node) - else: - self.accept(node) + with self.binder.top_frame_context(): + if isinstance(node, LambdaExpr): + self.expr_checker.accept(node) + else: + self.accept(node) def check_top_level(self, node: MypyFile) -> None: """Check only the top-level of a module, skipping function definitions.""" diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index ef5a66b6ecde..b681c544a6bd 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3409,3 +3409,40 @@ for factory in ( reveal_type(factory) # N: Revealed type is "def () -> Union[builtins.str, None]" var = factory() [builtins fixtures/tuple.pyi] + +[case testLambdaInDeferredDecoratorNoCrash] +def foo(x): + pass + +class Bar: + def baz(self, x): + pass + +class Qux(Bar): + @foo(lambda x: None) + def baz(self, x) -> None: + pass +[builtins fixtures/tuple.pyi] + +[case testGeneratorInDeferredDecoratorNoCrash] +from typing import Protocol, TypeVar + +T = TypeVar("T", covariant=True) + +class SupportsNext(Protocol[T]): + def __next__(self) -> T: ... + +def next(i: SupportsNext[T]) -> T: ... + +def foo(x): + pass + +class Bar: + def baz(self, x): + pass + +class Qux(Bar): + @next(f for f in [foo]) + def baz(self, x) -> None: + pass +[builtins fixtures/tuple.pyi] From fc5e1ffa4e4137b3483e0ed6052dd445604cc7fc Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 5 Aug 2024 09:50:28 +0300 Subject: [PATCH 0724/1617] Fix typo in error_code_list.rst (#17645) --- docs/source/error_code_list.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 64d9a1d03287..85c8d437a856 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1184,7 +1184,7 @@ comment: .. code-block:: python - class MyClass + class MyClass: @special # type: ignore[prop-decorator] @property def magic(self) -> str: From 9d56820a63d5b47b53ff0ab6e96ce0951c774ae5 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 5 Aug 2024 09:50:56 +0300 Subject: [PATCH 0725/1617] Add `enable_incomplete_feature` validation to `stubtest` (#17635) Closes #17634 Refs #17628 Refs #17629 --- mypy/main.py | 10 ++-------- mypy/options.py | 10 ++++++++++ mypy/stubtest.py | 6 ++++++ mypy/test/teststubtest.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 49a395b478b3..f177bb1c2062 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -23,7 +23,7 @@ from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path -from mypy.options import COMPLETE_FEATURES, INCOMPLETE_FEATURES, BuildType, Options +from mypy.options import INCOMPLETE_FEATURES, BuildType, Options from mypy.split_namespace import SplitNamespace from mypy.version import __version__ @@ -1336,13 +1336,7 @@ def set_strict_flags() -> None: validate_package_allow_list(options.untyped_calls_exclude) options.process_error_codes(error_callback=parser.error) - - # Validate incomplete features. - for feature in options.enable_incomplete_feature: - if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: - parser.error(f"Unknown incomplete feature: {feature}") - if feature in COMPLETE_FEATURES: - print(f"Warning: {feature} is already enabled by default") + options.process_incomplete_features(error_callback=parser.error, warning_callback=print) # Compute absolute path for custom typeshed (if present). if options.custom_typeshed_dir is not None: diff --git a/mypy/options.py b/mypy/options.py index 5ab397e0e156..5e64d5e40035 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -433,6 +433,16 @@ def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None: # Enabling an error code always overrides disabling self.disabled_error_codes -= self.enabled_error_codes + def process_incomplete_features( + self, *, error_callback: Callable[[str], Any], warning_callback: Callable[[str], Any] + ) -> None: + # Validate incomplete features. + for feature in self.enable_incomplete_feature: + if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: + error_callback(f"Unknown incomplete feature: {feature}") + if feature in COMPLETE_FEATURES: + warning_callback(f"Warning: {feature} is already enabled by default") + def apply_changes(self, changes: dict[str, object]) -> Options: # Note: effects of this method *must* be idempotent. new_options = Options() diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6299f21e48e9..ca17ccfe2f5b 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1945,7 +1945,13 @@ def error_callback(msg: str) -> typing.NoReturn: print(_style("error:", color="red", bold=True), msg) sys.exit(1) + def warning_callback(msg: str) -> None: + print(_style("warning:", color="yellow", bold=True), msg) + options.process_error_codes(error_callback=error_callback) + options.process_incomplete_features( + error_callback=error_callback, warning_callback=warning_callback + ) try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 1cc6c38e6e85..c10c683ffdac 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -2490,6 +2490,20 @@ def test_config_file_error_codes(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "Success: no issues found in 1 module\n" + def test_config_file_wrong_incomplete_feature(self) -> None: + runtime = "x = 1\n" + stub = "x: int\n" + config_file = "[mypy]\nenable_incomplete_feature = Unpack\n" + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == ( + "warning: Warning: Unpack is already enabled by default\n" + "Success: no issues found in 1 module\n" + ) + + config_file = "[mypy]\nenable_incomplete_feature = not-a-valid-name\n" + with self.assertRaises(SystemExit): + run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + def test_no_modules(self) -> None: output = io.StringIO() with contextlib.redirect_stdout(output): From 3f8c6cbf2d1d174065971d478e61144ed9c7a225 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 10 Aug 2024 13:33:11 -0700 Subject: [PATCH 0726/1617] Resolve TypeVar upper bounds in functools.partial (#17660) Mostly fixes #17646 --- mypy/checker.py | 2 ++ test-data/unit/check-functools.test | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 82633a5b6c0e..db65660bbfbd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -683,6 +683,8 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab inner_type = get_proper_type(inner_type) outer_type: CallableType | None = None if inner_type is not None and not isinstance(inner_type, AnyType): + if isinstance(inner_type, TypeVarLikeType): + inner_type = get_proper_type(inner_type.upper_bound) if isinstance(inner_type, TypeType): if isinstance(inner_type.item, Instance): inner_type = expand_type_by_instance( diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 710d3e66dfad..9f8fbd42440b 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -541,3 +541,19 @@ p(1, "no") # E: Argument 2 to "A" has incompatible type "str"; expected "int" q: partial[A] = partial(A, 1) # OK [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarBound] +from typing import Callable, TypeVar, Type +import functools + +T = TypeVar("T", bound=Callable[[str, int], str]) +S = TypeVar("S", bound=Type[int]) + +def foo(f: T) -> T: + g = functools.partial(f, "foo") + return f + +def bar(f: S) -> S: + g = functools.partial(f, "foo") + return f +[builtins fixtures/primitives.pyi] From 2632ea363191502b9ddb10553c7fedd553737b06 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sun, 11 Aug 2024 00:46:20 +0200 Subject: [PATCH 0727/1617] stubtest: Add support for cached_property (#17626) Fixes #17625 --- mypy/stubtest.py | 3 ++ mypy/test/teststubtest.py | 100 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index ca17ccfe2f5b..c54f83f33b00 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1224,6 +1224,9 @@ def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[s if isinstance(runtime, property): yield from _verify_final_method(stub.func, runtime.fget, MISSING) return + if isinstance(runtime, functools.cached_property): + yield from _verify_final_method(stub.func, runtime.func, MISSING) + return if inspect.isdatadescriptor(runtime): # It's enough like a property... return diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index c10c683ffdac..c6e92258e7f4 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -893,6 +893,106 @@ class FineAndDandy: error=None, ) + @collect_cases + def test_cached_property(self) -> Iterator[Case]: + yield Case( + stub=""" + from functools import cached_property + class Good: + @cached_property + def read_only_attr(self) -> int: ... + @cached_property + def read_only_attr2(self) -> int: ... + """, + runtime=""" + import functools as ft + from functools import cached_property + class Good: + @cached_property + def read_only_attr(self): return 1 + @ft.cached_property + def read_only_attr2(self): return 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class Bad: + @cached_property + def f(self) -> int: ... + """, + runtime=""" + class Bad: + def f(self) -> int: return 1 + """, + error="Bad.f", + ) + yield Case( + stub=""" + from functools import cached_property + class GoodCachedAttr: + @cached_property + def f(self) -> int: ... + """, + runtime=""" + class GoodCachedAttr: + f = 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class BadCachedAttr: + @cached_property + def f(self) -> str: ... + """, + runtime=""" + class BadCachedAttr: + f = 1 + """, + error="BadCachedAttr.f", + ) + yield Case( + stub=""" + from functools import cached_property + from typing import final + class FinalGood: + @cached_property + @final + def attr(self) -> int: ... + """, + runtime=""" + from functools import cached_property + from typing import final + class FinalGood: + @cached_property + @final + def attr(self): + return 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class FinalBad: + @cached_property + def attr(self) -> int: ... + """, + runtime=""" + from functools import cached_property + from typing_extensions import final + class FinalBad: + @cached_property + @final + def attr(self): + return 1 + """, + error="FinalBad.attr", + ) + @collect_cases def test_var(self) -> Iterator[Case]: yield Case(stub="x1: int", runtime="x1 = 5", error=None) From 3da16bd6004ec9685c476f77526abd36e7632813 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 11 Aug 2024 22:26:32 +0100 Subject: [PATCH 0728/1617] An alternative fix for a union-like literal string (#17639) It is unfortunate to add two extra slots to a common type (and I guess this is why it was rejected in the original PR), but all other alternatives I tried are hacky and/or dangerous. So, this is a price to pay for introducing a new type syntax. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/fastparse.py | 4 +--- mypy/typeanal.py | 6 +++++- mypy/types.py | 16 +++++++++++++--- test-data/unit/check-literal.test | 4 ++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index ab7eef924bd1..abcce74c6064 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -331,12 +331,10 @@ def parse_type_string( """ try: _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None) - if isinstance(node, UnboundType) and node.original_str_expr is None: + if isinstance(node, (UnboundType, UnionType)) and node.original_str_expr is None: node.original_str_expr = expr_string node.original_str_fallback = expr_fallback_name return node - elif isinstance(node, UnionType): - return node else: return RawExpressionType(expr_string, expr_fallback_name, line, column) except (SyntaxError, ValueError): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index f88c3f91d1c6..274b4b893a98 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1615,7 +1615,11 @@ def analyze_literal_type(self, t: UnboundType) -> Type: def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None: # This UnboundType was originally defined as a string. - if isinstance(arg, UnboundType) and arg.original_str_expr is not None: + if ( + isinstance(arg, ProperType) + and isinstance(arg, (UnboundType, UnionType)) + and arg.original_str_expr is not None + ): assert arg.original_str_fallback is not None return [ LiteralType( diff --git a/mypy/types.py b/mypy/types.py index 72374c1555c9..618228c361f3 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -914,7 +914,7 @@ class UnboundType(ProperType): def __init__( self, - name: str | None, + name: str, args: Sequence[Type] | None = None, line: int = -1, column: int = -1, @@ -926,7 +926,6 @@ def __init__( super().__init__(line, column) if not args: args = [] - assert name is not None self.name = name self.args = tuple(args) # Should this type be wrapped in an Optional? @@ -2849,7 +2848,13 @@ def is_singleton_type(self) -> bool: class UnionType(ProperType): """The union type Union[T1, ..., Tn] (at least one type argument).""" - __slots__ = ("items", "is_evaluated", "uses_pep604_syntax") + __slots__ = ( + "items", + "is_evaluated", + "uses_pep604_syntax", + "original_str_expr", + "original_str_fallback", + ) def __init__( self, @@ -2868,6 +2873,11 @@ def __init__( self.is_evaluated = is_evaluated # uses_pep604_syntax is True if Union uses OR syntax (X | Y) self.uses_pep604_syntax = uses_pep604_syntax + # The meaning of these two is the same as for UnboundType. A UnionType can be + # return by type parser from a string "A|B", and we need to be able to fall back + # to plain string, when such a string appears inside a Literal[...]. + self.original_str_expr: str | None = None + self.original_str_fallback: str | None = None def can_be_true_default(self) -> bool: return any(item.can_be_true for item in self.items) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index e8d942f5d74d..6d76ce176aaf 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -12,8 +12,12 @@ reveal_type(g1) # N: Revealed type is "def (x: Literal['A['])" def f2(x: 'A B') -> None: pass # E: Invalid type comment or annotation def g2(x: Literal['A B']) -> None: pass +def h2(x: 'A|int') -> None: pass # E: Name "A" is not defined +def i2(x: Literal['A|B']) -> None: pass reveal_type(f2) # N: Revealed type is "def (x: Any)" reveal_type(g2) # N: Revealed type is "def (x: Literal['A B'])" +reveal_type(h2) # N: Revealed type is "def (x: Union[Any, builtins.int])" +reveal_type(i2) # N: Revealed type is "def (x: Literal['A|B'])" [builtins fixtures/tuple.pyi] [out] From f0c394a8b9ed44ef9830ac3a7b2d13df2f60f084 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 12 Aug 2024 19:21:50 +0300 Subject: [PATCH 0729/1617] Stubgen add `--version` (#17662) --- mypy/stubgen.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 4e6e3efc000b..f4a418838161 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -54,6 +54,7 @@ import mypy.parse import mypy.traverser import mypy.util +import mypy.version from mypy.build import build from mypy.errors import CompileError, Errors from mypy.find_sources import InvalidSourceList, create_source_list @@ -1847,6 +1848,9 @@ def parse_options(args: list[str]) -> Options: dest="files", help="generate stubs for given files or directories", ) + parser.add_argument( + "--version", action="version", version="%(prog)s " + mypy.version.__version__ + ) ns = parser.parse_args(args) From 5b46f1c4baeb6271cbf3fc4ab57f8d78b0232e3f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 12 Aug 2024 19:40:32 +0300 Subject: [PATCH 0730/1617] [stubgen] Fix crash on literal class-level keywords (#17663) Closes https://github.com/python/mypy/issues/17661 --- mypy/stubgen.py | 22 ++++++++++++++++++++-- test-data/unit/stubgen.test | 10 ++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index f4a418838161..14417f55545e 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -305,9 +305,26 @@ def visit_name_expr(self, node: NameExpr) -> str: def visit_member_expr(self, o: MemberExpr) -> str: return self._visit_ref_expr(o) - def visit_str_expr(self, node: StrExpr) -> str: + def _visit_literal_node( + self, node: StrExpr | BytesExpr | IntExpr | FloatExpr | ComplexExpr + ) -> str: return repr(node.value) + def visit_str_expr(self, node: StrExpr) -> str: + return self._visit_literal_node(node) + + def visit_bytes_expr(self, node: BytesExpr) -> str: + return f"b{self._visit_literal_node(node)}" + + def visit_int_expr(self, node: IntExpr) -> str: + return self._visit_literal_node(node) + + def visit_float_expr(self, node: FloatExpr) -> str: + return self._visit_literal_node(node) + + def visit_complex_expr(self, node: ComplexExpr) -> str: + return self._visit_literal_node(node) + def visit_index_expr(self, node: IndexExpr) -> str: base_fullname = self.stubgen.get_fullname(node.base) if base_fullname == "typing.Union": @@ -805,7 +822,8 @@ def get_base_types(self, cdef: ClassDef) -> list[str]: for name, value in cdef.keywords.items(): if name == "metaclass": continue # handled separately - base_types.append(f"{name}={value.accept(p)}") + processed_value = value.accept(p) or "..." # at least, don't crash + base_types.append(f"{name}={processed_value}") return base_types def get_class_decorators(self, cdef: ClassDef) -> list[str]: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 94d0edb2ae37..fe0538159aa3 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4405,3 +4405,13 @@ Y = int | None Z = Incomplete W = int | str | None R = type[int | str] | None + +[case testClassInheritanceWithKeywordsConstants] +class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... +[out] +class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... + +[case testClassInheritanceWithKeywordsDynamic] +class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... +[out] +class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... From 8332c6e7992fa9655b79ef55c410a805ad1e6d34 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 14 Aug 2024 10:08:43 +0300 Subject: [PATCH 0731/1617] Add optional stderr result to `run_stubtest` in tests (#17636) This adds a test for incorrect error code in `stubtest`'s config file. Refs https://github.com/python/mypy/issues/17628 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jelle Zijlstra --- mypy/test/teststubtest.py | 42 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index c6e92258e7f4..70687b499651 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -144,9 +144,9 @@ def __invert__(self: _T) -> _T: pass """ -def run_stubtest( +def run_stubtest_with_stderr( stub: str, runtime: str, options: list[str], config_file: str | None = None -) -> str: +) -> tuple[str, str]: with use_tmp_dir(TEST_MODULE_NAME) as tmp_dir: with open("builtins.pyi", "w") as f: f.write(stubtest_builtins_stub) @@ -163,13 +163,26 @@ def run_stubtest( f.write(config_file) options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"] output = io.StringIO() - with contextlib.redirect_stdout(output): + outerr = io.StringIO() + with contextlib.redirect_stdout(output), contextlib.redirect_stderr(outerr): test_stubs(parse_options([TEST_MODULE_NAME] + options), use_builtins_fixtures=True) - return remove_color_code( - output.getvalue() - # remove cwd as it's not available from outside - .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") - ) + filtered_output = remove_color_code( + output.getvalue() + # remove cwd as it's not available from outside + .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") + ) + filtered_outerr = remove_color_code( + outerr.getvalue() + # remove cwd as it's not available from outside + .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") + ) + return filtered_output, filtered_outerr + + +def run_stubtest( + stub: str, runtime: str, options: list[str], config_file: str | None = None +) -> str: + return run_stubtest_with_stderr(stub, runtime, options, config_file)[0] class Case: @@ -2590,6 +2603,19 @@ def test_config_file_error_codes(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "Success: no issues found in 1 module\n" + def test_config_file_error_codes_invalid(self) -> None: + runtime = "temp = 5\n" + stub = "temp: int\n" + config_file = "[mypy]\ndisable_error_code = not-a-valid-name\n" + output, outerr = run_stubtest_with_stderr( + stub=stub, runtime=runtime, options=[], config_file=config_file + ) + assert output == "Success: no issues found in 1 module\n" + assert outerr == ( + "test_module_config.ini: [mypy]: disable_error_code: " + "Invalid error code(s): not-a-valid-name\n" + ) + def test_config_file_wrong_incomplete_feature(self) -> None: runtime = "x = 1\n" stub = "x: int\n" From fe15ee69b9225f808f8ed735671b73c31ae1bed8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 14 Aug 2024 22:34:18 +0100 Subject: [PATCH 0732/1617] [mypyc] Avoid uses of _PyObject_CallMethodOneArg on 3.13 (#17526) It's no longer available in CPython 3.13 betas. This fixes some dict primitives on 3.13. Work on mypyc/mypyc#1056. --- mypyc/lib-rt/dict_ops.c | 12 ++++++++++-- mypyc/lib-rt/pythonsupport.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/dict_ops.c b/mypyc/lib-rt/dict_ops.c index 031df8f63c49..b33233521afd 100644 --- a/mypyc/lib-rt/dict_ops.c +++ b/mypyc/lib-rt/dict_ops.c @@ -78,7 +78,11 @@ PyObject *CPyDict_SetDefault(PyObject *dict, PyObject *key, PyObject *value) { return ret; } _Py_IDENTIFIER(setdefault); - return _PyObject_CallMethodIdObjArgs(dict, &PyId_setdefault, key, value, NULL); + PyObject *name = _PyUnicode_FromId(&PyId_setdefault); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodObjArgs(dict, name, key, value, NULL); } PyObject *CPyDict_SetDefaultWithNone(PyObject *dict, PyObject *key) { @@ -133,7 +137,11 @@ static inline int CPy_ObjectToStatus(PyObject *obj) { static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) { _Py_IDENTIFIER(update); - PyObject *res = _PyObject_CallMethodIdOneArg(dict, &PyId_update, stuff); + PyObject *name = _PyUnicode_FromId(&PyId_update); /* borrowed */ + if (name == NULL) { + return -1; + } + PyObject *res = PyObject_CallMethodOneArg(dict, name, stuff); return CPy_ObjectToStatus(res); } diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 8edc9abcf9f8..bf7e5203758d 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -401,6 +401,8 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { _PyObject_CallMethodIdObjArgs((self), (name), NULL) #define _PyObject_CallMethodIdOneArg(self, name, arg) \ _PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL) +#define PyObject_CallMethodOneArg(self, name, arg) \ + PyObject_CallMethodObjArgs((self), (name), (arg), NULL) #endif #if CPY_3_13_FEATURES From 35679e2e7e5f2c8e4408806cc8deff4c15b4647f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 30 Aug 2024 11:47:14 +0100 Subject: [PATCH 0733/1617] Fix another crash scenario on recursive tuple types (#17708) Fixes https://github.com/python/mypy/issues/17691 This looks quite ad-hoc, but the only clean alternative is to remove the existing recursive types optimization we have in `subtypes.py`. But the best I can get without that optimization is -7% performance penalty (on self-check). So I think it is not worth it, since it is still a corner case. --- mypy/typeops.py | 9 +++++++-- mypy/types.py | 10 ++++++++-- test-data/unit/check-recursive-types.test | 12 ++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 036d782be89c..d22448a715e5 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -114,7 +114,11 @@ def tuple_fallback(typ: TupleType) -> Instance: else: items.append(item) return Instance( - info, [make_simplified_union(items)], extra_attrs=typ.partial_fallback.extra_attrs + info, + # Note: flattening recursive unions is dangerous, since it can fool recursive + # types optimization in subtypes.py and go into infinite recursion. + [make_simplified_union(items, handle_recursive=False)], + extra_attrs=typ.partial_fallback.extra_attrs, ) @@ -440,6 +444,7 @@ def make_simplified_union( *, keep_erased: bool = False, contract_literals: bool = True, + handle_recursive: bool = True, ) -> ProperType: """Build union type with redundant union items removed. @@ -465,7 +470,7 @@ def make_simplified_union( to_union(). """ # Step 1: expand all nested unions - items = flatten_nested_unions(items) + items = flatten_nested_unions(items, handle_recursive=handle_recursive) # Step 2: fast path for single item if len(items) == 1: diff --git a/mypy/types.py b/mypy/types.py index 618228c361f3..78244d0f9cf4 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3629,7 +3629,7 @@ def extend_args_for_prefix_and_suffix( def flatten_nested_unions( - types: Sequence[Type], handle_type_alias_type: bool = True + types: Sequence[Type], *, handle_type_alias_type: bool = True, handle_recursive: bool = True ) -> list[Type]: """Flatten nested unions in a type list.""" if not isinstance(types, list): @@ -3643,7 +3643,13 @@ def flatten_nested_unions( flat_items: list[Type] = [] for t in typelist: - tp = get_proper_type(t) if handle_type_alias_type else t + if handle_type_alias_type: + if not handle_recursive and isinstance(t, TypeAliasType) and t.is_recursive: + tp: Type = t + else: + tp = get_proper_type(t) + else: + tp = t if isinstance(tp, ProperType) and isinstance(tp, UnionType): flat_items.extend( flatten_nested_unions(tp.items, handle_type_alias_type=handle_type_alias_type) diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index d5c8acd1bc15..ac1ea0c0035a 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -994,3 +994,15 @@ class T2(Tuple[T1, "T4", "T4"]): ... class T3(Tuple[str, "T4", "T4"]): ... T4 = Union[T2, T3] [builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback5] +from typing import Protocol, Tuple, Union + +class Proto(Protocol): + def __len__(self) -> int: ... + +A = Union[Proto, Tuple[A]] +ta: Tuple[A] +p: Proto +p = ta +[builtins fixtures/tuple.pyi] From 0a6d40b125f6b353621016d8a9022212104f7877 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:50:02 +0200 Subject: [PATCH 0734/1617] Sync typeshed (#17724) The automatic sync failed due to a merge conflict. Source commit: https://github.com/python/typeshed/commit/661fe27658acd6b6058addcb29375381b8cae56e --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- ...ert-sum-literal-integer-change-13961.patch | 12 +- mypy/typeshed/stdlib/_ast.pyi | 451 +++++++++++++++++- mypy/typeshed/stdlib/_collections_abc.pyi | 9 +- mypy/typeshed/stdlib/_ctypes.pyi | 4 +- mypy/typeshed/stdlib/_operator.pyi | 6 +- mypy/typeshed/stdlib/_stat.pyi | 12 +- mypy/typeshed/stdlib/_thread.pyi | 26 +- mypy/typeshed/stdlib/_tkinter.pyi | 4 +- mypy/typeshed/stdlib/argparse.pyi | 12 +- mypy/typeshed/stdlib/ast.pyi | 35 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 12 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 2 +- mypy/typeshed/stdlib/builtins.pyi | 15 +- mypy/typeshed/stdlib/cmd.pyi | 3 +- mypy/typeshed/stdlib/collections/__init__.pyi | 9 +- mypy/typeshed/stdlib/contextlib.pyi | 2 + mypy/typeshed/stdlib/crypt.pyi | 11 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 5 + mypy/typeshed/stdlib/ctypes/_endian.pyi | 9 +- mypy/typeshed/stdlib/dataclasses.pyi | 15 +- mypy/typeshed/stdlib/datetime.pyi | 4 +- mypy/typeshed/stdlib/distutils/cmd.pyi | 120 +++++ .../stdlib/distutils/command/__init__.pyi | 48 ++ .../stdlib/distutils/command/check.pyi | 4 +- .../stdlib/distutils/command/config.pyi | 4 +- .../stdlib/distutils/command/install.pyi | 13 +- .../stdlib/distutils/command/install_lib.pyi | 4 +- mypy/typeshed/stdlib/distutils/core.pyi | 4 +- .../stdlib/distutils/cygwinccompiler.pyi | 10 +- mypy/typeshed/stdlib/distutils/debug.pyi | 4 +- mypy/typeshed/stdlib/distutils/dist.pyi | 153 +++++- .../stdlib/distutils/fancy_getopt.pyi | 12 +- mypy/typeshed/stdlib/distutils/log.pyi | 12 +- mypy/typeshed/stdlib/distutils/sysconfig.pyi | 14 +- mypy/typeshed/stdlib/email/message.pyi | 9 +- mypy/typeshed/stdlib/email/utils.pyi | 5 +- mypy/typeshed/stdlib/filecmp.pyi | 25 +- mypy/typeshed/stdlib/ftplib.pyi | 2 +- .../stdlib/importlib/metadata/__init__.pyi | 4 +- mypy/typeshed/stdlib/json/encoder.pyi | 12 +- .../stdlib/lib2to3/fixes/fix_unicode.pyi | 2 +- mypy/typeshed/stdlib/logging/__init__.pyi | 67 ++- .../stdlib/multiprocessing/sharedctypes.pyi | 8 +- mypy/typeshed/stdlib/operator.pyi | 3 + mypy/typeshed/stdlib/os/__init__.pyi | 4 +- mypy/typeshed/stdlib/pathlib.pyi | 29 +- mypy/typeshed/stdlib/pdb.pyi | 2 +- mypy/typeshed/stdlib/poplib.pyi | 3 +- mypy/typeshed/stdlib/pty.pyi | 11 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 6 +- mypy/typeshed/stdlib/pyexpat/errors.pyi | 88 ++-- mypy/typeshed/stdlib/pyexpat/model.pyi | 22 +- mypy/typeshed/stdlib/re.pyi | 18 +- mypy/typeshed/stdlib/socketserver.pyi | 13 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 10 +- mypy/typeshed/stdlib/symtable.pyi | 5 +- mypy/typeshed/stdlib/tarfile.pyi | 2 +- mypy/typeshed/stdlib/tempfile.pyi | 4 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 34 +- mypy/typeshed/stdlib/tkinter/constants.pyi | 12 +- mypy/typeshed/stdlib/tkinter/dialog.pyi | 4 +- mypy/typeshed/stdlib/tkinter/messagebox.pyi | 34 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 11 +- mypy/typeshed/stdlib/turtle.pyi | 12 +- mypy/typeshed/stdlib/types.pyi | 6 +- mypy/typeshed/stdlib/typing.pyi | 11 +- mypy/typeshed/stdlib/unittest/mock.pyi | 2 +- mypy/typeshed/stdlib/unittest/runner.pyi | 46 +- mypy/typeshed/stdlib/urllib/parse.pyi | 4 +- mypy/typeshed/stdlib/urllib/request.pyi | 1 + mypy/typeshed/stdlib/xml/dom/__init__.pyi | 44 +- .../stdlib/xml/etree/ElementInclude.pyi | 9 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 4 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 81 +++- mypy/typeshed/stdlib/zipfile/_path.pyi | 1 + test-data/unit/pythoneval.test | 4 +- 76 files changed, 1365 insertions(+), 369 deletions(-) diff --git a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch index 044e672bfda5..331628af1424 100644 --- a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch +++ b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch @@ -1,4 +1,4 @@ -From 44bc98bd50e7170887f0740b53ed95a8eb04f00e Mon Sep 17 00:00:00 2001 +From 58c6a6ab863c1c38e95ccafaf13792ed9c00e499 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH] Revert sum literal integer change (#13961) @@ -19,18 +19,18 @@ within mypy, I might pursue upstreaming this in typeshed. 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index 99919c64c..680cd5561 100644 +index ea9f8c894..a6065cc67 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi -@@ -1596,7 +1596,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit +@@ -1653,7 +1653,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload --def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] -+def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] +-def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... ++def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... @overload def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload -- -2.39.3 (Apple Git-146) +2.46.0 diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index d14c6d39a162..1dbceac428c1 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,7 +1,7 @@ import sys import typing_extensions from typing import Any, ClassVar, Generic, Literal, TypedDict, overload -from typing_extensions import Unpack +from typing_extensions import Self, Unpack PyCF_ONLY_AST: Literal[1024] PyCF_TYPE_COMMENTS: Literal[4096] @@ -34,6 +34,9 @@ class AST: if sys.version_info >= (3, 13): _field_types: ClassVar[dict[str, Any]] + if sys.version_info >= (3, 14): + def __replace__(self) -> Self: ... + class mod(AST): ... class type_ignore(AST): ... @@ -44,6 +47,9 @@ class TypeIgnore(type_ignore): tag: str def __init__(self, lineno: int, tag: str) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, lineno: int = ..., tag: str = ...) -> Self: ... + class FunctionType(mod): if sys.version_info >= (3, 10): __match_args__ = ("argtypes", "returns") @@ -57,6 +63,9 @@ class FunctionType(mod): else: def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, argtypes: list[expr] = ..., returns: expr = ...) -> Self: ... + class Module(mod): if sys.version_info >= (3, 10): __match_args__ = ("body", "type_ignores") @@ -67,6 +76,9 @@ class Module(mod): else: def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> Self: ... + class Interactive(mod): if sys.version_info >= (3, 10): __match_args__ = ("body",) @@ -76,12 +88,18 @@ class Interactive(mod): else: def __init__(self, body: list[stmt]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ...) -> Self: ... + class Expression(mod): if sys.version_info >= (3, 10): __match_args__ = ("body",) body: expr def __init__(self, body: expr) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, body: expr = ...) -> Self: ... + class stmt(AST): lineno: int col_offset: int @@ -89,6 +107,9 @@ class stmt(AST): end_col_offset: int | None def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + class FunctionDef(stmt): if sys.version_info >= (3, 12): __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") @@ -152,6 +173,19 @@ class FunctionDef(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = ..., + type_comment: str | None = ..., + type_params: list[type_param] = ..., + ) -> Self: ... + class AsyncFunctionDef(stmt): if sys.version_info >= (3, 12): __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") @@ -215,6 +249,19 @@ class AsyncFunctionDef(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + ) -> Self: ... + class ClassDef(stmt): if sys.version_info >= (3, 12): __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") @@ -260,12 +307,28 @@ class ClassDef(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class Return(stmt): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr | None def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Delete(stmt): if sys.version_info >= (3, 10): __match_args__ = ("targets",) @@ -275,6 +338,9 @@ class Delete(stmt): else: def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Assign(stmt): if sys.version_info >= (3, 10): __match_args__ = ("targets", "value", "type_comment") @@ -295,6 +361,11 @@ class Assign(stmt): self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, targets: list[expr] = ..., value: expr = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class AugAssign(stmt): if sys.version_info >= (3, 10): __match_args__ = ("target", "op", "value") @@ -305,6 +376,16 @@ class AugAssign(stmt): self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + op: operator = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class AnnAssign(stmt): if sys.version_info >= (3, 10): __match_args__ = ("target", "annotation", "value", "simple") @@ -332,6 +413,17 @@ class AnnAssign(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + annotation: expr = ..., + value: expr | None = ..., + simple: int = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class For(stmt): if sys.version_info >= (3, 10): __match_args__ = ("target", "iter", "body", "orelse", "type_comment") @@ -361,6 +453,18 @@ class For(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class AsyncFor(stmt): if sys.version_info >= (3, 10): __match_args__ = ("target", "iter", "body", "orelse", "type_comment") @@ -390,6 +494,18 @@ class AsyncFor(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class While(stmt): if sys.version_info >= (3, 10): __match_args__ = ("test", "body", "orelse") @@ -403,6 +519,9 @@ class While(stmt): else: def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> Self: ... + class If(stmt): if sys.version_info >= (3, 10): __match_args__ = ("test", "body", "orelse") @@ -416,6 +535,11 @@ class If(stmt): else: def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class With(stmt): if sys.version_info >= (3, 10): __match_args__ = ("items", "body", "type_comment") @@ -435,6 +559,16 @@ class With(stmt): self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class AsyncWith(stmt): if sys.version_info >= (3, 10): __match_args__ = ("items", "body", "type_comment") @@ -454,6 +588,16 @@ class AsyncWith(stmt): self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class Raise(stmt): if sys.version_info >= (3, 10): __match_args__ = ("exc", "cause") @@ -461,6 +605,9 @@ class Raise(stmt): cause: expr | None def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, exc: expr | None = ..., cause: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Try(stmt): if sys.version_info >= (3, 10): __match_args__ = ("body", "handlers", "orelse", "finalbody") @@ -487,6 +634,17 @@ class Try(stmt): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + if sys.version_info >= (3, 11): class TryStar(stmt): __match_args__ = ("body", "handlers", "orelse", "finalbody") @@ -513,6 +671,17 @@ if sys.version_info >= (3, 11): **kwargs: Unpack[_Attributes], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class Assert(stmt): if sys.version_info >= (3, 10): __match_args__ = ("test", "msg") @@ -520,6 +689,9 @@ class Assert(stmt): msg: expr | None def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, msg: expr | None, **kwargs: Unpack[_Attributes]) -> Self: ... + class Import(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) @@ -529,6 +701,9 @@ class Import(stmt): else: def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class ImportFrom(stmt): if sys.version_info >= (3, 10): __match_args__ = ("module", "names", "level") @@ -550,6 +725,11 @@ class ImportFrom(stmt): self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, module: str | None = ..., names: list[alias] = ..., level: int = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Global(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) @@ -559,6 +739,9 @@ class Global(stmt): else: def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> Self: ... + class Nonlocal(stmt): if sys.version_info >= (3, 10): __match_args__ = ("names",) @@ -568,12 +751,18 @@ class Nonlocal(stmt): else: def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Expr(stmt): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Pass(stmt): ... class Break(stmt): ... class Continue(stmt): ... @@ -585,6 +774,9 @@ class expr(AST): end_col_offset: int | None def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + class BoolOp(expr): if sys.version_info >= (3, 10): __match_args__ = ("op", "values") @@ -595,6 +787,9 @@ class BoolOp(expr): else: def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, op: boolop = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class BinOp(expr): if sys.version_info >= (3, 10): __match_args__ = ("left", "op", "right") @@ -603,6 +798,11 @@ class BinOp(expr): right: expr def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., op: operator = ..., right: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class UnaryOp(expr): if sys.version_info >= (3, 10): __match_args__ = ("op", "operand") @@ -610,6 +810,9 @@ class UnaryOp(expr): operand: expr def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, op: unaryop = ..., operand: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Lambda(expr): if sys.version_info >= (3, 10): __match_args__ = ("args", "body") @@ -617,6 +820,9 @@ class Lambda(expr): body: expr def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, args: arguments = ..., body: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class IfExp(expr): if sys.version_info >= (3, 10): __match_args__ = ("test", "body", "orelse") @@ -625,6 +831,11 @@ class IfExp(expr): orelse: expr def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: expr = ..., orelse: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Dict(expr): if sys.version_info >= (3, 10): __match_args__ = ("keys", "values") @@ -635,6 +846,11 @@ class Dict(expr): else: def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Set(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts",) @@ -644,6 +860,9 @@ class Set(expr): else: def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class ListComp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") @@ -654,6 +873,11 @@ class ListComp(expr): else: def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class SetComp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") @@ -664,6 +888,11 @@ class SetComp(expr): else: def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class DictComp(expr): if sys.version_info >= (3, 10): __match_args__ = ("key", "value", "generators") @@ -677,6 +906,11 @@ class DictComp(expr): else: def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, key: expr = ..., value: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class GeneratorExp(expr): if sys.version_info >= (3, 10): __match_args__ = ("elt", "generators") @@ -687,24 +921,38 @@ class GeneratorExp(expr): else: def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Await(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Yield(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr | None def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class YieldFrom(expr): if sys.version_info >= (3, 10): __match_args__ = ("value",) value: expr def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Compare(expr): if sys.version_info >= (3, 10): __match_args__ = ("left", "ops", "comparators") @@ -718,6 +966,11 @@ class Compare(expr): else: def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Call(expr): if sys.version_info >= (3, 10): __match_args__ = ("func", "args", "keywords") @@ -731,6 +984,11 @@ class Call(expr): else: def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, func: expr = ..., args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class FormattedValue(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "conversion", "format_spec") @@ -739,6 +997,11 @@ class FormattedValue(expr): format_spec: expr | None def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., conversion: int = ..., format_spec: expr | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class JoinedStr(expr): if sys.version_info >= (3, 10): __match_args__ = ("values",) @@ -748,16 +1011,24 @@ class JoinedStr(expr): else: def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Constant(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "kind") value: Any # None, str, bytes, bool, int, float, complex, Ellipsis kind: str | None - # Aliases for value, for backwards compatibility - s: Any - n: int | float | complex + if sys.version_info < (3, 14): + # Aliases for value, for backwards compatibility + s: Any + n: int | float | complex + def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Any = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class NamedExpr(expr): if sys.version_info >= (3, 10): __match_args__ = ("target", "value") @@ -765,6 +1036,9 @@ class NamedExpr(expr): value: expr def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, target: Name = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Attribute(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "attr", "ctx") @@ -773,6 +1047,11 @@ class Attribute(expr): ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., attr: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + if sys.version_info >= (3, 9): _Slice: typing_extensions.TypeAlias = expr _SliceAttributes: typing_extensions.TypeAlias = _Attributes @@ -792,6 +1071,16 @@ class Slice(_Slice): self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + lower: expr | None = ..., + upper: expr | None = ..., + step: expr | None = ..., + **kwargs: Unpack[_SliceAttributes], + ) -> Self: ... + if sys.version_info < (3, 9): class ExtSlice(slice): dims: list[slice] @@ -809,6 +1098,11 @@ class Subscript(expr): ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., slice: _Slice = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class Starred(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "ctx") @@ -816,6 +1110,9 @@ class Starred(expr): ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Name(expr): if sys.version_info >= (3, 10): __match_args__ = ("id", "ctx") @@ -823,6 +1120,9 @@ class Name(expr): ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, id: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class List(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts", "ctx") @@ -833,6 +1133,9 @@ class List(expr): else: def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class Tuple(expr): if sys.version_info >= (3, 10): __match_args__ = ("elts", "ctx") @@ -845,6 +1148,9 @@ class Tuple(expr): else: def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class expr_context(AST): ... if sys.version_info < (3, 9): @@ -908,6 +1214,9 @@ class comprehension(AST): else: def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, target: expr = ..., iter: expr = ..., ifs: list[expr] = ..., is_async: int = ...) -> Self: ... + class excepthandler(AST): lineno: int col_offset: int @@ -915,6 +1224,11 @@ class excepthandler(AST): end_col_offset: int | None def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int | None = ..., end_col_offset: int | None = ... + ) -> Self: ... + class ExceptHandler(excepthandler): if sys.version_info >= (3, 10): __match_args__ = ("type", "name", "body") @@ -935,6 +1249,16 @@ class ExceptHandler(excepthandler): self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + type: expr | None = ..., + name: _Identifier | None = ..., + body: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class arguments(AST): if sys.version_info >= (3, 10): __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") @@ -993,6 +1317,19 @@ class arguments(AST): defaults: list[expr], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = ..., + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = ..., + defaults: list[expr] = ..., + ) -> Self: ... + class arg(AST): lineno: int col_offset: int @@ -1007,6 +1344,16 @@ class arg(AST): self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + arg: _Identifier = ..., + annotation: expr | None = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + class keyword(AST): lineno: int col_offset: int @@ -1021,6 +1368,9 @@ class keyword(AST): @overload def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, arg: _Identifier | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class alias(AST): lineno: int col_offset: int @@ -1032,6 +1382,9 @@ class alias(AST): asname: _Identifier | None def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, name: str = ..., asname: _Identifier | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + class withitem(AST): if sys.version_info >= (3, 10): __match_args__ = ("context_expr", "optional_vars") @@ -1039,6 +1392,9 @@ class withitem(AST): optional_vars: expr | None def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, context_expr: expr = ..., optional_vars: expr | None = ...) -> Self: ... + if sys.version_info >= (3, 10): class Match(stmt): __match_args__ = ("subject", "cases") @@ -1049,6 +1405,11 @@ if sys.version_info >= (3, 10): else: def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + class pattern(AST): lineno: int col_offset: int @@ -1056,6 +1417,11 @@ if sys.version_info >= (3, 10): end_col_offset: int def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ... + ) -> Self: ... + # Without the alias, Pyright complains variables named pattern are recursively defined _Pattern: typing_extensions.TypeAlias = pattern @@ -1072,16 +1438,25 @@ if sys.version_info >= (3, 10): @overload def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, pattern: _Pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ... + class MatchValue(pattern): __match_args__ = ("value",) value: expr def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + class MatchSingleton(pattern): __match_args__ = ("value",) value: Literal[True, False] | None def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Literal[True, False] | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + class MatchSequence(pattern): __match_args__ = ("patterns",) patterns: list[pattern] @@ -1090,11 +1465,17 @@ if sys.version_info >= (3, 10): else: def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + class MatchStar(pattern): __match_args__ = ("name",) name: _Identifier | None def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + class MatchMapping(pattern): __match_args__ = ("keys", "patterns", "rest") keys: list[expr] @@ -1117,6 +1498,16 @@ if sys.version_info >= (3, 10): **kwargs: Unpack[_Attributes[int]], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + class MatchClass(pattern): __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") cls: expr @@ -1142,6 +1533,17 @@ if sys.version_info >= (3, 10): **kwargs: Unpack[_Attributes[int]], ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + cls: expr = ..., + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + class MatchAs(pattern): __match_args__ = ("pattern", "name") pattern: _Pattern | None @@ -1150,6 +1552,11 @@ if sys.version_info >= (3, 10): self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] ) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, pattern: _Pattern | None = ..., name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + class MatchOr(pattern): __match_args__ = ("patterns",) patterns: list[pattern] @@ -1158,6 +1565,9 @@ if sys.version_info >= (3, 10): else: def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + if sys.version_info >= (3, 12): class type_param(AST): lineno: int @@ -1166,6 +1576,9 @@ if sys.version_info >= (3, 12): end_col_offset: int def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes[int]]) -> Self: ... + class TypeVar(type_param): if sys.version_info >= (3, 13): __match_args__ = ("name", "bound", "default_value") @@ -1185,6 +1598,16 @@ if sys.version_info >= (3, 12): else: def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + bound: expr | None = ..., + default_value: expr | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + class ParamSpec(type_param): if sys.version_info >= (3, 13): __match_args__ = ("name", "default_value") @@ -1199,6 +1622,11 @@ if sys.version_info >= (3, 12): else: def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + class TypeVarTuple(type_param): if sys.version_info >= (3, 13): __match_args__ = ("name", "default_value") @@ -1213,6 +1641,11 @@ if sys.version_info >= (3, 12): else: def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + class TypeAlias(stmt): __match_args__ = ("name", "type_params", "value") name: Name @@ -1231,3 +1664,13 @@ if sys.version_info >= (3, 12): def __init__( self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: Name = ..., + type_params: list[type_param] = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 127488ee382c..8b1ac9c7eb8b 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -1,13 +1,12 @@ import sys from abc import abstractmethod from types import MappingProxyType -from typing import ( # noqa: Y022,Y038,Y057 +from typing import ( # noqa: Y022,Y038 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, Awaitable as Awaitable, - ByteString as ByteString, Callable as Callable, Collection as Collection, Container as Container, @@ -59,8 +58,12 @@ __all__ = [ "ValuesView", "Sequence", "MutableSequence", - "ByteString", ] +if sys.version_info < (3, 14): + from typing import ByteString as ByteString # noqa: Y057 + + __all__ += ["ByteString"] + if sys.version_info >= (3, 12): __all__ += ["Buffer"] diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index f7da03a67ead..0fe7521d7749 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -51,8 +51,8 @@ class _CDataMeta(type): # By default mypy complains about the following two methods, because strictly speaking cls # might not be a Type[_CT]. However this can never actually happen, because the only class that # uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here. - def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] class _CData(metaclass=_CDataMeta): _b_base_: int diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 69ee563b5cf4..1b0083f4e274 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import SupportsGetItem from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload -from typing_extensions import ParamSpec, TypeAlias, TypeVarTuple, Unpack +from typing_extensions import ParamSpec, TypeAlias, TypeIs, TypeVarTuple, Unpack _R = TypeVar("_R") _T = TypeVar("_T") @@ -145,3 +145,7 @@ if sys.version_info >= (3, 11): def call(obj: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... def _compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... + +if sys.version_info >= (3, 14): + def is_none(a: object, /) -> TypeIs[None]: ... + def is_not_none(a: _T | None, /) -> TypeIs[_T]: ... diff --git a/mypy/typeshed/stdlib/_stat.pyi b/mypy/typeshed/stdlib/_stat.pyi index 903571a64bca..7129a282b574 100644 --- a/mypy/typeshed/stdlib/_stat.pyi +++ b/mypy/typeshed/stdlib/_stat.pyi @@ -28,9 +28,9 @@ S_IFDIR: Final = 0o040000 # These are 0 on systems that don't support the specific kind of file. # Example: Linux doesn't support door files, so S_IFDOOR is 0 on linux. -S_IFDOOR: int -S_IFPORT: int -S_IFWHT: int +S_IFDOOR: Final[int] +S_IFPORT: Final[int] +S_IFWHT: Final[int] S_ISUID: Final = 0o4000 S_ISGID: Final = 0o2000 @@ -79,9 +79,9 @@ def S_ISWHT(mode: int, /) -> bool: ... def filemode(mode: int, /) -> str: ... if sys.platform == "win32": - IO_REPARSE_TAG_SYMLINK: int - IO_REPARSE_TAG_MOUNT_POINT: int - IO_REPARSE_TAG_APPEXECLINK: int + IO_REPARSE_TAG_SYMLINK: Final = 0xA000000C + IO_REPARSE_TAG_MOUNT_POINT: Final = 0xA0000003 + IO_REPARSE_TAG_APPEXECLINK: Final = 0x8000001B if sys.platform == "win32": FILE_ATTRIBUTE_ARCHIVE: Final = 32 diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 304cb79ec96b..b75e7608fa77 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -1,3 +1,4 @@ +import signal import sys from _typeshed import structseq from collections.abc import Callable @@ -16,16 +17,39 @@ class LockType: def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... def locked(self) -> bool: ... + def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release_lock(self) -> None: ... + def locked_lock(self) -> bool: ... def __enter__(self) -> bool: ... def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... +if sys.version_info >= (3, 13): + @final + class _ThreadHandle: + ident: int + + def join(self, timeout: float | None = None, /) -> None: ... + def is_done(self) -> bool: ... + def _set_done(self) -> None: ... + + def start_joinable_thread( + function: Callable[[], object], handle: _ThreadHandle | None = None, daemon: bool = True + ) -> _ThreadHandle: ... + lock = LockType + @overload def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... @overload def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... -def interrupt_main() -> None: ... + +if sys.version_info >= (3, 10): + def interrupt_main(signum: signal.Signals = ..., /) -> None: ... + +else: + def interrupt_main() -> None: ... + def exit() -> NoReturn: ... def allocate_lock() -> LockType: ... def get_ident() -> int: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index a7293054d293..63b1e7ca7cb4 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -106,8 +106,8 @@ EXCEPTION: Final = 8 READABLE: Final = 2 WRITABLE: Final = 4 -TCL_VERSION: str -TK_VERSION: str +TCL_VERSION: Final[str] +TK_VERSION: Final[str] @final class TkttType: diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 66fa4e15291f..2526322ac8f6 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -357,7 +357,17 @@ class Action(_AttributeHolder): if sys.version_info >= (3, 12): class BooleanOptionalAction(Action): - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 14): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool | None = None, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 13): @overload def __init__( self, diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 90ede461fe3c..80049cff4ce0 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -10,27 +10,28 @@ class _ABC(type): if sys.version_info >= (3, 9): def __init__(cls, *args: Unused) -> None: ... -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Num(Constant, metaclass=_ABC): - value: int | float | complex +if sys.version_info < (3, 14): + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Num(Constant, metaclass=_ABC): + value: int | float | complex -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Str(Constant, metaclass=_ABC): - value: str - # Aliases for value, for backwards compatibility - s: str + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Str(Constant, metaclass=_ABC): + value: str + # Aliases for value, for backwards compatibility + s: str -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Bytes(Constant, metaclass=_ABC): - value: bytes - # Aliases for value, for backwards compatibility - s: bytes + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Bytes(Constant, metaclass=_ABC): + value: bytes + # Aliases for value, for backwards compatibility + s: bytes -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class NameConstant(Constant, metaclass=_ABC): ... + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class NameConstant(Constant, metaclass=_ABC): ... -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Ellipsis(Constant, metaclass=_ABC): ... + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Ellipsis(Constant, metaclass=_ABC): ... if sys.version_info >= (3, 9): class slice(AST): ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index f23ecef126d6..bb423e857399 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -151,13 +151,13 @@ if sys.version_info >= (3, 10): @overload def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[overload-overlap] @overload - def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[overload-overlap] + def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], /, *, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -166,7 +166,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -176,7 +176,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -189,7 +189,7 @@ if sys.version_info >= (3, 10): tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index 5dd3831f9a0a..fb21c5b5fa05 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -159,7 +159,7 @@ if sys.platform != "win32": class _UnixSelectorEventLoop(BaseSelectorEventLoop): if sys.version_info >= (3, 13): - async def create_unix_server( # type: ignore[override] + async def create_unix_server( self, protocol_factory: _ProtocolFactory, path: StrPath | None = None, diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 07d2f1989558..a6065cc6777f 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -33,7 +33,8 @@ from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from types import CellType, CodeType, TracebackType -# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} are imported from collections.abc in builtins.pyi +# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} +# are imported from collections.abc in builtins.pyi from typing import ( # noqa: Y022 IO, Any, @@ -872,7 +873,9 @@ class tuple(Sequence[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -# Doesn't exist at runtime, but deleting this breaks mypy. See #2999 +# Doesn't exist at runtime, but deleting this breaks mypy and pyright. See: +# https://github.com/python/typeshed/issues/7580 +# https://github.com/python/mypy/issues/8240 @final @type_check_only class function: @@ -989,7 +992,8 @@ class dict(MutableMapping[_KT, _VT]): def keys(self) -> dict_keys[_KT, _VT]: ... def values(self) -> dict_values[_KT, _VT]: ... def items(self) -> dict_items[_KT, _VT]: ... - # Signature of `dict.fromkeys` should be kept identical to `fromkeys` methods of `OrderedDict`/`ChainMap`/`UserDict` in `collections` + # Signature of `dict.fromkeys` should be kept identical to + # `fromkeys` methods of `OrderedDict`/`ChainMap`/`UserDict` in `collections` # TODO: the true signature of `dict.fromkeys` is not expressible in the current type system. # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @@ -1649,7 +1653,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... @overload def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload @@ -1657,9 +1661,8 @@ def sum(iterable: Iterable[_AddableT1], /, start: _AddableT2) -> _AddableT1 | _A # The argument to `vars()` has to have a `__dict__` attribute, so the second overload can't be annotated with `object` # (A "SupportsDunderDict" protocol doesn't work) -# Use a type: ignore to make complaints about overlapping overloads go away @overload -def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... # type: ignore[overload-overlap] +def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... diff --git a/mypy/typeshed/stdlib/cmd.pyi b/mypy/typeshed/stdlib/cmd.pyi index 0733857433be..6e84133572bf 100644 --- a/mypy/typeshed/stdlib/cmd.pyi +++ b/mypy/typeshed/stdlib/cmd.pyi @@ -1,10 +1,11 @@ from collections.abc import Callable from typing import IO, Any, Final +from typing_extensions import LiteralString __all__ = ["Cmd"] PROMPT: Final = "(Cmd) " -IDENTCHARS: str # Too big to be `Literal` +IDENTCHARS: Final[LiteralString] # Too big to be `Literal` class Cmd: prompt: str diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 71e3c564dd57..b2ed53e4427e 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -345,15 +345,15 @@ class _OrderedDictValuesView(ValuesView[_VT_co], Reversible[_VT_co]): # but they are not exposed anywhere) # pyright doesn't have a specific error code for subclassing error! @final -class _odict_keys(dict_keys[_KT_co, _VT_co], Reversible[_KT_co]): # type: ignore[misc] # pyright: ignore +class _odict_keys(dict_keys[_KT_co, _VT_co], Reversible[_KT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_KT_co]: ... @final -class _odict_items(dict_items[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): # type: ignore[misc] # pyright: ignore +class _odict_items(dict_items[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... @final -class _odict_values(dict_values[_KT_co, _VT_co], Reversible[_VT_co], Generic[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore +class _odict_values(dict_values[_KT_co, _VT_co], Reversible[_VT_co], Generic[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_VT_co]: ... class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): @@ -475,7 +475,8 @@ class ChainMap(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _T) -> _VT | _T: ... def copy(self) -> Self: ... __copy__ = copy - # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, so the signature should be kept in line with `dict.fromkeys`. + # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, + # so the signature should be kept in line with `dict.fromkeys`. @classmethod @overload def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index 29ac7cde561a..daf218d5a138 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -55,6 +55,7 @@ class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]): ) -> _ExitT_co: ... class ContextDecorator: + def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... class _GeneratorContextManager(AbstractContextManager[_T_co, bool | None], ContextDecorator): @@ -80,6 +81,7 @@ if sys.version_info >= (3, 10): _AF = TypeVar("_AF", bound=Callable[..., Awaitable[Any]]) class AsyncContextDecorator: + def _recreate_cm(self) -> Self: ... def __call__(self, func: _AF) -> _AF: ... class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator): diff --git a/mypy/typeshed/stdlib/crypt.pyi b/mypy/typeshed/stdlib/crypt.pyi index 1ad0a384eae7..294003859286 100644 --- a/mypy/typeshed/stdlib/crypt.pyi +++ b/mypy/typeshed/stdlib/crypt.pyi @@ -1,12 +1,13 @@ import sys +from typing import Final if sys.platform != "win32": class _Method: ... - METHOD_CRYPT: _Method - METHOD_MD5: _Method - METHOD_SHA256: _Method - METHOD_SHA512: _Method - METHOD_BLOWFISH: _Method + METHOD_CRYPT: Final[_Method] + METHOD_MD5: Final[_Method] + METHOD_SHA256: Final[_Method] + METHOD_SHA512: Final[_Method] + METHOD_BLOWFISH: Final[_Method] methods: list[_Method] def mksalt(method: _Method | None = None, *, rounds: int | None = None) -> str: ... def crypt(word: str, salt: str | _Method | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index dfd61c8f8ffc..40a073d107c7 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -185,3 +185,8 @@ if sys.version_info >= (3, 12): c_time_t: type[c_int32 | c_int64] # alias for one or the other at runtime class py_object(_CanCastTo, _SimpleCData[_T]): ... + +if sys.version_info >= (3, 14): + class c_float_complex(_SimpleCData[complex]): ... + class c_double_complex(_SimpleCData[complex]): ... + class c_longdouble_complex(_SimpleCData[complex]): ... diff --git a/mypy/typeshed/stdlib/ctypes/_endian.pyi b/mypy/typeshed/stdlib/ctypes/_endian.pyi index add6365e615f..144f5ba5dd40 100644 --- a/mypy/typeshed/stdlib/ctypes/_endian.pyi +++ b/mypy/typeshed/stdlib/ctypes/_endian.pyi @@ -1,12 +1,5 @@ import sys -from _ctypes import RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL, Structure, Union -from ctypes import DEFAULT_MODE as DEFAULT_MODE, cdll as cdll, pydll as pydll, pythonapi as pythonapi - -if sys.version_info >= (3, 12): - from _ctypes import SIZEOF_TIME_T as SIZEOF_TIME_T - -if sys.platform == "win32": - from ctypes import oledll as oledll, windll as windll +from ctypes import Structure, Union # At runtime, the native endianness is an alias for Structure, # while the other is a subclass with a metaclass added in. diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 626608e8a59d..3295b1c1f835 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -5,7 +5,7 @@ from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping from typing import Any, Generic, Literal, Protocol, TypeVar, overload -from typing_extensions import TypeAlias, TypeIs +from typing_extensions import Never, TypeAlias, TypeIs if sys.version_info >= (3, 9): from types import GenericAlias @@ -213,6 +213,10 @@ else: ) -> Any: ... def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ... + +# HACK: `obj: Never` typing matches if object argument is using `Any` type. +@overload +def is_dataclass(obj: Never) -> TypeIs[DataclassInstance | type[DataclassInstance]]: ... # type: ignore[narrowed-type-not-subtype] # pyright: ignore[reportGeneralTypeIssues] @overload def is_dataclass(obj: type) -> TypeIs[type[DataclassInstance]]: ... @overload @@ -225,18 +229,17 @@ if sys.version_info >= (3, 9): else: class _InitVarMeta(type): # Not used, instead `InitVar.__class_getitem__` is called. - # pyright ignore is needed because pyright (not unreasonably) thinks this - # is an invalid use of InitVar. - def __getitem__(self, params: Any) -> InitVar[Any]: ... # pyright: ignore + # pyright (not unreasonably) thinks this is an invalid use of InitVar. + def __getitem__(self, params: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] class InitVar(Generic[_T], metaclass=_InitVarMeta): type: Type[_T] def __init__(self, type: Type[_T]) -> None: ... if sys.version_info >= (3, 9): @overload - def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore + def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore[reportInvalidTypeForm] @overload - def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore + def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] if sys.version_info >= (3, 12): def make_dataclass( diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 38d5ac4c0819..e8a4efdc61f3 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -265,12 +265,12 @@ class datetime(date): def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ... @classmethod - @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.UTC)") + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.timezone.utc)") def utcfromtimestamp(cls, t: float, /) -> Self: ... @classmethod def now(cls, tz: _TzInfo | None = None) -> Self: ... @classmethod - @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.UTC)") + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.timezone.utc)") def utcnow(cls) -> Self: ... @classmethod def combine(cls, date: _Date, time: _Time, tzinfo: _TzInfo | None = ...) -> Self: ... diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index ca4fb3265324..1f3f31c9c48a 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -1,6 +1,26 @@ from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused from abc import abstractmethod from collections.abc import Callable, Iterable +from distutils.command.bdist import bdist +from distutils.command.bdist_dumb import bdist_dumb +from distutils.command.bdist_rpm import bdist_rpm +from distutils.command.build import build +from distutils.command.build_clib import build_clib +from distutils.command.build_ext import build_ext +from distutils.command.build_py import build_py +from distutils.command.build_scripts import build_scripts +from distutils.command.check import check +from distutils.command.clean import clean +from distutils.command.config import config +from distutils.command.install import install +from distutils.command.install_data import install_data +from distutils.command.install_egg_info import install_egg_info +from distutils.command.install_headers import install_headers +from distutils.command.install_lib import install_lib +from distutils.command.install_scripts import install_scripts +from distutils.command.register import register +from distutils.command.sdist import sdist +from distutils.command.upload import upload from distutils.dist import Distribution from distutils.file_util import _BytesPathT, _StrPathT from typing import Any, ClassVar, Literal, TypeVar, overload @@ -28,8 +48,108 @@ class Command: def ensure_dirname(self, option: str) -> None: ... def get_command_name(self) -> str: ... def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ... + # NOTE: This list comes directly from the distutils/command folder. Minus bdist_msi and bdist_wininst. + @overload + def get_finalized_command(self, command: Literal["bdist"], create: bool | Literal[0, 1] = 1) -> bdist: ... + @overload + def get_finalized_command(self, command: Literal["bdist_dumb"], create: bool | Literal[0, 1] = 1) -> bdist_dumb: ... + @overload + def get_finalized_command(self, command: Literal["bdist_rpm"], create: bool | Literal[0, 1] = 1) -> bdist_rpm: ... + @overload + def get_finalized_command(self, command: Literal["build"], create: bool | Literal[0, 1] = 1) -> build: ... + @overload + def get_finalized_command(self, command: Literal["build_clib"], create: bool | Literal[0, 1] = 1) -> build_clib: ... + @overload + def get_finalized_command(self, command: Literal["build_ext"], create: bool | Literal[0, 1] = 1) -> build_ext: ... + @overload + def get_finalized_command(self, command: Literal["build_py"], create: bool | Literal[0, 1] = 1) -> build_py: ... + @overload + def get_finalized_command(self, command: Literal["build_scripts"], create: bool | Literal[0, 1] = 1) -> build_scripts: ... + @overload + def get_finalized_command(self, command: Literal["check"], create: bool | Literal[0, 1] = 1) -> check: ... + @overload + def get_finalized_command(self, command: Literal["clean"], create: bool | Literal[0, 1] = 1) -> clean: ... + @overload + def get_finalized_command(self, command: Literal["config"], create: bool | Literal[0, 1] = 1) -> config: ... + @overload + def get_finalized_command(self, command: Literal["install"], create: bool | Literal[0, 1] = 1) -> install: ... + @overload + def get_finalized_command(self, command: Literal["install_data"], create: bool | Literal[0, 1] = 1) -> install_data: ... + @overload + def get_finalized_command( + self, command: Literal["install_egg_info"], create: bool | Literal[0, 1] = 1 + ) -> install_egg_info: ... + @overload + def get_finalized_command(self, command: Literal["install_headers"], create: bool | Literal[0, 1] = 1) -> install_headers: ... + @overload + def get_finalized_command(self, command: Literal["install_lib"], create: bool | Literal[0, 1] = 1) -> install_lib: ... + @overload + def get_finalized_command(self, command: Literal["install_scripts"], create: bool | Literal[0, 1] = 1) -> install_scripts: ... + @overload + def get_finalized_command(self, command: Literal["register"], create: bool | Literal[0, 1] = 1) -> register: ... + @overload + def get_finalized_command(self, command: Literal["sdist"], create: bool | Literal[0, 1] = 1) -> sdist: ... + @overload + def get_finalized_command(self, command: Literal["upload"], create: bool | Literal[0, 1] = 1) -> upload: ... + @overload def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ... @overload + def reinitialize_command(self, command: Literal["bdist"], reinit_subcommands: bool | Literal[0, 1] = 0) -> bdist: ... + @overload + def reinitialize_command( + self, command: Literal["bdist_dumb"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> bdist_dumb: ... + @overload + def reinitialize_command(self, command: Literal["bdist_rpm"], reinit_subcommands: bool | Literal[0, 1] = 0) -> bdist_rpm: ... + @overload + def reinitialize_command(self, command: Literal["build"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build: ... + @overload + def reinitialize_command( + self, command: Literal["build_clib"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> build_clib: ... + @overload + def reinitialize_command(self, command: Literal["build_ext"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build_ext: ... + @overload + def reinitialize_command(self, command: Literal["build_py"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build_py: ... + @overload + def reinitialize_command( + self, command: Literal["build_scripts"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> build_scripts: ... + @overload + def reinitialize_command(self, command: Literal["check"], reinit_subcommands: bool | Literal[0, 1] = 0) -> check: ... + @overload + def reinitialize_command(self, command: Literal["clean"], reinit_subcommands: bool | Literal[0, 1] = 0) -> clean: ... + @overload + def reinitialize_command(self, command: Literal["config"], reinit_subcommands: bool | Literal[0, 1] = 0) -> config: ... + @overload + def reinitialize_command(self, command: Literal["install"], reinit_subcommands: bool | Literal[0, 1] = 0) -> install: ... + @overload + def reinitialize_command( + self, command: Literal["install_data"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_data: ... + @overload + def reinitialize_command( + self, command: Literal["install_egg_info"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_egg_info: ... + @overload + def reinitialize_command( + self, command: Literal["install_headers"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_headers: ... + @overload + def reinitialize_command( + self, command: Literal["install_lib"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_lib: ... + @overload + def reinitialize_command( + self, command: Literal["install_scripts"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_scripts: ... + @overload + def reinitialize_command(self, command: Literal["register"], reinit_subcommands: bool | Literal[0, 1] = 0) -> register: ... + @overload + def reinitialize_command(self, command: Literal["sdist"], reinit_subcommands: bool | Literal[0, 1] = 0) -> sdist: ... + @overload + def reinitialize_command(self, command: Literal["upload"], reinit_subcommands: bool | Literal[0, 1] = 0) -> upload: ... + @overload def reinitialize_command(self, command: str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... @overload def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool | Literal[0, 1] = 0) -> _CommandT: ... diff --git a/mypy/typeshed/stdlib/distutils/command/__init__.pyi b/mypy/typeshed/stdlib/distutils/command/__init__.pyi index e69de29bb2d1..4d7372858af3 100644 --- a/mypy/typeshed/stdlib/distutils/command/__init__.pyi +++ b/mypy/typeshed/stdlib/distutils/command/__init__.pyi @@ -0,0 +1,48 @@ +import sys + +from . import ( + bdist, + bdist_dumb, + bdist_rpm, + build, + build_clib, + build_ext, + build_py, + build_scripts, + check, + clean, + install, + install_data, + install_headers, + install_lib, + install_scripts, + register, + sdist, + upload, +) + +__all__ = [ + "build", + "build_py", + "build_ext", + "build_clib", + "build_scripts", + "clean", + "install", + "install_lib", + "install_headers", + "install_scripts", + "install_data", + "sdist", + "register", + "bdist", + "bdist_dumb", + "bdist_rpm", + "check", + "upload", +] + +if sys.version_info < (3, 10): + from . import bdist_wininst + + __all__ += ["bdist_wininst"] diff --git a/mypy/typeshed/stdlib/distutils/command/check.pyi b/mypy/typeshed/stdlib/distutils/command/check.pyi index c67e4cbfdfe0..e69627d20c7a 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,4 +1,4 @@ -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias from ..cmd import Command @@ -22,7 +22,7 @@ class SilentReporter(_Reporter): ) -> None: ... def system_message(self, level, message, *children, **kwargs): ... -HAS_DOCUTILS: bool +HAS_DOCUTILS: Final[bool] class check(Command): description: str diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi index 2f528c2c290b..b0910091d5b6 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,12 +1,12 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Final, Literal from ..ccompiler import CCompiler from ..cmd import Command -LANG_EXT: dict[str, str] +LANG_EXT: Final[dict[str, str]] class config(Command): description: str diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index b0a5a82fc3f6..24a4eff2fb10 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -1,11 +1,16 @@ +import sys from collections.abc import Callable -from typing import Any, ClassVar +from typing import Any, ClassVar, Final, Literal from ..cmd import Command -HAS_USER_SITE: bool -SCHEME_KEYS: tuple[str, ...] -INSTALL_SCHEMES: dict[str, dict[Any, Any]] +HAS_USER_SITE: Final[bool] + +SCHEME_KEYS: Final[tuple[Literal["purelib"], Literal["platlib"], Literal["headers"], Literal["scripts"], Literal["data"]]] +INSTALL_SCHEMES: Final[dict[str, dict[str, str]]] + +if sys.version_info < (3, 10): + WINDOWS_SCHEME: Final[dict[str, str]] class install(Command): description: str diff --git a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi index 718d082b7b07..149ecae89781 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi @@ -1,8 +1,8 @@ -from typing import Any, ClassVar +from typing import Any, ClassVar, Final from ..cmd import Command -PYTHON_SOURCE_EXTENSION: str +PYTHON_SOURCE_EXTENSION: Final = ".py" class install_lib(Command): description: str diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index f3c434df0b1a..a4d21f8ddd7b 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -3,9 +3,9 @@ from collections.abc import Mapping from distutils.cmd import Command as Command from distutils.dist import Distribution as Distribution from distutils.extension import Extension as Extension -from typing import Any, Literal +from typing import Any, Final, Literal -USAGE: str +USAGE: Final[str] def gen_usage(script_name: StrOrBytesPath) -> str: ... diff --git a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi index 5f2e623eeff6..80924d63e471 100644 --- a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi @@ -1,20 +1,20 @@ from distutils.unixccompiler import UnixCCompiler from distutils.version import LooseVersion from re import Pattern -from typing import Literal +from typing import Final, Literal def get_msvcr() -> list[str] | None: ... class CygwinCCompiler(UnixCCompiler): ... class Mingw32CCompiler(CygwinCCompiler): ... -CONFIG_H_OK: str -CONFIG_H_NOTOK: str -CONFIG_H_UNCERTAIN: str +CONFIG_H_OK: Final = "ok" +CONFIG_H_NOTOK: Final = "not ok" +CONFIG_H_UNCERTAIN: Final = "uncertain" def check_config_h() -> tuple[Literal["ok", "not ok", "uncertain"], str]: ... -RE_VERSION: Pattern[bytes] +RE_VERSION: Final[Pattern[bytes]] def get_versions() -> tuple[LooseVersion | None, ...]: ... def is_cygwingcc() -> bool: ... diff --git a/mypy/typeshed/stdlib/distutils/debug.pyi b/mypy/typeshed/stdlib/distutils/debug.pyi index 11f28a8bc8ae..30095883b064 100644 --- a/mypy/typeshed/stdlib/distutils/debug.pyi +++ b/mypy/typeshed/stdlib/distutils/debug.pyi @@ -1 +1,3 @@ -DEBUG: bool | None +from typing import Final + +DEBUG: Final[str | None] diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 21ddbc425918..e32fd70f7baa 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -1,6 +1,26 @@ from _typeshed import Incomplete, StrOrBytesPath, StrPath, SupportsWrite from collections.abc import Iterable, MutableMapping from distutils.cmd import Command +from distutils.command.bdist import bdist +from distutils.command.bdist_dumb import bdist_dumb +from distutils.command.bdist_rpm import bdist_rpm +from distutils.command.build import build +from distutils.command.build_clib import build_clib +from distutils.command.build_ext import build_ext +from distutils.command.build_py import build_py +from distutils.command.build_scripts import build_scripts +from distutils.command.check import check +from distutils.command.clean import clean +from distutils.command.config import config +from distutils.command.install import install +from distutils.command.install_data import install_data +from distutils.command.install_egg_info import install_egg_info +from distutils.command.install_headers import install_headers +from distutils.command.install_lib import install_lib +from distutils.command.install_scripts import install_scripts +from distutils.command.register import register +from distutils.command.sdist import sdist +from distutils.command.upload import upload from re import Pattern from typing import IO, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias @@ -63,10 +83,6 @@ class Distribution: def __init__(self, attrs: MutableMapping[str, Incomplete] | None = None) -> None: ... def get_option_dict(self, command: str) -> dict[str, tuple[str, str]]: ... def parse_config_files(self, filenames: Iterable[str] | None = None) -> None: ... - @overload - def get_command_obj(self, command: str, create: Literal[1, True] = 1) -> Command: ... - @overload - def get_command_obj(self, command: str, create: Literal[0, False]) -> Command | None: ... global_options: ClassVar[_OptionsList] common_usage: ClassVar[str] display_options: ClassVar[_OptionsList] @@ -108,8 +124,137 @@ class Distribution: def print_commands(self) -> None: ... def get_command_list(self): ... def get_command_packages(self): ... + # NOTE: This list comes directly from the distutils/command folder. Minus bdist_msi and bdist_wininst. + @overload + def get_command_obj(self, command: Literal["bdist"], create: Literal[1, True] = 1) -> bdist: ... + @overload + def get_command_obj(self, command: Literal["bdist_dumb"], create: Literal[1, True] = 1) -> bdist_dumb: ... + @overload + def get_command_obj(self, command: Literal["bdist_rpm"], create: Literal[1, True] = 1) -> bdist_rpm: ... + @overload + def get_command_obj(self, command: Literal["build"], create: Literal[1, True] = 1) -> build: ... + @overload + def get_command_obj(self, command: Literal["build_clib"], create: Literal[1, True] = 1) -> build_clib: ... + @overload + def get_command_obj(self, command: Literal["build_ext"], create: Literal[1, True] = 1) -> build_ext: ... + @overload + def get_command_obj(self, command: Literal["build_py"], create: Literal[1, True] = 1) -> build_py: ... + @overload + def get_command_obj(self, command: Literal["build_scripts"], create: Literal[1, True] = 1) -> build_scripts: ... + @overload + def get_command_obj(self, command: Literal["check"], create: Literal[1, True] = 1) -> check: ... + @overload + def get_command_obj(self, command: Literal["clean"], create: Literal[1, True] = 1) -> clean: ... + @overload + def get_command_obj(self, command: Literal["config"], create: Literal[1, True] = 1) -> config: ... + @overload + def get_command_obj(self, command: Literal["install"], create: Literal[1, True] = 1) -> install: ... + @overload + def get_command_obj(self, command: Literal["install_data"], create: Literal[1, True] = 1) -> install_data: ... + @overload + def get_command_obj(self, command: Literal["install_egg_info"], create: Literal[1, True] = 1) -> install_egg_info: ... + @overload + def get_command_obj(self, command: Literal["install_headers"], create: Literal[1, True] = 1) -> install_headers: ... + @overload + def get_command_obj(self, command: Literal["install_lib"], create: Literal[1, True] = 1) -> install_lib: ... + @overload + def get_command_obj(self, command: Literal["install_scripts"], create: Literal[1, True] = 1) -> install_scripts: ... + @overload + def get_command_obj(self, command: Literal["register"], create: Literal[1, True] = 1) -> register: ... + @overload + def get_command_obj(self, command: Literal["sdist"], create: Literal[1, True] = 1) -> sdist: ... + @overload + def get_command_obj(self, command: Literal["upload"], create: Literal[1, True] = 1) -> upload: ... + @overload + def get_command_obj(self, command: str, create: Literal[1, True] = 1) -> Command: ... + # Not replicating the overloads for "Command | None", user may use "isinstance" + @overload + def get_command_obj(self, command: str, create: Literal[0, False]) -> Command | None: ... + @overload + def get_command_class(self, command: Literal["bdist"]) -> type[bdist]: ... + @overload + def get_command_class(self, command: Literal["bdist_dumb"]) -> type[bdist_dumb]: ... + @overload + def get_command_class(self, command: Literal["bdist_rpm"]) -> type[bdist_rpm]: ... + @overload + def get_command_class(self, command: Literal["build"]) -> type[build]: ... + @overload + def get_command_class(self, command: Literal["build_clib"]) -> type[build_clib]: ... + @overload + def get_command_class(self, command: Literal["build_ext"]) -> type[build_ext]: ... + @overload + def get_command_class(self, command: Literal["build_py"]) -> type[build_py]: ... + @overload + def get_command_class(self, command: Literal["build_scripts"]) -> type[build_scripts]: ... + @overload + def get_command_class(self, command: Literal["check"]) -> type[check]: ... + @overload + def get_command_class(self, command: Literal["clean"]) -> type[clean]: ... + @overload + def get_command_class(self, command: Literal["config"]) -> type[config]: ... + @overload + def get_command_class(self, command: Literal["install"]) -> type[install]: ... + @overload + def get_command_class(self, command: Literal["install_data"]) -> type[install_data]: ... + @overload + def get_command_class(self, command: Literal["install_egg_info"]) -> type[install_egg_info]: ... + @overload + def get_command_class(self, command: Literal["install_headers"]) -> type[install_headers]: ... + @overload + def get_command_class(self, command: Literal["install_lib"]) -> type[install_lib]: ... + @overload + def get_command_class(self, command: Literal["install_scripts"]) -> type[install_scripts]: ... + @overload + def get_command_class(self, command: Literal["register"]) -> type[register]: ... + @overload + def get_command_class(self, command: Literal["sdist"]) -> type[sdist]: ... + @overload + def get_command_class(self, command: Literal["upload"]) -> type[upload]: ... + @overload def get_command_class(self, command: str) -> type[Command]: ... @overload + def reinitialize_command(self, command: Literal["bdist"], reinit_subcommands: bool = False) -> bdist: ... + @overload + def reinitialize_command(self, command: Literal["bdist_dumb"], reinit_subcommands: bool = False) -> bdist_dumb: ... + @overload + def reinitialize_command(self, command: Literal["bdist_rpm"], reinit_subcommands: bool = False) -> bdist_rpm: ... + @overload + def reinitialize_command(self, command: Literal["build"], reinit_subcommands: bool = False) -> build: ... + @overload + def reinitialize_command(self, command: Literal["build_clib"], reinit_subcommands: bool = False) -> build_clib: ... + @overload + def reinitialize_command(self, command: Literal["build_ext"], reinit_subcommands: bool = False) -> build_ext: ... + @overload + def reinitialize_command(self, command: Literal["build_py"], reinit_subcommands: bool = False) -> build_py: ... + @overload + def reinitialize_command(self, command: Literal["build_scripts"], reinit_subcommands: bool = False) -> build_scripts: ... + @overload + def reinitialize_command(self, command: Literal["check"], reinit_subcommands: bool = False) -> check: ... + @overload + def reinitialize_command(self, command: Literal["clean"], reinit_subcommands: bool = False) -> clean: ... + @overload + def reinitialize_command(self, command: Literal["config"], reinit_subcommands: bool = False) -> config: ... + @overload + def reinitialize_command(self, command: Literal["install"], reinit_subcommands: bool = False) -> install: ... + @overload + def reinitialize_command(self, command: Literal["install_data"], reinit_subcommands: bool = False) -> install_data: ... + @overload + def reinitialize_command( + self, command: Literal["install_egg_info"], reinit_subcommands: bool = False + ) -> install_egg_info: ... + @overload + def reinitialize_command(self, command: Literal["install_headers"], reinit_subcommands: bool = False) -> install_headers: ... + @overload + def reinitialize_command(self, command: Literal["install_lib"], reinit_subcommands: bool = False) -> install_lib: ... + @overload + def reinitialize_command(self, command: Literal["install_scripts"], reinit_subcommands: bool = False) -> install_scripts: ... + @overload + def reinitialize_command(self, command: Literal["register"], reinit_subcommands: bool = False) -> register: ... + @overload + def reinitialize_command(self, command: Literal["sdist"], reinit_subcommands: bool = False) -> sdist: ... + @overload + def reinitialize_command(self, command: Literal["upload"], reinit_subcommands: bool = False) -> upload: ... + @overload def reinitialize_command(self, command: str, reinit_subcommands: bool = False) -> Command: ... @overload def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool = False) -> _CommandT: ... diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi index f9916d4511b2..c4d37419ed06 100644 --- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi +++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi @@ -1,15 +1,15 @@ from collections.abc import Iterable, Mapping from re import Pattern -from typing import Any, overload +from typing import Any, Final, overload from typing_extensions import TypeAlias _Option: TypeAlias = tuple[str, str | None, str] _GR: TypeAlias = tuple[list[str], OptionDummy] -longopt_pat: str -longopt_re: Pattern[str] -neg_alias_re: Pattern[str] -longopt_xlate: dict[int, int] +longopt_pat: Final = r"[a-zA-Z](?:[a-zA-Z0-9-]*)" +longopt_re: Final[Pattern[str]] +neg_alias_re: Final[Pattern[str]] +longopt_xlate: Final[dict[int, int]] class FancyGetopt: def __init__(self, option_table: list[_Option] | None = None) -> None: ... @@ -25,7 +25,7 @@ def fancy_getopt( options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: list[str] | None ) -> list[str] | _GR: ... -WS_TRANS: dict[int, str] +WS_TRANS: Final[dict[int, str]] def wrap_text(text: str, width: int) -> list[str]: ... def translate_longopt(opt: str) -> str: ... diff --git a/mypy/typeshed/stdlib/distutils/log.pyi b/mypy/typeshed/stdlib/distutils/log.pyi index 14ed8d8aefa8..0ea135c28371 100644 --- a/mypy/typeshed/stdlib/distutils/log.pyi +++ b/mypy/typeshed/stdlib/distutils/log.pyi @@ -1,10 +1,10 @@ -from typing import Any +from typing import Any, Final -DEBUG: int -INFO: int -WARN: int -ERROR: int -FATAL: int +DEBUG: Final = 1 +INFO: Final = 2 +WARN: Final = 3 +ERROR: Final = 4 +FATAL: Final = 5 class Log: def __init__(self, threshold: int = 3) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/sysconfig.pyi b/mypy/typeshed/stdlib/distutils/sysconfig.pyi index da72e3275fe3..4a9c45eb562a 100644 --- a/mypy/typeshed/stdlib/distutils/sysconfig.pyi +++ b/mypy/typeshed/stdlib/distutils/sysconfig.pyi @@ -1,15 +1,15 @@ import sys from collections.abc import Mapping from distutils.ccompiler import CCompiler -from typing import Literal, overload +from typing import Final, Literal, overload from typing_extensions import deprecated -PREFIX: str -EXEC_PREFIX: str -BASE_PREFIX: str -BASE_EXEC_PREFIX: str -project_base: str -python_build: bool +PREFIX: Final[str] +EXEC_PREFIX: Final[str] +BASE_PREFIX: Final[str] +BASE_EXEC_PREFIX: Final[str] +project_base: Final[str] +python_build: Final[bool] def expand_makefile_vars(s: str, vars: Mapping[str, str]) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 4032bc6136d4..7e80f13adb8f 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -50,7 +50,8 @@ class Message(Generic[_HeaderT, _HeaderParamT]): def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | Any: ... @overload # not multipart, IDEM but w/o kwarg def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | Any: ... - # If `charset=None` and payload supports both `encode` AND `decode`, then an invalid payload could be passed, but this is unlikely + # If `charset=None` and payload supports both `encode` AND `decode`, + # then an invalid payload could be passed, but this is unlikely # Not[_SupportsEncodeToPayload] @overload def set_payload( @@ -146,7 +147,11 @@ class Message(Generic[_HeaderT, _HeaderParamT]): class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def __init__(self, policy: Policy | None = None) -> None: ... def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ... - def iter_attachments(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... + def attach(self, payload: Self) -> None: ... # type: ignore[override] + # The attachments are created via type(self) in the attach method. It's theoretically + # possible to sneak other attachment types into a MIMEPart instance, but could cause + # cause unforseen consequences. + def iter_attachments(self) -> Iterator[Self]: ... def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ... def set_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 2724dbf6ec2f..9dab22c18f6c 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -66,7 +66,10 @@ def mktime_tz(data: _PDTZ) -> int: ... def formatdate(timeval: float | None = None, localtime: bool = False, usegmt: bool = False) -> str: ... def format_datetime(dt: datetime.datetime, usegmt: bool = False) -> str: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 14): + def localtime(dt: datetime.datetime | None = None) -> datetime.datetime: ... + +elif sys.version_info >= (3, 12): @overload def localtime(dt: datetime.datetime | None = None) -> datetime.datetime: ... @overload diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index dfec2da72344..cb7b94596077 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -17,13 +17,24 @@ def cmpfiles( ) -> tuple[list[AnyStr], list[AnyStr], list[AnyStr]]: ... class dircmp(Generic[AnyStr]): - def __init__( - self, - a: GenericPath[AnyStr], - b: GenericPath[AnyStr], - ignore: Sequence[AnyStr] | None = None, - hide: Sequence[AnyStr] | None = None, - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + a: GenericPath[AnyStr], + b: GenericPath[AnyStr], + ignore: Sequence[AnyStr] | None = None, + hide: Sequence[AnyStr] | None = None, + *, + shallow: bool = True, + ) -> None: ... + else: + def __init__( + self, + a: GenericPath[AnyStr], + b: GenericPath[AnyStr], + ignore: Sequence[AnyStr] | None = None, + hide: Sequence[AnyStr] | None = None, + ) -> None: ... left: AnyStr right: AnyStr hide: Sequence[AnyStr] diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 1b96e0d504b7..3693d7c52a26 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -86,7 +86,7 @@ class FTP: def makeport(self) -> socket: ... def makepasv(self) -> tuple[str, int]: ... def login(self, user: str = "", passwd: str = "", acct: str = "") -> str: ... - # In practice, `rest` rest can actually be anything whose str() is an integer sequence, so to make it simple we allow integers. + # In practice, `rest` can actually be anything whose str() is an integer sequence, so to make it simple we allow integers def ntransfercmd(self, cmd: str, rest: int | str | None = None) -> tuple[socket, int | None]: ... def transfercmd(self, cmd: str, rest: int | str | None = None) -> socket: ... def retrbinary( diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 37b9a3882179..5e26f8987277 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -155,7 +155,7 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12): @property def names(self) -> set[str]: ... @overload - def select(self) -> Self: ... # type: ignore[misc] + def select(self) -> Self: ... @overload def select( self, @@ -277,7 +277,7 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 10): @overload - def entry_points() -> SelectableGroups: ... # type: ignore[overload-overlap] + def entry_points() -> SelectableGroups: ... @overload def entry_points( *, name: str = ..., value: str = ..., group: str = ..., module: str = ..., attr: str = ..., extras: list[str] = ... diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index 473398a60b2a..aa4a3bdf61d4 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -1,12 +1,12 @@ from collections.abc import Callable, Iterator from re import Pattern -from typing import Any +from typing import Any, Final -ESCAPE: Pattern[str] -ESCAPE_ASCII: Pattern[str] -HAS_UTF8: Pattern[bytes] -ESCAPE_DCT: dict[str, str] -INFINITY: float +ESCAPE: Final[Pattern[str]] +ESCAPE_ASCII: Final[Pattern[str]] +HAS_UTF8: Final[Pattern[bytes]] +ESCAPE_DCT: Final[dict[str, str]] +INFINITY: Final[float] def py_encode_basestring(s: str) -> str: ... # undocumented def py_encode_basestring_ascii(s: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi index 80d9d8b6e656..85d1315213b9 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi @@ -6,7 +6,7 @@ from ..pytree import Node class FixUnicode(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] - PATTERN: ClassVar[Literal["STRING | 'unicode' | 'unichr'"]] # type: ignore[name-defined] # Name "STRING" is not defined + PATTERN: ClassVar[str] unicode_literals: bool def start_tree(self, tree: Node, filename: StrPath) -> None: ... def transform(self, node, results): ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index e6e6e8f645a0..9a4827a8f626 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -55,10 +55,9 @@ __all__ = [ "setLogRecordFactory", "lastResort", "raiseExceptions", + "warn", ] -if sys.version_info < (3, 13): - __all__ += ["warn"] if sys.version_info >= (3, 11): __all__ += ["getLevelNamesMapping"] if sys.version_info >= (3, 12): @@ -157,17 +156,16 @@ class Logger(Filterer): stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - if sys.version_info < (3, 13): - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - + @deprecated("Deprecated; use warning() instead.") + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... def error( self, msg: object, @@ -412,18 +410,17 @@ class LoggerAdapter(Generic[_L]): extra: Mapping[str, object] | None = None, **kwargs: object, ) -> None: ... - if sys.version_info < (3, 13): - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - + @deprecated("Deprecated; use warning() instead.") + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + **kwargs: object, + ) -> None: ... def error( self, msg: object, @@ -523,17 +520,15 @@ def warning( stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - -if sys.version_info < (3, 13): - def warn( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - +@deprecated("Deprecated; use warning() instead.") +def warn( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... def error( msg: object, *args: object, diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 2b96ff047470..2b0498abc2c6 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -73,7 +73,7 @@ def copy(obj: _CT) -> _CT: ... @overload def synchronized(obj: _SimpleCData[_T], lock: _LockLike | None = None, ctx: Any | None = None) -> Synchronized[_T]: ... @overload -def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... # type: ignore +def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... @overload def synchronized( obj: ctypes.Array[_SimpleCData[_T]], lock: _LockLike | None = None, ctx: Any | None = None @@ -115,12 +115,12 @@ class SynchronizedArray(SynchronizedBase[ctypes.Array[_SimpleCData[_T]]], Generi class SynchronizedString(SynchronizedArray[bytes]): @overload # type: ignore[override] def __getitem__(self, i: slice) -> bytes: ... - @overload # type: ignore[override] + @overload def __getitem__(self, i: int) -> bytes: ... @overload # type: ignore[override] def __setitem__(self, i: slice, value: bytes) -> None: ... - @overload # type: ignore[override] - def __setitem__(self, i: int, value: bytes) -> None: ... # type: ignore[override] + @overload + def __setitem__(self, i: int, value: bytes) -> None: ... def __getslice__(self, start: int, stop: int) -> bytes: ... # type: ignore[override] def __setslice__(self, start: int, stop: int, values: bytes) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/operator.pyi b/mypy/typeshed/stdlib/operator.pyi index a0e5df7977da..1a817f00f3c1 100644 --- a/mypy/typeshed/stdlib/operator.pyi +++ b/mypy/typeshed/stdlib/operator.pyi @@ -61,6 +61,9 @@ __all__ = [ if sys.version_info >= (3, 11): __all__ += ["call"] +if sys.version_info >= (3, 14): + __all__ += ["is_none", "is_not_none"] + __lt__ = lt __le__ = le __eq__ = eq diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index e2d272cb4112..700e0e9df310 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -365,7 +365,9 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo if sys.version_info >= (3, 12) and sys.platform == "win32": @property @deprecated( - "Use st_birthtime instead to retrieve the file creation time. In the future, this property will contain the last metadata change time." + """\ +Use st_birthtime instead to retrieve the file creation time. \ +In the future, this property will contain the last metadata change time.""" ) def st_ctime(self) -> float: ... else: diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 116bf6431831..bdca375f626d 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -159,6 +159,20 @@ class Path(PurePath): def lchmod(self, mode: int) -> None: ... def lstat(self) -> stat_result: ... def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ... + + if sys.version_info >= (3, 14): + def copy(self, target: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> None: ... + def copytree( + self, + target: StrPath, + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + dirs_exist_ok: bool = False, + ignore: Callable[[Self], bool] | None = None, + on_error: Callable[[OSError], object] | None = None, + ) -> None: ... + # Adapted from builtins.open # Text mode: always returns a TextIOWrapper # The Traversable .open in stdlib/importlib/abc.pyi should be kept in sync with this. @@ -232,10 +246,18 @@ class Path(PurePath): if sys.version_info >= (3, 9): def readlink(self) -> Self: ... - def rename(self, target: str | PurePath) -> Self: ... - def replace(self, target: str | PurePath) -> Self: ... + if sys.version_info >= (3, 10): + def rename(self, target: StrPath) -> Self: ... + def replace(self, target: StrPath) -> Self: ... + else: + def rename(self, target: str | PurePath) -> Self: ... + def replace(self, target: str | PurePath) -> Self: ... + def resolve(self, strict: bool = False) -> Self: ... def rmdir(self) -> None: ... + if sys.version_info >= (3, 14): + def delete(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ... + def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ... if sys.version_info >= (3, 10): def hardlink_to(self, target: StrOrBytesPath) -> None: ... @@ -266,6 +288,9 @@ class Path(PurePath): self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ... ) -> Iterator[tuple[Self, list[str], list[str]]]: ... + if sys.version_info >= (3, 14): + def rmtree(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ... + class PosixPath(Path, PurePosixPath): ... class WindowsPath(Path, PureWindowsPath): ... diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index d49315427813..61e8b7176e84 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -84,7 +84,7 @@ class Pdb(Bdb, Cmd): def _runscript(self, filename: str) -> None: ... if sys.version_info >= (3, 13): - def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... # type: ignore[override] + def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... def do_commands(self, arg: str) -> bool | None: ... def do_break(self, arg: str, temporary: bool = ...) -> bool | None: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 7476f2991978..a1e41be86a7f 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -67,5 +67,6 @@ class POP3_SSL(POP3): timeout: float = ..., context: ssl.SSLContext | None = None, ) -> None: ... - # "context" is actually the last argument, but that breaks LSP and it doesn't really matter because all the arguments are ignored + # "context" is actually the last argument, + # but that breaks LSP and it doesn't really matter because all the arguments are ignored def stls(self, context: Any = None, keyfile: Any = None, certfile: Any = None) -> NoReturn: ... diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index 4c9e42b4ec5e..941915179c4a 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable from typing import Final -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated if sys.platform != "win32": __all__ = ["openpty", "fork", "spawn"] @@ -13,7 +13,12 @@ if sys.platform != "win32": CHILD: Final = 0 def openpty() -> tuple[int, int]: ... - def master_open() -> tuple[int, str]: ... # deprecated, use openpty() - def slave_open(tty_name: str) -> int: ... # deprecated, use openpty() + + if sys.version_info < (3, 14): + @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead") + def master_open() -> tuple[int, str]: ... + @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead") + def slave_open(tty_name: str) -> int: ... + def fork() -> tuple[int, int]: ... def spawn(argv: str | Iterable[str], master_read: _Reader = ..., stdin_read: _Reader = ...) -> int: ... diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 64decd56bee6..dc0156ef13bd 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -15,9 +15,9 @@ class ExpatError(Exception): offset: int error = ExpatError -XML_PARAM_ENTITY_PARSING_NEVER: int -XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: int -XML_PARAM_ENTITY_PARSING_ALWAYS: int +XML_PARAM_ENTITY_PARSING_NEVER: Final = 0 +XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: Final = 1 +XML_PARAM_ENTITY_PARSING_ALWAYS: Final = 2 _Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] diff --git a/mypy/typeshed/stdlib/pyexpat/errors.pyi b/mypy/typeshed/stdlib/pyexpat/errors.pyi index 2e512eb12989..cae4da089161 100644 --- a/mypy/typeshed/stdlib/pyexpat/errors.pyi +++ b/mypy/typeshed/stdlib/pyexpat/errors.pyi @@ -1,49 +1,51 @@ import sys +from typing import Final +from typing_extensions import LiteralString codes: dict[str, int] messages: dict[int, str] -XML_ERROR_ABORTED: str -XML_ERROR_ASYNC_ENTITY: str -XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: str -XML_ERROR_BAD_CHAR_REF: str -XML_ERROR_BINARY_ENTITY_REF: str -XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: str -XML_ERROR_DUPLICATE_ATTRIBUTE: str -XML_ERROR_ENTITY_DECLARED_IN_PE: str -XML_ERROR_EXTERNAL_ENTITY_HANDLING: str -XML_ERROR_FEATURE_REQUIRES_XML_DTD: str -XML_ERROR_FINISHED: str -XML_ERROR_INCOMPLETE_PE: str -XML_ERROR_INCORRECT_ENCODING: str -XML_ERROR_INVALID_TOKEN: str -XML_ERROR_JUNK_AFTER_DOC_ELEMENT: str -XML_ERROR_MISPLACED_XML_PI: str -XML_ERROR_NOT_STANDALONE: str -XML_ERROR_NOT_SUSPENDED: str -XML_ERROR_NO_ELEMENTS: str -XML_ERROR_NO_MEMORY: str -XML_ERROR_PARAM_ENTITY_REF: str -XML_ERROR_PARTIAL_CHAR: str -XML_ERROR_PUBLICID: str -XML_ERROR_RECURSIVE_ENTITY_REF: str -XML_ERROR_SUSPENDED: str -XML_ERROR_SUSPEND_PE: str -XML_ERROR_SYNTAX: str -XML_ERROR_TAG_MISMATCH: str -XML_ERROR_TEXT_DECL: str -XML_ERROR_UNBOUND_PREFIX: str -XML_ERROR_UNCLOSED_CDATA_SECTION: str -XML_ERROR_UNCLOSED_TOKEN: str -XML_ERROR_UNDECLARING_PREFIX: str -XML_ERROR_UNDEFINED_ENTITY: str -XML_ERROR_UNEXPECTED_STATE: str -XML_ERROR_UNKNOWN_ENCODING: str -XML_ERROR_XML_DECL: str +XML_ERROR_ABORTED: Final[LiteralString] +XML_ERROR_ASYNC_ENTITY: Final[LiteralString] +XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: Final[LiteralString] +XML_ERROR_BAD_CHAR_REF: Final[LiteralString] +XML_ERROR_BINARY_ENTITY_REF: Final[LiteralString] +XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: Final[LiteralString] +XML_ERROR_DUPLICATE_ATTRIBUTE: Final[LiteralString] +XML_ERROR_ENTITY_DECLARED_IN_PE: Final[LiteralString] +XML_ERROR_EXTERNAL_ENTITY_HANDLING: Final[LiteralString] +XML_ERROR_FEATURE_REQUIRES_XML_DTD: Final[LiteralString] +XML_ERROR_FINISHED: Final[LiteralString] +XML_ERROR_INCOMPLETE_PE: Final[LiteralString] +XML_ERROR_INCORRECT_ENCODING: Final[LiteralString] +XML_ERROR_INVALID_TOKEN: Final[LiteralString] +XML_ERROR_JUNK_AFTER_DOC_ELEMENT: Final[LiteralString] +XML_ERROR_MISPLACED_XML_PI: Final[LiteralString] +XML_ERROR_NOT_STANDALONE: Final[LiteralString] +XML_ERROR_NOT_SUSPENDED: Final[LiteralString] +XML_ERROR_NO_ELEMENTS: Final[LiteralString] +XML_ERROR_NO_MEMORY: Final[LiteralString] +XML_ERROR_PARAM_ENTITY_REF: Final[LiteralString] +XML_ERROR_PARTIAL_CHAR: Final[LiteralString] +XML_ERROR_PUBLICID: Final[LiteralString] +XML_ERROR_RECURSIVE_ENTITY_REF: Final[LiteralString] +XML_ERROR_SUSPENDED: Final[LiteralString] +XML_ERROR_SUSPEND_PE: Final[LiteralString] +XML_ERROR_SYNTAX: Final[LiteralString] +XML_ERROR_TAG_MISMATCH: Final[LiteralString] +XML_ERROR_TEXT_DECL: Final[LiteralString] +XML_ERROR_UNBOUND_PREFIX: Final[LiteralString] +XML_ERROR_UNCLOSED_CDATA_SECTION: Final[LiteralString] +XML_ERROR_UNCLOSED_TOKEN: Final[LiteralString] +XML_ERROR_UNDECLARING_PREFIX: Final[LiteralString] +XML_ERROR_UNDEFINED_ENTITY: Final[LiteralString] +XML_ERROR_UNEXPECTED_STATE: Final[LiteralString] +XML_ERROR_UNKNOWN_ENCODING: Final[LiteralString] +XML_ERROR_XML_DECL: Final[LiteralString] if sys.version_info >= (3, 11): - XML_ERROR_RESERVED_PREFIX_XML: str - XML_ERROR_RESERVED_PREFIX_XMLNS: str - XML_ERROR_RESERVED_NAMESPACE_URI: str - XML_ERROR_INVALID_ARGUMENT: str - XML_ERROR_NO_BUFFER: str - XML_ERROR_AMPLIFICATION_LIMIT_BREACH: str + XML_ERROR_RESERVED_PREFIX_XML: Final[LiteralString] + XML_ERROR_RESERVED_PREFIX_XMLNS: Final[LiteralString] + XML_ERROR_RESERVED_NAMESPACE_URI: Final[LiteralString] + XML_ERROR_INVALID_ARGUMENT: Final[LiteralString] + XML_ERROR_NO_BUFFER: Final[LiteralString] + XML_ERROR_AMPLIFICATION_LIMIT_BREACH: Final[LiteralString] diff --git a/mypy/typeshed/stdlib/pyexpat/model.pyi b/mypy/typeshed/stdlib/pyexpat/model.pyi index f357cf6511a2..bac8f3692ce5 100644 --- a/mypy/typeshed/stdlib/pyexpat/model.pyi +++ b/mypy/typeshed/stdlib/pyexpat/model.pyi @@ -1,11 +1,13 @@ -XML_CTYPE_ANY: int -XML_CTYPE_CHOICE: int -XML_CTYPE_EMPTY: int -XML_CTYPE_MIXED: int -XML_CTYPE_NAME: int -XML_CTYPE_SEQ: int +from typing import Final -XML_CQUANT_NONE: int -XML_CQUANT_OPT: int -XML_CQUANT_PLUS: int -XML_CQUANT_REP: int +XML_CTYPE_ANY: Final = 2 +XML_CTYPE_EMPTY: Final = 1 +XML_CTYPE_MIXED: Final = 3 +XML_CTYPE_NAME: Final = 4 +XML_CTYPE_CHOICE: Final = 5 +XML_CTYPE_SEQ: Final = 6 + +XML_CQUANT_NONE: Final = 0 +XML_CQUANT_OPT: Final = 1 +XML_CQUANT_REP: Final = 2 +XML_CQUANT_PLUS: Final = 3 diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index b06f494c0b7d..76f98dd9f2a2 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -74,7 +74,7 @@ class Match(Generic[AnyStr]): @overload def expand(self: Match[str], template: str) -> str: ... @overload - def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... # type: ignore[overload-overlap] + def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... @overload def expand(self, template: AnyStr) -> AnyStr: ... # group() returns "AnyStr" or "AnyStr | None", depending on the pattern. @@ -124,19 +124,21 @@ class Pattern(Generic[AnyStr]): @overload def search(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... @overload def search(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def match(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... @overload def match(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def fullmatch(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def fullmatch(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def fullmatch( + self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize + ) -> Match[bytes] | None: ... @overload def fullmatch(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload @@ -155,13 +157,15 @@ class Pattern(Generic[AnyStr]): @overload def finditer(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[str]]: ... @overload - def finditer(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[bytes]]: ... # type: ignore[overload-overlap] + def finditer( + self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize + ) -> Iterator[Match[bytes]]: ... @overload def finditer(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[AnyStr]]: ... @overload def sub(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> str: ... @overload - def sub( # type: ignore[overload-overlap] + def sub( self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, @@ -172,7 +176,7 @@ class Pattern(Generic[AnyStr]): @overload def subn(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> tuple[str, int]: ... @overload - def subn( # type: ignore[overload-overlap] + def subn( self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index 5753d1d661b9..ae6575d85082 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -3,8 +3,9 @@ import types from _socket import _Address, _RetAddress from _typeshed import ReadableBuffer from collections.abc import Callable +from io import BufferedIOBase from socket import socket as _socket -from typing import Any, BinaryIO, ClassVar +from typing import Any, ClassVar from typing_extensions import Self, TypeAlias __all__ = [ @@ -158,11 +159,11 @@ class StreamRequestHandler(BaseRequestHandler): timeout: ClassVar[float | None] # undocumented disable_nagle_algorithm: ClassVar[bool] # undocumented connection: Any # undocumented - rfile: BinaryIO - wfile: BinaryIO + rfile: BufferedIOBase + wfile: BufferedIOBase class DatagramRequestHandler(BaseRequestHandler): - packet: _socket # undocumented + packet: bytes # undocumented socket: _socket # undocumented - rfile: BinaryIO - wfile: BinaryIO + rfile: BufferedIOBase + wfile: BufferedIOBase diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 9e46012ee777..0ee511df4e37 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -29,7 +29,10 @@ def DateFromTicks(ticks: float) -> Date: ... def TimeFromTicks(ticks: float) -> Time: ... def TimestampFromTicks(ticks: float) -> Timestamp: ... -version_info: tuple[int, int, int] +if sys.version_info < (3, 14): + # Deprecated in 3.12, removed in 3.14. + version_info: tuple[int, int, int] + sqlite_version_info: tuple[int, int, int] Binary = memoryview @@ -90,7 +93,10 @@ SQLITE_UPDATE: Final[int] adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] converters: dict[str, _Converter] sqlite_version: str -version: str + +if sys.version_info < (3, 14): + # Deprecated in 3.12, removed in 3.14. + version: str if sys.version_info >= (3, 11): SQLITE_ABORT: Final[int] diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index 5481d4d1dd4a..ee0a1eb2f1cb 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -2,6 +2,7 @@ import sys from _collections_abc import dict_keys from collections.abc import Sequence from typing import Any +from typing_extensions import deprecated __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] @@ -51,7 +52,9 @@ class Function(SymbolTable): def get_nonlocals(self) -> tuple[str, ...]: ... class Class(SymbolTable): - def get_methods(self) -> tuple[str, ...]: ... + if sys.version_info < (3, 16): + @deprecated("deprecated in Python 3.14, will be removed in Python 3.16") + def get_methods(self) -> tuple[str, ...]: ... class Symbol: def __init__( diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index d6adf21c1900..e46903bf610f 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -423,7 +423,7 @@ class TarInfo: name: str path: str size: int - mtime: int + mtime: int | float chksum: int devmajor: int devminor: int diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index d31fd74d3482..62422b84eb37 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -463,7 +463,7 @@ class TemporaryDirectory(Generic[AnyStr]): # The overloads overlap, but they should still work fine. @overload -def mkstemp( # type: ignore[overload-overlap] +def mkstemp( suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None, text: bool = False ) -> tuple[int, str]: ... @overload @@ -473,7 +473,7 @@ def mkstemp( # The overloads overlap, but they should still work fine. @overload -def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... # type: ignore[overload-overlap] +def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... @overload def mkdtemp(suffix: bytes | None = None, prefix: bytes | None = None, dir: BytesPath | None = None) -> bytes: ... def mktemp(suffix: str = "", prefix: str = "tmp", dir: StrPath | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 77953525bebe..2a42eb789731 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -2148,11 +2148,12 @@ class Listbox(Widget, XView, YView): selectborderwidth: _ScreenUnits = 0, selectforeground: str = ..., # from listbox man page: "The value of the [selectmode] option may be - # arbitrary, but the default bindings expect it to be ..." + # arbitrary, but the default bindings expect it to be either single, + # browse, multiple, or extended" # # I have never seen anyone setting this to something else than what # "the default bindings expect", but let's support it anyway. - selectmode: str = "browse", + selectmode: str | Literal["single", "browse", "multiple", "extended"] = "browse", # noqa: Y051 setgrid: bool = False, state: Literal["normal", "disabled"] = "normal", takefocus: _TakeFocusValue = "", @@ -2187,7 +2188,7 @@ class Listbox(Widget, XView, YView): selectbackground: str = ..., selectborderwidth: _ScreenUnits = ..., selectforeground: str = ..., - selectmode: str = ..., + selectmode: str | Literal["single", "browse", "multiple", "extended"] = ..., # noqa: Y051 setgrid: bool = ..., state: Literal["normal", "disabled"] = ..., takefocus: _TakeFocusValue = ..., @@ -2907,6 +2908,9 @@ class Scrollbar(Widget): def set(self, first: float | str, last: float | str) -> None: ... _TextIndex: TypeAlias = _tkinter.Tcl_Obj | str | float | Misc +_WhatToCount: TypeAlias = Literal[ + "chars", "displaychars", "displayindices", "displaylines", "indices", "lines", "xpixels", "ypixels" +] class Text(Widget, XView, YView): def __init__( @@ -3021,7 +3025,27 @@ class Text(Widget, XView, YView): config = configure def bbox(self, index: _TextIndex) -> tuple[int, int, int, int] | None: ... # type: ignore[override] def compare(self, index1: _TextIndex, op: Literal["<", "<=", "==", ">=", ">", "!="], index2: _TextIndex) -> bool: ... - def count(self, index1, index2, *args): ... # TODO + @overload + def count(self, index1: _TextIndex, index2: _TextIndex) -> tuple[int] | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], /) -> tuple[int] | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: Literal["update"], arg2: _WhatToCount, /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: Literal["update"], /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + ) -> tuple[int, ...]: ... @overload def debug(self, boolean: None = None) -> bool: ... @overload @@ -3564,7 +3588,7 @@ class Spinbox(Widget, XView): def scan_dragto(self, x): ... def selection(self, *args) -> tuple[int, ...]: ... def selection_adjust(self, index): ... - def selection_clear(self): ... + def selection_clear(self): ... # type: ignore[override] def selection_element(self, element: Incomplete | None = None): ... def selection_from(self, index: int) -> None: ... def selection_present(self) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/constants.pyi b/mypy/typeshed/stdlib/tkinter/constants.pyi index 0b497f3a42e4..fbfe8b49b997 100644 --- a/mypy/typeshed/stdlib/tkinter/constants.pyi +++ b/mypy/typeshed/stdlib/tkinter/constants.pyi @@ -1,12 +1,12 @@ from typing import Final # These are not actually bools. See #4669 -NO: bool -YES: bool -TRUE: bool -FALSE: bool -ON: bool -OFF: bool +NO: Final[bool] +YES: Final[bool] +TRUE: Final[bool] +FALSE: Final[bool] +ON: Final[bool] +OFF: Final[bool] N: Final = "n" S: Final = "s" W: Final = "w" diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi index f76732a25460..b7d74c0fa71e 100644 --- a/mypy/typeshed/stdlib/tkinter/dialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi @@ -2,12 +2,12 @@ import sys from _typeshed import Incomplete from collections.abc import Mapping from tkinter import Widget -from typing import Any +from typing import Any, Final if sys.version_info >= (3, 9): __all__ = ["Dialog"] -DIALOG_ICON: str +DIALOG_ICON: Final = "questhead" class Dialog(Widget): widgetName: str diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi index 5a04b66d7866..5cdfe512f9b7 100644 --- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi +++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi @@ -1,6 +1,6 @@ import sys from tkinter.commondialog import Dialog -from typing import ClassVar +from typing import ClassVar, Final if sys.version_info >= (3, 9): __all__ = [ @@ -14,22 +14,22 @@ if sys.version_info >= (3, 9): "askretrycancel", ] -ERROR: str -INFO: str -QUESTION: str -WARNING: str -ABORTRETRYIGNORE: str -OK: str -OKCANCEL: str -RETRYCANCEL: str -YESNO: str -YESNOCANCEL: str -ABORT: str -RETRY: str -IGNORE: str -CANCEL: str -YES: str -NO: str +ERROR: Final = "error" +INFO: Final = "info" +QUESTION: Final = "question" +WARNING: Final = "warning" +ABORTRETRYIGNORE: Final = "abortretryignore" +OK: Final = "ok" +OKCANCEL: Final = "okcancel" +RETRYCANCEL: Final = "retrycancel" +YESNO: Final = "yesno" +YESNOCANCEL: Final = "yesnocancel" +ABORT: Final = "abort" +RETRY: Final = "retry" +IGNORE: Final = "ignore" +CANCEL: Final = "cancel" +YES: Final = "yes" +NO: Final = "no" class Message(Dialog): command: ClassVar[str] diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index 86a23ce82211..b851f478140a 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -556,7 +556,9 @@ class Notebook(Widget): sticky: str = ..., # consists of letters 'n', 's', 'w', 'e', no repeats, may be empty padding: _Padding = ..., text: str = ..., - image=..., # Sequence of an image name, followed by zero or more (sequences of one or more state names followed by an image name) + # `image` is a sequence of an image name, followed by zero or more + # (sequences of one or more state names followed by an image name) + image=..., compound: tkinter._Compound = ..., underline: int = ..., ) -> None: ... @@ -1040,7 +1042,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def heading(self, column: str | int, option: str) -> Any: ... @overload - def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[overload-overlap] + def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... @overload def heading( self, @@ -1052,7 +1054,8 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): anchor: tkinter._Anchor = ..., command: str | Callable[[], object] = ..., ) -> None: ... - def identify(self, component, x, y): ... # Internal Method. Leave untyped + # Internal Method. Leave untyped: + def identify(self, component, x, y): ... # type: ignore[override] def identify_row(self, y: int) -> str: ... def identify_column(self, x: int) -> str: ... def identify_region(self, x: int, y: int) -> Literal["heading", "separator", "tree", "cell", "nothing"]: ... @@ -1084,7 +1087,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def item(self, item: str | int, option: str) -> Any: ... @overload - def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... # type: ignore[overload-overlap] + def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... @overload def item( self, diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 199feee746cb..29d289303927 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -338,7 +338,7 @@ class TPen: def isvisible(self) -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload - def pen(self) -> _PenState: ... # type: ignore[overload-overlap] + def pen(self) -> _PenState: ... @overload def pen( self, @@ -384,7 +384,7 @@ class RawTurtle(TPen, TNavigator): def shape(self, name: str) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapesize(self) -> tuple[float, float, float]: ... # type: ignore[overload-overlap] + def shapesize(self) -> tuple[float, float, float]: ... @overload def shapesize( self, stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None @@ -395,7 +395,7 @@ class RawTurtle(TPen, TNavigator): def shearfactor(self, shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapetransform(self) -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] + def shapetransform(self) -> tuple[float, float, float, float]: ... @overload def shapetransform( self, t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None @@ -622,7 +622,7 @@ def isvisible() -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload -def pen() -> _PenState: ... # type: ignore[overload-overlap] +def pen() -> _PenState: ... @overload def pen( pen: _PenState | None = None, @@ -661,7 +661,7 @@ if sys.version_info >= (3, 12): # Unsafely overlaps when no arguments are provided @overload -def shapesize() -> tuple[float, float, float]: ... # type: ignore[overload-overlap] +def shapesize() -> tuple[float, float, float]: ... @overload def shapesize(stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None) -> None: ... @overload @@ -671,7 +671,7 @@ def shearfactor(shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload -def shapetransform() -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] +def shapetransform() -> tuple[float, float, float, float]: ... @overload def shapetransform( t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 1e3eacd9f1fa..0f6592a9883e 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -305,9 +305,9 @@ class MappingProxyType(Mapping[_KT, _VT_co]): def values(self) -> ValuesView[_VT_co]: ... def items(self) -> ItemsView[_KT, _VT_co]: ... @overload - def get(self, key: _KT, /) -> _VT_co | None: ... # type: ignore[override] + def get(self, key: _KT, /) -> _VT_co | None: ... @overload - def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ... # type: ignore[override] + def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT]: ... @@ -583,7 +583,7 @@ _P = ParamSpec("_P") # it's not really an Awaitable, but can be used in an await expression. Real type: Generator & Awaitable @overload -def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... # type: ignore[overload-overlap] +def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... @overload def coroutine(func: _Fn) -> _Fn: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index f4de1fa86de5..cadd06358d4a 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -2,7 +2,7 @@ # ruff: noqa: F811 # TODO: The collections import is required, otherwise mypy crashes. # https://github.com/python/mypy/issues/16744 -import collections # noqa: F401 # pyright: ignore +import collections # noqa: F401 # pyright: ignore[reportUnusedImport] import sys import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values @@ -800,18 +800,12 @@ class IO(Iterator[AnyStr]): def writable(self) -> bool: ... @abstractmethod @overload - def write(self: IO[str], s: str, /) -> int: ... - @abstractmethod - @overload def write(self: IO[bytes], s: ReadableBuffer, /) -> int: ... @abstractmethod @overload def write(self, s: AnyStr, /) -> int: ... @abstractmethod @overload - def writelines(self: IO[str], lines: Iterable[str], /) -> None: ... - @abstractmethod - @overload def writelines(self: IO[bytes], lines: Iterable[ReadableBuffer], /) -> None: ... @abstractmethod @overload @@ -846,7 +840,8 @@ class TextIO(IO[str]): @abstractmethod def __enter__(self) -> TextIO: ... -ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview +if sys.version_info < (3, 14): + ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview # Functions diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 84620b7f3889..1cfd38f540a4 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -299,7 +299,7 @@ class _patcher: # Ideally we'd be able to add an overload for it so that the return type is _patch[MagicMock], # but that's impossible with the current type system. @overload - def __call__( # type: ignore[overload-overlap] + def __call__( self, target: str, new: _T, diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 0033083ac406..46da85619d30 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -2,36 +2,52 @@ import sys import unittest.case import unittest.result import unittest.suite -from _typeshed import Incomplete +from _typeshed import SupportsFlush, SupportsWrite from collections.abc import Callable, Iterable -from typing import TextIO -from typing_extensions import TypeAlias +from typing import Any, Generic, Protocol, TypeVar +from typing_extensions import Never, TypeAlias -_ResultClassType: TypeAlias = Callable[[TextIO, bool, int], unittest.result.TestResult] +_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult] -class TextTestResult(unittest.result.TestResult): +class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ... + +# All methods used by unittest.runner.TextTestResult's stream +class _TextTestStream(_SupportsWriteAndFlush, Protocol): + def writeln(self, arg: str | None = None) -> str: ... + +# _WritelnDecorator should have all the same attrs as its stream param. +# But that's not feasible to do Generically +# We can expand the attributes if requested +class _WritelnDecorator(_TextTestStream): + def __init__(self, stream: _TextTestStream) -> None: ... + def __getattr__(self, attr: str) -> Any: ... # Any attribute from the stream type passed to __init__ + # These attributes are prevented by __getattr__ + stream: Never + __getstate__: Never + +_StreamT = TypeVar("_StreamT", bound=_TextTestStream, default=_WritelnDecorator) + +class TextTestResult(unittest.result.TestResult, Generic[_StreamT]): descriptions: bool # undocumented dots: bool # undocumented separator1: str separator2: str showAll: bool # undocumented - stream: TextIO # undocumented + stream: _StreamT # undocumented if sys.version_info >= (3, 12): durations: unittest.result._DurationsType | None def __init__( - self, stream: TextIO, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None + self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None ) -> None: ... else: - def __init__(self, stream: TextIO, descriptions: bool, verbosity: int) -> None: ... + def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int) -> None: ... def getDescription(self, test: unittest.case.TestCase) -> str: ... def printErrorList(self, flavour: str, errors: Iterable[tuple[unittest.case.TestCase, str]]) -> None: ... class TextTestRunner: resultclass: _ResultClassType - # TODO: add `_WritelnDecorator` type - # stream: _WritelnDecorator - stream: Incomplete + stream: _WritelnDecorator descriptions: bool verbosity: int failfast: bool @@ -43,7 +59,7 @@ class TextTestRunner: durations: unittest.result._DurationsType | None def __init__( self, - stream: TextIO | None = None, + stream: _SupportsWriteAndFlush | None = None, descriptions: bool = True, verbosity: int = 1, failfast: bool = False, @@ -57,7 +73,7 @@ class TextTestRunner: else: def __init__( self, - stream: TextIO | None = None, + stream: _SupportsWriteAndFlush | None = None, descriptions: bool = True, verbosity: int = 1, failfast: bool = False, @@ -68,5 +84,5 @@ class TextTestRunner: tb_locals: bool = False, ) -> None: ... - def _makeResult(self) -> unittest.result.TestResult: ... - def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... + def _makeResult(self) -> TextTestResult: ... + def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> TextTestResult: ... diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index 89a50995d553..785bb9678ec7 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -198,13 +198,13 @@ else: # Requires an iterable of length 6 @overload -def urlunparse(components: Iterable[None]) -> Literal[b""]: ... +def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] @overload def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ... # Requires an iterable of length 5 @overload -def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... +def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] @overload def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ... def unwrap(url: str) -> str: ... diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 2a6476f9e6d8..ad4f91fc31ae 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -79,6 +79,7 @@ else: def pathname2url(pathname: str) -> str: ... def getproxies() -> dict[str, str]: ... +def getproxies_environment() -> dict[str, str]: ... def parse_http_list(s: str) -> list[str]: ... def parse_keqv_list(l: list[str]) -> dict[str, str]: ... diff --git a/mypy/typeshed/stdlib/xml/dom/__init__.pyi b/mypy/typeshed/stdlib/xml/dom/__init__.pyi index e5b91bf2a795..8738015638a9 100644 --- a/mypy/typeshed/stdlib/xml/dom/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/dom/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Final from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation @@ -17,22 +17,22 @@ class Node: NOTATION_NODE: int # ExceptionCode -INDEX_SIZE_ERR: int -DOMSTRING_SIZE_ERR: int -HIERARCHY_REQUEST_ERR: int -WRONG_DOCUMENT_ERR: int -INVALID_CHARACTER_ERR: int -NO_DATA_ALLOWED_ERR: int -NO_MODIFICATION_ALLOWED_ERR: int -NOT_FOUND_ERR: int -NOT_SUPPORTED_ERR: int -INUSE_ATTRIBUTE_ERR: int -INVALID_STATE_ERR: int -SYNTAX_ERR: int -INVALID_MODIFICATION_ERR: int -NAMESPACE_ERR: int -INVALID_ACCESS_ERR: int -VALIDATION_ERR: int +INDEX_SIZE_ERR: Final[int] +DOMSTRING_SIZE_ERR: Final[int] +HIERARCHY_REQUEST_ERR: Final[int] +WRONG_DOCUMENT_ERR: Final[int] +INVALID_CHARACTER_ERR: Final[int] +NO_DATA_ALLOWED_ERR: Final[int] +NO_MODIFICATION_ALLOWED_ERR: Final[int] +NOT_FOUND_ERR: Final[int] +NOT_SUPPORTED_ERR: Final[int] +INUSE_ATTRIBUTE_ERR: Final[int] +INVALID_STATE_ERR: Final[int] +SYNTAX_ERR: Final[int] +INVALID_MODIFICATION_ERR: Final[int] +NAMESPACE_ERR: Final[int] +INVALID_ACCESS_ERR: Final[int] +VALIDATION_ERR: Final[int] class DOMException(Exception): code: int @@ -62,8 +62,8 @@ class UserDataHandler: NODE_DELETED: int NODE_RENAMED: int -XML_NAMESPACE: str -XMLNS_NAMESPACE: str -XHTML_NAMESPACE: str -EMPTY_NAMESPACE: None -EMPTY_PREFIX: None +XML_NAMESPACE: Final[str] +XMLNS_NAMESPACE: Final[str] +XHTML_NAMESPACE: Final[str] +EMPTY_NAMESPACE: Final[None] +EMPTY_PREFIX: Final[None] diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi index cbba15dd3ebe..5a15772ec2a9 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi @@ -1,14 +1,15 @@ import sys from _typeshed import FileDescriptorOrPath from collections.abc import Callable +from typing import Final from xml.etree.ElementTree import Element -XINCLUDE: str -XINCLUDE_INCLUDE: str -XINCLUDE_FALLBACK: str +XINCLUDE: Final[str] +XINCLUDE_INCLUDE: Final[str] +XINCLUDE_FALLBACK: Final[str] if sys.version_info >= (3, 9): - DEFAULT_MAX_INCLUSION_DEPTH: int + DEFAULT_MAX_INCLUSION_DEPTH: Final = 6 class FatalIncludeError(SyntaxError): ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 4849b0ea1c35..64ebbd3ee63f 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -2,7 +2,7 @@ import sys from _collections_abc import dict_keys from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence -from typing import Any, Literal, SupportsIndex, TypeVar, overload +from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload from typing_extensions import TypeAlias, TypeGuard, deprecated __all__ = [ @@ -41,7 +41,7 @@ _FileRead: TypeAlias = FileDescriptorOrPath | SupportsRead[bytes] | SupportsRead _FileWriteC14N: TypeAlias = FileDescriptorOrPath | SupportsWrite[bytes] _FileWrite: TypeAlias = _FileWriteC14N | SupportsWrite[str] -VERSION: str +VERSION: Final[str] class ParseError(SyntaxError): code: int diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index 57a8a6aaa40a..5b8f02f61bce 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -94,6 +94,20 @@ class ZipExtFile(io.BufferedIOBase): class _Writer(Protocol): def write(self, s: str, /) -> object: ... +class _ZipReadable(Protocol): + def seek(self, offset: int, whence: int = 0, /) -> int: ... + def read(self, n: int = -1, /) -> bytes: ... + +class _ZipTellable(Protocol): + def tell(self) -> int: ... + +class _ZipReadableTellable(_ZipReadable, _ZipTellable, Protocol): ... + +class _ZipWritable(Protocol): + def flush(self) -> None: ... + def close(self) -> None: ... + def write(self, b: bytes, /) -> int: ... + class ZipFile: filename: str | None debug: int @@ -106,24 +120,50 @@ class ZipFile: compresslevel: int | None # undocumented mode: _ZipFileMode # undocumented pwd: bytes | None # undocumented + # metadata_encoding is new in 3.11 if sys.version_info >= (3, 11): @overload def __init__( self, file: StrPath | IO[bytes], + mode: _ZipFileMode = "r", + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + metadata_encoding: str | None = None, + ) -> None: ... + # metadata_encoding is only allowed for read mode + @overload + def __init__( + self, + file: StrPath | _ZipReadable, mode: Literal["r"] = "r", compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, *, strict_timestamps: bool = True, - metadata_encoding: str | None, + metadata_encoding: str | None = None, ) -> None: ... @overload def __init__( self, - file: StrPath | IO[bytes], - mode: _ZipFileMode = "r", + file: StrPath | _ZipWritable, + mode: Literal["w", "x"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + metadata_encoding: None = None, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadableTellable, + mode: Literal["a"] = ..., compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -132,6 +172,7 @@ class ZipFile: metadata_encoding: None = None, ) -> None: ... else: + @overload def __init__( self, file: StrPath | IO[bytes], @@ -142,6 +183,39 @@ class ZipFile: *, strict_timestamps: bool = True, ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadable, + mode: Literal["r"] = "r", + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipWritable, + mode: Literal["w", "x"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadableTellable, + mode: Literal["a"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... def __enter__(self) -> Self: ... def __exit__( @@ -230,6 +304,7 @@ else: class Path: root: CompleteDirs + at: str def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... @property def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi index bafbbeeb0d0b..933acf2c4803 100644 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -31,6 +31,7 @@ if sys.version_info >= (3, 12): class Path: root: CompleteDirs + at: str def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... @property def name(self) -> str: ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index b65a4cd59f79..dbf228623d7c 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -274,9 +274,7 @@ def bin(f: IO[bytes]) -> None: txt(sys.stdout) bin(sys.stdout) [out] -_program.py:5: error: No overload variant of "write" of "IO" matches argument type "bytes" -_program.py:5: note: Possible overload variants: -_program.py:5: note: def write(self, str, /) -> int +_program.py:5: error: Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str" _program.py:10: error: Argument 1 to "bin" has incompatible type "Union[TextIO, Any]"; expected "IO[bytes]" [case testBuiltinOpen] From 0ca47e8db3240d56b93eb75337b2393a09306dd6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:27:57 -0700 Subject: [PATCH 0735/1617] Sync typeshed (#17729) Source commit: https://github.com/python/typeshed/commit/23d867efb2df6de5600f64656f1aa8a83e06109e Note that you will need to close and re-open the PR in order to trigger CI. --------- Co-authored-by: mypybot <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: AlexWaygood --- mypy/typeshed/stdlib/distutils/cmd.pyi | 1 + mypy/typeshed/stdlib/distutils/dist.pyi | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index 1f3f31c9c48a..dcb423a49b09 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -30,6 +30,7 @@ _CommandT = TypeVar("_CommandT", bound=Command) _Ts = TypeVarTuple("_Ts") class Command: + dry_run: Literal[0, 1] # Exposed from __getattr_. Same as Distribution.dry_run distribution: Distribution # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index e32fd70f7baa..7013167dddbf 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -88,9 +88,9 @@ class Distribution: display_options: ClassVar[_OptionsList] display_option_names: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - verbose: int - dry_run: int - help: int + verbose: Literal[0, 1] + dry_run: Literal[0, 1] + help: Literal[0, 1] command_packages: list[str] | None script_name: str | None script_args: list[str] | None From 0412590ba13395a04f122a5dd83764db9d001fe6 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 2 Sep 2024 06:26:53 -0700 Subject: [PATCH 0736/1617] [nit] conf.py: annotate the type, instead of ignoring the error (#17727) There was a type-ignore here. But we can simply do what mypy is asking us to do; in fact, the comment even already tells us these have to be strings, (like the other arrays). --- mypyc/doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/doc/conf.py b/mypyc/doc/conf.py index da887e0d8267..fdd98c12a221 100644 --- a/mypyc/doc/conf.py +++ b/mypyc/doc/conf.py @@ -36,7 +36,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] # type: ignore[var-annotated] +extensions: list[str] = [] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] From 0c1036717578b00e35625cc353a538e4eb63bc37 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 7 Sep 2024 13:42:54 -0700 Subject: [PATCH 0737/1617] Use newer Python in docs build (#17747) Helps with #17742 --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8bded1d380aa..c6ed3cf1a08d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.12' - name: Install tox run: pip install tox==4.11.0 - name: Setup tox environment From 72c413d2352da5ce1433ef241faca8f40fa1fe27 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 12 Sep 2024 16:26:36 +0200 Subject: [PATCH 0738/1617] stubgen: Use `Generator` type var defaults (#17670) Fixes #17669 --- mypy/stubgen.py | 13 ++++++++++--- test-data/unit/stubgen.test | 34 +++++++++++++++++----------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 14417f55545e..02c0c1e58ab5 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -588,8 +588,8 @@ def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: if has_yield_expression(o) or has_yield_from_expression(o): generator_name = self.add_name("collections.abc.Generator") yield_name = "None" - send_name = "None" - return_name = "None" + send_name: str | None = None + return_name: str | None = None if has_yield_from_expression(o): yield_name = send_name = self.add_name("_typeshed.Incomplete") else: @@ -600,7 +600,14 @@ def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: send_name = self.add_name("_typeshed.Incomplete") if has_return_statement(o): return_name = self.add_name("_typeshed.Incomplete") - return f"{generator_name}[{yield_name}, {send_name}, {return_name}]" + if return_name is not None: + if send_name is None: + send_name = "None" + return f"{generator_name}[{yield_name}, {send_name}, {return_name}]" + elif send_name is not None: + return f"{generator_name}[{yield_name}, {send_name}]" + else: + return f"{generator_name}[{yield_name}]" if not has_return_statement(o) and o.abstract_status == NOT_ABSTRACT: return "None" return None diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index fe0538159aa3..69781e9d2143 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1640,11 +1640,11 @@ def all(): from _typeshed import Incomplete from collections.abc import Generator -def f() -> Generator[Incomplete, None, None]: ... -def g() -> Generator[None, Incomplete, None]: ... -def h1() -> Generator[None, None, None]: ... +def f() -> Generator[Incomplete]: ... +def g() -> Generator[None, Incomplete]: ... +def h1() -> Generator[None]: ... def h2() -> Generator[None, None, Incomplete]: ... -def h3() -> Generator[None, None, None]: ... +def h3() -> Generator[None]: ... def all() -> Generator[Incomplete, Incomplete, Incomplete]: ... [case testFunctionYieldsNone] @@ -1656,8 +1656,8 @@ def g(): [out] from collections.abc import Generator -def f() -> Generator[None, None, None]: ... -def g() -> Generator[None, None, None]: ... +def f() -> Generator[None]: ... +def g() -> Generator[None]: ... [case testGeneratorAlreadyDefined] class Generator: @@ -1671,7 +1671,7 @@ from collections.abc import Generator as _Generator class Generator: ... -def f() -> _Generator[Incomplete, None, None]: ... +def f() -> _Generator[Incomplete]: ... [case testGeneratorYieldFrom] def g1(): @@ -1692,10 +1692,10 @@ def g5(): from _typeshed import Incomplete from collections.abc import Generator -def g1() -> Generator[Incomplete, Incomplete, None]: ... -def g2() -> Generator[Incomplete, Incomplete, None]: ... -def g3() -> Generator[Incomplete, Incomplete, None]: ... -def g4() -> Generator[Incomplete, Incomplete, None]: ... +def g1() -> Generator[Incomplete, Incomplete]: ... +def g2() -> Generator[Incomplete, Incomplete]: ... +def g3() -> Generator[Incomplete, Incomplete]: ... +def g4() -> Generator[Incomplete, Incomplete]: ... def g5() -> Generator[Incomplete, Incomplete, Incomplete]: ... [case testGeneratorYieldAndYieldFrom] @@ -1728,13 +1728,13 @@ def g7(): from _typeshed import Incomplete from collections.abc import Generator -def g1() -> Generator[Incomplete, Incomplete, None]: ... -def g2() -> Generator[Incomplete, Incomplete, None]: ... -def g3() -> Generator[Incomplete, Incomplete, None]: ... -def g4() -> Generator[Incomplete, Incomplete, None]: ... -def g5() -> Generator[Incomplete, Incomplete, None]: ... +def g1() -> Generator[Incomplete, Incomplete]: ... +def g2() -> Generator[Incomplete, Incomplete]: ... +def g3() -> Generator[Incomplete, Incomplete]: ... +def g4() -> Generator[Incomplete, Incomplete]: ... +def g5() -> Generator[Incomplete, Incomplete]: ... def g6() -> Generator[Incomplete, Incomplete, Incomplete]: ... -def g7() -> Generator[Incomplete, Incomplete, None]: ... +def g7() -> Generator[Incomplete, Incomplete]: ... [case testCallable] from typing import Callable From f8195bc735e4a4a424a863fd9e40b2448f79d616 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:39:24 -0700 Subject: [PATCH 0739/1617] Sync typeshed (#17772) Source commit: https://github.com/python/typeshed/commit/9e506eb5e8fc2823db8c60ad561b1145ff114947 --- mypy/typeshed/stdlib/VERSIONS | 2 +- mypy/typeshed/stdlib/_curses.pyi | 2 +- mypy/typeshed/stdlib/_locale.pyi | 149 ++++++++++-------- mypy/typeshed/stdlib/_winapi.pyi | 28 ++++ mypy/typeshed/stdlib/builtins.pyi | 1 + mypy/typeshed/stdlib/codecs.pyi | 2 +- mypy/typeshed/stdlib/copy.pyi | 9 +- mypy/typeshed/stdlib/distutils/dist.pyi | 25 ++- mypy/typeshed/stdlib/doctest.pyi | 26 ++- .../stdlib/email/_header_value_parser.pyi | 4 + mypy/typeshed/stdlib/email/_policybase.pyi | 39 +++-- mypy/typeshed/stdlib/email/errors.pyi | 3 + mypy/typeshed/stdlib/email/utils.pyi | 16 +- mypy/typeshed/stdlib/io.pyi | 3 +- mypy/typeshed/stdlib/subprocess.pyi | 5 + mypy/typeshed/stdlib/tempfile.pyi | 6 +- 16 files changed, 214 insertions(+), 106 deletions(-) diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 641f951ce3c0..66bf2bec7cb0 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -41,7 +41,7 @@ _json: 3.0- _locale: 3.0- _lsprof: 3.0- _markupbase: 3.0- -_msi: 3.0- +_msi: 3.0-3.12 _operator: 3.4- _osx_support: 3.0- _posixsubprocess: 3.2- diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 505637574af1..b68c8925a041 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -493,7 +493,7 @@ class _CursesWindow: def instr(self, y: int, x: int, n: int = ...) -> bytes: ... def is_linetouched(self, line: int, /) -> bool: ... def is_wintouched(self) -> bool: ... - def keypad(self, yes: bool) -> None: ... + def keypad(self, yes: bool, /) -> None: ... def leaveok(self, yes: bool) -> None: ... def move(self, new_y: int, new_x: int) -> None: ... def mvderwin(self, y: int, x: int) -> None: ... diff --git a/mypy/typeshed/stdlib/_locale.pyi b/mypy/typeshed/stdlib/_locale.pyi index 0825e12034f4..ccce7a0d9d70 100644 --- a/mypy/typeshed/stdlib/_locale.pyi +++ b/mypy/typeshed/stdlib/_locale.pyi @@ -1,17 +1,38 @@ import sys from _typeshed import StrPath -from collections.abc import Mapping +from typing import Final, Literal, TypedDict, type_check_only -LC_CTYPE: int -LC_COLLATE: int -LC_TIME: int -LC_MONETARY: int -LC_NUMERIC: int -LC_ALL: int -CHAR_MAX: int +@type_check_only +class _LocaleConv(TypedDict): + decimal_point: str + grouping: list[int] + thousands_sep: str + int_curr_symbol: str + currency_symbol: str + p_cs_precedes: Literal[0, 1, 127] + n_cs_precedes: Literal[0, 1, 127] + p_sep_by_space: Literal[0, 1, 127] + n_sep_by_space: Literal[0, 1, 127] + mon_decimal_point: str + frac_digits: int + int_frac_digits: int + mon_thousands_sep: str + mon_grouping: list[int] + positive_sign: str + negative_sign: str + p_sign_posn: Literal[0, 1, 2, 3, 4, 127] + n_sign_posn: Literal[0, 1, 2, 3, 4, 127] + +LC_CTYPE: Final[int] +LC_COLLATE: Final[int] +LC_TIME: Final[int] +LC_MONETARY: Final[int] +LC_NUMERIC: Final[int] +LC_ALL: Final[int] +CHAR_MAX: Final = 127 def setlocale(category: int, locale: str | None = None, /) -> str: ... -def localeconv() -> Mapping[str, int | str | list[int]]: ... +def localeconv() -> _LocaleConv: ... if sys.version_info >= (3, 11): def getencoding() -> str: ... @@ -25,67 +46,67 @@ def strxfrm(string: str, /) -> str: ... if sys.platform != "win32": LC_MESSAGES: int - ABDAY_1: int - ABDAY_2: int - ABDAY_3: int - ABDAY_4: int - ABDAY_5: int - ABDAY_6: int - ABDAY_7: int + ABDAY_1: Final[int] + ABDAY_2: Final[int] + ABDAY_3: Final[int] + ABDAY_4: Final[int] + ABDAY_5: Final[int] + ABDAY_6: Final[int] + ABDAY_7: Final[int] - ABMON_1: int - ABMON_2: int - ABMON_3: int - ABMON_4: int - ABMON_5: int - ABMON_6: int - ABMON_7: int - ABMON_8: int - ABMON_9: int - ABMON_10: int - ABMON_11: int - ABMON_12: int + ABMON_1: Final[int] + ABMON_2: Final[int] + ABMON_3: Final[int] + ABMON_4: Final[int] + ABMON_5: Final[int] + ABMON_6: Final[int] + ABMON_7: Final[int] + ABMON_8: Final[int] + ABMON_9: Final[int] + ABMON_10: Final[int] + ABMON_11: Final[int] + ABMON_12: Final[int] - DAY_1: int - DAY_2: int - DAY_3: int - DAY_4: int - DAY_5: int - DAY_6: int - DAY_7: int + DAY_1: Final[int] + DAY_2: Final[int] + DAY_3: Final[int] + DAY_4: Final[int] + DAY_5: Final[int] + DAY_6: Final[int] + DAY_7: Final[int] - ERA: int - ERA_D_T_FMT: int - ERA_D_FMT: int - ERA_T_FMT: int + ERA: Final[int] + ERA_D_T_FMT: Final[int] + ERA_D_FMT: Final[int] + ERA_T_FMT: Final[int] - MON_1: int - MON_2: int - MON_3: int - MON_4: int - MON_5: int - MON_6: int - MON_7: int - MON_8: int - MON_9: int - MON_10: int - MON_11: int - MON_12: int + MON_1: Final[int] + MON_2: Final[int] + MON_3: Final[int] + MON_4: Final[int] + MON_5: Final[int] + MON_6: Final[int] + MON_7: Final[int] + MON_8: Final[int] + MON_9: Final[int] + MON_10: Final[int] + MON_11: Final[int] + MON_12: Final[int] - CODESET: int - D_T_FMT: int - D_FMT: int - T_FMT: int - T_FMT_AMPM: int - AM_STR: int - PM_STR: int + CODESET: Final[int] + D_T_FMT: Final[int] + D_FMT: Final[int] + T_FMT: Final[int] + T_FMT_AMPM: Final[int] + AM_STR: Final[int] + PM_STR: Final[int] - RADIXCHAR: int - THOUSEP: int - YESEXPR: int - NOEXPR: int - CRNCYSTR: int - ALT_DIGITS: int + RADIXCHAR: Final[int] + THOUSEP: Final[int] + YESEXPR: Final[int] + NOEXPR: Final[int] + CRNCYSTR: Final[int] + ALT_DIGITS: Final[int] def nl_langinfo(key: int, /) -> str: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index 62ea124045cc..0f71a0687748 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -99,6 +99,20 @@ if sys.platform == "win32": SEC_RESERVE: Final = 0x4000000 SEC_WRITECOMBINE: Final = 0x40000000 + if sys.version_info >= (3, 13): + STARTF_FORCEOFFFEEDBACK: Final = 0x80 + STARTF_FORCEONFEEDBACK: Final = 0x40 + STARTF_PREVENTPINNING: Final = 0x2000 + STARTF_RUNFULLSCREEN: Final = 0x20 + STARTF_TITLEISAPPID: Final = 0x1000 + STARTF_TITLEISLINKNAME: Final = 0x800 + STARTF_UNTRUSTEDSOURCE: Final = 0x8000 + STARTF_USECOUNTCHARS: Final = 0x8 + STARTF_USEFILLATTRIBUTE: Final = 0x10 + STARTF_USEHOTKEY: Final = 0x200 + STARTF_USEPOSITION: Final = 0x4 + STARTF_USESIZE: Final = 0x2 + STARTF_USESHOWWINDOW: Final = 0x1 STARTF_USESTDHANDLES: Final = 0x100 @@ -250,6 +264,20 @@ if sys.platform == "win32": def cancel(self) -> None: ... def getbuffer(self) -> bytes | None: ... + if sys.version_info >= (3, 13): + def BatchedWaitForMultipleObjects( + handle_seq: Sequence[int], wait_all: bool, milliseconds: int = 0xFFFFFFFF + ) -> list[int]: ... + def CreateEventW(security_attributes: int, manual_reset: bool, initial_state: bool, name: str | None) -> int: ... + def CreateMutexW(security_attributes: int, initial_owner: bool, name: str) -> int: ... + def GetLongPathName(path: str) -> str: ... + def GetShortPathName(path: str) -> str: ... + def OpenEventW(desired_access: int, inherit_handle: bool, name: str) -> int: ... + def OpenMutexW(desired_access: int, inherit_handle: bool, name: str) -> int: ... + def ReleaseMutex(mutex: int) -> None: ... + def ResetEvent(event: int) -> None: ... + def SetEvent(event: int) -> None: ... + if sys.version_info >= (3, 12): def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ... def NeedCurrentDirectoryForExePath(exe_name: str, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index a6065cc6777f..0999fb1d6c36 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1,3 +1,4 @@ +# ruff: noqa: PYI036 # This is the module declaring BaseException import _ast import _typeshed import sys diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 9bc098dbc6d7..a41df9752d33 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -80,7 +80,7 @@ class _Encoder(Protocol): def __call__(self, input: str, errors: str = ..., /) -> tuple[bytes, int]: ... # signature of Codec().encode class _Decoder(Protocol): - def __call__(self, input: bytes, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode + def __call__(self, input: ReadableBuffer, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode class _StreamReader(Protocol): def __call__(self, stream: _ReadableStream, errors: str = ..., /) -> StreamReader: ... diff --git a/mypy/typeshed/stdlib/copy.pyi b/mypy/typeshed/stdlib/copy.pyi index 020ce6c31b58..2cceec6a2250 100644 --- a/mypy/typeshed/stdlib/copy.pyi +++ b/mypy/typeshed/stdlib/copy.pyi @@ -1,16 +1,15 @@ import sys from typing import Any, Protocol, TypeVar -from typing_extensions import ParamSpec, Self +from typing_extensions import Self __all__ = ["Error", "copy", "deepcopy"] _T = TypeVar("_T") -_SR = TypeVar("_SR", bound=_SupportsReplace[Any]) -_P = ParamSpec("_P") +_SR = TypeVar("_SR", bound=_SupportsReplace) -class _SupportsReplace(Protocol[_P]): +class _SupportsReplace(Protocol): # In reality doesn't support args, but there's no other great way to express this. - def __replace__(self, *args: _P.args, **kwargs: _P.kwargs) -> Self: ... + def __replace__(self, *args: Any, **kwargs: Any) -> Self: ... # None in CPython but non-None in Jython PyStringMap: Any diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 7013167dddbf..75fc7dbb388d 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -270,7 +270,7 @@ class Distribution: def has_data_files(self) -> bool: ... def is_pure(self) -> bool: ... - # Getter methods generated in __init__ + # Default getter methods generated in __init__ from self.metadata._METHOD_BASENAMES def get_name(self) -> str: ... def get_version(self) -> str: ... def get_fullname(self) -> str: ... @@ -292,3 +292,26 @@ class Distribution: def get_requires(self) -> list[str]: ... def get_provides(self) -> list[str]: ... def get_obsoletes(self) -> list[str]: ... + + # Default attributes generated in __init__ from self.display_option_names + help_commands: bool | Literal[0] + name: str | Literal[0] + version: str | Literal[0] + fullname: str | Literal[0] + author: str | Literal[0] + author_email: str | Literal[0] + maintainer: str | Literal[0] + maintainer_email: str | Literal[0] + contact: str | Literal[0] + contact_email: str | Literal[0] + url: str | Literal[0] + license: str | Literal[0] + licence: str | Literal[0] + description: str | Literal[0] + long_description: str | Literal[0] + platforms: str | list[str] | Literal[0] + classifiers: str | list[str] | Literal[0] + keywords: str | list[str] | Literal[0] + provides: list[str] | Literal[0] + requires: list[str] | Literal[0] + obsoletes: list[str] | Literal[0] diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 7e334ef0c504..4380083027a6 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -1,9 +1,10 @@ +import sys import types import unittest from _typeshed import ExcInfo from collections.abc import Callable -from typing import Any, NamedTuple -from typing_extensions import TypeAlias +from typing import Any, ClassVar, NamedTuple +from typing_extensions import Self, TypeAlias __all__ = [ "register_optionflag", @@ -41,9 +42,22 @@ __all__ = [ "debug", ] -class TestResults(NamedTuple): - failed: int - attempted: int +# MyPy errors on conditionals within named tuples. + +if sys.version_info >= (3, 13): + class TestResults(NamedTuple): + def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc] + skipped: int + failed: int + attempted: int + _fields: ClassVar = ("failed", "attempted") # type: ignore[misc] + __match_args__ = ("failed", "attempted") # type: ignore[misc] + __doc__: None # type: ignore[misc] + +else: + class TestResults(NamedTuple): + failed: int + attempted: int OPTIONFLAGS_BY_NAME: dict[str, int] @@ -134,6 +148,8 @@ class DocTestRunner: original_optionflags: int tries: int failures: int + if sys.version_info >= (3, 13): + skips: int test: DocTest def __init__(self, checker: OutputChecker | None = None, verbose: bool | None = None, optionflags: int = 0) -> None: ... def report_start(self, out: _Out, test: DocTest, example: Example) -> None: ... diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index 806fc84cf784..ff405a8b61d2 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -16,6 +16,10 @@ TOKEN_ENDS: Final[set[str]] ASPECIALS: Final[set[str]] ATTRIBUTE_ENDS: Final[set[str]] EXTENDED_ATTRIBUTE_ENDS: Final[set[str]] +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +NLSET: Final[set[str]] +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +SPECIALSNL: Final[set[str]] def quote_string(value: Any) -> str: ... diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi index a3dd61a282ce..9e1f653c9d78 100644 --- a/mypy/typeshed/stdlib/email/_policybase.pyi +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -3,20 +3,9 @@ from collections.abc import Callable from email.errors import MessageDefect from email.header import Header from email.message import Message -from typing import Any from typing_extensions import Self class _PolicyBase: - def __add__(self, other: Any) -> Self: ... - def clone(self, **kw: Any) -> Self: ... - -class Policy(_PolicyBase, metaclass=ABCMeta): - max_line_length: int | None - linesep: str - cte_type: str - raise_on_defect: bool - mangle_from_: bool - message_factory: Callable[[Policy], Message] | None def __init__( self, *, @@ -24,9 +13,35 @@ class Policy(_PolicyBase, metaclass=ABCMeta): linesep: str = "\n", cte_type: str = "8bit", raise_on_defect: bool = False, - mangle_from_: bool = False, + mangle_from_: bool = ..., # default depends on sub-class message_factory: Callable[[Policy], Message] | None = None, + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = True, ) -> None: ... + def clone( + self, + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: Callable[[Policy], Message] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., + ) -> Self: ... + def __add__(self, other: Policy) -> Self: ... + +class Policy(_PolicyBase, metaclass=ABCMeta): + max_line_length: int | None + linesep: str + cte_type: str + raise_on_defect: bool + mangle_from_: bool + message_factory: Callable[[Policy], Message] | None + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool + def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... def header_max_count(self, name: str) -> int | None: ... diff --git a/mypy/typeshed/stdlib/email/errors.pyi b/mypy/typeshed/stdlib/email/errors.pyi index c54f1560c9ae..f105576c5ee4 100644 --- a/mypy/typeshed/stdlib/email/errors.pyi +++ b/mypy/typeshed/stdlib/email/errors.pyi @@ -7,6 +7,9 @@ class BoundaryError(MessageParseError): ... class MultipartConversionError(MessageError, TypeError): ... class CharsetError(MessageError): ... +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +class HeaderWriteError(MessageError): ... + class MessageDefect(ValueError): def __init__(self, line: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 9dab22c18f6c..dc3eecb5ef7f 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -30,20 +30,12 @@ _PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None def quote(str: str) -> str: ... def unquote(str: str) -> str: ... -if sys.version_info >= (3, 13): - def parseaddr(addr: str | list[str], *, strict: bool = True) -> tuple[str, str]: ... - -else: - def parseaddr(addr: str) -> tuple[str, str]: ... - +# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +def parseaddr(addr: str | list[str], *, strict: bool = True) -> tuple[str, str]: ... def formataddr(pair: tuple[str | None, str], charset: str | Charset = "utf-8") -> str: ... -if sys.version_info >= (3, 13): - def getaddresses(fieldvalues: Iterable[str], *, strict: bool = True) -> list[tuple[str, str]]: ... - -else: - def getaddresses(fieldvalues: Iterable[str]) -> list[tuple[str, str]]: ... - +# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +def getaddresses(fieldvalues: Iterable[str], *, strict: bool = True) -> list[tuple[str, str]]: ... @overload def parsedate(data: None) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 2d64d261951d..7607608696dd 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -84,7 +84,6 @@ class RawIOBase(IOBase): def read(self, size: int = -1, /) -> bytes | None: ... class BufferedIOBase(IOBase): - raw: RawIOBase # This is not part of the BufferedIOBase API and may not exist on some implementations. def detach(self) -> RawIOBase: ... def readinto(self, buffer: WriteableBuffer, /) -> int: ... def write(self, buffer: ReadableBuffer, /) -> int: ... @@ -119,11 +118,13 @@ class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible d def read1(self, size: int | None = -1, /) -> bytes: ... class BufferedReader(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + raw: RawIOBase def __enter__(self) -> Self: ... def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... class BufferedWriter(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + raw: RawIOBase def __enter__(self) -> Self: ... def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... def write(self, buffer: ReadableBuffer, /) -> int: ... diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 2a5859807b51..703a5012012c 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -2582,6 +2582,11 @@ else: def list2cmdline(seq: Iterable[StrOrBytesPath]) -> str: ... # undocumented if sys.platform == "win32": + if sys.version_info >= (3, 13): + from _winapi import STARTF_FORCEOFFFEEDBACK, STARTF_FORCEONFEEDBACK + + __all__ += ["STARTF_FORCEOFFFEEDBACK", "STARTF_FORCEONFEEDBACK"] + class STARTUPINFO: def __init__( self, diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 62422b84eb37..0c19d56fc7a6 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -253,11 +253,11 @@ class _TemporaryFileWrapper(IO[AnyStr]): def truncate(self, size: int | None = ...) -> int: ... def writable(self) -> bool: ... @overload - def write(self: _TemporaryFileWrapper[str], s: str) -> int: ... + def write(self: _TemporaryFileWrapper[str], s: str, /) -> int: ... @overload - def write(self: _TemporaryFileWrapper[bytes], s: ReadableBuffer) -> int: ... + def write(self: _TemporaryFileWrapper[bytes], s: ReadableBuffer, /) -> int: ... @overload - def write(self, s: AnyStr) -> int: ... + def write(self, s: AnyStr, /) -> int: ... @overload def writelines(self: _TemporaryFileWrapper[str], lines: Iterable[str]) -> None: ... @overload From 8b1e6064928bdb173822df639a73c8cbac0845ec Mon Sep 17 00:00:00 2001 From: quinn-sasha Date: Mon, 16 Sep 2024 07:44:03 +0900 Subject: [PATCH 0740/1617] Make changelog visible in mypy documentation (#17742) Resolves #17717 --- CHANGELOG.md | 310 ++++++++++++++++++------------------- docs/requirements-docs.txt | 1 + docs/source/changelog.md | 3 + docs/source/conf.py | 2 +- docs/source/index.rst | 1 + 5 files changed, 161 insertions(+), 156 deletions(-) create mode 100644 docs/source/changelog.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b544e05ee573..9632cb39a8b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ We’ve just uploaded mypy 1.11 to the Python Package Index ([PyPI](https://pypi You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Support Python 3.12 Syntax for Generics (PEP 695) +### Support Python 3.12 Syntax for Generics (PEP 695) Mypy now supports the new type parameter syntax introduced in Python 3.12 ([PEP 695](https://peps.python.org/pep-0695/)). This feature is still experimental and must be enabled with the `--enable-incomplete-feature=NewGenericSyntax` flag, or with `enable_incomplete_feature = NewGenericSyntax` in the mypy configuration file. @@ -40,7 +40,7 @@ type A[T] = C[list[T]] This feature was contributed by Jukka Lehtosalo. -#### Support for `functools.partial` +### Support for `functools.partial` Mypy now type checks uses of `functools.partial`. Previously mypy would accept arbitrary arguments. @@ -60,7 +60,7 @@ g(11) This feature was contributed by Shantanu (PR [16939](https://github.com/python/mypy/pull/16939)). -#### Stricter Checks for Untyped Overrides +### Stricter Checks for Untyped Overrides Past mypy versions didn't check if untyped methods were compatible with overridden methods. This would result in false negatives. Now mypy performs these checks when using `--check-untyped-defs`. @@ -78,7 +78,7 @@ class Derived(Base): This feature was contributed by Steven Troxler (PR [17276](https://github.com/python/mypy/pull/17276)). -#### Type Inference Improvements +### Type Inference Improvements The new polymorphic inference algorithm introduced in mypy 1.5 is now used in more situations. This improves type inference involving generic higher-order functions, in particular. @@ -95,14 +95,14 @@ for x in (1, 'x'): This was also contributed by Ivan Levkivskyi (PR [17408](https://github.com/python/mypy/pull/17408)). -#### Improvements to Detection of Overlapping Overloads +### Improvements to Detection of Overlapping Overloads The details of how mypy checks if two `@overload` signatures are unsafely overlapping were overhauled. This both fixes some false positives, and allows mypy to detect additional unsafe signatures. This feature was contributed by Ivan Levkivskyi (PR [17392](https://github.com/python/mypy/pull/17392)). -#### Better Support for Type Hints in Expressions +### Better Support for Type Hints in Expressions Mypy now allows more expressions that evaluate to valid type annotations in all expression contexts. The inferred types of these expressions are also sometimes more precise. Previously they were often `object`. @@ -117,7 +117,7 @@ print(Callable[[], int] | None) # No error This feature was contributed by Jukka Lehtosalo (PR [17404](https://github.com/python/mypy/pull/17404)). -#### Mypyc Improvements +### Mypyc Improvements Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is signficantly faster basic operations on `int` values. @@ -129,18 +129,18 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab * Allow specifying primitives as pure (Jukka Lehtosalo, PR [17263](https://github.com/python/mypy/pull/17263)) -#### Changes to Stubtest +### Changes to Stubtest * Ignore `_ios_support` (Alex Waygood, PR [17270](https://github.com/python/mypy/pull/17270)) * Improve support for Python 3.13 (Shantanu, PR [17261](https://github.com/python/mypy/pull/17261)) -#### Changes to Stubgen +### Changes to Stubgen * Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (Ali Hamdan, PR [17386](https://github.com/python/mypy/pull/17386)) * Fix for Python 3.13 (Jelle Zijlstra, PR [17290](https://github.com/python/mypy/pull/17290)) * Preserve enum value initialisers (Shantanu, PR [17125](https://github.com/python/mypy/pull/17125)) -#### Miscellaneous New Features +### Miscellaneous New Features * Add error format support and JSON output option via `--output json` (Tushar Sadhwani, PR [11396](https://github.com/python/mypy/pull/11396)) * Support `enum.member` in Python 3.11+ (Nikita Sobolev, PR [17382](https://github.com/python/mypy/pull/17382)) * Support `enum.nonmember` in Python 3.11+ (Nikita Sobolev, PR [17376](https://github.com/python/mypy/pull/17376)) @@ -149,7 +149,7 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab * Add support for `__spec__` (Shantanu, PR [14739](https://github.com/python/mypy/pull/14739)) -#### Changes to Error Reporting +### Changes to Error Reporting * Mention `--enable-incomplete-feature=NewGenericSyntax` in messages (Shantanu, PR [17462](https://github.com/python/mypy/pull/17462)) * Do not report plugin-generated methods with `explicit-override` (sobolevn, PR [17433](https://github.com/python/mypy/pull/17433)) * Use and display namespaces for function type variables (Ivan Levkivskyi, PR [17311](https://github.com/python/mypy/pull/17311)) @@ -162,7 +162,7 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab * Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) -#### Fixes for Crashes +### Fixes for Crashes * Fix daemon crash on invalid type in TypedDict (Ivan Levkivskyi, PR [17495](https://github.com/python/mypy/pull/17495)) * Fix crash and bugs related to `partial()` (Ivan Levkivskyi, PR [17423](https://github.com/python/mypy/pull/17423)) * Fix crash when overriding with unpacked TypedDict (Ivan Levkivskyi, PR [17359](https://github.com/python/mypy/pull/17359)) @@ -174,14 +174,14 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab * Fix crash on type comment inside generic definitions (Bénédikt Tran, PR [16849](https://github.com/python/mypy/pull/16849)) -#### Changes to Documentation +### Changes to Documentation * Use inline config in documentation for optional error codes (Shantanu, PR [17374](https://github.com/python/mypy/pull/17374)) * Use lower-case generics in documentation (Seo Sanghyeon, PR [17176](https://github.com/python/mypy/pull/17176)) * Add documentation for show-error-code-links (GiorgosPapoutsakis, PR [17144](https://github.com/python/mypy/pull/17144)) * Update CONTRIBUTING.md to include commands for Windows (GiorgosPapoutsakis, PR [17142](https://github.com/python/mypy/pull/17142)) -#### Other Notable Improvements and Fixes +### Other Notable Improvements and Fixes * Fix ParamSpec inference against TypeVarTuple (Ivan Levkivskyi, PR [17431](https://github.com/python/mypy/pull/17431)) * Fix explicit type for `partial` (Ivan Levkivskyi, PR [17424](https://github.com/python/mypy/pull/17424)) * Always allow lambda calls (Ivan Levkivskyi, PR [17430](https://github.com/python/mypy/pull/17430)) @@ -206,12 +206,12 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab * Fix Literal strings containing pipe characters (Jelle Zijlstra, PR [17148](https://github.com/python/mypy/pull/17148)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: - Alex Waygood @@ -254,7 +254,7 @@ We’ve just uploaded mypy 1.10 to the Python Package Index ([PyPI](https://pypi You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Support TypeIs (PEP 742) +### Support TypeIs (PEP 742) Mypy now supports `TypeIs` ([PEP 742](https://peps.python.org/pep-0742/)), which allows functions to narrow the type of a value, similar to `isinstance()`. Unlike `TypeGuard`, @@ -281,7 +281,7 @@ can be used on earlier Python versions by importing it from This feature was contributed by Jelle Zijlstra (PR [16898](https://github.com/python/mypy/pull/16898)). -#### Support TypeVar Defaults (PEP 696) +### Support TypeVar Defaults (PEP 696) [PEP 696](https://peps.python.org/pep-0696/) adds support for type parameter defaults. Example: @@ -308,7 +308,7 @@ can be used with earlier Python releases by importing `TypeVar` from This feature was contributed by Marc Mueller (PR [16878](https://github.com/python/mypy/pull/16878) and PR [16925](https://github.com/python/mypy/pull/16925)). -#### Support TypeAliasType (PEP 695) +### Support TypeAliasType (PEP 695) As part of the initial steps towards implementing [PEP 695](https://peps.python.org/pep-0695/), mypy now supports `TypeAliasType`. `TypeAliasType` provides a backport of the new `type` statement in Python 3.12. @@ -344,7 +344,7 @@ c: ListOrSet[str] = 'test' # error: Incompatible types in assignment (expressio This feature was contributed by Ali Hamdan (PR [16926](https://github.com/python/mypy/pull/16926), PR [17038](https://github.com/python/mypy/pull/17038) and PR [17053](https://github.com/python/mypy/pull/17053)) -#### Detect Additional Unsafe Uses of super() +### Detect Additional Unsafe Uses of super() Mypy will reject unsafe uses of `super()` more consistently, when the target has a trivial (empty) body. Example: @@ -360,13 +360,13 @@ class Sub(Proto): This feature was contributed by Shantanu (PR [16756](https://github.com/python/mypy/pull/16756)). -#### Stubgen Improvements +### Stubgen Improvements - Preserve empty tuple annotation (Ali Hamdan, PR [16907](https://github.com/python/mypy/pull/16907)) - Add support for PEP 570 positional-only parameters (Ali Hamdan, PR [16904](https://github.com/python/mypy/pull/16904)) - Replace obsolete typing aliases with builtin containers (Ali Hamdan, PR [16780](https://github.com/python/mypy/pull/16780)) - Fix generated dataclass `__init__` signature (Ali Hamdan, PR [16906](https://github.com/python/mypy/pull/16906)) -#### Mypyc Improvements +### Mypyc Improvements - Provide an easier way to define IR-to-IR transforms (Jukka Lehtosalo, PR [16998](https://github.com/python/mypy/pull/16998)) - Implement lowering pass and add primitives for int (in)equality (Jukka Lehtosalo, PR [17027](https://github.com/python/mypy/pull/17027)) @@ -377,15 +377,15 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) - Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) -#### Documentation Improvements +### Documentation Improvements - Import `TypedDict` from `typing` instead of `typing_extensions` (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) - Add missing `mutable-override` to section title (James Braza, PR [16886](https://github.com/python/mypy/pull/16886)) -#### Error Reporting Improvements +### Error Reporting Improvements - Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes - Fix incorrect inferred type when accessing descriptor on union type (Matthieu Devlin, PR [16604](https://github.com/python/mypy/pull/16604)) - Fix crash when expanding invalid `Unpack` in a `Callable` alias (Ali Hamdan, PR [17028](https://github.com/python/mypy/pull/17028)) - Fix false positive when string formatting with string enum (roberfi, PR [16555](https://github.com/python/mypy/pull/16555)) @@ -403,15 +403,15 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Experimental: Support TypedDict within `type[...]` (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) - Experimtental: Fix issue with TypedDict with optional keys in `type[...]` (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Mypy 1.10.1 +### Mypy 1.10.1 - Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: - Alex Waygood @@ -454,7 +454,7 @@ We’ve just uploaded mypy 1.9 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Breaking Changes +### Breaking Changes Because the version of typeshed we use in mypy 1.9 doesn't support 3.7, neither does mypy 1.9. (Jared Hance, PR [16883](https://github.com/python/mypy/pull/16883)) @@ -476,7 +476,7 @@ projects to use `--local-partial-types`, but it's not yet clear whether this is practical. The migration usually involves adding some explicit type annotations to module-level and class-level variables. -#### Basic Support for Type Parameter Defaults (PEP 696) +### Basic Support for Type Parameter Defaults (PEP 696) This release contains new experimental support for type parameter defaults ([PEP 696](https://peps.python.org/pep-0696)). Please try it @@ -506,7 +506,7 @@ reveal_type(Context().bot) reveal_type(Context[MyBot]().bot) ``` -#### Type-checking Improvements +### Type-checking Improvements * Fix missing type store for overloads (Marc Mueller, PR [16803](https://github.com/python/mypy/pull/16803)) * Fix `'WriteToConn' object has no attribute 'flush'` (Charlie Denton, PR [16801](https://github.com/python/mypy/pull/16801)) * Improve TypeAlias error messages (Marc Mueller, PR [16831](https://github.com/python/mypy/pull/16831)) @@ -519,11 +519,11 @@ reveal_type(Context[MyBot]().bot) * Add `alias` support to `field()` in `attrs` plugin (Nikita Sobolev, PR [16610](https://github.com/python/mypy/pull/16610)) * Improve attrs hashability detection (Tin Tvrtković, PR [16556](https://github.com/python/mypy/pull/16556)) -#### Performance Improvements +### Performance Improvements * Speed up finding function type variables (Jukka Lehtosalo, PR [16562](https://github.com/python/mypy/pull/16562)) -#### Documentation Updates +### Documentation Updates * Document supported values for `--enable-incomplete-feature` in "mypy --help" (Froger David, PR [16661](https://github.com/python/mypy/pull/16661)) * Update new type system discussion links (thomaswhaley, PR [16841](https://github.com/python/mypy/pull/16841)) @@ -533,7 +533,7 @@ reveal_type(Context[MyBot]().bot) * Fix numbering error (Stefanie Molin, PR [16838](https://github.com/python/mypy/pull/16838)) * Various documentation improvements (Shantanu, PR [16836](https://github.com/python/mypy/pull/16836)) -#### Stubtest Improvements +### Stubtest Improvements * Ignore private function/method parameters when they are missing from the stub (private parameter names start with a single underscore and have a default) (PR [16507](https://github.com/python/mypy/pull/16507)) * Ignore a new protocol dunder (Alex Waygood, PR [16895](https://github.com/python/mypy/pull/16895)) * Private parameters can be omitted (Sebastian Rittau, PR [16507](https://github.com/python/mypy/pull/16507)) @@ -541,13 +541,13 @@ reveal_type(Context[MyBot]().bot) * Adjust symbol table logic (Shantanu, PR [16823](https://github.com/python/mypy/pull/16823)) * Fix posisitional-only handling in overload resolution (Shantanu, PR [16750](https://github.com/python/mypy/pull/16750)) -#### Stubgen Improvements +### Stubgen Improvements * Fix crash on star unpack of TypeVarTuple (Ali Hamdan, PR [16869](https://github.com/python/mypy/pull/16869)) * Use PEP 604 unions everywhere (Ali Hamdan, PR [16519](https://github.com/python/mypy/pull/16519)) * Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) * Support type stub generation for `staticmethod` (WeilerMarcel, PR [14934](https://github.com/python/mypy/pull/14934)) -#### Acknowledgements +### Acknowledgements ​Thanks to all mypy contributors who contributed to this release: @@ -591,7 +591,7 @@ We’ve just uploaded mypy 1.8 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Type-checking Improvements +### Type-checking Improvements * Do not intersect types in isinstance checks if at least one is final (Christoph Tyralla, PR [16330](https://github.com/python/mypy/pull/16330)) * Detect that `@final` class without `__bool__` cannot have falsey instances (Ilya Priven, PR [16566](https://github.com/python/mypy/pull/16566)) * Do not allow `TypedDict` classes with extra keywords (Nikita Sobolev, PR [16438](https://github.com/python/mypy/pull/16438)) @@ -601,44 +601,44 @@ You can read the full documentation for this release on [Read the Docs](http://m * Allow type ignores of PEP 695 constructs (Shantanu, PR [16608](https://github.com/python/mypy/pull/16608)) * Enable `type_check_only` support for `TypedDict` and `NamedTuple` (Nikita Sobolev, PR [16469](https://github.com/python/mypy/pull/16469)) -#### Performance Improvements +### Performance Improvements * Add fast path to analyzing special form assignments (Jukka Lehtosalo, PR [16561](https://github.com/python/mypy/pull/16561)) -#### Improvements to Error Reporting +### Improvements to Error Reporting * Don't show documentation links for plugin error codes (Ivan Levkivskyi, PR [16383](https://github.com/python/mypy/pull/16383)) * Improve error messages for `super` checks and add more tests (Nikita Sobolev, PR [16393](https://github.com/python/mypy/pull/16393)) * Add error code for mutable covariant override (Ivan Levkivskyi, PR [16399](https://github.com/python/mypy/pull/16399)) -#### Stubgen Improvements +### Stubgen Improvements * Preserve simple defaults in function signatures (Ali Hamdan, PR [15355](https://github.com/python/mypy/pull/15355)) * Include `__all__` in output (Jelle Zijlstra, PR [16356](https://github.com/python/mypy/pull/16356)) * Fix stubgen regressions with pybind11 and mypy 1.7 (Chad Dombrova, PR [16504](https://github.com/python/mypy/pull/16504)) -#### Stubtest Improvements +### Stubtest Improvements * Improve handling of unrepresentable defaults (Jelle Zijlstra, PR [16433](https://github.com/python/mypy/pull/16433)) * Print more helpful errors if a function is missing from stub (Alex Waygood, PR [16517](https://github.com/python/mypy/pull/16517)) * Support `@type_check_only` decorator (Nikita Sobolev, PR [16422](https://github.com/python/mypy/pull/16422)) * Warn about missing `__del__` (Shantanu, PR [16456](https://github.com/python/mypy/pull/16456)) * Fix crashes with some uses of `final` and `deprecated` (Shantanu, PR [16457](https://github.com/python/mypy/pull/16457)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` (Alex Waygood, PR [16541](https://github.com/python/mypy/pull/16541)) * Fix crash on TypeGuard in `__call__` (Ivan Levkivskyi, PR [16516](https://github.com/python/mypy/pull/16516)) * Fix crash on invalid enum in method (Ivan Levkivskyi, PR [16511](https://github.com/python/mypy/pull/16511)) * Fix crash on unimported Any in TypedDict (Ivan Levkivskyi, PR [16510](https://github.com/python/mypy/pull/16510)) -#### Documentation Updates +### Documentation Updates * Update soft-error-limit default value to -1 (Sveinung Gundersen, PR [16542](https://github.com/python/mypy/pull/16542)) * Support Sphinx 7.x (Michael R. Crusoe, PR [16460](https://github.com/python/mypy/pull/16460)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Allow mypy to output a junit file with per-file results (Matthew Wright, PR [16388](https://github.com/python/mypy/pull/16388)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Acknowledgements +### Acknowledgements ​Thanks to all mypy contributors who contributed to this release: @@ -672,7 +672,7 @@ We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Using TypedDict for `**kwargs` Typing +### Using TypedDict for `**kwargs` Typing Mypy now has support for using `Unpack[...]` with a TypedDict type to annotate `**kwargs` arguments enabled by default. Example: @@ -702,7 +702,7 @@ Refer to [PEP 692](https://peps.python.org/pep-0692/) for more information. Note This was contributed by Ivan Levkivskyi back in 2022 (PR [13471](https://github.com/python/mypy/pull/13471)). -#### TypeVarTuple Support Enabled (Experimental) +### TypeVarTuple Support Enabled (Experimental) Mypy now has support for variadic generics (TypeVarTuple) enabled by default, as an experimental feature. Refer to [PEP 646](https://peps.python.org/pep-0646/) for the details. @@ -724,7 +724,7 @@ Changes included in this release: * Subtyping and inference of user-defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) -#### New Way of Installing Mypyc Dependencies +### New Way of Installing Mypyc Dependencies If you want to install package dependencies needed by mypyc (not just mypy), you should now install `mypy[mypyc]` instead of just `mypy`: @@ -736,13 +736,13 @@ Mypy has many more users than mypyc, so always installing mypyc dependencies wou This change was contributed by Shantanu (PR [16229](https://github.com/python/mypy/pull/16229)). -#### New Rules for Re-exports +### New Rules for Re-exports Mypy no longer considers an import such as `import a.b as b` as an explicit re-export. The old behavior was arguably inconsistent and surprising. This may impact some stub packages, such as older versions of `types-six`. You can change the import to `from a import b as b`, if treating the import as a re-export was intentional. This change was contributed by Anders Kaseorg (PR [14086](https://github.com/python/mypy/pull/14086)). -#### Improved Type Inference +### Improved Type Inference The new type inference algorithm that was recently introduced to mypy (but was not enabled by default) is now enabled by default. It improves type inference of calls to generic callables where an argument is also a generic callable, in particular. You can use `--old-type-inference` to disable the new behavior. @@ -750,7 +750,7 @@ The new algorithm can (rarely) produce different error messages, different error The new type inference algorithm was contributed by Ivan Levkivskyi. PR [16345](https://github.com/python/mypy/pull/16345) enabled it by default. -#### Narrowing Tuple Types Using len() +### Narrowing Tuple Types Using len() Mypy now can narrow tuple types using `len()` checks. Example: @@ -763,7 +763,7 @@ def f(t: tuple[int, int] | tuple[int, int, int]) -> None: This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). -#### More Precise Tuple Lengths (Experimental) +### More Precise Tuple Lengths (Experimental) Mypy supports experimental, more precise checking of tuple type lengths through `--enable-incomplete-feature=PreciseTupleTypes`. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#enabling-incomplete-experimental-features) for more information. @@ -771,13 +771,13 @@ More generally, we are planning to use `--enable-incomplete-feature` to introduc This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). -#### Mypy Changelog +### Mypy Changelog We now maintain a [changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) in the mypy Git repository. It mirrors the contents of [mypy release blog posts](https://mypy-lang.blogspot.com/). We will continue to also publish release blog posts. In the future, release blog posts will be created based on the changelog near a release date. This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull/16280)). -#### Mypy Daemon Improvements +### Mypy Daemon Improvements * Fix daemon crash caused by deleted submodule (Jukka Lehtosalo, PR [16370](https://github.com/python/mypy/pull/16370)) * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) @@ -789,7 +789,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) * Make sure all dmypy errors are shown (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) -#### Mypyc Improvements +### Mypyc Improvements * Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) * Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) @@ -797,7 +797,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix direct `__dict__` access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) * Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) -#### Improvements to Error Reporting +### Improvements to Error Reporting * Update starred expression error message to match CPython (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) * Fix error code of "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) @@ -807,14 +807,14 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) * Don't suggest stubs packages where the runtime package now ships with types (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) -#### Performance Improvements +### Performance Improvements * Speed up type argument checking (Jukka Lehtosalo, PR [16353](https://github.com/python/mypy/pull/16353)) * Add fast path for checking self types (Jukka Lehtosalo, PR [16352](https://github.com/python/mypy/pull/16352)) * Cache information about whether file is typeshed file (Jukka Lehtosalo, PR [16351](https://github.com/python/mypy/pull/16351)) * Skip expensive `repr()` in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) -#### Attrs and Dataclass Improvements +### Attrs and Dataclass Improvements * `dataclass.replace`: Allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) * `dataclass.replace`: Fall through to typeshed signature (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) @@ -823,7 +823,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * `attrs`, `dataclasses`: Don't enforce slots when base class doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) -#### Stubgen Improvements +### Stubgen Improvements * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) * Fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) @@ -831,7 +831,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) * Generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) -#### Fixes to Crashes +### Fixes to Crashes * Fix incremental mode crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) @@ -840,7 +840,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) * Fix `__post_init__()` internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) -#### Documentation Updates +### Documentation Updates * Make it easier to copy commands from README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) * Document and rename `[overload-overlap]` error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) @@ -848,7 +848,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Propagate narrowed types to lambda expressions (Ivan Levkivskyi, PR [16407](https://github.com/python/mypy/pull/16407)) * Avoid importing from `setuptools._distutils` (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) @@ -882,11 +882,11 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Make iterable logic more consistent (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) * Fix inference for properties with `__call__` (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -932,7 +932,7 @@ We’ve just uploaded mypy 1.6 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Introduce Error Subcodes for Import Errors +### Introduce Error Subcodes for Import Errors Mypy now uses the error code import-untyped if an import targets an installed library that doesn’t support static type checking, and no stub files are available. Other invalid imports produce the import-not-found error code. They both are subcodes of the import error code, which was previously used for both kinds of import-related errors. @@ -942,17 +942,17 @@ If you use \--warn-unused-ignore or \--strict, mypy will complain if you use \# This feature was contributed by Shantanu (PR [15840](https://github.com/python/mypy/pull/15840), PR [14740](https://github.com/python/mypy/pull/14740)). -#### Remove Support for Targeting Python 3.6 and Earlier +### Remove Support for Targeting Python 3.6 and Earlier Running mypy with \--python-version 3.6, for example, is no longer supported. Python 3.6 hasn’t been properly supported by mypy for some time now, and this makes it explicit. This was contributed by Nikita Sobolev (PR [15668](https://github.com/python/mypy/pull/15668)). -#### Selective Filtering of \--disallow-untyped-calls Targets +### Selective Filtering of \--disallow-untyped-calls Targets Using \--disallow-untyped-calls could be annoying when using libraries with missing type information, as mypy would generate many errors about code that uses the library. Now you can use \--untyped-calls-exclude=acme, for example, to disable these errors about calls targeting functions defined in the acme package. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#cmdoption-mypy-untyped-calls-exclude) for more information. This feature was contributed by Ivan Levkivskyi (PR [15845](https://github.com/python/mypy/pull/15845)). -#### Improved Type Inference between Callable Types +### Improved Type Inference between Callable Types Mypy now does a better job inferring type variables inside arguments of callable types. For example, this code fragment now type checks correctly: @@ -965,7 +965,7 @@ reveal_type(f(g)) # Callable[[str, int, int], None] This was contributed by Ivan Levkivskyi (PR [15910](https://github.com/python/mypy/pull/15910)). -#### Don’t Consider None and TypeVar to Overlap in Overloads +### Don’t Consider None and TypeVar to Overlap in Overloads Mypy now doesn’t consider an overload item with an argument type None to overlap with a type variable: @@ -981,7 +981,7 @@ Previously mypy would generate an error about the definition of f above. This is This feature was contributed by Ivan Levkivskyi (PR [15846](https://github.com/python/mypy/pull/15846)). -#### Improvements to \--new-type-inference +### Improvements to \--new-type-inference The experimental new type inference algorithm (polymorphic inference) introduced as an opt-in feature in mypy 1.5 has several improvements: @@ -993,7 +993,7 @@ The experimental new type inference algorithm (polymorphic inference) introduced **Note:** We are planning to enable \--new-type-inference by default in mypy 1.7. Please try this out and let us know if you encounter any issues. -#### ParamSpec Improvements +### ParamSpec Improvements * Support self-types containing ParamSpec (Ivan Levkivskyi, PR [15903](https://github.com/python/mypy/pull/15903)) * Allow “…” in Concatenate, and clean up ParamSpec literals (Ivan Levkivskyi, PR [15905](https://github.com/python/mypy/pull/15905)) @@ -1002,12 +1002,12 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Fix crash on invalid type variable with ParamSpec (Ivan Levkivskyi, PR [15953](https://github.com/python/mypy/pull/15953)) * Fix subtyping between ParamSpecs (Ivan Levkivskyi, PR [15892](https://github.com/python/mypy/pull/15892)) -#### Stubgen Improvements +### Stubgen Improvements * Add option to include docstrings with stubgen (chylek, PR [13284](https://github.com/python/mypy/pull/13284)) * Add required ... initializer to NamedTuple fields with default values (Nikita Sobolev, PR [15680](https://github.com/python/mypy/pull/15680)) -#### Stubtest Improvements +### Stubtest Improvements * Fix \_\_mypy-replace false positives (Alex Waygood, PR [15689](https://github.com/python/mypy/pull/15689)) * Fix edge case for bytes enum subclasses (Alex Waygood, PR [15943](https://github.com/python/mypy/pull/15943)) @@ -1015,14 +1015,14 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Fixes to new check for missing stdlib modules (Alex Waygood, PR [15960](https://github.com/python/mypy/pull/15960)) * Fix stubtest enum.Flag edge case (Alex Waygood, PR [15933](https://github.com/python/mypy/pull/15933)) -#### Documentation Improvements +### Documentation Improvements * Do not advertise to create your own assert\_never helper (Nikita Sobolev, PR [15947](https://github.com/python/mypy/pull/15947)) * Fix all the missing references found within the docs (Albert Tugushev, PR [15875](https://github.com/python/mypy/pull/15875)) * Document await-not-async error code (Shantanu, PR [15858](https://github.com/python/mypy/pull/15858)) * Improve documentation of disabling error codes (Shantanu, PR [15841](https://github.com/python/mypy/pull/15841)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Make unsupported PEP 695 features (introduced in Python 3.12) give a reasonable error message (Shantanu, PR [16013](https://github.com/python/mypy/pull/16013)) * Remove the \--py2 command-line argument (Marc Mueller, PR [15670](https://github.com/python/mypy/pull/15670)) @@ -1045,11 +1045,11 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Add tox.ini to mypy sdist (Marcel Telka, PR [15853](https://github.com/python/mypy/pull/15853)) * Fix mypyc regression with pretty (Shantanu, PR [16124](https://github.com/python/mypy/pull/16124)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=6a8d653a671925b0a3af61729ff8cf3f90c9c662+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to Max Murin, who did most of the release manager work for this release (I just did the final steps). @@ -1088,11 +1088,11 @@ We’ve just uploaded mypy 1.5 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Drop Support for Python 3.7 +### Drop Support for Python 3.7 Mypy no longer supports running with Python 3.7, which has reached end-of-life. This was contributed by Shantanu (PR [15566](https://github.com/python/mypy/pull/15566)). -#### Optional Check to Require Explicit @override +### Optional Check to Require Explicit @override If you enable the explicit-override error code, mypy will generate an error if a method override doesn’t use the @typing.override decorator (as discussed in [PEP 698](https://peps.python.org/pep-0698/#strict-enforcement-per-project)). This way mypy will detect accidentally introduced overrides. Example: @@ -1122,7 +1122,7 @@ The override decorator will be available in typing in Python 3.12, but you can a This feature was contributed by Marc Mueller(PR [15512](https://github.com/python/mypy/pull/15512)). -#### More Flexible TypedDict Creation and Update +### More Flexible TypedDict Creation and Update Mypy was previously overly strict when type checking TypedDict creation and update operations. Though these checks were often technically correct, they sometimes triggered for apparently valid code. These checks have now been relaxed by default. You can enable stricter checking by using the new \--extra-checks flag. @@ -1150,11 +1150,11 @@ a.update(b) # OK (previously an error) This feature was contributed by Ivan Levkivskyi (PR [15425](https://github.com/python/mypy/pull/15425)). -#### Deprecated Flag: \--strict-concatenate +### Deprecated Flag: \--strict-concatenate The behavior of \--strict-concatenate is now included in the new \--extra-checks flag, and the old flag is deprecated. -#### Optionally Show Links to Error Code Documentation +### Optionally Show Links to Error Code Documentation If you use \--show-error-code-links, mypy will add documentation links to (many) reported errors. The links are not shown for error messages that are sufficiently obvious, and they are shown once per error code only. @@ -1165,19 +1165,19 @@ a.py:1: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-var-annotated f ``` This was contributed by Ivan Levkivskyi (PR [15449](https://github.com/python/mypy/pull/15449)). -#### Consistently Avoid Type Checking Unreachable Code +### Consistently Avoid Type Checking Unreachable Code If a module top level has unreachable code, mypy won’t type check the unreachable statements. This is consistent with how functions behave. The behavior of \--warn-unreachable is also more consistent now. This was contributed by Ilya Priven (PR [15386](https://github.com/python/mypy/pull/15386)). -#### Experimental Improved Type Inference for Generic Functions +### Experimental Improved Type Inference for Generic Functions You can use \--new-type-inference to opt into an experimental new type inference algorithm. It fixes issues when calling a generic functions with an argument that is also a generic function, in particular. This current implementation is still incomplete, but we encourage trying it out and reporting bugs if you encounter regressions. We are planning to enable the new algorithm by default in a future mypy release. This feature was contributed by Ivan Levkivskyi (PR [15287](https://github.com/python/mypy/pull/15287)). -#### Partial Support for Python 3.12 +### Partial Support for Python 3.12 Mypy and mypyc now support running on recent Python 3.12 development versions. Not all new Python 3.12 features are supported, and we don’t ship compiled wheels for Python 3.12 yet. @@ -1193,7 +1193,7 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * mypyc: Don't use \_PyErr\_ChainExceptions on 3.12, since it's deprecated (Jukka Lehtosalo, PR [15468](https://github.com/python/mypy/pull/15468)) * mypyc: Add Python 3.12 feature macro (Jukka Lehtosalo, PR [15465](https://github.com/python/mypy/pull/15465)) -#### Improvements to Dataclasses +### Improvements to Dataclasses * Improve signature of dataclasses.replace (Ilya Priven, PR [14849](https://github.com/python/mypy/pull/14849)) * Fix dataclass/protocol crash on joining types (Ilya Priven, PR [15629](https://github.com/python/mypy/pull/15629)) @@ -1202,7 +1202,7 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Add `__slots__` attribute to dataclasses (Nikita Sobolev, PR [15649](https://github.com/python/mypy/pull/15649)) * Support better \_\_post\_init\_\_ method signature for dataclasses (Nikita Sobolev, PR [15503](https://github.com/python/mypy/pull/15503)) -#### Mypyc Improvements +### Mypyc Improvements * Support unsigned 8-bit native integer type: mypy\_extensions.u8 (Jukka Lehtosalo, PR [15564](https://github.com/python/mypy/pull/15564)) * Support signed 16-bit native integer type: mypy\_extensions.i16 (Jukka Lehtosalo, PR [15464](https://github.com/python/mypy/pull/15464)) @@ -1212,20 +1212,20 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Use C99 compound literals for undefined tuple values (Jukka Lehtosalo, PR [15453](https://github.com/python/mypy/pull/15453)) * Don't explicitly assign NULL values in setup functions (Logan Hunt, PR [15379](https://github.com/python/mypy/pull/15379)) -#### Stubgen Improvements +### Stubgen Improvements * Teach stubgen to work with complex and unary expressions (Nikita Sobolev, PR [15661](https://github.com/python/mypy/pull/15661)) * Support ParamSpec and TypeVarTuple (Ali Hamdan, PR [15626](https://github.com/python/mypy/pull/15626)) * Fix crash on non-str docstring (Ali Hamdan, PR [15623](https://github.com/python/mypy/pull/15623)) -#### Documentation Updates +### Documentation Updates * Add documentation for additional error codes (Ivan Levkivskyi, PR [15539](https://github.com/python/mypy/pull/15539)) * Improve documentation of type narrowing (Ilya Priven, PR [15652](https://github.com/python/mypy/pull/15652)) * Small improvements to protocol documentation (Shantanu, PR [15460](https://github.com/python/mypy/pull/15460)) * Remove confusing instance variable example in cheat sheet (Adel Atallah, PR [15441](https://github.com/python/mypy/pull/15441)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Constant fold additional unary and binary expressions (Richard Si, PR [15202](https://github.com/python/mypy/pull/15202)) * Exclude the same special attributes from Protocol as CPython (Kyle Benesch, PR [15490](https://github.com/python/mypy/pull/15490)) @@ -1245,11 +1245,11 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Fix self types in subclass methods without Self annotation (Ivan Levkivskyi, PR [15541](https://github.com/python/mypy/pull/15541)) * Check for abstract class objects in tuples (Nikita Sobolev, PR [15366](https://github.com/python/mypy/pull/15366)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=fc7d4722eaa54803926cee5730e1f784979c0531+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1285,13 +1285,13 @@ We’ve just uploaded mypy 1.4 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### The Override Decorator +### The Override Decorator Mypy can now ensure that when renaming a method, overrides are also renamed. You can explicitly mark a method as overriding a base class method by using the @typing.override decorator ([PEP 698](https://peps.python.org/pep-0698/)). If the method is then renamed in the base class while the method override is not, mypy will generate an error. The decorator will be available in typing in Python 3.12, but you can also use the backport from a recent version of `typing_extensions` on all supported Python versions. This feature was contributed byThomas M Kehrenberg (PR [14609](https://github.com/python/mypy/pull/14609)). -#### Propagating Type Narrowing to Nested Functions +### Propagating Type Narrowing to Nested Functions Previously, type narrowing was not propagated to nested functions because it would not be sound if the narrowed variable changed between the definition of the nested function and the call site. Mypy will now propagate the narrowed type if the variable is not assigned to after the definition of the nested function: @@ -1311,7 +1311,7 @@ This may generate some new errors because asserts that were previously necessary This was contributed by Jukka Lehtosalo (PR [15133](https://github.com/python/mypy/pull/15133)). -#### Narrowing Enum Values Using “==” +### Narrowing Enum Values Using “==” Mypy now allows narrowing enum types using the \== operator. Previously this was only supported when using the is operator. This makes exhaustiveness checking with enum types more usable, as the requirement to use the is operator was not very intuitive. In this example mypy can detect that the developer forgot to handle the value MyEnum.C in example @@ -1380,18 +1380,18 @@ def test_something() -> None: This feature was contributed by Shantanu (PR [11521](https://github.com/python/mypy/pull/11521)). -#### Performance Improvements +### Performance Improvements * Speed up simplification of large union types and also fix a recursive tuple crash (Shantanu, PR [15128](https://github.com/python/mypy/pull/15128)) * Speed up union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) * Don't type check most function bodies when type checking third-party library code, or generally when ignoring errors (Jukka Lehtosalo, PR [14150](https://github.com/python/mypy/pull/14150)) -#### Improvements to Plugins +### Improvements to Plugins * attrs.evolve: Support generics and unions (Ilya Konstantinov, PR [15050](https://github.com/python/mypy/pull/15050)) * Fix ctypes plugin (Alex Waygood) -#### Fixes to Crashes +### Fixes to Crashes * Fix a crash when function-scope recursive alias appears as upper bound (Ivan Levkivskyi, PR [15159](https://github.com/python/mypy/pull/15159)) * Fix crash on follow\_imports\_for\_stubs (Ivan Levkivskyi, PR [15407](https://github.com/python/mypy/pull/15407)) @@ -1405,7 +1405,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Fix crash on lambda in generic context with generic method in body (Ivan Levkivskyi, PR [15155](https://github.com/python/mypy/pull/15155)) * Fix recursive type alias crash in make\_simplified\_union (Ivan Levkivskyi, PR [15216](https://github.com/python/mypy/pull/15216)) -#### Improvements to Error Messages +### Improvements to Error Messages * Use lower-case built-in collection types such as list\[…\] instead of List\[…\] in errors when targeting Python 3.9+ (Max Murin, PR [15070](https://github.com/python/mypy/pull/15070)) * Use X | Y union syntax in error messages when targeting Python 3.10+ (Omar Silva, PR [15102](https://github.com/python/mypy/pull/15102)) @@ -1418,7 +1418,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Add explanation if argument type is incompatible because of an unsupported numbers type (Jukka Lehtosalo, PR [15137](https://github.com/python/mypy/pull/15137)) * Add more detail to 'signature incompatible with supertype' messages for non-callables (Ilya Priven, PR [15263](https://github.com/python/mypy/pull/15263)) -#### Documentation Updates +### Documentation Updates * Add \--local-partial-types note to dmypy docs (Alan Du, PR [15259](https://github.com/python/mypy/pull/15259)) * Update getting started docs for mypyc for Windows (Valentin Stanciu, PR [15233](https://github.com/python/mypy/pull/15233)) @@ -1426,13 +1426,13 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Clarify difference between disallow\_untyped\_defs and disallow\_incomplete\_defs (Ilya Priven, PR [15247](https://github.com/python/mypy/pull/15247)) * Use attrs and @attrs.define in documentation and tests (Ilya Priven, PR [15152](https://github.com/python/mypy/pull/15152)) -#### Mypyc Improvements +### Mypyc Improvements * Fix unexpected TypeError for certain variables with an inferred optional type (Richard Si, PR [15206](https://github.com/python/mypy/pull/15206)) * Inline math literals (Logan Hunt, PR [15324](https://github.com/python/mypy/pull/15324)) * Support unpacking mappings in dict display (Richard Si, PR [15203](https://github.com/python/mypy/pull/15203)) -#### Changes to Stubgen +### Changes to Stubgen * Do not remove Generic from base classes (Ali Hamdan, PR [15316](https://github.com/python/mypy/pull/15316)) * Support yield from statements (Ali Hamdan, PR [15271](https://github.com/python/mypy/pull/15271)) @@ -1442,7 +1442,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Make stubgen respect MYPY\_CACHE\_DIR (Henrik Bäärnhielm, PR [14722](https://github.com/python/mypy/pull/14722)) * Fixes and simplifications (Ali Hamdan, PR [15232](https://github.com/python/mypy/pull/15232)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Fix nested async functions when using TypeVar value restriction (Jukka Lehtosalo, PR [14705](https://github.com/python/mypy/pull/14705)) * Always allow returning Any from lambda (Ivan Levkivskyi, PR [15413](https://github.com/python/mypy/pull/15413)) @@ -1455,11 +1455,11 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Fix match subject ignoring redefinitions (Vincent Vanlaer, PR [15306](https://github.com/python/mypy/pull/15306)) * Support `__all__`.remove (Shantanu, PR [15279](https://github.com/python/mypy/pull/15279)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=877e06ad1cfd9fd9967c0b0340a86d0c23ea89ce+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1515,12 +1515,12 @@ Posted by Jared Hance You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Performance Improvements +### Performance Improvements * Improve performance of union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) * Add negative subtype caches (Ivan Levkivskyi, PR [14884](https://github.com/python/mypy/pull/14884)) -#### Stub Tooling Improvements +### Stub Tooling Improvements * Stubtest: Check that the stub is abstract if the runtime is, even when the stub is an overloaded method (Alex Waygood, PR [14955](https://github.com/python/mypy/pull/14955)) * Stubtest: Verify stub methods or properties are decorated with @final if they are decorated with @final at runtime (Alex Waygood, PR [14951](https://github.com/python/mypy/pull/14951)) @@ -1528,12 +1528,12 @@ You can read the full documentation for this release on [Read the Docs](http://m * Stubgen: Support @functools.cached\_property (Nikita Sobolev, PR [14981](https://github.com/python/mypy/pull/14981)) * Improvements to stubgenc (Chad Dombrova, PR [14564](https://github.com/python/mypy/pull/14564)) -#### Improvements to attrs +### Improvements to attrs * Add support for converters with TypeVars on generic attrs classes (Chad Dombrova, PR [14908](https://github.com/python/mypy/pull/14908)) * Fix attrs.evolve on bound TypeVar (Ilya Konstantinov, PR [15022](https://github.com/python/mypy/pull/15022)) -#### Documentation Updates +### Documentation Updates * Improve async documentation (Shantanu, PR [14973](https://github.com/python/mypy/pull/14973)) * Improvements to cheat sheet (Shantanu, PR [14972](https://github.com/python/mypy/pull/14972)) @@ -1545,26 +1545,26 @@ You can read the full documentation for this release on [Read the Docs](http://m * Fix alignment of cheat sheet example (Ondřej Cvacho, PR [15039](https://github.com/python/mypy/pull/15039)) * Fix error for callback protocol matching against callable type object (Shantanu, PR [15042](https://github.com/python/mypy/pull/15042)) -#### Error Reporting Improvements +### Error Reporting Improvements * Improve bytes formatting error (Shantanu, PR [14959](https://github.com/python/mypy/pull/14959)) -#### Mypyc Improvements +### Mypyc Improvements * Fix unions of bools and ints (Tomer Chachamu, PR [15066](https://github.com/python/mypy/pull/15066)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Fix narrowing union types that include Self with isinstance (Christoph Tyralla, PR [14923](https://github.com/python/mypy/pull/14923)) * Allow objects matching SupportsKeysAndGetItem to be unpacked (Bryan Forbes, PR [14990](https://github.com/python/mypy/pull/14990)) * Check type guard validity for staticmethods (EXPLOSION, PR [14953](https://github.com/python/mypy/pull/14953)) * Fix sys.platform when cross-compiling with emscripten (Ethan Smith, PR [14888](https://github.com/python/mypy/pull/14888)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=b0ed50e9392a23e52445b630a808153e0e256976+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1603,14 +1603,14 @@ We’ve just uploaded mypy 1.2 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Improvements to Dataclass Transforms +### Improvements to Dataclass Transforms * Support implicit default for "init" parameter in field specifiers (Wesley Collin Wright and Jukka Lehtosalo, PR [15010](https://github.com/python/mypy/pull/15010)) * Support descriptors in dataclass transform (Jukka Lehtosalo, PR [15006](https://github.com/python/mypy/pull/15006)) * Fix frozen\_default in incremental mode (Wesley Collin Wright) * Fix frozen behavior for base classes with direct metaclasses (Wesley Collin Wright, PR [14878](https://github.com/python/mypy/pull/14878)) -#### Mypyc: Native Floats +### Mypyc: Native Floats Mypyc now uses a native, unboxed representation for values of type float. Previously these were heap-allocated Python objects. Native floats are faster and use less memory. Code that uses floating-point operations heavily can be several times faster when using native floats. @@ -1649,7 +1649,7 @@ Related changes: * Document native floats and integers (Jukka Lehtosalo, PR [14927](https://github.com/python/mypy/pull/14927)) * Fixes to float to int conversion (Jukka Lehtosalo, PR [14936](https://github.com/python/mypy/pull/14936)) -#### Mypyc: Native Integers +### Mypyc: Native Integers Mypyc now supports signed 32-bit and 64-bit integer types in addition to the arbitrary-precision int type. You can use the types mypy\_extensions.i32 and mypy\_extensions.i64 to speed up code that uses integer operations heavily. @@ -1663,33 +1663,33 @@ def inc(x: i64) -> i64: Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/using_type_annotations.html#native-integer-types) for more information. This feature was contributed by Jukka Lehtosalo. -#### Other Mypyc Fixes and Improvements +### Other Mypyc Fixes and Improvements * Support iterating over a TypedDict (Richard Si, PR [14747](https://github.com/python/mypy/pull/14747)) * Faster coercions between different tuple types (Jukka Lehtosalo, PR [14899](https://github.com/python/mypy/pull/14899)) * Faster calls via type aliases (Jukka Lehtosalo, PR [14784](https://github.com/python/mypy/pull/14784)) * Faster classmethod calls via cls (Jukka Lehtosalo, PR [14789](https://github.com/python/mypy/pull/14789)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash on class-level import in protocol definition (Ivan Levkivskyi, PR [14926](https://github.com/python/mypy/pull/14926)) * Fix crash on single item union of alias (Ivan Levkivskyi, PR [14876](https://github.com/python/mypy/pull/14876)) * Fix crash on ParamSpec in incremental mode (Ivan Levkivskyi, PR [14885](https://github.com/python/mypy/pull/14885)) -#### Documentation Updates +### Documentation Updates * Update adopting \--strict documentation for 1.0 (Shantanu, PR [14865](https://github.com/python/mypy/pull/14865)) * Some minor documentation tweaks (Jukka Lehtosalo, PR [14847](https://github.com/python/mypy/pull/14847)) * Improve documentation of top level mypy: disable-error-code comment (Nikita Sobolev, PR [14810](https://github.com/python/mypy/pull/14810)) -#### Error Reporting Improvements +### Error Reporting Improvements * Add error code to `typing_extensions` suggestion (Shantanu, PR [14881](https://github.com/python/mypy/pull/14881)) * Add a separate error code for top-level await (Nikita Sobolev, PR [14801](https://github.com/python/mypy/pull/14801)) * Don’t suggest two obsolete stub packages (Jelle Zijlstra, PR [14842](https://github.com/python/mypy/pull/14842)) * Add suggestions for pandas-stubs and lxml-stubs (Shantanu, PR [14737](https://github.com/python/mypy/pull/14737)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Multiple inheritance considers callable objects as subtypes of functions (Christoph Tyralla, PR [14855](https://github.com/python/mypy/pull/14855)) * stubtest: Respect @final runtime decorator and enforce it in stubs (Nikita Sobolev, PR [14922](https://github.com/python/mypy/pull/14922)) @@ -1708,11 +1708,11 @@ Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/using_type_a * Improve “used before definition” checks when a local definition has the same name as a global definition (Stas Ilinskiy, PR [14517](https://github.com/python/mypy/pull/14517)) * Honor NoReturn as \_\_setitem\_\_ return type to mark unreachable code (sterliakov, PR [12572](https://github.com/python/mypy/pull/12572)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=a544b75320e97424d2d927605316383c755cdac0+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1748,13 +1748,13 @@ Posted by Jukka Lehtosalo You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Support for `dataclass_transform`` +### Support for `dataclass_transform`` This release adds full support for the dataclass\_transform decorator defined in [PEP 681](https://peps.python.org/pep-0681/#decorator-function-example). This allows decorators, base classes, and metaclasses that generate a \_\_init\_\_ method or other methods based on the properties of that class (similar to dataclasses) to have those methods recognized by mypy. This was contributed by Wesley Collin Wright. -#### Dedicated Error Code for Method Assignments +### Dedicated Error Code for Method Assignments Mypy can’t safely check all assignments to methods (a form of monkey patching), so mypy generates an error by default. To make it easier to ignore this error, mypy now uses the new error code method-assign for this. By disabling this error code in a file or globally, mypy will no longer complain about assignments to methods if the signatures are compatible. @@ -1762,16 +1762,16 @@ Mypy also supports the old error code assignment for these assignments to preven This was contributed by Ivan Levkivskyi (PR [14570](https://github.com/python/mypy/pull/14570)). -#### Fixes to Crashes +### Fixes to Crashes * Fix a crash on walrus in comprehension at class scope (Ivan Levkivskyi, PR [14556](https://github.com/python/mypy/pull/14556)) * Fix crash related to value-constrained TypeVar (Shantanu, PR [14642](https://github.com/python/mypy/pull/14642)) -#### Fixes to Cache Corruption +### Fixes to Cache Corruption * Fix generic TypedDict/NamedTuple caching (Ivan Levkivskyi, PR [14675](https://github.com/python/mypy/pull/14675)) -#### Mypyc Fixes and Improvements +### Mypyc Fixes and Improvements * Raise "non-trait base must be first..." error less frequently (Richard Si, PR [14468](https://github.com/python/mypy/pull/14468)) * Generate faster code for bool comparisons and arithmetic (Jukka Lehtosalo, PR [14489](https://github.com/python/mypy/pull/14489)) @@ -1782,12 +1782,12 @@ This was contributed by Ivan Levkivskyi (PR [14570](https://github.com/python/my * Fix crash on star unpacking to underscore (Ivan Levkivskyi, PR [14624](https://github.com/python/mypy/pull/14624)) * Fix iterating over a union of dicts (Richard Si, PR [14713](https://github.com/python/mypy/pull/14713)) -#### Fixes to Detecting Undefined Names (used-before-def) +### Fixes to Detecting Undefined Names (used-before-def) * Correctly handle walrus operator (Stas Ilinskiy, PR [14646](https://github.com/python/mypy/pull/14646)) * Handle walrus declaration in match subject correctly (Stas Ilinskiy, PR [14665](https://github.com/python/mypy/pull/14665)) -#### Stubgen Improvements +### Stubgen Improvements Stubgen is a tool for automatically generating draft stubs for libraries. @@ -1795,14 +1795,14 @@ Stubgen is a tool for automatically generating draft stubs for libraries. * Fix crash with PEP 604 union in type variable bound (Shantanu, PR [14557](https://github.com/python/mypy/pull/14557)) * Preserve PEP 604 unions in generated .pyi files (hamdanal, PR [14601](https://github.com/python/mypy/pull/14601)) -#### Stubtest Improvements +### Stubtest Improvements Stubtest is a tool for testing that stubs conform to the implementations. * Update message format so that it’s easier to go to error location (Avasam, PR [14437](https://github.com/python/mypy/pull/14437)) * Handle name-mangling edge cases better (Alex Waygood, PR [14596](https://github.com/python/mypy/pull/14596)) -#### Changes to Error Reporting and Messages +### Changes to Error Reporting and Messages * Add new TypedDict error code typeddict-unknown-key (JoaquimEsteves, PR [14225](https://github.com/python/mypy/pull/14225)) * Give arguments a more reasonable location in error messages (Max Murin, PR [14562](https://github.com/python/mypy/pull/14562)) @@ -1814,7 +1814,7 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Adjust inconsistent dataclasses plugin error messages (Wesley Collin Wright, PR [14637](https://github.com/python/mypy/pull/14637)) * Consolidate literal bool argument error messages (Wesley Collin Wright, PR [14693](https://github.com/python/mypy/pull/14693)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Check that type guards accept a positional argument (EXPLOSION, PR [14238](https://github.com/python/mypy/pull/14238)) * Fix bug with in operator used with a union of Container and Iterable (Max Murin, PR [14384](https://github.com/python/mypy/pull/14384)) @@ -1822,11 +1822,11 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Allow overlapping comparisons between bytes-like types (Shantanu, PR [14658](https://github.com/python/mypy/pull/14658)) * Fix mypy daemon documentation link in README (Ivan Levkivskyi, PR [14644](https://github.com/python/mypy/pull/14644)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=5ebf892d0710a6e87925b8d138dfa597e7bb11cc+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1868,7 +1868,7 @@ We’ve just uploaded mypy 1.0 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### New Release Versioning Scheme +### New Release Versioning Scheme Now that mypy reached 1.0, we’ll switch to a new versioning scheme. Mypy version numbers will be of form x.y.z. @@ -1884,7 +1884,7 @@ Any significant backward incompatible change must be announced in the blog post See [”Release Process” in the mypy wiki](https://github.com/python/mypy/wiki/Release-Process) for more details and for the most up-to-date version of the versioning scheme. -#### Performance Improvements +### Performance Improvements Mypy 1.0 is up to 40% faster than mypy 0.991 when type checking the Dropbox internal codebase. We also set up a daily job to measure the performance of the most recent development version of mypy to make it easier to track changes in performance. @@ -1911,7 +1911,7 @@ Many optimizations contributed to this improvement: * Speed up freshening type variables (Jukka Lehtosalo, PR [14323](https://github.com/python/mypy/pull/14323)) * Optimize implementation of TypedDict types for \*\*kwds (Jukka Lehtosalo, PR [14316](https://github.com/python/mypy/pull/14316)) -#### Warn About Variables Used Before Definition +### Warn About Variables Used Before Definition Mypy will now generate an error if you use a variable before it’s defined. This feature is enabled by default. By default mypy reports an error when it infers that a variable is always undefined. ```python @@ -1920,7 +1920,7 @@ x = 0 ``` This feature was contributed by Stas Ilinskiy. -#### Detect Possibly Undefined Variables (Experimental) +### Detect Possibly Undefined Variables (Experimental) A new experimental possibly-undefined error code is now available that will detect variables that may be undefined: ```python @@ -1932,7 +1932,7 @@ The error code is disabled be default, since it can generate false positives. This feature was contributed by Stas Ilinskiy. -#### Support the “Self” Type +### Support the “Self” Type There is now a simpler syntax for declaring [generic self types](https://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self) introduced in [PEP 673](https://peps.python.org/pep-0673/): the Self type. You no longer have to define a type variable to use “self types”, and you can use them with attributes. Example from mypy documentation: ```python @@ -1958,7 +1958,7 @@ The feature was introduced in Python 3.11. In earlier Python versions a backport This was contributed by Ivan Levkivskyi (PR [14041](https://github.com/python/mypy/pull/14041)). -#### Support ParamSpec in Type Aliases +### Support ParamSpec in Type Aliases ParamSpec and Concatenate can now be used in type aliases. Example: ```python @@ -1972,11 +1972,11 @@ def f(c: A[int, str]) -> None: ``` This feature was contributed by Ivan Levkivskyi (PR [14159](https://github.com/python/mypy/pull/14159)). -#### ParamSpec and Generic Self Types No Longer Experimental +### ParamSpec and Generic Self Types No Longer Experimental Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and generic self types are no longer considered experimental. -#### Miscellaneous New Features +### Miscellaneous New Features * Minimal, partial implementation of dataclass\_transform ([PEP 681](https://peps.python.org/pep-0681/)) (Wesley Collin Wright, PR [14523](https://github.com/python/mypy/pull/14523)) * Add basic support for `typing_extensions`.TypeVar (Marc Mueller, PR [14313](https://github.com/python/mypy/pull/14313)) @@ -1989,7 +1989,7 @@ Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and * Generate error for class attribute access if attribute is defined with `__slots__` (Harrison McCarty, PR [14125](https://github.com/python/mypy/pull/14125)) * Support additional attributes in callback protocols (Ivan Levkivskyi, PR [14084](https://github.com/python/mypy/pull/14084)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash on prefixed ParamSpec with forward reference (Ivan Levkivskyi, PR [14569](https://github.com/python/mypy/pull/14569)) * Fix internal crash when resolving the same partial type twice (Shantanu, PR [14552](https://github.com/python/mypy/pull/14552)) @@ -2009,19 +2009,19 @@ Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and * Fix crash with enums (Michael Lee, PR [14021](https://github.com/python/mypy/pull/14021)) * Fix crash with malformed TypedDicts and disllow-any-expr (Michael Lee, PR [13963](https://github.com/python/mypy/pull/13963)) -#### Error Reporting Improvements +### Error Reporting Improvements * More helpful error for missing self (Shantanu, PR [14386](https://github.com/python/mypy/pull/14386)) * Add error-code truthy-iterable (Marc Mueller, PR [13762](https://github.com/python/mypy/pull/13762)) * Fix pluralization in error messages (KotlinIsland, PR [14411](https://github.com/python/mypy/pull/14411)) -#### Mypyc: Support Match Statement +### Mypyc: Support Match Statement Mypyc can now compile Python 3.10 match statements. This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/13953)). -#### Other Mypyc Fixes and Improvements +### Other Mypyc Fixes and Improvements * Optimize int(x)/float(x)/complex(x) on instances of native classes (Richard Si, PR [14450](https://github.com/python/mypy/pull/14450)) * Always emit warnings (Richard Si, PR [14451](https://github.com/python/mypy/pull/14451)) @@ -2037,7 +2037,7 @@ This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/ * Allow use of enum.Enum (Shantanu, PR [13995](https://github.com/python/mypy/pull/13995)) * Fix compiling on Arch Linux (dosisod, PR [13978](https://github.com/python/mypy/pull/13978)) -#### Documentation Improvements +### Documentation Improvements * Various documentation and error message tweaks (Jukka Lehtosalo, PR [14574](https://github.com/python/mypy/pull/14574)) * Improve Generics documentation (Shantanu, PR [14587](https://github.com/python/mypy/pull/14587)) @@ -2057,7 +2057,7 @@ This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/ * Flycheck-mypy is deprecated, since its functionality was merged to Flycheck (Ivan Levkivskyi, PR [14247](https://github.com/python/mypy/pull/14247)) * Update code example in "Declaring decorators" (ChristianWitzler, PR [14131](https://github.com/python/mypy/pull/14131)) -#### Stubtest Improvements +### Stubtest Improvements Stubtest is a tool for testing that stubs conform to the implementations. @@ -2068,13 +2068,13 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Add \_\_warningregistry\_\_ to the list of ignored module dunders (Nikita Sobolev, PR [14218](https://github.com/python/mypy/pull/14218)) * If a default is present in the stub, check that it is correct (Jelle Zijlstra, PR [14085](https://github.com/python/mypy/pull/14085)) -#### Stubgen Improvements +### Stubgen Improvements Stubgen is a tool for automatically generating draft stubs for libraries. * Treat dlls as C modules (Shantanu, PR [14503](https://github.com/python/mypy/pull/14503)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Update stub suggestions based on recent typeshed changes (Alex Waygood, PR [14265](https://github.com/python/mypy/pull/14265)) * Fix attrs protocol check with cache (Marc Mueller, PR [14558](https://github.com/python/mypy/pull/14558)) @@ -2113,11 +2113,11 @@ Stubgen is a tool for automatically generating draft stubs for libraries. * Improve handling of redefinitions through imports (Shantanu, PR [13969](https://github.com/python/mypy/pull/13969)) * Preserve (some) implicitly exported types (Shantanu, PR [13967](https://github.com/python/mypy/pull/13967)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=ea0ae2155e8a04c9837903c3aff8dd5ad5f36ebc+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index a3504b07824d..dc502d121ffc 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,2 +1,3 @@ sphinx>=5.1.0 furo>=2022.3.4 +myst-parser>=4.0.0 diff --git a/docs/source/changelog.md b/docs/source/changelog.md new file mode 100644 index 000000000000..a490ada727a6 --- /dev/null +++ b/docs/source/changelog.md @@ -0,0 +1,3 @@ + +```{include} ../../CHANGELOG.md +``` diff --git a/docs/source/conf.py b/docs/source/conf.py index 5934c7474536..f8faa03a09b2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder"] +extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder", "myst_parser"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/docs/source/index.rst b/docs/source/index.rst index c9dc6bc1f8c9..de3286d58ace 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -103,6 +103,7 @@ Contents error_code_list2 additional_features faq + changelog .. toctree:: :hidden: From 77919cfdd11340a7965fb3c850b5cd19bb0e289a Mon Sep 17 00:00:00 2001 From: Soubhik Kumar Mitra <59209034+x612skm@users.noreply.github.com> Date: Mon, 16 Sep 2024 04:15:22 +0530 Subject: [PATCH 0741/1617] Add missing lines-covered and lines-valid attributes (#17738) This PR resolves an issue where the Cobertura XML report generated by MyPy was missing the lines-covered and lines-valid attributes in the element. Changes made: - Added the lines-covered and lines-valid attributes to the element in the Cobertura XML report. - Updated the CoberturaReportSuite test suite to validate that these attributes are correctly included in the generated XML. Fixes #17689 --- mypy/report.py | 2 ++ test-data/unit/reports.test | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/report.py b/mypy/report.py index 764cfec7799a..73942b6c5ae3 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -689,6 +689,8 @@ def on_finish(self) -> None: self.root_package.covered_lines, self.root_package.total_lines ) self.root.attrib["branch-rate"] = "0" + self.root.attrib["lines-covered"] = str(self.root_package.covered_lines) + self.root.attrib["lines-valid"] = str(self.root_package.total_lines) sources = etree.SubElement(self.root, "sources") source_element = etree.SubElement(sources, "source") source_element.text = os.getcwd() diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 81e24240af2d..6e0fdba8aaa3 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -27,7 +27,7 @@ def bar() -> str: def untyped_function(): return 42 [outfile build/cobertura.xml] - + $PWD @@ -81,7 +81,7 @@ def foo(a: int) -> MyDict: return {"a": a} md: MyDict = MyDict(**foo(42)) [outfile build/cobertura.xml] - + $PWD From 5c38427a53f409bae068c314e119fecc77730d76 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 16 Sep 2024 01:46:08 +0300 Subject: [PATCH 0742/1617] Check for `truthy-bool` in `not ...` unary expressions (#17773) Closes https://github.com/python/mypy/issues/17769 --- mypy/checker.py | 13 +++++++++++-- mypy/checkexpr.py | 1 + test-data/unit/check-errorcodes.test | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index db65660bbfbd..9c4f4ce88690 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5651,7 +5651,16 @@ def _is_truthy_type(self, t: ProperType) -> bool: ) ) - def _check_for_truthy_type(self, t: Type, expr: Expression) -> None: + def check_for_truthy_type(self, t: Type, expr: Expression) -> None: + """ + Check if a type can have a truthy value. + + Used in checks like:: + + if x: # <--- + + not x # <--- + """ if not state.strict_optional: return # if everything can be None, all bets are off @@ -6145,7 +6154,7 @@ def has_no_custom_eq_checks(t: Type) -> bool: if in_boolean_context: # We don't check `:=` values in expressions like `(a := A())`, # because they produce two error messages. - self._check_for_truthy_type(original_vartype, node) + self.check_for_truthy_type(original_vartype, node) vartype = try_expanding_sum_type_to_union(original_vartype, "builtins.bool") if_type = true_only(vartype) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9dee743ad406..22595c85e702 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4256,6 +4256,7 @@ def visit_unary_expr(self, e: UnaryExpr) -> Type: op = e.op if op == "not": result: Type = self.bool_type() + self.chk.check_for_truthy_type(operand_type, e.expr) else: method = operators.unary_op_methods[op] result, method_type = self.check_method_call_by_name(method, operand_type, [], [], e) diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index c4d72388fba9..cca13347dd86 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -846,34 +846,48 @@ foo = Foo() if foo: # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass +not foo # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + zero = 0 if zero: pass +not zero + false = False if false: pass +not false + null = None if null: pass +not null + s = '' if s: pass +not s + good_union: Union[str, int] = 5 if good_union: pass if not good_union: pass +not good_union + bad_union: Union[Foo, Bar] = Foo() if bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass if not bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass +not bad_union # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + # 'object' is special and is treated as potentially falsy obj: object = Foo() if obj: @@ -881,18 +895,26 @@ if obj: if not obj: pass +not obj + lst: List[int] = [] if lst: pass +not lst + a: Any if a: pass +not a + any_or_object: Union[object, Any] if any_or_object: pass +not any_or_object + if (my_foo := Foo()): # E: "__main__.my_foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass @@ -909,6 +931,8 @@ if not f: # E: Function "f" could always be true in boolean context [truthy-fu pass conditional_result = 'foo' if f else 'bar' # E: Function "f" could always be true in boolean context [truthy-function] +not f # E: Function "f" could always be true in boolean context [truthy-function] + [case testTruthyIterable] # flags: --enable-error-code truthy-iterable from typing import Iterable @@ -916,6 +940,8 @@ def func(var: Iterable[str]) -> None: if var: # E: "var" has type "Iterable[str]" which can always be true in boolean context. Consider using "Collection[str]" instead. [truthy-iterable] ... + not var # E: "var" has type "Iterable[str]" which can always be true in boolean context. Consider using "Collection[str]" instead. [truthy-iterable] + [case testNoOverloadImplementation] from typing import overload From f68f76d7ab7a5a6277a47fbadd6012ddd16ea731 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Sep 2024 12:22:21 +0100 Subject: [PATCH 0743/1617] [PEP 695] Fix nested generic classes (#17776) There was confusion about the fullnames of type variables in nested generic classes. A type variable could be defined internally as `m.OuterClass.T`, but it was sometimes accessed as `m.T`. The root cause was that the semantic analyzer didn't initialize the attribute that refers to the enclosing class consistently. Fixes #17596. Fixes #17630. --- mypy/semanal.py | 5 ++ test-data/unit/check-python312.test | 66 ++++++++++++++++++++++ test-data/unit/fine-grained-python312.test | 20 +++++++ 3 files changed, 91 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 782985e3fbab..522b4abdd5e9 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -824,6 +824,11 @@ def file_context( self.num_incomplete_refs = 0 if active_type: + enclosing_fullname = active_type.fullname.rsplit(".", 1)[0] + if "." in enclosing_fullname: + enclosing_node = self.lookup_fully_qualified_or_none(enclosing_fullname) + if enclosing_node and isinstance(enclosing_node.node, TypeInfo): + self._type = enclosing_node.node self.push_type_args(active_type.defn.type_args, active_type.defn) self.incomplete_type_stack.append(False) scope.enter_class(active_type) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index a3f4c87120cd..89ced8be2c6f 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1713,3 +1713,69 @@ type XNested = (1 + (yield 1)) # E: Yield expression cannot be used within a ty type YNested = (1 + (yield from [])) # E: Yield expression cannot be used within a type alias type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a type alias type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias + +[case testPEP695NestedGenericClass1] +# flags: --enable-incomplete-feature=NewGenericSyntax +class C[T]: + def f(self) -> T: ... + +class A: + class B[Q]: + def __init__(self, a: Q) -> None: + self.a = a + + def f(self) -> Q: + return self.a + + def g(self, x: Q) -> None: ... + + b: B[str] + +x: A.B[int] +x.g("x") # E: Argument 1 to "g" of "B" has incompatible type "str"; expected "int" +reveal_type(x.a) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "__main__.A.B[builtins.int]" +reveal_type(A.b) # N: Revealed type is "__main__.A.B[builtins.str]" + +[case testPEP695NestedGenericClass2] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + def m(self) -> None: + class B[T]: + def f(self) -> T: ... + x: B[int] + reveal_type(x.f()) # N: Revealed type is "builtins.int" + self.a = B[str]() + +reveal_type(A().a) # N: Revealed type is "__main__.B@4[builtins.str]" +reveal_type(A().a.f()) # N: Revealed type is "builtins.str" + +[case testPEP695NestedGenericClass3] +# flags: --enable-incomplete-feature=NewGenericSyntax +class C[T]: + def f(self) -> T: ... + class D[S]: + x: T # E: Name "T" is not defined + def g(self) -> S: ... + +a: C[int] +reveal_type(a.f()) # N: Revealed type is "builtins.int" +b: C.D[str] +reveal_type(b.g()) # N: Revealed type is "builtins.str" + +class E[T]: + class F[T]: # E: "T" already defined as a type parameter + x: T + +c: E.F[int] + +[case testPEP695NestedGenericClass4] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class B[T]: + def __get__(self, instance: A, owner: type[A]) -> T: + return None # E: Incompatible return value type (got "None", expected "T") + f = B[int]() + +a = A() +v = a.f diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 3970c8cacfbf..80e4e4ca3bd8 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -80,3 +80,23 @@ from builtins import tuple as B == main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]") + +[case testPEP695NestedGenericClassMethodUpdated] +# flags: --enable-incomplete-feature=NewGenericSyntax +from a import f + +class C: + class D[T]: + x: T + def m(self) -> T: + f() + return self.x + +[file a.py] +def f() -> None: pass + +[file a.py.2] +def f(x: int) -> None: pass +[out] +== +main:8: error: Missing positional argument "x" in call to "f" From a47f301fc8f3fb71b0a4b4998c74f9a5b326db08 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Sep 2024 12:31:29 +0100 Subject: [PATCH 0744/1617] [PEP 695] Support Annotated[...] in new-style type aliases (#17777) The rvalue expression isn't semantically analyzed, so we can't rely on the `fullname` attribute to check if there is a reference to `Annotated`. Instead, use a lookup function provided by the caller to determine the fullname. Error reporting in the second argument to `Annotated` is still inconsistent, but this seems lower priority. I'll create a follow-up issue about (or update an existing issue if one exists). Fixes #17751. --- mypy/exprtotype.py | 39 ++++++++++++++++++++--------- mypy/semanal.py | 4 ++- test-data/unit/check-python312.test | 22 ++++++++++++++++ 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index 92316d11926d..c7df851668be 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -2,12 +2,15 @@ from __future__ import annotations +from typing import Callable + from mypy.fastparse import parse_type_string from mypy.nodes import ( MISSING_FALLBACK, BytesExpr, CallExpr, ComplexExpr, + Context, DictExpr, EllipsisExpr, Expression, @@ -21,6 +24,7 @@ RefExpr, StarExpr, StrExpr, + SymbolTableNode, TupleExpr, UnaryExpr, get_member_expr_fullname, @@ -63,12 +67,16 @@ def expr_to_unanalyzed_type( allow_new_syntax: bool = False, _parent: Expression | None = None, allow_unpack: bool = False, + lookup_qualified: Callable[[str, Context], SymbolTableNode | None] | None = None, ) -> ProperType: """Translate an expression to the corresponding type. The result is not semantically analyzed. It can be UnboundType or TypeList. Raise TypeTranslationError if the expression cannot represent a type. + If lookup_qualified is not provided, the expression is expected to be semantically + analyzed. + If allow_new_syntax is True, allow all type syntax independent of the target Python version (used in stubs). @@ -101,19 +109,26 @@ def expr_to_unanalyzed_type( else: args = [expr.index] - if isinstance(expr.base, RefExpr) and expr.base.fullname in ANNOTATED_TYPE_NAMES: - # TODO: this is not the optimal solution as we are basically getting rid - # of the Annotation definition and only returning the type information, - # losing all the annotations. + if isinstance(expr.base, RefExpr): + # Check if the type is Annotated[...]. For this we need the fullname, + # which must be looked up if the expression hasn't been semantically analyzed. + base_fullname = None + if lookup_qualified is not None: + sym = lookup_qualified(base.name, expr) + if sym and sym.node: + base_fullname = sym.node.fullname + else: + base_fullname = expr.base.fullname - return expr_to_unanalyzed_type(args[0], options, allow_new_syntax, expr) - else: - base.args = tuple( - expr_to_unanalyzed_type( - arg, options, allow_new_syntax, expr, allow_unpack=True - ) - for arg in args - ) + if base_fullname is not None and base_fullname in ANNOTATED_TYPE_NAMES: + # TODO: this is not the optimal solution as we are basically getting rid + # of the Annotation definition and only returning the type information, + # losing all the annotations. + return expr_to_unanalyzed_type(args[0], options, allow_new_syntax, expr) + base.args = tuple( + expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr, allow_unpack=True) + for arg in args + ) if not base.args: base.empty_tuple_index = True return base diff --git a/mypy/semanal.py b/mypy/semanal.py index 522b4abdd5e9..27d2a3abf93f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3749,7 +3749,9 @@ def analyze_alias( dynamic = bool(self.function_stack and self.function_stack[-1].is_dynamic()) global_scope = not self.type and not self.function_stack try: - typ = expr_to_unanalyzed_type(rvalue, self.options, self.is_stub_file) + typ = expr_to_unanalyzed_type( + rvalue, self.options, self.is_stub_file, lookup_qualified=self.lookup_qualified + ) except TypeTranslationError: self.fail( "Invalid type alias: expression is not a valid type", rvalue, code=codes.VALID_TYPE diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 89ced8be2c6f..0b3055212d20 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1714,6 +1714,28 @@ type YNested = (1 + (yield from [])) # E: Yield expression cannot be used within type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a type alias type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias +[case testPEP695TypeAliasAndAnnotated] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing_extensions import Annotated, Annotated as _Annotated +import typing_extensions as t + +def ann(*args): ... + +type A = Annotated[int, ann()] +type B = Annotated[int | str, ann((1, 2))] +type C = _Annotated[int, ann()] +type D = t.Annotated[str, ann()] + +x: A +y: B +z: C +zz: D +reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" +reveal_type(z) # N: Revealed type is "builtins.int" +reveal_type(zz) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + [case testPEP695NestedGenericClass1] # flags: --enable-incomplete-feature=NewGenericSyntax class C[T]: From fce14a0284b2c32d373cee57c9ed06f764769d93 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Sep 2024 14:42:11 +0100 Subject: [PATCH 0745/1617] [PEP 695] Allow covariance with attribute that has "_" name prefix (#17782) Fix this conformance test: ``` class ShouldBeCovariant5[T]: def __init__(self, x: T) -> None: self._x = x @property def x(self) -> T: return self._x vo5_1: ShouldBeCovariant5[float] = ShouldBeCovariant5[int](1) # OK vo5_2: ShouldBeCovariant5[int] = ShouldBeCovariant5[float](1) # E ``` My fix is to treat such attributes as not settable when inferring variance. Link: https://github.com/python/typing/blob/main/conformance/tests/generics_variance_inference.py#L79 --- mypy/subtypes.py | 22 +++++++--- test-data/unit/check-python312.test | 63 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 649cbae4c831..6e2366c4e0df 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -2006,16 +2006,22 @@ def infer_variance(info: TypeInfo, i: int) -> bool: for member in all_non_object_members(info): if member in ("__init__", "__new__"): continue - node = info[member].node - if isinstance(node, Var) and node.type is None: - tv.variance = VARIANCE_NOT_READY - return False + if isinstance(self_type, TupleType): self_type = mypy.typeops.tuple_fallback(self_type) - flags = get_member_flags(member, self_type) - typ = find_member(member, self_type, self_type) settable = IS_SETTABLE in flags + + node = info[member].node + if isinstance(node, Var): + if node.type is None: + tv.variance = VARIANCE_NOT_READY + return False + if has_underscore_prefix(member): + # Special case to avoid false positives (and to pass conformance tests) + settable = False + + typ = find_member(member, self_type, self_type) if typ: typ2 = expand_type(typ, {tvar.id: object_type}) if not is_subtype(typ, typ2): @@ -2036,6 +2042,10 @@ def infer_variance(info: TypeInfo, i: int) -> bool: return True +def has_underscore_prefix(name: str) -> bool: + return name.startswith("_") and not (name.startswith("__") and name.endswith("__")) + + def infer_class_variances(info: TypeInfo) -> bool: if not info.defn.type_args: return True diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 0b3055212d20..9dc52d2c07b0 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -342,6 +342,69 @@ class Invariant[T]: reveal_type(c(a1, a2)) # N: Revealed type is "Never" +[case testPEP695InferVarianceUnderscorePrefix] +# flags: --enable-incomplete-feature=NewGenericSyntax + +class Covariant1[T]: + def __init__(self, x: T) -> None: + self._x = x + + @property + def x(self) -> T: + return self._x + +co1_1: Covariant1[float] = Covariant1[int](1) +co1_2: Covariant1[int] = Covariant1[float](1) # E: Incompatible types in assignment (expression has type "Covariant1[float]", variable has type "Covariant1[int]") + +class Covariant2[T]: + def __init__(self, x: T) -> None: + self.__foo_bar = x + + @property + def x(self) -> T: + return self.__foo_bar + +co2_1: Covariant2[float] = Covariant2[int](1) +co2_2: Covariant2[int] = Covariant2[float](1) # E: Incompatible types in assignment (expression has type "Covariant2[float]", variable has type "Covariant2[int]") + +class Invariant1[T]: + def __init__(self, x: T) -> None: + self._x = x + + # Methods behave differently from attributes + def _f(self, x: T) -> None: ... + + @property + def x(self) -> T: + return self._x + +inv1_1: Invariant1[float] = Invariant1[int](1) # E: Incompatible types in assignment (expression has type "Invariant1[int]", variable has type "Invariant1[float]") +inv1_2: Invariant1[int] = Invariant1[float](1) # E: Incompatible types in assignment (expression has type "Invariant1[float]", variable has type "Invariant1[int]") + +class Invariant2[T]: + def __init__(self, x: T) -> None: + # Dunders are special + self.__x__ = x + + @property + def x(self) -> T: + return self.__x__ + +inv2_1: Invariant2[float] = Invariant2[int](1) # E: Incompatible types in assignment (expression has type "Invariant2[int]", variable has type "Invariant2[float]") +inv2_2: Invariant2[int] = Invariant2[float](1) # E: Incompatible types in assignment (expression has type "Invariant2[float]", variable has type "Invariant2[int]") + +class Invariant3[T]: + def __init__(self, x: T) -> None: + self._x = Invariant1(x) + + @property + def x(self) -> T: + return self._x._x + +inv3_1: Invariant3[float] = Invariant3[int](1) # E: Incompatible types in assignment (expression has type "Invariant3[int]", variable has type "Invariant3[float]") +inv3_2: Invariant3[int] = Invariant3[float](1) # E: Incompatible types in assignment (expression has type "Invariant3[float]", variable has type "Invariant3[int]") +[builtins fixtures/property.pyi] + [case testPEP695InheritInvariant] # flags: --enable-incomplete-feature=NewGenericSyntax From 9d7a04277ebb5edcfe649977fb5bb45ac91573f6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Sep 2024 14:42:25 +0100 Subject: [PATCH 0746/1617] [PEP 695] Fix covariance of frozen dataclasses (#17783) Fix this conformance test: ``` @dataclass(frozen=True) class ShouldBeCovariant4[T]: x: T vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK vo4_2: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1) # E ``` Link: https://github.com/python/typing/blob/main/conformance/tests/generics_variance_inference.py#L66 --- mypy/subtypes.py | 3 ++- test-data/unit/check-python312.test | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 6e2366c4e0df..5c4471cc5b62 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -2004,7 +2004,8 @@ def infer_variance(info: TypeInfo, i: int) -> bool: tvar = info.defn.type_vars[i] self_type = fill_typevars(info) for member in all_non_object_members(info): - if member in ("__init__", "__new__"): + # __mypy-replace is an implementation detail of the dataclass plugin + if member in ("__init__", "__new__", "__mypy-replace"): continue if isinstance(self_type, TupleType): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 9dc52d2c07b0..d0a39f7e56a6 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -249,6 +249,25 @@ if int(): if int(): f = e +[case testPEP695InferVarianceInFrozenDataclass] +# flags: --enable-incomplete-feature=NewGenericSyntax +from dataclasses import dataclass + +@dataclass(frozen=True) +class Covariant[T]: + x: T + +cov1: Covariant[float] = Covariant[int](1) +cov2: Covariant[int] = Covariant[float](1) # E: Incompatible types in assignment (expression has type "Covariant[float]", variable has type "Covariant[int]") + +@dataclass(frozen=True) +class Invariant[T]: + x: list[T] + +inv1: Invariant[float] = Invariant[int]([1]) # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[float]") +inv2: Invariant[int] = Invariant[float]([1]) # E: Incompatible types in assignment (expression has type "Invariant[float]", variable has type "Invariant[int]") +[builtins fixtures/tuple.pyi] + [case testPEP695InferVarianceCalculateOnDemand] # flags: --enable-incomplete-feature=NewGenericSyntax From 4554bd0c6d49506a4a1c77dcf344dca4efee92e1 Mon Sep 17 00:00:00 2001 From: Katrina Connors <32425204+katconnors@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:22:06 -0700 Subject: [PATCH 0747/1617] Added error code for overlapping function signatures (#17597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #17570. This is my first contribution to mypy! 🐍 Added an error code for overlapping function signatures. Test in check-errorcodes.test is a derivative of this post: https://stackoverflow.com/q/69341607 Co-authored-by: Jelle Zijlstra Co-authored-by: Alex Waygood --- docs/source/error_code_list.rst | 28 ++++++++++++++++++++++++++++ mypy/errorcodes.py | 8 ++++++++ mypy/messages.py | 1 + test-data/unit/check-errorcodes.test | 14 ++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 85c8d437a856..ad73bc999f00 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1149,6 +1149,34 @@ types you expect. See :ref:`overloading ` for more explanation. + +.. _code-overload-cannot-match: + +Check for overload signatures that cannot match [overload-cannot-match] +-------------------------------------------------------------------------- + +Warn if an ``@overload`` variant can never be matched, because an earlier +overload has a wider signature. For example, this can happen if the two +overloads accept the same parameters and each parameter on the first overload +has the same type or a wider type than the corresponding parameter on the second +overload. + +Example: + +.. code-block:: python + + from typing import overload, Union + + @overload + def process(response1: object, response2: object) -> object: + ... + @overload + def process(response1: int, response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + + def process(response1: object, response2: object) -> object: + return response1 + response2 + .. _code-annotation-unchecked: Notify about an annotation in an unchecked function [annotation-unchecked] diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 6e8763264ddd..ad061b161af1 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -273,6 +273,14 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final[ErrorCode] = ErrorCode("misc", "Miscellaneous other checks", "General") +OVERLOAD_CANNOT_MATCH: Final[ErrorCode] = ErrorCode( + "overload-cannot-match", + "Warn if an @overload signature can never be matched", + "General", + sub_code_of=MISC, +) + + OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", diff --git a/mypy/messages.py b/mypy/messages.py index 62846c536f3d..dadce149680e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1653,6 +1653,7 @@ def overloaded_signature_will_never_match( index1=index1, index2=index2 ), context, + code=codes.OVERLOAD_CANNOT_MATCH, ) def overloaded_signatures_typevar_specific(self, index: int, context: Context) -> None: diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index cca13347dd86..10cc145d0c70 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1222,3 +1222,17 @@ def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of inpu pass [builtins fixtures/tuple.pyi] + + +[case testOverloadedFunctionSignature] +from typing import overload, Union + +@overload +def process(response1: float,response2: float) -> float: + ... +@overload +def process(response1: int,response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + +def process(response1,response2)-> Union[float,int]: + return response1 + response2 From 2a8c91e6413f07f800833d6e3409070b91d725b4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Sep 2024 17:44:29 +0100 Subject: [PATCH 0748/1617] [PEP 695] Fix crash on invalid type var reference (#17788) Test case from typing conformance test suite: https://github.com/python/typing/blob/main/conformance/tests/generics_syntax_declarations.py#L45 --- mypy/semanal.py | 1 + test-data/unit/check-python312.test | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 27d2a3abf93f..780d0b614ae3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6955,6 +6955,7 @@ def name_not_defined(self, name: str, ctx: Context, namespace: str | None = None namespace is None and self.type and not self.is_func_scope() + and self.incomplete_type_stack and self.incomplete_type_stack[-1] and not self.final_iteration ): diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index d0a39f7e56a6..bf1115dc51c5 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1320,6 +1320,14 @@ class P[T](Protocol[T]): # E: No arguments expected for "Protocol" base class class P2[T](Protocol[S]): # E: No arguments expected for "Protocol" base class pass +[case testPEP695CannotUseTypeVarFromOuterClass] +# mypy: enable-incomplete-feature=NewGenericSyntax +class ClassG[V]: + # This used to crash + class ClassD[T: dict[str, V]]: # E: Name "V" is not defined + ... +[builtins fixtures/dict.pyi] + [case testPEP695MixNewAndOldStyleGenerics] # mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVar From a646f330641422d5bc9bf71d1f7751ab2d8a8d5d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Sep 2024 17:44:57 +0100 Subject: [PATCH 0749/1617] [PEP 695] Inherit variance if base class has explicit variance (#17787) Previously we only inferred variance based on member types, but if a base class has explicit variance for some type variables, we need to consider it as well. --- mypy/subtypes.py | 9 ++++++ test-data/unit/check-python312.test | 40 ++++++++++++++++++++++++ test-data/unit/fixtures/tuple-simple.pyi | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 5c4471cc5b62..df040dcb1311 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -2031,6 +2031,15 @@ def infer_variance(info: TypeInfo, i: int) -> bool: contra = False if settable: co = False + + # Infer variance from base classes, in case they have explicit variances + for base in info.bases: + base2 = expand_type(base, {tvar.id: object_type}) + if not is_subtype(base, base2): + co = False + if not is_subtype(base2, base): + contra = False + if co: v = COVARIANT elif contra: diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index bf1115dc51c5..5200395047bc 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1891,3 +1891,43 @@ class A: a = A() v = a.f + +[case testPEP695VarianceInheritedFromBaseWithExplicitVariance] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import TypeVar, Generic + +T = TypeVar("T") + +class ParentInvariant(Generic[T]): + pass + +class Invariant1[T](ParentInvariant[T]): + pass + +a1: Invariant1[int] = Invariant1[float]() # E: Incompatible types in assignment (expression has type "Invariant1[float]", variable has type "Invariant1[int]") +a2: Invariant1[float] = Invariant1[int]() # E: Incompatible types in assignment (expression has type "Invariant1[int]", variable has type "Invariant1[float]") + +T_contra = TypeVar("T_contra", contravariant=True) + +class ParentContravariant(Generic[T_contra]): + pass + +class Contravariant[T](ParentContravariant[T]): + pass + +b1: Contravariant[int] = Contravariant[float]() +b2: Contravariant[float] = Contravariant[int]() # E: Incompatible types in assignment (expression has type "Contravariant[int]", variable has type "Contravariant[float]") + +class Invariant2[T](ParentContravariant[T]): + def f(self) -> T: ... + +c1: Invariant2[int] = Invariant2[float]() # E: Incompatible types in assignment (expression has type "Invariant2[float]", variable has type "Invariant2[int]") +c2: Invariant2[float] = Invariant2[int]() # E: Incompatible types in assignment (expression has type "Invariant2[int]", variable has type "Invariant2[float]") + +class Multi[T, S](ParentInvariant[T], ParentContravariant[S]): + pass + +d1: Multi[int, str] = Multi[float, str]() # E: Incompatible types in assignment (expression has type "Multi[float, str]", variable has type "Multi[int, str]") +d2: Multi[float, str] = Multi[int, str]() # E: Incompatible types in assignment (expression has type "Multi[int, str]", variable has type "Multi[float, str]") +d3: Multi[str, int] = Multi[str, float]() +d4: Multi[str, float] = Multi[str, int]() # E: Incompatible types in assignment (expression has type "Multi[str, int]", variable has type "Multi[str, float]") diff --git a/test-data/unit/fixtures/tuple-simple.pyi b/test-data/unit/fixtures/tuple-simple.pyi index 6c816c1c5b7a..07f9edf63cdd 100644 --- a/test-data/unit/fixtures/tuple-simple.pyi +++ b/test-data/unit/fixtures/tuple-simple.pyi @@ -5,7 +5,7 @@ from typing import Iterable, TypeVar, Generic -T = TypeVar('T') +T = TypeVar('T', covariant=True) class object: def __init__(self): pass From 18fee78645a32b4333a1b80720c7b7765775ad4b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 20 Sep 2024 12:49:14 +0100 Subject: [PATCH 0750/1617] [PEP 695] Generate error if new-style type alias used as base class (#17789) It doesn't work at runtime. --- mypy/semanal.py | 15 ++++++++++++ test-data/unit/check-python312.test | 38 ++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 780d0b614ae3..e239fbf1f141 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1824,6 +1824,8 @@ def analyze_class(self, defn: ClassDef) -> None: defn, bases, context=defn ) + self.check_type_alias_bases(bases) + for tvd in tvar_defs: if isinstance(tvd, TypeVarType) and any( has_placeholder(t) for t in [tvd.upper_bound] + tvd.values @@ -1895,6 +1897,19 @@ def analyze_class(self, defn: ClassDef) -> None: self.analyze_class_body_common(defn) + def check_type_alias_bases(self, bases: list[Expression]) -> None: + for base in bases: + if isinstance(base, IndexExpr): + base = base.base + if ( + isinstance(base, RefExpr) + and isinstance(base.node, TypeAlias) + and base.node.python_3_12_type_alias + ): + self.fail( + 'Type alias defined using "type" statement not valid as base class', base + ) + def setup_type_vars(self, defn: ClassDef, tvar_defs: list[TypeVarLikeType]) -> None: defn.type_vars = tvar_defs defn.info.type_vars = [] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 5200395047bc..d9737694c262 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -591,6 +591,40 @@ a4: A4 reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/type.pyi] +[case testPEP695TypeAliasNotValidAsBaseClass] +# flags: --enable-incomplete-feature=NewGenericSyntax +from typing import TypeAlias + +import m + +type A1 = int +class Bad1(A1): # E: Type alias defined using "type" statement not valid as base class + pass + +type A2[T] = list[T] +class Bad2(A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad3(m.A1): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad4(m.A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +B1 = int +B2 = list +B3: TypeAlias = int +class Good1(B1): pass +class Good2(B2[int]): pass +class Good3(list[A1]): pass +class Good4(list[A2[int]]): pass +class Good5(B3): pass + +[file m.py] +type A1 = str +type A2[T] = list[T] +[typing fixtures/typing-medium.pyi] + [case testPEP695TypeAliasWithUnusedTypeParams] # flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = int @@ -637,9 +671,7 @@ class D: pass type A = C -# Note that this doesn't actually work at runtime, but we currently don't -# keep track whether a type alias is valid in various runtime type contexts. -class D(A): +class D(A): # E: Type alias defined using "type" statement not valid as base class pass class C: pass From 5dfc7d941253553ab77836e9845cb8fdfb9d23a9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 20 Sep 2024 14:31:03 +0100 Subject: [PATCH 0751/1617] [PEP 695] Enable new type parameter syntax by default (#17798) I think the PEP 695 syntax is supported well enough now to enable it by default. --- docs/source/command_line.rst | 15 +- mypy/fastparse.py | 66 ++---- mypy/options.py | 4 +- mypyc/test-data/run-python312.test | 2 - mypyc/test/test_run.py | 1 - test-data/unit/check-python312.test | 240 ++++----------------- test-data/unit/deps.test | 2 +- test-data/unit/diff.test | 16 +- test-data/unit/fine-grained-python312.test | 17 +- test-data/unit/parse-python312.test | 8 +- test-data/unit/pythoneval.test | 4 +- 11 files changed, 85 insertions(+), 290 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index c085b63107b0..a89a3c85d4ee 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -1008,7 +1008,7 @@ format into the specified directory. Enabling incomplete/experimental features ***************************************** -.. option:: --enable-incomplete-feature {PreciseTupleTypes, NewGenericSyntax, InlineTypedDict} +.. option:: --enable-incomplete-feature {PreciseTupleTypes, InlineTypedDict} Some features may require several mypy releases to implement, for example due to their complexity, potential for backwards incompatibility, or @@ -1055,19 +1055,6 @@ List of currently incomplete/experimental features: # Without PreciseTupleTypes: tuple[int, ...] # With PreciseTupleTypes: tuple[()] | tuple[int] | tuple[int, int] -* ``NewGenericSyntax``: this feature enables support for syntax defined - by :pep:`695`. For example: - - .. code-block:: python - - class Container[T]: # defines a generic class - content: T - - def first[T](items: list[T]) -> T: # defines a generic function - return items[0] - - type Items[T] = list[tuple[T, T]] # defines a generic type alias - * ``InlineTypedDict``: this feature enables non-standard syntax for inline :ref:`TypedDicts `, for example: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index abcce74c6064..18858b0fa0b8 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -92,7 +92,7 @@ YieldFromExpr, check_arg_names, ) -from mypy.options import NEW_GENERIC_SYNTAX, Options +from mypy.options import Options from mypy.patterns import ( AsPattern, ClassPattern, @@ -965,19 +965,7 @@ def do_func_def( return_type = AnyType(TypeOfAny.from_error) else: if sys.version_info >= (3, 12) and n.type_params: - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - explicit_type_params = self.translate_type_params(n.type_params) - else: - self.fail( - ErrorMessage( - "PEP 695 generics are not yet supported. " - "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", - code=codes.VALID_TYPE, - ), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + explicit_type_params = self.translate_type_params(n.type_params) arg_types = [a.type_annotation for a in args] return_type = TypeConverter( @@ -1157,19 +1145,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: explicit_type_params: list[TypeParam] | None = None if sys.version_info >= (3, 12) and n.type_params: - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - explicit_type_params = self.translate_type_params(n.type_params) - else: - self.fail( - ErrorMessage( - "PEP 695 generics are not yet supported. " - "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", - code=codes.VALID_TYPE, - ), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + explicit_type_params = self.translate_type_params(n.type_params) cdef = ClassDef( n.name, @@ -1843,31 +1819,17 @@ def validate_type_alias(self, n: ast_TypeAlias) -> None: # TypeAlias(identifier name, type_param* type_params, expr value) def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: node: TypeAliasStmt | AssignmentStmt - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - type_params = self.translate_type_params(n.type_params) - self.validate_type_alias(n) - value = self.visit(n.value) - # Since the value is evaluated lazily, wrap the value inside a lambda. - # This helps mypyc. - ret = ReturnStmt(value) - self.set_line(ret, n.value) - value_func = LambdaExpr(body=Block([ret])) - self.set_line(value_func, n.value) - node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) - return self.set_line(node, n) - else: - self.fail( - ErrorMessage( - "PEP 695 type aliases are not yet supported. " - "Use --enable-incomplete-feature=NewGenericSyntax for experimental support", - code=codes.VALID_TYPE, - ), - n.lineno, - n.col_offset, - blocker=False, - ) - node = AssignmentStmt([NameExpr(n.name.id)], self.visit(n.value)) - return self.set_line(node, n) + type_params = self.translate_type_params(n.type_params) + self.validate_type_alias(n) + value = self.visit(n.value) + # Since the value is evaluated lazily, wrap the value inside a lambda. + # This helps mypyc. + ret = ReturnStmt(value) + self.set_line(ret, n.value) + value_func = LambdaExpr(body=Block([ret])) + self.set_line(value_func, n.value) + node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) + return self.set_line(node, n) class TypeConverter: diff --git a/mypy/options.py b/mypy/options.py index 5e64d5e40035..56bd92957b41 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -75,8 +75,8 @@ class BuildType: PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" NEW_GENERIC_SYNTAX: Final = "NewGenericSyntax" INLINE_TYPEDDICT: Final = "InlineTypedDict" -INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, NEW_GENERIC_SYNTAX, INLINE_TYPEDDICT)) -COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, INLINE_TYPEDDICT)) +COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK, NEW_GENERIC_SYNTAX)) class Options: diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test index 5e8a388fd8d3..a5a3f058d1e2 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -1,5 +1,4 @@ [case testPEP695Basics] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Any, TypeAliasType, cast from testutil import assertRaises @@ -192,7 +191,6 @@ def test_recursive_type_alias() -> None: [typing fixtures/typing-full.pyi] [case testPEP695GenericTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable from types import GenericAlias diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 37de192a9291..668e5b124841 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -196,7 +196,6 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> options.preserve_asts = True options.allow_empty_bodies = True options.incremental = self.separate - options.enable_incomplete_feature.append("NewGenericSyntax") # Avoid checking modules/packages named 'unchecked', to provide a way # to test interacting with code we don't have types for. diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index d9737694c262..2f0b912c439e 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1,70 +1,56 @@ -[case test695TypeAlias] -type MyInt = int # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support +[case testPEP695TypeAliasBasic] +type MyInt = int def f(x: MyInt) -> MyInt: return reveal_type(x) # N: Revealed type is "builtins.int" -type MyList[T] = list[T] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "T" is not defined +type MyList[T] = list[T] -def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not valid as a type \ - # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases - return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]" +def g(x: MyList[int]) -> MyList[int]: + return reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" -type MyInt2 = int # type: ignore[valid-type] +type MyInt2 = int def h(x: MyInt2) -> MyInt2: return reveal_type(x) # N: Revealed type is "builtins.int" -[case test695Class] -class MyGen[T]: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support - def __init__(self, x: T) -> None: # E: Name "T" is not defined +[case testPEP695Class] +class MyGen[T]: + def __init__(self, x: T) -> None: self.x = x -def f(x: MyGen[int]): # E: "MyGen" expects no type arguments, but 1 given - reveal_type(x.x) # N: Revealed type is "Any" +def f(x: MyGen[int]): + reveal_type(x.x) # N: Revealed type is "builtins.int" -[case test695Function] -def f[T](x: T) -> T: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "T" is not defined - return reveal_type(x) # N: Revealed type is "Any" +[case testPEP695Function] +def f[T](x: T) -> T: + return reveal_type(x) # N: Revealed type is "T`-1" -reveal_type(f(1)) # N: Revealed type is "Any" +reveal_type(f(1)) # N: Revealed type is "builtins.int" -async def g[T](x: T) -> T: # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "T" is not defined - return reveal_type(x) # N: Revealed type is "Any" +async def g[T](x: T) -> T: + return reveal_type(x) # N: Revealed type is "T`-1" -reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ +reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, int]" must be used \ # N: Are you missing an await? \ - # N: Revealed type is "typing.Coroutine[Any, Any, Any]" + # N: Revealed type is "typing.Coroutine[Any, Any, builtins.int]" -[case test695TypeVar] +[case testPEP695TypeVarBasic] from typing import Callable -type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "T" is not defined -type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Value of type "int" is not indexable \ - # E: Name "P" is not defined -type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "Ts" is not defined - -class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support -class Cls2[**P]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support -class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support - -def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "T" is not defined - -def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ - # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ - # E: Name "P" is not defined -def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported. Use --enable-incomplete-feature=NewGenericSyntax for experimental support \ - # E: Name "Ts" is not defined +type Alias1[T: int] = list[T] +type Alias2[**P] = Callable[P, int] +type Alias3[*Ts] = tuple[*Ts] + +class Cls1[T: int]: ... +class Cls2[**P]: ... +class Cls3[*Ts]: ... + +def func1[T: int](x: T) -> T: ... +def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... +def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... [builtins fixtures/tuple.pyi] -[case test695TypeAliasType] +[case testPEP695TypeAliasType] from typing import Callable, TypeAliasType, TypeVar, TypeVarTuple T = TypeVar("T") @@ -86,9 +72,13 @@ reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] -[case testPEP695GenericFunctionSyntax] -# flags: --enable-incomplete-feature=NewGenericSyntax +[case testPEP695IncompleteFeatureIsAcceptedButHasNoEffect] +# mypy: enable-incomplete-feature=NewGenericSyntax +def f[T](x: T) -> T: + return x +reveal_type(f(1)) # N: Revealed type is "builtins.int" +[case testPEP695GenericFunctionSyntax] def ident[TV](x: TV) -> TV: y: TV = x y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "TV") @@ -107,8 +97,6 @@ reveal_type(tup(1, 'x')) # N: Revealed type is "Tuple[builtins.int, builtins.st [builtins fixtures/tuple.pyi] [case testPEP695GenericClassSyntax] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: x: T @@ -128,8 +116,6 @@ reveal_type(c.x) # N: Revealed type is "builtins.int" reveal_type(c.ident(1)) # N: Revealed type is "builtins.int" [case testPEP695GenericMethodInGenericClass] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: def m[S](self, x: S) -> T | S: ... @@ -139,8 +125,6 @@ b: C[object] = C[int]() reveal_type(C[str]().m(1)) # N: Revealed type is "Union[builtins.str, builtins.int]" [case testPEP695InferVarianceSimpleFromMethod] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self, x: T) -> None: pass @@ -178,8 +162,6 @@ if int(): f = e [case testPEP695InferVarianceSimpleFromAttribute] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant1[T]: def __init__(self, x: T) -> None: self.x = x @@ -214,8 +196,6 @@ if int(): b3 = a3 # E: Incompatible types in assignment (expression has type "Invariant3[object]", variable has type "Invariant3[int]") [case testPEP695InferVarianceRecursive] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self, x: Invariant[T]) -> Invariant[T]: return x @@ -250,7 +230,6 @@ if int(): f = e [case testPEP695InferVarianceInFrozenDataclass] -# flags: --enable-incomplete-feature=NewGenericSyntax from dataclasses import dataclass @dataclass(frozen=True) @@ -269,8 +248,6 @@ inv2: Invariant[int] = Invariant[float]([1]) # E: Incompatible types in assignm [builtins fixtures/tuple.pyi] [case testPEP695InferVarianceCalculateOnDemand] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Covariant[T]: def __init__(self) -> None: self.x = [1] @@ -286,8 +263,6 @@ class Covariant[T]: def h(self, x: Covariant[int]) -> None: pass [case testPEP695InferVarianceNotReadyWhenNeeded] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Covariant[T]: def f(self) -> None: c = Covariant[int]() @@ -328,8 +303,6 @@ if int(): b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") [case testPEP695InferVarianceNotReadyForJoin] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self) -> None: # Assume covariance if variance us not ready @@ -342,8 +315,6 @@ class Invariant[T]: reveal_type([Invariant(1), Invariant(object())]) # N: Revealed type is "builtins.list[builtins.object]" [case testPEP695InferVarianceNotReadyForMeet] -# flags: --enable-incomplete-feature=NewGenericSyntax - from typing import TypeVar, Callable S = TypeVar("S") @@ -362,8 +333,6 @@ class Invariant[T]: reveal_type(c(a1, a2)) # N: Revealed type is "Never" [case testPEP695InferVarianceUnderscorePrefix] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Covariant1[T]: def __init__(self, x: T) -> None: self._x = x @@ -425,8 +394,6 @@ inv3_2: Invariant3[int] = Invariant3[float](1) # E: Incompatible types in assig [builtins fixtures/property.pyi] [case testPEP695InheritInvariant] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: x: T @@ -448,7 +415,6 @@ if int(): b = a # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") [case testPEP695InheritanceMakesInvariant] -# flags: --enable-incomplete-feature=NewGenericSyntax class Covariant[T]: def f(self) -> T: ... @@ -463,7 +429,6 @@ a: Subclass[int] = Subclass[object]() # E: Incompatible types in assignment (ex b: Subclass[object] = Subclass[int]() # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") [case testPEP695InheritCoOrContravariant] -# flags: --enable-incomplete-feature=NewGenericSyntax class Contravariant[T]: def f(self, x: T) -> None: pass @@ -489,7 +454,6 @@ e: InvSubclass[int] = InvSubclass[object]() # E: Incompatible types in assignme f: InvSubclass[object] = InvSubclass[int]() # E: Incompatible types in assignment (expression has type "InvSubclass[int]", variable has type "InvSubclass[object]") [case testPEP695FinalAttribute] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Final class C[T]: @@ -500,8 +464,6 @@ a: C[int] = C[object](1) # E: Incompatible types in assignment (expression has b: C[object] = C[int](1) [case testPEP695TwoTypeVariables] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T, S]: def f(self, x: T) -> None: ... def g(self) -> S: ... @@ -512,8 +474,6 @@ c: C[int, int] = C[int, object]() # E: Incompatible types in assignment (expres d: C[int, object] = C[int, int]() [case testPEP695Properties] -# flags: --enable-incomplete-feature=NewGenericSyntax - class R[T]: @property def p(self) -> T: ... @@ -531,7 +491,6 @@ d: RW[object] = RW[int]() # E: Incompatible types in assignment (expression has [builtins fixtures/property.pyi] [case testPEP695Protocol] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Protocol class PContra[T](Protocol): @@ -568,8 +527,6 @@ if int(): f = e # E: Incompatible types in assignment (expression has type "PInv[int]", variable has type "PInv[object]") [case testPEP695TypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: pass class D[T, S]: pass @@ -592,7 +549,6 @@ reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/type.pyi] [case testPEP695TypeAliasNotValidAsBaseClass] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import TypeAlias import m @@ -626,14 +582,11 @@ type A2[T] = list[T] [typing fixtures/typing-medium.pyi] [case testPEP695TypeAliasWithUnusedTypeParams] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = int a: A[str] reveal_type(a) # N: Revealed type is "builtins.int" [case testPEP695TypeAliasForwardReference1] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A[T] = C[T] a: A[int] @@ -642,8 +595,6 @@ reveal_type(a) # N: Revealed type is "__main__.C[builtins.int]" class C[T]: pass [case testPEP695TypeAliasForwardReference2] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X = C type A = X @@ -655,8 +606,6 @@ class C: pass [typing fixtures/typing-full.pyi] [case testPEP695TypeAliasForwardReference3] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X = D type A = C[X] @@ -667,8 +616,6 @@ class C[T]: pass class D: pass [case testPEP695TypeAliasForwardReference4] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A = C class D(A): # E: Type alias defined using "type" statement not valid as base class @@ -680,7 +627,6 @@ x: C = D() y: D = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") [case testPEP695TypeAliasForwardReference5] -# flags: --enable-incomplete-feature=NewGenericSyntax type A = str type B[T] = C[T] class C[T]: pass @@ -692,13 +638,11 @@ reveal_type(b) # N: Revealed type is "__main__.C[builtins.int]" reveal_type(c) # N: Revealed type is "__main__.C[builtins.str]" [case testPEP695TypeAliasWithUndefineName] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = XXX # E: Name "XXX" is not defined a: A[int] reveal_type(a) # N: Revealed type is "Any" [case testPEP695TypeAliasInvalidType] -# flags: --enable-incomplete-feature=NewGenericSyntax type A = int | 1 # E: Invalid type: try using Literal[1] instead? a: A @@ -709,13 +653,10 @@ reveal_type(b) # N: Revealed type is "Any" [builtins fixtures/type.pyi] [case testPEP695TypeAliasBoundForwardReference] -# mypy: enable-incomplete-feature=NewGenericSyntax type B[T: Foo] = list[T] class Foo: pass [case testPEP695UpperBound] -# flags: --enable-incomplete-feature=NewGenericSyntax - class D: x: int class E(D): pass @@ -738,8 +679,6 @@ reveal_type(f(E())) # N: Revealed type is "__main__.E" f(1) # E: Value of type variable "T" of "f" cannot be "int" [case testPEP695UpperBoundForwardReference1] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T: D]: pass a: C[D] @@ -753,8 +692,6 @@ class D: pass class E(D): pass [case testPEP695UpperBoundForwardReference2] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A = D class C[T: A]: pass @@ -769,8 +706,6 @@ reveal_type(b) # N: Revealed type is "__main__.C[__main__.E]" c: C[int] # E: Type argument "int" of "C" must be a subtype of "D" [case testPEP695UpperBoundForwardReference3] -# flags: --enable-incomplete-feature=NewGenericSyntax - class D[T]: pass class E[T](D[T]): pass @@ -788,8 +723,6 @@ reveal_type(b) # N: Revealed type is "__main__.C[__main__.E[__main__.X]]" c: C[D[int]] # E: Type argument "D[int]" of "C" must be a subtype of "D[X]" [case testPEP695UpperBoundForwardReference4] -# flags: --enable-incomplete-feature=NewGenericSyntax - def f[T: D](a: T) -> T: reveal_type(a.x) # N: Revealed type is "builtins.int" return a @@ -803,8 +736,6 @@ reveal_type(f(E())) # N: Revealed type is "__main__.E" f(1) # E: Value of type variable "T" of "f" cannot be "int" [case testPEP695UpperBoundUndefinedName] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T: XX]: # E: Name "XX" is not defined pass @@ -815,8 +746,6 @@ def f[T: YY](x: T) -> T: # E: Name "YY" is not defined reveal_type(f) # N: Revealed type is "def [T <: Any] (x: T`-1) -> T`-1" [case testPEP695UpperBoundWithMultipleParams] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T, S: int]: pass class D[A: int, B]: pass @@ -834,8 +763,6 @@ f('x', None) # E: Value of type variable "T" of "f" cannot be "str" \ # E: Value of type variable "S" of "f" cannot be "None" [case testPEP695InferVarianceOfTupleType] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Cov[T](tuple[int, str]): def f(self) -> T: pass @@ -855,9 +782,7 @@ e: Contra[int] = Contra[object]() f: Contra[object] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[object]") [builtins fixtures/tuple-simple.pyi] -[case testPEP695ValueRestiction] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestriction] def f[T: (int, str)](x: T) -> T: reveal_type(x) # N: Revealed type is "builtins.int" \ # N: Revealed type is "builtins.str" @@ -873,9 +798,7 @@ a: C[object] b: C[None] c: C[int] # E: Value of type variable "T" of "C" cannot be "int" -[case testPEP695ValueRestictionForwardReference] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestrictionForwardReference] class C[T: (int, D)]: def __init__(self, x: T) -> None: a = x @@ -891,9 +814,7 @@ class D: pass C(D()) -[case testPEP695ValueRestictionUndefinedName] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestrictionUndefinedName] class C[T: (int, XX)]: # E: Name "XX" is not defined pass @@ -901,7 +822,6 @@ def f[S: (int, YY)](x: S) -> S: # E: Name "YY" is not defined return x [case testPEP695ParamSpec] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable def g[**P](f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: @@ -923,7 +843,6 @@ reveal_type(a.m) # N: Revealed type is "def (builtins.int, builtins.str)" [builtins fixtures/tuple.pyi] [case testPEP695ParamSpecTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable type C[**P] = Callable[P, int] @@ -934,8 +853,6 @@ reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, No [typing fixtures/typing-full.pyi] [case testPEP695TypeVarTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax - def f[*Ts](t: tuple[*Ts]) -> tuple[*Ts]: reveal_type(t) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" return t @@ -955,7 +872,6 @@ b = c # E: Incompatible types in assignment (expression has type "C[str]", vari [builtins fixtures/tuple.pyi] [case testPEP695TypeVarTupleAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable type C[*Ts] = tuple[*Ts, int] @@ -965,7 +881,6 @@ reveal_type(a) # N: Revealed type is "Tuple[builtins.str, None, builtins.int]" [builtins fixtures/tuple.pyi] [case testPEP695IncrementalFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -992,7 +907,6 @@ tmp/a.py:4: error: Value of type variable "T" of "g" cannot be "str" tmp/a.py:5: error: Value of type variable "S" of "g" cannot be "int" [case testPEP695IncrementalClass] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -1025,7 +939,6 @@ tmp/a.py:12: error: Value of type variable "S" of "D" cannot be "SS" tmp/a.py:13: error: Type argument "object" of "D" must be a subtype of "int" [case testPEP695IncrementalParamSpecAndTypeVarTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -1049,7 +962,6 @@ class D[**P]: tmp/a.py:6: note: Revealed type is "def (builtins.int, builtins.str)" [case testPEP695IncrementalTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -1073,8 +985,6 @@ tmp/a.py:3: note: Revealed type is "builtins.str" tmp/a.py:5: note: Revealed type is "b.Foo[builtins.int]" [case testPEP695UndefinedNameInGenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax - def f[T](x: T) -> T: return unknown() # E: Name "unknown" is not defined @@ -1083,7 +993,6 @@ class C: return unknown() # E: Name "unknown" is not defined [case testPEP695FunctionTypeVarAccessInFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast class C: @@ -1095,8 +1004,6 @@ class C: reveal_type(C().m(1)) # N: Revealed type is "builtins.int" [case testPEP695ScopingBasics] -# mypy: enable-incomplete-feature=NewGenericSyntax - T = 1 def f[T](x: T) -> T: @@ -1113,8 +1020,6 @@ class C[T]: reveal_type(T) # N: Revealed type is "builtins.int" [case testPEP695ClassScoping] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C: class D: pass @@ -1125,7 +1030,6 @@ C().m(C.D(), C.D()) C().m(1, C.D()) # E: Value of type variable "T" of "m" of "C" cannot be "int" [case testPEP695NestedGenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax def f[T](x: T) -> T: reveal_type(f(x)) # N: Revealed type is "T`-1" reveal_type(f(1)) # N: Revealed type is "builtins.int" @@ -1149,7 +1053,6 @@ def f[T](x: T) -> T: return x [case testPEP695NonLocalAndGlobal] -# mypy: enable-incomplete-feature=NewGenericSyntax def f() -> None: T = 1 def g[T](x: T) -> T: @@ -1179,7 +1082,6 @@ class C[T]: return a [case testPEP695ArgumentDefault] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast def f[T]( @@ -1199,7 +1101,6 @@ class C: [typing fixtures/typing-full.pyi] [case testPEP695ListComprehension] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast def f[T](x: T) -> T: @@ -1208,8 +1109,6 @@ def f[T](x: T) -> T: return x [case testPEP695ReuseNameInSameScope] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C[T]: def m[S](self, x: S, y: T) -> S | T: return x @@ -1231,7 +1130,6 @@ def g[T](x: T) -> T: return x [case testPEP695NestedScopingSpecialCases] -# mypy: enable-incomplete-feature=NewGenericSyntax # This is adapted from PEP 695 S = 0 @@ -1248,7 +1146,6 @@ def outer1[S]() -> None: global S [case testPEP695ScopingWithBaseClasses] -# mypy: enable-incomplete-feature=NewGenericSyntax # This is adapted from PEP 695 class Outer: class Private: @@ -1264,7 +1161,6 @@ class Outer: return a [case testPEP695RedefineTypeParameterInScope] -# mypy: enable-incomplete-feature=NewGenericSyntax class C[T]: def m[T](self, x: T) -> T: # E: "T" already defined as a type parameter return x @@ -1276,7 +1172,6 @@ def f[S, S](x: S) -> S: # E: "S" already defined as a type parameter return x [case testPEP695ClassDecorator] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Any T = 0 @@ -1288,8 +1183,6 @@ class C[T]: pass [case testPEP695RecursiceTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - type A = str | list[A] a: A reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.list[...]]" @@ -1302,8 +1195,6 @@ reveal_type(b) # N: Revealed type is "Union[__main__.C[builtins.int], builtins. [builtins fixtures/type.pyi] [case testPEP695BadRecursiveTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - type A = A # E: Cannot resolve name "A" (possible cyclic definition) type B = B | int # E: Invalid recursive alias: a union item of itself a: A @@ -1314,8 +1205,6 @@ reveal_type(b) # N: Revealed type is "Any" [typing fixtures/typing-full.pyi] [case testPEP695RecursiveTypeAliasForwardReference] -# mypy: enable-incomplete-feature=NewGenericSyntax - def f(a: A) -> None: if isinstance(a, str): reveal_type(a) # N: Revealed type is "builtins.str" @@ -1334,7 +1223,6 @@ f(C[int]()) # E: Argument 1 to "f" has incompatible type "C[int]"; expected "A" [builtins fixtures/isinstance.pyi] [case testPEP695InvalidGenericOrProtocolBaseClass] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Generic, Protocol, TypeVar S = TypeVar("S") @@ -1353,7 +1241,6 @@ class P2[T](Protocol[S]): # E: No arguments expected for "Protocol" base class pass [case testPEP695CannotUseTypeVarFromOuterClass] -# mypy: enable-incomplete-feature=NewGenericSyntax class ClassG[V]: # This used to crash class ClassD[T: dict[str, V]]: # E: Name "V" is not defined @@ -1361,7 +1248,6 @@ class ClassG[V]: [builtins fixtures/dict.pyi] [case testPEP695MixNewAndOldStyleGenerics] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVar S = TypeVar("S") @@ -1382,7 +1268,6 @@ class D[T](C[S]): # E: All type parameters should be declared ("S" not declared pass [case testPEP695MixNewAndOldStyleTypeVarTupleAndParamSpec] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVarTuple, ParamSpec, Callable Ts = TypeVarTuple("Ts") P = ParamSpec("P") @@ -1394,7 +1279,6 @@ def g[T](x: T, f: tuple[*Ts] # E: All type parameters should be declared ("Ts" [builtins fixtures/tuple.pyi] [case testPEP695MixNewAndOldStyleGenericsInTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVar, ParamSpec, TypeVarTuple, Callable T = TypeVar("T") @@ -1411,7 +1295,6 @@ type C = Callable[P, None] # E: All type parameters should be declared ("P" not [typing fixtures/typing-full.pyi] [case testPEP695NonGenericAliasToGenericClass] -# mypy: enable-incomplete-feature=NewGenericSyntax class C[T]: pass type A = C x: C @@ -1421,7 +1304,6 @@ reveal_type(y) # N: Revealed type is "__main__.C[Any]" z: A[int] # E: Bad number of arguments for type alias, expected 0, given 1 [case testPEP695SelfType] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Self class C: @@ -1452,8 +1334,6 @@ reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins [builtins fixtures/tuple.pyi] [case testPEP695CallAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C: def __init__(self, x: str) -> None: ... type A = C @@ -1476,7 +1356,6 @@ B2[int]() [typing fixtures/typing-full.pyi] [case testPEP695IncrementalTypeAliasKinds] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -1499,7 +1378,6 @@ C: TypeAlias = int tmp/a.py:2: error: "TypeAliasType" not callable [case testPEP695TypeAliasBoundAndValueChecking] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Any, cast class C: pass @@ -1540,8 +1418,6 @@ c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" [typing fixtures/typing-full.pyi] [case testPEP695TypeAliasInClassBodyOrFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C: type A = int type B[T] = list[T] | None @@ -1594,16 +1470,14 @@ reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" [typing fixtures/typing-full.pyi] [case testPEP695RedefineAsTypeAlias1] -# flags: --enable-incomplete-feature=NewGenericSyntax class C: pass -type C = int # E: Name "C" already defined on line 2 +type C = int # E: Name "C" already defined on line 1 A = 0 -type A = str # E: Name "A" already defined on line 5 +type A = str # E: Name "A" already defined on line 4 reveal_type(A) # N: Revealed type is "builtins.int" [case testPEP695RedefineAsTypeAlias2] -# flags: --enable-incomplete-feature=NewGenericSyntax from m import D type D = int # E: Name "D" already defined (possibly by an import) a: D @@ -1612,30 +1486,26 @@ reveal_type(a) # N: Revealed type is "m.D" class D: pass [case testPEP695RedefineAsTypeAlias3] -# flags: --enable-incomplete-feature=NewGenericSyntax D = list["Forward"] -type D = int # E: Name "D" already defined on line 2 +type D = int # E: Name "D" already defined on line 1 Forward = str x: D reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" [case testPEP695MultiDefinitionsForTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax if int(): type A[T] = list[T] else: - type A[T] = str # E: Name "A" already defined on line 3 + type A[T] = str # E: Name "A" already defined on line 2 x: T # E: Name "T" is not defined a: A[int] reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" [case testPEP695UndefinedNameInAnnotation] -# flags: --enable-incomplete-feature=NewGenericSyntax def f[T](x: foobar, y: T) -> T: ... # E: Name "foobar" is not defined reveal_type(f) # N: Revealed type is "def [T] (x: Any, y: T`-1) -> T`-1" [case testPEP695WrongNumberOfConstrainedTypes] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T: ()] = list[T] # E: Type variable must have at least two constrained types a: A[int] reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" @@ -1645,7 +1515,6 @@ b: B[str] reveal_type(b) # N: Revealed type is "builtins.list[builtins.str]" [case testPEP695UsingTypeVariableInOwnBoundOrConstraint] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T: list[T]] = str # E: Name "T" is not defined type B[S: (list[S], str)] = str # E: Name "S" is not defined type C[T, S: list[T]] = str # E: Name "T" is not defined @@ -1655,7 +1524,6 @@ class D[T: T]: # E: Name "T" is not defined pass [case testPEP695InvalidType] -# flags: --enable-incomplete-feature=NewGenericSyntax def f[T: 1](x: T) -> T: ... # E: Invalid type: try using Literal[1] instead? class C[T: (int, (1 + 2))]: pass # E: Invalid type comment or annotation type A = list[1] # E: Invalid type: try using Literal[1] instead? @@ -1666,7 +1534,6 @@ b: B reveal_type(b) # N: Revealed type is "Any" [case testPEP695GenericNamedTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import NamedTuple # Invariant because of the signature of the generated _replace method @@ -1693,7 +1560,6 @@ e: M[bool] # E: Value of type variable "T" of "M" cannot be "bool" [builtins fixtures/tuple.pyi] [case testPEP695GenericTypedDict] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import TypedDict class D[T](TypedDict): @@ -1714,7 +1580,6 @@ d: E[int] # E: Type argument "int" of "E" must be a subtype of "str" [typing fixtures/typing-full.pyi] [case testCurrentClassWorksAsBound] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Protocol class Comparable[T: Comparable](Protocol): @@ -1727,7 +1592,6 @@ x: Comparable[Good] y: Comparable[int] # E: Type argument "int" of "Comparable" must be a subtype of "Comparable[Any]" [case testPEP695TypeAliasWithDifferentTargetTypes] -# flags: --enable-incomplete-feature=NewGenericSyntax import types # We need GenericAlias from here, and test stubs don't bring in 'types' from typing import Any, Callable, List, Literal, TypedDict @@ -1779,7 +1643,7 @@ type I3 = None | C[TD] [typing fixtures/typing-full.pyi] [case testTypedDictInlineYesNewStyleAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax --enable-incomplete-feature=InlineTypedDict +# flags: --enable-incomplete-feature=InlineTypedDict type X[T] = {"item": T, "other": X[T] | None} x: X[str] reveal_type(x) # N: Revealed type is "TypedDict({'item': builtins.str, 'other': Union[..., None]})" @@ -1791,8 +1655,6 @@ type Y[T] = {"item": T, **Y[T]} # E: Overwriting TypedDict field "item" while m [typing fixtures/typing-full.pyi] [case testPEP695UsingIncorrectExpressionsInTypeVariableBound] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X[T: (yield 1)] = Any # E: Yield expression cannot be used as a type variable bound type Y[T: (yield from [])] = Any # E: Yield expression cannot be used as a type variable bound type Z[T: (a := 1)] = Any # E: Named expression cannot be used as a type variable bound @@ -1824,8 +1686,6 @@ def fooz_nested[T: (1 + (a := 1))](): pass # E: Named expression cannot be used def fook_nested[T: (1 +(await 1))](): pass # E: Await expression cannot be used as a type variable bound [case testPEP695UsingIncorrectExpressionsInTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X = (yield 1) # E: Yield expression cannot be used within a type alias type Y = (yield from []) # E: Yield expression cannot be used within a type alias type Z = (a := 1) # E: Named expression cannot be used within a type alias @@ -1837,7 +1697,6 @@ type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a typ type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias [case testPEP695TypeAliasAndAnnotated] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing_extensions import Annotated, Annotated as _Annotated import typing_extensions as t @@ -1859,7 +1718,6 @@ reveal_type(zz) # N: Revealed type is "builtins.str" [builtins fixtures/tuple.pyi] [case testPEP695NestedGenericClass1] -# flags: --enable-incomplete-feature=NewGenericSyntax class C[T]: def f(self) -> T: ... @@ -1882,7 +1740,6 @@ reveal_type(x) # N: Revealed type is "__main__.A.B[builtins.int]" reveal_type(A.b) # N: Revealed type is "__main__.A.B[builtins.str]" [case testPEP695NestedGenericClass2] -# flags: --enable-incomplete-feature=NewGenericSyntax class A: def m(self) -> None: class B[T]: @@ -1891,11 +1748,10 @@ class A: reveal_type(x.f()) # N: Revealed type is "builtins.int" self.a = B[str]() -reveal_type(A().a) # N: Revealed type is "__main__.B@4[builtins.str]" +reveal_type(A().a) # N: Revealed type is "__main__.B@3[builtins.str]" reveal_type(A().a.f()) # N: Revealed type is "builtins.str" [case testPEP695NestedGenericClass3] -# flags: --enable-incomplete-feature=NewGenericSyntax class C[T]: def f(self) -> T: ... class D[S]: @@ -1914,7 +1770,6 @@ class E[T]: c: E.F[int] [case testPEP695NestedGenericClass4] -# flags: --enable-incomplete-feature=NewGenericSyntax class A: class B[T]: def __get__(self, instance: A, owner: type[A]) -> T: @@ -1925,7 +1780,6 @@ a = A() v = a.f [case testPEP695VarianceInheritedFromBaseWithExplicitVariance] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import TypeVar, Generic T = TypeVar("T") diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 3364dee6c696..84cea99bf2f6 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1433,7 +1433,7 @@ class B(A): -> m [case testPEP695TypeAliasDeps] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from a import C, E type A = C type A2 = A diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 9212d902e8b2..10fb0a80cf38 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1532,7 +1532,7 @@ __main__.C.get_by_team_and_id __main__.Optional [case testPEP695TypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from typing_extensions import TypeAlias, TypeAliasType type A = int type B = str @@ -1544,7 +1544,7 @@ G = TypeAliasType("G", int) type H = int [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from typing_extensions import TypeAlias, TypeAliasType type A = str type B = str @@ -1566,7 +1566,7 @@ __main__.G __main__.H [case testPEP695TypeAlias2] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 type A[T: int] = list[T] type B[T: int] = list[T] type C[T: (int, str)] = list[T] @@ -1575,7 +1575,7 @@ type E[T: int] = list[T] type F[T: (int, str)] = list[T] [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 type A[T] = list[T] type B[T: str] = list[T] type C[T: (int, None)] = list[T] @@ -1590,13 +1590,13 @@ __main__.C __main__.D [case testPEP695GenericFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 def f[T](x: T) -> T: return x def g[T](x: T, y: T) -> T: return x [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 def f[T](x: T) -> T: return x def g[T, S](x: T, y: S) -> S: @@ -1605,7 +1605,7 @@ def g[T, S](x: T, y: S) -> S: __main__.g [case testPEP695GenericClass] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 class C[T]: pass class D[T]: @@ -1615,7 +1615,7 @@ class E[T]: class F[T]: def f(self, x: object) -> T: ... [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 class C[T]: pass class D[T: int]: diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 80e4e4ca3bd8..0e438ca06574 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -1,5 +1,4 @@ [case testPEP695TypeAliasDep] -# flags: --enable-incomplete-feature=NewGenericSyntax import m def g() -> m.C: return m.f() @@ -15,10 +14,9 @@ def f() -> int: pass [out] == -main:4: error: Incompatible return value type (got "int", expected "str") +main:3: error: Incompatible return value type (got "int", expected "str") [case testPEP695ChangeOldStyleToNewStyleTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from m import A A() @@ -31,10 +29,9 @@ type A = int [builtins fixtures/tuple.pyi] [out] == -main:3: error: "TypeAliasType" not callable +main:2: error: "TypeAliasType" not callable [case testPEP695VarianceChangesDueToDependency] -# flags: --enable-incomplete-feature=NewGenericSyntax from a import C x: C[object] = C[int]() @@ -55,10 +52,9 @@ class A[T]: [out] == -main:4: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]") +main:3: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]") [case testPEP695TypeAliasChangesDueToDependency] -# flags: --enable-incomplete-feature=NewGenericSyntax from a import A x: A x = 0 @@ -78,11 +74,10 @@ from builtins import tuple as B [typing fixtures/typing-full.pyi] [out] == -main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]") +main:3: error: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int, str]") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, str]") [case testPEP695NestedGenericClassMethodUpdated] -# flags: --enable-incomplete-feature=NewGenericSyntax from a import f class C: @@ -99,4 +94,4 @@ def f() -> None: pass def f(x: int) -> None: pass [out] == -main:8: error: Missing positional argument "x" in call to "f" +main:7: error: Missing positional argument "x" in call to "f" diff --git a/test-data/unit/parse-python312.test b/test-data/unit/parse-python312.test index 90ee96f38deb..2b1f9b42e0f7 100644 --- a/test-data/unit/parse-python312.test +++ b/test-data/unit/parse-python312.test @@ -1,5 +1,5 @@ [case testPEP695TypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment type A[T] = C[T] [out] MypyFile:1( @@ -15,7 +15,7 @@ MypyFile:1( NameExpr(T))))))) [case testPEP695GenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[T](): pass def g[T: str](): pass @@ -46,7 +46,7 @@ MypyFile:1( PassStmt:5()))) [case testPEP695ParamSpec] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[**P](): pass class C[T: int, **P]: pass @@ -68,7 +68,7 @@ MypyFile:1( PassStmt:4())) [case testPEP695TypeVarTuple] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[*Ts](): pass class C[T: int, *Ts]: pass diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index dbf228623d7c..26fc419030bf 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2099,7 +2099,7 @@ def f(d: Description) -> None: _testDataclassStrictOptionalAlwaysSet.py:9: note: Revealed type is "def (Union[builtins.int, None]) -> Union[builtins.str, None]" [case testPEP695VarianceInference] -# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +# flags: --python-version=3.12 from typing import Callable, Final class Job[_R_co]: @@ -2120,7 +2120,7 @@ def func( _testPEP695VarianceInference.py:17: error: Incompatible types in assignment (expression has type "Job[None]", variable has type "Job[int]") [case testPEP695TypeAliasWithDifferentTargetTypes] -# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +# flags: --python-version=3.12 from typing import Any, Callable, List, Literal, TypedDict, overload, TypeAlias, TypeVar, Never class C[T]: pass From 94109aaabf87be093200b19760a8e75318c62f9d Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 21 Sep 2024 18:13:18 -0400 Subject: [PATCH 0752/1617] Fix TypeVar upper bounds sometimes not being displayed in pretty callables (#17802) Fixes #17792. Related to #17791. Currently, [`pretty_callable`](https://github.com/python/mypy/blob/5dfc7d941253553ab77836e9845cb8fdfb9d23a9/mypy/messages.py#L2862) only renders `TypeVar` upper bounds if they are of type `Instance`: https://github.com/python/mypy/blob/5dfc7d941253553ab77836e9845cb8fdfb9d23a9/mypy/messages.py#L2943-L2949 However, there are some types that can appear as `TypeVar` upper bounds which are not represented by `Instance`, such as `UnionType` and `CallableType`. This PR allows such non-`Instance` upper bounds to be rendered as well. ## Effect Consider the below code. Playground link: https://mypy-play.net/?mypy=1.11.2&python=3.12&enable-incomplete-feature=NewGenericSyntax&gist=ba30c820cc3668e0919dadf2f391ff4b ```python from collections.abc import Callable from typing import Any, overload ### No matching overloads @overload def f1[T: int](x: T) -> T: ... @overload def f1[T: Callable[..., None]](x: T) -> T: ... @overload def f1[T: tuple[int]](x: T) -> T: ... @overload def f1[T: None](x: T) -> T: ... @overload def f1[T: type[int]](x: T) -> T: ... @overload def f1[T: bytes | bytearray](x: T) -> T: ... def f1(x): return x f1(1.23) ### Mismatching conditional definitions if input(): def f2[T](x: T) -> T: return x else: def f2[T: Callable[..., None]](x: T) -> T: return x ``` ### Before * In the first error on line 20, all overloads aside from the first one are displayed as `def [T] f1(x: T) -> T` (upper bound missing). Duplicate entries are suppressed. * In the second error on line 28, the second definition is displayed as `def [T] f2(x: T) -> T` (upper bound missing), and is removed as an apparent duplicate of the first. ```none main.py:20: error: No overload variant of "f1" matches argument type "float" [call-overload] main.py:20: note: Possible overload variants: main.py:20: note: def [T: int] f1(x: T) -> T main.py:20: note: def [T] f1(x: T) -> T main.py:28: error: All conditional function variants must have identical signatures [misc] main.py:28: note: Original: main.py:28: note: def [T] f2(x: T) -> T main.py:28: note: Redefinition: Found 2 errors in 1 file (checked 1 source file) ``` ### After * All type var upper bounds are rendered. ```none main.py:20: error: No overload variant of "f1" matches argument type "float" [call-overload] main.py:20: note: Possible overload variants: main.py:20: note: def [T: int] f1(x: T) -> T main.py:20: note: def [T: Callable[..., None]] f1(x: T) -> T main.py:20: note: def [T: tuple[int]] f1(x: T) -> T main.py:20: note: def [T: None] f1(x: T) -> T main.py:20: note: def [T: type[int]] f1(x: T) -> T main.py:20: note: def [T: bytes | bytearray] f1(x: T) -> T main.py:28: error: All conditional function variants must have identical signatures [misc] main.py:28: note: Original: main.py:28: note: def [T] f2(x: T) -> T main.py:28: note: Redefinition: main.py:28: note: def [T: Callable[..., None]] f2(x: T) -> T Found 2 errors in 1 file (checked 1 source file) ``` --- mypy/messages.py | 4 ++-- test-data/unit/check-functions.test | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index dadce149680e..6567d9d96d0b 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2942,9 +2942,9 @@ def [T <: int] f(self, x: int, y: T) -> None for tvar in tp.variables: if isinstance(tvar, TypeVarType): upper_bound = get_proper_type(tvar.upper_bound) - if ( + if not ( isinstance(upper_bound, Instance) - and upper_bound.type.fullname != "builtins.object" + and upper_bound.type.fullname == "builtins.object" ): tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}") elif tvar.values: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index b681c544a6bd..96f9815019e6 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1428,6 +1428,20 @@ else: # N: Redefinition: \ # N: def f(x: int = ...) -> None +[case testIncompatibleConditionalFunctionDefinition4] +from typing import Any, Union, TypeVar +T1 = TypeVar('T1') +T2 = TypeVar('T2', bound=Union[int, str]) +x = None # type: Any +if x: + def f(x: T1) -> T1: pass +else: + def f(x: T2) -> T2: pass # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def [T1] f(x: T1) -> T1 \ + # N: Redefinition: \ + # N: def [T2: Union[int, str]] f(x: T2) -> T2 + [case testConditionalFunctionDefinitionUsingDecorator1] from typing import Callable From 9ffb9ddf80fbf92956a55946c8f023fc09638959 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 21 Sep 2024 19:45:25 -0400 Subject: [PATCH 0753/1617] Fix crash when passing too many type arguments to generic base class accepting single ParamSpec (#17770) Fixes #17765 The offender for this crash appears to be this snippet: https://github.com/python/mypy/blob/72c413d2352da5ce1433ef241faca8f40fa1fe27/mypy/semanal.py#L5905-L5910 This branch triggers when applying type args to a type that is generic with respect to a single `ParamSpec`. It allows double brackets to be omitted when providing a parameter specification by wrapping all of the provided type arguments into a single parameter specification argument (i.e. equating `Foo[int, int]` to `Foo[[int, int]]`). This wrapping occurs *unless*: * there is only a single type argument, and it resolves to `Any` (e.g. `Foo[Any]`) * **there is only a single type argument**, and it's a bracketed parameter specification or a `ParamSpec` (e.g. `Foo[[int, int]]`) The problem occurs when multiple type arguments provided and at least one of them is a bracketed parameter specification, as in `Foo[[int, int], str]`. Per the rules above, since there is more than 1 type argument, mypy attempts to wrap the arguments into a single parameter specification. This results in the attempted creation of a `Parameters` instance that contains another `Parameters` instance, which triggers this assert inside `Parameters.__init__`: https://github.com/python/mypy/blob/72c413d2352da5ce1433ef241faca8f40fa1fe27/mypy/types.py#L1634 I think a reasonable solution is to forgo wrapping the type arguments into a single `Parameters` if **any** of the provided type arguments are a `Parameters`/`ParamSpecType`. That is, don't transform `Foo[A1, A2, ...]` to `Foo[[A1, A2, ...]]` if any of `A1, A2, ...` are a parameter specification. This change brings the crash case inline with mypy's current behavior for a similar case: ```python # Current behavior P = ParamSpec("P") class C(Generic[P]): ... c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed ``` Before this change: ```python P = ParamSpec("P") class C(Generic[P]): ... class D(C[int, [int, str], str]): ... # !!! CRASH !!! ``` After this change: ```python P = ParamSpec("P") class C(Generic[P]): ... class D(C[int, [int, str], str]): ... # E: Nested parameter specifications are not allowed ```` --- mypy/semanal.py | 5 ++--- test-data/unit/check-parameter-specification.test | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e239fbf1f141..0b654d6b145f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5926,9 +5926,8 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: if has_param_spec and num_args == 1 and types: first_arg = get_proper_type(types[0]) - if not ( - len(types) == 1 and isinstance(first_arg, (Parameters, ParamSpecType, AnyType)) - ): + single_any = len(types) == 1 and isinstance(first_arg, AnyType) + if not (single_any or any(isinstance(t, (Parameters, ParamSpecType)) for t in types)): types = [Parameters(types, [ARG_POS] * len(types), [None] * len(types))] return types diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index c2afb61586a8..654f36775172 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1836,6 +1836,15 @@ c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed reveal_type(c) # N: Revealed type is "__main__.C[Any]" [builtins fixtures/paramspec.pyi] +[case testParamSpecInheritNoCrashOnNested] +from typing import Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") +class C(Generic[P]): ... +class D(C[int, [int, str], str]): ... # E: Nested parameter specifications are not allowed +[builtins fixtures/paramspec.pyi] + [case testParamSpecConcatenateSelfType] from typing import Callable from typing_extensions import ParamSpec, Concatenate From cf3db994c1288a4b3ba445a0d11b85cf119a50a3 Mon Sep 17 00:00:00 2001 From: Michael I Chen Date: Tue, 24 Sep 2024 05:24:41 +0100 Subject: [PATCH 0754/1617] Copyedit final_attrs.rst (#17813) --- docs/source/final_attrs.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/source/final_attrs.rst b/docs/source/final_attrs.rst index 297b97eca787..81bfba650430 100644 --- a/docs/source/final_attrs.rst +++ b/docs/source/final_attrs.rst @@ -25,8 +25,8 @@ Final names You can use the ``typing.Final`` qualifier to indicate that a name or attribute should not be reassigned, redefined, or -overridden. This is often useful for module and class level constants -as a way to prevent unintended modification. Mypy will prevent +overridden. This is often useful for module and class-level +constants to prevent unintended modification. Mypy will prevent further assignments to final names in type-checked code: .. code-block:: python @@ -70,7 +70,7 @@ You can use ``Final`` in one of these forms: ID: Final[int] = 1 - Here mypy will infer type ``int`` for ``ID``. + Here, mypy will infer type ``int`` for ``ID``. * You can omit the type: @@ -78,15 +78,15 @@ You can use ``Final`` in one of these forms: ID: Final = 1 - Here mypy will infer type ``Literal[1]`` for ``ID``. Note that unlike for - generic classes this is *not* the same as ``Final[Any]``. + Here, mypy will infer type ``Literal[1]`` for ``ID``. Note that unlike for + generic classes, this is *not* the same as ``Final[Any]``. -* In class bodies and stub files you can omit the right hand side and just write +* In class bodies and stub files, you can omit the right-hand side and just write ``ID: Final[int]``. * Finally, you can write ``self.id: Final = 1`` (also optionally with a type in square brackets). This is allowed *only* in - :py:meth:`__init__ ` methods, so that the final instance attribute is + :py:meth:`__init__ ` methods so the final instance attribute is assigned only once when an instance is created. Details of using ``Final`` @@ -129,7 +129,7 @@ the scope of a final declaration automatically depending on whether it was initialized in the class body or in :py:meth:`__init__ `. A final attribute can't be overridden by a subclass (even with another -explicit final declaration). Note however that a final attribute can +explicit final declaration). Note, however, that a final attribute can override a read-only property: .. code-block:: python @@ -176,12 +176,12 @@ overriding. You can use the ``typing.final`` decorator for this purpose: This ``@final`` decorator can be used with instance methods, class methods, static methods, and properties. -For overloaded methods you should add ``@final`` on the implementation +For overloaded methods, you should add ``@final`` on the implementation to make it final (or on the first overload in stubs): .. code-block:: python - from typing import Any, overload + from typing import final, overload class Base: @overload @@ -224,7 +224,7 @@ Here are some situations where using a final class may be useful: An abstract class that defines at least one abstract method or property and has ``@final`` decorator will generate an error from -mypy, since those attributes could never be implemented. +mypy since those attributes could never be implemented. .. code-block:: python From 9518b6a96778b72f2f90dbd0edf7ae94837757d5 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Tue, 24 Sep 2024 08:26:32 +0200 Subject: [PATCH 0755/1617] Reject ParamSpec-typed callables calls with insufficient arguments (#17323) Fixes #14571. When type checking a call of a `ParamSpec`-typed callable, currently there is an incorrect "fast path" (if there are two arguments of shape `(*args: P.args, **kwargs: P.kwargs)`, accept), which breaks with `Concatenate` (such call was accepted even for `Concatenate[int, P]`). Also there was no checking that args and kwargs are actually present: since `*args` and `**kwargs` are not required, their absence was silently accepted. --- mypy/checkexpr.py | 23 +++- .../unit/check-parameter-specification.test | 111 ++++++++++++++++++ 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 22595c85e702..55c42335744d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1756,7 +1756,11 @@ def check_callable_call( ) param_spec = callee.param_spec() - if param_spec is not None and arg_kinds == [ARG_STAR, ARG_STAR2]: + if ( + param_spec is not None + and arg_kinds == [ARG_STAR, ARG_STAR2] + and len(formal_to_actual) == 2 + ): arg1 = self.accept(args[0]) arg2 = self.accept(args[1]) if ( @@ -2362,6 +2366,9 @@ def check_argument_count( # Positional argument when expecting a keyword argument. self.msg.too_many_positional_arguments(callee, context) ok = False + elif callee.param_spec() is not None and not formal_to_actual[i]: + self.msg.too_few_arguments(callee, context, actual_names) + ok = False return ok def check_for_extra_actual_arguments( @@ -2763,9 +2770,9 @@ def plausible_overload_call_targets( ) -> list[CallableType]: """Returns all overload call targets that having matching argument counts. - If the given args contains a star-arg (*arg or **kwarg argument), this method - will ensure all star-arg overloads appear at the start of the list, instead - of their usual location. + If the given args contains a star-arg (*arg or **kwarg argument, including + ParamSpec), this method will ensure all star-arg overloads appear at the start + of the list, instead of their usual location. The only exception is if the starred argument is something like a Tuple or a NamedTuple, which has a definitive "shape". If so, we don't move the corresponding @@ -2793,9 +2800,13 @@ def has_shape(typ: Type) -> bool: formal_to_actual = map_actuals_to_formals( arg_kinds, arg_names, typ.arg_kinds, typ.arg_names, lambda i: arg_types[i] ) - with self.msg.filter_errors(): - if self.check_argument_count( + if typ.param_spec() is not None: + # ParamSpec can be expanded in a lot of different ways. We may try + # to expand it here instead, but picking an impossible overload + # is safe: it will be filtered out later. + star_matches.append(typ) + elif self.check_argument_count( typ, arg_types, arg_kinds, arg_names, formal_to_actual, None ): if args_have_var_arg and typ.is_var_arg: diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 654f36775172..38fb62fe78e0 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2192,3 +2192,114 @@ parametrize(_test, Case(1, b=2), Case(3, b=4)) parametrize(_test, Case(1, 2), Case(3)) parametrize(_test, Case(1, 2), Case(3, b=4)) [builtins fixtures/paramspec.pyi] + +[case testRunParamSpecInsufficientArgs] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +_P = ParamSpec("_P") + +def run(predicate: Callable[_P, None], *args: _P.args, **kwargs: _P.kwargs) -> None: # N: "run" defined here + predicate() # E: Too few arguments + predicate(*args) # E: Too few arguments + predicate(**kwargs) # E: Too few arguments + predicate(*args, **kwargs) + +def fn() -> None: ... +def fn_args(x: int) -> None: ... +def fn_posonly(x: int, /) -> None: ... + +run(fn) +run(fn_args, 1) +run(fn_args, x=1) +run(fn_posonly, 1) +run(fn_posonly, x=1) # E: Unexpected keyword argument "x" for "run" + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecConcatenateInsufficientArgs] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +_P = ParamSpec("_P") + +def run(predicate: Callable[Concatenate[int, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None: # N: "run" defined here + predicate() # E: Too few arguments + predicate(1) # E: Too few arguments + predicate(1, *args) # E: Too few arguments + predicate(1, *args) # E: Too few arguments + predicate(1, **kwargs) # E: Too few arguments + predicate(*args, **kwargs) # E: Argument 1 has incompatible type "*_P.args"; expected "int" + predicate(1, *args, **kwargs) + +def fn() -> None: ... +def fn_args(x: int, y: str) -> None: ... +def fn_posonly(x: int, /) -> None: ... +def fn_posonly_args(x: int, /, y: str) -> None: ... + +run(fn) # E: Argument 1 to "run" has incompatible type "Callable[[], None]"; expected "Callable[[int], None]" +run(fn_args, 1, 'a') # E: Too many arguments for "run" \ + # E: Argument 2 to "run" has incompatible type "int"; expected "str" +run(fn_args, y='a') +run(fn_args, 'a') +run(fn_posonly) +run(fn_posonly, x=1) # E: Unexpected keyword argument "x" for "run" +run(fn_posonly_args) # E: Missing positional argument "y" in call to "run" +run(fn_posonly_args, 'a') +run(fn_posonly_args, y='a') + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecConcatenateInsufficientArgsInDecorator] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +P = ParamSpec("P") + +def decorator(fn: Callable[Concatenate[str, P], None]) -> Callable[P, None]: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: + fn("value") # E: Too few arguments + fn("value", *args) # E: Too few arguments + fn("value", **kwargs) # E: Too few arguments + fn(*args, **kwargs) # E: Argument 1 has incompatible type "*P.args"; expected "str" + fn("value", *args, **kwargs) + return inner + +@decorator +def foo(s: str, s2: str) -> None: ... + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecOverload] +from typing_extensions import ParamSpec +from typing import Callable, NoReturn, TypeVar, Union, overload + +P = ParamSpec("P") +T = TypeVar("T") + +@overload +def capture( + sync_fn: Callable[P, NoReturn], + *args: P.args, + **kwargs: P.kwargs, +) -> int: ... +@overload +def capture( + sync_fn: Callable[P, T], + *args: P.args, + **kwargs: P.kwargs, +) -> Union[T, int]: ... +def capture( + sync_fn: Callable[P, T], + *args: P.args, + **kwargs: P.kwargs, +) -> Union[T, int]: + return sync_fn(*args, **kwargs) + +def fn() -> str: return '' +def err() -> NoReturn: ... + +reveal_type(capture(fn)) # N: Revealed type is "Union[builtins.str, builtins.int]" +reveal_type(capture(err)) # N: Revealed type is "builtins.int" + +[builtins fixtures/paramspec.pyi] From ecfab6a02415c46eda5717ec6ee9bfac8115c1e9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 24 Sep 2024 15:30:23 +0100 Subject: [PATCH 0756/1617] [PEP 695] Allow Self return types with contravariance (#17786) Fix variance inference in this fragment from a typing conformance test: ``` class ClassA[T1, T2, T3](list[T1]): def method1(self, a: T2) -> None: ... def method2(self) -> T3: ... ``` Previously T2 was incorrectly inferred as invariant due to `list` having methods that return `Self`. Be more flexible with return types to allow inferring contravariance for type variables even if there are `Self` return types, in particular. We could probably make this even more lenient, but after thinking about this for a while, I wasn't sure what the most general rule would be, so I decided to just make a tweak to support the likely most common use case (which is probably actually not that common either). Link to conformance test: https://github.com/python/typing/blob/main/conformance/tests/generics_variance_inference.py#L15C1-L20C12 --- mypy/subtypes.py | 27 ++++++++++++++ test-data/unit/check-python312.test | 58 ++++++++++++++++++++++++++++- test-data/unit/pythoneval.test | 20 ++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index df040dcb1311..c76b3569fdd4 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -2024,6 +2024,16 @@ def infer_variance(info: TypeInfo, i: int) -> bool: typ = find_member(member, self_type, self_type) if typ: + # It's okay for a method in a generic class with a contravariant type + # variable to return a generic instance of the class, if it doesn't involve + # variance (i.e. values of type variables are propagated). Our normal rules + # would disallow this. Replace such return types with 'Any' to allow this. + # + # This could probably be more lenient (e.g. allow self type be nested, don't + # require all type arguments to be identical to self_type), but this will + # hopefully cover the vast majority of such cases, including Self. + typ = erase_return_self_types(typ, self_type) + typ2 = expand_type(typ, {tvar.id: object_type}) if not is_subtype(typ, typ2): co = False @@ -2066,3 +2076,20 @@ def infer_class_variances(info: TypeInfo) -> bool: if not infer_variance(info, i): success = False return success + + +def erase_return_self_types(typ: Type, self_type: Instance) -> Type: + """If a typ is function-like and returns self_type, replace return type with Any.""" + proper_type = get_proper_type(typ) + if isinstance(proper_type, CallableType): + ret = get_proper_type(proper_type.ret_type) + if isinstance(ret, Instance) and ret == self_type: + return proper_type.copy_modified(ret_type=AnyType(TypeOfAny.implementation_artifact)) + elif isinstance(proper_type, Overloaded): + return Overloaded( + [ + cast(CallableType, erase_return_self_types(it, self_type)) + for it in proper_type.items + ] + ) + return typ diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 2f0b912c439e..085cc052705d 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -213,7 +213,7 @@ b: Invariant[int] if int(): a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") if int(): - b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") + b = a c: Covariant[object] d: Covariant[int] @@ -393,6 +393,62 @@ inv3_1: Invariant3[float] = Invariant3[int](1) # E: Incompatible types in assig inv3_2: Invariant3[int] = Invariant3[float](1) # E: Incompatible types in assignment (expression has type "Invariant3[float]", variable has type "Invariant3[int]") [builtins fixtures/property.pyi] +[case testPEP695InferVarianceWithInheritedSelf] +from typing import overload, Self, TypeVar, Generic + +T = TypeVar("T") +S = TypeVar("S") + +class C(Generic[T]): + def f(self, x: T) -> Self: ... + def g(self) -> T: ... + +class D[T1, T2](C[T1]): + def m(self, x: T2) -> None: ... + +a1: D[int, int] = D[int, object]() +a2: D[int, object] = D[int, int]() # E: Incompatible types in assignment (expression has type "D[int, int]", variable has type "D[int, object]") +a3: D[int, int] = D[object, object]() # E: Incompatible types in assignment (expression has type "D[object, object]", variable has type "D[int, int]") +a4: D[object, int] = D[int, object]() # E: Incompatible types in assignment (expression has type "D[int, object]", variable has type "D[object, int]") + +[case testPEP695InferVarianceWithReturnSelf] +from typing import Self, overload + +class Cov[T]: + def f(self) -> Self: ... + +a1: Cov[int] = Cov[float]() # E: Incompatible types in assignment (expression has type "Cov[float]", variable has type "Cov[int]") +a2: Cov[float] = Cov[int]() + +class Contra[T]: + def f(self) -> Self: ... + def g(self, x: T) -> None: ... + +b1: Contra[int] = Contra[float]() +b2: Contra[float] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[float]") + +class Cov2[T]: + @overload + def f(self, x): ... + @overload + def f(self) -> Self: ... + def f(self, x=None): ... + +c1: Cov2[int] = Cov2[float]() # E: Incompatible types in assignment (expression has type "Cov2[float]", variable has type "Cov2[int]") +c2: Cov2[float] = Cov2[int]() + +class Contra2[T]: + @overload + def f(self, x): ... + @overload + def f(self) -> Self: ... + def f(self, x=None): ... + + def g(self, x: T) -> None: ... + +d1: Contra2[int] = Contra2[float]() +d2: Contra2[float] = Contra2[int]() # E: Incompatible types in assignment (expression has type "Contra2[int]", variable has type "Contra2[float]") + [case testPEP695InheritInvariant] class Invariant[T]: x: T diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 26fc419030bf..89f01bff963e 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2196,3 +2196,23 @@ type K4 = None | B[int] type L1 = Never type L2 = list[Never] + +[case testPEP695VarianceInferenceSpecialCaseWithTypeshed] +# flags: --python-version=3.12 +class C1[T1, T2](list[T1]): + def m(self, a: T2) -> None: ... + +def func1(p: C1[int, object]): + x: C1[int, int] = p + +class C2[T1, T2, T3](dict[T2, T3]): + def m(self, a: T1) -> None: ... + +def func2(p: C2[object, int, int]): + x: C2[int, int, int] = p + +class C3[T1, T2](tuple[T1, ...]): + def m(self, a: T2) -> None: ... + +def func3(p: C3[int, object]): + x: C3[int, int] = p From 5c5e0511116868a893b13052d4e2a8b6837bb9f0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:07:37 -0700 Subject: [PATCH 0757/1617] Test against latest Python 3.13, make testing 3.14 easy (#17812) --- .github/workflows/test.yml | 82 +++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01d5876635b0..6e29feef7b00 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,6 +71,22 @@ jobs: tox_extra_args: "-n 4" test_mypyc: true + - name: Test suit with py313-dev-ubuntu, mypyc-compiled + python: '3.13-dev' + arch: x64 + os: ubuntu-latest + toxenv: py + tox_extra_args: "-n 4" + test_mypyc: true + # - name: Test suit with py314-dev-ubuntu + # python: '3.14-dev' + # arch: x64 + # os: ubuntu-latest + # toxenv: py + # tox_extra_args: "-n 4" + # allow_failure: true + # test_mypyc: true + - name: mypyc runtime tests with py39-macos python: '3.9.18' arch: x64 @@ -119,12 +135,10 @@ jobs: MYPY_FORCE_TERMINAL_WIDTH: 200 # Pytest PYTEST_ADDOPTS: --color=yes + steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - architecture: ${{ matrix.arch }} + - name: Debug build if: ${{ matrix.debug_build }} run: | @@ -132,38 +146,58 @@ jobs: PYTHONDIR=~/python-debug/python-$PYTHONVERSION VENV=$PYTHONDIR/env ./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV + # TODO: does this do anything? env vars aren't passed to the next step right source $VENV/bin/activate + - name: Latest Dev build + if: ${{ endsWith(matrix.python, '-dev') }} + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential gdb lcov libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses5-dev \ + libreadline6-dev libsqlite3-dev libssl-dev lzma lzma-dev tk-dev uuid-dev zlib1g-dev + git clone --depth 1 https://github.com/python/cpython.git /tmp/cpython --branch $( echo ${{ matrix.python }} | sed 's/-dev//' ) + cd /tmp/cpython + echo git rev-parse HEAD; git rev-parse HEAD + git show --no-patch + ./configure --prefix=/opt/pythondev + make -j$(nproc) + sudo make install + sudo ln -s /opt/pythondev/bin/python3 /opt/pythondev/bin/python + sudo ln -s /opt/pythondev/bin/pip3 /opt/pythondev/bin/pip + echo "/opt/pythondev/bin" >> $GITHUB_PATH + - uses: actions/setup-python@v5 + if: ${{ !(matrix.debug_build || endsWith(matrix.python, '-dev')) }} + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.arch }} + - name: Install tox - run: pip install setuptools==68.2.2 tox==4.11.0 + run: | + echo PATH; echo $PATH + echo which python; which python + echo which pip; which pip + echo python version; python -c 'import sys; print(sys.version)' + echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))' + echo os.cpu_count; python -c 'import os; print(os.cpu_count())' + echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' + pip install setuptools==68.2.2 tox==4.11.0 + - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} run: | pip install -r test-requirements.txt CC=clang MYPYC_OPT_LEVEL=0 MYPY_USE_MYPYC=1 pip install -e . + - name: Setup tox environment run: | - tox run -e ${{ matrix.toxenv }} --notest - python -c 'import os; print("os.cpu_count", os.cpu_count(), "os.sched_getaffinity", len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' + tox run -e ${{ matrix.toxenv }} --notes - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} + continue-on-error: ${{ matrix.allow_failure == 'true' }} - python-nightly: - runs-on: ubuntu-latest - name: Test suite with Python nightly - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.13-dev' - - name: Install tox - run: pip install setuptools==68.2.2 tox==4.11.0 - - name: Setup tox environment - run: tox run -e py --notest - - name: Test - run: tox run -e py --skip-pkg-install -- "-n 4" - continue-on-error: true - - name: Mark as a success - run: exit 0 + - name: Mark as success (check failures manually) + if: ${{ matrix.allow_failure == 'true' }} + run: exit 0 python_32bits: runs-on: ubuntu-latest From 58825f748096797cd4e844a4d6e3b161556cf749 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 25 Sep 2024 12:45:57 +0100 Subject: [PATCH 0758/1617] [PEP 695] Document Python 3.12 type parameter syntax (#17816) Provide examples using both syntax variants, and give both of the syntax variants similar prominence. It's likely that both syntax variants will continue to be widely used for a long time. Also adjust terminology (e.g. use 'type parameter' / 'type argument' more consistently), since otherwise some descriptions would be unclear. I didn't update examples outside the generics chapter. I'll do this in a follow-up PR. Work on #17810. --------- Co-authored-by: Jelle Zijlstra Co-authored-by: Brian Schubert --- docs/source/generics.rst | 785 ++++++++++++++++++++++++++++++--------- 1 file changed, 619 insertions(+), 166 deletions(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 01ae7534ba93..4eb6463e4bd4 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -2,7 +2,7 @@ Generics ======== This section explains how you can define your own generic classes that take -one or more type parameters, similar to built-in types such as ``list[X]``. +one or more type arguments, similar to built-in types such as ``list[T]``. User-defined generics are a moderately advanced feature and you can get far without ever using them -- feel free to skip this section and come back later. @@ -12,18 +12,48 @@ Defining generic classes ************************ The built-in collection classes are generic classes. Generic types -have one or more type parameters, which can be arbitrary types. For -example, ``dict[int, str]`` has the type parameters ``int`` and -``str``, and ``list[int]`` has a type parameter ``int``. +accept one or more type arguments within ``[...]``, which can be +arbitrary types. For example, the type ``dict[int, str]`` has the +type arguments ``int`` and ``str``, and ``list[int]`` has the type +argument ``int``. Programs can also define new generic classes. Here is a very simple -generic class that represents a stack: +generic class that represents a stack (using the syntax introduced in +Python 3.12): + +.. code-block:: python + + class Stack[T]: + def __init__(self) -> None: + # Create an empty list with items of type T + self.items: list[T] = [] + + def push(self, item: T) -> None: + self.items.append(item) + + def pop(self) -> T: + return self.items.pop() + + def empty(self) -> bool: + return not self.items + +There are two syntax variants for defining generic classes in Python. +Python 3.12 introduced a +`new dedicated syntax `_ +for defining generic classes (and also functions and type aliases, which +we will discuss later). The above example used the new syntax. Most examples are +given using both the new and the old (or legacy) syntax variants. +Unless mentioned otherwise, they work the same -- but the new syntax +is more readable and more convenient. + +Here is the same example using the old syntax (required for Python 3.11 +and earlier, but also supported on newer Python versions): .. code-block:: python from typing import TypeVar, Generic - T = TypeVar('T') + T = TypeVar('T') # Define type variable "T" class Stack(Generic[T]): def __init__(self) -> None: @@ -39,8 +69,16 @@ generic class that represents a stack: def empty(self) -> bool: return not self.items +.. note:: + + There are currently no plans to deprecate the legacy syntax. + You can freely mix code using the new and old syntax variants, + even within a single file (but *not* within a single class). + The ``Stack`` class can be used to represent a stack of any type: -``Stack[int]``, ``Stack[tuple[int, str]]``, etc. +``Stack[int]``, ``Stack[tuple[int, str]]``, etc. You can think of +``Stack[int]`` as referring to the definition of ``Stack`` above, +but with all instances of ``T`` replaced with ``int``. Using ``Stack`` is similar to built-in container types: @@ -50,19 +88,49 @@ Using ``Stack`` is similar to built-in container types: stack = Stack[int]() stack.push(2) stack.pop() - stack.push('x') # error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" -Construction of instances of generic types is type checked: + # error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" + stack.push('x') + + stack2: Stack[str] = Stack() + stack2.append('x') + +Construction of instances of generic types is type checked (Python 3.12 syntax): .. code-block:: python - class Box(Generic[T]): + class Box[T]: def __init__(self, content: T) -> None: self.content = content Box(1) # OK, inferred type is Box[int] Box[int](1) # Also OK - Box[int]('some string') # error: Argument 1 to "Box" has incompatible type "str"; expected "int" + + # error: Argument 1 to "Box" has incompatible type "str"; expected "int" + Box[int]('some string') + +Here is the definition of ``Box`` using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Box(Generic[T]): + def __init__(self, content: T) -> None: + self.content = content + +.. note:: + + Before moving on, let's clarify some terminology. + The name ``T`` in ``class Stack[T]`` or ``class Stack(Generic[T])`` + declares a *type parameter* ``T`` (of class ``Stack``). + ``T`` is also called a *type variable*, especially in a type annotation, + such as in the signature of ``push`` above. + When the type ``Stack[...]`` is used in a type annotation, the type + within square brackets is called a *type argument*. + This is similar to the distinction between function parameters and arguments. .. _generic-subclasses: @@ -70,7 +138,37 @@ Defining subclasses of generic classes ************************************** User-defined generic classes and generic classes defined in :py:mod:`typing` -can be used as a base class for another class (generic or non-generic). For example: +can be used as a base class for another class (generic or non-generic). For +example (Python 3.12 syntax): + +.. code-block:: python + + from typing import Mapping, Iterator + + # This is a generic subclass of Mapping + class MyMapp[KT, VT](Mapping[KT, VT]): + def __getitem__(self, k: KT) -> VT: ... + def __iter__(self) -> Iterator[KT]: ... + def __len__(self) -> int: ... + + items: MyMap[str, int] # OK + + # This is a non-generic subclass of dict + class StrDict(dict[str, str]): + def __str__(self) -> str: + return f'StrDict({super().__str__()})' + + data: StrDict[int, int] # Error! StrDict is not generic + data2: StrDict # OK + + # This is a user-defined generic class + class Receiver[T]: + def accept(self, value: T) -> None: ... + + # This is a generic subclass of Receiver + class AdvancedReceiver[T](Receiver[T]): ... + +Here is the above example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -92,7 +190,6 @@ can be used as a base class for another class (generic or non-generic). For exam def __str__(self) -> str: return f'StrDict({super().__str__()})' - data: StrDict[int, int] # Error! StrDict is not generic data2: StrDict # OK @@ -105,25 +202,27 @@ can be used as a base class for another class (generic or non-generic). For exam .. note:: - You have to add an explicit :py:class:`~typing.Mapping` base class + You have to add an explicit :py:class:`~collections.abc.Mapping` base class if you want mypy to consider a user-defined class as a mapping (and - :py:class:`~typing.Sequence` for sequences, etc.). This is because mypy doesn't use - *structural subtyping* for these ABCs, unlike simpler protocols - like :py:class:`~typing.Iterable`, which use :ref:`structural subtyping `. + :py:class:`~collections.abc.Sequence` for sequences, etc.). This is because + mypy doesn't use *structural subtyping* for these ABCs, unlike simpler protocols + like :py:class:`~collections.abc.Iterable`, which use + :ref:`structural subtyping `. -:py:class:`Generic ` can be omitted from bases if there are +When using the legacy syntax, :py:class:`Generic ` can be omitted +from bases if there are other base classes that include type variables, such as ``Mapping[KT, VT]`` in the above example. If you include ``Generic[...]`` in bases, then it should list all type variables present in other bases (or more, -if needed). The order of type variables is defined by the following +if needed). The order of type parameters is defined by the following rules: -* If ``Generic[...]`` is present, then the order of variables is +* If ``Generic[...]`` is present, then the order of parameters is always determined by their order in ``Generic[...]``. -* If there are no ``Generic[...]`` in bases, then all type variables +* If there are no ``Generic[...]`` in bases, then all type parameters are collected in the lexicographic order (i.e. by first appearance). -For example: +Example: .. code-block:: python @@ -142,12 +241,26 @@ For example: x: First[int, str] # Here T is bound to int, S is bound to str y: Second[int, str, Any] # Here T is Any, S is int, and U is str +When using the Python 3.12 syntax, all type parameters must always be +explicitly defined immediately after the class name within ``[...]``, and the +``Generic[...]`` base class is never used. + .. _generic-functions: Generic functions ***************** -Type variables can be used to define generic functions: +Functions can also be generic, i.e. they can have type parameters (Python 3.12 syntax): + +.. code-block:: python + + from collections.abc import Sequence + + # A generic function! + def first[T](seq: Sequence[T]) -> T: + return seq[0] + +Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -159,24 +272,25 @@ Type variables can be used to define generic functions: def first(seq: Sequence[T]) -> T: return seq[0] -As with generic classes, the type variable can be replaced with any -type. That means ``first`` can be used with any sequence type, and the -return type is derived from the sequence item type. For example: +As with generic classes, the type parameter ``T`` can be replaced with any +type. That means ``first`` can be passed an argument with any sequence type, +and the return type is derived from the sequence item type. Example: .. code-block:: python reveal_type(first([1, 2, 3])) # Revealed type is "builtins.int" - reveal_type(first(['a', 'b'])) # Revealed type is "builtins.str" + reveal_type(first(('a', 'b'))) # Revealed type is "builtins.str" -Note also that a single definition of a type variable (such as ``T`` -above) can be used in multiple generic functions or classes. In this -example we use the same type variable in two generic functions: +When using the legacy syntax, a single definition of a type variable +(such as ``T`` above) can be used in multiple generic functions or +classes. In this example we use the same type variable in two generic +functions to declarare type parameters: .. code-block:: python from typing import TypeVar, Sequence - T = TypeVar('T') # Declare type variable + T = TypeVar('T') # Define type variable def first(seq: Sequence[T]) -> T: return seq[0] @@ -184,20 +298,62 @@ example we use the same type variable in two generic functions: def last(seq: Sequence[T]) -> T: return seq[-1] +Since the Python 3.12 syntax is more concise, it doesn't need (or have) +an equivalent way of sharing type parameter definitions. + A variable cannot have a type variable in its type unless the type variable is bound in a containing generic class or function. +When calling a generic function, you can't explicitly pass the values of +type parameters as type arguments. The values of type parameters are always +inferred by mypy. This is not valid: + +.. code-block:: python + + first[int]([1, 2]) # Error: can't use [...] with generic function + +If you really need this, you can define a generic class with a ``__call__`` +method. + .. _generic-methods-and-generic-self: Generic methods and generic self ******************************** -You can also define generic methods — just use a type variable in the -method signature that is different from class type variables. In -particular, the ``self`` argument may also be generic, allowing a +You can also define generic methods. In +particular, the ``self`` parameter may also be generic, allowing a method to return the most precise type known at the point of access. In this way, for example, you can type check a chain of setter -methods: +methods (Python 3.12 syntax): + +.. code-block:: python + + class Shape: + def set_scale[T: Shape](self: T, scale: float) -> T: + self.scale = scale + return self + + class Circle(Shape): + def set_radius(self, r: float) -> 'Circle': + self.radius = r + return self + + class Square(Shape): + def set_width(self, w: float) -> 'Square': + self.width = w + return self + + circle: Circle = Circle().set_scale(0.5).set_radius(2.7) + square: Square = Square().set_scale(0.5).set_width(3.2) + +Without using generic ``self``, the last two lines could not be type +checked properly, since the return type of ``set_scale`` would be +``Shape``, which doesn't define ``set_radius`` or ``set_width``. + +When using the legacy syntax, just use a type variable in the +method signature that is different from class type parameters (if any +are defined). Here is the above example using the legacy +syntax (3.11 and earlier): .. code-block:: python @@ -223,24 +379,41 @@ methods: circle: Circle = Circle().set_scale(0.5).set_radius(2.7) square: Square = Square().set_scale(0.5).set_width(3.2) -Without using generic ``self``, the last two lines could not be type -checked properly, since the return type of ``set_scale`` would be -``Shape``, which doesn't define ``set_radius`` or ``set_width``. +Other uses include factory methods, such as copy and deserialization methods. +For class methods, you can also define generic ``cls``, using ``type[T]`` +or :py:class:`Type[T] ` (Python 3.12 syntax): + +.. code-block:: python + + class Friend: + other: "Friend | None" = None + + @classmethod + def make_pair[T: Friend](cls: type[T]) -> tuple[T, T]: + a, b = cls(), cls() + a.other = b + b.other = a + return a, b -Other uses are factory methods, such as copy and deserialization. -For class methods, you can also define generic ``cls``, using :py:class:`Type[T] `: + class SuperFriend(Friend): + pass + + a, b = SuperFriend.make_pair() + +Here is the same example using the legacy syntax (3.11 and earlier, though +3.9 and later can use lower-case ``type[T]``): .. code-block:: python - from typing import TypeVar, Type + from typing import TypeVar T = TypeVar('T', bound='Friend') class Friend: - other: "Friend" = None + other: "Friend | None" = None @classmethod - def make_pair(cls: Type[T]) -> tuple[T, T]: + def make_pair(cls: type[T]) -> tuple[T, T]: a, b = cls(), cls() a.other = b b.other = a @@ -262,16 +435,13 @@ possibly by making use of the ``Any`` type or a ``# type: ignore`` comment. Note that mypy lets you use generic self types in certain unsafe ways in order to support common idioms. For example, using a generic -self type in an argument type is accepted even though it's unsafe: +self type in an argument type is accepted even though it's unsafe (Python 3.12 +syntax): .. code-block:: python - from typing import TypeVar - - T = TypeVar("T") - class Base: - def compare(self: T, other: T) -> bool: + def compare[T: Base](self: T, other: T) -> bool: return False class Sub(Base): @@ -280,7 +450,7 @@ self type in an argument type is accepted even though it's unsafe: # This is unsafe (see below) but allowed because it's # a common pattern and rarely causes issues in practice. - def compare(self, other: Sub) -> bool: + def compare(self, other: 'Sub') -> bool: return self.x > other.x b: Base = Sub(42) @@ -293,12 +463,12 @@ Automatic self types using typing.Self Since the patterns described above are quite common, mypy supports a simpler syntax, introduced in :pep:`673`, to make them easier to use. -Instead of defining a type variable and using an explicit annotation +Instead of introducing a type parameter and using an explicit annotation for ``self``, you can import the special type ``typing.Self`` that is -automatically transformed into a type variable with the current class -as the upper bound, and you don't need an annotation for ``self`` (or -``cls`` in class methods). The example from the previous section can -be made simpler by using ``Self``: +automatically transformed into a method-level type parameter with the +current class as the upper bound, and you don't need an annotation for +``self`` (or ``cls`` in class methods). The example from the previous +section can be made simpler by using ``Self``: .. code-block:: python @@ -319,7 +489,7 @@ be made simpler by using ``Self``: a, b = SuperFriend.make_pair() -This is more compact than using explicit type variables. Also, you can +This is more compact than using explicit type parameters. Also, you can use ``Self`` in attribute annotations in addition to methods. .. note:: @@ -354,8 +524,8 @@ Let us illustrate this by few simple examples: class Triangle(Shape): ... class Square(Shape): ... -* Most immutable containers, such as :py:class:`~typing.Sequence` and - :py:class:`~typing.FrozenSet` are covariant. :py:data:`~typing.Union` is +* Most immutable container types, such as :py:class:`~collections.abc.Sequence` + and :py:class:`~frozenset` are covariant. :py:data:`~typing.Union` is also covariant in all variables: ``Union[Triangle, int]`` is a subtype of ``Union[Shape, int]``. @@ -367,7 +537,7 @@ Let us illustrate this by few simple examples: triangles: Sequence[Triangle] count_lines(triangles) # OK - def foo(triangle: Triangle, num: int): + def foo(triangle: Triangle, num: int) -> None: shape_or_number: Union[Shape, int] # a Triangle is a Shape, and a Shape is a valid Union[Shape, int] shape_or_number = triangle @@ -400,8 +570,8 @@ Let us illustrate this by few simple examples: triangle. If we give it a callable that can calculate the area of an arbitrary shape (not just triangles), everything still works. -* :py:class:`~typing.List` is an invariant generic type. Naively, one would think - that it is covariant, like :py:class:`~typing.Sequence` above, but consider this code: +* ``list`` is an invariant generic type. Naively, one would think + that it is covariant, like :py:class:`~collections.abc.Sequence` above, but consider this code: .. code-block:: python @@ -416,13 +586,48 @@ Let us illustrate this by few simple examples: add_one(my_circles) # This may appear safe, but... my_circles[-1].rotate() # ...this will fail, since my_circles[0] is now a Shape, not a Circle - Another example of invariant type is :py:class:`~typing.Dict`. Most mutable containers + Another example of invariant type is ``dict``. Most mutable containers are invariant. -By default, mypy assumes that all user-defined generics are invariant. -To declare a given generic class as covariant or contravariant use -type variables defined with special keyword arguments ``covariant`` or -``contravariant``. For example: +When using the Python 3.12 syntax for generics, mypy will automatically +infer the most flexible variance for each class type variable. Here +``Box`` will be inferred as covariant: + +.. code-block:: python + + class Box[T]: # this type is implilicitly covariant + def __init__(self, content: T) -> None: + self._content = content + + def get_content(self) -> T: + return self._content + + def look_into(box: Box[Shape]): ... + + my_box = Box(Square()) + look_into(my_box) # OK, but mypy would complain here for an invariant type + +Here the underscore prefix for ``_content`` is significant. Without an +underscore prefix, the class would be invariant, as the attribute would +be understood as a public, mutable attribute (a single underscore prefix +has no special significance for mypy in most other contexts). By declaring +the attribute as ``Final``, the class could still be made covariant: + +.. code-block:: python + + from typing import Final + + class Box[T]: # this type is implilicitly covariant + def __init__(self, content: T) -> None: + self.content: Final = content + + def get_content(self) -> T: + return self._content + +When using the legacy syntax, mypy assumes that all user-defined generics +are invariant by default. To declare a given generic class as covariant or +contravariant, use type variables defined with special keyword arguments +``covariant`` or ``contravariant``. For example (Python 3.11 or earlier): .. code-block:: python @@ -437,9 +642,9 @@ type variables defined with special keyword arguments ``covariant`` or def get_content(self) -> T_co: return self._content - def look_into(box: Box[Animal]): ... + def look_into(box: Box[Shape]): ... - my_box = Box(Cat()) + my_box = Box(Square()) look_into(my_box) # OK, but mypy would complain here for an invariant type .. _type-variable-upper-bound: @@ -449,24 +654,32 @@ Type variables with upper bounds A type variable can also be restricted to having values that are subtypes of a specific type. This type is called the upper bound of -the type variable, and is specified with the ``bound=...`` keyword -argument to :py:class:`~typing.TypeVar`. +the type variable, and it is specified using ``T: `` when using the +Python 3.12 syntax. In the definition of a generic function that uses +such a type variable ``T``, the type represented by ``T`` is assumed +to be a subtype of its upper bound, so the function can use methods +of the upper bound on values of type ``T`` (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar, SupportsAbs + from typing import SupportsAbs - T = TypeVar('T', bound=SupportsAbs[float]) + def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T: + # Okay, because T is a subtype of SupportsAbs[float]. + return max(xs, key=abs) -In the definition of a generic function that uses such a type variable -``T``, the type represented by ``T`` is assumed to be a subtype of -its upper bound, so the function can use methods of the upper bound on -values of type ``T``. +An upper bound can also be specified with the ``bound=...`` keyword +argument to :py:class:`~typing.TypeVar`. +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - def largest_in_absolute_value(*xs: T) -> T: - return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. + from typing import TypeVar, SupportsAbs + + T = TypeVar('T', bound=SupportsAbs[float]) + + def max_by_abs(*xs: T) -> T: + return max(xs, key=abs) In a call to such a function, the type ``T`` must be replaced by a type that is a subtype of its upper bound. Continuing the example @@ -474,9 +687,9 @@ above: .. code-block:: python - largest_in_absolute_value(-3.5, 2) # Okay, has type float. - largest_in_absolute_value(5+6j, 7) # Okay, has type complex. - largest_in_absolute_value('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]. + max_by_abs(-3.5, 2) # Okay, has type float. + max_by_abs(5+6j, 7) # Okay, has type complex. + max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]. Type parameters of generic classes may also have upper bounds, which restrict the valid values for the type parameter in the same way. @@ -489,34 +702,34 @@ Type variables with value restriction By default, a type variable can be replaced with any type. However, sometimes it's useful to have a type variable that can only have some specific types as its value. A typical example is a type variable that can only have values -``str`` and ``bytes``: +``str`` and ``bytes``. This lets us define a function that can concatenate +two strings or bytes objects, but it can't be called with other argument +types (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar + def concat[S: (str, bytes)](x: S, y: S) -> S: + return x + y - AnyStr = TypeVar('AnyStr', str, bytes) + concat('a', 'b') # Okay + concat(b'a', b'b') # Okay + concat(1, 2) # Error! -This is actually such a common type variable that :py:data:`~typing.AnyStr` is -defined in :py:mod:`typing` and we don't need to define it ourselves. -We can use :py:data:`~typing.AnyStr` to define a function that can concatenate -two strings or bytes objects, but it can't be called with other -argument types: +The same thing is also possibly using the legacy syntax (Python 3.11 or earlier): .. code-block:: python - from typing import AnyStr + from typing import TypeVar + + AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: return x + y - concat('a', 'b') # Okay - concat(b'a', b'b') # Okay - concat(1, 2) # Error! - -Importantly, this is different from a union type, since combinations -of ``str`` and ``bytes`` are not accepted: +No matter which syntax you use, such a type variable is called a type variable +with a value restriction. Importantly, this is different from a union type, +since combinations of ``str`` and ``bytes`` are not accepted: .. code-block:: python @@ -524,11 +737,11 @@ of ``str`` and ``bytes`` are not accepted: In this case, this is exactly what we want, since it's not possible to concatenate a string and a bytes object! If we tried to use -``Union``, the type checker would complain about this possibility: +a union type, the type checker would complain about this possibility: .. code-block:: python - def union_concat(x: Union[str, bytes], y: Union[str, bytes]) -> Union[str, bytes]: + def union_concat(x: str | bytes, y: str | bytes) -> str | bytes: return x + y # Error: can't concatenate str and bytes Another interesting special case is calling ``concat()`` with a @@ -556,13 +769,19 @@ this is correct for ``concat``, since ``concat`` actually returns a >>> print(type(ss)) -You can also use a :py:class:`~typing.TypeVar` with a restricted set of possible -values when defining a generic class. For example, mypy uses the type -:py:class:`Pattern[AnyStr] ` for the return value of :py:func:`re.compile`, -since regular expressions can be based on a string or a bytes pattern. +You can also use type variables with a restricted set of possible +values when defining a generic class. For example, the type +:py:class:`Pattern[S] ` is used for the return +value of :py:func:`re.compile`, where ``S`` can be either ``str`` +or ``bytes``. Regular expressions can be based on a string or a +bytes pattern. + +A type variable may not have both a value restriction and an upper bound +(see :ref:`type-variable-upper-bound`). -A type variable may not have both a value restriction (see -:ref:`type-variable-upper-bound`) and an upper bound. +Note that you may come across :py:data:`~typing.AnyStr` imported from +:py:mod:`typing`. This feature is now deprecated, but it means the same +as our definition of ``AnyStr`` above. .. _declaring-decorators: @@ -571,11 +790,12 @@ Declaring decorators Decorators are typically functions that take a function as an argument and return another function. Describing this behaviour in terms of types can -be a little tricky; we'll show how you can use ``TypeVar`` and a special +be a little tricky; we'll show how you can use type variables and a special kind of type variable called a *parameter specification* to do so. Suppose we have the following decorator, not type annotated yet, -that preserves the original function's signature and merely prints the decorated function's name: +that preserves the original function's signature and merely prints the decorated +function's name: .. code-block:: python @@ -585,7 +805,7 @@ that preserves the original function's signature and merely prints the decorated return func(*args, **kwds) return wrapper -and we use it to decorate function ``add_forty_two``: +We can use it to decorate function ``add_forty_two``: .. code-block:: python @@ -611,7 +831,28 @@ Note that class decorators are handled differently than function decorators in mypy: decorating a class does not erase its type, even if the decorator has incomplete type annotations. -Here's how one could annotate the decorator: +Here's how one could annotate the decorator (Python 3.12 syntax): + +.. code-block:: python + + from typing import Any, Callable, cast + + # A decorator that preserves the signature. + def printing_decorator[F: Callable[..., Any]](func: F) -> F: + def wrapper(*args, **kwds): + print("Calling", func) + return func(*args, **kwds) + return cast(F, wrapper) + + @printing_decorator + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + reveal_type(a) # Revealed type is "builtins.int" + add_forty_two('x') # Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -636,15 +877,28 @@ Here's how one could annotate the decorator: This still has some shortcomings. First, we need to use the unsafe :py:func:`~typing.cast` to convince mypy that ``wrapper()`` has the same -signature as ``func``. See :ref:`casts `. +signature as ``func`` (see :ref:`casts `). Second, the ``wrapper()`` function is not tightly type checked, although wrapper functions are typically small enough that this is not a big problem. This is also the reason for the :py:func:`~typing.cast` call in the ``return`` statement in ``printing_decorator()``. -However, we can use a parameter specification (:py:class:`~typing.ParamSpec`), -for a more faithful type annotation: +However, we can use a parameter specification, introduced using ``**P``, +for a more faithful type annotation (Python 3.12 syntax): + +.. code-block:: python + + from typing import Callable + + def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func) + return func(*args, **kwds) + return wrapper + +The same is possible using the legacy syntax with :py:class:`~typing.ParamSpec` +(Python 3.11 and earlier): .. code-block:: python @@ -661,7 +915,27 @@ for a more faithful type annotation: return wrapper Parameter specifications also allow you to describe decorators that -alter the signature of the input function: +alter the signature of the input function (Python 3.12 syntax): + +.. code-block:: python + + from typing import Callable + + # We reuse 'P' in the return type, but replace 'T' with 'str' + def stringify[**P, T](func: Callable[P, T]) -> Callable[P, str]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> str: + return str(func(*args, **kwds)) + return wrapper + + @stringify + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + reveal_type(a) # Revealed type is "builtins.str" + add_forty_two('x') # error: Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + +Here is the above example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -671,21 +945,31 @@ alter the signature of the input function: P = ParamSpec('P') T = TypeVar('T') - # We reuse 'P' in the return type, but replace 'T' with 'str' + # We reuse 'P' in the return type, but replace 'T' with 'str' def stringify(func: Callable[P, T]) -> Callable[P, str]: def wrapper(*args: P.args, **kwds: P.kwargs) -> str: return str(func(*args, **kwds)) return wrapper - @stringify +You can also insert an argument in a decorator (Python 3.12 syntax): + +.. code-block:: python + + from typing import Callable, Concatenate + + def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: + def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func, "with", msg) + return func(*args, **kwds) + return wrapper + + @printing_decorator def add_forty_two(value: int) -> int: return value + 42 - a = add_forty_two(3) - reveal_type(a) # Revealed type is "builtins.str" - add_forty_two('x') # error: Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + a = add_forty_two('three', 3) -Or insert an argument: +Here is the same function using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -701,19 +985,31 @@ Or insert an argument: return func(*args, **kwds) return wrapper - @printing_decorator - def add_forty_two(value: int) -> int: - return value + 42 - - a = add_forty_two('three', 3) - .. _decorator-factories: Decorator factories ------------------- Functions that take arguments and return a decorator (also called second-order decorators), are -similarly supported via generics: +similarly supported via generics (Python 3.12 syntax): + +.. code-block:: python + + from typing import Any, Callable + + def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: + ... + + @route(url='/') + def index(request: Any) -> str: + return 'Hello world' + +Note that mypy infers that ``F`` is used to make the ``Callable`` return value +of ``route`` generic, instead of making ``route`` itself generic, since ``F`` is +only used in the return type. Python has no explicit syntax to mark that ``F`` +is only bound in the return value. + +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -729,23 +1025,21 @@ similarly supported via generics: return 'Hello world' Sometimes the same decorator supports both bare calls and calls with arguments. This can be -achieved by combining with :py:func:`@overload `: +achieved by combining with :py:func:`@overload ` (Python 3.12 syntax): .. code-block:: python - from typing import Any, Callable, Optional, TypeVar, overload - - F = TypeVar('F', bound=Callable[..., Any]) + from typing import Any, Callable, overload # Bare decorator usage @overload - def atomic(__func: F) -> F: ... + def atomic[F: Callable[..., Any]](func: F, /) -> F: ... # Decorator with arguments @overload - def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + def atomic[F: Callable[..., Any]](*, savepoint: bool = True) -> Callable[[F], F]: ... # Implementation - def atomic(__func: Optional[Callable[..., Any]] = None, *, savepoint: bool = True): + def atomic(func: Callable[..., Any] | None = None, /, *, savepoint: bool = True): def decorator(func: Callable[..., Any]): ... # Code goes here if __func is not None: @@ -760,21 +1054,40 @@ achieved by combining with :py:func:`@overload `: @atomic(savepoint=False) def func2() -> None: ... +Here is the decorator from the example using the legacy syntax +(Python 3.11 and earlier): + +.. code-block:: python + + from typing import Any, Callable, Optional, TypeVar, overload + + F = TypeVar('F', bound=Callable[..., Any]) + + # Bare decorator usage + @overload + def atomic(func: F, /) -> F: ... + # Decorator with arguments + @overload + def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + + # Implementation + def atomic(func: Optional[Callable[..., Any]] = None, /, *, savepoint: bool = True): + ... # Same as above + Generic protocols ***************** Mypy supports generic protocols (see also :ref:`protocol-types`). Several :ref:`predefined protocols ` are generic, such as -:py:class:`Iterable[T] `, and you can define additional generic protocols. Generic -protocols mostly follow the normal rules for generic classes. Example: +:py:class:`Iterable[T] `, and you can define additional +generic protocols. Generic protocols mostly follow the normal rules for +generic classes. Example (Python 3.12 syntax): .. code-block:: python - from typing import Protocol, TypeVar - - T = TypeVar('T') + from typing import Protocol - class Box(Protocol[T]): + class Box[T](Protocol): content: T def do_stuff(one: Box[str], other: Box[bytes]) -> None: @@ -794,15 +1107,29 @@ protocols mostly follow the normal rules for generic classes. Example: y: Box[int] = ... x = y # Error -- Box is invariant +Here is the definition of ``Box`` from the above example using the legacy +syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import Protocol, TypeVar + + T = TypeVar('T') + + class Box(Protocol[T]): + content: T + Note that ``class ClassName(Protocol[T])`` is allowed as a shorthand for -``class ClassName(Protocol, Generic[T])``, as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`, +``class ClassName(Protocol, Generic[T])`` when using the legacy syntax, +as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`. +This form is only valid when using the legacy syntax. -The main difference between generic protocols and ordinary generic -classes is that mypy checks that the declared variances of generic -type variables in a protocol match how they are used in the protocol -definition. The protocol in this example is rejected, since the type -variable ``T`` is used covariantly as a return type, but the type -variable is invariant: +When using the legacy syntax, there is an important difference between +generic protocols and ordinary generic classes: mypy checks that the +declared variances of generic type variables in a protocol match how +they are used in the protocol definition. The protocol in this example +is rejected, since the type variable ``T`` is used covariantly as +a return type, but the type variable is invariant: .. code-block:: python @@ -830,13 +1157,11 @@ This example correctly uses a covariant type variable: See :ref:`variance-of-generics` for more about variance. -Generic protocols can also be recursive. Example: +Generic protocols can also be recursive. Example (Python 3.12 synta): .. code-block:: python - T = TypeVar('T') - - class Linked(Protocol[T]): + class Linked[T](Protocol): val: T def next(self) -> 'Linked[T]': ... @@ -849,17 +1174,63 @@ Generic protocols can also be recursive. Example: result = last(L()) reveal_type(result) # Revealed type is "builtins.int" +Here is the definition of ``Linked`` using the legacy syntax +(Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar + + T = TypeVar('T') + + class Linked(Protocol[T]): + val: T + def next(self) -> 'Linked[T]': ... + .. _generic-type-aliases: Generic type aliases ******************** -Type aliases can be generic. In this case they can be used in two ways: -Subscripted aliases are equivalent to original types with substituted type -variables, so the number of type arguments must match the number of free type variables -in the generic type alias. Unsubscripted aliases are treated as original types with free -variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases -<484#type-aliases>`): +Type aliases can be generic. In this case they can be used in two ways. +First, subscripted aliases are equivalent to original types with substituted type +variables. Second, unsubscripted aliases are treated as original types with type +parameters replaced with ``Any``. + +The ``type`` statement introduced in Python 3.12 is used to define generic +type aliases (it also supports non-generic type aliases): + +.. code-block:: python + + from typing import Iterable, Callable + + type TInt[S] = tuple[int, S] + type UInt[S] = S | int + type CBack[S] = Callable[..., S] + + def response(query: str) -> UInt[str]: # Same as str | int + ... + def activate[S](cb: CBack[S]) -> S: # Same as Callable[..., S] + ... + table_entry: TInt # Same as tuple[int, Any] + + type Vec[T: (int, float, complex)] = Iterable[tuple[T, T]] + + def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: + return sum(x*y for x, y in v) + + def dilate[T: (int, float, complex)](v: Vec[T], scale: T) -> Vec[T]: + return ((x * scale, y * scale) for x, y in v) + + v1: Vec[int] = [] # Same as Iterable[tuple[int, int]] + v2: Vec = [] # Same as Iterable[tuple[Any, Any]] + v3: Vec[int, int] = [] # Error: Invalid alias, too many type arguments! + +There is also a legacy syntax that relies on ``TypeVar``. +Here the number of type arguments must match the number of free type variables +in the generic type alias definition. A type variables is free if it's not +a type parameter of a surrounding class or function. Example (following +:pep:`PEP 484: Type aliases <484#type-aliases>`, Python 3.11 and earlier): .. code-block:: python @@ -867,7 +1238,7 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases S = TypeVar('S') - TInt = tuple[int, S] + TInt = tuple[int, S] # 1 type parameter, since only S is free UInt = Union[S, int] CBack = Callable[..., S] @@ -894,7 +1265,36 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases Type aliases can be imported from modules just like other names. An alias can also target another alias, although building complex chains of aliases is not recommended -- this impedes code readability, thus -defeating the purpose of using aliases. Example: +defeating the purpose of using aliases. Example (Python 3.12 syntax): + +.. code-block:: python + + from example1 import AliasType + from example2 import Vec + + # AliasType and Vec are type aliases (Vec as defined above) + + def fun() -> AliasType: + ... + + type OIntVec = Vec[int] | None + +Type aliases defined using the ``type`` statement are not valid as +base classes, and they can't be used to construct instances: + +.. code-block:: python + + from example1 import AliasType + from example2 import Vec + + # AliasType and Vec are type aliases (Vec as defined above) + + class NewVec[T](Vec[T]): # Error: not valid as base class + ... + + x = AliasType() # Error: can't be used to create instances + +Here are examples using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -907,18 +1307,49 @@ defeating the purpose of using aliases. Example: def fun() -> AliasType: ... + OIntVec = Optional[Vec[int]] + T = TypeVar('T') + # Old-style type aliases can be used as base classes and you can + # construct instances using them + class NewVec(Vec[T]): ... + x = AliasType() + for i, j in NewVec[int](): ... - OIntVec = Optional[Vec[int]] +Using type variable bounds or value restriction in generic aliases has +the same effect as in generic classes and functions. + + +Differences between the new and old syntax +****************************************** -Using type variable bounds or values in generic aliases has the same effect -as in generic classes/functions. +There are a few notable differences between the new (Python 3.12 and later) +and the old syntax for generic classes, functions and type aliases, beyond +the obvious syntactic differences: + + * Type variables defined using the old syntax create definitions at runtime + in the surrounding namespace, whereas the type variables defined using the + new syntax are only defined within the class, function or type variable + that uses them. + * Type variable definitions can be shared when using the old syntax, but + the new syntax doesn't support this. + * When using the new syntax, the variance of class type variables is always + inferred. + * Type aliases defined using the new syntax can contain forward references + and recursive references without using string literal escaping. The + same is true for the bounds and constraints of type variables. + * The new syntax lets you define a generic alias where the definition doesn't + contain a reference to a type parameter. This is occasionally useful, at + least when conditionally defining type aliases. + * Type aliases defined using the new syntax can't be used as base classes + and can't be used to construct instances, unlike aliases defined using the + old syntax. Generic class internals @@ -926,7 +1357,20 @@ Generic class internals You may wonder what happens at runtime when you index a generic class. Indexing returns a *generic alias* to the original class that returns instances -of the original class on instantiation: +of the original class on instantiation (Python 3.12 syntax): + +.. code-block:: python + + >>> class Stack[T]: ... + >>> Stack + __main__.Stack + >>> Stack[int] + __main__.Stack[int] + >>> instance = Stack[int]() + >>> instance.__class__ + __main__.Stack + +Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -945,10 +1389,17 @@ Generic aliases can be instantiated or subclassed, similar to real classes, but the above examples illustrate that type variables are erased at runtime. Generic ``Stack`` instances are just ordinary Python objects, and they have no extra runtime overhead or magic due -to being generic, other than a metaclass that overloads the indexing -operator. +to being generic, other than the ``Generic`` base class that overloads +the indexing operator using ``__class_getitem__``. ``typing.Generic`` +is included as an implicit base class even when using the new syntax: + +.. code-block:: python + + >>> class Stack[T]: ... + >>> Stack.mro() + [, , ] -Note that in Python 3.8 and lower, the built-in types +Note that in Python 3.8 and earlier, the built-in types :py:class:`list`, :py:class:`dict` and others do not support indexing. This is why we have the aliases :py:class:`~typing.List`, :py:class:`~typing.Dict` and so on in the :py:mod:`typing` @@ -959,16 +1410,18 @@ class in more recent versions of Python: .. code-block:: python >>> # Only relevant for Python 3.8 and below - >>> # For Python 3.9 onwards, prefer `list[int]` syntax + >>> # If using Python 3.9 or newer, prefer the 'list[int]' syntax >>> from typing import List >>> List[int] typing.List[int] Note that the generic aliases in ``typing`` don't support constructing -instances: +instances, unlike the corresponding built-in classes: .. code-block:: python + >>> list[int]() + [] >>> from typing import List >>> List[int]() Traceback (most recent call last): From 7f3d7f8f10938618bcf8648d92f6a3da0a01117f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 25 Sep 2024 14:49:22 +0100 Subject: [PATCH 0759/1617] [PEP 695] Further documentation updates (#17826) Finish work started in #17816. Document `type` statement when discussing type aliases. Update some examples to have both old-style and new-style variants. In less common scenarios, examples only use a single syntax variant to reduce verbosity. Also update some examples to generally use more modern features. Closes #17810. --- docs/source/additional_features.rst | 18 +++----- docs/source/cheat_sheet_py3.rst | 15 ++++++- docs/source/error_code_list.rst | 20 +++------ docs/source/kinds_of_types.rst | 47 +++++++++++++++----- docs/source/literal_types.rst | 10 ++--- docs/source/metaclasses.rst | 8 ++-- docs/source/more_types.rst | 67 +++++++++++++++++++---------- docs/source/protocols.rst | 9 ++-- docs/source/runtime_troubles.rst | 14 +++--- docs/source/type_narrowing.rst | 14 ++---- 10 files changed, 135 insertions(+), 87 deletions(-) diff --git a/docs/source/additional_features.rst b/docs/source/additional_features.rst index ae625c157654..a9c3177d32a2 100644 --- a/docs/source/additional_features.rst +++ b/docs/source/additional_features.rst @@ -46,21 +46,18 @@ define dataclasses. For example: UnorderedPoint(1, 2) < UnorderedPoint(3, 4) # Error: Unsupported operand types Dataclasses can be generic and can be used in any other way a normal -class can be used: +class can be used (Python 3.12 syntax): .. code-block:: python from dataclasses import dataclass - from typing import Generic, TypeVar - - T = TypeVar('T') @dataclass - class BoxedData(Generic[T]): + class BoxedData[T]: data: T label: str - def unbox(bd: BoxedData[T]) -> T: + def unbox[T](bd: BoxedData[T]) -> T: ... val = unbox(BoxedData(42, "")) # OK, inferred type is int @@ -98,17 +95,16 @@ does **not** work: To have Mypy recognize a wrapper of :py:func:`dataclasses.dataclass ` -as a dataclass decorator, consider using the :py:func:`~typing.dataclass_transform` decorator: +as a dataclass decorator, consider using the :py:func:`~typing.dataclass_transform` +decorator (example uses Python 3.12 syntax): .. code-block:: python from dataclasses import dataclass, Field - from typing import TypeVar, dataclass_transform - - T = TypeVar('T') + from typing import dataclass_transform @dataclass_transform(field_specifiers=(Field,)) - def my_dataclass(cls: type[T]) -> type[T]: + def my_dataclass[T](cls: type[T]) -> type[T]: ... return dataclass(cls) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index b8e43960fd09..ca6dfc8eb63a 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -349,7 +349,20 @@ Decorators ********** Decorator functions can be expressed via generics. See -:ref:`declaring-decorators` for more details. +:ref:`declaring-decorators` for more details. Example using Python 3.12 +syntax: + +.. code-block:: python + + from typing import Any, Callable + + def bare_decorator[F: Callable[..., Any]](func: F) -> F: + ... + + def decorator_args[F: Callable[..., Any]](url: str) -> Callable[[F], F]: + ... + +The same example using pre-3.12 syntax: .. code-block:: python diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index ad73bc999f00..ec069fdcba1d 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -434,15 +434,11 @@ Check type variable values [type-var] Mypy checks that value of a type variable is compatible with a value restriction or the upper bound type. -Example: +Example (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar - - T1 = TypeVar('T1', int, float) - - def add(x: T1, y: T1) -> T1: + def add[T1: (int, float)](x: T1, y: T1) -> T1: return x + y add(4, 5.5) # OK @@ -783,27 +779,25 @@ Example: Safe handling of abstract type object types [type-abstract] ----------------------------------------------------------- -Mypy always allows instantiating (calling) type objects typed as ``Type[t]``, +Mypy always allows instantiating (calling) type objects typed as ``type[t]``, even if it is not known that ``t`` is non-abstract, since it is a common pattern to create functions that act as object factories (custom constructors). Therefore, to prevent issues described in the above section, when an abstract -type object is passed where ``Type[t]`` is expected, mypy will give an error. -Example: +type object is passed where ``type[t]`` is expected, mypy will give an error. +Example (Python 3.12 syntax): .. code-block:: python from abc import ABCMeta, abstractmethod - from typing import List, Type, TypeVar class Config(metaclass=ABCMeta): @abstractmethod def get_value(self, attr: str) -> str: ... - T = TypeVar("T") - def make_many(typ: Type[T], n: int) -> List[T]: + def make_many[T](typ: type[T], n: int) -> list[T]: return [typ() for _ in range(n)] # This will raise if typ is abstract - # Error: Only concrete class can be given where "Type[Config]" is expected [type-abstract] + # Error: Only concrete class can be given where "type[Config]" is expected [type-abstract] make_many(Config, 5) .. _code-safe-super: diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index d07eb40753f3..e645a27095d1 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -434,19 +434,20 @@ the runtime with some limitations (see :ref:`runtime_troubles`). Type aliases ************ -In certain situations, type names may end up being long and painful to type: +In certain situations, type names may end up being long and painful to type, +especially if they are used frequently: .. code-block:: python - def f() -> Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]]: + def f() -> list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]]: ... When cases like this arise, you can define a type alias by simply -assigning the type to a variable: +assigning the type to a variable (this is an *implicit type alias*): .. code-block:: python - AliasType = Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]] + AliasType = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] # Now we can use AliasType in place of the full name: @@ -459,8 +460,18 @@ assigning the type to a variable: another type -- it's equivalent to the target type except for :ref:`generic aliases `. -Since Mypy 0.930 you can also use *explicit type aliases*, which were -introduced in :pep:`613`. +Python 3.12 introduced the ``type`` statement for defining *explicit type aliases*. +Explicit type aliases are unambiguous and can also improve readability by +making the intent clear: + +.. code-block:: python + + type AliasType = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] + + # Now we can use AliasType in place of the full name: + + def f() -> AliasType: + ... There can be confusion about exactly when an assignment defines an implicit type alias -- for example, when the alias contains forward references, invalid types, or violates some other @@ -469,8 +480,17 @@ distinction between an unannotated variable and a type alias is implicit, ambiguous or incorrect type alias declarations default to defining a normal variable instead of a type alias. -Explicit type aliases are unambiguous and can also improve readability by -making the intent clear: +Aliases defined using the ``type`` statement have these properties, which +distinguish them from implicit type aliases: + +* The definition may contain forward references without having to use string + literal escaping, since it is evaluated lazily. +* The alias can be used in type annotations, type arguments, and casts, but + it can't be used in contexts which require a class object. For example, it's + not valid as a base class and it can't be used to construct instances. + +There is also use an older syntax for defining explicit type aliases, which was +introduced in Python 3.10 (:pep:`613`): .. code-block:: python @@ -604,14 +624,21 @@ doesn't see that the ``buyer`` variable has type ``ProUser``: buyer.pay() # Rejected, not a method on User However, using the ``type[C]`` syntax and a type variable with an upper bound (see -:ref:`type-variable-upper-bound`) we can do better: +:ref:`type-variable-upper-bound`) we can do better (Python 3.12 syntax): + +.. code-block:: python + + def new_user[U: User](user_class: type[U]) -> U: + # Same implementation as before + +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python U = TypeVar('U', bound=User) def new_user(user_class: type[U]) -> U: - # Same implementation as before + # Same implementation as before Now mypy will infer the correct type of the result when we call ``new_user()`` with a specific subclass of ``User``: diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index 283bf7f9dba1..972fce72740f 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -264,19 +264,15 @@ use the same technique with regular objects, tuples, or namedtuples. Similarly, tags do not need to be specifically str Literals: they can be any type you can normally narrow within ``if`` statements and the like. For example, you could have your tags be int or Enum Literals or even regular classes you narrow -using ``isinstance()``: +using ``isinstance()`` (Python 3.12 syntax): .. code-block:: python - from typing import Generic, TypeVar, Union - - T = TypeVar('T') - - class Wrapper(Generic[T]): + class Wrapper[T]: def __init__(self, inner: T) -> None: self.inner = inner - def process(w: Union[Wrapper[int], Wrapper[str]]) -> None: + def process(w: Wrapper[int] | Wrapper[str]) -> None: # Doing `if isinstance(w, Wrapper[int])` does not work: isinstance requires # that the second argument always be an *erased* type, with no generics. # This is because generics are a typing-only concept and do not exist at diff --git a/docs/source/metaclasses.rst b/docs/source/metaclasses.rst index 396d7dbb42cc..a3ee25f16054 100644 --- a/docs/source/metaclasses.rst +++ b/docs/source/metaclasses.rst @@ -34,13 +34,12 @@ Mypy supports the lookup of attributes in the metaclass: .. code-block:: python - from typing import Type, TypeVar, ClassVar - T = TypeVar('T') + from typing import ClassVar, Self class M(type): count: ClassVar[int] = 0 - def make(cls: Type[T]) -> T: + def make(cls) -> Self: M.count += 1 return cls() @@ -56,6 +55,9 @@ Mypy supports the lookup of attributes in the metaclass: b: B = B.make() # metaclasses are inherited print(B.count + " objects were created") # Error: Unsupported operand types for + ("int" and "str") +.. note:: + In Python 3.10 and earlier, ``Self`` is available in ``typing_extensions``. + .. _limitations: Gotchas and limitations of metaclass support diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index cb3ef64b39a7..2d3ce5925c02 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -256,11 +256,34 @@ method receives an integer we return a single item. If it receives a ``slice``, we return a :py:class:`~typing.Sequence` of items. We can precisely encode this relationship between the argument and the -return type by using overloads like so: +return type by using overloads like so (Python 3.12 syntax): .. code-block:: python - from typing import Sequence, TypeVar, Union, overload + from collections.abc import Sequence + from typing import overload + + class MyList[T](Sequence[T]): + @overload + def __getitem__(self, index: int) -> T: ... + + @overload + def __getitem__(self, index: slice) -> Sequence[T]: ... + + def __getitem__(self, index: int | slice) -> T | Sequence[T]: + if isinstance(index, int): + # Return a T here + elif isinstance(index, slice): + # Return a sequence of Ts here + else: + raise TypeError(...) + +Here is the same example using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from collections.abc import Sequence + from typing import TypeVar, Union, overload T = TypeVar('T') @@ -697,14 +720,13 @@ Restricted methods in generic classes ------------------------------------- In generic classes some methods may be allowed to be called only -for certain values of type arguments: +for certain values of type arguments (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') - - class Tag(Generic[T]): + class Tag[T]: item: T + def uppercase_item(self: Tag[str]) -> str: return self.item.upper() @@ -714,18 +736,18 @@ for certain values of type arguments: ts.uppercase_item() # This is OK This pattern also allows matching on nested types in situations where the type -argument is itself generic: +argument is itself generic (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T', covariant=True) - S = TypeVar('S') + from collections.abc import Sequence - class Storage(Generic[T]): + class Storage[T]: def __init__(self, content: T) -> None: - self.content = content - def first_chunk(self: Storage[Sequence[S]]) -> S: - return self.content[0] + self._content = content + + def first_chunk[S](self: Storage[Sequence[S]]) -> S: + return self._content[0] page: Storage[list[str]] page.first_chunk() # OK, type is "str" @@ -734,13 +756,13 @@ argument is itself generic: # "first_chunk" with type "Callable[[Storage[Sequence[S]]], S]" Finally, one can use overloads on self-type to express precise types of -some tricky methods: +some tricky methods (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') + from typing import overload, Callable - class Tag(Generic[T]): + class Tag[T]: @overload def export(self: Tag[str]) -> str: ... @overload @@ -799,23 +821,22 @@ Precise typing of alternative constructors ------------------------------------------ Some classes may define alternative constructors. If these -classes are generic, self-type allows giving them precise signatures: +classes are generic, self-type allows giving them precise +signatures (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') - - class Base(Generic[T]): - Q = TypeVar('Q', bound='Base[T]') + from typing import Self + class Base[T]: def __init__(self, item: T) -> None: self.item = item @classmethod - def make_pair(cls: Type[Q], item: T) -> tuple[Q, Q]: + def make_pair(cls, item: T) -> tuple[Self, Self]: return cls(item), cls(item) - class Sub(Base[T]): + class Sub[T](Base[T]): ... pair = Sub.make_pair('yes') # Type is "tuple[Sub[str], Sub[str]]" diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 731562867691..e143808e6c25 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -225,7 +225,7 @@ such as trees and linked lists: .. code-block:: python - from typing import TypeVar, Optional, Protocol + from typing import Optional, Protocol class TreeLike(Protocol): value: int @@ -315,8 +315,8 @@ member: # different name and kind in the callback Callback protocols and :py:data:`~typing.Callable` types can be used mostly interchangeably. -Argument names in :py:meth:`__call__ ` methods must be identical, unless -a double underscore prefix is used. For example: +Parameter names in :py:meth:`__call__ ` methods must be identical, unless +the parameters are positional-only. Example (using the legacy syntax for generic functions): .. code-block:: python @@ -325,7 +325,8 @@ a double underscore prefix is used. For example: T = TypeVar('T') class Copy(Protocol): - def __call__(self, __origin: T) -> T: ... + # '/' marks the end of positional-only parameters + def __call__(self, origin: T, /) -> T: ... copy_a: Callable[[T], T] copy_b: Copy diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index 66ab7b3a84c7..ee4cdf274ebe 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -69,7 +69,7 @@ Future annotations import (PEP 563) ----------------------------------- Many of the issues described here are caused by Python trying to evaluate -annotations. Future Python versions (potentially Python 3.12) will by default no +annotations. Future Python versions (potentially Python 3.14) will by default no longer attempt to evaluate function and variable annotations. This behaviour is made available in Python 3.7 and later through the use of ``from __future__ import annotations``. @@ -84,7 +84,7 @@ required to be valid Python syntax. For more details, see :pep:`563`. still require string literals or result in errors, typically involving use of forward references or generics in: - * :ref:`type aliases `; + * :ref:`type aliases ` not defined using the ``type`` statement; * :ref:`type narrowing `; * type definitions (see :py:class:`~typing.TypeVar`, :py:class:`~typing.NewType`, :py:class:`~typing.NamedTuple`); * base classes. @@ -93,6 +93,7 @@ required to be valid Python syntax. For more details, see :pep:`563`. # base class example from __future__ import annotations + class A(tuple['B', 'C']): ... # String literal types needed here class B: ... class C: ... @@ -244,7 +245,8 @@ complicated and you need to use :ref:`typing.TYPE_CHECKING task_queue: Tasks reveal_type(task_queue.get()) # Reveals str -If your subclass is also generic, you can use the following: +If your subclass is also generic, you can use the following (using the +legacy syntax for generic classes): .. code-block:: python @@ -262,9 +264,11 @@ If your subclass is also generic, you can use the following: task_queue: MyQueue[str] reveal_type(task_queue.get()) # Reveals str -In Python 3.9, we can just inherit directly from ``Queue[str]`` or ``Queue[T]`` +In Python 3.9 and later, we can just inherit directly from ``Queue[str]`` or ``Queue[T]`` since its :py:class:`queue.Queue` implements :py:meth:`~object.__class_getitem__`, so -the class object can be subscripted at runtime without issue. +the class object can be subscripted at runtime. You may still encounter issues (even if +you use a recent Python version) when subclassing generic classes defined in third-party +libraries if types are generic only in stubs. Using types defined in stubs but not at runtime ----------------------------------------------- diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index d698f35c44bc..131171844dfe 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -255,16 +255,13 @@ to the type specified as the first type parameter (``list[str]``). Generic TypeGuards ~~~~~~~~~~~~~~~~~~ -``TypeGuard`` can also work with generic types: +``TypeGuard`` can also work with generic types (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar from typing import TypeGuard # use `typing_extensions` for `python<3.10` - _T = TypeVar("_T") - - def is_two_element_tuple(val: tuple[_T, ...]) -> TypeGuard[tuple[_T, _T]]: + def is_two_element_tuple[T](val: tuple[T, ...]) -> TypeGuard[tuple[T, T]]: return len(val) == 2 def func(names: tuple[str, ...]): @@ -276,16 +273,13 @@ Generic TypeGuards TypeGuards with parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Type guard functions can accept extra arguments: +Type guard functions can accept extra arguments (Python 3.12 syntax): .. code-block:: python - from typing import Type, TypeVar from typing import TypeGuard # use `typing_extensions` for `python<3.10` - _T = TypeVar("_T") - - def is_set_of(val: set[Any], type: Type[_T]) -> TypeGuard[set[_T]]: + def is_set_of[T](val: set[Any], type: type[T]) -> TypeGuard[set[T]]: return all(isinstance(x, type) for x in val) items: set[Any] From f6520c84746072e72e8a017963d3d2a3a9361771 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Wed, 25 Sep 2024 21:07:44 -0400 Subject: [PATCH 0760/1617] Narrow falsey str/bytes/int to literal type (#17818) Closes #16891 ### Before ```python from typing import Literal def f1(a: str) -> Literal[""]: return a and exit() # E: Incompatible return value type (got "str", expected "Literal['']") def f2(a: int) -> Literal[0]: return a and exit() # E: Incompatible return value type (got "int", expected "Literal[0]") def f3(a: bytes) -> Literal[b""]: return a and exit() # E: Incompatible return value type (got "bytes", expected "Literal[b'']") ``` ### After ```none Success: no issues found in 1 source file ``` --- mypy/typeops.py | 4 ++++ test-data/unit/check-expressions.test | 2 +- test-data/unit/check-narrowing.test | 18 +++++++++++++++++- test-data/unit/check-optional.test | 10 +++++----- test-data/unit/check-python38.test | 8 ++++---- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index d22448a715e5..7f530d13d4e2 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -657,6 +657,10 @@ def false_only(t: Type) -> ProperType: new_items = [false_only(item) for item in t.items] can_be_false_items = [item for item in new_items if item.can_be_false] return make_simplified_union(can_be_false_items, line=t.line, column=t.column) + elif isinstance(t, Instance) and t.type.fullname in ("builtins.str", "builtins.bytes"): + return LiteralType("", fallback=t) + elif isinstance(t, Instance) and t.type.fullname == "builtins.int": + return LiteralType(0, fallback=t) else: ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type( t, name="__len__" diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 61cee1d00c58..d5ddc910bcd6 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -342,7 +342,7 @@ b: bool i: str j = b or i if not j: - reveal_type(j) # N: Revealed type is "builtins.str" + reveal_type(j) # N: Revealed type is "Literal['']" [builtins fixtures/bool.pyi] [case testAndOr] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index e142fdd5d060..0cb4bf8e4a19 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1007,7 +1007,7 @@ str_or_false: Union[Literal[False], str] if str_or_false: reveal_type(str_or_false) # N: Revealed type is "builtins.str" else: - reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], builtins.str]" + reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], Literal['']]" true_or_false: Literal[True, False] @@ -1017,6 +1017,22 @@ else: reveal_type(true_or_false) # N: Revealed type is "Literal[False]" [builtins fixtures/primitives.pyi] +[case testNarrowingFalseyToLiteral] +from typing import Union + +a: str +b: bytes +c: int +d: Union[str, bytes, int] + +if not a: + reveal_type(a) # N: Revealed type is "Literal['']" +if not b: + reveal_type(b) # N: Revealed type is "Literal[b'']" +if not c: + reveal_type(c) # N: Revealed type is "Literal[0]" +if not d: + reveal_type(d) # N: Revealed type is "Union[Literal[''], Literal[b''], Literal[0]]" [case testNarrowingIsInstanceFinalSubclass] # flags: --warn-unreachable diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index f80aa5115bc3..683ce0446915 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -53,14 +53,14 @@ x = None # type: Optional[int] if x: reveal_type(x) # N: Revealed type is "builtins.int" else: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" [builtins fixtures/bool.pyi] [case testIfNotCases] from typing import Optional x = None # type: Optional[int] if not x: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" else: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/bool.pyi] @@ -109,13 +109,13 @@ reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" from typing import Optional x = None # type: Optional[str] y1 = x and 'b' -reveal_type(y1) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(y1) # N: Revealed type is "Union[Literal[''], None, builtins.str]" y2 = x and 1 # x could be '', so... -reveal_type(y2) # N: Revealed type is "Union[builtins.str, None, builtins.int]" +reveal_type(y2) # N: Revealed type is "Union[Literal[''], None, builtins.int]" z1 = 'b' and x reveal_type(z1) # N: Revealed type is "Union[builtins.str, None]" z2 = int() and x -reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(z2) # N: Revealed type is "Union[Literal[0], builtins.str, None]" [case testLambdaReturningNone] f = lambda: None diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index dfb918defb0a..199014a66fed 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -463,9 +463,9 @@ def check_partial_list() -> None: if (x := 0): reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Literal[0]" -reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Literal[0]" [case testWalrusAssignmentAndConditionScopeForProperty] # flags: --warn-unreachable @@ -483,7 +483,7 @@ wrapper = PropertyWrapper() if x := wrapper.f: reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str" @@ -505,7 +505,7 @@ def f() -> str: ... if x := f(): reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str" From 6336ce117d857ac56c4cd140b8ab0ae8f70ecc83 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 26 Sep 2024 10:07:29 +0100 Subject: [PATCH 0761/1617] Discuss upper bounds before self types in documentation (#17827) The explanation of self types uses upper bounds, so it makes sense to discuss them earlier. I also made some other minor documentation tweaks to improve clarity. --- docs/source/generics.rst | 114 +++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 4eb6463e4bd4..3d175cedbb93 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -315,6 +315,53 @@ inferred by mypy. This is not valid: If you really need this, you can define a generic class with a ``__call__`` method. +.. _type-variable-upper-bound: + +Type variables with upper bounds +******************************** + +A type variable can also be restricted to having values that are +subtypes of a specific type. This type is called the upper bound of +the type variable, and it is specified using ``T: `` when using the +Python 3.12 syntax. In the definition of a generic function or a generic +class that uses such a type variable ``T``, the type represented by ``T`` +is assumed to be a subtype of its upper bound, so you can use methods +of the upper bound on values of type ``T`` (Python 3.12 syntax): + +.. code-block:: python + + from typing import SupportsAbs + + def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T: + # We can use abs(), because T is a subtype of SupportsAbs[float]. + return max(xs, key=abs) + +An upper bound can also be specified with the ``bound=...`` keyword +argument to :py:class:`~typing.TypeVar`. +Here is the example using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar, SupportsAbs + + T = TypeVar('T', bound=SupportsAbs[float]) + + def max_by_abs(*xs: T) -> T: + return max(xs, key=abs) + +In a call to such a function, the type ``T`` must be replaced by a +type that is a subtype of its upper bound. Continuing the example +above: + +.. code-block:: python + + max_by_abs(-3.5, 2) # Okay, has type 'float' + max_by_abs(5+6j, 7) # Okay, has type 'complex' + max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float] + +Type parameters of generic classes may also have upper bounds, which +restrict the valid values for the type parameter in the same way. + .. _generic-methods-and-generic-self: Generic methods and generic self @@ -400,8 +447,7 @@ or :py:class:`Type[T] ` (Python 3.12 syntax): a, b = SuperFriend.make_pair() -Here is the same example using the legacy syntax (3.11 and earlier, though -3.9 and later can use lower-case ``type[T]``): +Here is the same example using the legacy syntax (3.11 and earlier): .. code-block:: python @@ -433,7 +479,7 @@ or a deserialization method returns the actual type of self. Therefore you may need to silence mypy inside these methods (but not at the call site), possibly by making use of the ``Any`` type or a ``# type: ignore`` comment. -Note that mypy lets you use generic self types in certain unsafe ways +Mypy lets you use generic self types in certain unsafe ways in order to support common idioms. For example, using a generic self type in an argument type is accepted even though it's unsafe (Python 3.12 syntax): @@ -647,59 +693,13 @@ contravariant, use type variables defined with special keyword arguments my_box = Box(Square()) look_into(my_box) # OK, but mypy would complain here for an invariant type -.. _type-variable-upper-bound: - -Type variables with upper bounds -******************************** - -A type variable can also be restricted to having values that are -subtypes of a specific type. This type is called the upper bound of -the type variable, and it is specified using ``T: `` when using the -Python 3.12 syntax. In the definition of a generic function that uses -such a type variable ``T``, the type represented by ``T`` is assumed -to be a subtype of its upper bound, so the function can use methods -of the upper bound on values of type ``T`` (Python 3.12 syntax): - -.. code-block:: python - - from typing import SupportsAbs - - def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T: - # Okay, because T is a subtype of SupportsAbs[float]. - return max(xs, key=abs) - -An upper bound can also be specified with the ``bound=...`` keyword -argument to :py:class:`~typing.TypeVar`. -Here is the example using the legacy syntax (Python 3.11 and earlier): - -.. code-block:: python - - from typing import TypeVar, SupportsAbs - - T = TypeVar('T', bound=SupportsAbs[float]) - - def max_by_abs(*xs: T) -> T: - return max(xs, key=abs) - -In a call to such a function, the type ``T`` must be replaced by a -type that is a subtype of its upper bound. Continuing the example -above: - -.. code-block:: python - - max_by_abs(-3.5, 2) # Okay, has type float. - max_by_abs(5+6j, 7) # Okay, has type complex. - max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]. - -Type parameters of generic classes may also have upper bounds, which -restrict the valid values for the type parameter in the same way. - .. _type-variable-value-restriction: Type variables with value restriction ************************************* -By default, a type variable can be replaced with any type. However, sometimes +By default, a type variable can be replaced with any type -- or any type that +is a subtype of the upper bound, which defaults to ``object``. However, sometimes it's useful to have a type variable that can only have some specific types as its value. A typical example is a type variable that can only have values ``str`` and ``bytes``. This lets us define a function that can concatenate @@ -758,11 +758,10 @@ You may expect that the type of ``ss`` is ``S``, but the type is actually ``str``: a subtype gets promoted to one of the valid values for the type variable, which in this case is ``str``. -This is thus -subtly different from *bounded quantification* in languages such as -Java, where the return type would be ``S``. The way mypy implements -this is correct for ``concat``, since ``concat`` actually returns a -``str`` instance in the above example: +This is thus subtly different from using ``str | bytes`` as an upper bound, +where the return type would be ``S`` (see :ref:`type-variable-upper-bound`). +Using a value restriction is correct for ``concat``, since ``concat`` +actually returns a ``str`` instance in the above example: .. code-block:: python @@ -776,8 +775,7 @@ value of :py:func:`re.compile`, where ``S`` can be either ``str`` or ``bytes``. Regular expressions can be based on a string or a bytes pattern. -A type variable may not have both a value restriction and an upper bound -(see :ref:`type-variable-upper-bound`). +A type variable may not have both a value restriction and an upper bound. Note that you may come across :py:data:`~typing.AnyStr` imported from :py:mod:`typing`. This feature is now deprecated, but it means the same From 0dfb7184d72d1aad951b1eed1541e3162d7ca040 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 26 Sep 2024 11:19:15 +0100 Subject: [PATCH 0762/1617] Update various references to deprecated type aliases in docs (#17829) Use `collections.abc.Iterable`, for example, instead of `typing.Iterable`, unless we are discussing support for older Python versions. The latter has been deprecated since Python 3.9. Also update a few other out-of-date things that I happened to notice. Part of the motivation is that Python 3.8 will reach end of life in October, so we can soon start mostly assuming that users are on 3.9 or newer (even if we'll continue supporting 3.8 for a while still). --------- Co-authored-by: Jelle Zijlstra --- docs/source/additional_features.rst | 12 +-- docs/source/cheat_sheet_py3.rst | 12 ++- docs/source/class_basics.rst | 3 +- docs/source/command_line.rst | 4 +- docs/source/common_issues.rst | 7 +- docs/source/dynamic_typing.rst | 4 +- docs/source/error_code_list.rst | 6 +- docs/source/error_code_list2.rst | 4 +- docs/source/faq.rst | 9 +- docs/source/generics.rst | 40 +++++---- docs/source/getting_started.rst | 2 +- docs/source/kinds_of_types.rst | 13 ++- docs/source/more_types.rst | 22 +++-- docs/source/protocols.rst | 133 ++++++++++++++++------------ docs/source/runtime_troubles.rst | 5 +- docs/source/type_narrowing.rst | 5 +- docs/source/typed_dict.rst | 4 +- 17 files changed, 161 insertions(+), 124 deletions(-) diff --git a/docs/source/additional_features.rst b/docs/source/additional_features.rst index a9c3177d32a2..e7c162a0b0df 100644 --- a/docs/source/additional_features.rst +++ b/docs/source/additional_features.rst @@ -363,20 +363,20 @@ Extended Callable types This feature is deprecated. You can use :ref:`callback protocols ` as a replacement. -As an experimental mypy extension, you can specify :py:data:`~typing.Callable` types +As an experimental mypy extension, you can specify :py:class:`~collections.abc.Callable` types that support keyword arguments, optional arguments, and more. When -you specify the arguments of a :py:data:`~typing.Callable`, you can choose to supply just +you specify the arguments of a :py:class:`~collections.abc.Callable`, you can choose to supply just the type of a nameless positional argument, or an "argument specifier" representing a more complicated form of argument. This allows one to more closely emulate the full range of possibilities given by the ``def`` statement in Python. As an example, here's a complicated function definition and the -corresponding :py:data:`~typing.Callable`: +corresponding :py:class:`~collections.abc.Callable`: .. code-block:: python - from typing import Callable + from collections.abc import Callable from mypy_extensions import (Arg, DefaultArg, NamedArg, DefaultNamedArg, VarArg, KwArg) @@ -449,7 +449,7 @@ purpose: In all cases, the ``type`` argument defaults to ``Any``, and if the ``name`` argument is omitted the argument has no name (the name is required for ``NamedArg`` and ``DefaultNamedArg``). A basic -:py:data:`~typing.Callable` such as +:py:class:`~collections.abc.Callable` such as .. code-block:: python @@ -461,7 +461,7 @@ is equivalent to the following: MyFunc = Callable[[Arg(int), Arg(str), Arg(int)], float] -A :py:data:`~typing.Callable` with unspecified argument types, such as +A :py:class:`~collections.abc.Callable` with unspecified argument types, such as .. code-block:: python diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index ca6dfc8eb63a..eb2f8228cf52 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -88,7 +88,8 @@ Functions .. code-block:: python - from typing import Callable, Iterator, Union, Optional + from collections.abc import Iterator, Callable + from typing import Union, Optional # This is how you annotate a function definition def stringify(num: int) -> str: @@ -274,7 +275,8 @@ that are common in idiomatic Python are standardized. .. code-block:: python - from typing import Mapping, MutableMapping, Sequence, Iterable + from collections.abc import Mapping, MutableMapping, Sequence, Iterable + # or 'from typing import ...' (required in Python 3.8) # Use Iterable for generic iterables (anything usable in "for"), # and Sequence where a sequence (supporting "len" and "__getitem__") is @@ -354,7 +356,8 @@ syntax: .. code-block:: python - from typing import Any, Callable + from collections.abc import Callable + from typing import Any def bare_decorator[F: Callable[..., Any]](func: F) -> F: ... @@ -366,7 +369,8 @@ The same example using pre-3.12 syntax: .. code-block:: python - from typing import Any, Callable, TypeVar + from collections.abc import Callable + from typing import Any, TypeVar F = TypeVar('F', bound=Callable[..., Any]) diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 1d80da5830ec..241dbeae0f44 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -152,7 +152,8 @@ between class and instance variables with callable types. For example: .. code-block:: python - from typing import Callable, ClassVar + from collections.abc import Callable + from typing import ClassVar class A: foo: Callable[[int], None] diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index a89a3c85d4ee..3d4b841e0a7c 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -630,13 +630,11 @@ of the above sections. .. code-block:: python - from typing import Text - items: list[int] if 'some string' in items: # Error: non-overlapping container check! ... - text: Text + text: str if text != b'other bytes': # Error: non-overlapping equality check! ... diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index cfe82e19e77b..c34d20775cfd 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -363,7 +363,8 @@ explicit type cast: .. code-block:: python - from typing import Sequence, cast + from collections.abc import Sequence + from typing import cast def find_first_str(a: Sequence[object]) -> str: index = next((i for i, s in enumerate(a) if isinstance(s, str)), -1) @@ -700,7 +701,7 @@ This example demonstrates both safe and unsafe overrides: .. code-block:: python - from typing import Sequence, List, Iterable + from collections.abc import Sequence, Iterable class A: def test(self, t: Sequence[int]) -> Sequence[str]: @@ -713,7 +714,7 @@ This example demonstrates both safe and unsafe overrides: class NarrowerArgument(A): # A more specific argument type isn't accepted - def test(self, t: List[int]) -> Sequence[str]: # Error + def test(self, t: list[int]) -> Sequence[str]: # Error ... class NarrowerReturn(A): diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst index d3476de2ca64..1c31a535bdc1 100644 --- a/docs/source/dynamic_typing.rst +++ b/docs/source/dynamic_typing.rst @@ -81,9 +81,7 @@ treated as ``Any``: .. code-block:: python - from typing import List - - def f(x: List) -> None: + def f(x: list) -> None: reveal_type(x) # Revealed type is "builtins.list[Any]" reveal_type(x[0]) # Revealed type is "Any" x[0].anything_goes() # OK diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index ec069fdcba1d..bb745d9d5c98 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -124,8 +124,6 @@ Example: .. code-block:: python - from typing import Sequence - def greet(name: str) -> None: print('hello', name) @@ -210,11 +208,11 @@ This example incorrectly uses the function ``log`` as a type: for x in objs: f(x) -You can use :py:data:`~typing.Callable` as the type for callable objects: +You can use :py:class:`~collections.abc.Callable` as the type for callable objects: .. code-block:: python - from typing import Callable + from collections.abc import Callable # OK def log_all(objs: list[object], f: Callable[[object], None]) -> None: diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 0655ef2d35d8..6d50e217a77d 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -270,7 +270,7 @@ example: # mypy: enable-error-code="possibly-undefined" - from typing import Iterable + from collections.abc import Iterable def test(values: Iterable[int], flag: bool) -> None: if flag: @@ -318,7 +318,7 @@ Example: .. code-block:: python - from typing import Iterable + from collections.abc import Iterable def transform(items: Iterable[int]) -> list[int]: # Error: "items" has type "Iterable[int]" which can always be true in boolean context. Consider using "Collection[int]" instead. [truthy-iterable] diff --git a/docs/source/faq.rst b/docs/source/faq.rst index 195805382cd3..b7f5e3759a7e 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -102,8 +102,8 @@ Structural subtyping can be thought of as "static duck typing". Some argue that structural subtyping is better suited for languages with duck typing such as Python. Mypy however primarily uses nominal subtyping, leaving structural subtyping mostly opt-in (except for built-in protocols -such as :py:class:`~typing.Iterable` that always support structural subtyping). Here are some -reasons why: +such as :py:class:`~collections.abc.Iterable` that always support structural +subtyping). Here are some reasons why: 1. It is easy to generate short and informative error messages when using a nominal type system. This is especially important when @@ -140,13 +140,14 @@ How are mypy programs different from normal Python? Since you use a vanilla Python implementation to run mypy programs, mypy programs are also Python programs. The type checker may give warnings for some valid Python code, but the code is still always -runnable. Also, some Python features and syntax are still not +runnable. Also, a few Python features are still not supported by mypy, but this is gradually improving. The obvious difference is the availability of static type checking. The section :ref:`common_issues` mentions some modifications to Python code that may be required to make code type -check without errors. Also, your code must make attributes explicit. +check without errors. Also, your code must make defined +attributes explicit. Mypy supports modular, efficient type checking, and this seems to rule out type checking some language features, such as arbitrary diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 3d175cedbb93..982adf9324af 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -591,7 +591,7 @@ Let us illustrate this by few simple examples: Covariance should feel relatively intuitive, but contravariance and invariance can be harder to reason about. -* :py:data:`~typing.Callable` is an example of type that behaves contravariant +* :py:class:`~collections.abc.Callable` is an example of type that behaves contravariant in types of arguments. That is, ``Callable[[Shape], int]`` is a subtype of ``Callable[[Triangle], int]``, despite ``Shape`` being a supertype of ``Triangle``. To understand this, consider: @@ -833,7 +833,8 @@ Here's how one could annotate the decorator (Python 3.12 syntax): .. code-block:: python - from typing import Any, Callable, cast + from collections.abc import Callable + from typing import Any, cast # A decorator that preserves the signature. def printing_decorator[F: Callable[..., Any]](func: F) -> F: @@ -854,7 +855,8 @@ Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Any, Callable, TypeVar, cast + from collections.abc import Callable + from typing import Any, TypeVar, cast F = TypeVar('F', bound=Callable[..., Any]) @@ -887,7 +889,7 @@ for a more faithful type annotation (Python 3.12 syntax): .. code-block:: python - from typing import Callable + from collections.abc import Callable def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]: def wrapper(*args: P.args, **kwds: P.kwargs) -> T: @@ -900,7 +902,8 @@ The same is possible using the legacy syntax with :py:class:`~typing.ParamSpec` .. code-block:: python - from typing import Callable, TypeVar + from collections.abc import Callable + from typing import TypeVar from typing_extensions import ParamSpec P = ParamSpec('P') @@ -917,7 +920,7 @@ alter the signature of the input function (Python 3.12 syntax): .. code-block:: python - from typing import Callable + from collections.abc import Callable # We reuse 'P' in the return type, but replace 'T' with 'str' def stringify[**P, T](func: Callable[P, T]) -> Callable[P, str]: @@ -937,7 +940,8 @@ Here is the above example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Callable, TypeVar + from collections.abc import Callable + from typing import TypeVar from typing_extensions import ParamSpec P = ParamSpec('P') @@ -953,7 +957,8 @@ You can also insert an argument in a decorator (Python 3.12 syntax): .. code-block:: python - from typing import Callable, Concatenate + from collections.abc import Callable + from typing import Concatenate def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: @@ -971,7 +976,8 @@ Here is the same function using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Callable, TypeVar + from collections.abc import Callable + from typing import TypeVar from typing_extensions import Concatenate, ParamSpec P = ParamSpec('P') @@ -993,7 +999,8 @@ similarly supported via generics (Python 3.12 syntax): .. code-block:: python - from typing import Any, Callable + from colletions.abc import Callable + from typing import Any def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: ... @@ -1011,7 +1018,8 @@ Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Any, Callable, TypeVar + from collections.abc import Callable + from typing import Any, TypeVar F = TypeVar('F', bound=Callable[..., Any]) @@ -1027,7 +1035,8 @@ achieved by combining with :py:func:`@overload ` (Python 3.12 s .. code-block:: python - from typing import Any, Callable, overload + from collections.abc import Callable + from typing import Any, overload # Bare decorator usage @overload @@ -1057,7 +1066,8 @@ Here is the decorator from the example using the legacy syntax .. code-block:: python - from typing import Any, Callable, Optional, TypeVar, overload + from collections.abc import Callable + from typing import Any, Optional, TypeVar, overload F = TypeVar('F', bound=Callable[..., Any]) @@ -1077,7 +1087,7 @@ Generic protocols Mypy supports generic protocols (see also :ref:`protocol-types`). Several :ref:`predefined protocols ` are generic, such as -:py:class:`Iterable[T] `, and you can define additional +:py:class:`Iterable[T] `, and you can define additional generic protocols. Generic protocols mostly follow the normal rules for generic classes. Example (Python 3.12 syntax): @@ -1200,7 +1210,7 @@ type aliases (it also supports non-generic type aliases): .. code-block:: python - from typing import Iterable, Callable + from collections.abc import Callable, Iterable type TInt[S] = tuple[int, S] type UInt[S] = S | int diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 049d7af003b5..12422ef85bde 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -215,7 +215,7 @@ generic types or your own type aliases), look through the For brevity, we often omit imports from :py:mod:`typing` or :py:mod:`collections.abc` in code examples, but mypy will give an error if you use types such as - :py:class:`~typing.Iterable` without first importing them. + :py:class:`~collections.abc.Iterable` without first importing them. .. note:: diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index e645a27095d1..afdf07a559da 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -136,7 +136,7 @@ purpose. Example: .. note:: Usually it's a better idea to use ``Sequence[T]`` instead of ``tuple[T, ...]``, as - :py:class:`~typing.Sequence` is also compatible with lists and other non-tuple sequences. + :py:class:`~collections.abc.Sequence` is also compatible with lists and other non-tuple sequences. .. note:: @@ -155,7 +155,7 @@ and returns ``Rt`` is ``Callable[[A1, ..., An], Rt]``. Example: .. code-block:: python - from typing import Callable + from collections.abc import Callable def twice(i: int, next: Callable[[int], int]) -> int: return next(next(i)) @@ -165,6 +165,11 @@ and returns ``Rt`` is ``Callable[[A1, ..., An], Rt]``. Example: print(twice(3, add)) # 5 +.. note:: + + Import :py:data:`Callable[...] ` from ``typing`` instead + of ``collections.abc`` if you use Python 3.8 or earlier. + You can only have positional arguments, and only ones without default values, in callable types. These cover the vast majority of uses of callable types, but sometimes this isn't quite enough. Mypy recognizes @@ -178,7 +183,7 @@ Any)`` function signature. Example: .. code-block:: python - from typing import Callable + from collections.abc import Callable def arbitrary_call(f: Callable[..., int]) -> int: return f('x') + f(y=2) # OK @@ -205,7 +210,7 @@ Callables can also be used against type objects, matching their .. code-block:: python - from typing import Callable + from collections.abc import Callable class C: def __init__(self, app: str) -> None: diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 2d3ce5925c02..26edd9ef252c 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -253,7 +253,7 @@ calls like ``mouse_event(5, 25, 2)``. As another example, suppose we want to write a custom container class that implements the :py:meth:`__getitem__ ` method (``[]`` bracket indexing). If this method receives an integer we return a single item. If it receives a -``slice``, we return a :py:class:`~typing.Sequence` of items. +``slice``, we return a :py:class:`~collections.abc.Sequence` of items. We can precisely encode this relationship between the argument and the return type by using overloads like so (Python 3.12 syntax): @@ -760,7 +760,8 @@ some tricky methods (Python 3.12 syntax): .. code-block:: python - from typing import overload, Callable + from collections.abc import Callable + from typing import overload class Tag[T]: @overload @@ -875,8 +876,8 @@ expect to get back when ``await``-ing the coroutine. The result of calling an ``async def`` function *without awaiting* will automatically be inferred to be a value of type -:py:class:`Coroutine[Any, Any, T] `, which is a subtype of -:py:class:`Awaitable[T] `: +:py:class:`Coroutine[Any, Any, T] `, which is a subtype of +:py:class:`Awaitable[T] `: .. code-block:: python @@ -889,11 +890,12 @@ Asynchronous iterators ---------------------- If you have an asynchronous iterator, you can use the -:py:class:`~typing.AsyncIterator` type in your annotations: +:py:class:`~collections.abc.AsyncIterator` type in your annotations: .. code-block:: python - from typing import Optional, AsyncIterator + from collections.abc import AsyncIterator + from typing import Optional import asyncio class arange: @@ -926,7 +928,8 @@ async iterators: .. code-block:: python - from typing import AsyncGenerator, Optional + from collections.abc import AsyncGenerator + from typing import Optional import asyncio # Could also type this as returning AsyncIterator[int] @@ -943,7 +946,7 @@ One common confusion is that the presence of a ``yield`` statement in an .. code-block:: python - from typing import AsyncIterator + from collections.abc import AsyncIterator async def arange(stop: int) -> AsyncIterator[int]: # When called, arange gives you an async iterator @@ -969,7 +972,8 @@ This can sometimes come up when trying to define base classes, Protocols or over .. code-block:: python - from typing import AsyncIterator, Protocol, overload + from collections.abc import AsyncIterator + from typing import Protocol, overload class LauncherIncorrect(Protocol): # Because launch does not have yield, this has type diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index e143808e6c25..8cb53b1fd5ce 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -27,15 +27,17 @@ of protocols and structural subtyping in Python. Predefined protocols ******************** -The :py:mod:`typing` module defines various protocol classes that correspond -to common Python protocols, such as :py:class:`Iterable[T] `. If a class +The :py:mod:`collections.abc`, :py:mod:`typing` and other stdlib modules define +various protocol classes that correspond to common Python protocols, such as +:py:class:`Iterable[T] `. If a class defines a suitable :py:meth:`__iter__ ` method, mypy understands that it -implements the iterable protocol and is compatible with :py:class:`Iterable[T] `. +implements the iterable protocol and is compatible with :py:class:`Iterable[T] `. For example, ``IntList`` below is iterable, over ``int`` values: .. code-block:: python - from typing import Iterator, Iterable, Optional + from collections.abc import Iterator, Iterable + from typing import Optional class IntList: def __init__(self, value: int, next: Optional['IntList']) -> None: @@ -56,9 +58,18 @@ For example, ``IntList`` below is iterable, over ``int`` values: print_numbered(x) # OK print_numbered([4, 5]) # Also OK -:ref:`predefined_protocols_reference` lists all protocols defined in -:py:mod:`typing` and the signatures of the corresponding methods you need to define -to implement each protocol. +:ref:`predefined_protocols_reference` lists various protocols defined in +:py:mod:`collections.abc` and :py:mod:`typing` and the signatures of the corresponding methods +you need to define to implement each protocol. + +.. note:: + ``typing`` also contains deprecated aliases to protocols and ABCs defined in + :py:mod:`collections.abc`, such as :py:class:`Iterable[T] `. + These are only necessary in Python 3.8 and earlier, since the protocols in + ``collections.abc`` didn't yet support subscripting (``[]``) in Python 3.8, + but the aliases in ``typing`` have always supported + subscripting. In Python 3.9 and later, the aliases in ``typing`` don't provide + any extra functionality. Simple user-defined protocols ***************************** @@ -68,7 +79,8 @@ class: .. code-block:: python - from typing import Iterable, Protocol + from collections.abc import Iterable + from typing import Protocol class SupportsClose(Protocol): # Empty method body (explicit '...') @@ -290,13 +302,15 @@ Callback protocols ****************** Protocols can be used to define flexible callback types that are hard -(or even impossible) to express using the :py:data:`Callable[...] ` syntax, such as variadic, -overloaded, and complex generic callbacks. They are defined with a special :py:meth:`__call__ ` -member: +(or even impossible) to express using the +:py:class:`Callable[...] ` syntax, +such as variadic, overloaded, and complex generic callbacks. They are defined with a +special :py:meth:`__call__ ` member: .. code-block:: python - from typing import Optional, Iterable, Protocol + from collections.abc import Iterable + from typing import Optional, Protocol class Combiner(Protocol): def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ... @@ -314,13 +328,14 @@ member: batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of # different name and kind in the callback -Callback protocols and :py:data:`~typing.Callable` types can be used mostly interchangeably. +Callback protocols and :py:class:`~collections.abc.Callable` types can be used mostly interchangeably. Parameter names in :py:meth:`__call__ ` methods must be identical, unless the parameters are positional-only. Example (using the legacy syntax for generic functions): .. code-block:: python - from typing import Callable, Protocol, TypeVar + from collections.abc import Callable + from typing import Protocol, TypeVar T = TypeVar('T') @@ -345,8 +360,8 @@ Iteration protocols The iteration protocols are useful in many contexts. For example, they allow iteration of objects in for loops. -Iterable[T] ------------ +collections.abc.Iterable[T] +--------------------------- The :ref:`example above ` has a simple implementation of an :py:meth:`__iter__ ` method. @@ -355,17 +370,17 @@ The :ref:`example above ` has a simple implementation of a def __iter__(self) -> Iterator[T] -See also :py:class:`~typing.Iterable`. +See also :py:class:`~collections.abc.Iterable`. -Iterator[T] ------------ +collections.abc.Iterator[T] +--------------------------- .. code-block:: python def __next__(self) -> T def __iter__(self) -> Iterator[T] -See also :py:class:`~typing.Iterator`. +See also :py:class:`~collections.abc.Iterator`. Collection protocols .................... @@ -374,8 +389,8 @@ Many of these are implemented by built-in container types such as :py:class:`list` and :py:class:`dict`, and these are also useful for user-defined collection objects. -Sized ------ +collections.abc.Sized +--------------------- This is a type for objects that support :py:func:`len(x) `. @@ -383,10 +398,10 @@ This is a type for objects that support :py:func:`len(x) `. def __len__(self) -> int -See also :py:class:`~typing.Sized`. +See also :py:class:`~collections.abc.Sized`. -Container[T] ------------- +collections.abc.Container[T] +---------------------------- This is a type for objects that support the ``in`` operator. @@ -394,10 +409,10 @@ This is a type for objects that support the ``in`` operator. def __contains__(self, x: object) -> bool -See also :py:class:`~typing.Container`. +See also :py:class:`~collections.abc.Container`. -Collection[T] -------------- +collections.abc.Collection[T] +----------------------------- .. code-block:: python @@ -405,7 +420,7 @@ Collection[T] def __iter__(self) -> Iterator[T] def __contains__(self, x: object) -> bool -See also :py:class:`~typing.Collection`. +See also :py:class:`~collections.abc.Collection`. One-off protocols ................. @@ -413,8 +428,8 @@ One-off protocols These protocols are typically only useful with a single standard library function or class. -Reversible[T] -------------- +collections.abc.Reversible[T] +----------------------------- This is a type for objects that support :py:func:`reversed(x) `. @@ -422,10 +437,10 @@ This is a type for objects that support :py:func:`reversed(x) `. def __reversed__(self) -> Iterator[T] -See also :py:class:`~typing.Reversible`. +See also :py:class:`~collections.abc.Reversible`. -SupportsAbs[T] --------------- +typing.SupportsAbs[T] +--------------------- This is a type for objects that support :py:func:`abs(x) `. ``T`` is the type of value returned by :py:func:`abs(x) `. @@ -436,8 +451,8 @@ value returned by :py:func:`abs(x) `. See also :py:class:`~typing.SupportsAbs`. -SupportsBytes -------------- +typing.SupportsBytes +-------------------- This is a type for objects that support :py:class:`bytes(x) `. @@ -449,8 +464,8 @@ See also :py:class:`~typing.SupportsBytes`. .. _supports-int-etc: -SupportsComplex ---------------- +typing.SupportsComplex +---------------------- This is a type for objects that support :py:class:`complex(x) `. Note that no arithmetic operations are supported. @@ -461,8 +476,8 @@ are supported. See also :py:class:`~typing.SupportsComplex`. -SupportsFloat -------------- +typing.SupportsFloat +-------------------- This is a type for objects that support :py:class:`float(x) `. Note that no arithmetic operations are supported. @@ -473,8 +488,8 @@ are supported. See also :py:class:`~typing.SupportsFloat`. -SupportsInt ------------ +typing.SupportsInt +------------------ This is a type for objects that support :py:class:`int(x) `. Note that no arithmetic operations are supported. @@ -485,8 +500,8 @@ are supported. See also :py:class:`~typing.SupportsInt`. -SupportsRound[T] ----------------- +typing.SupportsRound[T] +----------------------- This is a type for objects that support :py:func:`round(x) `. @@ -502,33 +517,33 @@ Async protocols These protocols can be useful in async code. See :ref:`async-and-await` for more information. -Awaitable[T] ------------- +collections.abc.Awaitable[T] +---------------------------- .. code-block:: python def __await__(self) -> Generator[Any, None, T] -See also :py:class:`~typing.Awaitable`. +See also :py:class:`~collections.abc.Awaitable`. -AsyncIterable[T] ----------------- +collections.abc.AsyncIterable[T] +-------------------------------- .. code-block:: python def __aiter__(self) -> AsyncIterator[T] -See also :py:class:`~typing.AsyncIterable`. +See also :py:class:`~collections.abc.AsyncIterable`. -AsyncIterator[T] ----------------- +collections.abc.AsyncIterator[T] +-------------------------------- .. code-block:: python def __anext__(self) -> Awaitable[T] def __aiter__(self) -> AsyncIterator[T] -See also :py:class:`~typing.AsyncIterator`. +See also :py:class:`~collections.abc.AsyncIterator`. Context manager protocols ......................... @@ -537,8 +552,8 @@ There are two protocols for context managers -- one for regular context managers and one for async ones. These allow defining objects that can be used in ``with`` and ``async with`` statements. -ContextManager[T] ------------------ +contextlib.AbstractContextManager[T] +------------------------------------ .. code-block:: python @@ -548,10 +563,10 @@ ContextManager[T] exc_value: Optional[BaseException], traceback: Optional[TracebackType]) -> Optional[bool] -See also :py:class:`~typing.ContextManager`. +See also :py:class:`~contextlib.AbstractContextManager`. -AsyncContextManager[T] ----------------------- +contextlib.AbstractAsyncContextManager[T] +----------------------------------------- .. code-block:: python @@ -561,4 +576,4 @@ AsyncContextManager[T] exc_value: Optional[BaseException], traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]] -See also :py:class:`~typing.AsyncContextManager`. +See also :py:class:`~contextlib.AbstractAsyncContextManager`. diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index ee4cdf274ebe..9e7df05e879e 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -21,8 +21,9 @@ problems you may encounter. String literal types and type comments -------------------------------------- -Mypy allows you to add type annotations using ``# type:`` type comments. -For example: +Mypy lets you add type annotations using the (now deprecated) ``# type:`` +type comment syntax. These were required with Python versions older than 3.6, +since they didn't support type annotations on variables. Example: .. code-block:: python diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 131171844dfe..0f016497113e 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -114,7 +114,7 @@ So, we know what ``callable()`` will return. For example: .. code-block:: python - from typing import Callable + from collections.abc import Callable x: Callable[[], int] @@ -128,7 +128,8 @@ for callable and non-callable parts: .. code-block:: python - from typing import Callable, Union + from collections.abc import Callable + from typing import Union x: Union[int, Callable[[], int]] diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index c379b5449eae..e69b3895c668 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -89,7 +89,7 @@ A ``TypedDict`` object is not a subtype of the regular ``dict[...]`` type (and vice versa), since :py:class:`dict` allows arbitrary keys to be added and removed, unlike ``TypedDict``. However, any ``TypedDict`` object is a subtype of (that is, compatible with) ``Mapping[str, object]``, since -:py:class:`~typing.Mapping` only provides read-only access to the dictionary items: +:py:class:`~collections.abc.Mapping` only provides read-only access to the dictionary items: .. code-block:: python @@ -158,7 +158,7 @@ You must use string literals as keys when calling most of the methods, as otherwise mypy won't be able to check that the key is valid. List of supported operations: -* Anything included in :py:class:`~typing.Mapping`: +* Anything included in :py:class:`~collections.abc.Mapping`: * ``d[key]`` * ``key in d`` From db7b61b82a385bf0f9ef40e9f5a1e9c3185fdae1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 26 Sep 2024 15:01:35 +0100 Subject: [PATCH 0763/1617] Make "X | Y" union syntax more prominent in documentation (#17835) Soon 4 out of 5 supported Python versions will support the `X | Y` syntax, so we can make it more prominent (Python 3.13 will be out soon, and 3.8 will reach end of life). Use it in most examples. The syntax is available starting from Python 3.10, but it can be used in annotations in earlier Python versions as well if using `from __future__ import annotations`. --------- Co-authored-by: Brian Schubert Co-authored-by: Jelle Zijlstra --- docs/source/cheat_sheet_py3.rst | 23 ++-- docs/source/command_line.rst | 18 ++-- docs/source/common_issues.rst | 2 +- docs/source/config_file.rst | 6 +- docs/source/error_code_list.rst | 20 ++-- docs/source/generics.rst | 6 +- docs/source/getting_started.rst | 16 +-- docs/source/kinds_of_types.rst | 101 ++++++++++-------- docs/source/literal_types.rst | 2 +- docs/source/more_types.rst | 38 ++++--- docs/source/protocols.rst | 35 +++--- docs/source/runtime_troubles.rst | 4 +- .../source/type_inference_and_annotations.rst | 10 +- docs/source/type_narrowing.rst | 7 +- 14 files changed, 149 insertions(+), 139 deletions(-) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index eb2f8228cf52..7385a66863bf 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -72,9 +72,9 @@ Useful built-in types # On earlier versions, use Union x: list[Union[int, str]] = [3, 5, "test", "fun"] - # Use Optional[X] for a value that could be None - # Optional[X] is the same as X | None or Union[X, None] - x: Optional[str] = "something" if some_condition() else None + # Use X | None for a value that could be None on Python 3.10+ + # Use Optional[X] on 3.9 and earlier; Optional[X] is the same as 'X | None' + x: str | None = "something" if some_condition() else None if x is not None: # Mypy understands x won't be None here because of the if-statement print(x.upper()) @@ -122,13 +122,14 @@ Functions i += 1 # You can of course split a function annotation over multiple lines - def send_email(address: Union[str, list[str]], - sender: str, - cc: Optional[list[str]], - bcc: Optional[list[str]], - subject: str = '', - body: Optional[list[str]] = None - ) -> bool: + def send_email( + address: str | list[str], + sender: str, + cc: list[str] | None, + bcc: list[str] | None, + subject: str = '', + body: list[str] | None = None, + ) -> bool: ... # Mypy understands positional-only and keyword-only arguments @@ -231,7 +232,7 @@ When you're puzzled or when things are complicated # If you initialize a variable with an empty container or "None" # you may have to help mypy a bit by providing an explicit type annotation x: list[str] = [] - x: Optional[str] = None + x: str | None = None # Use Any if you don't know the type of something or it's too # dynamic to write a type for diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 3d4b841e0a7c..062739540ade 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -420,11 +420,11 @@ The following flags adjust how mypy handles values of type ``None``. .. option:: --implicit-optional - This flag causes mypy to treat arguments with a ``None`` - default value as having an implicit :py:data:`~typing.Optional` type. + This flag causes mypy to treat parameters with a ``None`` + default value as having an implicit optional type (``T | None``). For example, if this flag is set, mypy would assume that the ``x`` - parameter is actually of type ``Optional[int]`` in the code snippet below + parameter is actually of type ``int | None`` in the code snippet below, since the default parameter is ``None``: .. code-block:: python @@ -438,7 +438,7 @@ The following flags adjust how mypy handles values of type ``None``. .. option:: --no-strict-optional - This flag effectively disables checking of :py:data:`~typing.Optional` + This flag effectively disables checking of optional types and ``None`` values. With this option, mypy doesn't generally check the use of ``None`` values -- it is treated as compatible with every type. @@ -575,26 +575,24 @@ of the above sections. .. option:: --local-partial-types In mypy, the most common cases for partial types are variables initialized using ``None``, - but without explicit ``Optional`` annotations. By default, mypy won't check partial types + but without explicit ``X | None`` annotations. By default, mypy won't check partial types spanning module top level or class top level. This flag changes the behavior to only allow partial types at local level, therefore it disallows inferring variable type for ``None`` from two assignments in different scopes. For example: .. code-block:: python - from typing import Optional - a = None # Need type annotation here if using --local-partial-types - b: Optional[int] = None + b: int | None = None class Foo: bar = None # Need type annotation here if using --local-partial-types - baz: Optional[int] = None + baz: int | None = None def __init__(self) -> None: self.bar = 1 - reveal_type(Foo().bar) # Union[int, None] without --local-partial-types + reveal_type(Foo().bar) # 'int | None' without --local-partial-types Note: this option is always implicitly enabled in mypy daemon and will become enabled by default for mypy in a future release. diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index c34d20775cfd..39954b8e332a 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -803,7 +803,7 @@ This is best understood via an example: .. code-block:: python - def foo(x: Optional[int]) -> Callable[[], int]: + def foo(x: int | None) -> Callable[[], int]: if x is None: x = 5 print(x + 1) # mypy correctly deduces x must be an int here diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index b0e82a33255a..ded8476b60e3 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -574,8 +574,8 @@ section of the command line docs. :type: boolean :default: False - Causes mypy to treat arguments with a ``None`` - default value as having an implicit :py:data:`~typing.Optional` type. + Causes mypy to treat parameters with a ``None`` + default value as having an implicit optional type (``T | None``). **Note:** This was True by default in mypy versions 0.980 and earlier. @@ -584,7 +584,7 @@ section of the command line docs. :type: boolean :default: True - Effectively disables checking of :py:data:`~typing.Optional` + Effectively disables checking of optional types and ``None`` values. With this option, mypy doesn't generally check the use of ``None`` values -- it is treated as compatible with every type. diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index bb745d9d5c98..2739894dd3ec 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -59,8 +59,6 @@ Example: .. code-block:: python - from typing import Union - class Cat: def sleep(self) -> None: ... def miaow(self) -> None: ... @@ -69,10 +67,10 @@ Example: def sleep(self) -> None: ... def follow_me(self) -> None: ... - def func(animal: Union[Cat, Dog]) -> None: + def func(animal: Cat | Dog) -> None: # OK: 'sleep' is defined for both Cat and Dog animal.sleep() - # Error: Item "Cat" of "Union[Cat, Dog]" has no attribute "follow_me" [union-attr] + # Error: Item "Cat" of "Cat | Dog" has no attribute "follow_me" [union-attr] animal.follow_me() You can often work around these errors by using ``assert isinstance(obj, ClassName)`` @@ -142,9 +140,7 @@ Example: .. code-block:: python - from typing import Optional - - def first(x: list[int]) -> Optional[int]: + def first(x: list[int]) -> int: return x[0] if x else 0 t = (5, 4) @@ -165,7 +161,7 @@ Example: .. code-block:: python - from typing import overload, Optional + from typing import overload @overload def inc_maybe(x: None) -> None: ... @@ -173,7 +169,7 @@ Example: @overload def inc_maybe(x: int) -> int: ... - def inc_maybe(x: Optional[int]) -> Optional[int]: + def inc_maybe(x: int | None) -> int | None: if x is None: return None else: @@ -273,16 +269,14 @@ Example: .. code-block:: python - from typing import Optional, Union - class Base: def method(self, - arg: int) -> Optional[int]: + arg: int) -> int | None: ... class Derived(Base): def method(self, - arg: Union[int, str]) -> int: # OK + arg: int | str) -> int: # OK ... class DerivedBad(Base): diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 982adf9324af..9c0a308ee39a 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -571,9 +571,9 @@ Let us illustrate this by few simple examples: class Square(Shape): ... * Most immutable container types, such as :py:class:`~collections.abc.Sequence` - and :py:class:`~frozenset` are covariant. :py:data:`~typing.Union` is - also covariant in all variables: ``Union[Triangle, int]`` is - a subtype of ``Union[Shape, int]``. + and :py:class:`~frozenset` are covariant. Union types are + also covariant in all union items: ``Triangle | int`` is + a subtype of ``Shape | int``. .. code-block:: python diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 12422ef85bde..28a4481e502e 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -186,19 +186,23 @@ For example, a ``RuntimeError`` instance can be passed to a function that is ann as taking an ``Exception``. As another example, suppose you want to write a function that can accept *either* -ints or strings, but no other types. You can express this using the -:py:data:`~typing.Union` type. For example, ``int`` is a subtype of ``Union[int, str]``: +ints or strings, but no other types. You can express this using a +union type. For example, ``int`` is a subtype of ``int | str``: .. code-block:: python - from typing import Union - - def normalize_id(user_id: Union[int, str]) -> str: + def normalize_id(user_id: int | str) -> str: if isinstance(user_id, int): return f'user-{100_000 + user_id}' else: return user_id +.. note:: + + If using Python 3.9 or earlier, use ``typing.Union[int, str]`` instead of + ``int | str``, or use ``from __future__ import annotations`` at the top of + the file (see :ref:`runtime_troubles`). + The :py:mod:`typing` module contains many other useful types. For a quick overview, look through the :ref:`mypy cheatsheet `. @@ -210,7 +214,7 @@ generic types or your own type aliases), look through the .. note:: When adding types, the convention is to import types - using the form ``from typing import Union`` (as opposed to doing + using the form ``from typing import `` (as opposed to doing just ``import typing`` or ``import typing as t`` or ``from typing import *``). For brevity, we often omit imports from :py:mod:`typing` or :py:mod:`collections.abc` diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index afdf07a559da..54693cddf953 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -228,6 +228,7 @@ instance of ``C`` or the type of ``C`` itself. This also works with .. _union-types: +.. _alternative_union_syntax: Union types *********** @@ -236,8 +237,8 @@ Python functions often accept values of two or more different types. You can use :ref:`overloading ` to represent this, but union types are often more convenient. -Use the ``Union[T1, ..., Tn]`` type constructor to construct a union -type. For example, if an argument has type ``Union[int, str]``, both +Use ``T1 | ... | Tn`` to construct a union +type. For example, if an argument has type ``int | str``, both integers and strings are valid argument values. You can use an :py:func:`isinstance` check to narrow down a union type to a @@ -245,9 +246,7 @@ more specific type: .. code-block:: python - from typing import Union - - def f(x: Union[int, str]) -> None: + def f(x: int | str) -> None: x + 1 # Error: str + int is not valid if isinstance(x, int): # Here type of x is int. @@ -269,20 +268,38 @@ more specific type: since the caller may have to use :py:func:`isinstance` before doing anything interesting with the value. +Python 3.9 and older only partially support this syntax. Instead, you can +use the legacy ``Union[T1, ..., Tn]`` type constructor. Example: + +.. code-block:: python + + from typing import Union + + def f(x: Union[int, str]) -> None: + ... + +It is also possible to use the new syntax with versions of Python where it +isn't supported by the runtime with some limitations, if you use +``from __future__ import annotations`` (see :ref:`runtime_troubles`): + +.. code-block:: python + + from __future__ import annotations + + def f(x: int | str) -> None: # OK on Python 3.7 and later + ... + .. _strict_optional: Optional types and the None type ******************************** -You can use the :py:data:`~typing.Optional` type modifier to define a type variant -that allows ``None``, such as ``Optional[int]`` (``Optional[X]`` is -the preferred shorthand for ``Union[X, None]``): +You can use ``T | None`` to define a type variant that allows ``None`` values, +such as ``int | None``. This is called an *optional type*: .. code-block:: python - from typing import Optional - - def strlen(s: str) -> Optional[int]: + def strlen(s: str) -> int | None: if not s: return None # OK return len(s) @@ -292,12 +309,23 @@ the preferred shorthand for ``Union[X, None]``): return None # Error: None not compatible with int return len(s) -Most operations will not be allowed on unguarded ``None`` or :py:data:`~typing.Optional` -values: +To support Python 3.9 and earlier, you can use the :py:data:`~typing.Optional` +type modifier instead, such as ``Optional[int]`` (``Optional[X]`` is +the preferred shorthand for ``Union[X, None]``): .. code-block:: python - def my_inc(x: Optional[int]) -> int: + from typing import Optional + + def strlen(s: str) -> Optional[int]: + ... + +Most operations will not be allowed on unguarded ``None`` or *optional* values +(values with an optional type): + +.. code-block:: python + + def my_inc(x: int | None) -> int: return x + 1 # Error: Cannot add None and int Instead, an explicit ``None`` check is required. Mypy has @@ -307,7 +335,7 @@ recognizes ``is None`` checks: .. code-block:: python - def my_inc(x: Optional[int]) -> int: + def my_inc(x: int | None) -> int: if x is None: return 0 else: @@ -323,7 +351,7 @@ Other supported checks for guarding against a ``None`` value include .. code-block:: python - def concat(x: Optional[str], y: Optional[str]) -> Optional[str]: + def concat(x: str | None, y: str | None) -> str | None: if x is not None and y is not None: # Both x and y are not None here return x + y @@ -340,7 +368,7 @@ will complain about the possible ``None`` value. You can use .. code-block:: python class Resource: - path: Optional[str] = None + path: str | None = None def initialize(self, path: str) -> None: self.path = path @@ -363,7 +391,7 @@ This is why you need to annotate an attribute in cases like the class .. code-block:: python class Resource: - path: Optional[str] = None + path: str | None = None ... This also works for attributes defined within methods: @@ -372,10 +400,11 @@ This also works for attributes defined within methods: class Counter: def __init__(self) -> None: - self.count: Optional[int] = None + self.count: int | None = None -This is not a problem when using variable annotations, since no initial -value is needed: +Often it's easier to not use any initial value for an attribute. +This way you don't need to use an optional type and can avoid ``assert ... is not None`` +checks. No initial value is needed if you annotate an attribute in the class body: .. code-block:: python @@ -390,13 +419,13 @@ the right thing without an annotation: .. code-block:: python def f(i: int) -> None: - n = None # Inferred type Optional[int] because of the assignment below + n = None # Inferred type 'int | None' because of the assignment below if i > 0: n = i ... Sometimes you may get the error "Cannot determine type of ". In this -case you should add an explicit ``Optional[...]`` annotation (or type comment). +case you should add an explicit ``... | None`` annotation. .. note:: @@ -414,25 +443,11 @@ case you should add an explicit ``Optional[...]`` annotation (or type comment). .. note:: - ``Optional[...]`` *does not* mean a function argument with a default value. - It simply means that ``None`` is a valid value for the argument. This is - a common confusion because ``None`` is a common default value for arguments. - -.. _alternative_union_syntax: - -X | Y syntax for Unions ------------------------ - -:pep:`604` introduced an alternative way for spelling union types. In Python -3.10 and later, you can write ``Union[int, str]`` as ``int | str``. It is -possible to use this syntax in versions of Python where it isn't supported by -the runtime with some limitations (see :ref:`runtime_troubles`). - -.. code-block:: python - - t1: int | str # equivalent to Union[int, str] - - t2: int | None # equivalent to Optional[int] + The type ``Optional[T]`` *does not* mean a function parameter with a default value. + It simply means that ``None`` is a valid argument value. This is + a common confusion because ``None`` is a common default value for parameters, + and parameters with default values are sometimes called *optional* parameters + (or arguments). .. _type-aliases: @@ -501,7 +516,7 @@ introduced in Python 3.10 (:pep:`613`): from typing import TypeAlias # "from typing_extensions" in Python 3.9 and earlier - AliasType: TypeAlias = Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]] + AliasType: TypeAlias = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] .. _named-tuples: diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index 972fce72740f..877ab5de9087 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -70,7 +70,7 @@ complex types involving literals a little more convenient. Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being equivalent to just ``None``. This means that ``Literal[4, None]``, -``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent. +``Literal[4] | None``, and ``Optional[Literal[4]]`` are all equivalent. Literals may also contain aliases to other literal types. For example, the following program is legal: diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 26edd9ef252c..cbf40d5dcaa5 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -116,7 +116,7 @@ implicitly casting from ``UserId`` where ``int`` is expected. Examples: :py:class:`~typing.NewType` accepts exactly two arguments. The first argument must be a string literal containing the name of the new type and must equal the name of the variable to which the new type is assigned. The second argument must be a properly subclassable class, i.e., -not a type construct like :py:data:`~typing.Union`, etc. +not a type construct like a :ref:`union type `, etc. The callable returned by :py:class:`~typing.NewType` accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above). @@ -179,7 +179,7 @@ Function overloading ******************** Sometimes the arguments and types in a function depend on each other -in ways that can't be captured with a :py:data:`~typing.Union`. For example, suppose +in ways that can't be captured with a :ref:`union types `. For example, suppose we want to write a function that can accept x-y coordinates. If we pass in just a single x-y coordinate, we return a ``ClickEvent`` object. However, if we pass in two x-y coordinates, we return a ``DragEvent`` object. @@ -188,12 +188,10 @@ Our first attempt at writing this function might look like this: .. code-block:: python - from typing import Union, Optional - def mouse_event(x1: int, y1: int, - x2: Optional[int] = None, - y2: Optional[int] = None) -> Union[ClickEvent, DragEvent]: + x2: int | None = None, + y2: int | None = None) -> ClickEvent | DragEvent: if x2 is None and y2 is None: return ClickEvent(x1, y1) elif x2 is not None and y2 is not None: @@ -213,7 +211,7 @@ to more accurately describe the function's behavior: .. code-block:: python - from typing import Union, overload + from typing import overload # Overload *variants* for 'mouse_event'. # These variants give extra information to the type checker. @@ -236,8 +234,8 @@ to more accurately describe the function's behavior: def mouse_event(x1: int, y1: int, - x2: Optional[int] = None, - y2: Optional[int] = None) -> Union[ClickEvent, DragEvent]: + x2: int | None = None, + y2: int | None = None) -> ClickEvent | DragEvent: if x2 is None and y2 is None: return ClickEvent(x1, y1) elif x2 is not None and y2 is not None: @@ -283,7 +281,7 @@ Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python from collections.abc import Sequence - from typing import TypeVar, Union, overload + from typing import TypeVar, overload T = TypeVar('T') @@ -294,7 +292,7 @@ Here is the same example using the legacy syntax (Python 3.11 and earlier): @overload def __getitem__(self, index: slice) -> Sequence[T]: ... - def __getitem__(self, index: Union[int, slice]) -> Union[T, Sequence[T]]: + def __getitem__(self, index: int | slice) -> T | Sequence[T]: if isinstance(index, int): # Return a T here elif isinstance(index, slice): @@ -412,9 +410,9 @@ matching variant returns: .. code-block:: python - some_list: Union[list[int], list[str]] + some_list: list[int] | list[str] - # output3 is of type 'Union[float, str]' + # output3 is of type 'float | str' output3 = summarize(some_list) .. note:: @@ -441,7 +439,7 @@ types: .. code-block:: python - from typing import overload, Union + from typing import overload class Expression: # ...snip... @@ -492,7 +490,7 @@ the following unsafe overload definition: .. code-block:: python - from typing import overload, Union + from typing import overload @overload def unsafe_func(x: int) -> int: ... @@ -500,7 +498,7 @@ the following unsafe overload definition: @overload def unsafe_func(x: object) -> str: ... - def unsafe_func(x: object) -> Union[int, str]: + def unsafe_func(x: object) -> int | str: if isinstance(x, int): return 42 else: @@ -569,8 +567,8 @@ Type checking the implementation The body of an implementation is type-checked against the type hints provided on the implementation. For example, in the ``MyList`` example up above, the code in the body is checked with -argument list ``index: Union[int, slice]`` and a return type of -``Union[T, Sequence[T]]``. If there are no annotations on the +argument list ``index: int | slice`` and a return type of +``T | Sequence[T]``. If there are no annotations on the implementation, then the body is not type checked. If you want to force mypy to check the body anyways, use the :option:`--check-untyped-defs ` flag (:ref:`more details here `). @@ -578,10 +576,10 @@ flag (:ref:`more details here `). The variants must also also be compatible with the implementation type hints. In the ``MyList`` example, mypy will check that the parameter type ``int`` and the return type ``T`` are compatible with -``Union[int, slice]`` and ``Union[T, Sequence]`` for the +``int | slice`` and ``T | Sequence`` for the first variant. For the second variant it verifies the parameter type ``slice`` and the return type ``Sequence[T]`` are compatible -with ``Union[int, slice]`` and ``Union[T, Sequence]``. +with ``int | slice`` and ``T | Sequence``. .. note:: diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 8cb53b1fd5ce..ed8d94f62ef1 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -36,11 +36,12 @@ For example, ``IntList`` below is iterable, over ``int`` values: .. code-block:: python + from __future__ import annotations + from collections.abc import Iterator, Iterable - from typing import Optional class IntList: - def __init__(self, value: int, next: Optional['IntList']) -> None: + def __init__(self, value: int, next: IntList | None) -> None: self.value = value self.next = next @@ -237,22 +238,24 @@ such as trees and linked lists: .. code-block:: python - from typing import Optional, Protocol + from __future__ import annotations + + from typing import Protocol class TreeLike(Protocol): value: int @property - def left(self) -> Optional['TreeLike']: ... + def left(self) -> TreeLike | None: ... @property - def right(self) -> Optional['TreeLike']: ... + def right(self) -> TreeLike | None: ... class SimpleTree: def __init__(self, value: int) -> None: self.value = value - self.left: Optional['SimpleTree'] = None - self.right: Optional['SimpleTree'] = None + self.left: SimpleTree | None = None + self.right: SimpleTree | None = None root: TreeLike = SimpleTree(0) # OK @@ -313,15 +316,15 @@ special :py:meth:`__call__ ` member: from typing import Optional, Protocol class Combiner(Protocol): - def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ... + def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes: for item in data: ... - def good_cb(*vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: + def good_cb(*vals: bytes, maxlen: int | None = None) -> list[bytes]: ... - def bad_cb(*vals: bytes, maxitems: Optional[int]) -> list[bytes]: + def bad_cb(*vals: bytes, maxitems: int | None) -> list[bytes]: ... batch_proc([], good_cb) # OK @@ -559,9 +562,9 @@ contextlib.AbstractContextManager[T] def __enter__(self) -> T def __exit__(self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType]) -> Optional[bool] + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None See also :py:class:`~contextlib.AbstractContextManager`. @@ -572,8 +575,8 @@ contextlib.AbstractAsyncContextManager[T] def __aenter__(self) -> Awaitable[T] def __aexit__(self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]] + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> Awaitable[bool | None] See also :py:class:`~contextlib.AbstractAsyncContextManager`. diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index 9e7df05e879e..d039db30f3fa 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -320,8 +320,8 @@ notes at :ref:`future annotations import`. Using X | Y syntax for Unions ----------------------------- -Starting with Python 3.10 (:pep:`604`), you can spell union types as ``x: int | -str``, instead of ``x: typing.Union[int, str]``. +Starting with Python 3.10 (:pep:`604`), you can spell union types as +``x: int | str``, instead of ``x: typing.Union[int, str]``. There is limited support for using this syntax in Python 3.7 and later as well: if you use ``from __future__ import annotations``, mypy will understand this diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst index 6adb4e651224..318ca4cd9160 100644 --- a/docs/source/type_inference_and_annotations.rst +++ b/docs/source/type_inference_and_annotations.rst @@ -41,12 +41,10 @@ variable type annotation: .. code-block:: python - from typing import Union - - x: Union[int, str] = 1 + x: int | str = 1 Without the type annotation, the type of ``x`` would be just ``int``. We -use an annotation to give it a more general type ``Union[int, str]`` (this +use an annotation to give it a more general type ``int | str`` (this type means that the value can be either an ``int`` or a ``str``). The best way to think about this is that the type annotation sets the type of @@ -55,8 +53,8 @@ about the following code: .. code-block:: python - x: Union[int, str] = 1.1 # error: Incompatible types in assignment - # (expression has type "float", variable has type "Union[int, str]") + x: int | str = 1.1 # error: Incompatible types in assignment + # (expression has type "float", variable has type "int | str") .. note:: diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 0f016497113e..231a7edccfe7 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -123,15 +123,14 @@ So, we know what ``callable()`` will return. For example: else: ... # Will never be executed and will raise error with `--warn-unreachable` -``callable`` function can even split ``Union`` type -for callable and non-callable parts: +The ``callable`` function can even split union types into +callable and non-callable parts: .. code-block:: python from collections.abc import Callable - from typing import Union - x: Union[int, Callable[[], int]] + x: int | Callable[[], int] if callable(x): reveal_type(x) # N: Revealed type is "def () -> builtins.int" From 7237d558acb1eff9b8c469cf5b5c1e7a45aa5203 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:39:24 +0200 Subject: [PATCH 0764/1617] Sync typeshed (#17833) Source commit: https://github.com/python/typeshed/commit/a94c927642eda093db226eb3e21c3a4681577f85 --- mypy/typeshed/stdlib/VERSIONS | 2 + mypy/typeshed/stdlib/importlib/abc.pyi | 4 +- .../stdlib/importlib/resources/__init__.pyi | 54 +++++-- .../stdlib/importlib/resources/_common.pyi | 42 +++++ .../importlib/resources/_functional.pyi | 30 ++++ mypy/typeshed/stdlib/nt.pyi | 2 + mypy/typeshed/stdlib/os/__init__.pyi | 11 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 148 +++++++++++++++--- mypy/typeshed/stdlib/typing.pyi | 2 +- 9 files changed, 254 insertions(+), 41 deletions(-) create mode 100644 mypy/typeshed/stdlib/importlib/resources/_common.pyi create mode 100644 mypy/typeshed/stdlib/importlib/resources/_functional.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 66bf2bec7cb0..dfed62f694fc 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -161,6 +161,8 @@ importlib.metadata._meta: 3.10- importlib.metadata.diagnose: 3.13- importlib.readers: 3.10- importlib.resources: 3.7- +importlib.resources._common: 3.11- +importlib.resources._functional: 3.13- importlib.resources.abc: 3.11- importlib.resources.readers: 3.11- importlib.resources.simple: 3.11- diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 3937481159dc..4a0a70d0930d 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -145,10 +145,10 @@ if sys.version_info >= (3, 9): # which is not the case. @overload @abstractmethod - def open(self, mode: Literal["r"] = "r", /, *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... + def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open(self, mode: Literal["rb"], /) -> IO[bytes]: ... + def open(self, mode: Literal["rb"]) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi index 8d656563772c..f82df8c591fa 100644 --- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi @@ -7,10 +7,15 @@ from types import ModuleType from typing import Any, BinaryIO, TextIO from typing_extensions import TypeAlias +if sys.version_info >= (3, 11): + from importlib.resources._common import Package as Package +else: + Package: TypeAlias = str | ModuleType + if sys.version_info >= (3, 9): from importlib.abc import Traversable -__all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] +__all__ = ["Package", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] if sys.version_info >= (3, 9): __all__ += ["as_file", "files"] @@ -18,26 +23,45 @@ if sys.version_info >= (3, 9): if sys.version_info >= (3, 10): __all__ += ["ResourceReader"] -Package: TypeAlias = str | ModuleType +if sys.version_info < (3, 13): + __all__ += ["Resource"] -if sys.version_info >= (3, 11): - Resource: TypeAlias = str -else: +if sys.version_info < (3, 11): Resource: TypeAlias = str | os.PathLike[Any] +elif sys.version_info < (3, 13): + Resource: TypeAlias = str -def open_binary(package: Package, resource: Resource) -> BinaryIO: ... -def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ... -def read_binary(package: Package, resource: Resource) -> bytes: ... -def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ... -def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ... -def is_resource(package: Package, name: str) -> bool: ... -def contents(package: Package) -> Iterator[str]: ... +if sys.version_info >= (3, 13): + from importlib.resources._common import Anchor as Anchor -if sys.version_info >= (3, 9): + __all__ += ["Anchor"] + + from importlib.resources._functional import ( + contents as contents, + is_resource as is_resource, + open_binary as open_binary, + open_text as open_text, + path as path, + read_binary as read_binary, + read_text as read_text, + ) + +else: + def open_binary(package: Package, resource: Resource) -> BinaryIO: ... + def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ... + def read_binary(package: Package, resource: Resource) -> bytes: ... + def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ... + def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ... + def is_resource(package: Package, name: str) -> bool: ... + def contents(package: Package) -> Iterator[str]: ... + +if sys.version_info >= (3, 11): + from importlib.resources._common import as_file as as_file +elif sys.version_info >= (3, 9): def as_file(path: Traversable) -> AbstractContextManager[Path]: ... -if sys.version_info >= (3, 12): - def files(anchor: Package | None = ...) -> Traversable: ... +if sys.version_info >= (3, 11): + from importlib.resources._common import files as files elif sys.version_info >= (3, 9): def files(package: Package) -> Traversable: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi new file mode 100644 index 000000000000..f04f70f25e23 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi @@ -0,0 +1,42 @@ +import sys + +# Even though this file is 3.11+ only, Pyright will complain in stubtest for older versions. +if sys.version_info >= (3, 11): + import types + from collections.abc import Callable + from contextlib import AbstractContextManager + from importlib.abc import ResourceReader, Traversable + from pathlib import Path + from typing import overload + from typing_extensions import TypeAlias, deprecated + + Package: TypeAlias = str | types.ModuleType + + if sys.version_info >= (3, 12): + Anchor: TypeAlias = Package + + def package_to_anchor( + func: Callable[[Anchor | None], Traversable] + ) -> Callable[[Anchor | None, Anchor | None], Traversable]: ... + @overload + def files(anchor: Anchor | None = None) -> Traversable: ... + @overload + @deprecated("First parameter to files is renamed to 'anchor'") + def files(package: Anchor | None = None) -> Traversable: ... + + else: + def files(package: Package) -> Traversable: ... + + def get_resource_reader(package: types.ModuleType) -> ResourceReader | None: ... + + if sys.version_info >= (3, 12): + def resolve(cand: Anchor | None) -> types.ModuleType: ... + + else: + def resolve(cand: Package) -> types.ModuleType: ... + + if sys.version_info < (3, 12): + def get_package(package: Package) -> types.ModuleType: ... + + def from_package(package: types.ModuleType) -> Traversable: ... + def as_file(path: Traversable) -> AbstractContextManager[Path]: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi new file mode 100644 index 000000000000..97e46bdf0a53 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi @@ -0,0 +1,30 @@ +import sys + +# Even though this file is 3.13+ only, Pyright will complain in stubtest for older versions. +if sys.version_info >= (3, 13): + from _typeshed import StrPath + from collections.abc import Iterator + from contextlib import AbstractContextManager + from importlib.resources._common import Anchor + from io import TextIOWrapper + from pathlib import Path + from typing import BinaryIO, overload + from typing_extensions import Unpack + + def open_binary(anchor: Anchor, *path_names: StrPath) -> BinaryIO: ... + @overload + def open_text( + anchor: Anchor, *path_names: Unpack[tuple[StrPath]], encoding: str | None = "utf-8", errors: str | None = "strict" + ) -> TextIOWrapper: ... + @overload + def open_text(anchor: Anchor, *path_names: StrPath, encoding: str | None, errors: str | None = "strict") -> TextIOWrapper: ... + def read_binary(anchor: Anchor, *path_names: StrPath) -> bytes: ... + @overload + def read_text( + anchor: Anchor, *path_names: Unpack[tuple[StrPath]], encoding: str | None = "utf-8", errors: str | None = "strict" + ) -> str: ... + @overload + def read_text(anchor: Anchor, *path_names: StrPath, encoding: str | None, errors: str | None = "strict") -> str: ... + def path(anchor: Anchor, *path_names: StrPath) -> AbstractContextManager[Path]: ... + def is_resource(anchor: Anchor, *path_names: StrPath) -> bool: ... + def contents(anchor: Anchor, *path_names: StrPath) -> Iterator[str]: ... diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi index 4066096f4c71..e1d57d09a9bd 100644 --- a/mypy/typeshed/stdlib/nt.pyi +++ b/mypy/typeshed/stdlib/nt.pyi @@ -107,5 +107,7 @@ if sys.platform == "win32": listvolumes as listvolumes, set_blocking as set_blocking, ) + if sys.version_info >= (3, 13): + from os import fchmod as fchmod, lchmod as lchmod environ: dict[str, str] diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 700e0e9df310..d7bb4883a0f2 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -673,7 +673,6 @@ if sys.version_info >= (3, 12) or sys.platform != "win32": def set_blocking(fd: int, blocking: bool, /) -> None: ... if sys.platform != "win32": - def fchmod(fd: int, mode: int) -> None: ... def fchown(fd: int, uid: int, gid: int) -> None: ... def fpathconf(fd: int, name: str | int, /) -> int: ... def fstatvfs(fd: int, /) -> statvfs_result: ... @@ -754,7 +753,6 @@ def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, f if sys.platform != "win32" and sys.platform != "linux": def chflags(path: StrOrBytesPath, flags: int, follow_symlinks: bool = True) -> None: ... # some flavors of Unix def lchflags(path: StrOrBytesPath, flags: int) -> None: ... - def lchmod(path: StrOrBytesPath, mode: int) -> None: ... if sys.platform != "win32": def chroot(path: StrOrBytesPath) -> None: ... @@ -1179,3 +1177,12 @@ if sys.version_info >= (3, 13) and sys.platform == "linux": def timerfd_settime_ns(fd: FileDescriptor, /, *, flags: int = 0, initial: int = 0, interval: int = 0) -> tuple[int, int]: ... def timerfd_gettime(fd: FileDescriptor, /) -> tuple[float, float]: ... def timerfd_gettime_ns(fd: FileDescriptor, /) -> tuple[int, int]: ... + +if sys.version_info >= (3, 13) or sys.platform != "win32": + # Added to Windows in 3.13. + def fchmod(fd: int, mode: int) -> None: ... + +if sys.platform != "linux": + if sys.version_info >= (3, 13) or sys.platform != "win32": + # Added to Windows in 3.13. + def lchmod(path: StrOrBytesPath, mode: int) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 2a42eb789731..4d25a04f8eb7 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -3025,27 +3025,133 @@ class Text(Widget, XView, YView): config = configure def bbox(self, index: _TextIndex) -> tuple[int, int, int, int] | None: ... # type: ignore[override] def compare(self, index1: _TextIndex, op: Literal["<", "<=", "==", ">=", ">", "!="], index2: _TextIndex) -> bool: ... - @overload - def count(self, index1: _TextIndex, index2: _TextIndex) -> tuple[int] | None: ... - @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], /) -> tuple[int] | None: ... - @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: Literal["update"], arg2: _WhatToCount, /) -> int | None: ... - @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: Literal["update"], /) -> int | None: ... - @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /) -> tuple[int, int]: ... - @overload - def count( - self, - index1: _TextIndex, - index2: _TextIndex, - arg1: _WhatToCount | Literal["update"], - arg2: _WhatToCount | Literal["update"], - arg3: _WhatToCount | Literal["update"], - /, - *args: _WhatToCount | Literal["update"], - ) -> tuple[int, ...]: ... + if sys.version_info >= (3, 13): + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[True]) -> int: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], /, *, return_ints: Literal[True] + ) -> int: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: Literal["update"], + arg2: _WhatToCount, + /, + *, + return_ints: Literal[True], + ) -> int: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: Literal["update"], + /, + *, + return_ints: Literal[True], + ) -> int: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /, *, return_ints: Literal[True] + ) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + return_ints: Literal[True], + ) -> tuple[int, ...]: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[False] = False) -> tuple[int] | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg: _WhatToCount | Literal["update"], + /, + *, + return_ints: Literal[False] = False, + ) -> tuple[int] | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: Literal["update"], + arg2: _WhatToCount, + /, + *, + return_ints: Literal[False] = False, + ) -> int | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: Literal["update"], + /, + *, + return_ints: Literal[False] = False, + ) -> int | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: _WhatToCount, + /, + *, + return_ints: Literal[False] = False, + ) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + return_ints: Literal[False] = False, + ) -> tuple[int, ...]: ... + else: + @overload + def count(self, index1: _TextIndex, index2: _TextIndex) -> tuple[int] | None: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], / + ) -> tuple[int] | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: Literal["update"], arg2: _WhatToCount, /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: Literal["update"], /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + ) -> tuple[int, ...]: ... + @overload def debug(self, boolean: None = None) -> bool: ... @overload diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index cadd06358d4a..784f30ad7397 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -540,7 +540,7 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): def __aiter__(self) -> AsyncIterator[_T_co]: ... class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): - def __anext__(self) -> Awaitable[_YieldT_co]: ... + def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... @abstractmethod def asend(self, value: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @overload From 1c018589a621e38e4851a64f500a42ebe99ef21d Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Fri, 27 Sep 2024 02:32:59 -0400 Subject: [PATCH 0765/1617] Fix negative narrowing of tuples in match statement (#17817) Fixes #17328 ### Before Lines marked with `!!!` denote incorrect behavior. ([Playground link](https://mypy-play.net/?mypy=1.11.2&python=3.12&flags=strict%2Cwarn-unreachable&gist=7a7081c5fbc2fac9987f24e02421f24f)) ```python from typing import Literal m4: tuple[Literal[1], int] match m4: case (1, 5): reveal_type(m4) # N: Revealed type is "tuple[Literal[1], Literal[5]]" case (1, 6): reveal_type(m4) # !!! E: Statement is unreachable [unreachable] case _: reveal_type(m4) # !!! N: Revealed type is "tuple[Never, builtins.int]" m5: tuple[Literal[1, 2], Literal["a", "b"]] match m5: case (1, str()): reveal_type(m5) # N: Revealed type is "tuple[Literal[1], Union[Literal['a'], Literal['b']]]" case _: reveal_type(m5) # !!! N: Revealed type is "tuple[Literal[2], Never]" match m5: case (1, "a"): reveal_type(m5) # N: Revealed type is "tuple[Literal[1], Literal['a']]" case _: reveal_type(m5) # !!! N: Revealed type is "tuple[Literal[2], Literal['b']]" ``` ### After ```python from typing import Literal m4: tuple[Literal[1], int] match m4: case (1, 5): reveal_type(m4) # N: Revealed type is "tuple[Literal[1], Literal[5]]" case (1, 6): reveal_type(m4) # N: Revealed type is "tuple[Literal[1], Literal[6]]" case _: reveal_type(m4) # N: Revealed type is "tuple[Literal[1], builtins.int]" m5: tuple[Literal[1, 2], Literal["a", "b"]] match m5: case (1, str()): reveal_type(m5) # N: Revealed type is "tuple[Literal[1], Union[Literal['a'], Literal['b']]]" case _: reveal_type(m5) # N: Revealed type is "tuple[Literal[2], Union[Literal['a'], Literal['b']]]" match m5: case (1, "a"): reveal_type(m5) # N: Revealed type is "tuple[Literal[1], Literal['a']]" case _: reveal_type(m5) # N: Revealed type is "tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" ``` --- mypy/checkpattern.py | 12 +++++++++++- test-data/unit/check-python310.test | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index a23be464b825..cb3577ce2f6e 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -307,7 +307,7 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: for inner_type, new_inner_type in zip(inner_types, new_inner_types): (narrowed_inner_type, inner_rest_type) = ( self.chk.conditional_types_with_intersection( - new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type + inner_type, [get_type_range(new_inner_type)], o, default=inner_type ) ) narrowed_inner_types.append(narrowed_inner_type) @@ -320,6 +320,16 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: if all(is_uninhabited(typ) for typ in inner_rest_types): # All subpatterns always match, so we can apply negative narrowing rest_type = TupleType(rest_inner_types, current_type.partial_fallback) + elif sum(not is_uninhabited(typ) for typ in inner_rest_types) == 1: + # Exactly one subpattern may conditionally match, the rest always match. + # We can apply negative narrowing to this one position. + rest_type = TupleType( + [ + curr if is_uninhabited(rest) else rest + for curr, rest in zip(inner_types, inner_rest_types) + ], + current_type.partial_fallback, + ) elif isinstance(current_type, TupleType): # For variadic tuples it is too tricky to match individual items like for fixed # tuples, so we instead try to narrow the entire type. diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 5ecc69dc7c32..e7028a027e25 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1424,6 +1424,7 @@ def f(value: Literal[1] | Literal[2]) -> int: [case testMatchSequencePatternNegativeNarrowing] from typing import Union, Sequence, Tuple +from typing_extensions import Literal m1: Sequence[int | str] @@ -1448,6 +1449,31 @@ match m3: reveal_type(m3) # N: Revealed type is "Tuple[Literal[1]]" case r2: reveal_type(m3) # N: Revealed type is "Tuple[Union[builtins.int, builtins.str]]" + +m4: Tuple[Literal[1], int] + +match m4: + case (1, 5): + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], Literal[5]]" + case (1, 6): + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], Literal[6]]" + case _: + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], builtins.int]" + +m5: Tuple[Literal[1, 2], Literal["a", "b"]] + +match m5: + case (1, str()): + reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Union[Literal['a'], Literal['b']]]" + case _: + reveal_type(m5) # N: Revealed type is "Tuple[Literal[2], Union[Literal['a'], Literal['b']]]" + +match m5: + case (1, "a"): + reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Literal['a']]" + case _: + reveal_type(m5) # N: Revealed type is "Tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" + [builtins fixtures/tuple.pyi] [case testMatchEnumSingleChoice] From 1995155ef80a9e4ccc434240a93843868afc4a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:43:06 -0600 Subject: [PATCH 0766/1617] Document `--output=json` CLI option (#17611) Documents the new option added by https://github.com/python/mypy/pull/11396 --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- docs/source/command_line.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 062739540ade..20fb3821438a 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -96,6 +96,10 @@ Optional arguments Show program's version number and exit. +.. option:: -O FORMAT, --output FORMAT {json} + + Set a custom output format. + .. _config-file-flag: Config file From 26a77f9d373a49fa3796082fb55a54517a876364 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Sep 2024 17:03:59 +0100 Subject: [PATCH 0767/1617] Avoid type size explosion when expanding types (#17842) If TypedDict A has multiple items that refer to TypedDict B, don't duplicate the types representing B during type expansion (or generally when translating types). If TypedDicts are deeply nested, this could result in lot of redundant type objects. Example where this could matter (assume B is a big TypedDict): ``` class B(TypedDict): ... class A(TypedDict): a: B b: B c: B ... z: B ``` Also deduplicate large unions. It's common to have aliases that are defined as large unions, and again we want to avoid duplicating these unions. This may help with #17231, but this fix may not be sufficient. --- mypy/applytype.py | 1 + mypy/erasetype.py | 1 + mypy/expandtype.py | 19 +++++++++++++++++-- mypy/subtypes.py | 2 ++ mypy/type_visitor.py | 36 ++++++++++++++++++++++++++++++++++-- mypy/typeanal.py | 2 ++ mypy/types.py | 13 +++++++++---- 7 files changed, 66 insertions(+), 8 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 783748cd8a5e..e88947cc6430 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -215,6 +215,7 @@ def __init__( bound_tvars: frozenset[TypeVarLikeType] = frozenset(), seen_aliases: frozenset[TypeInfo] = frozenset(), ) -> None: + super().__init__() self.poly_tvars = set(poly_tvars) # This is a simplified version of TypeVarScope used during semantic analysis. self.bound_tvars = bound_tvars diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 5d95b221af15..222e7f2a6d7a 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -161,6 +161,7 @@ class TypeVarEraser(TypeTranslator): """Implementation of type erasure""" def __init__(self, erase_id: Callable[[TypeVarId], bool], replacement: Type) -> None: + super().__init__() self.erase_id = erase_id self.replacement = replacement diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 9336be54437b..b2040ec074c3 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -179,6 +179,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator): variables: Mapping[TypeVarId, Type] # TypeVar id -> TypeVar value def __init__(self, variables: Mapping[TypeVarId, Type]) -> None: + super().__init__() self.variables = variables self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {} @@ -454,15 +455,25 @@ def visit_tuple_type(self, t: TupleType) -> Type: return t.copy_modified(items=items, fallback=fallback) def visit_typeddict_type(self, t: TypedDictType) -> Type: + if cached := self.get_cached(t): + return cached fallback = t.fallback.accept(self) assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) - return t.copy_modified(item_types=self.expand_types(t.items.values()), fallback=fallback) + result = t.copy_modified(item_types=self.expand_types(t.items.values()), fallback=fallback) + self.set_cached(t, result) + return result def visit_literal_type(self, t: LiteralType) -> Type: # TODO: Verify this implementation is correct return t def visit_union_type(self, t: UnionType) -> Type: + # Use cache to avoid O(n**2) or worse expansion of types during translation + # (only for large unions, since caching adds overhead) + use_cache = len(t.items) > 3 + if use_cache and (cached := self.get_cached(t)): + return cached + expanded = self.expand_types(t.items) # After substituting for type variables in t.items, some resulting types # might be subtypes of others, however calling make_simplified_union() @@ -475,7 +486,11 @@ def visit_union_type(self, t: UnionType) -> Type: # otherwise a single item union of a type alias will break it. Note this should not # cause infinite recursion since pathological aliases like A = Union[A, B] are # banned at the semantic analysis level. - return get_proper_type(simplified) + result = get_proper_type(simplified) + + if use_cache: + self.set_cached(t, result) + return result def visit_partial_type(self, t: PartialType) -> Type: return t diff --git a/mypy/subtypes.py b/mypy/subtypes.py index c76b3569fdd4..608d098791a9 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -886,6 +886,8 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: if isinstance(right, Instance): return self._is_subtype(left.fallback, right) elif isinstance(right, TypedDictType): + if left == right: + return True # Fast path if not left.names_are_wider_than(right): return False for name, l, r in left.zip(right): diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 59e13d12485c..38e4c5ba0d01 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -181,8 +181,26 @@ class TypeTranslator(TypeVisitor[Type]): Subclass this and override some methods to implement a non-trivial transformation. + + We cache the results of certain translations to avoid + massively expanding the sizes of types. """ + def __init__(self, cache: dict[Type, Type] | None = None) -> None: + # For deduplication of results + self.cache = cache + + def get_cached(self, t: Type) -> Type | None: + if self.cache is None: + return None + return self.cache.get(t) + + def set_cached(self, orig: Type, new: Type) -> None: + if self.cache is None: + # Minor optimization: construct lazily + self.cache = {} + self.cache[orig] = new + def visit_unbound_type(self, t: UnboundType) -> Type: return t @@ -251,8 +269,11 @@ def visit_tuple_type(self, t: TupleType) -> Type: ) def visit_typeddict_type(self, t: TypedDictType) -> Type: + # Use cache to avoid O(n**2) or worse expansion of types during translation + if cached := self.get_cached(t): + return cached items = {item_name: item_type.accept(self) for (item_name, item_type) in t.items.items()} - return TypedDictType( + result = TypedDictType( items, t.required_keys, # TODO: This appears to be unsafe. @@ -260,6 +281,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: t.line, t.column, ) + self.set_cached(t, result) + return result def visit_literal_type(self, t: LiteralType) -> Type: fallback = t.fallback.accept(self) @@ -267,12 +290,21 @@ def visit_literal_type(self, t: LiteralType) -> Type: return LiteralType(value=t.value, fallback=fallback, line=t.line, column=t.column) def visit_union_type(self, t: UnionType) -> Type: - return UnionType( + # Use cache to avoid O(n**2) or worse expansion of types during translation + # (only for large unions, since caching adds overhead) + use_cache = len(t.items) > 3 + if use_cache and (cached := self.get_cached(t)): + return cached + + result = UnionType( self.translate_types(t.items), t.line, t.column, uses_pep604_syntax=t.uses_pep604_syntax, ) + if use_cache: + self.set_cached(t, result) + return result def translate_types(self, types: Iterable[Type]) -> list[Type]: return [t.accept(self) for t in types] diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 274b4b893a98..6c94390c23dc 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2271,6 +2271,7 @@ def __init__( lookup: Callable[[str, Context], SymbolTableNode | None], scope: TypeVarLikeScope, ) -> None: + super().__init__() self.seen_nodes = seen_nodes self.lookup = lookup self.scope = scope @@ -2660,6 +2661,7 @@ class TypeVarDefaultTranslator(TrivialSyntheticTypeTranslator): def __init__( self, api: SemanticAnalyzerInterface, tvar_expr_name: str, context: Context ) -> None: + super().__init__() self.api = api self.tvar_expr_name = tvar_expr_name self.context = context diff --git a/mypy/types.py b/mypy/types.py index 78244d0f9cf4..b1e57b2f6a86 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -357,7 +357,7 @@ def _expand_once(self) -> Type: def _partial_expansion(self, nothing_args: bool = False) -> tuple[ProperType, bool]: # Private method mostly for debugging and testing. - unroller = UnrollAliasVisitor(set()) + unroller = UnrollAliasVisitor(set(), {}) if nothing_args: alias = self.copy_modified(args=[UninhabitedType()] * len(self.args)) else: @@ -2586,7 +2586,8 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: if not isinstance(other, TypedDictType): return NotImplemented - + if self is other: + return True return ( frozenset(self.items.keys()) == frozenset(other.items.keys()) and all( @@ -3507,7 +3508,11 @@ def visit_type_list(self, t: TypeList) -> Type: class UnrollAliasVisitor(TrivialSyntheticTypeTranslator): - def __init__(self, initial_aliases: set[TypeAliasType]) -> None: + def __init__( + self, initial_aliases: set[TypeAliasType], cache: dict[Type, Type] | None + ) -> None: + assert cache is not None + super().__init__(cache) self.recursed = False self.initial_aliases = initial_aliases @@ -3519,7 +3524,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: # A = Tuple[B, B] # B = int # will not be detected as recursive on the second encounter of B. - subvisitor = UnrollAliasVisitor(self.initial_aliases | {t}) + subvisitor = UnrollAliasVisitor(self.initial_aliases | {t}, self.cache) result = get_proper_type(t).accept(subvisitor) if subvisitor.recursed: self.recursed = True From ca284252de6455444806ef9752d8a30d3a9d4c37 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 28 Sep 2024 16:20:16 -0400 Subject: [PATCH 0768/1617] Fix get_member_expr_fullname returning strings with embedded "None" (#17848) Fixes #17847 ### Before ```python from mypy.nodes import CallExpr, MemberExpr, NameExpr, get_member_expr_fullname m3 = MemberExpr(MemberExpr(CallExpr(NameExpr("a"), [], [], []), "b"), "c") # a().b.c >>> get_member_expr_fullname(m3) 'None.c' ``` ### After ```python from mypy.nodes import CallExpr, MemberExpr, NameExpr, get_member_expr_fullname m3 = MemberExpr(MemberExpr(CallExpr(NameExpr("a"), [], [], []), "b"), "c") # a().b.c >>> get_member_expr_fullname(m3) is None True ``` --- mypy/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 4a5c7240fa83..39cbee3c8525 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -4090,7 +4090,7 @@ def get_member_expr_fullname(expr: MemberExpr) -> str | None: initial = expr.expr.name elif isinstance(expr.expr, MemberExpr): initial = get_member_expr_fullname(expr.expr) - else: + if initial is None: return None return f"{initial}.{expr.name}" From 1a2c8e2a4df21532e4952191cad74ae50083f4ad Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Sep 2024 14:20:45 -0700 Subject: [PATCH 0769/1617] Fix tests on latest Python 3.13 (and 3.12) (#17849) Related to python/cpython@c6c3d970ba54f4ad4c4151bb262cef96d3299480 (python/cpython#121329), thanks to brianschubert for noticing --- mypyc/test-data/run-dicts.test | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index 58b862e3f303..d4f5b945309e 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -157,7 +157,11 @@ else: try: clear_during_iter(d) except RuntimeError as e: - assert str(e) == "OrderedDict changed size during iteration" + assert str(e) in ( + "OrderedDict changed size during iteration", + # Error message changed in Python 3.13 and some 3.12 patch version + "OrderedDict mutated during iteration", + ) else: assert False From 6726d77cd8414fccb5e52b084610aa47f5f189d3 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 30 Sep 2024 14:41:43 +0300 Subject: [PATCH 0770/1617] Add `ReadOnly` support for TypedDicts (#17644) Refs https://github.com/python/mypy/issues/17264 I will add docs in a separate PR. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/error_code_list.rst | 24 ++ mypy/checkexpr.py | 14 +- mypy/checkmember.py | 5 +- mypy/checkpattern.py | 2 +- mypy/copytype.py | 4 +- mypy/errorcodes.py | 3 + mypy/exprtotype.py | 2 +- mypy/fastparse.py | 2 +- mypy/join.py | 7 +- mypy/meet.py | 12 +- mypy/messages.py | 20 +- mypy/plugins/default.py | 22 +- mypy/plugins/proper_plugin.py | 1 + mypy/semanal.py | 8 +- mypy/semanal_shared.py | 2 +- mypy/semanal_typeddict.py | 105 ++++-- mypy/server/astdiff.py | 3 +- mypy/subtypes.py | 11 + mypy/type_visitor.py | 1 + mypy/typeanal.py | 50 ++- mypy/types.py | 56 ++- test-data/unit/check-typeddict.test | 338 +++++++++++++++++- test-data/unit/fine-grained.test | 28 ++ test-data/unit/fixtures/typing-typeddict.pyi | 5 + test-data/unit/lib-stub/typing_extensions.pyi | 1 + 25 files changed, 652 insertions(+), 74 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 2739894dd3ec..73171131bc8d 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1217,6 +1217,30 @@ If the code being checked is not syntactically valid, mypy issues a syntax error. Most, but not all, syntax errors are *blocking errors*: they can't be ignored with a ``# type: ignore`` comment. +.. _code-typeddict-readonly-mutated: + +ReadOnly key of a TypedDict is mutated [typeddict-readonly-mutated] +------------------------------------------------------------------- + +Consider this example: + +.. code-block:: python + + from datetime import datetime + from typing import TypedDict + from typing_extensions import ReadOnly + + class User(TypedDict): + username: ReadOnly[str] + last_active: datetime + + user: User = {'username': 'foobar', 'last_active': datetime.now()} + user['last_active'] = datetime.now() # ok + user['username'] = 'other' # error: ReadOnly TypedDict key "key" TypedDict is mutated [typeddict-readonly-mutated] + +`PEP 705 `_ specifies +how ``ReadOnly`` special form works for ``TypedDict`` objects. + .. _code-misc: Miscellaneous checks [misc] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 55c42335744d..98e6eb6a7fc3 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -986,6 +986,10 @@ def check_typeddict_call_with_kwargs( always_present_keys: set[str], ) -> Type: actual_keys = kwargs.keys() + if callee.to_be_mutated: + assigned_readonly_keys = actual_keys & callee.readonly_keys + if assigned_readonly_keys: + self.msg.readonly_keys_mutated(assigned_readonly_keys, context=context) if not ( callee.required_keys <= always_present_keys and actual_keys <= callee.items.keys() ): @@ -4349,7 +4353,7 @@ def visit_index_with_type( else: return self.nonliteral_tuple_index_helper(left_type, index) elif isinstance(left_type, TypedDictType): - return self.visit_typeddict_index_expr(left_type, e.index) + return self.visit_typeddict_index_expr(left_type, e.index)[0] elif isinstance(left_type, FunctionLike) and left_type.is_type_obj(): if left_type.type_object().is_enum: return self.visit_enum_index_expr(left_type.type_object(), e.index, e) @@ -4530,7 +4534,7 @@ def union_tuple_fallback_item(self, left_type: TupleType) -> Type: def visit_typeddict_index_expr( self, td_type: TypedDictType, index: Expression, setitem: bool = False - ) -> Type: + ) -> tuple[Type, set[str]]: if isinstance(index, StrExpr): key_names = [index.value] else: @@ -4553,17 +4557,17 @@ def visit_typeddict_index_expr( key_names.append(key_type.value) else: self.msg.typeddict_key_must_be_string_literal(td_type, index) - return AnyType(TypeOfAny.from_error) + return AnyType(TypeOfAny.from_error), set() value_types = [] for key_name in key_names: value_type = td_type.items.get(key_name) if value_type is None: self.msg.typeddict_key_not_found(td_type, key_name, index, setitem) - return AnyType(TypeOfAny.from_error) + return AnyType(TypeOfAny.from_error), set() else: value_types.append(value_type) - return make_simplified_union(value_types) + return make_simplified_union(value_types), set(key_names) def visit_enum_index_expr( self, enum_type: TypeInfo, index: Expression, context: Context diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0f117f5475ed..8f99f96e2dd5 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1185,9 +1185,12 @@ def analyze_typeddict_access( if isinstance(mx.context, IndexExpr): # Since we can get this during `a['key'] = ...` # it is safe to assume that the context is `IndexExpr`. - item_type = mx.chk.expr_checker.visit_typeddict_index_expr( + item_type, key_names = mx.chk.expr_checker.visit_typeddict_index_expr( typ, mx.context.index, setitem=True ) + assigned_readonly_keys = typ.readonly_keys & key_names + if assigned_readonly_keys: + mx.msg.readonly_keys_mutated(assigned_readonly_keys, context=mx.context) else: # It can also be `a.__setitem__(...)` direct call. # In this case `item_type` can be `Any`, diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index cb3577ce2f6e..fa23dfb5f453 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -498,7 +498,7 @@ def get_mapping_item_type( with self.msg.filter_errors() as local_errors: result: Type | None = self.chk.expr_checker.visit_typeddict_index_expr( mapping_type, key - ) + )[0] has_local_errors = local_errors.has_new_errors() # If we can't determine the type statically fall back to treating it as a normal # mapping diff --git a/mypy/copytype.py b/mypy/copytype.py index 465f06566f54..ecb1a89759b6 100644 --- a/mypy/copytype.py +++ b/mypy/copytype.py @@ -107,7 +107,9 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: return self.copy_common(t, TupleType(t.items, t.partial_fallback, implicit=t.implicit)) def visit_typeddict_type(self, t: TypedDictType) -> ProperType: - return self.copy_common(t, TypedDictType(t.items, t.required_keys, t.fallback)) + return self.copy_common( + t, TypedDictType(t.items, t.required_keys, t.readonly_keys, t.fallback) + ) def visit_literal_type(self, t: LiteralType) -> ProperType: return self.copy_common(t, LiteralType(value=t.value, fallback=t.fallback)) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index ad061b161af1..a170b5d4d65a 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -185,6 +185,9 @@ def __hash__(self) -> int: ANNOTATION_UNCHECKED = ErrorCode( "annotation-unchecked", "Notify about type annotations in unchecked functions", "General" ) +TYPEDDICT_READONLY_MUTATED = ErrorCode( + "typeddict-readonly-mutated", "TypedDict's ReadOnly key is mutated", "General" +) POSSIBLY_UNDEFINED: Final[ErrorCode] = ErrorCode( "possibly-undefined", "Warn about variables that are defined only in some execution paths", diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index c7df851668be..506194a4b285 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -244,7 +244,7 @@ def expr_to_unanalyzed_type( value, options, allow_new_syntax, expr ) result = TypedDictType( - items, set(), Instance(MISSING_FALLBACK, ()), expr.line, expr.column + items, set(), set(), Instance(MISSING_FALLBACK, ()), expr.line, expr.column ) result.extra_items_from = extra_items_from return result diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 18858b0fa0b8..bbbe2184738c 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2130,7 +2130,7 @@ def visit_Dict(self, n: ast3.Dict) -> Type: continue return self.invalid_type(n) items[item_name.value] = self.visit(value) - result = TypedDictType(items, set(), _dummy_fallback, n.lineno, n.col_offset) + result = TypedDictType(items, set(), set(), _dummy_fallback, n.lineno, n.col_offset) result.extra_items_from = extra_items_from return result diff --git a/mypy/join.py b/mypy/join.py index 5284be7dd2a1..865dd073d081 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -631,10 +631,13 @@ def visit_typeddict_type(self, t: TypedDictType) -> ProperType: ) } fallback = self.s.create_anonymous_fallback() + all_keys = set(items.keys()) # We need to filter by items.keys() since some required keys present in both t and # self.s might be missing from the join if the types are incompatible. - required_keys = set(items.keys()) & t.required_keys & self.s.required_keys - return TypedDictType(items, required_keys, fallback) + required_keys = all_keys & t.required_keys & self.s.required_keys + # If one type has a key as readonly, we mark it as readonly for both: + readonly_keys = (t.readonly_keys | t.readonly_keys) & all_keys + return TypedDictType(items, required_keys, readonly_keys, fallback) elif isinstance(self.s, Instance): return join_types(self.s, t.fallback) else: diff --git a/mypy/meet.py b/mypy/meet.py index 91abf43c0877..9f5c2d72a8cb 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1017,7 +1017,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> ProperType: items = dict(item_list) fallback = self.s.create_anonymous_fallback() required_keys = t.required_keys | self.s.required_keys - return TypedDictType(items, required_keys, fallback) + readonly_keys = t.readonly_keys | self.s.readonly_keys + return TypedDictType(items, required_keys, readonly_keys, fallback) elif isinstance(self.s, Instance) and is_subtype(t, self.s): return t else: @@ -1139,6 +1140,9 @@ def typed_dict_mapping_overlap( - TypedDict(x=str, y=str, total=False) doesn't overlap with Dict[str, int] - TypedDict(x=int, y=str, total=False) overlaps with Dict[str, str] + * A TypedDict with at least one ReadOnly[] key does not overlap + with Dict or MutableMapping, because they assume mutable data. + As usual empty, dictionaries lie in a gray area. In general, List[str] and List[str] are considered non-overlapping despite empty list belongs to both. However, List[int] and List[Never] are considered overlapping. @@ -1159,6 +1163,12 @@ def typed_dict_mapping_overlap( assert isinstance(right, TypedDictType) typed, other = right, left + mutable_mapping = next( + (base for base in other.type.mro if base.fullname == "typing.MutableMapping"), None + ) + if mutable_mapping is not None and typed.readonly_keys: + return False + mapping = next(base for base in other.type.mro if base.fullname == "typing.Mapping") other = map_instance_to_supertype(other, mapping) key_type, value_type = get_proper_types(other.args) diff --git a/mypy/messages.py b/mypy/messages.py index 6567d9d96d0b..adf150eab50a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -926,6 +926,17 @@ def invalid_index_type( code=code, ) + def readonly_keys_mutated(self, keys: set[str], context: Context) -> None: + if len(keys) == 1: + suffix = "is" + else: + suffix = "are" + self.fail( + "ReadOnly {} TypedDict {} mutated".format(format_key_list(sorted(keys)), suffix), + code=codes.TYPEDDICT_READONLY_MUTATED, + context=context, + ) + def too_few_arguments( self, callee: CallableType, context: Context, argument_names: Sequence[str | None] | None ) -> None: @@ -2613,10 +2624,13 @@ def format_literal_value(typ: LiteralType) -> str: return format(typ.fallback) items = [] for item_name, item_type in typ.items.items(): - modifier = "" if item_name in typ.required_keys else "?" + modifier = "" + if item_name not in typ.required_keys: + modifier += "?" + if item_name in typ.readonly_keys: + modifier += "=" items.append(f"{item_name!r}{modifier}: {format(item_type)}") - s = f"TypedDict({{{', '.join(items)}}})" - return s + return f"TypedDict({{{', '.join(items)}}})" elif isinstance(typ, LiteralType): return f"Literal[{format_literal_value(typ)}]" elif isinstance(typ, UnionType): diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 5139b9b82289..73c5742614ee 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import partial -from typing import Callable +from typing import Callable, Final import mypy.errorcodes as codes from mypy import message_registry @@ -372,6 +372,10 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: ) return AnyType(TypeOfAny.from_error) + assigned_readonly_keys = ctx.type.readonly_keys & set(keys) + if assigned_readonly_keys: + ctx.api.msg.readonly_keys_mutated(assigned_readonly_keys, context=ctx.context) + default_type = ctx.arg_types[1][0] value_types = [] @@ -415,13 +419,16 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type: return AnyType(TypeOfAny.from_error) for key in keys: - if key in ctx.type.required_keys: + if key in ctx.type.required_keys or key in ctx.type.readonly_keys: ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) elif key not in ctx.type.items: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return ctx.default_return_type +_TP_DICT_MUTATING_METHODS: Final = frozenset({"update of TypedDict", "__ior__ of TypedDict"}) + + def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for methods that update `TypedDict`. @@ -436,10 +443,19 @@ def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: arg_type = arg_type.as_anonymous() arg_type = arg_type.copy_modified(required_keys=set()) if ctx.args and ctx.args[0]: - with ctx.api.msg.filter_errors(): + if signature.name in _TP_DICT_MUTATING_METHODS: + # If we want to mutate this object in place, we need to set this flag, + # it will trigger an extra check in TypedDict's checker. + arg_type.to_be_mutated = True + with ctx.api.msg.filter_errors( + filter_errors=lambda name, info: info.code != codes.TYPEDDICT_READONLY_MUTATED, + save_filtered_errors=True, + ): inferred = get_proper_type( ctx.api.get_expression_type(ctx.args[0][0], type_context=arg_type) ) + if arg_type.to_be_mutated: + arg_type.to_be_mutated = False # Done! possible_tds = [] if isinstance(inferred, TypedDictType): possible_tds = [inferred] diff --git a/mypy/plugins/proper_plugin.py b/mypy/plugins/proper_plugin.py index a1fd05272b65..f51685c80afa 100644 --- a/mypy/plugins/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -106,6 +106,7 @@ def is_special_target(right: ProperType) -> bool: "mypy.types.ErasedType", "mypy.types.DeletedType", "mypy.types.RequiredType", + "mypy.types.ReadOnlyType", ): # Special case: these are not valid targets for a type alias and thus safe. # TODO: introduce a SyntheticType base to simplify this? diff --git a/mypy/semanal.py b/mypy/semanal.py index 0b654d6b145f..27abf2c1dc4c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -7169,7 +7169,7 @@ def type_analyzer( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7188,7 +7188,7 @@ def type_analyzer( allow_tuple_literal=allow_tuple_literal, report_invalid_types=report_invalid_types, allow_placeholder=allow_placeholder, - allow_required=allow_required, + allow_typed_dict_special_forms=allow_typed_dict_special_forms, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, @@ -7211,7 +7211,7 @@ def anal_type( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7246,7 +7246,7 @@ def anal_type( allow_unbound_tvars=allow_unbound_tvars, allow_tuple_literal=allow_tuple_literal, allow_placeholder=allow_placeholder, - allow_required=allow_required, + allow_typed_dict_special_forms=allow_typed_dict_special_forms, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index db19f074911f..cb0bdebab724 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -181,7 +181,7 @@ def anal_type( tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_placeholder: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 832530df55e5..d081898bf010 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -43,6 +43,7 @@ from mypy.types import ( TPDICT_NAMES, AnyType, + ReadOnlyType, RequiredType, Type, TypedDictType, @@ -102,13 +103,15 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N and defn.base_type_exprs[0].fullname in TPDICT_NAMES ): # Building a new TypedDict - fields, types, statements, required_keys = self.analyze_typeddict_classdef_fields(defn) + fields, types, statements, required_keys, readonly_keys = ( + self.analyze_typeddict_classdef_fields(defn) + ) if fields is None: return True, None # Defer if self.api.is_func_scope() and "@" not in defn.name: defn.name += "@" + str(defn.line) info = self.build_typeddict_typeinfo( - defn.name, fields, types, required_keys, defn.line, existing_info + defn.name, fields, types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -154,10 +157,13 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N keys: list[str] = [] types = [] required_keys = set() + readonly_keys = set() # Iterate over bases in reverse order so that leftmost base class' keys take precedence for base in reversed(typeddict_bases): - self.add_keys_and_types_from_base(base, keys, types, required_keys, defn) - (new_keys, new_types, new_statements, new_required_keys) = ( + self.add_keys_and_types_from_base( + base, keys, types, required_keys, readonly_keys, defn + ) + (new_keys, new_types, new_statements, new_required_keys, new_readonly_keys) = ( self.analyze_typeddict_classdef_fields(defn, keys) ) if new_keys is None: @@ -165,8 +171,9 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N keys.extend(new_keys) types.extend(new_types) required_keys.update(new_required_keys) + readonly_keys.update(new_readonly_keys) info = self.build_typeddict_typeinfo( - defn.name, keys, types, required_keys, defn.line, existing_info + defn.name, keys, types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -180,6 +187,7 @@ def add_keys_and_types_from_base( keys: list[str], types: list[Type], required_keys: set[str], + readonly_keys: set[str], ctx: Context, ) -> None: base_args: list[Type] = [] @@ -221,6 +229,7 @@ def add_keys_and_types_from_base( keys.extend(valid_items.keys()) types.extend(valid_items.values()) required_keys.update(base_typed_dict.required_keys) + readonly_keys.update(base_typed_dict.readonly_keys) def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: """Analyze arguments of base type expressions as types. @@ -241,7 +250,9 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: self.fail("Invalid TypedDict type argument", ctx) return None analyzed = self.api.anal_type( - type, allow_required=True, allow_placeholder=not self.api.is_func_scope() + type, + allow_typed_dict_special_forms=True, + allow_placeholder=not self.api.is_func_scope(), ) if analyzed is None: return None @@ -270,7 +281,7 @@ def map_items_to_base( def analyze_typeddict_classdef_fields( self, defn: ClassDef, oldfields: list[str] | None = None - ) -> tuple[list[str] | None, list[Type], list[Statement], set[str]]: + ) -> tuple[list[str] | None, list[Type], list[Statement], set[str], set[str]]: """Analyze fields defined in a TypedDict class definition. This doesn't consider inherited fields (if any). Also consider totality, @@ -316,17 +327,15 @@ def analyze_typeddict_classdef_fields( else: analyzed = self.api.anal_type( stmt.unanalyzed_type, - allow_required=True, + allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) if analyzed is None: - return None, [], [], set() # Need to defer + return None, [], [], set(), set() # Need to defer types.append(analyzed) if not has_placeholder(analyzed): - stmt.type = ( - analyzed.item if isinstance(analyzed, RequiredType) else analyzed - ) + stmt.type = self.extract_meta_info(analyzed, stmt)[0] # ...despite possible minor failures that allow further analysis. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax: self.fail(TPDICT_CLASS_ERROR, stmt) @@ -342,17 +351,49 @@ def analyze_typeddict_classdef_fields( if key == "total": continue self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) - required_keys = { - field - for (field, t) in zip(fields, types) - if (total or (isinstance(t, RequiredType) and t.required)) - and not (isinstance(t, RequiredType) and not t.required) - } - types = [ # unwrap Required[T] to just T - t.item if isinstance(t, RequiredType) else t for t in types - ] - - return fields, types, statements, required_keys + + res_types = [] + readonly_keys = set() + required_keys = set() + for field, t in zip(fields, types): + typ, required, readonly = self.extract_meta_info(t) + res_types.append(typ) + if (total or required is True) and required is not False: + required_keys.add(field) + if readonly: + readonly_keys.add(field) + + return fields, res_types, statements, required_keys, readonly_keys + + def extract_meta_info( + self, typ: Type, context: Context | None = None + ) -> tuple[Type, bool | None, bool]: + """Unwrap all metadata types.""" + is_required = None # default, no modification + readonly = False # by default all is mutable + + seen_required = False + seen_readonly = False + while isinstance(typ, (RequiredType, ReadOnlyType)): + if isinstance(typ, RequiredType): + if context is not None and seen_required: + self.fail( + '"{}" type cannot be nested'.format( + "Required[]" if typ.required else "NotRequired[]" + ), + context, + code=codes.VALID_TYPE, + ) + is_required = typ.required + seen_required = True + typ = typ.item + if isinstance(typ, ReadOnlyType): + if context is not None and seen_readonly: + self.fail('"ReadOnly[]" type cannot be nested', context, code=codes.VALID_TYPE) + readonly = True + seen_readonly = True + typ = typ.item + return typ, is_required, readonly def check_typeddict( self, node: Expression, var_name: str | None, is_func_scope: bool @@ -391,7 +432,7 @@ def check_typeddict( name += "@" + str(call.line) else: name = var_name = "TypedDict@" + str(call.line) - info = self.build_typeddict_typeinfo(name, [], [], set(), call.line, None) + info = self.build_typeddict_typeinfo(name, [], [], set(), set(), call.line, None) else: if var_name is not None and name != var_name: self.fail( @@ -410,8 +451,11 @@ def check_typeddict( if (total or (isinstance(t, RequiredType) and t.required)) and not (isinstance(t, RequiredType) and not t.required) } - types = [ # unwrap Required[T] to just T - t.item if isinstance(t, RequiredType) else t for t in types + readonly_keys = { + field for (field, t) in zip(items, types) if isinstance(t, ReadOnlyType) + } + types = [ # unwrap Required[T] or ReadOnly[T] to just T + t.item if isinstance(t, (RequiredType, ReadOnlyType)) else t for t in types ] # Perform various validations after unwrapping. @@ -428,7 +472,7 @@ def check_typeddict( if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info info = self.build_typeddict_typeinfo( - name, items, types, required_keys, call.line, existing_info + name, items, types, required_keys, readonly_keys, call.line, existing_info ) info.line = node.line # Store generated TypeInfo under both names, see semanal_namedtuple for more details. @@ -514,7 +558,7 @@ def parse_typeddict_fields_with_types( return [], [], False analyzed = self.api.anal_type( type, - allow_required=True, + allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) @@ -535,6 +579,7 @@ def build_typeddict_typeinfo( items: list[str], types: list[Type], required_keys: set[str], + readonly_keys: set[str], line: int, existing_info: TypeInfo | None, ) -> TypeInfo: @@ -546,7 +591,9 @@ def build_typeddict_typeinfo( ) assert fallback is not None info = existing_info or self.api.basic_new_typeinfo(name, fallback, line) - typeddict_type = TypedDictType(dict(zip(items, types)), required_keys, fallback) + typeddict_type = TypedDictType( + dict(zip(items, types)), required_keys, readonly_keys, fallback + ) if info.special_alias and has_placeholder(info.special_alias.target): self.api.process_placeholder( None, "TypedDict item", info, force_progress=typeddict_type != info.typeddict_type diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index f8a874005adb..fc868d288b4d 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -475,7 +475,8 @@ def visit_tuple_type(self, typ: TupleType) -> SnapshotItem: def visit_typeddict_type(self, typ: TypedDictType) -> SnapshotItem: items = tuple((key, snapshot_type(item_type)) for key, item_type in typ.items.items()) required = tuple(sorted(typ.required_keys)) - return ("TypedDictType", items, required) + readonly = tuple(sorted(typ.readonly_keys)) + return ("TypedDictType", items, required, readonly) def visit_literal_type(self, typ: LiteralType) -> SnapshotItem: return ("LiteralType", snapshot_type(typ.fallback), typ.value) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 608d098791a9..3b775a06bd6e 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -914,6 +914,17 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: # lands so here we are anticipating that change. if (name in left.required_keys) != (name in right.required_keys): return False + # Readonly fields check: + # + # A = TypedDict('A', {'x': ReadOnly[int]}) + # B = TypedDict('A', {'x': int}) + # def reset_x(b: B) -> None: + # b['x'] = 0 + # + # So, `A` cannot be a subtype of `B`, while `B` can be a subtype of `A`, + # because you can use `B` everywhere you use `A`, but not the other way around. + if name in left.readonly_keys and name not in right.readonly_keys: + return False # (NOTE: Fallbacks don't matter.) return True else: diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 38e4c5ba0d01..8aac7e5c2bbd 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -276,6 +276,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: result = TypedDictType( items, t.required_keys, + t.readonly_keys, # TODO: This appears to be unsafe. cast(Any, t.fallback.accept(self)), t.line, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 6c94390c23dc..0a6b7689136e 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -83,6 +83,7 @@ PlaceholderType, ProperType, RawExpressionType, + ReadOnlyType, RequiredType, SyntheticTypeVisitor, TrivialSyntheticTypeTranslator, @@ -219,7 +220,7 @@ def __init__( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -253,7 +254,7 @@ def __init__( # If false, record incomplete ref if we generate PlaceholderType. self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? - self.allow_required = allow_required + self.allow_typed_dict_special_forms = allow_typed_dict_special_forms # Are we in a context where ParamSpec literals are allowed? self.allow_param_spec_literals = allow_param_spec_literals # Are we in context where literal "..." specifically is allowed? @@ -684,7 +685,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ return AnyType(TypeOfAny.from_error) return self.anal_type(t.args[0]) elif fullname in ("typing_extensions.Required", "typing.Required"): - if not self.allow_required: + if not self.allow_typed_dict_special_forms: self.fail( "Required[] can be only used in a TypedDict definition", t, @@ -696,9 +697,11 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "Required[] must have exactly one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return RequiredType(self.anal_type(t.args[0]), required=True) + return RequiredType( + self.anal_type(t.args[0], allow_typed_dict_special_forms=True), required=True + ) elif fullname in ("typing_extensions.NotRequired", "typing.NotRequired"): - if not self.allow_required: + if not self.allow_typed_dict_special_forms: self.fail( "NotRequired[] can be only used in a TypedDict definition", t, @@ -710,7 +713,23 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "NotRequired[] must have exactly one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return RequiredType(self.anal_type(t.args[0]), required=False) + return RequiredType( + self.anal_type(t.args[0], allow_typed_dict_special_forms=True), required=False + ) + elif fullname in ("typing_extensions.ReadOnly", "typing.ReadOnly"): + if not self.allow_typed_dict_special_forms: + self.fail( + "ReadOnly[] can be only used in a TypedDict definition", + t, + code=codes.VALID_TYPE, + ) + return AnyType(TypeOfAny.from_error) + if len(t.args) != 1: + self.fail( + '"ReadOnly[]" must have exactly one type argument', t, code=codes.VALID_TYPE + ) + return AnyType(TypeOfAny.from_error) + return ReadOnlyType(self.anal_type(t.args[0], allow_typed_dict_special_forms=True)) elif ( self.anal_type_guard_arg(t, fullname) is not None or self.anal_type_is_arg(t, fullname) is not None @@ -1223,9 +1242,11 @@ def visit_tuple_type(self, t: TupleType) -> Type: def visit_typeddict_type(self, t: TypedDictType) -> Type: req_keys = set() + readonly_keys = set() items = {} for item_name, item_type in t.items.items(): - analyzed = self.anal_type(item_type, allow_required=True) + # TODO: rework + analyzed = self.anal_type(item_type, allow_typed_dict_special_forms=True) if isinstance(analyzed, RequiredType): if analyzed.required: req_keys.add(item_name) @@ -1233,6 +1254,9 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: else: # Keys are required by default. req_keys.add(item_name) + if isinstance(analyzed, ReadOnlyType): + readonly_keys.add(item_name) + analyzed = analyzed.item items[item_name] = analyzed if t.fallback.type is MISSING_FALLBACK: # anonymous/inline TypedDict if INLINE_TYPEDDICT not in self.options.enable_incomplete_feature: @@ -1257,10 +1281,12 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: items[sub_item_name] = sub_item_type if sub_item_name in p_analyzed.required_keys: req_keys.add(sub_item_name) + if sub_item_name in p_analyzed.readonly_keys: + readonly_keys.add(sub_item_name) else: required_keys = t.required_keys fallback = t.fallback - return TypedDictType(items, required_keys, fallback, t.line, t.column) + return TypedDictType(items, required_keys, readonly_keys, fallback, t.line, t.column) def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # We should never see a bare Literal. We synthesize these raw literals @@ -1811,12 +1837,12 @@ def anal_type( allow_param_spec: bool = False, allow_unpack: bool = False, allow_ellipsis: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, ) -> Type: if nested: self.nesting_level += 1 - old_allow_required = self.allow_required - self.allow_required = allow_required + old_allow_typed_dict_special_forms = self.allow_typed_dict_special_forms + self.allow_typed_dict_special_forms = allow_typed_dict_special_forms old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis old_allow_unpack = self.allow_unpack @@ -1826,7 +1852,7 @@ def anal_type( finally: if nested: self.nesting_level -= 1 - self.allow_required = old_allow_required + self.allow_typed_dict_special_forms = old_allow_typed_dict_special_forms self.allow_ellipsis = old_allow_ellipsis self.allow_unpack = old_allow_unpack if ( diff --git a/mypy/types.py b/mypy/types.py index b1e57b2f6a86..dff7e2c0c829 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -476,6 +476,20 @@ def accept(self, visitor: TypeVisitor[T]) -> T: return self.item.accept(visitor) +class ReadOnlyType(Type): + """ReadOnly[T] Only usable at top-level of a TypedDict definition.""" + + def __init__(self, item: Type) -> None: + super().__init__(line=item.line, column=item.column) + self.item = item + + def __repr__(self) -> str: + return f"ReadOnly[{self.item}]" + + def accept(self, visitor: TypeVisitor[T]) -> T: + return self.item.accept(visitor) + + class ProperType(Type): """Not a type alias. @@ -2554,17 +2568,28 @@ class TypedDictType(ProperType): TODO: The fallback structure is perhaps overly complicated. """ - __slots__ = ("items", "required_keys", "fallback", "extra_items_from") + __slots__ = ( + "items", + "required_keys", + "readonly_keys", + "fallback", + "extra_items_from", + "to_be_mutated", + ) items: dict[str, Type] # item_name -> item_type required_keys: set[str] + readonly_keys: set[str] fallback: Instance + extra_items_from: list[ProperType] # only used during semantic analysis + to_be_mutated: bool # only used in a plugin for `.update`, `|=`, etc def __init__( self, items: dict[str, Type], required_keys: set[str], + readonly_keys: set[str], fallback: Instance, line: int = -1, column: int = -1, @@ -2572,16 +2597,25 @@ def __init__( super().__init__(line, column) self.items = items self.required_keys = required_keys + self.readonly_keys = readonly_keys self.fallback = fallback self.can_be_true = len(self.items) > 0 self.can_be_false = len(self.required_keys) == 0 self.extra_items_from = [] + self.to_be_mutated = False def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_typeddict_type(self) def __hash__(self) -> int: - return hash((frozenset(self.items.items()), self.fallback, frozenset(self.required_keys))) + return hash( + ( + frozenset(self.items.items()), + self.fallback, + frozenset(self.required_keys), + frozenset(self.readonly_keys), + ) + ) def __eq__(self, other: object) -> bool: if not isinstance(other, TypedDictType): @@ -2596,6 +2630,7 @@ def __eq__(self, other: object) -> bool: ) and self.fallback == other.fallback and self.required_keys == other.required_keys + and self.readonly_keys == other.readonly_keys ) def serialize(self) -> JsonDict: @@ -2603,6 +2638,7 @@ def serialize(self) -> JsonDict: ".class": "TypedDictType", "items": [[n, t.serialize()] for (n, t) in self.items.items()], "required_keys": sorted(self.required_keys), + "readonly_keys": sorted(self.readonly_keys), "fallback": self.fallback.serialize(), } @@ -2612,6 +2648,7 @@ def deserialize(cls, data: JsonDict) -> TypedDictType: return TypedDictType( {n: deserialize_type(t) for (n, t) in data["items"]}, set(data["required_keys"]), + set(data["readonly_keys"]), Instance.deserialize(data["fallback"]), ) @@ -2635,6 +2672,7 @@ def copy_modified( item_types: list[Type] | None = None, item_names: list[str] | None = None, required_keys: set[str] | None = None, + readonly_keys: set[str] | None = None, ) -> TypedDictType: if fallback is None: fallback = self.fallback @@ -2644,10 +2682,12 @@ def copy_modified( items = dict(zip(self.items, item_types)) if required_keys is None: required_keys = self.required_keys + if readonly_keys is None: + readonly_keys = self.readonly_keys if item_names is not None: items = {k: v for (k, v) in items.items() if k in item_names} required_keys &= set(item_names) - return TypedDictType(items, required_keys, fallback, self.line, self.column) + return TypedDictType(items, required_keys, readonly_keys, fallback, self.line, self.column) def create_anonymous_fallback(self) -> Instance: anonymous = self.as_anonymous() @@ -3421,10 +3461,12 @@ def visit_tuple_type(self, t: TupleType) -> str: def visit_typeddict_type(self, t: TypedDictType) -> str: def item_str(name: str, typ: str) -> str: - if name in t.required_keys: - return f"{name!r}: {typ}" - else: - return f"{name!r}?: {typ}" + modifier = "" + if name not in t.required_keys: + modifier += "?" + if name in t.readonly_keys: + modifier += "=" + return f"{name!r}{modifier}: {typ}" s = ( "{" diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index dc1929751977..e1797421636e 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2384,10 +2384,12 @@ class ForceDeferredEval: pass [case testTypedDictRequiredUnimportedAny] # flags: --disallow-any-unimported -from typing import NotRequired, TypedDict +from typing import NotRequired, TypedDict, ReadOnly from nonexistent import Foo # type: ignore[import-not-found] class Bar(TypedDict): foo: NotRequired[Foo] # E: Type of variable becomes "Any" due to an unfollowed import + bar: ReadOnly[Foo] # E: Type of variable becomes "Any" due to an unfollowed import + baz: NotRequired[ReadOnly[Foo]] # E: Type of variable becomes "Any" due to an unfollowed import [typing fixtures/typing-typeddict.pyi] -- Required[] @@ -3631,6 +3633,16 @@ y = {"one": 1} # E: Expected TypedDict keys ("one", "other") but found only key [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictInlineReadOnly] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import ReadOnly + +x: {"one": int, "other": ReadOnly[int]} +x["one"] = 1 # ok +x["other"] = 1 # E: ReadOnly TypedDict key "other" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testTypedDictInlineNestedSchema] # flags: --enable-incomplete-feature=InlineTypedDict def nested() -> {"one": str, "other": {"a": int, "b": int}}: @@ -3652,3 +3664,327 @@ x: {"a": int, **X[str], "b": int} reveal_type(x) # N: Revealed type is "TypedDict({'a': builtins.int, 'b': builtins.int, 'item': builtins.str})" [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] + + +# ReadOnly +# See: https://peps.python.org/pep-0705 + +[case testTypedDictReadOnly] +# flags: --show-error-codes +from typing import ReadOnly, TypedDict + +class TP(TypedDict): + one: int + other: ReadOnly[str] + +x: TP +reveal_type(x["one"]) # N: Revealed type is "builtins.int" +reveal_type(x["other"]) # N: Revealed type is "builtins.str" +x["one"] = 1 # ok +x["other"] = "a" # E: ReadOnly TypedDict key "other" TypedDict is mutated [typeddict-readonly-mutated] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCreation] +from typing import ReadOnly, TypedDict + +class TD(TypedDict): + x: ReadOnly[int] + y: int + +# Ok: +x = TD({"x": 1, "y": 2}) +y = TD(x=1, y=2) +z: TD = {"x": 1, "y": 2} + +# Error: +x2 = TD({"x": "a", "y": 2}) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +y2 = TD(x="a", y=2) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +z2: TD = {"x": "a", "y": 2} # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyDel] +from typing import ReadOnly, TypedDict, NotRequired + +class TP(TypedDict): + required_key: ReadOnly[str] + optional_key: ReadOnly[NotRequired[str]] + +x: TP +del x["required_key"] # E: Key "required_key" of TypedDict "TP" cannot be deleted +del x["optional_key"] # E: Key "optional_key" of TypedDict "TP" cannot be deleted +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyMutateMethods] +from typing import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +reveal_type(x.pop("key")) # E: Key "key" of TypedDict "TP" cannot be deleted \ + # N: Revealed type is "builtins.str" + +x.update({"key": "abc", "other": 1, "mutable": True}) # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated +x.setdefault("key", "abc") # E: ReadOnly TypedDict key "key" TypedDict is mutated +x.setdefault("other", 1) # E: ReadOnly TypedDict key "other" TypedDict is mutated +x.setdefault("mutable", False) # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFromTypingExtensionsReadOnlyMutateMethods] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + +x: TP +x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFromMypyExtensionsReadOnlyMutateMethods] +from mypy_extensions import TypedDict +from typing_extensions import ReadOnly + +class TP(TypedDict): + key: ReadOnly[str] + +x: TP +x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyMutate__ior__Statements] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +x |= {"mutable": True} # ok +x |= {"key": "a"} # E: ReadOnly TypedDict key "key" TypedDict is mutated +x |= {"key": "a", "other": 1, "mutable": True} # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictReadOnlyMutate__or__Statements] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +# These are new objects, not mutation: +x = x | {"mutable": True} +x = x | {"key": "a"} +x = x | {"key": "a", "other": 1, "mutable": True} +y1 = x | {"mutable": True} +y2 = x | {"key": "a"} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictReadOnlyMutateWithOtherDicts] +from typing import ReadOnly, TypedDict, Dict + +class TP(TypedDict): + key: ReadOnly[str] + mutable: bool + +class Mutable(TypedDict): + mutable: bool + +class Regular(TypedDict): + key: str + +m: Mutable +r: Regular +d: Dict[str, object] + +# Creating new objects is ok: +tp: TP = {**r, **m} +tp1: TP = {**tp, **m} +tp2: TP = {**r, **m} +tp3: TP = {**tp, **r} +tp4: TP = {**tp, **d} # E: Unsupported type "Dict[str, object]" for ** expansion in TypedDict +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictGenericReadOnly] +from typing import ReadOnly, TypedDict, TypeVar, Generic + +T = TypeVar('T') + +class TP(TypedDict, Generic[T]): + key: ReadOnly[T] + +x: TP[int] +reveal_type(x["key"]) # N: Revealed type is "builtins.int" +x["key"] = 1 # E: ReadOnly TypedDict key "key" TypedDict is mutated +x["key"] = "a" # E: ReadOnly TypedDict key "key" TypedDict is mutated \ + # E: Value of "key" has incompatible type "str"; expected "int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyOtherTypedDict] +from typing import ReadOnly, TypedDict + +class First(TypedDict): + field: int + +class TP(TypedDict): + key: ReadOnly[First] + +x: TP +reveal_type(x["key"]["field"]) # N: Revealed type is "builtins.int" +x["key"]["field"] = 1 # ok +x["key"] = {"field": 2} # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyInheritance] +from typing import ReadOnly, TypedDict + +class Base(TypedDict): + a: ReadOnly[str] + +class Child(Base): + b: ReadOnly[int] + +base: Base +reveal_type(base["a"]) # N: Revealed type is "builtins.str" +base["a"] = "x" # E: ReadOnly TypedDict key "a" TypedDict is mutated +base["b"] # E: TypedDict "Base" has no key "b" + +child: Child +reveal_type(child["a"]) # N: Revealed type is "builtins.str" +reveal_type(child["b"]) # N: Revealed type is "builtins.int" +child["a"] = "x" # E: ReadOnly TypedDict key "a" TypedDict is mutated +child["b"] = 1 # E: ReadOnly TypedDict key "b" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlySubtyping] +from typing import ReadOnly, TypedDict + +class A(TypedDict): + key: ReadOnly[str] + +class B(TypedDict): + key: str + +a: A +b: B + +def accepts_A(d: A): ... +def accepts_B(d: B): ... + +accepts_A(a) +accepts_A(b) +accepts_B(a) # E: Argument 1 to "accepts_B" has incompatible type "A"; expected "B" +accepts_B(b) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCall] +from typing import ReadOnly, TypedDict + +TP = TypedDict("TP", {"one": int, "other": ReadOnly[str]}) + +x: TP +reveal_type(x["one"]) # N: Revealed type is "builtins.int" +reveal_type(x["other"]) # N: Revealed type is "builtins.str" +x["one"] = 1 # ok +x["other"] = "a" # E: ReadOnly TypedDict key "other" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyABCSubtypes] +from typing import ReadOnly, TypedDict, Mapping, Dict, MutableMapping + +class TP(TypedDict): + one: int + other: ReadOnly[int] + +def accepts_mapping(m: Mapping[str, object]): ... +def accepts_mutable_mapping(mm: MutableMapping[str, object]): ... +def accepts_dict(d: Dict[str, object]): ... + +x: TP +accepts_mapping(x) +accepts_mutable_mapping(x) # E: Argument 1 to "accepts_mutable_mapping" has incompatible type "TP"; expected "MutableMapping[str, object]" +accepts_dict(x) # E: Argument 1 to "accepts_dict" has incompatible type "TP"; expected "Dict[str, object]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyAndNotRequired] +from typing import ReadOnly, TypedDict, NotRequired + +class TP(TypedDict): + one: ReadOnly[NotRequired[int]] + two: NotRequired[ReadOnly[str]] + +x: TP +reveal_type(x) # N: Revealed type is "TypedDict('__main__.TP', {'one'?=: builtins.int, 'two'?=: builtins.str})" +reveal_type(x.get("one")) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(x.get("two")) # N: Revealed type is "Union[builtins.str, None]" +x["one"] = 1 # E: ReadOnly TypedDict key "one" TypedDict is mutated +x["two"] = "a" # E: ReadOnly TypedDict key "two" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testMeetOfTypedDictsWithReadOnly] +from typing import TypeVar, Callable, TypedDict, ReadOnly +XY = TypedDict('XY', {'x': ReadOnly[int], 'y': int}) +YZ = TypedDict('YZ', {'y': int, 'z': ReadOnly[int]}) +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass +def g(x: XY, y: YZ) -> None: pass +reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'=: builtins.int, 'y': builtins.int, 'z'=: builtins.int})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyUnpack] +from typing_extensions import TypedDict, Unpack, ReadOnly + +class TD(TypedDict): + x: ReadOnly[int] + y: str + +def func(**kwargs: Unpack[TD]): + kwargs["x"] = 1 # E: ReadOnly TypedDict key "x" TypedDict is mutated + kwargs["y" ] = "a" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testIncorrectTypedDictSpecialFormsUsage] +from typing import ReadOnly, TypedDict, NotRequired, Required + +x: ReadOnly[int] # E: ReadOnly[] can be only used in a TypedDict definition +y: Required[int] # E: Required[] can be only used in a TypedDict definition +z: NotRequired[int] # E: NotRequired[] can be only used in a TypedDict definition + +class TP(TypedDict): + a: ReadOnly[ReadOnly[int]] # E: "ReadOnly[]" type cannot be nested + b: ReadOnly[NotRequired[ReadOnly[str]]] # E: "ReadOnly[]" type cannot be nested + c: NotRequired[Required[int]] # E: "Required[]" type cannot be nested + d: Required[NotRequired[int]] # E: "NotRequired[]" type cannot be nested + e: Required[ReadOnly[NotRequired[int]]] # E: "NotRequired[]" type cannot be nested + f: ReadOnly[ReadOnly[ReadOnly[int]]] # E: "ReadOnly[]" type cannot be nested + g: Required[Required[int]] # E: "Required[]" type cannot be nested + h: NotRequired[NotRequired[int]] # E: "NotRequired[]" type cannot be nested + + j: NotRequired[ReadOnly[Required[ReadOnly[int]]]] # E: "Required[]" type cannot be nested \ + # E: "ReadOnly[]" type cannot be nested + + k: ReadOnly # E: "ReadOnly[]" must have exactly one type argument +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 2ad31311a402..faedd890922d 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -3705,6 +3705,34 @@ def foo() -> None: == b.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") +[case testTypedDictUpdateReadOnly] +import b +[file a.py] +from typing_extensions import TypedDict, ReadOnly +Point = TypedDict('Point', {'x': int, 'y': int}) +p = Point(x=1, y=2) +[file a.py.2] +from typing_extensions import TypedDict, ReadOnly +class Point(TypedDict): + x: int + y: ReadOnly[int] +p = Point(x=1, y=2) +[file a.py.3] +from typing_extensions import TypedDict, ReadOnly +Point = TypedDict('Point', {'x': ReadOnly[int], 'y': int}) +p = Point(x=1, y=2) +[file b.py] +from a import Point +def foo(x: Point) -> None: + x['x'] = 1 + x['y'] = 2 +[builtins fixtures/dict.pyi] +[out] +== +b.py:4: error: ReadOnly TypedDict key "y" TypedDict is mutated +== +b.py:3: error: ReadOnly TypedDict key "x" TypedDict is mutated + [case testBasicAliasUpdate] import b [file a.py] diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index d136ac4ab8be..7e9c642cf261 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -26,6 +26,7 @@ TypedDict = 0 NoReturn = 0 Required = 0 NotRequired = 0 +ReadOnly = 0 Self = 0 T = TypeVar('T') @@ -59,6 +60,10 @@ class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): def __len__(self) -> int: ... def __contains__(self, arg: object) -> int: pass +class MutableMapping(Mapping[T, T_co], Generic[T, T_co], metaclass=ABCMeta): + # Other methods are not used in tests. + def clear(self) -> None: ... + # Fallback type for all typed dicts (does not exist at runtime). class _TypedDict(Mapping[str, object]): # Needed to make this class non-abstract. It is explicitly declared abstract in diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index b5bfc1ab3f20..d9d7067efe0f 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -41,6 +41,7 @@ TypeVarTuple: _SpecialForm Unpack: _SpecialForm Required: _SpecialForm NotRequired: _SpecialForm +ReadOnly: _SpecialForm @final class TypeAliasType: From 7dc23fe83700babfe297e368b4019266e403c7bf Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 30 Sep 2024 19:35:25 -0400 Subject: [PATCH 0771/1617] Enable negative narrowing of Union TypeVar upper bounds (#17850) Fixes #15235 ### Before ```python from typing import TypeVar class A: pass class B: b: int T = TypeVar("T", bound=A | B) def foo(x: T) -> T: if isinstance(x, A): reveal_type(x) # N: Revealed type is "__main__.A" else: reveal_type(x) # N: Revealed type is "T`-1" x.b # E: Item "A" of the upper bound "A | B" of type variable "T" has no attribute "b" return x ``` ### After ```python from typing import TypeVar class A: pass class B: b: int T = TypeVar("T", bound=A | B) def foo(x: T) -> T: if isinstance(x, A): reveal_type(x) # N: Revealed type is "__main__.A" else: reveal_type(x) # N: Revealed type is "T`-1" x.b # ok! Upper bound of T narrowed to B return x ``` --- mypy/subtypes.py | 2 ++ test-data/unit/check-isinstance.test | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 3b775a06bd6e..787d5cb89b0a 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1941,6 +1941,8 @@ def restrict_subtype_away(t: Type, s: Type) -> Type: if (isinstance(get_proper_type(item), AnyType) or not covers_at_runtime(item, s)) ] return UnionType.make_union(new_items) + elif isinstance(p_t, TypeVarType): + return p_t.copy_modified(upper_bound=restrict_subtype_away(p_t.upper_bound, s)) elif covers_at_runtime(t, s): return UninhabitedType() else: diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index b7ee38b69d00..8fa1bc1ca1ac 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1833,6 +1833,30 @@ def f(x: T) -> None: reveal_type(x) # N: Revealed type is "T`-1" [builtins fixtures/isinstance.pyi] +[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound] +from typing import Union, TypeVar + +class A: + a: int +class B: + b: int + +T = TypeVar("T", bound=Union[A, B]) + +def f(x: T) -> T: + if isinstance(x, A): + reveal_type(x) # N: Revealed type is "__main__.A" + x.a + x.b # E: "A" has no attribute "b" + else: + reveal_type(x) # N: Revealed type is "T`-1" + x.a # E: "T" has no attribute "a" + x.b + x.a # E: Item "B" of the upper bound "Union[A, B]" of type variable "T" has no attribute "a" + x.b # E: Item "A" of the upper bound "Union[A, B]" of type variable "T" has no attribute "b" + return x +[builtins fixtures/isinstance.pyi] + [case testIsinstanceAndTypeType] from typing import Type def f(x: Type[int]) -> None: From 1855bb9ad71cad1c6b0ebefd2808c82273a35a3a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:11:22 -0700 Subject: [PATCH 0772/1617] Sync typeshed (#17855) Source commit: https://github.com/python/typeshed/commit/91a58b07cdd807b1d965e04ba85af2adab8bf924 --- mypy/typeshed/stdlib/cProfile.pyi | 6 +++--- mypy/typeshed/stdlib/calendar.pyi | 6 +++--- mypy/typeshed/stdlib/profile.pyi | 6 +++--- mypy/typeshed/stdlib/typing.pyi | 14 +++++++------- mypy/typeshed/stdlib/typing_extensions.pyi | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index 0cf6e34ec99e..e921584d4390 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -1,6 +1,6 @@ import _lsprof from _typeshed import StrOrBytesPath, Unused -from collections.abc import Callable +from collections.abc import Callable, Mapping from types import CodeType from typing import Any, TypeVar from typing_extensions import ParamSpec, Self, TypeAlias @@ -9,7 +9,7 @@ __all__ = ["run", "runctx", "Profile"] def run(statement: str, filename: str | None = None, sort: str | int = -1) -> None: ... def runctx( - statement: str, globals: dict[str, Any], locals: dict[str, Any], filename: str | None = None, sort: str | int = -1 + statement: str, globals: dict[str, Any], locals: Mapping[str, Any], filename: str | None = None, sort: str | int = -1 ) -> None: ... _T = TypeVar("_T") @@ -23,7 +23,7 @@ class Profile(_lsprof.Profiler): def create_stats(self) -> None: ... def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... - def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... + def runctx(self, cmd: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> Self: ... def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def __enter__(self) -> Self: ... def __exit__(self, *exc_info: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 39312d0b2523..cabf3b881c30 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -79,9 +79,9 @@ class Calendar: def monthdatescalendar(self, year: int, month: int) -> list[list[datetime.date]]: ... def monthdays2calendar(self, year: int, month: int) -> list[list[tuple[int, int]]]: ... def monthdayscalendar(self, year: int, month: int) -> list[list[int]]: ... - def yeardatescalendar(self, year: int, width: int = 3) -> list[list[int]]: ... - def yeardays2calendar(self, year: int, width: int = 3) -> list[list[tuple[int, int]]]: ... - def yeardayscalendar(self, year: int, width: int = 3) -> list[list[int]]: ... + def yeardatescalendar(self, year: int, width: int = 3) -> list[list[list[list[datetime.date]]]]: ... + def yeardays2calendar(self, year: int, width: int = 3) -> list[list[list[list[tuple[int, int]]]]]: ... + def yeardayscalendar(self, year: int, width: int = 3) -> list[list[list[list[int]]]]: ... def itermonthdays3(self, year: int, month: int) -> Iterable[tuple[int, int, int]]: ... def itermonthdays4(self, year: int, month: int) -> Iterable[tuple[int, int, int, int]]: ... diff --git a/mypy/typeshed/stdlib/profile.pyi b/mypy/typeshed/stdlib/profile.pyi index 73eba36344fe..696193d9dc16 100644 --- a/mypy/typeshed/stdlib/profile.pyi +++ b/mypy/typeshed/stdlib/profile.pyi @@ -1,5 +1,5 @@ from _typeshed import StrOrBytesPath -from collections.abc import Callable +from collections.abc import Callable, Mapping from typing import Any, TypeVar from typing_extensions import ParamSpec, Self, TypeAlias @@ -7,7 +7,7 @@ __all__ = ["run", "runctx", "Profile"] def run(statement: str, filename: str | None = None, sort: str | int = -1) -> None: ... def runctx( - statement: str, globals: dict[str, Any], locals: dict[str, Any], filename: str | None = None, sort: str | int = -1 + statement: str, globals: dict[str, Any], locals: Mapping[str, Any], filename: str | None = None, sort: str | int = -1 ) -> None: ... _T = TypeVar("_T") @@ -26,6 +26,6 @@ class Profile: def create_stats(self) -> None: ... def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... - def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... + def runctx(self, cmd: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> Self: ... def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def calibrate(self, m: int, verbose: int = 0) -> float: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 784f30ad7397..ce16d9adff99 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -861,13 +861,13 @@ if sys.version_info >= (3, 9): def get_type_hints( obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, - localns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, include_extras: bool = False, ) -> dict[str, Any]: ... else: def get_type_hints( - obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None + obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None ) -> dict[str, Any]: ... def get_args(tp: Any) -> tuple[Any, ...]: ... @@ -995,13 +995,13 @@ class ForwardRef: "that references a PEP 695 type parameter. It will be disallowed in Python 3.15." ) def _evaluate( - self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, *, recursive_guard: frozenset[str] + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, *, recursive_guard: frozenset[str] ) -> Any | None: ... @overload def _evaluate( self, globalns: dict[str, Any] | None, - localns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...], *, recursive_guard: frozenset[str], @@ -1010,17 +1010,17 @@ class ForwardRef: def _evaluate( self, globalns: dict[str, Any] | None, - localns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, *, recursive_guard: frozenset[str], ) -> Any | None: ... elif sys.version_info >= (3, 9): def _evaluate( - self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, recursive_guard: frozenset[str] + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, recursive_guard: frozenset[str] ) -> Any | None: ... else: - def _evaluate(self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None) -> Any | None: ... + def _evaluate(self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None) -> Any | None: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 1e4f90a0a722..3240eff0f5e9 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -261,7 +261,7 @@ OrderedDict = _Alias() def get_type_hints( obj: Callable[..., Any], globalns: dict[str, Any] | None = None, - localns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, include_extras: bool = False, ) -> dict[str, Any]: ... def get_args(tp: Any) -> tuple[Any, ...]: ... From 87b005ab2fc290002ec0ad7eda6058ab15e659b4 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 1 Oct 2024 23:33:24 -0400 Subject: [PATCH 0773/1617] .git-blame-ignore-revs: add #15059 and #16847 (#17862) Noticed these while walking through some git blames. Adds some recent black upgrades to `.git-blame-ignore-revs`, #15059 and #16847. --- .git-blame-ignore-revs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index be8582a6b221..8d89ec6d6043 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,7 @@ f98f78216ba9d6ab68c8e69c19e9f3c7926c5efe # run pyupgrade (#12711) fc335cb16315964b923eb1927e3aad1516891c28 +# update black to 23.3.0 (#15059) +4276308be01ea498d946a79554b4a10b1cf13ccb +# Update black to 24.1.1 (#16847) +8107e53158d83d30bb04d290ac10d8d3ccd344f8 From 329e38e6f23c92d6be7c7839d3b44bf7e2b517dc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 2 Oct 2024 15:44:28 +0100 Subject: [PATCH 0774/1617] Revert "Narrow based on collection containment (#17344)" (#17865) This reverts commit ed0cd4acba02ed19b1cf18ac0ac416dc251d7714. See #17864 for context. The implementation has some issues, and I'm reverting the PR to unblock the 1.12 release. Closes #17864. Closes #17841. --- mypy/checker.py | 15 ++-- test-data/unit/check-narrowing.test | 112 +------------------------- test-data/unit/fixtures/narrowing.pyi | 9 +-- 3 files changed, 8 insertions(+), 128 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9c4f4ce88690..a089576e6ffe 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6023,16 +6023,11 @@ def has_no_custom_eq_checks(t: Type) -> bool: if_map, else_map = {}, {} if left_index in narrowable_operand_index_to_hash: - collection_item_type = get_proper_type(builtin_item_type(iterable_type)) - # Narrow if the collection is a subtype - if ( - collection_item_type is not None - and collection_item_type != item_type - and is_subtype(collection_item_type, item_type) - ): - if_map[operands[left_index]] = collection_item_type - # Try and narrow away 'None' - elif is_overlapping_none(item_type): + # We only try and narrow away 'None' for now + if is_overlapping_none(item_type): + collection_item_type = get_proper_type( + builtin_item_type(iterable_type) + ) if ( collection_item_type is not None and not is_overlapping_none(collection_item_type) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 0cb4bf8e4a19..21a1523580c2 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1392,13 +1392,13 @@ else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" if val in (None,): - reveal_type(val) # N: Revealed type is "None" + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" if val not in (None,): reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" else: - reveal_type(val) # N: Revealed type is "None" + reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" [builtins fixtures/primitives.pyi] [case testNarrowingWithTupleOfTypes] @@ -2130,111 +2130,3 @@ else: [typing fixtures/typing-medium.pyi] [builtins fixtures/ops.pyi] - - -[case testTypeNarrowingStringInLiteralUnion] -from typing import Literal, Tuple -typ: Tuple[Literal['a', 'b'], ...] = ('a', 'b') -x: str = "hi!" -if x in typ: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -else: - reveal_type(x) # N: Revealed type is "builtins.str" -[builtins fixtures/tuple.pyi] -[typing fixtures/typing-medium.pyi] - -[case testTypeNarrowingStringInLiteralUnionSubset] -from typing import Literal, Tuple -typeAlpha: Tuple[Literal['a', 'b', 'c'], ...] = ('a', 'b') -strIn: str = "b" -strOut: str = "c" -if strIn in typeAlpha: - reveal_type(strIn) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" -else: - reveal_type(strIn) # N: Revealed type is "builtins.str" -if strOut in typeAlpha: - reveal_type(strOut) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" -else: - reveal_type(strOut) # N: Revealed type is "builtins.str" -[builtins fixtures/primitives.pyi] -[typing fixtures/typing-medium.pyi] - -[case testNarrowingStringNotInLiteralUnion] -from typing import Literal, Tuple -typeAlpha: Tuple[Literal['a', 'b', 'c'],...] = ('a', 'b', 'c') -strIn: str = "c" -strOut: str = "d" -if strIn not in typeAlpha: - reveal_type(strIn) # N: Revealed type is "builtins.str" -else: - reveal_type(strIn) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" -if strOut in typeAlpha: - reveal_type(strOut) # N: Revealed type is "Union[Literal['a'], Literal['b'], Literal['c']]" -else: - reveal_type(strOut) # N: Revealed type is "builtins.str" -[builtins fixtures/primitives.pyi] -[typing fixtures/typing-medium.pyi] - -[case testNarrowingStringInLiteralUnionDontExpand] -from typing import Literal, Tuple -typeAlpha: Tuple[Literal['a', 'b', 'c'], ...] = ('a', 'b', 'c') -strIn: Literal['c'] = "c" -reveal_type(strIn) # N: Revealed type is "Literal['c']" -#Check we don't expand a Literal into the Union type -if strIn not in typeAlpha: - reveal_type(strIn) # N: Revealed type is "Literal['c']" -else: - reveal_type(strIn) # N: Revealed type is "Literal['c']" -[builtins fixtures/primitives.pyi] -[typing fixtures/typing-medium.pyi] - -[case testTypeNarrowingStringInMixedUnion] -from typing import Literal, Tuple -typ: Tuple[Literal['a', 'b'], ...] = ('a', 'b') -x: str = "hi!" -if x in typ: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -else: - reveal_type(x) # N: Revealed type is "builtins.str" -[builtins fixtures/tuple.pyi] -[typing fixtures/typing-medium.pyi] - -[case testTypeNarrowingStringInSet] -from typing import Literal, Set -typ: Set[Literal['a', 'b']] = {'a', 'b'} -x: str = "hi!" -if x in typ: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -else: - reveal_type(x) # N: Revealed type is "builtins.str" -if x not in typ: - reveal_type(x) # N: Revealed type is "builtins.str" -else: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -[builtins fixtures/narrowing.pyi] -[typing fixtures/typing-medium.pyi] - -[case testTypeNarrowingStringInList] -from typing import Literal, List -typ: List[Literal['a', 'b']] = ['a', 'b'] -x: str = "hi!" -if x in typ: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -else: - reveal_type(x) # N: Revealed type is "builtins.str" -if x not in typ: - reveal_type(x) # N: Revealed type is "builtins.str" -else: - reveal_type(x) # N: Revealed type is "Union[Literal['a'], Literal['b']]" -[builtins fixtures/narrowing.pyi] -[typing fixtures/typing-medium.pyi] - -[case testTypeNarrowingUnionStringFloat] -from typing import Union -def foobar(foo: Union[str, float]): - if foo in ['a', 'b']: - reveal_type(foo) # N: Revealed type is "builtins.str" - else: - reveal_type(foo) # N: Revealed type is "Union[builtins.str, builtins.float]" -[builtins fixtures/primitives.pyi] -[typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/fixtures/narrowing.pyi b/test-data/unit/fixtures/narrowing.pyi index a36ac7f29bd2..89ee011c1c80 100644 --- a/test-data/unit/fixtures/narrowing.pyi +++ b/test-data/unit/fixtures/narrowing.pyi @@ -1,5 +1,5 @@ # Builtins stub used in check-narrowing test cases. -from typing import Generic, Sequence, Tuple, Type, TypeVar, Union, Iterable +from typing import Generic, Sequence, Tuple, Type, TypeVar, Union Tco = TypeVar('Tco', covariant=True) @@ -15,13 +15,6 @@ class function: pass class ellipsis: pass class int: pass class str: pass -class float: pass class dict(Generic[KT, VT]): pass def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass - -class list(Sequence[Tco]): - def __contains__(self, other: object) -> bool: pass -class set(Iterable[Tco], Generic[Tco]): - def __init__(self, iterable: Iterable[Tco] = ...) -> None: ... - def __contains__(self, item: object) -> bool: pass From aa7733a1e2dd5fc6014da839b3151b04f9533707 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 2 Oct 2024 16:45:56 +0100 Subject: [PATCH 0775/1617] Don't use equality to narrow when value is IntEnum/StrEnum (#17866) IntEnum/StrEnum values compare equal to the corresponding int/str values, which breaks the logic we use for narrowing based on equality to a literal value. Special case IntEnum/StrEnum to avoid the incorrect behavior. Fix #17860. --- mypy/typeops.py | 8 +++ test-data/unit/check-narrowing.test | 76 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/mypy/typeops.py b/mypy/typeops.py index 7f530d13d4e2..221976ce02b3 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1022,6 +1022,14 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool """ typ = get_proper_type(typ) if isinstance(typ, Instance): + if ( + typ.type.is_enum + and name in ("__eq__", "__ne__") + and any(base.fullname in ("enum.IntEnum", "enum.StrEnum") for base in typ.type.mro) + ): + # IntEnum and StrEnum values have non-straightfoward equality, so treat them + # as if they had custom __eq__ and __ne__ + return True method = typ.type.get(name) if method and isinstance(method.node, (SYMBOL_FUNCBASE_TYPES, Decorator, Var)): if method.node.info: diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 21a1523580c2..169523e61be7 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2130,3 +2130,79 @@ else: [typing fixtures/typing-medium.pyi] [builtins fixtures/ops.pyi] + +[case testNarrowingWithIntEnum] +# mypy: strict-equality +from __future__ import annotations +from typing import Any +from enum import IntEnum, StrEnum + +class IE(IntEnum): + X = 1 + Y = 2 + +def f1(x: int) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + if x != IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2(x: IE) -> None: + if x == 1: + reveal_type(x) # N: Revealed type is "__main__.IE" + else: + reveal_type(x) # N: Revealed type is "__main__.IE" + +def f3(x: object) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "builtins.object" + else: + reveal_type(x) # N: Revealed type is "builtins.object" + +def f4(x: int | Any) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + else: + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + +def f5(x: int) -> None: + if x is IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + if x is not IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" +[builtins fixtures/primitives.pyi] + +[case testNarrowingWithStrEnum] +# mypy: strict-equality +from enum import StrEnum + +class SE(StrEnum): + A = 'a' + B = 'b' + +def f1(x: str) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "builtins.str" + else: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2(x: SE) -> None: + if x == 'a': + reveal_type(x) # N: Revealed type is "__main__.SE" + else: + reveal_type(x) # N: Revealed type is "__main__.SE" + +def f3(x: object) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "builtins.object" + else: + reveal_type(x) # N: Revealed type is "builtins.object" +[builtins fixtures/primitives.pyi] From ac98ab59f7811a4b7272161610abc21958a528b2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 2 Oct 2024 16:46:12 +0100 Subject: [PATCH 0776/1617] Fix typos in .github/worflows/test.yml (#17867) --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e29feef7b00..39fbb14bd3b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,14 +71,14 @@ jobs: tox_extra_args: "-n 4" test_mypyc: true - - name: Test suit with py313-dev-ubuntu, mypyc-compiled + - name: Test suite with py313-dev-ubuntu, mypyc-compiled python: '3.13-dev' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 4" test_mypyc: true - # - name: Test suit with py314-dev-ubuntu + # - name: Test suite with py314-dev-ubuntu # python: '3.14-dev' # arch: x64 # os: ubuntu-latest From 3c09b3241d22e21d7fa160ae444a93c9487a927f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 3 Oct 2024 16:12:52 +0100 Subject: [PATCH 0777/1617] Filter overload items based on self type during type inference (#17873) Fix type argument inference for overloaded functions with explicit self types. Filter out the overload items based on the declared and actual types of self. The implementation is best effort and does the filtering only in simple cases, to reduce the risk of regressions (primarily performance, but I worry also about infinite recursion). I added a fast path for the typical case, since without it the filtering was quite expensive. Note that the overload item filtering already worked in many contexts. This only improves it in specific contexts -- at least when inferring generic protocol compatibility. This is a more localized (and thus lower-risk) fix compared to #14975 (thanks @tyralla!). #14975 might still be a good idea, but I'm not comfortable merging it now, and I want a quick fix to unblock the mypy 1.12 release. Fixes #15031. Fixes #17863. Co-authored by @tyralla. --- mypy/typeops.py | 62 ++++++++++++++++++- test-data/unit/check-overloading.test | 18 ++++++ test-data/unit/check-protocols.test | 88 +++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 3 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 221976ce02b3..efb5d8fa505f 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -14,6 +14,7 @@ from mypy.expandtype import expand_type, expand_type_by_instance from mypy.maptype import map_instance_to_supertype from mypy.nodes import ( + ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, @@ -305,9 +306,27 @@ class B(A): pass """ if isinstance(method, Overloaded): - items = [ - bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items - ] + items = [] + original_type = get_proper_type(original_type) + for c in method.items: + if isinstance(original_type, Instance): + # Filter based on whether declared self type can match actual object type. + # For example, if self has type C[int] and method is accessed on a C[str] value, + # omit this item. This is best effort since bind_self can be called in many + # contexts, and doing complete validation might trigger infinite recursion. + # + # Note that overload item filtering normally happens elsewhere. This is needed + # at least during constraint inference. + keep = is_valid_self_type_best_effort(c, original_type) + else: + keep = True + if keep: + items.append(bind_self(c, original_type, is_classmethod, ignore_instances)) + if len(items) == 0: + # If no item matches, returning all items helps avoid some spurious errors + items = [ + bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items + ] return cast(F, Overloaded(items)) assert isinstance(method, CallableType) func = method @@ -379,6 +398,43 @@ class B(A): pass return cast(F, res) +def is_valid_self_type_best_effort(c: CallableType, self_type: Instance) -> bool: + """Quickly check if self_type might match the self in a callable. + + Avoid performing any complex type operations. This is performance-critical. + + Default to returning True if we don't know (or it would be too expensive). + """ + if ( + self_type.args + and c.arg_types + and isinstance((arg_type := get_proper_type(c.arg_types[0])), Instance) + and c.arg_kinds[0] in (ARG_POS, ARG_OPT) + and arg_type.args + and self_type.type.fullname != "functools._SingleDispatchCallable" + ): + if self_type.type is not arg_type.type: + # We can't map to supertype, since it could trigger expensive checks for + # protocol types, so we consevatively assume this is fine. + return True + + # Fast path: no explicit annotation on self + if all( + ( + type(arg) is TypeVarType + and type(arg.upper_bound) is Instance + and arg.upper_bound.type.fullname == "builtins.object" + ) + for arg in arg_type.args + ): + return True + + from mypy.meet import is_overlapping_types + + return is_overlapping_types(self_type, c.arg_types[0]) + return True + + def erase_to_bound(t: Type) -> Type: # TODO: use value restrictions to produce a union? t = get_proper_type(t) diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 48d5996b226f..e414c1c9b0b6 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6750,3 +6750,21 @@ def foo(x: object) -> str: ... def bar(x: int) -> int: ... @overload def bar(x: Any) -> str: ... + +[case testOverloadOnInvalidTypeArgument] +from typing import TypeVar, Self, Generic, overload + +class C: pass + +T = TypeVar("T", bound=C) + +class D(Generic[T]): + @overload + def f(self, x: int) -> int: ... + @overload + def f(self, x: str) -> str: ... + def f(Self, x): ... + +a: D[str] # E: Type argument "str" of "D" must be a subtype of "C" +reveal_type(a.f(1)) # N: Revealed type is "builtins.int" +reveal_type(a.f("x")) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index ee7556461fd3..5ed2351e33e6 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4127,3 +4127,91 @@ class P(Protocol): class C(P): ... C(0) # OK + +[case testTypeVarValueConstraintAgainstGenericProtocol] +from typing import TypeVar, Generic, Protocol, overload + +T_contra = TypeVar("T_contra", contravariant=True) +AnyStr = TypeVar("AnyStr", str, bytes) + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra, /) -> None: ... + +class Buffer: ... + +class IO(Generic[AnyStr]): + @overload + def write(self: IO[bytes], s: Buffer, /) -> None: ... + @overload + def write(self, s: AnyStr, /) -> None: ... + def write(self, s): ... + +def foo(fdst: SupportsWrite[AnyStr]) -> None: ... + +x: IO[str] +foo(x) + +[case testTypeVarValueConstraintAgainstGenericProtocol2] +from typing import Generic, Protocol, TypeVar, overload + +AnyStr = TypeVar("AnyStr", str, bytes) +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + +class SupportsRead(Generic[T_co]): + def read(self) -> T_co: ... + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra) -> object: ... + +def copyfileobj(fsrc: SupportsRead[AnyStr], fdst: SupportsWrite[AnyStr]) -> None: ... + +class WriteToMe(Generic[AnyStr]): + @overload + def write(self: WriteToMe[str], s: str) -> int: ... + @overload + def write(self: WriteToMe[bytes], s: bytes) -> int: ... + def write(self, s): ... + +class WriteToMeOrReadFromMe(WriteToMe[AnyStr], SupportsRead[AnyStr]): ... + +copyfileobj(WriteToMeOrReadFromMe[bytes](), WriteToMe[bytes]()) + +[case testOverloadedMethodWithExplictSelfTypes] +from typing import Generic, overload, Protocol, TypeVar, Union + +AnyStr = TypeVar("AnyStr", str, bytes) +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + +class SupportsRead(Protocol[T_co]): + def read(self) -> T_co: ... + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra) -> int: ... + +class Input(Generic[AnyStr]): + def read(self) -> AnyStr: ... + +class Output(Generic[AnyStr]): + @overload + def write(self: Output[str], s: str) -> int: ... + @overload + def write(self: Output[bytes], s: bytes) -> int: ... + def write(self, s: Union[str, bytes]) -> int: ... + +def f(src: SupportsRead[AnyStr], dst: SupportsWrite[AnyStr]) -> None: ... + +def g1(a: Input[bytes], b: Output[bytes]) -> None: + f(a, b) + +def g2(a: Input[bytes], b: Output[bytes]) -> None: + f(a, b) + +def g3(a: Input[str], b: Output[bytes]) -> None: + f(a, b) # E: Cannot infer type argument 1 of "f" + +def g4(a: Input[bytes], b: Output[str]) -> None: + f(a, b) # E: Cannot infer type argument 1 of "f" + +[builtins fixtures/tuple.pyi] From 9330193cded6a872a6f8202ee197a5a378e17673 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 4 Oct 2024 12:52:01 +0100 Subject: [PATCH 0778/1617] Fix narrowing of IntEnum and StrEnum types (#17874) Fix regression introduced in #17866. It should still be possible to narrow IntEnum and StrEnum types, but only when types match or are disjoint. Add more logic to rule out narrowing when types are ambigous. --- mypy/checker.py | 46 +++++++++++++++++- mypy/typeops.py | 8 ---- test-data/unit/check-narrowing.test | 73 ++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index a089576e6ffe..0d8ae3da1c9f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5984,7 +5984,9 @@ def has_no_custom_eq_checks(t: Type) -> bool: coerce_only_in_literal_context = True expr_types = [operand_types[i] for i in expr_indices] - should_narrow_by_identity = all(map(has_no_custom_eq_checks, expr_types)) + should_narrow_by_identity = all( + map(has_no_custom_eq_checks, expr_types) + ) and not is_ambiguous_mix_of_enums(expr_types) if_map: TypeMap = {} else_map: TypeMap = {} @@ -8604,3 +8606,45 @@ def visit_starred_pattern(self, p: StarredPattern) -> None: self.lvalue = True p.capture.accept(self) self.lvalue = False + + +def is_ambiguous_mix_of_enums(types: list[Type]) -> bool: + """Do types have IntEnum/StrEnum types that are potentially overlapping with other types? + + If True, we shouldn't attempt type narrowing based on enum values, as it gets + too ambiguous. + + For example, return True if there's an 'int' type together with an IntEnum literal. + However, IntEnum together with a literal of the same IntEnum type is not ambiguous. + """ + # We need these things for this to be ambiguous: + # (1) an IntEnum or StrEnum type + # (2) either a different IntEnum/StrEnum type or a non-enum type ("") + # + # It would be slightly more correct to calculate this separately for IntEnum and + # StrEnum related types, as an IntEnum can't be confused with a StrEnum. + return len(_ambiguous_enum_variants(types)) > 1 + + +def _ambiguous_enum_variants(types: list[Type]) -> set[str]: + result = set() + for t in types: + t = get_proper_type(t) + if isinstance(t, UnionType): + result.update(_ambiguous_enum_variants(t.items)) + elif isinstance(t, Instance): + if t.last_known_value: + result.update(_ambiguous_enum_variants([t.last_known_value])) + elif t.type.is_enum and any( + base.fullname in ("enum.IntEnum", "enum.StrEnum") for base in t.type.mro + ): + result.add(t.type.fullname) + elif not t.type.is_enum: + # These might compare equal to IntEnum/StrEnum types (e.g. Decimal), so + # let's be conservative + result.add("") + elif isinstance(t, LiteralType): + result.update(_ambiguous_enum_variants([t.fallback])) + else: + result.add("") + return result diff --git a/mypy/typeops.py b/mypy/typeops.py index efb5d8fa505f..0699cda53cfa 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1078,14 +1078,6 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool """ typ = get_proper_type(typ) if isinstance(typ, Instance): - if ( - typ.type.is_enum - and name in ("__eq__", "__ne__") - and any(base.fullname in ("enum.IntEnum", "enum.StrEnum") for base in typ.type.mro) - ): - # IntEnum and StrEnum values have non-straightfoward equality, so treat them - # as if they had custom __eq__ and __ne__ - return True method = typ.type.get(name) if method and isinstance(method.node, (SYMBOL_FUNCBASE_TYPES, Decorator, Var)): if method.node.info: diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 169523e61be7..11bbf0a95084 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2135,7 +2135,7 @@ else: # mypy: strict-equality from __future__ import annotations from typing import Any -from enum import IntEnum, StrEnum +from enum import IntEnum class IE(IntEnum): X = 1 @@ -2178,6 +2178,71 @@ def f5(x: int) -> None: reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + +def f6(x: IE) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" +[builtins fixtures/primitives.pyi] + +[case testNarrowingWithIntEnum2] +# mypy: strict-equality +from __future__ import annotations +from typing import Any +from enum import IntEnum, Enum + +class MyDecimal: ... + +class IE(IntEnum): + X = 1 + Y = 2 + +class IE2(IntEnum): + X = 1 + Y = 2 + +class E(Enum): + X = 1 + Y = 2 + +def f1(x: IE | MyDecimal) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.MyDecimal]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.MyDecimal]" + +def f2(x: E | bytes) -> None: + if x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.E.Y], builtins.bytes]" + +def f3(x: IE | IE2) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.IE2]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.IE2]" + +def f4(x: IE | E) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + elif x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.IE.Y], Literal[__main__.E.Y]]" + +def f5(x: E | str | int) -> None: + if x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.E.Y], builtins.str, builtins.int]" + +def f6(x: IE | Any) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" [builtins fixtures/primitives.pyi] [case testNarrowingWithStrEnum] @@ -2205,4 +2270,10 @@ def f3(x: object) -> None: reveal_type(x) # N: Revealed type is "builtins.object" else: reveal_type(x) # N: Revealed type is "builtins.object" + +def f4(x: SE) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "Literal[__main__.SE.A]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.SE.B]" [builtins fixtures/primitives.pyi] From 012d52c36cbade3b0677f422a86a213e90788f18 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 4 Oct 2024 14:33:32 +0100 Subject: [PATCH 0779/1617] Don't consider None vs IntEnum comparison ambiguous (#17877) We can assume that None doesn't compare equal to an enum. --- mypy/checker.py | 2 ++ test-data/unit/check-narrowing.test | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 0d8ae3da1c9f..8d77bb02eeb2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8645,6 +8645,8 @@ def _ambiguous_enum_variants(types: list[Type]) -> set[str]: result.add("") elif isinstance(t, LiteralType): result.update(_ambiguous_enum_variants([t.fallback])) + elif isinstance(t, NoneType): + pass else: result.add("") return result diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 11bbf0a95084..60fc39dd817b 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2243,6 +2243,20 @@ def f6(x: IE | Any) -> None: reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" else: reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" + +def f7(x: IE | None) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.IE.Y], None]" + +def f8(x: IE | None) -> None: + if x is None: + reveal_type(x) # N: Revealed type is "None" + elif x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" [builtins fixtures/primitives.pyi] [case testNarrowingWithStrEnum] From bc8119150e49895f7a496ae7ae7362a2828e7e9e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 4 Oct 2024 15:26:48 +0100 Subject: [PATCH 0780/1617] [PEP 696] Report error if using unsupported type parameter defaults (#17876) We don't yet support default types for type parameters when using the new type parameter syntax. Generate an error instead of just ignoring them. Also make it possible to write Python 3.13 specific tests. --- mypy/fastparse.py | 7 +++++++ mypy/message_registry.py | 5 +++++ mypy/test/helpers.py | 13 +++---------- mypy/test/testcheck.py | 2 ++ test-data/unit/check-python313.test | 11 +++++++++++ 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 test-data/unit/check-python313.test diff --git a/mypy/fastparse.py b/mypy/fastparse.py index bbbe2184738c..726397adb849 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1198,6 +1198,13 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: for p in type_params: bound = None values: list[Type] = [] + if sys.version_info >= (3, 13) and p.default_value is not None: + self.fail( + message_registry.TYPE_PARAM_DEFAULT_NOT_SUPPORTED, + p.lineno, + p.col_offset, + blocker=False, + ) if isinstance(p, ast_ParamSpec): # type: ignore[misc] explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [])) elif isinstance(p, ast_TypeVarTuple): # type: ignore[misc] diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 29d539faaed6..507af6fdad74 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -362,3 +362,8 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage( "Await expression cannot be used within a type alias", codes.SYNTAX ) + +TYPE_PARAM_DEFAULT_NOT_SUPPORTED: Final = ErrorMessage( + "Type parameter default types not supported when using Python 3.12 type parameter syntax", + codes.MISC, +) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f532e77b82d3..4a80207d3ec7 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -256,16 +256,9 @@ def local_sys_path_set() -> Iterator[None]: def testfile_pyversion(path: str) -> tuple[int, int]: - if path.endswith("python312.test"): - return 3, 12 - elif path.endswith("python311.test"): - return 3, 11 - elif path.endswith("python310.test"): - return 3, 10 - elif path.endswith("python39.test"): - return 3, 9 - elif path.endswith("python38.test"): - return 3, 8 + m = re.search(r"python3([0-9]+)\.test$", path) + if m: + return 3, int(m.group(1)) else: return defaults.PYTHON3_VERSION diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 5fba6192dcaf..330e191af252 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -45,6 +45,8 @@ typecheck_files.remove("check-python311.test") if sys.version_info < (3, 12): typecheck_files.remove("check-python312.test") +if sys.version_info < (3, 13): + typecheck_files.remove("check-python313.test") # Special tests for platforms with case-insensitive filesystems. if sys.platform not in ("darwin", "win32"): diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test new file mode 100644 index 000000000000..0ba64ad67c91 --- /dev/null +++ b/test-data/unit/check-python313.test @@ -0,0 +1,11 @@ +[case testPEP695TypeParameterDefaultNotSupported] +class C[T = None]: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax + pass + +def f[T = list[int]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax + pass + +def g[**P = [int, str]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax + pass + +type A[T, S = int, U = str] = list[T] # E: Type parameter default types not supported when using Python 3.12 type parameter syntax From 16ba7f41a4a9786b55658dd89d23f74a9d9a1cb0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 4 Oct 2024 21:35:03 +0200 Subject: [PATCH 0781/1617] Bump version to 1.13.0+dev (#17879) The release branch has been cut: https://github.com/python/mypy/tree/release-1.12 --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 8e00b4cce702..e0671c9feb06 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.12.0+dev" +__version__ = "1.13.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 4e4826fa8006c2413486ffa55747790fa7484644 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:33:07 -0700 Subject: [PATCH 0782/1617] Add regression test cases (#17869) https://github.com/python/mypy/issues/17864 --- test-data/unit/check-narrowing.test | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 60fc39dd817b..d740708991d0 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2291,3 +2291,45 @@ def f4(x: SE) -> None: else: reveal_type(x) # N: Revealed type is "Literal[__main__.SE.B]" [builtins fixtures/primitives.pyi] + +[case testConsistentNarrowingEqAndIn] +# flags: --python-version 3.10 + +# https://github.com/python/mypy/issues/17864 +def f(x: str | int) -> None: + if x == "x": + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + y = x + + if x in ["x"]: + # TODO: we should fix this reveal https://github.com/python/mypy/issues/3229 + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + y = x + z = x + z = y +[builtins fixtures/primitives.pyi] + +[case testConsistentNarrowingInWithCustomEq] +# flags: --python-version 3.10 + +# https://github.com/python/mypy/issues/17864 +class C: + def __init__(self, x: int) -> None: + self.x = x + + def __eq__(self, other: object) -> bool: + raise + # Example implementation: + # if isinstance(other, C) and other.x == self.x: + # return True + # return NotImplemented + +class D(C): + pass + +def f(x: C) -> None: + if x in [D(5)]: + reveal_type(x) # D # N: Revealed type is "__main__.C" + +f(C(5)) +[builtins fixtures/primitives.pyi] From dfb7be19b6fc2b3fe20ee07851ae3759cf18033d Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 6 Oct 2024 05:56:44 -0400 Subject: [PATCH 0783/1617] Include CHANGELOG.md in sdists (#17882) Refs #17880 --- MANIFEST.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index c18b83cc0088..c2399d2b00b6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -41,8 +41,8 @@ include runtests.py include pytest.ini include tox.ini -include LICENSE mypyc/README.md -exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md CHANGELOG.md action.yml .editorconfig +include LICENSE mypyc/README.md CHANGELOG.md +exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md action.yml .editorconfig exclude .git-blame-ignore-revs .pre-commit-config.yaml global-exclude *.py[cod] From b10d781e004339c4d592b0ad5372f8e014130f7f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 6 Oct 2024 14:25:54 +0100 Subject: [PATCH 0784/1617] Fix re-processing cross-reference when node kind changes (#17883) This is quite a bad bug. Currently we rely on `SymbolNode` being updated in-place for all indirect references, but this is not the case when node kind (`FuncDef`, `Decorator`, etc.) changes, in this case a _new_ `SymbolNode` is created. I fix this by forcing reprocessing if the node kind changes. This currently blocks support for PEP 702, see https://github.com/python/mypy/pull/17476, so I will not wait for long before merging. --- mypy/server/astdiff.py | 4 +- test-data/unit/fine-grained.test | 98 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index fc868d288b4d..d5a303128126 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -219,7 +219,9 @@ def snapshot_symbol_table(name_prefix: str, table: SymbolTable) -> dict[str, Sym assert symbol.kind != UNBOUND_IMPORTED if node and get_prefix(node.fullname) != name_prefix: # This is a cross-reference to a node defined in another module. - result[name] = ("CrossRef", common) + # Include the node kind (FuncDef, Decorator, TypeInfo, ...), so that we will + # reprocess when a *new* node is created instead of merging an existing one. + result[name] = ("CrossRef", common, type(node).__name__) else: result[name] = snapshot_definition(node, common) return result diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index faedd890922d..15e47ff296ea 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10573,3 +10573,101 @@ m.py:9: error: Argument 1 to "foo" has incompatible type "int"; expected "str" m.py:9: error: Argument 2 to "foo" has incompatible type "str"; expected "int" m.py:10: error: Unexpected keyword argument "a" for "foo" partial.py:4: note: "foo" defined here + +[case testReplaceFunctionWithDecoratedFunctionIndirect] +from b import f +x: int = f() +import b +y: int = b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing import Callable +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testReplaceFunctionWithDecoratedFunctionIndirect2] +from c import f +x: int = f() +import c +y: int = c.f() + +[file c.py] +from b import f + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing import Callable +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testReplaceFunctionWithClassIndirect] +from b import f +x: int = f() +import b +y: int = b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +class f: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "f", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "f", variable has type "int") + +[case testReplaceFunctionWithClassIndirect2] +from c import f +x: int = f() +import c +y: int = c.f() + +[file c.py] +from b import f + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +class f: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "f", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "f", variable has type "int") From 13d6738f5295eef58aab2b660763baa6609bb690 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 6 Oct 2024 09:49:18 -0400 Subject: [PATCH 0785/1617] Add is_lvalue attribute to AttributeContext (#17881) Refs #17878 --- docs/source/extending_mypy.rst | 2 +- mypy/checkmember.py | 14 +++++++++++--- mypy/plugin.py | 1 + test-data/unit/check-custom-plugin.test | 3 +++ test-data/unit/plugins/attrhook2.py | 9 +++++++++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index bbbec2ad3880..bded07319b64 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -179,7 +179,7 @@ mypy will call ``get_method_signature_hook("ctypes.Array.__setitem__")`` so that the plugin can mimic the :py:mod:`ctypes` auto-convert behavior. **get_attribute_hook()** overrides instance member field lookups and property -access (not assignments, and not method calls). This hook is only called for +access (not method calls). This hook is only called for fields which already exist on the class. *Exception:* if :py:meth:`__getattr__ ` or :py:meth:`__getattribute__ ` is a method on the class, the hook is called for all fields which do not refer to methods. diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 8f99f96e2dd5..adfd5c2a97d6 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -572,7 +572,11 @@ def analyze_member_var_access( if hook: result = hook( AttributeContext( - get_proper_type(mx.original_type), result, mx.context, mx.chk + get_proper_type(mx.original_type), + result, + mx.is_lvalue, + mx.context, + mx.chk, ) ) return result @@ -829,7 +833,9 @@ def analyze_var( result = analyze_descriptor_access(result, mx) if hook: result = hook( - AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) + AttributeContext( + get_proper_type(mx.original_type), result, mx.is_lvalue, mx.context, mx.chk + ) ) return result @@ -1148,7 +1154,9 @@ def apply_class_attr_hook( ) -> Type | None: if hook: result = hook( - AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) + AttributeContext( + get_proper_type(mx.original_type), result, mx.is_lvalue, mx.context, mx.chk + ) ) return result diff --git a/mypy/plugin.py b/mypy/plugin.py index a1af7fa76350..a4dc0052ec79 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -495,6 +495,7 @@ class MethodContext(NamedTuple): class AttributeContext(NamedTuple): type: ProperType # Type of object with attribute default_attr_type: Type # Original attribute type + is_lvalue: bool # Whether the attribute is the target of an assignment context: Context # Relevant location context (e.g. for error messages) api: CheckerPluginInterface diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 2b3b3f4a8695..666bf9680405 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -232,6 +232,8 @@ reveal_type(magic.non_magic_method()) # N: Revealed type is "builtins.int" reveal_type(magic.non_magic_field) # N: Revealed type is "builtins.int" magic.nonexistent_field # E: Field does not exist reveal_type(magic.fallback_example) # N: Revealed type is "Any" +reveal_type(magic.no_assignment_field) # N: Revealed type is "builtins.float" +magic.no_assignment_field = "bad" # E: Cannot assign to field derived = DerivedMagic() reveal_type(derived.magic_field) # N: Revealed type is "builtins.str" @@ -250,6 +252,7 @@ class Magic: def __getattr__(self, x: Any) -> Any: ... def non_magic_method(self) -> int: ... non_magic_field: int + no_assignment_field: float class DerivedMagic(Magic): ... [file mypy.ini] diff --git a/test-data/unit/plugins/attrhook2.py b/test-data/unit/plugins/attrhook2.py index 2d41a0fdf52f..1ce318d2057b 100644 --- a/test-data/unit/plugins/attrhook2.py +++ b/test-data/unit/plugins/attrhook2.py @@ -12,6 +12,8 @@ def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type return magic_field_callback if fullname == "m.Magic.nonexistent_field": return nonexistent_field_callback + if fullname == "m.Magic.no_assignment_field": + return no_assignment_field_callback return None @@ -24,5 +26,12 @@ def nonexistent_field_callback(ctx: AttributeContext) -> Type: return AnyType(TypeOfAny.from_error) +def no_assignment_field_callback(ctx: AttributeContext) -> Type: + if ctx.is_lvalue: + ctx.api.fail(f"Cannot assign to field", ctx.context) + return AnyType(TypeOfAny.from_error) + return ctx.default_attr_type + + def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin From 4da779bea0e89f4945e3cd216c9bdf4a4e68537b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 6 Oct 2024 22:07:15 +0100 Subject: [PATCH 0786/1617] [mypyc] Make C unit tests faster by compiling with -O0 (#17884) Most time is spent in compilation, so runtime performance doesn't really matter. --- mypyc/test/test_external.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypyc/test/test_external.py b/mypyc/test/test_external.py index 22eb8019133c..010c74dee42e 100644 --- a/mypyc/test/test_external.py +++ b/mypyc/test/test_external.py @@ -20,7 +20,9 @@ def test_c_unit_test(self) -> None: cppflags: list[str] = [] env = os.environ.copy() if sys.platform == "darwin": - cppflags += ["-mmacosx-version-min=10.10", "-stdlib=libc++"] + cppflags += ["-O0", "-mmacosx-version-min=10.10", "-stdlib=libc++"] + elif sys.platform == "linux": + cppflags += ["-O0"] env["CPPFLAGS"] = " ".join(cppflags) # Build Python wrapper for C unit tests. From 94c49a8215c9c8d3a4ec146696213dd2b9ebfb42 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Mon, 7 Oct 2024 00:25:02 +0200 Subject: [PATCH 0787/1617] Add basic support for PEP 702 (@deprecated). (#17476) Closes #16111 This PR provides only basic support. Many special cases might need additional attention (descriptors, some special methods like `__int__`, etc.). Other open issues are code comments, eventual documentation updates, the deprecation message style, etc.). But I wanted to offer these first steps before going on vacation (so I cannot respond to possible reviews too soon). Maybe someone wants to extend the list of (test) cases the basic support should address? --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ivan Levkivskyi --- docs/source/command_line.rst | 6 + docs/source/error_code_list2.rst | 38 +++ mypy/checker.py | 46 ++++ mypy/checkexpr.py | 39 ++- mypy/checkmember.py | 13 +- mypy/errorcodes.py | 6 + mypy/errors.py | 5 +- mypy/main.py | 7 + mypy/nodes.py | 17 +- mypy/options.py | 3 + mypy/semanal.py | 64 ++++- mypy/server/astdiff.py | 1 + test-data/unit/check-deprecated.test | 396 +++++++++++++++++++++++++++ test-data/unit/fine-grained.test | 234 ++++++++++++++++ test-data/unit/pythoneval.test | 2 +- 15 files changed, 860 insertions(+), 17 deletions(-) create mode 100644 test-data/unit/check-deprecated.test diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 20fb3821438a..92db5d59d0ee 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -537,6 +537,12 @@ potentially problematic or redundant in some way. This limitation will be removed in future releases of mypy. +.. option:: --report-deprecated-as-error + + By default, mypy emits notes if your code imports or uses deprecated + features. This flag converts such notes to errors, causing mypy to + eventually finish with a non-zero exit code. Features are considered + deprecated when decorated with ``warnings.deprecated``. .. _miscellaneous-strictness-flags: diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 6d50e217a77d..eb18d76e2f2f 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -231,6 +231,44 @@ incorrect control flow or conditional checks that are accidentally always true o # Error: Statement is unreachable [unreachable] print('unreachable') +.. _code-deprecated: + +Check that imported or used feature is deprecated [deprecated] +-------------------------------------------------------------- + +By default, mypy generates a note if your code imports a deprecated feature explicitly with a +``from mod import depr`` statement or uses a deprecated feature imported otherwise or defined +locally. Features are considered deprecated when decorated with ``warnings.deprecated``, as +specified in `PEP 702 `_. You can silence single notes via +``# type: ignore[deprecated]`` or turn off this check completely via ``--disable-error-code=deprecated``. +Use the :option:`--report-deprecated-as-error ` option for +more strictness, which turns all such notes into errors. + +.. note:: + + The ``warnings`` module provides the ``@deprecated`` decorator since Python 3.13. + To use it with older Python versions, import it from ``typing_extensions`` instead. + +Examples: + +.. code-block:: python + + # mypy: report-deprecated-as-error + + # Error: abc.abstractproperty is deprecated: Deprecated, use 'property' with 'abstractmethod' instead + from abc import abstractproperty + + from typing_extensions import deprecated + + @deprecated("use new_function") + def old_function() -> None: + print("I am old") + + # Error: __main__.old_function is deprecated: use new_function + old_function() + old_function() # type: ignore[deprecated] + + .. _code-redundant-expr: Check that expression is redundant [redundant-expr] diff --git a/mypy/checker.py b/mypy/checker.py index 8d77bb02eeb2..4bbd49cd7198 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2838,6 +2838,9 @@ def check_metaclass_compatibility(self, typ: TypeInfo) -> None: ) def visit_import_from(self, node: ImportFrom) -> None: + for name, _ in node.names: + if (sym := self.globals.get(name)) is not None: + self.warn_deprecated(sym.node, node) self.check_import(node) def visit_import_all(self, node: ImportAll) -> None: @@ -2926,6 +2929,16 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: Handle all kinds of assignment statements (simple, indexed, multiple). """ + + if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + for lvalue in s.lvalues: + if ( + isinstance(lvalue, NameExpr) + and isinstance(var := lvalue.node, Var) + and isinstance(instance := get_proper_type(var.type), Instance) + ): + self.check_deprecated(instance.type, s) + # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such # as X | Y. @@ -4671,6 +4684,16 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None: if inplace: # There is __ifoo__, treat as x = x.__ifoo__(y) rvalue_type, method_type = self.expr_checker.check_op(method, lvalue_type, s.rvalue, s) + if isinstance(inst := get_proper_type(lvalue_type), Instance) and isinstance( + defn := inst.type.get_method(method), OverloadedFuncDef + ): + for item in defn.items: + if ( + isinstance(item, Decorator) + and isinstance(typ := item.func.type, CallableType) + and (bind_self(typ) == method_type) + ): + self.warn_deprecated(item.func, s) if not is_subtype(rvalue_type, lvalue_type): self.msg.incompatible_operator_assignment(s.op, s) else: @@ -7535,6 +7558,29 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: return self.expr_checker.accept(node, type_context=type_context) + def check_deprecated(self, node: SymbolNode | None, context: Context) -> None: + """Warn if deprecated and not directly imported with a `from` statement.""" + if isinstance(node, Decorator): + node = node.func + if isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) and ( + node.deprecated is not None + ): + for imp in self.tree.imports: + if isinstance(imp, ImportFrom) and any(node.name == n[0] for n in imp.names): + break + else: + self.warn_deprecated(node, context) + + def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: + """Warn if deprecated.""" + if isinstance(node, Decorator): + node = node.func + if isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) and ( + (deprecated := node.deprecated) is not None + ): + warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note + warn(deprecated, context, code=codes.DEPRECATED) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 98e6eb6a7fc3..0754b1db7141 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -126,6 +126,7 @@ validate_instance, ) from mypy.typeops import ( + bind_self, callable_type, custom_special_method, erase_to_union_or_bound, @@ -354,7 +355,9 @@ def visit_name_expr(self, e: NameExpr) -> Type: """ self.chk.module_refs.update(extract_refexpr_names(e)) result = self.analyze_ref_expr(e) - return self.narrow_type_from_binder(e, result) + narrowed = self.narrow_type_from_binder(e, result) + self.chk.check_deprecated(e.node, e) + return narrowed def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result: Type | None = None @@ -1479,6 +1482,10 @@ def check_call_expr_with_callee_type( object_type=object_type, ) proper_callee = get_proper_type(callee_type) + if isinstance(e.callee, NameExpr) and isinstance(e.callee.node, OverloadedFuncDef): + for item in e.callee.node.items: + if isinstance(item, Decorator) and (item.func.type == callee_type): + self.chk.check_deprecated(item.func, e) if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType): # Cache it for find_isinstance_check() if proper_callee.type_guard is not None: @@ -3267,7 +3274,9 @@ def visit_member_expr(self, e: MemberExpr, is_lvalue: bool = False) -> Type: """Visit member expression (of form e.id).""" self.chk.module_refs.update(extract_refexpr_names(e)) result = self.analyze_ordinary_member_access(e, is_lvalue) - return self.narrow_type_from_binder(e, result) + narrowed = self.narrow_type_from_binder(e, result) + self.chk.warn_deprecated(e.node, e) + return narrowed def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type: """Analyse member expression or member lvalue.""" @@ -3956,7 +3965,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: # This is the case even if the __add__ method is completely missing and the __radd__ # method is defined. - variants_raw = [(left_op, left_type, right_expr)] + variants_raw = [(op_name, left_op, left_type, right_expr)] elif ( is_subtype(right_type, left_type) and isinstance(left_type, Instance) @@ -3977,19 +3986,25 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: # As a special case, the alt_promote check makes sure that we don't use the # __radd__ method of int if the LHS is a native int type. - variants_raw = [(right_op, right_type, left_expr), (left_op, left_type, right_expr)] + variants_raw = [ + (rev_op_name, right_op, right_type, left_expr), + (op_name, left_op, left_type, right_expr), + ] else: # In all other cases, we do the usual thing and call __add__ first and # __radd__ second when doing "A() + B()". - variants_raw = [(left_op, left_type, right_expr), (right_op, right_type, left_expr)] + variants_raw = [ + (op_name, left_op, left_type, right_expr), + (rev_op_name, right_op, right_type, left_expr), + ] # STEP 3: # We now filter out all non-existent operators. The 'variants' list contains # all operator methods that are actually present, in the order that Python # attempts to invoke them. - variants = [(op, obj, arg) for (op, obj, arg) in variants_raw if op is not None] + variants = [(na, op, obj, arg) for (na, op, obj, arg) in variants_raw if op is not None] # STEP 4: # We now try invoking each one. If an operation succeeds, end early and return @@ -3998,13 +4013,23 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: errors = [] results = [] - for method, obj, arg in variants: + for name, method, obj, arg in variants: with self.msg.filter_errors(save_filtered_errors=True) as local_errors: result = self.check_method_call(op_name, obj, method, [arg], [ARG_POS], context) if local_errors.has_new_errors(): errors.append(local_errors.filtered_errors()) results.append(result) else: + if isinstance(obj, Instance) and isinstance( + defn := obj.type.get_method(name), OverloadedFuncDef + ): + for item in defn.items: + if ( + isinstance(item, Decorator) + and isinstance(typ := item.func.type, CallableType) + and bind_self(typ) == result[1] + ): + self.chk.check_deprecated(item.func, context) return result # We finish invoking above operators and no early return happens. Therefore, diff --git a/mypy/checkmember.py b/mypy/checkmember.py index adfd5c2a97d6..4448072d52c3 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -316,9 +316,12 @@ def analyze_instance_member_access( if method.is_property: assert isinstance(method, OverloadedFuncDef) - first_item = method.items[0] - assert isinstance(first_item, Decorator) - return analyze_var(name, first_item.var, typ, info, mx) + getter = method.items[0] + assert isinstance(getter, Decorator) + if mx.is_lvalue and (len(items := method.items) > 1): + mx.chk.warn_deprecated(items[1], mx.context) + return analyze_var(name, getter.var, typ, info, mx) + if mx.is_lvalue: mx.msg.cant_assign_to_method(mx.context) if not isinstance(method, OverloadedFuncDef): @@ -493,6 +496,8 @@ def analyze_member_var_access( # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) + mx.chk.warn_deprecated(v, mx.context) + vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. @@ -1010,6 +1015,8 @@ def analyze_class_attribute_access( # on the class object itself rather than the instance. return None + mx.chk.warn_deprecated(node.node, mx.context) + is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncBase) if mx.is_lvalue: diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index a170b5d4d65a..b835f27bbad9 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -304,5 +304,11 @@ def __hash__(self) -> int: "General", ) +DEPRECATED: Final = ErrorCode( + "deprecated", + "Warn when importing or using deprecated (overloaded) functions, methods or classes", + "General", +) + # This copy will not include any error codes defined later in the plugins. mypy_error_codes = error_codes.copy() diff --git a/mypy/errors.py b/mypy/errors.py index d6dcd4e49e13..13452b14a237 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -20,7 +20,7 @@ # Show error codes for some note-level messages (these usually appear alone # and not as a comment for a previous error-level message). -SHOW_NOTE_CODES: Final = {codes.ANNOTATION_UNCHECKED} +SHOW_NOTE_CODES: Final = {codes.ANNOTATION_UNCHECKED, codes.DEPRECATED} # Do not add notes with links to error code docs to errors with these codes. # We can tweak this set as we get more experience about what is helpful and what is not. @@ -194,6 +194,9 @@ def on_error(self, file: str, info: ErrorInfo) -> bool: Return True to filter out the error, preventing it from being seen by other ErrorWatcher further down the stack and from being recorded by Errors """ + if info.code == codes.DEPRECATED: + return False + self._has_new_errors = True if isinstance(self._filter, bool): should_filter = self._filter diff --git a/mypy/main.py b/mypy/main.py index f177bb1c2062..0674e3b7e79b 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -799,6 +799,13 @@ def add_invertible_flag( help="Warn about statements or expressions inferred to be unreachable", group=lint_group, ) + add_invertible_flag( + "--report-deprecated-as-error", + default=False, + strict_flag=False, + help="Report importing or using deprecated features as errors instead of notes", + group=lint_group, + ) # Note: this group is intentionally added here even though we don't add # --strict to this group near the end. diff --git a/mypy/nodes.py b/mypy/nodes.py index 39cbee3c8525..c4d23ca7bff8 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -561,17 +561,19 @@ class OverloadedFuncDef(FuncBase, SymbolNode, Statement): Overloaded variants must be consecutive in the source file. """ - __slots__ = ("items", "unanalyzed_items", "impl") + __slots__ = ("items", "unanalyzed_items", "impl", "deprecated") items: list[OverloadPart] unanalyzed_items: list[OverloadPart] impl: OverloadPart | None + deprecated: str | None def __init__(self, items: list[OverloadPart]) -> None: super().__init__() self.items = items self.unanalyzed_items = items.copy() self.impl = None + self.deprecated = None if items: # TODO: figure out how to reliably set end position (we don't know the impl here). self.set_line(items[0].line, items[0].column) @@ -596,6 +598,7 @@ def serialize(self) -> JsonDict: "fullname": self._fullname, "impl": None if self.impl is None else self.impl.serialize(), "flags": get_flags(self, FUNCBASE_FLAGS), + "deprecated": self.deprecated, } @classmethod @@ -615,6 +618,7 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef: res.type = typ res._fullname = data["fullname"] set_flags(res, data["flags"]) + res.deprecated = data["deprecated"] # NOTE: res.info will be set in the fixup phase. return res @@ -781,6 +785,7 @@ class FuncDef(FuncItem, SymbolNode, Statement): # Present only when a function is decorated with @typing.datasclass_transform or similar "dataclass_transform_spec", "docstring", + "deprecated", ) __match_args__ = ("name", "arguments", "type", "body") @@ -810,6 +815,7 @@ def __init__( self.is_mypy_only = False self.dataclass_transform_spec: DataclassTransformSpec | None = None self.docstring: str | None = None + self.deprecated: str | None = None @property def name(self) -> str: @@ -840,6 +846,7 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is None else self.dataclass_transform_spec.serialize() ), + "deprecated": self.deprecated, } @classmethod @@ -867,6 +874,7 @@ def deserialize(cls, data: JsonDict) -> FuncDef: if data["dataclass_transform_spec"] is not None else None ) + ret.deprecated = data["deprecated"] # Leave these uninitialized so that future uses will trigger an error del ret.arguments del ret.max_pos @@ -2942,6 +2950,7 @@ class is generic then it will be a type constructor of higher kind. "self_type", "dataclass_transform_spec", "is_type_check_only", + "deprecated", ) _fullname: str # Fully qualified name @@ -3095,6 +3104,9 @@ class is generic then it will be a type constructor of higher kind. # Is set to `True` when class is decorated with `@typing.type_check_only` is_type_check_only: bool + # The type's deprecation message (in case it is deprecated) + deprecated: str | None + FLAGS: Final = [ "is_abstract", "is_enum", @@ -3152,6 +3164,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None self.self_type = None self.dataclass_transform_spec = None self.is_type_check_only = False + self.deprecated = None def add_type_vars(self) -> None: self.has_type_var_tuple_type = False @@ -3374,6 +3387,7 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is not None else None ), + "deprecated": self.deprecated, } return data @@ -3441,6 +3455,7 @@ def deserialize(cls, data: JsonDict) -> TypeInfo: ti.dataclass_transform_spec = DataclassTransformSpec.deserialize( data["dataclass_transform_spec"] ) + ti.deprecated = data.get("deprecated") return ti diff --git a/mypy/options.py b/mypy/options.py index 56bd92957b41..d315d297e023 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -173,6 +173,9 @@ def __init__(self) -> None: # declared with a precise type self.warn_return_any = False + # Report importing or using deprecated features as errors instead of notes. + self.report_deprecated_as_error = False + # Warn about unused '# type: ignore' comments self.warn_unused_ignores = False diff --git a/mypy/semanal.py b/mypy/semanal.py index 27abf2c1dc4c..37aafc3b2647 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -249,6 +249,7 @@ from mypy.types import ( ASSERT_TYPE_NAMES, DATACLASS_TRANSFORM_NAMES, + DEPRECATED_TYPE_NAMES, FINAL_DECORATOR_NAMES, FINAL_TYPE_NAMES, IMPORTED_REVEAL_TYPE_NAMES, @@ -1260,10 +1261,51 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: return # We know this is an overload def. Infer properties and perform some checks. + self.process_deprecated_overload(defn) self.process_final_in_overload(defn) self.process_static_or_class_method_in_overload(defn) self.process_overload_impl(defn) + def process_deprecated_overload(self, defn: OverloadedFuncDef) -> None: + if defn.is_property: + return + + if isinstance(impl := defn.impl, Decorator) and ( + (deprecated := impl.func.deprecated) is not None + ): + defn.deprecated = deprecated + for item in defn.items: + if isinstance(item, Decorator): + item.func.deprecated = deprecated + + for item in defn.items: + deprecation = False + if isinstance(item, Decorator): + for d in item.decorators: + if deprecation and refers_to_fullname(d, OVERLOAD_NAMES): + self.msg.note("@overload should be placed before @deprecated", d) + elif (deprecated := self.get_deprecated(d)) is not None: + deprecation = True + if isinstance(typ := item.func.type, CallableType): + typestr = f" {typ} " + else: + typestr = " " + item.func.deprecated = ( + f"overload{typestr}of function {defn.fullname} is deprecated: " + f"{deprecated}" + ) + + @staticmethod + def get_deprecated(expression: Expression) -> str | None: + if ( + isinstance(expression, CallExpr) + and refers_to_fullname(expression.callee, DEPRECATED_TYPE_NAMES) + and (len(args := expression.args) >= 1) + and isinstance(deprecated := args[0], StrExpr) + ): + return deprecated.value + return None + def process_overload_impl(self, defn: OverloadedFuncDef) -> None: """Set flags for an overload implementation. @@ -1440,15 +1482,17 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - for i, item in enumerate(items[1:]): if isinstance(item, Decorator): if len(item.decorators) >= 1: - node = item.decorators[0] - if isinstance(node, MemberExpr): - if node.name == "setter": + first_node = item.decorators[0] + if isinstance(first_node, MemberExpr): + if first_node.name == "setter": # The first item represents the entire property. first_item.var.is_settable_property = True # Get abstractness from the original definition. item.func.abstract_status = first_item.func.abstract_status - if node.name == "deleter": + if first_node.name == "deleter": item.func.abstract_status = first_item.func.abstract_status + for other_node in item.decorators[1:]: + other_node.accept(self) else: self.fail( f"Only supported top decorator is @{first_item.func.name}.setter", item @@ -1460,6 +1504,14 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - for i in reversed(deleted_items): del items[i] + for item in items[1:]: + if isinstance(item, Decorator): + for d in item.decorators: + if (deprecated := self.get_deprecated(d)) is not None: + item.func.deprecated = ( + f"function {item.fullname} is deprecated: {deprecated}" + ) + def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> None: if self.is_class_scope(): assert self.type is not None @@ -1663,6 +1715,8 @@ def visit_decorator(self, dec: Decorator) -> None: d.callee, DATACLASS_TRANSFORM_NAMES ): dec.func.dataclass_transform_spec = self.parse_dataclass_transform_spec(d) + elif (deprecated := self.get_deprecated(d)) is not None: + dec.func.deprecated = f"function {dec.fullname} is deprecated: {deprecated}" elif not dec.var.is_property: # We have seen a "non-trivial" decorator before seeing @property, if # we will see a @property later, give an error, as we don't support this. @@ -2099,6 +2153,8 @@ def analyze_class_decorator_common( info.is_final = True elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES): info.is_type_check_only = True + elif (deprecated := self.get_deprecated(decorator)) is not None: + info.deprecated = f"class {defn.fullname} is deprecated: {deprecated}" def clean_up_bases_and_infer_type_variables( self, defn: ClassDef, base_type_exprs: list[Expression], context: Context diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index d5a303128126..131a13ffd62d 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -256,6 +256,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb signature, is_trivial_body, dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None, + node.deprecated if isinstance(node, FuncDef) else None, ) elif isinstance(node, Var): return ("Var", common, snapshot_optional_type(node.type), node.is_final) diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test new file mode 100644 index 000000000000..f587034d8059 --- /dev/null +++ b/test-data/unit/check-deprecated.test @@ -0,0 +1,396 @@ +-- Type checker test cases for reporting deprecations. + + +[case testDeprecatedDisableNotes] +# flags: --disable-error-code=deprecated + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedAsNoteWithErrorCode] +# flags: --show-error-codes + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() # type: ignore[deprecated] +f() # N: function __main__.f is deprecated: use f2 instead [deprecated] + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedAsErrorWithErrorCode] +# flags: --report-deprecated-as-error --show-error-codes + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() # type: ignore[deprecated] +f() # E: function __main__.f is deprecated: use f2 instead [deprecated] + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedFunction] + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f # N: function __main__.f is deprecated: use f2 instead # type: ignore[deprecated] +f(1) # N: function __main__.f is deprecated: use f2 instead \ + # E: Too many arguments for "f" +f[1] # N: function __main__.f is deprecated: use f2 instead \ + # E: Value of type "Callable[[], None]" is not indexable +g = f # N: function __main__.f is deprecated: use f2 instead +g() +t = (f, f, g) # N: function __main__.f is deprecated: use f2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedFunctionDifferentModule] + +import m +import p.s +import m as n +import p.s as ps +from m import f # N: function m.f is deprecated: use f2 instead +from p.s import g # N: function p.s.g is deprecated: use g2 instead +from k import * + +m.f() # N: function m.f is deprecated: use f2 instead +p.s.g() # N: function p.s.g is deprecated: use g2 instead +n.f() # N: function m.f is deprecated: use f2 instead +ps.g() # N: function p.s.g is deprecated: use g2 instead +f() +g() +h() # N: function k.h is deprecated: use h2 instead + +[file m.py] +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +[file p/s.py] +from typing_extensions import deprecated + +@deprecated("use g2 instead") +def g() -> None: ... + +[file k.py] +from typing_extensions import deprecated + +@deprecated("use h2 instead") +def h() -> None: ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClass] + +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +c: C # N: class __main__.C is deprecated: use C2 instead +C() # N: class __main__.C is deprecated: use C2 instead +C.missing() # N: class __main__.C is deprecated: use C2 instead \ + # E: "Type[C]" has no attribute "missing" +C.__init__(c) # N: class __main__.C is deprecated: use C2 instead +C(1) # N: class __main__.C is deprecated: use C2 instead \ + # E: Too many arguments for "C" +D = C # N: class __main__.C is deprecated: use C2 instead +D() +t = (C, C, D) # N: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassDifferentModule] + +import m +import p.s +import m as n +import p.s as ps +from m import C # N: class m.C is deprecated: use C2 instead +from p.s import D # N: class p.s.D is deprecated: use D2 instead +from k import * + +m.C() # N: class m.C is deprecated: use C2 instead +p.s.D() # N: class p.s.D is deprecated: use D2 instead +n.C() # N: class m.C is deprecated: use C2 instead +ps.D() # N: class p.s.D is deprecated: use D2 instead +C() +D() +E() # N: class k.E is deprecated: use E2 instead + +[file m.py] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +[file p/s.py] +from typing_extensions import deprecated + +@deprecated("use D2 instead") +class D: ... + +[file k.py] +from typing_extensions import deprecated + +@deprecated("use E2 instead") +class E: ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInitMethod] + +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: + def __init__(self) -> None: ... + +c: C # N: class __main__.C is deprecated: use C2 instead +C() # N: class __main__.C is deprecated: use C2 instead +C.__init__(c) # N: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedSpecialMethods] + +from typing import Iterator +from typing_extensions import deprecated + +class A: + @deprecated("no A + int") + def __add__(self, v: int) -> None: ... + + @deprecated("no int + A") + def __radd__(self, v: int) -> None: ... + + @deprecated("no A = A + int") + def __iadd__(self, v: int) -> A: ... + + @deprecated("no iteration") + def __iter__(self) -> Iterator[int]: ... + + @deprecated("no in") + def __contains__(self, v: int) -> int: ... + + @deprecated("no integer") + def __int__(self) -> int: ... + + @deprecated("no inversion") + def __invert__(self) -> A: ... + +class B: + @deprecated("still no in") + def __contains__(self, v: int) -> int: ... + +a = A() +b = B() +a + 1 # N: function __main__.A.__add__ is deprecated: no A + int +1 + a # N: function __main__.A.__radd__ is deprecated: no int + A +a += 1 # N: function __main__.A.__iadd__ is deprecated: no A = A + int +for i in a: # N: function __main__.A.__iter__ is deprecated: no iteration + reveal_type(i) # N: Revealed type is "builtins.int" +1 in a # N: function __main__.A.__contains__ is deprecated: no in +1 in b # N: function __main__.B.__contains__ is deprecated: still no in +~a # N: function __main__.A.__invert__ is deprecated: no inversion + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedSpecialMethods] + +from typing import Iterator, Union +from typing_extensions import deprecated, overload + +class A: + @overload + @deprecated("no A + int") + def __add__(self, v: int) -> None: ... + @overload + def __add__(self, v: str) -> None: ... + def __add__(self, v: Union[int, str]) -> None: ... + + @overload + def __radd__(self, v: int) -> None: ... + @overload + @deprecated("no str + A") + def __radd__(self, v: str) -> None: ... + def __radd__(self, v: Union[int, str]) -> None: ... + + @overload + def __iadd__(self, v: int) -> A: ... + @overload + def __iadd__(self, v: str) -> A: ... + @deprecated("no A += Any") + def __iadd__(self, v: Union[int, str]) -> A: ... + +a = A() +a + 1 # N: overload def (__main__.A, builtins.int) of function __main__.A.__add__ is deprecated: no A + int +a + "x" +1 + a +"x" + a # N: overload def (__main__.A, builtins.str) of function __main__.A.__radd__ is deprecated: no str + A +a += 1 # N: function __main__.A.__iadd__ is deprecated: no A += Any +a += "x" # N: function __main__.A.__iadd__ is deprecated: no A += Any + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedMethod] + +from typing_extensions import deprecated + +class C: + @deprecated("use g instead") + def f(self) -> None: ... + + def g(self) -> None: ... + + @staticmethod + @deprecated("use g instead") + def h() -> None: ... + + @deprecated("use g instead") + @staticmethod + def k() -> None: ... + +C.f # N: function __main__.C.f is deprecated: use g instead +C().f # N: function __main__.C.f is deprecated: use g instead +C().f() # N: function __main__.C.f is deprecated: use g instead +C().f(1) # N: function __main__.C.f is deprecated: use g instead \ + # E: Too many arguments for "f" of "C" +f = C().f # N: function __main__.C.f is deprecated: use g instead +f() +t = (C.f, C.f, C.g) # N: function __main__.C.f is deprecated: use g instead + +C().g() +C().h() # N: function __main__.C.h is deprecated: use g instead +C().k() # N: function __main__.C.k is deprecated: use g instead + +[builtins fixtures/callable.pyi] + + +[case testDeprecatedClassWithDeprecatedMethod] + +from typing_extensions import deprecated + +@deprecated("use D instead") +class C: + @deprecated("use g instead") + def f(self) -> None: ... + def g(self) -> None: ... + +C().f() # N: class __main__.C is deprecated: use D instead \ + # N: function __main__.C.f is deprecated: use g instead +C().g() # N: class __main__.C is deprecated: use D instead + +[builtins fixtures/callable.pyi] + + +[case testDeprecatedProperty] + +from typing_extensions import deprecated + +class C: + @property + @deprecated("use f2 instead") + def f(self) -> int: ... + + @property + def g(self) -> int: ... + @g.setter + @deprecated("use g2 instead") + def g(self, v: int) -> None: ... + + +C.f # N: function __main__.C.f is deprecated: use f2 instead +C().f # N: function __main__.C.f is deprecated: use f2 instead +C().f() # N: function __main__.C.f is deprecated: use f2 instead \ + # E: "int" not callable +C().f = 1 # N: function __main__.C.f is deprecated: use f2 instead \ + # E: Property "f" defined in "C" is read-only + + +C.g +C().g +C().g = 1 # N: function __main__.C.g is deprecated: use g2 instead +C().g = "x" # N: function __main__.C.g is deprecated: use g2 instead \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + +[builtins fixtures/property.pyi] + + +[case testDeprecatedOverloadedFunction] + +from typing import Union +from typing_extensions import deprecated, overload + +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +@deprecated("use f2 instead") +def f(x: Union[int, str]) -> Union[int, str]: ... + +f # N: function __main__.f is deprecated: use f2 instead +f(1) # N: function __main__.f is deprecated: use f2 instead +f("x") # N: function __main__.f is deprecated: use f2 instead +f(1.0) # N: function __main__.f is deprecated: use f2 instead \ + # E: No overload variant of "f" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def f(x: int) -> int \ + # N: def f(x: str) -> str + +@overload +@deprecated("work with str instead") +def g(x: int) -> int: ... +@overload +def g(x: str) -> str: ... +def g(x: Union[int, str]) -> Union[int, str]: ... + +g +g(1) # N: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead +g("x") +g(1.0) # E: No overload variant of "g" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def g(x: int) -> int \ + # N: def g(x: str) -> str + +@overload +def h(x: int) -> int: ... +@deprecated("work with int instead") +@overload # N: @overload should be placed before @deprecated +def h(x: str) -> str: ... +def h(x: Union[int, str]) -> Union[int, str]: ... + +h +h(1) +h("x") # N: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead +h(1.0) # E: No overload variant of "h" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def h(x: int) -> int \ + # N: def h(x: str) -> str + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 15e47ff296ea..d4c61cbf1d5b 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10671,3 +10671,237 @@ class f: ... == main:2: error: Incompatible types in assignment (expression has type "f", variable has type "int") main:4: error: Incompatible types in assignment (expression has type "f", variable has type "int") + + +[case testDeprecatedAddKeepChangeAndRemoveFunctionDeprecation] +from a import f +f() +import a +a.f() + +[file a.py] +def f() -> None: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.3] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.4] +from typing_extensions import deprecated +@deprecated("use f3 instead") +def f() -> None: ... + +[file a.py.5] +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead +== +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead +== +main:1: note: function a.f is deprecated: use f3 instead +main:4: note: function a.f is deprecated: use f3 instead +== + + +[case testDeprecatedRemoveFunctionDeprecation] +from a import f +f() +import a +a.f() + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.2] +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead +== + +[case testDeprecatedKeepFunctionDeprecation] +from a import f +f() +import a +a.f() + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead +== +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead + + +[case testDeprecatedAddFunctionDeprecationIndirectImport] +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: note: function a.f is deprecated: use f2 instead +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead + + +[case testDeprecatedChangeFunctionDeprecationIndirectImport] +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f1 instead") +def f() -> int: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: note: function a.f is deprecated: use f1 instead +main:1: note: function a.f is deprecated: use f1 instead +main:4: note: function a.f is deprecated: use f1 instead +== +b.py:1: note: function a.f is deprecated: use f2 instead +main:1: note: function a.f is deprecated: use f2 instead +main:4: note: function a.f is deprecated: use f2 instead + +[case testDeprecatedRemoveFunctionDeprecationIndirectImport] +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f1 instead") +def f() -> int: ... + +[file a.py.2] +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: note: function a.f is deprecated: use f1 instead +main:1: note: function a.f is deprecated: use f1 instead +main:4: note: function a.f is deprecated: use f1 instead +== + +[case testDeprecatedFunctionAlreadyDecorated1-only_when_cache] +from b import f +x: str = f() +import b +y: str = b.f() + +[file b.py] +from a import f + +[file a.py] +from typing import Callable + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[file a.py.2] +from typing import Callable +from typing_extensions import deprecated + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@deprecated("deprecated decorated function") +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: note: function a.f is deprecated: deprecated decorated function +main:1: note: function a.f is deprecated: deprecated decorated function +main:4: note: function a.f is deprecated: deprecated decorated function + + +[case testDeprecatedFunctionAlreadyDecorated2-only_when_nocache] +from b import f +x: str = f() +import b +y: str = b.f() + +[file b.py] +from a import f + +[file a.py] +from typing import Callable + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[file a.py.2] +from typing import Callable +from typing_extensions import deprecated + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@deprecated("deprecated decorated function") +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:1: note: function a.f is deprecated: deprecated decorated function +main:4: note: function a.f is deprecated: deprecated decorated function +b.py:1: note: function a.f is deprecated: deprecated decorated function diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 89f01bff963e..174ba8e98c3f 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -954,7 +954,7 @@ a = [] # type: List[Dict[str, str]] sorted(a, key=lambda y: y['']) [case testAbstractProperty] -from abc import abstractproperty, ABCMeta +from abc import abstractproperty, ABCMeta # type: ignore[deprecated] class A(metaclass=ABCMeta): @abstractproperty def x(self) -> int: pass From 7255ece5b0f2ab63baa4b8b9cd1a16d90cbedc1c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 8 Oct 2024 09:47:31 +0100 Subject: [PATCH 0788/1617] Add Python 3.13 to classifiers (#17891) Mypy now supports 3.13 (though not all features yet). --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 160e2b054b0e..a50afde4ce6b 100644 --- a/setup.py +++ b/setup.py @@ -190,6 +190,7 @@ def run(self): "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Software Development", "Typing :: Typed", ] From 82d04257eced45a2692f94999026497321619134 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 8 Oct 2024 10:12:50 +0100 Subject: [PATCH 0789/1617] Add changelog for mypy 1.12 (#17889) Related to #17815. --------- Co-authored-by: Jelle Zijlstra --- CHANGELOG.md | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9632cb39a8b1..c664121f097d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,292 @@ ## Next release +## Mypy 1.12 + +We’ve just uploaded mypy 1.12 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type +checker for Python. This release includes new features, performance improvements and bug fixes. +You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +### Support Python 3.12 Syntax for Generics (PEP 695) + +Support for the new type parameter syntax introduced in Python 3.12 is now enabled by default, +documented, and no longer experimental. It was available through a feature flag in +mypy 1.11 as an experimental feature. + +This example demonstrates the new syntax: + +```python +# Generic function +def f[T](x: T) -> T: ... + +reveal_type(f(1)) # Revealed type is 'int' + +# Generic class +class C[T]: + def __init__(self, x: T) -> None: + self.x = x + +c = C('a') +reveal_type(c.x) # Revealed type is 'str' + +# Type alias +type A[T] = C[list[T]] +``` + +For more information, refer to the [documentation](https://mypy.readthedocs.io/en/latest/generics.html). + +These improvements are included: + + * Document Python 3.12 type parameter syntax (Jukka Lehtosalo, PR [17816](https://github.com/python/mypy/pull/17816)) + * Further documentation updates (Jukka Lehtosalo, PR [17826](https://github.com/python/mypy/pull/17826)) + * Allow Self return types with contravariance (Jukka Lehtosalo, PR [17786](https://github.com/python/mypy/pull/17786)) + * Enable new type parameter syntax by default (Jukka Lehtosalo, PR [17798](https://github.com/python/mypy/pull/17798)) + * Generate error if new-style type alias used as base class (Jukka Lehtosalo, PR [17789](https://github.com/python/mypy/pull/17789)) + * Inherit variance if base class has explicit variance (Jukka Lehtosalo, PR [17787](https://github.com/python/mypy/pull/17787)) + * Fix crash on invalid type var reference (Jukka Lehtosalo, PR [17788](https://github.com/python/mypy/pull/17788)) + * Fix covariance of frozen dataclasses (Jukka Lehtosalo, PR [17783](https://github.com/python/mypy/pull/17783)) + * Allow covariance with attribute that has "`_`" name prefix (Jukka Lehtosalo, PR [17782](https://github.com/python/mypy/pull/17782)) + * Support `Annotated[...]` in new-style type aliases (Jukka Lehtosalo, PR [17777](https://github.com/python/mypy/pull/17777)) + * Fix nested generic classes (Jukka Lehtosalo, PR [17776](https://github.com/python/mypy/pull/17776)) + * Add detection and error reporting for the use of incorrect expressions within the scope of a type parameter and a type alias (Kirill Podoprigora, PR [17560](https://github.com/python/mypy/pull/17560)) + +### Basic Support for Python 3.13 + +This release adds partial support for Python 3.13 features and compiled binaries for +Python 3.13. Mypyc now also supports Python 3.13. + +In particular, these features are supported: + * Various new stdlib features and changes (through typeshed stub improvements) + * `typing.ReadOnly` (see below for more) + * `typing.TypeIs` (added in mypy 1.10, [PEP 742](https://peps.python.org/pep-0742/)) + * Type parameter defaults when using the legacy syntax ([PEP 696](https://peps.python.org/pep-0696/)) + +These features are not supported yet: + * `warnings.deprecated` ([PEP 702](https://peps.python.org/pep-0702/)) + * Type parameter defaults when using Python 3.12 type parameter syntax + +### Mypyc Support for Python 3.13 + +Mypyc now supports Python 3.13. This was contributed by Marc Mueller, with additional +fixes by Jukka Lehtosalo. Free threaded Python 3.13 builds are not supported yet. + +List of changes: + + * Add additional includes for Python 3.13 (Marc Mueller, PR [17506](https://github.com/python/mypy/pull/17506)) + * Add another include for Python 3.13 (Marc Mueller, PR [17509](https://github.com/python/mypy/pull/17509)) + * Fix ManagedDict functions for Python 3.13 (Marc Mueller, PR [17507](https://github.com/python/mypy/pull/17507)) + * Update mypyc test output for Python 3.13 (Marc Mueller, PR [17508](https://github.com/python/mypy/pull/17508)) + * Fix `PyUnicode` functions for Python 3.13 (Marc Mueller, PR [17504](https://github.com/python/mypy/pull/17504)) + * Fix `_PyObject_LookupAttrId` for Python 3.13 (Marc Mueller, PR [17505](https://github.com/python/mypy/pull/17505)) + * Fix `_PyList_Extend` for Python 3.13 (Marc Mueller, PR [17503](https://github.com/python/mypy/pull/17503)) + * Fix `gen_is_coroutine` for Python 3.13 (Marc Mueller, PR [17501](https://github.com/python/mypy/pull/17501)) + * Fix `_PyObject_FastCall` for Python 3.13 (Marc Mueller, PR [17502](https://github.com/python/mypy/pull/17502)) + * Avoid uses of `_PyObject_CallMethodOneArg` on 3.13 (Jukka Lehtosalo, PR [17526](https://github.com/python/mypy/pull/17526)) + * Don't rely on `_PyType_CalculateMetaclass` on 3.13 (Jukka Lehtosalo, PR [17525](https://github.com/python/mypy/pull/17525)) + * Don't use `_PyUnicode_FastCopyCharacters` on 3.13 (Jukka Lehtosalo, PR [17524](https://github.com/python/mypy/pull/17524)) + * Don't use `_PyUnicode_EQ` on 3.13, as it's no longer exported (Jukka Lehtosalo, PR [17523](https://github.com/python/mypy/pull/17523)) + +### Inferring Unions for Conditional Expressions + +Mypy now always tries to infer a union type for a conditional expression if left and right +operand types are different. This results in more precise inferred types and lets mypy detect +more issues. Example: + +```python +s = "foo" if cond() else 1 +# Type of "s" is now "str | int" (it used to be "object") +``` + +Notably, if one of the operands has type `Any`, the type of a conditional expression is +now ` | Any`. Previously the inferred type was just `Any`. The new type essentially +indicates that the value can be of type ``, and potentially of some (unknown) type. +Most operations performed on the result must also be valid for ``. +Example where this is relevant: + +```python +from typing import Any + +def func(a: Any, b: bool) -> None: + x = a if b else None + # Type of x is "Any | None" + print(x.y) # Error: None has no attribute "y" +``` + +This feature was contributed by Ivan Levkivskyi (PR [17427](https://github.com/python/mypy/pull/17427)). + +### ReadOnly Support for TypedDict (PEP 705) + +You can now use `typing.ReadOnly` to specity TypedDict items as +read-only ([PEP 705](https://peps.python.org/pep-0705/)): + +```python +from typing import TypedDict + +# Or "from typing ..." on Python 3.13 +from typing_extensions import ReadOnly + +class TD(TypedDict): + a: int + b: ReadOnly[int] + +d: TD = {"a": 1, "b": 2} +d["a"] = 3 # OK +d["b"] = 5 # Error: "b" is ReadOnly +``` + +This feature was contributed by Nikita Sobolev (PR [17644](https://github.com/python/mypy/pull/17644)). + +### Python 3.8 End of Life Approaching + +We are planning to drop support for Python 3.8 in the next mypy feature release or the +one after that. Python 3.8 reaches end of life in October 2024. + +### Planned Changes to Defaults + +We are planning to enable `--local-partial-types` by default in mypy 2.0. This will +often require at least minor code changes. This option is implicitly enabled by mypy +daemon, so this makes the behavior of daemon and non-daemon modes consistent. + +We recommend that mypy users start using local partial types soon (or to explicitly disable +them) to prepare for the change. + +This can also be configured in a mypy configuration file: + +``` +local_partial_types = True +``` + +For more information, refer to the +[documentation](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-local-partial-types). + +### Documentation Updates + +Mypy documentation now uses modern syntax variants and imports in many examples. Some +examples no longer work on Python 3.8, which is the earliest Python version that mypy supports. + +Notably, `Iterable` and other protocols/ABCs are imported from `collections.abc` instead of +`typing`: +```python +from collections.abc import Iterable, Callable +``` + +Examples also avoid the upper-case aliases to built-in types: `list[str]` is used instead +of `List[str]`. The `X | Y` union type syntax introduced in Python 3.10 is also now prevalent. + +List of documentation updates: + + * Document `--output=json` CLI option (Edgar Ramírez Mondragón, PR [17611](https://github.com/python/mypy/pull/17611)) + * Update various references to deprecated type aliases in docs (Jukka Lehtosalo, PR [17829](https://github.com/python/mypy/pull/17829)) + * Make "X | Y" union syntax more prominent in documentation (Jukka Lehtosalo, PR [17835](https://github.com/python/mypy/pull/17835)) + * Discuss upper bounds before self types in documentation (Jukka Lehtosalo, PR [17827](https://github.com/python/mypy/pull/17827)) + * Make changelog visible in mypy documentation (quinn-sasha, PR [17742](https://github.com/python/mypy/pull/17742)) + * List all incomplete features in `--enable-incomplete-feature` docs (sobolevn, PR [17633](https://github.com/python/mypy/pull/17633)) + * Remove the explicit setting of a pygments theme (Pradyun Gedam, PR [17571](https://github.com/python/mypy/pull/17571)) + +### Experimental Inline TypedDict Syntax + +Mypy now supports a non-standard, experimental syntax for defining anonymous TypedDicts. +Example: + +```python +def func(n: str, y: int) -> {"name": str, "year": int}: + return {"name": n, "year": y} +``` + +The feature is disabled by default. Use `--enable-incomplete-feature=InlineTypedDict` to +enable it. *We might remove this feature in a future release.* + +This feature was contributed by Ivan Levkivskyi (PR [17457](https://github.com/python/mypy/pull/17457)). + +### Stubgen Improvements + + * Fix crash on literal class-level keywords (sobolevn, PR [17663](https://github.com/python/mypy/pull/17663)) + * Stubgen add `--version` (sobolevn, PR [17662](https://github.com/python/mypy/pull/17662)) + * Fix `stubgen --no-analysis/--parse-only` docs (sobolevn, PR [17632](https://github.com/python/mypy/pull/17632)) + * Include keyword only args when generating signatures in stubgenc (Eric Mark Martin, PR [17448](https://github.com/python/mypy/pull/17448)) + * Add support for detecting `Literal` types when extracting types from docstrings (Michael Carlstrom, PR [17441](https://github.com/python/mypy/pull/17441)) + * Use `Generator` type var defaults (Sebastian Rittau, PR [17670](https://github.com/python/mypy/pull/17670)) + +### Stubtest Improvements + * Add support for `cached_property` (Ali Hamdan, PR [17626](https://github.com/python/mypy/pull/17626)) + * Add `enable_incomplete_feature` validation to `stubtest` (sobolevn, PR [17635](https://github.com/python/mypy/pull/17635)) + * Fix error code handling in `stubtest` with `--mypy-config-file` (sobolevn, PR [17629](https://github.com/python/mypy/pull/17629)) + +### Other Notables Fixes and Improvements + + * Report error if using unsupported type parameter defaults (Jukka Lehtosalo, PR [17876](https://github.com/python/mypy/pull/17876)) + * Fix re-processing cross-reference in mypy daemon when node kind changes (Ivan Levkivskyi, PR [17883](https://github.com/python/mypy/pull/17883)) + * Don't use equality to narrow when value is IntEnum/StrEnum (Jukka Lehtosalo, PR [17866](https://github.com/python/mypy/pull/17866)) + * Don't consider None vs IntEnum comparison ambiguous (Jukka Lehtosalo, PR [17877](https://github.com/python/mypy/pull/17877)) + * Fix narrowing of IntEnum and StrEnum types (Jukka Lehtosalo, PR [17874](https://github.com/python/mypy/pull/17874)) + * Filter overload items based on self type during type inference (Jukka Lehtosalo, PR [17873](https://github.com/python/mypy/pull/17873)) + * Enable negative narrowing of union TypeVar upper bounds (Brian Schubert, PR [17850](https://github.com/python/mypy/pull/17850)) + * Fix issue with member expression formatting (Brian Schubert, PR [17848](https://github.com/python/mypy/pull/17848)) + * Avoid type size explosion when expanding types (Jukka Lehtosalo, PR [17842](https://github.com/python/mypy/pull/17842)) + * Fix negative narrowing of tuples in match statement (Brian Schubert, PR [17817](https://github.com/python/mypy/pull/17817)) + * Narrow falsey str/bytes/int to literal type (Brian Schubert, PR [17818](https://github.com/python/mypy/pull/17818)) + * Test against latest Python 3.13, make testing 3.14 easy (Shantanu, PR [17812](https://github.com/python/mypy/pull/17812)) + * Reject ParamSpec-typed callables calls with insufficient arguments (Stanislav Terliakov, PR [17323](https://github.com/python/mypy/pull/17323)) + * Fix crash when passing too many type arguments to generic base class accepting single ParamSpec (Brian Schubert, PR [17770](https://github.com/python/mypy/pull/17770)) + * Fix TypeVar upper bounds sometimes not being displayed in pretty callables (Brian Schubert, PR [17802](https://github.com/python/mypy/pull/17802)) + * Added error code for overlapping function signatures (Katrina Connors, PR [17597](https://github.com/python/mypy/pull/17597)) + * Check for `truthy-bool` in `not ...` unary expressions (sobolevn, PR [17773](https://github.com/python/mypy/pull/17773)) + * Add missing lines-covered and lines-valid attributes (Soubhik Kumar Mitra, PR [17738](https://github.com/python/mypy/pull/17738)) + * Fix another crash scenario with recursive tuple types (Ivan Levkivskyi, PR [17708](https://github.com/python/mypy/pull/17708)) + * Resolve TypeVar upper bounds in `functools.partial` (Shantanu, PR [17660](https://github.com/python/mypy/pull/17660)) + * Always reset binder when checking deferred nodes (Ivan Levkivskyi, PR [17643](https://github.com/python/mypy/pull/17643)) + * Fix crash on a callable attribute with single unpack (Ivan Levkivskyi, PR [17641](https://github.com/python/mypy/pull/17641)) + * Fix mismatched signature between checker plugin API and implementation (bzoracler, PR [17343](https://github.com/python/mypy/pull/17343)) + * Indexing a type also produces a GenericAlias (Shantanu, PR [17546](https://github.com/python/mypy/pull/17546)) + * Fix crash on self-type in callable protocol (Ivan Levkivskyi, PR [17499](https://github.com/python/mypy/pull/17499)) + * Fix crash on NamedTuple with method and error in function (Ivan Levkivskyi, PR [17498](https://github.com/python/mypy/pull/17498)) + * Add `__replace__` for dataclasses in 3.13 (Max Muoto, PR [17469](https://github.com/python/mypy/pull/17469)) + * Fix help message for `--no-namespace-packages` (Raphael Krupinski, PR [17472](https://github.com/python/mypy/pull/17472)) + * Fix typechecking for async generators (Danny Yang, PR [17452](https://github.com/python/mypy/pull/17452)) + * Fix strict optional handling in attrs plugin (Ivan Levkivskyi, PR [17451](https://github.com/python/mypy/pull/17451)) + * Allow mixing ParamSpec and TypeVarTuple in Generic (Ivan Levkivskyi, PR [17450](https://github.com/python/mypy/pull/17450)) + +### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=91a58b07cdd807b1d965e04ba85af2adab8bf924+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Ali Hamdan +- Anders Kaseorg +- Bénédikt Tran +- Brian Schubert +- bzoracler +- Danny Yang +- Edgar Ramírez Mondragón +- Eric Mark Martin +- InSync +- Ivan Levkivskyi +- Jordandev678 +- Katrina Connors +- Kirill Podoprigora +- Marc Mueller +- Max Muoto +- Max Murin +- Michael Carlstrom +- Michael I Chen +- Pradyun Gedam +- quinn-sasha +- Raphael Krupinski +- Sebastian Rittau +- Shantanu +- sobolevn +- Soubhik Kumar Mitra +- Stanislav Terliakov +- wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + ## Mypy 1.11 From 1d4b45bd576d7e34ea7a471646dc1ed0aabe3d48 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 8 Oct 2024 12:25:44 -0700 Subject: [PATCH 0790/1617] stubtest: Stop telling people to use double underscores (#17897) Python 3.7 is long dead. --- mypy/stubtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index c54f83f33b00..65730828ad9f 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -133,7 +133,7 @@ def is_missing_stub(self) -> bool: def is_positional_only_related(self) -> bool: """Whether or not the error is for something being (or not being) positional-only.""" # TODO: This is hacky, use error codes or something more resilient - return "leading double underscore" in self.message + return "should be positional" in self.message def get_description(self, concise: bool = False) -> str: """Returns a description of the error. @@ -909,7 +909,7 @@ def _verify_signature( ): yield ( f'stub argument "{stub_arg.variable.name}" should be positional-only ' - f'(rename with a leading double underscore, i.e. "__{runtime_arg.name}")' + f'(add "/", e.g. "{runtime_arg.name}, /")' ) if ( runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY @@ -919,7 +919,7 @@ def _verify_signature( ): yield ( f'stub argument "{stub_arg.variable.name}" should be positional or keyword ' - "(remove leading double underscore)" + '(remove "/")' ) # Check unmatched positional args From b0db694cc7c6c359a9ce7b036727d0a6f3336f58 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 8 Oct 2024 23:47:41 +0200 Subject: [PATCH 0791/1617] Use 3.13.0 for ci tests (#17900) --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39fbb14bd3b7..296f6fde1523 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,8 +71,8 @@ jobs: tox_extra_args: "-n 4" test_mypyc: true - - name: Test suite with py313-dev-ubuntu, mypyc-compiled - python: '3.13-dev' + - name: Test suite with py313-ubuntu, mypyc-compiled + python: '3.13' arch: x64 os: ubuntu-latest toxenv: py From 621c7aa7088faa19aa6588ab7ad60561ec2fce53 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:56:59 -0700 Subject: [PATCH 0792/1617] Improvements to functools.partial of types (#17898) Fixes https://github.com/python/mypy/issues/17556 , fixes https://github.com/python/mypy/issues/17659 --- mypy/checker.py | 8 ++--- test-data/unit/check-functools.test | 36 +++++++++++++++++++ test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4bbd49cd7198..e0c2578df095 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -686,10 +686,10 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab if isinstance(inner_type, TypeVarLikeType): inner_type = get_proper_type(inner_type.upper_bound) if isinstance(inner_type, TypeType): - if isinstance(inner_type.item, Instance): - inner_type = expand_type_by_instance( - type_object_type(inner_type.item.type, self.named_type), inner_type.item - ) + inner_type = get_proper_type( + self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + ) + if isinstance(inner_type, CallableType): outer_type = inner_type elif isinstance(inner_type, Instance): diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 9f8fbd42440b..50de3789ebd2 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -557,3 +557,39 @@ def bar(f: S) -> S: g = functools.partial(f, "foo") return f [builtins fixtures/primitives.pyi] + + +[case testFunctoolsPartialAbstractType] +# flags: --python-version 3.9 +from abc import ABC, abstractmethod +from functools import partial + +class A(ABC): + def __init__(self) -> None: ... + @abstractmethod + def method(self) -> None: ... + +def f1(cls: type[A]) -> None: + cls() + partial_cls = partial(cls) + partial_cls() + +def f2() -> None: + A() # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls = partial(A) # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method" +[builtins fixtures/tuple.pyi] + + +[case testFunctoolsPartialSelfType] +from functools import partial +from typing_extensions import Self + +class A: + def __init__(self, ts: float, msg: str) -> None: ... + + @classmethod + def from_msg(cls, msg: str) -> Self: + factory = partial(cls, ts=0) + return factory(msg=msg) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index d9d7067efe0f..cb054b0e6b4f 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -43,6 +43,8 @@ Required: _SpecialForm NotRequired: _SpecialForm ReadOnly: _SpecialForm +Self: _SpecialForm + @final class TypeAliasType: def __init__( From 9e24b56e86fe10b2d89631854770bbd139bcf55d Mon Sep 17 00:00:00 2001 From: Chelsea Durazo Date: Wed, 9 Oct 2024 02:05:51 -0700 Subject: [PATCH 0793/1617] documentation for TypeIs (#17821) Fixes #17156. As requested in the issue, added documentation for the desired behaviour of TypeIs. --- docs/source/type_narrowing.rst | 166 +++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 231a7edccfe7..230c40b30894 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -389,3 +389,169 @@ or rewrite the function to be slightly more verbose: elif b is not None: return b return C() + + +.. _typeis: + +TypeIs +------ + +Mypy supports TypeIs (:pep:`742`). + +A `TypeIs narrowing function `_ +allows you to define custom type checks that can narrow the type of a variable +in `both the if and else _` +branches of a conditional, similar to how the built-in isinstance() function works. + +TypeIs is new in Python 3.13 — for use in older Python versions, use the backport +from `typing_extensions _` + +Consider the following example using TypeIs: + +.. code-block:: python + + from typing import TypeIs + + def is_str(x: object) -> TypeIs[str]: + return isinstance(x, str) + + def process(x: int | str) -> None: + if is_str(x): + reveal_type(x) # Revealed type is 'str' + print(x.upper()) # Valid: x is str + else: + reveal_type(x) # Revealed type is 'int' + print(x + 1) # Valid: x is int + +In this example, the function is_str is a type narrowing function +that returns TypeIs[str]. When used in an if statement, x is narrowed +to str in the if branch and to int in the else branch. + +Key points: + + +- The function must accept at least one positional argument. + +- The return type is annotated as ``TypeIs[T]``, where ``T`` is the type you + want to narrow to. + +- The function must return a ``bool`` value. + +- In the ``if`` branch (when the function returns ``True``), the type of the + argument is narrowed to the intersection of its original type and ``T``. + +- In the ``else`` branch (when the function returns ``False``), the type of + the argument is narrowed to the intersection of its original type and the + complement of ``T``. + + +TypeIs vs TypeGuard +~~~~~~~~~~~~~~~~~~~ + +While both TypeIs and TypeGuard allow you to define custom type narrowing +functions, they differ in important ways: + +- **Type narrowing behavior**: TypeIs narrows the type in both the if and else branches, + whereas TypeGuard narrows only in the if branch. + +- **Compatibility requirement**: TypeIs requires that the narrowed type T be + compatible with the input type of the function. TypeGuard does not have this restriction. + +- **Type inference**: With TypeIs, the type checker may infer a more precise type by + combining existing type information with T. + +Here's an example demonstrating the behavior with TypeGuard: + +.. code-block:: python + + from typing import TypeGuard, reveal_type + + def is_str(x: object) -> TypeGuard[str]: + return isinstance(x, str) + + def process(x: int | str) -> None: + if is_str(x): + reveal_type(x) # Revealed type is "builtins.str" + print(x.upper()) # ok: x is str + else: + reveal_type(x) # Revealed type is "Union[builtins.int, builtins.str]" + print(x + 1) # ERROR: Unsupported operand types for + ("str" and "int") [operator] + +Generic TypeIs +~~~~~~~~~~~~~~ + +``TypeIs`` functions can also work with generic types: + +.. code-block:: python + + from typing import TypeVar, TypeIs + + T = TypeVar('T') + + def is_two_element_tuple(val: tuple[T, ...]) -> TypeIs[tuple[T, T]]: + return len(val) == 2 + + def process(names: tuple[str, ...]) -> None: + if is_two_element_tuple(names): + reveal_type(names) # Revealed type is 'tuple[str, str]' + else: + reveal_type(names) # Revealed type is 'tuple[str, ...]' + + +TypeIs with Additional Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TypeIs functions can accept additional parameters beyond the first. +The type narrowing applies only to the first argument. + +.. code-block:: python + + from typing import Any, TypeVar, reveal_type, TypeIs + + T = TypeVar('T') + + def is_instance_of(val: Any, typ: type[T]) -> TypeIs[T]: + return isinstance(val, typ) + + def process(x: Any) -> None: + if is_instance_of(x, int): + reveal_type(x) # Revealed type is 'int' + print(x + 1) # ok + else: + reveal_type(x) # Revealed type is 'Any' + +TypeIs in Methods +~~~~~~~~~~~~~~~~~ + +A method can also serve as a ``TypeIs`` function. Note that in instance or +class methods, the type narrowing applies to the second parameter +(after ``self`` or ``cls``). + +.. code-block:: python + + class Validator: + def is_valid(self, instance: object) -> TypeIs[str]: + return isinstance(instance, str) + + def process(self, to_validate: object) -> None: + if Validator().is_valid(to_validate): + reveal_type(to_validate) # Revealed type is 'str' + print(to_validate.upper()) # ok: to_validate is str + + +Assignment Expressions with TypeIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the assignment expression operator ``:=`` with ``TypeIs`` to create a new variable and narrow its type simultaneously. + +.. code-block:: python + + from typing import TypeIs, reveal_type + + def is_float(x: object) -> TypeIs[float]: + return isinstance(x, float) + + def main(a: object) -> None: + if is_float(x := a): + reveal_type(x) # Revealed type is 'float' + # x is narrowed to float in this block + print(x + 1.0) From e126ba02fe32918ada61cf191b8a23047281ec9f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 9 Oct 2024 14:48:09 +0100 Subject: [PATCH 0794/1617] Make ReadOnly TypedDict items covariant (#17904) Fixes #17901. --- mypy/subtypes.py | 23 +++++++++------- test-data/unit/check-typeddict.test | 41 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 787d5cb89b0a..a63db93fd9cb 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -892,15 +892,20 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: return False for name, l, r in left.zip(right): # TODO: should we pass on the full subtype_context here and below? - if self.proper_subtype: - check = is_same_type(l, r) + right_readonly = name in right.readonly_keys + if not right_readonly: + if self.proper_subtype: + check = is_same_type(l, r) + else: + check = is_equivalent( + l, + r, + ignore_type_params=self.subtype_context.ignore_type_params, + options=self.options, + ) else: - check = is_equivalent( - l, - r, - ignore_type_params=self.subtype_context.ignore_type_params, - options=self.options, - ) + # Read-only items behave covariantly + check = self._is_subtype(l, r) if not check: return False # Non-required key is not compatible with a required key since @@ -917,7 +922,7 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: # Readonly fields check: # # A = TypedDict('A', {'x': ReadOnly[int]}) - # B = TypedDict('A', {'x': int}) + # B = TypedDict('B', {'x': int}) # def reset_x(b: B) -> None: # b['x'] = 0 # diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index e1797421636e..affa472bb640 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3988,3 +3988,44 @@ class TP(TypedDict): k: ReadOnly # E: "ReadOnly[]" must have exactly one type argument [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCovariant] +from typing import ReadOnly, TypedDict, Union + +class A(TypedDict): + a: ReadOnly[Union[int, str]] + +class A2(TypedDict): + a: ReadOnly[int] + +class B(TypedDict): + a: int + +class B2(TypedDict): + a: Union[int, str] + +class B3(TypedDict): + a: int + +def fa(a: A) -> None: ... +def fa2(a: A2) -> None: ... + +b: B = {"a": 1} +fa(b) +fa2(b) +b2: B2 = {"a": 1} +fa(b2) +fa2(b2) # E: Argument 1 to "fa2" has incompatible type "B2"; expected "A2" + +class C(TypedDict): + a: ReadOnly[Union[int, str]] + b: Union[str, bytes] + +class D(TypedDict): + a: int + b: str + +d: D = {"a": 1, "b": "x"} +c: C = d # E: Incompatible types in assignment (expression has type "D", variable has type "C") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From a1fa1c483febf8907fdb71f04e9e9a1d6be7809a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 9 Oct 2024 14:57:09 +0100 Subject: [PATCH 0795/1617] Document ReadOnly (PEP 705) (#17905) Add basic documentation for mypy 1.12 release. --------- Co-authored-by: sobolevn --- docs/source/typed_dict.rst | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index e69b3895c668..bbb10a12abe8 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -236,6 +236,46 @@ another ``TypedDict`` if all required keys in the other ``TypedDict`` are requir first ``TypedDict``, and all non-required keys of the other ``TypedDict`` are also non-required keys in the first ``TypedDict``. +Read-only items +--------------- + +You can use ``typing.ReadOnly``, introduced in Python 3.13, or +``typing_extensions.ReadOnly`` to mark TypedDict items as read-only (:pep:`705`): + +.. code-block:: python + + from typing import TypedDict + + # Or "from typing ..." on Python 3.13+ + from typing_extensions import ReadOnly + + class Movie(TypedDict): + name: ReadOnly[str] + num_watched: int + + m: Movie = {"name": "Jaws", "num_watched": 1} + m["name"] = "The Godfather" # Error: "name" is read-only + m["num_watched"] += 1 # OK + +A TypedDict with a mutable item can be assigned to a TypedDict +with a corresponding read-only item, and the type of the item can +vary :ref:`covariantly `: + +.. code-block:: python + + class Entry(TypedDict): + name: ReadOnly[str | None] + year: ReadOnly[int] + + class Movie(TypedDict): + name: str + year: int + + def process_entry(i: Entry) -> None: ... + + m: Movie = {"name": "Jaws", "year": 1975} + process_entry(m) # OK + Unions of TypedDicts -------------------- From 62a971c868e43d940769d9b9da341b4ed175ef15 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Wed, 9 Oct 2024 10:19:57 -0400 Subject: [PATCH 0796/1617] Emit error for "raise NotImplemented" (#17890) Refs #5710 Adds special-case handling for raising `NotImplemented`: ```python raise NotImplemented # E: Exception must be derived from BaseException; did you mean "NotImplementedError"? ``` Per the linked issue, there's some debate as to how to best handle `NotImplemented`. This PR special-cases its behavior in `raise` statements, whereas the leaning in the issue (at the time it was opened) was towards updating its definition in typeshed and possibly special-casing its use in the relevant dunder methods. Going the typeshed/special-dunder route may still happen, but it hasn't in the six years since the issue was opened. It would be nice to at least catch errors from raising `NotImplemented` in the interim. Making this change also uncovered a regression introduced in python/typeshed#4222. Previously, `NotImplemented` was annotated as `Any` in typeshed, so returning `NotImplemented` from a non-dunder would emit an error when `--warn-return-any` was used: ```python class A: def some(self) -> bool: return NotImplemented # E: Returning Any from function declared to return "bool" ``` However, in python/typeshed#4222, the type of `NotImplemented` was updated to be a subclass of `Any`. This broke the handling of `--warn-return-any`, but it wasn't caught since the definition of `NotImplemented` in `fixtures/notimplemented.pyi` wasn't updated along with the definition in typeshed. As a result, current mypy doesn't emit an error here ([playground](https://mypy-play.net/?mypy=1.11.2&python=3.12&flags=strict%2Cwarn-return-any&gist=8a78e3eb68b0b738f73fdd326f0bfca1)), despite having a test case saying that it should. As a bandaid, this PR add special handling for `NotImplemented` in return statements to treat it like an explicit `Any`, restoring the pre- python/typeshed#4222 behavior. --- mypy/checker.py | 15 +++++++++++++++ test-data/unit/check-statements.test | 7 +++++++ test-data/unit/fixtures/notimplemented.pyi | 10 +++++++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e0c2578df095..3483f1b029c8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4581,6 +4581,13 @@ def check_return_stmt(self, s: ReturnStmt) -> None: s.expr, return_type, allow_none_return=allow_none_func_call ) ) + # Treat NotImplemented as having type Any, consistent with its + # definition in typeshed prior to python/typeshed#4222. + if ( + isinstance(typ, Instance) + and typ.type.fullname == "builtins._NotImplementedType" + ): + typ = AnyType(TypeOfAny.special_form) if defn.is_async_generator: self.fail(message_registry.RETURN_IN_ASYNC_GENERATOR, s) @@ -4746,6 +4753,14 @@ def type_check_raise(self, e: Expression, s: RaiseStmt, optional: bool = False) # https://github.com/python/mypy/issues/11089 self.expr_checker.check_call(typ, [], [], e) + if isinstance(typ, Instance) and typ.type.fullname == "builtins._NotImplementedType": + self.fail( + message_registry.INVALID_EXCEPTION.with_additional_msg( + '; did you mean "NotImplementedError"?' + ), + s, + ) + def visit_try_stmt(self, s: TryStmt) -> None: """Type check a try statement.""" # Our enclosing frame will get the result if the try/except falls through. diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d1464423e90f..44880cf35204 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -519,6 +519,13 @@ if object(): raise BaseException from f # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] +[case testRaiseNotImplementedFails] +if object(): + raise NotImplemented # E: Exception must be derived from BaseException; did you mean "NotImplementedError"? +if object(): + raise NotImplemented() # E: NotImplemented? not callable +[builtins fixtures/notimplemented.pyi] + [case testTryFinallyStatement] import typing try: diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index 2ca376ea0760..92edf84a7fd1 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -1,6 +1,5 @@ # builtins stub used in NotImplemented related cases. -from typing import Any, cast - +from typing import Any class object: def __init__(self) -> None: pass @@ -10,5 +9,10 @@ class function: pass class bool: pass class int: pass class str: pass -NotImplemented = cast(Any, None) class dict: pass + +class _NotImplementedType(Any): + __call__: NotImplemented # type: ignore +NotImplemented: _NotImplementedType + +class BaseException: pass From 706a5468f078d27c9783db420755a3ebc86937d3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 9 Oct 2024 15:21:54 +0100 Subject: [PATCH 0797/1617] [mypyc] Add "runtests.py mypyc-fast" for running fast mypyc tests (#17906) Many tests are a bit slow and but don't increase test coverage by a lot. Running these fast tests should find the vast majority of issues while being significantly faster than running all mypyc tests. --- mypyc/doc/dev-intro.md | 4 ++++ runtests.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index d11df7068e91..461a19d37121 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -202,6 +202,10 @@ general overview of how things work. Test cases live under -q mypyc`. If you don't make changes to code under `mypy/`, it's not important to regularly run mypy tests during development. +You can use `python runtests.py mypyc-fast` to run a subset of mypyc +tests that covers most functionality but runs significantly quicker +than the entire test suite. + When you create a PR, we have Continuous Integration jobs set up that compile mypy using mypyc and run the mypy test suite using the compiled mypy. This will sometimes catch additional issues not caught diff --git a/runtests.py b/runtests.py index 80ef8d814ee1..9863e8491500 100755 --- a/runtests.py +++ b/runtests.py @@ -17,6 +17,8 @@ MYPYC_RUN_MULTI = "TestRunMultiFile" MYPYC_EXTERNAL = "TestExternal" MYPYC_COMMAND_LINE = "TestCommandLine" +MYPYC_SEPARATE = "TestRunSeparate" +MYPYC_MULTIMODULE = "multimodule" # Subset of mypyc run tests that are slow ERROR_STREAM = "ErrorStreamSuite" @@ -31,6 +33,7 @@ MYPYC_RUN_MULTI, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, + MYPYC_SEPARATE, ERROR_STREAM, ] @@ -40,7 +43,10 @@ # These must be enabled by explicitly including 'mypyc-extra' on the command line. -MYPYC_OPT_IN = [MYPYC_RUN, MYPYC_RUN_MULTI] +MYPYC_OPT_IN = [MYPYC_RUN, MYPYC_RUN_MULTI, MYPYC_SEPARATE] + +# These mypyc test filters cover most slow test cases +MYPYC_SLOW = [MYPYC_RUN_MULTI, MYPYC_COMMAND_LINE, MYPYC_SEPARATE, MYPYC_MULTIMODULE] # We split the pytest run into three parts to improve test @@ -77,6 +83,7 @@ "-k", " or ".join([DAEMON, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, ERROR_STREAM]), ], + "mypyc-fast": ["pytest", "-q", "mypyc", "-k", f"not ({' or '.join(MYPYC_SLOW)})"], # Test cases that might take minutes to run "pytest-extra": ["pytest", "-q", "-k", " or ".join(PYTEST_OPT_IN)], # Mypyc tests that aren't run by default, since they are slow and rarely @@ -87,7 +94,7 @@ # Stop run immediately if these commands fail FAST_FAIL = ["self", "lint"] -EXTRA_COMMANDS = ("pytest-extra", "mypyc-extra") +EXTRA_COMMANDS = ("pytest-extra", "mypyc-fast", "mypyc-extra") DEFAULT_COMMANDS = [cmd for cmd in cmds if cmd not in EXTRA_COMMANDS] assert all(cmd in cmds for cmd in FAST_FAIL) From eca206d3c96bf4082a0a087c2614deb5af8c4afd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 9 Oct 2024 19:52:47 +0100 Subject: [PATCH 0798/1617] Fix union callees with functools.partial (#17903) Fixes #17741. --- mypy/plugins/functools.py | 15 ++++++++++++++- test-data/unit/check-functools.test | 21 +++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 6650af637519..f09ea88f7162 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -18,6 +18,7 @@ Type, TypeOfAny, UnboundType, + UnionType, get_proper_type, ) @@ -130,7 +131,19 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: if isinstance(get_proper_type(ctx.arg_types[0][0]), Overloaded): # TODO: handle overloads, just fall back to whatever the non-plugin code does return ctx.default_return_type - fn_type = ctx.api.extract_callable_type(ctx.arg_types[0][0], ctx=ctx.default_return_type) + return handle_partial_with_callee(ctx, callee=ctx.arg_types[0][0]) + + +def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) -> Type: + if not isinstance(ctx.api, mypy.checker.TypeChecker): # use internals + return ctx.default_return_type + + if isinstance(callee_proper := get_proper_type(callee), UnionType): + return UnionType.make_union( + [handle_partial_with_callee(ctx, item) for item in callee_proper.items] + ) + + fn_type = ctx.api.extract_callable_type(callee, ctx=ctx.default_return_type) if fn_type is None: return ctx.default_return_type diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 50de3789ebd2..bee30931a92b 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -346,15 +346,32 @@ fn1: Union[Callable[[int], int], Callable[[int], int]] reveal_type(functools.partial(fn1, 2)()) # N: Revealed type is "builtins.int" fn2: Union[Callable[[int], int], Callable[[int], str]] -reveal_type(functools.partial(fn2, 2)()) # N: Revealed type is "builtins.object" +reveal_type(functools.partial(fn2, 2)()) # N: Revealed type is "Union[builtins.int, builtins.str]" fn3: Union[Callable[[int], int], str] reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \ - # E: "Union[Callable[[int], int], str]" not callable \ # N: Revealed type is "builtins.int" \ # E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]" [builtins fixtures/tuple.pyi] +[case testFunctoolsPartialUnionOfTypeAndCallable] +import functools +from typing import Callable, Union, Type +from typing_extensions import TypeAlias + +class FooBar: + def __init__(self, arg1: str) -> None: + pass + +def f1(t: Union[Type[FooBar], Callable[..., 'FooBar']]) -> None: + val = functools.partial(t) + +FooBarFunc: TypeAlias = Callable[..., 'FooBar'] + +def f2(t: Union[Type[FooBar], FooBarFunc]) -> None: + val = functools.partial(t) +[builtins fixtures/tuple.pyi] + [case testFunctoolsPartialExplicitType] from functools import partial from typing import Type, TypeVar, Callable From 33717e5ae0805076a98c1a9f2c45b032299c3f90 Mon Sep 17 00:00:00 2001 From: Joe Gordon Date: Thu, 10 Oct 2024 15:40:13 -0700 Subject: [PATCH 0799/1617] Better error for `mypy -p package` without py.typed (#17908) Improve the error message when running `mypy -p packagename` with a package that doesn't have py.typed set. > package 'example' is skipping due to missing py.typed marker. See https://mypy.readthedocs.io/en/stable/installed_packages.html for more details Fix #17048 --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/main.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 0674e3b7e79b..3f25ced16106 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -22,7 +22,14 @@ from mypy.errors import CompileError from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache -from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path +from mypy.modulefinder import ( + BuildSource, + FindModuleCache, + ModuleNotFoundReason, + SearchPaths, + get_search_dirs, + mypy_path, +) from mypy.options import INCOMPLETE_FEATURES, BuildType, Options from mypy.split_namespace import SplitNamespace from mypy.version import __version__ @@ -1413,7 +1420,15 @@ def set_strict_flags() -> None: fail(f"Package name '{p}' cannot have a slash in it.", stderr, options) p_targets = cache.find_modules_recursive(p) if not p_targets: - fail(f"Can't find package '{p}'", stderr, options) + reason = cache.find_module(p) + if reason is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: + fail( + f"Package '{p}' cannot be type checked due to missing py.typed marker. See https://mypy.readthedocs.io/en/stable/installed_packages.html for more details", + stderr, + options, + ) + else: + fail(f"Can't find package '{p}'", stderr, options) targets.extend(p_targets) for m in special_opts.modules: targets.append(BuildSource(None, m, None)) From bd9200bda5595fc71c01fe0dff9debbce3467a84 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:02:27 -0700 Subject: [PATCH 0800/1617] Remove debug build for now (#17917) It's started failing on master due to agent changes and also it never tested debug builds in the first place, see issues linked https://github.com/python/mypy/actions/runs/11283075094/job/31381791947 --- .github/workflows/test.yml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 296f6fde1523..6ec8954bbe3e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,7 +70,6 @@ jobs: toxenv: py tox_extra_args: "-n 4" test_mypyc: true - - name: Test suite with py313-ubuntu, mypyc-compiled python: '3.13' arch: x64 @@ -78,6 +77,7 @@ jobs: toxenv: py tox_extra_args: "-n 4" test_mypyc: true + # - name: Test suite with py314-dev-ubuntu # python: '3.14-dev' # arch: x64 @@ -94,13 +94,16 @@ jobs: os: macos-13 toxenv: py tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py" - - name: mypyc runtime tests with py38-debug-build-ubuntu - python: '3.8.17' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" - debug_build: true + # This is broken. See + # - https://github.com/python/mypy/issues/17819 + # - https://github.com/python/mypy/pull/17822 + # - name: mypyc runtime tests with py38-debug-build-ubuntu + # python: '3.8.17' + # arch: x64 + # os: ubuntu-latest + # toxenv: py + # tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" + # debug_build: true - name: Type check our own code (py38-ubuntu) python: '3.8' @@ -148,17 +151,17 @@ jobs: ./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV # TODO: does this do anything? env vars aren't passed to the next step right source $VENV/bin/activate - - name: Latest Dev build + - name: Latest dev build if: ${{ endsWith(matrix.python, '-dev') }} run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - build-essential gdb lcov libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses5-dev \ - libreadline6-dev libsqlite3-dev libssl-dev lzma lzma-dev tk-dev uuid-dev zlib1g-dev git clone --depth 1 https://github.com/python/cpython.git /tmp/cpython --branch $( echo ${{ matrix.python }} | sed 's/-dev//' ) cd /tmp/cpython echo git rev-parse HEAD; git rev-parse HEAD git show --no-patch + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential gdb lcov libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses5-dev \ + libreadline6-dev libsqlite3-dev libssl-dev lzma lzma-dev tk-dev uuid-dev zlib1g-dev ./configure --prefix=/opt/pythondev make -j$(nproc) sudo make install @@ -190,7 +193,7 @@ jobs: - name: Setup tox environment run: | - tox run -e ${{ matrix.toxenv }} --notes + tox run -e ${{ matrix.toxenv }} --notest - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} continue-on-error: ${{ matrix.allow_failure == 'true' }} From 46c108e35c215aef53b638b1f9e34e4665759ec4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 11 Oct 2024 08:47:26 +0100 Subject: [PATCH 0801/1617] Add latest 1.12 changes to changelog (#17921) These were cherry-picked to the release branch. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c664121f097d..ded92b58daaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -187,6 +187,8 @@ List of documentation updates: * Make changelog visible in mypy documentation (quinn-sasha, PR [17742](https://github.com/python/mypy/pull/17742)) * List all incomplete features in `--enable-incomplete-feature` docs (sobolevn, PR [17633](https://github.com/python/mypy/pull/17633)) * Remove the explicit setting of a pygments theme (Pradyun Gedam, PR [17571](https://github.com/python/mypy/pull/17571)) + * Document ReadOnly with TypedDict (Jukka Lehtosalo, PR [17905](https://github.com/python/mypy/pull/17905)) + * Document TypeIs (Chelsea Durazo, PR [17821](https://github.com/python/mypy/pull/17821)) ### Experimental Inline TypedDict Syntax @@ -250,6 +252,9 @@ This feature was contributed by Ivan Levkivskyi (PR [17457](https://github.com/p * Fix typechecking for async generators (Danny Yang, PR [17452](https://github.com/python/mypy/pull/17452)) * Fix strict optional handling in attrs plugin (Ivan Levkivskyi, PR [17451](https://github.com/python/mypy/pull/17451)) * Allow mixing ParamSpec and TypeVarTuple in Generic (Ivan Levkivskyi, PR [17450](https://github.com/python/mypy/pull/17450)) + * Improvements to `functools.partial` of types (Shantanu, PR [17898](https://github.com/python/mypy/pull/17898)) + * Make ReadOnly TypedDict items covariant (Jukka Lehtosalo, PR [17904](https://github.com/python/mypy/pull/17904)) + * Fix union callees with `functools.partial` (Jukka Lehtosalo, PR [17903](https://github.com/python/mypy/pull/17903)) ### Typeshed Updates @@ -263,6 +268,7 @@ Thanks to all mypy contributors who contributed to this release: - Bénédikt Tran - Brian Schubert - bzoracler +- Chelsea Durazo - Danny Yang - Edgar Ramírez Mondragón - Eric Mark Martin From 54f495421e3aaf7d3fede9ff899ce78c1cd6cf66 Mon Sep 17 00:00:00 2001 From: Max Chang <62986841+changhoetyng@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:15:58 +0100 Subject: [PATCH 0802/1617] [PEP 695] Fix multiple nested classes don't work (#17820) This PR modifies the `lookup_fully_qualified_or_none` method to support multiple nested classes. Fixes #17780 --- mypy/semanal.py | 50 ++++++++++---- test-data/unit/check-python312.test | 76 ++++++++++++++++++++++ test-data/unit/fine-grained-python312.test | 20 ++++++ 3 files changed, 135 insertions(+), 11 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 37aafc3b2647..95efe2b0f30c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6461,18 +6461,46 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non Note that this can't be used for names nested in class namespaces. """ # TODO: unify/clean-up/simplify lookup methods, see #4157. - # TODO: support nested classes (but consider performance impact, - # we might keep the module level only lookup for thing like 'builtins.int'). - assert "." in fullname module, name = fullname.rsplit(".", maxsplit=1) - if module not in self.modules: - return None - filenode = self.modules[module] - result = filenode.names.get(name) - if result is None and self.is_incomplete_namespace(module): - # TODO: More explicit handling of incomplete refs? - self.record_incomplete_ref() - return result + + if module in self.modules: + # If the module exists, look up the name in the module. + # This is the common case. + filenode = self.modules[module] + result = filenode.names.get(name) + if result is None and self.is_incomplete_namespace(module): + # TODO: More explicit handling of incomplete refs? + self.record_incomplete_ref() + return result + else: + # Else, try to find the longest prefix of the module name that is in the modules dictionary. + splitted_modules = fullname.split(".") + names = [] + + while splitted_modules and ".".join(splitted_modules) not in self.modules: + names.append(splitted_modules.pop()) + + if not splitted_modules or not names: + # If no module or name is found, return None. + return None + + # Reverse the names list to get the correct order of names. + names.reverse() + + module = ".".join(splitted_modules) + filenode = self.modules[module] + result = filenode.names.get(names[0]) + + if result is None and self.is_incomplete_namespace(module): + # TODO: More explicit handling of incomplete refs? + self.record_incomplete_ref() + + for part in names[1:]: + if result is not None and isinstance(result.node, TypeInfo): + result = result.node.names.get(part) + else: + return None + return result def object_type(self) -> Instance: return self.named_type("builtins.object") diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 085cc052705d..c5c8ada1aae1 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1873,3 +1873,79 @@ d1: Multi[int, str] = Multi[float, str]() # E: Incompatible types in assignment d2: Multi[float, str] = Multi[int, str]() # E: Incompatible types in assignment (expression has type "Multi[int, str]", variable has type "Multi[float, str]") d3: Multi[str, int] = Multi[str, float]() d4: Multi[str, float] = Multi[str, int]() # E: Incompatible types in assignment (expression has type "Multi[str, int]", variable has type "Multi[str, float]") + +[case testPEP695MultipleNestedGenericClass1] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class B: + class C: + class D[Q]: + def g(self, x: Q): ... + d: D[str] + +x: A.B.C.D[int] +x.g('a') # E: Argument 1 to "g" of "D" has incompatible type "str"; expected "int" +reveal_type(x) # N: Revealed type is "__main__.A.B.C.D[builtins.int]" +reveal_type(A.B.C.d) # N: Revealed type is "__main__.A.B.C.D[builtins.str]" + +[case testPEP695MultipleNestedGenericClass2] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class B: + def m(self) -> None: + class C[T]: + def f(self) -> T: ... + x: C[int] + reveal_type(x.f()) # N: Revealed type is "builtins.int" + self.a = C[str]() + +reveal_type(A().B().a) # N: Revealed type is "__main__.C@5[builtins.str]" + +[case testPEP695MultipleNestedGenericClass3] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class C[T]: + def f(self) -> T: ... + class D[S]: + x: T # E: Name "T" is not defined + def g(self) -> S: ... + +a: A.C[int] +reveal_type(a.f()) # N: Revealed type is "builtins.int" +b: A.C.D[str] +reveal_type(b.g()) # N: Revealed type is "builtins.str" + +class B: + class E[T]: + class F[T]: # E: "T" already defined as a type parameter + x: T + +c: B.E.F[int] + +[case testPEP695MultipleNestedGenericClass4] +# flags: --enable-incomplete-feature=NewGenericSyntax +class Z: + class A: + class B[T]: + def __get__(self, instance: Z.A, owner: type[Z.A]) -> T: + return None # E: Incompatible return value type (got "None", expected "T") + f = B[int]() + +a = Z.A() +v = a.f + +[case testPEP695MultipleNestedGenericClass5] +# flags: --enable-incomplete-feature=NewGenericSyntax +from a.b.c import d +x: d.D.E.F.G[int] +x.g('a') # E: Argument 1 to "g" of "G" has incompatible type "str"; expected "int" +reveal_type(x) # N: Revealed type is "a.b.c.d.D.E.F.G[builtins.int]" +reveal_type(d.D.E.F.d) # N: Revealed type is "a.b.c.d.D.E.F.G[builtins.str]" + +[file a/b/c/d.py] +class D: + class E: + class F: + class G[Q]: + def g(self, x: Q): ... + d: G[str] diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 0e438ca06574..2cb2148a66fe 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -95,3 +95,23 @@ def f(x: int) -> None: pass [out] == main:7: error: Missing positional argument "x" in call to "f" + +[case testPEP695MultipleNestedGenericClassMethodUpdated] +from a import f + +class A: + class C: + class D[T]: + x: T + def m(self) -> T: + f() + return self.x + +[file a.py] +def f() -> None: pass + +[file a.py.2] +def f(x: int) -> None: pass +[out] +== +main:8: error: Missing positional argument "x" in call to "f" From b1701e516ad019a8d1dfc463541a0fa850221543 Mon Sep 17 00:00:00 2001 From: Max Chang <62986841+changhoetyng@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:16:20 +0100 Subject: [PATCH 0803/1617] fix crash issue when using shadowfile with pretty #17853 (#17894) - Fix crash issue when using --pretty with --shadow-file Example Scenario: a.py ```python b: bytes ``` b.py ```python a: int = "" b: bytes = 1 ``` output ``` $ mypy a.py --pretty --shadow-file a.py b.py a.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int") a: int = "" ^~ a.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "bytes") b: bytes = 1 ^ ``` Fixes #17853 --- mypy/errors.py | 18 +++++++++++++++++- test-data/unit/cmdline.test | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/mypy/errors.py b/mypy/errors.py index 13452b14a237..1b3f485d19c0 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -922,9 +922,25 @@ def file_messages(self, path: str, formatter: ErrorFormatter | None = None) -> l self.flushed_files.add(path) source_lines = None if self.options.pretty and self.read_source: - source_lines = self.read_source(path) + # Find shadow file mapping and read source lines if a shadow file exists for the given path. + # If shadow file mapping is not found, read source lines + mapped_path = self.find_shadow_file_mapping(path) + if mapped_path: + source_lines = self.read_source(mapped_path) + else: + source_lines = self.read_source(path) return self.format_messages(error_tuples, source_lines) + def find_shadow_file_mapping(self, path: str) -> str | None: + """Return the shadow file path for a given source file path or None.""" + if self.options.shadow_file is None: + return None + + for i in self.options.shadow_file: + if i[0] == path: + return i[1] + return None + def new_messages(self) -> list[str]: """Return a string list of new error messages. diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 2262b7e7280c..38ea83cdbcf4 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -914,6 +914,23 @@ s4.py:2: error: Incompatible return value type (got "int", expected "str") s3.py:2: error: Incompatible return value type (got "List[int]", expected "int") s1.py:2: error: Incompatible return value type (got "int", expected "str") +[case testShadowFileWithPretty] +# cmd: mypy a.py --pretty --shadow-file a.py b.py +[file a.py] +b: bytes +[file b.py] +a: int = "" +b: bytes = 1 +[out] +a.py:1: error: Incompatible types in assignment (expression has type "str", +variable has type "int") + a: int = "" + ^~ +a.py:2: error: Incompatible types in assignment (expression has type "int", +variable has type "bytes") + b: bytes = 1 + ^ + [case testConfigWarnUnusedSection1] # cmd: mypy foo.py quux.py spam/eggs.py [file mypy.ini] From 0c10dc3b609f6afc9c6147a70adda54d93bcca86 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Fri, 11 Oct 2024 23:24:16 +0200 Subject: [PATCH 0804/1617] PEP 702 (@deprecated): improve the handling of explicit type annotations of assignment statements (#17899) Two improvements of the current PEP 702 implementation (deprecated): * Analyse the explicit type annotations of "normal" assignment statements (that have an rvalue). * Dive into nested type annotations of assignment statements. (I intend to continue working on PEP 702 and would prefer to do so in small steps if this is okay for the maintainers/reviewers.) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 18 ++++++++++++--- test-data/unit/check-deprecated.test | 33 +++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3483f1b029c8..ca35144456fe 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -287,6 +287,18 @@ class PartialTypeScope(NamedTuple): is_local: bool +class InstanceDeprecatedVisitor(TypeTraverserVisitor): + """Visitor that recursively checks for deprecations in nested instances.""" + + def __init__(self, typechecker: TypeChecker, context: Context) -> None: + self.typechecker = typechecker + self.context = context + + def visit_instance(self, t: Instance) -> None: + super().visit_instance(t) + self.typechecker.check_deprecated(t.type, self.context) + + class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. @@ -2930,14 +2942,14 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: Handle all kinds of assignment statements (simple, indexed, multiple). """ - if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if s.unanalyzed_type is not None: for lvalue in s.lvalues: if ( isinstance(lvalue, NameExpr) and isinstance(var := lvalue.node, Var) - and isinstance(instance := get_proper_type(var.type), Instance) + and (var.type is not None) ): - self.check_deprecated(instance.type, s) + var.type.accept(InstanceDeprecatedVisitor(typechecker=self, context=s)) # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index f587034d8059..13cebc85513e 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -102,7 +102,8 @@ def h() -> None: ... [case testDeprecatedClass] -from typing_extensions import deprecated +from typing import Callable, List, Optional, Tuple, Union +from typing_extensions import deprecated, TypeAlias, TypeVar @deprecated("use C2 instead") class C: ... @@ -114,10 +115,40 @@ C.missing() # N: class __main__.C is deprecated: use C2 instead \ C.__init__(c) # N: class __main__.C is deprecated: use C2 instead C(1) # N: class __main__.C is deprecated: use C2 instead \ # E: Too many arguments for "C" + D = C # N: class __main__.C is deprecated: use C2 instead D() t = (C, C, D) # N: class __main__.C is deprecated: use C2 instead +u1: Union[C, int] = 1 # N: class __main__.C is deprecated: use C2 instead +u1 = 1 +u2 = 1 # type: Union[C, int] # N: class __main__.C is deprecated: use C2 instead +u2 = 1 + +c1 = c2 = C() # N: class __main__.C is deprecated: use C2 instead +i, c3 = 1, C() # N: class __main__.C is deprecated: use C2 instead + +class E: ... + +x1: Optional[C] # N: class __main__.C is deprecated: use C2 instead +x2: Union[D, C, E] # N: class __main__.C is deprecated: use C2 instead +x3: Union[D, Optional[C], E] # N: class __main__.C is deprecated: use C2 instead +x4: Tuple[D, C, E] # N: class __main__.C is deprecated: use C2 instead +x5: Tuple[Tuple[D, C], E] # N: class __main__.C is deprecated: use C2 instead +x6: List[C] # N: class __main__.C is deprecated: use C2 instead +x7: List[List[C]] # N: class __main__.C is deprecated: use C2 instead +x8: List[Optional[Tuple[Union[List[C], int]]]] # N: class __main__.C is deprecated: use C2 instead +x9: Callable[[int], C] # N: class __main__.C is deprecated: use C2 instead +x10: Callable[[int, C, int], int] # N: class __main__.C is deprecated: use C2 instead + +T = TypeVar("T") +A1: TypeAlias = Optional[C] # ToDo +x11: A1 +A2: TypeAlias = List[Union[A2, C]] # ToDo +x12: A2 +A3: TypeAlias = List[Optional[T]] +x13: A3[C] # N: class __main__.C is deprecated: use C2 instead + [builtins fixtures/tuple.pyi] From ca97d9667da1cf0bf22886ecfc2280958b56ac68 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 02:42:11 +0200 Subject: [PATCH 0805/1617] [mypyc] Sync pythoncapi_compat.h (#17929) Use functions added for Python 3.14 * `PyBytes_Join` added in https://github.com/python/cpython/issues/121645 * `PyUnicode_Equal` added in https://github.com/python/cpython/issues/124502 --- mypyc/lib-rt/bytes_ops.c | 2 +- mypyc/lib-rt/getargsfast.c | 9 +- mypyc/lib-rt/pythoncapi_compat.h | 340 ++++++++++++++++++++++++++++++- mypyc/lib-rt/pythonsupport.h | 5 +- 4 files changed, 341 insertions(+), 15 deletions(-) diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 4da62be11571..0cb2f300d507 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -99,7 +99,7 @@ PyObject *CPyBytes_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) { // (mostly commonly, for bytearrays) PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter) { if (PyBytes_CheckExact(sep)) { - return _PyBytes_Join(sep, iter); + return PyBytes_Join(sep, iter); } else { _Py_IDENTIFIER(join); return _PyObject_CallMethodIdOneArg(sep, &PyId_join, iter); diff --git a/mypyc/lib-rt/getargsfast.c b/mypyc/lib-rt/getargsfast.c index 62d0dfed0a6d..e5667e22efe3 100644 --- a/mypyc/lib-rt/getargsfast.c +++ b/mypyc/lib-rt/getargsfast.c @@ -271,16 +271,9 @@ find_keyword(PyObject *kwnames, PyObject *const *kwstack, PyObject *key) for (i = 0; i < nkwargs; i++) { PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); assert(PyUnicode_Check(kwname)); -#if CPY_3_13_FEATURES - if (_PyUnicode_Equal(kwname, key)) { + if (PyUnicode_Equal(kwname, key)) { return kwstack[i]; } -#else - if (_PyUnicode_EQ(kwname, key)) { - return kwstack[i]; - } -#endif - } return NULL; } diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index 1b59f93de7ec..34a84c969b6a 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -45,6 +45,13 @@ extern "C" { # define _PyObject_CAST(op) _Py_CAST(PyObject*, op) #endif +#ifndef Py_BUILD_ASSERT +# define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char [1 - 2 * !(cond)]); \ + } while(0) +#endif + // bpo-42262 added Py_NewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) @@ -1338,9 +1345,169 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, } #endif +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + +static inline int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + +static inline int +PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 -// gh-116560 added PyLong_GetSign() to Python 3.14a4 -#if PY_VERSION_HEX < 0x030E00A1 +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 static inline int PyLong_GetSign(PyObject *obj, int *sign) { if (!PyLong_Check(obj)) { @@ -1354,6 +1521,175 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign) #endif +// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +{ + if (!PyUnicode_Check(str1)) { + PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + Py_TYPE(str1)->tp_name); + return -1; + } + if (!PyUnicode_Check(str2)) { + PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + Py_TYPE(str2)->tp_name); + return -1; + } + +#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) + extern int _PyUnicode_Equal(PyObject *str1, PyObject *str2); + + return _PyUnicode_Equal(str1, str2); +#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#else + return (PyUnicode_Compare(str1, str2) == 0); +#endif +} +#endif + + +// gh-121645 added PyBytes_Join() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) +{ + return _PyBytes_Join(sep, iterable); +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +{ +#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) + extern Py_hash_t _Py_HashBytes(const void *src, Py_ssize_t len); + + return _Py_HashBytes(ptr, len); +#else + Py_hash_t hash; + PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); + if (bytes == NULL) { + return -1; + } + hash = PyObject_Hash(bytes); + Py_DECREF(bytes); + return hash; +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +{ + iternextfunc tp_iternext; + + assert(iter != NULL); + assert(item != NULL); + + tp_iternext = Py_TYPE(iter)->tp_iternext; + if (tp_iternext == NULL) { + *item = NULL; + PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + Py_TYPE(iter)->tp_name); + return -1; + } + + if ((*item = tp_iternext(iter))) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return 0; + } + return -1; +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyLong_FromInt32(int32_t value) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + return PyLong_FromLong(value); +} + +static inline PyObject* PyLong_FromInt64(int64_t value) +{ + Py_BUILD_ASSERT(sizeof(long long) >= 8); + return PyLong_FromLongLong(value); +} + +static inline PyObject* PyLong_FromUInt32(uint32_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); + return PyLong_FromUnsignedLong(value); +} + +static inline PyObject* PyLong_FromUInt64(uint64_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); + return PyLong_FromUnsignedLongLong(value); +} + +static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(int) == 4); + int value = PyLong_AsInt(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int32_t)value; + return 0; +} + +static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + long long value = PyLong_AsLongLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int64_t)value; + return 0; +} + +static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + unsigned long value = PyLong_AsUnsignedLong(obj); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } +#if SIZEOF_LONG > 4 + if ((unsigned long)UINT32_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32_t"); + return -1; + } +#endif + *pvalue = (uint32_t)value; + return 0; +} + +static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + unsigned long long value = PyLong_AsUnsignedLongLong(obj); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (uint64_t)value; + return 0; +} +#endif + + #ifdef __cplusplus } #endif diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index bf7e5203758d..5d595dba1a12 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -17,13 +17,10 @@ #ifndef Py_BUILD_CORE #define Py_BUILD_CORE #endif -#include "internal/pycore_bytesobject.h" // _PyBytes_Join -#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdObjArgs, _PyObject_CallMethodIdOneArg +#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdOneArg #include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue -#include "internal/pycore_object.h" // _PyType_CalculateMetaclass #include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError #include "internal/pycore_setobject.h" // _PySet_Update -#include "internal/pycore_unicodeobject.h" // _PyUnicode_EQ, _PyUnicode_FastCopyCharacters #endif #if CPY_3_12_FEATURES From c32d11e60f04a0798292c438186199ccdec0db61 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 13 Oct 2024 17:50:15 -0700 Subject: [PATCH 0806/1617] Significantly speed up file handling error paths (#17920) This can have a huge overall impact on mypy performance when search paths are long --- mypy/build.py | 17 +++++++------- mypy/fscache.py | 59 +++++++++++++++++++++-------------------------- mypy/fswatcher.py | 13 +++++------ 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 733f0685792e..964da5aac8b0 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -736,8 +736,8 @@ def maybe_swap_for_shadow_path(self, path: str) -> str: shadow_file = self.shadow_equivalence_map.get(path) return shadow_file if shadow_file else path - def get_stat(self, path: str) -> os.stat_result: - return self.fscache.stat(self.maybe_swap_for_shadow_path(path)) + def get_stat(self, path: str) -> os.stat_result | None: + return self.fscache.stat_or_none(self.maybe_swap_for_shadow_path(path)) def getmtime(self, path: str) -> int: """Return a file's mtime; but 0 in bazel mode. @@ -1394,9 +1394,9 @@ def validate_meta( if bazel: # Normalize path under bazel to make sure it isn't absolute path = normpath(path, manager.options) - try: - st = manager.get_stat(path) - except OSError: + + st = manager.get_stat(path) + if st is None: return None if not stat.S_ISDIR(st.st_mode) and not stat.S_ISREG(st.st_mode): manager.log(f"Metadata abandoned for {id}: file or directory {path} does not exist") @@ -1572,10 +1572,9 @@ def write_cache( plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False)) # Obtain and set up metadata - try: - st = manager.get_stat(path) - except OSError as err: - manager.log(f"Cannot get stat for {path}: {err}") + st = manager.get_stat(path) + if st is None: + manager.log(f"Cannot get stat for {path}") # Remove apparently-invalid cache files. # (This is purely an optimization.) for filename in [data_json, meta_json]: diff --git a/mypy/fscache.py b/mypy/fscache.py index 15679ad03e85..8251f4bd9488 100644 --- a/mypy/fscache.py +++ b/mypy/fscache.py @@ -51,8 +51,8 @@ def set_package_root(self, package_root: list[str]) -> None: def flush(self) -> None: """Start another transaction and empty all caches.""" - self.stat_cache: dict[str, os.stat_result] = {} - self.stat_error_cache: dict[str, OSError] = {} + self.stat_or_none_cache: dict[str, os.stat_result | None] = {} + self.listdir_cache: dict[str, list[str]] = {} self.listdir_error_cache: dict[str, OSError] = {} self.isfile_case_cache: dict[str, bool] = {} @@ -62,24 +62,21 @@ def flush(self) -> None: self.hash_cache: dict[str, str] = {} self.fake_package_cache: set[str] = set() - def stat(self, path: str) -> os.stat_result: - if path in self.stat_cache: - return self.stat_cache[path] - if path in self.stat_error_cache: - raise copy_os_error(self.stat_error_cache[path]) + def stat_or_none(self, path: str) -> os.stat_result | None: + if path in self.stat_or_none_cache: + return self.stat_or_none_cache[path] + + st = None try: st = os.stat(path) - except OSError as err: + except OSError: if self.init_under_package_root(path): try: - return self._fake_init(path) + st = self._fake_init(path) except OSError: pass - # Take a copy to get rid of associated traceback and frame objects. - # Just assigning to __traceback__ doesn't free them. - self.stat_error_cache[path] = copy_os_error(err) - raise err - self.stat_cache[path] = st + + self.stat_or_none_cache[path] = st return st def init_under_package_root(self, path: str) -> bool: @@ -112,9 +109,9 @@ def init_under_package_root(self, path: str) -> bool: if not os.path.basename(dirname).isidentifier(): # Can't put an __init__.py in a place that's not an identifier return False - try: - st = self.stat(dirname) - except OSError: + + st = self.stat_or_none(dirname) + if st is None: return False else: if not stat.S_ISDIR(st.st_mode): @@ -145,7 +142,7 @@ def _fake_init(self, path: str) -> os.stat_result: assert basename == "__init__.py", path assert not os.path.exists(path), path # Not cached! dirname = os.path.normpath(dirname) - st = self.stat(dirname) # May raise OSError + st = os.stat(dirname) # May raise OSError # Get stat result as a list so we can modify it. seq: list[float] = list(st) seq[stat.ST_MODE] = stat.S_IFREG | 0o444 @@ -153,7 +150,6 @@ def _fake_init(self, path: str) -> os.stat_result: seq[stat.ST_NLINK] = 1 seq[stat.ST_SIZE] = 0 st = os.stat_result(seq) - self.stat_cache[path] = st # Make listdir() and read() also pretend this file exists. self.fake_package_cache.add(dirname) return st @@ -181,9 +177,8 @@ def listdir(self, path: str) -> list[str]: return results def isfile(self, path: str) -> bool: - try: - st = self.stat(path) - except OSError: + st = self.stat_or_none(path) + if st is None: return False return stat.S_ISREG(st.st_mode) @@ -248,18 +243,14 @@ def exists_case(self, path: str, prefix: str) -> bool: return res def isdir(self, path: str) -> bool: - try: - st = self.stat(path) - except OSError: + st = self.stat_or_none(path) + if st is None: return False return stat.S_ISDIR(st.st_mode) def exists(self, path: str) -> bool: - try: - self.stat(path) - except FileNotFoundError: - return False - return True + st = self.stat_or_none(path) + return st is not None def read(self, path: str) -> bytes: if path in self.read_cache: @@ -269,7 +260,7 @@ def read(self, path: str) -> bytes: # Need to stat first so that the contents of file are from no # earlier instant than the mtime reported by self.stat(). - self.stat(path) + self.stat_or_none(path) dirname, basename = os.path.split(path) dirname = os.path.normpath(dirname) @@ -294,8 +285,10 @@ def hash_digest(self, path: str) -> str: return self.hash_cache[path] def samefile(self, f1: str, f2: str) -> bool: - s1 = self.stat(f1) - s2 = self.stat(f2) + s1 = self.stat_or_none(f1) + s2 = self.stat_or_none(f2) + if s1 is None or s2 is None: + return False return os.path.samestat(s1, s2) diff --git a/mypy/fswatcher.py b/mypy/fswatcher.py index a574a36a0cc5..97a62ca9f9f7 100644 --- a/mypy/fswatcher.py +++ b/mypy/fswatcher.py @@ -2,6 +2,7 @@ from __future__ import annotations +import os from typing import AbstractSet, Iterable, NamedTuple from mypy.fscache import FileSystemCache @@ -56,8 +57,7 @@ def remove_watched_paths(self, paths: Iterable[str]) -> None: del self._file_data[path] self._paths -= set(paths) - def _update(self, path: str) -> None: - st = self.fs.stat(path) + def _update(self, path: str, st: os.stat_result) -> None: hash_digest = self.fs.hash_digest(path) self._file_data[path] = FileData(st.st_mtime, st.st_size, hash_digest) @@ -65,9 +65,8 @@ def _find_changed(self, paths: Iterable[str]) -> AbstractSet[str]: changed = set() for path in paths: old = self._file_data[path] - try: - st = self.fs.stat(path) - except FileNotFoundError: + st = self.fs.stat_or_none(path) + if st is None: if old is not None: # File was deleted. changed.add(path) @@ -76,13 +75,13 @@ def _find_changed(self, paths: Iterable[str]) -> AbstractSet[str]: if old is None: # File is new. changed.add(path) - self._update(path) + self._update(path, st) # Round mtimes down, to match the mtimes we write to meta files elif st.st_size != old.st_size or int(st.st_mtime) != int(old.st_mtime): # Only look for changes if size or mtime has changed as an # optimization, since calculating hash is expensive. new_hash = self.fs.hash_digest(path) - self._update(path) + self._update(path, st) if st.st_size != old.st_size or new_hash != old.hash: # Changed file. changed.add(path) From 1a074b6b2434ae2f7bbf5f12d3b0db208136dca8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 14 Oct 2024 02:50:03 +0100 Subject: [PATCH 0807/1617] Better handling of generic functions in partial plugin (#17925) Fixes https://github.com/python/mypy/issues/17411 The fix is that we remove type variables that can never be inferred from the initial `check_call()` call. Actual diff is tiny, I just moved a bunch of code, since I need formal to actual mapping sooner now. --- mypy/plugins/functools.py | 62 ++++++++++++++++++----------- test-data/unit/check-functools.test | 31 ++++++++++++++- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index f09ea88f7162..4dfeb752b5d2 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -10,6 +10,7 @@ from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var from mypy.plugins.common import add_method_to_class +from mypy.typeops import get_all_type_vars from mypy.types import ( AnyType, CallableType, @@ -17,6 +18,7 @@ Overloaded, Type, TypeOfAny, + TypeVarType, UnboundType, UnionType, get_proper_type, @@ -164,21 +166,6 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - ctx.api.type_context[-1] = None wrapped_return = False - defaulted = fn_type.copy_modified( - arg_kinds=[ - ( - ArgKind.ARG_OPT - if k == ArgKind.ARG_POS - else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) - ) - for k in fn_type.arg_kinds - ], - ret_type=ret_type, - ) - if defaulted.line < 0: - # Make up a line number if we don't have one - defaulted.set_line(ctx.default_return_type) - # Flatten actual to formal mapping, since this is what check_call() expects. actual_args = [] actual_arg_kinds = [] @@ -199,6 +186,43 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - actual_arg_names.append(ctx.arg_names[i][j]) actual_types.append(ctx.arg_types[i][j]) + formal_to_actual = map_actuals_to_formals( + actual_kinds=actual_arg_kinds, + actual_names=actual_arg_names, + formal_kinds=fn_type.arg_kinds, + formal_names=fn_type.arg_names, + actual_arg_type=lambda i: actual_types[i], + ) + + # We need to remove any type variables that appear only in formals that have + # no actuals, to avoid eagerly binding them in check_call() below. + can_infer_ids = set() + for i, arg_type in enumerate(fn_type.arg_types): + if not formal_to_actual[i]: + continue + can_infer_ids.update({tv.id for tv in get_all_type_vars(arg_type)}) + + defaulted = fn_type.copy_modified( + arg_kinds=[ + ( + ArgKind.ARG_OPT + if k == ArgKind.ARG_POS + else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) + ) + for k in fn_type.arg_kinds + ], + ret_type=ret_type, + variables=[ + tv + for tv in fn_type.variables + # Keep TypeVarTuple/ParamSpec to avoid spurious errors on empty args. + if tv.id in can_infer_ids or not isinstance(tv, TypeVarType) + ], + ) + if defaulted.line < 0: + # Make up a line number if we don't have one + defaulted.set_line(ctx.default_return_type) + # Create a valid context for various ad-hoc inspections in check_call(). call_expr = CallExpr( callee=ctx.args[0][0], @@ -231,14 +255,6 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - return ctx.default_return_type bound = bound.copy_modified(ret_type=ret_type.args[0]) - formal_to_actual = map_actuals_to_formals( - actual_kinds=actual_arg_kinds, - actual_names=actual_arg_names, - formal_kinds=fn_type.arg_kinds, - formal_names=fn_type.arg_names, - actual_arg_type=lambda i: actual_types[i], - ) - partial_kinds = [] partial_types = [] partial_names = [] diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index bee30931a92b..ea98a902d14b 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -575,7 +575,6 @@ def bar(f: S) -> S: return f [builtins fixtures/primitives.pyi] - [case testFunctoolsPartialAbstractType] # flags: --python-version 3.9 from abc import ABC, abstractmethod @@ -597,7 +596,6 @@ def f2() -> None: partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method" [builtins fixtures/tuple.pyi] - [case testFunctoolsPartialSelfType] from functools import partial from typing_extensions import Self @@ -610,3 +608,32 @@ class A: factory = partial(cls, ts=0) return factory(msg=msg) [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarValues] +from functools import partial +from typing import TypeVar + +T = TypeVar("T", int, str) + +def f(x: int, y: T) -> T: + return y + +def g(x: T, y: int) -> T: + return x + +def h(x: T, y: T) -> T: + return x + +fp = partial(f, 1) +reveal_type(fp(1)) # N: Revealed type is "builtins.int" +reveal_type(fp("a")) # N: Revealed type is "builtins.str" +fp(object()) # E: Value of type variable "T" of "f" cannot be "object" + +gp = partial(g, 1) +reveal_type(gp(1)) # N: Revealed type is "builtins.int" +gp("a") # E: Argument 1 to "g" has incompatible type "str"; expected "int" + +hp = partial(h, 1) +reveal_type(hp(1)) # N: Revealed type is "builtins.int" +hp("a") # E: Argument 1 to "h" has incompatible type "str"; expected "int" +[builtins fixtures/tuple.pyi] From 395108d287f1d143423a830e22b313e405b872b3 Mon Sep 17 00:00:00 2001 From: Newbyte Date: Mon, 14 Oct 2024 10:24:46 +0200 Subject: [PATCH 0808/1617] [mypyc] Support ellipsis (...) expressions in class bodies (#17923) This can be used to declare concise custom exceptions, e.g. class UnknownReleaseError(ValueError): ... which otherwise probably would be written class UnknownReleaseError(ValueError): pass and is supported by CPython. Closes https://github.com/mypyc/mypyc/issues/1069 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: hauntsaninja --- mypyc/irbuild/classdef.py | 5 ++++- mypyc/test-data/run-classes.test | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 7e0a842b1b41..bcc9594adcb9 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -12,6 +12,7 @@ CallExpr, ClassDef, Decorator, + EllipsisExpr, ExpressionStmt, FuncDef, Lvalue, @@ -145,7 +146,9 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: continue with builder.catch_errors(stmt.line): cls_builder.add_method(get_func_def(stmt)) - elif isinstance(stmt, PassStmt): + elif isinstance(stmt, PassStmt) or ( + isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, EllipsisExpr) + ): continue elif isinstance(stmt, AssignmentStmt): if len(stmt.lvalues) != 1: diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 59617714f7e7..7c2998874f78 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -3,8 +3,13 @@ class Empty: pass def f(e: Empty) -> Empty: return e + +class EmptyEllipsis: ... + +def g(e: EmptyEllipsis) -> EmptyEllipsis: + return e [file driver.py] -from native import Empty, f +from native import Empty, EmptyEllipsis, f, g print(isinstance(Empty, type)) print(Empty) @@ -12,11 +17,22 @@ print(str(Empty())[:20]) e = Empty() print(f(e) is e) + +print(isinstance(EmptyEllipsis, type)) +print(EmptyEllipsis) +print(str(EmptyEllipsis())[:28]) + +e2 = EmptyEllipsis() +print(g(e2) is e2) [out] True + Date: Mon, 14 Oct 2024 03:51:41 -0500 Subject: [PATCH 0809/1617] [mypyc] Optimize calls to final classes (#17886) Fixes #9612 This change allows to gain more efficiency where classes are annotated with `@final` bypassing entirely the vtable for method calls and property accessors. For example: In ```python @final class Vector: __slots__ = ("_x", "_y") def __init__(self, x: i32, y: i32) -> None: self._x = x self._y = y @property def y(self) -> i32: return self._y def test_vector() -> None: v3 = Vector(1, 2) assert v3.y == 2 ``` The call will produce: ```c ... cpy_r_r6 = CPyDef_Vector___y(cpy_r_r0); ... ``` Instead of: ```c ... cpy_r_r1 = CPY_GET_ATTR(cpy_r_r0, CPyType_Vector, 2, farm_rush___engine___vectors2___VectorObject, int32_t); /* y */ ... ``` (which uses vtable) --- mypyc/codegen/emitclass.py | 1 + mypyc/codegen/emitfunc.py | 42 ++++++++++-------- mypyc/ir/class_ir.py | 10 +++-- mypyc/ir/rtypes.py | 2 +- mypyc/irbuild/ll_builder.py | 4 +- mypyc/irbuild/prepare.py | 6 ++- mypyc/irbuild/util.py | 11 ++++- mypyc/test-data/run-classes.test | 75 ++++++++++++++++++++++++++++++++ 8 files changed, 125 insertions(+), 26 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index ad95a1b0f323..3ab6932546a6 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -571,6 +571,7 @@ def generate_setup_for_class( emitter.emit_line("}") else: emitter.emit_line(f"self->vtable = {vtable_name};") + for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS): field = emitter.bitmap_field(i) emitter.emit_line(f"self->{field} = 0;") diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index d945a28d8481..6088fb06dd32 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -72,6 +72,7 @@ from mypyc.ir.pprint import generate_names_for_ir from mypyc.ir.rtypes import ( RArray, + RInstance, RStruct, RTuple, RType, @@ -362,20 +363,23 @@ def visit_get_attr(self, op: GetAttr) -> None: prefer_method = cl.is_trait and attr_rtype.error_overlap if cl.get_method(op.attr, prefer_method=prefer_method): # Properties are essentially methods, so use vtable access for them. - version = "_TRAIT" if cl.is_trait else "" - self.emit_line( - "%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */" - % ( - dest, - version, - obj, - self.emitter.type_struct_name(rtype.class_ir), - rtype.getter_index(op.attr), - rtype.struct_name(self.names), - self.ctype(rtype.attr_type(op.attr)), - op.attr, + if cl.is_method_final(op.attr): + self.emit_method_call(f"{dest} = ", op.obj, op.attr, []) + else: + version = "_TRAIT" if cl.is_trait else "" + self.emit_line( + "%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */" + % ( + dest, + version, + obj, + self.emitter.type_struct_name(rtype.class_ir), + rtype.getter_index(op.attr), + rtype.struct_name(self.names), + self.ctype(rtype.attr_type(op.attr)), + op.attr, + ) ) - ) else: # Otherwise, use direct or offset struct access. attr_expr = self.get_attr_expr(obj, op, decl_cl) @@ -529,11 +533,13 @@ def visit_call(self, op: Call) -> None: def visit_method_call(self, op: MethodCall) -> None: """Call native method.""" dest = self.get_dest_assign(op) - obj = self.reg(op.obj) + self.emit_method_call(dest, op.obj, op.method, op.args) - rtype = op.receiver_type + def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Value]) -> None: + obj = self.reg(op_obj) + rtype = op_obj.type + assert isinstance(rtype, RInstance) class_ir = rtype.class_ir - name = op.method method = rtype.class_ir.get_method(name) assert method is not None @@ -547,7 +553,7 @@ def visit_method_call(self, op: MethodCall) -> None: if method.decl.kind == FUNC_STATICMETHOD else [f"(PyObject *)Py_TYPE({obj})"] if method.decl.kind == FUNC_CLASSMETHOD else [obj] ) - args = ", ".join(obj_args + [self.reg(arg) for arg in op.args]) + args = ", ".join(obj_args + [self.reg(arg) for arg in op_args]) mtype = native_function_type(method, self.emitter) version = "_TRAIT" if rtype.class_ir.is_trait else "" if is_direct: @@ -567,7 +573,7 @@ def visit_method_call(self, op: MethodCall) -> None: rtype.struct_name(self.names), mtype, args, - op.method, + name, ) ) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 18f3cbcff987..94bf714b28d4 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -93,6 +93,7 @@ def __init__( is_generated: bool = False, is_abstract: bool = False, is_ext_class: bool = True, + is_final_class: bool = False, ) -> None: self.name = name self.module_name = module_name @@ -100,6 +101,7 @@ def __init__( self.is_generated = is_generated self.is_abstract = is_abstract self.is_ext_class = is_ext_class + self.is_final_class = is_final_class # An augmented class has additional methods separate from what mypyc generates. # Right now the only one is dataclasses. self.is_augmented = False @@ -199,7 +201,8 @@ def __repr__(self) -> str: "ClassIR(" "name={self.name}, module_name={self.module_name}, " "is_trait={self.is_trait}, is_generated={self.is_generated}, " - "is_abstract={self.is_abstract}, is_ext_class={self.is_ext_class}" + "is_abstract={self.is_abstract}, is_ext_class={self.is_ext_class}, " + "is_final_class={self.is_final_class}" ")".format(self=self) ) @@ -248,8 +251,7 @@ def has_method(self, name: str) -> bool: def is_method_final(self, name: str) -> bool: subs = self.subclasses() if subs is None: - # TODO: Look at the final attribute! - return False + return self.is_final_class if self.has_method(name): method_decl = self.method_decl(name) @@ -349,6 +351,7 @@ def serialize(self) -> JsonDict: "is_abstract": self.is_abstract, "is_generated": self.is_generated, "is_augmented": self.is_augmented, + "is_final_class": self.is_final_class, "inherits_python": self.inherits_python, "has_dict": self.has_dict, "allow_interpreted_subclasses": self.allow_interpreted_subclasses, @@ -404,6 +407,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR: ir.is_abstract = data["is_abstract"] ir.is_ext_class = data["is_ext_class"] ir.is_augmented = data["is_augmented"] + ir.is_final_class = data["is_final_class"] ir.inherits_python = data["inherits_python"] ir.has_dict = data["has_dict"] ir.allow_interpreted_subclasses = data["allow_interpreted_subclasses"] diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fecfaee5ef77..53e3cee74e56 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -64,7 +64,7 @@ class RType: @abstractmethod def accept(self, visitor: RTypeVisitor[T]) -> T: - raise NotImplementedError + raise NotImplementedError() def short_name(self) -> str: return short_name(self.name) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 0c9310e6a5ca..c98136ce06d2 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1889,7 +1889,7 @@ def primitive_op( # Does this primitive map into calling a Python C API # or an internal mypyc C API function? if desc.c_function_name: - # TODO: Generate PrimitiOps here and transform them into CallC + # TODO: Generate PrimitiveOps here and transform them into CallC # ops only later in the lowering pass c_desc = CFunctionDescription( desc.name, @@ -1908,7 +1908,7 @@ def primitive_op( ) return self.call_c(c_desc, args, line, result_type) - # This primitve gets transformed in a lowering pass to + # This primitive gets transformed in a lowering pass to # lower-level IR ops using a custom transform function. coerced = [] diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 29e06439abdd..40a40b79df49 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -81,7 +81,11 @@ def build_type_map( # references even if there are import cycles. for module, cdef in classes: class_ir = ClassIR( - cdef.name, module.fullname, is_trait(cdef), is_abstract=cdef.info.is_abstract + cdef.name, + module.fullname, + is_trait(cdef), + is_abstract=cdef.info.is_abstract, + is_final_class=cdef.info.is_final, ) class_ir.is_ext_class = is_extension_class(cdef) if class_ir.is_ext_class: diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index ed01a59d1214..e27e509ad7fa 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -27,10 +27,16 @@ UnaryExpr, Var, ) +from mypy.semanal import refers_to_fullname +from mypy.types import FINAL_DECORATOR_NAMES DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"} +def is_final_decorator(d: Expression) -> bool: + return refers_to_fullname(d, FINAL_DECORATOR_NAMES) + + def is_trait_decorator(d: Expression) -> bool: return isinstance(d, RefExpr) and d.fullname == "mypy_extensions.trait" @@ -119,7 +125,10 @@ def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]: def is_extension_class(cdef: ClassDef) -> bool: if any( - not is_trait_decorator(d) and not is_dataclass_decorator(d) and not get_mypyc_attr_call(d) + not is_trait_decorator(d) + and not is_dataclass_decorator(d) + and not get_mypyc_attr_call(d) + and not is_final_decorator(d) for d in cdef.decorators ): return False diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 7c2998874f78..49eb3028c9ee 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2519,3 +2519,78 @@ class C: def test_final_attribute() -> None: assert C.A == -1 assert C.a == [-1] + +[case testClassWithFinalDecorator] +from typing import final + +@final +class C: + def a(self) -> int: + return 1 + +def test_class_final_attribute() -> None: + assert C().a() == 1 + + +[case testClassWithFinalDecoratorCtor] +from typing import final + +@final +class C: + def __init__(self) -> None: + self.a = 1 + + def b(self) -> int: + return 2 + + @property + def c(self) -> int: + return 3 + +def test_class_final_attribute() -> None: + assert C().a == 1 + assert C().b() == 2 + assert C().c == 3 + +[case testClassWithFinalDecoratorInheritedWithProperties] +from typing import final + +class B: + def a(self) -> int: + return 2 + + @property + def b(self) -> int: + return self.a() + 2 + + @property + def c(self) -> int: + return 3 + +def test_class_final_attribute_basic() -> None: + assert B().a() == 2 + assert B().b == 4 + assert B().c == 3 + +@final +class C(B): + def a(self) -> int: + return 1 + + @property + def b(self) -> int: + return self.a() + 1 + +def fn(cl: B) -> int: + return cl.a() + +def test_class_final_attribute_inherited() -> None: + assert C().a() == 1 + assert fn(C()) == 1 + assert B().a() == 2 + assert fn(B()) == 2 + + assert B().b == 4 + assert C().b == 2 + assert B().c == 3 + assert C().c == 3 From 706680f9cadfebe77208f20be965e2714e2b39df Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 14 Oct 2024 10:26:32 +0100 Subject: [PATCH 0810/1617] Add one more 1.12 changelog item (#17936) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ded92b58daaf..dfefc690195c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -255,6 +255,7 @@ This feature was contributed by Ivan Levkivskyi (PR [17457](https://github.com/p * Improvements to `functools.partial` of types (Shantanu, PR [17898](https://github.com/python/mypy/pull/17898)) * Make ReadOnly TypedDict items covariant (Jukka Lehtosalo, PR [17904](https://github.com/python/mypy/pull/17904)) * Fix union callees with `functools.partial` (Jukka Lehtosalo, PR [17903](https://github.com/python/mypy/pull/17903)) + * Improve handling of generic functions with `functools.partial` (Ivan Levkivskyi, PR [17925](https://github.com/python/mypy/pull/17925)) ### Typeshed Updates From e6ced4866e825bea01d9158c96b12376defd408e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:07:23 +0200 Subject: [PATCH 0811/1617] [mypyc] Use PyGen_GetCode in gen_is_coroutine (#17931) Instead of copying the implementation of `_PyGen_GetCode` every time it changes in cpython, use the public `PyGen_GetCode` function. The current implementation would break for Python 3.14 as it has been changed upstream in https://github.com/python/cpython/pull/120835. --- mypyc/lib-rt/pythonsupport.h | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 5d595dba1a12..61929f512608 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -402,45 +402,15 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { PyObject_CallMethodObjArgs((self), (name), (arg), NULL) #endif -#if CPY_3_13_FEATURES - -// These are copied from genobject.c in Python 3.13 - -/* Returns a borrowed reference */ -static inline PyCodeObject * -_PyGen_GetCode(PyGenObject *gen) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); - return _PyFrame_GetCode(frame); -} - -static int -gen_is_coroutine(PyObject *o) -{ - if (PyGen_CheckExact(o)) { - PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); - if (code->co_flags & CO_ITERABLE_COROUTINE) { - return 1; - } - } - return 0; -} - -#elif CPY_3_12_FEATURES +#if CPY_3_12_FEATURES // These are copied from genobject.c in Python 3.12 -/* Returns a borrowed reference */ -static inline PyCodeObject * -_PyGen_GetCode(PyGenObject *gen) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); - return frame->f_code; -} - static int gen_is_coroutine(PyObject *o) { if (PyGen_CheckExact(o)) { - PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); + PyCodeObject *code = PyGen_GetCode((PyGenObject*)o); if (code->co_flags & CO_ITERABLE_COROUTINE) { return 1; } From c8ebc75bf36494d99afe731e216d08bbb4c06375 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:18:01 +0200 Subject: [PATCH 0812/1617] Update black to 24.8.0 (#17939) Update to 24.8.0 the last release with support for Python 3.8. https://github.com/psf/black/blob/24.8.0/CHANGES.md --- .pre-commit-config.yaml | 2 +- test-requirements.in | 2 +- test-requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7ff48051aad..14b6dc17b6d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.1.1 # must match test-requirements.txt + rev: 24.8.0 # must match test-requirements.txt hooks: - id: black exclude: '^(test-data/)' diff --git a/test-requirements.in b/test-requirements.in index 5a888811bfcd..686dbba3b4de 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,7 +4,7 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==24.3.0 # must match version in .pre-commit-config.yaml +black==24.8.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' diff --git a/test-requirements.txt b/test-requirements.txt index f4fb4a20cce7..6e89e473bcb2 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ # attrs==23.1.0 # via -r test-requirements.in -black==24.3.0 +black==24.8.0 # via -r test-requirements.in click==8.1.7 # via black From 5fba078a21952d6d21f18456cfbcce9a2d2a2062 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:18:59 +0200 Subject: [PATCH 0813/1617] Update ruff to 0.6.9 (#17940) https://github.com/astral-sh/ruff/blob/0.6.9/CHANGELOG.md --- .pre-commit-config.yaml | 2 +- mypy/fastparse.py | 2 +- mypy/ipc.py | 3 +-- mypy/server/astmerge.py | 2 +- mypy/stubtest.py | 2 +- mypy/util.py | 3 +-- pyproject.toml | 2 ++ test-requirements.in | 2 +- test-requirements.txt | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14b6dc17b6d8..4efed772396e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 # must match test-requirements.txt + rev: v0.6.9 # must match test-requirements.txt hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 726397adb849..bc5b1ba8e57a 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2033,7 +2033,7 @@ def visit_UnaryOp(self, n: UnaryOp) -> Type: if ( isinstance(typ, RawExpressionType) # Use type() because we do not want to allow bools. - and type(typ.literal_value) is int # noqa: E721 + and type(typ.literal_value) is int ): if isinstance(n.op, USub): typ.literal_value *= -1 diff --git a/mypy/ipc.py b/mypy/ipc.py index ab01f1b79e7d..991f9ac56652 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -17,9 +17,8 @@ if sys.platform == "win32": # This may be private, but it is needed for IPC on Windows, and is basically stable - import ctypes - import _winapi + import ctypes _IPCHandle = int diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 174c2922c767..5dc254422328 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -160,7 +160,7 @@ def replacement_map_from_symbol_table( ): new_node = new[name] if ( - type(new_node.node) == type(node.node) # noqa: E721 + type(new_node.node) == type(node.node) and new_node.node and node.node and new_node.node.fullname == node.node.fullname diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 65730828ad9f..756f90dccf2e 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -700,7 +700,7 @@ def _verify_arg_default_value( stub_default != runtime_arg.default # We want the types to match exactly, e.g. in case the stub has # True and the runtime has 1 (or vice versa). - or type(stub_default) is not type(runtime_arg.default) # noqa: E721 + or type(stub_default) is not type(runtime_arg.default) ) ): yield ( diff --git a/mypy/util.py b/mypy/util.py index 4b1b918b92e6..d2cba9b7a662 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -15,9 +15,8 @@ from typing_extensions import Literal try: - import curses - import _curses # noqa: F401 + import curses CURSES_ENABLED = True except ImportError: diff --git a/pyproject.toml b/pyproject.toml index 12a0dc109cd5..7e1556357998 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,8 +66,10 @@ ignore = [ "E2", # conflicts with black "E402", # module level import not at top of file "E501", # conflicts with black + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for isinstance checks "E731", # Do not assign a `lambda` expression, use a `def` "E741", # Ambiguous variable name + "UP031", # Use format specifiers instead of percent format "UP032", # 'f-string always preferable to format' is controversial "C416", # There are a few cases where it's nice to have names for the dict items ] diff --git a/test-requirements.in b/test-requirements.in index 686dbba3b4de..b008522edebc 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -12,6 +12,6 @@ psutil>=4.0 pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.2.0 # must match version in .pre-commit-config.yaml +ruff==0.6.9 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 diff --git a/test-requirements.txt b/test-requirements.txt index 6e89e473bcb2..56cebf95bfde 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -45,7 +45,7 @@ pytest-cov==4.1.0 # via -r test-requirements.in pytest-xdist==3.3.1 # via -r test-requirements.in -ruff==0.2.0 +ruff==0.6.9 # via -r test-requirements.in tomli==2.0.1 # via -r test-requirements.in From fc75ca76366d43b3083aabfc2bcde848adb44bfd Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:16:06 +0200 Subject: [PATCH 0814/1617] [mypyc] Fix wheel build for cp313-win (#17941) https://github.com/python/mypy/pull/17929 seems to have broken the wheel builder but only on Windows with Python 3.13. ``` C:\Users\runneradmin\AppData\Local\pypa\cibuildwheel\Cache\nuget-cpython\python.3.13.0\tools\include\internal\pycore_unicodeobject.h(262): error C2375: '_PyUnicode_Equal': redefinition; different linkage D:\a\mypy_mypyc-wheels\mypy_mypyc-wheels\mypy\mypyc\lib-rt\pythoncapi_compat.h(1540): note: see declaration of '_PyUnicode_Equal' C:\Users\runneradmin\AppData\Local\pypa\cibuildwheel\Cache\nuget-cpython\python.3.13.0\tools\include\internal\pycore_pyhash.h(24): error C2375: '_Py_HashBytes': redefinition; different linkage D:\a\mypy_mypyc-wheels\mypy_mypyc-wheels\mypy\mypyc\lib-rt\pythoncapi_compat.h(1567): note: see declaration of '_Py_HashBytes' ``` https://github.com/mypyc/mypy_mypyc-wheels/actions/runs/11327195039/job/31497783023#step:4:3766 MSVC seems to require that the signature matches the upstream one exactly. --- mypyc/lib-rt/pythoncapi_compat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index 34a84c969b6a..acaadf34bf2e 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -1537,7 +1537,7 @@ static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) } #if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) - extern int _PyUnicode_Equal(PyObject *str1, PyObject *str2); + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); return _PyUnicode_Equal(str1, str2); #elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) @@ -1564,7 +1564,7 @@ static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) { #if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) - extern Py_hash_t _Py_HashBytes(const void *src, Py_ssize_t len); + PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); return _Py_HashBytes(ptr, len); #else From f4f16673b9f6cb3c7752d8a01e52e0e371353b6b Mon Sep 17 00:00:00 2001 From: Gaurav Giri <64427471+gaurovgiri@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:21:20 +0545 Subject: [PATCH 0815/1617] fix: make pre_commit installable via test-requirements.txt (#17911) Fixes #17902 --- test-requirements.in | 1 + test-requirements.txt | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test-requirements.in b/test-requirements.in index b008522edebc..cb4c7704c482 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -15,3 +15,4 @@ pytest-cov>=2.10.0 ruff==0.6.9 # must match version in .pre-commit-config.yaml setuptools>=65.5.1 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 +pre_commit>=3.5.0 diff --git a/test-requirements.txt b/test-requirements.txt index 56cebf95bfde..26159b0b68eb 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,14 +8,22 @@ attrs==23.1.0 # via -r test-requirements.in black==24.8.0 # via -r test-requirements.in +cfgv==3.4.0 + # via pre-commit click==8.1.7 # via black coverage==7.3.2 # via pytest-cov +distlib==0.3.9 + # via virtualenv execnet==2.0.2 # via pytest-xdist filelock==3.12.4 - # via -r test-requirements.in + # via + # -r test-requirements.in + # virtualenv +identify==2.6.1 + # via pre-commit iniconfig==2.0.0 # via pytest lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" @@ -24,6 +32,8 @@ mypy-extensions==1.0.0 # via # -r mypy-requirements.txt # black +nodeenv==1.9.1 + # via pre-commit packaging==23.2 # via # black @@ -31,9 +41,13 @@ packaging==23.2 pathspec==0.11.2 # via black platformdirs==3.11.0 - # via black + # via + # black + # virtualenv pluggy==1.4.0 # via pytest +pre-commit==3.5.0 + # via -r test-requirements.in psutil==5.9.6 # via -r test-requirements.in pytest==8.1.1 @@ -45,6 +59,8 @@ pytest-cov==4.1.0 # via -r test-requirements.in pytest-xdist==3.3.1 # via -r test-requirements.in +pyyaml==6.0.2 + # via pre-commit ruff==0.6.9 # via -r test-requirements.in tomli==2.0.1 @@ -55,6 +71,8 @@ types-setuptools==68.2.0.0 # via -r build-requirements.txt typing-extensions==4.12.2 # via -r mypy-requirements.txt +virtualenv==20.26.6 + # via pre-commit # The following packages are considered to be unsafe in a requirements file: setuptools==70.0.0 From 740292ad2614c184dbcbfabbae994c17553abf16 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:21:52 +0200 Subject: [PATCH 0816/1617] Update test requirements (#17938) --- test-requirements.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 26159b0b68eb..4b10b0e55197 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in # -attrs==23.1.0 +attrs==24.2.0 # via -r test-requirements.in black==24.8.0 # via -r test-requirements.in @@ -12,13 +12,13 @@ cfgv==3.4.0 # via pre-commit click==8.1.7 # via black -coverage==7.3.2 +coverage==7.6.1 # via pytest-cov distlib==0.3.9 # via virtualenv -execnet==2.0.2 +execnet==2.1.1 # via pytest-xdist -filelock==3.12.4 +filelock==3.16.1 # via # -r test-requirements.in # virtualenv @@ -34,38 +34,38 @@ mypy-extensions==1.0.0 # black nodeenv==1.9.1 # via pre-commit -packaging==23.2 +packaging==24.1 # via # black # pytest -pathspec==0.11.2 +pathspec==0.12.1 # via black -platformdirs==3.11.0 +platformdirs==4.3.6 # via # black # virtualenv -pluggy==1.4.0 +pluggy==1.5.0 # via pytest pre-commit==3.5.0 # via -r test-requirements.in -psutil==5.9.6 +psutil==6.0.0 # via -r test-requirements.in -pytest==8.1.1 +pytest==8.3.3 # via # -r test-requirements.in # pytest-cov # pytest-xdist -pytest-cov==4.1.0 +pytest-cov==5.0.0 # via -r test-requirements.in -pytest-xdist==3.3.1 +pytest-xdist==3.6.1 # via -r test-requirements.in pyyaml==6.0.2 # via pre-commit ruff==0.6.9 # via -r test-requirements.in -tomli==2.0.1 +tomli==2.0.2 # via -r test-requirements.in -types-psutil==5.9.5.17 +types-psutil==6.0.0.20241011 # via -r build-requirements.txt types-setuptools==68.2.0.0 # via -r build-requirements.txt From 80b59139e11faf6ab3867dc8969db327f8861694 Mon Sep 17 00:00:00 2001 From: Kanishk Pachauri Date: Tue, 15 Oct 2024 01:53:15 +0530 Subject: [PATCH 0817/1617] [docs] fix the edit page buttton link in docs (#17933) Fixes #17870 The current docs `edit` the page button gives a 404. This PR edits the sphinx `config.py` and configures the edit button url to the `master` branch of this repository. --- docs/source/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index f8faa03a09b2..7f5fe9d43d5f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -107,6 +107,12 @@ # a list of builtin themes. html_theme = "furo" +html_theme_options = { + "source_repository": "https://github.com/python/mypy", + "source_branch": "master", + "source_directory": "docs/source", +} + # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. From 970428a18704ba938c7bf0ddef60321cc785e402 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 15 Oct 2024 00:00:29 +0200 Subject: [PATCH 0818/1617] Update setuptools to 75.1.0 (#17943) https://setuptools.pypa.io/en/stable/history.html#v75-1-0 Since setuptools `70.1` the `bdist_wheel` command is shipped with setuptools directly. It's no longer necessary to specify `wheel` as a build system requirement. --- .github/workflows/test.yml | 4 ++-- pyproject.toml | 3 +-- test-requirements.in | 2 +- test-requirements.txt | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ec8954bbe3e..9c35903f5f4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -183,7 +183,7 @@ jobs: echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))' echo os.cpu_count; python -c 'import os; print(os.cpu_count())' echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' - pip install setuptools==68.2.2 tox==4.11.0 + pip install setuptools==75.1.0 tox==4.11.0 - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} @@ -243,7 +243,7 @@ jobs: default: 3.11.1 command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');" - name: Install tox - run: pip install setuptools==68.2.2 tox==4.11.0 + run: pip install setuptools==75.1.0 tox==4.11.0 - name: Setup tox environment run: tox run -e py --notest - name: Test diff --git a/pyproject.toml b/pyproject.toml index 7e1556357998..06f83506ae4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,8 +3,7 @@ requires = [ # NOTE: this needs to be kept in sync with mypy-requirements.txt # and build-requirements.txt, because those are both needed for # self-typechecking :/ - "setuptools >= 40.6.2", - "wheel >= 0.30.0", + "setuptools >= 75.1.0", # the following is from mypy-requirements.txt/setup.py "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", diff --git a/test-requirements.in b/test-requirements.in index cb4c7704c482..e702da28acf1 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -13,6 +13,6 @@ pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 ruff==0.6.9 # must match version in .pre-commit-config.yaml -setuptools>=65.5.1 +setuptools>=75.1.0 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 pre_commit>=3.5.0 diff --git a/test-requirements.txt b/test-requirements.txt index 4b10b0e55197..ab3884b99f3b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -67,7 +67,7 @@ tomli==2.0.2 # via -r test-requirements.in types-psutil==6.0.0.20241011 # via -r build-requirements.txt -types-setuptools==68.2.0.0 +types-setuptools==75.1.0.20241014 # via -r build-requirements.txt typing-extensions==4.12.2 # via -r mypy-requirements.txt @@ -75,5 +75,5 @@ virtualenv==20.26.6 # via pre-commit # The following packages are considered to be unsafe in a requirements file: -setuptools==70.0.0 +setuptools==75.1.0 # via -r test-requirements.in From 676ed066e9cee31bc104910f7785cac005dda7f7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 15 Oct 2024 01:17:33 +0200 Subject: [PATCH 0819/1617] Update tox to 4.21.2 (#17946) https://tox.wiki/en/latest/changelog.html#v4-21-2-2024-10-03 --- .github/workflows/docs.yml | 2 +- .github/workflows/test.yml | 8 ++++---- tox.ini | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c6ed3cf1a08d..923d74a02f71 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,7 @@ jobs: with: python-version: '3.12' - name: Install tox - run: pip install tox==4.11.0 + run: pip install tox==4.21.2 - name: Setup tox environment run: tox run -e ${{ env.TOXENV }} --notest - name: Test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c35903f5f4a..4150d1968bfb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -128,8 +128,8 @@ jobs: name: ${{ matrix.name }} env: TOX_SKIP_MISSING_INTERPRETERS: False - # Rich (pip) - FORCE_COLOR: 1 + # Rich (pip) -- Disable color for windows + pytest + FORCE_COLOR: ${{ !(startsWith(matrix.os, 'windows-') && startsWith(matrix.toxenv, 'py')) && 1 || 0 }} # Tox PY_COLORS: 1 # Mypy (see https://github.com/python/mypy/issues/7771) @@ -183,7 +183,7 @@ jobs: echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))' echo os.cpu_count; python -c 'import os; print(os.cpu_count())' echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' - pip install setuptools==75.1.0 tox==4.11.0 + pip install setuptools==75.1.0 tox==4.21.2 - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} @@ -243,7 +243,7 @@ jobs: default: 3.11.1 command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');" - name: Install tox - run: pip install setuptools==75.1.0 tox==4.11.0 + run: pip install setuptools==75.1.0 tox==4.21.2 - name: Setup tox environment run: tox run -e py --notest - name: Test diff --git a/tox.ini b/tox.ini index c2abd05d7b6c..7f89b7d4f7ef 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ envlist = py310, py311, py312, + py313, docs, lint, type, From 284677e99071d783686978ca3bc6f40e1b7f25b8 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 15 Oct 2024 01:48:34 -0700 Subject: [PATCH 0820/1617] Use sha1 for hashing (#17953) This is a pretty small win, it's below the noise floor on macrobenchmark, but if you time the hashing specifically it saves about 100ms (0.5%) on `python -m mypy -c 'import torch' --no-incremental`. blake2b is slower --- mypy/util.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/util.py b/mypy/util.py index d2cba9b7a662..4b19adc7ab1f 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -534,9 +534,7 @@ def hash_digest(data: bytes) -> str: accidental collision, but we don't really care about any of the cryptographic properties. """ - # Once we drop Python 3.5 support, we should consider using - # blake2b, which is faster. - return hashlib.sha256(data).hexdigest() + return hashlib.sha1(data).hexdigest() def parse_gray_color(cup: bytes) -> str: From fea947a7b0f35d5fbe7b189bd25600c80fb7e8b1 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 15 Oct 2024 02:01:08 -0700 Subject: [PATCH 0821/1617] Let mypyc optimise os.path.join (#17949) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/python/mypy/issues/17948 There's one call site which has varargs that I leave as os.path.join, it doesn't show up on my profile. I do see the `endswith` on the profile, we could try `path[-1] == '/'` instead (could save a few dozen milliseconds) In my work environment, this is about a 10% speedup: ``` λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_6eddd3ab1/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python' Benchmark 1: /tmp/mypy_primer/timer_mypy_6eddd3ab1/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python Time (mean ± σ): 30.842 s ± 0.119 s [User: 26.383 s, System: 4.396 s] Range (min … max): 30.706 s … 30.927 s 3 runs ``` Compared to: ``` λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_88ae62b4a/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python' Benchmark 1: /tmp/mypy_primer/timer_mypy_88ae62b4a/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python Time (mean ± σ): 34.161 s ± 0.163 s [User: 29.818 s, System: 4.289 s] Range (min … max): 34.013 s … 34.336 s 3 runs ``` In the toy "long" environment mentioned in the issue, this is about a 7% speedup: ``` λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_6eddd3ab1/venv/bin/mypy -c "import torch" --no-incremental --python-executable long/bin/python' Benchmark 1: /tmp/mypy_primer/timer_mypy_6eddd3ab1/venv/bin/mypy -c "import torch" --no-incremental --python-executable long/bin/python Time (mean ± σ): 23.177 s ± 0.317 s [User: 20.265 s, System: 2.873 s] Range (min … max): 22.815 s … 23.407 s 3 runs ``` Compared to: ``` λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_88ae62b4a/venv/bin/mypy -c "import torch" --python-executable=long/bin/python --no-incremental' Benchmark 1: /tmp/mypy_primer/timer_mypy_88ae62b4a/venv/bin/mypy -c "import torch" --python-executable=long/bin/python --no-incremental Time (mean ± σ): 24.838 s ± 0.237 s [User: 22.038 s, System: 2.750 s] Range (min … max): 24.598 s … 25.073 s 3 runs ``` In the "clean" environment, this is a 1% speedup, but below the noise floor. --- mypy/modulefinder.py | 31 ++++++++++++++++--------------- mypy/util.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 452cfef20f4c..0cfe8f3b9d2f 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -22,6 +22,7 @@ from mypy.nodes import MypyFile from mypy.options import Options from mypy.stubinfo import approved_stub_package_exists +from mypy.util import os_path_join # Paths to be searched in find_module(). @@ -205,7 +206,7 @@ def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None: d = os.path.dirname(p) for _ in range(id.count(".")): if not any( - self.fscache.isfile(os.path.join(d, "__init__" + x)) for x in PYTHON_EXTENSIONS + self.fscache.isfile(os_path_join(d, "__init__" + x)) for x in PYTHON_EXTENSIONS ): return None d = os.path.dirname(d) @@ -249,7 +250,7 @@ def find_lib_path_dirs(self, id: str, lib_path: tuple[str, ...]) -> PackageDirs: dirs = [] for pathitem in self.get_toplevel_possibilities(lib_path, components[0]): # e.g., '/usr/lib/python3.4/foo/bar' - dir = os.path.normpath(os.path.join(pathitem, dir_chain)) + dir = os.path.normpath(os_path_join(pathitem, dir_chain)) if self.fscache.isdir(dir): dirs.append((dir, True)) return dirs @@ -320,8 +321,8 @@ def _find_module_non_stub_helper( plausible_match = False dir_path = pkg_dir for index, component in enumerate(components): - dir_path = os.path.join(dir_path, component) - if self.fscache.isfile(os.path.join(dir_path, "py.typed")): + dir_path = os_path_join(dir_path, component) + if self.fscache.isfile(os_path_join(dir_path, "py.typed")): return os.path.join(pkg_dir, *components[:-1]), index == 0 elif not plausible_match and ( self.fscache.isdir(dir_path) or self.fscache.isfile(dir_path + ".py") @@ -418,9 +419,9 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: # Third-party stub/typed packages for pkg_dir in self.search_paths.package_path: stub_name = components[0] + "-stubs" - stub_dir = os.path.join(pkg_dir, stub_name) + stub_dir = os_path_join(pkg_dir, stub_name) if fscache.isdir(stub_dir): - stub_typed_file = os.path.join(stub_dir, "py.typed") + stub_typed_file = os_path_join(stub_dir, "py.typed") stub_components = [stub_name] + components[1:] path = os.path.join(pkg_dir, *stub_components[:-1]) if fscache.isdir(path): @@ -430,7 +431,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: # Partial here means that mypy should look at the runtime # package if installed. if fscache.read(stub_typed_file).decode().strip() == "partial": - runtime_path = os.path.join(pkg_dir, dir_chain) + runtime_path = os_path_join(pkg_dir, dir_chain) third_party_inline_dirs.append((runtime_path, True)) # if the package is partial, we don't verify the module, as # the partial stub package may not have a __init__.pyi @@ -580,7 +581,7 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]: # Skip certain names altogether if name in ("__pycache__", "site-packages", "node_modules") or name.startswith("."): continue - subpath = os.path.join(package_path, name) + subpath = os_path_join(package_path, name) if self.options and matches_exclude( subpath, self.options.exclude, self.fscache, self.options.verbosity >= 2 @@ -590,8 +591,8 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]: if self.fscache.isdir(subpath): # Only recurse into packages if (self.options and self.options.namespace_packages) or ( - self.fscache.isfile(os.path.join(subpath, "__init__.py")) - or self.fscache.isfile(os.path.join(subpath, "__init__.pyi")) + self.fscache.isfile(os_path_join(subpath, "__init__.py")) + or self.fscache.isfile(os_path_join(subpath, "__init__.pyi")) ): seen.add(name) sources.extend(self.find_modules_recursive(module + "." + name)) @@ -636,7 +637,7 @@ def verify_module(fscache: FileSystemCache, id: str, path: str, prefix: str) -> for i in range(id.count(".")): path = os.path.dirname(path) if not any( - fscache.isfile_case(os.path.join(path, f"__init__{extension}"), prefix) + fscache.isfile_case(os_path_join(path, f"__init__{extension}"), prefix) for extension in PYTHON_EXTENSIONS ): return False @@ -651,7 +652,7 @@ def highest_init_level(fscache: FileSystemCache, id: str, path: str, prefix: str for i in range(id.count(".")): path = os.path.dirname(path) if any( - fscache.isfile_case(os.path.join(path, f"__init__{extension}"), prefix) + fscache.isfile_case(os_path_join(path, f"__init__{extension}"), prefix) for extension in PYTHON_EXTENSIONS ): level = i + 1 @@ -842,11 +843,11 @@ def load_stdlib_py_versions(custom_typeshed_dir: str | None) -> StdlibVersions: None means there is no maximum version. """ - typeshed_dir = custom_typeshed_dir or os.path.join(os.path.dirname(__file__), "typeshed") - stdlib_dir = os.path.join(typeshed_dir, "stdlib") + typeshed_dir = custom_typeshed_dir or os_path_join(os.path.dirname(__file__), "typeshed") + stdlib_dir = os_path_join(typeshed_dir, "stdlib") result = {} - versions_path = os.path.join(stdlib_dir, "VERSIONS") + versions_path = os_path_join(stdlib_dir, "VERSIONS") assert os.path.isfile(versions_path), (custom_typeshed_dir, versions_path, __file__) with open(versions_path) as f: for line in f: diff --git a/mypy/util.py b/mypy/util.py index 4b19adc7ab1f..74bf08c9d6de 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -416,6 +416,23 @@ def is_sub_path(path1: str, path2: str) -> bool: return pathlib.Path(path2) in pathlib.Path(path1).parents +if sys.platform == "linux" or sys.platform == "darwin": + + def os_path_join(path: str, b: str) -> str: + # Based off of os.path.join, but simplified to str-only, 2 args and mypyc can compile it. + if b.startswith("/") or not path: + return b + elif path.endswith("/"): + return path + b + else: + return path + "/" + b + +else: + + def os_path_join(a: str, p: str) -> str: + return os.path.join(a, p) + + def hard_exit(status: int = 0) -> None: """Kill the current process without fully cleaning up. From 7f081542fb0e0c0d0e05dbcfa80d8e20b0fd460f Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 15 Oct 2024 02:24:54 -0700 Subject: [PATCH 0822/1617] Update changelog for 1.11.* releases (#17957) --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfefc690195c..4522465a484d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -503,6 +503,15 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +### Mypy 1.11.1 + * Fix `RawExpressionType.accept` crash with `--cache-fine-grained` (Anders Kaseorg, PR [17588](https://github.com/python/mypy/pull/17588)) + * Fix PEP 604 isinstance caching (Shantanu, PR [17563](https://github.com/python/mypy/pull/17563)) + * Fix `typing.TypeAliasType` being undefined on python < 3.12 (Nikita Sobolev, PR [17558](https://github.com/python/mypy/pull/17558)) + * Fix `types.GenericAlias` lookup crash (Shantanu, PR [17543](https://github.com/python/mypy/pull/17543)) + +### Mypy 1.11.2 + * Alternative fix for a union-like literal string (Ivan Levkivskyi, PR [17639](https://github.com/python/mypy/pull/17639)) + * Unwrap `TypedDict` item types before storing (Ivan Levkivskyi, PR [17640](https://github.com/python/mypy/pull/17640)) ### Acknowledgements Thanks to all mypy contributors who contributed to this release: From eb816b05ce70f8bf3c86ecf85a6ac64c10a17aeb Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 15 Oct 2024 07:58:42 -0700 Subject: [PATCH 0823/1617] Use fast path in modulefinder more often (#17950) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/python/mypy/issues/17948 This is about 1.06x faster on `mypy -c 'import torch'` (in both the clean and openai environments) - 19.094 -> 17.896 - 34.161 -> 32.214 ``` λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_36738b392/venv/bin/mypy -c "import torch" --no-incremental --python-executable clean/bin/python' Benchmark 1: /tmp/mypy_primer/timer_mypy_36738b392/venv/bin/mypy -c "import torch" --no-incremental --python-executable clean/bin/python Time (mean ± σ): 17.896 s ± 0.130 s [User: 16.472 s, System: 1.408 s] Range (min … max): 17.757 s … 18.014 s 3 runs λ hyperfine -w 1 -M 3 '/tmp/mypy_primer/timer_mypy_36738b392/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python' Benchmark 1: /tmp/mypy_primer/timer_mypy_36738b392/venv/bin/mypy -c "import torch" --no-incremental --python-executable /opt/oai/bin/python Time (mean ± σ): 32.214 s ± 0.106 s [User: 29.468 s, System: 2.722 s] Range (min … max): 32.098 s … 32.305 s 3 runs ``` --- mypy/build.py | 8 +++++--- mypy/modulefinder.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 964da5aac8b0..043b52f0a241 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -664,7 +664,7 @@ def __init__( for module in CORE_BUILTIN_MODULES: if options.use_builtins_fixtures: continue - path = self.find_module_cache.find_module(module) + path = self.find_module_cache.find_module(module, fast_path=True) if not isinstance(path, str): raise CompileError( [f"Failed to find builtin module {module}, perhaps typeshed is broken?"] @@ -2725,7 +2725,9 @@ def exist_added_packages(suppressed: list[str], manager: BuildManager, options: def find_module_simple(id: str, manager: BuildManager) -> str | None: """Find a filesystem path for module `id` or `None` if not found.""" - x = find_module_with_reason(id, manager) + t0 = time.time() + x = manager.find_module_cache.find_module(id, fast_path=True) + manager.add_stats(find_module_time=time.time() - t0, find_module_calls=1) if isinstance(x, ModuleNotFoundReason): return None return x @@ -2734,7 +2736,7 @@ def find_module_simple(id: str, manager: BuildManager) -> str | None: def find_module_with_reason(id: str, manager: BuildManager) -> ModuleSearchResult: """Find a filesystem path for module `id` or the reason it can't be found.""" t0 = time.time() - x = manager.find_module_cache.find_module(id) + x = manager.find_module_cache.find_module(id, fast_path=False) manager.add_stats(find_module_time=time.time() - t0, find_module_calls=1) return x diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 0cfe8f3b9d2f..59a71025f71e 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -557,7 +557,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: return ModuleNotFoundReason.NOT_FOUND def find_modules_recursive(self, module: str) -> list[BuildSource]: - module_path = self.find_module(module) + module_path = self.find_module(module, fast_path=True) if isinstance(module_path, ModuleNotFoundReason): return [] sources = [BuildSource(module_path, module, None)] From 10f3ce5584cc4053fc9c7fe1eee1b34c7d13f0ed Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:47:19 -0700 Subject: [PATCH 0824/1617] Fix generator comprehension in meet.py (#17969) Since mypyc will treat these as list comprehensions --- mypy/meet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index 9f5c2d72a8cb..d614ecc45a57 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -243,8 +243,8 @@ def is_enum_overlapping_union(x: ProperType, y: ProperType) -> bool: and x.type.is_enum and isinstance(y, UnionType) and any( - isinstance(p, LiteralType) and x.type == p.fallback.type - for p in (get_proper_type(z) for z in y.relevant_items()) + isinstance(p := get_proper_type(z), LiteralType) and x.type == p.fallback.type + for z in y.relevant_items() ) ) From fbae4325e5fa2b4cf8502402ee0440e7870f933d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 16 Oct 2024 15:36:09 +0100 Subject: [PATCH 0825/1617] Fix crash when showing partially analyzed type in error message (#17961) Fixes https://github.com/python/mypy/issues/17954 People say something about cache invalidation being one of the hardest problems... --- mypy/semanal.py | 8 ++++++-- test-data/unit/check-recursive-types.test | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 95efe2b0f30c..5332c98c8f0d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4014,8 +4014,10 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) if self.options.disallow_any_unimported and has_any_from_unimported_type(res): - self.msg.unimported_type_becomes_any("Type alias target", res, s) - res = make_any_non_unimported(res) + # Only show error message once, when the type is fully analyzed. + if not has_placeholder(res): + self.msg.unimported_type_becomes_any("Type alias target", res, s) + res = make_any_non_unimported(res) # Note: with the new (lazy) type alias representation we only need to set no_args to True # if the expected number of arguments is non-zero, so that aliases like `A = List` work # but not aliases like `A = TypeAliasType("A", List)` as these need explicit type params. @@ -4069,6 +4071,8 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: existing.node.alias_tvars = alias_tvars existing.node.no_args = no_args updated = True + # Invalidate recursive status cache in case it was previously set. + existing.node._is_recursive = None else: # Otherwise just replace existing placeholder with type alias. existing.node = alias_node diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index ac1ea0c0035a..4d7af98204fb 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -1006,3 +1006,11 @@ ta: Tuple[A] p: Proto p = ta [builtins fixtures/tuple.pyi] + +[case testRecursiveAliasesWithAnyUnimported] +# flags: --disallow-any-unimported +from typing import Callable +from bogus import Foo # type: ignore + +A = Callable[[Foo, "B"], Foo] # E: Type alias target becomes "Callable[[Any, B], Any]" due to an unfollowed import +B = Callable[[Foo, A], Foo] # E: Type alias target becomes "Callable[[Any, A], Any]" due to an unfollowed import From f63fdb391ac9314dc093f3750c4747c4a63c41f2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:52:16 +0200 Subject: [PATCH 0826/1617] [mypyc] Replace deprecated _PyDict_GetItemStringWithError (#17930) `_PyDict_GetItemStringWithError ` was deprecated for Python 3.14 in https://github.com/python/cpython/pull/119855. Use `PyDict_GetItemStringRef` instead. It was added in 3.13 but is available via `pythoncapi_compat.h`. https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemStringRef --- mypyc/lib-rt/getargs.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 1bc2f5b02ba8..4f2f8aa0be83 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -247,14 +247,14 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, #endif if (!skip) { if (i < nargs && i < max) { - current_arg = PyTuple_GET_ITEM(args, i); + current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); } else if (nkwargs && i >= pos) { - current_arg = _PyDict_GetItemStringWithError(kwargs, kwlist[i]); - if (current_arg) { + int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); + if (res == 1) { --nkwargs; } - else if (PyErr_Occurred()) { + else if (res == -1) { return 0; } } @@ -265,6 +265,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (current_arg) { PyObject **p = va_arg(*p_va, PyObject **); *p = current_arg; + Py_DECREF(current_arg); format++; continue; } @@ -370,8 +371,11 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, Py_ssize_t j; /* make sure there are no arguments given by name and position */ for (i = pos; i < bound_pos_args && i < len; i++) { - current_arg = _PyDict_GetItemStringWithError(kwargs, kwlist[i]); - if (unlikely(current_arg != NULL)) { + int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); + if (res == 1) { + Py_DECREF(current_arg); + } + else if (unlikely(res == 0)) { /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%s') " @@ -381,7 +385,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, kwlist[i], i+1); goto latefail; } - else if (unlikely(PyErr_Occurred() != NULL)) { + else if (unlikely(res == -1)) { goto latefail; } } From c1f2db34fbec3b8347f9037703a804a4cd427c33 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:03:25 -0700 Subject: [PATCH 0827/1617] Use orjson instead of json, when available (#17955) For `mypy -c 'import torch'`, the cache load time goes from 0.44s to 0.25s as measured by manager's data_json_load_time. If I time dump times specifically, I see a saving of 0.65s to 0.07s. Overall, a pretty reasonable perf win -- should we make it a required dependency? See also https://github.com/python/mypy/issues/3456 --- misc/apply-cache-diff.py | 12 +++++----- misc/diff-cache.py | 14 ++++++------ mypy/build.py | 45 +++++++++++++++---------------------- mypy/metastore.py | 39 +++++++++++++------------------- mypy/util.py | 31 ++++++++++++++++++++++++- mypyc/codegen/emitmodule.py | 8 +++---- 6 files changed, 81 insertions(+), 68 deletions(-) diff --git a/misc/apply-cache-diff.py b/misc/apply-cache-diff.py index 29c55247de92..8ede9766bd06 100644 --- a/misc/apply-cache-diff.py +++ b/misc/apply-cache-diff.py @@ -8,13 +8,13 @@ from __future__ import annotations import argparse -import json import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mypy.metastore import FilesystemMetadataStore, MetadataStore, SqliteMetadataStore +from mypy.util import json_dumps, json_loads def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: @@ -26,10 +26,10 @@ def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: cache = make_cache(cache_dir, sqlite) - with open(diff_file) as f: - diff = json.load(f) + with open(diff_file, "rb") as f: + diff = json_loads(f.read()) - old_deps = json.loads(cache.read("@deps.meta.json")) + old_deps = json_loads(cache.read("@deps.meta.json")) for file, data in diff.items(): if data is None: @@ -37,10 +37,10 @@ def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: else: cache.write(file, data) if file.endswith(".meta.json") and "@deps" not in file: - meta = json.loads(data) + meta = json_loads(data) old_deps["snapshot"][meta["id"]] = meta["hash"] - cache.write("@deps.meta.json", json.dumps(old_deps)) + cache.write("@deps.meta.json", json_dumps(old_deps)) cache.commit() diff --git a/misc/diff-cache.py b/misc/diff-cache.py index 15d3e5a83983..8441caf81304 100644 --- a/misc/diff-cache.py +++ b/misc/diff-cache.py @@ -8,7 +8,6 @@ from __future__ import annotations import argparse -import json import os import sys from collections import defaultdict @@ -17,6 +16,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mypy.metastore import FilesystemMetadataStore, MetadataStore, SqliteMetadataStore +from mypy.util import json_dumps, json_loads def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: @@ -33,7 +33,7 @@ def merge_deps(all: dict[str, set[str]], new: dict[str, set[str]]) -> None: def load(cache: MetadataStore, s: str) -> Any: data = cache.read(s) - obj = json.loads(data) + obj = json_loads(data) if s.endswith(".meta.json"): # For meta files, zero out the mtimes and sort the # dependencies to avoid spurious conflicts @@ -73,7 +73,7 @@ def main() -> None: type_misses: dict[str, int] = defaultdict(int) type_hits: dict[str, int] = defaultdict(int) - updates: dict[str, str | None] = {} + updates: dict[str, bytes | None] = {} deps1: dict[str, set[str]] = {} deps2: dict[str, set[str]] = {} @@ -96,7 +96,7 @@ def main() -> None: # so we can produce a much smaller direct diff of them. if ".deps." not in s: if obj2 is not None: - updates[s] = json.dumps(obj2) + updates[s] = json_dumps(obj2) else: updates[s] = None elif obj2: @@ -122,7 +122,7 @@ def main() -> None: merge_deps(new_deps, root_deps) new_deps_json = {k: list(v) for k, v in new_deps.items() if v} - updates["@root.deps.json"] = json.dumps(new_deps_json) + updates["@root.deps.json"] = json_dumps(new_deps_json) # Drop updates to deps.meta.json for size reasons. The diff # applier will manually fix it up. @@ -136,8 +136,8 @@ def main() -> None: print("hits", type_hits) print("misses", type_misses) - with open(args.output, "w") as f: - json.dump(updates, f) + with open(args.output, "wb") as f: + f.write(json_dumps(updates)) if __name__ == "__main__": diff --git a/mypy/build.py b/mypy/build.py index 043b52f0a241..fd1c63868a69 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -95,6 +95,7 @@ from mypy.stubinfo import legacy_bundled_packages, non_bundled_packages, stub_distribution_name from mypy.types import Type from mypy.typestate import reset_global_state, type_state +from mypy.util import json_dumps, json_loads from mypy.version import __version__ # Switch to True to produce debug output related to fine-grained incremental @@ -858,7 +859,7 @@ def load_fine_grained_deps(self, id: str) -> dict[str, set[str]]: t0 = time.time() if id in self.fg_deps_meta: # TODO: Assert deps file wasn't changed. - deps = json.loads(self.metastore.read(self.fg_deps_meta[id]["path"])) + deps = json_loads(self.metastore.read(self.fg_deps_meta[id]["path"])) else: deps = {} val = {k: set(v) for k, v in deps.items()} @@ -911,8 +912,8 @@ def stats_summary(self) -> Mapping[str, object]: return self.stats -def deps_to_json(x: dict[str, set[str]]) -> str: - return json.dumps({k: list(v) for k, v in x.items()}, separators=(",", ":")) +def deps_to_json(x: dict[str, set[str]]) -> bytes: + return json_dumps({k: list(v) for k, v in x.items()}) # File for storing metadata about all the fine-grained dependency caches @@ -980,7 +981,7 @@ def write_deps_cache( meta = {"snapshot": meta_snapshot, "deps_meta": fg_deps_meta} - if not metastore.write(DEPS_META_FILE, json.dumps(meta, separators=(",", ":"))): + if not metastore.write(DEPS_META_FILE, json_dumps(meta)): manager.log(f"Error writing fine-grained deps meta JSON file {DEPS_META_FILE}") error = True @@ -1048,7 +1049,7 @@ def generate_deps_for_cache(manager: BuildManager, graph: Graph) -> dict[str, di def write_plugins_snapshot(manager: BuildManager) -> None: """Write snapshot of versions and hashes of currently active plugins.""" - snapshot = json.dumps(manager.plugins_snapshot, separators=(",", ":")) + snapshot = json_dumps(manager.plugins_snapshot) if not manager.metastore.write(PLUGIN_SNAPSHOT_FILE, snapshot): manager.errors.set_file(_cache_dir_prefix(manager.options), None, manager.options) manager.errors.report(0, 0, "Error writing plugins snapshot", blocker=True) @@ -1079,8 +1080,8 @@ def read_quickstart_file( # just ignore it. raw_quickstart: dict[str, Any] = {} try: - with open(options.quickstart_file) as f: - raw_quickstart = json.load(f) + with open(options.quickstart_file, "rb") as f: + raw_quickstart = json_loads(f.read()) quickstart = {} for file, (x, y, z) in raw_quickstart.items(): @@ -1148,10 +1149,10 @@ def _load_json_file( manager.add_stats(metastore_read_time=time.time() - t0) # Only bother to compute the log message if we are logging it, since it could be big if manager.verbosity() >= 2: - manager.trace(log_success + data.rstrip()) + manager.trace(log_success + data.rstrip().decode()) try: t1 = time.time() - result = json.loads(data) + result = json_loads(data) manager.add_stats(data_json_load_time=time.time() - t1) except json.JSONDecodeError: manager.errors.set_file(file, None, manager.options) @@ -1343,8 +1344,8 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No # So that plugins can return data with tuples in it without # things silently always invalidating modules, we round-trip # the config data. This isn't beautiful. - plugin_data = json.loads( - json.dumps(manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=True))) + plugin_data = json_loads( + json_dumps(manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=True))) ) if m.plugin_data != plugin_data: manager.log(f"Metadata abandoned for {id}: plugin configuration differs") @@ -1478,10 +1479,7 @@ def validate_meta( "ignore_all": meta.ignore_all, "plugin_data": meta.plugin_data, } - if manager.options.debug_cache: - meta_str = json.dumps(meta_dict, indent=2, sort_keys=True) - else: - meta_str = json.dumps(meta_dict, separators=(",", ":")) + meta_bytes = json_dumps(meta_dict, manager.options.debug_cache) meta_json, _, _ = get_cache_names(id, path, manager.options) manager.log( "Updating mtime for {}: file {}, meta {}, mtime {}".format( @@ -1489,7 +1487,7 @@ def validate_meta( ) ) t1 = time.time() - manager.metastore.write(meta_json, meta_str) # Ignore errors, just an optimization. + manager.metastore.write(meta_json, meta_bytes) # Ignore errors, just an optimization. manager.add_stats(validate_update_time=time.time() - t1, validate_munging_time=t1 - t0) return meta @@ -1507,13 +1505,6 @@ def compute_hash(text: str) -> str: return hash_digest(text.encode("utf-8")) -def json_dumps(obj: Any, debug_cache: bool) -> str: - if debug_cache: - return json.dumps(obj, indent=2, sort_keys=True) - else: - return json.dumps(obj, sort_keys=True, separators=(",", ":")) - - def write_cache( id: str, path: str, @@ -1566,8 +1557,8 @@ def write_cache( # Serialize data and analyze interface data = tree.serialize() - data_str = json_dumps(data, manager.options.debug_cache) - interface_hash = compute_hash(data_str) + data_bytes = json_dumps(data, manager.options.debug_cache) + interface_hash = hash_digest(data_bytes) plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False)) @@ -1591,7 +1582,7 @@ def write_cache( manager.trace(f"Interface for {id} is unchanged") else: manager.trace(f"Interface for {id} has changed") - if not metastore.write(data_json, data_str): + if not metastore.write(data_json, data_bytes): # Most likely the error is the replace() call # (see https://github.com/python/mypy/issues/3215). manager.log(f"Error writing data JSON file {data_json}") @@ -3568,4 +3559,4 @@ def write_undocumented_ref_info( assert not ref_info_file.startswith(".") deps_json = get_undocumented_ref_info_json(state.tree, type_map) - metastore.write(ref_info_file, json.dumps(deps_json, separators=(",", ":"))) + metastore.write(ref_info_file, json_dumps(deps_json)) diff --git a/mypy/metastore.py b/mypy/metastore.py index 4caa7d7f0534..c9139045e3b1 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -33,14 +33,14 @@ def getmtime(self, name: str) -> float: """ @abstractmethod - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: """Read the contents of a metadata entry. Raises FileNotFound if the entry does not exist. """ @abstractmethod - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: """Write a metadata entry. If mtime is specified, set it as the mtime of the entry. Otherwise, @@ -86,16 +86,16 @@ def getmtime(self, name: str) -> float: return int(os.path.getmtime(os.path.join(self.cache_dir_prefix, name))) - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: assert os.path.normpath(name) != os.path.abspath(name), "Don't use absolute paths!" if not self.cache_dir_prefix: raise FileNotFoundError() - with open(os.path.join(self.cache_dir_prefix, name)) as f: + with open(os.path.join(self.cache_dir_prefix, name), "rb") as f: return f.read() - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: assert os.path.normpath(name) != os.path.abspath(name), "Don't use absolute paths!" if not self.cache_dir_prefix: @@ -105,7 +105,7 @@ def write(self, name: str, data: str, mtime: float | None = None) -> bool: tmp_filename = path + "." + random_string() try: os.makedirs(os.path.dirname(path), exist_ok=True) - with open(tmp_filename, "w") as f: + with open(tmp_filename, "wb") as f: f.write(data) os.replace(tmp_filename, path) if mtime is not None: @@ -135,15 +135,13 @@ def list_all(self) -> Iterable[str]: SCHEMA = """ -CREATE TABLE IF NOT EXISTS files ( +CREATE TABLE IF NOT EXISTS files2 ( path TEXT UNIQUE NOT NULL, mtime REAL, - data TEXT + data BLOB ); -CREATE INDEX IF NOT EXISTS path_idx on files(path); +CREATE INDEX IF NOT EXISTS path_idx on files2(path); """ -# No migrations yet -MIGRATIONS: list[str] = [] def connect_db(db_file: str) -> sqlite3.Connection: @@ -151,11 +149,6 @@ def connect_db(db_file: str) -> sqlite3.Connection: db = sqlite3.dbapi2.connect(db_file) db.executescript(SCHEMA) - for migr in MIGRATIONS: - try: - db.executescript(migr) - except sqlite3.OperationalError: - pass return db @@ -176,7 +169,7 @@ def _query(self, name: str, field: str) -> Any: if not self.db: raise FileNotFoundError() - cur = self.db.execute(f"SELECT {field} FROM files WHERE path = ?", (name,)) + cur = self.db.execute(f"SELECT {field} FROM files2 WHERE path = ?", (name,)) results = cur.fetchall() if not results: raise FileNotFoundError() @@ -188,12 +181,12 @@ def getmtime(self, name: str) -> float: assert isinstance(mtime, float) return mtime - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: data = self._query(name, "data") - assert isinstance(data, str) + assert isinstance(data, bytes) return data - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: import sqlite3 if not self.db: @@ -202,7 +195,7 @@ def write(self, name: str, data: str, mtime: float | None = None) -> bool: if mtime is None: mtime = time.time() self.db.execute( - "INSERT OR REPLACE INTO files(path, mtime, data) VALUES(?, ?, ?)", + "INSERT OR REPLACE INTO files2(path, mtime, data) VALUES(?, ?, ?)", (name, mtime, data), ) except sqlite3.OperationalError: @@ -213,7 +206,7 @@ def remove(self, name: str) -> None: if not self.db: raise FileNotFoundError() - self.db.execute("DELETE FROM files WHERE path = ?", (name,)) + self.db.execute("DELETE FROM files2 WHERE path = ?", (name,)) def commit(self) -> None: if self.db: @@ -221,5 +214,5 @@ def commit(self) -> None: def list_all(self) -> Iterable[str]: if self.db: - for row in self.db.execute("SELECT path FROM files"): + for row in self.db.execute("SELECT path FROM files2"): yield row[0] diff --git a/mypy/util.py b/mypy/util.py index 74bf08c9d6de..8ec979af27e1 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -4,6 +4,7 @@ import hashlib import io +import json import os import pathlib import re @@ -11,9 +12,15 @@ import sys import time from importlib import resources as importlib_resources -from typing import IO, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar +from typing import IO, Any, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar from typing_extensions import Literal +orjson: Any +try: + import orjson # type: ignore[import-not-found, no-redef, unused-ignore] +except ImportError: + orjson = None + try: import _curses # noqa: F401 import curses @@ -888,3 +895,25 @@ def quote_docstring(docstr: str) -> str: return f"''{docstr_repr}''" else: return f'""{docstr_repr}""' + + +def json_dumps(obj: object, debug: bool = False) -> bytes: + if orjson is not None: + if debug: + return orjson.dumps(obj, option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS) # type: ignore[no-any-return] + else: + # TODO: If we don't sort keys here, testIncrementalInternalScramble fails + # We should document exactly what is going on there + return orjson.dumps(obj, option=orjson.OPT_SORT_KEYS) # type: ignore[no-any-return] + + if debug: + return json.dumps(obj, indent=2, sort_keys=True).encode("utf-8") + else: + # See above for sort_keys comment + return json.dumps(obj, sort_keys=True, separators=(",", ":")).encode("utf-8") + + +def json_loads(data: bytes) -> Any: + if orjson is not None: + return orjson.loads(data) + return json.loads(data) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 1d8708912de5..b5e0a37f0cca 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -24,7 +24,7 @@ from mypy.nodes import MypyFile from mypy.options import Options from mypy.plugin import Plugin, ReportConfigContext -from mypy.util import hash_digest +from mypy.util import hash_digest, json_dumps from mypyc.codegen.cstring import c_string_initializer from mypyc.codegen.emit import Emitter, EmitterContext, HeaderDeclaration, c_array_initializer from mypyc.codegen.emitclass import generate_class, generate_class_type_decl @@ -154,7 +154,7 @@ def report_config_data(self, ctx: ReportConfigContext) -> tuple[str | None, list ir_data = json.loads(ir_json) # Check that the IR cache matches the metadata cache - if compute_hash(meta_json) != ir_data["meta_hash"]: + if hash_digest(meta_json) != ir_data["meta_hash"]: return None # Check that all of the source files are present and as @@ -369,11 +369,11 @@ def write_cache( newpath = get_state_ir_cache_name(st) ir_data = { "ir": module.serialize(), - "meta_hash": compute_hash(meta_data), + "meta_hash": hash_digest(meta_data), "src_hashes": hashes[group_map[id]], } - result.manager.metastore.write(newpath, json.dumps(ir_data, separators=(",", ":"))) + result.manager.metastore.write(newpath, json_dumps(ir_data)) result.manager.metastore.commit() From 6728848d8f360fe4c763ee51c16fa524815444fd Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:21:45 -0700 Subject: [PATCH 0828/1617] Fix cache-convert (#17974) Tested via: ``` set -x rm -rf .mypy_cache time python -m mypy --config-file mypy_self_check.ini -p mypy python misc/convert-cache.py --to-sqlite .mypy_cache/3.8 time python -m mypy --config-file mypy_self_check.ini -p mypy --sqlite-cache time python -m mypy --config-file mypy_self_check.ini -p mypy --sqlite-cache ``` --- mypy/metastore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/metastore.py b/mypy/metastore.py index c9139045e3b1..21fb8730f2c9 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -131,7 +131,7 @@ def list_all(self) -> Iterable[str]: for dir, _, files in os.walk(self.cache_dir_prefix): dir = os.path.relpath(dir, self.cache_dir_prefix) for file in files: - yield os.path.join(dir, file) + yield os.path.normpath(os.path.join(dir, file)) SCHEMA = """ From ddeb5bdd643d8f2b1c222b95dcb4f89e3ccc6958 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:12:22 -0700 Subject: [PATCH 0829/1617] Use kw-only args for member access booleans (#17975) Gets hard to know what's what at call site --- mypy/checker.py | 8 ++++---- mypy/checkexpr.py | 32 ++++++++++++++++---------------- mypy/checkmember.py | 35 ++++++++++++++++++----------------- mypy/checkpattern.py | 16 ++++++++-------- 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index ca35144456fe..c646ebf3736b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7570,10 +7570,10 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: name, typ, TempNode(AnyType(TypeOfAny.special_form)), - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=typ, chk=self, # This is not a real attribute lookup so don't mess with deferring nodes. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0754b1db7141..8d1833a772e2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1504,10 +1504,10 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str member, typ, e, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=object_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3302,10 +3302,10 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type e.name, original_type, e, - is_lvalue, - False, - False, - self.msg, + is_lvalue=is_lvalue, + is_super=False, + is_operator=False, + msg=self.msg, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3326,10 +3326,10 @@ def analyze_external_member_access( member, base_type, context, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3809,10 +3809,10 @@ def check_method_call_by_name( method, base_type, context, - False, - False, - True, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=True, + msg=self.msg, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 4448072d52c3..e6f7570c3df2 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -87,6 +87,7 @@ class MemberContext: def __init__( self, + *, is_lvalue: bool, is_super: bool, is_operator: bool, @@ -126,16 +127,16 @@ def copy_modified( original_type: Type | None = None, ) -> MemberContext: mx = MemberContext( - self.is_lvalue, - self.is_super, - self.is_operator, - self.original_type, - self.context, - self.msg, - self.chk, - self.self_type, - self.module_symbol_table, - self.no_deferral, + is_lvalue=self.is_lvalue, + is_super=self.is_super, + is_operator=self.is_operator, + original_type=self.original_type, + context=self.context, + msg=self.msg, + chk=self.chk, + self_type=self.self_type, + module_symbol_table=self.module_symbol_table, + no_deferral=self.no_deferral, ) if messages is not None: mx.msg = messages @@ -152,11 +153,11 @@ def analyze_member_access( name: str, typ: Type, context: Context, + *, is_lvalue: bool, is_super: bool, is_operator: bool, msg: MessageBuilder, - *, original_type: Type, chk: mypy.checker.TypeChecker, override_info: TypeInfo | None = None, @@ -190,12 +191,12 @@ def analyze_member_access( are not available via the type object directly) """ mx = MemberContext( - is_lvalue, - is_super, - is_operator, - original_type, - context, - msg, + is_lvalue=is_lvalue, + is_super=is_super, + is_operator=is_operator, + original_type=original_type, + context=context, + msg=msg, chk=chk, self_type=self_type, module_symbol_table=module_symbol_table, diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index fa23dfb5f453..6b4fa35f9c49 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -594,10 +594,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: "__match_args__", typ, o, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=typ, chk=self.chk, ) @@ -660,10 +660,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: keyword, narrowed_type, pattern, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=new_type, chk=self.chk, ) From 1be3a8b40153d28414fee8049b435d69db90e325 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:21:47 -0700 Subject: [PATCH 0830/1617] Speed up stubs suggestions (#17965) See https://github.com/python/mypy/issues/17948 This is starting to show up on profiles - 1.01x faster on clean (below noise) - 1.02x faster on long - 1.02x faster on openai - 1.01x faster on openai incremental I had a dumb bug that was preventing the optimisation for a while, I'll see if I can make it even faster. Currently it's a small improvement We could also get rid of the legacy stuff in mypy 2.0 --- mypy/build.py | 21 +++++--------- mypy/modulefinder.py | 5 ++-- mypy/stubinfo.py | 60 ++++++++++++++++++++++++++++++--------- mypy/test/teststubinfo.py | 42 ++++++++++++++++++++++++--- 4 files changed, 93 insertions(+), 35 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index fd1c63868a69..52c11e065b63 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -92,7 +92,7 @@ from mypy.plugins.default import DefaultPlugin from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor from mypy.stats import dump_type_stats -from mypy.stubinfo import legacy_bundled_packages, non_bundled_packages, stub_distribution_name +from mypy.stubinfo import is_module_from_legacy_bundled_package, stub_distribution_name from mypy.types import Type from mypy.typestate import reset_global_state, type_state from mypy.util import json_dumps, json_loads @@ -2658,17 +2658,13 @@ def find_module_and_diagnose( ignore_missing_imports = options.ignore_missing_imports - id_components = id.split(".") # Don't honor a global (not per-module) ignore_missing_imports # setting for modules that used to have bundled stubs, as # otherwise updating mypy can silently result in new false # negatives. (Unless there are stubs but they are incomplete.) global_ignore_missing_imports = manager.options.ignore_missing_imports if ( - any( - ".".join(id_components[:i]) in legacy_bundled_packages - for i in range(len(id_components), 0, -1) - ) + is_module_from_legacy_bundled_package(id) and global_ignore_missing_imports and not options.ignore_missing_imports_per_module and result is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED @@ -2789,18 +2785,15 @@ def module_not_found( code = codes.IMPORT errors.report(line, 0, msg.format(module=target), code=code) - components = target.split(".") - for i in range(len(components), 0, -1): - module = ".".join(components[:i]) - if module in legacy_bundled_packages or module in non_bundled_packages: - break - + dist = stub_distribution_name(target) for note in notes: if "{stub_dist}" in note: - note = note.format(stub_dist=stub_distribution_name(module)) + assert dist is not None + note = note.format(stub_dist=dist) errors.report(line, 0, note, severity="note", only_once=True, code=code) if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: - manager.missing_stub_packages.add(stub_distribution_name(module)) + assert dist is not None + manager.missing_stub_packages.add(dist) errors.set_import_context(save_import_context) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 59a71025f71e..94d8a5d59e1f 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -331,9 +331,8 @@ def _find_module_non_stub_helper( # If this is not a directory then we can't traverse further into it if not self.fscache.isdir(dir_path): break - for i in range(len(components), 0, -1): - if approved_stub_package_exists(".".join(components[:i])): - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + if approved_stub_package_exists(".".join(components)): + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED if plausible_match: return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 9d8dfbe43f37..0ec64b037fee 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -1,22 +1,53 @@ from __future__ import annotations -def is_legacy_bundled_package(prefix: str) -> bool: - return prefix in legacy_bundled_packages +def is_module_from_legacy_bundled_package(module: str) -> bool: + top_level = module.split(".", 1)[0] + return top_level in legacy_bundled_packages -def approved_stub_package_exists(prefix: str) -> bool: - return is_legacy_bundled_package(prefix) or prefix in non_bundled_packages +def approved_stub_package_exists(module: str) -> bool: + top_level = module.split(".", 1)[0] + if top_level in legacy_bundled_packages: + return True + if top_level in non_bundled_packages_flat: + return True + if top_level in non_bundled_packages_namespace: + namespace = non_bundled_packages_namespace[top_level] + components = module.split(".") + for i in range(len(components), 0, -1): + module = ".".join(components[:i]) + if module in namespace: + return True + return False -def stub_distribution_name(prefix: str) -> str: - return legacy_bundled_packages.get(prefix) or non_bundled_packages[prefix] +def stub_distribution_name(module: str) -> str | None: + top_level = module.split(".", 1)[0] + + dist = legacy_bundled_packages.get(top_level) + if dist: + return dist + dist = non_bundled_packages_flat.get(top_level) + if dist: + return dist + + if top_level in non_bundled_packages_namespace: + namespace = non_bundled_packages_namespace[top_level] + components = module.split(".") + for i in range(len(components), 0, -1): + module = ".".join(components[:i]) + dist = namespace.get(module) + if dist: + return dist + + return None # Stubs for these third-party packages used to be shipped with mypy. # # Map package name to PyPI stub distribution name. -legacy_bundled_packages = { +legacy_bundled_packages: dict[str, str] = { "aiofiles": "types-aiofiles", "bleach": "types-bleach", "boto": "types-boto", @@ -32,7 +63,6 @@ def stub_distribution_name(prefix: str) -> str: "docutils": "types-docutils", "first": "types-first", "gflags": "types-python-gflags", - "google.protobuf": "types-protobuf", "markdown": "types-Markdown", "mock": "types-mock", "OpenSSL": "types-pyOpenSSL", @@ -66,20 +96,17 @@ def stub_distribution_name(prefix: str) -> str: # include packages that have a release that includes PEP 561 type # information. # -# Package name can have one or two components ('a' or 'a.b'). -# # Note that these packages are omitted for now: # pika: typeshed's stubs are on PyPI as types-pika-ts. # types-pika already exists on PyPI, and is more complete in many ways, # but is a non-typeshed stubs package. -non_bundled_packages = { +non_bundled_packages_flat: dict[str, str] = { "MySQLdb": "types-mysqlclient", "PIL": "types-Pillow", "PyInstaller": "types-pyinstaller", "Xlib": "types-python-xlib", "aws_xray_sdk": "types-aws-xray-sdk", "babel": "types-babel", - "backports.ssl_match_hostname": "types-backports.ssl_match_hostname", "braintree": "types-braintree", "bs4": "types-beautifulsoup4", "bugbear": "types-flake8-bugbear", @@ -107,7 +134,6 @@ def stub_distribution_name(prefix: str) -> str: "flask_migrate": "types-Flask-Migrate", "fpdf": "types-fpdf2", "gdb": "types-gdb", - "google.cloud.ndb": "types-google-cloud-ndb", "hdbcli": "types-hdbcli", "html5lib": "types-html5lib", "httplib2": "types-httplib2", @@ -123,7 +149,6 @@ def stub_distribution_name(prefix: str) -> str: "oauthlib": "types-oauthlib", "openpyxl": "types-openpyxl", "opentracing": "types-opentracing", - "paho.mqtt": "types-paho-mqtt", "parsimonious": "types-parsimonious", "passlib": "types-passlib", "passpy": "types-passpy", @@ -171,3 +196,10 @@ def stub_distribution_name(prefix: str) -> str: "pandas": "pandas-stubs", # https://github.com/pandas-dev/pandas-stubs "lxml": "lxml-stubs", # https://github.com/lxml/lxml-stubs } + + +non_bundled_packages_namespace: dict[str, dict[str, str]] = { + "backports": {"backports.ssl_match_hostname": "types-backports.ssl_match_hostname"}, + "google": {"google.cloud.ndb": "types-google-cloud-ndb", "google.protobuf": "types-protobuf"}, + "paho": {"paho.mqtt": "types-paho-mqtt"}, +} diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py index eccee90244f3..10ce408e7023 100644 --- a/mypy/test/teststubinfo.py +++ b/mypy/test/teststubinfo.py @@ -2,11 +2,45 @@ import unittest -from mypy.stubinfo import is_legacy_bundled_package +from mypy.stubinfo import ( + approved_stub_package_exists, + is_module_from_legacy_bundled_package, + legacy_bundled_packages, + non_bundled_packages_flat, + stub_distribution_name, +) class TestStubInfo(unittest.TestCase): def test_is_legacy_bundled_packages(self) -> None: - assert not is_legacy_bundled_package("foobar_asdf") - assert is_legacy_bundled_package("pycurl") - assert is_legacy_bundled_package("dataclasses") + assert not is_module_from_legacy_bundled_package("foobar_asdf") + assert not is_module_from_legacy_bundled_package("PIL") + assert is_module_from_legacy_bundled_package("pycurl") + assert is_module_from_legacy_bundled_package("dataclasses") + + def test_approved_stub_package_exists(self) -> None: + assert not approved_stub_package_exists("foobar_asdf") + assert approved_stub_package_exists("pycurl") + assert approved_stub_package_exists("babel") + assert approved_stub_package_exists("google.cloud.ndb") + assert approved_stub_package_exists("google.cloud.ndb.submodule") + assert not approved_stub_package_exists("google.cloud.unknown") + assert approved_stub_package_exists("google.protobuf") + assert approved_stub_package_exists("google.protobuf.submodule") + assert not approved_stub_package_exists("google") + + def test_stub_distribution_name(self) -> None: + assert stub_distribution_name("foobar_asdf") is None + assert stub_distribution_name("pycurl") == "types-pycurl" + assert stub_distribution_name("babel") == "types-babel" + assert stub_distribution_name("google.cloud.ndb") == "types-google-cloud-ndb" + assert stub_distribution_name("google.cloud.ndb.submodule") == "types-google-cloud-ndb" + assert stub_distribution_name("google.cloud.unknown") is None + assert stub_distribution_name("google.protobuf") == "types-protobuf" + assert stub_distribution_name("google.protobuf.submodule") == "types-protobuf" + assert stub_distribution_name("google") is None + + def test_period_in_top_level(self) -> None: + for packages in (non_bundled_packages_flat, legacy_bundled_packages): + for top_level_module in packages: + assert "." not in top_level_module From c201a187be998ec9e9c179004e0cd28dc2c86137 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:52:20 -0700 Subject: [PATCH 0831/1617] Make is_sub_path faster (#17962) See #17948 - 1.01x faster on clean - 1.06x faster on long - 1.04x faster on openai - 1.26x faster on openai incremental --- mypy/build.py | 9 ++++----- mypy/modulefinder.py | 9 +++++++-- mypy/util.py | 24 ++++++++++++++++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 52c11e065b63..ac6471d2383f 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -59,7 +59,7 @@ get_mypy_comments, hash_digest, is_stub_package_file, - is_sub_path, + is_sub_path_normabs, is_typeshed_file, module_prefix, read_py_file, @@ -3528,10 +3528,9 @@ def is_silent_import_module(manager: BuildManager, path: str) -> bool: if manager.options.no_silence_site_packages: return False # Silence errors in site-package dirs and typeshed - return any( - is_sub_path(path, dir) - for dir in manager.search_paths.package_path + manager.search_paths.typeshed_path - ) + if any(is_sub_path_normabs(path, dir) for dir in manager.search_paths.package_path): + return True + return any(is_sub_path_normabs(path, dir) for dir in manager.search_paths.typeshed_path) def write_undocumented_ref_info( diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 94d8a5d59e1f..49c39a9ce91c 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -668,10 +668,13 @@ def mypy_path() -> list[str]: def default_lib_path( data_dir: str, pyversion: tuple[int, int], custom_typeshed_dir: str | None ) -> list[str]: - """Return default standard library search paths.""" + """Return default standard library search paths. Guaranteed to be normalised.""" + + data_dir = os.path.abspath(data_dir) path: list[str] = [] if custom_typeshed_dir: + custom_typeshed_dir = os.path.abspath(custom_typeshed_dir) typeshed_dir = os.path.join(custom_typeshed_dir, "stdlib") mypy_extensions_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-extensions") versions_file = os.path.join(typeshed_dir, "VERSIONS") @@ -711,7 +714,7 @@ def default_lib_path( @functools.lru_cache(maxsize=None) def get_search_dirs(python_executable: str | None) -> tuple[list[str], list[str]]: - """Find package directories for given python. + """Find package directories for given python. Guaranteed to return absolute paths. This runs a subprocess call, which generates a list of the directories in sys.path. To avoid repeatedly calling a subprocess (which can be slow!) we @@ -773,6 +776,7 @@ def compute_search_paths( root_dir = os.getenv("MYPY_TEST_PREFIX", None) if not root_dir: root_dir = os.path.dirname(os.path.dirname(__file__)) + root_dir = os.path.abspath(root_dir) lib_path.appendleft(os.path.join(root_dir, "test-data", "unit", "lib-stub")) # alt_lib_path is used by some tests to bypass the normal lib_path mechanics. # If we don't have one, grab directories of source files. @@ -829,6 +833,7 @@ def compute_search_paths( return SearchPaths( python_path=tuple(reversed(python_path)), mypy_path=tuple(mypypath), + # package_path and typeshed_path must be normalised and absolute via os.path.abspath package_path=tuple(sys_path + site_packages), typeshed_path=tuple(lib_path), ) diff --git a/mypy/util.py b/mypy/util.py index 8ec979af27e1..2eac2a86dfd0 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -6,7 +6,6 @@ import io import json import os -import pathlib import re import shutil import sys @@ -418,9 +417,26 @@ def replace_object_state( pass -def is_sub_path(path1: str, path2: str) -> bool: - """Given two paths, return if path1 is a sub-path of path2.""" - return pathlib.Path(path2) in pathlib.Path(path1).parents +def is_sub_path_normabs(path: str, dir: str) -> bool: + """Given two paths, return if path is a sub-path of dir. + + Moral equivalent of: Path(dir) in Path(path).parents + + Similar to the pathlib version: + - Treats paths case-sensitively + - Does not fully handle unnormalised paths (e.g. paths with "..") + - Does not handle a mix of absolute and relative paths + Unlike the pathlib version: + - Fast + - On Windows, assumes input has been slash normalised + - Handles even fewer unnormalised paths (e.g. paths with "." and "//") + + As a result, callers should ensure that inputs have had os.path.abspath called on them + (note that os.path.abspath will normalise) + """ + if not dir.endswith(os.sep): + dir += os.sep + return path.startswith(dir) if sys.platform == "linux" or sys.platform == "darwin": From 946c1bf424f0102e36156efe5f8149cf5d37c4a4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 17 Oct 2024 07:44:31 +0200 Subject: [PATCH 0832/1617] Sync typeshed (#17971) Source commit: https://github.com/python/typeshed/commit/2370b8b9d194d4a468907df8952ef0dd7daff187 --- ...e-of-LiteralString-in-builtins-13743.patch | 27 +- mypy/typeshed/stdlib/VERSIONS | 4 + mypy/typeshed/stdlib/_ast.pyi | 1807 ++------------- mypy/typeshed/stdlib/_asyncio.pyi | 120 + mypy/typeshed/stdlib/_csv.pyi | 17 +- mypy/typeshed/stdlib/_ctypes.pyi | 5 +- mypy/typeshed/stdlib/_curses.pyi | 31 +- mypy/typeshed/stdlib/_decimal.pyi | 265 +-- mypy/typeshed/stdlib/_io.pyi | 195 ++ mypy/typeshed/stdlib/_operator.pyi | 42 +- mypy/typeshed/stdlib/_socket.pyi | 20 +- mypy/typeshed/stdlib/_sqlite3.pyi | 312 +++ mypy/typeshed/stdlib/_ssl.pyi | 292 +++ mypy/typeshed/stdlib/_weakref.pyi | 31 +- mypy/typeshed/stdlib/ast.pyi | 2015 +++++++++++++++-- mypy/typeshed/stdlib/asyncio/events.pyi | 11 +- mypy/typeshed/stdlib/asyncio/futures.pyi | 47 +- mypy/typeshed/stdlib/asyncio/streams.pyi | 4 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 74 +- mypy/typeshed/stdlib/builtins.pyi | 7 +- mypy/typeshed/stdlib/bz2.pyi | 2 +- mypy/typeshed/stdlib/code.pyi | 6 +- mypy/typeshed/stdlib/collections/__init__.pyi | 15 +- .../stdlib/concurrent/futures/process.pyi | 5 +- mypy/typeshed/stdlib/contextlib.pyi | 14 +- mypy/typeshed/stdlib/csv.pyi | 11 +- mypy/typeshed/stdlib/curses/__init__.pyi | 9 +- mypy/typeshed/stdlib/curses/panel.pyi | 8 +- mypy/typeshed/stdlib/curses/textpad.pyi | 6 +- mypy/typeshed/stdlib/decimal.pyi | 258 ++- mypy/typeshed/stdlib/email/_policybase.pyi | 45 +- mypy/typeshed/stdlib/email/feedparser.pyi | 10 +- mypy/typeshed/stdlib/email/generator.pyi | 55 +- mypy/typeshed/stdlib/email/message.pyi | 14 +- mypy/typeshed/stdlib/email/parser.pyi | 10 +- mypy/typeshed/stdlib/email/policy.pyi | 38 +- mypy/typeshed/stdlib/enum.pyi | 15 +- mypy/typeshed/stdlib/fileinput.pyi | 6 +- mypy/typeshed/stdlib/hmac.pyi | 7 +- mypy/typeshed/stdlib/http/client.pyi | 5 +- mypy/typeshed/stdlib/http/cookiejar.pyi | 4 +- mypy/typeshed/stdlib/io.pyi | 233 +- mypy/typeshed/stdlib/ipaddress.pyi | 4 +- mypy/typeshed/stdlib/logging/config.pyi | 2 +- mypy/typeshed/stdlib/mmap.pyi | 4 +- .../stdlib/multiprocessing/connection.pyi | 46 +- .../stdlib/multiprocessing/context.pyi | 7 +- .../stdlib/multiprocessing/managers.pyi | 6 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- .../stdlib/multiprocessing/reduction.pyi | 4 +- mypy/typeshed/stdlib/operator.pyi | 104 +- mypy/typeshed/stdlib/os/__init__.pyi | 27 +- mypy/typeshed/stdlib/pickle.pyi | 14 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 6 +- mypy/typeshed/stdlib/re.pyi | 11 +- mypy/typeshed/stdlib/shlex.pyi | 2 +- mypy/typeshed/stdlib/socket.pyi | 16 +- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 466 +++- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 760 ++----- mypy/typeshed/stdlib/sre_constants.pyi | 9 +- mypy/typeshed/stdlib/ssl.pyi | 102 +- mypy/typeshed/stdlib/tarfile.pyi | 117 +- mypy/typeshed/stdlib/tokenize.pyi | 2 +- mypy/typeshed/stdlib/traceback.pyi | 4 +- mypy/typeshed/stdlib/types.pyi | 2 + mypy/typeshed/stdlib/unittest/mock.pyi | 4 +- mypy/typeshed/stdlib/unittest/suite.pyi | 2 +- mypy/typeshed/stdlib/weakref.pyi | 55 +- .../stdlib/xml/parsers/expat/__init__.pyi | 6 + mypy/typeshed/stdlib/xmlrpc/client.pyi | 2 +- mypy/typeshed/stdlib/xmlrpc/server.pyi | 5 +- mypy/typeshed/stdlib/zoneinfo/__init__.pyi | 61 +- mypy/typeshed/stdlib/zoneinfo/_common.pyi | 13 + mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi | 13 + test-data/unit/pythoneval.test | 22 +- 75 files changed, 4655 insertions(+), 3349 deletions(-) create mode 100644 mypy/typeshed/stdlib/_asyncio.pyi create mode 100644 mypy/typeshed/stdlib/_io.pyi create mode 100644 mypy/typeshed/stdlib/_sqlite3.pyi create mode 100644 mypy/typeshed/stdlib/_ssl.pyi create mode 100644 mypy/typeshed/stdlib/zoneinfo/_common.pyi create mode 100644 mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch index 683b0c322b71..91e255242ee9 100644 --- a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch +++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch @@ -1,17 +1,17 @@ -From 3ec9b878d6bbe3fae64a508a62372f10a886406f Mon Sep 17 00:00:00 2001 +From b4259edd94188f9e4cc77a22e768eea183a32053 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH] Remove use of LiteralString in builtins (#13743) --- - mypy/typeshed/stdlib/builtins.pyi | 95 ------------------------------- - 1 file changed, 95 deletions(-) + mypy/typeshed/stdlib/builtins.pyi | 100 +----------------------------- + 1 file changed, 1 insertion(+), 99 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index 53e00ec6a..bad3250ef 100644 +index 63c53a5f6..d55042b56 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi -@@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 +@@ -63,7 +63,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, @@ -19,7 +19,7 @@ index 53e00ec6a..bad3250ef 100644 ParamSpec, Self, TypeAlias, -@@ -435,31 +434,16 @@ class str(Sequence[str]): +@@ -438,31 +437,16 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... @@ -51,7 +51,7 @@ index 53e00ec6a..bad3250ef 100644 def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, mapping: _FormatMapMapping, /) -> str: ... def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... -@@ -475,99 +459,35 @@ class str(Sequence[str]): +@@ -478,99 +462,35 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... @@ -151,7 +151,7 @@ index 53e00ec6a..bad3250ef 100644 def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload -@@ -578,9 +498,6 @@ class str(Sequence[str]): +@@ -581,39 +501,21 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... @@ -161,8 +161,13 @@ index 53e00ec6a..bad3250ef 100644 def __add__(self, value: str, /) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] -@@ -589,25 +506,13 @@ class str(Sequence[str]): - def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: str, /) -> bool: ... +- @overload +- def __getitem__(self: LiteralString, key: SupportsIndex | slice, /) -> LiteralString: ... +- @overload +- def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... # type: ignore[misc] ++ def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... def __gt__(self, value: str, /) -> bool: ... def __hash__(self) -> int: ... - @overload @@ -188,5 +193,5 @@ index 53e00ec6a..bad3250ef 100644 def __getnewargs__(self) -> tuple[str]: ... -- -2.45.2 +2.47.0 diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index dfed62f694fc..ed23ee6ddcea 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -20,6 +20,7 @@ __future__: 3.0- __main__: 3.0- _ast: 3.0- +_asyncio: 3.0- _bisect: 3.0- _bootlocale: 3.4-3.9 _codecs: 3.0- @@ -37,6 +38,7 @@ _imp: 3.0- _interpchannels: 3.13- _interpqueues: 3.13- _interpreters: 3.13- +_io: 3.0- _json: 3.0- _locale: 3.0- _lsprof: 3.0- @@ -50,6 +52,8 @@ _pydecimal: 3.5- _random: 3.0- _sitebuiltins: 3.4- _socket: 3.0- # present in 3.0 at runtime, but not in typeshed +_sqlite3: 3.0- +_ssl: 3.0- _stat: 3.4- _thread: 3.0- _threading_local: 3.0- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 1dbceac428c1..8dc1bcbea32c 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,1676 +1,149 @@ import sys -import typing_extensions -from typing import Any, ClassVar, Generic, Literal, TypedDict, overload -from typing_extensions import Self, Unpack +from ast import ( + AST as AST, + Add as Add, + And as And, + AnnAssign as AnnAssign, + Assert as Assert, + Assign as Assign, + AsyncFor as AsyncFor, + AsyncFunctionDef as AsyncFunctionDef, + AsyncWith as AsyncWith, + Attribute as Attribute, + AugAssign as AugAssign, + Await as Await, + BinOp as BinOp, + BitAnd as BitAnd, + BitOr as BitOr, + BitXor as BitXor, + BoolOp as BoolOp, + Break as Break, + Call as Call, + ClassDef as ClassDef, + Compare as Compare, + Constant as Constant, + Continue as Continue, + Del as Del, + Delete as Delete, + Dict as Dict, + DictComp as DictComp, + Div as Div, + Eq as Eq, + ExceptHandler as ExceptHandler, + Expr as Expr, + Expression as Expression, + FloorDiv as FloorDiv, + For as For, + FormattedValue as FormattedValue, + FunctionDef as FunctionDef, + FunctionType as FunctionType, + GeneratorExp as GeneratorExp, + Global as Global, + Gt as Gt, + GtE as GtE, + If as If, + IfExp as IfExp, + Import as Import, + ImportFrom as ImportFrom, + In as In, + Interactive as Interactive, + Invert as Invert, + Is as Is, + IsNot as IsNot, + JoinedStr as JoinedStr, + Lambda as Lambda, + List as List, + ListComp as ListComp, + Load as Load, + LShift as LShift, + Lt as Lt, + LtE as LtE, + MatMult as MatMult, + Mod as Mod, + Module as Module, + Mult as Mult, + Name as Name, + NamedExpr as NamedExpr, + Nonlocal as Nonlocal, + Not as Not, + NotEq as NotEq, + NotIn as NotIn, + Or as Or, + Pass as Pass, + Pow as Pow, + Raise as Raise, + Return as Return, + RShift as RShift, + Set as Set, + SetComp as SetComp, + Slice as Slice, + Starred as Starred, + Store as Store, + Sub as Sub, + Subscript as Subscript, + Try as Try, + Tuple as Tuple, + TypeIgnore as TypeIgnore, + UAdd as UAdd, + UnaryOp as UnaryOp, + USub as USub, + While as While, + With as With, + Yield as Yield, + YieldFrom as YieldFrom, + alias as alias, + arg as arg, + arguments as arguments, + boolop as boolop, + cmpop as cmpop, + comprehension as comprehension, + excepthandler as excepthandler, + expr as expr, + expr_context as expr_context, + keyword as keyword, + mod as mod, + operator as operator, + stmt as stmt, + type_ignore as type_ignore, + unaryop as unaryop, + withitem as withitem, +) +from typing import Literal -PyCF_ONLY_AST: Literal[1024] -PyCF_TYPE_COMMENTS: Literal[4096] -PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] - -if sys.version_info >= (3, 13): - PyCF_OPTIMIZED_AST: Literal[33792] - -# Used for node end positions in constructor keyword arguments -_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) - -# Alias used for fields that must always be valid identifiers -# A string `x` counts as a valid identifier if both the following are True -# (1) `x.isidentifier()` evaluates to `True` -# (2) `keyword.iskeyword(x)` evaluates to `False` -_Identifier: typing_extensions.TypeAlias = str - -# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes -class _Attributes(TypedDict, Generic[_EndPositionT], total=False): - lineno: int - col_offset: int - end_lineno: _EndPositionT - end_col_offset: _EndPositionT - -class AST: - if sys.version_info >= (3, 10): - __match_args__ = () - _attributes: ClassVar[tuple[str, ...]] - _fields: ClassVar[tuple[str, ...]] - if sys.version_info >= (3, 13): - _field_types: ClassVar[dict[str, Any]] - - if sys.version_info >= (3, 14): - def __replace__(self) -> Self: ... - -class mod(AST): ... -class type_ignore(AST): ... - -class TypeIgnore(type_ignore): - if sys.version_info >= (3, 10): - __match_args__ = ("lineno", "tag") - lineno: int - tag: str - def __init__(self, lineno: int, tag: str) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, lineno: int = ..., tag: str = ...) -> Self: ... - -class FunctionType(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("argtypes", "returns") - argtypes: list[expr] - returns: expr - if sys.version_info >= (3, 13): - @overload - def __init__(self, argtypes: list[expr], returns: expr) -> None: ... - @overload - def __init__(self, argtypes: list[expr] = ..., *, returns: expr) -> None: ... - else: - def __init__(self, argtypes: list[expr], returns: expr) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, argtypes: list[expr] = ..., returns: expr = ...) -> Self: ... - -class Module(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body", "type_ignores") - body: list[stmt] - type_ignores: list[TypeIgnore] - if sys.version_info >= (3, 13): - def __init__(self, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> None: ... - else: - def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> Self: ... - -class Interactive(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body",) - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__(self, body: list[stmt] = ...) -> None: ... - else: - def __init__(self, body: list[stmt]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, body: list[stmt] = ...) -> Self: ... - -class Expression(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body",) - body: expr - def __init__(self, body: expr) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, body: expr = ...) -> Self: ... - -class stmt(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... - -class FunctionDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") - name: _Identifier - args: arguments - body: list[stmt] - decorator_list: list[expr] - returns: expr | None - type_comment: str | None - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - returns: expr | None = None, - type_comment: str | None = None, - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None, - type_comment: str | None, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - *, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - name: _Identifier = ..., - args: arguments = ..., - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - returns: expr | None = ..., - type_comment: str | None = ..., - type_params: list[type_param] = ..., - ) -> Self: ... - -class AsyncFunctionDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") - name: _Identifier - args: arguments - body: list[stmt] - decorator_list: list[expr] - returns: expr | None - type_comment: str | None - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - returns: expr | None = None, - type_comment: str | None = None, - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None, - type_comment: str | None, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - *, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - name: _Identifier = ..., - args: arguments = ..., - body: list[stmt], - decorator_list: list[expr], - returns: expr | None, - type_comment: str | None, - type_params: list[type_param], - ) -> Self: ... - -class ClassDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "bases", "keywords", "body", "decorator_list") - name: _Identifier - bases: list[expr] - keywords: list[keyword] - body: list[stmt] - decorator_list: list[expr] - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - bases: list[expr] = ..., - keywords: list[keyword] = ..., - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - def __init__( - self, - name: _Identifier, - bases: list[expr], - keywords: list[keyword], - body: list[stmt], - decorator_list: list[expr], - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - bases: list[expr], - keywords: list[keyword], - body: list[stmt], - decorator_list: list[expr], - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - name: _Identifier, - bases: list[expr], - keywords: list[keyword], - body: list[stmt], - decorator_list: list[expr], - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class Return(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr | None - def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Delete(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("targets",) - targets: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Assign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("targets", "value", "type_comment") - targets: list[expr] - value: expr - type_comment: str | None - if sys.version_info >= (3, 13): - @overload - def __init__( - self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - @overload - def __init__( - self, targets: list[expr] = ..., *, value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__( - self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, targets: list[expr] = ..., value: expr = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class AugAssign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "op", "value") - target: Name | Attribute | Subscript - op: operator - value: expr - def __init__( - self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - target: Name | Attribute | Subscript = ..., - op: operator = ..., - value: expr = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class AnnAssign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "annotation", "value", "simple") - target: Name | Attribute | Subscript - annotation: expr - value: expr | None - simple: int - @overload - def __init__( - self, - target: Name | Attribute | Subscript, - annotation: expr, - value: expr | None, - simple: int, - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - target: Name | Attribute | Subscript, - annotation: expr, - value: expr | None = None, - *, - simple: int, - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - target: Name | Attribute | Subscript = ..., - annotation: expr = ..., - value: expr | None = ..., - simple: int = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class For(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "body", "orelse", "type_comment") - target: expr - iter: expr - body: list[stmt] - orelse: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt], - orelse: list[stmt], - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - target: expr = ..., - iter: expr = ..., - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class AsyncFor(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "body", "orelse", "type_comment") - target: expr - iter: expr - body: list[stmt] - orelse: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt], - orelse: list[stmt], - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - target: expr = ..., - iter: expr = ..., - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class While(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: list[stmt] - orelse: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> Self: ... - -class If(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: list[stmt] - orelse: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, test: expr = ..., body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class With(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("items", "body", "type_comment") - items: list[withitem] - body: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class AsyncWith(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("items", "body", "type_comment") - items: list[withitem] - body: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class Raise(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("exc", "cause") - exc: expr | None - cause: expr | None - def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, exc: expr | None = ..., cause: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Try(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("body", "handlers", "orelse", "finalbody") - body: list[stmt] - handlers: list[ExceptHandler] - orelse: list[stmt] - finalbody: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - body: list[stmt], - handlers: list[ExceptHandler], - orelse: list[stmt], - finalbody: list[stmt], - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... +if sys.version_info >= (3, 12): + from ast import ParamSpec as ParamSpec, TypeVar as TypeVar, TypeVarTuple as TypeVarTuple, type_param as type_param if sys.version_info >= (3, 11): - class TryStar(stmt): - __match_args__ = ("body", "handlers", "orelse", "finalbody") - body: list[stmt] - handlers: list[ExceptHandler] - orelse: list[stmt] - finalbody: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - body: list[stmt], - handlers: list[ExceptHandler], - orelse: list[stmt], - finalbody: list[stmt], - **kwargs: Unpack[_Attributes], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class Assert(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "msg") - test: expr - msg: expr | None - def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, test: expr, msg: expr | None, **kwargs: Unpack[_Attributes]) -> Self: ... - -class Import(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[alias] - if sys.version_info >= (3, 13): - def __init__(self, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class ImportFrom(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("module", "names", "level") - module: str | None - names: list[alias] - level: int - if sys.version_info >= (3, 13): - @overload - def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__( - self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - @overload - def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__( - self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, module: str | None = ..., names: list[alias] = ..., level: int = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Global(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[_Identifier] - if sys.version_info >= (3, 13): - def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> Self: ... - -class Nonlocal(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[_Identifier] - if sys.version_info >= (3, 13): - def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Expr(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Pass(stmt): ... -class Break(stmt): ... -class Continue(stmt): ... - -class expr(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... - -class BoolOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("op", "values") - op: boolop - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, op: boolop, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, op: boolop = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class BinOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("left", "op", "right") - left: expr - op: operator - right: expr - def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, left: expr = ..., op: operator = ..., right: expr = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class UnaryOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("op", "operand") - op: unaryop - operand: expr - def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, op: unaryop = ..., operand: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Lambda(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("args", "body") - args: arguments - body: expr - def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, args: arguments = ..., body: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class IfExp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: expr - orelse: expr - def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, test: expr = ..., body: expr = ..., orelse: expr = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Dict(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("keys", "values") - keys: list[expr | None] - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Set(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts",) - elts: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class ListComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class SetComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class DictComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("key", "value", "generators") - key: expr - value: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__( - self, key: expr, value: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, key: expr = ..., value: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class GeneratorExp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Await(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Yield(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr | None - def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class YieldFrom(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Compare(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("left", "ops", "comparators") - left: expr - ops: list[cmpop] - comparators: list[expr] - if sys.version_info >= (3, 13): - def __init__( - self, left: expr, ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, left: expr = ..., ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Call(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("func", "args", "keywords") - func: expr - args: list[expr] - keywords: list[keyword] - if sys.version_info >= (3, 13): - def __init__( - self, func: expr, args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, func: expr = ..., args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class FormattedValue(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "conversion", "format_spec") - value: expr - conversion: int - format_spec: expr | None - def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, value: expr = ..., conversion: int = ..., format_spec: expr | None = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class JoinedStr(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("values",) - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Constant(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "kind") - value: Any # None, str, bytes, bool, int, float, complex, Ellipsis - kind: str | None - if sys.version_info < (3, 14): - # Aliases for value, for backwards compatibility - s: Any - n: int | float | complex - - def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: Any = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class NamedExpr(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "value") - target: Name - value: expr - def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, target: Name = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Attribute(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "attr", "ctx") - value: expr - attr: _Identifier - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, value: expr = ..., attr: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -if sys.version_info >= (3, 9): - _Slice: typing_extensions.TypeAlias = expr - _SliceAttributes: typing_extensions.TypeAlias = _Attributes -else: - class slice(AST): ... - _Slice: typing_extensions.TypeAlias = slice - - class _SliceAttributes(TypedDict): ... - -class Slice(_Slice): - if sys.version_info >= (3, 10): - __match_args__ = ("lower", "upper", "step") - lower: expr | None - upper: expr | None - step: expr | None - def __init__( - self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - lower: expr | None = ..., - upper: expr | None = ..., - step: expr | None = ..., - **kwargs: Unpack[_SliceAttributes], - ) -> Self: ... - -if sys.version_info < (3, 9): - class ExtSlice(slice): - dims: list[slice] - def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... - - class Index(slice): - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... - -class Subscript(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "slice", "ctx") - value: expr - slice: _Slice - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, value: expr = ..., slice: _Slice = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - -class Starred(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "ctx") - value: expr - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Name(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("id", "ctx") - id: _Identifier - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, id: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class List(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts", "ctx") - elts: list[expr] - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class Tuple(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts", "ctx") - elts: list[expr] - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - if sys.version_info >= (3, 9): - dims: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class expr_context(AST): ... - -if sys.version_info < (3, 9): - class AugLoad(expr_context): ... - class AugStore(expr_context): ... - class Param(expr_context): ... - - class Suite(mod): - body: list[stmt] - def __init__(self, body: list[stmt]) -> None: ... - -class Del(expr_context): ... -class Load(expr_context): ... -class Store(expr_context): ... -class boolop(AST): ... -class And(boolop): ... -class Or(boolop): ... -class operator(AST): ... -class Add(operator): ... -class BitAnd(operator): ... -class BitOr(operator): ... -class BitXor(operator): ... -class Div(operator): ... -class FloorDiv(operator): ... -class LShift(operator): ... -class Mod(operator): ... -class Mult(operator): ... -class MatMult(operator): ... -class Pow(operator): ... -class RShift(operator): ... -class Sub(operator): ... -class unaryop(AST): ... -class Invert(unaryop): ... -class Not(unaryop): ... -class UAdd(unaryop): ... -class USub(unaryop): ... -class cmpop(AST): ... -class Eq(cmpop): ... -class Gt(cmpop): ... -class GtE(cmpop): ... -class In(cmpop): ... -class Is(cmpop): ... -class IsNot(cmpop): ... -class Lt(cmpop): ... -class LtE(cmpop): ... -class NotEq(cmpop): ... -class NotIn(cmpop): ... - -class comprehension(AST): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "ifs", "is_async") - target: expr - iter: expr - ifs: list[expr] - is_async: int - if sys.version_info >= (3, 13): - @overload - def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... - @overload - def __init__(self, target: expr, iter: expr, ifs: list[expr] = ..., *, is_async: int) -> None: ... - else: - def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, target: expr = ..., iter: expr = ..., ifs: list[expr] = ..., is_async: int = ...) -> Self: ... - -class excepthandler(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int | None = ..., end_col_offset: int | None = ... - ) -> Self: ... - -class ExceptHandler(excepthandler): - if sys.version_info >= (3, 10): - __match_args__ = ("type", "name", "body") - type: expr | None - name: _Identifier | None - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - @overload - def __init__( - self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes] - ) -> None: ... - @overload - def __init__( - self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - type: expr | None = ..., - name: _Identifier | None = ..., - body: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class arguments(AST): - if sys.version_info >= (3, 10): - __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") - posonlyargs: list[arg] - args: list[arg] - vararg: arg | None - kwonlyargs: list[arg] - kw_defaults: list[expr | None] - kwarg: arg | None - defaults: list[expr] - if sys.version_info >= (3, 13): - def __init__( - self, - posonlyargs: list[arg] = ..., - args: list[arg] = ..., - vararg: arg | None = None, - kwonlyargs: list[arg] = ..., - kw_defaults: list[expr | None] = ..., - kwarg: arg | None = None, - defaults: list[expr] = ..., - ) -> None: ... - else: - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None, - defaults: list[expr], - ) -> None: ... - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None = None, - *, - defaults: list[expr], - ) -> None: ... - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None = None, - *, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None = None, - defaults: list[expr], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - posonlyargs: list[arg] = ..., - args: list[arg] = ..., - vararg: arg | None = ..., - kwonlyargs: list[arg] = ..., - kw_defaults: list[expr | None] = ..., - kwarg: arg | None = ..., - defaults: list[expr] = ..., - ) -> Self: ... - -class arg(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("arg", "annotation", "type_comment") - arg: _Identifier - annotation: expr | None - type_comment: str | None - def __init__( - self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - arg: _Identifier = ..., - annotation: expr | None = ..., - type_comment: str | None = ..., - **kwargs: Unpack[_Attributes], - ) -> Self: ... - -class keyword(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("arg", "value") - arg: _Identifier | None - value: expr - @overload - def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, arg: _Identifier | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class alias(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("name", "asname") - name: str - asname: _Identifier | None - def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, name: str = ..., asname: _Identifier | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - -class withitem(AST): - if sys.version_info >= (3, 10): - __match_args__ = ("context_expr", "optional_vars") - context_expr: expr - optional_vars: expr | None - def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, context_expr: expr = ..., optional_vars: expr | None = ...) -> Self: ... + from ast import TryStar as TryStar if sys.version_info >= (3, 10): - class Match(stmt): - __match_args__ = ("subject", "cases") - subject: expr - cases: list[match_case] - if sys.version_info >= (3, 13): - def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes] - ) -> Self: ... - - class pattern(AST): - lineno: int - col_offset: int - end_lineno: int - end_col_offset: int - def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ... - ) -> Self: ... - - # Without the alias, Pyright complains variables named pattern are recursively defined - _Pattern: typing_extensions.TypeAlias = pattern - - class match_case(AST): - __match_args__ = ("pattern", "guard", "body") - pattern: _Pattern - guard: expr | None - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... - else: - @overload - def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ... - @overload - def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, pattern: _Pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ... - - class MatchValue(pattern): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... - - class MatchSingleton(pattern): - __match_args__ = ("value",) - value: Literal[True, False] | None - def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, value: Literal[True, False] | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... - - class MatchSequence(pattern): - __match_args__ = ("patterns",) - patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... - else: - def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... - - class MatchStar(pattern): - __match_args__ = ("name",) - name: _Identifier | None - def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + from ast import ( + MatchAs as MatchAs, + MatchClass as MatchClass, + MatchMapping as MatchMapping, + MatchOr as MatchOr, + MatchSequence as MatchSequence, + MatchSingleton as MatchSingleton, + MatchStar as MatchStar, + MatchValue as MatchValue, + match_case as match_case, + pattern as pattern, + ) - if sys.version_info >= (3, 14): - def __replace__(self, *, name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... - - class MatchMapping(pattern): - __match_args__ = ("keys", "patterns", "rest") - keys: list[expr] - patterns: list[pattern] - rest: _Identifier | None - if sys.version_info >= (3, 13): - def __init__( - self, - keys: list[expr] = ..., - patterns: list[pattern] = ..., - rest: _Identifier | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__( - self, - keys: list[expr], - patterns: list[pattern], - rest: _Identifier | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - keys: list[expr] = ..., - patterns: list[pattern] = ..., - rest: _Identifier | None = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> Self: ... - - class MatchClass(pattern): - __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") - cls: expr - patterns: list[pattern] - kwd_attrs: list[_Identifier] - kwd_patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__( - self, - cls: expr, - patterns: list[pattern] = ..., - kwd_attrs: list[_Identifier] = ..., - kwd_patterns: list[pattern] = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__( - self, - cls: expr, - patterns: list[pattern], - kwd_attrs: list[_Identifier], - kwd_patterns: list[pattern], - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - cls: expr = ..., - patterns: list[pattern] = ..., - kwd_attrs: list[_Identifier] = ..., - kwd_patterns: list[pattern] = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> Self: ... - - class MatchAs(pattern): - __match_args__ = ("pattern", "name") - pattern: _Pattern | None - name: _Identifier | None - def __init__( - self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, pattern: _Pattern | None = ..., name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]] - ) -> Self: ... - - class MatchOr(pattern): - __match_args__ = ("patterns",) - patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... - else: - def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... - -if sys.version_info >= (3, 12): - class type_param(AST): - lineno: int - col_offset: int - end_lineno: int - end_col_offset: int - def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__(self, **kwargs: Unpack[_Attributes[int]]) -> Self: ... - - class TypeVar(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "bound", "default_value") - else: - __match_args__ = ("name", "bound") - name: _Identifier - bound: expr | None - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, - name: _Identifier, - bound: expr | None = None, - default_value: expr | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - name: _Identifier = ..., - bound: expr | None = ..., - default_value: expr | None = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> Self: ... - - class ParamSpec(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "default_value") - else: - __match_args__ = ("name",) - name: _Identifier - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] - ) -> Self: ... - - class TypeVarTuple(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "default_value") - else: - __match_args__ = ("name",) - name: _Identifier - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] - ) -> Self: ... +if sys.version_info < (3, 9): + from ast import ( + AugLoad as AugLoad, + AugStore as AugStore, + ExtSlice as ExtSlice, + Index as Index, + Param as Param, + Suite as Suite, + slice as slice, + ) - class TypeAlias(stmt): - __match_args__ = ("name", "type_params", "value") - name: Name - type_params: list[type_param] - value: expr - if sys.version_info >= (3, 13): - @overload - def __init__( - self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - @overload - def __init__( - self, name: Name, type_params: list[type_param] = ..., *, value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__( - self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... +PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +PyCF_ONLY_AST: Literal[1024] +PyCF_TYPE_COMMENTS: Literal[4096] - if sys.version_info >= (3, 14): - def __replace__( - self, - *, - name: Name = ..., - type_params: list[type_param] = ..., - value: expr = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> Self: ... +if sys.version_info >= (3, 13): + PyCF_OPTIMIZED_AST: Literal[33792] diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi new file mode 100644 index 000000000000..18920cd8a8a4 --- /dev/null +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -0,0 +1,120 @@ +import sys +from asyncio.events import AbstractEventLoop +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable +from contextvars import Context +from types import FrameType +from typing import Any, Literal, TextIO, TypeVar +from typing_extensions import Self, TypeAlias + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_TaskYieldType: TypeAlias = Future[object] | None + +class Future(Awaitable[_T], Iterable[_T]): + _state: str + @property + def _exception(self) -> BaseException | None: ... + _blocking: bool + @property + def _log_traceback(self) -> bool: ... + @_log_traceback.setter + def _log_traceback(self, val: Literal[False]) -> None: ... + _asyncio_future_blocking: bool # is a part of duck-typing contract for `Future` + def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ... + def __del__(self) -> None: ... + def get_loop(self) -> AbstractEventLoop: ... + @property + def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... + def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... + if sys.version_info >= (3, 9): + def cancel(self, msg: Any | None = None) -> bool: ... + else: + def cancel(self) -> bool: ... + + def cancelled(self) -> bool: ... + def done(self) -> bool: ... + def result(self) -> _T: ... + def exception(self) -> BaseException | None: ... + def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... + def set_result(self, result: _T, /) -> None: ... + def set_exception(self, exception: type | BaseException, /) -> None: ... + def __iter__(self) -> Generator[Any, None, _T]: ... + def __await__(self) -> Generator[Any, None, _T]: ... + @property + def _loop(self) -> AbstractEventLoop: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +if sys.version_info >= (3, 12): + _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co] +elif sys.version_info >= (3, 9): + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co] +else: + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co] + +# mypy and pyright complain that a subclass of an invariant class shouldn't be covariant. +# While this is true in general, here it's sort-of okay to have a covariant subclass, +# since the only reason why `asyncio.Future` is invariant is the `set_result()` method, +# and `asyncio.Task.set_result()` always raises. +class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] + if sys.version_info >= (3, 12): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None = ..., + context: Context | None = None, + eager_start: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None = ..., + context: Context | None = None, + ) -> None: ... + else: + def __init__( + self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... + ) -> None: ... + + if sys.version_info >= (3, 12): + def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ... + else: + def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... + + def get_name(self) -> str: ... + def set_name(self, value: object, /) -> None: ... + if sys.version_info >= (3, 12): + def get_context(self) -> Context: ... + + def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ... + def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ... + if sys.version_info >= (3, 11): + def cancelling(self) -> int: ... + def uncancel(self) -> int: ... + if sys.version_info < (3, 9): + @classmethod + def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... + @classmethod + def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +def get_event_loop() -> AbstractEventLoop: ... +def get_running_loop() -> AbstractEventLoop: ... +def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... +def _get_running_loop() -> AbstractEventLoop: ... +def _register_task(task: Task[Any]) -> None: ... +def _unregister_task(task: Task[Any]) -> None: ... +def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... +def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... + +if sys.version_info >= (3, 12): + def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 9bb5d27f6e35..6d1893cf8c16 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -20,6 +20,8 @@ _QuotingType: TypeAlias = int class Error(Exception): ... +_DialectLike: TypeAlias = str | Dialect | type[Dialect] + class Dialect: delimiter: str quotechar: str | None @@ -29,9 +31,18 @@ class Dialect: lineterminator: str quoting: _QuotingType strict: bool - def __init__(self) -> None: ... - -_DialectLike: TypeAlias = str | Dialect | type[Dialect] + def __init__( + self, + dialect: _DialectLike | None = ..., + delimiter: str = ",", + doublequote: bool = True, + escapechar: str | None = None, + lineterminator: str = "\r\n", + quotechar: str | None = '"', + quoting: _QuotingType = 0, + skipinitialspace: bool = False, + strict: bool = False, + ) -> None: ... class _reader(Iterator[list[str]]): @property diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 0fe7521d7749..91d95a154f8e 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from abc import abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from ctypes import CDLL, ArgumentError as ArgumentError +from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p from typing import Any, ClassVar, Generic, TypeVar, overload from typing_extensions import Self, TypeAlias @@ -99,6 +99,9 @@ class _Pointer(_PointerLike, _CData, Generic[_CT]): def __getitem__(self, key: slice, /) -> list[Any]: ... def __setitem__(self, key: int, value: Any, /) -> None: ... +@overload +def POINTER(type: None, /) -> type[c_void_p]: ... +@overload def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ... def pointer(obj: _CT, /) -> _Pointer[_CT]: ... diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index b68c8925a041..80075d77e8e6 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -298,7 +298,7 @@ if sys.version_info >= (3, 9): def getmouse() -> tuple[int, int, int, int, int]: ... def getsyx() -> tuple[int, int]: ... -def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... +def getwin(file: SupportsRead[bytes], /) -> window: ... def halfdelay(tenths: int, /) -> None: ... def has_colors() -> bool: ... @@ -310,7 +310,7 @@ def has_il() -> bool: ... def has_key(key: int, /) -> bool: ... def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... -def initscr() -> _CursesWindow: ... +def initscr() -> window: ... def intrflush(flag: bool, /) -> None: ... def is_term_resized(nlines: int, ncols: int, /) -> bool: ... def isendwin() -> bool: ... @@ -321,8 +321,8 @@ def meta(yes: bool, /) -> None: ... def mouseinterval(interval: int, /) -> None: ... def mousemask(newmask: int, /) -> tuple[int, int]: ... def napms(ms: int, /) -> int: ... -def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ... -def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ... +def newpad(nlines: int, ncols: int, /) -> window: ... +def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> window: ... def nl(flag: bool = True, /) -> None: ... def nocbreak() -> None: ... def noecho() -> None: ... @@ -378,7 +378,7 @@ def use_env(flag: bool, /) -> None: ... class error(Exception): ... @final -class _CursesWindow: +class window: # undocumented encoding: str @overload def addch(self, ch: _ChType, attr: int = ...) -> None: ... @@ -431,9 +431,9 @@ class _CursesWindow: def delch(self, y: int, x: int) -> None: ... def deleteln(self) -> None: ... @overload - def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def derwin(self, begin_y: int, begin_x: int) -> window: ... @overload - def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... def enclose(self, y: int, x: int, /) -> bool: ... def erase(self) -> None: ... @@ -505,16 +505,16 @@ class _CursesWindow: @overload def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... @overload - def overlay(self, destwin: _CursesWindow) -> None: ... + def overlay(self, destwin: window) -> None: ... @overload def overlay( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... @overload - def overwrite(self, destwin: _CursesWindow) -> None: ... + def overwrite(self, destwin: window) -> None: ... @overload def overwrite( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... def putwin(self, file: IO[Any], /) -> None: ... def redrawln(self, beg: int, num: int, /) -> None: ... @@ -530,13 +530,13 @@ class _CursesWindow: def standend(self) -> None: ... def standout(self) -> None: ... @overload - def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subpad(self, begin_y: int, begin_x: int) -> window: ... @overload - def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... @overload - def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subwin(self, begin_y: int, begin_x: int) -> window: ... @overload - def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... def syncdown(self) -> None: ... def syncok(self, flag: bool) -> None: ... def syncup(self) -> None: ... @@ -555,4 +555,3 @@ class _ncurses_version(NamedTuple): patch: int ncurses_version: _ncurses_version -window = _CursesWindow # undocumented diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 937a04ac3799..9fcc08dbb95d 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -1,22 +1,39 @@ -import numbers import sys -from collections.abc import Container, Sequence +from decimal import ( + Clamped as Clamped, + Context as Context, + ConversionSyntax as ConversionSyntax, + Decimal as Decimal, + DecimalException as DecimalException, + DecimalTuple as DecimalTuple, + DivisionByZero as DivisionByZero, + DivisionImpossible as DivisionImpossible, + DivisionUndefined as DivisionUndefined, + FloatOperation as FloatOperation, + Inexact as Inexact, + InvalidContext as InvalidContext, + InvalidOperation as InvalidOperation, + Overflow as Overflow, + Rounded as Rounded, + Subnormal as Subnormal, + Underflow as Underflow, +) from types import TracebackType -from typing import Any, ClassVar, Final, Literal, NamedTuple, overload -from typing_extensions import Self, TypeAlias +from typing import Final +from typing_extensions import TypeAlias -_Decimal: TypeAlias = Decimal | int -_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int] -_ComparableNum: TypeAlias = Decimal | float | numbers.Rational +_TrapType: TypeAlias = type[DecimalException] + +class _ContextManager: + new_context: Context + saved_context: Context + def __init__(self, new_context: Context) -> None: ... + def __enter__(self) -> Context: ... + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... __version__: Final[str] __libmpdec_version__: Final[str] -class DecimalTuple(NamedTuple): - sign: int - digits: tuple[int, ...] - exponent: int | Literal["n", "N", "F"] - ROUND_DOWN: Final[str] ROUND_HALF_UP: Final[str] ROUND_HALF_EVEN: Final[str] @@ -32,21 +49,6 @@ MAX_PREC: Final[int] MIN_EMIN: Final[int] MIN_ETINY: Final[int] -class DecimalException(ArithmeticError): ... -class Clamped(DecimalException): ... -class InvalidOperation(DecimalException): ... -class ConversionSyntax(InvalidOperation): ... -class DivisionByZero(DecimalException, ZeroDivisionError): ... -class DivisionImpossible(InvalidOperation): ... -class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... -class Inexact(DecimalException): ... -class InvalidContext(InvalidOperation): ... -class Rounded(DecimalException): ... -class Subnormal(DecimalException): ... -class Overflow(Inexact, Rounded): ... -class Underflow(Inexact, Rounded, Subnormal): ... -class FloatOperation(DecimalException, TypeError): ... - def setcontext(context: Context, /) -> None: ... def getcontext() -> Context: ... @@ -67,215 +69,6 @@ if sys.version_info >= (3, 11): else: def localcontext(ctx: Context | None = None) -> _ContextManager: ... -class Decimal: - def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... - @classmethod - def from_float(cls, f: float, /) -> Self: ... - def __bool__(self) -> bool: ... - def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __hash__(self) -> int: ... - def as_tuple(self) -> DecimalTuple: ... - def as_integer_ratio(self) -> tuple[int, int]: ... - def to_eng_string(self, context: Context | None = None) -> str: ... - def __abs__(self) -> Decimal: ... - def __add__(self, value: _Decimal, /) -> Decimal: ... - def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __eq__(self, value: object, /) -> bool: ... - def __floordiv__(self, value: _Decimal, /) -> Decimal: ... - def __ge__(self, value: _ComparableNum, /) -> bool: ... - def __gt__(self, value: _ComparableNum, /) -> bool: ... - def __le__(self, value: _ComparableNum, /) -> bool: ... - def __lt__(self, value: _ComparableNum, /) -> bool: ... - def __mod__(self, value: _Decimal, /) -> Decimal: ... - def __mul__(self, value: _Decimal, /) -> Decimal: ... - def __neg__(self) -> Decimal: ... - def __pos__(self) -> Decimal: ... - def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... - def __radd__(self, value: _Decimal, /) -> Decimal: ... - def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... - def __rmod__(self, value: _Decimal, /) -> Decimal: ... - def __rmul__(self, value: _Decimal, /) -> Decimal: ... - def __rsub__(self, value: _Decimal, /) -> Decimal: ... - def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... - def __sub__(self, value: _Decimal, /) -> Decimal: ... - def __truediv__(self, value: _Decimal, /) -> Decimal: ... - def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __float__(self) -> float: ... - def __int__(self) -> int: ... - def __trunc__(self) -> int: ... - @property - def real(self) -> Decimal: ... - @property - def imag(self) -> Decimal: ... - def conjugate(self) -> Decimal: ... - def __complex__(self) -> complex: ... - @overload - def __round__(self) -> int: ... - @overload - def __round__(self, ndigits: int, /) -> Decimal: ... - def __floor__(self) -> int: ... - def __ceil__(self) -> int: ... - def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... - def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... - def normalize(self, context: Context | None = None) -> Decimal: ... - def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... - def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def sqrt(self, context: Context | None = None) -> Decimal: ... - def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def adjusted(self) -> int: ... - def canonical(self) -> Decimal: ... - def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def copy_abs(self) -> Decimal: ... - def copy_negate(self) -> Decimal: ... - def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def exp(self, context: Context | None = None) -> Decimal: ... - def is_canonical(self) -> bool: ... - def is_finite(self) -> bool: ... - def is_infinite(self) -> bool: ... - def is_nan(self) -> bool: ... - def is_normal(self, context: Context | None = None) -> bool: ... - def is_qnan(self) -> bool: ... - def is_signed(self) -> bool: ... - def is_snan(self) -> bool: ... - def is_subnormal(self, context: Context | None = None) -> bool: ... - def is_zero(self) -> bool: ... - def ln(self, context: Context | None = None) -> Decimal: ... - def log10(self, context: Context | None = None) -> Decimal: ... - def logb(self, context: Context | None = None) -> Decimal: ... - def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_invert(self, context: Context | None = None) -> Decimal: ... - def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def next_minus(self, context: Context | None = None) -> Decimal: ... - def next_plus(self, context: Context | None = None) -> Decimal: ... - def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def number_class(self, context: Context | None = None) -> str: ... - def radix(self) -> Decimal: ... - def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... - def __copy__(self) -> Self: ... - def __deepcopy__(self, memo: Any, /) -> Self: ... - def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... - -class _ContextManager: - new_context: Context - saved_context: Context - def __init__(self, new_context: Context) -> None: ... - def __enter__(self) -> Context: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... - -_TrapType: TypeAlias = type[DecimalException] - -class Context: - # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, - # even settable attributes like `prec` and `rounding`, - # but that's inexpressable in the stub. - # Type checkers either ignore it or misinterpret it - # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub - prec: int - rounding: str - Emin: int - Emax: int - capitals: int - clamp: int - traps: dict[_TrapType, bool] - flags: dict[_TrapType, bool] - def __init__( - self, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - _ignored_flags: list[_TrapType] | None = ..., - ) -> None: ... - def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... - def clear_flags(self) -> None: ... - def clear_traps(self) -> None: ... - def copy(self) -> Context: ... - def __copy__(self) -> Context: ... - # see https://github.com/python/cpython/issues/94107 - __hash__: ClassVar[None] # type: ignore[assignment] - def Etiny(self) -> int: ... - def Etop(self) -> int: ... - def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... - def create_decimal_from_float(self, f: float, /) -> Decimal: ... - def abs(self, x: _Decimal, /) -> Decimal: ... - def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def canonical(self, x: Decimal, /) -> Decimal: ... - def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def copy_abs(self, x: _Decimal, /) -> Decimal: ... - def copy_decimal(self, x: _Decimal, /) -> Decimal: ... - def copy_negate(self, x: _Decimal, /) -> Decimal: ... - def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def exp(self, x: _Decimal, /) -> Decimal: ... - def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... - def is_canonical(self, x: _Decimal, /) -> bool: ... - def is_finite(self, x: _Decimal, /) -> bool: ... - def is_infinite(self, x: _Decimal, /) -> bool: ... - def is_nan(self, x: _Decimal, /) -> bool: ... - def is_normal(self, x: _Decimal, /) -> bool: ... - def is_qnan(self, x: _Decimal, /) -> bool: ... - def is_signed(self, x: _Decimal, /) -> bool: ... - def is_snan(self, x: _Decimal, /) -> bool: ... - def is_subnormal(self, x: _Decimal, /) -> bool: ... - def is_zero(self, x: _Decimal, /) -> bool: ... - def ln(self, x: _Decimal, /) -> Decimal: ... - def log10(self, x: _Decimal, /) -> Decimal: ... - def logb(self, x: _Decimal, /) -> Decimal: ... - def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_invert(self, x: _Decimal, /) -> Decimal: ... - def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def minus(self, x: _Decimal, /) -> Decimal: ... - def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def next_minus(self, x: _Decimal, /) -> Decimal: ... - def next_plus(self, x: _Decimal, /) -> Decimal: ... - def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def normalize(self, x: _Decimal, /) -> Decimal: ... - def number_class(self, x: _Decimal, /) -> str: ... - def plus(self, x: _Decimal, /) -> Decimal: ... - def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... - def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def radix(self) -> Decimal: ... - def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... - def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def sqrt(self, x: _Decimal, /) -> Decimal: ... - def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def to_eng_string(self, x: _Decimal, /) -> str: ... - def to_sci_string(self, x: _Decimal, /) -> str: ... - def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... - def to_integral_value(self, x: _Decimal, /) -> Decimal: ... - def to_integral(self, x: _Decimal, /) -> Decimal: ... - DefaultContext: Context BasicContext: Context ExtendedContext: Context diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi new file mode 100644 index 000000000000..e8290daad106 --- /dev/null +++ b/mypy/typeshed/stdlib/_io.pyi @@ -0,0 +1,195 @@ +import builtins +import codecs +import sys +from _typeshed import FileDescriptorOrPath, MaybeNone, ReadableBuffer, WriteableBuffer +from collections.abc import Callable, Iterable, Iterator +from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as UnsupportedOperation +from os import _Opener +from types import TracebackType +from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only +from typing_extensions import Self + +_T = TypeVar("_T") + +DEFAULT_BUFFER_SIZE: Final = 8192 + +open = builtins.open + +def open_code(path: str) -> IO[bytes]: ... + +BlockingIOError = builtins.BlockingIOError + +class _IOBase: + def __iter__(self) -> Iterator[bytes]: ... + def __next__(self) -> bytes: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + def close(self) -> None: ... + def fileno(self) -> int: ... + def flush(self) -> None: ... + def isatty(self) -> bool: ... + def readable(self) -> bool: ... + read: Callable[..., Any] + def readlines(self, hint: int = -1, /) -> list[bytes]: ... + def seek(self, offset: int, whence: int = ..., /) -> int: ... + def seekable(self) -> bool: ... + def tell(self) -> int: ... + def truncate(self, size: int | None = ..., /) -> int: ... + def writable(self) -> bool: ... + write: Callable[..., Any] + def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... + def readline(self, size: int | None = -1, /) -> bytes: ... + def __del__(self) -> None: ... + @property + def closed(self) -> bool: ... + def _checkClosed(self) -> None: ... # undocumented + +class _RawIOBase(_IOBase): + def readall(self) -> bytes: ... + # The following methods can return None if the file is in non-blocking mode + # and no data is available. + def readinto(self, buffer: WriteableBuffer, /) -> int | MaybeNone: ... + def write(self, b: ReadableBuffer, /) -> int | MaybeNone: ... + def read(self, size: int = -1, /) -> bytes | MaybeNone: ... + +class _BufferedIOBase(_IOBase): + def detach(self) -> RawIOBase: ... + def readinto(self, buffer: WriteableBuffer, /) -> int: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... + def readinto1(self, buffer: WriteableBuffer, /) -> int: ... + def read(self, size: int | None = ..., /) -> bytes: ... + def read1(self, size: int = ..., /) -> bytes: ... + +class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + mode: str + # The type of "name" equals the argument passed in to the constructor, + # but that can make FileIO incompatible with other I/O types that assume + # "name" is a str. In the future, making FileIO generic might help. + name: Any + def __init__( + self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... + ) -> None: ... + @property + def closefd(self) -> bool: ... + +class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ... + # BytesIO does not contain a "name" field. This workaround is necessary + # to allow BytesIO sub-classes to add this field, as it is defined + # as a read-only property on IO[]. + name: Any + def getvalue(self) -> bytes: ... + def getbuffer(self) -> memoryview: ... + def read1(self, size: int | None = -1, /) -> bytes: ... + +class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... + def peek(self, size: int = 0, /) -> bytes: ... + +class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... + +class BufferedRandom(BufferedReader, BufferedWriter, BufferedIOBase, _BufferedIOBase): # type: ignore[misc] # incompatible definitions of methods in the base classes + def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this + +class BufferedRWPair(BufferedIOBase, _BufferedIOBase): + def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ... + def peek(self, size: int = ..., /) -> bytes: ... + +class _TextIOBase(_IOBase): + encoding: str + errors: str | None + newlines: str | tuple[str, ...] | None + def __iter__(self) -> Iterator[str]: ... # type: ignore[override] + def __next__(self) -> str: ... # type: ignore[override] + def detach(self) -> BinaryIO: ... + def write(self, s: str, /) -> int: ... + def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] + def readline(self, size: int = ..., /) -> str: ... # type: ignore[override] + def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] + def read(self, size: int | None = ..., /) -> str: ... + +@type_check_only +class _WrappedBuffer(Protocol): + # "name" is wrapped by TextIOWrapper. Its type is inconsistent between + # the various I/O types, see the comments on TextIOWrapper.name and + # TextIO.name. + @property + def name(self) -> Any: ... + @property + def closed(self) -> bool: ... + def read(self, size: int = ..., /) -> ReadableBuffer: ... + # Optional: def read1(self, size: int, /) -> ReadableBuffer: ... + def write(self, b: bytes, /) -> object: ... + def flush(self) -> object: ... + def close(self) -> object: ... + def seekable(self) -> bool: ... + def readable(self) -> bool: ... + def writable(self) -> bool: ... + def truncate(self, size: int, /) -> int: ... + def fileno(self) -> int: ... + def isatty(self) -> bool: ... + # Optional: Only needs to be present if seekable() returns True. + # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... + # def tell(self) -> int: ... + +_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True) + +class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes + def __init__( + self, + buffer: _BufferT_co, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = False, + write_through: bool = False, + ) -> None: ... + # Equals the "buffer" argument passed in to the constructor. + @property + def buffer(self) -> _BufferT_co: ... # type: ignore[override] + @property + def line_buffering(self) -> bool: ... + @property + def write_through(self) -> bool: ... + def reconfigure( + self, + *, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool | None = None, + write_through: bool | None = None, + ) -> None: ... + def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] + # Equals the "buffer" argument passed in to the constructor. + def detach(self) -> _BufferT_co: ... # type: ignore[override] + # TextIOWrapper's version of seek only supports a limited subset of + # operations. + def seek(self, cookie: int, whence: int = 0, /) -> int: ... + +class StringIO(TextIOWrapper, TextIOBase, _TextIOBase): # type: ignore[misc] # incompatible definitions of write in the base classes + def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... + # StringIO does not contain a "name" field. This workaround is necessary + # to allow StringIO sub-classes to add this field, as it is defined + # as a read-only property on IO[]. + name: Any + def getvalue(self) -> str: ... + +class IncrementalNewlineDecoder(codecs.IncrementalDecoder): + def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ... + def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... + @property + def newlines(self) -> str | tuple[str, ...] | None: ... + def setstate(self, state: tuple[bytes, int], /) -> None: ... + +if sys.version_info >= (3, 10): + @overload + def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ... + @overload + def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 1b0083f4e274..967215d8fa21 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -1,18 +1,16 @@ import sys from _typeshed import SupportsGetItem from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence -from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload -from typing_extensions import ParamSpec, TypeAlias, TypeIs, TypeVarTuple, Unpack +from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller +from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload +from typing_extensions import ParamSpec, TypeAlias, TypeIs _R = TypeVar("_R") _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") _K = TypeVar("_K") _V = TypeVar("_V") _P = ParamSpec("_P") -_Ts = TypeVarTuple("_Ts") # The following protocols return "Any" instead of bool, since the comparison # operators can be overloaded to return an arbitrary object. For example, @@ -92,40 +90,6 @@ def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ... @overload def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ... def length_hint(obj: object, default: int = 0, /) -> int: ... -@final -class attrgetter(Generic[_T_co]): - @overload - def __new__(cls, attr: str, /) -> attrgetter[Any]: ... - @overload - def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ... - @overload - def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ... - @overload - def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ... - @overload - def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... - def __call__(self, obj: Any, /) -> _T_co: ... - -@final -class itemgetter(Generic[_T_co]): - @overload - def __new__(cls, item: _T, /) -> itemgetter[_T]: ... - @overload - def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... - # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: - # TypeVar "_KT_contra@SupportsGetItem" is contravariant - # "tuple[int, int]" is incompatible with protocol "SupportsIndex" - # preventing [_T_co, ...] instead of [Any, ...] - # - # A suspected mypy issue prevents using [..., _T] instead of [..., Any] here. - # https://github.com/python/mypy/issues/14032 - def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ... - -@final -class methodcaller: - def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... - def __call__(self, obj: Any) -> Any: ... - def iadd(a: Any, b: Any, /) -> Any: ... def iand(a: Any, b: Any, /) -> Any: ... def iconcat(a: Any, b: Any, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index affa8d63ecfa..7e4cf4e0364a 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Iterable +from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout from typing import Any, SupportsIndex, overload from typing_extensions import TypeAlias @@ -666,18 +667,6 @@ if sys.platform != "win32": if sys.platform != "win32" and sys.platform != "darwin": IPX_TYPE: int -# ===== Exceptions ===== - -error = OSError - -class herror(error): ... -class gaierror(error): ... - -if sys.version_info >= (3, 10): - timeout = TimeoutError -else: - class timeout(error): ... - # ===== Classes ===== class socket: @@ -687,8 +676,9 @@ class socket: def type(self) -> int: ... @property def proto(self) -> int: ... + # F811: "Redefinition of unused `timeout`" @property - def timeout(self) -> float | None: ... + def timeout(self) -> float | None: ... # noqa: F811 if sys.platform == "win32": def __init__( self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ... @@ -788,7 +778,9 @@ def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ... def inet_pton(address_family: int, ip_string: str, /) -> bytes: ... def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ... def getdefaulttimeout() -> float | None: ... -def setdefaulttimeout(timeout: float | None, /) -> None: ... + +# F811: "Redefinition of unused `timeout`" +def setdefaulttimeout(timeout: float | None, /) -> None: ... # noqa: F811 if sys.platform != "win32": def sethostname(name: str, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_sqlite3.pyi b/mypy/typeshed/stdlib/_sqlite3.pyi new file mode 100644 index 000000000000..6f06542c1ba7 --- /dev/null +++ b/mypy/typeshed/stdlib/_sqlite3.pyi @@ -0,0 +1,312 @@ +import sys +from _typeshed import ReadableBuffer, StrOrBytesPath +from collections.abc import Callable +from sqlite3 import ( + Connection as Connection, + Cursor as Cursor, + DatabaseError as DatabaseError, + DataError as DataError, + Error as Error, + IntegrityError as IntegrityError, + InterfaceError as InterfaceError, + InternalError as InternalError, + NotSupportedError as NotSupportedError, + OperationalError as OperationalError, + PrepareProtocol as PrepareProtocol, + ProgrammingError as ProgrammingError, + Row as Row, + Warning as Warning, +) +from typing import Any, Final, Literal, TypeVar, overload +from typing_extensions import TypeAlias + +if sys.version_info >= (3, 11): + from sqlite3 import Blob as Blob + +_T = TypeVar("_T") +_ConnectionT = TypeVar("_ConnectionT", bound=Connection) +_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None +_Adapter: TypeAlias = Callable[[_T], _SqliteData] +_Converter: TypeAlias = Callable[[bytes], Any] + +PARSE_COLNAMES: Final[int] +PARSE_DECLTYPES: Final[int] +SQLITE_ALTER_TABLE: Final[int] +SQLITE_ANALYZE: Final[int] +SQLITE_ATTACH: Final[int] +SQLITE_CREATE_INDEX: Final[int] +SQLITE_CREATE_TABLE: Final[int] +SQLITE_CREATE_TEMP_INDEX: Final[int] +SQLITE_CREATE_TEMP_TABLE: Final[int] +SQLITE_CREATE_TEMP_TRIGGER: Final[int] +SQLITE_CREATE_TEMP_VIEW: Final[int] +SQLITE_CREATE_TRIGGER: Final[int] +SQLITE_CREATE_VIEW: Final[int] +SQLITE_CREATE_VTABLE: Final[int] +SQLITE_DELETE: Final[int] +SQLITE_DENY: Final[int] +SQLITE_DETACH: Final[int] +SQLITE_DONE: Final[int] +SQLITE_DROP_INDEX: Final[int] +SQLITE_DROP_TABLE: Final[int] +SQLITE_DROP_TEMP_INDEX: Final[int] +SQLITE_DROP_TEMP_TABLE: Final[int] +SQLITE_DROP_TEMP_TRIGGER: Final[int] +SQLITE_DROP_TEMP_VIEW: Final[int] +SQLITE_DROP_TRIGGER: Final[int] +SQLITE_DROP_VIEW: Final[int] +SQLITE_DROP_VTABLE: Final[int] +SQLITE_FUNCTION: Final[int] +SQLITE_IGNORE: Final[int] +SQLITE_INSERT: Final[int] +SQLITE_OK: Final[int] +SQLITE_PRAGMA: Final[int] +SQLITE_READ: Final[int] +SQLITE_RECURSIVE: Final[int] +SQLITE_REINDEX: Final[int] +SQLITE_SAVEPOINT: Final[int] +SQLITE_SELECT: Final[int] +SQLITE_TRANSACTION: Final[int] +SQLITE_UPDATE: Final[int] +adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] +converters: dict[str, _Converter] +sqlite_version: str + +if sys.version_info < (3, 12): + version: str + +if sys.version_info >= (3, 12): + LEGACY_TRANSACTION_CONTROL: Final[int] + SQLITE_DBCONFIG_DEFENSIVE: Final[int] + SQLITE_DBCONFIG_DQS_DDL: Final[int] + SQLITE_DBCONFIG_DQS_DML: Final[int] + SQLITE_DBCONFIG_ENABLE_FKEY: Final[int] + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int] + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int] + SQLITE_DBCONFIG_ENABLE_QPSG: Final[int] + SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int] + SQLITE_DBCONFIG_ENABLE_VIEW: Final[int] + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int] + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int] + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int] + SQLITE_DBCONFIG_RESET_DATABASE: Final[int] + SQLITE_DBCONFIG_TRIGGER_EQP: Final[int] + SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int] + SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int] + +if sys.version_info >= (3, 11): + SQLITE_ABORT: Final[int] + SQLITE_ABORT_ROLLBACK: Final[int] + SQLITE_AUTH: Final[int] + SQLITE_AUTH_USER: Final[int] + SQLITE_BUSY: Final[int] + SQLITE_BUSY_RECOVERY: Final[int] + SQLITE_BUSY_SNAPSHOT: Final[int] + SQLITE_BUSY_TIMEOUT: Final[int] + SQLITE_CANTOPEN: Final[int] + SQLITE_CANTOPEN_CONVPATH: Final[int] + SQLITE_CANTOPEN_DIRTYWAL: Final[int] + SQLITE_CANTOPEN_FULLPATH: Final[int] + SQLITE_CANTOPEN_ISDIR: Final[int] + SQLITE_CANTOPEN_NOTEMPDIR: Final[int] + SQLITE_CANTOPEN_SYMLINK: Final[int] + SQLITE_CONSTRAINT: Final[int] + SQLITE_CONSTRAINT_CHECK: Final[int] + SQLITE_CONSTRAINT_COMMITHOOK: Final[int] + SQLITE_CONSTRAINT_FOREIGNKEY: Final[int] + SQLITE_CONSTRAINT_FUNCTION: Final[int] + SQLITE_CONSTRAINT_NOTNULL: Final[int] + SQLITE_CONSTRAINT_PINNED: Final[int] + SQLITE_CONSTRAINT_PRIMARYKEY: Final[int] + SQLITE_CONSTRAINT_ROWID: Final[int] + SQLITE_CONSTRAINT_TRIGGER: Final[int] + SQLITE_CONSTRAINT_UNIQUE: Final[int] + SQLITE_CONSTRAINT_VTAB: Final[int] + SQLITE_CORRUPT: Final[int] + SQLITE_CORRUPT_INDEX: Final[int] + SQLITE_CORRUPT_SEQUENCE: Final[int] + SQLITE_CORRUPT_VTAB: Final[int] + SQLITE_EMPTY: Final[int] + SQLITE_ERROR: Final[int] + SQLITE_ERROR_MISSING_COLLSEQ: Final[int] + SQLITE_ERROR_RETRY: Final[int] + SQLITE_ERROR_SNAPSHOT: Final[int] + SQLITE_FORMAT: Final[int] + SQLITE_FULL: Final[int] + SQLITE_INTERNAL: Final[int] + SQLITE_INTERRUPT: Final[int] + SQLITE_IOERR: Final[int] + SQLITE_IOERR_ACCESS: Final[int] + SQLITE_IOERR_AUTH: Final[int] + SQLITE_IOERR_BEGIN_ATOMIC: Final[int] + SQLITE_IOERR_BLOCKED: Final[int] + SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int] + SQLITE_IOERR_CLOSE: Final[int] + SQLITE_IOERR_COMMIT_ATOMIC: Final[int] + SQLITE_IOERR_CONVPATH: Final[int] + SQLITE_IOERR_CORRUPTFS: Final[int] + SQLITE_IOERR_DATA: Final[int] + SQLITE_IOERR_DELETE: Final[int] + SQLITE_IOERR_DELETE_NOENT: Final[int] + SQLITE_IOERR_DIR_CLOSE: Final[int] + SQLITE_IOERR_DIR_FSYNC: Final[int] + SQLITE_IOERR_FSTAT: Final[int] + SQLITE_IOERR_FSYNC: Final[int] + SQLITE_IOERR_GETTEMPPATH: Final[int] + SQLITE_IOERR_LOCK: Final[int] + SQLITE_IOERR_MMAP: Final[int] + SQLITE_IOERR_NOMEM: Final[int] + SQLITE_IOERR_RDLOCK: Final[int] + SQLITE_IOERR_READ: Final[int] + SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int] + SQLITE_IOERR_SEEK: Final[int] + SQLITE_IOERR_SHMLOCK: Final[int] + SQLITE_IOERR_SHMMAP: Final[int] + SQLITE_IOERR_SHMOPEN: Final[int] + SQLITE_IOERR_SHMSIZE: Final[int] + SQLITE_IOERR_SHORT_READ: Final[int] + SQLITE_IOERR_TRUNCATE: Final[int] + SQLITE_IOERR_UNLOCK: Final[int] + SQLITE_IOERR_VNODE: Final[int] + SQLITE_IOERR_WRITE: Final[int] + SQLITE_LIMIT_ATTACHED: Final[int] + SQLITE_LIMIT_COLUMN: Final[int] + SQLITE_LIMIT_COMPOUND_SELECT: Final[int] + SQLITE_LIMIT_EXPR_DEPTH: Final[int] + SQLITE_LIMIT_FUNCTION_ARG: Final[int] + SQLITE_LIMIT_LENGTH: Final[int] + SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int] + SQLITE_LIMIT_SQL_LENGTH: Final[int] + SQLITE_LIMIT_TRIGGER_DEPTH: Final[int] + SQLITE_LIMIT_VARIABLE_NUMBER: Final[int] + SQLITE_LIMIT_VDBE_OP: Final[int] + SQLITE_LIMIT_WORKER_THREADS: Final[int] + SQLITE_LOCKED: Final[int] + SQLITE_LOCKED_SHAREDCACHE: Final[int] + SQLITE_LOCKED_VTAB: Final[int] + SQLITE_MISMATCH: Final[int] + SQLITE_MISUSE: Final[int] + SQLITE_NOLFS: Final[int] + SQLITE_NOMEM: Final[int] + SQLITE_NOTADB: Final[int] + SQLITE_NOTFOUND: Final[int] + SQLITE_NOTICE: Final[int] + SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int] + SQLITE_NOTICE_RECOVER_WAL: Final[int] + SQLITE_OK_LOAD_PERMANENTLY: Final[int] + SQLITE_OK_SYMLINK: Final[int] + SQLITE_PERM: Final[int] + SQLITE_PROTOCOL: Final[int] + SQLITE_RANGE: Final[int] + SQLITE_READONLY: Final[int] + SQLITE_READONLY_CANTINIT: Final[int] + SQLITE_READONLY_CANTLOCK: Final[int] + SQLITE_READONLY_DBMOVED: Final[int] + SQLITE_READONLY_DIRECTORY: Final[int] + SQLITE_READONLY_RECOVERY: Final[int] + SQLITE_READONLY_ROLLBACK: Final[int] + SQLITE_ROW: Final[int] + SQLITE_SCHEMA: Final[int] + SQLITE_TOOBIG: Final[int] + SQLITE_WARNING: Final[int] + SQLITE_WARNING_AUTOINDEX: Final[int] + threadsafety: Final[int] + +# Can take or return anything depending on what's in the registry. +@overload +def adapt(obj: Any, proto: Any, /) -> Any: ... +@overload +def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ... +def complete_statement(statement: str) -> bool: ... + +if sys.version_info >= (3, 12): + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, + *, + autocommit: bool = ..., + ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + *, + autocommit: bool = ..., + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + autocommit: bool = ..., + ) -> _ConnectionT: ... + +else: + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, + ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... + +def enable_callback_tracebacks(enable: bool, /) -> None: ... + +if sys.version_info < (3, 12): + # takes a pos-or-keyword argument because there is a C wrapper + def enable_shared_cache(do_enable: int) -> None: ... + +if sys.version_info >= (3, 10): + def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ... + def register_converter(typename: str, converter: _Converter, /) -> None: ... + +else: + def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ... + def register_converter(name: str, converter: _Converter, /) -> None: ... + +if sys.version_info < (3, 10): + OptimizedUnicode = str diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi new file mode 100644 index 000000000000..3e88874143df --- /dev/null +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -0,0 +1,292 @@ +import sys +from _typeshed import ReadableBuffer, StrOrBytesPath +from collections.abc import Callable +from ssl import ( + SSLCertVerificationError as SSLCertVerificationError, + SSLContext, + SSLEOFError as SSLEOFError, + SSLError as SSLError, + SSLObject, + SSLSyscallError as SSLSyscallError, + SSLWantReadError as SSLWantReadError, + SSLWantWriteError as SSLWantWriteError, + SSLZeroReturnError as SSLZeroReturnError, +) +from typing import Any, Literal, TypedDict, final, overload +from typing_extensions import NotRequired, Self, TypeAlias + +_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] +_PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT] + +class _Cipher(TypedDict): + aead: bool + alg_bits: int + auth: str + description: str + digest: str | None + id: int + kea: str + name: str + protocol: str + strength_bits: int + symmetric: str + +class _CertInfo(TypedDict): + subject: tuple[tuple[tuple[str, str], ...], ...] + issuer: tuple[tuple[tuple[str, str], ...], ...] + version: int + serialNumber: str + notBefore: str + notAfter: str + subjectAltName: NotRequired[tuple[tuple[str, str], ...] | None] + OCSP: NotRequired[tuple[str, ...] | None] + caIssuers: NotRequired[tuple[str, ...] | None] + crlDistributionPoints: NotRequired[tuple[str, ...] | None] + +def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ... +def RAND_bytes(n: int, /) -> bytes: ... + +if sys.version_info < (3, 12): + def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ... + +if sys.version_info < (3, 10): + def RAND_egd(path: str) -> None: ... + +def RAND_status() -> bool: ... +def get_default_verify_paths() -> tuple[str, str, str, str]: ... + +if sys.platform == "win32": + _EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]] + def enum_certificates(store_name: str) -> _EnumRetType: ... + def enum_crls(store_name: str) -> _EnumRetType: ... + +def txt2obj(txt: str, name: bool = False) -> tuple[int, str, str, str]: ... +def nid2obj(nid: int, /) -> tuple[int, str, str, str]: ... + +class _SSLContext: + check_hostname: bool + keylog_filename: str | None + maximum_version: int + minimum_version: int + num_tickets: int + options: int + post_handshake_auth: bool + protocol: int + if sys.version_info >= (3, 10): + security_level: int + sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None + verify_flags: int + verify_mode: int + def __new__(cls, protocol: int, /) -> Self: ... + def cert_store_stats(self) -> dict[str, int]: ... + @overload + def get_ca_certs(self, binary_form: Literal[False] = False) -> list[_PeerCertRetDictType]: ... + @overload + def get_ca_certs(self, binary_form: Literal[True]) -> list[bytes]: ... + @overload + def get_ca_certs(self, binary_form: bool = False) -> Any: ... + def get_ciphers(self) -> list[_Cipher]: ... + def load_cert_chain( + self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None + ) -> None: ... + def load_dh_params(self, path: str, /) -> None: ... + def load_verify_locations( + self, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, + ) -> None: ... + def session_stats(self) -> dict[str, int]: ... + def set_ciphers(self, cipherlist: str, /) -> None: ... + def set_default_verify_paths(self) -> None: ... + def set_ecdh_curve(self, name: str, /) -> None: ... + if sys.version_info >= (3, 13): + def set_psk_client_callback(self, callback: Callable[[str | None], tuple[str | None, bytes]] | None) -> None: ... + def set_psk_server_callback( + self, callback: Callable[[str | None], tuple[str | None, bytes]] | None, identity_hint: str | None = None + ) -> None: ... + +@final +class MemoryBIO: + eof: bool + pending: int + def __new__(self) -> Self: ... + def read(self, size: int = -1, /) -> bytes: ... + def write(self, b: ReadableBuffer, /) -> int: ... + def write_eof(self) -> None: ... + +@final +class SSLSession: + @property + def has_ticket(self) -> bool: ... + @property + def id(self) -> bytes: ... + @property + def ticket_lifetime_hint(self) -> int: ... + @property + def time(self) -> int: ... + @property + def timeout(self) -> int: ... + +# _ssl.Certificate is weird: it can't be instantiated or subclassed. +# Instances can only be created via methods of the private _ssl._SSLSocket class, +# for which the relevant method signatures are: +# +# class _SSLSocket: +# def get_unverified_chain(self) -> list[Certificate] | None: ... +# def get_verified_chain(self) -> list[Certificate] | None: ... +# +# You can find a _ssl._SSLSocket object as the _sslobj attribute of a ssl.SSLSocket object + +if sys.version_info >= (3, 10): + @final + class Certificate: + def get_info(self) -> _CertInfo: ... + @overload + def public_bytes(self) -> str: ... + @overload + def public_bytes(self, format: Literal[1] = 1, /) -> str: ... # ENCODING_PEM + @overload + def public_bytes(self, format: Literal[2], /) -> bytes: ... # ENCODING_DER + @overload + def public_bytes(self, format: int, /) -> str | bytes: ... + +if sys.version_info < (3, 12): + err_codes_to_names: dict[tuple[int, int], str] + err_names_to_codes: dict[str, tuple[int, int]] + lib_codes_to_names: dict[int, str] + +_DEFAULT_CIPHERS: str + +# SSL error numbers +SSL_ERROR_ZERO_RETURN: int +SSL_ERROR_WANT_READ: int +SSL_ERROR_WANT_WRITE: int +SSL_ERROR_WANT_X509_LOOKUP: int +SSL_ERROR_SYSCALL: int +SSL_ERROR_SSL: int +SSL_ERROR_WANT_CONNECT: int +SSL_ERROR_EOF: int +SSL_ERROR_INVALID_ERROR_CODE: int + +# verify modes +CERT_NONE: int +CERT_OPTIONAL: int +CERT_REQUIRED: int + +# verify flags +VERIFY_DEFAULT: int +VERIFY_CRL_CHECK_LEAF: int +VERIFY_CRL_CHECK_CHAIN: int +VERIFY_X509_STRICT: int +VERIFY_X509_TRUSTED_FIRST: int +if sys.version_info >= (3, 10): + VERIFY_ALLOW_PROXY_CERTS: int + VERIFY_X509_PARTIAL_CHAIN: int + +# alert descriptions +ALERT_DESCRIPTION_CLOSE_NOTIFY: int +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: int +ALERT_DESCRIPTION_BAD_RECORD_MAC: int +ALERT_DESCRIPTION_RECORD_OVERFLOW: int +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: int +ALERT_DESCRIPTION_HANDSHAKE_FAILURE: int +ALERT_DESCRIPTION_BAD_CERTIFICATE: int +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: int +ALERT_DESCRIPTION_CERTIFICATE_REVOKED: int +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: int +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: int +ALERT_DESCRIPTION_ILLEGAL_PARAMETER: int +ALERT_DESCRIPTION_UNKNOWN_CA: int +ALERT_DESCRIPTION_ACCESS_DENIED: int +ALERT_DESCRIPTION_DECODE_ERROR: int +ALERT_DESCRIPTION_DECRYPT_ERROR: int +ALERT_DESCRIPTION_PROTOCOL_VERSION: int +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: int +ALERT_DESCRIPTION_INTERNAL_ERROR: int +ALERT_DESCRIPTION_USER_CANCELLED: int +ALERT_DESCRIPTION_NO_RENEGOTIATION: int +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: int +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: int +ALERT_DESCRIPTION_UNRECOGNIZED_NAME: int +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: int +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: int +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: int + +# protocol versions +PROTOCOL_SSLv23: int +PROTOCOL_TLS: int +PROTOCOL_TLS_CLIENT: int +PROTOCOL_TLS_SERVER: int +PROTOCOL_TLSv1: int +PROTOCOL_TLSv1_1: int +PROTOCOL_TLSv1_2: int + +# protocol options +OP_ALL: int +OP_NO_SSLv2: int +OP_NO_SSLv3: int +OP_NO_TLSv1: int +OP_NO_TLSv1_1: int +OP_NO_TLSv1_2: int +OP_NO_TLSv1_3: int +OP_CIPHER_SERVER_PREFERENCE: int +OP_SINGLE_DH_USE: int +OP_NO_TICKET: int +OP_SINGLE_ECDH_USE: int +OP_NO_COMPRESSION: int +OP_ENABLE_MIDDLEBOX_COMPAT: int +OP_NO_RENEGOTIATION: int +if sys.version_info >= (3, 11): + OP_IGNORE_UNEXPECTED_EOF: int +elif sys.version_info >= (3, 8) and sys.platform == "linux": + OP_IGNORE_UNEXPECTED_EOF: int +if sys.version_info >= (3, 12): + OP_LEGACY_SERVER_CONNECT: int + OP_ENABLE_KTLS: int + +# host flags +HOSTFLAG_ALWAYS_CHECK_SUBJECT: int +HOSTFLAG_NEVER_CHECK_SUBJECT: int +HOSTFLAG_NO_WILDCARDS: int +HOSTFLAG_NO_PARTIAL_WILDCARDS: int +HOSTFLAG_MULTI_LABEL_WILDCARDS: int +HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: int + +if sys.version_info >= (3, 10): + # certificate file types + # Typed as Literal so the overload on Certificate.public_bytes can work properly. + ENCODING_PEM: Literal[1] + ENCODING_DER: Literal[2] + +# protocol versions +PROTO_MINIMUM_SUPPORTED: int +PROTO_MAXIMUM_SUPPORTED: int +PROTO_SSLv3: int +PROTO_TLSv1: int +PROTO_TLSv1_1: int +PROTO_TLSv1_2: int +PROTO_TLSv1_3: int + +# feature support +HAS_SNI: bool +HAS_TLS_UNIQUE: bool +HAS_ECDH: bool +HAS_NPN: bool +if sys.version_info >= (3, 13): + HAS_PSK: bool +HAS_ALPN: bool +HAS_SSLv2: bool +HAS_SSLv3: bool +HAS_TLSv1: bool +HAS_TLSv1_1: bool +HAS_TLSv1_2: bool +HAS_TLSv1_3: bool + +# version info +OPENSSL_VERSION_NUMBER: int +OPENSSL_VERSION_INFO: tuple[int, int, int, int, int] +OPENSSL_VERSION: str +_OPENSSL_API_VERSION: tuple[int, int, int, int, int] diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index f142820c56c7..a744340afaab 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -1,37 +1,10 @@ -import sys from collections.abc import Callable -from typing import Any, Generic, TypeVar, final, overload -from typing_extensions import Self - -if sys.version_info >= (3, 9): - from types import GenericAlias +from typing import Any, TypeVar, overload +from weakref import CallableProxyType as CallableProxyType, ProxyType as ProxyType, ReferenceType as ReferenceType, ref as ref _C = TypeVar("_C", bound=Callable[..., Any]) _T = TypeVar("_T") -@final -class CallableProxyType(Generic[_C]): # "weakcallableproxy" - def __eq__(self, value: object, /) -> bool: ... - def __getattr__(self, attr: str) -> Any: ... - __call__: _C - -@final -class ProxyType(Generic[_T]): # "weakproxy" - def __eq__(self, value: object, /) -> bool: ... - def __getattr__(self, attr: str) -> Any: ... - -class ReferenceType(Generic[_T]): - __callback__: Callable[[Self], Any] - def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... - def __init__(self, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> None: ... - def __call__(self) -> _T | None: ... - def __eq__(self, value: object, /) -> bool: ... - def __hash__(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -ref = ReferenceType - def getweakrefcount(object: Any, /) -> int: ... def getweakrefs(object: Any, /) -> list[Any]: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 80049cff4ce0..3a43d62a3a60 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -1,184 +1,1717 @@ import os import sys -from _ast import * +import typing_extensions +from _ast import ( + PyCF_ALLOW_TOP_LEVEL_AWAIT as PyCF_ALLOW_TOP_LEVEL_AWAIT, + PyCF_ONLY_AST as PyCF_ONLY_AST, + PyCF_TYPE_COMMENTS as PyCF_TYPE_COMMENTS, +) from _typeshed import ReadableBuffer, Unused from collections.abc import Iterator -from typing import Any, Literal, TypeVar as _TypeVar, overload -from typing_extensions import deprecated +from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload +from typing_extensions import Self, Unpack, deprecated -class _ABC(type): - if sys.version_info >= (3, 9): - def __init__(cls, *args: Unused) -> None: ... +if sys.version_info >= (3, 13): + from _ast import PyCF_OPTIMIZED_AST as PyCF_OPTIMIZED_AST -if sys.version_info < (3, 14): - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Num(Constant, metaclass=_ABC): - value: int | float | complex +# Alias used for fields that must always be valid identifiers +# A string `x` counts as a valid identifier if both the following are True +# (1) `x.isidentifier()` evaluates to `True` +# (2) `keyword.iskeyword(x)` evaluates to `False` +_Identifier: typing_extensions.TypeAlias = str - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Str(Constant, metaclass=_ABC): - value: str - # Aliases for value, for backwards compatibility - s: str +# Used for node end positions in constructor keyword arguments +_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Bytes(Constant, metaclass=_ABC): - value: bytes +# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes +class _Attributes(TypedDict, Generic[_EndPositionT], total=False): + lineno: int + col_offset: int + end_lineno: _EndPositionT + end_col_offset: _EndPositionT + +# The various AST classes are implemented in C, and imported from _ast at runtime, +# but they consider themselves to live in the ast module, +# so we'll define the stubs in this file. +class AST: + if sys.version_info >= (3, 10): + __match_args__ = () + _attributes: ClassVar[tuple[str, ...]] + _fields: ClassVar[tuple[str, ...]] + if sys.version_info >= (3, 13): + _field_types: ClassVar[dict[str, Any]] + + if sys.version_info >= (3, 14): + def __replace__(self) -> Self: ... + +class mod(AST): ... + +class Module(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body", "type_ignores") + body: list[stmt] + type_ignores: list[TypeIgnore] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> None: ... + else: + def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> Self: ... + +class Interactive(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body",) + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ...) -> None: ... + else: + def __init__(self, body: list[stmt]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ...) -> Self: ... + +class Expression(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body",) + body: expr + def __init__(self, body: expr) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: expr = ...) -> Self: ... + +class FunctionType(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("argtypes", "returns") + argtypes: list[expr] + returns: expr + if sys.version_info >= (3, 13): + @overload + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + @overload + def __init__(self, argtypes: list[expr] = ..., *, returns: expr) -> None: ... + else: + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, argtypes: list[expr] = ..., returns: expr = ...) -> Self: ... + +class stmt(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + +class FunctionDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") + name: _Identifier + args: arguments + body: list[stmt] + decorator_list: list[expr] + returns: expr | None + type_comment: str | None + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = ..., + type_comment: str | None = ..., + type_params: list[type_param] = ..., + ) -> Self: ... + +class AsyncFunctionDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") + name: _Identifier + args: arguments + body: list[stmt] + decorator_list: list[expr] + returns: expr | None + type_comment: str | None + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + ) -> Self: ... + +class ClassDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "bases", "keywords", "body", "decorator_list") + name: _Identifier + bases: list[expr] + keywords: list[keyword] + body: list[stmt] + decorator_list: list[expr] + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + bases: list[expr] = ..., + keywords: list[keyword] = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class Return(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Delete(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("targets",) + targets: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Assign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("targets", "value", "type_comment") + targets: list[expr] + value: expr + type_comment: str | None + if sys.version_info >= (3, 13): + @overload + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, targets: list[expr] = ..., *, value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, targets: list[expr] = ..., value: expr = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +if sys.version_info >= (3, 12): + class TypeAlias(stmt): + __match_args__ = ("name", "type_params", "value") + name: Name + type_params: list[type_param] + value: expr + if sys.version_info >= (3, 13): + @overload + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + @overload + def __init__( + self, name: Name, type_params: list[type_param] = ..., *, value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: Name = ..., + type_params: list[type_param] = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + +class AugAssign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "op", "value") + target: Name | Attribute | Subscript + op: operator + value: expr + def __init__( + self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + op: operator = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AnnAssign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "annotation", "value", "simple") + target: Name | Attribute | Subscript + annotation: expr + value: expr | None + simple: int + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None = None, + *, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + annotation: expr = ..., + value: expr | None = ..., + simple: int = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class For(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "body", "orelse", "type_comment") + target: expr + iter: expr + body: list[stmt] + orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AsyncFor(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "body", "orelse", "type_comment") + target: expr + iter: expr + body: list[stmt] + orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class While(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: list[stmt] + orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> Self: ... + +class If(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: list[stmt] + orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class With(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("items", "body", "type_comment") + items: list[withitem] + body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AsyncWith(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("items", "body", "type_comment") + items: list[withitem] + body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +if sys.version_info >= (3, 10): + class Match(stmt): + __match_args__ = ("subject", "cases") + subject: expr + cases: list[match_case] + if sys.version_info >= (3, 13): + def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Raise(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("exc", "cause") + exc: expr | None + cause: expr | None + def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, exc: expr | None = ..., cause: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Try(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("body", "handlers", "orelse", "finalbody") + body: list[stmt] + handlers: list[ExceptHandler] + orelse: list[stmt] + finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +if sys.version_info >= (3, 11): + class TryStar(stmt): + __match_args__ = ("body", "handlers", "orelse", "finalbody") + body: list[stmt] + handlers: list[ExceptHandler] + orelse: list[stmt] + finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class Assert(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "msg") + test: expr + msg: expr | None + def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, msg: expr | None, **kwargs: Unpack[_Attributes]) -> Self: ... + +class Import(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[alias] + if sys.version_info >= (3, 13): + def __init__(self, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class ImportFrom(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("module", "names", "level") + module: str | None + names: list[alias] + level: int + if sys.version_info >= (3, 13): + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, module: str | None = ..., names: list[alias] = ..., level: int = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Global(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> Self: ... + +class Nonlocal(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Expr(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Pass(stmt): ... +class Break(stmt): ... +class Continue(stmt): ... + +class expr(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + +class BoolOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("op", "values") + op: boolop + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, op: boolop, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, op: boolop = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class NamedExpr(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "value") + target: Name + value: expr + def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, target: Name = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class BinOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("left", "op", "right") + left: expr + op: operator + right: expr + def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., op: operator = ..., right: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class UnaryOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("op", "operand") + op: unaryop + operand: expr + def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, op: unaryop = ..., operand: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Lambda(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("args", "body") + args: arguments + body: expr + def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, args: arguments = ..., body: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class IfExp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: expr + orelse: expr + def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: expr = ..., orelse: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Dict(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("keys", "values") + keys: list[expr | None] + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Set(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts",) + elts: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class ListComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class SetComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class DictComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("key", "value", "generators") + key: expr + value: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__( + self, key: expr, value: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, key: expr = ..., value: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class GeneratorExp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Await(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Yield(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class YieldFrom(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Compare(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("left", "ops", "comparators") + left: expr + ops: list[cmpop] + comparators: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, left: expr, ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Call(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("func", "args", "keywords") + func: expr + args: list[expr] + keywords: list[keyword] + if sys.version_info >= (3, 13): + def __init__( + self, func: expr, args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, func: expr = ..., args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class FormattedValue(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "conversion", "format_spec") + value: expr + conversion: int + format_spec: expr | None + def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., conversion: int = ..., format_spec: expr | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class JoinedStr(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("values",) + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Constant(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "kind") + value: Any # None, str, bytes, bool, int, float, complex, Ellipsis + kind: str | None + if sys.version_info < (3, 14): # Aliases for value, for backwards compatibility - s: bytes + s: Any + n: int | float | complex - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class NameConstant(Constant, metaclass=_ABC): ... + def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Ellipsis(Constant, metaclass=_ABC): ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Any = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Attribute(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "attr", "ctx") + value: expr + attr: _Identifier + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., attr: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Subscript(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "slice", "ctx") + value: expr + slice: _Slice + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., slice: _Slice = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Starred(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "ctx") + value: expr + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Name(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("id", "ctx") + id: _Identifier + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, id: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class List(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts", "ctx") + elts: list[expr] + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Tuple(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts", "ctx") + elts: list[expr] + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + if sys.version_info >= (3, 9): + dims: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class slice(AST): ... # deprecated and moved to ast.py for >= (3, 9) if sys.version_info >= (3, 9): - class slice(AST): ... - class ExtSlice(slice): ... - class Index(slice): ... - class Suite(mod): ... - class AugLoad(expr_context): ... - class AugStore(expr_context): ... - class Param(expr_context): ... + _Slice: typing_extensions.TypeAlias = expr + _SliceAttributes: typing_extensions.TypeAlias = _Attributes +else: + # alias for use with variables named slice + _Slice: typing_extensions.TypeAlias = slice -class NodeVisitor: - def visit(self, node: AST) -> Any: ... - def generic_visit(self, node: AST) -> Any: ... - def visit_Module(self, node: Module) -> Any: ... - def visit_Interactive(self, node: Interactive) -> Any: ... - def visit_Expression(self, node: Expression) -> Any: ... - def visit_FunctionDef(self, node: FunctionDef) -> Any: ... - def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ... - def visit_ClassDef(self, node: ClassDef) -> Any: ... - def visit_Return(self, node: Return) -> Any: ... - def visit_Delete(self, node: Delete) -> Any: ... - def visit_Assign(self, node: Assign) -> Any: ... - def visit_AugAssign(self, node: AugAssign) -> Any: ... - def visit_AnnAssign(self, node: AnnAssign) -> Any: ... - def visit_For(self, node: For) -> Any: ... - def visit_AsyncFor(self, node: AsyncFor) -> Any: ... - def visit_While(self, node: While) -> Any: ... - def visit_If(self, node: If) -> Any: ... - def visit_With(self, node: With) -> Any: ... - def visit_AsyncWith(self, node: AsyncWith) -> Any: ... - def visit_Raise(self, node: Raise) -> Any: ... - def visit_Try(self, node: Try) -> Any: ... - def visit_Assert(self, node: Assert) -> Any: ... - def visit_Import(self, node: Import) -> Any: ... - def visit_ImportFrom(self, node: ImportFrom) -> Any: ... - def visit_Global(self, node: Global) -> Any: ... - def visit_Nonlocal(self, node: Nonlocal) -> Any: ... - def visit_Expr(self, node: Expr) -> Any: ... - def visit_Pass(self, node: Pass) -> Any: ... - def visit_Break(self, node: Break) -> Any: ... - def visit_Continue(self, node: Continue) -> Any: ... - def visit_Slice(self, node: Slice) -> Any: ... - def visit_BoolOp(self, node: BoolOp) -> Any: ... - def visit_BinOp(self, node: BinOp) -> Any: ... - def visit_UnaryOp(self, node: UnaryOp) -> Any: ... - def visit_Lambda(self, node: Lambda) -> Any: ... - def visit_IfExp(self, node: IfExp) -> Any: ... - def visit_Dict(self, node: Dict) -> Any: ... - def visit_Set(self, node: Set) -> Any: ... - def visit_ListComp(self, node: ListComp) -> Any: ... - def visit_SetComp(self, node: SetComp) -> Any: ... - def visit_DictComp(self, node: DictComp) -> Any: ... - def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ... - def visit_Await(self, node: Await) -> Any: ... - def visit_Yield(self, node: Yield) -> Any: ... - def visit_YieldFrom(self, node: YieldFrom) -> Any: ... - def visit_Compare(self, node: Compare) -> Any: ... - def visit_Call(self, node: Call) -> Any: ... - def visit_FormattedValue(self, node: FormattedValue) -> Any: ... - def visit_JoinedStr(self, node: JoinedStr) -> Any: ... - def visit_Constant(self, node: Constant) -> Any: ... - def visit_NamedExpr(self, node: NamedExpr) -> Any: ... - def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... - def visit_Attribute(self, node: Attribute) -> Any: ... - def visit_Subscript(self, node: Subscript) -> Any: ... - def visit_Starred(self, node: Starred) -> Any: ... - def visit_Name(self, node: Name) -> Any: ... - def visit_List(self, node: List) -> Any: ... - def visit_Tuple(self, node: Tuple) -> Any: ... - def visit_Del(self, node: Del) -> Any: ... - def visit_Load(self, node: Load) -> Any: ... - def visit_Store(self, node: Store) -> Any: ... - def visit_And(self, node: And) -> Any: ... - def visit_Or(self, node: Or) -> Any: ... - def visit_Add(self, node: Add) -> Any: ... - def visit_BitAnd(self, node: BitAnd) -> Any: ... - def visit_BitOr(self, node: BitOr) -> Any: ... - def visit_BitXor(self, node: BitXor) -> Any: ... - def visit_Div(self, node: Div) -> Any: ... - def visit_FloorDiv(self, node: FloorDiv) -> Any: ... - def visit_LShift(self, node: LShift) -> Any: ... - def visit_Mod(self, node: Mod) -> Any: ... - def visit_Mult(self, node: Mult) -> Any: ... - def visit_MatMult(self, node: MatMult) -> Any: ... - def visit_Pow(self, node: Pow) -> Any: ... - def visit_RShift(self, node: RShift) -> Any: ... - def visit_Sub(self, node: Sub) -> Any: ... - def visit_Invert(self, node: Invert) -> Any: ... - def visit_Not(self, node: Not) -> Any: ... - def visit_UAdd(self, node: UAdd) -> Any: ... - def visit_USub(self, node: USub) -> Any: ... - def visit_Eq(self, node: Eq) -> Any: ... - def visit_Gt(self, node: Gt) -> Any: ... - def visit_GtE(self, node: GtE) -> Any: ... - def visit_In(self, node: In) -> Any: ... - def visit_Is(self, node: Is) -> Any: ... - def visit_IsNot(self, node: IsNot) -> Any: ... - def visit_Lt(self, node: Lt) -> Any: ... - def visit_LtE(self, node: LtE) -> Any: ... - def visit_NotEq(self, node: NotEq) -> Any: ... - def visit_NotIn(self, node: NotIn) -> Any: ... - def visit_comprehension(self, node: comprehension) -> Any: ... - def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ... - def visit_arguments(self, node: arguments) -> Any: ... - def visit_arg(self, node: arg) -> Any: ... - def visit_keyword(self, node: keyword) -> Any: ... - def visit_alias(self, node: alias) -> Any: ... - def visit_withitem(self, node: withitem) -> Any: ... + class _SliceAttributes(TypedDict): ... + +class Slice(_Slice): + if sys.version_info >= (3, 10): + __match_args__ = ("lower", "upper", "step") + lower: expr | None + upper: expr | None + step: expr | None + def __init__( + self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + lower: expr | None = ..., + upper: expr | None = ..., + step: expr | None = ..., + **kwargs: Unpack[_SliceAttributes], + ) -> Self: ... + +class ExtSlice(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + dims: list[slice] + def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... + +class Index(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... + +class expr_context(AST): ... +class AugLoad(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) +class AugStore(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) +class Param(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) + +class Suite(mod): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + body: list[stmt] + def __init__(self, body: list[stmt]) -> None: ... + +class Load(expr_context): ... +class Store(expr_context): ... +class Del(expr_context): ... +class boolop(AST): ... +class And(boolop): ... +class Or(boolop): ... +class operator(AST): ... +class Add(operator): ... +class Sub(operator): ... +class Mult(operator): ... +class MatMult(operator): ... +class Div(operator): ... +class Mod(operator): ... +class Pow(operator): ... +class LShift(operator): ... +class RShift(operator): ... +class BitOr(operator): ... +class BitXor(operator): ... +class BitAnd(operator): ... +class FloorDiv(operator): ... +class unaryop(AST): ... +class Invert(unaryop): ... +class Not(unaryop): ... +class UAdd(unaryop): ... +class USub(unaryop): ... +class cmpop(AST): ... +class Eq(cmpop): ... +class NotEq(cmpop): ... +class Lt(cmpop): ... +class LtE(cmpop): ... +class Gt(cmpop): ... +class GtE(cmpop): ... +class Is(cmpop): ... +class IsNot(cmpop): ... +class In(cmpop): ... +class NotIn(cmpop): ... + +class comprehension(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "ifs", "is_async") + target: expr + iter: expr + ifs: list[expr] + is_async: int + if sys.version_info >= (3, 13): + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr] = ..., *, is_async: int) -> None: ... + else: + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, target: expr = ..., iter: expr = ..., ifs: list[expr] = ..., is_async: int = ...) -> Self: ... + +class excepthandler(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int | None = ..., end_col_offset: int | None = ... + ) -> Self: ... + +class ExceptHandler(excepthandler): + if sys.version_info >= (3, 10): + __match_args__ = ("type", "name", "body") + type: expr | None + name: _Identifier | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__( + self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + type: expr | None = ..., + name: _Identifier | None = ..., + body: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class arguments(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") + posonlyargs: list[arg] + args: list[arg] + vararg: arg | None + kwonlyargs: list[arg] + kw_defaults: list[expr | None] + kwarg: arg | None + defaults: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = None, + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = None, + defaults: list[expr] = ..., + ) -> None: ... + else: + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + *, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None = None, + *, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + defaults: list[expr], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = ..., + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = ..., + defaults: list[expr] = ..., + ) -> Self: ... + +class arg(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("arg", "annotation", "type_comment") + arg: _Identifier + annotation: expr | None + type_comment: str | None + def __init__( + self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + arg: _Identifier = ..., + annotation: expr | None = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class keyword(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("arg", "value") + arg: _Identifier | None + value: expr + @overload + def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, arg: _Identifier | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class alias(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None if sys.version_info >= (3, 10): - def visit_Match(self, node: Match) -> Any: ... - def visit_match_case(self, node: match_case) -> Any: ... - def visit_MatchValue(self, node: MatchValue) -> Any: ... - def visit_MatchSequence(self, node: MatchSequence) -> Any: ... - def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ... - def visit_MatchStar(self, node: MatchStar) -> Any: ... - def visit_MatchMapping(self, node: MatchMapping) -> Any: ... - def visit_MatchClass(self, node: MatchClass) -> Any: ... - def visit_MatchAs(self, node: MatchAs) -> Any: ... - def visit_MatchOr(self, node: MatchOr) -> Any: ... + __match_args__ = ("name", "asname") + name: str + asname: _Identifier | None + def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - if sys.version_info >= (3, 11): - def visit_TryStar(self, node: TryStar) -> Any: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, name: str = ..., asname: _Identifier | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... - if sys.version_info >= (3, 12): - def visit_TypeVar(self, node: TypeVar) -> Any: ... - def visit_ParamSpec(self, node: ParamSpec) -> Any: ... - def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ... - def visit_TypeAlias(self, node: TypeAlias) -> Any: ... +class withitem(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("context_expr", "optional_vars") + context_expr: expr + optional_vars: expr | None + def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... - # visit methods for deprecated nodes - def visit_ExtSlice(self, node: ExtSlice) -> Any: ... - def visit_Index(self, node: Index) -> Any: ... - def visit_Suite(self, node: Suite) -> Any: ... - def visit_AugLoad(self, node: AugLoad) -> Any: ... - def visit_AugStore(self, node: AugStore) -> Any: ... - def visit_Param(self, node: Param) -> Any: ... - def visit_Num(self, node: Num) -> Any: ... - def visit_Str(self, node: Str) -> Any: ... - def visit_Bytes(self, node: Bytes) -> Any: ... - def visit_NameConstant(self, node: NameConstant) -> Any: ... - def visit_Ellipsis(self, node: Ellipsis) -> Any: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, context_expr: expr = ..., optional_vars: expr | None = ...) -> Self: ... -class NodeTransformer(NodeVisitor): - def generic_visit(self, node: AST) -> AST: ... - # TODO: Override the visit_* methods with better return types. - # The usual return type is AST | None, but Iterable[AST] - # is also allowed in some cases -- this needs to be mapped. +if sys.version_info >= (3, 10): + class match_case(AST): + __match_args__ = ("pattern", "guard", "body") + pattern: _Pattern + guard: expr | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... + else: + @overload + def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ... + @overload + def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, pattern: _Pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ... + + class pattern(AST): + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ... + ) -> Self: ... + + # Without the alias, Pyright complains variables named pattern are recursively defined + _Pattern: typing_extensions.TypeAlias = pattern + + class MatchValue(pattern): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchSingleton(pattern): + __match_args__ = ("value",) + value: Literal[True, False] | None + def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Literal[True, False] | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchSequence(pattern): + __match_args__ = ("patterns",) + patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchMapping(pattern): + __match_args__ = ("keys", "patterns", "rest") + keys: list[expr] + patterns: list[pattern] + rest: _Identifier | None + if sys.version_info >= (3, 13): + def __init__( + self, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + keys: list[expr], + patterns: list[pattern], + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class MatchClass(pattern): + __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") + cls: expr + patterns: list[pattern] + kwd_attrs: list[_Identifier] + kwd_patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__( + self, + cls: expr, + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + cls: expr, + patterns: list[pattern], + kwd_attrs: list[_Identifier], + kwd_patterns: list[pattern], + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + cls: expr = ..., + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class MatchStar(pattern): + __match_args__ = ("name",) + name: _Identifier | None + def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchAs(pattern): + __match_args__ = ("pattern", "name") + pattern: _Pattern | None + name: _Identifier | None + def __init__( + self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, pattern: _Pattern | None = ..., name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + + class MatchOr(pattern): + __match_args__ = ("patterns",) + patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + +class type_ignore(AST): ... + +class TypeIgnore(type_ignore): + if sys.version_info >= (3, 10): + __match_args__ = ("lineno", "tag") + lineno: int + tag: str + def __init__(self, lineno: int, tag: str) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, lineno: int = ..., tag: str = ...) -> Self: ... + +if sys.version_info >= (3, 12): + class type_param(AST): + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class TypeVar(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "bound", "default_value") + else: + __match_args__ = ("name", "bound") + name: _Identifier + bound: expr | None + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, + name: _Identifier, + bound: expr | None = None, + default_value: expr | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + bound: expr | None = ..., + default_value: expr | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class ParamSpec(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) + name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + + class TypeVarTuple(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) + name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + +class _ABC(type): + if sys.version_info >= (3, 9): + def __init__(cls, *args: Unused) -> None: ... + +if sys.version_info < (3, 14): + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Num(Constant, metaclass=_ABC): + value: int | float | complex + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Str(Constant, metaclass=_ABC): + value: str + # Aliases for value, for backwards compatibility + s: str + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Bytes(Constant, metaclass=_ABC): + value: bytes + # Aliases for value, for backwards compatibility + s: bytes + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class NameConstant(Constant, metaclass=_ABC): ... + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Ellipsis(Constant, metaclass=_ABC): ... + +# everything below here is defined in ast.py _T = _TypeVar("_T", bound=AST) @@ -332,10 +1865,7 @@ else: feature_version: None | int | tuple[int, int] = None, ) -> AST: ... -if sys.version_info >= (3, 9): - def unparse(ast_obj: AST) -> str: ... - -def copy_location(new_node: _T, old_node: AST) -> _T: ... +def literal_eval(node_or_string: str | AST) -> Any: ... if sys.version_info >= (3, 13): def dump( @@ -355,17 +1885,158 @@ elif sys.version_info >= (3, 9): else: def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ... +def copy_location(new_node: _T, old_node: AST) -> _T: ... def fix_missing_locations(node: _T) -> _T: ... -def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ... def increment_lineno(node: _T, n: int = 1) -> _T: ... -def iter_child_nodes(node: AST) -> Iterator[AST]: ... def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ... -def literal_eval(node_or_string: str | AST) -> Any: ... +def iter_child_nodes(node: AST) -> Iterator[AST]: ... +def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ... def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ... def walk(node: AST) -> Iterator[AST]: ... -if sys.version_info >= (3, 9): - def main() -> None: ... - if sys.version_info >= (3, 14): def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ... + +class NodeVisitor: + def visit(self, node: AST) -> Any: ... + def generic_visit(self, node: AST) -> Any: ... + def visit_Module(self, node: Module) -> Any: ... + def visit_Interactive(self, node: Interactive) -> Any: ... + def visit_Expression(self, node: Expression) -> Any: ... + def visit_FunctionDef(self, node: FunctionDef) -> Any: ... + def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ... + def visit_ClassDef(self, node: ClassDef) -> Any: ... + def visit_Return(self, node: Return) -> Any: ... + def visit_Delete(self, node: Delete) -> Any: ... + def visit_Assign(self, node: Assign) -> Any: ... + def visit_AugAssign(self, node: AugAssign) -> Any: ... + def visit_AnnAssign(self, node: AnnAssign) -> Any: ... + def visit_For(self, node: For) -> Any: ... + def visit_AsyncFor(self, node: AsyncFor) -> Any: ... + def visit_While(self, node: While) -> Any: ... + def visit_If(self, node: If) -> Any: ... + def visit_With(self, node: With) -> Any: ... + def visit_AsyncWith(self, node: AsyncWith) -> Any: ... + def visit_Raise(self, node: Raise) -> Any: ... + def visit_Try(self, node: Try) -> Any: ... + def visit_Assert(self, node: Assert) -> Any: ... + def visit_Import(self, node: Import) -> Any: ... + def visit_ImportFrom(self, node: ImportFrom) -> Any: ... + def visit_Global(self, node: Global) -> Any: ... + def visit_Nonlocal(self, node: Nonlocal) -> Any: ... + def visit_Expr(self, node: Expr) -> Any: ... + def visit_Pass(self, node: Pass) -> Any: ... + def visit_Break(self, node: Break) -> Any: ... + def visit_Continue(self, node: Continue) -> Any: ... + def visit_Slice(self, node: Slice) -> Any: ... + def visit_BoolOp(self, node: BoolOp) -> Any: ... + def visit_BinOp(self, node: BinOp) -> Any: ... + def visit_UnaryOp(self, node: UnaryOp) -> Any: ... + def visit_Lambda(self, node: Lambda) -> Any: ... + def visit_IfExp(self, node: IfExp) -> Any: ... + def visit_Dict(self, node: Dict) -> Any: ... + def visit_Set(self, node: Set) -> Any: ... + def visit_ListComp(self, node: ListComp) -> Any: ... + def visit_SetComp(self, node: SetComp) -> Any: ... + def visit_DictComp(self, node: DictComp) -> Any: ... + def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ... + def visit_Await(self, node: Await) -> Any: ... + def visit_Yield(self, node: Yield) -> Any: ... + def visit_YieldFrom(self, node: YieldFrom) -> Any: ... + def visit_Compare(self, node: Compare) -> Any: ... + def visit_Call(self, node: Call) -> Any: ... + def visit_FormattedValue(self, node: FormattedValue) -> Any: ... + def visit_JoinedStr(self, node: JoinedStr) -> Any: ... + def visit_Constant(self, node: Constant) -> Any: ... + def visit_NamedExpr(self, node: NamedExpr) -> Any: ... + def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... + def visit_Attribute(self, node: Attribute) -> Any: ... + def visit_Subscript(self, node: Subscript) -> Any: ... + def visit_Starred(self, node: Starred) -> Any: ... + def visit_Name(self, node: Name) -> Any: ... + def visit_List(self, node: List) -> Any: ... + def visit_Tuple(self, node: Tuple) -> Any: ... + def visit_Del(self, node: Del) -> Any: ... + def visit_Load(self, node: Load) -> Any: ... + def visit_Store(self, node: Store) -> Any: ... + def visit_And(self, node: And) -> Any: ... + def visit_Or(self, node: Or) -> Any: ... + def visit_Add(self, node: Add) -> Any: ... + def visit_BitAnd(self, node: BitAnd) -> Any: ... + def visit_BitOr(self, node: BitOr) -> Any: ... + def visit_BitXor(self, node: BitXor) -> Any: ... + def visit_Div(self, node: Div) -> Any: ... + def visit_FloorDiv(self, node: FloorDiv) -> Any: ... + def visit_LShift(self, node: LShift) -> Any: ... + def visit_Mod(self, node: Mod) -> Any: ... + def visit_Mult(self, node: Mult) -> Any: ... + def visit_MatMult(self, node: MatMult) -> Any: ... + def visit_Pow(self, node: Pow) -> Any: ... + def visit_RShift(self, node: RShift) -> Any: ... + def visit_Sub(self, node: Sub) -> Any: ... + def visit_Invert(self, node: Invert) -> Any: ... + def visit_Not(self, node: Not) -> Any: ... + def visit_UAdd(self, node: UAdd) -> Any: ... + def visit_USub(self, node: USub) -> Any: ... + def visit_Eq(self, node: Eq) -> Any: ... + def visit_Gt(self, node: Gt) -> Any: ... + def visit_GtE(self, node: GtE) -> Any: ... + def visit_In(self, node: In) -> Any: ... + def visit_Is(self, node: Is) -> Any: ... + def visit_IsNot(self, node: IsNot) -> Any: ... + def visit_Lt(self, node: Lt) -> Any: ... + def visit_LtE(self, node: LtE) -> Any: ... + def visit_NotEq(self, node: NotEq) -> Any: ... + def visit_NotIn(self, node: NotIn) -> Any: ... + def visit_comprehension(self, node: comprehension) -> Any: ... + def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ... + def visit_arguments(self, node: arguments) -> Any: ... + def visit_arg(self, node: arg) -> Any: ... + def visit_keyword(self, node: keyword) -> Any: ... + def visit_alias(self, node: alias) -> Any: ... + def visit_withitem(self, node: withitem) -> Any: ... + if sys.version_info >= (3, 10): + def visit_Match(self, node: Match) -> Any: ... + def visit_match_case(self, node: match_case) -> Any: ... + def visit_MatchValue(self, node: MatchValue) -> Any: ... + def visit_MatchSequence(self, node: MatchSequence) -> Any: ... + def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ... + def visit_MatchStar(self, node: MatchStar) -> Any: ... + def visit_MatchMapping(self, node: MatchMapping) -> Any: ... + def visit_MatchClass(self, node: MatchClass) -> Any: ... + def visit_MatchAs(self, node: MatchAs) -> Any: ... + def visit_MatchOr(self, node: MatchOr) -> Any: ... + + if sys.version_info >= (3, 11): + def visit_TryStar(self, node: TryStar) -> Any: ... + + if sys.version_info >= (3, 12): + def visit_TypeVar(self, node: TypeVar) -> Any: ... + def visit_ParamSpec(self, node: ParamSpec) -> Any: ... + def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ... + def visit_TypeAlias(self, node: TypeAlias) -> Any: ... + + # visit methods for deprecated nodes + def visit_ExtSlice(self, node: ExtSlice) -> Any: ... + def visit_Index(self, node: Index) -> Any: ... + def visit_Suite(self, node: Suite) -> Any: ... + def visit_AugLoad(self, node: AugLoad) -> Any: ... + def visit_AugStore(self, node: AugStore) -> Any: ... + def visit_Param(self, node: Param) -> Any: ... + def visit_Num(self, node: Num) -> Any: ... + def visit_Str(self, node: Str) -> Any: ... + def visit_Bytes(self, node: Bytes) -> Any: ... + def visit_NameConstant(self, node: NameConstant) -> Any: ... + def visit_Ellipsis(self, node: Ellipsis) -> Any: ... + +class NodeTransformer(NodeVisitor): + def generic_visit(self, node: AST) -> AST: ... + # TODO: Override the visit_* methods with better return types. + # The usual return type is AST | None, but Iterable[AST] + # is also allowed in some cases -- this needs to be mapped. + +if sys.version_info >= (3, 9): + def unparse(ast_obj: AST) -> str: ... + +if sys.version_info >= (3, 9): + def main() -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index eed688fc792a..ead64070671f 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -1,5 +1,11 @@ import ssl import sys +from _asyncio import ( + _get_running_loop as _get_running_loop, + _set_running_loop as _set_running_loop, + get_event_loop as get_event_loop, + get_running_loop as get_running_loop, +) from _typeshed import FileDescriptorLike, ReadableBuffer, StrPath, Unused, WriteableBuffer from abc import ABCMeta, abstractmethod from collections.abc import Callable, Sequence @@ -632,7 +638,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): def get_event_loop_policy() -> AbstractEventLoopPolicy: ... def set_event_loop_policy(policy: AbstractEventLoopPolicy | None) -> None: ... -def get_event_loop() -> AbstractEventLoop: ... def set_event_loop(loop: AbstractEventLoop | None) -> None: ... def new_event_loop() -> AbstractEventLoop: ... @@ -646,7 +651,3 @@ if sys.version_info < (3, 14): else: def get_child_watcher() -> AbstractChildWatcher: ... def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... - -def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... -def _get_running_loop() -> AbstractEventLoop: ... -def get_running_loop() -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index e19fd53f3311..28e6ca8c86a3 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -1,15 +1,10 @@ -import sys -from collections.abc import Awaitable, Callable, Generator, Iterable +from _asyncio import Future as Future from concurrent.futures._base import Future as _ConcurrentFuture -from contextvars import Context -from typing import Any, Literal, TypeVar -from typing_extensions import Self, TypeIs +from typing import Any, TypeVar +from typing_extensions import TypeIs from .events import AbstractEventLoop -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") @@ -18,40 +13,4 @@ _T = TypeVar("_T") # but it leads to circular import error in pytype tool. # That's why the import order is reversed. def isfuture(obj: object) -> TypeIs[Future[Any]]: ... - -class Future(Awaitable[_T], Iterable[_T]): - _state: str - @property - def _exception(self) -> BaseException | None: ... - _blocking: bool - @property - def _log_traceback(self) -> bool: ... - @_log_traceback.setter - def _log_traceback(self, val: Literal[False]) -> None: ... - _asyncio_future_blocking: bool # is a part of duck-typing contract for `Future` - def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ... - def __del__(self) -> None: ... - def get_loop(self) -> AbstractEventLoop: ... - @property - def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... - def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... - if sys.version_info >= (3, 9): - def cancel(self, msg: Any | None = None) -> bool: ... - else: - def cancel(self) -> bool: ... - - def cancelled(self) -> bool: ... - def done(self) -> bool: ... - def result(self) -> _T: ... - def exception(self) -> BaseException | None: ... - def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... - def set_result(self, result: _T, /) -> None: ... - def set_exception(self, exception: type | BaseException, /) -> None: ... - def __iter__(self) -> Generator[Any, None, _T]: ... - def __await__(self) -> Generator[Any, None, _T]: ... - @property - def _loop(self) -> AbstractEventLoop: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ... diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 0be5249e2169..ed95583c1847 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -1,7 +1,7 @@ import ssl import sys from _typeshed import ReadableBuffer, StrPath -from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence, Sized +from collections.abc import Awaitable, Callable, Iterable, Sequence, Sized from types import ModuleType from typing import Any, Protocol, SupportsIndex from typing_extensions import Self, TypeAlias @@ -137,7 +137,7 @@ class StreamWriter: elif sys.version_info >= (3, 11): def __del__(self) -> None: ... -class StreamReader(AsyncIterator[bytes]): +class StreamReader: def __init__(self, limit: int = 65536, loop: events.AbstractEventLoop | None = None) -> None: ... def exception(self) -> Exception: ... def set_exception(self, exc: Exception) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index bb423e857399..47d1bb13e4a3 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -1,16 +1,20 @@ import concurrent.futures import sys +from _asyncio import ( + Task as Task, + _enter_task as _enter_task, + _leave_task as _leave_task, + _register_task as _register_task, + _unregister_task as _unregister_task, +) from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator -from types import FrameType -from typing import Any, Literal, Protocol, TextIO, TypeVar, overload +from typing import Any, Literal, Protocol, TypeVar, overload from typing_extensions import TypeAlias from . import _CoroutineLike from .events import AbstractEventLoop from .futures import Future -if sys.version_info >= (3, 9): - from types import GenericAlias if sys.version_info >= (3, 11): from contextvars import Context @@ -400,58 +404,6 @@ elif sys.version_info >= (3, 9): else: _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co] -# mypy and pyright complain that a subclass of an invariant class shouldn't be covariant. -# While this is true in general, here it's sort-of okay to have a covariant subclass, -# since the only reason why `asyncio.Future` is invariant is the `set_result()` method, -# and `asyncio.Task.set_result()` always raises. -class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] - if sys.version_info >= (3, 12): - def __init__( - self, - coro: _TaskCompatibleCoro[_T_co], - *, - loop: AbstractEventLoop = ..., - name: str | None = ..., - context: Context | None = None, - eager_start: bool = False, - ) -> None: ... - elif sys.version_info >= (3, 11): - def __init__( - self, - coro: _TaskCompatibleCoro[_T_co], - *, - loop: AbstractEventLoop = ..., - name: str | None = ..., - context: Context | None = None, - ) -> None: ... - else: - def __init__( - self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... - ) -> None: ... - - if sys.version_info >= (3, 12): - def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ... - else: - def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... - - def get_name(self) -> str: ... - def set_name(self, value: object, /) -> None: ... - if sys.version_info >= (3, 12): - def get_context(self) -> Context: ... - - def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ... - def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ... - if sys.version_info >= (3, 11): - def cancelling(self) -> int: ... - def uncancel(self) -> int: ... - if sys.version_info < (3, 9): - @classmethod - def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... - @classmethod - def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... if sys.version_info >= (3, 11): @@ -460,9 +412,10 @@ if sys.version_info >= (3, 11): else: def create_task(coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... -def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... -def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... -def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... +if sys.version_info >= (3, 12): + from _asyncio import current_task as current_task +else: + def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... if sys.version_info >= (3, 12): _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True) @@ -499,6 +452,3 @@ if sys.version_info >= (3, 12): name: str | None = None, context: Context | None = None, ) -> Task[_T_co]: ... - -def _register_task(task: Task[Any]) -> None: ... -def _unregister_task(task: Task[Any]) -> None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 0999fb1d6c36..adfe1d83752b 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -799,6 +799,11 @@ class memoryview(Sequence[_I]): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... + # These are inherited from the Sequence ABC, but don't actually exist on memoryview. + # See https://github.com/python/cpython/issues/125420 + index: ClassVar[None] # type: ignore[assignment] + count: ClassVar[None] # type: ignore[assignment] + @final class bool(int): def __new__(cls, o: object = ..., /) -> Self: ... @@ -1110,7 +1115,7 @@ class frozenset(AbstractSet[_T_co]): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class enumerate(Iterator[tuple[int, _T]]): - def __new__(cls, iterable: Iterable[_T], start: int = ...) -> Self: ... + def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index a7837e1b9ff8..cd11baa1a0c1 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -131,7 +131,7 @@ class BZ2File(BaseStream, IO[bytes]): @final class BZ2Compressor: - def __init__(self, compresslevel: int = ...) -> None: ... + def __init__(self, compresslevel: int = 9) -> None: ... def compress(self, data: ReadableBuffer, /) -> bytes: ... def flush(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi index 02689238a9a5..54971f3ae93c 100644 --- a/mypy/typeshed/stdlib/code.pyi +++ b/mypy/typeshed/stdlib/code.pyi @@ -12,7 +12,11 @@ class InteractiveInterpreter: def __init__(self, locals: Mapping[str, Any] | None = None) -> None: ... def runsource(self, source: str, filename: str = "", symbol: str = "single") -> bool: ... def runcode(self, code: CodeType) -> None: ... - def showsyntaxerror(self, filename: str | None = None) -> None: ... + if sys.version_info >= (3, 13): + def showsyntaxerror(self, filename: str | None = None, *, source: str = "") -> None: ... + else: + def showsyntaxerror(self, filename: str | None = None) -> None: ... + def showtraceback(self) -> None: ... def write(self, data: str) -> None: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index b2ed53e4427e..2d136318813c 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -17,7 +17,6 @@ if sys.version_info >= (3, 10): Mapping, MutableMapping, MutableSequence, - Reversible, Sequence, ValuesView, ) @@ -331,13 +330,13 @@ class Counter(dict[_T, int], Generic[_T]): # The pure-Python implementations of the "views" classes # These are exposed at runtime in `collections/__init__.py` -class _OrderedDictKeysView(KeysView[_KT_co], Reversible[_KT_co]): +class _OrderedDictKeysView(KeysView[_KT_co]): def __reversed__(self) -> Iterator[_KT_co]: ... -class _OrderedDictItemsView(ItemsView[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): +class _OrderedDictItemsView(ItemsView[_KT_co, _VT_co]): def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... -class _OrderedDictValuesView(ValuesView[_VT_co], Reversible[_VT_co]): +class _OrderedDictValuesView(ValuesView[_VT_co]): def __reversed__(self) -> Iterator[_VT_co]: ... # The C implementations of the "views" classes @@ -345,18 +344,18 @@ class _OrderedDictValuesView(ValuesView[_VT_co], Reversible[_VT_co]): # but they are not exposed anywhere) # pyright doesn't have a specific error code for subclassing error! @final -class _odict_keys(dict_keys[_KT_co, _VT_co], Reversible[_KT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +class _odict_keys(dict_keys[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_KT_co]: ... @final -class _odict_items(dict_items[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +class _odict_items(dict_items[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... @final -class _odict_values(dict_values[_KT_co, _VT_co], Reversible[_VT_co], Generic[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +class _odict_values(dict_values[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_VT_co]: ... -class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): +class OrderedDict(dict[_KT, _VT]): def popitem(self, last: bool = True) -> tuple[_KT, _VT]: ... def move_to_end(self, key: _KT, last: bool = True) -> None: ... def copy(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index d3706a9c15a6..a1de3d679b23 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -19,8 +19,9 @@ _global_shutdown: bool class _ThreadWakeup: _closed: bool - _reader: Connection - _writer: Connection + # Any: Unused send and recv methods + _reader: Connection[Any, Any] + _writer: Connection[Any, Any] def close(self) -> None: ... def wakeup(self) -> None: ... def clear(self) -> None: ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index daf218d5a138..fb34251693ad 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -143,13 +143,15 @@ class _RedirectStream(AbstractContextManager[_T_io, None]): class redirect_stdout(_RedirectStream[_T_io]): ... class redirect_stderr(_RedirectStream[_T_io]): ... -# In reality this is a subclass of `AbstractContextManager`; -# see #7961 for why we don't do that in the stub -class ExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): +class _BaseExitStack(Generic[_ExitT_co]): def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def pop_all(self) -> Self: ... + +# In reality this is a subclass of `AbstractContextManager`; +# see #7961 for why we don't do that in the stub +class ExitStack(_BaseExitStack[_ExitT_co], metaclass=abc.ABCMeta): def close(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( @@ -163,16 +165,12 @@ _ACM_EF = TypeVar("_ACM_EF", bound=AbstractAsyncContextManager[Any, Any] | _Exit # In reality this is a subclass of `AbstractAsyncContextManager`; # see #7961 for why we don't do that in the stub -class AsyncExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): - def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... +class AsyncExitStack(_BaseExitStack[_ExitT_co], metaclass=abc.ABCMeta): async def enter_async_context(self, cm: AbstractAsyncContextManager[_T, _ExitT_co]) -> _T: ... - def push(self, exit: _CM_EF) -> _CM_EF: ... def push_async_exit(self, exit: _ACM_EF) -> _ACM_EF: ... - def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def push_async_callback( self, callback: Callable[_P, Awaitable[_T]], /, *args: _P.args, **kwds: _P.kwargs ) -> Callable[_P, Awaitable[_T]]: ... - def pop_all(self) -> Self: ... async def aclose(self) -> None: ... async def __aenter__(self) -> Self: ... async def __aexit__( diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 24f0db332165..89a019753f04 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -1,12 +1,10 @@ import sys - -# actually csv.Dialect is a different class to _csv.Dialect at runtime, but for typing purposes, they're identical from _csv import ( QUOTE_ALL as QUOTE_ALL, QUOTE_MINIMAL as QUOTE_MINIMAL, QUOTE_NONE as QUOTE_NONE, QUOTE_NONNUMERIC as QUOTE_NONNUMERIC, - Dialect as Dialect, + Dialect as _Dialect, Error as Error, __version__ as __version__, _DialectLike, @@ -26,7 +24,7 @@ if sys.version_info >= (3, 12): from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS from _typeshed import SupportsWrite -from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence +from collections.abc import Collection, Iterable, Mapping, Sequence from typing import Any, Generic, Literal, TypeVar, overload from typing_extensions import Self @@ -61,11 +59,14 @@ if sys.version_info < (3, 13): _T = TypeVar("_T") +class Dialect(_Dialect): + def __init__(self) -> None: ... + class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... -class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): +class DictReader(Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index 1df184dbaa60..939cec0cede9 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -1,5 +1,5 @@ from _curses import * -from _curses import _CursesWindow as _CursesWindow +from _curses import window as window from collections.abc import Callable from typing import TypeVar from typing_extensions import Concatenate, ParamSpec @@ -19,4 +19,9 @@ COLS: int COLORS: int COLOR_PAIRS: int -def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... +def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... + +# typeshed used the name _CursesWindow for the underlying C class before +# it was mapped to the name 'window' in 3.8. +# Kept here as a legacy alias in case any third-party code is relying on it. +_CursesWindow = window diff --git a/mypy/typeshed/stdlib/curses/panel.pyi b/mypy/typeshed/stdlib/curses/panel.pyi index 3d3448bd9584..d94f76635d8f 100644 --- a/mypy/typeshed/stdlib/curses/panel.pyi +++ b/mypy/typeshed/stdlib/curses/panel.pyi @@ -1,4 +1,4 @@ -from _curses import _CursesWindow +from _curses import window version: str @@ -9,14 +9,14 @@ class _Curses_Panel: # type is (note the s def hidden(self) -> bool: ... def hide(self) -> None: ... def move(self, y: int, x: int) -> None: ... - def replace(self, win: _CursesWindow) -> None: ... + def replace(self, win: window) -> None: ... def set_userptr(self, obj: object) -> None: ... def show(self) -> None: ... def top(self) -> None: ... def userptr(self) -> object: ... - def window(self) -> _CursesWindow: ... + def window(self) -> window: ... def bottom_panel() -> _Curses_Panel: ... -def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ... +def new_panel(win: window, /) -> _Curses_Panel: ... def top_panel() -> _Curses_Panel: ... def update_panels() -> _Curses_Panel: ... diff --git a/mypy/typeshed/stdlib/curses/textpad.pyi b/mypy/typeshed/stdlib/curses/textpad.pyi index ce6eed09b289..48ef67c9d85f 100644 --- a/mypy/typeshed/stdlib/curses/textpad.pyi +++ b/mypy/typeshed/stdlib/curses/textpad.pyi @@ -1,11 +1,11 @@ -from _curses import _CursesWindow +from _curses import window from collections.abc import Callable -def rectangle(win: _CursesWindow, uly: int, ulx: int, lry: int, lrx: int) -> None: ... +def rectangle(win: window, uly: int, ulx: int, lry: int, lrx: int) -> None: ... class Textbox: stripspaces: bool - def __init__(self, win: _CursesWindow, insert_mode: bool = False) -> None: ... + def __init__(self, win: window, insert_mode: bool = False) -> None: ... def edit(self, validate: Callable[[int], int] | None = None) -> str: ... def do_command(self, ch: str | int) -> None: ... def gather(self) -> str: ... diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 35fc4405f11b..3da75ec192ac 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -1,2 +1,256 @@ -from _decimal import * -from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__ +import numbers +from _decimal import ( + HAVE_CONTEXTVAR as HAVE_CONTEXTVAR, + HAVE_THREADS as HAVE_THREADS, + MAX_EMAX as MAX_EMAX, + MAX_PREC as MAX_PREC, + MIN_EMIN as MIN_EMIN, + MIN_ETINY as MIN_ETINY, + ROUND_05UP as ROUND_05UP, + ROUND_CEILING as ROUND_CEILING, + ROUND_DOWN as ROUND_DOWN, + ROUND_FLOOR as ROUND_FLOOR, + ROUND_HALF_DOWN as ROUND_HALF_DOWN, + ROUND_HALF_EVEN as ROUND_HALF_EVEN, + ROUND_HALF_UP as ROUND_HALF_UP, + ROUND_UP as ROUND_UP, + BasicContext as BasicContext, + DefaultContext as DefaultContext, + ExtendedContext as ExtendedContext, + __libmpdec_version__ as __libmpdec_version__, + __version__ as __version__, + getcontext as getcontext, + localcontext as localcontext, + setcontext as setcontext, +) +from collections.abc import Container, Sequence +from typing import Any, ClassVar, Literal, NamedTuple, overload +from typing_extensions import Self, TypeAlias + +_Decimal: TypeAlias = Decimal | int +_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int] +_ComparableNum: TypeAlias = Decimal | float | numbers.Rational +_TrapType: TypeAlias = type[DecimalException] + +# At runtime, these classes are implemented in C as part of "_decimal". +# However, they consider themselves to live in "decimal", so we'll put them here. + +class DecimalTuple(NamedTuple): + sign: int + digits: tuple[int, ...] + exponent: int | Literal["n", "N", "F"] + +class DecimalException(ArithmeticError): ... +class Clamped(DecimalException): ... +class InvalidOperation(DecimalException): ... +class ConversionSyntax(InvalidOperation): ... +class DivisionByZero(DecimalException, ZeroDivisionError): ... +class DivisionImpossible(InvalidOperation): ... +class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... +class Inexact(DecimalException): ... +class InvalidContext(InvalidOperation): ... +class Rounded(DecimalException): ... +class Subnormal(DecimalException): ... +class Overflow(Inexact, Rounded): ... +class Underflow(Inexact, Rounded, Subnormal): ... +class FloatOperation(DecimalException, TypeError): ... + +class Decimal: + def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... + @classmethod + def from_float(cls, f: float, /) -> Self: ... + def __bool__(self) -> bool: ... + def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __hash__(self) -> int: ... + def as_tuple(self) -> DecimalTuple: ... + def as_integer_ratio(self) -> tuple[int, int]: ... + def to_eng_string(self, context: Context | None = None) -> str: ... + def __abs__(self) -> Decimal: ... + def __add__(self, value: _Decimal, /) -> Decimal: ... + def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __eq__(self, value: object, /) -> bool: ... + def __floordiv__(self, value: _Decimal, /) -> Decimal: ... + def __ge__(self, value: _ComparableNum, /) -> bool: ... + def __gt__(self, value: _ComparableNum, /) -> bool: ... + def __le__(self, value: _ComparableNum, /) -> bool: ... + def __lt__(self, value: _ComparableNum, /) -> bool: ... + def __mod__(self, value: _Decimal, /) -> Decimal: ... + def __mul__(self, value: _Decimal, /) -> Decimal: ... + def __neg__(self) -> Decimal: ... + def __pos__(self) -> Decimal: ... + def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... + def __radd__(self, value: _Decimal, /) -> Decimal: ... + def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... + def __rmod__(self, value: _Decimal, /) -> Decimal: ... + def __rmul__(self, value: _Decimal, /) -> Decimal: ... + def __rsub__(self, value: _Decimal, /) -> Decimal: ... + def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... + def __sub__(self, value: _Decimal, /) -> Decimal: ... + def __truediv__(self, value: _Decimal, /) -> Decimal: ... + def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __float__(self) -> float: ... + def __int__(self) -> int: ... + def __trunc__(self) -> int: ... + @property + def real(self) -> Decimal: ... + @property + def imag(self) -> Decimal: ... + def conjugate(self) -> Decimal: ... + def __complex__(self) -> complex: ... + @overload + def __round__(self) -> int: ... + @overload + def __round__(self, ndigits: int, /) -> Decimal: ... + def __floor__(self) -> int: ... + def __ceil__(self) -> int: ... + def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... + def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... + def normalize(self, context: Context | None = None) -> Decimal: ... + def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... + def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def sqrt(self, context: Context | None = None) -> Decimal: ... + def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def adjusted(self) -> int: ... + def canonical(self) -> Decimal: ... + def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def copy_abs(self) -> Decimal: ... + def copy_negate(self) -> Decimal: ... + def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def exp(self, context: Context | None = None) -> Decimal: ... + def is_canonical(self) -> bool: ... + def is_finite(self) -> bool: ... + def is_infinite(self) -> bool: ... + def is_nan(self) -> bool: ... + def is_normal(self, context: Context | None = None) -> bool: ... + def is_qnan(self) -> bool: ... + def is_signed(self) -> bool: ... + def is_snan(self) -> bool: ... + def is_subnormal(self, context: Context | None = None) -> bool: ... + def is_zero(self) -> bool: ... + def ln(self, context: Context | None = None) -> Decimal: ... + def log10(self, context: Context | None = None) -> Decimal: ... + def logb(self, context: Context | None = None) -> Decimal: ... + def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def logical_invert(self, context: Context | None = None) -> Decimal: ... + def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def next_minus(self, context: Context | None = None) -> Decimal: ... + def next_plus(self, context: Context | None = None) -> Decimal: ... + def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def number_class(self, context: Context | None = None) -> str: ... + def radix(self) -> Decimal: ... + def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... + +class Context: + # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, + # even settable attributes like `prec` and `rounding`, + # but that's inexpressable in the stub. + # Type checkers either ignore it or misinterpret it + # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub + prec: int + rounding: str + Emin: int + Emax: int + capitals: int + clamp: int + traps: dict[_TrapType, bool] + flags: dict[_TrapType, bool] + def __init__( + self, + prec: int | None = ..., + rounding: str | None = ..., + Emin: int | None = ..., + Emax: int | None = ..., + capitals: int | None = ..., + clamp: int | None = ..., + flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + _ignored_flags: list[_TrapType] | None = ..., + ) -> None: ... + def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... + def clear_flags(self) -> None: ... + def clear_traps(self) -> None: ... + def copy(self) -> Context: ... + def __copy__(self) -> Context: ... + # see https://github.com/python/cpython/issues/94107 + __hash__: ClassVar[None] # type: ignore[assignment] + def Etiny(self) -> int: ... + def Etop(self) -> int: ... + def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... + def create_decimal_from_float(self, f: float, /) -> Decimal: ... + def abs(self, x: _Decimal, /) -> Decimal: ... + def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def canonical(self, x: Decimal, /) -> Decimal: ... + def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def copy_abs(self, x: _Decimal, /) -> Decimal: ... + def copy_decimal(self, x: _Decimal, /) -> Decimal: ... + def copy_negate(self, x: _Decimal, /) -> Decimal: ... + def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def exp(self, x: _Decimal, /) -> Decimal: ... + def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... + def is_canonical(self, x: _Decimal, /) -> bool: ... + def is_finite(self, x: _Decimal, /) -> bool: ... + def is_infinite(self, x: _Decimal, /) -> bool: ... + def is_nan(self, x: _Decimal, /) -> bool: ... + def is_normal(self, x: _Decimal, /) -> bool: ... + def is_qnan(self, x: _Decimal, /) -> bool: ... + def is_signed(self, x: _Decimal, /) -> bool: ... + def is_snan(self, x: _Decimal, /) -> bool: ... + def is_subnormal(self, x: _Decimal, /) -> bool: ... + def is_zero(self, x: _Decimal, /) -> bool: ... + def ln(self, x: _Decimal, /) -> Decimal: ... + def log10(self, x: _Decimal, /) -> Decimal: ... + def logb(self, x: _Decimal, /) -> Decimal: ... + def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_invert(self, x: _Decimal, /) -> Decimal: ... + def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def minus(self, x: _Decimal, /) -> Decimal: ... + def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def next_minus(self, x: _Decimal, /) -> Decimal: ... + def next_plus(self, x: _Decimal, /) -> Decimal: ... + def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def normalize(self, x: _Decimal, /) -> Decimal: ... + def number_class(self, x: _Decimal, /) -> str: ... + def plus(self, x: _Decimal, /) -> Decimal: ... + def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... + def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def radix(self) -> Decimal: ... + def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... + def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def sqrt(self, x: _Decimal, /) -> Decimal: ... + def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def to_eng_string(self, x: _Decimal, /) -> str: ... + def to_sci_string(self, x: _Decimal, /) -> str: ... + def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... + def to_integral_value(self, x: _Decimal, /) -> Decimal: ... + def to_integral(self, x: _Decimal, /) -> Decimal: ... diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi index 9e1f653c9d78..900224eabb3d 100644 --- a/mypy/typeshed/stdlib/email/_policybase.pyi +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -1,11 +1,29 @@ from abc import ABCMeta, abstractmethod -from collections.abc import Callable from email.errors import MessageDefect from email.header import Header from email.message import Message +from typing import Generic, Protocol, TypeVar, type_check_only from typing_extensions import Self -class _PolicyBase: +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) + +@type_check_only +class _MessageFactory(Protocol[_MessageT]): + def __call__(self, policy: Policy[_MessageT]) -> _MessageT: ... + +# Policy below is the only known direct subclass of _PolicyBase. We therefore +# assume that the __init__ arguments and attributes of _PolicyBase are +# the same as those of Policy. +class _PolicyBase(Generic[_MessageT]): + max_line_length: int | None + linesep: str + cte_type: str + raise_on_defect: bool + mangle_from_: bool + message_factory: _MessageFactory[_MessageT] | None + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool + def __init__( self, *, @@ -14,7 +32,7 @@ class _PolicyBase: cte_type: str = "8bit", raise_on_defect: bool = False, mangle_from_: bool = ..., # default depends on sub-class - message_factory: Callable[[Policy], Message] | None = None, + message_factory: _MessageFactory[_MessageT] | None = None, # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 verify_generated_headers: bool = True, ) -> None: ... @@ -26,24 +44,15 @@ class _PolicyBase: cte_type: str = ..., raise_on_defect: bool = ..., mangle_from_: bool = ..., - message_factory: Callable[[Policy], Message] | None = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 verify_generated_headers: bool = ..., ) -> Self: ... def __add__(self, other: Policy) -> Self: ... -class Policy(_PolicyBase, metaclass=ABCMeta): - max_line_length: int | None - linesep: str - cte_type: str - raise_on_defect: bool - mangle_from_: bool - message_factory: Callable[[Policy], Message] | None - # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 - verify_generated_headers: bool - - def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... - def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... +class Policy(_PolicyBase[_MessageT], metaclass=ABCMeta): + def handle_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ... + def register_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ... def header_max_count(self, name: str) -> int | None: ... @abstractmethod def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... @@ -56,11 +65,11 @@ class Policy(_PolicyBase, metaclass=ABCMeta): @abstractmethod def fold_binary(self, name: str, value: str) -> bytes: ... -class Compat32(Policy): +class Compat32(Policy[_MessageT]): def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... def header_fetch_parse(self, name: str, value: str) -> str | Header: ... # type: ignore[override] def fold(self, name: str, value: str) -> str: ... def fold_binary(self, name: str, value: str) -> bytes: ... -compat32: Compat32 +compat32: Compat32[Message] diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi index 22920fc99c69..8c268ca1ae18 100644 --- a/mypy/typeshed/stdlib/email/feedparser.pyi +++ b/mypy/typeshed/stdlib/email/feedparser.pyi @@ -5,19 +5,19 @@ from typing import Generic, TypeVar, overload __all__ = ["FeedParser", "BytesFeedParser"] -_MessageT = TypeVar("_MessageT", bound=Message) +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) class FeedParser(Generic[_MessageT]): @overload - def __init__(self: FeedParser[Message], _factory: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: FeedParser[Message], _factory: None = None, *, policy: Policy[Message] = ...) -> None: ... @overload - def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def feed(self, data: str) -> None: ... def close(self) -> _MessageT: ... class BytesFeedParser(FeedParser[_MessageT]): @overload - def __init__(self: BytesFeedParser[Message], _factory: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: BytesFeedParser[Message], _factory: None = None, *, policy: Policy[Message] = ...) -> None: ... @overload - def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def feed(self, data: bytes | bytearray) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/email/generator.pyi b/mypy/typeshed/stdlib/email/generator.pyi index faa6551fc925..dfa0604a20a9 100644 --- a/mypy/typeshed/stdlib/email/generator.pyi +++ b/mypy/typeshed/stdlib/email/generator.pyi @@ -1,34 +1,71 @@ from _typeshed import SupportsWrite from email.message import Message from email.policy import Policy +from typing import Any, Generic, TypeVar, overload from typing_extensions import Self __all__ = ["Generator", "DecodedGenerator", "BytesGenerator"] -class Generator: - def clone(self, fp: SupportsWrite[str]) -> Self: ... - def write(self, s: str) -> None: ... +# By default, generators do not have a message policy. +_MessageT = TypeVar("_MessageT", bound=Message, default=Any) + +class Generator(Generic[_MessageT]): + maxheaderlen: int | None + policy: Policy[_MessageT] | None + @overload + def __init__( + self: Generator[Any], # The Policy of the message is used. + outfp: SupportsWrite[str], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[str], mangle_from_: bool | None = None, maxheaderlen: int | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... - def flatten(self, msg: Message, unixfrom: bool = False, linesep: str | None = None) -> None: ... + def write(self, s: str) -> None: ... + def flatten(self, msg: _MessageT, unixfrom: bool = False, linesep: str | None = None) -> None: ... + def clone(self, fp: SupportsWrite[str]) -> Self: ... -class BytesGenerator(Generator): +class BytesGenerator(Generator[_MessageT]): + @overload + def __init__( + self: BytesGenerator[Any], # The Policy of the message is used. + outfp: SupportsWrite[bytes], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[bytes], mangle_from_: bool | None = None, maxheaderlen: int | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... -class DecodedGenerator(Generator): +class DecodedGenerator(Generator[_MessageT]): + @overload + def __init__( + self: DecodedGenerator[Any], # The Policy of the message is used. + outfp: SupportsWrite[str], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + fmt: str | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[str], @@ -36,5 +73,5 @@ class DecodedGenerator(Generator): maxheaderlen: int | None = None, fmt: str | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 7e80f13adb8f..f04b014431f9 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -30,10 +30,13 @@ class _SupportsDecodeToPayload(Protocol): def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ... class Message(Generic[_HeaderT, _HeaderParamT]): - policy: Policy # undocumented + # The policy attributes and arguments in this class and its subclasses + # would ideally use Policy[Self], but this is not possible. + policy: Policy[Any] # undocumented preamble: str | None epilogue: str | None defects: list[MessageDefect] + def __init__(self, policy: Policy[Any] = ...) -> None: ... def is_multipart(self) -> bool: ... def set_unixfrom(self, unixfrom: str) -> None: ... def get_unixfrom(self) -> str | None: ... @@ -126,8 +129,8 @@ class Message(Generic[_HeaderT, _HeaderParamT]): def get_charsets(self, failobj: _T) -> list[str | _T]: ... def walk(self) -> Generator[Self, None, None]: ... def get_content_disposition(self) -> str | None: ... - def as_string(self, unixfrom: bool = False, maxheaderlen: int = 0, policy: Policy | None = None) -> str: ... - def as_bytes(self, unixfrom: bool = False, policy: Policy | None = None) -> bytes: ... + def as_string(self, unixfrom: bool = False, maxheaderlen: int = 0, policy: Policy[Any] | None = None) -> str: ... + def as_bytes(self, unixfrom: bool = False, policy: Policy[Any] | None = None) -> bytes: ... def __bytes__(self) -> bytes: ... def set_param( self, @@ -139,13 +142,12 @@ class Message(Generic[_HeaderT, _HeaderParamT]): language: str = "", replace: bool = False, ) -> None: ... - def __init__(self, policy: Policy = ...) -> None: ... # The following two methods are undocumented, but a source code comment states that they are public API def set_raw(self, name: str, value: _HeaderParamT) -> None: ... def raw_items(self) -> Iterator[tuple[str, _HeaderT]]: ... class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): - def __init__(self, policy: Policy | None = None) -> None: ... + def __init__(self, policy: Policy[Any] | None = None) -> None: ... def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ... def attach(self, payload: Self) -> None: ... # type: ignore[override] # The attachments are created via type(self) in the attach method. It's theoretically @@ -163,7 +165,7 @@ class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def add_attachment(self, *args: Any, content_manager: ContentManager | None = ..., **kw: Any) -> None: ... def clear(self) -> None: ... def clear_content(self) -> None: ... - def as_string(self, unixfrom: bool = False, maxheaderlen: int | None = None, policy: Policy | None = None) -> str: ... + def as_string(self, unixfrom: bool = False, maxheaderlen: int | None = None, policy: Policy[Any] | None = None) -> str: ... def is_attachment(self) -> bool: ... class EmailMessage(MIMEPart): ... diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi index fecb29d90b2e..a1a57b4eef4b 100644 --- a/mypy/typeshed/stdlib/email/parser.pyi +++ b/mypy/typeshed/stdlib/email/parser.pyi @@ -12,9 +12,9 @@ _MessageT = TypeVar("_MessageT", bound=Message, default=Message) class Parser(Generic[_MessageT]): @overload - def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ...) -> None: ... @overload - def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def parse(self, fp: SupportsRead[str], headersonly: bool = False) -> _MessageT: ... def parsestr(self, text: str, headersonly: bool = False) -> _MessageT: ... @@ -25,9 +25,11 @@ class HeaderParser(Parser[_MessageT]): class BytesParser(Generic[_MessageT]): parser: Parser[_MessageT] @overload - def __init__(self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + def __init__( + self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ... + ) -> None: ... @overload - def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def parse(self, fp: _WrappedBuffer, headersonly: bool = False) -> _MessageT: ... def parsebytes(self, text: bytes | bytearray, headersonly: bool = False) -> _MessageT: ... diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index 5f1cf934eb3c..5adf4a8c2390 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -1,16 +1,34 @@ from collections.abc import Callable -from email._policybase import Compat32 as Compat32, Policy as Policy, compat32 as compat32 +from email._policybase import Compat32 as Compat32, Policy as Policy, _MessageFactory, compat32 as compat32 from email.contentmanager import ContentManager -from email.message import Message -from typing import Any +from email.message import EmailMessage, Message +from typing import Any, TypeVar, overload __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] -class EmailPolicy(Policy): +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) + +class EmailPolicy(Policy[_MessageT]): utf8: bool refold_source: str header_factory: Callable[[str, Any], Any] content_manager: ContentManager + @overload + def __init__( + self: EmailPolicy[EmailMessage], + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: None = None, + utf8: bool = ..., + refold_source: str = ..., + header_factory: Callable[[str, str], str] = ..., + content_manager: ContentManager = ..., + ) -> None: ... + @overload def __init__( self, *, @@ -19,7 +37,7 @@ class EmailPolicy(Policy): cte_type: str = ..., raise_on_defect: bool = ..., mangle_from_: bool = ..., - message_factory: Callable[[Policy], Message] | None = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., utf8: bool = ..., refold_source: str = ..., header_factory: Callable[[str, str], str] = ..., @@ -31,8 +49,8 @@ class EmailPolicy(Policy): def fold(self, name: str, value: str) -> Any: ... def fold_binary(self, name: str, value: str) -> bytes: ... -default: EmailPolicy -SMTP: EmailPolicy -SMTPUTF8: EmailPolicy -HTTP: EmailPolicy -strict: EmailPolicy +default: EmailPolicy[EmailMessage] +SMTP: EmailPolicy[EmailMessage] +SMTPUTF8: EmailPolicy[EmailMessage] +HTTP: EmailPolicy[EmailMessage] +strict: EmailPolicy[EmailMessage] diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 5c82b07c4185..3b6c325522d7 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -316,13 +316,24 @@ else: __rand__ = __and__ __rxor__ = __xor__ -# subclassing IntFlag so it picks up all implemented base functions, best modeling behavior of enum.auto() -class auto(IntFlag): +class auto: _value_: Any @_magic_enum_attr def value(self) -> Any: ... def __new__(cls) -> Self: ... + # These don't exist, but auto is basically immediately replaced with + # either an int or a str depending on the type of the enum. StrEnum's auto + # shouldn't have these, but they're needed for int versions of auto (mostly the __or__). + # Ideally type checkers would special case auto enough to handle this, + # but until then this is a slightly inaccurate helping hand. + def __or__(self, other: int | Self) -> Self: ... + def __and__(self, other: int | Self) -> Self: ... + def __xor__(self, other: int | Self) -> Self: ... + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + if sys.version_info >= (3, 11): def pickle_by_global_name(self: Enum, proto: int) -> str: ... def pickle_by_enum_name(self: _EnumMemberT, proto: int) -> tuple[Callable[..., Any], tuple[type[_EnumMemberT], str]]: ... diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index 1e6aa78e2607..bf6daad0aea7 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Callable, Iterable from types import TracebackType -from typing import IO, Any, AnyStr, Literal, Protocol, overload +from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -107,7 +107,7 @@ def fileno() -> int: ... def isfirstline() -> bool: ... def isstdin() -> bool: ... -class FileInput(Iterator[AnyStr]): +class FileInput(Generic[AnyStr]): if sys.version_info >= (3, 10): # encoding and errors are added @overload diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index ac1372dd1e9c..eccfbdc235f4 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,12 +1,11 @@ from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable +from hashlib import _Hash as _HashlibHash from types import ModuleType -from typing import Any, AnyStr, overload +from typing import AnyStr, overload from typing_extensions import TypeAlias -# TODO more precise type for object of hashlib -_Hash: TypeAlias = Any -_DigestMod: TypeAlias = str | Callable[[], _Hash] | ModuleType +_DigestMod: TypeAlias = str | Callable[[], _HashlibHash] | ModuleType trans_5C: bytes trans_36: bytes diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index f68d9d0ca7d7..f47c744a6c7d 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -34,6 +34,7 @@ __all__ = [ _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | ReadableBuffer _T = TypeVar("_T") _MessageT = TypeVar("_MessageT", bound=email.message.Message) +_HeaderValue: TypeAlias = ReadableBuffer | str | int HTTP_PORT: int HTTPS_PORT: int @@ -167,7 +168,7 @@ class HTTPConnection: method: str, url: str, body: _DataType | str | None = None, - headers: Mapping[str, str] = {}, + headers: Mapping[str, _HeaderValue] = {}, *, encode_chunked: bool = False, ) -> None: ... @@ -180,7 +181,7 @@ class HTTPConnection: def connect(self) -> None: ... def close(self) -> None: ... def putrequest(self, method: str, url: str, skip_host: bool = False, skip_accept_encoding: bool = False) -> None: ... - def putheader(self, header: str | bytes, *argument: str | bytes) -> None: ... + def putheader(self, header: str | bytes, *values: _HeaderValue) -> None: ... def endheaders(self, message_body: _DataType | None = None, *, encode_chunked: bool = False) -> None: ... def send(self, data: _DataType | str) -> None: ... diff --git a/mypy/typeshed/stdlib/http/cookiejar.pyi b/mypy/typeshed/stdlib/http/cookiejar.pyi index 56097f163afd..31e1d3fc8378 100644 --- a/mypy/typeshed/stdlib/http/cookiejar.pyi +++ b/mypy/typeshed/stdlib/http/cookiejar.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import StrPath -from collections.abc import Iterable, Iterator, Sequence +from collections.abc import Iterator, Sequence from http.client import HTTPResponse from re import Pattern from typing import ClassVar, TypeVar, overload @@ -21,7 +21,7 @@ _T = TypeVar("_T") class LoadError(OSError): ... -class CookieJar(Iterable[Cookie]): +class CookieJar: non_word_re: ClassVar[Pattern[str]] # undocumented quote_re: ClassVar[Pattern[str]] # undocumented strict_domain_re: ClassVar[Pattern[str]] # undocumented diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 7607608696dd..5c26cb245a2f 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -1,13 +1,26 @@ import abc -import builtins -import codecs import sys -from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer -from collections.abc import Callable, Iterable, Iterator -from os import _Opener -from types import TracebackType -from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only -from typing_extensions import Self +from _io import ( + DEFAULT_BUFFER_SIZE as DEFAULT_BUFFER_SIZE, + BlockingIOError as BlockingIOError, + BufferedRandom as BufferedRandom, + BufferedReader as BufferedReader, + BufferedRWPair as BufferedRWPair, + BufferedWriter as BufferedWriter, + BytesIO as BytesIO, + FileIO as FileIO, + IncrementalNewlineDecoder as IncrementalNewlineDecoder, + StringIO as StringIO, + TextIOWrapper as TextIOWrapper, + _BufferedIOBase, + _IOBase, + _RawIOBase, + _TextIOBase, + _WrappedBuffer as _WrappedBuffer, # used elsewhere in typeshed + open as open, + open_code as open_code, +) +from typing import Final __all__ = [ "BlockingIOError", @@ -32,208 +45,16 @@ __all__ = [ ] if sys.version_info >= (3, 11): - __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] - -_T = TypeVar("_T") + from _io import text_encoding as text_encoding -DEFAULT_BUFFER_SIZE: Final = 8192 + __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] SEEK_SET: Final = 0 SEEK_CUR: Final = 1 SEEK_END: Final = 2 -open = builtins.open - -def open_code(path: str) -> IO[bytes]: ... - -BlockingIOError = builtins.BlockingIOError - class UnsupportedOperation(OSError, ValueError): ... - -class IOBase(metaclass=abc.ABCMeta): - def __iter__(self) -> Iterator[bytes]: ... - def __next__(self) -> bytes: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - def close(self) -> None: ... - def fileno(self) -> int: ... - def flush(self) -> None: ... - def isatty(self) -> bool: ... - def readable(self) -> bool: ... - read: Callable[..., Any] - def readlines(self, hint: int = -1, /) -> list[bytes]: ... - def seek(self, offset: int, whence: int = ..., /) -> int: ... - def seekable(self) -> bool: ... - def tell(self) -> int: ... - def truncate(self, size: int | None = ..., /) -> int: ... - def writable(self) -> bool: ... - write: Callable[..., Any] - def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... - def readline(self, size: int | None = -1, /) -> bytes: ... - def __del__(self) -> None: ... - @property - def closed(self) -> bool: ... - def _checkClosed(self) -> None: ... # undocumented - -class RawIOBase(IOBase): - def readall(self) -> bytes: ... - def readinto(self, buffer: WriteableBuffer, /) -> int | None: ... - def write(self, b: ReadableBuffer, /) -> int | None: ... - def read(self, size: int = -1, /) -> bytes | None: ... - -class BufferedIOBase(IOBase): - def detach(self) -> RawIOBase: ... - def readinto(self, buffer: WriteableBuffer, /) -> int: ... - def write(self, buffer: ReadableBuffer, /) -> int: ... - def readinto1(self, buffer: WriteableBuffer, /) -> int: ... - def read(self, size: int | None = ..., /) -> bytes: ... - def read1(self, size: int = ..., /) -> bytes: ... - -class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes - mode: str - # The type of "name" equals the argument passed in to the constructor, - # but that can make FileIO incompatible with other I/O types that assume - # "name" is a str. In the future, making FileIO generic might help. - name: Any - def __init__( - self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... - ) -> None: ... - @property - def closefd(self) -> bool: ... - def write(self, b: ReadableBuffer, /) -> int: ... - def read(self, size: int = -1, /) -> bytes: ... - def __enter__(self) -> Self: ... - -class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ... - # BytesIO does not contain a "name" field. This workaround is necessary - # to allow BytesIO sub-classes to add this field, as it is defined - # as a read-only property on IO[]. - name: Any - def __enter__(self) -> Self: ... - def getvalue(self) -> bytes: ... - def getbuffer(self) -> memoryview: ... - def read1(self, size: int | None = -1, /) -> bytes: ... - -class BufferedReader(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes - raw: RawIOBase - def __enter__(self) -> Self: ... - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, size: int = 0, /) -> bytes: ... - -class BufferedWriter(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes - raw: RawIOBase - def __enter__(self) -> Self: ... - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def write(self, buffer: ReadableBuffer, /) -> int: ... - -class BufferedRandom(BufferedReader, BufferedWriter): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __enter__(self) -> Self: ... - def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this - -class BufferedRWPair(BufferedIOBase): - def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, size: int = ..., /) -> bytes: ... - -class TextIOBase(IOBase): - encoding: str - errors: str | None - newlines: str | tuple[str, ...] | None - def __iter__(self) -> Iterator[str]: ... # type: ignore[override] - def __next__(self) -> str: ... # type: ignore[override] - def detach(self) -> BinaryIO: ... - def write(self, s: str, /) -> int: ... - def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] - def readline(self, size: int = ..., /) -> str: ... # type: ignore[override] - def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] - def read(self, size: int | None = ..., /) -> str: ... - -@type_check_only -class _WrappedBuffer(Protocol): - # "name" is wrapped by TextIOWrapper. Its type is inconsistent between - # the various I/O types, see the comments on TextIOWrapper.name and - # TextIO.name. - @property - def name(self) -> Any: ... - @property - def closed(self) -> bool: ... - def read(self, size: int = ..., /) -> ReadableBuffer: ... - # Optional: def read1(self, size: int, /) -> ReadableBuffer: ... - def write(self, b: bytes, /) -> object: ... - def flush(self) -> object: ... - def close(self) -> object: ... - def seekable(self) -> bool: ... - def readable(self) -> bool: ... - def writable(self) -> bool: ... - def truncate(self, size: int, /) -> int: ... - def fileno(self) -> int: ... - def isatty(self) -> bool: ... - # Optional: Only needs to be present if seekable() returns True. - # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... - # def tell(self) -> int: ... - -_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True) - -class TextIOWrapper(TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes - def __init__( - self, - buffer: _BufferT_co, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool = False, - write_through: bool = False, - ) -> None: ... - # Equals the "buffer" argument passed in to the constructor. - @property - def buffer(self) -> _BufferT_co: ... # type: ignore[override] - @property - def closed(self) -> bool: ... - @property - def line_buffering(self) -> bool: ... - @property - def write_through(self) -> bool: ... - def reconfigure( - self, - *, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool | None = None, - write_through: bool | None = None, - ) -> None: ... - # These are inherited from TextIOBase, but must exist in the stub to satisfy mypy. - def __enter__(self) -> Self: ... - def __iter__(self) -> Iterator[str]: ... # type: ignore[override] - def __next__(self) -> str: ... # type: ignore[override] - def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] - def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] - def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] - # Equals the "buffer" argument passed in to the constructor. - def detach(self) -> _BufferT_co: ... # type: ignore[override] - # TextIOWrapper's version of seek only supports a limited subset of - # operations. - def seek(self, cookie: int, whence: int = 0, /) -> int: ... - -class StringIO(TextIOWrapper): - def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... - # StringIO does not contain a "name" field. This workaround is necessary - # to allow StringIO sub-classes to add this field, as it is defined - # as a read-only property on IO[]. - name: Any - def getvalue(self) -> str: ... - -class IncrementalNewlineDecoder(codecs.IncrementalDecoder): - def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ... - def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... - @property - def newlines(self) -> str | tuple[str, ...] | None: ... - def setstate(self, state: tuple[bytes, int], /) -> None: ... - -if sys.version_info >= (3, 10): - @overload - def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ... - @overload - def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ... +class IOBase(_IOBase, metaclass=abc.ABCMeta): ... +class RawIOBase(_RawIOBase, IOBase): ... +class BufferedIOBase(_BufferedIOBase, IOBase): ... +class TextIOBase(_TextIOBase, IOBase): ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index f51ea87dcfcf..fa2faf8e6c13 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Iterable, Iterator -from typing import Any, Final, Generic, Literal, SupportsInt, TypeVar, overload +from typing import Any, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias # Undocumented length constants @@ -31,7 +31,7 @@ class _IPAddressBase: @property def version(self) -> int: ... -class _BaseAddress(_IPAddressBase, SupportsInt): +class _BaseAddress(_IPAddressBase): def __init__(self, address: object) -> None: ... def __add__(self, other: int) -> Self: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 83fe7461cb5c..af57f36715be 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -116,7 +116,7 @@ class BaseConfigurator: # undocumented def cfg_convert(self, value: str) -> Any: ... def convert(self, value: Any) -> Any: ... def configure_custom(self, config: dict[str, Any]) -> Any: ... - def as_tuple(self, value: list[Any] | tuple[Any]) -> tuple[Any]: ... + def as_tuple(self, value: list[Any] | tuple[Any, ...]) -> tuple[Any, ...]: ... class DictConfigurator(BaseConfigurator): def configure(self) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index a0c150d6e7e8..5be7f7b76ba1 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import ReadableBuffer, Unused -from collections.abc import Iterable, Iterator, Sized +from collections.abc import Iterator from typing import Final, Literal, NoReturn, overload from typing_extensions import Self @@ -30,7 +30,7 @@ if sys.platform != "win32": PAGESIZE: int -class mmap(Iterable[int], Sized): +class mmap: if sys.platform == "win32": def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ... else: diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 7045a81b85be..9998239d3119 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -1,9 +1,9 @@ import socket import sys -import types -from _typeshed import ReadableBuffer +from _typeshed import Incomplete, ReadableBuffer from collections.abc import Iterable -from typing import Any, SupportsIndex +from types import TracebackType +from typing import Any, Generic, SupportsIndex, TypeVar from typing_extensions import Self, TypeAlias __all__ = ["Client", "Listener", "Pipe", "wait"] @@ -11,7 +11,11 @@ __all__ = ["Client", "Listener", "Pipe", "wait"] # https://docs.python.org/3/library/multiprocessing.html#address-formats _Address: TypeAlias = str | tuple[str, int] -class _ConnectionBase: +# Defaulting to Any to avoid forcing generics on a lot of pre-existing code +_SendT = TypeVar("_SendT", contravariant=True, default=Any) +_RecvT = TypeVar("_RecvT", covariant=True, default=Any) + +class _ConnectionBase(Generic[_SendT, _RecvT]): def __init__(self, handle: SupportsIndex, readable: bool = True, writable: bool = True) -> None: ... @property def closed(self) -> bool: ... # undocumented @@ -22,27 +26,27 @@ class _ConnectionBase: def fileno(self) -> int: ... def close(self) -> None: ... def send_bytes(self, buf: ReadableBuffer, offset: int = 0, size: int | None = None) -> None: ... - def send(self, obj: Any) -> None: ... + def send(self, obj: _SendT) -> None: ... def recv_bytes(self, maxlength: int | None = None) -> bytes: ... def recv_bytes_into(self, buf: Any, offset: int = 0) -> int: ... - def recv(self) -> Any: ... + def recv(self) -> _RecvT: ... def poll(self, timeout: float | None = 0.0) -> bool: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None ) -> None: ... def __del__(self) -> None: ... -class Connection(_ConnectionBase): ... +class Connection(_ConnectionBase[_SendT, _RecvT]): ... if sys.platform == "win32": - class PipeConnection(_ConnectionBase): ... + class PipeConnection(_ConnectionBase[_SendT, _RecvT]): ... class Listener: def __init__( self, address: _Address | None = None, family: str | None = None, backlog: int = 1, authkey: bytes | None = None ) -> None: ... - def accept(self) -> Connection: ... + def accept(self) -> Connection[Incomplete, Incomplete]: ... def close(self) -> None: ... @property def address(self) -> _Address: ... @@ -50,26 +54,30 @@ class Listener: def last_accepted(self) -> _Address | None: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None ) -> None: ... +# Any: send and recv methods unused if sys.version_info >= (3, 12): - def deliver_challenge(connection: Connection, authkey: bytes, digest_name: str = "sha256") -> None: ... + def deliver_challenge(connection: Connection[Any, Any], authkey: bytes, digest_name: str = "sha256") -> None: ... else: - def deliver_challenge(connection: Connection, authkey: bytes) -> None: ... + def deliver_challenge(connection: Connection[Any, Any], authkey: bytes) -> None: ... -def answer_challenge(connection: Connection, authkey: bytes) -> None: ... +def answer_challenge(connection: Connection[Any, Any], authkey: bytes) -> None: ... def wait( - object_list: Iterable[Connection | socket.socket | int], timeout: float | None = None -) -> list[Connection | socket.socket | int]: ... -def Client(address: _Address, family: str | None = None, authkey: bytes | None = None) -> Connection: ... + object_list: Iterable[Connection[_SendT, _RecvT] | socket.socket | int], timeout: float | None = None +) -> list[Connection[_SendT, _RecvT] | socket.socket | int]: ... +def Client(address: _Address, family: str | None = None, authkey: bytes | None = None) -> Connection[Any, Any]: ... # N.B. Keep this in sync with multiprocessing.context.BaseContext.Pipe. # _ConnectionBase is the common base class of Connection and PipeConnection # and can be used in cross-platform code. +# +# The two connections should have the same generic types but inverted (Connection[_T1, _T2], Connection[_T2, _T1]). +# However, TypeVars scoped entirely within a return annotation is unspecified in the spec. if sys.platform != "win32": - def Pipe(duplex: bool = True) -> tuple[Connection, Connection]: ... + def Pipe(duplex: bool = True) -> tuple[Connection[Any, Any], Connection[Any, Any]]: ... else: - def Pipe(duplex: bool = True) -> tuple[PipeConnection, PipeConnection]: ... + def Pipe(duplex: bool = True) -> tuple[PipeConnection[Any, Any], PipeConnection[Any, Any]]: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 605be4686c1f..9900f009a398 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -47,10 +47,13 @@ class BaseContext: # N.B. Keep this in sync with multiprocessing.connection.Pipe. # _ConnectionBase is the common base class of Connection and PipeConnection # and can be used in cross-platform code. + # + # The two connections should have the same generic types but inverted (Connection[_T1, _T2], Connection[_T2, _T1]). + # However, TypeVars scoped entirely within a return annotation is unspecified in the spec. if sys.platform != "win32": - def Pipe(self, duplex: bool = True) -> tuple[Connection, Connection]: ... + def Pipe(self, duplex: bool = True) -> tuple[Connection[Any, Any], Connection[Any, Any]]: ... else: - def Pipe(self, duplex: bool = True) -> tuple[PipeConnection, PipeConnection]: ... + def Pipe(self, duplex: bool = True) -> tuple[PipeConnection[Any, Any], PipeConnection[Any, Any]]: ... def Barrier( self, parties: int, action: Callable[..., object] | None = None, timeout: float | None = None diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 5d5b9cdcb913..c5a1134377a1 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -1,7 +1,7 @@ import queue import sys import threading -from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT +from _typeshed import Incomplete, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from types import TracebackType from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload @@ -129,7 +129,9 @@ class Server: self, registry: dict[str, tuple[Callable[..., Any], Any, Any, Any]], address: Any, authkey: bytes, serializer: str ) -> None: ... def serve_forever(self) -> None: ... - def accept_connection(self, c: Connection, name: str) -> None: ... + def accept_connection( + self, c: Connection[tuple[str, str | None], tuple[str, str, Iterable[Incomplete], Mapping[str, Incomplete]]], name: str + ) -> None: ... class BaseManager: if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 950ed1d8c56b..61d6d0781213 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,5 +1,5 @@ import sys -from collections.abc import Callable, Iterable, Iterator, Mapping +from collections.abc import Callable, Iterable, Mapping from types import TracebackType from typing import Any, Final, Generic, TypeVar from typing_extensions import Self @@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): error_callback: Callable[[BaseException], object] | None, ) -> None: ... -class IMapIterator(Iterator[_T]): +class IMapIterator(Generic[_T]): def __init__(self, pool: Pool) -> None: ... def __iter__(self) -> Self: ... def next(self, timeout: float | None = None) -> _T: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index a31987bcc3cb..473e90936d71 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -35,8 +35,8 @@ if sys.platform == "win32": handle: int, target_process: int | None = None, inheritable: bool = False, *, source_process: int | None = None ) -> int: ... def steal_handle(source_pid: int, handle: int) -> int: ... - def send_handle(conn: connection.PipeConnection, handle: int, destination_pid: int) -> None: ... - def recv_handle(conn: connection.PipeConnection) -> int: ... + def send_handle(conn: connection.PipeConnection[DupHandle, Any], handle: int, destination_pid: int) -> None: ... + def recv_handle(conn: connection.PipeConnection[Any, DupHandle]) -> int: ... class DupHandle: def __init__(self, handle: int, access: int, pid: int | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/operator.pyi b/mypy/typeshed/stdlib/operator.pyi index 1a817f00f3c1..b73e037f3ed9 100644 --- a/mypy/typeshed/stdlib/operator.pyi +++ b/mypy/typeshed/stdlib/operator.pyi @@ -1,5 +1,66 @@ import sys -from _operator import * +from _operator import ( + abs as abs, + add as add, + and_ as and_, + concat as concat, + contains as contains, + countOf as countOf, + delitem as delitem, + eq as eq, + floordiv as floordiv, + ge as ge, + getitem as getitem, + gt as gt, + iadd as iadd, + iand as iand, + iconcat as iconcat, + ifloordiv as ifloordiv, + ilshift as ilshift, + imatmul as imatmul, + imod as imod, + imul as imul, + index as index, + indexOf as indexOf, + inv as inv, + invert as invert, + ior as ior, + ipow as ipow, + irshift as irshift, + is_ as is_, + is_not as is_not, + isub as isub, + itruediv as itruediv, + ixor as ixor, + le as le, + length_hint as length_hint, + lshift as lshift, + lt as lt, + matmul as matmul, + mod as mod, + mul as mul, + ne as ne, + neg as neg, + not_ as not_, + or_ as or_, + pos as pos, + pow as pow, + rshift as rshift, + setitem as setitem, + sub as sub, + truediv as truediv, + truth as truth, + xor as xor, +) +from _typeshed import SupportsGetItem +from typing import Any, Generic, TypeVar, final, overload +from typing_extensions import TypeVarTuple, Unpack + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_Ts = TypeVarTuple("_Ts") __all__ = [ "abs", @@ -59,9 +120,13 @@ __all__ = [ ] if sys.version_info >= (3, 11): + from _operator import call as call + __all__ += ["call"] if sys.version_info >= (3, 14): + from _operator import is_none as is_none, is_not_none as is_not_none + __all__ += ["is_none", "is_not_none"] __lt__ = lt @@ -111,3 +176,40 @@ __itruediv__ = itruediv __ixor__ = ixor if sys.version_info >= (3, 11): __call__ = call + +# At runtime, these classes are implemented in C as part of the _operator module +# However, they consider themselves to live in the operator module, so we'll put +# them here. +@final +class attrgetter(Generic[_T_co]): + @overload + def __new__(cls, attr: str, /) -> attrgetter[Any]: ... + @overload + def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ... + @overload + def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ... + @overload + def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ... + @overload + def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... + def __call__(self, obj: Any, /) -> _T_co: ... + +@final +class itemgetter(Generic[_T_co]): + @overload + def __new__(cls, item: _T, /) -> itemgetter[_T]: ... + @overload + def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... + # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: + # TypeVar "_KT_contra@SupportsGetItem" is contravariant + # "tuple[int, int]" is incompatible with protocol "SupportsIndex" + # preventing [_T_co, ...] instead of [Any, ...] + # + # A suspected mypy issue prevents using [..., _T] instead of [..., Any] here. + # https://github.com/python/mypy/issues/14032 + def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ... + +@final +class methodcaller: + def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... + def __call__(self, obj: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index d7bb4883a0f2..833f9b1e56cb 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -23,8 +23,9 @@ from abc import abstractmethod from builtins import OSError from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, Sequence from contextlib import AbstractContextManager -from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper as _TextIOWrapper +from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from subprocess import Popen +from types import TracebackType from typing import ( IO, Any, @@ -578,7 +579,7 @@ def fdopen( newline: str | None = ..., closefd: bool = ..., opener: _Opener | None = ..., -) -> _TextIOWrapper: ... +) -> TextIOWrapper: ... @overload def fdopen( fd: int, @@ -917,9 +918,25 @@ if sys.platform != "win32": if sys.platform != "darwin" and sys.platform != "linux": def plock(op: int, /) -> None: ... -class _wrap_close(_TextIOWrapper): - def __init__(self, stream: _TextIOWrapper, proc: Popen[str]) -> None: ... - def close(self) -> int | None: ... # type: ignore[override] +class _wrap_close: + def __init__(self, stream: TextIOWrapper, proc: Popen[str]) -> None: ... + def close(self) -> int | None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + def __iter__(self) -> Iterator[str]: ... + # Methods below here don't exist directly on the _wrap_close object, but + # are copied from the wrapped TextIOWrapper object via __getattr__. + # The full set of TextIOWrapper methods are technically available this way, + # but undocumented. Only a subset are currently included here. + def read(self, size: int | None = -1, /) -> str: ... + def readable(self) -> bool: ... + def readline(self, size: int = -1, /) -> str: ... + def readlines(self, hint: int = -1, /) -> list[str]: ... + def writable(self) -> bool: ... + def write(self, s: str, /) -> int: ... + def writelines(self, lines: Iterable[str], /) -> None: ... def popen(cmd: str, mode: str = "r", buffering: int = -1) -> _wrap_close: ... def spawnl(mode: int, file: StrOrBytesPath, arg0: StrOrBytesPath, *args: StrOrBytesPath) -> int: ... diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 98ec80b0f14e..9bea92ef1c9e 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -157,10 +157,10 @@ class Pickler: def __init__( self, file: SupportsWrite[bytes], - protocol: int | None = ..., + protocol: int | None = None, *, - fix_imports: bool = ..., - buffer_callback: _BufferCallback = ..., + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, ) -> None: ... def reducer_override(self, obj: Any) -> Any: ... def dump(self, obj: Any, /) -> None: ... @@ -174,10 +174,10 @@ class Unpickler: self, file: _ReadableFileobj, *, - fix_imports: bool = ..., - encoding: str = ..., - errors: str = ..., - buffers: Iterable[Any] | None = ..., + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), ) -> None: ... def load(self) -> Any: ... def find_class(self, module_name: str, global_name: str, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index dc0156ef13bd..d38259a20d72 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -3,17 +3,13 @@ from collections.abc import Callable from pyexpat import errors as errors, model as model from typing import Any, Final, final from typing_extensions import TypeAlias +from xml.parsers.expat import ExpatError as ExpatError EXPAT_VERSION: Final[str] # undocumented version_info: tuple[int, int, int] # undocumented native_encoding: str # undocumented features: list[tuple[str, int]] # undocumented -class ExpatError(Exception): - code: int - lineno: int - offset: int - error = ExpatError XML_PARAM_ENTITY_PARSING_NEVER: Final = 0 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: Final = 1 diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 76f98dd9f2a2..ac297b7d4567 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -4,7 +4,6 @@ import sre_constants import sys from _typeshed import ReadableBuffer from collections.abc import Callable, Iterator, Mapping -from sre_constants import error as error from typing import Any, AnyStr, Generic, Literal, TypeVar, final, overload from typing_extensions import TypeAlias @@ -54,6 +53,16 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") +# The implementation defines this in re._constants (version_info >= 3, 11) or +# sre_constants. Typeshed has it here because its __module__ attribute is set to "re". +class error(Exception): + msg: str + pattern: str | bytes | None + pos: int | None + lineno: int + colno: int + def __init__(self, msg: str, pattern: str | bytes | None = None, pos: int | None = None) -> None: ... + @final class Match(Generic[AnyStr]): @property diff --git a/mypy/typeshed/stdlib/shlex.pyi b/mypy/typeshed/stdlib/shlex.pyi index daa8df439b26..1c27483782fb 100644 --- a/mypy/typeshed/stdlib/shlex.pyi +++ b/mypy/typeshed/stdlib/shlex.pyi @@ -27,7 +27,7 @@ def join(split_command: Iterable[str]) -> str: ... def quote(s: str) -> str: ... # TODO: Make generic over infile once PEP 696 is implemented. -class shlex(Iterable[str]): +class shlex: commenters: str wordchars: str whitespace: str diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index b626409d2dde..39a93ce4d0f3 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -109,8 +109,6 @@ from _socket import ( _RetAddress as _RetAddress, close as close, dup as dup, - error as error, - gaierror as gaierror, getdefaulttimeout as getdefaulttimeout, gethostbyaddr as gethostbyaddr, gethostbyname as gethostbyname, @@ -121,7 +119,6 @@ from _socket import ( getservbyname as getservbyname, getservbyport as getservbyport, has_ipv6 as has_ipv6, - herror as herror, htonl as htonl, htons as htons, if_indextoname as if_indextoname, @@ -134,7 +131,6 @@ from _socket import ( ntohl as ntohl, ntohs as ntohs, setdefaulttimeout as setdefaulttimeout, - timeout as timeout, ) from _typeshed import ReadableBuffer, Unused, WriteableBuffer from collections.abc import Iterable @@ -486,6 +482,18 @@ EBADF: int EAGAIN: int EWOULDBLOCK: int +# These errors are implemented in _socket at runtime +# but they consider themselves to live in socket so we'll put them here. +error = OSError + +class herror(error): ... +class gaierror(error): ... + +if sys.version_info >= (3, 10): + timeout = TimeoutError +else: + class timeout(error): ... + class AddressFamily(IntEnum): AF_INET = 2 AF_INET6 = 10 diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index d747be90fd0a..d6087aa755c1 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -1 +1,465 @@ -from sqlite3.dbapi2 import * +import sys +from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused +from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence +from sqlite3.dbapi2 import ( + PARSE_COLNAMES as PARSE_COLNAMES, + PARSE_DECLTYPES as PARSE_DECLTYPES, + SQLITE_ALTER_TABLE as SQLITE_ALTER_TABLE, + SQLITE_ANALYZE as SQLITE_ANALYZE, + SQLITE_ATTACH as SQLITE_ATTACH, + SQLITE_CREATE_INDEX as SQLITE_CREATE_INDEX, + SQLITE_CREATE_TABLE as SQLITE_CREATE_TABLE, + SQLITE_CREATE_TEMP_INDEX as SQLITE_CREATE_TEMP_INDEX, + SQLITE_CREATE_TEMP_TABLE as SQLITE_CREATE_TEMP_TABLE, + SQLITE_CREATE_TEMP_TRIGGER as SQLITE_CREATE_TEMP_TRIGGER, + SQLITE_CREATE_TEMP_VIEW as SQLITE_CREATE_TEMP_VIEW, + SQLITE_CREATE_TRIGGER as SQLITE_CREATE_TRIGGER, + SQLITE_CREATE_VIEW as SQLITE_CREATE_VIEW, + SQLITE_CREATE_VTABLE as SQLITE_CREATE_VTABLE, + SQLITE_DELETE as SQLITE_DELETE, + SQLITE_DENY as SQLITE_DENY, + SQLITE_DETACH as SQLITE_DETACH, + SQLITE_DONE as SQLITE_DONE, + SQLITE_DROP_INDEX as SQLITE_DROP_INDEX, + SQLITE_DROP_TABLE as SQLITE_DROP_TABLE, + SQLITE_DROP_TEMP_INDEX as SQLITE_DROP_TEMP_INDEX, + SQLITE_DROP_TEMP_TABLE as SQLITE_DROP_TEMP_TABLE, + SQLITE_DROP_TEMP_TRIGGER as SQLITE_DROP_TEMP_TRIGGER, + SQLITE_DROP_TEMP_VIEW as SQLITE_DROP_TEMP_VIEW, + SQLITE_DROP_TRIGGER as SQLITE_DROP_TRIGGER, + SQLITE_DROP_VIEW as SQLITE_DROP_VIEW, + SQLITE_DROP_VTABLE as SQLITE_DROP_VTABLE, + SQLITE_FUNCTION as SQLITE_FUNCTION, + SQLITE_IGNORE as SQLITE_IGNORE, + SQLITE_INSERT as SQLITE_INSERT, + SQLITE_OK as SQLITE_OK, + SQLITE_PRAGMA as SQLITE_PRAGMA, + SQLITE_READ as SQLITE_READ, + SQLITE_RECURSIVE as SQLITE_RECURSIVE, + SQLITE_REINDEX as SQLITE_REINDEX, + SQLITE_SAVEPOINT as SQLITE_SAVEPOINT, + SQLITE_SELECT as SQLITE_SELECT, + SQLITE_TRANSACTION as SQLITE_TRANSACTION, + SQLITE_UPDATE as SQLITE_UPDATE, + Binary as Binary, + Date as Date, + DateFromTicks as DateFromTicks, + Time as Time, + TimeFromTicks as TimeFromTicks, + TimestampFromTicks as TimestampFromTicks, + adapt as adapt, + adapters as adapters, + apilevel as apilevel, + complete_statement as complete_statement, + connect as connect, + converters as converters, + enable_callback_tracebacks as enable_callback_tracebacks, + paramstyle as paramstyle, + register_adapter as register_adapter, + register_converter as register_converter, + sqlite_version as sqlite_version, + sqlite_version_info as sqlite_version_info, + threadsafety as threadsafety, + version_info as version_info, +) +from types import TracebackType +from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload +from typing_extensions import Self, TypeAlias + +if sys.version_info >= (3, 12): + from sqlite3.dbapi2 import ( + LEGACY_TRANSACTION_CONTROL as LEGACY_TRANSACTION_CONTROL, + SQLITE_DBCONFIG_DEFENSIVE as SQLITE_DBCONFIG_DEFENSIVE, + SQLITE_DBCONFIG_DQS_DDL as SQLITE_DBCONFIG_DQS_DDL, + SQLITE_DBCONFIG_DQS_DML as SQLITE_DBCONFIG_DQS_DML, + SQLITE_DBCONFIG_ENABLE_FKEY as SQLITE_DBCONFIG_ENABLE_FKEY, + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER as SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION as SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + SQLITE_DBCONFIG_ENABLE_QPSG as SQLITE_DBCONFIG_ENABLE_QPSG, + SQLITE_DBCONFIG_ENABLE_TRIGGER as SQLITE_DBCONFIG_ENABLE_TRIGGER, + SQLITE_DBCONFIG_ENABLE_VIEW as SQLITE_DBCONFIG_ENABLE_VIEW, + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE as SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT as SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE as SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, + SQLITE_DBCONFIG_RESET_DATABASE as SQLITE_DBCONFIG_RESET_DATABASE, + SQLITE_DBCONFIG_TRIGGER_EQP as SQLITE_DBCONFIG_TRIGGER_EQP, + SQLITE_DBCONFIG_TRUSTED_SCHEMA as SQLITE_DBCONFIG_TRUSTED_SCHEMA, + SQLITE_DBCONFIG_WRITABLE_SCHEMA as SQLITE_DBCONFIG_WRITABLE_SCHEMA, + ) + +if sys.version_info >= (3, 11): + from sqlite3.dbapi2 import ( + SQLITE_ABORT as SQLITE_ABORT, + SQLITE_ABORT_ROLLBACK as SQLITE_ABORT_ROLLBACK, + SQLITE_AUTH as SQLITE_AUTH, + SQLITE_AUTH_USER as SQLITE_AUTH_USER, + SQLITE_BUSY as SQLITE_BUSY, + SQLITE_BUSY_RECOVERY as SQLITE_BUSY_RECOVERY, + SQLITE_BUSY_SNAPSHOT as SQLITE_BUSY_SNAPSHOT, + SQLITE_BUSY_TIMEOUT as SQLITE_BUSY_TIMEOUT, + SQLITE_CANTOPEN as SQLITE_CANTOPEN, + SQLITE_CANTOPEN_CONVPATH as SQLITE_CANTOPEN_CONVPATH, + SQLITE_CANTOPEN_DIRTYWAL as SQLITE_CANTOPEN_DIRTYWAL, + SQLITE_CANTOPEN_FULLPATH as SQLITE_CANTOPEN_FULLPATH, + SQLITE_CANTOPEN_ISDIR as SQLITE_CANTOPEN_ISDIR, + SQLITE_CANTOPEN_NOTEMPDIR as SQLITE_CANTOPEN_NOTEMPDIR, + SQLITE_CANTOPEN_SYMLINK as SQLITE_CANTOPEN_SYMLINK, + SQLITE_CONSTRAINT as SQLITE_CONSTRAINT, + SQLITE_CONSTRAINT_CHECK as SQLITE_CONSTRAINT_CHECK, + SQLITE_CONSTRAINT_COMMITHOOK as SQLITE_CONSTRAINT_COMMITHOOK, + SQLITE_CONSTRAINT_FOREIGNKEY as SQLITE_CONSTRAINT_FOREIGNKEY, + SQLITE_CONSTRAINT_FUNCTION as SQLITE_CONSTRAINT_FUNCTION, + SQLITE_CONSTRAINT_NOTNULL as SQLITE_CONSTRAINT_NOTNULL, + SQLITE_CONSTRAINT_PINNED as SQLITE_CONSTRAINT_PINNED, + SQLITE_CONSTRAINT_PRIMARYKEY as SQLITE_CONSTRAINT_PRIMARYKEY, + SQLITE_CONSTRAINT_ROWID as SQLITE_CONSTRAINT_ROWID, + SQLITE_CONSTRAINT_TRIGGER as SQLITE_CONSTRAINT_TRIGGER, + SQLITE_CONSTRAINT_UNIQUE as SQLITE_CONSTRAINT_UNIQUE, + SQLITE_CONSTRAINT_VTAB as SQLITE_CONSTRAINT_VTAB, + SQLITE_CORRUPT as SQLITE_CORRUPT, + SQLITE_CORRUPT_INDEX as SQLITE_CORRUPT_INDEX, + SQLITE_CORRUPT_SEQUENCE as SQLITE_CORRUPT_SEQUENCE, + SQLITE_CORRUPT_VTAB as SQLITE_CORRUPT_VTAB, + SQLITE_EMPTY as SQLITE_EMPTY, + SQLITE_ERROR as SQLITE_ERROR, + SQLITE_ERROR_MISSING_COLLSEQ as SQLITE_ERROR_MISSING_COLLSEQ, + SQLITE_ERROR_RETRY as SQLITE_ERROR_RETRY, + SQLITE_ERROR_SNAPSHOT as SQLITE_ERROR_SNAPSHOT, + SQLITE_FORMAT as SQLITE_FORMAT, + SQLITE_FULL as SQLITE_FULL, + SQLITE_INTERNAL as SQLITE_INTERNAL, + SQLITE_INTERRUPT as SQLITE_INTERRUPT, + SQLITE_IOERR as SQLITE_IOERR, + SQLITE_IOERR_ACCESS as SQLITE_IOERR_ACCESS, + SQLITE_IOERR_AUTH as SQLITE_IOERR_AUTH, + SQLITE_IOERR_BEGIN_ATOMIC as SQLITE_IOERR_BEGIN_ATOMIC, + SQLITE_IOERR_BLOCKED as SQLITE_IOERR_BLOCKED, + SQLITE_IOERR_CHECKRESERVEDLOCK as SQLITE_IOERR_CHECKRESERVEDLOCK, + SQLITE_IOERR_CLOSE as SQLITE_IOERR_CLOSE, + SQLITE_IOERR_COMMIT_ATOMIC as SQLITE_IOERR_COMMIT_ATOMIC, + SQLITE_IOERR_CONVPATH as SQLITE_IOERR_CONVPATH, + SQLITE_IOERR_CORRUPTFS as SQLITE_IOERR_CORRUPTFS, + SQLITE_IOERR_DATA as SQLITE_IOERR_DATA, + SQLITE_IOERR_DELETE as SQLITE_IOERR_DELETE, + SQLITE_IOERR_DELETE_NOENT as SQLITE_IOERR_DELETE_NOENT, + SQLITE_IOERR_DIR_CLOSE as SQLITE_IOERR_DIR_CLOSE, + SQLITE_IOERR_DIR_FSYNC as SQLITE_IOERR_DIR_FSYNC, + SQLITE_IOERR_FSTAT as SQLITE_IOERR_FSTAT, + SQLITE_IOERR_FSYNC as SQLITE_IOERR_FSYNC, + SQLITE_IOERR_GETTEMPPATH as SQLITE_IOERR_GETTEMPPATH, + SQLITE_IOERR_LOCK as SQLITE_IOERR_LOCK, + SQLITE_IOERR_MMAP as SQLITE_IOERR_MMAP, + SQLITE_IOERR_NOMEM as SQLITE_IOERR_NOMEM, + SQLITE_IOERR_RDLOCK as SQLITE_IOERR_RDLOCK, + SQLITE_IOERR_READ as SQLITE_IOERR_READ, + SQLITE_IOERR_ROLLBACK_ATOMIC as SQLITE_IOERR_ROLLBACK_ATOMIC, + SQLITE_IOERR_SEEK as SQLITE_IOERR_SEEK, + SQLITE_IOERR_SHMLOCK as SQLITE_IOERR_SHMLOCK, + SQLITE_IOERR_SHMMAP as SQLITE_IOERR_SHMMAP, + SQLITE_IOERR_SHMOPEN as SQLITE_IOERR_SHMOPEN, + SQLITE_IOERR_SHMSIZE as SQLITE_IOERR_SHMSIZE, + SQLITE_IOERR_SHORT_READ as SQLITE_IOERR_SHORT_READ, + SQLITE_IOERR_TRUNCATE as SQLITE_IOERR_TRUNCATE, + SQLITE_IOERR_UNLOCK as SQLITE_IOERR_UNLOCK, + SQLITE_IOERR_VNODE as SQLITE_IOERR_VNODE, + SQLITE_IOERR_WRITE as SQLITE_IOERR_WRITE, + SQLITE_LIMIT_ATTACHED as SQLITE_LIMIT_ATTACHED, + SQLITE_LIMIT_COLUMN as SQLITE_LIMIT_COLUMN, + SQLITE_LIMIT_COMPOUND_SELECT as SQLITE_LIMIT_COMPOUND_SELECT, + SQLITE_LIMIT_EXPR_DEPTH as SQLITE_LIMIT_EXPR_DEPTH, + SQLITE_LIMIT_FUNCTION_ARG as SQLITE_LIMIT_FUNCTION_ARG, + SQLITE_LIMIT_LENGTH as SQLITE_LIMIT_LENGTH, + SQLITE_LIMIT_LIKE_PATTERN_LENGTH as SQLITE_LIMIT_LIKE_PATTERN_LENGTH, + SQLITE_LIMIT_SQL_LENGTH as SQLITE_LIMIT_SQL_LENGTH, + SQLITE_LIMIT_TRIGGER_DEPTH as SQLITE_LIMIT_TRIGGER_DEPTH, + SQLITE_LIMIT_VARIABLE_NUMBER as SQLITE_LIMIT_VARIABLE_NUMBER, + SQLITE_LIMIT_VDBE_OP as SQLITE_LIMIT_VDBE_OP, + SQLITE_LIMIT_WORKER_THREADS as SQLITE_LIMIT_WORKER_THREADS, + SQLITE_LOCKED as SQLITE_LOCKED, + SQLITE_LOCKED_SHAREDCACHE as SQLITE_LOCKED_SHAREDCACHE, + SQLITE_LOCKED_VTAB as SQLITE_LOCKED_VTAB, + SQLITE_MISMATCH as SQLITE_MISMATCH, + SQLITE_MISUSE as SQLITE_MISUSE, + SQLITE_NOLFS as SQLITE_NOLFS, + SQLITE_NOMEM as SQLITE_NOMEM, + SQLITE_NOTADB as SQLITE_NOTADB, + SQLITE_NOTFOUND as SQLITE_NOTFOUND, + SQLITE_NOTICE as SQLITE_NOTICE, + SQLITE_NOTICE_RECOVER_ROLLBACK as SQLITE_NOTICE_RECOVER_ROLLBACK, + SQLITE_NOTICE_RECOVER_WAL as SQLITE_NOTICE_RECOVER_WAL, + SQLITE_OK_LOAD_PERMANENTLY as SQLITE_OK_LOAD_PERMANENTLY, + SQLITE_OK_SYMLINK as SQLITE_OK_SYMLINK, + SQLITE_PERM as SQLITE_PERM, + SQLITE_PROTOCOL as SQLITE_PROTOCOL, + SQLITE_RANGE as SQLITE_RANGE, + SQLITE_READONLY as SQLITE_READONLY, + SQLITE_READONLY_CANTINIT as SQLITE_READONLY_CANTINIT, + SQLITE_READONLY_CANTLOCK as SQLITE_READONLY_CANTLOCK, + SQLITE_READONLY_DBMOVED as SQLITE_READONLY_DBMOVED, + SQLITE_READONLY_DIRECTORY as SQLITE_READONLY_DIRECTORY, + SQLITE_READONLY_RECOVERY as SQLITE_READONLY_RECOVERY, + SQLITE_READONLY_ROLLBACK as SQLITE_READONLY_ROLLBACK, + SQLITE_ROW as SQLITE_ROW, + SQLITE_SCHEMA as SQLITE_SCHEMA, + SQLITE_TOOBIG as SQLITE_TOOBIG, + SQLITE_WARNING as SQLITE_WARNING, + SQLITE_WARNING_AUTOINDEX as SQLITE_WARNING_AUTOINDEX, + ) + +if sys.version_info < (3, 12): + from sqlite3.dbapi2 import enable_shared_cache as enable_shared_cache, version as version + +if sys.version_info < (3, 10): + from sqlite3.dbapi2 import OptimizedUnicode as OptimizedUnicode + +_CursorT = TypeVar("_CursorT", bound=Cursor) +_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None +# Data that is passed through adapters can be of any type accepted by an adapter. +_AdaptedInputData: TypeAlias = _SqliteData | Any +# The Mapping must really be a dict, but making it invariant is too annoying. +_Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData] + +class _AnyParamWindowAggregateClass(Protocol): + def step(self, *args: Any) -> object: ... + def inverse(self, *args: Any) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _WindowAggregateClass(Protocol): + step: Callable[..., object] + inverse: Callable[..., object] + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _AggregateProtocol(Protocol): + def step(self, value: int, /) -> object: ... + def finalize(self) -> int: ... + +class _SingleParamWindowAggregateClass(Protocol): + def step(self, param: Any, /) -> object: ... + def inverse(self, param: Any, /) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +# These classes are implemented in the C module _sqlite3. At runtime, they're imported +# from there into sqlite3.dbapi2 and from that module to here. However, they +# consider themselves to live in the sqlite3.* namespace, so we'll define them here. + +class Error(Exception): + if sys.version_info >= (3, 11): + sqlite_errorcode: int + sqlite_errorname: str + +class DatabaseError(Error): ... +class DataError(DatabaseError): ... +class IntegrityError(DatabaseError): ... +class InterfaceError(Error): ... +class InternalError(DatabaseError): ... +class NotSupportedError(DatabaseError): ... +class OperationalError(DatabaseError): ... +class ProgrammingError(DatabaseError): ... +class Warning(Exception): ... + +class Connection: + @property + def DataError(self) -> type[DataError]: ... + @property + def DatabaseError(self) -> type[DatabaseError]: ... + @property + def Error(self) -> type[Error]: ... + @property + def IntegrityError(self) -> type[IntegrityError]: ... + @property + def InterfaceError(self) -> type[InterfaceError]: ... + @property + def InternalError(self) -> type[InternalError]: ... + @property + def NotSupportedError(self) -> type[NotSupportedError]: ... + @property + def OperationalError(self) -> type[OperationalError]: ... + @property + def ProgrammingError(self) -> type[ProgrammingError]: ... + @property + def Warning(self) -> type[Warning]: ... + @property + def in_transaction(self) -> bool: ... + isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' + @property + def total_changes(self) -> int: ... + if sys.version_info >= (3, 12): + @property + def autocommit(self) -> int: ... + @autocommit.setter + def autocommit(self, val: int) -> None: ... + row_factory: Any + text_factory: Any + if sys.version_info >= (3, 12): + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + autocommit: bool = ..., + ) -> None: ... + else: + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + ) -> None: ... + + def close(self) -> None: ... + if sys.version_info >= (3, 11): + def blobopen(self, table: str, column: str, row: int, /, *, readonly: bool = False, name: str = "main") -> Blob: ... + + def commit(self) -> None: ... + def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... + if sys.version_info >= (3, 11): + # num_params determines how many params will be passed to the aggregate class. We provide an overload + # for the case where num_params = 1, which is expected to be the common case. + @overload + def create_window_function( + self, name: str, num_params: Literal[1], aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None, / + ) -> None: ... + # And for num_params = -1, which means the aggregate must accept any number of parameters. + @overload + def create_window_function( + self, name: str, num_params: Literal[-1], aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None, / + ) -> None: ... + @overload + def create_window_function( + self, name: str, num_params: int, aggregate_class: Callable[[], _WindowAggregateClass] | None, / + ) -> None: ... + + def create_collation(self, name: str, callback: Callable[[str, str], int | SupportsIndex] | None, /) -> None: ... + def create_function( + self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False + ) -> None: ... + @overload + def cursor(self, factory: None = None) -> Cursor: ... + @overload + def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... + def execute(self, sql: str, parameters: _Parameters = ..., /) -> Cursor: ... + def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... + def executescript(self, sql_script: str, /) -> Cursor: ... + def interrupt(self) -> None: ... + if sys.version_info >= (3, 13): + def iterdump(self, *, filter: str | None = None) -> Generator[str, None, None]: ... + else: + def iterdump(self) -> Generator[str, None, None]: ... + + def rollback(self) -> None: ... + def set_authorizer( + self, authorizer_callback: Callable[[int, str | None, str | None, str | None, str | None], int] | None + ) -> None: ... + def set_progress_handler(self, progress_handler: Callable[[], int | None] | None, n: int) -> None: ... + def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... + # enable_load_extension and load_extension is not available on python distributions compiled + # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 + def enable_load_extension(self, enable: bool, /) -> None: ... + if sys.version_info >= (3, 12): + def load_extension(self, name: str, /, *, entrypoint: str | None = None) -> None: ... + else: + def load_extension(self, name: str, /) -> None: ... + + def backup( + self, + target: Connection, + *, + pages: int = -1, + progress: Callable[[int, int, int], object] | None = None, + name: str = "main", + sleep: float = 0.25, + ) -> None: ... + if sys.version_info >= (3, 11): + def setlimit(self, category: int, limit: int, /) -> int: ... + def getlimit(self, category: int, /) -> int: ... + def serialize(self, *, name: str = "main") -> bytes: ... + def deserialize(self, data: ReadableBuffer, /, *, name: str = "main") -> None: ... + if sys.version_info >= (3, 12): + def getconfig(self, op: int, /) -> bool: ... + def setconfig(self, op: int, enable: bool = True, /) -> bool: ... + + def __call__(self, sql: str, /) -> _Statement: ... + def __enter__(self) -> Self: ... + def __exit__( + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / + ) -> Literal[False]: ... + +class Cursor: + arraysize: int + @property + def connection(self) -> Connection: ... + # May be None, but using | Any instead to avoid slightly annoying false positives. + @property + def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | Any: ... + @property + def lastrowid(self) -> int | None: ... + row_factory: Callable[[Cursor, Row], object] | None + @property + def rowcount(self) -> int: ... + def __init__(self, cursor: Connection, /) -> None: ... + def close(self) -> None: ... + def execute(self, sql: str, parameters: _Parameters = (), /) -> Self: ... + def executemany(self, sql: str, seq_of_parameters: Iterable[_Parameters], /) -> Self: ... + def executescript(self, sql_script: str, /) -> Cursor: ... + def fetchall(self) -> list[Any]: ... + def fetchmany(self, size: int | None = 1) -> list[Any]: ... + # Returns either a row (as created by the row_factory) or None, but + # putting None in the return annotation causes annoying false positives. + def fetchone(self) -> Any: ... + def setinputsizes(self, sizes: Unused, /) -> None: ... # does nothing + def setoutputsize(self, size: Unused, column: Unused = None, /) -> None: ... # does nothing + def __iter__(self) -> Self: ... + def __next__(self) -> Any: ... + +@final +class PrepareProtocol: + def __init__(self, *args: object, **kwargs: object) -> None: ... + +class Row(Sequence[Any]): + def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... + def keys(self) -> list[str]: ... + @overload + def __getitem__(self, key: int | str, /) -> Any: ... + @overload + def __getitem__(self, key: slice, /) -> tuple[Any, ...]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[Any]: ... + def __len__(self) -> int: ... + # These return NotImplemented for anything that is not a Row. + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: object, /) -> bool: ... + def __gt__(self, value: object, /) -> bool: ... + def __le__(self, value: object, /) -> bool: ... + def __lt__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + +@final +class _Statement: ... + +if sys.version_info >= (3, 11): + @final + class Blob: + def close(self) -> None: ... + def read(self, length: int = -1, /) -> bytes: ... + def write(self, data: ReadableBuffer, /) -> None: ... + def tell(self) -> int: ... + # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END + def seek(self, offset: int, origin: int = 0, /) -> None: ... + def __len__(self) -> int: ... + def __enter__(self) -> Self: ... + def __exit__(self, type: object, val: object, tb: object, /) -> Literal[False]: ... + def __getitem__(self, key: SupportsIndex | slice, /) -> int: ... + def __setitem__(self, key: SupportsIndex | slice, value: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 0ee511df4e37..d3ea3ef0e896 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -1,22 +1,226 @@ -import sqlite3 import sys -from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused -from collections.abc import Callable, Generator, Iterable, Iterator, Mapping +from _sqlite3 import ( + PARSE_COLNAMES as PARSE_COLNAMES, + PARSE_DECLTYPES as PARSE_DECLTYPES, + SQLITE_ALTER_TABLE as SQLITE_ALTER_TABLE, + SQLITE_ANALYZE as SQLITE_ANALYZE, + SQLITE_ATTACH as SQLITE_ATTACH, + SQLITE_CREATE_INDEX as SQLITE_CREATE_INDEX, + SQLITE_CREATE_TABLE as SQLITE_CREATE_TABLE, + SQLITE_CREATE_TEMP_INDEX as SQLITE_CREATE_TEMP_INDEX, + SQLITE_CREATE_TEMP_TABLE as SQLITE_CREATE_TEMP_TABLE, + SQLITE_CREATE_TEMP_TRIGGER as SQLITE_CREATE_TEMP_TRIGGER, + SQLITE_CREATE_TEMP_VIEW as SQLITE_CREATE_TEMP_VIEW, + SQLITE_CREATE_TRIGGER as SQLITE_CREATE_TRIGGER, + SQLITE_CREATE_VIEW as SQLITE_CREATE_VIEW, + SQLITE_CREATE_VTABLE as SQLITE_CREATE_VTABLE, + SQLITE_DELETE as SQLITE_DELETE, + SQLITE_DENY as SQLITE_DENY, + SQLITE_DETACH as SQLITE_DETACH, + SQLITE_DONE as SQLITE_DONE, + SQLITE_DROP_INDEX as SQLITE_DROP_INDEX, + SQLITE_DROP_TABLE as SQLITE_DROP_TABLE, + SQLITE_DROP_TEMP_INDEX as SQLITE_DROP_TEMP_INDEX, + SQLITE_DROP_TEMP_TABLE as SQLITE_DROP_TEMP_TABLE, + SQLITE_DROP_TEMP_TRIGGER as SQLITE_DROP_TEMP_TRIGGER, + SQLITE_DROP_TEMP_VIEW as SQLITE_DROP_TEMP_VIEW, + SQLITE_DROP_TRIGGER as SQLITE_DROP_TRIGGER, + SQLITE_DROP_VIEW as SQLITE_DROP_VIEW, + SQLITE_DROP_VTABLE as SQLITE_DROP_VTABLE, + SQLITE_FUNCTION as SQLITE_FUNCTION, + SQLITE_IGNORE as SQLITE_IGNORE, + SQLITE_INSERT as SQLITE_INSERT, + SQLITE_OK as SQLITE_OK, + SQLITE_PRAGMA as SQLITE_PRAGMA, + SQLITE_READ as SQLITE_READ, + SQLITE_RECURSIVE as SQLITE_RECURSIVE, + SQLITE_REINDEX as SQLITE_REINDEX, + SQLITE_SAVEPOINT as SQLITE_SAVEPOINT, + SQLITE_SELECT as SQLITE_SELECT, + SQLITE_TRANSACTION as SQLITE_TRANSACTION, + SQLITE_UPDATE as SQLITE_UPDATE, + adapt as adapt, + adapters as adapters, + complete_statement as complete_statement, + connect as connect, + converters as converters, + enable_callback_tracebacks as enable_callback_tracebacks, + register_adapter as register_adapter, + register_converter as register_converter, + sqlite_version as sqlite_version, +) from datetime import date, datetime, time -from types import TracebackType -from typing import Any, Final, Literal, Protocol, SupportsIndex, TypeVar, final, overload -from typing_extensions import Self, TypeAlias +from sqlite3 import ( + Connection as Connection, + Cursor as Cursor, + DatabaseError as DatabaseError, + DataError as DataError, + Error as Error, + IntegrityError as IntegrityError, + InterfaceError as InterfaceError, + InternalError as InternalError, + NotSupportedError as NotSupportedError, + OperationalError as OperationalError, + PrepareProtocol as PrepareProtocol, + ProgrammingError as ProgrammingError, + Row as Row, + Warning as Warning, +) -_T = TypeVar("_T") -_ConnectionT = TypeVar("_ConnectionT", bound=Connection) -_CursorT = TypeVar("_CursorT", bound=Cursor) -_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None -# Data that is passed through adapters can be of any type accepted by an adapter. -_AdaptedInputData: TypeAlias = _SqliteData | Any -# The Mapping must really be a dict, but making it invariant is too annoying. -_Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData] -_Adapter: TypeAlias = Callable[[_T], _SqliteData] -_Converter: TypeAlias = Callable[[bytes], Any] +if sys.version_info >= (3, 12): + from _sqlite3 import ( + LEGACY_TRANSACTION_CONTROL as LEGACY_TRANSACTION_CONTROL, + SQLITE_DBCONFIG_DEFENSIVE as SQLITE_DBCONFIG_DEFENSIVE, + SQLITE_DBCONFIG_DQS_DDL as SQLITE_DBCONFIG_DQS_DDL, + SQLITE_DBCONFIG_DQS_DML as SQLITE_DBCONFIG_DQS_DML, + SQLITE_DBCONFIG_ENABLE_FKEY as SQLITE_DBCONFIG_ENABLE_FKEY, + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER as SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION as SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + SQLITE_DBCONFIG_ENABLE_QPSG as SQLITE_DBCONFIG_ENABLE_QPSG, + SQLITE_DBCONFIG_ENABLE_TRIGGER as SQLITE_DBCONFIG_ENABLE_TRIGGER, + SQLITE_DBCONFIG_ENABLE_VIEW as SQLITE_DBCONFIG_ENABLE_VIEW, + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE as SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT as SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE as SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, + SQLITE_DBCONFIG_RESET_DATABASE as SQLITE_DBCONFIG_RESET_DATABASE, + SQLITE_DBCONFIG_TRIGGER_EQP as SQLITE_DBCONFIG_TRIGGER_EQP, + SQLITE_DBCONFIG_TRUSTED_SCHEMA as SQLITE_DBCONFIG_TRUSTED_SCHEMA, + SQLITE_DBCONFIG_WRITABLE_SCHEMA as SQLITE_DBCONFIG_WRITABLE_SCHEMA, + ) + +if sys.version_info >= (3, 11): + from _sqlite3 import ( + SQLITE_ABORT as SQLITE_ABORT, + SQLITE_ABORT_ROLLBACK as SQLITE_ABORT_ROLLBACK, + SQLITE_AUTH as SQLITE_AUTH, + SQLITE_AUTH_USER as SQLITE_AUTH_USER, + SQLITE_BUSY as SQLITE_BUSY, + SQLITE_BUSY_RECOVERY as SQLITE_BUSY_RECOVERY, + SQLITE_BUSY_SNAPSHOT as SQLITE_BUSY_SNAPSHOT, + SQLITE_BUSY_TIMEOUT as SQLITE_BUSY_TIMEOUT, + SQLITE_CANTOPEN as SQLITE_CANTOPEN, + SQLITE_CANTOPEN_CONVPATH as SQLITE_CANTOPEN_CONVPATH, + SQLITE_CANTOPEN_DIRTYWAL as SQLITE_CANTOPEN_DIRTYWAL, + SQLITE_CANTOPEN_FULLPATH as SQLITE_CANTOPEN_FULLPATH, + SQLITE_CANTOPEN_ISDIR as SQLITE_CANTOPEN_ISDIR, + SQLITE_CANTOPEN_NOTEMPDIR as SQLITE_CANTOPEN_NOTEMPDIR, + SQLITE_CANTOPEN_SYMLINK as SQLITE_CANTOPEN_SYMLINK, + SQLITE_CONSTRAINT as SQLITE_CONSTRAINT, + SQLITE_CONSTRAINT_CHECK as SQLITE_CONSTRAINT_CHECK, + SQLITE_CONSTRAINT_COMMITHOOK as SQLITE_CONSTRAINT_COMMITHOOK, + SQLITE_CONSTRAINT_FOREIGNKEY as SQLITE_CONSTRAINT_FOREIGNKEY, + SQLITE_CONSTRAINT_FUNCTION as SQLITE_CONSTRAINT_FUNCTION, + SQLITE_CONSTRAINT_NOTNULL as SQLITE_CONSTRAINT_NOTNULL, + SQLITE_CONSTRAINT_PINNED as SQLITE_CONSTRAINT_PINNED, + SQLITE_CONSTRAINT_PRIMARYKEY as SQLITE_CONSTRAINT_PRIMARYKEY, + SQLITE_CONSTRAINT_ROWID as SQLITE_CONSTRAINT_ROWID, + SQLITE_CONSTRAINT_TRIGGER as SQLITE_CONSTRAINT_TRIGGER, + SQLITE_CONSTRAINT_UNIQUE as SQLITE_CONSTRAINT_UNIQUE, + SQLITE_CONSTRAINT_VTAB as SQLITE_CONSTRAINT_VTAB, + SQLITE_CORRUPT as SQLITE_CORRUPT, + SQLITE_CORRUPT_INDEX as SQLITE_CORRUPT_INDEX, + SQLITE_CORRUPT_SEQUENCE as SQLITE_CORRUPT_SEQUENCE, + SQLITE_CORRUPT_VTAB as SQLITE_CORRUPT_VTAB, + SQLITE_EMPTY as SQLITE_EMPTY, + SQLITE_ERROR as SQLITE_ERROR, + SQLITE_ERROR_MISSING_COLLSEQ as SQLITE_ERROR_MISSING_COLLSEQ, + SQLITE_ERROR_RETRY as SQLITE_ERROR_RETRY, + SQLITE_ERROR_SNAPSHOT as SQLITE_ERROR_SNAPSHOT, + SQLITE_FORMAT as SQLITE_FORMAT, + SQLITE_FULL as SQLITE_FULL, + SQLITE_INTERNAL as SQLITE_INTERNAL, + SQLITE_INTERRUPT as SQLITE_INTERRUPT, + SQLITE_IOERR as SQLITE_IOERR, + SQLITE_IOERR_ACCESS as SQLITE_IOERR_ACCESS, + SQLITE_IOERR_AUTH as SQLITE_IOERR_AUTH, + SQLITE_IOERR_BEGIN_ATOMIC as SQLITE_IOERR_BEGIN_ATOMIC, + SQLITE_IOERR_BLOCKED as SQLITE_IOERR_BLOCKED, + SQLITE_IOERR_CHECKRESERVEDLOCK as SQLITE_IOERR_CHECKRESERVEDLOCK, + SQLITE_IOERR_CLOSE as SQLITE_IOERR_CLOSE, + SQLITE_IOERR_COMMIT_ATOMIC as SQLITE_IOERR_COMMIT_ATOMIC, + SQLITE_IOERR_CONVPATH as SQLITE_IOERR_CONVPATH, + SQLITE_IOERR_CORRUPTFS as SQLITE_IOERR_CORRUPTFS, + SQLITE_IOERR_DATA as SQLITE_IOERR_DATA, + SQLITE_IOERR_DELETE as SQLITE_IOERR_DELETE, + SQLITE_IOERR_DELETE_NOENT as SQLITE_IOERR_DELETE_NOENT, + SQLITE_IOERR_DIR_CLOSE as SQLITE_IOERR_DIR_CLOSE, + SQLITE_IOERR_DIR_FSYNC as SQLITE_IOERR_DIR_FSYNC, + SQLITE_IOERR_FSTAT as SQLITE_IOERR_FSTAT, + SQLITE_IOERR_FSYNC as SQLITE_IOERR_FSYNC, + SQLITE_IOERR_GETTEMPPATH as SQLITE_IOERR_GETTEMPPATH, + SQLITE_IOERR_LOCK as SQLITE_IOERR_LOCK, + SQLITE_IOERR_MMAP as SQLITE_IOERR_MMAP, + SQLITE_IOERR_NOMEM as SQLITE_IOERR_NOMEM, + SQLITE_IOERR_RDLOCK as SQLITE_IOERR_RDLOCK, + SQLITE_IOERR_READ as SQLITE_IOERR_READ, + SQLITE_IOERR_ROLLBACK_ATOMIC as SQLITE_IOERR_ROLLBACK_ATOMIC, + SQLITE_IOERR_SEEK as SQLITE_IOERR_SEEK, + SQLITE_IOERR_SHMLOCK as SQLITE_IOERR_SHMLOCK, + SQLITE_IOERR_SHMMAP as SQLITE_IOERR_SHMMAP, + SQLITE_IOERR_SHMOPEN as SQLITE_IOERR_SHMOPEN, + SQLITE_IOERR_SHMSIZE as SQLITE_IOERR_SHMSIZE, + SQLITE_IOERR_SHORT_READ as SQLITE_IOERR_SHORT_READ, + SQLITE_IOERR_TRUNCATE as SQLITE_IOERR_TRUNCATE, + SQLITE_IOERR_UNLOCK as SQLITE_IOERR_UNLOCK, + SQLITE_IOERR_VNODE as SQLITE_IOERR_VNODE, + SQLITE_IOERR_WRITE as SQLITE_IOERR_WRITE, + SQLITE_LIMIT_ATTACHED as SQLITE_LIMIT_ATTACHED, + SQLITE_LIMIT_COLUMN as SQLITE_LIMIT_COLUMN, + SQLITE_LIMIT_COMPOUND_SELECT as SQLITE_LIMIT_COMPOUND_SELECT, + SQLITE_LIMIT_EXPR_DEPTH as SQLITE_LIMIT_EXPR_DEPTH, + SQLITE_LIMIT_FUNCTION_ARG as SQLITE_LIMIT_FUNCTION_ARG, + SQLITE_LIMIT_LENGTH as SQLITE_LIMIT_LENGTH, + SQLITE_LIMIT_LIKE_PATTERN_LENGTH as SQLITE_LIMIT_LIKE_PATTERN_LENGTH, + SQLITE_LIMIT_SQL_LENGTH as SQLITE_LIMIT_SQL_LENGTH, + SQLITE_LIMIT_TRIGGER_DEPTH as SQLITE_LIMIT_TRIGGER_DEPTH, + SQLITE_LIMIT_VARIABLE_NUMBER as SQLITE_LIMIT_VARIABLE_NUMBER, + SQLITE_LIMIT_VDBE_OP as SQLITE_LIMIT_VDBE_OP, + SQLITE_LIMIT_WORKER_THREADS as SQLITE_LIMIT_WORKER_THREADS, + SQLITE_LOCKED as SQLITE_LOCKED, + SQLITE_LOCKED_SHAREDCACHE as SQLITE_LOCKED_SHAREDCACHE, + SQLITE_LOCKED_VTAB as SQLITE_LOCKED_VTAB, + SQLITE_MISMATCH as SQLITE_MISMATCH, + SQLITE_MISUSE as SQLITE_MISUSE, + SQLITE_NOLFS as SQLITE_NOLFS, + SQLITE_NOMEM as SQLITE_NOMEM, + SQLITE_NOTADB as SQLITE_NOTADB, + SQLITE_NOTFOUND as SQLITE_NOTFOUND, + SQLITE_NOTICE as SQLITE_NOTICE, + SQLITE_NOTICE_RECOVER_ROLLBACK as SQLITE_NOTICE_RECOVER_ROLLBACK, + SQLITE_NOTICE_RECOVER_WAL as SQLITE_NOTICE_RECOVER_WAL, + SQLITE_OK_LOAD_PERMANENTLY as SQLITE_OK_LOAD_PERMANENTLY, + SQLITE_OK_SYMLINK as SQLITE_OK_SYMLINK, + SQLITE_PERM as SQLITE_PERM, + SQLITE_PROTOCOL as SQLITE_PROTOCOL, + SQLITE_RANGE as SQLITE_RANGE, + SQLITE_READONLY as SQLITE_READONLY, + SQLITE_READONLY_CANTINIT as SQLITE_READONLY_CANTINIT, + SQLITE_READONLY_CANTLOCK as SQLITE_READONLY_CANTLOCK, + SQLITE_READONLY_DBMOVED as SQLITE_READONLY_DBMOVED, + SQLITE_READONLY_DIRECTORY as SQLITE_READONLY_DIRECTORY, + SQLITE_READONLY_RECOVERY as SQLITE_READONLY_RECOVERY, + SQLITE_READONLY_ROLLBACK as SQLITE_READONLY_ROLLBACK, + SQLITE_ROW as SQLITE_ROW, + SQLITE_SCHEMA as SQLITE_SCHEMA, + SQLITE_TOOBIG as SQLITE_TOOBIG, + SQLITE_WARNING as SQLITE_WARNING, + SQLITE_WARNING_AUTOINDEX as SQLITE_WARNING_AUTOINDEX, + ) + from sqlite3 import Blob as Blob + +if sys.version_info < (3, 14): + # Deprecated and removed from _sqlite3 in 3.12, but removed from here in 3.14. + version: str + +if sys.version_info < (3, 12): + if sys.version_info >= (3, 10): + # deprecation wrapper that has a different name for the argument... + def enable_shared_cache(enable: int) -> None: ... + else: + from _sqlite3 import enable_shared_cache as enable_shared_cache + +if sys.version_info < (3, 10): + from _sqlite3 import OptimizedUnicode as OptimizedUnicode paramstyle: str threadsafety: int @@ -35,527 +239,3 @@ if sys.version_info < (3, 14): sqlite_version_info: tuple[int, int, int] Binary = memoryview - -# The remaining definitions are imported from _sqlite3. - -PARSE_COLNAMES: Final[int] -PARSE_DECLTYPES: Final[int] -SQLITE_ALTER_TABLE: Final[int] -SQLITE_ANALYZE: Final[int] -SQLITE_ATTACH: Final[int] -SQLITE_CREATE_INDEX: Final[int] -SQLITE_CREATE_TABLE: Final[int] -SQLITE_CREATE_TEMP_INDEX: Final[int] -SQLITE_CREATE_TEMP_TABLE: Final[int] -SQLITE_CREATE_TEMP_TRIGGER: Final[int] -SQLITE_CREATE_TEMP_VIEW: Final[int] -SQLITE_CREATE_TRIGGER: Final[int] -SQLITE_CREATE_VIEW: Final[int] -SQLITE_CREATE_VTABLE: Final[int] -SQLITE_DELETE: Final[int] -SQLITE_DENY: Final[int] -SQLITE_DETACH: Final[int] -SQLITE_DONE: Final[int] -SQLITE_DROP_INDEX: Final[int] -SQLITE_DROP_TABLE: Final[int] -SQLITE_DROP_TEMP_INDEX: Final[int] -SQLITE_DROP_TEMP_TABLE: Final[int] -SQLITE_DROP_TEMP_TRIGGER: Final[int] -SQLITE_DROP_TEMP_VIEW: Final[int] -SQLITE_DROP_TRIGGER: Final[int] -SQLITE_DROP_VIEW: Final[int] -SQLITE_DROP_VTABLE: Final[int] -SQLITE_FUNCTION: Final[int] -SQLITE_IGNORE: Final[int] -SQLITE_INSERT: Final[int] -SQLITE_OK: Final[int] -if sys.version_info >= (3, 11): - SQLITE_LIMIT_LENGTH: Final[int] - SQLITE_LIMIT_SQL_LENGTH: Final[int] - SQLITE_LIMIT_COLUMN: Final[int] - SQLITE_LIMIT_EXPR_DEPTH: Final[int] - SQLITE_LIMIT_COMPOUND_SELECT: Final[int] - SQLITE_LIMIT_VDBE_OP: Final[int] - SQLITE_LIMIT_FUNCTION_ARG: Final[int] - SQLITE_LIMIT_ATTACHED: Final[int] - SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int] - SQLITE_LIMIT_VARIABLE_NUMBER: Final[int] - SQLITE_LIMIT_TRIGGER_DEPTH: Final[int] - SQLITE_LIMIT_WORKER_THREADS: Final[int] -SQLITE_PRAGMA: Final[int] -SQLITE_READ: Final[int] -SQLITE_REINDEX: Final[int] -SQLITE_RECURSIVE: Final[int] -SQLITE_SAVEPOINT: Final[int] -SQLITE_SELECT: Final[int] -SQLITE_TRANSACTION: Final[int] -SQLITE_UPDATE: Final[int] -adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] -converters: dict[str, _Converter] -sqlite_version: str - -if sys.version_info < (3, 14): - # Deprecated in 3.12, removed in 3.14. - version: str - -if sys.version_info >= (3, 11): - SQLITE_ABORT: Final[int] - SQLITE_ABORT_ROLLBACK: Final[int] - SQLITE_AUTH: Final[int] - SQLITE_AUTH_USER: Final[int] - SQLITE_BUSY: Final[int] - SQLITE_BUSY_RECOVERY: Final[int] - SQLITE_BUSY_SNAPSHOT: Final[int] - SQLITE_BUSY_TIMEOUT: Final[int] - SQLITE_CANTOPEN: Final[int] - SQLITE_CANTOPEN_CONVPATH: Final[int] - SQLITE_CANTOPEN_DIRTYWAL: Final[int] - SQLITE_CANTOPEN_FULLPATH: Final[int] - SQLITE_CANTOPEN_ISDIR: Final[int] - SQLITE_CANTOPEN_NOTEMPDIR: Final[int] - SQLITE_CANTOPEN_SYMLINK: Final[int] - SQLITE_CONSTRAINT: Final[int] - SQLITE_CONSTRAINT_CHECK: Final[int] - SQLITE_CONSTRAINT_COMMITHOOK: Final[int] - SQLITE_CONSTRAINT_FOREIGNKEY: Final[int] - SQLITE_CONSTRAINT_FUNCTION: Final[int] - SQLITE_CONSTRAINT_NOTNULL: Final[int] - SQLITE_CONSTRAINT_PINNED: Final[int] - SQLITE_CONSTRAINT_PRIMARYKEY: Final[int] - SQLITE_CONSTRAINT_ROWID: Final[int] - SQLITE_CONSTRAINT_TRIGGER: Final[int] - SQLITE_CONSTRAINT_UNIQUE: Final[int] - SQLITE_CONSTRAINT_VTAB: Final[int] - SQLITE_CORRUPT: Final[int] - SQLITE_CORRUPT_INDEX: Final[int] - SQLITE_CORRUPT_SEQUENCE: Final[int] - SQLITE_CORRUPT_VTAB: Final[int] - SQLITE_EMPTY: Final[int] - SQLITE_ERROR: Final[int] - SQLITE_ERROR_MISSING_COLLSEQ: Final[int] - SQLITE_ERROR_RETRY: Final[int] - SQLITE_ERROR_SNAPSHOT: Final[int] - SQLITE_FORMAT: Final[int] - SQLITE_FULL: Final[int] - SQLITE_INTERNAL: Final[int] - SQLITE_INTERRUPT: Final[int] - SQLITE_IOERR: Final[int] - SQLITE_IOERR_ACCESS: Final[int] - SQLITE_IOERR_AUTH: Final[int] - SQLITE_IOERR_BEGIN_ATOMIC: Final[int] - SQLITE_IOERR_BLOCKED: Final[int] - SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int] - SQLITE_IOERR_CLOSE: Final[int] - SQLITE_IOERR_COMMIT_ATOMIC: Final[int] - SQLITE_IOERR_CONVPATH: Final[int] - SQLITE_IOERR_CORRUPTFS: Final[int] - SQLITE_IOERR_DATA: Final[int] - SQLITE_IOERR_DELETE: Final[int] - SQLITE_IOERR_DELETE_NOENT: Final[int] - SQLITE_IOERR_DIR_CLOSE: Final[int] - SQLITE_IOERR_DIR_FSYNC: Final[int] - SQLITE_IOERR_FSTAT: Final[int] - SQLITE_IOERR_FSYNC: Final[int] - SQLITE_IOERR_GETTEMPPATH: Final[int] - SQLITE_IOERR_LOCK: Final[int] - SQLITE_IOERR_MMAP: Final[int] - SQLITE_IOERR_NOMEM: Final[int] - SQLITE_IOERR_RDLOCK: Final[int] - SQLITE_IOERR_READ: Final[int] - SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int] - SQLITE_IOERR_SEEK: Final[int] - SQLITE_IOERR_SHMLOCK: Final[int] - SQLITE_IOERR_SHMMAP: Final[int] - SQLITE_IOERR_SHMOPEN: Final[int] - SQLITE_IOERR_SHMSIZE: Final[int] - SQLITE_IOERR_SHORT_READ: Final[int] - SQLITE_IOERR_TRUNCATE: Final[int] - SQLITE_IOERR_UNLOCK: Final[int] - SQLITE_IOERR_VNODE: Final[int] - SQLITE_IOERR_WRITE: Final[int] - SQLITE_LOCKED: Final[int] - SQLITE_LOCKED_SHAREDCACHE: Final[int] - SQLITE_LOCKED_VTAB: Final[int] - SQLITE_MISMATCH: Final[int] - SQLITE_MISUSE: Final[int] - SQLITE_NOLFS: Final[int] - SQLITE_NOMEM: Final[int] - SQLITE_NOTADB: Final[int] - SQLITE_NOTFOUND: Final[int] - SQLITE_NOTICE: Final[int] - SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int] - SQLITE_NOTICE_RECOVER_WAL: Final[int] - SQLITE_OK_LOAD_PERMANENTLY: Final[int] - SQLITE_OK_SYMLINK: Final[int] - SQLITE_PERM: Final[int] - SQLITE_PROTOCOL: Final[int] - SQLITE_RANGE: Final[int] - SQLITE_READONLY: Final[int] - SQLITE_READONLY_CANTINIT: Final[int] - SQLITE_READONLY_CANTLOCK: Final[int] - SQLITE_READONLY_DBMOVED: Final[int] - SQLITE_READONLY_DIRECTORY: Final[int] - SQLITE_READONLY_RECOVERY: Final[int] - SQLITE_READONLY_ROLLBACK: Final[int] - SQLITE_ROW: Final[int] - SQLITE_SCHEMA: Final[int] - SQLITE_TOOBIG: Final[int] - SQLITE_WARNING: Final[int] - SQLITE_WARNING_AUTOINDEX: Final[int] - -if sys.version_info >= (3, 12): - LEGACY_TRANSACTION_CONTROL: Final[int] - SQLITE_DBCONFIG_DEFENSIVE: Final[int] - SQLITE_DBCONFIG_DQS_DDL: Final[int] - SQLITE_DBCONFIG_DQS_DML: Final[int] - SQLITE_DBCONFIG_ENABLE_FKEY: Final[int] - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int] - SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int] - SQLITE_DBCONFIG_ENABLE_QPSG: Final[int] - SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int] - SQLITE_DBCONFIG_ENABLE_VIEW: Final[int] - SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int] - SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int] - SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int] - SQLITE_DBCONFIG_RESET_DATABASE: Final[int] - SQLITE_DBCONFIG_TRIGGER_EQP: Final[int] - SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int] - SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int] - -# Can take or return anything depending on what's in the registry. -@overload -def adapt(obj: Any, proto: Any, /) -> Any: ... -@overload -def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ... -def complete_statement(statement: str) -> bool: ... - -if sys.version_info >= (3, 12): - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - cached_statements: int = 128, - uri: bool = False, - *, - autocommit: bool = ..., - ) -> Connection: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float, - detect_types: int, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, - check_same_thread: bool, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - *, - autocommit: bool = ..., - ) -> _ConnectionT: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - *, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - autocommit: bool = ..., - ) -> _ConnectionT: ... - -else: - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - cached_statements: int = 128, - uri: bool = False, - ) -> Connection: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float, - detect_types: int, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, - check_same_thread: bool, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - ) -> _ConnectionT: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - *, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - ) -> _ConnectionT: ... - -def enable_callback_tracebacks(enable: bool, /) -> None: ... - -if sys.version_info < (3, 12): - # takes a pos-or-keyword argument because there is a C wrapper - def enable_shared_cache(enable: int) -> None: ... - -if sys.version_info >= (3, 10): - def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ... - def register_converter(typename: str, converter: _Converter, /) -> None: ... - -else: - def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ... - def register_converter(name: str, converter: _Converter, /) -> None: ... - -class _AggregateProtocol(Protocol): - def step(self, value: int, /) -> object: ... - def finalize(self) -> int: ... - -class _SingleParamWindowAggregateClass(Protocol): - def step(self, param: Any, /) -> object: ... - def inverse(self, param: Any, /) -> object: ... - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class _AnyParamWindowAggregateClass(Protocol): - def step(self, *args: Any) -> object: ... - def inverse(self, *args: Any) -> object: ... - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class _WindowAggregateClass(Protocol): - step: Callable[..., object] - inverse: Callable[..., object] - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class Connection: - @property - def DataError(self) -> type[sqlite3.DataError]: ... - @property - def DatabaseError(self) -> type[sqlite3.DatabaseError]: ... - @property - def Error(self) -> type[sqlite3.Error]: ... - @property - def IntegrityError(self) -> type[sqlite3.IntegrityError]: ... - @property - def InterfaceError(self) -> type[sqlite3.InterfaceError]: ... - @property - def InternalError(self) -> type[sqlite3.InternalError]: ... - @property - def NotSupportedError(self) -> type[sqlite3.NotSupportedError]: ... - @property - def OperationalError(self) -> type[sqlite3.OperationalError]: ... - @property - def ProgrammingError(self) -> type[sqlite3.ProgrammingError]: ... - @property - def Warning(self) -> type[sqlite3.Warning]: ... - @property - def in_transaction(self) -> bool: ... - isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' - @property - def total_changes(self) -> int: ... - if sys.version_info >= (3, 12): - @property - def autocommit(self) -> int: ... - @autocommit.setter - def autocommit(self, val: int) -> None: ... - row_factory: Any - text_factory: Any - if sys.version_info >= (3, 12): - def __init__( - self, - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - autocommit: bool = ..., - ) -> None: ... - else: - def __init__( - self, - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - ) -> None: ... - - def close(self) -> None: ... - if sys.version_info >= (3, 11): - def blobopen(self, table: str, column: str, row: int, /, *, readonly: bool = False, name: str = "main") -> Blob: ... - - def commit(self) -> None: ... - def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... - if sys.version_info >= (3, 11): - # num_params determines how many params will be passed to the aggregate class. We provide an overload - # for the case where num_params = 1, which is expected to be the common case. - @overload - def create_window_function( - self, name: str, num_params: Literal[1], aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None, / - ) -> None: ... - # And for num_params = -1, which means the aggregate must accept any number of parameters. - @overload - def create_window_function( - self, name: str, num_params: Literal[-1], aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None, / - ) -> None: ... - @overload - def create_window_function( - self, name: str, num_params: int, aggregate_class: Callable[[], _WindowAggregateClass] | None, / - ) -> None: ... - - def create_collation(self, name: str, callback: Callable[[str, str], int | SupportsIndex] | None, /) -> None: ... - def create_function( - self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False - ) -> None: ... - @overload - def cursor(self, factory: None = None) -> Cursor: ... - @overload - def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... - def execute(self, sql: str, parameters: _Parameters = ..., /) -> Cursor: ... - def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... - def executescript(self, sql_script: str, /) -> Cursor: ... - def interrupt(self) -> None: ... - if sys.version_info >= (3, 13): - def iterdump(self, *, filter: str | None = None) -> Generator[str, None, None]: ... - else: - def iterdump(self) -> Generator[str, None, None]: ... - - def rollback(self) -> None: ... - def set_authorizer( - self, authorizer_callback: Callable[[int, str | None, str | None, str | None, str | None], int] | None - ) -> None: ... - def set_progress_handler(self, progress_handler: Callable[[], int | None] | None, n: int) -> None: ... - def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... - # enable_load_extension and load_extension is not available on python distributions compiled - # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 - def enable_load_extension(self, enable: bool, /) -> None: ... - def load_extension(self, name: str, /) -> None: ... - def backup( - self, - target: Connection, - *, - pages: int = -1, - progress: Callable[[int, int, int], object] | None = None, - name: str = "main", - sleep: float = 0.25, - ) -> None: ... - if sys.version_info >= (3, 11): - def setlimit(self, category: int, limit: int, /) -> int: ... - def getlimit(self, category: int, /) -> int: ... - def serialize(self, *, name: str = "main") -> bytes: ... - def deserialize(self, data: ReadableBuffer, /, *, name: str = "main") -> None: ... - if sys.version_info >= (3, 12): - def getconfig(self, op: int, /) -> bool: ... - def setconfig(self, op: int, enable: bool = True, /) -> bool: ... - - def __call__(self, sql: str, /) -> _Statement: ... - def __enter__(self) -> Self: ... - def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / - ) -> Literal[False]: ... - -class Cursor(Iterator[Any]): - arraysize: int - @property - def connection(self) -> Connection: ... - # May be None, but using | Any instead to avoid slightly annoying false positives. - @property - def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | Any: ... - @property - def lastrowid(self) -> int | None: ... - row_factory: Callable[[Cursor, Row], object] | None - @property - def rowcount(self) -> int: ... - def __init__(self, cursor: Connection, /) -> None: ... - def close(self) -> None: ... - def execute(self, sql: str, parameters: _Parameters = (), /) -> Self: ... - def executemany(self, sql: str, seq_of_parameters: Iterable[_Parameters], /) -> Self: ... - def executescript(self, sql_script: str, /) -> Cursor: ... - def fetchall(self) -> list[Any]: ... - def fetchmany(self, size: int | None = 1) -> list[Any]: ... - # Returns either a row (as created by the row_factory) or None, but - # putting None in the return annotation causes annoying false positives. - def fetchone(self) -> Any: ... - def setinputsizes(self, sizes: Unused, /) -> None: ... # does nothing - def setoutputsize(self, size: Unused, column: Unused = None, /) -> None: ... # does nothing - def __iter__(self) -> Self: ... - def __next__(self) -> Any: ... - -class Error(Exception): - if sys.version_info >= (3, 11): - sqlite_errorcode: int - sqlite_errorname: str - -class DatabaseError(Error): ... -class DataError(DatabaseError): ... -class IntegrityError(DatabaseError): ... -class InterfaceError(Error): ... -class InternalError(DatabaseError): ... -class NotSupportedError(DatabaseError): ... -class OperationalError(DatabaseError): ... - -if sys.version_info < (3, 10): - OptimizedUnicode = str - -@final -class PrepareProtocol: - def __init__(self, *args: object, **kwargs: object) -> None: ... - -class ProgrammingError(DatabaseError): ... - -class Row: - def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... - def keys(self) -> list[str]: ... - @overload - def __getitem__(self, key: int | str, /) -> Any: ... - @overload - def __getitem__(self, key: slice, /) -> tuple[Any, ...]: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[Any]: ... - def __len__(self) -> int: ... - # These return NotImplemented for anything that is not a Row. - def __eq__(self, value: object, /) -> bool: ... - def __ge__(self, value: object, /) -> bool: ... - def __gt__(self, value: object, /) -> bool: ... - def __le__(self, value: object, /) -> bool: ... - def __lt__(self, value: object, /) -> bool: ... - def __ne__(self, value: object, /) -> bool: ... - -@final -class _Statement: ... - -class Warning(Exception): ... - -if sys.version_info >= (3, 11): - @final - class Blob: - def close(self) -> None: ... - def read(self, length: int = -1, /) -> bytes: ... - def write(self, data: ReadableBuffer, /) -> None: ... - def tell(self) -> int: ... - # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END - def seek(self, offset: int, origin: int = 0, /) -> None: ... - def __len__(self) -> int: ... - def __enter__(self) -> Self: ... - def __exit__(self, type: object, val: object, tb: object, /) -> Literal[False]: ... - def __getitem__(self, key: SupportsIndex | slice, /) -> int: ... - def __setitem__(self, key: SupportsIndex | slice, value: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi index 0c1e484bb07e..383f0f7eb8bd 100644 --- a/mypy/typeshed/stdlib/sre_constants.pyi +++ b/mypy/typeshed/stdlib/sre_constants.pyi @@ -1,4 +1,5 @@ import sys +from re import error as error from typing import Any from typing_extensions import Self @@ -6,14 +7,6 @@ MAXGROUPS: int MAGIC: int -class error(Exception): - msg: str - pattern: str | bytes | None - pos: int | None - lineno: int - colno: int - def __init__(self, msg: str, pattern: str | bytes | None = None, pos: int | None = None) -> None: ... - class _NamedIntConstant(int): name: Any def __new__(cls, value: int, name: str) -> Self: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 81c68c69ec4e..1d97c02acc5e 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -1,18 +1,51 @@ import enum import socket import sys +from _ssl import ( + _DEFAULT_CIPHERS as _DEFAULT_CIPHERS, + _OPENSSL_API_VERSION as _OPENSSL_API_VERSION, + HAS_ALPN as HAS_ALPN, + HAS_ECDH as HAS_ECDH, + HAS_NPN as HAS_NPN, + HAS_SNI as HAS_SNI, + OPENSSL_VERSION as OPENSSL_VERSION, + OPENSSL_VERSION_INFO as OPENSSL_VERSION_INFO, + OPENSSL_VERSION_NUMBER as OPENSSL_VERSION_NUMBER, + HAS_SSLv2 as HAS_SSLv2, + HAS_SSLv3 as HAS_SSLv3, + HAS_TLSv1 as HAS_TLSv1, + HAS_TLSv1_1 as HAS_TLSv1_1, + HAS_TLSv1_2 as HAS_TLSv1_2, + HAS_TLSv1_3 as HAS_TLSv1_3, + MemoryBIO as MemoryBIO, + RAND_add as RAND_add, + RAND_bytes as RAND_bytes, + RAND_status as RAND_status, + SSLSession as SSLSession, + _PasswordType as _PasswordType, # typeshed only, but re-export for other type stubs to use + _SSLContext, +) from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Callable, Iterable -from typing import Any, Literal, NamedTuple, TypedDict, final, overload +from typing import Any, Literal, NamedTuple, TypedDict, overload from typing_extensions import Never, Self, TypeAlias +if sys.version_info >= (3, 13): + from _ssl import HAS_PSK as HAS_PSK + +if sys.version_info < (3, 12): + from _ssl import RAND_pseudo_bytes as RAND_pseudo_bytes + +if sys.version_info < (3, 10): + from _ssl import RAND_egd as RAND_egd + +if sys.platform == "win32": + from _ssl import enum_certificates as enum_certificates, enum_crls as enum_crls + _PCTRTT: TypeAlias = tuple[tuple[str, str], ...] _PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] _PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT] _PeerCertRetType: TypeAlias = _PeerCertRetDictType | bytes | None -_EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]] -_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray - _SrvnmeCbType: TypeAlias = Callable[[SSLSocket | SSLObject, str | None, SSLSocket], int | None] socket_error = OSError @@ -98,15 +131,6 @@ else: _create_default_https_context: Callable[..., SSLContext] -def RAND_bytes(n: int, /) -> bytes: ... - -if sys.version_info < (3, 12): - def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ... - -def RAND_status() -> bool: ... -def RAND_egd(path: str) -> None: ... -def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ... - if sys.version_info < (3, 12): def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ... @@ -133,10 +157,6 @@ class DefaultVerifyPaths(NamedTuple): def get_default_verify_paths() -> DefaultVerifyPaths: ... -if sys.platform == "win32": - def enum_certificates(store_name: str) -> _EnumRetType: ... - def enum_crls(store_name: str) -> _EnumRetType: ... - class VerifyMode(enum.IntEnum): CERT_NONE = 0 CERT_OPTIONAL = 1 @@ -229,21 +249,8 @@ if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: Options HAS_NEVER_CHECK_COMMON_NAME: bool -HAS_SSLv2: bool -HAS_SSLv3: bool -HAS_TLSv1: bool -HAS_TLSv1_1: bool -HAS_TLSv1_2: bool -HAS_TLSv1_3: bool -HAS_ALPN: bool -HAS_ECDH: bool -HAS_SNI: bool -HAS_NPN: bool -CHANNEL_BINDING_TYPES: list[str] -OPENSSL_VERSION: str -OPENSSL_VERSION_INFO: tuple[int, int, int, int, int] -OPENSSL_VERSION_NUMBER: int +CHANNEL_BINDING_TYPES: list[str] class AlertDescription(enum.IntEnum): ALERT_DESCRIPTION_ACCESS_DENIED = 49 @@ -379,17 +386,15 @@ class TLSVersion(enum.IntEnum): TLSv1_2 = 771 TLSv1_3 = 772 -class SSLContext: - check_hostname: bool +class SSLContext(_SSLContext): options: Options verify_flags: VerifyFlags verify_mode: VerifyMode @property - def protocol(self) -> _SSLMethod: ... + def protocol(self) -> _SSLMethod: ... # type: ignore[override] hostname_checks_common_name: bool maximum_version: TLSVersion minimum_version: TLSVersion - sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None # The following two attributes have class-level defaults. # However, the docs explicitly state that it's OK to override these attributes on instances, # so making these ClassVars wouldn't be appropriate @@ -406,10 +411,6 @@ class SSLContext: else: def __new__(cls, protocol: int = ..., *args: Any, **kwargs: Any) -> Self: ... - def cert_store_stats(self) -> dict[str, int]: ... - def load_cert_chain( - self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None - ) -> None: ... def load_default_certs(self, purpose: Purpose = ...) -> None: ... def load_verify_locations( self, @@ -448,7 +449,6 @@ class SSLContext: server_hostname: str | bytes | None = None, session: SSLSession | None = None, ) -> SSLObject: ... - def session_stats(self) -> dict[str, int]: ... class SSLObject: context: SSLContext @@ -483,28 +483,6 @@ class SSLObject: def get_verified_chain(self) -> list[bytes]: ... def get_unverified_chain(self) -> list[bytes]: ... -@final -class MemoryBIO: - pending: int - eof: bool - def read(self, size: int = -1, /) -> bytes: ... - def write(self, b: ReadableBuffer, /) -> int: ... - def write_eof(self) -> None: ... - -@final -class SSLSession: - @property - def has_ticket(self) -> bool: ... - @property - def id(self) -> bytes: ... - @property - def ticket_lifetime_hint(self) -> int: ... - @property - def time(self) -> int: ... - @property - def timeout(self) -> int: ... - def __eq__(self, value: object, /) -> bool: ... - class SSLErrorNumber(enum.IntEnum): SSL_ERROR_EOF = 8 SSL_ERROR_INVALID_ERROR_CODE = 10 diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index e46903bf610f..4d9ede57ea99 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -103,12 +103,10 @@ PAX_NAME_FIELDS: set[str] ENCODING: str -_FileCreationModes: TypeAlias = Literal["a", "w", "x"] - @overload def open( name: StrOrBytesPath | None = None, - mode: str = "r", + mode: Literal["r", "r:*", "r:", "r:gz", "r:bz2", "r:xz"] = "r", fileobj: IO[bytes] | None = None, bufsize: int = 10240, *, @@ -121,16 +119,124 @@ def open( pax_headers: Mapping[str, str] | None = ..., debug: int | None = ..., errorlevel: int | None = ..., - compresslevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x", "x:", "a", "a:", "w", "w:"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: Literal["x", "x:", "a", "a:", "w", "w:"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x:xz", "w:xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., ) -> TarFile: ... @overload def open( name: StrOrBytesPath | None = None, - mode: _FileCreationModes = ..., + *, + mode: Literal["x:xz", "w:xz"], fileobj: _Fileobj | None = None, bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., +) -> TarFile: ... + +# TODO: Temporary fallback for modes containing pipe characters. These don't +# work with mypy 1.10, but this should be fixed with mypy 1.11. +# https://github.com/python/typeshed/issues/12182 +@overload +def open( + name: StrOrBytesPath | None = None, *, + mode: str, + fileobj: IO[bytes] | None = None, + bufsize: int = 10240, format: int | None = ..., tarinfo: type[TarInfo] | None = ..., dereference: bool | None = ..., @@ -140,7 +246,6 @@ def open( pax_headers: Mapping[str, str] | None = ..., debug: int | None = ..., errorlevel: int | None = ..., - compresslevel: int | None = ..., preset: int | None = ..., ) -> TarFile: ... diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 3d2a93865df8..e1c8fedee55c 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -133,7 +133,7 @@ class Untokenizer: def untokenize(iterable: Iterable[_Token]) -> Any: ... def detect_encoding(readline: Callable[[], bytes | bytearray]) -> tuple[str, Sequence[bytes]]: ... def tokenize(readline: Callable[[], bytes | bytearray]) -> Generator[TokenInfo, None, None]: ... -def generate_tokens(readline: Callable[[], str]) -> Generator[TokenInfo, None, None]: ... # undocumented +def generate_tokens(readline: Callable[[], str]) -> Generator[TokenInfo, None, None]: ... def open(filename: FileDescriptorOrPath) -> TextIO: ... def group(*choices: str) -> str: ... # undocumented def any(*choices: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 075c0f4b9de8..1c4a59de66aa 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -231,7 +231,7 @@ class TracebackException: if sys.version_info >= (3, 11): def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ... -class FrameSummary(Iterable[Any]): +class FrameSummary: if sys.version_info >= (3, 11): def __init__( self, @@ -276,6 +276,8 @@ class FrameSummary(Iterable[Any]): def __getitem__(self, pos: Literal[3]) -> str | None: ... @overload def __getitem__(self, pos: int) -> Any: ... + @overload + def __getitem__(self, pos: slice) -> tuple[Any, ...]: ... def __iter__(self) -> Iterator[Any]: ... def __eq__(self, other: object) -> bool: ... def __len__(self) -> Literal[4]: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 0f6592a9883e..0c5a43617040 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -424,6 +424,8 @@ class MethodType: @property def __closure__(self) -> tuple[CellType, ...] | None: ... # inherited from the added function @property + def __code__(self) -> CodeType: ... # inherited from the added function + @property def __defaults__(self) -> tuple[Any, ...] | None: ... # inherited from the added function @property def __func__(self) -> Callable[..., Any]: ... diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 1cfd38f540a4..193a4123c395 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -363,7 +363,7 @@ class _patcher: patch: _patcher -class MagicMixin: +class MagicMixin(Base): def __init__(self, *args: Any, **kw: Any) -> None: ... class NonCallableMagicMock(MagicMixin, NonCallableMock): ... @@ -393,7 +393,7 @@ class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): # But, `NonCallableMock` super-class has the better version. def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... -class MagicProxy: +class MagicProxy(Base): name: str parent: Any def __init__(self, name: str, parent: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/suite.pyi b/mypy/typeshed/stdlib/unittest/suite.pyi index c10cbc75d7fd..ff583d0766a0 100644 --- a/mypy/typeshed/stdlib/unittest/suite.pyi +++ b/mypy/typeshed/stdlib/unittest/suite.pyi @@ -5,7 +5,7 @@ from typing_extensions import TypeAlias _TestType: TypeAlias = unittest.case.TestCase | TestSuite -class BaseTestSuite(Iterable[_TestType]): +class BaseTestSuite: _tests: list[unittest.case.TestCase] _removed_tests: int def __init__(self, tests: Iterable[_TestType] = ()) -> None: ... diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index aaba7ffc98d9..853caf3e8abb 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -1,19 +1,14 @@ import sys from _typeshed import SupportsKeysAndGetItem -from _weakref import ( - CallableProxyType as CallableProxyType, - ProxyType as ProxyType, - ReferenceType as ReferenceType, - getweakrefcount as getweakrefcount, - getweakrefs as getweakrefs, - proxy as proxy, - ref as ref, -) +from _weakref import getweakrefcount as getweakrefcount, getweakrefs as getweakrefs, proxy as proxy from _weakrefset import WeakSet as WeakSet from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping -from typing import Any, Generic, TypeVar, overload +from typing import Any, Generic, TypeVar, final, overload from typing_extensions import ParamSpec, Self +if sys.version_info >= (3, 9): + from types import GenericAlias + __all__ = [ "ref", "proxy", @@ -40,11 +35,39 @@ _P = ParamSpec("_P") ProxyTypes: tuple[type[Any], ...] +# These classes are implemented in C and imported from _weakref at runtime. However, +# they consider themselves to live in the weakref module for sys.version_info >= (3, 11), +# so defining their stubs here means we match their __module__ value. +# Prior to 3.11 they did not declare a module for themselves and ended up looking like they +# came from the builtin module at runtime, which was just wrong, and we won't attempt to +# duplicate that. + +@final +class CallableProxyType(Generic[_CallableT]): # "weakcallableproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + __call__: _CallableT + +@final +class ProxyType(Generic[_T]): # "weakproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + +class ReferenceType(Generic[_T]): # "weakref" + __callback__: Callable[[Self], Any] + def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... + def __call__(self) -> _T | None: ... + def __eq__(self, value: object, /) -> bool: ... + def __hash__(self) -> int: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +ref = ReferenceType + +# everything below here is implemented in weakref.py + class WeakMethod(ref[_CallableT]): - # `ref` is implemented in `C` so positional-only arguments are enforced, but not in `WeakMethod`. - def __new__( # pyright: ignore[reportInconsistentConstructor] - cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None - ) -> Self: ... + def __new__(cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None) -> Self: ... def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... @@ -103,8 +126,8 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): class KeyedRef(ref[_T], Generic[_KT, _T]): key: _KT - def __new__(type, ob: _T, callback: Callable[[_T], Any], key: _KT) -> Self: ... - def __init__(self, ob: _T, callback: Callable[[_T], Any], key: _KT) -> None: ... + def __new__(type, ob: _T, callback: Callable[[Self], Any], key: _KT) -> Self: ... + def __init__(self, ob: _T, callback: Callable[[Self], Any], key: _KT) -> None: ... class WeakKeyDictionary(MutableMapping[_KT, _VT]): @overload diff --git a/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi b/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi index 73f3758c61ec..d9b7ea536999 100644 --- a/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi @@ -1 +1,7 @@ from pyexpat import * + +# This is actually implemented in the C module pyexpat, but considers itself to live here. +class ExpatError(Exception): + code: int + lineno: int + offset: int diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index d254102acc55..5899d1d72a38 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -200,7 +200,7 @@ def dumps( allow_none: bool = False, ) -> str: ... def loads( - data: str, use_datetime: bool = False, use_builtin_types: bool = False + data: str | ReadableBuffer, use_datetime: bool = False, use_builtin_types: bool = False ) -> tuple[tuple[_Marshallable, ...], str | None]: ... def gzip_encode(data: ReadableBuffer) -> bytes: ... # undocumented def gzip_decode(data: ReadableBuffer, max_decode: int = 20971520) -> bytes: ... # undocumented diff --git a/mypy/typeshed/stdlib/xmlrpc/server.pyi b/mypy/typeshed/stdlib/xmlrpc/server.pyi index 8ca3a4d1a33c..5f497aa7190e 100644 --- a/mypy/typeshed/stdlib/xmlrpc/server.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi @@ -1,6 +1,7 @@ import http.server import pydoc import socketserver +from _typeshed import ReadableBuffer from collections.abc import Callable, Iterable, Mapping from re import Pattern from typing import Any, ClassVar, Protocol @@ -48,8 +49,8 @@ class SimpleXMLRPCDispatcher: # undocumented def register_multicall_functions(self) -> None: ... def _marshaled_dispatch( self, - data: str, - dispatch_method: Callable[[str | None, tuple[_Marshallable, ...]], Fault | tuple[_Marshallable, ...]] | None = None, + data: str | ReadableBuffer, + dispatch_method: Callable[[str, tuple[_Marshallable, ...]], Fault | tuple[_Marshallable, ...]] | None = None, path: Any | None = None, ) -> str: ... # undocumented def system_listMethods(self) -> list[str]: ... # undocumented diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index 77930ac79dd5..cc483afad9ff 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -1,38 +1,35 @@ -from _typeshed import StrPath -from collections.abc import Iterable, Sequence +import sys +from collections.abc import Iterable from datetime import datetime, timedelta, tzinfo -from typing import Any, Protocol from typing_extensions import Self -__all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] +# TODO: remove this version check +# In theory we shouldn't need this version check. Pyright complains about the imports +# from zoneinfo.* when run on 3.8 and 3.7 without this. Updates to typeshed's +# pyright test script are probably needed, see #11189 +if sys.version_info >= (3, 9): + from zoneinfo._common import ZoneInfoNotFoundError as ZoneInfoNotFoundError, _IOBytes + from zoneinfo._tzpath import ( + TZPATH as TZPATH, + InvalidTZPathWarning as InvalidTZPathWarning, + available_timezones as available_timezones, + reset_tzpath as reset_tzpath, + ) -class _IOBytes(Protocol): - def read(self, size: int, /) -> bytes: ... - def seek(self, size: int, whence: int = ..., /) -> Any: ... + __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] -class ZoneInfo(tzinfo): - @property - def key(self) -> str: ... - def __init__(self, key: str) -> None: ... - @classmethod - def no_cache(cls, key: str) -> Self: ... - @classmethod - def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... - @classmethod - def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... - def tzname(self, dt: datetime | None, /) -> str | None: ... - def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... - def dst(self, dt: datetime | None, /) -> timedelta | None: ... + class ZoneInfo(tzinfo): + @property + def key(self) -> str: ... + def __init__(self, key: str) -> None: ... + @classmethod + def no_cache(cls, key: str) -> Self: ... + @classmethod + def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... + @classmethod + def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... + def tzname(self, dt: datetime | None, /) -> str | None: ... + def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... + def dst(self, dt: datetime | None, /) -> timedelta | None: ... -# Note: Both here and in clear_cache, the types allow the use of `str` where -# a sequence of strings is required. This should be remedied if a solution -# to this typing bug is found: https://github.com/python/typing/issues/256 -def reset_tzpath(to: Sequence[StrPath] | None = None) -> None: ... -def available_timezones() -> set[str]: ... - -TZPATH: tuple[str, ...] - -class ZoneInfoNotFoundError(KeyError): ... -class InvalidTZPathWarning(RuntimeWarning): ... - -def __dir__() -> list[str]: ... + def __dir__() -> list[str]: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/_common.pyi b/mypy/typeshed/stdlib/zoneinfo/_common.pyi new file mode 100644 index 000000000000..a2f29f2d14f0 --- /dev/null +++ b/mypy/typeshed/stdlib/zoneinfo/_common.pyi @@ -0,0 +1,13 @@ +import io +from typing import Any, Protocol + +class _IOBytes(Protocol): + def read(self, size: int, /) -> bytes: ... + def seek(self, size: int, whence: int = ..., /) -> Any: ... + +def load_tzdata(key: str) -> io.BufferedReader: ... +def load_data( + fobj: _IOBytes, +) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[str, ...], bytes | None]: ... + +class ZoneInfoNotFoundError(KeyError): ... diff --git a/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi b/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi new file mode 100644 index 000000000000..0ef78d03e5f4 --- /dev/null +++ b/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi @@ -0,0 +1,13 @@ +from _typeshed import StrPath +from collections.abc import Sequence + +# Note: Both here and in clear_cache, the types allow the use of `str` where +# a sequence of strings is required. This should be remedied if a solution +# to this typing bug is found: https://github.com/python/typing/issues/256 +def reset_tzpath(to: Sequence[StrPath] | None = None) -> None: ... +def find_tzfile(key: str) -> str | None: ... +def available_timezones() -> set[str]: ... + +TZPATH: tuple[str, ...] + +class InvalidTZPathWarning(RuntimeWarning): ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 174ba8e98c3f..0dbd18ef188f 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -283,7 +283,7 @@ f.write('x') f.write(b'x') f.foobar() [out] -_program.py:3: error: Argument 1 to "write" of "TextIOBase" has incompatible type "bytes"; expected "str" +_program.py:3: error: Argument 1 to "write" of "_TextIOBase" has incompatible type "bytes"; expected "str" _program.py:4: error: "TextIOWrapper[_WrappedBuffer]" has no attribute "foobar" [case testOpenReturnTypeInference] @@ -293,9 +293,9 @@ reveal_type(open('x', 'rb')) mode = 'rb' reveal_type(open('x', mode)) [out] -_program.py:1: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_program.py:2: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_program.py:3: note: Revealed type is "io.BufferedReader" +_program.py:1: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:2: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:3: note: Revealed type is "_io.BufferedReader" _program.py:5: note: Revealed type is "typing.IO[Any]" [case testOpenReturnTypeInferenceSpecialCases] @@ -304,8 +304,8 @@ reveal_type(open(file='x', mode='rb')) mode = 'rb' reveal_type(open(mode=mode, file='r')) [out] -_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "io.BufferedReader" -_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "io.BufferedReader" +_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "_io.BufferedReader" +_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "_io.BufferedReader" _testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInference] @@ -317,9 +317,9 @@ reveal_type(p.open('rb')) mode = 'rb' reveal_type(p.open(mode)) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_program.py:5: note: Revealed type is "io.BufferedReader" +_program.py:3: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:5: note: Revealed type is "_io.BufferedReader" _program.py:7: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInferenceSpecialCases] @@ -330,8 +330,8 @@ reveal_type(p.open(errors='replace', mode='r')) mode = 'r' reveal_type(p.open(mode=mode, errors='replace')) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" +_program.py:3: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" _program.py:6: note: Revealed type is "typing.IO[Any]" [case testGenericPatterns] From e1d09d3a1a75a2af6bd6406b7e06288b281c3364 Mon Sep 17 00:00:00 2001 From: Ihor <31508183+nautics889@users.noreply.github.com> Date: Thu, 17 Oct 2024 09:05:34 +0300 Subject: [PATCH 0833/1617] Minor refactoring of dict literal usage (#16837) --- mypy/dmypy_util.py | 3 +-- mypy/gclogger.py | 13 +++++++------ mypy/server/update.py | 3 +-- mypy/types.py | 8 +++++--- mypyc/codegen/emitclass.py | 3 +-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 0baff863b3c3..9b21d78ce599 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -104,8 +104,7 @@ def truncate(self, size: int | None = 0) -> int: raise io.UnsupportedOperation def write(self, output: str) -> int: - resp: dict[str, Any] = {} - resp[self.output_key] = output + resp: dict[str, Any] = {self.output_key: output} send(self.server, resp) return len(output) diff --git a/mypy/gclogger.py b/mypy/gclogger.py index 75f754ddf4d5..d111e609223c 100644 --- a/mypy/gclogger.py +++ b/mypy/gclogger.py @@ -38,10 +38,11 @@ def __exit__(self, *args: object) -> None: def get_stats(self) -> Mapping[str, float]: end_time = time.time() - result = {} - result["gc_time"] = self.gc_time - result["gc_calls"] = self.gc_calls - result["gc_collected"] = self.gc_collected - result["gc_uncollectable"] = self.gc_uncollectable - result["build_time"] = end_time - self.start_time + result = { + "gc_time": self.gc_time, + "gc_calls": self.gc_calls, + "gc_collected": self.gc_collected, + "gc_uncollectable": self.gc_uncollectable, + "build_time": end_time - self.start_time, + } return result diff --git a/mypy/server/update.py b/mypy/server/update.py index 0cc7a2229514..6bf8c8d07c2d 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -1059,8 +1059,7 @@ def find_symbol_tables_recursive(prefix: str, symbols: SymbolTable) -> dict[str, Returns a dictionary from full name to corresponding symbol table. """ - result = {} - result[prefix] = symbols + result = {prefix: symbols} for name, node in symbols.items(): if isinstance(node.node, TypeInfo) and node.node.fullname.startswith(prefix + "."): more = find_symbol_tables_recursive(prefix + "." + name, node.node.names) diff --git a/mypy/types.py b/mypy/types.py index dff7e2c0c829..897e19d6ee19 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1493,9 +1493,11 @@ def serialize(self) -> JsonDict | str: type_ref = self.type.fullname if not self.args and not self.last_known_value: return type_ref - data: JsonDict = {".class": "Instance"} - data["type_ref"] = type_ref - data["args"] = [arg.serialize() for arg in self.args] + data: JsonDict = { + ".class": "Instance", + "type_ref": type_ref, + "args": [arg.serialize() for arg in self.args], + } if self.last_known_value is not None: data["last_known_value"] = self.last_known_value.serialize() data["extra_attrs"] = self.extra_attrs.serialize() if self.extra_attrs else None diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 3ab6932546a6..d1a9ad3bace1 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -213,8 +213,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: methods_name = f"{name_prefix}_methods" vtable_setup_name = f"{name_prefix}_trait_vtable_setup" - fields: dict[str, str] = {} - fields["tp_name"] = f'"{name}"' + fields: dict[str, str] = {"tp_name": f'"{name}"'} generate_full = not cl.is_trait and not cl.builtin_base needs_getseters = cl.needs_getseters or not cl.is_generated or cl.has_dict From dc352e90852bec0a9cbea92d00589c547f53f779 Mon Sep 17 00:00:00 2001 From: jianghuyiyuan Date: Thu, 17 Oct 2024 15:21:42 +0800 Subject: [PATCH 0834/1617] chore: fix some comments (#17615) --- mypy/checker.py | 2 +- mypy/main.py | 2 +- mypy/server/deps.py | 2 +- mypy/util.py | 2 +- mypyc/external/googletest/src/gtest.cc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c646ebf3736b..74890aa86cda 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5575,7 +5575,7 @@ def partition_by_callable( if isinstance(typ, TypeVarType): # We could do better probably? - # Refine the the type variable's bound as our type in the case that + # Refine the type variable's bound as our type in the case that # callable() is true. This unfortunately loses the information that # the type is a type variable in that branch. # This matches what is done for isinstance, but it may be possible to diff --git a/mypy/main.py b/mypy/main.py index 3f25ced16106..5991ddd9e62f 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1479,7 +1479,7 @@ def process_package_roots( root = "" package_root.append(root) options.package_root = package_root - # Pass the package root on the the filesystem cache. + # Pass the package root on the filesystem cache. fscache.set_package_root(package_root) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 9ed2d4549629..6376600ffc0c 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -56,7 +56,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a * 'mod.Cls' represents each method in class 'mod.Cls' + the top-level of the module 'mod'. (To simplify the implementation, there is no location that only includes the body of a class without the entire surrounding module top level.) -* Trigger '<...>' as a location is an indirect way of referring to to all +* Trigger '<...>' as a location is an indirect way of referring to all locations triggered by the trigger. These indirect locations keep the dependency map smaller and easier to manage. diff --git a/mypy/util.py b/mypy/util.py index 2eac2a86dfd0..3c550958a659 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -201,7 +201,7 @@ def trim_source_line(line: str, max_len: int, col: int, min_width: int) -> tuple A typical result looks like this: ...some_variable = function_to_call(one_arg, other_arg) or... - Return the trimmed string and the column offset to to adjust error location. + Return the trimmed string and the column offset to adjust error location. """ if max_len < 2 * min_width + 1: # In case the window is too tiny it is better to still show something. diff --git a/mypyc/external/googletest/src/gtest.cc b/mypyc/external/googletest/src/gtest.cc index d882ab2e36a1..4df3bd6b418a 100644 --- a/mypyc/external/googletest/src/gtest.cc +++ b/mypyc/external/googletest/src/gtest.cc @@ -1784,7 +1784,7 @@ std::string CodePointToUtf8(UInt32 code_point) { return str; } -// The following two functions only make sense if the the system +// The following two functions only make sense if the system // uses UTF-16 for wide string encoding. All supported systems // with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. From d1a7d09e1a30c74e3a197295f8a4e134e34316a1 Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Thu, 17 Oct 2024 10:28:09 +0300 Subject: [PATCH 0835/1617] Move some tests from cmdline.test (#5966 ) (#17565) Relates #5966. Below is the info what happened with the concrete test from `cmdline.test`: 1. `testErrorContextConfig` => check-flags.test (`testShowErrorContextFunction`) [duplicate] 2. `testNoConfigFile` => check-flags.test (`testNoConfigFile`) [move] 3. `testPerFileConfigSection` => check-flags.test (`testPerFileUntypedDefs`) [move] 4. `testIgnoreErrorsConfig` => check-flags.test (`testPerFileIgnoreErrors`) [move] 5. `testConfigFollowImportsNormal` => check-flags.test (`testFollowImportsNormal`) [move + modified] 6. `testConfigFollowImportsSilent` => check-flags (`testFollowImportsSilent`) [move + modified] 7. `testConfigFollowImportsSkip` => check-flags (`testFollowImportsSkip`) [move + modified] 8. `testConfigFollowImportsError` => check-flags.test (`testFollowImportsError`) [move + modified] 9. `testConfigFollowImportsSelective` => check-flags.test (`testFollowImportsSelective`) [move] 10. `testConfigSilentMissingImportsOff` => check-flags.test (`testSilentMissingImportsOff`) [move] 11. `testConfigSilentMissingImportsOn` => check-flags.test (`testSilentMissingImportsOn`) [move] 12. `testDisallowAnyGenericsBuiltinCollectionsPre39` => check-flags.test (`testDisallowAnyGenericsBuiltinTuplePre39`, `testDisallowAnyGenericsBuiltinListPre39`, `testDisallowAnyGenericsBuiltinSetPre39`, `testDisallowAnyGenericsBuiltinDictPre39`) [split] 13. `testDisallowAnyGenericsTypingCollections` => check-flags.test (`testDisallowAnyGenericsTupleWithNoTypeParamsGeneric`, `testDisallowAnyGenericsPlainList`, `testDisallowAnyGenericsPlainDict`, `testDisallowAnyGenericsPlainSet`) [split] 14. `testDisallowUntypedDefsAndGeneric` => check-flags.test (`testDisallowUntypedDefsAndGeneric`) [move] 15. `testParseError` => parse-errors.test (`testMissingBracket`) [move] 16. `testParseErrorAnnots` => check-fastparse.test (`testFasterParseTooManyArgumentsAnnotation`) [duplicate] 17. `testNotesOnlyResultInExitSuccess` => check-flags.test (`testNotesOnlyResultInExitSuccess`) [move] Let's compare test execution time. I've run `pytest -n 4 mypy/test/testcmdline.py` 3 times on my machine and calculated the average time. - Before: 130 tests, 1m 02s - After: 115 tests, 0m 55s Also, if it's possible to use fixture `FrozenSet` in `check-flags.test`, we'd be able to totally split items 12 and 13 from the above list. And `testMissingBracket` is skipped by pytest in the `parse-errors.test`-file, but, probably, this file is the best variant for it (not sure about it). --- test-data/unit/check-flags.test | 179 ++++++++++++++++++-- test-data/unit/cmdline.test | 275 +------------------------------ test-data/unit/parse-errors.test | 7 + 3 files changed, 176 insertions(+), 285 deletions(-) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 4f327a2f0edc..dd7bee3f7aec 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -79,6 +79,13 @@ async def g(x: int) -> Any: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] +[case testDisallowUntypedDefsAndGeneric] +# flags: --disallow-untyped-defs --disallow-any-generics +def get_tasks(self): + return 'whatever' +[out] +main:2: error: Function is missing a return type annotation + [case testDisallowUntypedDefsUntypedDecorator] # flags: --disallow-untyped-decorators def d(p): @@ -540,21 +547,30 @@ tmp/b.py:1: error: Unsupported operand types for + ("int" and "str") [case testFollowImportsNormal] # flags: --follow-imports=normal from mod import x -x + "" +x + 0 +x + "" # E: Unsupported operand types for + ("int" and "str") +import mod +mod.x + 0 +mod.x + "" # E: Unsupported operand types for + ("int" and "str") +mod.y # E: "object" has no attribute "y" +mod + 0 # E: Unsupported left operand type for + ("object") [file mod.py] -1 + "" +1 + "" # E: Unsupported operand types for + ("int" and "str") x = 0 -[out] -tmp/mod.py:1: error: Unsupported operand types for + ("int" and "str") -main:3: error: Unsupported operand types for + ("int" and "str") +x += "" # E: Unsupported operand types for + ("int" and "str") [case testFollowImportsSilent] # flags: --follow-imports=silent from mod import x x + "" # E: Unsupported operand types for + ("int" and "str") +import mod +mod.x + "" # E: Unsupported operand types for + ("int" and "str") +mod.y # E: "object" has no attribute "y" +mod + 0 # E: Unsupported left operand type for + ("object") [file mod.py] 1 + "" x = 0 +x += "" [case testFollowImportsSilentTypeIgnore] # flags: --warn-unused-ignores --follow-imports=silent @@ -565,20 +581,55 @@ x = 3 # type: ignore [case testFollowImportsSkip] # flags: --follow-imports=skip from mod import x +reveal_type(x) # N: Revealed type is "Any" x + "" +import mod +reveal_type(mod.x) # N: Revealed type is "Any" [file mod.py] this deliberate syntax error will not be reported -[out] [case testFollowImportsError] # flags: --follow-imports=error -from mod import x +from mod import x # E: Import of "mod" ignored \ + # N: (Using --follow-imports=error, module not passed on command line) x + "" +reveal_type(x) # N: Revealed type is "Any" +import mod +reveal_type(mod.x) # N: Revealed type is "Any" [file mod.py] deliberate syntax error -[out] -main:2: error: Import of "mod" ignored -main:2: note: (Using --follow-imports=error, module not passed on command line) + +[case testFollowImportsSelective] +# flags: --config-file tmp/mypy.ini +import normal +import silent +import skip +import error # E: Import of "error" ignored \ + # N: (Using --follow-imports=error, module not passed on command line) +reveal_type(normal.x) # N: Revealed type is "builtins.int" +reveal_type(silent.x) # N: Revealed type is "builtins.int" +reveal_type(skip) # N: Revealed type is "Any" +reveal_type(error) # N: Revealed type is "Any" +[file mypy.ini] +\[mypy] +\[mypy-normal] +follow_imports = normal +\[mypy-silent] +follow_imports = silent +\[mypy-skip] +follow_imports = skip +\[mypy-error] +follow_imports = error +[file normal.py] +x = 0 +x += '' # E: Unsupported operand types for + ("int" and "str") +[file silent.py] +x = 0 +x += '' +[file skip.py] +bla bla +[file error.py] +bla bla [case testIgnoreMissingImportsFalse] from mod import x @@ -591,6 +642,15 @@ main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missin from mod import x [out] +[case testNoConfigFile] +# flags: --config-file= +# type: ignore + +[file mypy.ini] +\[mypy] +warn_unused_ignores = True +[out] + [case testPerFileIncompleteDefsBasic] # flags: --config-file tmp/mypy.ini import standard, incomplete @@ -868,6 +928,16 @@ implicit_optional = true module = 'optional' strict_optional = true +[case testSilentMissingImportsOff] +-- ignore_missing_imports is False by default. +import missing # E: Cannot find implementation or library stub for module named "missing" \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +reveal_type(missing.x) # N: Revealed type is "Any" + +[case testSilentMissingImportsOn] +# flags: --ignore-missing-imports +import missing +reveal_type(missing.x) # N: Revealed type is "Any" [case testDisallowImplicitTypesIgnoreMissingTypes] # flags: --ignore-missing-imports --disallow-any-unimported @@ -1447,6 +1517,29 @@ class Queue(Generic[_T]): ... [builtins fixtures/async_await.pyi] [typing fixtures/typing-full.pyi] +[case testDisallowAnyGenericsBuiltinTuplePre39] +# flags: --disallow-any-generics --python-version 3.8 +s = tuple([1, 2, 3]) +def f(t: tuple) -> None: pass # E: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters +[builtins fixtures/tuple.pyi] + +[case testDisallowAnyGenericsBuiltinListPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = list([1, 2, 3]) +def f(t: list) -> None: pass # E: Implicit generic "Any". Use "typing.List" and specify generic parameters +[builtins fixtures/list.pyi] + +[case testDisallowAnyGenericsBuiltinSetPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = set({1, 2, 3}) +def f(s: set) -> None: pass # E: Implicit generic "Any". Use "typing.Set" and specify generic parameters +[builtins fixtures/set.pyi] + +[case testDisallowAnyGenericsBuiltinDictPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = dict([('a', 1)]) +def f(d: dict) -> None: pass # E: Implicit generic "Any". Use "typing.Dict" and specify generic parameters +[builtins fixtures/dict.pyi] [case testCheckDefaultAllowAnyGeneric] from typing import TypeVar, Callable @@ -1863,8 +1956,9 @@ x: Tuple = () # E: Missing type parameters for generic type "Tuple" # flags: --disallow-any-generics from typing import Tuple, List -def f(s: List[Tuple]) -> None: pass # E: Missing type parameters for generic type "Tuple" -def g(s: List[Tuple[str, str]]) -> None: pass # no error +def f(s: Tuple) -> None: pass # E: Missing type parameters for generic type "Tuple" +def g(s: List[Tuple]) -> None: pass # E: Missing type parameters for generic type "Tuple" +def h(s: List[Tuple[str, str]]) -> None: pass # no error [builtins fixtures/list.pyi] [case testDisallowAnyGenericsTypeType] @@ -1908,14 +2002,36 @@ x: A = ('a', 'b', 1) # E: Missing type parameters for generic type "A" from typing import List def f(l: List) -> None: pass # E: Missing type parameters for generic type "List" -def g(l: List[str]) -> None: pass # no error +def g(l: List[str]) -> None: pass def h(l: List[List]) -> None: pass # E: Missing type parameters for generic type "List" def i(l: List[List[List[List]]]) -> None: pass # E: Missing type parameters for generic type "List" +def j() -> List: pass # E: Missing type parameters for generic type "List" x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") y: List = [] # E: Missing type parameters for generic type "List" [builtins fixtures/list.pyi] +[case testDisallowAnyGenericsPlainDict] +# flags: --disallow-any-generics +from typing import List, Dict + +def f(d: Dict) -> None: pass # E: Missing type parameters for generic type "Dict" +def g(d: Dict[str, Dict]) -> None: pass # E: Missing type parameters for generic type "Dict" +def h(d: List[Dict]) -> None: pass # E: Missing type parameters for generic type "Dict" + +d: Dict = {} # E: Missing type parameters for generic type "Dict" +[builtins fixtures/dict.pyi] + +[case testDisallowAnyGenericsPlainSet] +# flags: --disallow-any-generics +from typing import Set + +def f(s: Set) -> None: pass # E: Missing type parameters for generic type "Set" +def g(s: Set[Set]) -> None: pass # E: Missing type parameters for generic type "Set" + +s: Set = set() # E: Missing type parameters for generic type "Set" +[builtins fixtures/set.pyi] + [case testDisallowAnyGenericsCustomGenericClass] # flags: --disallow-any-generics from typing import Generic, TypeVar, Any @@ -2162,6 +2278,38 @@ allow_untyped_defs = True allow_untyped_calls = True disable_error_code = var-annotated +[case testPerFileIgnoreErrors] +# flags: --config-file tmp/mypy.ini +import foo, bar +[file foo.py] +x: str = 5 +[file bar.py] +x: str = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +[file mypy.ini] +\[mypy] +\[mypy-foo] +ignore_errors = True + +[case testPerFileUntypedDefs] +# flags: --config-file tmp/mypy.ini +import x, y, z +[file x.py] +def f(a): ... # E: Function is missing a type annotation +def g(a: int) -> int: return f(a) +[file y.py] +def f(a): pass +def g(a: int) -> int: return f(a) +[file z.py] +def f(a): pass # E: Function is missing a type annotation +def g(a: int) -> int: return f(a) # E: Call to untyped function "f" in typed context +[file mypy.ini] +\[mypy] +disallow_untyped_defs = True +\[mypy-y] +disallow_untyped_defs = False +\[mypy-z] +disallow_untyped_calls = True + [case testPerModuleErrorCodesOverride] # flags: --config-file tmp/mypy.ini import tests.foo @@ -2284,3 +2432,8 @@ class C(Generic[T]): ... A = Union[C, List] # OK [builtins fixtures/list.pyi] + +[case testNotesOnlyResultInExitSuccess] +-- check_untyped_defs is False by default. +def f(): + x: int = "no" # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 38ea83cdbcf4..2bab19e0d42f 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -153,18 +153,6 @@ FLAG = False if not FLAG: x = "unreachable" -[case testErrorContextConfig] -# cmd: mypy main.py -[file mypy.ini] -\[mypy] -show_error_context=True -[file main.py] -def f() -> None: - 0 + "" -[out] -main.py: note: In function "f": -main.py:2: error: Unsupported operand types for + ("int" and "str") - [case testAltConfigFile] # cmd: mypy --config-file config.ini main.py [file config.ini] @@ -176,43 +164,6 @@ FLAG = False if not FLAG: x = "unreachable" -[case testNoConfigFile] -# cmd: mypy main.py --config-file= -[file mypy.ini] -\[mypy] -warn_unused_ignores = True -[file main.py] -# type: ignore - -[case testPerFileConfigSection] -# cmd: mypy x.py y.py z.py -[file mypy.ini] -\[mypy] -disallow_untyped_defs = True -\[mypy-y] -disallow_untyped_defs = False -\[mypy-z] -disallow_untyped_calls = True -[file x.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[file y.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[file z.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[out] -z.py:1: error: Function is missing a type annotation -z.py:4: error: Call to untyped function "f" in typed context -x.py:1: error: Function is missing a type annotation - [case testPerFileConfigSectionMultipleMatchesDisallowed] # cmd: mypy xx.py xy.py yx.py yy.py [file mypy.ini] @@ -326,43 +277,6 @@ file.py:1: error: Cannot find implementation or library stub for module named "n file.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports file.py:6: error: Argument 1 to "foo" has incompatible type "str"; expected "int" -[case testIgnoreErrorsConfig] -# cmd: mypy x.py y.py -[file mypy.ini] -\[mypy] -\[mypy-x] -ignore_errors = True -[file x.py] -x: str = 5 -[file y.py] -x: str = 5 -[out] -y.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") - -[case testConfigFollowImportsNormal] -# cmd: mypy main.py -[file main.py] -from a import x -x + 0 -x + '' # E -import a -a.x + 0 -a.x + '' # E -a.y # E -a + 0 # E -[file mypy.ini] -\[mypy] -follow_imports = normal -[file a.py] -x = 0 -x += '' # Error reported here -[out] -a.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:3: error: Unsupported operand types for + ("int" and "str") -main.py:6: error: Unsupported operand types for + ("int" and "str") -main.py:7: error: Module has no attribute "y" -main.py:8: error: Unsupported operand types for + (Module and "int") - [case testConfigFollowImportsSysPath] # cmd: mypy main.py [file main.py] @@ -389,102 +303,6 @@ main.py:6: error: Unsupported operand types for + ("int" and "str") main.py:7: error: Module has no attribute "y" main.py:8: error: Unsupported operand types for + (Module and "int") -[case testConfigFollowImportsSilent] -# cmd: mypy main.py -[file main.py] -from a import x -x + '' -import a -a.x + '' -a.y -a + 0 -[file mypy.ini] -\[mypy] -follow_imports = silent -[file a.py] -x = 0 -x += '' # No error reported -[out] -main.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:4: error: Unsupported operand types for + ("int" and "str") -main.py:5: error: Module has no attribute "y" -main.py:6: error: Unsupported operand types for + (Module and "int") - -[case testConfigFollowImportsSkip] -# cmd: mypy main.py -[file main.py] -from a import x -reveal_type(x) # Expect Any -import a -reveal_type(a.x) # Expect Any -[file mypy.ini] -\[mypy] -follow_imports = skip -[file a.py] -/ # No error reported -[out] -main.py:2: note: Revealed type is "Any" -main.py:4: note: Revealed type is "Any" -== Return code: 0 - -[case testConfigFollowImportsError] -# cmd: mypy main.py -[file main.py] -from a import x # Error reported here -reveal_type(x) # Expect Any -import a -reveal_type(a.x) # Expect Any -[file mypy.ini] -\[mypy] -follow_imports = error -[file a.py] -/ # No error reported -[out] -main.py:1: error: Import of "a" ignored -main.py:1: note: (Using --follow-imports=error, module not passed on command line) -main.py:2: note: Revealed type is "Any" -main.py:4: note: Revealed type is "Any" - -[case testConfigFollowImportsSelective] -# cmd: mypy main.py -[file mypy.ini] -\[mypy] -\[mypy-normal] -follow_imports = normal -\[mypy-silent] -follow_imports = silent -\[mypy-skip] -follow_imports = skip -\[mypy-error] -follow_imports = error -[file main.py] -import normal -import silent -import skip -import error -reveal_type(normal.x) -reveal_type(silent.x) -reveal_type(skip) -reveal_type(error) -[file normal.py] -x = 0 -x += '' -[file silent.py] -x = 0 -x += '' -[file skip.py] -bla bla -[file error.py] -bla bla -[out] -normal.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:4: error: Import of "error" ignored -main.py:4: note: (Using --follow-imports=error, module not passed on command line) -main.py:5: note: Revealed type is "builtins.int" -main.py:6: note: Revealed type is "builtins.int" -main.py:7: note: Revealed type is "Any" -main.py:8: note: Revealed type is "Any" - [case testConfigFollowImportsInvalid] # cmd: mypy main.py [file mypy.ini] @@ -495,31 +313,6 @@ follow_imports =True mypy.ini: [mypy]: follow_imports: invalid choice 'True' (choose from 'normal', 'silent', 'skip', 'error') == Return code: 0 -[case testConfigSilentMissingImportsOff] -# cmd: mypy main.py -[file main.py] -import missing # Expect error here -reveal_type(missing.x) # Expect Any -[file mypy.ini] -\[mypy] -ignore_missing_imports = False -[out] -main.py:1: error: Cannot find implementation or library stub for module named "missing" -main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main.py:2: note: Revealed type is "Any" - -[case testConfigSilentMissingImportsOn] -# cmd: mypy main.py -[file main.py] -import missing # No error here -reveal_type(missing.x) # Expect Any -[file mypy.ini] -\[mypy] -ignore_missing_imports = True -[out] -main.py:2: note: Revealed type is "Any" -== Return code: 0 - [case testFailedImportOnWrongCWD] # cmd: mypy main.py # cwd: main/subdir1/subdir2 @@ -683,21 +476,10 @@ int_pow.py:11: note: Revealed type is "Any" python_version = 3.8 \[mypy-m] disallow_any_generics = True - [file m.py] -s = tuple([1, 2, 3]) # no error - -def f(t: tuple) -> None: pass -def g() -> list: pass -def h(s: dict) -> None: pass -def i(s: set) -> None: pass def j(s: frozenset) -> None: pass [out] -m.py:3: error: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters -m.py:4: error: Implicit generic "Any". Use "typing.List" and specify generic parameters -m.py:5: error: Implicit generic "Any". Use "typing.Dict" and specify generic parameters -m.py:6: error: Implicit generic "Any". Use "typing.Set" and specify generic parameters -m.py:7: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters +m.py:1: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters [case testDisallowAnyGenericsTypingCollections] # cmd: mypy m.py @@ -705,21 +487,11 @@ m.py:7: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generi \[mypy] \[mypy-m] disallow_any_generics = True - [file m.py] -from typing import Tuple, List, Dict, Set, FrozenSet - -def f(t: Tuple) -> None: pass -def g() -> List: pass -def h(s: Dict) -> None: pass -def i(s: Set) -> None: pass +from typing import FrozenSet def j(s: FrozenSet) -> None: pass [out] -m.py:3: error: Missing type parameters for generic type "Tuple" -m.py:4: error: Missing type parameters for generic type "List" -m.py:5: error: Missing type parameters for generic type "Dict" -m.py:6: error: Missing type parameters for generic type "Set" -m.py:7: error: Missing type parameters for generic type "FrozenSet" +m.py:2: error: Missing type parameters for generic type "FrozenSet" [case testSectionInheritance] # cmd: mypy a @@ -756,18 +528,6 @@ ignore_errors = False a/b/c/d/e/__init__.py:2: error: Missing type parameters for generic type "List" a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "List[Any]" -[case testDisallowUntypedDefsAndGenerics] -# cmd: mypy a.py -[file mypy.ini] -\[mypy] -disallow_untyped_defs = True -disallow_any_generics = True -[file a.py] -def get_tasks(self): - return 'whatever' -[out] -a.py:1: error: Function is missing a return type annotation - [case testMissingFile] # cmd: mypy nope.py [out] @@ -775,26 +535,6 @@ mypy: can't read file 'nope.py': No such file or directory == Return code: 2 --' -[case testParseError] -# cmd: mypy a.py -[file a.py] -def foo( -[out] -a.py:1: error: unexpected EOF while parsing -== Return code: 2 -[out version>=3.10] -a.py:1: error: '(' was never closed -== Return code: 2 - -[case testParseErrorAnnots] -# cmd: mypy a.py -[file a.py] -def foo(x): - # type: (str, int) -> None - return -[out] -a.py:1: error: Type signature has too many arguments - [case testModulesAndPackages] # cmd: mypy --package p.a --package p.b --module c [file p/__init__.py] @@ -1489,15 +1229,6 @@ pass Warning: --new-type-inference flag is deprecated; new type inference algorithm is already enabled by default == Return code: 0 -[case testNotesOnlyResultInExitSuccess] -# cmd: mypy a.py -[file a.py] -def f(): - x: int = "no" -[out] -a.py:2: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs -== Return code: 0 - [case testCustomTypeshedDirFilePassedExplicitly] # cmd: mypy --custom-typeshed-dir dir m.py dir/stdlib/foo.pyi [file m.py] diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test index c6b1c00a6169..7b1078d3fa2f 100644 --- a/test-data/unit/parse-errors.test +++ b/test-data/unit/parse-errors.test @@ -151,6 +151,13 @@ x = 0 # type: A B #comment #7 [out] file:2: error: Syntax error in type comment "A B" +[case testMissingBracket] +def foo( +[out] +file:1: error: unexpected EOF while parsing +[out version>=3.10] +file:1: error: '(' was never closed + [case testInvalidSignatureInComment1] def f(): # type: x pass From 61ad5a47c0185549cb40b287aca8df1e5e862f0c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 17 Oct 2024 02:17:33 -0700 Subject: [PATCH 0836/1617] Add faster-cache extra, test in CI (#17978) Follow up to https://github.com/python/mypy/pull/17955 --- setup.py | 1 + tox.ini | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a50afde4ce6b..71124916a3e4 100644 --- a/setup.py +++ b/setup.py @@ -232,6 +232,7 @@ def run(self): "python2": "", "reports": "lxml", "install-types": "pip", + "faster-cache": "orjson", }, python_requires=">=3.8", include_package_data=True, diff --git a/tox.ini b/tox.ini index 7f89b7d4f7ef..a505950521fa 100644 --- a/tox.ini +++ b/tox.ini @@ -20,15 +20,18 @@ passenv = PROGRAMDATA PROGRAMFILES(X86) PYTEST_ADDOPTS -deps = -rtest-requirements.txt +deps = + -r test-requirements.txt + # This is a bit of a hack, but ensures the faster-cache path is tested in CI + orjson;python_version=='3.12' commands = python -m pytest {posargs} [testenv:dev] description = generate a DEV environment, that has all project libraries usedevelop = True deps = - -rtest-requirements.txt - -rdocs/requirements-docs.txt + -r test-requirements.txt + -r docs/requirements-docs.txt commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' @@ -38,7 +41,7 @@ commands = description = invoke sphinx-build to build the HTML docs passenv = VERIFY_MYPY_ERROR_CODES -deps = -rdocs/requirements-docs.txt +deps = -r docs/requirements-docs.txt commands = sphinx-build -n -d "{toxworkdir}/docs_doctree" docs/source "{toxworkdir}/docs_out" --color -W -bhtml {posargs} python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))' From 539513a5ff41954b0d0d5b0cd25b1cb03a10122b Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 17 Oct 2024 02:18:47 -0700 Subject: [PATCH 0837/1617] Fix iteration over union (when self type is involved) (#17976) Fixes https://github.com/python/mypy/issues/17945 --- mypy/checkexpr.py | 1 + test-data/unit/check-selftype.test | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 8d1833a772e2..b06aaa8f89f5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3814,6 +3814,7 @@ def check_method_call_by_name( is_operator=True, msg=self.msg, original_type=original_type, + self_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), ) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 9601852ef823..1a088fe05092 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2160,3 +2160,19 @@ class MyProtocol(Protocol): def test() -> None: ... value: MyProtocol = test + +[case testSelfTypeUnionIter] +from typing import Self, Iterator, Generic, TypeVar, Union + +T = TypeVar("T") + +class range(Generic[T]): + def __iter__(self) -> Self: ... + def __next__(self) -> T: ... + +class count: + def __iter__(self) -> Iterator[int]: ... + +def foo(x: Union[range[int], count]) -> None: + for item in x: + reveal_type(item) # N: Revealed type is "builtins.int" From 123eb919a806212438cfbf9a18284fb830bf3dba Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 18 Oct 2024 03:16:58 +0200 Subject: [PATCH 0838/1617] Update mypy_primer workflow (#17987) Update the mypy_primer workflow to use actions/upload-artifact@v4. https://github.com/actions/upload-artifact/tree/v4 --- .github/workflows/mypy_primer.yml | 37 +++++++++++++++++------ .github/workflows/mypy_primer_comment.yml | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 0c77d3a255d8..fee7e6d079f1 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -69,18 +69,35 @@ jobs: --output concise \ | tee diff_${{ matrix.shard-index }}.txt ) || [ $? -eq 1 ] - - name: Upload mypy_primer diff - uses: actions/upload-artifact@v3 - with: - name: mypy_primer_diffs - path: diff_${{ matrix.shard-index }}.txt - - if: ${{ matrix.shard-index }} == 0 + - if: ${{ matrix.shard-index == 0 }} name: Save PR number run: | echo ${{ github.event.pull_request.number }} | tee pr_number.txt - - if: ${{ matrix.shard-index }} == 0 - name: Upload PR number - uses: actions/upload-artifact@v3 + - if: ${{ matrix.shard-index == 0 }} + name: Upload mypy_primer diff + PR number + uses: actions/upload-artifact@v4 + with: + name: mypy_primer_diffs-${{ matrix.shard-index }} + path: | + diff_${{ matrix.shard-index }}.txt + pr_number.txt + - name: Upload mypy_primer diff + uses: actions/upload-artifact@v4 + if: ${{ matrix.shard-index != 0 }} + with: + name: mypy_primer_diffs-${{ matrix.shard-index }} + path: diff_${{ matrix.shard-index }}.txt + + join_artifacts: + name: Join artifacts + runs-on: ubuntu-latest + needs: [mypy_primer] + permissions: + contents: read + steps: + - name: Merge artifacts + uses: actions/upload-artifact/merge@v4 with: name: mypy_primer_diffs - path: pr_number.txt + pattern: mypy_primer_diffs-* + delete-merged: true diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 492e03aff16e..6e62d8c51713 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -18,7 +18,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download diffs - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const fs = require('fs'); From 5d9b1f92ec3078fe93a0ca46e0741c8ce19fe73e Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 18 Oct 2024 04:18:41 +0300 Subject: [PATCH 0839/1617] TypeGuard should be bool not Any when matching TypeVar (#17145) Fixes #17117 --- mypy/constraints.py | 8 -------- test-data/unit/check-typeguard.test | 9 +++++++++ test-data/unit/check-typeis.test | 9 +++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 49a2aea8fa05..5c815bf2af65 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1029,18 +1029,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard - elif template.type_guard is not None: - template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_guard is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) if template.type_is is not None and cactual.type_is is not None: template_ret_type = template.type_is cactual_ret_type = cactual.type_is - elif template.type_is is not None: - template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_is is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 27b88553fb43..e7a8eac4f043 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -87,6 +87,15 @@ def main(a: Tuple[T, ...]): reveal_type(a) # N: Revealed type is "Tuple[T`-1, T`-1]" [builtins fixtures/tuple.pyi] +[case testTypeGuardPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeGuard +T = TypeVar('T') +def is_str(x: object) -> TypeGuard[str]: ... +def main(f: Callable[[object], T]) -> T: ... +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeGuardNonOverlapping] from typing import List from typing_extensions import TypeGuard diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 6b96845504ab..2372f990fda1 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -104,6 +104,15 @@ def main(x: object, type_check_func: Callable[[object], TypeIs[T]]) -> T: reveal_type(main("a", is_str)) # N: Revealed type is "builtins.str" [builtins fixtures/exception.pyi] +[case testTypeIsPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeIs +T = TypeVar('T') +def is_str(x: object) -> TypeIs[str]: pass +def main(f: Callable[[object], T]) -> T: pass +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeIsUnionIn] from typing import Union from typing_extensions import TypeIs From 8150b513f2b298ee45a8ea02644aa7bc44786bd1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:53:47 -0700 Subject: [PATCH 0840/1617] Sync typeshed (#17988) Source commit: https://github.com/python/typeshed/commit/559ae9730ba3dab1305cdbaf2c29786ff38d740d --- mypy/typeshed/stdlib/_asyncio.pyi | 4 ++-- mypy/typeshed/stdlib/_csv.pyi | 3 ++- mypy/typeshed/stdlib/builtins.pyi | 5 ++++- mypy/typeshed/stdlib/csv.pyi | 11 +++++++++-- mypy/typeshed/stdlib/posixpath.pyi | 16 +++++++++++++--- mypy/typeshed/stdlib/unittest/runner.pyi | 3 ++- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi index 18920cd8a8a4..a259026615aa 100644 --- a/mypy/typeshed/stdlib/_asyncio.pyi +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -1,6 +1,6 @@ import sys from asyncio.events import AbstractEventLoop -from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable +from collections.abc import Awaitable, Callable, Coroutine, Generator from contextvars import Context from types import FrameType from typing import Any, Literal, TextIO, TypeVar @@ -13,7 +13,7 @@ _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _TaskYieldType: TypeAlias = Future[object] | None -class Future(Awaitable[_T], Iterable[_T]): +class Future(Awaitable[_T]): _state: str @property def _exception(self) -> BaseException | None: ... diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 6d1893cf8c16..0e206a63b260 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,3 +1,4 @@ +import csv import sys from _typeshed import SupportsWrite from collections.abc import Iterable, Iterator @@ -20,7 +21,7 @@ _QuotingType: TypeAlias = int class Error(Exception): ... -_DialectLike: TypeAlias = str | Dialect | type[Dialect] +_DialectLike: TypeAlias = str | Dialect | csv.Dialect | type[Dialect | csv.Dialect] class Dialect: delimiter: str diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index adfe1d83752b..eaa6695f7045 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -850,7 +850,10 @@ class slice: @overload def __new__(cls, start: Any, stop: Any, step: Any = ..., /) -> Self: ... def __eq__(self, value: object, /) -> bool: ... - __hash__: ClassVar[None] # type: ignore[assignment] + if sys.version_info >= (3, 12): + def __hash__(self) -> int: ... + else: + __hash__: ClassVar[None] # type: ignore[assignment] def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ... class tuple(Sequence[_T_co]): diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 89a019753f04..c5c7fea8fa25 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -4,7 +4,6 @@ from _csv import ( QUOTE_MINIMAL as QUOTE_MINIMAL, QUOTE_NONE as QUOTE_NONE, QUOTE_NONNUMERIC as QUOTE_NONNUMERIC, - Dialect as _Dialect, Error as Error, __version__ as __version__, _DialectLike, @@ -59,7 +58,15 @@ if sys.version_info < (3, 13): _T = TypeVar("_T") -class Dialect(_Dialect): +class Dialect: + delimiter: str + quotechar: str | None + escapechar: str | None + doublequote: bool + skipinitialspace: bool + lineterminator: str + quoting: _QuotingType + strict: bool def __init__(self) -> None: ... class excel(Dialect): ... diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index 31406f8df950..3313667f1781 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -77,7 +77,11 @@ pathsep: LiteralString defpath: LiteralString devnull: LiteralString -def abspath(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... +# Overloads are necessary to work around python/mypy#17952 & python/mypy#11880 +@overload +def abspath(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def abspath(path: AnyStr) -> AnyStr: ... @overload def basename(p: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -86,8 +90,14 @@ def basename(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... def dirname(p: PathLike[AnyStr]) -> AnyStr: ... @overload def dirname(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... -def expanduser(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... -def expandvars(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... +@overload +def expanduser(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def expanduser(path: AnyStr) -> AnyStr: ... +@overload +def expandvars(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def expandvars(path: AnyStr) -> AnyStr: ... @overload def normcase(s: PathLike[AnyStr]) -> AnyStr: ... @overload diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 46da85619d30..be546f42315e 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -13,13 +13,14 @@ class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ... # All methods used by unittest.runner.TextTestResult's stream class _TextTestStream(_SupportsWriteAndFlush, Protocol): - def writeln(self, arg: str | None = None) -> str: ... + def writeln(self, arg: str | None = None, /) -> str: ... # _WritelnDecorator should have all the same attrs as its stream param. # But that's not feasible to do Generically # We can expand the attributes if requested class _WritelnDecorator(_TextTestStream): def __init__(self, stream: _TextTestStream) -> None: ... + def writeln(self, arg: str | None = None) -> str: ... def __getattr__(self, attr: str) -> Any: ... # Any attribute from the stream type passed to __init__ # These attributes are prevented by __getattr__ stream: Never From df7cd88eded0702fc1b8bb225b286a514a442e37 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Oct 2024 11:01:16 +0100 Subject: [PATCH 0841/1617] Merge some pythoneval tests to speed them up (#17990) Each test has some non-trivial constant overhead, so merging tests will speed them up. Merge a few tests. The merged tests are old and unlikely to fail (but still marginally useful), so it isn't important if test failures are now a bit more difficult to investigate. --- test-data/unit/pythoneval.test | 177 +++++++++++++-------------------- 1 file changed, 68 insertions(+), 109 deletions(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 0dbd18ef188f..997cdc0d0ff0 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -11,138 +11,97 @@ print('hello, world') [out] hello, world -[case testReversed] +[case testMiscStdlibFeatures] +# Various legacy tests merged together to speed up test runtimes. + +def f(x: object) -> None: pass + +# testReversed from typing import Reversible -class A(Reversible): +class R(Reversible): def __iter__(self): return iter('oof') def __reversed__(self): return iter('foo') -print(list(reversed(range(5)))) -print(list(reversed([1,2,3]))) -print(list(reversed('abc'))) -print(list(reversed(A()))) -[out] --- Escape bracket at line beginning -\[4, 3, 2, 1, 0] -\[3, 2, 1] -\['c', 'b', 'a'] -\['f', 'o', 'o'] - -[case testIntAndFloatConversion] +f(list(reversed(range(5)))) +f(list(reversed([1,2,3]))) +f(list(reversed('abc'))) +f(list(reversed(R()))) + +# testIntAndFloatConversion from typing import SupportsInt, SupportsFloat class A(SupportsInt): def __int__(self): return 5 class B(SupportsFloat): def __float__(self): return 1.2 -print(int(1)) -print(int(6.2)) -print(int('3')) -print(int(b'4')) -print(int(A())) -print(float(-9)) -print(float(B())) -[out] -1 -6 -3 -4 -5 --9.0 -1.2 - -[case testAbs] +f(int(1)) +f(int(6.2)) +f(int('3')) +f(int(b'4')) +f(int(A())) +f(float(-9)) +f(float(B())) + +# testAbs from typing import SupportsAbs -class A(SupportsAbs[float]): +class Ab(SupportsAbs[float]): def __abs__(self) -> float: return 5.5 -print(abs(-1)) -print(abs(-1.2)) -print(abs(A())) -[out] -1 -1.2 -5.5 - -[case testAbs2] -n: int -f: float -n = abs(1) -abs(1) + 'x' # Error -f = abs(1.1) -abs(1.1) + 'x' # Error -[out] -_program.py:4: error: Unsupported operand types for + ("int" and "str") -_program.py:6: error: Unsupported operand types for + ("float" and "str") +f(abs(-1)) +f(abs(-1.2)) +f(abs(Ab())) -[case testRound] +# testRound from typing import SupportsRound -class A(SupportsRound): +class Ro(SupportsRound): def __round__(self, ndigits=0): return 'x%d' % ndigits -print(round(1.6)) -print(round(A())) -print(round(A(), 2)) -[out] -2 -x0 -x2 +f(round(1.6)) +f(round(Ro())) +f(round(Ro(), 2)) -[case testCallMethodViaTypeObject] -import typing -print(list.__add__([1, 2], [3, 4])) -[out] -\[1, 2, 3, 4] +# testCallMethodViaTypeObject +list.__add__([1, 2], [3, 4]) -[case testInheritedClassAttribute] +# testInheritedClassAttribute import typing -class A: +class AA: x = 1 - def f(self: typing.Optional["A"]) -> None: print('f') -class B(A): + def f(self: typing.Optional["AA"]) -> None: pass +class BB(AA): pass -B.f(None) -print(B.x) -[out] -f -1 - -[case testModuleAttributes] -import math -import typing -print(type(__spec__)) -print(math.__name__) -print(math.__spec__.name) -print(type(math.__dict__)) -print(type(math.__doc__ or '')) -print(type(math.__spec__).__name__) -print(math.__class__) -[out] - -math -math - - -ModuleSpec - +BB.f(None) +f(BB.x) -[case testSpecialAttributes] -import typing -class A: +# testSpecialAttributes +class Doc: """A docstring!""" -print(A().__doc__) -print(A().__class__) -[out] -A docstring! - +f(Doc().__doc__) +f(Doc().__class__) -[case testFunctionAttributes] -import typing -ord.__class__ -print(type(ord.__doc__ or '' + '')) -print(ord.__name__) -print(ord.__module__) +# testFunctionAttributes +f(ord.__class__) +f(type(ord.__doc__ or '' + '')) +f(ord.__name__) +f(ord.__module__) + +# testModuleAttributes +import math +f(type(__spec__)) +f(math.__name__) +f(math.__spec__.name) +f(type(math.__dict__)) +f(type(math.__doc__ or '')) +f(type(math.__spec__).__name__) +f(math.__class__) + +[case testAbs2] +n: int +f: float +n = abs(1) +abs(1) + 'x' # Error +f = abs(1.1) +abs(1.1) + 'x' # Error [out] - -ord -builtins +_program.py:4: error: Unsupported operand types for + ("int" and "str") +_program.py:6: error: Unsupported operand types for + ("float" and "str") [case testTypeAttributes] import typing From bd2aafc13686711d0db11d30efbf1d4f68718136 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Oct 2024 11:44:58 +0100 Subject: [PATCH 0842/1617] Fix type object with type var default in union context (#17991) Union type context wasn't handled previously, and it triggered false positives, but apparently only if a type object had type var defaults. Fixes #17942. --- mypy/checkexpr.py | 13 +++++++++++-- test-data/unit/check-typevar-defaults.test | 12 ++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index b06aaa8f89f5..08d7345452fb 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -399,8 +399,8 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: # TODO: always do this in type_object_type by passing the original context result.ret_type.line = e.line result.ret_type.column = e.column - if isinstance(get_proper_type(self.type_context[-1]), TypeType): - # This is the type in a Type[] expression, so substitute type + if is_type_type_context(self.type_context[-1]): + # This is the type in a type[] expression, so substitute type # variables with Any. result = erasetype.erase_typevars(result) elif isinstance(node, MypyFile): @@ -6617,3 +6617,12 @@ def get_partial_instance_type(t: Type | None) -> PartialType | None: if t is None or not isinstance(t, PartialType) or t.type is None: return None return t + + +def is_type_type_context(context: Type | None) -> bool: + context = get_proper_type(context) + if isinstance(context, TypeType): + return True + if isinstance(context, UnionType): + return any(is_type_type_context(item) for item in context.items) + return False diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 9ca67376da26..22e2594eb38b 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -717,3 +717,15 @@ def func_d3( reveal_type(c) # N: Revealed type is "__main__.B[__main__.A[builtins.dict[builtins.int, builtins.float]]]" reveal_type(d) # N: Revealed type is "__main__.B[builtins.int]" [builtins fixtures/dict.pyi] + +[case testTypeVarDefaultsAndTypeObjectTypeInUnion] +from __future__ import annotations +from typing import Generic +from typing_extensions import TypeVar + +_I = TypeVar("_I", default=int) + +class C(Generic[_I]): pass + +t: type[C] | int = C +[builtins fixtures/tuple.pyi] From c9d4c61d9c80b02279d1fcc9ca8a1974717b5e1c Mon Sep 17 00:00:00 2001 From: jairov4 <1904410+jairov4@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:37:40 -0500 Subject: [PATCH 0843/1617] [mypyc] Optimize dunder methods (#17934) This change gives mypyc the ability to optionally optimize dunder methods that can guarantee strict adherence to its signature typing. The optimization allows to bypass vtable for dunder methods in certain cases that are applicable. Currently, mypy has adopted the convention of accept dunder methods that return `NotImplemented` value even when its signature do not reflect this possibility. With this change and by enabling an special flag, mypyc will expect strict typing be honored and will unleash more optimizations like native call without vtable lookup for some cases on dunder method calls. For example it could avoid calls to RichCompare Python API making the code can be fully optimized in by the C compiler when some comparison with dunders are required. Example: ```python @final class A: def __init__(self, x: i32) -> None: self.x: Final = x def __lt__(self, other: "A") -> bool: return self.x < other.x A(1) < A(2) ``` would produce: ```c char CPyDef_A_____lt__(PyObject *cpy_r_self, PyObject *cpy_r_other) { int32_t cpy_r_r0; int32_t cpy_r_r1; char cpy_r_r2; cpy_r_r0 = ((AObject *)cpy_r_self)->_x; cpy_r_r1 = ((AObject *)cpy_r_other)->_x; cpy_r_r2 = cpy_r_r0 < cpy_r_r1; return cpy_r_r2; } ... cpy_r_r29 = CPyDef_A_____lt__(cpy_r_r27, cpy_r_r28); ... ``` Instead of: ```c PyObject *CPyDef_A_____lt__(PyObject *cpy_r_self, PyObject *cpy_r_other) { int32_t cpy_r_r0; int32_t cpy_r_r1; char cpy_r_r2; PyObject *cpy_r_r3; cpy_r_r0 = ((AObject *)cpy_r_self)->_x; cpy_r_r1 = ((AObject *)cpy_r_other)->_x; cpy_r_r2 = cpy_r_r0 < cpy_r_r1; cpy_r_r3 = cpy_r_r2 ? Py_True : Py_False; CPy_INCREF(cpy_r_r3); return cpy_r_r3; } ... cpy_r_r29 = PyObject_RichCompare(cpy_r_r27, cpy_r_r28, 0); ... ``` Default behavior is kept. Tests run with both of strict typing enabled and disabled. --- mypyc/__main__.py | 5 +- mypyc/build.py | 5 ++ mypyc/irbuild/classdef.py | 55 +++++++++++-------- mypyc/irbuild/function.py | 14 ++--- mypyc/irbuild/ll_builder.py | 53 ++++++++++++++---- mypyc/irbuild/mapper.py | 15 +++--- mypyc/irbuild/prepare.py | 68 +++++++++++++++++------- mypyc/options.py | 8 +++ mypyc/test-data/run-dunders-special.test | 9 ++++ mypyc/test-data/run-dunders.test | 34 +++++++++--- mypyc/test/test_run.py | 16 +++++- 11 files changed, 210 insertions(+), 72 deletions(-) create mode 100644 mypyc/test-data/run-dunders-special.test diff --git a/mypyc/__main__.py b/mypyc/__main__.py index a3b9d21bc65a..653199e0fb55 100644 --- a/mypyc/__main__.py +++ b/mypyc/__main__.py @@ -24,7 +24,7 @@ from mypyc.build import mypycify setup(name='mypyc_output', - ext_modules=mypycify({}, opt_level="{}", debug_level="{}"), + ext_modules=mypycify({}, opt_level="{}", debug_level="{}", strict_dunder_typing={}), ) """ @@ -38,10 +38,11 @@ def main() -> None: opt_level = os.getenv("MYPYC_OPT_LEVEL", "3") debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1") + strict_dunder_typing = bool(int(os.getenv("MYPYC_STRICT_DUNDER_TYPING", "0"))) setup_file = os.path.join(build_dir, "setup.py") with open(setup_file, "w") as f: - f.write(setup_format.format(sys.argv[1:], opt_level, debug_level)) + f.write(setup_format.format(sys.argv[1:], opt_level, debug_level, strict_dunder_typing)) # We don't use run_setup (like we do in the test suite) because it throws # away the error code from distutils, and we don't care about the slight diff --git a/mypyc/build.py b/mypyc/build.py index 485803acba46..6d59113ef872 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -470,6 +470,7 @@ def mypycify( skip_cgen_input: Any | None = None, target_dir: str | None = None, include_runtime_files: bool | None = None, + strict_dunder_typing: bool = False, ) -> list[Extension]: """Main entry point to building using mypyc. @@ -509,6 +510,9 @@ def mypycify( should be directly #include'd instead of linked separately in order to reduce compiler invocations. Defaults to False in multi_file mode, True otherwise. + strict_dunder_typing: If True, force dunder methods to have the return type + of the method strictly, which can lead to more + optimization opportunities. Defaults to False. """ # Figure out our configuration @@ -519,6 +523,7 @@ def mypycify( separate=separate is not False, target_dir=target_dir, include_runtime_files=include_runtime_files, + strict_dunder_typing=strict_dunder_typing, ) # Generate all the actual important C code diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index bcc9594adcb9..6ff6308b81d8 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -81,6 +81,7 @@ pytype_from_template_op, type_object_op, ) +from mypyc.subtype import is_subtype def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: @@ -801,30 +802,42 @@ def create_ne_from_eq(builder: IRBuilder, cdef: ClassDef) -> None: def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None: """Generate a "__ne__" method from a "__eq__" method.""" - with builder.enter_method(cls, "__ne__", object_rprimitive): - rhs_arg = builder.add_argument("rhs", object_rprimitive) - - # If __eq__ returns NotImplemented, then __ne__ should also - not_implemented_block, regular_block = BasicBlock(), BasicBlock() + func_ir = cls.get_method("__eq__") + assert func_ir + eq_sig = func_ir.decl.sig + strict_typing = builder.options.strict_dunders_typing + with builder.enter_method(cls, "__ne__", eq_sig.ret_type): + rhs_type = eq_sig.args[0].type if strict_typing else object_rprimitive + rhs_arg = builder.add_argument("rhs", rhs_type) eqval = builder.add(MethodCall(builder.self(), "__eq__", [rhs_arg], line)) - not_implemented = builder.add( - LoadAddress(not_implemented_op.type, not_implemented_op.src, line) - ) - builder.add( - Branch( - builder.translate_is_op(eqval, not_implemented, "is", line), - not_implemented_block, - regular_block, - Branch.BOOL, - ) - ) - builder.activate_block(regular_block) - retval = builder.coerce(builder.unary_op(eqval, "not", line), object_rprimitive, line) - builder.add(Return(retval)) + can_return_not_implemented = is_subtype(not_implemented_op.type, eq_sig.ret_type) + return_bool = is_subtype(eq_sig.ret_type, bool_rprimitive) - builder.activate_block(not_implemented_block) - builder.add(Return(not_implemented)) + if not strict_typing or can_return_not_implemented: + # If __eq__ returns NotImplemented, then __ne__ should also + not_implemented_block, regular_block = BasicBlock(), BasicBlock() + not_implemented = builder.add( + LoadAddress(not_implemented_op.type, not_implemented_op.src, line) + ) + builder.add( + Branch( + builder.translate_is_op(eqval, not_implemented, "is", line), + not_implemented_block, + regular_block, + Branch.BOOL, + ) + ) + builder.activate_block(regular_block) + rettype = bool_rprimitive if return_bool and strict_typing else object_rprimitive + retval = builder.coerce(builder.unary_op(eqval, "not", line), rettype, line) + builder.add(Return(retval)) + builder.activate_block(not_implemented_block) + builder.add(Return(not_implemented)) + else: + rettype = bool_rprimitive if return_bool and strict_typing else object_rprimitive + retval = builder.coerce(builder.unary_op(eqval, "not", line), rettype, line) + builder.add(Return(retval)) def load_non_ext_class( diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index c985e88b0e0c..f9d55db50f27 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -96,7 +96,8 @@ def transform_func_def(builder: IRBuilder, fdef: FuncDef) -> None: - func_ir, func_reg = gen_func_item(builder, fdef, fdef.name, builder.mapper.fdef_to_sig(fdef)) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, fdef.name, sig) # If the function that was visited was a nested function, then either look it up in our # current environment or define it if it was not already defined. @@ -113,9 +114,8 @@ def transform_overloaded_func_def(builder: IRBuilder, o: OverloadedFuncDef) -> N def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: - func_ir, func_reg = gen_func_item( - builder, dec.func, dec.func.name, builder.mapper.fdef_to_sig(dec.func) - ) + sig = builder.mapper.fdef_to_sig(dec.func, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, dec.func, dec.func.name, sig) decorated_func: Value | None = None if func_reg: decorated_func = load_decorated_func(builder, dec.func, func_reg) @@ -416,7 +416,8 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None # Perform the function of visit_method for methods inside extension classes. name = fdef.name class_ir = builder.mapper.type_to_ir[cdef.info] - func_ir, func_reg = gen_func_item(builder, fdef, name, builder.mapper.fdef_to_sig(fdef), cdef) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef) builder.functions.append(func_ir) if is_decorated(builder, fdef): @@ -481,7 +482,8 @@ def handle_non_ext_method( ) -> None: # Perform the function of visit_method for methods inside non-extension classes. name = fdef.name - func_ir, func_reg = gen_func_item(builder, fdef, name, builder.mapper.fdef_to_sig(fdef), cdef) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef) assert func_reg is not None builder.functions.append(func_ir) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index c98136ce06d2..cb23a74c69c6 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -14,7 +14,7 @@ from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, ArgKind -from mypy.operators import op_methods +from mypy.operators import op_methods, unary_op_methods from mypy.types import AnyType, TypeOfAny from mypyc.common import ( BITMAP_BITS, @@ -167,6 +167,7 @@ buf_init_item, fast_isinstance_op, none_object_op, + not_implemented_op, var_object_size, ) from mypyc.primitives.registry import ( @@ -1398,11 +1399,48 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if base_op in float_op_to_id: return self.float_op(lreg, rreg, base_op, line) + dunder_op = self.dunder_op(lreg, rreg, op, line) + if dunder_op: + return dunder_op + primitive_ops_candidates = binary_ops.get(op, []) target = self.matching_primitive_op(primitive_ops_candidates, [lreg, rreg], line) assert target, "Unsupported binary operation: %s" % op return target + def dunder_op(self, lreg: Value, rreg: Value | None, op: str, line: int) -> Value | None: + """ + Dispatch a dunder method if applicable. + For example for `a + b` it will use `a.__add__(b)` which can lead to higher performance + due to the fact that the method could be already compiled and optimized instead of going + all the way through `PyNumber_Add(a, b)` python api (making a jump into the python DL). + """ + ltype = lreg.type + if not isinstance(ltype, RInstance): + return None + + method_name = op_methods.get(op) if rreg else unary_op_methods.get(op) + if method_name is None: + return None + + if not ltype.class_ir.has_method(method_name): + return None + + decl = ltype.class_ir.method_decl(method_name) + if not rreg and len(decl.sig.args) != 1: + return None + + if rreg and (len(decl.sig.args) != 2 or not is_subtype(rreg.type, decl.sig.args[1].type)): + return None + + if rreg and is_subtype(not_implemented_op.type, decl.sig.ret_type): + # If the method is able to return NotImplemented, we should not optimize it. + # We can just let go so it will be handled through the python api. + return None + + args = [rreg] if rreg else [] + return self.gen_method_call(lreg, method_name, args, decl.sig.ret_type, line) + def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) -> Value: """Check if a tagged integer is a short integer. @@ -1558,16 +1596,9 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: if isinstance(value, Float): return Float(-value.value, value.line) if isinstance(typ, RInstance): - if expr_op == "-": - method = "__neg__" - elif expr_op == "+": - method = "__pos__" - elif expr_op == "~": - method = "__invert__" - else: - method = "" - if method and typ.class_ir.has_method(method): - return self.gen_method_call(value, method, [], None, line) + result = self.dunder_op(value, None, expr_op, line) + if result is not None: + return result call_c_ops_candidates = unary_ops.get(expr_op, []) target = self.matching_call_c(call_c_ops_candidates, [value], line) assert target, "Unsupported unary operation: %s" % expr_op diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 90ce0e16c741..78e54aceed3d 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -160,7 +160,7 @@ def get_arg_rtype(self, typ: Type, kind: ArgKind) -> RType: else: return self.type_to_rtype(typ) - def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature: + def fdef_to_sig(self, fdef: FuncDef, strict_dunders_typing: bool) -> FuncSignature: if isinstance(fdef.type, CallableType): arg_types = [ self.get_arg_rtype(typ, kind) @@ -199,11 +199,14 @@ def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature: ) ] - # We force certain dunder methods to return objects to support letting them - # return NotImplemented. It also avoids some pointless boxing and unboxing, - # since tp_richcompare needs an object anyways. - if fdef.name in ("__eq__", "__ne__", "__lt__", "__gt__", "__le__", "__ge__"): - ret = object_rprimitive + if not strict_dunders_typing: + # We force certain dunder methods to return objects to support letting them + # return NotImplemented. It also avoids some pointless boxing and unboxing, + # since tp_richcompare needs an object anyways. + # However, it also prevents some optimizations. + if fdef.name in ("__eq__", "__ne__", "__lt__", "__gt__", "__le__", "__ge__"): + ret = object_rprimitive + return FuncSignature(args, ret) def is_native_module(self, module: str) -> bool: diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 40a40b79df49..bed9a1684326 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -99,9 +99,11 @@ def build_type_map( for module, cdef in classes: with catch_errors(module.path, cdef.line): if mapper.type_to_ir[cdef.info].is_ext_class: - prepare_class_def(module.path, module.fullname, cdef, errors, mapper) + prepare_class_def(module.path, module.fullname, cdef, errors, mapper, options) else: - prepare_non_ext_class_def(module.path, module.fullname, cdef, errors, mapper) + prepare_non_ext_class_def( + module.path, module.fullname, cdef, errors, mapper, options + ) # Prepare implicit attribute accessors as needed if an attribute overrides a property. for module, cdef in classes: @@ -114,7 +116,7 @@ def build_type_map( # is conditionally defined. for module in modules: for func in get_module_func_defs(module): - prepare_func_def(module.fullname, None, func, mapper) + prepare_func_def(module.fullname, None, func, mapper, options) # TODO: what else? # Check for incompatible attribute definitions that were not @@ -168,27 +170,39 @@ def get_module_func_defs(module: MypyFile) -> Iterable[FuncDef]: def prepare_func_def( - module_name: str, class_name: str | None, fdef: FuncDef, mapper: Mapper + module_name: str, + class_name: str | None, + fdef: FuncDef, + mapper: Mapper, + options: CompilerOptions, ) -> FuncDecl: kind = ( FUNC_STATICMETHOD if fdef.is_static else (FUNC_CLASSMETHOD if fdef.is_class else FUNC_NORMAL) ) - decl = FuncDecl(fdef.name, class_name, module_name, mapper.fdef_to_sig(fdef), kind) + sig = mapper.fdef_to_sig(fdef, options.strict_dunders_typing) + decl = FuncDecl(fdef.name, class_name, module_name, sig, kind) mapper.func_to_decl[fdef] = decl return decl def prepare_method_def( - ir: ClassIR, module_name: str, cdef: ClassDef, mapper: Mapper, node: FuncDef | Decorator + ir: ClassIR, + module_name: str, + cdef: ClassDef, + mapper: Mapper, + node: FuncDef | Decorator, + options: CompilerOptions, ) -> None: if isinstance(node, FuncDef): - ir.method_decls[node.name] = prepare_func_def(module_name, cdef.name, node, mapper) + ir.method_decls[node.name] = prepare_func_def( + module_name, cdef.name, node, mapper, options + ) elif isinstance(node, Decorator): # TODO: do something about abstract methods here. Currently, they are handled just like # normal methods. - decl = prepare_func_def(module_name, cdef.name, node.func, mapper) + decl = prepare_func_def(module_name, cdef.name, node.func, mapper, options) if not node.decorators: ir.method_decls[node.name] = decl elif isinstance(node.decorators[0], MemberExpr) and node.decorators[0].name == "setter": @@ -241,7 +255,12 @@ def can_subclass_builtin(builtin_base: str) -> bool: def prepare_class_def( - path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper + path: str, + module_name: str, + cdef: ClassDef, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: """Populate the interface-level information in a class IR. @@ -308,7 +327,7 @@ def prepare_class_def( ir.mro = mro ir.base_mro = base_mro - prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) + prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper, options) prepare_init_method(cdef, ir, module_name, mapper) for base in bases: @@ -320,7 +339,13 @@ def prepare_class_def( def prepare_methods_and_attributes( - cdef: ClassDef, ir: ClassIR, path: str, module_name: str, errors: Errors, mapper: Mapper + cdef: ClassDef, + ir: ClassIR, + path: str, + module_name: str, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: """Populate attribute and method declarations.""" info = cdef.info @@ -342,20 +367,20 @@ def prepare_methods_and_attributes( add_setter_declaration(ir, name, attr_rtype, module_name) ir.attributes[name] = attr_rtype elif isinstance(node.node, (FuncDef, Decorator)): - prepare_method_def(ir, module_name, cdef, mapper, node.node) + prepare_method_def(ir, module_name, cdef, mapper, node.node, options) elif isinstance(node.node, OverloadedFuncDef): # Handle case for property with both a getter and a setter if node.node.is_property: if is_valid_multipart_property_def(node.node): for item in node.node.items: - prepare_method_def(ir, module_name, cdef, mapper, item) + prepare_method_def(ir, module_name, cdef, mapper, item, options) else: errors.error("Unsupported property decorator semantics", path, cdef.line) # Handle case for regular function overload else: assert node.node.impl - prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) + prepare_method_def(ir, module_name, cdef, mapper, node.node.impl, options) if ir.builtin_base: ir.attributes.clear() @@ -440,7 +465,7 @@ def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: M # Set up a constructor decl init_node = cdef.info["__init__"].node if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef): - init_sig = mapper.fdef_to_sig(init_node) + init_sig = mapper.fdef_to_sig(init_node, True) defining_ir = mapper.type_to_ir.get(init_node.info) # If there is a nontrivial __init__ that wasn't defined in an @@ -467,24 +492,29 @@ def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: M def prepare_non_ext_class_def( - path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper + path: str, + module_name: str, + cdef: ClassDef, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: ir = mapper.type_to_ir[cdef.info] info = cdef.info for node in info.names.values(): if isinstance(node.node, (FuncDef, Decorator)): - prepare_method_def(ir, module_name, cdef, mapper, node.node) + prepare_method_def(ir, module_name, cdef, mapper, node.node, options) elif isinstance(node.node, OverloadedFuncDef): # Handle case for property with both a getter and a setter if node.node.is_property: if not is_valid_multipart_property_def(node.node): errors.error("Unsupported property decorator semantics", path, cdef.line) for item in node.node.items: - prepare_method_def(ir, module_name, cdef, mapper, item) + prepare_method_def(ir, module_name, cdef, mapper, item, options) # Handle case for regular function overload else: - prepare_method_def(ir, module_name, cdef, mapper, get_func_def(node.node)) + prepare_method_def(ir, module_name, cdef, mapper, get_func_def(node.node), options) if any(cls in mapper.type_to_ir and mapper.type_to_ir[cls].is_ext_class for cls in info.mro): errors.error( diff --git a/mypyc/options.py b/mypyc/options.py index 5f0cf12aeefe..24e68163bb11 100644 --- a/mypyc/options.py +++ b/mypyc/options.py @@ -14,6 +14,7 @@ def __init__( include_runtime_files: bool | None = None, capi_version: tuple[int, int] | None = None, python_version: tuple[int, int] | None = None, + strict_dunder_typing: bool = False, ) -> None: self.strip_asserts = strip_asserts self.multi_file = multi_file @@ -30,3 +31,10 @@ def __init__( # features are used. self.capi_version = capi_version or sys.version_info[:2] self.python_version = python_version + # Make possible to inline dunder methods in the generated code. + # Typically, the convention is the dunder methods can return `NotImplemented` + # even when its return type is just `bool`. + # By enabling this option, this convention is no longer valid and the dunder + # will assume the return type of the method strictly, which can lead to + # more optimization opportunities. + self.strict_dunders_typing = strict_dunder_typing diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test new file mode 100644 index 000000000000..cd02cca65eef --- /dev/null +++ b/mypyc/test-data/run-dunders-special.test @@ -0,0 +1,9 @@ +[case testDundersNotImplemented] +# This case is special because it tests the behavior of NotImplemented +# used in a typed function which return type is bool. +# This is a convention that can be overriden by the user. +class UsesNotImplemented: + def __eq__(self, b: object) -> bool: + return NotImplemented + +assert UsesNotImplemented() != object() diff --git a/mypyc/test-data/run-dunders.test b/mypyc/test-data/run-dunders.test index 2845187de2c3..b8fb13c9dcec 100644 --- a/mypyc/test-data/run-dunders.test +++ b/mypyc/test-data/run-dunders.test @@ -32,10 +32,6 @@ class BoxedThing: class Subclass2(BoxedThing): pass -class UsesNotImplemented: - def __eq__(self, b: object) -> bool: - return NotImplemented - def index_into(x : Any, y : Any) -> Any: return x[y] @@ -81,8 +77,6 @@ assert is_truthy(Item('a')) assert not is_truthy(Subclass1('')) assert is_truthy(Subclass1('a')) -assert UsesNotImplemented() != object() - internal_index_into() [out] 7 7 @@ -943,3 +937,31 @@ def test_errors() -> None: pow(ForwardNotImplemented(), Child(), 3) # type: ignore with assertRaises(TypeError, "unsupported operand type(s) for ** or pow(): 'ForwardModRequired' and 'int'"): ForwardModRequired()**3 # type: ignore + +[case testDundersWithFinal] +from typing import final +class A: + def __init__(self, x: int) -> None: + self.x = x + + def __add__(self, y: int) -> int: + return self.x + y + + def __lt__(self, x: 'A') -> bool: + return self.x < x.x + +@final +class B(A): + def __add__(self, y: int) -> int: + return self.x + y + 1 + + def __lt__(self, x: 'A') -> bool: + return self.x < x.x + 1 + +def test_final() -> None: + a = A(5) + b = B(5) + assert a + 3 == 8 + assert b + 3 == 9 + assert (a < A(5)) is False + assert (b < A(5)) is True diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 668e5b124841..dd3c79da7b9b 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -63,6 +63,7 @@ "run-bench.test", "run-mypy-sim.test", "run-dunders.test", + "run-dunders-special.test", "run-singledispatch.test", "run-attrs.test", "run-python37.test", @@ -140,6 +141,7 @@ class TestRun(MypycDataSuite): optional_out = True multi_file = False separate = False # If True, using separate (incremental) compilation + strict_dunder_typing = False def run_case(self, testcase: DataDrivenTestCase) -> None: # setup.py wants to be run from the root directory of the package, which we accommodate @@ -232,7 +234,11 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> groups = construct_groups(sources, separate, len(module_names) > 1) try: - compiler_options = CompilerOptions(multi_file=self.multi_file, separate=self.separate) + compiler_options = CompilerOptions( + multi_file=self.multi_file, + separate=self.separate, + strict_dunder_typing=self.strict_dunder_typing, + ) result = emitmodule.parse_and_typecheck( sources=sources, options=options, @@ -401,6 +407,14 @@ class TestRunSeparate(TestRun): files = ["run-multimodule.test", "run-mypy-sim.test"] +class TestRunStrictDunderTyping(TestRun): + """Run the tests with strict dunder typing.""" + + strict_dunder_typing = True + test_name_suffix = "_dunder_typing" + files = ["run-dunders.test", "run-floats.test"] + + def fix_native_line_number(message: str, fnam: str, delta: int) -> str: """Update code locations in test case output to point to the .test file. From 603a3652b59c21e82927db407194b824b3ef7f7b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:55:31 +0200 Subject: [PATCH 0844/1617] Add initial support for new style TypeVar defaults (PEP 696) (#17985) Add initial support for TypeVar defaults using the new syntax. Similar to the old syntax, it doesn't fully work yet for ParamSpec, TypeVarTuple and recursive TypeVar defaults. Refs: #14851 --- mypy/checker.py | 15 ++ mypy/fastparse.py | 22 +- mypy/message_registry.py | 5 - mypy/nodes.py | 10 +- mypy/semanal.py | 132 ++++++----- mypy/strconv.py | 2 + mypy/test/testparse.py | 4 + test-data/unit/check-python313.test | 262 ++++++++++++++++++++- test-data/unit/check-typevar-defaults.test | 9 +- test-data/unit/parse-python313.test | 80 +++++++ 10 files changed, 456 insertions(+), 85 deletions(-) create mode 100644 test-data/unit/parse-python313.test diff --git a/mypy/checker.py b/mypy/checker.py index 74890aa86cda..4b3d6c3298b4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1159,6 +1159,7 @@ def check_func_def( ) -> None: """Type check a function definition.""" # Expand type variables with value restrictions to ordinary types. + self.check_typevar_defaults(typ.variables) expanded = self.expand_typevars(defn, typ) original_typ = typ for item, typ in expanded: @@ -2483,6 +2484,8 @@ def visit_class_def(self, defn: ClassDef) -> None: context=defn, code=codes.TYPE_VAR, ) + if typ.defn.type_vars: + self.check_typevar_defaults(typ.defn.type_vars) if typ.is_protocol and typ.defn.type_vars: self.check_protocol_variance(defn) @@ -2546,6 +2549,15 @@ def check_init_subclass(self, defn: ClassDef) -> None: # all other bases have already been checked. break + def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None: + for tv in tvars: + if not (isinstance(tv, TypeVarType) and tv.has_default()): + continue + if not is_subtype(tv.default, tv.upper_bound): + self.fail("TypeVar default must be a subtype of the bound type", tv) + if tv.values and not any(tv.default == value for value in tv.values): + self.fail("TypeVar default must be one of the constraint types", tv) + def check_enum(self, defn: ClassDef) -> None: assert defn.info.is_enum if defn.info.fullname not in ENUM_BASES: @@ -5365,6 +5377,9 @@ def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, del type_map[expr] def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + if o.alias_node: + self.check_typevar_defaults(o.alias_node.alias_tvars) + with self.msg.filter_errors(): self.expr_checker.accept(o.value) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index bc5b1ba8e57a..a47ed9b536da 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1196,19 +1196,17 @@ def validate_type_param(self, type_param: ast_TypeVar) -> None: def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: explicit_type_params = [] for p in type_params: - bound = None + bound: Type | None = None values: list[Type] = [] - if sys.version_info >= (3, 13) and p.default_value is not None: - self.fail( - message_registry.TYPE_PARAM_DEFAULT_NOT_SUPPORTED, - p.lineno, - p.col_offset, - blocker=False, - ) + default: Type | None = None + if sys.version_info >= (3, 13): + default = TypeConverter(self.errors, line=p.lineno).visit(p.default_value) if isinstance(p, ast_ParamSpec): # type: ignore[misc] - explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [])) + explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [], default)) elif isinstance(p, ast_TypeVarTuple): # type: ignore[misc] - explicit_type_params.append(TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [])) + explicit_type_params.append( + TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [], default) + ) else: if isinstance(p.bound, ast3.Tuple): if len(p.bound.elts) < 2: @@ -1224,7 +1222,9 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: elif p.bound is not None: self.validate_type_param(p) bound = TypeConverter(self.errors, line=p.lineno).visit(p.bound) - explicit_type_params.append(TypeParam(p.name, TYPE_VAR_KIND, bound, values)) + explicit_type_params.append( + TypeParam(p.name, TYPE_VAR_KIND, bound, values, default) + ) return explicit_type_params # Return(expr? value) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 507af6fdad74..29d539faaed6 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -362,8 +362,3 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage( "Await expression cannot be used within a type alias", codes.SYNTAX ) - -TYPE_PARAM_DEFAULT_NOT_SUPPORTED: Final = ErrorMessage( - "Type parameter default types not supported when using Python 3.12 type parameter syntax", - codes.MISC, -) diff --git a/mypy/nodes.py b/mypy/nodes.py index c4d23ca7bff8..4806a7b0be7d 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -670,7 +670,7 @@ def set_line( class TypeParam: - __slots__ = ("name", "kind", "upper_bound", "values") + __slots__ = ("name", "kind", "upper_bound", "values", "default") def __init__( self, @@ -678,11 +678,13 @@ def __init__( kind: int, upper_bound: mypy.types.Type | None, values: list[mypy.types.Type], + default: mypy.types.Type | None, ) -> None: self.name = name self.kind = kind self.upper_bound = upper_bound self.values = values + self.default = default FUNCITEM_FLAGS: Final = FUNCBASE_FLAGS + [ @@ -782,7 +784,7 @@ class FuncDef(FuncItem, SymbolNode, Statement): "deco_line", "is_trivial_body", "is_mypy_only", - # Present only when a function is decorated with @typing.datasclass_transform or similar + # Present only when a function is decorated with @typing.dataclass_transform or similar "dataclass_transform_spec", "docstring", "deprecated", @@ -1657,7 +1659,7 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class TypeAliasStmt(Statement): - __slots__ = ("name", "type_args", "value", "invalid_recursive_alias") + __slots__ = ("name", "type_args", "value", "invalid_recursive_alias", "alias_node") __match_args__ = ("name", "type_args", "value") @@ -1665,6 +1667,7 @@ class TypeAliasStmt(Statement): type_args: list[TypeParam] value: LambdaExpr # Return value will get translated into a type invalid_recursive_alias: bool + alias_node: TypeAlias | None def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr) -> None: super().__init__() @@ -1672,6 +1675,7 @@ def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr self.type_args = type_args self.value = value self.invalid_recursive_alias = False + self.alias_node = None def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_type_alias_stmt(self) diff --git a/mypy/semanal.py b/mypy/semanal.py index 5332c98c8f0d..a0d4daffca0c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1808,7 +1808,26 @@ def analyze_type_param( upper_bound = self.named_type("builtins.tuple", [self.object_type()]) else: upper_bound = self.object_type() - default = AnyType(TypeOfAny.from_omitted_generics) + if type_param.default: + default = self.anal_type( + type_param.default, + allow_placeholder=True, + allow_unbound_tvars=True, + report_invalid_types=False, + allow_param_spec_literals=type_param.kind == PARAM_SPEC_KIND, + allow_tuple_literal=type_param.kind == PARAM_SPEC_KIND, + allow_unpack=type_param.kind == TYPE_VAR_TUPLE_KIND, + ) + if default is None: + default = PlaceholderType(None, [], context.line) + elif type_param.kind == TYPE_VAR_KIND: + default = self.check_typevar_default(default, type_param.default) + elif type_param.kind == PARAM_SPEC_KIND: + default = self.check_paramspec_default(default, type_param.default) + elif type_param.kind == TYPE_VAR_TUPLE_KIND: + default = self.check_typevartuple_default(default, type_param.default) + else: + default = AnyType(TypeOfAny.from_omitted_generics) if type_param.kind == TYPE_VAR_KIND: values = [] if type_param.values: @@ -2243,21 +2262,7 @@ class Foo(Bar, Generic[T]): ... # grained incremental mode. defn.removed_base_type_exprs.append(defn.base_type_exprs[i]) del base_type_exprs[i] - tvar_defs: list[TypeVarLikeType] = [] - last_tvar_name_with_default: str | None = None - for name, tvar_expr in declared_tvars: - tvar_expr.default = tvar_expr.default.accept( - TypeVarDefaultTranslator(self, tvar_expr.name, context) - ) - tvar_def = self.tvar_scope.bind_new(name, tvar_expr) - if last_tvar_name_with_default is not None and not tvar_def.has_default(): - self.msg.tvar_without_default_type( - tvar_def.name, last_tvar_name_with_default, context - ) - tvar_def.default = AnyType(TypeOfAny.from_error) - elif tvar_def.has_default(): - last_tvar_name_with_default = tvar_def.name - tvar_defs.append(tvar_def) + tvar_defs = self.tvar_defs_from_tvars(declared_tvars, context) return base_type_exprs, tvar_defs, is_protocol def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList, bool] | None: @@ -2358,6 +2363,26 @@ def get_all_bases_tvars( tvars.extend(base_tvars) return remove_dups(tvars) + def tvar_defs_from_tvars( + self, tvars: TypeVarLikeList, context: Context + ) -> list[TypeVarLikeType]: + tvar_defs: list[TypeVarLikeType] = [] + last_tvar_name_with_default: str | None = None + for name, tvar_expr in tvars: + tvar_expr.default = tvar_expr.default.accept( + TypeVarDefaultTranslator(self, tvar_expr.name, context) + ) + tvar_def = self.tvar_scope.bind_new(name, tvar_expr) + if last_tvar_name_with_default is not None and not tvar_def.has_default(): + self.msg.tvar_without_default_type( + tvar_def.name, last_tvar_name_with_default, context + ) + tvar_def.default = AnyType(TypeOfAny.from_error) + elif tvar_def.has_default(): + last_tvar_name_with_default = tvar_def.name + tvar_defs.append(tvar_def) + return tvar_defs + def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLikeType]: """Return all type variable references in item type expressions. @@ -3833,21 +3858,8 @@ def analyze_alias( tvar_defs: list[TypeVarLikeType] = [] namespace = self.qualified_name(name) alias_type_vars = found_type_vars if declared_type_vars is None else declared_type_vars - last_tvar_name_with_default: str | None = None with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): - for name, tvar_expr in alias_type_vars: - tvar_expr.default = tvar_expr.default.accept( - TypeVarDefaultTranslator(self, tvar_expr.name, typ) - ) - tvar_def = self.tvar_scope.bind_new(name, tvar_expr) - if last_tvar_name_with_default is not None and not tvar_def.has_default(): - self.msg.tvar_without_default_type( - tvar_def.name, last_tvar_name_with_default, typ - ) - tvar_def.default = AnyType(TypeOfAny.from_error) - elif tvar_def.has_default(): - last_tvar_name_with_default = tvar_def.name - tvar_defs.append(tvar_def) + tvar_defs = self.tvar_defs_from_tvars(alias_type_vars, typ) if python_3_12_type_alias: with self.allow_unbound_tvars_set(): @@ -4615,6 +4627,40 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: self.add_symbol(name, call.analyzed, s) return True + def check_typevar_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if isinstance(typ, AnyType) and typ.is_from_error: + self.fail( + message_registry.TYPEVAR_ARG_MUST_BE_TYPE.format("TypeVar", "default"), context + ) + return default + + def check_paramspec_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if isinstance(typ, Parameters): + for i, arg_type in enumerate(typ.arg_types): + arg_ptype = get_proper_type(arg_type) + if isinstance(arg_ptype, AnyType) and arg_ptype.is_from_error: + self.fail(f"Argument {i} of ParamSpec default must be a type", context) + elif ( + isinstance(typ, AnyType) + and typ.is_from_error + or not isinstance(typ, (AnyType, UnboundType)) + ): + self.fail( + "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec", + context, + ) + default = AnyType(TypeOfAny.from_error) + return default + + def check_typevartuple_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if not isinstance(typ, UnpackType): + self.fail("The default argument to TypeVarTuple must be an Unpacked tuple", context) + default = AnyType(TypeOfAny.from_error) + return default + def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> bool: """Checks that the name of a TypeVar or ParamSpec matches its variable.""" name = unmangle(name) @@ -4822,23 +4868,7 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: report_invalid_typevar_arg=False, ) default = tv_arg or AnyType(TypeOfAny.from_error) - if isinstance(tv_arg, Parameters): - for i, arg_type in enumerate(tv_arg.arg_types): - typ = get_proper_type(arg_type) - if isinstance(typ, AnyType) and typ.is_from_error: - self.fail( - f"Argument {i} of ParamSpec default must be a type", param_value - ) - elif ( - isinstance(default, AnyType) - and default.is_from_error - or not isinstance(default, (AnyType, UnboundType)) - ): - self.fail( - "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec", - param_value, - ) - default = AnyType(TypeOfAny.from_error) + default = self.check_paramspec_default(default, param_value) else: # ParamSpec is different from a regular TypeVar: # arguments are not semantically valid. But, allowed in runtime. @@ -4899,12 +4929,7 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: allow_unpack=True, ) default = tv_arg or AnyType(TypeOfAny.from_error) - if not isinstance(default, UnpackType): - self.fail( - "The default argument to TypeVarTuple must be an Unpacked tuple", - param_value, - ) - default = AnyType(TypeOfAny.from_error) + default = self.check_typevartuple_default(default, param_value) else: self.fail(f'Unexpected keyword argument "{param_name}" for "TypeVarTuple"', s) @@ -5503,6 +5528,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: eager=eager, python_3_12_type_alias=True, ) + s.alias_node = alias_node if ( existing diff --git a/mypy/strconv.py b/mypy/strconv.py index a96a27c45d75..d2ac71412f90 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -349,6 +349,8 @@ def type_param(self, p: mypy.nodes.TypeParam) -> list[Any]: a.append(p.upper_bound) if p.values: a.append(("Values", p.values)) + if p.default: + a.append(("Default", [p.default])) return [("TypeParam", a)] # Expressions diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index e215920a6797..074ccfb379d0 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -25,6 +25,8 @@ class ParserSuite(DataSuite): files.remove("parse-python310.test") if sys.version_info < (3, 12): files.remove("parse-python312.test") + if sys.version_info < (3, 13): + files.remove("parse-python313.test") def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -43,6 +45,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None: options.python_version = (3, 10) elif testcase.file.endswith("python312.test"): options.python_version = (3, 12) + elif testcase.file.endswith("python313.test"): + options.python_version = (3, 13) else: options.python_version = defaults.PYTHON3_VERSION diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test index 0ba64ad67c91..2729ad3e21d1 100644 --- a/test-data/unit/check-python313.test +++ b/test-data/unit/check-python313.test @@ -1,11 +1,257 @@ -[case testPEP695TypeParameterDefaultNotSupported] -class C[T = None]: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +[case testPEP695TypeParameterDefaultSupported] +class C[T = None]: ... +def f[T = list[int]]() -> None: ... +def g[**P = [int, str]]() -> None: ... +type A[T, S = int, U = str] = list[T] -def f[T = list[int]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +[case testPEP695TypeParameterDefaultBasic] +from typing import Callable -def g[**P = [int, str]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +def f1[T1 = int](a: T1) -> list[T1]: ... +reveal_type(f1) # N: Revealed type is "def [T1 = builtins.int] (a: T1`-1 = builtins.int) -> builtins.list[T1`-1 = builtins.int]" -type A[T, S = int, U = str] = list[T] # E: Type parameter default types not supported when using Python 3.12 type parameter syntax +def f2[**P1 = [int, str]](a: Callable[P1, None]) -> Callable[P1, None]: ... +reveal_type(f2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)" + +def f3[*Ts1 = *tuple[int, str]](a: tuple[*Ts1]) -> tuple[*Ts1]: ... +reveal_type(f3) # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] (a: Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]) -> Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]" + + +class ClassA1[T1 = int]: ... +class ClassA2[**P1 = [int, str]]: ... +class ClassA3[*Ts1 = *tuple[int, str]]: ... + +reveal_type(ClassA1) # N: Revealed type is "def [T1 = builtins.int] () -> __main__.ClassA1[T1`1 = builtins.int]" +reveal_type(ClassA2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] () -> __main__.ClassA2[P1`1 = [builtins.int, builtins.str]]" +reveal_type(ClassA3) # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[Tuple[builtins.int, builtins.str]]]]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultValid] +from typing import Any + +class ClassT1[T = int]: ... +class ClassT2[T: float = int]: ... +class ClassT3[T: list[Any] = list[int]]: ... +class ClassT4[T: (int, str) = int]: ... + +class ClassP1[**P = []]: ... +class ClassP2[**P = ...]: ... +class ClassP3[**P = [int, str]]: ... + +class ClassTs1[*Ts = *tuple[int]]: ... +class ClassTs2[*Ts = *tuple[int, ...]]: ... +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid] +class ClassT1[T = 2]: ... # E: TypeVar "default" must be a type +class ClassT2[T = [int]]: ... # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +class ClassT3[T: str = int]: ... # E: TypeVar default must be a subtype of the bound type +class ClassT4[T: list[str] = list[int]]: ... # E: TypeVar default must be a subtype of the bound type +class ClassT5[T: (int, str) = bytes]: ... # E: TypeVar default must be one of the constraint types +class ClassT6[T: (int, str) = int | str]: ... # E: TypeVar default must be one of the constraint types +class ClassT7[T: (float, str) = int]: ... # E: TypeVar default must be one of the constraint types + +class ClassP1[**P = int]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP2[**P = 2]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP3[**P = (2, int)]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP4[**P = [2, int]]: ... # E: Argument 0 of ParamSpec default must be a type + +class ClassTs1[*Ts = 2]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +class ClassTs2[*Ts = int]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +class ClassTs3[*Ts = tuple[int]]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid2] +from typing import overload +def f1[T = 2]() -> None: ... # E: TypeVar "default" must be a type +def f2[T = [int]]() -> None: ... # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +def f3[T: str = int](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +def f4[T: list[str] = list[int]](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +def f5[T: (int, str) = bytes](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f6[T: (int, str) = int | str](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f7[T: (float, str) = int](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f8[T: str = int]() -> None: ... # TODO check unused TypeVars +@overload +def f9[T: str = int](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +@overload +def f9[T: (int, str) = bytes](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f9() -> None: ... # type: ignore[misc] + +def g1[**P = int]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g2[**P = 2]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g3[**P = (2, int)]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g4[**P = [2, int]]() -> None: ... # E: Argument 0 of ParamSpec default must be a type + +def h1[*Ts = 2]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +def h2[*Ts = int]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +def h3[*Ts = tuple[int]]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid3] +from typing import Callable + +type TA1[T: str = 1] = list[T] # E: TypeVar "default" must be a type +type TA2[T: str = [int]] = list[T] # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +type TA3[T: str = int] = list[T] # E: TypeVar default must be a subtype of the bound type +type TA4[T: list[str] = list[int]] = list[T] # E: TypeVar default must be a subtype of the bound type +type TA5[T: (int, str) = bytes] = list[T] # E: TypeVar default must be one of the constraint types +type TA6[T: (int, str) = int | str] = list[T] # E: TypeVar default must be one of the constraint types +type TA7[T: (float, str) = int] = list[T] # E: TypeVar default must be one of the constraint types + +type TB1[**P = int] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB2[**P = 2] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB3[**P = (2, int)] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB4[**P = [2, int]] = Callable[P, None] # E: Argument 0 of ParamSpec default must be a type + +type TC1[*Ts = 2] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +type TC2[*Ts = int] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +type TC3[*Ts = tuple[int]] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultFunctions] +from typing import Callable + +def callback1(x: str) -> None: ... + +def func_a1[T = str](x: int | T) -> T: ... +reveal_type(func_a1(2)) # N: Revealed type is "builtins.str" +reveal_type(func_a1(2.1)) # N: Revealed type is "builtins.float" + +def func_a2[T = str](x: int | T) -> list[T]: ... +reveal_type(func_a2(2)) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(func_a2(2.1)) # N: Revealed type is "builtins.list[builtins.float]" + + +def func_a3[T: str = str](x: int | T) -> T: ... +reveal_type(func_a3(2)) # N: Revealed type is "builtins.str" + +def func_a4[T: (bytes, str) = str](x: int | T) -> T: ... +reveal_type(func_a4(2)) # N: Revealed type is "builtins.str" + +def func_b1[**P = [int, str]](x: int | Callable[P, None]) -> Callable[P, None]: ... +reveal_type(func_b1(callback1)) # N: Revealed type is "def (x: builtins.str)" +reveal_type(func_b1(2)) # N: Revealed type is "def (builtins.int, builtins.str)" + +def func_c1[*Ts = *tuple[int, str]](x: int | Callable[[*Ts], None]) -> tuple[*Ts]: ... +# reveal_type(func_c1(callback1)) # Revealed type is "Tuple[str]" # TODO +reveal_type(func_c1(2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultClass1] +# flags: --disallow-any-generics + +class ClassA1[T2 = int, T3 = str]: ... + +def func_a1( + a: ClassA1, + b: ClassA1[float], + c: ClassA1[float, float], + d: ClassA1[float, float, float], # E: "ClassA1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultClass2] +# flags: --disallow-any-generics + +class ClassB1[**P2 = [int, str], **P3 = ...]: ... + +def func_b1( + a: ClassB1, + b: ClassB1[[float]], + c: ClassB1[[float], [float]], + d: ClassB1[[float], [float], [float]], # E: "ClassB1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], ...]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + + k = ClassB1() + reveal_type(k) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + l = ClassB1[[float]]() + reveal_type(l) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + m = ClassB1[[float], [float]]() + reveal_type(m) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + n = ClassB1[[float], [float], [float]]() # E: Type application has too many types (expected between 0 and 2) + reveal_type(n) # N: Revealed type is "Any" + +[case testPEP695TypeParameterDefaultClass3] +# flags: --disallow-any-generics + +class ClassC1[*Ts = *tuple[int, str]]: ... + +def func_c1( + a: ClassC1, + b: ClassC1[float], +) -> None: + # reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]" + + k = ClassC1() + reveal_type(k) # N: Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" + l = ClassC1[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassC1[builtins.float]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias1] +# flags: --disallow-any-generics + +type TA1[T2 = int, T3 = str] = dict[T2, T3] + +def func_a1( + a: TA1, + b: TA1[float], + c: TA1[float, float], + d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias2] +# flags: --disallow-any-generics + +class ClassB1[**P2, **P3]: ... +type TB1[**P2 = [int, str], **P3 = ...] = ClassB1[P2, P3] + +def func_b1( + a: TB1, + b: TB1[[float]], + c: TB1[[float], [float]], + d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias3] +# flags: --disallow-any-generics + +type TC1[*Ts = *tuple[int, str]] = tuple[*Ts] + +def func_c1( + a: TC1, + b: TC1[float], +) -> None: + # reveal_type(a) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "Tuple[builtins.float]" + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 22e2594eb38b..3cd94f4a46d2 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -1,5 +1,4 @@ [case testTypeVarDefaultsBasic] -import builtins from typing import Generic, TypeVar, ParamSpec, Callable, Tuple, List from typing_extensions import TypeVarTuple, Unpack @@ -10,7 +9,7 @@ Ts1 = TypeVarTuple("Ts1", default=Unpack[Tuple[int, str]]) def f1(a: T1) -> List[T1]: ... reveal_type(f1) # N: Revealed type is "def [T1 = builtins.int] (a: T1`-1 = builtins.int) -> builtins.list[T1`-1 = builtins.int]" -def f2(a: Callable[P1, None] ) -> Callable[P1, None]: ... +def f2(a: Callable[P1, None]) -> Callable[P1, None]: ... reveal_type(f2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)" def f3(a: Tuple[Unpack[Ts1]]) -> Tuple[Unpack[Ts1]]: ... @@ -68,7 +67,7 @@ P2 = ParamSpec("P2", default=2) # E: The default argument to ParamSpec must be P3 = ParamSpec("P3", default=(2, int)) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec P4 = ParamSpec("P4", default=[2, int]) # E: Argument 0 of ParamSpec default must be a type -Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple must be an Unpacked tuple +Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts2 = TypeVarTuple("Ts2", default=int) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts3 = TypeVarTuple("Ts3", default=Tuple[int]) # E: The default argument to TypeVarTuple must be an Unpacked tuple [builtins fixtures/tuple.pyi] @@ -181,8 +180,8 @@ reveal_type(func_b1(callback1)) # N: Revealed type is "def (x: builtins.str)" reveal_type(func_b1(2)) # N: Revealed type is "def (builtins.int, builtins.str)" def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: ... -# reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO -# reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO +# reveal_type(func_c1(callback1)) # Revealed type is "Tuple[str]" # TODO +reveal_type(func_c1(2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsClass1] diff --git a/test-data/unit/parse-python313.test b/test-data/unit/parse-python313.test new file mode 100644 index 000000000000..efbafb0766f5 --- /dev/null +++ b/test-data/unit/parse-python313.test @@ -0,0 +1,80 @@ +[case testPEP696TypeAlias] +type A[T = int] = C[T] +[out] +MypyFile:1( + TypeAliasStmt:1( + NameExpr(A) + TypeParam( + T + Default( + int?)) + LambdaExpr:1( + Block:-1( + ReturnStmt:1( + IndexExpr:1( + NameExpr(C) + NameExpr(T))))))) + +[case testPEP696GenericFunction] +def f[T = int](): pass +class C[T = int]: pass +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + T + Default( + int?)) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + T + Default( + int?)) + PassStmt:2())) + +[case testPEP696ParamSpec] +def f[**P = [int, str]](): pass +class C[**P = [int, str]]: pass +[out] +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + **P + Default( + )) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + **P + Default( + )) + PassStmt:2())) + +[case testPEP696TypeVarTuple] +def f[*Ts = *tuple[str, int]](): pass +class C[*Ts = *tuple[str, int]]: pass +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + *Ts + Default( + Unpack[tuple?[str?, int?]])) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + *Ts + Default( + Unpack[tuple?[str?, int?]])) + PassStmt:2())) From 49e3a0ff8d6597663aac6709d8e6a022e0424718 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:45:24 -0700 Subject: [PATCH 0845/1617] Update changelog for 1.12.1 (#17999) --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4522465a484d..d0cbe466a5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -261,6 +261,12 @@ This feature was contributed by Ivan Levkivskyi (PR [17457](https://github.com/p Please see [git log](https://github.com/python/typeshed/commits/main?after=91a58b07cdd807b1d965e04ba85af2adab8bf924+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +### Mypy 1.12.1 + * Fix crash when showing partially analyzed type in error message (Ivan Levkivskyi, PR [17961](https://github.com/python/mypy/pull/17961)) + * Fix iteration over union (when self type is involved) (Shantanu, PR [17976](https://github.com/python/mypy/pull/17976)) + * Fix type object with type var default in union context (Jukka Lehtosalo, PR [17991](https://github.com/python/mypy/pull/17991)) + * Revert change to `os.path` stubs affecting use of `os.PathLike[Any]` (Shantanu, PR [17995](https://github.com/python/mypy/pull/17995)) + ### Acknowledgements Thanks to all mypy contributors who contributed to this release: From e3dbce48601ccfdf97be6b378ee31939c7558143 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:49:53 -0700 Subject: [PATCH 0846/1617] Bump version to 1.14.0+dev (#17998) I'll be making a 1.13 (that's cut from the 1.12 branch), so we should bump the version on master as well --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index e0671c9feb06..4510cc56f32b 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.13.0+dev" +__version__ = "1.14.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From b16c19277b13aebedbae5542af4dcf107a475125 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 18:03:16 -0700 Subject: [PATCH 0847/1617] Sync typeshed (#17994) Source commit: https://github.com/python/typeshed/commit/7fb84668fc66e41a3886e460abe54af0bfc5dc6f --- .../stdlib/distutils/command/bdist.pyi | 16 ++--- .../stdlib/distutils/command/bdist_dumb.pyi | 17 ++--- .../stdlib/distutils/command/bdist_msi.pyi | 35 ++++----- .../stdlib/distutils/command/bdist_rpm.pyi | 71 ++++++++++--------- .../stdlib/distutils/command/build.pyi | 22 +++--- .../stdlib/distutils/command/build_clib.pyi | 20 +++--- .../stdlib/distutils/command/build_ext.pyi | 48 ++++++------- .../stdlib/distutils/command/build_py.pyi | 23 +++--- .../distutils/command/build_scripts.pyi | 15 ++-- .../stdlib/distutils/command/check.pyi | 5 +- .../stdlib/distutils/command/clean.pyi | 15 ++-- .../stdlib/distutils/command/config.pyi | 6 +- .../stdlib/distutils/command/install.pyi | 41 +++++------ .../stdlib/distutils/command/install_data.pyi | 11 +-- .../distutils/command/install_egg_info.pyi | 9 +-- .../distutils/command/install_headers.pyi | 7 +- .../stdlib/distutils/command/install_lib.pyi | 13 ++-- .../distutils/command/install_scripts.pyi | 11 +-- .../stdlib/distutils/command/register.pyi | 3 +- .../stdlib/distutils/command/sdist.pyi | 18 ++--- .../stdlib/distutils/command/upload.pyi | 9 +-- mypy/typeshed/stdlib/distutils/core.pyi | 7 +- mypy/typeshed/stdlib/distutils/log.pyi | 1 + mypy/typeshed/stdlib/itertools.pyi | 38 +++++----- 24 files changed, 239 insertions(+), 222 deletions(-) diff --git a/mypy/typeshed/stdlib/distutils/command/bdist.pyi b/mypy/typeshed/stdlib/distutils/command/bdist.pyi index 43d77087f7d8..6f996207077e 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist.pyi @@ -1,6 +1,6 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +from typing import ClassVar from ..cmd import Command @@ -15,13 +15,13 @@ class bdist(Command): default_format: ClassVar[dict[str, str]] format_commands: ClassVar[list[str]] format_command: ClassVar[dict[str, tuple[str, str]]] - bdist_base: Any - plat_name: Any - formats: Any - dist_dir: Any + bdist_base: Incomplete + plat_name: Incomplete + formats: Incomplete + dist_dir: Incomplete skip_build: int - group: Any - owner: Any + group: Incomplete + owner: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi index 19997882dd53..297a0c39ed43 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -7,15 +8,15 @@ class bdist_dumb(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] default_format: ClassVar[dict[str, str]] - bdist_dir: Any - plat_name: Any - format: Any + bdist_dir: Incomplete + plat_name: Incomplete + format: Incomplete keep_temp: int - dist_dir: Any - skip_build: Any + dist_dir: Incomplete + skip_build: Incomplete relative: int - owner: Any - group: Any + owner: Incomplete + group: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi index d0eac1a3be5b..baeee7d3eccb 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi @@ -1,42 +1,43 @@ import sys -from typing import Any, ClassVar, Literal +from _typeshed import Incomplete +from typing import ClassVar, Literal from ..cmd import Command if sys.platform == "win32": - from msilib import Dialog + from msilib import Control, Dialog class PyDialog(Dialog): def __init__(self, *args, **kw) -> None: ... def title(self, title) -> None: ... - def back(self, title, next, name: str = "Back", active: bool | Literal[0, 1] = 1): ... - def cancel(self, title, next, name: str = "Cancel", active: bool | Literal[0, 1] = 1): ... - def next(self, title, next, name: str = "Next", active: bool | Literal[0, 1] = 1): ... - def xbutton(self, name, title, next, xpos): ... + def back(self, title, next, name: str = "Back", active: bool | Literal[0, 1] = 1) -> Control: ... + def cancel(self, title, next, name: str = "Cancel", active: bool | Literal[0, 1] = 1) -> Control: ... + def next(self, title, next, name: str = "Next", active: bool | Literal[0, 1] = 1) -> Control: ... + def xbutton(self, name, title, next, xpos) -> Control: ... class bdist_msi(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - all_versions: Any + all_versions: Incomplete other_version: str if sys.version_info >= (3, 9): def __init__(self, *args, **kw) -> None: ... - bdist_dir: Any - plat_name: Any + bdist_dir: Incomplete + plat_name: Incomplete keep_temp: int no_target_compile: int no_target_optimize: int - target_version: Any - dist_dir: Any - skip_build: Any - install_script: Any - pre_install_script: Any - versions: Any + target_version: Incomplete + dist_dir: Incomplete + skip_build: Incomplete + install_script: Incomplete + pre_install_script: Incomplete + versions: Incomplete def initialize_options(self) -> None: ... - install_script_key: Any + install_script_key: Incomplete def finalize_options(self) -> None: ... - db: Any + db: Incomplete def run(self) -> None: ... def add_files(self) -> None: ... def add_find_python(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi index 89c43e1b974c..83b4161094c5 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -7,44 +8,44 @@ class bdist_rpm(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - bdist_base: Any - rpm_base: Any - dist_dir: Any - python: Any - fix_python: Any - spec_only: Any - binary_only: Any - source_only: Any - use_bzip2: Any - distribution_name: Any - group: Any - release: Any - serial: Any - vendor: Any - packager: Any - doc_files: Any - changelog: Any - icon: Any - prep_script: Any - build_script: Any - install_script: Any - clean_script: Any - verify_script: Any - pre_install: Any - post_install: Any - pre_uninstall: Any - post_uninstall: Any - prep: Any - provides: Any - requires: Any - conflicts: Any - build_requires: Any - obsoletes: Any + bdist_base: Incomplete + rpm_base: Incomplete + dist_dir: Incomplete + python: Incomplete + fix_python: Incomplete + spec_only: Incomplete + binary_only: Incomplete + source_only: Incomplete + use_bzip2: Incomplete + distribution_name: Incomplete + group: Incomplete + release: Incomplete + serial: Incomplete + vendor: Incomplete + packager: Incomplete + doc_files: Incomplete + changelog: Incomplete + icon: Incomplete + prep_script: Incomplete + build_script: Incomplete + install_script: Incomplete + clean_script: Incomplete + verify_script: Incomplete + pre_install: Incomplete + post_install: Incomplete + pre_uninstall: Incomplete + post_uninstall: Incomplete + prep: Incomplete + provides: Incomplete + requires: Incomplete + conflicts: Incomplete + build_requires: Incomplete + obsoletes: Incomplete keep_temp: int use_rpm_opt_flags: int rpm3_mode: int no_autoreq: int - force_arch: Any + force_arch: Incomplete quiet: int def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build.pyi b/mypy/typeshed/stdlib/distutils/command/build.pyi index 78ba6b7042dc..3ec0c9614d62 100644 --- a/mypy/typeshed/stdlib/distutils/command/build.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build.pyi @@ -1,4 +1,4 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -12,17 +12,17 @@ class build(Command): boolean_options: ClassVar[list[str]] help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] build_base: str - build_purelib: Any - build_platlib: Any - build_lib: Any - build_temp: Any - build_scripts: Any - compiler: Any - plat_name: Any - debug: Any + build_purelib: Incomplete + build_platlib: Incomplete + build_lib: Incomplete + build_temp: Incomplete + build_scripts: Incomplete + compiler: Incomplete + plat_name: Incomplete + debug: Incomplete force: int - executable: Any - parallel: Any + executable: Incomplete + parallel: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi index 1f66e2efc20c..69cfbe7120d8 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi @@ -1,6 +1,6 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +from typing import ClassVar from ..cmd import Command @@ -11,15 +11,15 @@ class build_clib(Command): user_options: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] - build_clib: Any - build_temp: Any - libraries: Any - include_dirs: Any - define: Any - undef: Any - debug: Any + build_clib: Incomplete + build_temp: Incomplete + libraries: Incomplete + include_dirs: Incomplete + define: Incomplete + undef: Incomplete + debug: Incomplete force: int - compiler: Any + compiler: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi index a0813c314021..c5a9b5d508f0 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi @@ -1,40 +1,40 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +from typing import ClassVar from ..cmd import Command -extension_name_re: Any +extension_name_re: Incomplete def show_compilers() -> None: ... class build_ext(Command): description: str - sep_by: Any + sep_by: Incomplete user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] - extensions: Any - build_lib: Any - plat_name: Any - build_temp: Any + extensions: Incomplete + build_lib: Incomplete + plat_name: Incomplete + build_temp: Incomplete inplace: int - package: Any - include_dirs: Any - define: Any - undef: Any - libraries: Any - library_dirs: Any - rpath: Any - link_objects: Any - debug: Any - force: Any - compiler: Any - swig: Any - swig_cpp: Any - swig_opts: Any - user: Any - parallel: Any + package: Incomplete + include_dirs: Incomplete + define: Incomplete + undef: Incomplete + libraries: Incomplete + library_dirs: Incomplete + rpath: Incomplete + link_objects: Incomplete + debug: Incomplete + force: Incomplete + compiler: Incomplete + swig: Incomplete + swig_cpp: Incomplete + swig_opts: Incomplete + user: Incomplete + parallel: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_py.pyi b/mypy/typeshed/stdlib/distutils/command/build_py.pyi index 90f06751416a..23ed230bb2d8 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_py.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_py.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar, Literal +from _typeshed import Incomplete +from typing import ClassVar, Literal from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 @@ -8,17 +9,17 @@ class build_py(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - build_lib: Any - py_modules: Any - package: Any - package_data: Any - package_dir: Any + build_lib: Incomplete + py_modules: Incomplete + package: Incomplete + package_data: Incomplete + package_dir: Incomplete compile: int optimize: int - force: Any + force: Incomplete def initialize_options(self) -> None: ... - packages: Any - data_files: Any + packages: Incomplete + data_files: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def get_data_files(self): ... @@ -32,13 +33,13 @@ class build_py(Command): def find_all_modules(self): ... def get_source_files(self): ... def get_module_outfile(self, build_dir, package, module): ... - def get_outputs(self, include_bytecode: bool | Literal[0, 1] = 1): ... + def get_outputs(self, include_bytecode: bool | Literal[0, 1] = 1) -> list[str]: ... def build_module(self, module, module_file, package): ... def build_modules(self) -> None: ... def build_packages(self) -> None: ... def byte_compile(self, files) -> None: ... class build_py_2to3(build_py, Mixin2to3): - updated_files: Any + updated_files: Incomplete def run(self) -> None: ... def build_module(self, module, module_file, package): ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi index 7871bb8a5719..8372919bbd53 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi @@ -1,19 +1,20 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 -first_line_re: Any +first_line_re: Incomplete class build_scripts(Command): description: str user_options: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] - build_dir: Any - scripts: Any - force: Any - executable: Any - outfiles: Any + build_dir: Incomplete + scripts: Incomplete + force: Incomplete + executable: Incomplete + outfiles: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def get_source_files(self): ... diff --git a/mypy/typeshed/stdlib/distutils/command/check.pyi b/mypy/typeshed/stdlib/distutils/command/check.pyi index e69627d20c7a..2c807fd2c439 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias @@ -9,13 +10,13 @@ _Reporter: TypeAlias = Any # really docutils.utils.Reporter # Depends on a third-party stub. Since distutils is deprecated anyway, # it's easier to just suppress the "any subclassing" error. class SilentReporter(_Reporter): - messages: Any + messages: Incomplete def __init__( self, source, report_level, halt_level, - stream: Any | None = ..., + stream: Incomplete | None = ..., debug: bool | Literal[0, 1] = 0, encoding: str = ..., error_handler: str = ..., diff --git a/mypy/typeshed/stdlib/distutils/command/clean.pyi b/mypy/typeshed/stdlib/distutils/command/clean.pyi index 55f0a0eeaf10..0f3768d6dcf4 100644 --- a/mypy/typeshed/stdlib/distutils/command/clean.pyi +++ b/mypy/typeshed/stdlib/distutils/command/clean.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,12 +7,12 @@ class clean(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - build_base: Any - build_lib: Any - build_temp: Any - build_scripts: Any - bdist_base: Any - all: Any + build_base: Incomplete + build_lib: Incomplete + build_temp: Incomplete + build_scripts: Incomplete + bdist_base: Incomplete + all: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi index b0910091d5b6..562ff3a5271f 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,7 +1,7 @@ -from _typeshed import StrOrBytesPath +from _typeshed import Incomplete, StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any, ClassVar, Final, Literal +from typing import ClassVar, Final, Literal from ..ccompiler import CCompiler from ..cmd import Command @@ -81,4 +81,4 @@ class config(Command): self, header: str, include_dirs: Sequence[str] | None = None, library_dirs: Sequence[str] | None = None, lang: str = "c" ) -> bool: ... -def dump_file(filename: StrOrBytesPath, head: Any | None = None) -> None: ... +def dump_file(filename: StrOrBytesPath, head: Incomplete | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index 24a4eff2fb10..1714e01a2c28 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import Incomplete from collections.abc import Callable from typing import Any, ClassVar, Final, Literal @@ -18,33 +19,33 @@ class install(Command): boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] prefix: str | None - exec_prefix: Any + exec_prefix: Incomplete home: str | None user: bool - install_base: Any - install_platbase: Any + install_base: Incomplete + install_platbase: Incomplete root: str | None - install_purelib: Any - install_platlib: Any - install_headers: Any + install_purelib: Incomplete + install_platlib: Incomplete + install_headers: Incomplete install_lib: str | None - install_scripts: Any - install_data: Any - install_userbase: Any - install_usersite: Any - compile: Any - optimize: Any - extra_path: Any + install_scripts: Incomplete + install_data: Incomplete + install_userbase: Incomplete + install_usersite: Incomplete + compile: Incomplete + optimize: Incomplete + extra_path: Incomplete install_path_file: int force: int skip_build: int warn_dir: int - build_base: Any - build_lib: Any - record: Any + build_base: Incomplete + build_lib: Incomplete + record: Incomplete def initialize_options(self) -> None: ... - config_vars: Any - install_libbase: Any + config_vars: Incomplete + install_libbase: Incomplete def finalize_options(self) -> None: ... def dump_dirs(self, msg) -> None: ... def finalize_unix(self) -> None: ... @@ -53,8 +54,8 @@ class install(Command): def expand_basedirs(self) -> None: ... def expand_dirs(self) -> None: ... def convert_paths(self, *names) -> None: ... - path_file: Any - extra_dirs: Any + path_file: Incomplete + extra_dirs: Incomplete def handle_extra_path(self) -> None: ... def change_roots(self, *names) -> None: ... def create_home_path(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_data.pyi b/mypy/typeshed/stdlib/distutils/command/install_data.pyi index 342c7a7ccca4..609de62b04b5 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_data.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_data.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,11 +7,11 @@ class install_data(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any - outfiles: Any - root: Any + install_dir: Incomplete + outfiles: Incomplete + root: Incomplete force: int - data_files: Any + data_files: Incomplete warn_dir: int def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi index 3fd54989d14f..75bb906ce582 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi @@ -1,14 +1,15 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class install_egg_info(Command): description: ClassVar[str] user_options: ClassVar[list[tuple[str, str, str]]] - install_dir: Any + install_dir: Incomplete def initialize_options(self) -> None: ... - target: Any - outputs: Any + target: Incomplete + outputs: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def get_outputs(self) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi index 7854d2393a98..3caad8a07dca 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,9 +7,9 @@ class install_headers(Command): description: str user_options: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any + install_dir: Incomplete force: int - outfiles: Any + outfiles: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi index 149ecae89781..a537e254904a 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar, Final +from _typeshed import Incomplete +from typing import ClassVar, Final from ..cmd import Command @@ -9,12 +10,12 @@ class install_lib(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - install_dir: Any - build_dir: Any + install_dir: Incomplete + build_dir: Incomplete force: int - compile: Any - optimize: Any - skip_build: Any + compile: Incomplete + optimize: Incomplete + skip_build: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi index 5ee5589ad33d..658594f32e43 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,13 +7,13 @@ class install_scripts(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any + install_dir: Incomplete force: int - build_dir: Any - skip_build: Any + build_dir: Incomplete + skip_build: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... - outfiles: Any + outfiles: Incomplete def run(self) -> None: ... def get_inputs(self): ... def get_outputs(self): ... diff --git a/mypy/typeshed/stdlib/distutils/command/register.pyi b/mypy/typeshed/stdlib/distutils/command/register.pyi index a5e251d2d01e..cf98e178a9ba 100644 --- a/mypy/typeshed/stdlib/distutils/command/register.pyi +++ b/mypy/typeshed/stdlib/distutils/command/register.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete from collections.abc import Callable from typing import Any, ClassVar @@ -17,4 +18,4 @@ class register(PyPIRCCommand): def verify_metadata(self) -> None: ... def send_metadata(self) -> None: ... def build_post_data(self, action): ... - def post_to_server(self, data, auth: Any | None = None): ... + def post_to_server(self, data, auth: Incomplete | None = None): ... diff --git a/mypy/typeshed/stdlib/distutils/command/sdist.pyi b/mypy/typeshed/stdlib/distutils/command/sdist.pyi index 5b7fe2419551..48a140714dda 100644 --- a/mypy/typeshed/stdlib/distutils/command/sdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/sdist.pyi @@ -1,4 +1,4 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -16,22 +16,22 @@ class sdist(Command): # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] READMES: ClassVar[tuple[str, ...]] - template: Any - manifest: Any + template: Incomplete + manifest: Incomplete use_defaults: int prune: int manifest_only: int force_manifest: int - formats: Any + formats: Incomplete keep_temp: int - dist_dir: Any - archive_files: Any + dist_dir: Incomplete + archive_files: Incomplete metadata_check: int - owner: Any - group: Any + owner: Incomplete + group: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... - filelist: Any + filelist: Incomplete def run(self) -> None: ... def check_metadata(self) -> None: ... def get_file_list(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/upload.pyi b/mypy/typeshed/stdlib/distutils/command/upload.pyi index e6b77825c5f5..afcfbaf48677 100644 --- a/mypy/typeshed/stdlib/distutils/command/upload.pyi +++ b/mypy/typeshed/stdlib/distutils/command/upload.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..config import PyPIRCCommand @@ -8,10 +9,10 @@ class upload(PyPIRCCommand): password: str show_response: int sign: bool - identity: Any + identity: Incomplete def initialize_options(self) -> None: ... - repository: Any - realm: Any + repository: Incomplete + realm: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def upload_file(self, command: str, pyversion: str, filename: str) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index a4d21f8ddd7b..174f24991351 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -1,4 +1,4 @@ -from _typeshed import StrOrBytesPath +from _typeshed import Incomplete, StrOrBytesPath from collections.abc import Mapping from distutils.cmd import Command as Command from distutils.dist import Distribution as Distribution @@ -32,7 +32,7 @@ def setup( distclass: type[Distribution] = ..., script_name: str = ..., script_args: list[str] = ..., - options: Mapping[str, Any] = ..., + options: Mapping[str, Incomplete] = ..., license: str = ..., keywords: list[str] | str = ..., platforms: list[str] | str = ..., @@ -43,7 +43,7 @@ def setup( provides: list[str] = ..., requires: list[str] = ..., command_packages: list[str] = ..., - command_options: Mapping[str, Mapping[str, tuple[Any, Any]]] = ..., + command_options: Mapping[str, Mapping[str, tuple[Incomplete, Incomplete]]] = ..., package_data: Mapping[str, list[str]] = ..., include_package_data: bool | Literal[0, 1] = ..., libraries: list[str] = ..., @@ -52,6 +52,7 @@ def setup( include_dirs: list[str] = ..., password: str = ..., fullname: str = ..., + # Custom Distributions could accept more params **attrs: Any, ) -> Distribution: ... def run_setup(script_name: str, script_args: list[str] | None = None, stop_after: str = "run") -> Distribution: ... diff --git a/mypy/typeshed/stdlib/distutils/log.pyi b/mypy/typeshed/stdlib/distutils/log.pyi index 0ea135c28371..7246dd6be0cd 100644 --- a/mypy/typeshed/stdlib/distutils/log.pyi +++ b/mypy/typeshed/stdlib/distutils/log.pyi @@ -8,6 +8,7 @@ FATAL: Final = 5 class Log: def __init__(self, threshold: int = 3) -> None: ... + # Arbitrary msg args' type depends on the format method def log(self, level: int, msg: str, *args: Any) -> None: ... def debug(self, msg: str, *args: Any) -> None: ... def info(self, msg: str, *args: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 1635b6a0a072..18076011178f 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -28,7 +28,7 @@ _Predicate: TypeAlias = Callable[[_T], object] # Technically count can take anything that implements a number protocol and has an add method # but we can't enforce the add method -class count(Iterator[_N]): +class count(Generic[_N]): @overload def __new__(cls) -> count[int]: ... @overload @@ -38,12 +38,12 @@ class count(Iterator[_N]): def __next__(self) -> _N: ... def __iter__(self) -> Self: ... -class cycle(Iterator[_T]): +class cycle(Generic[_T]): def __init__(self, iterable: Iterable[_T], /) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... -class repeat(Iterator[_T]): +class repeat(Generic[_T]): @overload def __init__(self, object: _T) -> None: ... @overload @@ -52,7 +52,7 @@ class repeat(Iterator[_T]): def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... -class accumulate(Iterator[_T]): +class accumulate(Generic[_T]): @overload def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... @overload @@ -60,7 +60,7 @@ class accumulate(Iterator[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class chain(Iterator[_T]): +class chain(Generic[_T]): def __init__(self, *iterables: Iterable[_T]) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -70,22 +70,22 @@ class chain(Iterator[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class compress(Iterator[_T]): +class compress(Generic[_T]): def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class dropwhile(Iterator[_T]): +class dropwhile(Generic[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class filterfalse(Iterator[_T]): +class filterfalse(Generic[_T]): def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): +class groupby(Generic[_T_co, _S_co]): @overload def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... @overload @@ -93,7 +93,7 @@ class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... -class islice(Iterator[_T]): +class islice(Generic[_T]): @overload def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... @overload @@ -101,19 +101,19 @@ class islice(Iterator[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class starmap(Iterator[_T_co]): +class starmap(Generic[_T_co]): def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class takewhile(Iterator[_T]): +class takewhile(Generic[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... -class zip_longest(Iterator[_T_co]): +class zip_longest(Generic[_T_co]): # one iterable (fillvalue doesn't matter) @overload def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... @@ -191,7 +191,7 @@ class zip_longest(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class product(Iterator[_T_co]): +class product(Generic[_T_co]): @overload def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... @overload @@ -276,7 +276,7 @@ class product(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class permutations(Iterator[_T_co]): +class permutations(Generic[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... @overload @@ -290,7 +290,7 @@ class permutations(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations(Iterator[_T_co]): +class combinations(Generic[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... @overload @@ -304,7 +304,7 @@ class combinations(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations_with_replacement(Iterator[_T_co]): +class combinations_with_replacement(Generic[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... @overload @@ -319,13 +319,13 @@ class combinations_with_replacement(Iterator[_T_co]): def __next__(self) -> _T_co: ... if sys.version_info >= (3, 10): - class pairwise(Iterator[_T_co]): + class pairwise(Generic[_T_co]): def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... if sys.version_info >= (3, 12): - class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): + class batched(Generic[_T_co]): if sys.version_info >= (3, 13): def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... else: From cb9dd7d201f58bfce0f708b9d820da5c7fedf4c4 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:25:20 -0700 Subject: [PATCH 0848/1617] [mypyc] fix relative imports in __init__.py (#17979) Fixes https://github.com/mypyc/mypyc/issues/1070 Missed in https://github.com/python/mypy/pull/10543 --- mypyc/irbuild/statement.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 4d828b1b9d82..ac3fa33f36ca 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -347,10 +347,10 @@ def transform_import_from(builder: IRBuilder, node: ImportFrom) -> None: return module_state = builder.graph[builder.module_name] - if module_state.ancestors is not None and module_state.ancestors: - module_package = module_state.ancestors[0] - elif builder.module_path.endswith("__init__.py"): + if builder.module_path.endswith("__init__.py"): module_package = builder.module_name + elif module_state.ancestors is not None and module_state.ancestors: + module_package = module_state.ancestors[0] else: module_package = "" From 5bae05dbf8c8a449dc320f8ba0da8727e78170f9 Mon Sep 17 00:00:00 2001 From: aatle <168398276+aatle@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:28:20 -0700 Subject: [PATCH 0849/1617] [mypyc] fix name generation for modules with similar full names (#18001) Fixes https://github.com/mypyc/mypyc/issues/1071 Adds a test to cover this case Building certain package layouts now succeeds instead of failing. The behavior for all package layouts not affected by the error is unchanged. In `namegen.make_module_translation_map(names)`, if argument `names` have `"foo"` and `"foo.foo"`, all suffixes found for `"foo"` are also found for `"foo.foo"`. This means that module `foo` has no unique suffixes, which currently causes an `AssertionError`. The fix forces a module to take the last, fullest suffix if none are unique. It is guaranteed that no other module will also take the same suffix because they either will have a unique suffix to take, or they will take the fullest suffix for their name which is always going to be different. --- mypyc/namegen.py | 5 ++--- mypyc/test/test_namegen.py | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mypyc/namegen.py b/mypyc/namegen.py index 675dae9001c7..ce84fde143d1 100644 --- a/mypyc/namegen.py +++ b/mypyc/namegen.py @@ -100,10 +100,9 @@ def make_module_translation_map(names: list[str]) -> dict[str, str]: for name in names: for suffix in candidate_suffixes(name): if num_instances[suffix] == 1: - result[name] = suffix break - else: - assert False, names + # Takes the last suffix if none are unique + result[name] = suffix return result diff --git a/mypyc/test/test_namegen.py b/mypyc/test/test_namegen.py index 509018b4c3bd..f88edbd00dce 100644 --- a/mypyc/test/test_namegen.py +++ b/mypyc/test/test_namegen.py @@ -35,6 +35,12 @@ def test_make_module_translation_map(self) -> None: "fu.bar": "fu.bar.", "foo.baz": "baz.", } + assert make_module_translation_map(["foo", "foo.foo", "bar.foo", "bar.foo.bar.foo"]) == { + "foo": "foo.", + "foo.foo": "foo.foo.", + "bar.foo": "bar.foo.", + "bar.foo.bar.foo": "foo.bar.foo.", + } def test_name_generator(self) -> None: g = NameGenerator([["foo", "foo.zar"]]) From 8a0dd7ad6c7a30170de3b87f997577b8c86e2451 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:26:55 +0200 Subject: [PATCH 0850/1617] Add timeout-minutes to ci config (#18003) Prevent blocking resources unnecessarily if something goes wrong and a CI task doesn't terminate. The default timeout would be 360 minutes. --- .github/workflows/docs.yml | 1 + .github/workflows/mypy_primer.yml | 1 + .github/workflows/sync_typeshed.yml | 1 + .github/workflows/test.yml | 2 ++ .github/workflows/test_stubgenc.yml | 1 + 5 files changed, 6 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 923d74a02f71..112102954dd3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,6 +28,7 @@ concurrency: jobs: docs: runs-on: ubuntu-latest + timeout-minutes: 10 env: TOXENV: docs TOX_SKIP_MISSING_INTERPRETERS: False diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index fee7e6d079f1..2b2327798a72 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -32,6 +32,7 @@ jobs: matrix: shard-index: [0, 1, 2, 3, 4] fail-fast: false + timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index b545e7b0662b..84d246441f3d 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -14,6 +14,7 @@ jobs: name: Sync typeshed if: github.repository == 'python/mypy' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4150d1968bfb..9e6c9cd1d9b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -126,6 +126,7 @@ jobs: toxenv: lint name: ${{ matrix.name }} + timeout-minutes: 60 env: TOX_SKIP_MISSING_INTERPRETERS: False # Rich (pip) -- Disable color for windows + pytest @@ -205,6 +206,7 @@ jobs: python_32bits: runs-on: ubuntu-latest name: Test mypyc suite with 32-bit Python + timeout-minutes: 60 env: TOX_SKIP_MISSING_INTERPRETERS: False # Rich (pip) diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 519f63ac2bd7..0652702a0fc0 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -25,6 +25,7 @@ jobs: # Check stub file generation for a small pybind11 project # (full text match is required to pass) runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 From e106dd7a0653c24d67597adf3ae6939d7ff9a376 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:10:08 -0700 Subject: [PATCH 0851/1617] Changelog for 1.13 (#18000) --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0cbe466a5d5..801d592945c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,49 @@ ## Next release +## Mypy 1.13 + +We’ve just uploaded mypy 1.13 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +Mypy is a static type checker for Python. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +Note that unlike typical releases, Mypy 1.13 does not have any changes to type checking semantics +from 1.12.1. + +### Improved performance + +Mypy 1.13 contains several performance improvements. Users can expect mypy to be 5-20% faster. +In environments with long search paths (such as environments using many editable installs), mypy +can be significantly faster, e.g. 2.2x faster in the use case targeted by these improvements. + +Mypy 1.13 allows use of the `orjson` library for handling the cache instead of the stdlib `json`, +for improved performance. You can ensure the presence of `orjson` using the `faster-cache` extra: + + python3 -m pip install -U mypy[faster-cache] + +Mypy may depend on `orjson` by default in the future. + +These improvements were contributed by Shantanu. + +List of changes: +* Significantly speed up file handling error paths (Shantanu, PR [17920](https://github.com/python/mypy/pull/17920)) +* Use fast path in modulefinder more often (Shantanu, PR [17950](https://github.com/python/mypy/pull/17950)) +* Let mypyc optimise os.path.join (Shantanu, PR [17949](https://github.com/python/mypy/pull/17949)) +* Make is_sub_path faster (Shantanu, PR [17962](https://github.com/python/mypy/pull/17962)) +* Speed up stubs suggestions (Shantanu, PR [17965](https://github.com/python/mypy/pull/17965)) +* Use sha1 for hashing (Shantanu, PR [17953](https://github.com/python/mypy/pull/17953)) +* Use orjson instead of json, when available (Shantanu, PR [17955](https://github.com/python/mypy/pull/17955)) +* Add faster-cache extra, test in CI (Shantanu, PR [17978](https://github.com/python/mypy/pull/17978)) + +### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Shantanu Jain +- Jukka Lehtosalo + ## Mypy 1.12 We’ve just uploaded mypy 1.12 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type From 60d1b3776229d3e3c332413ef49ad89be469a5e4 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 22 Oct 2024 23:19:00 -0400 Subject: [PATCH 0852/1617] Fix compatibility checks for conditional function definitions using decorators (#18020) Fixes #17211, resolves this `# TODO`: https://github.com/python/mypy/blob/e106dd7a0653c24d67597adf3ae6939d7ff9a376/test-data/unit/check-functions.test#L1486-L1494 ### Before ```python from typing import Callable def dec(f: object) -> Callable[[int], None]: raise NotImplementedError if int(): def f(x: str) -> None: pass else: @dec def f() -> None: pass # uh oh! passes without error ``` ### After ```python from typing import Callable def dec(f: object) -> Callable[[int], None]: raise NotImplementedError if int(): def f(x: str) -> None: pass else: @dec def f() -> None: pass # E: All conditional function variants must have identical signatures \ # N: Original: \ # N: def f(x: str) -> None \ # N: Redefinition: \ # N: def f(int, /) -> None ``` --- mypy/checker.py | 88 +++++++++++++++------------ test-data/unit/check-functions.test | 11 ++-- test-data/unit/check-newsemanal.test | 4 +- test-data/unit/check-overloading.test | 6 +- 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4b3d6c3298b4..f52bebdaa052 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1072,46 +1072,7 @@ def _visit_func_def(self, defn: FuncDef) -> None: if defn.original_def: # Override previous definition. new_type = self.function_type(defn) - if isinstance(defn.original_def, FuncDef): - # Function definition overrides function definition. - old_type = self.function_type(defn.original_def) - if not is_same_type(new_type, old_type): - self.msg.incompatible_conditional_function_def(defn, old_type, new_type) - else: - # Function definition overrides a variable initialized via assignment or a - # decorated function. - orig_type = defn.original_def.type - if orig_type is None: - # If other branch is unreachable, we don't type check it and so we might - # not have a type for the original definition - return - if isinstance(orig_type, PartialType): - if orig_type.type is None: - # Ah this is a partial type. Give it the type of the function. - orig_def = defn.original_def - if isinstance(orig_def, Decorator): - var = orig_def.var - else: - var = orig_def - partial_types = self.find_partial_types(var) - if partial_types is not None: - var.type = new_type - del partial_types[var] - else: - # Trying to redefine something like partial empty list as function. - self.fail(message_registry.INCOMPATIBLE_REDEFINITION, defn) - else: - name_expr = NameExpr(defn.name) - name_expr.node = defn.original_def - self.binder.assign_type(name_expr, new_type, orig_type) - self.check_subtype( - new_type, - orig_type, - defn, - message_registry.INCOMPATIBLE_REDEFINITION, - "redefinition with type", - "original type", - ) + self.check_func_def_override(defn, new_type) def check_func_item( self, @@ -1147,6 +1108,49 @@ def check_func_item( if dataclasses_plugin.is_processed_dataclass(defn.info): dataclasses_plugin.check_post_init(self, defn, defn.info) + def check_func_def_override(self, defn: FuncDef, new_type: FunctionLike) -> None: + assert defn.original_def is not None + if isinstance(defn.original_def, FuncDef): + # Function definition overrides function definition. + old_type = self.function_type(defn.original_def) + if not is_same_type(new_type, old_type): + self.msg.incompatible_conditional_function_def(defn, old_type, new_type) + else: + # Function definition overrides a variable initialized via assignment or a + # decorated function. + orig_type = defn.original_def.type + if orig_type is None: + # If other branch is unreachable, we don't type check it and so we might + # not have a type for the original definition + return + if isinstance(orig_type, PartialType): + if orig_type.type is None: + # Ah this is a partial type. Give it the type of the function. + orig_def = defn.original_def + if isinstance(orig_def, Decorator): + var = orig_def.var + else: + var = orig_def + partial_types = self.find_partial_types(var) + if partial_types is not None: + var.type = new_type + del partial_types[var] + else: + # Trying to redefine something like partial empty list as function. + self.fail(message_registry.INCOMPATIBLE_REDEFINITION, defn) + else: + name_expr = NameExpr(defn.name) + name_expr.node = defn.original_def + self.binder.assign_type(name_expr, new_type, orig_type) + self.check_subtype( + new_type, + orig_type, + defn, + message_registry.INCOMPATIBLE_REDEFINITION, + "redefinition with type", + "original type", + ) + @contextmanager def enter_attribute_inference_context(self) -> Iterator[None]: old_types = self.inferred_attribute_types @@ -5120,6 +5124,10 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None if e.type and not isinstance(get_proper_type(e.type), (FunctionLike, AnyType)): self.fail(message_registry.BAD_CONSTRUCTOR_TYPE, e) + if e.func.original_def and isinstance(sig, FunctionLike): + # Function definition overrides function definition. + self.check_func_def_override(e.func, sig) + def check_for_untyped_decorator( self, func: FuncDef, dec_type: Type, dec_expr: Expression ) -> None: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 96f9815019e6..b8a02a1ec7d4 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1474,7 +1474,7 @@ def dec(f) -> Callable[[int], None]: pass x = int() if x: - def f(x: int) -> None: pass + def f(x: int, /) -> None: pass else: @dec def f(): pass @@ -1489,9 +1489,12 @@ x = int() if x: def f(x: str) -> None: pass else: - # TODO: Complain about incompatible redefinition @dec - def f(): pass + def f(): pass # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def f(x: str) -> None \ + # N: Redefinition: \ + # N: def f(int, /) -> None [case testConditionalFunctionDefinitionUnreachable] def bar() -> None: @@ -1599,7 +1602,7 @@ else: def f(): yield [file m.py] -def f(): pass +def f() -> None: pass [case testDefineConditionallyAsImportedAndDecoratedWithInference] if int(): diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 511c7b003015..fe02ac3ccd5e 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -1908,9 +1908,9 @@ else: @dec def f(x: int) -> None: 1() # E: "int" not callable -reveal_type(f) # N: Revealed type is "def (x: builtins.str)" +reveal_type(f) # N: Revealed type is "def (builtins.str)" [file m.py] -def f(x: str) -> None: pass +def f(x: str, /) -> None: pass [case testNewAnalyzerConditionallyDefineFuncOverVar] from typing import Callable diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index e414c1c9b0b6..9d01ce6bd480 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6463,7 +6463,7 @@ class D: ... def f1(g: A) -> A: ... if True: @overload # E: Single overload definition, multiple required - def f1(g: B) -> B: ... + def f1(g: B) -> B: ... # E: Incompatible redefinition (redefinition with type "Callable[[B], B]", original type "Callable[[A], A]") if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload @@ -6480,14 +6480,14 @@ if True: def f2(g: B) -> B: ... elif maybe_true: # E: Name "maybe_true" is not defined @overload # E: Single overload definition, multiple required - def f2(g: C) -> C: ... + def f2(g: C) -> C: ... # E: Incompatible redefinition (redefinition with type "Callable[[C], C]", original type "Callable[[A], A]") def f2(g): ... # E: Name "f2" already defined on line 21 @overload # E: Single overload definition, multiple required def f3(g: A) -> A: ... if True: @overload # E: Single overload definition, multiple required - def f3(g: B) -> B: ... + def f3(g: B) -> B: ... # E: Incompatible redefinition (redefinition with type "Callable[[B], B]", original type "Callable[[A], A]") if True: pass # Some other node @overload # E: Name "f3" already defined on line 32 \ From 9e689593c9b99555631c2e2bcc05cb5481a557fb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:52:29 +0200 Subject: [PATCH 0853/1617] Improve for loop index variable type narrowing (#18014) Preserve the literal type of index expressions a bit longer (until the next assignment) to support TypedDict lookups. ```py from typing import TypedDict class X(TypedDict): hourly: int daily: int def func(x: X) -> None: for var in ("hourly", "daily"): print(x[var]) ``` Closes #9230 --- mypy/checker.py | 9 +++++++++ mypy/nodes.py | 3 +++ mypy/semanal.py | 18 +++++++++++++++--- test-data/unit/check-inference.test | 29 +++++++++++++++++++++++++++++ test-data/unit/fixtures/for.pyi | 4 +++- 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index f52bebdaa052..3e433408e40b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3175,6 +3175,14 @@ def check_assignment( # Don't use type binder for definitions of special forms, like named tuples. if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form): self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False) + if ( + isinstance(lvalue, NameExpr) + and isinstance(lvalue.node, Var) + and lvalue.node.is_inferred + and lvalue.node.is_index_var + and lvalue_type is not None + ): + lvalue.node.type = remove_instance_last_known_values(lvalue_type) elif index_lvalue: self.check_indexed_assignment(index_lvalue, rvalue, lvalue) @@ -3184,6 +3192,7 @@ def check_assignment( rvalue_type = self.expr_checker.accept(rvalue, type_context=type_context) if not ( inferred.is_final + or inferred.is_index_var or (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__") ): rvalue_type = remove_instance_last_known_values(rvalue_type) diff --git a/mypy/nodes.py b/mypy/nodes.py index 4806a7b0be7d..7b620bd52008 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -969,6 +969,7 @@ def is_dynamic(self) -> bool: "is_classvar", "is_abstract_var", "is_final", + "is_index_var", "final_unset_in_class", "final_set_in_init", "explicit_self_type", @@ -1005,6 +1006,7 @@ class Var(SymbolNode): "is_classvar", "is_abstract_var", "is_final", + "is_index_var", "final_unset_in_class", "final_set_in_init", "is_suppressed_import", @@ -1039,6 +1041,7 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None: self.is_settable_property = False self.is_classvar = False self.is_abstract_var = False + self.is_index_var = False # Set to true when this variable refers to a module we were unable to # parse for some reason (eg a silenced module) self.is_suppressed_import = False diff --git a/mypy/semanal.py b/mypy/semanal.py index a0d4daffca0c..e182322cfbe6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4225,6 +4225,7 @@ def analyze_lvalue( is_final: bool = False, escape_comprehensions: bool = False, has_explicit_value: bool = False, + is_index_var: bool = False, ) -> None: """Analyze an lvalue or assignment target. @@ -4235,6 +4236,7 @@ def analyze_lvalue( escape_comprehensions: If we are inside a comprehension, set the variable in the enclosing scope instead. This implements https://www.python.org/dev/peps/pep-0572/#scope-of-the-target + is_index_var: If lval is the index variable in a for loop """ if escape_comprehensions: assert isinstance(lval, NameExpr), "assignment expression target must be NameExpr" @@ -4245,6 +4247,7 @@ def analyze_lvalue( is_final, escape_comprehensions, has_explicit_value=has_explicit_value, + is_index_var=is_index_var, ) elif isinstance(lval, MemberExpr): self.analyze_member_lvalue(lval, explicit_type, is_final, has_explicit_value) @@ -4271,6 +4274,7 @@ def analyze_name_lvalue( is_final: bool, escape_comprehensions: bool, has_explicit_value: bool, + is_index_var: bool, ) -> None: """Analyze an lvalue that targets a name expression. @@ -4309,7 +4313,9 @@ def analyze_name_lvalue( if (not existing or isinstance(existing.node, PlaceholderNode)) and not outer: # Define new variable. - var = self.make_name_lvalue_var(lvalue, kind, not explicit_type, has_explicit_value) + var = self.make_name_lvalue_var( + lvalue, kind, not explicit_type, has_explicit_value, is_index_var + ) added = self.add_symbol(name, var, lvalue, escape_comprehensions=escape_comprehensions) # Only bind expression if we successfully added name to symbol table. if added: @@ -4361,7 +4367,12 @@ def is_alias_for_final_name(self, name: str) -> bool: return existing is not None and is_final_node(existing.node) def make_name_lvalue_var( - self, lvalue: NameExpr, kind: int, inferred: bool, has_explicit_value: bool + self, + lvalue: NameExpr, + kind: int, + inferred: bool, + has_explicit_value: bool, + is_index_var: bool, ) -> Var: """Return a Var node for an lvalue that is a name expression.""" name = lvalue.name @@ -4380,6 +4391,7 @@ def make_name_lvalue_var( v._fullname = name v.is_ready = False # Type not inferred yet v.has_explicit_value = has_explicit_value + v.is_index_var = is_index_var return v def make_name_lvalue_point_to_existing_def( @@ -5290,7 +5302,7 @@ def visit_for_stmt(self, s: ForStmt) -> None: s.expr.accept(self) # Bind index variables and check if they define new names. - self.analyze_lvalue(s.index, explicit_type=s.index_type is not None) + self.analyze_lvalue(s.index, explicit_type=s.index_type is not None, is_index_var=True) if s.index_type: if self.is_classvar(s.index_type): self.fail_invalid_classvar(s.index) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0dbefbc774a3..b0ec590b54f9 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1238,6 +1238,35 @@ class B: pass [builtins fixtures/for.pyi] [out] +[case testForStatementIndexNarrowing] +from typing_extensions import TypedDict + +class X(TypedDict): + hourly: int + daily: int + +x: X +for a in ("hourly", "daily"): + reveal_type(a) # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]" + reveal_type(x[a]) # N: Revealed type is "builtins.int" + reveal_type(a.upper()) # N: Revealed type is "builtins.str" + c = a + reveal_type(c) # N: Revealed type is "builtins.str" + a = "monthly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = "yearly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + d = a + reveal_type(d) # N: Revealed type is "builtins.str" + +b: str +for b in ("hourly", "daily"): + reveal_type(b) # N: Revealed type is "builtins.str" + reveal_type(b.upper()) # N: Revealed type is "builtins.str" +[builtins fixtures/for.pyi] + -- Regression tests -- ---------------- diff --git a/test-data/unit/fixtures/for.pyi b/test-data/unit/fixtures/for.pyi index 694f83e940b2..10f45e68cd7d 100644 --- a/test-data/unit/fixtures/for.pyi +++ b/test-data/unit/fixtures/for.pyi @@ -12,9 +12,11 @@ class type: pass class tuple(Generic[t]): def __iter__(self) -> Iterator[t]: pass class function: pass +class ellipsis: pass class bool: pass class int: pass # for convenience -class str: pass # for convenience +class str: # for convenience + def upper(self) -> str: ... class list(Iterable[t], Generic[t]): def __iter__(self) -> Iterator[t]: pass From eb0575eaa8720b2cdac62c7deb4c54c53035ea3b Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 23 Oct 2024 22:06:39 +0200 Subject: [PATCH 0854/1617] [stubtest] Verify __all__ exists in stub (#18005) Previously it wasn't an error if runtime included an `__all__` declaration, but the stubs did not. This PR changes this to reflect the consensus that it would be a good idea to ensure consistency in this case. Fixes #13300 --- mypy/stubtest.py | 2 ++ mypy/test/teststubtest.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 756f90dccf2e..0de5411b01de 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -348,6 +348,8 @@ def verify_mypyfile( # Only verify the contents of the stub's __all__ # if the stub actually defines __all__ yield from _verify_exported_names(object_path, stub, runtime_all_as_set) + else: + yield Error(object_path + ["__all__"], "is not present in stub", MISSING, runtime) else: runtime_all_as_set = None diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 70687b499651..4cab62875647 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1403,7 +1403,7 @@ def test_all_at_runtime_not_stub(self) -> Iterator[Case]: runtime=""" __all__ = [] Z = 5""", - error=None, + error="__all__", ) @collect_cases @@ -1443,7 +1443,7 @@ def h(x: str): ... runtime="", error="h", ) - yield Case(stub="", runtime="__all__ = []", error=None) # dummy case + yield Case(stub="", runtime="__all__ = []", error="__all__") # dummy case yield Case(stub="", runtime="__all__ += ['y']\ny = 5", error="y") yield Case(stub="", runtime="__all__ += ['g']\ndef g(): pass", error="g") # Here we should only check that runtime has B, since the stub explicitly re-exports it From 3420ef1554c40b433a638e31cb2109e591e85008 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 24 Oct 2024 11:36:01 +0100 Subject: [PATCH 0855/1617] Fix ternary union for literals (#18023) Fixes https://github.com/python/mypy/issues/18021 When I switched to (almost) always using unions as inferred from ternary expressions, I had a choice, because before we used either full context (i.e. l.h.s.) or the if type to infer the else type. After some playing I found the second one usually works better. But as we see this is not always the case, so I add some special-casing. --- mypy/checkexpr.py | 6 ++++++ test-data/unit/check-literal.test | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 08d7345452fb..22f4b05b7ad4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5805,6 +5805,12 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F context=if_type_fallback, allow_none_return=allow_none_return, ) + + # In most cases using if_type as a context for right branch gives better inferred types. + # This is however not the case for literal types, so use the full context instead. + if is_literal_type_like(full_context_else_type) and not is_literal_type_like(else_type): + else_type = full_context_else_type + res: Type = make_simplified_union([if_type, else_type]) if has_uninhabited_component(res) and not isinstance( get_proper_type(self.type_context[-1]), UnionType diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 6d76ce176aaf..2f94b5df0f83 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2960,3 +2960,27 @@ class C(B[Literal["word"]]): reveal_type(C().collection) # N: Revealed type is "builtins.list[Literal['word']]" reveal_type(C().word) # N: Revealed type is "Literal['word']" [builtins fixtures/tuple.pyi] + +[case testLiteralTernaryUnionNarrowing] +from typing_extensions import Literal +from typing import Optional + +SEP = Literal["a", "b"] + +class Base: + def feed_data( + self, + sep: SEP, + ) -> int: + return 0 + +class C(Base): + def feed_data( + self, + sep: Optional[SEP] = None, + ) -> int: + if sep is None: + sep = "a" if int() else "b" + reveal_type(sep) # N: Revealed type is "Union[Literal['a'], Literal['b']]" + return super().feed_data(sep) +[builtins fixtures/tuple.pyi] From 87fb470e873be84578f987a88e4b1ea23fa3df78 Mon Sep 17 00:00:00 2001 From: vasiliy <68805512+v-spassky@users.noreply.github.com> Date: Fri, 25 Oct 2024 02:56:22 +0300 Subject: [PATCH 0856/1617] [docs] fix broken markup in `type_narrowing.rst` (#18028) [Here](https://mypy.readthedocs.io/en/stable/type_narrowing.html#typeis), the markup is broken: ![Screenshot from 2024-10-24 14-46-10](https://github.com/user-attachments/assets/d3b2f6ee-a114-4a2b-b000-49667db50daf) After applying the changes: ![Screenshot from 2024-10-24 14-49-27](https://github.com/user-attachments/assets/2e362f88-5cc1-4e4e-852f-d1917a2d4ede) --- docs/source/type_narrowing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 230c40b30894..697a1519a603 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -400,11 +400,11 @@ Mypy supports TypeIs (:pep:`742`). A `TypeIs narrowing function `_ allows you to define custom type checks that can narrow the type of a variable -in `both the if and else _` +in `both the if and else `_ branches of a conditional, similar to how the built-in isinstance() function works. TypeIs is new in Python 3.13 — for use in older Python versions, use the backport -from `typing_extensions _` +from `typing_extensions `_ Consider the following example using TypeIs: From 776d01d842338af186b8f3075977630069271cf7 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 24 Oct 2024 21:17:23 -0700 Subject: [PATCH 0857/1617] allow the use of --show-traceback and --pdb with stubtest (#18037) Mypy's error handling suggests using -`-show-traceback` and `--pdb` for more information, and this can occur while running stubtest. This MR adds support for both flags to stubtest so that the suggestions can be followed without needing to figure out the equivalent mypy invocation first. --- mypy/stubtest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 0de5411b01de..36cd0a213d4d 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1893,6 +1893,8 @@ class _Arguments: custom_typeshed_dir: str | None check_typeshed: bool version: str + show_traceback: bool + pdb: bool # typeshed added a stub for __main__, but that causes stubtest to check itself @@ -1938,6 +1940,8 @@ def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: options.abs_custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) options.config_file = args.mypy_config_file options.use_builtins_fixtures = use_builtins_fixtures + options.show_traceback = args.show_traceback + options.pdb = args.pdb if options.config_file: @@ -2091,6 +2095,10 @@ def parse_options(args: list[str]) -> _Arguments: parser.add_argument( "--version", action="version", version="%(prog)s " + mypy.version.__version__ ) + parser.add_argument("--pdb", action="store_true", help="Invoke pdb on fatal error") + parser.add_argument( + "--show-traceback", "--tb", action="store_true", help="Show traceback on fatal error" + ) return parser.parse_args(args, namespace=_Arguments()) From 376e2ad72da9739f1c771ffa15e8ba0f847e3e71 Mon Sep 17 00:00:00 2001 From: jhance Date: Fri, 25 Oct 2024 02:08:51 -0700 Subject: [PATCH 0858/1617] [mypyc] Fix is_native_ref_expr for class attrs (#18031) This addresses https://github.com/mypyc/mypyc/issues/932 in which in some cases before we were incorrectly not determining that the symbol was native. Because the problematic implementation of `is_native_ref_expr` is present in multiple places, the one in `builder.py` is rewritten to use the one in `mapper.py`. Additional run tests are added to cover the case. Use of a dictionary here prevents falling under a secondary, more optimized codepath, that avoided the issue entirely. --- mypyc/irbuild/builder.py | 10 +++------- mypyc/irbuild/mapper.py | 4 +++- mypyc/irbuild/prepare.py | 2 ++ mypyc/test-data/run-classes.test | 12 ++++++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index a9e1ce471953..823f1581ba2e 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -979,17 +979,13 @@ def _analyze_iterable_item_type(self, expr: Expression) -> Type: def is_native_module(self, module: str) -> bool: """Is the given module one compiled by mypyc?""" - return module in self.mapper.group_map + return self.mapper.is_native_module(module) def is_native_ref_expr(self, expr: RefExpr) -> bool: - if expr.node is None: - return False - if "." in expr.node.fullname: - return self.is_native_module(expr.node.fullname.rpartition(".")[0]) - return True + return self.mapper.is_native_ref_expr(expr) def is_native_module_ref_expr(self, expr: RefExpr) -> bool: - return self.is_native_ref_expr(expr) and expr.kind == GDEF + return self.mapper.is_native_module_ref_expr(expr) def is_synthetic_type(self, typ: TypeInfo) -> bool: """Is a type something other than just a class we've created?""" diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 78e54aceed3d..9cd263c40ae4 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -62,6 +62,7 @@ def __init__(self, group_map: dict[str, str | None]) -> None: self.group_map = group_map self.type_to_ir: dict[TypeInfo, ClassIR] = {} self.func_to_decl: dict[SymbolNode, FuncDecl] = {} + self.symbol_fullnames: set[str] = set() def type_to_rtype(self, typ: Type | None) -> RType: if typ is None: @@ -217,7 +218,8 @@ def is_native_ref_expr(self, expr: RefExpr) -> bool: if expr.node is None: return False if "." in expr.node.fullname: - return self.is_native_module(expr.node.fullname.rpartition(".")[0]) + name = expr.node.fullname.rpartition(".")[0] + return self.is_native_module(name) or name in self.symbol_fullnames return True def is_native_module_ref_expr(self, expr: RefExpr) -> bool: diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index bed9a1684326..4b132bb83722 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -94,6 +94,7 @@ def build_type_map( if not options.global_opts: class_ir.children = None mapper.type_to_ir[cdef.info] = class_ir + mapper.symbol_fullnames.add(class_ir.fullname) # Populate structural information in class IR for extension classes. for module, cdef in classes: @@ -149,6 +150,7 @@ def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps) if isinstance(node.node, TypeInfo) and is_from_module(node.node, module): ir = deser_ctx.classes[node.node.fullname] mapper.type_to_ir[node.node] = ir + mapper.symbol_fullnames.add(node.node.fullname) mapper.func_to_decl[node.node] = ir.ctor for module in modules: diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 49eb3028c9ee..40287ab963aa 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2594,3 +2594,15 @@ def test_class_final_attribute_inherited() -> None: assert C().b == 2 assert B().c == 3 assert C().c == 3 + +[case testClassWithFinalAttributeAccess] +from typing import Final + +class C: + a: Final = {'x': 'y'} + b: Final = C.a + +def test_final_attribute() -> None: + assert C.a['x'] == 'y' + assert C.b['x'] == 'y' + assert C.a is C.b From 1cb8cc4de03ef2886121989a3792e01aefcd1fbb Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:16:08 +0200 Subject: [PATCH 0859/1617] Do not prioritize ParamSpec signatures during overload resolution (#18033) Closes #18027. Var-args and star-args overloads are handled first to handle functions like `zip` correctly in cases like `zip(*[[1],[2],[3]])`. That does not seem to be necessary in case of `ParamSpec` overloads (or at least such use case is much less obvious). So this PR prevents `ParamSpec` overloads from floating up in the list of overload targets. --- mypy/checkexpr.py | 6 ++-- .../unit/check-parameter-specification.test | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 22f4b05b7ad4..d63cf6e782c7 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2781,7 +2781,7 @@ def plausible_overload_call_targets( ) -> list[CallableType]: """Returns all overload call targets that having matching argument counts. - If the given args contains a star-arg (*arg or **kwarg argument, including + If the given args contains a star-arg (*arg or **kwarg argument, except for ParamSpec), this method will ensure all star-arg overloads appear at the start of the list, instead of their usual location. @@ -2816,7 +2816,9 @@ def has_shape(typ: Type) -> bool: # ParamSpec can be expanded in a lot of different ways. We may try # to expand it here instead, but picking an impossible overload # is safe: it will be filtered out later. - star_matches.append(typ) + # Unlike other var-args signatures, ParamSpec produces essentially + # a fixed signature, so there's no need to push them to the top. + matches.append(typ) elif self.check_argument_count( typ, arg_types, arg_kinds, arg_names, formal_to_actual, None ): diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 38fb62fe78e0..f499bac45102 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2303,3 +2303,38 @@ reveal_type(capture(fn)) # N: Revealed type is "Union[builtins.str, builtins.in reveal_type(capture(err)) # N: Revealed type is "builtins.int" [builtins fixtures/paramspec.pyi] + +[case testRunParamSpecOverlappingOverloadsOrder] +from typing import Any, Callable, overload +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +class Base: + pass +class Child(Base): + def __call__(self) -> str: ... +class NotChild: + def __call__(self) -> str: ... + +@overload +def handle(func: Base) -> int: ... +@overload +def handle(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ... +def handle(func: Any, *args: Any, **kwargs: Any) -> Any: + return func(*args, **kwargs) + +@overload +def handle_reversed(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ... +@overload +def handle_reversed(func: Base) -> int: ... +def handle_reversed(func: Any, *args: Any, **kwargs: Any) -> Any: + return func(*args, **kwargs) + +reveal_type(handle(Child())) # N: Revealed type is "builtins.int" +reveal_type(handle(NotChild())) # N: Revealed type is "builtins.str" + +reveal_type(handle_reversed(Child())) # N: Revealed type is "builtins.str" +reveal_type(handle_reversed(NotChild())) # N: Revealed type is "builtins.str" + +[builtins fixtures/paramspec.pyi] From 9323b88bf10f5adf03730f49d15055b6b262c63e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 25 Oct 2024 12:59:14 +0100 Subject: [PATCH 0860/1617] Fix overlap check for ParamSpec types (#18040) ParamSpec types can match arbitrary parameter types, so treat them as having 'object' as the upper bound. This also fixes issues with filtering of overload items based on self type. Fixes #18036. --- mypy/meet.py | 6 ++++- test-data/unit/check-selftype.test | 38 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/mypy/meet.py b/mypy/meet.py index d614ecc45a57..f51d354d8f2f 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -223,7 +223,11 @@ def get_possible_variants(typ: Type) -> list[Type]: else: return [typ.upper_bound] elif isinstance(typ, ParamSpecType): - return [typ.upper_bound] + # Extract 'object' from the final mro item + upper_bound = get_proper_type(typ.upper_bound) + if isinstance(upper_bound, Instance): + return [Instance(upper_bound.type.mro[-1], [])] + return [AnyType(TypeOfAny.implementation_artifact)] elif isinstance(typ, TypeVarTupleType): return [typ.upper_bound] elif isinstance(typ, UnionType): diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 1a088fe05092..fa853ac48e5a 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2176,3 +2176,41 @@ class count: def foo(x: Union[range[int], count]) -> None: for item in x: reveal_type(item) # N: Revealed type is "builtins.int" + +[case testGenericDescriptorWithSelfTypeAnnotationsAndOverloads] +from __future__ import annotations +from typing import Any, overload, Callable, TypeVar, Generic, ParamSpec +from typing_extensions import Concatenate + +C = TypeVar("C", bound=Callable[..., Any]) +S = TypeVar("S") +P = ParamSpec("P") +R = TypeVar("R") + +class Descriptor(Generic[C]): + def __init__(self, impl: C) -> None: ... + + @overload + def __get__( + self: Descriptor[C], instance: None, owner: type | None + ) -> Descriptor[C]: ... + + @overload + def __get__( + self: Descriptor[Callable[Concatenate[S, P], R]], instance: S, owner: type | None, + ) -> Callable[P, R]: ... + + def __get__(self, *args, **kwargs): ... + +class Test: + @Descriptor + def method(self, foo: int, bar: str) -> bytes: ... + +reveal_type(Test().method) # N: Revealed type is "def (foo: builtins.int, bar: builtins.str) -> builtins.bytes" + +class Test2: + @Descriptor + def method(self, foo: int, *, bar: str) -> bytes: ... + +reveal_type(Test2().method) # N: Revealed type is "def (foo: builtins.int, *, bar: builtins.str) -> builtins.bytes" +[builtins fixtures/tuple.pyi] From 4b8e7dfb0216c6021382bcbef6d65cd8886cf210 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 25 Oct 2024 16:05:57 +0100 Subject: [PATCH 0861/1617] Refactor "==" and "is" type narrowing logic (#18042) Split a big function to make it easier to modify and understand. --- mypy/checker.py | 149 +++++++++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3e433408e40b..070d695fb9c2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6032,72 +6032,14 @@ def find_isinstance_check_helper( partial_type_maps = [] for operator, expr_indices in simplified_operator_list: if operator in {"is", "is not", "==", "!="}: - # is_valid_target: - # Controls which types we're allowed to narrow exprs to. Note that - # we cannot use 'is_literal_type_like' in both cases since doing - # 'x = 10000 + 1; x is 10001' is not always True in all Python - # implementations. - # - # coerce_only_in_literal_context: - # If true, coerce types into literal types only if one or more of - # the provided exprs contains an explicit Literal type. This could - # technically be set to any arbitrary value, but it seems being liberal - # with narrowing when using 'is' and conservative when using '==' seems - # to break the least amount of real-world code. - # - # should_narrow_by_identity: - # Set to 'false' only if the user defines custom __eq__ or __ne__ methods - # that could cause identity-based narrowing to produce invalid results. - if operator in {"is", "is not"}: - is_valid_target: Callable[[Type], bool] = is_singleton_type - coerce_only_in_literal_context = False - should_narrow_by_identity = True - else: - - def is_exactly_literal_type(t: Type) -> bool: - return isinstance(get_proper_type(t), LiteralType) - - def has_no_custom_eq_checks(t: Type) -> bool: - return not custom_special_method( - t, "__eq__", check_all=False - ) and not custom_special_method(t, "__ne__", check_all=False) - - is_valid_target = is_exactly_literal_type - coerce_only_in_literal_context = True - - expr_types = [operand_types[i] for i in expr_indices] - should_narrow_by_identity = all( - map(has_no_custom_eq_checks, expr_types) - ) and not is_ambiguous_mix_of_enums(expr_types) - - if_map: TypeMap = {} - else_map: TypeMap = {} - if should_narrow_by_identity: - if_map, else_map = self.refine_identity_comparison_expression( - operands, - operand_types, - expr_indices, - narrowable_operand_index_to_hash.keys(), - is_valid_target, - coerce_only_in_literal_context, - ) - - # Strictly speaking, we should also skip this check if the objects in the expr - # chain have custom __eq__ or __ne__ methods. But we (maybe optimistically) - # assume nobody would actually create a custom objects that considers itself - # equal to None. - if if_map == {} and else_map == {}: - if_map, else_map = self.refine_away_none_in_comparison( - operands, - operand_types, - expr_indices, - narrowable_operand_index_to_hash.keys(), - ) - - # If we haven't been able to narrow types yet, we might be dealing with a - # explicit type(x) == some_type check - if if_map == {} and else_map == {}: - if_map, else_map = self.find_type_equals_check(node, expr_indices) + if_map, else_map = self.equality_type_narrowing_helper( + node, + operator, + operands, + operand_types, + expr_indices, + narrowable_operand_index_to_hash, + ) elif operator in {"in", "not in"}: assert len(expr_indices) == 2 left_index, right_index = expr_indices @@ -6242,6 +6184,81 @@ def has_no_custom_eq_checks(t: Type) -> bool: else_map = {node: else_type} if not isinstance(else_type, UninhabitedType) else None return if_map, else_map + def equality_type_narrowing_helper( + self, + node: ComparisonExpr, + operator: str, + operands: list[Expression], + operand_types: list[Type], + expr_indices: list[int], + narrowable_operand_index_to_hash: dict[int, tuple[Key, ...]], + ) -> tuple[TypeMap, TypeMap]: + """Calculate type maps for '==', '!=', 'is' or 'is not' expression.""" + # is_valid_target: + # Controls which types we're allowed to narrow exprs to. Note that + # we cannot use 'is_literal_type_like' in both cases since doing + # 'x = 10000 + 1; x is 10001' is not always True in all Python + # implementations. + # + # coerce_only_in_literal_context: + # If true, coerce types into literal types only if one or more of + # the provided exprs contains an explicit Literal type. This could + # technically be set to any arbitrary value, but it seems being liberal + # with narrowing when using 'is' and conservative when using '==' seems + # to break the least amount of real-world code. + # + # should_narrow_by_identity: + # Set to 'false' only if the user defines custom __eq__ or __ne__ methods + # that could cause identity-based narrowing to produce invalid results. + if operator in {"is", "is not"}: + is_valid_target: Callable[[Type], bool] = is_singleton_type + coerce_only_in_literal_context = False + should_narrow_by_identity = True + else: + + def is_exactly_literal_type(t: Type) -> bool: + return isinstance(get_proper_type(t), LiteralType) + + def has_no_custom_eq_checks(t: Type) -> bool: + return not custom_special_method( + t, "__eq__", check_all=False + ) and not custom_special_method(t, "__ne__", check_all=False) + + is_valid_target = is_exactly_literal_type + coerce_only_in_literal_context = True + + expr_types = [operand_types[i] for i in expr_indices] + should_narrow_by_identity = all( + map(has_no_custom_eq_checks, expr_types) + ) and not is_ambiguous_mix_of_enums(expr_types) + + if_map: TypeMap = {} + else_map: TypeMap = {} + if should_narrow_by_identity: + if_map, else_map = self.refine_identity_comparison_expression( + operands, + operand_types, + expr_indices, + narrowable_operand_index_to_hash.keys(), + is_valid_target, + coerce_only_in_literal_context, + ) + + # Strictly speaking, we should also skip this check if the objects in the expr + # chain have custom __eq__ or __ne__ methods. But we (maybe optimistically) + # assume nobody would actually create a custom objects that considers itself + # equal to None. + if if_map == {} and else_map == {}: + if_map, else_map = self.refine_away_none_in_comparison( + operands, operand_types, expr_indices, narrowable_operand_index_to_hash.keys() + ) + + # If we haven't been able to narrow types yet, we might be dealing with a + # explicit type(x) == some_type check + if if_map == {} and else_map == {}: + if_map, else_map = self.find_type_equals_check(node, expr_indices) + return if_map, else_map + def propagate_up_typemap_info(self, new_types: TypeMap) -> TypeMap: """Attempts refining parent expressions of any MemberExpr or IndexExprs in new_types. From 9365fbfb8635bff93d749ed5321561e789c68cd3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 25 Oct 2024 16:33:29 +0100 Subject: [PATCH 0862/1617] Refactor type narrowing further (#18043) Move a big chunk of code to a helper function. --- mypy/checker.py | 232 ++++++++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 114 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 070d695fb9c2..dbc997ba33c6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5983,121 +5983,10 @@ def find_isinstance_check_helper( ), ) elif isinstance(node, ComparisonExpr): - # Step 1: Obtain the types of each operand and whether or not we can - # narrow their types. (For example, we shouldn't try narrowing the - # types of literal string or enum expressions). - - operands = [collapse_walrus(x) for x in node.operands] - operand_types = [] - narrowable_operand_index_to_hash = {} - for i, expr in enumerate(operands): - if not self.has_type(expr): - return {}, {} - expr_type = self.lookup_type(expr) - operand_types.append(expr_type) - - if ( - literal(expr) == LITERAL_TYPE - and not is_literal_none(expr) - and not self.is_literal_enum(expr) - ): - h = literal_hash(expr) - if h is not None: - narrowable_operand_index_to_hash[i] = h - - # Step 2: Group operands chained by either the 'is' or '==' operands - # together. For all other operands, we keep them in groups of size 2. - # So the expression: - # - # x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 - # - # ...is converted into the simplified operator list: - # - # [("==", [0, 1, 2]), ("<", [2, 3]), ("<", [3, 4]), - # ("is", [4, 5, 6]), ("is not", [6, 7]), ("is not", [7, 8])] - # - # We group identity/equality expressions so we can propagate information - # we discover about one operand across the entire chain. We don't bother - # handling 'is not' and '!=' chains in a special way: those are very rare - # in practice. - - simplified_operator_list = group_comparison_operands( - node.pairwise(), narrowable_operand_index_to_hash, {"==", "is"} - ) - - # Step 3: Analyze each group and infer more precise type maps for each - # assignable operand, if possible. We combine these type maps together - # in the final step. - - partial_type_maps = [] - for operator, expr_indices in simplified_operator_list: - if operator in {"is", "is not", "==", "!="}: - if_map, else_map = self.equality_type_narrowing_helper( - node, - operator, - operands, - operand_types, - expr_indices, - narrowable_operand_index_to_hash, - ) - elif operator in {"in", "not in"}: - assert len(expr_indices) == 2 - left_index, right_index = expr_indices - item_type = operand_types[left_index] - iterable_type = operand_types[right_index] - - if_map, else_map = {}, {} - - if left_index in narrowable_operand_index_to_hash: - # We only try and narrow away 'None' for now - if is_overlapping_none(item_type): - collection_item_type = get_proper_type( - builtin_item_type(iterable_type) - ) - if ( - collection_item_type is not None - and not is_overlapping_none(collection_item_type) - and not ( - isinstance(collection_item_type, Instance) - and collection_item_type.type.fullname == "builtins.object" - ) - and is_overlapping_erased_types(item_type, collection_item_type) - ): - if_map[operands[left_index]] = remove_optional(item_type) - - if right_index in narrowable_operand_index_to_hash: - if_type, else_type = self.conditional_types_for_iterable( - item_type, iterable_type - ) - expr = operands[right_index] - if if_type is None: - if_map = None - else: - if_map[expr] = if_type - if else_type is None: - else_map = None - else: - else_map[expr] = else_type - - else: - if_map = {} - else_map = {} - - if operator in {"is not", "!=", "not in"}: - if_map, else_map = else_map, if_map - - partial_type_maps.append((if_map, else_map)) - - # If we have found non-trivial restrictions from the regular comparisons, - # then return soon. Otherwise try to infer restrictions involving `len(x)`. - # TODO: support regular and len() narrowing in the same chain. - if any(m != ({}, {}) for m in partial_type_maps): - return reduce_conditional_maps(partial_type_maps) - else: - # Use meet for `and` maps to get correct results for chained checks - # like `if 1 < len(x) < 4: ...` - return reduce_conditional_maps(self.find_tuple_len_narrowing(node), use_meet=True) + return self.comparison_type_narrowing_helper(node) elif isinstance(node, AssignmentExpr): + if_map: dict[Expression, Type] | None + else_map: dict[Expression, Type] | None if_map = {} else_map = {} @@ -6184,6 +6073,121 @@ def find_isinstance_check_helper( else_map = {node: else_type} if not isinstance(else_type, UninhabitedType) else None return if_map, else_map + def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMap, TypeMap]: + """Infer type narrowing from a comparison expression.""" + # Step 1: Obtain the types of each operand and whether or not we can + # narrow their types. (For example, we shouldn't try narrowing the + # types of literal string or enum expressions). + + operands = [collapse_walrus(x) for x in node.operands] + operand_types = [] + narrowable_operand_index_to_hash = {} + for i, expr in enumerate(operands): + if not self.has_type(expr): + return {}, {} + expr_type = self.lookup_type(expr) + operand_types.append(expr_type) + + if ( + literal(expr) == LITERAL_TYPE + and not is_literal_none(expr) + and not self.is_literal_enum(expr) + ): + h = literal_hash(expr) + if h is not None: + narrowable_operand_index_to_hash[i] = h + + # Step 2: Group operands chained by either the 'is' or '==' operands + # together. For all other operands, we keep them in groups of size 2. + # So the expression: + # + # x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 + # + # ...is converted into the simplified operator list: + # + # [("==", [0, 1, 2]), ("<", [2, 3]), ("<", [3, 4]), + # ("is", [4, 5, 6]), ("is not", [6, 7]), ("is not", [7, 8])] + # + # We group identity/equality expressions so we can propagate information + # we discover about one operand across the entire chain. We don't bother + # handling 'is not' and '!=' chains in a special way: those are very rare + # in practice. + + simplified_operator_list = group_comparison_operands( + node.pairwise(), narrowable_operand_index_to_hash, {"==", "is"} + ) + + # Step 3: Analyze each group and infer more precise type maps for each + # assignable operand, if possible. We combine these type maps together + # in the final step. + + partial_type_maps = [] + for operator, expr_indices in simplified_operator_list: + if operator in {"is", "is not", "==", "!="}: + if_map, else_map = self.equality_type_narrowing_helper( + node, + operator, + operands, + operand_types, + expr_indices, + narrowable_operand_index_to_hash, + ) + elif operator in {"in", "not in"}: + assert len(expr_indices) == 2 + left_index, right_index = expr_indices + item_type = operand_types[left_index] + iterable_type = operand_types[right_index] + + if_map, else_map = {}, {} + + if left_index in narrowable_operand_index_to_hash: + # We only try and narrow away 'None' for now + if is_overlapping_none(item_type): + collection_item_type = get_proper_type(builtin_item_type(iterable_type)) + if ( + collection_item_type is not None + and not is_overlapping_none(collection_item_type) + and not ( + isinstance(collection_item_type, Instance) + and collection_item_type.type.fullname == "builtins.object" + ) + and is_overlapping_erased_types(item_type, collection_item_type) + ): + if_map[operands[left_index]] = remove_optional(item_type) + + if right_index in narrowable_operand_index_to_hash: + if_type, else_type = self.conditional_types_for_iterable( + item_type, iterable_type + ) + expr = operands[right_index] + if if_type is None: + if_map = None + else: + if_map[expr] = if_type + if else_type is None: + else_map = None + else: + else_map[expr] = else_type + + else: + if_map = {} + else_map = {} + + if operator in {"is not", "!=", "not in"}: + if_map, else_map = else_map, if_map + + partial_type_maps.append((if_map, else_map)) + + # If we have found non-trivial restrictions from the regular comparisons, + # then return soon. Otherwise try to infer restrictions involving `len(x)`. + # TODO: support regular and len() narrowing in the same chain. + if any(m != ({}, {}) for m in partial_type_maps): + return reduce_conditional_maps(partial_type_maps) + else: + # Use meet for `and` maps to get correct results for chained checks + # like `if 1 < len(x) < 4: ...` + return reduce_conditional_maps(self.find_tuple_len_narrowing(node), use_meet=True) + def equality_type_narrowing_helper( self, node: ComparisonExpr, From 715b7686cc25ddd0088709ae33dbf841920ee2c4 Mon Sep 17 00:00:00 2001 From: falsedrow <140980541+falsedrow@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:20:18 -0400 Subject: [PATCH 0863/1617] Filter to possible package paths before trying to resolve a module (#18038) With a long sys.path (it's got 300 entries), this removes 94% of stat syscalls from running mypy. With all the filesystem caching, that's only a small time savings, though it will depend on your filesystem. Local benchmarks showed a 20% time savings but they're pretty noisy from all the I/O. --------- Co-authored-by: Daniel Jacobowitz Co-authored-by: hauntsaninja --- mypy/modulefinder.py | 21 ++++++++++++--------- mypy/test/testmodulefinder.py | 2 +- test-data/unit/check-modules.test | 7 ++++++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 49c39a9ce91c..3b6e1a874d37 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -331,8 +331,6 @@ def _find_module_non_stub_helper( # If this is not a directory then we can't traverse further into it if not self.fscache.isdir(dir_path): break - if approved_stub_package_exists(".".join(components)): - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED if plausible_match: return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: @@ -414,9 +412,16 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: third_party_inline_dirs: PackageDirs = [] third_party_stubs_dirs: PackageDirs = [] found_possible_third_party_missing_type_hints = False - need_installed_stubs = False # Third-party stub/typed packages + candidate_package_dirs = { + package_dir[0] + for component in (components[0], components[0] + "-stubs") + for package_dir in self.find_lib_path_dirs(component, self.search_paths.package_path) + } for pkg_dir in self.search_paths.package_path: + pkg_dir = os.path.normpath(pkg_dir) + if pkg_dir not in candidate_package_dirs: + continue stub_name = components[0] + "-stubs" stub_dir = os_path_join(pkg_dir, stub_name) if fscache.isdir(stub_dir): @@ -445,11 +450,10 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: if isinstance(non_stub_match, ModuleNotFoundReason): if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: found_possible_third_party_missing_type_hints = True - elif non_stub_match is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: - need_installed_stubs = True else: third_party_inline_dirs.append(non_stub_match) self._update_ns_ancestors(components, non_stub_match) + if self.options and self.options.use_builtins_fixtures: # Everything should be in fixtures. third_party_inline_dirs.clear() @@ -548,12 +552,11 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: if ancestor is not None: return ancestor - if need_installed_stubs: + if approved_stub_package_exists(id): return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED - elif found_possible_third_party_missing_type_hints: + if found_possible_third_party_missing_type_hints: return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS - else: - return ModuleNotFoundReason.NOT_FOUND + return ModuleNotFoundReason.NOT_FOUND def find_modules_recursive(self, module: str) -> list[BuildSource]: module_path = self.find_module(module, fast_path=True) diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py index 943913d6cadb..bec0e60c551f 100644 --- a/mypy/test/testmodulefinder.py +++ b/mypy/test/testmodulefinder.py @@ -165,7 +165,7 @@ def setUp(self) -> None: self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options) def path(self, *parts: str) -> str: - return os.path.join(self.package_dir, *parts) + return os.path.normpath(os.path.join(self.package_dir, *parts)) def test__packages_with_ns(self) -> None: cases = [ diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 5fd48577e436..f368f244eb34 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3146,8 +3146,13 @@ main:1: note: (or run "mypy --install-types" to install all missing stub package main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports main:2: error: Library stubs not installed for "bleach.abc" -[case testMissingSubmoduleOfInstalledStubPackageIgnored] +[case testMissingSubmoduleOfInstalledStubPackageIgnored-xfail] # flags: --ignore-missing-imports + +# TODO: testMissingSubmoduleOfInstalledStubPackageIgnored was regressed in +# https://github.com/python/mypy/pull/15347 but didn't cause failures because we don't have a +# package path in this unit test + import bleach.xyz from bleach.abc import fgh [file bleach/__init__.pyi] From 52facccf3d25ab0481bf14f303f908bd333b462a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9amus=20=C3=93=20Ceanainn?= Date: Fri, 25 Oct 2024 23:09:59 +0100 Subject: [PATCH 0864/1617] Make `disallow-any-unimported` flag invertible (#18030) The `--follow-imports=skip` CLI option is pretty much unusable in a project where `disallow_any_unimported` is set to true in the configuration file, as this results in a large number of errors (due to both flags being incompatible). We have a pretty standard project configuration file (with `disallow_any_unimported = true` and `follow_imports = 'normal'`), but for specific local development cases where we want to run the mypy CLI with `--follow-imports=skip` it's incredibly noisy due to the number of errors produced. This change proposes making the `disallow-any-unimported` invertible, so that the CLI can be used with `--follow-imports=skip` in a less noisy way by using: ```bash mypy --follow-imports=skip --allow-any-unimported path/to/my/file.py ``` --- mypy/main.py | 12 ++++++------ test-data/unit/check-flags.test | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 5991ddd9e62f..4860b93400c2 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -645,12 +645,6 @@ def add_invertible_flag( title="Disallow dynamic typing", description="Disallow the use of the dynamic 'Any' type under certain conditions.", ) - disallow_any_group.add_argument( - "--disallow-any-unimported", - default=False, - action="store_true", - help="Disallow Any types resulting from unfollowed imports", - ) disallow_any_group.add_argument( "--disallow-any-expr", default=False, @@ -677,6 +671,12 @@ def add_invertible_flag( help="Disallow usage of generic types that do not specify explicit type parameters", group=disallow_any_group, ) + add_invertible_flag( + "--disallow-any-unimported", + default=False, + help="Disallow Any types resulting from unfollowed imports", + group=disallow_any_group, + ) add_invertible_flag( "--disallow-subclassing-any", default=False, diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index dd7bee3f7aec..c6419923ebc6 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -963,6 +963,12 @@ from missing import Unchecked t: Unchecked = 12 # E: Type of variable becomes "Any" due to an unfollowed import +[case testAllowImplicitAnyVariableDefinition] +# flags: --ignore-missing-imports --allow-any-unimported +from missing import Unchecked + +t: Unchecked = 12 + [case testDisallowImplicitAnyGeneric] # flags: --ignore-missing-imports --disallow-any-unimported from missing import Unchecked From 38d26aa655400e06ebe5101d27c8afabba203ee7 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 25 Oct 2024 23:28:50 +0100 Subject: [PATCH 0865/1617] Remove unnecessary concatenated strings (#18044) We have some strings which use implicit concatenation unnecessarily (where the whole string can fit on a single line without implicit concatenation). This PR removes them. For some of the changed lines here, I also did some driveby modernisations to use f-strings, since I was touching those lines anyway. --- mypy/checkstrformat.py | 14 ++++++++------ mypy/inspections.py | 3 +-- mypy/main.py | 18 ++++++++---------- mypy/message_registry.py | 3 +-- mypy/messages.py | 37 +++++++++++++++++++------------------ mypy/modulefinder.py | 3 +-- mypy/plugins/dataclasses.py | 3 +-- mypy/semanal.py | 3 +-- mypy/semanal_namedtuple.py | 3 +-- mypy/typeanal.py | 10 ++++------ 10 files changed, 45 insertions(+), 52 deletions(-) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index c63210a96c44..dd42fe7755a0 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -393,8 +393,10 @@ def check_specs_in_format_call( # If the explicit conversion is given, then explicit conversion is called _first_. if spec.conversion[1] not in "rsa": self.msg.fail( - 'Invalid conversion type "{}",' - ' must be one of "r", "s" or "a"'.format(spec.conversion[1]), + ( + f'Invalid conversion type "{spec.conversion[1]}", ' + f'must be one of "r", "s" or "a"' + ), call, code=codes.STRING_FORMATTING, ) @@ -472,8 +474,7 @@ def find_replacements_in_call(self, call: CallExpr, keys: list[str]) -> list[Exp expr = self.get_expr_by_position(int(key), call) if not expr: self.msg.fail( - "Cannot find replacement for positional" - " format specifier {}".format(key), + f"Cannot find replacement for positional format specifier {key}", call, code=codes.STRING_FORMATTING, ) @@ -654,8 +655,9 @@ class User(TypedDict): assert spec.key, "Call this method only after auto-generating keys!" assert spec.field self.msg.fail( - "Invalid index expression in format field" - ' accessor "{}"'.format(spec.field[len(spec.key) :]), + 'Invalid index expression in format field accessor "{}"'.format( + spec.field[len(spec.key) :] + ), ctx, code=codes.STRING_FORMATTING, ) diff --git a/mypy/inspections.py b/mypy/inspections.py index 3e660a0bd7a6..0baf0896f7e5 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -469,8 +469,7 @@ def missing_type(self, expression: Expression) -> str: def missing_node(self, expression: Expression) -> str: return ( - f'Cannot find definition for "{type(expression).__name__}"' - f" at {expr_span(expression)}" + f'Cannot find definition for "{type(expression).__name__}" at {expr_span(expression)}' ) def add_prefixes(self, result: str, expression: Expression) -> str: diff --git a/mypy/main.py b/mypy/main.py index 4860b93400c2..ae3e7d75be7f 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -552,15 +552,16 @@ def add_invertible_flag( ) config_group.add_argument( "--config-file", - help="Configuration file, must have a [mypy] section " - "(defaults to {})".format(", ".join(defaults.CONFIG_FILES)), + help=( + f"Configuration file, must have a [mypy] section " + f"(defaults to {', '.join(defaults.CONFIG_FILES)})" + ), ) add_invertible_flag( "--warn-unused-configs", default=False, strict_flag=True, - help="Warn about unused '[mypy-]' or '[[tool.mypy.overrides]]' " - "config sections", + help="Warn about unused '[mypy-]' or '[[tool.mypy.overrides]]' config sections", group=config_group, ) @@ -589,8 +590,7 @@ def add_invertible_flag( "--python-executable", action="store", metavar="EXECUTABLE", - help="Python executable used for finding PEP 561 compliant installed" - " packages and stubs", + help="Python executable used for finding PEP 561 compliant installed packages and stubs", dest="special-opts:python_executable", ) imports_group.add_argument( @@ -623,8 +623,7 @@ def add_invertible_flag( "--platform", action="store", metavar="PLATFORM", - help="Type check special-cased code for the given OS platform " - "(defaults to sys.platform)", + help="Type check special-cased code for the given OS platform (defaults to sys.platform)", ) platform_group.add_argument( "--always-true", @@ -655,8 +654,7 @@ def add_invertible_flag( "--disallow-any-decorated", default=False, action="store_true", - help="Disallow functions that have Any in their signature " - "after decorator transformation", + help="Disallow functions that have Any in their signature after decorator transformation", ) disallow_any_group.add_argument( "--disallow-any-explicit", diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 29d539faaed6..c0b422d4a35d 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -196,8 +196,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPEVAR_ARG_MUST_BE_TYPE: Final = '{} "{}" must be a type' TYPEVAR_UNEXPECTED_ARGUMENT: Final = 'Unexpected argument to "TypeVar()"' UNBOUND_TYPEVAR: Final = ( - "A function returning TypeVar should receive at least " - "one argument containing the same TypeVar" + "A function returning TypeVar should receive at least one argument containing the same TypeVar" ) TYPE_PARAMETERS_SHOULD_BE_DECLARED: Final = ( "All type parameters should be declared ({} not declared)" diff --git a/mypy/messages.py b/mypy/messages.py index adf150eab50a..d63df92c80a7 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -675,8 +675,7 @@ def incompatible_argument( arg_type, callee.arg_types[n - 1], options=self.options ) info = ( - f" (expression has type {arg_type_str}, " - f"target has type {callee_type_str})" + f" (expression has type {arg_type_str}, target has type {callee_type_str})" ) error_msg = ( message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT.with_additional_msg(info) @@ -1433,8 +1432,7 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) def unsafe_super(self, method: str, cls: str, ctx: Context) -> None: self.fail( - 'Call to abstract method "{}" of "{}" with trivial body' - " via super() is unsafe".format(method, cls), + f'Call to abstract method "{method}" of "{cls}" with trivial body via super() is unsafe', ctx, code=codes.SAFE_SUPER, ) @@ -1588,8 +1586,10 @@ def final_cant_override_writable(self, name: str, ctx: Context) -> None: def cant_override_final(self, name: str, base_name: str, ctx: Context) -> None: self.fail( - 'Cannot override final attribute "{}"' - ' (previously declared in base class "{}")'.format(name, base_name), + ( + f'Cannot override final attribute "{name}" ' + f'(previously declared in base class "{base_name}")' + ), ctx, ) @@ -1676,15 +1676,16 @@ def overloaded_signatures_typevar_specific(self, index: int, context: Context) - def overloaded_signatures_arg_specific(self, index: int, context: Context) -> None: self.fail( - "Overloaded function implementation does not accept all possible arguments " - "of signature {}".format(index), + ( + f"Overloaded function implementation does not accept all possible arguments " + f"of signature {index}" + ), context, ) def overloaded_signatures_ret_specific(self, index: int, context: Context) -> None: self.fail( - "Overloaded function implementation cannot produce return type " - "of signature {}".format(index), + f"Overloaded function implementation cannot produce return type of signature {index}", context, ) @@ -1707,8 +1708,7 @@ def operator_method_signatures_overlap( context: Context, ) -> None: self.fail( - 'Signatures of "{}" of "{}" and "{}" of {} ' - "are unsafely overlapping".format( + 'Signatures of "{}" of "{}" and "{}" of {} are unsafely overlapping'.format( reverse_method, reverse_class.name, forward_method, @@ -1997,8 +1997,7 @@ def bad_proto_variance( self, actual: int, tvar_name: str, expected: int, context: Context ) -> None: msg = capitalize( - '{} type variable "{}" used in protocol where' - " {} one is expected".format( + '{} type variable "{}" used in protocol where {} one is expected'.format( variance_string(actual), tvar_name, variance_string(expected) ) ) @@ -2246,15 +2245,17 @@ def report_protocol_problems( for name, subflags, superflags in conflict_flags[:MAX_ITEMS]: if not class_obj and IS_CLASSVAR in subflags and IS_CLASSVAR not in superflags: self.note( - "Protocol member {}.{} expected instance variable," - " got class variable".format(supertype.type.name, name), + "Protocol member {}.{} expected instance variable, got class variable".format( + supertype.type.name, name + ), context, code=code, ) if not class_obj and IS_CLASSVAR in superflags and IS_CLASSVAR not in subflags: self.note( - "Protocol member {}.{} expected class variable," - " got instance variable".format(supertype.type.name, name), + "Protocol member {}.{} expected class variable, got instance variable".format( + supertype.type.name, name + ), context, code=code, ) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 3b6e1a874d37..774ce4f8f9b8 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -73,8 +73,7 @@ def error_message_templates(self, daemon: bool) -> tuple[str, list[str]]: elif self is ModuleNotFoundReason.WRONG_WORKING_DIRECTORY: msg = 'Cannot find implementation or library stub for module named "{module}"' notes = [ - "You may be running mypy in a subpackage, " - "mypy should be run on the package root" + "You may be running mypy in a subpackage, mypy should be run on the package root" ] elif self is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: msg = ( diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index edfc6840fc37..81a5e70e6b3a 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -444,8 +444,7 @@ def add_slots( # This means that version is lower than `3.10`, # it is just a non-existent argument for `dataclass` function. self._api.fail( - 'Keyword argument "slots" for "dataclass" ' - "is only valid in Python 3.10 and higher", + 'Keyword argument "slots" for "dataclass" is only valid in Python 3.10 and higher', self._reason, ) return diff --git a/mypy/semanal.py b/mypy/semanal.py index e182322cfbe6..23310e0974cd 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2636,8 +2636,7 @@ def calculate_class_mro( calculate_mro(defn.info, obj_type) except MroError: self.fail( - "Cannot determine consistent method resolution " - 'order (MRO) for "%s"' % defn.name, + f'Cannot determine consistent method resolution order (MRO) for "{defn.name}"', defn, ) self.set_dummy_mro(defn.info) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 768dd265b338..7c6da7721e8f 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -380,8 +380,7 @@ def parse_namedtuple_args( rename = arg.name == "True" else: self.fail( - 'Boolean literal expected as the "rename" argument to ' - f"{type_name}()", + f'Boolean literal expected as the "rename" argument to {type_name}()', arg, code=ARG_TYPE, ) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 0a6b7689136e..d3ed3ff2bae6 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -968,14 +968,12 @@ def analyze_unbound_type_without_type_info( message = 'Type variable "{}" is unbound' short = name.split(".")[-1] notes.append( - ( - '(Hint: Use "Generic[{}]" or "Protocol[{}]" base class' - ' to bind "{}" inside a class)' - ).format(short, short, short) + f'(Hint: Use "Generic[{short}]" or "Protocol[{short}]" base class' + f' to bind "{short}" inside a class)' ) notes.append( - '(Hint: Use "{}" in function signature to bind "{}"' - " inside a function)".format(short, short) + f'(Hint: Use "{short}" in function signature ' + f'to bind "{short}" inside a function)' ) else: message = 'Cannot interpret reference "{}" as a type' From 55b5aed8aa7229195dc2193e173c6aad2677e197 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:29:11 -0700 Subject: [PATCH 0866/1617] Further caution against `--follow-imports=skip` (#18048) --- docs/source/running_mypy.rst | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 42474ae94c48..a8ebc61d4774 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -503,7 +503,7 @@ accepts one of four string values: main.py:1: note: (Using --follow-imports=error, module not passed on command line) If you are starting a new codebase and plan on using type hints from -the start, we recommend you use either :option:`--follow-imports=normal ` +the start, we **recommend** you use either :option:`--follow-imports=normal ` (the default) or :option:`--follow-imports=error `. Either option will help make sure you are not skipping checking any part of your codebase by accident. @@ -514,16 +514,27 @@ files that do not use type hints) pass under :option:`--follow-imports=normal `. Even if -mypy is unable to perfectly type check a file, it can still glean some +Only if doing this is intractable, try passing mypy just the files +you want to type check and using :option:`--follow-imports=silent `. +Even if mypy is unable to perfectly type check a file, it can still glean some useful information by parsing it (for example, understanding what methods a given object has). See :ref:`existing-code` for more recommendations. -We do not recommend using ``skip`` unless you know what you are doing: -while this option can be quite powerful, it can also cause many -hard-to-debug errors. - Adjusting import following behaviour is often most useful when restricted to specific modules. This can be accomplished by setting a per-module :confval:`follow_imports` config option. + +.. warning:: + + We do not recommend using ``follow_imports=skip`` unless you're really sure + you know what you are doing. This option greatly restricts the analysis mypy + can perform and you will lose a lot of the benefits of type checking. + + This is especially true at the global level. Setting a per-module + ``follow_imports=skip`` for a specific problematic module can be + useful without causing too much harm. + +.. note:: + + If you're looking to resolve import errors related to libraries, try following + the advice in :ref:`fix-missing-imports` before messing with ``follow_imports``. From afdafb66cb68525e7b2733b8a8b46b205cc9f0d1 Mon Sep 17 00:00:00 2001 From: John Doknjas <32089502+johndoknjas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:38:01 -0700 Subject: [PATCH 0867/1617] Remove some unused functions. (#18049) This PR just removes some functions that are unused (from what I can tell). --- mypy/traverser.py | 6 ------ mypy/typeanal.py | 9 --------- mypy/typevartuples.py | 10 ---------- mypyc/analysis/dataflow.py | 22 ---------------------- mypyc/codegen/emitmodule.py | 15 --------------- mypyc/irbuild/function.py | 4 ---- 6 files changed, 66 deletions(-) diff --git a/mypy/traverser.py b/mypy/traverser.py index 6f162c9ec576..9c333c587f7c 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -958,9 +958,3 @@ def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None: def visit_yield_from_expr(self, expr: YieldFromExpr) -> None: self.yield_from_expressions.append((expr, self.in_assignment)) - - -def all_yield_from_expressions(node: Node) -> list[tuple[YieldFromExpr, bool]]: - v = YieldFromCollector() - node.accept(v) - return v.yield_from_expressions diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d3ed3ff2bae6..76ead74d38a8 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2276,15 +2276,6 @@ def set_any_tvars( return t -def flatten_tvars(lists: list[list[T]]) -> list[T]: - result: list[T] = [] - for lst in lists: - for item in lst: - if item not in result: - result.append(item) - return result - - class DivergingAliasDetector(TrivialSyntheticTypeTranslator): """See docstring of detect_diverging_alias() for details.""" diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index 2a9998c10746..3bc67dc55ef3 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -7,12 +7,10 @@ from mypy.types import ( AnyType, Instance, - ProperType, Type, TypeVarLikeType, TypeVarTupleType, UnpackType, - get_proper_type, split_with_prefix_and_suffix, ) @@ -27,14 +25,6 @@ def split_with_instance( ) -def extract_unpack(types: Sequence[Type]) -> ProperType | None: - """Given a list of types, extracts either a single type from an unpack, or returns None.""" - if len(types) == 1: - if isinstance(types[0], UnpackType): - return get_proper_type(types[0].type) - return None - - def erased_vars(type_vars: Sequence[TypeVarLikeType], type_of_any: int) -> list[Type]: args: list[Type] = [] for tv in type_vars: diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 9babf860fb31..411fc8093404 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -5,7 +5,6 @@ from abc import abstractmethod from typing import Dict, Generic, Iterable, Iterator, Set, Tuple, TypeVar -from mypyc.ir.func_ir import all_values from mypyc.ir.ops import ( Assign, AssignMulti, @@ -437,27 +436,6 @@ def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]: return set(), set() -def analyze_undefined_regs( - blocks: list[BasicBlock], cfg: CFG, initial_defined: set[Value] -) -> AnalysisResult[Value]: - """Calculate potentially undefined registers at each CFG location. - - A register is undefined if there is some path from initial block - where it has an undefined value. - - Function arguments are assumed to be always defined. - """ - initial_undefined = set(all_values([], blocks)) - initial_defined - return run_analysis( - blocks=blocks, - cfg=cfg, - gen_and_kill=UndefinedVisitor(), - initial=initial_undefined, - backward=False, - kind=MAYBE_ANALYSIS, - ) - - def non_trivial_sources(op: Op) -> set[Value]: result = set() for source in op.sources(): diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index b5e0a37f0cca..5b2812c2293a 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -47,7 +47,6 @@ use_vectorcall, ) from mypyc.errors import Errors -from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncIR from mypyc.ir.module_ir import ModuleIR, ModuleIRs, deserialize_modules from mypyc.ir.ops import DeserMaps, LoadLiteral @@ -1075,20 +1074,6 @@ def declare_type_vars(self, module: str, type_var_names: list[str], emitter: Emi ) -def sort_classes(classes: list[tuple[str, ClassIR]]) -> list[tuple[str, ClassIR]]: - mod_name = {ir: name for name, ir in classes} - irs = [ir for _, ir in classes] - deps: dict[ClassIR, set[ClassIR]] = {} - for ir in irs: - if ir not in deps: - deps[ir] = set() - if ir.base: - deps[ir].add(ir.base) - deps[ir].update(ir.traits) - sorted_irs = toposort(deps) - return [(mod_name[ir], ir) for ir in sorted_irs] - - T = TypeVar("T") diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index f9d55db50f27..fcfe568fcd28 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -990,10 +990,6 @@ def singledispatch_main_func_name(orig_name: str) -> str: return f"__mypyc_singledispatch_main_function_{orig_name}__" -def get_registry_identifier(fitem: FuncDef) -> str: - return f"__mypyc_singledispatch_registry_{fitem.fullname}__" - - def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: line = fitem.line is_singledispatch_main_func = fitem in builder.singledispatch_impls From 724e259eb1441cdf1b0cb77b347b7f5c71c4bc24 Mon Sep 17 00:00:00 2001 From: chiri <109767616+chirizxc@users.noreply.github.com> Date: Sat, 26 Oct 2024 03:45:43 +0300 Subject: [PATCH 0868/1617] [docs] automatic copyright year update (#17982) sorry for https://github.com/python/mypy/pull/17972 and thanks @Viicos Co-authored-by: chiri <109767616+yachirig@users.noreply.github.com> --- docs/requirements-docs.txt | 2 +- docs/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index dc502d121ffc..a94c1b7ba95c 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,3 +1,3 @@ -sphinx>=5.1.0 +sphinx>=8.1.0 furo>=2022.3.4 myst-parser>=4.0.0 diff --git a/docs/source/conf.py b/docs/source/conf.py index 7f5fe9d43d5f..ddc9923c6c93 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -51,7 +51,7 @@ # General information about the project. project = "mypy" -copyright = "2012-2022 Jukka Lehtosalo and mypy contributors" +copyright = "2012-%Y Jukka Lehtosalo and mypy contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 2b033cbd7ab7b5d0cc9a625df14dcaeab7100c24 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:11:15 -0700 Subject: [PATCH 0869/1617] Fix approved stub ignore, remove normpath (#18045) Follow up to https://github.com/python/mypy/pull/18038 This maybe should have been two PRs --- mypy/build.py | 5 ++- mypy/modulefinder.py | 63 +++++++++++++++++++++++-------- mypy/strconv.py | 2 +- mypy/stubinfo.py | 16 -------- mypy/test/testmodulefinder.py | 28 +++++++------- mypy/test/teststubinfo.py | 12 ------ test-data/unit/check-modules.test | 20 +++------- 7 files changed, 71 insertions(+), 75 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index ac6471d2383f..ff9b48d2d7b4 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2143,7 +2143,8 @@ def parse_file(self, *, temporary: bool = False) -> None: raise CompileError( [ "mypy: can't read file '{}': {}".format( - self.path, os.strerror(ioerr.errno) + self.path.replace(os.getcwd() + os.sep, ""), + os.strerror(ioerr.errno), ) ], module_with_blocker=self.id, @@ -2861,7 +2862,7 @@ def log_configuration(manager: BuildManager, sources: list[BuildSource]) -> None manager.log(f"{'Found source:':24}{source}") # Complete list of searched paths can get very long, put them under TRACE - for path_type, paths in manager.search_paths._asdict().items(): + for path_type, paths in manager.search_paths.asdict().items(): if not paths: manager.trace(f"No {path_type}") continue diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 774ce4f8f9b8..9b15f2aff90e 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,7 +13,7 @@ import subprocess import sys from enum import Enum, unique -from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union +from typing import Dict, Final, List, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo @@ -21,16 +21,35 @@ from mypy.fscache import FileSystemCache from mypy.nodes import MypyFile from mypy.options import Options -from mypy.stubinfo import approved_stub_package_exists +from mypy.stubinfo import stub_distribution_name from mypy.util import os_path_join # Paths to be searched in find_module(). -class SearchPaths(NamedTuple): - python_path: tuple[str, ...] # where user code is found - mypy_path: tuple[str, ...] # from $MYPYPATH or config variable - package_path: tuple[str, ...] # from get_site_packages_dirs() - typeshed_path: tuple[str, ...] # paths in typeshed +class SearchPaths: + def __init__( + self, + python_path: tuple[str, ...], + mypy_path: tuple[str, ...], + package_path: tuple[str, ...], + typeshed_path: tuple[str, ...], + ) -> None: + # where user code is found + self.python_path = tuple(map(os.path.abspath, python_path)) + # from $MYPYPATH or config variable + self.mypy_path = tuple(map(os.path.abspath, mypy_path)) + # from get_site_packages_dirs() + self.package_path = tuple(map(os.path.abspath, package_path)) + # paths in typeshed + self.typeshed_path = tuple(map(os.path.abspath, typeshed_path)) + + def asdict(self) -> dict[str, tuple[str, ...]]: + return { + "python_path": self.python_path, + "mypy_path": self.mypy_path, + "package_path": self.package_path, + "typeshed_path": self.typeshed_path, + } # Package dirs are a two-tuple of path to search and whether to verify the module @@ -239,17 +258,17 @@ def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None: return None def find_lib_path_dirs(self, id: str, lib_path: tuple[str, ...]) -> PackageDirs: - """Find which elements of a lib_path have the directory a module needs to exist. - - This is run for the python_path, mypy_path, and typeshed_path search paths. - """ + """Find which elements of a lib_path have the directory a module needs to exist.""" components = id.split(".") dir_chain = os.sep.join(components[:-1]) # e.g., 'foo/bar' dirs = [] for pathitem in self.get_toplevel_possibilities(lib_path, components[0]): # e.g., '/usr/lib/python3.4/foo/bar' - dir = os.path.normpath(os_path_join(pathitem, dir_chain)) + if dir_chain: + dir = os_path_join(pathitem, dir_chain) + else: + dir = pathitem if self.fscache.isdir(dir): dirs.append((dir, True)) return dirs @@ -418,7 +437,6 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: for package_dir in self.find_lib_path_dirs(component, self.search_paths.package_path) } for pkg_dir in self.search_paths.package_path: - pkg_dir = os.path.normpath(pkg_dir) if pkg_dir not in candidate_package_dirs: continue stub_name = components[0] + "-stubs" @@ -551,8 +569,22 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: if ancestor is not None: return ancestor - if approved_stub_package_exists(id): - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + approved_dist_name = stub_distribution_name(id) + if approved_dist_name: + if len(components) == 1: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + # If we're a missing submodule of an already installed approved stubs, we don't want to + # error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND. + for i in range(1, len(components)): + parent_id = ".".join(components[:i]) + if stub_distribution_name(parent_id) == approved_dist_name: + break + else: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + if self.find_module(parent_id) is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + return ModuleNotFoundReason.NOT_FOUND + if found_possible_third_party_missing_type_hints: return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS return ModuleNotFoundReason.NOT_FOUND @@ -835,7 +867,6 @@ def compute_search_paths( return SearchPaths( python_path=tuple(reversed(python_path)), mypy_path=tuple(mypypath), - # package_path and typeshed_path must be normalised and absolute via os.path.abspath package_path=tuple(sys_path + site_packages), typeshed_path=tuple(lib_path), ) diff --git a/mypy/strconv.py b/mypy/strconv.py index d2ac71412f90..2d595d4b67b0 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -112,7 +112,7 @@ def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> str: if o.path != "main": # Insert path. Normalize directory separators to / to unify test # case# output in all platforms. - a.insert(0, o.path.replace(os.sep, "/")) + a.insert(0, o.path.replace(os.getcwd() + os.sep, "").replace(os.sep, "/")) if o.ignored_lines: a.append("IgnoredLines(%s)" % ", ".join(str(line) for line in sorted(o.ignored_lines))) return self.dump(a, o) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 0ec64b037fee..8d89a2a4bede 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -6,22 +6,6 @@ def is_module_from_legacy_bundled_package(module: str) -> bool: return top_level in legacy_bundled_packages -def approved_stub_package_exists(module: str) -> bool: - top_level = module.split(".", 1)[0] - if top_level in legacy_bundled_packages: - return True - if top_level in non_bundled_packages_flat: - return True - if top_level in non_bundled_packages_namespace: - namespace = non_bundled_packages_namespace[top_level] - components = module.split(".") - for i in range(len(components), 0, -1): - module = ".".join(components[:i]) - if module in namespace: - return True - return False - - def stub_distribution_name(module: str) -> str | None: top_level = module.split(".", 1)[0] diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py index bec0e60c551f..65d9a66c5fa0 100644 --- a/mypy/test/testmodulefinder.py +++ b/mypy/test/testmodulefinder.py @@ -53,12 +53,12 @@ def test__no_namespace_packages__find_a_in_pkg1(self) -> None: Find find pkg1/a.py for "a" with namespace_packages False. """ found_module = self.fmc_nons.find_module("a") - expected = os.path.join(data_path, "pkg1", "a.py") + expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py")) assert_equal(expected, found_module) def test__no_namespace_packages__find_b_in_pkg2(self) -> None: found_module = self.fmc_ns.find_module("b") - expected = os.path.join(data_path, "pkg2", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None: @@ -67,7 +67,7 @@ def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None: the path to the first one found in mypypath. """ found_module = self.fmc_ns.find_module("nsx") - expected = os.path.join(data_path, "nsx-pkg1", "nsx") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx")) assert_equal(expected, found_module) def test__find_nsx_a_init_in_pkg1(self) -> None: @@ -75,7 +75,7 @@ def test__find_nsx_a_init_in_pkg1(self) -> None: Find nsx-pkg1/nsx/a/__init__.py for "nsx.a" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.a") - expected = os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_b_init_in_pkg2(self) -> None: @@ -83,7 +83,7 @@ def test__find_nsx_b_init_in_pkg2(self) -> None: Find nsx-pkg2/nsx/b/__init__.py for "nsx.b" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.b") - expected = os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_c_c_in_pkg3(self) -> None: @@ -91,7 +91,7 @@ def test__find_nsx_c_c_in_pkg3(self) -> None: Find nsx-pkg3/nsx/c/c.py for "nsx.c.c" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.c.c") - expected = os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py")) assert_equal(expected, found_module) def test__find_nsy_a__init_pyi(self) -> None: @@ -99,7 +99,7 @@ def test__find_nsy_a__init_pyi(self) -> None: Prefer nsy-pkg1/a/__init__.pyi file over __init__.py. """ found_module = self.fmc_ns.find_module("nsy.a") - expected = os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi")) assert_equal(expected, found_module) def test__find_nsy_b__init_py(self) -> None: @@ -109,7 +109,7 @@ def test__find_nsy_b__init_py(self) -> None: a package is preferred over a module. """ found_module = self.fmc_ns.find_module("nsy.b") - expected = os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsy_c_pyi(self) -> None: @@ -119,17 +119,17 @@ def test__find_nsy_c_pyi(self) -> None: .pyi is preferred over .py. """ found_module = self.fmc_ns.find_module("nsy.c") - expected = os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi")) assert_equal(expected, found_module) def test__find_a_in_pkg1(self) -> None: found_module = self.fmc_ns.find_module("a") - expected = os.path.join(data_path, "pkg1", "a.py") + expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py")) assert_equal(expected, found_module) def test__find_b_init_in_pkg2(self) -> None: found_module = self.fmc_ns.find_module("b") - expected = os.path.join(data_path, "pkg2", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_d_nowhere(self) -> None: @@ -165,7 +165,7 @@ def setUp(self) -> None: self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options) def path(self, *parts: str) -> str: - return os.path.normpath(os.path.join(self.package_dir, *parts)) + return os.path.abspath(os.path.join(self.package_dir, *parts)) def test__packages_with_ns(self) -> None: cases = [ @@ -214,7 +214,7 @@ def test__packages_with_ns(self) -> None: # A regular package with an installed set of stubs ("foo.bar", self.path("foo-stubs", "bar.pyi")), # A regular, non-site-packages module - ("a", os.path.join(data_path, "pkg1", "a.py")), + ("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))), ] for module, expected in cases: template = "Find(" + module + ") got {}; expected {}" @@ -269,7 +269,7 @@ def test__packages_without_ns(self) -> None: # A regular package with an installed set of stubs ("foo.bar", self.path("foo-stubs", "bar.pyi")), # A regular, non-site-packages module - ("a", os.path.join(data_path, "pkg1", "a.py")), + ("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))), ] for module, expected in cases: template = "Find(" + module + ") got {}; expected {}" diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py index 10ce408e7023..518194d35e1d 100644 --- a/mypy/test/teststubinfo.py +++ b/mypy/test/teststubinfo.py @@ -3,7 +3,6 @@ import unittest from mypy.stubinfo import ( - approved_stub_package_exists, is_module_from_legacy_bundled_package, legacy_bundled_packages, non_bundled_packages_flat, @@ -18,17 +17,6 @@ def test_is_legacy_bundled_packages(self) -> None: assert is_module_from_legacy_bundled_package("pycurl") assert is_module_from_legacy_bundled_package("dataclasses") - def test_approved_stub_package_exists(self) -> None: - assert not approved_stub_package_exists("foobar_asdf") - assert approved_stub_package_exists("pycurl") - assert approved_stub_package_exists("babel") - assert approved_stub_package_exists("google.cloud.ndb") - assert approved_stub_package_exists("google.cloud.ndb.submodule") - assert not approved_stub_package_exists("google.cloud.unknown") - assert approved_stub_package_exists("google.protobuf") - assert approved_stub_package_exists("google.protobuf.submodule") - assert not approved_stub_package_exists("google") - def test_stub_distribution_name(self) -> None: assert stub_distribution_name("foobar_asdf") is None assert stub_distribution_name("pycurl") == "types-pycurl" diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index f368f244eb34..68897790e4bf 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3136,23 +3136,15 @@ import google.cloud.ndb # E: Library stubs not installed for "google.cloud.ndb" from google.cloud import ndb [case testMissingSubmoduleOfInstalledStubPackage] -import bleach.xyz -from bleach.abc import fgh +import bleach.exists +import bleach.xyz # E: Cannot find implementation or library stub for module named "bleach.xyz" \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +from bleach.abc import fgh # E: Cannot find implementation or library stub for module named "bleach.abc" [file bleach/__init__.pyi] -[out] -main:1: error: Library stubs not installed for "bleach.xyz" -main:1: note: Hint: "python3 -m pip install types-bleach" -main:1: note: (or run "mypy --install-types" to install all missing stub packages) -main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main:2: error: Library stubs not installed for "bleach.abc" +[file bleach/exists.pyi] -[case testMissingSubmoduleOfInstalledStubPackageIgnored-xfail] +[case testMissingSubmoduleOfInstalledStubPackageIgnored] # flags: --ignore-missing-imports - -# TODO: testMissingSubmoduleOfInstalledStubPackageIgnored was regressed in -# https://github.com/python/mypy/pull/15347 but didn't cause failures because we don't have a -# package path in this unit test - import bleach.xyz from bleach.abc import fgh [file bleach/__init__.pyi] From e7db89cc41dd5c77a4ccc72695fabf4234a8b67b Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 26 Oct 2024 23:59:54 +0200 Subject: [PATCH 0870/1617] PEP 702 (@deprecated): consider all possible type positions (#17926) This pull request generalises #17899. Initially, it started with extending #17899 to function signatures only, as can be seen from the following initial comments and the subsequent discussions. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 27 +--- mypy/semanal.py | 2 + mypy/server/astdiff.py | 1 + mypy/typeanal.py | 22 +++ test-data/unit/check-deprecated.test | 123 +++++++++++++- test-data/unit/fine-grained.test | 233 +++++++++++++++++++++++++++ 6 files changed, 382 insertions(+), 26 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index dbc997ba33c6..8644e8d2e93e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -287,18 +287,6 @@ class PartialTypeScope(NamedTuple): is_local: bool -class InstanceDeprecatedVisitor(TypeTraverserVisitor): - """Visitor that recursively checks for deprecations in nested instances.""" - - def __init__(self, typechecker: TypeChecker, context: Context) -> None: - self.typechecker = typechecker - self.context = context - - def visit_instance(self, t: Instance) -> None: - super().visit_instance(t) - self.typechecker.check_deprecated(t.type, self.context) - - class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. @@ -2958,15 +2946,6 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: Handle all kinds of assignment statements (simple, indexed, multiple). """ - if s.unanalyzed_type is not None: - for lvalue in s.lvalues: - if ( - isinstance(lvalue, NameExpr) - and isinstance(var := lvalue.node, Var) - and (var.type is not None) - ): - var.type.accept(InstanceDeprecatedVisitor(typechecker=self, context=s)) - # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such # as X | Y. @@ -7655,8 +7634,10 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: """Warn if deprecated.""" if isinstance(node, Decorator): node = node.func - if isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) and ( - (deprecated := node.deprecated) is not None + if ( + isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) + and ((deprecated := node.deprecated) is not None) + and not self.is_typeshed_stub ): warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note warn(deprecated, context, code=codes.DEPRECATED) diff --git a/mypy/semanal.py b/mypy/semanal.py index 23310e0974cd..55eb9cbaf426 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3870,6 +3870,7 @@ def analyze_alias( self.tvar_scope, self.plugin, self.options, + self.cur_mod_node, self.is_typeshed_stub_file, allow_placeholder=allow_placeholder, in_dynamic_func=dynamic, @@ -7308,6 +7309,7 @@ def type_analyzer( tvar_scope, self.plugin, self.options, + self.cur_mod_node, self.is_typeshed_stub_file, allow_unbound_tvars=allow_unbound_tvars, allow_tuple_literal=allow_tuple_literal, diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 131a13ffd62d..85f77a269e43 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -303,6 +303,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb [snapshot_type(base) for base in node.bases], [snapshot_type(p) for p in node._promote], dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None, + node.deprecated, ) prefix = node.fullname symbol_table = snapshot_symbol_table(prefix, node.names) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 76ead74d38a8..0c241f5c0f99 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -34,6 +34,7 @@ ArgKind, Context, Decorator, + ImportFrom, MypyFile, ParamSpecExpr, PlaceholderNode, @@ -148,6 +149,7 @@ def analyze_type_alias( tvar_scope: TypeVarLikeScope, plugin: Plugin, options: Options, + cur_mod_node: MypyFile, is_typeshed_stub: bool, allow_placeholder: bool = False, in_dynamic_func: bool = False, @@ -167,6 +169,7 @@ def analyze_type_alias( tvar_scope, plugin, options, + cur_mod_node, is_typeshed_stub, defining_alias=True, allow_placeholder=allow_placeholder, @@ -213,6 +216,7 @@ def __init__( tvar_scope: TypeVarLikeScope, plugin: Plugin, options: Options, + cur_mod_node: MypyFile, is_typeshed_stub: bool, *, defining_alias: bool = False, @@ -266,6 +270,7 @@ def __init__( self.report_invalid_types = report_invalid_types self.plugin = plugin self.options = options + self.cur_mod_node = cur_mod_node self.is_typeshed_stub = is_typeshed_stub # Names of type aliases encountered while analysing a type will be collected here. self.aliases_used: set[str] = set() @@ -771,6 +776,21 @@ 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, fullname) + def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None: + """Similar logic to `TypeChecker.check_deprecated` and `TypeChecker.warn_deprecated.""" + + if ( + (deprecated := info.deprecated) + and not self.is_typeshed_stub + and not (self.api.type and (self.api.type.fullname == info.fullname)) + ): + for imp in self.cur_mod_node.imports: + if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names): + break + else: + warn = self.fail if self.options.report_deprecated_as_error else self.note + warn(deprecated, ctx, code=codes.DEPRECATED) + def analyze_type_with_type_info( self, info: TypeInfo, args: Sequence[Type], ctx: Context, empty_tuple_index: bool ) -> Type: @@ -779,6 +799,8 @@ def analyze_type_with_type_info( This handles simple cases like 'int', 'modname.UserClass[str]', etc. """ + self.check_and_warn_deprecated(info, ctx) + if len(args) > 0 and info.fullname == "builtins.tuple": fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line) return TupleType(self.anal_array(args, allow_unpack=True), fallback, ctx.line) diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 13cebc85513e..fbfdfcce5a14 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -142,9 +142,9 @@ x9: Callable[[int], C] # N: class __main__.C is deprecated: use C2 instead x10: Callable[[int, C, int], int] # N: class __main__.C is deprecated: use C2 instead T = TypeVar("T") -A1: TypeAlias = Optional[C] # ToDo +A1: TypeAlias = Optional[C] # N: class __main__.C is deprecated: use C2 instead x11: A1 -A2: TypeAlias = List[Union[A2, C]] # ToDo +A2: TypeAlias = List[Union[A2, C]] # N: class __main__.C is deprecated: use C2 instead x12: A2 A3: TypeAlias = List[Optional[T]] x13: A3[C] # N: class __main__.C is deprecated: use C2 instead @@ -152,13 +152,119 @@ x13: A3[C] # N: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] +[case testDeprecatedBaseClass] + +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +class D(C): ... # N: class __main__.C is deprecated: use C2 instead +class E(D): ... +class F(D, C): ... # N: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInTypeVar] + +from typing import Generic, TypeVar +from typing_extensions import deprecated + +class B: ... +@deprecated("use C2 instead") +class C: ... + +T = TypeVar("T", bound=C) # N: class __main__.C is deprecated: use C2 instead +def f(x: T) -> T: ... +class D(Generic[T]): ... + +V = TypeVar("V", B, C) # N: class __main__.C is deprecated: use C2 instead +def g(x: V) -> V: ... +class E(Generic[V]): ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInCast] + +from typing import cast, Generic +from typing_extensions import deprecated + +class B: ... +@deprecated("use C2 instead") +class C: ... + +c = C() # N: class __main__.C is deprecated: use C2 instead +b = cast(B, c) + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedInstanceInFunctionDefinition] + +from typing import Generic, List, Optional, TypeVar +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +def f1(c: C) -> None: # N: class __main__.C is deprecated: use C2 instead + def g1() -> None: ... + +def f2(c: List[Optional[C]]) -> None: # N: class __main__.C is deprecated: use C2 instead + def g2() -> None: ... + +def f3() -> C: # N: class __main__.C is deprecated: use C2 instead + def g3() -> None: ... + return C() # N: class __main__.C is deprecated: use C2 instead + +def f4() -> List[Optional[C]]: # N: class __main__.C is deprecated: use C2 instead + def g4() -> None: ... + return [] + +def f5() -> None: + def g5(c: C) -> None: ... # N: class __main__.C is deprecated: use C2 instead + +def f6() -> None: + def g6() -> C: ... # N: class __main__.C is deprecated: use C2 instead + + +@deprecated("use D2 instead") +class D: + + def f1(self, c: C) -> None: # N: class __main__.C is deprecated: use C2 instead + def g1() -> None: ... + + def f2(self, c: List[Optional[C]]) -> None: # N: class __main__.C is deprecated: use C2 instead + def g2() -> None: ... + + def f3(self) -> None: + def g3(c: C) -> None: ... # N: class __main__.C is deprecated: use C2 instead + + def f4(self) -> None: + def g4() -> C: ... # N: class __main__.C is deprecated: use C2 instead + +T = TypeVar("T") + +@deprecated("use E2 instead") +class E(Generic[T]): + + def f1(self: E[C]) -> None: ... # N: class __main__.C is deprecated: use C2 instead + def f2(self, e: E[C]) -> None: ... # N: class __main__.C is deprecated: use C2 instead + def f3(self) -> E[C]: ... # N: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + [case testDeprecatedClassDifferentModule] import m import p.s import m as n import p.s as ps -from m import C # N: class m.C is deprecated: use C2 instead +from m import B, C # N: class m.B is deprecated: use B2 instead \ + # N: class m.C is deprecated: use C2 instead from p.s import D # N: class p.s.D is deprecated: use D2 instead from k import * @@ -170,9 +276,20 @@ C() D() E() # N: class k.E is deprecated: use E2 instead +x1: m.A # N: class m.A is deprecated: use A2 instead +x2: m.A = m.A() # N: class m.A is deprecated: use A2 instead +y1: B +y2: B = B() + [file m.py] from typing_extensions import deprecated +@deprecated("use A2 instead") +class A: ... + +@deprecated("use B2 instead") +class B: ... + @deprecated("use C2 instead") class C: ... diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index d4c61cbf1d5b..a611320909e5 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10837,6 +10837,7 @@ main:1: note: function a.f is deprecated: use f1 instead main:4: note: function a.f is deprecated: use f1 instead == + [case testDeprecatedFunctionAlreadyDecorated1-only_when_cache] from b import f x: str = f() @@ -10905,3 +10906,235 @@ def f() -> str: ... main:1: note: function a.f is deprecated: deprecated decorated function main:4: note: function a.f is deprecated: deprecated decorated function b.py:1: note: function a.f is deprecated: deprecated decorated function + + +[case testDeprecatedAddClassDeprecationIndirectImport1-only_when_cache] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +class C: ... +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: note: class a.C is deprecated: use C2 instead +b.py:2: note: class a.D is deprecated: use D2 instead +main:1: note: class a.C is deprecated: use C2 instead +main:5: note: class a.D is deprecated: use D2 instead +main:6: note: class a.D is deprecated: use D2 instead + + +[case testDeprecatedAddClassDeprecationIndirectImport2-only_when_nocache] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +class C: ... +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:1: note: class a.C is deprecated: use C2 instead +main:5: note: class a.D is deprecated: use D2 instead +main:6: note: class a.D is deprecated: use D2 instead +b.py:1: note: class a.C is deprecated: use C2 instead +b.py:2: note: class a.D is deprecated: use D2 instead + + +[case testDeprecatedChangeClassDeprecationIndirectImport] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing_extensions import deprecated + +@deprecated("use C1 instead") +class C: ... +@deprecated("use D1 instead") +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: note: class a.C is deprecated: use C1 instead +b.py:2: note: class a.D is deprecated: use D1 instead +main:1: note: class a.C is deprecated: use C1 instead +main:5: note: class a.D is deprecated: use D1 instead +main:6: note: class a.D is deprecated: use D1 instead +== +b.py:1: note: class a.C is deprecated: use C2 instead +b.py:2: note: class a.D is deprecated: use D2 instead +main:1: note: class a.C is deprecated: use C2 instead +main:5: note: class a.D is deprecated: use D2 instead +main:6: note: class a.D is deprecated: use D2 instead + + +[case testDeprecatedRemoveClassDeprecationIndirectImport] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing_extensions import deprecated + +@deprecated("use C1 instead") +class C: ... +@deprecated("use D1 instead") +class D: ... + +[file a.py.2] +class C: ... +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: note: class a.C is deprecated: use C1 instead +b.py:2: note: class a.D is deprecated: use D1 instead +main:1: note: class a.C is deprecated: use C1 instead +main:5: note: class a.D is deprecated: use D1 instead +main:6: note: class a.D is deprecated: use D1 instead +== + + +[case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated1-only_when_cache] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing import TypeVar + +T = TypeVar("T") +def dec(x: T) -> T: ... + +@dec +class C: ... +@dec +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: note: class a.C is deprecated: use C2 instead +b.py:2: note: class a.D is deprecated: use D2 instead +main:1: note: class a.C is deprecated: use C2 instead +main:5: note: class a.D is deprecated: use D2 instead +main:6: note: class a.D is deprecated: use D2 instead + + +[case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated2-only_when_nocache] +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing import TypeVar + +T = TypeVar("T") +def dec(x: T) -> T: ... + +@dec +class C: ... +@dec +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:1: note: class a.C is deprecated: use C2 instead +main:5: note: class a.D is deprecated: use D2 instead +main:6: note: class a.D is deprecated: use D2 instead +b.py:1: note: class a.C is deprecated: use C2 instead +b.py:2: note: class a.D is deprecated: use D2 instead From a7069144307ea3c4d17a5d846be11bd8a8901843 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sun, 27 Oct 2024 00:04:35 +0200 Subject: [PATCH 0871/1617] Support ParamSpec mapping with functools.partial (#17355) Follow-up for #17323, resolving a false positive discovered there. Closes #17960. This enables use of `functools.partial` to bind some `*args` or `**kwargs` on a callable typed with `ParamSpec`. --------- Co-authored-by: Shantanu Jain --- mypy/checkexpr.py | 6 +- mypy/plugins/functools.py | 47 ++++++- mypy/types.py | 4 +- .../unit/check-parameter-specification.test | 124 ++++++++++++++++++ 4 files changed, 174 insertions(+), 7 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index d63cf6e782c7..577576a4e5f8 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2377,7 +2377,11 @@ def check_argument_count( # Positional argument when expecting a keyword argument. self.msg.too_many_positional_arguments(callee, context) ok = False - elif callee.param_spec() is not None and not formal_to_actual[i]: + elif ( + callee.param_spec() is not None + and not formal_to_actual[i] + and callee.special_sig != "partial" + ): self.msg.too_few_arguments(callee, context, actual_names) ok = False return ok diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 4dfeb752b5d2..6a063174bfcb 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -8,7 +8,7 @@ import mypy.plugin import mypy.semanal from mypy.argmap import map_actuals_to_formals -from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var +from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, NameExpr, Var from mypy.plugins.common import add_method_to_class from mypy.typeops import get_all_type_vars from mypy.types import ( @@ -16,6 +16,8 @@ CallableType, Instance, Overloaded, + ParamSpecFlavor, + ParamSpecType, Type, TypeOfAny, TypeVarType, @@ -202,6 +204,7 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - continue can_infer_ids.update({tv.id for tv in get_all_type_vars(arg_type)}) + # special_sig="partial" allows omission of args/kwargs typed with ParamSpec defaulted = fn_type.copy_modified( arg_kinds=[ ( @@ -218,6 +221,7 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - # Keep TypeVarTuple/ParamSpec to avoid spurious errors on empty args. if tv.id in can_infer_ids or not isinstance(tv, TypeVarType) ], + special_sig="partial", ) if defaulted.line < 0: # Make up a line number if we don't have one @@ -296,10 +300,19 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - arg_kinds=partial_kinds, arg_names=partial_names, ret_type=ret_type, + special_sig="partial", ) ret = ctx.api.named_generic_type(PARTIAL, [ret_type]) ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied) + if partially_applied.param_spec(): + assert ret.extra_attrs is not None # copy_with_extra_attr above ensures this + attrs = ret.extra_attrs.copy() + if ArgKind.ARG_STAR in actual_arg_kinds: + attrs.immutable.add("__mypy_partial_paramspec_args_bound") + if ArgKind.ARG_STAR2 in actual_arg_kinds: + attrs.immutable.add("__mypy_partial_paramspec_kwargs_bound") + ret.extra_attrs = attrs return ret @@ -314,7 +327,8 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: ): return ctx.default_return_type - partial_type = ctx.type.extra_attrs.attrs["__mypy_partial"] + extra_attrs = ctx.type.extra_attrs + partial_type = get_proper_type(extra_attrs.attrs["__mypy_partial"]) if len(ctx.arg_types) != 2: # *args, **kwargs return ctx.default_return_type @@ -332,11 +346,36 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: actual_arg_kinds.append(ctx.arg_kinds[i][j]) actual_arg_names.append(ctx.arg_names[i][j]) - result = ctx.api.expr_checker.check_call( + result, _ = ctx.api.expr_checker.check_call( callee=partial_type, args=actual_args, arg_kinds=actual_arg_kinds, arg_names=actual_arg_names, context=ctx.context, ) - return result[0] + if not isinstance(partial_type, CallableType) or partial_type.param_spec() is None: + return result + + args_bound = "__mypy_partial_paramspec_args_bound" in extra_attrs.immutable + kwargs_bound = "__mypy_partial_paramspec_kwargs_bound" in extra_attrs.immutable + + passed_paramspec_parts = [ + arg.node.type + for arg in actual_args + if isinstance(arg, NameExpr) + and isinstance(arg.node, Var) + and isinstance(arg.node.type, ParamSpecType) + ] + # ensure *args: P.args + args_passed = any(part.flavor == ParamSpecFlavor.ARGS for part in passed_paramspec_parts) + if not args_bound and not args_passed: + ctx.api.expr_checker.msg.too_few_arguments(partial_type, ctx.context, actual_arg_names) + elif args_bound and args_passed: + ctx.api.expr_checker.msg.too_many_arguments(partial_type, ctx.context) + + # ensure **kwargs: P.kwargs + kwargs_passed = any(part.flavor == ParamSpecFlavor.KWARGS for part in passed_paramspec_parts) + if not kwargs_bound and not kwargs_passed: + ctx.api.expr_checker.msg.too_few_arguments(partial_type, ctx.context, actual_arg_names) + + return result diff --git a/mypy/types.py b/mypy/types.py index 897e19d6ee19..0b010ca9d593 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1827,8 +1827,8 @@ class CallableType(FunctionLike): "implicit", # Was this type implicitly generated instead of explicitly # specified by the user? "special_sig", # Non-None for signatures that require special handling - # (currently only value is 'dict' for a signature similar to - # 'dict') + # (currently only values are 'dict' for a signature similar to + # 'dict' and 'partial' for a `functools.partial` evaluation) "from_type_type", # Was this callable generated by analyzing Type[...] # instantiation? "bound_args", # Bound type args, mostly unused but may be useful for diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f499bac45102..674e3894940b 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2338,3 +2338,127 @@ reveal_type(handle_reversed(Child())) # N: Revealed type is "builtins.str" reveal_type(handle_reversed(NotChild())) # N: Revealed type is "builtins.str" [builtins fixtures/paramspec.pyi] + +[case testBindPartial] +from functools import partial +from typing_extensions import ParamSpec +from typing import Callable, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + return func2(*args) + +def run2(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) + return func2(**kwargs) + +def run3(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2() + +def run4(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2(**kwargs) + +def run_bad(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2(*args) # E: Too many arguments + +def run_bad2(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + return func2(**kwargs) # E: Too few arguments + +def run_bad3(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) + return func2() # E: Too few arguments + +[builtins fixtures/paramspec.pyi] + +[case testBindPartialConcatenate] +from functools import partial +from typing_extensions import Concatenate, ParamSpec +from typing import Callable, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, **kwargs) + return func2(*args) + +def run2(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + p = [""] + func2(1, *p) # E: Too few arguments \ + # E: Argument 2 has incompatible type "*List[str]"; expected "P.args" + func2(1, 2, *p) # E: Too few arguments \ + # E: Argument 2 has incompatible type "int"; expected "P.args" \ + # E: Argument 3 has incompatible type "*List[str]"; expected "P.args" + func2(1, *args, *p) # E: Argument 3 has incompatible type "*List[str]"; expected "P.args" + func2(1, *p, *args) # E: Argument 2 has incompatible type "*List[str]"; expected "P.args" + return func2(1, *args) + +def run3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args) + d = {"":""} + func2(**d) # E: Too few arguments \ + # E: Argument 1 has incompatible type "**Dict[str, str]"; expected "P.kwargs" + return func2(**kwargs) + +def run4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1) + return func2(*args, **kwargs) + +def run5(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args, **kwargs) + func2() + return func2(**kwargs) + +def run_bad(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) # E: Argument 1 has incompatible type "*P.args"; expected "int" + return func2(1, **kwargs) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad2(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args) + func2() # E: Too few arguments + func2(*args, **kwargs) # E: Too many arguments + return func2(1, **kwargs) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, **kwargs) + func2() # E: Too few arguments + return func2(1, *args) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1) + func2() # E: Too few arguments + func2(*args) # E: Too few arguments + func2(1, *args) # E: Too few arguments \ + # E: Argument 1 has incompatible type "int"; expected "P.args" + func2(1, **kwargs) # E: Too few arguments \ + # E: Argument 1 has incompatible type "int"; expected "P.args" + return func2(**kwargs) # E: Too few arguments + +[builtins fixtures/paramspec.pyi] + +[case testOtherVarArgs] +from functools import partial +from typing_extensions import Concatenate, ParamSpec +from typing import Callable, TypeVar, Tuple + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[Concatenate[int, str, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + args_prefix: Tuple[int, str] = (1, 'a') + func2(*args_prefix) # E: Too few arguments + func2(*args, *args_prefix) # E: Argument 1 has incompatible type "*P.args"; expected "int" \ + # E: Argument 1 has incompatible type "*P.args"; expected "str" \ + # E: Argument 2 has incompatible type "*Tuple[int, str]"; expected "P.args" + return func2(*args_prefix, *args) + +[builtins fixtures/paramspec.pyi] From 2d785dffee4440b9e5fd4c4f7dbf8787a829f5fa Mon Sep 17 00:00:00 2001 From: Abel Sen <19356702+neuroevolutus@users.noreply.github.com> Date: Sat, 26 Oct 2024 18:27:22 -0500 Subject: [PATCH 0872/1617] Correct note about `--disallow-any-generics` flag in docs (#18055) Co-authored-by: Jelle Zijlstra --- docs/source/dynamic_typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst index 1c31a535bdc1..304e25c085a8 100644 --- a/docs/source/dynamic_typing.rst +++ b/docs/source/dynamic_typing.rst @@ -86,7 +86,7 @@ treated as ``Any``: reveal_type(x[0]) # Revealed type is "Any" x[0].anything_goes() # OK -You can make mypy warn you about untyped function parameters using the +You can make mypy warn you about missing generic parameters using the :option:`--disallow-any-generics ` flag. Finally, another major source of ``Any`` types leaking into your program is from From 80843fe93afbce9cb699420fc68d4f81df2225af Mon Sep 17 00:00:00 2001 From: Ekin Dursun Date: Sun, 27 Oct 2024 22:31:55 +0300 Subject: [PATCH 0873/1617] Sync typeshed (#18057) Source commit: python/typeshed@61ba4de --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_blake2.pyi | 117 ++++++++++++++++++ mypy/typeshed/stdlib/_interpreters.pyi | 2 +- mypy/typeshed/stdlib/_io.pyi | 21 +++- mypy/typeshed/stdlib/ast.pyi | 17 ++- mypy/typeshed/stdlib/asyncio/tasks.pyi | 9 +- mypy/typeshed/stdlib/builtins.pyi | 37 ++++-- mypy/typeshed/stdlib/contextlib.pyi | 14 ++- .../stdlib/distutils/_msvccompiler.pyi | 13 ++ mypy/typeshed/stdlib/email/message.pyi | 13 +- mypy/typeshed/stdlib/email/policy.pyi | 21 ++++ mypy/typeshed/stdlib/hashlib.pyi | 90 ++++---------- mypy/typeshed/stdlib/http/client.pyi | 8 +- mypy/typeshed/stdlib/itertools.pyi | 11 +- .../stdlib/multiprocessing/synchronize.pyi | 7 +- mypy/typeshed/stdlib/optparse.pyi | 4 +- mypy/typeshed/stdlib/os/__init__.pyi | 8 ++ mypy/typeshed/stdlib/re.pyi | 22 ++-- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 6 +- mypy/typeshed/stdlib/subprocess.pyi | 4 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 6 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 4 +- mypy/typeshed/stdlib/types.pyi | 6 +- mypy/typeshed/stdlib/typing.pyi | 4 +- mypy/typeshed/stdlib/unittest/runner.pyi | 5 +- mypy/typeshed/stdlib/warnings.pyi | 3 +- mypy/typeshed/stdlib/zipimport.pyi | 2 + 27 files changed, 309 insertions(+), 146 deletions(-) create mode 100644 mypy/typeshed/stdlib/_blake2.pyi create mode 100644 mypy/typeshed/stdlib/distutils/_msvccompiler.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index ed23ee6ddcea..1d04b47c77bc 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -22,6 +22,7 @@ __main__: 3.0- _ast: 3.0- _asyncio: 3.0- _bisect: 3.0- +_blake2: 3.6- _bootlocale: 3.4-3.9 _codecs: 3.0- _collections_abc: 3.3- diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi new file mode 100644 index 000000000000..10d7019a222f --- /dev/null +++ b/mypy/typeshed/stdlib/_blake2.pyi @@ -0,0 +1,117 @@ +import sys +from _typeshed import ReadableBuffer +from typing import ClassVar, final +from typing_extensions import Self + +BLAKE2B_MAX_DIGEST_SIZE: int = 64 +BLAKE2B_MAX_KEY_SIZE: int = 64 +BLAKE2B_PERSON_SIZE: int = 16 +BLAKE2B_SALT_SIZE: int = 16 +BLAKE2S_MAX_DIGEST_SIZE: int = 32 +BLAKE2S_MAX_KEY_SIZE: int = 32 +BLAKE2S_PERSON_SIZE: int = 8 +BLAKE2S_SALT_SIZE: int = 8 + +@final +class blake2b: + MAX_DIGEST_SIZE: ClassVar[int] = 64 + MAX_KEY_SIZE: ClassVar[int] = 64 + PERSON_SIZE: ClassVar[int] = 16 + SALT_SIZE: ClassVar[int] = 16 + block_size: int + digest_size: int + name: str + if sys.version_info >= (3, 9): + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 64, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + usedforsecurity: bool = True, + ) -> None: ... + else: + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 64, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + ) -> None: ... + + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... + +@final +class blake2s: + MAX_DIGEST_SIZE: ClassVar[int] = 32 + MAX_KEY_SIZE: ClassVar[int] = 32 + PERSON_SIZE: ClassVar[int] = 8 + SALT_SIZE: ClassVar[int] = 8 + block_size: int + digest_size: int + name: str + if sys.version_info >= (3, 9): + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 32, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + usedforsecurity: bool = True, + ) -> None: ... + else: + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 32, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + ) -> None: ... + + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi index 75f661a7e8e1..a57ef13c6d0f 100644 --- a/mypy/typeshed/stdlib/_interpreters.pyi +++ b/mypy/typeshed/stdlib/_interpreters.pyi @@ -7,7 +7,7 @@ _Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""] class InterpreterError(Exception): ... class InterpreterNotFoundError(InterpreterError): ... -class NotShareableError(Exception): ... +class NotShareableError(ValueError): ... class CrossInterpreterBufferView: def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi index e8290daad106..ccd47726f356 100644 --- a/mypy/typeshed/stdlib/_io.pyi +++ b/mypy/typeshed/stdlib/_io.pyi @@ -86,19 +86,24 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes raw: RawIOBase - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes raw: RawIOBase - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def write(self, buffer: ReadableBuffer, /) -> int: ... -class BufferedRandom(BufferedReader, BufferedWriter, BufferedIOBase, _BufferedIOBase): # type: ignore[misc] # incompatible definitions of methods in the base classes +class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + mode: str + name: Any + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this + def peek(self, size: int = 0, /) -> bytes: ... class BufferedRWPair(BufferedIOBase, _BufferedIOBase): - def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ... + def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192) -> None: ... def peek(self, size: int = ..., /) -> bytes: ... class _TextIOBase(_IOBase): @@ -173,19 +178,23 @@ class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # t # operations. def seek(self, cookie: int, whence: int = 0, /) -> int: ... -class StringIO(TextIOWrapper, TextIOBase, _TextIOBase): # type: ignore[misc] # incompatible definitions of write in the base classes +class StringIO(TextIOBase, _TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... # StringIO does not contain a "name" field. This workaround is necessary # to allow StringIO sub-classes to add this field, as it is defined # as a read-only property on IO[]. name: Any def getvalue(self) -> str: ... + @property + def line_buffering(self) -> bool: ... -class IncrementalNewlineDecoder(codecs.IncrementalDecoder): +class IncrementalNewlineDecoder: def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ... def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... @property def newlines(self) -> str | tuple[str, ...] | None: ... + def getstate(self) -> tuple[bytes, int]: ... + def reset(self) -> None: ... def setstate(self, state: tuple[bytes, int], /) -> None: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 3a43d62a3a60..351a4af2fb75 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -2023,11 +2023,18 @@ class NodeVisitor: def visit_AugLoad(self, node: AugLoad) -> Any: ... def visit_AugStore(self, node: AugStore) -> Any: ... def visit_Param(self, node: Param) -> Any: ... - def visit_Num(self, node: Num) -> Any: ... - def visit_Str(self, node: Str) -> Any: ... - def visit_Bytes(self, node: Bytes) -> Any: ... - def visit_NameConstant(self, node: NameConstant) -> Any: ... - def visit_Ellipsis(self, node: Ellipsis) -> Any: ... + + if sys.version_info < (3, 14): + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Num(self, node: Num) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Str(self, node: Str) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Bytes(self, node: Bytes) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_NameConstant(self, node: NameConstant) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Ellipsis(self, node: Ellipsis) -> Any: ... # type: ignore[deprecated] class NodeTransformer(NodeVisitor): def generic_visit(self, node: AST) -> AST: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 47d1bb13e4a3..d1ff7d425ba4 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -7,7 +7,7 @@ from _asyncio import ( _register_task as _register_task, _unregister_task as _unregister_task, ) -from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator +from collections.abc import AsyncIterator, Awaitable, Coroutine, Generator, Iterable, Iterator from typing import Any, Literal, Protocol, TypeVar, overload from typing_extensions import TypeAlias @@ -84,7 +84,12 @@ FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION ALL_COMPLETED = concurrent.futures.ALL_COMPLETED -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 13): + class _SyncAndAsyncIterator(Iterator[_T_co], AsyncIterator[_T_co], Protocol[_T_co]): ... + + def as_completed(fs: Iterable[_FutureLike[_T]], *, timeout: float | None = None) -> _SyncAndAsyncIterator[Future[_T]]: ... + +elif sys.version_info >= (3, 10): def as_completed(fs: Iterable[_FutureLike[_T]], *, timeout: float | None = None) -> Iterator[Future[_T]]: ... else: diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index eaa6695f7045..1db7f0edc241 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -9,6 +9,7 @@ from _typeshed import ( ConvertibleToFloat, ConvertibleToInt, FileDescriptorOrPath, + MaybeNone, OpenBinaryMode, OpenBinaryModeReading, OpenBinaryModeUpdating, @@ -93,6 +94,9 @@ _SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) _P = ParamSpec("_P") +_StartT = TypeVar("_StartT", covariant=True, default=Any) +_StopT = TypeVar("_StopT", covariant=True, default=Any) +_StepT = TypeVar("_StepT", covariant=True, default=Any) class object: __doc__: str | None @@ -786,7 +790,7 @@ class memoryview(Sequence[_I]): @overload def __setitem__(self, key: slice, value: ReadableBuffer, /) -> None: ... @overload - def __setitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], value: SupportsIndex, /) -> None: ... + def __setitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], value: _I, /) -> None: ... if sys.version_info >= (3, 10): def tobytes(self, order: Literal["C", "F", "A"] | None = "C") -> bytes: ... else: @@ -838,22 +842,31 @@ class bool(int): def __invert__(self) -> int: ... @final -class slice: +class slice(Generic[_StartT, _StopT, _StepT]): @property - def start(self) -> Any: ... + def start(self) -> _StartT: ... @property - def step(self) -> Any: ... + def step(self) -> _StepT: ... @property - def stop(self) -> Any: ... + def stop(self) -> _StopT: ... @overload - def __new__(cls, stop: Any, /) -> Self: ... + def __new__(cls, stop: int | None, /) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... @overload - def __new__(cls, start: Any, stop: Any, step: Any = ..., /) -> Self: ... + def __new__( + cls, start: int | None, stop: int | None, step: int | None = None, / + ) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... + @overload + def __new__(cls, stop: _T2, /) -> slice[Any, _T2, Any]: ... + @overload + def __new__(cls, start: _T1, stop: _T2, /) -> slice[_T1, _T2, Any]: ... + @overload + def __new__(cls, start: _T1, stop: _T2, step: _T3, /) -> slice[_T1, _T2, _T3]: ... def __eq__(self, value: object, /) -> bool: ... if sys.version_info >= (3, 12): def __hash__(self) -> int: ... else: __hash__: ClassVar[None] # type: ignore[assignment] + def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ... class tuple(Sequence[_T_co]): @@ -1117,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class enumerate(Iterator[tuple[int, _T]]): +class enumerate(Generic[_T]): def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... @@ -1311,7 +1324,7 @@ else: def exit(code: sys._ExitCode = None) -> NoReturn: ... -class filter(Iterator[_T]): +class filter(Generic[_T]): @overload def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... @overload @@ -1372,7 +1385,7 @@ def len(obj: Sized, /) -> int: ... def license() -> None: ... def locals() -> dict[str, Any]: ... -class map(Iterator[_S]): +class map(Generic[_S]): @overload def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... @overload @@ -1614,7 +1627,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... def quit(code: sys._ExitCode = None) -> NoReturn: ... -class reversed(Iterator[_T]): +class reversed(Generic[_T]): @overload def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] @overload @@ -1675,7 +1688,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... -class zip(Iterator[_T_co]): +class zip(Generic[_T_co]): if sys.version_info >= (3, 10): @overload def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index fb34251693ad..8a5b7dd6101c 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -58,9 +58,11 @@ class ContextDecorator: def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManager(AbstractContextManager[_T_co, bool | None], ContextDecorator): +class _GeneratorContextManagerBase: ... + +class _GeneratorContextManager(_GeneratorContextManagerBase, AbstractContextManager[_T_co, bool | None], ContextDecorator): # __init__ and all instance attributes are actually inherited from _GeneratorContextManagerBase - # _GeneratorContextManagerBase is more trouble than it's worth to include in the stub; see #6676 + # adding them there is more trouble than it's worth to include in the stub; see #6676 def __init__(self, func: Callable[..., Iterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: Generator[_T_co, Any, Any] func: Callable[..., Generator[_T_co, Any, Any]] @@ -84,9 +86,11 @@ if sys.version_info >= (3, 10): def _recreate_cm(self) -> Self: ... def __call__(self, func: _AF) -> _AF: ... - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator): + class _AsyncGeneratorContextManager( + _GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator + ): # __init__ and these attributes are actually defined in the base class _GeneratorContextManagerBase, - # which is more trouble than it's worth to include in the stub (see #6676) + # adding them there is more trouble than it's worth to include in the stub (see #6676) def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] @@ -97,7 +101,7 @@ if sys.version_info >= (3, 10): ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None]): + class _AsyncGeneratorContextManager(_GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None]): def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] diff --git a/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi b/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi new file mode 100644 index 000000000000..bba9373b72db --- /dev/null +++ b/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi @@ -0,0 +1,13 @@ +from _typeshed import Incomplete +from distutils.ccompiler import CCompiler +from typing import ClassVar, Final + +PLAT_SPEC_TO_RUNTIME: Final[dict[str, str]] +PLAT_TO_VCVARS: Final[dict[str, str]] + +class MSVCCompiler(CCompiler): + compiler_type: ClassVar[str] + executables: ClassVar[dict[Incomplete, Incomplete]] + res_extension: ClassVar[str] + initialized: bool + def initialize(self, plat_name: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index f04b014431f9..ebad05a1cf7b 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -1,3 +1,4 @@ +from _typeshed import MaybeNone from collections.abc import Generator, Iterator, Sequence from email import _ParamsType, _ParamType from email.charset import Charset @@ -42,17 +43,17 @@ class Message(Generic[_HeaderT, _HeaderParamT]): def get_unixfrom(self) -> str | None: ... def attach(self, payload: _PayloadType) -> None: ... # `i: int` without a multipart payload results in an error - # `| Any`: can be None for cleared or unset payload, but annoying to check + # `| MaybeNone` acts like `| Any`: can be None for cleared or unset payload, but annoying to check @overload # multipart def get_payload(self, i: int, decode: Literal[True]) -> None: ... @overload # multipart - def get_payload(self, i: int, decode: Literal[False] = False) -> _PayloadType | Any: ... + def get_payload(self, i: int, decode: Literal[False] = False) -> _PayloadType | MaybeNone: ... @overload # either - def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | Any: ... + def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | MaybeNone: ... @overload # not multipart - def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | Any: ... + def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | MaybeNone: ... @overload # not multipart, IDEM but w/o kwarg - def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | Any: ... + def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | MaybeNone: ... # If `charset=None` and payload supports both `encode` AND `decode`, # then an invalid payload could be passed, but this is unlikely # Not[_SupportsEncodeToPayload] @@ -75,7 +76,7 @@ class Message(Generic[_HeaderT, _HeaderParamT]): # This is important for protocols using __getitem__, like SupportsKeysAndGetItem # Morally, the return type should be `AnyOf[_HeaderType, None]`, # so using "the Any trick" instead. - def __getitem__(self, name: str) -> _HeaderT | Any: ... + def __getitem__(self, name: str) -> _HeaderT | MaybeNone: ... def __setitem__(self, name: str, val: _HeaderParamT) -> None: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index 5adf4a8c2390..5b145bcf2318 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -3,6 +3,7 @@ from email._policybase import Compat32 as Compat32, Policy as Policy, _MessageFa from email.contentmanager import ContentManager from email.message import EmailMessage, Message from typing import Any, TypeVar, overload +from typing_extensions import Self __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] @@ -23,6 +24,8 @@ class EmailPolicy(Policy[_MessageT]): raise_on_defect: bool = ..., mangle_from_: bool = ..., message_factory: None = None, + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., utf8: bool = ..., refold_source: str = ..., header_factory: Callable[[str, str], str] = ..., @@ -38,6 +41,8 @@ class EmailPolicy(Policy[_MessageT]): raise_on_defect: bool = ..., mangle_from_: bool = ..., message_factory: _MessageFactory[_MessageT] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., utf8: bool = ..., refold_source: str = ..., header_factory: Callable[[str, str], str] = ..., @@ -48,6 +53,22 @@ class EmailPolicy(Policy[_MessageT]): def header_fetch_parse(self, name: str, value: str) -> Any: ... def fold(self, name: str, value: str) -> Any: ... def fold_binary(self, name: str, value: str) -> bytes: ... + def clone( + self, + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., + utf8: bool = ..., + refold_source: str = ..., + header_factory: Callable[[str, str], str] = ..., + content_manager: ContentManager = ..., + ) -> Self: ... default: EmailPolicy[EmailMessage] SMTP: EmailPolicy[EmailMessage] diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 93bd986c9d31..74687d9abe3d 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,7 +1,8 @@ import sys +from _blake2 import blake2b as blake2b, blake2s as blake2s from _typeshed import ReadableBuffer from collections.abc import Callable, Set as AbstractSet -from typing import Protocol, final +from typing import Protocol from typing_extensions import Self if sys.version_info >= (3, 11): @@ -55,12 +56,20 @@ class _Hash: def block_size(self) -> int: ... @property def name(self) -> str: ... - def __init__(self, data: ReadableBuffer = ...) -> None: ... def copy(self) -> Self: ... def digest(self) -> bytes: ... def hexdigest(self) -> str: ... def update(self, data: ReadableBuffer, /) -> None: ... +class _VarLenHash: + digest_size: int + block_size: int + name: str + def copy(self) -> _VarLenHash: ... + def digest(self, length: int, /) -> bytes: ... + def hexdigest(self, length: int, /) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... + if sys.version_info >= (3, 9): def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> _Hash: ... def md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... @@ -69,6 +78,12 @@ if sys.version_info >= (3, 9): def sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... def sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... def sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _VarLenHash: ... + def shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _VarLenHash: ... else: def new(name: str, data: ReadableBuffer = b"") -> _Hash: ... @@ -78,6 +93,12 @@ else: def sha256(string: ReadableBuffer = b"") -> _Hash: ... def sha384(string: ReadableBuffer = b"") -> _Hash: ... def sha512(string: ReadableBuffer = b"") -> _Hash: ... + def sha3_224(string: ReadableBuffer = b"") -> _Hash: ... + def sha3_256(string: ReadableBuffer = b"") -> _Hash: ... + def sha3_384(string: ReadableBuffer = b"") -> _Hash: ... + def sha3_512(string: ReadableBuffer = b"") -> _Hash: ... + def shake_128(string: ReadableBuffer = b"") -> _VarLenHash: ... + def shake_256(string: ReadableBuffer = b"") -> _VarLenHash: ... algorithms_guaranteed: AbstractSet[str] algorithms_available: AbstractSet[str] @@ -85,74 +106,9 @@ algorithms_available: AbstractSet[str] def pbkdf2_hmac( hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None ) -> bytes: ... - -class _VarLenHash: - digest_size: int - block_size: int - name: str - def __init__(self, data: ReadableBuffer = ...) -> None: ... - def copy(self) -> _VarLenHash: ... - def digest(self, length: int, /) -> bytes: ... - def hexdigest(self, length: int, /) -> str: ... - def update(self, data: ReadableBuffer, /) -> None: ... - -sha3_224 = _Hash -sha3_256 = _Hash -sha3_384 = _Hash -sha3_512 = _Hash -shake_128 = _VarLenHash -shake_256 = _VarLenHash - def scrypt( password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 ) -> bytes: ... -@final -class _BlakeHash(_Hash): - MAX_DIGEST_SIZE: int - MAX_KEY_SIZE: int - PERSON_SIZE: int - SALT_SIZE: int - - if sys.version_info >= (3, 9): - def __init__( - self, - data: ReadableBuffer = ..., - /, - *, - digest_size: int = ..., - key: ReadableBuffer = ..., - salt: ReadableBuffer = ..., - person: ReadableBuffer = ..., - fanout: int = ..., - depth: int = ..., - leaf_size: int = ..., - node_offset: int = ..., - node_depth: int = ..., - inner_size: int = ..., - last_node: bool = ..., - usedforsecurity: bool = ..., - ) -> None: ... - else: - def __init__( - self, - data: ReadableBuffer = ..., - /, - *, - digest_size: int = ..., - key: ReadableBuffer = ..., - salt: ReadableBuffer = ..., - person: ReadableBuffer = ..., - fanout: int = ..., - depth: int = ..., - leaf_size: int = ..., - node_offset: int = ..., - node_depth: int = ..., - inner_size: int = ..., - last_node: bool = ..., - ) -> None: ... - -blake2b = _BlakeHash -blake2s = _BlakeHash if sys.version_info >= (3, 11): class _BytesIOLike(Protocol): diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index f47c744a6c7d..3db764ef1e7c 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -3,10 +3,10 @@ import io import ssl import sys import types -from _typeshed import ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer +from _typeshed import MaybeNone, ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer from collections.abc import Callable, Iterable, Iterator, Mapping from socket import socket -from typing import Any, BinaryIO, TypeVar, overload +from typing import BinaryIO, TypeVar, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -154,7 +154,7 @@ class HTTPConnection: timeout: float | None host: str port: int - sock: socket | Any # can be `None` if `.connect()` was not called + sock: socket | MaybeNone # can be `None` if `.connect()` was not called def __init__( self, host: str, @@ -187,7 +187,7 @@ class HTTPConnection: class HTTPSConnection(HTTPConnection): # Can be `None` if `.connect()` was not called: - sock: ssl.SSLSocket | Any + sock: ssl.SSLSocket | MaybeNone if sys.version_info >= (3, 12): def __init__( self, diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 18076011178f..013c3cba120f 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import MaybeNone from collections.abc import Callable, Iterable, Iterator from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload from typing_extensions import Self, TypeAlias @@ -122,7 +123,7 @@ class zip_longest(Generic[_T_co]): # In the overloads without fillvalue, all of the tuple members could theoretically be None, # but we return Any instead to avoid false positives for code where we know one of the iterables # is longer. - def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip_longest[tuple[_T1 | Any, _T2 | Any]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, fillvalue: _T @@ -131,7 +132,7 @@ class zip_longest(Generic[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, fillvalue: _T @@ -140,7 +141,7 @@ class zip_longest(Generic[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone, _T4 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, fillvalue: _T @@ -149,7 +150,7 @@ class zip_longest(Generic[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any, _T5 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone, _T4 | MaybeNone, _T5 | MaybeNone]]: ... @overload def __new__( cls, @@ -174,7 +175,7 @@ class zip_longest(Generic[_T_co]): iter6: Iterable[_T], /, *iterables: Iterable[_T], - ) -> zip_longest[tuple[_T | Any, ...]]: ... + ) -> zip_longest[tuple[_T | MaybeNone, ...]]: ... @overload def __new__( cls, diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index b417925fb17b..d6f46b527905 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -1,6 +1,5 @@ import threading from collections.abc import Callable -from contextlib import AbstractContextManager from multiprocessing.context import BaseContext from types import TracebackType from typing_extensions import TypeAlias @@ -14,7 +13,7 @@ class Barrier(threading.Barrier): self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *ctx: BaseContext ) -> None: ... -class Condition(AbstractContextManager[bool, None]): +class Condition: def __init__(self, lock: _LockLike | None = None, *, ctx: BaseContext) -> None: ... def notify(self, n: int = 1) -> None: ... def notify_all(self) -> None: ... @@ -22,6 +21,7 @@ class Condition(AbstractContextManager[bool, None]): def wait_for(self, predicate: Callable[[], bool], timeout: float | None = None) -> bool: ... def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... def release(self) -> None: ... + def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... @@ -34,9 +34,10 @@ class Event: def wait(self, timeout: float | None = None) -> bool: ... # Not part of public API -class SemLock(AbstractContextManager[bool, None]): +class SemLock: def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... def release(self) -> None: ... + def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index b513bb647060..d6db7a06f291 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,4 +1,4 @@ -from _typeshed import Incomplete +from _typeshed import Incomplete, MaybeNone from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence from typing import IO, Any, AnyStr, Literal, NoReturn, overload @@ -56,7 +56,7 @@ class HelpFormatter: current_indent: int default_tag: str help_position: int - help_width: int | Any # initialized as None and computed later as int when storing option strings + help_width: int | MaybeNone # initialized as None and computed later as int when storing option strings indent_increment: int level: int max_help_position: int diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 833f9b1e56cb..203956786a9b 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -1076,6 +1076,14 @@ if sys.platform != "win32": def cpu_count() -> int | None: ... +if sys.version_info >= (3, 13): + # Documented to return `int | None`, but falls back to `len(sched_getaffinity(0))` when + # available. See https://github.com/python/cpython/blob/417c130/Lib/os.py#L1175-L1186. + if sys.platform != "win32" and sys.platform != "darwin": + def process_cpu_count() -> int: ... + else: + def process_cpu_count() -> int | None: ... + if sys.platform != "win32": # Unix only def confstr(name: str | int, /) -> str | None: ... diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index ac297b7d4567..b8fe2e9e1a46 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -2,7 +2,7 @@ import enum import sre_compile import sre_constants import sys -from _typeshed import ReadableBuffer +from _typeshed import MaybeNone, ReadableBuffer from collections.abc import Callable, Iterator, Mapping from typing import Any, AnyStr, Generic, Literal, TypeVar, final, overload from typing_extensions import TypeAlias @@ -90,19 +90,19 @@ class Match(Generic[AnyStr]): @overload def group(self, group: Literal[0] = 0, /) -> AnyStr: ... @overload - def group(self, group: str | int, /) -> AnyStr | Any: ... + def group(self, group: str | int, /) -> AnyStr | MaybeNone: ... @overload - def group(self, group1: str | int, group2: str | int, /, *groups: str | int) -> tuple[AnyStr | Any, ...]: ... + def group(self, group1: str | int, group2: str | int, /, *groups: str | int) -> tuple[AnyStr | MaybeNone, ...]: ... # Each item of groups()'s return tuple is either "AnyStr" or # "AnyStr | None", depending on the pattern. @overload - def groups(self) -> tuple[AnyStr | Any, ...]: ... + def groups(self) -> tuple[AnyStr | MaybeNone, ...]: ... @overload def groups(self, default: _T) -> tuple[AnyStr | _T, ...]: ... # Each value in groupdict()'s return dict is either "AnyStr" or # "AnyStr | None", depending on the pattern. @overload - def groupdict(self) -> dict[str, AnyStr | Any]: ... + def groupdict(self) -> dict[str, AnyStr | MaybeNone]: ... @overload def groupdict(self, default: _T) -> dict[str, AnyStr | _T]: ... def start(self, group: int | str = 0, /) -> int: ... @@ -114,7 +114,7 @@ class Match(Generic[AnyStr]): @overload def __getitem__(self, key: Literal[0], /) -> AnyStr: ... @overload - def __getitem__(self, key: int | str, /) -> AnyStr | Any: ... + def __getitem__(self, key: int | str, /) -> AnyStr | MaybeNone: ... def __copy__(self) -> Match[AnyStr]: ... def __deepcopy__(self, memo: Any, /) -> Match[AnyStr]: ... if sys.version_info >= (3, 9): @@ -151,11 +151,11 @@ class Pattern(Generic[AnyStr]): @overload def fullmatch(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload - def split(self: Pattern[str], string: str, maxsplit: int = 0) -> list[str | Any]: ... + def split(self: Pattern[str], string: str, maxsplit: int = 0) -> list[str | MaybeNone]: ... @overload - def split(self: Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0) -> list[bytes | Any]: ... + def split(self: Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0) -> list[bytes | MaybeNone]: ... @overload - def split(self, string: AnyStr, maxsplit: int = 0) -> list[AnyStr | Any]: ... + def split(self, string: AnyStr, maxsplit: int = 0) -> list[AnyStr | MaybeNone]: ... # return type depends on the number of groups in the pattern @overload def findall(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> list[Any]: ... @@ -270,11 +270,11 @@ def fullmatch(pattern: str | Pattern[str], string: str, flags: _FlagsType = 0) - @overload def fullmatch(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = 0) -> Match[bytes] | None: ... @overload -def split(pattern: str | Pattern[str], string: str, maxsplit: int = 0, flags: _FlagsType = 0) -> list[str | Any]: ... +def split(pattern: str | Pattern[str], string: str, maxsplit: int = 0, flags: _FlagsType = 0) -> list[str | MaybeNone]: ... @overload def split( pattern: bytes | Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0, flags: _FlagsType = 0 -) -> list[bytes | Any]: ... +) -> list[bytes | MaybeNone]: ... @overload def findall(pattern: str | Pattern[str], string: str, flags: _FlagsType = 0) -> list[Any]: ... @overload diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index d6087aa755c1..931e8d03cb75 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused +from _typeshed import MaybeNone, ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence from sqlite3.dbapi2 import ( PARSE_COLNAMES as PARSE_COLNAMES, @@ -401,9 +401,9 @@ class Cursor: arraysize: int @property def connection(self) -> Connection: ... - # May be None, but using | Any instead to avoid slightly annoying false positives. + # May be None, but using `| MaybeNone` (`| Any`) instead to avoid slightly annoying false positives. @property - def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | Any: ... + def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | MaybeNone: ... @property def lastrowid(self) -> int | None: ... row_factory: Callable[[Cursor, Row], object] | None diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 703a5012012c..fef35b56945a 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import ReadableBuffer, StrOrBytesPath +from _typeshed import MaybeNone, ReadableBuffer, StrOrBytesPath from collections.abc import Callable, Collection, Iterable, Mapping, Sequence from types import TracebackType from typing import IO, Any, AnyStr, Final, Generic, Literal, TypeVar, overload @@ -1848,7 +1848,7 @@ class Popen(Generic[AnyStr]): stdout: IO[AnyStr] | None stderr: IO[AnyStr] | None pid: int - returncode: int | Any + returncode: int | MaybeNone universal_newlines: bool if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 4d25a04f8eb7..5d3550ea62ab 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,6 +1,6 @@ import _tkinter import sys -from _typeshed import Incomplete, StrEnum, StrOrBytesPath +from _typeshed import Incomplete, MaybeNone, StrEnum, StrOrBytesPath from collections.abc import Callable, Iterable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription @@ -509,7 +509,7 @@ class Misc: pad: _ScreenUnits = ..., uniform: str = ..., weight: int = ..., - ) -> _GridIndexInfo | Any: ... # can be None but annoying to check + ) -> _GridIndexInfo | MaybeNone: ... # can be None but annoying to check def grid_rowconfigure( self, index: int | str | list[int] | tuple[int, ...], @@ -519,7 +519,7 @@ class Misc: pad: _ScreenUnits = ..., uniform: str = ..., weight: int = ..., - ) -> _GridIndexInfo | Any: ... # can be None but annoying to check + ) -> _GridIndexInfo | MaybeNone: ... # can be None but annoying to check columnconfigure = grid_columnconfigure rowconfigure = grid_rowconfigure def grid_location(self, x: _ScreenUnits, y: _ScreenUnits) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index b851f478140a..dacef0620b22 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1,6 +1,6 @@ import _tkinter import tkinter -from _typeshed import Incomplete +from _typeshed import Incomplete, MaybeNone from collections.abc import Callable from tkinter.font import _FontDescription from typing import Any, Literal, TypedDict, overload @@ -1156,7 +1156,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): background: str = ..., font: _FontDescription = ..., image: tkinter._ImageSpec = ..., - ) -> _TreeviewTagDict | Any: ... # can be None but annoying to check + ) -> _TreeviewTagDict | MaybeNone: ... # can be None but annoying to check @overload def tag_has(self, tagname: str, item: None = None) -> tuple[str, ...]: ... @overload diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 0c5a43617040..ebea6f22fb12 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import SupportsKeysAndGetItem +from _typeshed import MaybeNone, SupportsKeysAndGetItem from _typeshed.importlib import LoaderProtocol from collections.abc import ( AsyncGenerator, @@ -528,9 +528,9 @@ class FrameType: def f_lasti(self) -> int: ... # see discussion in #6769: f_lineno *can* sometimes be None, # but you should probably file a bug report with CPython if you encounter it being None in the wild. - # An `int | None` annotation here causes too many false-positive errors. + # An `int | None` annotation here causes too many false-positive errors, so applying `int | Any`. @property - def f_lineno(self) -> int | Any: ... + def f_lineno(self) -> int | MaybeNone: ... @property def f_locals(self) -> dict[str, Any]: ... f_trace: Callable[[FrameType, str, Any], Any] | None diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index ce16d9adff99..27ae11daba70 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -575,7 +575,7 @@ class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): @abstractmethod def __len__(self) -> int: ... -class Sequence(Collection[_T_co], Reversible[_T_co]): +class Sequence(Reversible[_T_co], Collection[_T_co]): @overload @abstractmethod def __getitem__(self, index: int) -> _T_co: ... @@ -760,7 +760,7 @@ TYPE_CHECKING: bool # In stubs, the arguments of the IO class are marked as positional-only. # This differs from runtime, but better reflects the fact that in reality # classes deriving from IO use different names for the arguments. -class IO(Iterator[AnyStr]): +class IO(Generic[AnyStr]): # At runtime these are all abstract properties, # but making them abstract in the stub is hugely disruptive, for not much gain. # See #8726 diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index be546f42315e..393d03dfa0fc 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -18,13 +18,16 @@ class _TextTestStream(_SupportsWriteAndFlush, Protocol): # _WritelnDecorator should have all the same attrs as its stream param. # But that's not feasible to do Generically # We can expand the attributes if requested -class _WritelnDecorator(_TextTestStream): +class _WritelnDecorator: def __init__(self, stream: _TextTestStream) -> None: ... def writeln(self, arg: str | None = None) -> str: ... def __getattr__(self, attr: str) -> Any: ... # Any attribute from the stream type passed to __init__ # These attributes are prevented by __getattr__ stream: Never __getstate__: Never + # Methods proxied from the wrapped stream object via __getattr__ + def flush(self) -> object: ... + def write(self, s: str, /) -> object: ... _StreamT = TypeVar("_StreamT", bound=_TextTestStream, default=_WritelnDecorator) diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index c7ab1cb091dd..533a36817506 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -1,3 +1,4 @@ +import re import sys from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence @@ -25,7 +26,7 @@ if sys.version_info >= (3, 14): _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] else: _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "all", "module", "once"] -filters: Sequence[tuple[str, str | None, type[Warning], str | None, int]] # undocumented, do not mutate +filters: Sequence[tuple[str, re.Pattern[str] | None, type[Warning], re.Pattern[str] | None, int]] # undocumented, do not mutate def showwarning( message: Warning | str, diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index f53b09e188eb..71d4f4478c43 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -3,6 +3,7 @@ from _typeshed import StrOrBytesPath from importlib.abc import ResourceReader from importlib.machinery import ModuleSpec from types import CodeType, ModuleType +from typing_extensions import deprecated __all__ = ["ZipImportError", "zipimporter"] @@ -26,6 +27,7 @@ class zipimporter: def get_resource_reader(self, fullname: str) -> ResourceReader | None: ... # undocumented def get_source(self, fullname: str) -> str | None: ... def is_package(self, fullname: str) -> bool: ... + @deprecated("Deprecated since 3.10; use exec_module() instead") def load_module(self, fullname: str) -> ModuleType: ... if sys.version_info >= (3, 10): def exec_module(self, module: ModuleType) -> None: ... From 7f09f0c83abb192a91f39b6cbe8896a09440cd5c Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 27 Oct 2024 15:34:06 -0400 Subject: [PATCH 0874/1617] Emit `[mutable-override]` for covariant override of attribute with method (#18058) Fixes #18052 Given: ```python # flags: --enable-error-code=mutable-override from typing import Callable class Parent: func: Callable[[str], None] class Child(Parent): def func(self, x: object) -> None: pass ``` Before: ``` Success: no issues found in 1 source file ``` After: ``` main.py:7 error: Covariant override of a mutable attribute (base class "Parent" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") Found 1 error in 1 file (checked 1 source file) ``` --- mypy/checker.py | 20 +++++++++- test-data/unit/check-classes.test | 66 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 8644e8d2e93e..e2bb35613066 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2136,7 +2136,7 @@ def check_method_override_for_base_with_name( pass elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): # Check that the types are compatible. - self.check_override( + ok = self.check_override( typ, original_type, defn.name, @@ -2146,6 +2146,21 @@ def check_method_override_for_base_with_name( override_class_or_static, context, ) + if ( + ok + and original_node + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + and not is_subtype(original_type, typ, ignore_pos_arg_names=True) + ): + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options + ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) elif is_equivalent(original_type, typ): # Assume invariance for a non-callable attribute here. Note # that this doesn't affect read-only properties which can have @@ -2235,7 +2250,7 @@ def check_override( original_class_or_static: bool, override_class_or_static: bool, node: Context, - ) -> None: + ) -> bool: """Check a method override with given signatures. Arguments: @@ -2385,6 +2400,7 @@ def erase_override(t: Type) -> Type: node, code=codes.OVERRIDE, ) + return not fail def check__exit__return_type(self, defn: FuncItem) -> None: """Generate error if the return type of __exit__ is problematic. diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 82208d27df41..a6ae963d6c23 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -710,6 +710,72 @@ class B(A): @dec def f(self) -> Y: pass +[case testOverrideCallableAttributeWithMethod] +from typing import Callable + +class A: + f1: Callable[[str], None] + f2: Callable[[str], None] + f3: Callable[[str], None] + +class B(A): + def f1(self, x: object) -> None: + pass + + @classmethod + def f2(cls, x: object) -> None: + pass + + @staticmethod + def f3(x: object) -> None: + pass +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableAttributeWithSettableProperty] +from typing import Callable + +class A: + f: Callable[[str], None] + +class B(A): + @property + def f(self) -> Callable[[object], None]: pass + @func.setter + def f(self, x: object) -> None: pass +[builtins fixtures/property.pyi] + +[case testOverrideCallableAttributeWithMethodMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable + +class A: + f1: Callable[[str], None] + f2: Callable[[str], None] + f3: Callable[[str], None] + +class B(A): + def f1(self, x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + + @classmethod + def f2(cls, x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + + @staticmethod + def f3(x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableAttributeWithSettablePropertyMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable + +class A: + f: Callable[[str], None] + +class B(A): + @property # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + def f(self) -> Callable[[object], None]: pass + @func.setter + def f(self, x: object) -> None: pass +[builtins fixtures/property.pyi] -- Constructors -- ------------ From 53134979cd4442b434637eef81557d4c0b2a5d00 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 28 Oct 2024 07:50:25 +0100 Subject: [PATCH 0875/1617] stubgen: add support for PEPs 695 and 696 syntax (#18054) closes #17997 --- mypy/stubdoc.py | 5 +- mypy/stubgen.py | 20 +++++++- mypy/stubutil.py | 26 +++++++++++ test-data/unit/stubgen.test | 93 +++++++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 5 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 928d024514f3..434de0ea3bcb 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -76,6 +76,7 @@ class FunctionSig(NamedTuple): name: str args: list[ArgSig] ret_type: str | None + type_args: str = "" # TODO implement in stubgenc and remove the default def is_special_method(self) -> bool: return bool( @@ -141,9 +142,7 @@ def format_sig( retfield = " -> " + ret_type prefix = "async " if is_async else "" - sig = "{indent}{prefix}def {name}({args}){ret}:".format( - indent=indent, prefix=prefix, name=self.name, args=", ".join(args), ret=retfield - ) + sig = f"{indent}{prefix}def {self.name}{self.type_args}({', '.join(args)}){retfield}:" if docstring: suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" else: diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 02c0c1e58ab5..684e85d58758 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -106,6 +106,7 @@ StrExpr, TempNode, TupleExpr, + TypeAliasStmt, TypeInfo, UnaryExpr, Var, @@ -398,6 +399,9 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: for name in get_assigned_names(o.lvalues): self.names.add(name) + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + self.names.add(o.name.name) + def find_referenced_names(file: MypyFile) -> set[str]: finder = ReferenceFinder() @@ -507,7 +511,8 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: def get_default_function_sig(self, func_def: FuncDef, ctx: FunctionContext) -> FunctionSig: args = self._get_func_args(func_def, ctx) retname = self._get_func_return(func_def, ctx) - return FunctionSig(func_def.name, args, retname) + type_args = self.format_type_args(func_def) + return FunctionSig(func_def.name, args, retname, type_args) def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: args: list[ArgSig] = [] @@ -765,7 +770,8 @@ def visit_class_def(self, o: ClassDef) -> None: self.import_tracker.add_import("abc") self.import_tracker.require_name("abc") bases = f"({', '.join(base_types)})" if base_types else "" - self.add(f"{self._indent}class {o.name}{bases}:\n") + type_args = self.format_type_args(o) + self.add(f"{self._indent}class {o.name}{type_args}{bases}:\n") self.indent() if self._include_docstrings and o.docstring: docstring = mypy.util.quote_docstring(o.docstring) @@ -1101,6 +1107,16 @@ def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: self.record_name(lvalue.name) self._vars[-1].append(lvalue.name) + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + """Type aliases defined with the `type` keyword (PEP 695).""" + p = AliasPrinter(self) + name = o.name.name + rvalue = o.value.expr() + type_args = self.format_type_args(o) + self.add(f"{self._indent}type {name}{type_args} = {rvalue.accept(p)}\n") + self.record_name(name) + self._vars[-1].append(name) + def visit_if_stmt(self, o: IfStmt) -> None: # Ignore if __name__ == '__main__'. expr = o.expr[0] diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 04b36e149957..e824c3f1a8e2 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -16,6 +16,7 @@ import mypy.options from mypy.modulefinder import ModuleNotFoundReason from mypy.moduleinspect import InspectError, ModuleInspect +from mypy.nodes import PARAM_SPEC_KIND, TYPE_VAR_TUPLE_KIND, ClassDef, FuncDef, TypeAliasStmt from mypy.stubdoc import ArgSig, FunctionSig from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType @@ -777,6 +778,31 @@ def format_func_def( ) return lines + def format_type_args(self, o: TypeAliasStmt | FuncDef | ClassDef) -> str: + if not o.type_args: + return "" + p = AnnotationPrinter(self) + type_args_list: list[str] = [] + for type_arg in o.type_args: + if type_arg.kind == PARAM_SPEC_KIND: + prefix = "**" + elif type_arg.kind == TYPE_VAR_TUPLE_KIND: + prefix = "*" + else: + prefix = "" + if type_arg.upper_bound: + bound_or_values = f": {type_arg.upper_bound.accept(p)}" + elif type_arg.values: + bound_or_values = f": ({', '.join(v.accept(p) for v in type_arg.values)})" + else: + bound_or_values = "" + if type_arg.default: + default = f" = {type_arg.default.accept(p)}" + else: + default = "" + type_args_list.append(f"{prefix}{type_arg.name}{bound_or_values}{default}") + return "[" + ", ".join(type_args_list) + "]" + def print_annotation( self, t: Type, diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 69781e9d2143..e64c9c66d65d 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4415,3 +4415,96 @@ class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... [out] class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... + +[case testPEP695GenericClass] +# flags: --python-version=3.12 + +class C[T]: ... +class C1[T1](int): ... +class C2[T2: int]: ... +class C3[T3: str | bytes]: ... +class C4[T4: (str, bytes)]: ... + +class Outer: + class Inner[T]: ... + +[out] +class C[T]: ... +class C1[T1](int): ... +class C2[T2: int]: ... +class C3[T3: str | bytes]: ... +class C4[T4: (str, bytes)]: ... + +class Outer: + class Inner[T]: ... + +[case testPEP695GenericFunction] +# flags: --python-version=3.12 + +def f1[T1](): ... +def f2[T2: int](): ... +def f3[T3: str | bytes](): ... +def f4[T4: (str, bytes)](): ... + +class C: + def m[T](self, x: T) -> T: ... + +[out] +def f1[T1]() -> None: ... +def f2[T2: int]() -> None: ... +def f3[T3: str | bytes]() -> None: ... +def f4[T4: (str, bytes)]() -> None: ... + +class C: + def m[T](self, x: T) -> T: ... + +[case testPEP695TypeAlias] +# flags: --python-version=3.12 + +type Alias = int | str +type Alias1[T1] = list[T1] | set[T1] +type Alias2[T2: int] = list[T2] | set[T2] +type Alias3[T3: str | bytes] = list[T3] | set[T3] +type Alias4[T4: (str, bytes)] = list[T4] | set[T4] + +class C: + type IndentedAlias[T] = list[T] + +[out] +type Alias = int | str +type Alias1[T1] = list[T1] | set[T1] +type Alias2[T2: int] = list[T2] | set[T2] +type Alias3[T3: str | bytes] = list[T3] | set[T3] +type Alias4[T4: (str, bytes)] = list[T4] | set[T4] +class C: + type IndentedAlias[T] = list[T] + +[case testPEP695Syntax_semanal] +# flags: --python-version=3.12 + +class C[T]: ... +def f[S](): ... +type A[R] = list[R] + +[out] +class C[T]: ... + +def f[S]() -> None: ... +type A[R] = list[R] + +[case testPEP696Syntax] +# flags: --python-version=3.13 + +type Alias1[T1 = int] = list[T1] | set[T1] +type Alias2[T2: int | float = int] = list[T2] | set[T2] +class C3[T3 = int]: ... +class C4[T4: int | float = int](list[T4]): ... +def f5[T5 = int](): ... + +[out] +type Alias1[T1 = int] = list[T1] | set[T1] +type Alias2[T2: int | float = int] = list[T2] | set[T2] +class C3[T3 = int]: ... +class C4[T4: int | float = int](list[T4]): ... + +def f5[T5 = int]() -> None: ... From 06a566b81993a711470aa9e80b9d86d1e666b029 Mon Sep 17 00:00:00 2001 From: Ruslan Sayfutdinov Date: Mon, 28 Oct 2024 18:17:42 +0000 Subject: [PATCH 0876/1617] stubgen: Add flagfile support (#18061) ## Description This is already supported by main `mypy` binary and `dmypy` but not by `stubgen`. ## Context I maintain https://github.com/KapJI/homeassistant-stubs which automatically generates typing stubs. Currently list of arguments there is 50 KB long and this can be longer that supported by OS (depending on configuration). It'd help to write these args to flagfile. --- mypy/stubgen.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 684e85d58758..4626d8b2e11d 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1793,7 +1793,9 @@ def generate_stubs(options: Options) -> None: def parse_options(args: list[str]) -> Options: - parser = argparse.ArgumentParser(prog="stubgen", usage=HEADER, description=DESCRIPTION) + parser = argparse.ArgumentParser( + prog="stubgen", usage=HEADER, description=DESCRIPTION, fromfile_prefix_chars="@" + ) parser.add_argument( "--ignore-errors", From c7c42881466b4df9b850638a1707f76b06e5d93f Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 28 Oct 2024 20:54:30 -0400 Subject: [PATCH 0877/1617] Allow union-with-callable attributes to be overridden by methods (#18018) Fixes #12569 ### Before ```python from typing import Callable class Parent: f1: Callable[[str], str] f2: Callable[[str], str] | str class Child(Parent): def f1(self, x: str) -> str: return x # ok def f2(self, x: str) -> str: return x # Signature of "f2" incompatible with supertype "Parent" ``` ### After ```python from typing import Callable class Parent: f1: Callable[[str], str] f2: Callable[[str], str] | str class Child(Parent): def f1(self, x: str) -> str: return x # ok def f2(self, x: str) -> str: return x # ok ``` --- mypy/checker.py | 20 ++++++++ test-data/unit/check-classes.test | 78 +++++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e2bb35613066..b26970d996b3 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2146,6 +2146,7 @@ def check_method_override_for_base_with_name( override_class_or_static, context, ) + # Check if this override is covariant. if ( ok and original_node @@ -2161,6 +2162,25 @@ def check_method_override_for_base_with_name( f" override has type {override_str})" ) self.fail(msg, context) + elif isinstance(original_type, UnionType) and any( + is_subtype(typ, orig_typ, ignore_pos_arg_names=True) + for orig_typ in original_type.items + ): + # This method is a subtype of at least one union variant. + if ( + original_node + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + ): + # Covariant override of mutable attribute. + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options + ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) elif is_equivalent(original_type, typ): # Assume invariance for a non-callable attribute here. Note # that this doesn't affect read-only properties which can have diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a6ae963d6c23..5ce80faaee18 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -147,7 +147,6 @@ class Base: class Derived(Base): __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") - [case testOverridePartialAttributeWithMethod] # This was crashing: https://github.com/python/mypy/issues/11686. class Base: @@ -731,19 +730,6 @@ class B(A): pass [builtins fixtures/classmethod.pyi] -[case testOverrideCallableAttributeWithSettableProperty] -from typing import Callable - -class A: - f: Callable[[str], None] - -class B(A): - @property - def f(self) -> Callable[[object], None]: pass - @func.setter - def f(self, x: object) -> None: pass -[builtins fixtures/property.pyi] - [case testOverrideCallableAttributeWithMethodMutableOverride] # flags: --enable-error-code=mutable-override from typing import Callable @@ -763,6 +749,19 @@ class B(A): def f3(x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") [builtins fixtures/classmethod.pyi] +[case testOverrideCallableAttributeWithSettableProperty] +from typing import Callable + +class A: + f: Callable[[str], None] + +class B(A): + @property + def f(self) -> Callable[[object], None]: pass + @func.setter + def f(self, x: object) -> None: pass +[builtins fixtures/property.pyi] + [case testOverrideCallableAttributeWithSettablePropertyMutableOverride] # flags: --enable-error-code=mutable-override from typing import Callable @@ -777,6 +776,57 @@ class B(A): def f(self, x: object) -> None: pass [builtins fixtures/property.pyi] +[case testOverrideCallableUnionAttributeWithMethod] +from typing import Callable, Union + +class A: + f1: Union[Callable[[str], str], str] + f2: Union[Callable[[str], str], str] + f3: Union[Callable[[str], str], str] + f4: Union[Callable[[str], str], str] + +class B(A): + def f1(self, x: str) -> str: + pass + + def f2(self, x: object) -> str: + pass + + @classmethod + def f3(cls, x: str) -> str: + pass + + @staticmethod + def f4(x: str) -> str: + pass +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableUnionAttributeWithMethodMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable, Union + +class A: + f1: Union[Callable[[str], str], str] + f2: Union[Callable[[str], str], str] + f3: Union[Callable[[str], str], str] + f4: Union[Callable[[str], str], str] + +class B(A): + def f1(self, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass + + def f2(self, x: object) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[object], str]") + pass + + @classmethod + def f3(cls, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass + + @staticmethod + def f4(x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass +[builtins fixtures/classmethod.pyi] + -- Constructors -- ------------ From 654ae202dd0229ca8ffe2b1025ebfdb1d5d59d13 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 28 Oct 2024 21:41:05 -0400 Subject: [PATCH 0878/1617] Fix crash when checking slice expression with step 0 in tuple index (#18063) Fixes #18062 I plan to follow up with another PR that adds a better error messages for slice expressions with `step=0` when indexing standard containers that disallow that. --- mypy/types.py | 3 +++ test-data/unit/check-tuples.test | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/mypy/types.py b/mypy/types.py index 0b010ca9d593..b9bee535c05e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2502,6 +2502,9 @@ def slice( if fallback is None: fallback = self.partial_fallback + if stride == 0: + return None + if any(isinstance(t, UnpackType) for t in self.items): total = len(self.items) unpack_index = find_unpack_in_list(self.items) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 972bccf8c24b..d675a35c4aae 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1438,6 +1438,12 @@ reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[Union[builtins.int, bu t[y:] # E: Slice index must be an integer, SupportsIndex or None [builtins fixtures/tuple.pyi] +[case testTupleSliceStepZeroNoCrash] +# This was crashing: https://github.com/python/mypy/issues/18062 +# TODO: emit better error when 0 is used for step +()[::0] # E: Ambiguous slice of a variadic tuple +[builtins fixtures/tuple.pyi] + [case testInferTupleTypeFallbackAgainstInstance] from typing import TypeVar, Generic, Tuple T = TypeVar('T') From d81a9ef6aa58a126f9f24de47123fd4fc99c7dbe Mon Sep 17 00:00:00 2001 From: Terence Honles Date: Tue, 29 Oct 2024 09:16:46 +0100 Subject: [PATCH 0879/1617] Fix enum attributes are not members (#17207) This adds on to the change in https://github.com/python/mypy/pull/17182 and fixes enum attributes being used as members. Fixes: https://github.com/python/mypy/issues/16730 --------- Co-authored-by: Jelle Zijlstra Co-authored-by: Ali Hamdan Co-authored-by: hauntsaninja --- mypy/semanal.py | 7 +++ mypy/semanal_enum.py | 6 +++ mypy/typeops.py | 24 +++------- mypy/types.py | 9 +++- mypyc/test-data/run-classes.test | 4 +- test-data/unit/check-enum.test | 68 ++++++++++++++++++++++++++++- test-data/unit/check-python310.test | 54 +++++++++++++++++++++++ test-data/unit/pythoneval.test | 12 ----- 8 files changed, 150 insertions(+), 34 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 55eb9cbaf426..59e4594353f0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4311,6 +4311,13 @@ def analyze_name_lvalue( lvalue, ) + if explicit_type and has_explicit_value: + self.fail("Enum members must be left unannotated", lvalue) + self.note( + "See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members", + lvalue, + ) + if (not existing or isinstance(existing.node, PlaceholderNode)) and not outer: # Define new variable. var = self.make_name_lvalue_var( diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 30e0bd56c312..0094b719bc96 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -143,6 +143,12 @@ def build_enum_call_typeinfo( var = Var(item) var.info = info var.is_property = True + # When an enum is created by its functional form `Enum(name, values)` + # - if it is a string it is first split by commas/whitespace + # - if it is an iterable of single items each item is assigned a value starting at `start` + # - if it is an iterable of (name, value) then the given values will be used + # either way, each item should be treated as if it has an explicit value. + var.has_explicit_value = True var._fullname = f"{info.fullname}.{item}" info.names[item] = SymbolTableNode(MDEF, var) return info diff --git a/mypy/typeops.py b/mypy/typeops.py index 0699cda53cfa..36e929284bf4 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -31,7 +31,6 @@ ) from mypy.state import state from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, ExtraAttrs, @@ -958,27 +957,16 @@ class Status(Enum): items = [ try_expanding_sum_type_to_union(item, target_fullname) for item in typ.relevant_items() ] - return make_simplified_union(items, contract_literals=False) elif isinstance(typ, Instance) and typ.type.fullname == target_fullname: if typ.type.is_enum: - new_items = [] - for name, symbol in typ.type.names.items(): - if not isinstance(symbol.node, Var): - continue - # Skip these since Enum will remove it - if name in ENUM_REMOVED_PROPS: - continue - # Skip private attributes - if name.startswith("__"): - continue - new_items.append(LiteralType(name, typ)) - return make_simplified_union(new_items, contract_literals=False) + items = [LiteralType(name, typ) for name in typ.get_enum_values()] elif typ.type.fullname == "builtins.bool": - return make_simplified_union( - [LiteralType(True, typ), LiteralType(False, typ)], contract_literals=False - ) + items = [LiteralType(True, typ), LiteralType(False, typ)] + else: + return typ - return typ + # if the expanded union would be `Never` leave the type as is + return typ if not items else make_simplified_union(items, contract_literals=False) def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]: diff --git a/mypy/types.py b/mypy/types.py index b9bee535c05e..9632bd22adb6 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1566,7 +1566,14 @@ def is_singleton_type(self) -> bool: def get_enum_values(self) -> list[str]: """Return the list of values for an Enum.""" return [ - name for name, sym in self.type.names.items() if isinstance(sym.node, mypy.nodes.Var) + name + for name, sym in self.type.names.items() + if ( + isinstance(sym.node, mypy.nodes.Var) + and name not in ENUM_REMOVED_PROPS + and not name.startswith("__") + and sym.node.has_explicit_value + ) ] diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 40287ab963aa..90d0521ffc37 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -270,8 +270,8 @@ from enum import Enum class TestEnum(Enum): _order_ = "a b" - a : int = 1 - b : int = 2 + a = 1 + b = 2 @classmethod def test(cls) -> int: diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 78a114eda764..09e2abb30358 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1764,7 +1764,8 @@ class B(A): x = 1 # E: Cannot override writable attribute "x" with a final one class A1(Enum): - x: int = 1 + x: int = 1 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members class B1(A1): # E: Cannot extend enum with existing members: "A1" pass @@ -1779,6 +1780,7 @@ class A3(Enum): x: Final[int] # type: ignore class B3(A3): x = 1 # E: Cannot override final attribute "x" (previously declared in base class "A3") + [builtins fixtures/bool.pyi] [case testEnumNotFinalWithMethodsAndUninitializedValuesStub] @@ -2185,3 +2187,67 @@ reveal_type(A.y.value) # N: Revealed type is "Literal[2]?" def some_a(a: A): reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" [builtins fixtures/dict.pyi] + + +[case testErrorOnAnnotatedMember] +from enum import Enum + +class Medal(Enum): + gold: int = 1 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members + silver: str = 2 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + bronze = 3 + +[case testEnumMemberWithPlaceholder] +from enum import Enum + +class Pet(Enum): + CAT = ... + DOG: str = ... # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "str") + +[case testEnumValueWithPlaceholderNodeType] +# https://github.com/python/mypy/issues/11971 +from enum import Enum +from typing import Any, Callable, Dict +class Foo(Enum): + Bar: Foo = Callable[[str], None] # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "", variable has type "Foo") + Baz: Any = Callable[[Dict[str, "Missing"]], None] # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Type application targets a non-generic function or class \ + # E: Name "Missing" is not defined + +reveal_type(Foo.Bar) # N: Revealed type is "Literal[__main__.Foo.Bar]?" +reveal_type(Foo.Bar.value) # N: Revealed type is "__main__.Foo" +reveal_type(Foo.Baz) # N: Revealed type is "Literal[__main__.Foo.Baz]?" +reveal_type(Foo.Baz.value) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + + +[case testEnumWithOnlyImplicitMembersUsingAnnotationOnly] +# flags: --warn-unreachable +import enum + + +class E(enum.IntEnum): + A: int + B: int + + +def do_check(value: E) -> None: + reveal_type(value) # N: Revealed type is "__main__.E" + # this is a nonmember check, not an emum member check, and it should not narrow the value + if value is E.A: + return + + reveal_type(value) # N: Revealed type is "__main__.E" + "should be reachable" + +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index e7028a027e25..58b70d7b74d8 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1525,6 +1525,60 @@ def g(m: Medal) -> int: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" return 2 + +[case testMatchLiteralPatternEnumWithTypedAttribute] +from enum import Enum +from typing import NoReturn +def assert_never(x: NoReturn) -> None: ... + +class int: + def __new__(cls, value: int): pass + +class Medal(int, Enum): + prize: str + + def __new__(cls, value: int, prize: str) -> Medal: + enum = int.__new__(cls, value) + enum._value_ = value + enum.prize = prize + return enum + + gold = (1, 'cash prize') + silver = (2, 'sponsorship') + bronze = (3, 'nothing') + +m: Medal + +match m: + case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" + case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" + case _ as unreachable: + assert_never(unreachable) + +[builtins fixtures/tuple.pyi] + +[case testMatchLiteralPatternFunctionalEnum] +from enum import Enum +from typing import NoReturn +def assert_never(x: NoReturn) -> None: ... + +Medal = Enum('Medal', 'gold silver bronze') +m: Medal + +match m: + case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" + case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" + case _ as unreachable: + assert_never(unreachable) + [case testMatchLiteralPatternEnumCustomEquals-skip] from enum import Enum class Medal(Enum): diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 997cdc0d0ff0..4942a5fd5f2f 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1555,18 +1555,6 @@ if isinstance(obj, Awaitable): _testSpecialTypingProtocols.py:6: note: Revealed type is "Tuple[builtins.int]" _testSpecialTypingProtocols.py:8: error: Statement is unreachable -[case testEnumValueWithPlaceholderNodeType] -# https://github.com/python/mypy/issues/11971 -from enum import Enum -from typing import Callable, Dict -class Foo(Enum): - Bar: Foo = Callable[[str], None] - Baz: Foo = Callable[[Dict[str, "Missing"]], None] -[out] -_testEnumValueWithPlaceholderNodeType.py:5: error: Incompatible types in assignment (expression has type "", variable has type "Foo") -_testEnumValueWithPlaceholderNodeType.py:6: error: Incompatible types in assignment (expression has type "", variable has type "Foo") -_testEnumValueWithPlaceholderNodeType.py:6: error: Name "Missing" is not defined - [case testTypeshedRecursiveTypesExample] from typing import List, Union From 35967938d1566d688682caa7b12981eb8d45240f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 29 Oct 2024 22:41:39 +0300 Subject: [PATCH 0880/1617] Lint github actions and dependabot.yml (#18069) Taken from https://github.com/python/cpython/commit/dcda92f8fcfa70ef48935db0dc468734de897d96 I temporarly disabled some checks due to https://github.com/python/mypy/pull/17822 --- .pre-commit-config.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4efed772396e..27e3e65efdf4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,5 +15,19 @@ repos: hooks: - id: ruff args: [--exit-non-zero-on-fix] + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.29.4 + hooks: + - id: check-dependabot + - id: check-github-workflows + - repo: https://github.com/rhysd/actionlint + rev: v1.7.3 + hooks: + - id: actionlint + args: [ + -ignore=property "debug_build" is not defined, + -ignore=property "allow_failure" is not defined, + -ignore=SC2(046|086), + ] ci: autoupdate_schedule: quarterly From 58f76280d816bfdc1ce625c0c0ab3519337bbbe7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:21:37 -0700 Subject: [PATCH 0881/1617] Issue warning for enum with no members in stub (#18068) Follow up to https://github.com/python/mypy/pull/17207 --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++ mypy/checker.py | 30 ++++++++++++++++--------- mypy/checkmember.py | 4 ++-- mypy/nodes.py | 17 +++++++++++++++ mypy/semanal_enum.py | 5 +++-- mypy/test/teststubtest.py | 26 +++++++++++----------- mypy/typeops.py | 24 +++++++++++--------- mypy/types.py | 19 +--------------- mypyc/irbuild/classdef.py | 5 +++-- test-data/unit/check-enum.test | 7 ++++-- 10 files changed, 118 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 801d592945c1..a8208fb48294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,46 @@ ## Next release +### Change to enum membership semantics + +As per the updated [typing specification for enums](https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members), +enum members must be left unannotated. + +```python +class Pet(Enum): + CAT = 1 # Member attribute + DOG = 2 # Member attribute + WOLF: int = 3 # New error: Enum members must be left unannotated + + species: str # Considered a non-member attribute +``` + +In particular, the specification change can result in issues in type stubs (`.pyi` files), since +historically it was common to leave the value absent: + +```python +# In a type stub (.pyi file) + +class Pet(Enum): + # Change in semantics: previously considered members, now non-member attributes + CAT: int + DOG: int + + # Mypy will now issue a warning if it detects this situation in type stubs: + # > Detected enum "Pet" in a type stub with zero members. + # > There is a chance this is due to a recent change in the semantics of enum membership. + # > If so, use `member = value` to mark an enum member, instead of `member: type` + +class Pet(Enum): + # As per the specification, you should now do one of the following: + DOG = 1 # Member attribute with value 1 and known type + WOLF = cast(int, ...) # Member attribute with unknown value but known type + LION = ... # Member attribute with unknown value and unknown type +``` + +Contributed by Terence Honles in PR [17207](https://github.com/python/mypy/pull/17207) and +Shantanu Jain in PR [18068](https://github.com/python/mypy/pull/18068). + ## Mypy 1.13 We’ve just uploaded mypy 1.13 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). diff --git a/mypy/checker.py b/mypy/checker.py index b26970d996b3..a650bdf2a639 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2588,20 +2588,30 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None: def check_enum(self, defn: ClassDef) -> None: assert defn.info.is_enum - if defn.info.fullname not in ENUM_BASES: - for sym in defn.info.names.values(): - if ( - isinstance(sym.node, Var) - and sym.node.has_explicit_value - and sym.node.name == "__members__" - ): - # `__members__` will always be overwritten by `Enum` and is considered - # read-only so we disallow assigning a value to it - self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN, sym.node) + if defn.info.fullname not in ENUM_BASES and "__members__" in defn.info.names: + sym = defn.info.names["__members__"] + if isinstance(sym.node, Var) and sym.node.has_explicit_value: + # `__members__` will always be overwritten by `Enum` and is considered + # read-only so we disallow assigning a value to it + self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN, sym.node) for base in defn.info.mro[1:-1]: # we don't need self and `object` if base.is_enum and base.fullname not in ENUM_BASES: self.check_final_enum(defn, base) + if self.is_stub and self.tree.fullname not in {"enum", "_typeshed"}: + if not defn.info.enum_members: + self.fail( + f'Detected enum "{defn.info.fullname}" in a type stub with zero members. ' + "There is a chance this is due to a recent change in the semantics of " + "enum membership. If so, use `member = value` to mark an enum member, " + "instead of `member: type`", + defn, + ) + self.note( + "See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members", + defn, + ) + self.check_enum_bases(defn) self.check_enum_new(defn) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e6f7570c3df2..9dc8d5475b1a 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -17,6 +17,7 @@ ARG_POS, ARG_STAR, ARG_STAR2, + EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, Context, Decorator, @@ -48,7 +49,6 @@ type_object_type_from_function, ) from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, DeletedType, @@ -1173,7 +1173,7 @@ def analyze_enum_class_attribute_access( itype: Instance, name: str, mx: MemberContext ) -> Type | None: # Skip these since Enum will remove it - if name in ENUM_REMOVED_PROPS: + if name in EXCLUDED_ENUM_ATTRIBUTES: return report_missing_attribute(mx.original_type, itype, name, mx) # Dunders and private names are not Enum members if name.startswith("__") and name.replace("_", "") != "": diff --git a/mypy/nodes.py b/mypy/nodes.py index 7b620bd52008..dabfb463cc95 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2902,6 +2902,10 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: } ) +# Attributes that can optionally be defined in the body of a subclass of +# enum.Enum but are removed from the class __dict__ by EnumMeta. +EXCLUDED_ENUM_ATTRIBUTES: Final = frozenset({"_ignore_", "_order_", "__order__"}) + class TypeInfo(SymbolNode): """The type structure of a single class. @@ -3229,6 +3233,19 @@ def protocol_members(self) -> list[str]: members.add(name) return sorted(members) + @property + def enum_members(self) -> list[str]: + return [ + name + for name, sym in self.names.items() + if ( + isinstance(sym.node, Var) + and name not in EXCLUDED_ENUM_ATTRIBUTES + and not name.startswith("__") + and sym.node.has_explicit_value + ) + ] + def __getitem__(self, name: str) -> SymbolTableNode: n = self.get(name) if n: diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 0094b719bc96..b1e267b4c781 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -10,6 +10,7 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, + EXCLUDED_ENUM_ATTRIBUTES, MDEF, AssignmentStmt, CallExpr, @@ -30,7 +31,7 @@ ) from mypy.options import Options from mypy.semanal_shared import SemanticAnalyzerInterface -from mypy.types import ENUM_REMOVED_PROPS, LiteralType, get_proper_type +from mypy.types import LiteralType, get_proper_type # Note: 'enum.EnumMeta' is deliberately excluded from this list. Classes that directly use # enum.EnumMeta do not necessarily automatically have the 'name' and 'value' attributes. @@ -43,7 +44,7 @@ "value", "_name_", "_value_", - *ENUM_REMOVED_PROPS, + *EXCLUDED_ENUM_ATTRIBUTES, # Also attributes from `object`: "__module__", "__annotations__", diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 4cab62875647..fcbf07b4d371 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1267,9 +1267,9 @@ def test_enum(self) -> Iterator[Case]: yield Case( stub=""" class X(enum.Enum): - a: int - b: str - c: str + a = ... + b = "asdf" + c = "oops" """, runtime=""" class X(enum.Enum): @@ -1282,8 +1282,8 @@ class X(enum.Enum): yield Case( stub=""" class Flags1(enum.Flag): - a: int - b: int + a = ... + b = 2 def foo(x: Flags1 = ...) -> None: ... """, runtime=""" @@ -1297,8 +1297,8 @@ def foo(x=Flags1.a|Flags1.b): pass yield Case( stub=""" class Flags2(enum.Flag): - a: int - b: int + a = ... + b = 2 def bar(x: Flags2 | None = None) -> None: ... """, runtime=""" @@ -1312,8 +1312,8 @@ def bar(x=Flags2.a|Flags2.b): pass yield Case( stub=""" class Flags3(enum.Flag): - a: int - b: int + a = ... + b = 2 def baz(x: Flags3 | None = ...) -> None: ... """, runtime=""" @@ -1346,8 +1346,8 @@ class WeirdEnum(enum.Enum): yield Case( stub=""" class Flags4(enum.Flag): - a: int - b: int + a = 1 + b = 2 def spam(x: Flags4 | None = None) -> None: ... """, runtime=""" @@ -1362,7 +1362,7 @@ def spam(x=Flags4(0)): pass stub=""" from typing_extensions import Final, Literal class BytesEnum(bytes, enum.Enum): - a: bytes + a = b'foo' FOO: Literal[BytesEnum.a] BAR: Final = BytesEnum.a BAZ: BytesEnum @@ -1897,7 +1897,7 @@ def test_good_literal(self) -> Iterator[Case]: import enum class Color(enum.Enum): - RED: int + RED = ... NUM: Literal[1] CHAR: Literal['a'] diff --git a/mypy/typeops.py b/mypy/typeops.py index 36e929284bf4..9e5e347533c6 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -957,16 +957,20 @@ class Status(Enum): items = [ try_expanding_sum_type_to_union(item, target_fullname) for item in typ.relevant_items() ] - elif isinstance(typ, Instance) and typ.type.fullname == target_fullname: - if typ.type.is_enum: - items = [LiteralType(name, typ) for name in typ.get_enum_values()] - elif typ.type.fullname == "builtins.bool": + return make_simplified_union(items, contract_literals=False) + + if isinstance(typ, Instance) and typ.type.fullname == target_fullname: + if typ.type.fullname == "builtins.bool": items = [LiteralType(True, typ), LiteralType(False, typ)] - else: - return typ + return make_simplified_union(items, contract_literals=False) + + if typ.type.is_enum: + items = [LiteralType(name, typ) for name in typ.type.enum_members] + if not items: + return typ + return make_simplified_union(items, contract_literals=False) - # if the expanded union would be `Never` leave the type as is - return typ if not items else make_simplified_union(items, contract_literals=False) + return typ def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]: @@ -990,7 +994,7 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType] if fullname not in sum_types: sum_types[fullname] = ( ( - set(typ.fallback.get_enum_values()) + set(typ.fallback.type.enum_members) if typ.fallback.type.is_enum else {True, False} ), @@ -1023,7 +1027,7 @@ def coerce_to_literal(typ: Type) -> Type: if typ.last_known_value: return typ.last_known_value elif typ.type.is_enum: - enum_values = typ.get_enum_values() + enum_values = typ.type.enum_members if len(enum_values) == 1: return LiteralType(value=enum_values[0], fallback=typ) return original_type diff --git a/mypy/types.py b/mypy/types.py index 9632bd22adb6..cc9c65299ee8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -150,10 +150,6 @@ OVERLOAD_NAMES: Final = ("typing.overload", "typing_extensions.overload") -# Attributes that can optionally be defined in the body of a subclass of -# enum.Enum but are removed from the class __dict__ by EnumMeta. -ENUM_REMOVED_PROPS: Final = ("_ignore_", "_order_", "__order__") - NEVER_NAMES: Final = ( "typing.NoReturn", "typing_extensions.NoReturn", @@ -1559,23 +1555,10 @@ def is_singleton_type(self) -> bool: # Also make this return True if the type corresponds to NotImplemented? return ( self.type.is_enum - and len(self.get_enum_values()) == 1 + and len(self.type.enum_members) == 1 or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"} ) - def get_enum_values(self) -> list[str]: - """Return the list of values for an Enum.""" - return [ - name - for name, sym in self.type.names.items() - if ( - isinstance(sym.node, mypy.nodes.Var) - and name not in ENUM_REMOVED_PROPS - and not name.startswith("__") - and sym.node.has_explicit_value - ) - ] - class FunctionLike(ProperType): """Abstract base class for function types.""" diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 6ff6308b81d8..590bd12ce7b3 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,6 +7,7 @@ from typing import Callable, Final from mypy.nodes import ( + EXCLUDED_ENUM_ATTRIBUTES, TYPE_VAR_TUPLE_KIND, AssignmentStmt, CallExpr, @@ -27,7 +28,7 @@ TypeParam, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, get_proper_type +from mypy.types import Instance, UnboundType, get_proper_type from mypyc.common import PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature @@ -683,7 +684,7 @@ def add_non_ext_class_attr( cdef.info.bases and cdef.info.bases[0].type.fullname == "enum.Enum" # Skip these since Enum will remove it - and lvalue.name not in ENUM_REMOVED_PROPS + and lvalue.name not in EXCLUDED_ENUM_ATTRIBUTES ): # Enum values are always boxed, so use object_rprimitive. attr_to_cache.append((lvalue, object_rprimitive)) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 09e2abb30358..533da6652f8f 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1788,14 +1788,17 @@ import lib [file lib.pyi] from enum import Enum -class A(Enum): +class A(Enum): # E: Detected enum "lib.A" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members x: int class B(A): # E: Cannot extend enum with existing members: "A" x = 1 # E: Cannot override writable attribute "x" with a final one class C(Enum): x = 1 -class D(C): # E: Cannot extend enum with existing members: "C" +class D(C): # E: Cannot extend enum with existing members: "C" \ + # E: Detected enum "lib.D" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members x: int # E: Cannot assign to final name "x" [builtins fixtures/bool.pyi] From 5a296f055cf138e5080014cfd36eae77a1d9ed37 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:09:23 -0700 Subject: [PATCH 0882/1617] Clean up new_semantic_analyzer config (#18071) This has been very dead for years, no plugin I can tell is using it --- mypy/config_parser.py | 7 +------ mypy/options.py | 7 +------ mypy/plugin.py | 3 --- mypyc/irbuild/builder.py | 2 +- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a6bf021000c1..9fa99333a42a 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -472,12 +472,7 @@ def parse_section( ) continue else: - dv = None - # We have to keep new_semantic_analyzer in Options - # for plugin compatibility but it is not a valid option anymore. - assert hasattr(template, "new_semantic_analyzer") - if key != "new_semantic_analyzer": - dv = getattr(template, key, None) + dv = getattr(template, key, None) if dv is None: if key.endswith("_report"): report_type = key[:-7].replace("_", "-") diff --git a/mypy/options.py b/mypy/options.py index d315d297e023..367267d1053a 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -400,17 +400,12 @@ def use_or_syntax(self) -> bool: def use_star_unpack(self) -> bool: return self.python_version >= (3, 11) - # To avoid breaking plugin compatibility, keep providing new_semantic_analyzer - @property - def new_semantic_analyzer(self) -> bool: - return True - def snapshot(self) -> dict[str, object]: """Produce a comparable snapshot of this Option""" # Under mypyc, we don't have a __dict__, so we need to do worse things. d = dict(getattr(self, "__dict__", ())) for k in get_class_descriptors(Options): - if hasattr(self, k) and k != "new_semantic_analyzer": + if hasattr(self, k): d[k] = getattr(self, k) # Remove private attributes from snapshot d = {k: v for k, v in d.items() if not k.startswith("_")} diff --git a/mypy/plugin.py b/mypy/plugin.py index a4dc0052ec79..fcbbc32f6237 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -114,9 +114,6 @@ class C: pass Note that a forward reference in a function signature won't trigger another pass, since all functions are processed only after the top level has been fully analyzed. - -You can use `api.options.new_semantic_analyzer` to check whether the new -semantic analyzer is enabled (it's always true in mypy 0.730 and later). """ from __future__ import annotations diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 823f1581ba2e..4a5647b9ffdf 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -594,7 +594,7 @@ def get_assignment_target( if isinstance(symbol, Decorator): symbol = symbol.func if symbol is None: - # New semantic analyzer doesn't create ad-hoc Vars for special forms. + # Semantic analyzer doesn't create ad-hoc Vars for special forms. assert lvalue.is_special_form symbol = Var(lvalue.name) if not for_read and isinstance(symbol, Var) and symbol.is_cls: From b59878ec74bc4dd4dc3eafc1d3cdb2668da5f60a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 31 Oct 2024 00:44:52 +0300 Subject: [PATCH 0883/1617] Use `strict = True` for `mypy_bootstrap.ini` (#18076) We almost had all strict flags set anyway, so many will just work. If not, it would be easier to turn something off. --- mypy_bootstrap.ini | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/mypy_bootstrap.ini b/mypy_bootstrap.ini index c680990fbd9e..6e82f23b0530 100644 --- a/mypy_bootstrap.ini +++ b/mypy_bootstrap.ini @@ -1,16 +1,6 @@ [mypy] -disallow_untyped_calls = True -disallow_untyped_defs = True -disallow_incomplete_defs = True -check_untyped_defs = True -disallow_subclassing_any = True -warn_no_return = True -strict_optional = True -no_implicit_optional = True -disallow_any_generics = True -disallow_any_unimported = True -warn_redundant_casts = True -warn_unused_configs = True +strict = True +warn_unused_ignores = False show_traceback = True always_true = MYPYC From 46595eef151a12a0aafabe7e8c96763e9de04f98 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 1 Nov 2024 01:00:13 -0700 Subject: [PATCH 0884/1617] Check that errno is not None (#18084) Unblocks typeshed sync --- mypy/build.py | 1 + mypy/modulefinder.py | 1 + 2 files changed, 2 insertions(+) diff --git a/mypy/build.py b/mypy/build.py index ff9b48d2d7b4..4745610d7920 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2140,6 +2140,7 @@ def parse_file(self, *, temporary: bool = False) -> None: # other systems, but os.strerror(ioerr.errno) does not, so we use that. # (We want the error messages to be platform-independent so that the # tests have predictable output.) + assert ioerr.errno is not None raise CompileError( [ "mypy: can't read file '{}': {}".format( diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 9b15f2aff90e..bc11c1304776 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -777,6 +777,7 @@ def get_search_dirs(python_executable: str | None) -> tuple[list[str], list[str] print(err.stdout) raise except OSError as err: + assert err.errno is not None reason = os.strerror(err.errno) raise CompileError( [f"mypy: Invalid python executable '{python_executable}': {reason}"] From 05a9e79068a5830e57264390c9f6bca859e92053 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 02:04:20 -0700 Subject: [PATCH 0885/1617] Sync typeshed (#18082) Sync typeshed Source commit: https://github.com/python/typeshed/commit/d262beb07502cda412db2179fb406d45d1a9486f --- mypy/typeshed/stdlib/VERSIONS | 4 + mypy/typeshed/stdlib/_frozen_importlib.pyi | 112 ++++++++++ .../stdlib/_frozen_importlib_external.pyi | 178 ++++++++++++++++ mypy/typeshed/stdlib/_ssl.pyi | 2 +- mypy/typeshed/stdlib/builtins.pyi | 2 +- mypy/typeshed/stdlib/importlib/__init__.pyi | 11 +- mypy/typeshed/stdlib/importlib/_bootstrap.pyi | 2 + .../stdlib/importlib/_bootstrap_external.pyi | 2 + mypy/typeshed/stdlib/importlib/abc.pyi | 5 +- mypy/typeshed/stdlib/importlib/machinery.pyi | 191 ++---------------- mypy/typeshed/stdlib/importlib/util.pyi | 29 +-- mypy/typeshed/stdlib/types.pyi | 7 + mypy/typeshed/stdlib/zipimport.pyi | 7 +- 13 files changed, 342 insertions(+), 210 deletions(-) create mode 100644 mypy/typeshed/stdlib/_frozen_importlib.pyi create mode 100644 mypy/typeshed/stdlib/_frozen_importlib_external.pyi create mode 100644 mypy/typeshed/stdlib/importlib/_bootstrap.pyi create mode 100644 mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 1d04b47c77bc..7c9f3cfda837 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -34,6 +34,8 @@ _curses: 3.0- _decimal: 3.3- _dummy_thread: 3.0-3.8 _dummy_threading: 3.0-3.8 +_frozen_importlib: 3.0- +_frozen_importlib_external: 3.5- _heapq: 3.0- _imp: 3.0- _interpchannels: 3.13- @@ -161,6 +163,8 @@ imghdr: 3.0-3.12 imp: 3.0-3.11 importlib: 3.0- importlib._abc: 3.10- +importlib._bootstrap: 3.0- +importlib._bootstrap_external: 3.5- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- importlib.metadata.diagnose: 3.13- diff --git a/mypy/typeshed/stdlib/_frozen_importlib.pyi b/mypy/typeshed/stdlib/_frozen_importlib.pyi new file mode 100644 index 000000000000..b6d7a1842048 --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib.pyi @@ -0,0 +1,112 @@ +import importlib.abc +import importlib.machinery +import sys +import types +from _typeshed.importlib import LoaderProtocol +from collections.abc import Mapping, Sequence +from types import ModuleType +from typing import Any + +# Signature of `builtins.__import__` should be kept identical to `importlib.__import__` +def __import__( + name: str, + globals: Mapping[str, object] | None = None, + locals: Mapping[str, object] | None = None, + fromlist: Sequence[str] = (), + level: int = 0, +) -> ModuleType: ... +def spec_from_loader( + name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None +) -> importlib.machinery.ModuleSpec | None: ... +def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ... +def _init_module_attrs( + spec: importlib.machinery.ModuleSpec, module: types.ModuleType, *, override: bool = False +) -> types.ModuleType: ... + +class ModuleSpec: + def __init__( + self, + name: str, + loader: importlib.abc.Loader | None, + *, + origin: str | None = None, + loader_state: Any = None, + is_package: bool | None = None, + ) -> None: ... + name: str + loader: importlib.abc.Loader | None + origin: str | None + submodule_search_locations: list[str] | None + loader_state: Any + cached: str | None + @property + def parent(self) -> str | None: ... + has_location: bool + def __eq__(self, other: object) -> bool: ... + +class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): + # MetaPathFinder + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + # InspectLoader + @classmethod + def is_package(cls, fullname: str) -> bool: ... + @classmethod + def load_module(cls, fullname: str) -> types.ModuleType: ... + @classmethod + def get_code(cls, fullname: str) -> None: ... + @classmethod + def get_source(cls, fullname: str) -> None: ... + # Loader + if sys.version_info < (3, 12): + @staticmethod + def module_repr(module: types.ModuleType) -> str: ... + if sys.version_info >= (3, 10): + @staticmethod + def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... + @staticmethod + def exec_module(module: types.ModuleType) -> None: ... + else: + @classmethod + def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... + @classmethod + def exec_module(cls, module: types.ModuleType) -> None: ... + +class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): + # MetaPathFinder + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + # InspectLoader + @classmethod + def is_package(cls, fullname: str) -> bool: ... + @classmethod + def load_module(cls, fullname: str) -> types.ModuleType: ... + @classmethod + def get_code(cls, fullname: str) -> None: ... + @classmethod + def get_source(cls, fullname: str) -> None: ... + # Loader + if sys.version_info < (3, 12): + @staticmethod + def module_repr(m: types.ModuleType) -> str: ... + if sys.version_info >= (3, 10): + @staticmethod + def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... + else: + @classmethod + def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... + + @staticmethod + def exec_module(module: types.ModuleType) -> None: ... diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi new file mode 100644 index 000000000000..933d7466248e --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi @@ -0,0 +1,178 @@ +import _ast +import _io +import importlib.abc +import importlib.machinery +import sys +import types +from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath +from _typeshed.importlib import LoaderProtocol +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableSequence, Sequence +from importlib.machinery import ModuleSpec +from importlib.metadata import DistributionFinder, PathDistribution +from typing import Any, Literal +from typing_extensions import Self, deprecated + +if sys.version_info >= (3, 10): + import importlib.readers + +if sys.platform == "win32": + path_separators: Literal["\\/"] + path_sep: Literal["\\"] + path_sep_tuple: tuple[Literal["\\"], Literal["/"]] +else: + path_separators: Literal["/"] + path_sep: Literal["/"] + path_sep_tuple: tuple[Literal["/"]] + +MAGIC_NUMBER: bytes + +def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... +def source_from_cache(path: str) -> str: ... +def decode_source(source_bytes: ReadableBuffer) -> str: ... +def spec_from_file_location( + name: str, + location: StrOrBytesPath | None = None, + *, + loader: LoaderProtocol | None = None, + submodule_search_locations: list[str] | None = ..., +) -> importlib.machinery.ModuleSpec | None: ... + +class WindowsRegistryFinder(importlib.abc.MetaPathFinder): + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + +class PathFinder(importlib.abc.MetaPathFinder): + if sys.version_info >= (3, 10): + @staticmethod + def invalidate_caches() -> None: ... + else: + @classmethod + def invalidate_caches(cls) -> None: ... + if sys.version_info >= (3, 10): + @staticmethod + def find_distributions(context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... + else: + @classmethod + def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + +SOURCE_SUFFIXES: list[str] +DEBUG_BYTECODE_SUFFIXES: list[str] +OPTIMIZED_BYTECODE_SUFFIXES: list[str] +BYTECODE_SUFFIXES: list[str] +EXTENSION_SUFFIXES: list[str] + +class FileFinder(importlib.abc.PathEntryFinder): + path: str + def __init__(self, path: str, *loader_details: tuple[type[importlib.abc.Loader], list[str]]) -> None: ... + @classmethod + def path_hook( + cls, *loader_details: tuple[type[importlib.abc.Loader], list[str]] + ) -> Callable[[str], importlib.abc.PathEntryFinder]: ... + +class _LoaderBasics: + def is_package(self, fullname: str) -> bool: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + def load_module(self, fullname: str) -> types.ModuleType: ... + +class SourceLoader(_LoaderBasics): + def path_mtime(self, path: str) -> float: ... + def set_data(self, path: str, data: bytes) -> None: ... + def get_source(self, fullname: str) -> str | None: ... + def path_stats(self, path: str) -> Mapping[str, Any]: ... + def source_to_code( + self, data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive, path: ReadableBuffer | StrPath + ) -> types.CodeType: ... + def get_code(self, fullname: str) -> types.CodeType | None: ... + +class FileLoader: + name: str + path: str + def __init__(self, fullname: str, path: str) -> None: ... + def get_data(self, path: str) -> bytes: ... + def get_filename(self, name: str | None = None) -> str: ... + def load_module(self, name: str | None = None) -> types.ModuleType: ... + if sys.version_info >= (3, 10): + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.FileReader: ... + else: + def get_resource_reader(self, module: types.ModuleType) -> Self | None: ... + def open_resource(self, resource: str) -> _io.FileIO: ... + def resource_path(self, resource: str) -> str: ... + def is_resource(self, name: str) -> bool: ... + def contents(self) -> Iterator[str]: ... + +class SourceFileLoader(importlib.abc.FileLoader, FileLoader, importlib.abc.SourceLoader, SourceLoader): # type: ignore[misc] # incompatible method arguments in base classes + def set_data(self, path: str, data: ReadableBuffer, *, _mode: int = 0o666) -> None: ... + def path_stats(self, path: str) -> Mapping[str, Any]: ... + +class SourcelessFileLoader(importlib.abc.FileLoader, FileLoader, _LoaderBasics): + def get_code(self, fullname: str) -> types.CodeType | None: ... + def get_source(self, fullname: str) -> None: ... + +class ExtensionFileLoader(FileLoader, _LoaderBasics, importlib.abc.ExecutionLoader): + def __init__(self, name: str, path: str) -> None: ... + def get_filename(self, name: str | None = None) -> str: ... + def get_source(self, fullname: str) -> None: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType: ... + def exec_module(self, module: types.ModuleType) -> None: ... + def get_code(self, fullname: str) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + +if sys.version_info >= (3, 11): + class NamespaceLoader(importlib.abc.InspectLoader): + def __init__( + self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] + ) -> None: ... + def is_package(self, fullname: str) -> Literal[True]: ... + def get_source(self, fullname: str) -> Literal[""]: ... + def get_code(self, fullname: str) -> types.CodeType: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + @deprecated("load_module() is deprecated; use exec_module() instead") + def load_module(self, fullname: str) -> types.ModuleType: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + if sys.version_info < (3, 12): + @staticmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(module: types.ModuleType) -> str: ... + + _NamespaceLoader = NamespaceLoader +else: + class _NamespaceLoader: + def __init__( + self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] + ) -> None: ... + def is_package(self, fullname: str) -> Literal[True]: ... + def get_source(self, fullname: str) -> Literal[""]: ... + def get_code(self, fullname: str) -> types.CodeType: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + @deprecated("load_module() is deprecated; use exec_module() instead") + def load_module(self, fullname: str) -> types.ModuleType: ... + if sys.version_info >= (3, 10): + @staticmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(module: types.ModuleType) -> str: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + else: + @classmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(cls, module: types.ModuleType) -> str: ... + +if sys.version_info >= (3, 13): + class AppleFrameworkLoader(ExtensionFileLoader, importlib.abc.ExecutionLoader): ... diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi index 3e88874143df..938135eb1192 100644 --- a/mypy/typeshed/stdlib/_ssl.pyi +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -105,7 +105,7 @@ class _SSLContext: if sys.version_info >= (3, 13): def set_psk_client_callback(self, callback: Callable[[str | None], tuple[str | None, bytes]] | None) -> None: ... def set_psk_server_callback( - self, callback: Callable[[str | None], tuple[str | None, bytes]] | None, identity_hint: str | None = None + self, callback: Callable[[str | None], bytes] | None, identity_hint: str | None = None ) -> None: ... @final diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 1db7f0edc241..371de9821339 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1818,7 +1818,7 @@ class StopIteration(Exception): value: Any class OSError(Exception): - errno: int + errno: int | None strerror: str # filename, filename2 are actually str | bytes | None filename: Any diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi index 8506efc01171..cab81512e92f 100644 --- a/mypy/typeshed/stdlib/importlib/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/__init__.pyi @@ -1,19 +1,10 @@ import sys -from collections.abc import Mapping, Sequence +from importlib._bootstrap import __import__ as __import__ from importlib.abc import Loader from types import ModuleType __all__ = ["__import__", "import_module", "invalidate_caches", "reload"] -# Signature of `builtins.__import__` should be kept identical to `importlib.__import__` -def __import__( - name: str, - globals: Mapping[str, object] | None = None, - locals: Mapping[str, object] | None = None, - fromlist: Sequence[str] = (), - level: int = 0, -) -> ModuleType: ... - # `importlib.import_module` return type should be kept the same as `builtins.__import__` def import_module(name: str, package: str | None = None) -> ModuleType: ... diff --git a/mypy/typeshed/stdlib/importlib/_bootstrap.pyi b/mypy/typeshed/stdlib/importlib/_bootstrap.pyi new file mode 100644 index 000000000000..02427ff42062 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/_bootstrap.pyi @@ -0,0 +1,2 @@ +from _frozen_importlib import * +from _frozen_importlib import __import__ as __import__, _init_module_attrs as _init_module_attrs diff --git a/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi b/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi new file mode 100644 index 000000000000..6210ce7083af --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi @@ -0,0 +1,2 @@ +from _frozen_importlib_external import * +from _frozen_importlib_external import _NamespaceLoader as _NamespaceLoader diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 4a0a70d0930d..588377d7d871 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -4,6 +4,7 @@ import types from _typeshed import ReadableBuffer, StrPath from abc import ABCMeta, abstractmethod from collections.abc import Iterator, Mapping, Sequence +from importlib import _bootstrap_external from importlib.machinery import ModuleSpec from io import BufferedReader from typing import IO, Any, Literal, Protocol, overload, runtime_checkable @@ -56,7 +57,7 @@ class ExecutionLoader(InspectLoader): @abstractmethod def get_filename(self, fullname: str) -> str: ... -class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): +class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta): # type: ignore[misc] # incompatible definitions of source_to_code in the base classes def path_mtime(self, path: str) -> float: ... def set_data(self, path: str, data: bytes) -> None: ... def get_source(self, fullname: str) -> str | None: ... @@ -101,7 +102,7 @@ else: # Not defined on the actual class, but expected to exist. def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... -class FileLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): +class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta): name: str path: str def __init__(self, fullname: str, path: str) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index 586c2b80ab7b..bb1a6f93d0e0 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -1,179 +1,20 @@ -import importlib.abc import sys -import types -from _typeshed import ReadableBuffer -from collections.abc import Callable, Iterable, MutableSequence, Sequence -from importlib.metadata import DistributionFinder, PathDistribution -from typing import Any, Literal -from typing_extensions import deprecated - -class ModuleSpec: - def __init__( - self, - name: str, - loader: importlib.abc.Loader | None, - *, - origin: str | None = None, - loader_state: Any = None, - is_package: bool | None = None, - ) -> None: ... - name: str - loader: importlib.abc.Loader | None - origin: str | None - submodule_search_locations: list[str] | None - loader_state: Any - cached: str | None - @property - def parent(self) -> str | None: ... - has_location: bool - def __eq__(self, other: object) -> bool: ... - -class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): - # MetaPathFinder - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - # InspectLoader - @classmethod - def is_package(cls, fullname: str) -> bool: ... - @classmethod - def load_module(cls, fullname: str) -> types.ModuleType: ... - @classmethod - def get_code(cls, fullname: str) -> None: ... - @classmethod - def get_source(cls, fullname: str) -> None: ... - # Loader - if sys.version_info < (3, 12): - @staticmethod - def module_repr(module: types.ModuleType) -> str: ... - if sys.version_info >= (3, 10): - @staticmethod - def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... - @staticmethod - def exec_module(module: types.ModuleType) -> None: ... - else: - @classmethod - def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... - @classmethod - def exec_module(cls, module: types.ModuleType) -> None: ... - -class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): - # MetaPathFinder - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - # InspectLoader - @classmethod - def is_package(cls, fullname: str) -> bool: ... - @classmethod - def load_module(cls, fullname: str) -> types.ModuleType: ... - @classmethod - def get_code(cls, fullname: str) -> None: ... - @classmethod - def get_source(cls, fullname: str) -> None: ... - # Loader - if sys.version_info < (3, 12): - @staticmethod - def module_repr(m: types.ModuleType) -> str: ... - if sys.version_info >= (3, 10): - @staticmethod - def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... - else: - @classmethod - def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... - - @staticmethod - def exec_module(module: types.ModuleType) -> None: ... - -class WindowsRegistryFinder(importlib.abc.MetaPathFinder): - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - -class PathFinder: - if sys.version_info >= (3, 10): - @staticmethod - def invalidate_caches() -> None: ... - else: - @classmethod - def invalidate_caches(cls) -> None: ... - if sys.version_info >= (3, 10): - @staticmethod - def find_distributions(context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - else: - @classmethod - def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - -SOURCE_SUFFIXES: list[str] -DEBUG_BYTECODE_SUFFIXES: list[str] -OPTIMIZED_BYTECODE_SUFFIXES: list[str] -BYTECODE_SUFFIXES: list[str] -EXTENSION_SUFFIXES: list[str] - -def all_suffixes() -> list[str]: ... - -class FileFinder(importlib.abc.PathEntryFinder): - path: str - def __init__(self, path: str, *loader_details: tuple[type[importlib.abc.Loader], list[str]]) -> None: ... - @classmethod - def path_hook( - cls, *loader_details: tuple[type[importlib.abc.Loader], list[str]] - ) -> Callable[[str], importlib.abc.PathEntryFinder]: ... - -class SourceFileLoader(importlib.abc.FileLoader, importlib.abc.SourceLoader): - def set_data(self, path: str, data: ReadableBuffer, *, _mode: int = 0o666) -> None: ... - -class SourcelessFileLoader(importlib.abc.FileLoader, importlib.abc.SourceLoader): ... - -class ExtensionFileLoader(importlib.abc.ExecutionLoader): - def __init__(self, name: str, path: str) -> None: ... - def get_filename(self, name: str | None = None) -> str: ... - def get_source(self, fullname: str) -> None: ... - def create_module(self, spec: ModuleSpec) -> types.ModuleType: ... - def exec_module(self, module: types.ModuleType) -> None: ... - def get_code(self, fullname: str) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... +from importlib._bootstrap import BuiltinImporter as BuiltinImporter, FrozenImporter as FrozenImporter, ModuleSpec as ModuleSpec +from importlib._bootstrap_external import ( + BYTECODE_SUFFIXES as BYTECODE_SUFFIXES, + DEBUG_BYTECODE_SUFFIXES as DEBUG_BYTECODE_SUFFIXES, + EXTENSION_SUFFIXES as EXTENSION_SUFFIXES, + OPTIMIZED_BYTECODE_SUFFIXES as OPTIMIZED_BYTECODE_SUFFIXES, + SOURCE_SUFFIXES as SOURCE_SUFFIXES, + ExtensionFileLoader as ExtensionFileLoader, + FileFinder as FileFinder, + PathFinder as PathFinder, + SourceFileLoader as SourceFileLoader, + SourcelessFileLoader as SourcelessFileLoader, + WindowsRegistryFinder as WindowsRegistryFinder, +) if sys.version_info >= (3, 11): - import importlib.readers + from importlib._bootstrap_external import NamespaceLoader as NamespaceLoader - class NamespaceLoader(importlib.abc.InspectLoader): - def __init__( - self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] - ) -> None: ... - def is_package(self, fullname: str) -> Literal[True]: ... - def get_source(self, fullname: str) -> Literal[""]: ... - def get_code(self, fullname: str) -> types.CodeType: ... - def create_module(self, spec: ModuleSpec) -> None: ... - def exec_module(self, module: types.ModuleType) -> None: ... - @deprecated("load_module() is deprecated; use exec_module() instead") - def load_module(self, fullname: str) -> types.ModuleType: ... - def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... - if sys.version_info < (3, 12): - @staticmethod - @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") - def module_repr(module: types.ModuleType) -> str: ... +def all_suffixes() -> list[str]: ... diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index 2492c76d5c6c..cc1c98ae4d0e 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -2,10 +2,16 @@ import importlib.abc import importlib.machinery import sys import types -from _typeshed import ReadableBuffer, StrOrBytesPath -from _typeshed.importlib import LoaderProtocol +from _typeshed import ReadableBuffer from collections.abc import Callable -from typing import Any +from importlib._bootstrap import module_from_spec as module_from_spec, spec_from_loader as spec_from_loader +from importlib._bootstrap_external import ( + MAGIC_NUMBER as MAGIC_NUMBER, + cache_from_source as cache_from_source, + decode_source as decode_source, + source_from_cache as source_from_cache, + spec_from_file_location as spec_from_file_location, +) from typing_extensions import ParamSpec _P = ParamSpec("_P") @@ -16,24 +22,7 @@ if sys.version_info < (3, 12): def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... def resolve_name(name: str, package: str | None) -> str: ... - -MAGIC_NUMBER: bytes - -def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... -def source_from_cache(path: str) -> str: ... -def decode_source(source_bytes: ReadableBuffer) -> str: ... def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None: ... -def spec_from_loader( - name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None -) -> importlib.machinery.ModuleSpec | None: ... -def spec_from_file_location( - name: str, - location: StrOrBytesPath | None = None, - *, - loader: LoaderProtocol | None = None, - submodule_search_locations: list[str] | None = ..., -) -> importlib.machinery.ModuleSpec | None: ... -def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ... class LazyLoader(importlib.abc.Loader): def __init__(self, loader: importlib.abc.Loader) -> None: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index ebea6f22fb12..b513bd77468a 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -337,6 +337,13 @@ class ModuleType: __package__: str | None __path__: MutableSequence[str] __spec__: ModuleSpec | None + # N.B. Although this is the same type as `builtins.object.__doc__`, + # it is deliberately redeclared here. Most symbols declared in the namespace + # of `types.ModuleType` are available as "implicit globals" within a module's + # namespace, but this is not true for symbols declared in the namespace of `builtins.object`. + # Redeclaring `__doc__` here helps some type checkers understand that `__doc__` is available + # as an implicit global in all modules, similar to `__name__`, `__file__`, `__spec__`, etc. + __doc__: str | None def __init__(self, name: str, doc: str | None = ...) -> None: ... # __getattr__ doesn't exist at runtime, # but having it here in typeshed makes dynamic imports diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index 71d4f4478c43..3e94c681b7a2 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -5,11 +5,16 @@ from importlib.machinery import ModuleSpec from types import CodeType, ModuleType from typing_extensions import deprecated +if sys.version_info >= (3, 10): + from _frozen_importlib_external import _LoaderBasics +else: + _LoaderBasics = object + __all__ = ["ZipImportError", "zipimporter"] class ZipImportError(ImportError): ... -class zipimporter: +class zipimporter(_LoaderBasics): archive: str prefix: str if sys.version_info >= (3, 11): From 1f200dde451493a070b9464011998dd837df4bc2 Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Sat, 2 Nov 2024 07:47:48 -0700 Subject: [PATCH 0886/1617] stubgen: fix FunctionContext.fullname for nested classes (#17963) This fixes an issue with the computation of `FunctionContext.fullname` for nested classes. For a module named `spangle`, with the following classes: ```python class Foo: class Bar: pass ``` The previous output for the class `Bar` was `"spangle.Bar"` and with this fix it is now `"spangle.Foo.Bar"`. --- mypy/stubgen.py | 16 +++++++++++----- mypy/stubgenc.py | 10 +++++++--- mypy/stubutil.py | 16 ++++++++++++++-- mypy/test/teststubgen.py | 11 +++++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 4626d8b2e11d..fdad5c2ddd89 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -472,7 +472,7 @@ def __init__( self._vars: list[list[str]] = [[]] # What was generated previously in the stub file. self._state = EMPTY - self._current_class: ClassDef | None = None + self._class_stack: list[ClassDef] = [] # Was the tree semantically analysed before? self.analyzed = analyzed # Short names of methods defined in the body of the current class @@ -480,6 +480,10 @@ def __init__( self.processing_enum = False self.processing_dataclass = False + @property + def _current_class(self) -> ClassDef | None: + return self._class_stack[-1] if self._class_stack else None + def visit_mypy_file(self, o: MypyFile) -> None: self.module_name = o.fullname # Current module being processed self.path = o.path @@ -651,12 +655,14 @@ def visit_func_def(self, o: FuncDef) -> None: if init_code: self.add(init_code) - if self._current_class is not None: + if self._class_stack: if len(o.arguments): self_var = o.arguments[0].variable.name else: self_var = "self" - class_info = ClassInfo(self._current_class.name, self_var) + class_info: ClassInfo | None = None + for class_def in self._class_stack: + class_info = ClassInfo(class_def.name, self_var, parent=class_info) else: class_info = None @@ -746,7 +752,7 @@ def get_fullname(self, expr: Expression) -> str: return self.resolve_name(name) def visit_class_def(self, o: ClassDef) -> None: - self._current_class = o + self._class_stack.append(o) self.method_names = find_method_names(o.defs.body) sep: int | None = None if self.is_top_level() and self._state != EMPTY: @@ -792,8 +798,8 @@ def visit_class_def(self, o: ClassDef) -> None: self._state = CLASS self.method_names = set() self.processing_dataclass = False + self._class_stack.pop(-1) self.processing_enum = False - self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: """Get list of base classes for a class.""" diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 7ab500b4fe12..1cd709b9d603 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -787,7 +787,9 @@ def get_base_types(self, obj: type) -> list[str]: bases.append(base) return [self.strip_or_import(self.get_type_fullname(base)) for base in bases] - def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> None: + def generate_class_stub( + self, class_name: str, cls: type, output: list[str], parent_class: ClassInfo | None = None + ) -> None: """Generate stub for a single class using runtime introspection. The result lines will be appended to 'output'. If necessary, any @@ -808,7 +810,9 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> self.record_name(class_name) self.indent() - class_info = ClassInfo(class_name, "", getattr(cls, "__doc__", None), cls) + class_info = ClassInfo( + class_name, "", getattr(cls, "__doc__", None), cls, parent=parent_class + ) for attr, value in items: # use unevaluated descriptors when dealing with property inspection @@ -843,7 +847,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> class_info, ) elif inspect.isclass(value) and self.is_defined_in_module(value): - self.generate_class_stub(attr, value, types) + self.generate_class_stub(attr, value, types, parent_class=class_info) else: attrs.append((attr, value)) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index e824c3f1a8e2..3f917ca7665d 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -307,12 +307,18 @@ def args_str(self, args: Iterable[Type]) -> str: class ClassInfo: def __init__( - self, name: str, self_var: str, docstring: str | None = None, cls: type | None = None + self, + name: str, + self_var: str, + docstring: str | None = None, + cls: type | None = None, + parent: ClassInfo | None = None, ) -> None: self.name = name self.self_var = self_var self.docstring = docstring self.cls = cls + self.parent = parent class FunctionContext: @@ -335,7 +341,13 @@ def __init__( def fullname(self) -> str: if self._fullname is None: if self.class_info: - self._fullname = f"{self.module_name}.{self.class_info.name}.{self.name}" + parents = [] + class_info: ClassInfo | None = self.class_info + while class_info is not None: + parents.append(class_info.name) + class_info = class_info.parent + namespace = ".".join(reversed(parents)) + self._fullname = f"{self.module_name}.{namespace}.{self.name}" else: self._fullname = f"{self.module_name}.{self.name}" return self._fullname diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e65a16c8f395..46e805d9ca82 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -38,6 +38,7 @@ from mypy.stubgenc import InspectionStubGenerator, infer_c_method_args from mypy.stubutil import ( ClassInfo, + FunctionContext, common_dir_prefix, infer_method_ret_type, remove_misplaced_type_comments, @@ -612,6 +613,16 @@ def test_common_dir_prefix_win(self) -> None: assert common_dir_prefix([r"foo\bar/x.pyi"]) == r"foo\bar" assert common_dir_prefix([r"foo/bar/x.pyi"]) == r"foo\bar" + def test_function_context_nested_classes(self) -> None: + ctx = FunctionContext( + module_name="spangle", + name="foo", + class_info=ClassInfo( + name="Nested", self_var="self", parent=ClassInfo(name="Parent", self_var="self") + ), + ) + assert ctx.fullname == "spangle.Parent.Nested.foo" + class StubgenHelpersSuite(unittest.TestCase): def test_is_blacklisted_path(self) -> None: From ee3122f22167b5c1260d7bfe9b90ba08ce44cdd2 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:53:34 -0800 Subject: [PATCH 0887/1617] Fix subtyping between Instance and Overloaded (#18102) Fixes #18101 --- mypy/subtypes.py | 4 ++-- test-data/unit/check-protocols.test | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a63db93fd9cb..11f3421331a5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -625,8 +625,8 @@ def visit_instance(self, left: Instance) -> bool: return is_named_instance(item, "builtins.object") if isinstance(right, LiteralType) and left.last_known_value is not None: return self._is_subtype(left.last_known_value, right) - if isinstance(right, CallableType): - # Special case: Instance can be a subtype of Callable. + if isinstance(right, FunctionLike): + # Special case: Instance can be a subtype of Callable / Overloaded. call = find_member("__call__", left, left, is_operator=True) if call: return self._is_subtype(call, right) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 5ed2351e33e6..0367be3dde65 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4215,3 +4215,24 @@ def g4(a: Input[bytes], b: Output[str]) -> None: f(a, b) # E: Cannot infer type argument 1 of "f" [builtins fixtures/tuple.pyi] + +[case testOverloadProtocolSubtyping] +from typing import Protocol, Self, overload + +class NumpyFloat: + __add__: "FloatOP" + +class FloatOP(Protocol): + @overload + def __call__(self, other: float) -> NumpyFloat: ... + @overload + def __call__(self, other: NumpyFloat) -> NumpyFloat: ... + +class SupportsAdd(Protocol): + @overload + def __add__(self, other: float) -> Self: ... + @overload + def __add__(self, other: NumpyFloat) -> Self: ... + +x: SupportsAdd = NumpyFloat() +[builtins fixtures/tuple.pyi] From 1de4871ab4890dc48653d73240eea9e48a5195e4 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 4 Nov 2024 14:27:48 +0300 Subject: [PATCH 0888/1617] Update `config_file` docs (#18103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Fix `strict_equality` markup. It used to be like this: Снимок экрана 2024-11-04 в 13 47 35 - Add `extra_checks` confval --- docs/source/config_file.rst | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index ded8476b60e3..310d0c3dbcb1 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -717,6 +717,14 @@ section of the command line docs. Note: This option will override disabled error codes from the disable_error_code option. +.. confval:: extra_checks + + :type: boolean + :default: False + + This flag enables additional checks that are technically correct but may be impractical in real code. + See :option:`mypy --extra-checks` for more info. + .. confval:: implicit_reexport :type: boolean @@ -739,23 +747,23 @@ section of the command line docs. .. confval:: strict_concatenate - :type: boolean - :default: False + :type: boolean + :default: False - Make arguments prepended via ``Concatenate`` be truly positional-only. + Make arguments prepended via ``Concatenate`` be truly positional-only. .. confval:: strict_equality - :type: boolean - :default: False + :type: boolean + :default: False Prohibit equality checks, identity checks, and container checks between non-overlapping types. .. confval:: strict - :type: boolean - :default: False + :type: boolean + :default: False Enable all optional error checking flags. You can see the list of flags enabled by strict mode in the full :option:`mypy --help` From 976d1056a4abd2e3d145f2476e1d74cd6962cac1 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 4 Nov 2024 13:03:30 +0000 Subject: [PATCH 0889/1617] Fix couple crashes in dmypy (#18098) Fixes https://github.com/python/mypy/issues/18019 Fixes https://github.com/python/mypy/issues/17775 These two are essentially variations of the same thing. Instead of adding e.g. `types` to `SENSITIVE_INTERNAL_MODULES` (which would be fragile and re-introduce same crashes whenever we add a new "core" module) I add _all stdlib modules_. The only scenario when stdlib changes is when a version of mypy changes, and in this case the daemon will be (or should be) restarted anyway. While adding tests for these I noticed a discrepancy in `--follow-imports=normal` in the daemon: the files explicitly added on the command line should be always treated as changed, since otherwise we will not detect errors if a file was removed from command line in an intermediate run. Finally the tests also discovered a spurious error when cache is disabled (via `/dev/null`). --- mypy/build.py | 5 ++++- mypy/dmypy_server.py | 29 ++++++++++++++++++----------- mypy/metastore.py | 2 +- mypy/server/update.py | 16 ++++++++-------- mypy/util.py | 12 ++++++++++++ test-data/unit/daemon.test | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 21 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 4745610d7920..40dd73313335 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1050,7 +1050,10 @@ def generate_deps_for_cache(manager: BuildManager, graph: Graph) -> dict[str, di def write_plugins_snapshot(manager: BuildManager) -> None: """Write snapshot of versions and hashes of currently active plugins.""" snapshot = json_dumps(manager.plugins_snapshot) - if not manager.metastore.write(PLUGIN_SNAPSHOT_FILE, snapshot): + if ( + not manager.metastore.write(PLUGIN_SNAPSHOT_FILE, snapshot) + and manager.options.cache_dir != os.devnull + ): manager.errors.set_file(_cache_dir_prefix(manager.options), None, manager.options) manager.errors.report(0, 0, "Error writing plugins snapshot", blocker=True) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index f8a0f91f87d9..70cfaa5b2fb9 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -625,6 +625,8 @@ def fine_grained_increment_follow_imports( changed, new_files = self.find_reachable_changed_modules( sources, graph, seen, changed_paths ) + # Same as in fine_grained_increment(). + self.add_explicitly_new(sources, changed) if explicit_export_types: # Same as in fine_grained_increment(). add_all_sources_to_changed(sources, changed) @@ -888,6 +890,22 @@ def _find_changed( assert path removed.append((source.module, path)) + self.add_explicitly_new(sources, changed) + + # Find anything that has had its module path change because of added or removed __init__s + last = {s.path: s.module for s in self.previous_sources} + for s in sources: + assert s.path + if s.path in last and last[s.path] != s.module: + # Mark it as removed from its old name and changed at its new name + removed.append((last[s.path], s.path)) + changed.append((s.module, s.path)) + + return changed, removed + + def add_explicitly_new( + self, sources: list[BuildSource], changed: list[tuple[str, str]] + ) -> None: # Always add modules that were (re-)added, since they may be detected as not changed by # fswatcher (if they were actually not changed), but they may still need to be checked # in case they had errors before they were deleted from sources on previous runs. @@ -903,17 +921,6 @@ def _find_changed( ] ) - # Find anything that has had its module path change because of added or removed __init__s - last = {s.path: s.module for s in self.previous_sources} - for s in sources: - assert s.path - if s.path in last and last[s.path] != s.module: - # Mark it as removed from its old name and changed at its new name - removed.append((last[s.path], s.path)) - changed.append((s.module, s.path)) - - return changed, removed - def cmd_inspect( self, show: str, diff --git a/mypy/metastore.py b/mypy/metastore.py index 21fb8730f2c9..ece397360e5b 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -27,7 +27,7 @@ class MetadataStore: @abstractmethod def getmtime(self, name: str) -> float: - """Read the mtime of a metadata entry.. + """Read the mtime of a metadata entry. Raises FileNotFound if the entry does not exist. """ diff --git a/mypy/server/update.py b/mypy/server/update.py index 6bf8c8d07c2d..fdc311bbfa6b 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -146,11 +146,7 @@ TypeInfo, ) from mypy.options import Options -from mypy.semanal_main import ( - core_modules, - semantic_analysis_for_scc, - semantic_analysis_for_targets, -) +from mypy.semanal_main import semantic_analysis_for_scc, semantic_analysis_for_targets from mypy.server.astdiff import ( SymbolSnapshot, compare_symbol_table_snapshots, @@ -162,11 +158,12 @@ from mypy.server.target import trigger_to_target from mypy.server.trigger import WILDCARD_TAG, make_trigger from mypy.typestate import type_state -from mypy.util import module_prefix, split_target +from mypy.util import is_stdlib_file, module_prefix, split_target MAX_ITER: Final = 1000 -SENSITIVE_INTERNAL_MODULES = tuple(core_modules) + ("mypy_extensions", "typing_extensions") +# These are modules beyond stdlib that have some special meaning for mypy. +SENSITIVE_INTERNAL_MODULES = ("mypy_extensions", "typing_extensions") class FineGrainedBuildManager: @@ -406,7 +403,10 @@ def update_module( # builtins and friends could potentially get triggered because # of protocol stuff, but nothing good could possibly come from # actually updating them. - if module in SENSITIVE_INTERNAL_MODULES: + if ( + is_stdlib_file(self.manager.options.abs_custom_typeshed_dir, path) + or module in SENSITIVE_INTERNAL_MODULES + ): return [], (module, path), None manager = self.manager diff --git a/mypy/util.py b/mypy/util.py index 3c550958a659..67d1aa519c84 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -870,6 +870,18 @@ def is_typeshed_file(typeshed_dir: str | None, file: str) -> bool: return False +def is_stdlib_file(typeshed_dir: str | None, file: str) -> bool: + if "stdlib" not in file: + # Fast path + return False + typeshed_dir = typeshed_dir if typeshed_dir is not None else TYPESHED_DIR + stdlib_dir = os.path.join(typeshed_dir, "stdlib") + try: + return os.path.commonpath((stdlib_dir, os.path.abspath(file))) == stdlib_dir + except ValueError: # Different drives on Windows + return False + + def is_stub_package_file(file: str) -> bool: # Use hacky heuristics to check whether file is part of a PEP 561 stub package. if not file.endswith(".pyi"): diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index ca2c969d2f5e..7dfddd8f74df 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -263,6 +263,38 @@ mypy-daemon: error: Missing target module, package, files, or command. $ dmypy stop Daemon stopped +[case testDaemonRunTwoFilesFullTypeshed] +$ dmypy run x.py +Daemon started +Success: no issues found in 1 source file +$ dmypy run y.py +Success: no issues found in 1 source file +$ dmypy run x.py +Success: no issues found in 1 source file +[file x.py] +[file y.py] + +[case testDaemonCheckTwoFilesFullTypeshed] +$ dmypy start +Daemon started +$ dmypy check foo.py +foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy check bar.py +Success: no issues found in 1 source file +$ dmypy check foo.py +foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +[file foo.py] +from bar import add +x: str = add("a", "b") +x_error: int = add("a", "b") +[file bar.py] +def add(a, b) -> str: + return a + b + [case testDaemonWarningSuccessExitCode-posix] $ dmypy run -- foo.py --follow-imports=error --python-version=3.11 Daemon started From 7788c21269006ac2e3ac3bc69e52d68403741e6e Mon Sep 17 00:00:00 2001 From: ag-tafe <144874109+ag-tafe@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:44:54 +0800 Subject: [PATCH 0890/1617] Update for Windows platform. Resolves #18096 (#18097) Fixes #18096 --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5d339330a75..24f7e516e9e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,16 +65,16 @@ However, if you wish to do so, you can run the full test suite like this: ```bash -python3 runtests.py +python runtests.py ``` Some useful commands for running specific tests include: ```bash # Use mypy to check mypy's own code -python3 runtests.py self +python runtests.py self # or equivalently: -python3 -m mypy --config-file mypy_self_check.ini -p mypy +python -m mypy --config-file mypy_self_check.ini -p mypy # Run a single test from the test suite pytest -n0 -k 'test_name' @@ -117,7 +117,7 @@ tox -e dev --override testenv:dev.allowlist_externals+=env -- env # inspect the ``` If you don't already have `tox` installed, you can use a virtual environment as -described above to install `tox` via `pip` (e.g., ``python3 -m pip install tox``). +described above to install `tox` via `pip` (e.g., ``python -m pip install tox``). ## First time contributors From 78fb78bab6ccde06c363f803b8faea59cb0b4248 Mon Sep 17 00:00:00 2001 From: yihong Date: Wed, 6 Nov 2024 22:11:17 +0800 Subject: [PATCH 0891/1617] Fix typos in `generics.rst` (#18110) Found some minor typos in `generics.rst` --- docs/source/generics.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 9c0a308ee39a..4ba6d322417d 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -146,7 +146,7 @@ example (Python 3.12 syntax): from typing import Mapping, Iterator # This is a generic subclass of Mapping - class MyMapp[KT, VT](Mapping[KT, VT]): + class MyMap[KT, VT](Mapping[KT, VT]): def __getitem__(self, k: KT) -> VT: ... def __iter__(self) -> Iterator[KT]: ... def __len__(self) -> int: ... @@ -641,7 +641,7 @@ infer the most flexible variance for each class type variable. Here .. code-block:: python - class Box[T]: # this type is implilicitly covariant + class Box[T]: # this type is implicitly covariant def __init__(self, content: T) -> None: self._content = content @@ -663,12 +663,12 @@ the attribute as ``Final``, the class could still be made covariant: from typing import Final - class Box[T]: # this type is implilicitly covariant + class Box[T]: # this type is implicitly covariant def __init__(self, content: T) -> None: self.content: Final = content def get_content(self) -> T: - return self._content + return self.content When using the legacy syntax, mypy assumes that all user-defined generics are invariant by default. To declare a given generic class as covariant or From eedee206f03cde72e3cfbe5ae7ff560d8ca5f211 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:04:58 -0800 Subject: [PATCH 0892/1617] Sync typeshed (#18114) Source commit: https://github.com/python/typeshed/commit/3d853d5fa80ca58afe703b1603f4a913900fb02e --- .../stdlib/_frozen_importlib_external.pyi | 4 +- mypy/typeshed/stdlib/_io.pyi | 34 ++++++++++----- mypy/typeshed/stdlib/_threading_local.pyi | 3 +- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 4 +- mypy/typeshed/stdlib/getopt.pyi | 8 +++- mypy/typeshed/stdlib/ipaddress.pyi | 41 ++++++++++++------- .../stdlib/multiprocessing/managers.pyi | 3 +- .../typeshed/stdlib/multiprocessing/spawn.pyi | 2 +- mypy/typeshed/stdlib/tarfile.pyi | 4 +- 9 files changed, 66 insertions(+), 37 deletions(-) diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi index 933d7466248e..d3127666da30 100644 --- a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi +++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi @@ -107,9 +107,9 @@ class FileLoader: def get_filename(self, name: str | None = None) -> str: ... def load_module(self, name: str | None = None) -> types.ModuleType: ... if sys.version_info >= (3, 10): - def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.FileReader: ... + def get_resource_reader(self, name: str | None = None) -> importlib.readers.FileReader: ... else: - def get_resource_reader(self, module: types.ModuleType) -> Self | None: ... + def get_resource_reader(self, name: str | None = None) -> Self | None: ... def open_resource(self, resource: str) -> _io.FileIO: ... def resource_path(self, resource: str) -> str: ... def is_resource(self, name: str) -> bool: ... diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi index ccd47726f356..284d99f92b60 100644 --- a/mypy/typeshed/stdlib/_io.pyi +++ b/mypy/typeshed/stdlib/_io.pyi @@ -33,10 +33,10 @@ class _IOBase: def readable(self) -> bool: ... read: Callable[..., Any] def readlines(self, hint: int = -1, /) -> list[bytes]: ... - def seek(self, offset: int, whence: int = ..., /) -> int: ... + def seek(self, offset: int, whence: int = 0, /) -> int: ... def seekable(self) -> bool: ... def tell(self) -> int: ... - def truncate(self, size: int | None = ..., /) -> int: ... + def truncate(self, size: int | None = None, /) -> int: ... def writable(self) -> bool: ... write: Callable[..., Any] def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... @@ -59,8 +59,8 @@ class _BufferedIOBase(_IOBase): def readinto(self, buffer: WriteableBuffer, /) -> int: ... def write(self, buffer: ReadableBuffer, /) -> int: ... def readinto1(self, buffer: WriteableBuffer, /) -> int: ... - def read(self, size: int | None = ..., /) -> bytes: ... - def read1(self, size: int = ..., /) -> bytes: ... + def read(self, size: int | None = -1, /) -> bytes: ... + def read1(self, size: int = -1, /) -> bytes: ... class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes mode: str @@ -69,13 +69,15 @@ class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompat # "name" is a str. In the future, making FileIO generic might help. name: Any def __init__( - self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... + self, file: FileDescriptorOrPath, mode: str = "r", closefd: bool = True, opener: _Opener | None = None ) -> None: ... @property def closefd(self) -> bool: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... + def read(self, size: int | None = -1, /) -> bytes | MaybeNone: ... class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ... + def __init__(self, initial_bytes: ReadableBuffer = b"") -> None: ... # BytesIO does not contain a "name" field. This workaround is necessary # to allow BytesIO sub-classes to add this field, as it is defined # as a read-only property on IO[]. @@ -83,16 +85,22 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] def getvalue(self) -> bytes: ... def getbuffer(self) -> memoryview: ... def read1(self, size: int | None = -1, /) -> bytes: ... + def readlines(self, size: int | None = None, /) -> list[bytes]: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes raw: RawIOBase def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... + def seek(self, target: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes raw: RawIOBase def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def write(self, buffer: ReadableBuffer, /) -> int: ... + def seek(self, target: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes mode: str @@ -101,10 +109,11 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this def peek(self, size: int = 0, /) -> bytes: ... + def truncate(self, pos: int | None = None, /) -> int: ... class BufferedRWPair(BufferedIOBase, _BufferedIOBase): def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192) -> None: ... - def peek(self, size: int = ..., /) -> bytes: ... + def peek(self, size: int = 0, /) -> bytes: ... class _TextIOBase(_IOBase): encoding: str @@ -115,9 +124,9 @@ class _TextIOBase(_IOBase): def detach(self) -> BinaryIO: ... def write(self, s: str, /) -> int: ... def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] - def readline(self, size: int = ..., /) -> str: ... # type: ignore[override] + def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] - def read(self, size: int | None = ..., /) -> str: ... + def read(self, size: int | None = -1, /) -> str: ... @type_check_only class _WrappedBuffer(Protocol): @@ -177,9 +186,10 @@ class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # t # TextIOWrapper's version of seek only supports a limited subset of # operations. def seek(self, cookie: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... class StringIO(TextIOBase, _TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes - def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... + def __init__(self, initial_value: str | None = "", newline: str | None = "\n") -> None: ... # StringIO does not contain a "name" field. This workaround is necessary # to allow StringIO sub-classes to add this field, as it is defined # as a read-only property on IO[]. @@ -187,9 +197,11 @@ class StringIO(TextIOBase, _TextIOBase, TextIO): # type: ignore[misc] # incomp def getvalue(self) -> str: ... @property def line_buffering(self) -> bool: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... class IncrementalNewlineDecoder: - def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ... + def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = "strict") -> None: ... def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... @property def newlines(self) -> str | tuple[str, ...] | None: ... diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi index 98683dabcef8..f9ac942278b9 100644 --- a/mypy/typeshed/stdlib/_threading_local.pyi +++ b/mypy/typeshed/stdlib/_threading_local.pyi @@ -1,5 +1,5 @@ from typing import Any -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias from weakref import ReferenceType __all__ = ["local"] @@ -12,6 +12,7 @@ class _localimpl: def create_dict(self) -> _LocalDict: ... class local: + def __new__(cls, /, *args: Any, **kw: Any) -> Self: ... def __getattribute__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... def __delattr__(self, name: str) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index e0f33f430e5a..56617d8a7b8d 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,5 +1,5 @@ from _typeshed import BytesPath, StrPath, Unused -from collections.abc import Callable, Iterable +from collections.abc import Callable, Iterable, Sequence from distutils.file_util import _BytesPathT, _StrPathT from typing import Literal, overload from typing_extensions import TypeAlias, TypeVarTuple, Unpack @@ -63,7 +63,7 @@ class CCompiler: def set_executables(self, **args: str) -> None: ... def compile( self, - sources: list[str], + sources: Sequence[StrPath], output_dir: str | None = None, macros: list[_Macro] | None = None, include_dirs: list[str] | None = None, diff --git a/mypy/typeshed/stdlib/getopt.pyi b/mypy/typeshed/stdlib/getopt.pyi index bc9d4da4796b..bcc8d9750b19 100644 --- a/mypy/typeshed/stdlib/getopt.pyi +++ b/mypy/typeshed/stdlib/getopt.pyi @@ -1,7 +1,11 @@ +from collections.abc import Iterable + __all__ = ["GetoptError", "error", "getopt", "gnu_getopt"] -def getopt(args: list[str], shortopts: str, longopts: list[str] = []) -> tuple[list[tuple[str, str]], list[str]]: ... -def gnu_getopt(args: list[str], shortopts: str, longopts: list[str] = []) -> tuple[list[tuple[str, str]], list[str]]: ... +def getopt(args: list[str], shortopts: str, longopts: Iterable[str] | str = []) -> tuple[list[tuple[str, str]], list[str]]: ... +def gnu_getopt( + args: list[str], shortopts: str, longopts: Iterable[str] | str = [] +) -> tuple[list[tuple[str, str]], list[str]]: ... class GetoptError(Exception): msg: str diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index fa2faf8e6c13..fce233e84555 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -128,19 +128,6 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def hostmask(self) -> _A: ... -class _BaseInterface(_BaseAddress, Generic[_A, _N]): - hostmask: _A - netmask: _A - network: _N - @property - def ip(self) -> _A: ... - @property - def with_hostmask(self) -> str: ... - @property - def with_netmask(self) -> str: ... - @property - def with_prefixlen(self) -> str: ... - class _BaseV4: @property def version(self) -> Literal[4]: ... @@ -154,9 +141,21 @@ class IPv4Address(_BaseV4, _BaseAddress): class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): ... -class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): +class IPv4Interface(IPv4Address): + netmask: IPv4Address + network: IPv4Network def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... + @property + def hostmask(self) -> IPv4Address: ... + @property + def ip(self) -> IPv4Address: ... + @property + def with_hostmask(self) -> str: ... + @property + def with_netmask(self) -> str: ... + @property + def with_prefixlen(self) -> str: ... class _BaseV6: @property @@ -184,9 +183,21 @@ class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]): @property def is_site_local(self) -> bool: ... -class IPv6Interface(IPv6Address, _BaseInterface[IPv6Address, IPv6Network]): +class IPv6Interface(IPv6Address): + netmask: IPv6Address + network: IPv6Network def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... + @property + def hostmask(self) -> IPv6Address: ... + @property + def ip(self) -> IPv6Address: ... + @property + def with_hostmask(self) -> str: ... + @property + def with_netmask(self) -> str: ... + @property + def with_prefixlen(self) -> str: ... def v4_int_to_packed(address: int) -> bytes: ... def v6_int_to_packed(address: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index c5a1134377a1..8c65eccad07a 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -10,6 +10,7 @@ from typing_extensions import Self, TypeAlias from .connection import Connection from .context import BaseContext from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory +from .util import Finalize as _Finalize __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"] @@ -156,7 +157,7 @@ class BaseManager: def get_server(self) -> Server: ... def connect(self) -> None: ... def start(self, initializer: Callable[..., object] | None = None, initargs: Iterable[Any] = ()) -> None: ... - def shutdown(self) -> None: ... # only available after start() was called + shutdown: _Finalize # only available after start() was called def join(self, timeout: float | None = None) -> None: ... # undocumented @property def address(self) -> Any: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi index 43ce2f07d996..4a9753222897 100644 --- a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi @@ -23,7 +23,7 @@ def get_command_line(**kwds: Any) -> list[str]: ... def spawn_main(pipe_handle: int, parent_pid: int | None = None, tracker_fd: int | None = None) -> None: ... # undocumented -def _main(fd: int) -> Any: ... +def _main(fd: int, parent_sentinel: int) -> int: ... def get_preparation_data(name: str) -> dict[str, Any]: ... old_main_modules: list[ModuleType] diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 4d9ede57ea99..a7135d8150ee 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -1,7 +1,7 @@ import bz2 import io import sys -from _typeshed import StrOrBytesPath, StrPath +from _typeshed import StrOrBytesPath, StrPath, SupportsRead from builtins import list as _list # aliases to avoid name clashes with fields named "type" or "list" from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj @@ -481,7 +481,7 @@ class TarFile: *, filter: Callable[[TarInfo], TarInfo | None] | None = None, ) -> None: ... - def addfile(self, tarinfo: TarInfo, fileobj: IO[bytes] | None = None) -> None: ... + def addfile(self, tarinfo: TarInfo, fileobj: SupportsRead[bytes] | None = None) -> None: ... def gettarinfo( self, name: StrOrBytesPath | None = None, arcname: str | None = None, fileobj: IO[bytes] | None = None ) -> TarInfo: ... From 3b00002acdf098e7241df8f2e1843f8b8260b168 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Thu, 7 Nov 2024 12:06:17 -0500 Subject: [PATCH 0893/1617] Fix [override] error with no line number when argument node has no line number (#18122) Refs #18115 When a parameter type in a method override is incompatible with the parameter type in the supertype definition, mypy emits an error using the `Argument` node as the context. However, sometimes the the `Argument` node doesn't have a line number set, causing the error message to have no associated line number. This happens with the `__replace__` methods created in the dataclass plugin, which have line numbers set on the `FuncDef` nodes, but no line numbers set on the individual argument nodes. This PR fixes the missing line number in the error by falling-back to the FuncDef line number when a line number isn't set on the `Argument` node. (As an alternative fix, we could add line numbers to the `Argument` nodes in the dataclass plugin, but that looks like a more complicated change since multiple methods would be affected). --- mypy/checker.py | 7 ++++--- test-data/unit/check-dataclasses.test | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index a650bdf2a639..1bee348bc252 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2367,10 +2367,11 @@ def erase_override(t: Type) -> Type: else: continue if not is_subtype(original_arg_type, erase_override(override_arg_type)): + context: Context = node if isinstance(node, FuncDef) and not node.is_property: - context: Context = node.arguments[i + len(override.bound_args)] - else: - context = node + arg_node = node.arguments[i + len(override.bound_args)] + if arg_node.line != -1: + context = arg_node self.msg.argument_incompatible_with_supertype( i + 1, name, diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 0f726242b25b..294612db7ea5 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2523,3 +2523,18 @@ reveal_type(replaced_2) # N: Revealed type is "__main__.Gen[builtins.int]" Gen(2).__replace__(x="not an int") # E: Argument "x" to "__replace__" of "Gen" has incompatible type "str"; expected "int" [builtins fixtures/tuple.pyi] + +[case testDunderReplaceCovariantOverride] +# flags: --python-version 3.13 +from dataclasses import dataclass + +@dataclass +class Base: + a: object + +@dataclass +class Child(Base): # E: Argument 1 of "__replace__" is incompatible with supertype "Base"; supertype defines the argument type as "object" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + a: int +[builtins fixtures/tuple.pyi] From 54a2c6d38800195091f23e98e7cd3ef3a6a66702 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 11 Nov 2024 10:07:32 -0500 Subject: [PATCH 0894/1617] Fix type inference of positional parameter in class pattern involving builtin subtype (#18141) Fixes #18140 ### Demo ```python from typing import reveal_type class A(str): pass class B(str): __match_args__ = ("b",) @property def b(self) -> int: return 1 match A("a"): case A(a): reveal_type(a) # before: Revealed type is "__main__.A" # after: Revealed type is "__main__.A" print(type(a)) # output: match B("b"): case B(b): reveal_type(b) # before: Revealed type is "__main__.B" # after: Revealed type is "builtins.int" print(type(b)) # output: ``` --- mypy/checkpattern.py | 4 +++- test-data/unit/check-python310.test | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 6b4fa35f9c49..a7121712a6db 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -693,7 +693,9 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: def should_self_match(self, typ: Type) -> bool: typ = get_proper_type(typ) - if isinstance(typ, Instance) and typ.type.is_named_tuple: + if isinstance(typ, Instance) and typ.type.get("__match_args__") is not None: + # Named tuples and other subtypes of builtins that define __match_args__ + # should not self match. return False for other in self.self_match_types: if is_subtype(typ, other): diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 58b70d7b74d8..8187f27353a9 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -648,6 +648,25 @@ match m: reveal_type(m) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/primitives.pyi] +[case testMatchClassPatternCaptureSelfSubtype] +class A(str): + pass + +class B(str): + __match_args__ = ("b",) + b: int + +def f1(x: A): + match x: + case A(a): + reveal_type(a) # N: Revealed type is "__main__.A" + +def f2(x: B): + match x: + case B(b): + reveal_type(b) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + [case testMatchInvalidClassPattern] m: object From f067db4b7246c6684b86bc53d67079edfbac8a89 Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 11 Nov 2024 23:10:26 +0800 Subject: [PATCH 0895/1617] Fix `OR` pattern structural matching exhaustiveness (#18119) Fixes #18108 This PR fixes the issue of missing value comparisons in a `MatchStmt` due to `OrPattern` incorrectly narrowing down the type of the subject. --- mypy/checkpattern.py | 3 ++- test-data/unit/check-python310.test | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index a7121712a6db..5ea617d3a684 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -158,7 +158,8 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType: for pattern in o.patterns: pattern_type = self.accept(pattern, current_type) pattern_types.append(pattern_type) - current_type = pattern_type.rest_type + if not is_uninhabited(pattern_type.type): + current_type = pattern_type.rest_type # # Collect the final type diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 8187f27353a9..c7bd08900f5f 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1721,6 +1721,22 @@ def f(x: int | str) -> int: case str() as s: return 1 +[case testMatchOrPatternExhaustiveness] +from typing import NoReturn, Literal +def assert_never(x: NoReturn) -> None: ... + +Color = Literal["blue", "green", "red"] +c: Color + +match c: + case "blue": + reveal_type(c) # N: Revealed type is "Literal['blue']" + case "green" | "notColor": + reveal_type(c) # N: Revealed type is "Literal['green']" + case _: + assert_never(c) # E: Argument 1 to "assert_never" has incompatible type "Literal['red']"; expected "Never" +[typing fixtures/typing-typeddict.pyi] + [case testMatchAsPatternIntersection-skip] class A: pass class B: pass From 1ec2ef3d0e3fbb479b08e232c39355e9e20e9052 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:07:40 +0100 Subject: [PATCH 0896/1617] [ci] Try to fix Windows CI failures (#18145) I often encounter test failures on Windows (failing tests are always related to `mypy` daemon). https://github.com/python/mypy/actions/runs/11783610549/job/32821129579 https://github.com/python/mypy/actions/runs/11767558564/job/32776369396 https://github.com/python/mypy/actions/runs/11763771676/job/32768172783 They always manifest as a `RecursionError` during test teardown, the following two frames are repeated many times: ``` ___________ ERROR at teardown of testDaemonRunIgnoreMissingImports ____________ [gw3] win32 -- Python 3.8.10 D:\a\mypy\mypy\.tox\py38\Scripts\python.EXE path = 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp' onerror = .onerror at 0x00000250EE0F94C0> def _rmtree_unsafe(path, onerror): try: with os.scandir(path) as scandir_it: entries = list(scandir_it) except OSError: onerror(os.scandir, path, sys.exc_info()) entries = [] for entry in entries: fullname = entry.path if _rmtree_isdir(entry): try: if entry.is_symlink(): # This can only happen if someone replaces # a directory with a symlink after the call to # os.scandir or entry.is_dir above. raise OSError("Cannot call rmtree on a symbolic link") except OSError: onerror(os.path.islink, fullname, sys.exc_info()) continue _rmtree_unsafe(fullname, onerror) else: try: os.unlink(fullname) except OSError: onerror(os.unlink, fullname, sys.exc_info()) try: > os.rmdir(path) E PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp' C:\hostedtoolcache\windows\Python\3.8.10\x64\lib\shutil.py:620: PermissionError During handling of the above exception, another exception occurred: func = path = 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp' exc_info = (, PermissionError(13, 'The process cannot access the file because it is being used by another process'), ) def onerror(func, path, exc_info): if issubclass(exc_info[0], PermissionError): def resetperms(path): try: _os.chflags(path, 0) except AttributeError: pass _os.chmod(path, 0o700) try: if path != name: resetperms(_os.path.dirname(path)) resetperms(path) try: > _os.unlink(path) E PermissionError: [WinError 5] Access is denied: 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp' C:\hostedtoolcache\windows\Python\3.8.10\x64\lib\tempfile.py:802: PermissionError ``` This likely can be solved by `TemporaryDirectory.ignore_cleanup_errors` but it is only available on 3.10+. I hope this PR does not mask any real deficiency - those failures are extremely annoying. --- mypy/test/data.py | 15 ++++++--------- mypy/test/testfinegrained.py | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index ee567afe2125..bc17178d20e0 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -304,7 +304,7 @@ def __init__( self.data = data self.line = line self.old_cwd: str | None = None - self.tmpdir: tempfile.TemporaryDirectory[str] | None = None + self.tmpdir: str | None = None def runtest(self) -> None: if self.skip: @@ -323,19 +323,19 @@ def runtest(self) -> None: save_dir: str | None = self.config.getoption("--save-failures-to", None) if save_dir: assert self.tmpdir is not None - target_dir = os.path.join(save_dir, os.path.basename(self.tmpdir.name)) + target_dir = os.path.join(save_dir, os.path.basename(self.tmpdir)) print(f"Copying data from test {self.name} to {target_dir}") if not os.path.isabs(target_dir): assert self.old_cwd target_dir = os.path.join(self.old_cwd, target_dir) - shutil.copytree(self.tmpdir.name, target_dir) + shutil.copytree(self.tmpdir, target_dir) raise def setup(self) -> None: parse_test_case(case=self) self.old_cwd = os.getcwd() - self.tmpdir = tempfile.TemporaryDirectory(prefix="mypy-test-") - os.chdir(self.tmpdir.name) + self.tmpdir = tempfile.mkdtemp(prefix="mypy-test-") + os.chdir(self.tmpdir) os.mkdir(test_temp_dir) # Precalculate steps for find_steps() @@ -371,10 +371,7 @@ def teardown(self) -> None: if self.old_cwd is not None: os.chdir(self.old_cwd) if self.tmpdir is not None: - try: - self.tmpdir.cleanup() - except OSError: - pass + shutil.rmtree(self.tmpdir, ignore_errors=True) self.old_cwd = None self.tmpdir = None diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 800ba2dff087..cb8672dfaf29 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -101,8 +101,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if messages: a.extend(normalize_messages(messages)) - assert testcase.tmpdir - a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name)) + assert testcase.tmpdir is not None + a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir)) a.extend(self.maybe_inspect(step, server, main_src)) if server.fine_grained_manager: @@ -248,8 +248,8 @@ def perform_step( new_messages = normalize_messages(new_messages) a = new_messages - assert testcase.tmpdir - a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name)) + assert testcase.tmpdir is not None + a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir)) a.extend(self.maybe_inspect(step, server, main_src)) return a, triggered From 2ebc690279c7e20ed8bec006787030c5ba57c40e Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Tue, 12 Nov 2024 19:00:32 +0100 Subject: [PATCH 0897/1617] [chore] Remove black and ruff from test-requirements (#18139) #15210 standardized lint hooks invocations with pre-commit, but did not remove test requirements that became unneeded. Grepping for `ruff` and `black` revealed that they are never used directly any longer except for one outdated README notice. Remove direct dependency on `black` and `ruff`, retaining them only in pre-commit configuration. pre-commit does not use local package installations and manages all virtual environments internally. --- .pre-commit-config.yaml | 6 +++--- test-data/unit/README.md | 10 ++++++++-- test-requirements.in | 2 -- test-requirements.txt | 20 +++----------------- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27e3e65efdf4..d8e66ecb4dfc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,17 @@ exclude: '^(mypyc/external/)|(mypy/typeshed/)|misc/typeshed_patches' # Exclude all vendored code from lints repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 # must match test-requirements.txt + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.8.0 # must match test-requirements.txt + rev: 24.8.0 hooks: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 # must match test-requirements.txt + rev: v0.6.9 hooks: - id: ruff args: [--exit-non-zero-on-fix] diff --git a/test-data/unit/README.md b/test-data/unit/README.md index 5a9416603541..aaf774d1b62f 100644 --- a/test-data/unit/README.md +++ b/test-data/unit/README.md @@ -100,6 +100,10 @@ First install any additional dependencies needed for testing: python3 -m pip install -U -r test-requirements.txt +Configure `pre-commit` to run the linters automatically when you commit: + + pre-commit install + The unit test suites are driven by the `pytest` framework. To run all mypy tests, run `pytest` in the mypy repository: @@ -157,9 +161,11 @@ To run mypy on itself: python3 -m mypy --config-file mypy_self_check.ini -p mypy -To run the linter: +To run the linter (this commands just wraps `pre-commit`, so you can also +invoke it directly like `pre-commit run -a`, and this will also run when you +`git commit` if enabled): - ruff . + python3 runtests.py lint You can also run all of the above tests using `runtests.py` (this includes type checking mypy and linting): diff --git a/test-requirements.in b/test-requirements.in index e702da28acf1..4e53c63cc36b 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,7 +4,6 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==24.8.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' @@ -12,7 +11,6 @@ psutil>=4.0 pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.6.9 # must match version in .pre-commit-config.yaml setuptools>=75.1.0 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 pre_commit>=3.5.0 diff --git a/test-requirements.txt b/test-requirements.txt index ab3884b99f3b..6eb6f6a95ac8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,12 +6,8 @@ # attrs==24.2.0 # via -r test-requirements.in -black==24.8.0 - # via -r test-requirements.in cfgv==3.4.0 # via pre-commit -click==8.1.7 - # via black coverage==7.6.1 # via pytest-cov distlib==0.3.9 @@ -29,21 +25,13 @@ iniconfig==2.0.0 lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" # via -r test-requirements.in mypy-extensions==1.0.0 - # via - # -r mypy-requirements.txt - # black + # via -r mypy-requirements.txt nodeenv==1.9.1 # via pre-commit packaging==24.1 - # via - # black - # pytest -pathspec==0.12.1 - # via black + # via pytest platformdirs==4.3.6 - # via - # black - # virtualenv + # via virtualenv pluggy==1.5.0 # via pytest pre-commit==3.5.0 @@ -61,8 +49,6 @@ pytest-xdist==3.6.1 # via -r test-requirements.in pyyaml==6.0.2 # via pre-commit -ruff==0.6.9 - # via -r test-requirements.in tomli==2.0.2 # via -r test-requirements.in types-psutil==6.0.0.20241011 From b1372546520fe9672d294521ca96aa7b67327cfd Mon Sep 17 00:00:00 2001 From: q0w <43147888+q0w@users.noreply.github.com> Date: Thu, 14 Nov 2024 06:27:11 +0300 Subject: [PATCH 0898/1617] Fallback to stdlib json if integer exceeds 64-bit range (#18148) Fixes the error below: ```python File "mypy/main.py", line 102, in main File "mypy/main.py", line 186, in run_build File "mypy/build.py", line 194, in build File "mypy/build.py", line 269, in _build File "mypy/build.py", line 2935, in dispatch File "mypy/build.py", line 3333, in process_graph File "mypy/build.py", line 3460, in process_stale_scc File "mypy/build.py", line 2497, in write_cache File "mypy/build.py", line 1560, in write_cache File "mypy/util.py", line 924, in json_dumps TypeError: Integer exceeds 64-bit range ``` Related: https://github.com/ijl/orjson/issues/116 --- mypy/util.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mypy/util.py b/mypy/util.py index 67d1aa519c84..e0a9cf9ce1b2 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -928,11 +928,17 @@ def quote_docstring(docstr: str) -> str: def json_dumps(obj: object, debug: bool = False) -> bytes: if orjson is not None: if debug: - return orjson.dumps(obj, option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS) # type: ignore[no-any-return] + dumps_option = orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS else: # TODO: If we don't sort keys here, testIncrementalInternalScramble fails # We should document exactly what is going on there - return orjson.dumps(obj, option=orjson.OPT_SORT_KEYS) # type: ignore[no-any-return] + dumps_option = orjson.OPT_SORT_KEYS + + try: + return orjson.dumps(obj, option=dumps_option) # type: ignore[no-any-return] + except TypeError as e: + if str(e) != "Integer exceeds 64-bit range": + raise if debug: return json.dumps(obj, indent=2, sort_keys=True).encode("utf-8") From 0ee6dc9f865308dba931df0fa9aee1fd5cd6a4ba Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 14 Nov 2024 04:27:42 +0100 Subject: [PATCH 0899/1617] Move static project metadata to pyproject.toml (#18146) Setuptools supports using `setup.py` and `pyproject.toml` simultaneously. This PR moves most of the static project metadata to `pyproject.toml`. Diff for **`.dist-info/METADATA`** ```diff Metadata-Version: 2.1 Name: mypy Version: 1.14.0+dev Summary: Optional static typing for Python -Home-page: https://www.mypy-lang.org/ +Author-email: Jukka Lehtosalo -Author: Jukka Lehtosalo -Author-email: jukka.lehtosalo@iki.fi License: MIT +Project-URL: Homepage, https://www.mypy-lang.org/ Project-URL: Documentation, https://mypy.readthedocs.io/en/stable/index.html ... ``` **`dist-info/RECORD`** is the same (except for the changed hash for `.dist-info/METADATA`). https://packaging.python.org/en/latest/specifications/core-metadata/ --- pyproject.toml | 66 +++++++++++++++++++++++++++++++++++++++++++++ setup.py | 73 ++------------------------------------------------ 2 files changed, 68 insertions(+), 71 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 06f83506ae4f..2bc6d976db79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,72 @@ requires = [ ] build-backend = "setuptools.build_meta" +[project] +name = "mypy" +description = "Optional static typing for Python" +authors = [{name = "Jukka Lehtosalo", email = "jukka.lehtosalo@iki.fi"}] +license = {text = "MIT"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development", + "Typing :: Typed", +] +requires-python = ">=3.8" +dependencies = [ + # When changing this, also update build-system.requires and mypy-requirements.txt + "typing_extensions>=4.6.0", + "mypy_extensions>=1.0.0", + "tomli>=1.1.0; python_version<'3.11'", +] +dynamic = ["version", "readme"] + +[project.optional-dependencies] +dmypy = ["psutil>=4.0"] +mypyc = ["setuptools>=50"] +python2 = [] +reports = ["lxml"] +install-types = ["pip"] +faster-cache = ["orjson"] + +[project.urls] +Homepage = "https://www.mypy-lang.org/" +Documentation = "https://mypy.readthedocs.io/en/stable/index.html" +Repository = "https://github.com/python/mypy" +Changelog = "https://github.com/python/mypy/blob/master/CHANGELOG.md" +Issues = "https://github.com/python/mypy/issues" + +[project.scripts] +mypy = "mypy.__main__:console_entry" +stubgen = "mypy.stubgen:main" +stubtest = "mypy.stubtest:main" +dmypy = "mypy.dmypy.client:console_entry" +mypyc = "mypyc.__main__:main" + +[tool.setuptools.packages.find] +include = ["mypy*", "mypyc*", "*__mypyc*"] +namespaces = false + +[tool.setuptools.package-data] +mypy = [ + "py.typed", + "typeshed/**/*.py", + "typeshed/**/*.pyi", + "typeshed/stdlib/VERSIONS", + "xml/*.xsd", + "xml/*.xslt", + "xml/*.css", +] + [tool.black] line-length = 99 target-version = ["py38", "py39", "py310", "py311", "py312"] diff --git a/setup.py b/setup.py index 71124916a3e4..7b1c26c6c51a 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ # This requires setuptools when building; setuptools is not needed # when installing from a wheel file (though it is still needed for # alternative forms of installing, as suggested by README.md). -from setuptools import Extension, find_packages, setup +from setuptools import Extension, setup from setuptools.command.build_py import build_py from mypy.version import __version__ as version @@ -26,7 +26,6 @@ if TYPE_CHECKING: from typing_extensions import TypeGuard -description = "Optional static typing for Python" long_description = """ Mypy -- Optional Static Typing for Python ========================================= @@ -78,13 +77,6 @@ def run(self): cmdclass = {"build_py": CustomPythonBuild} -package_data = ["py.typed"] - -package_data += find_package_data(os.path.join("mypy", "typeshed"), ["*.py", "*.pyi"]) -package_data += [os.path.join("mypy", "typeshed", "stdlib", "VERSIONS")] - -package_data += find_package_data(os.path.join("mypy", "xml"), ["*.xsd", "*.xslt", "*.css"]) - USE_MYPYC = False # To compile with mypyc, a mypyc checkout must be present on the PYTHONPATH if len(sys.argv) > 1 and "--use-mypyc" in sys.argv: @@ -179,67 +171,6 @@ def run(self): ext_modules = [] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development", - "Typing :: Typed", -] - setup( - name="mypy", - version=version, - description=description, - long_description=long_description, - author="Jukka Lehtosalo", - author_email="jukka.lehtosalo@iki.fi", - url="https://www.mypy-lang.org/", - license="MIT", - py_modules=[], - ext_modules=ext_modules, - packages=find_packages(), - package_data={"mypy": package_data}, - entry_points={ - "console_scripts": [ - "mypy=mypy.__main__:console_entry", - "stubgen=mypy.stubgen:main", - "stubtest=mypy.stubtest:main", - "dmypy=mypy.dmypy.client:console_entry", - "mypyc=mypyc.__main__:main", - ] - }, - classifiers=classifiers, - cmdclass=cmdclass, - # When changing this, also update mypy-requirements.txt and pyproject.toml - install_requires=[ - "typing_extensions>=4.6.0", - "mypy_extensions >= 1.0.0", - "tomli>=1.1.0; python_version<'3.11'", - ], - # Same here. - extras_require={ - "dmypy": "psutil >= 4.0", - "mypyc": "setuptools >= 50", - "python2": "", - "reports": "lxml", - "install-types": "pip", - "faster-cache": "orjson", - }, - python_requires=">=3.8", - include_package_data=True, - project_urls={ - "Documentation": "https://mypy.readthedocs.io/en/stable/index.html", - "Repository": "https://github.com/python/mypy", - "Changelog": "https://github.com/python/mypy/blob/master/CHANGELOG.md", - "Issues": "https://github.com/python/mypy/issues", - }, + version=version, long_description=long_description, ext_modules=ext_modules, cmdclass=cmdclass ) From 3b6389193d40f2af69da384151c64f87195c3dd3 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Wed, 13 Nov 2024 22:28:13 -0500 Subject: [PATCH 0900/1617] ruff: enable pep8-naming rules (#18144) The vast majority of mypy's code follows PEP 8 naming conventions. However, we currently don't enforce this in the linter config. I noticed this in a recent PR which included a `camelCase` name: https://github.com/python/mypy/pull/18132#discussion_r1836870819. Ruff has some rules to enforce PEP 8 naming style ([pep8-naming](https://docs.astral.sh/ruff/rules/#pep8-naming-n)). I think it would be a good idea to enable some of these to help contributors catch naming discrepancies before PR review. We have a few notable exceptions to PEP 8 naming (e.g. functions named to match ast node names), but these are easily accounted for with some config file ignores and handful of `# noqa`'s. --- mypy/nodes.py | 2 +- mypy/test/teststubgen.py | 2 +- mypyc/lib-rt/setup.py | 2 +- mypyc/test/test_emitfunc.py | 2 +- pyproject.toml | 7 +++++++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index dabfb463cc95..56c61ce26d63 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1727,7 +1727,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_str_expr(self) -def is_StrExpr_list(seq: list[Expression]) -> TypeGuard[list[StrExpr]]: +def is_StrExpr_list(seq: list[Expression]) -> TypeGuard[list[StrExpr]]: # noqa: N802 return all(isinstance(item, StrExpr) for item in seq) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 46e805d9ca82..dffa1aa80c5d 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -987,7 +987,7 @@ def test(cls, arg0: str) -> None: def test_generate_c_type_classmethod_with_overloads(self) -> None: class TestClass: @classmethod - def test(self, arg0: str) -> None: + def test(cls, arg0: str) -> None: """ test(cls, arg0: str) test(cls, arg0: int) diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index 66b130581cb3..1faacc8fc136 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -21,7 +21,7 @@ compile_args = ["--std=c++11"] -class build_ext_custom(build_ext): +class build_ext_custom(build_ext): # noqa: N801 def get_library_names(self): return ["gtest"] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 317427afac5a..273ba409fac2 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -134,7 +134,7 @@ def test_integer(self) -> None: def test_tuple_get(self) -> None: self.assert_emit(TupleGet(self.t, 1, 0), "cpy_r_r0 = cpy_r_t.f1;") - def test_load_None(self) -> None: + def test_load_None(self) -> None: # noqa: N802 self.assert_emit( LoadAddress(none_object_op.type, none_object_op.src, 0), "cpy_r_r0 = (PyObject *)&_Py_NoneStruct;", diff --git a/pyproject.toml b/pyproject.toml index 2bc6d976db79..9f9766a75182 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,7 @@ select = [ "W", # pycodestyle (warning) "B", # flake8-bugbear "I", # isort + "N", # pep8-naming "RUF100", # Unused noqa comments "PGH004", # blanket noqa comments "UP", # pyupgrade @@ -134,6 +135,8 @@ ignore = [ "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for isinstance checks "E731", # Do not assign a `lambda` expression, use a `def` "E741", # Ambiguous variable name + "N818", # Exception should be named with an Error suffix + "N806", # UPPER_CASE used for constant local variables "UP031", # Use format specifiers instead of percent format "UP032", # 'f-string always preferable to format' is controversial "C416", # There are a few cases where it's nice to have names for the dict items @@ -147,6 +150,10 @@ unfixable = [ "UP036", # sometimes it's better to just noqa this ] +[tool.ruff.lint.per-file-ignores] +# Mixed case variable and function names. +"mypy/fastparse.py" = ["N802", "N816"] + [tool.ruff.lint.isort] combine-as-imports = true extra-standard-library = ["typing_extensions"] From 3e52d0c1d37344a58c53d4b7a0920b4e38ebedff Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Thu, 14 Nov 2024 04:45:47 +0100 Subject: [PATCH 0901/1617] stubgen: do not include mypy generated symbols (#18137) Addresses part of #18081 stubgen still does not handle dataclass transforms correctly but with this change we make sure to never include private mypy generated symbols in the stubs. --- mypy/stubutil.py | 2 ++ test-data/unit/stubgen.test | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 3f917ca7665d..c11843c57f2a 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -832,6 +832,8 @@ def is_not_in_all(self, name: str) -> bool: return False def is_private_name(self, name: str, fullname: str | None = None) -> bool: + if "__mypy-" in name: + return True # Never include mypy generated symbols if self._include_private: return False if fullname in self.EXTRA_EXPORTED: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index e64c9c66d65d..0801d9a27011 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4508,3 +4508,21 @@ class C3[T3 = int]: ... class C4[T4: int | float = int](list[T4]): ... def f5[T5 = int]() -> None: ... + +[case testIgnoreMypyGeneratedMethods_semanal] +# flags: --include-private --python-version=3.13 +from typing_extensions import dataclass_transform + +# TODO: preserve dataclass_transform decorator +@dataclass_transform() +class DCMeta(type): ... +class DC(metaclass=DCMeta): + x: str + +[out] +class DCMeta(type): ... + +class DC(metaclass=DCMeta): + x: str + def __init__(self, x) -> None: ... + def __replace__(self, *, x) -> None: ... From fa01a0744eded37b61e44c2e00067524a8f9df24 Mon Sep 17 00:00:00 2001 From: David Salvisberg Date: Thu, 14 Nov 2024 04:58:11 +0100 Subject: [PATCH 0902/1617] Fix incorrect truthyness for Enum types and literals (#17337) Fixes: #17333 This ensures `can_be_true` and `can_be_false` on enum literals depends on the specific `Enum` fallback type behind the `Literal`, since `__bool__` can be overriden like on any other type. Additionally typeops `true_only` and `false_only` now respect the metaclass when looking up the return values of `__bool__` and `__len__`, which ensures that a default `Enum` that doesn't override `__bool__` is still considered always truthy. --- mypy/typeops.py | 8 +++ mypy/types.py | 18 ++++++ test-data/unit/check-custom-plugin.test | 1 + test-data/unit/check-enum.test | 75 ++++++++++++++++++++++-- test-data/unit/check-incremental.test | 1 + test-data/unit/check-newsemanal.test | 1 + test-data/unit/check-python310.test | 4 ++ test-data/unit/deps-classes.test | 1 + test-data/unit/deps-types.test | 1 + test-data/unit/diff.test | 2 + test-data/unit/fine-grained.test | 8 +++ test-data/unit/fixtures/enum.pyi | 16 +++++ test-data/unit/fixtures/staticmethod.pyi | 1 + test-data/unit/lib-stub/enum.pyi | 2 + test-data/unit/merge.test | 1 + 15 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 test-data/unit/fixtures/enum.pyi diff --git a/mypy/typeops.py b/mypy/typeops.py index 9e5e347533c6..f190168a18d7 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -650,8 +650,16 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: t = get_proper_type(t) + # For Enum literals the ret_type can change based on the Enum + # we need to check the type of the enum rather than the literal + if isinstance(t, LiteralType) and t.is_enum_literal(): + t = t.fallback + if isinstance(t, Instance): sym = t.type.get(name) + # Fallback to the metaclass for the lookup when necessary + if not sym and (m := t.type.metaclass_type): + sym = m.type.get(name) if sym: sym_type = get_proper_type(sym.type) if isinstance(sym_type, CallableType): diff --git a/mypy/types.py b/mypy/types.py index cc9c65299ee8..3a6933922902 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2818,10 +2818,28 @@ def __init__( self.fallback = fallback self._hash = -1 # Cached hash value + # NOTE: Enum types are always truthy by default, but this can be changed + # in subclasses, so we need to get the truthyness from the Enum + # type rather than base it on the value (which is a non-empty + # string for enums, so always truthy) + # TODO: We should consider moving this branch to the `can_be_true` + # `can_be_false` properties instead, so the truthyness only + # needs to be determined once per set of Enum literals. + # However, the same can be said for `TypeAliasType` in some + # cases and we only set the default based on the type it is + # aliasing. So if we decide to change this, we may want to + # change that as well. perf_compare output was inconclusive + # but slightly favored this version, probably because we have + # almost no test cases where we would redundantly compute + # `can_be_false`/`can_be_true`. def can_be_false_default(self) -> bool: + if self.fallback.type.is_enum: + return self.fallback.can_be_false return not self.value def can_be_true_default(self) -> bool: + if self.fallback.type.is_enum: + return self.fallback.can_be_true return bool(self.value) def accept(self, visitor: TypeVisitor[T]) -> T: diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 666bf9680405..1e06f300570e 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -967,6 +967,7 @@ class Cls(enum.Enum): attr = 'test' reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[builtins fixtures/enum.pyi] [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/class_attr_hook.py diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 533da6652f8f..422beb5c82f0 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -11,6 +11,8 @@ m = Medal.gold if int(): m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") +[builtins fixtures/enum.pyi] + -- Creation from Enum call -- ----------------------- @@ -79,6 +81,7 @@ from typing import Generic, TypeVar T = TypeVar("T") class Medal(Generic[T], metaclass=EnumMeta): # E: Enum class cannot be generic q = None +[builtins fixtures/enum.pyi] [case testEnumNameAndValue] from enum import Enum @@ -175,6 +178,31 @@ def infer_truth(truth: Truth) -> None: reveal_type(truth.value) # N: Revealed type is "builtins.bool" [builtins fixtures/bool.pyi] +[case testEnumTruthyness] +# mypy: warn-unreachable +import enum +class E(enum.Enum): + x = 0 +if not E.x: + "noop" +[builtins fixtures/tuple.pyi] +[out] +main:6: error: Statement is unreachable + +[case testEnumTruthynessCustomDunderBool] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal +class E(enum.Enum): + x = 0 + def __bool__(self) -> Literal[False]: + return False +if E.x: + "noop" +[builtins fixtures/enum.pyi] +[out] +main:9: error: Statement is unreachable + [case testEnumUnique] import enum @enum.unique @@ -183,6 +211,7 @@ class E(enum.Enum): y = 1 # NOTE: This duplicate value is not detected by mypy at the moment x = 1 x = E.x +[builtins fixtures/enum.pyi] [out] main:7: error: Incompatible types in assignment (expression has type "E", variable has type "int") @@ -197,6 +226,7 @@ if int(): s = '' if int(): s = N.y # E: Incompatible types in assignment (expression has type "N", variable has type "str") +[builtins fixtures/enum.pyi] [case testIntEnum_functionTakingIntEnum] from enum import IntEnum @@ -207,6 +237,7 @@ def takes_some_int_enum(n: SomeIntEnum): takes_some_int_enum(SomeIntEnum.x) takes_some_int_enum(1) # Error takes_some_int_enum(SomeIntEnum(1)) # How to deal with the above +[builtins fixtures/enum.pyi] [out] main:7: error: Argument 1 to "takes_some_int_enum" has incompatible type "int"; expected "SomeIntEnum" @@ -218,6 +249,7 @@ def takes_int(i: int): pass takes_int(SomeIntEnum.x) takes_int(2) +[builtins fixtures/enum.pyi] [case testIntEnum_functionReturningIntEnum] from enum import IntEnum @@ -230,6 +262,7 @@ an_int = returns_some_int_enum() an_enum = SomeIntEnum.x an_enum = returns_some_int_enum() +[builtins fixtures/enum.pyi] [out] [case testStrEnumCreation] @@ -244,6 +277,7 @@ reveal_type(MyStrEnum.x) # N: Revealed type is "Literal[__main__.MyStrEnum.x]?" reveal_type(MyStrEnum.x.value) # N: Revealed type is "Literal['x']?" reveal_type(MyStrEnum.y) # N: Revealed type is "Literal[__main__.MyStrEnum.y]?" reveal_type(MyStrEnum.y.value) # N: Revealed type is "Literal['y']?" +[builtins fixtures/enum.pyi] [out] [case testEnumMethods] @@ -278,6 +312,7 @@ takes_int(SomeExtIntEnum.x) def takes_some_ext_int_enum(s: SomeExtIntEnum): pass takes_some_ext_int_enum(SomeExtIntEnum.x) +[builtins fixtures/enum.pyi] [case testNamedTupleEnum] from typing import NamedTuple @@ -299,6 +334,7 @@ class E(IntEnum): a = 1 x: int reveal_type(E(x)) +[builtins fixtures/tuple.pyi] [out] main:5: note: Revealed type is "__main__.E" @@ -308,6 +344,7 @@ class E(IntEnum): a = 1 s: str reveal_type(E[s]) +[builtins fixtures/enum.pyi] [out] main:5: note: Revealed type is "__main__.E" @@ -317,6 +354,7 @@ class E(IntEnum): a = 1 E[1] # E: Enum index should be a string (actual index type "int") x = E[1] # E: Enum index should be a string (actual index type "int") +[builtins fixtures/enum.pyi] [case testEnumIndexIsNotAnAlias] from enum import Enum @@ -334,6 +372,7 @@ def get_member(name: str) -> E: return val reveal_type(get_member('a')) # N: Revealed type is "__main__.E" +[builtins fixtures/enum.pyi] [case testGenericEnum] from enum import Enum @@ -346,6 +385,7 @@ class F(Generic[T], Enum): # E: Enum class cannot be generic y: T reveal_type(F[int].x) # N: Revealed type is "__main__.F[builtins.int]" +[builtins fixtures/enum.pyi] [case testEnumFlag] from enum import Flag @@ -357,6 +397,7 @@ if int(): x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") if int(): x = x | C.b +[builtins fixtures/enum.pyi] [case testEnumIntFlag] from enum import IntFlag @@ -368,6 +409,7 @@ if int(): x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") if int(): x = x | C.b +[builtins fixtures/enum.pyi] [case testAnonymousEnum] from enum import Enum @@ -378,6 +420,7 @@ class A: self.x = E.a a = A() reveal_type(a.x) +[builtins fixtures/enum.pyi] [out] main:8: note: Revealed type is "__main__.E@4" @@ -393,6 +436,7 @@ x = A.E.a y = B.E.a if int(): x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +[builtins fixtures/enum.pyi] [case testFunctionalEnumString] from enum import Enum, IntEnum @@ -402,6 +446,7 @@ reveal_type(E.foo) reveal_type(E.bar.value) reveal_type(I.bar) reveal_type(I.baz.value) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Any" @@ -414,6 +459,7 @@ E = Enum('E', ('foo', 'bar')) F = IntEnum('F', ['bar', 'baz']) reveal_type(E.foo) reveal_type(F.baz) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -426,6 +472,7 @@ reveal_type(E.foo) reveal_type(F.baz) reveal_type(E.foo.value) reveal_type(F.bar.name) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -440,6 +487,7 @@ reveal_type(E.foo) reveal_type(F.baz) reveal_type(E.foo.value) reveal_type(F.bar.name) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -455,6 +503,7 @@ fake_enum1 = Enum('fake_enum1', ['a', 'b']) fake_enum2 = Enum('fake_enum2', names=['a', 'b']) fake_enum3 = Enum(value='fake_enum3', names=['a', 'b']) fake_enum4 = Enum(value='fake_enum4', names=['a', 'b'] , module=__name__) +[builtins fixtures/enum.pyi] [case testFunctionalEnumErrors] from enum import Enum, IntEnum @@ -484,6 +533,7 @@ X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(.. reveal_type(X.a) # N: Revealed type is "Literal[__main__.Something@23.a]?" X.asdf # E: "Type[Something@23]" has no attribute "asdf" +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [case testFunctionalEnumFlag] @@ -498,6 +548,7 @@ reveal_type(B.a.name) # N: Revealed type is "Literal['a']?" # TODO: The revealed type should be 'int' here reveal_type(A.x.value) # N: Revealed type is "Any" reveal_type(B.a.value) # N: Revealed type is "Any" +[builtins fixtures/enum.pyi] [case testAnonymousFunctionalEnum] from enum import Enum @@ -507,6 +558,7 @@ class A: self.x = E.a a = A() reveal_type(a.x) +[builtins fixtures/enum.pyi] [out] main:7: note: Revealed type is "__main__.A.E@4" @@ -520,6 +572,7 @@ x = A.E.a y = B.E.a if int(): x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +[builtins fixtures/enum.pyi] [case testFunctionalEnumProtocols] from enum import IntEnum @@ -537,6 +590,7 @@ a: E = E.x # type: ignore[used-before-def] class E(Enum): x = 1 y = 2 +[builtins fixtures/enum.pyi] [out] [case testEnumWorkWithForward2] @@ -547,6 +601,7 @@ F = Enum('F', {'x': 1, 'y': 2}) def fn(x: F) -> None: pass fn(b) +[builtins fixtures/enum.pyi] [out] [case testFunctionalEnum] @@ -564,6 +619,7 @@ Gu.a Gb.a Hu.a Hb.a +[builtins fixtures/enum.pyi] [out] [case testEnumIncremental] @@ -576,6 +632,7 @@ class E(Enum): a = 1 b = 2 F = Enum('F', 'a b') +[builtins fixtures/enum.pyi] [rechecked] [stale] [out1] @@ -734,6 +791,7 @@ class SomeEnum(Enum): from enum import Enum class SomeEnum(Enum): a = "foo" +[builtins fixtures/enum.pyi] [out] main:2: note: Revealed type is "Literal[1]?" [out2] @@ -949,7 +1007,7 @@ x: Optional[Foo] if x: reveal_type(x) # N: Revealed type is "__main__.Foo" else: - reveal_type(x) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(x) # N: Revealed type is "None" if x is not None: reveal_type(x) # N: Revealed type is "__main__.Foo" @@ -961,7 +1019,7 @@ if x is Foo.A: else: reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.B], Literal[__main__.Foo.C], None]" reveal_type(x) # N: Revealed type is "Union[__main__.Foo, None]" -[builtins fixtures/bool.pyi] +[builtins fixtures/enum.pyi] [case testEnumReachabilityWithMultipleEnums] from enum import Enum @@ -1104,6 +1162,7 @@ class A: self.b = Enum("b", [("foo", "bar")]) # E: Enum type as attribute is not supported reveal_type(A().b) # N: Revealed type is "Any" +[builtins fixtures/enum.pyi] [case testEnumReachabilityWithChaining] from enum import Enum @@ -1307,6 +1366,7 @@ class Foo(Enum): a = Foo.A reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" reveal_type(a._value_) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" +[builtins fixtures/enum.pyi] [case testNewSetsUnexpectedValueType] from enum import Enum @@ -1325,7 +1385,7 @@ class Foo(bytes, Enum): a = Foo.A reveal_type(a.value) # N: Revealed type is "Any" reveal_type(a._value_) # N: Revealed type is "Any" -[builtins fixtures/primitives.pyi] +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [case testValueTypeWithNewInParentClass] @@ -1398,6 +1458,7 @@ class E(IntEnum): A = N(0) reveal_type(E.A.value) # N: Revealed type is "__main__.N" +[builtins fixtures/enum.pyi] [case testEnumFinalValues] @@ -1410,6 +1471,7 @@ class Medal(Enum): Medal.gold = 0 # E: Cannot assign to final attribute "gold" # Same value: Medal.silver = 2 # E: Cannot assign to final attribute "silver" +[builtins fixtures/enum.pyi] [case testEnumFinalValuesCannotRedefineValueProp] @@ -1417,6 +1479,7 @@ from enum import Enum class Types(Enum): key = 0 value = 1 +[builtins fixtures/enum.pyi] [case testEnumReusedKeys] @@ -2035,7 +2098,7 @@ class C(Enum): _ignore_ = 'X' C._ignore_ # E: "Type[C]" has no attribute "_ignore_" -[typing fixtures/typing-medium.pyi] +[builtins fixtures/enum.pyi] [case testCanOverrideDunderAttributes] import typing @@ -2114,6 +2177,7 @@ class AllPartialList(Enum): def check(self) -> None: reveal_type(self.value) # N: Revealed type is "builtins.list[Any]" +[builtins fixtures/tuple.pyi] [case testEnumPrivateAttributeNotMember] from enum import Enum @@ -2125,6 +2189,7 @@ class MyEnum(Enum): # TODO: change the next line to use MyEnum._MyEnum__my_dict when mypy implements name mangling x: MyEnum = MyEnum.__my_dict # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum") +[builtins fixtures/enum.pyi] [case testEnumWithPrivateAttributeReachability] # flags: --warn-unreachable @@ -2202,6 +2267,7 @@ class Medal(Enum): # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ # E: Incompatible types in assignment (expression has type "int", variable has type "str") bronze = 3 +[builtins fixtures/enum.pyi] [case testEnumMemberWithPlaceholder] from enum import Enum @@ -2211,6 +2277,7 @@ class Pet(Enum): DOG: str = ... # E: Enum members must be left unannotated \ # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "str") +[builtins fixtures/enum.pyi] [case testEnumValueWithPlaceholderNodeType] # https://github.com/python/mypy/issues/11971 diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 173265e48e6f..888b4c26a7c7 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6586,6 +6586,7 @@ class TheClass: for x in names }) self.enum_type = pyenum +[builtins fixtures/tuple.pyi] [out] [out2] tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index fe02ac3ccd5e..784b9db9f66e 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2069,6 +2069,7 @@ from enum import Enum A = Enum('A', ['x', 'y']) A = Enum('A', ['z', 't']) # E: Name "A" already defined on line 3 +[builtins fixtures/tuple.pyi] [case testNewAnalyzerNewTypeRedefinition] from typing import NewType diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index c7bd08900f5f..8f121e578130 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1543,6 +1543,7 @@ def g(m: Medal) -> int: case Medal.bronze: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" return 2 +[builtins fixtures/enum.pyi] [case testMatchLiteralPatternEnumWithTypedAttribute] @@ -1597,6 +1598,7 @@ match m: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" case _ as unreachable: assert_never(unreachable) +[builtins fixtures/enum.pyi] [case testMatchLiteralPatternEnumCustomEquals-skip] from enum import Enum @@ -1614,6 +1616,7 @@ match m: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" case _: reveal_type(m) # N: Revealed type is "__main__.Medal" +[builtins fixtures/enum.pyi] [case testMatchNarrowUsingPatternGuardSpecialCase] def f(x: int | str) -> int: @@ -1691,6 +1694,7 @@ def union(x: str | bool) -> None: case True: return reveal_type(x) # N: Revealed type is "Union[builtins.str, Literal[False]]" +[builtins fixtures/tuple.pyi] [case testMatchAssertFalseToSilenceFalsePositives] class C: diff --git a/test-data/unit/deps-classes.test b/test-data/unit/deps-classes.test index ebe2e9caed02..a8fc5d629491 100644 --- a/test-data/unit/deps-classes.test +++ b/test-data/unit/deps-classes.test @@ -178,6 +178,7 @@ def g() -> None: A.X [file m.py] class B: pass +[builtins fixtures/enum.pyi] [out] -> m.g -> , m.A, m.f, m.g diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index def117fe04df..6992a5bdec00 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -905,6 +905,7 @@ def g() -> None: A.X [file mod.py] class B: pass +[builtins fixtures/tuple.pyi] [out] -> m.g -> , m.f, m.g diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 10fb0a80cf38..4acf451e2c34 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -566,6 +566,7 @@ A = Enum('A', 'x') B = Enum('B', 'y') C = IntEnum('C', 'x') D = IntEnum('D', 'x y') +[builtins fixtures/enum.pyi] [out] __main__.B.x __main__.B.y @@ -605,6 +606,7 @@ class D(Enum): Y = 'b' class F(Enum): X = 0 +[builtins fixtures/enum.pyi] [out] __main__.B.Y __main__.B.Z diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index a611320909e5..f30f1e232364 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -5355,6 +5355,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/enum.pyi] [out] == == @@ -5386,6 +5387,7 @@ if int(): n = C.X if int(): n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5410,6 +5412,7 @@ from enum import Enum class C(Enum): X = 0 +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == @@ -5432,6 +5435,7 @@ from enum import Enum class C(Enum): X = 0 Y = 1 +[builtins fixtures/enum.pyi] [out] == a.py:4: error: Argument 1 to "f" has incompatible type "C"; expected "int" @@ -5456,6 +5460,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/tuple.pyi] [out] == == @@ -5485,6 +5490,7 @@ if int(): n: int n = C.X n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5506,6 +5512,7 @@ C = Enum('C', 'X Y') from enum import Enum C = Enum('C', 'X') +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == @@ -5526,6 +5533,7 @@ class C: [file b.py.2] from enum import Enum C = Enum('C', [('X', 0), ('Y', 1)]) +[builtins fixtures/tuple.pyi] [out] == a.py:4: error: Argument 1 to "f" has incompatible type "C"; expected "int" diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi new file mode 100644 index 000000000000..debffacf8f32 --- /dev/null +++ b/test-data/unit/fixtures/enum.pyi @@ -0,0 +1,16 @@ +# Minimal set of builtins required to work with Enums +from typing import TypeVar, Generic + +T = TypeVar('T') + +class object: + def __init__(self): pass + +class type: pass +class tuple(Generic[T]): + def __getitem__(self, x: int) -> T: pass + +class int: pass +class str: pass +class dict: pass +class ellipsis: pass diff --git a/test-data/unit/fixtures/staticmethod.pyi b/test-data/unit/fixtures/staticmethod.pyi index 8a87121b2a71..a0ca831c7527 100644 --- a/test-data/unit/fixtures/staticmethod.pyi +++ b/test-data/unit/fixtures/staticmethod.pyi @@ -19,3 +19,4 @@ class str: pass class bytes: pass class ellipsis: pass class dict: pass +class tuple: pass diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi index 0e0b8e025d9f..ccb3818b9d25 100644 --- a/test-data/unit/lib-stub/enum.pyi +++ b/test-data/unit/lib-stub/enum.pyi @@ -1,4 +1,5 @@ from typing import Any, TypeVar, Union, Type, Sized, Iterator +from typing_extensions import Literal _T = TypeVar('_T') @@ -7,6 +8,7 @@ class EnumMeta(type, Sized): def __iter__(self: Type[_T]) -> Iterator[_T]: pass def __reversed__(self: Type[_T]) -> Iterator[_T]: pass def __getitem__(self: Type[_T], name: str) -> _T: pass + def __bool__(self) -> Literal[True]: pass class Enum(metaclass=EnumMeta): def __new__(cls: Type[_T], value: object) -> _T: pass diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index 19b1839f86c0..a6a64c75b2a3 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -1490,6 +1490,7 @@ from enum import Enum class A(Enum): X = 0 Y = 1 +[builtins fixtures/enum.pyi] [out] TypeInfo<0>( Name(target.A) From 880eb87b11ba72fca79fc3c5c234e516d7a96972 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Fri, 15 Nov 2024 10:17:05 -0500 Subject: [PATCH 0903/1617] Fix checking of match sequence pattern against bounded type variables (#18091) Fixes #18089 Adds handling for bounded type variables when checking match sequence patterns. Previously, this crashed. Now, this correctly narrows the upper bound of the type variable: ```python from typing import TypeVar, Sequence T = TypeVar("T", bound=Sequence[int | str]) def accept_seq_int(x: Sequence[int]): pass def f(x: T) -> None: match x: case [1, 2]: accept_seq_int(x) # ok: upper bound narrowed to Sequence[int] case _: accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]" ``` --- mypy/checkpattern.py | 21 +++++++++++++++------ test-data/unit/check-python310.test | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 5ea617d3a684..43f42039b199 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -46,6 +46,7 @@ TypedDictType, TypeOfAny, TypeVarTupleType, + TypeVarType, UninhabitedType, UnionType, UnpackType, @@ -343,13 +344,11 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: new_inner_type = UninhabitedType() for typ in new_inner_types: new_inner_type = join_types(new_inner_type, typ) - new_type = self.construct_sequence_child(current_type, new_inner_type) - if is_subtype(new_type, current_type): - new_type, _ = self.chk.conditional_types_with_intersection( - current_type, [get_type_range(new_type)], o, default=current_type - ) + if isinstance(current_type, TypeVarType): + new_bound = self.narrow_sequence_child(current_type.upper_bound, new_inner_type, o) + new_type = current_type.copy_modified(upper_bound=new_bound) else: - new_type = current_type + new_type = self.narrow_sequence_child(current_type, new_inner_type, o) return PatternType(new_type, rest_type, captures) def get_sequence_type(self, t: Type, context: Context) -> Type | None: @@ -448,6 +447,16 @@ def expand_starred_pattern_types( return new_types + def narrow_sequence_child(self, outer_type: Type, inner_type: Type, ctx: Context) -> Type: + new_type = self.construct_sequence_child(outer_type, inner_type) + if is_subtype(new_type, outer_type): + new_type, _ = self.chk.conditional_types_with_intersection( + outer_type, [get_type_range(new_type)], ctx, default=outer_type + ) + else: + new_type = outer_type + return new_type + def visit_starred_pattern(self, o: StarredPattern) -> PatternType: captures: dict[Expression, Type] = {} if o.capture is not None: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 8f121e578130..435fbde3e2ae 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -2382,3 +2382,30 @@ def test(xs: Tuple[Unpack[Ts]]) -> None: reveal_type(b3) # N: Revealed type is "builtins.list[builtins.object]" reveal_type(c3) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternTypeVarBoundNoCrash] +# This was crashing: https://github.com/python/mypy/issues/18089 +from typing import TypeVar, Sequence, Any + +T = TypeVar("T", bound=Sequence[Any]) + +def f(x: T) -> None: + match x: + case [_]: + pass +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternTypeVarBoundNarrows] +from typing import TypeVar, Sequence + +T = TypeVar("T", bound=Sequence[int | str]) + +def accept_seq_int(x: Sequence[int]): ... + +def f(x: T) -> None: + match x: + case [1, 2]: + accept_seq_int(x) + case _: + accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]" +[builtins fixtures/tuple.pyi] From 35048bf4776d656117570ec2cab24094b504221e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:10:15 -0800 Subject: [PATCH 0904/1617] Sync typeshed (#18157) Source commit: https://github.com/python/typeshed/commit/5052fa2f18db4493892e0f2775030683c9d06531 --- mypy/typeshed/stdlib/VERSIONS | 7 ++ mypy/typeshed/stdlib/_bz2.pyi | 18 ++++ mypy/typeshed/stdlib/_contextvars.pyi | 61 ++++++++++++ mypy/typeshed/stdlib/_dbm.pyi | 43 +++++++++ mypy/typeshed/stdlib/_gdbm.pyi | 47 +++++++++ mypy/typeshed/stdlib/_lzma.pyi | 60 ++++++++++++ mypy/typeshed/stdlib/_queue.pyi | 20 ++++ mypy/typeshed/stdlib/_struct.pyi | 22 +++++ mypy/typeshed/stdlib/builtins.pyi | 6 +- mypy/typeshed/stdlib/bz2.pyi | 19 +--- mypy/typeshed/stdlib/contextvars.pyi | 62 +----------- mypy/typeshed/stdlib/dbm/gnu.pyi | 48 +--------- mypy/typeshed/stdlib/dbm/ndbm.pyi | 44 +-------- mypy/typeshed/stdlib/fractions.pyi | 10 +- mypy/typeshed/stdlib/logging/config.pyi | 6 +- mypy/typeshed/stdlib/logging/handlers.pyi | 2 + mypy/typeshed/stdlib/lzma.pyi | 95 +++++++------------ mypy/typeshed/stdlib/mmap.pyi | 19 +++- .../stdlib/multiprocessing/managers.pyi | 77 ++++++++++----- mypy/typeshed/stdlib/queue.pyi | 13 +-- mypy/typeshed/stdlib/struct.pyi | 23 +---- 21 files changed, 405 insertions(+), 297 deletions(-) create mode 100644 mypy/typeshed/stdlib/_bz2.pyi create mode 100644 mypy/typeshed/stdlib/_contextvars.pyi create mode 100644 mypy/typeshed/stdlib/_dbm.pyi create mode 100644 mypy/typeshed/stdlib/_gdbm.pyi create mode 100644 mypy/typeshed/stdlib/_lzma.pyi create mode 100644 mypy/typeshed/stdlib/_queue.pyi create mode 100644 mypy/typeshed/stdlib/_struct.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 7c9f3cfda837..702fdbab75c0 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -24,18 +24,22 @@ _asyncio: 3.0- _bisect: 3.0- _blake2: 3.6- _bootlocale: 3.4-3.9 +_bz2: 3.3- _codecs: 3.0- _collections_abc: 3.3- _compat_pickle: 3.1- _compression: 3.5- +_contextvars: 3.7- _csv: 3.0- _ctypes: 3.0- _curses: 3.0- +_dbm: 3.0- _decimal: 3.3- _dummy_thread: 3.0-3.8 _dummy_threading: 3.0-3.8 _frozen_importlib: 3.0- _frozen_importlib_external: 3.5- +_gdbm: 3.0- _heapq: 3.0- _imp: 3.0- _interpchannels: 3.13- @@ -45,6 +49,7 @@ _io: 3.0- _json: 3.0- _locale: 3.0- _lsprof: 3.0- +_lzma: 3.3- _markupbase: 3.0- _msi: 3.0-3.12 _operator: 3.4- @@ -52,12 +57,14 @@ _osx_support: 3.0- _posixsubprocess: 3.2- _py_abc: 3.7- _pydecimal: 3.5- +_queue: 3.7- _random: 3.0- _sitebuiltins: 3.4- _socket: 3.0- # present in 3.0 at runtime, but not in typeshed _sqlite3: 3.0- _ssl: 3.0- _stat: 3.4- +_struct: 3.0- _thread: 3.0- _threading_local: 3.0- _tkinter: 3.0- diff --git a/mypy/typeshed/stdlib/_bz2.pyi b/mypy/typeshed/stdlib/_bz2.pyi new file mode 100644 index 000000000000..4ba26fe96be0 --- /dev/null +++ b/mypy/typeshed/stdlib/_bz2.pyi @@ -0,0 +1,18 @@ +from _typeshed import ReadableBuffer +from typing import final + +@final +class BZ2Compressor: + def __init__(self, compresslevel: int = 9) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self) -> bytes: ... + +@final +class BZ2Decompressor: + def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... + @property + def eof(self) -> bool: ... + @property + def needs_input(self) -> bool: ... + @property + def unused_data(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi new file mode 100644 index 000000000000..2e21a8c5d017 --- /dev/null +++ b/mypy/typeshed/stdlib/_contextvars.pyi @@ -0,0 +1,61 @@ +import sys +from collections.abc import Callable, Iterator, Mapping +from typing import Any, ClassVar, Generic, TypeVar, final, overload +from typing_extensions import ParamSpec + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") +_D = TypeVar("_D") +_P = ParamSpec("_P") + +@final +class ContextVar(Generic[_T]): + @overload + def __init__(self, name: str) -> None: ... + @overload + def __init__(self, name: str, *, default: _T) -> None: ... + def __hash__(self) -> int: ... + @property + def name(self) -> str: ... + @overload + def get(self) -> _T: ... + @overload + def get(self, default: _T, /) -> _T: ... + @overload + def get(self, default: _D, /) -> _D | _T: ... + def set(self, value: _T, /) -> Token[_T]: ... + def reset(self, token: Token[_T], /) -> None: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +@final +class Token(Generic[_T]): + @property + def var(self) -> ContextVar[_T]: ... + @property + def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express + MISSING: ClassVar[object] + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +def copy_context() -> Context: ... + +# It doesn't make sense to make this generic, because for most Contexts each ContextVar will have +# a different value. +@final +class Context(Mapping[ContextVar[Any], Any]): + def __init__(self) -> None: ... + @overload + def get(self, key: ContextVar[_T], default: None = None, /) -> _T | None: ... + @overload + def get(self, key: ContextVar[_T], default: _T, /) -> _T: ... + @overload + def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... + def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... + def copy(self) -> Context: ... + def __getitem__(self, key: ContextVar[_T], /) -> _T: ... + def __iter__(self) -> Iterator[ContextVar[Any]]: ... + def __len__(self) -> int: ... + def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/_dbm.pyi b/mypy/typeshed/stdlib/_dbm.pyi new file mode 100644 index 000000000000..4113a7e3ffb9 --- /dev/null +++ b/mypy/typeshed/stdlib/_dbm.pyi @@ -0,0 +1,43 @@ +import sys +from _typeshed import ReadOnlyBuffer, StrOrBytesPath +from types import TracebackType +from typing import TypeVar, overload +from typing_extensions import Self, TypeAlias + +if sys.platform != "win32": + _T = TypeVar("_T") + _KeyType: TypeAlias = str | ReadOnlyBuffer + _ValueType: TypeAlias = str | ReadOnlyBuffer + + class error(OSError): ... + library: str + + # Actual typename dbm, not exposed by the implementation + class _dbm: + def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + + def __getitem__(self, item: _KeyType) -> bytes: ... + def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... + def __delitem__(self, key: _KeyType) -> None: ... + def __len__(self) -> int: ... + def __del__(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + @overload + def get(self, k: _KeyType) -> bytes | None: ... + @overload + def get(self, k: _KeyType, default: _T) -> bytes | _T: ... + def keys(self) -> list[bytes]: ... + def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... + # Don't exist at runtime + __new__: None # type: ignore[assignment] + __init__: None # type: ignore[assignment] + + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... diff --git a/mypy/typeshed/stdlib/_gdbm.pyi b/mypy/typeshed/stdlib/_gdbm.pyi new file mode 100644 index 000000000000..1d1d541f5477 --- /dev/null +++ b/mypy/typeshed/stdlib/_gdbm.pyi @@ -0,0 +1,47 @@ +import sys +from _typeshed import ReadOnlyBuffer, StrOrBytesPath +from types import TracebackType +from typing import TypeVar, overload +from typing_extensions import Self, TypeAlias + +if sys.platform != "win32": + _T = TypeVar("_T") + _KeyType: TypeAlias = str | ReadOnlyBuffer + _ValueType: TypeAlias = str | ReadOnlyBuffer + + open_flags: str + + class error(OSError): ... + # Actual typename gdbm, not exposed by the implementation + class _gdbm: + def firstkey(self) -> bytes | None: ... + def nextkey(self, key: _KeyType) -> bytes | None: ... + def reorganize(self) -> None: ... + def sync(self) -> None: ... + def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + + def __getitem__(self, item: _KeyType) -> bytes: ... + def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... + def __delitem__(self, key: _KeyType) -> None: ... + def __contains__(self, key: _KeyType) -> bool: ... + def __len__(self) -> int: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + @overload + def get(self, k: _KeyType) -> bytes | None: ... + @overload + def get(self, k: _KeyType, default: _T) -> bytes | _T: ... + def keys(self) -> list[bytes]: ... + def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... + # Don't exist at runtime + __new__: None # type: ignore[assignment] + __init__: None # type: ignore[assignment] + + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... diff --git a/mypy/typeshed/stdlib/_lzma.pyi b/mypy/typeshed/stdlib/_lzma.pyi new file mode 100644 index 000000000000..1f5be78876c6 --- /dev/null +++ b/mypy/typeshed/stdlib/_lzma.pyi @@ -0,0 +1,60 @@ +from _typeshed import ReadableBuffer +from collections.abc import Mapping, Sequence +from typing import Any, Final, final +from typing_extensions import TypeAlias + +_FilterChain: TypeAlias = Sequence[Mapping[str, Any]] + +FORMAT_AUTO: Final = 0 +FORMAT_XZ: Final = 1 +FORMAT_ALONE: Final = 2 +FORMAT_RAW: Final = 3 +CHECK_NONE: Final = 0 +CHECK_CRC32: Final = 1 +CHECK_CRC64: Final = 4 +CHECK_SHA256: Final = 10 +CHECK_ID_MAX: Final = 15 +CHECK_UNKNOWN: Final = 16 +FILTER_LZMA1: int # v big number +FILTER_LZMA2: Final = 33 +FILTER_DELTA: Final = 3 +FILTER_X86: Final = 4 +FILTER_IA64: Final = 6 +FILTER_ARM: Final = 7 +FILTER_ARMTHUMB: Final = 8 +FILTER_SPARC: Final = 9 +FILTER_POWERPC: Final = 5 +MF_HC3: Final = 3 +MF_HC4: Final = 4 +MF_BT2: Final = 18 +MF_BT3: Final = 19 +MF_BT4: Final = 20 +MODE_FAST: Final = 1 +MODE_NORMAL: Final = 2 +PRESET_DEFAULT: Final = 6 +PRESET_EXTREME: int # v big number + +@final +class LZMADecompressor: + def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... + def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... + @property + def check(self) -> int: ... + @property + def eof(self) -> bool: ... + @property + def unused_data(self) -> bytes: ... + @property + def needs_input(self) -> bool: ... + +@final +class LZMACompressor: + def __init__( + self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... + ) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self) -> bytes: ... + +class LZMAError(Exception): ... + +def is_check_supported(check_id: int, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/_queue.pyi b/mypy/typeshed/stdlib/_queue.pyi new file mode 100644 index 000000000000..0d4caea7442e --- /dev/null +++ b/mypy/typeshed/stdlib/_queue.pyi @@ -0,0 +1,20 @@ +import sys +from typing import Any, Generic, TypeVar + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") + +class Empty(Exception): ... + +class SimpleQueue(Generic[_T]): + def __init__(self) -> None: ... + def empty(self) -> bool: ... + def get(self, block: bool = True, timeout: float | None = None) -> _T: ... + def get_nowait(self) -> _T: ... + def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ... + def put_nowait(self, item: _T) -> None: ... + def qsize(self) -> int: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/_struct.pyi b/mypy/typeshed/stdlib/_struct.pyi new file mode 100644 index 000000000000..662170e869f3 --- /dev/null +++ b/mypy/typeshed/stdlib/_struct.pyi @@ -0,0 +1,22 @@ +from _typeshed import ReadableBuffer, WriteableBuffer +from collections.abc import Iterator +from typing import Any + +def pack(fmt: str | bytes, /, *v: Any) -> bytes: ... +def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ... +def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... +def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... +def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... +def calcsize(format: str | bytes, /) -> int: ... + +class Struct: + @property + def format(self) -> str: ... + @property + def size(self) -> int: ... + def __init__(self, format: str | bytes) -> None: ... + def pack(self, *v: Any) -> bytes: ... + def pack_into(self, buffer: WriteableBuffer, offset: int, *v: Any) -> None: ... + def unpack(self, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... + def unpack_from(self, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... + def iter_unpack(self, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 371de9821339..a45cc13f10e3 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1186,9 +1186,7 @@ class property: @final class _NotImplementedType(Any): - # A little weird, but typing the __call__ as NotImplemented makes the error message - # for NotImplemented() much better - __call__: NotImplemented # type: ignore[valid-type] # pyright: ignore[reportInvalidTypeForm] + __call__: None NotImplemented: _NotImplementedType @@ -1819,7 +1817,7 @@ class StopIteration(Exception): class OSError(Exception): errno: int | None - strerror: str + strerror: str | None # filename, filename2 are actually str | bytes | None filename: Any filename2: Any diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index cd11baa1a0c1..ee5587031f2a 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -1,9 +1,10 @@ import _compression import sys +from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Iterable -from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, final, overload +from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, overload from typing_extensions import Self, TypeAlias __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"] @@ -128,19 +129,3 @@ class BZ2File(BaseStream, IO[bytes]): def seek(self, offset: int, whence: int = 0) -> int: ... def write(self, data: ReadableBuffer) -> int: ... def writelines(self, seq: Iterable[ReadableBuffer]) -> None: ... - -@final -class BZ2Compressor: - def __init__(self, compresslevel: int = 9) -> None: ... - def compress(self, data: ReadableBuffer, /) -> bytes: ... - def flush(self) -> bytes: ... - -@final -class BZ2Decompressor: - def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... - @property - def eof(self) -> bool: ... - @property - def needs_input(self) -> bool: ... - @property - def unused_data(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index dd5ea0acbe2c..22dc33006e9d 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -1,63 +1,3 @@ -import sys -from collections.abc import Callable, Iterator, Mapping -from typing import Any, ClassVar, Generic, TypeVar, final, overload -from typing_extensions import ParamSpec - -if sys.version_info >= (3, 9): - from types import GenericAlias +from _contextvars import Context as Context, ContextVar as ContextVar, Token as Token, copy_context as copy_context __all__ = ("Context", "ContextVar", "Token", "copy_context") - -_T = TypeVar("_T") -_D = TypeVar("_D") -_P = ParamSpec("_P") - -@final -class ContextVar(Generic[_T]): - @overload - def __init__(self, name: str) -> None: ... - @overload - def __init__(self, name: str, *, default: _T) -> None: ... - def __hash__(self) -> int: ... - @property - def name(self) -> str: ... - @overload - def get(self) -> _T: ... - @overload - def get(self, default: _T, /) -> _T: ... - @overload - def get(self, default: _D, /) -> _D | _T: ... - def set(self, value: _T, /) -> Token[_T]: ... - def reset(self, token: Token[_T], /) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -@final -class Token(Generic[_T]): - @property - def var(self) -> ContextVar[_T]: ... - @property - def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express - MISSING: ClassVar[object] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -def copy_context() -> Context: ... - -# It doesn't make sense to make this generic, because for most Contexts each ContextVar will have -# a different value. -@final -class Context(Mapping[ContextVar[Any], Any]): - def __init__(self) -> None: ... - @overload - def get(self, key: ContextVar[_T], default: None = None, /) -> _T | None: ... - @overload - def get(self, key: ContextVar[_T], default: _T, /) -> _T: ... - @overload - def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... - def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... - def copy(self) -> Context: ... - def __getitem__(self, key: ContextVar[_T], /) -> _T: ... - def __iter__(self) -> Iterator[ContextVar[Any]]: ... - def __len__(self) -> int: ... - def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index 1d1d541f5477..2dac3d12b0ca 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -1,47 +1 @@ -import sys -from _typeshed import ReadOnlyBuffer, StrOrBytesPath -from types import TracebackType -from typing import TypeVar, overload -from typing_extensions import Self, TypeAlias - -if sys.platform != "win32": - _T = TypeVar("_T") - _KeyType: TypeAlias = str | ReadOnlyBuffer - _ValueType: TypeAlias = str | ReadOnlyBuffer - - open_flags: str - - class error(OSError): ... - # Actual typename gdbm, not exposed by the implementation - class _gdbm: - def firstkey(self) -> bytes | None: ... - def nextkey(self, key: _KeyType) -> bytes | None: ... - def reorganize(self) -> None: ... - def sync(self) -> None: ... - def close(self) -> None: ... - if sys.version_info >= (3, 13): - def clear(self) -> None: ... - - def __getitem__(self, item: _KeyType) -> bytes: ... - def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... - def __delitem__(self, key: _KeyType) -> None: ... - def __contains__(self, key: _KeyType) -> bool: ... - def __len__(self) -> int: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - @overload - def get(self, k: _KeyType) -> bytes | None: ... - @overload - def get(self, k: _KeyType, default: _T) -> bytes | _T: ... - def keys(self) -> list[bytes]: ... - def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... - # Don't exist at runtime - __new__: None # type: ignore[assignment] - __init__: None # type: ignore[assignment] - - if sys.version_info >= (3, 11): - def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... - else: - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... +from _gdbm import * diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 4113a7e3ffb9..66c943ab640b 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -1,43 +1 @@ -import sys -from _typeshed import ReadOnlyBuffer, StrOrBytesPath -from types import TracebackType -from typing import TypeVar, overload -from typing_extensions import Self, TypeAlias - -if sys.platform != "win32": - _T = TypeVar("_T") - _KeyType: TypeAlias = str | ReadOnlyBuffer - _ValueType: TypeAlias = str | ReadOnlyBuffer - - class error(OSError): ... - library: str - - # Actual typename dbm, not exposed by the implementation - class _dbm: - def close(self) -> None: ... - if sys.version_info >= (3, 13): - def clear(self) -> None: ... - - def __getitem__(self, item: _KeyType) -> bytes: ... - def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... - def __delitem__(self, key: _KeyType) -> None: ... - def __len__(self) -> int: ... - def __del__(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - @overload - def get(self, k: _KeyType) -> bytes | None: ... - @overload - def get(self, k: _KeyType, default: _T) -> bytes | _T: ... - def keys(self) -> list[bytes]: ... - def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... - # Don't exist at runtime - __new__: None # type: ignore[assignment] - __init__: None # type: ignore[assignment] - - if sys.version_info >= (3, 11): - def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... - else: - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... +from _dbm import * diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 086aff50344c..fbcfa868cc1b 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -2,7 +2,7 @@ import sys from collections.abc import Callable from decimal import Decimal from numbers import Integral, Rational, Real -from typing import Any, Literal, SupportsIndex, overload +from typing import Any, Literal, Protocol, SupportsIndex, overload from typing_extensions import Self, TypeAlias _ComparableNum: TypeAlias = int | float | Decimal | Real @@ -20,11 +20,19 @@ else: @overload def gcd(a: Integral, b: Integral) -> Integral: ... +class _ConvertibleToIntegerRatio(Protocol): + def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ... + class Fraction(Rational): @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload def __new__(cls, value: float | Decimal | str, /) -> Self: ... + + if sys.version_info >= (3, 14): + @overload + def __new__(cls, value: _ConvertibleToIntegerRatio) -> Self: ... + @classmethod def from_float(cls, f: float) -> Self: ... @classmethod diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index af57f36715be..5c444e66c4c7 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import StrOrBytesPath -from collections.abc import Callable, Hashable, Iterable, Sequence +from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence from configparser import RawConfigParser from re import Pattern from threading import Thread @@ -63,7 +63,7 @@ def dictConfig(config: _DictConfigArgs | dict[str, Any]) -> None: ... if sys.version_info >= (3, 10): def fileConfig( fname: StrOrBytesPath | IO[str] | RawConfigParser, - defaults: dict[str, str] | None = None, + defaults: Mapping[str, str] | None = None, disable_existing_loggers: bool = True, encoding: str | None = None, ) -> None: ... @@ -71,7 +71,7 @@ if sys.version_info >= (3, 10): else: def fileConfig( fname: StrOrBytesPath | IO[str] | RawConfigParser, - defaults: dict[str, str] | None = None, + defaults: Mapping[str, str] | None = None, disable_existing_loggers: bool = True, ) -> None: ... diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index 91f9fe57e46f..d594d6569a7e 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -260,6 +260,8 @@ class QueueHandler(Handler): def __init__(self, queue: _QueueLike[Any]) -> None: ... def prepare(self, record: LogRecord) -> Any: ... def enqueue(self, record: LogRecord) -> None: ... + if sys.version_info >= (3, 12): + listener: QueueListener | None class QueueListener: handlers: tuple[Handler, ...] # undocumented diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index 2df2b9a8bd6a..2f0279f5986b 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,7 +1,41 @@ from _compression import BaseStream +from _lzma import ( + CHECK_CRC32 as CHECK_CRC32, + CHECK_CRC64 as CHECK_CRC64, + CHECK_ID_MAX as CHECK_ID_MAX, + CHECK_NONE as CHECK_NONE, + CHECK_SHA256 as CHECK_SHA256, + CHECK_UNKNOWN as CHECK_UNKNOWN, + FILTER_ARM as FILTER_ARM, + FILTER_ARMTHUMB as FILTER_ARMTHUMB, + FILTER_DELTA as FILTER_DELTA, + FILTER_IA64 as FILTER_IA64, + FILTER_LZMA1 as FILTER_LZMA1, + FILTER_LZMA2 as FILTER_LZMA2, + FILTER_POWERPC as FILTER_POWERPC, + FILTER_SPARC as FILTER_SPARC, + FILTER_X86 as FILTER_X86, + FORMAT_ALONE as FORMAT_ALONE, + FORMAT_AUTO as FORMAT_AUTO, + FORMAT_RAW as FORMAT_RAW, + FORMAT_XZ as FORMAT_XZ, + MF_BT2 as MF_BT2, + MF_BT3 as MF_BT3, + MF_BT4 as MF_BT4, + MF_HC3 as MF_HC3, + MF_HC4 as MF_HC4, + MODE_FAST as MODE_FAST, + MODE_NORMAL as MODE_NORMAL, + PRESET_DEFAULT as PRESET_DEFAULT, + PRESET_EXTREME as PRESET_EXTREME, + LZMACompressor as LZMACompressor, + LZMADecompressor as LZMADecompressor, + LZMAError as LZMAError, + _FilterChain, + is_check_supported as is_check_supported, +) from _typeshed import ReadableBuffer, StrOrBytesPath -from collections.abc import Mapping, Sequence -from typing import IO, Any, Final, Literal, TextIO, final, overload +from typing import IO, Literal, TextIO, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -48,62 +82,6 @@ _OpenTextWritingMode: TypeAlias = Literal["wt", "xt", "at"] _PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes] -_FilterChain: TypeAlias = Sequence[Mapping[str, Any]] - -FORMAT_AUTO: Final = 0 -FORMAT_XZ: Final = 1 -FORMAT_ALONE: Final = 2 -FORMAT_RAW: Final = 3 -CHECK_NONE: Final = 0 -CHECK_CRC32: Final = 1 -CHECK_CRC64: Final = 4 -CHECK_SHA256: Final = 10 -CHECK_ID_MAX: Final = 15 -CHECK_UNKNOWN: Final = 16 -FILTER_LZMA1: int # v big number -FILTER_LZMA2: Final = 33 -FILTER_DELTA: Final = 3 -FILTER_X86: Final = 4 -FILTER_IA64: Final = 6 -FILTER_ARM: Final = 7 -FILTER_ARMTHUMB: Final = 8 -FILTER_SPARC: Final = 9 -FILTER_POWERPC: Final = 5 -MF_HC3: Final = 3 -MF_HC4: Final = 4 -MF_BT2: Final = 18 -MF_BT3: Final = 19 -MF_BT4: Final = 20 -MODE_FAST: Final = 1 -MODE_NORMAL: Final = 2 -PRESET_DEFAULT: Final = 6 -PRESET_EXTREME: int # v big number - -# from _lzma.c -@final -class LZMADecompressor: - def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... - def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... - @property - def check(self) -> int: ... - @property - def eof(self) -> bool: ... - @property - def unused_data(self) -> bytes: ... - @property - def needs_input(self) -> bool: ... - -# from _lzma.c -@final -class LZMACompressor: - def __init__( - self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... - ) -> None: ... - def compress(self, data: ReadableBuffer, /) -> bytes: ... - def flush(self) -> bytes: ... - -class LZMAError(Exception): ... - class LZMAFile(BaseStream, IO[bytes]): # type: ignore[misc] # incompatible definitions of writelines in the base classes def __init__( self, @@ -194,4 +172,3 @@ def compress( def decompress( data: ReadableBuffer, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None ) -> bytes: ... -def is_check_supported(check_id: int, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 5be7f7b76ba1..f94e876237d1 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -34,9 +34,22 @@ class mmap: if sys.platform == "win32": def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ... else: - def __init__( - self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + fileno: int, + length: int, + flags: int = ..., + prot: int = ..., + access: int = ..., + offset: int = ..., + *, + trackfd: bool = True, + ) -> None: ... + else: + def __init__( + self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... + ) -> None: ... def close(self) -> None: ... def flush(self, offset: int = ..., size: int = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 8c65eccad07a..1669c5f09f97 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -61,31 +61,58 @@ class ValueProxy(BaseProxy, Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): - __builtins__: ClassVar[dict[str, Any]] - def __len__(self) -> int: ... - def __getitem__(self, key: _KT, /) -> _VT: ... - def __setitem__(self, key: _KT, value: _VT, /) -> None: ... - def __delitem__(self, key: _KT, /) -> None: ... - def __iter__(self) -> Iterator[_KT]: ... - def copy(self) -> dict[_KT, _VT]: ... - @overload # type: ignore[override] - def get(self, key: _KT, /) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT, /) -> _VT: ... - @overload - def get(self, key: _KT, default: _T, /) -> _VT | _T: ... - @overload - def pop(self, key: _KT, /) -> _VT: ... - @overload - def pop(self, key: _KT, default: _VT, /) -> _VT: ... - @overload - def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... - def keys(self) -> list[_KT]: ... # type: ignore[override] - def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] - def values(self) -> list[_VT]: ... # type: ignore[override] - if sys.version_info >= (3, 13): - def __class_getitem__(cls, args: Any, /) -> Any: ... +if sys.version_info >= (3, 13): + class _BaseDictProxy(BaseProxy, MutableMapping[_KT, _VT]): + __builtins__: ClassVar[dict[str, Any]] + def __len__(self) -> int: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... + def __iter__(self) -> Iterator[_KT]: ... + def copy(self) -> dict[_KT, _VT]: ... + @overload # type: ignore[override] + def get(self, key: _KT, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + @overload + def pop(self, key: _KT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... + def keys(self) -> list[_KT]: ... # type: ignore[override] + def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> list[_VT]: ... # type: ignore[override] + + class DictProxy(_BaseDictProxy[_KT, _VT]): + def __class_getitem__(cls, args: Any, /) -> GenericAlias: ... + +else: + class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): + __builtins__: ClassVar[dict[str, Any]] + def __len__(self) -> int: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... + def __iter__(self) -> Iterator[_KT]: ... + def copy(self) -> dict[_KT, _VT]: ... + @overload # type: ignore[override] + def get(self, key: _KT, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + @overload + def pop(self, key: _KT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... + def keys(self) -> list[_KT]: ... # type: ignore[override] + def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> list[_VT]: ... # type: ignore[override] class BaseListProxy(BaseProxy, MutableSequence[_T]): __builtins__: ClassVar[dict[str, Any]] diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi index 16643c99d08d..4fb49cb6102b 100644 --- a/mypy/typeshed/stdlib/queue.pyi +++ b/mypy/typeshed/stdlib/queue.pyi @@ -1,4 +1,5 @@ import sys +from _queue import Empty as Empty, SimpleQueue as SimpleQueue from threading import Condition, Lock from typing import Any, Generic, TypeVar @@ -11,7 +12,6 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") -class Empty(Exception): ... class Full(Exception): ... if sys.version_info >= (3, 13): @@ -55,14 +55,3 @@ class PriorityQueue(Queue[_T]): class LifoQueue(Queue[_T]): queue: list[_T] - -class SimpleQueue(Generic[_T]): - def __init__(self) -> None: ... - def empty(self) -> bool: ... - def get(self, block: bool = True, timeout: float | None = None) -> _T: ... - def get_nowait(self) -> _T: ... - def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ... - def put_nowait(self, item: _T) -> None: ... - def qsize(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/struct.pyi b/mypy/typeshed/stdlib/struct.pyi index e684632489ea..2c26908746ec 100644 --- a/mypy/typeshed/stdlib/struct.pyi +++ b/mypy/typeshed/stdlib/struct.pyi @@ -1,26 +1,5 @@ -from _typeshed import ReadableBuffer, WriteableBuffer -from collections.abc import Iterator -from typing import Any +from _struct import * __all__ = ["calcsize", "pack", "pack_into", "unpack", "unpack_from", "iter_unpack", "Struct", "error"] class error(Exception): ... - -def pack(fmt: str | bytes, /, *v: Any) -> bytes: ... -def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ... -def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... -def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... -def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... -def calcsize(format: str | bytes, /) -> int: ... - -class Struct: - @property - def format(self) -> str: ... - @property - def size(self) -> int: ... - def __init__(self, format: str | bytes) -> None: ... - def pack(self, *v: Any) -> bytes: ... - def pack_into(self, buffer: WriteableBuffer, offset: int, *v: Any) -> None: ... - def unpack(self, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... - def unpack_from(self, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... - def iter_unpack(self, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... From 8ef21976cfd190d0b1974f438a7d30e8eaea5272 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:54:37 -0800 Subject: [PATCH 0905/1617] [mypyc] Loading type from imported modules. (#18158) Fixes [#1075](https://github.com/mypyc/mypyc/issues/1075) Previously, this compiled but failed to run: ```python import collections from typing_extensions import TypedDict class C(TypedDict): x: collections.deque ``` Although type of `x` was being inferred correctly, it was not looking for `collections.deque` in imported modules. --- mypyc/irbuild/function.py | 5 +++++ mypyc/test-data/run-classes.test | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index fcfe568fcd28..8678c1d89c58 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -806,6 +806,11 @@ def load_type(builder: IRBuilder, typ: TypeInfo, line: int) -> Value: elif typ.fullname in builtin_names: builtin_addr_type, src = builtin_names[typ.fullname] class_obj = builder.add(LoadAddress(builtin_addr_type, src, line)) + elif typ.module_name in builder.imports: + loaded_module = builder.load_module(typ.module_name) + class_obj = builder.builder.get_attr( + loaded_module, typ.name, object_rprimitive, line, borrow=False + ) else: class_obj = builder.load_global_str(typ.name, line) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 90d0521ffc37..6f58125a1113 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -76,6 +76,19 @@ assert hasattr(c, 'x') 1000000000000000000000000000001 1000000000000000000000000000002 +[case testTypedDictWithFields] +import collections +from typing_extensions import TypedDict +class C(TypedDict): + x: collections.deque +[file driver.py] +from native import C +from collections import deque + +print(C.__annotations__["x"] is deque) +[out] +True + [case testClassWithDeletableAttributes] from typing import Any, cast from testutil import assertRaises From 6759dbd5bbcca4501748dc52d93d5a50060a825a Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 19 Nov 2024 03:26:01 -0500 Subject: [PATCH 0906/1617] Infer generic type arguments for slice expressions (#18160) Fixes #18149 Slices were made generic in https://github.com/python/typeshed/pull/11637. Currently, all slice expressions are inferred to have type `slice[Any, Any, Any]`. This PR fills in the generic type arguments more appropriately using start/stop/stride expression types. Given ```python class Foo: def __getitem__[T](self, item: T) -> T: return item x = Foo() reveal_type(x[1:]) ``` Before: ```none main.py:5: note: Revealed type is "builtins.slice[Any, Any, Any]" ``` After: ```none main.py:5: note: Revealed type is "builtins.slice[builtins.int, None, None]" ``` --- mypy/checkexpr.py | 6 +++++- test-data/unit/check-expressions.test | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 577576a4e5f8..11a9cffe18c3 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5612,11 +5612,15 @@ def visit_slice_expr(self, e: SliceExpr) -> Type: except KeyError: supports_index = self.chk.named_type("builtins.int") # thanks, fixture life expected = make_optional_type(supports_index) + type_args = [] for index in [e.begin_index, e.end_index, e.stride]: if index: t = self.accept(index) self.chk.check_subtype(t, expected, index, message_registry.INVALID_SLICE_INDEX) - return self.named_type("builtins.slice") + type_args.append(t) + else: + type_args.append(NoneType()) + return self.chk.named_generic_type("builtins.slice", type_args) def visit_list_comprehension(self, e: ListComprehension) -> Type: return self.check_generator_or_comprehension( diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index d5ddc910bcd6..cd26c9bb408a 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1178,8 +1178,8 @@ class B: pass [case testSlicingWithInvalidBase] a: A -a[1:2] # E: Invalid index type "slice" for "A"; expected type "int" -a[:] # E: Invalid index type "slice" for "A"; expected type "int" +a[1:2] # E: Invalid index type "slice[int, int, None]" for "A"; expected type "int" +a[:] # E: Invalid index type "slice[None, None, None]" for "A"; expected type "int" class A: def __getitem__(self, n: int) -> 'A': pass [builtins fixtures/slice.pyi] From 11c58a7fdd0dff0a7ade65b8db56f0c7a77fed04 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 19 Nov 2024 04:09:27 -0500 Subject: [PATCH 0907/1617] Allow nesting of Annotated with TypedDict special forms inside TypedDicts (#18165) This is allowed per the [typing spec](https://typing.readthedocs.io/en/latest/spec/typeddict.html#interaction-with-other-special-types). Updates the TypeAnalyzer to remember whether TypedDict special forms are allowed when visiting `Annotated` types. --- mypy/typeanal.py | 4 +++- test-data/unit/check-typeddict.test | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 0c241f5c0f99..74f649ed69b1 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -688,7 +688,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ code=codes.VALID_TYPE, ) return AnyType(TypeOfAny.from_error) - return self.anal_type(t.args[0]) + return self.anal_type( + t.args[0], allow_typed_dict_special_forms=self.allow_typed_dict_special_forms + ) elif fullname in ("typing_extensions.Required", "typing.Required"): if not self.allow_typed_dict_special_forms: self.fail( diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index affa472bb640..c62061f75521 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3989,6 +3989,16 @@ class TP(TypedDict): [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictAnnotatedWithSpecialForms] +from typing import NotRequired, ReadOnly, Required, TypedDict +from typing_extensions import Annotated + +class A(TypedDict): + a: Annotated[NotRequired[ReadOnly[int]], ""] # ok + b: NotRequired[ReadOnly[Annotated[int, ""]]] # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testTypedDictReadOnlyCovariant] from typing import ReadOnly, TypedDict, Union From 87998c8674d4addfa632914770312b8647cbc392 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 19 Nov 2024 04:11:23 -0500 Subject: [PATCH 0908/1617] Allow TypedDict assignment of Required item to NotRequired ReadOnly item (#18164) Fixes #18162 --- mypy/subtypes.py | 14 ++++++-------- test-data/unit/check-typeddict.test | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 11f3421331a5..a26aaf798b58 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -910,14 +910,12 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: return False # Non-required key is not compatible with a required key since # indexing may fail unexpectedly if a required key is missing. - # Required key is not compatible with a non-required key since - # the prior doesn't support 'del' but the latter should support - # it. - # - # NOTE: 'del' support is currently not implemented (#3550). We - # don't want to have to change subtyping after 'del' support - # lands so here we are anticipating that change. - if (name in left.required_keys) != (name in right.required_keys): + # Required key is not compatible with a non-read-only non-required + # key since the prior doesn't support 'del' but the latter should + # support it. + # Required key is compatible with a read-only non-required key. + required_differ = (name in left.required_keys) != (name in right.required_keys) + if not right_readonly and required_differ: return False # Readonly fields check: # diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index c62061f75521..a30fec1b9422 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3894,6 +3894,20 @@ accepts_B(b) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictRequiredConsistentWithNotRequiredReadOnly] +from typing import NotRequired, ReadOnly, Required, TypedDict + +class A(TypedDict): + x: NotRequired[ReadOnly[str]] + +class B(TypedDict): + x: Required[str] + +def f(b: B): + a: A = b # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testTypedDictReadOnlyCall] from typing import ReadOnly, TypedDict From e2a47e249f1ac3d2cef05b9c19bbd2bb4b2d1c17 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Tue, 19 Nov 2024 10:11:38 +0100 Subject: [PATCH 0909/1617] Support `==`-based narrowing of Optional (#18163) Closes #18135 This change implements the third approach mentioned in #18135, which is stricter than similar narrowings, as clarified by the new/modified code comments. Personally, I prefer this more stringent way but could also switch this PR to approach two if there is consent that convenience is more important than type safety here. --- mypy/checker.py | 37 +++++++++++++++++------------ test-data/unit/check-narrowing.test | 4 ++-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1bee348bc252..ef3f7502d7ce 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6274,10 +6274,6 @@ def has_no_custom_eq_checks(t: Type) -> bool: coerce_only_in_literal_context, ) - # Strictly speaking, we should also skip this check if the objects in the expr - # chain have custom __eq__ or __ne__ methods. But we (maybe optimistically) - # assume nobody would actually create a custom objects that considers itself - # equal to None. if if_map == {} and else_map == {}: if_map, else_map = self.refine_away_none_in_comparison( operands, operand_types, expr_indices, narrowable_operand_index_to_hash.keys() @@ -6602,25 +6598,36 @@ def refine_away_none_in_comparison( For more details about what the different arguments mean, see the docstring of 'refine_identity_comparison_expression' up above. """ + non_optional_types = [] for i in chain_indices: typ = operand_types[i] if not is_overlapping_none(typ): non_optional_types.append(typ) - # Make sure we have a mixture of optional and non-optional types. - if len(non_optional_types) == 0 or len(non_optional_types) == len(chain_indices): - return {}, {} + if_map, else_map = {}, {} - if_map = {} - for i in narrowable_operand_indices: - expr_type = operand_types[i] - if not is_overlapping_none(expr_type): - continue - if any(is_overlapping_erased_types(expr_type, t) for t in non_optional_types): - if_map[operands[i]] = remove_optional(expr_type) + if not non_optional_types or (len(non_optional_types) != len(chain_indices)): - return if_map, {} + # Narrow e.g. `Optional[A] == "x"` or `Optional[A] is "x"` to `A` (which may be + # convenient but is strictly not type-safe): + for i in narrowable_operand_indices: + expr_type = operand_types[i] + if not is_overlapping_none(expr_type): + continue + if any(is_overlapping_erased_types(expr_type, t) for t in non_optional_types): + if_map[operands[i]] = remove_optional(expr_type) + + # Narrow e.g. `Optional[A] != None` to `A` (which is stricter than the above step and + # so type-safe but less convenient, because e.g. `Optional[A] == None` still results + # in `Optional[A]`): + if any(isinstance(get_proper_type(ot), NoneType) for ot in operand_types): + for i in narrowable_operand_indices: + expr_type = operand_types[i] + if is_overlapping_none(expr_type): + else_map[operands[i]] = remove_optional(expr_type) + + return if_map, else_map def is_len_of_tuple(self, expr: Expression) -> bool: """Is this expression a `len(x)` call where x is a tuple or union of tuples?""" diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index d740708991d0..bc763095477e 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1385,9 +1385,9 @@ val: Optional[A] if val == None: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" else: - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "__main__.A" if val != None: - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "__main__.A" else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" From 21587f01045246a9ecb54a054a5ba03e20cbbd19 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:36:09 +0100 Subject: [PATCH 0910/1617] Move long_description metadata to pyproject.toml (#18172) Fixes #18168 --- pyproject.toml | 13 ++++++++++++- setup.py | 16 +--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9f9766a75182..1a7adf21c0a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,17 @@ build-backend = "setuptools.build_meta" [project] name = "mypy" description = "Optional static typing for Python" +readme = {text = """ +Mypy -- Optional Static Typing for Python +========================================= + +Add type annotations to your Python programs, and use mypy to type +check them. Mypy is essentially a Python linter on steroids, and it +can catch many programming errors by analyzing your program, without +actually having to run it. Mypy has a powerful type system with +features such as type inference, gradual typing, generics and union +types. +""", content-type = "text/x-rst"} authors = [{name = "Jukka Lehtosalo", email = "jukka.lehtosalo@iki.fi"}] license = {text = "MIT"} classifiers = [ @@ -41,7 +52,7 @@ dependencies = [ "mypy_extensions>=1.0.0", "tomli>=1.1.0; python_version<'3.11'", ] -dynamic = ["version", "readme"] +dynamic = ["version"] [project.optional-dependencies] dmypy = ["psutil>=4.0"] diff --git a/setup.py b/setup.py index 7b1c26c6c51a..180faf6d8ded 100644 --- a/setup.py +++ b/setup.py @@ -26,18 +26,6 @@ if TYPE_CHECKING: from typing_extensions import TypeGuard -long_description = """ -Mypy -- Optional Static Typing for Python -========================================= - -Add type annotations to your Python programs, and use mypy to type -check them. Mypy is essentially a Python linter on steroids, and it -can catch many programming errors by analyzing your program, without -actually having to run it. Mypy has a powerful type system with -features such as type inference, gradual typing, generics and union -types. -""".lstrip() - def is_list_of_setuptools_extension(items: list[Any]) -> TypeGuard[list[Extension]]: return all(isinstance(item, Extension) for item in items) @@ -171,6 +159,4 @@ def run(self): ext_modules = [] -setup( - version=version, long_description=long_description, ext_modules=ext_modules, cmdclass=cmdclass -) +setup(version=version, ext_modules=ext_modules, cmdclass=cmdclass) From eec80711d0c16a272cf572f94806057d34837d11 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Wed, 20 Nov 2024 17:22:15 -0500 Subject: [PATCH 0911/1617] Disallow bare `ParamSpec` in type aliases (#18174) Emit errors for both of the following aliases: ```python P = ParamSpec("P") Bad1: TypeAlias = P Bad2: TypeAlias = Concatenate[int, P] ``` This is done by swapping a top-level call to `type.accept(analyzer)` with `analyzer.anal_type(type)`, the latter of which simply calls the former and then performs some checks. --- mypy/typeanal.py | 2 +- test-data/unit/check-parameter-specification.test | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 74f649ed69b1..daf7ab1951ea 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -180,7 +180,7 @@ def analyze_type_alias( ) analyzer.in_dynamic_func = in_dynamic_func analyzer.global_scope = global_scope - res = type.accept(analyzer) + res = analyzer.anal_type(type, nested=False) return res, analyzer.aliases_used diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 674e3894940b..7f038b811741 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1452,6 +1452,17 @@ y: C[int, str] reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str) -> builtins.int" [builtins fixtures/paramspec.pyi] +[case testParamSpecInTypeAliasIllegalBare] +from typing import ParamSpec +from typing_extensions import Concatenate, TypeAlias + +P = ParamSpec("P") +Bad1: TypeAlias = P # E: Invalid location for ParamSpec "P" \ + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +Bad2: TypeAlias = Concatenate[int, P] # E: Invalid location for Concatenate \ + # N: You can use Concatenate as the first argument to Callable +[builtins fixtures/paramspec.pyi] + [case testParamSpecInTypeAliasRecursive] from typing import ParamSpec, Callable, Union From e840275ff53092aee06bf3872118c6b558d2decb Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 21 Nov 2024 06:45:30 -0800 Subject: [PATCH 0912/1617] Special case types.DynamicClassAttribute as property-like (#18150) This enables typeshed to define types.DynamicClassAttribute as a different class from builtins.property without breakage. Would enable https://github.com/python/typeshed/pull/12762 see also https://github.com/python/mypy/pull/14133 --- mypy/semanal.py | 1 + test-data/unit/pythoneval.test | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 59e4594353f0..9d2d6f5fa484 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1684,6 +1684,7 @@ def visit_decorator(self, dec: Decorator) -> None: "abc.abstractproperty", "functools.cached_property", "enum.property", + "types.DynamicClassAttribute", ), ): removed.append(i) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 4942a5fd5f2f..70003545754c 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2163,3 +2163,21 @@ class C3[T1, T2](tuple[T1, ...]): def func3(p: C3[int, object]): x: C3[int, int] = p + + +[case testDynamicClassAttribute] +# Some things that can break if DynamicClassAttribute isn't handled properly +from types import DynamicClassAttribute +from enum import Enum + +class TestClass: + @DynamicClassAttribute + def name(self) -> str: ... + +class TestClass2(TestClass, Enum): ... + +class Status(Enum): + ABORTED = -1 + +def imperfect(status: Status) -> str: + return status.name.lower() From 08340c29a939a203868d3abd5cfd997720228f32 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:46:50 +0100 Subject: [PATCH 0913/1617] Do not consider bare TypeVar not overlapping with None for reachability analysis (#18138) Fixes #18126. Simply allowing such intersection was insufficient: existing binder logic widened the type to `T | None` after the `is None` check. This PR extends the binder logic to prevent constructing a union type when all conditional branches are reachable and contain no assignments: checking `if isinstance(something, Something)` does not change the type of `something` after the end of the `if` block. --- mypy/binder.py | 55 +++++++++++++++++------- mypy/checker.py | 27 ++++++------ test-data/unit/check-enum.test | 4 +- test-data/unit/check-isinstance.test | 17 ++++---- test-data/unit/check-narrowing.test | 19 ++++++++ test-data/unit/check-python310.test | 30 +++++++++++++ test-data/unit/check-type-promotion.test | 6 +-- 7 files changed, 116 insertions(+), 42 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 9d0a33b54bc2..52ae9774e6d4 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -2,7 +2,7 @@ from collections import defaultdict from contextlib import contextmanager -from typing import DefaultDict, Iterator, List, Optional, Tuple, Union, cast +from typing import DefaultDict, Iterator, List, NamedTuple, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values @@ -30,6 +30,11 @@ BindableExpression: _TypeAlias = Union[IndexExpr, MemberExpr, NameExpr] +class CurrentType(NamedTuple): + type: Type + from_assignment: bool + + class Frame: """A Frame represents a specific point in the execution of a program. It carries information about the current types of expressions at @@ -44,7 +49,7 @@ class Frame: def __init__(self, id: int, conditional_frame: bool = False) -> None: self.id = id - self.types: dict[Key, Type] = {} + self.types: dict[Key, CurrentType] = {} self.unreachable = False self.conditional_frame = conditional_frame self.suppress_unreachable_warnings = False @@ -132,10 +137,10 @@ def push_frame(self, conditional_frame: bool = False) -> Frame: self.options_on_return.append([]) return f - def _put(self, key: Key, type: Type, index: int = -1) -> None: - self.frames[index].types[key] = type + def _put(self, key: Key, type: Type, from_assignment: bool, index: int = -1) -> None: + self.frames[index].types[key] = CurrentType(type, from_assignment) - def _get(self, key: Key, index: int = -1) -> Type | None: + def _get(self, key: Key, index: int = -1) -> CurrentType | None: if index < 0: index += len(self.frames) for i in range(index, -1, -1): @@ -143,7 +148,7 @@ def _get(self, key: Key, index: int = -1) -> Type | None: return self.frames[i].types[key] return None - def put(self, expr: Expression, typ: Type) -> None: + def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> None: if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return if not literal(expr): @@ -153,7 +158,7 @@ def put(self, expr: Expression, typ: Type) -> None: if key not in self.declarations: self.declarations[key] = get_declaration(expr) self._add_dependencies(key) - self._put(key, typ) + self._put(key, typ, from_assignment) def unreachable(self) -> None: self.frames[-1].unreachable = True @@ -164,7 +169,10 @@ def suppress_unreachable_warnings(self) -> None: def get(self, expr: Expression) -> Type | None: key = literal_hash(expr) assert key is not None, "Internal error: binder tried to get non-literal" - return self._get(key) + found = self._get(key) + if found is None: + return None + return found.type def is_unreachable(self) -> bool: # TODO: Copy the value of unreachable into new frames to avoid @@ -193,7 +201,7 @@ def update_from_options(self, frames: list[Frame]) -> bool: If a key is declared as AnyType, only update it if all the options are the same. """ - + all_reachable = all(not f.unreachable for f in frames) frames = [f for f in frames if not f.unreachable] changed = False keys = {key for f in frames for key in f.types} @@ -207,17 +215,30 @@ def update_from_options(self, frames: list[Frame]) -> bool: # know anything about key in at least one possible frame. continue - type = resulting_values[0] - assert type is not None + if all_reachable and all( + x is not None and not x.from_assignment for x in resulting_values + ): + # Do not synthesize a new type if we encountered a conditional block + # (if, while or match-case) without assignments. + # See check-isinstance.test::testNoneCheckDoesNotMakeTypeVarOptional + # This is a safe assumption: the fact that we checked something with `is` + # or `isinstance` does not change the type of the value. + continue + + current_type = resulting_values[0] + assert current_type is not None + type = current_type.type declaration_type = get_proper_type(self.declarations.get(key)) if isinstance(declaration_type, AnyType): # At this point resulting values can't contain None, see continue above - if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]): + if not all( + t is not None and is_same_type(type, t.type) for t in resulting_values[1:] + ): type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type) else: for other in resulting_values[1:]: assert other is not None - type = join_simple(self.declarations[key], type, other) + type = join_simple(self.declarations[key], type, other.type) # Try simplifying resulting type for unions involving variadic tuples. # Technically, everything is still valid without this step, but if we do # not do this, this may create long unions after exiting an if check like: @@ -236,8 +257,8 @@ def update_from_options(self, frames: list[Frame]) -> bool: ) if simplified == self.declarations[key]: type = simplified - if current_value is None or not is_same_type(type, current_value): - self._put(key, type) + if current_value is None or not is_same_type(type, current_value[0]): + self._put(key, type, from_assignment=True) changed = True self.frames[-1].unreachable = not frames @@ -374,7 +395,9 @@ def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Ty key = literal_hash(expr) assert key is not None enclosers = [get_declaration(expr)] + [ - f.types[key] for f in self.frames if key in f.types and is_subtype(type, f.types[key]) + f.types[key].type + for f in self.frames + if key in f.types and is_subtype(type, f.types[key][0]) ] return enclosers[-1] diff --git a/mypy/checker.py b/mypy/checker.py index ef3f7502d7ce..1b11caf01f18 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4725,11 +4725,11 @@ def visit_if_stmt(self, s: IfStmt) -> None: # XXX Issue a warning if condition is always False? with self.binder.frame_context(can_skip=True, fall_through=2): - self.push_type_map(if_map) + self.push_type_map(if_map, from_assignment=False) self.accept(b) # XXX Issue a warning if condition is always True? - self.push_type_map(else_map) + self.push_type_map(else_map, from_assignment=False) with self.binder.frame_context(can_skip=False, fall_through=2): if s.else_body: @@ -5310,18 +5310,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None: if b.is_unreachable or isinstance( get_proper_type(pattern_type.type), UninhabitedType ): - self.push_type_map(None) + self.push_type_map(None, from_assignment=False) else_map: TypeMap = {} else: pattern_map, else_map = conditional_types_to_typemaps( named_subject, pattern_type.type, pattern_type.rest_type ) self.remove_capture_conflicts(pattern_type.captures, inferred_types) - self.push_type_map(pattern_map) + self.push_type_map(pattern_map, from_assignment=False) if pattern_map: for expr, typ in pattern_map.items(): - self.push_type_map(self._get_recursive_sub_patterns_map(expr, typ)) - self.push_type_map(pattern_type.captures) + self.push_type_map( + self._get_recursive_sub_patterns_map(expr, typ), + from_assignment=False, + ) + self.push_type_map(pattern_type.captures, from_assignment=False) if g is not None: with self.binder.frame_context(can_skip=False, fall_through=3): gt = get_proper_type(self.expr_checker.accept(g)) @@ -5347,11 +5350,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None: continue type_map[named_subject] = type_map[expr] - self.push_type_map(guard_map) + self.push_type_map(guard_map, from_assignment=False) self.accept(b) else: self.accept(b) - self.push_type_map(else_map) + self.push_type_map(else_map, from_assignment=False) # This is needed due to a quirk in frame_context. Without it types will stay narrowed # after the match. @@ -7372,12 +7375,12 @@ def iterable_item_type( def function_type(self, func: FuncBase) -> FunctionLike: return function_type(func, self.named_type("builtins.function")) - def push_type_map(self, type_map: TypeMap) -> None: + def push_type_map(self, type_map: TypeMap, *, from_assignment: bool = True) -> None: if type_map is None: self.binder.unreachable() else: for expr, type in type_map.items(): - self.binder.put(expr, type) + self.binder.put(expr, type, from_assignment=from_assignment) def infer_issubclass_maps(self, node: CallExpr, expr: Expression) -> tuple[TypeMap, TypeMap]: """Infer type restrictions for an expression in issubclass call.""" @@ -7750,9 +7753,7 @@ def conditional_types( ) and is_proper_subtype(current_type, proposed_type, ignore_promotions=True): # Expression is always of one of the types in proposed_type_ranges return default, UninhabitedType() - elif not is_overlapping_types( - current_type, proposed_type, prohibit_none_typevar_overlap=True, ignore_promotions=True - ): + elif not is_overlapping_types(current_type, proposed_type, ignore_promotions=True): # Expression is never of any type in proposed_type_ranges return UninhabitedType(), default else: diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 422beb5c82f0..e6e42d805052 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -815,7 +815,7 @@ elif x is Foo.C: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.C]" else: reveal_type(x) # No output here: this branch is unreachable -reveal_type(x) # N: Revealed type is "__main__.Foo" +reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.A], Literal[__main__.Foo.B], Literal[__main__.Foo.C]]" if Foo.A is x: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.A]" @@ -825,7 +825,7 @@ elif Foo.C is x: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.C]" else: reveal_type(x) # No output here: this branch is unreachable -reveal_type(x) # N: Revealed type is "__main__.Foo" +reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.A], Literal[__main__.Foo.B], Literal[__main__.Foo.C]]" y: Foo if y is Foo.A: diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 8fa1bc1ca1ac..99bd62765b11 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -2207,23 +2207,24 @@ def foo2(x: Optional[str]) -> None: reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/isinstance.pyi] -[case testNoneCheckDoesNotNarrowWhenUsingTypeVars] - -# Note: this test (and the following one) are testing checker.conditional_type_map: -# if you set the 'prohibit_none_typevar_overlap' keyword argument to False when calling -# 'is_overlapping_types', the binder will incorrectly infer that 'out' has a type of -# Union[T, None] after the if statement. - +[case testNoneCheckDoesNotMakeTypeVarOptional] from typing import TypeVar T = TypeVar('T') -def foo(x: T) -> T: +def foo_if(x: T) -> T: out = None out = x if out is None: pass return out + +def foo_while(x: T) -> T: + out = None + out = x + while out is None: + pass + return out [builtins fixtures/isinstance.pyi] [case testNoneCheckDoesNotNarrowWhenUsingTypeVarsNoStrictOptional] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index bc763095477e..285d56ff7e50 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2333,3 +2333,22 @@ def f(x: C) -> None: f(C(5)) [builtins fixtures/primitives.pyi] + +[case testNarrowingTypeVarNone] +# flags: --warn-unreachable + +# https://github.com/python/mypy/issues/18126 +from typing import TypeVar + +T = TypeVar("T") + +def fn_if(arg: T) -> None: + if arg is None: + return None + return None + +def fn_while(arg: T) -> None: + while arg is None: + return None + return None +[builtins fixtures/primitives.pyi] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 435fbde3e2ae..0231b47cf4a0 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -2409,3 +2409,33 @@ def f(x: T) -> None: case _: accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]" [builtins fixtures/tuple.pyi] + +[case testNarrowingTypeVarMatch] +# flags: --warn-unreachable + +# https://github.com/python/mypy/issues/18126 +from typing import TypeVar + +T = TypeVar("T") + +def fn_case(arg: T) -> None: + match arg: + case None: + return None + return None +[builtins fixtures/primitives.pyi] + +[case testNoneCheckDoesNotMakeTypeVarOptionalMatch] +from typing import TypeVar + +T = TypeVar('T') + +def foo(x: T) -> T: + out = None + out = x + match out: + case None: + pass + return out + +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-type-promotion.test b/test-data/unit/check-type-promotion.test index e66153726e7d..d98d0c60e164 100644 --- a/test-data/unit/check-type-promotion.test +++ b/test-data/unit/check-type-promotion.test @@ -91,7 +91,7 @@ else: reveal_type(x) # N: Revealed type is "builtins.complex" # Note we make type precise, since type promotions are involved -reveal_type(x) # N: Revealed type is "Union[builtins.complex, builtins.int, builtins.float]" +reveal_type(x) # N: Revealed type is "builtins.complex" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion3] @@ -127,7 +127,7 @@ if isinstance(x, int): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "Union[builtins.float, builtins.complex]" -reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.float, builtins.complex]" +reveal_type(x) # N: Revealed type is "Union[builtins.float, builtins.complex]" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion6] @@ -139,7 +139,7 @@ if isinstance(x, int): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.complex]" -reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, builtins.complex]" +reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.complex]" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion7] From 499adaed8adbded1a180e30d071438fef81779ec Mon Sep 17 00:00:00 2001 From: coldwolverine <166780337+coldwolverine@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:00:59 -0500 Subject: [PATCH 0914/1617] Added checks for invalid usage of continue/break/return in except* block (#18132) Fixes #18123 This PR addresses an issue where mypy incorrectly allows break/continue/return statements in the except* block. (see https://peps.python.org/pep-0654/#forbidden-combinations) --- mypy/semanal.py | 44 +++++++++++++-- test-data/unit/check-python311.test | 86 +++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 9d2d6f5fa484..bad2dfbefae0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -484,6 +484,12 @@ def __init__( # Used to pass information about current overload index to visit_func_def(). self.current_overload_item: int | None = None + # Used to track whether currently inside an except* block. This helps + # to invoke errors when continue/break/return is used inside except* block. + self.inside_except_star_block: bool = False + # Used to track edge case when return is still inside except* if it enters a loop + self.return_stmt_inside_except_star_block: bool = False + # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties @property @@ -511,6 +517,25 @@ def allow_unbound_tvars_set(self) -> Iterator[None]: finally: self.allow_unbound_tvars = old + @contextmanager + def inside_except_star_block_set( + self, value: bool, entering_loop: bool = False + ) -> Iterator[None]: + old = self.inside_except_star_block + self.inside_except_star_block = value + + # Return statement would still be in except* scope if entering loops + if not entering_loop: + old_return_stmt_flag = self.return_stmt_inside_except_star_block + self.return_stmt_inside_except_star_block = value + + try: + yield + finally: + self.inside_except_star_block = old + if not entering_loop: + self.return_stmt_inside_except_star_block = old_return_stmt_flag + # # Preparing module (performed before semantic analysis) # @@ -877,7 +902,8 @@ def visit_func_def(self, defn: FuncDef) -> None: return with self.scope.function_scope(defn): - self.analyze_func_def(defn) + with self.inside_except_star_block_set(value=False): + self.analyze_func_def(defn) def function_fullname(self, fullname: str) -> str: if self.current_overload_item is None: @@ -5264,6 +5290,8 @@ def visit_return_stmt(self, s: ReturnStmt) -> None: self.statement = s if not self.is_func_scope(): self.fail('"return" outside function', s) + if self.return_stmt_inside_except_star_block: + self.fail('"return" not allowed in except* block', s, serious=True) if s.expr: s.expr.accept(self) @@ -5297,7 +5325,8 @@ def visit_while_stmt(self, s: WhileStmt) -> None: self.statement = s s.expr.accept(self) self.loop_depth[-1] += 1 - s.body.accept(self) + with self.inside_except_star_block_set(value=False, entering_loop=True): + s.body.accept(self) self.loop_depth[-1] -= 1 self.visit_block_maybe(s.else_body) @@ -5321,20 +5350,24 @@ def visit_for_stmt(self, s: ForStmt) -> None: s.index_type = analyzed self.loop_depth[-1] += 1 - self.visit_block(s.body) + with self.inside_except_star_block_set(value=False, entering_loop=True): + self.visit_block(s.body) self.loop_depth[-1] -= 1 - self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: self.statement = s if self.loop_depth[-1] == 0: self.fail('"break" outside loop', s, serious=True, blocker=True) + if self.inside_except_star_block: + self.fail('"break" not allowed in except* block', s, serious=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: self.statement = s if self.loop_depth[-1] == 0: self.fail('"continue" outside loop', s, serious=True, blocker=True) + if self.inside_except_star_block: + self.fail('"continue" not allowed in except* block', s, serious=True) def visit_if_stmt(self, s: IfStmt) -> None: self.statement = s @@ -5355,7 +5388,8 @@ def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None]) -> None: type.accept(visitor) if var: self.analyze_lvalue(var) - handler.accept(visitor) + with self.inside_except_star_block_set(self.inside_except_star_block or s.is_star): + handler.accept(visitor) if s.else_body: s.else_body.accept(visitor) if s.finally_body: diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 28951824999f..6f4c540572b0 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -173,3 +173,89 @@ Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 reveal_type(x4) # N: Revealed type is "def (*Any) -> builtins.int" [builtins fixtures/tuple.pyi] + +[case testReturnInExceptStarBlock1] +# flags: --python-version 3.11 +def foo() -> None: + try: + pass + except* Exception: + return # E: "return" not allowed in except* block + finally: + return +[builtins fixtures/exception.pyi] + +[case testReturnInExceptStarBlock2] +# flags: --python-version 3.11 +def foo(): + while True: + try: + pass + except* Exception: + while True: + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testContinueInExceptBlockNestedInExceptStarBlock] +# flags: --python-version 3.11 +while True: + try: + ... + except* Exception: + try: + ... + except Exception: + continue # E: "continue" not allowed in except* block + continue # E: "continue" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testReturnInExceptBlockNestedInExceptStarBlock] +# flags: --python-version 3.11 +def foo(): + try: + ... + except* Exception: + try: + ... + except Exception: + return # E: "return" not allowed in except* block + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testBreakContinueReturnInExceptStarBlock1] +# flags: --python-version 3.11 +from typing import Iterable +def foo(x: Iterable[int]) -> None: + for _ in x: + try: + pass + except* Exception: + continue # E: "continue" not allowed in except* block + except* Exception: + for _ in x: + continue + break # E: "break" not allowed in except* block + except* Exception: + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testBreakContinueReturnInExceptStarBlock2] +# flags: --python-version 3.11 +def foo(): + while True: + try: + pass + except* Exception: + def inner(): + while True: + if 1 < 1: + continue + else: + break + return + if 1 < 2: + break # E: "break" not allowed in except* block + if 1 < 2: + continue # E: "continue" not allowed in except* block + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] From 30a2007ab25b496c740e76d8d694f795b6f8ee66 Mon Sep 17 00:00:00 2001 From: Sukhorosov Aleksey <39103632+alexdrydew@users.noreply.github.com> Date: Wed, 27 Nov 2024 01:33:01 +0100 Subject: [PATCH 0915/1617] Preserve typevar default None in type alias (#18197) Fixes #18188 Currently it seems that `None` is dropped from type variable defaults when assigning generic class to a type alias.

Example ```python from typing_extensions import TypeVar from typing import Generic, Union T1 = TypeVar("T1", default=Union[int, None]) T2 = TypeVar("T2", default=Union[int, None]) class A(Generic[T1, T2]): def __init__(self, a: T1, b: T2) -> None: self.a = a self.b = b MyA = A[T1, int] a: MyA = A(None, 10) # error: Argument 1 to "A" has incompatible type "None"; expected "int" [arg-type] reveal_type(a.a) ```
The change is similar to https://github.com/python/mypy/pull/16859 --- mypy/typeanal.py | 3 ++- test-data/unit/check-typevar-defaults.test | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index daf7ab1951ea..b7e7da17e209 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2277,7 +2277,8 @@ def set_any_tvars( env[tv.id] = arg t = TypeAliasType(node, args, newline, newcolumn) if not has_type_var_tuple_type: - fixed = expand_type(t, env) + with state.strict_optional_set(options.strict_optional): + fixed = expand_type(t, env) assert isinstance(fixed, TypeAliasType) t.args = fixed.args diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 3cd94f4a46d2..93d20eb26f6e 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -728,3 +728,24 @@ class C(Generic[_I]): pass t: type[C] | int = C [builtins fixtures/tuple.pyi] + + + +[case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault] +from typing_extensions import TypeVar +from typing import Generic, Union + +T1 = TypeVar("T1", default=Union[int, None]) +T2 = TypeVar("T2", default=Union[int, None]) + + +class A(Generic[T1, T2]): + def __init__(self, a: T1, b: T2) -> None: + self.a = a + self.b = b + + +MyA = A[T1, int] +a: MyA = A(None, 10) +reveal_type(a.a) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/tuple.pyi] From 15cd6d30f888a4b6ce217e05c4ccfd09dfa2945c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:21:19 -0800 Subject: [PATCH 0916/1617] Set default strict_optional state to True (#18198) --- mypy/state.py | 2 +- mypy/test/testsubtypes.py | 4 +- mypy/test/testtypes.py | 110 +++++++++++++++++++++++--------------- mypy/test/typefixture.py | 1 + 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/mypy/state.py b/mypy/state.py index cd3a360dd15f..533dceeb1f24 100644 --- a/mypy/state.py +++ b/mypy/state.py @@ -24,5 +24,5 @@ def strict_optional_set(self, value: bool) -> Iterator[None]: self.strict_optional = saved -state: Final = StrictOptionalState(strict_optional=False) +state: Final = StrictOptionalState(strict_optional=True) find_occurrences: tuple[str, str] | None = None diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 480fe38a90a7..175074a2b140 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -4,7 +4,7 @@ from mypy.subtypes import is_subtype from mypy.test.helpers import Suite from mypy.test.typefixture import InterfaceTypeFixture, TypeFixture -from mypy.types import Instance, Type, UnpackType +from mypy.types import Instance, Type, UninhabitedType, UnpackType class SubtypingSuite(Suite): @@ -87,7 +87,7 @@ def test_basic_callable_subtyping(self) -> None: ) self.assert_strict_subtype( - self.fx.callable(self.fx.a, self.fx.nonet), self.fx.callable(self.fx.a, self.fx.a) + self.fx.callable(self.fx.a, UninhabitedType()), self.fx.callable(self.fx.a, self.fx.a) ) self.assert_unrelated( diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 0218d33cc124..dbf7619f3a44 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -766,18 +766,19 @@ def test_type_vars(self) -> None: self.assert_join(self.fx.t, self.fx.s, self.fx.o) def test_none(self) -> None: - # Any type t joined with None results in t. - for t in [ - NoneType(), - self.fx.a, - self.fx.o, - UnboundType("x"), - self.fx.t, - self.tuple(), - self.callable(self.fx.a, self.fx.b), - self.fx.anyt, - ]: - self.assert_join(t, NoneType(), t) + with state.strict_optional_set(False): + # Any type t joined with None results in t. + for t in [ + NoneType(), + self.fx.a, + self.fx.o, + UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + self.fx.anyt, + ]: + self.assert_join(t, NoneType(), t) def test_unbound_type(self) -> None: self.assert_join(UnboundType("x"), UnboundType("x"), self.fx.anyt) @@ -798,11 +799,15 @@ def test_unbound_type(self) -> None: def test_any_type(self) -> None: # Join against 'Any' type always results in 'Any'. + with state.strict_optional_set(False): + self.assert_join(NoneType(), self.fx.anyt, self.fx.anyt) + for t in [ self.fx.anyt, self.fx.a, self.fx.o, - NoneType(), + # TODO: fix this is not currently symmetric + # NoneType(), UnboundType("x"), self.fx.t, self.tuple(), @@ -834,7 +839,11 @@ def test_other_mixed_types(self) -> None: self.assert_join(t1, t2, self.fx.o) def test_simple_generics(self) -> None: - self.assert_join(self.fx.ga, self.fx.nonet, self.fx.ga) + with state.strict_optional_set(False): + self.assert_join(self.fx.ga, self.fx.nonet, self.fx.ga) + with state.strict_optional_set(True): + self.assert_join(self.fx.ga, self.fx.nonet, UnionType([self.fx.ga, NoneType()])) + self.assert_join(self.fx.ga, self.fx.anyt, self.fx.anyt) for t in [ @@ -1105,8 +1114,8 @@ def test_class_subtyping(self) -> None: self.assert_meet(self.fx.a, self.fx.o, self.fx.a) self.assert_meet(self.fx.a, self.fx.b, self.fx.b) self.assert_meet(self.fx.b, self.fx.o, self.fx.b) - self.assert_meet(self.fx.a, self.fx.d, NoneType()) - self.assert_meet(self.fx.b, self.fx.c, NoneType()) + self.assert_meet(self.fx.a, self.fx.d, UninhabitedType()) + self.assert_meet(self.fx.b, self.fx.c, UninhabitedType()) def test_tuples(self) -> None: self.assert_meet(self.tuple(), self.tuple(), self.tuple()) @@ -1114,13 +1123,15 @@ def test_tuples(self) -> None: self.assert_meet( self.tuple(self.fx.b, self.fx.c), self.tuple(self.fx.a, self.fx.d), - self.tuple(self.fx.b, NoneType()), + self.tuple(self.fx.b, UninhabitedType()), ) self.assert_meet( self.tuple(self.fx.a, self.fx.a), self.fx.std_tuple, self.tuple(self.fx.a, self.fx.a) ) - self.assert_meet(self.tuple(self.fx.a), self.tuple(self.fx.a, self.fx.a), NoneType()) + self.assert_meet( + self.tuple(self.fx.a), self.tuple(self.fx.a, self.fx.a), UninhabitedType() + ) def test_function_types(self) -> None: self.assert_meet( @@ -1143,7 +1154,7 @@ def test_function_types(self) -> None: def test_type_vars(self) -> None: self.assert_meet(self.fx.t, self.fx.t, self.fx.t) self.assert_meet(self.fx.s, self.fx.s, self.fx.s) - self.assert_meet(self.fx.t, self.fx.s, NoneType()) + self.assert_meet(self.fx.t, self.fx.s, UninhabitedType()) def test_none(self) -> None: self.assert_meet(NoneType(), NoneType(), NoneType()) @@ -1151,15 +1162,28 @@ def test_none(self) -> None: self.assert_meet(NoneType(), self.fx.anyt, NoneType()) # Any type t joined with None results in None, unless t is Any. - for t in [ - self.fx.a, - self.fx.o, - UnboundType("x"), - self.fx.t, - self.tuple(), - self.callable(self.fx.a, self.fx.b), - ]: - self.assert_meet(t, NoneType(), NoneType()) + with state.strict_optional_set(False): + for t in [ + self.fx.a, + self.fx.o, + UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + ]: + self.assert_meet(t, NoneType(), NoneType()) + + with state.strict_optional_set(True): + self.assert_meet(self.fx.o, NoneType(), NoneType()) + for t in [ + self.fx.a, + # TODO: fix this is not currently symmetric + # UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + ]: + self.assert_meet(t, NoneType(), UninhabitedType()) def test_unbound_type(self) -> None: self.assert_meet(UnboundType("x"), UnboundType("x"), self.fx.anyt) @@ -1197,28 +1221,28 @@ def test_simple_generics(self) -> None: self.assert_meet(self.fx.ga, self.fx.ga, self.fx.ga) self.assert_meet(self.fx.ga, self.fx.o, self.fx.ga) self.assert_meet(self.fx.ga, self.fx.gb, self.fx.gb) - self.assert_meet(self.fx.ga, self.fx.gd, self.fx.nonet) - self.assert_meet(self.fx.ga, self.fx.g2a, self.fx.nonet) + self.assert_meet(self.fx.ga, self.fx.gd, UninhabitedType()) + self.assert_meet(self.fx.ga, self.fx.g2a, UninhabitedType()) - self.assert_meet(self.fx.ga, self.fx.nonet, self.fx.nonet) + self.assert_meet(self.fx.ga, self.fx.nonet, UninhabitedType()) self.assert_meet(self.fx.ga, self.fx.anyt, self.fx.ga) for t in [self.fx.a, self.fx.t, self.tuple(), self.callable(self.fx.a, self.fx.b)]: - self.assert_meet(t, self.fx.ga, self.fx.nonet) + self.assert_meet(t, self.fx.ga, UninhabitedType()) def test_generics_with_multiple_args(self) -> None: self.assert_meet(self.fx.hab, self.fx.hab, self.fx.hab) self.assert_meet(self.fx.hab, self.fx.haa, self.fx.hab) - self.assert_meet(self.fx.hab, self.fx.had, self.fx.nonet) + self.assert_meet(self.fx.hab, self.fx.had, UninhabitedType()) self.assert_meet(self.fx.hab, self.fx.hbb, self.fx.hbb) def test_generics_with_inheritance(self) -> None: self.assert_meet(self.fx.gsab, self.fx.gb, self.fx.gsab) - self.assert_meet(self.fx.gsba, self.fx.gb, self.fx.nonet) + self.assert_meet(self.fx.gsba, self.fx.gb, UninhabitedType()) def test_generics_with_inheritance_and_shared_supertype(self) -> None: - self.assert_meet(self.fx.gsba, self.fx.gs2a, self.fx.nonet) - self.assert_meet(self.fx.gsab, self.fx.gs2a, self.fx.nonet) + self.assert_meet(self.fx.gsba, self.fx.gs2a, UninhabitedType()) + self.assert_meet(self.fx.gsab, self.fx.gs2a, UninhabitedType()) def test_generic_types_and_dynamic(self) -> None: self.assert_meet(self.fx.gdyn, self.fx.ga, self.fx.ga) @@ -1232,33 +1256,33 @@ def test_callables_with_dynamic(self) -> None: def test_meet_interface_types(self) -> None: self.assert_meet(self.fx.f, self.fx.f, self.fx.f) - self.assert_meet(self.fx.f, self.fx.f2, self.fx.nonet) + self.assert_meet(self.fx.f, self.fx.f2, UninhabitedType()) self.assert_meet(self.fx.f, self.fx.f3, self.fx.f3) def test_meet_interface_and_class_types(self) -> None: self.assert_meet(self.fx.o, self.fx.f, self.fx.f) - self.assert_meet(self.fx.a, self.fx.f, self.fx.nonet) + self.assert_meet(self.fx.a, self.fx.f, UninhabitedType()) self.assert_meet(self.fx.e, self.fx.f, self.fx.e) def test_meet_class_types_with_shared_interfaces(self) -> None: # These have nothing special with respect to meets, unlike joins. These # are for completeness only. - self.assert_meet(self.fx.e, self.fx.e2, self.fx.nonet) - self.assert_meet(self.fx.e2, self.fx.e3, self.fx.nonet) + self.assert_meet(self.fx.e, self.fx.e2, UninhabitedType()) + self.assert_meet(self.fx.e2, self.fx.e3, UninhabitedType()) def test_meet_with_generic_interfaces(self) -> None: fx = InterfaceTypeFixture() self.assert_meet(fx.gfa, fx.m1, fx.m1) self.assert_meet(fx.gfa, fx.gfa, fx.gfa) - self.assert_meet(fx.gfb, fx.m1, fx.nonet) + self.assert_meet(fx.gfb, fx.m1, UninhabitedType()) def test_type_type(self) -> None: self.assert_meet(self.fx.type_a, self.fx.type_b, self.fx.type_b) self.assert_meet(self.fx.type_b, self.fx.type_any, self.fx.type_b) self.assert_meet(self.fx.type_b, self.fx.type_type, self.fx.type_b) - self.assert_meet(self.fx.type_b, self.fx.type_c, self.fx.nonet) - self.assert_meet(self.fx.type_c, self.fx.type_d, self.fx.nonet) + self.assert_meet(self.fx.type_b, self.fx.type_c, self.fx.type_never) + self.assert_meet(self.fx.type_c, self.fx.type_d, self.fx.type_never) self.assert_meet(self.fx.type_type, self.fx.type_any, self.fx.type_any) self.assert_meet(self.fx.type_b, self.fx.anyt, self.fx.type_b) diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index 5a813f70117c..d6c904732b17 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -216,6 +216,7 @@ def make_type_var( self.type_d = TypeType.make_normalized(self.d) self.type_t = TypeType.make_normalized(self.t) self.type_any = TypeType.make_normalized(self.anyt) + self.type_never = TypeType.make_normalized(UninhabitedType()) self._add_bool_dunder(self.bool_type_info) self._add_bool_dunder(self.ai) From 2842e8f27993e303d757a916acdf278bfd6fd682 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:38:45 -0800 Subject: [PATCH 0917/1617] [mypyc] Fixing check for enum classes. (#18178) Fixes [mypyc/mypyc#1065](https://github.com/mypyc/mypyc/issues/1065) Fixes [mypyc/mypyc#1059](https://github.com/mypyc/mypyc/issues/1059) Fixes [mypyc/mypyc#1022](https://github.com/mypyc/mypyc/issues/1022) * Checking for enum classes using `is_enum` flag instead of `fullname` of base class. That way, mypyc recognizes classes derived from `IntEnum` as enum classes too. * After fixing the above bug, test failures revealed that mypyc was sending all MRO classes to `__prepare__` function. For example, for the `Player` test class added in this PR, mypyc was generating C code to call `__prepare__` of the base class with `(IntEnum, int, Enum)` instead of just `(IntEnum,)`. This bug has been fixed in `classdef.py:populate_non_ext_bases`. --- mypyc/irbuild/classdef.py | 4 ++-- mypyc/test-data/run-classes.test | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 590bd12ce7b3..5fd55ff4b5a4 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -513,7 +513,7 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: is_named_tuple = cdef.info.is_named_tuple ir = builder.mapper.type_to_ir[cdef.info] bases = [] - for cls in cdef.info.mro[1:]: + for cls in (b.type for b in cdef.info.bases): if cls.fullname == "builtins.object": continue if is_named_tuple and cls.fullname in ( @@ -682,7 +682,7 @@ def add_non_ext_class_attr( # are final. if ( cdef.info.bases - and cdef.info.bases[0].type.fullname == "enum.Enum" + and cdef.info.bases[0].type.is_enum # Skip these since Enum will remove it and lvalue.name not in EXCLUDED_ENUM_ATTRIBUTES ): diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 6f58125a1113..d76974f7d83e 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2619,3 +2619,15 @@ def test_final_attribute() -> None: assert C.a['x'] == 'y' assert C.b['x'] == 'y' assert C.a is C.b + +[case testClassDerivedFromIntEnum] +from enum import IntEnum, auto + +class Player(IntEnum): + MIN = auto() + +print(f'{Player.MIN = }') +[file driver.py] +from native import Player +[out] +Player.MIN = From d39eaccf7bbbad5c1d3d1e0c0a971501d764a1a2 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:39:33 -0800 Subject: [PATCH 0918/1617] [mypyc] Fixing index variable in for-loop with builtins.enumerate. (#18202) Fixes [mypyc/mypyc#1046](https://github.com/mypyc/mypyc/issues/1046) This change fixes two problems: 1. The index variable was getting instantiated even while enumerating an empty iterable. 2. After exiting the for-loop, the value of the index variable is off by 1 (see issue linked above). This change fixes both problems by assigning the temporary register to the index variable at the beginning of the for-loop body. Before this change, this assignment was happening before the for-loop and at the end of the for-loop body. --- mypyc/irbuild/for_helpers.py | 5 +++-- mypyc/test-data/irbuild-statements.test | 14 +++++--------- mypyc/test-data/run-loops.test | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5d8315e88f72..ffb1fc15f228 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -985,7 +985,6 @@ def init(self) -> None: zero = Integer(0) self.index_reg = builder.maybe_spill_assignable(zero) self.index_target: Register | AssignmentTarget = builder.get_assignment_target(self.index) - builder.assign(self.index_target, zero, self.line) def gen_step(self) -> None: builder = self.builder @@ -997,7 +996,9 @@ def gen_step(self) -> None: short_int_rprimitive, builder.read(self.index_reg, line), Integer(1), IntOp.ADD, line ) builder.assign(self.index_reg, new_val, line) - builder.assign(self.index_target, new_val, line) + + def begin_body(self) -> None: + self.builder.assign(self.index_target, self.builder.read(self.index_reg), self.line) class ForEnumerate(ForGenerator): diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index f9d3354b317c..825bc750f7a7 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -864,18 +864,16 @@ def g(x: Iterable[int]) -> None: [out] def f(a): a :: list - r0 :: short_int - i :: int - r1 :: short_int + r0, r1 :: short_int r2 :: native_int r3 :: short_int r4 :: bit + i :: int r5 :: object r6, x, r7 :: int r8, r9 :: short_int L0: r0 = 0 - i = 0 r1 = 0 L1: r2 = var_object_size a @@ -883,6 +881,7 @@ L1: r4 = int_lt r1, r3 if r4 goto L2 else goto L4 :: bool L2: + i = r0 r5 = CPyList_GetItemUnsafe(a, r1) r6 = unbox(int, r5) x = r6 @@ -890,7 +889,6 @@ L2: L3: r8 = r0 + 2 r0 = r8 - i = r8 r9 = r1 + 2 r1 = r9 goto L1 @@ -900,25 +898,23 @@ L5: def g(x): x :: object r0 :: short_int - i :: int r1, r2 :: object - r3, n :: int + i, r3, n :: int r4 :: short_int r5 :: bit L0: r0 = 0 - i = 0 r1 = PyObject_GetIter(x) L1: r2 = PyIter_Next(r1) if is_error(r2) goto L4 else goto L2 L2: + i = r0 r3 = unbox(int, r2) n = r3 L3: r4 = r0 + 2 r0 = r4 - i = r4 goto L1 L4: r5 = CPy_NoErrOccured() diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 95b79af1a411..76fbb06200a3 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -228,6 +228,7 @@ def nested_enumerate() -> None: assert i == inner inner += 1 outer += 1 + assert i == 2 assert outer_seen == l1 def nested_range() -> None: @@ -465,6 +466,29 @@ assert g([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] assert f([6, 7], [8]) == [(0, 6, 8)] assert f([6], [8, 9]) == [(0, 6, 8)] +[case testEnumerateEmptyList] +from typing import List + +def get_enumerate_locals(iterable: List[int]) -> int: + for i, j in enumerate(iterable): + pass + try: + return i + except NameError: + return -100 + +[file driver.py] +from native import get_enumerate_locals + +print(get_enumerate_locals([])) +print(get_enumerate_locals([55])) +print(get_enumerate_locals([551, 552])) + +[out] +-100 +0 +1 + [case testIterTypeTrickiness] # Test inferring the type of a for loop body doesn't cause us grief # Extracted from somethings that broke in mypy From 1711de86db110225508ebe3fae94ba02d07fa217 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 29 Nov 2024 15:03:11 +0000 Subject: [PATCH 0919/1617] [mypyc] Support unicode surrogates in string literals (#18209) Previously surrogates would trigger a compiler crash. --- mypyc/codegen/literals.py | 2 +- mypyc/lib-rt/misc_ops.c | 2 +- mypyc/test-data/run-strings.test | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mypyc/codegen/literals.py b/mypyc/codegen/literals.py index 1f0c3bc6ec7b..2c4ab0c1dc2e 100644 --- a/mypyc/codegen/literals.py +++ b/mypyc/codegen/literals.py @@ -230,7 +230,7 @@ def format_int(n: int) -> bytes: def format_str_literal(s: str) -> bytes: - utf8 = s.encode("utf-8") + utf8 = s.encode("utf-8", errors="surrogatepass") return format_int(len(utf8)) + utf8 diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index 1572c4496e30..d3e8e69ed19b 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -563,7 +563,7 @@ int CPyStatics_Initialize(PyObject **statics, while (num-- > 0) { size_t len; data = parse_int(data, &len); - PyObject *obj = PyUnicode_FromStringAndSize(data, len); + PyObject *obj = PyUnicode_DecodeUTF8(data, len, "surrogatepass"); if (obj == NULL) { return -1; } diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index be668435d073..2becae848f7c 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -639,3 +639,12 @@ def test_encode() -> None: assert u'\u00E1'.encode('latin1') == b'\xe1' with assertRaises(UnicodeEncodeError): u.encode('latin1') + +[case testUnicodeSurrogate] +def f() -> str: + return "\ud800" + +def test_surrogate() -> None: + assert ord(f()) == 0xd800 + assert ord("\udfff") == 0xdfff + assert repr("foobar\x00\xab\ud912\U00012345") == r"'foobar\x00«\ud912𒍅'" From e7c095af6f0b4695bd8009b642eb3364969a9e59 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 29 Nov 2024 16:50:57 +0000 Subject: [PATCH 0920/1617] [mypyc] Refactor: use new-style primitives for function ops (#18211) Eventually we'll get rid of the old-style CallC primitives. --- mypyc/irbuild/builder.py | 16 +++++++++----- mypyc/irbuild/classdef.py | 8 +++---- mypyc/irbuild/expression.py | 8 +++---- mypyc/irbuild/for_helpers.py | 2 +- mypyc/irbuild/format_str_tokenizer.py | 6 ++--- mypyc/irbuild/function.py | 8 ++++--- mypyc/irbuild/ll_builder.py | 20 +++++++++-------- mypyc/irbuild/match.py | 4 ++-- mypyc/irbuild/statement.py | 10 ++++++--- mypyc/primitives/registry.py | 32 +++++++++++++-------------- mypyc/test/test_cheader.py | 8 ++----- 11 files changed, 65 insertions(+), 57 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 4a5647b9ffdf..7561070b012f 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -84,6 +84,7 @@ IntOp, LoadStatic, Op, + PrimitiveDescription, RaiseStandardError, Register, SetAttr, @@ -381,6 +382,9 @@ def load_module(self, name: str) -> Value: def call_c(self, desc: CFunctionDescription, args: list[Value], line: int) -> Value: return self.builder.call_c(desc, args, line) + def primitive_op(self, desc: PrimitiveDescription, args: list[Value], line: int) -> Value: + return self.builder.primitive_op(desc, args, line) + def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.builder.int_op(type, lhs, rhs, op, line) @@ -691,7 +695,7 @@ def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: i else: key = self.load_str(target.attr) boxed_reg = self.builder.box(rvalue_reg) - self.call_c(py_setattr_op, [target.obj, key, boxed_reg], line) + self.primitive_op(py_setattr_op, [target.obj, key, boxed_reg], line) elif isinstance(target, AssignmentTargetIndex): target_reg2 = self.gen_method_call( target.base, "__setitem__", [target.index, rvalue_reg], None, line @@ -768,7 +772,7 @@ def process_iterator_tuple_assignment_helper( def process_iterator_tuple_assignment( self, target: AssignmentTargetTuple, rvalue_reg: Value, line: int ) -> None: - iterator = self.call_c(iter_op, [rvalue_reg], line) + iterator = self.primitive_op(iter_op, [rvalue_reg], line) # This may be the whole lvalue list if there is no starred value split_idx = target.star_idx if target.star_idx is not None else len(target.items) @@ -794,7 +798,7 @@ def process_iterator_tuple_assignment( # Assign the starred value and all values after it if target.star_idx is not None: post_star_vals = target.items[split_idx + 1 :] - iter_list = self.call_c(to_list, [iterator], line) + iter_list = self.primitive_op(to_list, [iterator], line) iter_list_len = self.builtin_len(iter_list, line) post_star_len = Integer(len(post_star_vals)) condition = self.binary_op(post_star_len, iter_list_len, "<=", line) @@ -1051,9 +1055,9 @@ def call_refexpr_with_args( # Handle data-driven special-cased primitive call ops. if callee.fullname and expr.arg_kinds == [ARG_POS] * len(arg_values): fullname = get_call_target_fullname(callee) - call_c_ops_candidates = function_ops.get(fullname, []) - target = self.builder.matching_call_c( - call_c_ops_candidates, arg_values, expr.line, self.node_type(expr) + primitive_candidates = function_ops.get(fullname, []) + target = self.builder.matching_primitive_op( + primitive_candidates, arg_values, expr.line, self.node_type(expr) ) if target: return target diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 5fd55ff4b5a4..58be87c88a3f 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -292,7 +292,7 @@ def add_attr(self, lvalue: NameExpr, stmt: AssignmentStmt) -> None: return typ = self.builder.load_native_type_object(self.cdef.fullname) value = self.builder.accept(stmt.rvalue) - self.builder.call_c( + self.builder.primitive_op( py_setattr_op, [typ, self.builder.load_str(lvalue.name), value], stmt.line ) if self.builder.non_function_scope() and stmt.is_final_def: @@ -452,7 +452,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: ) ) # Populate a '__mypyc_attrs__' field containing the list of attrs - builder.call_c( + builder.primitive_op( py_setattr_op, [ tp, @@ -483,7 +483,7 @@ def make_generic_base_class( for tv, type_param in zip(tvs, type_args): if type_param.kind == TYPE_VAR_TUPLE_KIND: # Evaluate *Ts for a TypeVarTuple - it = builder.call_c(iter_op, [tv], line) + it = builder.primitive_op(iter_op, [tv], line) tv = builder.call_c(next_op, [it], line) args.append(tv) @@ -603,7 +603,7 @@ def setup_non_ext_dict( This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. - has_prepare = builder.call_c( + has_prepare = builder.primitive_op( py_hasattr_op, [metaclass, builder.load_str("__prepare__")], cdef.line ) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 8d7c089e20cd..a2a3203d12ca 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -476,7 +476,7 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe # Grab first argument vself: Value = builder.self() if decl.kind == FUNC_CLASSMETHOD: - vself = builder.call_c(type_op, [vself], expr.line) + vself = builder.primitive_op(type_op, [vself], expr.line) elif builder.fn_info.is_generator: # For generator classes, the self target is the 6th value # in the symbol table (which is an ordered dict). This is sort @@ -953,7 +953,7 @@ def transform_tuple_expr(builder: IRBuilder, expr: TupleExpr) -> Value: def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value: """Create a list, then turn it into a tuple.""" val_as_list = _visit_list_display(builder, expr.items, expr.line) - return builder.call_c(list_tuple_op, [val_as_list], expr.line) + return builder.primitive_op(list_tuple_op, [val_as_list], expr.line) def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value: @@ -1045,12 +1045,12 @@ def get_arg(arg: Expression | None) -> Value: return builder.accept(arg) args = [get_arg(expr.begin_index), get_arg(expr.end_index), get_arg(expr.stride)] - return builder.call_c(new_slice_op, args, expr.line) + return builder.primitive_op(new_slice_op, args, expr.line) def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: builder.warning("Treating generator comprehension as list", o.line) - return builder.call_c(iter_op, [translate_list_comprehension(builder, o)], o.line) + return builder.primitive_op(iter_op, [translate_list_comprehension(builder, o)], o.line) def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value: diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index ffb1fc15f228..b37c39e70f11 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -586,7 +586,7 @@ def init(self, expr_reg: Value, target_type: RType) -> None: # for the for-loop. If we are inside of a generator function, spill these into the # environment class. builder = self.builder - iter_reg = builder.call_c(iter_op, [expr_reg], self.line) + iter_reg = builder.primitive_op(iter_op, [expr_reg], self.line) builder.maybe_spill(expr_reg) self.iter_target = builder.maybe_spill(iter_reg) self.target_type = target_type diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 0b46887811fb..eaa4027ed768 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -146,12 +146,12 @@ def convert_format_expr_to_str( if is_str_rprimitive(node_type): var_str = builder.accept(x) elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): - var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: - var_str = builder.call_c(str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(str_op, [builder.accept(x)], line) elif format_op == FormatOp.INT: if is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): - var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: return None else: diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 8678c1d89c58..4d04c549d392 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -433,7 +433,9 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None # Set the callable object representing the decorated method as an attribute of the # extension class. - builder.call_c(py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line) + builder.primitive_op( + py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line + ) if fdef.is_property: # If there is a property setter, it will be processed after the getter, @@ -973,7 +975,7 @@ def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None: cache_dict = builder.call_c(dict_new_op, [], line) dispatch_cache_str = builder.load_str("dispatch_cache") # use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__ - builder.call_c(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) + builder.primitive_op(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) # the generated C code seems to expect that __init__ returns a char, so just return 1 builder.add(Return(Integer(1, bool_rprimitive, line), line)) @@ -1016,7 +1018,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: registry_dict = builder.builder.make_dict([(loaded_object_type, main_func_obj)], line) dispatch_func_obj = builder.load_global_str(fitem.name, line) - builder.call_c( + builder.primitive_op( py_setattr_op, [dispatch_func_obj, builder.load_str("registry"), registry_dict], line ) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index cb23a74c69c6..df73f8481751 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -620,7 +620,7 @@ def py_get_attr(self, obj: Value, attr: str, line: int) -> Value: Prefer get_attr() which generates optimized code for native classes. """ key = self.load_str(attr) - return self.call_c(py_getattr_op, [obj, key], line) + return self.primitive_op(py_getattr_op, [obj, key], line) # isinstance() checks @@ -656,7 +656,9 @@ def isinstance_native(self, obj: Value, class_ir: ClassIR, line: int) -> Value: """ concrete = all_concrete_classes(class_ir) if concrete is None or len(concrete) > FAST_ISINSTANCE_MAX_SUBCLASSES + 1: - return self.call_c(fast_isinstance_op, [obj, self.get_native_type(class_ir)], line) + return self.primitive_op( + fast_isinstance_op, [obj, self.get_native_type(class_ir)], line + ) if not concrete: # There can't be any concrete instance that matches this. return self.false() @@ -857,7 +859,7 @@ def _construct_varargs( if star_result is None: star_result = self.new_tuple(star_values, line) else: - star_result = self.call_c(list_tuple_op, [star_result], line) + star_result = self.primitive_op(list_tuple_op, [star_result], line) if has_star2 and star2_result is None: star2_result = self._create_dict(star2_keys, star2_values, line) @@ -1515,7 +1517,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val # Cast to bool if necessary since most types uses comparison returning a object type # See generic_ops.py for more information if not is_bool_rprimitive(compare.type): - compare = self.call_c(bool_op, [compare], line) + compare = self.primitive_op(bool_op, [compare], line) if i < len(lhs.type.types) - 1: branch = Branch(compare, early_stop, check_blocks[i + 1], Branch.BOOL) else: @@ -1534,7 +1536,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val def translate_instance_contains(self, inst: Value, item: Value, op: str, line: int) -> Value: res = self.gen_method_call(inst, "__contains__", [item], None, line) if not is_bool_rprimitive(res.type): - res = self.call_c(bool_op, [res], line) + res = self.primitive_op(bool_op, [res], line) if op == "not in": res = self.bool_bitwise_op(res, Integer(1, rtype=bool_rprimitive), "^", line) return res @@ -1667,7 +1669,7 @@ def new_list_op(self, values: list[Value], line: int) -> Value: return result_list def new_set_op(self, values: list[Value], line: int) -> Value: - return self.call_c(new_set_op, values, line) + return self.primitive_op(new_set_op, values, line) def setup_rarray( self, item_type: RType, values: Sequence[Value], *, object_ptr: bool = False @@ -1775,7 +1777,7 @@ def bool_value(self, value: Value) -> Value: self.goto(end) self.activate_block(end) else: - result = self.call_c(bool_op, [value], value.line) + result = self.primitive_op(bool_op, [value], value.line) return result def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> None: @@ -2065,7 +2067,7 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: self.activate_block(copysign) # If the remainder is zero, CPython ensures the result has the # same sign as the denominator. - adj = self.call_c(copysign_op, [Float(0.0), rhs], line) + adj = self.primitive_op(copysign_op, [Float(0.0), rhs], line) self.add(Assign(res, adj)) self.add(Goto(done)) self.activate_block(done) @@ -2260,7 +2262,7 @@ def new_tuple_with_length(self, length: Value, line: int) -> Value: return self.call_c(new_tuple_with_length_op, [length], line) def int_to_float(self, n: Value, line: int) -> Value: - return self.call_c(int_to_float_op, [n], line) + return self.primitive_op(int_to_float_op, [n], line) # Internal helpers diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index a1e671911ea5..976a8810b327 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -131,7 +131,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None: else slow_isinstance_op ) - cond = self.builder.call_c( + cond = self.builder.primitive_op( isinstance_op, [self.subject, self.builder.accept(pattern.class_ref)], pattern.line ) @@ -246,7 +246,7 @@ def visit_mapping_pattern(self, pattern: MappingPattern) -> None: self.builder.activate_block(self.code_block) self.code_block = BasicBlock() - rest = self.builder.call_c(dict_copy, [self.subject], pattern.rest.line) + rest = self.builder.primitive_op(dict_copy, [self.subject], pattern.rest.line) target = self.builder.get_assignment_target(pattern.rest) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index ac3fa33f36ca..bd4acccf077a 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -58,6 +58,7 @@ LoadLiteral, LoadStatic, MethodCall, + PrimitiveDescription, RaiseStandardError, Register, Return, @@ -757,7 +758,7 @@ def transform_with( value = builder.add(MethodCall(mgr_v, f"__{al}enter__", args=[], line=line)) exit_ = None else: - typ = builder.call_c(type_op, [mgr_v], line) + typ = builder.primitive_op(type_op, [mgr_v], line) exit_ = builder.maybe_spill(builder.py_get_attr(typ, f"__{al}exit__", line)) value = builder.py_call(builder.py_get_attr(typ, f"__{al}enter__", line), [mgr_v], line) @@ -876,7 +877,7 @@ def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) line, ) key = builder.load_str(target.attr) - builder.call_c(py_delattr_op, [target.obj, key], line) + builder.primitive_op(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. @@ -924,7 +925,10 @@ def emit_yield_from_or_await( received_reg = Register(object_rprimitive) get_op = coro_op if is_await else iter_op - iter_val = builder.call_c(get_op, [val], line) + if isinstance(get_op, PrimitiveDescription): + iter_val = builder.primitive_op(get_op, [val], line) + else: + iter_val = builder.call_c(get_op, [val], line) iter_reg = builder.maybe_spill_assignable(iter_val) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 5190b01adf4a..25ad83be14f5 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -70,13 +70,13 @@ class LoadAddressDescription(NamedTuple): src: str # name of the target to load -# CallC op for method call(such as 'str.join') +# CallC op for method call (such as 'str.join') method_call_ops: dict[str, list[CFunctionDescription]] = {} -# CallC op for top level function call(such as 'builtins.list') -function_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for top level function call (such as 'builtins.list') +function_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for binary ops +# Primitive ops for binary operations binary_ops: dict[str, list[PrimitiveDescription]] = {} # CallC op for unary ops @@ -161,8 +161,8 @@ def function_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, -) -> CFunctionDescription: - """Define a c function call op that replaces a function call. +) -> PrimitiveDescription: + """Define a C function call op that replaces a function call. This will be automatically generated by matching against the AST. @@ -175,19 +175,19 @@ def function_op( if extra_int_constants is None: extra_int_constants = [] ops = function_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, arg_types, return_type, - var_arg_type, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + var_arg_type=var_arg_type, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, is_pure=False, ) ops.append(desc) diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index f2af41c22ea9..08b5a4648ca5 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -26,18 +26,14 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [ - registry.method_call_ops.values(), - registry.function_ops.values(), - registry.unary_ops.values(), - ]: + for old_values in [registry.method_call_ops.values(), registry.unary_ops.values()]: for old_ops in old_values: if isinstance(old_ops, CFunctionDescription): old_ops = [old_ops] for old_op in old_ops: check_name(old_op.c_function_name) - for values in [registry.binary_ops.values()]: + for values in [registry.binary_ops.values(), registry.function_ops.values()]: for ops in values: if isinstance(ops, PrimitiveDescription): ops = [ops] From 70eab9aefde74bccb3ad6eb7c661d488c163da4c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 29 Nov 2024 17:25:57 +0000 Subject: [PATCH 0921/1617] [mypyc] Refactor: use new-style primitives for unary ops (#18213) --- mypyc/ir/ops.py | 2 +- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/primitives/int_ops.py | 11 ++--------- mypyc/primitives/registry.py | 28 ++++++++++++++-------------- mypyc/test-data/irbuild-int.test | 20 +++++++++++++++++++- mypyc/test/test_cheader.py | 8 ++++++-- mypyc/test/test_emitfunc.py | 1 + 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 896ba3ac091c..6e186c4ef0fc 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -625,7 +625,7 @@ def __init__( assert error_kind == ERR_NEVER def __repr__(self) -> str: - return f"" + return f"" class PrimitiveOp(RegisterOp): diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index df73f8481751..a5a524b25af0 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1601,8 +1601,8 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: result = self.dunder_op(value, None, expr_op, line) if result is not None: return result - call_c_ops_candidates = unary_ops.get(expr_op, []) - target = self.matching_call_c(call_c_ops_candidates, [value], line) + primitive_ops_candidates = unary_ops.get(expr_op, []) + target = self.matching_primitive_op(primitive_ops_candidates, [value], line) assert target, "Unsupported unary operation: %s" % expr_op return target diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 2eff233403f4..657578d20046 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -31,14 +31,7 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import ( - CFunctionDescription, - binary_op, - custom_op, - function_op, - load_address_op, - unary_op, -) +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, unary_op # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. @@ -176,7 +169,7 @@ def int_binary_op( int_binary_op("<<=", "CPyTagged_Lshift", error_kind=ERR_MAGIC) -def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: +def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: return unary_op( name=name, arg_type=int_rprimitive, diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 25ad83be14f5..26faabc0c1e2 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -79,8 +79,8 @@ class LoadAddressDescription(NamedTuple): # Primitive ops for binary operations binary_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for unary ops -unary_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for unary ops +unary_ops: dict[str, list[PrimitiveDescription]] = {} builtin_names: dict[str, tuple[RType, str]] = {} @@ -327,8 +327,8 @@ def unary_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: - """Define a c function call op for an unary operation. +) -> PrimitiveDescription: + """Define a primitive op for an unary operation. This will be automatically generated by matching against the AST. @@ -338,19 +338,19 @@ def unary_op( if extra_int_constants is None: extra_int_constants = [] ops = unary_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, [arg_type], return_type, - None, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + var_arg_type=None, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, is_pure=is_pure, ) ops.append(desc) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index b1a712103e70..9082cc0136d9 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -182,13 +182,31 @@ def int_to_int(n): L0: return n -[case testIntUnaryPlus] +[case testIntUnaryOps] +def unary_minus(n: int) -> int: + x = -n + return x def unary_plus(n: int) -> int: x = +n return x +def unary_invert(n: int) -> int: + x = ~n + return x [out] +def unary_minus(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Negate(n) + x = r0 + return x def unary_plus(n): n, x :: int L0: x = n return x +def unary_invert(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Invert(n) + x = r0 + return x diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index 08b5a4648ca5..9c09f14e93dd 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -26,14 +26,18 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [registry.method_call_ops.values(), registry.unary_ops.values()]: + for old_values in [registry.method_call_ops.values()]: for old_ops in old_values: if isinstance(old_ops, CFunctionDescription): old_ops = [old_ops] for old_op in old_ops: check_name(old_op.c_function_name) - for values in [registry.binary_ops.values(), registry.function_ops.values()]: + for values in [ + registry.binary_ops.values(), + registry.unary_ops.values(), + registry.function_ops.values(), + ]: for ops in values: if isinstance(ops, PrimitiveDescription): ops = [ops] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 273ba409fac2..b97ad78d6970 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -154,6 +154,7 @@ def test_int_sub(self) -> None: ) def test_int_neg(self) -> None: + assert int_neg_op.c_function_name is not None self.assert_emit( CallC( int_neg_op.c_function_name, From c00f82f801b5d02c31c298fdcfafc40760357bfd Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:57:44 -0800 Subject: [PATCH 0922/1617] Always complain about invalid varargs and varkwargs (#18207) --- mypy/checkexpr.py | 20 +++++++++++--------- mypy/messages.py | 2 +- test-data/unit/check-functools.test | 4 +++- test-data/unit/check-kwargs.test | 3 +++ test-data/unit/check-varargs.test | 16 +++++++++------- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 11a9cffe18c3..247ff5edec13 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2481,6 +2481,17 @@ def check_argument_types( check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. mapper = ArgTypeExpander(self.argument_infer_context()) + + for arg_type, arg_kind in zip(arg_types, arg_kinds): + arg_type = get_proper_type(arg_type) + if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type): + self.msg.invalid_var_arg(arg_type, context) + if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type): + is_mapping = is_subtype( + arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") + ) + self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context) + for i, actuals in enumerate(formal_to_actual): orig_callee_arg_type = get_proper_type(callee.arg_types[i]) @@ -2573,15 +2584,6 @@ def check_argument_types( if actual_type is None: continue # Some kind of error was already reported. # Check that a *arg is valid as varargs. - if actual_kind == nodes.ARG_STAR and not self.is_valid_var_arg(actual_type): - self.msg.invalid_var_arg(actual_type, context) - if actual_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg( - actual_type - ): - is_mapping = is_subtype( - actual_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") - ) - self.msg.invalid_keyword_var_arg(actual_type, is_mapping, context) expanded_actual = mapper.expand_actual_type( actual_type, actual_kind, diff --git a/mypy/messages.py b/mypy/messages.py index d63df92c80a7..3ec1574827eb 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1394,7 +1394,7 @@ def could_not_infer_type_arguments( self.fail("Cannot infer function type argument", context) def invalid_var_arg(self, typ: Type, context: Context) -> None: - self.fail("List or tuple expected as variadic arguments", context) + self.fail("Expected iterable as variadic argument", context) def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) -> None: typ = get_proper_type(typ) diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index ea98a902d14b..c1868b3e3d72 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -215,7 +215,7 @@ def bar(*a: bytes, **k: int): p1("a", d="a", **k) p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str" p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str" - p1(*a) # E: List or tuple expected as variadic arguments + p1(*a) # E: Expected iterable as variadic argument def baz(a: int, b: int) -> int: ... @@ -223,6 +223,8 @@ def test_baz(xs: List[int]): p3 = functools.partial(baz, *xs) p3() p3(1) # E: Too many arguments for "baz" + + [builtins fixtures/dict.pyi] [case testFunctoolsPartialGeneric] diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 4beac047e278..3a8c7f5ba454 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -358,6 +358,9 @@ f(**d) # E: Keywords must be strings f(**A()) # E: Argument after ** must be a mapping, not "A" kwargs: Optional[Any] f(**kwargs) # E: Argument after ** must be a mapping, not "Optional[Any]" + +def g(a: int) -> None: pass +g(a=1, **4) # E: Argument after ** must be a mapping, not "int" [builtins fixtures/dict.pyi] [case testPassingKeywordVarArgsToNonVarArgsFunction] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 2495a883aa71..bb0e80acee1e 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -268,17 +268,19 @@ f(a, *(b, cc)) [builtins fixtures/tuple.pyi] [case testInvalidVarArg] -# flags: --no-strict-optional def f(a: 'A') -> None: pass class A: pass -a = None # type: A +a = A() -f(*None) -f(*a) # E: List or tuple expected as variadic arguments +f(*None) # E: Expected iterable as variadic argument +f(*a) # E: Expected iterable as variadic argument f(*(a,)) + +f(*4) # E: Expected iterable as variadic argument +f(a, *4) # E: Expected iterable as variadic argument [builtins fixtures/tuple.pyi] @@ -543,9 +545,9 @@ if int(): if int(): b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" if int(): - a, b = f(a, *a) # E: List or tuple expected as variadic arguments + a, b = f(a, *a) # E: Expected iterable as variadic argument if int(): - a, b = f(*a) # E: List or tuple expected as variadic arguments + a, b = f(*a) # E: Expected iterable as variadic argument if int(): a, a = f(*aa) @@ -737,7 +739,7 @@ bar(*good1) bar(*good2) bar(*good3) bar(*bad1) # E: Argument 1 to "bar" has incompatible type "*I[str]"; expected "float" -bar(*bad2) # E: List or tuple expected as variadic arguments +bar(*bad2) # E: Expected iterable as variadic argument [builtins fixtures/dict.pyi] -- Keyword arguments unpacking From 9405bfd9205ea369c11150907764fa46c03cb1f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 30 Nov 2024 17:33:45 -0800 Subject: [PATCH 0923/1617] Sync typeshed (#18218) Source commit: https://github.com/python/typeshed/commit/0a2da01946a406ede42e9c66f416a7e7758991d6 --- mypy/typeshed/stdlib/VERSIONS | 9 + mypy/typeshed/stdlib/_codecs.pyi | 5 +- mypy/typeshed/stdlib/_collections_abc.pyi | 3 + mypy/typeshed/stdlib/_csv.pyi | 56 +- mypy/typeshed/stdlib/_ctypes.pyi | 221 ++++-- mypy/typeshed/stdlib/_curses.pyi | 8 +- mypy/typeshed/stdlib/_curses_panel.pyi | 27 + mypy/typeshed/stdlib/_dbm.pyi | 13 +- mypy/typeshed/stdlib/_decimal.pyi | 9 +- mypy/typeshed/stdlib/_hashlib.pyi | 80 ++ mypy/typeshed/stdlib/_json.pyi | 1 + mypy/typeshed/stdlib/_multibytecodec.pyi | 44 ++ mypy/typeshed/stdlib/_posixsubprocess.pyi | 1 - mypy/typeshed/stdlib/_socket.pyi | 134 ++-- mypy/typeshed/stdlib/_thread.pyi | 8 + mypy/typeshed/stdlib/_threading_local.pyi | 4 + mypy/typeshed/stdlib/builtins.pyi | 23 +- mypy/typeshed/stdlib/bz2.pyi | 1 + mypy/typeshed/stdlib/codecs.pyi | 6 +- mypy/typeshed/stdlib/configparser.pyi | 26 +- mypy/typeshed/stdlib/contextlib.pyi | 12 +- mypy/typeshed/stdlib/crypt.pyi | 11 +- mypy/typeshed/stdlib/csv.pyi | 10 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 44 +- .../stdlib/ctypes/macholib/__init__.pyi | 1 + mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi | 8 + .../typeshed/stdlib/ctypes/macholib/dylib.pyi | 14 + .../stdlib/ctypes/macholib/framework.pyi | 14 + mypy/typeshed/stdlib/ctypes/util.pyi | 2 + mypy/typeshed/stdlib/ctypes/wintypes.pyi | 16 +- mypy/typeshed/stdlib/curses/__init__.pyi | 20 +- mypy/typeshed/stdlib/curses/panel.pyi | 23 +- mypy/typeshed/stdlib/datetime.pyi | 21 +- mypy/typeshed/stdlib/dbm/__init__.pyi | 4 +- mypy/typeshed/stdlib/decimal.pyi | 11 +- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 2 +- mypy/typeshed/stdlib/distutils/spawn.pyi | 6 +- mypy/typeshed/stdlib/doctest.pyi | 16 +- mypy/typeshed/stdlib/email/__init__.pyi | 38 +- mypy/typeshed/stdlib/email/_policybase.pyi | 2 + mypy/typeshed/stdlib/encodings/aliases.pyi | 1 + mypy/typeshed/stdlib/encodings/ascii.pyi | 30 + .../stdlib/encodings/base64_codec.pyi | 26 + mypy/typeshed/stdlib/encodings/big5.pyi | 23 + mypy/typeshed/stdlib/encodings/big5hkscs.pyi | 23 + mypy/typeshed/stdlib/encodings/bz2_codec.pyi | 26 + mypy/typeshed/stdlib/encodings/charmap.pyi | 33 + mypy/typeshed/stdlib/encodings/cp037.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1006.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1026.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1125.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1140.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1250.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1251.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1252.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1253.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1254.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1255.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1256.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1257.pyi | 21 + mypy/typeshed/stdlib/encodings/cp1258.pyi | 21 + mypy/typeshed/stdlib/encodings/cp273.pyi | 21 + mypy/typeshed/stdlib/encodings/cp424.pyi | 21 + mypy/typeshed/stdlib/encodings/cp437.pyi | 21 + mypy/typeshed/stdlib/encodings/cp500.pyi | 21 + mypy/typeshed/stdlib/encodings/cp720.pyi | 21 + mypy/typeshed/stdlib/encodings/cp737.pyi | 21 + mypy/typeshed/stdlib/encodings/cp775.pyi | 21 + mypy/typeshed/stdlib/encodings/cp850.pyi | 21 + mypy/typeshed/stdlib/encodings/cp852.pyi | 21 + mypy/typeshed/stdlib/encodings/cp855.pyi | 21 + mypy/typeshed/stdlib/encodings/cp856.pyi | 21 + mypy/typeshed/stdlib/encodings/cp857.pyi | 21 + mypy/typeshed/stdlib/encodings/cp858.pyi | 21 + mypy/typeshed/stdlib/encodings/cp860.pyi | 21 + mypy/typeshed/stdlib/encodings/cp861.pyi | 21 + mypy/typeshed/stdlib/encodings/cp862.pyi | 21 + mypy/typeshed/stdlib/encodings/cp863.pyi | 21 + mypy/typeshed/stdlib/encodings/cp864.pyi | 21 + mypy/typeshed/stdlib/encodings/cp865.pyi | 21 + mypy/typeshed/stdlib/encodings/cp866.pyi | 21 + mypy/typeshed/stdlib/encodings/cp869.pyi | 21 + mypy/typeshed/stdlib/encodings/cp874.pyi | 21 + mypy/typeshed/stdlib/encodings/cp875.pyi | 21 + mypy/typeshed/stdlib/encodings/cp932.pyi | 23 + mypy/typeshed/stdlib/encodings/cp949.pyi | 23 + mypy/typeshed/stdlib/encodings/cp950.pyi | 23 + .../stdlib/encodings/euc_jis_2004.pyi | 23 + .../stdlib/encodings/euc_jisx0213.pyi | 23 + mypy/typeshed/stdlib/encodings/euc_jp.pyi | 23 + mypy/typeshed/stdlib/encodings/euc_kr.pyi | 23 + mypy/typeshed/stdlib/encodings/gb18030.pyi | 23 + mypy/typeshed/stdlib/encodings/gb2312.pyi | 23 + mypy/typeshed/stdlib/encodings/gbk.pyi | 23 + mypy/typeshed/stdlib/encodings/hex_codec.pyi | 26 + mypy/typeshed/stdlib/encodings/hp_roman8.pyi | 21 + mypy/typeshed/stdlib/encodings/hz.pyi | 23 + mypy/typeshed/stdlib/encodings/idna.pyi | 26 + mypy/typeshed/stdlib/encodings/iso2022_jp.pyi | 23 + .../stdlib/encodings/iso2022_jp_1.pyi | 23 + .../stdlib/encodings/iso2022_jp_2.pyi | 23 + .../stdlib/encodings/iso2022_jp_2004.pyi | 23 + .../stdlib/encodings/iso2022_jp_3.pyi | 23 + .../stdlib/encodings/iso2022_jp_ext.pyi | 23 + mypy/typeshed/stdlib/encodings/iso2022_kr.pyi | 23 + mypy/typeshed/stdlib/encodings/iso8859_1.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_10.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_11.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_13.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_14.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_15.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_16.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_2.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_3.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_4.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_5.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_6.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_7.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_8.pyi | 21 + mypy/typeshed/stdlib/encodings/iso8859_9.pyi | 21 + mypy/typeshed/stdlib/encodings/johab.pyi | 23 + mypy/typeshed/stdlib/encodings/koi8_r.pyi | 21 + mypy/typeshed/stdlib/encodings/koi8_t.pyi | 21 + mypy/typeshed/stdlib/encodings/koi8_u.pyi | 21 + mypy/typeshed/stdlib/encodings/kz1048.pyi | 21 + mypy/typeshed/stdlib/encodings/latin_1.pyi | 30 + mypy/typeshed/stdlib/encodings/mac_arabic.pyi | 21 + .../stdlib/encodings/mac_centeuro.pyi | 21 + .../stdlib/encodings/mac_croatian.pyi | 21 + .../stdlib/encodings/mac_cyrillic.pyi | 21 + mypy/typeshed/stdlib/encodings/mac_farsi.pyi | 21 + mypy/typeshed/stdlib/encodings/mac_greek.pyi | 21 + .../typeshed/stdlib/encodings/mac_iceland.pyi | 21 + mypy/typeshed/stdlib/encodings/mac_latin2.pyi | 21 + mypy/typeshed/stdlib/encodings/mac_roman.pyi | 21 + .../stdlib/encodings/mac_romanian.pyi | 21 + .../typeshed/stdlib/encodings/mac_turkish.pyi | 21 + mypy/typeshed/stdlib/encodings/mbcs.pyi | 28 + mypy/typeshed/stdlib/encodings/oem.pyi | 28 + mypy/typeshed/stdlib/encodings/palmos.pyi | 21 + mypy/typeshed/stdlib/encodings/ptcp154.pyi | 21 + mypy/typeshed/stdlib/encodings/punycode.pyi | 33 + .../stdlib/encodings/quopri_codec.pyi | 26 + .../stdlib/encodings/raw_unicode_escape.pyi | 34 + mypy/typeshed/stdlib/encodings/rot_13.pyi | 23 + mypy/typeshed/stdlib/encodings/shift_jis.pyi | 23 + .../stdlib/encodings/shift_jis_2004.pyi | 23 + .../stdlib/encodings/shift_jisx0213.pyi | 23 + mypy/typeshed/stdlib/encodings/tis_620.pyi | 21 + mypy/typeshed/stdlib/encodings/undefined.pyi | 20 + .../stdlib/encodings/unicode_escape.pyi | 34 + mypy/typeshed/stdlib/encodings/utf_16.pyi | 20 + mypy/typeshed/stdlib/encodings/utf_16_be.pyi | 26 + mypy/typeshed/stdlib/encodings/utf_16_le.pyi | 26 + mypy/typeshed/stdlib/encodings/utf_32.pyi | 20 + mypy/typeshed/stdlib/encodings/utf_32_be.pyi | 26 + mypy/typeshed/stdlib/encodings/utf_32_le.pyi | 26 + mypy/typeshed/stdlib/encodings/utf_7.pyi | 26 + mypy/typeshed/stdlib/encodings/utf_8.pyi | 9 +- mypy/typeshed/stdlib/encodings/uu_codec.pyi | 28 + mypy/typeshed/stdlib/encodings/zlib_codec.pyi | 26 + mypy/typeshed/stdlib/fcntl.pyi | 4 +- mypy/typeshed/stdlib/gettext.pyi | 2 + mypy/typeshed/stdlib/hashlib.pyi | 93 +-- mypy/typeshed/stdlib/hmac.pyi | 15 +- .../stdlib/importlib/resources/abc.pyi | 2 + mypy/typeshed/stdlib/ipaddress.pyi | 53 +- mypy/typeshed/stdlib/json/scanner.pyi | 3 + mypy/typeshed/stdlib/modulefinder.pyi | 2 + .../stdlib/multiprocessing/context.pyi | 3 +- .../stdlib/multiprocessing/sharedctypes.pyi | 3 +- .../stdlib/multiprocessing/synchronize.pyi | 14 +- mypy/typeshed/stdlib/os/__init__.pyi | 550 ++++++++++++-- mypy/typeshed/stdlib/platform.pyi | 44 +- mypy/typeshed/stdlib/posix.pyi | 40 +- mypy/typeshed/stdlib/pstats.pyi | 40 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 4 +- mypy/typeshed/stdlib/sched.pyi | 7 +- mypy/typeshed/stdlib/socket.pyi | 683 ++++++++++++++++-- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 4 +- mypy/typeshed/stdlib/sqlite3/dump.pyi | 2 + mypy/typeshed/stdlib/ssl.pyi | 4 +- mypy/typeshed/stdlib/sys/__init__.pyi | 120 ++- mypy/typeshed/stdlib/termios.pyi | 180 +++-- mypy/typeshed/stdlib/tkinter/__init__.pyi | 153 ++-- mypy/typeshed/stdlib/tkinter/font.pyi | 4 +- mypy/typeshed/stdlib/tokenize.pyi | 4 +- mypy/typeshed/stdlib/turtle.pyi | 19 +- mypy/typeshed/stdlib/typing.pyi | 18 +- mypy/typeshed/stdlib/typing_extensions.pyi | 13 +- mypy/typeshed/stdlib/webbrowser.pyi | 1 + mypy/typeshed/stdlib/wsgiref/types.pyi | 4 +- mypy/typeshed/stdlib/xml/__init__.pyi | 4 +- mypy/typeshed/stdlib/zipfile/_path.pyi | 2 + mypy/typeshed/stdlib/zlib.pyi | 30 +- 195 files changed, 5162 insertions(+), 616 deletions(-) create mode 100644 mypy/typeshed/stdlib/_curses_panel.pyi create mode 100644 mypy/typeshed/stdlib/_hashlib.pyi create mode 100644 mypy/typeshed/stdlib/_multibytecodec.pyi create mode 100644 mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi create mode 100644 mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi create mode 100644 mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi create mode 100644 mypy/typeshed/stdlib/ctypes/macholib/framework.pyi create mode 100644 mypy/typeshed/stdlib/encodings/aliases.pyi create mode 100644 mypy/typeshed/stdlib/encodings/ascii.pyi create mode 100644 mypy/typeshed/stdlib/encodings/base64_codec.pyi create mode 100644 mypy/typeshed/stdlib/encodings/big5.pyi create mode 100644 mypy/typeshed/stdlib/encodings/big5hkscs.pyi create mode 100644 mypy/typeshed/stdlib/encodings/bz2_codec.pyi create mode 100644 mypy/typeshed/stdlib/encodings/charmap.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp037.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1006.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1026.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1125.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1140.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1250.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1251.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1252.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1253.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1254.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1255.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1256.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1257.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp1258.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp273.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp424.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp437.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp500.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp720.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp737.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp775.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp850.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp852.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp855.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp856.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp857.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp858.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp860.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp861.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp862.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp863.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp864.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp865.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp866.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp869.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp874.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp875.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp932.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp949.pyi create mode 100644 mypy/typeshed/stdlib/encodings/cp950.pyi create mode 100644 mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi create mode 100644 mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi create mode 100644 mypy/typeshed/stdlib/encodings/euc_jp.pyi create mode 100644 mypy/typeshed/stdlib/encodings/euc_kr.pyi create mode 100644 mypy/typeshed/stdlib/encodings/gb18030.pyi create mode 100644 mypy/typeshed/stdlib/encodings/gb2312.pyi create mode 100644 mypy/typeshed/stdlib/encodings/gbk.pyi create mode 100644 mypy/typeshed/stdlib/encodings/hex_codec.pyi create mode 100644 mypy/typeshed/stdlib/encodings/hp_roman8.pyi create mode 100644 mypy/typeshed/stdlib/encodings/hz.pyi create mode 100644 mypy/typeshed/stdlib/encodings/idna.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso2022_kr.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_1.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_10.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_11.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_13.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_14.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_15.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_16.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_2.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_3.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_4.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_5.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_6.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_7.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_8.pyi create mode 100644 mypy/typeshed/stdlib/encodings/iso8859_9.pyi create mode 100644 mypy/typeshed/stdlib/encodings/johab.pyi create mode 100644 mypy/typeshed/stdlib/encodings/koi8_r.pyi create mode 100644 mypy/typeshed/stdlib/encodings/koi8_t.pyi create mode 100644 mypy/typeshed/stdlib/encodings/koi8_u.pyi create mode 100644 mypy/typeshed/stdlib/encodings/kz1048.pyi create mode 100644 mypy/typeshed/stdlib/encodings/latin_1.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_arabic.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_centeuro.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_croatian.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_farsi.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_greek.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_iceland.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_latin2.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_roman.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_romanian.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mac_turkish.pyi create mode 100644 mypy/typeshed/stdlib/encodings/mbcs.pyi create mode 100644 mypy/typeshed/stdlib/encodings/oem.pyi create mode 100644 mypy/typeshed/stdlib/encodings/palmos.pyi create mode 100644 mypy/typeshed/stdlib/encodings/ptcp154.pyi create mode 100644 mypy/typeshed/stdlib/encodings/punycode.pyi create mode 100644 mypy/typeshed/stdlib/encodings/quopri_codec.pyi create mode 100644 mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi create mode 100644 mypy/typeshed/stdlib/encodings/rot_13.pyi create mode 100644 mypy/typeshed/stdlib/encodings/shift_jis.pyi create mode 100644 mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi create mode 100644 mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi create mode 100644 mypy/typeshed/stdlib/encodings/tis_620.pyi create mode 100644 mypy/typeshed/stdlib/encodings/undefined.pyi create mode 100644 mypy/typeshed/stdlib/encodings/unicode_escape.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_16.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_16_be.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_16_le.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_32.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_32_be.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_32_le.pyi create mode 100644 mypy/typeshed/stdlib/encodings/utf_7.pyi create mode 100644 mypy/typeshed/stdlib/encodings/uu_codec.pyi create mode 100644 mypy/typeshed/stdlib/encodings/zlib_codec.pyi create mode 100644 mypy/typeshed/stdlib/json/scanner.pyi create mode 100644 mypy/typeshed/stdlib/sqlite3/dump.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 702fdbab75c0..7ff14c55d3a8 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -33,6 +33,7 @@ _contextvars: 3.7- _csv: 3.0- _ctypes: 3.0- _curses: 3.0- +_curses_panel: 3.0- _dbm: 3.0- _decimal: 3.3- _dummy_thread: 3.0-3.8 @@ -40,6 +41,7 @@ _dummy_threading: 3.0-3.8 _frozen_importlib: 3.0- _frozen_importlib_external: 3.5- _gdbm: 3.0- +_hashlib: 3.0- _heapq: 3.0- _imp: 3.0- _interpchannels: 3.13- @@ -52,6 +54,7 @@ _lsprof: 3.0- _lzma: 3.3- _markupbase: 3.0- _msi: 3.0-3.12 +_multibytecodec: 3.0- _operator: 3.4- _osx_support: 3.0- _posixsubprocess: 3.2- @@ -139,6 +142,12 @@ doctest: 3.0- dummy_threading: 3.0-3.8 email: 3.0- encodings: 3.0- +encodings.cp1125: 3.4- +encodings.cp273: 3.4- +encodings.cp858: 3.2- +encodings.koi8_t: 3.5- +encodings.kz1048: 3.5- +encodings.mac_centeuro: 3.0-3.8 ensurepip: 3.0- enum: 3.4- errno: 3.0- diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index ecf874d33ddd..11c5d58a855b 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -2,10 +2,13 @@ import codecs import sys from _typeshed import ReadableBuffer from collections.abc import Callable -from typing import Literal, overload +from typing import Literal, final, overload, type_check_only from typing_extensions import TypeAlias # This type is not exposed; it is defined in unicodeobject.c +# At runtime it calls itself builtins.EncodingMap +@final +@type_check_only class _EncodingMap: def size(self) -> int: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 8b1ac9c7eb8b..bf7f2991f9a4 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -73,6 +73,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + def __reversed__(self) -> Iterator[_KT_co]: ... if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... if sys.version_info >= (3, 10): @@ -81,6 +82,7 @@ class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented + def __reversed__(self) -> Iterator[_VT_co]: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -88,6 +90,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 0e206a63b260..afa2870be158 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,9 +1,9 @@ import csv import sys from _typeshed import SupportsWrite -from collections.abc import Iterable, Iterator -from typing import Any, Final -from typing_extensions import TypeAlias +from collections.abc import Iterable +from typing import Any, Final, type_check_only +from typing_extensions import Self, TypeAlias __version__: Final[str] @@ -45,17 +45,47 @@ class Dialect: strict: bool = False, ) -> None: ... -class _reader(Iterator[list[str]]): - @property - def dialect(self) -> Dialect: ... - line_num: int - def __next__(self) -> list[str]: ... +if sys.version_info >= (3, 10): + # This class calls itself _csv.reader. + class Reader: + @property + def dialect(self) -> Dialect: ... + line_num: int + def __iter__(self) -> Self: ... + def __next__(self) -> list[str]: ... -class _writer: - @property - def dialect(self) -> Dialect: ... - def writerow(self, row: Iterable[Any]) -> Any: ... - def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... + # This class calls itself _csv.writer. + class Writer: + @property + def dialect(self) -> Dialect: ... + if sys.version_info >= (3, 13): + def writerow(self, row: Iterable[Any], /) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]], /) -> None: ... + else: + def writerow(self, row: Iterable[Any]) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... + + # For the return types below. + # These aliases can be removed when typeshed drops support for 3.9. + _reader = Reader + _writer = Writer +else: + # This class is not exposed. It calls itself _csv.reader. + @type_check_only + class _reader: + @property + def dialect(self) -> Dialect: ... + line_num: int + def __iter__(self) -> Self: ... + def __next__(self) -> list[str]: ... + + # This class is not exposed. It calls itself _csv.writer. + @type_check_only + class _writer: + @property + def dialect(self) -> Dialect: ... + def writerow(self, row: Iterable[Any]) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... def writer( csvfile: SupportsWrite[str], diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 91d95a154f8e..ecb07a29bb75 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -1,9 +1,10 @@ +import _typeshed import sys -from _typeshed import ReadableBuffer, WriteableBuffer +from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from abc import abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p -from typing import Any, ClassVar, Generic, TypeVar, overload +from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -47,46 +48,79 @@ if sys.platform == "win32": def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ... def FreeLibrary(handle: int, /) -> None: ... -class _CDataMeta(type): - # By default mypy complains about the following two methods, because strictly speaking cls - # might not be a Type[_CT]. However this can never actually happen, because the only class that - # uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here. - def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +else: + def dlclose(handle: int, /) -> None: ... + # The default for flag is RTLD_GLOBAL|RTLD_LOCAL, which is platform dependent. + def dlopen(name: StrOrBytesPath, flag: int = ..., /) -> int: ... + def dlsym(handle: int, name: str, /) -> int: ... -class _CData(metaclass=_CDataMeta): +if sys.version_info >= (3, 13): + # This class is not exposed. It calls itself _ctypes.CType_Type. + @type_check_only + class _CType_Type(type): + # By default mypy complains about the following two methods, because strictly speaking cls + # might not be a Type[_CT]. However this doesn't happen because this is only a + # metaclass for subclasses of _CData. + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + + _CTypeBaseType = _CType_Type + +else: + _CTypeBaseType = type + +# This class is not exposed. +@type_check_only +class _CData: _b_base_: int _b_needsfree_: bool _objects: Mapping[Any, int] | None - # At runtime the following classmethods are available only on classes, not - # on instances. This can't be reflected properly in the type system: - # - # Structure.from_buffer(...) # valid at runtime - # Structure(...).from_buffer(...) # invalid at runtime - # - @classmethod - def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_address(cls, address: int) -> Self: ... - @classmethod - def from_param(cls, value: Any, /) -> Self | _CArgObject: ... - @classmethod - def in_dll(cls, library: CDLL, name: str) -> Self: ... def __buffer__(self, flags: int, /) -> memoryview: ... - def __release_buffer__(self, buffer: memoryview, /) -> None: ... + def __ctypes_from_outparam__(self, /) -> Self: ... -class _SimpleCData(_CData, Generic[_T]): +# this is a union of all the subclasses of _CData, which is useful because of +# the methods that are present on each of those subclasses which are not present +# on _CData itself. +_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any] + +# This class is not exposed. It calls itself _ctypes.PyCSimpleType. +@type_check_only +class _PyCSimpleType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType): value: _T # The TypeVar can be unsolved here, # but we can't use overloads without creating many, many mypy false-positive errors def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] + def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override] class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... -class _Pointer(_PointerLike, _CData, Generic[_CT]): +# This type is not exposed. It calls itself _ctypes.PyCPointerType. +@type_check_only +class _PyCPointerType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + def set_type(self, type: Any, /) -> None: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType): _type_: type[_CT] contents: _CT @overload @@ -105,16 +139,32 @@ def POINTER(type: None, /) -> type[c_void_p]: ... def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ... def pointer(obj: _CT, /) -> _Pointer[_CT]: ... +# This class is not exposed. It calls itself _ctypes.CArgObject. +@final +@type_check_only class _CArgObject: ... -def byref(obj: _CData, offset: int = ...) -> _CArgObject: ... +def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ... -_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData] +_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType] _PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any] -class CFuncPtr(_PointerLike, _CData): - restype: type[_CData] | Callable[[int], Any] | None - argtypes: Sequence[type[_CData]] +# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType. +@type_check_only +class _PyCFuncPtrType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType): + restype: type[_CDataType] | Callable[[int], Any] | None + argtypes: Sequence[type[_CDataType]] errcheck: _ECT # Abstract attribute that must be defined on subclasses _flags_: ClassVar[int] @@ -129,7 +179,7 @@ class CFuncPtr(_PointerLike, _CData): if sys.platform == "win32": @overload def __init__( - self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., / + self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / ) -> None: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @@ -137,30 +187,95 @@ class CFuncPtr(_PointerLike, _CData): _GetT = TypeVar("_GetT") _SetT = TypeVar("_SetT") +# This class is not exposed. It calls itself _ctypes.CField. +@final +@type_check_only class _CField(Generic[_CT, _GetT, _SetT]): offset: int size: int - @overload - def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... - @overload - def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + if sys.version_info >= (3, 10): + @overload + def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ... + else: + @overload + def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + def __set__(self, instance: Any, value: _SetT, /) -> None: ... -class _StructUnionMeta(_CDataMeta): - _fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]] - _pack_: int - _anonymous_: Sequence[str] +# This class is not exposed. It calls itself _ctypes.UnionType. +@type_check_only +class _UnionType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Union subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Union(_CData, metaclass=_UnionType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] + + def __init__(self, *args: Any, **kw: Any) -> None: ... + def __getattr__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + +# This class is not exposed. It calls itself _ctypes.PyCStructType. +@type_check_only +class _PyCStructType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Structure subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. + def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Structure(_CData, metaclass=_PyCStructType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] -class _StructUnionBase(_CData, metaclass=_StructUnionMeta): def __init__(self, *args: Any, **kw: Any) -> None: ... def __getattr__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... -class Union(_StructUnionBase): ... -class Structure(_StructUnionBase): ... +# This class is not exposed. It calls itself _ctypes.PyCArrayType. +@type_check_only +class _PyCArrayType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] -class Array(_CData, Generic[_CT]): +class Array(_CData, Generic[_CT], metaclass=_PyCArrayType): @property @abstractmethod def _length_(self) -> int: ... @@ -201,9 +316,15 @@ class Array(_CData, Generic[_CT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -def addressof(obj: _CData, /) -> int: ... -def alignment(obj_or_type: _CData | type[_CData], /) -> int: ... +def addressof(obj: _CData | _CDataType, /) -> int: ... +def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... def get_errno() -> int: ... -def resize(obj: _CData, size: int, /) -> None: ... +def resize(obj: _CData | _CDataType, size: int, /) -> None: ... def set_errno(value: int, /) -> int: ... -def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ... +def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... +def PyObj_FromPtr(address: int, /) -> Any: ... +def Py_DECREF(o: _T, /) -> _T: ... +def Py_INCREF(o: _T, /) -> _T: ... +def buffer_info(o: _CData | _CDataType | type[_CData | _CDataType], /) -> tuple[str, int, tuple[int, ...]]: ... +def call_cdeclfunction(address: int, arguments: tuple[Any, ...], /) -> Any: ... +def call_function(address: int, arguments: tuple[Any, ...], /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 80075d77e8e6..9e06a1414da5 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadOnlyBuffer, SupportsRead -from typing import IO, Any, NamedTuple, final, overload +from curses import _ncurses_version +from typing import IO, Any, final, overload from typing_extensions import TypeAlias # NOTE: This module is ordinarily only available on Unix, but the windows-curses @@ -549,9 +550,4 @@ class window: # undocumented @overload def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... -class _ncurses_version(NamedTuple): - major: int - minor: int - patch: int - ncurses_version: _ncurses_version diff --git a/mypy/typeshed/stdlib/_curses_panel.pyi b/mypy/typeshed/stdlib/_curses_panel.pyi new file mode 100644 index 000000000000..ddec22236b96 --- /dev/null +++ b/mypy/typeshed/stdlib/_curses_panel.pyi @@ -0,0 +1,27 @@ +from _curses import window +from typing import final + +__version__: str +version: str + +class error(Exception): ... + +@final +class panel: + def above(self) -> panel: ... + def below(self) -> panel: ... + def bottom(self) -> None: ... + def hidden(self) -> bool: ... + def hide(self) -> None: ... + def move(self, y: int, x: int, /) -> None: ... + def replace(self, win: window, /) -> None: ... + def set_userptr(self, obj: object, /) -> None: ... + def show(self) -> None: ... + def top(self) -> None: ... + def userptr(self) -> object: ... + def window(self) -> window: ... + +def bottom_panel() -> panel: ... +def new_panel(win: window, /) -> panel: ... +def top_panel() -> panel: ... +def update_panels() -> panel: ... diff --git a/mypy/typeshed/stdlib/_dbm.pyi b/mypy/typeshed/stdlib/_dbm.pyi index 4113a7e3ffb9..7e53cca3c704 100644 --- a/mypy/typeshed/stdlib/_dbm.pyi +++ b/mypy/typeshed/stdlib/_dbm.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadOnlyBuffer, StrOrBytesPath from types import TracebackType -from typing import TypeVar, overload +from typing import TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias if sys.platform != "win32": @@ -13,6 +13,8 @@ if sys.platform != "win32": library: str # Actual typename dbm, not exposed by the implementation + @final + @type_check_only class _dbm: def close(self) -> None: ... if sys.version_info >= (3, 13): @@ -22,18 +24,17 @@ if sys.platform != "win32": def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... def __delitem__(self, key: _KeyType) -> None: ... def __len__(self) -> int: ... - def __del__(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... @overload - def get(self, k: _KeyType) -> bytes | None: ... + def get(self, k: _KeyType, /) -> bytes | None: ... @overload - def get(self, k: _KeyType, default: _T) -> bytes | _T: ... + def get(self, k: _KeyType, default: _T, /) -> bytes | _T: ... def keys(self) -> list[bytes]: ... - def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... - # Don't exist at runtime + def setdefault(self, k: _KeyType, default: _ValueType = ..., /) -> bytes: ... + # This isn't true, but the class can't be instantiated. See #13024 __new__: None # type: ignore[assignment] __init__: None # type: ignore[assignment] diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 9fcc08dbb95d..cdd0268a1bdf 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -17,20 +17,13 @@ from decimal import ( Rounded as Rounded, Subnormal as Subnormal, Underflow as Underflow, + _ContextManager, ) -from types import TracebackType from typing import Final from typing_extensions import TypeAlias _TrapType: TypeAlias = type[DecimalException] -class _ContextManager: - new_context: Context - saved_context: Context - def __init__(self, new_context: Context) -> None: ... - def __enter__(self) -> Context: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... - __version__: Final[str] __libmpdec_version__: Final[str] diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi new file mode 100644 index 000000000000..5cf85e4cacaa --- /dev/null +++ b/mypy/typeshed/stdlib/_hashlib.pyi @@ -0,0 +1,80 @@ +import sys +from _typeshed import ReadableBuffer +from collections.abc import Callable +from types import ModuleType +from typing import AnyStr, final, overload +from typing_extensions import Self, TypeAlias + +_DigestMod: TypeAlias = str | Callable[[], HASH] | ModuleType | None + +openssl_md_meth_names: frozenset[str] + +class HASH: + @property + def digest_size(self) -> int: ... + @property + def block_size(self) -> int: ... + @property + def name(self) -> str: ... + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, obj: ReadableBuffer, /) -> None: ... + +if sys.version_info >= (3, 10): + class UnsupportedDigestmodError(ValueError): ... + +if sys.version_info >= (3, 9): + class HASHXOF(HASH): + def digest(self, length: int) -> bytes: ... # type: ignore[override] + def hexdigest(self, length: int) -> str: ... # type: ignore[override] + + @final + class HMAC: + @property + def digest_size(self) -> int: ... + @property + def block_size(self) -> int: ... + @property + def name(self) -> str: ... + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, msg: ReadableBuffer) -> None: ... + + @overload + def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... + @overload + def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... + def get_fips_mode() -> int: ... + def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ... + def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ... + def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ... + +else: + def new(name: str, string: ReadableBuffer = b"") -> HASH: ... + def openssl_md5(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha1(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha224(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha256(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha384(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha512(string: ReadableBuffer = b"") -> HASH: ... + +def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ... +def pbkdf2_hmac( + hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None +) -> bytes: ... +def scrypt( + password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 +) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index 069fb6eac4bf..e1c7c52ca3b1 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -45,5 +45,6 @@ class make_scanner: def __init__(self, context: make_scanner) -> None: ... def __call__(self, string: str, index: int) -> tuple[Any, int]: ... +def encode_basestring(s: str, /) -> str: ... def encode_basestring_ascii(s: str, /) -> str: ... def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/_multibytecodec.pyi b/mypy/typeshed/stdlib/_multibytecodec.pyi new file mode 100644 index 000000000000..7e408f2aa30e --- /dev/null +++ b/mypy/typeshed/stdlib/_multibytecodec.pyi @@ -0,0 +1,44 @@ +from _typeshed import ReadableBuffer +from codecs import _ReadableStream, _WritableStream +from collections.abc import Iterable +from typing import final, type_check_only + +# This class is not exposed. It calls itself _multibytecodec.MultibyteCodec. +@final +@type_check_only +class _MultibyteCodec: + def decode(self, input: ReadableBuffer, errors: str | None = None) -> str: ... + def encode(self, input: str, errors: str | None = None) -> bytes: ... + +class MultibyteIncrementalDecoder: + errors: str + def __init__(self, errors: str = "strict") -> None: ... + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + def getstate(self) -> tuple[bytes, int]: ... + def reset(self) -> None: ... + def setstate(self, state: tuple[bytes, int], /) -> None: ... + +class MultibyteIncrementalEncoder: + errors: str + def __init__(self, errors: str = "strict") -> None: ... + def encode(self, input: str, final: bool = False) -> bytes: ... + def getstate(self) -> int: ... + def reset(self) -> None: ... + def setstate(self, state: int, /) -> None: ... + +class MultibyteStreamReader: + errors: str + stream: _ReadableStream + def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ... + def read(self, sizeobj: int | None = None, /) -> str: ... + def readline(self, sizeobj: int | None = None, /) -> str: ... + def readlines(self, sizehintobj: int | None = None, /) -> list[str]: ... + def reset(self) -> None: ... + +class MultibyteStreamWriter: + errors: str + stream: _WritableStream + def __init__(self, stream: _WritableStream, errors: str = "strict") -> None: ... + def reset(self) -> None: ... + def write(self, strobj: str, /) -> None: ... + def writelines(self, lines: Iterable[str], /) -> None: ... diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 3df56d9a3d03..df05dcd80be8 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -4,7 +4,6 @@ from collections.abc import Callable, Sequence from typing import SupportsIndex if sys.platform != "win32": - def cloexec_pipe() -> tuple[int, int]: ... def fork_exec( args: Sequence[StrOrBytesPath] | None, executable_list: Sequence[bytes], diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 7e4cf4e0364a..36bc5c31c646 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Iterable from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout from typing import Any, SupportsIndex, overload -from typing_extensions import TypeAlias +from typing_extensions import CapsuleType, TypeAlias _CMSG: TypeAlias = tuple[int, int, bytes] _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] @@ -72,7 +72,8 @@ SO_SNDBUF: int SO_SNDLOWAT: int SO_SNDTIMEO: int SO_TYPE: int -SO_USELOOPBACK: int +if sys.platform != "linux": + SO_USELOOPBACK: int if sys.platform == "win32": SO_EXCLUSIVEADDRUSE: int if sys.platform != "win32": @@ -87,7 +88,10 @@ if sys.platform != "win32" and sys.platform != "darwin": SO_PEERSEC: int SO_PRIORITY: int SO_PROTOCOL: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": SO_SETFIB: int +if sys.platform == "linux" and sys.version_info >= (3, 13): + SO_BINDTOIFINDEX: int SOMAXCONN: int @@ -99,27 +103,32 @@ MSG_TRUNC: int MSG_WAITALL: int if sys.platform != "win32": MSG_DONTWAIT: int - MSG_EOF: int MSG_EOR: int MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not if sys.platform != "darwin": - MSG_BCAST: int MSG_ERRQUEUE: int +if sys.platform == "win32": + MSG_BCAST: int MSG_MCAST: int if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG: int MSG_CMSG_CLOEXEC: int MSG_CONFIRM: int - MSG_ETAG: int MSG_FASTOPEN: int MSG_MORE: int +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF: int +if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin": MSG_NOTIFICATION: int + MSG_BTAG: int # Not FreeBSD either + MSG_ETAG: int # Not FreeBSD either SOL_IP: int SOL_SOCKET: int SOL_TCP: int SOL_UDP: int if sys.platform != "win32" and sys.platform != "darwin": + # Defined in socket.h for Linux, but these aren't always present for + # some reason. SOL_ATALK: int SOL_AX25: int SOL_HCI: int @@ -128,10 +137,11 @@ if sys.platform != "win32" and sys.platform != "darwin": SOL_ROSE: int if sys.platform != "win32": - SCM_CREDS: int SCM_RIGHTS: int if sys.platform != "win32" and sys.platform != "darwin": SCM_CREDENTIALS: int +if sys.platform != "win32" and sys.platform != "linux": + SCM_CREDS: int IPPROTO_ICMP: int IPPROTO_IP: int @@ -143,21 +153,22 @@ IPPROTO_DSTOPTS: int IPPROTO_EGP: int IPPROTO_ESP: int IPPROTO_FRAGMENT: int -IPPROTO_GGP: int IPPROTO_HOPOPTS: int IPPROTO_ICMPV6: int IPPROTO_IDP: int IPPROTO_IGMP: int -IPPROTO_IPV4: int IPPROTO_IPV6: int -IPPROTO_MAX: int -IPPROTO_ND: int IPPROTO_NONE: int IPPROTO_PIM: int IPPROTO_PUP: int IPPROTO_ROUTING: int IPPROTO_SCTP: int -if sys.platform != "darwin": +if sys.platform != "linux": + IPPROTO_GGP: int + IPPROTO_IPV4: int + IPPROTO_MAX: int + IPPROTO_ND: int +if sys.platform == "win32": IPPROTO_CBT: int IPPROTO_ICLFXBM: int IPPROTO_IGP: int @@ -166,18 +177,19 @@ if sys.platform != "darwin": IPPROTO_RDP: int IPPROTO_ST: int if sys.platform != "win32": - IPPROTO_EON: int IPPROTO_GRE: int - IPPROTO_HELLO: int - IPPROTO_IPCOMP: int IPPROTO_IPIP: int IPPROTO_RSVP: int IPPROTO_TP: int +if sys.platform != "win32" and sys.platform != "linux": + IPPROTO_EON: int + IPPROTO_HELLO: int + IPPROTO_IPCOMP: int IPPROTO_XTP: int -if sys.platform != "win32" and sys.platform != "darwin": - IPPROTO_BIP: int - IPPROTO_MOBILE: int - IPPROTO_VRRP: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + IPPROTO_BIP: int # Not FreeBSD either + IPPROTO_MOBILE: int # Not FreeBSD either + IPPROTO_VRRP: int # Not FreeBSD either if sys.version_info >= (3, 9) and sys.platform == "linux": # Availability: Linux >= 2.6.20, FreeBSD >= 10.1 IPPROTO_UDPLITE: int @@ -202,11 +214,10 @@ IP_MULTICAST_IF: int IP_MULTICAST_LOOP: int IP_MULTICAST_TTL: int IP_OPTIONS: int -IP_RECVDSTADDR: int +if sys.platform != "linux": + IP_RECVDSTADDR: int if sys.version_info >= (3, 10): IP_RECVTOS: int -elif sys.platform != "win32" and sys.platform != "darwin": - IP_RECVTOS: int IP_TOS: int IP_TTL: int if sys.platform != "win32": @@ -218,6 +229,7 @@ if sys.platform != "win32": IP_RETOPTS: int if sys.platform != "win32" and sys.platform != "darwin": IP_TRANSPARENT: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11): IP_BIND_ADDRESS_NO_PORT: int if sys.version_info >= (3, 12): IP_ADD_SOURCE_MEMBERSHIP: int @@ -255,6 +267,9 @@ if sys.platform != "win32": IPV6_RECVPATHMTU: int IPV6_RECVPKTINFO: int IPV6_RTHDRDSTOPTS: int + +if sys.platform != "win32" and sys.platform != "linux": + if sys.version_info >= (3, 9) or sys.platform != "darwin": IPV6_USE_MIN_MTU: int EAI_AGAIN: int @@ -268,11 +283,12 @@ EAI_SERVICE: int EAI_SOCKTYPE: int if sys.platform != "win32": EAI_ADDRFAMILY: int + EAI_OVERFLOW: int + EAI_SYSTEM: int +if sys.platform != "win32" and sys.platform != "linux": EAI_BADHINTS: int EAI_MAX: int - EAI_OVERFLOW: int EAI_PROTOCOL: int - EAI_SYSTEM: int AI_ADDRCONFIG: int AI_ALL: int @@ -281,7 +297,7 @@ AI_NUMERICHOST: int AI_NUMERICSERV: int AI_PASSIVE: int AI_V4MAPPED: int -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT: int AI_MASK: int AI_V4MAPPED_CFG: int @@ -293,6 +309,8 @@ NI_NAMEREQD: int NI_NOFQDN: int NI_NUMERICHOST: int NI_NUMERICSERV: int +if sys.platform == "linux" and sys.version_info >= (3, 13): + NI_IDN: int TCP_FASTOPEN: int TCP_KEEPCNT: int @@ -318,6 +336,27 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_SYNCNT: int TCP_USER_TIMEOUT: int TCP_WINDOW_CLAMP: int +if sys.platform == "linux" and sys.version_info >= (3, 12): + TCP_CC_INFO: int + TCP_FASTOPEN_CONNECT: int + TCP_FASTOPEN_KEY: int + TCP_FASTOPEN_NO_COOKIE: int + TCP_INQ: int + TCP_MD5SIG: int + TCP_MD5SIG_EXT: int + TCP_QUEUE_SEQ: int + TCP_REPAIR: int + TCP_REPAIR_OPTIONS: int + TCP_REPAIR_QUEUE: int + TCP_REPAIR_WINDOW: int + TCP_SAVED_SYN: int + TCP_SAVE_SYN: int + TCP_THIN_DUPACK: int + TCP_THIN_LINEAR_TIMEOUTS: int + TCP_TIMESTAMP: int + TCP_TX_DELAY: int + TCP_ULP: int + TCP_ZEROCOPY_RECEIVE: int # -------------------- # Specifically documented constants @@ -334,12 +373,13 @@ if sys.platform == "linux": CAN_ERR_FLAG: int CAN_ERR_MASK: int CAN_RAW: int - CAN_RAW_ERR_FILTER: int CAN_RAW_FILTER: int CAN_RAW_LOOPBACK: int CAN_RAW_RECV_OWN_MSGS: int CAN_RTR_FLAG: int CAN_SFF_MASK: int + if sys.version_info < (3, 11): + CAN_RAW_ERR_FILTER: int if sys.platform == "linux": # Availability: Linux >= 2.6.25 @@ -437,12 +477,13 @@ if sys.platform == "linux": AF_RDS: int PF_RDS: int SOL_RDS: int + # These are present in include/linux/rds.h but don't always show up + # here. RDS_CANCEL_SENT_TO: int RDS_CMSG_RDMA_ARGS: int RDS_CMSG_RDMA_DEST: int RDS_CMSG_RDMA_MAP: int RDS_CMSG_RDMA_STATUS: int - RDS_CMSG_RDMA_UPDATE: int RDS_CONG_MONITOR: int RDS_FREE_MR: int RDS_GET_MR: int @@ -456,6 +497,10 @@ if sys.platform == "linux": RDS_RDMA_USE_ONCE: int RDS_RECVERR: int + # This is supported by CPython but doesn't seem to be a real thing. + # The closest existing constant in rds.h is RDS_CMSG_CONG_UPDATE + # RDS_CMSG_RDMA_UPDATE: int + if sys.platform == "win32": SIO_RCVALL: int SIO_KEEPALIVE_VALS: int @@ -522,16 +567,17 @@ if sys.platform == "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): # Documented as only available on BSD, macOS, but empirically sometimes # available on Windows - AF_LINK: int + if sys.platform != "linux": + AF_LINK: int has_ipv6: bool -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): BDADDR_ANY: str BDADDR_LOCAL: str -if sys.platform != "win32" and sys.platform != "darwin": +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": HCI_FILTER: int # not in NetBSD or DragonFlyBSD HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD @@ -580,36 +626,37 @@ if sys.version_info >= (3, 12): if sys.platform == "linux": # Netlink is defined by Linux AF_NETLINK: int - NETLINK_ARPD: int NETLINK_CRYPTO: int NETLINK_DNRTMSG: int NETLINK_FIREWALL: int NETLINK_IP6_FW: int NETLINK_NFLOG: int - NETLINK_ROUTE6: int NETLINK_ROUTE: int - NETLINK_SKIP: int - NETLINK_TAPBASE: int - NETLINK_TCPDIAG: int NETLINK_USERSOCK: int - NETLINK_W1: int NETLINK_XFRM: int + # Technically still supported by CPython + # NETLINK_ARPD: int # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_ROUTE6: int # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_SKIP: int # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_TAPBASE: int # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_TCPDIAG: int # linux 2.6.0 to 2.6.13 (EOL December 2005) + # NETLINK_W1: int # linux 2.6.13 to 2.6.17 (EOL October 2006) if sys.platform == "darwin": PF_SYSTEM: int SYSPROTO_CONTROL: int -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.version_info >= (3, 9) or sys.platform != "win32": AF_BLUETOOTH: int -if sys.platform != "win32" and sys.platform != "darwin": +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": # Linux and some BSD support is explicit in the docs # Windows and macOS do not support in practice BTPROTO_HCI: int BTPROTO_L2CAP: int BTPROTO_SCO: int # not in FreeBSD -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.version_info >= (3, 9) or sys.platform != "win32": BTPROTO_RFCOMM: int @@ -636,13 +683,14 @@ AF_SNA: int if sys.platform != "win32": AF_ROUTE: int + +if sys.platform == "darwin": AF_SYSTEM: int if sys.platform != "darwin": AF_IRDA: int if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5: int AF_ASH: int AF_ATMPVC: int AF_ATMSVC: int @@ -661,10 +709,12 @@ if sys.platform != "win32" and sys.platform != "darwin": # Miscellaneous undocumented -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": LOCAL_PEERCRED: int if sys.platform != "win32" and sys.platform != "darwin": + # Defined in linux socket.h, but this isn't always present for + # some reason. IPX_TYPE: int # ===== Classes ===== @@ -792,4 +842,4 @@ def if_nameindex() -> list[tuple[int, str]]: ... def if_nametoindex(oname: str, /) -> int: ... def if_indextoname(index: int, /) -> str: ... -CAPI: object +CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index b75e7608fa77..f0b70ed2a0b0 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -44,6 +44,12 @@ def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpa @overload def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... +# Obsolete synonym for start_new_thread() +@overload +def start_new(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... +@overload +def start_new(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... + if sys.version_info >= (3, 10): def interrupt_main(signum: signal.Signals = ..., /) -> None: ... @@ -51,7 +57,9 @@ else: def interrupt_main() -> None: ... def exit() -> NoReturn: ... +def exit_thread() -> NoReturn: ... # Obsolete synonym for exit() def allocate_lock() -> LockType: ... +def allocate() -> LockType: ... # Obsolete synonym for allocate_lock() def get_ident() -> int: ... def stack_size(size: int = 0, /) -> int: ... diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi index f9ac942278b9..07a825f0d816 100644 --- a/mypy/typeshed/stdlib/_threading_local.pyi +++ b/mypy/typeshed/stdlib/_threading_local.pyi @@ -1,3 +1,4 @@ +from threading import RLock from typing import Any from typing_extensions import Self, TypeAlias from weakref import ReferenceType @@ -8,6 +9,9 @@ _LocalDict: TypeAlias = dict[Any, Any] class _localimpl: key: str dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]] + # Keep localargs in sync with the *args, **kwargs annotation on local.__new__ + localargs: tuple[list[Any], dict[str, Any]] + locallock: RLock def get_dict(self) -> _LocalDict: ... def create_dict(self) -> _LocalDict: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index a45cc13f10e3..1a4ca925168a 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -106,7 +106,7 @@ class object: @property def __class__(self) -> type[Self]: ... @__class__.setter - def __class__(self, type: type[object], /) -> None: ... + def __class__(self, type: type[Self], /) -> None: ... def __init__(self) -> None: ... def __new__(cls) -> Self: ... # N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers. @@ -1865,14 +1865,33 @@ class StopAsyncIteration(Exception): class SyntaxError(Exception): msg: str + filename: str | None lineno: int | None offset: int | None text: str | None - filename: str | None + # Errors are displayed differently if this attribute exists on the exception. + # The value is always None. + print_file_and_line: None if sys.version_info >= (3, 10): end_lineno: int | None end_offset: int | None + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, msg: object, /) -> None: ... + # Second argument is the tuple (filename, lineno, offset, text) + @overload + def __init__(self, msg: str, info: tuple[str | None, int | None, int | None, str | None], /) -> None: ... + if sys.version_info >= (3, 10): + # end_lineno and end_offset must both be provided if one is. + @overload + def __init__( + self, msg: str, info: tuple[str | None, int | None, int | None, str | None, int | None, int | None], / + ) -> None: ... + # If you provide more than two arguments, it still creates the SyntaxError, but + # the arguments from the info tuple are not parsed. This form is omitted. + class SystemError(Exception): ... class TypeError(Exception): ... class ValueError(Exception): ... diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index ee5587031f2a..2f869f9697f4 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -126,6 +126,7 @@ class BZ2File(BaseStream, IO[bytes]): def readline(self, size: SupportsIndex = -1) -> bytes: ... # type: ignore[override] def readinto(self, b: WriteableBuffer) -> int: ... def readlines(self, size: SupportsIndex = -1) -> list[bytes]: ... + def peek(self, n: int = 0) -> bytes: ... def seek(self, offset: int, whence: int = 0) -> int: ... def write(self, data: ReadableBuffer) -> int: ... def writelines(self, seq: Iterable[ReadableBuffer]) -> None: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index a41df9752d33..b3c721f1e283 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -3,7 +3,7 @@ from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO +from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO from typing_extensions import Self __all__ = [ @@ -202,6 +202,7 @@ class StreamWriter(Codec): def write(self, object: str) -> None: ... def writelines(self, list: Iterable[str]) -> None: ... def reset(self) -> None: ... + def seek(self, offset: int, whence: int = 0) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ... @@ -209,11 +210,14 @@ class StreamWriter(Codec): class StreamReader(Codec): stream: _ReadableStream errors: str + # This is set to str, but some subclasses set to bytes instead. + charbuffertype: ClassVar[type] = ... def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ... def read(self, size: int = -1, chars: int = -1, firstline: bool = False) -> str: ... def readline(self, size: int | None = None, keepends: bool = True) -> str: ... def readlines(self, sizehint: int | None = None, keepends: bool = True) -> list[str]: ... def reset(self) -> None: ... + def seek(self, offset: int, whence: int = 0) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... def __iter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index ee5000196e0e..a44dc2e1c035 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import StrOrBytesPath, SupportsWrite +from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence from re import Pattern from typing import Any, ClassVar, Final, Literal, TypeVar, overload @@ -263,11 +263,11 @@ class RawConfigParser(_Parser): ) -> _T: ... # This is incompatible with MutableMapping so we ignore the type @overload # type: ignore[override] - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | Any: ... + def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ... @overload def get( self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T - ) -> str | _T | Any: ... + ) -> str | _T | MaybeNone: ... @overload def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ... @overload @@ -277,6 +277,8 @@ class RawConfigParser(_Parser): def remove_option(self, section: str, option: str) -> bool: ... def remove_section(self, section: str) -> bool: ... def optionxform(self, optionstr: str) -> str: ... + @property + def converters(self) -> ConverterMapping: ... class ConfigParser(RawConfigParser): # This is incompatible with MutableMapping so we ignore the type @@ -300,28 +302,34 @@ class SectionProxy(MutableMapping[str, str]): def parser(self) -> RawConfigParser: ... @property def name(self) -> str: ... - def get( # type: ignore[override] + # This is incompatible with MutableMapping so we ignore the type + @overload # type: ignore[override] + def get( + self, option: str, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any + ) -> str | None: ... + @overload + def get( self, option: str, - fallback: str | None = None, + fallback: _T, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any, - ) -> str | Any: ... # can be None in RawConfigParser's sections + ) -> str | _T: ... # These are partially-applied version of the methods with the same names in # RawConfigParser; the stubs should be kept updated together @overload - def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ... + def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int | None: ... @overload def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ... @overload - def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ... + def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float | None: ... @overload def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ... @overload - def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ... + def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool | None: ... @overload def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ... # SectionProxy can have arbitrary attributes when custom converters are used diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index 8a5b7dd6101c..dc5d926775f3 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -1,7 +1,7 @@ import abc import sys from _typeshed import FileDescriptorOrPath, Unused -from abc import abstractmethod +from abc import ABC, abstractmethod from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator from types import TracebackType from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable @@ -38,16 +38,22 @@ _P = ParamSpec("_P") _ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it is on the +# allowlist for use as a Protocol. @runtime_checkable -class AbstractContextManager(Protocol[_T_co, _ExitT_co]): +class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __enter__(self) -> _T_co: ... @abstractmethod def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> _ExitT_co: ... +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it is on the +# allowlist for use as a Protocol. @runtime_checkable -class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]): +class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( diff --git a/mypy/typeshed/stdlib/crypt.pyi b/mypy/typeshed/stdlib/crypt.pyi index 294003859286..bd22b5f8daba 100644 --- a/mypy/typeshed/stdlib/crypt.pyi +++ b/mypy/typeshed/stdlib/crypt.pyi @@ -1,8 +1,15 @@ import sys -from typing import Final +from typing import Final, NamedTuple, type_check_only if sys.platform != "win32": - class _Method: ... + @type_check_only + class _MethodBase(NamedTuple): + name: str + ident: str | None + salt_chars: int + total_size: int + + class _Method(_MethodBase): ... METHOD_CRYPT: Final[_Method] METHOD_MD5: Final[_Method] METHOD_SHA256: Final[_Method] diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index c5c7fea8fa25..4a82de638136 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -8,8 +8,6 @@ from _csv import ( __version__ as __version__, _DialectLike, _QuotingType, - _reader, - _writer, field_size_limit as field_size_limit, get_dialect as get_dialect, list_dialects as list_dialects, @@ -21,6 +19,10 @@ from _csv import ( if sys.version_info >= (3, 12): from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS +if sys.version_info >= (3, 10): + from _csv import Reader, Writer +else: + from _csv import _reader as Reader, _writer as Writer from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Mapping, Sequence @@ -77,7 +79,7 @@ class DictReader(Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None - reader: _reader + reader: Reader dialect: _DialectLike line_num: int @overload @@ -125,7 +127,7 @@ class DictWriter(Generic[_T]): fieldnames: Collection[_T] restval: Any | None extrasaction: Literal["raise", "ignore"] - writer: _writer + writer: Writer def __init__( self, f: SupportsWrite[str], diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 40a073d107c7..3e0e7c45bf15 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -10,13 +10,11 @@ from _ctypes import ( _CanCastTo as _CanCastTo, _CArgObject as _CArgObject, _CData as _CData, - _CDataMeta as _CDataMeta, + _CDataType as _CDataType, _CField as _CField, _Pointer as _Pointer, _PointerLike as _PointerLike, _SimpleCData as _SimpleCData, - _StructUnionBase as _StructUnionBase, - _StructUnionMeta as _StructUnionMeta, addressof as addressof, alignment as alignment, byref as byref, @@ -28,7 +26,7 @@ from _ctypes import ( ) from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure from typing import Any, ClassVar, Generic, TypeVar -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias, deprecated if sys.platform == "win32": from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error @@ -41,6 +39,7 @@ if sys.version_info >= (3, 9): _T = TypeVar("_T") _DLLT = TypeVar("_DLLT", bound=CDLL) +_CT = TypeVar("_CT", bound=_CData) DEFAULT_MODE: int @@ -48,7 +47,7 @@ class ArgumentError(Exception): ... class CDLL: _func_flags_: ClassVar[int] - _func_restype_: ClassVar[_CData] + _func_restype_: ClassVar[_CDataType] _name: str _handle: int _FuncPtr: type[_FuncPointer] @@ -91,15 +90,21 @@ class _NamedFuncPointer(_FuncPointer): __name__: str def CFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... if sys.platform == "win32": def WINFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... -def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ... # Any type that can be implicitly converted to c_void_p when passed as a C function argument. # (bytes is not included here, see below.) @@ -112,12 +117,17 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes _CastT = TypeVar("_CastT", bound=_CanCastTo) -def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... +def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ... c_buffer = create_string_buffer def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ... +@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") +def SetPointerType( + pointer: type[_Pointer[Any]], cls: Any # noqa: F811 # Redefinition of unused `pointer` from line 22 +) -> None: ... +def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to remove if sys.platform == "win32": def DllCanUnloadNow() -> int: ... @@ -126,12 +136,12 @@ if sys.platform == "win32": def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... def memset(dst: _CVoidPLike, c: int, count: int) -> int: ... -def string_at(address: _CVoidConstPLike, size: int = -1) -> bytes: ... +def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ... if sys.platform == "win32": def WinError(code: int | None = None, descr: str | None = None) -> OSError: ... -def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ... +def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ... class c_byte(_SimpleCData[int]): ... @@ -140,6 +150,8 @@ class c_char(_SimpleCData[bytes]): class c_char_p(_PointerLike, _SimpleCData[bytes | None]): def __init__(self, value: int | bytes | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_double(_SimpleCData[float]): ... class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double @@ -155,7 +167,13 @@ class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ulong(_SimpleCData[int]): ... class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ushort(_SimpleCData[int]): ... -class c_void_p(_PointerLike, _SimpleCData[int | None]): ... + +class c_void_p(_PointerLike, _SimpleCData[int | None]): + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... + +c_voidp = c_void_p # backwards compatibility (to a bug) + class c_wchar(_SimpleCData[str]): ... c_int8 = c_byte @@ -174,6 +192,8 @@ class c_uint64(_SimpleCData[int]): ... class c_wchar_p(_PointerLike, _SimpleCData[str | None]): def __init__(self, value: int | str | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_bool(_SimpleCData[bool]): def __init__(self, value: bool = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi new file mode 100644 index 000000000000..bda5b5a7f4cc --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi @@ -0,0 +1 @@ +__version__: str diff --git a/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi b/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi new file mode 100644 index 000000000000..c7e94daa2149 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi @@ -0,0 +1,8 @@ +from collections.abc import Mapping +from ctypes.macholib.dylib import dylib_info as dylib_info +from ctypes.macholib.framework import framework_info as framework_info + +__all__ = ["dyld_find", "framework_find", "framework_info", "dylib_info"] + +def dyld_find(name: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ... +def framework_find(fn: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi b/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi new file mode 100644 index 000000000000..95945edfd155 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi @@ -0,0 +1,14 @@ +from typing import TypedDict, type_check_only + +__all__ = ["dylib_info"] + +# Actual result is produced by re.match.groupdict() +@type_check_only +class _DylibInfo(TypedDict): + location: str + name: str + shortname: str + version: str | None + suffix: str | None + +def dylib_info(filename: str) -> _DylibInfo | None: ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi b/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi new file mode 100644 index 000000000000..e92bf3700e84 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi @@ -0,0 +1,14 @@ +from typing import TypedDict, type_check_only + +__all__ = ["framework_info"] + +# Actual result is produced by re.match.groupdict() +@type_check_only +class _FrameworkInfo(TypedDict): + location: str + name: str + shortname: str + version: str | None + suffix: str | None + +def framework_info(filename: str) -> _FrameworkInfo | None: ... diff --git a/mypy/typeshed/stdlib/ctypes/util.pyi b/mypy/typeshed/stdlib/ctypes/util.pyi index c0274f5e539b..316f7a2b3e2f 100644 --- a/mypy/typeshed/stdlib/ctypes/util.pyi +++ b/mypy/typeshed/stdlib/ctypes/util.pyi @@ -4,3 +4,5 @@ def find_library(name: str) -> str | None: ... if sys.platform == "win32": def find_msvcrt() -> str | None: ... + +def test() -> None: ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 8847860f2002..e938d8f22957 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -1,7 +1,7 @@ +from _ctypes import _CArgObject, _CField from ctypes import ( Array, Structure, - _CField, _Pointer, _SimpleCData, c_byte, @@ -21,8 +21,8 @@ from ctypes import ( c_wchar, c_wchar_p, ) -from typing import TypeVar -from typing_extensions import TypeAlias +from typing import Any, TypeVar +from typing_extensions import Self, TypeAlias BYTE = c_byte WORD = c_ushort @@ -241,10 +241,16 @@ LPBYTE = PBYTE PBOOLEAN = PBYTE # LP_c_char -class PCHAR(_Pointer[CHAR]): ... +class PCHAR(_Pointer[CHAR]): + # this is inherited from ctypes.c_char_p, kind of. + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_wchar -class PWCHAR(_Pointer[WCHAR]): ... +class PWCHAR(_Pointer[WCHAR]): + # inherited from ctypes.c_wchar_p, kind of + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_void_p class PHANDLE(_Pointer[HANDLE]): ... diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index 939cec0cede9..edc64a00cd39 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -1,7 +1,9 @@ +import sys from _curses import * from _curses import window as window +from _typeshed import structseq from collections.abc import Callable -from typing import TypeVar +from typing import Final, TypeVar, final, type_check_only from typing_extensions import Concatenate, ParamSpec # NOTE: The _curses module is ordinarily only available on Unix, but the @@ -25,3 +27,19 @@ def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwd # it was mapped to the name 'window' in 3.8. # Kept here as a legacy alias in case any third-party code is relying on it. _CursesWindow = window + +# At runtime this class is unexposed and calls itself curses.ncurses_version. +# That name would conflict with the actual curses.ncurses_version, which is +# an instance of this class. +@final +@type_check_only +class _ncurses_version(structseq[int], tuple[int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("major", "minor", "patch") + + @property + def major(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def patch(self) -> int: ... diff --git a/mypy/typeshed/stdlib/curses/panel.pyi b/mypy/typeshed/stdlib/curses/panel.pyi index d94f76635d8f..861559d38bc5 100644 --- a/mypy/typeshed/stdlib/curses/panel.pyi +++ b/mypy/typeshed/stdlib/curses/panel.pyi @@ -1,22 +1 @@ -from _curses import window - -version: str - -class _Curses_Panel: # type is (note the space in the class name) - def above(self) -> _Curses_Panel: ... - def below(self) -> _Curses_Panel: ... - def bottom(self) -> None: ... - def hidden(self) -> bool: ... - def hide(self) -> None: ... - def move(self, y: int, x: int) -> None: ... - def replace(self, win: window) -> None: ... - def set_userptr(self, obj: object) -> None: ... - def show(self) -> None: ... - def top(self) -> None: ... - def userptr(self) -> object: ... - def window(self) -> window: ... - -def bottom_panel() -> _Curses_Panel: ... -def new_panel(win: window, /) -> _Curses_Panel: ... -def top_panel() -> _Curses_Panel: ... -def update_panels() -> _Curses_Panel: ... +from _curses_panel import * diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index e8a4efdc61f3..87037ef39be7 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,8 +1,8 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload -from typing_extensions import Self, TypeAlias, deprecated +from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only +from typing_extensions import CapsuleType, Self, TypeAlias, deprecated if sys.version_info >= (3, 11): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") @@ -40,10 +40,17 @@ if sys.version_info >= (3, 11): UTC: timezone if sys.version_info >= (3, 9): - class _IsoCalendarDate(NamedTuple): - year: int - week: int - weekday: int + # This class calls itself datetime.IsoCalendarDate. It's neither + # NamedTuple nor structseq. + @final + @type_check_only + class _IsoCalendarDate(tuple[int, int, int]): + @property + def year(self) -> int: ... + @property + def week(self) -> int: ... + @property + def weekday(self) -> int: ... class date: min: ClassVar[date] @@ -325,3 +332,5 @@ class datetime(date): def __sub__(self, value: Self, /) -> timedelta: ... @overload def __sub__(self, value: timedelta, /) -> Self: ... + +datetime_CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index f414763e02a6..7f344060f9ab 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Iterator, MutableMapping from types import TracebackType -from typing import Literal +from typing import Literal, type_check_only from typing_extensions import Self, TypeAlias __all__ = ["open", "whichdb", "error"] @@ -89,6 +89,8 @@ class _Database(MutableMapping[_KeyType, bytes]): self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... +# This class is not exposed. It calls itself dbm.error. +@type_check_only class _error(Exception): ... error: tuple[type[_error], type[OSError]] diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 3da75ec192ac..7f8708a020fd 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -24,7 +24,8 @@ from _decimal import ( setcontext as setcontext, ) from collections.abc import Container, Sequence -from typing import Any, ClassVar, Literal, NamedTuple, overload +from types import TracebackType +from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only from typing_extensions import Self, TypeAlias _Decimal: TypeAlias = Decimal | int @@ -35,6 +36,14 @@ _TrapType: TypeAlias = type[DecimalException] # At runtime, these classes are implemented in C as part of "_decimal". # However, they consider themselves to live in "decimal", so we'll put them here. +# This type isn't exposed at runtime. It calls itself decimal.ContextManager +@final +@type_check_only +class _ContextManager: + def __init__(self, new_context: Context) -> None: ... + def __enter__(self) -> Context: ... + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... + class DecimalTuple(NamedTuple): sign: int digits: tuple[int, ...] diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index 56617d8a7b8d..5bff209807ee 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -165,7 +165,7 @@ class CCompiler: def execute( self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 ) -> None: ... - def spawn(self, cmd: list[str]) -> None: ... + def spawn(self, cmd: Iterable[str]) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... @overload def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ... diff --git a/mypy/typeshed/stdlib/distutils/spawn.pyi b/mypy/typeshed/stdlib/distutils/spawn.pyi index 50d89aeb9e5f..ae07a49504fe 100644 --- a/mypy/typeshed/stdlib/distutils/spawn.pyi +++ b/mypy/typeshed/stdlib/distutils/spawn.pyi @@ -1,6 +1,10 @@ +from collections.abc import Iterable from typing import Literal def spawn( - cmd: list[str], search_path: bool | Literal[0, 1] = 1, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 + cmd: Iterable[str], + search_path: bool | Literal[0, 1] = 1, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, ) -> None: ... def find_executable(executable: str, path: str | None = None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 4380083027a6..562b5a5bdac9 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -3,7 +3,7 @@ import types import unittest from _typeshed import ExcInfo from collections.abc import Callable -from typing import Any, ClassVar, NamedTuple +from typing import Any, NamedTuple, type_check_only from typing_extensions import Self, TypeAlias __all__ = [ @@ -42,17 +42,15 @@ __all__ = [ "debug", ] -# MyPy errors on conditionals within named tuples. - if sys.version_info >= (3, 13): - class TestResults(NamedTuple): - def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc] - skipped: int + @type_check_only + class _TestResultsBase(NamedTuple): failed: int attempted: int - _fields: ClassVar = ("failed", "attempted") # type: ignore[misc] - __match_args__ = ("failed", "attempted") # type: ignore[misc] - __doc__: None # type: ignore[misc] + + class TestResults(_TestResultsBase): + def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... + skipped: int else: class TestResults(NamedTuple): diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi index fca302f5f1a7..f564ced105bd 100644 --- a/mypy/typeshed/stdlib/email/__init__.pyi +++ b/mypy/typeshed/stdlib/email/__init__.pyi @@ -4,6 +4,29 @@ from email.policy import Policy from typing import IO from typing_extensions import TypeAlias +# At runtime, listing submodules in __all__ without them being imported is +# valid, and causes them to be included in a star import. See #6523 + +__all__ = [ # noqa: F822 # Undefined names in __all__ + "base64mime", # pyright: ignore[reportUnsupportedDunderAll] + "charset", # pyright: ignore[reportUnsupportedDunderAll] + "encoders", # pyright: ignore[reportUnsupportedDunderAll] + "errors", # pyright: ignore[reportUnsupportedDunderAll] + "feedparser", # pyright: ignore[reportUnsupportedDunderAll] + "generator", # pyright: ignore[reportUnsupportedDunderAll] + "header", # pyright: ignore[reportUnsupportedDunderAll] + "iterators", # pyright: ignore[reportUnsupportedDunderAll] + "message", # pyright: ignore[reportUnsupportedDunderAll] + "message_from_file", + "message_from_binary_file", + "message_from_string", + "message_from_bytes", + "mime", # pyright: ignore[reportUnsupportedDunderAll] + "parser", # pyright: ignore[reportUnsupportedDunderAll] + "quoprimime", # pyright: ignore[reportUnsupportedDunderAll] + "utils", # pyright: ignore[reportUnsupportedDunderAll] +] + # Definitions imported by multiple submodules in typeshed _ParamType: TypeAlias = str | tuple[str | None, str | None, str] # noqa: Y047 _ParamsType: TypeAlias = str | None | tuple[str, str | None, str] # noqa: Y047 @@ -12,18 +35,3 @@ def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy: def message_from_bytes(s: bytes | bytearray, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... def message_from_file(fp: IO[str], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... def message_from_binary_file(fp: IO[bytes], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... - -# Names in __all__ with no definition: -# base64mime -# charset -# encoders -# errors -# feedparser -# generator -# header -# iterators -# message -# mime -# parser -# quoprimime -# utils diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi index 900224eabb3d..f5dbbd96da14 100644 --- a/mypy/typeshed/stdlib/email/_policybase.pyi +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -5,6 +5,8 @@ from email.message import Message from typing import Generic, Protocol, TypeVar, type_check_only from typing_extensions import Self +__all__ = ["Policy", "Compat32", "compat32"] + _MessageT = TypeVar("_MessageT", bound=Message, default=Message) @type_check_only diff --git a/mypy/typeshed/stdlib/encodings/aliases.pyi b/mypy/typeshed/stdlib/encodings/aliases.pyi new file mode 100644 index 000000000000..079af85d51ee --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/aliases.pyi @@ -0,0 +1 @@ +aliases: dict[str, str] diff --git a/mypy/typeshed/stdlib/encodings/ascii.pyi b/mypy/typeshed/stdlib/encodings/ascii.pyi new file mode 100644 index 000000000000..a85585af32ed --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/ascii.pyi @@ -0,0 +1,30 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.ascii_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.ascii_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +# Note: encode being a decode function and decode being an encode function is accurate to runtime. +class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes + # At runtime, this is codecs.ascii_decode + @staticmethod + def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override] + # At runtime, this is codecs.ascii_encode + @staticmethod + def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/base64_codec.pyi b/mypy/typeshed/stdlib/encodings/base64_codec.pyi new file mode 100644 index 000000000000..0c4f1cb1fe59 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/base64_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def base64_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def base64_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/big5.pyi b/mypy/typeshed/stdlib/encodings/big5.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/big5.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/big5hkscs.pyi b/mypy/typeshed/stdlib/encodings/big5hkscs.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/big5hkscs.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/bz2_codec.pyi b/mypy/typeshed/stdlib/encodings/bz2_codec.pyi new file mode 100644 index 000000000000..468346a93da9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/bz2_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def bz2_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def bz2_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/charmap.pyi b/mypy/typeshed/stdlib/encodings/charmap.pyi new file mode 100644 index 000000000000..a971a15860b5 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/charmap.pyi @@ -0,0 +1,33 @@ +import codecs +from _codecs import _CharMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.charmap_encode + @staticmethod + def encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.charmap_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + mapping: _CharMap | None + def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + mapping: _CharMap | None + def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): + mapping: _CharMap | None + def __init__(self, stream: codecs._WritableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class StreamReader(Codec, codecs.StreamReader): + mapping: _CharMap | None + def __init__(self, stream: codecs._ReadableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp037.pyi b/mypy/typeshed/stdlib/encodings/cp037.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp037.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1006.pyi b/mypy/typeshed/stdlib/encodings/cp1006.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1006.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1026.pyi b/mypy/typeshed/stdlib/encodings/cp1026.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1026.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1125.pyi b/mypy/typeshed/stdlib/encodings/cp1125.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1125.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp1140.pyi b/mypy/typeshed/stdlib/encodings/cp1140.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1140.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1250.pyi b/mypy/typeshed/stdlib/encodings/cp1250.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1250.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1251.pyi b/mypy/typeshed/stdlib/encodings/cp1251.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1251.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1252.pyi b/mypy/typeshed/stdlib/encodings/cp1252.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1252.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1253.pyi b/mypy/typeshed/stdlib/encodings/cp1253.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1253.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1254.pyi b/mypy/typeshed/stdlib/encodings/cp1254.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1254.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1255.pyi b/mypy/typeshed/stdlib/encodings/cp1255.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1255.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1256.pyi b/mypy/typeshed/stdlib/encodings/cp1256.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1256.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1257.pyi b/mypy/typeshed/stdlib/encodings/cp1257.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1257.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1258.pyi b/mypy/typeshed/stdlib/encodings/cp1258.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1258.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp273.pyi b/mypy/typeshed/stdlib/encodings/cp273.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp273.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp424.pyi b/mypy/typeshed/stdlib/encodings/cp424.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp424.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp437.pyi b/mypy/typeshed/stdlib/encodings/cp437.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp437.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp500.pyi b/mypy/typeshed/stdlib/encodings/cp500.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp500.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp720.pyi b/mypy/typeshed/stdlib/encodings/cp720.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp720.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp737.pyi b/mypy/typeshed/stdlib/encodings/cp737.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp737.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp775.pyi b/mypy/typeshed/stdlib/encodings/cp775.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp775.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp850.pyi b/mypy/typeshed/stdlib/encodings/cp850.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp850.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp852.pyi b/mypy/typeshed/stdlib/encodings/cp852.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp852.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp855.pyi b/mypy/typeshed/stdlib/encodings/cp855.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp855.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp856.pyi b/mypy/typeshed/stdlib/encodings/cp856.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp856.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp857.pyi b/mypy/typeshed/stdlib/encodings/cp857.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp857.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp858.pyi b/mypy/typeshed/stdlib/encodings/cp858.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp858.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp860.pyi b/mypy/typeshed/stdlib/encodings/cp860.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp860.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp861.pyi b/mypy/typeshed/stdlib/encodings/cp861.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp861.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp862.pyi b/mypy/typeshed/stdlib/encodings/cp862.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp862.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp863.pyi b/mypy/typeshed/stdlib/encodings/cp863.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp863.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp864.pyi b/mypy/typeshed/stdlib/encodings/cp864.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp864.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp865.pyi b/mypy/typeshed/stdlib/encodings/cp865.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp865.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp866.pyi b/mypy/typeshed/stdlib/encodings/cp866.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp866.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp869.pyi b/mypy/typeshed/stdlib/encodings/cp869.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp869.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp874.pyi b/mypy/typeshed/stdlib/encodings/cp874.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp874.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp875.pyi b/mypy/typeshed/stdlib/encodings/cp875.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp875.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp932.pyi b/mypy/typeshed/stdlib/encodings/cp932.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp932.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp949.pyi b/mypy/typeshed/stdlib/encodings/cp949.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp949.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp950.pyi b/mypy/typeshed/stdlib/encodings/cp950.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp950.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi b/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi b/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jp.pyi b/mypy/typeshed/stdlib/encodings/euc_jp.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jp.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_kr.pyi b/mypy/typeshed/stdlib/encodings/euc_kr.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_kr.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gb18030.pyi b/mypy/typeshed/stdlib/encodings/gb18030.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gb18030.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gb2312.pyi b/mypy/typeshed/stdlib/encodings/gb2312.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gb2312.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gbk.pyi b/mypy/typeshed/stdlib/encodings/gbk.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gbk.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/hex_codec.pyi b/mypy/typeshed/stdlib/encodings/hex_codec.pyi new file mode 100644 index 000000000000..3fd4fe38898a --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hex_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def hex_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def hex_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/hp_roman8.pyi b/mypy/typeshed/stdlib/encodings/hp_roman8.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hp_roman8.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/hz.pyi b/mypy/typeshed/stdlib/encodings/hz.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hz.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/idna.pyi b/mypy/typeshed/stdlib/encodings/idna.pyi new file mode 100644 index 000000000000..3e2c8baf1cb2 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/idna.pyi @@ -0,0 +1,26 @@ +import codecs +import re +from _typeshed import ReadableBuffer + +dots: re.Pattern[str] +ace_prefix: bytes +sace_prefix: str + +def nameprep(label: str) -> str: ... +def ToASCII(label: str) -> bytes: ... +def ToUnicode(label: bytes | str) -> str: ... + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: ReadableBuffer | str, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, input: str, errors: str, final: bool) -> tuple[bytes, int]: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer | str, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi b/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso8859_1.pyi b/mypy/typeshed/stdlib/encodings/iso8859_1.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_1.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_10.pyi b/mypy/typeshed/stdlib/encodings/iso8859_10.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_10.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_11.pyi b/mypy/typeshed/stdlib/encodings/iso8859_11.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_11.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_13.pyi b/mypy/typeshed/stdlib/encodings/iso8859_13.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_13.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_14.pyi b/mypy/typeshed/stdlib/encodings/iso8859_14.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_14.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_15.pyi b/mypy/typeshed/stdlib/encodings/iso8859_15.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_15.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_16.pyi b/mypy/typeshed/stdlib/encodings/iso8859_16.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_16.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_2.pyi b/mypy/typeshed/stdlib/encodings/iso8859_2.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_2.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_3.pyi b/mypy/typeshed/stdlib/encodings/iso8859_3.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_3.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_4.pyi b/mypy/typeshed/stdlib/encodings/iso8859_4.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_4.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_5.pyi b/mypy/typeshed/stdlib/encodings/iso8859_5.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_5.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_6.pyi b/mypy/typeshed/stdlib/encodings/iso8859_6.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_6.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_7.pyi b/mypy/typeshed/stdlib/encodings/iso8859_7.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_7.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_8.pyi b/mypy/typeshed/stdlib/encodings/iso8859_8.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_8.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_9.pyi b/mypy/typeshed/stdlib/encodings/iso8859_9.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_9.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/johab.pyi b/mypy/typeshed/stdlib/encodings/johab.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/johab.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/koi8_r.pyi b/mypy/typeshed/stdlib/encodings/koi8_r.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_r.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/koi8_t.pyi b/mypy/typeshed/stdlib/encodings/koi8_t.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_t.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/koi8_u.pyi b/mypy/typeshed/stdlib/encodings/koi8_u.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_u.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/kz1048.pyi b/mypy/typeshed/stdlib/encodings/kz1048.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/kz1048.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/latin_1.pyi b/mypy/typeshed/stdlib/encodings/latin_1.pyi new file mode 100644 index 000000000000..3b06773eac03 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/latin_1.pyi @@ -0,0 +1,30 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.latin_1_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.latin_1_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +# Note: encode being a decode function and decode being an encode function is accurate to runtime. +class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes + # At runtime, this is codecs.latin_1_decode + @staticmethod + def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override] + # At runtime, this is codecs.latin_1_encode + @staticmethod + def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/mac_arabic.pyi b/mypy/typeshed/stdlib/encodings/mac_arabic.pyi new file mode 100644 index 000000000000..42781b489298 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_arabic.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi b/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_croatian.pyi b/mypy/typeshed/stdlib/encodings/mac_croatian.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_croatian.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi b/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_farsi.pyi b/mypy/typeshed/stdlib/encodings/mac_farsi.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_farsi.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_greek.pyi b/mypy/typeshed/stdlib/encodings/mac_greek.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_greek.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_iceland.pyi b/mypy/typeshed/stdlib/encodings/mac_iceland.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_iceland.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_latin2.pyi b/mypy/typeshed/stdlib/encodings/mac_latin2.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_latin2.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_roman.pyi b/mypy/typeshed/stdlib/encodings/mac_roman.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_roman.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_romanian.pyi b/mypy/typeshed/stdlib/encodings/mac_romanian.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_romanian.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_turkish.pyi b/mypy/typeshed/stdlib/encodings/mac_turkish.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_turkish.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mbcs.pyi b/mypy/typeshed/stdlib/encodings/mbcs.pyi new file mode 100644 index 000000000000..2c2917d63f6d --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mbcs.pyi @@ -0,0 +1,28 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +if sys.platform == "win32": + encode = codecs.mbcs_encode + + def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + + class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.mbcs_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.mbcs_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + + class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.mbcs_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/oem.pyi b/mypy/typeshed/stdlib/encodings/oem.pyi new file mode 100644 index 000000000000..376c12c445f4 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/oem.pyi @@ -0,0 +1,28 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +if sys.platform == "win32": + encode = codecs.oem_encode + + def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + + class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.oem_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.oem_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + + class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.oem_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/palmos.pyi b/mypy/typeshed/stdlib/encodings/palmos.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/palmos.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/ptcp154.pyi b/mypy/typeshed/stdlib/encodings/ptcp154.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/ptcp154.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/punycode.pyi b/mypy/typeshed/stdlib/encodings/punycode.pyi new file mode 100644 index 000000000000..eb99e667b416 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/punycode.pyi @@ -0,0 +1,33 @@ +import codecs +from typing import Literal + +def segregate(str: str) -> tuple[bytes, list[int]]: ... +def selective_len(str: str, max: int) -> int: ... +def selective_find(str: str, char: str, index: int, pos: int) -> tuple[int, int]: ... +def insertion_unsort(str: str, extended: list[int]) -> list[int]: ... +def T(j: int, bias: int) -> int: ... + +digits: Literal[b"abcdefghijklmnopqrstuvwxyz0123456789"] + +def generate_generalized_integer(N: int, bias: int) -> bytes: ... +def adapt(delta: int, first: bool, numchars: int) -> int: ... +def generate_integers(baselen: int, deltas: list[int]) -> bytes: ... +def punycode_encode(text: str) -> bytes: ... +def decode_generalized_number(extended: bytes, extpos: int, bias: int, errors: str) -> tuple[int, int | None]: ... +def insertion_sort(base: str, extended: bytes, errors: str) -> str: ... +def punycode_decode(text: memoryview | bytes | bytearray | str, errors: str) -> str: ... + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: memoryview | bytes | bytearray | str, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: memoryview | bytes | bytearray | str, final: bool = False) -> str: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/quopri_codec.pyi b/mypy/typeshed/stdlib/encodings/quopri_codec.pyi new file mode 100644 index 000000000000..e9deadd8d463 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/quopri_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def quopri_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def quopri_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi new file mode 100644 index 000000000000..74abb4623fab --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi @@ -0,0 +1,34 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.raw_unicode_escape_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.raw_unicode_escape_decode + if sys.version_info >= (3, 9): + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ... + else: + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +if sys.version_info >= (3, 9): + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +else: + class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + if sys.version_info >= (3, 9): + def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/rot_13.pyi b/mypy/typeshed/stdlib/encodings/rot_13.pyi new file mode 100644 index 000000000000..8d71bc957594 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/rot_13.pyi @@ -0,0 +1,23 @@ +import codecs +from _typeshed import SupportsRead, SupportsWrite + +# This codec is string to string. + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + def decode(self, input: str, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> str: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str, final: bool = False) -> str: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +rot13_map: dict[int, int] + +def rot13(infile: SupportsRead[str], outfile: SupportsWrite[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jis.pyi b/mypy/typeshed/stdlib/encodings/shift_jis.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jis.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi b/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi b/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi new file mode 100644 index 000000000000..d613026a5a86 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/tis_620.pyi b/mypy/typeshed/stdlib/encodings/tis_620.pyi new file mode 100644 index 000000000000..f62195662ce9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/tis_620.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/undefined.pyi b/mypy/typeshed/stdlib/encodings/undefined.pyi new file mode 100644 index 000000000000..4775dac752f2 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/undefined.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +# These return types are just to match the base types. In reality, these always +# raise an error. + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi new file mode 100644 index 000000000000..1e942f57916e --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi @@ -0,0 +1,34 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.unicode_escape_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.unicode_escape_decode + if sys.version_info >= (3, 9): + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ... + else: + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +if sys.version_info >= (3, 9): + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +else: + class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + if sys.version_info >= (3, 9): + def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16.pyi b/mypy/typeshed/stdlib/encodings/utf_16.pyi new file mode 100644 index 000000000000..3b712cde420a --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16_be.pyi b/mypy/typeshed/stdlib/encodings/utf_16_be.pyi new file mode 100644 index 000000000000..cc7d1534fc69 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16_be.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_be_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_16_be_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_16_be_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_16_be_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16_le.pyi b/mypy/typeshed/stdlib/encodings/utf_16_le.pyi new file mode 100644 index 000000000000..ba103eb088e3 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16_le.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_le_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_16_le_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_16_le_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_16_le_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32.pyi b/mypy/typeshed/stdlib/encodings/utf_32.pyi new file mode 100644 index 000000000000..c925be712c72 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32_be.pyi b/mypy/typeshed/stdlib/encodings/utf_32_be.pyi new file mode 100644 index 000000000000..9d28f5199c50 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32_be.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_be_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_32_be_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_32_be_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_32_be_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32_le.pyi b/mypy/typeshed/stdlib/encodings/utf_32_le.pyi new file mode 100644 index 000000000000..5be14a91a3e6 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32_le.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_le_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_32_le_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_32_le_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_32_le_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_7.pyi b/mypy/typeshed/stdlib/encodings/utf_7.pyi new file mode 100644 index 000000000000..dc1162f34c28 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_7.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_7_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_7_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_7_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_7_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_8.pyi b/mypy/typeshed/stdlib/encodings/utf_8.pyi index bb745399eb8c..918712d80473 100644 --- a/mypy/typeshed/stdlib/encodings/utf_8.pyi +++ b/mypy/typeshed/stdlib/encodings/utf_8.pyi @@ -1,21 +1,26 @@ import codecs from _typeshed import ReadableBuffer +encode = codecs.utf_8_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + class IncrementalEncoder(codecs.IncrementalEncoder): def encode(self, input: str, final: bool = False) -> bytes: ... class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_8_decode @staticmethod def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_8_encode @staticmethod def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_8_decode @staticmethod def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... def getregentry() -> codecs.CodecInfo: ... -def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... -def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/encodings/uu_codec.pyi b/mypy/typeshed/stdlib/encodings/uu_codec.pyi new file mode 100644 index 000000000000..e32ba8ac0a1a --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/uu_codec.pyi @@ -0,0 +1,28 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def uu_encode( + input: ReadableBuffer, errors: str = "strict", filename: str = "", mode: int = 0o666 +) -> tuple[bytes, int]: ... +def uu_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/zlib_codec.pyi b/mypy/typeshed/stdlib/encodings/zlib_codec.pyi new file mode 100644 index 000000000000..0f13d0e810e9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/zlib_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def zlib_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def zlib_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 376611f166b8..71078b3b4579 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -70,8 +70,10 @@ if sys.platform != "win32": LOCK_RW: int LOCK_WRITE: int - # These are highly problematic, they might be present or not, depends on the specific OS. if sys.platform == "linux": + # Constants for the POSIX STREAMS interface. Present in glibc until 2.29 (released February 2019). + # Never implemented on BSD, and considered "obsolescent" starting in POSIX 2008. + # Probably still used on Solaris. I_ATMARK: int I_CANPUT: int I_CKBAND: int diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index 4c17c0dc5de4..d8fd92a00e13 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -167,3 +167,5 @@ if sys.version_info < (3, 11): def bind_textdomain_codeset(domain: str, codeset: str | None = None) -> str: ... Catalog = translation + +def c2py(plural: str) -> Callable[[int], int]: ... diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 74687d9abe3d..db6f8635054d 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,9 +1,19 @@ import sys from _blake2 import blake2b as blake2b, blake2s as blake2s +from _hashlib import ( + HASH, + openssl_md5 as md5, + openssl_sha1 as sha1, + openssl_sha224 as sha224, + openssl_sha256 as sha256, + openssl_sha384 as sha384, + openssl_sha512 as sha512, + pbkdf2_hmac as pbkdf2_hmac, + scrypt as scrypt, +) from _typeshed import ReadableBuffer from collections.abc import Callable, Set as AbstractSet -from typing import Protocol -from typing_extensions import Self +from typing import Protocol, type_check_only if sys.version_info >= (3, 11): __all__ = ( @@ -49,67 +59,35 @@ else: "pbkdf2_hmac", ) -class _Hash: - @property - def digest_size(self) -> int: ... - @property - def block_size(self) -> int: ... - @property - def name(self) -> str: ... - def copy(self) -> Self: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def update(self, data: ReadableBuffer, /) -> None: ... - -class _VarLenHash: - digest_size: int - block_size: int - name: str - def copy(self) -> _VarLenHash: ... - def digest(self, length: int, /) -> bytes: ... - def hexdigest(self, length: int, /) -> str: ... - def update(self, data: ReadableBuffer, /) -> None: ... - if sys.version_info >= (3, 9): - def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> _Hash: ... - def md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _VarLenHash: ... - def shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _VarLenHash: ... + def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> HASH: ... + from _hashlib import ( + openssl_sha3_224 as sha3_224, + openssl_sha3_256 as sha3_256, + openssl_sha3_384 as sha3_384, + openssl_sha3_512 as sha3_512, + openssl_shake_128 as shake_128, + openssl_shake_256 as shake_256, + ) else: - def new(name: str, data: ReadableBuffer = b"") -> _Hash: ... - def md5(string: ReadableBuffer = b"") -> _Hash: ... - def sha1(string: ReadableBuffer = b"") -> _Hash: ... - def sha224(string: ReadableBuffer = b"") -> _Hash: ... - def sha256(string: ReadableBuffer = b"") -> _Hash: ... - def sha384(string: ReadableBuffer = b"") -> _Hash: ... - def sha512(string: ReadableBuffer = b"") -> _Hash: ... - def sha3_224(string: ReadableBuffer = b"") -> _Hash: ... - def sha3_256(string: ReadableBuffer = b"") -> _Hash: ... - def sha3_384(string: ReadableBuffer = b"") -> _Hash: ... - def sha3_512(string: ReadableBuffer = b"") -> _Hash: ... + @type_check_only + class _VarLenHash(HASH): + def digest(self, length: int) -> bytes: ... # type: ignore[override] + def hexdigest(self, length: int) -> str: ... # type: ignore[override] + + def new(name: str, data: ReadableBuffer = b"") -> HASH: ... + # At runtime these aren't functions but classes imported from _sha3 + def sha3_224(string: ReadableBuffer = b"") -> HASH: ... + def sha3_256(string: ReadableBuffer = b"") -> HASH: ... + def sha3_384(string: ReadableBuffer = b"") -> HASH: ... + def sha3_512(string: ReadableBuffer = b"") -> HASH: ... def shake_128(string: ReadableBuffer = b"") -> _VarLenHash: ... def shake_256(string: ReadableBuffer = b"") -> _VarLenHash: ... algorithms_guaranteed: AbstractSet[str] algorithms_available: AbstractSet[str] -def pbkdf2_hmac( - hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None -) -> bytes: ... -def scrypt( - password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 -) -> bytes: ... - if sys.version_info >= (3, 11): class _BytesIOLike(Protocol): def getbuffer(self) -> ReadableBuffer: ... @@ -119,5 +97,8 @@ if sys.version_info >= (3, 11): def readable(self) -> bool: ... def file_digest( - fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _Hash], /, *, _bufsize: int = 262144 - ) -> _Hash: ... + fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], HASH], /, *, _bufsize: int = 262144 + ) -> HASH: ... + +# Legacy typing-only alias +_Hash = HASH diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index eccfbdc235f4..efd649ec39a8 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,6 +1,7 @@ +import sys +from _hashlib import HASH as _HashlibHash from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable -from hashlib import _Hash as _HashlibHash from types import ModuleType from typing import AnyStr, overload from typing_extensions import TypeAlias @@ -30,8 +31,12 @@ class HMAC: def hexdigest(self) -> str: ... def copy(self) -> HMAC: ... -@overload -def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... -@overload -def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... def digest(key: SizedBuffer, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ... + +if sys.version_info >= (3, 9): + from _hashlib import compare_digest as compare_digest +else: + @overload + def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... + @overload + def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/abc.pyi b/mypy/typeshed/stdlib/importlib/resources/abc.pyi index a36c952d01ac..ad80605f7c71 100644 --- a/mypy/typeshed/stdlib/importlib/resources/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/abc.pyi @@ -10,3 +10,5 @@ if sys.version_info >= (3, 11): Traversable as Traversable, TraversableResources as TraversableResources, ) + + __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index fce233e84555..f5cee43d6b32 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -51,25 +51,6 @@ class _BaseAddress(_IPAddressBase): def __gt__(self, other: Self, NotImplemented: Any = ...) -> bool: ... def __le__(self, other: Self, NotImplemented: Any = ...) -> bool: ... - @property - def is_global(self) -> bool: ... - @property - def is_link_local(self) -> bool: ... - @property - def is_loopback(self) -> bool: ... - @property - def is_multicast(self) -> bool: ... - @property - def is_private(self) -> bool: ... - @property - def is_reserved(self) -> bool: ... - @property - def is_unspecified(self) -> bool: ... - @property - def max_prefixlen(self) -> int: ... - @property - def packed(self) -> bytes: ... - class _BaseNetwork(_IPAddressBase, Generic[_A]): network_address: _A netmask: _A @@ -109,8 +90,6 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def is_unspecified(self) -> bool: ... @property - def max_prefixlen(self) -> int: ... - @property def num_addresses(self) -> int: ... def overlaps(self, other: _BaseNetwork[IPv4Address] | _BaseNetwork[IPv6Address]) -> bool: ... @property @@ -135,6 +114,22 @@ class _BaseV4: def max_prefixlen(self) -> Literal[32]: ... class IPv4Address(_BaseV4, _BaseAddress): + @property + def is_global(self) -> bool: ... + @property + def is_link_local(self) -> bool: ... + @property + def is_loopback(self) -> bool: ... + @property + def is_multicast(self) -> bool: ... + @property + def is_private(self) -> bool: ... + @property + def is_reserved(self) -> bool: ... + @property + def is_unspecified(self) -> bool: ... + @property + def packed(self) -> bytes: ... if sys.version_info >= (3, 13): @property def ipv6_mapped(self) -> IPv6Address: ... @@ -164,6 +159,22 @@ class _BaseV6: def max_prefixlen(self) -> Literal[128]: ... class IPv6Address(_BaseV6, _BaseAddress): + @property + def is_global(self) -> bool: ... + @property + def is_link_local(self) -> bool: ... + @property + def is_loopback(self) -> bool: ... + @property + def is_multicast(self) -> bool: ... + @property + def is_private(self) -> bool: ... + @property + def is_reserved(self) -> bool: ... + @property + def is_unspecified(self) -> bool: ... + @property + def packed(self) -> bytes: ... @property def ipv4_mapped(self) -> IPv4Address | None: ... @property diff --git a/mypy/typeshed/stdlib/json/scanner.pyi b/mypy/typeshed/stdlib/json/scanner.pyi new file mode 100644 index 000000000000..f3b98996b752 --- /dev/null +++ b/mypy/typeshed/stdlib/json/scanner.pyi @@ -0,0 +1,3 @@ +from _json import make_scanner as make_scanner + +__all__ = ["make_scanner"] diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 2cf948ba898a..6db665a18e69 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -64,3 +64,5 @@ class ModuleFinder: def any_missing(self) -> list[str]: ... # undocumented def any_missing_maybe(self) -> tuple[list[str], list[str]]: ... # undocumented def replace_paths_in_code(self, co: CodeType) -> CodeType: ... # undocumented + +def test() -> ModuleFinder | None: ... # undocumented diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 9900f009a398..03d1d2e5c220 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -1,7 +1,8 @@ import ctypes import sys +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from logging import Logger, _Level as _LoggingLevel from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, popen_spawn_win32, queues, synchronize from multiprocessing.managers import SyncManager diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 2b0498abc2c6..5283445d8545 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -1,6 +1,7 @@ import ctypes +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from multiprocessing.context import BaseContext from multiprocessing.synchronize import _LockLike from types import TracebackType diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index d6f46b527905..e3cbfbc0ec82 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -10,7 +10,7 @@ _LockLike: TypeAlias = Lock | RLock class Barrier(threading.Barrier): def __init__( - self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *ctx: BaseContext + self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *, ctx: BaseContext ) -> None: ... class Condition: @@ -19,12 +19,14 @@ class Condition: def notify_all(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... def wait_for(self, predicate: Callable[[], bool], timeout: float | None = None) -> bool: ... - def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... - def release(self) -> None: ... def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... + # These methods are copied from the lock passed to the constructor, or an + # instance of ctx.RLock() if lock was None. + def acquire(self, block: bool = True, timeout: float | None = None) -> bool: ... + def release(self) -> None: ... class Event: def __init__(self, *, ctx: BaseContext) -> None: ... @@ -35,12 +37,14 @@ class Event: # Not part of public API class SemLock: - def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... - def release(self) -> None: ... + def __init__(self, kind: int, value: int, maxvalue: int, *, ctx: BaseContext | None) -> None: ... def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... + # These methods are copied from the wrapped _multiprocessing.SemLock object + def acquire(self, block: bool = True, timeout: float | None = None) -> bool: ... + def release(self) -> None: ... class Lock(SemLock): def __init__(self, *, ctx: BaseContext) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 203956786a9b..98260b14e7ed 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -19,10 +19,9 @@ from _typeshed import ( WriteableBuffer, structseq, ) -from abc import abstractmethod +from abc import ABC, abstractmethod from builtins import OSError from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, Sequence -from contextlib import AbstractContextManager from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from subprocess import Popen from types import TracebackType @@ -48,6 +47,446 @@ from . import path as _path if sys.version_info >= (3, 9): from types import GenericAlias +__all__ = [ + "F_OK", + "O_APPEND", + "O_CREAT", + "O_EXCL", + "O_RDONLY", + "O_RDWR", + "O_TRUNC", + "O_WRONLY", + "P_NOWAIT", + "P_NOWAITO", + "P_WAIT", + "R_OK", + "SEEK_CUR", + "SEEK_END", + "SEEK_SET", + "TMP_MAX", + "W_OK", + "X_OK", + "DirEntry", + "_exit", + "abort", + "access", + "altsep", + "chdir", + "chmod", + "close", + "closerange", + "cpu_count", + "curdir", + "defpath", + "device_encoding", + "devnull", + "dup", + "dup2", + "environ", + "error", + "execl", + "execle", + "execlp", + "execlpe", + "execv", + "execve", + "execvp", + "execvpe", + "extsep", + "fdopen", + "fsdecode", + "fsencode", + "fspath", + "fstat", + "fsync", + "ftruncate", + "get_exec_path", + "get_inheritable", + "get_terminal_size", + "getcwd", + "getcwdb", + "getenv", + "getlogin", + "getpid", + "getppid", + "isatty", + "kill", + "linesep", + "link", + "listdir", + "lseek", + "lstat", + "makedirs", + "mkdir", + "name", + "open", + "pardir", + "path", + "pathsep", + "pipe", + "popen", + "putenv", + "read", + "readlink", + "remove", + "removedirs", + "rename", + "renames", + "replace", + "rmdir", + "scandir", + "sep", + "set_inheritable", + "spawnl", + "spawnle", + "spawnv", + "spawnve", + "stat", + "stat_result", + "statvfs_result", + "strerror", + "supports_bytes_environ", + "symlink", + "system", + "terminal_size", + "times", + "times_result", + "truncate", + "umask", + "uname_result", + "unlink", + "urandom", + "utime", + "waitpid", + "walk", + "write", +] +if sys.version_info >= (3, 9): + __all__ += ["waitstatus_to_exitcode"] +if sys.platform == "darwin" and sys.version_info >= (3, 12): + __all__ += ["PRIO_DARWIN_BG", "PRIO_DARWIN_NONUI", "PRIO_DARWIN_PROCESS", "PRIO_DARWIN_THREAD"] +if sys.platform == "darwin" and sys.version_info >= (3, 10): + __all__ += ["O_EVTONLY", "O_NOFOLLOW_ANY", "O_SYMLINK"] +if sys.platform == "linux": + __all__ += [ + "GRND_NONBLOCK", + "GRND_RANDOM", + "MFD_ALLOW_SEALING", + "MFD_CLOEXEC", + "MFD_HUGETLB", + "MFD_HUGE_16GB", + "MFD_HUGE_16MB", + "MFD_HUGE_1GB", + "MFD_HUGE_1MB", + "MFD_HUGE_256MB", + "MFD_HUGE_2GB", + "MFD_HUGE_2MB", + "MFD_HUGE_32MB", + "MFD_HUGE_512KB", + "MFD_HUGE_512MB", + "MFD_HUGE_64KB", + "MFD_HUGE_8MB", + "MFD_HUGE_MASK", + "MFD_HUGE_SHIFT", + "O_DIRECT", + "O_LARGEFILE", + "O_NOATIME", + "O_PATH", + "O_RSYNC", + "O_TMPFILE", + "RTLD_DEEPBIND", + "SCHED_BATCH", + "SCHED_IDLE", + "SCHED_RESET_ON_FORK", + "XATTR_CREATE", + "XATTR_REPLACE", + "XATTR_SIZE_MAX", + "copy_file_range", + "getrandom", + "getxattr", + "listxattr", + "memfd_create", + "removexattr", + "setxattr", + ] +if sys.platform == "linux" and sys.version_info >= (3, 13): + __all__ += [ + "POSIX_SPAWN_CLOSEFROM", + "TFD_CLOEXEC", + "TFD_NONBLOCK", + "TFD_TIMER_ABSTIME", + "TFD_TIMER_CANCEL_ON_SET", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime_ns", + "timerfd_settime", + "timerfd_settime_ns", + ] +if sys.platform == "linux" and sys.version_info >= (3, 12): + __all__ += [ + "CLONE_FILES", + "CLONE_FS", + "CLONE_NEWCGROUP", + "CLONE_NEWIPC", + "CLONE_NEWNET", + "CLONE_NEWNS", + "CLONE_NEWPID", + "CLONE_NEWUSER", + "CLONE_NEWUTS", + "CLONE_SIGHAND", + "CLONE_SYSVSEM", + "CLONE_THREAD", + "CLONE_VM", + "setns", + "unshare", + ] +if sys.platform == "linux" and sys.version_info >= (3, 10): + __all__ += [ + "EFD_CLOEXEC", + "EFD_NONBLOCK", + "EFD_SEMAPHORE", + "RWF_APPEND", + "SPLICE_F_MORE", + "SPLICE_F_MOVE", + "SPLICE_F_NONBLOCK", + "eventfd", + "eventfd_read", + "eventfd_write", + "splice", + ] +if sys.platform == "linux" and sys.version_info >= (3, 9): + __all__ += ["P_PIDFD", "pidfd_open"] +if sys.platform == "win32": + __all__ += [ + "O_BINARY", + "O_NOINHERIT", + "O_RANDOM", + "O_SEQUENTIAL", + "O_SHORT_LIVED", + "O_TEMPORARY", + "O_TEXT", + "P_DETACH", + "P_OVERLAY", + "get_handle_inheritable", + "set_handle_inheritable", + "startfile", + ] +if sys.platform == "win32" and sys.version_info >= (3, 12): + __all__ += ["listdrives", "listmounts", "listvolumes"] +if sys.platform != "win32": + __all__ += [ + "CLD_CONTINUED", + "CLD_DUMPED", + "CLD_EXITED", + "CLD_TRAPPED", + "EX_CANTCREAT", + "EX_CONFIG", + "EX_DATAERR", + "EX_IOERR", + "EX_NOHOST", + "EX_NOINPUT", + "EX_NOPERM", + "EX_NOUSER", + "EX_OSERR", + "EX_OSFILE", + "EX_PROTOCOL", + "EX_SOFTWARE", + "EX_TEMPFAIL", + "EX_UNAVAILABLE", + "EX_USAGE", + "F_LOCK", + "F_TEST", + "F_TLOCK", + "F_ULOCK", + "NGROUPS_MAX", + "O_ACCMODE", + "O_ASYNC", + "O_CLOEXEC", + "O_DIRECTORY", + "O_DSYNC", + "O_NDELAY", + "O_NOCTTY", + "O_NOFOLLOW", + "O_NONBLOCK", + "O_SYNC", + "POSIX_SPAWN_CLOSE", + "POSIX_SPAWN_DUP2", + "POSIX_SPAWN_OPEN", + "PRIO_PGRP", + "PRIO_PROCESS", + "PRIO_USER", + "P_ALL", + "P_PGID", + "P_PID", + "RTLD_GLOBAL", + "RTLD_LAZY", + "RTLD_LOCAL", + "RTLD_NODELETE", + "RTLD_NOLOAD", + "RTLD_NOW", + "SCHED_FIFO", + "SCHED_OTHER", + "SCHED_RR", + "SEEK_DATA", + "SEEK_HOLE", + "ST_NOSUID", + "ST_RDONLY", + "WCONTINUED", + "WCOREDUMP", + "WEXITED", + "WEXITSTATUS", + "WIFCONTINUED", + "WIFEXITED", + "WIFSIGNALED", + "WIFSTOPPED", + "WNOHANG", + "WNOWAIT", + "WSTOPPED", + "WSTOPSIG", + "WTERMSIG", + "WUNTRACED", + "chown", + "chroot", + "confstr", + "confstr_names", + "ctermid", + "environb", + "fchdir", + "fchown", + "fork", + "forkpty", + "fpathconf", + "fstatvfs", + "fwalk", + "getegid", + "getenvb", + "geteuid", + "getgid", + "getgrouplist", + "getgroups", + "getloadavg", + "getpgid", + "getpgrp", + "getpriority", + "getsid", + "getuid", + "initgroups", + "killpg", + "lchown", + "lockf", + "major", + "makedev", + "minor", + "mkfifo", + "mknod", + "nice", + "openpty", + "pathconf", + "pathconf_names", + "posix_spawn", + "posix_spawnp", + "pread", + "preadv", + "pwrite", + "pwritev", + "readv", + "register_at_fork", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_yield", + "sendfile", + "setegid", + "seteuid", + "setgid", + "setgroups", + "setpgid", + "setpgrp", + "setpriority", + "setregid", + "setreuid", + "setsid", + "setuid", + "spawnlp", + "spawnlpe", + "spawnvp", + "spawnvpe", + "statvfs", + "sync", + "sysconf", + "sysconf_names", + "tcgetpgrp", + "tcsetpgrp", + "ttyname", + "uname", + "wait", + "wait3", + "wait4", + "writev", + ] +if sys.platform != "win32" and sys.version_info >= (3, 13): + __all__ += ["grantpt", "posix_openpt", "ptsname", "unlockpt"] +if sys.platform != "win32" and sys.version_info >= (3, 11): + __all__ += ["login_tty"] +if sys.platform != "win32" and sys.version_info >= (3, 10): + __all__ += ["O_FSYNC"] +if sys.platform != "win32" and sys.version_info >= (3, 9): + __all__ += ["CLD_KILLED", "CLD_STOPPED"] +if sys.platform != "darwin" and sys.platform != "win32": + __all__ += [ + "POSIX_FADV_DONTNEED", + "POSIX_FADV_NOREUSE", + "POSIX_FADV_NORMAL", + "POSIX_FADV_RANDOM", + "POSIX_FADV_SEQUENTIAL", + "POSIX_FADV_WILLNEED", + "RWF_DSYNC", + "RWF_HIPRI", + "RWF_NOWAIT", + "RWF_SYNC", + "ST_APPEND", + "ST_MANDLOCK", + "ST_NOATIME", + "ST_NODEV", + "ST_NODIRATIME", + "ST_NOEXEC", + "ST_RELATIME", + "ST_SYNCHRONOUS", + "ST_WRITE", + "fdatasync", + "getresgid", + "getresuid", + "pipe2", + "posix_fadvise", + "posix_fallocate", + "sched_getaffinity", + "sched_getparam", + "sched_getscheduler", + "sched_param", + "sched_rr_get_interval", + "sched_setaffinity", + "sched_setparam", + "sched_setscheduler", + "setresgid", + "setresuid", + ] +if sys.platform != "linux" and sys.platform != "win32": + __all__ += ["O_EXLOCK", "O_SHLOCK", "chflags", "lchflags"] +if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13): + __all__ += ["O_EXEC", "O_SEARCH"] +if sys.platform != "darwin" or sys.version_info >= (3, 13): + if sys.platform != "win32": + __all__ += ["waitid", "waitid_result"] +if sys.platform != "win32" or sys.version_info >= (3, 13): + __all__ += ["fchmod"] + if sys.platform != "linux": + __all__ += ["lchmod"] +if sys.platform != "win32" or sys.version_info >= (3, 12): + __all__ += ["get_blocking", "set_blocking"] +if sys.platform != "win32" or sys.version_info >= (3, 11): + __all__ += ["EX_OK"] +if sys.platform != "win32" or sys.version_info >= (3, 9): + __all__ += ["unsetenv"] + # This unnecessary alias is to work around various errors path = _path @@ -125,15 +564,16 @@ if sys.platform != "win32": CLD_KILLED: int CLD_STOPPED: int - # TODO: SCHED_RESET_ON_FORK not available on darwin? - # TODO: SCHED_BATCH and SCHED_IDLE are linux only? - SCHED_OTHER: int # some flavors of Unix - SCHED_BATCH: int # some flavors of Unix - SCHED_IDLE: int # some flavors of Unix - SCHED_SPORADIC: int # some flavors of Unix - SCHED_FIFO: int # some flavors of Unix - SCHED_RR: int # some flavors of Unix - SCHED_RESET_ON_FORK: int # some flavors of Unix + SCHED_OTHER: int + SCHED_FIFO: int + SCHED_RR: int + if sys.platform != "darwin" and sys.platform != "linux": + SCHED_SPORADIC: int + +if sys.platform == "linux": + SCHED_BATCH: int + SCHED_IDLE: int + SCHED_RESET_ON_FORK: int if sys.platform != "win32": RTLD_LAZY: int @@ -158,8 +598,8 @@ SEEK_SET: int SEEK_CUR: int SEEK_END: int if sys.platform != "win32": - SEEK_DATA: int # some flavors of Unix - SEEK_HOLE: int # some flavors of Unix + SEEK_DATA: int + SEEK_HOLE: int O_RDONLY: int O_WRONLY: int @@ -168,34 +608,50 @@ O_APPEND: int O_CREAT: int O_EXCL: int O_TRUNC: int -# We don't use sys.platform for O_* flags to denote platform-dependent APIs because some codes, -# including tests for mypy, use a more finer way than sys.platform before using these APIs -# See https://github.com/python/typeshed/pull/2286 for discussions -O_DSYNC: int # Unix only -O_RSYNC: int # Unix only -O_SYNC: int # Unix only -O_NDELAY: int # Unix only -O_NONBLOCK: int # Unix only -O_NOCTTY: int # Unix only -O_CLOEXEC: int # Unix only -O_SHLOCK: int # Unix only -O_EXLOCK: int # Unix only -O_BINARY: int # Windows only -O_NOINHERIT: int # Windows only -O_SHORT_LIVED: int # Windows only -O_TEMPORARY: int # Windows only -O_RANDOM: int # Windows only -O_SEQUENTIAL: int # Windows only -O_TEXT: int # Windows only -O_ASYNC: int # Gnu extension if in C library -O_DIRECT: int # Gnu extension if in C library -O_DIRECTORY: int # Gnu extension if in C library -O_NOFOLLOW: int # Gnu extension if in C library -O_NOATIME: int # Gnu extension if in C library -O_PATH: int # Gnu extension if in C library -O_TMPFILE: int # Gnu extension if in C library -O_LARGEFILE: int # Gnu extension if in C library -O_ACCMODE: int # TODO: when does this exist? +if sys.platform == "win32": + O_BINARY: int + O_NOINHERIT: int + O_SHORT_LIVED: int + O_TEMPORARY: int + O_RANDOM: int + O_SEQUENTIAL: int + O_TEXT: int + +if sys.platform != "win32": + O_DSYNC: int + O_SYNC: int + O_NDELAY: int + O_NONBLOCK: int + O_NOCTTY: int + O_CLOEXEC: int + O_ASYNC: int # Gnu extension if in C library + O_DIRECTORY: int # Gnu extension if in C library + O_NOFOLLOW: int # Gnu extension if in C library + O_ACCMODE: int # TODO: when does this exist? + +if sys.platform == "linux": + O_RSYNC: int + O_DIRECT: int # Gnu extension if in C library + O_NOATIME: int # Gnu extension if in C library + O_PATH: int # Gnu extension if in C library + O_TMPFILE: int # Gnu extension if in C library + O_LARGEFILE: int # Gnu extension if in C library + +if sys.platform != "linux" and sys.platform != "win32": + O_SHLOCK: int + O_EXLOCK: int + +if sys.platform == "darwin" and sys.version_info >= (3, 10): + O_EVTONLY: int + O_NOFOLLOW_ANY: int + O_SYMLINK: int + +if sys.platform != "win32" and sys.version_info >= (3, 10): + O_FSYNC: int + +if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13): + O_EXEC: int + O_SEARCH: int if sys.platform != "win32" and sys.platform != "darwin": # posix, but apparently missing on macos @@ -413,8 +869,11 @@ In the future, this property will contain the last metadata change time.""" # Attributes documented as sometimes appearing, but deliberately omitted from the stub: `st_creator`, `st_rsize`, `st_type`. # See https://github.com/python/typeshed/pull/6560#issuecomment-991253327 +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it will be +# on the allowlist for use as a Protocol starting in 3.14. @runtime_checkable -class PathLike(Protocol[AnyStr_co]): +class PathLike(ABC, Protocol[AnyStr_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] @abstractmethod def __fspath__(self) -> AnyStr_co: ... @@ -794,9 +1253,12 @@ def replace( src: StrOrBytesPath, dst: StrOrBytesPath, *, src_dir_fd: int | None = None, dst_dir_fd: int | None = None ) -> None: ... def rmdir(path: StrOrBytesPath, *, dir_fd: int | None = None) -> None: ... - -class _ScandirIterator(Iterator[DirEntry[AnyStr]], AbstractContextManager[_ScandirIterator[AnyStr], None]): +@final +class _ScandirIterator(Generic[AnyStr]): + def __del__(self) -> None: ... + def __iter__(self) -> Self: ... def __next__(self) -> DirEntry[AnyStr]: ... + def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi index c47ecdc51df4..73393eada02c 100644 --- a/mypy/typeshed/stdlib/platform.pyi +++ b/mypy/typeshed/stdlib/platform.pyi @@ -1,5 +1,6 @@ import sys -from typing import NamedTuple +from typing import NamedTuple, type_check_only +from typing_extensions import Self def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ... def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ... @@ -14,13 +15,40 @@ def java_ver( def system_alias(system: str, release: str, version: str) -> tuple[str, str, str]: ... def architecture(executable: str = sys.executable, bits: str = "", linkage: str = "") -> tuple[str, str]: ... -class uname_result(NamedTuple): - system: str - node: str - release: str - version: str - machine: str - processor: str +if sys.version_info >= (3, 9): + # This class is not exposed. It calls itself platform.uname_result_base. + # At runtime it only has 5 fields. + @type_check_only + class _uname_result_base(NamedTuple): + system: str + node: str + release: str + version: str + machine: str + # This base class doesn't have this field at runtime, but claiming it + # does is the least bad way to handle the situation. Nobody really + # sees this class anyway. See #13068 + processor: str + + # uname_result emulates a 6-field named tuple, but the processor field + # is lazily evaluated rather than being passed in to the constructor. + class uname_result(_uname_result_base): + if sys.version_info >= (3, 10): + __match_args__ = ("system", "node", "release", "version", "machine") # pyright: ignore[reportAssignmentType] + + def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ... + @property + def processor(self) -> str: ... + +else: + # On 3.8, uname_result is actually just a regular NamedTuple. + class uname_result(NamedTuple): + system: str + node: str + release: str + version: str + machine: str + processor: str def uname() -> uname_result: ... def system() -> str: ... diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 1a4f22af82cf..7a4d6cb4bdbe 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -29,22 +29,20 @@ if sys.platform != "win32": F_TLOCK as F_TLOCK, F_ULOCK as F_ULOCK, NGROUPS_MAX as NGROUPS_MAX, + O_ACCMODE as O_ACCMODE, O_APPEND as O_APPEND, O_ASYNC as O_ASYNC, + O_CLOEXEC as O_CLOEXEC, O_CREAT as O_CREAT, - O_DIRECT as O_DIRECT, O_DIRECTORY as O_DIRECTORY, O_DSYNC as O_DSYNC, O_EXCL as O_EXCL, - O_LARGEFILE as O_LARGEFILE, O_NDELAY as O_NDELAY, - O_NOATIME as O_NOATIME, O_NOCTTY as O_NOCTTY, O_NOFOLLOW as O_NOFOLLOW, O_NONBLOCK as O_NONBLOCK, O_RDONLY as O_RDONLY, O_RDWR as O_RDWR, - O_RSYNC as O_RSYNC, O_SYNC as O_SYNC, O_TRUNC as O_TRUNC, O_WRONLY as O_WRONLY, @@ -64,13 +62,9 @@ if sys.platform != "win32": RTLD_NODELETE as RTLD_NODELETE, RTLD_NOLOAD as RTLD_NOLOAD, RTLD_NOW as RTLD_NOW, - SCHED_BATCH as SCHED_BATCH, SCHED_FIFO as SCHED_FIFO, - SCHED_IDLE as SCHED_IDLE, SCHED_OTHER as SCHED_OTHER, - SCHED_RESET_ON_FORK as SCHED_RESET_ON_FORK, SCHED_RR as SCHED_RR, - SCHED_SPORADIC as SCHED_SPORADIC, SEEK_DATA as SEEK_DATA, SEEK_HOLE as SEEK_HOLE, ST_NOSUID as ST_NOSUID, @@ -233,6 +227,9 @@ if sys.platform != "win32": if sys.version_info >= (3, 9): from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode + if sys.version_info >= (3, 10): + from os import O_FSYNC as O_FSYNC + if sys.version_info >= (3, 11): from os import login_tty as login_tty @@ -254,10 +251,13 @@ if sys.platform != "win32": ) if sys.platform != "linux": - from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod + from os import O_EXLOCK as O_EXLOCK, O_SHLOCK as O_SHLOCK, chflags as chflags, lchflags as lchflags, lchmod as lchmod if sys.platform != "linux" and sys.platform != "darwin": - from os import EX_NOTFOUND as EX_NOTFOUND + from os import EX_NOTFOUND as EX_NOTFOUND, SCHED_SPORADIC as SCHED_SPORADIC + + if sys.platform != "linux" and sys.version_info >= (3, 13): + from os import O_EXEC as O_EXEC, O_SEARCH as O_SEARCH if sys.platform != "darwin": from os import ( @@ -271,6 +271,15 @@ if sys.platform != "win32": RWF_HIPRI as RWF_HIPRI, RWF_NOWAIT as RWF_NOWAIT, RWF_SYNC as RWF_SYNC, + ST_APPEND as ST_APPEND, + ST_MANDLOCK as ST_MANDLOCK, + ST_NOATIME as ST_NOATIME, + ST_NODEV as ST_NODEV, + ST_NODIRATIME as ST_NODIRATIME, + ST_NOEXEC as ST_NOEXEC, + ST_RELATIME as ST_RELATIME, + ST_SYNCHRONOUS as ST_SYNCHRONOUS, + ST_WRITE as ST_WRITE, fdatasync as fdatasync, getresgid as getresgid, getresuid as getresuid, @@ -315,7 +324,16 @@ if sys.platform != "win32": MFD_HUGE_MASK as MFD_HUGE_MASK, MFD_HUGE_SHIFT as MFD_HUGE_SHIFT, MFD_HUGETLB as MFD_HUGETLB, + O_DIRECT as O_DIRECT, + O_LARGEFILE as O_LARGEFILE, + O_NOATIME as O_NOATIME, + O_PATH as O_PATH, + O_RSYNC as O_RSYNC, + O_TMPFILE as O_TMPFILE, RTLD_DEEPBIND as RTLD_DEEPBIND, + SCHED_BATCH as SCHED_BATCH, + SCHED_IDLE as SCHED_IDLE, + SCHED_RESET_ON_FORK as SCHED_RESET_ON_FORK, XATTR_CREATE as XATTR_CREATE, XATTR_REPLACE as XATTR_REPLACE, XATTR_SIZE_MAX as XATTR_SIZE_MAX, @@ -373,6 +391,8 @@ if sys.platform != "win32": PRIO_DARWIN_PROCESS as PRIO_DARWIN_PROCESS, PRIO_DARWIN_THREAD as PRIO_DARWIN_THREAD, ) + if sys.platform == "darwin" and sys.version_info >= (3, 10): + from os import O_EVTONLY as O_EVTONLY, O_NOFOLLOW_ANY as O_NOFOLLOW_ANY, O_SYMLINK as O_SYMLINK # Not same as os.environ or os.environb # Because of this variable, we can't do "from posix import *" in os/__init__.pyi diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 83256b433035..d41fa202cf77 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -1,11 +1,16 @@ import sys -from _typeshed import StrEnum, StrOrBytesPath +from _typeshed import StrOrBytesPath from collections.abc import Iterable from cProfile import Profile as _cProfile from profile import Profile from typing import IO, Any, Literal, overload from typing_extensions import Self, TypeAlias +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from enum import Enum + if sys.version_info >= (3, 9): __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] else: @@ -13,16 +18,29 @@ else: _Selector: TypeAlias = str | float | int -class SortKey(StrEnum): - CALLS = "calls" - CUMULATIVE = "cumulative" - FILENAME = "filename" - LINE = "line" - NAME = "name" - NFL = "nfl" - PCALLS = "pcalls" - STDNAME = "stdname" - TIME = "time" +if sys.version_info >= (3, 11): + class SortKey(StrEnum): + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" + +else: + class SortKey(str, Enum): + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" if sys.version_info >= (3, 9): from dataclasses import dataclass diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index d38259a20d72..21e676052098 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -2,7 +2,7 @@ from _typeshed import ReadableBuffer, SupportsRead from collections.abc import Callable from pyexpat import errors as errors, model as model from typing import Any, Final, final -from typing_extensions import TypeAlias +from typing_extensions import CapsuleType, TypeAlias from xml.parsers.expat import ExpatError as ExpatError EXPAT_VERSION: Final[str] # undocumented @@ -78,3 +78,5 @@ def ErrorString(code: int, /) -> str: ... def ParserCreate( encoding: str | None = None, namespace_separator: str | None = None, intern: dict[str, Any] | None = None ) -> XMLParserType: ... + +expat_CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/sched.pyi b/mypy/typeshed/stdlib/sched.pyi index 75dd63d0414a..ace501430847 100644 --- a/mypy/typeshed/stdlib/sched.pyi +++ b/mypy/typeshed/stdlib/sched.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from typing import Any, NamedTuple +from typing import Any, NamedTuple, type_check_only from typing_extensions import TypeAlias __all__ = ["scheduler"] @@ -17,13 +17,16 @@ if sys.version_info >= (3, 10): kwargs: dict[str, Any] else: - class Event(NamedTuple): + @type_check_only + class _EventBase(NamedTuple): time: float priority: Any action: _ActionCallback argument: tuple[Any, ...] kwargs: dict[str, Any] + class Event(_EventBase): ... + class scheduler: timefunc: Callable[[], float] delayfunc: Callable[[float], object] diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index 39a93ce4d0f3..e42bba757fc3 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -28,7 +28,6 @@ from _socket import ( IP_MULTICAST_LOOP as IP_MULTICAST_LOOP, IP_MULTICAST_TTL as IP_MULTICAST_TTL, IP_OPTIONS as IP_OPTIONS, - IP_RECVDSTADDR as IP_RECVDSTADDR, IP_TOS as IP_TOS, IP_TTL as IP_TTL, IPPORT_RESERVED as IPPORT_RESERVED, @@ -38,17 +37,13 @@ from _socket import ( IPPROTO_EGP as IPPROTO_EGP, IPPROTO_ESP as IPPROTO_ESP, IPPROTO_FRAGMENT as IPPROTO_FRAGMENT, - IPPROTO_GGP as IPPROTO_GGP, IPPROTO_HOPOPTS as IPPROTO_HOPOPTS, IPPROTO_ICMP as IPPROTO_ICMP, IPPROTO_ICMPV6 as IPPROTO_ICMPV6, IPPROTO_IDP as IPPROTO_IDP, IPPROTO_IGMP as IPPROTO_IGMP, IPPROTO_IP as IPPROTO_IP, - IPPROTO_IPV4 as IPPROTO_IPV4, IPPROTO_IPV6 as IPPROTO_IPV6, - IPPROTO_MAX as IPPROTO_MAX, - IPPROTO_ND as IPPROTO_ND, IPPROTO_NONE as IPPROTO_NONE, IPPROTO_PIM as IPPROTO_PIM, IPPROTO_PUP as IPPROTO_PUP, @@ -93,7 +88,6 @@ from _socket import ( SO_SNDLOWAT as SO_SNDLOWAT, SO_SNDTIMEO as SO_SNDTIMEO, SO_TYPE as SO_TYPE, - SO_USELOOPBACK as SO_USELOOPBACK, SOL_IP as SOL_IP, SOL_SOCKET as SOL_SOCKET, SOL_TCP as SOL_TCP, @@ -139,8 +133,176 @@ from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase from typing import Any, Literal, Protocol, SupportsIndex, overload from typing_extensions import Self +__all__ = [ + "fromfd", + "getfqdn", + "create_connection", + "create_server", + "has_dualstack_ipv6", + "AddressFamily", + "SocketKind", + "AF_APPLETALK", + "AF_DECnet", + "AF_INET", + "AF_INET6", + "AF_IPX", + "AF_SNA", + "AF_UNSPEC", + "AI_ADDRCONFIG", + "AI_ALL", + "AI_CANONNAME", + "AI_NUMERICHOST", + "AI_NUMERICSERV", + "AI_PASSIVE", + "AI_V4MAPPED", + "CAPI", + "EAI_AGAIN", + "EAI_BADFLAGS", + "EAI_FAIL", + "EAI_FAMILY", + "EAI_MEMORY", + "EAI_NODATA", + "EAI_NONAME", + "EAI_SERVICE", + "EAI_SOCKTYPE", + "INADDR_ALLHOSTS_GROUP", + "INADDR_ANY", + "INADDR_BROADCAST", + "INADDR_LOOPBACK", + "INADDR_MAX_LOCAL_GROUP", + "INADDR_NONE", + "INADDR_UNSPEC_GROUP", + "IPPORT_RESERVED", + "IPPORT_USERRESERVED", + "IPPROTO_AH", + "IPPROTO_DSTOPTS", + "IPPROTO_EGP", + "IPPROTO_ESP", + "IPPROTO_FRAGMENT", + "IPPROTO_HOPOPTS", + "IPPROTO_ICMP", + "IPPROTO_ICMPV6", + "IPPROTO_IDP", + "IPPROTO_IGMP", + "IPPROTO_IP", + "IPPROTO_IPV6", + "IPPROTO_NONE", + "IPPROTO_PIM", + "IPPROTO_PUP", + "IPPROTO_RAW", + "IPPROTO_ROUTING", + "IPPROTO_SCTP", + "IPPROTO_TCP", + "IPPROTO_UDP", + "IPV6_CHECKSUM", + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_RECVTCLASS", + "IPV6_TCLASS", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_HDRINCL", + "IP_MULTICAST_IF", + "IP_MULTICAST_LOOP", + "IP_MULTICAST_TTL", + "IP_OPTIONS", + "IP_TOS", + "IP_TTL", + "MSG_CTRUNC", + "MSG_DONTROUTE", + "MSG_OOB", + "MSG_PEEK", + "MSG_TRUNC", + "MSG_WAITALL", + "NI_DGRAM", + "NI_MAXHOST", + "NI_MAXSERV", + "NI_NAMEREQD", + "NI_NOFQDN", + "NI_NUMERICHOST", + "NI_NUMERICSERV", + "SHUT_RD", + "SHUT_RDWR", + "SHUT_WR", + "SOCK_DGRAM", + "SOCK_RAW", + "SOCK_RDM", + "SOCK_SEQPACKET", + "SOCK_STREAM", + "SOL_IP", + "SOL_SOCKET", + "SOL_TCP", + "SOL_UDP", + "SOMAXCONN", + "SO_ACCEPTCONN", + "SO_BROADCAST", + "SO_DEBUG", + "SO_DONTROUTE", + "SO_ERROR", + "SO_KEEPALIVE", + "SO_LINGER", + "SO_OOBINLINE", + "SO_RCVBUF", + "SO_RCVLOWAT", + "SO_RCVTIMEO", + "SO_REUSEADDR", + "SO_SNDBUF", + "SO_SNDLOWAT", + "SO_SNDTIMEO", + "SO_TYPE", + "SocketType", + "TCP_FASTOPEN", + "TCP_KEEPCNT", + "TCP_KEEPINTVL", + "TCP_MAXSEG", + "TCP_NODELAY", + "close", + "dup", + "error", + "gaierror", + "getaddrinfo", + "getdefaulttimeout", + "gethostbyaddr", + "gethostbyname", + "gethostbyname_ex", + "gethostname", + "getnameinfo", + "getprotobyname", + "getservbyname", + "getservbyport", + "has_ipv6", + "herror", + "htonl", + "htons", + "if_indextoname", + "if_nameindex", + "if_nametoindex", + "inet_aton", + "inet_ntoa", + "inet_ntop", + "inet_pton", + "ntohl", + "ntohs", + "setdefaulttimeout", + "socket", + "socketpair", + "timeout", +] + if sys.platform == "win32": from _socket import ( + IPPROTO_CBT as IPPROTO_CBT, + IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, + IPPROTO_IGP as IPPROTO_IGP, + IPPROTO_L2TP as IPPROTO_L2TP, + IPPROTO_PGM as IPPROTO_PGM, + IPPROTO_RDP as IPPROTO_RDP, + IPPROTO_ST as IPPROTO_ST, RCVALL_MAX as RCVALL_MAX, RCVALL_OFF as RCVALL_OFF, RCVALL_ON as RCVALL_ON, @@ -151,6 +313,28 @@ if sys.platform == "win32": SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE, ) + __all__ += [ + "IPPROTO_CBT", + "IPPROTO_ICLFXBM", + "IPPROTO_IGP", + "IPPROTO_L2TP", + "IPPROTO_PGM", + "IPPROTO_RDP", + "IPPROTO_ST", + "RCVALL_MAX", + "RCVALL_OFF", + "RCVALL_ON", + "RCVALL_SOCKETLEVELONLY", + "SIO_KEEPALIVE_VALS", + "SIO_LOOPBACK_FAST_PATH", + "SIO_RCVALL", + "SO_EXCLUSIVEADDRUSE", + "fromshare", + "errorTab", + "MSG_BCAST", + "MSG_MCAST", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): from _socket import ( IPV6_DONTFRAG as IPV6_DONTFRAG, @@ -161,33 +345,26 @@ if sys.platform != "darwin" or sys.version_info >= (3, 9): IPV6_RTHDR as IPV6_RTHDR, ) + __all__ += ["IPV6_DONTFRAG", "IPV6_HOPLIMIT", "IPV6_HOPOPTS", "IPV6_PKTINFO", "IPV6_RECVRTHDR", "IPV6_RTHDR"] + if sys.platform == "darwin": from _socket import PF_SYSTEM as PF_SYSTEM, SYSPROTO_CONTROL as SYSPROTO_CONTROL + __all__ += ["PF_SYSTEM", "SYSPROTO_CONTROL", "AF_SYSTEM"] + if sys.platform != "darwin": - from _socket import ( - IPPROTO_CBT as IPPROTO_CBT, - IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, - IPPROTO_IGP as IPPROTO_IGP, - IPPROTO_L2TP as IPPROTO_L2TP, - IPPROTO_PGM as IPPROTO_PGM, - IPPROTO_RDP as IPPROTO_RDP, - IPPROTO_ST as IPPROTO_ST, - TCP_KEEPIDLE as TCP_KEEPIDLE, - ) + from _socket import TCP_KEEPIDLE as TCP_KEEPIDLE + + __all__ += ["TCP_KEEPIDLE", "AF_IRDA", "MSG_ERRQUEUE"] if sys.version_info >= (3, 10): from _socket import IP_RECVTOS as IP_RECVTOS -elif sys.platform != "win32" and sys.platform != "darwin": - from _socket import IP_RECVTOS as IP_RECVTOS + + __all__ += ["IP_RECVTOS"] if sys.platform != "win32" and sys.platform != "darwin": from _socket import ( - IP_BIND_ADDRESS_NO_PORT as IP_BIND_ADDRESS_NO_PORT, IP_TRANSPARENT as IP_TRANSPARENT, - IPPROTO_BIP as IPPROTO_BIP, - IPPROTO_MOBILE as IPPROTO_MOBILE, - IPPROTO_VRRP as IPPROTO_VRRP, IPX_TYPE as IPX_TYPE, SCM_CREDENTIALS as SCM_CREDENTIALS, SO_BINDTODEVICE as SO_BINDTODEVICE, @@ -199,7 +376,6 @@ if sys.platform != "win32" and sys.platform != "darwin": SO_PEERSEC as SO_PEERSEC, SO_PRIORITY as SO_PRIORITY, SO_PROTOCOL as SO_PROTOCOL, - SO_SETFIB as SO_SETFIB, SOL_ATALK as SOL_ATALK, SOL_AX25 as SOL_AX25, SOL_HCI as SOL_HCI, @@ -217,15 +393,59 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP, ) + __all__ += [ + "IP_TRANSPARENT", + "SCM_CREDENTIALS", + "SO_BINDTODEVICE", + "SO_DOMAIN", + "SO_MARK", + "SO_PASSCRED", + "SO_PASSSEC", + "SO_PEERCRED", + "SO_PEERSEC", + "SO_PRIORITY", + "SO_PROTOCOL", + "TCP_CONGESTION", + "TCP_CORK", + "TCP_DEFER_ACCEPT", + "TCP_INFO", + "TCP_LINGER2", + "TCP_QUICKACK", + "TCP_SYNCNT", + "TCP_USER_TIMEOUT", + "TCP_WINDOW_CLAMP", + "AF_ASH", + "AF_ATMPVC", + "AF_ATMSVC", + "AF_AX25", + "AF_BRIDGE", + "AF_ECONET", + "AF_KEY", + "AF_LLC", + "AF_NETBEUI", + "AF_NETROM", + "AF_PPPOX", + "AF_ROSE", + "AF_SECURITY", + "AF_WANPIPE", + "AF_X25", + "MSG_CMSG_CLOEXEC", + "MSG_CONFIRM", + "MSG_FASTOPEN", + "MSG_MORE", + ] + +if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11): + from _socket import IP_BIND_ADDRESS_NO_PORT as IP_BIND_ADDRESS_NO_PORT + + __all__ += ["IP_BIND_ADDRESS_NO_PORT"] + if sys.platform != "win32": from _socket import ( CMSG_LEN as CMSG_LEN, CMSG_SPACE as CMSG_SPACE, EAI_ADDRFAMILY as EAI_ADDRFAMILY, - EAI_BADHINTS as EAI_BADHINTS, - EAI_MAX as EAI_MAX, EAI_OVERFLOW as EAI_OVERFLOW, - EAI_PROTOCOL as EAI_PROTOCOL, EAI_SYSTEM as EAI_SYSTEM, IP_DEFAULT_MULTICAST_LOOP as IP_DEFAULT_MULTICAST_LOOP, IP_DEFAULT_MULTICAST_TTL as IP_DEFAULT_MULTICAST_TTL, @@ -233,23 +453,45 @@ if sys.platform != "win32": IP_RECVOPTS as IP_RECVOPTS, IP_RECVRETOPTS as IP_RECVRETOPTS, IP_RETOPTS as IP_RETOPTS, - IPPROTO_EON as IPPROTO_EON, IPPROTO_GRE as IPPROTO_GRE, - IPPROTO_HELLO as IPPROTO_HELLO, - IPPROTO_IPCOMP as IPPROTO_IPCOMP, IPPROTO_IPIP as IPPROTO_IPIP, IPPROTO_RSVP as IPPROTO_RSVP, IPPROTO_TP as IPPROTO_TP, - IPPROTO_XTP as IPPROTO_XTP, IPV6_RTHDR_TYPE_0 as IPV6_RTHDR_TYPE_0, - LOCAL_PEERCRED as LOCAL_PEERCRED, - SCM_CREDS as SCM_CREDS, SCM_RIGHTS as SCM_RIGHTS, SO_REUSEPORT as SO_REUSEPORT, TCP_NOTSENT_LOWAT as TCP_NOTSENT_LOWAT, sethostname as sethostname, ) + __all__ += [ + "CMSG_LEN", + "CMSG_SPACE", + "EAI_ADDRFAMILY", + "EAI_OVERFLOW", + "EAI_SYSTEM", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_MAX_MEMBERSHIPS", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RETOPTS", + "IPPROTO_GRE", + "IPPROTO_IPIP", + "IPPROTO_RSVP", + "IPPROTO_TP", + "IPV6_RTHDR_TYPE_0", + "SCM_RIGHTS", + "SO_REUSEPORT", + "TCP_NOTSENT_LOWAT", + "sethostname", + "AF_ROUTE", + "AF_UNIX", + "MSG_DONTWAIT", + "MSG_EOR", + "MSG_NOSIGNAL", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): from _socket import ( IPV6_DSTOPTS as IPV6_DSTOPTS, @@ -261,19 +503,36 @@ if sys.platform != "win32": IPV6_RECVPATHMTU as IPV6_RECVPATHMTU, IPV6_RECVPKTINFO as IPV6_RECVPKTINFO, IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS, - IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU, ) -if sys.platform != "darwin": + __all__ += [ + "IPV6_DSTOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPATHMTU", + "IPV6_RECVPKTINFO", + "IPV6_RTHDRDSTOPTS", + ] + +if sys.platform != "darwin" and sys.platform != "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM + __all__ += ["BDADDR_ANY", "BDADDR_LOCAL", "BTPROTO_RFCOMM"] + if sys.platform == "darwin" and sys.version_info >= (3, 10): from _socket import TCP_KEEPALIVE as TCP_KEEPALIVE + __all__ += ["TCP_KEEPALIVE"] + if sys.platform == "darwin" and sys.version_info >= (3, 11): from _socket import TCP_CONNECTION_INFO as TCP_CONNECTION_INFO + __all__ += ["TCP_CONNECTION_INFO"] + if sys.platform == "linux": from _socket import ( ALG_OP_DECRYPT as ALG_OP_DECRYPT, @@ -317,7 +576,6 @@ if sys.platform == "linux": CAN_ERR_MASK as CAN_ERR_MASK, CAN_ISOTP as CAN_ISOTP, CAN_RAW as CAN_RAW, - CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES as CAN_RAW_FD_FRAMES, CAN_RAW_FILTER as CAN_RAW_FILTER, CAN_RAW_LOOPBACK as CAN_RAW_LOOPBACK, @@ -325,19 +583,13 @@ if sys.platform == "linux": CAN_RTR_FLAG as CAN_RTR_FLAG, CAN_SFF_MASK as CAN_SFF_MASK, IOCTL_VM_SOCKETS_GET_LOCAL_CID as IOCTL_VM_SOCKETS_GET_LOCAL_CID, - NETLINK_ARPD as NETLINK_ARPD, NETLINK_CRYPTO as NETLINK_CRYPTO, NETLINK_DNRTMSG as NETLINK_DNRTMSG, NETLINK_FIREWALL as NETLINK_FIREWALL, NETLINK_IP6_FW as NETLINK_IP6_FW, NETLINK_NFLOG as NETLINK_NFLOG, NETLINK_ROUTE as NETLINK_ROUTE, - NETLINK_ROUTE6 as NETLINK_ROUTE6, - NETLINK_SKIP as NETLINK_SKIP, - NETLINK_TAPBASE as NETLINK_TAPBASE, - NETLINK_TCPDIAG as NETLINK_TCPDIAG, NETLINK_USERSOCK as NETLINK_USERSOCK, - NETLINK_W1 as NETLINK_W1, NETLINK_XFRM as NETLINK_XFRM, PACKET_BROADCAST as PACKET_BROADCAST, PACKET_FASTROUTE as PACKET_FASTROUTE, @@ -354,7 +606,6 @@ if sys.platform == "linux": RDS_CMSG_RDMA_DEST as RDS_CMSG_RDMA_DEST, RDS_CMSG_RDMA_MAP as RDS_CMSG_RDMA_MAP, RDS_CMSG_RDMA_STATUS as RDS_CMSG_RDMA_STATUS, - RDS_CMSG_RDMA_UPDATE as RDS_CMSG_RDMA_UPDATE, RDS_CONG_MONITOR as RDS_CONG_MONITOR, RDS_FREE_MR as RDS_FREE_MR, RDS_GET_MR as RDS_GET_MR, @@ -404,10 +655,130 @@ if sys.platform == "linux": VMADDR_PORT_ANY as VMADDR_PORT_ANY, ) + __all__ += [ + "ALG_OP_DECRYPT", + "ALG_OP_ENCRYPT", + "ALG_OP_SIGN", + "ALG_OP_VERIFY", + "ALG_SET_AEAD_ASSOCLEN", + "ALG_SET_AEAD_AUTHSIZE", + "ALG_SET_IV", + "ALG_SET_KEY", + "ALG_SET_OP", + "ALG_SET_PUBKEY", + "CAN_BCM", + "CAN_BCM_CAN_FD_FRAME", + "CAN_BCM_RX_ANNOUNCE_RESUME", + "CAN_BCM_RX_CHANGED", + "CAN_BCM_RX_CHECK_DLC", + "CAN_BCM_RX_DELETE", + "CAN_BCM_RX_FILTER_ID", + "CAN_BCM_RX_NO_AUTOTIMER", + "CAN_BCM_RX_READ", + "CAN_BCM_RX_RTR_FRAME", + "CAN_BCM_RX_SETUP", + "CAN_BCM_RX_STATUS", + "CAN_BCM_RX_TIMEOUT", + "CAN_BCM_SETTIMER", + "CAN_BCM_STARTTIMER", + "CAN_BCM_TX_ANNOUNCE", + "CAN_BCM_TX_COUNTEVT", + "CAN_BCM_TX_CP_CAN_ID", + "CAN_BCM_TX_DELETE", + "CAN_BCM_TX_EXPIRED", + "CAN_BCM_TX_READ", + "CAN_BCM_TX_RESET_MULTI_IDX", + "CAN_BCM_TX_SEND", + "CAN_BCM_TX_SETUP", + "CAN_BCM_TX_STATUS", + "CAN_EFF_FLAG", + "CAN_EFF_MASK", + "CAN_ERR_FLAG", + "CAN_ERR_MASK", + "CAN_ISOTP", + "CAN_RAW", + "CAN_RAW_FD_FRAMES", + "CAN_RAW_FILTER", + "CAN_RAW_LOOPBACK", + "CAN_RAW_RECV_OWN_MSGS", + "CAN_RTR_FLAG", + "CAN_SFF_MASK", + "IOCTL_VM_SOCKETS_GET_LOCAL_CID", + "NETLINK_CRYPTO", + "NETLINK_DNRTMSG", + "NETLINK_FIREWALL", + "NETLINK_IP6_FW", + "NETLINK_NFLOG", + "NETLINK_ROUTE", + "NETLINK_USERSOCK", + "NETLINK_XFRM", + "PACKET_BROADCAST", + "PACKET_FASTROUTE", + "PACKET_HOST", + "PACKET_LOOPBACK", + "PACKET_MULTICAST", + "PACKET_OTHERHOST", + "PACKET_OUTGOING", + "PF_CAN", + "PF_PACKET", + "PF_RDS", + "SO_VM_SOCKETS_BUFFER_MAX_SIZE", + "SO_VM_SOCKETS_BUFFER_MIN_SIZE", + "SO_VM_SOCKETS_BUFFER_SIZE", + "SOL_ALG", + "SOL_CAN_BASE", + "SOL_CAN_RAW", + "SOL_RDS", + "SOL_TIPC", + "TIPC_ADDR_ID", + "TIPC_ADDR_NAME", + "TIPC_ADDR_NAMESEQ", + "TIPC_CFG_SRV", + "TIPC_CLUSTER_SCOPE", + "TIPC_CONN_TIMEOUT", + "TIPC_CRITICAL_IMPORTANCE", + "TIPC_DEST_DROPPABLE", + "TIPC_HIGH_IMPORTANCE", + "TIPC_IMPORTANCE", + "TIPC_LOW_IMPORTANCE", + "TIPC_MEDIUM_IMPORTANCE", + "TIPC_NODE_SCOPE", + "TIPC_PUBLISHED", + "TIPC_SRC_DROPPABLE", + "TIPC_SUB_CANCEL", + "TIPC_SUB_PORTS", + "TIPC_SUB_SERVICE", + "TIPC_SUBSCR_TIMEOUT", + "TIPC_TOP_SRV", + "TIPC_WAIT_FOREVER", + "TIPC_WITHDRAWN", + "TIPC_ZONE_SCOPE", + "VM_SOCKETS_INVALID_VERSION", + "VMADDR_CID_ANY", + "VMADDR_CID_HOST", + "VMADDR_PORT_ANY", + "AF_CAN", + "AF_PACKET", + "AF_RDS", + "AF_TIPC", + "AF_ALG", + "AF_NETLINK", + "AF_VSOCK", + "AF_QIPCRTR", + "SOCK_CLOEXEC", + "SOCK_NONBLOCK", + ] + + if sys.version_info < (3, 11): + from _socket import CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER + + __all__ += ["CAN_RAW_ERR_FILTER"] + if sys.platform == "linux" and sys.version_info >= (3, 9): from _socket import ( CAN_J1939 as CAN_J1939, CAN_RAW_JOIN_FILTERS as CAN_RAW_JOIN_FILTERS, + IPPROTO_UDPLITE as IPPROTO_UDPLITE, J1939_EE_INFO_NONE as J1939_EE_INFO_NONE, J1939_EE_INFO_TX_ABORT as J1939_EE_INFO_TX_ABORT, J1939_FILTER_MAX as J1939_FILTER_MAX, @@ -434,11 +805,97 @@ if sys.platform == "linux" and sys.version_info >= (3, 9): UDPLITE_RECV_CSCOV as UDPLITE_RECV_CSCOV, UDPLITE_SEND_CSCOV as UDPLITE_SEND_CSCOV, ) + + __all__ += [ + "CAN_J1939", + "CAN_RAW_JOIN_FILTERS", + "IPPROTO_UDPLITE", + "J1939_EE_INFO_NONE", + "J1939_EE_INFO_TX_ABORT", + "J1939_FILTER_MAX", + "J1939_IDLE_ADDR", + "J1939_MAX_UNICAST_ADDR", + "J1939_NLA_BYTES_ACKED", + "J1939_NLA_PAD", + "J1939_NO_ADDR", + "J1939_NO_NAME", + "J1939_NO_PGN", + "J1939_PGN_ADDRESS_CLAIMED", + "J1939_PGN_ADDRESS_COMMANDED", + "J1939_PGN_MAX", + "J1939_PGN_PDU1_MAX", + "J1939_PGN_REQUEST", + "SCM_J1939_DEST_ADDR", + "SCM_J1939_DEST_NAME", + "SCM_J1939_ERRQUEUE", + "SCM_J1939_PRIO", + "SO_J1939_ERRQUEUE", + "SO_J1939_FILTER", + "SO_J1939_PROMISC", + "SO_J1939_SEND_PRIO", + "UDPLITE_RECV_CSCOV", + "UDPLITE_SEND_CSCOV", + ] if sys.platform == "linux" and sys.version_info >= (3, 10): from _socket import IPPROTO_MPTCP as IPPROTO_MPTCP + + __all__ += ["IPPROTO_MPTCP"] if sys.platform == "linux" and sys.version_info >= (3, 11): from _socket import SO_INCOMING_CPU as SO_INCOMING_CPU + __all__ += ["SO_INCOMING_CPU"] +if sys.platform == "linux" and sys.version_info >= (3, 12): + from _socket import ( + TCP_CC_INFO as TCP_CC_INFO, + TCP_FASTOPEN_CONNECT as TCP_FASTOPEN_CONNECT, + TCP_FASTOPEN_KEY as TCP_FASTOPEN_KEY, + TCP_FASTOPEN_NO_COOKIE as TCP_FASTOPEN_NO_COOKIE, + TCP_INQ as TCP_INQ, + TCP_MD5SIG as TCP_MD5SIG, + TCP_MD5SIG_EXT as TCP_MD5SIG_EXT, + TCP_QUEUE_SEQ as TCP_QUEUE_SEQ, + TCP_REPAIR as TCP_REPAIR, + TCP_REPAIR_OPTIONS as TCP_REPAIR_OPTIONS, + TCP_REPAIR_QUEUE as TCP_REPAIR_QUEUE, + TCP_REPAIR_WINDOW as TCP_REPAIR_WINDOW, + TCP_SAVE_SYN as TCP_SAVE_SYN, + TCP_SAVED_SYN as TCP_SAVED_SYN, + TCP_THIN_DUPACK as TCP_THIN_DUPACK, + TCP_THIN_LINEAR_TIMEOUTS as TCP_THIN_LINEAR_TIMEOUTS, + TCP_TIMESTAMP as TCP_TIMESTAMP, + TCP_TX_DELAY as TCP_TX_DELAY, + TCP_ULP as TCP_ULP, + TCP_ZEROCOPY_RECEIVE as TCP_ZEROCOPY_RECEIVE, + ) + + __all__ += [ + "TCP_CC_INFO", + "TCP_FASTOPEN_CONNECT", + "TCP_FASTOPEN_KEY", + "TCP_FASTOPEN_NO_COOKIE", + "TCP_INQ", + "TCP_MD5SIG", + "TCP_MD5SIG_EXT", + "TCP_QUEUE_SEQ", + "TCP_REPAIR", + "TCP_REPAIR_OPTIONS", + "TCP_REPAIR_QUEUE", + "TCP_REPAIR_WINDOW", + "TCP_SAVED_SYN", + "TCP_SAVE_SYN", + "TCP_THIN_DUPACK", + "TCP_THIN_LINEAR_TIMEOUTS", + "TCP_TIMESTAMP", + "TCP_TX_DELAY", + "TCP_ULP", + "TCP_ZEROCOPY_RECEIVE", + ] + +if sys.platform == "linux" and sys.version_info >= (3, 13): + from _socket import NI_IDN as NI_IDN, SO_BINDTOIFINDEX as SO_BINDTOIFINDEX + + __all__ += ["NI_IDN", "SO_BINDTOIFINDEX"] + if sys.version_info >= (3, 12): from _socket import ( IP_ADD_SOURCE_MEMBERSHIP as IP_ADD_SOURCE_MEMBERSHIP, @@ -448,6 +905,8 @@ if sys.version_info >= (3, 12): IP_UNBLOCK_SOURCE as IP_UNBLOCK_SOURCE, ) + __all__ += ["IP_ADD_SOURCE_MEMBERSHIP", "IP_BLOCK_SOURCE", "IP_DROP_SOURCE_MEMBERSHIP", "IP_PKTINFO", "IP_UNBLOCK_SOURCE"] + if sys.platform == "win32": from _socket import ( HV_GUID_BROADCAST as HV_GUID_BROADCAST, @@ -462,6 +921,20 @@ if sys.version_info >= (3, 12): HVSOCKET_CONNECT_TIMEOUT_MAX as HVSOCKET_CONNECT_TIMEOUT_MAX, HVSOCKET_CONNECTED_SUSPEND as HVSOCKET_CONNECTED_SUSPEND, ) + + __all__ += [ + "HV_GUID_BROADCAST", + "HV_GUID_CHILDREN", + "HV_GUID_LOOPBACK", + "HV_GUID_PARENT", + "HV_GUID_WILDCARD", + "HV_GUID_ZERO", + "HV_PROTOCOL_RAW", + "HVSOCKET_ADDRESS_FLAG_PASSTHRU", + "HVSOCKET_CONNECT_TIMEOUT", + "HVSOCKET_CONNECT_TIMEOUT_MAX", + "HVSOCKET_CONNECTED_SUSPEND", + ] else: from _socket import ( ETHERTYPE_ARP as ETHERTYPE_ARP, @@ -470,13 +943,88 @@ if sys.version_info >= (3, 12): ETHERTYPE_VLAN as ETHERTYPE_VLAN, ) + __all__ += ["ETHERTYPE_ARP", "ETHERTYPE_IP", "ETHERTYPE_IPV6", "ETHERTYPE_VLAN"] + if sys.platform == "linux": from _socket import ETH_P_ALL as ETH_P_ALL + __all__ += ["ETH_P_ALL"] + if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": # FreeBSD >= 14.0 from _socket import PF_DIVERT as PF_DIVERT + __all__ += ["PF_DIVERT", "AF_DIVERT"] + +if sys.platform != "win32" and sys.version_info >= (3, 9): + __all__ += ["send_fds", "recv_fds"] + +if sys.platform != "win32" or sys.version_info >= (3, 9): + if sys.platform != "linux": + __all__ += ["AF_LINK"] + if sys.platform != "darwin" and sys.platform != "linux": + __all__ += ["AF_BLUETOOTH"] + +if sys.platform == "win32" and sys.version_info >= (3, 12): + __all__ += ["AF_HYPERV"] + +if sys.platform != "win32" and sys.platform != "linux": + from _socket import ( + EAI_BADHINTS as EAI_BADHINTS, + EAI_MAX as EAI_MAX, + EAI_PROTOCOL as EAI_PROTOCOL, + IPPROTO_EON as IPPROTO_EON, + IPPROTO_HELLO as IPPROTO_HELLO, + IPPROTO_IPCOMP as IPPROTO_IPCOMP, + IPPROTO_XTP as IPPROTO_XTP, + LOCAL_PEERCRED as LOCAL_PEERCRED, + SCM_CREDS as SCM_CREDS, + ) + + __all__ += [ + "EAI_BADHINTS", + "EAI_MAX", + "EAI_PROTOCOL", + "IPPROTO_EON", + "IPPROTO_HELLO", + "IPPROTO_IPCOMP", + "IPPROTO_XTP", + "LOCAL_PEERCRED", + "SCM_CREDS", + "AI_DEFAULT", + "AI_MASK", + "AI_V4MAPPED_CFG", + "MSG_EOF", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): + from _socket import IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU + + __all__ += ["IPV6_USE_MIN_MTU"] + +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + from _socket import ( + IPPROTO_BIP as IPPROTO_BIP, + IPPROTO_MOBILE as IPPROTO_MOBILE, + IPPROTO_VRRP as IPPROTO_VRRP, + MSG_BTAG as MSG_BTAG, + MSG_ETAG as MSG_ETAG, + SO_SETFIB as SO_SETFIB, + ) + + __all__ += ["SO_SETFIB", "MSG_BTAG", "MSG_ETAG", "IPPROTO_BIP", "IPPROTO_MOBILE", "IPPROTO_VRRP", "MSG_NOTIFICATION"] + +if sys.platform != "linux": + from _socket import ( + IP_RECVDSTADDR as IP_RECVDSTADDR, + IPPROTO_GGP as IPPROTO_GGP, + IPPROTO_IPV4 as IPPROTO_IPV4, + IPPROTO_MAX as IPPROTO_MAX, + IPPROTO_ND as IPPROTO_ND, + SO_USELOOPBACK as SO_USELOOPBACK, + ) + + __all__ += ["IPPROTO_GGP", "IPPROTO_IPV4", "IPPROTO_MAX", "IPPROTO_ND", "IP_RECVDSTADDR", "SO_USELOOPBACK"] + # Re-exported from errno EBADF: int EAGAIN: int @@ -506,10 +1054,10 @@ class AddressFamily(IntEnum): AF_IRDA = 23 if sys.platform != "win32": AF_ROUTE = 16 - AF_SYSTEM = 32 AF_UNIX = 1 + if sys.platform == "darwin": + AF_SYSTEM = 32 if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5 = ... AF_ASH = 18 AF_ATMPVC = 8 AF_ATMSVC = 20 @@ -535,8 +1083,9 @@ class AddressFamily(IntEnum): AF_VSOCK = 40 AF_QIPCRTR = 42 if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK = 33 - if sys.platform != "darwin": + if sys.platform != "linux": + AF_LINK = 33 + if sys.platform != "darwin" and sys.platform != "linux": AF_BLUETOOTH = 32 if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = 34 @@ -557,11 +1106,12 @@ if sys.platform != "darwin": if sys.platform != "win32": AF_ROUTE = AddressFamily.AF_ROUTE - AF_SYSTEM = AddressFamily.AF_SYSTEM AF_UNIX = AddressFamily.AF_UNIX +if sys.platform == "darwin": + AF_SYSTEM = AddressFamily.AF_SYSTEM + if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5 = AddressFamily.AF_AAL5 AF_ASH = AddressFamily.AF_ASH AF_ATMPVC = AddressFamily.AF_ATMPVC AF_ATMSVC = AddressFamily.AF_ATMSVC @@ -589,8 +1139,9 @@ if sys.platform == "linux": AF_QIPCRTR = AddressFamily.AF_QIPCRTR if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK = AddressFamily.AF_LINK - if sys.platform != "darwin": + if sys.platform != "linux": + AF_LINK = AddressFamily.AF_LINK + if sys.platform != "darwin" and sys.platform != "linux": AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH if sys.platform == "win32" and sys.version_info >= (3, 12): @@ -625,26 +1176,28 @@ class MsgFlag(IntFlag): MSG_PEEK = 2 MSG_TRUNC = 32 MSG_WAITALL = 256 - - if sys.platform != "darwin": + if sys.platform == "win32": MSG_BCAST = 1024 MSG_MCAST = 2048 + + if sys.platform != "darwin": MSG_ERRQUEUE = 8192 if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG = ... MSG_CMSG_CLOEXEC = 1073741821 MSG_CONFIRM = 2048 - MSG_ETAG = ... MSG_FASTOPEN = 536870912 MSG_MORE = 32768 - MSG_NOTIFICATION = ... + + if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + MSG_NOTIFICATION = 8192 if sys.platform != "win32": MSG_DONTWAIT = 64 - MSG_EOF = 256 MSG_EOR = 128 MSG_NOSIGNAL = 16384 # sometimes this exists on darwin, sometimes not + if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF = 256 MSG_CTRUNC = MsgFlag.MSG_CTRUNC MSG_DONTROUTE = MsgFlag.MSG_DONTROUTE @@ -653,26 +1206,30 @@ MSG_PEEK = MsgFlag.MSG_PEEK MSG_TRUNC = MsgFlag.MSG_TRUNC MSG_WAITALL = MsgFlag.MSG_WAITALL -if sys.platform != "darwin": +if sys.platform == "win32": MSG_BCAST = MsgFlag.MSG_BCAST MSG_MCAST = MsgFlag.MSG_MCAST + +if sys.platform != "darwin": MSG_ERRQUEUE = MsgFlag.MSG_ERRQUEUE if sys.platform != "win32": MSG_DONTWAIT = MsgFlag.MSG_DONTWAIT - MSG_EOF = MsgFlag.MSG_EOF MSG_EOR = MsgFlag.MSG_EOR MSG_NOSIGNAL = MsgFlag.MSG_NOSIGNAL # Sometimes this exists on darwin, sometimes not if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG = MsgFlag.MSG_BTAG MSG_CMSG_CLOEXEC = MsgFlag.MSG_CMSG_CLOEXEC MSG_CONFIRM = MsgFlag.MSG_CONFIRM - MSG_ETAG = MsgFlag.MSG_ETAG MSG_FASTOPEN = MsgFlag.MSG_FASTOPEN MSG_MORE = MsgFlag.MSG_MORE + +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": MSG_NOTIFICATION = MsgFlag.MSG_NOTIFICATION +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF = MsgFlag.MSG_EOF + class AddressInfo(IntFlag): AI_ADDRCONFIG = 32 AI_ALL = 16 @@ -681,7 +1238,7 @@ class AddressInfo(IntFlag): AI_NUMERICSERV = 1024 AI_PASSIVE = 1 AI_V4MAPPED = 8 - if sys.platform != "win32": + if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT = 1536 AI_MASK = 5127 AI_V4MAPPED_CFG = 512 @@ -694,7 +1251,7 @@ AI_NUMERICSERV = AddressInfo.AI_NUMERICSERV AI_PASSIVE = AddressInfo.AI_PASSIVE AI_V4MAPPED = AddressInfo.AI_V4MAPPED -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT = AddressInfo.AI_DEFAULT AI_MASK = AddressInfo.AI_MASK AI_V4MAPPED_CFG = AddressInfo.AI_V4MAPPED_CFG @@ -718,7 +1275,7 @@ class socket(_socket.socket): ) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... - def dup(self) -> Self: ... # noqa: F811 + def dup(self) -> Self: ... def accept(self) -> tuple[socket, _RetAddress]: ... # Note that the makefile's documented windows-specific behavior is not represented # mode strings with duplicates are intentionally excluded diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index 931e8d03cb75..bc0ff6469d5e 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -63,7 +63,7 @@ from sqlite3.dbapi2 import ( version_info as version_info, ) from types import TracebackType -from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload +from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 12): @@ -446,7 +446,9 @@ class Row(Sequence[Any]): def __lt__(self, value: object, /) -> bool: ... def __ne__(self, value: object, /) -> bool: ... +# This class is not exposed. It calls itself sqlite3.Statement. @final +@type_check_only class _Statement: ... if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/sqlite3/dump.pyi b/mypy/typeshed/stdlib/sqlite3/dump.pyi new file mode 100644 index 000000000000..ed95fa46e1c7 --- /dev/null +++ b/mypy/typeshed/stdlib/sqlite3/dump.pyi @@ -0,0 +1,2 @@ +# This file is intentionally empty. The runtime module contains only +# private functions. diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 1d97c02acc5e..f587b51d5ac0 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -27,7 +27,7 @@ from _ssl import ( ) from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Callable, Iterable -from typing import Any, Literal, NamedTuple, TypedDict, overload +from typing import Any, Literal, NamedTuple, TypedDict, overload, type_check_only from typing_extensions import Never, Self, TypeAlias if sys.version_info >= (3, 13): @@ -309,6 +309,8 @@ ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: AlertDescription ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: AlertDescription ALERT_DESCRIPTION_USER_CANCELLED: AlertDescription +# This class is not exposed. It calls itself ssl._ASN1Object. +@type_check_only class _ASN1ObjectBase(NamedTuple): nid: int shortname: str diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index d65ddfe3825d..c4b1adca9bc6 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -5,14 +5,13 @@ from builtins import object as _object from collections.abc import AsyncGenerator, Callable, Sequence from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType -from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final +from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final, type_check_only from typing_extensions import TypeAlias _T = TypeVar("_T") # see https://github.com/python/typeshed/issues/8513#issue-1333671093 for the rationale behind this alias _ExitCode: TypeAlias = str | int | None -_OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall 2022 or later # ----- sys variables ----- if sys.platform != "win32": @@ -90,13 +89,63 @@ _UninstantiableStructseq: TypeAlias = structseq[Any] flags: _flags -if sys.version_info >= (3, 10): - _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int, int] -else: - _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int] - +# This class is not exposed at runtime. It calls itself sys.flags. +# As a tuple, it can have a length between 15 and 18. We don't model +# the exact length here because that varies by patch version due to +# the backported security fix int_max_str_digits. The exact length shouldn't +# be relied upon. See #13031 +# This can be re-visited when typeshed drops support for 3.10, +# at which point all supported versions will include int_max_str_digits +# in all patch versions. +# 3.8 and 3.9 are 15 or 16-tuple +# 3.10 is 16 or 17-tuple +# 3.11+ is an 18-tuple. @final -class _flags(_UninstantiableStructseq, _FlagTuple): +@type_check_only +class _flags(_UninstantiableStructseq, tuple[int, ...]): + # `safe_path` was added in py311 + if sys.version_info >= (3, 11): + __match_args__: Final = ( + "debug", + "inspect", + "interactive", + "optimize", + "dont_write_bytecode", + "no_user_site", + "no_site", + "ignore_environment", + "verbose", + "bytes_warning", + "quiet", + "hash_randomization", + "isolated", + "dev_mode", + "utf8_mode", + "warn_default_encoding", + "safe_path", + "int_max_str_digits", + ) + elif sys.version_info >= (3, 10): + __match_args__: Final = ( + "debug", + "inspect", + "interactive", + "optimize", + "dont_write_bytecode", + "no_user_site", + "no_site", + "ignore_environment", + "verbose", + "bytes_warning", + "quiet", + "hash_randomization", + "isolated", + "dev_mode", + "utf8_mode", + "warn_default_encoding", + "int_max_str_digits", + ) + @property def debug(self) -> int: ... @property @@ -129,15 +178,39 @@ class _flags(_UninstantiableStructseq, _FlagTuple): def utf8_mode(self) -> int: ... if sys.version_info >= (3, 10): @property - def warn_default_encoding(self) -> int: ... # undocumented + def warn_default_encoding(self) -> int: ... if sys.version_info >= (3, 11): @property def safe_path(self) -> bool: ... + # Whether or not this exists on lower versions of Python + # may depend on which patch release you're using + # (it was backported to all Python versions on 3.8+ as a security fix) + # Added in: 3.8.14, 3.9.14, 3.10.7 + # and present in all versions of 3.11 and later. + @property + def int_max_str_digits(self) -> int: ... float_info: _float_info +# This class is not exposed at runtime. It calls itself sys.float_info. @final +@type_check_only class _float_info(structseq[float], tuple[float, int, int, float, int, int, int, int, float, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ( + "max", + "max_exp", + "max_10_exp", + "min", + "min_exp", + "min_10_exp", + "dig", + "mant_dig", + "epsilon", + "radix", + "rounds", + ) + @property def max(self) -> float: ... # DBL_MAX @property @@ -163,8 +236,13 @@ class _float_info(structseq[float], tuple[float, int, int, float, int, int, int, hash_info: _hash_info +# This class is not exposed at runtime. It calls itself sys.hash_info. @final +@type_check_only class _hash_info(structseq[Any | int], tuple[int, int, int, int, int, str, int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("width", "modulus", "inf", "nan", "imag", "algorithm", "hash_bits", "seed_bits", "cutoff") + @property def width(self) -> int: ... @property @@ -186,6 +264,9 @@ class _hash_info(structseq[Any | int], tuple[int, int, int, int, int, str, int, implementation: _implementation +# This class isn't really a thing. At runtime, implementation is an instance +# of types.SimpleNamespace. This allows for better typing. +@type_check_only class _implementation: name: str version: _version_info @@ -198,8 +279,13 @@ class _implementation: int_info: _int_info +# This class is not exposed at runtime. It calls itself sys.int_info. @final +@type_check_only class _int_info(structseq[int], tuple[int, int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("bits_per_digit", "sizeof_digit", "default_max_str_digits", "str_digits_check_threshold") + @property def bits_per_digit(self) -> int: ... @property @@ -212,8 +298,13 @@ class _int_info(structseq[int], tuple[int, int, int, int]): _ThreadInfoName: TypeAlias = Literal["nt", "pthread", "pthread-stubs", "solaris"] _ThreadInfoLock: TypeAlias = Literal["semaphore", "mutex+cond"] | None +# This class is not exposed at runtime. It calls itself sys.thread_info. @final +@type_check_only class _thread_info(_UninstantiableStructseq, tuple[_ThreadInfoName, _ThreadInfoLock, str | None]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("name", "lock", "version") + @property def name(self) -> _ThreadInfoName: ... @property @@ -224,8 +315,13 @@ class _thread_info(_UninstantiableStructseq, tuple[_ThreadInfoName, _ThreadInfoL thread_info: _thread_info _ReleaseLevel: TypeAlias = Literal["alpha", "beta", "candidate", "final"] +# This class is not exposed at runtime. It calls itself sys.version_info. @final +@type_check_only class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("major", "minor", "micro", "releaselevel", "serial") + @property def major(self) -> int: ... @property @@ -318,6 +414,7 @@ if sys.version_info < (3, 9): def callstats() -> tuple[int, int, int, int, int, int, int, int, int, int, int] | None: ... # Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily. +@type_check_only class UnraisableHookArgs(Protocol): exc_type: type[BaseException] exc_value: BaseException | None @@ -333,8 +430,13 @@ def audit(event: str, /, *args: Any) -> None: ... _AsyncgenHook: TypeAlias = Callable[[AsyncGenerator[Any, Any]], None] | None +# This class is not exposed at runtime. It calls itself builtins.asyncgen_hooks. @final +@type_check_only class _asyncgen_hooks(structseq[_AsyncgenHook], tuple[_AsyncgenHook, _AsyncgenHook]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("firstiter", "finalizer") + @property def firstiter(self) -> _AsyncgenHook: ... @property diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index a5378e40fdf2..5a5a1f53be3c 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -10,58 +10,38 @@ _AttrReturn: TypeAlias = list[Any] if sys.platform != "win32": B0: int - B1000000: int B110: int B115200: int - B1152000: int B1200: int B134: int B150: int - B1500000: int B1800: int B19200: int B200: int - B2000000: int B230400: int B2400: int - B2500000: int B300: int - B3000000: int - B3500000: int B38400: int - B4000000: int - B460800: int B4800: int B50: int - B500000: int B57600: int - B576000: int B600: int B75: int - B921600: int B9600: int BRKINT: int BS0: int BS1: int BSDLY: int - CBAUD: int - CBAUDEX: int - CDEL: int CDSUSP: int CEOF: int CEOL: int - CEOL2: int CEOT: int CERASE: int - CESC: int CFLUSH: int - CIBAUD: int CINTR: int CKILL: int CLNEXT: int CLOCAL: int - CNUL: int - COMMON: int CQUIT: int CR0: int CR1: int @@ -80,7 +60,6 @@ if sys.platform != "win32": CSTOP: int CSTOPB: int CSUSP: int - CSWTCH: int CWERASE: int ECHO: int ECHOCTL: int @@ -101,7 +80,6 @@ if sys.platform != "win32": FIONREAD: int FLUSHO: int HUPCL: int - IBSHIFT: int ICANON: int ICRNL: int IEXTEN: int @@ -109,33 +87,21 @@ if sys.platform != "win32": IGNCR: int IGNPAR: int IMAXBEL: int - INIT_C_CC: int INLCR: int INPCK: int - IOCSIZE_MASK: int - IOCSIZE_SHIFT: int ISIG: int ISTRIP: int - IUCLC: int IXANY: int IXOFF: int IXON: int - N_MOUSE: int - N_PPP: int - N_SLIP: int - N_STRIP: int - N_TTY: int - NCC: int NCCS: int NL0: int NL1: int NLDLY: int NOFLSH: int - NSWTCH: int OCRNL: int OFDEL: int OFILL: int - OLCUC: int ONLCR: int ONLRET: int ONOCR: int @@ -149,9 +115,6 @@ if sys.platform != "win32": TAB2: int TAB3: int TABDLY: int - TCFLSH: int - TCGETA: int - TCGETS: int TCIFLUSH: int TCIOFF: int TCIOFLUSH: int @@ -162,28 +125,11 @@ if sys.platform != "win32": TCSADRAIN: int TCSAFLUSH: int TCSANOW: int - TCSASOFT: int - TCSBRK: int - TCSBRKP: int - TCSETA: int - TCSETAF: int - TCSETAW: int - TCSETS: int - TCSETSF: int - TCSETSW: int - TCXONC: int TIOCCONS: int TIOCEXCL: int TIOCGETD: int - TIOCGICOUNT: int - TIOCGLCKTRMIOS: int TIOCGPGRP: int - TIOCGSERIAL: int - TIOCGSIZE: int - TIOCGSOFTCAR: int TIOCGWINSZ: int - TIOCINQ: int - TIOCLINUX: int TIOCM_CAR: int TIOCM_CD: int TIOCM_CTS: int @@ -198,7 +144,6 @@ if sys.platform != "win32": TIOCMBIC: int TIOCMBIS: int TIOCMGET: int - TIOCMIWAIT: int TIOCMSET: int TIOCNOTTY: int TIOCNXCL: int @@ -212,23 +157,10 @@ if sys.platform != "win32": TIOCPKT_STOP: int TIOCPKT: int TIOCSCTTY: int - TIOCSER_TEMT: int - TIOCSERCONFIG: int - TIOCSERGETLSR: int - TIOCSERGETMULTI: int - TIOCSERGSTRUCT: int - TIOCSERGWILD: int - TIOCSERSETMULTI: int - TIOCSERSWILD: int TIOCSETD: int - TIOCSLCKTRMIOS: int TIOCSPGRP: int - TIOCSSERIAL: int - TIOCSSIZE: int - TIOCSSOFTCAR: int TIOCSTI: int TIOCSWINSZ: int - TIOCTTYGSTRUCT: int TOSTOP: int VDISCARD: int VEOF: int @@ -244,15 +176,119 @@ if sys.platform != "win32": VSTART: int VSTOP: int VSUSP: int - VSWTC: int - VSWTCH: int VT0: int VT1: int VTDLY: int VTIME: int VWERASE: int - XCASE: int - XTABS: int + + if sys.version_info >= (3, 13): + EXTPROC: int + IUTF8: int + + if sys.platform == "darwin" and sys.version_info >= (3, 13): + ALTWERASE: int + B14400: int + B28800: int + B7200: int + B76800: int + CCAR_OFLOW: int + CCTS_OFLOW: int + CDSR_OFLOW: int + CDTR_IFLOW: int + CIGNORE: int + CRTS_IFLOW: int + MDMBUF: int + NL2: int + NL3: int + NOKERNINFO: int + ONOEOT: int + OXTABS: int + VDSUSP: int + VSTATUS: int + + if sys.platform == "darwin" and sys.version_info >= (3, 11): + TIOCGSIZE: int + TIOCSSIZE: int + + if sys.platform == "linux": + B1152000: int + B576000: int + CBAUD: int + CBAUDEX: int + CIBAUD: int + IOCSIZE_MASK: int + IOCSIZE_SHIFT: int + IUCLC: int + N_MOUSE: int + N_PPP: int + N_SLIP: int + N_STRIP: int + N_TTY: int + NCC: int + OLCUC: int + TCFLSH: int + TCGETA: int + TCGETS: int + TCSBRK: int + TCSBRKP: int + TCSETA: int + TCSETAF: int + TCSETAW: int + TCSETS: int + TCSETSF: int + TCSETSW: int + TCXONC: int + TIOCGICOUNT: int + TIOCGLCKTRMIOS: int + TIOCGSERIAL: int + TIOCGSOFTCAR: int + TIOCINQ: int + TIOCLINUX: int + TIOCMIWAIT: int + TIOCTTYGSTRUCT: int + TIOCSER_TEMT: int + TIOCSERCONFIG: int + TIOCSERGETLSR: int + TIOCSERGETMULTI: int + TIOCSERGSTRUCT: int + TIOCSERGWILD: int + TIOCSERSETMULTI: int + TIOCSERSWILD: int + TIOCSLCKTRMIOS: int + TIOCSSERIAL: int + TIOCSSOFTCAR: int + VSWTC: int + VSWTCH: int + XCASE: int + XTABS: int + + if sys.platform != "darwin": + B1000000: int + B1500000: int + B2000000: int + B2500000: int + B3000000: int + B3500000: int + B4000000: int + B460800: int + B500000: int + B921600: int + + if sys.platform != "linux": + TCSASOFT: int + + if sys.platform != "darwin" and sys.platform != "linux": + # not available on FreeBSD either. + CDEL: int + CEOL2: int + CESC: int + CNUL: int + COMMON: int + CSWTCH: int + IBSHIFT: int + INIT_C_CC: int + NSWTCH: int def tcgetattr(fd: FileDescriptorLike, /) -> _AttrReturn: ... def tcsetattr(fd: FileDescriptorLike, when: int, attributes: _Attr, /) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 5d3550ea62ab..d6a234d67919 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,13 +1,18 @@ import _tkinter import sys -from _typeshed import Incomplete, MaybeNone, StrEnum, StrOrBytesPath +from _typeshed import Incomplete, MaybeNone, StrOrBytesPath from collections.abc import Callable, Iterable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, overload, type_check_only +from typing import Any, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from enum import Enum + if sys.version_info >= (3, 9): __all__ = [ "TclError", @@ -186,53 +191,99 @@ _XYScrollCommand: TypeAlias = str | Callable[[float, float], object] _TakeFocusValue: TypeAlias = bool | Literal[0, 1, ""] | Callable[[str], bool | None] # -takefocus in manual page named 'options' if sys.version_info >= (3, 11): - class _VersionInfoType(NamedTuple): + @type_check_only + class _VersionInfoTypeBase(NamedTuple): major: int minor: int micro: int releaselevel: str serial: int -class EventType(StrEnum): - Activate = "36" - ButtonPress = "4" - Button = ButtonPress - ButtonRelease = "5" - Circulate = "26" - CirculateRequest = "27" - ClientMessage = "33" - Colormap = "32" - Configure = "22" - ConfigureRequest = "23" - Create = "16" - Deactivate = "37" - Destroy = "17" - Enter = "7" - Expose = "12" - FocusIn = "9" - FocusOut = "10" - GraphicsExpose = "13" - Gravity = "24" - KeyPress = "2" - Key = "2" - KeyRelease = "3" - Keymap = "11" - Leave = "8" - Map = "19" - MapRequest = "20" - Mapping = "34" - Motion = "6" - MouseWheel = "38" - NoExpose = "14" - Property = "28" - Reparent = "21" - ResizeRequest = "25" - Selection = "31" - SelectionClear = "29" - SelectionRequest = "30" - Unmap = "18" - VirtualEvent = "35" - Visibility = "15" + class _VersionInfoType(_VersionInfoTypeBase): ... + +if sys.version_info >= (3, 11): + class EventType(StrEnum): + Activate = "36" + ButtonPress = "4" + Button = ButtonPress + ButtonRelease = "5" + Circulate = "26" + CirculateRequest = "27" + ClientMessage = "33" + Colormap = "32" + Configure = "22" + ConfigureRequest = "23" + Create = "16" + Deactivate = "37" + Destroy = "17" + Enter = "7" + Expose = "12" + FocusIn = "9" + FocusOut = "10" + GraphicsExpose = "13" + Gravity = "24" + KeyPress = "2" + Key = "2" + KeyRelease = "3" + Keymap = "11" + Leave = "8" + Map = "19" + MapRequest = "20" + Mapping = "34" + Motion = "6" + MouseWheel = "38" + NoExpose = "14" + Property = "28" + Reparent = "21" + ResizeRequest = "25" + Selection = "31" + SelectionClear = "29" + SelectionRequest = "30" + Unmap = "18" + VirtualEvent = "35" + Visibility = "15" + +else: + class EventType(str, Enum): + Activate = "36" + ButtonPress = "4" + Button = ButtonPress + ButtonRelease = "5" + Circulate = "26" + CirculateRequest = "27" + ClientMessage = "33" + Colormap = "32" + Configure = "22" + ConfigureRequest = "23" + Create = "16" + Deactivate = "37" + Destroy = "17" + Enter = "7" + Expose = "12" + FocusIn = "9" + FocusOut = "10" + GraphicsExpose = "13" + Gravity = "24" + KeyPress = "2" + Key = "2" + KeyRelease = "3" + Keymap = "11" + Leave = "8" + Map = "19" + MapRequest = "20" + Mapping = "34" + Motion = "6" + MouseWheel = "38" + NoExpose = "14" + Property = "28" + Reparent = "21" + ResizeRequest = "25" + Selection = "31" + SelectionClear = "29" + SelectionRequest = "30" + Unmap = "18" + VirtualEvent = "35" + Visibility = "15" _W = TypeVar("_W", bound=Misc) # Events considered covariant because you should never assign to event.widget. @@ -576,7 +627,8 @@ class Misc: def __getitem__(self, key: str) -> Any: ... def cget(self, key: str) -> Any: ... def configure(self, cnf: Any = None) -> Any: ... - # TODO: config is an alias of configure, but adding that here creates lots of mypy errors + # TODO: config is an alias of configure, but adding that here creates + # conflict with the type of config in the subclasses. See #13149 class CallWrapper: func: Incomplete @@ -3403,11 +3455,14 @@ class OptionMenu(Menubutton): # configure, config, cget are inherited from Menubutton # destroy and __getitem__ are overridden, signature does not change -# Marker to indicate that it is a valid bitmap/photo image. PIL implements compatible versions -# which don't share a class hierarchy. The actual API is a __str__() which returns a valid name, -# not something that type checkers can detect. +# This matches tkinter's image classes (PhotoImage and BitmapImage) +# and PIL's tkinter-compatible class (PIL.ImageTk.PhotoImage), +# but not a plain PIL image that isn't tkinter compatible. +# The reason is that PIL has width and height attributes, not methods. @type_check_only -class _Image: ... +class _Image(Protocol): + def width(self) -> int: ... + def height(self) -> int: ... @type_check_only class _BitmapImageLike(_Image): ... @@ -3426,9 +3481,7 @@ class Image(_Image): def __getitem__(self, key): ... configure: Incomplete config: Incomplete - def height(self) -> int: ... def type(self): ... - def width(self) -> int: ... class PhotoImage(Image, _PhotoImageLike): # This should be kept in sync with PIL.ImageTK.PhotoImage.__init__() diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 317f3068be63..097c2e4b4382 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -1,7 +1,8 @@ import _tkinter +import itertools import sys import tkinter -from typing import Any, Final, Literal, TypedDict, overload +from typing import Any, ClassVar, Final, Literal, TypedDict, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 9): @@ -40,6 +41,7 @@ class _MetricsDict(TypedDict): class Font: name: str delete_font: bool + counter: ClassVar[itertools.count[int]] # undocumented def __init__( self, # In tkinter, 'root' refers to tkinter.Tk by convention, but the code diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index e1c8fedee55c..7e9a945cdc46 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -4,7 +4,7 @@ from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from token import * from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES -from typing import Any, NamedTuple, TextIO +from typing import Any, NamedTuple, TextIO, type_check_only from typing_extensions import TypeAlias __all__ = [ @@ -98,6 +98,8 @@ blank_re: Pattern[bytes] _Position: TypeAlias = tuple[int, int] +# This class is not exposed. It calls itself tokenize.TokenInfo. +@type_check_only class _TokenInfo(NamedTuple): type: int string: str diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 29d289303927..a2ab728de943 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Sequence from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar -from typing import Any, ClassVar, overload +from typing import Any, ClassVar, Literal, TypedDict, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -141,8 +141,18 @@ if sys.version_info < (3, 13): _Color: TypeAlias = str | tuple[float, float, float] _AnyColor: TypeAlias = Any -# TODO: Replace this with a TypedDict once it becomes standardized. -_PenState: TypeAlias = dict[str, Any] +class _PenState(TypedDict): + shown: bool + pendown: bool + pencolor: _Color + fillcolor: _Color + pensize: int + speed: int + resizemode: Literal["auto", "user", "noresize"] + stretchfactor: tuple[float, float] + shearfactor: float + outline: int + tilt: float _Speed: TypeAlias = str | float _PolygonCoords: TypeAlias = Sequence[tuple[float, float]] @@ -283,6 +293,7 @@ class TNavigator: def heading(self) -> float: ... def setheading(self, to_angle: float) -> None: ... def circle(self, radius: float, extent: float | None = None, steps: int | None = None) -> None: ... + def speed(self, s: int | None = 0) -> int | None: ... fd = forward bk = back backward = back @@ -363,7 +374,7 @@ class TPen: st = showturtle ht = hideturtle -class RawTurtle(TPen, TNavigator): +class RawTurtle(TPen, TNavigator): # type: ignore[misc] # Conflicting methods in base classes screen: TurtleScreen screens: ClassVar[list[TurtleScreen]] def __init__( diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 27ae11daba70..8f0d4fbb6a02 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -133,6 +133,8 @@ if sys.version_info >= (3, 13): Any = object() +class _Final: ... + def final(f: _T) -> _T: ... @final class TypeVar: @@ -191,7 +193,7 @@ _promote = object() # N.B. Keep this definition in sync with typing_extensions._SpecialForm @final -class _SpecialForm: +class _SpecialForm(_Final): def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -211,8 +213,7 @@ Tuple: _SpecialForm Final: _SpecialForm Literal: _SpecialForm -# TypedDict is a (non-subscriptable) special form. -TypedDict: object +TypedDict: _SpecialForm if sys.version_info >= (3, 11): Self: _SpecialForm @@ -462,7 +463,11 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return @overload @abstractmethod def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> _YieldT_co: ... - def close(self) -> None: ... + if sys.version_info >= (3, 13): + def close(self) -> _ReturnT_co | None: ... + else: + def close(self) -> None: ... + def __iter__(self) -> Generator[_YieldT_co, _SendT_contra, _ReturnT_co]: ... @property def gi_code(self) -> CodeType: ... @@ -659,7 +664,6 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, item: object) -> bool: ... def __iter__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... - def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... def __or__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... @@ -673,7 +677,6 @@ class KeysView(MappingView, AbstractSet[_KT_co]): def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, key: object) -> bool: ... def __iter__(self) -> Iterator[_KT_co]: ... - def __reversed__(self) -> Iterator[_KT_co]: ... def __or__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[_KT_co]: ... @@ -685,7 +688,6 @@ class ValuesView(MappingView, Collection[_VT_co]): def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ... # undocumented def __contains__(self, value: object) -> bool: ... def __iter__(self) -> Iterator[_VT_co]: ... - def __reversed__(self) -> Iterator[_VT_co]: ... class Mapping(Collection[_KT], Generic[_KT, _VT_co]): # TODO: We wish the key type could also be covariant, but that doesn't work, @@ -973,7 +975,7 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] @final -class ForwardRef: +class ForwardRef(_Final): __forward_arg__: str __forward_code__: CodeType __forward_evaluated__: bool diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 3240eff0f5e9..a6b606e6b670 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -58,6 +58,7 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 TextIO as TextIO, Tuple as Tuple, Type as Type, + TypedDict as TypedDict, Union as Union, ValuesView as ValuesView, _Alias, @@ -190,8 +191,10 @@ _T = typing.TypeVar("_T") _F = typing.TypeVar("_F", bound=Callable[..., Any]) _TC = typing.TypeVar("_TC", bound=type[object]) +class _Final: ... # This should be imported from typing but that breaks pytype + # unfortunately we have to duplicate this class definition from typing.pyi or we break pytype -class _SpecialForm: +class _SpecialForm(_Final): def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -253,9 +256,6 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): # supposedly incompatible definitions of `__ior__` and `__or__`: def __ior__(self, value: Self, /) -> Self: ... # type: ignore[misc] -# TypedDict is a (non-subscriptable) special form. -TypedDict: object - OrderedDict = _Alias() def get_type_hints( @@ -409,8 +409,11 @@ else: def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... + # mypy and pyright object to this being both ABC and Protocol. + # At runtime it inherits from ABC and is not a Protocol, but it is on the + # allowlist for use as a Protocol. @runtime_checkable - class Buffer(Protocol): + class Buffer(Protocol, abc.ABC): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Not actually a Protocol at runtime; see # https://github.com/python/typeshed/issues/10224 for why we're defining it this way def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index d7bf033172f6..773786c24821 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -66,6 +66,7 @@ if sys.platform == "darwin": if sys.version_info < (3, 13): @deprecated("Deprecated in 3.11, to be removed in 3.13.") class MacOSX(BaseBrowser): + def __init__(self, name: str) -> None: ... def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... class MacOSXOSAScript(BaseBrowser): # In runtime this class does not have `name` and `basename` diff --git a/mypy/typeshed/stdlib/wsgiref/types.pyi b/mypy/typeshed/stdlib/wsgiref/types.pyi index 86212df8ccdc..57276fd05ea8 100644 --- a/mypy/typeshed/stdlib/wsgiref/types.pyi +++ b/mypy/typeshed/stdlib/wsgiref/types.pyi @@ -1,5 +1,5 @@ +from _typeshed import OptExcInfo from collections.abc import Callable, Iterable, Iterator -from sys import _OptExcInfo from typing import Any, Protocol from typing_extensions import TypeAlias @@ -7,7 +7,7 @@ __all__ = ["StartResponse", "WSGIEnvironment", "WSGIApplication", "InputStream", class StartResponse(Protocol): def __call__( - self, status: str, headers: list[tuple[str, str]], exc_info: _OptExcInfo | None = ..., / + self, status: str, headers: list[tuple[str, str]], exc_info: OptExcInfo | None = ..., / ) -> Callable[[bytes], object]: ... WSGIEnvironment: TypeAlias = dict[str, Any] diff --git a/mypy/typeshed/stdlib/xml/__init__.pyi b/mypy/typeshed/stdlib/xml/__init__.pyi index a487d2467f41..7a240965136e 100644 --- a/mypy/typeshed/stdlib/xml/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/__init__.pyi @@ -1 +1,3 @@ -from xml import parsers as parsers +# At runtime, listing submodules in __all__ without them being imported is +# valid, and causes them to be included in a star import. See #6523 +__all__ = ["dom", "parsers", "sax", "etree"] # noqa: F822 # pyright: ignore[reportUnsupportedDunderAll] diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi index 933acf2c4803..a7248ba7ab72 100644 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -12,6 +12,8 @@ _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] _ZF = TypeVar("_ZF", bound=ZipFile) if sys.version_info >= (3, 12): + __all__ = ["Path"] + class InitializedState: def __init__(self, *args: object, **kwargs: object) -> None: ... def __getstate__(self) -> tuple[list[object], dict[object, object]]: ... diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index 2f6c40656038..7cafb44b34a7 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadableBuffer -from typing import Final +from typing import Any, Final, final, type_check_only +from typing_extensions import Self DEFLATED: Final = 8 DEF_MEM_LEVEL: int # can change @@ -27,17 +28,30 @@ Z_TREES: Final = 6 class error(Exception): ... +# This class is not exposed at runtime. It calls itself zlib.Compress. +@final +@type_check_only class _Compress: - def compress(self, data: ReadableBuffer) -> bytes: ... - def flush(self, mode: int = ...) -> bytes: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self, mode: int = 4, /) -> bytes: ... def copy(self) -> _Compress: ... +# This class is not exposed at runtime. It calls itself zlib.Decompress. +@final +@type_check_only class _Decompress: - unused_data: bytes - unconsumed_tail: bytes - eof: bool - def decompress(self, data: ReadableBuffer, max_length: int = ...) -> bytes: ... - def flush(self, length: int = ...) -> bytes: ... + @property + def unused_data(self) -> bytes: ... + @property + def unconsumed_tail(self) -> bytes: ... + @property + def eof(self) -> bool: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def decompress(self, data: ReadableBuffer, /, max_length: int = 0) -> bytes: ... + def flush(self, length: int = 16384, /) -> bytes: ... def copy(self) -> _Decompress: ... def adler32(data: ReadableBuffer, value: int = 1, /) -> int: ... From 267a35dbe12c6b7d03e21b698c8efd158d160148 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 1 Dec 2024 17:14:22 -0500 Subject: [PATCH 0924/1617] Refactor: merge duplicate HasTypeVars query visitors (#18222) `mypy.checkexpr.HasTypeVarQuery` has only one usage and is virtually[^1] identical to `mypy.types.HasTypeVars`. Merging them seems sensible. The latter has wider usage via `has_type_vars`, so it seems like the better one to keep. Some history for the record: * `mypy.checkexpr.HasTypeVarQuery` was added in 12 years ago a40efb48f0a40a5a4a6038960909ddec98965beb * `mypy.types.HasTypeVars` was added 5 years ago in 59617e8f895ca12a6fb0ec46f378ce384a757118 [^1]: the only difference is that `HasTypeVars` sets `self.skip_alias_target = True`, but this seem to fit the usage of `HasTypeVarQuery` just as well. --- mypy/checkexpr.py | 19 ++----------------- mypy/types.py | 2 ++ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 247ff5edec13..549026ca89c2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -181,6 +181,7 @@ get_proper_type, get_proper_types, has_recursive_types, + has_type_vars, is_named_instance, split_with_prefix_and_suffix, ) @@ -6350,23 +6351,7 @@ def __init__(self) -> None: def visit_callable_type(self, t: CallableType) -> bool: # TODO: we need to check only for type variables of original callable. - return self.query_types(t.arg_types) or t.accept(HasTypeVarQuery()) - - -class HasTypeVarQuery(types.BoolTypeQuery): - """Visitor for querying whether a type has a type variable component.""" - - def __init__(self) -> None: - super().__init__(types.ANY_STRATEGY) - - def visit_type_var(self, t: TypeVarType) -> bool: - return True - - def visit_param_spec(self, t: ParamSpecType) -> bool: - return True - - def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: - return True + return self.query_types(t.arg_types) or has_type_vars(t) def has_erased_component(t: Type | None) -> bool: diff --git a/mypy/types.py b/mypy/types.py index 3a6933922902..e92ab0889991 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3612,6 +3612,8 @@ def visit_type_alias_type(self, typ: TypeAliasType) -> None: class HasTypeVars(BoolTypeQuery): + """Visitor for querying whether a type has a type variable component.""" + def __init__(self) -> None: super().__init__(ANY_STRATEGY) self.skip_alias_target = True From 3268a7ac3fd64eb3c0166a8833fe73394c7b78b9 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 1 Dec 2024 17:17:37 -0500 Subject: [PATCH 0925/1617] Disallow `TypeVar` constraints parameterized by type variables (#18186) Emit an error for [this test case](https://github.com/python/typing/blob/46b05a4c10ed3841c9bc5126ba9f31dd8ae061e7/conformance/tests/generics_basic.py#L54-L55) from the conformance tests: ```python class Test(Generic[T]): BadConstraint2 = TypeVar("BadConstraint2", str, list[T]) # E ``` --- mypy/message_registry.py | 4 ++++ mypy/semanal.py | 15 ++++++++++++--- test-data/unit/check-python312.test | 23 +++++++++++++++++++++++ test-data/unit/semanal-errors.test | 16 ++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index c0b422d4a35d..346a677a8e85 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -350,6 +350,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage: "Await expression cannot be used as a type variable bound", codes.SYNTAX ) +TYPE_VAR_GENERIC_CONSTRAINT_TYPE: Final = ErrorMessage( + "TypeVar constraint type cannot be parametrized by type variables", codes.MISC +) + TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage( "Yield expression cannot be used within a type alias", codes.SYNTAX ) diff --git a/mypy/semanal.py b/mypy/semanal.py index bad2dfbefae0..edcc50e66e30 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -290,6 +290,7 @@ UnpackType, get_proper_type, get_proper_types, + has_type_vars, is_named_instance, remove_dups, type_vars_as_args, @@ -1856,13 +1857,17 @@ def analyze_type_param( else: default = AnyType(TypeOfAny.from_omitted_generics) if type_param.kind == TYPE_VAR_KIND: - values = [] + values: list[Type] = [] if type_param.values: for value in type_param.values: analyzed = self.anal_type(value, allow_placeholder=True) if analyzed is None: analyzed = PlaceholderType(None, [], context.line) - values.append(analyzed) + if has_type_vars(analyzed): + self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, context) + values.append(AnyType(TypeOfAny.from_error)) + else: + values.append(analyzed) return TypeVarExpr( name=type_param.name, fullname=fullname, @@ -5044,7 +5049,11 @@ def analyze_value_types(self, items: list[Expression]) -> list[Type]: # soon, even if some value is not ready yet, see process_typevar_parameters() # for an example. analyzed = PlaceholderType(None, [], node.line) - result.append(analyzed) + if has_type_vars(analyzed): + self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, node) + result.append(AnyType(TypeOfAny.from_error)) + else: + result.append(analyzed) except TypeTranslationError: self.fail("Type expected", node) result.append(AnyType(TypeOfAny.from_error)) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index c5c8ada1aae1..8b4d638ecdaa 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1525,6 +1525,29 @@ reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] +[case testPEP695TypeAliasInvalidGenericConstraint] +class A[T]: + class a[S: (int, list[T])]: pass # E: Name "T" is not defined + type b[S: (int, list[T])] = S # E: TypeVar constraint type cannot be parametrized by type variables + def c[S: (int, list[T])](self) -> None: ... # E: TypeVar constraint type cannot be parametrized by type variables +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasUnboundTypeVarConstraint] +from typing import TypeVar +T = TypeVar("T") +class a[S: (int, list[T])]: pass # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +type b[S: (int, list[T])] = S # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +def c[S: (int, list[T])](self) -> None: ... # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + [case testPEP695RedefineAsTypeAlias1] class C: pass type C = int # E: Name "C" already defined on line 1 diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 33c8f9b80aa0..2f0a4c140915 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1060,6 +1060,22 @@ S = TypeVar('S', covariant=True, contravariant=True) \ # E: TypeVar cannot be both covariant and contravariant [builtins fixtures/bool.pyi] +[case testInvalidTypevarArgumentsGenericConstraint] +from typing import Generic, List, TypeVar +from typing_extensions import Self + +T = TypeVar("T") + +def f(x: T) -> None: + Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables +class C(Generic[T]): + Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables +class D: + Bad = TypeVar("Bad", int, List[Self]) # E: TypeVar constraint type cannot be parametrized by type variables +S = TypeVar("S", int, List[T]) # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) + [case testInvalidTypevarValues] from typing import TypeVar b = TypeVar('b', *[int]) # E: Unexpected argument to "TypeVar()" From 9dad4643fac0b15c9bcd6ee0f952b529921fff91 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:15:39 +0100 Subject: [PATCH 0926/1617] Do not include non-init fields in the synthesized `__replace__` method for dataclasses (#18221) At runtime, non init fields are not allowed when using replace. See [the source code](https://github.com/python/cpython/blob/1bc4f076d193ad157bdc69a1d62685a15f95113f/Lib/dataclasses.py#L1671-L1676) of the CPython implementation. --- mypy/plugins/dataclasses.py | 6 +++++- test-data/unit/check-dataclasses.test | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 81a5e70e6b3a..349eca7f0143 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -400,7 +400,11 @@ def transform(self) -> bool: def _add_dunder_replace(self, attributes: list[DataclassAttribute]) -> None: """Add a `__replace__` method to the class, which is used to replace attributes in the `copy` module.""" - args = [attr.to_argument(self._cls.info, of="replace") for attr in attributes] + args = [ + attr.to_argument(self._cls.info, of="replace") + for attr in attributes + if attr.is_in_init + ] type_vars = [tv for tv in self._cls.type_vars] add_method_to_class( self._api, diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 294612db7ea5..6de428109c72 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2492,12 +2492,14 @@ class Child(Base): [case testDunderReplacePresent] # flags: --python-version 3.13 -from dataclasses import dataclass +from dataclasses import dataclass, field @dataclass class Coords: x: int y: int + # non-init fields are not allowed with replace: + z: int = field(init=False) replaced = Coords(2, 4).__replace__(x=2, y=5) From e666217f6487ec7e6490981f7d6fdd5c7e57d81b Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 1 Dec 2024 19:10:26 -0500 Subject: [PATCH 0927/1617] Preserve block unreachablility when checking function definitions with constrained TypeVars (#18217) Fixes #18210 When checking function definitions with constrained type variables (i.e. type variables with value restrictions), mypy expands the function definition for each possible type variable value. However, blocks in the expanded function definitions have their `is_unreachable` reset to `False`, which leads to spurious errors in blocks that were marked as unreachable during semantic analysis. This PR preserves the value of `is_unreachable` on blocks in the expanded function definitions. --- mypy/nodes.py | 4 ++-- mypy/treetransform.py | 2 +- test-data/unit/check-unreachable-code.test | 10 ++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 56c61ce26d63..9e26103e2f58 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1263,7 +1263,7 @@ class Block(Statement): __match_args__ = ("body", "is_unreachable") - def __init__(self, body: list[Statement]) -> None: + def __init__(self, body: list[Statement], *, is_unreachable: bool = False) -> None: super().__init__() self.body = body # True if we can determine that this block is not executed during semantic @@ -1271,7 +1271,7 @@ def __init__(self, body: list[Statement]) -> None: # something like "if PY3:" when using Python 2. However, some code is # only considered unreachable during type checking and this is not true # in those cases. - self.is_unreachable = False + self.is_unreachable = is_unreachable def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_block(self) diff --git a/mypy/treetransform.py b/mypy/treetransform.py index bb34d8de2884..aafa4e95d530 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -276,7 +276,7 @@ def visit_nonlocal_decl(self, node: NonlocalDecl) -> NonlocalDecl: return NonlocalDecl(node.names.copy()) def visit_block(self, node: Block) -> Block: - return Block(self.statements(node.body)) + return Block(self.statements(node.body), is_unreachable=node.is_unreachable) def visit_decorator(self, node: Decorator) -> Decorator: # Note that a Decorator must be transformed to a Decorator. diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index cbad1bd5449e..e6818ab5c3c7 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1000,6 +1000,16 @@ class Test4(Generic[T3]): [builtins fixtures/isinstancelist.pyi] +[case testUnreachableBlockStaysUnreachableWithTypeVarConstraints] +# flags: --always-false COMPILE_TIME_FALSE +from typing import TypeVar +COMPILE_TIME_FALSE = False +T = TypeVar("T", int, str) +def foo(x: T) -> T: + if COMPILE_TIME_FALSE: + return "bad" + return x + [case testUnreachableFlagContextManagersNoSuppress] # flags: --warn-unreachable from contextlib import contextmanager From 1a9596453bf6377b8fee822cf0bf74350993ec28 Mon Sep 17 00:00:00 2001 From: MechanicalConstruct Date: Sun, 1 Dec 2024 19:11:48 -0500 Subject: [PATCH 0928/1617] Make join and meet symmetric with strict_optional (#18227) Fixes #18199 Updated `join.py` to return `AnyType(TypeOfAny.special_form)` for both `UnboundType` and `AnyType`. Updated `meet.py` to return `UninhabitedType()` when visiting a `UnboundType` as a `NoneType` when `state.strict_optional` is true. --- mypy/join.py | 2 +- mypy/meet.py | 2 +- mypy/test/testtypes.py | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index 865dd073d081..2ada7479789b 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -317,7 +317,7 @@ def visit_none_type(self, t: NoneType) -> ProperType: if state.strict_optional: if isinstance(self.s, (NoneType, UninhabitedType)): return t - elif isinstance(self.s, UnboundType): + elif isinstance(self.s, (UnboundType, AnyType)): return AnyType(TypeOfAny.special_form) else: return mypy.typeops.make_simplified_union([self.s, t]) diff --git a/mypy/meet.py b/mypy/meet.py index f51d354d8f2f..cbe3e99cdcd8 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -691,7 +691,7 @@ def __init__(self, s: ProperType) -> None: def visit_unbound_type(self, t: UnboundType) -> ProperType: if isinstance(self.s, NoneType): if state.strict_optional: - return AnyType(TypeOfAny.special_form) + return UninhabitedType() else: return self.s elif isinstance(self.s, UninhabitedType): diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index dbf7619f3a44..0380d1aa82d1 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -806,8 +806,7 @@ def test_any_type(self) -> None: self.fx.anyt, self.fx.a, self.fx.o, - # TODO: fix this is not currently symmetric - # NoneType(), + NoneType(), UnboundType("x"), self.fx.t, self.tuple(), @@ -1177,8 +1176,7 @@ def test_none(self) -> None: self.assert_meet(self.fx.o, NoneType(), NoneType()) for t in [ self.fx.a, - # TODO: fix this is not currently symmetric - # UnboundType("x"), + UnboundType("x"), self.fx.t, self.tuple(), self.callable(self.fx.a, self.fx.b), From 725145e3153b43fe8cf70d56bbdf4064e31e8960 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Mon, 2 Dec 2024 03:11:17 -0800 Subject: [PATCH 0929/1617] [mypyc] Fixing condition to fall back to PyCall for staticmethod and classmethod (#18228) Fixes mypyc/mypyc#1076 This change has been copied from [mypyc/irbuild/builder.py#1084](https://github.com/advait-dixit/mypy/blob/1a9596453bf6377b8fee822cf0bf74350993ec28/mypyc/irbuild/builder.py#L1084). --- mypyc/irbuild/expression.py | 2 ++ mypyc/test-data/run-classes.test | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index a2a3203d12ca..7a43a3ba9f5f 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -10,6 +10,7 @@ from typing import Callable, Sequence from mypy.nodes import ( + ARG_NAMED, ARG_POS, LDEF, AssertTypeExpr, @@ -355,6 +356,7 @@ def translate_method_call(builder: IRBuilder, expr: CallExpr, callee: MemberExpr and isinstance(callee.expr.node, TypeInfo) and callee.expr.node in builder.mapper.type_to_ir and builder.mapper.type_to_ir[callee.expr.node].has_method(callee.name) + and all(kind in (ARG_POS, ARG_NAMED) for kind in expr.arg_kinds) ): # Call a method via the *class* assert isinstance(callee.expr.node, TypeInfo) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index d76974f7d83e..cf30bddbef64 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2631,3 +2631,27 @@ print(f'{Player.MIN = }') from native import Player [out] Player.MIN = + +[case testStaticCallsWithUnpackingArgs] +from typing import Tuple + +class Foo: + @staticmethod + def static(a: int, b: int, c: int) -> Tuple[int, int, int]: + return (c+1, a+2, b+3) + + @classmethod + def clsmethod(cls, a: int, b: int, c: int) -> Tuple[int, int, int]: + return (c+1, a+2, b+3) + + +print(Foo.static(*[10, 20, 30])) +print(Foo.static(*(40, 50), *[60])) +assert Foo.static(70, 80, *[90]) == Foo.clsmethod(70, *(80, 90)) + +[file driver.py] +import native + +[out] +(31, 12, 23) +(61, 42, 53) From 54ff364f4a7e802bca936136d0848116c5e3627e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Dec 2024 12:11:58 +0000 Subject: [PATCH 0930/1617] [mypyc] Refactor: use new-style primitives for unary and method ops (#18230) Also fix an issue with redundant coercions for some primitive ops. Add a few tests. --- mypyc/irbuild/builder.py | 8 ++++---- mypyc/irbuild/classdef.py | 8 ++++---- mypyc/irbuild/expression.py | 13 +++++++------ mypyc/irbuild/for_helpers.py | 4 ++-- mypyc/irbuild/function.py | 8 ++++---- mypyc/irbuild/ll_builder.py | 14 +++++++------- mypyc/primitives/registry.py | 8 ++++---- mypyc/test/test_cheader.py | 9 +-------- mypyc/test/test_emitfunc.py | 12 ++++++------ 9 files changed, 39 insertions(+), 45 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 7561070b012f..a0837ba2bfc7 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -404,7 +404,7 @@ def add_to_non_ext_dict( ) -> None: # Add an attribute entry into the class dict of a non-extension class. key_unicode = self.load_str(key) - self.call_c(dict_set_item_op, [non_ext.dict, key_unicode, val], line) + self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line) def gen_import(self, id: str, line: int) -> None: self.imports[id] = None @@ -435,7 +435,7 @@ def get_module(self, module: str, line: int) -> Value: # Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :( mod_dict = self.call_c(get_module_dict_op, [], line) # Get module object from modules dict. - return self.call_c(dict_get_item_op, [mod_dict, self.load_str(module)], line) + return self.primitive_op(dict_get_item_op, [mod_dict, self.load_str(module)], line) def get_module_attr(self, module: str, attr: str, line: int) -> Value: """Look up an attribute of a module without storing it in the local namespace. @@ -817,7 +817,7 @@ def process_iterator_tuple_assignment( self.activate_block(ok_block) for litem in reversed(post_star_vals): - ritem = self.call_c(list_pop_last, [iter_list], line) + ritem = self.primitive_op(list_pop_last, [iter_list], line) self.assign(litem, ritem, line) # Assign the starred value @@ -1302,7 +1302,7 @@ def load_global(self, expr: NameExpr) -> Value: def load_global_str(self, name: str, line: int) -> Value: _globals = self.load_globals_dict() reg = self.load_str(name) - return self.call_c(dict_get_item_op, [_globals, reg], line) + return self.primitive_op(dict_get_item_op, [_globals, reg], line) def load_globals_dict(self) -> Value: return self.add(LoadStatic(dict_rprimitive, "globals", self.module_name)) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 58be87c88a3f..6072efa2c593 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -256,7 +256,7 @@ def finalize(self, ir: ClassIR) -> None: ) # Add the non-extension class to the dict - self.builder.call_c( + self.builder.primitive_op( dict_set_item_op, [ self.builder.load_globals_dict(), @@ -466,7 +466,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(cdef.name), tp], cdef.line ) @@ -493,7 +493,7 @@ def make_generic_base_class( else: arg = builder.new_tuple(args, line) - base = builder.call_c(py_get_item_op, [gent, arg], line) + base = builder.primitive_op(py_get_item_op, [gent, arg], line) return base @@ -661,7 +661,7 @@ def add_non_ext_class_attr_ann( typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) key = builder.load_str(lvalue.name) - builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) + builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) def add_non_ext_class_attr( diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 7a43a3ba9f5f..97cd31af93af 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -60,6 +60,7 @@ Integer, LoadAddress, LoadLiteral, + PrimitiveDescription, RaiseStandardError, Register, TupleGet, @@ -99,7 +100,7 @@ from mypyc.primitives.generic_ops import iter_op from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op -from mypyc.primitives.registry import CFunctionDescription, builtin_names +from mypyc.primitives.registry import builtin_names from mypyc.primitives.set_ops import set_add_op, set_in_op, set_update_op from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op @@ -182,7 +183,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: # AST doesn't include a Var node for the module. We # instead load the module separately on each access. mod_dict = builder.call_c(get_module_dict_op, [], expr.line) - obj = builder.call_c( + obj = builder.primitive_op( dict_get_item_op, [mod_dict, builder.load_str(expr.node.fullname)], expr.line ) return obj @@ -979,8 +980,8 @@ def _visit_display( builder: IRBuilder, items: list[Expression], constructor_op: Callable[[list[Value], int], Value], - append_op: CFunctionDescription, - extend_op: CFunctionDescription, + append_op: PrimitiveDescription, + extend_op: PrimitiveDescription, line: int, is_list: bool, ) -> Value: @@ -1001,7 +1002,7 @@ def _visit_display( if result is None: result = constructor_op(initial_items, line) - builder.call_c(extend_op if starred else append_op, [result, value], line) + builder.primitive_op(extend_op if starred else append_op, [result, value], line) if result is None: result = constructor_op(initial_items, line) @@ -1030,7 +1031,7 @@ def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehe def gen_inner_stmts() -> None: k = builder.accept(o.key) v = builder.accept(o.value) - builder.call_c(dict_set_item_op, [builder.read(d), k, v], o.line) + builder.primitive_op(dict_set_item_op, [builder.read(d), k, v], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return builder.read(d) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index b37c39e70f11..9b34a094db60 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -251,7 +251,7 @@ def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Valu def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(list_append_op, [builder.read(list_ops), e], gen.line) + builder.primitive_op(list_append_op, [builder.read(list_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(list_ops) @@ -286,7 +286,7 @@ def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(set_add_op, [builder.read(set_ops), e], gen.line) + builder.primitive_op(set_add_op, [builder.read(set_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(set_ops) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 4d04c549d392..a84db5a08863 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -133,7 +133,7 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: if decorated_func is not None: # Set the callable object representing the decorated function as a global. - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func], decorated_func.line, @@ -849,7 +849,7 @@ def generate_singledispatch_dispatch_function( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) call_find_impl, use_cache, call_func = BasicBlock(), BasicBlock(), BasicBlock() - get_result = builder.call_c(dict_get_method_with_none, [dispatch_cache, arg_type], line) + get_result = builder.primitive_op(dict_get_method_with_none, [dispatch_cache, arg_type], line) is_not_none = builder.translate_is_op(get_result, builder.none_object(), "is not", line) impl_to_use = Register(object_rprimitive) builder.add_bool_branch(is_not_none, use_cache, call_find_impl) @@ -862,7 +862,7 @@ def generate_singledispatch_dispatch_function( find_impl = builder.load_module_attr_by_fullname("functools._find_impl", line) registry = load_singledispatch_registry(builder, dispatch_func_obj, line) uncached_impl = builder.py_call(find_impl, [arg_type, registry], line) - builder.call_c(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) + builder.primitive_op(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) builder.assign(impl_to_use, uncached_impl, line) builder.goto(call_func) @@ -1039,7 +1039,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: registry = load_singledispatch_registry(builder, dispatch_func_obj, line) for typ in types: loaded_type = load_type(builder, typ, line) - builder.call_c(dict_set_item_op, [registry, loaded_type, to_insert], line) + builder.primitive_op(dict_set_item_op, [registry, loaded_type, to_insert], line) dispatch_cache = builder.builder.get_attr( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a5a524b25af0..556d753b89f8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -762,7 +762,7 @@ def _construct_varargs( if kind == ARG_STAR: if star_result is None: star_result = self.new_list_op(star_values, line) - self.call_c(list_extend_op, [star_result, value], line) + self.primitive_op(list_extend_op, [star_result, value], line) elif kind == ARG_STAR2: if star2_result is None: star2_result = self._create_dict(star2_keys, star2_values, line) @@ -1939,7 +1939,7 @@ def primitive_op( desc.priority, is_pure=desc.is_pure, ) - return self.call_c(c_desc, args, line, result_type) + return self.call_c(c_desc, args, line, result_type=result_type) # This primitive gets transformed in a lowering pass to # lower-level IR ops using a custom transform function. @@ -2005,7 +2005,7 @@ def matching_primitive_op( else: matching = desc if matching: - return self.primitive_op(matching, args, line=line) + return self.primitive_op(matching, args, line=line, result_type=result_type) return None def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> Value: @@ -2346,11 +2346,11 @@ def translate_special_method_call( Return None if no translation found; otherwise return the target register. """ - call_c_ops_candidates = method_call_ops.get(name, []) - call_c_op = self.matching_call_c( - call_c_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow + primitive_ops_candidates = method_call_ops.get(name, []) + primitive_op = self.matching_primitive_op( + primitive_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow ) - return call_c_op + return primitive_op def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value | None: """Add a equality comparison operation. diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 26faabc0c1e2..5e7ecb70f55d 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -70,8 +70,8 @@ class LoadAddressDescription(NamedTuple): src: str # name of the target to load -# CallC op for method call (such as 'str.join') -method_call_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for method call (such as 'str.join') +method_call_ops: dict[str, list[PrimitiveDescription]] = {} # Primitive ops for top level function call (such as 'builtins.list') function_ops: dict[str, list[PrimitiveDescription]] = {} @@ -99,7 +99,7 @@ def method_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: +) -> PrimitiveDescription: """Define a c function call op that replaces a method call. This will be automatically generated by matching against the AST. @@ -129,7 +129,7 @@ def method_op( if extra_int_constants is None: extra_int_constants = [] ops = method_call_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, arg_types, return_type, diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index 9c09f14e93dd..7ab055c735ad 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -9,7 +9,6 @@ from mypyc.ir.ops import PrimitiveDescription from mypyc.primitives import registry -from mypyc.primitives.registry import CFunctionDescription class TestHeaderInclusion(unittest.TestCase): @@ -26,14 +25,8 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [registry.method_call_ops.values()]: - for old_ops in old_values: - if isinstance(old_ops, CFunctionDescription): - old_ops = [old_ops] - for old_op in old_ops: - check_name(old_op.c_function_name) - for values in [ + registry.method_call_ops.values(), registry.binary_ops.values(), registry.unary_ops.values(), registry.function_ops.values(), diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index b97ad78d6970..90df131288f9 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -303,7 +303,7 @@ def test_dec_ref_tuple_nested(self) -> None: def test_list_get_item(self) -> None: self.assert_emit( CallC( - list_get_item_op.c_function_name, + str(list_get_item_op.c_function_name), [self.m, self.k], list_get_item_op.return_type, list_get_item_op.steals, @@ -317,7 +317,7 @@ def test_list_get_item(self) -> None: def test_list_set_item(self) -> None: self.assert_emit( CallC( - list_set_item_op.c_function_name, + str(list_set_item_op.c_function_name), [self.l, self.n, self.o], list_set_item_op.return_type, list_set_item_op.steals, @@ -353,7 +353,7 @@ def test_unbox_i64(self) -> None: def test_list_append(self) -> None: self.assert_emit( CallC( - list_append_op.c_function_name, + str(list_append_op.c_function_name), [self.l, self.o], list_append_op.return_type, list_append_op.steals, @@ -493,7 +493,7 @@ def test_set_attr_init_with_bitmap(self) -> None: def test_dict_get_item(self) -> None: self.assert_emit( CallC( - dict_get_item_op.c_function_name, + str(dict_get_item_op.c_function_name), [self.d, self.o2], dict_get_item_op.return_type, dict_get_item_op.steals, @@ -507,7 +507,7 @@ def test_dict_get_item(self) -> None: def test_dict_set_item(self) -> None: self.assert_emit( CallC( - dict_set_item_op.c_function_name, + str(dict_set_item_op.c_function_name), [self.d, self.o, self.o2], dict_set_item_op.return_type, dict_set_item_op.steals, @@ -521,7 +521,7 @@ def test_dict_set_item(self) -> None: def test_dict_update(self) -> None: self.assert_emit( CallC( - dict_update_op.c_function_name, + str(dict_update_op.c_function_name), [self.d, self.o], dict_update_op.return_type, dict_update_op.steals, From 222b104a6bee2ccb92d8023777191218095c6203 Mon Sep 17 00:00:00 2001 From: MechanicalConstruct Date: Mon, 2 Dec 2024 18:39:53 -0500 Subject: [PATCH 0931/1617] Show Protocol __call__ for arguments with incompatible types (#18214) Fixes issue #17840 Check if callee_type.is_protocol if so calls find_member like before on the callee_type and then calls self.note_call() to report the note. --- mypy/messages.py | 5 ++++- test-data/unit/check-inference.test | 12 ++++++++---- test-data/unit/check-protocols.test | 24 ++++++++++++++++-------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 3ec1574827eb..6b0760cd79c6 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -870,7 +870,10 @@ def incompatible_argument_note( ) if call: self.note_call(original_caller_type, call, context, code=code) - + if isinstance(callee_type, Instance) and callee_type.type.is_protocol: + call = find_member("__call__", callee_type, callee_type, is_operator=True) + if call: + self.note_call(callee_type, call, context, code=code) self.maybe_note_concatenate_pos_args(original_caller_type, callee_type, context, code) def maybe_note_concatenate_pos_args( diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index b0ec590b54f9..5a99e65c9c90 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3677,7 +3677,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(__x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsPosOnly] @@ -3693,7 +3694,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(*, x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallablePosOnlyVsKwargs] @@ -3709,7 +3711,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(**x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsArgs] @@ -3725,7 +3728,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(*args: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]" [builtins fixtures/list.pyi] [case testInferenceAgainstTypeVarActualBound] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 0367be3dde65..dd19eb1f21d6 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2473,7 +2473,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int, VarArg(str)], None]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int, VarArg(str)], None]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(str, 'x'), VarArg(int)], None]" [builtins fixtures/tuple.pyi] [out] @@ -2510,7 +2511,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int], int]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int], int]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(T, 'x')], T]" [builtins fixtures/tuple.pyi] [out] @@ -2530,7 +2532,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(int, 'x')], int]" [builtins fixtures/tuple.pyi] [out] @@ -2557,7 +2560,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[Union[int, str]], Union[int, str]]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[Union[int, str]], Union[int, str]]"; expected "Caller" \ + # N: "Caller.__call__" has type overloaded function [out] [case testCallableImplementsProtocolExtraNote] @@ -2596,7 +2600,8 @@ def anon(caller: CallerAnon) -> None: func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[str], None]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[str], None]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(str, 'x')], None]" anon(bad) [out] @@ -2619,7 +2624,8 @@ a: Other b: Bad func(a) -func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" +func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" \ + # N: "One.__call__" has type "Callable[[Arg(str, 'x')], None]" [out] [case testJoinProtocolCallback] @@ -3589,7 +3595,8 @@ test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" # N: Expected: \ # N: def __call__(x: int, y: int) -> Any \ # N: Got: \ - # N: def __init__(x: int, y: str) -> C + # N: def __init__(x: int, y: str) -> C \ + # N: "P.__call__" has type "Callable[[Arg(int, 'x'), Arg(int, 'y')], Any]" [case testProtocolClassObjectPureCallback] from typing import Any, ClassVar, Protocol @@ -3610,7 +3617,8 @@ test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" # N: Expected: \ # N: def __call__(x: int, y: int) -> Any \ # N: Got: \ - # N: def __init__(x: int, y: str) -> C + # N: def __init__(x: int, y: str) -> C \ + # N: "P.__call__" has type "Callable[[Arg(int, 'x'), Arg(int, 'y')], Any]" [builtins fixtures/type.pyi] [case testProtocolClassObjectCallableError] From e731185489cd18b31ec23354eee47eb8b949d1b1 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:28:59 +0000 Subject: [PATCH 0932/1617] [mypyc] Optimize str.encode with specializations for common used encodings (#18232) Tested with: ``` import time start = time.time() for i in range(20000000): "test".encode('utf-8') print(time.time() - start) ``` With PR applied and running mypyc, `python3 -c "import test"` runs in: 0.5383486747741699 0.5224344730377197 0.555696964263916 Without PR applied: 0.7315819263458252 0.7105758190155029 0.7471706867218018 Similar times observed for "ascii" --- mypyc/irbuild/specialize.py | 57 +++++++++++++++++++ mypyc/primitives/str_ops.py | 24 ++++++++ mypyc/test-data/fixtures/ir.py | 2 +- mypyc/test-data/irbuild-str.test | 95 ++++++++++++++++++++++++++++---- 4 files changed, 166 insertions(+), 12 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 7c5958457886..cb69852af1ce 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -89,6 +89,11 @@ dict_values_op, ) from mypyc.primitives.list_ops import new_list_set_item_op +from mypyc.primitives.str_ops import ( + str_encode_ascii_strict, + str_encode_latin1_strict, + str_encode_utf8_strict, +) from mypyc.primitives.tuple_ops import new_tuple_set_item_op # Specializers are attempted before compiling the arguments to the @@ -682,6 +687,58 @@ def translate_fstring(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va return None +@specialize_function("encode", str_rprimitive) +def str_encode_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + """Specialize common cases of str.encode for most used encodings and strict errors.""" + + if not isinstance(callee, MemberExpr): + return None + + # We can only specialize if we have string literals as args + if len(expr.arg_kinds) > 0 and not isinstance(expr.args[0], StrExpr): + return None + if len(expr.arg_kinds) > 1 and not isinstance(expr.args[1], StrExpr): + return None + + encoding = "utf8" + errors = "strict" + if len(expr.arg_kinds) > 0 and isinstance(expr.args[0], StrExpr): + if expr.arg_kinds[0] == ARG_NAMED: + if expr.arg_names[0] == "encoding": + encoding = expr.args[0].value + elif expr.arg_names[0] == "errors": + errors = expr.args[0].value + elif expr.arg_kinds[0] == ARG_POS: + encoding = expr.args[0].value + else: + return None + if len(expr.arg_kinds) > 1 and isinstance(expr.args[1], StrExpr): + if expr.arg_kinds[1] == ARG_NAMED: + if expr.arg_names[1] == "encoding": + encoding = expr.args[1].value + elif expr.arg_names[1] == "errors": + errors = expr.args[1].value + elif expr.arg_kinds[1] == ARG_POS: + errors = expr.args[1].value + else: + return None + + if errors != "strict": + # We can only specialize strict errors + return None + + encoding = encoding.lower().replace("-", "").replace("_", "") # normalize + # Specialized encodings and their accepted aliases + if encoding in ["u8", "utf", "utf8", "cp65001"]: + return builder.call_c(str_encode_utf8_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in ["646", "ascii", "usascii"]: + return builder.call_c(str_encode_ascii_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in ["iso88591", "8859", "cp819", "latin", "latin1", "l1"]: + return builder.call_c(str_encode_latin1_strict, [builder.accept(callee.expr)], expr.line) + + return None + + @specialize_function("mypy_extensions.i64") def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 2ff1fbdb4b3e..3a5495e21c1b 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -219,6 +219,30 @@ extra_int_constants=[(0, pointer_rprimitive)], ) +# str.encode(encoding) - utf8 strict specialization +str_encode_utf8_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsUTF8String", + error_kind=ERR_MAGIC, +) + +# str.encode(encoding) - ascii strict specialization +str_encode_ascii_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsASCIIString", + error_kind=ERR_MAGIC, +) + +# str.encode(encoding) - latin1 strict specialization +str_encode_latin1_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsLatin1String", + error_kind=ERR_MAGIC, +) + # str.encode(encoding, errors) method_op( name="encode", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index ac95ffe2c047..be66307286fc 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -110,7 +110,7 @@ def upper(self) -> str: ... def startswith(self, x: str, start: int=..., end: int=...) -> bool: ... def endswith(self, x: str, start: int=..., end: int=...) -> bool: ... def replace(self, old: str, new: str, maxcount: int=...) -> str: ... - def encode(self, x: str=..., y: str=...) -> bytes: ... + def encode(self, encoding: str=..., errors: str=...) -> bytes: ... class float: def __init__(self, x: object) -> None: pass diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 771dcc4c0e68..61e5a42cf3ef 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -293,20 +293,93 @@ L0: def f(s: str) -> None: s.encode() s.encode('utf-8') + s.encode('utf8', 'strict') + s.encode('latin1', errors='strict') + s.encode(encoding='ascii') + s.encode(errors='strict', encoding='latin-1') + s.encode('utf-8', 'backslashreplace') s.encode('ascii', 'backslashreplace') + encoding = 'utf8' + s.encode(encoding) + errors = 'strict' + s.encode('utf8', errors) + s.encode('utf8', errors=errors) + s.encode(errors=errors) + s.encode(encoding=encoding, errors=errors) + s.encode('latin2') + [out] def f(s): s :: str - r0 :: bytes - r1 :: str - r2 :: bytes - r3, r4 :: str - r5 :: bytes + r0, r1, r2, r3, r4, r5 :: bytes + r6, r7 :: str + r8 :: bytes + r9, r10 :: str + r11 :: bytes + r12, encoding :: str + r13 :: bytes + r14, errors, r15 :: str + r16 :: bytes + r17, r18 :: str + r19 :: object + r20 :: str + r21 :: tuple + r22 :: dict + r23 :: object + r24 :: str + r25 :: object + r26 :: str + r27 :: tuple + r28 :: dict + r29 :: object + r30 :: str + r31 :: object + r32, r33 :: str + r34 :: tuple + r35 :: dict + r36 :: object + r37 :: str + r38 :: bytes L0: - r0 = CPy_Encode(s, 0, 0) - r1 = 'utf-8' - r2 = CPy_Encode(s, r1, 0) - r3 = 'ascii' - r4 = 'backslashreplace' - r5 = CPy_Encode(s, r3, r4) + r0 = PyUnicode_AsUTF8String(s) + r1 = PyUnicode_AsUTF8String(s) + r2 = PyUnicode_AsUTF8String(s) + r3 = PyUnicode_AsLatin1String(s) + r4 = PyUnicode_AsASCIIString(s) + r5 = PyUnicode_AsLatin1String(s) + r6 = 'utf-8' + r7 = 'backslashreplace' + r8 = CPy_Encode(s, r6, r7) + r9 = 'ascii' + r10 = 'backslashreplace' + r11 = CPy_Encode(s, r9, r10) + r12 = 'utf8' + encoding = r12 + r13 = CPy_Encode(s, encoding, 0) + r14 = 'strict' + errors = r14 + r15 = 'utf8' + r16 = CPy_Encode(s, r15, errors) + r17 = 'utf8' + r18 = 'encode' + r19 = CPyObject_GetAttr(s, r18) + r20 = 'errors' + r21 = PyTuple_Pack(1, r17) + r22 = CPyDict_Build(1, r20, errors) + r23 = PyObject_Call(r19, r21, r22) + r24 = 'encode' + r25 = CPyObject_GetAttr(s, r24) + r26 = 'errors' + r27 = PyTuple_Pack(0) + r28 = CPyDict_Build(1, r26, errors) + r29 = PyObject_Call(r25, r27, r28) + r30 = 'encode' + r31 = CPyObject_GetAttr(s, r30) + r32 = 'encoding' + r33 = 'errors' + r34 = PyTuple_Pack(0) + r35 = CPyDict_Build(2, r32, encoding, r33, errors) + r36 = PyObject_Call(r31, r34, r35) + r37 = 'latin2' + r38 = CPy_Encode(s, r37, 0) return 1 From 411e1f168e197dd7ca08c820ae04b3160f41071b Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 3 Dec 2024 18:43:18 +0100 Subject: [PATCH 0933/1617] Fixed typo in extending mypy docs. (#18234) --- docs/source/extending_mypy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index bded07319b64..0df45ea22d33 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -245,4 +245,4 @@ Mypy ships ``mypy.plugins.proper_plugin`` plugin which can be useful for plugin authors, since it finds missing ``get_proper_type()`` calls, which is a pretty common mistake. -It is recommended to enable it is a part of your plugin's CI. +It is recommended to enable it as a part of your plugin's CI. From 242873a2e8a1d98762b30fcf7b28a699a230279d Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Wed, 4 Dec 2024 08:00:03 +0100 Subject: [PATCH 0934/1617] Implement flag to allow typechecking of untyped modules (#17712) Add a flag and config ini options `"follow_untyped_imports"`. Setting it to `True` instructs mypy to typecheck also modules that do not have stubs or a `py.typed` marker. Fixes #8545 --- docs/source/command_line.rst | 4 ++++ docs/source/config_file.rst | 12 ++++++++++++ docs/source/running_mypy.rst | 6 ++++++ mypy/main.py | 5 +++++ mypy/modulefinder.py | 9 +++++++-- mypy/options.py | 3 +++ test-data/unit/pep561.test | 7 +++++++ 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 92db5d59d0ee..e65317331d55 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -166,6 +166,10 @@ imports. For more details, see :ref:`ignore-missing-imports`. +.. option:: --follow-untyped-imports + + This flag makes mypy analyze imports without stubs or a py.typed marker. + .. option:: --follow-imports {normal,silent,skip,error} This flag adjusts how mypy follows imported modules that were not diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 310d0c3dbcb1..e970c23a9ecb 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -315,6 +315,18 @@ section of the command line docs. match the name of the *imported* module, not the module containing the import statement. +.. confval:: follow_untyped_imports + + :type: boolean + :default: False + + Typechecks imports from modules that do not have stubs or a py.typed marker. + + If this option is used in a per-module section, the module name should + match the name of the *imported* module, not the module containing the + import statement. Note that scanning all unannotated modules might + significantly increase the runtime of your mypy calls. + .. confval:: follow_imports :type: string diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index a8ebc61d4774..91fe525c46e0 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -321,6 +321,12 @@ not catch errors in its use. recommend avoiding ``--ignore-missing-imports`` if possible: it's equivalent to adding a ``# type: ignore`` to all unresolved imports in your codebase. +4. To make mypy typecheck imports from modules without stubs or a py.typed + marker, you can set the :option:`--follow-untyped-imports ` + command line flag or set the :confval:`follow_untyped_imports` config file option to True, + either in the global section of your mypy config file, or individually on a + per-module basis. + Library stubs not installed --------------------------- diff --git a/mypy/main.py b/mypy/main.py index ae3e7d75be7f..fb4a1f61a01d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -580,6 +580,11 @@ def add_invertible_flag( action="store_true", help="Silently ignore imports of missing modules", ) + imports_group.add_argument( + "--follow-untyped-imports", + action="store_true", + help="Typecheck modules without stubs or py.typed marker", + ) imports_group.add_argument( "--follow-imports", choices=["normal", "silent", "skip", "error"], diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index bc11c1304776..fdd89837002f 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -334,10 +334,11 @@ def _typeshed_has_version(self, module: str) -> bool: return version >= min_version and (max_version is None or version <= max_version) def _find_module_non_stub_helper( - self, components: list[str], pkg_dir: str + self, id: str, pkg_dir: str ) -> OnePackageDir | ModuleNotFoundReason: plausible_match = False dir_path = pkg_dir + components = id.split(".") for index, component in enumerate(components): dir_path = os_path_join(dir_path, component) if self.fscache.isfile(os_path_join(dir_path, "py.typed")): @@ -350,6 +351,10 @@ def _find_module_non_stub_helper( if not self.fscache.isdir(dir_path): break if plausible_match: + if self.options: + module_specific_options = self.options.clone_for_module(id) + if module_specific_options.follow_untyped_imports: + return os.path.join(pkg_dir, *components[:-1]), False return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: return ModuleNotFoundReason.NOT_FOUND @@ -463,7 +468,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: third_party_stubs_dirs.append((path, True)) else: third_party_stubs_dirs.append((path, True)) - non_stub_match = self._find_module_non_stub_helper(components, pkg_dir) + non_stub_match = self._find_module_non_stub_helper(id, pkg_dir) if isinstance(non_stub_match, ModuleNotFoundReason): if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: found_possible_third_party_missing_type_hints = True diff --git a/mypy/options.py b/mypy/options.py index 367267d1053a..561b23fec7d0 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -42,6 +42,7 @@ class BuildType: "extra_checks", "follow_imports_for_stubs", "follow_imports", + "follow_untyped_imports", "ignore_errors", "ignore_missing_imports", "implicit_optional", @@ -113,6 +114,8 @@ def __init__(self) -> None: self.ignore_missing_imports = False # Is ignore_missing_imports set in a per-module section self.ignore_missing_imports_per_module = False + # Typecheck modules without stubs or py.typed marker + self.follow_untyped_imports = False self.follow_imports = "normal" # normal|silent|skip|error # Whether to respect the follow_imports setting even for stub files. # Intended to be used for disabling specific stubs. diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index 9969c2894c36..fb303a8fb5ec 100644 --- a/test-data/unit/pep561.test +++ b/test-data/unit/pep561.test @@ -187,6 +187,13 @@ b.bf(1) testNamespacePkgWStubsWithNamespacePackagesFlag.py:7: error: Argument 1 to "bf" has incompatible type "int"; expected "bool" testNamespacePkgWStubsWithNamespacePackagesFlag.py:8: error: Argument 1 to "bf" has incompatible type "int"; expected "bool" +[case testMissingPytypedFlag] +# pkgs: typedpkg_ns_b +# flags: --namespace-packages --follow-untyped-imports +import typedpkg_ns.b.bbb as b +b.bf("foo", "bar") +[out] +testMissingPytypedFlag.py:4: error: Too many arguments for "bf" [case testTypedPkgNamespaceRegFromImportTwiceMissing] # pkgs: typedpkg_ns_a From f51090d053874aed0f3bfdb354e42f641296eaac Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:49:35 +0000 Subject: [PATCH 0935/1617] Make "deprecated" Note a standard Error, disabled by default (#18192) While working on the relase of mypy 1.14 we noticed a large number of notes for deprecated. Speaking with Jukka, he suggested we make this disabled by default. And if it's disabled by default, having it as a note is not as useful. We also don't have many stand alone notes. Most notes are "attached" with an error. This PR makes the deprecated error disabled by default and by default reports it as error when enabled. `--report-deprecated-as-note` can be used to report them as notes instead. --- docs/source/command_line.rst | 10 +- docs/source/error_code_list2.rst | 10 +- mypy/checker.py | 2 +- mypy/errorcodes.py | 1 + mypy/main.py | 4 +- mypy/options.py | 2 +- mypy/typeanal.py | 2 +- test-data/unit/check-deprecated.test | 226 ++++++++++++++------------- test-data/unit/fine-grained.test | 145 +++++++++-------- 9 files changed, 216 insertions(+), 186 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index e65317331d55..1d91625084fd 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -541,12 +541,12 @@ potentially problematic or redundant in some way. This limitation will be removed in future releases of mypy. -.. option:: --report-deprecated-as-error +.. option:: --report-deprecated-as-note - By default, mypy emits notes if your code imports or uses deprecated - features. This flag converts such notes to errors, causing mypy to - eventually finish with a non-zero exit code. Features are considered - deprecated when decorated with ``warnings.deprecated``. + If error code ``deprecated`` is enabled, mypy emits errors if your code + imports or uses deprecated features. This flag converts such errors to + notes, causing mypy to eventually finish with a zero exit code. Features + are considered deprecated when decorated with ``warnings.deprecated``. .. _miscellaneous-strictness-flags: diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index eb18d76e2f2f..df8b696745fc 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -236,13 +236,13 @@ incorrect control flow or conditional checks that are accidentally always true o Check that imported or used feature is deprecated [deprecated] -------------------------------------------------------------- -By default, mypy generates a note if your code imports a deprecated feature explicitly with a +If you use :option:`--enable-error-code deprecated `, +mypy generates an error if your code imports a deprecated feature explicitly with a ``from mod import depr`` statement or uses a deprecated feature imported otherwise or defined locally. Features are considered deprecated when decorated with ``warnings.deprecated``, as -specified in `PEP 702 `_. You can silence single notes via -``# type: ignore[deprecated]`` or turn off this check completely via ``--disable-error-code=deprecated``. -Use the :option:`--report-deprecated-as-error ` option for -more strictness, which turns all such notes into errors. +specified in `PEP 702 `_. +Use the :option:`--report-deprecated-as-note ` option to +turn all such errors into notes. .. note:: diff --git a/mypy/checker.py b/mypy/checker.py index 1b11caf01f18..379da3f1c0da 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7696,7 +7696,7 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: and ((deprecated := node.deprecated) is not None) and not self.is_typeshed_stub ): - warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note + warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail warn(deprecated, context, code=codes.DEPRECATED) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index b835f27bbad9..5736be5c143e 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -308,6 +308,7 @@ def __hash__(self) -> int: "deprecated", "Warn when importing or using deprecated (overloaded) functions, methods or classes", "General", + default_enabled=False, ) # This copy will not include any error codes defined later in the plugins. diff --git a/mypy/main.py b/mypy/main.py index fb4a1f61a01d..7032682c9fd0 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -810,10 +810,10 @@ def add_invertible_flag( group=lint_group, ) add_invertible_flag( - "--report-deprecated-as-error", + "--report-deprecated-as-note", default=False, strict_flag=False, - help="Report importing or using deprecated features as errors instead of notes", + help="Report importing or using deprecated features as notes instead of errors", group=lint_group, ) diff --git a/mypy/options.py b/mypy/options.py index 561b23fec7d0..33a2c75d164e 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -177,7 +177,7 @@ def __init__(self) -> None: self.warn_return_any = False # Report importing or using deprecated features as errors instead of notes. - self.report_deprecated_as_error = False + self.report_deprecated_as_note = False # Warn about unused '# type: ignore' comments self.warn_unused_ignores = False diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b7e7da17e209..2f85e83bb3c3 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -790,7 +790,7 @@ def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None: if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names): break else: - warn = self.fail if self.options.report_deprecated_as_error else self.note + warn = self.note if self.options.report_deprecated_as_note else self.fail warn(deprecated, ctx, code=codes.DEPRECATED) def analyze_type_with_type_info( diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index fbfdfcce5a14..8bbb887d4567 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -1,8 +1,7 @@ -- Type checker test cases for reporting deprecations. -[case testDeprecatedDisableNotes] -# flags: --disable-error-code=deprecated +[case testDeprecatedDisabled] from typing_extensions import deprecated @@ -15,7 +14,7 @@ f() [case testDeprecatedAsNoteWithErrorCode] -# flags: --show-error-codes +# flags: --enable-error-code=deprecated --show-error-codes --report-deprecated-as-note from typing_extensions import deprecated @@ -29,7 +28,7 @@ f() # N: function __main__.f is deprecated: use f2 instead [deprecated] [case testDeprecatedAsErrorWithErrorCode] -# flags: --report-deprecated-as-error --show-error-codes +# flags: --enable-error-code=deprecated --show-error-codes from typing_extensions import deprecated @@ -43,41 +42,43 @@ f() # E: function __main__.f is deprecated: use f2 instead [deprecated] [case testDeprecatedFunction] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @deprecated("use f2 instead") def f() -> None: ... -f # N: function __main__.f is deprecated: use f2 instead # type: ignore[deprecated] -f(1) # N: function __main__.f is deprecated: use f2 instead \ +f # E: function __main__.f is deprecated: use f2 instead # type: ignore[deprecated] +f(1) # E: function __main__.f is deprecated: use f2 instead \ # E: Too many arguments for "f" -f[1] # N: function __main__.f is deprecated: use f2 instead \ +f[1] # E: function __main__.f is deprecated: use f2 instead \ # E: Value of type "Callable[[], None]" is not indexable -g = f # N: function __main__.f is deprecated: use f2 instead +g = f # E: function __main__.f is deprecated: use f2 instead g() -t = (f, f, g) # N: function __main__.f is deprecated: use f2 instead +t = (f, f, g) # E: function __main__.f is deprecated: use f2 instead [builtins fixtures/tuple.pyi] [case testDeprecatedFunctionDifferentModule] +# flags: --enable-error-code=deprecated import m import p.s import m as n import p.s as ps -from m import f # N: function m.f is deprecated: use f2 instead -from p.s import g # N: function p.s.g is deprecated: use g2 instead +from m import f # E: function m.f is deprecated: use f2 instead +from p.s import g # E: function p.s.g is deprecated: use g2 instead from k import * -m.f() # N: function m.f is deprecated: use f2 instead -p.s.g() # N: function p.s.g is deprecated: use g2 instead -n.f() # N: function m.f is deprecated: use f2 instead -ps.g() # N: function p.s.g is deprecated: use g2 instead +m.f() # E: function m.f is deprecated: use f2 instead +p.s.g() # E: function p.s.g is deprecated: use g2 instead +n.f() # E: function m.f is deprecated: use f2 instead +ps.g() # E: function p.s.g is deprecated: use g2 instead f() g() -h() # N: function k.h is deprecated: use h2 instead +h() # E: function k.h is deprecated: use h2 instead [file m.py] from typing_extensions import deprecated @@ -101,6 +102,7 @@ def h() -> None: ... [case testDeprecatedClass] +# flags: --enable-error-code=deprecated from typing import Callable, List, Optional, Tuple, Union from typing_extensions import deprecated, TypeAlias, TypeVar @@ -108,65 +110,67 @@ from typing_extensions import deprecated, TypeAlias, TypeVar @deprecated("use C2 instead") class C: ... -c: C # N: class __main__.C is deprecated: use C2 instead -C() # N: class __main__.C is deprecated: use C2 instead -C.missing() # N: class __main__.C is deprecated: use C2 instead \ +c: C # E: class __main__.C is deprecated: use C2 instead +C() # E: class __main__.C is deprecated: use C2 instead +C.missing() # E: class __main__.C is deprecated: use C2 instead \ # E: "Type[C]" has no attribute "missing" -C.__init__(c) # N: class __main__.C is deprecated: use C2 instead -C(1) # N: class __main__.C is deprecated: use C2 instead \ +C.__init__(c) # E: class __main__.C is deprecated: use C2 instead +C(1) # E: class __main__.C is deprecated: use C2 instead \ # E: Too many arguments for "C" -D = C # N: class __main__.C is deprecated: use C2 instead +D = C # E: class __main__.C is deprecated: use C2 instead D() -t = (C, C, D) # N: class __main__.C is deprecated: use C2 instead +t = (C, C, D) # E: class __main__.C is deprecated: use C2 instead -u1: Union[C, int] = 1 # N: class __main__.C is deprecated: use C2 instead +u1: Union[C, int] = 1 # E: class __main__.C is deprecated: use C2 instead u1 = 1 -u2 = 1 # type: Union[C, int] # N: class __main__.C is deprecated: use C2 instead +u2 = 1 # type: Union[C, int] # E: class __main__.C is deprecated: use C2 instead u2 = 1 -c1 = c2 = C() # N: class __main__.C is deprecated: use C2 instead -i, c3 = 1, C() # N: class __main__.C is deprecated: use C2 instead +c1 = c2 = C() # E: class __main__.C is deprecated: use C2 instead +i, c3 = 1, C() # E: class __main__.C is deprecated: use C2 instead class E: ... -x1: Optional[C] # N: class __main__.C is deprecated: use C2 instead -x2: Union[D, C, E] # N: class __main__.C is deprecated: use C2 instead -x3: Union[D, Optional[C], E] # N: class __main__.C is deprecated: use C2 instead -x4: Tuple[D, C, E] # N: class __main__.C is deprecated: use C2 instead -x5: Tuple[Tuple[D, C], E] # N: class __main__.C is deprecated: use C2 instead -x6: List[C] # N: class __main__.C is deprecated: use C2 instead -x7: List[List[C]] # N: class __main__.C is deprecated: use C2 instead -x8: List[Optional[Tuple[Union[List[C], int]]]] # N: class __main__.C is deprecated: use C2 instead -x9: Callable[[int], C] # N: class __main__.C is deprecated: use C2 instead -x10: Callable[[int, C, int], int] # N: class __main__.C is deprecated: use C2 instead +x1: Optional[C] # E: class __main__.C is deprecated: use C2 instead +x2: Union[D, C, E] # E: class __main__.C is deprecated: use C2 instead +x3: Union[D, Optional[C], E] # E: class __main__.C is deprecated: use C2 instead +x4: Tuple[D, C, E] # E: class __main__.C is deprecated: use C2 instead +x5: Tuple[Tuple[D, C], E] # E: class __main__.C is deprecated: use C2 instead +x6: List[C] # E: class __main__.C is deprecated: use C2 instead +x7: List[List[C]] # E: class __main__.C is deprecated: use C2 instead +x8: List[Optional[Tuple[Union[List[C], int]]]] # E: class __main__.C is deprecated: use C2 instead +x9: Callable[[int], C] # E: class __main__.C is deprecated: use C2 instead +x10: Callable[[int, C, int], int] # E: class __main__.C is deprecated: use C2 instead T = TypeVar("T") -A1: TypeAlias = Optional[C] # N: class __main__.C is deprecated: use C2 instead +A1: TypeAlias = Optional[C] # E: class __main__.C is deprecated: use C2 instead x11: A1 -A2: TypeAlias = List[Union[A2, C]] # N: class __main__.C is deprecated: use C2 instead +A2: TypeAlias = List[Union[A2, C]] # E: class __main__.C is deprecated: use C2 instead x12: A2 A3: TypeAlias = List[Optional[T]] -x13: A3[C] # N: class __main__.C is deprecated: use C2 instead +x13: A3[C] # E: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] [case testDeprecatedBaseClass] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @deprecated("use C2 instead") class C: ... -class D(C): ... # N: class __main__.C is deprecated: use C2 instead +class D(C): ... # E: class __main__.C is deprecated: use C2 instead class E(D): ... -class F(D, C): ... # N: class __main__.C is deprecated: use C2 instead +class F(D, C): ... # E: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] [case testDeprecatedClassInTypeVar] +# flags: --enable-error-code=deprecated from typing import Generic, TypeVar from typing_extensions import deprecated @@ -175,11 +179,11 @@ class B: ... @deprecated("use C2 instead") class C: ... -T = TypeVar("T", bound=C) # N: class __main__.C is deprecated: use C2 instead +T = TypeVar("T", bound=C) # E: class __main__.C is deprecated: use C2 instead def f(x: T) -> T: ... class D(Generic[T]): ... -V = TypeVar("V", B, C) # N: class __main__.C is deprecated: use C2 instead +V = TypeVar("V", B, C) # E: class __main__.C is deprecated: use C2 instead def g(x: V) -> V: ... class E(Generic[V]): ... @@ -187,6 +191,7 @@ class E(Generic[V]): ... [case testDeprecatedClassInCast] +# flags: --enable-error-code=deprecated from typing import cast, Generic from typing_extensions import deprecated @@ -195,13 +200,14 @@ class B: ... @deprecated("use C2 instead") class C: ... -c = C() # N: class __main__.C is deprecated: use C2 instead +c = C() # E: class __main__.C is deprecated: use C2 instead b = cast(B, c) [builtins fixtures/tuple.pyi] [case testDeprecatedInstanceInFunctionDefinition] +# flags: --enable-error-code=deprecated from typing import Generic, List, Optional, TypeVar from typing_extensions import deprecated @@ -209,75 +215,76 @@ from typing_extensions import deprecated @deprecated("use C2 instead") class C: ... -def f1(c: C) -> None: # N: class __main__.C is deprecated: use C2 instead +def f1(c: C) -> None: # E: class __main__.C is deprecated: use C2 instead def g1() -> None: ... -def f2(c: List[Optional[C]]) -> None: # N: class __main__.C is deprecated: use C2 instead +def f2(c: List[Optional[C]]) -> None: # E: class __main__.C is deprecated: use C2 instead def g2() -> None: ... -def f3() -> C: # N: class __main__.C is deprecated: use C2 instead +def f3() -> C: # E: class __main__.C is deprecated: use C2 instead def g3() -> None: ... - return C() # N: class __main__.C is deprecated: use C2 instead + return C() # E: class __main__.C is deprecated: use C2 instead -def f4() -> List[Optional[C]]: # N: class __main__.C is deprecated: use C2 instead +def f4() -> List[Optional[C]]: # E: class __main__.C is deprecated: use C2 instead def g4() -> None: ... return [] def f5() -> None: - def g5(c: C) -> None: ... # N: class __main__.C is deprecated: use C2 instead + def g5(c: C) -> None: ... # E: class __main__.C is deprecated: use C2 instead def f6() -> None: - def g6() -> C: ... # N: class __main__.C is deprecated: use C2 instead + def g6() -> C: ... # E: class __main__.C is deprecated: use C2 instead @deprecated("use D2 instead") class D: - def f1(self, c: C) -> None: # N: class __main__.C is deprecated: use C2 instead + def f1(self, c: C) -> None: # E: class __main__.C is deprecated: use C2 instead def g1() -> None: ... - def f2(self, c: List[Optional[C]]) -> None: # N: class __main__.C is deprecated: use C2 instead + def f2(self, c: List[Optional[C]]) -> None: # E: class __main__.C is deprecated: use C2 instead def g2() -> None: ... def f3(self) -> None: - def g3(c: C) -> None: ... # N: class __main__.C is deprecated: use C2 instead + def g3(c: C) -> None: ... # E: class __main__.C is deprecated: use C2 instead def f4(self) -> None: - def g4() -> C: ... # N: class __main__.C is deprecated: use C2 instead + def g4() -> C: ... # E: class __main__.C is deprecated: use C2 instead T = TypeVar("T") @deprecated("use E2 instead") class E(Generic[T]): - def f1(self: E[C]) -> None: ... # N: class __main__.C is deprecated: use C2 instead - def f2(self, e: E[C]) -> None: ... # N: class __main__.C is deprecated: use C2 instead - def f3(self) -> E[C]: ... # N: class __main__.C is deprecated: use C2 instead + def f1(self: E[C]) -> None: ... # E: class __main__.C is deprecated: use C2 instead + def f2(self, e: E[C]) -> None: ... # E: class __main__.C is deprecated: use C2 instead + def f3(self) -> E[C]: ... # E: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] [case testDeprecatedClassDifferentModule] +# flags: --enable-error-code=deprecated import m import p.s import m as n import p.s as ps -from m import B, C # N: class m.B is deprecated: use B2 instead \ - # N: class m.C is deprecated: use C2 instead -from p.s import D # N: class p.s.D is deprecated: use D2 instead +from m import B, C # E: class m.B is deprecated: use B2 instead \ + # E: class m.C is deprecated: use C2 instead +from p.s import D # E: class p.s.D is deprecated: use D2 instead from k import * -m.C() # N: class m.C is deprecated: use C2 instead -p.s.D() # N: class p.s.D is deprecated: use D2 instead -n.C() # N: class m.C is deprecated: use C2 instead -ps.D() # N: class p.s.D is deprecated: use D2 instead +m.C() # E: class m.C is deprecated: use C2 instead +p.s.D() # E: class p.s.D is deprecated: use D2 instead +n.C() # E: class m.C is deprecated: use C2 instead +ps.D() # E: class p.s.D is deprecated: use D2 instead C() D() -E() # N: class k.E is deprecated: use E2 instead +E() # E: class k.E is deprecated: use E2 instead -x1: m.A # N: class m.A is deprecated: use A2 instead -x2: m.A = m.A() # N: class m.A is deprecated: use A2 instead +x1: m.A # E: class m.A is deprecated: use A2 instead +x2: m.A = m.A() # E: class m.A is deprecated: use A2 instead y1: B y2: B = B() @@ -309,6 +316,7 @@ class E: ... [case testDeprecatedClassInitMethod] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @@ -316,14 +324,15 @@ from typing_extensions import deprecated class C: def __init__(self) -> None: ... -c: C # N: class __main__.C is deprecated: use C2 instead -C() # N: class __main__.C is deprecated: use C2 instead -C.__init__(c) # N: class __main__.C is deprecated: use C2 instead +c: C # E: class __main__.C is deprecated: use C2 instead +C() # E: class __main__.C is deprecated: use C2 instead +C.__init__(c) # E: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] [case testDeprecatedSpecialMethods] +# flags: --enable-error-code=deprecated from typing import Iterator from typing_extensions import deprecated @@ -356,19 +365,20 @@ class B: a = A() b = B() -a + 1 # N: function __main__.A.__add__ is deprecated: no A + int -1 + a # N: function __main__.A.__radd__ is deprecated: no int + A -a += 1 # N: function __main__.A.__iadd__ is deprecated: no A = A + int -for i in a: # N: function __main__.A.__iter__ is deprecated: no iteration +a + 1 # E: function __main__.A.__add__ is deprecated: no A + int +1 + a # E: function __main__.A.__radd__ is deprecated: no int + A +a += 1 # E: function __main__.A.__iadd__ is deprecated: no A = A + int +for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration reveal_type(i) # N: Revealed type is "builtins.int" -1 in a # N: function __main__.A.__contains__ is deprecated: no in -1 in b # N: function __main__.B.__contains__ is deprecated: still no in -~a # N: function __main__.A.__invert__ is deprecated: no inversion +1 in a # E: function __main__.A.__contains__ is deprecated: no in +1 in b # E: function __main__.B.__contains__ is deprecated: still no in +~a # E: function __main__.A.__invert__ is deprecated: no inversion [builtins fixtures/tuple.pyi] [case testDeprecatedOverloadedSpecialMethods] +# flags: --enable-error-code=deprecated from typing import Iterator, Union from typing_extensions import deprecated, overload @@ -396,17 +406,18 @@ class A: def __iadd__(self, v: Union[int, str]) -> A: ... a = A() -a + 1 # N: overload def (__main__.A, builtins.int) of function __main__.A.__add__ is deprecated: no A + int +a + 1 # E: overload def (__main__.A, builtins.int) of function __main__.A.__add__ is deprecated: no A + int a + "x" 1 + a -"x" + a # N: overload def (__main__.A, builtins.str) of function __main__.A.__radd__ is deprecated: no str + A -a += 1 # N: function __main__.A.__iadd__ is deprecated: no A += Any -a += "x" # N: function __main__.A.__iadd__ is deprecated: no A += Any +"x" + a # E: overload def (__main__.A, builtins.str) of function __main__.A.__radd__ is deprecated: no str + A +a += 1 # E: function __main__.A.__iadd__ is deprecated: no A += Any +a += "x" # E: function __main__.A.__iadd__ is deprecated: no A += Any [builtins fixtures/tuple.pyi] [case testDeprecatedMethod] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @@ -424,23 +435,24 @@ class C: @staticmethod def k() -> None: ... -C.f # N: function __main__.C.f is deprecated: use g instead -C().f # N: function __main__.C.f is deprecated: use g instead -C().f() # N: function __main__.C.f is deprecated: use g instead -C().f(1) # N: function __main__.C.f is deprecated: use g instead \ +C.f # E: function __main__.C.f is deprecated: use g instead +C().f # E: function __main__.C.f is deprecated: use g instead +C().f() # E: function __main__.C.f is deprecated: use g instead +C().f(1) # E: function __main__.C.f is deprecated: use g instead \ # E: Too many arguments for "f" of "C" -f = C().f # N: function __main__.C.f is deprecated: use g instead +f = C().f # E: function __main__.C.f is deprecated: use g instead f() -t = (C.f, C.f, C.g) # N: function __main__.C.f is deprecated: use g instead +t = (C.f, C.f, C.g) # E: function __main__.C.f is deprecated: use g instead C().g() -C().h() # N: function __main__.C.h is deprecated: use g instead -C().k() # N: function __main__.C.k is deprecated: use g instead +C().h() # E: function __main__.C.h is deprecated: use g instead +C().k() # E: function __main__.C.k is deprecated: use g instead [builtins fixtures/callable.pyi] [case testDeprecatedClassWithDeprecatedMethod] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @@ -450,14 +462,15 @@ class C: def f(self) -> None: ... def g(self) -> None: ... -C().f() # N: class __main__.C is deprecated: use D instead \ - # N: function __main__.C.f is deprecated: use g instead -C().g() # N: class __main__.C is deprecated: use D instead +C().f() # E: class __main__.C is deprecated: use D instead \ + # E: function __main__.C.f is deprecated: use g instead +C().g() # E: class __main__.C is deprecated: use D instead [builtins fixtures/callable.pyi] [case testDeprecatedProperty] +# flags: --enable-error-code=deprecated from typing_extensions import deprecated @@ -473,24 +486,25 @@ class C: def g(self, v: int) -> None: ... -C.f # N: function __main__.C.f is deprecated: use f2 instead -C().f # N: function __main__.C.f is deprecated: use f2 instead -C().f() # N: function __main__.C.f is deprecated: use f2 instead \ +C.f # E: function __main__.C.f is deprecated: use f2 instead +C().f # E: function __main__.C.f is deprecated: use f2 instead +C().f() # E: function __main__.C.f is deprecated: use f2 instead \ # E: "int" not callable -C().f = 1 # N: function __main__.C.f is deprecated: use f2 instead \ +C().f = 1 # E: function __main__.C.f is deprecated: use f2 instead \ # E: Property "f" defined in "C" is read-only C.g C().g -C().g = 1 # N: function __main__.C.g is deprecated: use g2 instead -C().g = "x" # N: function __main__.C.g is deprecated: use g2 instead \ +C().g = 1 # E: function __main__.C.g is deprecated: use g2 instead +C().g = "x" # E: function __main__.C.g is deprecated: use g2 instead \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/property.pyi] [case testDeprecatedOverloadedFunction] +# flags: --enable-error-code=deprecated from typing import Union from typing_extensions import deprecated, overload @@ -502,10 +516,10 @@ def f(x: str) -> str: ... @deprecated("use f2 instead") def f(x: Union[int, str]) -> Union[int, str]: ... -f # N: function __main__.f is deprecated: use f2 instead -f(1) # N: function __main__.f is deprecated: use f2 instead -f("x") # N: function __main__.f is deprecated: use f2 instead -f(1.0) # N: function __main__.f is deprecated: use f2 instead \ +f # E: function __main__.f is deprecated: use f2 instead +f(1) # E: function __main__.f is deprecated: use f2 instead +f("x") # E: function __main__.f is deprecated: use f2 instead +f(1.0) # E: function __main__.f is deprecated: use f2 instead \ # E: No overload variant of "f" matches argument type "float" \ # N: Possible overload variants: \ # N: def f(x: int) -> int \ @@ -519,7 +533,7 @@ def g(x: str) -> str: ... def g(x: Union[int, str]) -> Union[int, str]: ... g -g(1) # N: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead +g(1) # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead g("x") g(1.0) # E: No overload variant of "g" matches argument type "float" \ # N: Possible overload variants: \ @@ -535,7 +549,7 @@ def h(x: Union[int, str]) -> Union[int, str]: ... h h(1) -h("x") # N: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead +h("x") # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead h(1.0) # E: No overload variant of "h" matches argument type "float" \ # N: Possible overload variants: \ # N: def h(x: int) -> int \ diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index f30f1e232364..9ff8a37ae9ae 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10682,6 +10682,8 @@ main:4: error: Incompatible types in assignment (expression has type "f", variab [case testDeprecatedAddKeepChangeAndRemoveFunctionDeprecation] +# flags: --enable-error-code=deprecated + from a import f f() import a @@ -10711,18 +10713,19 @@ def f() -> None: ... [builtins fixtures/tuple.pyi] [out] == -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +main:3: error: function a.f is deprecated: use f2 instead +main:6: error: function a.f is deprecated: use f2 instead == -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +main:3: error: function a.f is deprecated: use f2 instead +main:6: error: function a.f is deprecated: use f2 instead == -main:1: note: function a.f is deprecated: use f3 instead -main:4: note: function a.f is deprecated: use f3 instead +main:3: error: function a.f is deprecated: use f3 instead +main:6: error: function a.f is deprecated: use f3 instead == [case testDeprecatedRemoveFunctionDeprecation] +# flags: --enable-error-code=deprecated from a import f f() import a @@ -10738,11 +10741,12 @@ def f() -> None: ... [builtins fixtures/tuple.pyi] [out] -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead == [case testDeprecatedKeepFunctionDeprecation] +# flags: --enable-error-code=deprecated from a import f f() import a @@ -10760,14 +10764,15 @@ def f() -> None: ... [builtins fixtures/tuple.pyi] [out] -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead == -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead [case testDeprecatedAddFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated from b import f f() import b @@ -10787,12 +10792,13 @@ def f() -> int: ... [builtins fixtures/tuple.pyi] [out] == -b.py:1: note: function a.f is deprecated: use f2 instead -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +b.py:1: error: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead [case testDeprecatedChangeFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated from b import f f() import b @@ -10813,15 +10819,16 @@ def f() -> int: ... [builtins fixtures/tuple.pyi] [out] -b.py:1: note: function a.f is deprecated: use f1 instead -main:1: note: function a.f is deprecated: use f1 instead -main:4: note: function a.f is deprecated: use f1 instead +b.py:1: error: function a.f is deprecated: use f1 instead +main:2: error: function a.f is deprecated: use f1 instead +main:5: error: function a.f is deprecated: use f1 instead == -b.py:1: note: function a.f is deprecated: use f2 instead -main:1: note: function a.f is deprecated: use f2 instead -main:4: note: function a.f is deprecated: use f2 instead +b.py:1: error: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead [case testDeprecatedRemoveFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated from b import f f() import b @@ -10840,13 +10847,14 @@ def f() -> int: ... [builtins fixtures/tuple.pyi] [out] -b.py:1: note: function a.f is deprecated: use f1 instead -main:1: note: function a.f is deprecated: use f1 instead -main:4: note: function a.f is deprecated: use f1 instead +b.py:1: error: function a.f is deprecated: use f1 instead +main:2: error: function a.f is deprecated: use f1 instead +main:5: error: function a.f is deprecated: use f1 instead == [case testDeprecatedFunctionAlreadyDecorated1-only_when_cache] +# flags: --enable-error-code=deprecated from b import f x: str = f() import b @@ -10876,12 +10884,13 @@ def f() -> str: ... [builtins fixtures/tuple.pyi] [out] == -b.py:1: note: function a.f is deprecated: deprecated decorated function -main:1: note: function a.f is deprecated: deprecated decorated function -main:4: note: function a.f is deprecated: deprecated decorated function +b.py:1: error: function a.f is deprecated: deprecated decorated function +main:2: error: function a.f is deprecated: deprecated decorated function +main:5: error: function a.f is deprecated: deprecated decorated function [case testDeprecatedFunctionAlreadyDecorated2-only_when_nocache] +# flags: --enable-error-code=deprecated from b import f x: str = f() import b @@ -10911,12 +10920,13 @@ def f() -> str: ... [builtins fixtures/tuple.pyi] [out] == -main:1: note: function a.f is deprecated: deprecated decorated function -main:4: note: function a.f is deprecated: deprecated decorated function -b.py:1: note: function a.f is deprecated: deprecated decorated function +main:2: error: function a.f is deprecated: deprecated decorated function +main:5: error: function a.f is deprecated: deprecated decorated function +b.py:1: error: function a.f is deprecated: deprecated decorated function [case testDeprecatedAddClassDeprecationIndirectImport1-only_when_cache] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -10944,14 +10954,15 @@ class D: ... [builtins fixtures/tuple.pyi] [out] == -b.py:1: note: class a.C is deprecated: use C2 instead -b.py:2: note: class a.D is deprecated: use D2 instead -main:1: note: class a.C is deprecated: use C2 instead -main:5: note: class a.D is deprecated: use D2 instead -main:6: note: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead [case testDeprecatedAddClassDeprecationIndirectImport2-only_when_nocache] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -10979,14 +10990,15 @@ class D: ... [builtins fixtures/tuple.pyi] [out] == -main:1: note: class a.C is deprecated: use C2 instead -main:5: note: class a.D is deprecated: use D2 instead -main:6: note: class a.D is deprecated: use D2 instead -b.py:1: note: class a.C is deprecated: use C2 instead -b.py:2: note: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead [case testDeprecatedChangeClassDeprecationIndirectImport] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -11017,20 +11029,21 @@ class D: ... [builtins fixtures/tuple.pyi] [out] -b.py:1: note: class a.C is deprecated: use C1 instead -b.py:2: note: class a.D is deprecated: use D1 instead -main:1: note: class a.C is deprecated: use C1 instead -main:5: note: class a.D is deprecated: use D1 instead -main:6: note: class a.D is deprecated: use D1 instead +b.py:1: error: class a.C is deprecated: use C1 instead +b.py:2: error: class a.D is deprecated: use D1 instead +main:2: error: class a.C is deprecated: use C1 instead +main:6: error: class a.D is deprecated: use D1 instead +main:7: error: class a.D is deprecated: use D1 instead == -b.py:1: note: class a.C is deprecated: use C2 instead -b.py:2: note: class a.D is deprecated: use D2 instead -main:1: note: class a.C is deprecated: use C2 instead -main:5: note: class a.D is deprecated: use D2 instead -main:6: note: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead [case testDeprecatedRemoveClassDeprecationIndirectImport] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -11056,15 +11069,16 @@ class D: ... [builtins fixtures/tuple.pyi] [out] -b.py:1: note: class a.C is deprecated: use C1 instead -b.py:2: note: class a.D is deprecated: use D1 instead -main:1: note: class a.C is deprecated: use C1 instead -main:5: note: class a.D is deprecated: use D1 instead -main:6: note: class a.D is deprecated: use D1 instead +b.py:1: error: class a.C is deprecated: use C1 instead +b.py:2: error: class a.D is deprecated: use D1 instead +main:2: error: class a.C is deprecated: use C1 instead +main:6: error: class a.D is deprecated: use D1 instead +main:7: error: class a.D is deprecated: use D1 instead == [case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated1-only_when_cache] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -11099,14 +11113,15 @@ class D: ... [builtins fixtures/tuple.pyi] [out] == -b.py:1: note: class a.C is deprecated: use C2 instead -b.py:2: note: class a.D is deprecated: use D2 instead -main:1: note: class a.C is deprecated: use C2 instead -main:5: note: class a.D is deprecated: use D2 instead -main:6: note: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead [case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated2-only_when_nocache] +# flags: --enable-error-code=deprecated from b import C x: C C() @@ -11141,8 +11156,8 @@ class D: ... [builtins fixtures/tuple.pyi] [out] == -main:1: note: class a.C is deprecated: use C2 instead -main:5: note: class a.D is deprecated: use D2 instead -main:6: note: class a.D is deprecated: use D2 instead -b.py:1: note: class a.C is deprecated: use C2 instead -b.py:2: note: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead From cc45bec732d6ccf4f846c5fd40617fed9cc04e8d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Dec 2024 12:31:19 +0000 Subject: [PATCH 0936/1617] [mypyc] Make exception type check in assertRaises test helper precise (#18241) --- mypyc/test-data/fixtures/testutil.py | 2 +- mypyc/test-data/run-i64.test | 18 +++++++++--------- mypyc/test-data/run-misc.test | 4 ++-- mypyc/test-data/run-primitives.test | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index f210faf71109..36ec41c8f38b 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -45,7 +45,7 @@ def assertRaises(typ: type, msg: str = '') -> Iterator[None]: try: yield except Exception as e: - assert isinstance(e, typ), f"{e!r} is not a {typ.__name__}" + assert type(e) is typ, f"{e!r} is not a {typ.__name__}" assert msg in str(e), f'Message "{e}" does not match "{msg}"' else: assert False, f"Expected {typ.__name__} but got no exception" diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 1a82ac3e2dd1..36567c949d79 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1307,28 +1307,28 @@ def test_many_locals() -> None: a31: i64 = 31 a32: i64 = 32 a33: i64 = 33 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a0) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a32) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a0 = 5 assert a0 == 5 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a32) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a32 = 55 assert a0 == 5 assert a32 == 55 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a31 = 10 a33 = 20 diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index f07ac51dae6c..2252f3aa104a 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -37,9 +37,9 @@ from testutil import assertRaises f(True, True) f(False, False) -with assertRaises(NameError): +with assertRaises(UnboundLocalError): f(False, True) -with assertRaises(NameError): +with assertRaises(UnboundLocalError): g() [out] lol diff --git a/mypyc/test-data/run-primitives.test b/mypyc/test-data/run-primitives.test index b95f742977be..694700d4738c 100644 --- a/mypyc/test-data/run-primitives.test +++ b/mypyc/test-data/run-primitives.test @@ -345,10 +345,10 @@ delAttribute() delAttributeMultiple() with assertRaises(AttributeError): native.global_var -with assertRaises(NameError, 'local variable "dummy" referenced before assignment'): +with assertRaises(UnboundLocalError, 'local variable "dummy" referenced before assignment'): delLocal(True) assert delLocal(False) == 10 -with assertRaises(NameError, 'local variable "dummy" referenced before assignment'): +with assertRaises(UnboundLocalError, 'local variable "dummy" referenced before assignment'): delLocalLoop() [out] (1, 2, 3) From ee19ea7d26ccdda3e0e79ef36aeb98cddb527903 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Dec 2024 13:28:19 +0000 Subject: [PATCH 0937/1617] [mypyc] Add primitives and specialization for ord() (#18240) This makes a microbenchmark adapted from an internal production codebase that heavily uses `ord()` over 10x faster. Work on mypyc/mypyc#644 and mypyc/mypyc#880. --- mypyc/doc/str_operations.rst | 6 +++++ mypyc/irbuild/specialize.py | 11 ++++++++ mypyc/lib-rt/CPy.h | 2 ++ mypyc/lib-rt/bytes_ops.c | 17 +++++++++++++ mypyc/lib-rt/str_ops.c | 12 +++++++++ mypyc/primitives/bytes_ops.py | 8 ++++++ mypyc/primitives/str_ops.py | 8 ++++++ mypyc/test-data/irbuild-str.test | 43 ++++++++++++++++++++++++++++++++ mypyc/test-data/run-bytes.test | 23 +++++++++++++++++ mypyc/test-data/run-strings.test | 27 ++++++++++++-------- 10 files changed, 147 insertions(+), 10 deletions(-) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 5420c8af7d31..a8f2cf43a991 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -33,3 +33,9 @@ Methods * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` * ``s1.startswith(s2: str)`` + +Functions +--------- + + * ``len(s: str)`` + * ``ord(s: str)`` diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index cb69852af1ce..f652449f5289 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -19,6 +19,7 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, + BytesExpr, CallExpr, DictExpr, Expression, @@ -877,3 +878,13 @@ def translate_float(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Valu # No-op float conversion. return builder.accept(arg) return None + + +@specialize_function("builtins.ord") +def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: + return None + arg = expr.args[0] + if isinstance(arg, (StrExpr, BytesExpr)) and len(arg.value) == 1: + return Integer(ord(arg.value)) + return None diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 833b1bd2e76a..d3637cde49ff 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -730,6 +730,7 @@ bool CPyStr_IsTrue(PyObject *obj); Py_ssize_t CPyStr_Size_size_t(PyObject *str); PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors); PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors); +CPyTagged CPyStr_Ord(PyObject *obj); // Bytes operations @@ -740,6 +741,7 @@ PyObject *CPyBytes_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); CPyTagged CPyBytes_GetItem(PyObject *o, CPyTagged index); PyObject *CPyBytes_Concat(PyObject *a, PyObject *b); PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter); +CPyTagged CPyBytes_Ord(PyObject *obj); int CPyBytes_Compare(PyObject *left, PyObject *right); diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 0cb2f300d507..5ddf3528211f 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -141,3 +141,20 @@ PyObject *CPyBytes_Build(Py_ssize_t len, ...) { return (PyObject *)ret; } + + +CPyTagged CPyBytes_Ord(PyObject *obj) { + if (PyBytes_Check(obj)) { + Py_ssize_t s = PyBytes_GET_SIZE(obj); + if (s == 1) { + return (unsigned char)(PyBytes_AS_STRING(obj)[0]) << 1; + } + } else if (PyByteArray_Check(obj)) { + Py_ssize_t s = PyByteArray_GET_SIZE(obj); + if (s == 1) { + return (unsigned char)(PyByteArray_AS_STRING(obj)[0]) << 1; + } + } + PyErr_SetString(PyExc_TypeError, "ord() expects a character"); + return CPY_INT_TAG; +} diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 4ba181bcce85..68026037502d 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -243,3 +243,15 @@ PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) { return NULL; } } + + +CPyTagged CPyStr_Ord(PyObject *obj) { + Py_ssize_t s = PyUnicode_GET_LENGTH(obj); + if (s == 1) { + int kind = PyUnicode_KIND(obj); + return PyUnicode_READ(kind, PyUnicode_DATA(obj), 0) << 1; + } + PyErr_Format( + PyExc_TypeError, "ord() expected a character, but a string of length %zd found", s); + return CPY_INT_TAG; +} diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index d7a7f3e2f59b..1afd196cff84 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -99,3 +99,11 @@ error_kind=ERR_MAGIC, var_arg_type=bytes_rprimitive, ) + +function_op( + name="builtins.ord", + arg_types=[bytes_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyBytes_Ord", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 3a5495e21c1b..0accffd86a17 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -251,3 +251,11 @@ c_function_name="CPy_Encode", error_kind=ERR_MAGIC, ) + +function_op( + name="builtins.ord", + arg_types=[str_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyStr_Ord", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 61e5a42cf3ef..d17c66bba22f 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -383,3 +383,46 @@ L0: r37 = 'latin2' r38 = CPy_Encode(s, r37, 0) return 1 + +[case testOrd] +def str_ord(x: str) -> int: + return ord(x) +def str_ord_literal() -> int: + return ord("a") +def bytes_ord(x: bytes) -> int: + return ord(x) +def bytes_ord_literal() -> int: + return ord(b"a") +def any_ord(x) -> int: + return ord(x) +[out] +def str_ord(x): + x :: str + r0 :: int +L0: + r0 = CPyStr_Ord(x) + return r0 +def str_ord_literal(): +L0: + return 194 +def bytes_ord(x): + x :: bytes + r0 :: int +L0: + r0 = CPyBytes_Ord(x) + return r0 +def bytes_ord_literal(): +L0: + return 194 +def any_ord(x): + x, r0 :: object + r1 :: str + r2, r3 :: object + r4 :: int +L0: + r0 = builtins :: module + r1 = 'ord' + r2 = CPyObject_GetAttr(r0, r1) + r3 = PyObject_CallFunctionObjArgs(r2, x, 0) + r4 = unbox(int, r3) + return r4 diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index aaf541194ac6..fa63c46a6798 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -111,6 +111,29 @@ def test_len() -> None: assert len(b) == 3 assert len(bytes()) == 0 +def test_ord() -> None: + assert ord(b'a') == ord('a') + assert ord(b'a' + bytes()) == ord('a') + assert ord(b'\x00') == 0 + assert ord(b'\x00' + bytes()) == 0 + assert ord(b'\xfe') == 254 + assert ord(b'\xfe' + bytes()) == 254 + + with assertRaises(TypeError): + ord(b'aa') + with assertRaises(TypeError): + ord(b'') + +def test_ord_bytesarray() -> None: + assert ord(bytearray(b'a')) == ord('a') + assert ord(bytearray(b'\x00')) == 0 + assert ord(bytearray(b'\xfe')) == 254 + + with assertRaises(TypeError): + ord(bytearray(b'aa')) + with assertRaises(TypeError): + ord(bytearray(b'')) + [case testBytesSlicing] def test_bytes_slicing() -> None: b = b'abcdefg' diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 2becae848f7c..1caddce9848d 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -565,25 +565,32 @@ def test_chr() -> None: assert try_invalid(1114112) [case testOrd] +from testutil import assertRaises + def test_ord() -> None: + assert ord(' ') == 32 + assert ord(' ' + str()) == 32 + assert ord('\x00') == 0 + assert ord('\x00' + str()) == 0 assert ord('\ue000') == 57344 - s = "a\xac\u1234\u20ac\U00008000" - # ^^^^ two-digit hex escape - # ^^^^^^ four-digit Unicode escape - # ^^^^^^^^^^ eight-digit Unicode escape + assert ord('\ue000' + str()) == 57344 + s = "a\xac\u1234\u20ac\U00010000" + # ^^^^ two-digit hex escape + # ^^^^^^ four-digit Unicode escape + # ^^^^^^^^^^ eight-digit Unicode escape l1 = [ord(c) for c in s] - assert l1 == [97, 172, 4660, 8364, 32768] + assert l1 == [97, 172, 4660, 8364, 65536] u = 'abcdé' assert ord(u[-1]) == 233 assert ord(b'a') == 97 assert ord(b'a' + bytes()) == 97 - u2 = '\U0010ffff' + u2 = '\U0010ffff' + str() assert ord(u2) == 1114111 - try: + assert ord('\U0010ffff') == 1114111 + with assertRaises(TypeError, "ord() expected a character, but a string of length 2 found"): ord('aa') - assert False - except TypeError: - pass + with assertRaises(TypeError): + ord('') [case testDecode] def test_decode() -> None: From 5082a221f33e6f2ceb32830f733890a1b21061db Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Dec 2024 13:52:02 +0000 Subject: [PATCH 0938/1617] [mypyc] Document optimized bytes ops and additional str ops (#18242) The docs were somewhat out of date. --- mypyc/doc/bytes_operations.rst | 46 ++++++++++++++++++++++++++++++++++ mypyc/doc/index.rst | 1 + mypyc/doc/str_operations.rst | 22 ++++++++++++++-- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 mypyc/doc/bytes_operations.rst diff --git a/mypyc/doc/bytes_operations.rst b/mypyc/doc/bytes_operations.rst new file mode 100644 index 000000000000..038da6391949 --- /dev/null +++ b/mypyc/doc/bytes_operations.rst @@ -0,0 +1,46 @@ +.. _bytes-ops: + +Native bytes operations +======================== + +These ``bytes`` operations have fast, optimized implementations. Other +bytes operations use generic implementations that are often slower. + +Construction +------------ + +* Bytes literal +* ``bytes(x: list)`` + +Operators +--------- + +* Concatenation (``b1 + b2``) +* Indexing (``b[n]``) +* Slicing (``b[n:m]``, ``b[n:]``, ``b[:m]``) +* Comparisons (``==``, ``!=``) + +.. _bytes-methods: + +Methods +------- + +* ``b.decode()`` +* ``b.decode(encoding: str)`` +* ``b.decode(encoding: str, errors: str)`` +* ``b.join(x: Iterable)`` + +.. note:: + + :ref:`str.encode() ` is also optimized. + +Formatting +---------- + +A subset of % formatting operations are optimized (``b"..." % (...)``). + +Functions +--------- + +* ``len(b: bytes)`` +* ``ord(b: bytes)`` diff --git a/mypyc/doc/index.rst b/mypyc/doc/index.rst index 5b1cc48fab3d..584d6739e803 100644 --- a/mypyc/doc/index.rst +++ b/mypyc/doc/index.rst @@ -36,6 +36,7 @@ generate fast code. bool_operations float_operations str_operations + bytes_operations list_operations dict_operations set_operations diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index a8f2cf43a991..9e94f1b6d7bb 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -22,9 +22,14 @@ Operators * Comparisons (``==``, ``!=``) * Augmented assignment (``s1 += s2``) +.. _str-methods: + Methods ------- +* ``s.encode()`` +* ``s.encode(encoding: str)`` +* ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` * ``s.join(x: Iterable)`` * ``s.replace(old: str, new: str)`` @@ -34,8 +39,21 @@ Methods * ``s.split(sep: str, maxsplit: int)`` * ``s1.startswith(s2: str)`` +.. note:: + + :ref:`bytes.decode() ` is also optimized. + +Formatting +---------- + +A subset of these common string formatting expressions are optimized: + +* F-strings +* ``"...".format(...)`` +* ``"..." % (...)`` + Functions --------- - * ``len(s: str)`` - * ``ord(s: str)`` +* ``len(s: str)`` +* ``ord(s: str)`` From 04ffee108125ee5cdab493c60110137f2c63116b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 5 Dec 2024 10:23:14 +0000 Subject: [PATCH 0939/1617] [mypyc] Update docstrings of IR builder classes (#18246) Having the documentation in class docstrings makes it easier to find. --- mypyc/irbuild/builder.py | 36 ++++++++++++++++++++++++++---------- mypyc/irbuild/ll_builder.py | 24 ++++++++++++++++++------ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index a0837ba2bfc7..1d0dd495eea5 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -1,14 +1,6 @@ -"""Builder class used to transform a mypy AST to the IR form. +"""Builder class to transform a mypy AST to the IR form. -The IRBuilder class maintains transformation state and provides access -to various helpers used to implement the transform. - -The top-level transform control logic is in mypyc.irbuild.main. - -mypyc.irbuild.visitor.IRBuilderVisitor is used to dispatch based on mypy -AST node type to code that actually does the bulk of the work. For -example, expressions are transformed in mypyc.irbuild.expression and -functions are transformed in mypyc.irbuild.function. +See the docstring of class IRBuilder for more information. """ from __future__ import annotations @@ -154,6 +146,30 @@ class UnsupportedException(Exception): class IRBuilder: + """Builder class used to construct mypyc IR from a mypy AST. + + The IRBuilder class maintains IR transformation state and provides access + to various helpers used to implement the transform. + + mypyc.irbuild.visitor.IRBuilderVisitor is used to dispatch based on mypy + AST node type to code that actually does the bulk of the work. For + example, expressions are transformed in mypyc.irbuild.expression and + functions are transformed in mypyc.irbuild.function. + + Use the "accept()" method to translate individual mypy AST nodes to IR. + Other methods are used to generate IR for various lower-level operations. + + This class wraps the lower-level LowLevelIRBuilder class, an instance + of which is available through the "builder" attribute. The low-level + builder class doesn't have any knowledge of the mypy AST. Wrappers for + some LowLevelIRBuilder method are provided for convenience, but others + can also be accessed via the "builder" attribute. + + See also: + * The mypyc IR is defined in the mypyc.ir package. + * The top-level IR transform control logic is in mypyc.irbuild.main. + """ + def __init__( self, current_module: str, diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 556d753b89f8..5c9bd9412e9b 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1,11 +1,7 @@ """A "low-level" IR builder class. -LowLevelIRBuilder provides core abstractions we use for constructing -IR as well as a number of higher-level ones (accessing attributes, -calling functions and methods, and coercing between types, for -example). The core principle of the low-level IR builder is that all -of its facilities operate solely on the IR level and not the AST -level---it has *no knowledge* of mypy types or expressions. +See the docstring of class LowLevelIRBuiler for more information. + """ from __future__ import annotations @@ -224,6 +220,22 @@ class LowLevelIRBuilder: + """A "low-level" IR builder class. + + LowLevelIRBuilder provides core abstractions we use for constructing + IR as well as a number of higher-level ones (accessing attributes, + calling functions and methods, and coercing between types, for + example). + + The core principle of the low-level IR builder is that all of its + facilities operate solely on the mypyc IR level and not the mypy AST + level---it has *no knowledge* of mypy types or expressions. + + The mypyc.irbuilder.builder.IRBuilder class wraps an instance of this + class and provides additional functionality to transform mypy AST nodes + to IR. + """ + def __init__(self, errors: Errors | None, options: CompilerOptions) -> None: self.errors = errors self.options = options From 71ec4a62f08df4fa28f6b1a9f5bc45c272eaa49f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:16:34 +0100 Subject: [PATCH 0940/1617] Sync typeshed (#18248) Source commit: https://github.com/python/typeshed/commit/633a4d73f257d3d1e73f8fdae24f2ddcca724399 --- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_dummy_threading.pyi | 146 ++------------- mypy/typeshed/stdlib/_pickle.pyi | 108 +++++++++++ mypy/typeshed/stdlib/_socket.pyi | 4 +- mypy/typeshed/stdlib/_tkinter.pyi | 37 ++-- mypy/typeshed/stdlib/argparse.pyi | 36 ++-- mypy/typeshed/stdlib/codeop.pyi | 6 +- .../stdlib/concurrent/futures/process.pyi | 14 +- mypy/typeshed/stdlib/gzip.pyi | 10 +- .../stdlib/multiprocessing/reduction.pyi | 2 +- mypy/typeshed/stdlib/pickle.pyi | 137 +++++--------- mypy/typeshed/stdlib/pickletools.pyi | 9 +- mypy/typeshed/stdlib/select.pyi | 6 +- mypy/typeshed/stdlib/selectors.pyi | 2 +- mypy/typeshed/stdlib/socket.pyi | 10 +- mypy/typeshed/stdlib/sys/__init__.pyi | 2 +- mypy/typeshed/stdlib/tarfile.pyi | 30 ++- mypy/typeshed/stdlib/tkinter/__init__.pyi | 171 +++++++++++++++++- mypy/typeshed/stdlib/token.pyi | 2 +- mypy/typeshed/stdlib/tokenize.pyi | 2 +- mypy/typeshed/stdlib/typing.pyi | 2 +- mypy/typeshed/stdlib/weakref.pyi | 8 +- mypy/typeshed/stdlib/xml/sax/expatreader.pyi | 53 ++++++ .../zipfile/{_path.pyi => _path/__init__.pyi} | 0 mypy/typeshed/stdlib/zipfile/_path/glob.pyi | 22 +++ 25 files changed, 543 insertions(+), 277 deletions(-) create mode 100644 mypy/typeshed/stdlib/_pickle.pyi create mode 100644 mypy/typeshed/stdlib/xml/sax/expatreader.pyi rename mypy/typeshed/stdlib/zipfile/{_path.pyi => _path/__init__.pyi} (100%) create mode 100644 mypy/typeshed/stdlib/zipfile/_path/glob.pyi diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 7ff14c55d3a8..3c6898dc1a77 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -57,6 +57,7 @@ _msi: 3.0-3.12 _multibytecodec: 3.0- _operator: 3.4- _osx_support: 3.0- +_pickle: 3.0- _posixsubprocess: 3.2- _py_abc: 3.7- _pydecimal: 3.5- diff --git a/mypy/typeshed/stdlib/_dummy_threading.pyi b/mypy/typeshed/stdlib/_dummy_threading.pyi index 21d1d1921c0e..1b66fb414d7a 100644 --- a/mypy/typeshed/stdlib/_dummy_threading.pyi +++ b/mypy/typeshed/stdlib/_dummy_threading.pyi @@ -1,11 +1,23 @@ -import sys -from _thread import _excepthook, _ExceptHookArgs +from _threading_local import local as local from _typeshed import ProfileFunction, TraceFunction -from collections.abc import Callable, Iterable, Mapping -from types import TracebackType -from typing import Any, TypeVar - -_T = TypeVar("_T") +from threading import ( + TIMEOUT_MAX as TIMEOUT_MAX, + Barrier as Barrier, + BoundedSemaphore as BoundedSemaphore, + BrokenBarrierError as BrokenBarrierError, + Condition as Condition, + Event as Event, + ExceptHookArgs as ExceptHookArgs, + Lock as Lock, + RLock as RLock, + Semaphore as Semaphore, + Thread as Thread, + ThreadError as ThreadError, + Timer as Timer, + _DummyThread as _DummyThread, + _RLock as _RLock, + excepthook as excepthook, +) __all__ = [ "get_ident", @@ -42,123 +54,3 @@ def main_thread() -> Thread: ... def settrace(func: TraceFunction) -> None: ... def setprofile(func: ProfileFunction | None) -> None: ... def stack_size(size: int | None = None) -> int: ... - -TIMEOUT_MAX: float - -class ThreadError(Exception): ... - -class local: - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - -class Thread: - name: str - daemon: bool - @property - def ident(self) -> int | None: ... - def __init__( - self, - group: None = None, - target: Callable[..., object] | None = None, - name: str | None = None, - args: Iterable[Any] = (), - kwargs: Mapping[str, Any] | None = None, - *, - daemon: bool | None = None, - ) -> None: ... - def start(self) -> None: ... - def run(self) -> None: ... - def join(self, timeout: float | None = None) -> None: ... - def getName(self) -> str: ... - def setName(self, name: str) -> None: ... - @property - def native_id(self) -> int | None: ... # only available on some platforms - def is_alive(self) -> bool: ... - if sys.version_info < (3, 9): - def isAlive(self) -> bool: ... - - def isDaemon(self) -> bool: ... - def setDaemon(self, daemonic: bool) -> None: ... - -class _DummyThread(Thread): ... - -class Lock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def locked(self) -> bool: ... - -class _RLock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... - def release(self) -> None: ... - -RLock = _RLock - -class Condition: - def __init__(self, lock: Lock | _RLock | None = None) -> None: ... - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def wait(self, timeout: float | None = None) -> bool: ... - def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ... - def notify(self, n: int = 1) -> None: ... - def notify_all(self) -> None: ... - def notifyAll(self) -> None: ... - -class Semaphore: - def __init__(self, value: int = 1) -> None: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ... - def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ... - if sys.version_info >= (3, 9): - def release(self, n: int = ...) -> None: ... - else: - def release(self) -> None: ... - -class BoundedSemaphore(Semaphore): ... - -class Event: - def is_set(self) -> bool: ... - def set(self) -> None: ... - def clear(self) -> None: ... - def wait(self, timeout: float | None = None) -> bool: ... - -excepthook = _excepthook -ExceptHookArgs = _ExceptHookArgs - -class Timer(Thread): - def __init__( - self, - interval: float, - function: Callable[..., object], - args: Iterable[Any] | None = None, - kwargs: Mapping[str, Any] | None = None, - ) -> None: ... - def cancel(self) -> None: ... - -class Barrier: - @property - def parties(self) -> int: ... - @property - def n_waiting(self) -> int: ... - @property - def broken(self) -> bool: ... - def __init__(self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None) -> None: ... - def wait(self, timeout: float | None = None) -> int: ... - def reset(self) -> None: ... - def abort(self) -> None: ... - -class BrokenBarrierError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi new file mode 100644 index 000000000000..5566f0f65d6e --- /dev/null +++ b/mypy/typeshed/stdlib/_pickle.pyi @@ -0,0 +1,108 @@ +import sys +from _typeshed import ReadableBuffer, SupportsWrite +from collections.abc import Callable, Iterable, Iterator, Mapping +from pickle import PickleBuffer as PickleBuffer +from typing import Any, Protocol, type_check_only +from typing_extensions import TypeAlias + +class _ReadableFileobj(Protocol): + def read(self, n: int, /) -> bytes: ... + def readline(self) -> bytes: ... + +_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None + +_ReducedType: TypeAlias = ( + str + | tuple[Callable[..., Any], tuple[Any, ...]] + | tuple[Callable[..., Any], tuple[Any, ...], Any] + | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None] + | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None, Iterator[Any] | None] +) + +def dump( + obj: Any, + file: SupportsWrite[bytes], + protocol: int | None = None, + *, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, +) -> None: ... +def dumps( + obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None +) -> bytes: ... +def load( + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... +def loads( + data: ReadableBuffer, + /, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... + +class PickleError(Exception): ... +class PicklingError(PickleError): ... +class UnpicklingError(PickleError): ... + +@type_check_only +class PicklerMemoProxy: + def clear(self, /) -> None: ... + def copy(self, /) -> dict[int, tuple[int, Any]]: ... + +class Pickler: + fast: bool + dispatch_table: Mapping[type, Callable[[Any], _ReducedType]] + reducer_override: Callable[[Any], Any] + bin: bool # undocumented + def __init__( + self, + file: SupportsWrite[bytes], + protocol: int | None = None, + *, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, + ) -> None: ... + @property + def memo(self) -> PicklerMemoProxy: ... + @memo.setter + def memo(self, value: PicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ... + def dump(self, obj: Any, /) -> None: ... + def clear_memo(self) -> None: ... + if sys.version_info >= (3, 13): + def persistent_id(self, obj: Any, /) -> Any: ... + else: + persistent_id: Callable[[Any], Any] + +@type_check_only +class UnpicklerMemoProxy: + def clear(self, /) -> None: ... + def copy(self, /) -> dict[int, tuple[int, Any]]: ... + +class Unpickler: + def __init__( + self, + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), + ) -> None: ... + @property + def memo(self) -> UnpicklerMemoProxy: ... + @memo.setter + def memo(self, value: UnpicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ... + def load(self) -> Any: ... + def find_class(self, module_name: str, global_name: str, /) -> Any: ... + if sys.version_info >= (3, 13): + def persistent_load(self, pid: Any, /) -> Any: ... + else: + persistent_load: Callable[[Any], Any] diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 36bc5c31c646..4cf71cbcadfa 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -78,8 +78,10 @@ if sys.platform == "win32": SO_EXCLUSIVEADDRUSE: int if sys.platform != "win32": SO_REUSEPORT: int + if sys.platform != "darwin" or sys.version_info >= (3, 13): + SO_BINDTODEVICE: int + if sys.platform != "win32" and sys.platform != "darwin": - SO_BINDTODEVICE: int SO_DOMAIN: int SO_MARK: int SO_PASSCRED: int diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index 63b1e7ca7cb4..4206a2114f95 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -113,16 +113,31 @@ TK_VERSION: Final[str] class TkttType: def deletetimerhandler(self): ... -def create( - screenName: str | None = None, - baseName: str = "", - className: str = "Tk", - interactive: bool = False, - wantobjects: bool = False, - wantTk: bool = True, - sync: bool = False, - use: str | None = None, - /, -): ... +if sys.version_info >= (3, 13): + def create( + screenName: str | None = None, + baseName: str = "", + className: str = "Tk", + interactive: bool = False, + wantobjects: int = 0, + wantTk: bool = True, + sync: bool = False, + use: str | None = None, + /, + ): ... + +else: + def create( + screenName: str | None = None, + baseName: str = "", + className: str = "Tk", + interactive: bool = False, + wantobjects: bool = False, + wantTk: bool = True, + sync: bool = False, + use: str | None = None, + /, + ): ... + def getbusywaitinterval(): ... def setbusywaitinterval(new_val, /): ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 2526322ac8f6..365617077f09 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -182,30 +182,30 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def add_subparsers( self: _ArgumentParserT, *, - title: str = ..., - description: str | None = ..., - prog: str = ..., + title: str = "subcommands", + description: str | None = None, + prog: str | None = None, action: type[Action] = ..., option_string: str = ..., - dest: str | None = ..., - required: bool = ..., - help: str | None = ..., - metavar: str | None = ..., + dest: str | None = None, + required: bool = False, + help: str | None = None, + metavar: str | None = None, ) -> _SubParsersAction[_ArgumentParserT]: ... @overload def add_subparsers( self, *, - title: str = ..., - description: str | None = ..., - prog: str = ..., + title: str = "subcommands", + description: str | None = None, + prog: str | None = None, parser_class: type[_ArgumentParserT], action: type[Action] = ..., option_string: str = ..., - dest: str | None = ..., - required: bool = ..., - help: str | None = ..., - metavar: str | None = ..., + dest: str | None = None, + required: bool = False, + help: str | None = None, + metavar: str | None = None, ) -> _SubParsersAction[_ArgumentParserT]: ... def print_usage(self, file: IO[str] | None = None) -> None: ... def print_help(self, file: IO[str] | None = None) -> None: ... @@ -237,7 +237,13 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): # undocumented def _get_optional_actions(self) -> list[Action]: ... def _get_positional_actions(self) -> list[Action]: ... - def _parse_known_args(self, arg_strings: list[str], namespace: Namespace) -> tuple[Namespace, list[str]]: ... + if sys.version_info >= (3, 12): + def _parse_known_args( + self, arg_strings: list[str], namespace: Namespace, intermixed: bool + ) -> tuple[Namespace, list[str]]: ... + else: + def _parse_known_args(self, arg_strings: list[str], namespace: Namespace) -> tuple[Namespace, list[str]]: ... + def _read_args_from_files(self, arg_strings: list[str]) -> list[str]: ... def _match_argument(self, action: Action, arg_strings_pattern: str) -> int: ... def _match_arguments_partial(self, actions: Sequence[Action], arg_strings_pattern: str) -> list[int]: ... diff --git a/mypy/typeshed/stdlib/codeop.pyi b/mypy/typeshed/stdlib/codeop.pyi index 6a51b7786384..cfe52e9b35de 100644 --- a/mypy/typeshed/stdlib/codeop.pyi +++ b/mypy/typeshed/stdlib/codeop.pyi @@ -1,3 +1,4 @@ +import sys from types import CodeType __all__ = ["compile_command", "Compile", "CommandCompiler"] @@ -6,7 +7,10 @@ def compile_command(source: str, filename: str = "", symbol: str = "singl class Compile: flags: int - def __call__(self, source: str, filename: str, symbol: str) -> CodeType: ... + if sys.version_info >= (3, 13): + def __call__(self, source: str, filename: str, symbol: str, flags: int = 0) -> CodeType: ... + else: + def __call__(self, source: str, filename: str, symbol: str) -> CodeType: ... class CommandCompiler: compiler: Compile diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index a1de3d679b23..97dc261be7ed 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -72,9 +72,19 @@ class _CallItem: class _SafeQueue(Queue[Future[Any]]): pending_work_items: dict[int, _WorkItem[Any]] - shutdown_lock: Lock + if sys.version_info < (3, 12): + shutdown_lock: Lock thread_wakeup: _ThreadWakeup - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 12): + def __init__( + self, + max_size: int | None = 0, + *, + ctx: BaseContext, + pending_work_items: dict[int, _WorkItem[Any]], + thread_wakeup: _ThreadWakeup, + ) -> None: ... + elif sys.version_info >= (3, 9): def __init__( self, max_size: int | None = 0, diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 9b32008dcbf6..b7fb40fbd82e 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -2,8 +2,8 @@ import _compression import sys import zlib from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath -from io import FileIO -from typing import Final, Literal, Protocol, TextIO, overload +from io import FileIO, TextIOWrapper +from typing import Final, Literal, Protocol, overload from typing_extensions import TypeAlias __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] @@ -57,13 +57,13 @@ def open( ) -> GzipFile: ... @overload def open( - filename: StrOrBytesPath, + filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj, mode: _OpenTextMode, compresslevel: int = 9, encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj, @@ -72,7 +72,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> GzipFile | TextIO: ... +) -> GzipFile | TextIOWrapper: ... class _PaddedFile: file: _ReadableFileobj diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index 473e90936d71..942e92ce530e 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -1,12 +1,12 @@ import pickle import sys +from _pickle import _ReducedType from _typeshed import HasFileno, SupportsWrite, Unused from abc import ABCMeta from builtins import type as Type # alias to avoid name clash from collections.abc import Callable from copyreg import _DispatchTableType from multiprocessing import connection -from pickle import _ReducedType from socket import socket from typing import Any, Final diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 9bea92ef1c9e..5e398f2d4921 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -1,7 +1,20 @@ +from _pickle import ( + PickleError as PickleError, + Pickler as Pickler, + PicklingError as PicklingError, + Unpickler as Unpickler, + UnpicklingError as UnpicklingError, + _BufferCallback, + _ReadableFileobj, + _ReducedType, + dump as dump, + dumps as dumps, + load as load, + loads as loads, +) from _typeshed import ReadableBuffer, SupportsWrite -from collections.abc import Callable, Iterable, Iterator, Mapping -from typing import Any, ClassVar, Protocol, SupportsBytes, SupportsIndex, final -from typing_extensions import TypeAlias +from collections.abc import Callable, Iterable, Mapping +from typing import Any, ClassVar, SupportsBytes, SupportsIndex, final __all__ = [ "PickleBuffer", @@ -93,10 +106,6 @@ DEFAULT_PROTOCOL: int bytes_types: tuple[type[Any], ...] # undocumented -class _ReadableFileobj(Protocol): - def read(self, n: int, /) -> bytes: ... - def readline(self) -> bytes: ... - @final class PickleBuffer: def __init__(self, buffer: ReadableBuffer) -> None: ... @@ -105,84 +114,6 @@ class PickleBuffer: def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... -_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None - -def dump( - obj: Any, - file: SupportsWrite[bytes], - protocol: int | None = None, - *, - fix_imports: bool = True, - buffer_callback: _BufferCallback = None, -) -> None: ... -def dumps( - obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None -) -> bytes: ... -def load( - file: _ReadableFileobj, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), -) -> Any: ... -def loads( - data: ReadableBuffer, - /, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), -) -> Any: ... - -class PickleError(Exception): ... -class PicklingError(PickleError): ... -class UnpicklingError(PickleError): ... - -_ReducedType: TypeAlias = ( - str - | tuple[Callable[..., Any], tuple[Any, ...]] - | tuple[Callable[..., Any], tuple[Any, ...], Any] - | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None] - | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None, Iterator[Any] | None] -) - -class Pickler: - fast: bool - dispatch_table: Mapping[type, Callable[[Any], _ReducedType]] - bin: bool # undocumented - dispatch: ClassVar[dict[type, Callable[[Unpickler, Any], None]]] # undocumented, _Pickler only - - def __init__( - self, - file: SupportsWrite[bytes], - protocol: int | None = None, - *, - fix_imports: bool = True, - buffer_callback: _BufferCallback = None, - ) -> None: ... - def reducer_override(self, obj: Any) -> Any: ... - def dump(self, obj: Any, /) -> None: ... - def clear_memo(self) -> None: ... - def persistent_id(self, obj: Any) -> Any: ... - -class Unpickler: - dispatch: ClassVar[dict[int, Callable[[Unpickler], None]]] # undocumented, _Unpickler only - - def __init__( - self, - file: _ReadableFileobj, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), - ) -> None: ... - def load(self) -> Any: ... - def find_class(self, module_name: str, global_name: str, /) -> Any: ... - def persistent_load(self, pid: Any) -> Any: ... - MARK: bytes STOP: bytes POP: bytes @@ -266,6 +197,36 @@ READONLY_BUFFER: bytes def encode_long(x: int) -> bytes: ... # undocumented def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ... # undocumented -# pure-Python implementations -_Pickler = Pickler # undocumented -_Unpickler = Unpickler # undocumented +# undocumented pure-Python implementations +class _Pickler: + fast: bool + dispatch_table: Mapping[type, Callable[[Any], _ReducedType]] + bin: bool # undocumented + dispatch: ClassVar[dict[type, Callable[[Unpickler, Any], None]]] # undocumented, _Pickler only + reducer_override: Callable[[Any], Any] + def __init__( + self, + file: SupportsWrite[bytes], + protocol: int | None = None, + *, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, + ) -> None: ... + def dump(self, obj: Any) -> None: ... + def clear_memo(self) -> None: ... + def persistent_id(self, obj: Any) -> Any: ... + +class _Unpickler: + dispatch: ClassVar[dict[int, Callable[[Unpickler], None]]] # undocumented, _Unpickler only + def __init__( + self, + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = None, + ) -> None: ... + def load(self) -> Any: ... + def find_class(self, module: str, name: str) -> Any: ... + def persistent_load(self, pid: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/pickletools.pyi b/mypy/typeshed/stdlib/pickletools.pyi index 542172814926..cdade08d39a8 100644 --- a/mypy/typeshed/stdlib/pickletools.pyi +++ b/mypy/typeshed/stdlib/pickletools.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Callable, Iterator, MutableMapping from typing import IO, Any from typing_extensions import TypeAlias @@ -40,7 +41,13 @@ def read_uint8(f: IO[bytes]) -> int: ... uint8: ArgumentDescriptor -def read_stringnl(f: IO[bytes], decode: bool = True, stripquotes: bool = True) -> bytes | str: ... +if sys.version_info >= (3, 12): + def read_stringnl( + f: IO[bytes], decode: bool = True, stripquotes: bool = True, *, encoding: str = "latin-1" + ) -> bytes | str: ... + +else: + def read_stringnl(f: IO[bytes], decode: bool = True, stripquotes: bool = True) -> bytes | str: ... stringnl: ArgumentDescriptor diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index 6d4c8d8f4c15..67203905ab66 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -10,7 +10,8 @@ if sys.platform != "win32": POLLERR: int POLLHUP: int POLLIN: int - POLLMSG: int + if sys.platform == "linux": + POLLMSG: int POLLNVAL: int POLLOUT: int POLLPRI: int @@ -77,7 +78,8 @@ if sys.platform != "linux" and sys.platform != "win32": KQ_EV_ONESHOT: int KQ_EV_SYSFLAGS: int KQ_FILTER_AIO: int - KQ_FILTER_NETDEV: int + if sys.platform != "darwin": + KQ_FILTER_NETDEV: int KQ_FILTER_PROC: int KQ_FILTER_READ: int KQ_FILTER_SIGNAL: int diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index a857d0e242ab..7dad0c13bf2a 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -53,7 +53,7 @@ if sys.platform == "linux": class DevpollSelector(_PollLikeSelector): def fileno(self) -> int: ... -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": class KqueueSelector(_BaseSelectorImpl): def fileno(self) -> int: ... def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index e42bba757fc3..ab22cced0bb5 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -367,7 +367,6 @@ if sys.platform != "win32" and sys.platform != "darwin": IP_TRANSPARENT as IP_TRANSPARENT, IPX_TYPE as IPX_TYPE, SCM_CREDENTIALS as SCM_CREDENTIALS, - SO_BINDTODEVICE as SO_BINDTODEVICE, SO_DOMAIN as SO_DOMAIN, SO_MARK as SO_MARK, SO_PASSCRED as SO_PASSCRED, @@ -396,7 +395,6 @@ if sys.platform != "win32" and sys.platform != "darwin": __all__ += [ "IP_TRANSPARENT", "SCM_CREDENTIALS", - "SO_BINDTODEVICE", "SO_DOMAIN", "SO_MARK", "SO_PASSCRED", @@ -517,6 +515,11 @@ if sys.platform != "win32": "IPV6_RTHDRDSTOPTS", ] + if sys.platform != "darwin" or sys.version_info >= (3, 13): + from _socket import SO_BINDTODEVICE as SO_BINDTODEVICE + + __all__ += ["SO_BINDTODEVICE"] + if sys.platform != "darwin" and sys.platform != "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM @@ -1046,7 +1049,6 @@ class AddressFamily(IntEnum): AF_INET = 2 AF_INET6 = 10 AF_APPLETALK = 5 - AF_DECnet = ... AF_IPX = 4 AF_SNA = 22 AF_UNSPEC = 0 @@ -1096,7 +1098,7 @@ class AddressFamily(IntEnum): AF_INET = AddressFamily.AF_INET AF_INET6 = AddressFamily.AF_INET6 AF_APPLETALK = AddressFamily.AF_APPLETALK -AF_DECnet = AddressFamily.AF_DECnet +AF_DECnet: Literal[12] AF_IPX = AddressFamily.AF_IPX AF_SNA = AddressFamily.AF_SNA AF_UNSPEC = AddressFamily.AF_UNSPEC diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index c4b1adca9bc6..fb1e24f3e864 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -73,7 +73,7 @@ if sys.version_info >= (3, 10): __stdin__: Final[TextIOWrapper | None] # Contains the original value of stdin __stdout__: Final[TextIOWrapper | None] # Contains the original value of stdout __stderr__: Final[TextIOWrapper | None] # Contains the original value of stderr -tracebacklimit: int +tracebacklimit: int | None version: str api_version: int warnoptions: Any diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index a7135d8150ee..a717c280a423 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -1,7 +1,7 @@ import bz2 import io import sys -from _typeshed import StrOrBytesPath, StrPath, SupportsRead +from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath, SupportsRead, WriteableBuffer from builtins import list as _list # aliases to avoid name clashes with fields named "type" or "list" from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj @@ -226,15 +226,29 @@ def open( errorlevel: int | None = ..., preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., ) -> TarFile: ... - -# TODO: Temporary fallback for modes containing pipe characters. These don't -# work with mypy 1.10, but this should be fixed with mypy 1.11. -# https://github.com/python/typeshed/issues/12182 @overload def open( - name: StrOrBytesPath | None = None, + name: StrOrBytesPath | ReadableBuffer | None = None, + *, + mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"], + fileobj: IO[bytes] | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | WriteableBuffer | None = None, *, - mode: str, + mode: Literal["w|", "w|gz", "w|bz2", "w|xz"], fileobj: IO[bytes] | None = None, bufsize: int = 10240, format: int | None = ..., @@ -557,7 +571,7 @@ class TarInfo: self, *, name: str = ..., - mtime: int = ..., + mtime: float = ..., mode: int = ..., linkname: str = ..., uid: int = ..., diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index d6a234d67919..a9ec97c45b40 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -403,6 +403,9 @@ class Misc: # after_idle is essentially partialmethod(after, "idle") def after_idle(self, func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> str: ... def after_cancel(self, id: str) -> None: ... + if sys.version_info >= (3, 13): + def after_info(self, id: str | None = None) -> tuple[str, ...]: ... + def bell(self, displayof: Literal[0] | Misc | None = 0) -> None: ... def clipboard_get(self, *, displayof: Misc = ..., type: str = ...) -> str: ... def clipboard_clear(self, *, displayof: Misc = ...) -> None: ... @@ -659,6 +662,38 @@ class YView: @overload def yview_scroll(self, number: _ScreenUnits, what: Literal["pixels"]) -> None: ... +if sys.platform == "darwin": + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + fullscreen: bool + modified: bool + notify: bool + titlepath: str + topmost: bool + transparent: bool + type: str # Present, but not actually used on darwin + +elif sys.platform == "win32": + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + transparentcolor: str + disabled: bool + fullscreen: bool + toolwindow: bool + topmost: bool + +else: + # X11 + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + topmost: bool + zoomed: bool + fullscreen: bool + type: str + class Wm: @overload def wm_aspect(self, minNumer: int, minDenom: int, maxNumer: int, maxDenom: int) -> None: ... @@ -667,12 +702,144 @@ class Wm: self, minNumer: None = None, minDenom: None = None, maxNumer: None = None, maxDenom: None = None ) -> tuple[int, int, int, int] | None: ... aspect = wm_aspect + if sys.version_info >= (3, 13): + @overload + def wm_attributes(self, *, return_python_dict: Literal[False] = False) -> tuple[Any, ...]: ... + @overload + def wm_attributes(self, *, return_python_dict: Literal[True]) -> _WmAttributes: ... + + else: + @overload + def wm_attributes(self) -> tuple[Any, ...]: ... + + @overload + def wm_attributes(self, option: Literal["-alpha"], /) -> float: ... @overload - def wm_attributes(self) -> tuple[Any, ...]: ... + def wm_attributes(self, option: Literal["-fullscreen"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-topmost"], /) -> bool: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["-modified"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-notify"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-titlepath"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["-transparent"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-type"], /) -> str: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["-transparentcolor"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["-disabled"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-toolwindow"], /) -> bool: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["-zoomed"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-type"], /) -> str: ... + if sys.version_info >= (3, 13): + @overload + def wm_attributes(self, option: Literal["alpha"], /) -> float: ... + @overload + def wm_attributes(self, option: Literal["fullscreen"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["topmost"], /) -> bool: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["modified"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["notify"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["titlepath"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["transparent"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["type"], /) -> str: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["transparentcolor"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["disabled"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["toolwindow"], /) -> bool: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["zoomed"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["type"], /) -> str: ... + @overload def wm_attributes(self, option: str, /): ... @overload - def wm_attributes(self, option: str, value, /, *__other_option_value_pairs: Any) -> None: ... + def wm_attributes(self, option: Literal["-alpha"], value: float, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-fullscreen"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-topmost"], value: bool, /) -> Literal[""]: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["-modified"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-notify"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-titlepath"], value: str, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-transparent"], value: bool, /) -> Literal[""]: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["-transparentcolor"], value: str, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-disabled"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-toolwindow"], value: bool, /) -> Literal[""]: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["-zoomed"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-type"], value: str, /) -> Literal[""]: ... + + @overload + def wm_attributes(self, option: str, value, /, *__other_option_value_pairs: Any) -> Literal[""]: ... + if sys.version_info >= (3, 13): + if sys.platform == "darwin": + @overload + def wm_attributes( + self, + *, + alpha: float = ..., + fullscreen: bool = ..., + modified: bool = ..., + notify: bool = ..., + titlepath: str = ..., + topmost: bool = ..., + transparent: bool = ..., + ) -> None: ... + elif sys.platform == "win32": + @overload + def wm_attributes( + self, + *, + alpha: float = ..., + transparentcolor: str = ..., + disabled: bool = ..., + fullscreen: bool = ..., + toolwindow: bool = ..., + topmost: bool = ..., + ) -> None: ... + else: + # X11 + @overload + def wm_attributes( + self, *, alpha: float = ..., topmost: bool = ..., zoomed: bool = ..., fullscreen: bool = ..., type: str = ... + ) -> None: ... + attributes = wm_attributes def wm_client(self, name: str | None = None) -> str: ... client = wm_client diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi index 668987d7c2bf..741ce5b035b7 100644 --- a/mypy/typeshed/stdlib/token.pyi +++ b/mypy/typeshed/stdlib/token.pyi @@ -76,7 +76,7 @@ if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] if sys.version_info >= (3, 12): - __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START", "EXACT_TOKEN_TYPES"] ENDMARKER: int NAME: int diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 7e9a945cdc46..7b68f791a8c0 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -88,7 +88,7 @@ if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] if sys.version_info >= (3, 12): - __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START", "EXACT_TOKEN_TYPES"] if sys.version_info >= (3, 13): __all__ += ["TokenError", "open"] diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 8f0d4fbb6a02..741e7b8a3167 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -757,7 +757,7 @@ class MutableMapping(Mapping[_KT, _VT]): Text = str -TYPE_CHECKING: bool +TYPE_CHECKING: Final[bool] # In stubs, the arguments of the IO class are marked as positional-only. # This differs from runtime, but better reflects the fact that in reality diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 853caf3e8abb..4203756c718d 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -172,11 +172,11 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): @overload def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... -class finalize: # TODO: This is a good candidate for to be a `Generic[_P, _T]` class - def __init__(self, obj: object, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... +class finalize(Generic[_P, _T]): + def __init__(self, obj: _T, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def __call__(self, _: Any = None) -> Any | None: ... - def detach(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... - def peek(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... + def detach(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ... + def peek(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ... @property def alive(self) -> bool: ... atexit: bool diff --git a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi new file mode 100644 index 000000000000..0f7bda5872c0 --- /dev/null +++ b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi @@ -0,0 +1,53 @@ +import sys +from _typeshed import Unused +from xml.sax import xmlreader + +version: str +AttributesImpl = xmlreader.AttributesImpl +AttributesNSImpl = xmlreader.AttributesNSImpl + +class _ClosedParser: ... + +class ExpatLocator(xmlreader.Locator): + def __init__(self, parser: ExpatParser) -> None: ... + def getColumnNumber(self) -> int: ... + def getLineNumber(self) -> int: ... + def getPublicId(self): ... + def getSystemId(self): ... + +class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): + def __init__(self, namespaceHandling: int = 0, bufsize: int = 65516) -> None: ... + def parse(self, source) -> None: ... + def prepareParser(self, source) -> None: ... + def setContentHandler(self, handler) -> None: ... + def getFeature(self, name: str): ... + def setFeature(self, name: str, state) -> None: ... + def getProperty(self, name: str): ... + def setProperty(self, name: str, value) -> None: ... + if sys.version_info >= (3, 9): + def feed(self, data, isFinal: bool = False) -> None: ... + else: + def feed(self, data, isFinal: int = 0) -> None: ... + + def flush(self) -> None: ... + def close(self) -> None: ... + def reset(self) -> None: ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int: ... + def getPublicId(self): ... + def getSystemId(self): ... + def start_element(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def end_element(self, name: str) -> None: ... + def start_element_ns(self, name: str, attrs) -> None: ... + def end_element_ns(self, name: str) -> None: ... + def processing_instruction(self, target: str, data: str) -> None: ... + def character_data(self, data: str) -> None: ... + def start_namespace_decl(self, prefix: str | None, uri: str) -> None: ... + def end_namespace_decl(self, prefix: str | None) -> None: ... + def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: Unused) -> None: ... + def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name) -> None: ... + def notation_decl(self, name, base, sysid, pubid) -> None: ... + def external_entity_ref(self, context, base, sysid, pubid): ... + def skipped_entity_handler(self, name: str, is_pe: bool) -> None: ... + +def create_parser(namespaceHandling: int = 0, bufsize: int = 65516) -> ExpatParser: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi similarity index 100% rename from mypy/typeshed/stdlib/zipfile/_path.pyi rename to mypy/typeshed/stdlib/zipfile/_path/__init__.pyi diff --git a/mypy/typeshed/stdlib/zipfile/_path/glob.pyi b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi new file mode 100644 index 000000000000..f25ae71725c0 --- /dev/null +++ b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi @@ -0,0 +1,22 @@ +import sys +from collections.abc import Iterator +from re import Match + +if sys.version_info >= (3, 13): + class Translator: + def __init__(self, seps: str = ...) -> None: ... + def translate(self, pattern: str) -> str: ... + def extend(self, pattern: str) -> str: ... + def match_dirs(self, pattern: str) -> str: ... + def translate_core(self, pattern: str) -> str: ... + def replace(self, match: Match[str]) -> str: ... + def restrict_rglob(self, pattern: str) -> None: ... + def star_not_empty(self, pattern: str) -> str: ... + +else: + def translate(pattern: str) -> str: ... + def match_dirs(pattern: str) -> str: ... + def translate_core(pattern: str) -> str: ... + def replace(match: Match[str]) -> str: ... + +def separate(pattern: str) -> Iterator[Match[str]]: ... From 802266b74e0dcce472103db158e55bb2f95e76b2 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:12:37 +1300 Subject: [PATCH 0941/1617] fix: mark mypyc package with `py.typed` (#18253) mypyc is fully typed, so it'd be nice to get rid of mypy errors to do with missing `py.typed` markers when writing `setup.py`s or similar. ```python import mypyc # E: Skipping analyzing "mypyc": module is installed, but missing library stubs or py. typed marker [import-untyped] ``` --- MANIFEST.in | 1 + mypyc/py.typed | 0 2 files changed, 1 insertion(+) create mode 100644 mypyc/py.typed diff --git a/MANIFEST.in b/MANIFEST.in index c2399d2b00b6..f9992d44e7ff 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ recursive-include mypy/typeshed *.pyi # mypy and mypyc include mypy/py.typed +include mypyc/py.typed recursive-include mypy *.py recursive-include mypyc *.py diff --git a/mypyc/py.typed b/mypyc/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 From 82de0d89e7a6f20dd2d25477abb30ae723408128 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:19:40 -0800 Subject: [PATCH 0942/1617] [mypyc] Fixing iteration over NamedTuple objects. (#18254) Fixes mypyc/mypyc#1063. Adding elif-blocks for getting RType of items in classes derived from `NamedTuple`. --- mypyc/irbuild/builder.py | 22 +++++++++++++++++----- mypyc/test-data/run-loops.test | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 1d0dd495eea5..5f79b911dda2 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -52,6 +52,7 @@ Type, TypedDictType, TypeOfAny, + TypeVarLikeType, UninhabitedType, UnionType, get_proper_type, @@ -926,11 +927,22 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType: return RUnion.make_simplified_union( [self.get_sequence_type_from_type(item) for item in target_type.items] ) - assert isinstance(target_type, Instance), target_type - if target_type.type.fullname == "builtins.str": - return str_rprimitive - else: - return self.type_to_rtype(target_type.args[0]) + elif isinstance(target_type, Instance): + if target_type.type.fullname == "builtins.str": + return str_rprimitive + else: + return self.type_to_rtype(target_type.args[0]) + # This elif-blocks are needed for iterating over classes derived from NamedTuple. + elif isinstance(target_type, TypeVarLikeType): + return self.get_sequence_type_from_type(target_type.upper_bound) + elif isinstance(target_type, TupleType): + # Tuple might have elements of different types. + rtypes = {self.mapper.type_to_rtype(item) for item in target_type.items} + if len(rtypes) == 1: + return rtypes.pop() + else: + return RUnion.make_simplified_union(list(rtypes)) + assert False, target_type def get_dict_base_type(self, expr: Expression) -> list[Instance]: """Find dict type of a dict-like expression. diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 76fbb06200a3..3cbb07297e6e 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -545,3 +545,29 @@ def test_range_object() -> None: r4 = range(4, 12, 0) except ValueError as e: assert "range() arg 3 must not be zero" in str(e) + +[case testNamedTupleLoop] +from collections.abc import Iterable +from typing import NamedTuple, Any +from typing_extensions import Self + + +class Vector2(NamedTuple): + x: int + y: float + + @classmethod + def from_iter(cls, iterable: Iterable[Any]) -> Self: + return cls(*iter(iterable)) + + def __neg__(self) -> Self: + return self.from_iter(-c for c in self) + +[file driver.py] +import native +print(-native.Vector2(2, -3.1)) +print([x for x in native.Vector2(4, -5.2)]) + +[out] +Vector2(x=-2, y=3.1) +\[4, -5.2] From 605020405b4ed898b9011fe9ae93e3428e13db84 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 6 Dec 2024 12:11:12 +0000 Subject: [PATCH 0943/1617] Allow bytearray/bytes comparisons with --disable-bytearray-promotion (#18255) Previously comparing a bytearray against a bytes literal was reported as a non-overlapping comparison when using `--strict-equality`. This was a false positive. This is in preparation for disabling bytearray to bytes promotion by default in mypy 2.0. --- mypy/checkexpr.py | 12 ++++++++++++ test-data/unit/check-flags.test | 11 ++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 549026ca89c2..76ed3892cfee 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3790,6 +3790,18 @@ def dangerous_comparison( if isinstance(left.value, bool) and isinstance(right.value, bool): # Comparing different booleans is not dangerous. return False + if isinstance(left, LiteralType) and isinstance(right, Instance): + # bytes/bytearray comparisons are supported + if left.fallback.type.fullname == "builtins.bytes" and right.type.has_base( + "builtins.bytearray" + ): + return False + if isinstance(right, LiteralType) and isinstance(left, Instance): + # bytes/bytearray comparisons are supported + if right.fallback.type.fullname == "builtins.bytes" and left.type.has_base( + "builtins.bytearray" + ): + return False return not is_overlapping_types(left, right, ignore_promotions=False) def check_method_call_by_name( diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index c6419923ebc6..c3a5f9e3bc04 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2345,10 +2345,19 @@ x: int = "" # E: Incompatible types in assignment (expression has type "str", v x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testDisableBytearrayPromotion] -# flags: --disable-bytearray-promotion +# flags: --disable-bytearray-promotion --strict-equality def f(x: bytes) -> None: ... f(bytearray(b"asdf")) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" f(memoryview(b"asdf")) +ba = bytearray(b"") +if ba == b"": + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if b"" == ba: + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if ba == bytes(): + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if bytes() == ba: + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" [builtins fixtures/primitives.pyi] [case testDisableMemoryviewPromotion] From a53cf3da199f7470290a4f1e5a6d1b9acc2705b0 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Fri, 6 Dec 2024 23:42:30 +0100 Subject: [PATCH 0944/1617] PEP 702 (@deprecated): descriptors (#18090) --- mypy/checker.py | 27 ++++++++-- mypy/checkexpr.py | 6 +-- mypy/checkmember.py | 10 +++- test-data/unit/check-deprecated.test | 78 ++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 8 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 379da3f1c0da..2edcaa6bc5c5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4426,7 +4426,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, assignment=True) 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, @@ -4493,6 +4493,12 @@ def check_member_assignment( callable_name=callable_name, ) + # Search for possible deprecations: + mx.chk.check_deprecated(dunder_set, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_set, mx.context, target=inferred_dunder_set_type, selftype=attribute_type + ) + # In the following cases, a message already will have been recorded in check_call. if (not isinstance(inferred_dunder_set_type, CallableType)) or ( len(inferred_dunder_set_type.arg_types) < 2 @@ -7674,7 +7680,7 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: return self.expr_checker.accept(node, type_context=type_context) - def check_deprecated(self, node: SymbolNode | None, context: Context) -> None: + def check_deprecated(self, node: Node | None, context: Context) -> None: """Warn if deprecated and not directly imported with a `from` statement.""" if isinstance(node, Decorator): node = node.func @@ -7687,7 +7693,7 @@ def check_deprecated(self, node: SymbolNode | None, context: Context) -> None: else: self.warn_deprecated(node, context) - def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: + def warn_deprecated(self, node: Node | None, context: Context) -> None: """Warn if deprecated.""" if isinstance(node, Decorator): node = node.func @@ -7699,6 +7705,21 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail warn(deprecated, context, code=codes.DEPRECATED) + def warn_deprecated_overload_item( + self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None + ) -> None: + """Warn if the overload item corresponding to the given callable is deprecated.""" + target = get_proper_type(target) + if isinstance(node, OverloadedFuncDef) and isinstance(target, CallableType): + for item in node.items: + if isinstance(item, Decorator) and isinstance( + candidate := item.func.type, CallableType + ): + if selftype is not None: + candidate = bind_self(candidate, selftype) + if candidate == target: + self.warn_deprecated(item.func, context) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 76ed3892cfee..adb65a126f38 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1483,10 +1483,8 @@ def check_call_expr_with_callee_type( object_type=object_type, ) proper_callee = get_proper_type(callee_type) - if isinstance(e.callee, NameExpr) and isinstance(e.callee.node, OverloadedFuncDef): - for item in e.callee.node.items: - if isinstance(item, Decorator) and (item.func.type == callee_type): - self.chk.check_deprecated(item.func, e) + if isinstance(e.callee, (NameExpr, MemberExpr)): + self.chk.warn_deprecated_overload_item(e.callee.node, e, target=callee_type) if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType): # Cache it for find_isinstance_check() if proper_callee.type_guard is not None: diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 9dc8d5475b1a..50e54ca30460 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -638,7 +638,9 @@ 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, *, assignment: bool = False +) -> Type: """Type check descriptor access. Arguments: @@ -719,6 +721,12 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: callable_name=callable_name, ) + if not assignment: + mx.chk.check_deprecated(dunder_get, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_get, mx.context, target=inferred_dunder_get_type, selftype=descriptor_type + ) + inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type) if isinstance(inferred_dunder_get_type, AnyType): # check_call failed, and will have reported an error diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 8bbb887d4567..362d8725f183 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -503,6 +503,60 @@ C().g = "x" # E: function __main__.C.g is deprecated: use g2 instead \ [builtins fixtures/property.pyi] +[case testDeprecatedDescriptor] +# flags: --enable-error-code=deprecated + +from typing import Any, Optional, Union +from typing_extensions import deprecated, overload + +@deprecated("use E1 instead") +class D1: + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D1, int]: ... + +class D2: + @deprecated("use E2.__get__ instead") + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D2, int]: ... + + @deprecated("use E2.__set__ instead") + def __set__(self, obj: C, value: int) -> None: ... + +class D3: + @overload + @deprecated("use E3.__get__ instead") + def __get__(self, obj: None, objtype: Any) -> D3: ... + @overload + @deprecated("use E3.__get__ instead") + def __get__(self, obj: C, objtype: Any) -> int: ... + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D3, int]: ... + + @overload + def __set__(self, obj: C, value: int) -> None: ... + @overload + @deprecated("use E3.__set__ instead") + def __set__(self, obj: C, value: str) -> None: ... + def __set__(self, obj: C, value: Union[int, str]) -> None: ... + +class C: + d1 = D1() # E: class __main__.D1 is deprecated: use E1 instead + d2 = D2() + d3 = D3() + +c: C +C.d1 +c.d1 +c.d1 = 1 + +C.d2 # E: function __main__.D2.__get__ is deprecated: use E2.__get__ instead +c.d2 # E: function __main__.D2.__get__ is deprecated: use E2.__get__ instead +c.d2 = 1 # E: function __main__.D2.__set__ is deprecated: use E2.__set__ instead + +C.d3 # E: overload def (self: __main__.D3, obj: None, objtype: Any) -> __main__.D3 of function __main__.D3.__get__ is deprecated: use E3.__get__ instead +c.d3 # E: overload def (self: __main__.D3, obj: __main__.C, objtype: Any) -> builtins.int of function __main__.D3.__get__ is deprecated: use E3.__get__ instead +c.d3 = 1 +c.d3 = "x" # E: overload def (self: __main__.D3, obj: __main__.C, value: builtins.str) of function __main__.D3.__set__ is deprecated: use E3.__set__ instead +[builtins fixtures/property.pyi] + + [case testDeprecatedOverloadedFunction] # flags: --enable-error-code=deprecated @@ -556,3 +610,27 @@ h(1.0) # E: No overload variant of "h" matches argument type "float" \ # N: def h(x: str) -> str [builtins fixtures/tuple.pyi] + + +[case testDeprecatedImportedOverloadedFunction] +# flags: --enable-error-code=deprecated + +import m + +m.g +m.g(1) # E: overload def (x: builtins.int) -> builtins.int of function m.g is deprecated: work with str instead +m.g("x") + +[file m.py] + +from typing import Union +from typing_extensions import deprecated, overload + +@overload +@deprecated("work with str instead") +def g(x: int) -> int: ... +@overload +def g(x: str) -> str: ... +def g(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] From d0ebee42a579b6c06422fc20a1c0af86bfd5398b Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 6 Dec 2024 23:23:02 -0800 Subject: [PATCH 0945/1617] Warn about --follow-untyped-imports (#18249) Co-authored-by: Jelle Zijlstra Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- docs/source/command_line.rst | 9 ++++++++- docs/source/config_file.rst | 12 +++++++++--- docs/source/running_mypy.rst | 35 +++++++++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 1d91625084fd..ea96e9f64790 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -168,7 +168,14 @@ imports. .. option:: --follow-untyped-imports - This flag makes mypy analyze imports without stubs or a py.typed marker. + This flag makes mypy analyze imports from installed packages even if + missing a :ref:`py.typed marker or stubs `. + + .. warning:: + + Note that analyzing all unannotated modules might result in issues + when analyzing code not designed to be type checked and may significantly + increase how long mypy takes to run. .. option:: --follow-imports {normal,silent,skip,error} diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index e970c23a9ecb..d7ae1b7a00df 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -320,12 +320,18 @@ section of the command line docs. :type: boolean :default: False - Typechecks imports from modules that do not have stubs or a py.typed marker. + Makes mypy analyze imports from installed packages even if missing a + :ref:`py.typed marker or stubs `. If this option is used in a per-module section, the module name should match the name of the *imported* module, not the module containing the - import statement. Note that scanning all unannotated modules might - significantly increase the runtime of your mypy calls. + import statement. + + .. warning:: + + Note that analyzing all unannotated modules might result in issues + when analyzing code not designed to be type checked and may significantly + increase how long mypy takes to run. .. confval:: follow_imports diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 91fe525c46e0..ff042b395e99 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -277,6 +277,25 @@ If you are getting this error, try to obtain type hints for the library you're u to the library -- see our documentation on creating :ref:`PEP 561 compliant packages `. +4. Force mypy to analyze the library as best as it can (as if the library provided + a ``py.typed`` file), despite it likely missing any type annotations. In general, + the quality of type checking will be poor and mypy may have issues when + analyzing code not designed to be type checked. + + You can do this via setting the + :option:`--follow-untyped-imports ` + command line flag or :confval:`follow_untyped_imports` config file option to True. + This option can be specified on a per-module basis as well:: + + # mypy.ini + [mypy-untyped_package.*] + follow_untyped_imports = True + + # pyproject.toml + [[tool.mypy.overrides]] + module = ["untyped_package.*"] + follow_untyped_imports = true + If you are unable to find any existing type hints nor have time to write your own, you can instead *suppress* the errors. @@ -295,9 +314,15 @@ not catch errors in its use. all import errors associated with that library and that library alone by adding the following section to your config file:: + # mypy.ini [mypy-foobar.*] ignore_missing_imports = True + # pyproject.toml + [[tool.mypy.overrides]] + module = ["foobar.*"] + ignore_missing_imports = true + Note: this option is equivalent to adding a ``# type: ignore`` to every import of ``foobar`` in your codebase. For more information, see the documentation about configuring @@ -311,9 +336,13 @@ not catch errors in its use. You can also set :confval:`disable_error_code`, like so:: + # mypy.ini [mypy] disable_error_code = import-untyped + # pyproject.toml + [tool.mypy] + disable_error_code = ["import-untyped"] You can also set the :option:`--ignore-missing-imports ` command line flag or set the :confval:`ignore_missing_imports` config file @@ -321,12 +350,6 @@ not catch errors in its use. recommend avoiding ``--ignore-missing-imports`` if possible: it's equivalent to adding a ``# type: ignore`` to all unresolved imports in your codebase. -4. To make mypy typecheck imports from modules without stubs or a py.typed - marker, you can set the :option:`--follow-untyped-imports ` - command line flag or set the :confval:`follow_untyped_imports` config file option to True, - either in the global section of your mypy config file, or individually on a - per-module basis. - Library stubs not installed --------------------------- From f7a7ed7be911bc3a7d12b4af8b04a5e09abb3f54 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 7 Dec 2024 01:22:34 -0800 Subject: [PATCH 0946/1617] Minor README improvements (#18260) --- README.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 07c170d46cb3..45b71c8a4824 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Got a question? We are always happy to answer questions! Here are some good places to ask them: -- for anything you're curious about, try [gitter chat](https://gitter.im/python/typing) - for general questions about Python typing, try [typing discussions](https://github.com/python/typing/discussions) +- for anything you're curious about, try [gitter chat](https://gitter.im/python/typing) If you're just getting started, [the documentation](https://mypy.readthedocs.io/en/stable/index.html) @@ -30,7 +30,6 @@ If you think you've found a bug: - check our [common issues page](https://mypy.readthedocs.io/en/stable/common_issues.html) - search our [issue tracker](https://github.com/python/mypy/issues) to see if it's already been reported -- consider asking on [gitter chat](https://gitter.im/python/typing) To report a bug or request an enhancement: @@ -101,8 +100,6 @@ repo directly: ```bash python3 -m pip install -U git+https://github.com/python/mypy.git -# or if you don't have 'git' installed -python3 -m pip install -U https://github.com/python/mypy/zipball/master ``` Now you can type-check the [statically typed parts] of a program like this: @@ -118,14 +115,16 @@ programs, even if mypy reports type errors: python3 PROGRAM ``` -You can also try mypy in an [online playground](https://mypy-play.net/) (developed by -Yusuke Miyazaki). If you are working with large code bases, you can run mypy in +If you are working with large code bases, you can run mypy in [daemon mode], that will give much faster (often sub-second) incremental updates: ```bash dmypy run -- PROGRAM ``` +You can also try mypy in an [online playground](https://mypy-play.net/) (developed by +Yusuke Miyazaki). + [statically typed parts]: https://mypy.readthedocs.io/en/latest/getting_started.html#function-signatures-and-dynamic-vs-static-typing [daemon mode]: https://mypy.readthedocs.io/en/stable/mypy_daemon.html @@ -134,6 +133,7 @@ Integrations Mypy can be integrated into popular IDEs: +- VS Code: provides [basic integration](https://code.visualstudio.com/docs/python/linting#_mypy) with mypy. - Vim: - Using [Syntastic](https://github.com/vim-syntastic/syntastic): in `~/.vimrc` add `let g:syntastic_python_checkers=['mypy']` @@ -141,11 +141,9 @@ Mypy can be integrated into popular IDEs: or can be explicitly enabled by adding `let b:ale_linters = ['mypy']` in `~/vim/ftplugin/python.vim` - Emacs: using [Flycheck](https://github.com/flycheck/) - Sublime Text: [SublimeLinter-contrib-mypy](https://github.com/fredcallaway/SublimeLinter-contrib-mypy) -- Atom: [linter-mypy](https://atom.io/packages/linter-mypy) -- PyCharm: [mypy plugin](https://github.com/dropbox/mypy-PyCharm-plugin) (PyCharm integrates - [its own implementation](https://www.jetbrains.com/help/pycharm/type-hinting-in-product.html) of [PEP 484](https://peps.python.org/pep-0484/)) -- VS Code: provides [basic integration](https://code.visualstudio.com/docs/python/linting#_mypy) with mypy. -- pre-commit: use [pre-commit mirrors-mypy](https://github.com/pre-commit/mirrors-mypy). +- PyCharm: [mypy plugin](https://github.com/dropbox/mypy-PyCharm-plugin) +- pre-commit: use [pre-commit mirrors-mypy](https://github.com/pre-commit/mirrors-mypy), although + note by default this will limit mypy's ability to analyse your third party dependencies. Web site and documentation -------------------------- @@ -171,8 +169,6 @@ contributors of all experience levels. To get started with developing mypy, see [CONTRIBUTING.md](CONTRIBUTING.md). -If you need help getting started, don't hesitate to ask on [gitter](https://gitter.im/python/typing). - Mypyc and compiled version of mypy ---------------------------------- @@ -190,4 +186,4 @@ To use a compiled version of a development version of mypy, directly install a binary from . -To contribute to the mypyc project, check out +To contribute to the mypyc project, check out the issue tracker at From ec4ccb07cca10b7cfc15e126312fbbaa32a548ec Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:44:05 +0100 Subject: [PATCH 0947/1617] Add sphinx_inline_tabs to docs (#18262) https://sphinx-inline-tabs.readthedocs.io/en/latest/ --- docs/requirements-docs.txt | 1 + docs/source/conf.py | 7 +++- docs/source/running_mypy.rst | 64 +++++++++++++++++++++++------------- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index a94c1b7ba95c..747f376a8f5a 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,3 +1,4 @@ sphinx>=8.1.0 furo>=2022.3.4 myst-parser>=4.0.0 +sphinx_inline_tabs>=2023.04.21 diff --git a/docs/source/conf.py b/docs/source/conf.py index ddc9923c6c93..79a5c0619615 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,12 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder", "myst_parser"] +extensions = [ + "sphinx.ext.intersphinx", + "sphinx_inline_tabs", + "docs.source.html_builder", + "myst_parser", +] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index ff042b395e99..9f7461d24f72 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -285,16 +285,22 @@ If you are getting this error, try to obtain type hints for the library you're u You can do this via setting the :option:`--follow-untyped-imports ` command line flag or :confval:`follow_untyped_imports` config file option to True. - This option can be specified on a per-module basis as well:: + This option can be specified on a per-module basis as well: - # mypy.ini - [mypy-untyped_package.*] - follow_untyped_imports = True + .. tab:: mypy.ini - # pyproject.toml - [[tool.mypy.overrides]] - module = ["untyped_package.*"] - follow_untyped_imports = true + .. code-block:: ini + + [mypy-untyped_package.*] + follow_untyped_imports = True + + .. tab:: pyproject.toml + + .. code-block:: toml + + [[tool.mypy.overrides]] + module = ["untyped_package.*"] + follow_untyped_imports = true If you are unable to find any existing type hints nor have time to write your own, you can instead *suppress* the errors. @@ -312,16 +318,22 @@ not catch errors in its use. suppose your codebase makes heavy use of an (untyped) library named ``foobar``. You can silence all import errors associated with that library and that library alone by - adding the following section to your config file:: + adding the following section to your config file: + + .. tab:: mypy.ini + + .. code-block:: ini - # mypy.ini - [mypy-foobar.*] - ignore_missing_imports = True + [mypy-foobar.*] + ignore_missing_imports = True - # pyproject.toml - [[tool.mypy.overrides]] - module = ["foobar.*"] - ignore_missing_imports = true + .. tab:: pyproject.toml + + .. code-block:: toml + + [[tool.mypy.overrides]] + module = ["foobar.*"] + ignore_missing_imports = true Note: this option is equivalent to adding a ``# type: ignore`` to every import of ``foobar`` in your codebase. For more information, see the @@ -334,15 +346,21 @@ not catch errors in its use. in your codebase, use :option:`--disable-error-code=import-untyped `. See :ref:`code-import-untyped` for more details on this error code. - You can also set :confval:`disable_error_code`, like so:: + You can also set :confval:`disable_error_code`, like so: + + .. tab:: mypy.ini + + .. code-block:: ini + + [mypy] + disable_error_code = import-untyped + + .. tab:: pyproject.toml - # mypy.ini - [mypy] - disable_error_code = import-untyped + .. code-block:: ini - # pyproject.toml - [tool.mypy] - disable_error_code = ["import-untyped"] + [tool.mypy] + disable_error_code = ["import-untyped"] You can also set the :option:`--ignore-missing-imports ` command line flag or set the :confval:`ignore_missing_imports` config file From ac8957755a35a255f638c122e22c03b0e75b9a79 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:53:56 +0100 Subject: [PATCH 0948/1617] Add regression test for isinstance narrowing (#18272) Regression test for https://github.com/python/mypy/issues/11839 resolved with https://github.com/python/mypy/pull/18138. --- test-data/unit/check-isinstance.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 99bd62765b11..4ad128914c4e 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -2917,3 +2917,18 @@ if hasattr(mod, "y"): [file mod.py] def __getattr__(attr: str) -> str: ... [builtins fixtures/module.pyi] + +[case testTypeIsntLostAfterNarrowing] +from typing import Any + +var: Any +reveal_type(var) # N: Revealed type is "Any" +assert isinstance(var, (bool, str)) +reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]" + +if isinstance(var, bool): + reveal_type(var) # N: Revealed type is "builtins.bool" + +# Type of var shouldn't fall back to Any +reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]" +[builtins fixtures/isinstance.pyi] From d920e6c9859be7d5bcd2c875a5f12ff715a2a079 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 10 Dec 2024 10:56:09 +0000 Subject: [PATCH 0949/1617] Add num iterations option to the perf_compare internal tool (#18275) Sometimes we need more iterations to get precise results. --- misc/perf_compare.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/misc/perf_compare.py b/misc/perf_compare.py index be05bb6ddc32..a5d22c04ff94 100644 --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -2,7 +2,7 @@ Simple usage: - python misc/perf_compare.py my-branch master ... + python misc/perf_compare.py master my-branch ... What this does: @@ -78,10 +78,17 @@ def run_benchmark(compiled_dir: str, check_dir: str) -> float: def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("commit", nargs="+") + parser.add_argument( + "-n", + metavar="NUM", + default=15, + type=int, + help="number of measurements to perform (default=15)", + ) + parser.add_argument("commit", nargs="+", help="git revision to measure (e.g. branch name)") args = parser.parse_args() commits = args.commit - num_runs = 16 + num_runs: int = args.n + 1 if not (os.path.isdir(".git") and os.path.isdir("mypyc")): sys.exit("error: Run this the mypy repo root") From 568648df310ae7b145928f9947e24fa04208f313 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 10 Dec 2024 12:12:57 +0000 Subject: [PATCH 0950/1617] [mypyc] Add lowered primitive for unsafe list get item op (#18136) This inlines the list get item op in loops like `for x in `. I estimated the impact using two microbenchmarks that iterate over `list[int]` objects. One of them was 1.3x faster, while the other was 1.09x faster. Since we now generate detailed IR for the op, instead of using a C primitive function, this also opens up further IR optimization opportunities in the future. --- mypyc/irbuild/builder.py | 12 ++++++-- mypyc/irbuild/for_helpers.py | 2 +- mypyc/irbuild/ll_builder.py | 8 ++++-- mypyc/lower/list_ops.py | 30 ++++++++++++++++++- mypyc/primitives/list_ops.py | 4 +-- mypyc/test-data/irbuild-basic.test | 8 +++--- mypyc/test-data/irbuild-lists.test | 10 +++---- mypyc/test-data/irbuild-set.test | 4 +-- mypyc/test-data/irbuild-statements.test | 12 ++++---- mypyc/test-data/irbuild-tuple.test | 2 +- mypyc/test-data/lowering-int.test | 38 ++++++++++++++++--------- mypyc/test/test_lowering.py | 7 ++++- 12 files changed, 94 insertions(+), 43 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 5f79b911dda2..ee980ff48b48 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -399,8 +399,14 @@ def load_module(self, name: str) -> Value: def call_c(self, desc: CFunctionDescription, args: list[Value], line: int) -> Value: return self.builder.call_c(desc, args, line) - def primitive_op(self, desc: PrimitiveDescription, args: list[Value], line: int) -> Value: - return self.builder.primitive_op(desc, args, line) + def primitive_op( + self, + desc: PrimitiveDescription, + args: list[Value], + line: int, + result_type: RType | None = None, + ) -> Value: + return self.builder.primitive_op(desc, args, line, result_type) def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.builder.int_op(type, lhs, rhs, op, line) @@ -760,7 +766,7 @@ def process_sequence_assignment( item = target.items[i] index = self.builder.load_int(i) if is_list_rprimitive(rvalue.type): - item_value = self.call_c(list_get_item_unsafe_op, [rvalue, index], line) + item_value = self.primitive_op(list_get_item_unsafe_op, [rvalue, index], line) else: item_value = self.builder.gen_method_call( rvalue, "__getitem__", [index], item.type, line diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 9b34a094db60..c5b1d1273bef 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -693,7 +693,7 @@ def unsafe_index(builder: IRBuilder, target: Value, index: Value, line: int) -> # since we want to use __getitem__ if we don't have an unsafe version, # so we just check manually. if is_list_rprimitive(target.type): - return builder.call_c(list_get_item_unsafe_op, [target, index], line) + return builder.primitive_op(list_get_item_unsafe_op, [target, index], line) else: return builder.gen_method_call(target, "__getitem__", [index], None, line) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 5c9bd9412e9b..bbfe14a68c93 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -509,10 +509,12 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - return res def coerce_short_int_to_fixed_width(self, src: Value, target_type: RType, line: int) -> Value: - if is_int64_rprimitive(target_type): + if is_int64_rprimitive(target_type) or ( + PLATFORM_SIZE == 4 and is_int32_rprimitive(target_type) + ): return self.int_op(target_type, src, Integer(1, target_type), IntOp.RIGHT_SHIFT, line) - # TODO: i32 - assert False, (src.type, target_type) + # TODO: i32 on 64-bit platform + assert False, (src.type, target_type, PLATFORM_SIZE) def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: if ( diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py index 0d2e3e7169d8..f719a9fcd23d 100644 --- a/mypyc/lower/list_ops.py +++ b/mypyc/lower/list_ops.py @@ -1,7 +1,7 @@ from __future__ import annotations from mypyc.common import PLATFORM_SIZE -from mypyc.ir.ops import GetElementPtr, Integer, IntOp, LoadMem, SetMem, Value +from mypyc.ir.ops import GetElementPtr, IncRef, Integer, IntOp, LoadMem, SetMem, Value from mypyc.ir.rtypes import ( PyListObject, c_pyssize_t_rprimitive, @@ -43,3 +43,31 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V def list_items(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: ob_item_ptr = builder.add(GetElementPtr(args[0], PyListObject, "ob_item", line)) return builder.add(LoadMem(pointer_rprimitive, ob_item_ptr, line)) + + +def list_item_ptr(builder: LowLevelIRBuilder, obj: Value, index: Value, line: int) -> Value: + """Get a pointer to a list item (index must be valid and non-negative). + + Type of index must be c_pyssize_t_rprimitive, and obj must refer to a list object. + """ + # List items are represented as an array of pointers. Pointer to the item obj[index] is + # + index * . + items = list_items(builder, [obj], line) + delta = builder.add( + IntOp( + c_pyssize_t_rprimitive, + index, + Integer(PLATFORM_SIZE, c_pyssize_t_rprimitive), + IntOp.MUL, + ) + ) + return builder.add(IntOp(pointer_rprimitive, items, delta, IntOp.ADD)) + + +@lower_primitive_op("list_get_item_unsafe") +def list_get_item_unsafe(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + index = builder.coerce(args[1], c_pyssize_t_rprimitive, line) + item_ptr = list_item_ptr(builder, args[0], index, line) + value = builder.add(LoadMem(object_rprimitive, item_ptr, line)) + builder.add(IncRef(value)) + return value diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index cb75e19a8dea..f3af17d3859e 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -134,10 +134,10 @@ # This is unsafe because it assumes that the index is a non-negative short integer # that is in-bounds for the list. -list_get_item_unsafe_op = custom_op( +list_get_item_unsafe_op = custom_primitive_op( + name="list_get_item_unsafe", arg_types=[list_rprimitive, short_int_rprimitive], return_type=object_rprimitive, - c_function_name="CPyList_GetItemUnsafe", error_kind=ERR_NEVER, ) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 11df241b5074..a43e0d0ada56 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1874,7 +1874,7 @@ L1: r9 = int_lt r6, r8 if r9 goto L2 else goto L8 :: bool L2: - r10 = CPyList_GetItemUnsafe(r1, r6) + r10 = list_get_item_unsafe r1, r6 r11 = unbox(int, r10) x = r11 r12 = int_ne x, 4 @@ -1938,7 +1938,7 @@ L1: r9 = int_lt r6, r8 if r9 goto L2 else goto L8 :: bool L2: - r10 = CPyList_GetItemUnsafe(r1, r6) + r10 = list_get_item_unsafe r1, r6 r11 = unbox(int, r10) x = r11 r12 = int_ne x, 4 @@ -2000,7 +2000,7 @@ L1: r3 = int_lt r0, r2 if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(l, r0) + r4 = list_get_item_unsafe l, r0 r5 = unbox(tuple[int, int, int], r4) r6 = r5[0] x = r6 @@ -2022,7 +2022,7 @@ L5: r15 = int_lt r12, r14 if r15 goto L6 else goto L8 :: bool L6: - r16 = CPyList_GetItemUnsafe(l, r12) + r16 = list_get_item_unsafe l, r12 r17 = unbox(tuple[int, int, int], r16) r18 = r17[0] x_2 = r18 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 725f218b686a..56ad2d53b7eb 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -341,7 +341,7 @@ L1: r5 = int_lt r2, r4 if r5 goto L2 else goto L4 :: bool L2: - r6 = CPyList_GetItemUnsafe(source, r2) + r6 = list_get_item_unsafe source, r2 r7 = unbox(int, r6) x = r7 r8 = CPyTagged_Add(x, 2) @@ -362,7 +362,7 @@ L5: r17 = int_lt r14, r16 if r17 goto L6 else goto L8 :: bool L6: - r18 = CPyList_GetItemUnsafe(source, r14) + r18 = list_get_item_unsafe source, r14 r19 = unbox(int, r18) x_2 = r19 r20 = CPyTagged_Add(x_2, 2) @@ -403,7 +403,7 @@ L1: r3 = int_lt r0, r2 if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(x, r0) + r4 = list_get_item_unsafe x, r0 r5 = unbox(int, r4) i = r5 r6 = box(int, i) @@ -476,7 +476,7 @@ L1: r3 = int_lt r0, r2 if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(a, r0) + r4 = list_get_item_unsafe a, r0 r5 = cast(union[str, bytes], r4) x = r5 L3: @@ -502,7 +502,7 @@ L1: r3 = int_lt r0, r2 if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(a, r0) + r4 = list_get_item_unsafe a, r0 r5 = cast(union[str, None], r4) x = r5 L3: diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 110801b78a66..42429cf7072e 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -115,7 +115,7 @@ L1: r9 = int_lt r6, r8 if r9 goto L2 else goto L4 :: bool L2: - r10 = CPyList_GetItemUnsafe(tmp_list, r6) + r10 = list_get_item_unsafe tmp_list, r6 r11 = unbox(int, r10) x = r11 r12 = f(x) @@ -361,7 +361,7 @@ L1: r13 = int_lt r10, r12 if r13 goto L2 else goto L6 :: bool L2: - r14 = CPyList_GetItemUnsafe(tmp_list, r10) + r14 = list_get_item_unsafe tmp_list, r10 r15 = unbox(int, r14) z = r15 r16 = int_lt z, 8 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 825bc750f7a7..cc9d98be51c9 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -246,7 +246,7 @@ L1: r3 = int_lt r0, r2 if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(ls, r0) + r4 = list_get_item_unsafe ls, r0 r5 = unbox(int, r4) x = r5 r6 = CPyTagged_Add(y, x) @@ -594,8 +594,8 @@ def f(l, t): L0: r0 = CPySequence_CheckUnpackCount(l, 2) r1 = r0 >= 0 :: signed - r2 = CPyList_GetItemUnsafe(l, 0) - r3 = CPyList_GetItemUnsafe(l, 2) + r2 = list_get_item_unsafe l, 0 + r3 = list_get_item_unsafe l, 2 x = r2 r4 = unbox(int, r3) y = r4 @@ -882,7 +882,7 @@ L1: if r4 goto L2 else goto L4 :: bool L2: i = r0 - r5 = CPyList_GetItemUnsafe(a, r1) + r5 = list_get_item_unsafe a, r1 r6 = unbox(int, r5) x = r6 r7 = CPyTagged_Add(i, x) @@ -961,7 +961,7 @@ L2: r5 = PyIter_Next(r1) if is_error(r5) goto L7 else goto L3 L3: - r6 = CPyList_GetItemUnsafe(a, r0) + r6 = list_get_item_unsafe a, r0 r7 = unbox(int, r6) x = r7 r8 = unbox(bool, r5) @@ -1015,7 +1015,7 @@ L3: L4: r8 = unbox(bool, r3) x = r8 - r9 = CPyList_GetItemUnsafe(b, r1) + r9 = list_get_item_unsafe b, r1 r10 = unbox(int, r9) y = r10 x = 0 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index a6813de4ee44..abb180dde89b 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -265,7 +265,7 @@ L1: r10 = int_lt r7, r9 if r10 goto L2 else goto L4 :: bool L2: - r11 = CPyList_GetItemUnsafe(source, r7) + r11 = list_get_item_unsafe source, r7 r12 = unbox(int, r11) x = r12 r13 = f(x) diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test index e7df944c4458..ad561c561872 100644 --- a/mypyc/test-data/lowering-int.test +++ b/mypyc/test-data/lowering-int.test @@ -332,7 +332,7 @@ L4: L5: return 4 -[case testLowerIntForLoop] +[case testLowerIntForLoop_64bit] from __future__ import annotations def f(l: list[int]) -> None: @@ -346,10 +346,14 @@ def f(l): r2 :: native_int r3 :: short_int r4 :: bit - r5 :: object - r6, x :: int - r7 :: short_int - r8 :: None + r5 :: native_int + r6, r7 :: ptr + r8 :: native_int + r9 :: ptr + r10 :: object + r11, x :: int + r12 :: short_int + r13 :: None L0: r0 = 0 L1: @@ -359,19 +363,25 @@ L1: r4 = r0 < r3 :: signed if r4 goto L2 else goto L5 :: bool L2: - r5 = CPyList_GetItemUnsafe(l, r0) - r6 = unbox(int, r5) - dec_ref r5 - if is_error(r6) goto L6 (error at f:4) else goto L3 + r5 = r0 >> 1 + r6 = get_element_ptr l ob_item :: PyListObject + r7 = load_mem r6 :: ptr* + r8 = r5 * 8 + r9 = r7 + r8 + r10 = load_mem r9 :: builtins.object* + inc_ref r10 + r11 = unbox(int, r10) + dec_ref r10 + if is_error(r11) goto L6 (error at f:4) else goto L3 L3: - x = r6 + x = r11 dec_ref x :: int L4: - r7 = r0 + 2 - r0 = r7 + r12 = r0 + 2 + r0 = r12 goto L1 L5: return 1 L6: - r8 = :: None - return r8 + r13 = :: None + return r13 diff --git a/mypyc/test/test_lowering.py b/mypyc/test/test_lowering.py index 50a9a7390855..86745b6d390b 100644 --- a/mypyc/test/test_lowering.py +++ b/mypyc/test/test_lowering.py @@ -15,6 +15,7 @@ MypycDataSuite, assert_test_output, build_ir_for_single_file, + infer_ir_build_options_from_test_name, remove_comment_lines, replace_word_size, use_custom_builtins, @@ -31,11 +32,15 @@ class TestLowering(MypycDataSuite): base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: + options = infer_ir_build_options_from_test_name(testcase.name) + if options is None: + # Skipped test case + return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) expected_output = replace_word_size(expected_output) try: - ir = build_ir_for_single_file(testcase.input) + ir = build_ir_for_single_file(testcase.input, options) except CompileError as e: actual = e.messages else: From 6427ef17f0180422e0113bc67440d2b911d68f39 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Tue, 10 Dec 2024 13:13:53 +0100 Subject: [PATCH 0951/1617] Add `--strict-bytes` flag (#18263) Closes #18256 --- docs/source/command_line.rst | 29 +++++++++++++++++++++++++++++ docs/source/config_file.rst | 8 ++++++++ mypy/main.py | 13 +++++++++++++ mypy/options.py | 4 ++++ test-data/unit/check-flags.test | 14 ++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index ea96e9f64790..17758484f243 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -659,6 +659,35 @@ of the above sections. assert text is not None # OK, check against None is allowed as a special case. + +.. option:: --strict-bytes + + By default, mypy treats ``bytearray`` and ``memoryview`` as subtypes of ``bytes`` which + is not true at runtime. Use this flag to disable this behavior. ``--strict-bytes`` will + be enabled by default in *mypy 2.0*. + + .. code-block:: python + + def f(buf: bytes) -> None: + assert isinstance(buf, bytes) # Raises runtime AssertionError with bytearray/memoryview + with open("binary_file", "wb") as fp: + fp.write(buf) + + f(bytearray(b"")) # error: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" + f(memoryview(b"")) # error: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes" + + # If `f` accepts any object that implements the buffer protocol, consider using: + from collections.abc import Buffer # "from typing_extensions" in Python 3.11 and earlier + + def f(buf: Buffer) -> None: + with open("binary_file", "wb") as fp: + fp.write(buf) + + f(b"") # Ok + f(bytearray(b"")) # Ok + f(memoryview(b"")) # Ok + + .. option:: --extra-checks This flag enables additional checks that are technically correct but may be diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index d7ae1b7a00df..747ef3a9fdaa 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -778,6 +778,14 @@ section of the command line docs. Prohibit equality checks, identity checks, and container checks between non-overlapping types. +.. confval:: strict_bytes + + :type: boolean + :default: False + + Disable treating ``bytearray`` and ``memoryview`` as subtypes of ``bytes``. + This will be enabled by default in *mypy 2.0*. + .. confval:: strict :type: boolean diff --git a/mypy/main.py b/mypy/main.py index 7032682c9fd0..e1c9f20400bc 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -859,6 +859,14 @@ def add_invertible_flag( group=strictness_group, ) + add_invertible_flag( + "--strict-bytes", + default=False, + strict_flag=False, + help="Disable treating bytearray and memoryview as subtypes of bytes", + group=strictness_group, + ) + add_invertible_flag( "--extra-checks", default=False, @@ -1386,6 +1394,11 @@ def set_strict_flags() -> None: process_cache_map(parser, special_opts, options) + # Process --strict-bytes + if options.strict_bytes: + options.disable_bytearray_promotion = True + options.disable_memoryview_promotion = True + # An explicitly specified cache_fine_grained implies local_partial_types # (because otherwise the cache is not compatible with dmypy) if options.cache_fine_grained: diff --git a/mypy/options.py b/mypy/options.py index 33a2c75d164e..eb3d389b5d8a 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -67,6 +67,7 @@ class BuildType: "plugins", "disable_bytearray_promotion", "disable_memoryview_promotion", + "strict_bytes", } ) - {"debug_cache"} @@ -215,6 +216,9 @@ def __init__(self) -> None: # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors. self.strict_equality = False + # Disable treating bytearray and memoryview as subtypes of bytes + self.strict_bytes = False + # Deprecated, use extra_checks instead. self.strict_concatenate = False diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index c3a5f9e3bc04..86a65d85a8b2 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2393,6 +2393,20 @@ def f(x: bytes, y: bytearray, z: memoryview) -> None: x in z [builtins fixtures/primitives.pyi] +[case testStrictBytes] +# flags: --strict-bytes +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +f(memoryview(b"asdf")) # E: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes" +[builtins fixtures/primitives.pyi] + +[case testNoStrictBytes] +# flags: --no-strict-bytes +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) +f(memoryview(b"asdf")) +[builtins fixtures/primitives.pyi] + [case testNoCrashFollowImportsForStubs] # flags: --config-file tmp/mypy.ini {**{"x": "y"}} From 14974072c0a70f8ca29c17c740475187b800e714 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 10 Dec 2024 06:06:34 -0800 Subject: [PATCH 0952/1617] Add Self misuse to common issues (#18261) This has come up at least a half dozen times on the tracker --- docs/source/common_issues.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 39954b8e332a..61b71c108ea0 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -819,3 +819,30 @@ This is best understood via an example: To get this code to type check, you could assign ``y = x`` after ``x`` has been narrowed, and use ``y`` in the inner function, or add an assert in the inner function. + +.. _incorrect-self: + +Incorrect use of ``Self`` +------------------------- + +``Self`` is not the type of the current class; it's a type variable with upper +bound of the current class. That is, it represents the type of the current class +or of potential subclasses. + +.. code-block:: python + + from typing import Self + + class Foo: + @classmethod + def constructor(cls) -> Self: + # Instead, either call cls() or change the annotation to -> Foo + return Foo() # error: Incompatible return value type (got "Foo", expected "Self") + + class Bar(Foo): + ... + + reveal_type(Foo.constructor()) # note: Revealed type is "Foo" + # In the context of the subclass Bar, the Self return type promises + # that the return value will be Bar + reveal_type(Bar.constructor()) # note: Revealed type is "Bar" From 40730c9e6d8a576b8374527056a3672ab80f7d65 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 11 Dec 2024 11:10:54 +0300 Subject: [PATCH 0953/1617] Do not allow `type[]` to contain `Literal` types (#18276) Closes https://github.com/python/mypy/issues/18196 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/typeanal.py | 11 +++++----- mypy/types_utils.py | 25 +++++++++++++++++------ test-data/unit/check-literal.test | 9 ++++++++ test-data/unit/check-recursive-types.test | 5 +++-- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 2f85e83bb3c3..bc340c194cdc 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -110,7 +110,7 @@ get_proper_type, has_type_vars, ) -from mypy.types_utils import is_bad_type_type_item +from mypy.types_utils import get_bad_type_type_item from mypy.typevars import fill_typevars T = TypeVar("T") @@ -652,14 +652,15 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ # To prevent assignment of 'builtins.type' inferred as 'builtins.object' # See https://github.com/python/mypy/issues/9476 for more information return None + type_str = "Type[...]" if fullname == "typing.Type" else "type[...]" if len(t.args) != 1: - type_str = "Type[...]" if fullname == "typing.Type" else "type[...]" self.fail( - type_str + " must have exactly one type argument", t, code=codes.VALID_TYPE + f"{type_str} must have exactly one type argument", t, code=codes.VALID_TYPE ) item = self.anal_type(t.args[0]) - if is_bad_type_type_item(item): - self.fail("Type[...] can't contain another Type[...]", t, code=codes.VALID_TYPE) + bad_item_name = get_bad_type_type_item(item) + if bad_item_name: + self.fail(f'{type_str} can\'t contain "{bad_item_name}"', t, code=codes.VALID_TYPE) item = AnyType(TypeOfAny.from_error) return TypeType.make_normalized(item, line=t.line, column=t.column) elif fullname == "typing.ClassVar": diff --git a/mypy/types_utils.py b/mypy/types_utils.py index 1cd56eae5835..aaa7d7fba37a 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -15,6 +15,7 @@ AnyType, CallableType, Instance, + LiteralType, NoneType, Overloaded, ParamSpecType, @@ -75,21 +76,33 @@ def is_invalid_recursive_alias(seen_nodes: set[TypeAlias], target: Type) -> bool return False -def is_bad_type_type_item(item: Type) -> bool: +def get_bad_type_type_item(item: Type) -> str | None: """Prohibit types like Type[Type[...]]. Such types are explicitly prohibited by PEP 484. Also, they cause problems with recursive types like T = Type[T], because internal representation of TypeType item is normalized (i.e. always a proper type). + + Also forbids `Type[Literal[...]]`, because typing spec does not allow it. """ + # TODO: what else cannot be present in `type[...]`? item = get_proper_type(item) if isinstance(item, TypeType): - return True + return "Type[...]" + if isinstance(item, LiteralType): + return "Literal[...]" if isinstance(item, UnionType): - return any( - isinstance(get_proper_type(i), TypeType) for i in flatten_nested_unions(item.items) - ) - return False + items = [ + bad_item + for typ in flatten_nested_unions(item.items) + if (bad_item := get_bad_type_type_item(typ)) is not None + ] + if not items: + return None + if len(items) == 1: + return items[0] + return f"Union[{', '.join(items)}]" + return None def is_union_with_any(tp: Type) -> bool: diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 2f94b5df0f83..b2d3024d3b44 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2984,3 +2984,12 @@ class C(Base): reveal_type(sep) # N: Revealed type is "Union[Literal['a'], Literal['b']]" return super().feed_data(sep) [builtins fixtures/tuple.pyi] + +[case testLiteralInsideAType] +from typing_extensions import Literal +from typing import Type, Union + +x: Type[Literal[1]] # E: Type[...] can't contain "Literal[...]" +y: Type[Union[Literal[1], Literal[2]]] # E: Type[...] can't contain "Union[Literal[...], Literal[...]]" +z: Type[Literal[1, 2]] # E: Type[...] can't contain "Union[Literal[...], Literal[...]]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 4d7af98204fb..a00a31863771 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -409,8 +409,9 @@ def local() -> None: x: L reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, Any]]" -S = Type[S] # E: Type[...] can't contain another Type[...] -U = Type[Union[int, U]] # E: Type[...] can't contain another Type[...] +S = Type[S] # E: Type[...] can't contain "Type[...]" +U = Type[Union[int, U]] # E: Type[...] can't contain "Union[Type[...], Type[...]]" \ + # E: Type[...] can't contain "Type[...]" x: U reveal_type(x) # N: Revealed type is "Type[Any]" From bec5cad6ca204fc30b9f47656521df8b7f7b90fc Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 12 Dec 2024 13:33:45 +0300 Subject: [PATCH 0954/1617] Do not allow `ClassVar` and `Final` in `TypedDict` and `NamedTuple` (#18281) Closes #18220 --- mypy/checker.py | 5 +++- mypy/semanal.py | 11 +++++++- mypy/semanal_namedtuple.py | 2 ++ mypy/semanal_shared.py | 1 + mypy/semanal_typeddict.py | 2 ++ mypy/typeanal.py | 26 +++++++++++++++---- test-data/unit/check-namedtuple.test | 19 ++++++++++++++ test-data/unit/check-typeddict.test | 19 ++++++++++++++ test-data/unit/fixtures/typing-namedtuple.pyi | 1 + test-data/unit/fixtures/typing-typeddict.pyi | 1 + 10 files changed, 80 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2edcaa6bc5c5..8b7d5207711c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3565,7 +3565,7 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp else: lvs = [s.lvalue] is_final_decl = s.is_final_def if isinstance(s, AssignmentStmt) else False - if is_final_decl and self.scope.active_class(): + if is_final_decl and (active_class := self.scope.active_class()): lv = lvs[0] assert isinstance(lv, RefExpr) if lv.node is not None: @@ -3579,6 +3579,9 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp # then we already reported the error about missing r.h.s. isinstance(s, AssignmentStmt) and s.type is not None + # Avoid extra error message for NamedTuples, + # they were reported during semanal + and not active_class.is_named_tuple ): self.msg.final_without_value(s) for lv in lvs: diff --git a/mypy/semanal.py b/mypy/semanal.py index edcc50e66e30..e90ab9f160e0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3646,7 +3646,12 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: invalid_bare_final = False if not s.unanalyzed_type.args: s.type = None - if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if ( + isinstance(s.rvalue, TempNode) + and s.rvalue.no_rhs + # Filter duplicate errors, we already reported this: + and not (self.type and self.type.is_named_tuple) + ): invalid_bare_final = True self.fail("Type in Final[...] can only be omitted if there is an initializer", s) else: @@ -7351,6 +7356,7 @@ def type_analyzer( allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allow_type_any: bool = False, ) -> TypeAnalyser: if tvar_scope is None: @@ -7370,6 +7376,7 @@ def type_analyzer( allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, + prohibit_special_class_field_types=prohibit_special_class_field_types, allow_type_any=allow_type_any, ) tpan.in_dynamic_func = bool(self.function_stack and self.function_stack[-1].is_dynamic()) @@ -7394,6 +7401,7 @@ def anal_type( allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allow_type_any: bool = False, ) -> Type | None: """Semantically analyze a type. @@ -7429,6 +7437,7 @@ def anal_type( allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, prohibit_self_type=prohibit_self_type, + prohibit_special_class_field_types=prohibit_special_class_field_types, allow_type_any=allow_type_any, ) tag = self.track_incomplete_refs() diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 7c6da7721e8f..dfc99576e617 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -191,6 +191,7 @@ def check_namedtuple_classdef( stmt.type, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", + prohibit_special_class_field_types="NamedTuple", ) if analyzed is None: # Something is incomplete. We need to defer this named tuple. @@ -483,6 +484,7 @@ def parse_namedtuple_fields_with_types( type, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", + prohibit_special_class_field_types="NamedTuple", ) # Workaround #4987 and avoid introducing a bogus UnboundType if isinstance(analyzed, UnboundType): diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index cb0bdebab724..941a16a7fd5d 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -185,6 +185,7 @@ def anal_type( allow_placeholder: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, ) -> Type | None: raise NotImplementedError diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index d081898bf010..7b6e48eacb39 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -330,6 +330,7 @@ def analyze_typeddict_classdef_fields( allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", + prohibit_special_class_field_types="TypedDict", ) if analyzed is None: return None, [], [], set(), set() # Need to defer @@ -561,6 +562,7 @@ def parse_typeddict_fields_with_types( allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", + prohibit_special_class_field_types="TypedDict", ) if analyzed is None: return None diff --git a/mypy/typeanal.py b/mypy/typeanal.py index bc340c194cdc..32aad5ba4089 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -229,6 +229,7 @@ def __init__( allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allowed_alias_tvars: list[TypeVarLikeType] | None = None, allow_type_any: bool = False, alias_type_params_names: list[str] | None = None, @@ -275,6 +276,8 @@ def __init__( # Names of type aliases encountered while analysing a type will be collected here. self.aliases_used: set[str] = set() self.prohibit_self_type = prohibit_self_type + # Set when we analyze TypedDicts or NamedTuples, since they are special: + self.prohibit_special_class_field_types = prohibit_special_class_field_types # Allow variables typed as Type[Any] and type (useful for base classes). self.allow_type_any = allow_type_any self.allow_type_var_tuple = False @@ -596,11 +599,18 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ elif fullname == "typing.Any" or fullname == "builtins.Any": return AnyType(TypeOfAny.explicit, line=t.line, column=t.column) elif fullname in FINAL_TYPE_NAMES: - self.fail( - "Final can be only used as an outermost qualifier in a variable annotation", - t, - code=codes.VALID_TYPE, - ) + if self.prohibit_special_class_field_types: + self.fail( + f"Final[...] can't be used inside a {self.prohibit_special_class_field_types}", + t, + code=codes.VALID_TYPE, + ) + else: + self.fail( + "Final can be only used as an outermost qualifier in a variable annotation", + t, + code=codes.VALID_TYPE, + ) return AnyType(TypeOfAny.from_error) elif fullname == "typing.Tuple" or ( fullname == "builtins.tuple" @@ -668,6 +678,12 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ self.fail( "Invalid type: ClassVar nested inside other type", t, code=codes.VALID_TYPE ) + if self.prohibit_special_class_field_types: + self.fail( + f"ClassVar[...] can't be used inside a {self.prohibit_special_class_field_types}", + t, + code=codes.VALID_TYPE, + ) if len(t.args) == 0: return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) if len(t.args) != 1: diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index df2c7ffc8067..566b5ef57350 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1441,3 +1441,22 @@ def bar() -> None: misspelled_var_name # E: Name "misspelled_var_name" is not defined [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + + +[case testNamedTupleFinalAndClassVar] +from typing import NamedTuple, Final, ClassVar + +class My(NamedTuple): + a: Final # E: Final[...] can't be used inside a NamedTuple + b: Final[int] # E: Final[...] can't be used inside a NamedTuple + c: ClassVar # E: ClassVar[...] can't be used inside a NamedTuple + d: ClassVar[int] # E: ClassVar[...] can't be used inside a NamedTuple + +Func = NamedTuple('Func', [ + ('a', Final), # E: Final[...] can't be used inside a NamedTuple + ('b', Final[int]), # E: Final[...] can't be used inside a NamedTuple + ('c', ClassVar), # E: ClassVar[...] can't be used inside a NamedTuple + ('d', ClassVar[int]), # E: ClassVar[...] can't be used inside a NamedTuple +]) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index a30fec1b9422..6a86dd63a3cd 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -4053,3 +4053,22 @@ d: D = {"a": 1, "b": "x"} c: C = d # E: Incompatible types in assignment (expression has type "D", variable has type "C") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictFinalAndClassVar] +from typing import TypedDict, Final, ClassVar + +class My(TypedDict): + a: Final # E: Final[...] can't be used inside a TypedDict + b: Final[int] # E: Final[...] can't be used inside a TypedDict + c: ClassVar # E: ClassVar[...] can't be used inside a TypedDict + d: ClassVar[int] # E: ClassVar[...] can't be used inside a TypedDict + +Func = TypedDict('Func', { + 'a': Final, # E: Final[...] can't be used inside a TypedDict + 'b': Final[int], # E: Final[...] can't be used inside a TypedDict + 'c': ClassVar, # E: ClassVar[...] can't be used inside a TypedDict + 'd': ClassVar[int], # E: ClassVar[...] can't be used inside a TypedDict +}) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/fixtures/typing-namedtuple.pyi b/test-data/unit/fixtures/typing-namedtuple.pyi index bcdcfc44c3d2..fbb4e43b62e6 100644 --- a/test-data/unit/fixtures/typing-namedtuple.pyi +++ b/test-data/unit/fixtures/typing-namedtuple.pyi @@ -8,6 +8,7 @@ Optional = 0 Self = 0 Tuple = 0 ClassVar = 0 +Final = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index 7e9c642cf261..a54dc8bcfa94 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -28,6 +28,7 @@ Required = 0 NotRequired = 0 ReadOnly = 0 Self = 0 +ClassVar = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) From 52888aec43ef8ba59645c7cd3ff5725ff9a861d7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 12 Dec 2024 12:52:06 +0000 Subject: [PATCH 0955/1617] Use a fixed hash seed in perf_compare script (#18285) With a random hash seed the measurements can vary a lot even for different builds based on the same commit. Some builds were consistently faster/slower than others, even though there were no code changes. This makes the measurements more predictable. It looks like mypyc output has some randomness, though I haven't looked into the root cause in detail. --- misc/perf_compare.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/perf_compare.py b/misc/perf_compare.py index a5d22c04ff94..dd32d07489ab 100644 --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -39,6 +39,7 @@ def build_mypy(target_dir: str) -> None: env = os.environ.copy() env["CC"] = "clang" env["MYPYC_OPT_LEVEL"] = "2" + env["PYTHONHASHSEED"] = "1" cmd = [sys.executable, "setup.py", "--use-mypyc", "build_ext", "--inplace"] subprocess.run(cmd, env=env, check=True, cwd=target_dir) @@ -60,6 +61,7 @@ def run_benchmark(compiled_dir: str, check_dir: str) -> float: shutil.rmtree(cache_dir) env = os.environ.copy() env["PYTHONPATH"] = os.path.abspath(compiled_dir) + env["PYTHONHASHSEED"] = "1" abschk = os.path.abspath(check_dir) cmd = [ sys.executable, From 46c7ec7ed25de55452783ee7d45718c01018c764 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 13 Dec 2024 14:50:30 +0000 Subject: [PATCH 0956/1617] Support measuring incremental runs in perf_compare script (#18289) Use `--incremental` to measure incremental instead of full self checks. The warmup runs are used to populate incremental caches. --- misc/perf_compare.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/misc/perf_compare.py b/misc/perf_compare.py index dd32d07489ab..878f6d8f2d83 100644 --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -55,9 +55,17 @@ def clone(target_dir: str, commit: str | None) -> None: subprocess.run(["git", "checkout", commit], check=True, cwd=target_dir) -def run_benchmark(compiled_dir: str, check_dir: str) -> float: +def edit_python_file(fnam: str) -> None: + with open(fnam) as f: + data = f.read() + data += "\n#" + with open(fnam, "w") as f: + f.write(data) + + +def run_benchmark(compiled_dir: str, check_dir: str, *, incremental: bool) -> float: cache_dir = os.path.join(compiled_dir, ".mypy_cache") - if os.path.isdir(cache_dir): + if os.path.isdir(cache_dir) and not incremental: shutil.rmtree(cache_dir) env = os.environ.copy() env["PYTHONPATH"] = os.path.abspath(compiled_dir) @@ -72,6 +80,10 @@ def run_benchmark(compiled_dir: str, check_dir: str) -> float: ] cmd += glob.glob(os.path.join(abschk, "mypy/*.py")) cmd += glob.glob(os.path.join(abschk, "mypy/*/*.py")) + if incremental: + # Update a few files to force non-trivial incremental run + edit_python_file(os.path.join(abschk, "mypy/__main__.py")) + edit_python_file(os.path.join(abschk, "mypy/test/testcheck.py")) t0 = time.time() # Ignore errors, since some commits being measured may generate additional errors. subprocess.run(cmd, cwd=compiled_dir, env=env) @@ -80,6 +92,12 @@ def run_benchmark(compiled_dir: str, check_dir: str) -> float: def main() -> None: parser = argparse.ArgumentParser() + parser.add_argument( + "--incremental", + default=False, + action="store_true", + help="measure incremental run (fully cached)", + ) parser.add_argument( "-n", metavar="NUM", @@ -89,6 +107,7 @@ def main() -> None: ) parser.add_argument("commit", nargs="+", help="git revision to measure (e.g. branch name)") args = parser.parse_args() + incremental: bool = args.incremental commits = args.commit num_runs: int = args.n + 1 @@ -127,7 +146,7 @@ def main() -> None: items = list(enumerate(commits)) random.shuffle(items) for i, commit in items: - tt = run_benchmark(target_dirs[i], self_check_dir) + tt = run_benchmark(target_dirs[i], self_check_dir, incremental=incremental) # Don't record the first warm-up run if n > 0: print(f"{commit}: t={tt:.3f}s") From c4f5056d6c43db556b5215cb3c330fcde25a77cd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 13 Dec 2024 17:31:41 +0000 Subject: [PATCH 0957/1617] Limit build parallelism in perf_compare script (#18288) Running too many parallel builds risks running out of memory, especially on systems with 16 GB or less RAM. By default run 8 builds, which may already be too many for smaller systems, but `-j N` can be used to lower the number of parallel builds. Also rename `-n` to `--num-runs` to avoid ambiguity, since `-n` is used by pytest to set parallelism. --- misc/perf_compare.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/misc/perf_compare.py b/misc/perf_compare.py index 878f6d8f2d83..ccb9f46d5835 100644 --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -25,8 +25,8 @@ import statistics import subprocess import sys -import threading import time +from concurrent.futures import ThreadPoolExecutor, as_completed def heading(s: str) -> None: @@ -99,30 +99,34 @@ def main() -> None: help="measure incremental run (fully cached)", ) parser.add_argument( - "-n", - metavar="NUM", + "--num-runs", + metavar="N", default=15, type=int, - help="number of measurements to perform (default=15)", + help="set number of measurements to perform (default=15)", + ) + parser.add_argument( + "-j", + metavar="N", + default=8, + type=int, + help="set maximum number of parallel builds (default=8)", ) parser.add_argument("commit", nargs="+", help="git revision to measure (e.g. branch name)") args = parser.parse_args() incremental: bool = args.incremental commits = args.commit - num_runs: int = args.n + 1 + num_runs: int = args.num_runs + 1 + max_workers: int = args.j if not (os.path.isdir(".git") and os.path.isdir("mypyc")): sys.exit("error: Run this the mypy repo root") - build_threads = [] target_dirs = [] for i, commit in enumerate(commits): target_dir = f"mypy.{i}.tmpdir" target_dirs.append(target_dir) clone(target_dir, commit) - t = threading.Thread(target=lambda: build_mypy(target_dir)) - t.start() - build_threads.append(t) self_check_dir = "mypy.self.tmpdir" clone(self_check_dir, commits[0]) @@ -130,8 +134,10 @@ def main() -> None: heading("Compiling mypy") print("(This will take a while...)") - for t in build_threads: - t.join() + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = [executor.submit(build_mypy, target_dir) for target_dir in target_dirs] + for future in as_completed(futures): + future.result() print(f"Finished compiling mypy ({len(commits)} builds)") From 9db236818df2e6ef14ad95f1fcfb3d08684ef0af Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 14 Dec 2024 01:10:40 -0500 Subject: [PATCH 0958/1617] Use more precise context for invalid type argument errors (#18290) Fixes #12274 Uses the actual invalid type argument as the error context when possible. Given: ```python # flags: --pretty --show-column-number class Foo[S, T: int]: pass x: Foo[str, str] ``` Before: ``` main.py:3:4: error: Type argument "str" of "Foo" must be a subtype of "int" [type-var] x: Foo[str, str] ^ ``` After: ``` main.py:3:13: error: Type argument "str" of "Foo" must be a subtype of "int" [type-var] x: Foo[str, str] ^ ``` --- mypy/semanal_typeargs.py | 17 ++++++++++------- test-data/unit/check-classes.test | 4 ++-- test-data/unit/check-columns.test | 11 ++++++++++- test-data/unit/check-generics.test | 2 +- test-data/unit/check-newsemanal.test | 4 ++-- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 646bb28a3b6e..435abb78ca43 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -148,17 +148,18 @@ def validate_args( is_error = False is_invalid = False for (i, arg), tvar in zip(enumerate(args), type_vars): + context = ctx if arg.line < 0 else arg if isinstance(tvar, TypeVarType): if isinstance(arg, ParamSpecType): is_invalid = True self.fail( INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)), - ctx, + context, code=codes.VALID_TYPE, ) self.note( INVALID_PARAM_SPEC_LOCATION_NOTE.format(arg.name), - ctx, + context, code=codes.VALID_TYPE, ) continue @@ -167,7 +168,7 @@ def validate_args( self.fail( f"Cannot use {format_type(arg, self.options)} for regular type variable," " only for ParamSpec", - ctx, + context, code=codes.VALID_TYPE, ) continue @@ -182,13 +183,15 @@ def validate_args( is_error = True self.fail( message_registry.INVALID_TYPEVAR_AS_TYPEARG.format(arg.name, name), - ctx, + context, code=codes.TYPE_VAR, ) continue else: arg_values = [arg] - if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx): + if self.check_type_var_values( + name, arg_values, tvar.name, tvar.values, context + ): is_error = True # Check against upper bound. Since it's object the vast majority of the time, # add fast path to avoid a potentially slow subtype check. @@ -209,7 +212,7 @@ def validate_args( name, format_type(upper_bound, self.options), ), - ctx, + context, code=codes.TYPE_VAR, ) elif isinstance(tvar, ParamSpecType): @@ -220,7 +223,7 @@ def validate_args( self.fail( "Can only replace ParamSpec with a parameter types list or" f" another ParamSpec, got {format_type(arg, self.options)}", - ctx, + context, code=codes.VALID_TYPE, ) if is_invalid: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 5ce80faaee18..a3d35da15107 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6112,8 +6112,8 @@ A = G x: A[B[int]] # E B = G [out] -main:8:4: error: Type argument "G[int]" of "G" must be a subtype of "str" -main:8:6: error: Type argument "int" of "G" must be a subtype of "str" +main:8:6: error: Type argument "G[int]" of "G" must be a subtype of "str" +main:8:8: error: Type argument "int" of "G" must be a subtype of "str" [case testExtremeForwardReferencing] from typing import TypeVar, Generic diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 44524b9df943..79a2f31b574b 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -310,9 +310,18 @@ T = TypeVar('T', int, str) class C(Generic[T]): pass -def f(c: C[object]) -> None: pass # E:10: Value of type variable "T" of "C" cannot be "object" +def f(c: C[object]) -> None: pass # E:12: Value of type variable "T" of "C" cannot be "object" (C[object]()) # E:2: Value of type variable "T" of "C" cannot be "object" +[case testColumnInvalidLocationForParamSpec] +from typing import List +from typing_extensions import ParamSpec + +P = ParamSpec('P') +def foo(x: List[P]): pass # E:17: Invalid location for ParamSpec "P" \ + # N:17: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +[builtins fixtures/list.pyi] + [case testColumnSyntaxErrorInTypeAnnotation] if int(): def f(x # type: int, diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b8cc0422b749..5791b9c471d5 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -671,7 +671,7 @@ reveal_type(a) # N: Revealed type is "other.array[Any, other.dtype[builtins.floa [out] main:3: error: Type argument "float" of "Array" must be a subtype of "generic" [type-var] a: other.Array[float] - ^ + ^ [file other.py] from typing import Any, Generic, TypeVar diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 784b9db9f66e..81b0066dbf81 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -1666,8 +1666,8 @@ T = TypeVar('T', bound=int) class C(Generic[T]): pass class C2(Generic[T]): pass -A = C[str] # E: Type argument "str" of "C" must be a subtype of "int" \ - # E: Value of type variable "T" of "C" cannot be "str" +A = C[str] # E: Value of type variable "T" of "C" cannot be "str" \ + # E: Type argument "str" of "C" must be a subtype of "int" B = Union[C[str], int] # E: Type argument "str" of "C" must be a subtype of "int" S = TypeVar('S', bound=C[str]) # E: Type argument "str" of "C" must be a subtype of "int" U = TypeVar('U', C[str], str) # E: Type argument "str" of "C" must be a subtype of "int" From 7abcffef3f4a291a90cc54950b9a0559242ce73b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 14 Dec 2024 11:13:14 +0000 Subject: [PATCH 0959/1617] Enable type checking code fragment using perf_compare tool (#18291) Previously the tool only supported measuring self-check performance. Now a code fragment can be passed using `-c "..."`. A typical use case would be something like `perf_compare.py -c "import torch" ...`, to measure the speed of processing `torch`. --- misc/perf_compare.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/misc/perf_compare.py b/misc/perf_compare.py index ccb9f46d5835..ef9976b8e2eb 100644 --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -63,7 +63,9 @@ def edit_python_file(fnam: str) -> None: f.write(data) -def run_benchmark(compiled_dir: str, check_dir: str, *, incremental: bool) -> float: +def run_benchmark( + compiled_dir: str, check_dir: str, *, incremental: bool, code: str | None +) -> float: cache_dir = os.path.join(compiled_dir, ".mypy_cache") if os.path.isdir(cache_dir) and not incremental: shutil.rmtree(cache_dir) @@ -71,19 +73,17 @@ def run_benchmark(compiled_dir: str, check_dir: str, *, incremental: bool) -> fl env["PYTHONPATH"] = os.path.abspath(compiled_dir) env["PYTHONHASHSEED"] = "1" abschk = os.path.abspath(check_dir) - cmd = [ - sys.executable, - "-m", - "mypy", - "--config-file", - os.path.join(abschk, "mypy_self_check.ini"), - ] - cmd += glob.glob(os.path.join(abschk, "mypy/*.py")) - cmd += glob.glob(os.path.join(abschk, "mypy/*/*.py")) - if incremental: - # Update a few files to force non-trivial incremental run - edit_python_file(os.path.join(abschk, "mypy/__main__.py")) - edit_python_file(os.path.join(abschk, "mypy/test/testcheck.py")) + cmd = [sys.executable, "-m", "mypy"] + if code: + cmd += ["-c", code] + else: + cmd += ["--config-file", os.path.join(abschk, "mypy_self_check.ini")] + cmd += glob.glob(os.path.join(abschk, "mypy/*.py")) + cmd += glob.glob(os.path.join(abschk, "mypy/*/*.py")) + if incremental: + # Update a few files to force non-trivial incremental run + edit_python_file(os.path.join(abschk, "mypy/__main__.py")) + edit_python_file(os.path.join(abschk, "mypy/test/testcheck.py")) t0 = time.time() # Ignore errors, since some commits being measured may generate additional errors. subprocess.run(cmd, cwd=compiled_dir, env=env) @@ -112,12 +112,20 @@ def main() -> None: type=int, help="set maximum number of parallel builds (default=8)", ) + parser.add_argument( + "-c", + metavar="CODE", + default=None, + type=str, + help="measure time to type check Python code fragment instead of mypy self-check", + ) parser.add_argument("commit", nargs="+", help="git revision to measure (e.g. branch name)") args = parser.parse_args() incremental: bool = args.incremental commits = args.commit num_runs: int = args.num_runs + 1 max_workers: int = args.j + code: str | None = args.c if not (os.path.isdir(".git") and os.path.isdir("mypyc")): sys.exit("error: Run this the mypy repo root") @@ -152,7 +160,7 @@ def main() -> None: items = list(enumerate(commits)) random.shuffle(items) for i, commit in items: - tt = run_benchmark(target_dirs[i], self_check_dir, incremental=incremental) + tt = run_benchmark(target_dirs[i], self_check_dir, incremental=incremental, code=code) # Don't record the first warm-up run if n > 0: print(f"{commit}: t={tt:.3f}s") From 973618a6bfa88398e08dc250c8427b381b3a0fce Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 14 Dec 2024 11:38:04 -0500 Subject: [PATCH 0960/1617] Gracefully handle encoding errors when writing to stdout (#18292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #12692 Sets the [encoding error handler](https://docs.python.org/3/library/codecs.html#error-handlers) for `stdout` to `"backslashreplace"`. This prevents mypy from crashing if an error message has a character that can't be represented by the current I/O encoding. No change is made to `stderr` since its default is already `"backslashreplace"`. **Before** ```shell $ PYTHONIOENCODING=ascii mypy -c "x=γ" Traceback (most recent call last): ... UnicodeEncodeError: 'ascii' codec can't encode character '\u03b3' in position 50: ordinal not in range(128) ``` **After:** ```shell $ PYTHONIOENCODING=ascii mypy -c "x=γ" :1: error: Name "\u03b3" is not defined [name-defined] Found 1 error in 1 file (checked 1 source file) ``` Externally setting the error handler to something other than `"strict"` still works. For example: ```shell $ PYTHONIOENCODING=ascii:namereplace mypy -c "x=γ" :1: error: Name "\N{GREEK SMALL LETTER GAMMA}" is not defined [name-defined] Found 1 error in 1 file (checked 1 source file) ``` --- mypy/main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypy/main.py b/mypy/main.py index e1c9f20400bc..d2a28a18c6a8 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -9,6 +9,7 @@ import time from collections import defaultdict from gettext import gettext +from io import TextIOWrapper from typing import IO, Any, Final, NoReturn, Sequence, TextIO from mypy import build, defaults, state, util @@ -74,6 +75,10 @@ def main( if args is None: args = sys.argv[1:] + # Write an escape sequence instead of raising an exception on encoding errors. + if isinstance(stdout, TextIOWrapper) and stdout.errors == "strict": + stdout.reconfigure(errors="backslashreplace") + fscache = FileSystemCache() sources, options = process_options(args, stdout=stdout, stderr=stderr, fscache=fscache) if clean_exit: From ce1404369c563a1faa9196112902a845add4434f Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sat, 14 Dec 2024 16:56:27 -0500 Subject: [PATCH 0961/1617] Use more precise context for TypedDict plugin errors (#18293) Fixes #12271 Uses an applicable argument expression as the error context instead of the overall CallExpr. **Given:** ```python # flags: --pretty --show-column-number from typing import TypedDict class A(TypedDict): x: int a: A x.setdefault("y", 123) x.setdefault("x", "bad") # Non-TypedDict case for reference b: dict[str, int] b.setdefault("x", "bad") ``` **Before:** ``` main.py:8:1: error: TypedDict "A" has no key "y" [typeddict-item] a.setdefault("y", 123) ^~~~~~~~~~~~~~~~~~~~~~ main.py:9:1: error: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "int" [typeddict-item] a.setdefault("x", "bad") ^~~~~~~~~~~~~~~~~~~~~~~~ main.py:13:19: error: Argument 2 to "setdefault" of "MutableMapping" has incompatible type "str"; expected "int" [arg-type] b.setdefault("x", "bad") ^~~~~ Found 3 errors in 1 file (checked 1 source file) ``` **After:** ``` main.py:8:14: error: TypedDict "A" has no key "y" [typeddict-item] a.setdefault("y", 123) ^~~ main.py:9:19: error: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "int" [typeddict-item] a.setdefault("x", "bad") ^~~~~ main.py:13:19: error: Argument 2 to "setdefault" of "MutableMapping" has incompatible type "str"; expected "int" [arg-type] b.setdefault("x", "bad") ^~~~~ Found 3 errors in 1 file (checked 1 source file) ``` --- mypy/plugins/default.py | 30 ++++++++++++++++------------- test-data/unit/check-columns.test | 12 +++++++++++- test-data/unit/check-literal.test | 5 +++-- test-data/unit/check-typeddict.test | 15 +++++++++------ 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 73c5742614ee..03cb379a8173 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -304,11 +304,12 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) return AnyType(TypeOfAny.from_error) @@ -316,13 +317,13 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type: value_types = [] for key in keys: if key in ctx.type.required_keys: - ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, key_expr) value_type = ctx.type.items.get(key) if value_type: value_types.append(value_type) else: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return AnyType(TypeOfAny.from_error) if len(ctx.args[1]) == 0: @@ -363,27 +364,29 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types[0]) == 1 and len(ctx.arg_types[1]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) return AnyType(TypeOfAny.from_error) assigned_readonly_keys = ctx.type.readonly_keys & set(keys) if assigned_readonly_keys: - ctx.api.msg.readonly_keys_mutated(assigned_readonly_keys, context=ctx.context) + ctx.api.msg.readonly_keys_mutated(assigned_readonly_keys, context=key_expr) default_type = ctx.arg_types[1][0] + default_expr = ctx.args[1][0] value_types = [] for key in keys: value_type = ctx.type.items.get(key) if value_type is None: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return AnyType(TypeOfAny.from_error) # The signature_callback above can't always infer the right signature @@ -392,7 +395,7 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: # default can be assigned to all key-value pairs we're updating. if not is_subtype(default_type, value_type): ctx.api.msg.typeddict_setdefault_arguments_inconsistent( - default_type, value_type, ctx.context + default_type, value_type, default_expr ) return AnyType(TypeOfAny.from_error) @@ -409,20 +412,21 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types) == 1 and len(ctx.arg_types[0]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) return AnyType(TypeOfAny.from_error) for key in keys: if key in ctx.type.required_keys or key in ctx.type.readonly_keys: - ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, key_expr) elif key not in ctx.type.items: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return ctx.default_return_type diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 79a2f31b574b..0aba0cfca09c 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -227,9 +227,19 @@ class D(TypedDict): x: int t: D = {'x': 'y'} # E:5: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +s: str if int(): - del t['y'] # E:5: TypedDict "D" has no key "y" + del t[s] # E:11: Expected TypedDict key to be string literal + del t["x"] # E:11: Key "x" of TypedDict "D" cannot be deleted + del t["y"] # E:11: TypedDict "D" has no key "y" + +t.pop(s) # E:7: Expected TypedDict key to be string literal +t.pop("y") # E:7: TypedDict "D" has no key "y" + +t.setdefault(s, 123) # E:14: Expected TypedDict key to be string literal +t.setdefault("x", "a") # E:19: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "int" +t.setdefault("y", 123) # E:14: TypedDict "D" has no key "y" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index b2d3024d3b44..cff6e07670a7 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1909,8 +1909,9 @@ reveal_type(d.get(a_key, u)) # N: Revealed type is "Union[builtins.int, __main_ reveal_type(d.get(b_key, u)) # N: Revealed type is "Union[builtins.str, __main__.Unrelated]" reveal_type(d.get(c_key, u)) # N: Revealed type is "builtins.object" -reveal_type(d.pop(a_key)) # E: Key "a" of TypedDict "Outer" cannot be deleted \ - # N: Revealed type is "builtins.int" +reveal_type(d.pop(a_key)) # N: Revealed type is "builtins.int" \ + # E: Key "a" of TypedDict "Outer" cannot be deleted + reveal_type(d.pop(b_key)) # N: Revealed type is "builtins.str" d.pop(c_key) # E: TypedDict "Outer" has no key "c" diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 6a86dd63a3cd..5234ced8ea86 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1747,8 +1747,9 @@ td: Union[TDA, TDB] reveal_type(td.pop('a')) # N: Revealed type is "builtins.int" reveal_type(td.pop('b')) # N: Revealed type is "Union[builtins.str, builtins.int]" -reveal_type(td.pop('c')) # E: TypedDict "TDA" has no key "c" \ - # N: Revealed type is "Union[Any, builtins.int]" +reveal_type(td.pop('c')) # N: Revealed type is "Union[Any, builtins.int]" \ + # E: TypedDict "TDA" has no key "c" + [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2614,8 +2615,9 @@ def func(foo: Union[Foo1, Foo2]): del foo["missing"] # E: TypedDict "Foo1" has no key "missing" \ # E: TypedDict "Foo2" has no key "missing" - del foo[1] # E: Expected TypedDict key to be string literal \ - # E: Argument 1 to "__delitem__" has incompatible type "int"; expected "str" + del foo[1] # E: Argument 1 to "__delitem__" has incompatible type "int"; expected "str" \ + # E: Expected TypedDict key to be string literal + [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -3726,8 +3728,9 @@ class TP(TypedDict): mutable: bool x: TP -reveal_type(x.pop("key")) # E: Key "key" of TypedDict "TP" cannot be deleted \ - # N: Revealed type is "builtins.str" +reveal_type(x.pop("key")) # N: Revealed type is "builtins.str" \ + # E: Key "key" of TypedDict "TP" cannot be deleted + x.update({"key": "abc", "other": 1, "mutable": True}) # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated x.setdefault("key", "abc") # E: ReadOnly TypedDict key "key" TypedDict is mutated From be87d3dcbdc9eb7e103bc6dc8f347b2ebc82aaff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2024 18:46:38 -0800 Subject: [PATCH 0962/1617] Sync typeshed (#18294) Source commit: https://github.com/python/typeshed/commit/fc11e835108394728930059c8db5b436209bc957 --- mypy/typeshed/stdlib/_sitebuiltins.pyi | 5 +++-- .../stdlib/asyncio/proactor_events.pyi | 1 + .../stdlib/asyncio/selector_events.pyi | 2 ++ mypy/typeshed/stdlib/builtins.pyi | 21 ++++++++++++------- mypy/typeshed/stdlib/ctypes/__init__.pyi | 7 +++++-- mypy/typeshed/stdlib/fractions.pyi | 4 ++-- mypy/typeshed/stdlib/optparse.pyi | 2 ++ mypy/typeshed/stdlib/os/__init__.pyi | 1 + mypy/typeshed/stdlib/traceback.pyi | 2 ++ 9 files changed, 32 insertions(+), 13 deletions(-) diff --git a/mypy/typeshed/stdlib/_sitebuiltins.pyi b/mypy/typeshed/stdlib/_sitebuiltins.pyi index 49e88a196825..eb6c81129421 100644 --- a/mypy/typeshed/stdlib/_sitebuiltins.pyi +++ b/mypy/typeshed/stdlib/_sitebuiltins.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Iterable from typing import ClassVar, Literal, NoReturn @@ -5,7 +6,7 @@ class Quitter: name: str eof: str def __init__(self, name: str, eof: str) -> None: ... - def __call__(self, code: int | None = None) -> NoReturn: ... + def __call__(self, code: sys._ExitCode = None) -> NoReturn: ... class _Printer: MAXLINES: ClassVar[Literal[23]] @@ -13,4 +14,4 @@ class _Printer: def __call__(self) -> None: ... class _Helper: - def __call__(self, request: object) -> None: ... + def __call__(self, request: object = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi index 957fdd6ce255..909d671df289 100644 --- a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi @@ -62,3 +62,4 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, _ProactorBaseWritePip class BaseProactorEventLoop(base_events.BaseEventLoop): def __init__(self, proactor: Any) -> None: ... + async def sock_recv(self, sock: socket, n: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/asyncio/selector_events.pyi b/mypy/typeshed/stdlib/asyncio/selector_events.pyi index 430f2dd405cd..18c5df033e2f 100644 --- a/mypy/typeshed/stdlib/asyncio/selector_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/selector_events.pyi @@ -1,4 +1,5 @@ import selectors +from socket import socket from . import base_events @@ -6,3 +7,4 @@ __all__ = ("BaseSelectorEventLoop",) class BaseSelectorEventLoop(base_events.BaseEventLoop): def __init__(self, selector: selectors.BaseSelector | None = None) -> None: ... + async def sock_recv(self, sock: socket, n: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 1a4ca925168a..5c6d321f772e 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1,5 +1,6 @@ # ruff: noqa: PYI036 # This is the module declaring BaseException import _ast +import _sitebuiltins import _typeshed import sys import types @@ -46,7 +47,6 @@ from typing import ( # noqa: Y022 Mapping, MutableMapping, MutableSequence, - NoReturn, Protocol, Sequence, SupportsAbs, @@ -1264,8 +1264,10 @@ def compile( *, _feature_version: int = -1, ) -> Any: ... -def copyright() -> None: ... -def credits() -> None: ... + +copyright: _sitebuiltins._Printer +credits: _sitebuiltins._Printer + def delattr(obj: object, name: str, /) -> None: ... def dir(o: object = ..., /) -> list[str]: ... @overload @@ -1320,7 +1322,7 @@ else: /, ) -> None: ... -def exit(code: sys._ExitCode = None) -> NoReturn: ... +exit: _sitebuiltins.Quitter class filter(Generic[_T]): @overload @@ -1354,7 +1356,9 @@ def getattr(o: object, name: str, default: _T, /) -> Any | _T: ... def globals() -> dict[str, Any]: ... def hasattr(obj: object, name: str, /) -> bool: ... def hash(obj: object, /) -> int: ... -def help(request: object = ...) -> None: ... + +help: _sitebuiltins._Helper + def hex(number: int | SupportsIndex, /) -> str: ... def id(obj: object, /) -> int: ... def input(prompt: object = "", /) -> str: ... @@ -1380,7 +1384,9 @@ else: def isinstance(obj: object, class_or_tuple: _ClassInfo, /) -> bool: ... def issubclass(cls: type, class_or_tuple: _ClassInfo, /) -> bool: ... def len(obj: Sized, /) -> int: ... -def license() -> None: ... + +license: _sitebuiltins._Printer + def locals() -> dict[str, Any]: ... class map(Generic[_S]): @@ -1623,7 +1629,8 @@ def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... @overload def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... -def quit(code: sys._ExitCode = None) -> NoReturn: ... + +quit: _sitebuiltins.Quitter class reversed(Generic[_T]): @overload diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 3e0e7c45bf15..a15dd3615c0c 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -47,7 +47,7 @@ class ArgumentError(Exception): ... class CDLL: _func_flags_: ClassVar[int] - _func_restype_: ClassVar[_CDataType] + _func_restype_: ClassVar[type[_CDataType]] _name: str _handle: int _FuncPtr: type[_FuncPointer] @@ -202,7 +202,10 @@ if sys.platform == "win32": class HRESULT(_SimpleCData[int]): ... # TODO undocumented if sys.version_info >= (3, 12): - c_time_t: type[c_int32 | c_int64] # alias for one or the other at runtime + # At runtime, this is an alias for either c_int32 or c_int64, + # which are themselves an alias for one of c_short, c_int, c_long, or c_longlong + # This covers all our bases. + c_time_t: type[c_int32 | c_int64 | c_short | c_int | c_long | c_longlong] class py_object(_CanCastTo, _SimpleCData[_T]): ... diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index fbcfa868cc1b..33bc766df15d 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -27,11 +27,11 @@ class Fraction(Rational): @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload - def __new__(cls, value: float | Decimal | str, /) -> Self: ... + def __new__(cls, numerator: float | Decimal | str) -> Self: ... if sys.version_info >= (3, 14): @overload - def __new__(cls, value: _ConvertibleToIntegerRatio) -> Self: ... + def __new__(cls, numerator: _ConvertibleToIntegerRatio) -> Self: ... @classmethod def from_float(cls, f: float) -> Self: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index d6db7a06f291..6096ac4a2a1d 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -182,6 +182,8 @@ class Values: def ensure_value(self, attr: str, value): ... def read_file(self, filename: str, mode: str = "careful") -> None: ... def read_module(self, modname: str, mode: str = "careful") -> None: ... + # __getattr__ doesn't exist, but anything passed as a default to __init__ + # is set on the instance. def __getattr__(self, name: str): ... def __setattr__(self, name: str, value, /) -> None: ... def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 98260b14e7ed..64691b514a48 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -231,6 +231,7 @@ if sys.platform == "linux" and sys.version_info >= (3, 12): "CLONE_NEWNET", "CLONE_NEWNS", "CLONE_NEWPID", + "CLONE_NEWTIME", "CLONE_NEWUSER", "CLONE_NEWUTS", "CLONE_SIGHAND", diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 1c4a59de66aa..e36081acfa03 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -115,6 +115,8 @@ if sys.version_info >= (3, 11): class TracebackException: __cause__: TracebackException __context__: TracebackException + if sys.version_info >= (3, 11): + exceptions: list[TracebackException] | None __suppress_context__: bool stack: StackSummary filename: str From d3be43d8e06abee3f7eef525c401b9d19875bf8e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 16 Dec 2024 23:20:09 +0000 Subject: [PATCH 0963/1617] Speed up SCC dependency inference (#18299) Avoid redundant computation of `frozenset(scc)`. This helps with incremental type checking of torch, since it has a big SCC. In my measurements this speeds up incremental checking of `-c "import torch"` by about 11%. --- mypy/graph_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/graph_utils.py b/mypy/graph_utils.py index 399301a6b0fd..5c0d25e425eb 100644 --- a/mypy/graph_utils.py +++ b/mypy/graph_utils.py @@ -57,7 +57,11 @@ def prepare_sccs( sccs: list[set[T]], edges: dict[T, list[T]] ) -> dict[AbstractSet[T], set[AbstractSet[T]]]: """Use original edges to organize SCCs in a graph by dependencies between them.""" - sccsmap = {v: frozenset(scc) for scc in sccs for v in scc} + sccsmap = {} + for scc in sccs: + scc_frozen = frozenset(scc) + for v in scc: + sccsmap[v] = scc_frozen data: dict[AbstractSet[T], set[AbstractSet[T]]] = {} for scc in sccs: deps: set[AbstractSet[T]] = set() From 73ba1e737d3ddf3770ca06c02e43aded7bd6781c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Dec 2024 10:18:24 +0000 Subject: [PATCH 0964/1617] Optimize type indirection visitor (#18298) This was a performance bottleneck when type checking torch. It used to perform lots of set unions and hash value calculations on mypy type objects, which are both pretty expensive. Now we mostly rely on set contains and set add operations with strings, which are much faster. We also avoid constructing many temporary objects. Speeds up type checking torch by about 3%. Also appears to speed up self check by about 2%. --- mypy/indirection.py | 135 ++++++++++++++++++++++------------------- mypy/test/testtypes.py | 6 +- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/mypy/indirection.py b/mypy/indirection.py index 00356d7a4ddb..1be33e45ecba 100644 --- a/mypy/indirection.py +++ b/mypy/indirection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable, Set +from typing import Iterable import mypy.types as types from mypy.types import TypeVisitor @@ -17,105 +17,118 @@ def extract_module_names(type_name: str | None) -> list[str]: return [] -class TypeIndirectionVisitor(TypeVisitor[Set[str]]): +class TypeIndirectionVisitor(TypeVisitor[None]): """Returns all module references within a particular type.""" def __init__(self) -> None: - self.cache: dict[types.Type, set[str]] = {} + # Module references are collected here + self.modules: set[str] = set() + # User to avoid infinite recursion with recursive type aliases self.seen_aliases: set[types.TypeAliasType] = set() + # Used to avoid redundant work + self.seen_fullnames: set[str] = set() def find_modules(self, typs: Iterable[types.Type]) -> set[str]: - self.seen_aliases.clear() - return self._visit(typs) + self.modules = set() + self.seen_fullnames = set() + self.seen_aliases = set() + self._visit(typs) + return self.modules - def _visit(self, typ_or_typs: types.Type | Iterable[types.Type]) -> set[str]: + def _visit(self, typ_or_typs: types.Type | Iterable[types.Type]) -> None: typs = [typ_or_typs] if isinstance(typ_or_typs, types.Type) else typ_or_typs - output: set[str] = set() for typ in typs: if isinstance(typ, types.TypeAliasType): # Avoid infinite recursion for recursive type aliases. if typ in self.seen_aliases: continue self.seen_aliases.add(typ) - if typ in self.cache: - modules = self.cache[typ] - else: - modules = typ.accept(self) - self.cache[typ] = set(modules) - output.update(modules) - return output + typ.accept(self) - def visit_unbound_type(self, t: types.UnboundType) -> set[str]: - return self._visit(t.args) + def _visit_module_name(self, module_name: str) -> None: + if module_name not in self.modules: + self.modules.update(split_module_names(module_name)) - def visit_any(self, t: types.AnyType) -> set[str]: - return set() + def visit_unbound_type(self, t: types.UnboundType) -> None: + self._visit(t.args) - def visit_none_type(self, t: types.NoneType) -> set[str]: - return set() + def visit_any(self, t: types.AnyType) -> None: + pass - def visit_uninhabited_type(self, t: types.UninhabitedType) -> set[str]: - return set() + def visit_none_type(self, t: types.NoneType) -> None: + pass - def visit_erased_type(self, t: types.ErasedType) -> set[str]: - return set() + def visit_uninhabited_type(self, t: types.UninhabitedType) -> None: + pass - def visit_deleted_type(self, t: types.DeletedType) -> set[str]: - return set() + def visit_erased_type(self, t: types.ErasedType) -> None: + pass - def visit_type_var(self, t: types.TypeVarType) -> set[str]: - return self._visit(t.values) | self._visit(t.upper_bound) | self._visit(t.default) + def visit_deleted_type(self, t: types.DeletedType) -> None: + pass - def visit_param_spec(self, t: types.ParamSpecType) -> set[str]: - return self._visit(t.upper_bound) | self._visit(t.default) + def visit_type_var(self, t: types.TypeVarType) -> None: + self._visit(t.values) + self._visit(t.upper_bound) + self._visit(t.default) - def visit_type_var_tuple(self, t: types.TypeVarTupleType) -> set[str]: - return self._visit(t.upper_bound) | self._visit(t.default) + def visit_param_spec(self, t: types.ParamSpecType) -> None: + self._visit(t.upper_bound) + self._visit(t.default) - def visit_unpack_type(self, t: types.UnpackType) -> set[str]: - return t.type.accept(self) + def visit_type_var_tuple(self, t: types.TypeVarTupleType) -> None: + self._visit(t.upper_bound) + self._visit(t.default) - def visit_parameters(self, t: types.Parameters) -> set[str]: - return self._visit(t.arg_types) + def visit_unpack_type(self, t: types.UnpackType) -> None: + t.type.accept(self) - def visit_instance(self, t: types.Instance) -> set[str]: - out = self._visit(t.args) + def visit_parameters(self, t: types.Parameters) -> None: + self._visit(t.arg_types) + + def visit_instance(self, t: types.Instance) -> None: + self._visit(t.args) if t.type: # Uses of a class depend on everything in the MRO, # as changes to classes in the MRO can add types to methods, # change property types, change the MRO itself, etc. for s in t.type.mro: - out.update(split_module_names(s.module_name)) + self._visit_module_name(s.module_name) if t.type.metaclass_type is not None: - out.update(split_module_names(t.type.metaclass_type.type.module_name)) - return out + self._visit_module_name(t.type.metaclass_type.type.module_name) - def visit_callable_type(self, t: types.CallableType) -> set[str]: - out = self._visit(t.arg_types) | self._visit(t.ret_type) + def visit_callable_type(self, t: types.CallableType) -> None: + self._visit(t.arg_types) + self._visit(t.ret_type) if t.definition is not None: - out.update(extract_module_names(t.definition.fullname)) - return out + fullname = t.definition.fullname + if fullname not in self.seen_fullnames: + self.modules.update(extract_module_names(t.definition.fullname)) + self.seen_fullnames.add(fullname) - def visit_overloaded(self, t: types.Overloaded) -> set[str]: - return self._visit(t.items) | self._visit(t.fallback) + def visit_overloaded(self, t: types.Overloaded) -> None: + self._visit(t.items) + self._visit(t.fallback) - def visit_tuple_type(self, t: types.TupleType) -> set[str]: - return self._visit(t.items) | self._visit(t.partial_fallback) + def visit_tuple_type(self, t: types.TupleType) -> None: + self._visit(t.items) + self._visit(t.partial_fallback) - def visit_typeddict_type(self, t: types.TypedDictType) -> set[str]: - return self._visit(t.items.values()) | self._visit(t.fallback) + def visit_typeddict_type(self, t: types.TypedDictType) -> None: + self._visit(t.items.values()) + self._visit(t.fallback) - def visit_literal_type(self, t: types.LiteralType) -> set[str]: - return self._visit(t.fallback) + def visit_literal_type(self, t: types.LiteralType) -> None: + self._visit(t.fallback) - def visit_union_type(self, t: types.UnionType) -> set[str]: - return self._visit(t.items) + def visit_union_type(self, t: types.UnionType) -> None: + self._visit(t.items) - def visit_partial_type(self, t: types.PartialType) -> set[str]: - return set() + def visit_partial_type(self, t: types.PartialType) -> None: + pass - def visit_type_type(self, t: types.TypeType) -> set[str]: - return self._visit(t.item) + def visit_type_type(self, t: types.TypeType) -> None: + self._visit(t.item) - def visit_type_alias_type(self, t: types.TypeAliasType) -> set[str]: - return self._visit(types.get_proper_type(t)) + def visit_type_alias_type(self, t: types.TypeAliasType) -> None: + self._visit(types.get_proper_type(t)) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 0380d1aa82d1..35102be80f5d 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -230,12 +230,14 @@ def test_recursive_nested_in_non_recursive(self) -> None: def test_indirection_no_infinite_recursion(self) -> None: A, _ = self.fx.def_alias_1(self.fx.a) visitor = TypeIndirectionVisitor() - modules = A.accept(visitor) + A.accept(visitor) + modules = visitor.modules assert modules == {"__main__", "builtins"} A, _ = self.fx.def_alias_2(self.fx.a) visitor = TypeIndirectionVisitor() - modules = A.accept(visitor) + A.accept(visitor) + modules = visitor.modules assert modules == {"__main__", "builtins"} From fadb308f8a8e42821a715be20172cdb5739ac77e Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:24:59 +0000 Subject: [PATCH 0965/1617] Bump version to 1.15.0+dev (#18300) The release branch has been cut: https://github.com/python/mypy/tree/release-1.14 Increase the dev version. --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 4510cc56f32b..8ad0efd03cdb 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.14.0+dev" +__version__ = "1.15.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 7e79c4a51c253bc8619b698f110d1782db4b9ff8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Dec 2024 02:18:30 +0000 Subject: [PATCH 0966/1617] Micro-optimize cache deserialization (fixup) (#18303) Mypyc is bad at compiling tuple unpacking, so this should be faster, based on a microbenchmark I created. Also fewer tuple objects need to be allocated and freed. The impact is probably too small to be measured in a real workload, but every little helps. --- mypy/fixup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index f2b5bc17d32e..1117b5a9ced3 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -117,7 +117,8 @@ def visit_type_info(self, info: TypeInfo) -> None: # NOTE: This method *definitely* isn't part of the NodeVisitor API. def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: # Copy the items because we may mutate symtab. - for key, value in list(symtab.items()): + for key in list(symtab): + value = symtab[key] cross_ref = value.cross_ref if cross_ref is not None: # Fix up cross-reference. value.cross_ref = None From 7d81f292161c54bf571a96c8279ffa063b70e820 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 Dec 2024 16:43:00 +0000 Subject: [PATCH 0967/1617] Speed up State.finish_passes (#18302) Don't use a set to deduplicate mypy `Type` objects, since taking the hash of a type, and possibly comparing for equality (which is needed to add a type to a set) is more expensive than processing duplicates in TypeIndirectionVisitor. Many of the most expensive types to process are complex types such as callables, which often don't have many duplicates and have complex `__hash__` methods. This seems to speed up type checking torch slightly, by about 0.5% (average of 100 runs). --- mypy/build.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 40dd73313335..7ccbd5146b77 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2373,23 +2373,20 @@ def finish_passes(self) -> None: # We should always patch indirect dependencies, even in full (non-incremental) builds, # because the cache still may be written, and it must be correct. # TODO: find a more robust way to traverse *all* relevant types? - expr_types = set(self.type_map().values()) - symbol_types = set() + all_types = list(self.type_map().values()) for _, sym, _ in self.tree.local_definitions(): if sym.type is not None: - symbol_types.add(sym.type) + all_types.append(sym.type) if isinstance(sym.node, TypeInfo): # TypeInfo symbols have some extra relevant types. - symbol_types.update(sym.node.bases) + all_types.extend(sym.node.bases) if sym.node.metaclass_type: - symbol_types.add(sym.node.metaclass_type) + all_types.append(sym.node.metaclass_type) if sym.node.typeddict_type: - symbol_types.add(sym.node.typeddict_type) + all_types.append(sym.node.typeddict_type) if sym.node.tuple_type: - symbol_types.add(sym.node.tuple_type) - self._patch_indirect_dependencies( - self.type_checker().module_refs, expr_types | symbol_types - ) + all_types.append(sym.node.tuple_type) + self._patch_indirect_dependencies(self.type_checker().module_refs, all_types) if self.options.dump_inference_stats: dump_type_stats( @@ -2418,7 +2415,7 @@ def free_state(self) -> None: self._type_checker.reset() self._type_checker = None - def _patch_indirect_dependencies(self, module_refs: set[str], types: set[Type]) -> None: + def _patch_indirect_dependencies(self, module_refs: set[str], types: list[Type]) -> None: assert None not in types valid = self.valid_references() From 1f9317f593dc41a2805a3093e2e1890665485e76 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:01:21 +1300 Subject: [PATCH 0968/1617] fix: fail check if not enough or too many types provided to `TypeAliasType` (#18308) Fixes #18307 by failing a type alias call check if the number of positional arguments isn't exactly 2 (one for the type name as a literal string, one for the target type to alias). Before: ```python from typing_extensions import TypeAliasType T1 = TypeAliasType("T1", int, str) # Silently passes and uses `int` as the target type, should be an error T2 = TypeAliasType("T2") # Crashes ``` After: ```python T1 = TypeAliasType("T1", int, str) # E: Too many positional arguments for "TypeAliasType" T2 = TypeAliasType("T2") # E: Missing positional argument "value" in call to "TypeAliasType" ``` The error messages above are propagated from a check with the [`TypeAliasType` constructor definition from the stubs](https://github.com/python/typeshed/blob/bd728fbfae18395c37d9fd020e31b1d4f30c6136/stdlib/typing.pyi#L1041-L1044), so no further special-casing is necessary: ```python class TypeAliasType: def __init__( self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () ) -> None: ... ``` --- mypy/semanal.py | 6 +++++- test-data/unit/check-type-aliases.test | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e90ab9f160e0..42803727a958 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4157,8 +4157,12 @@ def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGu names.append("typing.TypeAliasType") if not refers_to_fullname(rvalue.callee, tuple(names)): return False + if not self.check_typevarlike_name(rvalue, name, rvalue): + return False + if rvalue.arg_kinds.count(ARG_POS) != 2: + return False - return self.check_typevarlike_name(rvalue, name, rvalue) + return True def analyze_type_alias_type_params( self, rvalue: CallExpr diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index c7b9694a9188..4073836dd973 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1105,6 +1105,10 @@ t1: T1 # E: Variable "__main__.T1" is not valid as a type \ T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead? t3: T3 reveal_type(t3) # N: Revealed type is "Any" + +T4 = TypeAliasType("T4") # E: Missing positional argument "value" in call to "TypeAliasType" +T5 = TypeAliasType("T5", int, str) # E: Too many positional arguments for "TypeAliasType" \ + # E: Argument 3 to "TypeAliasType" has incompatible type "Type[str]"; expected "Tuple[Union[TypeVar?, ParamSpec?, TypeVarTuple?], ...]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] From 313220758fa45cb6f93253697965038a383ee319 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:16:54 +0000 Subject: [PATCH 0969/1617] Update changelog for release 1.14 (#18301) As with all releases, I've omitted non user visible changes (e.g. refactoring, test-only changes) and trivial changes (e.g. fix typo) for individual list of PRs, but contributors should still be in the final "thanks" list. --- CHANGELOG.md | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8208fb48294..c854e18a2f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## Next release +... + +## Mypy 1.14 (unreleased) + +We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +Mypy is a static type checker for Python. This release includes new features and bug fixes. +You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + ### Change to enum membership semantics As per the updated [typing specification for enums](https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members), @@ -39,8 +51,244 @@ class Pet(Enum): LION = ... # Member attribute with unknown value and unknown type ``` -Contributed by Terence Honles in PR [17207](https://github.com/python/mypy/pull/17207) and -Shantanu Jain in PR [18068](https://github.com/python/mypy/pull/18068). +Contributed by Terence Honles (PR [17207](https://github.com/python/mypy/pull/17207)) and +Shantanu Jain (PR [18068](https://github.com/python/mypy/pull/18068)). + +### Added support for @deprecated decorator (PEP 702) + +Mypy can now issue errors or notes when code imports a deprecated feature +explicitly with a `from mod import depr` statement, or uses a deprecated feature +imported otherwise or defined locally. Features are considered deprecated when +decorated with `warnings.deprecated`, as specified in [PEP 702](https://peps.python.org/pep-0702). + +You can enable the error code via `--enable-error-code=deprecated` on the mypy +command line or `enable_error_code = deprecated` in the mypy config file. +Use the command line flag `--report-deprecated-as-note` or config file option +`report_deprecated_as_note=True` to turn all such errors into notes. + +Deprecation errors will be enabled by default in a future mypy version. + +Contributed by Christoph Tyralla + +List of changes: + + * PEP 702 (@deprecated): descriptors (Christoph Tyralla, PR [18090](https://github.com/python/mypy/pull/18090)) + * Make "deprecated" Note a standard Error, disabled by default (Valentin Stanciu, PR [18192](https://github.com/python/mypy/pull/18192)) + * PEP 702 (@deprecated): consider all possible type positions (Christoph Tyralla, PR [17926](https://github.com/python/mypy/pull/17926)) + * PEP 702 (@deprecated): improve the handling of explicit type annotations of assignment statements (Christoph Tyralla, PR [17899](https://github.com/python/mypy/pull/17899)) + * Add basic support for PEP 702 (@deprecated). (Christoph Tyralla, PR [17476](https://github.com/python/mypy/pull/17476)) + +### Mypy can be configured to analyze untyped modules + +Mypy normally doesn't analyze imports from modules without stubs or a py.typed marker. +To force mypy to analyze these imports you can now set the `--follow-untyped-imports` command line +flag or the `follow_untyped_imports` config file option to True. This can be set either in the +global section of your mypy config file, or individually on a per-module basis. + +Contributed by Jannick Kremer + +List of changes: + + * Implement flag to allow typechecking of untyped modules (Jannick Kremer, PR [17712](https://github.com/python/mypy/pull/17712)) + * Warn about --follow-untyped-imports (Shantanu, PR [18249](https://github.com/python/mypy/pull/18249)) + +### Added support for new style TypeVar Defaults (PEP 696) + +Mypy now supports TypeVar defaults using the new syntax described in PEP 696, that was introduced in Python 3.13. + +```python +@dataclass +class Box[T = int]: + value: T | None = None + +reveal_type(Box()) # type is Box[int], since it's the default +reveal_type(Box(value="Hello World!")) # type is Box[str] +``` + +Contributed by Marc Mueller (PR [17985](https://github.com/python/mypy/pull/17985)) + +### Improved for loop index variable type narrowing + +Mypy now preserves the literal type of index expressions until the next assignment to support `TypedDict` lookups. + +```python +from typing import TypedDict + +class X(TypedDict): + hourly: int + daily: int + +def func(x: X) -> int: + s = 0 + for var in ("hourly", "daily"): + reveal_type(var) # Revealed type is "Union[Literal['hourly']?, Literal['daily']?]" + s += x[var] # x[var] would previously cause a literal-required error + return s +``` + +Contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/pull/18014)) + +### Mypyc Improvements + + * [mypyc] Document optimized bytes ops and additional str ops (Jukka Lehtosalo, PR [18242](https://github.com/python/mypy/pull/18242)) + * [mypyc] Add primitives and specialization for ord() (Jukka Lehtosalo, PR [18240](https://github.com/python/mypy/pull/18240)) + * [mypyc] Make exception type check in assertRaises test helper precise (Jukka Lehtosalo, PR [18241](https://github.com/python/mypy/pull/18241)) + * [mypyc] Optimize str.encode with specializations for common used encodings (Valentin Stanciu, PR [18232](https://github.com/python/mypy/pull/18232)) + * [mypyc] Refactor: use new-style primitives for unary and method ops (Jukka Lehtosalo, PR [18230](https://github.com/python/mypy/pull/18230)) + * [mypyc] Fixing condition to fall back to PyCall for staticmethod and classmethod (Advait Dixit, PR [18228](https://github.com/python/mypy/pull/18228)) + * [mypyc] Refactor: use new-style primitives for unary ops (Jukka Lehtosalo, PR [18213](https://github.com/python/mypy/pull/18213)) + * [mypyc] Refactor: use new-style primitives for function ops (Jukka Lehtosalo, PR [18211](https://github.com/python/mypy/pull/18211)) + * [mypyc] Support unicode surrogates in string literals (Jukka Lehtosalo, PR [18209](https://github.com/python/mypy/pull/18209)) + * [mypyc] Fixing index variable in for-loop with builtins.enumerate. (Advait Dixit, PR [18202](https://github.com/python/mypy/pull/18202)) + * [mypyc] Fixing check for enum classes. (Advait Dixit, PR [18178](https://github.com/python/mypy/pull/18178)) + * [mypyc] Loading type from imported modules. (Advait Dixit, PR [18158](https://github.com/python/mypy/pull/18158)) + * [mypyc] Fix is_native_ref_expr for class attrs (Jared Hance, PR [18031](https://github.com/python/mypy/pull/18031)) + * [mypyc] fix name generation for modules with similar full names (aatle, PR [18001](https://github.com/python/mypy/pull/18001)) + * [mypyc] fix relative imports in __init__.py (Shantanu, PR [17979](https://github.com/python/mypy/pull/17979)) + * [mypyc] Optimize dunder methods (jairov4, PR [17934](https://github.com/python/mypy/pull/17934)) + * [mypyc] Replace deprecated _PyDict_GetItemStringWithError (Marc Mueller, PR [17930](https://github.com/python/mypy/pull/17930)) + * [mypyc] Fix wheel build for cp313-win (Marc Mueller, PR [17941](https://github.com/python/mypy/pull/17941)) + * [mypyc] Use PyGen_GetCode in gen_is_coroutine (Marc Mueller, PR [17931](https://github.com/python/mypy/pull/17931)) + * [mypyc] Optimize calls to final classes (jairov4, PR [17886](https://github.com/python/mypy/pull/17886)) + * [mypyc] Support ellipsis (...) expressions in class bodies (Newbyte, PR [17923](https://github.com/python/mypy/pull/17923)) + * [mypyc] Sync pythoncapi_compat.h (Marc Mueller, PR [17929](https://github.com/python/mypy/pull/17929)) + * [mypyc] Add "runtests.py mypyc-fast" for running fast mypyc tests (Jukka Lehtosalo, PR [17906](https://github.com/python/mypy/pull/17906)) + * [mypyc] Make C unit tests faster by compiling with -O0 (Jukka Lehtosalo, PR [17884](https://github.com/python/mypy/pull/17884)) + +### Stubgen improvements + + * stubgen: do not include mypy generated symbols (Ali Hamdan, PR [18137](https://github.com/python/mypy/pull/18137)) + * stubgen: fix FunctionContext.fullname for nested classes (Chad Dombrova, PR [17963](https://github.com/python/mypy/pull/17963)) + * stubgen: Add flagfile support (Ruslan Sayfutdinov, PR [18061](https://github.com/python/mypy/pull/18061)) + * stubgen: add support for PEPs 695 and 696 syntax (Ali Hamdan, PR [18054](https://github.com/python/mypy/pull/18054)) + +### Stubtest improvements + + * allow the use of --show-traceback and --pdb with stubtest (Stephen Morton, PR [18037](https://github.com/python/mypy/pull/18037)) + * [stubtest] Verify __all__ exists in stub (Sebastian Rittau, PR [18005](https://github.com/python/mypy/pull/18005)) + * stubtest: Stop telling people to use double underscores (Jelle Zijlstra, PR [17897](https://github.com/python/mypy/pull/17897)) + +### Documentation Updates + + * Fixed typo in extending mypy docs. (Carlton Gibson, PR [18234](https://github.com/python/mypy/pull/18234)) + * Update `config_file` docs (sobolevn, PR [18103](https://github.com/python/mypy/pull/18103)) + * Update for Windows platform. Resolves #18096 (ag-tafe, PR [18097](https://github.com/python/mypy/pull/18097)) + * Correct note about `--disallow-any-generics` flag in docs (Abel Sen, PR [18055](https://github.com/python/mypy/pull/18055)) + * Further caution against `--follow-imports=skip` (Shantanu, PR [18048](https://github.com/python/mypy/pull/18048)) + * [docs] fix broken markup in `type_narrowing.rst` (vasiliy, PR [18028](https://github.com/python/mypy/pull/18028)) + * [docs] automatic copyright year update (chiri, PR [17982](https://github.com/python/mypy/pull/17982)) + * [docs] fix the edit page buttton link in docs (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) + +### Other Notables Fixes and Improvements + + * Show `Protocol` `__call__` for arguments with incompatible types (MechanicalConstruct, PR [18214](https://github.com/python/mypy/pull/18214)) + * Make join and meet symmetric with strict_optional (MechanicalConstruct, PR [18227](https://github.com/python/mypy/pull/18227)) + * Preserve block unreachablility when checking function definitions with constrained TypeVars (Brian Schubert, PR [18217](https://github.com/python/mypy/pull/18217)) + * Do not include non-init fields in the synthesized `__replace__` method for dataclasses (Victorien, PR [18221](https://github.com/python/mypy/pull/18221)) + * Disallow `TypeVar` constraints parameterized by type variables (Brian Schubert, PR [18186](https://github.com/python/mypy/pull/18186)) + * Refactor: merge duplicate HasTypeVars query visitors (Brian Schubert, PR [18222](https://github.com/python/mypy/pull/18222)) + * Always complain about invalid varargs and varkwargs (Shantanu, PR [18207](https://github.com/python/mypy/pull/18207)) + * Set default strict_optional state to True (Shantanu, PR [18198](https://github.com/python/mypy/pull/18198)) + * Preserve typevar default None in type alias (Sukhorosov Aleksey, PR [18197](https://github.com/python/mypy/pull/18197)) + * Added checks for invalid usage of continue/break/return in except* block (coldwolverine, PR [18132](https://github.com/python/mypy/pull/18132)) + * Do not consider bare TypeVar not overlapping with None for reachability analysis (Stanislav Terliakov, PR [18138](https://github.com/python/mypy/pull/18138)) + * Special case types.DynamicClassAttribute as property-like (Stephen Morton, PR [18150](https://github.com/python/mypy/pull/18150)) + * Disallow bare `ParamSpec` in type aliases (Brian Schubert, PR [18174](https://github.com/python/mypy/pull/18174)) + * Move long_description metadata to pyproject.toml (Marc Mueller, PR [18172](https://github.com/python/mypy/pull/18172)) + * Support `==`-based narrowing of Optional (Christoph Tyralla, PR [18163](https://github.com/python/mypy/pull/18163)) + * Allow TypedDict assignment of Required item to NotRequired ReadOnly item (Brian Schubert, PR [18164](https://github.com/python/mypy/pull/18164)) + * Allow nesting of Annotated with TypedDict special forms inside TypedDicts (Brian Schubert, PR [18165](https://github.com/python/mypy/pull/18165)) + * Infer generic type arguments for slice expressions (Brian Schubert, PR [18160](https://github.com/python/mypy/pull/18160)) + * Fix checking of match sequence pattern against bounded type variables (Brian Schubert, PR [18091](https://github.com/python/mypy/pull/18091)) + * Fix incorrect truthyness for Enum types and literals (David Salvisberg, PR [17337](https://github.com/python/mypy/pull/17337)) + * Move static project metadata to pyproject.toml (Marc Mueller, PR [18146](https://github.com/python/mypy/pull/18146)) + * Fallback to stdlib json if integer exceeds 64-bit range (q0w, PR [18148](https://github.com/python/mypy/pull/18148)) + * Fix `OR` pattern structural matching exhaustiveness (yihong, PR [18119](https://github.com/python/mypy/pull/18119)) + * Fix type inference of positional parameter in class pattern involving builtin subtype (Brian Schubert, PR [18141](https://github.com/python/mypy/pull/18141)) + * Fix [override] error with no line number when argument node has no line number (Brian Schubert, PR [18122](https://github.com/python/mypy/pull/18122)) + * Fix typos in `generics.rst` (yihong, PR [18110](https://github.com/python/mypy/pull/18110)) + * Fix couple crashes in dmypy (Ivan Levkivskyi, PR [18098](https://github.com/python/mypy/pull/18098)) + * Fix subtyping between Instance and Overloaded (Shantanu, PR [18102](https://github.com/python/mypy/pull/18102)) + * Clean up new_semantic_analyzer config (Shantanu, PR [18071](https://github.com/python/mypy/pull/18071)) + * Issue warning for enum with no members in stub (Shantanu, PR [18068](https://github.com/python/mypy/pull/18068)) + * Fix enum attributes are not members (Terence Honles, PR [17207](https://github.com/python/mypy/pull/17207)) + * Fix crash when checking slice expression with step 0 in tuple index (Brian Schubert, PR [18063](https://github.com/python/mypy/pull/18063)) + * Allow union-with-callable attributes to be overridden by methods (Brian Schubert, PR [18018](https://github.com/python/mypy/pull/18018)) + * Emit `[mutable-override]` for covariant override of attribute with method (Brian Schubert, PR [18058](https://github.com/python/mypy/pull/18058)) + * Support ParamSpec mapping with functools.partial (Stanislav Terliakov, PR [17355](https://github.com/python/mypy/pull/17355)) + * Fix approved stub ignore, remove normpath (Shantanu, PR [18045](https://github.com/python/mypy/pull/18045)) + * Make `disallow-any-unimported` flag invertible (Séamus Ó Ceanainn, PR [18030](https://github.com/python/mypy/pull/18030)) + * Filter to possible package paths before trying to resolve a module (falsedrow, PR [18038](https://github.com/python/mypy/pull/18038)) + * Refactor type narrowing further (Jukka Lehtosalo, PR [18043](https://github.com/python/mypy/pull/18043)) + * Refactor "==" and "is" type narrowing logic (Jukka Lehtosalo, PR [18042](https://github.com/python/mypy/pull/18042)) + * Fix overlap check for ParamSpec types (Jukka Lehtosalo, PR [18040](https://github.com/python/mypy/pull/18040)) + * Do not prioritize ParamSpec signatures during overload resolution (Stanislav Terliakov, PR [18033](https://github.com/python/mypy/pull/18033)) + * Fix ternary union for literals (Ivan Levkivskyi, PR [18023](https://github.com/python/mypy/pull/18023)) + * Fix compatibility checks for conditional function definitions using decorators (Brian Schubert, PR [18020](https://github.com/python/mypy/pull/18020)) + * Add timeout-minutes to ci config (Marc Mueller, PR [18003](https://github.com/python/mypy/pull/18003)) + * TypeGuard should be bool not Any when matching TypeVar (Evgeniy Slobodkin, PR [17145](https://github.com/python/mypy/pull/17145)) + * Fix cache-convert (Shantanu, PR [17974](https://github.com/python/mypy/pull/17974)) + * Fix generator comprehension in meet.py (Shantanu, PR [17969](https://github.com/python/mypy/pull/17969)) + * fix crash issue when using shadowfile with pretty #17853 (Max Chang, PR [17894](https://github.com/python/mypy/pull/17894)) + * [PEP 695] Fix multiple nested classes don't work (Max Chang, PR [17820](https://github.com/python/mypy/pull/17820)) + * Better error for `mypy -p package` without py.typed (Joe Gordon, PR [17908](https://github.com/python/mypy/pull/17908)) + * Emit error for "raise NotImplemented" (Brian Schubert, PR [17890](https://github.com/python/mypy/pull/17890)) + * Add is_lvalue attribute to AttributeContext (Brian Schubert, PR [17881](https://github.com/python/mypy/pull/17881)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- aatle +- Abel Sen +- Advait Dixit +- ag-tafe +- Alex Waygood +- Ali Hamdan +- Brian Schubert +- Carlton Gibson +- Chad Dombrova +- Chelsea Durazo +- chiri +- Christoph Tyralla +- coldwolverine +- David Salvisberg +- Ekin Dursun +- Evgeniy Slobodkin +- falsedrow +- Gaurav Giri +- Ihor +- Ivan Levkivskyi +- jairov4 +- Jannick Kremer +- Jelle Zijlstra +- jhance +- jianghuyiyuan +- Joe Gordon +- John Doknjas +- Jukka Lehtosalo +- Kanishk Pachauri +- Marc Mueller +- Max Chang +- MechanicalConstruct +- Newbyte +- q0w +- Ruslan Sayfutdinov +- Sebastian Rittau +- Shantanu +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Sukhorosov Aleksey +- Séamus Ó Ceanainn +- Terence Honles +- Valentin Stanciu +- vasiliy +- Victorien +- yihong + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + ## Mypy 1.13 From aa91842ea42f682bcee0ced88b95f47fe046f37a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Dec 2024 15:30:36 +0000 Subject: [PATCH 0970/1617] Minor updates to 1.14 changelog (#18310) --- CHANGELOG.md | 204 ++++++++++++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c854e18a2f39..01c3ed16ddbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ... -## Mypy 1.14 (unreleased) +## Mypy 1.14 We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features and bug fixes. @@ -14,7 +14,7 @@ You can install it as follows: You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -### Change to enum membership semantics +### Change to Enum Membership Semantics As per the updated [typing specification for enums](https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members), enum members must be left unannotated. @@ -23,7 +23,9 @@ enum members must be left unannotated. class Pet(Enum): CAT = 1 # Member attribute DOG = 2 # Member attribute - WOLF: int = 3 # New error: Enum members must be left unannotated + + # New error: Enum members must be left unannotated + WOLF: int = 3 species: str # Considered a non-member attribute ``` @@ -35,26 +37,33 @@ historically it was common to leave the value absent: # In a type stub (.pyi file) class Pet(Enum): - # Change in semantics: previously considered members, now non-member attributes + # Change in semantics: previously considered members, + # now non-member attributes CAT: int DOG: int - # Mypy will now issue a warning if it detects this situation in type stubs: - # > Detected enum "Pet" in a type stub with zero members. - # > There is a chance this is due to a recent change in the semantics of enum membership. - # > If so, use `member = value` to mark an enum member, instead of `member: type` + # Mypy will now issue a warning if it detects this + # situation in type stubs: + # > Detected enum "Pet" in a type stub with zero + # > members. There is a chance this is due to a recent + # > change in the semantics of enum membership. If so, + # > use `member = value` to mark an enum member, + # > instead of `member: type` class Pet(Enum): - # As per the specification, you should now do one of the following: + # As per the specification, you should now do one of + # the following: DOG = 1 # Member attribute with value 1 and known type - WOLF = cast(int, ...) # Member attribute with unknown value but known type - LION = ... # Member attribute with unknown value and unknown type + WOLF = cast(int, ...) # Member attribute with unknown + # value but known type + LION = ... # Member attribute with unknown value and + # # unknown type ``` Contributed by Terence Honles (PR [17207](https://github.com/python/mypy/pull/17207)) and Shantanu Jain (PR [18068](https://github.com/python/mypy/pull/18068)). -### Added support for @deprecated decorator (PEP 702) +### Support for @deprecated Decorator (PEP 702) Mypy can now issue errors or notes when code imports a deprecated feature explicitly with a `from mod import depr` statement, or uses a deprecated feature @@ -68,48 +77,51 @@ Use the command line flag `--report-deprecated-as-note` or config file option Deprecation errors will be enabled by default in a future mypy version. -Contributed by Christoph Tyralla +This feature was contributed by Christoph Tyralla. List of changes: - * PEP 702 (@deprecated): descriptors (Christoph Tyralla, PR [18090](https://github.com/python/mypy/pull/18090)) - * Make "deprecated" Note a standard Error, disabled by default (Valentin Stanciu, PR [18192](https://github.com/python/mypy/pull/18192)) - * PEP 702 (@deprecated): consider all possible type positions (Christoph Tyralla, PR [17926](https://github.com/python/mypy/pull/17926)) - * PEP 702 (@deprecated): improve the handling of explicit type annotations of assignment statements (Christoph Tyralla, PR [17899](https://github.com/python/mypy/pull/17899)) - * Add basic support for PEP 702 (@deprecated). (Christoph Tyralla, PR [17476](https://github.com/python/mypy/pull/17476)) + * Add basic support for PEP 702 (`@deprecated`) (Christoph Tyralla, PR [17476](https://github.com/python/mypy/pull/17476)) + * Support descriptors with `@deprecated` (Christoph Tyralla, PR [18090](https://github.com/python/mypy/pull/18090)) + * Make "deprecated" note an error, disabled by default (Valentin Stanciu, PR [18192](https://github.com/python/mypy/pull/18192)) + * Consider all possible type positions with `@deprecated` (Christoph Tyralla, PR [17926](https://github.com/python/mypy/pull/17926)) + * Improve the handling of explicit type annotations in assignment statements with `@deprecated` (Christoph Tyralla, PR [17899](https://github.com/python/mypy/pull/17899)) -### Mypy can be configured to analyze untyped modules +### Optionally Analyzing Untyped Modules -Mypy normally doesn't analyze imports from modules without stubs or a py.typed marker. -To force mypy to analyze these imports you can now set the `--follow-untyped-imports` command line -flag or the `follow_untyped_imports` config file option to True. This can be set either in the -global section of your mypy config file, or individually on a per-module basis. +Mypy normally doesn't analyze imports from third-party modules (installed using pip, for example) +if there are no stubs or a py.typed marker file. To force mypy to analyze these imports, you +can now use the `--follow-untyped-imports` flag or set the `follow_untyped_imports` +config file option to True. This can be set either in the global section of your mypy config +file, or individually on a per-module basis. -Contributed by Jannick Kremer +This feature was contributed by Jannick Kremer. List of changes: - * Implement flag to allow typechecking of untyped modules (Jannick Kremer, PR [17712](https://github.com/python/mypy/pull/17712)) - * Warn about --follow-untyped-imports (Shantanu, PR [18249](https://github.com/python/mypy/pull/18249)) + * Implement flag to allow type checking of untyped modules (Jannick Kremer, PR [17712](https://github.com/python/mypy/pull/17712)) + * Warn about `--follow-untyped-imports` (Shantanu, PR [18249](https://github.com/python/mypy/pull/18249)) -### Added support for new style TypeVar Defaults (PEP 696) +### Support New Style Type Variable Defaults (PEP 696) -Mypy now supports TypeVar defaults using the new syntax described in PEP 696, that was introduced in Python 3.13. +Mypy now supports type variable defaults using the new syntax described in PEP 696, which +was introduced in Python 3.13. Example: ```python @dataclass -class Box[T = int]: +class Box[T = int]: # Set default for "T" value: T | None = None reveal_type(Box()) # type is Box[int], since it's the default reveal_type(Box(value="Hello World!")) # type is Box[str] ``` -Contributed by Marc Mueller (PR [17985](https://github.com/python/mypy/pull/17985)) +This feature was contributed by Marc Mueller (PR [17985](https://github.com/python/mypy/pull/17985)). -### Improved for loop index variable type narrowing +### Improved For Loop Index Variable Type Narrowing -Mypy now preserves the literal type of index expressions until the next assignment to support `TypedDict` lookups. +Mypy now preserves the literal type of for loop index variables, to support `TypedDict` +lookups. Example: ```python from typing import TypedDict @@ -121,78 +133,72 @@ class X(TypedDict): def func(x: X) -> int: s = 0 for var in ("hourly", "daily"): - reveal_type(var) # Revealed type is "Union[Literal['hourly']?, Literal['daily']?]" - s += x[var] # x[var] would previously cause a literal-required error + # "Union[Literal['hourly']?, Literal['daily']?]" + reveal_type(var) + + # x[var] no longer triggers a literal-required error + s += x[var] return s ``` -Contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/pull/18014)) +This was contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/pull/18014)). ### Mypyc Improvements - * [mypyc] Document optimized bytes ops and additional str ops (Jukka Lehtosalo, PR [18242](https://github.com/python/mypy/pull/18242)) - * [mypyc] Add primitives and specialization for ord() (Jukka Lehtosalo, PR [18240](https://github.com/python/mypy/pull/18240)) - * [mypyc] Make exception type check in assertRaises test helper precise (Jukka Lehtosalo, PR [18241](https://github.com/python/mypy/pull/18241)) - * [mypyc] Optimize str.encode with specializations for common used encodings (Valentin Stanciu, PR [18232](https://github.com/python/mypy/pull/18232)) - * [mypyc] Refactor: use new-style primitives for unary and method ops (Jukka Lehtosalo, PR [18230](https://github.com/python/mypy/pull/18230)) - * [mypyc] Fixing condition to fall back to PyCall for staticmethod and classmethod (Advait Dixit, PR [18228](https://github.com/python/mypy/pull/18228)) - * [mypyc] Refactor: use new-style primitives for unary ops (Jukka Lehtosalo, PR [18213](https://github.com/python/mypy/pull/18213)) - * [mypyc] Refactor: use new-style primitives for function ops (Jukka Lehtosalo, PR [18211](https://github.com/python/mypy/pull/18211)) - * [mypyc] Support unicode surrogates in string literals (Jukka Lehtosalo, PR [18209](https://github.com/python/mypy/pull/18209)) - * [mypyc] Fixing index variable in for-loop with builtins.enumerate. (Advait Dixit, PR [18202](https://github.com/python/mypy/pull/18202)) - * [mypyc] Fixing check for enum classes. (Advait Dixit, PR [18178](https://github.com/python/mypy/pull/18178)) - * [mypyc] Loading type from imported modules. (Advait Dixit, PR [18158](https://github.com/python/mypy/pull/18158)) - * [mypyc] Fix is_native_ref_expr for class attrs (Jared Hance, PR [18031](https://github.com/python/mypy/pull/18031)) - * [mypyc] fix name generation for modules with similar full names (aatle, PR [18001](https://github.com/python/mypy/pull/18001)) - * [mypyc] fix relative imports in __init__.py (Shantanu, PR [17979](https://github.com/python/mypy/pull/17979)) - * [mypyc] Optimize dunder methods (jairov4, PR [17934](https://github.com/python/mypy/pull/17934)) - * [mypyc] Replace deprecated _PyDict_GetItemStringWithError (Marc Mueller, PR [17930](https://github.com/python/mypy/pull/17930)) - * [mypyc] Fix wheel build for cp313-win (Marc Mueller, PR [17941](https://github.com/python/mypy/pull/17941)) - * [mypyc] Use PyGen_GetCode in gen_is_coroutine (Marc Mueller, PR [17931](https://github.com/python/mypy/pull/17931)) - * [mypyc] Optimize calls to final classes (jairov4, PR [17886](https://github.com/python/mypy/pull/17886)) - * [mypyc] Support ellipsis (...) expressions in class bodies (Newbyte, PR [17923](https://github.com/python/mypy/pull/17923)) - * [mypyc] Sync pythoncapi_compat.h (Marc Mueller, PR [17929](https://github.com/python/mypy/pull/17929)) - * [mypyc] Add "runtests.py mypyc-fast" for running fast mypyc tests (Jukka Lehtosalo, PR [17906](https://github.com/python/mypy/pull/17906)) - * [mypyc] Make C unit tests faster by compiling with -O0 (Jukka Lehtosalo, PR [17884](https://github.com/python/mypy/pull/17884)) - -### Stubgen improvements - - * stubgen: do not include mypy generated symbols (Ali Hamdan, PR [18137](https://github.com/python/mypy/pull/18137)) - * stubgen: fix FunctionContext.fullname for nested classes (Chad Dombrova, PR [17963](https://github.com/python/mypy/pull/17963)) - * stubgen: Add flagfile support (Ruslan Sayfutdinov, PR [18061](https://github.com/python/mypy/pull/18061)) - * stubgen: add support for PEPs 695 and 696 syntax (Ali Hamdan, PR [18054](https://github.com/python/mypy/pull/18054)) - -### Stubtest improvements - - * allow the use of --show-traceback and --pdb with stubtest (Stephen Morton, PR [18037](https://github.com/python/mypy/pull/18037)) - * [stubtest] Verify __all__ exists in stub (Sebastian Rittau, PR [18005](https://github.com/python/mypy/pull/18005)) - * stubtest: Stop telling people to use double underscores (Jelle Zijlstra, PR [17897](https://github.com/python/mypy/pull/17897)) + * Document optimized bytes operations and additional str operations (Jukka Lehtosalo, PR [18242](https://github.com/python/mypy/pull/18242)) + * Add primitives and specialization for `ord()` (Jukka Lehtosalo, PR [18240](https://github.com/python/mypy/pull/18240)) + * Optimize `str.encode` with specializations for common used encodings (Valentin Stanciu, PR [18232](https://github.com/python/mypy/pull/18232)) + * Fix fall back to generic operation for staticmethod and classmethod (Advait Dixit, PR [18228](https://github.com/python/mypy/pull/18228)) + * Support unicode surrogates in string literals (Jukka Lehtosalo, PR [18209](https://github.com/python/mypy/pull/18209)) + * Fix index variable in for loop with `builtins.enumerate` (Advait Dixit, PR [18202](https://github.com/python/mypy/pull/18202)) + * Fix check for enum classes (Advait Dixit, PR [18178](https://github.com/python/mypy/pull/18178)) + * Fix loading type from imported modules (Advait Dixit, PR [18158](https://github.com/python/mypy/pull/18158)) + * Fix initializers of final attributes in class body (Jared Hance, PR [18031](https://github.com/python/mypy/pull/18031)) + * Fix name generation for modules with similar full names (aatle, PR [18001](https://github.com/python/mypy/pull/18001)) + * Fix relative imports in `__init__.py` (Shantanu, PR [17979](https://github.com/python/mypy/pull/17979)) + * Optimize dunder methods (jairov4, PR [17934](https://github.com/python/mypy/pull/17934)) + * Replace deprecated `_PyDict_GetItemStringWithError` (Marc Mueller, PR [17930](https://github.com/python/mypy/pull/17930)) + * Fix wheel build for cp313-win (Marc Mueller, PR [17941](https://github.com/python/mypy/pull/17941)) + * Use public PyGen_GetCode instead of vendored implementation (Marc Mueller, PR [17931](https://github.com/python/mypy/pull/17931)) + * Optimize calls to final classes (jairov4, PR [17886](https://github.com/python/mypy/pull/17886)) + * Support ellipsis (`...`) expressions in class bodies (Newbyte, PR [17923](https://github.com/python/mypy/pull/17923)) + * Sync `pythoncapi_compat.h` (Marc Mueller, PR [17929](https://github.com/python/mypy/pull/17929)) + * Add `runtests.py mypyc-fast` for running fast mypyc tests (Jukka Lehtosalo, PR [17906](https://github.com/python/mypy/pull/17906)) + +### Stubgen Improvements + + * Do not include mypy generated symbols (Ali Hamdan, PR [18137](https://github.com/python/mypy/pull/18137)) + * Fix `FunctionContext.fullname` for nested classes (Chad Dombrova, PR [17963](https://github.com/python/mypy/pull/17963)) + * Add flagfile support (Ruslan Sayfutdinov, PR [18061](https://github.com/python/mypy/pull/18061)) + * Add support for PEP 695 and PEP 696 syntax (Ali Hamdan, PR [18054](https://github.com/python/mypy/pull/18054)) + +### Stubtest Improvements + + * Allow the use of `--show-traceback` and `--pdb` with stubtest (Stephen Morton, PR [18037](https://github.com/python/mypy/pull/18037)) + * Verify `__all__` exists in stub (Sebastian Rittau, PR [18005](https://github.com/python/mypy/pull/18005)) + * Stop telling people to use double underscores (Jelle Zijlstra, PR [17897](https://github.com/python/mypy/pull/17897)) ### Documentation Updates - * Fixed typo in extending mypy docs. (Carlton Gibson, PR [18234](https://github.com/python/mypy/pull/18234)) - * Update `config_file` docs (sobolevn, PR [18103](https://github.com/python/mypy/pull/18103)) - * Update for Windows platform. Resolves #18096 (ag-tafe, PR [18097](https://github.com/python/mypy/pull/18097)) - * Correct note about `--disallow-any-generics` flag in docs (Abel Sen, PR [18055](https://github.com/python/mypy/pull/18055)) + * Update config file documentation (sobolevn, PR [18103](https://github.com/python/mypy/pull/18103)) + * Improve contributor documentation for Windows (ag-tafe, PR [18097](https://github.com/python/mypy/pull/18097)) + * Correct note about `--disallow-any-generics` flag in documentation (Abel Sen, PR [18055](https://github.com/python/mypy/pull/18055)) * Further caution against `--follow-imports=skip` (Shantanu, PR [18048](https://github.com/python/mypy/pull/18048)) - * [docs] fix broken markup in `type_narrowing.rst` (vasiliy, PR [18028](https://github.com/python/mypy/pull/18028)) - * [docs] automatic copyright year update (chiri, PR [17982](https://github.com/python/mypy/pull/17982)) - * [docs] fix the edit page buttton link in docs (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) + * Fix the edit page buttton link in documentation (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) ### Other Notables Fixes and Improvements * Show `Protocol` `__call__` for arguments with incompatible types (MechanicalConstruct, PR [18214](https://github.com/python/mypy/pull/18214)) - * Make join and meet symmetric with strict_optional (MechanicalConstruct, PR [18227](https://github.com/python/mypy/pull/18227)) + * Make join and meet symmetric with `strict_optional` (MechanicalConstruct, PR [18227](https://github.com/python/mypy/pull/18227)) * Preserve block unreachablility when checking function definitions with constrained TypeVars (Brian Schubert, PR [18217](https://github.com/python/mypy/pull/18217)) * Do not include non-init fields in the synthesized `__replace__` method for dataclasses (Victorien, PR [18221](https://github.com/python/mypy/pull/18221)) * Disallow `TypeVar` constraints parameterized by type variables (Brian Schubert, PR [18186](https://github.com/python/mypy/pull/18186)) - * Refactor: merge duplicate HasTypeVars query visitors (Brian Schubert, PR [18222](https://github.com/python/mypy/pull/18222)) * Always complain about invalid varargs and varkwargs (Shantanu, PR [18207](https://github.com/python/mypy/pull/18207)) * Set default strict_optional state to True (Shantanu, PR [18198](https://github.com/python/mypy/pull/18198)) - * Preserve typevar default None in type alias (Sukhorosov Aleksey, PR [18197](https://github.com/python/mypy/pull/18197)) - * Added checks for invalid usage of continue/break/return in except* block (coldwolverine, PR [18132](https://github.com/python/mypy/pull/18132)) + * Preserve type variable default None in type alias (Sukhorosov Aleksey, PR [18197](https://github.com/python/mypy/pull/18197)) + * Add checks for invalid usage of continue/break/return in `except*` block (coldwolverine, PR [18132](https://github.com/python/mypy/pull/18132)) * Do not consider bare TypeVar not overlapping with None for reachability analysis (Stanislav Terliakov, PR [18138](https://github.com/python/mypy/pull/18138)) - * Special case types.DynamicClassAttribute as property-like (Stephen Morton, PR [18150](https://github.com/python/mypy/pull/18150)) + * Special case `types.DynamicClassAttribute` as property-like (Stephen Morton, PR [18150](https://github.com/python/mypy/pull/18150)) * Disallow bare `ParamSpec` in type aliases (Brian Schubert, PR [18174](https://github.com/python/mypy/pull/18174)) * Move long_description metadata to pyproject.toml (Marc Mueller, PR [18172](https://github.com/python/mypy/pull/18172)) * Support `==`-based narrowing of Optional (Christoph Tyralla, PR [18163](https://github.com/python/mypy/pull/18163)) @@ -203,37 +209,33 @@ Contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/pull/1801 * Fix incorrect truthyness for Enum types and literals (David Salvisberg, PR [17337](https://github.com/python/mypy/pull/17337)) * Move static project metadata to pyproject.toml (Marc Mueller, PR [18146](https://github.com/python/mypy/pull/18146)) * Fallback to stdlib json if integer exceeds 64-bit range (q0w, PR [18148](https://github.com/python/mypy/pull/18148)) - * Fix `OR` pattern structural matching exhaustiveness (yihong, PR [18119](https://github.com/python/mypy/pull/18119)) + * Fix 'or' pattern structural matching exhaustiveness (yihong, PR [18119](https://github.com/python/mypy/pull/18119)) * Fix type inference of positional parameter in class pattern involving builtin subtype (Brian Schubert, PR [18141](https://github.com/python/mypy/pull/18141)) - * Fix [override] error with no line number when argument node has no line number (Brian Schubert, PR [18122](https://github.com/python/mypy/pull/18122)) - * Fix typos in `generics.rst` (yihong, PR [18110](https://github.com/python/mypy/pull/18110)) - * Fix couple crashes in dmypy (Ivan Levkivskyi, PR [18098](https://github.com/python/mypy/pull/18098)) - * Fix subtyping between Instance and Overloaded (Shantanu, PR [18102](https://github.com/python/mypy/pull/18102)) + * Fix `[override]` error with no line number when argument node has no line number (Brian Schubert, PR [18122](https://github.com/python/mypy/pull/18122)) + * Fix some dmypy crashes (Ivan Levkivskyi, PR [18098](https://github.com/python/mypy/pull/18098)) + * Fix subtyping between instance type and overloaded (Shantanu, PR [18102](https://github.com/python/mypy/pull/18102)) * Clean up new_semantic_analyzer config (Shantanu, PR [18071](https://github.com/python/mypy/pull/18071)) * Issue warning for enum with no members in stub (Shantanu, PR [18068](https://github.com/python/mypy/pull/18068)) * Fix enum attributes are not members (Terence Honles, PR [17207](https://github.com/python/mypy/pull/17207)) * Fix crash when checking slice expression with step 0 in tuple index (Brian Schubert, PR [18063](https://github.com/python/mypy/pull/18063)) * Allow union-with-callable attributes to be overridden by methods (Brian Schubert, PR [18018](https://github.com/python/mypy/pull/18018)) * Emit `[mutable-override]` for covariant override of attribute with method (Brian Schubert, PR [18058](https://github.com/python/mypy/pull/18058)) - * Support ParamSpec mapping with functools.partial (Stanislav Terliakov, PR [17355](https://github.com/python/mypy/pull/17355)) + * Support ParamSpec mapping with `functools.partial` (Stanislav Terliakov, PR [17355](https://github.com/python/mypy/pull/17355)) * Fix approved stub ignore, remove normpath (Shantanu, PR [18045](https://github.com/python/mypy/pull/18045)) * Make `disallow-any-unimported` flag invertible (Séamus Ó Ceanainn, PR [18030](https://github.com/python/mypy/pull/18030)) * Filter to possible package paths before trying to resolve a module (falsedrow, PR [18038](https://github.com/python/mypy/pull/18038)) - * Refactor type narrowing further (Jukka Lehtosalo, PR [18043](https://github.com/python/mypy/pull/18043)) - * Refactor "==" and "is" type narrowing logic (Jukka Lehtosalo, PR [18042](https://github.com/python/mypy/pull/18042)) * Fix overlap check for ParamSpec types (Jukka Lehtosalo, PR [18040](https://github.com/python/mypy/pull/18040)) * Do not prioritize ParamSpec signatures during overload resolution (Stanislav Terliakov, PR [18033](https://github.com/python/mypy/pull/18033)) * Fix ternary union for literals (Ivan Levkivskyi, PR [18023](https://github.com/python/mypy/pull/18023)) * Fix compatibility checks for conditional function definitions using decorators (Brian Schubert, PR [18020](https://github.com/python/mypy/pull/18020)) - * Add timeout-minutes to ci config (Marc Mueller, PR [18003](https://github.com/python/mypy/pull/18003)) * TypeGuard should be bool not Any when matching TypeVar (Evgeniy Slobodkin, PR [17145](https://github.com/python/mypy/pull/17145)) - * Fix cache-convert (Shantanu, PR [17974](https://github.com/python/mypy/pull/17974)) - * Fix generator comprehension in meet.py (Shantanu, PR [17969](https://github.com/python/mypy/pull/17969)) - * fix crash issue when using shadowfile with pretty #17853 (Max Chang, PR [17894](https://github.com/python/mypy/pull/17894)) - * [PEP 695] Fix multiple nested classes don't work (Max Chang, PR [17820](https://github.com/python/mypy/pull/17820)) + * Fix convert-cache tool (Shantanu, PR [17974](https://github.com/python/mypy/pull/17974)) + * Fix generator comprehension with mypyc (Shantanu, PR [17969](https://github.com/python/mypy/pull/17969)) + * Fix crash issue when using shadowfile with pretty (Max Chang, PR [17894](https://github.com/python/mypy/pull/17894)) + * Fix multiple nested classes with new generics syntax (Max Chang, PR [17820](https://github.com/python/mypy/pull/17820)) * Better error for `mypy -p package` without py.typed (Joe Gordon, PR [17908](https://github.com/python/mypy/pull/17908)) - * Emit error for "raise NotImplemented" (Brian Schubert, PR [17890](https://github.com/python/mypy/pull/17890)) - * Add is_lvalue attribute to AttributeContext (Brian Schubert, PR [17881](https://github.com/python/mypy/pull/17881)) + * Emit error for `raise NotImplemented` (Brian Schubert, PR [17890](https://github.com/python/mypy/pull/17890)) + * Add `is_lvalue` attribute to AttributeContext (Brian Schubert, PR [17881](https://github.com/python/mypy/pull/17881)) ### Acknowledgements @@ -261,8 +263,8 @@ Thanks to all mypy contributors who contributed to this release: - Ivan Levkivskyi - jairov4 - Jannick Kremer +- Jared Hance - Jelle Zijlstra -- jhance - jianghuyiyuan - Joe Gordon - John Doknjas @@ -302,7 +304,7 @@ You can read the full documentation for this release on [Read the Docs](http://m Note that unlike typical releases, Mypy 1.13 does not have any changes to type checking semantics from 1.12.1. -### Improved performance +### Improved Performance Mypy 1.13 contains several performance improvements. Users can expect mypy to be 5-20% faster. In environments with long search paths (such as environments using many editable installs), mypy From f078faa3fed940ebdfcdbb46b8d5fea3a613447c Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:07:05 +0000 Subject: [PATCH 0971/1617] [misc] Properly format subheadings and code in gen_blog_post_html.py (#18311) Our Changelog uses 3 number signs (#) for subheadings, not 4. This should generate proper `

` in the blog post. Also, code blocks are generated with just `
` tags. Let's also add
`` tags so we can potentially apply syntax highlighting with js
like highlight.js in the future, if we want to.
highlight.js detects `
 ... 
`. We can also put the language, but it detects it automatically: https://highlightjs.readthedocs.io/en/latest/readme.html#in-the-browser --- misc/gen_blog_post_html.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py index 7170696d5d09..2641ae1c466d 100644 --- a/misc/gen_blog_post_html.py +++ b/misc/gen_blog_post_html.py @@ -46,7 +46,7 @@ def format_code(h: str) -> str: indent = a[i].startswith(" ") if not indent: i += 1 - r.append("
")
+            r.append("
")
             while i < len(a) and (
                 (indent and a[i].startswith("    ")) or (not indent and not a[i].startswith("```"))
             ):
@@ -56,7 +56,7 @@ def format_code(h: str) -> str:
                     line = "    " + line
                 r.append(html.escape(line))
                 i += 1
-            r.append("
") + r.append("
") if not indent and a[i].startswith("```"): i += 1 else: @@ -76,7 +76,7 @@ def convert(src: str) -> str: h = re.sub(r"^## (Mypy [0-9.]+)", r"

\1 Released

", h, flags=re.MULTILINE) # Subheadings - h = re.sub(r"\n#### ([A-Z`].*)\n", r"\n

\1

\n", h) + h = re.sub(r"\n### ([A-Z`].*)\n", r"\n

\1

\n", h) # Sub-subheadings h = re.sub(r"\n\*\*([A-Z_`].*)\*\*\n", r"\n

\1

\n", h) From 0f7a662230e59067cc467232b110011b473503d5 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:35:46 +0000 Subject: [PATCH 0972/1617] [misc] Fix previous change to `
`
 (#18312)

Turns out that `
...
` blocks ignore the first empty line, but `
...
` blocks don't. So if we put the first real line of code on the html line after the tags, it will render as an empty line. (a problem that didn't exist for just pre tags) Let's remove those extra newlines after code tags. (I still think it's nice to have code tags for future syntax highlighting on the blog posts) --- misc/gen_blog_post_html.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py index 2641ae1c466d..00e167e4a3a2 100644 --- a/misc/gen_blog_post_html.py +++ b/misc/gen_blog_post_html.py @@ -62,7 +62,9 @@ def format_code(h: str) -> str: else: r.append(a[i]) i += 1 - return "\n".join(r) + formatted = "\n".join(r) + # remove empty first line for code blocks + return re.sub(r"\n", r"", formatted) def convert(src: str) -> str: From 823c0e5605f3c64d2540ea6c4cbea356dda7b6ff Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Dec 2024 18:33:22 +0000 Subject: [PATCH 0973/1617] Use more aggressive gc thresholds for a big speedup (#18306) In some cases gc was consuming a significant fraction of CPU, so run gc less often. This made incremental checking of torch 27% faster for me (based on 100 measurements), and also speeds up incremental self check by about 20% and non-incremental self check by about 10%. All measurements were on Python 3.13. --- mypy/build.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 7ccbd5146b77..88c334309900 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -218,8 +218,9 @@ def _build( extra_plugins: Sequence[Plugin], ) -> BuildResult: if platform.python_implementation() == "CPython": - # This seems the most reasonable place to tune garbage collection. - gc.set_threshold(150 * 1000) + # Run gc less frequently, as otherwise we can spent a large fraction of + # cpu in gc. This seems the most reasonable place to tune garbage collection. + gc.set_threshold(200 * 1000, 30, 30) data_dir = default_data_dir() fscache = fscache or FileSystemCache() From f445369482e0aa45f6e1c8420a63cfa765a51f34 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:48:03 -0800 Subject: [PATCH 0974/1617] [mypyc] Getting capsule pointer from module instead of PyCapsule_Import (#18286) Fixes mypyc/mypyc#999. `PyCapsule_Import` was failing in sub-packages. Since the capsule is an attribute of the module, we can access the capsule from the module instead of importing it. --- mypyc/lib-rt/module_shim.tmpl | 5 ++++- mypyc/test-data/commandline.test | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/module_shim.tmpl b/mypyc/lib-rt/module_shim.tmpl index 6e772efd34ec..28cce9478d25 100644 --- a/mypyc/lib-rt/module_shim.tmpl +++ b/mypyc/lib-rt/module_shim.tmpl @@ -5,8 +5,11 @@ PyInit_{modname}(void) {{ PyObject *tmp; if (!(tmp = PyImport_ImportModule("{libname}"))) return NULL; + PyObject *capsule = PyObject_GetAttrString(tmp, "init_{full_modname}"); Py_DECREF(tmp); - void *init_func = PyCapsule_Import("{libname}.init_{full_modname}", 0); + if (capsule == NULL) return NULL; + void *init_func = PyCapsule_GetPointer(capsule, "{libname}.init_{full_modname}"); + Py_DECREF(capsule); if (!init_func) {{ return NULL; }} diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index 672e879fbe1e..0b07c2929273 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -243,3 +243,22 @@ def i(arg: Foo) -> None: [file test.py] names = (str(v) for v in [1, 2, 3]) # W: Treating generator comprehension as list + +[case testSubPackage] +# cmd: pkg/sub/foo.py +from pkg.sub import foo + +[file pkg/__init__.py] + +[file pkg/sub/__init__.py] +print("importing...") +from . import foo +print("done") + +[file pkg/sub/foo.py] +print("imported foo") + +[out] +importing... +imported foo +done From d33cef8396c456d87db16dce3525ebf431f4b57f Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 19 Dec 2024 18:23:53 -0800 Subject: [PATCH 0975/1617] stubtest: distinguish metaclass attributes from class attributes (#18314) If the runtime attribute of a class is actually from the metaclass, consider it to be MISSING at runtime. This only occurs a couple times in the stdlib: it shows up when a descriptor is present on the metaclass but not the class, and we want to lie in the stub and say it's a thing on the class anyway. I found this after noticing that `enum.auto.__or__` had a comment that said it didn't exist at runtime, but stubtest thought that it actually did. The issue is that on 3.10+, `type.__or__` is defined for the purpose of Union types, and stubtest doesn't know the difference between `type.__or__` and `__or__` on the actual class. Currently this matches on these things in typeshed's stdlib: ``` abc.ABCMeta.__abstractmethods__ builtins.object.__annotations__ enum.auto.__or__ enum.auto.__ror__ types.NotImplementedType.__call__ ``` This MR doesn't resolve any allowlist entries for typeshed, and it doesn't create any new ones either, but should generate more accurate error messages in this particular edge case. --- mypy/stubtest.py | 8 ++++++++ mypy/test/teststubtest.py | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 36cd0a213d4d..6c8d03319893 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -568,6 +568,13 @@ def verify_typeinfo( # Catch all exceptions in case the runtime raises an unexpected exception # from __getattr__ or similar. continue + + # If it came from the metaclass, consider the runtime_attr to be MISSING + # for a more accurate message + if runtime_attr is not MISSING and type(runtime) is not runtime: + if getattr(runtime_attr, "__objclass__", None) is type(runtime): + runtime_attr = MISSING + # Do not error for an object missing from the stub # If the runtime object is a types.WrapperDescriptorType object # and has a non-special dunder name. @@ -1519,6 +1526,7 @@ def is_probably_a_function(runtime: Any) -> bool: isinstance(runtime, (types.FunctionType, types.BuiltinFunctionType)) or isinstance(runtime, (types.MethodType, types.BuiltinMethodType)) or (inspect.ismethoddescriptor(runtime) and callable(runtime)) + or (isinstance(runtime, types.MethodWrapperType) and callable(runtime)) ) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index fcbf07b4d371..b16cb18ace21 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1460,6 +1460,16 @@ def h(x: str): ... runtime="__all__ += ['Z']\nclass Z:\n def __reduce__(self): return (Z,)", error=None, ) + # __call__ exists on type, so it appears to exist on the class. + # This checks that we identify it as missing at runtime anyway. + yield Case( + stub=""" + class ClassWithMetaclassOverride: + def __call__(*args, **kwds): ... + """, + runtime="class ClassWithMetaclassOverride: ...", + error="ClassWithMetaclassOverride.__call__", + ) @collect_cases def test_missing_no_runtime_all(self) -> Iterator[Case]: From 7959a2025c7c9bae429589928490270e4d140329 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Fri, 20 Dec 2024 09:51:36 -0500 Subject: [PATCH 0976/1617] Suppress errors for unreachable branches in conditional expressions (#18295) Fixes #4134 Fixes #9195 Suppress errors when analyzing unreachable conditional expression branches. Same idea as what's done when analyzing the right-hand operand of `and`/`or`: https://github.com/python/mypy/blob/973618a6bfa88398e08dc250c8427b381b3a0fce/mypy/checkexpr.py#L4252-L4256 This PR originally added filters of the same form to the places where `analyze_cond_branch` is called in `ExpressionChecker.visit_conditional_expr`. However, since 5 out of the 6 `analyze_cond_branch` call sites now use `filter_errors` for the case when `map is None`, I decided to move the error filtering logic to inside `analyze_cond_branch`. **Given:** ```python from typing import TypeVar T = TypeVar("T", int, str) def foo(x: T) -> T: return x + 1 if isinstance(x, int) else x + "a" ``` **Before:** ```none main.py:5:16: error: Unsupported operand types for + ("str" and "int") [operator] main.py:5:49: error: Unsupported operand types for + ("int" and "str") [operator] Found 2 errors in 1 file (checked 1 source file) ``` **After:** ``` Success: no issues found in 1 source file ``` --- mypy/checker.py | 4 +++- mypy/checkexpr.py | 13 ++++++------- test-data/unit/check-expressions.test | 7 +++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 8b7d5207711c..fafc857654a7 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4793,7 +4793,9 @@ def visit_assert_stmt(self, s: AssertStmt) -> None: # If this is asserting some isinstance check, bind that type in the following code true_map, else_map = self.find_isinstance_check(s.expr) if s.msg is not None: - self.expr_checker.analyze_cond_branch(else_map, s.msg, None) + self.expr_checker.analyze_cond_branch( + else_map, s.msg, None, suppress_unreachable_errors=False + ) self.push_type_map(true_map) def visit_raise_stmt(self, s: RaiseStmt) -> None: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index adb65a126f38..a00d866d54ec 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4249,11 +4249,7 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: ): self.msg.unreachable_right_operand(e.op, e.right) - # If right_map is None then we know mypy considers the right branch - # to be unreachable and therefore any errors found in the right branch - # should be suppressed. - with self.msg.filter_errors(filter_errors=right_map is None): - right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type) + right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type) if left_map is None and right_map is None: return UninhabitedType() @@ -5851,12 +5847,15 @@ def analyze_cond_branch( node: Expression, context: Type | None, allow_none_return: bool = False, + suppress_unreachable_errors: bool = True, ) -> Type: with self.chk.binder.frame_context(can_skip=True, fall_through=0): if map is None: # We still need to type check node, in case we want to - # process it for isinstance checks later - self.accept(node, type_context=context, allow_none_return=allow_none_return) + # process it for isinstance checks later. Since the branch was + # determined to be unreachable, any errors should be suppressed. + with self.msg.filter_errors(filter_errors=suppress_unreachable_errors): + self.accept(node, type_context=context, allow_none_return=allow_none_return) return UninhabitedType() self.chk.push_type_map(map) return self.accept(node, type_context=context, allow_none_return=allow_none_return) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index cd26c9bb408a..68bfb24e288b 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1506,6 +1506,13 @@ x.append(y) if bool() else x.append(y) z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value (it only ever returns None) [builtins fixtures/list.pyi] +[case testConditionalExpressionWithUnreachableBranches] +from typing import TypeVar +T = TypeVar("T", int, str) +def foo(x: T) -> T: + return x + 1 if isinstance(x, int) else x + "a" +[builtins fixtures/isinstancelist.pyi] + -- Special cases -- ------------- From c859cb1f18bcf4084d5e937cc6e4433b0fbbd0c8 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Fri, 20 Dec 2024 11:38:37 -0500 Subject: [PATCH 0977/1617] Disallow no-args generic aliases when using PEP 613 explicit aliases (#18173) Per the type system conformance tests, [this is ok](https://github.com/python/typing/blob/46b05a4c10ed3841c9bc5126ba9f31dd8ae061e7/conformance/tests/aliases_implicit.py#L130): ```python ListAlias = list x = ListAlias[int]() # OK ``` While [this is not](https://github.com/python/typing/blob/46b05a4c10ed3841c9bc5126ba9f31dd8ae061e7/conformance/tests/aliases_explicit.py#L100): ```python ListAlias: TypeAlias = list x: ListAlias[int] # E: already specialized ``` Mypy currently permits both. This PR makes mypy reject the latter case, improving conformance. As part of this, no-args PEP 613 explicit aliases are no longer eagerly expanded. (Also removed a stale comment referencing `TypeAliasExpr.no_args`, which was removed in #15924) --- mypy/nodes.py | 2 +- mypy/semanal.py | 13 ++++--- test-data/unit/check-type-aliases.test | 53 ++++++++++++++++++++++++++ test-data/unit/diff.test | 1 - 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 9e26103e2f58..a1e1282ef9ee 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3593,7 +3593,7 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here type will be initially an instance type with wrong number of type arguments. Such instances are all fixed either during or after main semantic analysis passes. We therefore store the difference between `List` and `List[Any]` rvalues (targets) - using the `no_args` flag. See also TypeAliasExpr.no_args. + using the `no_args` flag. Meaning of other fields: diff --git a/mypy/semanal.py b/mypy/semanal.py index 42803727a958..8c74c9c2528c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2797,15 +2797,15 @@ def get_declared_metaclass( return None, True, False # defer later in the caller # Support type aliases, like `_Meta: TypeAlias = type` + metaclass_info: Node | None = sym.node if ( isinstance(sym.node, TypeAlias) - and sym.node.no_args - and isinstance(sym.node.target, ProperType) - and isinstance(sym.node.target, Instance) + and not sym.node.python_3_12_type_alias + and not sym.node.alias_tvars ): - metaclass_info: Node | None = sym.node.target.type - else: - metaclass_info = sym.node + target = get_proper_type(sym.node.target) + if isinstance(target, Instance): + metaclass_info = target.type if not isinstance(metaclass_info, TypeInfo) or metaclass_info.tuple_type is not None: self.fail(f'Invalid metaclass "{metaclass_name}"', metaclass_expr) @@ -4077,6 +4077,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: and not res.args and not empty_tuple_index and not pep_695 + and not pep_613 ) if isinstance(res, ProperType) and isinstance(res, Instance): if not validate_instance(res, self.fail, empty_tuple_index): diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 4073836dd973..f04bd777ee4e 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1242,3 +1242,56 @@ from typing import List, Union A = Union[int, List[A]] def func(x: A) -> int: ... [builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsBasic] +from typing import Any, List, assert_type +from typing_extensions import TypeAlias + +Implicit = List +Explicit: TypeAlias = List + +x1: Implicit[str] +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, List[str]) +assert_type(x2, List[Any]) +[builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsGenericClass] +# flags: --python-version 3.9 +from typing import Any, assert_type +from typing_extensions import TypeAlias + +Implicit = list +Explicit: TypeAlias = list + +x1: Implicit[str] +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, list[str]) +assert_type(x2, list[Any]) +[builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsTuple] +from typing import Any, Tuple, assert_type +from typing_extensions import TypeAlias + +Implicit = Tuple +Explicit: TypeAlias = Tuple + +x1: Implicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, Tuple[Any, ...]) +assert_type(x2, Tuple[Any, ...]) +[builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsCallable] +from typing import Any, Callable, assert_type +from typing_extensions import TypeAlias + +Implicit = Callable +Explicit: TypeAlias = Callable + +x1: Implicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, Callable[..., Any]) +assert_type(x2, Callable[..., Any]) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 4acf451e2c34..b7c71c7f37f2 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1563,7 +1563,6 @@ type H[T] = int __main__.A __main__.C __main__.D -__main__.E __main__.G __main__.H From 0901689b5a554ba956f23a3244d4143f6eab7f96 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 21 Dec 2024 04:38:39 +0100 Subject: [PATCH 0978/1617] Revisit the body of a loop if the number of partial types has changed. (#18180) Fixes #5423 --- mypy/checker.py | 5 +++- mypyc/test-data/commandline.test | 4 +-- test-data/unit/check-narrowing.test | 37 ++++++++++++++++++++++++++ test-data/unit/fixtures/primitives.pyi | 1 + 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index fafc857654a7..05345f176603 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -607,11 +607,14 @@ def accept_loop( """ # The outer frame accumulates the results of all iterations with self.binder.frame_context(can_skip=False, conditional_frame=True): + partials_old = sum(len(pts.map) for pts in self.partial_types) while True: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): self.accept(body) - if not self.binder.last_pop_changed: + partials_new = sum(len(pts.map) for pts in self.partial_types) + if (partials_new == partials_old) and not self.binder.last_pop_changed: break + partials_old = partials_new if exit_condition: _, else_map = self.find_isinstance_check(exit_condition) self.push_type_map(else_map) diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index 0b07c2929273..c5fb7e88dd1a 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -200,9 +200,9 @@ wtvr = next(i for i in range(10) if i == 5) d1 = {1: 2} -# Make sure we can produce an error when we hit the awful None case +# Since PR 18180, the following pattern should pose no problems anymore: def f(l: List[object]) -> None: - x = None # E: Local variable "x" has inferred type None; add an annotation + x = None for i in l: if x is None: x = i diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 285d56ff7e50..ad59af01010c 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2352,3 +2352,40 @@ def fn_while(arg: T) -> None: return None return None [builtins fixtures/primitives.pyi] + +[case testRefinePartialTypeWithinLoop] + +x = None +for _ in range(2): + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.int" + x = 1 +reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +def f() -> bool: ... + +y = None +while f(): + reveal_type(y) # N: Revealed type is "None" \ + # N: Revealed type is "Union[builtins.int, None]" + y = 1 +reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" + +z = [] # E: Need type annotation for "z" (hint: "z: List[] = ...") +def g() -> None: + for i in range(2): + while f(): + if z: + z[0] + "v" # E: Unsupported operand types for + ("int" and "str") + z.append(1) + +class A: + def g(self) -> None: + z = [] # E: Need type annotation for "z" (hint: "z: List[] = ...") + for i in range(2): + while f(): + if z: + z[0] + "v" # E: Unsupported operand types for + ("int" and "str") + z.append(1) + +[builtins fixtures/primitives.pyi] diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index 63128a8ae03d..e7d3e12bd5e6 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -48,6 +48,7 @@ class memoryview(Sequence[int]): class tuple(Generic[T]): def __contains__(self, other: object) -> bool: pass class list(Sequence[T]): + def append(self, v: T) -> None: pass def __iter__(self) -> Iterator[T]: pass def __contains__(self, other: object) -> bool: pass def __getitem__(self, item: int) -> T: pass From 924f818f902bd63b2363b4a62a86430e570e2b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 21 Dec 2024 04:41:22 +0100 Subject: [PATCH 0979/1617] Fix typos (#18317) Found few misspellings. Please help me if it comes to deprecation or failing tests. This is the best spellchecker out there: https://github.com/crate-ci/typos --- CHANGELOG.md | 4 ++-- CONTRIBUTING.md | 2 +- docs/source/common_issues.rst | 2 +- misc/docker/README.md | 2 +- misc/trigger_wheel_build.sh | 2 +- mypy/checker.py | 6 +++--- mypy/checkexpr.py | 4 ++-- mypy/dmypy_server.py | 2 +- mypy/errors.py | 2 +- mypy/inspections.py | 2 +- mypy/message_registry.py | 2 +- mypy/nodes.py | 2 +- mypy/plugins/common.py | 2 +- mypy/plugins/enums.py | 2 +- mypy/semanal.py | 6 +++--- mypy/solve.py | 2 +- mypy/stubgenc.py | 2 +- mypy/test/testconstraints.py | 4 ++-- mypy/test/testinfer.py | 2 +- mypy/test/testpythoneval.py | 6 +++--- mypy/test/update_data.py | 2 +- mypy/typeanal.py | 2 +- mypy/typeshed/stdlib/_tkinter.pyi | 2 +- mypy/typeshed/stdlib/email/message.pyi | 2 +- mypyc/analysis/attrdefined.py | 4 ++-- mypyc/codegen/emit.py | 2 +- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/irbuild/match.py | 2 +- mypyc/lib-rt/CPy.h | 4 ++-- mypyc/lib-rt/int_ops.c | 2 +- mypyc/lib-rt/misc_ops.c | 4 ++-- mypyc/primitives/exc_ops.py | 2 +- mypyc/primitives/set_ops.py | 2 +- mypyc/test-data/irbuild-basic.test | 8 ++++---- mypyc/test-data/irbuild-classes.test | 2 +- mypyc/test-data/irbuild-dict.test | 10 +++++----- mypyc/test-data/irbuild-match.test | 2 +- mypyc/test-data/irbuild-set.test | 14 +++++++------- mypyc/test-data/irbuild-statements.test | 10 +++++----- mypyc/test-data/refcount.test | 2 +- mypyc/test-data/run-bools.test | 6 +++--- mypyc/test-data/run-dunders-special.test | 2 +- mypyc/test-data/run-functions.test | 6 +++--- test-data/unit/check-annotated.test | 2 +- test-data/unit/check-classes.test | 2 +- test-data/unit/check-classvar.test | 2 +- test-data/unit/check-dataclass-transform.test | 2 +- test-data/unit/check-enum.test | 4 ++-- test-data/unit/check-functions.test | 4 ++-- test-data/unit/check-generics.test | 2 +- test-data/unit/check-inference.test | 2 +- test-data/unit/check-literal.test | 2 +- test-data/unit/check-modules.test | 2 +- test-data/unit/check-parameter-specification.test | 2 +- test-data/unit/check-protocols.test | 4 ++-- test-data/unit/check-python310.test | 8 ++++---- test-data/unit/check-typeddict.test | 2 +- test-data/unit/cmdline.pyproject.test | 6 +++--- test-data/unit/deps.test | 2 +- test-data/unit/fine-grained.test | 6 +++--- test-data/unit/semanal-basic.test | 2 +- test-data/unit/semanal-errors.test | 2 +- 62 files changed, 106 insertions(+), 106 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c3ed16ddbb..bae57dd1b0e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,7 +184,7 @@ This was contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/ * Improve contributor documentation for Windows (ag-tafe, PR [18097](https://github.com/python/mypy/pull/18097)) * Correct note about `--disallow-any-generics` flag in documentation (Abel Sen, PR [18055](https://github.com/python/mypy/pull/18055)) * Further caution against `--follow-imports=skip` (Shantanu, PR [18048](https://github.com/python/mypy/pull/18048)) - * Fix the edit page buttton link in documentation (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) + * Fix the edit page button link in documentation (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) ### Other Notables Fixes and Improvements @@ -751,7 +751,7 @@ This feature was contributed by Jukka Lehtosalo (PR [17404](https://github.com/p ### Mypyc Improvements -Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is signficantly faster basic operations on `int` values. +Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is significantly faster basic operations on `int` values. * Support Python 3.12 syntax for generic functions and classes (Jukka Lehtosalo, PR [17357](https://github.com/python/mypy/pull/17357)) * Support Python 3.12 type alias syntax (Jukka Lehtosalo, PR [17384](https://github.com/python/mypy/pull/17384)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24f7e516e9e2..89d667dfb6ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ articulated in the [Python Community Code of Conduct](https://www.python.org/psf #### (1) Fork the mypy repository -Within Github, navigate to and fork the repository. +Within GitHub, navigate to and fork the repository. #### (2) Clone the mypy repository and enter into it diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 61b71c108ea0..4cb00e55c2f3 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -757,7 +757,7 @@ type check such code. Consider this example: x: int = 'abc' # Unreachable -- no error It's easy to see that any statement after ``return`` is unreachable, -and hence mypy will not complain about the mis-typed code below +and hence mypy will not complain about the mistyped code below it. For a more subtle example, consider this code: .. code-block:: python diff --git a/misc/docker/README.md b/misc/docker/README.md index 839f9761cb03..0e9a3a80ff0e 100644 --- a/misc/docker/README.md +++ b/misc/docker/README.md @@ -12,7 +12,7 @@ Why use Docker? Mypyc tests can be significantly faster in a Docker container than running natively on macOS. -Also, if it's inconvient to install the necessary dependencies on the +Also, if it's inconvenient to install the necessary dependencies on the host operating system, or there are issues getting some tests to pass on the host operating system, using a container can be an easy workaround. diff --git a/misc/trigger_wheel_build.sh b/misc/trigger_wheel_build.sh index c914a6e7cf86..a2608d93f349 100755 --- a/misc/trigger_wheel_build.sh +++ b/misc/trigger_wheel_build.sh @@ -3,7 +3,7 @@ # Trigger a build of mypyc compiled mypy wheels by updating the mypy # submodule in the git repo that drives those builds. -# $WHEELS_PUSH_TOKEN is stored in Github Settings and is an API token +# $WHEELS_PUSH_TOKEN is stored in GitHub Settings and is an API token # for the mypy-build-bot account. git config --global user.email "nobody" diff --git a/mypy/checker.py b/mypy/checker.py index 05345f176603..b2c4f2263262 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2597,7 +2597,7 @@ def check_enum(self, defn: ClassDef) -> None: if isinstance(sym.node, Var) and sym.node.has_explicit_value: # `__members__` will always be overwritten by `Enum` and is considered # read-only so we disallow assigning a value to it - self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN, sym.node) + self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDDEN, sym.node) for base in defn.info.mro[1:-1]: # we don't need self and `object` if base.is_enum and base.fullname not in ENUM_BASES: self.check_final_enum(defn, base) @@ -3645,7 +3645,7 @@ def is_assignable_slot(self, lvalue: Lvalue, typ: Type | None) -> bool: typ = get_proper_type(typ) if typ is None or isinstance(typ, AnyType): - return True # Any can be literally anything, like `@propery` + return True # Any can be literally anything, like `@property` if isinstance(typ, Instance): # When working with instances, we need to know if they contain # `__set__` special method. Like `@property` does. @@ -8524,7 +8524,7 @@ def group_comparison_operands( x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 - If we get these expressions in a pairwise way (e.g. by calling ComparisionExpr's + If we get these expressions in a pairwise way (e.g. by calling ComparisonExpr's 'pairwise()' method), we get the following as input: [('==', x0, x1), ('==', x1, x2), ('<', x2, x3), ('<', x3, x4), diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a00d866d54ec..3ad125cc8bbe 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -844,7 +844,7 @@ def validate_typeddict_kwargs( # Having an optional key not explicitly declared by a ** unpacked # TypedDict is unsafe, it may be an (incompatible) subtype at runtime. # TODO: catch the cases where a declared key is overridden by a subsequent - # ** item without it (and not again overriden with complete ** item). + # ** item without it (and not again overridden with complete ** item). self.msg.non_required_keys_absent_with_star(absent_keys, last_star_found) return result, always_present_keys @@ -5349,7 +5349,7 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: can_skip=True, fall_through=0 ), self.chk.scope.push_function(e): # Lambdas can have more than one element in body, - # when we add "fictional" AssigmentStatement nodes, like in: + # when we add "fictional" AssignmentStatement nodes, like in: # `lambda (a, b): a` for stmt in e.body.body[:-1]: stmt.accept(self.chk) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 70cfaa5b2fb9..10ff07451461 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -714,7 +714,7 @@ def refresh_file(module: str, path: str) -> list[str]: find_changes_time=t1 - t0, fg_update_time=t2 - t1, refresh_suppressed_time=t3 - t2, - find_added_supressed_time=t4 - t3, + find_added_suppressed_time=t4 - t3, cleanup_time=t5 - t4, ) diff --git a/mypy/errors.py b/mypy/errors.py index 1b3f485d19c0..0395a3a0d821 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -268,7 +268,7 @@ class Errors: show_column_numbers: bool = False # Set to True to show end line and end column in error messages. - # Ths implies `show_column_numbers`. + # This implies `show_column_numbers`. show_error_end: bool = False # Set to True to show absolute file paths in error messages. diff --git a/mypy/inspections.py b/mypy/inspections.py index 0baf0896f7e5..bc76ab247901 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -335,7 +335,7 @@ def expr_attrs(self, expression: Expression) -> tuple[str, bool]: node = expression.node names = sorted(node.names) if "__builtins__" in names: - # This is just to make tests stable. No one will really need ths name. + # This is just to make tests stable. No one will really need this name. names.remove("__builtins__") mod_dict = {f'"<{node.fullname}>"': [f'"{name}"' for name in names]} else: diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 346a677a8e85..0c7464246990 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -241,7 +241,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final") # Enum -ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN: Final = ErrorMessage( +ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDDEN: Final = ErrorMessage( 'Assigned "__members__" will be overridden by "Enum" internally' ) diff --git a/mypy/nodes.py b/mypy/nodes.py index a1e1282ef9ee..5f28bde2ceab 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1179,7 +1179,7 @@ def __init__( self.keywords = dict(keywords) if keywords else {} self.analyzed = None self.has_incompatible_baseclass = False - # Used for error reporting (to keep backwad compatibility with pre-3.8) + # Used for error reporting (to keep backward compatibility with pre-3.8) self.deco_line: int | None = None self.docstring: str | None = None self.removed_statements = [] diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index f0ff6f30a3b9..43caa6483236 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -164,7 +164,7 @@ def find_shallow_matching_overload_item(overload: Overloaded, call: CallExpr) -> def _get_callee_type(call: CallExpr) -> CallableType | None: - """Return the type of the callee, regardless of its syntatic form.""" + """Return the type of the callee, regardless of its syntactic form.""" callee_node: Node | None = call.callee diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 816241fa6e9a..86e7f1f7b31e 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -129,7 +129,7 @@ def _implements_new(info: TypeInfo) -> bool: def enum_member_callback(ctx: mypy.plugin.FunctionContext) -> Type: - """By default `member(1)` will be infered as `member[int]`, + """By default `member(1)` will be inferred as `member[int]`, we want to improve the inference to be `Literal[1]` here.""" if ctx.arg_types or ctx.arg_types[0]: arg = get_proper_type(ctx.arg_types[0][0]) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8c74c9c2528c..6e3335aed4e1 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4170,7 +4170,7 @@ def analyze_type_alias_type_params( ) -> tuple[TypeVarLikeList, list[str]]: """Analyze type_params of TypeAliasType. - Returns declared unbound type variable expressions and a list of all decalred type + Returns declared unbound type variable expressions and a list of all declared type variable names for error reporting. """ if "type_params" in rvalue.arg_names: @@ -4436,7 +4436,7 @@ def make_name_lvalue_var( if kind != LDEF: v._fullname = self.qualified_name(name) else: - # fullanme should never stay None + # fullname should never stay None v._fullname = name v.is_ready = False # Type not inferred yet v.has_explicit_value = has_explicit_value @@ -6218,7 +6218,7 @@ def visit_yield_expr(self, e: YieldExpr) -> None: def visit_await_expr(self, expr: AwaitExpr) -> None: if not self.is_func_scope() or not self.function_stack: # We check both because is_function_scope() returns True inside comprehensions. - # This is not a blocker, because some enviroments (like ipython) + # This is not a blocker, because some environments (like ipython) # support top level awaits. self.fail('"await" outside function', expr, serious=True, code=codes.TOP_LEVEL_AWAIT) elif not self.function_stack[-1].is_coroutine: diff --git a/mypy/solve.py b/mypy/solve.py index 8a1495a9a246..4b09baee64c6 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -139,7 +139,7 @@ def solve_with_dependent( * Find dependencies between type variables, group them in SCCs, and sort topologically * Check that all SCC are intrinsically linear, we can't solve (express) T <: List[T] * Variables in leaf SCCs that don't have constant bounds are free (choose one per SCC) - * Solve constraints iteratively starting from leafs, updating bounds after each step. + * Solve constraints iteratively starting from leaves, updating bounds after each step. """ graph, lowers, uppers = transitive_closure(vars, constraints) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 1cd709b9d603..3a2b242638e5 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -241,7 +241,7 @@ def __init__( self.module_name = module_name if self.is_c_module: # Add additional implicit imports. - # C-extensions are given more lattitude since they do not import the typing module. + # C-extensions are given more latitude since they do not import the typing module. self.known_imports.update( { "typing": [ diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index a701a173cbaa..277694a328c9 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -62,7 +62,7 @@ def test_type_var_tuple_with_prefix_and_suffix(self) -> None: Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.d), } - def test_unpack_homogenous_tuple(self) -> None: + def test_unpack_homogeneous_tuple(self) -> None: fx = self.fx assert set( infer_constraints( @@ -77,7 +77,7 @@ def test_unpack_homogenous_tuple(self) -> None: Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b), } - def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None: + def test_unpack_homogeneous_tuple_with_prefix_and_suffix(self) -> None: fx = self.fx assert set( infer_constraints( diff --git a/mypy/test/testinfer.py b/mypy/test/testinfer.py index 08926c179623..107c4d8dc98a 100644 --- a/mypy/test/testinfer.py +++ b/mypy/test/testinfer.py @@ -366,7 +366,7 @@ def test_single_pair(self) -> None: ) def test_empty_pair_list(self) -> None: - # This case should never occur in practice -- ComparisionExprs + # This case should never occur in practice -- ComparisonExprs # always contain at least one comparison. But in case it does... self.assertEqual(group_comparison_operands([], {}, set()), []) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index baeea1853ded..32c07087292e 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -63,9 +63,9 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None additional_flags = m.group(1).split() for flag in additional_flags: if flag.startswith("--python-version="): - targetted_python_version = flag.split("=")[1] - targetted_major, targetted_minor = targetted_python_version.split(".") - if (int(targetted_major), int(targetted_minor)) > ( + targeted_python_version = flag.split("=")[1] + targeted_major, targeted_minor = targeted_python_version.split(".") + if (int(targeted_major), int(targeted_minor)) > ( sys.version_info.major, sys.version_info.minor, ): diff --git a/mypy/test/update_data.py b/mypy/test/update_data.py index 2d66752f61bd..2e1a6a9b3d1d 100644 --- a/mypy/test/update_data.py +++ b/mypy/test/update_data.py @@ -69,7 +69,7 @@ def _iter_fixes( source_line = source_line[: comment_match.start("indent")] # strip old comment if reports: indent = comment_match.group("indent") if comment_match else " " - # multiline comments are on the first line and then on subsequent lines emtpy lines + # multiline comments are on the first line and then on subsequent lines empty lines # with a continuation backslash for j, (severity, msg) in enumerate(reports): out_l = source_line if j == 0 else " " * len(source_line) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 32aad5ba4089..751ed85ea6f3 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1000,7 +1000,7 @@ def analyze_unbound_type_without_type_info( elif unbound_tvar: assert isinstance(sym.node, TypeVarLikeExpr) if sym.node.is_new_style: - # PEP 695 type paramaters are never considered unbound -- they are undefined + # PEP 695 type parameters are never considered unbound -- they are undefined # in contexts where they aren't valid, such as in argument default values. message = 'Name "{}" is not defined' name = name.split(".")[-1] diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index 4206a2114f95..bd41b9ebc78e 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -82,7 +82,7 @@ class TkappType: def mainloop(self, threshold: int = 0, /): ... def quit(self): ... def record(self, script, /): ... - def setvar(self, *ags, **kwargs): ... + def setvar(self, *args, **kwargs): ... if sys.version_info < (3, 11): def split(self, arg, /): ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index ebad05a1cf7b..8993a3217185 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -153,7 +153,7 @@ class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def attach(self, payload: Self) -> None: ... # type: ignore[override] # The attachments are created via type(self) in the attach method. It's theoretically # possible to sneak other attachment types into a MIMEPart instance, but could cause - # cause unforseen consequences. + # cause unforeseen consequences. def iter_attachments(self) -> Iterator[Self]: ... def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ... diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py index 350158246cdb..e4038bfaa238 100644 --- a/mypyc/analysis/attrdefined.py +++ b/mypyc/analysis/attrdefined.py @@ -176,7 +176,7 @@ def analyze_always_defined_attrs_in_class(cl: ClassIR, seen: set[ClassIR]) -> No m.blocks, self_reg, maybe_defined, dirty ) - mark_attr_initialiation_ops(m.blocks, self_reg, maybe_defined, dirty) + mark_attr_initialization_ops(m.blocks, self_reg, maybe_defined, dirty) # Check if __init__ can run unpredictable code (leak 'self'). any_dirty = False @@ -260,7 +260,7 @@ def find_sometimes_defined_attributes( return attrs -def mark_attr_initialiation_ops( +def mark_attr_initialization_ops( blocks: list[BasicBlock], self_reg: Register, maybe_defined: AnalysisResult[str], diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index fce6896e8d11..97302805fd3b 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -195,7 +195,7 @@ def attr(self, name: str) -> str: return ATTR_PREFIX + name def object_annotation(self, obj: object, line: str) -> str: - """Build a C comment with an object's string represention. + """Build a C comment with an object's string representation. If the comment exceeds the line length limit, it's wrapped into a multiline string (with the extra lines indented to be aligned with diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index bbfe14a68c93..7216826906cb 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1,6 +1,6 @@ """A "low-level" IR builder class. -See the docstring of class LowLevelIRBuiler for more information. +See the docstring of class LowLevelIRBuilder for more information. """ @@ -439,7 +439,7 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - size = target_type.size if size < int_rprimitive.size: - # Add a range check when the target type is smaller than the source tyoe + # Add a range check when the target type is smaller than the source type fast2, fast3 = BasicBlock(), BasicBlock() upper_bound = 1 << (size * 8 - 1) if not target_type.is_signed: diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index 976a8810b327..ee7b6027bbda 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -124,7 +124,7 @@ def visit_or_pattern(self, pattern: OrPattern) -> None: def visit_class_pattern(self, pattern: ClassPattern) -> None: # TODO: use faster instance check for native classes (while still - # making sure to account for inheritence) + # making sure to account for inheritance) isinstance_op = ( fast_isinstance_op if self.builder.is_builtin_ref_expr(pattern.class_ref) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index d3637cde49ff..1e6f50306ba1 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -785,7 +785,7 @@ static inline PyObject *_CPy_FromDummy(PyObject *p) { return p; } -static int CPy_NoErrOccured(void) { +static int CPy_NoErrOccurred(void) { return PyErr_Occurred() == NULL; } @@ -856,7 +856,7 @@ PyObject *CPy_FetchStopIterationValue(void); PyObject *CPyType_FromTemplate(PyObject *template_, PyObject *orig_bases, PyObject *modname); -PyObject *CPyType_FromTemplateWarpper(PyObject *template_, +PyObject *CPyType_FromTemplateWrapper(PyObject *template_, PyObject *orig_bases, PyObject *modname); int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 9b5d4ef65fb1..b7fff2535c12 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -124,7 +124,7 @@ CPyTagged CPyTagged_Add_(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -// Tagged int subraction slow path, where the result may be a long integer +// Tagged int subtraction slow path, where the result may be a long integer CPyTagged CPyTagged_Subtract_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index d3e8e69ed19b..a2b03e7df247 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -897,7 +897,7 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, } -// Adapated from ceval.c GET_AITER +// Adapted from ceval.c GET_AITER PyObject *CPy_GetAIter(PyObject *obj) { unaryfunc getter = NULL; @@ -935,7 +935,7 @@ PyObject *CPy_GetAIter(PyObject *obj) return iter; } -// Adapated from ceval.c GET_ANEXT +// Adapted from ceval.c GET_ANEXT PyObject *CPy_GetANext(PyObject *aiter) { unaryfunc getter = NULL; diff --git a/mypyc/primitives/exc_ops.py b/mypyc/primitives/exc_ops.py index ad105056158a..9a5f6392a917 100644 --- a/mypyc/primitives/exc_ops.py +++ b/mypyc/primitives/exc_ops.py @@ -41,7 +41,7 @@ no_err_occurred_op = custom_op( arg_types=[], return_type=bit_rprimitive, - c_function_name="CPy_NoErrOccured", + c_function_name="CPy_NoErrOccurred", error_kind=ERR_FALSE, ) diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index fcfb7847dc7d..a0313861fb30 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -22,7 +22,7 @@ # Get the 'builtins.set' type object. load_address_op(name="builtins.set", type=object_rprimitive, src="PySet_Type") -# Get the 'builtins.frozenset' tyoe object. +# Get the 'builtins.frozenset' type object. load_address_op(name="builtins.frozenset", type=object_rprimitive, src="PyFrozenSet_Type") # Construct an empty set. diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index a43e0d0ada56..835543168a6b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -2711,7 +2711,7 @@ L4: L5: goto L1 L6: - r5 = CPy_NoErrOccured() + r5 = CPy_NoErrOccurred() L7: L8: return r0 @@ -2740,7 +2740,7 @@ L4: L5: goto L1 L6: - r6 = CPy_NoErrOccured() + r6 = CPy_NoErrOccurred() L7: L8: return r0 @@ -2780,7 +2780,7 @@ L2: L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r10 = CPy_NoErrOccurred() L5: return r0 @@ -3301,7 +3301,7 @@ L2: L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r10 = CPy_NoErrOccurred() L5: return 1 def range_in_loop(): diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 2c15f09c9c34..dbc1f8927669 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -613,7 +613,7 @@ class C: class D: @classmethod def f(cls, x: int) -> int: - # TODO: This could aso be optimized, since g is not ever overridden + # TODO: This could also be optimized, since g is not ever overridden return cls.g(x) @classmethod diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 6139a02029b9..68c9ccb9f0e5 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -186,7 +186,7 @@ L3: r14 = CPyDict_CheckSize(d, r2) goto L1 L4: - r15 = CPy_NoErrOccured() + r15 = CPy_NoErrOccurred() L5: return d @@ -295,7 +295,7 @@ L5: r13 = CPyDict_CheckSize(d1, r2) goto L1 L6: - r14 = CPy_NoErrOccured() + r14 = CPy_NoErrOccurred() L7: r15 = 0 r16 = PyDict_Size(d2) @@ -325,7 +325,7 @@ L10: r33 = CPyDict_CheckSize(d2, r17) goto L8 L11: - r34 = CPy_NoErrOccured() + r34 = CPy_NoErrOccurred() L12: return 1 def union_of_dicts(d): @@ -377,7 +377,7 @@ L3: r18 = CPyDict_CheckSize(d, r3) goto L1 L4: - r19 = CPy_NoErrOccured() + r19 = CPy_NoErrOccurred() L5: return 1 def typeddict(d): @@ -436,7 +436,7 @@ L8: r17 = CPyDict_CheckSize(d, r2) goto L1 L9: - r18 = CPy_NoErrOccured() + r18 = CPy_NoErrOccurred() L10: return 1 diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index ab5a19624ba6..ba9a0d5464ea 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -148,7 +148,7 @@ L2: L3: r10 = box(None, 1) return r10 -[case testMatchExaustivePattern_python3_10] +[case testMatchExhaustivePattern_python3_10] def f(): match 123: case _: diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 42429cf7072e..c1a00ce67504 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -157,7 +157,7 @@ L2: L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r10 = CPy_NoErrOccurred() L5: b = r1 return 1 @@ -211,7 +211,7 @@ L3: r21 = CPyDict_CheckSize(tmp_dict, r10) goto L1 L4: - r22 = CPy_NoErrOccured() + r22 = CPy_NoErrOccurred() L5: c = r7 return 1 @@ -393,7 +393,7 @@ L8: L9: goto L7 L10: - r30 = CPy_NoErrOccured() + r30 = CPy_NoErrOccurred() L11: r31 = PyObject_GetIter(r8) r32 = PyObject_GetIter(r31) @@ -410,7 +410,7 @@ L13: L14: goto L12 L15: - r39 = CPy_NoErrOccured() + r39 = CPy_NoErrOccurred() L16: a = r7 return 1 @@ -752,7 +752,7 @@ L2: L3: goto L1 L4: - r4 = CPy_NoErrOccured() + r4 = CPy_NoErrOccurred() L5: return 1 def precomputed2(): @@ -770,7 +770,7 @@ L2: L3: goto L1 L4: - r3 = CPy_NoErrOccured() + r3 = CPy_NoErrOccurred() L5: return 1 def not_precomputed(): @@ -804,6 +804,6 @@ L2: L3: goto L1 L4: - r11 = CPy_NoErrOccured() + r11 = CPy_NoErrOccurred() L5: return 1 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index cc9d98be51c9..c85dcb09e80a 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -301,7 +301,7 @@ L3: r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r13 = CPy_NoErrOccured() + r13 = CPy_NoErrOccurred() L5: return 1 @@ -368,7 +368,7 @@ L5: r18 = CPyDict_CheckSize(d, r2) goto L1 L6: - r19 = CPy_NoErrOccured() + r19 = CPy_NoErrOccurred() L7: return s @@ -917,7 +917,7 @@ L3: r0 = r4 goto L1 L4: - r5 = CPy_NoErrOccured() + r5 = CPy_NoErrOccurred() L5: return 1 @@ -978,7 +978,7 @@ L6: r0 = r12 goto L1 L7: - r13 = CPy_NoErrOccured() + r13 = CPy_NoErrOccurred() L8: return 1 def g(a, b): @@ -1027,7 +1027,7 @@ L5: z = r12 goto L1 L6: - r13 = CPy_NoErrOccured() + r13 = CPy_NoErrOccurred() L7: return 1 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index e719ecb2afe1..c84ddfd73ba2 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -771,7 +771,7 @@ L3: r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r13 = CPy_NoErrOccured() + r13 = CPy_NoErrOccurred() L5: return 1 L6: diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test index d7a2aa37ade7..a0b8ea31ebc0 100644 --- a/mypyc/test-data/run-bools.test +++ b/mypyc/test-data/run-bools.test @@ -31,7 +31,7 @@ def test_if() -> None: assert f(False) is True def test_bitwise_and() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t & t == True @@ -44,7 +44,7 @@ def test_bitwise_and() -> None: assert t == False def test_bitwise_or() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t | t == True @@ -57,7 +57,7 @@ def test_bitwise_or() -> None: assert f == True def test_bitwise_xor() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t ^ t == False diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test index cd02cca65eef..30c618374f88 100644 --- a/mypyc/test-data/run-dunders-special.test +++ b/mypyc/test-data/run-dunders-special.test @@ -1,7 +1,7 @@ [case testDundersNotImplemented] # This case is special because it tests the behavior of NotImplemented # used in a typed function which return type is bool. -# This is a convention that can be overriden by the user. +# This is a convention that can be overridden by the user. class UsesNotImplemented: def __eq__(self, b: object) -> bool: return NotImplemented diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index cf519f30dad8..ac4894bad304 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -140,7 +140,7 @@ def triple(a: int) -> Callable[[], Callable[[int], int]]: return outer def if_else(flag: int) -> str: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'if_else.dummy_function' if flag < 0: @@ -155,7 +155,7 @@ def if_else(flag: int) -> str: return inner() def for_loop() -> int: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'for_loop.dummy_function' for i in range(5): @@ -166,7 +166,7 @@ def for_loop() -> int: return 0 def while_loop() -> int: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'while_loop.dummy_function' i = 0 diff --git a/test-data/unit/check-annotated.test b/test-data/unit/check-annotated.test index d4309b8ad213..47fe33bfb42a 100644 --- a/test-data/unit/check-annotated.test +++ b/test-data/unit/check-annotated.test @@ -139,7 +139,7 @@ reveal_type(f2) # N: Revealed type is "def (a: builtins.str) -> Any" def f3(a: Annotated["notdefined", "metadata"]): # E: Name "notdefined" is not defined pass T = TypeVar('T') -def f4(a: Annotated[T, "metatdata"]): +def f4(a: Annotated[T, "metadata"]): pass reveal_type(f4) # N: Revealed type is "def [T] (a: T`-1) -> Any" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a3d35da15107..5ccb9fa06c34 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2219,7 +2219,7 @@ reveal_type(B() + A()) # N: Revealed type is "__main__.A" reveal_type(A() + B()) # N: Revealed type is "__main__.A" [builtins fixtures/isinstance.pyi] -[case testBinaryOpeartorMethodPositionalArgumentsOnly] +[case testBinaryOperatorMethodPositionalArgumentsOnly] class A: def __add__(self, other: int) -> int: pass def __iadd__(self, other: int) -> int: pass diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test index 1e87e441dea2..918926627bfd 100644 --- a/test-data/unit/check-classvar.test +++ b/test-data/unit/check-classvar.test @@ -200,7 +200,7 @@ f().x = 0 [out] main:6: error: Cannot assign to class variable "x" via instance -[case testOverrideWithIncomatibleType] +[case testOverrideWithIncompatibleType] from typing import ClassVar class A: x = None # type: ClassVar[int] diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 51b2e186214f..8213f8df282a 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -118,7 +118,7 @@ from typing import dataclass_transform, Type BOOLEAN_CONSTANT = True -@dataclass_transform(nonexistant=True) # E: Unrecognized dataclass_transform parameter "nonexistant" +@dataclass_transform(nonexistent=True) # E: Unrecognized dataclass_transform parameter "nonexistent" def foo(cls: Type) -> Type: return cls diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index e6e42d805052..b67bb566224e 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -154,7 +154,7 @@ def infer_truth(truth: Truth) -> None: reveal_type(truth.value) # N: Revealed type is "builtins.str" [builtins fixtures/primitives.pyi] -[case testEnumValueInhomogenous] +[case testEnumValueInhomogeneous] from enum import Enum class Truth(Enum): true = 'True' @@ -2163,7 +2163,7 @@ class Mixed(Enum): reveal_type(Mixed.b.value) # N: Revealed type is "None" # Inferring Any here instead of a union seems to be a deliberate - # choice; see the testEnumValueInhomogenous case above. + # choice; see the testEnumValueInhomogeneous case above. reveal_type(self.value) # N: Revealed type is "Any" for field in Mixed: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index b8a02a1ec7d4..e4b8c31e8b46 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1781,7 +1781,7 @@ def a(f: F): f("foo") # E: Argument 1 has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] -[case testCallableParsingInInheritence] +[case testCallableParsingInInheritance] from collections import namedtuple class C(namedtuple('t', 'x')): @@ -2349,7 +2349,7 @@ a.__eq__(other=a) # E: Unexpected keyword argument "other" for "__eq__" of "A" [builtins fixtures/bool.pyi] --- Type variable shenanagins +-- Type variable shenanigans -- ------------------------- [case testGenericFunctionTypeDecl] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 5791b9c471d5..74003f824e5d 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -713,7 +713,7 @@ reveal_type(z) # N: Revealed type is "__main__.Node[Any, Any]" [out] -[case testGenericTypeAliasesAcessingMethods] +[case testGenericTypeAliasesAccessingMethods] from typing import TypeVar, Generic, List T = TypeVar('T') class Node(Generic[T]): diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 5a99e65c9c90..bec3a9a07593 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1669,7 +1669,7 @@ class A: self.a.append('') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" [builtins fixtures/list.pyi] -[case testInferListInitializedToEmptyInClassBodyAndOverriden] +[case testInferListInitializedToEmptyInClassBodyAndOverridden] from typing import List class A: diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index cff6e07670a7..296956334d20 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2138,7 +2138,7 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [builtins fixtures/primitives.pyi] [out] -[case testLiteralFinalDirectInstanceTypesSupercedeInferredLiteral] +[case testLiteralFinalDirectInstanceTypesSupersedeInferredLiteral] from typing_extensions import Final, Literal var1: Final[int] = 1 diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 68897790e4bf..bee0984c0c03 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2927,7 +2927,7 @@ reveal_type(abc.__name__) # N: Revealed type is "builtins.str" reveal_type(builtins.__name__) # N: Revealed type is "builtins.str" reveal_type(typing.__name__) # N: Revealed type is "builtins.str" -[case testSpecialAttrsAreAvaliableInClasses] +[case testSpecialAttrsAreAvailableInClasses] class Some: name = __name__ reveal_type(Some.name) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 7f038b811741..fca72f3bebc3 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1193,7 +1193,7 @@ def func(callback: Callable[P, str]) -> Callable[P, str]: return inner [builtins fixtures/paramspec.pyi] -[case testParamSpecArgsAndKwargsMissmatch] +[case testParamSpecArgsAndKwargsMismatch] from typing import Callable from typing_extensions import ParamSpec diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index dd19eb1f21d6..0571c1729302 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -667,7 +667,7 @@ def fun4(x: U, y: P[U, U]) -> U: pass reveal_type(fun4('a', C())) # N: Revealed type is "builtins.object" -[case testUnrealtedGenericProtolsEquivalent] +[case testUnrealtedGenericProtocolsEquivalent] from typing import TypeVar, Protocol T = TypeVar('T') @@ -4185,7 +4185,7 @@ class WriteToMeOrReadFromMe(WriteToMe[AnyStr], SupportsRead[AnyStr]): ... copyfileobj(WriteToMeOrReadFromMe[bytes](), WriteToMe[bytes]()) -[case testOverloadedMethodWithExplictSelfTypes] +[case testOverloadedMethodWithExplicitSelfTypes] from typing import Generic, overload, Protocol, TypeVar, Union AnyStr = TypeVar("AnyStr", str, bytes) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 0231b47cf4a0..616846789c98 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1915,7 +1915,7 @@ class Regular: x: str y: int __match_args__ = ("x",) -class ReveresedOrder: +class ReversedOrder: x: int y: str __match_args__ = ("y",) @@ -1933,7 +1933,7 @@ class GenericDataclass(Generic[T]): input_arg: Union[ Regular, - ReveresedOrder, + ReversedOrder, GenericRegular[str], GenericWithFinal[str], RegularSubtype, @@ -1944,7 +1944,7 @@ input_arg: Union[ match input_arg: case Regular(a): reveal_type(a) # N: Revealed type is "builtins.str" - case ReveresedOrder(a): + case ReversedOrder(a): reveal_type(a) # N: Revealed type is "builtins.str" case GenericWithFinal(a): reveal_type(a) # N: Revealed type is "builtins.str" @@ -1959,7 +1959,7 @@ match input_arg: match input_arg: case Regular(x=a): reveal_type(a) # N: Revealed type is "builtins.str" - case ReveresedOrder(x=b): # Order is different + case ReversedOrder(x=b): # Order is different reveal_type(b) # N: Revealed type is "builtins.int" case GenericWithFinal(x=a): reveal_type(a) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 5234ced8ea86..5515cfc61b10 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3015,7 +3015,7 @@ class Bar(TypedDict): b: int foo: Foo = {"a": 1, "b": "a"} -bar1: Bar = {**foo, "b": 2} # Incompatible item is overriden +bar1: Bar = {**foo, "b": 2} # Incompatible item is overridden bar2: Bar = {**foo, "a": 2} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index 831bce2eb63d..57e6facad032 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -92,7 +92,7 @@ exclude = '''(?x)( [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! @@ -107,7 +107,7 @@ exclude = """(?x)( [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! @@ -122,7 +122,7 @@ exclude = [ [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 84cea99bf2f6..757bd9541fc9 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -749,7 +749,7 @@ class C: -> m.outer -> m, m.outer -[case testDecoratorDepsDeeepNested] +[case testDecoratorDepsDeepNested] import mod def outer() -> None: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 9ff8a37ae9ae..19564133e375 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -2369,7 +2369,7 @@ a.py:7: error: "B" has no attribute "x" == a.py:5: error: Missing positional argument "x" in call to "C" -[case testDecoratorUpdateDeeepNested] +[case testDecoratorUpdateDeepNested] import a [file a.py] import mod @@ -6966,7 +6966,7 @@ class A: == main:3: error: "A" has no attribute "__iter__" (not iterable) -[case testWeAreCarefullWithBuiltinProtocolsBase] +[case testWeAreCarefulWithBuiltinProtocolsBase] import a x: a.A for i in x: @@ -7119,7 +7119,7 @@ class AS: == main:9: error: Incompatible types in assignment (expression has type "int", variable has type "str") -[case testOverloadsUpdatedTypeRechekConsistency] +[case testOverloadsUpdatedTypeRecheckConsistency] from typing import overload import mod class Outer: diff --git a/test-data/unit/semanal-basic.test b/test-data/unit/semanal-basic.test index 169769f06a00..1f03ed22648d 100644 --- a/test-data/unit/semanal-basic.test +++ b/test-data/unit/semanal-basic.test @@ -316,7 +316,7 @@ MypyFile:1( NameExpr(x* [l]) NameExpr(None [builtins.None]))))) -[case testGlobaWithinMethod] +[case testGlobalWithinMethod] x = None class A: def f(self): diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 2f0a4c140915..5e7da27f17cb 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1475,7 +1475,7 @@ from typing_extensions import Unpack from typing import Tuple heterogenous_tuple: Tuple[Unpack[Tuple[int, str]]] -homogenous_tuple: Tuple[Unpack[Tuple[int, ...]]] +homogeneous_tuple: Tuple[Unpack[Tuple[int, ...]]] bad: Tuple[Unpack[int]] # E: "int" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/tuple.pyi] From ceaf48d3f98e1d8dd25a2760f5b466a5d5926ee3 Mon Sep 17 00:00:00 2001 From: Gareth Date: Sat, 21 Dec 2024 14:42:51 -0800 Subject: [PATCH 0980/1617] Print InspectError traceback in stubgen `walk_packages` when verbose is specified (#18224) This change modifies `walk_packages` such that the full `ImporError` traceback is printed when a module cannot be imported. The goal is to provide the user with more context to debug the error. I implemented this change by mirroring existing behavior in `find_module_paths_using_imports`: https://github.com/python/mypy/blob/9405bfd9205ea369c11150907764fa46c03cb1f7/mypy/stubgen.py#L1522-L1529 --- mypy/stubgen.py | 2 +- mypy/stubutil.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index fdad5c2ddd89..b197f4a57123 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1524,7 +1524,7 @@ def find_module_paths_using_imports( except CantImport as e: tb = traceback.format_exc() if verbose: - sys.stdout.write(tb) + sys.stderr.write(tb) if not quiet: report_missing(mod, e.message, tb) continue diff --git a/mypy/stubutil.py b/mypy/stubutil.py index c11843c57f2a..8ccf8301ee43 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -5,6 +5,7 @@ import os.path import re import sys +import traceback from abc import abstractmethod from collections import defaultdict from contextlib import contextmanager @@ -70,6 +71,9 @@ def walk_packages( try: prop = inspect.get_package_properties(package_name) except InspectError: + if verbose: + tb = traceback.format_exc() + sys.stderr.write(tb) report_missing(package_name) continue yield prop.name From 9ff9946ca5476da00760e05519244a5adb070971 Mon Sep 17 00:00:00 2001 From: Cameron Matsui <127134527+cam-matsui@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:36:52 -0500 Subject: [PATCH 0981/1617] Fix typo in `generics.rst` (#18332) --- docs/source/generics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 4ba6d322417d..731365d3789b 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -284,7 +284,7 @@ and the return type is derived from the sequence item type. Example: When using the legacy syntax, a single definition of a type variable (such as ``T`` above) can be used in multiple generic functions or classes. In this example we use the same type variable in two generic -functions to declarare type parameters: +functions to declare type parameters: .. code-block:: python From 2d6b5219878526e3bf5472dfc5937fd621ebe4ad Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 Dec 2024 04:56:20 +0100 Subject: [PATCH 0982/1617] Drop support for running with Python 3.8 (#17492) Similar to last year (#15566), start by dropping support for running mypy with Python 3.8. Users will still be able to type check 3.8 code with `--python-version 3.8` until typeshed drops the support for it. It's a bit early as the EOL for Python 3.8 is in ~3 months. However, since the branch for `1.11.0` has been cut already, we'd only drop the support with `1.12.0` which isn't due for another 1-2 months. Additionally dropping `3.8` now will make it easier to support `3.13` with its C-API changes and also give us enough time to cleanup the remaining 3.8 code blocks and documentation references. --------- Co-authored-by: Ali Hamdan --- .github/workflows/test.yml | 28 ++++++++----------- .github/workflows/test_stubgenc.yml | 4 +-- CONTRIBUTING.md | 2 +- docs/source/getting_started.rst | 2 +- mypy/defaults.py | 2 +- mypy/test/meta/test_parse_data.py | 6 ++-- mypy/util.py | 6 ++-- mypy_self_check.ini | 2 +- mypyc/doc/getting_started.rst | 2 +- mypyc/test/test_run.py | 1 + pyproject.toml | 3 +- setup.py | 4 +-- test-data/unit/check-columns.test | 2 +- test-data/unit/check-errorcodes.test | 2 +- test-data/unit/check-functools.test | 1 + test-data/unit/check-generics.test | 2 ++ test-data/unit/check-incremental.test | 4 +-- .../unit/fine-grained-cache-incremental.test | 6 ++-- test-data/unit/fine-grained.test | 2 +- test-data/unit/parse-errors.test | 4 +-- test-requirements.in | 2 +- 21 files changed, 42 insertions(+), 45 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e6c9cd1d9b3..97bc62e002c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,24 +31,18 @@ jobs: include: # Make sure to run mypyc compiled unit tests for both # the oldest and newest supported Python versions - - name: Test suite with py38-ubuntu, mypyc-compiled - python: '3.8' + - name: Test suite with py39-ubuntu, mypyc-compiled + python: '3.9' arch: x64 os: ubuntu-latest toxenv: py tox_extra_args: "-n 4" test_mypyc: true - - name: Test suite with py38-windows-64 - python: '3.8' - arch: x64 - os: windows-latest - toxenv: py38 - tox_extra_args: "-n 4" - - name: Test suite with py39-ubuntu + - name: Test suite with py39-windows-64 python: '3.9' arch: x64 - os: ubuntu-latest - toxenv: py + os: windows-latest + toxenv: py39 tox_extra_args: "-n 4" - name: Test suite with py310-ubuntu python: '3.10' @@ -88,7 +82,7 @@ jobs: # test_mypyc: true - name: mypyc runtime tests with py39-macos - python: '3.9.18' + python: '3.9.21' arch: x64 # TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version os: macos-13 @@ -98,20 +92,20 @@ jobs: # - https://github.com/python/mypy/issues/17819 # - https://github.com/python/mypy/pull/17822 # - name: mypyc runtime tests with py38-debug-build-ubuntu - # python: '3.8.17' + # python: '3.9.21' # arch: x64 # os: ubuntu-latest # toxenv: py # tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" # debug_build: true - - name: Type check our own code (py38-ubuntu) - python: '3.8' + - name: Type check our own code (py39-ubuntu) + python: '3.9' arch: x64 os: ubuntu-latest toxenv: type - - name: Type check our own code (py38-windows-64) - python: '3.8' + - name: Type check our own code (py39-windows-64) + python: '3.9' arch: x64 os: windows-latest toxenv: type diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 0652702a0fc0..115eb047556e 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -30,10 +30,10 @@ jobs: - uses: actions/checkout@v4 - - name: Setup 🐍 3.8 + - name: Setup 🐍 3.9 uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.9 - name: Test stubgenc run: misc/test-stubgenc.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89d667dfb6ce..e782158ba21f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ hash -r # This resets shell PATH cache, not necessary on Windows ``` > **Note** -> You'll need Python 3.8 or higher to install all requirements listed in +> You'll need Python 3.9 or higher to install all requirements listed in > test-requirements.txt ### Running tests diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 28a4481e502e..9b510314fd8f 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -16,7 +16,7 @@ may not make much sense otherwise. Installing and running mypy *************************** -Mypy requires Python 3.8 or later to run. You can install mypy using pip: +Mypy requires Python 3.9 or later to run. You can install mypy using pip: .. code-block:: shell diff --git a/mypy/defaults.py b/mypy/defaults.py index 2bbae23d7e2d..6f309668d224 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -6,7 +6,7 @@ # Earliest fully supported Python 3.x version. Used as the default Python # version in tests. Mypy wheels should be built starting with this version, # and CI tests should be run on this version (and later versions). -PYTHON3_VERSION: Final = (3, 8) +PYTHON3_VERSION: Final = (3, 9) # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index bff2d6977612..8c6fc1610e63 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -50,13 +50,13 @@ def test_bad_ge_version_check(self) -> None: """ [case abc] s: str - [out version>=3.8] + [out version>=3.9] abc """ ) # Assert - assert "version>=3.8 always true since minimum runtime version is (3, 8)" in actual.stdout + assert "version>=3.9 always true since minimum runtime version is (3, 9)" in actual.stdout def test_bad_eq_version_check(self) -> None: # Act @@ -70,4 +70,4 @@ def test_bad_eq_version_check(self) -> None: ) # Assert - assert "version==3.7 always false since minimum runtime version is (3, 8)" in actual.stdout + assert "version==3.7 always false since minimum runtime version is (3, 9)" in actual.stdout diff --git a/mypy/util.py b/mypy/util.py index e0a9cf9ce1b2..ef6286150e60 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -490,10 +490,10 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 8): # noqa: UP036 + if sys.version_info[:2] < (3, 9): sys.exit( - "Running {name} with Python 3.7 or lower is not supported; " - "please upgrade to 3.8 or newer".format(name=program) + "Running {name} with Python 3.8 or lower is not supported; " + "please upgrade to 3.9 or newer".format(name=program) ) diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 7f1f9689a757..d4c0e8445f48 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -6,7 +6,7 @@ show_traceback = True pretty = True always_false = MYPYC plugins = mypy.plugins.proper_plugin -python_version = 3.8 +python_version = 3.9 exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes diff --git a/mypyc/doc/getting_started.rst b/mypyc/doc/getting_started.rst index adc617419ffa..f85981f08d02 100644 --- a/mypyc/doc/getting_started.rst +++ b/mypyc/doc/getting_started.rst @@ -38,7 +38,7 @@ Installation ------------ Mypyc is shipped as part of the mypy distribution. Install mypy like -this (you need Python 3.8 or later): +this (you need Python 3.9 or later): .. code-block:: diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index dd3c79da7b9b..8048870a79f2 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -320,6 +320,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> # TODO: testDecorators1 hangs on 3.12, remove this once fixed proc.wait(timeout=30) output = proc.communicate()[0].decode("utf8") + output = output.replace(f' File "{os.getcwd()}{os.sep}', ' File "') outlines = output.splitlines() if testcase.config.getoption("--mypyc-showc"): diff --git a/pyproject.toml b/pyproject.toml index 1a7adf21c0a6..8be581b44761 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -45,7 +44,7 @@ classifiers = [ "Topic :: Software Development", "Typing :: Typed", ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ # When changing this, also update build-system.requires and mypy-requirements.txt "typing_extensions>=4.6.0", diff --git a/setup.py b/setup.py index 180faf6d8ded..44a9a72e34e0 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 8, 0): # noqa: UP036 - sys.stderr.write("ERROR: You need Python 3.8 or later to use mypy.\n") +if sys.version_info < (3, 9, 0): + sys.stderr.write("ERROR: You need Python 3.9 or later to use mypy.\n") exit(1) # we'll import stuff from the source tree, let's ensure is on the sys path diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 0aba0cfca09c..8bb768cfe13b 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -260,7 +260,7 @@ class D(A): # N:5: def f(self) -> None [case testColumnMissingTypeParameters] -# flags: --disallow-any-generics +# flags: --python-version 3.8 --disallow-any-generics from typing import List, Callable def f(x: List) -> None: pass # E:10: Missing type parameters for generic type "List" def g(x: list) -> None: pass # E:10: Implicit generic "Any". Use "typing.List" and specify generic parameters diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 10cc145d0c70..cc0227bc6664 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -328,7 +328,7 @@ a: A a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] [case testErrorCodeMissingTypeArg] -# flags: --disallow-any-generics +# flags: --python-version 3.8 --disallow-any-generics from typing import List, TypeVar x: List # E: Missing type parameters for generic type "List" [type-arg] y: list # E: Implicit generic "Any". Use "typing.List" and specify generic parameters [type-arg] diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index c1868b3e3d72..22159580163d 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -289,6 +289,7 @@ p1("a", "b") # TODO: false negative [builtins fixtures/dict.pyi] [case testFunctoolsPartialTypeGuard] +# flags: --python-version 3.8 import functools from typing_extensions import TypeGuard diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 74003f824e5d..08dfb3b54b3a 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -515,6 +515,7 @@ Alias[int]("a") # E: Argument 1 to "Node" has incompatible type "str"; expected [out] [case testTypeApplicationCrash] +# flags: --python-version 3.8 import types type[int] # this was crashing, see #2302 (comment) # E: The type "Type[type]" is not generic and not indexable [builtins fixtures/tuple.pyi] @@ -1130,6 +1131,7 @@ reveal_type(Bad) # N: Revealed type is "Any" [out] [case testNoSubscriptionOfBuiltinAliases] +# flags: --python-version 3.8 from typing import List, TypeVar list[int]() # E: "list" is not subscriptable diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 888b4c26a7c7..55360f15f5c5 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3753,7 +3753,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.8/@deps.meta.json.2] +[file ../.mypy_cache/3.9/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file ../.mypy_cache/.gitignore] # Another hack to not trigger a .gitignore creation failure "false positive" @@ -3788,7 +3788,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by deleting -- the proto deps file. -[delete ../.mypy_cache/3.8/@deps.meta.json.2] +[delete ../.mypy_cache/3.9/@deps.meta.json.2] [file b.py.2] # uh -- Every file should get reloaded, since the cache was invalidated diff --git a/test-data/unit/fine-grained-cache-incremental.test b/test-data/unit/fine-grained-cache-incremental.test index 00157333efd7..f622cefc5b8e 100644 --- a/test-data/unit/fine-grained-cache-incremental.test +++ b/test-data/unit/fine-grained-cache-incremental.test @@ -202,7 +202,7 @@ a.py:8: note: x: expected "int", got "str" [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.8/@deps.meta.json.2] +[file ../.mypy_cache/3.9/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file b.py.2] @@ -234,8 +234,8 @@ x = 10 [file p/c.py] class C: pass -[delete ../.mypy_cache/3.8/b.meta.json.2] -[delete ../.mypy_cache/3.8/p/c.meta.json.2] +[delete ../.mypy_cache/3.9/b.meta.json.2] +[delete ../.mypy_cache/3.9/p/c.meta.json.2] [out] == diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 19564133e375..0f6e018fe325 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10228,7 +10228,7 @@ class Base(Protocol): main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe [case testPrettyMessageSorting] -# flags: --pretty +# flags: --python-version 3.8 --pretty import a [file a.py] diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test index 7b1078d3fa2f..33c2a6ddf5c0 100644 --- a/test-data/unit/parse-errors.test +++ b/test-data/unit/parse-errors.test @@ -55,7 +55,7 @@ file:1: error: invalid syntax [case testUnexpectedEof] if 1: [out] -file:1: error: unexpected EOF while parsing +file:1: error: expected an indented block [case testInvalidKeywordArguments1] f(x=y, z) @@ -434,7 +434,7 @@ file:1: error: invalid syntax [case testSmartQuotes] foo = ‘bar’ [out] -file:1: error: invalid character in identifier +file:1: error: invalid character '‘' (U+2018) [case testExceptCommaInPython3] try: diff --git a/test-requirements.in b/test-requirements.in index 4e53c63cc36b..767a94e5c14d 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -12,5 +12,5 @@ pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 setuptools>=75.1.0 -tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 +tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.9 pre_commit>=3.5.0 From b3eff87ffd285e2fe77cd14cd4f855b333e1056b Mon Sep 17 00:00:00 2001 From: Kcornw <141114704+kcornw@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:40:35 +0800 Subject: [PATCH 0983/1617] Clarify status in `dmypy status` output (#18331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revised the status message output from the `dmypy status` command to eliminate potential misunderstandings about the daemon's operational state. Given the daemon’s synchronous design, the server may appear unresponsive during periods of heavy processing. When encountering a timeout, the status message could suggest that the daemon was "stuck", prompting users to prematurely consider stopping it. Fixes #18008 --- mypy/dmypy/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 9f0751e93609..a534a78542da 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -436,7 +436,7 @@ def do_status(args: argparse.Namespace) -> None: if args.verbose or "error" in response: show_stats(response) if "error" in response: - fail(f"Daemon is stuck; consider {sys.argv[0]} kill") + fail(f"Daemon may be busy processing; if this persists, consider {sys.argv[0]} kill") print("Daemon is up and running") @@ -447,7 +447,7 @@ def do_stop(args: argparse.Namespace) -> None: response = request(args.status_file, "stop", timeout=5) if "error" in response: show_stats(response) - fail(f"Daemon is stuck; consider {sys.argv[0]} kill") + fail(f"Daemon may be busy processing; if this persists, consider {sys.argv[0]} kill") else: print("Daemon stopped") From ed4cd382715e868b2544127d4c88351a41864fc1 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:00:10 +0100 Subject: [PATCH 0984/1617] Update black formatting for Python 3.9 (#18335) --- mypy/checkexpr.py | 7 ++++--- mypyc/test/test_run.py | 7 ++++--- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 3ad125cc8bbe..964149fa8df4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5345,9 +5345,10 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: self.chk.return_types.append(AnyType(TypeOfAny.special_form)) # Type check everything in the body except for the final return # statement (it can contain tuple unpacking before return). - with self.chk.binder.frame_context( - can_skip=True, fall_through=0 - ), self.chk.scope.push_function(e): + with ( + self.chk.binder.frame_context(can_skip=True, fall_through=0), + self.chk.scope.push_function(e), + ): # Lambdas can have more than one element in body, # when we add "fictional" AssignmentStatement nodes, like in: # `lambda (a, b): a` diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 8048870a79f2..0f3be7891779 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -146,9 +146,10 @@ class TestRun(MypycDataSuite): def run_case(self, testcase: DataDrivenTestCase) -> None: # setup.py wants to be run from the root directory of the package, which we accommodate # by chdiring into tmp/ - with use_custom_builtins( - os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase - ), chdir_manager("tmp"): + with ( + use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase), + chdir_manager("tmp"), + ): self.run_case_inner(testcase) def run_case_inner(self, testcase: DataDrivenTestCase) -> None: diff --git a/pyproject.toml b/pyproject.toml index 8be581b44761..24f13921eaf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ mypy = [ [tool.black] line-length = 99 -target-version = ["py38", "py39", "py310", "py311", "py312"] +target-version = ["py39", "py310", "py311", "py312", "py313"] skip-magic-trailing-comma = true force-exclude = ''' ^/mypy/typeshed| From 624e1793517042fa8c397cdee40ec6b3e6763dbd Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 Dec 2024 21:12:27 +0100 Subject: [PATCH 0985/1617] Update pre-commit versions (#18339) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8e66ecb4dfc..1e53f084e675 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,27 +1,27 @@ exclude: '^(mypyc/external/)|(mypy/typeshed/)|misc/typeshed_patches' # Exclude all vendored code from lints repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.8.4 hooks: - id: ruff args: [--exit-non-zero-on-fix] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.29.4 + rev: 0.30.0 hooks: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/rhysd/actionlint - rev: v1.7.3 + rev: v1.7.4 hooks: - id: actionlint args: [ From aa0b6f0288e6a511b750f7fe8f49a0e321362105 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 Dec 2024 22:36:49 +0100 Subject: [PATCH 0986/1617] Replace lru_cache with functools.cache (#18337) Python 3.9 added `functools.cache` which can replace `lru_cache(maxsize=None)`. https://docs.python.org/3/library/functools.html#functools.cache --- mypy/modulefinder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index fdd89837002f..a5d28a30dea8 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -751,7 +751,7 @@ def default_lib_path( return path -@functools.lru_cache(maxsize=None) +@functools.cache def get_search_dirs(python_executable: str | None) -> tuple[list[str], list[str]]: """Find package directories for given python. Guaranteed to return absolute paths. From 645081f397e9d8f2dd2457ea149c4437608143c3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:37:05 +0100 Subject: [PATCH 0987/1617] Update pythoncapi_compat.h (#18340) https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h --- mypyc/lib-rt/pythoncapi_compat.h | 255 ++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 6 deletions(-) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index acaadf34bf2e..cee282d7efed 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -7,7 +7,7 @@ // https://github.com/python/pythoncapi_compat // // Latest version: -// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h +// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h // // SPDX-License-Identifier: 0BSD @@ -24,6 +24,9 @@ extern "C" { #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) # include "frameobject.h" // PyFrameObject, PyFrame_GetBack() #endif +#if PY_VERSION_HEX < 0x030C00A3 +# include // T_SHORT, READONLY +#endif #ifndef _Py_CAST @@ -287,7 +290,7 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) // bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) static inline PyInterpreterState * PyThreadState_GetInterpreter(PyThreadState *tstate) { @@ -918,7 +921,7 @@ static inline int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) { PyObject **dict = _PyObject_GetDictPtr(obj); - if (*dict == NULL) { + if (dict == NULL || *dict == NULL) { return -1; } Py_VISIT(*dict); @@ -929,7 +932,7 @@ static inline void PyObject_ClearManagedDict(PyObject *obj) { PyObject **dict = _PyObject_GetDictPtr(obj); - if (*dict == NULL) { + if (dict == NULL || *dict == NULL) { return; } Py_CLEAR(*dict); @@ -1204,11 +1207,11 @@ static inline int PyTime_PerfCounter(PyTime_t *result) #endif // gh-111389 added hash constants to Python 3.13.0a5. These constants were -// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. #if (!defined(PyHASH_BITS) \ && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ - && PYPY_VERSION_NUM >= 0x07090000))) + && PYPY_VERSION_NUM >= 0x07030800))) # define PyHASH_BITS _PyHASH_BITS # define PyHASH_MODULUS _PyHASH_MODULUS # define PyHASH_INF _PyHASH_INF @@ -1520,6 +1523,36 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign) } #endif +// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 +#if PY_VERSION_HEX < 0x030E00A2 +static inline int PyLong_IsPositive(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 1; +} + +static inline int PyLong_IsNegative(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == -1; +} + +static inline int PyLong_IsZero(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 0; +} +#endif + // gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 #if PY_VERSION_HEX < 0x030E00A0 @@ -1690,6 +1723,216 @@ static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) #endif +// gh-102471 added import and export API for integers to 3.14.0a2. +#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +// Helpers to access PyLongObject internals. +static inline void +_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) +{ +#if PY_VERSION_HEX >= 0x030C0000 + op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3); +#elif PY_VERSION_HEX >= 0x030900A4 + Py_SET_SIZE(op, sign * size); +#else + Py_SIZE(op) = sign * size; +#endif +} + +static inline Py_ssize_t +_PyLong_DigitCount(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (Py_ssize_t)(op->long_value.lv_tag >> 3); +#else + return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); +#endif +} + +static inline digit* +_PyLong_GetDigits(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (digit*)(op->long_value.ob_digit); +#else + return (digit*)(op->ob_digit); +#endif +} + +typedef struct PyLongLayout { + uint8_t bits_per_digit; + uint8_t digit_size; + int8_t digits_order; + int8_t digit_endianness; +} PyLongLayout; + +typedef struct PyLongExport { + int64_t value; + uint8_t negative; + Py_ssize_t ndigits; + const void *digits; + Py_uintptr_t _reserved; +} PyLongExport; + +typedef struct PyLongWriter PyLongWriter; + +static inline const PyLongLayout* +PyLong_GetNativeLayout(void) +{ + static const PyLongLayout PyLong_LAYOUT = { + PyLong_SHIFT, + sizeof(digit), + -1, // least significant first + PY_LITTLE_ENDIAN ? -1 : 1, + }; + + return &PyLong_LAYOUT; +} + +static inline int +PyLong_Export(PyObject *obj, PyLongExport *export_long) +{ + if (!PyLong_Check(obj)) { + memset(export_long, 0, sizeof(*export_long)); + PyErr_Format(PyExc_TypeError, "expected int, got %s", + Py_TYPE(obj)->tp_name); + return -1; + } + + // Fast-path: try to convert to a int64_t + PyLongObject *self = (PyLongObject*)obj; + int overflow; +#if SIZEOF_LONG == 8 + long value = PyLong_AsLongAndOverflow(obj, &overflow); +#else + // Windows has 32-bit long, so use 64-bit long long instead + long long value = PyLong_AsLongLongAndOverflow(obj, &overflow); +#endif + Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t)); + // the function cannot fail since obj is a PyLongObject + assert(!(value == -1 && PyErr_Occurred())); + + if (!overflow) { + export_long->value = value; + export_long->negative = 0; + export_long->ndigits = 0; + export_long->digits = 0; + export_long->_reserved = 0; + } + else { + export_long->value = 0; + export_long->negative = _PyLong_Sign(obj) < 0; + export_long->ndigits = _PyLong_DigitCount(self); + if (export_long->ndigits == 0) { + export_long->ndigits = 1; + } + export_long->digits = _PyLong_GetDigits(self); + export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj); + } + return 0; +} + +static inline void +PyLong_FreeExport(PyLongExport *export_long) +{ + PyObject *obj = (PyObject*)export_long->_reserved; + + if (obj) { + export_long->_reserved = 0; + Py_DECREF(obj); + } +} + +static inline PyLongWriter* +PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) +{ + if (ndigits <= 0) { + PyErr_SetString(PyExc_ValueError, "ndigits must be positive"); + return NULL; + } + assert(digits != NULL); + + PyLongObject *obj = _PyLong_New(ndigits); + if (obj == NULL) { + return NULL; + } + _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits); + + *digits = _PyLong_GetDigits(obj); + return (PyLongWriter*)obj; +} + +static inline void +PyLongWriter_Discard(PyLongWriter *writer) +{ + PyLongObject *obj = (PyLongObject *)writer; + + assert(Py_REFCNT(obj) == 1); + Py_DECREF(obj); +} + +static inline PyObject* +PyLongWriter_Finish(PyLongWriter *writer) +{ + PyObject *obj = (PyObject *)writer; + PyLongObject *self = (PyLongObject*)obj; + Py_ssize_t j = _PyLong_DigitCount(self); + Py_ssize_t i = j; + int sign = _PyLong_Sign(obj); + + assert(Py_REFCNT(obj) == 1); + + // Normalize and get singleton if possible + while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) { + --i; + } + if (i != j) { + if (i == 0) { + sign = 0; + } + _PyLong_SetSignAndDigitCount(self, sign, i); + } + if (i <= 1) { + long val = sign * (long)(_PyLong_GetDigits(self)[0]); + Py_DECREF(obj); + return PyLong_FromLong(val); + } + + return obj; +} +#endif + + +#if PY_VERSION_HEX < 0x030C00A3 +# define Py_T_SHORT T_SHORT +# define Py_T_INT T_INT +# define Py_T_LONG T_LONG +# define Py_T_FLOAT T_FLOAT +# define Py_T_DOUBLE T_DOUBLE +# define Py_T_STRING T_STRING +# define _Py_T_OBJECT T_OBJECT +# define Py_T_CHAR T_CHAR +# define Py_T_BYTE T_BYTE +# define Py_T_UBYTE T_UBYTE +# define Py_T_USHORT T_USHORT +# define Py_T_UINT T_UINT +# define Py_T_ULONG T_ULONG +# define Py_T_STRING_INPLACE T_STRING_INPLACE +# define Py_T_BOOL T_BOOL +# define Py_T_OBJECT_EX T_OBJECT_EX +# define Py_T_LONGLONG T_LONGLONG +# define Py_T_ULONGLONG T_ULONGLONG +# define Py_T_PYSSIZET T_PYSSIZET + +# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +# define _Py_T_NONE T_NONE +# endif + +# define Py_READONLY READONLY +# define Py_AUDIT_READ READ_RESTRICTED +# define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED +#endif + + #ifdef __cplusplus } #endif From ec04f737d7595df8a25116036d1b071b0284c81b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:38:27 +0100 Subject: [PATCH 0988/1617] Update sys.version_info guards after dropping Python 3.8 (#18338) --- mypy/fastparse.py | 41 ++++----------------------------------- mypy/pyinfo.py | 4 ++-- mypy/test/testcheck.py | 2 -- mypy/test/testpep561.py | 1 - mypy/test/teststubtest.py | 11 +++++------ mypy/util.py | 12 ++---------- setup.py | 2 +- 7 files changed, 14 insertions(+), 59 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index a47ed9b536da..39782035d6ac 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1,6 +1,5 @@ from __future__ import annotations -import copy import re import sys import warnings @@ -241,13 +240,6 @@ def parse( path=fnam, ).visit(ast) except SyntaxError as e: - # alias to please mypyc - is_py38_or_earlier = sys.version_info < (3, 9) - if is_py38_or_earlier and e.filename == "": - # In Python 3.8 and earlier, syntax errors in f-strings have lineno relative to the - # start of the f-string. This would be misleading, as mypy will report the error as the - # lineno within the file. - e.lineno = None message = e.msg if feature_version > sys.version_info.minor and message.startswith("invalid syntax"): python_version_str = f"{options.python_version[0]}.{options.python_version[1]}" @@ -2069,40 +2061,15 @@ def visit_Index(self, n: ast3.Index) -> Type: def visit_Slice(self, n: ast3.Slice) -> Type: return self.invalid_type(n, note="did you mean to use ',' instead of ':' ?") - # Subscript(expr value, slice slice, expr_context ctx) # Python 3.8 and before # Subscript(expr value, expr slice, expr_context ctx) # Python 3.9 and later def visit_Subscript(self, n: ast3.Subscript) -> Type: - if sys.version_info >= (3, 9): # Really 3.9a5 or later - sliceval: Any = n.slice - # Python 3.8 or earlier use a different AST structure for subscripts - elif isinstance(n.slice, ast3.Index): - sliceval: Any = n.slice.value - elif isinstance(n.slice, ast3.Slice): - sliceval = copy.deepcopy(n.slice) # so we don't mutate passed AST - if getattr(sliceval, "col_offset", None) is None: - # Fix column information so that we get Python 3.9+ message order - sliceval.col_offset = sliceval.lower.col_offset - else: - assert isinstance(n.slice, ast3.ExtSlice) - dims = cast(List[ast3.expr], copy.deepcopy(n.slice.dims)) - for s in dims: - # These fields don't actually have a col_offset attribute but we add - # it manually. - if getattr(s, "col_offset", None) is None: - if isinstance(s, ast3.Index): - s.col_offset = s.value.col_offset - elif isinstance(s, ast3.Slice): - assert s.lower is not None - s.col_offset = s.lower.col_offset - sliceval = ast3.Tuple(dims, n.ctx) - empty_tuple_index = False - if isinstance(sliceval, ast3.Tuple): - params = self.translate_expr_list(sliceval.elts) - if len(sliceval.elts) == 0: + if isinstance(n.slice, ast3.Tuple): + params = self.translate_expr_list(n.slice.elts) + if len(n.slice.elts) == 0: empty_tuple_index = True else: - params = [self.visit(sliceval)] + params = [self.visit(n.slice)] value = self.visit(n.value) if isinstance(value, UnboundType) and not value.args: diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index ee5307cfaebb..98350f46363c 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -2,9 +2,9 @@ """Utilities to find the site and prefix information of a Python executable. -This file MUST remain compatible with all Python 3.8+ versions. Since we cannot make any +This file MUST remain compatible with all Python 3.9+ versions. Since we cannot make any assumptions about the Python being executed, this module should not use *any* dependencies outside -of the standard library found in Python 3.8. This file is run each mypy run, so it should be kept +of the standard library found in Python 3.9. This file is run each mypy run, so it should be kept as fast as possible. """ import sys diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 330e191af252..e6415ddff906 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -37,8 +37,6 @@ typecheck_files = find_test_files(pattern="check-*.test") # Tests that use Python version specific features: -if sys.version_info < (3, 9): - typecheck_files.remove("check-python39.test") if sys.version_info < (3, 10): typecheck_files.remove("check-python310.test") if sys.version_info < (3, 11): diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index 9d2628c1fa5f..a95b9ea2a084 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -52,7 +52,6 @@ def upgrade_pip(python_executable: str) -> None: sys.version_info >= (3, 11) or (3, 10, 3) <= sys.version_info < (3, 11) or (3, 9, 11) <= sys.version_info < (3, 10) - or (3, 8, 13) <= sys.version_info < (3, 9) ): # Skip for more recent Python releases which come with pip>=21.3.1 # out of the box - for performance reasons. diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index b16cb18ace21..6dc1feb67089 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1533,12 +1533,11 @@ def test_dunders(self) -> Iterator[Case]: runtime="class C:\n def __init_subclass__(cls, e=1, **kwargs): pass", error=None, ) - if sys.version_info >= (3, 9): - yield Case( - stub="class D:\n def __class_getitem__(cls, type: type) -> type: ...", - runtime="class D:\n def __class_getitem__(cls, type): ...", - error=None, - ) + yield Case( + stub="class D:\n def __class_getitem__(cls, type: type) -> type: ...", + runtime="class D:\n def __class_getitem__(cls, type): ...", + error=None, + ) @collect_cases def test_not_subclassable(self) -> Iterator[Case]: diff --git a/mypy/util.py b/mypy/util.py index ef6286150e60..23f558e7ce7d 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -30,15 +30,7 @@ T = TypeVar("T") -if sys.version_info >= (3, 9): - TYPESHED_DIR: Final = str(importlib_resources.files("mypy") / "typeshed") -else: - with importlib_resources.path( - "mypy", # mypy-c doesn't support __package__ - "py.typed", # a marker file for type information, we assume typeshed to live in the same dir - ) as _resource: - TYPESHED_DIR = str(_resource.parent / "typeshed") - +TYPESHED_DIR: Final = str(importlib_resources.files("mypy") / "typeshed") ENCODING_RE: Final = re.compile(rb"([ \t\v]*#.*(\r\n?|\n))??[ \t\v]*#.*coding[:=][ \t]*([-\w.]+)") @@ -490,7 +482,7 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 9): + if sys.version_info[:2] < (3, 9): # noqa: UP036, RUF100 sys.exit( "Running {name} with Python 3.8 or lower is not supported; " "please upgrade to 3.9 or newer".format(name=program) diff --git a/setup.py b/setup.py index 44a9a72e34e0..e995068b4c5d 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 9, 0): +if sys.version_info < (3, 9, 0): # noqa: UP036, RUF100 sys.stderr.write("ERROR: You need Python 3.9 or later to use mypy.\n") exit(1) From 6d13d0dbcbfff93d36b9b78bf3caeeb581e66270 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Thu, 26 Dec 2024 16:41:16 -0800 Subject: [PATCH 0989/1617] [mypyc] Fixing InitVar for dataclasses. (#18319) Fixes mypyc/mypyc#934. `InitVar` variables are not attributes of a dataclass `PyTypeObject`. Adding check before removing `InitVar` keys from `PyTypeObject` in `CPyDataclass_SleightOfHand`. --- mypyc/lib-rt/misc_ops.c | 3 ++- mypyc/test-data/run-classes.test | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index a2b03e7df247..a7f67fd67d50 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -365,7 +365,8 @@ CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, pos = 0; PyObject *key; while (PyDict_Next(annotations, &pos, &key, NULL)) { - if (PyObject_DelAttr(tp, key) != 0) { + // Check and delete key. Key may be absent from tp for InitVar variables. + if (PyObject_HasAttr(tp, key) == 1 && PyObject_DelAttr(tp, key) != 0) { goto fail; } } diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index cf30bddbef64..f1d1d44fffc1 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2655,3 +2655,21 @@ import native [out] (31, 12, 23) (61, 42, 53) + +[case testDataclassInitVar] +import dataclasses + +@dataclasses.dataclass +class C: + init_v: dataclasses.InitVar[int] + v: float = dataclasses.field(init=False) + + def __post_init__(self, init_v): + self.v = init_v + 0.1 + +[file driver.py] +import native +print(native.C(22).v) + +[out] +22.1 From 60da03aae5cade43e106f6e33fcd26f524e63213 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Dec 2024 00:55:44 -0800 Subject: [PATCH 0990/1617] Minor changes to mypy_primer workflow (#18349) Just bring things in line with other repos --- .github/workflows/mypy_primer.yml | 4 ++-- .github/workflows/mypy_primer_comment.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 2b2327798a72..54fa2177716c 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -74,9 +74,9 @@ jobs: name: Save PR number run: | echo ${{ github.event.pull_request.number }} | tee pr_number.txt - - if: ${{ matrix.shard-index == 0 }} - name: Upload mypy_primer diff + PR number + - name: Upload mypy_primer diff + PR number uses: actions/upload-artifact@v4 + if: ${{ matrix.shard-index == 0 }} with: name: mypy_primer_diffs-${{ matrix.shard-index }} path: | diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 6e62d8c51713..72f111b96c53 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -48,7 +48,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const MAX_CHARACTERS = 30000 + const MAX_CHARACTERS = 50000 const MAX_CHARACTERS_PER_PROJECT = MAX_CHARACTERS / 3 const fs = require('fs') From 670f486aa249e18b0793295fbb13b5c4ee845f63 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 17:55:18 +0100 Subject: [PATCH 0991/1617] stubtest: Fix crash with numpy array default values (#18353) See https://github.com/python/mypy/issues/18343#issuecomment-2564314519 --- mypy/stubtest.py | 42 +++++++++++++++++++++++---------------- mypy/test/teststubtest.py | 12 +++++++++++ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6c8d03319893..6b5ea0d5af61 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -670,7 +670,7 @@ def _verify_arg_default_value( stub_arg: nodes.Argument, runtime_arg: inspect.Parameter ) -> Iterator[str]: """Checks whether argument default values are compatible.""" - if runtime_arg.default != inspect.Parameter.empty: + if runtime_arg.default is not inspect.Parameter.empty: if stub_arg.kind.is_required(): yield ( f'runtime argument "{runtime_arg.name}" ' @@ -705,18 +705,26 @@ def _verify_arg_default_value( stub_default is not UNKNOWN and stub_default is not ... and runtime_arg.default is not UNREPRESENTABLE - and ( - stub_default != runtime_arg.default - # We want the types to match exactly, e.g. in case the stub has - # True and the runtime has 1 (or vice versa). - or type(stub_default) is not type(runtime_arg.default) - ) ): - yield ( - f'runtime argument "{runtime_arg.name}" ' - f"has a default value of {runtime_arg.default!r}, " - f"which is different from stub argument default {stub_default!r}" - ) + defaults_match = True + # We want the types to match exactly, e.g. in case the stub has + # True and the runtime has 1 (or vice versa). + if type(stub_default) is not type(runtime_arg.default): + defaults_match = False + else: + try: + defaults_match = bool(stub_default == runtime_arg.default) + except Exception: + # Exception can be raised in bool dunder method (e.g. numpy arrays) + # At this point, consider the default to be different, it is probably + # too complex to put in a stub anyway. + defaults_match = False + if not defaults_match: + yield ( + f'runtime argument "{runtime_arg.name}" ' + f"has a default value of {runtime_arg.default!r}, " + f"which is different from stub argument default {stub_default!r}" + ) else: if stub_arg.kind.is_optional(): yield ( @@ -758,7 +766,7 @@ def get_type(arg: Any) -> str | None: def has_default(arg: Any) -> bool: if isinstance(arg, inspect.Parameter): - return bool(arg.default != inspect.Parameter.empty) + return arg.default is not inspect.Parameter.empty if isinstance(arg, nodes.Argument): return arg.kind.is_optional() raise AssertionError @@ -1628,13 +1636,13 @@ def anytype() -> mypy.types.AnyType: arg_names.append( None if arg.kind == inspect.Parameter.POSITIONAL_ONLY else arg.name ) - has_default = arg.default == inspect.Parameter.empty + no_default = arg.default is inspect.Parameter.empty if arg.kind == inspect.Parameter.POSITIONAL_ONLY: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.KEYWORD_ONLY: - arg_kinds.append(nodes.ARG_NAMED if has_default else nodes.ARG_NAMED_OPT) + arg_kinds.append(nodes.ARG_NAMED if no_default else nodes.ARG_NAMED_OPT) elif arg.kind == inspect.Parameter.VAR_POSITIONAL: arg_kinds.append(nodes.ARG_STAR) elif arg.kind == inspect.Parameter.VAR_KEYWORD: diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 6dc1feb67089..f099ebdc55a5 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -529,6 +529,18 @@ def f11(text=None) -> None: pass error="f11", ) + # Simulate numpy ndarray.__bool__ that raises an error + yield Case( + stub="def f12(x=1): ...", + runtime=""" + class _ndarray: + def __eq__(self, obj): return self + def __bool__(self): raise ValueError + def f12(x=_ndarray()) -> None: pass + """, + error="f12", + ) + @collect_cases def test_static_class_method(self) -> Iterator[Case]: yield Case( From 44bf7e50a0a1788a12122d541dda527d08610031 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Dec 2024 14:45:56 -0800 Subject: [PATCH 0992/1617] Don't erase type object args in diagnostics (#18352) Fixes https://github.com/python/mypy/issues/16875 --- mypy/messages.py | 2 +- test-data/unit/check-generics.test | 3 +-- test-data/unit/check-inference.test | 2 +- test-data/unit/check-newsemanal.test | 6 ++---- test-data/unit/fine-grained-inspect.test | 2 +- test-data/unit/pythoneval.test | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 6b0760cd79c6..40b0e7ee695a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2700,7 +2700,7 @@ def format_literal_value(typ: LiteralType) -> str: if func.is_type_obj(): # The type of a type object type can be derived from the # return type (this always works). - return format(TypeType.make_normalized(erase_type(func.items[0].ret_type))) + return format(TypeType.make_normalized(func.items[0].ret_type)) elif isinstance(func, CallableType): if func.type_guard is not None: return_type = f"TypeGuard[{format(func.type_guard)}]" diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 08dfb3b54b3a..5d6ad8e19631 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1773,8 +1773,7 @@ T = TypeVar('T') class C(Generic[T]): def __init__(self) -> None: pass x = C # type: Callable[[], C[int]] -y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "Type[C[Any]]", variable has type "Callable[[], int]") - +y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "Type[C[T]]", variable has type "Callable[[], int]") -- Special cases -- ------------- diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index bec3a9a07593..560092ed1a43 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2488,7 +2488,7 @@ T = TypeVar('T') class C(Sequence[T], Generic[T]): pass C[0] = 0 [out] -main:4: error: Unsupported target for indexed assignment ("Type[C[Any]]") +main:4: error: Unsupported target for indexed assignment ("Type[C[T]]") main:4: error: Invalid type: try using Literal[0] instead? [case testNoCrashOnPartialMember] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 81b0066dbf81..7ac90d07e504 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2743,13 +2743,11 @@ T = TypeVar('T') class C(Generic[T]): pass -# TODO: Error message is confusing + C = C[int] # E: Cannot assign to a type \ - # E: Incompatible types in assignment (expression has type "Type[C[Any]]", variable has type "Type[C[Any]]") + # E: Incompatible types in assignment (expression has type "Type[C[int]]", variable has type "Type[C[T]]") x: C reveal_type(x) # N: Revealed type is "__main__.C[Any]" -[out] -[out2] [case testNewAnalyzerClassVariableOrdering] def foo(x: str) -> None: pass diff --git a/test-data/unit/fine-grained-inspect.test b/test-data/unit/fine-grained-inspect.test index ed89f2f099f9..0e05769370a2 100644 --- a/test-data/unit/fine-grained-inspect.test +++ b/test-data/unit/fine-grained-inspect.test @@ -23,7 +23,7 @@ NameExpr -> "C[T]" MemberExpr -> "T" NameExpr -> "C[T]" MemberExpr -> "T" -12:5:12:5 -> "Type[foo.C[Any]]" +12:5:12:5 -> "Type[foo.C[builtins.int]]" 12:5:12:9 -> "foo.C[builtins.int]" 12:1:12:10 -> "builtins.int" CallExpr:12:5:12:9 -> "C[int]" diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 70003545754c..66ceafb91370 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -823,7 +823,7 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]): MyDDict(dict)['0'] MyDDict(dict)[0] [out] -_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Optional[Callable[[], str]]" +_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[_T]]"; expected "Optional[Callable[[], str]]" _program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" _program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str") _program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[Never]]"; expected "defaultdict[int, List[Never]]" From a07ccf77c53278334b6c72f23d1aaafd8ca7bbeb Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 23:47:28 +0100 Subject: [PATCH 0993/1617] ArgumentParser: use broader file type (#18354) This is in anticipation of https://github.com/python/typeshed/pull/13324 --- mypy/main.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index d2a28a18c6a8..9873907ddf03 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -10,7 +10,7 @@ from collections import defaultdict from gettext import gettext from io import TextIOWrapper -from typing import IO, Any, Final, NoReturn, Sequence, TextIO +from typing import IO, Any, Final, NoReturn, Protocol, Sequence, TextIO from mypy import build, defaults, state, util from mypy.config_parser import ( @@ -35,6 +35,11 @@ from mypy.split_namespace import SplitNamespace from mypy.version import __version__ + +class _SupportsWrite(Protocol): + def write(self, s: str, /) -> object: ... + + orig_stat: Final = os.stat MEM_PROFILE: Final = False # If True, dump memory profile @@ -372,17 +377,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # ===================== # Help-printing methods # ===================== - def print_usage(self, file: IO[str] | None = None) -> None: + def print_usage(self, file: _SupportsWrite | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_usage(), file) - def print_help(self, file: IO[str] | None = None) -> None: + def print_help(self, file: _SupportsWrite | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_help(), file) - def _print_message(self, message: str, file: IO[str] | None = None) -> None: + def _print_message(self, message: str, file: _SupportsWrite | None = None) -> None: if message: if file is None: file = self.stderr From d79d89e3ff31ed67a41b0663da9ec8e037d41fa0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Dec 2024 14:47:58 -0800 Subject: [PATCH 0994/1617] Fix getargs argument passing (#18350) Fixes https://github.com/mypyc/mypyc/issues/1078 Introduced in https://github.com/python/mypy/pull/17930 See the first commit to see the bug (wrong condition) --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- mypyc/lib-rt/getargs.c | 21 +++++++++------------ mypyc/test-data/run-classes.test | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 4f2f8aa0be83..163b9ac2b163 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -250,13 +250,12 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); } else if (nkwargs && i >= pos) { - int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); - if (res == 1) { - --nkwargs; - } - else if (res == -1) { + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { return 0; } + if (current_arg) { + --nkwargs; + } } else { current_arg = NULL; @@ -371,11 +370,12 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, Py_ssize_t j; /* make sure there are no arguments given by name and position */ for (i = pos; i < bound_pos_args && i < len; i++) { - int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); - if (res == 1) { - Py_DECREF(current_arg); + PyObject *current_arg; + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { + goto latefail; } - else if (unlikely(res == 0)) { + if (unlikely(current_arg != NULL)) { + Py_DECREF(current_arg); /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%s') " @@ -385,9 +385,6 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, kwlist[i], i+1); goto latefail; } - else if (unlikely(res == -1)) { - goto latefail; - } } /* make sure there are no extraneous keyword arguments */ j = 0; diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index f1d1d44fffc1..055327e786a2 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -467,6 +467,21 @@ a = A(10) assert a.foo() == 11 assert foo() == 21 +[case testClassKwargs] +class X: + def __init__(self, msg: str, **variables: int) -> None: + pass +[file driver.py] +import traceback +from native import X +X('hello', a=0) +try: + X('hello', msg='hello') +except TypeError as e: + print(f"{type(e).__name__}: {e}") +[out] +TypeError: argument for __init__() given by name ('msg') and position (1) + [case testGenericClass] from typing import TypeVar, Generic, Sequence T = TypeVar('T') From 94639291e4b3a493c7ff7d8b70d0af22882c4b2a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 29 Dec 2024 23:07:10 +0100 Subject: [PATCH 0995/1617] Make visit method arguments pos-only (#18361) Extracted from #18356. Make `visit_*` method arguments positional only to ensure better LSP compatibility. Also update some visitors which don't have violations yet but are base classes for other ones, like `TypeTranslator` and `TypeQuery`. --- mypy/mixedtraverser.py | 32 ++-- mypy/traverser.py | 270 +++++++++++++++++----------------- mypy/type_visitor.py | 198 ++++++++++++------------- mypy/types.py | 62 ++++---- mypy/typetraverser.py | 54 +++---- mypy/visitor.py | 324 ++++++++++++++++++++--------------------- mypyc/ir/rtypes.py | 14 +- 7 files changed, 477 insertions(+), 477 deletions(-) diff --git a/mypy/mixedtraverser.py b/mypy/mixedtraverser.py index dfde41859c67..9fdc4457d18e 100644 --- a/mypy/mixedtraverser.py +++ b/mypy/mixedtraverser.py @@ -30,14 +30,14 @@ def __init__(self) -> None: # Symbol nodes - def visit_var(self, var: Var) -> None: + def visit_var(self, var: Var, /) -> None: self.visit_optional_type(var.type) - def visit_func(self, o: FuncItem) -> None: + def visit_func(self, o: FuncItem, /) -> None: super().visit_func(o) self.visit_optional_type(o.type) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: # TODO: Should we visit generated methods/variables as well, either here or in # TraverserVisitor? super().visit_class_def(o) @@ -46,67 +46,67 @@ def visit_class_def(self, o: ClassDef) -> None: for base in info.bases: base.accept(self) - def visit_type_alias_expr(self, o: TypeAliasExpr) -> None: + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: super().visit_type_alias_expr(o) self.in_type_alias_expr = True o.node.target.accept(self) self.in_type_alias_expr = False - def visit_type_var_expr(self, o: TypeVarExpr) -> None: + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: super().visit_type_var_expr(o) o.upper_bound.accept(self) for value in o.values: value.accept(self) - def visit_typeddict_expr(self, o: TypedDictExpr) -> None: + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: super().visit_typeddict_expr(o) self.visit_optional_type(o.info.typeddict_type) - def visit_namedtuple_expr(self, o: NamedTupleExpr) -> None: + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: super().visit_namedtuple_expr(o) assert o.info.tuple_type o.info.tuple_type.accept(self) - def visit__promote_expr(self, o: PromoteExpr) -> None: + def visit__promote_expr(self, o: PromoteExpr, /) -> None: super().visit__promote_expr(o) o.type.accept(self) - def visit_newtype_expr(self, o: NewTypeExpr) -> None: + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: super().visit_newtype_expr(o) self.visit_optional_type(o.old_type) # Statements - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: super().visit_assignment_stmt(o) self.visit_optional_type(o.type) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_for_stmt(self, o: ForStmt, /) -> None: super().visit_for_stmt(o) self.visit_optional_type(o.index_type) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: super().visit_with_stmt(o) for typ in o.analyzed_types: typ.accept(self) # Expressions - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: super().visit_cast_expr(o) o.type.accept(self) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: super().visit_assert_type_expr(o) o.type.accept(self) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: super().visit_type_application(o) for t in o.types: t.accept(self) # Helpers - def visit_optional_type(self, t: Type | None) -> None: + def visit_optional_type(self, t: Type | None, /) -> None: if t: t.accept(self) diff --git a/mypy/traverser.py b/mypy/traverser.py index 9c333c587f7c..2c8ea49491bc 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -111,15 +111,15 @@ def __init__(self) -> None: # Visit methods - def visit_mypy_file(self, o: MypyFile) -> None: + def visit_mypy_file(self, o: MypyFile, /) -> None: for d in o.defs: d.accept(self) - def visit_block(self, block: Block) -> None: + def visit_block(self, block: Block, /) -> None: for s in block.body: s.accept(self) - def visit_func(self, o: FuncItem) -> None: + def visit_func(self, o: FuncItem, /) -> None: if o.arguments is not None: for arg in o.arguments: init = arg.initializer @@ -131,16 +131,16 @@ def visit_func(self, o: FuncItem) -> None: o.body.accept(self) - def visit_func_def(self, o: FuncDef) -> None: + def visit_func_def(self, o: FuncDef, /) -> None: self.visit_func(o) - def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: + def visit_overloaded_func_def(self, o: OverloadedFuncDef, /) -> None: for item in o.items: item.accept(self) if o.impl: o.impl.accept(self) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: for d in o.decorators: d.accept(self) for base in o.base_type_exprs: @@ -153,52 +153,52 @@ def visit_class_def(self, o: ClassDef) -> None: if o.analyzed: o.analyzed.accept(self) - def visit_decorator(self, o: Decorator) -> None: + def visit_decorator(self, o: Decorator, /) -> None: o.func.accept(self) o.var.accept(self) for decorator in o.decorators: decorator.accept(self) - def visit_expression_stmt(self, o: ExpressionStmt) -> None: + def visit_expression_stmt(self, o: ExpressionStmt, /) -> None: o.expr.accept(self) - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: o.rvalue.accept(self) for l in o.lvalues: l.accept(self) - def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt) -> None: + def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt, /) -> None: o.rvalue.accept(self) o.lvalue.accept(self) - def visit_while_stmt(self, o: WhileStmt) -> None: + def visit_while_stmt(self, o: WhileStmt, /) -> None: o.expr.accept(self) o.body.accept(self) if o.else_body: o.else_body.accept(self) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_for_stmt(self, o: ForStmt, /) -> None: o.index.accept(self) o.expr.accept(self) o.body.accept(self) if o.else_body: o.else_body.accept(self) - def visit_return_stmt(self, o: ReturnStmt) -> None: + def visit_return_stmt(self, o: ReturnStmt, /) -> None: if o.expr is not None: o.expr.accept(self) - def visit_assert_stmt(self, o: AssertStmt) -> None: + def visit_assert_stmt(self, o: AssertStmt, /) -> None: if o.expr is not None: o.expr.accept(self) if o.msg is not None: o.msg.accept(self) - def visit_del_stmt(self, o: DelStmt) -> None: + def visit_del_stmt(self, o: DelStmt, /) -> None: if o.expr is not None: o.expr.accept(self) - def visit_if_stmt(self, o: IfStmt) -> None: + def visit_if_stmt(self, o: IfStmt, /) -> None: for e in o.expr: e.accept(self) for b in o.body: @@ -206,13 +206,13 @@ def visit_if_stmt(self, o: IfStmt) -> None: if o.else_body: o.else_body.accept(self) - def visit_raise_stmt(self, o: RaiseStmt) -> None: + def visit_raise_stmt(self, o: RaiseStmt, /) -> None: if o.expr is not None: o.expr.accept(self) if o.from_expr is not None: o.from_expr.accept(self) - def visit_try_stmt(self, o: TryStmt) -> None: + def visit_try_stmt(self, o: TryStmt, /) -> None: o.body.accept(self) for i in range(len(o.types)): tp = o.types[i] @@ -227,7 +227,7 @@ def visit_try_stmt(self, o: TryStmt) -> None: if o.finally_body is not None: o.finally_body.accept(self) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: for i in range(len(o.expr)): o.expr[i].accept(self) targ = o.target[i] @@ -235,7 +235,7 @@ def visit_with_stmt(self, o: WithStmt) -> None: targ.accept(self) o.body.accept(self) - def visit_match_stmt(self, o: MatchStmt) -> None: + def visit_match_stmt(self, o: MatchStmt, /) -> None: o.subject.accept(self) for i in range(len(o.patterns)): o.patterns[i].accept(self) @@ -244,38 +244,38 @@ def visit_match_stmt(self, o: MatchStmt) -> None: guard.accept(self) o.bodies[i].accept(self) - def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + def visit_type_alias_stmt(self, o: TypeAliasStmt, /) -> None: o.name.accept(self) o.value.accept(self) - def visit_member_expr(self, o: MemberExpr) -> None: + def visit_member_expr(self, o: MemberExpr, /) -> None: o.expr.accept(self) - def visit_yield_from_expr(self, o: YieldFromExpr) -> None: + def visit_yield_from_expr(self, o: YieldFromExpr, /) -> None: o.expr.accept(self) - def visit_yield_expr(self, o: YieldExpr) -> None: + def visit_yield_expr(self, o: YieldExpr, /) -> None: if o.expr: o.expr.accept(self) - def visit_call_expr(self, o: CallExpr) -> None: + def visit_call_expr(self, o: CallExpr, /) -> None: o.callee.accept(self) for a in o.args: a.accept(self) if o.analyzed: o.analyzed.accept(self) - def visit_op_expr(self, o: OpExpr) -> None: + def visit_op_expr(self, o: OpExpr, /) -> None: o.left.accept(self) o.right.accept(self) if o.analyzed is not None: o.analyzed.accept(self) - def visit_comparison_expr(self, o: ComparisonExpr) -> None: + def visit_comparison_expr(self, o: ComparisonExpr, /) -> None: for operand in o.operands: operand.accept(self) - def visit_slice_expr(self, o: SliceExpr) -> None: + def visit_slice_expr(self, o: SliceExpr, /) -> None: if o.begin_index is not None: o.begin_index.accept(self) if o.end_index is not None: @@ -283,13 +283,13 @@ def visit_slice_expr(self, o: SliceExpr) -> None: if o.stride is not None: o.stride.accept(self) - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: o.expr.accept(self) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: o.expr.accept(self) - def visit_reveal_expr(self, o: RevealExpr) -> None: + def visit_reveal_expr(self, o: RevealExpr, /) -> None: if o.kind == REVEAL_TYPE: assert o.expr is not None o.expr.accept(self) @@ -297,38 +297,38 @@ def visit_reveal_expr(self, o: RevealExpr) -> None: # RevealLocalsExpr doesn't have an inner expression pass - def visit_assignment_expr(self, o: AssignmentExpr) -> None: + def visit_assignment_expr(self, o: AssignmentExpr, /) -> None: o.target.accept(self) o.value.accept(self) - def visit_unary_expr(self, o: UnaryExpr) -> None: + def visit_unary_expr(self, o: UnaryExpr, /) -> None: o.expr.accept(self) - def visit_list_expr(self, o: ListExpr) -> None: + def visit_list_expr(self, o: ListExpr, /) -> None: for item in o.items: item.accept(self) - def visit_tuple_expr(self, o: TupleExpr) -> None: + def visit_tuple_expr(self, o: TupleExpr, /) -> None: for item in o.items: item.accept(self) - def visit_dict_expr(self, o: DictExpr) -> None: + def visit_dict_expr(self, o: DictExpr, /) -> None: for k, v in o.items: if k is not None: k.accept(self) v.accept(self) - def visit_set_expr(self, o: SetExpr) -> None: + def visit_set_expr(self, o: SetExpr, /) -> None: for item in o.items: item.accept(self) - def visit_index_expr(self, o: IndexExpr) -> None: + def visit_index_expr(self, o: IndexExpr, /) -> None: o.base.accept(self) o.index.accept(self) if o.analyzed: o.analyzed.accept(self) - def visit_generator_expr(self, o: GeneratorExpr) -> None: + def visit_generator_expr(self, o: GeneratorExpr, /) -> None: for index, sequence, conditions in zip(o.indices, o.sequences, o.condlists): sequence.accept(self) index.accept(self) @@ -336,7 +336,7 @@ def visit_generator_expr(self, o: GeneratorExpr) -> None: cond.accept(self) o.left_expr.accept(self) - def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: + def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None: for index, sequence, conditions in zip(o.indices, o.sequences, o.condlists): sequence.accept(self) index.accept(self) @@ -345,54 +345,54 @@ def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: o.key.accept(self) o.value.accept(self) - def visit_list_comprehension(self, o: ListComprehension) -> None: + def visit_list_comprehension(self, o: ListComprehension, /) -> None: o.generator.accept(self) - def visit_set_comprehension(self, o: SetComprehension) -> None: + def visit_set_comprehension(self, o: SetComprehension, /) -> None: o.generator.accept(self) - def visit_conditional_expr(self, o: ConditionalExpr) -> None: + def visit_conditional_expr(self, o: ConditionalExpr, /) -> None: o.cond.accept(self) o.if_expr.accept(self) o.else_expr.accept(self) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: o.expr.accept(self) - def visit_lambda_expr(self, o: LambdaExpr) -> None: + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: self.visit_func(o) - def visit_star_expr(self, o: StarExpr) -> None: + def visit_star_expr(self, o: StarExpr, /) -> None: o.expr.accept(self) - def visit_await_expr(self, o: AwaitExpr) -> None: + def visit_await_expr(self, o: AwaitExpr, /) -> None: o.expr.accept(self) - def visit_super_expr(self, o: SuperExpr) -> None: + def visit_super_expr(self, o: SuperExpr, /) -> None: o.call.accept(self) - def visit_as_pattern(self, o: AsPattern) -> None: + def visit_as_pattern(self, o: AsPattern, /) -> None: if o.pattern is not None: o.pattern.accept(self) if o.name is not None: o.name.accept(self) - def visit_or_pattern(self, o: OrPattern) -> None: + def visit_or_pattern(self, o: OrPattern, /) -> None: for p in o.patterns: p.accept(self) - def visit_value_pattern(self, o: ValuePattern) -> None: + def visit_value_pattern(self, o: ValuePattern, /) -> None: o.expr.accept(self) - def visit_sequence_pattern(self, o: SequencePattern) -> None: + def visit_sequence_pattern(self, o: SequencePattern, /) -> None: for p in o.patterns: p.accept(self) - def visit_starred_pattern(self, o: StarredPattern) -> None: + def visit_starred_pattern(self, o: StarredPattern, /) -> None: if o.capture is not None: o.capture.accept(self) - def visit_mapping_pattern(self, o: MappingPattern) -> None: + def visit_mapping_pattern(self, o: MappingPattern, /) -> None: for key in o.keys: key.accept(self) for value in o.values: @@ -400,18 +400,18 @@ def visit_mapping_pattern(self, o: MappingPattern) -> None: if o.rest is not None: o.rest.accept(self) - def visit_class_pattern(self, o: ClassPattern) -> None: + def visit_class_pattern(self, o: ClassPattern, /) -> None: o.class_ref.accept(self) for p in o.positionals: p.accept(self) for v in o.keyword_values: v.accept(self) - def visit_import(self, o: Import) -> None: + def visit_import(self, o: Import, /) -> None: for a in o.assignments: a.accept(self) - def visit_import_from(self, o: ImportFrom) -> None: + def visit_import_from(self, o: ImportFrom, /) -> None: for a in o.assignments: a.accept(self) @@ -432,402 +432,402 @@ def visit(self, o: Node) -> bool: # If returns True, will continue to nested nodes. return True - def visit_mypy_file(self, o: MypyFile) -> None: + def visit_mypy_file(self, o: MypyFile, /) -> None: if not self.visit(o): return super().visit_mypy_file(o) # Module structure - def visit_import(self, o: Import) -> None: + def visit_import(self, o: Import, /) -> None: if not self.visit(o): return super().visit_import(o) - def visit_import_from(self, o: ImportFrom) -> None: + def visit_import_from(self, o: ImportFrom, /) -> None: if not self.visit(o): return super().visit_import_from(o) - def visit_import_all(self, o: ImportAll) -> None: + def visit_import_all(self, o: ImportAll, /) -> None: if not self.visit(o): return super().visit_import_all(o) # Definitions - def visit_func_def(self, o: FuncDef) -> None: + def visit_func_def(self, o: FuncDef, /) -> None: if not self.visit(o): return super().visit_func_def(o) - def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: + def visit_overloaded_func_def(self, o: OverloadedFuncDef, /) -> None: if not self.visit(o): return super().visit_overloaded_func_def(o) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: if not self.visit(o): return super().visit_class_def(o) - def visit_global_decl(self, o: GlobalDecl) -> None: + def visit_global_decl(self, o: GlobalDecl, /) -> None: if not self.visit(o): return super().visit_global_decl(o) - def visit_nonlocal_decl(self, o: NonlocalDecl) -> None: + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: if not self.visit(o): return super().visit_nonlocal_decl(o) - def visit_decorator(self, o: Decorator) -> None: + def visit_decorator(self, o: Decorator, /) -> None: if not self.visit(o): return super().visit_decorator(o) - def visit_type_alias(self, o: TypeAlias) -> None: + def visit_type_alias(self, o: TypeAlias, /) -> None: if not self.visit(o): return super().visit_type_alias(o) # Statements - def visit_block(self, block: Block) -> None: + def visit_block(self, block: Block, /) -> None: if not self.visit(block): return super().visit_block(block) - def visit_expression_stmt(self, o: ExpressionStmt) -> None: + def visit_expression_stmt(self, o: ExpressionStmt, /) -> None: if not self.visit(o): return super().visit_expression_stmt(o) - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: if not self.visit(o): return super().visit_assignment_stmt(o) - def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt) -> None: + def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt, /) -> None: if not self.visit(o): return super().visit_operator_assignment_stmt(o) - def visit_while_stmt(self, o: WhileStmt) -> None: + def visit_while_stmt(self, o: WhileStmt, /) -> None: if not self.visit(o): return super().visit_while_stmt(o) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_for_stmt(self, o: ForStmt, /) -> None: if not self.visit(o): return super().visit_for_stmt(o) - def visit_return_stmt(self, o: ReturnStmt) -> None: + def visit_return_stmt(self, o: ReturnStmt, /) -> None: if not self.visit(o): return super().visit_return_stmt(o) - def visit_assert_stmt(self, o: AssertStmt) -> None: + def visit_assert_stmt(self, o: AssertStmt, /) -> None: if not self.visit(o): return super().visit_assert_stmt(o) - def visit_del_stmt(self, o: DelStmt) -> None: + def visit_del_stmt(self, o: DelStmt, /) -> None: if not self.visit(o): return super().visit_del_stmt(o) - def visit_if_stmt(self, o: IfStmt) -> None: + def visit_if_stmt(self, o: IfStmt, /) -> None: if not self.visit(o): return super().visit_if_stmt(o) - def visit_break_stmt(self, o: BreakStmt) -> None: + def visit_break_stmt(self, o: BreakStmt, /) -> None: if not self.visit(o): return super().visit_break_stmt(o) - def visit_continue_stmt(self, o: ContinueStmt) -> None: + def visit_continue_stmt(self, o: ContinueStmt, /) -> None: if not self.visit(o): return super().visit_continue_stmt(o) - def visit_pass_stmt(self, o: PassStmt) -> None: + def visit_pass_stmt(self, o: PassStmt, /) -> None: if not self.visit(o): return super().visit_pass_stmt(o) - def visit_raise_stmt(self, o: RaiseStmt) -> None: + def visit_raise_stmt(self, o: RaiseStmt, /) -> None: if not self.visit(o): return super().visit_raise_stmt(o) - def visit_try_stmt(self, o: TryStmt) -> None: + def visit_try_stmt(self, o: TryStmt, /) -> None: if not self.visit(o): return super().visit_try_stmt(o) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: if not self.visit(o): return super().visit_with_stmt(o) - def visit_match_stmt(self, o: MatchStmt) -> None: + def visit_match_stmt(self, o: MatchStmt, /) -> None: if not self.visit(o): return super().visit_match_stmt(o) # Expressions (default no-op implementation) - def visit_int_expr(self, o: IntExpr) -> None: + def visit_int_expr(self, o: IntExpr, /) -> None: if not self.visit(o): return super().visit_int_expr(o) - def visit_str_expr(self, o: StrExpr) -> None: + def visit_str_expr(self, o: StrExpr, /) -> None: if not self.visit(o): return super().visit_str_expr(o) - def visit_bytes_expr(self, o: BytesExpr) -> None: + def visit_bytes_expr(self, o: BytesExpr, /) -> None: if not self.visit(o): return super().visit_bytes_expr(o) - def visit_float_expr(self, o: FloatExpr) -> None: + def visit_float_expr(self, o: FloatExpr, /) -> None: if not self.visit(o): return super().visit_float_expr(o) - def visit_complex_expr(self, o: ComplexExpr) -> None: + def visit_complex_expr(self, o: ComplexExpr, /) -> None: if not self.visit(o): return super().visit_complex_expr(o) - def visit_ellipsis(self, o: EllipsisExpr) -> None: + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: if not self.visit(o): return super().visit_ellipsis(o) - def visit_star_expr(self, o: StarExpr) -> None: + def visit_star_expr(self, o: StarExpr, /) -> None: if not self.visit(o): return super().visit_star_expr(o) - def visit_name_expr(self, o: NameExpr) -> None: + def visit_name_expr(self, o: NameExpr, /) -> None: if not self.visit(o): return super().visit_name_expr(o) - def visit_member_expr(self, o: MemberExpr) -> None: + def visit_member_expr(self, o: MemberExpr, /) -> None: if not self.visit(o): return super().visit_member_expr(o) - def visit_yield_from_expr(self, o: YieldFromExpr) -> None: + def visit_yield_from_expr(self, o: YieldFromExpr, /) -> None: if not self.visit(o): return super().visit_yield_from_expr(o) - def visit_yield_expr(self, o: YieldExpr) -> None: + def visit_yield_expr(self, o: YieldExpr, /) -> None: if not self.visit(o): return super().visit_yield_expr(o) - def visit_call_expr(self, o: CallExpr) -> None: + def visit_call_expr(self, o: CallExpr, /) -> None: if not self.visit(o): return super().visit_call_expr(o) - def visit_op_expr(self, o: OpExpr) -> None: + def visit_op_expr(self, o: OpExpr, /) -> None: if not self.visit(o): return super().visit_op_expr(o) - def visit_comparison_expr(self, o: ComparisonExpr) -> None: + def visit_comparison_expr(self, o: ComparisonExpr, /) -> None: if not self.visit(o): return super().visit_comparison_expr(o) - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: if not self.visit(o): return super().visit_cast_expr(o) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: if not self.visit(o): return super().visit_assert_type_expr(o) - def visit_reveal_expr(self, o: RevealExpr) -> None: + def visit_reveal_expr(self, o: RevealExpr, /) -> None: if not self.visit(o): return super().visit_reveal_expr(o) - def visit_super_expr(self, o: SuperExpr) -> None: + def visit_super_expr(self, o: SuperExpr, /) -> None: if not self.visit(o): return super().visit_super_expr(o) - def visit_assignment_expr(self, o: AssignmentExpr) -> None: + def visit_assignment_expr(self, o: AssignmentExpr, /) -> None: if not self.visit(o): return super().visit_assignment_expr(o) - def visit_unary_expr(self, o: UnaryExpr) -> None: + def visit_unary_expr(self, o: UnaryExpr, /) -> None: if not self.visit(o): return super().visit_unary_expr(o) - def visit_list_expr(self, o: ListExpr) -> None: + def visit_list_expr(self, o: ListExpr, /) -> None: if not self.visit(o): return super().visit_list_expr(o) - def visit_dict_expr(self, o: DictExpr) -> None: + def visit_dict_expr(self, o: DictExpr, /) -> None: if not self.visit(o): return super().visit_dict_expr(o) - def visit_tuple_expr(self, o: TupleExpr) -> None: + def visit_tuple_expr(self, o: TupleExpr, /) -> None: if not self.visit(o): return super().visit_tuple_expr(o) - def visit_set_expr(self, o: SetExpr) -> None: + def visit_set_expr(self, o: SetExpr, /) -> None: if not self.visit(o): return super().visit_set_expr(o) - def visit_index_expr(self, o: IndexExpr) -> None: + def visit_index_expr(self, o: IndexExpr, /) -> None: if not self.visit(o): return super().visit_index_expr(o) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: if not self.visit(o): return super().visit_type_application(o) - def visit_lambda_expr(self, o: LambdaExpr) -> None: + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: if not self.visit(o): return super().visit_lambda_expr(o) - def visit_list_comprehension(self, o: ListComprehension) -> None: + def visit_list_comprehension(self, o: ListComprehension, /) -> None: if not self.visit(o): return super().visit_list_comprehension(o) - def visit_set_comprehension(self, o: SetComprehension) -> None: + def visit_set_comprehension(self, o: SetComprehension, /) -> None: if not self.visit(o): return super().visit_set_comprehension(o) - def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: + def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None: if not self.visit(o): return super().visit_dictionary_comprehension(o) - def visit_generator_expr(self, o: GeneratorExpr) -> None: + def visit_generator_expr(self, o: GeneratorExpr, /) -> None: if not self.visit(o): return super().visit_generator_expr(o) - def visit_slice_expr(self, o: SliceExpr) -> None: + def visit_slice_expr(self, o: SliceExpr, /) -> None: if not self.visit(o): return super().visit_slice_expr(o) - def visit_conditional_expr(self, o: ConditionalExpr) -> None: + def visit_conditional_expr(self, o: ConditionalExpr, /) -> None: if not self.visit(o): return super().visit_conditional_expr(o) - def visit_type_var_expr(self, o: TypeVarExpr) -> None: + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: if not self.visit(o): return super().visit_type_var_expr(o) - def visit_paramspec_expr(self, o: ParamSpecExpr) -> None: + def visit_paramspec_expr(self, o: ParamSpecExpr, /) -> None: if not self.visit(o): return super().visit_paramspec_expr(o) - def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr) -> None: + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr, /) -> None: if not self.visit(o): return super().visit_type_var_tuple_expr(o) - def visit_type_alias_expr(self, o: TypeAliasExpr) -> None: + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: if not self.visit(o): return super().visit_type_alias_expr(o) - def visit_namedtuple_expr(self, o: NamedTupleExpr) -> None: + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: if not self.visit(o): return super().visit_namedtuple_expr(o) - def visit_enum_call_expr(self, o: EnumCallExpr) -> None: + def visit_enum_call_expr(self, o: EnumCallExpr, /) -> None: if not self.visit(o): return super().visit_enum_call_expr(o) - def visit_typeddict_expr(self, o: TypedDictExpr) -> None: + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: if not self.visit(o): return super().visit_typeddict_expr(o) - def visit_newtype_expr(self, o: NewTypeExpr) -> None: + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: if not self.visit(o): return super().visit_newtype_expr(o) - def visit_await_expr(self, o: AwaitExpr) -> None: + def visit_await_expr(self, o: AwaitExpr, /) -> None: if not self.visit(o): return super().visit_await_expr(o) # Patterns - def visit_as_pattern(self, o: AsPattern) -> None: + def visit_as_pattern(self, o: AsPattern, /) -> None: if not self.visit(o): return super().visit_as_pattern(o) - def visit_or_pattern(self, o: OrPattern) -> None: + def visit_or_pattern(self, o: OrPattern, /) -> None: if not self.visit(o): return super().visit_or_pattern(o) - def visit_value_pattern(self, o: ValuePattern) -> None: + def visit_value_pattern(self, o: ValuePattern, /) -> None: if not self.visit(o): return super().visit_value_pattern(o) - def visit_singleton_pattern(self, o: SingletonPattern) -> None: + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: if not self.visit(o): return super().visit_singleton_pattern(o) - def visit_sequence_pattern(self, o: SequencePattern) -> None: + def visit_sequence_pattern(self, o: SequencePattern, /) -> None: if not self.visit(o): return super().visit_sequence_pattern(o) - def visit_starred_pattern(self, o: StarredPattern) -> None: + def visit_starred_pattern(self, o: StarredPattern, /) -> None: if not self.visit(o): return super().visit_starred_pattern(o) - def visit_mapping_pattern(self, o: MappingPattern) -> None: + def visit_mapping_pattern(self, o: MappingPattern, /) -> None: if not self.visit(o): return super().visit_mapping_pattern(o) - def visit_class_pattern(self, o: ClassPattern) -> None: + def visit_class_pattern(self, o: ClassPattern, /) -> None: if not self.visit(o): return super().visit_class_pattern(o) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 8aac7e5c2bbd..a6888f21a402 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -62,87 +62,87 @@ class TypeVisitor(Generic[T]): """ @abstractmethod - def visit_unbound_type(self, t: UnboundType) -> T: + def visit_unbound_type(self, t: UnboundType, /) -> T: pass @abstractmethod - def visit_any(self, t: AnyType) -> T: + def visit_any(self, t: AnyType, /) -> T: pass @abstractmethod - def visit_none_type(self, t: NoneType) -> T: + def visit_none_type(self, t: NoneType, /) -> T: pass @abstractmethod - def visit_uninhabited_type(self, t: UninhabitedType) -> T: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> T: pass @abstractmethod - def visit_erased_type(self, t: ErasedType) -> T: + def visit_erased_type(self, t: ErasedType, /) -> T: pass @abstractmethod - def visit_deleted_type(self, t: DeletedType) -> T: + def visit_deleted_type(self, t: DeletedType, /) -> T: pass @abstractmethod - def visit_type_var(self, t: TypeVarType) -> T: + def visit_type_var(self, t: TypeVarType, /) -> T: pass @abstractmethod - def visit_param_spec(self, t: ParamSpecType) -> T: + def visit_param_spec(self, t: ParamSpecType, /) -> T: pass @abstractmethod - def visit_parameters(self, t: Parameters) -> T: + def visit_parameters(self, t: Parameters, /) -> T: pass @abstractmethod - def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> T: pass @abstractmethod - def visit_instance(self, t: Instance) -> T: + def visit_instance(self, t: Instance, /) -> T: pass @abstractmethod - def visit_callable_type(self, t: CallableType) -> T: + def visit_callable_type(self, t: CallableType, /) -> T: pass @abstractmethod - def visit_overloaded(self, t: Overloaded) -> T: + def visit_overloaded(self, t: Overloaded, /) -> T: pass @abstractmethod - def visit_tuple_type(self, t: TupleType) -> T: + def visit_tuple_type(self, t: TupleType, /) -> T: pass @abstractmethod - def visit_typeddict_type(self, t: TypedDictType) -> T: + def visit_typeddict_type(self, t: TypedDictType, /) -> T: pass @abstractmethod - def visit_literal_type(self, t: LiteralType) -> T: + def visit_literal_type(self, t: LiteralType, /) -> T: pass @abstractmethod - def visit_union_type(self, t: UnionType) -> T: + def visit_union_type(self, t: UnionType, /) -> T: pass @abstractmethod - def visit_partial_type(self, t: PartialType) -> T: + def visit_partial_type(self, t: PartialType, /) -> T: pass @abstractmethod - def visit_type_type(self, t: TypeType) -> T: + def visit_type_type(self, t: TypeType, /) -> T: pass @abstractmethod - def visit_type_alias_type(self, t: TypeAliasType) -> T: + def visit_type_alias_type(self, t: TypeAliasType, /) -> T: pass @abstractmethod - def visit_unpack_type(self, t: UnpackType) -> T: + def visit_unpack_type(self, t: UnpackType, /) -> T: pass @@ -155,23 +155,23 @@ class SyntheticTypeVisitor(TypeVisitor[T]): """ @abstractmethod - def visit_type_list(self, t: TypeList) -> T: + def visit_type_list(self, t: TypeList, /) -> T: pass @abstractmethod - def visit_callable_argument(self, t: CallableArgument) -> T: + def visit_callable_argument(self, t: CallableArgument, /) -> T: pass @abstractmethod - def visit_ellipsis_type(self, t: EllipsisType) -> T: + def visit_ellipsis_type(self, t: EllipsisType, /) -> T: pass @abstractmethod - def visit_raw_expression_type(self, t: RawExpressionType) -> T: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> T: pass @abstractmethod - def visit_placeholder_type(self, t: PlaceholderType) -> T: + def visit_placeholder_type(self, t: PlaceholderType, /) -> T: pass @@ -201,25 +201,25 @@ def set_cached(self, orig: Type, new: Type) -> None: self.cache = {} self.cache[orig] = new - def visit_unbound_type(self, t: UnboundType) -> Type: + def visit_unbound_type(self, t: UnboundType, /) -> Type: return t - def visit_any(self, t: AnyType) -> Type: + def visit_any(self, t: AnyType, /) -> Type: return t - def visit_none_type(self, t: NoneType) -> Type: + def visit_none_type(self, t: NoneType, /) -> Type: return t - def visit_uninhabited_type(self, t: UninhabitedType) -> Type: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> Type: return t - def visit_erased_type(self, t: ErasedType) -> Type: + def visit_erased_type(self, t: ErasedType, /) -> Type: return t - def visit_deleted_type(self, t: DeletedType) -> Type: + def visit_deleted_type(self, t: DeletedType, /) -> Type: return t - def visit_instance(self, t: Instance) -> Type: + def visit_instance(self, t: Instance, /) -> Type: last_known_value: LiteralType | None = None if t.last_known_value is not None: raw_last_known_value = t.last_known_value.accept(self) @@ -234,32 +234,32 @@ def visit_instance(self, t: Instance) -> Type: extra_attrs=t.extra_attrs, ) - def visit_type_var(self, t: TypeVarType) -> Type: + def visit_type_var(self, t: TypeVarType, /) -> Type: return t - def visit_param_spec(self, t: ParamSpecType) -> Type: + def visit_param_spec(self, t: ParamSpecType, /) -> Type: return t - def visit_parameters(self, t: Parameters) -> Type: + def visit_parameters(self, t: Parameters, /) -> Type: return t.copy_modified(arg_types=self.translate_types(t.arg_types)) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> Type: return t - def visit_partial_type(self, t: PartialType) -> Type: + def visit_partial_type(self, t: PartialType, /) -> Type: return t - def visit_unpack_type(self, t: UnpackType) -> Type: + def visit_unpack_type(self, t: UnpackType, /) -> Type: return UnpackType(t.type.accept(self)) - def visit_callable_type(self, t: CallableType) -> Type: + def visit_callable_type(self, t: CallableType, /) -> Type: return t.copy_modified( arg_types=self.translate_types(t.arg_types), ret_type=t.ret_type.accept(self), variables=self.translate_variables(t.variables), ) - def visit_tuple_type(self, t: TupleType) -> Type: + def visit_tuple_type(self, t: TupleType, /) -> Type: return TupleType( self.translate_types(t.items), # TODO: This appears to be unsafe. @@ -268,7 +268,7 @@ def visit_tuple_type(self, t: TupleType) -> Type: t.column, ) - def visit_typeddict_type(self, t: TypedDictType) -> Type: + def visit_typeddict_type(self, t: TypedDictType, /) -> Type: # Use cache to avoid O(n**2) or worse expansion of types during translation if cached := self.get_cached(t): return cached @@ -285,12 +285,12 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: self.set_cached(t, result) return result - def visit_literal_type(self, t: LiteralType) -> Type: + def visit_literal_type(self, t: LiteralType, /) -> Type: fallback = t.fallback.accept(self) assert isinstance(fallback, Instance) # type: ignore[misc] return LiteralType(value=t.value, fallback=fallback, line=t.line, column=t.column) - def visit_union_type(self, t: UnionType) -> Type: + def visit_union_type(self, t: UnionType, /) -> Type: # Use cache to avoid O(n**2) or worse expansion of types during translation # (only for large unions, since caching adds overhead) use_cache = len(t.items) > 3 @@ -315,7 +315,7 @@ def translate_variables( ) -> Sequence[TypeVarLikeType]: return variables - def visit_overloaded(self, t: Overloaded) -> Type: + def visit_overloaded(self, t: Overloaded, /) -> Type: items: list[CallableType] = [] for item in t.items: new = item.accept(self) @@ -323,11 +323,11 @@ def visit_overloaded(self, t: Overloaded) -> Type: items.append(new) return Overloaded(items=items) - def visit_type_type(self, t: TypeType) -> Type: + def visit_type_type(self, t: TypeType, /) -> Type: return TypeType.make_normalized(t.item.accept(self), line=t.line, column=t.column) @abstractmethod - def visit_type_alias_type(self, t: TypeAliasType) -> Type: + def visit_type_alias_type(self, t: TypeAliasType, /) -> Type: # This method doesn't have a default implementation for type translators, # because type aliases are special: some information is contained in the # TypeAlias node, and we normally don't generate new nodes. Every subclass @@ -359,83 +359,83 @@ def __init__(self, strategy: Callable[[list[T]], T]) -> None: # to skip targets in some cases (e.g. when collecting type variables). self.skip_alias_target = False - def visit_unbound_type(self, t: UnboundType) -> T: + def visit_unbound_type(self, t: UnboundType, /) -> T: return self.query_types(t.args) - def visit_type_list(self, t: TypeList) -> T: + def visit_type_list(self, t: TypeList, /) -> T: return self.query_types(t.items) - def visit_callable_argument(self, t: CallableArgument) -> T: + def visit_callable_argument(self, t: CallableArgument, /) -> T: return t.typ.accept(self) - def visit_any(self, t: AnyType) -> T: + def visit_any(self, t: AnyType, /) -> T: return self.strategy([]) - def visit_uninhabited_type(self, t: UninhabitedType) -> T: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> T: return self.strategy([]) - def visit_none_type(self, t: NoneType) -> T: + def visit_none_type(self, t: NoneType, /) -> T: return self.strategy([]) - def visit_erased_type(self, t: ErasedType) -> T: + def visit_erased_type(self, t: ErasedType, /) -> T: return self.strategy([]) - def visit_deleted_type(self, t: DeletedType) -> T: + def visit_deleted_type(self, t: DeletedType, /) -> T: return self.strategy([]) - def visit_type_var(self, t: TypeVarType) -> T: + def visit_type_var(self, t: TypeVarType, /) -> T: return self.query_types([t.upper_bound, t.default] + t.values) - def visit_param_spec(self, t: ParamSpecType) -> T: + def visit_param_spec(self, t: ParamSpecType, /) -> T: return self.query_types([t.upper_bound, t.default, t.prefix]) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> T: return self.query_types([t.upper_bound, t.default]) - def visit_unpack_type(self, t: UnpackType) -> T: + def visit_unpack_type(self, t: UnpackType, /) -> T: return self.query_types([t.type]) - def visit_parameters(self, t: Parameters) -> T: + def visit_parameters(self, t: Parameters, /) -> T: return self.query_types(t.arg_types) - def visit_partial_type(self, t: PartialType) -> T: + def visit_partial_type(self, t: PartialType, /) -> T: return self.strategy([]) - def visit_instance(self, t: Instance) -> T: + def visit_instance(self, t: Instance, /) -> T: return self.query_types(t.args) - def visit_callable_type(self, t: CallableType) -> T: + def visit_callable_type(self, t: CallableType, /) -> T: # FIX generics return self.query_types(t.arg_types + [t.ret_type]) - def visit_tuple_type(self, t: TupleType) -> T: + def visit_tuple_type(self, t: TupleType, /) -> T: return self.query_types(t.items) - def visit_typeddict_type(self, t: TypedDictType) -> T: + def visit_typeddict_type(self, t: TypedDictType, /) -> T: return self.query_types(t.items.values()) - def visit_raw_expression_type(self, t: RawExpressionType) -> T: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> T: return self.strategy([]) - def visit_literal_type(self, t: LiteralType) -> T: + def visit_literal_type(self, t: LiteralType, /) -> T: return self.strategy([]) - def visit_union_type(self, t: UnionType) -> T: + def visit_union_type(self, t: UnionType, /) -> T: return self.query_types(t.items) - def visit_overloaded(self, t: Overloaded) -> T: + def visit_overloaded(self, t: Overloaded, /) -> T: return self.query_types(t.items) - def visit_type_type(self, t: TypeType) -> T: + def visit_type_type(self, t: TypeType, /) -> T: return t.item.accept(self) - def visit_ellipsis_type(self, t: EllipsisType) -> T: + def visit_ellipsis_type(self, t: EllipsisType, /) -> T: return self.strategy([]) - def visit_placeholder_type(self, t: PlaceholderType) -> T: + def visit_placeholder_type(self, t: PlaceholderType, /) -> T: return self.query_types(t.args) - def visit_type_alias_type(self, t: TypeAliasType) -> T: + def visit_type_alias_type(self, t: TypeAliasType, /) -> T: # Skip type aliases already visited types to avoid infinite recursion. # TODO: Ideally we should fire subvisitors here (or use caching) if we care # about duplicates. @@ -493,52 +493,52 @@ def reset(self) -> None: """ self.seen_aliases = None - def visit_unbound_type(self, t: UnboundType) -> bool: + def visit_unbound_type(self, t: UnboundType, /) -> bool: return self.query_types(t.args) - def visit_type_list(self, t: TypeList) -> bool: + def visit_type_list(self, t: TypeList, /) -> bool: return self.query_types(t.items) - def visit_callable_argument(self, t: CallableArgument) -> bool: + def visit_callable_argument(self, t: CallableArgument, /) -> bool: return t.typ.accept(self) - def visit_any(self, t: AnyType) -> bool: + def visit_any(self, t: AnyType, /) -> bool: return self.default - def visit_uninhabited_type(self, t: UninhabitedType) -> bool: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> bool: return self.default - def visit_none_type(self, t: NoneType) -> bool: + def visit_none_type(self, t: NoneType, /) -> bool: return self.default - def visit_erased_type(self, t: ErasedType) -> bool: + def visit_erased_type(self, t: ErasedType, /) -> bool: return self.default - def visit_deleted_type(self, t: DeletedType) -> bool: + def visit_deleted_type(self, t: DeletedType, /) -> bool: return self.default - def visit_type_var(self, t: TypeVarType) -> bool: + def visit_type_var(self, t: TypeVarType, /) -> bool: return self.query_types([t.upper_bound, t.default] + t.values) - def visit_param_spec(self, t: ParamSpecType) -> bool: + def visit_param_spec(self, t: ParamSpecType, /) -> bool: return self.query_types([t.upper_bound, t.default]) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> bool: return self.query_types([t.upper_bound, t.default]) - def visit_unpack_type(self, t: UnpackType) -> bool: + def visit_unpack_type(self, t: UnpackType, /) -> bool: return self.query_types([t.type]) - def visit_parameters(self, t: Parameters) -> bool: + def visit_parameters(self, t: Parameters, /) -> bool: return self.query_types(t.arg_types) - def visit_partial_type(self, t: PartialType) -> bool: + def visit_partial_type(self, t: PartialType, /) -> bool: return self.default - def visit_instance(self, t: Instance) -> bool: + def visit_instance(self, t: Instance, /) -> bool: return self.query_types(t.args) - def visit_callable_type(self, t: CallableType) -> bool: + def visit_callable_type(self, t: CallableType, /) -> bool: # FIX generics # Avoid allocating any objects here as an optimization. args = self.query_types(t.arg_types) @@ -548,34 +548,34 @@ def visit_callable_type(self, t: CallableType) -> bool: else: return args and ret - def visit_tuple_type(self, t: TupleType) -> bool: + def visit_tuple_type(self, t: TupleType, /) -> bool: return self.query_types(t.items) - def visit_typeddict_type(self, t: TypedDictType) -> bool: + def visit_typeddict_type(self, t: TypedDictType, /) -> bool: return self.query_types(list(t.items.values())) - def visit_raw_expression_type(self, t: RawExpressionType) -> bool: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> bool: return self.default - def visit_literal_type(self, t: LiteralType) -> bool: + def visit_literal_type(self, t: LiteralType, /) -> bool: return self.default - def visit_union_type(self, t: UnionType) -> bool: + def visit_union_type(self, t: UnionType, /) -> bool: return self.query_types(t.items) - def visit_overloaded(self, t: Overloaded) -> bool: + def visit_overloaded(self, t: Overloaded, /) -> bool: return self.query_types(t.items) # type: ignore[arg-type] - def visit_type_type(self, t: TypeType) -> bool: + def visit_type_type(self, t: TypeType, /) -> bool: return t.item.accept(self) - def visit_ellipsis_type(self, t: EllipsisType) -> bool: + def visit_ellipsis_type(self, t: EllipsisType, /) -> bool: return self.default - def visit_placeholder_type(self, t: PlaceholderType) -> bool: + def visit_placeholder_type(self, t: PlaceholderType, /) -> bool: return self.query_types(t.args) - def visit_type_alias_type(self, t: TypeAliasType) -> bool: + def visit_type_alias_type(self, t: TypeAliasType, /) -> bool: # Skip type aliases already visited types to avoid infinite recursion. # TODO: Ideally we should fire subvisitors here (or use caching) if we care # about duplicates. diff --git a/mypy/types.py b/mypy/types.py index e92ab0889991..c174f94c066d 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3258,43 +3258,43 @@ def __init__(self, id_mapper: IdMapper | None = None, *, options: Options) -> No self.any_as_dots = False self.options = options - def visit_unbound_type(self, t: UnboundType) -> str: + def visit_unbound_type(self, t: UnboundType, /) -> str: s = t.name + "?" if t.args: s += f"[{self.list_str(t.args)}]" return s - def visit_type_list(self, t: TypeList) -> str: + def visit_type_list(self, t: TypeList, /) -> str: return f"" - def visit_callable_argument(self, t: CallableArgument) -> str: + def visit_callable_argument(self, t: CallableArgument, /) -> str: typ = t.typ.accept(self) if t.name is None: return f"{t.constructor}({typ})" else: return f"{t.constructor}({typ}, {t.name})" - def visit_any(self, t: AnyType) -> str: + def visit_any(self, t: AnyType, /) -> str: if self.any_as_dots and t.type_of_any == TypeOfAny.special_form: return "..." return "Any" - def visit_none_type(self, t: NoneType) -> str: + def visit_none_type(self, t: NoneType, /) -> str: return "None" - def visit_uninhabited_type(self, t: UninhabitedType) -> str: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> str: return "Never" - def visit_erased_type(self, t: ErasedType) -> str: + def visit_erased_type(self, t: ErasedType, /) -> str: return "" - def visit_deleted_type(self, t: DeletedType) -> str: + def visit_deleted_type(self, t: DeletedType, /) -> str: if t.source is None: return "" else: return f"" - def visit_instance(self, t: Instance) -> str: + 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. @@ -3314,7 +3314,7 @@ def visit_instance(self, t: Instance) -> str: s += f"<{self.id_mapper.id(t.type)}>" return s - def visit_type_var(self, t: TypeVarType) -> str: + def visit_type_var(self, t: TypeVarType, /) -> str: if t.name is None: # Anonymous type variable type (only numeric id). s = f"`{t.id}" @@ -3327,7 +3327,7 @@ def visit_type_var(self, t: TypeVarType) -> str: s += f" = {t.default.accept(self)}" return s - def visit_param_spec(self, t: ParamSpecType) -> str: + def visit_param_spec(self, t: ParamSpecType, /) -> str: # prefixes are displayed as Concatenate s = "" if t.prefix.arg_types: @@ -3344,7 +3344,7 @@ def visit_param_spec(self, t: ParamSpecType) -> str: s += f" = {t.default.accept(self)}" return s - def visit_parameters(self, t: Parameters) -> str: + def visit_parameters(self, t: Parameters, /) -> str: # This is copied from visit_callable -- is there a way to decrease duplication? if t.is_ellipsis_args: return "..." @@ -3373,7 +3373,7 @@ def visit_parameters(self, t: Parameters) -> str: return f"[{s}]" - def visit_type_var_tuple(self, t: TypeVarTupleType) -> str: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> str: if t.name is None: # Anonymous type variable type (only numeric id). s = f"`{t.id}" @@ -3384,7 +3384,7 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> str: s += f" = {t.default.accept(self)}" return s - def visit_callable_type(self, t: CallableType) -> str: + def visit_callable_type(self, t: CallableType, /) -> str: param_spec = t.param_spec() if param_spec is not None: num_skip = 2 @@ -3457,13 +3457,13 @@ def visit_callable_type(self, t: CallableType) -> str: return f"def {s}" - def visit_overloaded(self, t: Overloaded) -> str: + def visit_overloaded(self, t: Overloaded, /) -> str: a = [] for i in t.items: a.append(i.accept(self)) return f"Overload({', '.join(a)})" - def visit_tuple_type(self, t: TupleType) -> 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: @@ -3472,7 +3472,7 @@ def visit_tuple_type(self, t: TupleType) -> str: 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 visit_typeddict_type(self, t: TypedDictType, /) -> str: def item_str(name: str, typ: str) -> str: modifier = "" if name not in t.required_keys: @@ -3492,36 +3492,36 @@ def item_str(name: str, typ: str) -> str: prefix = repr(t.fallback.type.fullname) + ", " return f"TypedDict({prefix}{s})" - def visit_raw_expression_type(self, t: RawExpressionType) -> str: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> str: return repr(t.literal_value) - def visit_literal_type(self, t: LiteralType) -> str: + def visit_literal_type(self, t: LiteralType, /) -> str: return f"Literal[{t.value_repr()}]" - def visit_union_type(self, t: UnionType) -> str: + def visit_union_type(self, t: UnionType, /) -> str: s = self.list_str(t.items) return f"Union[{s}]" - def visit_partial_type(self, t: PartialType) -> str: + def visit_partial_type(self, t: PartialType, /) -> str: if t.type is None: return "" else: return "".format(t.type.name, ", ".join(["?"] * len(t.type.type_vars))) - def visit_ellipsis_type(self, t: EllipsisType) -> str: + def visit_ellipsis_type(self, t: EllipsisType, /) -> str: return "..." - def visit_type_type(self, t: TypeType) -> str: + 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)}]" - def visit_placeholder_type(self, t: PlaceholderType) -> str: + def visit_placeholder_type(self, t: PlaceholderType, /) -> str: return f"" - def visit_type_alias_type(self, t: TypeAliasType) -> str: + def visit_type_alias_type(self, t: TypeAliasType, /) -> str: if t.alias is not None: unrolled, recursed = t._partial_expansion() self.any_as_dots = recursed @@ -3530,7 +3530,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> str: return type_str return "" - def visit_unpack_type(self, t: UnpackType) -> str: + def visit_unpack_type(self, t: UnpackType, /) -> str: return f"Unpack[{t.type.accept(self)}]" def list_str(self, a: Iterable[Type]) -> str: @@ -3546,19 +3546,19 @@ def list_str(self, a: Iterable[Type]) -> str: class TrivialSyntheticTypeTranslator(TypeTranslator, SyntheticTypeVisitor[Type]): """A base class for type translators that need to be run during semantic analysis.""" - def visit_placeholder_type(self, t: PlaceholderType) -> Type: + def visit_placeholder_type(self, t: PlaceholderType, /) -> Type: return t - def visit_callable_argument(self, t: CallableArgument) -> Type: + def visit_callable_argument(self, t: CallableArgument, /) -> Type: return t - def visit_ellipsis_type(self, t: EllipsisType) -> Type: + def visit_ellipsis_type(self, t: EllipsisType, /) -> Type: return t - def visit_raw_expression_type(self, t: RawExpressionType) -> Type: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> Type: return t - def visit_type_list(self, t: TypeList) -> Type: + def visit_type_list(self, t: TypeList, /) -> Type: return t diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index a28bbf422b61..e2333ae8aa6d 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -42,45 +42,45 @@ class TypeTraverserVisitor(SyntheticTypeVisitor[None]): # Atomic types - def visit_any(self, t: AnyType) -> None: + def visit_any(self, t: AnyType, /) -> None: pass - def visit_uninhabited_type(self, t: UninhabitedType) -> None: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> None: pass - def visit_none_type(self, t: NoneType) -> None: + def visit_none_type(self, t: NoneType, /) -> None: pass - def visit_erased_type(self, t: ErasedType) -> None: + def visit_erased_type(self, t: ErasedType, /) -> None: pass - def visit_deleted_type(self, t: DeletedType) -> None: + def visit_deleted_type(self, t: DeletedType, /) -> None: pass - def visit_type_var(self, t: TypeVarType) -> None: + def visit_type_var(self, t: TypeVarType, /) -> None: # Note that type variable values and upper bound aren't treated as # components, since they are components of the type variable # definition. We want to traverse everything just once. t.default.accept(self) - def visit_param_spec(self, t: ParamSpecType) -> None: + def visit_param_spec(self, t: ParamSpecType, /) -> None: t.default.accept(self) - def visit_parameters(self, t: Parameters) -> None: + def visit_parameters(self, t: Parameters, /) -> None: self.traverse_types(t.arg_types) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> None: t.default.accept(self) - def visit_literal_type(self, t: LiteralType) -> None: + def visit_literal_type(self, t: LiteralType, /) -> None: t.fallback.accept(self) # Composite types - def visit_instance(self, t: Instance) -> None: + def visit_instance(self, t: Instance, /) -> None: self.traverse_types(t.args) - def visit_callable_type(self, t: CallableType) -> None: + def visit_callable_type(self, t: CallableType, /) -> None: # FIX generics self.traverse_types(t.arg_types) t.ret_type.accept(self) @@ -92,57 +92,57 @@ def visit_callable_type(self, t: CallableType) -> None: if t.type_is is not None: t.type_is.accept(self) - def visit_tuple_type(self, t: TupleType) -> None: + def visit_tuple_type(self, t: TupleType, /) -> None: self.traverse_types(t.items) t.partial_fallback.accept(self) - def visit_typeddict_type(self, t: TypedDictType) -> None: + def visit_typeddict_type(self, t: TypedDictType, /) -> None: self.traverse_types(t.items.values()) t.fallback.accept(self) - def visit_union_type(self, t: UnionType) -> None: + def visit_union_type(self, t: UnionType, /) -> None: self.traverse_types(t.items) - def visit_overloaded(self, t: Overloaded) -> None: + def visit_overloaded(self, t: Overloaded, /) -> None: self.traverse_types(t.items) - def visit_type_type(self, t: TypeType) -> None: + def visit_type_type(self, t: TypeType, /) -> None: t.item.accept(self) # Special types (not real types) - def visit_callable_argument(self, t: CallableArgument) -> None: + def visit_callable_argument(self, t: CallableArgument, /) -> None: t.typ.accept(self) - def visit_unbound_type(self, t: UnboundType) -> None: + def visit_unbound_type(self, t: UnboundType, /) -> None: self.traverse_types(t.args) - def visit_type_list(self, t: TypeList) -> None: + def visit_type_list(self, t: TypeList, /) -> None: self.traverse_types(t.items) - def visit_ellipsis_type(self, t: EllipsisType) -> None: + def visit_ellipsis_type(self, t: EllipsisType, /) -> None: pass - def visit_placeholder_type(self, t: PlaceholderType) -> None: + def visit_placeholder_type(self, t: PlaceholderType, /) -> None: self.traverse_types(t.args) - def visit_partial_type(self, t: PartialType) -> None: + def visit_partial_type(self, t: PartialType, /) -> None: pass - def visit_raw_expression_type(self, t: RawExpressionType) -> None: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> None: pass - def visit_type_alias_type(self, t: TypeAliasType) -> None: + def visit_type_alias_type(self, t: TypeAliasType, /) -> None: # TODO: sometimes we want to traverse target as well # We need to find a way to indicate explicitly the intent, # maybe make this method abstract (like for TypeTranslator)? self.traverse_types(t.args) - def visit_unpack_type(self, t: UnpackType) -> None: + def visit_unpack_type(self, t: UnpackType, /) -> None: t.type.accept(self) # Helpers - def traverse_types(self, types: Iterable[Type]) -> None: + def traverse_types(self, types: Iterable[Type], /) -> None: for typ in types: typ.accept(self) diff --git a/mypy/visitor.py b/mypy/visitor.py index 340e1af64e00..6613b6cbb144 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -20,179 +20,179 @@ @mypyc_attr(allow_interpreted_subclasses=True) class ExpressionVisitor(Generic[T]): @abstractmethod - def visit_int_expr(self, o: mypy.nodes.IntExpr) -> T: + def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: pass @abstractmethod - def visit_str_expr(self, o: mypy.nodes.StrExpr) -> T: + def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: pass @abstractmethod - def visit_bytes_expr(self, o: mypy.nodes.BytesExpr) -> T: + def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: pass @abstractmethod - def visit_float_expr(self, o: mypy.nodes.FloatExpr) -> T: + def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: pass @abstractmethod - def visit_complex_expr(self, o: mypy.nodes.ComplexExpr) -> T: + def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: pass @abstractmethod - def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr) -> T: + def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: pass @abstractmethod - def visit_star_expr(self, o: mypy.nodes.StarExpr) -> T: + def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: pass @abstractmethod - def visit_name_expr(self, o: mypy.nodes.NameExpr) -> T: + def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: pass @abstractmethod - def visit_member_expr(self, o: mypy.nodes.MemberExpr) -> T: + def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: pass @abstractmethod - def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr) -> T: + def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: pass @abstractmethod - def visit_yield_expr(self, o: mypy.nodes.YieldExpr) -> T: + def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: pass @abstractmethod - def visit_call_expr(self, o: mypy.nodes.CallExpr) -> T: + def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: pass @abstractmethod - def visit_op_expr(self, o: mypy.nodes.OpExpr) -> T: + def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: pass @abstractmethod - def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> T: + def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: pass @abstractmethod - def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> T: + def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: pass @abstractmethod - def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> T: + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: pass @abstractmethod - def visit_reveal_expr(self, o: mypy.nodes.RevealExpr) -> T: + def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: pass @abstractmethod - def visit_super_expr(self, o: mypy.nodes.SuperExpr) -> T: + def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: pass @abstractmethod - def visit_unary_expr(self, o: mypy.nodes.UnaryExpr) -> T: + def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: pass @abstractmethod - def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr) -> T: + def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: pass @abstractmethod - def visit_list_expr(self, o: mypy.nodes.ListExpr) -> T: + def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: pass @abstractmethod - def visit_dict_expr(self, o: mypy.nodes.DictExpr) -> T: + def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: pass @abstractmethod - def visit_tuple_expr(self, o: mypy.nodes.TupleExpr) -> T: + def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: pass @abstractmethod - def visit_set_expr(self, o: mypy.nodes.SetExpr) -> T: + def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: pass @abstractmethod - def visit_index_expr(self, o: mypy.nodes.IndexExpr) -> T: + def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: pass @abstractmethod - def visit_type_application(self, o: mypy.nodes.TypeApplication) -> T: + def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: pass @abstractmethod - def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr) -> T: + def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: pass @abstractmethod - def visit_list_comprehension(self, o: mypy.nodes.ListComprehension) -> T: + def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: pass @abstractmethod - def visit_set_comprehension(self, o: mypy.nodes.SetComprehension) -> T: + def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: pass @abstractmethod - def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension) -> T: + def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: pass @abstractmethod - def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr) -> T: + def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: pass @abstractmethod - def visit_slice_expr(self, o: mypy.nodes.SliceExpr) -> T: + def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: pass @abstractmethod - def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr) -> T: + def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: pass @abstractmethod - def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> T: + def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: pass @abstractmethod - def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> T: + def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: pass @abstractmethod - def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> T: + def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: pass @abstractmethod - def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> T: + def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: pass @abstractmethod - def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> T: + def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: pass @abstractmethod - def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> T: + def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: pass @abstractmethod - def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> T: + def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: pass @abstractmethod - def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> T: + def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: pass @abstractmethod - def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> T: + def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: pass @abstractmethod - def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> T: + def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: pass @abstractmethod - def visit_temp_node(self, o: mypy.nodes.TempNode) -> T: + def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: pass @@ -202,115 +202,115 @@ class StatementVisitor(Generic[T]): # Definitions @abstractmethod - def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt) -> T: + def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: pass @abstractmethod - def visit_for_stmt(self, o: mypy.nodes.ForStmt) -> T: + def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: pass @abstractmethod - def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> T: + def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: pass @abstractmethod - def visit_del_stmt(self, o: mypy.nodes.DelStmt) -> T: + def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: pass @abstractmethod - def visit_func_def(self, o: mypy.nodes.FuncDef) -> T: + def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: pass @abstractmethod - def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef) -> T: + def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: pass @abstractmethod - def visit_class_def(self, o: mypy.nodes.ClassDef) -> T: + def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: pass @abstractmethod - def visit_global_decl(self, o: mypy.nodes.GlobalDecl) -> T: + def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: pass @abstractmethod - def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> T: + def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: pass @abstractmethod - def visit_decorator(self, o: mypy.nodes.Decorator) -> T: + def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: pass # Module structure @abstractmethod - def visit_import(self, o: mypy.nodes.Import) -> T: + def visit_import(self, o: mypy.nodes.Import, /) -> T: pass @abstractmethod - def visit_import_from(self, o: mypy.nodes.ImportFrom) -> T: + def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: pass @abstractmethod - def visit_import_all(self, o: mypy.nodes.ImportAll) -> T: + def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: pass # Statements @abstractmethod - def visit_block(self, o: mypy.nodes.Block) -> T: + def visit_block(self, o: mypy.nodes.Block, /) -> T: pass @abstractmethod - def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt) -> T: + def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: pass @abstractmethod - def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt) -> T: + def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: pass @abstractmethod - def visit_while_stmt(self, o: mypy.nodes.WhileStmt) -> T: + def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: pass @abstractmethod - def visit_return_stmt(self, o: mypy.nodes.ReturnStmt) -> T: + def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: pass @abstractmethod - def visit_assert_stmt(self, o: mypy.nodes.AssertStmt) -> T: + def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: pass @abstractmethod - def visit_if_stmt(self, o: mypy.nodes.IfStmt) -> T: + def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: pass @abstractmethod - def visit_break_stmt(self, o: mypy.nodes.BreakStmt) -> T: + def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: pass @abstractmethod - def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt) -> T: + def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: pass @abstractmethod - def visit_pass_stmt(self, o: mypy.nodes.PassStmt) -> T: + def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: pass @abstractmethod - def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt) -> T: + def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: pass @abstractmethod - def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> T: + def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: pass @abstractmethod - def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: + def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: pass @abstractmethod - def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: pass @@ -318,35 +318,35 @@ def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: @mypyc_attr(allow_interpreted_subclasses=True) class PatternVisitor(Generic[T]): @abstractmethod - def visit_as_pattern(self, o: mypy.patterns.AsPattern) -> T: + def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: pass @abstractmethod - def visit_or_pattern(self, o: mypy.patterns.OrPattern) -> T: + def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: pass @abstractmethod - def visit_value_pattern(self, o: mypy.patterns.ValuePattern) -> T: + def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: pass @abstractmethod - def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern) -> T: + def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: pass @abstractmethod - def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern) -> T: + def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: pass @abstractmethod - def visit_starred_pattern(self, o: mypy.patterns.StarredPattern) -> T: + def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: pass @abstractmethod - def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern) -> T: + def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: pass @abstractmethod - def visit_class_pattern(self, o: mypy.patterns.ClassPattern) -> T: + def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: pass @@ -365,264 +365,264 @@ class NodeVisitor(Generic[T], ExpressionVisitor[T], StatementVisitor[T], Pattern # Not in superclasses: - def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> T: + def visit_mypy_file(self, o: mypy.nodes.MypyFile, /) -> T: pass # TODO: We have a visit_var method, but no visit_typeinfo or any # other non-Statement SymbolNode (accepting those will raise a # runtime error). Maybe this should be resolved in some direction. - def visit_var(self, o: mypy.nodes.Var) -> T: + def visit_var(self, o: mypy.nodes.Var, /) -> T: pass # Module structure - def visit_import(self, o: mypy.nodes.Import) -> T: + def visit_import(self, o: mypy.nodes.Import, /) -> T: pass - def visit_import_from(self, o: mypy.nodes.ImportFrom) -> T: + def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: pass - def visit_import_all(self, o: mypy.nodes.ImportAll) -> T: + def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: pass # Definitions - def visit_func_def(self, o: mypy.nodes.FuncDef) -> T: + def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: pass - def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef) -> T: + def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: pass - def visit_class_def(self, o: mypy.nodes.ClassDef) -> T: + def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: pass - def visit_global_decl(self, o: mypy.nodes.GlobalDecl) -> T: + def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: pass - def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> T: + def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: pass - def visit_decorator(self, o: mypy.nodes.Decorator) -> T: + def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: pass - def visit_type_alias(self, o: mypy.nodes.TypeAlias) -> T: + def visit_type_alias(self, o: mypy.nodes.TypeAlias, /) -> T: pass - def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode) -> T: + def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode, /) -> T: pass # Statements - def visit_block(self, o: mypy.nodes.Block) -> T: + def visit_block(self, o: mypy.nodes.Block, /) -> T: pass - def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt) -> T: + def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: pass - def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt) -> T: + def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: pass - def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt) -> T: + def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: pass - def visit_while_stmt(self, o: mypy.nodes.WhileStmt) -> T: + def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: pass - def visit_for_stmt(self, o: mypy.nodes.ForStmt) -> T: + def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: pass - def visit_return_stmt(self, o: mypy.nodes.ReturnStmt) -> T: + def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: pass - def visit_assert_stmt(self, o: mypy.nodes.AssertStmt) -> T: + def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: pass - def visit_del_stmt(self, o: mypy.nodes.DelStmt) -> T: + def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: pass - def visit_if_stmt(self, o: mypy.nodes.IfStmt) -> T: + def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: pass - def visit_break_stmt(self, o: mypy.nodes.BreakStmt) -> T: + def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: pass - def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt) -> T: + def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: pass - def visit_pass_stmt(self, o: mypy.nodes.PassStmt) -> T: + def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: pass - def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt) -> T: + def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: pass - def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> T: + def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: pass - def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> T: + def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: pass - def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: + def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: pass - def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: pass # Expressions (default no-op implementation) - def visit_int_expr(self, o: mypy.nodes.IntExpr) -> T: + def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: pass - def visit_str_expr(self, o: mypy.nodes.StrExpr) -> T: + def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: pass - def visit_bytes_expr(self, o: mypy.nodes.BytesExpr) -> T: + def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: pass - def visit_float_expr(self, o: mypy.nodes.FloatExpr) -> T: + def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: pass - def visit_complex_expr(self, o: mypy.nodes.ComplexExpr) -> T: + def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: pass - def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr) -> T: + def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: pass - def visit_star_expr(self, o: mypy.nodes.StarExpr) -> T: + def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: pass - def visit_name_expr(self, o: mypy.nodes.NameExpr) -> T: + def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: pass - def visit_member_expr(self, o: mypy.nodes.MemberExpr) -> T: + def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: pass - def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr) -> T: + def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: pass - def visit_yield_expr(self, o: mypy.nodes.YieldExpr) -> T: + def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: pass - def visit_call_expr(self, o: mypy.nodes.CallExpr) -> T: + def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: pass - def visit_op_expr(self, o: mypy.nodes.OpExpr) -> T: + def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: pass - def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> T: + def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: pass - def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> T: + def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: pass - def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> T: + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: pass - def visit_reveal_expr(self, o: mypy.nodes.RevealExpr) -> T: + def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: pass - def visit_super_expr(self, o: mypy.nodes.SuperExpr) -> T: + def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: pass - def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr) -> T: + def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: pass - def visit_unary_expr(self, o: mypy.nodes.UnaryExpr) -> T: + def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: pass - def visit_list_expr(self, o: mypy.nodes.ListExpr) -> T: + def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: pass - def visit_dict_expr(self, o: mypy.nodes.DictExpr) -> T: + def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: pass - def visit_tuple_expr(self, o: mypy.nodes.TupleExpr) -> T: + def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: pass - def visit_set_expr(self, o: mypy.nodes.SetExpr) -> T: + def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: pass - def visit_index_expr(self, o: mypy.nodes.IndexExpr) -> T: + def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: pass - def visit_type_application(self, o: mypy.nodes.TypeApplication) -> T: + def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: pass - def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr) -> T: + def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: pass - def visit_list_comprehension(self, o: mypy.nodes.ListComprehension) -> T: + def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: pass - def visit_set_comprehension(self, o: mypy.nodes.SetComprehension) -> T: + def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: pass - def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension) -> T: + def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: pass - def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr) -> T: + def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: pass - def visit_slice_expr(self, o: mypy.nodes.SliceExpr) -> T: + def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: pass - def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr) -> T: + def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: pass - def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> T: + def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: pass - def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> T: + def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: pass - def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> T: + def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: pass - def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> T: + def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: pass - def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> T: + def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: pass - def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> T: + def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: pass - def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> T: + def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: pass - def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> T: + def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: pass - def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> T: + def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: pass - def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> T: + def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: pass - def visit_temp_node(self, o: mypy.nodes.TempNode) -> T: + def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: pass # Patterns - def visit_as_pattern(self, o: mypy.patterns.AsPattern) -> T: + def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: pass - def visit_or_pattern(self, o: mypy.patterns.OrPattern) -> T: + def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: pass - def visit_value_pattern(self, o: mypy.patterns.ValuePattern) -> T: + def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: pass - def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern) -> T: + def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: pass - def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern) -> T: + def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: pass - def visit_starred_pattern(self, o: mypy.patterns.StarredPattern) -> T: + def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: pass - def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern) -> T: + def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: pass - def visit_class_pattern(self, o: mypy.patterns.ClassPattern) -> T: + def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: pass diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 53e3cee74e56..96288423550c 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -109,31 +109,31 @@ class RTypeVisitor(Generic[T]): """Generic visitor over RTypes (uses the visitor design pattern).""" @abstractmethod - def visit_rprimitive(self, typ: RPrimitive) -> T: + def visit_rprimitive(self, typ: RPrimitive, /) -> T: raise NotImplementedError @abstractmethod - def visit_rinstance(self, typ: RInstance) -> T: + def visit_rinstance(self, typ: RInstance, /) -> T: raise NotImplementedError @abstractmethod - def visit_runion(self, typ: RUnion) -> T: + def visit_runion(self, typ: RUnion, /) -> T: raise NotImplementedError @abstractmethod - def visit_rtuple(self, typ: RTuple) -> T: + def visit_rtuple(self, typ: RTuple, /) -> T: raise NotImplementedError @abstractmethod - def visit_rstruct(self, typ: RStruct) -> T: + def visit_rstruct(self, typ: RStruct, /) -> T: raise NotImplementedError @abstractmethod - def visit_rarray(self, typ: RArray) -> T: + def visit_rarray(self, typ: RArray, /) -> T: raise NotImplementedError @abstractmethod - def visit_rvoid(self, typ: RVoid) -> T: + def visit_rvoid(self, typ: RVoid, /) -> T: raise NotImplementedError From 3433a0ec0e611d5164c8a953ada390c9e402bab0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 29 Dec 2024 23:07:32 +0100 Subject: [PATCH 0996/1617] Fix LSP violations in test files (#18362) Extracted from #18356 --- mypy/test/test_find_sources.py | 22 +++++++++++----------- mypy/test/testpep561.py | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mypy/test/test_find_sources.py b/mypy/test/test_find_sources.py index 21ba0903a824..321f3405e999 100644 --- a/mypy/test/test_find_sources.py +++ b/mypy/test/test_find_sources.py @@ -17,20 +17,20 @@ class FakeFSCache(FileSystemCache): def __init__(self, files: set[str]) -> None: self.files = {os.path.abspath(f) for f in files} - def isfile(self, file: str) -> bool: - return file in self.files + def isfile(self, path: str) -> bool: + return path in self.files - def isdir(self, dir: str) -> bool: - if not dir.endswith(os.sep): - dir += os.sep - return any(f.startswith(dir) for f in self.files) + def isdir(self, path: str) -> bool: + if not path.endswith(os.sep): + path += os.sep + return any(f.startswith(path) for f in self.files) - def listdir(self, dir: str) -> list[str]: - if not dir.endswith(os.sep): - dir += os.sep - return list({f[len(dir) :].split(os.sep)[0] for f in self.files if f.startswith(dir)}) + def listdir(self, path: str) -> list[str]: + if not path.endswith(os.sep): + path += os.sep + return list({f[len(path) :].split(os.sep)[0] for f in self.files if f.startswith(path)}) - def init_under_package_root(self, file: str) -> bool: + def init_under_package_root(self, path: str) -> bool: return False diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index a95b9ea2a084..d7344ec898c7 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -23,8 +23,8 @@ class PEP561Suite(DataSuite): files = ["pep561.test"] base_path = "." - def run_case(self, test_case: DataDrivenTestCase) -> None: - test_pep561(test_case) + def run_case(self, testcase: DataDrivenTestCase) -> None: + test_pep561(testcase) @contextmanager From b9056f92847af8168e5fb9352721e6d721bd3900 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 29 Dec 2024 14:08:08 -0800 Subject: [PATCH 0997/1617] Add some highlights to changelog (#18357) Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- CHANGELOG.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bae57dd1b0e7..fb4bbc3afe7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,25 @@ ## Next release -... +### `--strict-bytes` + +By default, mypy treats an annotation of ``bytes`` as permitting ``bytearray`` and ``memoryview``. +[PEP 688](https://peps.python.org/pep-0688) specified the removal of this special case. +Use this flag to disable this behavior. `--strict-bytes` will be enabled by default in **mypy 2.0**. + +Contributed by Ali Hamdan (PR [18137](https://github.com/python/mypy/pull/18263/)) and +Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). + +### Improvements to partial type handling in loops + +This change results in mypy better modelling control flow within loops and hence detecting several +issues it previously did not detect. In some cases, this change may require use of an additional +explicit annotation of a variable. + +Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180)). + +(Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` +by default in **mypy 2.0**). ## Mypy 1.14 From ac6151ae6657a8bbd9793c74691c9985dae02c15 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sun, 29 Dec 2024 23:17:09 +0100 Subject: [PATCH 0998/1617] Reject promotions when checking against protocols. (#18360) Fixes #18359 Fixes #18257 The change is simple but may be way too general. Making it more precise should be straightforward, but I want to see the Mypy Primer results and hear other opinions first. --- mypy/subtypes.py | 2 +- test-data/unit/check-type-promotion.test | 16 ++++++++++++++++ test-data/unit/fixtures/primitives.pyi | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a26aaf798b58..666bdebed742 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -499,7 +499,7 @@ def visit_instance(self, left: Instance) -> bool: return True if type_state.is_cached_negative_subtype_check(self._subtype_kind, left, right): return False - if not self.subtype_context.ignore_promotions: + if not self.subtype_context.ignore_promotions and not right.type.is_protocol: for base in left.type.mro: if base._promote and any( self._is_subtype(p, self.right) for p in base._promote diff --git a/test-data/unit/check-type-promotion.test b/test-data/unit/check-type-promotion.test index d98d0c60e164..1b69174a4545 100644 --- a/test-data/unit/check-type-promotion.test +++ b/test-data/unit/check-type-promotion.test @@ -187,3 +187,19 @@ if isinstance(x, (float, complex)): else: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/primitives.pyi] + +[case testRejectPromotionsForProtocols] +from typing import Protocol + +class H(Protocol): + def hex(self, /) -> str: ... + +f: H = 1.0 +o: H = object() # E: Incompatible types in assignment (expression has type "object", variable has type "H") +c: H = 1j # E: Incompatible types in assignment (expression has type "complex", variable has type "H") +i: H = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "H") +b: H = False # E: Incompatible types in assignment (expression has type "bool", variable has type "H") + +class N(float): ... +n: H = N() +[builtins fixtures/primitives.pyi] diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index e7d3e12bd5e6..fc220a4e2ee0 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -22,6 +22,7 @@ class int: class float: def __float__(self) -> float: pass def __add__(self, x: float) -> float: pass + def hex(self) -> str: pass class complex: def __add__(self, x: complex) -> complex: pass class bool(int): pass From a73e957fb4945d41f015f63cfaaa32b05864f08c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 01:03:15 +0100 Subject: [PATCH 0999/1617] Add some more details for the next release notes (#18364) Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb4bbc3afe7f..a4e5992ca93f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ ## Next release +### Drop Support for Python 3.8 + +Mypy no longer supports running with Python 3.8, which has reached end-of-life. +When running mypy with Python 3.9+, it is still possible to type check code +that needs to support Python 3.8 with the `--python-version 3.8` argument. +Support for this will be dropped in the first half of 2025! +Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). + +### Mypyc accelerated mypy wheels for aarch64 + +Mypy can compile itself to C extension modules using mypyc. This makes mypy 3-5x faster +than if mypy is interpreted with pure Python. We now build and upload mypyc accelerated +mypy wheels for `manylinux_aarch64` to PyPI, making it easy for users on such platforms +to realise this speedup. + +Contributed by Christian Bundy (PR [mypy_mypyc-wheels#76](https://github.com/mypyc/mypy_mypyc-wheels/pull/76)) + ### `--strict-bytes` By default, mypy treats an annotation of ``bytes`` as permitting ``bytearray`` and ``memoryview``. From 4a0b331e8ff393b18a3595afeee4bdac52c2dc6e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 01:18:18 +0100 Subject: [PATCH 1000/1617] Fix markdown formatting (#18368) Followup to #18364. The `Contributed by` should be placed in a separate paragraph. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e5992ca93f..81da1cd05a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Mypy no longer supports running with Python 3.8, which has reached end-of-life. When running mypy with Python 3.9+, it is still possible to type check code that needs to support Python 3.8 with the `--python-version 3.8` argument. Support for this will be dropped in the first half of 2025! + Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). ### Mypyc accelerated mypy wheels for aarch64 From c821503ff1926b45aae5b723ffce36b4b6dbc7d9 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 29 Dec 2024 16:23:46 -0800 Subject: [PATCH 1001/1617] More LSP compatibility on arg names (#18363) Got lost when #18356 was broken up --- mypy/plugin.py | 15 ++++++++------- mypy/semanal_shared.py | 15 ++++++++------- mypy/typeanal.py | 16 ++++++---------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/mypy/plugin.py b/mypy/plugin.py index fcbbc32f6237..39841d5b907a 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -170,12 +170,12 @@ def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None raise NotImplementedError @abstractmethod - def named_type(self, name: str, args: list[Type]) -> Instance: + def named_type(self, fullname: str, args: list[Type], /) -> Instance: """Construct an instance of a builtin type with given name.""" raise NotImplementedError @abstractmethod - def analyze_type(self, typ: Type) -> Type: + def analyze_type(self, typ: Type, /) -> Type: """Analyze an unbound type using the default mypy logic.""" raise NotImplementedError @@ -319,7 +319,8 @@ def fail( @abstractmethod def anal_type( self, - t: Type, + typ: Type, + /, *, tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, @@ -340,7 +341,7 @@ def class_type(self, self_type: Type) -> Type: raise NotImplementedError @abstractmethod - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: """Lookup a symbol by its fully qualified name. Raise an error if not found. @@ -348,7 +349,7 @@ def lookup_fully_qualified(self, name: str) -> SymbolTableNode: raise NotImplementedError @abstractmethod - def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None: + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: """Lookup a symbol by its fully qualified name. Return None if not found. @@ -384,12 +385,12 @@ def add_plugin_dependency(self, trigger: str, target: str | None = None) -> None raise NotImplementedError @abstractmethod - def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> Any: + def add_symbol_table_node(self, name: str, symbol: SymbolTableNode) -> Any: """Add node to global symbol table (or to nearest class if there is one).""" raise NotImplementedError @abstractmethod - def qualified_name(self, n: str) -> str: + def qualified_name(self, name: str) -> str: """Make qualified name using current module and enclosing class (if any).""" raise NotImplementedError diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 941a16a7fd5d..b7d50e411016 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -76,11 +76,11 @@ def lookup_qualified( raise NotImplementedError @abstractmethod - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: raise NotImplementedError @abstractmethod - def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None: + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: raise NotImplementedError @abstractmethod @@ -176,7 +176,8 @@ def accept(self, node: Node) -> None: @abstractmethod def anal_type( self, - t: Type, + typ: Type, + /, *, tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, @@ -198,11 +199,11 @@ def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: in raise NotImplementedError @abstractmethod - def schedule_patch(self, priority: int, fn: Callable[[], None]) -> None: + def schedule_patch(self, priority: int, patch: Callable[[], None]) -> None: raise NotImplementedError @abstractmethod - def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> bool: + def add_symbol_table_node(self, name: str, symbol: SymbolTableNode) -> bool: """Add node to the current symbol table.""" raise NotImplementedError @@ -242,7 +243,7 @@ def parse_bool(self, expr: Expression) -> bool | None: raise NotImplementedError @abstractmethod - def qualified_name(self, n: str) -> str: + def qualified_name(self, name: str) -> str: raise NotImplementedError @property @@ -309,7 +310,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None: class _NamedTypeCallback(Protocol): - def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> Instance: ... + def __call__(self, fullname: str, args: list[Type] | None = None) -> Instance: ... def paramspec_args( diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 751ed85ea6f3..d0dd8542fd91 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -288,8 +288,8 @@ def lookup_qualified( ) -> SymbolTableNode | None: return self.api.lookup_qualified(name, ctx, suppress_errors) - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: - return self.api.lookup_fully_qualified(name) + def lookup_fully_qualified(self, fullname: str) -> SymbolTableNode: + return self.api.lookup_fully_qualified(fullname) def visit_unbound_type(self, t: UnboundType, defining_literal: bool = False) -> Type: typ = self.visit_unbound_type_nonoptional(t, defining_literal) @@ -1762,8 +1762,8 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] self.fail(f"Parameter {idx} of Literal[...] is invalid", ctx, code=codes.VALID_TYPE) return None - def analyze_type(self, t: Type) -> Type: - return t.accept(self) + def analyze_type(self, typ: Type) -> Type: + return typ.accept(self) def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None: self.fail_func(msg, ctx, code=code) @@ -1937,13 +1937,9 @@ def anal_var_defs(self, var_defs: Sequence[TypeVarLikeType]) -> list[TypeVarLike return [self.anal_var_def(vd) for vd in var_defs] def named_type( - self, - fully_qualified_name: str, - args: list[Type] | None = None, - line: int = -1, - column: int = -1, + self, fullname: str, args: list[Type] | None = None, line: int = -1, column: int = -1 ) -> Instance: - node = self.lookup_fully_qualified(fully_qualified_name) + node = self.lookup_fully_qualified(fullname) assert isinstance(node.node, TypeInfo) any_type = AnyType(TypeOfAny.special_form) if args is not None: From 7b619454636fb08a09441654bb7972c7736d6609 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 29 Dec 2024 16:57:23 -0800 Subject: [PATCH 1002/1617] Fix --install-types masking failure details (#17485) It seems that: if the mypy cache dir wasn't created, this code would do an exit, preventing the actual errors from being printed. So I've removed the exit. More information is available at the issue I claim this fixes. Fixes #10768 --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 9873907ddf03..dd9a9c42c568 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1575,8 +1575,9 @@ def read_types_packages_to_install(cache_dir: str, after_run: bool) -> list[str] + "(and no cache from previous mypy run)\n" ) else: - sys.stderr.write("error: --install-types failed (no mypy cache directory)\n") - sys.exit(2) + sys.stderr.write( + "error: --install-types failed (an error blocked analysis of which types to install)\n" + ) fnam = build.missing_stubs_file(cache_dir) if not os.path.isfile(fnam): # No missing stubs. From f2a77eaaca098bdba051e998cb46dbc71dddfdb0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:05:36 -0800 Subject: [PATCH 1003/1617] Update to include latest stubs in typeshed (#18366) Fixes #18365 See https://github.com/python/mypy/pull/18367 for script --- mypy/stubinfo.py | 149 +++++++++++++++++++++++++-- test-data/unit/check-errorcodes.test | 5 +- 2 files changed, 145 insertions(+), 9 deletions(-) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 8d89a2a4bede..56d66e00f0bf 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -85,27 +85,45 @@ def stub_distribution_name(module: str) -> str | None: # types-pika already exists on PyPI, and is more complete in many ways, # but is a non-typeshed stubs package. non_bundled_packages_flat: dict[str, str] = { - "MySQLdb": "types-mysqlclient", - "PIL": "types-Pillow", - "PyInstaller": "types-pyinstaller", - "Xlib": "types-python-xlib", + "_cffi_backend": "types-cffi", + "_win32typing": "types-pywin32", + "antlr4": "types-antlr4-python3-runtime", + "assertpy": "types-assertpy", + "atheris": "types-atheris", + "authlib": "types-Authlib", "aws_xray_sdk": "types-aws-xray-sdk", "babel": "types-babel", + "boltons": "types-boltons", "braintree": "types-braintree", "bs4": "types-beautifulsoup4", "bugbear": "types-flake8-bugbear", "caldav": "types-caldav", + "capturer": "types-capturer", "cffi": "types-cffi", "chevron": "types-chevron", + "click_default_group": "types-click-default-group", + "click_log": "types-click-log", + "click_web": "types-click-web", "colorama": "types-colorama", + "commctrl": "types-pywin32", "commonmark": "types-commonmark", "consolemenu": "types-console-menu", + "corus": "types-corus", + "cronlog": "types-python-crontab", "crontab": "types-python-crontab", + "crontabs": "types-python-crontab", "d3dshot": "types-D3DShot", + "datemath": "types-python-datemath", + "dateparser_data": "types-dateparser", + "dde": "types-pywin32", + "defusedxml": "types-defusedxml", + "docker": "types-docker", "dockerfile_parse": "types-dockerfile-parse", "docopt": "types-docopt", "editdistance": "types-editdistance", "entrypoints": "types-entrypoints", + "exifread": "types-ExifRead", + "fanstatic": "types-fanstatic", "farmhash": "types-pyfarmhash", "flake8_2020": "types-flake8-2020", "flake8_builtins": "types-flake8-builtins", @@ -114,23 +132,54 @@ def stub_distribution_name(module: str) -> str | None: "flake8_rst_docstrings": "types-flake8-rst-docstrings", "flake8_simplify": "types-flake8-simplify", "flake8_typing_imports": "types-flake8-typing-imports", + "flake8": "types-flake8", "flask_cors": "types-Flask-Cors", "flask_migrate": "types-Flask-Migrate", + "flask_socketio": "types-Flask-SocketIO", "fpdf": "types-fpdf2", "gdb": "types-gdb", + "gevent": "types-gevent", + "greenlet": "types-greenlet", "hdbcli": "types-hdbcli", "html5lib": "types-html5lib", "httplib2": "types-httplib2", "humanfriendly": "types-humanfriendly", + "hvac": "types-hvac", + "ibm_db": "types-ibm-db", + "icalendar": "types-icalendar", + "import_export": "types-django-import-export", + "influxdb_client": "types-influxdb-client", + "inifile": "types-inifile", "invoke": "types-invoke", + "isapi": "types-pywin32", "jack": "types-JACK-Client", + "jenkins": "types-python-jenkins", + "Jetson": "types-Jetson.GPIO", + "jks": "types-pyjks", "jmespath": "types-jmespath", "jose": "types-python-jose", "jsonschema": "types-jsonschema", + "jwcrypto": "types-jwcrypto", "keyboard": "types-keyboard", "ldap3": "types-ldap3", + "lupa": "types-lupa", + "lzstring": "types-lzstring", + "m3u8": "types-m3u8", + "mmapfile": "types-pywin32", + "mmsystem": "types-pywin32", + "mypy_extensions": "types-mypy-extensions", + "MySQLdb": "types-mysqlclient", + "nanoid": "types-nanoid", + "nanoleafapi": "types-nanoleafapi", + "netaddr": "types-netaddr", + "netifaces": "types-netifaces", + "networkx": "types-networkx", "nmap": "types-python-nmap", + "ntsecuritycon": "types-pywin32", "oauthlib": "types-oauthlib", + "objgraph": "types-objgraph", + "odbc": "types-pywin32", + "olefile": "types-olefile", "openpyxl": "types-openpyxl", "opentracing": "types-opentracing", "parsimonious": "types-parsimonious", @@ -138,41 +187,125 @@ def stub_distribution_name(module: str) -> str | None: "passpy": "types-passpy", "peewee": "types-peewee", "pep8ext_naming": "types-pep8-naming", + "perfmon": "types-pywin32", + "pexpect": "types-pexpect", + "PIL": "types-Pillow", + "playhouse": "types-peewee", "playsound": "types-playsound", + "portpicker": "types-portpicker", "psutil": "types-psutil", "psycopg2": "types-psycopg2", + "pyasn1": "types-pyasn1", "pyaudio": "types-pyaudio", "pyautogui": "types-PyAutoGUI", "pycocotools": "types-pycocotools", "pyflakes": "types-pyflakes", + "pygit2": "types-pygit2", "pygments": "types-Pygments", "pyi_splash": "types-pyinstaller", + "PyInstaller": "types-pyinstaller", "pynput": "types-pynput", - "pythoncom": "types-pywin32", - "pythonwin": "types-pywin32", "pyscreeze": "types-PyScreeze", "pysftp": "types-pysftp", "pytest_lazyfixture": "types-pytest-lazy-fixture", + "python_http_client": "types-python-http-client", + "pythoncom": "types-pywin32", + "pythonwin": "types-pywin32", "pywintypes": "types-pywin32", + "qrbill": "types-qrbill", + "qrcode": "types-qrcode", "regex": "types-regex", + "regutil": "types-pywin32", + "reportlab": "types-reportlab", + "requests_oauthlib": "types-requests-oauthlib", + "RPi": "types-RPi.GPIO", + "s2clientprotocol": "types-s2clientprotocol", + "sass": "types-libsass", + "sassutils": "types-libsass", + "seaborn": "types-seaborn", "send2trash": "types-Send2Trash", + "serial": "types-pyserial", + "servicemanager": "types-pywin32", + "setuptools": "types-setuptools", + "shapely": "types-shapely", "slumber": "types-slumber", + "sspicon": "types-pywin32", "stdlib_list": "types-stdlib-list", + "str2bool": "types-str2bool", "stripe": "types-stripe", + "tensorflow": "types-tensorflow", + "tgcrypto": "types-TgCrypto", + "timer": "types-pywin32", "toposort": "types-toposort", "tqdm": "types-tqdm", - "tree_sitter": "types-tree-sitter", + "translationstring": "types-translationstring", "tree_sitter_languages": "types-tree-sitter-languages", + "tree_sitter": "types-tree-sitter", "ttkthemes": "types-ttkthemes", + "unidiff": "types-unidiff", + "untangle": "types-untangle", + "usersettings": "types-usersettings", + "uwsgi": "types-uWSGI", + "uwsgidecorators": "types-uWSGI", "vobject": "types-vobject", + "webob": "types-WebOb", "whatthepatch": "types-whatthepatch", + "win2kras": "types-pywin32", "win32": "types-pywin32", "win32api": "types-pywin32", - "win32con": "types-pywin32", + "win32clipboard": "types-pywin32", "win32com": "types-pywin32", "win32comext": "types-pywin32", + "win32con": "types-pywin32", + "win32console": "types-pywin32", + "win32cred": "types-pywin32", + "win32crypt": "types-pywin32", + "win32cryptcon": "types-pywin32", + "win32event": "types-pywin32", + "win32evtlog": "types-pywin32", + "win32evtlogutil": "types-pywin32", + "win32file": "types-pywin32", + "win32gui_struct": "types-pywin32", "win32gui": "types-pywin32", + "win32help": "types-pywin32", + "win32inet": "types-pywin32", + "win32inetcon": "types-pywin32", + "win32job": "types-pywin32", + "win32lz": "types-pywin32", + "win32net": "types-pywin32", + "win32netcon": "types-pywin32", + "win32pdh": "types-pywin32", + "win32pdhquery": "types-pywin32", + "win32pipe": "types-pywin32", + "win32print": "types-pywin32", + "win32process": "types-pywin32", + "win32profile": "types-pywin32", + "win32ras": "types-pywin32", + "win32security": "types-pywin32", + "win32service": "types-pywin32", + "win32serviceutil": "types-pywin32", + "win32timezone": "types-pywin32", + "win32trace": "types-pywin32", + "win32transaction": "types-pywin32", + "win32ts": "types-pywin32", + "win32ui": "types-pywin32", + "win32uiole": "types-pywin32", + "win32verstamp": "types-pywin32", + "win32wnet": "types-pywin32", + "winerror": "types-pywin32", + "winioctlcon": "types-pywin32", + "winnt": "types-pywin32", + "winperf": "types-pywin32", + "winxpgui": "types-pywin32", + "winxptheme": "types-pywin32", + "workalendar": "types-workalendar", + "wtforms": "types-WTForms", + "wurlitzer": "types-wurlitzer", + "xdg": "types-pyxdg", + "xdgenvpy": "types-xdgenvpy", + "Xlib": "types-python-xlib", "xmltodict": "types-xmltodict", + "zstd": "types-zstd", "zxcvbn": "types-zxcvbn", # Stub packages that are not from typeshed # Since these can be installed automatically via --install-types, we have a high trust bar diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index cc0227bc6664..a5a22cb6cabd 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -522,13 +522,16 @@ if int() is str(): # E: Non-overlapping identity check (left operand type: "int [builtins fixtures/primitives.pyi] [case testErrorCodeMissingModule] -from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import-not-found] +from defusedxml import xyz # E: Library stubs not installed for "defusedxml" [import-untyped] \ + # N: Hint: "python3 -m pip install types-defusedxml" \ + # N: (or run "mypy --install-types" to install all missing stub packages) from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import-not-found] import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import-not-found] from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import-not-found] from pkg import bad # E: Module "pkg" has no attribute "bad" [attr-defined] from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import-not-found] \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports + [file pkg/__init__.py] [case testErrorCodeAlreadyDefined] From 7982761e6eea7f6ecb7e7e8d4753b90799310852 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 06:33:26 +0100 Subject: [PATCH 1004/1617] Replace deprecated typing imports with collections.abc (#18336) --- misc/analyze_cache.py | 3 ++- misc/upload-pypi.py | 3 ++- mypy/applytype.py | 3 ++- mypy/argmap.py | 3 ++- mypy/binder.py | 3 ++- mypy/build.py | 4 +--- mypy/checker.py | 5 +---- mypy/checkexpr.py | 3 ++- mypy/checkmember.py | 3 ++- mypy/checkstrformat.py | 3 ++- mypy/config_parser.py | 16 ++-------------- mypy/constraints.py | 3 ++- mypy/dmypy/client.py | 3 ++- mypy/dmypy_server.py | 3 ++- mypy/dmypy_util.py | 3 ++- mypy/erasetype.py | 3 ++- mypy/errors.py | 3 ++- mypy/expandtype.py | 3 ++- mypy/fastparse.py | 3 ++- mypy/find_sources.py | 3 ++- mypy/fswatcher.py | 3 ++- mypy/gclogger.py | 2 +- mypy/graph_utils.py | 3 ++- mypy/indirection.py | 2 +- mypy/infer.py | 3 ++- mypy/join.py | 3 ++- mypy/literals.py | 3 ++- mypy/main.py | 3 ++- mypy/memprofile.py | 3 ++- mypy/messages.py | 3 ++- mypy/metastore.py | 3 ++- mypy/nodes.py | 3 +-- mypy/options.py | 4 +++- mypy/plugins/attrs.py | 3 ++- mypy/plugins/dataclasses.py | 3 ++- mypy/plugins/enums.py | 3 ++- mypy/plugins/singledispatch.py | 3 ++- mypy/renaming.py | 3 ++- mypy/report.py | 3 ++- mypy/scope.py | 3 ++- mypy/semanal.py | 3 ++- mypy/semanal_namedtuple.py | 3 ++- mypy/server/astdiff.py | 3 ++- mypy/server/aststrip.py | 3 ++- mypy/server/objgraph.py | 4 ++-- mypy/server/update.py | 3 ++- mypy/solve.py | 2 +- mypy/state.py | 3 ++- mypy/stats.py | 3 ++- mypy/strconv.py | 3 ++- mypy/stubdoc.py | 3 ++- mypy/stubgen.py | 3 ++- mypy/stubgenc.py | 3 ++- mypy/stubtest.py | 3 ++- mypy/stubutil.py | 3 ++- mypy/subtypes.py | 3 ++- mypy/suggestions.py | 3 ++- mypy/test/data.py | 4 +++- mypy/test/helpers.py | 4 +++- mypy/test/meta/_pytest.py | 2 +- mypy/test/testpep561.py | 2 +- mypy/test/teststubtest.py | 3 ++- mypy/test/update_data.py | 2 +- mypy/treetransform.py | 3 ++- mypy/type_visitor.py | 3 ++- mypy/typeanal.py | 3 ++- mypy/typeops.py | 3 ++- mypy/types.py | 3 +-- mypy/types_utils.py | 3 ++- mypy/typetraverser.py | 2 +- mypy/typevartuples.py | 2 +- mypy/util.py | 3 ++- mypyc/analysis/dataflow.py | 3 ++- mypyc/build.py | 3 ++- mypyc/codegen/emitclass.py | 3 ++- mypyc/codegen/emitmodule.py | 3 ++- mypyc/codegen/emitwrapper.py | 2 +- mypyc/crash.py | 3 ++- mypyc/ir/func_ir.py | 3 ++- mypyc/ir/ops.py | 3 ++- mypyc/ir/pprint.py | 3 ++- mypyc/irbuild/builder.py | 3 ++- mypyc/irbuild/expression.py | 3 ++- mypyc/irbuild/function.py | 3 ++- mypyc/irbuild/ll_builder.py | 3 ++- mypyc/irbuild/match.py | 3 ++- mypyc/irbuild/prepare.py | 3 ++- mypyc/irbuild/statement.py | 3 ++- mypyc/namegen.py | 2 +- mypyc/test/test_run.py | 3 ++- mypyc/test/testutil.py | 3 ++- mypyc/transform/refcount.py | 3 ++- 92 files changed, 173 insertions(+), 113 deletions(-) diff --git a/misc/analyze_cache.py b/misc/analyze_cache.py index 33205f5132fc..62a5112b0ccd 100644 --- a/misc/analyze_cache.py +++ b/misc/analyze_cache.py @@ -6,7 +6,8 @@ import os import os.path from collections import Counter -from typing import Any, Dict, Final, Iterable +from collections.abc import Iterable +from typing import Any, Dict, Final from typing_extensions import TypeAlias as _TypeAlias ROOT: Final = ".mypy_cache/3.5" diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py index 9d8827c5e46c..90ae80da643f 100644 --- a/misc/upload-pypi.py +++ b/misc/upload-pypi.py @@ -16,9 +16,10 @@ import tarfile import tempfile import venv +from collections.abc import Iterator from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from typing import Any, Iterator +from typing import Any from urllib.request import urlopen BASE = "https://api.github.com/repos" diff --git a/mypy/applytype.py b/mypy/applytype.py index e88947cc6430..e87bf939c81a 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Callable, Iterable, Sequence +from collections.abc import Iterable, Sequence +from typing import Callable import mypy.subtypes from mypy.erasetype import erase_typevars diff --git a/mypy/argmap.py b/mypy/argmap.py index e6700c9f1092..c863844f90ad 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Callable from mypy import nodes from mypy.maptype import map_instance_to_supertype diff --git a/mypy/binder.py b/mypy/binder.py index 52ae9774e6d4..34447550e3bb 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -1,8 +1,9 @@ from __future__ import annotations from collections import defaultdict +from collections.abc import Iterator from contextlib import contextmanager -from typing import DefaultDict, Iterator, List, NamedTuple, Optional, Tuple, Union +from typing import DefaultDict, List, NamedTuple, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values diff --git a/mypy/build.py b/mypy/build.py index 88c334309900..108ba320054c 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -25,6 +25,7 @@ import sys import time import types +from collections.abc import Iterator, Mapping, Sequence from typing import ( TYPE_CHECKING, AbstractSet, @@ -33,11 +34,8 @@ ClassVar, Dict, Final, - Iterator, - Mapping, NamedTuple, NoReturn, - Sequence, TextIO, ) from typing_extensions import TypeAlias as _TypeAlias, TypedDict diff --git a/mypy/checker.py b/mypy/checker.py index b2c4f2263262..440b161618ee 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4,6 +4,7 @@ import itertools from collections import defaultdict +from collections.abc import Iterable, Iterator, Mapping, Sequence from contextlib import ExitStack, contextmanager from typing import ( AbstractSet, @@ -11,12 +12,8 @@ Dict, Final, Generic, - Iterable, - Iterator, - Mapping, NamedTuple, Optional, - Sequence, Tuple, TypeVar, Union, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 964149fa8df4..7000cfba25c1 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6,8 +6,9 @@ import itertools import time from collections import defaultdict +from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, ClassVar, Final, Iterable, Iterator, List, Optional, Sequence, cast +from typing import Callable, ClassVar, Final, List, Optional, cast from typing_extensions import TypeAlias as _TypeAlias, assert_never, overload import mypy.checker diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 50e54ca30460..19ebe07b1032 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence, cast +from collections.abc import Sequence +from typing import TYPE_CHECKING, Callable, cast from mypy import meet, message_registry, subtypes from mypy.erasetype import erase_typevars diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index dd42fe7755a0..3d92897246fe 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -13,7 +13,8 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Callable, Dict, Final, Match, Pattern, Tuple, Union, cast +from re import Match, Pattern +from typing import TYPE_CHECKING, Callable, Dict, Final, Tuple, Union, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.errorcodes as codes diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 9fa99333a42a..e54afd4a07f3 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -15,20 +15,8 @@ else: import tomli as tomllib -from typing import ( - Any, - Callable, - Dict, - Final, - Iterable, - List, - Mapping, - MutableMapping, - Sequence, - TextIO, - Tuple, - Union, -) +from collections.abc import Iterable, Mapping, MutableMapping, Sequence +from typing import Any, Callable, Dict, Final, List, TextIO, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import defaults diff --git a/mypy/constraints.py b/mypy/constraints.py index 5c815bf2af65..410d33cae50c 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterable, List, Sequence +from collections.abc import Iterable, Sequence +from typing import TYPE_CHECKING, Final, List import mypy.subtypes import mypy.typeops diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index a534a78542da..8ca4f1bd7ea2 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -14,7 +14,8 @@ import sys import time import traceback -from typing import Any, Callable, Mapping, NoReturn +from collections.abc import Mapping +from typing import Any, Callable, NoReturn from mypy.dmypy_os import alive, kill from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 10ff07451461..43b8c5eb05be 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -16,8 +16,9 @@ import sys import time import traceback +from collections.abc import Sequence from contextlib import redirect_stderr, redirect_stdout -from typing import AbstractSet, Any, Callable, Final, List, Sequence, Tuple +from typing import AbstractSet, Any, Callable, Final, List, Tuple from typing_extensions import TypeAlias as _TypeAlias import mypy.build diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 9b21d78ce599..eeb918b7877e 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -7,8 +7,9 @@ import io import json +from collections.abc import Iterable, Iterator from types import TracebackType -from typing import Any, Final, Iterable, Iterator, TextIO +from typing import Any, Final, TextIO from mypy.ipc import IPCBase diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 222e7f2a6d7a..0e6a8bf8a829 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Callable, Container, cast +from collections.abc import Container +from typing import Callable, cast from mypy.nodes import ARG_STAR, ARG_STAR2 from mypy.types import ( diff --git a/mypy/errors.py b/mypy/errors.py index 0395a3a0d821..c7af95461af1 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -4,7 +4,8 @@ import sys import traceback from collections import defaultdict -from typing import Callable, Final, Iterable, NoReturn, Optional, TextIO, Tuple, TypeVar +from collections.abc import Iterable +from typing import Callable, Final, NoReturn, Optional, TextIO, Tuple, TypeVar from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes diff --git a/mypy/expandtype.py b/mypy/expandtype.py index b2040ec074c3..8750da34d963 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload +from collections.abc import Iterable, Mapping, Sequence +from typing import Final, TypeVar, cast, overload from mypy.nodes import ARG_STAR, FakeInfo, Var from mypy.state import state diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 39782035d6ac..07e3dcd26ee0 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -3,7 +3,8 @@ import re import sys import warnings -from typing import Any, Callable, Final, List, Optional, Sequence, TypeVar, Union, cast +from collections.abc import Sequence +from typing import Any, Callable, Final, List, Optional, TypeVar, Union, cast from typing_extensions import Literal, overload from mypy import defaults, errorcodes as codes, message_registry diff --git a/mypy/find_sources.py b/mypy/find_sources.py index 3565fc4609cd..783642960fb3 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -4,7 +4,8 @@ import functools import os -from typing import Final, Sequence +from collections.abc import Sequence +from typing import Final from mypy.fscache import FileSystemCache from mypy.modulefinder import PYTHON_EXTENSIONS, BuildSource, matches_exclude, mypy_path diff --git a/mypy/fswatcher.py b/mypy/fswatcher.py index 97a62ca9f9f7..a51b1fa95337 100644 --- a/mypy/fswatcher.py +++ b/mypy/fswatcher.py @@ -3,7 +3,8 @@ from __future__ import annotations import os -from typing import AbstractSet, Iterable, NamedTuple +from collections.abc import Iterable +from typing import AbstractSet, NamedTuple from mypy.fscache import FileSystemCache diff --git a/mypy/gclogger.py b/mypy/gclogger.py index d111e609223c..bc908bdb6107 100644 --- a/mypy/gclogger.py +++ b/mypy/gclogger.py @@ -2,7 +2,7 @@ import gc import time -from typing import Mapping +from collections.abc import Mapping class GcLogger: diff --git a/mypy/graph_utils.py b/mypy/graph_utils.py index 5c0d25e425eb..9083ed6a12f7 100644 --- a/mypy/graph_utils.py +++ b/mypy/graph_utils.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import AbstractSet, Iterable, Iterator, TypeVar +from collections.abc import Iterable, Iterator +from typing import AbstractSet, TypeVar T = TypeVar("T") diff --git a/mypy/indirection.py b/mypy/indirection.py index 1be33e45ecba..4f455d2c1dc9 100644 --- a/mypy/indirection.py +++ b/mypy/indirection.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable import mypy.types as types from mypy.types import TypeVisitor diff --git a/mypy/infer.py b/mypy/infer.py index bcf0c95808ab..cdc43797d3b1 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import NamedTuple, Sequence +from collections.abc import Sequence +from typing import NamedTuple from mypy.constraints import ( SUBTYPE_OF, diff --git a/mypy/join.py b/mypy/join.py index 2ada7479789b..166434f58f8d 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Sequence, overload +from collections.abc import Sequence +from typing import overload import mypy.typeops from mypy.expandtype import expand_type diff --git a/mypy/literals.py b/mypy/literals.py index cba5712644be..a4527a47f3a6 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Final, Iterable, Optional, Tuple +from collections.abc import Iterable +from typing import Any, Final, Optional, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( diff --git a/mypy/main.py b/mypy/main.py index dd9a9c42c568..211d6952c2ac 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -8,9 +8,10 @@ import sys import time from collections import defaultdict +from collections.abc import Sequence from gettext import gettext from io import TextIOWrapper -from typing import IO, Any, Final, NoReturn, Protocol, Sequence, TextIO +from typing import IO, Any, Final, NoReturn, Protocol, TextIO from mypy import build, defaults, state, util from mypy.config_parser import ( diff --git a/mypy/memprofile.py b/mypy/memprofile.py index 48c0cb5ce022..e47d0deb1ab3 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -9,7 +9,8 @@ import gc import sys from collections import defaultdict -from typing import Dict, Iterable, cast +from collections.abc import Iterable +from typing import Dict, cast from mypy.nodes import FakeInfo, Node from mypy.types import Type diff --git a/mypy/messages.py b/mypy/messages.py index 40b0e7ee695a..fe4552112f16 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -14,9 +14,10 @@ import difflib import itertools import re +from collections.abc import Collection, Iterable, Iterator, Sequence from contextlib import contextmanager from textwrap import dedent -from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, Sequence, cast +from typing import Any, Callable, Final, List, cast import mypy.typeops from mypy import errorcodes as codes, message_registry diff --git a/mypy/metastore.py b/mypy/metastore.py index ece397360e5b..442c7dc77461 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -14,7 +14,8 @@ import os import time from abc import abstractmethod -from typing import TYPE_CHECKING, Any, Iterable +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: # We avoid importing sqlite3 unless we are using it so we can mostly work diff --git a/mypy/nodes.py b/mypy/nodes.py index 5f28bde2ceab..e287fdb652d6 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -5,6 +5,7 @@ import os from abc import abstractmethod from collections import defaultdict +from collections.abc import Iterator, Sequence from enum import Enum, unique from typing import ( TYPE_CHECKING, @@ -12,10 +13,8 @@ Callable, Dict, Final, - Iterator, List, Optional, - Sequence, Tuple, TypeVar, Union, diff --git a/mypy/options.py b/mypy/options.py index eb3d389b5d8a..4e5273774f26 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -4,7 +4,9 @@ import re import sys import sysconfig -from typing import Any, Callable, Final, Mapping, Pattern +from collections.abc import Mapping +from re import Pattern +from typing import Any, Callable, Final from mypy import defaults from mypy.errorcodes import ErrorCode, error_codes diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index b67a285af11d..15d442db0e58 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -3,8 +3,9 @@ from __future__ import annotations from collections import defaultdict +from collections.abc import Iterable, Mapping from functools import reduce -from typing import Final, Iterable, List, Mapping, cast +from typing import Final, List, cast from typing_extensions import Literal import mypy.plugin # To avoid circular imports. diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 349eca7f0143..538f689f5e07 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterator, Literal +from collections.abc import Iterator +from typing import TYPE_CHECKING, Final, Literal from mypy import errorcodes, message_registry from mypy.expandtype import expand_type, expand_type_by_instance diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 86e7f1f7b31e..8b7c5df6f51f 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -13,7 +13,8 @@ from __future__ import annotations -from typing import Final, Iterable, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import Final, TypeVar, cast import mypy.plugin # To avoid circular imports. from mypy.nodes import TypeInfo diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index c5ce20233a0a..be4b405ce610 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Final, NamedTuple, Sequence, TypeVar, Union +from collections.abc import Sequence +from typing import Final, NamedTuple, TypeVar, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.messages import format_type diff --git a/mypy/renaming.py b/mypy/renaming.py index 8db336205960..7cc96566235a 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final from mypy.nodes import ( AssignmentStmt, diff --git a/mypy/report.py b/mypy/report.py index 73942b6c5ae3..1beb375299bd 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -11,8 +11,9 @@ import time import tokenize from abc import ABCMeta, abstractmethod +from collections.abc import Iterator from operator import attrgetter -from typing import Any, Callable, Dict, Final, Iterator, Tuple +from typing import Any, Callable, Dict, Final, Tuple from typing_extensions import TypeAlias as _TypeAlias from urllib.request import pathname2url diff --git a/mypy/scope.py b/mypy/scope.py index 021dd9a7d8a5..c13c45573557 100644 --- a/mypy/scope.py +++ b/mypy/scope.py @@ -5,8 +5,9 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Iterator, Optional, Tuple +from typing import Optional, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import FuncBase, TypeInfo diff --git a/mypy/semanal.py b/mypy/semanal.py index 6e3335aed4e1..4e1769a29866 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -50,8 +50,9 @@ from __future__ import annotations +from collections.abc import Collection, Iterable, Iterator from contextlib import contextmanager -from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, TypeVar, cast +from typing import Any, Callable, Final, List, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias, TypeGuard from mypy import errorcodes as codes, message_registry diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index dfc99576e617..52665b0fa121 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -6,8 +6,9 @@ from __future__ import annotations import keyword +from collections.abc import Container, Iterator, Mapping from contextlib import contextmanager -from typing import Container, Final, Iterator, List, Mapping, cast +from typing import Final, List, cast from mypy.errorcodes import ARG_TYPE, ErrorCode from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 85f77a269e43..a2711f9e0a8f 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -52,7 +52,8 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' from __future__ import annotations -from typing import Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.expandtype import expand_type diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py index 05af6a3d53a1..410b3ecfa976 100644 --- a/mypy/server/aststrip.py +++ b/mypy/server/aststrip.py @@ -33,8 +33,9 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Dict, Iterator, Tuple +from typing import Dict, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index a13fd8412934..e5096d5befa3 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -4,8 +4,8 @@ import types import weakref -from collections.abc import Iterable -from typing import Final, Iterator, Mapping +from collections.abc import Iterable, Iterator, Mapping +from typing import Final method_descriptor_type: Final = type(object.__dir__) method_wrapper_type: Final = type(object().__ne__) diff --git a/mypy/server/update.py b/mypy/server/update.py index fdc311bbfa6b..9891e2417b94 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -118,7 +118,8 @@ import re import sys import time -from typing import Callable, Final, NamedTuple, Sequence, Union +from collections.abc import Sequence +from typing import Callable, Final, NamedTuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.build import ( diff --git a/mypy/solve.py b/mypy/solve.py index 4b09baee64c6..cac1a23c5a33 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, Sequence +from collections.abc import Iterable, Sequence from typing_extensions import TypeAlias as _TypeAlias from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op diff --git a/mypy/state.py b/mypy/state.py index 533dceeb1f24..a3055bf6b208 100644 --- a/mypy/state.py +++ b/mypy/state.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final # These are global mutable state. Don't add anything here unless there's a very # good reason. diff --git a/mypy/stats.py b/mypy/stats.py index 9c69a245741b..6bad400ce5d5 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -4,8 +4,9 @@ import os from collections import Counter +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final from mypy import nodes from mypy.argmap import map_formals_to_actuals diff --git a/mypy/strconv.py b/mypy/strconv.py index 2d595d4b67b0..3e9d37586f72 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -4,7 +4,8 @@ import os import re -from typing import TYPE_CHECKING, Any, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any import mypy.nodes from mypy.options import Options diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 434de0ea3bcb..79365bec33bd 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -11,7 +11,8 @@ import keyword import re import tokenize -from typing import Any, Final, MutableMapping, MutableSequence, NamedTuple, Sequence, Tuple +from collections.abc import MutableMapping, MutableSequence, Sequence +from typing import Any, Final, NamedTuple, Tuple from typing_extensions import TypeAlias as _TypeAlias import mypy.util diff --git a/mypy/stubgen.py b/mypy/stubgen.py index b197f4a57123..ca1fda27a976 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -47,7 +47,8 @@ import os.path import sys import traceback -from typing import Final, Iterable, Iterator +from collections.abc import Iterable, Iterator +from typing import Final import mypy.build import mypy.mixedtraverser diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 3a2b242638e5..9895d23ffaab 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -11,8 +11,9 @@ import inspect import keyword import os.path +from collections.abc import Mapping from types import FunctionType, ModuleType -from typing import Any, Callable, Mapping +from typing import Any, Callable from mypy.fastparse import parse_type_comment from mypy.moduleinspect import is_c_module diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6b5ea0d5af61..48dc565bfe14 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -25,10 +25,11 @@ import typing_extensions import warnings from collections import defaultdict +from collections.abc import Iterator from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path -from typing import AbstractSet, Any, Generic, Iterator, TypeVar, Union +from typing import AbstractSet, Any, Generic, TypeVar, Union from typing_extensions import get_origin, is_typeddict import mypy.build diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 8ccf8301ee43..34808be8a8e4 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -8,8 +8,9 @@ import traceback from abc import abstractmethod from collections import defaultdict +from collections.abc import Iterable, Iterator, Mapping from contextlib import contextmanager -from typing import Final, Iterable, Iterator, Mapping +from typing import Final from typing_extensions import overload from mypy_extensions import mypyc_attr diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 666bdebed742..7e3d34deda27 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager -from typing import Any, Callable, Final, Iterator, List, TypeVar, cast +from typing import Any, Callable, Final, List, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.applytype diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 268f3032fc9b..193733ecce47 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -27,8 +27,9 @@ import itertools import json import os +from collections.abc import Iterator from contextlib import contextmanager -from typing import Callable, Iterator, NamedTuple, TypeVar, cast +from typing import Callable, NamedTuple, TypeVar, cast from typing_extensions import TypedDict from mypy.argmap import map_actuals_to_formals diff --git a/mypy/test/data.py b/mypy/test/data.py index bc17178d20e0..dcad0e1cbd58 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -10,9 +10,11 @@ import sys import tempfile from abc import abstractmethod +from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path -from typing import Any, Final, Iterator, NamedTuple, NoReturn, Pattern, Union +from re import Pattern +from typing import Any, Final, NamedTuple, NoReturn, Union from typing_extensions import TypeAlias as _TypeAlias import pytest diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 4a80207d3ec7..d9013221116a 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -8,7 +8,9 @@ import shutil import sys import time -from typing import IO, Any, Callable, Iterable, Iterator, Pattern +from collections.abc import Iterable, Iterator +from re import Pattern +from typing import IO, Any, Callable # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly diff --git a/mypy/test/meta/_pytest.py b/mypy/test/meta/_pytest.py index b8648f033143..0caa6b8694b7 100644 --- a/mypy/test/meta/_pytest.py +++ b/mypy/test/meta/_pytest.py @@ -3,9 +3,9 @@ import sys import textwrap import uuid +from collections.abc import Iterable from dataclasses import dataclass from pathlib import Path -from typing import Iterable from mypy.test.config import test_data_prefix diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index d7344ec898c7..4a5301d2cdb8 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -5,8 +5,8 @@ import subprocess import sys import tempfile +from collections.abc import Iterator from contextlib import contextmanager -from typing import Iterator import filelock diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index f099ebdc55a5..f3199dae7f73 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -9,7 +9,8 @@ import tempfile import textwrap import unittest -from typing import Any, Callable, Iterator +from collections.abc import Iterator +from typing import Any, Callable import mypy.stubtest from mypy.stubtest import parse_options, test_stubs diff --git a/mypy/test/update_data.py b/mypy/test/update_data.py index 2e1a6a9b3d1d..84b6383b3f0c 100644 --- a/mypy/test/update_data.py +++ b/mypy/test/update_data.py @@ -2,7 +2,7 @@ import re from collections import defaultdict -from typing import Iterator +from collections.abc import Iterator from mypy.test.data import DataDrivenTestCase, DataFileCollector, DataFileFix, parse_test_data diff --git a/mypy/treetransform.py b/mypy/treetransform.py index aafa4e95d530..3e5a7ef3f2ca 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -5,7 +5,8 @@ from __future__ import annotations -from typing import Iterable, Optional, cast +from collections.abc import Iterable +from typing import Optional, cast from mypy.nodes import ( GDEF, diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index a6888f21a402..f62d67bc26cc 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -14,7 +14,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import Any, Callable, Final, Generic, Iterable, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import Any, Callable, Final, Generic, TypeVar, cast from mypy_extensions import mypyc_attr, trait diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d0dd8542fd91..b3df842f9d05 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -3,8 +3,9 @@ from __future__ import annotations import itertools +from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, Final, Iterable, Iterator, List, Sequence, Tuple, TypeVar +from typing import Callable, Final, List, Tuple, TypeVar from typing_extensions import Protocol from mypy import errorcodes as codes, message_registry, nodes diff --git a/mypy/typeops.py b/mypy/typeops.py index f190168a18d7..f29682f4ecd5 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -8,7 +8,8 @@ from __future__ import annotations import itertools -from typing import Any, Iterable, List, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import Any, List, TypeVar, cast from mypy.copytype import copy_type from mypy.expandtype import expand_type, expand_type_by_instance diff --git a/mypy/types.py b/mypy/types.py index c174f94c066d..119a104c299a 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -4,16 +4,15 @@ import sys from abc import abstractmethod +from collections.abc import Iterable, Sequence from typing import ( TYPE_CHECKING, Any, ClassVar, Dict, Final, - Iterable, NamedTuple, NewType, - Sequence, TypeVar, Union, cast, diff --git a/mypy/types_utils.py b/mypy/types_utils.py index aaa7d7fba37a..124d024e8c1e 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -8,7 +8,8 @@ from __future__ import annotations -from typing import Callable, Iterable, cast +from collections.abc import Iterable +from typing import Callable, cast from mypy.nodes import ARG_STAR, ARG_STAR2, FuncItem, TypeAlias from mypy.types import ( diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index e2333ae8aa6d..cc6d4b637d2e 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from mypy_extensions import trait diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index 3bc67dc55ef3..1bf1a59f7d3f 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from mypy.types import ( AnyType, diff --git a/mypy/util.py b/mypy/util.py index 23f558e7ce7d..797498e29e9e 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -10,8 +10,9 @@ import shutil import sys import time +from collections.abc import Container, Iterable, Sequence, Sized from importlib import resources as importlib_resources -from typing import IO, Any, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar +from typing import IO, Any, Callable, Final, TypeVar from typing_extensions import Literal orjson: Any diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 411fc8093404..0ef78fd600ae 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -3,7 +3,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import Dict, Generic, Iterable, Iterator, Set, Tuple, TypeVar +from collections.abc import Iterable, Iterator +from typing import Dict, Generic, Set, Tuple, TypeVar from mypyc.ir.ops import ( Assign, diff --git a/mypyc/build.py b/mypyc/build.py index 6d59113ef872..3880860f3613 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -25,7 +25,8 @@ import re import sys import time -from typing import TYPE_CHECKING, Any, Dict, Iterable, NoReturn, Union, cast +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any, Dict, NoReturn, Union, cast from mypy.build import BuildSource from mypy.errors import CompileError diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index d1a9ad3bace1..9f290b9c99a8 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Callable, Mapping, Tuple +from collections.abc import Mapping +from typing import Callable, Tuple from mypyc.codegen.emit import Emitter, HeaderDeclaration, ReturnHandler from mypyc.codegen.emitfunc import native_function_header diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 5b2812c2293a..e64465aef0ff 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -7,7 +7,8 @@ import json import os -from typing import Iterable, List, Optional, Tuple, TypeVar +from collections.abc import Iterable +from typing import List, Optional, Tuple, TypeVar from mypy.build import ( BuildResult, diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py index 45c6c7a05867..f9bed440bb28 100644 --- a/mypyc/codegen/emitwrapper.py +++ b/mypyc/codegen/emitwrapper.py @@ -12,7 +12,7 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from mypy.nodes import ARG_NAMED, ARG_NAMED_OPT, ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, ArgKind from mypy.operators import op_methods_to_symbols, reverse_op_method_names, reverse_op_methods diff --git a/mypyc/crash.py b/mypyc/crash.py index 19136ea2f1de..1227aa8978af 100644 --- a/mypyc/crash.py +++ b/mypyc/crash.py @@ -2,8 +2,9 @@ import sys import traceback +from collections.abc import Iterator from contextlib import contextmanager -from typing import Iterator, NoReturn +from typing import NoReturn @contextmanager diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 44847c7bb0b3..bf21816fb07a 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Final, Sequence +from collections.abc import Sequence +from typing import Final from mypy.nodes import ARG_POS, ArgKind, Block, FuncDef from mypyc.common import BITMAP_BITS, JsonDict, bitmap_name, get_id_from_name, short_id_from_name diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6e186c4ef0fc..9ee745380872 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -12,7 +12,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, Final, Generic, List, NamedTuple, Sequence, TypeVar, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Generic, List, NamedTuple, TypeVar, Union from mypy_extensions import trait diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 59ee994f012d..ac0e791290ab 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Final, Sequence, Union +from collections.abc import Sequence +from typing import Any, Final, Union from mypyc.common import short_name from mypyc.ir.func_ir import FuncIR, all_values_full diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index ee980ff48b48..983bd6845207 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -5,8 +5,9 @@ from __future__ import annotations +from collections.abc import Iterator, Sequence from contextlib import contextmanager -from typing import Any, Callable, Final, Iterator, Sequence, Union +from typing import Any, Callable, Final, Union from typing_extensions import overload from mypy.build import Graph diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 97cd31af93af..c8c67cae309b 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -7,7 +7,8 @@ from __future__ import annotations import math -from typing import Callable, Sequence +from collections.abc import Sequence +from typing import Callable from mypy.nodes import ( ARG_NAMED, diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index a84db5a08863..dd996985e43d 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -13,7 +13,8 @@ from __future__ import annotations from collections import defaultdict -from typing import NamedTuple, Sequence +from collections.abc import Sequence +from typing import NamedTuple from mypy.nodes import ( ArgKind, diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 7216826906cb..cc6c501aa21c 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -6,7 +6,8 @@ from __future__ import annotations -from typing import Callable, Final, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import Callable, Final, Optional, Tuple from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, ArgKind diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index ee7b6027bbda..82250955f6e6 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -1,5 +1,6 @@ +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator, List, Optional, Tuple +from typing import List, Optional, Tuple from mypy.nodes import MatchStmt, NameExpr, TypeInfo from mypy.patterns import ( diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 4b132bb83722..89c4e883ec29 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -14,7 +14,8 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, NamedTuple, Tuple +from collections.abc import Iterable +from typing import NamedTuple, Tuple from mypy.build import Graph from mypy.nodes import ( diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index bd4acccf077a..cdc1d54589eb 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -9,7 +9,8 @@ from __future__ import annotations import importlib.util -from typing import Callable, Sequence +from collections.abc import Sequence +from typing import Callable from mypy.nodes import ( ARG_NAMED, diff --git a/mypyc/namegen.py b/mypyc/namegen.py index ce84fde143d1..5f57fa9a70ed 100644 --- a/mypyc/namegen.py +++ b/mypyc/namegen.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable class NameGenerator: diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 0f3be7891779..71367b25880b 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -11,7 +11,8 @@ import subprocess import sys import time -from typing import Any, Iterator +from collections.abc import Iterator +from typing import Any from mypy import build from mypy.errors import CompileError diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 6446af3427af..da6d7fc71a9d 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -7,7 +7,8 @@ import os.path import re import shutil -from typing import Callable, Iterator +from collections.abc import Iterator +from typing import Callable from mypy import build from mypy.errors import CompileError diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index f2ab438f6576..50d3f11ffe2a 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -18,7 +18,8 @@ from __future__ import annotations -from typing import Dict, Iterable, Tuple +from collections.abc import Iterable +from typing import Dict, Tuple from mypyc.analysis.dataflow import ( AnalysisDict, From 777b2a35d4be5734b603a2f72856485a0775d6de Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 00:14:28 -0800 Subject: [PATCH 1005/1617] Use PEP 585 collections (#18378) --- misc/analyze_cache.py | 4 ++-- misc/incremental_checker.py | 4 ++-- mypy/binder.py | 4 ++-- mypy/build.py | 3 +-- mypy/checker.py | 6 ++---- mypy/checkexpr.py | 4 ++-- mypy/checkstrformat.py | 6 +++--- mypy/config_parser.py | 4 ++-- mypy/constraints.py | 4 ++-- mypy/dmypy_server.py | 8 ++++---- mypy/errors.py | 6 +++--- mypy/fastparse.py | 14 +++++++------- mypy/literals.py | 4 ++-- mypy/memprofile.py | 4 ++-- mypy/messages.py | 4 ++-- mypy/modulefinder.py | 8 ++++---- mypy/nodes.py | 22 +++++----------------- mypy/plugins/attrs.py | 6 +++--- mypy/reachability.py | 4 ++-- mypy/report.py | 6 +++--- mypy/scope.py | 4 ++-- mypy/semanal.py | 4 ++-- mypy/semanal_main.py | 6 +++--- mypy/semanal_namedtuple.py | 4 ++-- mypy/server/astdiff.py | 6 +++--- mypy/server/aststrip.py | 3 +-- mypy/server/deps.py | 3 +-- mypy/stubdoc.py | 4 ++-- mypy/subtypes.py | 4 ++-- mypy/test/testsemanal.py | 3 +-- mypy/typeanal.py | 6 +++--- mypy/typeops.py | 4 ++-- mypy/types.py | 15 ++------------- mypy/typestate.py | 8 ++++---- mypyc/analysis/attrdefined.py | 4 ++-- mypyc/analysis/dataflow.py | 6 +++--- mypyc/analysis/selfleaks.py | 4 +--- mypyc/build.py | 4 ++-- mypyc/codegen/emitclass.py | 4 ++-- mypyc/codegen/emitmodule.py | 8 ++++---- mypyc/codegen/literals.py | 4 ++-- mypyc/common.py | 4 ++-- mypyc/ir/class_ir.py | 4 ++-- mypyc/ir/module_ir.py | 4 +--- mypyc/ir/ops.py | 4 ++-- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/irbuild/match.py | 10 +++++----- mypyc/irbuild/prepare.py | 4 ++-- mypyc/lower/registry.py | 4 ++-- mypyc/transform/refcount.py | 7 +++---- 50 files changed, 122 insertions(+), 156 deletions(-) diff --git a/misc/analyze_cache.py b/misc/analyze_cache.py index 62a5112b0ccd..0a05493b77a3 100644 --- a/misc/analyze_cache.py +++ b/misc/analyze_cache.py @@ -7,12 +7,12 @@ import os.path from collections import Counter from collections.abc import Iterable -from typing import Any, Dict, Final +from typing import Any, Final from typing_extensions import TypeAlias as _TypeAlias ROOT: Final = ".mypy_cache/3.5" -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] class CacheData: diff --git a/misc/incremental_checker.py b/misc/incremental_checker.py index 4e42aef333bb..a9ed61d13414 100755 --- a/misc/incremental_checker.py +++ b/misc/incremental_checker.py @@ -44,7 +44,7 @@ import textwrap import time from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter -from typing import Any, Dict, Final +from typing import Any, Final from typing_extensions import TypeAlias as _TypeAlias CACHE_PATH: Final = ".incremental_checker_cache.json" @@ -52,7 +52,7 @@ MYPY_TARGET_FILE: Final = "mypy" DAEMON_CMD: Final = ["python3", "-m", "mypy.dmypy"] -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] def print_offset(text: str, indent_length: int = 4) -> None: diff --git a/mypy/binder.py b/mypy/binder.py index 34447550e3bb..3d833153d628 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -3,7 +3,7 @@ from collections import defaultdict from collections.abc import Iterator from contextlib import contextmanager -from typing import DefaultDict, List, NamedTuple, Optional, Tuple, Union +from typing import NamedTuple, Optional, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values @@ -59,7 +59,7 @@ def __repr__(self) -> str: return f"Frame({self.id}, {self.types}, {self.unreachable}, {self.conditional_frame})" -Assigns = DefaultDict[Expression, List[Tuple[Type, Optional[Type]]]] +Assigns = defaultdict[Expression, list[tuple[Type, Optional[Type]]]] class ConditionalTypeBinder: diff --git a/mypy/build.py b/mypy/build.py index 108ba320054c..884862dcf568 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -32,7 +32,6 @@ Any, Callable, ClassVar, - Dict, Final, NamedTuple, NoReturn, @@ -118,7 +117,7 @@ } -Graph: _TypeAlias = Dict[str, "State"] +Graph: _TypeAlias = dict[str, "State"] # TODO: Get rid of BuildResult. We might as well return a BuildManager. diff --git a/mypy/checker.py b/mypy/checker.py index 440b161618ee..6adf8fe26a0d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -9,12 +9,10 @@ from typing import ( AbstractSet, Callable, - Dict, Final, Generic, NamedTuple, Optional, - Tuple, TypeVar, Union, cast, @@ -265,7 +263,7 @@ class FineGrainedDeferredNode(NamedTuple): # (such as two references to the same variable). TODO: it would # probably be better to have the dict keyed by the nodes' literal_hash # field instead. -TypeMap: _TypeAlias = Optional[Dict[Expression, Type]] +TypeMap: _TypeAlias = Optional[dict[Expression, Type]] # An object that represents either a precise type or a type with an upper bound; @@ -7813,7 +7811,7 @@ def conditional_types_to_typemaps( assert typ is not None maps.append({expr: typ}) - return cast(Tuple[TypeMap, TypeMap], tuple(maps)) + return cast(tuple[TypeMap, TypeMap], tuple(maps)) def gen_unique_name(base: str, table: SymbolTable) -> str: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 7000cfba25c1..2ba60744635f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -8,7 +8,7 @@ from collections import defaultdict from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, ClassVar, Final, List, Optional, cast +from typing import Callable, ClassVar, Final, Optional, cast from typing_extensions import TypeAlias as _TypeAlias, assert_never, overload import mypy.checker @@ -1966,7 +1966,7 @@ def infer_arg_types_in_context( if not t: res[i] = self.accept(args[i]) assert all(tp is not None for tp in res) - return cast(List[Type], res) + return cast(list[Type], res) def infer_function_type_arguments_using_context( self, callable: CallableType, error_context: Context diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 3d92897246fe..289961523b1d 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -14,7 +14,7 @@ import re from re import Match, Pattern -from typing import TYPE_CHECKING, Callable, Dict, Final, Tuple, Union, cast +from typing import TYPE_CHECKING, Callable, Final, Union, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.errorcodes as codes @@ -70,8 +70,8 @@ from mypy.typeops import custom_special_method FormatStringExpr: _TypeAlias = Union[StrExpr, BytesExpr] -Checkers: _TypeAlias = Tuple[Callable[[Expression], None], Callable[[Type], bool]] -MatchMap: _TypeAlias = Dict[Tuple[int, int], Match[str]] # span -> match +Checkers: _TypeAlias = tuple[Callable[[Expression], None], Callable[[Type], bool]] +MatchMap: _TypeAlias = dict[tuple[int, int], Match[str]] # span -> match def compile_format_re() -> Pattern[str]: diff --git a/mypy/config_parser.py b/mypy/config_parser.py index e54afd4a07f3..a0f93f663522 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -16,14 +16,14 @@ import tomli as tomllib from collections.abc import Iterable, Mapping, MutableMapping, Sequence -from typing import Any, Callable, Dict, Final, List, TextIO, Tuple, Union +from typing import Any, Callable, Final, TextIO, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import defaults from mypy.options import PER_MODULE_OPTIONS, Options _CONFIG_VALUE_TYPES: _TypeAlias = Union[ - str, bool, int, float, Dict[str, str], List[str], Tuple[int, int] + str, bool, int, float, dict[str, str], list[str], tuple[int, int] ] _INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES] diff --git a/mypy/constraints.py b/mypy/constraints.py index 410d33cae50c..848dec07cbcb 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Final, List +from typing import TYPE_CHECKING, Final import mypy.subtypes import mypy.typeops @@ -627,7 +627,7 @@ def visit_uninhabited_type(self, t: UninhabitedType) -> bool: return False -class ConstraintBuilderVisitor(TypeVisitor[List[Constraint]]): +class ConstraintBuilderVisitor(TypeVisitor[list[Constraint]]): """Visitor class for inferring type constraints.""" # The type that is compared against a template diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 43b8c5eb05be..ee1590a25141 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -18,7 +18,7 @@ import traceback from collections.abc import Sequence from contextlib import redirect_stderr, redirect_stdout -from typing import AbstractSet, Any, Callable, Final, List, Tuple +from typing import AbstractSet, Any, Callable, Final from typing_extensions import TypeAlias as _TypeAlias import mypy.build @@ -162,9 +162,9 @@ def ignore_suppressed_imports(module: str) -> bool: return module.startswith("encodings.") -ModulePathPair: _TypeAlias = Tuple[str, str] -ModulePathPairs: _TypeAlias = List[ModulePathPair] -ChangesAndRemovals: _TypeAlias = Tuple[ModulePathPairs, ModulePathPairs] +ModulePathPair: _TypeAlias = tuple[str, str] +ModulePathPairs: _TypeAlias = list[ModulePathPair] +ChangesAndRemovals: _TypeAlias = tuple[ModulePathPairs, ModulePathPairs] class Server: diff --git a/mypy/errors.py b/mypy/errors.py index c7af95461af1..2dd5af96eeef 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -5,7 +5,7 @@ import traceback from collections import defaultdict from collections.abc import Iterable -from typing import Callable, Final, NoReturn, Optional, TextIO, Tuple, TypeVar +from typing import Callable, Final, NoReturn, Optional, TextIO, TypeVar from typing_extensions import Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes @@ -152,7 +152,7 @@ def __init__( # Type used internally to represent errors: # (path, line, column, end_line, end_column, severity, message, allow_dups, code) -ErrorTuple: _TypeAlias = Tuple[ +ErrorTuple: _TypeAlias = tuple[ Optional[str], int, int, int, int, str, str, bool, Optional[ErrorCode] ] @@ -1328,7 +1328,7 @@ def __init__( # (file_path, line, column) -_ErrorLocation = Tuple[str, int, int] +_ErrorLocation = tuple[str, int, int] def create_errors(error_tuples: list[ErrorTuple]) -> list[MypyError]: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 07e3dcd26ee0..2ffe033b1e08 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -4,7 +4,7 @@ import sys import warnings from collections.abc import Sequence -from typing import Any, Callable, Final, List, Optional, TypeVar, Union, cast +from typing import Any, Callable, Final, Optional, TypeVar, Union, cast from typing_extensions import Literal, overload from mypy import defaults, errorcodes as codes, message_registry @@ -425,7 +425,7 @@ def translate_opt_expr_list(self, l: Sequence[AST | None]) -> list[Expression | return res def translate_expr_list(self, l: Sequence[AST]) -> list[Expression]: - return cast(List[Expression], self.translate_opt_expr_list(l)) + return cast(list[Expression], self.translate_opt_expr_list(l)) def get_lineno(self, node: ast3.expr | ast3.stmt) -> int: if ( @@ -668,7 +668,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: current_overload.append(last_if_overload) last_if_stmt, last_if_overload = None, None if isinstance(if_block_with_overload.body[-1], OverloadedFuncDef): - skipped_if_stmts.extend(cast(List[IfStmt], if_block_with_overload.body[:-1])) + skipped_if_stmts.extend(cast(list[IfStmt], if_block_with_overload.body[:-1])) current_overload.extend(if_block_with_overload.body[-1].items) else: current_overload.append( @@ -715,7 +715,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: last_if_stmt_overload_name = None if if_block_with_overload is not None: skipped_if_stmts.extend( - cast(List[IfStmt], if_block_with_overload.body[:-1]) + cast(list[IfStmt], if_block_with_overload.body[:-1]) ) last_if_overload = cast( Union[Decorator, FuncDef, OverloadedFuncDef], @@ -939,7 +939,7 @@ def do_func_def( self.errors, line=lineno, override_column=n.col_offset ).translate_expr_list(func_type_ast.argtypes) # Use a cast to work around `list` invariance - arg_types = cast(List[Optional[Type]], translated_args) + arg_types = cast(list[Optional[Type]], translated_args) return_type = TypeConverter(self.errors, line=lineno).visit(func_type_ast.returns) # add implicit self type @@ -1051,7 +1051,7 @@ def transform_args( ) -> list[Argument]: new_args = [] names: list[ast3.arg] = [] - posonlyargs = getattr(args, "posonlyargs", cast(List[ast3.arg], [])) + posonlyargs = getattr(args, "posonlyargs", cast(list[ast3.arg], [])) args_args = posonlyargs + args.args args_defaults = args.defaults num_no_defaults = len(args_args) - len(args_defaults) @@ -1589,7 +1589,7 @@ def visit_Call(self, n: Call) -> CallExpr: self.visit(n.func), arg_types, arg_kinds, - cast("List[Optional[str]]", [None] * len(args)) + keyword_names, + cast("list[Optional[str]]", [None] * len(args)) + keyword_names, ) return self.set_line(e, n) diff --git a/mypy/literals.py b/mypy/literals.py index a4527a47f3a6..32b5ad7b9fde 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterable -from typing import Any, Final, Optional, Tuple +from typing import Any, Final, Optional from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( @@ -129,7 +129,7 @@ def literal(e: Expression) -> int: return LITERAL_NO -Key: _TypeAlias = Tuple[Any, ...] +Key: _TypeAlias = tuple[Any, ...] def subkeys(key: Key) -> Iterable[Key]: diff --git a/mypy/memprofile.py b/mypy/memprofile.py index e47d0deb1ab3..4bab4ecb262e 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -10,7 +10,7 @@ import sys from collections import defaultdict from collections.abc import Iterable -from typing import Dict, cast +from typing import cast from mypy.nodes import FakeInfo, Node from mypy.types import Type @@ -109,7 +109,7 @@ def visit(o: object) -> None: # Processing these would cause a crash. continue if type(obj) in (dict, defaultdict): - for key, val in cast(Dict[object, object], obj).items(): + for key, val in cast(dict[object, object], obj).items(): visit(key) visit(val) if type(obj) in (list, tuple, set): diff --git a/mypy/messages.py b/mypy/messages.py index fe4552112f16..5fa4dc0c05ad 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -17,7 +17,7 @@ from collections.abc import Collection, Iterable, Iterator, Sequence from contextlib import contextmanager from textwrap import dedent -from typing import Any, Callable, Final, List, cast +from typing import Any, Callable, Final, cast import mypy.typeops from mypy import errorcodes as codes, message_registry @@ -955,7 +955,7 @@ def too_few_arguments( msg = "Missing positional arguments" callee_name = callable_name(callee) if callee_name is not None and diff and all(d is not None for d in diff): - args = '", "'.join(cast(List[str], diff)) + args = '", "'.join(cast(list[str], diff)) msg += f' "{args}" in call to {callee_name}' else: msg = "Too few arguments" + for_function(callee) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index a5d28a30dea8..61dbb6c61d1f 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,7 +13,7 @@ import subprocess import sys from enum import Enum, unique -from typing import Dict, Final, List, Optional, Tuple, Union +from typing import Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo @@ -53,11 +53,11 @@ def asdict(self) -> dict[str, tuple[str, ...]]: # Package dirs are a two-tuple of path to search and whether to verify the module -OnePackageDir = Tuple[str, bool] -PackageDirs = List[OnePackageDir] +OnePackageDir = tuple[str, bool] +PackageDirs = list[OnePackageDir] # Minimum and maximum Python versions for modules in stdlib as (major, minor) -StdlibVersions: _TypeAlias = Dict[str, Tuple[Tuple[int, int], Optional[Tuple[int, int]]]] +StdlibVersions: _TypeAlias = dict[str, tuple[tuple[int, int], Optional[tuple[int, int]]]] PYTHON_EXTENSIONS: Final = [".pyi", ".py"] diff --git a/mypy/nodes.py b/mypy/nodes.py index e287fdb652d6..585012d5a865 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -7,19 +7,7 @@ from collections import defaultdict from collections.abc import Iterator, Sequence from enum import Enum, unique -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Final, - List, - Optional, - Tuple, - TypeVar, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, Callable, Final, Optional, TypeVar, Union, cast from typing_extensions import TypeAlias as _TypeAlias, TypeGuard from mypy_extensions import trait @@ -80,7 +68,7 @@ def set_line( T = TypeVar("T") -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] # Symbol table node kinds @@ -264,7 +252,7 @@ def deserialize(cls, data: JsonDict) -> SymbolNode: # Items: fullname, related symbol table node, surrounding type (if any) -Definition: _TypeAlias = Tuple[str, "SymbolTableNode", Optional["TypeInfo"]] +Definition: _TypeAlias = tuple[str, "SymbolTableNode", Optional["TypeInfo"]] class MypyFile(SymbolNode): @@ -3743,7 +3731,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias: fullname, line, column, - alias_tvars=cast(List[mypy.types.TypeVarLikeType], alias_tvars), + alias_tvars=cast(list[mypy.types.TypeVarLikeType], alias_tvars), no_args=no_args, normalized=normalized, python_3_12_type_alias=python_3_12_type_alias, @@ -4008,7 +3996,7 @@ def deserialize(cls, data: JsonDict) -> SymbolTableNode: return stnode -class SymbolTable(Dict[str, SymbolTableNode]): +class SymbolTable(dict[str, SymbolTableNode]): """Static representation of a namespace dictionary. This is used for module, class and function namespaces. diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 15d442db0e58..e7eed030ce1f 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -5,7 +5,7 @@ from collections import defaultdict from collections.abc import Iterable, Mapping from functools import reduce -from typing import Final, List, cast +from typing import Final, cast from typing_extensions import Literal import mypy.plugin # To avoid circular imports. @@ -807,7 +807,7 @@ def _parse_assignments( rvalues: list[Expression] = [] if isinstance(lvalue, (TupleExpr, ListExpr)): if all(isinstance(item, NameExpr) for item in lvalue.items): - lvalues = cast(List[NameExpr], lvalue.items) + lvalues = cast(list[NameExpr], lvalue.items) if isinstance(stmt.rvalue, (TupleExpr, ListExpr)): rvalues = stmt.rvalue.items elif isinstance(lvalue, NameExpr): @@ -1088,7 +1088,7 @@ def _get_expanded_attr_types( return None init_func = expand_type_by_instance(init_func, typ) # [1:] to skip the self argument of AttrClass.__init__ - field_names = cast(List[str], init_func.arg_names[1:]) + field_names = cast(list[str], init_func.arg_names[1:]) field_types = init_func.arg_types[1:] return [dict(zip(field_names, field_types))] else: diff --git a/mypy/reachability.py b/mypy/reachability.py index a25b9dff4581..e69a857553d5 100644 --- a/mypy/reachability.py +++ b/mypy/reachability.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Final, Tuple, TypeVar +from typing import Final, TypeVar from mypy.literals import literal from mypy.nodes import ( @@ -254,7 +254,7 @@ def consider_sys_platform(expr: Expression, platform: str) -> int: return TRUTH_VALUE_UNKNOWN -Targ = TypeVar("Targ", int, str, Tuple[int, ...]) +Targ = TypeVar("Targ", int, str, tuple[int, ...]) def fixed_comparison(left: Targ, op: str, right: Targ) -> int: diff --git a/mypy/report.py b/mypy/report.py index 1beb375299bd..39cd80ed38bf 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -13,7 +13,7 @@ from abc import ABCMeta, abstractmethod from collections.abc import Iterator from operator import attrgetter -from typing import Any, Callable, Dict, Final, Tuple +from typing import Any, Callable, Final from typing_extensions import TypeAlias as _TypeAlias from urllib.request import pathname2url @@ -44,8 +44,8 @@ ] ) -ReporterClasses: _TypeAlias = Dict[ - str, Tuple[Callable[["Reports", str], "AbstractReporter"], bool] +ReporterClasses: _TypeAlias = dict[ + str, tuple[Callable[["Reports", str], "AbstractReporter"], bool] ] reporter_classes: Final[ReporterClasses] = {} diff --git a/mypy/scope.py b/mypy/scope.py index c13c45573557..766048c41180 100644 --- a/mypy/scope.py +++ b/mypy/scope.py @@ -7,12 +7,12 @@ from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Optional, Tuple +from typing import Optional from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import FuncBase, TypeInfo -SavedScope: _TypeAlias = Tuple[str, Optional[TypeInfo], Optional[FuncBase]] +SavedScope: _TypeAlias = tuple[str, Optional[TypeInfo], Optional[FuncBase]] class Scope: diff --git a/mypy/semanal.py b/mypy/semanal.py index 4e1769a29866..02e34dd00c63 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -52,7 +52,7 @@ from collections.abc import Collection, Iterable, Iterator from contextlib import contextmanager -from typing import Any, Callable, Final, List, TypeVar, cast +from typing import Any, Callable, Final, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias, TypeGuard from mypy import errorcodes as codes, message_registry @@ -5140,7 +5140,7 @@ def process_module_assignment( # with unpacking assignment like `x, y = a, b`. Mypy didn't # understand our all(isinstance(...)), so cast them as TupleExpr # so mypy knows it is safe to access their .items attribute. - seq_lvals = cast(List[TupleExpr], lvals) + seq_lvals = cast(list[TupleExpr], lvals) # given an assignment like: # (x, y) = (m, n) = (a, b) # we now have: diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 09a1223be6aa..ded2a9412168 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -27,7 +27,7 @@ from __future__ import annotations from contextlib import nullcontext -from typing import TYPE_CHECKING, Callable, Final, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Callable, Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias import mypy.build @@ -59,7 +59,7 @@ from mypy.build import Graph, State -Patches: _TypeAlias = List[Tuple[int, Callable[[], None]]] +Patches: _TypeAlias = list[tuple[int, Callable[[], None]]] # If we perform this many iterations, raise an exception since we are likely stuck. @@ -304,7 +304,7 @@ def process_top_level_function( analyzer.saved_locals.clear() -TargetInfo: _TypeAlias = Tuple[ +TargetInfo: _TypeAlias = tuple[ str, Union[MypyFile, FuncDef, OverloadedFuncDef, Decorator], Optional[TypeInfo] ] diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 52665b0fa121..a18d0591364c 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -8,7 +8,7 @@ import keyword from collections.abc import Container, Iterator, Mapping from contextlib import contextmanager -from typing import Final, List, cast +from typing import Final, cast from mypy.errorcodes import ARG_TYPE, ErrorCode from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type @@ -605,7 +605,7 @@ def add_method( items = [arg.variable.name for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types - signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) + signature = CallableType(cast(list[Type], types), arg_kinds, items, ret, function_type) signature.variables = [self_type] func = FuncDef(funcname, args, Block([])) func.info = info diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index a2711f9e0a8f..f91687823841 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -53,7 +53,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' from __future__ import annotations from collections.abc import Sequence -from typing import Tuple, Union +from typing import Union from typing_extensions import TypeAlias as _TypeAlias from mypy.expandtype import expand_type @@ -115,10 +115,10 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' # Type snapshots are strict, they must be hashable and ordered (e.g. for Unions). Primitive: _TypeAlias = Union[str, float, int, bool] # float is for Literal[3.14] support. -SnapshotItem: _TypeAlias = Tuple[Union[Primitive, "SnapshotItem"], ...] +SnapshotItem: _TypeAlias = tuple[Union[Primitive, "SnapshotItem"], ...] # Symbol snapshots can be more lenient. -SymbolSnapshot: _TypeAlias = Tuple[object, ...] +SymbolSnapshot: _TypeAlias = tuple[object, ...] def compare_symbol_table_snapshots( diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py index 410b3ecfa976..a70dfc30deb5 100644 --- a/mypy/server/aststrip.py +++ b/mypy/server/aststrip.py @@ -35,7 +35,6 @@ from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Dict, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( @@ -69,7 +68,7 @@ from mypy.types import CallableType from mypy.typestate import type_state -SavedAttributes: _TypeAlias = Dict[Tuple[ClassDef, str], SymbolTableNode] +SavedAttributes: _TypeAlias = dict[tuple[ClassDef, str], SymbolTableNode] def strip_target( diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 6376600ffc0c..f4e7b86abf63 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -82,7 +82,6 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a from __future__ import annotations from collections import defaultdict -from typing import List from mypy.nodes import ( GDEF, @@ -947,7 +946,7 @@ def get_type_triggers( return typ.accept(TypeTriggersVisitor(use_logical_deps, seen_aliases)) -class TypeTriggersVisitor(TypeVisitor[List[str]]): +class TypeTriggersVisitor(TypeVisitor[list[str]]): def __init__( self, use_logical_deps: bool, seen_aliases: set[TypeAliasType] | None = None ) -> None: diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 79365bec33bd..e99204f3ade5 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -12,13 +12,13 @@ import re import tokenize from collections.abc import MutableMapping, MutableSequence, Sequence -from typing import Any, Final, NamedTuple, Tuple +from typing import Any, Final, NamedTuple from typing_extensions import TypeAlias as _TypeAlias import mypy.util # Type alias for signatures strings in format ('func_name', '(arg, opt_arg=False)'). -Sig: _TypeAlias = Tuple[str, str] +Sig: _TypeAlias = tuple[str, str] _TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], .\"\']*(\.[a-zA-Z_][\w\[\], ]*)*$") diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 7e3d34deda27..ceb9b7f0298a 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -2,7 +2,7 @@ from collections.abc import Iterator from contextlib import contextmanager -from typing import Any, Callable, Final, List, TypeVar, cast +from typing import Any, Callable, Final, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.applytype @@ -1886,7 +1886,7 @@ def unify_generic_callable( ) if None in inferred_vars: return None - non_none_inferred_vars = cast(List[Type], inferred_vars) + non_none_inferred_vars = cast(list[Type], inferred_vars) had_errors = False def report(*args: Any) -> None: diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index cdecc4739168..a544e1f91829 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -3,7 +3,6 @@ from __future__ import annotations import sys -from typing import Dict from mypy import build from mypy.defaults import PYTHON3_VERSION @@ -199,7 +198,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: ) -class TypeInfoMap(Dict[str, TypeInfo]): +class TypeInfoMap(dict[str, TypeInfo]): def __str__(self) -> str: a: list[str] = ["TypeInfoMap("] for x, y in sorted(self.items()): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b3df842f9d05..031ec0450db1 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -5,7 +5,7 @@ import itertools from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, Final, List, Tuple, TypeVar +from typing import Callable, Final, TypeVar from typing_extensions import Protocol from mypy import errorcodes as codes, message_registry, nodes @@ -1977,7 +1977,7 @@ def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: ) -TypeVarLikeList = List[Tuple[str, TypeVarLikeExpr]] +TypeVarLikeList = list[tuple[str, TypeVarLikeExpr]] class MsgCallback(Protocol): @@ -2432,7 +2432,7 @@ def collect_all_inner_types(t: Type) -> list[Type]: return t.accept(CollectAllInnerTypesQuery()) -class CollectAllInnerTypesQuery(TypeQuery[List[Type]]): +class CollectAllInnerTypesQuery(TypeQuery[list[Type]]): def __init__(self) -> None: super().__init__(self.combine_lists_strategy) diff --git a/mypy/typeops.py b/mypy/typeops.py index f29682f4ecd5..7c7e2b8bf8e5 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -9,7 +9,7 @@ import itertools from collections.abc import Iterable, Sequence -from typing import Any, List, TypeVar, cast +from typing import Any, TypeVar, cast from mypy.copytype import copy_type from mypy.expandtype import expand_type, expand_type_by_instance @@ -1051,7 +1051,7 @@ def get_all_type_vars(tp: Type) -> list[TypeVarLikeType]: return tp.accept(TypeVarExtractor(include_all=True)) -class TypeVarExtractor(TypeQuery[List[TypeVarLikeType]]): +class TypeVarExtractor(TypeQuery[list[TypeVarLikeType]]): def __init__(self, include_all: bool = False) -> None: super().__init__(self._merge) self.include_all = include_all diff --git a/mypy/types.py b/mypy/types.py index 119a104c299a..164e18be032e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -5,18 +5,7 @@ import sys from abc import abstractmethod from collections.abc import Iterable, Sequence -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - Dict, - Final, - NamedTuple, - NewType, - TypeVar, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, ClassVar, Final, NamedTuple, NewType, TypeVar, Union, cast from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard, overload import mypy.nodes @@ -37,7 +26,7 @@ T = TypeVar("T") -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] # The set of all valid expressions that can currently be contained # inside of a Literal[...]. diff --git a/mypy/typestate.py b/mypy/typestate.py index 0082c5564705..574618668477 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Dict, Final, Set, Tuple +from typing import Final from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import VARIANCE_NOT_READY, TypeInfo @@ -16,15 +16,15 @@ MAX_NEGATIVE_CACHE_ENTRIES: Final = 10000 # Represents that the 'left' instance is a subtype of the 'right' instance -SubtypeRelationship: _TypeAlias = Tuple[Instance, Instance] +SubtypeRelationship: _TypeAlias = tuple[Instance, Instance] # A tuple encoding the specific conditions under which we performed the subtype check. # (e.g. did we want a proper subtype? A regular subtype while ignoring variance?) -SubtypeKind: _TypeAlias = Tuple[bool, ...] +SubtypeKind: _TypeAlias = tuple[bool, ...] # A cache that keeps track of whether the given TypeInfo is a part of a particular # subtype relationship -SubtypeCache: _TypeAlias = Dict[TypeInfo, Dict[SubtypeKind, Set[SubtypeRelationship]]] +SubtypeCache: _TypeAlias = dict[TypeInfo, dict[SubtypeKind, set[SubtypeRelationship]]] class TypeState: diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py index e4038bfaa238..896527bdcf14 100644 --- a/mypyc/analysis/attrdefined.py +++ b/mypyc/analysis/attrdefined.py @@ -63,7 +63,7 @@ def foo(self) -> int: from __future__ import annotations -from typing import Final, Set, Tuple +from typing import Final from mypyc.analysis.dataflow import ( CFG, @@ -279,7 +279,7 @@ def mark_attr_initialization_ops( op.mark_as_initializer() -GenAndKill = Tuple[Set[str], Set[str]] +GenAndKill = tuple[set[str], set[str]] def attributes_initialized_by_init_call(op: Call) -> set[str]: diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 0ef78fd600ae..26b58e224634 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -4,7 +4,7 @@ from abc import abstractmethod from collections.abc import Iterable, Iterator -from typing import Dict, Generic, Set, Tuple, TypeVar +from typing import Generic, TypeVar from mypyc.ir.ops import ( Assign, @@ -155,7 +155,7 @@ def cleanup_cfg(blocks: list[BasicBlock]) -> None: T = TypeVar("T") -AnalysisDict = Dict[Tuple[BasicBlock, int], Set[T]] +AnalysisDict = dict[tuple[BasicBlock, int], set[T]] class AnalysisResult(Generic[T]): @@ -167,7 +167,7 @@ def __str__(self) -> str: return f"before: {self.before}\nafter: {self.after}\n" -GenAndKill = Tuple[Set[T], Set[T]] +GenAndKill = tuple[set[T], set[T]] class BaseAnalysisVisitor(OpVisitor[GenAndKill[T]]): diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py index 5d89a9bfc7c6..4d3a7c87c5d1 100644 --- a/mypyc/analysis/selfleaks.py +++ b/mypyc/analysis/selfleaks.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Set, Tuple - from mypyc.analysis.dataflow import CFG, MAYBE_ANALYSIS, AnalysisResult, run_analysis from mypyc.ir.ops import ( Assign, @@ -47,7 +45,7 @@ ) from mypyc.ir.rtypes import RInstance -GenAndKill = Tuple[Set[None], Set[None]] +GenAndKill = tuple[set[None], set[None]] CLEAN: GenAndKill = (set(), set()) DIRTY: GenAndKill = ({None}, {None}) diff --git a/mypyc/build.py b/mypyc/build.py index 3880860f3613..d0709fceb97d 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -26,7 +26,7 @@ import sys import time from collections.abc import Iterable -from typing import TYPE_CHECKING, Any, Dict, NoReturn, Union, cast +from typing import TYPE_CHECKING, Any, NoReturn, Union, cast from mypy.build import BuildSource from mypy.errors import CompileError @@ -88,7 +88,7 @@ def setup_mypycify_vars() -> None: # There has to be a better approach to this. # The vars can contain ints but we only work with str ones - vars = cast(Dict[str, str], sysconfig.get_config_vars()) + vars = cast(dict[str, str], sysconfig.get_config_vars()) if sys.platform == "darwin": # Disable building 32-bit binaries, since we generate too much code # for a 32-bit Mach-O object. There has to be a better way to do this. diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 9f290b9c99a8..54c979482f66 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Mapping -from typing import Callable, Tuple +from typing import Callable from mypyc.codegen.emit import Emitter, HeaderDeclaration, ReturnHandler from mypyc.codegen.emitfunc import native_function_header @@ -40,7 +40,7 @@ def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: # and return the function name to stick in the slot. # TODO: Add remaining dunder methods SlotGenerator = Callable[[ClassIR, FuncIR, Emitter], str] -SlotTable = Mapping[str, Tuple[str, SlotGenerator]] +SlotTable = Mapping[str, tuple[str, SlotGenerator]] SLOT_DEFS: SlotTable = { "__init__": ("tp_init", lambda c, t, e: generate_init_for_class(c, t, e)), diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index e64465aef0ff..bd2958c285c3 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -8,7 +8,7 @@ import json import os from collections.abc import Iterable -from typing import List, Optional, Tuple, TypeVar +from typing import Optional, TypeVar from mypy.build import ( BuildResult, @@ -84,11 +84,11 @@ # its modules along with the name of the group. (Which can be None # only if we are compiling only a single group with a single file in it # and not using shared libraries). -Group = Tuple[List[BuildSource], Optional[str]] -Groups = List[Group] +Group = tuple[list[BuildSource], Optional[str]] +Groups = list[Group] # A list of (file name, file contents) pairs. -FileContents = List[Tuple[str, str]] +FileContents = list[tuple[str, str]] class MarkedDeclaration: diff --git a/mypyc/codegen/literals.py b/mypyc/codegen/literals.py index 2c4ab0c1dc2e..4cd41e0f4d32 100644 --- a/mypyc/codegen/literals.py +++ b/mypyc/codegen/literals.py @@ -1,12 +1,12 @@ from __future__ import annotations -from typing import Final, FrozenSet, Tuple, Union +from typing import Final, Union from typing_extensions import TypeGuard # Supported Python literal types. All tuple / frozenset items must have supported # literal types as well, but we can't represent the type precisely. LiteralValue = Union[ - str, bytes, int, bool, float, complex, Tuple[object, ...], FrozenSet[object], None + str, bytes, int, bool, float, complex, tuple[object, ...], frozenset[object], None ] diff --git a/mypyc/common.py b/mypyc/common.py index 31567c689c34..724f61c34b78 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -2,7 +2,7 @@ import sys import sysconfig -from typing import Any, Dict, Final +from typing import Any, Final from mypy.util import unnamed_function @@ -83,7 +83,7 @@ ] -JsonDict = Dict[str, Any] +JsonDict = dict[str, Any] def shared_lib_name(group_name: str) -> str: diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 94bf714b28d4..94181e115145 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, NamedTuple +from typing import NamedTuple from mypyc.common import PROPSET_PREFIX, JsonDict from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature @@ -76,7 +76,7 @@ class VTableMethod(NamedTuple): shadow_method: FuncIR | None -VTableEntries = List[VTableMethod] +VTableEntries = list[VTableMethod] class ClassIR: diff --git a/mypyc/ir/module_ir.py b/mypyc/ir/module_ir.py index e3b240629eda..7d95b48e197e 100644 --- a/mypyc/ir/module_ir.py +++ b/mypyc/ir/module_ir.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import Dict - from mypyc.common import JsonDict from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncDecl, FuncIR @@ -91,4 +89,4 @@ def deserialize_modules(data: dict[str, JsonDict], ctx: DeserMaps) -> dict[str, # ModulesIRs should also always be an *OrderedDict*, but if we # declared it that way we would need to put it in quotes everywhere... -ModuleIRs = Dict[str, ModuleIR] +ModuleIRs = dict[str, ModuleIR] diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 9ee745380872..6a2e70aee6d7 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -13,7 +13,7 @@ from abc import abstractmethod from collections.abc import Sequence -from typing import TYPE_CHECKING, Final, Generic, List, NamedTuple, TypeVar, Union +from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union from mypy_extensions import trait @@ -1025,7 +1025,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: # True steals all arguments, False steals none, a list steals those in matching positions -StealsDescription = Union[bool, List[bool]] +StealsDescription = Union[bool, list[bool]] class CallC(RegisterOp): diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index cc6c501aa21c..bae38f27b346 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -7,7 +7,7 @@ from __future__ import annotations from collections.abc import Sequence -from typing import Callable, Final, Optional, Tuple +from typing import Callable, Final, Optional from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, ArgKind @@ -181,7 +181,7 @@ from mypyc.sametype import is_same_type from mypyc.subtype import is_subtype -DictEntry = Tuple[Optional[Value], Value] +DictEntry = tuple[Optional[Value], Value] # If the number of items is less than the threshold when initializing # a list, we would inline the generate IR using SetMem and expanded diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index 82250955f6e6..beb88311fe4d 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -1,6 +1,6 @@ from collections.abc import Generator from contextlib import contextmanager -from typing import List, Optional, Tuple +from typing import Optional from mypy.nodes import MatchStmt, NameExpr, TypeInfo from mypy.patterns import ( @@ -158,7 +158,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None: match_args_type = get_proper_type(ty.type) assert isinstance(match_args_type, TupleType) - match_args: List[str] = [] + match_args: list[str] = [] for item in match_args_type.items: proper_item = get_proper_type(item) @@ -221,7 +221,7 @@ def visit_mapping_pattern(self, pattern: MappingPattern) -> None: self.builder.add_bool_branch(is_dict, self.code_block, self.next_block) - keys: List[Value] = [] + keys: list[Value] = [] for key, value in zip(pattern.keys, pattern.values): self.builder.activate_block(self.code_block) @@ -340,10 +340,10 @@ def enter_subpattern(self, subject: Value) -> Generator[None, None, None]: def prep_sequence_pattern( seq_pattern: SequencePattern, -) -> Tuple[Optional[int], Optional[NameExpr], List[Pattern]]: +) -> tuple[Optional[int], Optional[NameExpr], list[Pattern]]: star_index: Optional[int] = None capture: Optional[NameExpr] = None - patterns: List[Pattern] = [] + patterns: list[Pattern] = [] for i, pattern in enumerate(seq_pattern.patterns): if isinstance(pattern, StarredPattern): diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 89c4e883ec29..b6cd632e475f 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -15,7 +15,7 @@ from collections import defaultdict from collections.abc import Iterable -from typing import NamedTuple, Tuple +from typing import NamedTuple from mypy.build import Graph from mypy.nodes import ( @@ -525,7 +525,7 @@ def prepare_non_ext_class_def( ) -RegisterImplInfo = Tuple[TypeInfo, FuncDef] +RegisterImplInfo = tuple[TypeInfo, FuncDef] class SingledispatchInfo(NamedTuple): diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py index 084d57df4608..3feedfc385ee 100644 --- a/mypyc/lower/registry.py +++ b/mypyc/lower/registry.py @@ -1,11 +1,11 @@ from __future__ import annotations -from typing import Callable, Final, List +from typing import Callable, Final from mypyc.ir.ops import Value from mypyc.irbuild.ll_builder import LowLevelIRBuilder -LowerFunc = Callable[[LowLevelIRBuilder, List[Value], int], Value] +LowerFunc = Callable[[LowLevelIRBuilder, list[Value], int], Value] lowering_registry: Final[dict[str, LowerFunc]] = {} diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index 50d3f11ffe2a..b2ca03d44630 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -19,7 +19,6 @@ from __future__ import annotations from collections.abc import Iterable -from typing import Dict, Tuple from mypyc.analysis.dataflow import ( AnalysisDict, @@ -47,13 +46,13 @@ Value, ) -Decs = Tuple[Tuple[Value, bool], ...] -Incs = Tuple[Value, ...] +Decs = tuple[tuple[Value, bool], ...] +Incs = tuple[Value, ...] # A cache of basic blocks that decrement and increment specific values # and then jump to some target block. This lets us cut down on how # much code we generate in some circumstances. -BlockCache = Dict[Tuple[BasicBlock, Decs, Incs], BasicBlock] +BlockCache = dict[tuple[BasicBlock, Decs, Incs], BasicBlock] def insert_ref_count_opcodes(ir: FuncIR) -> None: From 556ae16f3a856c3e7382bc195fe52152b4e62fc0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 00:51:21 -0800 Subject: [PATCH 1006/1617] Some improvements to linting (#18381) --- misc/upload-pypi.py | 2 +- mypy/build.py | 14 ++------------ mypy/checker.py | 15 ++------------- mypy/dmypy_server.py | 4 ++-- mypy/fswatcher.py | 4 ++-- mypy/graph_utils.py | 4 ++-- mypy/semanal_shared.py | 2 +- mypy/stubgenc.py | 2 +- mypy/stubtest.py | 4 ++-- mypy/test/testgraph.py | 2 +- mypyc/codegen/emit.py | 2 +- pyproject.toml | 11 +++++++++-- 12 files changed, 26 insertions(+), 40 deletions(-) diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py index 90ae80da643f..c0ff1b2a075e 100644 --- a/misc/upload-pypi.py +++ b/misc/upload-pypi.py @@ -27,7 +27,7 @@ def is_whl_or_tar(name: str) -> bool: - return name.endswith(".tar.gz") or name.endswith(".whl") + return name.endswith((".tar.gz", ".whl")) def item_ok_for_pypi(name: str) -> bool: diff --git a/mypy/build.py b/mypy/build.py index 884862dcf568..a1a9206367af 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -25,18 +25,8 @@ import sys import time import types -from collections.abc import Iterator, Mapping, Sequence -from typing import ( - TYPE_CHECKING, - AbstractSet, - Any, - Callable, - ClassVar, - Final, - NamedTuple, - NoReturn, - TextIO, -) +from collections.abc import Iterator, Mapping, Sequence, Set as AbstractSet +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, NamedTuple, NoReturn, TextIO from typing_extensions import TypeAlias as _TypeAlias, TypedDict import mypy.semanal_main diff --git a/mypy/checker.py b/mypy/checker.py index 6adf8fe26a0d..2b078f721736 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4,20 +4,9 @@ import itertools from collections import defaultdict -from collections.abc import Iterable, Iterator, Mapping, Sequence +from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet from contextlib import ExitStack, contextmanager -from typing import ( - AbstractSet, - Callable, - Final, - Generic, - NamedTuple, - Optional, - TypeVar, - Union, - cast, - overload, -) +from typing import Callable, Final, Generic, NamedTuple, Optional, TypeVar, Union, cast, overload from typing_extensions import TypeAlias as _TypeAlias import mypy.checkexpr diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index ee1590a25141..d73487efe3bc 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -16,9 +16,9 @@ import sys import time import traceback -from collections.abc import Sequence +from collections.abc import Sequence, Set as AbstractSet from contextlib import redirect_stderr, redirect_stdout -from typing import AbstractSet, Any, Callable, Final +from typing import Any, Callable, Final from typing_extensions import TypeAlias as _TypeAlias import mypy.build diff --git a/mypy/fswatcher.py b/mypy/fswatcher.py index a51b1fa95337..d5873f3a0a99 100644 --- a/mypy/fswatcher.py +++ b/mypy/fswatcher.py @@ -3,8 +3,8 @@ from __future__ import annotations import os -from collections.abc import Iterable -from typing import AbstractSet, NamedTuple +from collections.abc import Iterable, Set as AbstractSet +from typing import NamedTuple from mypy.fscache import FileSystemCache diff --git a/mypy/graph_utils.py b/mypy/graph_utils.py index 9083ed6a12f7..154efcef48a9 100644 --- a/mypy/graph_utils.py +++ b/mypy/graph_utils.py @@ -2,8 +2,8 @@ from __future__ import annotations -from collections.abc import Iterable, Iterator -from typing import AbstractSet, TypeVar +from collections.abc import Iterable, Iterator, Set as AbstractSet +from typing import TypeVar T = TypeVar("T") diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index b7d50e411016..40af5ce81d9e 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -453,7 +453,7 @@ def require_bool_literal_argument( api: SemanticAnalyzerInterface | SemanticAnalyzerPluginInterface, expression: Expression, name: str, - default: Literal[True] | Literal[False], + default: Literal[True, False], ) -> bool: ... diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 9895d23ffaab..694be8e4beda 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -203,7 +203,7 @@ def _from_sigs(cls, sigs: list[FunctionSig], is_abstract: bool = False) -> CFunc sigs[0].name, "\n".join(sig.format_sig()[:-4] for sig in sigs), is_abstract ) - def __get__(self) -> None: + def __get__(self) -> None: # noqa: PLE0302 """ This exists to make this object look like a method descriptor and thus return true for CStubGenerator.ismethod() diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 48dc565bfe14..21e8736ff6a7 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -25,11 +25,11 @@ import typing_extensions import warnings from collections import defaultdict -from collections.abc import Iterator +from collections.abc import Iterator, Set as AbstractSet from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path -from typing import AbstractSet, Any, Generic, TypeVar, Union +from typing import Any, Generic, TypeVar, Union from typing_extensions import get_origin, is_typeddict import mypy.build diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index 0355e75e8c34..238869f36fdf 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -3,7 +3,7 @@ from __future__ import annotations import sys -from typing import AbstractSet +from collections.abc import Set as AbstractSet from mypy.build import BuildManager, BuildSourceSet, State, order_ascc, sorted_components from mypy.errors import Errors diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 97302805fd3b..f6663e6194dc 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -1034,7 +1034,7 @@ def emit_box( self.emit_line(f"if (unlikely({dest} == NULL))") self.emit_line(" CPyError_OutOfMemory();") # TODO: Fail if dest is None - for i in range(0, len(typ.types)): + for i in range(len(typ.types)): if not typ.is_unboxed: self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}") else: diff --git a/pyproject.toml b/pyproject.toml index 24f13921eaf8..5edbc8a75224 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ force-exclude = ''' [tool.ruff] line-length = 99 -target-version = "py38" +target-version = "py39" fix = true extend-exclude = [ @@ -126,11 +126,13 @@ select = [ "B", # flake8-bugbear "I", # isort "N", # pep8-naming + "PIE", # flake8-pie + "PLE", # pylint error "RUF100", # Unused noqa comments "PGH004", # blanket noqa comments "UP", # pyupgrade "C4", # flake8-comprehensions - "SIM201", "SIM202", # simplify comparisons involving not + "SIM201", "SIM202", "SIM222", "SIM223", # flake8-simplify "ISC001", # implicitly concatenated string "RET501", "RET502", # better return None handling ] @@ -149,7 +151,10 @@ ignore = [ "N806", # UPPER_CASE used for constant local variables "UP031", # Use format specifiers instead of percent format "UP032", # 'f-string always preferable to format' is controversial + "C409", # https://github.com/astral-sh/ruff/issues/12912 + "C420", # reads a little worse. fromkeys predates dict comprehensions "C416", # There are a few cases where it's nice to have names for the dict items + "PIE790", # there's nothing wrong with pass ] unfixable = [ @@ -158,6 +163,8 @@ unfixable = [ "F602", # automatic fix might obscure issue "B018", # automatic fix might obscure issue "UP036", # sometimes it's better to just noqa this + "SIM222", # automatic fix might obscure issue + "SIM223", # automatic fix might obscure issue ] [tool.ruff.lint.per-file-ignores] From 9e40be6e4f3fc9832c1cebb7542d724833b55d75 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:51:43 +0100 Subject: [PATCH 1007/1617] Replace optional in annotations (#18382) --- mypyc/irbuild/match.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index beb88311fe4d..04a6cff9779c 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -1,6 +1,7 @@ +from __future__ import annotations + from collections.abc import Generator from contextlib import contextmanager -from typing import Optional from mypy.nodes import MatchStmt, NameExpr, TypeInfo from mypy.patterns import ( @@ -57,7 +58,7 @@ class MatchVisitor(TraverserVisitor): subject: Value match: MatchStmt - as_pattern: Optional[AsPattern] = None + as_pattern: AsPattern | None = None def __init__(self, builder: IRBuilder, match_node: MatchStmt) -> None: self.builder = builder @@ -340,9 +341,9 @@ def enter_subpattern(self, subject: Value) -> Generator[None, None, None]: def prep_sequence_pattern( seq_pattern: SequencePattern, -) -> tuple[Optional[int], Optional[NameExpr], list[Pattern]]: - star_index: Optional[int] = None - capture: Optional[NameExpr] = None +) -> tuple[int | None, NameExpr | None, list[Pattern]]: + star_index: int | None = None + capture: NameExpr | None = None patterns: list[Pattern] = [] for i, pattern in enumerate(seq_pattern.patterns): From 80e5e8ba27052dd6fc23a7f3eeb59177cba6608d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=8A=B9=EC=9B=90?= Date: Mon, 30 Dec 2024 18:06:02 +0900 Subject: [PATCH 1008/1617] Allow to use Final and ClassVar after Python 3.13 (#18358) This PR allows to use Final and ClassVar after python 3.13 I saw this [PR](https://github.com/python/mypy/pull/10478) and I saw recent changes of python 3.13 https://docs.python.org/3/library/typing.html#typing.Final Final now can be nested with ClassVar. so I added a version check! --------- Co-authored-by: triumph1 Co-authored-by: hauntsaninja --- mypy/semanal.py | 10 +++++++++- mypy/typeanal.py | 18 ++++++++++++------ test-data/unit/check-final.test | 11 +++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 02e34dd00c63..8335f91c4d3b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3658,7 +3658,11 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: else: s.type = s.unanalyzed_type.args[0] - if s.type is not None and self.is_classvar(s.type): + if ( + s.type is not None + and self.options.python_version < (3, 13) + and self.is_classvar(s.type) + ): self.fail("Variable should not be annotated with both ClassVar and Final", s) return False @@ -7358,6 +7362,7 @@ def type_analyzer( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7379,6 +7384,7 @@ def type_analyzer( report_invalid_types=report_invalid_types, allow_placeholder=allow_placeholder, allow_typed_dict_special_forms=allow_typed_dict_special_forms, + allow_final=allow_final, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, @@ -7403,6 +7409,7 @@ def anal_type( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7439,6 +7446,7 @@ def anal_type( allow_tuple_literal=allow_tuple_literal, allow_placeholder=allow_placeholder, allow_typed_dict_special_forms=allow_typed_dict_special_forms, + allow_final=allow_final, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 031ec0450db1..6e2106875e1a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -226,6 +226,7 @@ def __init__( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = True, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -261,6 +262,8 @@ def __init__( self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? self.allow_typed_dict_special_forms = allow_typed_dict_special_forms + # Set True when we analyze ClassVar else False + self.allow_final = allow_final # Are we in a context where ParamSpec literals are allowed? self.allow_param_spec_literals = allow_param_spec_literals # Are we in context where literal "..." specifically is allowed? @@ -607,11 +610,12 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ code=codes.VALID_TYPE, ) else: - self.fail( - "Final can be only used as an outermost qualifier in a variable annotation", - t, - code=codes.VALID_TYPE, - ) + if not self.allow_final: + self.fail( + "Final can be only used as an outermost qualifier in a variable annotation", + t, + code=codes.VALID_TYPE, + ) return AnyType(TypeOfAny.from_error) elif fullname == "typing.Tuple" or ( fullname == "builtins.tuple" @@ -692,7 +696,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "ClassVar[...] must have at most one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return self.anal_type(t.args[0]) + return self.anal_type(t.args[0], allow_final=self.options.python_version >= (3, 13)) elif fullname in NEVER_NAMES: return UninhabitedType() elif fullname in LITERAL_TYPE_NAMES: @@ -1878,11 +1882,13 @@ def anal_type( allow_unpack: bool = False, allow_ellipsis: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, ) -> Type: if nested: self.nesting_level += 1 old_allow_typed_dict_special_forms = self.allow_typed_dict_special_forms self.allow_typed_dict_special_forms = allow_typed_dict_special_forms + self.allow_final = allow_final old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis old_allow_unpack = self.allow_unpack diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 763183159e94..51ce0edc66c2 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -194,6 +194,7 @@ def g(x: int) -> Final[int]: ... # E: Final can be only used as an outermost qu [out] [case testFinalDefiningNotInMethodExtensions] +# flags: --python-version 3.14 from typing_extensions import Final def f(x: Final[int]) -> int: ... # E: Final can be only used as an outermost qualifier in a variable annotation @@ -1128,6 +1129,7 @@ class A: [builtins fixtures/tuple.pyi] [case testFinalUsedWithClassVar] +# flags: --python-version 3.12 from typing import Final, ClassVar class A: @@ -1136,6 +1138,15 @@ class A: c: ClassVar[Final] = 1 # E: Final can be only used as an outermost qualifier in a variable annotation [out] +[case testFinalUsedWithClassVarAfterPy313] +# flags: --python-version 3.13 +from typing import Final, ClassVar + +class A: + a: Final[ClassVar[int]] = 1 + b: ClassVar[Final[int]] = 1 + c: ClassVar[Final] = 1 + [case testFinalClassWithAbstractMethod] from typing import final from abc import ABC, abstractmethod From 1c427e77db02840a60bcf3a8e6192513d002c7d4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:30:13 +0100 Subject: [PATCH 1009/1617] Update typing_extensions imports for Python 3.9 (#18383) --- mypy/build.py | 14 ++++++++++++-- mypy/checkexpr.py | 4 ++-- mypy/fastparse.py | 3 +-- mypy/plugins/attrs.py | 3 +-- mypy/semanal_shared.py | 3 +-- mypy/stubtest.py | 16 +++++++--------- mypy/stubutil.py | 3 +-- mypy/suggestions.py | 3 +-- mypy/typeanal.py | 3 +-- mypy/types.py | 15 +++++++++++++-- mypy/util.py | 3 +-- mypyc/irbuild/builder.py | 3 +-- 12 files changed, 42 insertions(+), 31 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index a1a9206367af..342331243b96 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -26,8 +26,18 @@ import time import types from collections.abc import Iterator, Mapping, Sequence, Set as AbstractSet -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, NamedTuple, NoReturn, TextIO -from typing_extensions import TypeAlias as _TypeAlias, TypedDict +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Final, + NamedTuple, + NoReturn, + TextIO, + TypedDict, +) +from typing_extensions import TypeAlias as _TypeAlias import mypy.semanal_main from mypy.checker import TypeChecker diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 2ba60744635f..b6618109bb44 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -8,8 +8,8 @@ from collections import defaultdict from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, ClassVar, Final, Optional, cast -from typing_extensions import TypeAlias as _TypeAlias, assert_never, overload +from typing import Callable, ClassVar, Final, Optional, cast, overload +from typing_extensions import TypeAlias as _TypeAlias, assert_never import mypy.checker import mypy.errorcodes as codes diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 2ffe033b1e08..6985fd567402 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -4,8 +4,7 @@ import sys import warnings from collections.abc import Sequence -from typing import Any, Callable, Final, Optional, TypeVar, Union, cast -from typing_extensions import Literal, overload +from typing import Any, Callable, Final, Literal, Optional, TypeVar, Union, cast, overload from mypy import defaults, errorcodes as codes, message_registry from mypy.errors import Errors diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index e7eed030ce1f..0c29d992c22e 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -5,8 +5,7 @@ from collections import defaultdict from collections.abc import Iterable, Mapping from functools import reduce -from typing import Final, cast -from typing_extensions import Literal +from typing import Final, Literal, cast import mypy.plugin # To avoid circular imports. from mypy.applytype import apply_generic_arguments diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 40af5ce81d9e..bdd01ef6a6f3 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -3,8 +3,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Callable, Final, overload -from typing_extensions import Literal, Protocol +from typing import Callable, Final, Literal, Protocol, overload from mypy_extensions import trait diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 21e8736ff6a7..5d19c4777916 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -29,7 +29,7 @@ from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path -from typing import Any, Generic, TypeVar, Union +from typing import Any, Final, Generic, TypeVar, Union from typing_extensions import get_origin, is_typeddict import mypy.build @@ -52,7 +52,7 @@ def __repr__(self) -> str: return "MISSING" -MISSING: typing_extensions.Final = Missing() +MISSING: Final = Missing() T = TypeVar("T") MaybeMissing: typing_extensions.TypeAlias = Union[T, Missing] @@ -65,10 +65,10 @@ def __repr__(self) -> str: return "" -UNREPRESENTABLE: typing_extensions.Final = Unrepresentable() +UNREPRESENTABLE: Final = Unrepresentable() -_formatter: typing_extensions.Final = FancyFormatter(sys.stdout, sys.stderr, False) +_formatter: Final = FancyFormatter(sys.stdout, sys.stderr, False) def _style(message: str, **kwargs: Any) -> str: @@ -1447,7 +1447,7 @@ def verify_typealias( # ==================== -IGNORED_MODULE_DUNDERS: typing_extensions.Final = frozenset( +IGNORED_MODULE_DUNDERS: Final = frozenset( { "__file__", "__doc__", @@ -1469,7 +1469,7 @@ def verify_typealias( } ) -IGNORABLE_CLASS_DUNDERS: typing_extensions.Final = frozenset( +IGNORABLE_CLASS_DUNDERS: Final = frozenset( { # Special attributes "__dict__", @@ -1915,9 +1915,7 @@ class _Arguments: # typeshed added a stub for __main__, but that causes stubtest to check itself -ANNOYING_STDLIB_MODULES: typing_extensions.Final = frozenset( - {"antigravity", "this", "__main__", "_ios_support"} -) +ANNOYING_STDLIB_MODULES: Final = frozenset({"antigravity", "this", "__main__", "_ios_support"}) def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 34808be8a8e4..cbb3d2f77414 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -10,8 +10,7 @@ from collections import defaultdict from collections.abc import Iterable, Iterator, Mapping from contextlib import contextmanager -from typing import Final -from typing_extensions import overload +from typing import Final, overload from mypy_extensions import mypyc_attr diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 193733ecce47..36dc7e8e2acd 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -29,8 +29,7 @@ import os from collections.abc import Iterator from contextlib import contextmanager -from typing import Callable, NamedTuple, TypeVar, cast -from typing_extensions import TypedDict +from typing import Callable, NamedTuple, TypedDict, TypeVar, cast from mypy.argmap import map_actuals_to_formals from mypy.build import Graph, State diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 6e2106875e1a..7de987a83a2b 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -5,8 +5,7 @@ import itertools from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, Final, TypeVar -from typing_extensions import Protocol +from typing import Callable, Final, Protocol, TypeVar from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode diff --git a/mypy/types.py b/mypy/types.py index 164e18be032e..f3745695889f 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -5,8 +5,19 @@ import sys from abc import abstractmethod from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Any, ClassVar, Final, NamedTuple, NewType, TypeVar, Union, cast -from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard, overload +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Final, + NamedTuple, + NewType, + TypeVar, + Union, + cast, + overload, +) +from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard import mypy.nodes from mypy.bogus_type import Bogus diff --git a/mypy/util.py b/mypy/util.py index 797498e29e9e..f79d7113ca91 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -12,8 +12,7 @@ import time from collections.abc import Container, Iterable, Sequence, Sized from importlib import resources as importlib_resources -from typing import IO, Any, Callable, Final, TypeVar -from typing_extensions import Literal +from typing import IO, Any, Callable, Final, Literal, TypeVar orjson: Any try: diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 983bd6845207..b0597617bdc5 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -7,8 +7,7 @@ from collections.abc import Iterator, Sequence from contextlib import contextmanager -from typing import Any, Callable, Final, Union -from typing_extensions import overload +from typing import Any, Callable, Final, Union, overload from mypy.build import Graph from mypy.maptype import map_instance_to_supertype From 60bff6c057831271b455a90c1a7f03f500582d34 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:30:27 +0100 Subject: [PATCH 1010/1617] Use Generator TypeVar defaults (#18384) `collections.abc.Generator` doesn't check the number of TypeVars (in contrast to `typing.Generator`). So it's possible to use `Generator[None]` even for Python 3.9. --- mypyc/irbuild/match.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index 04a6cff9779c..0daf1d609581 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -332,7 +332,7 @@ def bind_as_pattern(self, value: Value, new_block: bool = False) -> None: self.builder.goto(self.code_block) @contextmanager - def enter_subpattern(self, subject: Value) -> Generator[None, None, None]: + def enter_subpattern(self, subject: Value) -> Generator[None]: old_subject = self.subject self.subject = subject yield From e139a0d26c455060e5dde9ffdcc79a4cefd25abe Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 05:53:27 -0800 Subject: [PATCH 1011/1617] Fix enum truthiness for StrEnum (#18379) Fixes #18376 See also https://snarky.ca/unravelling-not-in-python/ --- mypy/typeops.py | 12 ++--- test-data/unit/check-enum.test | 93 ++++++++++++++++++++++++++++---- test-data/unit/fixtures/enum.pyi | 4 +- 3 files changed, 91 insertions(+), 18 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 7c7e2b8bf8e5..4a269f725cef 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -648,9 +648,7 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ return items -def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: - t = get_proper_type(t) - +def _get_type_method_ret_type(t: ProperType, *, name: str) -> Type | None: # For Enum literals the ret_type can change based on the Enum # we need to check the type of the enum rather than the literal if isinstance(t, LiteralType) and t.is_enum_literal(): @@ -658,9 +656,6 @@ def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: if isinstance(t, Instance): sym = t.type.get(name) - # Fallback to the metaclass for the lookup when necessary - if not sym and (m := t.type.metaclass_type): - sym = m.type.get(name) if sym: sym_type = get_proper_type(sym.type) if isinstance(sym_type, CallableType): @@ -733,7 +728,10 @@ def false_only(t: Type) -> ProperType: if ret_type: if not ret_type.can_be_false: return UninhabitedType(line=t.line) - elif isinstance(t, Instance) and t.type.is_final: + elif isinstance(t, Instance): + if t.type.is_final or t.type.is_enum: + return UninhabitedType(line=t.line) + elif isinstance(t, LiteralType) and t.is_enum_literal(): return UninhabitedType(line=t.line) new_t = copy_type(t) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index b67bb566224e..37c63f43179d 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -181,27 +181,100 @@ def infer_truth(truth: Truth) -> None: [case testEnumTruthyness] # mypy: warn-unreachable import enum +from typing_extensions import Literal + class E(enum.Enum): - x = 0 -if not E.x: - "noop" + zero = 0 + one = 1 + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") +if not E.zero: + print("zero is false") # E: Statement is unreachable + +if E.one: + print("one is true") +if not E.one: + print("one is false") # E: Statement is unreachable + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") + if not zero: + print("zero is false") # E: Statement is unreachable + if one: + print("one is true") + if not one: + print("one is false") # E: Statement is unreachable [builtins fixtures/tuple.pyi] -[out] -main:6: error: Statement is unreachable [case testEnumTruthynessCustomDunderBool] # mypy: warn-unreachable import enum from typing_extensions import Literal + class E(enum.Enum): - x = 0 + zero = 0 + one = 1 def __bool__(self) -> Literal[False]: return False -if E.x: - "noop" + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") # E: Statement is unreachable +if not E.zero: + print("zero is false") + +if E.one: + print("one is true") # E: Statement is unreachable +if not E.one: + print("one is false") + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") # E: Statement is unreachable + if not zero: + print("zero is false") + if one: + print("one is true") # E: Statement is unreachable + if not one: + print("one is false") +[builtins fixtures/enum.pyi] + +[case testEnumTruthynessStrEnum] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal + +class E(enum.StrEnum): + empty = "" + not_empty = "asdf" + +def print(s: str) -> None: ... + +if E.empty: + print("empty is true") +if not E.empty: + print("empty is false") + +if E.not_empty: + print("not_empty is true") +if not E.not_empty: + print("not_empty is false") + +def main(empty: Literal[E.empty], not_empty: Literal[E.not_empty]) -> None: + if empty: + print("empty is true") + if not empty: + print("empty is false") + if not_empty: + print("not_empty is true") + if not not_empty: + print("not_empty is false") [builtins fixtures/enum.pyi] -[out] -main:9: error: Statement is unreachable [case testEnumUnique] import enum diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi index debffacf8f32..135e9cd16e7c 100644 --- a/test-data/unit/fixtures/enum.pyi +++ b/test-data/unit/fixtures/enum.pyi @@ -11,6 +11,8 @@ class tuple(Generic[T]): def __getitem__(self, x: int) -> T: pass class int: pass -class str: pass +class str: + def __len__(self) -> int: pass + class dict: pass class ellipsis: pass From 55d4c1725bae29ad5ac2ce857b4b4b3363e5518c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:00:39 +0100 Subject: [PATCH 1012/1617] Revert "Remove redundant inheritances from Iterator in builtins" (#18324) Revert https://github.com/python/typeshed/pull/12851 Ref: https://github.com/python/mypy/issues/18320 --- ...redundant-inheritances-from-Iterator.patch | 324 ++++++++++++++++++ mypy/typeshed/stdlib/_asyncio.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 10 +- mypy/typeshed/stdlib/csv.pyi | 4 +- mypy/typeshed/stdlib/fileinput.pyi | 6 +- mypy/typeshed/stdlib/itertools.pyi | 38 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- test-data/unit/pythoneval.test | 10 + 9 files changed, 368 insertions(+), 34 deletions(-) create mode 100644 misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch new file mode 100644 index 000000000000..b23461b447a1 --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch @@ -0,0 +1,324 @@ +From 25250cbe1f7ee0e924ac03b3f19297e1885dd13e Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sat, 21 Dec 2024 22:36:38 +0100 +Subject: [PATCH] Revert Remove redundant inheritances from Iterator in + builtins + +--- + mypy/typeshed/stdlib/_asyncio.pyi | 4 +- + mypy/typeshed/stdlib/builtins.pyi | 10 ++--- + mypy/typeshed/stdlib/csv.pyi | 4 +- + mypy/typeshed/stdlib/fileinput.pyi | 6 +-- + mypy/typeshed/stdlib/itertools.pyi | 38 +++++++++---------- + mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- + mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- + 7 files changed, 34 insertions(+), 34 deletions(-) + +diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi +index a25902661..18920cd8a 100644 +--- a/mypy/typeshed/stdlib/_asyncio.pyi ++++ b/mypy/typeshed/stdlib/_asyncio.pyi +@@ -1,6 +1,6 @@ + import sys + from asyncio.events import AbstractEventLoop +-from collections.abc import Awaitable, Callable, Coroutine, Generator ++from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable + from contextvars import Context + from types import FrameType + from typing import Any, Literal, TextIO, TypeVar +@@ -13,7 +13,7 @@ _T = TypeVar("_T") + _T_co = TypeVar("_T_co", covariant=True) + _TaskYieldType: TypeAlias = Future[object] | None + +-class Future(Awaitable[_T]): ++class Future(Awaitable[_T], Iterable[_T]): + _state: str + @property + def _exception(self) -> BaseException | None: ... +diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi +index 5c6d321f7..56a5969d1 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class enumerate(Generic[_T]): ++class enumerate(Iterator[tuple[int, _T]]): + def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[int, _T]: ... +@@ -1324,7 +1324,7 @@ else: + + exit: _sitebuiltins.Quitter + +-class filter(Generic[_T]): ++class filter(Iterator[_T]): + @overload + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... + @overload +@@ -1389,7 +1389,7 @@ license: _sitebuiltins._Printer + + def locals() -> dict[str, Any]: ... + +-class map(Generic[_S]): ++class map(Iterator[_S]): + @overload + def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... + @overload +@@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex + + quit: _sitebuiltins.Quitter + +-class reversed(Generic[_T]): ++class reversed(Iterator[_T]): + @overload + def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] + @overload +@@ -1693,7 +1693,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... + @overload + def vars(object: Any = ..., /) -> dict[str, Any]: ... + +-class zip(Generic[_T_co]): ++class zip(Iterator[_T_co]): + if sys.version_info >= (3, 10): + @overload + def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... +diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi +index 4a82de638..ef93129d6 100644 +--- a/mypy/typeshed/stdlib/csv.pyi ++++ b/mypy/typeshed/stdlib/csv.pyi +@@ -25,7 +25,7 @@ else: + from _csv import _reader as Reader, _writer as Writer + + from _typeshed import SupportsWrite +-from collections.abc import Collection, Iterable, Mapping, Sequence ++from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence + from typing import Any, Generic, Literal, TypeVar, overload + from typing_extensions import Self + +@@ -75,7 +75,7 @@ class excel(Dialect): ... + class excel_tab(excel): ... + class unix_dialect(Dialect): ... + +-class DictReader(Generic[_T]): ++class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): + fieldnames: Sequence[_T] | None + restkey: _T | None + restval: str | Any | None +diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi +index bf6daad0a..1e6aa78e2 100644 +--- a/mypy/typeshed/stdlib/fileinput.pyi ++++ b/mypy/typeshed/stdlib/fileinput.pyi +@@ -1,8 +1,8 @@ + import sys + from _typeshed import AnyStr_co, StrOrBytesPath +-from collections.abc import Callable, Iterable ++from collections.abc import Callable, Iterable, Iterator + from types import TracebackType +-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload ++from typing import IO, Any, AnyStr, Literal, Protocol, overload + from typing_extensions import Self, TypeAlias + + if sys.version_info >= (3, 9): +@@ -107,7 +107,7 @@ def fileno() -> int: ... + def isfirstline() -> bool: ... + def isstdin() -> bool: ... + +-class FileInput(Generic[AnyStr]): ++class FileInput(Iterator[AnyStr]): + if sys.version_info >= (3, 10): + # encoding and errors are added + @overload +diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi +index 013c3cba1..f69665882 100644 +--- a/mypy/typeshed/stdlib/itertools.pyi ++++ b/mypy/typeshed/stdlib/itertools.pyi +@@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] + + # Technically count can take anything that implements a number protocol and has an add method + # but we can't enforce the add method +-class count(Generic[_N]): ++class count(Iterator[_N]): + @overload + def __new__(cls) -> count[int]: ... + @overload +@@ -39,12 +39,12 @@ class count(Generic[_N]): + def __next__(self) -> _N: ... + def __iter__(self) -> Self: ... + +-class cycle(Generic[_T]): ++class cycle(Iterator[_T]): + def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... + +-class repeat(Generic[_T]): ++class repeat(Iterator[_T]): + @overload + def __init__(self, object: _T) -> None: ... + @overload +@@ -53,7 +53,7 @@ class repeat(Generic[_T]): + def __iter__(self) -> Self: ... + def __length_hint__(self) -> int: ... + +-class accumulate(Generic[_T]): ++class accumulate(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + @overload +@@ -61,7 +61,7 @@ class accumulate(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class chain(Generic[_T]): ++class chain(Iterator[_T]): + def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... +@@ -71,22 +71,22 @@ class chain(Generic[_T]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class compress(Generic[_T]): ++class compress(Iterator[_T]): + def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class dropwhile(Generic[_T]): ++class dropwhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class filterfalse(Generic[_T]): ++class filterfalse(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class groupby(Generic[_T_co, _S_co]): ++class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): + @overload + def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... + @overload +@@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... + +-class islice(Generic[_T]): ++class islice(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + @overload +@@ -102,19 +102,19 @@ class islice(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class starmap(Generic[_T_co]): ++class starmap(Iterator[_T_co]): + def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class takewhile(Generic[_T]): ++class takewhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... + +-class zip_longest(Generic[_T_co]): ++class zip_longest(Iterator[_T_co]): + # one iterable (fillvalue doesn't matter) + @overload + def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... +@@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class product(Generic[_T_co]): ++class product(Iterator[_T_co]): + @overload + def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... + @overload +@@ -277,7 +277,7 @@ class product(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class permutations(Generic[_T_co]): ++class permutations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... + @overload +@@ -291,7 +291,7 @@ class permutations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations(Generic[_T_co]): ++class combinations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... + @overload +@@ -305,7 +305,7 @@ class combinations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations_with_replacement(Generic[_T_co]): ++class combinations_with_replacement(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... + @overload +@@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 10): +- class pairwise(Generic[_T_co]): ++ class pairwise(Iterator[_T_co]): + def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 12): +- class batched(Generic[_T_co]): ++ class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): + if sys.version_info >= (3, 13): + def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... + else: +diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +index 61d6d0781..950ed1d8c 100644 +--- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi ++++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +@@ -1,5 +1,5 @@ + import sys +-from collections.abc import Callable, Iterable, Mapping ++from collections.abc import Callable, Iterable, Iterator, Mapping + from types import TracebackType + from typing import Any, Final, Generic, TypeVar + from typing_extensions import Self +@@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): + error_callback: Callable[[BaseException], object] | None, + ) -> None: ... + +-class IMapIterator(Generic[_T]): ++class IMapIterator(Iterator[_T]): + def __init__(self, pool: Pool) -> None: ... + def __iter__(self) -> Self: ... + def next(self, timeout: float | None = None) -> _T: ... +diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +index bc0ff6469..730404bde 100644 +--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi ++++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +@@ -397,7 +397,7 @@ class Connection: + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / + ) -> Literal[False]: ... + +-class Cursor: ++class Cursor(Iterator[Any]): + arraysize: int + @property + def connection(self) -> Connection: ... +-- +2.47.1 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi index a259026615aa..18920cd8a8a4 100644 --- a/mypy/typeshed/stdlib/_asyncio.pyi +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -1,6 +1,6 @@ import sys from asyncio.events import AbstractEventLoop -from collections.abc import Awaitable, Callable, Coroutine, Generator +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable from contextvars import Context from types import FrameType from typing import Any, Literal, TextIO, TypeVar @@ -13,7 +13,7 @@ _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _TaskYieldType: TypeAlias = Future[object] | None -class Future(Awaitable[_T]): +class Future(Awaitable[_T], Iterable[_T]): _state: str @property def _exception(self) -> BaseException | None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 5c6d321f772e..56a5969d102a 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class enumerate(Generic[_T]): +class enumerate(Iterator[tuple[int, _T]]): def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... @@ -1324,7 +1324,7 @@ else: exit: _sitebuiltins.Quitter -class filter(Generic[_T]): +class filter(Iterator[_T]): @overload def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... @overload @@ -1389,7 +1389,7 @@ license: _sitebuiltins._Printer def locals() -> dict[str, Any]: ... -class map(Generic[_S]): +class map(Iterator[_S]): @overload def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... @overload @@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex quit: _sitebuiltins.Quitter -class reversed(Generic[_T]): +class reversed(Iterator[_T]): @overload def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] @overload @@ -1693,7 +1693,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... -class zip(Generic[_T_co]): +class zip(Iterator[_T_co]): if sys.version_info >= (3, 10): @overload def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 4a82de638136..ef93129d6546 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -25,7 +25,7 @@ else: from _csv import _reader as Reader, _writer as Writer from _typeshed import SupportsWrite -from collections.abc import Collection, Iterable, Mapping, Sequence +from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence from typing import Any, Generic, Literal, TypeVar, overload from typing_extensions import Self @@ -75,7 +75,7 @@ class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... -class DictReader(Generic[_T]): +class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index bf6daad0aea7..1e6aa78e2607 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath -from collections.abc import Callable, Iterable +from collections.abc import Callable, Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload +from typing import IO, Any, AnyStr, Literal, Protocol, overload from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -107,7 +107,7 @@ def fileno() -> int: ... def isfirstline() -> bool: ... def isstdin() -> bool: ... -class FileInput(Generic[AnyStr]): +class FileInput(Iterator[AnyStr]): if sys.version_info >= (3, 10): # encoding and errors are added @overload diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 013c3cba120f..f69665882498 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] # Technically count can take anything that implements a number protocol and has an add method # but we can't enforce the add method -class count(Generic[_N]): +class count(Iterator[_N]): @overload def __new__(cls) -> count[int]: ... @overload @@ -39,12 +39,12 @@ class count(Generic[_N]): def __next__(self) -> _N: ... def __iter__(self) -> Self: ... -class cycle(Generic[_T]): +class cycle(Iterator[_T]): def __init__(self, iterable: Iterable[_T], /) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... -class repeat(Generic[_T]): +class repeat(Iterator[_T]): @overload def __init__(self, object: _T) -> None: ... @overload @@ -53,7 +53,7 @@ class repeat(Generic[_T]): def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... -class accumulate(Generic[_T]): +class accumulate(Iterator[_T]): @overload def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... @overload @@ -61,7 +61,7 @@ class accumulate(Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class chain(Generic[_T]): +class chain(Iterator[_T]): def __init__(self, *iterables: Iterable[_T]) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -71,22 +71,22 @@ class chain(Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class compress(Generic[_T]): +class compress(Iterator[_T]): def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class dropwhile(Generic[_T]): +class dropwhile(Iterator[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class filterfalse(Generic[_T]): +class filterfalse(Iterator[_T]): def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class groupby(Generic[_T_co, _S_co]): +class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): @overload def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... @overload @@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... -class islice(Generic[_T]): +class islice(Iterator[_T]): @overload def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... @overload @@ -102,19 +102,19 @@ class islice(Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class starmap(Generic[_T_co]): +class starmap(Iterator[_T_co]): def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class takewhile(Generic[_T]): +class takewhile(Iterator[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... -class zip_longest(Generic[_T_co]): +class zip_longest(Iterator[_T_co]): # one iterable (fillvalue doesn't matter) @overload def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... @@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class product(Generic[_T_co]): +class product(Iterator[_T_co]): @overload def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... @overload @@ -277,7 +277,7 @@ class product(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class permutations(Generic[_T_co]): +class permutations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... @overload @@ -291,7 +291,7 @@ class permutations(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations(Generic[_T_co]): +class combinations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... @overload @@ -305,7 +305,7 @@ class combinations(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations_with_replacement(Generic[_T_co]): +class combinations_with_replacement(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... @overload @@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]): def __next__(self) -> _T_co: ... if sys.version_info >= (3, 10): - class pairwise(Generic[_T_co]): + class pairwise(Iterator[_T_co]): def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... if sys.version_info >= (3, 12): - class batched(Generic[_T_co]): + class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): if sys.version_info >= (3, 13): def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... else: diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 61d6d0781213..950ed1d8c56b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,5 +1,5 @@ import sys -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType from typing import Any, Final, Generic, TypeVar from typing_extensions import Self @@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): error_callback: Callable[[BaseException], object] | None, ) -> None: ... -class IMapIterator(Generic[_T]): +class IMapIterator(Iterator[_T]): def __init__(self, pool: Pool) -> None: ... def __iter__(self) -> Self: ... def next(self, timeout: float | None = None) -> _T: ... diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index bc0ff6469d5e..730404bde218 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -397,7 +397,7 @@ class Connection: self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / ) -> Literal[False]: ... -class Cursor: +class Cursor(Iterator[Any]): arraysize: int @property def connection(self) -> Connection: ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 66ceafb91370..08e99edba5c4 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2181,3 +2181,13 @@ class Status(Enum): def imperfect(status: Status) -> str: return status.name.lower() + +[case testUnpackIteratorBuiltins] +# Regression test for https://github.com/python/mypy/issues/18320 +# Caused by https://github.com/python/typeshed/pull/12851 +x = [1, 2] +reveal_type([*reversed(x)]) +reveal_type([*map(str, x)]) +[out] +_testUnpackIteratorBuiltins.py:4: note: Revealed type is "builtins.list[builtins.int]" +_testUnpackIteratorBuiltins.py:5: note: Revealed type is "builtins.list[builtins.str]" From b2b32e745799fa8b082cd3c50a6eb649321e0927 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:52:28 -0800 Subject: [PATCH 1013/1617] Allow inverting --local-partial-types (#18377) Also add it to a bunch of test cases where it is needed --- mypy/main.py | 2 +- test-data/unit/check-bound.test | 4 ++-- test-data/unit/check-classes.test | 2 +- test-data/unit/check-columns.test | 1 + test-data/unit/check-custom-plugin.test | 2 +- test-data/unit/check-errorcodes.test | 2 +- test-data/unit/check-incremental.test | 1 + test-data/unit/check-inference.test | 20 +++++++++++--------- test-data/unit/check-narrowing.test | 1 + test-data/unit/check-optional.test | 7 +++++-- test-data/unit/check-protocols.test | 4 +++- test-data/unit/deps.test | 2 ++ 12 files changed, 30 insertions(+), 18 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 211d6952c2ac..c657f09e2600 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1185,7 +1185,7 @@ def add_invertible_flag( parser.add_argument("--test-env", action="store_true", help=argparse.SUPPRESS) # --local-partial-types disallows partial types spanning module top level and a function # (implicitly defined in fine-grained incremental mode) - parser.add_argument("--local-partial-types", action="store_true", help=argparse.SUPPRESS) + add_invertible_flag("--local-partial-types", default=False, help=argparse.SUPPRESS) # --logical-deps adds some more dependencies that are not semantically needed, but # may be helpful to determine relative importance of classes and functions for overall # type precision in a code base. It also _removes_ some deps, so this flag should be never diff --git a/test-data/unit/check-bound.test b/test-data/unit/check-bound.test index 1c713fd77c38..1f9eba612020 100644 --- a/test-data/unit/check-bound.test +++ b/test-data/unit/check-bound.test @@ -46,7 +46,7 @@ z = G(B()) [case testBoundVoid] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import TypeVar, Generic T = TypeVar('T', bound=int) class C(Generic[T]): @@ -75,7 +75,7 @@ z: C [case testBoundHigherOrderWithVoid] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import TypeVar, Callable class A: pass T = TypeVar('T', bound=A) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 5ccb9fa06c34..618b2c7a40c9 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -113,7 +113,7 @@ A().f = None # E: Cannot assign to a method \ from typing import Protocol class Base: - __hash__ = None + __hash__: None = None class Derived(Base): def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" \ diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 8bb768cfe13b..940e0846c959 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -210,6 +210,7 @@ y: Dict[int, int] = { [builtins fixtures/dict.pyi] [case testColumnCannotDetermineType] +# flags: --no-local-partial-types (x) # E:2: Cannot determine type of "x" # E:2: Name "x" is used before definition x = None diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 1e06f300570e..01facb63c6a6 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1007,7 +1007,7 @@ reveal_type(Cls.attr) # N: Revealed type is "builtins.int" plugins=/test-data/unit/plugins/class_attr_hook.py [case testClassAttrPluginPartialType] -# flags: --config-file tmp/mypy.ini +# flags: --config-file tmp/mypy.ini --no-local-partial-types class Cls: attr = None diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index a5a22cb6cabd..294038664415 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -837,7 +837,7 @@ Foo = TypedDict("Bar", {}) # E: First argument "Bar" to TypedDict() does not ma [builtins fixtures/dict.pyi] [case testTruthyBool] -# flags: --enable-error-code truthy-bool +# flags: --enable-error-code truthy-bool --no-local-partial-types from typing import List, Union, Any class Foo: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 55360f15f5c5..77170280ecae 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6317,6 +6317,7 @@ class C: ... [out3] [case testNoCrashOnPartialLambdaInference] +# flags: --no-local-partial-types import m [file m.py] from typing import TypeVar, Callable diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 560092ed1a43..0da1c092efe8 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1728,12 +1728,14 @@ b[{}] = 1 [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyAndUpdatedFromMethod] +# flags: --no-local-partial-types map = {} def add() -> None: map[1] = 2 [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyAndUpdatedFromMethodUnannotated] +# flags: --no-local-partial-types map = {} def add(): map[1] = 2 @@ -1921,6 +1923,7 @@ reveal_type(C().a) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] [case testInferAttributeInitializedToNoneAndAssignedClassBody] +# flags: --no-local-partial-types class C: a = None def __init__(self) -> None: @@ -2069,6 +2072,7 @@ x = 1 [out] [case testPartiallyInitializedVariableDoesNotEscapeScope2] +# flags: --no-local-partial-types x = None def f() -> None: x = None @@ -2114,36 +2118,32 @@ class C: -- ------------------------ [case testPartialTypeErrorSpecialCase1] +# flags: --no-local-partial-types # This used to crash. class A: x = None def f(self) -> None: - for a in self.x: + for a in self.x: # E: "None" has no attribute "__iter__" (not iterable) pass [builtins fixtures/for.pyi] -[out] -main:5: error: "None" has no attribute "__iter__" (not iterable) [case testPartialTypeErrorSpecialCase2] # This used to crash. class A: - x = [] + x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") def f(self) -> None: for a in self.x: pass [builtins fixtures/for.pyi] -[out] -main:3: error: Need type annotation for "x" (hint: "x: List[] = ...") [case testPartialTypeErrorSpecialCase3] +# flags: --no-local-partial-types class A: x = None def f(self) -> None: - for a in A.x: + for a in A.x: # E: "None" has no attribute "__iter__" (not iterable) pass [builtins fixtures/for.pyi] -[out] -main:4: error: "None" has no attribute "__iter__" (not iterable) [case testPartialTypeErrorSpecialCase4] # This used to crash. @@ -2492,6 +2492,7 @@ main:4: error: Unsupported target for indexed assignment ("Type[C[T]]") main:4: error: Invalid type: try using Literal[0] instead? [case testNoCrashOnPartialMember] +# flags: --no-local-partial-types class C: x = None def __init__(self) -> None: @@ -2512,6 +2513,7 @@ reveal_type(x) # N: Revealed type is "builtins.str" [out] [case testNoCrashOnPartialVariable2] +# flags: --no-local-partial-types from typing import Tuple, TypeVar T = TypeVar('T', bound=str) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index ad59af01010c..ac6c6436ba8d 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2354,6 +2354,7 @@ def fn_while(arg: T) -> None: [builtins fixtures/primitives.pyi] [case testRefinePartialTypeWithinLoop] +# flags: --no-local-partial-types x = None for _ in range(2): diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 683ce0446915..c14b6ae376ae 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -321,10 +321,13 @@ def f() -> Generator[None, None, None]: [out] [case testNoneAndStringIsNone] -a = None +a: None = None b = "foo" reveal_type(a and b) # N: Revealed type is "None" +c = None +reveal_type(c and b) # N: Revealed type is "None" + [case testNoneMatchesObjectInOverload] import a a.f(None) @@ -581,7 +584,7 @@ x is not None and x + '42' # E: Unsupported operand types for + ("int" and "str [case testInvalidBooleanBranchIgnored] from typing import Optional -x = None +x: None = None x is not None and x + 42 [builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 0571c1729302..ed8edea5f0d5 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2906,6 +2906,7 @@ hs(None) [case testPartialTypeProtocol] +# flags: --no-local-partial-types from typing import Protocol class Flapper(Protocol): @@ -2944,7 +2945,7 @@ class DataArray(ObjectHashable): [case testPartialAttributeNoneType] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import Optional, Protocol, runtime_checkable @runtime_checkable @@ -2962,6 +2963,7 @@ class MyClass: [case testPartialAttributeNoneTypeStrictOptional] +# flags: --no-local-partial-types from typing import Optional, Protocol, runtime_checkable @runtime_checkable diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 757bd9541fc9..1aa025579535 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -597,6 +597,7 @@ class C: -> m.C.__init__ [case testPartialNoneTypeAttributeCrash1] +# flags: --no-local-partial-types class C: pass class A: @@ -612,6 +613,7 @@ class A: -> , m.A.f, m.C [case testPartialNoneTypeAttributeCrash2] +# flags: --no-local-partial-types class C: pass class A: From 485b1209a330e0553f3ebb775877995e3f715857 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:53:27 -0800 Subject: [PATCH 1014/1617] Enable local_partial_types on mypy (#18370) --- mypy_self_check.ini | 1 + test-data/unit/plugins/dyn_class.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy_self_check.ini b/mypy_self_check.ini index d4c0e8445f48..f54c1f17f025 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -1,6 +1,7 @@ [mypy] strict = True +local_partial_types = True disallow_any_unimported = True show_traceback = True pretty = True diff --git a/test-data/unit/plugins/dyn_class.py b/test-data/unit/plugins/dyn_class.py index 18e948e3dd2a..1471267b24ee 100644 --- a/test-data/unit/plugins/dyn_class.py +++ b/test-data/unit/plugins/dyn_class.py @@ -6,7 +6,7 @@ from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin from mypy.types import Instance, get_proper_type -DECL_BASES = set() +DECL_BASES: set[str] = set() class DynPlugin(Plugin): From 69ca89c0892ac54f8c8014a68bb65159b2049847 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:53:56 -0800 Subject: [PATCH 1015/1617] Remove stubs no longer in typeshed (#18373) See https://github.com/python/mypy/pull/18367 for script See https://github.com/python/mypy/pull/18366 for additions --- mypy/stubinfo.py | 21 --------------------- mypy/test/teststubinfo.py | 4 ++-- test-data/unit/pythoneval.test | 12 ++++++------ 3 files changed, 8 insertions(+), 29 deletions(-) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 56d66e00f0bf..77426bb09b7b 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -34,19 +34,15 @@ def stub_distribution_name(module: str) -> str | None: legacy_bundled_packages: dict[str, str] = { "aiofiles": "types-aiofiles", "bleach": "types-bleach", - "boto": "types-boto", "cachetools": "types-cachetools", "click_spinner": "types-click-spinner", - "contextvars": "types-contextvars", "croniter": "types-croniter", - "dataclasses": "types-dataclasses", "dateparser": "types-dateparser", "dateutil": "types-python-dateutil", "decorator": "types-decorator", "deprecated": "types-Deprecated", "docutils": "types-docutils", "first": "types-first", - "gflags": "types-python-gflags", "markdown": "types-Markdown", "mock": "types-mock", "OpenSSL": "types-pyOpenSSL", @@ -56,20 +52,14 @@ def stub_distribution_name(module: str) -> str | None: "pycurl": "types-pycurl", "pymysql": "types-PyMySQL", "pyrfc3339": "types-pyRFC3339", - "python2": "types-six", "pytz": "types-pytz", - "pyVmomi": "types-pyvmomi", - "redis": "types-redis", "requests": "types-requests", "retry": "types-retry", "simplejson": "types-simplejson", "singledispatch": "types-singledispatch", "six": "types-six", - "slugify": "types-python-slugify", "tabulate": "types-tabulate", "toml": "types-toml", - "typed_ast": "types-typed-ast", - "tzlocal": "types-tzlocal", "ujson": "types-ujson", "waitress": "types-waitress", "yaml": "types-PyYAML", @@ -92,7 +82,6 @@ def stub_distribution_name(module: str) -> str | None: "atheris": "types-atheris", "authlib": "types-Authlib", "aws_xray_sdk": "types-aws-xray-sdk", - "babel": "types-babel", "boltons": "types-boltons", "braintree": "types-braintree", "bs4": "types-beautifulsoup4", @@ -112,23 +101,19 @@ def stub_distribution_name(module: str) -> str | None: "cronlog": "types-python-crontab", "crontab": "types-python-crontab", "crontabs": "types-python-crontab", - "d3dshot": "types-D3DShot", "datemath": "types-python-datemath", "dateparser_data": "types-dateparser", "dde": "types-pywin32", "defusedxml": "types-defusedxml", "docker": "types-docker", "dockerfile_parse": "types-dockerfile-parse", - "docopt": "types-docopt", "editdistance": "types-editdistance", "entrypoints": "types-entrypoints", "exifread": "types-ExifRead", "fanstatic": "types-fanstatic", "farmhash": "types-pyfarmhash", - "flake8_2020": "types-flake8-2020", "flake8_builtins": "types-flake8-builtins", "flake8_docstrings": "types-flake8-docstrings", - "flake8_plugin_utils": "types-flake8-plugin-utils", "flake8_rst_docstrings": "types-flake8-rst-docstrings", "flake8_simplify": "types-flake8-simplify", "flake8_typing_imports": "types-flake8-typing-imports", @@ -150,7 +135,6 @@ def stub_distribution_name(module: str) -> str | None: "import_export": "types-django-import-export", "influxdb_client": "types-influxdb-client", "inifile": "types-inifile", - "invoke": "types-invoke", "isapi": "types-pywin32", "jack": "types-JACK-Client", "jenkins": "types-python-jenkins", @@ -189,9 +173,7 @@ def stub_distribution_name(module: str) -> str | None: "pep8ext_naming": "types-pep8-naming", "perfmon": "types-pywin32", "pexpect": "types-pexpect", - "PIL": "types-Pillow", "playhouse": "types-peewee", - "playsound": "types-playsound", "portpicker": "types-portpicker", "psutil": "types-psutil", "psycopg2": "types-psycopg2", @@ -230,9 +212,7 @@ def stub_distribution_name(module: str) -> str | None: "shapely": "types-shapely", "slumber": "types-slumber", "sspicon": "types-pywin32", - "stdlib_list": "types-stdlib-list", "str2bool": "types-str2bool", - "stripe": "types-stripe", "tensorflow": "types-tensorflow", "tgcrypto": "types-TgCrypto", "timer": "types-pywin32", @@ -240,7 +220,6 @@ def stub_distribution_name(module: str) -> str | None: "tqdm": "types-tqdm", "translationstring": "types-translationstring", "tree_sitter_languages": "types-tree-sitter-languages", - "tree_sitter": "types-tree-sitter", "ttkthemes": "types-ttkthemes", "unidiff": "types-unidiff", "untangle": "types-untangle", diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py index 518194d35e1d..e90c72335bf8 100644 --- a/mypy/test/teststubinfo.py +++ b/mypy/test/teststubinfo.py @@ -15,12 +15,12 @@ def test_is_legacy_bundled_packages(self) -> None: assert not is_module_from_legacy_bundled_package("foobar_asdf") assert not is_module_from_legacy_bundled_package("PIL") assert is_module_from_legacy_bundled_package("pycurl") - assert is_module_from_legacy_bundled_package("dataclasses") + assert is_module_from_legacy_bundled_package("dateparser") def test_stub_distribution_name(self) -> None: assert stub_distribution_name("foobar_asdf") is None assert stub_distribution_name("pycurl") == "types-pycurl" - assert stub_distribution_name("babel") == "types-babel" + assert stub_distribution_name("bs4") == "types-beautifulsoup4" assert stub_distribution_name("google.cloud.ndb") == "types-google-cloud-ndb" assert stub_distribution_name("google.cloud.ndb.submodule") == "types-google-cloud-ndb" assert stub_distribution_name("google.cloud.unknown") is None diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 08e99edba5c4..fa6da49df1cc 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1507,24 +1507,24 @@ note: A user-defined top-level module with name "typing" is not supported # flags: --ignore-missing-imports import scribe # No Python 3 stubs available for scribe from scribe import x -import python2 # Python 3 stubs available for python2 +import pytz # Python 3 stubs available for pytz import foobar_asdf import jack # This has a stubs package but was never bundled with mypy, so ignoring works [out] -_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "python2" -_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-six" +_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "pytz" +_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-pytz" _testIgnoreImportIfNoPython3StubAvailable.py:4: note: (or run "mypy --install-types" to install all missing stub packages) _testIgnoreImportIfNoPython3StubAvailable.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [case testNoPython3StubAvailable] import scribe from scribe import x -import python2 +import pytz [out] _testNoPython3StubAvailable.py:1: error: Cannot find implementation or library stub for module named "scribe" _testNoPython3StubAvailable.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "python2" -_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-six" +_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "pytz" +_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-pytz" _testNoPython3StubAvailable.py:3: note: (or run "mypy --install-types" to install all missing stub packages) From 1161487899094a8735eeae00c5b656d0abea9f34 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:54:21 -0800 Subject: [PATCH 1016/1617] Script to update stubinfo.py (#18367) Additions in #18366 , will do removals once merged --- misc/update-stubinfo.py | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 misc/update-stubinfo.py diff --git a/misc/update-stubinfo.py b/misc/update-stubinfo.py new file mode 100644 index 000000000000..beaed34a8a47 --- /dev/null +++ b/misc/update-stubinfo.py @@ -0,0 +1,67 @@ +import argparse +from pathlib import Path + +import tomli as tomllib + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--typeshed", type=Path, required=True) + args = parser.parse_args() + + typeshed_p_to_d = {} + for stub in (args.typeshed / "stubs").iterdir(): + if not stub.is_dir(): + continue + try: + metadata = tomllib.loads((stub / "METADATA.toml").read_text()) + except FileNotFoundError: + continue + d = metadata.get("stub_distribution", f"types-{stub.name}") + for p in stub.iterdir(): + if not p.stem.isidentifier(): + continue + if p.is_dir() and not any(f.suffix == ".pyi" for f in p.iterdir()): + # ignore namespace packages + continue + if p.is_file() and p.suffix != ".pyi": + continue + typeshed_p_to_d[p.stem] = d + + import mypy.stubinfo + + mypy_p = set(mypy.stubinfo.non_bundled_packages_flat) | set( + mypy.stubinfo.legacy_bundled_packages + ) + + for p in typeshed_p_to_d.keys() & mypy_p: + mypy_d = mypy.stubinfo.non_bundled_packages_flat.get(p) + mypy_d = mypy_d or mypy.stubinfo.legacy_bundled_packages.get(p) + if mypy_d != typeshed_p_to_d[p]: + raise ValueError( + f"stub_distribution mismatch for {p}: {mypy_d} != {typeshed_p_to_d[p]}" + ) + + print("=" * 40) + print("Add the following to non_bundled_packages_flat:") + print("=" * 40) + for p in sorted(typeshed_p_to_d.keys() - mypy_p): + if p in { + "pika", # see comment in stubinfo.py + "distutils", # don't recommend types-setuptools here + }: + continue + print(f'"{p}": "{typeshed_p_to_d[p]}",') + print() + + print("=" * 40) + print("Consider removing the following packages no longer in typeshed:") + print("=" * 40) + for p in sorted(mypy_p - typeshed_p_to_d.keys()): + if p in {"lxml", "pandas"}: # never in typeshed + continue + print(p) + + +if __name__ == "__main__": + main() From 7b4f86294b355fff96acb6cdc9cfb05de525491c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:59:17 -0800 Subject: [PATCH 1017/1617] [minor] improve getargs test (#18389) Improves test added in #18350 to confirm the args are actually parsed correctly --- mypyc/test-data/run-classes.test | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 055327e786a2..db5459e22f5e 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -470,11 +470,15 @@ assert foo() == 21 [case testClassKwargs] class X: def __init__(self, msg: str, **variables: int) -> None: - pass + self.msg = msg + self.variables = variables + [file driver.py] import traceback from native import X -X('hello', a=0) +x = X('hello', a=0, b=1) +assert x.msg == 'hello' +assert x.variables == {'a': 0, 'b': 1} try: X('hello', msg='hello') except TypeError as e: From 9bf5169ae401bd07d10f02976167f609ea14d8da Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:09:26 -0800 Subject: [PATCH 1018/1617] Fix line number for decorator issues (#18392) Fixes #18391 --- mypy/checker.py | 2 +- test-data/unit/check-functions.test | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2b078f721736..3d0f40283606 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5117,7 +5117,7 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) continue dec = self.expr_checker.accept(d) - temp = self.temp_node(sig, context=e) + temp = self.temp_node(sig, context=d) fullname = None if isinstance(d, RefExpr): fullname = d.fullname or None diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index e4b8c31e8b46..18425efb9cb0 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -916,10 +916,19 @@ f(None) # E: Too many arguments for "f" from typing import Any, Callable def dec1(f: Callable[[Any], None]) -> Callable[[], None]: pass def dec2(f: Callable[[Any, Any], None]) -> Callable[[Any], None]: pass -@dec1 # E: Argument 1 to "dec2" has incompatible type "Callable[[Any], Any]"; expected "Callable[[Any, Any], None]" -@dec2 +@dec1 +@dec2 # E: Argument 1 to "dec2" has incompatible type "Callable[[Any], Any]"; expected "Callable[[Any, Any], None]" def f(x): pass +def faulty(c: Callable[[int], None]) -> Callable[[tuple[int, int]], None]: + return lambda x: None + +@faulty # E: Argument 1 to "faulty" has incompatible type "Callable[[Tuple[int, int]], None]"; expected "Callable[[int], None]" +@faulty # E: Argument 1 to "faulty" has incompatible type "Callable[[str], None]"; expected "Callable[[int], None]" +def g(x: str) -> None: + return None +[builtins fixtures/tuple.pyi] + [case testInvalidDecorator2] from typing import Any, Callable def dec1(f: Callable[[Any, Any], None]) -> Callable[[], None]: pass From e05770d899874abbe670caa2ddc888d7f41a6116 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 1 Jan 2025 23:02:44 +0100 Subject: [PATCH 1019/1617] Sync typeshed (#18403) Source commit: https://github.com/python/typeshed/commit/b66d6cfa9917fd675356f3e04bc2dd689d8fe76d --- ...redundant-inheritances-from-Iterator.patch | 36 +- mypy/typeshed/stdlib/_asyncio.pyi | 6 +- mypy/typeshed/stdlib/_blake2.pyi | 24 +- mypy/typeshed/stdlib/_bz2.pyi | 8 +- mypy/typeshed/stdlib/_collections_abc.pyi | 3 + mypy/typeshed/stdlib/_contextvars.pyi | 8 +- mypy/typeshed/stdlib/_csv.pyi | 8 +- mypy/typeshed/stdlib/_ctypes.pyi | 14 +- mypy/typeshed/stdlib/_curses.pyi | 6 +- mypy/typeshed/stdlib/_frozen_importlib.pyi | 3 +- mypy/typeshed/stdlib/_io.pyi | 2 +- mypy/typeshed/stdlib/_json.pyi | 9 +- mypy/typeshed/stdlib/_lzma.pyi | 21 +- mypy/typeshed/stdlib/_pickle.pyi | 1 - mypy/typeshed/stdlib/_ssl.pyi | 3 +- mypy/typeshed/stdlib/_thread.pyi | 40 +- mypy/typeshed/stdlib/_tkinter.pyi | 2 +- mypy/typeshed/stdlib/_weakrefset.pyi | 3 +- mypy/typeshed/stdlib/argparse.pyi | 11 +- mypy/typeshed/stdlib/array.pyi | 23 +- mypy/typeshed/stdlib/ast.pyi | 31 +- mypy/typeshed/stdlib/asyncio/__init__.pyi | 1300 +++++++++++++++++ mypy/typeshed/stdlib/asyncio/base_events.pyi | 2 + mypy/typeshed/stdlib/asyncio/coroutines.pyi | 1 + mypy/typeshed/stdlib/asyncio/events.pyi | 1 + mypy/typeshed/stdlib/asyncio/exceptions.pyi | 1 + mypy/typeshed/stdlib/asyncio/futures.pyi | 1 + mypy/typeshed/stdlib/asyncio/locks.pyi | 1 + mypy/typeshed/stdlib/asyncio/protocols.pyi | 1 + mypy/typeshed/stdlib/asyncio/queues.pyi | 1 + mypy/typeshed/stdlib/asyncio/runners.pyi | 1 + mypy/typeshed/stdlib/asyncio/streams.pyi | 1 + mypy/typeshed/stdlib/asyncio/subprocess.pyi | 1 + mypy/typeshed/stdlib/asyncio/taskgroups.pyi | 1 + mypy/typeshed/stdlib/asyncio/tasks.pyi | 1 + mypy/typeshed/stdlib/asyncio/threads.pyi | 1 + mypy/typeshed/stdlib/asyncio/timeouts.pyi | 1 + mypy/typeshed/stdlib/asyncio/transports.pyi | 1 + mypy/typeshed/stdlib/asyncio/unix_events.pyi | 3 + .../stdlib/asyncio/windows_events.pyi | 1 + mypy/typeshed/stdlib/builtins.pyi | 16 +- mypy/typeshed/stdlib/codecs.pyi | 2 + mypy/typeshed/stdlib/collections/__init__.pyi | 4 +- mypy/typeshed/stdlib/contextlib.pyi | 43 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 71 +- mypy/typeshed/stdlib/dataclasses.pyi | 14 +- mypy/typeshed/stdlib/datetime.pyi | 2 +- mypy/typeshed/stdlib/decimal.pyi | 1 - mypy/typeshed/stdlib/email/charset.pyi | 3 +- mypy/typeshed/stdlib/email/header.pyi | 3 +- mypy/typeshed/stdlib/email/headerregistry.pyi | 2 + mypy/typeshed/stdlib/email/message.pyi | 2 +- mypy/typeshed/stdlib/fractions.pyi | 2 +- mypy/typeshed/stdlib/http/__init__.pyi | 21 +- mypy/typeshed/stdlib/http/client.pyi | 138 +- mypy/typeshed/stdlib/inspect.pyi | 10 +- mypy/typeshed/stdlib/ipaddress.pyi | 8 +- mypy/typeshed/stdlib/itertools.pyi | 24 +- mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi | 3 +- mypy/typeshed/stdlib/lib2to3/pytree.pyi | 3 +- mypy/typeshed/stdlib/mmap.pyi | 12 +- .../stdlib/multiprocessing/managers.pyi | 80 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 3 + .../stdlib/multiprocessing/synchronize.pyi | 1 + mypy/typeshed/stdlib/numbers.pyi | 3 +- mypy/typeshed/stdlib/operator.pyi | 4 +- mypy/typeshed/stdlib/optparse.pyi | 119 +- mypy/typeshed/stdlib/parser.pyi | 3 +- mypy/typeshed/stdlib/pickle.pyi | 3 +- mypy/typeshed/stdlib/plistlib.pyi | 3 +- mypy/typeshed/stdlib/sched.pyi | 5 +- mypy/typeshed/stdlib/select.pyi | 16 +- mypy/typeshed/stdlib/selectors.pyi | 6 +- mypy/typeshed/stdlib/signal.pyi | 94 +- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- mypy/typeshed/stdlib/ssl.pyi | 4 + mypy/typeshed/stdlib/threading.pyi | 20 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 25 +- mypy/typeshed/stdlib/tkinter/font.pyi | 9 +- mypy/typeshed/stdlib/traceback.pyi | 19 +- mypy/typeshed/stdlib/types.pyi | 52 +- mypy/typeshed/stdlib/typing.pyi | 90 +- mypy/typeshed/stdlib/typing_extensions.pyi | 65 +- mypy/typeshed/stdlib/unittest/mock.pyi | 4 +- mypy/typeshed/stdlib/unittest/runner.pyi | 23 +- mypy/typeshed/stdlib/unittest/suite.pyi | 2 + mypy/typeshed/stdlib/weakref.pyi | 16 +- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 3 +- mypy/typeshed/stdlib/xmlrpc/client.pyi | 7 +- mypy/typeshed/stdlib/xxlimited.pyi | 6 +- mypy/typeshed/stdlib/zoneinfo/__init__.pyi | 2 +- test-data/unit/pythoneval.test | 4 +- 92 files changed, 2187 insertions(+), 473 deletions(-) diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch index b23461b447a1..ef1d9f4d3fa3 100644 --- a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch +++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch @@ -1,4 +1,4 @@ -From 25250cbe1f7ee0e924ac03b3f19297e1885dd13e Mon Sep 17 00:00:00 2001 +From abc5225e3c69d7ae8f3388c87260fe496efaecac Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 21 Dec 2024 22:36:38 +0100 Subject: [PATCH] Revert Remove redundant inheritances from Iterator in @@ -15,7 +15,7 @@ Subject: [PATCH] Revert Remove redundant inheritances from Iterator in 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi -index a25902661..18920cd8a 100644 +index 89cdff6cc..1397e579d 100644 --- a/mypy/typeshed/stdlib/_asyncio.pyi +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -1,6 +1,6 @@ @@ -36,7 +36,7 @@ index a25902661..18920cd8a 100644 @property def _exception(self) -> BaseException | None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index 5c6d321f7..56a5969d1 100644 +index b75e34fc5..526406acc 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): @@ -64,7 +64,7 @@ index 5c6d321f7..56a5969d1 100644 -class map(Generic[_S]): +class map(Iterator[_S]): @overload - def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... + def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ... @overload @@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex @@ -131,7 +131,7 @@ index bf6daad0a..1e6aa78e2 100644 # encoding and errors are added @overload diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi -index 013c3cba1..f69665882 100644 +index 55b0814ac..675533d44 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] @@ -149,14 +149,14 @@ index 013c3cba1..f69665882 100644 -class cycle(Generic[_T]): +class cycle(Iterator[_T]): - def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, iterable: Iterable[_T], /) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... -class repeat(Generic[_T]): +class repeat(Iterator[_T]): @overload - def __init__(self, object: _T) -> None: ... + def __new__(cls, object: _T) -> Self: ... @overload @@ -53,7 +53,7 @@ class repeat(Generic[_T]): def __iter__(self) -> Self: ... @@ -165,7 +165,7 @@ index 013c3cba1..f69665882 100644 -class accumulate(Generic[_T]): +class accumulate(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ... @overload @@ -61,7 +61,7 @@ class accumulate(Generic[_T]): def __iter__(self) -> Self: ... @@ -173,7 +173,7 @@ index 013c3cba1..f69665882 100644 -class chain(Generic[_T]): +class chain(Iterator[_T]): - def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __new__(cls, *iterables: Iterable[_T]) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -71,22 +71,22 @@ class chain(Generic[_T]): @@ -182,19 +182,19 @@ index 013c3cba1..f69665882 100644 -class compress(Generic[_T]): +class compress(Iterator[_T]): - def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class dropwhile(Generic[_T]): +class dropwhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class filterfalse(Generic[_T]): +class filterfalse(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -210,7 +210,7 @@ index 013c3cba1..f69665882 100644 -class islice(Generic[_T]): +class islice(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ... @overload @@ -102,19 +102,19 @@ class islice(Generic[_T]): def __iter__(self) -> Self: ... @@ -224,7 +224,7 @@ index 013c3cba1..f69665882 100644 -class takewhile(Generic[_T]): +class takewhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -288,17 +288,17 @@ index 013c3cba1..f69665882 100644 def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... else: diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi -index 61d6d0781..950ed1d8c 100644 +index 2937d45e3..93197e5d4 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,5 +1,5 @@ import sys -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping + from multiprocessing.context import DefaultContext, Process from types import TracebackType from typing import Any, Final, Generic, TypeVar - from typing_extensions import Self -@@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): +@@ -37,7 +37,7 @@ class MapResult(ApplyResult[list[_T]]): error_callback: Callable[[BaseException], object] | None, ) -> None: ... @@ -308,7 +308,7 @@ index 61d6d0781..950ed1d8c 100644 def __iter__(self) -> Self: ... def next(self, timeout: float | None = None) -> _T: ... diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi -index bc0ff6469..730404bde 100644 +index b83516b4d..724bc3166 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -397,7 +397,7 @@ class Connection: diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi index 18920cd8a8a4..1397e579d53b 100644 --- a/mypy/typeshed/stdlib/_asyncio.pyi +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -65,7 +65,7 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn self, coro: _TaskCompatibleCoro[_T_co], *, - loop: AbstractEventLoop = ..., + loop: AbstractEventLoop | None = None, name: str | None = ..., context: Context | None = None, eager_start: bool = False, @@ -75,13 +75,13 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn self, coro: _TaskCompatibleCoro[_T_co], *, - loop: AbstractEventLoop = ..., + loop: AbstractEventLoop | None = None, name: str | None = ..., context: Context | None = None, ) -> None: ... else: def __init__( - self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... + self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop | None = None, name: str | None = ... ) -> None: ... if sys.version_info >= (3, 12): diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi index 10d7019a222f..3d17cb59c79b 100644 --- a/mypy/typeshed/stdlib/_blake2.pyi +++ b/mypy/typeshed/stdlib/_blake2.pyi @@ -22,8 +22,8 @@ class blake2b: digest_size: int name: str if sys.version_info >= (3, 9): - def __init__( - self, + def __new__( + cls, data: ReadableBuffer = b"", /, *, @@ -39,10 +39,10 @@ class blake2b: inner_size: int = 0, last_node: bool = False, usedforsecurity: bool = True, - ) -> None: ... + ) -> Self: ... else: - def __init__( - self, + def __new__( + cls, data: ReadableBuffer = b"", /, *, @@ -57,7 +57,7 @@ class blake2b: node_depth: int = 0, inner_size: int = 0, last_node: bool = False, - ) -> None: ... + ) -> Self: ... def copy(self) -> Self: ... def digest(self) -> bytes: ... @@ -74,8 +74,8 @@ class blake2s: digest_size: int name: str if sys.version_info >= (3, 9): - def __init__( - self, + def __new__( + cls, data: ReadableBuffer = b"", /, *, @@ -91,10 +91,10 @@ class blake2s: inner_size: int = 0, last_node: bool = False, usedforsecurity: bool = True, - ) -> None: ... + ) -> Self: ... else: - def __init__( - self, + def __new__( + cls, data: ReadableBuffer = b"", /, *, @@ -109,7 +109,7 @@ class blake2s: node_depth: int = 0, inner_size: int = 0, last_node: bool = False, - ) -> None: ... + ) -> Self: ... def copy(self) -> Self: ... def digest(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_bz2.pyi b/mypy/typeshed/stdlib/_bz2.pyi index 4ba26fe96be0..fdad932ca22e 100644 --- a/mypy/typeshed/stdlib/_bz2.pyi +++ b/mypy/typeshed/stdlib/_bz2.pyi @@ -1,9 +1,15 @@ +import sys from _typeshed import ReadableBuffer from typing import final +from typing_extensions import Self @final class BZ2Compressor: - def __init__(self, compresslevel: int = 9) -> None: ... + if sys.version_info >= (3, 12): + def __new__(cls, compresslevel: int = 9, /) -> Self: ... + else: + def __init__(self, compresslevel: int = 9, /) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... def flush(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index bf7f2991f9a4..8bac0ce1dca3 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -8,6 +8,7 @@ from typing import ( # noqa: Y022,Y038 AsyncIterator as AsyncIterator, Awaitable as Awaitable, Callable as Callable, + ClassVar, Collection as Collection, Container as Container, Coroutine as Coroutine, @@ -74,6 +75,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... def __reversed__(self) -> Iterator[_KT_co]: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... if sys.version_info >= (3, 10): @@ -91,6 +93,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi index 2e21a8c5d017..c7d0814b3cb4 100644 --- a/mypy/typeshed/stdlib/_contextvars.pyi +++ b/mypy/typeshed/stdlib/_contextvars.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterator, Mapping from typing import Any, ClassVar, Generic, TypeVar, final, overload -from typing_extensions import ParamSpec +from typing_extensions import ParamSpec, Self if sys.version_info >= (3, 9): from types import GenericAlias @@ -13,9 +13,9 @@ _P = ParamSpec("_P") @final class ContextVar(Generic[_T]): @overload - def __init__(self, name: str) -> None: ... + def __new__(cls, name: str) -> Self: ... @overload - def __init__(self, name: str, *, default: _T) -> None: ... + def __new__(cls, name: str, *, default: _T) -> Self: ... def __hash__(self) -> int: ... @property def name(self) -> str: ... @@ -37,6 +37,7 @@ class Token(Generic[_T]): @property def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express MISSING: ClassVar[object] + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @@ -55,6 +56,7 @@ class Context(Mapping[ContextVar[Any], Any]): def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... def copy(self) -> Context: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __getitem__(self, key: ContextVar[_T], /) -> _T: ... def __iter__(self) -> Iterator[ContextVar[Any]]: ... def __len__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index afa2870be158..aa9fc538417e 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -32,8 +32,8 @@ class Dialect: lineterminator: str quoting: _QuotingType strict: bool - def __init__( - self, + def __new__( + cls, dialect: _DialectLike | None = ..., delimiter: str = ",", doublequote: bool = True, @@ -43,7 +43,7 @@ class Dialect: quoting: _QuotingType = 0, skipinitialspace: bool = False, strict: bool = False, - ) -> None: ... + ) -> Self: ... if sys.version_info >= (3, 10): # This class calls itself _csv.reader. @@ -115,7 +115,7 @@ def reader( ) -> _reader: ... def register_dialect( name: str, - dialect: type[Dialect] = ..., + dialect: type[Dialect | csv.Dialect] = ..., *, delimiter: str = ",", quotechar: str | None = '"', diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index ecb07a29bb75..2977bf5afa94 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -169,18 +169,18 @@ class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType): # Abstract attribute that must be defined on subclasses _flags_: ClassVar[int] @overload - def __init__(self) -> None: ... + def __new__(cls) -> Self: ... @overload - def __init__(self, address: int, /) -> None: ... + def __new__(cls, address: int, /) -> Self: ... @overload - def __init__(self, callable: Callable[..., Any], /) -> None: ... + def __new__(cls, callable: Callable[..., Any], /) -> Self: ... @overload - def __init__(self, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> None: ... + def __new__(cls, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> Self: ... if sys.platform == "win32": @overload - def __init__( - self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / - ) -> None: ... + def __new__( + cls, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / + ) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index 9e06a1414da5..52c5185727e7 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -1,7 +1,7 @@ import sys -from _typeshed import ReadOnlyBuffer, SupportsRead +from _typeshed import ReadOnlyBuffer, SupportsRead, SupportsWrite from curses import _ncurses_version -from typing import IO, Any, final, overload +from typing import Any, final, overload from typing_extensions import TypeAlias # NOTE: This module is ordinarily only available on Unix, but the windows-curses @@ -517,7 +517,7 @@ class window: # undocumented def overwrite( self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... - def putwin(self, file: IO[Any], /) -> None: ... + def putwin(self, file: SupportsWrite[bytes], /) -> None: ... def redrawln(self, beg: int, num: int, /) -> None: ... def redrawwin(self) -> None: ... @overload diff --git a/mypy/typeshed/stdlib/_frozen_importlib.pyi b/mypy/typeshed/stdlib/_frozen_importlib.pyi index b6d7a1842048..3dbc8c6b52f0 100644 --- a/mypy/typeshed/stdlib/_frozen_importlib.pyi +++ b/mypy/typeshed/stdlib/_frozen_importlib.pyi @@ -5,7 +5,7 @@ import types from _typeshed.importlib import LoaderProtocol from collections.abc import Mapping, Sequence from types import ModuleType -from typing import Any +from typing import Any, ClassVar # Signature of `builtins.__import__` should be kept identical to `importlib.__import__` def __import__( @@ -43,6 +43,7 @@ class ModuleSpec: def parent(self) -> str | None: ... has_location: bool def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): # MetaPathFinder diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi index 284d99f92b60..54efd3199760 100644 --- a/mypy/typeshed/stdlib/_io.pyi +++ b/mypy/typeshed/stdlib/_io.pyi @@ -112,7 +112,7 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore def truncate(self, pos: int | None = None, /) -> int: ... class BufferedRWPair(BufferedIOBase, _BufferedIOBase): - def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192) -> None: ... + def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... class _TextIOBase(_IOBase): diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index e1c7c52ca3b1..5296b8e62a02 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -1,5 +1,6 @@ from collections.abc import Callable from typing import Any, final +from typing_extensions import Self @final class make_encoder: @@ -19,8 +20,8 @@ class make_encoder: def encoder(self) -> Callable[[str], str]: ... @property def item_separator(self) -> str: ... - def __init__( - self, + def __new__( + cls, markers: dict[int, Any] | None, default: Callable[[Any], Any], encoder: Callable[[str], str], @@ -30,7 +31,7 @@ class make_encoder: sort_keys: bool, skipkeys: bool, allow_nan: bool, - ) -> None: ... + ) -> Self: ... def __call__(self, obj: object, _current_indent_level: int) -> Any: ... @final @@ -42,7 +43,7 @@ class make_scanner: parse_float: Any strict: bool # TODO: 'context' needs the attrs above (ducktype), but not __call__. - def __init__(self, context: make_scanner) -> None: ... + def __new__(cls, context: make_scanner) -> Self: ... def __call__(self, string: str, index: int) -> tuple[Any, int]: ... def encode_basestring(s: str, /) -> str: ... diff --git a/mypy/typeshed/stdlib/_lzma.pyi b/mypy/typeshed/stdlib/_lzma.pyi index 1f5be78876c6..1a27c7428e8e 100644 --- a/mypy/typeshed/stdlib/_lzma.pyi +++ b/mypy/typeshed/stdlib/_lzma.pyi @@ -1,7 +1,8 @@ +import sys from _typeshed import ReadableBuffer from collections.abc import Mapping, Sequence from typing import Any, Final, final -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias _FilterChain: TypeAlias = Sequence[Mapping[str, Any]] @@ -36,7 +37,11 @@ PRESET_EXTREME: int # v big number @final class LZMADecompressor: - def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... + if sys.version_info >= (3, 12): + def __new__(cls, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> Self: ... + else: + def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... + def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... @property def check(self) -> int: ... @@ -49,9 +54,15 @@ class LZMADecompressor: @final class LZMACompressor: - def __init__( - self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... - ) -> None: ... + if sys.version_info >= (3, 12): + def __new__( + cls, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... + ) -> Self: ... + else: + def __init__( + self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... + ) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... def flush(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi index 5566f0f65d6e..50bbb6bc16cd 100644 --- a/mypy/typeshed/stdlib/_pickle.pyi +++ b/mypy/typeshed/stdlib/_pickle.pyi @@ -66,7 +66,6 @@ class Pickler: self, file: SupportsWrite[bytes], protocol: int | None = None, - *, fix_imports: bool = True, buffer_callback: _BufferCallback = None, ) -> None: ... diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi index 938135eb1192..1a068b997539 100644 --- a/mypy/typeshed/stdlib/_ssl.pyi +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -12,7 +12,7 @@ from ssl import ( SSLWantWriteError as SSLWantWriteError, SSLZeroReturnError as SSLZeroReturnError, ) -from typing import Any, Literal, TypedDict, final, overload +from typing import Any, ClassVar, Literal, TypedDict, final, overload from typing_extensions import NotRequired, Self, TypeAlias _PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray @@ -119,6 +119,7 @@ class MemoryBIO: @final class SSLSession: + __hash__: ClassVar[None] # type: ignore[assignment] @property def has_ticket(self) -> bool: ... @property diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index f0b70ed2a0b0..378ac2423757 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -13,17 +13,11 @@ error = RuntimeError def _count() -> int: ... @final -class LockType: +class RLock: def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... - def locked(self) -> bool: ... - def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool: ... - def release_lock(self) -> None: ... - def locked_lock(self) -> bool: ... - def __enter__(self) -> bool: ... - def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None - ) -> None: ... + __enter__ = acquire + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... if sys.version_info >= (3, 13): @final @@ -37,7 +31,33 @@ if sys.version_info >= (3, 13): def start_joinable_thread( function: Callable[[], object], handle: _ThreadHandle | None = None, daemon: bool = True ) -> _ThreadHandle: ... - lock = LockType + @final + class lock: + def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release(self) -> None: ... + def locked(self) -> bool: ... + def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release_lock(self) -> None: ... + def locked_lock(self) -> bool: ... + def __enter__(self) -> bool: ... + def __exit__( + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None + ) -> None: ... + + LockType = lock +else: + @final + class LockType: + def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release(self) -> None: ... + def locked(self) -> bool: ... + def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release_lock(self) -> None: ... + def locked_lock(self) -> bool: ... + def __enter__(self) -> bool: ... + def __exit__( + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None + ) -> None: ... @overload def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index bd41b9ebc78e..4206a2114f95 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -82,7 +82,7 @@ class TkappType: def mainloop(self, threshold: int = 0, /): ... def quit(self): ... def record(self, script, /): ... - def setvar(self, *args, **kwargs): ... + def setvar(self, *ags, **kwargs): ... if sys.version_info < (3, 11): def split(self, arg, /): ... diff --git a/mypy/typeshed/stdlib/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi index 2a4e682f64ed..b55318528208 100644 --- a/mypy/typeshed/stdlib/_weakrefset.pyi +++ b/mypy/typeshed/stdlib/_weakrefset.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Iterable, Iterator, MutableSet -from typing import Any, TypeVar, overload +from typing import Any, ClassVar, TypeVar, overload from typing_extensions import Self if sys.version_info >= (3, 9): @@ -21,6 +21,7 @@ class WeakSet(MutableSet[_T]): def copy(self) -> Self: ... def remove(self, item: _T) -> None: ... def update(self, other: Iterable[_T]) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __contains__(self, item: object) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T]: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 365617077f09..b9652ec5f75a 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import sentinel +from _typeshed import SupportsWrite, sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern -from typing import IO, Any, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload +from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -207,8 +207,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): help: str | None = None, metavar: str | None = None, ) -> _SubParsersAction[_ArgumentParserT]: ... - def print_usage(self, file: IO[str] | None = None) -> None: ... - def print_help(self, file: IO[str] | None = None) -> None: ... + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_help(self, file: SupportsWrite[str] | None = None) -> None: ... def format_usage(self) -> str: ... def format_help(self) -> str: ... @overload @@ -254,7 +254,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def _get_value(self, action: Action, arg_string: str) -> Any: ... def _check_value(self, action: Action, value: Any) -> None: ... def _get_formatter(self) -> HelpFormatter: ... - def _print_message(self, message: str, file: IO[str] | None = None) -> None: ... + def _print_message(self, message: str, file: SupportsWrite[str] | None = None) -> None: ... class HelpFormatter: # undocumented @@ -456,6 +456,7 @@ class Namespace(_AttributeHolder): def __setattr__(self, name: str, value: Any, /) -> None: ... def __contains__(self, key: str) -> bool: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] class FileType: # undocumented diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 878d8d8cb808..19ec8c1e78f9 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Iterable # pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence -from typing import Any, Literal, MutableSequence, SupportsIndex, TypeVar, overload # noqa: Y022 +from typing import Any, ClassVar, Literal, MutableSequence, SupportsIndex, TypeVar, overload # noqa: Y022 from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 12): @@ -24,19 +24,21 @@ class array(MutableSequence[_T]): @property def itemsize(self) -> int: ... @overload - def __init__(self: array[int], typecode: _IntTypeCode, initializer: bytes | bytearray | Iterable[int] = ..., /) -> None: ... + def __new__( + cls: type[array[int]], typecode: _IntTypeCode, initializer: bytes | bytearray | Iterable[int] = ..., / + ) -> array[int]: ... @overload - def __init__( - self: array[float], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., / - ) -> None: ... + def __new__( + cls: type[array[float]], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., / + ) -> array[float]: ... @overload - def __init__( - self: array[str], typecode: _UnicodeTypeCode, initializer: bytes | bytearray | Iterable[str] = ..., / - ) -> None: ... + def __new__( + cls: type[array[str]], typecode: _UnicodeTypeCode, initializer: bytes | bytearray | Iterable[str] = ..., / + ) -> array[str]: ... @overload - def __init__(self, typecode: str, initializer: Iterable[_T], /) -> None: ... + def __new__(cls, typecode: str, initializer: Iterable[_T], /) -> Self: ... @overload - def __init__(self, typecode: str, initializer: bytes | bytearray = ..., /) -> None: ... + def __new__(cls, typecode: str, initializer: bytes | bytearray = ..., /) -> Self: ... def append(self, v: _T, /) -> None: ... def buffer_info(self) -> tuple[int, int]: ... def byteswap(self) -> None: ... @@ -62,6 +64,7 @@ class array(MutableSequence[_T]): def fromstring(self, buffer: str | ReadableBuffer, /) -> None: ... def tostring(self) -> bytes: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __len__(self) -> int: ... @overload def __getitem__(self, key: SupportsIndex, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 351a4af2fb75..7a4438a33fbc 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -7,7 +7,7 @@ from _ast import ( PyCF_TYPE_COMMENTS as PyCF_TYPE_COMMENTS, ) from _typeshed import ReadableBuffer, Unused -from collections.abc import Iterator +from collections.abc import Iterable, Iterator from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload from typing_extensions import Self, Unpack, deprecated @@ -1154,6 +1154,7 @@ class Tuple(expr): if sys.version_info >= (3, 14): def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... +@deprecated("Deprecated since Python 3.9.") class slice(AST): ... # deprecated and moved to ast.py for >= (3, 9) if sys.version_info >= (3, 9): @@ -1185,22 +1186,38 @@ class Slice(_Slice): **kwargs: Unpack[_SliceAttributes], ) -> Self: ... +@deprecated("Deprecated since Python 3.9. Use ast.Tuple instead.") class ExtSlice(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) - dims: list[slice] - def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... + if sys.version_info >= (3, 9): + def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_SliceAttributes]) -> Tuple: ... # type: ignore[misc] + else: + dims: list[slice] + def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... +@deprecated("Deprecated since Python 3.9. Use the index value directly instead.") class Index(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... + if sys.version_info >= (3, 9): + def __new__(cls, value: expr, **kwargs: Unpack[_SliceAttributes]) -> expr: ... # type: ignore[misc] + else: + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... class expr_context(AST): ... + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") class AugLoad(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") class AugStore(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") class Param(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") class Suite(mod): # deprecated and moved to ast.py if sys.version_info >= (3, 9) - body: list[stmt] - def __init__(self, body: list[stmt]) -> None: ... + if sys.version_info < (3, 9): + body: list[stmt] + def __init__(self, body: list[stmt]) -> None: ... class Load(expr_context): ... class Store(expr_context): ... diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index daf28862aa6a..7c3ac6ede4fe 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -30,6 +30,1306 @@ if sys.platform == "win32": else: from .unix_events import * +if sys.platform == "win32": + if sys.version_info >= (3, 14): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + "EventLoop", # from windows_events + ) + elif sys.version_info >= (3, 13): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + "EventLoop", # from windows_events + ) + elif sys.version_info >= (3, 12): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + elif sys.version_info >= (3, 11): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + elif sys.version_info >= (3, 10): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + elif sys.version_info >= (3, 9): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + else: + __all__ = ( + "BaseEventLoop", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) +else: + if sys.version_info >= (3, 14): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + "EventLoop", # from unix_events + ) + elif sys.version_info >= (3, 13): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + "EventLoop", # from unix_events + ) + elif sys.version_info >= (3, 12): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + elif sys.version_info >= (3, 11): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + elif sys.version_info >= (3, 10): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + elif sys.version_info >= (3, 9): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + else: + __all__ = ( + "BaseEventLoop", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + _T_co = TypeVar("_T_co", covariant=True) # Aliases imported by multiple submodules in typeshed diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index cba2c7799528..d410193a3379 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -13,6 +13,7 @@ from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Literal, TypeVar, overload from typing_extensions import TypeAlias, TypeVarTuple, Unpack +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 9): __all__ = ("BaseEventLoop", "Server") else: @@ -452,6 +453,7 @@ class BaseEventLoop(AbstractEventLoop): bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, + text: Literal[False] | None = None, **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... def add_reader(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/coroutines.pyi b/mypy/typeshed/stdlib/asyncio/coroutines.pyi index bc797de7fd51..8ef30b3d3198 100644 --- a/mypy/typeshed/stdlib/asyncio/coroutines.pyi +++ b/mypy/typeshed/stdlib/asyncio/coroutines.pyi @@ -3,6 +3,7 @@ from collections.abc import Awaitable, Callable, Coroutine from typing import Any, TypeVar, overload from typing_extensions import ParamSpec, TypeGuard, TypeIs +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("iscoroutinefunction", "iscoroutine") else: diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index ead64070671f..af1594524c45 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -22,6 +22,7 @@ from .tasks import Task from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from .unix_events import AbstractChildWatcher +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 14): __all__ = ( "AbstractEventLoopPolicy", diff --git a/mypy/typeshed/stdlib/asyncio/exceptions.pyi b/mypy/typeshed/stdlib/asyncio/exceptions.pyi index 0746394d582f..759838f45de4 100644 --- a/mypy/typeshed/stdlib/asyncio/exceptions.pyi +++ b/mypy/typeshed/stdlib/asyncio/exceptions.pyi @@ -1,5 +1,6 @@ import sys +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ( "BrokenBarrierError", diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index 28e6ca8c86a3..cb2785012fb2 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -5,6 +5,7 @@ from typing_extensions import TypeIs from .events import AbstractEventLoop +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index 0114aeb23329..4eef69dee5c3 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -15,6 +15,7 @@ if sys.version_info >= (3, 10): else: _LoopBoundMixin = object +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("Lock", "Event", "Condition", "Semaphore", "BoundedSemaphore", "Barrier") else: diff --git a/mypy/typeshed/stdlib/asyncio/protocols.pyi b/mypy/typeshed/stdlib/asyncio/protocols.pyi index 5173b74ed5a0..5425336c49a8 100644 --- a/mypy/typeshed/stdlib/asyncio/protocols.pyi +++ b/mypy/typeshed/stdlib/asyncio/protocols.pyi @@ -2,6 +2,7 @@ from _typeshed import ReadableBuffer from asyncio import transports from typing import Any +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") class BaseProtocol: diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index 895205aa9519..d287fe779297 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -13,6 +13,7 @@ else: class QueueEmpty(Exception): ... class QueueFull(Exception): ... +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 13): __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty", "QueueShutDown") diff --git a/mypy/typeshed/stdlib/asyncio/runners.pyi b/mypy/typeshed/stdlib/asyncio/runners.pyi index 37a85b709cdc..caf5e4996cf4 100644 --- a/mypy/typeshed/stdlib/asyncio/runners.pyi +++ b/mypy/typeshed/stdlib/asyncio/runners.pyi @@ -7,6 +7,7 @@ from typing_extensions import Self from .events import AbstractEventLoop +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("Runner", "run") else: diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index ed95583c1847..43df5ae2d0c8 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -9,6 +9,7 @@ from typing_extensions import Self, TypeAlias from . import events, protocols, transports from .base_events import Server +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform == "win32": __all__ = ("StreamReader", "StreamWriter", "StreamReaderProtocol", "open_connection", "start_server") else: diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index 19452d4eb469..50d75391f36d 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -5,6 +5,7 @@ from asyncio import events, protocols, streams, transports from collections.abc import Callable, Collection from typing import IO, Any, Literal +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("create_subprocess_exec", "create_subprocess_shell") PIPE: int diff --git a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi index aec3f1127f15..30b7c9129f6f 100644 --- a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi +++ b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi @@ -8,6 +8,7 @@ from . import _CoroutineLike from .events import AbstractEventLoop from .tasks import Task +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 12): __all__ = ("TaskGroup",) else: diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index d1ff7d425ba4..a349e81d80e9 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -18,6 +18,7 @@ from .futures import Future if sys.version_info >= (3, 11): from contextvars import Context +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 12): __all__ = ( "Task", diff --git a/mypy/typeshed/stdlib/asyncio/threads.pyi b/mypy/typeshed/stdlib/asyncio/threads.pyi index 799efd25fea4..00aae2ea814c 100644 --- a/mypy/typeshed/stdlib/asyncio/threads.pyi +++ b/mypy/typeshed/stdlib/asyncio/threads.pyi @@ -2,6 +2,7 @@ from collections.abc import Callable from typing import TypeVar from typing_extensions import ParamSpec +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("to_thread",) _P = ParamSpec("_P") _R = TypeVar("_R") diff --git a/mypy/typeshed/stdlib/asyncio/timeouts.pyi b/mypy/typeshed/stdlib/asyncio/timeouts.pyi index 2f0e40e25680..668cccbfe8b1 100644 --- a/mypy/typeshed/stdlib/asyncio/timeouts.pyi +++ b/mypy/typeshed/stdlib/asyncio/timeouts.pyi @@ -2,6 +2,7 @@ from types import TracebackType from typing import final from typing_extensions import Self +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("Timeout", "timeout", "timeout_at") @final diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi index 531f77672438..c28ae234f2cc 100644 --- a/mypy/typeshed/stdlib/asyncio/transports.pyi +++ b/mypy/typeshed/stdlib/asyncio/transports.pyi @@ -4,6 +4,7 @@ from collections.abc import Iterable, Mapping from socket import _Address from typing import Any +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseTransport", "ReadTransport", "WriteTransport", "Transport", "DatagramTransport", "SubprocessTransport") class BaseTransport: diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index fb21c5b5fa05..abf5d7ffd699 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -13,10 +13,12 @@ from .selector_events import BaseSelectorEventLoop _Ts = TypeVarTuple("_Ts") +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform != "win32": if sys.version_info >= (3, 14): __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy", "EventLoop") elif sys.version_info >= (3, 13): + # Adds EventLoop __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", @@ -29,6 +31,7 @@ if sys.platform != "win32": "EventLoop", ) elif sys.version_info >= (3, 9): + # adds PidfdChildWatcher __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index e5205ba4dcb0..2ffc2eccb228 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -6,6 +6,7 @@ from typing import IO, Any, ClassVar, Final, NoReturn from . import events, futures, proactor_events, selector_events, streams, windows_utils +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform == "win32": if sys.version_info >= (3, 13): # 3.13 added `EventLoop`. diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 56a5969d102a..6fb901b9f009 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1391,18 +1391,18 @@ def locals() -> dict[str, Any]: ... class map(Iterator[_S]): @overload - def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... + def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ... @overload - def __new__(cls, func: Callable[[_T1, _T2], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... + def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... @overload def __new__( - cls, func: Callable[[_T1, _T2, _T3], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / + cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / ) -> Self: ... @overload def __new__( cls, func: Callable[[_T1, _T2, _T3, _T4], _S], - iter1: Iterable[_T1], + iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], @@ -1412,7 +1412,7 @@ class map(Iterator[_S]): def __new__( cls, func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], - iter1: Iterable[_T1], + iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], @@ -1423,7 +1423,7 @@ class map(Iterator[_S]): def __new__( cls, func: Callable[..., _S], - iter1: Iterable[Any], + iterable: Iterable[Any], iter2: Iterable[Any], iter3: Iterable[Any], iter4: Iterable[Any], @@ -1866,9 +1866,7 @@ class NameError(Exception): class ReferenceError(Exception): ... class RuntimeError(Exception): ... - -class StopAsyncIteration(Exception): - value: Any +class StopAsyncIteration(Exception): ... class SyntaxError(Exception): msg: str diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index b3c721f1e283..c6f517adb3cd 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -254,6 +254,8 @@ class StreamReaderWriter(TextIO): def writable(self) -> bool: ... class StreamRecoder(BinaryIO): + data_encoding: str + file_encoding: str def __init__( self, stream: _Stream, diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 2d136318813c..0f99b5c3c67e 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -1,7 +1,7 @@ import sys from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT -from typing import Any, Generic, NoReturn, SupportsIndex, TypeVar, final, overload +from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload from typing_extensions import Self if sys.version_info >= (3, 9): @@ -119,6 +119,7 @@ class UserList(MutableSequence[_T]): def __init__(self, initlist: None = None) -> None: ... @overload def __init__(self, initlist: Iterable[_T]) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __lt__(self, other: list[_T] | UserList[_T]) -> bool: ... def __le__(self, other: list[_T] | UserList[_T]) -> bool: ... def __gt__(self, other: list[_T] | UserList[_T]) -> bool: ... @@ -254,6 +255,7 @@ class deque(MutableSequence[_T]): def rotate(self, n: int = 1, /) -> None: ... def __copy__(self) -> Self: ... def __len__(self) -> int: ... + __hash__: ClassVar[None] # type: ignore[assignment] # These methods of deque don't take slices, unlike MutableSequence, hence the type: ignores def __getitem__(self, key: SupportsIndex, /) -> _T: ... # type: ignore[override] def __setitem__(self, key: SupportsIndex, value: _T, /) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index dc5d926775f3..f57e7fa67036 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -33,8 +33,12 @@ _T_co = TypeVar("_T_co", covariant=True) _T_io = TypeVar("_T_io", bound=IO[str] | None) _ExitT_co = TypeVar("_ExitT_co", covariant=True, bound=bool | None, default=bool | None) _F = TypeVar("_F", bound=Callable[..., Any]) +_G = TypeVar("_G", bound=Generator[Any, Any, Any] | AsyncGenerator[Any, Any], covariant=True) _P = ParamSpec("_P") +_SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) + _ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) @@ -64,16 +68,19 @@ class ContextDecorator: def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManagerBase: ... - -class _GeneratorContextManager(_GeneratorContextManagerBase, AbstractContextManager[_T_co, bool | None], ContextDecorator): - # __init__ and all instance attributes are actually inherited from _GeneratorContextManagerBase - # adding them there is more trouble than it's worth to include in the stub; see #6676 - def __init__(self, func: Callable[..., Iterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... - gen: Generator[_T_co, Any, Any] - func: Callable[..., Generator[_T_co, Any, Any]] +class _GeneratorContextManagerBase(Generic[_G]): + # Ideally this would use ParamSpec, but that requires (*args, **kwargs), which this isn't. see #6676 + def __init__(self, func: Callable[..., _G], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... + gen: _G + func: Callable[..., _G] args: tuple[Any, ...] kwds: dict[str, Any] + +class _GeneratorContextManager( + _GeneratorContextManagerBase[Generator[_T_co, _SendT_contra, _ReturnT_co]], + AbstractContextManager[_T_co, bool | None], + ContextDecorator, +): if sys.version_info >= (3, 9): def __exit__( self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None @@ -93,26 +100,18 @@ if sys.version_info >= (3, 10): def __call__(self, func: _AF) -> _AF: ... class _AsyncGeneratorContextManager( - _GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator + _GeneratorContextManagerBase[AsyncGenerator[_T_co, _SendT_contra]], + AbstractAsyncContextManager[_T_co, bool | None], + AsyncContextDecorator, ): - # __init__ and these attributes are actually defined in the base class _GeneratorContextManagerBase, - # adding them there is more trouble than it's worth to include in the stub (see #6676) - def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... - gen: AsyncGenerator[_T_co, Any] - func: Callable[..., AsyncGenerator[_T_co, Any]] - args: tuple[Any, ...] - kwds: dict[str, Any] async def __aexit__( self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(_GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None]): - def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... - gen: AsyncGenerator[_T_co, Any] - func: Callable[..., AsyncGenerator[_T_co, Any]] - args: tuple[Any, ...] - kwds: dict[str, Any] + class _AsyncGeneratorContextManager( + _GeneratorContextManagerBase[AsyncGenerator[_T_co, _SendT_contra]], AbstractAsyncContextManager[_T_co, bool | None] + ): async def __aexit__( self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> bool | None: ... diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index a15dd3615c0c..5533a22770b8 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -24,8 +24,9 @@ from _ctypes import ( set_errno as set_errno, sizeof as sizeof, ) +from _typeshed import StrPath from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure -from typing import Any, ClassVar, Generic, TypeVar +from typing import Any, ClassVar, Generic, TypeVar, type_check_only from typing_extensions import Self, TypeAlias, deprecated if sys.platform == "win32": @@ -45,15 +46,32 @@ DEFAULT_MODE: int class ArgumentError(Exception): ... +# defined within CDLL.__init__ +# Runtime name is ctypes.CDLL.__init__.._FuncPtr +@type_check_only +class _CDLLFuncPointer(_CFuncPtr): + _flags_: ClassVar[int] + _restype_: ClassVar[type[_CDataType]] + +# Not a real class; _CDLLFuncPointer with a __name__ set on it. +@type_check_only +class _NamedFuncPointer(_CDLLFuncPointer): + __name__: str + +if sys.version_info >= (3, 12): + _NameTypes: TypeAlias = StrPath | None +else: + _NameTypes: TypeAlias = str | None + class CDLL: _func_flags_: ClassVar[int] _func_restype_: ClassVar[type[_CDataType]] _name: str _handle: int - _FuncPtr: type[_FuncPointer] + _FuncPtr: type[_CDLLFuncPointer] def __init__( self, - name: str | None, + name: _NameTypes, mode: int = ..., handle: int | None = None, use_errno: bool = False, @@ -84,27 +102,36 @@ if sys.platform == "win32": pydll: LibraryLoader[PyDLL] pythonapi: PyDLL -class _FuncPointer(_CFuncPtr): ... +# Class definition within CFUNCTYPE / WINFUNCTYPE / PYFUNCTYPE +# Names at runtime are +# ctypes.CFUNCTYPE..CFunctionType +# ctypes.WINFUNCTYPE..WinFunctionType +# ctypes.PYFUNCTYPE..CFunctionType +@type_check_only +class _CFunctionType(_CFuncPtr): + _argtypes_: ClassVar[list[type[_CData | _CDataType]]] + _restype_: ClassVar[type[_CData | _CDataType] | None] + _flags_: ClassVar[int] -class _NamedFuncPointer(_FuncPointer): - __name__: str +# Alias for either function pointer type +_FuncPointer: TypeAlias = _CDLLFuncPointer | _CFunctionType # noqa: Y047 # not used here def CFUNCTYPE( restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType], - use_errno: bool = ..., - use_last_error: bool = ..., -) -> type[_FuncPointer]: ... + use_errno: bool = False, + use_last_error: bool = False, +) -> type[_CFunctionType]: ... if sys.platform == "win32": def WINFUNCTYPE( restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType], - use_errno: bool = ..., - use_last_error: bool = ..., - ) -> type[_FuncPointer]: ... + use_errno: bool = False, + use_last_error: bool = False, + ) -> type[_CFunctionType]: ... -def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_CFunctionType]: ... # Any type that can be implicitly converted to c_void_p when passed as a C function argument. # (bytes is not included here, see below.) @@ -134,8 +161,22 @@ if sys.platform == "win32": def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO not documented def GetLastError() -> int: ... -def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... -def memset(dst: _CVoidPLike, c: int, count: int) -> int: ... +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemmoveFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... + +memmove: _MemmoveFunctionType + +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemsetFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, c: int, count: int) -> int: ... + +memset: _MemsetFunctionType + def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ... if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 3295b1c1f835..3d89b830352b 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -152,33 +152,37 @@ if sys.version_info >= (3, 10): def field( *, default: _T, + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> _T: ... @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> _T: ... @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> Any: ... else: @@ -186,6 +190,7 @@ else: def field( *, default: _T, + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, @@ -195,6 +200,7 @@ else: @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, @@ -205,6 +211,8 @@ else: @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 87037ef39be7..4907bf4607c8 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -29,7 +29,7 @@ class timezone(tzinfo): utc: ClassVar[timezone] min: ClassVar[timezone] max: ClassVar[timezone] - def __init__(self, offset: timedelta, name: str = ...) -> None: ... + def __new__(cls, offset: timedelta, name: str = ...) -> Self: ... def tzname(self, dt: datetime | None, /) -> str: ... def utcoffset(self, dt: datetime | None, /) -> timedelta: ... def dst(self, dt: datetime | None, /) -> None: ... diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 7f8708a020fd..7eb922c8a7ed 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -189,7 +189,6 @@ class Context: clamp: int | None = ..., flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - _ignored_flags: list[_TrapType] | None = ..., ) -> None: ... def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... def clear_flags(self) -> None: ... diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index 2939192c9526..683daa468cf3 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Iterator from email.message import Message -from typing import Final, overload +from typing import ClassVar, Final, overload __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] @@ -24,6 +24,7 @@ class Charset: def body_encode(self, string: None) -> None: ... @overload def body_encode(self, string: str | bytes) -> str: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... def __ne__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/header.pyi b/mypy/typeshed/stdlib/email/header.pyi index 212132c6be18..a26bbb516e09 100644 --- a/mypy/typeshed/stdlib/email/header.pyi +++ b/mypy/typeshed/stdlib/email/header.pyi @@ -1,6 +1,6 @@ from collections.abc import Iterable from email.charset import Charset -from typing import Any +from typing import Any, ClassVar __all__ = ["Header", "decode_header", "make_header"] @@ -16,6 +16,7 @@ class Header: ) -> None: ... def append(self, s: bytes | bytearray | str, charset: Charset | str | None = None, errors: str = "strict") -> None: ... def encode(self, splitchars: str = ";, \t", maxlinelen: int | None = None, linesep: str = "\n") -> str: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... def __ne__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi index 2ffdca9b2f22..dc641c8c952b 100644 --- a/mypy/typeshed/stdlib/email/headerregistry.pyi +++ b/mypy/typeshed/stdlib/email/headerregistry.pyi @@ -167,6 +167,7 @@ class Address: def __init__( self, display_name: str = "", username: str | None = "", domain: str | None = "", addr_spec: str | None = None ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... class Group: @@ -175,4 +176,5 @@ class Group: @property def addresses(self) -> tuple[Address, ...]: ... def __init__(self, display_name: str | None = None, addresses: Iterable[Address] | None = None) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 8993a3217185..ebad05a1cf7b 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -153,7 +153,7 @@ class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def attach(self, payload: Self) -> None: ... # type: ignore[override] # The attachments are created via type(self) in the attach method. It's theoretically # possible to sneak other attachment types into a MIMEPart instance, but could cause - # cause unforeseen consequences. + # cause unforseen consequences. def iter_attachments(self) -> Iterator[Self]: ... def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 33bc766df15d..aaa3a22087fc 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -138,7 +138,7 @@ class Fraction(Rational): def __round__(self, ndigits: None = None) -> int: ... @overload def __round__(self, ndigits: int) -> Fraction: ... - def __hash__(self) -> int: ... + def __hash__(self) -> int: ... # type: ignore[override] def __eq__(a, b: object) -> bool: ... def __lt__(a, b: _ComparableNum) -> bool: ... def __gt__(a, b: _ComparableNum) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index d455283948d1..ef413a349125 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -14,9 +14,14 @@ class HTTPStatus(IntEnum): def phrase(self) -> str: ... @property def description(self) -> str: ... + + # Keep these synced with the global constants in http/client.pyi. CONTINUE = 100 SWITCHING_PROTOCOLS = 101 PROCESSING = 102 + if sys.version_info >= (3, 9): + EARLY_HINTS = 103 + OK = 200 CREATED = 201 ACCEPTED = 202 @@ -27,6 +32,7 @@ class HTTPStatus(IntEnum): MULTI_STATUS = 207 ALREADY_REPORTED = 208 IM_USED = 226 + MULTIPLE_CHOICES = 300 MOVED_PERMANENTLY = 301 FOUND = 302 @@ -35,6 +41,7 @@ class HTTPStatus(IntEnum): USE_PROXY = 305 TEMPORARY_REDIRECT = 307 PERMANENT_REDIRECT = 308 + BAD_REQUEST = 400 UNAUTHORIZED = 401 PAYMENT_REQUIRED = 402 @@ -59,15 +66,22 @@ class HTTPStatus(IntEnum): RANGE_NOT_SATISFIABLE = 416 REQUESTED_RANGE_NOT_SATISFIABLE = 416 EXPECTATION_FAILED = 417 + if sys.version_info >= (3, 9): + IM_A_TEAPOT = 418 + MISDIRECTED_REQUEST = 421 if sys.version_info >= (3, 13): UNPROCESSABLE_CONTENT = 422 UNPROCESSABLE_ENTITY = 422 LOCKED = 423 FAILED_DEPENDENCY = 424 + if sys.version_info >= (3, 9): + TOO_EARLY = 425 UPGRADE_REQUIRED = 426 PRECONDITION_REQUIRED = 428 TOO_MANY_REQUESTS = 429 REQUEST_HEADER_FIELDS_TOO_LARGE = 431 + UNAVAILABLE_FOR_LEGAL_REASONS = 451 + INTERNAL_SERVER_ERROR = 500 NOT_IMPLEMENTED = 501 BAD_GATEWAY = 502 @@ -79,12 +93,7 @@ class HTTPStatus(IntEnum): LOOP_DETECTED = 508 NOT_EXTENDED = 510 NETWORK_AUTHENTICATION_REQUIRED = 511 - MISDIRECTED_REQUEST = 421 - UNAVAILABLE_FOR_LEGAL_REASONS = 451 - if sys.version_info >= (3, 9): - EARLY_HINTS = 103 - IM_A_TEAPOT = 418 - TOO_EARLY = 425 + if sys.version_info >= (3, 12): @property def is_informational(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 3db764ef1e7c..cd2fc4f5a652 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -6,7 +6,7 @@ import types from _typeshed import MaybeNone, ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer from collections.abc import Callable, Iterable, Iterator, Mapping from socket import socket -from typing import BinaryIO, TypeVar, overload +from typing import BinaryIO, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -39,63 +39,85 @@ _HeaderValue: TypeAlias = ReadableBuffer | str | int HTTP_PORT: int HTTPS_PORT: int -CONTINUE: int -SWITCHING_PROTOCOLS: int -PROCESSING: int - -OK: int -CREATED: int -ACCEPTED: int -NON_AUTHORITATIVE_INFORMATION: int -NO_CONTENT: int -RESET_CONTENT: int -PARTIAL_CONTENT: int -MULTI_STATUS: int -IM_USED: int - -MULTIPLE_CHOICES: int -MOVED_PERMANENTLY: int -FOUND: int -SEE_OTHER: int -NOT_MODIFIED: int -USE_PROXY: int -TEMPORARY_REDIRECT: int - -BAD_REQUEST: int -UNAUTHORIZED: int -PAYMENT_REQUIRED: int -FORBIDDEN: int -NOT_FOUND: int -METHOD_NOT_ALLOWED: int -NOT_ACCEPTABLE: int -PROXY_AUTHENTICATION_REQUIRED: int -REQUEST_TIMEOUT: int -CONFLICT: int -GONE: int -LENGTH_REQUIRED: int -PRECONDITION_FAILED: int -REQUEST_ENTITY_TOO_LARGE: int -REQUEST_URI_TOO_LONG: int -UNSUPPORTED_MEDIA_TYPE: int -REQUESTED_RANGE_NOT_SATISFIABLE: int -EXPECTATION_FAILED: int -UNPROCESSABLE_ENTITY: int -LOCKED: int -FAILED_DEPENDENCY: int -UPGRADE_REQUIRED: int -PRECONDITION_REQUIRED: int -TOO_MANY_REQUESTS: int -REQUEST_HEADER_FIELDS_TOO_LARGE: int - -INTERNAL_SERVER_ERROR: int -NOT_IMPLEMENTED: int -BAD_GATEWAY: int -SERVICE_UNAVAILABLE: int -GATEWAY_TIMEOUT: int -HTTP_VERSION_NOT_SUPPORTED: int -INSUFFICIENT_STORAGE: int -NOT_EXTENDED: int -NETWORK_AUTHENTICATION_REQUIRED: int +# Keep these global constants in sync with http.HTTPStatus (http/__init__.pyi). +# They are present for backward compatibility reasons. +CONTINUE: Literal[100] +SWITCHING_PROTOCOLS: Literal[101] +PROCESSING: Literal[102] +if sys.version_info >= (3, 9): + EARLY_HINTS: Literal[103] + +OK: Literal[200] +CREATED: Literal[201] +ACCEPTED: Literal[202] +NON_AUTHORITATIVE_INFORMATION: Literal[203] +NO_CONTENT: Literal[204] +RESET_CONTENT: Literal[205] +PARTIAL_CONTENT: Literal[206] +MULTI_STATUS: Literal[207] +ALREADY_REPORTED: Literal[208] +IM_USED: Literal[226] + +MULTIPLE_CHOICES: Literal[300] +MOVED_PERMANENTLY: Literal[301] +FOUND: Literal[302] +SEE_OTHER: Literal[303] +NOT_MODIFIED: Literal[304] +USE_PROXY: Literal[305] +TEMPORARY_REDIRECT: Literal[307] +PERMANENT_REDIRECT: Literal[308] + +BAD_REQUEST: Literal[400] +UNAUTHORIZED: Literal[401] +PAYMENT_REQUIRED: Literal[402] +FORBIDDEN: Literal[403] +NOT_FOUND: Literal[404] +METHOD_NOT_ALLOWED: Literal[405] +NOT_ACCEPTABLE: Literal[406] +PROXY_AUTHENTICATION_REQUIRED: Literal[407] +REQUEST_TIMEOUT: Literal[408] +CONFLICT: Literal[409] +GONE: Literal[410] +LENGTH_REQUIRED: Literal[411] +PRECONDITION_FAILED: Literal[412] +if sys.version_info >= (3, 13): + CONTENT_TOO_LARGE: Literal[413] +REQUEST_ENTITY_TOO_LARGE: Literal[413] +if sys.version_info >= (3, 13): + URI_TOO_LONG: Literal[414] +REQUEST_URI_TOO_LONG: Literal[414] +UNSUPPORTED_MEDIA_TYPE: Literal[415] +if sys.version_info >= (3, 13): + RANGE_NOT_SATISFIABLE: Literal[416] +REQUESTED_RANGE_NOT_SATISFIABLE: Literal[416] +EXPECTATION_FAILED: Literal[417] +if sys.version_info >= (3, 9): + IM_A_TEAPOT: Literal[418] +MISDIRECTED_REQUEST: Literal[421] +if sys.version_info >= (3, 13): + UNPROCESSABLE_CONTENT: Literal[422] +UNPROCESSABLE_ENTITY: Literal[422] +LOCKED: Literal[423] +FAILED_DEPENDENCY: Literal[424] +if sys.version_info >= (3, 9): + TOO_EARLY: Literal[425] +UPGRADE_REQUIRED: Literal[426] +PRECONDITION_REQUIRED: Literal[428] +TOO_MANY_REQUESTS: Literal[429] +REQUEST_HEADER_FIELDS_TOO_LARGE: Literal[431] +UNAVAILABLE_FOR_LEGAL_REASONS: Literal[451] + +INTERNAL_SERVER_ERROR: Literal[500] +NOT_IMPLEMENTED: Literal[501] +BAD_GATEWAY: Literal[502] +SERVICE_UNAVAILABLE: Literal[503] +GATEWAY_TIMEOUT: Literal[504] +HTTP_VERSION_NOT_SUPPORTED: Literal[505] +VARIANT_ALSO_NEGOTIATES: Literal[506] +INSUFFICIENT_STORAGE: Literal[507] +LOOP_DETECTED: Literal[508] +NOT_EXTENDED: Literal[510] +NETWORK_AUTHENTICATION_REQUIRED: Literal[511] responses: dict[int, str] diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 1eb9fc502e12..c6836c837eaa 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -416,16 +416,16 @@ class BoundArguments: def __init__(self, signature: Signature, arguments: OrderedDict[str, Any]) -> None: ... def apply_defaults(self) -> None: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] # # Classes and functions # -# TODO: The actual return type should be list[_ClassTreeItem] but mypy doesn't -# seem to be supporting this at the moment: -# _ClassTreeItem = list[_ClassTreeItem] | Tuple[type, Tuple[type, ...]] -def getclasstree(classes: list[type], unique: bool = False) -> list[Any]: ... -def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> list[Any]: ... +_ClassTreeItem: TypeAlias = list[tuple[type, ...]] | list[_ClassTreeItem] + +def getclasstree(classes: list[type], unique: bool = False) -> _ClassTreeItem: ... +def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> _ClassTreeItem: ... class Arguments(NamedTuple): args: list[str] diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index f5cee43d6b32..0563ed9b00ba 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -32,7 +32,6 @@ class _IPAddressBase: def version(self) -> int: ... class _BaseAddress(_IPAddressBase): - def __init__(self, address: object) -> None: ... def __add__(self, other: int) -> Self: ... def __hash__(self) -> int: ... def __int__(self) -> int: ... @@ -54,7 +53,6 @@ class _BaseAddress(_IPAddressBase): class _BaseNetwork(_IPAddressBase, Generic[_A]): network_address: _A netmask: _A - def __init__(self, address: object, strict: bool = ...) -> None: ... def __contains__(self, other: Any) -> bool: ... def __getitem__(self, n: int) -> _A: ... def __iter__(self) -> Iterator[_A]: ... @@ -114,6 +112,7 @@ class _BaseV4: def max_prefixlen(self) -> Literal[32]: ... class IPv4Address(_BaseV4, _BaseAddress): + def __init__(self, address: object) -> None: ... @property def is_global(self) -> bool: ... @property @@ -134,7 +133,8 @@ class IPv4Address(_BaseV4, _BaseAddress): @property def ipv6_mapped(self) -> IPv6Address: ... -class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): ... +class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): + def __init__(self, address: object, strict: bool = ...) -> None: ... class IPv4Interface(IPv4Address): netmask: IPv4Address @@ -159,6 +159,7 @@ class _BaseV6: def max_prefixlen(self) -> Literal[128]: ... class IPv6Address(_BaseV6, _BaseAddress): + def __init__(self, address: object) -> None: ... @property def is_global(self) -> bool: ... @property @@ -191,6 +192,7 @@ class IPv6Address(_BaseV6, _BaseAddress): def __eq__(self, other: object) -> bool: ... class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]): + def __init__(self, address: object, strict: bool = ...) -> None: ... @property def is_site_local(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index f69665882498..675533d44a68 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -40,29 +40,29 @@ class count(Iterator[_N]): def __iter__(self) -> Self: ... class cycle(Iterator[_T]): - def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, iterable: Iterable[_T], /) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... class repeat(Iterator[_T]): @overload - def __init__(self, object: _T) -> None: ... + def __new__(cls, object: _T) -> Self: ... @overload - def __init__(self, object: _T, times: int) -> None: ... + def __new__(cls, object: _T, times: int) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... class accumulate(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ... @overload - def __init__(self, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> None: ... + def __new__(cls, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... class chain(Iterator[_T]): - def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __new__(cls, *iterables: Iterable[_T]) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @classmethod @@ -72,17 +72,17 @@ class chain(Iterator[_T]): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class compress(Iterator[_T]): - def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... class dropwhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... class filterfalse(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -96,9 +96,9 @@ class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): class islice(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ... @overload - def __init__(self, iterable: Iterable[_T], start: int | None, stop: int | None, step: int | None = ..., /) -> None: ... + def __new__(cls, iterable: Iterable[_T], start: int | None, stop: int | None, step: int | None = ..., /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... @@ -108,7 +108,7 @@ class starmap(Iterator[_T_co]): def __next__(self) -> _T_co: ... class takewhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi index 6d9f776c61ae..5776d100d1da 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete, StrPath from collections.abc import Iterable, Iterator -from typing import IO, NoReturn, overload +from typing import IO, ClassVar, NoReturn, overload from . import grammar from .tokenize import _TokenInfo @@ -46,5 +46,6 @@ class DFAState: def addarc(self, next: DFAState, label: str) -> None: ... def unifystate(self, old: DFAState, new: DFAState) -> None: ... def __eq__(self, other: DFAState) -> bool: ... # type: ignore[override] + __hash__: ClassVar[None] # type: ignore[assignment] def generate_grammar(filename: StrPath = "Grammar.txt") -> PgenGrammar: ... diff --git a/mypy/typeshed/stdlib/lib2to3/pytree.pyi b/mypy/typeshed/stdlib/lib2to3/pytree.pyi index 138333bd58af..51bdbc75e142 100644 --- a/mypy/typeshed/stdlib/lib2to3/pytree.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pytree.pyi @@ -1,7 +1,7 @@ from _typeshed import Incomplete, SupportsGetItem, SupportsLenAndGetItem, Unused from abc import abstractmethod from collections.abc import Iterable, Iterator, MutableSequence -from typing import Final +from typing import ClassVar, Final from typing_extensions import Self, TypeAlias from .fixer_base import BaseFix @@ -24,6 +24,7 @@ class Base: was_changed: bool was_checked: bool def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @abstractmethod def _eq(self, other: Base) -> bool: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index f94e876237d1..c9b8358cde6c 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -35,8 +35,8 @@ class mmap: def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ... else: if sys.version_info >= (3, 13): - def __init__( - self, + def __new__( + cls, fileno: int, length: int, flags: int = ..., @@ -45,11 +45,11 @@ class mmap: offset: int = ..., *, trackfd: bool = True, - ) -> None: ... + ) -> Self: ... else: - def __init__( - self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... - ) -> None: ... + def __new__( + cls, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... + ) -> Self: ... def close(self) -> None: ... def flush(self, offset: int = ..., size: int = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 1669c5f09f97..ad5697e0ab1c 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -1,13 +1,14 @@ import queue import sys import threading -from _typeshed import Incomplete, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT +from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from types import TracebackType from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload from typing_extensions import Self, TypeAlias -from .connection import Connection +from . import pool +from .connection import Connection, _Address from .context import BaseContext from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory from .util import Finalize as _Finalize @@ -30,14 +31,14 @@ _Namespace: TypeAlias = Namespace class Token: typeid: str | bytes | None - address: tuple[str | bytes, int] + address: _Address | None id: str | bytes | int | None - def __init__(self, typeid: bytes | str | None, address: tuple[str | bytes, int], id: str | bytes | int | None) -> None: ... + def __init__(self, typeid: bytes | str | None, address: _Address | None, id: str | bytes | int | None) -> None: ... def __getstate__(self) -> tuple[str | bytes | None, tuple[str | bytes, int], str | bytes | int | None]: ... def __setstate__(self, state: tuple[str | bytes | None, tuple[str | bytes, int], str | bytes | int | None]) -> None: ... class BaseProxy: - _address_to_local: dict[Any, Any] + _address_to_local: dict[_Address, Any] _mutex: Any def __init__( self, @@ -129,6 +130,7 @@ class BaseListProxy(BaseProxy, MutableSequence[_T]): def __setitem__(self, s: slice, o: Iterable[_T], /) -> None: ... def __mul__(self, n: SupportsIndex, /) -> list[_T]: ... def __rmul__(self, n: SupportsIndex, /) -> list[_T]: ... + def __imul__(self, value: SupportsIndex, /) -> Self: ... def __reversed__(self) -> Iterator[_T]: ... def append(self, object: _T, /) -> None: ... def extend(self, iterable: Iterable[_T], /) -> None: ... @@ -150,22 +152,50 @@ class ListProxy(BaseListProxy[_T]): if sys.version_info >= (3, 13): def __class_getitem__(cls, args: Any, /) -> Any: ... +# Send is (kind, result) +# Receive is (id, methodname, args, kwds) +_ServerConnection: TypeAlias = Connection[tuple[str, Any], tuple[str, str, Iterable[Any], Mapping[str, Any]]] + # Returned by BaseManager.get_server() class Server: - address: Any + address: _Address | None + id_to_obj: dict[str, tuple[Any, set[str], dict[str, str]]] + fallback_mapping: dict[str, Callable[[_ServerConnection, str, Any], Any]] + public: list[str] + # Registry values are (callable, exposed, method_to_typeid, proxytype) def __init__( - self, registry: dict[str, tuple[Callable[..., Any], Any, Any, Any]], address: Any, authkey: bytes, serializer: str + self, + registry: dict[str, tuple[Callable[..., Any], Iterable[str], dict[str, str], Any]], + address: _Address | None, + authkey: bytes, + serializer: str, ) -> None: ... def serve_forever(self) -> None: ... - def accept_connection( - self, c: Connection[tuple[str, str | None], tuple[str, str, Iterable[Incomplete], Mapping[str, Incomplete]]], name: str - ) -> None: ... + def accepter(self) -> None: ... + if sys.version_info >= (3, 10): + def handle_request(self, conn: _ServerConnection) -> None: ... + else: + def handle_request(self, c: _ServerConnection) -> None: ... + + def serve_client(self, conn: _ServerConnection) -> None: ... + def fallback_getvalue(self, conn: _ServerConnection, ident: str, obj: _T) -> _T: ... + def fallback_str(self, conn: _ServerConnection, ident: str, obj: Any) -> str: ... + def fallback_repr(self, conn: _ServerConnection, ident: str, obj: Any) -> str: ... + def dummy(self, c: _ServerConnection) -> None: ... + def debug_info(self, c: _ServerConnection) -> str: ... + def number_of_objects(self, c: _ServerConnection) -> int: ... + def shutdown(self, c: _ServerConnection) -> None: ... + def create(self, c: _ServerConnection, typeid: str, /, *args: Any, **kwds: Any) -> tuple[str, tuple[str, ...]]: ... + def get_methods(self, c: _ServerConnection, token: Token) -> set[str]: ... + def accept_connection(self, c: _ServerConnection, name: str) -> None: ... + def incref(self, c: _ServerConnection, ident: str) -> None: ... + def decref(self, c: _ServerConnection, ident: str) -> None: ... class BaseManager: if sys.version_info >= (3, 11): def __init__( self, - address: Any | None = None, + address: _Address | None = None, authkey: bytes | None = None, serializer: str = "pickle", ctx: BaseContext | None = None, @@ -175,7 +205,7 @@ class BaseManager: else: def __init__( self, - address: Any | None = None, + address: _Address | None = None, authkey: bytes | None = None, serializer: str = "pickle", ctx: BaseContext | None = None, @@ -187,7 +217,7 @@ class BaseManager: shutdown: _Finalize # only available after start() was called def join(self, timeout: float | None = None) -> None: ... # undocumented @property - def address(self) -> Any: ... + def address(self) -> _Address | None: ... @classmethod def register( cls, @@ -204,14 +234,26 @@ class BaseManager: ) -> None: ... class SyncManager(BaseManager): - def BoundedSemaphore(self, value: Any = ...) -> threading.BoundedSemaphore: ... - def Condition(self, lock: Any = ...) -> threading.Condition: ... + def Barrier( + self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None + ) -> threading.Barrier: ... + def BoundedSemaphore(self, value: int = 1) -> threading.BoundedSemaphore: ... + def Condition(self, lock: threading.Lock | threading._RLock | None = None) -> threading.Condition: ... def Event(self) -> threading.Event: ... def Lock(self) -> threading.Lock: ... def Namespace(self) -> _Namespace: ... + def Pool( + self, + processes: int | None = None, + initializer: Callable[..., object] | None = None, + initargs: Iterable[Any] = (), + maxtasksperchild: int | None = None, + context: Any | None = None, + ) -> pool.Pool: ... def Queue(self, maxsize: int = ...) -> queue.Queue[Any]: ... + def JoinableQueue(self, maxsize: int = ...) -> queue.Queue[Any]: ... def RLock(self) -> threading.RLock: ... - def Semaphore(self, value: Any = ...) -> threading.Semaphore: ... + def Semaphore(self, value: int = 1) -> threading.Semaphore: ... def Array(self, typecode: Any, sequence: Sequence[_T]) -> Sequence[_T]: ... def Value(self, typecode: Any, value: _T) -> ValueProxy[_T]: ... # Overloads are copied from builtins.dict.__init__ @@ -237,7 +279,11 @@ class SyncManager(BaseManager): def list(self) -> ListProxy[Any]: ... class RemoteError(Exception): ... -class SharedMemoryServer(Server): ... + +class SharedMemoryServer(Server): + def track_segment(self, c: _ServerConnection, segment_name: str) -> None: ... + def release_segment(self, c: _ServerConnection, segment_name: str) -> None: ... + def list_segments(self, c: _ServerConnection) -> list[str]: ... class SharedMemoryManager(BaseManager): def get_server(self) -> SharedMemoryServer: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 950ed1d8c56b..93197e5d4265 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,5 +1,6 @@ import sys from collections.abc import Callable, Iterable, Iterator, Mapping +from multiprocessing.context import DefaultContext, Process from types import TracebackType from typing import Any, Final, Generic, TypeVar from typing_extensions import Self @@ -53,6 +54,8 @@ class Pool: maxtasksperchild: int | None = None, context: Any | None = None, ) -> None: ... + @staticmethod + def Process(ctx: DefaultContext, *args: Any, **kwds: Any) -> Process: ... def apply(self, func: Callable[..., _T], args: Iterable[Any] = (), kwds: Mapping[str, Any] = {}) -> _T: ... def apply_async( self, diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index e3cbfbc0ec82..a0d97baa0633 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -54,6 +54,7 @@ class RLock(SemLock): class Semaphore(SemLock): def __init__(self, value: int = 1, *, ctx: BaseContext) -> None: ... + def get_value(self) -> int: ... class BoundedSemaphore(Semaphore): def __init__(self, value: int = 1, *, ctx: BaseContext) -> None: ... diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi index e129de2cdc67..f2bca4e58bc5 100644 --- a/mypy/typeshed/stdlib/numbers.pyi +++ b/mypy/typeshed/stdlib/numbers.pyi @@ -9,7 +9,7 @@ from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Literal, Protocol, overload +from typing import ClassVar, Literal, Protocol, overload __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -102,6 +102,7 @@ class Complex(Number, _ComplexLike): def conjugate(self) -> _ComplexLike: ... @abstractmethod def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] # See comment at the top of the file # for why some of these return types are purposefully vague diff --git a/mypy/typeshed/stdlib/operator.pyi b/mypy/typeshed/stdlib/operator.pyi index b73e037f3ed9..bc2b5e026617 100644 --- a/mypy/typeshed/stdlib/operator.pyi +++ b/mypy/typeshed/stdlib/operator.pyi @@ -54,7 +54,7 @@ from _operator import ( ) from _typeshed import SupportsGetItem from typing import Any, Generic, TypeVar, final, overload -from typing_extensions import TypeVarTuple, Unpack +from typing_extensions import Self, TypeVarTuple, Unpack _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -211,5 +211,5 @@ class itemgetter(Generic[_T_co]): @final class methodcaller: - def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... + def __new__(cls, name: str, /, *args: Any, **kwargs: Any) -> Self: ... def __call__(self, obj: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index 6096ac4a2a1d..ff5e83cf26db 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,7 +1,9 @@ +import builtins from _typeshed import Incomplete, MaybeNone from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, Literal, NoReturn, overload +from typing import IO, Any, AnyStr, ClassVar, Literal, NoReturn, overload +from typing_extensions import Self __all__ = [ "Option", @@ -27,8 +29,9 @@ NO_DEFAULT: tuple[str, ...] SUPPRESS_HELP: str SUPPRESS_USAGE: str -def check_builtin(option: Option, opt, value: str): ... -def check_choice(option: Option, opt, value: str) -> str: ... +# Can return complex, float, or int depending on the option's type +def check_builtin(option: Option, opt: str, value: str) -> complex: ... +def check_choice(option: Option, opt: str, value: str) -> str: ... class OptParseError(Exception): msg: str @@ -62,9 +65,11 @@ class HelpFormatter: max_help_position: int option_strings: dict[Option, str] parser: OptionParser - short_first: Incomplete + short_first: bool | Literal[0, 1] width: int - def __init__(self, indent_increment: int, max_help_position: int, width: int | None, short_first: int) -> None: ... + def __init__( + self, indent_increment: int, max_help_position: int, width: int | None, short_first: bool | Literal[0, 1] + ) -> None: ... def dedent(self) -> None: ... def expand_default(self, option: Option) -> str: ... def format_description(self, description: str | None) -> str: ... @@ -83,14 +88,22 @@ class HelpFormatter: class IndentedHelpFormatter(HelpFormatter): def __init__( - self, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, short_first: int = 1 + self, + indent_increment: int = 2, + max_help_position: int = 24, + width: int | None = None, + short_first: bool | Literal[0, 1] = 1, ) -> None: ... def format_heading(self, heading: str) -> str: ... def format_usage(self, usage: str) -> str: ... class TitledHelpFormatter(HelpFormatter): def __init__( - self, indent_increment: int = 0, max_help_position: int = 24, width: int | None = None, short_first: int = 0 + self, + indent_increment: int = 0, + max_help_position: int = 24, + width: int | None = None, + short_first: bool | Literal[0, 1] = 0, ) -> None: ... def format_heading(self, heading: str) -> str: ... def format_usage(self, usage: str) -> str: ... @@ -99,25 +112,46 @@ class Option: ACTIONS: tuple[str, ...] ALWAYS_TYPED_ACTIONS: tuple[str, ...] ATTRS: list[str] - CHECK_METHODS: list[Callable[..., Incomplete]] | None + CHECK_METHODS: list[Callable[[Self], object]] | None CONST_ACTIONS: tuple[str, ...] STORE_ACTIONS: tuple[str, ...] TYPED_ACTIONS: tuple[str, ...] TYPES: tuple[str, ...] - TYPE_CHECKER: dict[str, Callable[[Option, str, Incomplete], Any]] + TYPE_CHECKER: dict[str, Callable[[Option, str, str], object]] _long_opts: list[str] _short_opts: list[str] action: str + type: str | None dest: str | None - default: Incomplete + default: Any # default can be "any" type nargs: int - type: Incomplete - callback: Callable[..., Incomplete] | None - callback_args: tuple[Incomplete, ...] | None - callback_kwargs: dict[str, Incomplete] | None + const: Any | None # const can be "any" type + choices: list[str] | tuple[str, ...] | None + # Callback args and kwargs cannot be expressed in Python's type system. + # Revisit if ParamSpec is ever changed to work with packed args/kwargs. + callback: Callable[..., object] | None + callback_args: tuple[Any, ...] | None + callback_kwargs: dict[str, Any] | None help: str | None metavar: str | None - def __init__(self, *opts: str | None, **attrs) -> None: ... + def __init__( + self, + *opts: str | None, + # The following keywords are handled by the _set_attrs method. All default to + # `None` except for `default`, which defaults to `NO_DEFAULT`. + action: str | None = None, + type: str | builtins.type | None = None, + dest: str | None = None, + default: Any = ..., # = NO_DEFAULT + nargs: int | None = None, + const: Any | None = None, + choices: list[str] | tuple[str, ...] | None = None, + callback: Callable[..., object] | None = None, + callback_args: tuple[Any, ...] | None = None, + callback_kwargs: dict[str, Any] | None = None, + help: str | None = None, + metavar: str | None = None, + ) -> None: ... def _check_action(self) -> None: ... def _check_callback(self) -> None: ... def _check_choice(self) -> None: ... @@ -126,13 +160,14 @@ class Option: def _check_nargs(self) -> None: ... def _check_opt_strings(self, opts: Iterable[str | None]) -> list[str]: ... def _check_type(self) -> None: ... - def _set_attrs(self, attrs: dict[str, Incomplete]) -> None: ... + def _set_attrs(self, attrs: dict[str, Any]) -> None: ... # accepted attrs depend on the ATTRS attribute def _set_opt_strings(self, opts: Iterable[str]) -> None: ... - def check_value(self, opt: str, value): ... - def convert_value(self, opt: str, value): ... + def check_value(self, opt: str, value: str) -> Any: ... # return type cannot be known statically + def convert_value(self, opt: str, value: str | tuple[str, ...] | None) -> Any: ... # return type cannot be known statically def get_opt_string(self) -> str: ... - def process(self, opt, value, values, parser: OptionParser) -> int: ... - def take_action(self, action: str, dest: str, opt, value, values, parser: OptionParser) -> int: ... + def process(self, opt: str, value: str | tuple[str, ...] | None, values: Values, parser: OptionParser) -> int: ... + # value of take_action can be "any" type + def take_action(self, action: str, dest: str, opt: str, value: Any, values: Values, parser: OptionParser) -> int: ... def takes_value(self) -> bool: ... make_option = Option @@ -141,7 +176,7 @@ class OptionContainer: _long_opt: dict[str, Option] _short_opt: dict[str, Option] conflict_handler: str - defaults: dict[str, Incomplete] + defaults: dict[str, Any] # default values can be "any" type description: str | None option_class: type[Option] def __init__( @@ -153,7 +188,25 @@ class OptionContainer: @overload def add_option(self, opt: Option, /) -> Option: ... @overload - def add_option(self, arg: str, /, *args: str | None, **kwargs) -> Option: ... + def add_option( + self, + opt_str: str, + /, + *opts: str | None, + action: str | None = None, + type: str | builtins.type | None = None, + dest: str | None = None, + default: Any = ..., # = NO_DEFAULT + nargs: int | None = None, + const: Any | None = None, + choices: list[str] | tuple[str, ...] | None = None, + callback: Callable[..., object] | None = None, + callback_args: tuple[Any, ...] | None = None, + callback_kwargs: dict[str, Any] | None = None, + help: str | None = None, + metavar: str | None = None, + **kwargs, # Allow arbitrary keyword arguments for user defined option_class + ) -> Option: ... def add_options(self, option_list: Iterable[Option]) -> None: ... def destroy(self) -> None: ... def format_option_help(self, formatter: HelpFormatter) -> str: ... @@ -175,17 +228,19 @@ class OptionGroup(OptionContainer): def set_title(self, title: str) -> None: ... class Values: - def __init__(self, defaults: Mapping[str, Incomplete] | None = None) -> None: ... - def _update(self, dict: Mapping[str, Incomplete], mode) -> None: ... - def _update_careful(self, dict: Mapping[str, Incomplete]) -> None: ... - def _update_loose(self, dict: Mapping[str, Incomplete]) -> None: ... - def ensure_value(self, attr: str, value): ... - def read_file(self, filename: str, mode: str = "careful") -> None: ... - def read_module(self, modname: str, mode: str = "careful") -> None: ... + def __init__(self, defaults: Mapping[str, object] | None = None) -> None: ... + def _update(self, dict: Mapping[str, object], mode: Literal["careful", "loose"]) -> None: ... + def _update_careful(self, dict: Mapping[str, object]) -> None: ... + def _update_loose(self, dict: Mapping[str, object]) -> None: ... + def ensure_value(self, attr: str, value: object) -> Any: ... # return type cannot be known statically + def read_file(self, filename: str, mode: Literal["careful", "loose"] = "careful") -> None: ... + def read_module(self, modname: str, mode: Literal["careful", "loose"] = "careful") -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] # __getattr__ doesn't exist, but anything passed as a default to __init__ # is set on the instance. - def __getattr__(self, name: str): ... - def __setattr__(self, name: str, value, /) -> None: ... + def __getattr__(self, name: str) -> Any: ... + # TODO mypy infers -> object for __getattr__ if __setattr__ has `value: object` + def __setattr__(self, name: str, value: Any, /) -> None: ... def __eq__(self, other: object) -> bool: ... class OptionParser(OptionContainer): @@ -229,7 +284,7 @@ class OptionParser(OptionContainer): @overload def add_option_group(self, opt_group: OptionGroup, /) -> OptionGroup: ... @overload - def add_option_group(self, *args, **kwargs) -> OptionGroup: ... + def add_option_group(self, title: str, /, description: str | None = None) -> OptionGroup: ... def check_values(self, values: Values, args: list[str]) -> tuple[Values, list[str]]: ... def disable_interspersed_args(self) -> None: ... def enable_interspersed_args(self) -> None: ... diff --git a/mypy/typeshed/stdlib/parser.pyi b/mypy/typeshed/stdlib/parser.pyi index bafc8015fed9..26140c76248a 100644 --- a/mypy/typeshed/stdlib/parser.pyi +++ b/mypy/typeshed/stdlib/parser.pyi @@ -1,7 +1,7 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from types import CodeType -from typing import Any, final +from typing import Any, ClassVar, final def expr(source: str) -> STType: ... def suite(source: str) -> STType: ... @@ -17,6 +17,7 @@ class ParserError(Exception): ... @final class STType: + __hash__: ClassVar[None] # type: ignore[assignment] def compile(self, filename: StrOrBytesPath = ...) -> CodeType: ... def isexpr(self) -> bool: ... def issuite(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 5e398f2d4921..2d80d61645e0 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -15,6 +15,7 @@ from _pickle import ( from _typeshed import ReadableBuffer, SupportsWrite from collections.abc import Callable, Iterable, Mapping from typing import Any, ClassVar, SupportsBytes, SupportsIndex, final +from typing_extensions import Self __all__ = [ "PickleBuffer", @@ -108,7 +109,7 @@ bytes_types: tuple[type[Any], ...] # undocumented @final class PickleBuffer: - def __init__(self, buffer: ReadableBuffer) -> None: ... + def __new__(cls, buffer: ReadableBuffer) -> Self: ... def raw(self) -> memoryview: ... def release(self) -> None: ... def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index 09637673ce21..72b5398f0a52 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer from collections.abc import Mapping, MutableMapping from datetime import datetime from enum import Enum -from typing import IO, Any +from typing import IO, Any, ClassVar from typing_extensions import Self __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"] @@ -100,6 +100,7 @@ if sys.version_info < (3, 9): class Data: data: bytes def __init__(self, data: bytes) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] class UID: data: int diff --git a/mypy/typeshed/stdlib/sched.pyi b/mypy/typeshed/stdlib/sched.pyi index ace501430847..52f87ab68ff5 100644 --- a/mypy/typeshed/stdlib/sched.pyi +++ b/mypy/typeshed/stdlib/sched.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from typing import Any, NamedTuple, type_check_only +from typing import Any, ClassVar, NamedTuple, type_check_only from typing_extensions import TypeAlias __all__ = ["scheduler"] @@ -25,7 +25,8 @@ else: argument: tuple[Any, ...] kwargs: dict[str, Any] - class Event(_EventBase): ... + class Event(_EventBase): + __hash__: ClassVar[None] # type: ignore[assignment] class scheduler: timefunc: Callable[[], float] diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index 67203905ab66..42941b9e41fa 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import FileDescriptorLike from collections.abc import Iterable from types import TracebackType -from typing import Any, final +from typing import Any, ClassVar, final from typing_extensions import Self if sys.platform != "win32": @@ -22,11 +22,14 @@ if sys.platform != "win32": POLLWRBAND: int POLLWRNORM: int -class poll: - def register(self, fd: FileDescriptorLike, eventmask: int = ...) -> None: ... - def modify(self, fd: FileDescriptorLike, eventmask: int) -> None: ... - def unregister(self, fd: FileDescriptorLike) -> None: ... - def poll(self, timeout: float | None = ...) -> list[tuple[int, int]]: ... + # This is actually a function that returns an instance of a class. + # The class is not accessible directly, and also calls itself select.poll. + class poll: + # default value is select.POLLIN | select.POLLPRI | select.POLLOUT + def register(self, fd: FileDescriptorLike, eventmask: int = 7, /) -> None: ... + def modify(self, fd: FileDescriptorLike, eventmask: int, /) -> None: ... + def unregister(self, fd: FileDescriptorLike, /) -> None: ... + def poll(self, timeout: float | None = None, /) -> list[tuple[int, int]]: ... def select( rlist: Iterable[Any], wlist: Iterable[Any], xlist: Iterable[Any], timeout: float | None = None, / @@ -53,6 +56,7 @@ if sys.platform != "linux" and sys.platform != "win32": data: Any = ..., udata: Any = ..., ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] # BSD only @final diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index 7dad0c13bf2a..0ba843a403d8 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -50,8 +50,10 @@ if sys.platform == "linux": class EpollSelector(_PollLikeSelector): def fileno(self) -> int: ... -class DevpollSelector(_PollLikeSelector): - def fileno(self) -> int: ... +if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": + # Solaris only + class DevpollSelector(_PollLikeSelector): + def fileno(self) -> int: ... if sys.platform != "win32" and sys.platform != "linux": class KqueueSelector(_BaseSelectorImpl): diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 2e3ac5bf24c3..8fc853b25cc1 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -3,7 +3,7 @@ from _typeshed import structseq from collections.abc import Callable, Iterable from enum import IntEnum from types import FrameType -from typing import Any, Final, final +from typing import Any, Final, Literal, final from typing_extensions import Never, TypeAlias NSIG: int @@ -61,8 +61,8 @@ class Handlers(IntEnum): SIG_DFL = 0 SIG_IGN = 1 -SIG_DFL: Handlers -SIG_IGN: Handlers +SIG_DFL: Literal[Handlers.SIG_DFL] +SIG_IGN: Literal[Handlers.SIG_IGN] _SIGNUM: TypeAlias = int | Signals _HANDLER: TypeAlias = Callable[[int, FrameType | None], Any] | int | Handlers | None @@ -77,45 +77,45 @@ else: def getsignal(signalnum: _SIGNUM, /) -> _HANDLER: ... def signal(signalnum: _SIGNUM, handler: _HANDLER, /) -> _HANDLER: ... -SIGABRT: Signals -SIGFPE: Signals -SIGILL: Signals -SIGINT: Signals -SIGSEGV: Signals -SIGTERM: Signals +SIGABRT: Literal[Signals.SIGABRT] +SIGFPE: Literal[Signals.SIGFPE] +SIGILL: Literal[Signals.SIGILL] +SIGINT: Literal[Signals.SIGINT] +SIGSEGV: Literal[Signals.SIGSEGV] +SIGTERM: Literal[Signals.SIGTERM] if sys.platform == "win32": - SIGBREAK: Signals - CTRL_C_EVENT: Signals - CTRL_BREAK_EVENT: Signals + SIGBREAK: Literal[Signals.SIGBREAK] + CTRL_C_EVENT: Literal[Signals.CTRL_C_EVENT] + CTRL_BREAK_EVENT: Literal[Signals.CTRL_BREAK_EVENT] else: if sys.platform != "linux": - SIGINFO: Signals - SIGEMT: Signals - SIGALRM: Signals - SIGBUS: Signals - SIGCHLD: Signals - SIGCONT: Signals - SIGHUP: Signals - SIGIO: Signals - SIGIOT: Signals - SIGKILL: Signals - SIGPIPE: Signals - SIGPROF: Signals - SIGQUIT: Signals - SIGSTOP: Signals - SIGSYS: Signals - SIGTRAP: Signals - SIGTSTP: Signals - SIGTTIN: Signals - SIGTTOU: Signals - SIGURG: Signals - SIGUSR1: Signals - SIGUSR2: Signals - SIGVTALRM: Signals - SIGWINCH: Signals - SIGXCPU: Signals - SIGXFSZ: Signals + SIGINFO: Literal[Signals.SIGINFO] + SIGEMT: Literal[Signals.SIGEMT] + SIGALRM: Literal[Signals.SIGALRM] + SIGBUS: Literal[Signals.SIGBUS] + SIGCHLD: Literal[Signals.SIGCHLD] + SIGCONT: Literal[Signals.SIGCONT] + SIGHUP: Literal[Signals.SIGHUP] + SIGIO: Literal[Signals.SIGIO] + SIGIOT: Literal[Signals.SIGABRT] # alias + SIGKILL: Literal[Signals.SIGKILL] + SIGPIPE: Literal[Signals.SIGPIPE] + SIGPROF: Literal[Signals.SIGPROF] + SIGQUIT: Literal[Signals.SIGQUIT] + SIGSTOP: Literal[Signals.SIGSTOP] + SIGSYS: Literal[Signals.SIGSYS] + SIGTRAP: Literal[Signals.SIGTRAP] + SIGTSTP: Literal[Signals.SIGTSTP] + SIGTTIN: Literal[Signals.SIGTTIN] + SIGTTOU: Literal[Signals.SIGTTOU] + SIGURG: Literal[Signals.SIGURG] + SIGUSR1: Literal[Signals.SIGUSR1] + SIGUSR2: Literal[Signals.SIGUSR2] + SIGVTALRM: Literal[Signals.SIGVTALRM] + SIGWINCH: Literal[Signals.SIGWINCH] + SIGXCPU: Literal[Signals.SIGXCPU] + SIGXFSZ: Literal[Signals.SIGXFSZ] class ItimerError(OSError): ... ITIMER_PROF: int @@ -127,9 +127,9 @@ else: SIG_UNBLOCK = 1 SIG_SETMASK = 2 - SIG_BLOCK = Sigmasks.SIG_BLOCK - SIG_UNBLOCK = Sigmasks.SIG_UNBLOCK - SIG_SETMASK = Sigmasks.SIG_SETMASK + SIG_BLOCK: Literal[Sigmasks.SIG_BLOCK] + SIG_UNBLOCK: Literal[Sigmasks.SIG_UNBLOCK] + SIG_SETMASK: Literal[Sigmasks.SIG_SETMASK] def alarm(seconds: int, /) -> int: ... def getitimer(which: int, /) -> tuple[float, float]: ... def pause() -> None: ... @@ -147,13 +147,13 @@ else: else: def sigwait(sigset: Iterable[int], /) -> _SIGNUM: ... if sys.platform != "darwin": - SIGCLD: Signals - SIGPOLL: Signals - SIGPWR: Signals - SIGRTMAX: Signals - SIGRTMIN: Signals + SIGCLD: Literal[Signals.SIGCHLD] # alias + SIGPOLL: Literal[Signals.SIGIO] # alias + SIGPWR: Literal[Signals.SIGPWR] + SIGRTMAX: Literal[Signals.SIGRTMAX] + SIGRTMIN: Literal[Signals.SIGRTMIN] if sys.version_info >= (3, 11): - SIGSTKFLT: Signals + SIGSTKFLT: Literal[Signals.SIGSTKFLT] @final class struct_siginfo(structseq[int], tuple[int, int, int, int, int, int, int]): diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index 730404bde218..724bc3166fd0 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -429,7 +429,7 @@ class PrepareProtocol: def __init__(self, *args: object, **kwargs: object) -> None: ... class Row(Sequence[Any]): - def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... + def __new__(cls, cursor: Cursor, data: tuple[Any, ...], /) -> Self: ... def keys(self) -> list[str]: ... @overload def __getitem__(self, key: int | str, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index f587b51d5ac0..388e521c1ef5 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -325,6 +325,10 @@ class _ASN1Object(_ASN1ObjectBase): def fromname(cls, name: str) -> Self: ... class Purpose(_ASN1Object, enum.Enum): + # Normally this class would inherit __new__ from _ASN1Object, but + # because this is an enum, the inherited __new__ is replaced at runtime with + # Enum.__new__. + def __new__(cls, value: object) -> Self: ... SERVER_AUTH = (129, "serverAuth", "TLS Web Server Authentication", "1.3.6.1.5.5.7.3.2") # pyright: ignore[reportCallIssue] CLIENT_AUTH = (130, "clientAuth", "TLS Web Client Authentication", "1.3.6.1.5.5.7.3.1") # pyright: ignore[reportCallIssue] diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index c441a04681e2..efeea69d0234 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -100,30 +100,22 @@ class Thread: class _DummyThread(Thread): def __init__(self) -> None: ... -@final -class Lock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def locked(self) -> bool: ... - def acquire_lock(self, blocking: bool = ..., timeout: float = ...) -> bool: ... # undocumented - def release_lock(self) -> None: ... # undocumented - def locked_lock(self) -> bool: ... # undocumented +# This is actually the function _thread.allocate_lock for <= 3.12 +Lock = _thread.LockType +# Python implementation of RLock. @final class _RLock: + _count: int def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... __enter__ = acquire def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... -RLock = _RLock +RLock = _thread.RLock # Actually a function at runtime. class Condition: - def __init__(self, lock: Lock | _RLock | None = None) -> None: ... + def __init__(self, lock: Lock | _RLock | RLock | None = None) -> None: ... def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index a9ec97c45b40..751de523bf7a 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only +from typing import Any, ClassVar, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated if sys.version_info >= (3, 11): @@ -330,6 +330,7 @@ class Variable: def trace_vinfo(self): ... def __eq__(self, other: object) -> bool: ... def __del__(self) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] class StringVar(Variable): def __init__(self, master: Misc | None = None, value: str | None = None, name: str | None = None) -> None: ... @@ -370,6 +371,9 @@ class _GridIndexInfo(TypedDict, total=False): uniform: str | None weight: int +class _BusyInfo(TypedDict): + cursor: _Cursor + class Misc: master: Misc | None tk: _tkinter.TkappType @@ -407,6 +411,25 @@ class Misc: def after_info(self, id: str | None = None) -> tuple[str, ...]: ... def bell(self, displayof: Literal[0] | Misc | None = 0) -> None: ... + if sys.version_info >= (3, 13): + # Supports options from `_BusyInfo`` + def tk_busy_cget(self, option: Literal["cursor"]) -> _Cursor: ... + busy_cget = tk_busy_cget + def tk_busy_configure(self, cnf: Any = None, **kw: Any) -> Any: ... + tk_busy_config = tk_busy_configure + busy_configure = tk_busy_configure + busy_config = tk_busy_configure + def tk_busy_current(self, pattern: str | None = None) -> list[Misc]: ... + busy_current = tk_busy_current + def tk_busy_forget(self) -> None: ... + busy_forget = tk_busy_forget + def tk_busy_hold(self, **kw: Unpack[_BusyInfo]) -> None: ... + tk_busy = tk_busy_hold + busy_hold = tk_busy_hold + busy = tk_busy_hold + def tk_busy_status(self) -> bool: ... + busy_status = tk_busy_status + def clipboard_get(self, *, displayof: Misc = ..., type: str = ...) -> str: ... def clipboard_clear(self, *, displayof: Misc = ...) -> None: ... def clipboard_append(self, string: str, *, displayof: Misc = ..., format: str = ..., type: str = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 097c2e4b4382..3b73f982c4ca 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -3,7 +3,7 @@ import itertools import sys import tkinter from typing import Any, ClassVar, Final, Literal, TypedDict, overload -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, Unpack if sys.version_info >= (3, 9): __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] @@ -18,9 +18,9 @@ _FontDescription: TypeAlias = ( | Font # A font object constructed in Python | list[Any] # ["Helvetica", 12, BOLD] | tuple[str] # ("Liberation Sans",) needs wrapping in tuple/list to handle spaces - | tuple[str, int] # ("Liberation Sans", 12) - | tuple[str, int, str] # ("Liberation Sans", 12, "bold") - | tuple[str, int, list[str] | tuple[str, ...]] # e.g. bold and italic + # ("Liberation Sans", 12) or ("Liberation Sans", 12, "bold", "italic", "underline") + | tuple[str, int, Unpack[tuple[str, ...]]] # Any number of trailing options is permitted + | tuple[str, int, list[str] | tuple[str, ...]] # Options can also be passed as list/tuple | _tkinter.Tcl_Obj # A font object constructed in Tcl ) @@ -58,6 +58,7 @@ class Font: underline: bool = ..., overstrike: bool = ..., ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __setitem__(self, key: str, value: Any) -> None: ... @overload def cget(self, option: Literal["family"]) -> str: ... diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index e36081acfa03..4f132d51c617 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import SupportsWrite, Unused from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType -from typing import Any, Literal, overload +from typing import Any, ClassVar, Literal, overload from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -113,17 +113,26 @@ if sys.version_info >= (3, 11): def emit(self, text_gen: str | Iterable[str], margin_char: str | None = None) -> Generator[str, None, None]: ... class TracebackException: - __cause__: TracebackException - __context__: TracebackException + __cause__: TracebackException | None + __context__: TracebackException | None if sys.version_info >= (3, 11): exceptions: list[TracebackException] | None __suppress_context__: bool + if sys.version_info >= (3, 11): + __notes__: list[str] | None stack: StackSummary + + # These fields only exist for `SyntaxError`s, but there is no way to express that in the type system. filename: str - lineno: int + lineno: str | None + if sys.version_info >= (3, 10): + end_lineno: str | None text: str offset: int + if sys.version_info >= (3, 10): + end_offset: int | None msg: str + if sys.version_info >= (3, 13): @property def exc_type_str(self) -> str: ... @@ -220,6 +229,7 @@ class TracebackException: ) -> Self: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 11): def format(self, *, chain: bool = True, _ctx: _ExceptionPrintContext | None = None) -> Generator[str, None, None]: ... else: @@ -283,6 +293,7 @@ class FrameSummary: def __iter__(self) -> Iterator[Any]: ... def __eq__(self, other: object) -> bool: ... def __len__(self) -> Literal[4]: ... + __hash__: ClassVar[None] # type: ignore[assignment] class StackSummary(list[FrameSummary]): @classmethod diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index b513bd77468a..b294a0b2f8f7 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -89,14 +89,26 @@ class FunctionType: __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str - def __new__( - cls, - code: CodeType, - globals: dict[str, Any], - name: str | None = ..., - argdefs: tuple[object, ...] | None = ..., - closure: tuple[CellType, ...] | None = ..., - ) -> Self: ... + if sys.version_info >= (3, 13): + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + kwdefaults: dict[str, object] | None = None, + ) -> Self: ... + else: + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + ) -> Self: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @overload def __get__(self, instance: None, owner: type, /) -> FunctionType: ... @@ -362,6 +374,12 @@ _ReturnT_co = TypeVar("_ReturnT_co", covariant=True) @final class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): + @property + def gi_code(self) -> CodeType: ... + @property + def gi_frame(self) -> FrameType: ... + @property + def gi_running(self) -> bool: ... @property def gi_yieldfrom(self) -> GeneratorType[_YieldT_co, _SendT_contra, Any] | None: ... if sys.version_info >= (3, 11): @@ -385,6 +403,12 @@ class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @property def ag_await(self) -> Awaitable[Any] | None: ... + @property + def ag_code(self) -> CodeType: ... + @property + def ag_frame(self) -> FrameType: ... + @property + def ag_running(self) -> bool: ... __name__: str __qualname__: str if sys.version_info >= (3, 12): @@ -409,6 +433,14 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): __name__: str __qualname__: str @property + def cr_await(self) -> Any | None: ... + @property + def cr_code(self) -> CodeType: ... + @property + def cr_frame(self) -> FrameType: ... + @property + def cr_running(self) -> bool: ... + @property def cr_origin(self) -> tuple[tuple[str, int, str], ...] | None: ... if sys.version_info >= (3, 11): @property @@ -442,7 +474,7 @@ class MethodType: def __name__(self) -> str: ... # inherited from the added function @property def __qualname__(self) -> str: ... # inherited from the added function - def __new__(cls, func: Callable[..., Any], obj: object, /) -> Self: ... + def __new__(cls, func: Callable[..., Any], instance: object, /) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @@ -604,7 +636,7 @@ if sys.version_info >= (3, 9): def __args__(self) -> tuple[Any, ...]: ... @property def __parameters__(self) -> tuple[Any, ...]: ... - def __new__(cls, origin: type, args: Any) -> Self: ... + def __new__(cls, origin: type, args: Any, /) -> Self: ... def __getitem__(self, typeargs: Any, /) -> GenericAlias: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 741e7b8a3167..7c1b171a730b 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -12,7 +12,6 @@ from re import Match as Match, Pattern as Pattern from types import ( BuiltinFunctionType, CodeType, - FrameType, FunctionType, MethodDescriptorType, MethodType, @@ -155,8 +154,8 @@ class TypeVar: @property def __default__(self) -> Any: ... if sys.version_info >= (3, 13): - def __init__( - self, + def __new__( + cls, name: str, *constraints: Any, bound: Any | None = None, @@ -164,17 +163,21 @@ class TypeVar: covariant: bool = False, infer_variance: bool = False, default: Any = ..., - ) -> None: ... + ) -> Self: ... elif sys.version_info >= (3, 12): - def __init__( - self, + def __new__( + cls, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False, infer_variance: bool = False, - ) -> None: ... + ) -> Self: ... + elif sys.version_info >= (3, 11): + def __new__( + cls, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False + ) -> Self: ... else: def __init__( self, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False @@ -232,7 +235,9 @@ if sys.version_info >= (3, 11): def __default__(self) -> Any: ... def has_default(self) -> bool: ... if sys.version_info >= (3, 13): - def __init__(self, name: str, *, default: Any = ...) -> None: ... + def __new__(cls, name: str, *, default: Any = ...) -> Self: ... + elif sys.version_info >= (3, 12): + def __new__(cls, name: str) -> Self: ... else: def __init__(self, name: str) -> None: ... @@ -245,15 +250,25 @@ if sys.version_info >= (3, 10): class ParamSpecArgs: @property def __origin__(self) -> ParamSpec: ... - def __init__(self, origin: ParamSpec) -> None: ... + if sys.version_info >= (3, 12): + def __new__(cls, origin: ParamSpec) -> Self: ... + else: + def __init__(self, origin: ParamSpec) -> None: ... + def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @final class ParamSpecKwargs: @property def __origin__(self) -> ParamSpec: ... - def __init__(self, origin: ParamSpec) -> None: ... + if sys.version_info >= (3, 12): + def __new__(cls, origin: ParamSpec) -> Self: ... + else: + def __init__(self, origin: ParamSpec) -> None: ... + def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @final class ParamSpec: @@ -272,8 +287,8 @@ if sys.version_info >= (3, 10): @property def __default__(self) -> Any: ... if sys.version_info >= (3, 13): - def __init__( - self, + def __new__( + cls, name: str, *, bound: Any | None = None, @@ -281,17 +296,21 @@ if sys.version_info >= (3, 10): covariant: bool = False, infer_variance: bool = False, default: Any = ..., - ) -> None: ... + ) -> Self: ... elif sys.version_info >= (3, 12): - def __init__( - self, + def __new__( + cls, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False, infer_variance: bool = False, - ) -> None: ... + ) -> Self: ... + elif sys.version_info >= (3, 11): + def __new__( + cls, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False + ) -> Self: ... else: def __init__( self, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False @@ -333,6 +352,8 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _P = _ParamSpec("_P") _T = TypeVar("_T") +_FT = TypeVar("_FT", bound=Callable[..., Any] | type) + # These type variables are used by the container types. _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. @@ -347,7 +368,7 @@ def no_type_check(arg: _F) -> _F: ... def no_type_check_decorator(decorator: Callable[_P, _T]) -> Callable[_P, _T]: ... # This itself is only available during type checking -def type_check_only(func_or_cls: _F) -> _F: ... +def type_check_only(func_or_cls: _FT) -> _FT: ... # Type aliases and type constructors @@ -451,7 +472,8 @@ _YieldT_co = TypeVar("_YieldT_co", covariant=True) _SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) _ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) -class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): +@runtime_checkable +class Generator(Iterator[_YieldT_co], Protocol[_YieldT_co, _SendT_contra, _ReturnT_co]): def __next__(self) -> _YieldT_co: ... @abstractmethod def send(self, value: _SendT_contra, /) -> _YieldT_co: ... @@ -469,14 +491,6 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return def close(self) -> None: ... def __iter__(self) -> Generator[_YieldT_co, _SendT_contra, _ReturnT_co]: ... - @property - def gi_code(self) -> CodeType: ... - @property - def gi_frame(self) -> FrameType: ... - @property - def gi_running(self) -> bool: ... - @property - def gi_yieldfrom(self) -> Generator[Any, Any, Any] | None: ... # NOTE: Prior to Python 3.13 these aliases are lacking the second _ExitT_co parameter if sys.version_info >= (3, 13): @@ -502,14 +516,7 @@ _ReturnT_co_nd = TypeVar("_ReturnT_co_nd", covariant=True) class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd]): __name__: str __qualname__: str - @property - def cr_await(self) -> Any | None: ... - @property - def cr_code(self) -> CodeType: ... - @property - def cr_frame(self) -> FrameType | None: ... - @property - def cr_running(self) -> bool: ... + @abstractmethod def send(self, value: _SendT_contra_nd, /) -> _YieldT_co: ... @overload @@ -544,7 +551,8 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): def __anext__(self) -> Awaitable[_T_co]: ... def __aiter__(self) -> AsyncIterator[_T_co]: ... -class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): +@runtime_checkable +class AsyncGenerator(AsyncIterator[_YieldT_co], Protocol[_YieldT_co, _SendT_contra]): def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... @abstractmethod def asend(self, value: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @@ -559,14 +567,6 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contr self, typ: BaseException, val: None = None, tb: TracebackType | None = None, / ) -> Coroutine[Any, Any, _YieldT_co]: ... def aclose(self) -> Coroutine[Any, Any, None]: ... - @property - def ag_await(self) -> Any: ... - @property - def ag_code(self) -> CodeType: ... - @property - def ag_frame(self) -> FrameType: ... - @property - def ag_running(self) -> bool: ... @runtime_checkable class Container(Protocol[_T_co]): @@ -1039,9 +1039,7 @@ if sys.version_info >= (3, 12): def override(method: _F, /) -> _F: ... @final class TypeAliasType: - def __init__( - self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () - ) -> None: ... + def __new__(cls, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()) -> Self: ... @property def __value__(self) -> Any: ... @property diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index a6b606e6b670..33af1a388aa5 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -1,3 +1,5 @@ +# Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self +# ruff: noqa: PYI034 import abc import sys import typing @@ -48,12 +50,6 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 Sequence as Sequence, Set as Set, Sized as Sized, - SupportsAbs as SupportsAbs, - SupportsBytes as SupportsBytes, - SupportsComplex as SupportsComplex, - SupportsFloat as SupportsFloat, - SupportsInt as SupportsInt, - SupportsRound as SupportsRound, Text as Text, TextIO as TextIO, Tuple as Tuple, @@ -190,6 +186,7 @@ __all__ = [ _T = typing.TypeVar("_T") _F = typing.TypeVar("_F", bound=Callable[..., Any]) _TC = typing.TypeVar("_TC", bound=type[object]) +_T_co = typing.TypeVar("_T_co", covariant=True) # Any type covariant containers. class _Final: ... # This should be imported from typing but that breaks pytype @@ -282,11 +279,6 @@ def get_origin(tp: Any) -> Any | None: ... Annotated: _SpecialForm _AnnotatedAlias: Any # undocumented -@runtime_checkable -class SupportsIndex(Protocol, metaclass=abc.ABCMeta): - @abc.abstractmethod - def __index__(self) -> int: ... - # New and changed things in 3.10 if sys.version_info >= (3, 10): from typing import ( @@ -383,7 +375,17 @@ else: if sys.version_info >= (3, 12): from collections.abc import Buffer as Buffer from types import get_original_bases as get_original_bases - from typing import TypeAliasType as TypeAliasType, override as override + from typing import ( + SupportsAbs as SupportsAbs, + SupportsBytes as SupportsBytes, + SupportsComplex as SupportsComplex, + SupportsFloat as SupportsFloat, + SupportsIndex as SupportsIndex, + SupportsInt as SupportsInt, + SupportsRound as SupportsRound, + TypeAliasType as TypeAliasType, + override as override, + ) else: def override(arg: _F, /) -> _F: ... def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... @@ -418,6 +420,45 @@ else: # https://github.com/python/typeshed/issues/10224 for why we're defining it this way def __buffer__(self, flags: int, /) -> memoryview: ... + @runtime_checkable + class SupportsInt(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def __int__(self) -> int: ... + + @runtime_checkable + class SupportsFloat(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def __float__(self) -> float: ... + + @runtime_checkable + class SupportsComplex(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def __complex__(self) -> complex: ... + + @runtime_checkable + class SupportsBytes(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def __bytes__(self) -> bytes: ... + + @runtime_checkable + class SupportsIndex(Protocol, metaclass=abc.ABCMeta): + @abc.abstractmethod + def __index__(self) -> int: ... + + @runtime_checkable + class SupportsAbs(Protocol[_T_co]): + @abc.abstractmethod + def __abs__(self) -> _T_co: ... + + @runtime_checkable + class SupportsRound(Protocol[_T_co]): + @overload + @abc.abstractmethod + def __round__(self) -> int: ... + @overload + @abc.abstractmethod + def __round__(self, ndigits: int, /) -> _T_co: ... + if sys.version_info >= (3, 13): from types import CapsuleType as CapsuleType from typing import ( diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 193a4123c395..4b32f15095d6 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -2,7 +2,7 @@ import sys from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, Sequence from contextlib import _GeneratorContextManager from types import TracebackType -from typing import Any, Final, Generic, Literal, TypeVar, overload +from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload from typing_extensions import ParamSpec, Self, TypeAlias _T = TypeVar("_T") @@ -85,6 +85,7 @@ class _Call(tuple[Any, ...]): two: bool = False, from_kall: bool = True, ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... def __ne__(self, value: object, /) -> bool: ... def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... @@ -403,6 +404,7 @@ class MagicProxy(Base): class _ANY: def __eq__(self, other: object) -> Literal[True]: ... def __ne__(self, other: object) -> Literal[False]: ... + __hash__: ClassVar[None] # type: ignore[assignment] ANY: Any diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 393d03dfa0fc..783764464a53 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -6,21 +6,22 @@ from _typeshed import SupportsFlush, SupportsWrite from collections.abc import Callable, Iterable from typing import Any, Generic, Protocol, TypeVar from typing_extensions import Never, TypeAlias +from warnings import _ActionKind -_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult] +_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult[Any]] class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ... # All methods used by unittest.runner.TextTestResult's stream class _TextTestStream(_SupportsWriteAndFlush, Protocol): - def writeln(self, arg: str | None = None, /) -> str: ... + def writeln(self, arg: str | None = None, /) -> None: ... # _WritelnDecorator should have all the same attrs as its stream param. # But that's not feasible to do Generically # We can expand the attributes if requested class _WritelnDecorator: - def __init__(self, stream: _TextTestStream) -> None: ... - def writeln(self, arg: str | None = None) -> str: ... + def __init__(self, stream: _SupportsWriteAndFlush) -> None: ... + def writeln(self, arg: str | None = None) -> None: ... def __getattr__(self, attr: str) -> Any: ... # Any attribute from the stream type passed to __init__ # These attributes are prevented by __getattr__ stream: Never @@ -39,10 +40,8 @@ class TextTestResult(unittest.result.TestResult, Generic[_StreamT]): showAll: bool # undocumented stream: _StreamT # undocumented if sys.version_info >= (3, 12): - durations: unittest.result._DurationsType | None - def __init__( - self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None - ) -> None: ... + durations: int | None + def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: int | None = None) -> None: ... else: def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int) -> None: ... @@ -56,11 +55,11 @@ class TextTestRunner: verbosity: int failfast: bool buffer: bool - warnings: str | None + warnings: _ActionKind | None tb_locals: bool if sys.version_info >= (3, 12): - durations: unittest.result._DurationsType | None + durations: int | None def __init__( self, stream: _SupportsWriteAndFlush | None = None, @@ -69,10 +68,10 @@ class TextTestRunner: failfast: bool = False, buffer: bool = False, resultclass: _ResultClassType | None = None, - warnings: str | None = None, + warnings: _ActionKind | None = None, *, tb_locals: bool = False, - durations: unittest.result._DurationsType | None = None, + durations: int | None = None, ) -> None: ... else: def __init__( diff --git a/mypy/typeshed/stdlib/unittest/suite.pyi b/mypy/typeshed/stdlib/unittest/suite.pyi index ff583d0766a0..443396164b6f 100644 --- a/mypy/typeshed/stdlib/unittest/suite.pyi +++ b/mypy/typeshed/stdlib/unittest/suite.pyi @@ -1,6 +1,7 @@ import unittest.case import unittest.result from collections.abc import Iterable, Iterator +from typing import ClassVar from typing_extensions import TypeAlias _TestType: TypeAlias = unittest.case.TestCase | TestSuite @@ -17,6 +18,7 @@ class BaseTestSuite: def countTestCases(self) -> int: ... def __iter__(self) -> Iterator[_TestType]: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] class TestSuite(BaseTestSuite): def run(self, result: unittest.result.TestResult, debug: bool = False) -> unittest.result.TestResult: ... diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 4203756c718d..05a7b2bcda66 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -3,7 +3,7 @@ from _typeshed import SupportsKeysAndGetItem from _weakref import getweakrefcount as getweakrefcount, getweakrefs as getweakrefs, proxy as proxy from _weakrefset import WeakSet as WeakSet from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping -from typing import Any, Generic, TypeVar, final, overload +from typing import Any, ClassVar, Generic, TypeVar, final, overload from typing_extensions import ParamSpec, Self if sys.version_info >= (3, 9): @@ -47,11 +47,13 @@ class CallableProxyType(Generic[_CallableT]): # "weakcallableproxy" def __eq__(self, value: object, /) -> bool: ... def __getattr__(self, attr: str) -> Any: ... __call__: _CallableT + __hash__: ClassVar[None] # type: ignore[assignment] @final class ProxyType(Generic[_T]): # "weakproxy" def __eq__(self, value: object, /) -> bool: ... def __getattr__(self, attr: str) -> Any: ... + __hash__: ClassVar[None] # type: ignore[assignment] class ReferenceType(Generic[_T]): # "weakref" __callback__: Callable[[Self], Any] @@ -115,6 +117,12 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... + @overload + def update(self, other: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... + @overload + def update(self, other: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + @overload + def update(self, other: None = None, /, **kwargs: _VT) -> None: ... if sys.version_info >= (3, 9): def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... @@ -163,6 +171,12 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... + @overload + def update(self, dict: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... + @overload + def update(self, dict: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + @overload + def update(self, dict: None = None, /, **kwargs: _VT) -> None: ... if sys.version_info >= (3, 9): def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index fae2c4d98714..4eda3897a00c 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -1,7 +1,7 @@ import sys import xml.dom from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite -from typing import Literal, NoReturn, TypeVar, overload +from typing import ClassVar, Literal, NoReturn, TypeVar, overload from typing_extensions import Self from xml.dom.minicompat import NodeList from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS @@ -151,6 +151,7 @@ class NamedNodeMap: def keysNS(self): ... def values(self): ... def get(self, name: str, value: Incomplete | None = None): ... + __hash__: ClassVar[None] # type: ignore[assignment] def __len__(self) -> int: ... def __eq__(self, other: object) -> bool: ... def __ge__(self, other: NamedNodeMap) -> bool: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index 5899d1d72a38..6cc4361f4a09 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Final, Literal, Protocol, overload +from typing import Any, ClassVar, Final, Literal, Protocol, overload from typing_extensions import Self, TypeAlias class _SupportsTimeTuple(Protocol): @@ -76,6 +76,7 @@ def _strftime(value: _XMLDate) -> str: ... # undocumented class DateTime: value: str # undocumented def __init__(self, value: int | str | datetime | time.struct_time | tuple[int, ...] = 0) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __lt__(self, other: _DateTimeComparable) -> bool: ... def __le__(self, other: _DateTimeComparable) -> bool: ... def __gt__(self, other: _DateTimeComparable) -> bool: ... @@ -95,6 +96,7 @@ class Binary: def decode(self, data: ReadableBuffer) -> None: ... def encode(self, out: SupportsWrite[str]) -> None: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] def _binary(data: ReadableBuffer) -> Binary: ... # undocumented @@ -108,8 +110,7 @@ class ExpatParser: # undocumented _WriteCallback: TypeAlias = Callable[[str], object] class Marshaller: - # TODO: Replace 'Any' with some kind of binding - dispatch: dict[type[Any], Callable[[Marshaller, Any, _WriteCallback], None]] + dispatch: dict[type[_Marshallable] | Literal["_arbitrary_instance"], Callable[[Marshaller, Any, _WriteCallback], None]] memo: dict[Any, None] data: None encoding: str | None diff --git a/mypy/typeshed/stdlib/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index 6bae87a8db2a..78a50b85f405 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -1,5 +1,5 @@ import sys -from typing import Any, final +from typing import Any, ClassVar, final class Str(str): ... @@ -17,6 +17,8 @@ if sys.version_info >= (3, 10): else: class error(Exception): ... - class Null: ... + + class Null: + __hash__: ClassVar[None] # type: ignore[assignment] def roj(b: Any, /) -> None: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index cc483afad9ff..fb21b00c45dc 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -21,7 +21,7 @@ if sys.version_info >= (3, 9): class ZoneInfo(tzinfo): @property def key(self) -> str: ... - def __init__(self, key: str) -> None: ... + def __new__(cls, key: str) -> Self: ... @classmethod def no_cache(cls, key: str) -> Self: ... @classmethod diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index fa6da49df1cc..61cb69b2d281 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1026,8 +1026,8 @@ reveal_type(g) with f('') as s: reveal_type(s) [out] -_program.py:13: note: Revealed type is "def (x: builtins.int) -> contextlib._GeneratorContextManager[builtins.str]" -_program.py:14: note: Revealed type is "def (*x: builtins.str) -> contextlib._GeneratorContextManager[builtins.int]" +_program.py:13: note: Revealed type is "def (x: builtins.int) -> contextlib._GeneratorContextManager[builtins.str, None, None]" +_program.py:14: note: Revealed type is "def (*x: builtins.str) -> contextlib._GeneratorContextManager[builtins.int, None, None]" _program.py:16: error: Argument 1 to "f" has incompatible type "str"; expected "int" _program.py:17: note: Revealed type is "builtins.str" From 025642bbdbb9f6a00f3ed7a511be2e9d45795618 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:39:04 +0100 Subject: [PATCH 1020/1617] Reject invalid ParamSpec locations (#18278) Fixes #14832, fixes #13966, fixes #14622. Still does not report error in #14777, I'll work separately on that. Move all `ParamSpec` validity checking to `typeanal.py`. Stop treating `P.args` and `P.kwargs` as binding - only bare typevar makes it available in scope. Reject keyword arguments following `P.args`. This also makes one more conformance test pass. --- mypy/semanal.py | 60 -------- mypy/typeanal.py | 119 +++++++++------- .../unit/check-parameter-specification.test | 129 +++++++++++++----- 3 files changed, 161 insertions(+), 147 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8335f91c4d3b..034d8fb28b42 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -72,7 +72,6 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, - ARG_STAR, ARG_STAR2, CONTRAVARIANT, COVARIANT, @@ -981,7 +980,6 @@ def analyze_func_def(self, defn: FuncDef) -> None: defn.type = result self.add_type_alias_deps(analyzer.aliases_used) self.check_function_signature(defn) - self.check_paramspec_definition(defn) if isinstance(defn, FuncDef): assert isinstance(defn.type, CallableType) defn.type = set_callable_name(defn.type, defn) @@ -1610,64 +1608,6 @@ def check_function_signature(self, fdef: FuncItem) -> None: elif len(sig.arg_types) > len(fdef.arguments): self.fail("Type signature has too many arguments", fdef, blocker=True) - def check_paramspec_definition(self, defn: FuncDef) -> None: - func = defn.type - assert isinstance(func, CallableType) - - if not any(isinstance(var, ParamSpecType) for var in func.variables): - return # Function does not have param spec variables - - args = func.var_arg() - kwargs = func.kw_arg() - if args is None and kwargs is None: - return # Looks like this function does not have starred args - - args_defn_type = None - kwargs_defn_type = None - for arg_def, arg_kind in zip(defn.arguments, defn.arg_kinds): - if arg_kind == ARG_STAR: - args_defn_type = arg_def.type_annotation - elif arg_kind == ARG_STAR2: - kwargs_defn_type = arg_def.type_annotation - - # This may happen on invalid `ParamSpec` args / kwargs definition, - # type analyzer sets types of arguments to `Any`, but keeps - # definition types as `UnboundType` for now. - if not ( - (isinstance(args_defn_type, UnboundType) and args_defn_type.name.endswith(".args")) - or ( - isinstance(kwargs_defn_type, UnboundType) - and kwargs_defn_type.name.endswith(".kwargs") - ) - ): - # Looks like both `*args` and `**kwargs` are not `ParamSpec` - # It might be something else, skipping. - return - - args_type = args.typ if args is not None else None - kwargs_type = kwargs.typ if kwargs is not None else None - - if ( - not isinstance(args_type, ParamSpecType) - or not isinstance(kwargs_type, ParamSpecType) - or args_type.name != kwargs_type.name - ): - if isinstance(args_defn_type, UnboundType) and args_defn_type.name.endswith(".args"): - param_name = args_defn_type.name.split(".")[0] - elif isinstance(kwargs_defn_type, UnboundType) and kwargs_defn_type.name.endswith( - ".kwargs" - ): - param_name = kwargs_defn_type.name.split(".")[0] - else: - # Fallback for cases that probably should not ever happen: - param_name = "P" - - self.fail( - f'ParamSpec must have "*args" typed as "{param_name}.args" and "**kwargs" typed as "{param_name}.kwargs"', - func, - code=codes.VALID_TYPE, - ) - def visit_decorator(self, dec: Decorator) -> None: self.statement = dec # TODO: better don't modify them at all. diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 7de987a83a2b..008e3c2477a1 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -310,6 +310,15 @@ def not_declared_in_type_params(self, tvar_name: str) -> bool: def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) -> Type: sym = self.lookup_qualified(t.name, t) + param_spec_name = None + if t.name.endswith((".args", ".kwargs")): + param_spec_name = t.name.rsplit(".", 1)[0] + maybe_param_spec = self.lookup_qualified(param_spec_name, t) + if maybe_param_spec and isinstance(maybe_param_spec.node, ParamSpecExpr): + sym = maybe_param_spec + else: + param_spec_name = None + if sym is not None: node = sym.node if isinstance(node, PlaceholderNode): @@ -362,10 +371,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if tvar_def is None: if self.allow_unbound_tvars: return t + name = param_spec_name or t.name if self.defining_alias and self.not_declared_in_type_params(t.name): - msg = f'ParamSpec "{t.name}" is not included in type_params' + msg = f'ParamSpec "{name}" is not included in type_params' else: - msg = f'ParamSpec "{t.name}" is unbound' + msg = f'ParamSpec "{name}" is unbound' self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, ParamSpecType) @@ -373,6 +383,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.fail( f'ParamSpec "{t.name}" used with arguments', t, code=codes.VALID_TYPE ) + if param_spec_name is not None and not self.allow_param_spec_literals: + self.fail( + "ParamSpec components are not allowed here", t, code=codes.VALID_TYPE + ) + return AnyType(TypeOfAny.from_error) # Change the line number return ParamSpecType( tvar_def.name, @@ -1113,46 +1128,57 @@ def visit_callable_type( variables, _ = self.bind_function_type_variables(t, t) type_guard = self.anal_type_guard(t.ret_type) type_is = self.anal_type_is(t.ret_type) + arg_kinds = t.arg_kinds - if len(arg_kinds) >= 2 and arg_kinds[-2] == ARG_STAR and arg_kinds[-1] == ARG_STAR2: - arg_types = self.anal_array(t.arg_types[:-2], nested=nested) + [ - self.anal_star_arg_type(t.arg_types[-2], ARG_STAR, nested=nested), - self.anal_star_arg_type(t.arg_types[-1], ARG_STAR2, nested=nested), - ] - # If nested is True, it means we are analyzing a Callable[...] type, rather - # than a function definition type. We need to "unpack" ** TypedDict annotation - # here (for function definitions it is done in semanal). - if nested and isinstance(arg_types[-1], UnpackType): + arg_types = [] + param_spec_with_args = param_spec_with_kwargs = None + param_spec_invalid = False + for kind, ut in zip(arg_kinds, t.arg_types): + if kind == ARG_STAR: + param_spec_with_args, at = self.anal_star_arg_type(ut, kind, nested=nested) + elif kind == ARG_STAR2: + param_spec_with_kwargs, at = self.anal_star_arg_type(ut, kind, nested=nested) + else: + if param_spec_with_args: + param_spec_invalid = True + self.fail( + "Arguments not allowed after ParamSpec.args", t, code=codes.VALID_TYPE + ) + at = self.anal_type(ut, nested=nested, allow_unpack=False) + arg_types.append(at) + + if nested and arg_types: + # If we've got a Callable[[Unpack[SomeTypedDict]], None], make sure + # Unpack is interpreted as `**` and not as `*`. + last = arg_types[-1] + if isinstance(last, UnpackType): # TODO: it would be better to avoid this get_proper_type() call. - unpacked = get_proper_type(arg_types[-1].type) - if isinstance(unpacked, TypedDictType): - arg_types[-1] = unpacked + p_at = get_proper_type(last.type) + if isinstance(p_at, TypedDictType) and not last.from_star_syntax: + # Automatically detect Unpack[Foo] in Callable as backwards + # compatible syntax for **Foo, if Foo is a TypedDict. + arg_kinds[-1] = ARG_STAR2 + arg_types[-1] = p_at unpacked_kwargs = True - arg_types = self.check_unpacks_in_list(arg_types) - else: - star_index = None + arg_types = self.check_unpacks_in_list(arg_types) + + if not param_spec_invalid and param_spec_with_args != param_spec_with_kwargs: + # If already invalid, do not report more errors - definition has + # to be fixed anyway + name = param_spec_with_args or param_spec_with_kwargs + self.fail( + f'ParamSpec must have "*args" typed as "{name}.args" and "**kwargs" typed as "{name}.kwargs"', + t, + code=codes.VALID_TYPE, + ) + param_spec_invalid = True + + if param_spec_invalid: if ARG_STAR in arg_kinds: - star_index = arg_kinds.index(ARG_STAR) - star2_index = None + arg_types[arg_kinds.index(ARG_STAR)] = AnyType(TypeOfAny.from_error) if ARG_STAR2 in arg_kinds: - star2_index = arg_kinds.index(ARG_STAR2) - arg_types = [] - for i, ut in enumerate(t.arg_types): - at = self.anal_type( - ut, nested=nested, allow_unpack=i in (star_index, star2_index) - ) - if nested and isinstance(at, UnpackType) and i == star_index: - # TODO: it would be better to avoid this get_proper_type() call. - p_at = get_proper_type(at.type) - if isinstance(p_at, TypedDictType) and not at.from_star_syntax: - # Automatically detect Unpack[Foo] in Callable as backwards - # compatible syntax for **Foo, if Foo is a TypedDict. - at = p_at - arg_kinds[i] = ARG_STAR2 - unpacked_kwargs = True - arg_types.append(at) - if nested: - arg_types = self.check_unpacks_in_list(arg_types) + arg_types[arg_kinds.index(ARG_STAR2)] = AnyType(TypeOfAny.from_error) + # If there were multiple (invalid) unpacks, the arg types list will become shorter, # we need to trim the kinds/names as well to avoid crashes. arg_kinds = t.arg_kinds[: len(arg_types)] @@ -1207,7 +1233,7 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: return self.anal_type(t.args[0]) return None - def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: + def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> tuple[str | None, Type]: """Analyze signature argument type for *args and **kwargs argument.""" if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: components = t.name.split(".") @@ -1234,7 +1260,7 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: ) else: assert False, kind - return make_paramspec( + return tvar_name, make_paramspec( tvar_def.name, tvar_def.fullname, tvar_def.id, @@ -1242,7 +1268,7 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: line=t.line, column=t.column, ) - return self.anal_type(t, nested=nested, allow_unpack=True) + return None, self.anal_type(t, nested=nested, allow_unpack=True) def visit_overloaded(self, t: Overloaded) -> Type: # Overloaded types are manually constructed in semanal.py by analyzing the @@ -2586,18 +2612,7 @@ def _seems_like_callable(self, type: UnboundType) -> bool: def visit_unbound_type(self, t: UnboundType) -> None: name = t.name - node = None - - # Special case P.args and P.kwargs for ParamSpecs only. - if name.endswith("args"): - if name.endswith((".args", ".kwargs")): - base = ".".join(name.split(".")[:-1]) - n = self.api.lookup_qualified(base, t) - if n is not None and isinstance(n.node, ParamSpecExpr): - node = n - name = base - if node is None: - node = self.api.lookup_qualified(name, t) + node = self.api.lookup_qualified(name, t) if node and node.fullname in SELF_TYPE_NAMES: self.has_self_type = True if ( diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index fca72f3bebc3..fa3d98036ec3 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -14,7 +14,7 @@ P5 = ParamSpec("P5", covariant=True, bound=int) # E: The variance and bound arg [builtins fixtures/paramspec.pyi] [case testParamSpecLocations] -from typing import Callable, List +from typing import Any, Callable, List, Type from typing_extensions import ParamSpec, Concatenate P = ParamSpec('P') @@ -36,6 +36,25 @@ def foo5(x: Callable[[int, str], P]) -> None: ... # E: Invalid location for Par def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" + +def foo7( + *args: P.args, **kwargs: P.kwargs # E: ParamSpec "P" is unbound +) -> Callable[[Callable[P, T]], Type[T]]: + ... + +def wrapper(f: Callable[P, int]) -> None: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: ... # OK + + def extra_args_left(x: int, *args: P.args, **kwargs: P.kwargs) -> None: ... # OK + def extra_args_between(*args: P.args, x: int, **kwargs: P.kwargs) -> None: ... # E: Arguments not allowed after ParamSpec.args + + def swapped(*args: P.kwargs, **kwargs: P.args) -> None: ... # E: Use "P.args" for variadic "*" parameter \ + # E: Use "P.kwargs" for variadic "**" parameter + def bad_kwargs(*args: P.args, **kwargs: P.args) -> None: ... # E: Use "P.kwargs" for variadic "**" parameter + def bad_args(*args: P.kwargs, **kwargs: P.kwargs) -> None: ... # E: Use "P.args" for variadic "*" parameter + + def misplaced(x: P.args) -> None: ... # E: ParamSpec components are not allowed here + def bad_kwargs_any(*args: P.args, **kwargs: Any) -> None: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [builtins fixtures/paramspec.pyi] [case testParamSpecImports] @@ -1264,7 +1283,7 @@ def f1(f: Callable[P, int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSp def f2(f: Callable[P, int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f3(f: Callable[P, int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f4(f: Callable[P, int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" -def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" +def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: Arguments not allowed after ParamSpec.args # Error message test: P1 = ParamSpec('P1') @@ -1294,7 +1313,7 @@ def f1(f: Callable[Concatenate[int, P], int], *args, **kwargs: P.kwargs) -> int: def f2(f: Callable[Concatenate[int, P], int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f3(f: Callable[Concatenate[int, P], int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f4(f: Callable[Concatenate[int, P], int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" -def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" +def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: Arguments not allowed after ParamSpec.args [builtins fixtures/paramspec.pyi] @@ -1326,22 +1345,28 @@ from typing import Callable, ParamSpec P1 = ParamSpec('P1') P2 = ParamSpec('P2') -def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P2" is unbound \ + # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f1(*args: P1.args): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f2(**kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f3(*args: P1.args, **kwargs: int): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f4(*args: int, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f1(*args: P1.args): ... # E: ParamSpec "P1" is unbound +def f2(**kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound +def f3(*args: P1.args, **kwargs: int): ... # E: ParamSpec "P1" is unbound +def f4(*args: int, **kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound # Error message is based on the `args` definition: -def f5(*args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" -def f6(*args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f5(*args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec "P2" is unbound \ + # E: ParamSpec "P1" is unbound +def f6(*args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P1" is unbound \ + # E: ParamSpec "P2" is unbound # Multiple `ParamSpec` variables can be found, they should not affect error message: P3 = ParamSpec('P3') -def f7(first: Callable[P3, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f8(first: Callable[P3, int], *args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" +def f7(first: Callable[P3, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P1" is unbound \ + # E: ParamSpec "P2" is unbound +def f8(first: Callable[P3, int], *args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec "P2" is unbound \ + # E: ParamSpec "P1" is unbound + [builtins fixtures/paramspec.pyi] @@ -1354,7 +1379,8 @@ P = ParamSpec('P') class Some(Generic[P]): def call(self, *args: P.args, **kwargs: P.kwargs): ... -def call(*args: P.args, **kwargs: P.kwargs): ... +def call(*args: P.args, **kwargs: P.kwargs): ... # E: ParamSpec "P" is unbound + [builtins fixtures/paramspec.pyi] [case testParamSpecInferenceCrash] @@ -2137,28 +2163,6 @@ submit( ) [builtins fixtures/paramspec.pyi] -[case testParamSpecGenericWithNamedArg2] -from typing import Callable, TypeVar, Type -from typing_extensions import ParamSpec - -P= ParamSpec("P") -T = TypeVar("T") - -def smoke_testable(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], Type[T]]: - ... - -@smoke_testable(name="bob", size=512, flt=0.5) -class SomeClass: - def __init__(self, size: int, name: str, flt: float) -> None: - pass - -# Error message is confusing, but this is a known issue, see #4530. -@smoke_testable(name=42, size="bad", flt=0.5) # E: Argument 1 has incompatible type "Type[OtherClass]"; expected "Callable[[int, str, float], OtherClass]" -class OtherClass: - def __init__(self, size: int, name: str, flt: float) -> None: - pass -[builtins fixtures/paramspec.pyi] - [case testInferenceAgainstGenericCallableUnionParamSpec] from typing import Callable, TypeVar, List, Union from typing_extensions import ParamSpec @@ -2473,3 +2477,58 @@ def run(func: Callable[Concatenate[int, str, P], T], *args: P.args, **kwargs: P. return func2(*args_prefix, *args) [builtins fixtures/paramspec.pyi] + +[case testParamSpecScoping] +from typing import Any, Callable, Generic +from typing_extensions import Concatenate, ParamSpec + +P = ParamSpec("P") +P2 = ParamSpec("P2") + +def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... +def contains_other(f: Callable[P2, None], c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + +def contains_only_other(c: Callable[P2, None], *args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + +def puts_p_into_scope(f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +def puts_p_into_scope_concatenate(f: Callable[Concatenate[int, P], int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +def wrapper() -> None: + def puts_p_into_scope1(f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +class Wrapper: + def puts_p_into_scope1(self, f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + + def contains(self, c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + + def uses(self, *args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + + def method(self) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + +class GenericWrapper(Generic[P]): + x: P.args # E: ParamSpec components are not allowed here + y: P.kwargs # E: ParamSpec components are not allowed here + + def contains(self, c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + + def puts_p_into_scope1(self, f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + + def uses(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + + def method(self) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... +[builtins fixtures/paramspec.pyi] From 845628761790c8edb24c141fdf929ce7b36bb358 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 2 Jan 2025 13:26:22 -0800 Subject: [PATCH 1021/1617] Mention ignore_errors in exclude docs (#18412) --- docs/source/command_line.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 17758484f243..7b6b75b98b6f 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -68,10 +68,11 @@ for full details, see :ref:`running-mypy`. checked. For instance, ``mypy --exclude '/setup.py$' but_still_check/setup.py``. - In particular, ``--exclude`` does not affect mypy's :ref:`import following - `. You can use a per-module :confval:`follow_imports` config - option to additionally avoid mypy from following imports and checking code - you do not wish to be checked. + In particular, ``--exclude`` does not affect mypy's discovery of files + via :ref:`import following `. You can use a per-module + :confval:`ignore_errors` config option to silence errors from a given module, + or a per-module :confval:`follow_imports` config option to additionally avoid + mypy from following imports and checking code you do not wish to be checked. Note that mypy will never recursively discover files and directories named "site-packages", "node_modules" or "__pycache__", or those whose name starts From bac9984a0e2696eed5d65ca49d006c545ba83a54 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:41:15 +0100 Subject: [PATCH 1022/1617] Use SupportsWrite protocol from typeshed (#18404) --- mypy/main.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index c657f09e2600..ae7a3b9d5c86 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -11,7 +11,7 @@ from collections.abc import Sequence from gettext import gettext from io import TextIOWrapper -from typing import IO, Any, Final, NoReturn, Protocol, TextIO +from typing import IO, TYPE_CHECKING, Any, Final, NoReturn, TextIO from mypy import build, defaults, state, util from mypy.config_parser import ( @@ -36,9 +36,8 @@ from mypy.split_namespace import SplitNamespace from mypy.version import __version__ - -class _SupportsWrite(Protocol): - def write(self, s: str, /) -> object: ... +if TYPE_CHECKING: + from _typeshed import SupportsWrite orig_stat: Final = os.stat @@ -378,17 +377,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # ===================== # Help-printing methods # ===================== - def print_usage(self, file: _SupportsWrite | None = None) -> None: + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_usage(), file) - def print_help(self, file: _SupportsWrite | None = None) -> None: + def print_help(self, file: SupportsWrite[str] | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_help(), file) - def _print_message(self, message: str, file: _SupportsWrite | None = None) -> None: + def _print_message(self, message: str, file: SupportsWrite[str] | None = None) -> None: if message: if file is None: file = self.stderr From 02c07c893c47200e7da52a29e6ed7f05e2d63f52 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:21:25 -0800 Subject: [PATCH 1023/1617] Add regression test for NamedTuple with recursive bound (#18399) See https://github.com/python/mypy/pull/18351#pullrequestreview-2525435197 Co-authored-by: ilevkivskyi --- test-data/unit/check-namedtuple.test | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 566b5ef57350..172228820add 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1460,3 +1460,17 @@ Func = NamedTuple('Func', [ ]) [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testGenericNamedTupleRecursiveBound] +from typing import Generic, NamedTuple, TypeVar +T = TypeVar("T", bound="NT") +class NT(NamedTuple, Generic[T]): + parent: T + item: int + +def main(n: NT[T]) -> None: + reveal_type(n.parent) # N: Revealed type is "T`-1" + reveal_type(n.item) # N: Revealed type is "builtins.int" + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] From b96a3f1ae121f6255d45afc8b11cd1d3e6e24d47 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:21:53 -0800 Subject: [PATCH 1024/1617] Dedicated error code for explicit any (#18398) --- docs/source/error_code_list.rst | 16 +++++ docs/source/error_code_list2.rst | 21 ++++--- mypy/errorcodes.py | 4 ++ mypy/messages.py | 2 +- test-data/unit/check-flags.test | 66 ++++++++++---------- test-data/unit/fixtures/typing-typeddict.pyi | 3 +- 6 files changed, 66 insertions(+), 46 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 73171131bc8d..49cb8a0c06c1 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1241,6 +1241,22 @@ Consider this example: `PEP 705 `_ specifies how ``ReadOnly`` special form works for ``TypedDict`` objects. +.. _code-narrowed-type-not-subtype: + +Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] +--------------------------------------------------------------- + +:pep:`742` requires that when ``TypeIs`` is used, the narrowed +type must be a subtype of the original type:: + + from typing_extensions import TypeIs + + def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int + ... + + def g(x: object) -> TypeIs[str]: # OK + ... + .. _code-misc: Miscellaneous checks [misc] diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index df8b696745fc..508574b36e09 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -594,18 +594,19 @@ Correct usage: When this code is enabled, using ``reveal_locals`` is always an error, because there's no way one can import it. -.. _code-narrowed-type-not-subtype: -Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] ---------------------------------------------------------------- +.. _code-explicit-any: -:pep:`742` requires that when ``TypeIs`` is used, the narrowed -type must be a subtype of the original type:: +Check that explicit Any type annotations are not allowed [explicit-any] +----------------------------------------------------------------------- - from typing_extensions import TypeIs +If you use :option:`--disallow-any-explicit `, mypy generates an error +if you use an explicit ``Any`` type annotation. - def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int - ... +Example: - def g(x: object) -> TypeIs[str]: # OK - ... +.. code-block:: python + + # mypy: disallow-any-explicit + from typing import Any + x: Any = 1 # Error: Explicit "Any" type annotation [explicit-any] diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 5736be5c143e..8f650aa30605 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -304,6 +304,10 @@ def __hash__(self) -> int: "General", ) +EXPLICIT_ANY: Final = ErrorCode( + "explicit-any", "Warn about explicit Any type annotations", "General" +) + DEPRECATED: Final = ErrorCode( "deprecated", "Warn when importing or using deprecated (overloaded) functions, methods or classes", diff --git a/mypy/messages.py b/mypy/messages.py index 5fa4dc0c05ad..cdd8f3187d63 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1821,7 +1821,7 @@ def need_annotation_for_var( ) def explicit_any(self, ctx: Context) -> None: - self.fail('Explicit "Any" is not allowed', ctx) + self.fail('Explicit "Any" is not allowed', ctx, code=codes.EXPLICIT_ANY) def unsupported_target_for_star_typeddict(self, typ: Type, ctx: Context) -> None: self.fail( diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 86a65d85a8b2..6dceb28b5cb6 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1829,51 +1829,51 @@ x: A # E:4: Missing type parameters for generic type "A" [builtins fixtures/list.pyi] [case testDisallowAnyExplicitDefSignature] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -def f(x: Any) -> None: # E: Explicit "Any" is not allowed +def f(x: Any) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass -def g() -> Any: # E: Explicit "Any" is not allowed +def g() -> Any: # E: Explicit "Any" is not allowed [explicit-any] pass -def h() -> List[Any]: # E: Explicit "Any" is not allowed +def h() -> List[Any]: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any -v: Any = '' # E: Explicit "Any" is not allowed -w = '' # type: Any # E: Explicit "Any" is not allowed +v: Any = '' # E: Explicit "Any" is not allowed [explicit-any] +w = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] class X: - y = '' # type: Any # E: Explicit "Any" is not allowed + y = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] [case testDisallowAnyExplicitGenericVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -v: List[Any] = [] # E: Explicit "Any" is not allowed +v: List[Any] = [] # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitInheritance] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -class C(Any): # E: Explicit "Any" is not allowed +class C(Any): # E: Explicit "Any" is not allowed [explicit-any] pass -class D(List[Any]): # E: Explicit "Any" is not allowed +class D(List[Any]): # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -X = Any # E: Explicit "Any" is not allowed -Y = List[Any] # E: Explicit "Any" is not allowed +X = Any # E: Explicit "Any" is not allowed [explicit-any] +Y = List[Any] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: X) -> Y: # no error x.nonexistent() # no error @@ -1881,68 +1881,68 @@ def foo(x: X) -> Y: # no error [builtins fixtures/list.pyi] [case testDisallowAnyExplicitGenericAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, TypeVar, Tuple T = TypeVar('T') -TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed +TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: TupleAny[str]) -> None: # no error pass -def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed +def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/tuple.pyi] [case testDisallowAnyExplicitCast] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, cast x = 1 -y = cast(Any, x) # E: Explicit "Any" is not allowed -z = cast(List[Any], x) # E: Explicit "Any" is not allowed +y = cast(Any, x) # E: Explicit "Any" is not allowed [explicit-any] +z = cast(List[Any], x) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNamedTuple] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NamedTuple -Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed +Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypeVarConstraint] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, TypeVar -T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed +T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNewType] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NewType # this error does not come from `--disallow-any-explicit` flag -Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") -Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed +Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype] +Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypedDictSimple] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from mypy_extensions import TypedDict from typing import Any -M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed [explicit-any] M(x='x', y=2) # no error def f(m: M) -> None: pass # no error [builtins fixtures/dict.pyi] [case testDisallowAnyExplicitTypedDictGeneric] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from mypy_extensions import TypedDict from typing import Any, List -M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed [explicit-any] N = TypedDict('N', {'x': str, 'y': List}) # no error [builtins fixtures/dict.pyi] diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index a54dc8bcfa94..df703b239743 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -47,8 +47,7 @@ class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass class Sequence(Iterable[T_co]): - # misc is for explicit Any. - def __getitem__(self, n: Any) -> T_co: pass # type: ignore[misc] + def __getitem__(self, n: Any) -> T_co: pass # type: ignore[explicit-any] class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): def keys(self) -> Iterable[T]: pass # Approximate return type From 6181b0f51bf7fc9b67630afbf075be01ec7964dd Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:22:18 -0800 Subject: [PATCH 1025/1617] Fix line number for slices, clean up old logic (#18397) Fixes #17655 The decorator cleanup moves a type ignore, but so does the bug fix for decorators in #18392 , so might as well batch into a single release --- CHANGELOG.md | 12 ++++++++++ mypy/fastparse.py | 37 +++++++----------------------- mypy/messages.py | 2 +- mypy/nodes.py | 6 ----- mypy/plugins/common.py | 1 - test-data/unit/check-python38.test | 4 ++-- test-data/unit/parse.test | 26 +++++++++++++++++---- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81da1cd05a2f..b8e9d0078a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Next release +### Performance improvements + +TODO + ### Drop Support for Python 3.8 Mypy no longer supports running with Python 3.8, which has reached end-of-life. @@ -40,6 +44,14 @@ Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull (Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` by default in **mypy 2.0**). +### Better line numbers for decorators and slice expressions + +Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, this +may necessitate changing the location of a `# type: ignore` comment. + +Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392), +PR [18397](https://github.com/python/mypy/pull/18397)). + ## Mypy 1.14 We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 6985fd567402..cd7aab86daa0 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1009,28 +1009,22 @@ def do_func_def( func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def - func_type.line = lineno + func_type.set_line(lineno) if n.decorator_list: - # Set deco_line to the old pre-3.8 lineno, in order to keep - # existing "# type: ignore" comments working: - deco_line = n.decorator_list[0].lineno - var = Var(func_def.name) var.is_ready = False var.set_line(lineno) func_def.is_decorated = True - func_def.deco_line = deco_line - func_def.set_line(lineno, n.col_offset, end_line, end_column) + self.set_line(func_def, n) deco = Decorator(func_def, self.translate_expr_list(n.decorator_list), var) first = n.decorator_list[0] deco.set_line(first.lineno, first.col_offset, end_line, end_column) retval: FuncDef | Decorator = deco else: - # FuncDef overrides set_line -- can't use self.set_line - func_def.set_line(lineno, n.col_offset, end_line, end_column) + self.set_line(func_def, n) retval = func_def if self.options.include_docstrings: func_def.docstring = ast3.get_docstring(n, clean=False) @@ -1149,10 +1143,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: type_args=explicit_type_params, ) cdef.decorators = self.translate_expr_list(n.decorator_list) - # Set lines to match the old mypy 0.700 lines, in order to keep - # existing "# type: ignore" comments working: - cdef.line = n.lineno - cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + self.set_line(cdef, n) if self.options.include_docstrings: cdef.docstring = ast3.get_docstring(n, clean=False) @@ -1247,8 +1238,7 @@ def visit_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt: line = n.lineno if n.value is None: # always allow 'x: int' rvalue: Expression = TempNode(AnyType(TypeOfAny.special_form), no_rhs=True) - rvalue.line = line - rvalue.column = n.col_offset + self.set_line(rvalue, n) else: rvalue = self.visit(n.value) typ = TypeConverter(self.errors, line=line).visit(n.annotation) @@ -1675,19 +1665,7 @@ def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr: # Subscript(expr value, slice slice, expr_context ctx) def visit_Subscript(self, n: ast3.Subscript) -> IndexExpr: e = IndexExpr(self.visit(n.value), self.visit(n.slice)) - self.set_line(e, n) - # alias to please mypyc - is_py38_or_earlier = sys.version_info < (3, 9) - if isinstance(n.slice, ast3.Slice) or ( - is_py38_or_earlier and isinstance(n.slice, ast3.ExtSlice) - ): - # Before Python 3.9, Slice has no line/column in the raw ast. To avoid incompatibility - # visit_Slice doesn't set_line, even in Python 3.9 on. - # ExtSlice also has no line/column info. In Python 3.9 on, line/column is set for - # e.index when visiting n.slice. - e.index.line = e.line - e.index.column = e.column - return e + return self.set_line(e, n) # Starred(expr value, expr_context ctx) def visit_Starred(self, n: Starred) -> StarExpr: @@ -1718,7 +1696,8 @@ def visit_Tuple(self, n: ast3.Tuple) -> TupleExpr: # Slice(expr? lower, expr? upper, expr? step) def visit_Slice(self, n: ast3.Slice) -> SliceExpr: - return SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step)) + e = SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step)) + return self.set_line(e, n) # ExtSlice(slice* dims) def visit_ExtSlice(self, n: ast3.ExtSlice) -> TupleExpr: diff --git a/mypy/messages.py b/mypy/messages.py index cdd8f3187d63..b63310825f7d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -244,7 +244,7 @@ def span_from_context(ctx: Context) -> Iterable[int]: TODO: address this in follow up PR """ if isinstance(ctx, (ClassDef, FuncDef)): - return range(ctx.deco_line or ctx.line, ctx.line + 1) + return range(ctx.line, ctx.line + 1) elif not isinstance(ctx, Expression): return [ctx.line] else: diff --git a/mypy/nodes.py b/mypy/nodes.py index 585012d5a865..b7b09f506c35 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -768,7 +768,6 @@ class FuncDef(FuncItem, SymbolNode, Statement): "is_conditional", "abstract_status", "original_def", - "deco_line", "is_trivial_body", "is_mypy_only", # Present only when a function is decorated with @typing.dataclass_transform or similar @@ -798,8 +797,6 @@ def __init__( self.is_trivial_body = False # Original conditional definition self.original_def: None | FuncDef | Var | Decorator = None - # Used for error reporting (to keep backward compatibility with pre-3.8) - self.deco_line: int | None = None # Definitions that appear in if TYPE_CHECKING are marked with this flag. self.is_mypy_only = False self.dataclass_transform_spec: DataclassTransformSpec | None = None @@ -1115,7 +1112,6 @@ class ClassDef(Statement): "keywords", "analyzed", "has_incompatible_baseclass", - "deco_line", "docstring", "removed_statements", ) @@ -1166,8 +1162,6 @@ def __init__( self.keywords = dict(keywords) if keywords else {} self.analyzed = None self.has_incompatible_baseclass = False - # Used for error reporting (to keep backward compatibility with pre-3.8) - self.deco_line: int | None = None self.docstring: str | None = None self.removed_statements = [] diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 43caa6483236..ac00171a037c 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -282,7 +282,6 @@ def add_overloaded_method_to_class( var = Var(func.name, func.type) var.set_line(func.line) func.is_decorated = True - func.deco_line = func.line deco = Decorator(func, [], var) else: diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 199014a66fed..4add107baef4 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -17,8 +17,8 @@ def f(): ... # E: Function is missing a return type annotation \ # flags: --disallow-untyped-defs --warn-unused-ignores def d(f): ... # type: ignore @d -# type: ignore -def f(): ... # type: ignore # E: Unused "type: ignore" comment +# type: ignore # E: Unused "type: ignore" comment +def f(): ... # type: ignore [case testIgnoreDecoratedFunction2] # flags: --disallow-untyped-defs diff --git a/test-data/unit/parse.test b/test-data/unit/parse.test index 10ceaa947fd4..943ca49081f1 100644 --- a/test-data/unit/parse.test +++ b/test-data/unit/parse.test @@ -3171,10 +3171,10 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( ) - SliceExpr:-1( + SliceExpr:1( ))))) @@ -3186,10 +3186,10 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( IntExpr(1) IntExpr(2)) - SliceExpr:-1( + SliceExpr:1( ))))) @@ -3201,13 +3201,29 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( IntExpr(1) IntExpr(2) IntExpr(3)) Ellipsis IntExpr(1))))) +[case testParseExtendedSlicing4] +m[*index, :] +[out] +main:1: error: invalid syntax +[out version>=3.11] +MypyFile:1( + ExpressionStmt:1( + IndexExpr:1( + NameExpr(m) + TupleExpr:1( + StarExpr:1( + NameExpr(index)) + SliceExpr:1( + + ))))) + [case testParseIfExprInDictExpr] test = { 'spam': 'eggs' if True else 'bacon' } [out] From 8951a33d96f293d06183f973f5ab6e496451e8e9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:51:31 -0800 Subject: [PATCH 1026/1617] [pre-commit.ci] pre-commit autoupdate (#17085) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e53f084e675..587a16b3fb72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.4 + rev: v0.8.6 hooks: - id: ruff args: [--exit-non-zero-on-fix] @@ -21,7 +21,7 @@ repos: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/rhysd/actionlint - rev: v1.7.4 + rev: v1.7.6 hooks: - id: actionlint args: [ From 32b860e319813f2bfc2499365b714da133c289d6 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:53:47 +0100 Subject: [PATCH 1027/1617] [stubgen] Improve self annotations (#18420) Print annotations for self variables if given. Aside from the most common ones for `str`, `int`, `bool` etc. those were previously inferred as `Incomplete`. --- mypy/stubgen.py | 10 +++++----- test-data/unit/stubgen.test | 11 +++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ca1fda27a976..27d868ed2624 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -648,11 +648,11 @@ def visit_func_def(self, o: FuncDef) -> None: self.add("\n") if not self.is_top_level(): self_inits = find_self_initializers(o) - for init, value in self_inits: + for init, value, annotation in self_inits: if init in self.method_names: # Can't have both an attribute and a method/property with the same name. continue - init_code = self.get_init(init, value) + init_code = self.get_init(init, value, annotation) if init_code: self.add(init_code) @@ -1414,7 +1414,7 @@ def find_method_names(defs: list[Statement]) -> set[str]: class SelfTraverser(mypy.traverser.TraverserVisitor): def __init__(self) -> None: - self.results: list[tuple[str, Expression]] = [] + self.results: list[tuple[str, Expression, Type | None]] = [] def visit_assignment_stmt(self, o: AssignmentStmt) -> None: lvalue = o.lvalues[0] @@ -1423,10 +1423,10 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and isinstance(lvalue.expr, NameExpr) and lvalue.expr.name == "self" ): - self.results.append((lvalue.name, o.rvalue)) + self.results.append((lvalue.name, o.rvalue, o.unanalyzed_type)) -def find_self_initializers(fdef: FuncBase) -> list[tuple[str, Expression]]: +def find_self_initializers(fdef: FuncBase) -> list[tuple[str, Expression, Type | None]]: """Find attribute initializers in a method. Return a list of pairs (attribute name, r.h.s. expression). diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 0801d9a27011..9cfe301a9d0b 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -238,13 +238,24 @@ class C: def __init__(self, x: str) -> None: ... [case testSelfAssignment] +from mod import A +from typing import Any, Dict, Union class C: def __init__(self): + self.a: A = A() self.x = 1 x.y = 2 + self.y: Dict[str, Any] = {} + self.z: Union[int, str, bool, None] = None [out] +from mod import A +from typing import Any + class C: + a: A x: int + y: dict[str, Any] + z: int | str | bool | None def __init__(self) -> None: ... [case testSelfAndClassBodyAssignment] From 306c1afb9f7a8f6d10cbd1ab05fabe4de53fdcd4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:54:24 +0100 Subject: [PATCH 1028/1617] [stubgen] Fix UnpackType for 3.11+ (#18421) Don't replace `*Ts` with `Unpack[Ts]` on Python 3.11+. This is broken currently since `Unpack` isn't added as import in the stub file. --- mypy/stubutil.py | 16 +++++++++++++++- test-data/unit/stubgen.test | 12 ++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index cbb3d2f77414..fecd9b29d57d 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -19,7 +19,16 @@ from mypy.moduleinspect import InspectError, ModuleInspect from mypy.nodes import PARAM_SPEC_KIND, TYPE_VAR_TUPLE_KIND, ClassDef, FuncDef, TypeAliasStmt from mypy.stubdoc import ArgSig, FunctionSig -from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType +from mypy.types import ( + AnyType, + NoneType, + Type, + TypeList, + TypeStrVisitor, + UnboundType, + UnionType, + UnpackType, +) # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () @@ -292,6 +301,11 @@ def visit_type_list(self, t: TypeList) -> str: def visit_union_type(self, t: UnionType) -> str: return " | ".join([item.accept(self) for item in t.items]) + def visit_unpack_type(self, t: UnpackType) -> str: + if self.options.python_version >= (3, 11): + return f"*{t.type.accept(self)}" + return super().visit_unpack_type(t) + def args_str(self, args: Iterable[Type]) -> str: """Convert an array of arguments to strings and join the results with commas. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 9cfe301a9d0b..5d35f1bb77ce 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1236,6 +1236,7 @@ from typing import Generic from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... [out] from typing import Generic from typing_extensions import TypeVarTuple, Unpack @@ -1244,11 +1245,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... + [case testGenericClassTypeVarTuple_semanal] from typing import Generic from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... [out] from typing import Generic from typing_extensions import TypeVarTuple, Unpack @@ -1257,11 +1261,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... + [case testGenericClassTypeVarTuplePy311] # flags: --python-version=3.11 from typing import Generic, TypeVarTuple Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... [out] from typing import Generic, TypeVarTuple @@ -1269,11 +1276,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... + [case testGenericClassTypeVarTuplePy311_semanal] # flags: --python-version=3.11 from typing import Generic, TypeVarTuple Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... [out] from typing import Generic, TypeVarTuple @@ -1281,6 +1291,8 @@ Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... + [case testObjectBaseClass] class A(object): ... [out] From 20355d5c9b54e2349d1eff49f0d635562d7acdaf Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:57:11 +0100 Subject: [PATCH 1029/1617] [stubgen] Preserve dataclass_transform decorator (#18418) Ref: https://github.com/python/mypy/issues/18081 --- mypy/stubgen.py | 29 +++++++- test-data/unit/stubgen.test | 130 +++++++++++++++++++++++++++++++++--- 2 files changed, 148 insertions(+), 11 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 27d868ed2624..c74e9f700861 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -113,6 +113,7 @@ Var, ) from mypy.options import Options as MypyOptions +from mypy.semanal_shared import find_dataclass_transform_spec from mypy.sharedparse import MAGIC_METHODS_POS_ARGS_ONLY from mypy.stubdoc import ArgSig, FunctionSig from mypy.stubgenc import InspectionStubGenerator, generate_stub_for_c_module @@ -139,6 +140,7 @@ has_yield_from_expression, ) from mypy.types import ( + DATACLASS_TRANSFORM_NAMES, OVERLOAD_NAMES, TPDICT_NAMES, TYPED_NAMEDTUPLE_NAMES, @@ -701,10 +703,13 @@ def process_decorator(self, o: Decorator) -> None: """ o.func.is_overload = False for decorator in o.original_decorators: - if not isinstance(decorator, (NameExpr, MemberExpr)): + d = decorator + if isinstance(d, CallExpr): + d = d.callee + if not isinstance(d, (NameExpr, MemberExpr)): continue - qualname = get_qualified_name(decorator) - fullname = self.get_fullname(decorator) + qualname = get_qualified_name(d) + fullname = self.get_fullname(d) if fullname in ( "builtins.property", "builtins.staticmethod", @@ -739,6 +744,9 @@ def process_decorator(self, o: Decorator) -> None: o.func.is_overload = True elif qualname.endswith((".setter", ".deleter")): self.add_decorator(qualname, require_name=False) + elif fullname in DATACLASS_TRANSFORM_NAMES: + p = AliasPrinter(self) + self._decorators.append(f"@{decorator.accept(p)}") def get_fullname(self, expr: Expression) -> str: """Return the expression's full name.""" @@ -785,6 +793,8 @@ def visit_class_def(self, o: ClassDef) -> None: self.add(f"{self._indent}{docstring}\n") n = len(self._output) self._vars.append([]) + if self.analyzed and find_dataclass_transform_spec(o): + self.processing_dataclass = True super().visit_class_def(o) self.dedent() self._vars.pop() @@ -854,6 +864,9 @@ def get_class_decorators(self, cdef: ClassDef) -> list[str]: decorators.append(d.accept(p)) self.import_tracker.require_name(get_qualified_name(d)) self.processing_dataclass = True + if self.is_dataclass_transform(d): + decorators.append(d.accept(p)) + self.import_tracker.require_name(get_qualified_name(d)) return decorators def is_dataclass(self, expr: Expression) -> bool: @@ -861,6 +874,16 @@ def is_dataclass(self, expr: Expression) -> bool: expr = expr.callee return self.get_fullname(expr) == "dataclasses.dataclass" + def is_dataclass_transform(self, expr: Expression) -> bool: + if isinstance(expr, CallExpr): + expr = expr.callee + if self.get_fullname(expr) in DATACLASS_TRANSFORM_NAMES: + return True + if find_dataclass_transform_spec(expr) is not None: + self.processing_dataclass = True + return True + return False + def visit_block(self, o: Block) -> None: # Unreachable statements may be partially uninitialized and that may # cause trouble. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 5d35f1bb77ce..fa462dc23a9a 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -3104,15 +3104,12 @@ class C: x = attrs.field() [out] -from _typeshed import Incomplete +import attrs +@attrs.define class C: - x: Incomplete + x = ... def __init__(self, x) -> None: ... - def __lt__(self, other): ... - def __le__(self, other): ... - def __gt__(self, other): ... - def __ge__(self, other): ... [case testNamedTupleInClass] from collections import namedtuple @@ -4249,6 +4246,122 @@ class Y(missing.Base): generated_kwargs_: float def __init__(self, *generated_args__, generated_args, generated_args_, generated_kwargs, generated_kwargs_, **generated_kwargs__) -> None: ... +[case testDataclassTransform] +# dataclass_transform detection only works with sementic analysis. +# Test stubgen doesn't break too badly without it. +from typing_extensions import dataclass_transform + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): + return cls + +@create_model +class X: + a: int + b: str = "hello" + +@typing_extensions.dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class Y(ModelBase): + a: int + b: str = "hello" + +@typing_extensions.dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class Z(metaclass=DCMeta): + a: int + b: str = "hello" + +[out] +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): ... + +class X: + a: int + b: str + +@typing_extensions.dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class Y(ModelBase): + a: int + b: str + +@typing_extensions.dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class Z(metaclass=DCMeta): + a: int + b: str + +[case testDataclassTransformDecorator_semanal] +import typing_extensions + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): + return cls + +@create_model +class X: + a: int + b: str = "hello" + +[out] +import typing_extensions + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): ... + +@create_model +class X: + a: int + b: str = ... + def __init__(self, *, a, b=...) -> None: ... + +[case testDataclassTransformClass_semanal] +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class X(ModelBase): + a: int + b: str = "hello" + +[out] +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class X(ModelBase): + a: int + b: str = ... + def __init__(self, *, a, b=...) -> None: ... + +[case testDataclassTransformMetaclass_semanal] +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: int + b: str = "hello" + +[out] +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: int + b: str = ... + def __init__(self, *, a, b=...) -> None: ... + [case testAlwaysUsePEP604Union] import typing import typing as t @@ -4536,16 +4649,17 @@ def f5[T5 = int]() -> None: ... # flags: --include-private --python-version=3.13 from typing_extensions import dataclass_transform -# TODO: preserve dataclass_transform decorator @dataclass_transform() class DCMeta(type): ... class DC(metaclass=DCMeta): x: str [out] +from typing_extensions import dataclass_transform + +@dataclass_transform() class DCMeta(type): ... class DC(metaclass=DCMeta): x: str def __init__(self, x) -> None: ... - def __replace__(self, *, x) -> None: ... From ccf05db67f6f99878c73eb902fc59a6f037b18a6 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Mon, 6 Jan 2025 22:37:00 -0800 Subject: [PATCH 1030/1617] Fix list index while checking for Enum class. (#18426) Fixes mypyc/mypyc#1080 Python requires that Enum must be the last class in the parent class list. This change fixes the index in `ClassDef.bases` list where we check for `Enum`. --- mypyc/irbuild/classdef.py | 3 ++- mypyc/test-data/run-classes.test | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 6072efa2c593..84dd493c6d15 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -682,7 +682,8 @@ def add_non_ext_class_attr( # are final. if ( cdef.info.bases - and cdef.info.bases[0].type.is_enum + # Enum class must be the last parent class. + and cdef.info.bases[-1].type.is_enum # Skip these since Enum will remove it and lvalue.name not in EXCLUDED_ENUM_ATTRIBUTES ): diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index db5459e22f5e..0eab15d89746 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2692,3 +2692,16 @@ print(native.C(22).v) [out] 22.1 + +[case testLastParentEnum] +from enum import Enum + +class ColorCode(str, Enum): + OKGREEN = "okgreen" + +[file driver.py] +import native +print(native.ColorCode.OKGREEN.value) + +[out] +okgreen From 106f714ad5967b82dcacd965c46fe6bbff38c05d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:22:10 +0100 Subject: [PATCH 1031/1617] Remove unnecessary mypyc files from wheel (#18416) Remove mypyc docs and some testing files from wheels. They aren't included for mypy itself as well. The sdist content will stay the same, so it's possible for distributors to continue to run the tests. Files which will no longer be included ``` mypyc/README.md mypyc/doc/** mypyc/external/googletest/** mypyc/lib-rt/setup.py mypyc/lib-rt/test_capi.cc mypyc/test-data/** ``` --- MANIFEST.in | 3 +++ pyproject.toml | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index f9992d44e7ff..80d73ab5f48e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -26,8 +26,10 @@ prune docs/source/_build # assorted mypyc requirements graft mypyc/external graft mypyc/lib-rt +graft mypyc/test graft mypyc/test-data graft mypyc/doc +prune mypyc/doc/build # files necessary for testing sdist include mypy-requirements.txt @@ -37,6 +39,7 @@ include test-requirements.txt include mypy_self_check.ini prune misc graft test-data +graft mypy/test include conftest.py include runtests.py include pytest.ini diff --git a/pyproject.toml b/pyproject.toml index 5edbc8a75224..157c26385e4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ mypyc = "mypyc.__main__:main" [tool.setuptools.packages.find] include = ["mypy*", "mypyc*", "*__mypyc*"] +exclude = ["mypyc.test-data*"] namespaces = false [tool.setuptools.package-data] @@ -89,6 +90,15 @@ mypy = [ "xml/*.xslt", "xml/*.css", ] +[tool.setuptools.exclude-package-data] +mypyc = [ + "README.md", + "doc/**", + "external/**", + "lib-rt/test_capi.cc", + "lib-rt/setup.py", + "test-data/**", +] [tool.black] line-length = 99 From d86b1e52a865cde01a4fbc142ec3d28d00dc6e48 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sat, 11 Jan 2025 19:38:32 +0100 Subject: [PATCH 1032/1617] Fix attribute type resolution with multiple inheritance (#18415) Fixes #18268. Fixes #9319. Fixes #14279. Fixes #9031. Supersedes #18270 as requested by @ilevkivskyi. This PR introduces two changes: * Add missing `map_type_from_supertype` when checking generic attributes * Only compare the first base defining a name to all following in MRO - others are not necessarily pairwise compatible. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/checker.py | 25 +++++++------ test-data/unit/check-generic-subtyping.test | 35 +++++++++++++++++++ .../unit/check-multiple-inheritance.test | 26 ++++++++++++++ 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3d0f40283606..80de4254766b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2733,19 +2733,20 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: return # Verify that inherited attributes are compatible. mro = typ.mro[1:] - for i, base in enumerate(mro): + all_names = {name for base in mro for name in base.names} + for name in sorted(all_names - typ.names.keys()): + # Sort for reproducible message order. # Attributes defined in both the type and base are skipped. # Normal checks for attribute compatibility should catch any problems elsewhere. - non_overridden_attrs = base.names.keys() - typ.names.keys() - for name in non_overridden_attrs: - if is_private(name): - continue - for base2 in mro[i + 1 :]: - # We only need to check compatibility of attributes from classes not - # in a subclass relationship. For subclasses, normal (single inheritance) - # checks suffice (these are implemented elsewhere). - if name in base2.names and base2 not in base.mro: - self.check_compatibility(name, base, base2, typ) + if is_private(name): + continue + # Compare the first base defining a name with the rest. + # Remaining bases may not be pairwise compatible as the first base provides + # the used definition. + i, base = next((i, base) for i, base in enumerate(mro) if name in base.names) + for base2 in mro[i + 1 :]: + if name in base2.names and base2 not in base.mro: + self.check_compatibility(name, base, base2, typ) def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None: if sym.type is not None: @@ -2826,8 +2827,10 @@ class C(B, A[int]): ... # this is unsafe because... ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True) elif first_type and second_type: if isinstance(first.node, Var): + first_type = get_proper_type(map_type_from_supertype(first_type, ctx, base1)) first_type = expand_self_type(first.node, first_type, fill_typevars(ctx)) if isinstance(second.node, Var): + second_type = get_proper_type(map_type_from_supertype(second_type, ctx, base2)) second_type = expand_self_type(second.node, second_type, fill_typevars(ctx)) ok = is_equivalent(first_type, second_type) if not ok: diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 90180e0f83f6..03a0654520fd 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -1065,3 +1065,38 @@ class F(E[T_co], Generic[T_co]): ... # E: Variance of TypeVar "T_co" incompatib class G(Generic[T]): ... class H(G[T_contra], Generic[T_contra]): ... # E: Variance of TypeVar "T_contra" incompatible with variance in parent type + +[case testMultipleInheritanceCompatibleTypeVar] +from typing import Generic, TypeVar + +T = TypeVar("T") +U = TypeVar("U") + +class A(Generic[T]): + x: T + def fn(self, t: T) -> None: ... + +class A2(A[T]): + y: str + z: str + +class B(Generic[T]): + x: T + def fn(self, t: T) -> None: ... + +class C1(A2[str], B[str]): pass +class C2(A2[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +class C3(A2[T], B[T]): pass +class C4(A2[U], B[U]): pass +class C5(A2[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" + +class D1(A[str], B[str]): pass +class D2(A[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +class D3(A[T], B[T]): pass +class D4(A[U], B[U]): pass +class D5(A[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-multiple-inheritance.test b/test-data/unit/check-multiple-inheritance.test index d03f2e35e1c4..9cb3bd2e7ca2 100644 --- a/test-data/unit/check-multiple-inheritance.test +++ b/test-data/unit/check-multiple-inheritance.test @@ -706,3 +706,29 @@ class C34(B3, B4): ... class C41(B4, B1): ... class C42(B4, B2): ... class C43(B4, B3): ... + +[case testMultipleInheritanceExplicitDiamondResolution] +# Adapted from #14279 +class A: + class M: + pass + +class B0(A): + class M(A.M): + pass + +class B1(A): + class M(A.M): + pass + +class C(B0,B1): + class M(B0.M, B1.M): + pass + +class D0(B0): + pass +class D1(B1): + pass + +class D(D0,D1,C): + pass From 1affabe0aafabb27ea909d1daf0f3d05c0acd3ae Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 11 Jan 2025 19:53:16 +0100 Subject: [PATCH 1033/1617] Fix mypyc wheel tests (#18444) #18416 removed the `mypyc/test-data` package from the wheel. This caused the wheel tests to fail. Use the `test_data_prefix` instead which uses the existing `MYPY_TEST_PREFIX` to determine the correct file location. https://github.com/mypyc/mypy_mypyc-wheels/actions/runs/12712285989 --- mypyc/test/test_run.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 71367b25880b..03d9f0486107 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -24,6 +24,7 @@ from mypyc.codegen import emitmodule from mypyc.errors import Errors from mypyc.options import CompilerOptions +from mypyc.test.config import test_data_prefix from mypyc.test.test_serialization import check_serialization_roundtrip from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, @@ -291,9 +292,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> # No driver.py provided by test case. Use the default one # (mypyc/test-data/driver/driver.py) that calls each # function named test_*. - default_driver = os.path.join( - os.path.dirname(__file__), "..", "test-data", "driver", "driver.py" - ) + default_driver = os.path.join(test_data_prefix, "driver", "driver.py") shutil.copy(default_driver, driver_path) env = os.environ.copy() env["MYPYC_RUN_BENCH"] = "1" if bench else "0" From 9274a07bfa3c92b38fe35cf9736beb068ae9196b Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sat, 11 Jan 2025 22:48:00 +0100 Subject: [PATCH 1034/1617] Fix parent generics mapping when overriding generic attribute with property (#18441) Fixes #18189. Following #18415, this fixes one more place where parent class generics aren't mapped to attributes correctly. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/checker.py | 7 ++ test-data/unit/check-generic-subtyping.test | 120 ++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 80de4254766b..6a53d12791c5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2095,6 +2095,13 @@ def check_method_override_for_base_with_name( if original_node and is_property(original_node): original_type = get_property_type(original_type) + if isinstance(original_node, Var): + expanded_type = map_type_from_supertype(original_type, defn.info, base) + expanded_type = expand_self_type( + original_node, expanded_type, fill_typevars(defn.info) + ) + original_type = get_proper_type(expanded_type) + if is_property(defn): inner: FunctionLike | None if isinstance(typ, FunctionLike): diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 03a0654520fd..89465869f09d 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -1066,6 +1066,126 @@ class F(E[T_co], Generic[T_co]): ... # E: Variance of TypeVar "T_co" incompatib class G(Generic[T]): ... class H(G[T_contra], Generic[T_contra]): ... # E: Variance of TypeVar "T_contra" incompatible with variance in parent type +[case testParameterizedGenericOverrideWithProperty] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class A(Generic[T]): + def __init__(self, val: T): + self.member: T = val + +class B(A[str]): + member: str + +class GoodPropertyOverride(A[str]): + @property + def member(self) -> str: ... + @member.setter + def member(self, val: str): ... + +class BadPropertyOverride(A[str]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: int + def member(self) -> int: ... + @member.setter + def member(self, val: int): ... + +class BadGenericPropertyOverride(A[str], Generic[T]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: T + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericPropertyOverrideWithProperty] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class A(Generic[T]): + @property + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... + +class B(A[str]): + member: str + +class GoodPropertyOverride(A[str]): + @property + def member(self) -> str: ... + @member.setter + def member(self, val: str): ... + +class BadPropertyOverride(A[str]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: int + def member(self) -> int: ... + @member.setter + def member(self, val: int): ... + +class BadGenericPropertyOverride(A[str], Generic[T]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: T + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericOverrideSelfWithProperty] +from typing_extensions import Self + +class A: + def __init__(self, val: Self): + self.member: Self = val + +class GoodPropertyOverride(A): + @property + def member(self) -> "GoodPropertyOverride": ... + @member.setter + def member(self, val: "GoodPropertyOverride"): ... + +class GoodPropertyOverrideSelf(A): + @property + def member(self) -> Self: ... + @member.setter + def member(self, val: Self): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericOverrideWithSelfProperty] +from typing import TypeVar, Generic +from typing_extensions import Self + +T = TypeVar("T") + +class A(Generic[T]): + def __init__(self, val: T): + self.member: T = val + +class B(A["B"]): + member: Self + +class GoodPropertyOverride(A["GoodPropertyOverride"]): + @property + def member(self) -> Self: ... + @member.setter + def member(self, val: Self): ... +[builtins fixtures/property.pyi] + [case testMultipleInheritanceCompatibleTypeVar] from typing import Generic, TypeVar From 9685171372e003f2c0bad28706f44fea2d5782b0 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sun, 12 Jan 2025 23:24:06 +0100 Subject: [PATCH 1035/1617] Avoid false `unreachable` and `redundant-expr` warnings in loops. (#18433) Fixes #18348 Fixes #13973 Fixes #11612 Fixes #8721 Fixes #8865 Fixes #7204 I manually checked all the listed issues. Some of them were already partly fixed by #18180. --- mypy/checker.py | 29 ++++++++++++++++++++++++----- test-data/unit/check-narrowing.test | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6a53d12791c5..f6193a1273eb 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -584,14 +584,21 @@ def accept_loop( *, exit_condition: Expression | None = None, ) -> None: - """Repeatedly type check a loop body until the frame doesn't change. - If exit_condition is set, assume it must be False on exit from the loop. + """Repeatedly type check a loop body until the frame doesn't change.""" - Then check the else_body. - """ - # The outer frame accumulates the results of all iterations + # The outer frame accumulates the results of all iterations: with self.binder.frame_context(can_skip=False, conditional_frame=True): + + # Check for potential decreases in the number of partial types so as not to stop the + # iteration too early: partials_old = sum(len(pts.map) for pts in self.partial_types) + + # Disable error types that we cannot safely identify in intermediate iteration steps: + warn_unreachable = self.options.warn_unreachable + warn_redundant = codes.REDUNDANT_EXPR in self.options.enabled_error_codes + self.options.warn_unreachable = False + self.options.enabled_error_codes.discard(codes.REDUNDANT_EXPR) + while True: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): self.accept(body) @@ -599,9 +606,21 @@ def accept_loop( if (partials_new == partials_old) and not self.binder.last_pop_changed: break partials_old = partials_new + + # If necessary, reset the modified options and make up for the postponed error checks: + self.options.warn_unreachable = warn_unreachable + if warn_redundant: + self.options.enabled_error_codes.add(codes.REDUNDANT_EXPR) + if warn_unreachable or warn_redundant: + with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): + self.accept(body) + + # If exit_condition is set, assume it must be False on exit from the loop: if exit_condition: _, else_map = self.find_isinstance_check(exit_condition) self.push_type_map(else_map) + + # Check the else body: if else_body: self.accept(else_body) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index ac6c6436ba8d..b9866c67c86c 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2390,3 +2390,29 @@ class A: z.append(1) [builtins fixtures/primitives.pyi] + +[case testAvoidFalseUnreachableInLoop] +# flags: --warn-unreachable --python-version 3.11 + +def f() -> int | None: ... +def b() -> bool: ... + +x: int | None +x = 1 +while x is not None or b(): + x = f() + +[builtins fixtures/bool.pyi] + +[case testAvoidFalseRedundantExprInLoop] +# flags: --enable-error-code redundant-expr --python-version 3.11 + +def f() -> int | None: ... +def b() -> bool: ... + +x: int | None +x = 1 +while x is not None and b(): + x = f() + +[builtins fixtures/primitives.pyi] From ee364ce34b1e97d1e5ddecebbb0ccc51a6de735f Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 13 Jan 2025 07:25:02 +0100 Subject: [PATCH 1036/1617] Allow `Any` to match sequence patterns in match/case (#18448) Fixes #17095 (comment, the primary issue was already fixed somewhere before). Fixes #16272. Fixes #12532. Fixes #12770. Prior to this PR mypy did not consider that `Any` can match any patterns, including sequence patterns (e.g. `case [_]`). This PR allows matching `Any` against any such patterns. --- mypy/checkpattern.py | 5 +- mypyc/test-data/irbuild-match.test | 164 +++++++++++++++------------- test-data/unit/check-python310.test | 32 ++++++ 3 files changed, 124 insertions(+), 77 deletions(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 43f42039b199..4b34c0ddb54b 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -713,6 +713,8 @@ def should_self_match(self, typ: Type) -> bool: return False def can_match_sequence(self, typ: ProperType) -> bool: + if isinstance(typ, AnyType): + return True if isinstance(typ, UnionType): return any(self.can_match_sequence(get_proper_type(item)) for item in typ.items) for other in self.non_sequence_match_types: @@ -763,6 +765,8 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: or class T(Sequence[Tuple[T, T]]), there is no way any of those can map to Sequence[str]. """ proper_type = get_proper_type(outer_type) + if isinstance(proper_type, AnyType): + return outer_type if isinstance(proper_type, UnionType): types = [ self.construct_sequence_child(item, inner_type) @@ -772,7 +776,6 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: return make_simplified_union(types) sequence = self.chk.named_generic_type("typing.Sequence", [inner_type]) if is_subtype(outer_type, self.chk.named_type("typing.Sequence")): - proper_type = get_proper_type(outer_type) if isinstance(proper_type, TupleType): proper_type = tuple_fallback(proper_type) assert isinstance(proper_type, Instance) diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index ba9a0d5464ea..bd8878c5009e 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -1378,14 +1378,15 @@ def f(x): r15 :: bit r16 :: bool r17 :: native_int - r18, rest :: object - r19 :: str - r20 :: object - r21 :: str - r22 :: object - r23 :: object[1] - r24 :: object_ptr - r25, r26 :: object + r18 :: object + r19, rest :: list + r20 :: str + r21 :: object + r22 :: str + r23 :: object + r24 :: object[1] + r25 :: object_ptr + r26, r27 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1414,21 +1415,23 @@ L3: L4: r17 = r2 - 0 r18 = PySequence_GetSlice(x, 2, r17) - rest = r18 + r19 = cast(list, r18) + rest = r19 L5: - r19 = 'matched' - r20 = builtins :: module - r21 = 'print' - r22 = CPyObject_GetAttr(r20, r21) - r23 = [r19] - r24 = load_address r23 - r25 = _PyObject_Vectorcall(r22, r24, 1, 0) - keep_alive r19 + r20 = 'matched' + r21 = builtins :: module + r22 = 'print' + r23 = CPyObject_GetAttr(r21, r22) + r24 = [r20] + r25 = load_address r24 + r26 = _PyObject_Vectorcall(r23, r25, 1, 0) + keep_alive r20 goto L7 L6: L7: - r26 = box(None, 1) - return r26 + r27 = box(None, 1) + return r27 + [case testMatchSequenceWithStarPatternInTheMiddle_python3_10] def f(x): match x: @@ -1455,14 +1458,15 @@ def f(x): r16 :: bit r17 :: bool r18 :: native_int - r19, rest :: object - r20 :: str - r21 :: object - r22 :: str - r23 :: object - r24 :: object[1] - r25 :: object_ptr - r26, r27 :: object + r19 :: object + r20, rest :: list + r21 :: str + r22 :: object + r23 :: str + r24 :: object + r25 :: object[1] + r26 :: object_ptr + r27, r28 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1492,21 +1496,23 @@ L3: L4: r18 = r2 - 1 r19 = PySequence_GetSlice(x, 1, r18) - rest = r19 + r20 = cast(list, r19) + rest = r20 L5: - r20 = 'matched' - r21 = builtins :: module - r22 = 'print' - r23 = CPyObject_GetAttr(r21, r22) - r24 = [r20] - r25 = load_address r24 - r26 = _PyObject_Vectorcall(r23, r25, 1, 0) - keep_alive r20 + r21 = 'matched' + r22 = builtins :: module + r23 = 'print' + r24 = CPyObject_GetAttr(r22, r23) + r25 = [r21] + r26 = load_address r25 + r27 = _PyObject_Vectorcall(r24, r26, 1, 0) + keep_alive r21 goto L7 L6: L7: - r27 = box(None, 1) - return r27 + r28 = box(None, 1) + return r28 + [case testMatchSequenceWithStarPatternAtTheStart_python3_10] def f(x): match x: @@ -1530,14 +1536,15 @@ def f(x): r17 :: bit r18 :: bool r19 :: native_int - r20, rest :: object - r21 :: str - r22 :: object - r23 :: str - r24 :: object - r25 :: object[1] - r26 :: object_ptr - r27, r28 :: object + r20 :: object + r21, rest :: list + r22 :: str + r23 :: object + r24 :: str + r25 :: object + r26 :: object[1] + r27 :: object_ptr + r28, r29 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1568,21 +1575,23 @@ L3: L4: r19 = r2 - 2 r20 = PySequence_GetSlice(x, 0, r19) - rest = r20 + r21 = cast(list, r20) + rest = r21 L5: - r21 = 'matched' - r22 = builtins :: module - r23 = 'print' - r24 = CPyObject_GetAttr(r22, r23) - r25 = [r21] - r26 = load_address r25 - r27 = _PyObject_Vectorcall(r24, r26, 1, 0) - keep_alive r21 + r22 = 'matched' + r23 = builtins :: module + r24 = 'print' + r25 = CPyObject_GetAttr(r23, r24) + r26 = [r22] + r27 = load_address r26 + r28 = _PyObject_Vectorcall(r25, r27, 1, 0) + keep_alive r22 goto L7 L6: L7: - r28 = box(None, 1) - return r28 + r29 = box(None, 1) + return r29 + [case testMatchBuiltinClassPattern_python3_10] def f(x): match x: @@ -1634,14 +1643,15 @@ def f(x): r2 :: native_int r3, r4 :: bit r5 :: native_int - r6, rest :: object - r7 :: str - r8 :: object - r9 :: str - r10 :: object - r11 :: object[1] - r12 :: object_ptr - r13, r14 :: object + r6 :: object + r7, rest :: list + r8 :: str + r9 :: object + r10 :: str + r11 :: object + r12 :: object[1] + r13 :: object_ptr + r14, r15 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1654,21 +1664,23 @@ L1: L2: r5 = r2 - 0 r6 = PySequence_GetSlice(x, 0, r5) - rest = r6 + r7 = cast(list, r6) + rest = r7 L3: - r7 = 'matched' - r8 = builtins :: module - r9 = 'print' - r10 = CPyObject_GetAttr(r8, r9) - r11 = [r7] - r12 = load_address r11 - r13 = _PyObject_Vectorcall(r10, r12, 1, 0) - keep_alive r7 + r8 = 'matched' + r9 = builtins :: module + r10 = 'print' + r11 = CPyObject_GetAttr(r9, r10) + r12 = [r8] + r13 = load_address r12 + r14 = _PyObject_Vectorcall(r11, r13, 1, 0) + keep_alive r8 goto L5 L4: L5: - r14 = box(None, 1) - return r14 + r15 = box(None, 1) + return r15 + [case testMatchTypeAnnotatedNativeClass_python3_10] class A: a: int diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 616846789c98..d4af449fc7d7 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -2439,3 +2439,35 @@ def foo(x: T) -> T: return out [builtins fixtures/isinstance.pyi] + +[case testMatchSequenceReachableFromAny] +# flags: --warn-unreachable +from typing import Any + +def maybe_list(d: Any) -> int: + match d: + case []: + return 0 + case [[_]]: + return 1 + case [_]: + return 1 + case _: + return 2 + +def with_guard(d: Any) -> None: + match d: + case [s] if isinstance(s, str): + reveal_type(s) # N: Revealed type is "builtins.str" + match d: + case (s,) if isinstance(s, str): + reveal_type(s) # N: Revealed type is "builtins.str" + +def nested_in_dict(d: dict[str, Any]) -> int: + match d: + case {"src": ["src"]}: + return 1 + case _: + return 0 + +[builtins fixtures/dict.pyi] From a49d99139ace6627143fd8a913f04252c0f29e1e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 12 Jan 2025 22:58:10 -0800 Subject: [PATCH 1037/1617] Update CHANGELOG.md (#18453) --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e9d0078a36..e5260104f3fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ ### Performance improvements -TODO +Mypy may be 5-30% faster. This improvement comes largely from tuning the performance of the +garbage collector. + +Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306)). ### Drop Support for Python 3.8 @@ -33,13 +36,14 @@ Use this flag to disable this behavior. `--strict-bytes` will be enabled by defa Contributed by Ali Hamdan (PR [18137](https://github.com/python/mypy/pull/18263/)) and Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). -### Improvements to partial type handling in loops +### Improvements to reachability analysis and partial type handling in loops This change results in mypy better modelling control flow within loops and hence detecting several issues it previously did not detect. In some cases, this change may require use of an additional explicit annotation of a variable. -Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180)). +Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180), +[PR](https://github.com/python/mypy/pull/18433)). (Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` by default in **mypy 2.0**). From ee1f4c9650fc3fb7bfb403a3310f1a71e4b4ebe2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 13 Jan 2025 13:26:25 +0300 Subject: [PATCH 1038/1617] Update docs not to mention 3.8 where possible (#18455) I updated docs to not mention EOL 3.8, where it is possible to use other versions / examples. --- docs/source/common_issues.rst | 4 ++-- docs/source/runtime_troubles.rst | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 4cb00e55c2f3..7165955e67d3 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -427,8 +427,8 @@ More specifically, mypy will understand the use of :py:data:`sys.version_info` a import sys # Distinguishing between different versions of Python: - if sys.version_info >= (3, 8): - # Python 3.8+ specific definitions and imports + if sys.version_info >= (3, 13): + # Python 3.13+ specific definitions and imports else: # Other definitions and imports diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index d039db30f3fa..d63d0f9a74ae 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -335,16 +335,14 @@ Using new additions to the typing module ---------------------------------------- You may find yourself wanting to use features added to the :py:mod:`typing` -module in earlier versions of Python than the addition, for example, using any -of ``Literal``, ``Protocol``, ``TypedDict`` with Python 3.6. +module in earlier versions of Python than the addition. The easiest way to do this is to install and use the ``typing_extensions`` package from PyPI for the relevant imports, for example: .. code-block:: python - from typing_extensions import Literal - x: Literal["open", "close"] + from typing_extensions import TypeIs If you don't want to rely on ``typing_extensions`` being installed on newer Pythons, you could alternatively use: @@ -352,12 +350,10 @@ Pythons, you could alternatively use: .. code-block:: python import sys - if sys.version_info >= (3, 8): - from typing import Literal + if sys.version_info >= (3, 13): + from typing import TypeIs else: - from typing_extensions import Literal - - x: Literal["open", "close"] + from typing_extensions import TypeIs This plays nicely well with following :pep:`508` dependency specification: -``typing_extensions; python_version<"3.8"`` +``typing_extensions; python_version<"3.13"`` From 469b4e4e55fe03cb4e50e21715a94b7172809ec5 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:38:56 +0100 Subject: [PATCH 1039/1617] Unwrap `type[Union[...]]` when solving typevar constraints (#18266) Closes #18265, closes #12115. `type[A | B]` is internally represented as `type[A] | type[B]`, and this causes problems for a typevar solver. Prevent using meet in such cases by unwraping `type[...]` if both sides have such shape. --- mypy/constraints.py | 38 +++++++++++++++++- test-data/unit/check-typevar-unbound.test | 47 +++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 848dec07cbcb..45a96b993563 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Final +from typing import TYPE_CHECKING, Final, cast +from typing_extensions import TypeGuard import mypy.subtypes import mypy.typeops @@ -340,6 +341,16 @@ def _infer_constraints( if isinstance(actual, AnyType) and actual.type_of_any == TypeOfAny.suggestion_engine: return [] + # type[A | B] is always represented as type[A] | type[B] internally. + # This makes our constraint solver choke on type[T] <: type[A] | type[B], + # solving T as generic meet(A, B) which is often `object`. Force unwrap such unions + # if both sides are type[...] or unions thereof. See `testTypeVarType` test + type_type_unwrapped = False + if _is_type_type(template) and _is_type_type(actual): + type_type_unwrapped = True + template = _unwrap_type_type(template) + actual = _unwrap_type_type(actual) + # If the template is simply a type variable, emit a Constraint directly. # We need to handle this case before handling Unions for two reasons: # 1. "T <: Union[U1, U2]" is not equivalent to "T <: U1 or T <: U2", @@ -373,6 +384,11 @@ def _infer_constraints( if direction == SUPERTYPE_OF and isinstance(actual, UnionType): res = [] for a_item in actual.items: + # `orig_template` has to be preserved intact in case it's recursive. + # If we unwraped ``type[...]`` previously, wrap the item back again, + # as ``type[...]`` can't be removed from `orig_template`. + if type_type_unwrapped: + a_item = TypeType.make_normalized(a_item) res.extend(infer_constraints(orig_template, a_item, direction)) return res @@ -411,6 +427,26 @@ def _infer_constraints( return template.accept(ConstraintBuilderVisitor(actual, direction, skip_neg_op)) +def _is_type_type(tp: ProperType) -> TypeGuard[TypeType | UnionType]: + """Is ``tp`` a ``type[...]`` or a union thereof? + + ``Type[A | B]`` is internally represented as ``type[A] | type[B]``, and this + troubles the solver sometimes. + """ + return ( + isinstance(tp, TypeType) + or isinstance(tp, UnionType) + and all(isinstance(get_proper_type(o), TypeType) for o in tp.items) + ) + + +def _unwrap_type_type(tp: TypeType | UnionType) -> ProperType: + """Extract the inner type from ``type[...]`` expression or a union thereof.""" + if isinstance(tp, TypeType): + return tp.item + return UnionType.make_union([cast(TypeType, get_proper_type(o)).item for o in tp.items]) + + def infer_constraints_if_possible( template: Type, actual: Type, direction: int ) -> list[Constraint] | None: diff --git a/test-data/unit/check-typevar-unbound.test b/test-data/unit/check-typevar-unbound.test index ed6beaa100db..587ae6577328 100644 --- a/test-data/unit/check-typevar-unbound.test +++ b/test-data/unit/check-typevar-unbound.test @@ -69,3 +69,50 @@ from typing import TypeVar T = TypeVar("T") def f(t: T) -> None: a, *b = t # E: "object" object is not iterable + +[case testTypeVarType] +from typing import Mapping, Type, TypeVar, Union +T = TypeVar("T") + +class A: ... +class B: ... + +lookup_table: Mapping[str, Type[Union[A,B]]] +def load(lookup_table: Mapping[str, Type[T]], lookup_key: str) -> T: + ... +reveal_type(load(lookup_table, "a")) # N: Revealed type is "Union[__main__.A, __main__.B]" + +lookup_table_a: Mapping[str, Type[A]] +def load2(lookup_table: Mapping[str, Type[Union[T, int]]], lookup_key: str) -> T: + ... +reveal_type(load2(lookup_table_a, "a")) # N: Revealed type is "__main__.A" + +[builtins fixtures/tuple.pyi] + +[case testTypeVarTypeAssignment] +# Adapted from https://github.com/python/mypy/issues/12115 +from typing import TypeVar, Type, Callable, Union, Any + +t1: Type[bool] = bool +t2: Union[Type[bool], Type[str]] = bool + +T1 = TypeVar("T1", bound=Union[bool, str]) +def foo1(t: Type[T1]) -> None: ... +foo1(t1) +foo1(t2) + +T2 = TypeVar("T2", bool, str) +def foo2(t: Type[T2]) -> None: ... +foo2(t1) +# Rejected correctly: T2 cannot be Union[bool, str] +foo2(t2) # E: Value of type variable "T2" of "foo2" cannot be "Union[bool, str]" + +T3 = TypeVar("T3") +def foo3(t: Type[T3]) -> None: ... +foo3(t1) +foo3(t2) + +def foo4(t: Type[Union[bool, str]]) -> None: ... +foo4(t1) +foo4(t2) +[builtins fixtures/tuple.pyi] From 9be49b3b15cd26ce712ff286719dc7af61fa1ad5 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:08:41 +0100 Subject: [PATCH 1040/1617] Prevent crashing when `match` arms use name of existing callable (#18449) Fixes #16793. Fixes crash in #13666. Previously mypy considered that variables in match/case patterns must be Var's, causing a hard crash when a name of captured pattern clashes with a name of some existing function. This PR removes such assumption about Var and allows other nodes. --- mypy/checker.py | 19 +++++++---- test-data/unit/check-python310.test | 51 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index f6193a1273eb..79d178f3c644 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5402,17 +5402,21 @@ def _get_recursive_sub_patterns_map( return sub_patterns_map - def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[Var, Type]: - all_captures: dict[Var, list[tuple[NameExpr, Type]]] = defaultdict(list) + def infer_variable_types_from_type_maps( + self, type_maps: list[TypeMap] + ) -> dict[SymbolNode, Type]: + # Type maps may contain variables inherited from previous code which are not + # necessary `Var`s (e.g. a function defined earlier with the same name). + all_captures: dict[SymbolNode, list[tuple[NameExpr, Type]]] = defaultdict(list) for tm in type_maps: if tm is not None: for expr, typ in tm.items(): if isinstance(expr, NameExpr): node = expr.node - assert isinstance(node, Var) + assert node is not None all_captures[node].append((expr, typ)) - inferred_types: dict[Var, Type] = {} + inferred_types: dict[SymbolNode, Type] = {} for var, captures in all_captures.items(): already_exists = False types: list[Type] = [] @@ -5436,16 +5440,19 @@ def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[ new_type = UnionType.make_union(types) # Infer the union type at the first occurrence first_occurrence, _ = captures[0] + # If it didn't exist before ``match``, it's a Var. + assert isinstance(var, Var) inferred_types[var] = new_type self.infer_variable_type(var, first_occurrence, new_type, first_occurrence) return inferred_types - def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, Type]) -> None: + def remove_capture_conflicts( + self, type_map: TypeMap, inferred_types: dict[SymbolNode, Type] + ) -> None: if type_map: for expr, typ in list(type_map.items()): if isinstance(expr, NameExpr): node = expr.node - assert isinstance(node, Var) if node not in inferred_types or not is_subtype(typ, inferred_types[node]): del type_map[expr] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index d4af449fc7d7..9adb798c4ae7 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -2471,3 +2471,54 @@ def nested_in_dict(d: dict[str, Any]) -> int: return 0 [builtins fixtures/dict.pyi] + +[case testMatchRebindsOuterFunctionName] +# flags: --warn-unreachable +from typing_extensions import Literal + +def x() -> tuple[Literal["test"]]: ... + +match x(): + case (x,) if x == "test": # E: Incompatible types in capture pattern (pattern captures type "Literal['test']", variable has type "Callable[[], Tuple[Literal['test']]]") + reveal_type(x) # N: Revealed type is "def () -> Tuple[Literal['test']]" + case foo: + foo + +[builtins fixtures/dict.pyi] + +[case testMatchRebindsInnerFunctionName] +# flags: --warn-unreachable +class Some: + value: int | str + __match_args__ = ("value",) + +def fn1(x: Some | int | str) -> None: + match x: + case int(): + def value(): + return 1 + reveal_type(value) # N: Revealed type is "def () -> Any" + case str(): + def value(): + return 1 + reveal_type(value) # N: Revealed type is "def () -> Any" + case Some(value): # E: Incompatible types in capture pattern (pattern captures type "Union[int, str]", variable has type "Callable[[], Any]") + pass + +def fn2(x: Some | int | str) -> None: + match x: + case int(): + def value() -> str: + return "" + reveal_type(value) # N: Revealed type is "def () -> builtins.str" + case str(): + def value() -> int: # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def value() -> str \ + # N: Redefinition: \ + # N: def value() -> int + return 1 + reveal_type(value) # N: Revealed type is "def () -> builtins.str" + case Some(value): # E: Incompatible types in capture pattern (pattern captures type "Union[int, str]", variable has type "Callable[[], str]") + pass +[builtins fixtures/dict.pyi] From d7ebe2e6fcd32e4c9c32e007aaa0b130e40a9829 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:25:09 -0800 Subject: [PATCH 1041/1617] Fix crash with `--cache-fine-grained --cache-dir=/dev/null` (#18457) Fixes #18454 Couldn't easily repro in test suite --- mypy/build.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 342331243b96..a7a76a51f958 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -973,8 +973,10 @@ def write_deps_cache( if st.source_hash: hash = st.source_hash else: - assert st.meta, "Module must be either parsed or cached" - hash = st.meta.hash + if st.meta: + hash = st.meta.hash + else: + hash = "" meta_snapshot[id] = hash meta = {"snapshot": meta_snapshot, "deps_meta": fg_deps_meta} From a6c1184f7ef3cddcd070a45803cd3b352f128a29 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 14 Jan 2025 15:21:45 +0300 Subject: [PATCH 1042/1617] Improve security of our GitHub Actions (#18413) Recently CPython introduced this new tool: https://github.com/python/cpython/blob/8eebe4e6d02bb4ad3f1ca6c52624186903dce893/.pre-commit-config.yaml#L64-L67 Which finds different security related problems with GitHub Actions. I added this tool to our `.pre-commit-config.yaml` and followed all its recommendations. Changes: - I added `persist-credentials: false` to all `checkout` actions, see `# Whether to configure the token or SSH key with the local git config` in https://github.com/actions/checkout - I moved all permissions from workflow level to job level - I changed `.github/workflows/mypy_primer_comment.yml` to be a reusable workflow, see https://woodruffw.github.io/zizmor/audits/#dangerous-triggers --- .github/workflows/build_wheels.yml | 4 +++- .github/workflows/docs.yml | 2 ++ .github/workflows/mypy_primer.yml | 5 +---- .github/workflows/mypy_primer_comment.yml | 9 +++++---- .github/workflows/sync_typeshed.yml | 8 +++++--- .github/workflows/test.yml | 4 ++++ .github/workflows/test_stubgenc.yml | 2 ++ .pre-commit-config.yaml | 20 ++++++++++++++++++-- action.yml | 2 +- 9 files changed, 41 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 8055cfd24180..dae4937d5081 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -6,7 +6,7 @@ on: tags: ['*'] permissions: - contents: write + contents: read jobs: build-wheels: @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.11' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 112102954dd3..3f945b84b7f0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,6 +35,8 @@ jobs: VERIFY_MYPY_ERROR_CODES: 1 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.12' diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 54fa2177716c..cf62ce24fb9e 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -26,8 +26,6 @@ jobs: mypy_primer: name: Run mypy_primer runs-on: ubuntu-latest - permissions: - contents: read strategy: matrix: shard-index: [0, 1, 2, 3, 4] @@ -38,6 +36,7 @@ jobs: with: path: mypy_to_test fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "3.12" @@ -93,8 +92,6 @@ jobs: name: Join artifacts runs-on: ubuntu-latest needs: [mypy_primer] - permissions: - contents: read steps: - name: Merge artifacts uses: actions/upload-artifact/merge@v4 diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 72f111b96c53..21f1222a5b89 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -1,20 +1,21 @@ name: Comment with mypy_primer diff -on: +on: # zizmor: ignore[dangerous-triggers] workflow_run: workflows: - Run mypy_primer types: - completed -permissions: - contents: read - pull-requests: write +permissions: {} jobs: comment: name: Comment PR from mypy_primer runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download diffs diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index 84d246441f3d..2d5361a5919c 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -5,20 +5,22 @@ on: schedule: - cron: "0 0 1,15 * *" -permissions: - contents: write - pull-requests: write +permissions: {} jobs: sync_typeshed: name: Sync typeshed if: github.repository == 'python/mypy' runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write timeout-minutes: 10 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: true # needed to `git push` the PR branch # TODO: use whatever solution ends up working for # https://github.com/python/typeshed/issues/8434 - uses: actions/setup-python@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97bc62e002c5..a57d08fa4da8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,6 +136,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Debug build if: ${{ matrix.debug_build }} @@ -217,6 +219,8 @@ jobs: CC: i686-linux-gnu-gcc steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install 32-bit build dependencies run: | sudo dpkg --add-architecture i386 && \ diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 115eb047556e..4676acf8695b 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -29,6 +29,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup 🐍 3.9 uses: actions/setup-python@v5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 587a16b3fb72..dc411c6da49b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,10 +16,11 @@ repos: - id: ruff args: [--exit-non-zero-on-fix] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.30.0 + rev: 0.31.0 hooks: - - id: check-dependabot - id: check-github-workflows + - id: check-github-actions + - id: check-readthedocs - repo: https://github.com/rhysd/actionlint rev: v1.7.6 hooks: @@ -29,5 +30,20 @@ repos: -ignore=property "allow_failure" is not defined, -ignore=SC2(046|086), ] + additional_dependencies: + # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions + # and checks these with shellcheck. This is arguably its most useful feature, + # but the integration only works if shellcheck is installed + - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.10.0" + - repo: https://github.com/woodruffw/zizmor-pre-commit + rev: v1.0.1 + hooks: + - id: zizmor + # Should be the last one: + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes + ci: autoupdate_schedule: quarterly diff --git a/action.yml b/action.yml index df8715327830..732929412651 100644 --- a/action.yml +++ b/action.yml @@ -32,7 +32,7 @@ branding: runs: using: composite steps: - - name: mypy setup + - name: mypy setup # zizmor: ignore[template-injection] shell: bash run: | echo ::group::Installing mypy... From ce61d116bdb848071fe71e189c2f62b2e5d3fe9b Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:15:52 +0000 Subject: [PATCH 1043/1617] [mypyc] Report error for nested class instead of crashing (#18460) fixes https://github.com/mypyc/mypyc/issues/864 --- mypyc/irbuild/classdef.py | 4 ++++ mypyc/test-data/irbuild-classes.test | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 84dd493c6d15..dda8f31fd893 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -97,6 +97,10 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: This is the main entry point to this module. """ + if cdef.info not in builder.mapper.type_to_ir: + builder.error("Nested class definitions not supported", cdef.line) + return + ir = builder.mapper.type_to_ir[cdef.info] # We do this check here because the base field of parent diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index dbc1f8927669..e0f7dfe6514f 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1300,3 +1300,15 @@ class T: class E(T): y: str # E: Type of "y" is incompatible with definition in trait "T" + + +[case testNestedClasses] +def outer(): + class Inner: # E: Nested class definitions not supported + pass + + return Inner + +if True: + class OtherInner: # E: Nested class definitions not supported + pass From 075f79a1dad3459f81a77c678217a2a540410a2e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 14 Jan 2025 17:14:30 +0000 Subject: [PATCH 1044/1617] [mypyc] Updates to dev docs, including debugging segfaults (#18462) Co-authored-by: Valentin Stanciu <250871+svalentin@users.noreply.github.com> --- mypyc/doc/dev-intro.md | 63 +++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index 461a19d37121..036ead34c42c 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -51,11 +51,9 @@ good error message. Here are some major things that aren't yet supported in compiled code: -* Many dunder methods (only some work, such as `__init__` and `__eq__`) +* Some dunder methods (most work though) * Monkey patching compiled functions or classes * General multiple inheritance (a limited form is supported) -* Named tuple defined using the class-based syntax -* Defining protocols We are generally happy to accept contributions that implement new Python features. @@ -73,16 +71,16 @@ compiled code. For example, you may want to do interactive testing or to run benchmarks. This is also handy if you want to inspect the generated C code (see Inspecting Generated C). -Run `mypyc` to compile a module to a C extension using your +Run `python -m mypyc` to compile a module to a C extension using your development version of mypyc: ``` -$ mypyc program.py +$ python -m mypyc program.py ``` This will generate a C extension for `program` in the current working -directory. For example, on a Linux system the generated file may be -called `program.cpython-37m-x86_64-linux-gnu.so`. +directory. For example, on a macOS system the generated file may be +called `program.cpython-313-darwin.so`. Since C extensions can't be run as programs, use `python3 -c` to run the compiled module as a program: @@ -95,7 +93,7 @@ Note that `__name__` in `program.py` will now be `program`, not `__main__`! You can manually delete the C extension to get back to an interpreted -version (this example works on Linux): +version (this example works on macOS or Linux): ``` $ rm program.*.so @@ -114,9 +112,9 @@ extensions) in compiled code. Mypyc will only make compiled code faster. To see a significant speedup, you must make sure that most of the time is spent in compiled -code -- and not in libraries, for example. +code, and not in libraries or I/O. -Mypyc has these passes: +Mypyc has these main passes: * Type check the code using mypy and infer types for variables and expressions. This produces a mypy AST (defined in `mypy.nodes`) and @@ -193,13 +191,13 @@ information. See the test cases in `mypyc/test-data/irbuild-basic.test` for examples of what the IR looks like in a pretty-printed form. -## Testing overview +## Testing Overview Most mypyc test cases are defined in the same format (`.test`) as used for test cases for mypy. Look at mypy developer documentation for a general overview of how things work. Test cases live under `mypyc/test-data/`, and you can run all mypyc tests via `pytest --q mypyc`. If you don't make changes to code under `mypy/`, it's not + mypyc`. If you don't make changes to code under `mypy/`, it's not important to regularly run mypy tests during development. You can use `python runtests.py mypyc-fast` to run a subset of mypyc @@ -228,7 +226,7 @@ We also have tests that verify the generate IR ## Type-checking Mypyc -`./runtests.py self` type checks mypy and mypyc. This is pretty slow, +`./runtests.py self` type checks mypy and mypyc. This is a little slow, however, since it's using an uncompiled mypy. Installing a released version of mypy using `pip` (which is compiled) @@ -311,7 +309,7 @@ number of components at once, insensitive to the particular details of the IR), but there really is no substitute for running code. You can also write tests that test the generated IR, however. -### Tests that compile and run code +### Tests That Compile and Run Code Test cases that compile and run code are located in `mypyc/test-data/run*.test` and the test runner is in @@ -364,7 +362,40 @@ Test cases can also have a `[out]` section, which specifies the expected contents of stdout the test case should produce. New test cases should prefer assert statements to `[out]` sections. -### IR tests +### Debuggging Segfaults + +If you experience a segfault, it's recommended to use a debugger that supports +C, such as gdb or lldb, to look into the segfault. + +If a test case segfaults, you can run tests using the debugger, so +you can inspect the stack: + +``` +$ pytest mypyc -n0 -s --mypyc-debug=gdb -k +``` + +You must use `-n0 -s` to enable interactive input to the debugger. +Instad of `gdb`, you can also try `lldb`. + +To get better C stack tracebacks and more assertions in the Python +runtime, you can build Python in debug mode and use that to run tests +or debug outside the test framework. + +Here are some hints that may help (for Ubuntu): + +``` +$ sudo apt install gdb build-essential libncursesw5-dev libssl-dev libgdbm-dev libc6-dev libsqlite3-dev libbz2-dev libffi-dev libgdbm-compat-dev +$ +$ cd Python-3.XX.Y +$ ./configure --with-pydebug +$ make -s -j16 +$ ./python -m venv ~/ +$ source ~//bin/activate +$ cd +$ pip install -r test-requirements.txt +``` + +### IR Tests If the specifics of the generated IR of a change is important (because, for example, you want to make sure a particular optimization @@ -372,7 +403,7 @@ is triggering), you should add a `mypyc.irbuild` test as well. Test cases are located in `mypyc/test-data/irbuild-*.test` and the test driver is in `mypyc.test.test_irbuild`. IR build tests do a direct comparison of the IR output, so try to make the test as targeted as -possible so as to capture only the important details. (Many of our +possible so as to capture only the important details. (Some of our existing IR build tests do not follow this advice, unfortunately!) If you pass the `--update-data` flag to pytest, it will automatically From 9fffd9e93c58a4bec1bb8b5d49162ad1ae4cff5f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 14 Jan 2025 17:57:45 +0000 Subject: [PATCH 1045/1617] [mypyc] Update README and add wiki links (#18463) Remove stale content and add various links. --- mypyc/README.md | 135 +++-------------------------------------- mypyc/doc/dev-intro.md | 8 +++ 2 files changed, 15 insertions(+), 128 deletions(-) diff --git a/mypyc/README.md b/mypyc/README.md index cb6cf5bf225c..720e64875735 100644 --- a/mypyc/README.md +++ b/mypyc/README.md @@ -1,133 +1,12 @@ mypyc: Mypy to Python C Extension Compiler ========================================== -**NOTE: We are in the process of moving the mypyc README to the** -**[mypyc repository](https://github.com/mypyc/mypyc)** +For the mypyc README, refer to the [mypyc repository](https://github.com/mypyc/mypyc). The mypyc +repository also contains the mypyc issue tracker. All mypyc code lives +here in the mypy repository. -**This may be out of date!** +Source code for the mypyc user documentation lives under +[mypyc/doc](./doc). -Mypyc is a compiler that compiles mypy-annotated, statically typed -Python modules into CPython C extensions. Currently our primary focus -is on making mypy faster through compilation -- the default mypy wheels -are compiled with mypyc. Compiled mypy is about 4x faster than -without compilation. - -Mypyc compiles what is essentially a Python language variant using "strict" -semantics. This means (among some other things): - - * Most type annotations are enforced at runtime (raising ``TypeError`` on mismatch) - - * Classes are compiled into extension classes without ``__dict__`` - (much, but not quite, like if they used ``__slots__``) - - * Monkey patching doesn't work - - * Instance attributes won't fall back to class attributes if undefined - - * Also there are still a bunch of bad bugs and unsupported features :) - -Compiled modules can import arbitrary Python modules, and compiled modules -can be used from other Python modules. Typically mypyc is used to only -compile modules that contain performance bottlenecks. - -You can run compiled modules also as normal, interpreted Python -modules, since mypyc targets valid Python code. This means that -all Python developer tools and debuggers can be used. - -macOS Requirements ------------------- - -* macOS Sierra or later - -* Xcode command line tools - -* Python 3.5+ from python.org (other versions are untested) - -Linux Requirements ------------------- - -* A recent enough C/C++ build environment - -* Python 3.5+ - -Windows Requirements --------------------- - -* Windows has been tested with Windows 10 and MSVC 2017. - -* Python 3.5+ - -Quick Start for Contributors ----------------------------- - -First clone the mypy git repository: - - $ git clone https://github.com/python/mypy.git - $ cd mypy - -Optionally create a virtualenv (recommended): - - $ python3 -m venv - $ source /bin/activate - -Then install the dependencies: - - $ python3 -m pip install -r test-requirements.txt - -Now you can run the tests: - - $ pytest -q mypyc - -Look at the [issue tracker](https://github.com/mypyc/mypyc/issues) -for things to work on. Please express your interest in working on an -issue by adding a comment before doing any significant work, since -there is a risk of duplicate work. - -Note that the issue tracker is hosted on the mypyc GitHub project, not -with mypy itself. - -Documentation -------------- - -We have some [developer documentation](doc/dev-intro.md). - -Development Status and Roadmap ------------------------------- - -These are the current planned major milestones: - -1. [DONE] Support a smallish but useful Python subset. Focus on compiling - single modules, while the rest of the program is interpreted and does not - need to be type checked. - -2. [DONE] Support compiling multiple modules as a single compilation unit (or - dynamic linking of compiled modules). Without this inter-module - calls will use slower Python-level objects, wrapper functions and - Python namespaces. - -3. [DONE] Mypyc can compile mypy. - -4. [DONE] Optimize some important performance bottlenecks. - -5. [PARTIALLY DONE] Generate useful errors for code that uses unsupported Python - features instead of crashing or generating bad code. - -6. [DONE] Release a version of mypy that includes a compiled mypy. - -7. - 1. More feature/compatibility work. (100% compatibility with Python is distinctly - an anti-goal, but more than we have now is a good idea.) - 2. [DONE] Support compiling Black, which is a prominent tool that could benefit - and has maintainer buy-in. - (Let us know if you maintain another Python tool or library and are - interested in working with us on this!) - 3. More optimization! Code size reductions in particular are likely to - be valuable and will speed up mypyc compilation. - -8. We'll see! Adventure is out there! - -Future ------- - -We have some ideas for -[future improvements and optimizations](doc/future.md). +Mypyc welcomes new contributors! Refer to our +[developer documentation](./doc/dev-intro.md) for more information. diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index 036ead34c42c..ee59b82b2c0e 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -4,6 +4,14 @@ This is a short introduction aimed at anybody who is interested in contributing to mypyc, or anybody who is curious to understand how mypyc works internally. +## Developer Documentation in the Wiki + +We have more mypyc developer documentation in our +[wiki](https://github.com/python/mypy/wiki/Developer-Guides). + +For basic information common to both mypy and mypyc development, refer +to the [mypy wiki home page](https://github.com/python/mypy/wiki). + ## Key Differences from Python Code compiled using mypyc is often much faster than CPython since it From b68c545e469aae14ad6e623624140e4be5e23192 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:34:18 +0100 Subject: [PATCH 1046/1617] Bind self to the class being defined when checking multiple inheritance (#18465) Fixes #18458. When checking base class compatibility, the class being defined is not yet in scope. However, it should be equivalent to the class passed to `bind_and_map_method` with free typevars, as that's exactly what we are currently defining. --- mypy/checker.py | 4 ++-- test-data/unit/check-selftype.test | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 79d178f3c644..06e31cddd068 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2232,8 +2232,8 @@ def bind_and_map_method( is_class_method = sym.node.is_class mapped_typ = cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info)) - active_self_type = self.scope.active_self_type() - if isinstance(mapped_typ, Overloaded) and active_self_type: + active_self_type = fill_typevars(sub_info) + if isinstance(mapped_typ, Overloaded): # If we have an overload, filter to overloads that match the self type. # This avoids false positives for concrete subclasses of generic classes, # see testSelfTypeOverrideCompatibility for an example. diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index fa853ac48e5a..814007f0e144 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2214,3 +2214,22 @@ class Test2: reveal_type(Test2().method) # N: Revealed type is "def (foo: builtins.int, *, bar: builtins.str) -> builtins.bytes" [builtins fixtures/tuple.pyi] + +[case testSelfInMultipleInheritance] +from typing_extensions import Self + +class A: + foo: int + def method(self: Self, other: Self) -> None: + self.foo + other.foo + +class B: + bar: str + def method(self: Self, other: Self) -> None: + self.bar + other.bar + +class C(A, B): # OK: both methods take Self + pass +[builtins fixtures/tuple.pyi] From 5e119d0062ca425d927c0f036a63d95bf0cad367 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:38:47 +0100 Subject: [PATCH 1047/1617] Ignore dataclass.__replace__ LSP violations (#18464) Refining dataclass attributes with a narrower type has historically been accepted. Mypy shouldn't emit an LSP warning for the synthesized `__replace__` method added in Python 3.13 either. Users are instead encouraged to enable `--enable-error-code mutable-override` to highlight potential issues. Fixes #18216 --- mypy/checker.py | 15 +++++++++------ test-data/unit/check-classes.test | 14 ++++++++++++++ test-data/unit/check-dataclasses.test | 25 +++++++++++++++++++------ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 06e31cddd068..47b08b683e36 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1980,12 +1980,15 @@ def check_method_override( Return a list of base classes which contain an attribute with the method name. """ # Check against definitions in base classes. - check_override_compatibility = defn.name not in ( - "__init__", - "__new__", - "__init_subclass__", - "__post_init__", - ) and (self.options.check_untyped_defs or not defn.is_dynamic()) + check_override_compatibility = ( + defn.name not in ("__init__", "__new__", "__init_subclass__", "__post_init__") + and (self.options.check_untyped_defs or not defn.is_dynamic()) + and ( + # don't check override for synthesized __replace__ methods from dataclasses + defn.name != "__replace__" + or defn.info.metadata.get("dataclass_tag") is None + ) + ) found_method_base_classes: list[TypeInfo] = [] for base in defn.info.mro[1:]: result = self.check_method_or_accessor_override_for_base( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 618b2c7a40c9..d1c33c4729a9 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -687,6 +687,20 @@ class B(A): def h(cls) -> int: pass [builtins fixtures/classmethod.pyi] +[case testOverrideReplaceMethod] +# flags: --show-error-codes +from typing import Optional +from typing_extensions import Self +class A: + def __replace__(self, x: Optional[str]) -> Self: pass + +class B(A): + def __replace__(self, x: str) -> Self: pass # E: \ + # E: Argument 1 of "__replace__" is incompatible with supertype "A"; supertype defines the argument type as "Optional[str]" [override] \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +[builtins fixtures/tuple.pyi] + [case testAllowCovarianceInReadOnlyAttributes] from typing import Callable, TypeVar diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 6de428109c72..2e7259e4de0a 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2527,16 +2527,29 @@ Gen(2).__replace__(x="not an int") # E: Argument "x" to "__replace__" of "Gen" [builtins fixtures/tuple.pyi] [case testDunderReplaceCovariantOverride] -# flags: --python-version 3.13 +# flags: --python-version 3.13 --enable-error-code mutable-override from dataclasses import dataclass +from typing import Optional +from typing_extensions import dataclass_transform @dataclass class Base: - a: object + a: Optional[int] @dataclass -class Child(Base): # E: Argument 1 of "__replace__" is incompatible with supertype "Base"; supertype defines the argument type as "object" \ - # N: This violates the Liskov substitution principle \ - # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides - a: int +class Child(Base): + a: int # E: Covariant override of a mutable attribute (base class "Base" defined the type as "Optional[int]", expression has type "int") + +@dataclass +class Other(Base): + a: str # E: Incompatible types in assignment (expression has type "str", base class "Base" defined the type as "Optional[int]") + +@dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: Optional[int] + +class Y(X): + a: int # E: Covariant override of a mutable attribute (base class "X" defined the type as "Optional[int]", expression has type "int") [builtins fixtures/tuple.pyi] From fb7b254ba811f3f477e23597561a925e0418f15e Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:53:17 +0100 Subject: [PATCH 1048/1617] Prevent crash with Unpack of a fixed tuple in PEP695 type alias (#18451) Fixes #18309. Add missing `visit_type_alias_stmt()` implementation to mixedtraverser.py to visit the alias target directly. --- mypy/mixedtraverser.py | 17 ++++++++++++++--- test-data/unit/check-python312.test | 16 ++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/mypy/mixedtraverser.py b/mypy/mixedtraverser.py index 9fdc4457d18e..324e8a87c1bd 100644 --- a/mypy/mixedtraverser.py +++ b/mypy/mixedtraverser.py @@ -10,7 +10,9 @@ NamedTupleExpr, NewTypeExpr, PromoteExpr, + TypeAlias, TypeAliasExpr, + TypeAliasStmt, TypeApplication, TypedDictExpr, TypeVarExpr, @@ -48,9 +50,7 @@ def visit_class_def(self, o: ClassDef, /) -> None: def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: super().visit_type_alias_expr(o) - self.in_type_alias_expr = True - o.node.target.accept(self) - self.in_type_alias_expr = False + o.node.accept(self) def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: super().visit_type_var_expr(o) @@ -81,6 +81,17 @@ def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: super().visit_assignment_stmt(o) self.visit_optional_type(o.type) + def visit_type_alias_stmt(self, o: TypeAliasStmt, /) -> None: + super().visit_type_alias_stmt(o) + if o.alias_node is not None: + o.alias_node.accept(self) + + def visit_type_alias(self, o: TypeAlias, /) -> None: + super().visit_type_alias(o) + self.in_type_alias_expr = True + o.target.accept(self) + self.in_type_alias_expr = False + def visit_for_stmt(self, o: ForStmt, /) -> None: super().visit_for_stmt(o) self.visit_optional_type(o.index_type) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 8b4d638ecdaa..80cceea85581 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1972,3 +1972,19 @@ class D: class G[Q]: def g(self, x: Q): ... d: G[str] + +[case testTypeAliasNormalization] +from collections.abc import Callable +from typing import Unpack +from typing_extensions import TypeAlias + +type RK_function_args = tuple[float, int] +type RK_functionBIS = Callable[[Unpack[RK_function_args], int], int] + +def ff(a: float, b: int, c: int) -> int: + return 2 + +bis: RK_functionBIS = ff +res: int = bis(1.0, 2, 3) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] From c9ed867352b0be6b2cca9df3c856f9047409751b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:53:14 +0100 Subject: [PATCH 1049/1617] Update asyncio eval tests for 3.14 (#18468) Starting in Python 3.14, `asyncio.get_event_loop` will raise a RuntimeError if no current loop exists in the current thread. Update the eval tests to use `asyncio.run` instead. --- test-data/unit/pythoneval-asyncio.test | 187 +++++++++++++------------ 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index 4a185557495b..e1f0f861eef3 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -25,11 +25,7 @@ async def greet_every_two_seconds() -> None: print('After', n) n += 1 -loop = asyncio.get_event_loop() -try: - loop.run_until_complete(greet_every_two_seconds()) -finally: - loop.close() +asyncio.run(greet_every_two_seconds()) [out] Prev 0 After 0 @@ -56,9 +52,7 @@ async def print_sum(x: int, y: int) -> None: result = await compute(x, y) # The type of result will be int (is extracted from Future[int] print("%s + %s = %s" % (x, y, result)) -loop = asyncio.get_event_loop() -loop.run_until_complete(print_sum(1, 2)) -loop.close() +asyncio.run(print_sum(1, 2)) [out] Compute 1 + 2 ... 1 + 2 = 3 @@ -72,12 +66,13 @@ async def slow_operation(future: 'Future[str]') -> None: await asyncio.sleep(0.01) future.set_result('Future is done!') -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + await future + print(future.result()) + +asyncio.run(main()) [out] Future is done! @@ -95,10 +90,13 @@ def got_result(future: 'Future[str]') -> None: print(future.result()) loop.stop() -loop = asyncio.get_event_loop() # type: AbstractEventLoop -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Here create a task with the function. (The Task need a Future[T] as first argument) -future.add_done_callback(got_result) # and assign the callback to the future +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Here create a task with the function. (The Task need a Future[T] as first argument) + future.add_done_callback(got_result) # and assign the callback to the future + +loop = asyncio.new_event_loop() # type: AbstractEventLoop +loop.run_until_complete(main()) try: loop.run_forever() finally: @@ -119,13 +117,14 @@ async def factorial(name, number) -> None: f *= i print("Task %s: factorial(%s) = %s" % (name, number, f)) -loop = asyncio.get_event_loop() -tasks = [ - asyncio.Task(factorial("A", 2)), - asyncio.Task(factorial("B", 3)), - asyncio.Task(factorial("C", 4))] -loop.run_until_complete(asyncio.wait(tasks)) -loop.close() +async def main() -> None: + tasks = [ + asyncio.Task(factorial("A", 2)), + asyncio.Task(factorial("B", 3)), + asyncio.Task(factorial("C", 4))] + await asyncio.wait(tasks) + +asyncio.run(main()) [out] Task A: Compute factorial(2)... Task B: Compute factorial(2)... @@ -144,6 +143,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future[int] + async def h4() -> int: x = await future return x @@ -162,12 +163,14 @@ async def h() -> None: x = await h2() print("h: %s" % x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[int] -future.set_result(42) -loop.run_until_complete(h()) -print("Outside %s" % future.result()) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(42) + await h() + print("Outside %s" % future.result()) + +asyncio.run(main()) [out] h3: 42 h2: 42 @@ -182,13 +185,13 @@ from asyncio import Future async def h4() -> "Future[int]": await asyncio.sleep(0.01) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> "Future[Future[int]]": x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -205,9 +208,7 @@ async def h() -> None: print(normalize(y)) print(normalize(x)) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] Before 42 @@ -221,6 +222,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future["A"] + class A: def __init__(self, x: int) -> None: self.x = x @@ -229,12 +232,14 @@ async def h() -> None: x = await future print("h: %s" % x.x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[A] -future.set_result(A(42)) -loop.run_until_complete(h()) -print("Outside %s" % future.result().x) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(A(42)) + await h() + print("Outside %s" % future.result().x) + +asyncio.run(main()) [out] h: 42 Outside 42 @@ -255,11 +260,7 @@ async def test() -> None: await greet() x = await greet() # Error -loop = asyncio.get_event_loop() -try: - loop.run_until_complete(test()) -finally: - loop.close() +asyncio.run(test()) [out] _program.py:11: error: Function does not return a value (it only ever returns None) @@ -277,10 +278,7 @@ async def print_sum(x: int, y: int) -> None: result = await compute(x, y) print("%s + %s = %s" % (x, y, result)) -loop = asyncio.get_event_loop() -loop.run_until_complete(print_sum(1, 2)) -loop.close() - +asyncio.run(print_sum(1, 2)) [out] _program.py:8: error: Incompatible return value type (got "str", expected "int") @@ -293,12 +291,13 @@ async def slow_operation(future: 'Future[str]') -> None: await asyncio.sleep(1) future.set_result(42) # Error -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:7: error: Argument 1 to "set_result" of "Future" has incompatible type "int"; expected "str" @@ -312,12 +311,13 @@ async def slow_operation(future: 'Future[int]') -> None: await asyncio.sleep(1) future.set_result(42) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Error -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Error + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:11: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]" @@ -328,14 +328,15 @@ from asyncio import Future async def slow_operation(future: 'Future[int]') -> None: await asyncio.sleep(1) - future.set_result('42') #Try to set an str as result to a Future[int] - -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Error -loop.run_until_complete(future) -print(future.result()) -loop.close() + future.set_result('42') # Try to set an str as result to a Future[int] + +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Error + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:7: error: Argument 1 to "set_result" of "Future" has incompatible type "str"; expected "int" _program.py:11: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]" @@ -354,11 +355,13 @@ def got_result(future: 'Future[int]') -> None: print(future.result()) loop.stop() -loop = asyncio.get_event_loop() # type: AbstractEventLoop -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -future.add_done_callback(got_result) # Error +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + future.add_done_callback(got_result) # Error +loop = asyncio.new_event_loop() +loop.run_until_complete(main()) try: loop.run_forever() finally: @@ -374,13 +377,13 @@ from asyncio import Future async def h4() -> Future[int]: await asyncio.sleep(1) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> Future[Future[Future[int]]]: x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -393,9 +396,7 @@ async def h() -> None: print(y) print(x) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] _program.py:16: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[Future[Future[int]]]") @@ -407,13 +408,13 @@ from asyncio import Future async def h4() -> Future[int]: await asyncio.sleep(1) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> Future[int]: x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -424,9 +425,7 @@ async def h() -> None: print(y) print(x) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] _program.py:16: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[int]") _program.py:16: note: Maybe you forgot to use "await"? @@ -437,6 +436,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future["A"] + class A: def __init__(self, x: int) -> None: self.x = x @@ -446,16 +447,18 @@ class B: self.x = x async def h() -> None: - x = await future # type: B # Error + x = await future # type: B # Error print("h: %s" % x.x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[A] -future.set_result(A(42)) -loop.run_until_complete(h()) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(A(42)) + await h() + +asyncio.run(main()) [out] -_program.py:15: error: Incompatible types in assignment (expression has type "A", variable has type "B") +_program.py:17: error: Incompatible types in assignment (expression has type "A", variable has type "B") [case testForwardRefToBadAsyncShouldNotCrash_newsemanal] from typing import TypeVar From b20eefddef1ade28a908b834abcf35539ecf96bd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 15 Jan 2025 13:31:58 +0000 Subject: [PATCH 1050/1617] [mypyc] Give more guidance about debugging segfaults in tests (#18475) When a test case segfaults, detect it and print more information. Add more detail to developer docs about debugging segfaults, including some macOS specific information. --- mypyc/doc/dev-intro.md | 27 +++++++++++++++++++++------ mypyc/test/test_run.py | 17 ++++++++++++++++- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index ee59b82b2c0e..633bbaadbe1b 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -376,20 +376,35 @@ If you experience a segfault, it's recommended to use a debugger that supports C, such as gdb or lldb, to look into the segfault. If a test case segfaults, you can run tests using the debugger, so -you can inspect the stack: +you can inspect the stack. Example of inspecting the C stack when a +test case segfaults (user input after `$` and `(gdb)` prompts): ``` $ pytest mypyc -n0 -s --mypyc-debug=gdb -k +... +(gdb) r +... +Program received signal SIGSEGV, Segmentation fault. +... +(gdb) bt +#0 0x00005555556ed1a2 in _PyObject_HashFast (op=0x0) at ./Include/object.h:336 +#1 PyDict_GetItemWithError (op=0x7ffff6c894c0, key=0x0) at Objects/dictobject.c:2394 +... ``` You must use `-n0 -s` to enable interactive input to the debugger. -Instad of `gdb`, you can also try `lldb`. +Instad of `gdb`, you can also try `lldb` (especially on macOS). To get better C stack tracebacks and more assertions in the Python -runtime, you can build Python in debug mode and use that to run tests -or debug outside the test framework. +runtime, you can build Python in debug mode and use that to run tests, +or to manually run the debugger outside the test framework. -Here are some hints that may help (for Ubuntu): +**Note:** You may need to build Python yourself on macOS, as official +Python builds may not have sufficient entitlements to use a debugger. + +Here are some hints about building a debug version of CPython that may +help (for Ubuntu, macOS is mostly similar except for installing build +dependencies): ``` $ sudo apt install gdb build-essential libncursesw5-dev libssl-dev libgdbm-dev libc6-dev libsqlite3-dev libbz2-dev libffi-dev libgdbm-compat-dev @@ -397,7 +412,7 @@ $ $ cd Python-3.XX.Y $ ./configure --with-pydebug $ make -s -j16 -$ ./python -m venv ~/ +$ ./python -m venv ~/ # Use ./python.exe -m venv ... on macOS $ source ~//bin/activate $ cd $ pip install -r test-requirements.txt diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 03d9f0486107..f0c0f9e37cb4 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -328,7 +328,22 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> show_c(cfiles) if proc.returncode != 0: print() - print("*** Exit status: %d" % proc.returncode) + signal = proc.returncode == -11 + extra = "" + if signal: + extra = " (likely segmentation fault)" + print(f"*** Exit status: {proc.returncode}{extra}") + if signal and not sys.platform.startswith("win"): + print() + if sys.platform == "darwin": + debugger = "lldb" + else: + debugger = "gdb" + print( + f'hint: Use "pytest -n0 -s --mypyc-debug={debugger} -k " to run test in debugger' + ) + print("hint: You may need to build a debug version of Python first and use it") + print('hint: See also "Debuggging Segfaults" in mypyc/doc/dev-intro.md') # Verify output. if bench: From a8ab85da8f2984be03acfcb20faeab756f0661b8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 15 Jan 2025 16:57:51 +0000 Subject: [PATCH 1051/1617] [mypyc] Improve access to generated C on test failures and document this (#18476) Now the generated C files for the first mypyc run test failure in a pytest session will be copied to the `.mypyc_test_output` directory, and this will be indicated in the test output. This is a convenience feature to help in the common scenario where all test failures have the same root cause, so any single output is sufficient for debugging. Document this and `--mypyc-showc`, which allows showing generated C for every test failure. The latter is too verbose to be enabled by default. --- .gitignore | 1 + mypy/test/config.py | 3 +++ mypy/test/data.py | 9 ++++++++- mypyc/doc/dev-intro.md | 16 ++++++++++++++++ mypyc/test/test_run.py | 18 +++++++++++++++++- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6c35e3d89342..9c325f3e29f8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ venv/ test-data/packages/.pip_lock dmypy.json .dmypy.json +/.mypyc_test_output # Packages *.egg diff --git a/mypy/test/config.py b/mypy/test/config.py index 3806cf3dfa13..2dc4208b1e9d 100644 --- a/mypy/test/config.py +++ b/mypy/test/config.py @@ -18,6 +18,9 @@ # It is also hard-coded in numerous places, so don't change it. test_temp_dir = "tmp" +# Mypyc tests may write intermediate files (e.g. generated C) here on failure +mypyc_output_dir = os.path.join(PREFIX, ".mypyc_test_output") + # The PEP 561 tests do a bunch of pip installs which, even though they operate # on distinct temporary virtual environments, run into race conditions on shared # file-system state. To make this work reliably in parallel mode, we'll use a diff --git a/mypy/test/data.py b/mypy/test/data.py index dcad0e1cbd58..50e452de4c0a 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -20,7 +20,7 @@ import pytest from mypy import defaults -from mypy.test.config import PREFIX, test_data_prefix, test_temp_dir +from mypy.test.config import PREFIX, mypyc_output_dir, test_data_prefix, test_temp_dir root_dir = os.path.normpath(PREFIX) @@ -586,6 +586,13 @@ def fix_cobertura_filename(line: str) -> str: ## +def pytest_sessionstart(session: Any) -> None: + # Clean up directory where mypyc tests write intermediate files on failure + # to avoid any confusion between test runs + if os.path.isdir(mypyc_output_dir): + shutil.rmtree(mypyc_output_dir) + + # This function name is special to pytest. See # https://docs.pytest.org/en/latest/reference.html#initialization-hooks def pytest_addoption(parser: Any) -> None: diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index 633bbaadbe1b..a8a04a297688 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -296,6 +296,22 @@ Compiled native functions have the prefix `CPyDef_`, while wrapper functions used for calling functions from interpreted Python code have the `CPyPy_` prefix. +When running a test, the first test failure will copy generated C code +into the `.mypyc_test_output` directory. You will see something like +this in the test output: + +``` +... +---------------------------- Captured stderr call ----------------------------- + +Generated files: /Users/me/src/mypy/.mypyc_test_output (for first failure only) + +... +``` + +You can also run pytest with `--mypyc-showc` to display C code on every +test failure. + ## Other Important Limitations All of these limitations will likely be fixed in the future: diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index f0c0f9e37cb4..6dfa7819e585 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -17,7 +17,7 @@ from mypy import build from mypy.errors import CompileError from mypy.options import Options -from mypy.test.config import test_temp_dir +from mypy.test.config import mypyc_output_dir, test_temp_dir from mypy.test.data import DataDrivenTestCase from mypy.test.helpers import assert_module_equivalence, perform_file_operations from mypyc.build import construct_groups @@ -281,6 +281,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> if not run_setup(setup_file, ["build_ext", "--inplace"]): if testcase.config.getoption("--mypyc-showc"): show_c(cfiles) + copy_output_files(mypyc_output_dir) assert False, "Compilation failed" # Assert that an output file got created @@ -344,6 +345,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> ) print("hint: You may need to build a debug version of Python first and use it") print('hint: See also "Debuggging Segfaults" in mypyc/doc/dev-intro.md') + copy_output_files(mypyc_output_dir) # Verify output. if bench: @@ -457,3 +459,17 @@ def fix_native_line_number(message: str, fnam: str, delta: int) -> str: message, ) return message + + +def copy_output_files(target_dir: str) -> None: + try: + os.mkdir(target_dir) + except OSError: + # Only copy data for the first failure, to avoid excessive output in case + # many tests fail + return + + for fnam in glob.glob("build/*.[ch]"): + shutil.copy(fnam, target_dir) + + sys.stderr.write(f"\nGenerated files: {target_dir} (for first failure only)\n\n") From 8859d5163fc6bd16c2161e24fcf2677e3d6479e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:53:21 -0800 Subject: [PATCH 1052/1617] Sync typeshed (#18467) Sync typeshed Source commit: https://github.com/python/typeshed/commit/101287091cbd71a3305a4fc4a1a8eb5df0e3f6f7 --- mypy/typeshed/stdlib/_interpqueues.pyi | 15 +++++++++------ mypy/typeshed/stdlib/_interpreters.pyi | 4 +++- mypy/typeshed/stdlib/_ssl.pyi | 4 +--- mypy/typeshed/stdlib/ctypes/__init__.pyi | 9 ++++++++- mypy/typeshed/stdlib/socket.pyi | 4 ++-- mypy/typeshed/stdlib/sys/__init__.pyi | 8 ++++++-- mypy/typeshed/stdlib/tarfile.pyi | 4 ++-- mypy/typeshed/stdlib/telnetlib.pyi | 4 ++-- mypy/typeshed/stdlib/tkinter/filedialog.pyi | 18 +++++++++--------- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 6 +++--- 10 files changed, 45 insertions(+), 31 deletions(-) diff --git a/mypy/typeshed/stdlib/_interpqueues.pyi b/mypy/typeshed/stdlib/_interpqueues.pyi index db5e4cff5068..c9323b106f3d 100644 --- a/mypy/typeshed/stdlib/_interpqueues.pyi +++ b/mypy/typeshed/stdlib/_interpqueues.pyi @@ -1,16 +1,19 @@ -from typing import Any, SupportsIndex +from typing import Any, Literal, SupportsIndex +from typing_extensions import TypeAlias + +_UnboundOp: TypeAlias = Literal[1, 2, 3] class QueueError(RuntimeError): ... class QueueNotFoundError(QueueError): ... def bind(qid: SupportsIndex) -> None: ... -def create(maxsize: SupportsIndex, fmt: SupportsIndex) -> int: ... +def create(maxsize: SupportsIndex, fmt: SupportsIndex, unboundop: _UnboundOp) -> int: ... def destroy(qid: SupportsIndex) -> None: ... -def get(qid: SupportsIndex) -> tuple[Any, int]: ... +def get(qid: SupportsIndex) -> tuple[Any, int, _UnboundOp | None]: ... def get_count(qid: SupportsIndex) -> int: ... def get_maxsize(qid: SupportsIndex) -> int: ... -def get_queue_defaults(qid: SupportsIndex) -> tuple[int]: ... +def get_queue_defaults(qid: SupportsIndex) -> tuple[int, _UnboundOp]: ... def is_full(qid: SupportsIndex) -> bool: ... -def list_all() -> list[tuple[int, int]]: ... -def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex) -> None: ... +def list_all() -> list[tuple[int, int, _UnboundOp]]: ... +def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex, unboundop: _UnboundOp) -> None: ... def release(qid: SupportsIndex) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi index a57ef13c6d0f..caa1115e9d3d 100644 --- a/mypy/typeshed/stdlib/_interpreters.pyi +++ b/mypy/typeshed/stdlib/_interpreters.pyi @@ -21,7 +21,9 @@ def get_main() -> tuple[int, int]: ... def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ... def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ... def whence(id: SupportsIndex) -> int: ... -def exec(id: SupportsIndex, code: str, shared: bool | None = None, *, restrict: bool = False) -> None: ... +def exec( + id: SupportsIndex, code: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False +) -> None | types.SimpleNamespace: ... def call( id: SupportsIndex, callable: Callable[..., object], diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi index 1a068b997539..e39ab5eb6de8 100644 --- a/mypy/typeshed/stdlib/_ssl.pyi +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -240,9 +240,7 @@ OP_SINGLE_ECDH_USE: int OP_NO_COMPRESSION: int OP_ENABLE_MIDDLEBOX_COMPAT: int OP_NO_RENEGOTIATION: int -if sys.version_info >= (3, 11): - OP_IGNORE_UNEXPECTED_EOF: int -elif sys.version_info >= (3, 8) and sys.platform == "linux": +if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: int if sys.version_info >= (3, 12): OP_LEGACY_SERVER_CONNECT: int diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 5533a22770b8..4f44975d657f 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -159,7 +159,14 @@ def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to if sys.platform == "win32": def DllCanUnloadNow() -> int: ... def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO not documented - def GetLastError() -> int: ... + + # Actually just an instance of _NamedFuncPointer (aka _CDLLFuncPointer), + # but we want to set a more specific __call__ + @type_check_only + class _GetLastErrorFunctionType(_NamedFuncPointer): + def __call__(self) -> int: ... + + GetLastError: _GetLastErrorFunctionType # Actually just an instance of _CFunctionType, but we want to set a more # specific __call__. diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index ab22cced0bb5..f982c9b893d8 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -1399,7 +1399,7 @@ def create_server( address: _Address, *, family: int = ..., backlog: int | None = None, reuse_port: bool = False, dualstack_ipv6: bool = False ) -> socket: ... -# the 5th tuple item is an address +# The 5th tuple item is the socket address, for IP4, IP6, or IP6 if Python is compiled with --disable-ipv6, respectively. def getaddrinfo( host: bytes | str | None, port: bytes | str | int | None, family: int = 0, type: int = 0, proto: int = 0, flags: int = 0 -) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index fb1e24f3e864..d11e64d109b5 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -6,7 +6,7 @@ from collections.abc import AsyncGenerator, Callable, Sequence from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final, type_check_only -from typing_extensions import TypeAlias +from typing_extensions import LiteralString, TypeAlias _T = TypeVar("_T") @@ -45,7 +45,7 @@ if sys.version_info >= (3, 10): path: list[str] path_hooks: list[Callable[[str], PathEntryFinderProtocol]] path_importer_cache: dict[str, PathEntryFinderProtocol | None] -platform: str +platform: LiteralString if sys.version_info >= (3, 9): platlibdir: str prefix: str @@ -393,6 +393,10 @@ if sys.platform == "win32": def getwindowsversion() -> _WinVersion: ... def intern(string: str, /) -> str: ... + +if sys.version_info >= (3, 13): + def _is_gil_enabled() -> bool: ... + def is_finalizing() -> bool: ... def breakpointhook(*args: Any, **kwargs: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index a717c280a423..009aa9070aa8 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -123,7 +123,7 @@ def open( @overload def open( name: StrOrBytesPath | None, - mode: Literal["x", "x:", "a", "a:", "w", "w:"], + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], fileobj: _Fileobj | None = None, bufsize: int = 10240, *, @@ -141,7 +141,7 @@ def open( def open( name: StrOrBytesPath | None = None, *, - mode: Literal["x", "x:", "a", "a:", "w", "w:"], + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], fileobj: _Fileobj | None = None, bufsize: int = 10240, format: int | None = ..., diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi index 294a1cb12b63..6b599256d17b 100644 --- a/mypy/typeshed/stdlib/telnetlib.pyi +++ b/mypy/typeshed/stdlib/telnetlib.pyi @@ -1,5 +1,5 @@ import socket -from collections.abc import Callable, Sequence +from collections.abc import Callable, MutableSequence, Sequence from re import Match, Pattern from types import TracebackType from typing import Any @@ -114,7 +114,7 @@ class Telnet: def mt_interact(self) -> None: ... def listener(self) -> None: ... def expect( - self, list: Sequence[Pattern[bytes] | bytes], timeout: float | None = None + self, list: MutableSequence[Pattern[bytes] | bytes] | Sequence[Pattern[bytes]], timeout: float | None = None ) -> tuple[int, Match[bytes] | None, bytes]: ... def __enter__(self) -> Self: ... def __exit__( diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi index 3d62f079178e..03f89cfbe3e6 100644 --- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi @@ -80,8 +80,8 @@ class Directory(commondialog.Dialog): # TODO: command kwarg available on macos def asksaveasfilename( *, - confirmoverwrite: bool | None = ..., - defaultextension: str | None = ..., + confirmoverwrite: bool | None = True, + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -91,7 +91,7 @@ def asksaveasfilename( ) -> str: ... # can be empty string def askopenfilename( *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -101,7 +101,7 @@ def askopenfilename( ) -> str: ... # can be empty string def askopenfilenames( *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -110,15 +110,15 @@ def askopenfilenames( typevariable: StringVar | str | None = ..., ) -> Literal[""] | tuple[str, ...]: ... def askdirectory( - *, initialdir: StrOrBytesPath | None = ..., mustexist: bool | None = ..., parent: Misc | None = ..., title: str | None = ... + *, initialdir: StrOrBytesPath | None = ..., mustexist: bool | None = False, parent: Misc | None = ..., title: str | None = ... ) -> str: ... # can be empty string # TODO: If someone actually uses these, overload to have the actual return type of open(..., mode) def asksaveasfile( mode: str = "w", *, - confirmoverwrite: bool | None = ..., - defaultextension: str | None = ..., + confirmoverwrite: bool | None = True, + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -129,7 +129,7 @@ def asksaveasfile( def askopenfile( mode: str = "r", *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -140,7 +140,7 @@ def askopenfile( def askopenfiles( mode: str = "r", *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index 4eda3897a00c..d7da59a7ed4b 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -291,8 +291,8 @@ class ReadOnlySequentialNamedNodeMap: def length(self) -> int: ... class Identified: - publicId: Incomplete - systemId: Incomplete + publicId: str | None + systemId: str | None class DocumentType(Identified, Childless, Node): nodeType: int @@ -331,7 +331,7 @@ class Notation(Identified, Childless, Node): class DOMImplementation(DOMImplementationLS): def hasFeature(self, feature: str, version: str | None) -> bool: ... def createDocument(self, namespaceURI: str | None, qualifiedName: str | None, doctype: DocumentType | None) -> Document: ... - def createDocumentType(self, qualifiedName: str | None, publicId: str, systemId: str) -> DocumentType: ... + def createDocumentType(self, qualifiedName: str | None, publicId: str | None, systemId: str | None) -> DocumentType: ... def getInterface(self, feature: str) -> Self | None: ... class ElementInfo: From 21156fdcd9ee427851e2c99680435adb0ce4f8d2 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:00:23 +0000 Subject: [PATCH 1053/1617] [misc] use highlight.js for syntax highlighting in blog post (#18480) Toe get highlighting to work, we just need to import the CSS and run the highlight.js that does the highlighting in JS client side. We can add the lines at the top of the blog post to do this. I've made it only support bash and python for now to help with detection. But if we have a reason to, we can remove that and let it try them all. In a previous PR I've added the necessary `` tags. Since we're highlighting nicely now, I also removed the extra indendation. I've also noticed that we're pretty good at specifying the language in code blocks in the changelog. So we can take that language and use it in the code block as a class to tell highlight.js exactly what language that code block is in. If this is useful, we can remove the limitation of only python and bash support from the top configuration in the future. This is useful for smaller blocks of a few lines where maybe it doesn't detect the language properly. Used on mypy 1.14 blog post - https://mypy-lang.blogspot.com/2024/12/mypy-114-released.html --- misc/gen_blog_post_html.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py index 00e167e4a3a2..847e05399654 100644 --- a/misc/gen_blog_post_html.py +++ b/misc/gen_blog_post_html.py @@ -44,16 +44,23 @@ def format_code(h: str) -> str: while i < len(a): if a[i].startswith(" ") or a[i].startswith("```"): indent = a[i].startswith(" ") + language: str = "" if not indent: + language = a[i][3:] i += 1 - r.append("
")
+            if language:
+                r.append(f'
')
+            else:
+                r.append("
")
             while i < len(a) and (
                 (indent and a[i].startswith("    ")) or (not indent and not a[i].startswith("```"))
             ):
                 # Undo > and <
                 line = a[i].replace(">", ">").replace("<", "<")
-                if not indent:
-                    line = "    " + line
+                if indent:
+                    # Undo this extra level of indentation so it looks nice with
+                    # syntax highlighting CSS.
+                    line = line[4:]
                 r.append(html.escape(line))
                 i += 1
             r.append("
") @@ -64,7 +71,7 @@ def format_code(h: str) -> str: i += 1 formatted = "\n".join(r) # remove empty first line for code blocks - return re.sub(r"\n", r"", formatted) + return re.sub(r"]*)>\n", r"", formatted) def convert(src: str) -> str: @@ -131,8 +138,18 @@ def convert(src: str) -> str: h, ) - # Add missing top-level HTML tags - h = '\n\n\n' + h + "\n" + # Add top-level HTML tags and headers for syntax highlighting css/js. + # We're configuring hljs to highlight python and bash code. We can remove + # this configure call to make it try all the languages it supports. + h = f""" + + + + + +{h} + +""" return h From 55a884069dc4521770666c2169230f75a81fce54 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 16 Jan 2025 23:15:56 +0100 Subject: [PATCH 1054/1617] Add a function to search for pyproject.toml in a project root (#16965) Here's a solution to fix https://github.com/python/mypy/issues/10613. The tests are covered. It adds the functionality of searching `pyproject.toml` recursively from the current directory up to a project root (directory with either `.git` or `.hg`) to `mypy` --- mypy/defaults.py | 34 +++++++++++++++++++++++++- test-data/unit/cmdline.pyproject.test | 35 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/mypy/defaults.py b/mypy/defaults.py index 6f309668d224..ed0b8d0dc6d9 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -12,9 +12,41 @@ # mypy, at least version PYTHON3_VERSION is needed. PYTHON3_VERSION_MIN: Final = (3, 8) # Keep in sync with typeshed's python support + +def find_pyproject() -> str: + """Search for file pyproject.toml in the parent directories recursively. + + It resolves symlinks, so if there is any symlink up in the tree, it does not respect them + + If the file is not found until the root of FS or repository, PYPROJECT_FILE is used + """ + + def is_root(current_dir: str) -> bool: + parent = os.path.join(current_dir, os.path.pardir) + return os.path.samefile(current_dir, parent) or any( + os.path.isdir(os.path.join(current_dir, cvs_root)) for cvs_root in (".git", ".hg") + ) + + # Preserve the original behavior, returning PYPROJECT_FILE if exists + if os.path.isfile(PYPROJECT_FILE) or is_root(os.path.curdir): + return PYPROJECT_FILE + + # And iterate over the tree + current_dir = os.path.pardir + while not is_root(current_dir): + config_file = os.path.join(current_dir, PYPROJECT_FILE) + if os.path.isfile(config_file): + return config_file + parent = os.path.join(current_dir, os.path.pardir) + current_dir = parent + + return PYPROJECT_FILE + + CACHE_DIR: Final = ".mypy_cache" CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"] -PYPROJECT_CONFIG_FILES: Final = ["pyproject.toml"] +PYPROJECT_FILE: Final = "pyproject.toml" +PYPROJECT_CONFIG_FILES: Final = [find_pyproject()] SHARED_CONFIG_FILES: Final = ["setup.cfg"] USER_CONFIG_FILES: Final = ["~/.config/mypy/config", "~/.mypy.ini"] if os.environ.get("XDG_CONFIG_HOME"): diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index 57e6facad032..e6e5f113a844 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -133,3 +133,38 @@ Neither is this! description = "Factory ⸻ A code generator 🏭" \[tool.mypy] [file x.py] + +[case testSearchRecursively] +# cmd: mypy x.py +[file ../pyproject.toml] +\[tool.mypy] +\[tool.mypy.overrides] +module = "x" +disallow_untyped_defs = false +[file x.py] +pass +[out] +../pyproject.toml: tool.mypy.overrides sections must be an array. Please make sure you are using double brackets like so: [[tool.mypy.overrides]] +== Return code: 0 + +[case testSearchRecursivelyStopsGit] +# cmd: mypy x.py +[file .git/test] +[file ../pyproject.toml] +\[tool.mypy] +\[tool.mypy.overrides] +module = "x" +disallow_untyped_defs = false +[file x.py] +i: int = 0 + +[case testSearchRecursivelyStopsHg] +# cmd: mypy x.py +[file .hg/test] +[file ../pyproject.toml] +\[tool.mypy] +\[tool.mypy.overrides] +module = "x" +disallow_untyped_defs = false +[file x.py] +i: int = 0 From c61bce4e728f87ea8d10f5e2cd0d10135807b72f Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Fri, 17 Jan 2025 02:33:51 -0800 Subject: [PATCH 1055/1617] [mypyc] Fixing __init__ for classes with @attr.s(slots=True). (#18447) Fixes mypyc/mypyc#1079. `@attr.s` generates a `__init__` function which was getting lost in `CPyDataclass_SleightOfHand`. This change copies the generated `__init__` function and a couple of others ones to maintain consistency with CPython. --- mypyc/irbuild/classdef.py | 3 ++- mypyc/irbuild/util.py | 2 ++ mypyc/lib-rt/CPy.h | 3 ++- mypyc/lib-rt/misc_ops.c | 28 +++++++++++++++++++++++++--- mypyc/primitives/misc_ops.py | 8 +++++++- mypyc/test-data/run-classes.test | 14 ++++++++++++++ 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index dda8f31fd893..03368d74c407 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -381,9 +381,10 @@ def finalize(self, ir: ClassIR) -> None: dec = self.builder.accept( next(d for d in self.cdef.decorators if is_dataclass_decorator(d)) ) + dataclass_type_val = self.builder.load_str(dataclass_type(self.cdef) or "unknown") self.builder.call_c( dataclass_sleight_of_hand, - [dec, self.type_obj, self.non_ext.dict, self.non_ext.anns], + [dec, self.type_obj, self.non_ext.dict, self.non_ext.anns, dataclass_type_val], self.cdef.line, ) diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index e27e509ad7fa..43ee547f8b4f 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -73,6 +73,8 @@ def is_dataclass(cdef: ClassDef) -> bool: return any(is_dataclass_decorator(d) for d in cdef.decorators) +# The string values returned by this function are inspected in +# mypyc/lib-rt/misc_ops.c:CPyDataclass_SleightOfHand(...). def dataclass_type(cdef: ClassDef) -> str | None: for d in cdef.decorators: typ = dataclass_decorator_type(d) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 1e6f50306ba1..f72eaea55daf 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -860,7 +860,8 @@ PyObject *CPyType_FromTemplateWrapper(PyObject *template_, PyObject *orig_bases, PyObject *modname); int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, - PyObject *dict, PyObject *annotations); + PyObject *dict, PyObject *annotations, + PyObject *dataclass_type); PyObject *CPyPickle_SetState(PyObject *obj, PyObject *state); PyObject *CPyPickle_GetState(PyObject *obj); CPyTagged CPyTagged_Id(PyObject *o); diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index a7f67fd67d50..e71ef0dc6b48 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -347,13 +347,15 @@ static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict) * tp: The class we are making a dataclass * dict: The dictionary containing values that dataclasses needs * annotations: The type annotation dictionary + * dataclass_type: A str object with the return value of util.py:dataclass_type() */ int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, - PyObject *dict, PyObject *annotations) { + PyObject *dict, PyObject *annotations, + PyObject *dataclass_type) { PyTypeObject *ttp = (PyTypeObject *)tp; Py_ssize_t pos; - PyObject *res; + PyObject *res = NULL; /* Make a copy of the original class __dict__ */ PyObject *orig_dict = PyDict_Copy(ttp->tp_dict); @@ -381,17 +383,37 @@ CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, if (!res) { goto fail; } - Py_DECREF(res); + const char *dataclass_type_ptr = PyUnicode_AsUTF8(dataclass_type); + if (dataclass_type_ptr == NULL) { + goto fail; + } + if (strcmp(dataclass_type_ptr, "attr") == 0 || + strcmp(dataclass_type_ptr, "attr-auto") == 0) { + // These attributes are added or modified by @attr.s(slots=True). + const char * const keys[] = {"__attrs_attrs__", "__attrs_own_setattr__", "__init__", ""}; + for (const char * const *key_iter = keys; **key_iter != '\0'; key_iter++) { + PyObject *value = NULL; + int rv = PyObject_GetOptionalAttrString(res, *key_iter, &value); + if (rv == 1) { + PyObject_SetAttrString(tp, *key_iter, value); + Py_DECREF(value); + } else if (rv == -1) { + goto fail; + } + } + } /* Copy back the original contents of the dict */ if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) { goto fail; } + Py_DECREF(res); Py_DECREF(orig_dict); return 1; fail: + Py_XDECREF(res); Py_XDECREF(orig_dict); return 0; } diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index e9016e24c46d..2d8a2d362293 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -224,7 +224,13 @@ # Create a dataclass from an extension class. See # CPyDataclass_SleightOfHand for more docs. dataclass_sleight_of_hand = custom_op( - arg_types=[object_rprimitive, object_rprimitive, dict_rprimitive, dict_rprimitive], + arg_types=[ + object_rprimitive, + object_rprimitive, + dict_rprimitive, + dict_rprimitive, + str_rprimitive, + ], return_type=bit_rprimitive, c_function_name="CPyDataclass_SleightOfHand", error_kind=ERR_FALSE, diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 0eab15d89746..168477d5a8ee 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2705,3 +2705,17 @@ print(native.ColorCode.OKGREEN.value) [out] okgreen + +[case testAttrWithSlots] +import attr + +@attr.s(slots=True) +class A: + ints: list[int] = attr.ib() + +[file driver.py] +import native +print(native.A(ints=[1, -17]).ints) + +[out] +\[1, -17] From f80920471bce55e90ac6578424d70976c2445e6b Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:41:49 -0800 Subject: [PATCH 1056/1617] Use a dict to keep track of TypedDict fields in semanal (#18369) Useful for #7435 --- mypy/semanal_typeddict.py | 113 +++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 7b6e48eacb39..0d6a0b7ff87f 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Collection from typing import Final from mypy import errorcodes as codes, message_registry @@ -97,21 +98,23 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N existing_info = None if isinstance(defn.analyzed, TypedDictExpr): existing_info = defn.analyzed.info + + field_types: dict[str, Type] | None if ( len(defn.base_type_exprs) == 1 and isinstance(defn.base_type_exprs[0], RefExpr) and defn.base_type_exprs[0].fullname in TPDICT_NAMES ): # Building a new TypedDict - fields, types, statements, required_keys, readonly_keys = ( + field_types, statements, required_keys, readonly_keys = ( self.analyze_typeddict_classdef_fields(defn) ) - if fields is None: + if field_types is None: return True, None # Defer if self.api.is_func_scope() and "@" not in defn.name: defn.name += "@" + str(defn.line) info = self.build_typeddict_typeinfo( - defn.name, fields, types, required_keys, readonly_keys, defn.line, existing_info + defn.name, field_types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -154,26 +157,24 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N else: self.fail("All bases of a new TypedDict must be TypedDict types", defn) - keys: list[str] = [] - types = [] + field_types = {} required_keys = set() readonly_keys = set() # Iterate over bases in reverse order so that leftmost base class' keys take precedence for base in reversed(typeddict_bases): self.add_keys_and_types_from_base( - base, keys, types, required_keys, readonly_keys, defn + base, field_types, required_keys, readonly_keys, defn ) - (new_keys, new_types, new_statements, new_required_keys, new_readonly_keys) = ( - self.analyze_typeddict_classdef_fields(defn, keys) + (new_field_types, new_statements, new_required_keys, new_readonly_keys) = ( + self.analyze_typeddict_classdef_fields(defn, oldfields=field_types) ) - if new_keys is None: + if new_field_types is None: return True, None # Defer - keys.extend(new_keys) - types.extend(new_types) + field_types.update(new_field_types) required_keys.update(new_required_keys) readonly_keys.update(new_readonly_keys) info = self.build_typeddict_typeinfo( - defn.name, keys, types, required_keys, readonly_keys, defn.line, existing_info + defn.name, field_types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -184,8 +185,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N def add_keys_and_types_from_base( self, base: Expression, - keys: list[str], - types: list[Type], + field_types: dict[str, Type], required_keys: set[str], readonly_keys: set[str], ctx: Context, @@ -224,10 +224,10 @@ def add_keys_and_types_from_base( with state.strict_optional_set(self.options.strict_optional): valid_items = self.map_items_to_base(valid_items, tvars, base_args) for key in base_items: - if key in keys: + if key in field_types: self.fail(TYPEDDICT_OVERRIDE_MERGE.format(key), ctx) - keys.extend(valid_items.keys()) - types.extend(valid_items.values()) + + field_types.update(valid_items) required_keys.update(base_typed_dict.required_keys) readonly_keys.update(base_typed_dict.readonly_keys) @@ -280,23 +280,34 @@ def map_items_to_base( return mapped_items def analyze_typeddict_classdef_fields( - self, defn: ClassDef, oldfields: list[str] | None = None - ) -> tuple[list[str] | None, list[Type], list[Statement], set[str], set[str]]: + self, defn: ClassDef, oldfields: Collection[str] | None = None + ) -> tuple[dict[str, Type] | None, list[Statement], set[str], set[str]]: """Analyze fields defined in a TypedDict class definition. This doesn't consider inherited fields (if any). Also consider totality, if given. Return tuple with these items: - * List of keys (or None if found an incomplete reference --> deferral) - * List of types for each key + * Dict of key -> type (or None if found an incomplete reference -> deferral) * List of statements from defn.defs.body that are legally allowed to be a part of a TypedDict definition * Set of required keys """ - fields: list[str] = [] - types: list[Type] = [] + fields: dict[str, Type] = {} + readonly_keys = set[str]() + required_keys = set[str]() statements: list[Statement] = [] + + total: bool | None = True + for key in defn.keywords: + if key == "total": + total = require_bool_literal_argument( + self.api, defn.keywords["total"], "total", True + ) + continue + for_function = ' for "__init_subclass__" of "TypedDict"' + self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) + for stmt in defn.defs.body: if not isinstance(stmt, AssignmentStmt): # Still allow pass or ... (for empty TypedDict's) and docstrings @@ -320,10 +331,11 @@ def analyze_typeddict_classdef_fields( self.fail(f'Duplicate TypedDict key "{name}"', stmt) continue # Append stmt, name, and type in this case... - fields.append(name) statements.append(stmt) + + field_type: Type if stmt.unanalyzed_type is None: - types.append(AnyType(TypeOfAny.unannotated)) + field_type = AnyType(TypeOfAny.unannotated) else: analyzed = self.api.anal_type( stmt.unanalyzed_type, @@ -333,38 +345,27 @@ def analyze_typeddict_classdef_fields( prohibit_special_class_field_types="TypedDict", ) if analyzed is None: - return None, [], [], set(), set() # Need to defer - types.append(analyzed) + return None, [], set(), set() # Need to defer + field_type = analyzed if not has_placeholder(analyzed): stmt.type = self.extract_meta_info(analyzed, stmt)[0] + + field_type, required, readonly = self.extract_meta_info(field_type) + fields[name] = field_type + + if (total or required is True) and required is not False: + required_keys.add(name) + if readonly: + readonly_keys.add(name) + # ...despite possible minor failures that allow further analysis. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax: self.fail(TPDICT_CLASS_ERROR, stmt) elif not isinstance(stmt.rvalue, TempNode): # x: int assigns rvalue to TempNode(AnyType()) self.fail("Right hand side values are not supported in TypedDict", stmt) - total: bool | None = True - if "total" in defn.keywords: - total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True) - if defn.keywords and defn.keywords.keys() != {"total"}: - for_function = ' for "__init_subclass__" of "TypedDict"' - for key in defn.keywords: - if key == "total": - continue - self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) - res_types = [] - readonly_keys = set() - required_keys = set() - for field, t in zip(fields, types): - typ, required, readonly = self.extract_meta_info(t) - res_types.append(typ) - if (total or required is True) and required is not False: - required_keys.add(field) - if readonly: - readonly_keys.add(field) - - return fields, res_types, statements, required_keys, readonly_keys + return fields, statements, required_keys, readonly_keys def extract_meta_info( self, typ: Type, context: Context | None = None @@ -433,7 +434,7 @@ def check_typeddict( name += "@" + str(call.line) else: name = var_name = "TypedDict@" + str(call.line) - info = self.build_typeddict_typeinfo(name, [], [], set(), set(), call.line, None) + info = self.build_typeddict_typeinfo(name, {}, set(), set(), call.line, None) else: if var_name is not None and name != var_name: self.fail( @@ -473,7 +474,12 @@ def check_typeddict( if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info info = self.build_typeddict_typeinfo( - name, items, types, required_keys, readonly_keys, call.line, existing_info + name, + dict(zip(items, types)), + required_keys, + readonly_keys, + call.line, + existing_info, ) info.line = node.line # Store generated TypeInfo under both names, see semanal_namedtuple for more details. @@ -578,8 +584,7 @@ def fail_typeddict_arg( def build_typeddict_typeinfo( self, name: str, - items: list[str], - types: list[Type], + item_types: dict[str, Type], required_keys: set[str], readonly_keys: set[str], line: int, @@ -593,9 +598,7 @@ def build_typeddict_typeinfo( ) assert fallback is not None info = existing_info or self.api.basic_new_typeinfo(name, fallback, line) - typeddict_type = TypedDictType( - dict(zip(items, types)), required_keys, readonly_keys, fallback - ) + typeddict_type = TypedDictType(item_types, required_keys, readonly_keys, fallback) if info.special_alias and has_placeholder(info.special_alias.target): self.api.process_placeholder( None, "TypedDict item", info, force_progress=typeddict_type != info.typeddict_type From c4e2eb79905d7b381db8caed161cebb04622ebc2 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:15:34 -0800 Subject: [PATCH 1057/1617] Document any TYPE_CHECKING name works (#18443) --- docs/source/common_issues.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 7165955e67d3..96d73e5f0399 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -455,7 +455,7 @@ Example: # The rest of this file doesn't apply to Windows. Some other expressions exhibit similar behavior; in particular, -:py:data:`~typing.TYPE_CHECKING`, variables named ``MYPY``, and any variable +:py:data:`~typing.TYPE_CHECKING`, variables named ``MYPY`` or ``TYPE_CHECKING``, and any variable whose name is passed to :option:`--always-true ` or :option:`--always-false `. (However, ``True`` and ``False`` are not treated specially!) From 68cffa7afe03d2b663aced9a70254e58704857db Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 19 Jan 2025 22:55:13 +0100 Subject: [PATCH 1058/1617] [stubgen] Improve dataclass init signatures (#18430) Remove generated incomplete `__init__` signatures for dataclasses. Keep the field specifiers instead. --- mypy/plugins/dataclasses.py | 4 +- mypy/stubgen.py | 33 ++++++++++++---- test-data/unit/stubgen.test | 78 +++++++++++++++++++++++++------------ 3 files changed, 81 insertions(+), 34 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 538f689f5e07..6e0e22272356 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -79,6 +79,8 @@ # The set of decorators that generate dataclasses. dataclass_makers: Final = {"dataclass", "dataclasses.dataclass"} +# Default field specifiers for dataclasses +DATACLASS_FIELD_SPECIFIERS: Final = ("dataclasses.Field", "dataclasses.field") SELF_TVAR_NAME: Final = "_DT" @@ -87,7 +89,7 @@ order_default=False, kw_only_default=False, frozen_default=False, - field_specifiers=("dataclasses.Field", "dataclasses.field"), + field_specifiers=DATACLASS_FIELD_SPECIFIERS, ) _INTERNAL_REPLACE_SYM_NAME: Final = "__mypy-replace" _INTERNAL_POST_INIT_SYM_NAME: Final = "__mypy-post_init" diff --git a/mypy/stubgen.py b/mypy/stubgen.py index c74e9f700861..1f8a1a4740f1 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -95,6 +95,7 @@ ImportFrom, IndexExpr, IntExpr, + LambdaExpr, ListExpr, MemberExpr, MypyFile, @@ -113,6 +114,7 @@ Var, ) from mypy.options import Options as MypyOptions +from mypy.plugins.dataclasses import DATACLASS_FIELD_SPECIFIERS from mypy.semanal_shared import find_dataclass_transform_spec from mypy.sharedparse import MAGIC_METHODS_POS_ARGS_ONLY from mypy.stubdoc import ArgSig, FunctionSig @@ -342,11 +344,12 @@ def visit_index_expr(self, node: IndexExpr) -> str: base = node.base.accept(self) index = node.index.accept(self) if len(index) > 2 and index.startswith("(") and index.endswith(")"): - index = index[1:-1] + index = index[1:-1].rstrip(",") return f"{base}[{index}]" def visit_tuple_expr(self, node: TupleExpr) -> str: - return f"({', '.join(n.accept(self) for n in node.items)})" + suffix = "," if len(node.items) == 1 else "" + return f"({', '.join(n.accept(self) for n in node.items)}{suffix})" def visit_list_expr(self, node: ListExpr) -> str: return f"[{', '.join(n.accept(self) for n in node.items)}]" @@ -368,6 +371,10 @@ def visit_op_expr(self, o: OpExpr) -> str: def visit_star_expr(self, o: StarExpr) -> str: return f"*{o.expr.accept(self)}" + def visit_lambda_expr(self, o: LambdaExpr) -> str: + # TODO: Required for among other things dataclass.field default_factory + return self.stubgen.add_name("_typeshed.Incomplete") + def find_defined_names(file: MypyFile) -> set[str]: finder = DefinitionFinder() @@ -482,6 +489,7 @@ def __init__( self.method_names: set[str] = set() self.processing_enum = False self.processing_dataclass = False + self.dataclass_field_specifier: tuple[str, ...] = () @property def _current_class(self) -> ClassDef | None: @@ -636,8 +644,8 @@ def visit_func_def(self, o: FuncDef) -> None: is_dataclass_generated = ( self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated ) - if is_dataclass_generated and o.name != "__init__": - # Skip methods generated by the @dataclass decorator (except for __init__) + if is_dataclass_generated: + # Skip methods generated by the @dataclass decorator return if ( self.is_private_name(o.name, o.fullname) @@ -793,8 +801,9 @@ def visit_class_def(self, o: ClassDef) -> None: self.add(f"{self._indent}{docstring}\n") n = len(self._output) self._vars.append([]) - if self.analyzed and find_dataclass_transform_spec(o): + if self.analyzed and (spec := find_dataclass_transform_spec(o)): self.processing_dataclass = True + self.dataclass_field_specifier = spec.field_specifiers super().visit_class_def(o) self.dedent() self._vars.pop() @@ -809,6 +818,7 @@ def visit_class_def(self, o: ClassDef) -> None: self._state = CLASS self.method_names = set() self.processing_dataclass = False + self.dataclass_field_specifier = () self._class_stack.pop(-1) self.processing_enum = False @@ -879,8 +889,9 @@ def is_dataclass_transform(self, expr: Expression) -> bool: expr = expr.callee if self.get_fullname(expr) in DATACLASS_TRANSFORM_NAMES: return True - if find_dataclass_transform_spec(expr) is not None: + if (spec := find_dataclass_transform_spec(expr)) is not None: self.processing_dataclass = True + self.dataclass_field_specifier = spec.field_specifiers return True return False @@ -1259,8 +1270,14 @@ def get_assign_initializer(self, rvalue: Expression) -> str: and not isinstance(rvalue, TempNode) ): return " = ..." - if self.processing_dataclass and not (isinstance(rvalue, TempNode) and rvalue.no_rhs): - return " = ..." + if self.processing_dataclass: + if isinstance(rvalue, CallExpr): + fullname = self.get_fullname(rvalue.callee) + if fullname in (self.dataclass_field_specifier or DATACLASS_FIELD_SPECIFIERS): + p = AliasPrinter(self) + return f" = {rvalue.accept(p)}" + if not (isinstance(rvalue, TempNode) and rvalue.no_rhs): + return " = ..." # TODO: support other possible cases, where initializer is important # By default, no initializer is required: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index fa462dc23a9a..7700f04c6797 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -3101,15 +3101,14 @@ import attrs @attrs.define class C: - x = attrs.field() + x: int = attrs.field() [out] import attrs @attrs.define class C: - x = ... - def __init__(self, x) -> None: ... + x: int = attrs.field() [case testNamedTupleInClass] from collections import namedtuple @@ -4050,8 +4049,9 @@ def i(x=..., y=..., z=...) -> None: ... [case testDataclass] import dataclasses import dataclasses as dcs -from dataclasses import dataclass, InitVar, KW_ONLY +from dataclasses import dataclass, field, Field, InitVar, KW_ONLY from dataclasses import dataclass as dc +from datetime import datetime from typing import ClassVar @dataclasses.dataclass @@ -4066,6 +4066,10 @@ class X: h: int = 1 i: InitVar[str] j: InitVar = 100 + # Lambda not supported yet -> marked as Incomplete instead + k: str = Field( + default_factory=lambda: datetime.utcnow().isoformat(" ", timespec="seconds") + ) non_field = None @dcs.dataclass @@ -4083,7 +4087,8 @@ class V: ... [out] import dataclasses import dataclasses as dcs -from dataclasses import InitVar, KW_ONLY, dataclass, dataclass as dc +from _typeshed import Incomplete +from dataclasses import Field, InitVar, KW_ONLY, dataclass, dataclass as dc, field from typing import ClassVar @dataclasses.dataclass @@ -4092,12 +4097,13 @@ class X: b: str = ... c: ClassVar d: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) _: KW_ONLY h: int = ... i: InitVar[str] j: InitVar = ... + k: str = Field(default_factory=Incomplete) non_field = ... @dcs.dataclass @@ -4110,8 +4116,9 @@ class W: ... class V: ... [case testDataclass_semanal] -from dataclasses import InitVar, dataclass, field +from dataclasses import Field, InitVar, dataclass, field from typing import ClassVar +from datetime import datetime @dataclass class X: @@ -4125,13 +4132,18 @@ class X: h: int = 1 i: InitVar = 100 j: list[int] = field(default_factory=list) + # Lambda not supported yet -> marked as Incomplete instead + k: str = Field( + default_factory=lambda: datetime.utcnow().isoformat(" ", timespec="seconds") + ) non_field = None @dataclass(init=False, repr=False, frozen=True) class Y: ... [out] -from dataclasses import InitVar, dataclass +from _typeshed import Incomplete +from dataclasses import Field, InitVar, dataclass, field from typing import ClassVar @dataclass @@ -4141,13 +4153,13 @@ class X: c: str = ... d: ClassVar e: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) h: int = ... i: InitVar = ... - j: list[int] = ... + j: list[int] = field(default_factory=list) + k: str = Field(default_factory=Incomplete) non_field = ... - def __init__(self, a, b, c=..., *, g=..., h=..., i=..., j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... @@ -4175,7 +4187,7 @@ class X: class Y: ... [out] -from dataclasses import InitVar, KW_ONLY, dataclass +from dataclasses import InitVar, KW_ONLY, dataclass, field from typing import ClassVar @dataclass @@ -4184,14 +4196,13 @@ class X: b: str = ... c: ClassVar d: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) _: KW_ONLY h: int = ... i: InitVar[str] j: InitVar = ... non_field = ... - def __init__(self, a, b=..., *, g=..., h=..., i, j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... @@ -4236,7 +4247,6 @@ from dataclasses import dataclass @dataclass class X(missing.Base): a: int - def __init__(self, *generated_args, a, **generated_kwargs) -> None: ... @dataclass class Y(missing.Base): @@ -4244,7 +4254,6 @@ class Y(missing.Base): generated_args_: str generated_kwargs: float generated_kwargs_: float - def __init__(self, *generated_args__, generated_args, generated_args_, generated_kwargs, generated_kwargs_, **generated_kwargs__) -> None: ... [case testDataclassTransform] # dataclass_transform detection only works with sementic analysis. @@ -4298,6 +4307,7 @@ class Z(metaclass=DCMeta): [case testDataclassTransformDecorator_semanal] import typing_extensions +from dataclasses import field @typing_extensions.dataclass_transform(kw_only_default=True) def create_model(cls): @@ -4307,9 +4317,11 @@ def create_model(cls): class X: a: int b: str = "hello" + c: bool = field(default=True) [out] import typing_extensions +from dataclasses import field @typing_extensions.dataclass_transform(kw_only_default=True) def create_model(cls): ... @@ -4318,9 +4330,10 @@ def create_model(cls): ... class X: a: int b: str = ... - def __init__(self, *, a, b=...) -> None: ... + c: bool = field(default=True) [case testDataclassTransformClass_semanal] +from dataclasses import field from typing_extensions import dataclass_transform @dataclass_transform(kw_only_default=True) @@ -4329,8 +4342,10 @@ class ModelBase: ... class X(ModelBase): a: int b: str = "hello" + c: bool = field(default=True) [out] +from dataclasses import field from typing_extensions import dataclass_transform @dataclass_transform(kw_only_default=True) @@ -4339,28 +4354,42 @@ class ModelBase: ... class X(ModelBase): a: int b: str = ... - def __init__(self, *, a, b=...) -> None: ... + c: bool = field(default=True) [case testDataclassTransformMetaclass_semanal] +from dataclasses import field +from typing import Any from typing_extensions import dataclass_transform -@dataclass_transform(kw_only_default=True) +def custom_field(*, default: bool, kw_only: bool) -> Any: ... + +@dataclass_transform(kw_only_default=True, field_specifiers=(custom_field,)) class DCMeta(type): ... class X(metaclass=DCMeta): a: int b: str = "hello" + c: bool = field(default=True) # should be ignored, not field_specifier here + +class Y(X): + d: str = custom_field(default="Hello") [out] +from typing import Any from typing_extensions import dataclass_transform -@dataclass_transform(kw_only_default=True) +def custom_field(*, default: bool, kw_only: bool) -> Any: ... + +@dataclass_transform(kw_only_default=True, field_specifiers=(custom_field,)) class DCMeta(type): ... class X(metaclass=DCMeta): a: int b: str = ... - def __init__(self, *, a, b=...) -> None: ... + c: bool = ... + +class Y(X): + d: str = custom_field(default='Hello') [case testAlwaysUsePEP604Union] import typing @@ -4662,4 +4691,3 @@ class DCMeta(type): ... class DC(metaclass=DCMeta): x: str - def __init__(self, x) -> None: ... From 42a97bb3de805b27c4532fae1695661a06816e16 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:01:14 -0800 Subject: [PATCH 1059/1617] Walk up for all config files and handle precedence (#18482) Follow up to #16965 Fixes #16070 Handles other mypy configuration files and handles precedence between them. Also fixes few small things, like use in git worktrees --- CHANGELOG.md | 32 +++++-- docs/source/config_file.rst | 34 ++++--- mypy/config_parser.py | 115 +++++++++++++++-------- mypy/defaults.py | 41 +------- mypy/main.py | 2 +- mypy/test/test_config_parser.py | 130 ++++++++++++++++++++++++++ test-data/unit/cmdline.pyproject.test | 35 ------- 7 files changed, 254 insertions(+), 135 deletions(-) create mode 100644 mypy/test/test_config_parser.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e5260104f3fe..3acec84fec5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,6 @@ garbage collector. Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306)). -### Drop Support for Python 3.8 - -Mypy no longer supports running with Python 3.8, which has reached end-of-life. -When running mypy with Python 3.9+, it is still possible to type check code -that needs to support Python 3.8 with the `--python-version 3.8` argument. -Support for this will be dropped in the first half of 2025! - -Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). - ### Mypyc accelerated mypy wheels for aarch64 Mypy can compile itself to C extension modules using mypyc. This makes mypy 3-5x faster @@ -25,7 +16,9 @@ than if mypy is interpreted with pure Python. We now build and upload mypyc acce mypy wheels for `manylinux_aarch64` to PyPI, making it easy for users on such platforms to realise this speedup. -Contributed by Christian Bundy (PR [mypy_mypyc-wheels#76](https://github.com/mypyc/mypy_mypyc-wheels/pull/76)) +Contributed by Christian Bundy and Marc Mueller +(PR [mypy_mypyc-wheels#76](https://github.com/mypyc/mypy_mypyc-wheels/pull/76), +PR [mypy_mypyc-wheels#89](https://github.com/mypyc/mypy_mypyc-wheels/pull/89)). ### `--strict-bytes` @@ -48,6 +41,16 @@ Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull (Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` by default in **mypy 2.0**). +### Better discovery of configuration files + +Mypy will now walk up the filesystem (up until a repository or file system root) to discover +configuration files. See the +[mypy configuration file documentation](https://mypy.readthedocs.io/en/stable/config_file.html) +for more details. + +Contributed by Mikhail Shiryaev and Shantanu Jain +(PR [16965](https://github.com/python/mypy/pull/16965), PR [18482](https://github.com/python/mypy/pull/18482) + ### Better line numbers for decorators and slice expressions Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, this @@ -56,6 +59,15 @@ may necessitate changing the location of a `# type: ignore` comment. Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392), PR [18397](https://github.com/python/mypy/pull/18397)). +### Drop Support for Python 3.8 + +Mypy no longer supports running with Python 3.8, which has reached end-of-life. +When running mypy with Python 3.9+, it is still possible to type check code +that needs to support Python 3.8 with the `--python-version 3.8` argument. +Support for this will be dropped in the first half of 2025! + +Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). + ## Mypy 1.14 We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 747ef3a9fdaa..41dadbe7d2a3 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -7,22 +7,30 @@ Mypy is very configurable. This is most useful when introducing typing to an existing codebase. See :ref:`existing-code` for concrete advice for that situation. -Mypy supports reading configuration settings from a file with the following precedence order: +Mypy supports reading configuration settings from a file. By default, mypy will +discover configuration files by walking up the file system (up until the root of +a repository or the root of the filesystem). In each directory, it will look for +the following configuration files (in this order): - 1. ``./mypy.ini`` - 2. ``./.mypy.ini`` - 3. ``./pyproject.toml`` - 4. ``./setup.cfg`` - 5. ``$XDG_CONFIG_HOME/mypy/config`` - 6. ``~/.config/mypy/config`` - 7. ``~/.mypy.ini`` + 1. ``mypy.ini`` + 2. ``.mypy.ini`` + 3. ``pyproject.toml`` (containing a ``[tool.mypy]`` section) + 4. ``setup.cfg`` (containing a ``[mypy]`` section) + +If no configuration file is found by this method, mypy will then look for +configuration files in the following locations (in this order): + + 1. ``$XDG_CONFIG_HOME/mypy/config`` + 2. ``~/.config/mypy/config`` + 3. ``~/.mypy.ini`` + +The :option:`--config-file ` command-line flag has the +highest precedence and must point towards a valid configuration file; +otherwise mypy will report an error and exit. Without the command line option, +mypy will look for configuration files in the precedence order above. It is important to understand that there is no merging of configuration -files, as it would lead to ambiguity. The :option:`--config-file ` -command-line flag has the highest precedence and -must be correct; otherwise mypy will report an error and exit. Without the -command line option, mypy will look for configuration files in the -precedence order above. +files, as it would lead to ambiguity. Most flags correspond closely to :ref:`command-line flags ` but there are some differences in flag names and some diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a0f93f663522..4161f7e04dd3 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -15,7 +15,7 @@ else: import tomli as tomllib -from collections.abc import Iterable, Mapping, MutableMapping, Sequence +from collections.abc import Mapping, MutableMapping, Sequence from typing import Any, Callable, Final, TextIO, Union from typing_extensions import TypeAlias as _TypeAlias @@ -217,6 +217,72 @@ def split_commas(value: str) -> list[str]: ) +def _parse_individual_file( + config_file: str, stderr: TextIO | None = None +) -> tuple[MutableMapping[str, Any], dict[str, _INI_PARSER_CALLABLE], str] | None: + + if not os.path.exists(config_file): + return None + + parser: MutableMapping[str, Any] + try: + if is_toml(config_file): + with open(config_file, "rb") as f: + toml_data = tomllib.load(f) + # Filter down to just mypy relevant toml keys + toml_data = toml_data.get("tool", {}) + if "mypy" not in toml_data: + return None + toml_data = {"mypy": toml_data["mypy"]} + parser = destructure_overrides(toml_data) + config_types = toml_config_types + else: + parser = configparser.RawConfigParser() + parser.read(config_file) + config_types = ini_config_types + + except (tomllib.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: + print(f"{config_file}: {err}", file=stderr) + return None + + if os.path.basename(config_file) in defaults.SHARED_CONFIG_NAMES and "mypy" not in parser: + return None + + return parser, config_types, config_file + + +def _find_config_file( + stderr: TextIO | None = None, +) -> tuple[MutableMapping[str, Any], dict[str, _INI_PARSER_CALLABLE], str] | None: + + current_dir = os.path.abspath(os.getcwd()) + + while True: + for name in defaults.CONFIG_NAMES + defaults.SHARED_CONFIG_NAMES: + config_file = os.path.relpath(os.path.join(current_dir, name)) + ret = _parse_individual_file(config_file, stderr) + if ret is None: + continue + return ret + + if any( + os.path.exists(os.path.join(current_dir, cvs_root)) for cvs_root in (".git", ".hg") + ): + break + parent_dir = os.path.dirname(current_dir) + if parent_dir == current_dir: + break + current_dir = parent_dir + + for config_file in defaults.USER_CONFIG_FILES: + ret = _parse_individual_file(config_file, stderr) + if ret is None: + continue + return ret + + return None + + def parse_config_file( options: Options, set_strict_flags: Callable[[], None], @@ -233,47 +299,20 @@ def parse_config_file( stdout = stdout or sys.stdout stderr = stderr or sys.stderr - if filename is not None: - config_files: tuple[str, ...] = (filename,) - else: - config_files_iter: Iterable[str] = map(os.path.expanduser, defaults.CONFIG_FILES) - config_files = tuple(config_files_iter) - - config_parser = configparser.RawConfigParser() - - for config_file in config_files: - if not os.path.exists(config_file): - continue - try: - if is_toml(config_file): - with open(config_file, "rb") as f: - toml_data = tomllib.load(f) - # Filter down to just mypy relevant toml keys - toml_data = toml_data.get("tool", {}) - if "mypy" not in toml_data: - continue - toml_data = {"mypy": toml_data["mypy"]} - parser: MutableMapping[str, Any] = destructure_overrides(toml_data) - config_types = toml_config_types - else: - config_parser.read(config_file) - parser = config_parser - config_types = ini_config_types - except (tomllib.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: - print(f"{config_file}: {err}", file=stderr) - else: - if config_file in defaults.SHARED_CONFIG_FILES and "mypy" not in parser: - continue - file_read = config_file - options.config_file = file_read - break - else: + ret = ( + _parse_individual_file(filename, stderr) + if filename is not None + else _find_config_file(stderr) + ) + if ret is None: return + parser, config_types, file_read = ret - os.environ["MYPY_CONFIG_FILE_DIR"] = os.path.dirname(os.path.abspath(config_file)) + options.config_file = file_read + os.environ["MYPY_CONFIG_FILE_DIR"] = os.path.dirname(os.path.abspath(file_read)) if "mypy" not in parser: - if filename or file_read not in defaults.SHARED_CONFIG_FILES: + if filename or os.path.basename(file_read) not in defaults.SHARED_CONFIG_NAMES: print(f"{file_read}: No [mypy] section in config file", file=stderr) else: section = parser["mypy"] diff --git a/mypy/defaults.py b/mypy/defaults.py index ed0b8d0dc6d9..67628d544edf 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -12,50 +12,15 @@ # mypy, at least version PYTHON3_VERSION is needed. PYTHON3_VERSION_MIN: Final = (3, 8) # Keep in sync with typeshed's python support +CACHE_DIR: Final = ".mypy_cache" -def find_pyproject() -> str: - """Search for file pyproject.toml in the parent directories recursively. - - It resolves symlinks, so if there is any symlink up in the tree, it does not respect them - - If the file is not found until the root of FS or repository, PYPROJECT_FILE is used - """ - - def is_root(current_dir: str) -> bool: - parent = os.path.join(current_dir, os.path.pardir) - return os.path.samefile(current_dir, parent) or any( - os.path.isdir(os.path.join(current_dir, cvs_root)) for cvs_root in (".git", ".hg") - ) - - # Preserve the original behavior, returning PYPROJECT_FILE if exists - if os.path.isfile(PYPROJECT_FILE) or is_root(os.path.curdir): - return PYPROJECT_FILE - - # And iterate over the tree - current_dir = os.path.pardir - while not is_root(current_dir): - config_file = os.path.join(current_dir, PYPROJECT_FILE) - if os.path.isfile(config_file): - return config_file - parent = os.path.join(current_dir, os.path.pardir) - current_dir = parent - - return PYPROJECT_FILE - +CONFIG_NAMES: Final = ["mypy.ini", ".mypy.ini"] +SHARED_CONFIG_NAMES: Final = ["pyproject.toml", "setup.cfg"] -CACHE_DIR: Final = ".mypy_cache" -CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"] -PYPROJECT_FILE: Final = "pyproject.toml" -PYPROJECT_CONFIG_FILES: Final = [find_pyproject()] -SHARED_CONFIG_FILES: Final = ["setup.cfg"] USER_CONFIG_FILES: Final = ["~/.config/mypy/config", "~/.mypy.ini"] if os.environ.get("XDG_CONFIG_HOME"): USER_CONFIG_FILES.insert(0, os.path.join(os.environ["XDG_CONFIG_HOME"], "mypy/config")) -CONFIG_FILES: Final = ( - CONFIG_FILE + PYPROJECT_CONFIG_FILES + SHARED_CONFIG_FILES + USER_CONFIG_FILES -) - # This must include all reporters defined in mypy.report. This is defined here # to make reporter names available without importing mypy.report -- this speeds # up startup. diff --git a/mypy/main.py b/mypy/main.py index ae7a3b9d5c86..79147f8bf0bd 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -564,7 +564,7 @@ def add_invertible_flag( "--config-file", help=( f"Configuration file, must have a [mypy] section " - f"(defaults to {', '.join(defaults.CONFIG_FILES)})" + f"(defaults to {', '.join(defaults.CONFIG_NAMES + defaults.SHARED_CONFIG_NAMES)})" ), ) add_invertible_flag( diff --git a/mypy/test/test_config_parser.py b/mypy/test/test_config_parser.py new file mode 100644 index 000000000000..597143738f23 --- /dev/null +++ b/mypy/test/test_config_parser.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import contextlib +import os +import tempfile +import unittest +from collections.abc import Iterator +from pathlib import Path + +from mypy.config_parser import _find_config_file +from mypy.defaults import CONFIG_NAMES, SHARED_CONFIG_NAMES + + +@contextlib.contextmanager +def chdir(target: Path) -> Iterator[None]: + # Replace with contextlib.chdir in Python 3.11 + dir = os.getcwd() + os.chdir(target) + try: + yield + finally: + os.chdir(dir) + + +def write_config(path: Path, content: str | None = None) -> None: + if path.suffix == ".toml": + if content is None: + content = "[tool.mypy]\nstrict = true" + path.write_text(content) + else: + if content is None: + content = "[mypy]\nstrict = True" + path.write_text(content) + + +class FindConfigFileSuite(unittest.TestCase): + + def test_no_config(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + (tmpdir / ".git").touch() + with chdir(tmpdir): + result = _find_config_file() + assert result is None + + def test_parent_config_with_and_without_git(self) -> None: + for name in CONFIG_NAMES + SHARED_CONFIG_NAMES: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + config = tmpdir / name + write_config(config) + + child = tmpdir / "child" + child.mkdir() + + with chdir(child): + result = _find_config_file() + assert result is not None + assert Path(result[2]).resolve() == config.resolve() + + git = child / ".git" + git.touch() + + result = _find_config_file() + assert result is None + + git.unlink() + result = _find_config_file() + assert result is not None + hg = child / ".hg" + hg.touch() + + result = _find_config_file() + assert result is None + + def test_precedence(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + pyproject = tmpdir / "pyproject.toml" + setup_cfg = tmpdir / "setup.cfg" + mypy_ini = tmpdir / "mypy.ini" + dot_mypy = tmpdir / ".mypy.ini" + + child = tmpdir / "child" + child.mkdir() + + for cwd in [tmpdir, child]: + write_config(pyproject) + write_config(setup_cfg) + write_config(mypy_ini) + write_config(dot_mypy) + + with chdir(cwd): + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "mypy.ini" + + mypy_ini.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == ".mypy.ini" + + dot_mypy.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "pyproject.toml" + + pyproject.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "setup.cfg" + + def test_precedence_missing_section(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + child = tmpdir / "child" + child.mkdir() + + parent_mypy = tmpdir / "mypy.ini" + child_pyproject = child / "pyproject.toml" + write_config(parent_mypy) + write_config(child_pyproject, content="") + + with chdir(child): + result = _find_config_file() + assert result is not None + assert Path(result[2]).resolve() == parent_mypy.resolve() diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index e6e5f113a844..57e6facad032 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -133,38 +133,3 @@ Neither is this! description = "Factory ⸻ A code generator 🏭" \[tool.mypy] [file x.py] - -[case testSearchRecursively] -# cmd: mypy x.py -[file ../pyproject.toml] -\[tool.mypy] -\[tool.mypy.overrides] -module = "x" -disallow_untyped_defs = false -[file x.py] -pass -[out] -../pyproject.toml: tool.mypy.overrides sections must be an array. Please make sure you are using double brackets like so: [[tool.mypy.overrides]] -== Return code: 0 - -[case testSearchRecursivelyStopsGit] -# cmd: mypy x.py -[file .git/test] -[file ../pyproject.toml] -\[tool.mypy] -\[tool.mypy.overrides] -module = "x" -disallow_untyped_defs = false -[file x.py] -i: int = 0 - -[case testSearchRecursivelyStopsHg] -# cmd: mypy x.py -[file .hg/test] -[file ../pyproject.toml] -\[tool.mypy] -\[tool.mypy.overrides] -module = "x" -disallow_untyped_defs = false -[file x.py] -i: int = 0 From 43ea203e566901510dbdd59e8907fcddb2a8ee70 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 21 Jan 2025 18:31:35 +0300 Subject: [PATCH 1060/1617] Infer correct types with overloads of `Type[Guard | Is]` (#17678) Closes https://github.com/python/mypy/issues/17579 Consider this as a prototype, because I understand that there might be a lot of extra work to get this right. However, this does solve this problem in the original issue. --- mypy/checker.py | 24 +++++- mypy/checkexpr.py | 83 ++++++++++++++++--- test-data/unit/check-typeguard.test | 56 +++++++++++++ test-data/unit/check-typeis.test | 119 ++++++++++++++++++++++++++++ 4 files changed, 268 insertions(+), 14 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 47b08b683e36..5829b31447fe 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6036,15 +6036,31 @@ def find_isinstance_check_helper( # considered "always right" (i.e. even if the types are not overlapping). # Also note that a care must be taken to unwrap this back at read places # where we use this to narrow down declared type. - if node.callee.type_guard is not None: - return {expr: TypeGuardedType(node.callee.type_guard)}, {} + with self.msg.filter_errors(), self.local_type_map(): + # `node.callee` can be an `overload`ed function, + # we need to resolve the real `overload` case. + _, real_func = self.expr_checker.check_call( + get_proper_type(self.lookup_type(node.callee)), + node.args, + node.arg_kinds, + node, + node.arg_names, + ) + real_func = get_proper_type(real_func) + if not isinstance(real_func, CallableType) or not ( + real_func.type_guard or real_func.type_is + ): + return {}, {} + + if real_func.type_guard is not None: + return {expr: TypeGuardedType(real_func.type_guard)}, {} else: - assert node.callee.type_is is not None + assert real_func.type_is is not None return conditional_types_to_typemaps( expr, *self.conditional_types_with_intersection( self.lookup_type(expr), - [TypeRange(node.callee.type_is, is_upper_bound=False)], + [TypeRange(real_func.type_is, is_upper_bound=False)], expr, ), ) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index b6618109bb44..a10dc00bb1de 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2906,16 +2906,37 @@ def infer_overload_return_type( elif all_same_types([erase_type(typ) for typ in return_types]): self.chk.store_types(type_maps[0]) return erase_type(return_types[0]), erase_type(inferred_types[0]) - else: - return self.check_call( - callee=AnyType(TypeOfAny.special_form), - args=args, - arg_kinds=arg_kinds, - arg_names=arg_names, - context=context, - callable_name=callable_name, - object_type=object_type, - ) + return self.check_call( + callee=AnyType(TypeOfAny.special_form), + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type, + ) + elif not all_same_type_narrowers(matches): + # This is an example of how overloads can be: + # + # @overload + # def is_int(obj: float) -> TypeGuard[float]: ... + # @overload + # def is_int(obj: int) -> TypeGuard[int]: ... + # + # x: Any + # if is_int(x): + # reveal_type(x) # N: int | float + # + # So, we need to check that special case. + return self.check_call( + callee=self.combine_function_signatures(cast("list[ProperType]", matches)), + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type, + ) else: # Success! No ambiguity; return the first match. self.chk.store_types(type_maps[0]) @@ -3130,6 +3151,8 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call new_args: list[list[Type]] = [[] for _ in range(len(callables[0].arg_types))] new_kinds = list(callables[0].arg_kinds) new_returns: list[Type] = [] + new_type_guards: list[Type] = [] + new_type_narrowers: list[Type] = [] too_complex = False for target in callables: @@ -3156,8 +3179,25 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call for i, arg in enumerate(target.arg_types): new_args[i].append(arg) new_returns.append(target.ret_type) + if target.type_guard: + new_type_guards.append(target.type_guard) + if target.type_is: + new_type_narrowers.append(target.type_is) + + if new_type_guards and new_type_narrowers: + # They cannot be definined at the same time, + # declaring this function as too complex! + too_complex = True + union_type_guard = None + union_type_is = None + else: + union_type_guard = make_simplified_union(new_type_guards) if new_type_guards else None + union_type_is = ( + make_simplified_union(new_type_narrowers) if new_type_narrowers else None + ) union_return = make_simplified_union(new_returns) + if too_complex: any = AnyType(TypeOfAny.special_form) return callables[0].copy_modified( @@ -3167,6 +3207,8 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call ret_type=union_return, variables=variables, implicit=True, + type_guard=union_type_guard, + type_is=union_type_is, ) final_args = [] @@ -3180,6 +3222,8 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call ret_type=union_return, variables=variables, implicit=True, + type_guard=union_type_guard, + type_is=union_type_is, ) def erased_signature_similarity( @@ -6520,6 +6564,25 @@ def all_same_types(types: list[Type]) -> bool: return all(is_same_type(t, types[0]) for t in types[1:]) +def all_same_type_narrowers(types: list[CallableType]) -> bool: + if len(types) <= 1: + return True + + type_guards: list[Type] = [] + type_narrowers: list[Type] = [] + + for typ in types: + if typ.type_guard: + type_guards.append(typ.type_guard) + if typ.type_is: + type_narrowers.append(typ.type_is) + if type_guards and type_narrowers: + # Some overloads declare `TypeGuard` and some declare `TypeIs`, + # we cannot handle this in a union. + return False + return all_same_types(type_guards) and all_same_types(type_narrowers) + + def merge_typevars_in_callables_by_name( callables: Sequence[CallableType], ) -> tuple[list[CallableType], list[TypeVarType]]: diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index e7a8eac4f043..eff3ce068cc7 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -730,3 +730,59 @@ x: object assert a(x=x) reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testTypeGuardInOverloads] +from typing import Any, overload, Union +from typing_extensions import TypeGuard + +@overload +def func1(x: str) -> TypeGuard[str]: + ... + +@overload +def func1(x: int) -> TypeGuard[int]: + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Any): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.str, builtins.int]" + else: + reveal_type(val) # N: Revealed type is "Any" + +def func3(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def func4(val: int): + if func1(val): + reveal_type(val) # N: Revealed type is "builtins.int" + else: + reveal_type(val) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeIsInOverloadsSameReturn] +from typing import Any, overload, Union +from typing_extensions import TypeGuard + +@overload +def func1(x: str) -> TypeGuard[str]: + ... + +@overload +def func1(x: int) -> TypeGuard[str]: + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "builtins.str" + else: + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 2372f990fda1..7d1754bf8340 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -817,3 +817,122 @@ accept_typeguard(typeis) # E: Argument 1 to "accept_typeguard" has incompatible accept_typeguard(typeguard) [builtins fixtures/tuple.pyi] + +[case testTypeIsInOverloads] +from typing import Any, overload, Union +from typing_extensions import TypeIs + +@overload +def func1(x: str) -> TypeIs[str]: + ... + +@overload +def func1(x: int) -> TypeIs[int]: + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Any): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.str, builtins.int]" + else: + reveal_type(val) # N: Revealed type is "Any" + +def func3(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(val) + +def func4(val: int): + if func1(val): + reveal_type(val) # N: Revealed type is "builtins.int" + else: + reveal_type(val) +[builtins fixtures/tuple.pyi] + +[case testTypeIsInOverloadsSameReturn] +from typing import Any, overload, Union +from typing_extensions import TypeIs + +@overload +def func1(x: str) -> TypeIs[str]: + ... + +@overload +def func1(x: int) -> TypeIs[str]: # type: ignore + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "builtins.str" + else: + reveal_type(val) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeIsInOverloadsUnionizeError] +from typing import Any, overload, Union +from typing_extensions import TypeIs, TypeGuard + +@overload +def func1(x: str) -> TypeIs[str]: + ... + +@overload +def func1(x: int) -> TypeGuard[int]: + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsInOverloadsUnionizeError2] +from typing import Any, overload, Union +from typing_extensions import TypeIs, TypeGuard + +@overload +def func1(x: int) -> TypeGuard[int]: + ... + +@overload +def func1(x: str) -> TypeIs[str]: + ... + +def func1(x: Any) -> Any: + return True + +def func2(val: Union[int, str]): + if func1(val): + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsLikeIsDataclass] +from typing import Any, overload, Union, Type +from typing_extensions import TypeIs + +class DataclassInstance: ... + +@overload +def is_dataclass(obj: type) -> TypeIs[Type[DataclassInstance]]: ... +@overload +def is_dataclass(obj: object) -> TypeIs[Union[DataclassInstance, Type[DataclassInstance]]]: ... + +def is_dataclass(obj: Union[type, object]) -> bool: + return False + +def func(arg: Any) -> None: + if is_dataclass(arg): + reveal_type(arg) # N: Revealed type is "Union[Type[__main__.DataclassInstance], __main__.DataclassInstance]" +[builtins fixtures/tuple.pyi] From 58de753b6b76227ff726e9ce1888cfc24c7ba44b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 21 Jan 2025 18:08:23 +0000 Subject: [PATCH 1061/1617] [mypyc] Reduce impact of immortality on reference counting performance (#18459) Fixes mypyc/mypyc#1044. The addition of object immortality in Python 3.12 (PEP 683) introduced an extra immortality check to incref and decref operations. Objects with a specific reference count are treated as immortal, and their reference counts are never updated. It turns out that this slowed down the performance of certain workloads a lot (up to 70% increase in runtime, compared to 3.11). This PR reduces the impact of immortality via a few optimizations: 1. Assume instances of native classes and list objects are not immortal (skip immortality checks). 2. Skip incref of certain objects in some contexts when we know that they are immortal (e.g. avoid incref of `None`). The second change should be clear. We generally depend on CPython implementation details to improve performance, and this seems safe to do here as well. The first change could turn immortal objects into non-immortal ones. For native classes this is a decision we can arguably make -- native classes don't properly support immortality, and they can't be shared between subinterpreters. As discussed in PEP 683, skipping immortality checks here is acceptable even in cases where somebody tries to make a native instance immortal, but this could have some performance or memory use impact. The performance gains make this a good tradeoff. Since lists are mutable, they can't be safely shared between subinterpreters, so again not dealing with immortality is acceptable. It could reduce performance in some use cases by deimmortalizing lists, but this potential impact seems marginal compared to faster incref and decref operations on lists, which are some of the more common objects in Python programs. This speeds up self check by about 1.5% on Python 3.13. This speeds up the richards benchmark by 30-35% (!) on 3.13, and also some other benchmarks see smaller improvements. --- mypyc/codegen/emit.py | 13 +++- mypyc/codegen/emitfunc.py | 18 ++++++ mypyc/common.py | 6 ++ mypyc/ir/rtypes.py | 45 +++++++++++++- mypyc/lib-rt/mypyc_util.h | 29 +++++++++ mypyc/test/test_emit.py | 119 +++++++++++++++++++++++++++++++++--- mypyc/test/test_emitfunc.py | 29 ++++++++- 7 files changed, 241 insertions(+), 18 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index f6663e6194dc..bef560b3d42a 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -12,6 +12,7 @@ ATTR_PREFIX, BITMAP_BITS, FAST_ISINSTANCE_MAX_SUBCLASSES, + HAVE_IMMORTAL, NATIVE_PREFIX, REG_PREFIX, STATIC_PREFIX, @@ -511,8 +512,11 @@ def emit_inc_ref(self, dest: str, rtype: RType, *, rare: bool = False) -> None: for i, item_type in enumerate(rtype.types): self.emit_inc_ref(f"{dest}.f{i}", item_type) elif not rtype.is_unboxed: - # Always inline, since this is a simple op - self.emit_line("CPy_INCREF(%s);" % dest) + # Always inline, since this is a simple but very hot op + if rtype.may_be_immortal or not HAVE_IMMORTAL: + self.emit_line("CPy_INCREF(%s);" % dest) + else: + self.emit_line("CPy_INCREF_NO_IMM(%s);" % dest) # Otherwise assume it's an unboxed, pointerless value and do nothing. def emit_dec_ref( @@ -540,7 +544,10 @@ def emit_dec_ref( self.emit_line(f"CPy_{x}DecRef({dest});") else: # Inlined - self.emit_line(f"CPy_{x}DECREF({dest});") + if rtype.may_be_immortal or not HAVE_IMMORTAL: + self.emit_line(f"CPy_{x}DECREF({dest});") + else: + self.emit_line(f"CPy_{x}DECREF_NO_IMM({dest});") # Otherwise assume it's an unboxed, pointerless value and do nothing. def pretty_name(self, typ: RType) -> str: diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 6088fb06dd32..7239e0835da0 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -7,6 +7,7 @@ from mypyc.analysis.blockfreq import frequently_executed_blocks from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer from mypyc.common import ( + HAVE_IMMORTAL, MODULE_PREFIX, NATIVE_PREFIX, REG_PREFIX, @@ -76,9 +77,11 @@ RStruct, RTuple, RType, + is_bool_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, + is_none_rprimitive, is_pointer_rprimitive, is_tagged, ) @@ -578,6 +581,21 @@ def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Va ) def visit_inc_ref(self, op: IncRef) -> None: + if ( + isinstance(op.src, Box) + and (is_none_rprimitive(op.src.src.type) or is_bool_rprimitive(op.src.src.type)) + and HAVE_IMMORTAL + ): + # On Python 3.12+, None/True/False are immortal, and we can skip inc ref + return + + if isinstance(op.src, LoadLiteral) and HAVE_IMMORTAL: + value = op.src.value + # We can skip inc ref for immortal literals on Python 3.12+ + if type(value) is int and -5 <= value <= 256: + # Small integers are immortal + return + src = self.reg(op.src) self.emit_inc_ref(src, op.src.type) diff --git a/mypyc/common.py b/mypyc/common.py index 724f61c34b78..c49952510c07 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -82,6 +82,12 @@ "pythonsupport.c", ] +# Python 3.12 introduced immortal objects, specified via a special reference count +# value. The reference counts of immortal objects are normally not modified, but it's +# not strictly wrong to modify them. See PEP 683 for more information, but note that +# some details in the PEP are out of date. +HAVE_IMMORTAL: Final = sys.version_info >= (3, 12) + JsonDict = dict[str, Any] diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 96288423550c..6e7e94a618ab 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -26,7 +26,7 @@ from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar from typing_extensions import TypeGuard -from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name +from mypyc.common import HAVE_IMMORTAL, IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator if TYPE_CHECKING: @@ -69,6 +69,11 @@ def accept(self, visitor: RTypeVisitor[T]) -> T: def short_name(self) -> str: return short_name(self.name) + @property + @abstractmethod + def may_be_immortal(self) -> bool: + raise NotImplementedError + def __str__(self) -> str: return short_name(self.name) @@ -151,6 +156,10 @@ class RVoid(RType): def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rvoid(self) + @property + def may_be_immortal(self) -> bool: + return False + def serialize(self) -> str: return "void" @@ -193,6 +202,7 @@ def __init__( ctype: str = "PyObject *", size: int = PLATFORM_SIZE, error_overlap: bool = False, + may_be_immortal: bool = True, ) -> None: RPrimitive.primitive_map[name] = self @@ -204,6 +214,7 @@ def __init__( self._ctype = ctype self.size = size self.error_overlap = error_overlap + self._may_be_immortal = may_be_immortal and HAVE_IMMORTAL if ctype == "CPyTagged": self.c_undefined = "CPY_INT_TAG" elif ctype in ("int16_t", "int32_t", "int64_t"): @@ -230,6 +241,10 @@ def __init__( def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rprimitive(self) + @property + def may_be_immortal(self) -> bool: + return self._may_be_immortal + def serialize(self) -> str: return self.name @@ -433,8 +448,12 @@ def __hash__(self) -> int: "builtins.None", is_unboxed=True, is_refcounted=False, ctype="char", size=1 ) -# Python list object (or an instance of a subclass of list). -list_rprimitive: Final = RPrimitive("builtins.list", is_unboxed=False, is_refcounted=True) +# Python list object (or an instance of a subclass of list). These could be +# immortal, but since this is expected to be very rare, and the immortality checks +# can be pretty expensive for lists, we treat lists as non-immortal. +list_rprimitive: Final = RPrimitive( + "builtins.list", is_unboxed=False, is_refcounted=True, may_be_immortal=False +) # Python dict object (or an instance of a subclass of dict). dict_rprimitive: Final = RPrimitive("builtins.dict", is_unboxed=False, is_refcounted=True) @@ -642,6 +661,10 @@ def __init__(self, types: list[RType]) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rtuple(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: return "tuple[%s]" % ", ".join(str(typ) for typ in self.types) @@ -763,6 +786,10 @@ def __init__(self, name: str, names: list[str], types: list[RType]) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rstruct(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: # if not tuple(unnamed structs) return "{}{{{}}}".format( @@ -823,6 +850,10 @@ def __init__(self, class_ir: ClassIR) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rinstance(self) + @property + def may_be_immortal(self) -> bool: + return False + def struct_name(self, names: NameGenerator) -> str: return self.class_ir.struct_name(names) @@ -883,6 +914,10 @@ def make_simplified_union(items: list[RType]) -> RType: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_runion(self) + @property + def may_be_immortal(self) -> bool: + return any(item.may_be_immortal for item in self.items) + def __repr__(self) -> str: return "" % ", ".join(str(item) for item in self.items) @@ -953,6 +988,10 @@ def __init__(self, item_type: RType, length: int) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rarray(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: return f"{self.item_type}[{self.length}]" diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 9967f0a13b4f..01344331f04e 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -31,6 +31,35 @@ // Here just for consistency #define CPy_XDECREF(p) Py_XDECREF(p) +// The *_NO_IMM operations below perform refcount manipulation for +// non-immortal objects (Python 3.12 and later). +// +// Py_INCREF and other CPython operations check for immortality. This +// can be expensive when we know that an object cannot be immortal. + +static inline void CPy_INCREF_NO_IMM(PyObject *op) +{ + op->ob_refcnt++; +} + +static inline void CPy_DECREF_NO_IMM(PyObject *op) +{ + if (--op->ob_refcnt == 0) { + _Py_Dealloc(op); + } +} + +static inline void CPy_XDECREF_NO_IMM(PyObject *op) +{ + if (op != NULL && --op->ob_refcnt == 0) { + _Py_Dealloc(op); + } +} + +#define CPy_INCREF_NO_IMM(op) CPy_INCREF_NO_IMM((PyObject *)(op)) +#define CPy_DECREF_NO_IMM(op) CPy_DECREF_NO_IMM((PyObject *)(op)) +#define CPy_XDECREF_NO_IMM(op) CPy_XDECREF_NO_IMM((PyObject *)(op)) + // Tagged integer -- our representation of Python 'int' objects. // Small enough integers are represented as unboxed integers (shifted // left by 1); larger integers (larger than 63 bits on a 64-bit diff --git a/mypyc/test/test_emit.py b/mypyc/test/test_emit.py index e4ace3ec01f0..1baed3964299 100644 --- a/mypyc/test/test_emit.py +++ b/mypyc/test/test_emit.py @@ -3,8 +3,21 @@ import unittest from mypyc.codegen.emit import Emitter, EmitterContext +from mypyc.common import HAVE_IMMORTAL +from mypyc.ir.class_ir import ClassIR from mypyc.ir.ops import BasicBlock, Register, Value -from mypyc.ir.rtypes import RTuple, bool_rprimitive, int_rprimitive, str_rprimitive +from mypyc.ir.rtypes import ( + RInstance, + RTuple, + RUnion, + bool_rprimitive, + int_rprimitive, + list_rprimitive, + none_rprimitive, + object_rprimitive, + str_rprimitive, +) +from mypyc.irbuild.vtable import compute_vtable from mypyc.namegen import NameGenerator @@ -12,10 +25,15 @@ class TestEmitter(unittest.TestCase): def setUp(self) -> None: self.n = Register(int_rprimitive, "n") self.context = EmitterContext(NameGenerator([["mod"]])) + self.emitter = Emitter(self.context, {}) + + ir = ClassIR("A", "mod") + compute_vtable(ir) + ir.mro = [ir] + self.instance_a = RInstance(ir) def test_label(self) -> None: - emitter = Emitter(self.context, {}) - assert emitter.label(BasicBlock(4)) == "CPyL4" + assert self.emitter.label(BasicBlock(4)) == "CPyL4" def test_reg(self) -> None: names: dict[Value, str] = {self.n: "n"} @@ -23,17 +41,16 @@ def test_reg(self) -> None: assert emitter.reg(self.n) == "cpy_r_n" def test_object_annotation(self) -> None: - emitter = Emitter(self.context, {}) - assert emitter.object_annotation("hello, world", "line;") == " /* 'hello, world' */" + assert self.emitter.object_annotation("hello, world", "line;") == " /* 'hello, world' */" assert ( - emitter.object_annotation(list(range(30)), "line;") + self.emitter.object_annotation(list(range(30)), "line;") == """\ /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] */""" ) def test_emit_line(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter emitter.emit_line("line;") emitter.emit_line("a {") emitter.emit_line("f();") @@ -51,13 +68,13 @@ def test_emit_line(self) -> None: ) def test_emit_undefined_value_for_simple_type(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG" assert emitter.c_undefined_value(str_rprimitive) == "NULL" assert emitter.c_undefined_value(bool_rprimitive) == "2" def test_emit_undefined_value_for_tuple(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter assert ( emitter.c_undefined_value(RTuple([str_rprimitive, int_rprimitive, bool_rprimitive])) == "(tuple_T3OIC) { NULL, CPY_INT_TAG, 2 }" @@ -67,3 +84,87 @@ def test_emit_undefined_value_for_tuple(self) -> None: emitter.c_undefined_value(RTuple([RTuple([str_rprimitive]), bool_rprimitive])) == "(tuple_T2T1OC) { { NULL }, 2 }" ) + + def test_emit_inc_ref_object(self) -> None: + self.emitter.emit_inc_ref("x", object_rprimitive) + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_int(self) -> None: + self.emitter.emit_inc_ref("x", int_rprimitive) + self.assert_output("CPyTagged_INCREF(x);\n") + + def test_emit_inc_ref_rare(self) -> None: + self.emitter.emit_inc_ref("x", object_rprimitive, rare=True) + self.assert_output("CPy_INCREF(x);\n") + self.emitter.emit_inc_ref("x", int_rprimitive, rare=True) + self.assert_output("CPyTagged_IncRef(x);\n") + + def test_emit_inc_ref_list(self) -> None: + self.emitter.emit_inc_ref("x", list_rprimitive) + if HAVE_IMMORTAL: + self.assert_output("CPy_INCREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_instance(self) -> None: + self.emitter.emit_inc_ref("x", self.instance_a) + if HAVE_IMMORTAL: + self.assert_output("CPy_INCREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_optional(self) -> None: + optional = RUnion([self.instance_a, none_rprimitive]) + self.emitter.emit_inc_ref("o", optional) + self.assert_output("CPy_INCREF(o);\n") + + def test_emit_dec_ref_object(self) -> None: + self.emitter.emit_dec_ref("x", object_rprimitive) + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", object_rprimitive, is_xdec=True) + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_int(self) -> None: + self.emitter.emit_dec_ref("x", int_rprimitive) + self.assert_output("CPyTagged_DECREF(x);\n") + self.emitter.emit_dec_ref("x", int_rprimitive, is_xdec=True) + self.assert_output("CPyTagged_XDECREF(x);\n") + + def test_emit_dec_ref_rare(self) -> None: + self.emitter.emit_dec_ref("x", object_rprimitive, rare=True) + self.assert_output("CPy_DecRef(x);\n") + self.emitter.emit_dec_ref("x", int_rprimitive, rare=True) + self.assert_output("CPyTagged_DecRef(x);\n") + + def test_emit_dec_ref_list(self) -> None: + self.emitter.emit_dec_ref("x", list_rprimitive) + if HAVE_IMMORTAL: + self.assert_output("CPy_DECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", list_rprimitive, is_xdec=True) + if HAVE_IMMORTAL: + self.assert_output("CPy_XDECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_instance(self) -> None: + self.emitter.emit_dec_ref("x", self.instance_a) + if HAVE_IMMORTAL: + self.assert_output("CPy_DECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", self.instance_a, is_xdec=True) + if HAVE_IMMORTAL: + self.assert_output("CPy_XDECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_optional(self) -> None: + optional = RUnion([self.instance_a, none_rprimitive]) + self.emitter.emit_dec_ref("o", optional) + self.assert_output("CPy_DECREF(o);\n") + + def assert_output(self, expected: str) -> None: + assert "".join(self.emitter.fragments) == expected + self.emitter.fragments = [] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 90df131288f9..275e8c383a4b 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -5,7 +5,7 @@ from mypy.test.helpers import assert_string_arrays_equal from mypyc.codegen.emit import Emitter, EmitterContext from mypyc.codegen.emitfunc import FunctionEmitterVisitor, generate_native_function -from mypyc.common import PLATFORM_SIZE +from mypyc.common import HAVE_IMMORTAL, PLATFORM_SIZE from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg from mypyc.ir.ops import ( @@ -28,6 +28,7 @@ Integer, IntOp, LoadAddress, + LoadLiteral, LoadMem, Op, Register, @@ -53,6 +54,7 @@ int64_rprimitive, int_rprimitive, list_rprimitive, + none_rprimitive, object_rprimitive, pointer_rprimitive, short_int_rprimitive, @@ -114,6 +116,7 @@ def add_local(name: str, rtype: RType) -> Register: compute_vtable(ir) ir.mro = [ir] self.r = add_local("r", RInstance(ir)) + self.none = add_local("none", none_rprimitive) self.context = EmitterContext(NameGenerator([["mod"]])) @@ -805,9 +808,25 @@ def test_extend(self) -> None: Extend(a, int_rprimitive, signed=False), """cpy_r_r0 = (uint32_t)cpy_r_a;""" ) + def test_inc_ref_none(self) -> None: + b = Box(self.none) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + + def test_inc_ref_bool(self) -> None: + b = Box(self.b) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + + def test_inc_ref_int_literal(self) -> None: + for x in -5, 0, 1, 5, 255, 256: + b = LoadLiteral(x, object_rprimitive) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + for x in -1123355, -6, 257, 123235345: + b = LoadLiteral(x, object_rprimitive) + self.assert_emit([b, IncRef(b)], "CPy_INCREF(cpy_r_r0);") + def assert_emit( self, - op: Op, + op: Op | list[Op], expected: str, next_block: BasicBlock | None = None, *, @@ -816,7 +835,11 @@ def assert_emit( skip_next: bool = False, ) -> None: block = BasicBlock(0) - block.ops.append(op) + if isinstance(op, Op): + block.ops.append(op) + else: + block.ops.extend(op) + op = op[-1] value_names = generate_names_for_ir(self.registers, [block]) emitter = Emitter(self.context, value_names) declarations = Emitter(self.context, value_names) From 878d892babba490640e4757a5041dcb575c903b0 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Wed, 22 Jan 2025 20:43:14 +0100 Subject: [PATCH 1062/1617] Better names of and more compatibility between ad hoc intersections of instances (#18506) While working on #18433, we encountered [this bug.](https://github.com/python/mypy/pull/18433#issuecomment-2583314142). @ilevkivskyi identified [the underlying problem](https://github.com/python/mypy/pull/18433#issuecomment-2585455830), and we decided to try to reuse previously created ad hoc intersections of instances instead of always creating new ones. While working on this PR, I realised that reusing intersections requires more complete names. Currently, module and type variable specifications are not included, which could result in mistakes when using these names as identifiers. So, I switched to more complete names. Now, for example, `` becomes ``. Hence, I had to adjust many existing test cases where `reveal_type` is used. `testReuseIntersectionForRepeatedIsinstanceCalls` confirms that the mentioned bug is fixed. `testIsInstanceAdHocIntersectionIncrementalNestedClass` and `testIsInstanceAdHocIntersectionIncrementalUnions` are in a separate commit. I think they are not really necessary, so we might prefer to remove them. I added them originally because I had to adjust `lookup_fully_qualified` a little. The change is very simple, but I could not create a test case where it is not sufficient. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 39 +++++++------- mypy/lookup.py | 10 ++-- test-data/unit/check-incremental.test | 71 ++++++++++++++++++++---- test-data/unit/check-isinstance.test | 77 ++++++++++++++++----------- test-data/unit/check-narrowing.test | 6 +-- test-data/unit/check-protocols.test | 4 +- test-data/unit/check-python310.test | 14 ++--- test-data/unit/check-typeguard.test | 2 +- test-data/unit/check-typeis.test | 10 ++-- test-data/unit/deps.test | 2 +- test-data/unit/fine-grained.test | 14 ++--- 11 files changed, 159 insertions(+), 90 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5829b31447fe..7b0b88186f76 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5501,13 +5501,9 @@ def intersect_instances( theoretical subclass of the instances the user may be trying to use the generated intersection can serve as a placeholder. - This function will create a fresh subclass every time you call it, - even if you pass in the exact same arguments. So this means calling - `self.intersect_intersection([inst_1, inst_2], ctx)` twice will result - in instances of two distinct subclasses of inst_1 and inst_2. - - This is by design: we want each ad-hoc intersection to be unique since - they're supposed represent some other unknown subclass. + This function will create a fresh subclass the first time you call it. + So this means calling `self.intersect_intersection([inst_1, inst_2], ctx)` + twice will return the same subclass of inst_1 and inst_2. Returns None if creating the subclass is impossible (e.g. due to MRO errors or incompatible signatures). If we do successfully create @@ -5540,20 +5536,19 @@ def _get_base_classes(instances_: tuple[Instance, Instance]) -> list[Instance]: return base_classes_ def _make_fake_typeinfo_and_full_name( - base_classes_: list[Instance], curr_module_: MypyFile + base_classes_: list[Instance], curr_module_: MypyFile, options: Options ) -> tuple[TypeInfo, str]: - names_list = pretty_seq([x.type.name for x in base_classes_], "and") - short_name = f"" - full_name_ = gen_unique_name(short_name, curr_module_.names) - cdef, info_ = self.make_fake_typeinfo( - curr_module_.fullname, full_name_, short_name, base_classes_ - ) - return info_, full_name_ + names = [format_type_bare(x, options=options, verbosity=2) for x in base_classes_] + name = f"" + if (symbol := curr_module_.names.get(name)) is not None: + assert isinstance(symbol.node, TypeInfo) + return symbol.node, name + cdef, info_ = self.make_fake_typeinfo(curr_module_.fullname, name, name, base_classes_) + return info_, name base_classes = _get_base_classes(instances) - # 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. + # We use the pretty_names_list for error messages but for the real name that goes + # into the symbol table because it is not specific enough. pretty_names_list = pretty_seq( format_type_distinctly(*base_classes, options=self.options, bare=True), "and" ) @@ -5567,13 +5562,17 @@ def _make_fake_typeinfo_and_full_name( return None try: - info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) + info, full_name = _make_fake_typeinfo_and_full_name( + base_classes, curr_module, self.options + ) with self.msg.filter_errors() as local_errors: self.check_multiple_inheritance(info) if local_errors.has_new_errors(): # "class A(B, C)" unsafe, now check "class A(C, B)": base_classes = _get_base_classes(instances[::-1]) - info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) + info, full_name = _make_fake_typeinfo_and_full_name( + base_classes, curr_module, self.options + ) with self.msg.filter_errors() as local_errors: self.check_multiple_inheritance(info) info.is_intersection = True diff --git a/mypy/lookup.py b/mypy/lookup.py index 8fc8cf8be3c2..640481ff703c 100644 --- a/mypy/lookup.py +++ b/mypy/lookup.py @@ -22,9 +22,11 @@ def lookup_fully_qualified( This function should *not* be used to find a module. Those should be looked in the modules dictionary. """ - head = name + # 1. Exclude the names of ad hoc instance intersections from step 2. + i = name.find("" +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalNoChangeSameName] import b @@ -5291,7 +5291,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] [out2] -tmp/b.py:2: note: Revealed type is "a." +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalNoChangeTuple] @@ -5313,7 +5313,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] [out2] -tmp/b.py:2: note: Revealed type is "a." +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalIsInstanceChange] import c @@ -5347,9 +5347,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [out2] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalUnderlyingObjChang] import c @@ -5375,9 +5375,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "b." +tmp/c.py:2: note: Revealed type is "b." [out2] -tmp/c.py:2: note: Revealed type is "b." +tmp/c.py:2: note: Revealed type is "b." [case testIsInstanceAdHocIntersectionIncrementalIntersectionToUnreachable] import c @@ -5408,7 +5408,7 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [out2] tmp/b.py:2: error: Cannot determine type of "y" tmp/c.py:2: note: Revealed type is "Any" @@ -5445,7 +5445,60 @@ reveal_type(z) tmp/b.py:2: error: Cannot determine type of "y" tmp/c.py:2: note: Revealed type is "Any" [out2] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." + +[case testIsInstanceAdHocIntersectionIncrementalNestedClass] +import b +[file a.py] +class A: + class B: ... + class C: ... + class D: + def __init__(self) -> None: + x: A.B + assert isinstance(x, A.C) + self.x = x +[file b.py] +from a import A +[file b.py.2] +from a import A +reveal_type(A.D.x) +[builtins fixtures/isinstance.pyi] +[out] +[out2] +tmp/b.py:2: note: Revealed type is "a." + +[case testIsInstanceAdHocIntersectionIncrementalUnions] +import c +[file a.py] +import b +class A: + p: b.D +class B: + p: b.D +class C: + p: b.D + c: str +x: A +assert isinstance(x, (B, C)) +y = x +[file b.py] +class D: + p: int +[file c.py] +from a import y +[file c.py.2] +from a import y, C +reveal_type(y) +reveal_type(y.p.p) +assert isinstance(y, C) +reveal_type(y.c) +[builtins fixtures/isinstance.pyi] +[out] +[out2] +tmp/c.py:2: note: Revealed type is "Union[a., a.]" +tmp/c.py:3: note: Revealed type is "builtins.int" +tmp/c.py:5: note: Revealed type is "builtins.str" [case testStubFixupIssues] import a diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 4ad128914c4e..037e8edf8b51 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1359,7 +1359,7 @@ class B: pass x = B() if isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.B" reveal_type(x) # N: Revealed type is "__main__.B" @@ -2178,7 +2178,7 @@ def foo2(x: Optional[str]) -> None: if x is None: reveal_type(x) # N: Revealed type is "None" elif isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/isinstance.pyi] @@ -2202,7 +2202,7 @@ def foo2(x: Optional[str]) -> None: if x is None: reveal_type(x) # N: Revealed type is "None" elif isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/isinstance.pyi] @@ -2313,15 +2313,15 @@ class C: x: A if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, C): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." reveal_type(x.f1()) # N: Revealed type is "builtins.int" reveal_type(x.f2()) # N: Revealed type is "builtins.int" reveal_type(x.f3()) # N: Revealed type is "builtins.int" - x.bad() # E: "" has no attribute "bad" + x.bad() # E: "" has no attribute "bad" else: - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.A" [builtins fixtures/isinstance.pyi] @@ -2334,11 +2334,11 @@ class B: pass x: A if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionIncompatibleClasses] @@ -2359,7 +2359,7 @@ else: y: C if isinstance(y, B): - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." if isinstance(y, A): # E: Subclass of "C", "B", and "A" cannot exist: would have incompatible method signatures reveal_type(y) # E: Statement is unreachable [builtins fixtures/isinstance.pyi] @@ -2393,19 +2393,19 @@ class B: def t1(self) -> None: if isinstance(self, A1): - reveal_type(self) # N: Revealed type is "__main__." + reveal_type(self) # N: Revealed type is "__main__." x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[0]") x1: Literal[1] = self.f() def t2(self) -> None: if isinstance(self, (A0, A1)): - reveal_type(self) # N: Revealed type is "__main__.1" + reveal_type(self) # N: Revealed type is "__main__." x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[0]") x1: Literal[1] = self.f() def t3(self) -> None: if isinstance(self, (A1, A2)): - reveal_type(self) # N: Revealed type is "Union[__main__.2, __main__.]" + reveal_type(self) # N: Revealed type is "Union[__main__., __main__.]" x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1, 2]", variable has type "Literal[0]") x1: Literal[1] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1, 2]", variable has type "Literal[1]") @@ -2432,14 +2432,14 @@ else: y: A[Parent] if isinstance(y, B): - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." reveal_type(y.f()) # N: Revealed type is "__main__.Parent" else: reveal_type(y) # N: Revealed type is "__main__.A[__main__.Parent]" z: A[Child] if isinstance(z, B): - reveal_type(z) # N: Revealed type is "__main__.1" + reveal_type(z) # N: Revealed type is "__main__." reveal_type(z.f()) # N: Revealed type is "__main__.Child" else: reveal_type(z) # N: Revealed type is "__main__.A[__main__.Child]" @@ -2460,10 +2460,10 @@ T1 = TypeVar('T1', A, B) def f1(x: T1) -> T1: if isinstance(x, A): reveal_type(x) # N: Revealed type is "__main__.A" \ - # N: Revealed type is "__main__." + # N: Revealed type is "__main__." if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." \ - # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." \ + # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.A" else: @@ -2502,7 +2502,7 @@ T1 = TypeVar('T1', A, B) def f1(x: T1) -> T1: if isinstance(x, A): # The error message is confusing, but we indeed do run into problems if - # 'x' is a subclass of A and B + # 'x' is a subclass of __main__.A and __main__.B return A() # E: Incompatible return value type (got "A", expected "B") else: return B() @@ -2530,10 +2530,10 @@ def accept_concrete(c: Concrete) -> None: pass x: A if isinstance(x, B): var = x - reveal_type(var) # N: Revealed type is "__main__." + reveal_type(var) # N: Revealed type is "__main__." accept_a(var) accept_b(var) - accept_concrete(var) # E: Argument 1 to "accept_concrete" has incompatible type ""; expected "Concrete" + accept_concrete(var) # E: Argument 1 to "accept_concrete" has incompatible type ""; expected "Concrete" [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionReinfer] @@ -2543,14 +2543,14 @@ class B: pass x: A assert isinstance(x, B) -reveal_type(x) # N: Revealed type is "__main__." +reveal_type(x) # N: Revealed type is "__main__." y: A assert isinstance(y, B) -reveal_type(y) # N: Revealed type is "__main__.1" +reveal_type(y) # N: Revealed type is "__main__." x = y -reveal_type(x) # N: Revealed type is "__main__.1" +reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionWithUnions] @@ -2563,15 +2563,15 @@ class D: pass v1: A if isinstance(v1, (B, C)): - reveal_type(v1) # N: Revealed type is "Union[__main__., __main__.]" + reveal_type(v1) # N: Revealed type is "Union[__main__., __main__.]" v2: Union[A, B] if isinstance(v2, C): - reveal_type(v2) # N: Revealed type is "Union[__main__.1, __main__.]" + reveal_type(v2) # N: Revealed type is "Union[__main__., __main__.]" v3: Union[A, B] if isinstance(v3, (C, D)): - reveal_type(v3) # N: Revealed type is "Union[__main__.2, __main__., __main__.1, __main__.]" + reveal_type(v3) # N: Revealed type is "Union[__main__., __main__., __main__., __main__.]" [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionSameNames] @@ -2581,7 +2581,7 @@ class A: pass x: A if isinstance(x, A2): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." [file foo.py] class A: pass @@ -2611,7 +2611,7 @@ class Ambiguous: # We bias towards assuming these two classes could be overlapping foo: Concrete if isinstance(foo, Ambiguous): - reveal_type(foo) # N: Revealed type is "__main__." + reveal_type(foo) # N: Revealed type is "__main__." reveal_type(foo.x) # N: Revealed type is "builtins.int" [builtins fixtures/isinstance.pyi] @@ -2628,11 +2628,11 @@ class C: x: Type[A] if issubclass(x, B): - reveal_type(x) # N: Revealed type is "Type[__main__.]" + reveal_type(x) # N: Revealed type is "Type[__main__.]" if issubclass(x, C): # E: Subclass of "A", "B", and "C" cannot exist: would have incompatible method signatures reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Type[__main__.]" + reveal_type(x) # N: Revealed type is "Type[__main__.]" else: reveal_type(x) # N: Revealed type is "Type[__main__.A]" [builtins fixtures/isinstance.pyi] @@ -2932,3 +2932,16 @@ if isinstance(var, bool): # Type of var shouldn't fall back to Any reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]" [builtins fixtures/isinstance.pyi] + +[case testReuseIntersectionForRepeatedIsinstanceCalls] + +class A: ... +class B: ... + +a: A +if isinstance(a, B): + c = a +if isinstance(a, B): + c = a + +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index b9866c67c86c..ec647366e743 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2095,11 +2095,11 @@ class Z: ... x: X if isinstance(x, (Y, Z)): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Y, NoneType)): - reveal_type(x) # N: Revealed type is "__main__.1" + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Y, Z, NoneType)): - reveal_type(x) # N: Revealed type is "__main__.2" + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Z, NoneType)): # E: Subclass of "X" and "Z" cannot exist: "Z" is final \ # E: Subclass of "X" and "NoneType" cannot exist: "NoneType" is final reveal_type(x) # E: Statement is unreachable diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index ed8edea5f0d5..72dc161c6048 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -1754,7 +1754,7 @@ if isinstance(c1i, P1): else: reveal_type(c1i) # Unreachable if isinstance(c1i, P): - reveal_type(c1i) # N: Revealed type is "__main__." + reveal_type(c1i) # N: Revealed type is "__main__." else: reveal_type(c1i) # N: Revealed type is "__main__.C1[builtins.int]" @@ -1766,7 +1766,7 @@ else: c2: C2 if isinstance(c2, P): - reveal_type(c2) # N: Revealed type is "__main__." + reveal_type(c2) # N: Revealed type is "__main__." else: reveal_type(c2) # N: Revealed type is "__main__.C2" diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 9adb798c4ae7..ea6cc7ffe56a 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -64,7 +64,7 @@ m: A match m: case b.b: - reveal_type(m) # N: Revealed type is "__main__.1" + reveal_type(m) # N: Revealed type is "__main__." [file b.py] class B: ... b: B @@ -933,9 +933,9 @@ m: B match m: case A(): - reveal_type(m) # N: Revealed type is "__main__.2" + reveal_type(m) # N: Revealed type is "__main__." case A(i, j): - reveal_type(m) # N: Revealed type is "__main__.3" + reveal_type(m) # N: Revealed type is "__main__." [builtins fixtures/tuple.pyi] [case testMatchClassPatternNonexistentKeyword] @@ -1309,7 +1309,7 @@ m: A match m: case a if isinstance(a, B): - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." [builtins fixtures/isinstancelist.pyi] [case testMatchUnreachablePatternGuard] @@ -1749,10 +1749,10 @@ class C: pass def f(x: A) -> None: match x: case B() as y: - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." case C() as y: - reveal_type(y) # N: Revealed type is "__main__." - reveal_type(y) # N: Revealed type is "Union[__main__., __main__.]" + reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "Union[__main__., __main__.]" [case testMatchWithBreakAndContinue] def f(x: int | str | None) -> None: diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index eff3ce068cc7..c69e16c5cc9e 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -452,7 +452,7 @@ def g(x: object) -> None: ... def test(x: List[object]) -> None: if not(f(x) or isinstance(x, A)): return - g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" + g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" [builtins fixtures/tuple.pyi] [case testTypeGuardMultipleCondition-xfail] diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 7d1754bf8340..e70c71a4b62e 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -384,9 +384,9 @@ def guard(a: object) -> TypeIs[B]: a = A() if guard(a): - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." a = A() reveal_type(a) # N: Revealed type is "__main__.A" reveal_type(a) # N: Revealed type is "__main__.A" @@ -454,7 +454,7 @@ def g(x: object) -> None: ... def test(x: List[Any]) -> None: if not(f(x) or isinstance(x, A)): return - g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" + g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" [builtins fixtures/tuple.pyi] [case testTypeIsMultipleCondition] @@ -473,13 +473,13 @@ def is_bar(item: object) -> TypeIs[Bar]: def foobar(x: object): if not isinstance(x, Foo) or not isinstance(x, Bar): return - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." def foobar_typeis(x: object): if not is_foo(x) or not is_bar(x): return # Looks like a typo but this is what our unique name generation produces - reveal_type(x) # N: Revealed type is "__main__.1" + reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/tuple.pyi] [case testTypeIsAsFunctionArgAsBoolSubtype] diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 1aa025579535..6ba3f97a79df 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -432,7 +432,7 @@ def f(x: A) -> None: x.y [builtins fixtures/isinstancelist.pyi] [out] -.y> -> m.f +.y> -> m.f -> , m.A, m.f -> m.B, m.f diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 0f6e018fe325..c988a2dc80aa 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9591,7 +9591,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] == -b.py:2: note: Revealed type is "a." +b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalIsInstanceChange] import c @@ -9625,9 +9625,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." == -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalUnderlyingObjChang] import c @@ -9653,9 +9653,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "b." +c.py:2: note: Revealed type is "b." == -c.py:2: note: Revealed type is "b." +c.py:2: note: Revealed type is "b." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalIntersectionToUnreachable] import c @@ -9686,7 +9686,7 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." == c.py:2: note: Revealed type is "Any" b.py:2: error: Cannot determine type of "y" @@ -9723,7 +9723,7 @@ reveal_type(z) b.py:2: error: Cannot determine type of "y" c.py:2: note: Revealed type is "Any" == -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." [case testStubFixupIssues] [file a.py] From 48f9fc5ec1eaba3416166fbdf6f584a086dd493b Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:02:49 +0100 Subject: [PATCH 1063/1617] Hint at argument names when formatting callables with compatible return types in error messages (#18495) Fixes #18493. Improves message in #12013 and #4530, but probably still doesn't make it clear enough. Use higher verbosity for type formatting in error message if callables' return types are compatible and supertype has some named arguments, as that is a popular source of confusion. --- mypy/messages.py | 24 ++++++++++++++- test-data/unit/check-functions.test | 48 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index b63310825f7d..8e614f02277a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2855,7 +2855,29 @@ def format_type_distinctly(*types: Type, options: Options, bare: bool = False) - quoting them (such as prepending * or **) should use this. """ overlapping = find_type_overlaps(*types) - for verbosity in range(2): + + def format_single(arg: Type) -> str: + return format_type_inner(arg, verbosity=0, options=options, fullnames=overlapping) + + min_verbosity = 0 + # Prevent emitting weird errors like: + # ... has incompatible type "Callable[[int], Child]"; expected "Callable[[int], Parent]" + if len(types) == 2: + left, right = types + left = get_proper_type(left) + right = get_proper_type(right) + # If the right type has named arguments, they may be the reason for incompatibility. + # This excludes cases when right is Callable[[Something], None] without named args, + # because that's usually the right thing to do. + if ( + isinstance(left, CallableType) + and isinstance(right, CallableType) + and any(right.arg_names) + and is_subtype(left, right, ignore_pos_arg_names=True) + ): + min_verbosity = 1 + + for verbosity in range(min_verbosity, 2): strs = [ format_type_inner(type, verbosity=verbosity, options=options, fullnames=overlapping) for type in types diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 18425efb9cb0..58973307a1ae 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3472,3 +3472,51 @@ class Qux(Bar): def baz(self, x) -> None: pass [builtins fixtures/tuple.pyi] + +[case testDistinctFormatting] +from typing import Awaitable, Callable, ParamSpec + +P = ParamSpec("P") + +class A: pass +class B(A): pass + +def decorator(f: Callable[P, None]) -> Callable[[Callable[P, A]], None]: + return lambda _: None + +def key(x: int) -> None: ... +def fn_b(b: int) -> B: ... + +decorator(key)(fn_b) # E: Argument 1 has incompatible type "Callable[[Arg(int, 'b')], B]"; expected "Callable[[Arg(int, 'x')], A]" + +def decorator2(f: Callable[P, None]) -> Callable[ + [Callable[P, Awaitable[None]]], + Callable[P, Awaitable[None]], +]: + return lambda f: f + +def key2(x: int) -> None: + ... + +@decorator2(key2) # E: Argument 1 has incompatible type "Callable[[Arg(int, 'y')], Coroutine[Any, Any, None]]"; expected "Callable[[Arg(int, 'x')], Awaitable[None]]" +async def foo2(y: int) -> None: + ... + +class Parent: + def method_without(self) -> "Parent": ... + def method_with(self, param: str) -> "Parent": ... + +class Child(Parent): + method_without: Callable[["Child"], "Child"] + method_with: Callable[["Child", str], "Child"] # E: Incompatible types in assignment (expression has type "Callable[[str], Child]", base class "Parent" defined the type as "Callable[[Arg(str, 'param')], Parent]") +[builtins fixtures/tuple.pyi] + +[case testDistinctFormattingUnion] +from typing import Callable, Union +from mypy_extensions import Arg + +def f(x: Callable[[Arg(int, 'x')], None]) -> None: pass + +y: Callable[[Union[int, str]], None] +f(y) # E: Argument 1 to "f" has incompatible type "Callable[[Union[int, str]], None]"; expected "Callable[[Arg(int, 'x')], None]" +[builtins fixtures/tuple.pyi] From 59868834f59c9657889d511dac4561d2cb6e8a84 Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Thu, 23 Jan 2025 02:13:03 +0100 Subject: [PATCH 1064/1617] pytest options moved into pyproject.toml (#18501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes this warning: ``` reading manifest template 'MANIFEST.in' […] warning: no files found matching 'pytest.ini' ``` --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 80d73ab5f48e..f36c98f4dd3b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -42,7 +42,6 @@ graft test-data graft mypy/test include conftest.py include runtests.py -include pytest.ini include tox.ini include LICENSE mypyc/README.md CHANGELOG.md From 65193350d87fcc75636b1e3f4404693d57309e4f Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:07:02 -0800 Subject: [PATCH 1065/1617] Fix isinstance with explicit (non generic) type alias (#18512) This is a partial revert of #18173 to unblock the 1.15 release Fixes #18488 --- mypy/semanal.py | 1 - test-data/unit/check-type-aliases.test | 31 +++++++++----------------- test-data/unit/diff.test | 1 + 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 034d8fb28b42..febb9590887e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4022,7 +4022,6 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: and not res.args and not empty_tuple_index and not pep_695 - and not pep_613 ) if isinstance(res, ProperType) and isinstance(res, Instance): if not validate_instance(res, self.fail, empty_tuple_index): diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index f04bd777ee4e..9527c85ed26a 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1243,31 +1243,22 @@ A = Union[int, List[A]] def func(x: A) -> int: ... [builtins fixtures/tuple.pyi] -[case testAliasExplicitNoArgsBasic] -from typing import Any, List, assert_type +[case testAliasNonGeneric] from typing_extensions import TypeAlias +class Foo: ... -Implicit = List -Explicit: TypeAlias = List +ImplicitFoo = Foo +ExplicitFoo: TypeAlias = Foo -x1: Implicit[str] -x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 -assert_type(x1, List[str]) -assert_type(x2, List[Any]) -[builtins fixtures/tuple.pyi] - -[case testAliasExplicitNoArgsGenericClass] -# flags: --python-version 3.9 -from typing import Any, assert_type -from typing_extensions import TypeAlias +x1: ImplicitFoo[str] # E: "Foo" expects no type arguments, but 1 given +x2: ExplicitFoo[str] # E: "Foo" expects no type arguments, but 1 given -Implicit = list -Explicit: TypeAlias = list +def is_foo(x: object): + if isinstance(x, ImplicitFoo): + pass + if isinstance(x, ExplicitFoo): + pass -x1: Implicit[str] -x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 -assert_type(x1, list[str]) -assert_type(x2, list[Any]) [builtins fixtures/tuple.pyi] [case testAliasExplicitNoArgsTuple] diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index b7c71c7f37f2..4acf451e2c34 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1563,6 +1563,7 @@ type H[T] = int __main__.A __main__.C __main__.D +__main__.E __main__.G __main__.H From 905ea7b9a7a3461ad81a6ab1ad229d83e02ce777 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:22:32 +0100 Subject: [PATCH 1066/1617] Bump version to 1.16.0+dev (#18509) The release branch has been cut: https://github.com/python/mypy/tree/release-1.15 Increase the dev version. --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 8ad0efd03cdb..ffebfb7aa9ad 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.15.0+dev" +__version__ = "1.16.0+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 4a76a1a84153283cae6496e29572cb821e0bc270 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Thu, 23 Jan 2025 21:58:59 +0100 Subject: [PATCH 1067/1617] Add missing lineno to `yield from` with wrong type (#18518) Fixes #18517 --- mypy/checkexpr.py | 1 + test-data/unit/check-statements.test | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a10dc00bb1de..0752fa0b466f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6141,6 +6141,7 @@ def visit_yield_from_expr(self, e: YieldFromExpr, allow_none_return: bool = Fals generic_generator_type = self.chk.named_generic_type( "typing.Generator", [any_type, any_type, any_type] ) + generic_generator_type.set_line(e) iter_type, _ = self.check_method_call_by_name( "__iter__", subexpr_type, [], [], context=generic_generator_type ) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 44880cf35204..14904bc32e1b 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2283,6 +2283,20 @@ def get_strings(foo: bool) -> Iterator[str]: yield "bar2" [builtins fixtures/tuple.pyi] +[case testYieldFromInvalidType] +from collections.abc import Iterator + +class A: + def list(self) -> None: ... + + def foo(self) -> list[int]: # E: Function "__main__.A.list" is not valid as a type \ + # N: Perhaps you need "Callable[...]" or a callback protocol? + return [] + +def fn() -> Iterator[int]: + yield from A().foo() # E: "list?[builtins.int]" has no attribute "__iter__" (not iterable) +[builtins fixtures/tuple.pyi] + [case testNoCrashOnStarRightHandSide] x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi] From 2348b8dc639f9ea1ceb7c14df042e3a11fdd058e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:44:13 -0800 Subject: [PATCH 1068/1617] Improve inference in tuple multiplication plugin (#18521) Simple typo that I noticed --- mypy/plugins/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 03cb379a8173..81d2f19dc17b 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -554,7 +554,7 @@ def tuple_mul_callback(ctx: MethodContext) -> Type: value = arg_type.last_known_value.value if isinstance(value, int): return ctx.type.copy_modified(items=ctx.type.items * value) - elif isinstance(ctx.type, LiteralType): + elif isinstance(arg_type, LiteralType): value = arg_type.value if isinstance(value, int): return ctx.type.copy_modified(items=ctx.type.items * value) From 1b24bf771de5ca8d7a9584e693a09290a7bb4ef0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:43:58 +0100 Subject: [PATCH 1069/1617] Update NoReturn imports in tests (#18529) `mypy_extensions.NoReturn` has been redundant for a while now. With the next mypy_extensions release, it will raise a `DeprecationWarning` when imported. Replace existing imports in tests with `typing.NoReturn`. --- test-data/unit/check-flags.test | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 6dceb28b5cb6..ba0df196af22 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -347,7 +347,7 @@ def f() -> int: [case testNoReturnDisallowsReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def f() -> NoReturn: if bool(): @@ -358,7 +358,7 @@ def f() -> NoReturn: [case testNoReturnWithoutImplicitReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> NoReturn: @@ -367,7 +367,7 @@ def f() -> NoReturn: [case testNoReturnDisallowsImplicitReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def f() -> NoReturn: # E: Implicit return in function which does not return non_trivial_function = 1 @@ -391,7 +391,7 @@ x = force_forward_reference() [case testNoReturnNoWarnNoReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> int: @@ -403,7 +403,7 @@ def f() -> int: [case testNoReturnInExpr] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> int: @@ -413,14 +413,14 @@ reveal_type(f() or no_return()) # N: Revealed type is "builtins.int" [case testNoReturnVariable] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "Never") [builtins fixtures/dict.pyi] [case testNoReturnAsync] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn async def f() -> NoReturn: ... From 0c605483d11f902501afeb954e915dfbf096d577 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:44:12 +0100 Subject: [PATCH 1070/1617] Update TypedDict imports in tests (#18528) `mypy_extensions.TypedDict` has been redundant for a while now. With the next mypy_extensions release, it will raise a `DeprecationWarning` when imported. Replace existing imports in tests with `typing.TypedDict`. --- test-data/unit/check-classes.test | 45 +- test-data/unit/check-custom-plugin.test | 4 +- test-data/unit/check-flags.test | 23 +- test-data/unit/check-incremental.test | 27 +- test-data/unit/check-isinstance.test | 5 +- test-data/unit/check-literal.test | 12 +- test-data/unit/check-newsemanal.test | 18 +- test-data/unit/check-overloading.test | 20 +- test-data/unit/check-serialize.test | 6 +- test-data/unit/check-statements.test | 10 +- test-data/unit/check-typeddict.test | 471 ++++++++++--------- test-data/unit/deps-types.test | 6 +- test-data/unit/deps.test | 9 +- test-data/unit/diff.test | 20 +- test-data/unit/fine-grained.test | 30 +- test-data/unit/fixtures/typing-typeddict.pyi | 1 + test-data/unit/merge.test | 10 +- test-data/unit/pythoneval.test | 19 +- test-data/unit/reports.test | 5 +- test-data/unit/semanal-errors.test | 3 +- test-data/unit/semanal-typeddict.test | 17 +- 21 files changed, 412 insertions(+), 349 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index d1c33c4729a9..993c03bcceff 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -5184,11 +5184,12 @@ def test() -> None: [builtins fixtures/tuple.pyi] [case testCrashOnSelfRecursiveTypedDictVar] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'a': 'A'}) # type: ignore a: A [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCrashInJoinOfSelfRecursiveNamedTuples] @@ -5205,7 +5206,7 @@ lst = [n, m] [builtins fixtures/isinstancelist.pyi] [case testCorrectJoinOfSelfRecursiveTypedDicts] -from mypy_extensions import TypedDict +from typing import TypedDict def test() -> None: class N(TypedDict): @@ -5220,6 +5221,7 @@ def test() -> None: lst = [n, m] reveal_type(lst[0]['x']) # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCrashInForwardRefToNamedTupleWithIsinstance] from typing import Dict, NamedTuple @@ -5236,8 +5238,7 @@ def parse_ast(name_dict: NameDict) -> None: [typing fixtures/typing-medium.pyi] [case testCrashInForwardRefToTypedDictWithIsinstance] -from mypy_extensions import TypedDict -from typing import Dict +from typing import Dict, TypedDict NameDict = Dict[str, 'NameInfo'] class NameInfo(TypedDict): @@ -5248,7 +5249,7 @@ def parse_ast(name_dict: NameDict) -> None: pass reveal_type(name_dict['']['ast']) # N: Revealed type is "builtins.bool" [builtins fixtures/isinstancelist.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCorrectIsinstanceInForwardRefToNewType] from typing import Dict, NewType @@ -5313,13 +5314,13 @@ x = NT(N(1)) [case testNewTypeFromForwardTypedDict] -from typing import NewType, Tuple -from mypy_extensions import TypedDict +from typing import NewType, Tuple, TypedDict NT = NewType('NT', 'N') # E: Argument 2 to NewType(...) must be subclassable (got "N") class N(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCorrectAttributeInForwardRefToNamedTuple] @@ -5335,7 +5336,7 @@ class Process(NamedTuple): [out] [case testCorrectItemTypeInForwardRefToTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict proc: Process reveal_type(proc['state']) # N: Revealed type is "builtins.int" @@ -5344,6 +5345,7 @@ def get_state(proc: 'Process') -> int: class Process(TypedDict): state: int [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCorrectDoubleForwardNamedTuple] @@ -5362,7 +5364,7 @@ reveal_type(x.one.attr) # N: Revealed type is "builtins.str" [out] [case testCrashOnDoubleForwardTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict x: A class A(TypedDict): @@ -5373,6 +5375,7 @@ class B(TypedDict): reveal_type(x['one']['attr']) # N: Revealed type is "builtins.str" [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashOnForwardUnionOfNamedTuples] @@ -5392,8 +5395,7 @@ def foo(node: Node) -> int: [out] [case testCrashOnForwardUnionOfTypedDicts] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union NodeType = Union['Foo', 'Bar'] class Foo(TypedDict): @@ -5405,6 +5407,7 @@ def foo(node: NodeType) -> int: x = node return x['x'] [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testSupportForwardUnionOfNewTypes] @@ -5471,8 +5474,7 @@ def f(x: ForwardUnion) -> None: [out] [case testCrashInvalidArgsSyntheticClassSyntax] -from typing import List, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NamedTuple, TypedDict class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given class NM(NamedTuple): @@ -5482,11 +5484,11 @@ class NM(NamedTuple): TD({'x': []}) NM(x=[]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashInvalidArgsSyntheticClassSyntaxReveals] -from typing import List, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NamedTuple, TypedDict class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given class NM(NamedTuple): @@ -5501,11 +5503,11 @@ reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.l reveal_type(y) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]" reveal_type(y1) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashInvalidArgsSyntheticFunctionSyntax] -from typing import List, NewType, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NewType, NamedTuple, TypedDict TD = TypedDict('TD', {'x': List[int, str]}) # E: "list" expects 1 type argument, but 2 given NM = NamedTuple('NM', [('x', List[int, str])]) # E: "list" expects 1 type argument, but 2 given NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 given @@ -5515,11 +5517,11 @@ TD({'x': []}) NM(x=[]) NT([]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashForwardSyntheticClassSyntax] -from typing import NamedTuple -from mypy_extensions import TypedDict +from typing import NamedTuple, TypedDict class A1(NamedTuple): b: 'B' x: int @@ -5533,11 +5535,11 @@ y: A2 reveal_type(x.b) # N: Revealed type is "__main__.B" reveal_type(y['b']) # N: Revealed type is "__main__.B" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashForwardSyntheticFunctionSyntax] -from typing import NamedTuple -from mypy_extensions import TypedDict +from typing import NamedTuple, TypedDict A1 = NamedTuple('A1', [('b', 'B'), ('x', int)]) A2 = TypedDict('A2', {'b': 'B', 'x': int}) class B: @@ -5547,6 +5549,7 @@ y: A2 reveal_type(x.b) # N: Revealed type is "__main__.B" reveal_type(y['b']) # N: Revealed type is "__main__.B" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -- Special support for six diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 01facb63c6a6..db2ea2d5e659 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -579,8 +579,7 @@ plugins=/test-data/unit/plugins/method_sig_hook.py [case testMethodSignatureHookNamesFullyQualified] # flags: --config-file tmp/mypy.ini -from mypy_extensions import TypedDict -from typing import NamedTuple +from typing import NamedTuple, TypedDict class FullyQualifiedTestClass: @classmethod @@ -601,6 +600,7 @@ reveal_type(FullyQualifiedTestNamedTuple('')._asdict()) # N: Revealed type is "b \[mypy] plugins=/test-data/unit/plugins/fully_qualified_test_hook.py [builtins fixtures/classmethod.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDynamicClassPlugin] # flags: --config-file tmp/mypy.ini diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index ba0df196af22..2a75b465099b 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1082,25 +1082,25 @@ main:6: error: A type on this line becomes "Any" due to an unfollowed import [case testDisallowUnimportedAnyTypedDictSimple] # flags: --ignore-missing-imports --disallow-any-unimported -from mypy_extensions import TypedDict +from typing import TypedDict from x import Unchecked M = TypedDict('M', {'x': str, 'y': Unchecked}) # E: Type of a TypedDict key becomes "Any" due to an unfollowed import def f(m: M) -> M: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowUnimportedAnyTypedDictGeneric] # flags: --ignore-missing-imports --disallow-any-unimported - -from mypy_extensions import TypedDict -from typing import List +from typing import List, TypedDict from x import Unchecked M = TypedDict('M', {'x': str, 'y': List[Unchecked]}) # E: Type of a TypedDict key becomes "List[Any]" due to an unfollowed import def f(m: M) -> M: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyDecoratedUnannotatedDecorator] # flags: --disallow-any-decorated @@ -1337,13 +1337,14 @@ def k(s: E) -> None: pass [case testDisallowAnyExprTypedDict] # flags: --disallow-any-expr -from mypy_extensions import TypedDict +from typing import TypedDict Movie = TypedDict('Movie', {'name': str, 'year': int}) def g(m: Movie) -> Movie: return m [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowIncompleteDefs] # flags: --disallow-incomplete-defs @@ -1483,8 +1484,7 @@ n: N [case testCheckDisallowAnyGenericsTypedDict] # flags: --disallow-any-generics -from typing import Dict, Any, Optional -from mypy_extensions import TypedDict +from typing import Dict, Any, Optional, TypedDict VarsDict = Dict[str, Any] HostsDict = Dict[str, Optional[VarsDict]] @@ -1497,6 +1497,7 @@ GroupDataDict = TypedDict( GroupsDict = Dict[str, GroupDataDict] # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCheckDisallowAnyGenericsStubOnly] @@ -1929,22 +1930,22 @@ Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed [explicit-a [case testDisallowAnyExplicitTypedDictSimple] # flags: --disallow-any-explicit --show-error-codes -from mypy_extensions import TypedDict -from typing import Any +from typing import Any, TypedDict M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed [explicit-any] M(x='x', y=2) # no error def f(m: M) -> None: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyExplicitTypedDictGeneric] # flags: --disallow-any-explicit --show-error-codes -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed [explicit-any] N = TypedDict('N', {'x': str, 'y': List}) # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyGenericsTupleNoTypeParams] # flags: --disallow-any-generics diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 82362e00de1f..784e5e8a461a 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1893,11 +1893,12 @@ main:1: error: Module "ntcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1906,12 +1907,13 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod2] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: class D: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1920,13 +1922,14 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod3] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def a(self): class D: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1935,8 +1938,7 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalNewTypeInMethod] from ntcrash import nope [file ntcrash.py] -from mypy_extensions import TypedDict -from typing import NewType, NamedTuple +from typing import NewType, NamedTuple, TypedDict class C: def f(self) -> None: X = NewType('X', int) @@ -1949,6 +1951,7 @@ def f() -> None: B = NamedTuple('B', [('x', X)]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "ntcrash" has no attribute "nope" [out2] @@ -2088,10 +2091,11 @@ reveal_type(b.x) y: b.A reveal_type(y) [file b.py] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) x: A [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('b.A', {'x': builtins.int, 'y': builtins.str})" main:4: note: Revealed type is "TypedDict('b.A', {'x': builtins.int, 'y': builtins.str})" @@ -2532,14 +2536,14 @@ x = NT(N(1)) [out] [case testNewTypeFromForwardTypedDictIncremental] -from typing import NewType, Tuple, Dict -from mypy_extensions import TypedDict +from typing import NewType, Tuple, TypedDict, Dict NT = NewType('NT', N) # type: ignore class N(TypedDict): x: A A = Dict[str, int] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -- Some crazy self-referential named tuples, types dicts, and aliases @@ -4146,7 +4150,7 @@ from d import k [case testCachedBadProtocolNote] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) [file b.py] from typing import Iterable @@ -4158,8 +4162,8 @@ from typing import Iterable from a import Point p: Point it: Iterable[int] = p # change -[typing fixtures/typing-medium.pyi] [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [out] tmp/b.py:4: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") tmp/b.py:4: note: Following member(s) of "Point" have conflicts: @@ -4643,10 +4647,11 @@ from typing import NamedTuple from other import B A = NamedTuple('A', [('x', B)]) [file other.pyi] -from mypy_extensions import TypedDict +from typing import TypedDict from lib import A B = TypedDict('B', {'x': A}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]" diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 037e8edf8b51..2e483bbbfc26 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -2087,8 +2087,7 @@ else: [out] [case testNarrowTypeAfterInTypedDict] -from typing import Optional -from mypy_extensions import TypedDict +from typing import Optional, TypedDict class TD(TypedDict): a: int b: str @@ -2099,8 +2098,8 @@ def f() -> None: if x not in td: return reveal_type(x) # N: Revealed type is "builtins.str" -[typing fixtures/typing-typeddict.pyi] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testIsinstanceWidensWithAnyArg] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 296956334d20..fb97bec051e1 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1884,8 +1884,7 @@ tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression [builtins fixtures/slice.pyi] [case testLiteralIntelligentIndexingTypedDict] -from typing_extensions import Literal -from mypy_extensions import TypedDict +from typing_extensions import Literal, TypedDict class Unrelated: pass u: Unrelated @@ -1924,8 +1923,7 @@ del d[c_key] # E: TypedDict "Outer" has no key "c" [case testLiteralIntelligentIndexingUsingFinal] from typing import Tuple, NamedTuple -from typing_extensions import Literal, Final -from mypy_extensions import TypedDict +from typing_extensions import Literal, Final, TypedDict int_key_good: Final = 0 int_key_bad: Final = 3 @@ -1992,8 +1990,7 @@ tup2[idx_bad] # E: Tuple index out of range [out] [case testLiteralIntelligentIndexingTypedDictUnions] -from typing_extensions import Literal, Final -from mypy_extensions import TypedDict +from typing_extensions import Literal, Final, TypedDict class A: pass class B: pass @@ -2045,8 +2042,7 @@ del test[bad_keys] # E: Key "a" of TypedDict "Test" cannot be delet [case testLiteralIntelligentIndexingMultiTypedDict] from typing import Union -from typing_extensions import Literal -from mypy_extensions import TypedDict +from typing_extensions import Literal, TypedDict class A: pass class B: pass diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 7ac90d07e504..d5101e2e25f3 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -229,7 +229,7 @@ class C(B): [targets b, a, b, a, __main__] [case testNewAnalyzerTypedDictClass] -from mypy_extensions import TypedDict +from typing import TypedDict import a class T1(TypedDict): x: A @@ -237,7 +237,7 @@ class A: pass reveal_type(T1(x=A())) # E [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict from b import TD1 as TD2, TD3 class T2(TD3): x: int @@ -246,7 +246,8 @@ reveal_type(T2(x=2)) # E [file b.py] from a import TypedDict as TD1 from a import TD2 as TD3 -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/a.py:5: note: Revealed type is "TypedDict('a.T2', {'x': builtins.int})" @@ -254,7 +255,7 @@ main:6: note: Revealed type is "TypedDict('__main__.T1', {'x': __main__.A})" [case testNewAnalyzerTypedDictClassInheritance] -from mypy_extensions import TypedDict +from typing import TypedDict class T2(T1): y: int @@ -275,7 +276,8 @@ x: T2 reveal_type(x) # N: Revealed type is "TypedDict('__main__.T2', {'x': builtins.str, 'y': builtins.int})" y: T4 reveal_type(y) # N: Revealed type is "TypedDict('__main__.T4', {'x': builtins.str, 'y': __main__.A})" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerRedefinitionAndDeferral1a] import a @@ -1659,8 +1661,7 @@ tmp/a.py:10: error: Type argument "str" of "C" must be a subtype of "int" tmp/a.py:11: error: Type argument "str" of "C" must be a subtype of "int" [case testNewAnalyzerTypeArgBoundCheckDifferentNodes] -from typing import TypeVar, Generic, NamedTuple, NewType, Union, Any, cast, overload -from mypy_extensions import TypedDict +from typing import TypeVar, TypedDict, Generic, NamedTuple, NewType, Union, Any, cast, overload T = TypeVar('T', bound=int) class C(Generic[T]): pass @@ -1706,7 +1707,8 @@ def g(x: int) -> int: ... def g(x: Union[C[str], int]) -> int: # E: Type argument "str" of "C" must be a subtype of "int" y: C[object] # E: Type argument "object" of "C" must be a subtype of "int" return 0 -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerTypeArgBoundCheckWithStrictOptional] # flags: --config-file tmp/mypy.ini diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 9d01ce6bd480..5b8bd51ff9dc 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -2913,8 +2913,7 @@ class Wrapper(Generic[T]): [builtins fixtures/list.pyi] [case testOverloadTypedDictDifferentRequiredKeysMeansDictsAreDisjoint] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int, 'y': int}) B = TypedDict('B', {'x': int, 'y': str}) @@ -2925,10 +2924,10 @@ def f(x: A) -> int: ... def f(x: B) -> str: ... def f(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictPartiallyOverlappingRequiredKeys] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union A = TypedDict('A', {'x': int, 'y': Union[int, str]}) B = TypedDict('B', {'x': int, 'y': Union[str, float]}) @@ -2945,10 +2944,10 @@ def g(x: A) -> int: ... def g(x: B) -> object: ... def g(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictFullyNonTotalDictsAreAlwaysPartiallyOverlapping] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int, 'y': str}, total=False) B = TypedDict('B', {'a': bool}, total=False) @@ -2966,10 +2965,10 @@ def g(x: A) -> int: ... # E: Overloaded function signatures 1 and 2 overlap wit def g(x: C) -> str: ... def g(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTotalAndNonTotalTypedDictsCanPartiallyOverlap] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': Union[int, str], 'y': str, 'z': int}, total=False) @@ -2987,10 +2986,10 @@ def f2(x: A) -> str: ... def f2(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictsWithSomeOptionalKeysArePartiallyOverlapping] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union class A(TypedDict): x: int @@ -3009,6 +3008,7 @@ def f(x: C) -> str: ... def f(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedPartiallyOverlappingInheritedTypes1] from typing import overload, List, Union, TypeVar, Generic diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index 81da94c0591c..042a962be9b3 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -1054,7 +1054,7 @@ reveal_type(C().a) reveal_type(C().b) reveal_type(C().c) [file ntcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def __init__(self) -> None: A = TypedDict('A', {'x': int}) @@ -1062,6 +1062,7 @@ class C: self.b = A(x=0) # type: A self.c = A [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" main:3: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" @@ -1075,10 +1076,11 @@ main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.C from m import d reveal_type(d) [file m.py] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('m.D', {'x'?: builtins.int, 'y'?: builtins.str})" [out2] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 14904bc32e1b..48e0f2aa681f 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2182,8 +2182,7 @@ class M(N): pass [out] [case testForwardRefsInWithStatementImplicit] -from typing import ContextManager, Any -from mypy_extensions import TypedDict +from typing import ContextManager, Any, TypedDict cm: ContextManager[N] with cm as g: @@ -2191,12 +2190,11 @@ with cm as g: N = TypedDict('N', {'x': int}) [builtins fixtures/dict.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-full.pyi] [out] [case testForwardRefsInWithStatement] -from typing import ContextManager, Any -from mypy_extensions import TypedDict +from typing import ContextManager, Any, TypedDict cm: ContextManager[Any] with cm as g: # type: N @@ -2204,7 +2202,7 @@ with cm as g: # type: N N = TypedDict('N', {'x': int}) [builtins fixtures/dict.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-full.pyi] [out] [case testGlobalWithoutInitialization] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 5515cfc61b10..22e9963944a2 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1,7 +1,7 @@ -- Create Instance [case testCanCreateTypedDictInstanceWithKeywordArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -12,7 +12,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [targets __main__] [case testCanCreateTypedDictInstanceWithDictCall] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -22,7 +22,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictInstanceWithDictLiteral] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point({'x': 42, 'y': 1337}) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -32,8 +32,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictInstanceWithNoArguments] -from typing import TypeVar, Union -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union EmptyDict = TypedDict('EmptyDict', {}) p = EmptyDict() reveal_type(p) # N: Revealed type is "TypedDict('__main__.EmptyDict', {})" @@ -45,49 +44,55 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" -- Create Instance (Errors) [case testCannotCreateTypedDictInstanceWithUnknownArgumentPattern] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(42, 1337) # E: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceNonLiteralItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) x = 'x' p = Point({x: 42, 'y': 1337}) # E: Expected TypedDict key to be string literal [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithExtraItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42, y=1337, z=666) # E: Extra key "z" for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithMissingItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42) # E: Missing key "y" for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithIncompatibleItemType] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x='meaning_of_life', y=1337) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithInlineTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', { 'x': TypedDict('E', { # E: Use dict literal for nested TypedDict 'y': int }) }) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Define TypedDict (Class syntax) [case testCanCreateTypedDictWithClass] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -96,9 +101,10 @@ class Point(TypedDict): p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithSubclass] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1D(TypedDict): x: int @@ -109,9 +115,10 @@ p: Point2D reveal_type(r) # N: Revealed type is "TypedDict('__main__.Point1D', {'x': builtins.int})" reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithSubclass2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1D(TypedDict): x: int @@ -121,9 +128,10 @@ class Point2D(TypedDict, Point1D): # We also allow to include TypedDict in bases p: Point2D reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictClassEmpty] -from mypy_extensions import TypedDict +from typing import TypedDict class EmptyDict(TypedDict): pass @@ -131,12 +139,12 @@ class EmptyDict(TypedDict): p = EmptyDict() reveal_type(p) # N: Revealed type is "TypedDict('__main__.EmptyDict', {})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOldVersion] # Test that we can use class-syntax to merge function-based TypedDicts - -from mypy_extensions import TypedDict +from typing import TypedDict MovieBase1 = TypedDict( 'MovieBase1', {'name': str, 'year': int}) @@ -152,13 +160,13 @@ def foo(x): foo({}) # E: Missing keys ("name", "year") for TypedDict "Movie" foo({'name': 'lol', 'year': 2009, 'based_on': 0}) # E: Incompatible types (expression has type "int", TypedDict item "based_on" has type "str") - [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Define TypedDict (Class syntax errors) [case testCannotCreateTypedDictWithClassOtherBases] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass @@ -170,6 +178,7 @@ class Point2D(Point1D, A): # E: All bases of a new TypedDict must be TypedDict t p: Point2D reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateBases] # https://github.com/python/mypy/issues/3673 @@ -187,7 +196,7 @@ class C(TypedDict, TypedDict): # E: Duplicate base class "TypedDict" [typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithClassWithOtherStuff] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -198,6 +207,7 @@ class Point(TypedDict): p = Point(x=42, y=1337, z='whatever') reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int, 'z': Any})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithClassWithFunctionUsedToCrash] # https://github.com/python/mypy/issues/11079 @@ -237,12 +247,13 @@ class Foo(TypedDict): [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictTypeWithUnderscoreItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int, '_fallback': object}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassUnderscores] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -251,9 +262,10 @@ class Point(TypedDict): p: Point reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, '_y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateKey1] -from mypy_extensions import TypedDict +from typing import TypedDict class Bad(TypedDict): x: int @@ -262,6 +274,7 @@ class Bad(TypedDict): b: Bad reveal_type(b) # N: Revealed type is "TypedDict('__main__.Bad', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateKey2] from typing import TypedDict @@ -280,7 +293,7 @@ reveal_type(d2) # N: Revealed type is "TypedDict('__main__.D2', {'x': builtins.s [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOverwriting] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1(TypedDict): x: int @@ -292,9 +305,10 @@ class Bad(Point1, Point2): # E: Overwriting TypedDict field "x" while merging b: Bad reveal_type(b) # N: Revealed type is "TypedDict('__main__.Bad', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOverwriting2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1(TypedDict): x: int @@ -304,104 +318,111 @@ class Point2(Point1): p2: Point2 reveal_type(p2) # N: Revealed type is "TypedDict('__main__.Point2', {'x': builtins.float})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Subtyping [case testCanConvertTypedDictToItself] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def identity(p: Point) -> Point: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToEquivalentTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict PointA = TypedDict('PointA', {'x': int, 'y': int}) PointB = TypedDict('PointB', {'x': int, 'y': int}) def identity(p: PointA) -> PointB: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithNarrowerItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) ObjectPoint = TypedDict('ObjectPoint', {'x': object, 'y': object}) def convert(op: ObjectPoint) -> Point: return op # E: Incompatible return value type (got "ObjectPoint", expected "Point") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithWiderItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) ObjectPoint = TypedDict('ObjectPoint', {'x': object, 'y': object}) def convert(p: Point) -> ObjectPoint: return p # E: Incompatible return value type (got "Point", expected "ObjectPoint") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithIncompatibleItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Chameleon = TypedDict('Chameleon', {'x': str, 'y': str}) def convert(p: Point) -> Chameleon: return p # E: Incompatible return value type (got "Point", expected "Chameleon") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToNarrowerTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Point1D = TypedDict('Point1D', {'x': int}) def narrow(p: Point) -> Point1D: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToWiderTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Point3D = TypedDict('Point3D', {'x': int, 'y': int, 'z': int}) def widen(p: Point) -> Point3D: return p # E: Incompatible return value type (got "Point", expected "Point3D") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToCompatibleMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_mapping(p: Point) -> Mapping[str, object]: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToIncompatibleMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_mapping(p: Point) -> Mapping[str, int]: return p # E: Incompatible return value type (got "Point", expected "Mapping[str, int]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAcceptsIntForFloatDuckTypes] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': float, 'y': float}) def create_point() -> Point: return Point(x=1, y=2) reveal_type(Point(x=1, y=2)) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.float, 'y': builtins.float})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictDoesNotAcceptsFloatForInt] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def create_point() -> Point: return Point(x=1.2, y=2.5) [out] -main:5: error: Incompatible types (expression has type "float", TypedDict item "x" has type "int") -main:5: error: Incompatible types (expression has type "float", TypedDict item "y" has type "int") +main:4: error: Incompatible types (expression has type "float", TypedDict item "x" has type "int") +main:4: error: Incompatible types (expression has type "float", TypedDict item "y" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAcceptsAnyType] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': float, 'y': float}) def create_point(something: Any) -> Point: return Point({ @@ -409,17 +430,17 @@ def create_point(something: Any) -> Point: 'y': something.y }) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictValueTypeContext] -from mypy_extensions import TypedDict -from typing import List +from typing import List, TypedDict D = TypedDict('D', {'x': List[int]}) reveal_type(D(x=[])) # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.list[builtins.int]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToDictOrMutableMapping] -from mypy_extensions import TypedDict -from typing import Dict, MutableMapping +from typing import Dict, MutableMapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_dict(p: Point) -> Dict[str, int]: return p # E: Incompatible return value type (got "Point", expected "Dict[str, int]") @@ -429,15 +450,15 @@ def as_mutable_mapping(p: Point) -> MutableMapping[str, object]: [typing fixtures/typing-full.pyi] [case testCanConvertTypedDictToAny] -from mypy_extensions import TypedDict -from typing import Any +from typing import Any, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def unprotect(p: Point) -> Any: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testAnonymousTypedDictInErrorMessages] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': int, 'z': str, 'a': int}) @@ -453,6 +474,7 @@ f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int})]"; ll = [b, c] f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int, 'z': str})]"; expected "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithSimpleProtocol] from typing_extensions import Protocol, TypedDict @@ -507,7 +529,7 @@ reveal_type(fun(b)) # N: Revealed type is "builtins.object" -- Join [case testJoinOfTypedDictHasOnlyCommonKeysAndNewFallback] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) Point3D = TypedDict('Point3D', {'x': int, 'y': int, 'z': int}) p1 = TaggedPoint(type='2d', x=0, y=0) @@ -520,7 +542,7 @@ reveal_type(joined_points) # N: Revealed type is "TypedDict({'x': builtins.int, [typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictRemovesNonequivalentKeys] -from mypy_extensions import TypedDict +from typing import TypedDict CellWithInt = TypedDict('CellWithInt', {'value': object, 'meta': int}) CellWithObject = TypedDict('CellWithObject', {'value': object, 'meta': object}) c1 = CellWithInt(value=1, meta=42) @@ -530,9 +552,10 @@ reveal_type(c1) # N: Revealed type is "TypedDict('__main__.CellWithI reveal_type(c2) # N: Revealed type is "TypedDict('__main__.CellWithObject', {'value': builtins.object, 'meta': builtins.object})" reveal_type(joined_cells) # N: Revealed type is "builtins.list[TypedDict({'value': builtins.object})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfDisjointTypedDictsIsEmptyTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Cell = TypedDict('Cell', {'value': object}) d1 = Point(x=0, y=0) @@ -542,10 +565,10 @@ reveal_type(d1) # N: Revealed type is "TypedDict('__main__.Point', { reveal_type(d2) # N: Revealed type is "TypedDict('__main__.Cell', {'value': builtins.object})" reveal_type(joined_dicts) # N: Revealed type is "builtins.list[TypedDict({})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithCompatibleMappingIsMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = {'score': 999} # type: Mapping[str, int] @@ -554,10 +577,10 @@ joined2 = [right, left] reveal_type(joined1) # N: Revealed type is "builtins.list[typing.Mapping[builtins.str, builtins.object]]" reveal_type(joined2) # N: Revealed type is "builtins.list[typing.Mapping[builtins.str, builtins.object]]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithCompatibleMappingSupertypeIsSupertype] -from mypy_extensions import TypedDict -from typing import Sized +from typing import Sized, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = {'score': 999} # type: Sized @@ -569,8 +592,7 @@ reveal_type(joined2) # N: Revealed type is "builtins.list[typing.Sized]" [typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithIncompatibleTypeIsObject] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = 42 @@ -579,13 +601,13 @@ joined2 = [right, left] reveal_type(joined1) # N: Revealed type is "builtins.list[builtins.object]" reveal_type(joined2) # N: Revealed type is "builtins.list[builtins.object]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Meet [case testMeetOfTypedDictsWithCompatibleCommonKeysHasAllKeysAndNewFallback] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -593,10 +615,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XY, y: YZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict({'x': builtins.int, 'y': builtins.int, 'z': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithIncompatibleCommonKeysIsUninhabited] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XYa = TypedDict('XYa', {'x': int, 'y': int}) YbZ = TypedDict('YbZ', {'y': object, 'z': int}) T = TypeVar('T') @@ -604,10 +626,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XYa, y: YbZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithNoCommonKeysHasAllKeysAndNewFallback] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable X = TypedDict('X', {'x': int}) Z = TypedDict('Z', {'z': int}) T = TypeVar('T') @@ -615,11 +637,11 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: Z) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict({'x': builtins.int, 'z': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] # TODO: It would be more accurate for the meet to be TypedDict instead. [case testMeetOfTypedDictWithCompatibleMappingIsUninhabitedForNow] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Mapping +from typing import TypedDict, TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) M = Mapping[str, int] T = TypeVar('T') @@ -627,10 +649,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictWithIncompatibleMappingIsUninhabited] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Mapping +from typing import TypedDict, TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) M = Mapping[str, str] T = TypeVar('T') @@ -638,10 +660,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictWithCompatibleMappingSuperclassIsUninhabitedForNow] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Iterable +from typing import TypedDict, TypeVar, Callable, Iterable X = TypedDict('X', {'x': int}) I = Iterable[str] T = TypeVar('T') @@ -649,10 +671,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: I) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict('__main__.X', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithNonTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}, total=False) T = TypeVar('T') @@ -660,10 +682,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XY, y: YZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'?: builtins.int, 'y'?: builtins.int, 'z'?: builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithNonTotalAndTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -671,10 +693,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XY, y: YZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'?: builtins.int, 'y': builtins.int, 'z': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithIncompatibleNonTotalAndTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -682,13 +704,13 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XY, y: YZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Constraint Solver [case testTypedDictConstraintsAgainstIterable] -from typing import TypeVar, Iterable -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Iterable T = TypeVar('T') def f(x: Iterable[T]) -> T: pass A = TypedDict('A', {'x': int}) @@ -703,25 +725,26 @@ reveal_type(f(a)) # N: Revealed type is "builtins.str" -- Special Method: __getitem__ [case testCanGetItemOfTypedDictWithValidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) reveal_type(p['type']) # N: Revealed type is "builtins.str" reveal_type(p['x']) # N: Revealed type is "builtins.int" reveal_type(p['y']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfTypedDictWithInvalidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p: TaggedPoint p['typ'] # E: TypedDict "TaggedPoint" has no key "typ" \ # N: Did you mean "type"? [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfAnonymousTypedDictWithInvalidStringLiteralKey] -from typing import TypeVar -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': str, 'y': int, 'z': str}) B = TypedDict('B', {'x': str, 'z': int}) C = TypedDict('C', {'x': str, 'y': int, 'z': int}) @@ -732,68 +755,73 @@ ac = join(A(x='', y=1, z=''), C(x='', y=0, z=1)) ab['y'] # E: "y" is not a valid TypedDict key; expected one of ("x") ac['a'] # E: "a" is not a valid TypedDict key; expected one of ("x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfTypedDictWithNonLiteralKey] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) def get_coordinate(p: TaggedPoint, key: str) -> Union[str, int]: return p[key] # E: TypedDict key must be a string literal; expected one of ("type", "x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Special Method: __setitem__ [case testCanSetItemOfTypedDictWithValidStringLiteralKeyAndCompatibleValueType] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['type'] = 'two_d' p['x'] = 1 [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithIncompatibleValueType] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['x'] = 'y' # E: Value of "x" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithInvalidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['z'] = 1 # E: TypedDict "TaggedPoint" has no key "z" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithNonLiteralKey] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) def set_coordinate(p: TaggedPoint, key: str, value: int) -> None: p[key] = value # E: TypedDict key must be a string literal; expected one of ("type", "x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- isinstance [case testTypedDictWithIsInstanceAndIsSubclass] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int}) d: object if isinstance(d, D): # E: Cannot use isinstance() with TypedDict type reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.int})" issubclass(object, D) # E: Cannot use issubclass() with TypedDict type [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] -- Scoping [case testTypedDictInClassNamespace] # https://github.com/python/mypy/pull/2553#issuecomment-266474341 -from mypy_extensions import TypedDict +from typing import TypedDict class C: def f(self): A = TypedDict('A', {'x': int}) @@ -801,20 +829,21 @@ class C: A = TypedDict('A', {'y': int}) C.A # E: "Type[C]" has no attribute "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictInFunction] -from mypy_extensions import TypedDict +from typing import TypedDict def f() -> None: A = TypedDict('A', {'x': int}) A # E: Name "A" is not defined [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Union simplification / proper subtype checks [case testTypedDictUnionSimplification] -from typing import TypeVar, Union, Any, cast -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Any, cast T = TypeVar('T') S = TypeVar('S') @@ -842,10 +871,10 @@ reveal_type(u(f, c)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a': reveal_type(u(c, g)) # N: Revealed type is "Union[TypedDict('__main__.G', {'a': Any}), TypedDict('__main__.C', {'a': builtins.int})]" reveal_type(u(g, c)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a': builtins.int}), TypedDict('__main__.G', {'a': Any})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionSimplification2] -from typing import TypeVar, Union, Mapping, Any -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Mapping, Any T = TypeVar('T') S = TypeVar('S') @@ -865,6 +894,7 @@ reveal_type(u(c, m_s_s)) # N: Revealed type is "Union[typing.Mapping[builtins.st reveal_type(u(c, m_i_i)) # N: Revealed type is "Union[typing.Mapping[builtins.int, builtins.int], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]" reveal_type(u(c, m_s_a)) # N: Revealed type is "Union[typing.Mapping[builtins.str, Any], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionUnambiguousCase] from typing import Union, Mapping, Any, cast @@ -901,7 +931,7 @@ c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} # E: Type of TypedDict is -- Use dict literals [case testTypedDictDictLiterals] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) @@ -919,9 +949,10 @@ f(dict(x=1, y=3, z=4)) # E: Extra key "z" for TypedDict "Point" f(dict(x=1, y=3, z=4, a=5)) # E: Extra keys ("z", "a") for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictExplicitTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) @@ -938,10 +969,10 @@ if int(): p4: Point = {'x': 1, 'y': 2} [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateAnonymousTypedDictInstanceUsingDictLiteralWithExtraItems] -from mypy_extensions import TypedDict -from typing import TypeVar +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int, 'y': int}) B = TypedDict('B', {'x': int, 'y': str}) T = TypeVar('T') @@ -950,10 +981,10 @@ ab = join(A(x=1, y=1), B(x=1, y='')) if int(): ab = {'x': 1, 'z': 1} # E: Expected TypedDict key "x" but found keys ("x", "z") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateAnonymousTypedDictInstanceUsingDictLiteralWithMissingItems] -from mypy_extensions import TypedDict -from typing import TypeVar +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int, 'y': int, 'z': int}) B = TypedDict('B', {'x': int, 'y': int, 'z': str}) T = TypeVar('T') @@ -962,12 +993,13 @@ ab = join(A(x=1, y=1, z=1), B(x=1, y=1, z='')) if int(): ab = {} # E: Expected TypedDict keys ("x", "y") but found no keys [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Other TypedDict methods [case testTypedDictGetMethod] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'x': int, 'y': str}) d: D @@ -980,8 +1012,7 @@ reveal_type(d.get('y', None)) # N: Revealed type is "Union[builtins.str, None]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetMethodTypeContext] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict class A: pass D = TypedDict('D', {'x': List[int], 'y': int}) d: D @@ -993,7 +1024,7 @@ reveal_type(d.get('x', a)) # N: Revealed type is "Union[builtins.list[builtins.i [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetMethodInvalidArgs] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) d: D d.get() # E: All overload variants of "get" of "Mapping" require at least one argument \ @@ -1013,14 +1044,15 @@ reveal_type(y) # N: Revealed type is "builtins.object" [typing fixtures/typing-typeddict.pyi] [case testTypedDictMissingMethod] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) d: D d.bad(1) # E: "D" has no attribute "bad" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictChainedGetMethodWithDictFallback] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) E = TypedDict('E', {'d': D}) p = E(d=D(x=0, y='')) @@ -1029,7 +1061,7 @@ reveal_type(p.get('d', {'x': 1, 'y': ''})) # N: Revealed type is "TypedDict('__m [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetDefaultParameterStillTypeChecked] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p.get('x', 1 + 'y') # E: Unsupported operand types for + ("int" and "str") @@ -1037,7 +1069,7 @@ p.get('x', 1 + 'y') # E: Unsupported operand types for + ("int" and "str") [typing fixtures/typing-typeddict.pyi] [case testTypedDictChainedGetWithEmptyDictDefault] -from mypy_extensions import TypedDict +from typing import TypedDict C = TypedDict('C', {'a': int}) D = TypedDict('D', {'x': C, 'y': str}) d: D @@ -1054,23 +1086,25 @@ reveal_type(d.get('x', {})['a']) # N: Revealed type is "builtins.int" -- Totality (the "total" keyword argument) [case testTypedDictWithTotalTrue] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=True) d: D reveal_type(d) \ # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.int, 'y': builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithInvalidTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int}, total=0) # E: "total" argument must be a True or False literal B = TypedDict('B', {'x': int}, total=bool) # E: "total" argument must be a True or False literal C = TypedDict('C', {'x': int}, x=False) # E: Unexpected keyword argument "x" for "TypedDict" D = TypedDict('D', {'x': int}, False) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) def f(d: D) -> None: reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" @@ -1081,9 +1115,10 @@ f({'x': 1, 'y': ''}) f({'x': 1, 'z': ''}) # E: Extra key "z" for TypedDict "D" f({'x': ''}) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictConstructorWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) def f(d: D) -> None: pass reveal_type(D()) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" @@ -1093,9 +1128,10 @@ f(D(x=1, y='')) f(D(x=1, z='')) # E: Extra key "z" for TypedDict "D" f(D(x='')) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictIndexingWithNonRequiredKey] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D reveal_type(d['x']) # N: Revealed type is "builtins.int" @@ -1106,7 +1142,7 @@ reveal_type(d.get('y')) # N: Revealed type is "Union[builtins.str, None]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictSubtypingWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': int}, total=False) C = TypedDict('C', {'x': int, 'y': str}, total=False) @@ -1123,10 +1159,10 @@ fb(a) # E: Argument 1 to "fb" has incompatible type "A"; expected "B" fa(b) # E: Argument 1 to "fa" has incompatible type "B"; expected "A" fc(b) # E: Argument 1 to "fc" has incompatible type "B"; expected "C" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictJoinWithTotalFalse] -from typing import TypeVar -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': int}, total=False) C = TypedDict('C', {'x': int, 'y': str}, total=False) @@ -1146,18 +1182,20 @@ reveal_type(j(b, c)) \ reveal_type(j(c, b)) \ # N: Revealed type is "TypedDict({'x'?: builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassWithTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class D(TypedDict, total=False): x: int y: str d: D reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassWithInvalidTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class D(TypedDict, total=1): # E: "total" argument must be a True or False literal x: int class E(TypedDict, total=bool): # E: "total" argument must be a True or False literal @@ -1166,9 +1204,10 @@ class F(TypedDict, total=xyz): # E: Name "xyz" is not defined \ # E: "total" argument must be a True or False literal x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassInheritanceWithTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): x: int class B(TypedDict, A, total=False): @@ -1178,9 +1217,10 @@ class C(TypedDict, B, total=True): c: C reveal_type(c) # N: Revealed type is "TypedDict('__main__.C', {'x': builtins.int, 'y'?: builtins.int, 'z': builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNonTotalTypedDictInErrorMessages] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}, total=False) B = TypedDict('B', {'x': int, 'z': str, 'a': int}, total=False) @@ -1196,10 +1236,11 @@ f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int})]" ll = [b, c] f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int, 'z'?: str})]"; expected "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNonTotalTypedDictCanBeEmpty] # flags: --warn-unreachable -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): ... @@ -1216,70 +1257,80 @@ if not a: if not b: reveal_type(b) # N: Revealed type is "TypedDict('__main__.B', {'x'?: builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Create Type (Errors) [case testCannotCreateTypedDictTypeWithTooFewArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point') # E: Too few arguments for TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithTooManyArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}, dict) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(dict, {'x': int, 'y': int}) # E: TypedDict() expects a string literal as the first argument [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x'}) # E: TypedDict() expects a dictionary literal as the second argument [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithKwargs] -from mypy_extensions import TypedDict +from typing import TypedDict d = {'x': int, 'y': int} Point = TypedDict('Point', {**d}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithBytes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(b'Point', {'x': int, 'y': int}) # E: TypedDict() expects a string literal as the first argument # This technically works at runtime but doesn't make sense. Point2 = TypedDict('Point2', {b'x': int}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- NOTE: The following code works at runtime but is not yet supported by mypy. -- Keyword arguments may potentially be supported in the future. [case testCannotCreateTypedDictTypeWithNonpositionalArgs] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(typename='Point', fields={'x': int, 'y': int}) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {int: int, int: int}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItemType] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': 1, 'y': 1}) # E: Invalid type: try using Literal[1] instead? [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidName2] -from mypy_extensions import TypedDict +from typing import TypedDict X = TypedDict('Y', {'x': int}) # E: First argument "Y" to TypedDict() does not match variable name "X" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Overloading [case testTypedDictOverloading] -from typing import overload, Iterable -from mypy_extensions import TypedDict +from typing import overload, Iterable, TypedDict A = TypedDict('A', {'x': int}) @@ -1296,8 +1347,7 @@ reveal_type(f(1)) # N: Revealed type is "builtins.int" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading2] -from typing import overload, Iterable -from mypy_extensions import TypedDict +from typing import overload, Iterable, TypedDict A = TypedDict('A', {'x': int}) @@ -1312,16 +1362,15 @@ f(a) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] [out] -main:13: error: Argument 1 to "f" has incompatible type "A"; expected "Iterable[int]" -main:13: note: Following member(s) of "A" have conflicts: -main:13: note: Expected: -main:13: note: def __iter__(self) -> Iterator[int] -main:13: note: Got: -main:13: note: def __iter__(self) -> Iterator[str] +main:12: error: Argument 1 to "f" has incompatible type "A"; expected "Iterable[int]" +main:12: note: Following member(s) of "A" have conflicts: +main:12: note: Expected: +main:12: note: def __iter__(self) -> Iterator[int] +main:12: note: Got: +main:12: note: def __iter__(self) -> Iterator[str] [case testTypedDictOverloading3] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) @@ -1340,8 +1389,7 @@ f(a) # E: No overload variant of "f" matches argument type "A" \ [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading4] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': str}) @@ -1361,8 +1409,7 @@ f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "A" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading5] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'y': str}) @@ -1384,8 +1431,7 @@ f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading6] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'y': str}) @@ -1407,8 +1453,7 @@ reveal_type(f(b)) # N: Revealed type is "builtins.str" -- Special cases [case testForwardReferenceInTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping X = TypedDict('X', {'b': 'B', 'c': 'C'}) class B: pass class C(B): pass @@ -1417,10 +1462,10 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'b': __main__.B, m1: Mapping[str, object] = x m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testForwardReferenceInClassTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping class X(TypedDict): b: 'B' c: 'C' @@ -1431,19 +1476,20 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'b': __main__.B, m1: Mapping[str, object] = x m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testForwardReferenceToTypedDictInTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping X = TypedDict('X', {'a': 'A'}) A = TypedDict('A', {'b': int}) x: X reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'a': TypedDict('__main__.A', {'b': builtins.int})})" reveal_type(x['a']['b']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSelfRecursiveTypedDictInheriting] -from mypy_extensions import TypedDict +from typing import TypedDict def test() -> None: class MovieBase(TypedDict): @@ -1456,10 +1502,10 @@ def test() -> None: m: Movie reveal_type(m['director']['name']) # N: Revealed type is "Any" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSubclassOfRecursiveTypedDict] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict def test() -> None: class Command(TypedDict): @@ -1470,13 +1516,13 @@ def test() -> None: pass hc = HelpCommand(subcommands=[]) - reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand@8', {'subcommands': builtins.list[Any]})" + reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand@7', {'subcommands': builtins.list[Any]})" [builtins fixtures/list.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictForwardAsUpperBound] -from typing import TypeVar, Generic -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Generic T = TypeVar('T', bound='M') class G(Generic[T]): x: T @@ -1488,12 +1534,13 @@ z: int = G[M]().x['x'] # type: ignore[used-before-def] class M(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictWithImportCycleForward] import a [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict from b import f N = TypedDict('N', {'a': str}) @@ -1504,6 +1551,7 @@ def f(x: a.N) -> None: reveal_type(x) reveal_type(x['a']) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/b.py:4: note: Revealed type is "TypedDict('a.N', {'a': builtins.str})" tmp/b.py:5: note: Revealed type is "builtins.str" @@ -1524,14 +1572,15 @@ tp(x='no') # E: Incompatible types (expression has type "str", TypedDict item " [file b.py] from a import C -from mypy_extensions import TypedDict +from typing import TypedDict tp = TypedDict('tp', {'x': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictAsStarStarArg] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) class B: pass @@ -1551,11 +1600,11 @@ f4(**a) # E: Extra argument "y" from **args for "f4" f5(**a) # E: Missing positional arguments "y", "z" in call to "f5" f6(**a) # E: Extra argument "y" from **args for "f6" f1(1, **a) # E: "f1" gets multiple values for keyword argument "x" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarArgConstraints] -from typing import TypeVar, Union -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union T = TypeVar('T') S = TypeVar('S') @@ -1564,10 +1613,11 @@ def f1(x: T, y: S) -> Union[T, S]: ... A = TypedDict('A', {'y': int, 'x': str}) a: A reveal_type(f1(**a)) # N: Revealed type is "Union[builtins.str, builtins.int]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarArgCalleeKwargs] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': str, 'y': str}) @@ -1585,9 +1635,10 @@ g(1, **a) # E: "g" gets multiple values for keyword argument "x" g(1, **b) # E: "g" gets multiple values for keyword argument "x" \ # E: Argument "x" to "g" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarTwice] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'z': bytes}) @@ -1609,11 +1660,11 @@ f1(**a, **c) # E: "f1" gets multiple values for keyword argument "x" \ # E: Argument "x" to "f1" has incompatible type "str"; expected "int" f1(**c, **a) # E: "f1" gets multiple values for keyword argument "x" \ # E: Argument "x" to "f1" has incompatible type "str"; expected "int" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarAndDictAsStarStar] -from mypy_extensions import TypedDict -from typing import Any, Dict +from typing import Any, Dict, TypedDict TD = TypedDict('TD', {'x': int, 'y': str}) @@ -1628,10 +1679,10 @@ f1(**d, **td) f2(**td, **d) f2(**d, **td) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictNonMappingMethods] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}) a: A @@ -1661,10 +1712,10 @@ a.update({'z': 1, 'x': 1}) # E: Expected TypedDict key "x" but found keys ("z", d = {'x': 1} a.update(d) # E: Argument 1 to "update" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'x'?: int, 'y'?: List[int]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictPopMethod] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}, total=False) B = TypedDict('B', {'x': int}) @@ -1683,10 +1734,10 @@ pop = b.pop pop('x') # E: Argument 1 has incompatible type "str"; expected "Never" pop('invalid') # E: Argument 1 has incompatible type "str"; expected "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictDel] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}, total=False) B = TypedDict('B', {'x': int}) @@ -1703,10 +1754,10 @@ alias = b.__delitem__ alias('x') alias(s) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testPluginUnionsOfTypedDicts] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class TDA(TypedDict): a: int @@ -1731,8 +1782,7 @@ reveal_type(td['c']) # N: Revealed type is "Union[Any, builtins.int]" \ [typing fixtures/typing-typeddict.pyi] [case testPluginUnionsOfTypedDictsNonTotal] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class TDA(TypedDict, total=False): a: int @@ -1777,8 +1827,7 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtin [typing fixtures/typing-typeddict.pyi] [case testTypedDictOptionalUpdate] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class A(TypedDict): x: int @@ -1786,6 +1835,7 @@ class A(TypedDict): d: A d.update({'x': 1}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictOverlapWithDict] # mypy: strict-equality @@ -2249,8 +2299,7 @@ if foo[KEY_NAME] is not None: [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardClass] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict class Foo(TypedDict): bar: Bar @@ -2265,8 +2314,7 @@ reveal_type(foo['baz']) # N: Revealed type is "builtins.list[Any]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardFunc] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict Foo = TypedDict('Foo', {'bar': 'Bar', 'baz': 'Bar'}) @@ -2279,8 +2327,7 @@ reveal_type(foo['baz']) # N: Revealed type is "builtins.list[Any]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardMixed] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict Bar = List[Any] @@ -2357,11 +2404,12 @@ d[True] # E: TypedDict key must be a string literal; expected one of ("foo") [typing fixtures/typing-typeddict.pyi] [case testTypedDictUppercaseKey] -from mypy_extensions import TypedDict +from typing import TypedDict Foo = TypedDict('Foo', {'camelCaseKey': str}) value: Foo = {} # E: Missing key "camelCaseKey" for TypedDict "Foo" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithDeferredFieldTypeEval] from typing import Generic, TypeVar, TypedDict, NotRequired @@ -2896,7 +2944,7 @@ d[''] # E: TypedDict "A" has no key "" [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdate] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2911,7 +2959,7 @@ a.update(a) [case testTypedDictStrictUpdate] # flags: --extra-checks -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2925,8 +2973,7 @@ a.update(a) # OK [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdateUnion] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2939,8 +2986,7 @@ a.update(u) [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdateUnionExtra] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int, "extra": int}) @@ -2954,8 +3000,7 @@ a.update(u) [case testTypedDictFlexibleUpdateUnionStrict] # flags: --extra-checks -from typing import Union, NotRequired -from mypy_extensions import TypedDict +from typing import TypedDict, Union, NotRequired A = TypedDict("A", {"foo": int, "bar": int}) A1 = TypedDict("A1", {"foo": int, "bar": NotRequired[int]}) @@ -3139,7 +3184,7 @@ bar2: Bar = {**bar, "c": {**bar["c"], "b": "wrong"}, "d": 2} # E: Incompatible [typing fixtures/typing-typeddict.pyi] [case testTypedDictUnpackOverrideRequired] -from mypy_extensions import TypedDict +from typing import TypedDict Details = TypedDict('Details', {'first_name': str, 'last_name': str}) DetailsSubset = TypedDict('DetailsSubset', {'first_name': str, 'last_name': str}, total=False) @@ -3270,8 +3315,7 @@ f: Foo = {**foo("no")} # E: Argument 1 to "foo" has incompatible type "str"; ex [case testTypedDictWith__or__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3311,7 +3355,7 @@ bar | d2 # E: Unsupported operand types for | ("Bar" and "Dict[int, str]") [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__or__method_error] -from mypy_extensions import TypedDict +from typing import TypedDict class Foo(TypedDict): key: int @@ -3334,8 +3378,7 @@ main:10: note: def [T, T2] __ror__(self, Dict[T, T2], /) -> Dict[Union[Any, [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__ror__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3374,8 +3417,7 @@ d2 | bar # E: Unsupported operand types for | ("Dict[int, str]" and "Bar") [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__ior__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3471,7 +3513,7 @@ class TotalInTheMiddle(TypedDict, a=1, total=True, b=2, c=3): # E: Unexpected k [typing fixtures/typing-typeddict.pyi] [case testCanCreateClassWithFunctionBasedTypedDictBase] -from mypy_extensions import TypedDict +from typing import TypedDict class Params(TypedDict("Params", {'x': int})): pass @@ -3479,6 +3521,7 @@ class Params(TypedDict("Params", {'x': int})): p: Params = {'x': 2} reveal_type(p) # N: Revealed type is "TypedDict('__main__.Params', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testInitTypedDictFromType] from typing import TypedDict, Type @@ -3751,7 +3794,7 @@ x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated [typing fixtures/typing-typeddict.pyi] [case testTypedDictFromMypyExtensionsReadOnlyMutateMethods] -from mypy_extensions import TypedDict +from typing import TypedDict from typing_extensions import ReadOnly class TP(TypedDict): diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index 6992a5bdec00..7642e6d7a14c 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -818,7 +818,7 @@ class I: pass -> a [case testAliasDepsTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict from mod import I A = I class P(TypedDict): @@ -826,6 +826,7 @@ class P(TypedDict): [file mod.py] class I: pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m.P @@ -836,7 +837,7 @@ class I: pass [case testAliasDepsTypedDictFunctional] # __dump_all__ -from mypy_extensions import TypedDict +from typing import TypedDict import a P = TypedDict('P', {'x': a.A}) [file a.py] @@ -845,6 +846,7 @@ A = I [file mod.py] class I: pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m
-> m diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 6ba3f97a79df..2c231c9afff6 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -644,12 +644,13 @@ x = 1 -> m, pkg, pkg.mod [case testTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -657,13 +658,14 @@ def foo(x: Point) -> int: -> m [case testTypedDict2] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass Point = TypedDict('Point', {'x': int, 'y': A}) p = Point(dict(x=42, y=A())) def foo(x: Point) -> int: return x['x'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -674,7 +676,7 @@ def foo(x: Point) -> int: -> m [case testTypedDict3] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass class Point(TypedDict): x: int @@ -683,6 +685,7 @@ p = Point(dict(x=42, y=A())) def foo(x: Point) -> int: return x['x'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 4acf451e2c34..70178b0366ba 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -617,57 +617,61 @@ __main__.E __main__.F [case testTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': str}) p = Point(dict(x=42, y='lurr')) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: str p = Point(dict(x=42, y='lurr')) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict3] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int}) p = Point(dict(x=42)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict4] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}, total=False) p = Point(dict(x=42, y=1337)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index c988a2dc80aa..5b49aa6b3a02 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -3591,27 +3591,28 @@ c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypedDictRefresh] -[builtins fixtures/dict.pyi] import a [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) # dummy change +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == [case testTypedDictUpdate] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': str}) p = Point(dict(x=42, y='lurr')) [file b.py] @@ -3619,6 +3620,7 @@ from a import Point def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:3: error: Unsupported operand types for + ("int" and "str") @@ -3626,13 +3628,13 @@ b.py:3: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdate2] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: str @@ -3642,6 +3644,7 @@ from a import Point def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:3: error: Unsupported operand types for + ("int" and "str") @@ -3649,16 +3652,14 @@ b.py:3: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdate3] import b [file a.py] -from mypy_extensions import TypedDict -from typing import Optional +from typing import Optional, TypedDict class Point(TypedDict): x: Optional[Point] y: int z: int p = Point(dict(x=None, y=1337, z=0)) [file a.py.2] -from mypy_extensions import TypedDict -from typing import Optional +from typing import Optional, TypedDict class Point(TypedDict): x: Optional[Point] y: str @@ -3670,6 +3671,7 @@ def foo(x: Point) -> int: assert x['x'] is not None return x['x']['z'] + x['x']['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:4: error: Unsupported operand types for + ("int" and "str") @@ -3677,13 +3679,12 @@ b.py:4: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdateGeneric] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int [file a.py.2] -from mypy_extensions import TypedDict -from typing import Generic, TypeVar +from typing import Generic, TypedDict, TypeVar T = TypeVar("T") class Point(TypedDict, Generic[T]): @@ -3700,6 +3701,7 @@ def foo() -> None: p = Point(x=0, y="no") i: int = p["y"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == == diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index df703b239743..f841a9aae6e7 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -24,6 +24,7 @@ Final = 0 Literal = 0 TypedDict = 0 NoReturn = 0 +NewType = 0 Required = 0 NotRequired = 0 ReadOnly = 0 diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index a6a64c75b2a3..eea6fe505b49 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -1332,23 +1332,25 @@ MypyFile:1<1>( [case testMergeTypedDict_symtable] import target [file target.py] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'a': A}) d: D [file target.py.next] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'a': A, 'b': int}) d: D [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [out] __main__: target: MypyFile<0> target: A: TypeInfo<1> D: TypeInfo<2> - TypedDict: FuncDef<3> + TypedDict: Var<3> d: Var<4>(TypedDict('target.D', {'a': target.A<1>})) ==> __main__: @@ -1356,7 +1358,7 @@ __main__: target: A: TypeInfo<1> D: TypeInfo<2> - TypedDict: FuncDef<3> + TypedDict: Var<3> d: Var<4>(TypedDict('target.D', {'a': target.A<1>, 'b': builtins.int<5>})) [case testNewType_symtable] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 61cb69b2d281..48d6ee04b514 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1033,7 +1033,7 @@ _program.py:17: note: Revealed type is "builtins.str" [case testTypedDictGet] # Test that TypedDict get plugin works with typeshed stubs -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'x': int, 'y': str}) d: D @@ -1054,7 +1054,7 @@ _testTypedDictGet.py:9: note: def [_T] get(self, str, /, default: object) -> _testTypedDictGet.py:11: note: Revealed type is "builtins.object" [case testTypedDictMappingMethods] -from mypy_extensions import TypedDict +from typing import TypedDict Cell = TypedDict('Cell', {'value': int}) c = Cell(value=42) for x in c: @@ -1098,8 +1098,7 @@ def foo(mymap) -> Optional[MyNamedTuple]: [out] [case testCanConvertTypedDictToAnySuperclassOfMapping] -from mypy_extensions import TypedDict -from typing import Sized, Iterable, Container +from typing import Sized, TypedDict, Iterable, Container Point = TypedDict('Point', {'x': int, 'y': int}) @@ -1110,12 +1109,12 @@ c: Container[str] = p o: object = p it2: Iterable[int] = p [out] -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Following member(s) of "Point" have conflicts: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Expected: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: def __iter__(self) -> Iterator[int] -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Got: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: def __iter__(self) -> Iterator[str] +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Following member(s) of "Point" have conflicts: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Expected: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: def __iter__(self) -> Iterator[int] +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Got: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: def __iter__(self) -> Iterator[str] [case testAsyncioGatherPreciseType-xfail] # Mysteriously regressed in #11905 diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 6e0fdba8aaa3..82c3869bb855 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -306,10 +306,7 @@ Total 1 11 90.91% [file i.py] from enum import Enum -from mypy_extensions import TypedDict -from typing import NewType, NamedTuple, TypeVar - -from typing import TypeVar +from typing import NewType, NamedTuple, TypedDict, TypeVar T = TypeVar('T') # no error diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 5e7da27f17cb..52c658c97c3b 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1411,12 +1411,13 @@ class N: # E: Name "N" already defined on line 2 [out] [case testDuplicateDefTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) class Point: # E: Name "Point" already defined on line 2 pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] diff --git a/test-data/unit/semanal-typeddict.test b/test-data/unit/semanal-typeddict.test index 9ce89155c308..936ed1aed3ee 100644 --- a/test-data/unit/semanal-typeddict.test +++ b/test-data/unit/semanal-typeddict.test @@ -2,40 +2,43 @@ -- TODO: Implement support for this syntax. --[case testCanCreateTypedDictTypeWithDictCall] ---from mypy_extensions import TypedDict +--from typing import TypedDict --Point = TypedDict('Point', dict(x=int, y=int)) --[builtins fixtures/dict.pyi] +--[typing fixtures/typing-typeddict.pyi] --[out] --MypyFile:1( --- ImportFrom:1(mypy_extensions, [TypedDict]) +-- ImportFrom:1(typing, [TypedDict]) -- AssignmentStmt:2( -- NameExpr(Point* [__main__.Point]) -- TypedDictExpr:2(Point))) [case testCanCreateTypedDictTypeWithDictLiteral] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] MypyFile:1( - ImportFrom:1(mypy_extensions, [TypedDict]) + ImportFrom:1(typing, [TypedDict]) AssignmentStmt:2( NameExpr(Point* [__main__.Point]) TypedDictExpr:2(Point))) [case testTypedDictWithDocString] -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): """foo""" x: str [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] MypyFile:1( - ImportFrom:1(mypy_extensions, [TypedDict]) + ImportFrom:1(typing, [TypedDict]) ClassDef:2( A BaseType( - mypy_extensions._TypedDict) + typing._TypedDict) ExpressionStmt:3( StrExpr(foo)) AssignmentStmt:4( From 48d888abd9f4aa06a5f893af20f543420dd672e4 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:06:08 -0800 Subject: [PATCH 1071/1617] Disallow inline config of Python version (#18497) Fixes #18450 --- mypy/config_parser.py | 5 +++++ test-data/unit/check-inline-config.test | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 4161f7e04dd3..c68efe9e44ef 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -647,6 +647,11 @@ def parse_mypy_comments( # method is to create a config parser. parser = configparser.RawConfigParser() options, parse_errors = mypy_comments_to_config_map(line, template) + + if "python_version" in options: + errors.append((lineno, "python_version not supported in inline configuration")) + del options["python_version"] + parser["dummy"] = options errors.extend((lineno, x) for x in parse_errors) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index bedba811d95b..c81dcac94afd 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -323,3 +323,7 @@ class Foo: foo = Foo() if foo: ... 42 + "no" # type: ignore + + +[case testInlinePythonVersion] +# mypy: python-version=3.10 # E: python_version not supported in inline configuration From ebafbcefaae2e7cd1f9b2eb6fd294f8fdf4fa484 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:06:25 +0100 Subject: [PATCH 1072/1617] Use new Github actions ARM runners for tests (#18483) https://github.blog/changelog/2025-01-16-linux-arm64-hosted-runners-now-available-for-free-in-public-repositories-public-preview/ --- .github/workflows/test.yml | 25 ++++++------------------- .pre-commit-config.yaml | 2 +- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a57d08fa4da8..c42550431bb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,49 +33,42 @@ jobs: # the oldest and newest supported Python versions - name: Test suite with py39-ubuntu, mypyc-compiled python: '3.9' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py39-windows-64 python: '3.9' - arch: x64 os: windows-latest toxenv: py39 tox_extra_args: "-n 4" - name: Test suite with py310-ubuntu python: '3.10' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" - name: Test suite with py311-ubuntu, mypyc-compiled python: '3.11' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled python: '3.12' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py313-ubuntu, mypyc-compiled python: '3.13' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true # - name: Test suite with py314-dev-ubuntu # python: '3.14-dev' - # arch: x64 - # os: ubuntu-latest + # os: ubuntu-24.04-arm # toxenv: py # tox_extra_args: "-n 4" # allow_failure: true @@ -83,7 +76,6 @@ jobs: - name: mypyc runtime tests with py39-macos python: '3.9.21' - arch: x64 # TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version os: macos-13 toxenv: py @@ -93,7 +85,6 @@ jobs: # - https://github.com/python/mypy/pull/17822 # - name: mypyc runtime tests with py38-debug-build-ubuntu # python: '3.9.21' - # arch: x64 # os: ubuntu-latest # toxenv: py # tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" @@ -101,12 +92,10 @@ jobs: - name: Type check our own code (py39-ubuntu) python: '3.9' - arch: x64 os: ubuntu-latest toxenv: type - name: Type check our own code (py39-windows-64) python: '3.9' - arch: x64 os: windows-latest toxenv: type @@ -115,7 +104,6 @@ jobs: # to ensure the tox env works as expected - name: Formatting and code style with Black + ruff python: '3.10' - arch: x64 os: ubuntu-latest toxenv: lint @@ -169,7 +157,6 @@ jobs: if: ${{ !(matrix.debug_build || endsWith(matrix.python, '-dev')) }} with: python-version: ${{ matrix.python }} - architecture: ${{ matrix.arch }} - name: Install tox run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc411c6da49b..59bd490987d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - id: check-github-actions - id: check-readthedocs - repo: https://github.com/rhysd/actionlint - rev: v1.7.6 + rev: v1.7.7 hooks: - id: actionlint args: [ From 3ced11a43e75dd97554d5c7af78da296ee0d04ec Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:07:06 +0100 Subject: [PATCH 1073/1617] [stubgen] Include simple decorators in stub files (#18489) Stubgen historically only included a selected number of decorators in the generated stubs. I couldn't find the actual reason for it, however it's likely fair to assume that decorator typing only started being possible with PEP 612 thus most had been untyped previously. As it's fairly simple to annotate decorators with `ParamSpec` now, it's probably fair to include them in the stub file now. --- mypy/stubgen.py | 3 +++ test-data/unit/stubgen.test | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 1f8a1a4740f1..86f9a108f1d6 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -755,6 +755,9 @@ def process_decorator(self, o: Decorator) -> None: elif fullname in DATACLASS_TRANSFORM_NAMES: p = AliasPrinter(self) self._decorators.append(f"@{decorator.accept(p)}") + elif isinstance(decorator, (NameExpr, MemberExpr)): + p = AliasPrinter(self) + self._decorators.append(f"@{decorator.accept(p)}") def get_fullname(self, expr: Expression) -> str: """Return the expression's full name.""" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 7700f04c6797..5c0d2d6f8e00 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -338,10 +338,24 @@ class A: ... class B(A): ... [case testDecoratedFunction] +import x + @decorator def foo(x): ... + +@x.decorator +def bar(x): ... + +@decorator(x=1, y={"a": 1}) +def foo_bar(x): ... [out] +import x + +@decorator def foo(x) -> None: ... +@x.decorator +def bar(x) -> None: ... +def foo_bar(x) -> None: ... [case testMultipleAssignment] x, y = 1, 2 From 1eb9d4ce36144d9e2e7c79dc35d517b4e1963dd7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 25 Jan 2025 20:18:49 +0000 Subject: [PATCH 1074/1617] Support properties with setter type different from getter type (#18510) Fixes https://github.com/python/mypy/issues/3004 Fixes https://github.com/python/mypy/issues/11892 Fixes https://github.com/python/mypy/issues/12892 Fixes https://github.com/python/mypy/issues/14301 _Note:_ this PR should be reviewed with "hide whitespace" option (in couple long functions I replace huge `if x: ...` with `if not x: return; ...` to reduce indent level). The core logic is quite straightforward (almost trivial). The only couple things are: * We should be careful with binder (we simpy can't use it for properties with setter type different from getter type, since we don't know underlying property implementation) * We need to handle gracefully existing settable properties that are generated by plugins The tricky part is subclassing and protocols. The summary is as following: * For protocols I simply implement everything the "correct way", i.e. for settable attributes (whether it is a variable or a settable property) compare getter types covariantly and setter types contravariantly. The tricky part here is generating meaningful error messages that are also not too verbose. * For subclassing I cannot simply do the same, because there is a flag about covariant mutable override, that is off by default. So instead what I do is if either subclass node, or superclass node is a "custom property" (i.e. a property with setter type different from getter type), then I use the "correct way", otherwise the old logic (i.e. flag dependent check) is used. Two things that are not implemented are multiple inheritance, and new generic syntax (inferred variance). In these cases setter types are simply ignored. There is nothing conceptually difficult about these, I simply run out of steam (and the PR is getting big). I left `TODO`s in code for these. In most cases these will generate false negatives (and they are already kind of corner cases) so I think it is OK to postpone these indefinitely. --- mypy/checker.py | 550 +++++++++++++++++--------- mypy/checkmember.py | 18 +- mypy/errors.py | 7 +- mypy/fixup.py | 2 + mypy/messages.py | 72 +++- mypy/nodes.py | 15 + mypy/server/astdiff.py | 6 + mypy/server/astmerge.py | 1 + mypy/subtypes.py | 72 +++- mypy/typeops.py | 15 +- test-data/unit/check-classes.test | 258 +++++++++++- test-data/unit/check-incremental.test | 26 ++ test-data/unit/check-protocols.test | 216 ++++++++++ test-data/unit/fine-grained.test | 50 +++ test-data/unit/fixtures/property.pyi | 2 +- 15 files changed, 1077 insertions(+), 233 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7b0b88186f76..3734f3170790 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7,7 +7,7 @@ from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet from contextlib import ExitStack, contextmanager from typing import Callable, Final, Generic, NamedTuple, Optional, TypeVar, Union, cast, overload -from typing_extensions import TypeAlias as _TypeAlias +from typing_extensions import TypeAlias as _TypeAlias, TypeGuard import mypy.checkexpr from mypy import errorcodes as codes, join, message_registry, nodes, operators @@ -647,6 +647,20 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # HACK: Infer the type of the property. assert isinstance(defn.items[0], Decorator) self.visit_decorator(defn.items[0]) + if defn.items[0].var.is_settable_property: + assert isinstance(defn.items[1], Decorator) + self.visit_func_def(defn.items[1].func) + setter_type = self.function_type(defn.items[1].func) + assert isinstance(setter_type, CallableType) + if len(setter_type.arg_types) != 2: + self.fail("Invalid property setter signature", defn.items[1].func) + any_type = AnyType(TypeOfAny.from_error) + setter_type = setter_type.copy_modified( + arg_types=[any_type, any_type], + arg_kinds=[ARG_POS, ARG_POS], + arg_names=[None, None], + ) + defn.items[0].var.setter_type = setter_type for fdef in defn.items: assert isinstance(fdef, Decorator) if defn.is_property: @@ -2042,6 +2056,44 @@ def check_method_or_accessor_override_for_base( return None return found_base_method + def check_setter_type_override( + self, defn: OverloadedFuncDef, base_attr: SymbolTableNode, base: TypeInfo + ) -> None: + """Check override of a setter type of a mutable attribute. + + Currently, this should be only called when either base node or the current node + is a custom settable property (i.e. where setter type is different from getter type). + Note that this check is contravariant. + """ + base_node = base_attr.node + assert isinstance(base_node, (OverloadedFuncDef, Var)) + original_type, is_original_setter = get_raw_setter_type(base_node) + if isinstance(base_node, Var): + expanded_type = map_type_from_supertype(original_type, defn.info, base) + original_type = get_proper_type( + expand_self_type(base_node, expanded_type, fill_typevars(defn.info)) + ) + else: + assert isinstance(original_type, ProperType) + assert isinstance(original_type, CallableType) + original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base) + assert isinstance(original_type, CallableType) + if is_original_setter: + original_type = original_type.arg_types[0] + else: + original_type = original_type.ret_type + + typ, is_setter = get_raw_setter_type(defn) + assert isinstance(typ, ProperType) and isinstance(typ, CallableType) + typ = bind_self(typ, self.scope.active_self_type()) + if is_setter: + typ = typ.arg_types[0] + else: + typ = typ.ret_type + + if not is_subtype(original_type, typ): + self.msg.incompatible_setter_override(defn.items[1], typ, original_type, base) + def check_method_override_for_base_with_name( self, defn: FuncDef | OverloadedFuncDef | Decorator, name: str, base: TypeInfo ) -> bool: @@ -2050,169 +2102,179 @@ def check_method_override_for_base_with_name( Return True if the supertype node was not analysed yet, and `defn` was deferred. """ base_attr = base.names.get(name) - if base_attr: - # The name of the method is defined in the base class. + if not base_attr: + return False + # The name of the method is defined in the base class. - # Point errors at the 'def' line (important for backward compatibility - # of type ignores). - if not isinstance(defn, Decorator): - context = defn - else: - context = defn.func - - # Construct the type of the overriding method. - # TODO: this logic is much less complete than similar one in checkmember.py - if isinstance(defn, (FuncDef, OverloadedFuncDef)): - typ: Type = self.function_type(defn) - override_class_or_static = defn.is_class or defn.is_static - override_class = defn.is_class - else: - assert defn.var.is_ready - assert defn.var.type is not None - typ = defn.var.type - override_class_or_static = defn.func.is_class or defn.func.is_static - override_class = defn.func.is_class - typ = get_proper_type(typ) - if isinstance(typ, FunctionLike) and not is_static(context): - typ = bind_self(typ, self.scope.active_self_type(), is_classmethod=override_class) - # Map the overridden method type to subtype context so that - # it can be checked for compatibility. - original_type = get_proper_type(base_attr.type) - original_node = base_attr.node - # `original_type` can be partial if (e.g.) it is originally an - # instance variable from an `__init__` block that becomes deferred. - if original_type is None or isinstance(original_type, PartialType): - if self.pass_num < self.last_pass: - # If there are passes left, defer this node until next pass, - # otherwise try reconstructing the method type from available information. - self.defer_node(defn, defn.info) - return True - elif isinstance(original_node, (FuncDef, OverloadedFuncDef)): - original_type = self.function_type(original_node) - elif isinstance(original_node, Decorator): - original_type = self.function_type(original_node.func) - elif isinstance(original_node, Var): - # Super type can define method as an attribute. - # See https://github.com/python/mypy/issues/10134 - - # We also check that sometimes `original_node.type` is None. - # This is the case when we use something like `__hash__ = None`. - if original_node.type is not None: - original_type = get_proper_type(original_node.type) - else: - original_type = NoneType() + # Point errors at the 'def' line (important for backward compatibility + # of type ignores). + if not isinstance(defn, Decorator): + context = defn + else: + context = defn.func + + # Construct the type of the overriding method. + # TODO: this logic is much less complete than similar one in checkmember.py + if isinstance(defn, (FuncDef, OverloadedFuncDef)): + typ: Type = self.function_type(defn) + override_class_or_static = defn.is_class or defn.is_static + override_class = defn.is_class + else: + assert defn.var.is_ready + assert defn.var.type is not None + typ = defn.var.type + override_class_or_static = defn.func.is_class or defn.func.is_static + override_class = defn.func.is_class + typ = get_proper_type(typ) + if isinstance(typ, FunctionLike) and not is_static(context): + typ = bind_self(typ, self.scope.active_self_type(), is_classmethod=override_class) + # Map the overridden method type to subtype context so that + # it can be checked for compatibility. + original_type = get_proper_type(base_attr.type) + original_node = base_attr.node + always_allow_covariant = False + if is_settable_property(defn) and ( + is_settable_property(original_node) or isinstance(original_node, Var) + ): + if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)): + always_allow_covariant = True + self.check_setter_type_override(defn, base_attr, base) + # `original_type` can be partial if (e.g.) it is originally an + # instance variable from an `__init__` block that becomes deferred. + if original_type is None or isinstance(original_type, PartialType): + if self.pass_num < self.last_pass: + # If there are passes left, defer this node until next pass, + # otherwise try reconstructing the method type from available information. + self.defer_node(defn, defn.info) + return True + elif isinstance(original_node, (FuncDef, OverloadedFuncDef)): + original_type = self.function_type(original_node) + elif isinstance(original_node, Decorator): + original_type = self.function_type(original_node.func) + elif isinstance(original_node, Var): + # Super type can define method as an attribute. + # See https://github.com/python/mypy/issues/10134 + + # We also check that sometimes `original_node.type` is None. + # This is the case when we use something like `__hash__ = None`. + if original_node.type is not None: + original_type = get_proper_type(original_node.type) else: - # Will always fail to typecheck below, since we know the node is a method original_type = NoneType() - if isinstance(original_node, (FuncDef, OverloadedFuncDef)): - original_class_or_static = original_node.is_class or original_node.is_static - elif isinstance(original_node, Decorator): - fdef = original_node.func - original_class_or_static = fdef.is_class or fdef.is_static else: - original_class_or_static = False # a variable can't be class or static - - if isinstance(original_type, FunctionLike): - original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base) - if original_node and is_property(original_node): - original_type = get_property_type(original_type) + # Will always fail to typecheck below, since we know the node is a method + original_type = NoneType() + if isinstance(original_node, (FuncDef, OverloadedFuncDef)): + original_class_or_static = original_node.is_class or original_node.is_static + elif isinstance(original_node, Decorator): + fdef = original_node.func + original_class_or_static = fdef.is_class or fdef.is_static + else: + original_class_or_static = False # a variable can't be class or static - if isinstance(original_node, Var): - expanded_type = map_type_from_supertype(original_type, defn.info, base) - expanded_type = expand_self_type( - original_node, expanded_type, fill_typevars(defn.info) - ) - original_type = get_proper_type(expanded_type) + if isinstance(original_type, FunctionLike): + original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base) + if original_node and is_property(original_node): + original_type = get_property_type(original_type) - if is_property(defn): - inner: FunctionLike | None - if isinstance(typ, FunctionLike): - inner = typ - else: - inner = self.extract_callable_type(typ, context) - if inner is not None: - typ = inner - typ = get_property_type(typ) - if ( - isinstance(original_node, Var) - and not original_node.is_final - and (not original_node.is_property or original_node.is_settable_property) - and isinstance(defn, Decorator) - ): - # We only give an error where no other similar errors will be given. - if not isinstance(original_type, AnyType): - self.msg.fail( - "Cannot override writeable attribute with read-only property", - # Give an error on function line to match old behaviour. - defn.func, - code=codes.OVERRIDE, - ) + if isinstance(original_node, Var): + expanded_type = map_type_from_supertype(original_type, defn.info, base) + expanded_type = expand_self_type( + original_node, expanded_type, fill_typevars(defn.info) + ) + original_type = get_proper_type(expanded_type) - if isinstance(original_type, AnyType) or isinstance(typ, AnyType): - pass - elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): - # Check that the types are compatible. - ok = self.check_override( - typ, - original_type, - defn.name, - name, - base.name, - original_class_or_static, - override_class_or_static, - context, - ) - # Check if this override is covariant. + if is_property(defn): + inner: FunctionLike | None + if isinstance(typ, FunctionLike): + inner = typ + else: + inner = self.extract_callable_type(typ, context) + if inner is not None: + typ = inner + typ = get_property_type(typ) if ( - ok - and original_node - and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes - and self.is_writable_attribute(original_node) - and not is_subtype(original_type, typ, ignore_pos_arg_names=True) + isinstance(original_node, Var) + and not original_node.is_final + and (not original_node.is_property or original_node.is_settable_property) + and isinstance(defn, Decorator) ): - base_str, override_str = format_type_distinctly( - original_type, typ, options=self.options - ) - msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( - f' (base class "{base.name}" defined the type as {base_str},' - f" override has type {override_str})" - ) - self.fail(msg, context) - elif isinstance(original_type, UnionType) and any( - is_subtype(typ, orig_typ, ignore_pos_arg_names=True) - for orig_typ in original_type.items + # We only give an error where no other similar errors will be given. + if not isinstance(original_type, AnyType): + self.msg.fail( + "Cannot override writeable attribute with read-only property", + # Give an error on function line to match old behaviour. + defn.func, + code=codes.OVERRIDE, + ) + + if isinstance(original_type, AnyType) or isinstance(typ, AnyType): + pass + elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): + # Check that the types are compatible. + ok = self.check_override( + typ, + original_type, + defn.name, + name, + base.name, + original_class_or_static, + override_class_or_static, + context, + ) + # Check if this override is covariant. + if ( + ok + and original_node + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + and not always_allow_covariant + and not is_subtype(original_type, typ, ignore_pos_arg_names=True) ): - # This method is a subtype of at least one union variant. - if ( - original_node - and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes - and self.is_writable_attribute(original_node) - ): - # Covariant override of mutable attribute. - base_str, override_str = format_type_distinctly( - original_type, typ, options=self.options - ) - msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( - f' (base class "{base.name}" defined the type as {base_str},' - f" override has type {override_str})" - ) - self.fail(msg, context) - elif is_equivalent(original_type, typ): - # Assume invariance for a non-callable attribute here. Note - # that this doesn't affect read-only properties which can have - # covariant overrides. - pass - elif ( + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options + ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) + elif isinstance(original_type, UnionType) and any( + is_subtype(typ, orig_typ, ignore_pos_arg_names=True) + for orig_typ in original_type.items + ): + # This method is a subtype of at least one union variant. + if ( original_node - and not self.is_writable_attribute(original_node) - and is_subtype(typ, original_type) + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + and not always_allow_covariant ): - # If the attribute is read-only, allow covariance - pass - else: - self.msg.signature_incompatible_with_supertype( - defn.name, name, base.name, context, original=original_type, override=typ + # Covariant override of mutable attribute. + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) + elif is_equivalent(original_type, typ): + # Assume invariance for a non-callable attribute here. Note + # that this doesn't affect read-only properties which can have + # covariant overrides. + pass + elif ( + original_node + and (not self.is_writable_attribute(original_node) or always_allow_covariant) + and is_subtype(typ, original_type) + ): + # If the attribute is read-only, allow covariance + pass + else: + self.msg.signature_incompatible_with_supertype( + defn.name, name, base.name, context, original=original_type, override=typ + ) return False def bind_and_map_method( @@ -2833,6 +2895,7 @@ class C(B, A[int]): ... # this is unsafe because... # TODO: use more principled logic to decide is_subtype() vs is_equivalent(). # We should rely on mutability of superclass node, not on types being Callable. + # (in particular handle settable properties with setter type different from getter). # start with the special case that Instance can be a subtype of FunctionLike call = None @@ -3165,7 +3228,7 @@ def check_assignment( ): # Ignore member access to modules instance_type = self.expr_checker.accept(lvalue.expr) rvalue_type, lvalue_type, infer_lvalue_type = self.check_member_assignment( - instance_type, lvalue_type, rvalue, context=rvalue + lvalue, instance_type, lvalue_type, rvalue, context=rvalue ) else: # Hacky special case for assigning a literal None @@ -3353,17 +3416,36 @@ def check_compatibility_all_supers( continue base_type, base_node = self.lvalue_type_from_base(lvalue_node, base) + custom_setter = is_custom_settable_property(base_node) if isinstance(base_type, PartialType): base_type = None if base_type: assert base_node is not None if not self.check_compatibility_super( - lvalue, lvalue_type, rvalue, base, base_type, base_node + lvalue, + lvalue_type, + rvalue, + base, + base_type, + base_node, + always_allow_covariant=custom_setter, ): # Only show one error per variable; even if other # base classes are also incompatible return True + if lvalue_type and custom_setter: + base_type, _ = self.lvalue_type_from_base( + lvalue_node, base, setter_type=True + ) + # Setter type for a custom property must be ready if + # the getter type is ready. + assert base_type is not None + if not is_subtype(base_type, lvalue_type): + self.msg.incompatible_setter_override( + lvalue, lvalue_type, base_type, base + ) + return True if base is last_immediate_base: # At this point, the attribute was found to be compatible with all # immediate parents. @@ -3378,6 +3460,7 @@ def check_compatibility_super( base: TypeInfo, base_type: Type, base_node: Node, + always_allow_covariant: bool, ) -> bool: lvalue_node = lvalue.node assert isinstance(lvalue_node, Var) @@ -3437,6 +3520,7 @@ def check_compatibility_super( ok and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes and self.is_writable_attribute(base_node) + and not always_allow_covariant ): ok = self.check_subtype( base_type, @@ -3450,49 +3534,62 @@ def check_compatibility_super( return True def lvalue_type_from_base( - self, expr_node: Var, base: TypeInfo - ) -> tuple[Type | None, Node | None]: - """For a NameExpr that is part of a class, walk all base classes and try - to find the first class that defines a Type for the same name.""" + self, expr_node: Var, base: TypeInfo, setter_type: bool = False + ) -> tuple[Type | None, SymbolNode | None]: + """Find a type for a variable name in base class. + + Return the type found and the corresponding node defining the name or None + for both if the name is not defined in base or the node type is not known (yet). + The type returned is already properly mapped/bound to the subclass. + If setter_type is True, return setter types for settable properties (otherwise the + getter type is returned). + """ expr_name = expr_node.name base_var = base.names.get(expr_name) - if base_var: - base_node = base_var.node - base_type = base_var.type - if isinstance(base_node, Var) and base_type is not None: - base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info)) - if isinstance(base_node, Decorator): - base_node = base_node.func - base_type = base_node.type - - if base_type: - if not has_no_typevars(base_type): - self_type = self.scope.active_self_type() - assert self_type is not None, "Internal error: base lookup outside class" - if isinstance(self_type, TupleType): - instance = tuple_fallback(self_type) - else: - instance = self_type - itype = map_instance_to_supertype(instance, base) - base_type = expand_type_by_instance(base_type, itype) - - base_type = get_proper_type(base_type) - if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef): - # If we are a property, return the Type of the return - # value, not the Callable - if base_node.is_property: - base_type = get_proper_type(base_type.ret_type) - if isinstance(base_type, FunctionLike) and isinstance( - base_node, OverloadedFuncDef - ): - # Same for properties with setter - if base_node.is_property: - base_type = base_type.items[0].ret_type + if not base_var: + return None, None + base_node = base_var.node + base_type = base_var.type + if isinstance(base_node, Var) and base_type is not None: + base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info)) + if isinstance(base_node, Decorator): + base_node = base_node.func + base_type = base_node.type + + if not base_type: + return None, None + if not has_no_typevars(base_type): + self_type = self.scope.active_self_type() + assert self_type is not None, "Internal error: base lookup outside class" + if isinstance(self_type, TupleType): + instance = tuple_fallback(self_type) + else: + instance = self_type + itype = map_instance_to_supertype(instance, base) + base_type = expand_type_by_instance(base_type, itype) - return base_type, base_node + base_type = get_proper_type(base_type) + if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef): + # If we are a property, return the Type of the return + # value, not the Callable + if base_node.is_property: + base_type = get_proper_type(base_type.ret_type) + if isinstance(base_type, FunctionLike) and isinstance(base_node, OverloadedFuncDef): + # Same for properties with setter + if base_node.is_property: + if setter_type: + assert isinstance(base_node.items[0], Decorator) + base_type = base_node.items[0].var.setter_type + # This flag is True only for custom properties, so it is safe to assert. + assert base_type is not None + base_type = self.bind_and_map_method(base_var, base_type, expr_node.info, base) + assert isinstance(base_type, CallableType) + base_type = get_proper_type(base_type.arg_types[0]) + else: + base_type = base_type.items[0].ret_type - return None, None + return base_type, base_node def check_compatibility_classvar_super( self, node: Var, base: TypeInfo, base_node: Node | None @@ -4411,7 +4508,12 @@ def check_simple_assignment( return rvalue_type def check_member_assignment( - self, instance_type: Type, attribute_type: Type, rvalue: Expression, context: Context + self, + lvalue: MemberExpr, + instance_type: Type, + attribute_type: Type, + rvalue: Expression, + context: Context, ) -> tuple[Type, Type, bool]: """Type member assignment. @@ -4433,10 +4535,16 @@ def check_member_assignment( rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) return rvalue_type, attribute_type, True + with self.msg.filter_errors(filter_deprecated=True): + get_lvalue_type = self.expr_checker.analyze_ordinary_member_access( + lvalue, is_lvalue=False + ) + use_binder = is_same_type(get_lvalue_type, attribute_type) + if not isinstance(attribute_type, Instance): # TODO: support __set__() for union types. rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) - return rvalue_type, attribute_type, True + return rvalue_type, attribute_type, use_binder mx = MemberContext( is_lvalue=False, @@ -4455,7 +4563,7 @@ def check_member_assignment( # (which allow you to override the descriptor with any value), but preserves # the type of accessing the attribute (even after the override). rvalue_type = self.check_simple_assignment(get_type, rvalue, context) - return rvalue_type, get_type, True + return rvalue_type, get_type, use_binder dunder_set = attribute_type.type.get_method("__set__") if dunder_set is None: @@ -8701,6 +8809,60 @@ def is_property(defn: SymbolNode) -> bool: return False +def is_settable_property(defn: SymbolNode | None) -> TypeGuard[OverloadedFuncDef]: + if isinstance(defn, OverloadedFuncDef): + if defn.items and isinstance(defn.items[0], Decorator): + return defn.items[0].func.is_property + return False + + +def is_custom_settable_property(defn: SymbolNode | None) -> bool: + """Check if a node is a settable property with a non-trivial setter type. + + By non-trivial here we mean that it is known (i.e. definition was already type + checked), it is not Any, and it is different from the property getter type. + """ + if defn is None: + return False + if not is_settable_property(defn): + return False + first_item = defn.items[0] + assert isinstance(first_item, Decorator) + if not first_item.var.is_settable_property: + return False + var = first_item.var + if var.type is None or var.setter_type is None or isinstance(var.type, PartialType): + # The caller should defer in case of partial types or not ready variables. + return False + setter_type = var.setter_type.arg_types[1] + if isinstance(get_proper_type(setter_type), AnyType): + return False + return not is_same_type(get_property_type(get_proper_type(var.type)), setter_type) + + +def get_raw_setter_type(defn: OverloadedFuncDef | Var) -> tuple[Type, bool]: + """Get an effective original setter type for a node. + + For a variable it is simply its type. For a property it is the type + of the setter method (if not None), or the getter method (used as fallback + for the plugin generated properties). + Return the type and a flag indicating that we didn't fall back to getter. + """ + if isinstance(defn, Var): + # This function should not be called if the var is not ready. + assert defn.type is not None + return defn.type, True + first_item = defn.items[0] + assert isinstance(first_item, Decorator) + var = first_item.var + # This function may be called on non-custom properties, so we need + # to handle the situation when it is synthetic (plugin generated). + if var.setter_type is not None: + return var.setter_type, True + assert var.type is not None + return var.type, False + + def get_property_type(t: ProperType) -> ProperType: if isinstance(t, CallableType): return get_proper_type(t.ret_type) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 19ebe07b1032..f6b5e6be2c53 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -658,7 +658,10 @@ def analyze_descriptor_access( 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, assignment=assignment) + for typ in descriptor_type.items + ] ) elif not isinstance(descriptor_type, Instance): return orig_descriptor_type @@ -776,7 +779,13 @@ def analyze_var( # Found a member variable. original_itype = itype itype = map_instance_to_supertype(itype, var.info) - typ = var.type + if var.is_settable_property and mx.is_lvalue: + typ: Type | None = var.setter_type + if typ is None and var.is_ready: + # Existing synthetic properties may not set setter type. Fall back to getter. + typ = var.type + else: + typ = var.type if typ: if isinstance(typ, PartialType): return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context) @@ -834,7 +843,10 @@ def analyze_var( if var.is_property: # A property cannot have an overloaded type => the cast is fine. assert isinstance(expanded_signature, CallableType) - result = expanded_signature.ret_type + if var.is_settable_property and mx.is_lvalue and var.setter_type is not None: + result = expanded_signature.arg_types[0] + else: + result = expanded_signature.ret_type else: result = expanded_signature else: diff --git a/mypy/errors.py b/mypy/errors.py index 2dd5af96eeef..f720cb04b16c 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -39,7 +39,7 @@ codes.OVERRIDE, } -allowed_duplicates: Final = ["@overload", "Got:", "Expected:"] +allowed_duplicates: Final = ["@overload", "Got:", "Expected:", "Expected setter type:"] BASE_RTD_URL: Final = "https://mypy.rtfd.io/en/stable/_refs.html#code" @@ -172,10 +172,12 @@ def __init__( *, filter_errors: bool | Callable[[str, ErrorInfo], bool] = False, save_filtered_errors: bool = False, + filter_deprecated: bool = False, ) -> None: self.errors = errors self._has_new_errors = False self._filter = filter_errors + self._filter_deprecated = filter_deprecated self._filtered: list[ErrorInfo] | None = [] if save_filtered_errors else None def __enter__(self) -> ErrorWatcher: @@ -196,7 +198,8 @@ def on_error(self, file: str, info: ErrorInfo) -> bool: ErrorWatcher further down the stack and from being recorded by Errors """ if info.code == codes.DEPRECATED: - return False + # Deprecated is not a type error, so it is handled on opt-in basis here. + return self._filter_deprecated self._has_new_errors = True if isinstance(self._filter, bool): diff --git a/mypy/fixup.py b/mypy/fixup.py index 1117b5a9ced3..8e7cd40544bf 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -210,6 +210,8 @@ def visit_var(self, v: Var) -> None: v.info = self.current_info if v.type is not None: v.type.accept(self.type_fixer) + if v.setter_type is not None: + v.setter_type.accept(self.type_fixer) def visit_type_alias(self, a: TypeAlias) -> None: a.target.accept(self.type_fixer) diff --git a/mypy/messages.py b/mypy/messages.py index 8e614f02277a..c5245daabaa5 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -56,6 +56,7 @@ from mypy.subtypes import ( IS_CLASS_OR_STATIC, IS_CLASSVAR, + IS_EXPLICIT_SETTER, IS_SETTABLE, IS_VAR, find_member, @@ -186,9 +187,13 @@ def filter_errors( *, filter_errors: bool | Callable[[str, ErrorInfo], bool] = True, save_filtered_errors: bool = False, + filter_deprecated: bool = False, ) -> ErrorWatcher: return ErrorWatcher( - self.errors, filter_errors=filter_errors, save_filtered_errors=save_filtered_errors + self.errors, + filter_errors=filter_errors, + save_filtered_errors=save_filtered_errors, + filter_deprecated=filter_deprecated, ) def add_errors(self, errors: list[ErrorInfo]) -> None: @@ -1164,6 +1169,20 @@ def overload_signature_incompatible_with_supertype( note_template = 'Overload variants must be defined in the same order as they are in "{}"' self.note(note_template.format(supertype), context, code=codes.OVERRIDE) + def incompatible_setter_override( + self, defn: Context, typ: Type, original_type: Type, base: TypeInfo + ) -> None: + self.fail("Incompatible override of a setter type", defn, code=codes.OVERRIDE) + base_str, override_str = format_type_distinctly(original_type, typ, options=self.options) + self.note( + f' (base class "{base.name}" defined the type as {base_str},', + defn, + code=codes.OVERRIDE, + ) + self.note(f" override has type {override_str})", defn, code=codes.OVERRIDE) + if is_subtype(typ, original_type): + self.note(" Setter types should behave contravariantly", defn, code=codes.OVERRIDE) + def signature_incompatible_with_supertype( self, name: str, @@ -2201,22 +2220,34 @@ def report_protocol_problems( ): 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]: + for name, got, exp, is_lvalue in conflict_types[:MAX_ITEMS]: exp = get_proper_type(exp) got = get_proper_type(got) + setter_suffix = " setter type" if is_lvalue else "" if not isinstance(exp, (CallableType, Overloaded)) or not isinstance( got, (CallableType, Overloaded) ): self.note( - "{}: expected {}, got {}".format( - name, *format_type_distinctly(exp, got, options=self.options) + "{}: expected{} {}, got {}".format( + name, + setter_suffix, + *format_type_distinctly(exp, got, options=self.options), ), context, offset=OFFSET, code=code, ) + if is_lvalue and is_subtype(got, exp, options=self.options): + self.note( + "Setter types should behave contravariantly", + context, + offset=OFFSET, + code=code, + ) else: - self.note("Expected:", context, offset=OFFSET, code=code) + self.note( + "Expected{}:".format(setter_suffix), context, offset=OFFSET, code=code + ) if isinstance(exp, CallableType): self.note( pretty_callable(exp, self.options, skip_self=class_obj or is_module), @@ -3029,12 +3060,12 @@ def get_missing_protocol_members(left: Instance, right: Instance, skip: list[str def get_conflict_protocol_types( left: Instance, right: Instance, class_obj: bool = False, options: Options | None = None -) -> list[tuple[str, Type, Type]]: +) -> list[tuple[str, Type, Type, bool]]: """Find members that are defined in 'left' but have incompatible types. - Return them as a list of ('member', 'got', 'expected'). + Return them as a list of ('member', 'got', 'expected', 'is_lvalue'). """ assert right.type.is_protocol - conflicts: list[tuple[str, Type, Type]] = [] + conflicts: list[tuple[str, Type, Type, bool]] = [] for member in right.type.protocol_members: if member in ("__init__", "__new__"): continue @@ -3044,10 +3075,29 @@ def get_conflict_protocol_types( if not subtype: continue is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True, options=options) - if IS_SETTABLE in get_member_flags(member, right): - is_compat = is_compat and is_subtype(supertype, subtype, options=options) if not is_compat: - conflicts.append((member, subtype, supertype)) + conflicts.append((member, subtype, supertype, False)) + superflags = get_member_flags(member, right) + if IS_SETTABLE not in superflags: + continue + different_setter = False + if IS_EXPLICIT_SETTER in superflags: + set_supertype = find_member(member, right, left, is_lvalue=True) + if set_supertype and not is_same_type(set_supertype, supertype): + different_setter = True + supertype = set_supertype + if IS_EXPLICIT_SETTER in get_member_flags(member, left): + set_subtype = mypy.typeops.get_protocol_member(left, member, class_obj, is_lvalue=True) + if set_subtype and not is_same_type(set_subtype, subtype): + different_setter = True + subtype = set_subtype + if not is_compat and not different_setter: + # We already have this conflict listed, avoid duplicates. + continue + assert supertype is not None and subtype is not None + is_compat = is_subtype(supertype, subtype, options=options) + if not is_compat: + conflicts.append((member, subtype, supertype, different_setter)) return conflicts diff --git a/mypy/nodes.py b/mypy/nodes.py index b7b09f506c35..9364805d44d4 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -977,6 +977,7 @@ class Var(SymbolNode): "_fullname", "info", "type", + "setter_type", "final_value", "is_self", "is_cls", @@ -1011,6 +1012,8 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None: # TODO: Should be Optional[TypeInfo] self.info = VAR_NO_INFO self.type: mypy.types.Type | None = type # Declared or inferred type, or None + # The setter type for settable properties. + self.setter_type: mypy.types.CallableType | None = None # Is this the first argument to an ordinary method (usually "self")? self.is_self = False # Is this the first argument to a classmethod (typically "cls")? @@ -1076,6 +1079,7 @@ def serialize(self) -> JsonDict: "name": self._name, "fullname": self._fullname, "type": None if self.type is None else self.type.serialize(), + "setter_type": None if self.setter_type is None else self.setter_type.serialize(), "flags": get_flags(self, VAR_FLAGS), } if self.final_value is not None: @@ -1087,7 +1091,18 @@ def deserialize(cls, data: JsonDict) -> Var: assert data[".class"] == "Var" name = data["name"] type = None if data["type"] is None else mypy.types.deserialize_type(data["type"]) + setter_type = ( + None + if data["setter_type"] is None + else mypy.types.deserialize_type(data["setter_type"]) + ) v = Var(name, type) + assert ( + setter_type is None + or isinstance(setter_type, mypy.types.ProperType) + and isinstance(setter_type, mypy.types.CallableType) + ) + v.setter_type = setter_type v.is_ready = False # Override True default set in __init__ v._fullname = data["fullname"] set_flags(v, data["flags"]) diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index f91687823841..07bc6333ce88 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -245,6 +245,11 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb impl = node elif isinstance(node, OverloadedFuncDef) and node.impl: impl = node.impl.func if isinstance(node.impl, Decorator) else node.impl + setter_type = None + if isinstance(node, OverloadedFuncDef) and node.items: + first_item = node.items[0] + if isinstance(first_item, Decorator) and first_item.func.is_property: + setter_type = snapshot_optional_type(first_item.var.setter_type) is_trivial_body = impl.is_trivial_body if impl else False dataclass_transform_spec = find_dataclass_transform_spec(node) return ( @@ -258,6 +263,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb is_trivial_body, dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None, node.deprecated if isinstance(node, FuncDef) else None, + setter_type, # multi-part properties are stored as OverloadedFuncDef ) elif isinstance(node, Var): return ("Var", common, snapshot_optional_type(node.type), node.is_final) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 5dc254422328..bb5606758571 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -330,6 +330,7 @@ def visit_enum_call_expr(self, node: EnumCallExpr) -> None: def visit_var(self, node: Var) -> None: node.info = self.fixup(node.info) self.fixup_type(node.type) + self.fixup_type(node.setter_type) super().visit_var(node) def visit_type_alias(self, node: TypeAlias) -> None: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index ceb9b7f0298a..804930fc9d0c 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -80,6 +80,7 @@ IS_CLASSVAR: Final = 2 IS_CLASS_OR_STATIC: Final = 3 IS_VAR: Final = 4 +IS_EXPLICIT_SETTER: Final = 5 TypeParameterChecker: _TypeAlias = Callable[[Type, Type, int, bool, "SubtypeContext"], bool] @@ -1172,7 +1173,7 @@ def f(self) -> A: ... ignore_names = member != "__call__" # __call__ can be passed kwargs # The third argument below indicates to what self type is bound. # We always bind self to the subtype. (Similarly to nominal types). - supertype = get_proper_type(find_member(member, right, left)) + supertype = find_member(member, right, left) assert supertype is not None subtype = mypy.typeops.get_protocol_member(left, member, class_obj) @@ -1181,15 +1182,6 @@ def f(self) -> A: ... # print(member, 'of', right, 'has type', supertype) if not subtype: return False - if isinstance(subtype, PartialType): - subtype = ( - NoneType() - if subtype.type is None - else Instance( - subtype.type, - [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars), - ) - ) if not proper_subtype: # Nominal check currently ignores arg names # NOTE: If we ever change this, be sure to also change the call to @@ -1201,15 +1193,28 @@ def f(self) -> A: ... is_compat = is_proper_subtype(subtype, supertype) if not is_compat: return False - if isinstance(subtype, NoneType) and isinstance(supertype, CallableType): + if isinstance(get_proper_type(subtype), NoneType) and isinstance( + get_proper_type(supertype), CallableType + ): # We want __hash__ = None idiom to work even without --strict-optional return False subflags = get_member_flags(member, left, class_obj=class_obj) superflags = get_member_flags(member, right) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. + if IS_EXPLICIT_SETTER in superflags: + supertype = find_member(member, right, left, is_lvalue=True) + if IS_EXPLICIT_SETTER in subflags: + subtype = mypy.typeops.get_protocol_member( + left, member, class_obj, is_lvalue=True + ) + # At this point we know attribute is present on subtype, otherwise we + # would return False above. + assert supertype is not None and subtype is not None if not is_subtype(supertype, subtype, options=options): return False + if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: + return False if not class_obj: if IS_SETTABLE not in superflags: if IS_CLASSVAR in superflags and IS_CLASSVAR not in subflags: @@ -1223,8 +1228,6 @@ def f(self) -> A: ... if IS_CLASSVAR in superflags: # This can be never matched by a class object. return False - if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: - return False # This rule is copied from nominal check in checker.py if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: return False @@ -1243,7 +1246,13 @@ def f(self) -> A: ... def find_member( - name: str, itype: Instance, subtype: Type, is_operator: bool = False, class_obj: bool = False + name: str, + itype: Instance, + subtype: Type, + *, + is_operator: bool = False, + class_obj: bool = False, + is_lvalue: bool = False, ) -> Type | None: """Find the type of member by 'name' in 'itype's TypeInfo. @@ -1261,7 +1270,10 @@ def find_member( assert isinstance(method, OverloadedFuncDef) dec = method.items[0] assert isinstance(dec, Decorator) - return find_node_type(dec.var, itype, subtype, class_obj=class_obj) + # Pass on is_lvalue flag as this may be a property with different setter type. + return find_node_type( + dec.var, itype, subtype, class_obj=class_obj, is_lvalue=is_lvalue + ) return find_node_type(method, itype, subtype, class_obj=class_obj) else: # don't have such method, maybe variable or decorator? @@ -1326,7 +1338,10 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set dec = method.items[0] assert isinstance(dec, Decorator) if dec.var.is_settable_property or setattr_meth: - return {IS_VAR, IS_SETTABLE} + flags = {IS_VAR, IS_SETTABLE} + if dec.var.setter_type is not None: + flags.add(IS_EXPLICIT_SETTER) + return flags else: return {IS_VAR} return set() # Just a regular method @@ -1357,7 +1372,11 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set def find_node_type( - node: Var | FuncBase, itype: Instance, subtype: Type, class_obj: bool = False + node: Var | FuncBase, + itype: Instance, + subtype: Type, + class_obj: bool = False, + is_lvalue: bool = False, ) -> Type: """Find type of a variable or method 'node' (maybe also a decorated method). Apply type arguments from 'itype', and bind 'self' to 'subtype'. @@ -1369,7 +1388,13 @@ def find_node_type( node, fallback=Instance(itype.type.mro[-1], []) ) else: - typ = node.type + # This part and the one below are simply copies of the logic from checkmember.py. + if node.is_settable_property and is_lvalue: + typ = node.setter_type + if typ is None and node.is_ready: + typ = node.type + else: + typ = node.type if typ is not None: typ = expand_self_type(node, typ, subtype) p_typ = get_proper_type(typ) @@ -1393,7 +1418,15 @@ def find_node_type( ) if node.is_property and not class_obj: assert isinstance(signature, CallableType) - typ = signature.ret_type + if ( + isinstance(node, Var) + and node.is_settable_property + and is_lvalue + and node.setter_type is not None + ): + typ = signature.arg_types[0] + else: + typ = signature.ret_type else: typ = signature itype = map_instance_to_supertype(itype, node.info) @@ -2041,6 +2074,7 @@ def infer_variance(info: TypeInfo, i: int) -> bool: # Special case to avoid false positives (and to pass conformance tests) settable = False + # TODO: handle settable properties with setter type different from getter. typ = find_member(member, self_type, self_type) if typ: # It's okay for a method in a generic class with a contravariant type diff --git a/mypy/typeops.py b/mypy/typeops.py index 4a269f725cef..1667e8431a17 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1147,7 +1147,9 @@ def fixup_partial_type(typ: Type) -> Type: return Instance(typ.type, [AnyType(TypeOfAny.unannotated)] * len(typ.type.type_vars)) -def get_protocol_member(left: Instance, member: str, class_obj: bool) -> ProperType | None: +def get_protocol_member( + left: Instance, member: str, class_obj: bool, is_lvalue: bool = False +) -> Type | None: if member == "__call__" and class_obj: # Special case: class objects always have __call__ that is just the constructor. from mypy.checkmember import type_object_type @@ -1164,4 +1166,13 @@ def named_type(fullname: str) -> Instance: from mypy.subtypes import find_member - return get_proper_type(find_member(member, left, left, class_obj=class_obj)) + subtype = find_member(member, left, left, class_obj=class_obj, is_lvalue=is_lvalue) + if isinstance(subtype, PartialType): + subtype = ( + NoneType() + if subtype.type is None + else Instance( + subtype.type, [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars) + ) + ) + return subtype diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 993c03bcceff..cf401bc2aece 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -784,7 +784,7 @@ class A: f: Callable[[str], None] class B(A): - @property # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + @property def f(self) -> Callable[[object], None]: pass @func.setter def f(self, x: object) -> None: pass @@ -8067,6 +8067,217 @@ class Bar(Foo): def x(self, value: int) -> None: ... [builtins fixtures/property.pyi] +[case testOverridePropertyDifferentSetterBoth] +class B: ... +class C(B): ... + +class B1: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C1(B1): + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B2: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C2(B2): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B2" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B3: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C3(B3): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B4: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C4(B4): + @property + def foo(self) -> C: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B4" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B5: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C5(B5): + @property # E: Signature of "foo" incompatible with supertype "B5" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: C + def foo(self) -> C: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B5" defined the type as "B", \ + # N: override has type "str") + def foo(self, x: str) -> None: ... + +class B6: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C6(B6): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +[builtins fixtures/property.pyi] + +[case testOverridePropertyDifferentSetterVarSuper] +class B: ... +class C(B): ... + +class B1: + foo: B +class C1(B1): + @property + def foo(self) -> B: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B1" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B2: + foo: C +class C2(B2): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B3: + foo: B +class C3(B3): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +[builtins fixtures/property.pyi] + +[case testOverridePropertyDifferentSetterVarSub] +class B: ... +class C(B): ... + +class B1: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C1(B1): + foo: C + +class B2: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C2(B2): + foo: B + +class B3: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C3(B3): + foo: C # E: Incompatible override of a setter type \ + # N: (base class "B3" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly +[builtins fixtures/property.pyi] + +[case testOverridePropertyInvalidSetter] +class B1: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: str) -> None: ... +class C1(B1): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature + +class B2: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature +class C2(B2): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class B3: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature +class C3(B3): + foo: int +[builtins fixtures/property.pyi] + +[case testOverridePropertyGeneric] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class B1(Generic[T]): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: T) -> None: ... +class C1(B1[str]): + @property + def foo(self) -> int: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B1" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... + +class B2: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self: T, x: T) -> None: ... +class C2(B2): + @property + def foo(self) -> int: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B2" defined the type as "C2", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... +[builtins fixtures/property.pyi] + [case testOverrideMethodProperty] class B: def foo(self) -> int: @@ -8187,3 +8398,48 @@ class C: def f(self) -> None: __module__ # E: Name "__module__" is not defined __qualname__ # E: Name "__qualname__" is not defined + +[case testPropertySetterType] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +a = A() +a.f = '' # OK +reveal_type(a.f) # N: Revealed type is "builtins.int" +a.f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +reveal_type(a.f) # N: Revealed type is "builtins.int" +[builtins fixtures/property.pyi] + +[case testPropertySetterTypeGeneric] +from typing import TypeVar, Generic, List + +T = TypeVar("T") + +class B(Generic[T]): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: T) -> None: ... + +class C(B[List[T]]): ... + +a = C[str]() +a.foo = ["foo", "bar"] +reveal_type(a.foo) # N: Revealed type is "builtins.int" +a.foo = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "List[str]") +reveal_type(a.foo) # N: Revealed type is "builtins.int" +[builtins fixtures/property.pyi] + +[case testPropertyDeleterNoSetterOK] +class C: + @property + def x(self) -> int: + return 0 + @x.deleter + def x(self) -> None: + pass +[builtins fixtures/property.pyi] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 784e5e8a461a..2cc072eb16e7 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6803,3 +6803,29 @@ from typing_extensions import TypeAlias IntOrStr: TypeAlias = int | str assert isinstance(1, IntOrStr) [builtins fixtures/type.pyi] + +[case testPropertySetterTypeIncremental] +import b +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +[file b.py] +from a import A +[file b.py.2] +from a import A +a = A() +a.f = '' # OK +reveal_type(a.f) +a.f = 1 +reveal_type(a.f) +[builtins fixtures/property.pyi] +[out] +[out2] +tmp/b.py:4: note: Revealed type is "builtins.int" +tmp/b.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "str") +tmp/b.py:6: note: Revealed type is "builtins.int" diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 72dc161c6048..294bacb1b7d9 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4246,3 +4246,219 @@ class SupportsAdd(Protocol): x: SupportsAdd = NumpyFloat() [builtins fixtures/tuple.pyi] + +[case testSetterPropertyProtocolSubtypingBoth] +from typing import Protocol + +class B1: ... +class C1(B1): ... +class B2: ... +class C2(B2): ... + +class P1(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class P2(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: B2) -> None: ... + +class A1: + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class A2: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class A3: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class A4: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) +f1(a2) +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "C2", got "str" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C2", got "str" + +f2(a1) # E: Argument 1 to "f2" has incompatible type "A1"; expected "P2" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "B2", got "C2" \ + # N: Setter types should behave contravariantly +f2(a2) # E: Argument 1 to "f2" has incompatible type "A2"; expected "P2" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected setter type "B2", got "C2" \ + # N: Setter types should behave contravariantly +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "B2", got "str" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "B2", got "str" +[builtins fixtures/property.pyi] + +[case testSetterPropertyProtocolSubtypingVarSuper] +from typing import Protocol + +class B1: ... +class C1(B1): ... + +class P1(Protocol): + foo: B1 + +class P2(Protocol): + foo: C1 + +class A1: + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C1) -> None: ... + +class A2: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: B1) -> None: ... + +class A3: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class A4: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) # E: Argument 1 to "f1" has incompatible type "A1"; expected "P1" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "B1", got "C1" \ + # N: Setter types should behave contravariantly +f1(a2) +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "B1", got "str" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" + +f2(a1) # E: Argument 1 to "f2" has incompatible type "A1"; expected "P2" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected "C1", got "B1" +f2(a2) +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "C1", got "str" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "C1", got "str" +[builtins fixtures/property.pyi] + +[case testSetterPropertyProtocolSubtypingVarSub] +from typing import Protocol + +class B1: ... +class C1(B1): ... +class B2: ... +class C2(B2): ... + +class P1(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class P2(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C1) -> None: ... + +class A1: + foo: B1 + +class A2: + foo: B2 + +class A3: + foo: C2 + +class A4: + foo: str + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) # E: Argument 1 to "f1" has incompatible type "A1"; expected "P1" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "C2", got "B1" +f1(a2) # E: Argument 1 to "f1" has incompatible type "A2"; expected "P1" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected "B1", got "B2" +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected "B1", got "C2" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C2", got "str" + +f2(a1) +f2(a2) # E: Argument 1 to "f2" has incompatible type "A2"; expected "P2" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected "B1", got "B2" \ + # N: foo: expected setter type "C1", got "B2" +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected "B1", got "C2" \ + # N: foo: expected setter type "C1", got "C2" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C1", got "str" +[builtins fixtures/property.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 5b49aa6b3a02..98e72e7b3be7 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -11163,3 +11163,53 @@ main:6: error: class a.D is deprecated: use D2 instead main:7: error: class a.D is deprecated: use D2 instead b.py:1: error: class a.C is deprecated: use C2 instead b.py:2: error: class a.D is deprecated: use D2 instead + +[case testPropertySetterTypeFineGrained] +from a import A +a = A() +a.f = '' +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +[file a.py.2] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: int) -> None: + pass +[builtins fixtures/property.pyi] +[out] +== +main:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testPropertyDeleteSetterFineGrained] +from a import A +a = A() +a.f = 1 +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: int) -> None: + pass +[file a.py.2] +class A: + @property + def f(self) -> int: + return 1 + @f.deleter + def f(self) -> None: + pass +[builtins fixtures/property.pyi] +[out] +== +main:3: error: Property "f" defined in "A" is read-only diff --git a/test-data/unit/fixtures/property.pyi b/test-data/unit/fixtures/property.pyi index 667bdc02d0f5..933868ac9907 100644 --- a/test-data/unit/fixtures/property.pyi +++ b/test-data/unit/fixtures/property.pyi @@ -13,7 +13,7 @@ class function: pass property = object() # Dummy definition class classmethod: pass -class list: pass +class list(typing.Generic[_T]): pass class dict: pass class int: pass class float: pass From 0451880759d926bc918eca02d0cfbc6233b0f120 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sun, 26 Jan 2025 23:30:46 +0100 Subject: [PATCH 1075/1617] Allow redefinitions in except/else/finally (#18515) Fixes #18514. Only `try` clause should be treated as fallible, this should not prevent `--allow-redefinition` from working in other try clauses (except, else, finally). --- mypy/renaming.py | 16 +++++++- test-data/unit/check-python311.test | 44 ++++++++++++++++++++ test-data/unit/check-redefine.test | 64 ++++++++++++++++++++++++++++- 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 7cc96566235a..dff76b157acc 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -152,7 +152,21 @@ def visit_try_stmt(self, stmt: TryStmt) -> None: # type checker which allows them to be always redefined, so no need to # do renaming here. with self.enter_try(): - super().visit_try_stmt(stmt) + stmt.body.accept(self) + + for var, tp, handler in zip(stmt.vars, stmt.types, stmt.handlers): + with self.enter_block(): + # Handle except variable together with its body + if tp is not None: + tp.accept(self) + if var is not None: + self.handle_def(var) + for s in handler.body: + s.accept(self) + if stmt.else_body is not None: + stmt.else_body.accept(self) + if stmt.finally_body is not None: + stmt.finally_body.accept(self) def visit_with_stmt(self, stmt: WithStmt) -> None: for expr in stmt.expr: diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 6f4c540572b0..dfbb3d45e56f 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -259,3 +259,47 @@ def foo(): continue # E: "continue" not allowed in except* block return # E: "return" not allowed in except* block [builtins fixtures/exception.pyi] + +[case testRedefineLocalWithinExceptStarTryClauses] +# flags: --allow-redefinition +def fn_str(_: str) -> int: ... +def fn_int(_: int) -> None: ... +def fn_exc(_: Exception) -> str: ... + +def in_block() -> None: + try: + a = "" + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" + except* Exception: + b = "" + b = fn_str(b) + fn_int(b) + else: + c = "" + c = fn_str(c) + fn_int(c) + finally: + d = "" + d = fn_str(d) + fn_int(d) + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(b) # N: Revealed type is "builtins.int" + reveal_type(c) # N: Revealed type is "builtins.int" + reveal_type(d) # N: Revealed type is "builtins.int" + +def across_blocks() -> None: + try: + a = "" + except* Exception: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + else: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + +def exc_name() -> None: + try: + pass + except* RuntimeError as e: + e = fn_exc(e) +[builtins fixtures/exception.pyi] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index b7642d30efc8..1aacffe1fc93 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -88,6 +88,7 @@ def h(a: Iterable[int]) -> None: [case testCannotRedefineLocalWithinTry] # flags: --allow-redefinition +def g(): pass def f() -> None: try: x = 0 @@ -102,7 +103,67 @@ def f() -> None: y y = '' -def g(): pass +[case testRedefineLocalWithinTryClauses] +# flags: --allow-redefinition +def fn_str(_: str) -> int: ... +def fn_int(_: int) -> None: ... + +def in_block() -> None: + try: + a = "" + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" + except: + b = "" + b = fn_str(b) + fn_int(b) + else: + c = "" + c = fn_str(c) + fn_int(c) + finally: + d = "" + d = fn_str(d) + fn_int(d) + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(b) # N: Revealed type is "builtins.int" + reveal_type(c) # N: Revealed type is "builtins.int" + reveal_type(d) # N: Revealed type is "builtins.int" + +def across_blocks() -> None: + try: + a = "" + except: + pass + else: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + +[case testRedefineLocalExceptVar] +# flags: --allow-redefinition +def fn_exc(_: Exception) -> str: ... + +def exc_name() -> None: + try: + pass + except RuntimeError as e: + e = fn_exc(e) +[builtins fixtures/exception.pyi] + +[case testRedefineNestedInTry] +# flags: --allow-redefinition + +def fn_int(_: int) -> None: ... + +try: + try: + ... + finally: + a = "" + a = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" +except: + pass [case testRedefineLocalWithinWith] # flags: --allow-redefinition @@ -274,7 +335,6 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "int", variable has type "TypeVar") reveal_type(x) # N: Revealed type is "typing.TypeVar" y = 1 - # NOTE: '"int" not callable' is due to test stubs y = TypeVar('y') # E: Cannot redefine "y" as a type variable \ # E: Incompatible types in assignment (expression has type "TypeVar", variable has type "int") def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \ From 6274218c48408241b1beda68a99536ffa3a80ef8 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 02:24:11 +0100 Subject: [PATCH 1076/1617] Suggest typing.Literal for exit-return error messages (#18541) `typing.Literal` was added to the stdlib in Python 3.8. --- mypy/messages.py | 2 +- test-data/unit/check-errorcodes.test | 2 +- test-data/unit/check-statements.test | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index c5245daabaa5..04ab40fc4474 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1992,7 +1992,7 @@ def incorrect__exit__return(self, context: Context) -> None: code=codes.EXIT_RETURN, ) self.note( - 'Use "typing_extensions.Literal[False]" as the return type or change it to "None"', + 'Use "typing.Literal[False]" as the return type or change it to "None"', context, code=codes.EXIT_RETURN, ) diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 294038664415..af311b5334b0 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -729,7 +729,7 @@ main:2: error: Syntax error in type comment "int" [syntax] [case testErrorCode__exit__Return] class InvalidReturn: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False [exit-return] \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions return False [builtins fixtures/bool.pyi] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 48e0f2aa681f..1650a6948c93 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -1527,13 +1527,13 @@ from typing import Optional class InvalidReturn1: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions return False class InvalidReturn2: def __exit__(self, x, y, z) -> Optional[bool]: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions if int(): return False @@ -1542,7 +1542,7 @@ class InvalidReturn2: class InvalidReturn3: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions def nested() -> bool: return True From 67a2d04c3c2485cf8405322ed60a4cef9349f8e0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:40:00 -0800 Subject: [PATCH 1077/1617] Run mypy_primer in 3.13 (#18542) I think this is needed for homeassistant --- .github/workflows/mypy_primer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index cf62ce24fb9e..ee868484751e 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | python -m pip install -U pip From 065c8fa39371385d9df936ba5fd16f5df2efd207 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 11:11:41 +0100 Subject: [PATCH 1078/1617] Update math error messages for 3.14 (#18534) The error messages for some math functions got changed in https://github.com/python/cpython/pull/124299. Adjust mypyc to emit the same ones. Fixes `mypyc/test/test_run.py::TestRun::run-math.test::testMathOps` --- mypyc/lib-rt/float_ops.c | 26 ++++++++++++++++++++++++++ mypyc/lib-rt/mypyc_util.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/mypyc/lib-rt/float_ops.c b/mypyc/lib-rt/float_ops.c index d8c6f25955fa..48ebc44431da 100644 --- a/mypyc/lib-rt/float_ops.c +++ b/mypyc/lib-rt/float_ops.c @@ -16,6 +16,24 @@ static double CPy_MathRangeError(void) { return CPY_FLOAT_ERROR; } +static double CPy_MathExpectedNonNegativeInputError(double x) { + char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL); + if (buf) { + PyErr_Format(PyExc_ValueError, "expected a nonnegative input, got %s", buf); + PyMem_Free(buf); + } + return CPY_FLOAT_ERROR; +} + +static double CPy_MathExpectedPositiveInputError(double x) { + char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL); + if (buf) { + PyErr_Format(PyExc_ValueError, "expected a positive input, got %s", buf); + PyMem_Free(buf); + } + return CPY_FLOAT_ERROR; +} + double CPyFloat_FromTagged(CPyTagged x) { if (CPyTagged_CheckShort(x)) { return CPyTagged_ShortAsSsize_t(x); @@ -52,7 +70,11 @@ double CPyFloat_Tan(double x) { double CPyFloat_Sqrt(double x) { if (x < 0.0) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedNonNegativeInputError(x); +#else return CPy_DomainError(); +#endif } return sqrt(x); } @@ -67,7 +89,11 @@ double CPyFloat_Exp(double x) { double CPyFloat_Log(double x) { if (x <= 0.0) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedPositiveInputError(x); +#else return CPy_DomainError(); +#endif } return log(x); } diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 01344331f04e..66d5d106056b 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -147,4 +147,7 @@ static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { // Are we targeting Python 3.13 or newer? #define CPY_3_13_FEATURES (PY_VERSION_HEX >= 0x030d0000) +// Are we targeting Python 3.14 or newer? +#define CPY_3_14_FEATURES (PY_VERSION_HEX >= 0x030e0000) + #endif From 7e8213f600c9cf6948e507251f69ac386d45bd9d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 27 Jan 2025 10:57:52 +0000 Subject: [PATCH 1079/1617] Update docs on extra_checks flag (#18537) Fixes https://github.com/python/mypy/issues/16189 Few things here: * Soften a bit the language on the flag * Delete docs for old deprecated `strict_concatenate` option that is now part of `extra_checks` * Add a bit more motivation to the flag description * Update docs for `--strict` flag to mention `extra_checks` instead of `strict_concatenate` Note that the docs on config file option requested in the issue were added a while ago. --- docs/source/command_line.rst | 12 +++++++++--- docs/source/config_file.rst | 9 +-------- docs/source/existing_code.rst | 7 +++++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 7b6b75b98b6f..3fee6431f8cd 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -692,9 +692,8 @@ of the above sections. .. option:: --extra-checks This flag enables additional checks that are technically correct but may be - impractical in real code. In particular, it prohibits partial overlap in - ``TypedDict`` updates, and makes arguments prepended via ``Concatenate`` - positional-only. For example: + impractical. In particular, it prohibits partial overlap in ``TypedDict`` updates, + and makes arguments prepended via ``Concatenate`` positional-only. For example: .. code-block:: python @@ -717,6 +716,13 @@ of the above sections. bad: Bad = {"a": 0, "b": "no"} test(bad, bar) + In future more checks may be added to this flag if: + + * The corresponding use cases are rare, thus not justifying a dedicated + strictness flag. + + * The new check cannot be supported as an opt-in error code. + .. option:: --strict This flag mode enables all optional error checking flags. You can see the diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 41dadbe7d2a3..e06303777ea9 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -748,7 +748,7 @@ section of the command line docs. :type: boolean :default: False - This flag enables additional checks that are technically correct but may be impractical in real code. + This flag enables additional checks that are technically correct but may be impractical. See :option:`mypy --extra-checks` for more info. .. confval:: implicit_reexport @@ -771,13 +771,6 @@ section of the command line docs. from foo import bar __all__ = ['bar'] -.. confval:: strict_concatenate - - :type: boolean - :default: False - - Make arguments prepended via ``Concatenate`` be truly positional-only. - .. confval:: strict_equality :type: boolean diff --git a/docs/source/existing_code.rst b/docs/source/existing_code.rst index 0a5ac2bfa8f6..dfdc7ef19e16 100644 --- a/docs/source/existing_code.rst +++ b/docs/source/existing_code.rst @@ -199,9 +199,8 @@ The following config is equivalent to ``--strict`` (as of mypy 1.0): warn_redundant_casts = True warn_unused_ignores = True - # Getting these passing should be easy + # Getting this passing should be easy strict_equality = True - strict_concatenate = True # Strongly recommend enabling this one as soon as you can check_untyped_defs = True @@ -223,6 +222,10 @@ The following config is equivalent to ``--strict`` (as of mypy 1.0): # This one can be tricky to get passing if you use a lot of untyped libraries warn_return_any = True + # This one is a catch-all flag for the rest of strict checks that are technically + # correct but may not be practical + extra_checks = True + Note that you can also start with ``--strict`` and subtract, for instance: .. code-block:: text From 16e19f8fd435d0b87ad2ec11137964065410a43d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 27 Jan 2025 10:58:16 +0000 Subject: [PATCH 1080/1617] Fix literal context for ternary expressions (for real) (#18545) I am not waiting for review as the fix is obvious. The only annoying thing is that we had an exact test as in the repro but it passed accidentally because we use builtins fixtures. --- mypy/checker.py | 2 +- test-data/unit/check-literal.test | 2 +- test-data/unit/fixtures/primitives.pyi | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 3734f3170790..bf6c8423c12b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4469,7 +4469,7 @@ def check_simple_assignment( if ( isinstance(get_proper_type(lvalue_type), UnionType) # Skip literal types, as they have special logic (for better errors). - and not isinstance(get_proper_type(rvalue_type), LiteralType) + and not is_literal_type_like(rvalue_type) and not self.simple_rvalue(rvalue) ): # Try re-inferring r.h.s. in empty context, and use that if it diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index fb97bec051e1..856bc941435d 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2980,7 +2980,7 @@ class C(Base): sep = "a" if int() else "b" reveal_type(sep) # N: Revealed type is "Union[Literal['a'], Literal['b']]" return super().feed_data(sep) -[builtins fixtures/tuple.pyi] +[builtins fixtures/primitives.pyi] [case testLiteralInsideAType] from typing_extensions import Literal diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index fc220a4e2ee0..2f8623c79b9f 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -19,6 +19,7 @@ class int: def __init__(self, x: object = ..., base: int = ...) -> None: pass def __add__(self, i: int) -> int: pass def __rmul__(self, x: int) -> int: pass + def __bool__(self) -> bool: pass class float: def __float__(self) -> float: pass def __add__(self, x: float) -> float: pass From 42e005c999d8341c0da6d7b93b10d05f2db2099c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:57:05 +0100 Subject: [PATCH 1081/1617] Update capi_version for mypyc tests to 3.8 (#18341) This PR updates the `capi_version` used for mypyc tests to `3.8` (mypy / mypyc requires `>=3.9`). Test data updates done with `--update-data`. For Python 3.8+ mypyc uses `_PyObject_Vectorcall` instead of `PyObject_CallFunctionObjArgs` and `PyObject_Call` where ever possible. Will remove the now unnecessary `use_vectorcall` check in a followup. https://github.com/python/mypy/blob/aa0b6f0288e6a511b750f7fe8f49a0e321362105/mypyc/common.py#L103-L105 --- mypyc/lib-rt/pythonsupport.h | 4 +- mypyc/primitives/generic_ops.py | 2 +- mypyc/test-data/exceptions.test | 104 +-- mypyc/test-data/irbuild-basic.test | 715 ++++++++++++-------- mypyc/test-data/irbuild-bytes.test | 32 +- mypyc/test-data/irbuild-classes.test | 232 ++++--- mypyc/test-data/irbuild-dict.test | 28 +- mypyc/test-data/irbuild-glue-methods.test | 58 +- mypyc/test-data/irbuild-match.test | 70 +- mypyc/test-data/irbuild-nested.test | 112 +-- mypyc/test-data/irbuild-singledispatch.test | 119 ++-- mypyc/test-data/irbuild-statements.test | 12 +- mypyc/test-data/irbuild-str.test | 73 +- mypyc/test-data/irbuild-try.test | 448 +++++++----- mypyc/test-data/irbuild-unreachable.test | 12 +- mypyc/test-data/irbuild-vectorcall.test | 16 +- mypyc/test-data/refcount.test | 26 +- mypyc/test/testutil.py | 8 +- 18 files changed, 1218 insertions(+), 853 deletions(-) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 61929f512608..33c2848b2df1 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -69,7 +69,7 @@ update_bases(PyObject *bases) } continue; } - new_base = _PyObject_Vectorcall(meth, stack, 1, NULL); + new_base = PyObject_Vectorcall(meth, stack, 1, NULL); Py_DECREF(meth); if (!new_base) { goto error; @@ -118,7 +118,7 @@ init_subclass(PyTypeObject *type, PyObject *kwds) PyObject *super, *func, *result; PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; - super = _PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); + super = PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); if (super == NULL) { return -1; } diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index fe42767db11e..54510d99cf87 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -281,7 +281,7 @@ object_rprimitive, ], # Keyword arg names tuple (or NULL) return_type=object_rprimitive, - c_function_name="_PyObject_Vectorcall", + c_function_name="PyObject_Vectorcall", error_kind=ERR_MAGIC, ) diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 1ec03dd9a671..18983b2c92e9 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -163,9 +163,12 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9 :: object - r10 :: bit - r11 :: None + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bit + r13 :: None L0: L1: r0 = builtins :: module @@ -173,7 +176,7 @@ L1: r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L3 (error at g:3) else goto L2 L2: - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) dec_ref r2 if is_error(r3) goto L3 (error at g:3) else goto L10 L3: @@ -184,9 +187,11 @@ L3: r8 = CPyObject_GetAttr(r6, r7) if is_error(r8) goto L6 (error at g:5) else goto L4 L4: - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) dec_ref r8 - if is_error(r9) goto L6 (error at g:5) else goto L11 + if is_error(r11) goto L6 (error at g:5) else goto L11 L5: CPy_RestoreExcInfo(r4) dec_ref r4 @@ -194,20 +199,20 @@ L5: L6: CPy_RestoreExcInfo(r4) dec_ref r4 - r10 = CPy_KeepPropagating() - if not r10 goto L9 else goto L7 :: bool + r12 = CPy_KeepPropagating() + if not r12 goto L9 else goto L7 :: bool L7: unreachable L8: return 1 L9: - r11 = :: None - return r11 + r13 = :: None + return r13 L10: dec_ref r3 goto L8 L11: - dec_ref r9 + dec_ref r11 goto L5 [case testGenopsTryFinally] @@ -229,9 +234,12 @@ def a(): r10 :: str r11 :: object r12 :: str - r13, r14 :: object - r15 :: bit - r16 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17 :: bit + r18 :: str L0: L1: r0 = builtins :: module @@ -239,7 +247,7 @@ L1: r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L5 (error at a:3) else goto L2 L2: - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) dec_ref r2 if is_error(r3) goto L5 (error at a:3) else goto L19 L3: @@ -262,9 +270,11 @@ L6: r13 = CPyObject_GetAttr(r11, r12) if is_error(r13) goto L20 (error at a:6) else goto L7 L7: - r14 = PyObject_CallFunctionObjArgs(r13, r10, 0) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) dec_ref r13 - if is_error(r14) goto L20 (error at a:6) else goto L21 + if is_error(r16) goto L20 (error at a:6) else goto L21 L8: if is_error(r7) goto L11 else goto L22 L9: @@ -282,15 +292,15 @@ L14: CPy_RestoreExcInfo(r7) xdec_ref r7 L15: - r15 = CPy_KeepPropagating() - if not r15 goto L18 else goto L16 :: bool + r17 = CPy_KeepPropagating() + if not r17 goto L18 else goto L16 :: bool L16: unreachable L17: unreachable L18: - r16 = :: str - return r16 + r18 = :: str + return r18 L19: dec_ref r3 goto L3 @@ -298,7 +308,7 @@ L20: xdec_ref r5 goto L13 L21: - dec_ref r14 + dec_ref r16 goto L8 L22: xdec_ref r5 @@ -446,8 +456,11 @@ def f(b): r6 :: str r7 :: object r8 :: bool - r9 :: object - r10 :: None + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bool + r13 :: None L0: r0 = :: str v = r0 @@ -455,50 +468,59 @@ L0: inc_ref r1 u = r1 L1: - if b goto L10 else goto L11 :: bool + if b goto L13 else goto L14 :: bool L2: r2 = 'b' inc_ref r2 v = r2 r3 = v == u r4 = r3 ^ 1 - if r4 goto L11 else goto L1 :: bool + if r4 goto L14 else goto L1 :: bool L3: r5 = builtins :: module r6 = 'print' r7 = CPyObject_GetAttr(r5, r6) - if is_error(r7) goto L12 (error at f:7) else goto L4 + if is_error(r7) goto L15 (error at f:7) else goto L4 L4: - if is_error(v) goto L13 else goto L7 + if is_error(v) goto L16 else goto L7 L5: r8 = raise UnboundLocalError('local variable "v" referenced before assignment') - if not r8 goto L9 (error at f:7) else goto L6 :: bool + if not r8 goto L12 (error at f:-1) else goto L6 :: bool L6: unreachable L7: - r9 = PyObject_CallFunctionObjArgs(r7, v, 0) + r9 = [v] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r7, r10, 1, 0) dec_ref r7 - xdec_ref v - if is_error(r9) goto L9 (error at f:7) else goto L14 + if is_error(r11) goto L15 (error at f:7) else goto L17 L8: - return 1 + if is_error(v) goto L9 else goto L11 L9: - r10 = :: None - return r10 + r12 = raise UnboundLocalError('local variable "v" referenced before assignment') + if not r12 goto L12 (error at f:-1) else goto L10 :: bool L10: + unreachable +L11: + xdec_ref v + return 1 +L12: + r13 = :: None + return r13 +L13: xdec_ref v goto L2 -L11: +L14: dec_ref u goto L3 -L12: +L15: xdec_ref v - goto L9 -L13: + goto L12 +L16: dec_ref r7 goto L5 -L14: - dec_ref r9 +L17: + dec_ref r11 goto L8 [case testExceptionWithOverlappingErrorValue] diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 835543168a6b..075e6386663b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -484,16 +484,22 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int L0: r0 = testmodule :: module r1 = 'factorial' r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + return r7 [case testImport_toplevel] import sys @@ -581,7 +587,7 @@ L2: r33 = single :: module r34 = 'hello' r35 = CPyObject_GetAttr(r33, r34) - r36 = PyObject_CallFunctionObjArgs(r35, 0) + r36 = PyObject_Vectorcall(r35, 0, 0, 0) return 1 [case testFromImport_toplevel] @@ -600,36 +606,42 @@ def f(x): x :: int r0 :: dict r1 :: str - r2, r3, r4 :: object - r5 :: int - r6 :: dict - r7 :: str - r8, r9 :: object - r10, r11 :: int - r12 :: dict - r13 :: str - r14, r15 :: object - r16, r17 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8 :: dict + r9 :: str + r10, r11 :: object + r12, r13 :: int + r14 :: dict + r15 :: str + r16, r17 :: object + r18, r19 :: int L0: r0 = __main__.globals :: static r1 = 'g' r2 = CPyDict_GetItem(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = __main__.globals :: static - r7 = 'h' - r8 = CPyDict_GetItem(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, 0) - r10 = unbox(int, r9) - r11 = CPyTagged_Add(r5, r10) - r12 = __main__.globals :: static - r13 = 'two' - r14 = CPyDict_GetItem(r12, r13) - r15 = PyObject_CallFunctionObjArgs(r14, 0) - r16 = unbox(int, r15) - r17 = CPyTagged_Add(r11, r16) - return r17 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = __main__.globals :: static + r9 = 'h' + r10 = CPyDict_GetItem(r8, r9) + r11 = PyObject_Vectorcall(r10, 0, 0, 0) + r12 = unbox(int, r11) + r13 = CPyTagged_Add(r7, r12) + r14 = __main__.globals :: static + r15 = 'two' + r16 = CPyDict_GetItem(r14, r15) + r17 = PyObject_Vectorcall(r16, 0, 0, 0) + r18 = unbox(int, r17) + r19 = CPyTagged_Add(r13, r18) + return r19 def __top_level__(): r0, r1 :: object r2 :: bit @@ -673,13 +685,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'print' r2 = CPyObject_GetAttr(r0, r1) r3 = object 5 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testPrint] @@ -691,13 +709,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'print' r2 = CPyObject_GetAttr(r0, r1) r3 = object 5 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testUnicodeLiteral] @@ -1105,16 +1129,22 @@ def call_python_function(x): x :: int r0 :: dict r1 :: str - r2, r3, r4 :: object - r5 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int L0: r0 = __main__.globals :: static r1 = 'f' r2 = CPyDict_GetItem(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + return r7 def return_float(): L0: return 5.0 @@ -1133,7 +1163,7 @@ def call_callable_type(): L0: r0 = return_callable_type() f = r0 - r1 = PyObject_CallFunctionObjArgs(f, 0) + r1 = PyObject_Vectorcall(f, 0, 0, 0) r2 = unbox(float, r1) return r2 @@ -1151,58 +1181,53 @@ def call_python_method_with_keyword_args(xs: List[int], first: int, second: int) [out] def call_python_function_with_keyword_arg(x): x :: str - r0 :: object - r1 :: str - r2 :: tuple - r3 :: object - r4 :: dict - r5 :: object + r0, r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4, r5 :: object r6 :: int L0: r0 = load_address PyLong_Type - r1 = 'base' - r2 = PyTuple_Pack(1, x) - r3 = object 2 - r4 = CPyDict_Build(1, r1, r3) - r5 = PyObject_Call(r0, r2, r4) + r1 = object 2 + r2 = [x, r1] + r3 = load_address r2 + r4 = ('base',) + r5 = PyObject_Vectorcall(r0, r3, 1, r4) + keep_alive x, r1 r6 = unbox(int, r5) return r6 def call_python_method_with_keyword_args(xs, first, second): xs :: list first, second :: int r0 :: str - r1 :: object - r2 :: str - r3 :: object - r4 :: tuple - r5 :: object - r6 :: dict - r7 :: object + r1, r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6, r7 :: object r8 :: str - r9 :: object - r10, r11 :: str - r12 :: tuple - r13, r14 :: object - r15 :: dict - r16 :: object + r9, r10, r11 :: object + r12 :: object[2] + r13 :: object_ptr + r14, r15 :: object L0: r0 = 'insert' r1 = CPyObject_GetAttr(xs, r0) - r2 = 'x' - r3 = object 0 - r4 = PyTuple_Pack(1, r3) - r5 = box(int, first) - r6 = CPyDict_Build(1, r2, r5) - r7 = PyObject_Call(r1, r4, r6) + r2 = object 0 + r3 = box(int, first) + r4 = [r2, r3] + r5 = load_address r4 + r6 = ('x',) + r7 = PyObject_Vectorcall(r1, r5, 1, r6) + keep_alive r2, r3 r8 = 'insert' r9 = CPyObject_GetAttr(xs, r8) - r10 = 'x' - r11 = 'i' - r12 = PyTuple_Pack(0) - r13 = box(int, second) - r14 = object 1 - r15 = CPyDict_Build(2, r10, r13, r11, r14) - r16 = PyObject_Call(r9, r12, r15) + r10 = box(int, second) + r11 = object 1 + r12 = [r10, r11] + r13 = load_address r12 + r14 = ('x', 'i') + r15 = PyObject_Vectorcall(r9, r13, 0, r14) + keep_alive r10, r11 return xs [case testObjectAsBoolean] @@ -1368,7 +1393,7 @@ L0: r0 = builtins :: module r1 = 'Exception' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) CPy_Raise(r3) unreachable def bar(): @@ -1396,7 +1421,10 @@ def f(): r3 :: int r4 :: object r5 :: str - r6, r7, r8 :: object + r6, r7 :: object + r8 :: object[1] + r9 :: object_ptr + r10 :: object L0: r0 = __main__.globals :: static r1 = 'x' @@ -1406,7 +1434,10 @@ L0: r5 = 'print' r6 = CPyObject_GetAttr(r4, r5) r7 = box(int, r3) - r8 = PyObject_CallFunctionObjArgs(r6, r7, 0) + r8 = [r7] + r9 = load_address r8 + r10 = PyObject_Vectorcall(r6, r9, 1, 0) + keep_alive r7 return 1 def __top_level__(): r0, r1 :: object @@ -1424,7 +1455,10 @@ def __top_level__(): r13 :: int r14 :: object r15 :: str - r16, r17, r18 :: object + r16, r17 :: object + r18 :: object[1] + r19 :: object_ptr + r20 :: object L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -1448,7 +1482,10 @@ L2: r15 = 'print' r16 = CPyObject_GetAttr(r14, r15) r17 = box(int, r13) - r18 = PyObject_CallFunctionObjArgs(r16, r17, 0) + r18 = [r17] + r19 = load_address r18 + r20 = PyObject_Vectorcall(r16, r19, 1, 0) + keep_alive r17 return 1 [case testCallOverloaded] @@ -1465,16 +1502,22 @@ def f(x: str) -> int: ... def f(): r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: str + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: str L0: r0 = m :: module r1 = 'f' r2 = CPyObject_GetAttr(r0, r1) r3 = object 1 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = cast(str, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = cast(str, r6) + return r7 [case testCallOverloadedNative] from typing import overload, Union @@ -2147,45 +2190,54 @@ def __top_level__(): r19 :: object r20 :: dict r21 :: str - r22, r23 :: object - r24 :: dict - r25 :: str - r26 :: i32 - r27 :: bit - r28 :: str - r29 :: dict + r22 :: object + r23 :: object[2] + r24 :: object_ptr + r25 :: object + r26 :: dict + r27 :: str + r28 :: i32 + r29 :: bit r30 :: str - r31, r32, r33 :: object - r34 :: tuple - r35 :: dict - r36 :: str - r37 :: i32 - r38 :: bit + r31 :: dict + r32 :: str + r33, r34 :: object + r35 :: object[2] + r36 :: object_ptr + r37 :: object + r38 :: tuple r39 :: dict r40 :: str - r41, r42, r43 :: object - r44 :: dict - r45 :: str - r46 :: i32 - r47 :: bit - r48 :: str - r49 :: dict - r50 :: str - r51 :: object - r52 :: dict - r53 :: str - r54, r55 :: object + r41 :: i32 + r42 :: bit + r43 :: dict + r44 :: str + r45, r46, r47 :: object + r48 :: dict + r49 :: str + r50 :: i32 + r51 :: bit + r52 :: str + r53 :: dict + r54 :: str + r55 :: object r56 :: dict r57 :: str - r58 :: i32 - r59 :: bit - r60 :: list - r61, r62, r63 :: object - r64 :: ptr - r65 :: dict - r66 :: str - r67 :: i32 - r68 :: bit + r58 :: object + r59 :: object[2] + r60 :: object_ptr + r61 :: object + r62 :: dict + r63 :: str + r64 :: i32 + r65 :: bit + r66 :: list + r67, r68, r69 :: object + r70 :: ptr + r71 :: dict + r72 :: str + r73 :: i32 + r74 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2215,56 +2267,65 @@ L2: r20 = __main__.globals :: static r21 = 'NamedTuple' r22 = CPyDict_GetItem(r20, r21) - r23 = PyObject_CallFunctionObjArgs(r22, r9, r19, 0) - r24 = __main__.globals :: static - r25 = 'Lol' - r26 = CPyDict_SetItem(r24, r25, r23) - r27 = r26 >= 0 :: signed - r28 = '' - r29 = __main__.globals :: static - r30 = 'Lol' - r31 = CPyDict_GetItem(r29, r30) - r32 = object 1 - r33 = PyObject_CallFunctionObjArgs(r31, r32, r28, 0) - r34 = cast(tuple, r33) - r35 = __main__.globals :: static - r36 = 'x' - r37 = CPyDict_SetItem(r35, r36, r34) - r38 = r37 >= 0 :: signed + r23 = [r9, r19] + r24 = load_address r23 + r25 = PyObject_Vectorcall(r22, r24, 2, 0) + keep_alive r9, r19 + r26 = __main__.globals :: static + r27 = 'Lol' + r28 = CPyDict_SetItem(r26, r27, r25) + r29 = r28 >= 0 :: signed + r30 = '' + r31 = __main__.globals :: static + r32 = 'Lol' + r33 = CPyDict_GetItem(r31, r32) + r34 = object 1 + r35 = [r34, r30] + r36 = load_address r35 + r37 = PyObject_Vectorcall(r33, r36, 2, 0) + keep_alive r34, r30 + r38 = cast(tuple, r37) r39 = __main__.globals :: static - r40 = 'List' - r41 = CPyDict_GetItem(r39, r40) - r42 = load_address PyLong_Type - r43 = PyObject_GetItem(r41, r42) - r44 = __main__.globals :: static - r45 = 'Foo' - r46 = CPyDict_SetItem(r44, r45, r43) - r47 = r46 >= 0 :: signed - r48 = 'Bar' - r49 = __main__.globals :: static - r50 = 'Foo' - r51 = CPyDict_GetItem(r49, r50) - r52 = __main__.globals :: static - r53 = 'NewType' - r54 = CPyDict_GetItem(r52, r53) - r55 = PyObject_CallFunctionObjArgs(r54, r48, r51, 0) + r40 = 'x' + r41 = CPyDict_SetItem(r39, r40, r38) + r42 = r41 >= 0 :: signed + r43 = __main__.globals :: static + r44 = 'List' + r45 = CPyDict_GetItem(r43, r44) + r46 = load_address PyLong_Type + r47 = PyObject_GetItem(r45, r46) + r48 = __main__.globals :: static + r49 = 'Foo' + r50 = CPyDict_SetItem(r48, r49, r47) + r51 = r50 >= 0 :: signed + r52 = 'Bar' + r53 = __main__.globals :: static + r54 = 'Foo' + r55 = CPyDict_GetItem(r53, r54) r56 = __main__.globals :: static - r57 = 'Bar' - r58 = CPyDict_SetItem(r56, r57, r55) - r59 = r58 >= 0 :: signed - r60 = PyList_New(3) - r61 = object 1 - r62 = object 2 - r63 = object 3 - r64 = list_items r60 - buf_init_item r64, 0, r61 - buf_init_item r64, 1, r62 - buf_init_item r64, 2, r63 - keep_alive r60 - r65 = __main__.globals :: static - r66 = 'y' - r67 = CPyDict_SetItem(r65, r66, r60) - r68 = r67 >= 0 :: signed + r57 = 'NewType' + r58 = CPyDict_GetItem(r56, r57) + r59 = [r52, r55] + r60 = load_address r59 + r61 = PyObject_Vectorcall(r58, r60, 2, 0) + keep_alive r52, r55 + r62 = __main__.globals :: static + r63 = 'Bar' + r64 = CPyDict_SetItem(r62, r63, r61) + r65 = r64 >= 0 :: signed + r66 = PyList_New(3) + r67 = object 1 + r68 = object 2 + r69 = object 3 + r70 = list_items r66 + buf_init_item r70, 0, r67 + buf_init_item r70, 1, r68 + buf_init_item r70, 2, r69 + keep_alive r66 + r71 = __main__.globals :: static + r72 = 'y' + r73 = CPyDict_SetItem(r71, r72, r66) + r74 = r73 >= 0 :: signed return 1 [case testChainedConditional] @@ -2378,25 +2439,37 @@ def g_a_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'Entering' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = 'Exited' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = 'Exited' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def a(f): f :: object @@ -2431,25 +2504,37 @@ def g_b_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = '---' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = '---' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = '---' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def b(f): f :: object @@ -2484,14 +2569,20 @@ def d_c_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'd' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 return 1 def c(): r0 :: __main__.c_env @@ -2499,18 +2590,27 @@ def c(): r2 :: bool r3 :: dict r4 :: str - r5, r6 :: object - r7 :: dict - r8 :: str - r9, r10, d :: object - r11 :: dict - r12 :: str - r13 :: i32 - r14 :: bit - r15 :: str - r16 :: object - r17 :: str - r18, r19, r20 :: object + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: dict + r10 :: str + r11 :: object + r12 :: object[1] + r13 :: object_ptr + r14, d :: object + r15 :: dict + r16 :: str + r17 :: i32 + r18 :: bit + r19 :: str + r20 :: object + r21 :: str + r22 :: object + r23 :: object[1] + r24 :: object_ptr + r25, r26 :: object L0: r0 = c_env() r1 = d_c_obj() @@ -2518,22 +2618,31 @@ L0: r3 = __main__.globals :: static r4 = 'b' r5 = CPyDict_GetItem(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r1, 0) - r7 = __main__.globals :: static - r8 = 'a' - r9 = CPyDict_GetItem(r7, r8) - r10 = PyObject_CallFunctionObjArgs(r9, r6, 0) - d = r10 - r11 = __main__.globals :: static - r12 = 'd' - r13 = CPyDict_SetItem(r11, r12, r10) - r14 = r13 >= 0 :: signed - r15 = 'c' - r16 = builtins :: module - r17 = 'print' - r18 = CPyObject_GetAttr(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, 0) - r20 = PyObject_CallFunctionObjArgs(d, 0) + r6 = [r1] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r1 + r9 = __main__.globals :: static + r10 = 'a' + r11 = CPyDict_GetItem(r9, r10) + r12 = [r8] + r13 = load_address r12 + r14 = PyObject_Vectorcall(r11, r13, 1, 0) + keep_alive r8 + d = r14 + r15 = __main__.globals :: static + r16 = 'd' + r17 = CPyDict_SetItem(r15, r16, r14) + r18 = r17 >= 0 :: signed + r19 = 'c' + r20 = builtins :: module + r21 = 'print' + r22 = CPyObject_GetAttr(r20, r21) + r23 = [r19] + r24 = load_address r23 + r25 = PyObject_Vectorcall(r22, r24, 1, 0) + keep_alive r19 + r26 = PyObject_Vectorcall(d, 0, 0, 0) return 1 def __top_level__(): r0, r1 :: object @@ -2548,14 +2657,20 @@ def __top_level__(): r11 :: object r12 :: dict r13 :: str - r14, r15 :: object - r16 :: dict - r17 :: str - r18, r19 :: object - r20 :: dict - r21 :: str - r22 :: i32 - r23 :: bit + r14 :: object + r15 :: object[1] + r16 :: object_ptr + r17 :: object + r18 :: dict + r19 :: str + r20 :: object + r21 :: object[1] + r22 :: object_ptr + r23 :: object + r24 :: dict + r25 :: str + r26 :: i32 + r27 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2577,15 +2692,21 @@ L2: r12 = __main__.globals :: static r13 = 'b' r14 = CPyDict_GetItem(r12, r13) - r15 = PyObject_CallFunctionObjArgs(r14, r11, 0) - r16 = __main__.globals :: static - r17 = 'a' - r18 = CPyDict_GetItem(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, 0) - r20 = __main__.globals :: static - r21 = 'c' - r22 = CPyDict_SetItem(r20, r21, r19) - r23 = r22 >= 0 :: signed + r15 = [r11] + r16 = load_address r15 + r17 = PyObject_Vectorcall(r14, r16, 1, 0) + keep_alive r11 + r18 = __main__.globals :: static + r19 = 'a' + r20 = CPyDict_GetItem(r18, r19) + r21 = [r17] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r20, r22, 1, 0) + keep_alive r17 + r24 = __main__.globals :: static + r25 = 'c' + r26 = CPyDict_SetItem(r24, r25, r23) + r27 = r26 >= 0 :: signed return 1 [case testDecoratorsSimple_toplevel] @@ -2618,25 +2739,37 @@ def g_a_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'Entering' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = 'Exited' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = 'Exited' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def a(f): f :: object @@ -2757,10 +2890,13 @@ def call_sum(l, comparison): r0 :: int r1, r2 :: object r3, x :: int - r4, r5 :: object - r6, r7 :: bool - r8, r9 :: int - r10 :: bit + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, r9 :: bool + r10, r11 :: int + r12 :: bit L0: r0 = 0 r1 = PyObject_GetIter(l) @@ -2771,16 +2907,19 @@ L2: r3 = unbox(int, r2) x = r3 r4 = box(int, x) - r5 = PyObject_CallFunctionObjArgs(comparison, r4, 0) - r6 = unbox(bool, r5) - r7 = r6 << 1 - r8 = extend r7: builtins.bool to builtins.int - r9 = CPyTagged_Add(r0, r8) - r0 = r9 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(comparison, r6, 1, 0) + keep_alive r4 + r8 = unbox(bool, r7) + r9 = r8 << 1 + r10 = extend r9: builtins.bool to builtins.int + r11 = CPyTagged_Add(r0, r10) + r0 = r11 L3: goto L1 L4: - r10 = CPy_NoErrOccurred() + r12 = CPy_NoErrOccurred() L5: return r0 @@ -3060,13 +3199,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'reveal_type' r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testCallCWithStrJoinMethod] @@ -3274,34 +3419,40 @@ def range_in_loop() -> None: sum += i [out] def range_object(): - r0, r1, r2, r3, r4 :: object - r5, r :: range + r0, r1, r2, r3 :: object + r4 :: object[3] + r5 :: object_ptr + r6 :: object + r7, r :: range sum :: int - r6, r7 :: object - r8, i, r9 :: int - r10 :: bit + r8, r9 :: object + r10, i, r11 :: int + r12 :: bit L0: r0 = load_address PyRange_Type r1 = object 4 r2 = object 12 r3 = object 2 - r4 = PyObject_CallFunctionObjArgs(r0, r1, r2, r3, 0) - r5 = cast(range, r4) - r = r5 + r4 = [r1, r2, r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r0, r5, 3, 0) + keep_alive r1, r2, r3 + r7 = cast(range, r6) + r = r7 sum = 0 - r6 = PyObject_GetIter(r) + r8 = PyObject_GetIter(r) L1: - r7 = PyIter_Next(r6) - if is_error(r7) goto L4 else goto L2 + r9 = PyIter_Next(r8) + if is_error(r9) goto L4 else goto L2 L2: - r8 = unbox(int, r7) - i = r8 - r9 = CPyTagged_Add(sum, i) - sum = r9 + r10 = unbox(int, r9) + i = r10 + r11 = CPyTagged_Add(sum, i) + sum = r11 L3: goto L1 L4: - r10 = CPy_NoErrOccurred() + r12 = CPy_NoErrOccurred() L5: return 1 def range_in_loop(): diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index b41836d8829f..476c5ac59f48 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -13,24 +13,30 @@ def f(num, l, d, s): s :: str r0, r1 :: object r2, b1 :: bytes - r3, r4, r5 :: object - r6, b2, r7, b3, r8, b4, r9, b5 :: bytes + r3, r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, b2, r9, b3, r10, b4, r11, b5 :: bytes L0: r0 = load_address PyBytes_Type - r1 = PyObject_CallFunctionObjArgs(r0, 0) + r1 = PyObject_Vectorcall(r0, 0, 0, 0) r2 = cast(bytes, r1) b1 = r2 r3 = load_address PyBytes_Type r4 = box(int, num) - r5 = PyObject_CallFunctionObjArgs(r3, r4, 0) - r6 = cast(bytes, r5) - b2 = r6 - r7 = PyBytes_FromObject(l) - b3 = r7 - r8 = PyBytes_FromObject(d) - b4 = r8 - r9 = PyBytes_FromObject(s) - b5 = r9 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 1, 0) + keep_alive r4 + r8 = cast(bytes, r7) + b2 = r8 + r9 = PyBytes_FromObject(l) + b3 = r9 + r10 = PyBytes_FromObject(d) + b4 = r10 + r11 = PyBytes_FromObject(s) + b5 = r11 return 1 [case testBytearrayBasics] @@ -53,7 +59,7 @@ L0: r0 = builtins :: module r1 = 'bytearray' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) a = r3 r4 = PyByteArray_FromObject(s) b = r4 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index e0f7dfe6514f..2364b508aad9 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -209,53 +209,56 @@ def __top_level__(): r13 :: str r14 :: dict r15 :: str - r16, r17 :: object - r18 :: dict - r19 :: str - r20 :: i32 - r21 :: bit - r22 :: object - r23 :: str - r24, r25 :: object - r26 :: bool - r27 :: str - r28 :: tuple - r29 :: i32 - r30 :: bit - r31 :: dict - r32 :: str - r33 :: i32 - r34 :: bit - r35 :: object - r36 :: str - r37, r38 :: object - r39 :: str - r40 :: tuple - r41 :: i32 - r42 :: bit - r43 :: dict - r44 :: str - r45 :: i32 - r46 :: bit - r47, r48 :: object - r49 :: dict - r50 :: str - r51 :: object - r52 :: dict - r53 :: str - r54, r55 :: object - r56 :: tuple - r57 :: str - r58, r59 :: object - r60 :: bool - r61, r62 :: str - r63 :: tuple - r64 :: i32 - r65 :: bit - r66 :: dict - r67 :: str - r68 :: i32 - r69 :: bit + r16 :: object + r17 :: object[1] + r18 :: object_ptr + r19 :: object + r20 :: dict + r21 :: str + r22 :: i32 + r23 :: bit + r24 :: object + r25 :: str + r26, r27 :: object + r28 :: bool + r29 :: str + r30 :: tuple + r31 :: i32 + r32 :: bit + r33 :: dict + r34 :: str + r35 :: i32 + r36 :: bit + r37 :: object + r38 :: str + r39, r40 :: object + r41 :: str + r42 :: tuple + r43 :: i32 + r44 :: bit + r45 :: dict + r46 :: str + r47 :: i32 + r48 :: bit + r49, r50 :: object + r51 :: dict + r52 :: str + r53 :: object + r54 :: dict + r55 :: str + r56, r57 :: object + r58 :: tuple + r59 :: str + r60, r61 :: object + r62 :: bool + r63, r64 :: str + r65 :: tuple + r66 :: i32 + r67 :: bit + r68 :: dict + r69 :: str + r70 :: i32 + r71 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -280,62 +283,65 @@ L2: r14 = __main__.globals :: static r15 = 'TypeVar' r16 = CPyDict_GetItem(r14, r15) - r17 = PyObject_CallFunctionObjArgs(r16, r13, 0) - r18 = __main__.globals :: static - r19 = 'T' - r20 = CPyDict_SetItem(r18, r19, r17) - r21 = r20 >= 0 :: signed - r22 = :: object - r23 = '__main__' - r24 = __main__.C_template :: type - r25 = CPyType_FromTemplate(r24, r22, r23) - r26 = C_trait_vtable_setup() - r27 = '__mypyc_attrs__' - r28 = PyTuple_Pack(0) - r29 = PyObject_SetAttr(r25, r27, r28) - r30 = r29 >= 0 :: signed - __main__.C = r25 :: type - r31 = __main__.globals :: static - r32 = 'C' - r33 = CPyDict_SetItem(r31, r32, r25) - r34 = r33 >= 0 :: signed - r35 = :: object - r36 = '__main__' - r37 = __main__.S_template :: type - r38 = CPyType_FromTemplate(r37, r35, r36) - r39 = '__mypyc_attrs__' - r40 = PyTuple_Pack(0) - r41 = PyObject_SetAttr(r38, r39, r40) - r42 = r41 >= 0 :: signed - __main__.S = r38 :: type - r43 = __main__.globals :: static - r44 = 'S' - r45 = CPyDict_SetItem(r43, r44, r38) - r46 = r45 >= 0 :: signed - r47 = __main__.C :: type - r48 = __main__.S :: type - r49 = __main__.globals :: static - r50 = 'Generic' - r51 = CPyDict_GetItem(r49, r50) - r52 = __main__.globals :: static - r53 = 'T' - r54 = CPyDict_GetItem(r52, r53) - r55 = PyObject_GetItem(r51, r54) - r56 = PyTuple_Pack(3, r47, r48, r55) - r57 = '__main__' - r58 = __main__.D_template :: type - r59 = CPyType_FromTemplate(r58, r56, r57) - r60 = D_trait_vtable_setup() - r61 = '__mypyc_attrs__' - r62 = '__dict__' - r63 = PyTuple_Pack(1, r62) - r64 = PyObject_SetAttr(r59, r61, r63) - r65 = r64 >= 0 :: signed - __main__.D = r59 :: type - r66 = __main__.globals :: static - r67 = 'D' - r68 = CPyDict_SetItem(r66, r67, r59) - r69 = r68 >= 0 :: signed + r17 = [r13] + r18 = load_address r17 + r19 = PyObject_Vectorcall(r16, r18, 1, 0) + keep_alive r13 + r20 = __main__.globals :: static + r21 = 'T' + r22 = CPyDict_SetItem(r20, r21, r19) + r23 = r22 >= 0 :: signed + r24 = :: object + r25 = '__main__' + r26 = __main__.C_template :: type + r27 = CPyType_FromTemplate(r26, r24, r25) + r28 = C_trait_vtable_setup() + r29 = '__mypyc_attrs__' + r30 = PyTuple_Pack(0) + r31 = PyObject_SetAttr(r27, r29, r30) + r32 = r31 >= 0 :: signed + __main__.C = r27 :: type + r33 = __main__.globals :: static + r34 = 'C' + r35 = CPyDict_SetItem(r33, r34, r27) + r36 = r35 >= 0 :: signed + r37 = :: object + r38 = '__main__' + r39 = __main__.S_template :: type + r40 = CPyType_FromTemplate(r39, r37, r38) + r41 = '__mypyc_attrs__' + r42 = PyTuple_Pack(0) + r43 = PyObject_SetAttr(r40, r41, r42) + r44 = r43 >= 0 :: signed + __main__.S = r40 :: type + r45 = __main__.globals :: static + r46 = 'S' + r47 = CPyDict_SetItem(r45, r46, r40) + r48 = r47 >= 0 :: signed + r49 = __main__.C :: type + r50 = __main__.S :: type + r51 = __main__.globals :: static + r52 = 'Generic' + r53 = CPyDict_GetItem(r51, r52) + r54 = __main__.globals :: static + r55 = 'T' + r56 = CPyDict_GetItem(r54, r55) + r57 = PyObject_GetItem(r53, r56) + r58 = PyTuple_Pack(3, r49, r50, r57) + r59 = '__main__' + r60 = __main__.D_template :: type + r61 = CPyType_FromTemplate(r60, r58, r59) + r62 = D_trait_vtable_setup() + r63 = '__mypyc_attrs__' + r64 = '__dict__' + r65 = PyTuple_Pack(1, r64) + r66 = PyObject_SetAttr(r61, r63, r65) + r67 = r66 >= 0 :: signed + __main__.D = r61 :: type + r68 = __main__.globals :: static + r69 = 'D' + r70 = CPyDict_SetItem(r68, r69, r61) + r71 = r70 >= 0 :: signed return 1 [case testIsInstance] @@ -747,18 +753,24 @@ def DictSubclass.__init__(self): self :: dict r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: str - r6, r7 :: object + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8, r9 :: object L0: r0 = builtins :: module r1 = 'super' r2 = CPyObject_GetAttr(r0, r1) r3 = __main__.DictSubclass :: type - r4 = PyObject_CallFunctionObjArgs(r2, r3, self, 0) - r5 = '__init__' - r6 = CPyObject_GetAttr(r4, r5) - r7 = PyObject_CallFunctionObjArgs(r6, 0) + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__init__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = PyObject_Vectorcall(r8, 0, 0, 0) return 1 [case testClassVariable] diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 68c9ccb9f0e5..258bf953b09c 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -342,11 +342,14 @@ def union_of_dicts(d): r11 :: union[int, str] k :: str v :: union[int, str] - r12, r13 :: object - r14 :: int + r12 :: object + r13 :: object[1] + r14 :: object_ptr r15 :: object - r16 :: i32 - r17, r18, r19 :: bit + r16 :: int + r17 :: object + r18 :: i32 + r19, r20, r21 :: bit L0: r0 = PyDict_New() new = r0 @@ -368,16 +371,19 @@ L2: k = r10 v = r11 r12 = load_address PyLong_Type - r13 = PyObject_CallFunctionObjArgs(r12, v, 0) - r14 = unbox(int, r13) - r15 = box(int, r14) - r16 = CPyDict_SetItem(new, k, r15) - r17 = r16 >= 0 :: signed + r13 = [v] + r14 = load_address r13 + r15 = PyObject_Vectorcall(r12, r14, 1, 0) + keep_alive v + r16 = unbox(int, r15) + r17 = box(int, r16) + r18 = CPyDict_SetItem(new, k, r17) + r19 = r18 >= 0 :: signed L3: - r18 = CPyDict_CheckSize(d, r3) + r20 = CPyDict_CheckSize(d, r3) goto L1 L4: - r19 = CPy_NoErrOccurred() + r21 = CPy_NoErrOccurred() L5: return 1 def typeddict(d): diff --git a/mypyc/test-data/irbuild-glue-methods.test b/mypyc/test-data/irbuild-glue-methods.test index 3012c79586f2..35e6be1283eb 100644 --- a/mypyc/test-data/irbuild-glue-methods.test +++ b/mypyc/test-data/irbuild-glue-methods.test @@ -194,18 +194,24 @@ def DerivedProperty.next(self): self :: __main__.DerivedProperty r0 :: object r1 :: int - r2, r3, r4 :: object - r5 :: int - r6 :: __main__.DerivedProperty + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8 :: __main__.DerivedProperty L0: r0 = self._incr_func r1 = self.value r2 = self._incr_func r3 = box(int, r1) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = DerivedProperty(r0, r5) - return r6 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = DerivedProperty(r0, r7) + return r8 def DerivedProperty.next__BaseProperty_glue(__mypyc_self__): __mypyc_self__, r0 :: __main__.DerivedProperty L0: @@ -224,24 +230,36 @@ def AgainProperty.next(self): self :: __main__.AgainProperty r0 :: object r1 :: int - r2, r3, r4 :: object - r5 :: int - r6, r7, r8 :: object - r9 :: int - r10 :: __main__.AgainProperty + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8, r9 :: object + r10 :: object[1] + r11 :: object_ptr + r12 :: object + r13 :: int + r14 :: __main__.AgainProperty L0: r0 = self._incr_func r1 = self.value r2 = self._incr_func r3 = box(int, r1) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = self._incr_func - r7 = box(int, r5) - r8 = PyObject_CallFunctionObjArgs(r6, r7, 0) - r9 = unbox(int, r8) - r10 = AgainProperty(r0, r9) - return r10 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = self._incr_func + r9 = box(int, r7) + r10 = [r9] + r11 = load_address r10 + r12 = PyObject_Vectorcall(r8, r11, 1, 0) + keep_alive r9 + r13 = unbox(int, r12) + r14 = AgainProperty(r0, r13) + return r14 def AgainProperty.next__DerivedProperty_glue(__mypyc_self__): __mypyc_self__, r0 :: __main__.AgainProperty L0: diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index bd8878c5009e..c5dc81bbf049 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -23,7 +23,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L3 L2: @@ -61,7 +61,7 @@ L3: r5 = CPyObject_GetAttr(r3, r4) r6 = [r2] r7 = load_address r6 - r8 = _PyObject_Vectorcall(r5, r7, 1, 0) + r8 = PyObject_Vectorcall(r5, r7, 1, 0) keep_alive r2 goto L5 L4: @@ -105,7 +105,7 @@ L5: r7 = CPyObject_GetAttr(r5, r6) r8 = [r4] r9 = load_address r8 - r10 = _PyObject_Vectorcall(r7, r9, 1, 0) + r10 = PyObject_Vectorcall(r7, r9, 1, 0) keep_alive r4 goto L7 L6: @@ -141,7 +141,7 @@ L1: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L3 L2: @@ -170,7 +170,7 @@ L1: r3 = CPyObject_GetAttr(r1, r2) r4 = [r0] r5 = load_address r4 - r6 = _PyObject_Vectorcall(r3, r5, 1, 0) + r6 = PyObject_Vectorcall(r3, r5, 1, 0) keep_alive r0 goto L3 L2: @@ -212,7 +212,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L5 L2: @@ -225,7 +225,7 @@ L3: r12 = CPyObject_GetAttr(r10, r11) r13 = [r9] r14 = load_address r13 - r15 = _PyObject_Vectorcall(r12, r14, 1, 0) + r15 = PyObject_Vectorcall(r12, r14, 1, 0) keep_alive r9 goto L5 L4: @@ -278,7 +278,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L9 L2: @@ -296,7 +296,7 @@ L5: r13 = CPyObject_GetAttr(r11, r12) r14 = [r10] r15 = load_address r14 - r16 = _PyObject_Vectorcall(r13, r15, 1, 0) + r16 = PyObject_Vectorcall(r13, r15, 1, 0) keep_alive r10 goto L9 L6: @@ -309,7 +309,7 @@ L7: r21 = CPyObject_GetAttr(r19, r20) r22 = [r18] r23 = load_address r22 - r24 = _PyObject_Vectorcall(r21, r23, 1, 0) + r24 = PyObject_Vectorcall(r21, r23, 1, 0) keep_alive r18 goto L9 L8: @@ -344,7 +344,7 @@ L2: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L4 L3: @@ -400,7 +400,7 @@ L1: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L7 L2: @@ -415,7 +415,7 @@ L3: r16 = CPyObject_GetAttr(r14, r15) r17 = [r13] r18 = load_address r17 - r19 = _PyObject_Vectorcall(r16, r18, 1, 0) + r19 = PyObject_Vectorcall(r16, r18, 1, 0) keep_alive r13 goto L7 L4: @@ -430,7 +430,7 @@ L5: r26 = CPyObject_GetAttr(r24, r25) r27 = [r23] r28 = load_address r27 - r29 = _PyObject_Vectorcall(r26, r28, 1, 0) + r29 = PyObject_Vectorcall(r26, r28, 1, 0) keep_alive r23 goto L7 L6: @@ -471,7 +471,7 @@ L3: r7 = CPyObject_GetAttr(r5, r6) r8 = [r4] r9 = load_address r8 - r10 = _PyObject_Vectorcall(r7, r9, 1, 0) + r10 = PyObject_Vectorcall(r7, r9, 1, 0) keep_alive r4 goto L5 L4: @@ -504,7 +504,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [x] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive x goto L3 L2: @@ -546,7 +546,7 @@ L3: r6 = CPyObject_GetAttr(r4, r5) r7 = [x] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive x goto L5 L4: @@ -584,7 +584,7 @@ L2: r6 = box(int, i) r7 = [r6] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r5, r8, 1, 0) + r9 = PyObject_Vectorcall(r5, r8, 1, 0) keep_alive r6 goto L4 L3: @@ -682,7 +682,7 @@ L4: r28 = CPyObject_GetAttr(r26, r27) r29 = [r25] r30 = load_address r29 - r31 = _PyObject_Vectorcall(r28, r30, 1, 0) + r31 = PyObject_Vectorcall(r28, r30, 1, 0) keep_alive r25 goto L6 L5: @@ -767,7 +767,7 @@ L4: r28 = CPyObject_GetAttr(r26, r27) r29 = [r25] r30 = load_address r29 - r31 = _PyObject_Vectorcall(r28, r30, 1, 0) + r31 = PyObject_Vectorcall(r28, r30, 1, 0) keep_alive r25 goto L6 L5: @@ -835,7 +835,7 @@ L4: r19 = CPyObject_GetAttr(r17, r18) r20 = [r16] r21 = load_address r20 - r22 = _PyObject_Vectorcall(r19, r21, 1, 0) + r22 = PyObject_Vectorcall(r19, r21, 1, 0) keep_alive r16 goto L6 L5: @@ -920,7 +920,7 @@ L4: r22 = CPyObject_GetAttr(r20, r21) r23 = [r19] r24 = load_address r23 - r25 = _PyObject_Vectorcall(r22, r24, 1, 0) + r25 = PyObject_Vectorcall(r22, r24, 1, 0) keep_alive r19 goto L6 L5: @@ -980,7 +980,7 @@ L2: r10 = CPyObject_GetAttr(r8, r9) r11 = [r7] r12 = load_address r11 - r13 = _PyObject_Vectorcall(r10, r12, 1, 0) + r13 = PyObject_Vectorcall(r10, r12, 1, 0) keep_alive r7 goto L4 L3: @@ -1015,7 +1015,7 @@ L1: r5 = CPyObject_GetAttr(r3, r4) r6 = [r2] r7 = load_address r6 - r8 = _PyObject_Vectorcall(r5, r7, 1, 0) + r8 = PyObject_Vectorcall(r5, r7, 1, 0) keep_alive r2 goto L3 L2: @@ -1072,7 +1072,7 @@ L3: r14 = CPyObject_GetAttr(r12, r13) r15 = [r11] r16 = load_address r15 - r17 = _PyObject_Vectorcall(r14, r16, 1, 0) + r17 = PyObject_Vectorcall(r14, r16, 1, 0) keep_alive r11 goto L5 L4: @@ -1111,7 +1111,7 @@ L2: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L4 L3: @@ -1176,7 +1176,7 @@ L4: r17 = CPyObject_GetAttr(r15, r16) r18 = [r14] r19 = load_address r18 - r20 = _PyObject_Vectorcall(r17, r19, 1, 0) + r20 = PyObject_Vectorcall(r17, r19, 1, 0) keep_alive r14 goto L6 L5: @@ -1218,7 +1218,7 @@ L2: r8 = CPyObject_GetAttr(r6, r7) r9 = [r5] r10 = load_address r9 - r11 = _PyObject_Vectorcall(r8, r10, 1, 0) + r11 = PyObject_Vectorcall(r8, r10, 1, 0) keep_alive r5 goto L4 L3: @@ -1284,7 +1284,7 @@ L4: r20 = CPyObject_GetAttr(r18, r19) r21 = [r17] r22 = load_address r21 - r23 = _PyObject_Vectorcall(r20, r22, 1, 0) + r23 = PyObject_Vectorcall(r20, r22, 1, 0) keep_alive r17 goto L6 L5: @@ -1350,7 +1350,7 @@ L4: r20 = CPyObject_GetAttr(r18, r19) r21 = [r17] r22 = load_address r21 - r23 = _PyObject_Vectorcall(r20, r22, 1, 0) + r23 = PyObject_Vectorcall(r20, r22, 1, 0) keep_alive r17 goto L6 L5: @@ -1424,7 +1424,7 @@ L5: r23 = CPyObject_GetAttr(r21, r22) r24 = [r20] r25 = load_address r24 - r26 = _PyObject_Vectorcall(r23, r25, 1, 0) + r26 = PyObject_Vectorcall(r23, r25, 1, 0) keep_alive r20 goto L7 L6: @@ -1505,7 +1505,7 @@ L5: r24 = CPyObject_GetAttr(r22, r23) r25 = [r21] r26 = load_address r25 - r27 = _PyObject_Vectorcall(r24, r26, 1, 0) + r27 = PyObject_Vectorcall(r24, r26, 1, 0) keep_alive r21 goto L7 L6: @@ -1584,7 +1584,7 @@ L5: r25 = CPyObject_GetAttr(r23, r24) r26 = [r22] r27 = load_address r26 - r28 = _PyObject_Vectorcall(r25, r27, 1, 0) + r28 = PyObject_Vectorcall(r25, r27, 1, 0) keep_alive r22 goto L7 L6: @@ -1623,7 +1623,7 @@ L2: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L4 L3: @@ -1673,7 +1673,7 @@ L3: r11 = CPyObject_GetAttr(r9, r10) r12 = [r8] r13 = load_address r12 - r14 = _PyObject_Vectorcall(r11, r13, 1, 0) + r14 = PyObject_Vectorcall(r11, r13, 1, 0) keep_alive r8 goto L5 L4: diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 62ae6eb9ee35..1b390e9c3504 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -194,23 +194,33 @@ def d(num): r2 :: bool inner :: object r3 :: str - r4 :: object - r5, a, r6 :: str - r7 :: object - r8, b :: str + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7, a, r8 :: str + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12, b :: str L0: r0 = d_env() r1 = inner_d_obj() r1.__mypyc_env__ = r0; r2 = is_error inner = r1 r3 = 'one' - r4 = PyObject_CallFunctionObjArgs(inner, r3, 0) - r5 = cast(str, r4) - a = r5 - r6 = 'two' - r7 = PyObject_CallFunctionObjArgs(inner, r6, 0) - r8 = cast(str, r7) - b = r8 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(inner, r5, 1, 0) + keep_alive r3 + r7 = cast(str, r6) + a = r7 + r8 = 'two' + r9 = [r8] + r10 = load_address r9 + r11 = PyObject_Vectorcall(inner, r10, 1, 0) + keep_alive r8 + r12 = cast(str, r11) + b = r12 return a def inner(): r0 :: str @@ -290,7 +300,7 @@ L0: r2 = inner_a_obj() r2.__mypyc_env__ = r0; r3 = is_error inner = r2 - r4 = PyObject_CallFunctionObjArgs(inner, 0) + r4 = PyObject_Vectorcall(inner, 0, 0, 0) r5 = unbox(int, r4) return r5 def inner_b_obj.__get__(__mypyc_self__, instance, owner): @@ -330,7 +340,7 @@ L0: r2 = inner_b_obj() r2.__mypyc_env__ = r0; r3 = is_error inner = r2 - r4 = PyObject_CallFunctionObjArgs(inner, 0) + r4 = PyObject_Vectorcall(inner, 0, 0, 0) r5 = unbox(int, r4) r6 = r0.num r7 = CPyTagged_Add(r5, r6) @@ -400,7 +410,7 @@ L2: r3.__mypyc_env__ = r0; r4 = is_error inner = r3 L3: - r5 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = PyObject_Vectorcall(inner, 0, 0, 0) r6 = cast(str, r5) return r6 @@ -472,7 +482,7 @@ L0: r6 = c_a_b_obj() r6.__mypyc_env__ = r1; r7 = is_error c = r6 - r8 = PyObject_CallFunctionObjArgs(c, 0) + r8 = PyObject_Vectorcall(c, 0, 0, 0) r9 = unbox(int, r8) return r9 def a(): @@ -488,7 +498,7 @@ L0: r2 = b_a_obj() r2.__mypyc_env__ = r0; r3 = is_error b = r2 - r4 = PyObject_CallFunctionObjArgs(b, 0) + r4 = PyObject_Vectorcall(b, 0, 0, 0) r5 = unbox(int, r4) return r5 @@ -567,7 +577,7 @@ L2: r3.__mypyc_env__ = r0; r4 = is_error inner = r3 L3: - r5 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = PyObject_Vectorcall(inner, 0, 0, 0) r6 = cast(str, r5) return r6 @@ -632,7 +642,7 @@ def bar_f_obj.__call__(__mypyc_self__): L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.foo - r2 = PyObject_CallFunctionObjArgs(r1, 0) + r2 = PyObject_Vectorcall(r1, 0, 0, 0) r3 = unbox(int, r2) return r3 def baz_f_obj.__get__(__mypyc_self__, instance, owner): @@ -654,8 +664,11 @@ def baz_f_obj.__call__(__mypyc_self__, n): r0 :: __main__.f_env r1 :: bit r2 :: int - r3, r4, r5 :: object - r6, r7 :: int + r3, r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, r9 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = int_eq n, 0 @@ -666,10 +679,13 @@ L2: r2 = CPyTagged_Subtract(n, 2) r3 = r0.baz r4 = box(int, r2) - r5 = PyObject_CallFunctionObjArgs(r3, r4, 0) - r6 = unbox(int, r5) - r7 = CPyTagged_Add(n, r6) - return r7 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 1, 0) + keep_alive r4 + r8 = unbox(int, r7) + r9 = CPyTagged_Add(n, r8) + return r9 def f(a): a :: int r0 :: __main__.f_env @@ -682,8 +698,11 @@ def f(a): r9, r10 :: bool r11, r12 :: object r13, r14 :: int - r15, r16, r17 :: object - r18, r19 :: int + r15, r16 :: object + r17 :: object[1] + r18 :: object_ptr + r19 :: object + r20, r21 :: int L0: r0 = f_env() r0.a = a; r1 = is_error @@ -697,15 +716,18 @@ L0: r8.__mypyc_env__ = r0; r9 = is_error r0.baz = r8; r10 = is_error r11 = r0.bar - r12 = PyObject_CallFunctionObjArgs(r11, 0) + r12 = PyObject_Vectorcall(r11, 0, 0, 0) r13 = unbox(int, r12) r14 = r0.a r15 = r0.baz r16 = box(int, r14) - r17 = PyObject_CallFunctionObjArgs(r15, r16, 0) - r18 = unbox(int, r17) - r19 = CPyTagged_Add(r13, r18) - return r19 + r17 = [r16] + r18 = load_address r17 + r19 = PyObject_Vectorcall(r15, r18, 1, 0) + keep_alive r16 + r20 = unbox(int, r19) + r21 = CPyTagged_Add(r13, r20) + return r21 [case testLambdas] def f(x: int, y: int) -> None: @@ -753,12 +775,18 @@ def __mypyc_lambda__1_f_obj.__call__(__mypyc_self__, a, b): __mypyc_self__ :: __main__.__mypyc_lambda__1_f_obj a, b :: object r0 :: __main__.f_env - r1, r2 :: object + r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.s - r2 = PyObject_CallFunctionObjArgs(r1, a, b, 0) - return r2 + r2 = [a, b] + r3 = load_address r2 + r4 = PyObject_Vectorcall(r1, r3, 2, 0) + keep_alive a, b + return r4 def f(x, y): x, y :: int r0 :: __main__.f_env @@ -766,8 +794,11 @@ def f(x, y): r2, r3 :: bool r4 :: __main__.__mypyc_lambda__1_f_obj r5 :: bool - t, r6, r7, r8 :: object - r9 :: None + t, r6, r7 :: object + r8 :: object[2] + r9 :: object_ptr + r10 :: object + r11 :: None L0: r0 = f_env() r1 = __mypyc_lambda__0_f_obj() @@ -778,9 +809,12 @@ L0: t = r4 r6 = box(int, x) r7 = box(int, y) - r8 = PyObject_CallFunctionObjArgs(t, r6, r7, 0) - r9 = unbox(None, r8) - return r9 + r8 = [r6, r7] + r9 = load_address r8 + r10 = PyObject_Vectorcall(t, r9, 2, 0) + keep_alive r6, r7 + r11 = unbox(None, r10) + return r11 [case testRecursiveFunction] from typing import Callable diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test index e1053397546f..c95e832cc5df 100644 --- a/mypyc/test-data/irbuild-singledispatch.test +++ b/mypyc/test-data/irbuild-singledispatch.test @@ -38,19 +38,23 @@ def f_obj.__call__(__mypyc_self__, arg): r8 :: str r9 :: object r10 :: dict - r11 :: object - r12 :: i32 - r13 :: bit - r14 :: object - r15 :: ptr + r11 :: object[2] + r12 :: object_ptr + r13 :: object + r14 :: i32 + r15 :: bit r16 :: object - r17 :: bit - r18 :: int + r17 :: ptr + r18 :: object r19 :: bit r20 :: int - r21 :: bool - r22 :: object + r21 :: bit + r22 :: int r23 :: bool + r24 :: object[1] + r25 :: object_ptr + r26 :: object + r27 :: bool L0: r0 = get_element_ptr arg ob_type :: PyObject r1 = load_mem r0 :: builtins.object* @@ -68,31 +72,37 @@ L2: r8 = '_find_impl' r9 = CPyObject_GetAttr(r7, r8) r10 = __mypyc_self__.registry - r11 = PyObject_CallFunctionObjArgs(r9, r1, r10, 0) - r12 = CPyDict_SetItem(r2, r1, r11) - r13 = r12 >= 0 :: signed - r6 = r11 + r11 = [r1, r10] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r9, r12, 2, 0) + keep_alive r1, r10 + r14 = CPyDict_SetItem(r2, r1, r13) + r15 = r14 >= 0 :: signed + r6 = r13 L3: - r14 = load_address PyLong_Type - r15 = get_element_ptr r6 ob_type :: PyObject - r16 = load_mem r15 :: builtins.object* + r16 = load_address PyLong_Type + r17 = get_element_ptr r6 ob_type :: PyObject + r18 = load_mem r17 :: builtins.object* keep_alive r6 - r17 = r16 == r14 - if r17 goto L4 else goto L7 :: bool + r19 = r18 == r16 + if r19 goto L4 else goto L7 :: bool L4: - r18 = unbox(int, r6) - r19 = int_eq r18, 0 - if r19 goto L5 else goto L6 :: bool + r20 = unbox(int, r6) + r21 = int_eq r20, 0 + if r21 goto L5 else goto L6 :: bool L5: - r20 = unbox(int, arg) - r21 = g(r20) - return r21 + r22 = unbox(int, arg) + r23 = g(r22) + return r23 L6: unreachable L7: - r22 = PyObject_CallFunctionObjArgs(r6, arg, 0) - r23 = unbox(bool, r22) - return r23 + r24 = [arg] + r25 = load_address r24 + r26 = PyObject_Vectorcall(r6, r25, 1, 0) + keep_alive arg + r27 = unbox(bool, r26) + return r27 def f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -129,7 +139,6 @@ def g(arg): L0: return 1 - [case testCallsToSingledispatchFunctionsAreNative] from functools import singledispatch @@ -170,16 +179,20 @@ def f_obj.__call__(__mypyc_self__, x): r8 :: str r9 :: object r10 :: dict - r11 :: object - r12 :: i32 - r13 :: bit - r14 :: object - r15 :: ptr + r11 :: object[2] + r12 :: object_ptr + r13 :: object + r14 :: i32 + r15 :: bit r16 :: object - r17 :: bit - r18 :: int - r19 :: object - r20 :: None + r17 :: ptr + r18 :: object + r19 :: bit + r20 :: int + r21 :: object[1] + r22 :: object_ptr + r23 :: object + r24 :: None L0: r0 = get_element_ptr x ob_type :: PyObject r1 = load_mem r0 :: builtins.object* @@ -197,24 +210,30 @@ L2: r8 = '_find_impl' r9 = CPyObject_GetAttr(r7, r8) r10 = __mypyc_self__.registry - r11 = PyObject_CallFunctionObjArgs(r9, r1, r10, 0) - r12 = CPyDict_SetItem(r2, r1, r11) - r13 = r12 >= 0 :: signed - r6 = r11 + r11 = [r1, r10] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r9, r12, 2, 0) + keep_alive r1, r10 + r14 = CPyDict_SetItem(r2, r1, r13) + r15 = r14 >= 0 :: signed + r6 = r13 L3: - r14 = load_address PyLong_Type - r15 = get_element_ptr r6 ob_type :: PyObject - r16 = load_mem r15 :: builtins.object* + r16 = load_address PyLong_Type + r17 = get_element_ptr r6 ob_type :: PyObject + r18 = load_mem r17 :: builtins.object* keep_alive r6 - r17 = r16 == r14 - if r17 goto L4 else goto L5 :: bool + r19 = r18 == r16 + if r19 goto L4 else goto L5 :: bool L4: - r18 = unbox(int, r6) + r20 = unbox(int, r6) unreachable L5: - r19 = PyObject_CallFunctionObjArgs(r6, x, 0) - r20 = unbox(None, r19) - return r20 + r21 = [x] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r6, r22, 1, 0) + keep_alive x + r24 = unbox(None, r23) + return r24 def f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index c85dcb09e80a..d5df984cfe4b 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -655,7 +655,10 @@ def complex_msg(x, s): r3 :: bit r4 :: object r5 :: str - r6, r7 :: object + r6 :: object + r7 :: object[1] + r8 :: object_ptr + r9 :: object L0: r0 = load_address _Py_NoneStruct r1 = x != r0 @@ -668,8 +671,11 @@ L2: r4 = builtins :: module r5 = 'AssertionError' r6 = CPyObject_GetAttr(r4, r5) - r7 = PyObject_CallFunctionObjArgs(r6, s, 0) - CPy_Raise(r7) + r7 = [s] + r8 = load_address r7 + r9 = PyObject_Vectorcall(r6, r8, 1, 0) + keep_alive s + CPy_Raise(r9) unreachable L3: return 1 diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index d17c66bba22f..35edc79f4ae5 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -322,24 +322,21 @@ def f(s): r16 :: bytes r17, r18 :: str r19 :: object - r20 :: str - r21 :: tuple - r22 :: dict - r23 :: object + r20 :: object[2] + r21 :: object_ptr + r22, r23 :: object r24 :: str r25 :: object - r26 :: str - r27 :: tuple - r28 :: dict - r29 :: object + r26 :: object[1] + r27 :: object_ptr + r28, r29 :: object r30 :: str r31 :: object - r32, r33 :: str - r34 :: tuple - r35 :: dict - r36 :: object - r37 :: str - r38 :: bytes + r32 :: object[2] + r33 :: object_ptr + r34, r35 :: object + r36 :: str + r37 :: bytes L0: r0 = PyUnicode_AsUTF8String(s) r1 = PyUnicode_AsUTF8String(s) @@ -363,25 +360,27 @@ L0: r17 = 'utf8' r18 = 'encode' r19 = CPyObject_GetAttr(s, r18) - r20 = 'errors' - r21 = PyTuple_Pack(1, r17) - r22 = CPyDict_Build(1, r20, errors) - r23 = PyObject_Call(r19, r21, r22) + r20 = [r17, errors] + r21 = load_address r20 + r22 = ('errors',) + r23 = PyObject_Vectorcall(r19, r21, 1, r22) + keep_alive r17, errors r24 = 'encode' r25 = CPyObject_GetAttr(s, r24) - r26 = 'errors' - r27 = PyTuple_Pack(0) - r28 = CPyDict_Build(1, r26, errors) - r29 = PyObject_Call(r25, r27, r28) + r26 = [errors] + r27 = load_address r26 + r28 = ('errors',) + r29 = PyObject_Vectorcall(r25, r27, 0, r28) + keep_alive errors r30 = 'encode' r31 = CPyObject_GetAttr(s, r30) - r32 = 'encoding' - r33 = 'errors' - r34 = PyTuple_Pack(0) - r35 = CPyDict_Build(2, r32, encoding, r33, errors) - r36 = PyObject_Call(r31, r34, r35) - r37 = 'latin2' - r38 = CPy_Encode(s, r37, 0) + r32 = [encoding, errors] + r33 = load_address r32 + r34 = ('encoding', 'errors') + r35 = PyObject_Vectorcall(r31, r33, 0, r34) + keep_alive encoding, errors + r36 = 'latin2' + r37 = CPy_Encode(s, r36, 0) return 1 [case testOrd] @@ -417,12 +416,18 @@ L0: def any_ord(x): x, r0 :: object r1 :: str - r2, r3 :: object - r4 :: int + r2 :: object + r3 :: object[1] + r4 :: object_ptr + r5 :: object + r6 :: int L0: r0 = builtins :: module r1 = 'ord' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, x, 0) - r4 = unbox(int, r3) - return r4 + r3 = [x] + r4 = load_address r3 + r5 = PyObject_Vectorcall(r2, r4, 1, 0) + keep_alive x + r6 = unbox(int, r5) + return r6 diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index a5b7b9a55b86..ad1aa78c0554 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -13,14 +13,17 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9 :: object - r10 :: bit + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bit L0: L1: r0 = builtins :: module r1 = 'object' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) goto L5 L2: (handler for L1) r4 = CPy_CatchError() @@ -28,13 +31,16 @@ L2: (handler for L1) r6 = builtins :: module r7 = 'print' r8 = CPyObject_GetAttr(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) + keep_alive r5 L3: CPy_RestoreExcInfo(r4) goto L5 L4: (handler for L2) CPy_RestoreExcInfo(r4) - r10 = CPy_KeepPropagating() + r12 = CPy_KeepPropagating() unreachable L5: return 1 @@ -59,8 +65,11 @@ def g(b): r7 :: str r8 :: object r9 :: str - r10, r11 :: object - r12 :: bit + r10 :: object + r11 :: object[1] + r12 :: object_ptr + r13 :: object + r14 :: bit L0: L1: if b goto L2 else goto L3 :: bool @@ -68,7 +77,7 @@ L2: r0 = builtins :: module r1 = 'object' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) goto L4 L3: r4 = 'hi' @@ -81,13 +90,16 @@ L5: (handler for L1, L2, L3, L4) r8 = builtins :: module r9 = 'print' r10 = CPyObject_GetAttr(r8, r9) - r11 = PyObject_CallFunctionObjArgs(r10, r7, 0) + r11 = [r7] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r10, r12, 1, 0) + keep_alive r7 L6: CPy_RestoreExcInfo(r6) goto L8 L7: (handler for L5) CPy_RestoreExcInfo(r6) - r12 = CPy_KeepPropagating() + r14 = CPy_KeepPropagating() unreachable L8: return 1 @@ -107,80 +119,98 @@ def g(): r0 :: str r1 :: object r2 :: str - r3, r4, r5 :: object - r6 :: str - r7, r8 :: object - r9 :: tuple[object, object, object] - r10 :: object - r11 :: str + r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6, r7 :: object + r8 :: str + r9, r10 :: object + r11 :: tuple[object, object, object] r12 :: object - r13 :: bit - r14, e :: object - r15 :: str - r16 :: object + r13 :: str + r14 :: object + r15 :: bit + r16, e :: object r17 :: str - r18, r19 :: object - r20 :: bit - r21 :: tuple[object, object, object] - r22 :: str + r18 :: object + r19 :: str + r20 :: object + r21 :: object[2] + r22 :: object_ptr r23 :: object - r24 :: str - r25, r26 :: object - r27 :: bit + r24 :: bit + r25 :: tuple[object, object, object] + r26 :: str + r27 :: object + r28 :: str + r29 :: object + r30 :: object[1] + r31 :: object_ptr + r32 :: object + r33 :: bit L0: L1: r0 = 'a' r1 = builtins :: module r2 = 'print' r3 = CPyObject_GetAttr(r1, r2) - r4 = PyObject_CallFunctionObjArgs(r3, r0, 0) + r4 = [r0] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r3, r5, 1, 0) + keep_alive r0 L2: - r5 = builtins :: module - r6 = 'object' - r7 = CPyObject_GetAttr(r5, r6) - r8 = PyObject_CallFunctionObjArgs(r7, 0) + r7 = builtins :: module + r8 = 'object' + r9 = CPyObject_GetAttr(r7, r8) + r10 = PyObject_Vectorcall(r9, 0, 0, 0) goto L8 L3: (handler for L2) - r9 = CPy_CatchError() - r10 = builtins :: module - r11 = 'AttributeError' - r12 = CPyObject_GetAttr(r10, r11) - r13 = CPy_ExceptionMatches(r12) - if r13 goto L4 else goto L5 :: bool + r11 = CPy_CatchError() + r12 = builtins :: module + r13 = 'AttributeError' + r14 = CPyObject_GetAttr(r12, r13) + r15 = CPy_ExceptionMatches(r14) + if r15 goto L4 else goto L5 :: bool L4: - r14 = CPy_GetExcValue() - e = r14 - r15 = 'b' - r16 = builtins :: module - r17 = 'print' - r18 = CPyObject_GetAttr(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, e, 0) + r16 = CPy_GetExcValue() + e = r16 + r17 = 'b' + r18 = builtins :: module + r19 = 'print' + r20 = CPyObject_GetAttr(r18, r19) + r21 = [r17, e] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r20, r22, 2, 0) + keep_alive r17, e goto L6 L5: CPy_Reraise() unreachable L6: - CPy_RestoreExcInfo(r9) + CPy_RestoreExcInfo(r11) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r9) - r20 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r11) + r24 = CPy_KeepPropagating() unreachable L8: goto L12 L9: (handler for L1, L6, L7, L8) - r21 = CPy_CatchError() - r22 = 'weeee' - r23 = builtins :: module - r24 = 'print' - r25 = CPyObject_GetAttr(r23, r24) - r26 = PyObject_CallFunctionObjArgs(r25, r22, 0) + r25 = CPy_CatchError() + r26 = 'weeee' + r27 = builtins :: module + r28 = 'print' + r29 = CPyObject_GetAttr(r27, r28) + r30 = [r26] + r31 = load_address r30 + r32 = PyObject_Vectorcall(r29, r31, 1, 0) + keep_alive r26 L10: - CPy_RestoreExcInfo(r21) + CPy_RestoreExcInfo(r25) goto L12 L11: (handler for L9) - CPy_RestoreExcInfo(r21) - r27 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r25) + r33 = CPy_KeepPropagating() unreachable L12: return 1 @@ -203,15 +233,21 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9, r10 :: object - r11 :: str - r12 :: object - r13 :: bit - r14 :: str - r15 :: object + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11, r12 :: object + r13 :: str + r14 :: object + r15 :: bit r16 :: str - r17, r18 :: object - r19 :: bit + r17 :: object + r18 :: str + r19 :: object + r20 :: object[1] + r21 :: object_ptr + r22 :: object + r23 :: bit L0: L1: goto L9 @@ -227,20 +263,26 @@ L3: r6 = builtins :: module r7 = 'print' r8 = CPyObject_GetAttr(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) + keep_alive r5 goto L7 L4: - r10 = builtins :: module - r11 = 'IndexError' - r12 = CPyObject_GetAttr(r10, r11) - r13 = CPy_ExceptionMatches(r12) - if r13 goto L5 else goto L6 :: bool + r12 = builtins :: module + r13 = 'IndexError' + r14 = CPyObject_GetAttr(r12, r13) + r15 = CPy_ExceptionMatches(r14) + if r15 goto L5 else goto L6 :: bool L5: - r14 = 'yo' - r15 = builtins :: module - r16 = 'print' - r17 = CPyObject_GetAttr(r15, r16) - r18 = PyObject_CallFunctionObjArgs(r17, r14, 0) + r16 = 'yo' + r17 = builtins :: module + r18 = 'print' + r19 = CPyObject_GetAttr(r17, r18) + r20 = [r16] + r21 = load_address r20 + r22 = PyObject_Vectorcall(r19, r21, 1, 0) + keep_alive r16 goto L7 L6: CPy_Reraise() @@ -250,7 +292,7 @@ L7: goto L9 L8: (handler for L2, L3, L4, L5, L6) CPy_RestoreExcInfo(r0) - r19 = CPy_KeepPropagating() + r23 = CPy_KeepPropagating() unreachable L9: return 1 @@ -268,13 +310,19 @@ def a(b): r0 :: str r1 :: object r2 :: str - r3, r4 :: object - r5, r6, r7 :: tuple[object, object, object] - r8 :: str - r9 :: object + r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7, r8, r9 :: tuple[object, object, object] r10 :: str - r11, r12 :: object - r13 :: bit + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17 :: bit L0: L1: if b goto L2 else goto L3 :: bool @@ -283,36 +331,42 @@ L2: r1 = builtins :: module r2 = 'Exception' r3 = CPyObject_GetAttr(r1, r2) - r4 = PyObject_CallFunctionObjArgs(r3, r0, 0) - CPy_Raise(r4) + r4 = [r0] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r3, r5, 1, 0) + keep_alive r0 + CPy_Raise(r6) unreachable L3: L4: L5: - r5 = :: tuple[object, object, object] - r6 = r5 + r7 = :: tuple[object, object, object] + r8 = r7 goto L7 L6: (handler for L1, L2, L3) - r7 = CPy_CatchError() - r6 = r7 + r9 = CPy_CatchError() + r8 = r9 L7: - r8 = 'finally' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) - if is_error(r6) goto L9 else goto L8 + r10 = 'finally' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 + if is_error(r8) goto L9 else goto L8 L8: CPy_Reraise() unreachable L9: goto L13 L10: (handler for L7, L8) - if is_error(r6) goto L12 else goto L11 + if is_error(r8) goto L12 else goto L11 L11: - CPy_RestoreExcInfo(r6) + CPy_RestoreExcInfo(r8) L12: - r13 = CPy_KeepPropagating() + r17 = CPy_KeepPropagating() unreachable L13: return 1 @@ -328,90 +382,114 @@ def foo(x): r2 :: str r3 :: object r4 :: str - r5, r6 :: object - r7 :: bool + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: bool y :: object - r8 :: str - r9 :: object r10 :: str - r11, r12 :: object - r13, r14 :: tuple[object, object, object] - r15, r16, r17, r18 :: object - r19 :: i32 - r20 :: bit - r21 :: bool - r22 :: bit - r23, r24, r25 :: tuple[object, object, object] - r26, r27 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17, r18 :: tuple[object, object, object] + r19, r20, r21 :: object + r22 :: object[4] + r23 :: object_ptr + r24 :: object + r25 :: i32 + r26 :: bit + r27 :: bool r28 :: bit + r29, r30, r31 :: tuple[object, object, object] + r32 :: object + r33 :: object[4] + r34 :: object_ptr + r35 :: object + r36 :: bit L0: - r0 = PyObject_CallFunctionObjArgs(x, 0) + r0 = PyObject_Vectorcall(x, 0, 0, 0) r1 = PyObject_Type(r0) r2 = '__exit__' r3 = CPyObject_GetAttr(r1, r2) r4 = '__enter__' r5 = CPyObject_GetAttr(r1, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r0, 0) - r7 = 1 + r6 = [r0] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r0 + r9 = 1 L1: L2: - y = r6 - r8 = 'hello' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + y = r8 + r10 = 'hello' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 goto L8 L3: (handler for L2) - r13 = CPy_CatchError() - r7 = 0 - r14 = CPy_GetExcInfo() - r15 = r14[0] - r16 = r14[1] - r17 = r14[2] - r18 = PyObject_CallFunctionObjArgs(r3, r0, r15, r16, r17, 0) - r19 = PyObject_IsTrue(r18) - r20 = r19 >= 0 :: signed - r21 = truncate r19: i32 to builtins.bool - if r21 goto L5 else goto L4 :: bool + r17 = CPy_CatchError() + r9 = 0 + r18 = CPy_GetExcInfo() + r19 = r18[0] + r20 = r18[1] + r21 = r18[2] + r22 = [r0, r19, r20, r21] + r23 = load_address r22 + r24 = PyObject_Vectorcall(r3, r23, 4, 0) + keep_alive r0, r19, r20, r21 + r25 = PyObject_IsTrue(r24) + r26 = r25 >= 0 :: signed + r27 = truncate r25: i32 to builtins.bool + if r27 goto L5 else goto L4 :: bool L4: CPy_Reraise() unreachable L5: L6: - CPy_RestoreExcInfo(r13) + CPy_RestoreExcInfo(r17) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r13) - r22 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r17) + r28 = CPy_KeepPropagating() unreachable L8: L9: L10: - r23 = :: tuple[object, object, object] - r24 = r23 + r29 = :: tuple[object, object, object] + r30 = r29 goto L12 L11: (handler for L1, L6, L7, L8) - r25 = CPy_CatchError() - r24 = r25 + r31 = CPy_CatchError() + r30 = r31 L12: - if r7 goto L13 else goto L14 :: bool + if r9 goto L13 else goto L14 :: bool L13: - r26 = load_address _Py_NoneStruct - r27 = PyObject_CallFunctionObjArgs(r3, r0, r26, r26, r26, 0) + r32 = load_address _Py_NoneStruct + r33 = [r0, r32, r32, r32] + r34 = load_address r33 + r35 = PyObject_Vectorcall(r3, r34, 4, 0) + keep_alive r0, r32, r32, r32 L14: - if is_error(r24) goto L16 else goto L15 + if is_error(r30) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r24) goto L19 else goto L18 + if is_error(r30) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r24) + CPy_RestoreExcInfo(r30) L19: - r28 = CPy_KeepPropagating() + r36 = CPy_KeepPropagating() unreachable L20: return 1 @@ -443,19 +521,22 @@ def foo(x): r2 :: str r3 :: object r4 :: str - r5, r6 :: object - r7, r8 :: tuple[object, object, object] - r9, r10, r11 :: object - r12 :: None - r13 :: object - r14 :: i32 - r15 :: bit - r16 :: bool + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9, r10 :: tuple[object, object, object] + r11, r12, r13 :: object + r14 :: None + r15 :: object + r16 :: i32 r17 :: bit - r18, r19, r20 :: tuple[object, object, object] - r21 :: object - r22 :: None - r23 :: bit + r18 :: bool + r19 :: bit + r20, r21, r22 :: tuple[object, object, object] + r23 :: object + r24 :: None + r25 :: bit L0: r0 = x.__enter__() r1 = 1 @@ -465,59 +546,62 @@ L2: r3 = builtins :: module r4 = 'print' r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) + r6 = [r2] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r2 goto L8 L3: (handler for L2) - r7 = CPy_CatchError() + r9 = CPy_CatchError() r1 = 0 - r8 = CPy_GetExcInfo() - r9 = r8[0] - r10 = r8[1] - r11 = r8[2] - r12 = x.__exit__(r9, r10, r11) - r13 = box(None, r12) - r14 = PyObject_IsTrue(r13) - r15 = r14 >= 0 :: signed - r16 = truncate r14: i32 to builtins.bool - if r16 goto L5 else goto L4 :: bool + r10 = CPy_GetExcInfo() + r11 = r10[0] + r12 = r10[1] + r13 = r10[2] + r14 = x.__exit__(r11, r12, r13) + r15 = box(None, r14) + r16 = PyObject_IsTrue(r15) + r17 = r16 >= 0 :: signed + r18 = truncate r16: i32 to builtins.bool + if r18 goto L5 else goto L4 :: bool L4: CPy_Reraise() unreachable L5: L6: - CPy_RestoreExcInfo(r7) + CPy_RestoreExcInfo(r9) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r7) - r17 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r9) + r19 = CPy_KeepPropagating() unreachable L8: L9: L10: - r18 = :: tuple[object, object, object] - r19 = r18 + r20 = :: tuple[object, object, object] + r21 = r20 goto L12 L11: (handler for L1, L6, L7, L8) - r20 = CPy_CatchError() - r19 = r20 + r22 = CPy_CatchError() + r21 = r22 L12: if r1 goto L13 else goto L14 :: bool L13: - r21 = load_address _Py_NoneStruct - r22 = x.__exit__(r21, r21, r21) + r23 = load_address _Py_NoneStruct + r24 = x.__exit__(r23, r23, r23) L14: - if is_error(r19) goto L16 else goto L15 + if is_error(r21) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r19) goto L19 else goto L18 + if is_error(r21) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r19) + CPy_RestoreExcInfo(r21) L19: - r23 = CPy_KeepPropagating() + r25 = CPy_KeepPropagating() unreachable L20: return 1 diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test index b5188c91ac58..7209c00ce75d 100644 --- a/mypyc/test-data/irbuild-unreachable.test +++ b/mypyc/test-data/irbuild-unreachable.test @@ -205,7 +205,7 @@ L1: r0 = builtins :: module r1 = 'ValueError' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) CPy_Raise(r3) unreachable L2: @@ -223,7 +223,10 @@ def f(x): r1 :: str r2 :: object r3 :: str - r4, r5 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object L0: if x goto L1 else goto L4 :: bool L1: @@ -236,6 +239,9 @@ L3: r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 L4: return 4 diff --git a/mypyc/test-data/irbuild-vectorcall.test b/mypyc/test-data/irbuild-vectorcall.test index 1ba08efc2501..dec8c95f46d1 100644 --- a/mypyc/test-data/irbuild-vectorcall.test +++ b/mypyc/test-data/irbuild-vectorcall.test @@ -3,7 +3,7 @@ -- Vectorcalls are faster than the legacy API, especially with keyword arguments, -- since there is no need to allocate a temporary dictionary for keyword args. -[case testeVectorcallBasic_python3_8] +[case testeVectorcallBasic] from typing import Any def f(c: Any) -> None: @@ -17,16 +17,16 @@ def f(c): r4 :: object_ptr r5 :: object L0: - r0 = _PyObject_Vectorcall(c, 0, 0, 0) + r0 = PyObject_Vectorcall(c, 0, 0, 0) r1 = 'x' r2 = 'y' r3 = [r1, r2] r4 = load_address r3 - r5 = _PyObject_Vectorcall(c, r4, 2, 0) + r5 = PyObject_Vectorcall(c, r4, 2, 0) keep_alive r1, r2 return 1 -[case testVectorcallKeywords_python3_8] +[case testVectorcallKeywords] from typing import Any def f(c: Any) -> None: @@ -48,7 +48,7 @@ L0: r1 = [r0] r2 = load_address r1 r3 = ('x',) - r4 = _PyObject_Vectorcall(c, r2, 0, r3) + r4 = PyObject_Vectorcall(c, r2, 0, r3) keep_alive r0 r5 = 'x' r6 = 'y' @@ -56,11 +56,11 @@ L0: r8 = [r5, r6, r7] r9 = load_address r8 r10 = ('a', 'b') - r11 = _PyObject_Vectorcall(c, r9, 1, r10) + r11 = PyObject_Vectorcall(c, r9, 1, r10) keep_alive r5, r6, r7 return 1 -[case testVectorcallMethod_python3_8] +[case testVectorcallMethod] from typing import Any def f(o: Any) -> None: @@ -88,7 +88,7 @@ L0: r7 = [r3, r4] r8 = load_address r7 r9 = ('a',) - r10 = _PyObject_Vectorcall(r6, r8, 1, r9) + r10 = PyObject_Vectorcall(r6, r8, 1, r9) keep_alive r3, r4 return 1 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index c84ddfd73ba2..6b2b3d05fc19 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -684,22 +684,18 @@ def g(x: str) -> int: [out] def g(x): x :: str - r0 :: object - r1 :: str - r2 :: tuple - r3 :: object - r4 :: dict - r5 :: object + r0, r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4, r5 :: object r6 :: int L0: r0 = load_address PyLong_Type - r1 = 'base' - r2 = PyTuple_Pack(1, x) - r3 = object 2 - r4 = CPyDict_Build(1, r1, r3) - r5 = PyObject_Call(r0, r2, r4) - dec_ref r2 - dec_ref r4 + r1 = object 2 + r2 = [x, r1] + r3 = load_address r2 + r4 = ('base',) + r5 = PyObject_Vectorcall(r0, r3, 1, r4) r6 = unbox(int, r5) dec_ref r5 return r6 @@ -875,7 +871,7 @@ L11: xdec_ref y :: int goto L6 -[case testVectorcall_python3_8] +[case testVectorcall] from typing import Any def call(f: Any, x: int) -> int: @@ -894,7 +890,7 @@ L0: r0 = box(int, x) r1 = [r0] r2 = load_address r1 - r3 = _PyObject_Vectorcall(f, r2, 1, 0) + r3 = PyObject_Vectorcall(f, r2, 1, 0) dec_ref r0 r4 = unbox(int, r3) dec_ref r3 diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index da6d7fc71a9d..a5f704966338 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -103,13 +103,13 @@ def build_ir_for_single_file2( # By default generate IR compatible with the earliest supported Python C API. # If a test needs more recent API features, this should be overridden. - compiler_options = compiler_options or CompilerOptions(capi_version=(3, 7)) + compiler_options = compiler_options or CompilerOptions(capi_version=(3, 8)) options = Options() options.show_traceback = True options.hide_error_codes = True options.use_builtins_fixtures = True options.strict_optional = True - options.python_version = compiler_options.python_version or (3, 6) + options.python_version = compiler_options.python_version or (3, 8) options.export_types = True options.preserve_asts = True options.allow_empty_bodies = True @@ -273,8 +273,8 @@ def infer_ir_build_options_from_test_name(name: str) -> CompilerOptions | None: return None if "_32bit" in name and not IS_32_BIT_PLATFORM: return None - options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 7)) - # A suffix like _python3.8 is used to set the target C API version. + options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 8)) + # A suffix like _python3_8 is used to set the target C API version. m = re.search(r"_python([3-9]+)_([0-9]+)(_|\b)", name) if m: options.capi_version = (int(m.group(1)), int(m.group(2))) From dd73273d9332f489f4e1c7e2b06eab00db952f48 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:10:47 +0100 Subject: [PATCH 1082/1617] Update capi_version for mypyc tests to 3.9 (#18546) Followup to #18341 This PR updates the `capi_version` used for mypyc tests to `3.9` (mypy / mypyc requires `>=3.9`). For Python 3.9+ mypyc uses `PyObject_VectorcallMethod` instead of `CPyObject_CallMethodObjArgs` and select `PyObject_Vectorcall` where ever possible. Will remove the now unnecessary `use_method_vectorcall` check in a followup. https://github.com/python/mypy/blob/42e005c999d8341c0da6d7b93b10d05f2db2099c/mypyc/common.py#L114-L116 --- mypyc/test-data/irbuild-basic.test | 88 +++++++++--------- mypyc/test-data/irbuild-classes.test | 35 +++++--- mypyc/test-data/irbuild-str.test | 115 ++++++++++++------------ mypyc/test-data/irbuild-vectorcall.test | 36 +------- mypyc/test-data/refcount.test | 2 +- mypyc/test/testutil.py | 6 +- 6 files changed, 134 insertions(+), 148 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 075e6386663b..6e5267fc34dd 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -750,7 +750,7 @@ L0: r1 = b'1234' return r1 -[case testPyMethodCall1] +[case testPyMethodCall1_64bit] from typing import Any def f(x: Any) -> int: y: int = x.pop() @@ -759,20 +759,30 @@ def f(x: Any) -> int: def f(x): x :: object r0 :: str - r1 :: object - r2, y :: int - r3 :: str - r4 :: object - r5 :: int + r1 :: object[1] + r2 :: object_ptr + r3 :: object + r4, y :: int + r5 :: str + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: int L0: r0 = 'pop' - r1 = CPyObject_CallMethodObjArgs(x, r0, 0) - r2 = unbox(int, r1) - y = r2 - r3 = 'pop' - r4 = CPyObject_CallMethodObjArgs(x, r3, 0) - r5 = unbox(int, r4) - return r5 + r1 = [x] + r2 = load_address r1 + r3 = PyObject_VectorcallMethod(r0, r2, 9223372036854775809, 0) + keep_alive x + r4 = unbox(int, r3) + y = r4 + r5 = 'pop' + r6 = [x] + r7 = load_address r6 + r8 = PyObject_VectorcallMethod(r5, r7, 9223372036854775809, 0) + keep_alive x + r9 = unbox(int, r8) + return r9 [case testObjectType] def g(y: object) -> None: @@ -1167,7 +1177,7 @@ L0: r2 = unbox(float, r1) return r2 -[case testCallableTypesWithKeywordArgs] +[case testCallableTypesWithKeywordArgs_64bit] from typing import List def call_python_function_with_keyword_arg(x: str) -> int: @@ -1200,34 +1210,32 @@ def call_python_method_with_keyword_args(xs, first, second): xs :: list first, second :: int r0 :: str - r1, r2, r3 :: object - r4 :: object[2] - r5 :: object_ptr - r6, r7 :: object - r8 :: str - r9, r10, r11 :: object - r12 :: object[2] - r13 :: object_ptr - r14, r15 :: object + r1, r2 :: object + r3 :: object[3] + r4 :: object_ptr + r5, r6 :: object + r7 :: str + r8, r9 :: object + r10 :: object[3] + r11 :: object_ptr + r12, r13 :: object L0: r0 = 'insert' - r1 = CPyObject_GetAttr(xs, r0) - r2 = object 0 - r3 = box(int, first) - r4 = [r2, r3] - r5 = load_address r4 - r6 = ('x',) - r7 = PyObject_Vectorcall(r1, r5, 1, r6) - keep_alive r2, r3 - r8 = 'insert' - r9 = CPyObject_GetAttr(xs, r8) - r10 = box(int, second) - r11 = object 1 - r12 = [r10, r11] - r13 = load_address r12 - r14 = ('x', 'i') - r15 = PyObject_Vectorcall(r9, r13, 0, r14) - keep_alive r10, r11 + r1 = object 0 + r2 = box(int, first) + r3 = [xs, r1, r2] + r4 = load_address r3 + r5 = ('x',) + r6 = PyObject_VectorcallMethod(r0, r4, 9223372036854775810, r5) + keep_alive xs, r1, r2 + r7 = 'insert' + r8 = box(int, second) + r9 = object 1 + r10 = [xs, r8, r9] + r11 = load_address r10 + r12 = ('x', 'i') + r13 = PyObject_VectorcallMethod(r7, r11, 9223372036854775809, r12) + keep_alive xs, r8, r9 return xs [case testObjectAsBoolean] diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 2364b508aad9..605ab46181e2 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -606,7 +606,7 @@ L0: r3 = CPyTagged_Add(r0, r2) return r3 -[case testCallClassMethodViaCls] +[case testCallClassMethodViaCls_64bit] class C: @classmethod def f(cls, x: int) -> int: @@ -647,14 +647,20 @@ def D.f(cls, x): cls :: object x :: int r0 :: str - r1, r2 :: object - r3 :: int + r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4 :: object + r5 :: int L0: r0 = 'g' r1 = box(int, x) - r2 = CPyObject_CallMethodObjArgs(cls, r0, r1, 0) - r3 = unbox(int, r2) - return r3 + r2 = [cls, r1] + r3 = load_address r2 + r4 = PyObject_VectorcallMethod(r0, r3, 9223372036854775810, 0) + keep_alive cls, r1 + r5 = unbox(int, r4) + return r5 def D.g(cls, x): cls :: object x :: int @@ -904,7 +910,7 @@ L0: r1 = unbox(bool, r0) return r1 -[case testEqDefinedLater] +[case testEqDefinedLater_64bit] def f(a: 'Base', b: 'Base') -> bool: return a == b @@ -951,13 +957,18 @@ L0: def fOpt2(a, b): a, b :: __main__.Derived r0 :: str - r1 :: object - r2 :: bool + r1 :: object[2] + r2 :: object_ptr + r3 :: object + r4 :: bool L0: r0 = '__ne__' - r1 = CPyObject_CallMethodObjArgs(a, r0, b, 0) - r2 = unbox(bool, r1) - return r2 + r1 = [a, b] + r2 = load_address r1 + r3 = PyObject_VectorcallMethod(r0, r2, 9223372036854775810, 0) + keep_alive a, b + r4 = unbox(bool, r3) + return r4 def Derived.__eq__(self, other): self :: __main__.Derived other, r0 :: object diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 35edc79f4ae5..af77a351fb62 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -189,7 +189,7 @@ L0: s4 = r13 return 1 -[case testFStrings] +[case testFStrings_64bit] def f(var: str, num: int) -> None: s1 = f"Hi! I'm {var}. I am {num} years old." s2 = f'Hello {var:>{num}}' @@ -200,11 +200,13 @@ def f(var, num): var :: str num :: int r0, r1, r2, r3, r4, s1, r5, r6, r7, r8, r9, r10, r11 :: str - r12 :: object - r13 :: str - r14 :: list - r15 :: ptr - r16, s2, r17, s3, r18, s4 :: str + r12 :: object[3] + r13 :: object_ptr + r14 :: object + r15 :: str + r16 :: list + r17 :: ptr + r18, s2, r19, s3, r20, s4 :: str L0: r0 = "Hi! I'm " r1 = '. I am ' @@ -219,19 +221,22 @@ L0: r9 = CPyTagged_Str(num) r10 = CPyStr_Build(2, r8, r9) r11 = 'format' - r12 = CPyObject_CallMethodObjArgs(r7, r11, var, r10, 0) - r13 = cast(str, r12) - r14 = PyList_New(2) - r15 = list_items r14 - buf_init_item r15, 0, r6 - buf_init_item r15, 1, r13 - keep_alive r14 - r16 = PyUnicode_Join(r5, r14) - s2 = r16 - r17 = '' - s3 = r17 - r18 = 'abc' - s4 = r18 + r12 = [r7, var, r10] + r13 = load_address r12 + r14 = PyObject_VectorcallMethod(r11, r13, 9223372036854775811, 0) + keep_alive r7, var, r10 + r15 = cast(str, r14) + r16 = PyList_New(2) + r17 = list_items r16 + buf_init_item r17, 0, r6 + buf_init_item r17, 1, r15 + keep_alive r16 + r18 = PyUnicode_Join(r5, r16) + s2 = r18 + r19 = '' + s3 = r19 + r20 = 'abc' + s4 = r20 return 1 [case testStringFormattingCStyle] @@ -289,7 +294,7 @@ L0: r5 = CPy_Decode(b, r3, r4) return 1 -[case testEncode] +[case testEncode_64bit] def f(s: str) -> None: s.encode() s.encode('utf-8') @@ -321,22 +326,19 @@ def f(s): r14, errors, r15 :: str r16 :: bytes r17, r18 :: str - r19 :: object - r20 :: object[2] - r21 :: object_ptr - r22, r23 :: object - r24 :: str - r25 :: object - r26 :: object[1] - r27 :: object_ptr - r28, r29 :: object - r30 :: str - r31 :: object - r32 :: object[2] - r33 :: object_ptr - r34, r35 :: object - r36 :: str - r37 :: bytes + r19 :: object[3] + r20 :: object_ptr + r21, r22 :: object + r23 :: str + r24 :: object[2] + r25 :: object_ptr + r26, r27 :: object + r28 :: str + r29 :: object[3] + r30 :: object_ptr + r31, r32 :: object + r33 :: str + r34 :: bytes L0: r0 = PyUnicode_AsUTF8String(s) r1 = PyUnicode_AsUTF8String(s) @@ -359,28 +361,25 @@ L0: r16 = CPy_Encode(s, r15, errors) r17 = 'utf8' r18 = 'encode' - r19 = CPyObject_GetAttr(s, r18) - r20 = [r17, errors] - r21 = load_address r20 - r22 = ('errors',) - r23 = PyObject_Vectorcall(r19, r21, 1, r22) - keep_alive r17, errors - r24 = 'encode' - r25 = CPyObject_GetAttr(s, r24) - r26 = [errors] - r27 = load_address r26 - r28 = ('errors',) - r29 = PyObject_Vectorcall(r25, r27, 0, r28) - keep_alive errors - r30 = 'encode' - r31 = CPyObject_GetAttr(s, r30) - r32 = [encoding, errors] - r33 = load_address r32 - r34 = ('encoding', 'errors') - r35 = PyObject_Vectorcall(r31, r33, 0, r34) - keep_alive encoding, errors - r36 = 'latin2' - r37 = CPy_Encode(s, r36, 0) + r19 = [s, r17, errors] + r20 = load_address r19 + r21 = ('errors',) + r22 = PyObject_VectorcallMethod(r18, r20, 9223372036854775810, r21) + keep_alive s, r17, errors + r23 = 'encode' + r24 = [s, errors] + r25 = load_address r24 + r26 = ('errors',) + r27 = PyObject_VectorcallMethod(r23, r25, 9223372036854775809, r26) + keep_alive s, errors + r28 = 'encode' + r29 = [s, encoding, errors] + r30 = load_address r29 + r31 = ('encoding', 'errors') + r32 = PyObject_VectorcallMethod(r28, r30, 9223372036854775809, r31) + keep_alive s, encoding, errors + r33 = 'latin2' + r34 = CPy_Encode(s, r33, 0) return 1 [case testOrd] diff --git a/mypyc/test-data/irbuild-vectorcall.test b/mypyc/test-data/irbuild-vectorcall.test index dec8c95f46d1..15e717191ff0 100644 --- a/mypyc/test-data/irbuild-vectorcall.test +++ b/mypyc/test-data/irbuild-vectorcall.test @@ -60,39 +60,7 @@ L0: keep_alive r5, r6, r7 return 1 -[case testVectorcallMethod] -from typing import Any - -def f(o: Any) -> None: - # On Python 3.8 vectorcalls are only faster with keyword args - o.m('x') - o.m('x', a='y') -[out] -def f(o): - o :: object - r0, r1 :: str - r2 :: object - r3, r4, r5 :: str - r6 :: object - r7 :: object[2] - r8 :: object_ptr - r9, r10 :: object -L0: - r0 = 'x' - r1 = 'm' - r2 = CPyObject_CallMethodObjArgs(o, r1, r0, 0) - r3 = 'x' - r4 = 'y' - r5 = 'm' - r6 = CPyObject_GetAttr(o, r5) - r7 = [r3, r4] - r8 = load_address r7 - r9 = ('a',) - r10 = PyObject_Vectorcall(r6, r8, 1, r9) - keep_alive r3, r4 - return 1 - -[case testVectorcallMethod_python3_9_64bit] +[case testVectorcallMethod_64bit] from typing import Any def f(o: Any) -> None: @@ -128,7 +96,7 @@ L0: keep_alive o, r5, r6, r7 return 1 -[case testVectorcallMethod_python3_9_32bit] +[case testVectorcallMethod_32bit] from typing import Any def f(o: Any) -> None: diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 6b2b3d05fc19..e757b3684c79 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -896,7 +896,7 @@ L0: dec_ref r3 return r4 -[case testVectorcallMethod_python3_9_64bit] +[case testVectorcallMethod_64bit] from typing import Any def call(o: Any, x: int) -> int: diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index a5f704966338..65a29c4b1218 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -103,7 +103,7 @@ def build_ir_for_single_file2( # By default generate IR compatible with the earliest supported Python C API. # If a test needs more recent API features, this should be overridden. - compiler_options = compiler_options or CompilerOptions(capi_version=(3, 8)) + compiler_options = compiler_options or CompilerOptions(capi_version=(3, 9)) options = Options() options.show_traceback = True options.hide_error_codes = True @@ -273,8 +273,8 @@ def infer_ir_build_options_from_test_name(name: str) -> CompilerOptions | None: return None if "_32bit" in name and not IS_32_BIT_PLATFORM: return None - options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 8)) - # A suffix like _python3_8 is used to set the target C API version. + options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 9)) + # A suffix like _python3_9 is used to set the target C API version. m = re.search(r"_python([3-9]+)_([0-9]+)(_|\b)", name) if m: options.capi_version = (int(m.group(1)), int(m.group(2))) From ae5689280283ae9b22c105f2e34f69e37f54f2f0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:44:10 +0100 Subject: [PATCH 1083/1617] Update pythoncapi_compat.h (#18535) https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h --- mypyc/lib-rt/pythoncapi_compat.h | 264 ++++++++++++++++++++++++++++++- 1 file changed, 263 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index cee282d7efed..4d2884622f1f 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -19,6 +19,7 @@ extern "C" { #endif #include +#include // offsetof() // Python 3.11.0b4 added PyFrame_Back() to Python.h #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) @@ -583,7 +584,7 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) return 0; } *pobj = Py_NewRef(obj); - return (*pobj != NULL); + return 1; } #endif @@ -1933,6 +1934,267 @@ PyLongWriter_Finish(PyLongWriter *writer) #endif +// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 +#if PY_VERSION_HEX < 0x030E00A4 +static inline FILE* Py_fopen(PyObject *path, const char *mode) +{ +#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) + extern FILE* _Py_fopen_obj(PyObject *path, const char *mode); + return _Py_fopen_obj(path, mode); +#else + FILE *f; + PyObject *bytes; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_FSConverter(path, &bytes)) { + return NULL; + } +#else + if (!PyString_Check(path)) { + PyErr_SetString(PyExc_TypeError, "except str"); + return NULL; + } + bytes = Py_NewRef(path); +#endif + const char *path_bytes = PyBytes_AS_STRING(bytes); + + f = fopen(path_bytes, mode); + Py_DECREF(bytes); + + if (f == NULL) { + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); + return NULL; + } + return f; +#endif +} + +static inline int Py_fclose(FILE *file) +{ + return fclose(file); +} +#endif + + +#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +static inline PyObject* +PyConfig_Get(const char *name) +{ + typedef enum { + _PyConfig_MEMBER_INT, + _PyConfig_MEMBER_UINT, + _PyConfig_MEMBER_ULONG, + _PyConfig_MEMBER_BOOL, + _PyConfig_MEMBER_WSTR, + _PyConfig_MEMBER_WSTR_OPT, + _PyConfig_MEMBER_WSTR_LIST, + } PyConfigMemberType; + + typedef struct { + const char *name; + size_t offset; + PyConfigMemberType type; + const char *sys_attr; + } PyConfigSpec; + +#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ + {#MEMBER, offsetof(PyConfig, MEMBER), \ + _PyConfig_MEMBER_##TYPE, sys_attr} + + static const PyConfigSpec config_spec[] = { + PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), + PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"), + PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"), + PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), + PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), + PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), + PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), + PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), + PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), +#endif + PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), + PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), + PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), +#if 0x030D0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), +#endif +#ifdef Py_GIL_DISABLED + PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL), +#ifdef MS_WINDOWS + PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), +#endif + PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), +#endif + }; + +#undef PYTHONCAPI_COMPAT_SPEC + + const PyConfigSpec *spec; + int found = 0; + for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { + spec = &config_spec[i]; + if (strcmp(spec->name, name) == 0) { + found = 1; + break; + } + } + if (found) { + if (spec->sys_attr != NULL) { + PyObject *value = PySys_GetObject(spec->sys_attr); + if (value == NULL) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); + return NULL; + } + return Py_NewRef(value); + } + + extern const PyConfig* _Py_GetConfig(void); + const PyConfig *config = _Py_GetConfig(); + void *member = (char *)config + spec->offset; + switch (spec->type) { + case _PyConfig_MEMBER_INT: + case _PyConfig_MEMBER_UINT: + { + int value = *(int *)member; + return PyLong_FromLong(value); + } + case _PyConfig_MEMBER_BOOL: + { + int value = *(int *)member; + return PyBool_FromLong(value != 0); + } + case _PyConfig_MEMBER_ULONG: + { + unsigned long value = *(unsigned long *)member; + return PyLong_FromUnsignedLong(value); + } + case _PyConfig_MEMBER_WSTR: + case _PyConfig_MEMBER_WSTR_OPT: + { + wchar_t *wstr = *(wchar_t **)member; + if (wstr != NULL) { + return PyUnicode_FromWideChar(wstr, -1); + } + else { + return Py_NewRef(Py_None); + } + } + case _PyConfig_MEMBER_WSTR_LIST: + { + const PyWideStringList *list = (const PyWideStringList *)member; + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { + return NULL; + } + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; + } + default: + Py_UNREACHABLE(); + } + } + + PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); + return NULL; +} + +static inline int +PyConfig_GetInt(const char *name, int *value) +{ + PyObject *obj = PyConfig_Get(name); + if (obj == NULL) { + return -1; + } + + if (!PyLong_Check(obj)) { + Py_DECREF(obj); + PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); + return -1; + } + + int as_int = PyLong_AsInt(obj); + Py_DECREF(obj); + if (as_int == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_OverflowError, + "config option %s value does not fit into a C int", name); + return -1; + } + + *value = as_int; + return 0; +} +#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) + + #ifdef __cplusplus } #endif From 42b5999028c359d619297434fbdaacee905c0674 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:58:45 +0100 Subject: [PATCH 1084/1617] Replace deprecated `_PyLong_new` with `PyLongWriter` API (#18532) `_PyLong_New` will be deprecated in `3.14.0a5`. Replace it with the `PyLongWriter` API available in `pythoncapi_compat.h` for older versions. https://docs.python.org/dev/c-api/long.html#pylongwriter-api --- mypyc/lib-rt/int_ops.c | 24 ++++++++---------------- mypyc/lib-rt/mypyc_util.h | 11 ----------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index b7fff2535c12..e2c302eea576 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -232,13 +232,6 @@ PyObject *CPyBool_Str(bool b) { return PyObject_Str(b ? Py_True : Py_False); } -static void CPyLong_NormalizeUnsigned(PyLongObject *v) { - Py_ssize_t i = CPY_LONG_SIZE_UNSIGNED(v); - while (i > 0 && CPY_LONG_DIGIT(v, i - 1) == 0) - i--; - CPyLong_SetUnsignedSize(v, i); -} - // Bitwise op '&', '|' or '^' using the generic (slow) API static CPyTagged GenericBitwiseOp(CPyTagged a, CPyTagged b, char op) { PyObject *aobj = CPyTagged_AsObject(a); @@ -302,7 +295,6 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op) { digit *adigits = GetIntDigits(a, &asize, abuf); digit *bdigits = GetIntDigits(b, &bsize, bbuf); - PyLongObject *r; if (unlikely(asize < 0 || bsize < 0)) { // Negative operand. This is slower, but bitwise ops on them are pretty rare. return GenericBitwiseOp(a, b, op); @@ -317,31 +309,31 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op) { asize = bsize; bsize = tmp_size; } - r = _PyLong_New(op == '&' ? asize : bsize); - if (unlikely(r == NULL)) { + void *digits = NULL; + PyLongWriter *writer = PyLongWriter_Create(0, op == '&' ? asize : bsize, &digits); + if (unlikely(writer == NULL)) { CPyError_OutOfMemory(); } Py_ssize_t i; if (op == '&') { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] & bdigits[i]; + ((digit *)digits)[i] = adigits[i] & bdigits[i]; } } else { if (op == '|') { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] | bdigits[i]; + ((digit *)digits)[i] = adigits[i] | bdigits[i]; } } else { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] ^ bdigits[i]; + ((digit *)digits)[i] = adigits[i] ^ bdigits[i]; } } for (; i < bsize; i++) { - CPY_LONG_DIGIT(r, i) = bdigits[i]; + ((digit *)digits)[i] = bdigits[i]; } } - CPyLong_NormalizeUnsigned(r); - return CPyTagged_StealFromObject((PyObject *)r); + return CPyTagged_StealFromObject(PyLongWriter_Finish(writer)); } // Bitwise '~' slow path diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 66d5d106056b..80019d23bb06 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -124,13 +124,6 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) { // Number of digits, assuming int is non-negative #define CPY_LONG_SIZE_UNSIGNED(o) CPY_LONG_SIZE(o) -static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { - if (n == 0) - o->long_value.lv_tag = CPY_SIGN_ZERO; - else - o->long_value.lv_tag = n << CPY_NON_SIZE_BITS; -} - #else #define CPY_LONG_DIGIT(o, n) ((o)->ob_digit[n]) @@ -138,10 +131,6 @@ static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { #define CPY_LONG_SIZE_SIGNED(o) ((o)->ob_base.ob_size) #define CPY_LONG_SIZE_UNSIGNED(o) ((o)->ob_base.ob_size) -static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { - o->ob_base.ob_size = n; -} - #endif // Are we targeting Python 3.13 or newer? From b82697b3f50056c2697ef6e884e1d6dd87881f8d Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:28:46 +0100 Subject: [PATCH 1085/1617] Retain None (unreachable) when typemap is None with `type(x) is Foo` check (#18486) Fixes #18428. Prevents rewriting `None` ("unreachable") typemaps as empty dicts ("nothing to infer") --- mypy/checker.py | 2 ++ test-data/unit/check-isinstance.test | 27 ++++++++++++++++++++++++ test-data/unit/check-typevar-values.test | 13 ++++++++++++ 3 files changed, 42 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index bf6c8423c12b..90b1e5e03d7e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6021,6 +6021,8 @@ def is_type_call(expr: CallExpr) -> bool: def combine_maps(list_maps: list[TypeMap]) -> TypeMap: """Combine all typemaps in list_maps into one typemap""" + if all(m is None for m in list_maps): + return None result_map = {} for d in list_maps: if d is not None: diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 2e483bbbfc26..04fbced5347c 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -2660,6 +2660,33 @@ y: Any if type(y) is int: reveal_type(y) # N: Revealed type is "builtins.int" +[case testTypeEqualsCheckUsingIsNonOverlapping] +# flags: --warn-unreachable +from typing import Union + +y: str +if type(y) is int: # E: Subclass of "str" and "int" cannot exist: would have incompatible method signatures + y # E: Statement is unreachable +else: + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/isinstance.pyi] + +[case testTypeEqualsCheckUsingIsNonOverlappingChild-xfail] +# flags: --warn-unreachable +from typing import Union + +class A: ... +class B: ... +class C(A): ... +x: Union[B, C] +# C instance cannot be exactly its parent A, we need reversed subtyping relationship +# here (type(parent) is Child). +if type(x) is A: + reveal_type(x) # E: Statement is unreachable +else: + reveal_type(x) # N: Revealed type is "Union[__main__.B, __main__.C]" +[builtins fixtures/isinstance.pyi] + [case testTypeEqualsNarrowingUnionWithElse] from typing import Union diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 8b961d88d23d..f932cf53c1d4 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -732,3 +732,16 @@ def foo3(x: NT) -> None: def foo4(x: NT) -> None: p, q = 1, 2.0 # type: (int, float) [builtins fixtures/tuple.pyi] + +[case testTypeVarValuesNarrowing] +from typing import TypeVar + +W = TypeVar("W", int, str) + +def fn(w: W) -> W: + if type(w) is str: + reveal_type(w) # N: Revealed type is "builtins.str" + elif type(w) is int: + reveal_type(w) # N: Revealed type is "builtins.int" + return w +[builtins fixtures/isinstance.pyi] From 82f4e88fe75504091e9e0c5bfddc4344d9522138 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 28 Jan 2025 08:12:24 +0900 Subject: [PATCH 1086/1617] Start propagating end columns/lines through for `type-arg` errors (#18533) Fixes https://github.com/python/mypy/issues/18531 --- mypy/fastparse.py | 7 +++++-- mypy/semanal.py | 10 +++++++++- mypy/typeanal.py | 4 ++++ test-data/unit/check-columns.test | 14 ++++++++++++++ test-data/unit/check-parameter-specification.test | 4 ++-- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index cd7aab86daa0..14b30e5d7826 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2052,13 +2052,16 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: value = self.visit(n.value) if isinstance(value, UnboundType) and not value.args: - return UnboundType( + result = UnboundType( value.name, params, line=self.line, column=value.column, empty_tuple_index=empty_tuple_index, ) + result.end_column = getattr(n, "end_col_offset", None) + result.end_line = getattr(n, "end_lineno", None) + return result else: return self.invalid_type(n) @@ -2092,7 +2095,7 @@ def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) if isinstance(before_dot, UnboundType) and not before_dot.args: - return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line) + return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line, column=n.col_offset) else: return self.invalid_type(n) diff --git a/mypy/semanal.py b/mypy/semanal.py index febb9590887e..f357813ff38b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -7218,7 +7218,15 @@ def fail( if code is None: code = msg.code msg = msg.value - self.errors.report(ctx.line, ctx.column, msg, blocker=blocker, code=code) + self.errors.report( + ctx.line, + ctx.column, + msg, + blocker=blocker, + code=code, + end_line=ctx.end_line, + end_column=ctx.end_column, + ) def note(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: if not self.in_checked_function(): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 008e3c2477a1..b93c7ddd001a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -862,6 +862,8 @@ def analyze_type_with_type_info( ctx.line, ctx.column, ) + instance.end_line = ctx.end_line + instance.end_column = ctx.end_column if len(info.type_vars) == 1 and info.has_param_spec_type: instance.args = tuple(self.pack_paramspec_args(instance.args)) @@ -2204,6 +2206,8 @@ def instantiate_type_alias( tp = Instance(node.target.type, args) tp.line = ctx.line tp.column = ctx.column + tp.end_line = ctx.end_line + tp.end_column = ctx.end_column return tp if node.tvar_tuple_index is None: if any(isinstance(a, UnpackType) for a in args): diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 940e0846c959..8f91d99a0576 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -408,3 +408,17 @@ x[0] main:2:10:2:17: error: Incompatible types in assignment (expression has type "str", variable has type "int") main:6:3:7:1: error: Argument 1 to "f" has incompatible type "int"; expected "str" main:8:1:8:4: error: Value of type "int" is not indexable + +[case testEndColumnsWithTooManyTypeVars] +# flags: --pretty +import typing + +x1: typing.List[typing.List[int, int]] +x2: list[list[int, int]] +[out] +main:4:17: error: "list" expects 1 type argument, but 2 given + x1: typing.List[typing.List[int, int]] + ^~~~~~~~~~~~~~~~~~~~~ +main:5:10: error: "list" expects 1 type argument, but 2 given + x2: list[list[int, int]] + ^~~~~~~~~~~~~~ diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index fa3d98036ec3..352503023f97 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1345,8 +1345,8 @@ from typing import Callable, ParamSpec P1 = ParamSpec('P1') P2 = ParamSpec('P2') -def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P2" is unbound \ - # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" \ + # E: ParamSpec "P2" is unbound def f1(*args: P1.args): ... # E: ParamSpec "P1" is unbound def f2(**kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound From e046a54b4c51a5fe26c6bb8b2e5bc905eb2c6dbf Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 28 Jan 2025 19:29:56 +0900 Subject: [PATCH 1087/1617] Fix unsafe default return values in NodeVisitor methods (#18536) Raise NotImplementedError by default in NodeVisitor visit methods. --- mypy/checker.py | 13 ++++ mypy/semanal.py | 33 +++++++++ mypy/traverser.py | 82 +++++++++++++++++++++ mypy/visitor.py | 171 ++++++++++++++++++++++---------------------- mypy_self_check.ini | 4 -- 5 files changed, 212 insertions(+), 91 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 90b1e5e03d7e..0194efe25799 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -77,6 +77,7 @@ FuncBase, FuncDef, FuncItem, + GlobalDecl, IfStmt, Import, ImportAll, @@ -92,6 +93,7 @@ MypyFile, NameExpr, Node, + NonlocalDecl, OperatorAssignmentStmt, OpExpr, OverloadedFuncDef, @@ -7876,6 +7878,17 @@ def warn_deprecated_overload_item( if candidate == target: self.warn_deprecated(item.func, context) + # leafs + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index f357813ff38b..8463e07e61cb 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -97,10 +97,12 @@ AwaitExpr, Block, BreakStmt, + BytesExpr, CallExpr, CastExpr, ClassDef, ComparisonExpr, + ComplexExpr, ConditionalExpr, Context, ContinueStmt, @@ -114,6 +116,7 @@ Expression, ExpressionStmt, FakeExpression, + FloatExpr, ForStmt, FuncBase, FuncDef, @@ -126,6 +129,7 @@ ImportBase, ImportFrom, IndexExpr, + IntExpr, LambdaExpr, ListComprehension, ListExpr, @@ -193,6 +197,7 @@ MappingPattern, OrPattern, SequencePattern, + SingletonPattern, StarredPattern, ValuePattern, ) @@ -7529,6 +7534,34 @@ def parse_dataclass_transform_field_specifiers(self, arg: Expression) -> tuple[s names.append(specifier.fullname) return tuple(names) + # leafs + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike: if isinstance(sig, CallableType): diff --git a/mypy/traverser.py b/mypy/traverser.py index 2c8ea49491bc..7d7794822396 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -58,6 +58,7 @@ OverloadedFuncDef, ParamSpecExpr, PassStmt, + PromoteExpr, RaiseStmt, ReturnStmt, RevealExpr, @@ -67,6 +68,7 @@ StarExpr, StrExpr, SuperExpr, + TempNode, TryStmt, TupleExpr, TypeAlias, @@ -77,6 +79,7 @@ TypeVarExpr, TypeVarTupleExpr, UnaryExpr, + Var, WhileStmt, WithStmt, YieldExpr, @@ -415,6 +418,85 @@ def visit_import_from(self, o: ImportFrom, /) -> None: for a in o.assignments: a.accept(self) + # leaf nodes + def visit_name_expr(self, o: NameExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_var(self, o: Var, /) -> None: + return None + + def visit_continue_stmt(self, o: ContinueStmt, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_break_stmt(self, o: BreakStmt, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + + def visit_import_all(self, o: ImportAll, /) -> None: + return None + + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: + return None + + def visit_paramspec_expr(self, o: ParamSpecExpr, /) -> None: + return None + + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr, /) -> None: + return None + + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: + return None + + def visit_type_alias(self, o: TypeAlias, /) -> None: + return None + + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: + return None + + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: + return None + + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: + return None + + def visit__promote_expr(self, o: PromoteExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_enum_call_expr(self, o: EnumCallExpr, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + class ExtendedTraverserVisitor(TraverserVisitor): """This is a more flexible traverser. diff --git a/mypy/visitor.py b/mypy/visitor.py index 6613b6cbb144..d1b2ca416410 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -356,273 +356,270 @@ class NodeVisitor(Generic[T], ExpressionVisitor[T], StatementVisitor[T], Pattern """Empty base class for parse tree node visitors. The T type argument specifies the return type of the visit - methods. As all methods defined here return None by default, + methods. As all methods defined here raise by default, subclasses do not always need to override all the methods. - - TODO: make the default return value explicit, then turn on - empty body checking in mypy_self_check.ini. """ # Not in superclasses: def visit_mypy_file(self, o: mypy.nodes.MypyFile, /) -> T: - pass + raise NotImplementedError() # TODO: We have a visit_var method, but no visit_typeinfo or any # other non-Statement SymbolNode (accepting those will raise a # runtime error). Maybe this should be resolved in some direction. def visit_var(self, o: mypy.nodes.Var, /) -> T: - pass + raise NotImplementedError() # Module structure def visit_import(self, o: mypy.nodes.Import, /) -> T: - pass + raise NotImplementedError() def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: - pass + raise NotImplementedError() def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: - pass + raise NotImplementedError() # Definitions def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: - pass + raise NotImplementedError() def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: - pass + raise NotImplementedError() def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: - pass + raise NotImplementedError() def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: - pass + raise NotImplementedError() def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: - pass + raise NotImplementedError() def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: - pass + raise NotImplementedError() def visit_type_alias(self, o: mypy.nodes.TypeAlias, /) -> T: - pass + raise NotImplementedError() def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode, /) -> T: - pass + raise NotImplementedError() # Statements def visit_block(self, o: mypy.nodes.Block, /) -> T: - pass + raise NotImplementedError() def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: - pass + raise NotImplementedError() def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: - pass + raise NotImplementedError() def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: - pass + raise NotImplementedError() def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: - pass + raise NotImplementedError() def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: - pass + raise NotImplementedError() def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: - pass + raise NotImplementedError() def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: - pass + raise NotImplementedError() def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: - pass + raise NotImplementedError() def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: - pass + raise NotImplementedError() def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: - pass + raise NotImplementedError() def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: - pass + raise NotImplementedError() def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: - pass + raise NotImplementedError() def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: - pass + raise NotImplementedError() def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: - pass + raise NotImplementedError() def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: - pass + raise NotImplementedError() def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: - pass + raise NotImplementedError() def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: - pass + raise NotImplementedError() # Expressions (default no-op implementation) def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: - pass + raise NotImplementedError() def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: - pass + raise NotImplementedError() def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: - pass + raise NotImplementedError() def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: - pass + raise NotImplementedError() def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: - pass + raise NotImplementedError() def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: - pass + raise NotImplementedError() def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: - pass + raise NotImplementedError() def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: - pass + raise NotImplementedError() def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: - pass + raise NotImplementedError() def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: - pass + raise NotImplementedError() def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: - pass + raise NotImplementedError() def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: - pass + raise NotImplementedError() def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: - pass + raise NotImplementedError() def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: - pass + raise NotImplementedError() def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: - pass + raise NotImplementedError() def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: - pass + raise NotImplementedError() def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: - pass + raise NotImplementedError() def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: - pass + raise NotImplementedError() def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: - pass + raise NotImplementedError() def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: - pass + raise NotImplementedError() def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: - pass + raise NotImplementedError() def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: - pass + raise NotImplementedError() def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: - pass + raise NotImplementedError() def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: - pass + raise NotImplementedError() def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: - pass + raise NotImplementedError() def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: - pass + raise NotImplementedError() def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: - pass + raise NotImplementedError() def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: - pass + raise NotImplementedError() def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: - pass + raise NotImplementedError() def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: - pass + raise NotImplementedError() def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: - pass + raise NotImplementedError() def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: - pass + raise NotImplementedError() def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: - pass + raise NotImplementedError() def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: - pass + raise NotImplementedError() def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: - pass + raise NotImplementedError() def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: - pass + raise NotImplementedError() def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: - pass + raise NotImplementedError() def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: - pass + raise NotImplementedError() # Patterns def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: - pass + raise NotImplementedError() def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: - pass + raise NotImplementedError() def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: - pass + raise NotImplementedError() def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: - pass + raise NotImplementedError() def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: - pass + raise NotImplementedError() def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: - pass + raise NotImplementedError() def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: - pass + raise NotImplementedError() def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: - pass + raise NotImplementedError() diff --git a/mypy_self_check.ini b/mypy_self_check.ini index f54c1f17f025..7198a1f6f342 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -12,7 +12,3 @@ exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True - -[mypy-mypy.visitor] -# See docstring for NodeVisitor for motivation. -disable_error_code = empty-body From 7ceca5ffb18b06f6402ae39b5e054299901ee29e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:46:58 +0100 Subject: [PATCH 1088/1617] Remove old marcos for _PyObject_CallMethod.. (#18555) Mypyc requires Python 3.9+. The functions are available from cpython itself. --- mypyc/lib-rt/pythonsupport.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 33c2848b2df1..f35f8a1a6e4e 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -392,16 +392,6 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { #define _CPyObject_HasAttrId _PyObject_HasAttrId #endif -#if PY_VERSION_HEX < 0x03090000 -// OneArgs and NoArgs functions got added in 3.9 -#define _PyObject_CallMethodIdNoArgs(self, name) \ - _PyObject_CallMethodIdObjArgs((self), (name), NULL) -#define _PyObject_CallMethodIdOneArg(self, name, arg) \ - _PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL) -#define PyObject_CallMethodOneArg(self, name, arg) \ - PyObject_CallMethodObjArgs((self), (name), (arg), NULL) -#endif - #if CPY_3_12_FEATURES // These are copied from genobject.c in Python 3.12 From d5628fa8354ba081a0e33e7abee1592028dd68e0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:51:09 +0100 Subject: [PATCH 1089/1617] Update test requirements (#18551) --- test-requirements.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 6eb6f6a95ac8..5083639e6ef9 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,21 +4,21 @@ # # pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in # -attrs==24.2.0 +attrs==25.1.0 # via -r test-requirements.in cfgv==3.4.0 # via pre-commit -coverage==7.6.1 +coverage==7.6.10 # via pytest-cov distlib==0.3.9 # via virtualenv execnet==2.1.1 # via pytest-xdist -filelock==3.16.1 +filelock==3.17.0 # via # -r test-requirements.in # virtualenv -identify==2.6.1 +identify==2.6.6 # via pre-commit iniconfig==2.0.0 # via pytest @@ -28,38 +28,38 @@ mypy-extensions==1.0.0 # via -r mypy-requirements.txt nodeenv==1.9.1 # via pre-commit -packaging==24.1 +packaging==24.2 # via pytest platformdirs==4.3.6 # via virtualenv pluggy==1.5.0 # via pytest -pre-commit==3.5.0 +pre-commit==4.1.0 # via -r test-requirements.in -psutil==6.0.0 +psutil==6.1.1 # via -r test-requirements.in -pytest==8.3.3 +pytest==8.3.4 # via # -r test-requirements.in # pytest-cov # pytest-xdist -pytest-cov==5.0.0 +pytest-cov==6.0.0 # via -r test-requirements.in pytest-xdist==3.6.1 # via -r test-requirements.in pyyaml==6.0.2 # via pre-commit -tomli==2.0.2 +tomli==2.2.1 # via -r test-requirements.in -types-psutil==6.0.0.20241011 +types-psutil==6.1.0.20241221 # via -r build-requirements.txt -types-setuptools==75.1.0.20241014 +types-setuptools==75.8.0.20250110 # via -r build-requirements.txt typing-extensions==4.12.2 # via -r mypy-requirements.txt -virtualenv==20.26.6 +virtualenv==20.29.1 # via pre-commit # The following packages are considered to be unsafe in a requirements file: -setuptools==75.1.0 +setuptools==75.8.0 # via -r test-requirements.in From 93d1ce4133467a84b02ea06e5936cf6480afe08f Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Tue, 28 Jan 2025 11:54:39 +0100 Subject: [PATCH 1090/1617] PEP 702 (@deprecated): "normal" overloaded methods (#18477) Fixes #18474 It seems I covered overloaded functions, descriptors, and special methods so far but completely forgot about "normal" methods (thanks to @sobolevn for pointing this out). This addition should do the trick. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: sobolevn --- mypy/checker.py | 2 +- mypy/checkexpr.py | 9 +- test-data/unit/check-deprecated.test | 165 +++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0194efe25799..c69b80a55fd9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7873,7 +7873,7 @@ def warn_deprecated_overload_item( if isinstance(item, Decorator) and isinstance( candidate := item.func.type, CallableType ): - if selftype is not None: + if selftype is not None and not node.is_static: candidate = bind_self(candidate, selftype) if candidate == target: self.warn_deprecated(item.func, context) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0752fa0b466f..4b7e39d2042a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1485,7 +1485,14 @@ def check_call_expr_with_callee_type( ) proper_callee = get_proper_type(callee_type) if isinstance(e.callee, (NameExpr, MemberExpr)): - self.chk.warn_deprecated_overload_item(e.callee.node, e, target=callee_type) + node = e.callee.node + if node is None and member is not None and isinstance(object_type, Instance): + if (symbol := object_type.type.get(member)) is not None: + node = symbol.node + self.chk.check_deprecated(node, e) + self.chk.warn_deprecated_overload_item( + node, e, target=callee_type, selftype=object_type + ) if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType): # Cache it for find_isinstance_check() if proper_callee.type_guard is not None: diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 362d8725f183..df9695332a5b 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -377,6 +377,171 @@ for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration [builtins fixtures/tuple.pyi] +[case testDeprecatedOverloadedInstanceMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union +from typing_extensions import deprecated, overload + +class A: + @overload + @deprecated("pass `str` instead") + def f(self, v: int) -> None: ... + @overload + def f(self, v: str) -> None: ... + def f(self, v: Union[int, str]) -> None: ... + + @overload + def g(self, v: int) -> None: ... + @overload + @deprecated("pass `int` instead") + def g(self, v: str) -> None: ... + def g(self, v: Union[int, str]) -> None: ... + + @overload + def h(self, v: int) -> A: ... + @overload + def h(self, v: str) -> A: ... + @deprecated("use `h2` instead") + def h(self, v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedClassMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union +from typing_extensions import deprecated, overload + +class A: + @overload + @classmethod + @deprecated("pass `str` instead") + def f(cls, v: int) -> None: ... + @overload + @classmethod + def f(cls, v: str) -> None: ... + @classmethod + def f(cls, v: Union[int, str]) -> None: ... + + @overload + @classmethod + def g(cls, v: int) -> None: ... + @overload + @classmethod + @deprecated("pass `int` instead") + def g(cls, v: str) -> None: ... + @classmethod + def g(cls, v: Union[int, str]) -> None: ... + + @overload + @classmethod + def h(cls, v: int) -> A: ... + @overload + @classmethod + def h(cls, v: str) -> A: ... + @deprecated("use `h2` instead") + @classmethod + def h(cls, v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedStaticMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union +from typing_extensions import deprecated, overload + +class A: + @overload + @staticmethod + @deprecated("pass `str` instead") + def f(v: int) -> None: ... + @overload + @staticmethod + def f(v: str) -> None: ... + @staticmethod + def f(v: Union[int, str]) -> None: ... + + @overload + @staticmethod + def g(v: int) -> None: ... + @overload + @staticmethod + @deprecated("pass `int` instead") + def g(v: str) -> None: ... + @staticmethod + def g(v: Union[int, str]) -> None: ... + + @overload + @staticmethod + def h(v: int) -> A: ... + @overload + @staticmethod + def h(v: str) -> A: ... + @deprecated("use `h2` instead") + @staticmethod + def h(v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/classmethod.pyi] + + [case testDeprecatedOverloadedSpecialMethods] # flags: --enable-error-code=deprecated From c08719d8d93f48fd9428f36bc690910930fdd65b Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:26:41 +0000 Subject: [PATCH 1091/1617] [mypyc] Add debug op (and builder helper) for printing str or Value (#18552) It generates C code to print to stdout, but tries to preserve the error state and not affect the code it's added to. Added test for it, but also tested this by adding `builder.debug_print(typ)` in `add_non_ext_class_attr_ann` and it prints the class name. It's also useful to use it like `builder.debug_print("MARKER")` and then to search in the generated C code for MARKER. For more complex debugging tasks, this is useful in finding your way around the generated C code and quickly looking at the interesting part. I saw that there's already a misc op `CPyDebug_Print`. I haven't seen it used though. I think we can remove that one if this is proving useful. --- mypyc/irbuild/builder.py | 3 +++ mypyc/irbuild/ll_builder.py | 6 ++++++ mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/misc_ops.c | 16 ++++++++++++++++ mypyc/primitives/misc_ops.py | 8 ++++++++ mypyc/test/test_misc.py | 20 ++++++++++++++++++++ 6 files changed, 54 insertions(+) create mode 100644 mypyc/test/test_misc.py diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index b0597617bdc5..aafa7f3a0976 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -420,6 +420,9 @@ def builtin_len(self, val: Value, line: int) -> Value: def new_tuple(self, items: list[Value], line: int) -> Value: return self.builder.new_tuple(items, line) + def debug_print(self, toprint: str | Value) -> None: + return self.builder.debug_print(toprint) + # Helpers for IR building def add_to_non_ext_dict( diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index bae38f27b346..767cf08d9b96 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -162,6 +162,7 @@ from mypyc.primitives.misc_ops import ( bool_op, buf_init_item, + debug_print_op, fast_isinstance_op, none_object_op, not_implemented_op, @@ -300,6 +301,11 @@ def flush_keep_alives(self) -> None: self.add(KeepAlive(self.keep_alives.copy())) self.keep_alives = [] + def debug_print(self, toprint: str | Value) -> None: + if isinstance(toprint, str): + toprint = self.load_str(toprint) + self.primitive_op(debug_print_op, [toprint], -1) + # Type conversions def box(self, src: Value) -> Value: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index f72eaea55daf..a240f20d31d8 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -866,6 +866,7 @@ PyObject *CPyPickle_SetState(PyObject *obj, PyObject *state); PyObject *CPyPickle_GetState(PyObject *obj); CPyTagged CPyTagged_Id(PyObject *o); void CPyDebug_Print(const char *msg); +void CPyDebug_PrintObject(PyObject *obj); void CPy_Init(void); int CPyArg_ParseTupleAndKeywords(PyObject *, PyObject *, const char *, const char *, const char * const *, ...); diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index e71ef0dc6b48..a674240d8940 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -535,6 +535,22 @@ void CPyDebug_Print(const char *msg) { fflush(stdout); } +void CPyDebug_PrintObject(PyObject *obj) { + // Printing can cause errors. We don't want this to affect any existing + // state so we'll save any existing error and restore it at the end. + PyObject *exc_type, *exc_value, *exc_traceback; + PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + + if (PyObject_Print(obj, stderr, 0) == -1) { + PyErr_Print(); + } else { + fprintf(stderr, "\n"); + } + fflush(stderr); + + PyErr_Restore(exc_type, exc_value, exc_traceback); +} + int CPySequence_CheckUnpackCount(PyObject *sequence, Py_ssize_t expected) { Py_ssize_t actual = Py_SIZE(sequence); if (unlikely(actual != expected)) { diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 2d8a2d362293..7494b46790ce 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -283,3 +283,11 @@ return_type=void_rtype, error_kind=ERR_NEVER, ) + +debug_print_op = custom_primitive_op( + name="debug_print", + c_function_name="CPyDebug_PrintObject", + arg_types=[object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) diff --git a/mypyc/test/test_misc.py b/mypyc/test/test_misc.py new file mode 100644 index 000000000000..f92da2ca3fe1 --- /dev/null +++ b/mypyc/test/test_misc.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +import unittest + +from mypyc.ir.ops import BasicBlock +from mypyc.ir.pprint import format_blocks, generate_names_for_ir +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.options import CompilerOptions + + +class TestMisc(unittest.TestCase): + def test_debug_op(self) -> None: + block = BasicBlock() + builder = LowLevelIRBuilder(errors=None, options=CompilerOptions()) + builder.activate_block(block) + builder.debug_print("foo") + + names = generate_names_for_ir([], [block]) + code = format_blocks([block], names, {}) + assert code[:-1] == ["L0:", " r0 = 'foo'", " CPyDebug_PrintObject(r0)"] From f49a1cb265db5497882a34e2dd6b4f6883b3430f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:25:32 +0100 Subject: [PATCH 1092/1617] [mypyc] Fix wheel build for cp313-win (#18560) Sync `pythoncapi_compat.h` with latest fix from https://github.com/python/pythoncapi-compat/pull/137. Ref: https://github.com/python/mypy/pull/18535#issuecomment-2618918615 --- mypyc/lib-rt/pythoncapi_compat.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index 4d2884622f1f..e534c1cbb7cc 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -1939,7 +1939,8 @@ PyLongWriter_Finish(PyLongWriter *writer) static inline FILE* Py_fopen(PyObject *path, const char *mode) { #if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) - extern FILE* _Py_fopen_obj(PyObject *path, const char *mode); + PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); + return _Py_fopen_obj(path, mode); #else FILE *f; @@ -2109,7 +2110,8 @@ PyConfig_Get(const char *name) return Py_NewRef(value); } - extern const PyConfig* _Py_GetConfig(void); + PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); + const PyConfig *config = _Py_GetConfig(); void *member = (char *)config + spec->offset; switch (spec->type) { From d4e7a81ef9a66a80cb395b6afb8498f7dbcd3c96 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 29 Jan 2025 01:42:22 +0100 Subject: [PATCH 1093/1617] Update lxml test requirement to 5.3.0 (#18558) `lxml` has wheels for both `manylinux_2_17` and `manylinux_2_28` so we won't run into issue installing it. Furthermore there are also wheels for `win32` and `win_amd64`. Basically all platforms are fully supported now. The upper bound can be updated too, once wheels for `3.14` are available. https://pypi.org/project/lxml/5.3.0/#files --- test-requirements.in | 3 +-- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test-requirements.in b/test-requirements.in index 767a94e5c14d..666dd9fc082c 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -5,8 +5,7 @@ -r build-requirements.txt attrs>=18.0 filelock>=3.3.0 -# lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 -lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' +lxml>=5.3.0; python_version<'3.14' psutil>=4.0 pytest>=8.1.0 pytest-xdist>=1.34.0 diff --git a/test-requirements.txt b/test-requirements.txt index 5083639e6ef9..e2a12655a1aa 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -22,7 +22,7 @@ identify==2.6.6 # via pre-commit iniconfig==2.0.0 # via pytest -lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" +lxml==5.3.0 ; python_version < "3.14" # via -r test-requirements.in mypy-extensions==1.0.0 # via -r mypy-requirements.txt From 05d389823af25ae0c61f372dbc6ef1986b67dbcb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:31:56 +0100 Subject: [PATCH 1094/1617] Update black to 25.1.0 (#18570) https://github.com/psf/black/releases/tag/25.1.0 --- .pre-commit-config.yaml | 2 +- mypy/test/testinfer.py | 2 +- mypy/types.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 59bd490987d6..050f01b063cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 + rev: 25.1.0 hooks: - id: black exclude: '^(test-data/)' diff --git a/mypy/test/testinfer.py b/mypy/test/testinfer.py index 107c4d8dc98a..9c18624e0283 100644 --- a/mypy/test/testinfer.py +++ b/mypy/test/testinfer.py @@ -134,7 +134,7 @@ def expand_caller_kinds( def expand_callee_kinds( - kinds_and_names: list[ArgKind | tuple[ArgKind, str]] + kinds_and_names: list[ArgKind | tuple[ArgKind, str]], ) -> tuple[list[ArgKind], list[str | None]]: kinds = [] names: list[str | None] = [] diff --git a/mypy/types.py b/mypy/types.py index f3745695889f..1e85cd62cd82 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3205,12 +3205,12 @@ def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: @overload def get_proper_types( - types: list[Type | None] | tuple[Type | None, ...] + types: list[Type | None] | tuple[Type | None, ...], ) -> list[ProperType | None]: ... def get_proper_types( - types: list[Type] | list[Type | None] | tuple[Type | None, ...] + types: list[Type] | list[Type | None] | tuple[Type | None, ...], ) -> list[ProperType] | list[ProperType | None]: if isinstance(types, list): typelist = types From 7d084e97b38bdf5badc05449774cff24294a5bc5 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:36:23 +0100 Subject: [PATCH 1095/1617] Add constants for Concatenate and Unpack type names (#18553) --- mypy/semanal.py | 5 +++-- mypy/stubgen.py | 10 ++-------- mypy/typeanal.py | 10 ++++++---- mypy/types.py | 6 ++++++ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 8463e07e61cb..d769178dc298 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -268,6 +268,7 @@ TYPE_CHECK_ONLY_NAMES, TYPE_VAR_LIKE_NAMES, TYPED_NAMEDTUPLE_NAMES, + UNPACK_TYPE_NAMES, AnyType, CallableType, FunctionLike, @@ -2286,7 +2287,7 @@ def analyze_unbound_tvar(self, t: Type) -> tuple[str, TypeVarLikeExpr] | None: return self.analyze_unbound_tvar_impl(t.type, is_unpacked=True) if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + if sym and sym.fullname in UNPACK_TYPE_NAMES: inner_t = t.args[0] if isinstance(inner_t, UnboundType): return self.analyze_unbound_tvar_impl(inner_t, is_unpacked=True) @@ -4171,7 +4172,7 @@ def analyze_type_alias_type_params( base, code=codes.TYPE_VAR, ) - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + if sym and sym.fullname in UNPACK_TYPE_NAMES: self.note( "Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR ) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 86f9a108f1d6..60460ee1e330 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -145,6 +145,7 @@ DATACLASS_TRANSFORM_NAMES, OVERLOAD_NAMES, TPDICT_NAMES, + TYPE_VAR_LIKE_NAMES, TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, @@ -1090,14 +1091,7 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: or module alias. """ # Assignment of TypeVar(...) and other typevar-likes are passed through - if isinstance(expr, CallExpr) and self.get_fullname(expr.callee) in ( - "typing.TypeVar", - "typing_extensions.TypeVar", - "typing.ParamSpec", - "typing_extensions.ParamSpec", - "typing.TypeVarTuple", - "typing_extensions.TypeVarTuple", - ): + if isinstance(expr, CallExpr) and self.get_fullname(expr.callee) in TYPE_VAR_LIKE_NAMES: return True elif isinstance(expr, EllipsisExpr): return not top_level diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b93c7ddd001a..fa7cf4242d82 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -62,10 +62,12 @@ from mypy.types import ( ANNOTATED_TYPE_NAMES, ANY_STRATEGY, + CONCATENATE_TYPE_NAMES, FINAL_TYPE_NAMES, LITERAL_TYPE_NAMES, NEVER_NAMES, TYPE_ALIAS_NAMES, + UNPACK_TYPE_NAMES, AnyType, BoolTypeQuery, CallableArgument, @@ -525,7 +527,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) elif node.fullname in TYPE_ALIAS_NAMES: return AnyType(TypeOfAny.special_form) # Concatenate is an operator, no need for a proper type - elif node.fullname in ("typing_extensions.Concatenate", "typing.Concatenate"): + elif node.fullname in CONCATENATE_TYPE_NAMES: # We check the return type further up the stack for valid use locations return self.apply_concatenate_operator(t) else: @@ -779,7 +781,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ ): # In most contexts, TypeGuard[...] acts as an alias for bool (ignoring its args) return self.named_type("builtins.bool") - elif fullname in ("typing.Unpack", "typing_extensions.Unpack"): + elif fullname in UNPACK_TYPE_NAMES: if len(t.args) != 1: self.fail("Unpack[...] requires exactly one type argument", t) return AnyType(TypeOfAny.from_error) @@ -1503,7 +1505,7 @@ def analyze_callable_args_for_concatenate( return None if sym.node is None: return None - if sym.node.fullname not in ("typing_extensions.Concatenate", "typing.Concatenate"): + if sym.node.fullname not in CONCATENATE_TYPE_NAMES: return None tvar_def = self.anal_type(callable_args, allow_param_spec=True) @@ -1652,7 +1654,7 @@ def analyze_callable_args( return None elif ( isinstance(arg, UnboundType) - and self.refers_to_full_names(arg, ("typing_extensions.Unpack", "typing.Unpack")) + and self.refers_to_full_names(arg, UNPACK_TYPE_NAMES) or isinstance(arg, UnpackType) ): if seen_unpack: diff --git a/mypy/types.py b/mypy/types.py index 1e85cd62cd82..f700be887116 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -130,6 +130,12 @@ # Supported Annotated type names. ANNOTATED_TYPE_NAMES: Final = ("typing.Annotated", "typing_extensions.Annotated") +# Supported Concatenate type names. +CONCATENATE_TYPE_NAMES: Final = ("typing.Concatenate", "typing_extensions.Concatenate") + +# Supported Unpack type names. +UNPACK_TYPE_NAMES: Final = ("typing.Unpack", "typing_extensions.Unpack") + # Supported @deprecated type names DEPRECATED_TYPE_NAMES: Final = ("warnings.deprecated", "typing_extensions.deprecated") From e2b821bd3d2492f6cb3b4c82a9566c5a1659fd7e Mon Sep 17 00:00:00 2001 From: A5rocks Date: Thu, 30 Jan 2025 20:04:06 +0900 Subject: [PATCH 1096/1617] Update the overlapping check for tuples to account for NamedTuples (#18564) Fixes https://github.com/python/mypy/issues/18562. Fixes https://github.com/python/mypy/issues/6623. Fixes https://github.com/python/mypy/issues/18520. (Only incidentally and I'm not exactly sure why.) I was investigating what input mypy thought satisfied both overloads and I found that mypy is missing a check on the fallback type. --- mypy/meet.py | 12 ++++++++++- test-data/unit/check-namedtuple.test | 32 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/mypy/meet.py b/mypy/meet.py index cbe3e99cdcd8..ea2411b8ccc9 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -50,6 +50,7 @@ find_unpack_in_list, get_proper_type, get_proper_types, + is_named_instance, split_with_prefix_and_suffix, ) @@ -645,7 +646,16 @@ def are_tuples_overlapping( if len(left.items) != len(right.items): return False - return all(is_overlapping(l, r) for l, r in zip(left.items, right.items)) + if not all(is_overlapping(l, r) for l, r in zip(left.items, right.items)): + return False + + # Check that the tuples aren't from e.g. different NamedTuples. + if is_named_instance(right.partial_fallback, "builtins.tuple") or is_named_instance( + left.partial_fallback, "builtins.tuple" + ): + return True + else: + return is_overlapping(left.partial_fallback, right.partial_fallback) def expand_tuple_if_possible(tup: TupleType, target: int) -> TupleType: diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 172228820add..a65a99cc25d0 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1474,3 +1474,35 @@ def main(n: NT[T]) -> None: [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNamedTupleOverlappingCheck] +from typing import overload, NamedTuple, Union + +class AKey(NamedTuple): + k: str + +class A(NamedTuple): + key: AKey + + +class BKey(NamedTuple): + k: str + +class B(NamedTuple): + key: BKey + +@overload +def f(arg: A) -> A: ... +@overload +def f(arg: B) -> B: ... +def f(arg: Union[A, B]) -> Union[A, B]: ... + +def g(x: Union[A, B, str]) -> Union[A, B, str]: + if isinstance(x, str): + return x + else: + reveal_type(x) # N: Revealed type is "Union[Tuple[Tuple[builtins.str, fallback=__main__.AKey], fallback=__main__.A], Tuple[Tuple[builtins.str, fallback=__main__.BKey], fallback=__main__.B]]" + return x._replace() + +# no errors should be raised above. +[builtins fixtures/tuple.pyi] From bec0dd3dc88e950ddb36d863783d8db368a07443 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:32:22 +0100 Subject: [PATCH 1097/1617] Remove old TypedDict + NamedTuple code from mypyc (#18554) Mypyc requires Python 3.9+. This PR removes the old TypedDict and NamedTuple code for versions prior to 3.9. --- mypyc/irbuild/classdef.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 03368d74c407..b01e16f57b88 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -2,7 +2,6 @@ from __future__ import annotations -import typing_extensions from abc import abstractmethod from typing import Callable, Final @@ -542,29 +541,10 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: # HAX: Mypy internally represents TypedDict classes differently from what # should happen at runtime. Replace with something that works. module = "typing" - if builder.options.capi_version < (3, 9): - name = "TypedDict" - if builder.options.capi_version < (3, 8): - # TypedDict was added to typing in Python 3.8. - module = "typing_extensions" - # TypedDict is not a real type on typing_extensions 4.7.0+ - name = "_TypedDict" - if isinstance(typing_extensions.TypedDict, type): - raise RuntimeError( - "It looks like you may have an old version " - "of typing_extensions installed. " - "typing_extensions>=4.7.0 is required on Python 3.7." - ) - else: - # In Python 3.9 TypedDict is not a real type. - name = "_TypedDict" + name = "_TypedDict" base = builder.get_module_attr(module, name, cdef.line) elif is_named_tuple and cls.fullname == "builtins.tuple": - if builder.options.capi_version < (3, 9): - name = "NamedTuple" - else: - # This was changed in Python 3.9. - name = "_NamedTuple" + name = "_NamedTuple" base = builder.get_module_attr("typing", name, cdef.line) else: cls_module = cls.fullname.rsplit(".", 1)[0] From acfd53ae3afbba070c661c454380cfe60b4af9b0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:33:43 +0100 Subject: [PATCH 1098/1617] Cleanup backwards compat layer for use_vectorcall and use_method_vectorcall (#18548) Followup to #18341 and #18546 We only support Python 3.9+, so `PyObject_Vectorcall` and `PyObject_VectorcallMethod` are always available. Remove backwards compatibility layer. --- mypyc/codegen/emit.py | 4 ---- mypyc/codegen/emitclass.py | 17 ++++------------- mypyc/codegen/emitmodule.py | 3 +-- mypyc/codegen/emitwrapper.py | 3 +-- mypyc/common.py | 10 ---------- mypyc/irbuild/ll_builder.py | 22 ++++++++-------------- 6 files changed, 14 insertions(+), 45 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index bef560b3d42a..bb889028b961 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -17,7 +17,6 @@ REG_PREFIX, STATIC_PREFIX, TYPE_PREFIX, - use_vectorcall, ) from mypyc.ir.class_ir import ClassIR, all_concrete_classes from mypyc.ir.func_ir import FuncDecl @@ -398,9 +397,6 @@ def _emit_attr_bitmap_update( if value: self.emit_line("}") - def use_vectorcall(self) -> bool: - return use_vectorcall(self.capi_version) - def emit_undefined_attr_check( self, rtype: RType, diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 54c979482f66..79ae6abf1f60 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -31,10 +31,6 @@ def native_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: return f"{NATIVE_PREFIX}{fn.cname(emitter.names)}" -def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - return f"{PREFIX}{fn.cname(emitter.names)}" - - # We maintain a table from dunder function names to struct slots they # correspond to and functions that generate a wrapper (if necessary) # and return the function name to stick in the slot. @@ -137,12 +133,7 @@ def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: def generate_call_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - if emitter.use_vectorcall(): - # Use vectorcall wrapper if supported (PEP 590). - return "PyVectorcall_Call" - else: - # On older Pythons use the legacy wrapper. - return wrapper_slot(cl, fn, emitter) + return "PyVectorcall_Call" def slot_key(attr: str) -> str: @@ -333,7 +324,7 @@ def emit_line() -> None: flags = ["Py_TPFLAGS_DEFAULT", "Py_TPFLAGS_HEAPTYPE", "Py_TPFLAGS_BASETYPE"] if generate_full: flags.append("Py_TPFLAGS_HAVE_GC") - if cl.has_method("__call__") and emitter.use_vectorcall(): + if cl.has_method("__call__"): fields["tp_vectorcall_offset"] = "offsetof({}, vectorcall)".format( cl.struct_name(emitter.names) ) @@ -381,7 +372,7 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: seen_attrs: set[tuple[str, RType]] = set() lines: list[str] = [] lines += ["typedef struct {", "PyObject_HEAD", "CPyVTableItem *vtable;"] - if cl.has_method("__call__") and emitter.use_vectorcall(): + if cl.has_method("__call__"): lines.append("vectorcallfunc vectorcall;") bitmap_attrs = [] for base in reversed(cl.base_mro): @@ -576,7 +567,7 @@ def generate_setup_for_class( field = emitter.bitmap_field(i) emitter.emit_line(f"self->{field} = 0;") - if cl.has_method("__call__") and emitter.use_vectorcall(): + if cl.has_method("__call__"): name = cl.method_decl("__call__").cname(emitter.names) emitter.emit_line(f"self->vectorcall = {PREFIX}{name};") diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index bd2958c285c3..1ec3064eb5b9 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -45,7 +45,6 @@ TYPE_VAR_PREFIX, shared_lib_name, short_id_from_name, - use_vectorcall, ) from mypyc.errors import Errors from mypyc.ir.func_ir import FuncIR @@ -1106,7 +1105,7 @@ def is_fastcall_supported(fn: FuncIR, capi_version: tuple[int, int]) -> bool: if fn.class_name is not None: if fn.name == "__call__": # We can use vectorcalls (PEP 590) when supported - return use_vectorcall(capi_version) + return True # TODO: Support fastcall for __init__. return fn.name != "__init__" return True diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py index f9bed440bb28..1918c946772c 100644 --- a/mypyc/codegen/emitwrapper.py +++ b/mypyc/codegen/emitwrapper.py @@ -24,7 +24,6 @@ NATIVE_PREFIX, PREFIX, bitmap_name, - use_vectorcall, ) from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncIR, RuntimeArg @@ -173,7 +172,7 @@ def generate_wrapper_function( arg_ptrs += [f"&obj_{groups[ARG_STAR2][0].name}" if groups[ARG_STAR2] else "NULL"] arg_ptrs += [f"&obj_{arg.name}" for arg in reordered_args] - if fn.name == "__call__" and use_vectorcall(emitter.capi_version): + if fn.name == "__call__": nargs = "PyVectorcall_NARGS(nargs)" else: nargs = "nargs" diff --git a/mypyc/common.py b/mypyc/common.py index c49952510c07..992376472086 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -106,16 +106,6 @@ def short_name(name: str) -> str: return name -def use_vectorcall(capi_version: tuple[int, int]) -> bool: - # We can use vectorcalls to make calls on Python 3.8+ (PEP 590). - return capi_version >= (3, 8) - - -def use_method_vectorcall(capi_version: tuple[int, int]) -> bool: - # We can use a dedicated vectorcall API to call methods on Python 3.9+. - return capi_version >= (3, 9) - - def get_id_from_name(name: str, fullname: str, line: int) -> str: """Create a unique id for a function. diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 767cf08d9b96..396d40938024 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -21,8 +21,6 @@ MIN_LITERAL_SHORT_INT, MIN_SHORT_INT, PLATFORM_SIZE, - use_method_vectorcall, - use_vectorcall, ) from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -898,11 +896,9 @@ def py_call( Use py_call_op or py_call_with_kwargs_op for Python function call. """ - if use_vectorcall(self.options.capi_version): - # More recent Python versions support faster vectorcalls. - result = self._py_vector_call(function, arg_values, line, arg_kinds, arg_names) - if result is not None: - return result + result = self._py_vector_call(function, arg_values, line, arg_kinds, arg_names) + if result is not None: + return result # If all arguments are positional, we can use py_call_op. if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds): @@ -977,13 +973,11 @@ def py_method_call( arg_names: Sequence[str | None] | None, ) -> Value: """Call a Python method (non-native and slow).""" - if use_method_vectorcall(self.options.capi_version): - # More recent Python versions support faster vectorcalls. - result = self._py_vector_method_call( - obj, method_name, arg_values, line, arg_kinds, arg_names - ) - if result is not None: - return result + result = self._py_vector_method_call( + obj, method_name, arg_values, line, arg_kinds, arg_names + ) + if result is not None: + return result if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds): # Use legacy method call API From ce1be2a32ad54adf773f4af842d2afdb562a3069 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:00:07 +0100 Subject: [PATCH 1099/1617] [mypyc] Improve support for frozenset (#18571) Add optimized methods for `len` and `contains` on `frozenset` objects. --- mypyc/codegen/emit.py | 4 + mypyc/doc/frozenset_operations.rst | 29 ++++++ mypyc/doc/index.rst | 1 + mypyc/ir/rtypes.py | 9 ++ mypyc/irbuild/ll_builder.py | 3 +- mypyc/irbuild/mapper.py | 3 + mypyc/primitives/set_ops.py | 26 +++++- mypyc/test-data/irbuild-frozenset.test | 115 ++++++++++++++++++++++++ mypyc/test-data/irbuild-set.test | 2 +- mypyc/test-data/run-sets.test | 118 +++++++++++++++++++++++++ mypyc/test/test_irbuild.py | 1 + 11 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 mypyc/doc/frozenset_operations.rst create mode 100644 mypyc/test-data/irbuild-frozenset.test diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index bb889028b961..d7d7d9c7abda 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -34,6 +34,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_frozenset_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, @@ -609,6 +610,7 @@ def emit_cast( is_list_rprimitive(typ) or is_dict_rprimitive(typ) or is_set_rprimitive(typ) + or is_frozenset_rprimitive(typ) or is_str_rprimitive(typ) or is_range_rprimitive(typ) or is_float_rprimitive(typ) @@ -625,6 +627,8 @@ def emit_cast( prefix = "PyDict" elif is_set_rprimitive(typ): prefix = "PySet" + elif is_frozenset_rprimitive(typ): + prefix = "PyFrozenSet" elif is_str_rprimitive(typ): prefix = "PyUnicode" elif is_range_rprimitive(typ): diff --git a/mypyc/doc/frozenset_operations.rst b/mypyc/doc/frozenset_operations.rst new file mode 100644 index 000000000000..a30b6a55c584 --- /dev/null +++ b/mypyc/doc/frozenset_operations.rst @@ -0,0 +1,29 @@ +.. _frozenset-ops: + +Native frozenset operations +====================== + +These ``frozenset`` operations have fast, optimized implementations. Other +frozenset operations use generic implementations that are often slower. + +Construction +------------ + +Construct empty frozenset: + +* ``frozenset()`` + +Construct frozenset from iterable: + +* ``frozenset(x: Iterable)`` + + +Operators +--------- + +* ``item in s`` + +Functions +--------- + +* ``len(s: set)`` diff --git a/mypyc/doc/index.rst b/mypyc/doc/index.rst index 584d6739e803..094e0f8cd9b8 100644 --- a/mypyc/doc/index.rst +++ b/mypyc/doc/index.rst @@ -41,6 +41,7 @@ generate fast code. dict_operations set_operations tuple_operations + frozenset_operations .. toctree:: :maxdepth: 2 diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 6e7e94a618ab..d5cc7a209491 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -461,6 +461,11 @@ def __hash__(self) -> int: # Python set object (or an instance of a subclass of set). set_rprimitive: Final = RPrimitive("builtins.set", is_unboxed=False, is_refcounted=True) +# Python frozenset object (or an instance of a subclass of frozenset). +frozenset_rprimitive: Final = RPrimitive( + "builtins.frozenset", is_unboxed=False, is_refcounted=True +) + # Python str object. At the C layer, str is referred to as unicode # (PyUnicode). str_rprimitive: Final = RPrimitive("builtins.str", is_unboxed=False, is_refcounted=True) @@ -565,6 +570,10 @@ def is_set_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.set" +def is_frozenset_rprimitive(rtype: RType) -> bool: + return isinstance(rtype, RPrimitive) and rtype.name == "builtins.frozenset" + + def is_str_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.str" diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 396d40938024..e7c256331842 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -99,6 +99,7 @@ is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_frozenset_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, @@ -2219,7 +2220,7 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val size_value = None if is_list_rprimitive(typ) or is_tuple_rprimitive(typ) or is_bytes_rprimitive(typ): size_value = self.primitive_op(var_object_size, [val], line) - elif is_set_rprimitive(typ): + elif is_set_rprimitive(typ) or is_frozenset_rprimitive(typ): elem_address = self.add(GetElementPtr(val, PySetObject, "used")) size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) self.add(KeepAlive([val])) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 9cd263c40ae4..7c6e03d0037c 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -33,6 +33,7 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, + frozenset_rprimitive, int16_rprimitive, int32_rprimitive, int64_rprimitive, @@ -89,6 +90,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return dict_rprimitive elif typ.type.fullname == "builtins.set": return set_rprimitive + elif typ.type.fullname == "builtins.frozenset": + return frozenset_rprimitive elif typ.type.fullname == "builtins.tuple": return tuple_rprimitive # Varying-length tuple elif typ.type.fullname == "builtins.range": diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index a0313861fb30..eb7c9b46609d 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -1,4 +1,4 @@ -"""Primitive set (and frozenset) ops.""" +"""Primitive set and frozenset ops.""" from __future__ import annotations @@ -7,6 +7,7 @@ bit_rprimitive, bool_rprimitive, c_int_rprimitive, + frozenset_rprimitive, object_rprimitive, pointer_rprimitive, set_rprimitive, @@ -44,11 +45,21 @@ error_kind=ERR_MAGIC, ) +# Construct an empty frozenset +function_op( + name="builtins.frozenset", + arg_types=[], + return_type=frozenset_rprimitive, + c_function_name="PyFrozenSet_New", + error_kind=ERR_MAGIC, + extra_int_constants=[(0, pointer_rprimitive)], +) + # frozenset(obj) function_op( name="builtins.frozenset", arg_types=[object_rprimitive], - return_type=object_rprimitive, + return_type=frozenset_rprimitive, c_function_name="PyFrozenSet_New", error_kind=ERR_MAGIC, ) @@ -64,6 +75,17 @@ ordering=[1, 0], ) +# item in frozenset +binary_op( + name="in", + arg_types=[object_rprimitive, frozenset_rprimitive], + return_type=c_int_rprimitive, + c_function_name="PySet_Contains", + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0], +) + # set.remove(obj) method_op( name="remove", diff --git a/mypyc/test-data/irbuild-frozenset.test b/mypyc/test-data/irbuild-frozenset.test new file mode 100644 index 000000000000..2fa84a2ed055 --- /dev/null +++ b/mypyc/test-data/irbuild-frozenset.test @@ -0,0 +1,115 @@ +[case testNewFrozenSet] +from typing import FrozenSet +def f() -> FrozenSet[int]: + return frozenset({1, 2, 3}) +[out] +def f(): + r0 :: set + r1 :: object + r2 :: i32 + r3 :: bit + r4 :: object + r5 :: i32 + r6 :: bit + r7 :: object + r8 :: i32 + r9 :: bit + r10 :: frozenset +L0: + r0 = PySet_New(0) + r1 = object 1 + r2 = PySet_Add(r0, r1) + r3 = r2 >= 0 :: signed + r4 = object 2 + r5 = PySet_Add(r0, r4) + r6 = r5 >= 0 :: signed + r7 = object 3 + r8 = PySet_Add(r0, r7) + r9 = r8 >= 0 :: signed + r10 = PyFrozenSet_New(r0) + return r10 + +[case testNewEmptyFrozenSet] +from typing import FrozenSet +def f1() -> FrozenSet[int]: + return frozenset() + +def f2() -> FrozenSet[int]: + return frozenset(()) +[out] +def f1(): + r0 :: frozenset +L0: + r0 = PyFrozenSet_New(0) + return r0 +def f2(): + r0 :: tuple[] + r1 :: object + r2 :: frozenset +L0: + r0 = () + r1 = box(tuple[], r0) + r2 = PyFrozenSet_New(r1) + return r2 + +[case testNewFrozenSetFromIterable] +from typing import FrozenSet, List, TypeVar + +T = TypeVar("T") + +def f(l: List[T]) -> FrozenSet[T]: + return frozenset(l) +[out] +def f(l): + l :: list + r0 :: frozenset +L0: + r0 = PyFrozenSet_New(l) + return r0 + +[case testFrozenSetSize] +from typing import FrozenSet +def f() -> int: + return len(frozenset((1, 2, 3))) +[out] +def f(): + r0 :: tuple[int, int, int] + r1 :: object + r2 :: frozenset + r3 :: ptr + r4 :: native_int + r5 :: short_int +L0: + r0 = (2, 4, 6) + r1 = box(tuple[int, int, int], r0) + r2 = PyFrozenSet_New(r1) + r3 = get_element_ptr r2 used :: PySetObject + r4 = load_mem r3 :: native_int* + keep_alive r2 + r5 = r4 << 1 + return r5 + +[case testFrozenSetContains] +from typing import FrozenSet +def f() -> bool: + x = frozenset((3, 4)) + return (5 in x) +[out] +def f(): + r0 :: tuple[int, int] + r1 :: object + r2, x :: frozenset + r3 :: object + r4 :: i32 + r5 :: bit + r6 :: bool +L0: + r0 = (6, 8) + r1 = box(tuple[int, int], r0) + r2 = PyFrozenSet_New(r1) + x = r2 + r3 = object 5 + r4 = PySet_Contains(x, r3) + r5 = r4 >= 0 :: signed + r6 = truncate r4: i32 to builtins.bool + return r6 diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index c1a00ce67504..6da3c26c42f7 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -686,7 +686,7 @@ def not_precomputed_nested_set(i): r1 :: object r2 :: i32 r3 :: bit - r4 :: object + r4 :: frozenset r5 :: set r6 :: i32 r7 :: bit diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test index 8d178d03a75b..57d5cde65bb8 100644 --- a/mypyc/test-data/run-sets.test +++ b/mypyc/test-data/run-sets.test @@ -116,6 +116,124 @@ s = {1, 2, 3} update(s, [5, 4, 3]) assert s == {1, 2, 3, 4, 5} +[case testFrozenSets] +from typing import FrozenSet, List, Any, cast +from testutil import assertRaises + +def instantiateLiteral() -> FrozenSet[int]: + return frozenset((1, 2, 3, 5, 8)) + +def emptyFrozenSet1() -> FrozenSet[int]: + return frozenset() + +def emptyFrozenSet2() -> FrozenSet[int]: + return frozenset(()) + +def fromIterator() -> List[FrozenSet[int]]: + a = frozenset([1, 3, 5]) + b = frozenset((1, 3, 5)) + c = frozenset({1, 3, 5}) + d = frozenset({1: '1', 3: '3', 5: '5'}) + e = frozenset(x for x in range(1, 6, 2)) + f = frozenset((x for x in range(1, 6, 2))) + return [a, b, c, d, e, f] + +def fromIterator2() -> FrozenSet[int]: + tmp_list = [1, 2, 3, 4, 5] + return frozenset((x + 1) for x in ((y * 10) for y in (z for z in tmp_list if z < 4))) + +def castFrozenSet() -> FrozenSet[int]: + x: Any = frozenset((1, 2, 3, 5, 8)) + return cast(FrozenSet, x) + +def castFrozenSetError() -> FrozenSet[int]: + x: Any = {1, 2, 3, 5, 8} + return cast(FrozenSet, x) + +def test_frozen_sets() -> None: + val = instantiateLiteral() + assert 1 in val + assert 2 in val + assert 3 in val + assert 5 in val + assert 8 in val + assert len(val) == 5 + assert val == {1, 2, 3, 5, 8} + s = 0 + for i in val: + s += i + assert s == 19 + + empty_set1 = emptyFrozenSet1() + assert empty_set1 == frozenset() + + empty_set2 = emptyFrozenSet2() + assert empty_set2 == frozenset() + + sets = fromIterator() + for s2 in sets: + assert s2 == {1, 3, 5} + + s3 = fromIterator2() + assert s3 == {11, 21, 31} + + val2 = castFrozenSet() + assert val2 == {1, 2, 3, 5, 8} + + with assertRaises(TypeError, "frozenset object expected; got set"): + castFrozenSetError() + +[case testFrozenSetsFromIterables] +from typing import FrozenSet + +def f(x: int) -> int: + return x + +def f1() -> FrozenSet[int]: + tmp_list = [1, 3, 5] + return frozenset(f(x) for x in tmp_list) + +def f2() -> FrozenSet[int]: + tmp_tuple = (1, 3, 5) + return frozenset(f(x) for x in tmp_tuple) + +def f3() -> FrozenSet[int]: + tmp_set = {1, 3, 5} + return frozenset(f(x) for x in tmp_set) + +def f4() -> FrozenSet[int]: + tmp_dict = {1: '1', 3: '3', 5: '5'} + return frozenset(f(x) for x in tmp_dict) + +def f5() -> FrozenSet[int]: + return frozenset(f(x) for x in range(1, 6, 2)) + +def f6() -> FrozenSet[int]: + return frozenset((f(x) for x in range(1, 6, 2))) + +def g1(x: int) -> int: + return x + +def g2(x: int) -> int: + return x * 10 + +def g3(x: int) -> int: + return x + 1 + +def g4() -> FrozenSet[int]: + tmp_list = [1, 2, 3, 4, 5] + return frozenset(g3(x) for x in (g2(y) for y in (g1(z) for z in tmp_list if z < 4))) + +def test_frozen_sets_from_iterables() -> None: + val = frozenset({1, 3, 5}) + assert f1() == val + assert f2() == val + assert f3() == val + assert f4() == val + assert f5() == val + assert f6() == val + assert g4() == frozenset({11, 21, 31}) + [case testPrecomputedFrozenSets] from typing import Any from typing_extensions import Final diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 5b3f678d8f17..9c0ad06416a7 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -32,6 +32,7 @@ "irbuild-str.test", "irbuild-bytes.test", "irbuild-float.test", + "irbuild-frozenset.test", "irbuild-statements.test", "irbuild-nested.test", "irbuild-classes.test", From 23e2d0f8cbaa2f4874d8f6a6bee3756922c78407 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 31 Jan 2025 16:53:27 +0000 Subject: [PATCH 1100/1617] [mypyc] Use lower-case generic types such as "list[t]" in docs (#18576) We no longer support 3.8, so all supported Python versions support `list[t]` and friends. --- mypyc/doc/differences_from_python.rst | 2 +- mypyc/doc/native_classes.rst | 2 +- mypyc/doc/performance_tips_and_tricks.rst | 5 ++--- mypyc/doc/using_type_annotations.rst | 16 ++++++++-------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst index f1d4d05a3a87..65ad709677af 100644 --- a/mypyc/doc/differences_from_python.rst +++ b/mypyc/doc/differences_from_python.rst @@ -107,7 +107,7 @@ performance. integer values. A side effect of this is that the exact runtime type of ``int`` values is lost. For example, consider this simple function:: - def first_int(x: List[int]) -> int: + def first_int(x: list[int]) -> int: return x[0] print(first_int([True])) # Output is 1, instead of True! diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst index b2935a6f7185..7f892de3e239 100644 --- a/mypyc/doc/native_classes.rst +++ b/mypyc/doc/native_classes.rst @@ -56,7 +56,7 @@ These non-native classes can be used as base classes of native classes: * ``object`` -* ``dict`` (and ``Dict[k, v]``) +* ``dict`` (and ``dict[k, v]``) * ``BaseException`` * ``Exception`` * ``ValueError`` diff --git a/mypyc/doc/performance_tips_and_tricks.rst b/mypyc/doc/performance_tips_and_tricks.rst index ae0b2950814c..5b3c1cb42cd7 100644 --- a/mypyc/doc/performance_tips_and_tricks.rst +++ b/mypyc/doc/performance_tips_and_tricks.rst @@ -57,12 +57,11 @@ here we call ``acme.get_items()``, but it has no type annotation. We can use an explicit type annotation for the variable to which we assign the result:: - from typing import List, Tuple import acme def work() -> None: # Annotate "items" to help mypyc - items: List[Tuple[int, str]] = acme.get_items() + items: list[tuple[int, str]] = acme.get_items() for item in items: ... # Do some work here @@ -140,7 +139,7 @@ Similarly, caching a frequently called method in a local variable can help in CPython, but it can slow things down in compiled code, since the code won't use :ref:`early binding `:: - def squares(n: int) -> List[int]: + def squares(n: int) -> list[int]: a = [] append = a.append # Not a good idea in compiled code! for i in range(n): diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 04c923819d54..dc0b04a974fd 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -37,10 +37,10 @@ implementations: * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) -* ``List[T]`` (:ref:`native operations `) -* ``Dict[K, V]`` (:ref:`native operations `) -* ``Set[T]`` (:ref:`native operations `) -* ``Tuple[T, ...]`` (variable-length tuple; :ref:`native operations `) +* ``list[T]`` (:ref:`native operations `) +* ``dict[K, V]`` (:ref:`native operations `) +* ``set[T]`` (:ref:`native operations `) +* ``tuple[T, ...]`` (variable-length tuple; :ref:`native operations `) * ``None`` The link after each type lists all supported native, optimized @@ -61,10 +61,10 @@ variable. For example, here we have a runtime type error on the final line of ``example`` (the ``Any`` type means an arbitrary, unchecked value):: - from typing import List, Any + from typing import Any - def example(a: List[Any]) -> None: - b: List[int] = a # No error -- items are not checked + def example(a: list[Any]) -> None: + b: list[int] = a # No error -- items are not checked print(b[0]) # Error here -- got str, but expected int example(["x"]) @@ -126,7 +126,7 @@ Tuple types Fixed-length `tuple types `_ -such as ``Tuple[int, str]`` are represented +such as ``tuple[int, str]`` are represented as :ref:`value types ` when stored in variables, passed as arguments, or returned from functions. Value types are allocated in the low-level machine stack or in CPU registers, as From f44a60dd9d02ce496561c08ded134d5e2e3bc8ca Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 1 Feb 2025 02:43:47 +0100 Subject: [PATCH 1101/1617] Remove support for `builtins.Any` (#18578) While doing some cleanup, I noticed a reference to `builtins.Any`. To the best of my knowledge, it was never implemented. --- mypy/typeanal.py | 2 +- test-data/unit/check-ctypes.test | 1 + test-data/unit/fixtures/float.pyi | 4 +--- test-data/unit/fixtures/floatdict.pyi | 4 +--- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index fa7cf4242d82..06e3aef33d7f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -616,7 +616,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ """ if fullname == "builtins.None": return NoneType() - elif fullname == "typing.Any" or fullname == "builtins.Any": + elif fullname == "typing.Any": return AnyType(TypeOfAny.explicit, line=t.line, column=t.column) elif fullname in FINAL_TYPE_NAMES: if self.prohibit_special_class_field_types: diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test index 1eefdd3c66c1..1e58ebc77d0f 100644 --- a/test-data/unit/check-ctypes.test +++ b/test-data/unit/check-ctypes.test @@ -138,6 +138,7 @@ cua.raw # E: Array attribute "raw" is only available with element type "c_char" [case testCtypesAnyArrayAttrs] import ctypes +from typing import Any aa: ctypes.Array[Any] reveal_type(aa.value) # N: Revealed type is "Any" diff --git a/test-data/unit/fixtures/float.pyi b/test-data/unit/fixtures/float.pyi index 5db4525849c0..9e2d20f04edf 100644 --- a/test-data/unit/fixtures/float.pyi +++ b/test-data/unit/fixtures/float.pyi @@ -1,8 +1,6 @@ -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Any T = TypeVar('T') -Any = 0 - class object: def __init__(self) -> None: pass diff --git a/test-data/unit/fixtures/floatdict.pyi b/test-data/unit/fixtures/floatdict.pyi index 7baa7ca9206f..10586218b551 100644 --- a/test-data/unit/fixtures/floatdict.pyi +++ b/test-data/unit/fixtures/floatdict.pyi @@ -1,11 +1,9 @@ -from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union +from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Any T = TypeVar('T') KT = TypeVar('KT') VT = TypeVar('VT') -Any = 0 - class object: def __init__(self) -> None: pass From 1f509eca228b7efde9a70bcc4d8e8fe4ee99093e Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 3 Feb 2025 00:44:34 +0100 Subject: [PATCH 1102/1617] Fix a few PR links in the changelog (#18586) And add a local pre-commit hook to detect when a PR number in a link text is different from the link body --- .pre-commit-config.yaml | 8 ++++++++ CHANGELOG.md | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 050f01b063cf..b2319b3925bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,6 +39,14 @@ repos: rev: v1.0.1 hooks: - id: zizmor + - repo: local + hooks: + - id: bad-pr-link + name: Bad PR link + description: Detect PR links text that don't match their URL + language: pygrep + entry: '\[(\d+)\]\(https://github.com/python/mypy/pull/(?!\1/?\))\d+/?\)' + files: CHANGELOG.md # Should be the last one: - repo: meta hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3acec84fec5d..bc3a0f83d907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ By default, mypy treats an annotation of ``bytes`` as permitting ``bytearray`` a [PEP 688](https://peps.python.org/pep-0688) specified the removal of this special case. Use this flag to disable this behavior. `--strict-bytes` will be enabled by default in **mypy 2.0**. -Contributed by Ali Hamdan (PR [18137](https://github.com/python/mypy/pull/18263/)) and +Contributed by Ali Hamdan (PR [18263](https://github.com/python/mypy/pull/18263)) and Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). ### Improvements to reachability analysis and partial type handling in loops @@ -36,7 +36,7 @@ issues it previously did not detect. In some cases, this change may require use explicit annotation of a variable. Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180), -[PR](https://github.com/python/mypy/pull/18433)). +PR [18433](https://github.com/python/mypy/pull/18433)). (Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` by default in **mypy 2.0**). @@ -49,7 +49,7 @@ configuration files. See the for more details. Contributed by Mikhail Shiryaev and Shantanu Jain -(PR [16965](https://github.com/python/mypy/pull/16965), PR [18482](https://github.com/python/mypy/pull/18482) +(PR [16965](https://github.com/python/mypy/pull/16965), PR [18482](https://github.com/python/mypy/pull/18482)) ### Better line numbers for decorators and slice expressions From c8489a2fb79049699eee1110d8397a65ed4155c2 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 3 Feb 2025 10:13:45 +0000 Subject: [PATCH 1103/1617] [mypyc] Handle non extention classes with attribute annotations for forward defined classes (#18577) This PR makes `add_non_ext_class_attr_ann` behave the same way standard python handles modules with `from __future__ import annotations` by using string types. With this we can reference types declared further in the file. But since this will change in future versions of python, let's only do this for forward references, for types that are defined further down in the same module. This also works with string type annotations. Fixes https://github.com/mypyc/mypyc/issues/992 --- mypyc/irbuild/classdef.py | 11 ++++++++++- mypyc/test-data/run-classes.test | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index b01e16f57b88..01224adb8a00 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -625,7 +625,16 @@ def add_non_ext_class_attr_ann( if get_type_info is not None: type_info = get_type_info(stmt) if type_info: - typ = load_type(builder, type_info, stmt.line) + # NOTE: Using string type information is similar to using + # `from __future__ import annotations` in standard python. + # NOTE: For string types we need to use the fullname since it + # includes the module. If string type doesn't have the module, + # @dataclass will try to get the current module and fail since the + # current module is not in sys.modules. + if builder.current_module == type_info.module_name and stmt.line < type_info.line: + typ = builder.load_str(type_info.fullname) + else: + typ = load_type(builder, type_info, stmt.line) if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 168477d5a8ee..60abf76be1e6 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2719,3 +2719,32 @@ print(native.A(ints=[1, -17]).ints) [out] \[1, -17] + +[case testDataclassClassReference] +from __future__ import annotations +from dataclasses import dataclass + +class BackwardDefinedClass: + pass + +@dataclass +class Data: + bitem: BackwardDefinedClass + bitems: 'BackwardDefinedClass' + fitem: ForwardDefinedClass + fitems: 'ForwardDefinedClass' + +class ForwardDefinedClass: + pass + +def test_function(): + d = Data( + bitem=BackwardDefinedClass(), + bitems=BackwardDefinedClass(), + fitem=ForwardDefinedClass(), + fitems=ForwardDefinedClass(), + ) + assert(isinstance(d.bitem, BackwardDefinedClass)) + assert(isinstance(d.bitems, BackwardDefinedClass)) + assert(isinstance(d.fitem, ForwardDefinedClass)) + assert(isinstance(d.fitems, ForwardDefinedClass)) From 274af1c14d3a3d8bf7625187d5a9775a79c97c34 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 3 Feb 2025 14:53:40 +0000 Subject: [PATCH 1104/1617] Fix inference when class and instance match protocol (#18587) Fixes https://github.com/python/mypy/issues/14688 The bug resulted from (accidentally) inferring against `Iterable` for both instance and class object. While working on this I noticed there are also couple flaws in direction handling in constrain inference, namely: * A protocol can never ever be a subtype of class object or a `Type[X]` * When matching against callback protocol, subtype check direction must match inference direction I also (conservatively) fix some unrelated issues uncovered by the fix (to avoid fallout): * Callable subtyping with trivial suffixes was broken for positional-only args * Join of `Parameters` could lead to meaningless results in case of incompatible arg kinds * Protocol inference was inconsistent with protocol subtyping w.r.t. metaclasses. --- mypy/constraints.py | 51 +++++++++++-------- mypy/join.py | 12 ++++- mypy/subtypes.py | 9 +++- test-data/unit/check-enum.test | 22 ++++++++ test-data/unit/check-functions.test | 30 +++++++++-- .../unit/check-parameter-specification.test | 27 ++++++++++ test-data/unit/fixtures/enum.pyi | 9 +++- 7 files changed, 132 insertions(+), 28 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 45a96b993563..defcac21bc66 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -756,40 +756,40 @@ def visit_instance(self, template: Instance) -> list[Constraint]: "__call__", template, actual, is_operator=True ) assert call is not None - if mypy.subtypes.is_subtype(actual, erase_typevars(call)): - subres = infer_constraints(call, actual, self.direction) - res.extend(subres) + if ( + self.direction == SUPERTYPE_OF + and mypy.subtypes.is_subtype(actual, erase_typevars(call)) + or self.direction == SUBTYPE_OF + and mypy.subtypes.is_subtype(erase_typevars(call), actual) + ): + res.extend(infer_constraints(call, actual, self.direction)) template.type.inferring.pop() if isinstance(actual, CallableType) and actual.fallback is not None: - if actual.is_type_obj() and template.type.is_protocol: + if ( + actual.is_type_obj() + and template.type.is_protocol + and self.direction == SUPERTYPE_OF + ): ret_type = get_proper_type(actual.ret_type) if isinstance(ret_type, TupleType): ret_type = mypy.typeops.tuple_fallback(ret_type) if isinstance(ret_type, Instance): - if self.direction == SUBTYPE_OF: - subtype = template - else: - subtype = ret_type res.extend( self.infer_constraints_from_protocol_members( - ret_type, template, subtype, template, class_obj=True + ret_type, template, ret_type, template, class_obj=True ) ) actual = actual.fallback if isinstance(actual, TypeType) and template.type.is_protocol: - if isinstance(actual.item, Instance): - if self.direction == SUBTYPE_OF: - subtype = template - else: - subtype = actual.item - res.extend( - self.infer_constraints_from_protocol_members( - actual.item, template, subtype, template, class_obj=True - ) - ) if self.direction == SUPERTYPE_OF: - # Infer constraints for Type[T] via metaclass of T when it makes sense. a_item = actual.item + if isinstance(a_item, Instance): + res.extend( + self.infer_constraints_from_protocol_members( + a_item, template, a_item, template, class_obj=True + ) + ) + # Infer constraints for Type[T] via metaclass of T when it makes sense. if isinstance(a_item, TypeVarType): a_item = get_proper_type(a_item.upper_bound) if isinstance(a_item, Instance) and a_item.type.metaclass_type: @@ -1043,6 +1043,17 @@ def infer_constraints_from_protocol_members( return [] # See #11020 # The above is safe since at this point we know that 'instance' is a subtype # of (erased) 'template', therefore it defines all protocol members + if class_obj: + # For class objects we must only infer constraints if possible, otherwise it + # can lead to confusion between class and instance, for example StrEnum is + # Iterable[str] for an instance, but Iterable[StrEnum] for a class object. + if not mypy.subtypes.is_subtype( + inst, erase_typevars(temp), ignore_pos_arg_names=True + ): + continue + # This exception matches the one in subtypes.py, see PR #14121 for context. + if member == "__call__" and instance.type.is_metaclass(): + continue res.extend(infer_constraints(temp, inst, self.direction)) if mypy.subtypes.IS_SETTABLE in mypy.subtypes.get_member_flags(member, protocol): # Settable members are invariant, add opposite constraints diff --git a/mypy/join.py b/mypy/join.py index 166434f58f8d..9fa6e27207f4 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -355,7 +355,8 @@ def visit_unpack_type(self, t: UnpackType) -> UnpackType: def visit_parameters(self, t: Parameters) -> ProperType: if isinstance(self.s, Parameters): - if len(t.arg_types) != len(self.s.arg_types): + if not is_similar_params(t, self.s): + # TODO: it would be prudent to return [*object, **object] instead of Any. return self.default(self.s) from mypy.meet import meet_types @@ -724,6 +725,15 @@ def is_similar_callables(t: CallableType, s: CallableType) -> bool: ) +def is_similar_params(t: Parameters, s: Parameters) -> bool: + # This matches the logic in is_similar_callables() above. + return ( + len(t.arg_types) == len(s.arg_types) + and t.min_args == s.min_args + and (t.var_arg() is not None) == (s.var_arg() is not None) + ) + + def update_callable_ids(c: CallableType, ids: list[TypeVarId]) -> CallableType: tv_map = {} tvs = [] diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 804930fc9d0c..75cc7e25fde3 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1719,11 +1719,16 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N ): return False + if trivial_suffix: + # For trivial right suffix we *only* check that every non-star right argument + # has a valid match on the left. + return True + # Phase 1c: Check var args. Right has an infinite series of optional positional # arguments. Get all further positional args of left, and make sure # they're more general than the corresponding member in right. # TODO: are we handling UnpackType correctly here? - if right_star is not None and not trivial_suffix: + if right_star is not None: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) assert right_by_position is not None @@ -1750,7 +1755,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1d: Check kw args. Right has an infinite series of optional named # arguments. Get all further named args of left, and make sure # they're more general than the corresponding member in right. - if right_star2 is not None and not trivial_suffix: + if right_star2 is not None: right_names = {name for name in right.arg_names if name is not None} left_only_names = set() for name, kind in zip(left.arg_names, left.arg_kinds): diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 37c63f43179d..4b7460696aec 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -2394,3 +2394,25 @@ def do_check(value: E) -> None: [builtins fixtures/primitives.pyi] [typing fixtures/typing-full.pyi] + +[case testStrEnumClassCorrectIterable] +from enum import StrEnum +from typing import Type, TypeVar + +class Choices(StrEnum): + LOREM = "lorem" + IPSUM = "ipsum" + +var = list(Choices) +reveal_type(var) # N: Revealed type is "builtins.list[__main__.Choices]" + +e: type[StrEnum] +reveal_type(list(e)) # N: Revealed type is "builtins.list[enum.StrEnum]" + +T = TypeVar("T", bound=StrEnum) +def list_vals(e: Type[T]) -> list[T]: + reveal_type(list(e)) # N: Revealed type is "builtins.list[T`-1]" + return list(e) + +reveal_type(list_vals(Choices)) # N: Revealed type is "builtins.list[__main__.Choices]" +[builtins fixtures/enum.pyi] diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 58973307a1ae..ccce2cb96a88 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -105,16 +105,38 @@ if int(): h = h [case testSubtypingFunctionsDoubleCorrespondence] +def l(x) -> None: ... +def r(__x, *, x) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Any, NamedArg(Any, 'x')], None]") +[case testSubtypingFunctionsDoubleCorrespondenceNamedOptional] def l(x) -> None: ... -def r(__, *, x) -> None: ... -r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Any, NamedArg(Any, 'x')], None]") +def r(__x, *, x = 1) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Any, DefaultNamedArg(Any, 'x')], None]") -[case testSubtypingFunctionsRequiredLeftArgNotPresent] +[case testSubtypingFunctionsDoubleCorrespondenceBothNamedOptional] +def l(x = 1) -> None: ... +def r(__x, *, x = 1) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Any, DefaultNamedArg(Any, 'x')], None]") + +[case testSubtypingFunctionsTrivialSuffixRequired] +def l(__x) -> None: ... +def r(x, *args, **kwargs) -> None: ... + +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Arg(Any, 'x'), VarArg(Any), KwArg(Any)], None]") +[builtins fixtures/dict.pyi] +[case testSubtypingFunctionsTrivialSuffixOptional] +def l(__x = 1) -> None: ... +def r(x = 1, *args, **kwargs) -> None: ... + +r = l # E: Incompatible types in assignment (expression has type "Callable[[DefaultArg(Any)], None]", variable has type "Callable[[DefaultArg(Any, 'x'), VarArg(Any), KwArg(Any)], None]") +[builtins fixtures/dict.pyi] + +[case testSubtypingFunctionsRequiredLeftArgNotPresent] def l(x, y) -> None: ... def r(x) -> None: ... -r = l # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") [case testSubtypingFunctionsImplicitNames] from typing import Any diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 352503023f97..f938226f8472 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2532,3 +2532,30 @@ class GenericWrapper(Generic[P]): def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... [builtins fixtures/paramspec.pyi] + +[case testCallbackProtocolClassObjectParamSpec] +from typing import Any, Callable, Protocol, Optional, Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +class App: ... + +class MiddlewareFactory(Protocol[P]): + def __call__(self, app: App, /, *args: P.args, **kwargs: P.kwargs) -> App: + ... + +class Capture(Generic[P]): ... + +class ServerErrorMiddleware(App): + def __init__( + self, + app: App, + handler: Optional[str] = None, + debug: bool = False, + ) -> None: ... + +def fn(f: MiddlewareFactory[P]) -> Capture[P]: ... + +reveal_type(fn(ServerErrorMiddleware)) # N: Revealed type is "__main__.Capture[[handler: Union[builtins.str, None] =, debug: builtins.bool =]]" +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi index 135e9cd16e7c..22e7193da041 100644 --- a/test-data/unit/fixtures/enum.pyi +++ b/test-data/unit/fixtures/enum.pyi @@ -1,5 +1,5 @@ # Minimal set of builtins required to work with Enums -from typing import TypeVar, Generic +from typing import TypeVar, Generic, Iterator, Sequence, overload, Iterable T = TypeVar('T') @@ -13,6 +13,13 @@ class tuple(Generic[T]): class int: pass class str: def __len__(self) -> int: pass + def __iter__(self) -> Iterator[str]: pass class dict: pass class ellipsis: pass + +class list(Sequence[T]): + @overload + def __init__(self) -> None: pass + @overload + def __init__(self, x: Iterable[T]) -> None: pass From 02f9a7082fb5ea25003aa3b1b2be5f0f3caf7105 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 3 Feb 2025 14:55:01 +0000 Subject: [PATCH 1105/1617] Add object self-type to tuple test fixture (#18592) This makes it more similar to the real typeshed. It is needed to reproduce tricky failures in tests, e.g. https://github.com/python/mypy/pull/18585. If this causes slower tests, some tests may be switched to `tuple-simple.pyi`. --- test-data/unit/fixtures/tuple.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index 3b62d7fc1513..d01cd0034d26 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -1,13 +1,14 @@ # Builtins stub used in tuple-related test cases. import _typeshed -from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type +from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type, Self _T = TypeVar("_T") _Tco = TypeVar('_Tco', covariant=True) class object: def __init__(self) -> None: pass + def __new__(cls) -> Self: ... class type: def __init__(self, *a: object) -> None: pass From 237933a5428fe1c6a510d6da71e7695117d720e5 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 3 Feb 2025 20:54:40 +0100 Subject: [PATCH 1106/1617] Prevent crash on generic NamedTuple with unresolved typevar bound (#18585) Fixes #18582. Fixes #17396. Supersedes #18351. --------- Co-authored-by: hauntsaninja --- mypy/checker.py | 5 +++++ mypy/type_visitor.py | 4 ++-- test-data/unit/check-incremental.test | 19 +++++++++++++++++++ test-data/unit/check-inference.test | 8 ++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c69b80a55fd9..35c883276029 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8485,6 +8485,11 @@ def visit_type_var(self, t: TypeVarType) -> bool: # multi-step type inference. return t.id.is_meta_var() + def visit_tuple_type(self, t: TupleType, /) -> bool: + # Exclude fallback to avoid bogus "need type annotation" errors + # TODO: Maybe erase plain tuples used as fallback in TupleType constructor? + return self.query_types(t.items) + class SetNothingToAny(TypeTranslator): """Replace all ambiguous Uninhabited types with Any (to avoid spurious extra errors).""" diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index f62d67bc26cc..d935b9a47a51 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -410,7 +410,7 @@ def visit_callable_type(self, t: CallableType, /) -> T: return self.query_types(t.arg_types + [t.ret_type]) def visit_tuple_type(self, t: TupleType, /) -> T: - return self.query_types(t.items) + return self.query_types([t.partial_fallback] + t.items) def visit_typeddict_type(self, t: TypedDictType, /) -> T: return self.query_types(t.items.values()) @@ -550,7 +550,7 @@ def visit_callable_type(self, t: CallableType, /) -> bool: return args and ret def visit_tuple_type(self, t: TupleType, /) -> bool: - return self.query_types(t.items) + return self.query_types([t.partial_fallback] + t.items) def visit_typeddict_type(self, t: TypedDictType, /) -> bool: return self.query_types(list(t.items.values())) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 2cc072eb16e7..6b888c0047c3 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6829,3 +6829,22 @@ reveal_type(a.f) tmp/b.py:4: note: Revealed type is "builtins.int" tmp/b.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "str") tmp/b.py:6: note: Revealed type is "builtins.int" + +[case testSerializeDeferredGenericNamedTuple] +import pkg +[file pkg/__init__.py] +from .lib import NT +[file pkg/lib.py] +from typing import Generic, NamedTuple, TypeVar +from pkg import does_not_exist # type: ignore +from pkg.missing import also_missing # type: ignore + +T = TypeVar("T", bound=does_not_exist) +class NT(NamedTuple, Generic[T]): + values: also_missing[T] +[file pkg/__init__.py.2] +# touch +from .lib import NT +[builtins fixtures/tuple.pyi] +[out] +[out2] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0da1c092efe8..bdd0ac305904 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3886,3 +3886,11 @@ def a4(x: List[str], y: List[Never]) -> None: reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]" z1[1].append("asdf") # E: "object" has no attribute "append" [builtins fixtures/dict.pyi] + +[case testTupleJoinFallbackInference] +foo = [ + (1, ("a", "b")), + (2, []), +] +reveal_type(foo) # N: Revealed type is "builtins.list[Tuple[builtins.int, typing.Sequence[builtins.str]]]" +[builtins fixtures/tuple.pyi] From 947f6507b82639aba221d7a9ab09548658b1e6bb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 4 Feb 2025 07:06:25 +0000 Subject: [PATCH 1107/1617] Use lower case "list" and "dict" in invariance notes (#18594) All supported Python versions support `list[...]` and `dict[...]`. --- mypy/messages.py | 4 ++-- test-data/unit/check-basic.test | 4 ++-- test-data/unit/check-functions.test | 6 +++--- test-data/unit/check-inference.test | 4 ++-- test-data/unit/check-literal.test | 2 +- test-data/unit/check-optional.test | 2 +- test-data/unit/check-overloading.test | 2 +- test-data/unit/check-typevar-values.test | 2 +- test-data/unit/check-unions.test | 2 +- test-data/unit/check-varargs.test | 6 +++--- test-data/unit/cmdline.test | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 04ab40fc4474..3beb287bcc21 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -3270,7 +3270,7 @@ def append_invariance_notes( and expected_type.type.fullname == "builtins.list" and is_subtype(arg_type.args[0], expected_type.args[0]) ): - invariant_type = "List" + invariant_type = "list" covariant_suggestion = 'Consider using "Sequence" instead, which is covariant' elif ( arg_type.type.fullname == "builtins.dict" @@ -3278,7 +3278,7 @@ def append_invariance_notes( and is_same_type(arg_type.args[0], expected_type.args[0]) and is_subtype(arg_type.args[1], expected_type.args[1]) ): - invariant_type = "Dict" + invariant_type = "dict" covariant_suggestion = ( 'Consider using "Mapping" instead, which is covariant in the value type' ) diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 959d80cb2104..13968bdfb885 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -379,7 +379,7 @@ from typing import List x: List[int] y: List[float] y = x # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] @@ -388,7 +388,7 @@ from typing import Dict x: Dict[str, int] y: Dict[str, float] y = x # E: Incompatible types in assignment (expression has type "Dict[str, int]", variable has type "Dict[str, float]") \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index ccce2cb96a88..92a74a717893 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2503,14 +2503,14 @@ from typing import Union, Dict, List def f() -> List[Union[str, int]]: x = ['a'] return x # E: Incompatible return value type (got "List[str]", expected "List[Union[str, int]]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant \ # N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]" def g() -> Dict[str, Union[str, int]]: x = {'a': 'a'} return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[str, Union[str, int]]") \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type \ # N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]" @@ -2522,7 +2522,7 @@ def h() -> Dict[Union[str, int], str]: def i() -> List[Union[int, float]]: x: List[int] = [1] return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index bdd0ac305904..473a3f9d3df6 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1535,7 +1535,7 @@ if int(): if int(): a = x3 \ # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-medium.pyi] @@ -1558,7 +1558,7 @@ if int(): if int(): a = x3 \ # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 856bc941435d..c5d834374d0d 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -947,7 +947,7 @@ a: List[Literal[1]] b: List[Literal[1, 2, 3]] foo(a) # E: Argument 1 to "foo" has incompatible type "List[Literal[1]]"; expected "List[Literal[1, 2]]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant foo(b) # E: Argument 1 to "foo" has incompatible type "List[Literal[1, 2, 3]]"; expected "List[Literal[1, 2]]" bar(a) diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index c14b6ae376ae..5d866345c66f 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -786,7 +786,7 @@ asdf(x) strict_optional = False [out] main:4: error: Argument 1 to "asdf" has incompatible type "List[str]"; expected "List[Optional[str]]" -main:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +main:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance main:4: note: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 5b8bd51ff9dc..2092f99487b0 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1415,7 +1415,7 @@ main:18: note: Revealed type is "builtins.str" main:19: note: Revealed type is "Any" main:20: note: Revealed type is "Union[builtins.int, builtins.str]" main:21: error: Argument 1 to "foo" has incompatible type "List[bool]"; expected "List[int]" -main:21: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +main:21: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance main:21: note: Consider using "Sequence" instead, which is covariant main:22: error: Argument 1 to "foo" has incompatible type "List[object]"; expected "List[int]" main:23: error: Argument 1 to "foo" has incompatible type "List[Union[int, str]]"; expected "List[int]" diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index f932cf53c1d4..500dd6be4ffa 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -21,7 +21,7 @@ if int(): s = f('') o = f(1) \ # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 329896f7a1a7..cea1305ddc7d 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1050,7 +1050,7 @@ def do_thing_with_enums(enums: Union[List[Enum], Enum]) -> None: ... boop: List[Boop] = [] do_thing_with_enums(boop) # E: Argument 1 to "do_thing_with_enums" has incompatible type "List[Boop]"; expected "Union[List[Enum], Enum]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index bb0e80acee1e..4405948367cb 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -679,12 +679,12 @@ b = {'b': ['c', 'd']} c = {'c': 1.0} d = {'d': 1} f(a) # E: Argument 1 to "f" has incompatible type "Dict[str, List[int]]"; expected "Dict[str, Sequence[int]]" \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type f(b) # E: Argument 1 to "f" has incompatible type "Dict[str, List[str]]"; expected "Dict[str, Sequence[int]]" g(c) g(d) # E: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "Dict[str, float]" \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type h(c) # E: Argument 1 to "h" has incompatible type "Dict[str, float]"; expected "Dict[str, int]" h(d) @@ -696,7 +696,7 @@ from typing import List, Union def f(numbers: List[Union[int, float]]) -> None: pass a = [1, 2] f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Union[int, float]]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant x = [1] y = ['a'] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 2bab19e0d42f..2c53266866f4 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -832,7 +832,7 @@ y = [] # type: List[int] x = y [out] bad.py:4: error: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") -bad.py:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +bad.py:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance bad.py:4: note: Consider using "Sequence" instead, which is covariant Found 1 error in 1 file (checked 1 source file) From c30670ebd46ddffe8287697be918128ad6b30e65 Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Tue, 4 Feb 2025 08:34:14 -0600 Subject: [PATCH 1108/1617] Prepare changelog for 1.15 release (#18583) Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- CHANGELOG.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc3a0f83d907..dbc6cf576709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,30 @@ # Mypy Release Notes -## Next release +## Next Release -### Performance improvements +... + +## Mypy 1.15 (Unreleased) + +We’ve just uploaded mypy 1.15 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +Mypy is a static type checker for Python. This release includes new features and bug fixes. +You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +### Performance Improvements Mypy may be 5-30% faster. This improvement comes largely from tuning the performance of the -garbage collector. +garbage collector. Additionally, the release includes several micro-optimizations that may be +impactful for very large projects. -Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306)). +Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306), +PR [18302](https://github.com/python/mypy/pull/18302, PR [18298](https://github.com/python/mypy/pull/18298, +PR [18299](https://github.com/python/mypy/pull/18299). -### Mypyc accelerated mypy wheels for aarch64 +### Mypyc Accelerated Mypy Wheels for `aarch64` Mypy can compile itself to C extension modules using mypyc. This makes mypy 3-5x faster than if mypy is interpreted with pure Python. We now build and upload mypyc accelerated @@ -29,7 +44,7 @@ Use this flag to disable this behavior. `--strict-bytes` will be enabled by defa Contributed by Ali Hamdan (PR [18263](https://github.com/python/mypy/pull/18263)) and Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). -### Improvements to reachability analysis and partial type handling in loops +### Improvements to Reachability Analysis and Partial Type Handling in Loops This change results in mypy better modelling control flow within loops and hence detecting several issues it previously did not detect. In some cases, this change may require use of an additional @@ -41,7 +56,7 @@ PR [18433](https://github.com/python/mypy/pull/18433)). (Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` by default in **mypy 2.0**). -### Better discovery of configuration files +### Better Discovery of Configuration Files Mypy will now walk up the filesystem (up until a repository or file system root) to discover configuration files. See the @@ -51,7 +66,7 @@ for more details. Contributed by Mikhail Shiryaev and Shantanu Jain (PR [16965](https://github.com/python/mypy/pull/16965), PR [18482](https://github.com/python/mypy/pull/18482)) -### Better line numbers for decorators and slice expressions +### Better Line Numbers for Decorators and Slice Expressions Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, this may necessitate changing the location of a `# type: ignore` comment. @@ -68,6 +83,105 @@ Support for this will be dropped in the first half of 2025! Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). +### Mypyc Improvements + + * Fix `__init__` for classes with `@attr.s(slots=True)` (Advait Dixit, PR [18447](https://github.com/python/mypy/pull/18447)) + * Report error for nested class instead of crashing (Valentin Stanciu, PR [18460](https://github.com/python/mypy/pull/18460)) + * Fix `InitVar` for dataclasses (Advait Dixit, PR [18319](https://github.com/python/mypy/pull/18319)) + * Remove unnecessary mypyc files from wheels (Marc Mueller, PR [18416](https://github.com/python/mypy/pull/18416)) + * Get capsule pointer from module instead of `PyCapsule_Import` (Advait Dixit, PR [18286](https://github.com/python/mypy/pull/18286)) + * Add lowered primitive for unsafe list get item op (Jukka Lehtosalo, PR [18136](https://github.com/python/mypy/pull/18136)) + * Fix iteration over `NamedTuple` objects (Advait Dixit, PR [18254](https://github.com/python/mypy/pull/18254)) + * Mark mypyc package with `py.typed` (bzoracler, PR [18253](https://github.com/python/mypy/pull/18253)) + * Update docstrings of IR builder classes (Jukka Lehtosalo, PR [18246](https://github.com/python/mypy/pull/18246)) + * Fix list index while checking for `Enum` class (Advait Dixit, PR [18426](https://github.com/python/mypy/pull/18426)) + * Update `pythoncapi_compat.h` (Marc Mueller, PR [18340](https://github.com/python/mypy/pull/18340)) + +### Stubgen Improvements + + * Improve dataclass init signatures (Marc Mueller, PR [18430](https://github.com/python/mypy/pull/18430)) + * Preserve `dataclass_transform` decorator (Marc Mueller, PR [18418](https://github.com/python/mypy/pull/18418)) + * Fix `UnpackType` for 3.11+ (Marc Mueller, PR [18421](https://github.com/python/mypy/pull/18421)) + * Improve `self` annotations (Marc Mueller, PR [18420](https://github.com/python/mypy/pull/18420)) + * Print `InspectError` traceback in stubgen `walk_packages` when verbose is specified (Gareth, PR [18224](https://github.com/python/mypy/pull/18224)) + +### Stubtest Improvements + + * Fix crash with numpy array default values (Ali Hamdan, PR [18353](https://github.com/python/mypy/pull/18353)) + * Distinguish metaclass attributes from class attributes (Stephen Morton, PR [18314](https://github.com/python/mypy/pull/18314)) + +### Fixes to Crashes + + * Prevent crash with `Unpack` of a fixed tuple in PEP695 type alias (Stanislav Terliakov, PR [18451](https://github.com/python/mypy/pull/18451)) + * Fix crash with `--cache-fine-grained --cache-dir=/dev/null` (Shantanu, PR [18457](https://github.com/python/mypy/pull/18457)) + * Prevent crashing when `match` arms use name of existing callable (Stanislav Terliakov, PR [18449](https://github.com/python/mypy/pull/18449)) + * Gracefully handle encoding errors when writing to stdout (Brian Schubert, PR [18292](https://github.com/python/mypy/pull/18292)) + +### Documentation Updates + + * Add `sphinx_inline_tabs` to docs (Marc Mueller, PR [18262](https://github.com/python/mypy/pull/18262)) + * Document any `TYPE_CHECKING` name works (Shantanu, PR [18443](https://github.com/python/mypy/pull/18443)) + * Update docs not to mention 3.8 where possible (sobolevn, PR [18455](https://github.com/python/mypy/pull/18455)) + * Mention `ignore_errors` in exclude docs (Shantanu, PR [18412](https://github.com/python/mypy/pull/18412)) + * Add `Self` misuse to common issues (Shantanu, PR [18261](https://github.com/python/mypy/pull/18261)) + +### Other Notable Fixes and Improvements + + * Fix literal context for ternary expressions (Ivan Levkivskyi, PR [18545](https://github.com/python/mypy/pull/18545)) + * Ignore `dataclass.__replace__` LSP violations (Marc Mueller, PR [18464](https://github.com/python/mypy/pull/18464)) + * Bind `self` to the class being defined when checking multiple inheritance (Stanislav Terliakov, PR [18465](https://github.com/python/mypy/pull/18465)) + * Fix attribute type resolution with multiple inheritance (Stanislav Terliakov, PR [18415](https://github.com/python/mypy/pull/18415)) + * Improve security of our GitHub Actions (sobolevn, PR [18413](https://github.com/python/mypy/pull/18413)) + * Unwrap `type[Union[...]]` when solving typevar constraints (Stanislav Terliakov, PR [18266](https://github.com/python/mypy/pull/18266)) + * Allow `Any` to match sequence patterns in match/case (Stanislav Terliakov, PR [18448](https://github.com/python/mypy/pull/18448)) + * Fix parent generics mapping when overriding generic attribute with property (Stanislav Terliakov, PR [18441](https://github.com/python/mypy/pull/18441)) + * Dedicated error code for explicit `Any` (Shantanu, PR [18398](https://github.com/python/mypy/pull/18398)) + * Reject invalid `ParamSpec` locations (Stanislav Terliakov, PR [18278](https://github.com/python/mypy/pull/18278)) + * Remove stubs no longer in typeshed (Shantanu, PR [18373](https://github.com/python/mypy/pull/18373)) + * Allow inverting `--local-partial-types` (Shantanu, PR [18377](https://github.com/python/mypy/pull/18377)) + * Allow to use `Final` and `ClassVar` after Python 3.13 (정승원, PR [18358](https://github.com/python/mypy/pull/18358)) + * Update to include latest stubs in typeshed (Shantanu, PR [18366](https://github.com/python/mypy/pull/18366)) + * Fix `--install-types` masking failure details (wyattscarpenter, PR [17485](https://github.com/python/mypy/pull/17485)) + * Reject promotions when checking against protocols (Christoph Tyralla, PR [18360](https://github.com/python/mypy/pull/18360)) + * Don't erase type object args in diagnostics (Shantanu, PR [18352](https://github.com/python/mypy/pull/18352)) + * Clarify status in `dmypy status` output (Kcornw, PR [18331](https://github.com/python/mypy/pull/18331)) + * Disallow no-args generic aliases when using PEP 613 explicit aliases (Brian Schubert, PR [18173](https://github.com/python/mypy/pull/18173)) + * Suppress errors for unreachable branches in conditional expressions (Brian Schubert, PR [18295](https://github.com/python/mypy/pull/18295)) + * Do not allow `ClassVar` and `Final` in `TypedDict` and `NamedTuple` (sobolevn, PR [18281](https://github.com/python/mypy/pull/18281)) + * Fail typecheck if not enough or too many types provided to `TypeAliasType` (bzoracler, PR [18308](https://github.com/python/mypy/pull/18308)) + * Use more precise context for `TypedDict` plugin errors (Brian Schubert, PR [18293](https://github.com/python/mypy/pull/18293)) + * Use more precise context for invalid type argument errors (Brian Schubert, PR [18290](https://github.com/python/mypy/pull/18290)) + * Do not allow `type[]` to contain `Literal` types (sobolevn, PR [18276](https://github.com/python/mypy/pull/18276)) + * Allow bytearray/bytes comparisons with --disable-bytearray-promotion (Jukka Lehtosalo, PR [18255](https://github.com/python/mypy/pull/18255)) + * More LSP compatibility on arg names (Shantanu, PR [18363](https://github.com/python/mypy/pull/18363)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- Advait Dixit +- Ali Hamdan +- Brian Schubert +- bzoracler +- Cameron Matsui +- Christoph Tyralla +- Gareth +- Ivan Levkivskyi +- Jukka Lehtosalo +- Kcornw +- Marc Mueller +- Mikhail f. Shiryaev +- Shantanu +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Valentin Stanciu +- Viktor Szépe +- wyattscarpenter +- 정승원 + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + ## Mypy 1.14 We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). From 8dd616b7d6eed0048ae97b91dd597b173086e995 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 4 Feb 2025 16:27:24 +0000 Subject: [PATCH 1109/1617] Various small updates to 1.15 changelog (#18599) Co-authored-by: Wesley Collin Wright --- CHANGELOG.md | 71 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc6cf576709..8feed91b6e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ ## Mypy 1.15 (Unreleased) We’ve just uploaded mypy 1.15 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). -Mypy is a static type checker for Python. This release includes new features and bug fixes. -You can install it as follows: +Mypy is a static type checker for Python. This release includes new features, performance +improvements and bug fixes. You can install it as follows: python3 -m pip install -U mypy @@ -16,20 +16,20 @@ You can read the full documentation for this release on [Read the Docs](http://m ### Performance Improvements -Mypy may be 5-30% faster. This improvement comes largely from tuning the performance of the -garbage collector. Additionally, the release includes several micro-optimizations that may be -impactful for very large projects. +Mypy is up to 40% faster in some use cases. This improvement comes largely from tuning the performance +of the garbage collector. Additionally, the release includes several micro-optimizations that may +be impactful for large projects. Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306), PR [18302](https://github.com/python/mypy/pull/18302, PR [18298](https://github.com/python/mypy/pull/18298, PR [18299](https://github.com/python/mypy/pull/18299). -### Mypyc Accelerated Mypy Wheels for `aarch64` +### Mypyc Accelerated Mypy Wheels for ARM Linux -Mypy can compile itself to C extension modules using mypyc. This makes mypy 3-5x faster -than if mypy is interpreted with pure Python. We now build and upload mypyc accelerated -mypy wheels for `manylinux_aarch64` to PyPI, making it easy for users on such platforms -to realise this speedup. +For best performance, mypy can be compiled to C extension modules using mypyc. This makes +mypy 3-5x faster than when interpreted with pure Python. We now build and upload mypyc +accelerated mypy wheels for `manylinux_aarch64` to PyPI, making it easy for Linux users on +ARM platforms to realise this speedup -- just `pip install` the latest mypy. Contributed by Christian Bundy and Marc Mueller (PR [mypy_mypyc-wheels#76](https://github.com/mypyc/mypy_mypyc-wheels/pull/76), @@ -37,24 +37,25 @@ PR [mypy_mypyc-wheels#89](https://github.com/mypyc/mypy_mypyc-wheels/pull/89)). ### `--strict-bytes` -By default, mypy treats an annotation of ``bytes`` as permitting ``bytearray`` and ``memoryview``. -[PEP 688](https://peps.python.org/pep-0688) specified the removal of this special case. -Use this flag to disable this behavior. `--strict-bytes` will be enabled by default in **mypy 2.0**. +By default, mypy treats `bytearray` and `memoryview` values as assignable to the `bytes` +type, for historical reasons. Use the `--strict-bytes` flag to disable this +behavior. [PEP 688](https://peps.python.org/pep-0688) specified the removal of this +special case. The flag will be enabled by default in **mypy 2.0**. Contributed by Ali Hamdan (PR [18263](https://github.com/python/mypy/pull/18263)) and Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). ### Improvements to Reachability Analysis and Partial Type Handling in Loops -This change results in mypy better modelling control flow within loops and hence detecting several -issues it previously did not detect. In some cases, this change may require use of an additional -explicit annotation of a variable. +This change results in mypy better modelling control flow within loops and hence detecting +several previously ignored issues. In some cases, this change may require additional +explicit variable annotations. Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180), PR [18433](https://github.com/python/mypy/pull/18433)). -(Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types` -by default in **mypy 2.0**). +(Speaking of partial types, remember that we plan to enable `--local-partial-types` +by default in **mypy 2.0**.) ### Better Discovery of Configuration Files @@ -68,8 +69,8 @@ Contributed by Mikhail Shiryaev and Shantanu Jain ### Better Line Numbers for Decorators and Slice Expressions -Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, this -may necessitate changing the location of a `# type: ignore` comment. +Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, +you may have to change the location of a `# type: ignore` comment. Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392), PR [18397](https://github.com/python/mypy/pull/18397)). @@ -89,13 +90,11 @@ Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/1749 * Report error for nested class instead of crashing (Valentin Stanciu, PR [18460](https://github.com/python/mypy/pull/18460)) * Fix `InitVar` for dataclasses (Advait Dixit, PR [18319](https://github.com/python/mypy/pull/18319)) * Remove unnecessary mypyc files from wheels (Marc Mueller, PR [18416](https://github.com/python/mypy/pull/18416)) - * Get capsule pointer from module instead of `PyCapsule_Import` (Advait Dixit, PR [18286](https://github.com/python/mypy/pull/18286)) - * Add lowered primitive for unsafe list get item op (Jukka Lehtosalo, PR [18136](https://github.com/python/mypy/pull/18136)) + * Fix issues with relative imports (Advait Dixit, PR [18286](https://github.com/python/mypy/pull/18286)) + * Add faster primitive for some list get item operations (Jukka Lehtosalo, PR [18136](https://github.com/python/mypy/pull/18136)) * Fix iteration over `NamedTuple` objects (Advait Dixit, PR [18254](https://github.com/python/mypy/pull/18254)) * Mark mypyc package with `py.typed` (bzoracler, PR [18253](https://github.com/python/mypy/pull/18253)) - * Update docstrings of IR builder classes (Jukka Lehtosalo, PR [18246](https://github.com/python/mypy/pull/18246)) * Fix list index while checking for `Enum` class (Advait Dixit, PR [18426](https://github.com/python/mypy/pull/18426)) - * Update `pythoncapi_compat.h` (Marc Mueller, PR [18340](https://github.com/python/mypy/pull/18340)) ### Stubgen Improvements @@ -116,13 +115,14 @@ Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/1749 * Fix crash with `--cache-fine-grained --cache-dir=/dev/null` (Shantanu, PR [18457](https://github.com/python/mypy/pull/18457)) * Prevent crashing when `match` arms use name of existing callable (Stanislav Terliakov, PR [18449](https://github.com/python/mypy/pull/18449)) * Gracefully handle encoding errors when writing to stdout (Brian Schubert, PR [18292](https://github.com/python/mypy/pull/18292)) + * Prevent crash on generic NamedTuple with unresolved typevar bound (Stanislav Terliakov, PR [18585](https://github.com/python/mypy/pull/18585)) ### Documentation Updates - * Add `sphinx_inline_tabs` to docs (Marc Mueller, PR [18262](https://github.com/python/mypy/pull/18262)) + * Add inline tabs to documentation (Marc Mueller, PR [18262](https://github.com/python/mypy/pull/18262)) * Document any `TYPE_CHECKING` name works (Shantanu, PR [18443](https://github.com/python/mypy/pull/18443)) - * Update docs not to mention 3.8 where possible (sobolevn, PR [18455](https://github.com/python/mypy/pull/18455)) - * Mention `ignore_errors` in exclude docs (Shantanu, PR [18412](https://github.com/python/mypy/pull/18412)) + * Update documentation to not mention 3.8 where possible (sobolevn, PR [18455](https://github.com/python/mypy/pull/18455)) + * Mention `ignore_errors` in exclude documentation (Shantanu, PR [18412](https://github.com/python/mypy/pull/18412)) * Add `Self` misuse to common issues (Shantanu, PR [18261](https://github.com/python/mypy/pull/18261)) ### Other Notable Fixes and Improvements @@ -132,28 +132,27 @@ Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/1749 * Bind `self` to the class being defined when checking multiple inheritance (Stanislav Terliakov, PR [18465](https://github.com/python/mypy/pull/18465)) * Fix attribute type resolution with multiple inheritance (Stanislav Terliakov, PR [18415](https://github.com/python/mypy/pull/18415)) * Improve security of our GitHub Actions (sobolevn, PR [18413](https://github.com/python/mypy/pull/18413)) - * Unwrap `type[Union[...]]` when solving typevar constraints (Stanislav Terliakov, PR [18266](https://github.com/python/mypy/pull/18266)) + * Unwrap `type[Union[...]]` when solving type variable constraints (Stanislav Terliakov, PR [18266](https://github.com/python/mypy/pull/18266)) * Allow `Any` to match sequence patterns in match/case (Stanislav Terliakov, PR [18448](https://github.com/python/mypy/pull/18448)) * Fix parent generics mapping when overriding generic attribute with property (Stanislav Terliakov, PR [18441](https://github.com/python/mypy/pull/18441)) - * Dedicated error code for explicit `Any` (Shantanu, PR [18398](https://github.com/python/mypy/pull/18398)) + * Add dedicated error code for explicit `Any` (Shantanu, PR [18398](https://github.com/python/mypy/pull/18398)) * Reject invalid `ParamSpec` locations (Stanislav Terliakov, PR [18278](https://github.com/python/mypy/pull/18278)) - * Remove stubs no longer in typeshed (Shantanu, PR [18373](https://github.com/python/mypy/pull/18373)) + * Stop suggesting stubs that have been removed from typeshed (Shantanu, PR [18373](https://github.com/python/mypy/pull/18373)) * Allow inverting `--local-partial-types` (Shantanu, PR [18377](https://github.com/python/mypy/pull/18377)) * Allow to use `Final` and `ClassVar` after Python 3.13 (정승원, PR [18358](https://github.com/python/mypy/pull/18358)) - * Update to include latest stubs in typeshed (Shantanu, PR [18366](https://github.com/python/mypy/pull/18366)) + * Update suggestions to include latest stubs in typeshed (Shantanu, PR [18366](https://github.com/python/mypy/pull/18366)) * Fix `--install-types` masking failure details (wyattscarpenter, PR [17485](https://github.com/python/mypy/pull/17485)) * Reject promotions when checking against protocols (Christoph Tyralla, PR [18360](https://github.com/python/mypy/pull/18360)) - * Don't erase type object args in diagnostics (Shantanu, PR [18352](https://github.com/python/mypy/pull/18352)) + * Don't erase type object arguments in diagnostics (Shantanu, PR [18352](https://github.com/python/mypy/pull/18352)) * Clarify status in `dmypy status` output (Kcornw, PR [18331](https://github.com/python/mypy/pull/18331)) - * Disallow no-args generic aliases when using PEP 613 explicit aliases (Brian Schubert, PR [18173](https://github.com/python/mypy/pull/18173)) + * Disallow no-argument generic aliases when using PEP 613 explicit aliases (Brian Schubert, PR [18173](https://github.com/python/mypy/pull/18173)) * Suppress errors for unreachable branches in conditional expressions (Brian Schubert, PR [18295](https://github.com/python/mypy/pull/18295)) * Do not allow `ClassVar` and `Final` in `TypedDict` and `NamedTuple` (sobolevn, PR [18281](https://github.com/python/mypy/pull/18281)) - * Fail typecheck if not enough or too many types provided to `TypeAliasType` (bzoracler, PR [18308](https://github.com/python/mypy/pull/18308)) + * Report error if not enough or too many types provided to `TypeAliasType` (bzoracler, PR [18308](https://github.com/python/mypy/pull/18308)) * Use more precise context for `TypedDict` plugin errors (Brian Schubert, PR [18293](https://github.com/python/mypy/pull/18293)) * Use more precise context for invalid type argument errors (Brian Schubert, PR [18290](https://github.com/python/mypy/pull/18290)) * Do not allow `type[]` to contain `Literal` types (sobolevn, PR [18276](https://github.com/python/mypy/pull/18276)) - * Allow bytearray/bytes comparisons with --disable-bytearray-promotion (Jukka Lehtosalo, PR [18255](https://github.com/python/mypy/pull/18255)) - * More LSP compatibility on arg names (Shantanu, PR [18363](https://github.com/python/mypy/pull/18363)) + * Allow bytearray/bytes comparisons with `--strict-bytes` (Jukka Lehtosalo, PR [18255](https://github.com/python/mypy/pull/18255)) ### Acknowledgements From b50f3a1a44038b5f6304f77263f6e08c157f9aa8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 5 Feb 2025 00:42:50 +0000 Subject: [PATCH 1110/1617] Use union types instead of join in binder (#18538) This would be more consistent with what we already do for ternary expressions. Note the change in match test results from match logic not handling well the situation when initial type is a union. A possible workaround would be to force "collapsing" union of tuples back into a tuple with union, but it is not easy and was planning to do some cleanup in the match handling as well (in particular it uses joins instead of unions in a way that will be inconsistent with new binder behavior). I want to put the switch from join to union for match statement in a separate PR. Note I also simplify a bunch of special-casing around `Any` in the binder that existed mostly because `join(Any, X) == Any`. Fixes https://github.com/python/mypy/issues/3724 --- mypy/binder.py | 108 +++++++++--------- mypy/checker.py | 7 +- mypy/fastparse.py | 4 +- mypy/join.py | 49 -------- mypy/test/testtypes.py | 10 +- mypyc/test-data/irbuild-any.test | 9 -- test-data/unit/check-classes.test | 2 +- test-data/unit/check-dynamic-typing.test | 8 +- test-data/unit/check-isinstance.test | 19 ++- test-data/unit/check-narrowing.test | 18 +++ test-data/unit/check-optional.test | 18 ++- .../unit/check-parameter-specification.test | 5 +- test-data/unit/check-python310.test | 8 +- test-data/unit/check-redefine.test | 5 +- test-data/unit/check-typeguard.test | 17 +++ test-data/unit/check-unions.test | 1 - test-data/unit/typexport-basic.test | 2 +- 17 files changed, 138 insertions(+), 152 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 3d833153d628..4a9b5208336f 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -7,10 +7,10 @@ from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values -from mypy.join import join_simple from mypy.literals import Key, literal, literal_hash, subkeys from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var from mypy.subtypes import is_same_type, is_subtype +from mypy.typeops import make_simplified_union from mypy.types import ( AnyType, Instance, @@ -21,6 +21,7 @@ Type, TypeOfAny, TypeType, + TypeVarType, UnionType, UnpackType, find_unpack_in_list, @@ -237,9 +238,21 @@ def update_from_options(self, frames: list[Frame]) -> bool: ): type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type) else: - for other in resulting_values[1:]: - assert other is not None - type = join_simple(self.declarations[key], type, other.type) + possible_types = [] + for t in resulting_values: + assert t is not None + possible_types.append(t.type) + if len(possible_types) == 1: + # This is to avoid calling get_proper_type() unless needed, as this may + # interfere with our (hacky) TypeGuard support. + type = possible_types[0] + else: + type = make_simplified_union(possible_types) + # Legacy guard for corner case when the original type is TypeVarType. + if isinstance(declaration_type, TypeVarType) and not is_subtype( + type, declaration_type + ): + type = declaration_type # Try simplifying resulting type for unions involving variadic tuples. # Technically, everything is still valid without this step, but if we do # not do this, this may create long unions after exiting an if check like: @@ -258,7 +271,7 @@ def update_from_options(self, frames: list[Frame]) -> bool: ) if simplified == self.declarations[key]: type = simplified - if current_value is None or not is_same_type(type, current_value[0]): + if current_value is None or not is_same_type(type, current_value.type): self._put(key, type, from_assignment=True) changed = True @@ -300,9 +313,7 @@ def accumulate_type_assignments(self) -> Iterator[Assigns]: yield self.type_assignments self.type_assignments = old_assignments - def assign_type( - self, expr: Expression, type: Type, declared_type: Type | None, restrict_any: bool = False - ) -> None: + def assign_type(self, expr: Expression, type: Type, declared_type: Type | None) -> None: # We should erase last known value in binder, because if we are using it, # it means that the target is not final, and therefore can't hold a literal. type = remove_instance_last_known_values(type) @@ -333,41 +344,39 @@ def assign_type( p_declared = get_proper_type(declared_type) p_type = get_proper_type(type) - enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type)) - if isinstance(enclosing_type, AnyType) and not restrict_any: - # If x is Any and y is int, after x = y we do not infer that x is int. - # This could be changed. - # Instead, since we narrowed type from Any in a recent frame (probably an - # isinstance check), but now it is reassigned, we broaden back - # to Any (which is the most recent enclosing type) - self.put(expr, enclosing_type) - # As a special case, when assigning Any to a variable with a - # declared Optional type that has been narrowed to None, - # replace all the Nones in the declared Union type with Any. - # This overrides the normal behavior of ignoring Any assignments to variables - # in order to prevent false positives. - # (See discussion in #3526) - elif ( - isinstance(p_type, AnyType) - and isinstance(p_declared, UnionType) - and any(isinstance(get_proper_type(item), NoneType) for item in p_declared.items) - and isinstance( - get_proper_type(self.most_recent_enclosing_type(expr, NoneType())), NoneType - ) - ): - # Replace any Nones in the union type with Any - new_items = [ - type if isinstance(get_proper_type(item), NoneType) else item - for item in p_declared.items - ] - self.put(expr, UnionType(new_items)) - elif isinstance(p_type, AnyType) and not ( - isinstance(p_declared, UnionType) - and any(isinstance(get_proper_type(item), AnyType) for item in p_declared.items) - ): - # Assigning an Any value doesn't affect the type to avoid false negatives, unless - # there is an Any item in a declared union type. - self.put(expr, declared_type) + if isinstance(p_type, AnyType): + # Any type requires some special casing, for both historical reasons, + # and to optimise user experience without sacrificing correctness too much. + if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred: + # First case: a local/global variable without explicit annotation, + # in this case we just assign Any (essentially following the SSA logic). + self.put(expr, type) + elif isinstance(p_declared, UnionType) and any( + isinstance(get_proper_type(item), NoneType) for item in p_declared.items + ): + # Second case: explicit optional type, in this case we optimize for a common + # pattern when an untyped value used as a fallback replacing None. + new_items = [ + type if isinstance(get_proper_type(item), NoneType) else item + for item in p_declared.items + ] + self.put(expr, UnionType(new_items)) + elif isinstance(p_declared, UnionType) and any( + isinstance(get_proper_type(item), AnyType) for item in p_declared.items + ): + # Third case: a union already containing Any (most likely from an un-imported + # name), in this case we allow assigning Any as well. + self.put(expr, type) + else: + # In all other cases we don't narrow to Any to minimize false negatives. + self.put(expr, declared_type) + elif isinstance(p_declared, AnyType): + # Mirroring the first case above, we don't narrow to a precise type if the variable + # has an explicit `Any` type annotation. + if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred: + self.put(expr, type) + else: + self.put(expr, declared_type) else: self.put(expr, type) @@ -389,19 +398,6 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None: for dep in self.dependencies.get(key, set()): self._cleanse_key(dep) - def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type | None: - type = get_proper_type(type) - if isinstance(type, AnyType): - return get_declaration(expr) - key = literal_hash(expr) - assert key is not None - enclosers = [get_declaration(expr)] + [ - f.types[key].type - for f in self.frames - if key in f.types and is_subtype(type, f.types[key][0]) - ] - return enclosers[-1] - def allow_jump(self, index: int) -> None: # self.frames and self.options_on_return have different lengths # so make sure the index is positive diff --git a/mypy/checker.py b/mypy/checker.py index 35c883276029..999d75678aa4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3284,7 +3284,7 @@ def check_assignment( if rvalue_type and infer_lvalue_type and not isinstance(lvalue_type, PartialType): # Don't use type binder for definitions of special forms, like named tuples. if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form): - self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False) + self.binder.assign_type(lvalue, rvalue_type, lvalue_type) if ( isinstance(lvalue, NameExpr) and isinstance(lvalue.node, Var) @@ -4023,7 +4023,7 @@ def check_multi_assignment_from_union( if isinstance(expr, StarExpr): expr = expr.expr - # TODO: See todo in binder.py, ConditionalTypeBinder.assign_type + # TODO: See comment in binder.py, ConditionalTypeBinder.assign_type # It's unclear why the 'declared_type' param is sometimes 'None' clean_items: list[tuple[Type, Type]] = [] for type, declared_type in items: @@ -4035,7 +4035,6 @@ def check_multi_assignment_from_union( expr, make_simplified_union(list(types)), make_simplified_union(list(declared_types)), - False, ) for union, lv in zip(union_types, self.flatten_lvalues(lvalues)): # Properly store the inferred types. @@ -5233,7 +5232,7 @@ def visit_del_stmt(self, s: DelStmt) -> None: for elt in flatten(s.expr): if isinstance(elt, NameExpr): self.binder.assign_type( - elt, DeletedType(source=elt.name), get_declaration(elt), False + elt, DeletedType(source=elt.name), get_declaration(elt) ) def visit_decorator(self, e: Decorator) -> None: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 14b30e5d7826..a58ebbcaded1 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1106,7 +1106,9 @@ def make_argument( if argument_elide_name(arg.arg): pos_only = True - argument = Argument(Var(arg.arg, arg_type), arg_type, self.visit(default), kind, pos_only) + var = Var(arg.arg, arg_type) + var.is_inferred = False + argument = Argument(var, arg_type, self.visit(default), kind, pos_only) argument.set_line( arg.lineno, arg.col_offset, diff --git a/mypy/join.py b/mypy/join.py index 9fa6e27207f4..a5c30b4b835d 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -183,55 +183,6 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType: return best -def join_simple(declaration: Type | None, s: Type, t: Type) -> ProperType: - """Return a simple least upper bound given the declared type. - - This function should be only used by binder, and should not recurse. - For all other uses, use `join_types()`. - """ - declaration = get_proper_type(declaration) - s = get_proper_type(s) - t = get_proper_type(t) - - if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): - # if types are restricted in different ways, use the more general versions - s = mypy.typeops.true_or_false(s) - t = mypy.typeops.true_or_false(t) - - if isinstance(s, AnyType): - return s - - if isinstance(s, ErasedType): - return t - - if is_proper_subtype(s, t, ignore_promotions=True): - return t - - if is_proper_subtype(t, s, ignore_promotions=True): - return s - - if isinstance(declaration, UnionType): - return mypy.typeops.make_simplified_union([s, t]) - - if isinstance(s, NoneType) and not isinstance(t, NoneType): - s, t = t, s - - if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): - s, t = t, s - - # Meets/joins require callable type normalization. - s, t = normalize_callables(s, t) - - if isinstance(s, UnionType) and not isinstance(t, UnionType): - s, t = t, s - - value = t.accept(TypeJoinVisitor(s)) - if declaration is None or is_subtype(value, declaration): - return value - - return declaration - - def trivial_join(s: Type, t: Type) -> Type: """Return one of types (expanded) if it is a supertype of other, otherwise top type.""" if is_subtype(s, t): diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 35102be80f5d..174441237ab4 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -7,7 +7,7 @@ from mypy.erasetype import erase_type, remove_instance_last_known_values from mypy.indirection import TypeIndirectionVisitor -from mypy.join import join_simple, join_types +from mypy.join import join_types from mypy.meet import meet_types, narrow_declared_type from mypy.nodes import ( ARG_NAMED, @@ -817,12 +817,12 @@ def test_any_type(self) -> None: self.assert_join(t, self.fx.anyt, self.fx.anyt) def test_mixed_truth_restricted_type_simple(self) -> None: - # join_simple against differently restricted truthiness types drops restrictions. + # make_simplified_union against differently restricted truthiness types drops restrictions. true_a = true_only(self.fx.a) false_o = false_only(self.fx.o) - j = join_simple(self.fx.o, true_a, false_o) - assert j.can_be_true - assert j.can_be_false + u = make_simplified_union([true_a, false_o]) + assert u.can_be_true + assert u.can_be_false def test_mixed_truth_restricted_type(self) -> None: # join_types against differently restricted truthiness types drops restrictions. diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 3bfb1587fb3b..55783a9a9498 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -37,7 +37,6 @@ def f(a: Any, n: int, c: C) -> None: c.n = a a = n n = a - a.a = n [out] def f(a, n, c): a :: object @@ -49,10 +48,6 @@ def f(a, n, c): r3 :: bool r4 :: object r5 :: int - r6 :: str - r7 :: object - r8 :: i32 - r9 :: bit L0: r0 = box(int, n) c.a = r0; r1 = is_error @@ -62,10 +57,6 @@ L0: a = r4 r5 = unbox(int, a) n = r5 - r6 = 'a' - r7 = box(int, n) - r8 = PyObject_SetAttr(a, r6, r7) - r9 = r8 >= 0 :: signed return 1 [case testCoerceAnyInOps] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index cf401bc2aece..8a5af4ba1e0f 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3709,7 +3709,7 @@ def new(uc: Type[U]) -> U: if 1: u = uc(0) u.foo() - u = uc('') # Error + uc('') # Error u.foo(0) # Error return uc() u = new(User) diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index 21fd52169ff5..ffab5afeda3e 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -252,7 +252,7 @@ if int(): if int(): a = d.foo(a, a) d.x = a -d.x.y.z # E: "A" has no attribute "y" +d.x.y.z class A: pass [out] @@ -320,8 +320,10 @@ d = None # All ok d = t d = g d = A -t = d -f = d + +d1: Any +t = d1 +f = d1 [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 04fbced5347c..759d38445c55 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -115,8 +115,8 @@ if int(): x = B() x.z x = foo() - x.z # E: "A" has no attribute "z" - x.y + reveal_type(x) # N: Revealed type is "Any" +reveal_type(x) # N: Revealed type is "__main__.A" [case testSingleMultiAssignment] x = 'a' @@ -1915,17 +1915,28 @@ if isinstance(x, str, 1): # E: Too many arguments for "isinstance" reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/isinstancelist.pyi] -[case testIsinstanceNarrowAny] +[case testIsinstanceNarrowAnyExplicit] from typing import Any def narrow_any_to_str_then_reassign_to_int() -> None: - v = 1 # type: Any + v: Any = 1 if isinstance(v, str): reveal_type(v) # N: Revealed type is "builtins.str" v = 2 reveal_type(v) # N: Revealed type is "Any" +[builtins fixtures/isinstance.pyi] +[case testIsinstanceNarrowAnyImplicit] +def foo(): ... + +def narrow_any_to_str_then_reassign_to_int() -> None: + v = foo() + + if isinstance(v, str): + reveal_type(v) # N: Revealed type is "builtins.str" + v = 2 + reveal_type(v) # N: Revealed type is "builtins.int" [builtins fixtures/isinstance.pyi] [case testNarrowTypeAfterInList] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index ec647366e743..feb1c951ad72 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -2416,3 +2416,21 @@ while x is not None and b(): x = f() [builtins fixtures/primitives.pyi] + +[case testNarrowingTypeVarMultiple] +from typing import TypeVar + +class A: ... +class B: ... + +T = TypeVar("T") +def foo(x: T) -> T: + if isinstance(x, A): + pass + elif isinstance(x, B): + pass + else: + raise + reveal_type(x) # N: Revealed type is "T`-1" + return x +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 5d866345c66f..5ed4c15f470e 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -728,7 +728,6 @@ def g(x: Optional[int]) -> int: reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" return x - [builtins fixtures/bool.pyi] [case testOptionalAssignAny2] @@ -741,12 +740,11 @@ def g(x: Optional[int]) -> int: reveal_type(x) # N: Revealed type is "None" x = 1 reveal_type(x) # N: Revealed type is "builtins.int" - # Since we've assigned to x, the special case None behavior shouldn't happen + # Same as above, even after we've assigned to x x = f() - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" - return x # E: Incompatible return value type (got "Optional[int]", expected "int") - + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + return x [builtins fixtures/bool.pyi] [case testOptionalAssignAny3] @@ -758,11 +756,9 @@ def g(x: Optional[int]) -> int: if x is not None: return x reveal_type(x) # N: Revealed type is "None" - if 1: - x = f() - reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" - return x - + x = f() + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + return x [builtins fixtures/bool.pyi] [case testStrictOptionalCovarianceCrossModule] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f938226f8472..5530bc0ecbf9 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -343,8 +343,9 @@ class C(Generic[P]): a = kwargs args = kwargs # E: Incompatible types in assignment (expression has type "P.kwargs", variable has type "P.args") kwargs = args # E: Incompatible types in assignment (expression has type "P.args", variable has type "P.kwargs") - args = a - kwargs = a + a1: Any + args = a1 + kwargs = a1 [builtins fixtures/dict.pyi] [case testParamSpecSubtypeChecking2] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index ea6cc7ffe56a..e10d0c76c717 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1487,11 +1487,13 @@ match m5: case _: reveal_type(m5) # N: Revealed type is "Tuple[Literal[2], Union[Literal['a'], Literal['b']]]" -match m5: +m6: Tuple[Literal[1, 2], Literal["a", "b"]] + +match m6: case (1, "a"): - reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Literal['a']]" + reveal_type(m6) # N: Revealed type is "Tuple[Literal[1], Literal['a']]" case _: - reveal_type(m5) # N: Revealed type is "Tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" + reveal_type(m6) # N: Revealed type is "Tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 1aacffe1fc93..aaec94b546f5 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -254,7 +254,8 @@ def f() -> None: _, _ = 1, '' if 1: _, _ = '', 1 - reveal_type(_) # N: Revealed type is "Any" + # This is unintentional but probably fine. No one is going to read _ value. + reveal_type(_) # N: Revealed type is "builtins.int" [case testRedefineWithBreakAndContinue] # flags: --allow-redefinition @@ -381,7 +382,7 @@ def f() -> None: x = 1 if int(): x = '' - reveal_type(x) # N: Revealed type is "builtins.object" + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" x = '' reveal_type(x) # N: Revealed type is "builtins.str" if int(): diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index c69e16c5cc9e..71c4473fbfaa 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -786,3 +786,20 @@ def func2(val: Union[int, str]): else: reveal_type(val) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] + +[case testTypeGuardRestrictAwaySingleInvariant] +from typing import List +from typing_extensions import TypeGuard + +class B: ... +class C(B): ... + +def is_c_list(x: list[B]) -> TypeGuard[list[C]]: ... + +def test() -> None: + x: List[B] + if not is_c_list(x): + reveal_type(x) # N: Revealed type is "builtins.list[__main__.B]" + return + reveal_type(x) # N: Revealed type is "builtins.list[__main__.C]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index cea1305ddc7d..8e92b6a91e8a 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -528,7 +528,6 @@ x: Union[int, str] a: Any if bool(): x = a - # TODO: Maybe we should infer Any as the type instead. reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/bool.pyi] diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index d78cf0f179f2..512b572801d2 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -255,7 +255,7 @@ NameExpr(6) : A NameExpr(6) : A MemberExpr(7) : A MemberExpr(7) : A -MemberExpr(7) : A +MemberExpr(7) : Any NameExpr(7) : A NameExpr(7) : A From 5aa34570ec3e760db17453cc48179f68f2fddd84 Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Tue, 4 Feb 2025 19:43:42 -0600 Subject: [PATCH 1111/1617] remove "unreleased" from 1.15 changelog entry (#18602) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8feed91b6e4e..a150262be896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ... -## Mypy 1.15 (Unreleased) +## Mypy 1.15 We’ve just uploaded mypy 1.15 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance From 88d6890bcd75a742df06608b0aeb344bb4414128 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 5 Feb 2025 11:14:13 +0300 Subject: [PATCH 1112/1617] Fix markup in CHANGELOG for mypy@1.15 (#18607) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: Снимок экрана 2025-02-05 в 11 10 16 After: Снимок экрана 2025-02-05 в 11 11 06 --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a150262be896..d9c772dc7c04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,11 @@ Mypy is up to 40% faster in some use cases. This improvement comes largely from of the garbage collector. Additionally, the release includes several micro-optimizations that may be impactful for large projects. -Contributed by Jukka Lehtosalo (PR [18306](https://github.com/python/mypy/pull/18306), -PR [18302](https://github.com/python/mypy/pull/18302, PR [18298](https://github.com/python/mypy/pull/18298, -PR [18299](https://github.com/python/mypy/pull/18299). +Contributed by Jukka Lehtosalo +- PR [18306](https://github.com/python/mypy/pull/18306) +- PR [18302](https://github.com/python/mypy/pull/18302) +- PR [18298](https://github.com/python/mypy/pull/18298) +- PR [18299](https://github.com/python/mypy/pull/18299) ### Mypyc Accelerated Mypy Wheels for ARM Linux From 6f32ef955c485fac41a9372e186db4d5eb8ca5c4 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Wed, 5 Feb 2025 10:59:04 +0100 Subject: [PATCH 1113/1617] Add missing TypedDict special case to checkmember.py (#18604) Fixes #18600 --- mypy/checkexpr.py | 22 ++++++------------ mypy/checkmember.py | 36 ++++++++++++++++++++++++++++- test-data/unit/check-typeddict.test | 20 ++++++++++++++++ 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4b7e39d2042a..286ef0dab6ae 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -15,7 +15,12 @@ import mypy.errorcodes as codes from mypy import applytype, erasetype, join, message_registry, nodes, operators, types from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals -from mypy.checkmember import analyze_member_access, freeze_all_type_vars, type_object_type +from mypy.checkmember import ( + analyze_member_access, + freeze_all_type_vars, + type_object_type, + typeddict_callable, +) from mypy.checkstrformat import StringFormatterChecker from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars from mypy.errors import ErrorWatcher, report_internal_error @@ -955,20 +960,7 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType: Note it is not safe to move this to type_object_type() since it will crash on plugin-generated TypedDicts, that may not have the special_alias. """ - assert info.special_alias is not None - target = info.special_alias.target - assert isinstance(target, ProperType) and isinstance(target, TypedDictType) - expected_types = list(target.items.values()) - kinds = [ArgKind.ARG_NAMED] * len(expected_types) - names = list(target.items.keys()) - return CallableType( - expected_types, - kinds, - names, - target, - self.named_type("builtins.type"), - variables=info.defn.type_vars, - ) + return typeddict_callable(info, self.named_type) def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType: return CallableType( diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f6b5e6be2c53..515f0c12c5b9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -20,6 +20,7 @@ ARG_STAR2, EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, + ArgKind, Context, Decorator, FuncBase, @@ -1148,8 +1149,16 @@ def analyze_class_attribute_access( ) return AnyType(TypeOfAny.from_error) + # TODO: some logic below duplicates analyze_ref_expr in checkexpr.py if isinstance(node.node, TypeInfo): - return type_object_type(node.node, mx.named_type) + if node.node.typeddict_type: + # We special-case TypedDict, because they don't define any constructor. + return typeddict_callable(node.node, mx.named_type) + elif node.node.fullname == "types.NoneType": + # We special case NoneType, because its stub definition is not related to None. + return TypeType(NoneType()) + else: + return type_object_type(node.node, mx.named_type) if isinstance(node.node, MypyFile): # Reference to a module object. @@ -1330,6 +1339,31 @@ class B(A[str]): pass return t +def typeddict_callable(info: TypeInfo, named_type: Callable[[str], Instance]) -> CallableType: + """Construct a reasonable type for a TypedDict type in runtime context. + + If it appears as a callee, it will be special-cased anyway, e.g. it is + also allowed to accept a single positional argument if it is a dict literal. + + Note it is not safe to move this to type_object_type() since it will crash + on plugin-generated TypedDicts, that may not have the special_alias. + """ + assert info.special_alias is not None + target = info.special_alias.target + assert isinstance(target, ProperType) and isinstance(target, TypedDictType) + expected_types = list(target.items.values()) + kinds = [ArgKind.ARG_NAMED] * len(expected_types) + names = list(target.items.keys()) + return CallableType( + expected_types, + kinds, + names, + target, + named_type("builtins.type"), + variables=info.defn.type_vars, + ) + + def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: """Return the type of a type object. diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 22e9963944a2..feea5e2dff0f 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -4118,3 +4118,23 @@ Func = TypedDict('Func', { }) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + +[case testTypedDictNestedInClassAndInherited] +from typing_extensions import TypedDict + +class Base: + class Params(TypedDict): + name: str + +class Derived(Base): + pass + +class DerivedOverride(Base): + class Params(Base.Params): + pass + +Base.Params(name="Robert") +Derived.Params(name="Robert") +DerivedOverride.Params(name="Robert") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 75a4bc499533374c74b46559ed288e808c786b79 Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Wed, 5 Feb 2025 04:14:48 -0600 Subject: [PATCH 1114/1617] [misc] automatically strip periods when generating changelog entries (#18598) Manually removing the periods caused a small amount of busy work in the 1.15 release. It's easy enough to tweak the script, so I went ahead and added a `removesuffix` call. --- misc/generate_changelog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/generate_changelog.py b/misc/generate_changelog.py index ebab6c569152..c53a06e39133 100644 --- a/misc/generate_changelog.py +++ b/misc/generate_changelog.py @@ -145,7 +145,8 @@ def format_changelog_entry(c: CommitInfo) -> str: s += f" (#{c.pr_number})" s += f" ({c.author})" """ - s = f" * {c.title} ({c.author}" + title = c.title.removesuffix(".") + s = f" * {title} ({c.author}" if c.pr_number: s += f", PR [{c.pr_number}](https://github.com/python/mypy/pull/{c.pr_number})" s += ")" From ac921ae5f5f60092df4fc719dd038e74cba82a3f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 5 Feb 2025 16:56:36 +0300 Subject: [PATCH 1115/1617] Fix "not callable" issue for `@dataclass(frozen=True)` with `Final` attr (#18572) Closes #18567 We should allow inferenced `a: Final = 1` --- mypy/plugins/dataclasses.py | 2 ++ test-data/unit/check-dataclasses.test | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 6e0e22272356..acb785aad70a 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -768,6 +768,8 @@ def _freeze(self, attributes: list[DataclassAttribute]) -> None: if sym_node is not None: var = sym_node.node if isinstance(var, Var): + if var.is_final: + continue # do not turn `Final` attrs to `@property` var.is_property = True else: var = attr.to_var(info) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 2e7259e4de0a..26c81812ab62 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2553,3 +2553,26 @@ class X(metaclass=DCMeta): class Y(X): a: int # E: Covariant override of a mutable attribute (base class "X" defined the type as "Optional[int]", expression has type "int") [builtins fixtures/tuple.pyi] + + +[case testFrozenWithFinal] +from dataclasses import dataclass +from typing import Final + +@dataclass(frozen=True) +class My: + a: Final = 1 + b: Final[int] = 2 + +reveal_type(My.a) # N: Revealed type is "Literal[1]?" +reveal_type(My.b) # N: Revealed type is "builtins.int" +My.a = 1 # E: Cannot assign to final attribute "a" +My.b = 2 # E: Cannot assign to final attribute "b" + +m = My() +reveal_type(m.a) # N: Revealed type is "Literal[1]?" +reveal_type(m.b) # N: Revealed type is "builtins.int" + +m.a = 1 # E: Cannot assign to final attribute "a" +m.b = 2 # E: Cannot assign to final attribute "b" +[builtins fixtures/tuple.pyi] From e9a813c32637e0c9eb1c6cff6426dab504d9656d Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Wed, 5 Feb 2025 08:40:29 -0600 Subject: [PATCH 1116/1617] [misc] fix blog post generator quirks (#18601) While preparing the 1.15 release blog posts, I noticed a few quirks with our maarkdown converter: - We treat any `\n` followed by a capital letter as a new paragraph, which can cause too many `

` tags to be inserted at times (a common cause in this blog post was a line break followed by the word "PR"). - Using multiple consecutive backticks for an inline code section is valid markdown (it's commonly used for strings where you need to include single backticks, eg ``` ``a string with a single ` :)`` ```), but our script was confused by this and generated lots of erroneous `` sections where they didn't belong. - Including a `#\d` in the middle of a word caused the script to assume it was a PR that it should link. In this specific case, the changelog contains several occurrences of `mypy_mypyc-wheels#`, which the script was stomping on. This PR contains some minor tweaks for the blog post generation script that attempt to address these quirks. --- misc/gen_blog_post_html.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py index 847e05399654..1c2d87648604 100644 --- a/misc/gen_blog_post_html.py +++ b/misc/gen_blog_post_html.py @@ -95,7 +95,7 @@ def convert(src: str) -> str: h = re.sub(r"`\*\*`", "**", h) # Paragraphs - h = re.sub(r"\n([A-Z])", r"\n

\1", h) + h = re.sub(r"\n\n([A-Z])", r"\n\n

\1", h) # Bullet lists h = format_lists(h) @@ -104,6 +104,7 @@ def convert(src: str) -> str: h = format_code(h) # Code fragments + h = re.sub(r"``([^`]+)``", r"\1", h) h = re.sub(r"`([^`]+)`", r"\1", h) # Remove **** noise @@ -125,7 +126,9 @@ def convert(src: str) -> str: r'fixes issue \1', h, ) - h = re.sub(r"#([0-9]+)", r'PR \1', h) + # Note the leading space to avoid stomping on strings that contain #\d in the middle (such as + # links to PRs in other repos) + h = re.sub(r" #([0-9]+)", r' PR \1', h) h = re.sub(r"\) \(PR", ", PR", h) # Markdown links From fc991a0ac83929d2d250a20b8283ba1229a15e75 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 6 Feb 2025 15:42:07 +0000 Subject: [PATCH 1117/1617] Update comments and docstrings related to binder (#18611) Hopefully this makes this a little less confusing. --- mypy/binder.py | 63 ++++++++++++++++++++++++++++++++++-------------- mypy/literals.py | 27 +++++++++++++++------ mypy/nodes.py | 11 ++++++--- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 4a9b5208336f..384bdca728b2 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -41,12 +41,22 @@ class Frame: """A Frame represents a specific point in the execution of a program. It carries information about the current types of expressions at that point, arising either from assignments to those expressions - or the result of isinstance checks. It also records whether it is - possible to reach that point at all. + or the result of isinstance checks and other type narrowing + operations. It also records whether it is possible to reach that + point at all. + + We add a new frame wherenever there is a new scope or control flow + branching. This information is not copied into a new Frame when it is pushed onto the stack, so a given Frame only has information about types that were assigned in that frame. + + Expressions are stored in dicts using 'literal hashes' as keys (type + "Key"). These are hashable values derived from expression AST nodes + (only those that can be narrowed). literal_hash(expr) is used to + calculate the hashes. Note that this isn't directly related to literal + types -- the concept predates literal types. """ def __init__(self, id: int, conditional_frame: bool = False) -> None: @@ -66,22 +76,21 @@ def __repr__(self) -> str: class ConditionalTypeBinder: """Keep track of conditional types of variables. - NB: Variables are tracked by literal expression, so it is possible - to confuse the binder; for example, - - ``` - class A: - a: Union[int, str] = None - x = A() - lst = [x] - reveal_type(x.a) # Union[int, str] - x.a = 1 - reveal_type(x.a) # int - reveal_type(lst[0].a) # Union[int, str] - lst[0].a = 'a' - reveal_type(x.a) # int - reveal_type(lst[0].a) # str - ``` + NB: Variables are tracked by literal hashes of expressions, so it is + possible to confuse the binder when there is aliasing. Example: + + class A: + a: int | str + + x = A() + lst = [x] + reveal_type(x.a) # int | str + x.a = 1 + reveal_type(x.a) # int + reveal_type(lst[0].a) # int | str + lst[0].a = 'a' + reveal_type(x.a) # int + reveal_type(lst[0].a) # str """ # Stored assignments for situations with tuple/list lvalue and rvalue of union type. @@ -89,6 +98,7 @@ class A: type_assignments: Assigns | None = None def __init__(self) -> None: + # Each frame gets an increasing, distinct id. self.next_id = 1 # The stack of frames currently used. These map @@ -116,6 +126,7 @@ def __init__(self) -> None: # Whether the last pop changed the newly top frame on exit self.last_pop_changed = False + # These are used to track control flow in try statements and loops. self.try_frames: set[int] = set() self.break_frames: list[int] = [] self.continue_frames: list[int] = [] @@ -151,6 +162,10 @@ def _get(self, key: Key, index: int = -1) -> CurrentType | None: return None def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> None: + """Directly set the narrowed type of expression (if it supports it). + + This is used for isinstance() etc. Assignments should go through assign_type(). + """ if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return if not literal(expr): @@ -314,6 +329,13 @@ def accumulate_type_assignments(self) -> Iterator[Assigns]: self.type_assignments = old_assignments def assign_type(self, expr: Expression, type: Type, declared_type: Type | None) -> None: + """Narrow type of expression through an assignment. + + Do nothing if the expression doesn't support narrowing. + + When not narrowing though an assignment (isinstance() etc.), use put() + directly. This omits some special-casing logic for assignments. + """ # We should erase last known value in binder, because if we are using it, # it means that the target is not final, and therefore can't hold a literal. type = remove_instance_last_known_values(type) @@ -488,6 +510,11 @@ def top_frame_context(self) -> Iterator[Frame]: def get_declaration(expr: BindableExpression) -> Type | None: + """Get the declared or inferred type of a RefExpr expression. + + Return None if there is no type or the expression is not a RefExpr. + This can return None if the type hasn't been inferred yet. + """ if isinstance(expr, RefExpr): if isinstance(expr.node, Var): type = expr.node.type diff --git a/mypy/literals.py b/mypy/literals.py index 32b5ad7b9fde..5b0c46f4bee8 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -96,7 +96,27 @@ # of an index expression, or the operands of an operator expression). +Key: _TypeAlias = tuple[Any, ...] + + +def literal_hash(e: Expression) -> Key | None: + """Generate a hashable, (mostly) opaque key for expressions supported by the binder. + + These allow using expressions as dictionary keys based on structural/value + matching (instead of based on expression identity). + + Return None if the expression type is not supported (it cannot be narrowed). + + See the comment above for more information. + + NOTE: This is not directly related to literal types. + """ + return e.accept(_hasher) + + def literal(e: Expression) -> int: + """Return the literal kind for an expression.""" + if isinstance(e, ComparisonExpr): return min(literal(o) for o in e.operands) @@ -129,17 +149,10 @@ def literal(e: Expression) -> int: return LITERAL_NO -Key: _TypeAlias = tuple[Any, ...] - - def subkeys(key: Key) -> Iterable[Key]: return [elt for elt in key if isinstance(elt, tuple)] -def literal_hash(e: Expression) -> Key | None: - return e.accept(_hasher) - - def extract_var_from_literal_hash(key: Key) -> Var | None: """If key refers to a Var node, return it. diff --git a/mypy/nodes.py b/mypy/nodes.py index 9364805d44d4..010e6ce4de6e 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -89,9 +89,14 @@ def set_line( REVEAL_TYPE: Final = 0 REVEAL_LOCALS: Final = 1 -LITERAL_YES: Final = 2 -LITERAL_TYPE: Final = 1 -LITERAL_NO: Final = 0 +# Kinds of 'literal' expressions. +# +# Use the function mypy.literals.literal to calculate these. +# +# TODO: Can we make these less confusing? +LITERAL_YES: Final = 2 # Value of expression known statically +LITERAL_TYPE: Final = 1 # Type of expression can be narrowed (e.g. variable reference) +LITERAL_NO: Final = 0 # None of the above node_kinds: Final = {LDEF: "Ldef", GDEF: "Gdef", MDEF: "Mdef", UNBOUND_IMPORTED: "UnboundImported"} inverse_node_kinds: Final = {_kind: _name for _name, _kind in node_kinds.items()} From 5ee02cd3d9ec2baa57f401b1f4302fa9f05657f3 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Feb 2025 22:51:13 +0300 Subject: [PATCH 1118/1617] Allow trailing commas for `files` setting in `mypy.ini` and `setup.ini` (#18621) Now ```ini files = a.py, b.py ``` and ```ini files = a.py, b.py, ``` will be the same thing. Previously, adding a traling comma would add `''` to `paths`, which resulted in a strange error like: ``` a.py: error: Duplicate module named "a" (also at "a.py") (diff) a.py: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules for more info (diff) a.py: note: Common resolutions include: a) using `--exclude` to avoid checking one of them, b) adding `__init__.py` somewhere, c) using `--explicit-package-bases` or adjusting MYPYPATH (diff) ``` Refs https://github.com/python/mypy/pull/14240 Refs https://github.com/python/cpython/pull/129708 --- mypy/config_parser.py | 2 +- test-data/unit/cmdline.test | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index c68efe9e44ef..94427a347779 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -126,7 +126,7 @@ def split_and_match_files(paths: str) -> list[str]: Returns a list of file paths """ - return split_and_match_files_list(paths.split(",")) + return split_and_match_files_list(split_commas(paths)) def check_follow_imports(choice: str) -> str: diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 2c53266866f4..f298f6dbe2df 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1300,6 +1300,22 @@ foo.py:1: error: "int" not callable [out] foo/m.py:1: error: "int" not callable +[case testCmdlineCfgFilesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +files = + a.py, + b.py, +[file a.py] +x: str = 'x' # ok +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + [case testCmdlineCfgEnableErrorCodeTrailingComma] # cmd: mypy . [file mypy.ini] From c8fad3f6a97eda2f5a0fa3a581db1194976998b8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 7 Feb 2025 00:26:52 +0000 Subject: [PATCH 1119/1617] Add missing test case for polymorphic inference (#18626) --- test-data/unit/check-generics.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 5d6ad8e19631..767b55efcac2 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3035,6 +3035,21 @@ def id(x: V) -> V: reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]" [builtins fixtures/tuple.pyi] +[case testInferenceAgainstGenericSecondary] +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[List[T]], List[int]]) -> Callable[[T], T]: ... + +@dec +def id(x: U) -> U: + ... +reveal_type(id) # N: Revealed type is "def (builtins.int) -> builtins.int" +[builtins fixtures/tuple.pyi] + [case testInferenceAgainstGenericEllipsisSelfSpecialCase] # flags: --new-type-inference from typing import Self, Callable, TypeVar From 75b56040dca6f1d64f5618479f170281a623c003 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 7 Feb 2025 09:57:38 +0000 Subject: [PATCH 1120/1617] Fix crashes on incorectly detected recursive aliases (#18625) Fixes https://github.com/python/mypy/issues/18505 Fixes https://github.com/python/mypy/issues/16757 Fixing the crash is trivial, we simply give an error on something like `type X = X`, instead of crashing. But then I looked at what people actually want to do, they want to create an alias to something currently in scope (not a meaningless no-op alias). Right now we special-case classes/aliases to allow forward references (including recursive type aliases). However, I don't think we have any clear "scoping rules" for forward references. For example: ```python class C: Y = X class X: ... class X: ... ``` where `Y` should point to, `__main__.X` or `__main__.C.X`? Moreover, before this PR forward references can take precedence over real references: ```python class X: ... class C: Y = X # this resolves to __main__.C.X class X: ... ``` After some thinking I found this is not something I can fix in a simple PR. So instead I do just two things here: * Fix the actual crashes (and other potential similar crashes). * Add minimal change to accommodate the typical use case. --- mypy/semanal.py | 29 +++++++++++++++--- test-data/unit/check-newsemanal.test | 16 ++++++++++ test-data/unit/check-python312.test | 41 ++++++++++++++++++++++++++ test-data/unit/check-type-aliases.test | 28 ++++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index d769178dc298..86234d100c27 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4194,7 +4194,16 @@ def disable_invalid_recursive_aliases( ) -> None: """Prohibit and fix recursive type aliases that are invalid/unsupported.""" messages = [] - if is_invalid_recursive_alias({current_node}, current_node.target): + if ( + isinstance(current_node.target, TypeAliasType) + and current_node.target.alias is current_node + ): + # We want to have consistent error messages, but not calling name_not_defined(), + # since it will do a bunch of unrelated things we don't want here. + messages.append( + f'Cannot resolve name "{current_node.name}" (possible cyclic definition)' + ) + elif is_invalid_recursive_alias({current_node}, current_node.target): target = ( "tuple" if isinstance(get_proper_type(current_node.target), TupleType) else "union" ) @@ -6315,12 +6324,24 @@ class C: if self.statement is None: # Assume it's fine -- don't have enough context to check return True - return ( + if ( node is None or self.is_textually_before_statement(node) or not self.is_defined_in_current_module(node.fullname) - or isinstance(node, (TypeInfo, TypeAlias)) - or (isinstance(node, PlaceholderNode) and node.becomes_typeinfo) + ): + return True + if self.is_type_like(node): + # Allow forward references to classes/type aliases (see docstring), but + # a forward reference should never shadow an existing regular reference. + if node.name not in self.globals: + return True + global_node = self.globals[node.name] + return not self.is_type_like(global_node.node) + return False + + def is_type_like(self, node: SymbolNode | None) -> bool: + return isinstance(node, (TypeInfo, TypeAlias)) or ( + isinstance(node, PlaceholderNode) and node.becomes_typeinfo ) def is_textually_before_statement(self, node: SymbolNode) -> bool: diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index d5101e2e25f3..1eafd462aa51 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3140,6 +3140,22 @@ from typing import Final x: Final = 0 x = x # E: Cannot assign to final name "x" +[case testNewAnalyzerIdentityAssignmentClassImplicit] +class C: ... +class A: + C = C[str] # E: "C" expects no type arguments, but 1 given +[builtins fixtures/tuple.pyi] + +[case testNewAnalyzerIdentityAssignmentClassExplicit] +from typing_extensions import TypeAlias + +class A: + C: TypeAlias = C +class C: ... +c: A.C +reveal_type(c) # N: Revealed type is "__main__.C" +[builtins fixtures/tuple.pyi] + [case testNewAnalyzerClassPropertiesInAllScopes] from abc import abstractmethod, ABCMeta diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 80cceea85581..ba4104a50048 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1988,3 +1988,44 @@ bis: RK_functionBIS = ff res: int = bis(1.0, 2, 3) [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasNotReadyClass] +class CustomizeResponse: + related_resources: "ResourceRule" + +class ResourceRule: pass + +class DecoratorController: + type CustomizeResponse = CustomizeResponse + +x: DecoratorController.CustomizeResponse +reveal_type(x.related_resources) # N: Revealed type is "__main__.ResourceRule" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeAliasRecursiveOuterClass] +class A: + type X = X +class X: ... + +class Y: ... +class B: + type Y = Y + +x: A.X +reveal_type(x) # N: Revealed type is "__main__.X" +y: B.Y +reveal_type(y) # N: Revealed type is "__main__.Y" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeAliasRecursiveInvalid] +type X = X # E: Cannot resolve name "X" (possible cyclic definition) +type Z = Z[int] # E: Cannot resolve name "Z" (possible cyclic definition) +def foo() -> None: + type X = X # OK, refers to outer (invalid) X + x: X + reveal_type(x) # N: Revealed type is "Any" + type Y = Y # E: Cannot resolve name "Y" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope +class Z: ... # E: Name "Z" already defined on line 2 +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 9527c85ed26a..c5915176a5ff 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1286,3 +1286,31 @@ x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, give assert_type(x1, Callable[..., Any]) assert_type(x2, Callable[..., Any]) [builtins fixtures/tuple.pyi] + +[case testExplicitTypeAliasToSameNameOuterProhibited] +from typing import TypeVar, Generic +from typing_extensions import TypeAlias + +T = TypeVar("T") +class Foo(Generic[T]): + bar: Bar[T] + +class Bar(Generic[T]): + Foo: TypeAlias = Foo[T] # E: Can't use bound type variable "T" to define generic alias +[builtins fixtures/tuple.pyi] + +[case testExplicitTypeAliasToSameNameOuterAllowed] +from typing import TypeVar, Generic +from typing_extensions import TypeAlias + +T = TypeVar("T") +class Foo(Generic[T]): + bar: Bar[T] + +U = TypeVar("U") +class Bar(Generic[T]): + Foo: TypeAlias = Foo[U] + var: Foo[T] +x: Bar[int] +reveal_type(x.var.bar) # N: Revealed type is "__main__.Bar[builtins.int]" +[builtins fixtures/tuple.pyi] From dd6df2ecb77a06add6ce95d32a3f91cb751cf71e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 7 Feb 2025 15:37:58 +0000 Subject: [PATCH 1121/1617] Include fullname in Var repr (#18632) This makes debugging easier. --- mypy/nodes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/nodes.py b/mypy/nodes.py index 010e6ce4de6e..ff79c0494fc3 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1073,6 +1073,9 @@ def name(self) -> str: def fullname(self) -> str: return self._fullname + def __repr__(self) -> str: + return f"" + def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_var(self) From 52496543b972f0e204343c9e62541d59861259e0 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 8 Feb 2025 02:24:52 +0300 Subject: [PATCH 1122/1617] Test and fix trailing commas in many multiline string options in `pyproject.toml` (#18624) Refs https://github.com/python/mypy/pull/18621 Closes https://github.com/python/mypy/issues/18623 With a lot more tests. --- mypy/config_parser.py | 6 +- test-data/unit/check-custom-plugin.test | 12 ++++ test-data/unit/cmdline.pyproject.test | 93 +++++++++++++++++++++++++ test-data/unit/cmdline.test | 32 +++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 94427a347779..0e033471d2e9 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -55,8 +55,10 @@ def parse_version(v: str | float) -> tuple[int, int]: def try_split(v: str | Sequence[str], split_regex: str = "[,]") -> list[str]: """Split and trim a str or list of str into a list of str""" if isinstance(v, str): - return [p.strip() for p in re.split(split_regex, v)] - + items = [p.strip() for p in re.split(split_regex, v)] + if items and items[-1] == "": + items.pop(-1) + return items return [p.strip() for p in v] diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index db2ea2d5e659..feb135bee165 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1098,3 +1098,15 @@ reveal_type(1) # N: Revealed type is "Literal[1]?" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/custom_errorcode.py + + +[case testPyprojectPluginsTrailingComma] +# flags: --config-file tmp/pyproject.toml +[file pyproject.toml] +# This test checks that trailing commas in string-based `plugins` are allowed. +\[tool.mypy] +plugins = """ + /test-data/unit/plugins/function_sig_hook.py, + /test-data/unit/plugins/method_in_decorator.py, +""" +[out] diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index 57e6facad032..f9691ba245f9 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -133,3 +133,96 @@ Neither is this! description = "Factory ⸻ A code generator 🏭" \[tool.mypy] [file x.py] + +[case testPyprojectFilesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +# We combine multiple tests in a single one here, because these tests are slow. +files = """ + a.py, + b.py, +""" +always_true = """ + FLAG_A1, + FLAG_B1, +""" +always_false = """ + FLAG_A2, + FLAG_B2, +""" +[file a.py] +x: str = 'x' # ok' + +# --always-true +FLAG_A1 = False +FLAG_B1 = False +if not FLAG_A1: # unreachable + x: int = 'x' +if not FLAG_B1: # unreachable + y: int = 'y' + +# --always-false +FLAG_A2 = True +FLAG_B2 = True +if FLAG_A2: # unreachable + x: int = 'x' +if FLAG_B2: # unreachable + y: int = 'y' +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + +[case testPyprojectModulesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +# We combine multiple tests in a single one here, because these tests are slow. +modules = """ + a, + b, +""" +disable_error_code = """ + operator, + import, +""" +enable_error_code = """ + redundant-expr, + ignore-without-code, +""" +[file a.py] +x: str = 'x' # ok + +# --enable-error-code +a: int = 'a' # type: ignore + +# --disable-error-code +'a' + 1 +[file b.py] +y: int = 'y' +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] +b.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int") +a.py:4: error: "type: ignore" comment without error code (consider "type: ignore[assignment]" instead) + +[case testPyprojectPackagesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +packages = """ + a, + b, +""" +[file a/__init__.py] +x: str = 'x' # ok +[file b/__init__.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c/__init__.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index f298f6dbe2df..b9da5883c793 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1342,6 +1342,38 @@ always_true = MY_VAR, [out] +[case testCmdlineCfgModulesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +modules = + a, + b, +[file a.py] +x: str = 'x' # ok +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + +[case testCmdlineCfgPackagesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +packages = + a, + b, +[file a/__init__.py] +x: str = 'x' # ok +[file b/__init__.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c/__init__.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + [case testTypeVarTupleUnpackEnabled] # cmd: mypy --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack a.py [file a.py] From 7c0c4b49532bfd7f947f9df50c3d147946f4715b Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sat, 8 Feb 2025 06:08:30 +0100 Subject: [PATCH 1123/1617] Allow lambdas in except* clauses (#18620) Fixes #18618 --- mypy/semanal.py | 3 ++- test-data/unit/check-python311.test | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 86234d100c27..b6e534d3c8b3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6143,7 +6143,8 @@ def analyze_comp_for_2(self, expr: GeneratorExpr | DictionaryComprehension) -> N def visit_lambda_expr(self, expr: LambdaExpr) -> None: self.analyze_arg_initializers(expr) - self.analyze_function_body(expr) + with self.inside_except_star_block_set(False, entering_loop=False): + self.analyze_function_body(expr) def visit_conditional_expr(self, expr: ConditionalExpr) -> None: expr.if_expr.accept(self) diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index dfbb3d45e56f..c6d42660403e 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -260,6 +260,24 @@ def foo(): return # E: "return" not allowed in except* block [builtins fixtures/exception.pyi] +[case testLambdaInExceptStarBlock] +# flags: --python-version 3.11 +def foo(): + try: + pass + except* Exception: + x = lambda: 0 + return lambda: 0 # E: "return" not allowed in except* block + +def loop(): + while True: + try: + pass + except* Exception: + x = lambda: 0 + return lambda: 0 # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + [case testRedefineLocalWithinExceptStarTryClauses] # flags: --allow-redefinition def fn_str(_: str) -> int: ... From a8c2345ffcd3c9de6b8b2a7a9c99ecdac270bc0d Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 8 Feb 2025 16:11:41 +0100 Subject: [PATCH 1124/1617] Report that `NamedTuple` and `dataclass` are incompatile instead of crashing. (#18633) Fixes #18527 The fix is pretty simple. I could not find a situation where combining `NamedTuple` and `dataclass` makes sense, so emitting an error and just not applying the dataclass transformations seems sensible. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: sobolevn --- mypy/plugins/dataclasses.py | 3 +++ test-data/unit/check-dataclasses.test | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index acb785aad70a..90c983b0bacd 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -965,6 +965,9 @@ def dataclass_tag_callback(ctx: ClassDefContext) -> None: def dataclass_class_maker_callback(ctx: ClassDefContext) -> bool: """Hooks into the class typechecking process to add support for dataclasses.""" + if any(i.is_named_tuple for i in ctx.cls.info.mro): + ctx.api.fail("A NamedTuple cannot be a dataclass", ctx=ctx.cls.info) + return True transformer = DataclassTransformer( ctx.cls, ctx.reason, _get_transform_spec(ctx.reason), ctx.api ) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 26c81812ab62..9109b2b7c36d 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2576,3 +2576,20 @@ reveal_type(m.b) # N: Revealed type is "builtins.int" m.a = 1 # E: Cannot assign to final attribute "a" m.b = 2 # E: Cannot assign to final attribute "b" [builtins fixtures/tuple.pyi] + +[case testNoCrashForDataclassNamedTupleCombination] +# flags: --python-version 3.13 +from dataclasses import dataclass +from typing import NamedTuple + +@dataclass +class A(NamedTuple): # E: A NamedTuple cannot be a dataclass + i: int + +class B1(NamedTuple): + i: int +@dataclass +class B2(B1): # E: A NamedTuple cannot be a dataclass + pass + +[builtins fixtures/tuple.pyi] From 5bb681a26e3283524804ed7f332d626fbb83be25 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Sun, 9 Feb 2025 02:09:38 +0100 Subject: [PATCH 1125/1617] [mypyc] Recognize Literal types in __match_args__ (#18636) Fixes #18614 --- mypyc/irbuild/match.py | 41 +++++++++------- mypyc/test-data/irbuild-match.test | 78 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index 0daf1d609581..d7bf9e0b94de 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -16,7 +16,7 @@ ValuePattern, ) from mypy.traverser import TraverserVisitor -from mypy.types import Instance, TupleType, get_proper_type +from mypy.types import Instance, LiteralType, TupleType, get_proper_type from mypyc.ir.ops import BasicBlock, Value from mypyc.ir.rtypes import object_rprimitive from mypyc.irbuild.builder import IRBuilder @@ -152,23 +152,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None: node = pattern.class_ref.node assert isinstance(node, TypeInfo) - - ty = node.names.get("__match_args__") - assert ty - - match_args_type = get_proper_type(ty.type) - assert isinstance(match_args_type, TupleType) - - match_args: list[str] = [] - - for item in match_args_type.items: - proper_item = get_proper_type(item) - assert isinstance(proper_item, Instance) and proper_item.last_known_value - - match_arg = proper_item.last_known_value.value - assert isinstance(match_arg, str) - - match_args.append(match_arg) + match_args = extract_dunder_match_args_names(node) for i, expr in enumerate(pattern.positionals): self.builder.activate_block(self.code_block) @@ -355,3 +339,24 @@ def prep_sequence_pattern( patterns.append(pattern) return star_index, capture, patterns + + +def extract_dunder_match_args_names(info: TypeInfo) -> list[str]: + ty = info.names.get("__match_args__") + assert ty + match_args_type = get_proper_type(ty.type) + assert isinstance(match_args_type, TupleType) + + match_args: list[str] = [] + for item in match_args_type.items: + proper_item = get_proper_type(item) + + match_arg = None + if isinstance(proper_item, Instance) and proper_item.last_known_value: + match_arg = proper_item.last_known_value.value + elif isinstance(proper_item, LiteralType): + match_arg = proper_item.value + assert isinstance(match_arg, str), f"Unrecognized __match_args__ item: {item}" + + match_args.append(match_arg) + return match_args diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index c5dc81bbf049..57d9e5c22d40 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -1727,3 +1727,81 @@ L4: L5: L6: unreachable + +[case testMatchLiteralMatchArgs_python3_10] +from typing_extensions import Literal + +class Foo: + __match_args__: tuple[Literal["foo"]] = ("foo",) + foo: str + +def f(x: Foo) -> None: + match x: + case Foo(foo): + print("foo") + case _: + assert False, "Unreachable" +[out] +def Foo.__mypyc_defaults_setup(__mypyc_self__): + __mypyc_self__ :: __main__.Foo + r0 :: str + r1 :: tuple[str] +L0: + r0 = 'foo' + r1 = (r0) + __mypyc_self__.__match_args__ = r1 + return 1 +def f(x): + x :: __main__.Foo + r0 :: object + r1 :: i32 + r2 :: bit + r3 :: bool + r4 :: str + r5 :: object + r6, foo, r7 :: str + r8 :: object + r9 :: str + r10 :: object + r11 :: object[1] + r12 :: object_ptr + r13, r14 :: object + r15 :: i32 + r16 :: bit + r17, r18 :: bool +L0: + r0 = __main__.Foo :: type + r1 = PyObject_IsInstance(x, r0) + r2 = r1 >= 0 :: signed + r3 = truncate r1: i32 to builtins.bool + if r3 goto L1 else goto L3 :: bool +L1: + r4 = 'foo' + r5 = CPyObject_GetAttr(x, r4) + r6 = cast(str, r5) + foo = r6 +L2: + r7 = 'foo' + r8 = builtins :: module + r9 = 'print' + r10 = CPyObject_GetAttr(r8, r9) + r11 = [r7] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r10, r12, 1, 0) + keep_alive r7 + goto L8 +L3: +L4: + r14 = box(bool, 0) + r15 = PyObject_IsTrue(r14) + r16 = r15 >= 0 :: signed + r17 = truncate r15: i32 to builtins.bool + if r17 goto L6 else goto L5 :: bool +L5: + r18 = raise AssertionError('Unreachable') + unreachable +L6: + goto L8 +L7: +L8: + return 1 From 876f6361e43633ff7b77f980e1ad40d4a6934d56 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:36:42 -0800 Subject: [PATCH 1126/1617] Improve support for functools.partial of overloaded callable protocol (#18639) Resolves #18637 Mypy's behaviour here is not correct (see test case), but this PR makes mypy's behaviour match what it used to be before we added the functools.partial plugin Support for overloads tracked in #17585 --- mypy/checker.py | 89 ++++++++++++++++------------- test-data/unit/check-functools.test | 17 ++++++ 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 999d75678aa4..54ee53986f53 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -703,50 +703,57 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> CallableType | None: """Get type as seen by an overload item caller.""" inner_type = get_proper_type(inner_type) - outer_type: CallableType | None = None - if inner_type is not None and not isinstance(inner_type, AnyType): - if isinstance(inner_type, TypeVarLikeType): - inner_type = get_proper_type(inner_type.upper_bound) - if isinstance(inner_type, TypeType): - inner_type = get_proper_type( - self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) - ) + outer_type: FunctionLike | None = None + if inner_type is None or isinstance(inner_type, AnyType): + return None + if isinstance(inner_type, TypeVarLikeType): + inner_type = get_proper_type(inner_type.upper_bound) + if isinstance(inner_type, TypeType): + inner_type = get_proper_type( + self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + ) - if isinstance(inner_type, CallableType): - outer_type = inner_type - elif isinstance(inner_type, Instance): - inner_call = get_proper_type( - analyze_member_access( - name="__call__", - typ=inner_type, - context=ctx, - is_lvalue=False, - is_super=False, - is_operator=True, - msg=self.msg, - original_type=inner_type, - chk=self, - ) + if isinstance(inner_type, FunctionLike): + outer_type = inner_type + elif isinstance(inner_type, Instance): + inner_call = get_proper_type( + analyze_member_access( + name="__call__", + typ=inner_type, + context=ctx, + is_lvalue=False, + is_super=False, + is_operator=True, + msg=self.msg, + original_type=inner_type, + chk=self, ) - if isinstance(inner_call, CallableType): - outer_type = inner_call - elif isinstance(inner_type, UnionType): - union_type = make_simplified_union(inner_type.items) - if isinstance(union_type, UnionType): - items = [] - for item in union_type.items: - callable_item = self.extract_callable_type(item, ctx) - if callable_item is None: - break - items.append(callable_item) - else: - joined_type = get_proper_type(join.join_type_list(items)) - if isinstance(joined_type, CallableType): - outer_type = joined_type + ) + if isinstance(inner_call, FunctionLike): + outer_type = inner_call + elif isinstance(inner_type, UnionType): + union_type = make_simplified_union(inner_type.items) + if isinstance(union_type, UnionType): + items = [] + for item in union_type.items: + callable_item = self.extract_callable_type(item, ctx) + if callable_item is None: + break + items.append(callable_item) else: - return self.extract_callable_type(union_type, ctx) - if outer_type is None: - self.msg.not_callable(inner_type, ctx) + joined_type = get_proper_type(join.join_type_list(items)) + if isinstance(joined_type, FunctionLike): + outer_type = joined_type + else: + return self.extract_callable_type(union_type, ctx) + + if outer_type is None: + self.msg.not_callable(inner_type, ctx) + return None + if isinstance(outer_type, Overloaded): + return None + + assert isinstance(outer_type, CallableType) return outer_type def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 22159580163d..5bdc3ce7a352 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -640,3 +640,20 @@ hp = partial(h, 1) reveal_type(hp(1)) # N: Revealed type is "builtins.int" hp("a") # E: Argument 1 to "h" has incompatible type "str"; expected "int" [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialOverloadedCallableProtocol] +from functools import partial +from typing import Callable, Protocol, overload + +class P(Protocol): + @overload + def __call__(self, x: int) -> int: ... + @overload + def __call__(self, x: str) -> str: ... + +def f(x: P): + reveal_type(partial(x, 1)()) # N: Revealed type is "builtins.int" + + # TODO: but this is incorrect, predating the functools.partial plugin + reveal_type(partial(x, "a")()) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] From 29ffa3eb7e0fc7b822fc77819b66080cafc03921 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 9 Feb 2025 09:08:00 +0000 Subject: [PATCH 1127/1617] Fix overlap check for variadic generics (#18638) Fixes https://github.com/python/mypy/issues/18105 When I implemented this initially, I only handled tuples, but forgot instances, thus causing the crash. I don't add many tests, since the instance overlap check simply relays to the tuple one, that is already tested. (Btw I already forgot how verbose everything is in the `TypeVarTuple` world :-)) --- mypy/meet.py | 24 +++++++++++++++++++++-- test-data/unit/check-typevar-tuple.test | 26 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index ea2411b8ccc9..b5262f87c0bd 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -553,7 +553,27 @@ def _type_object_overlap(left: Type, right: Type) -> bool: else: return False - if len(left.args) == len(right.args): + if right.type.has_type_var_tuple_type: + # Similar to subtyping, we delegate the heavy lifting to the tuple overlap. + assert right.type.type_var_tuple_prefix is not None + assert right.type.type_var_tuple_suffix is not None + prefix = right.type.type_var_tuple_prefix + suffix = right.type.type_var_tuple_suffix + tvt = right.type.defn.type_vars[prefix] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + left_prefix, left_middle, left_suffix = split_with_prefix_and_suffix( + left.args, prefix, suffix + ) + right_prefix, right_middle, right_suffix = split_with_prefix_and_suffix( + right.args, prefix, suffix + ) + left_args = left_prefix + (TupleType(list(left_middle), fallback),) + left_suffix + right_args = right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix + else: + left_args = left.args + right_args = right.args + if len(left_args) == len(right_args): # Note: we don't really care about variance here, since the overlapping check # is symmetric and since we want to return 'True' even for partial overlaps. # @@ -570,7 +590,7 @@ def _type_object_overlap(left: Type, right: Type) -> bool: # to contain only instances of B at runtime. if all( _is_overlapping_types(left_arg, right_arg) - for left_arg, right_arg in zip(left.args, right.args) + for left_arg, right_arg in zip(left_args, right_args) ): return True diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index f49e1b3c6613..754151ffb559 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2487,3 +2487,29 @@ class C(Generic[P, R]): c: C[int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" reveal_type(c.fn) # N: Revealed type is "def (*Any, **Any)" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleInstanceOverlap] +# flags: --strict-equality +from typing import TypeVarTuple, Unpack, Generic + +Ts = TypeVarTuple("Ts") + +class Foo(Generic[Unpack[Ts]]): + pass + +x1: Foo[Unpack[tuple[int, ...]]] +y1: Foo[Unpack[tuple[str, ...]]] +x1 is y1 # E: Non-overlapping identity check (left operand type: "Foo[Unpack[Tuple[int, ...]]]", right operand type: "Foo[Unpack[Tuple[str, ...]]]") + +x2: Foo[Unpack[tuple[int, ...]]] +y2: Foo[Unpack[tuple[int, ...]]] +x2 is y2 + +x3: Foo[Unpack[tuple[int, ...]]] +y3: Foo[Unpack[tuple[int, int]]] +x3 is y3 + +x4: Foo[Unpack[tuple[str, ...]]] +y4: Foo[Unpack[tuple[int, int]]] +x4 is y4 # E: Non-overlapping identity check (left operand type: "Foo[Unpack[Tuple[str, ...]]]", right operand type: "Foo[int, int]") +[builtins fixtures/tuple.pyi] From ecc13c803e35f075ed11ab5609eccc9548cf2a04 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 9 Feb 2025 19:59:48 +0100 Subject: [PATCH 1128/1617] Fix spelling (#18642) Co-authored-by: Christian Clauss --- docs/source/generics.rst | 2 +- docs/source/more_types.rst | 2 +- docs/source/runtime_troubles.rst | 2 +- mypy/argmap.py | 2 +- mypy/checkexpr.py | 2 +- mypy/constraints.py | 2 +- mypy/inspections.py | 2 +- mypy/join.py | 2 +- mypy/messages.py | 2 +- mypy/semanal_namedtuple.py | 2 +- mypy/semanal_newtype.py | 2 +- mypy/stubgenc.py | 2 +- mypy/suggestions.py | 2 +- mypy/type_visitor.py | 4 ++-- mypyc/codegen/emitfunc.py | 2 +- mypyc/doc/dev-intro.md | 8 ++++---- mypyc/irbuild/prebuildvisitor.py | 2 +- mypyc/test/test_run.py | 2 +- test-data/unit/stubgen.test | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 731365d3789b..15538dea13bf 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -999,7 +999,7 @@ similarly supported via generics (Python 3.12 syntax): .. code-block:: python - from colletions.abc import Callable + from collections.abc import Callable from typing import Any def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index cbf40d5dcaa5..0383c3448d06 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -390,7 +390,7 @@ program: The ``summarize([])`` call matches both variants: an empty list could be either a ``list[int]`` or a ``list[str]``. In this case, mypy will break the tie by picking the first matching variant: ``output`` -will have an inferred type of ``float``. The implementor is responsible +will have an inferred type of ``float``. The implementer is responsible for making sure ``summarize`` breaks ties in the same way at runtime. However, there are two exceptions to the "pick the first match" rule. diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index d63d0f9a74ae..b61f0048dd0a 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -274,7 +274,7 @@ libraries if types are generic only in stubs. Using types defined in stubs but not at runtime ----------------------------------------------- -Sometimes stubs that you're using may define types you wish to re-use that do +Sometimes stubs that you're using may define types you wish to reuse that do not exist at runtime. Importing these types naively will cause your code to fail at runtime with ``ImportError`` or ``ModuleNotFoundError``. Similar to previous sections, these can be dealt with by using :ref:`typing.TYPE_CHECKING diff --git a/mypy/argmap.py b/mypy/argmap.py index c863844f90ad..8db78b5413e8 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -220,7 +220,7 @@ def expand_actual_type( self.tuple_index += 1 item = actual_type.items[self.tuple_index - 1] if isinstance(item, UnpackType) and not allow_unpack: - # An upack item that doesn't have special handling, use upper bound as above. + # An unpack item that doesn't have special handling, use upper bound as above. unpacked = get_proper_type(item.type) if isinstance(unpacked, TypeVarTupleType): fallback = get_proper_type(unpacked.upper_bound) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 286ef0dab6ae..963667188d6c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3184,7 +3184,7 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call new_type_narrowers.append(target.type_is) if new_type_guards and new_type_narrowers: - # They cannot be definined at the same time, + # They cannot be defined at the same time, # declaring this function as too complex! too_complex = True union_type_guard = None diff --git a/mypy/constraints.py b/mypy/constraints.py index defcac21bc66..3c0d08089722 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -385,7 +385,7 @@ def _infer_constraints( res = [] for a_item in actual.items: # `orig_template` has to be preserved intact in case it's recursive. - # If we unwraped ``type[...]`` previously, wrap the item back again, + # If we unwrapped ``type[...]`` previously, wrap the item back again, # as ``type[...]`` can't be removed from `orig_template`. if type_type_unwrapped: a_item = TypeType.make_normalized(a_item) diff --git a/mypy/inspections.py b/mypy/inspections.py index bc76ab247901..ac48fac56fa4 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -564,7 +564,7 @@ def run_inspection( ) -> dict[str, object]: """Top-level logic to inspect expression(s) at a location. - This can be re-used by various simple inspections. + This can be reused by various simple inspections. """ try: file, pos = parse_location(location) diff --git a/mypy/join.py b/mypy/join.py index a5c30b4b835d..9a13dfb42b64 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -451,7 +451,7 @@ def join_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: return items return None if s_unpack_index is not None and t_unpack_index is not None: - # The most complex case: both tuples have an upack item. + # The most complex case: both tuples have an unpack item. s_unpack = s.items[s_unpack_index] assert isinstance(s_unpack, UnpackType) s_unpacked = get_proper_type(s_unpack.type) diff --git a/mypy/messages.py b/mypy/messages.py index 3beb287bcc21..9315e77dfd98 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -215,7 +215,7 @@ def are_type_names_disabled(self) -> bool: def prefer_simple_messages(self) -> bool: """Should we generate simple/fast error messages? - If errors aren't shown to the user, we don't want to waste cyles producing + If errors aren't shown to the user, we don't want to waste cycles producing complex error messages. """ return self.errors.prefer_simple_messages() diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index a18d0591364c..b67747d16887 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -198,7 +198,7 @@ def check_namedtuple_classdef( # Something is incomplete. We need to defer this named tuple. return None types.append(analyzed) - # ...despite possible minor failures that allow further analyzis. + # ...despite possible minor failures that allow further analysis. if name.startswith("_"): self.fail( f"NamedTuple field name cannot start with an underscore: {name}", stmt diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index c9c0c46f7aee..0c717b5d9a0e 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -174,7 +174,7 @@ def analyze_newtype_declaration(self, s: AssignmentStmt) -> tuple[str | None, Ca def check_newtype_args( self, name: str, call: CallExpr, context: Context ) -> tuple[Type | None, bool]: - """Ananlyze base type in NewType call. + """Analyze base type in NewType call. Return a tuple (type, should defer). """ diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 694be8e4beda..b5bb4f8f727b 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -341,7 +341,7 @@ def get_pos_default(i: int, _arg: str) -> Any | None: # Add *args if present if varargs: arglist.append(ArgSig(f"*{varargs}", get_annotation(varargs))) - # if we have keyword only args, then wee need to add "*" + # if we have keyword only args, then we need to add "*" elif kwonlyargs: arglist.append(ArgSig("*")) diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 36dc7e8e2acd..16e630bf8c6e 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -454,7 +454,7 @@ def get_guesses_from_parent(self, node: FuncDef) -> list[CallableType]: pnode = parent.names.get(node.name) if pnode and isinstance(pnode.node, (FuncDef, Decorator)): typ = get_proper_type(pnode.node.type) - # FIXME: Doesn't work right with generic tyeps + # FIXME: Doesn't work right with generic types if isinstance(typ, CallableType) and len(typ.arg_types) == len(node.arguments): # Return the first thing we find, since it probably doesn't make sense # to grab things further up in the chain if an earlier parent has it. diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index d935b9a47a51..ab1ec8b46fdd 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -344,7 +344,7 @@ class TypeQuery(SyntheticTypeVisitor[T]): common use cases involve a boolean query using `any` or `all`. Note: this visitor keeps an internal state (tracks type aliases to avoid - recursion), so it should *never* be re-used for querying different types, + recursion), so it should *never* be reused for querying different types, create a new visitor instance instead. # TODO: check that we don't have existing violations of this rule. @@ -467,7 +467,7 @@ class BoolTypeQuery(SyntheticTypeVisitor[bool]): be ANY_STRATEGY or ALL_STRATEGY. Note: This visitor keeps an internal state (tracks type aliases to avoid - recursion), so it should *never* be re-used for querying different types + recursion), so it should *never* be reused for querying different types unless you call reset() first. """ diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 7239e0835da0..c854516825af 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -152,7 +152,7 @@ def generate_native_function( # generates them will add instructions between the branch and the # next label, causing the label to be wrongly removed. A better # solution would be to change the IR so that it adds a basic block - # inbetween the calls. + # in between the calls. is_problematic_op = isinstance(terminator, Branch) and any( isinstance(s, GetAttr) for s in terminator.sources() ) diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index a8a04a297688..5f6c064dac37 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -229,7 +229,7 @@ pretty-printed IR into `build/ops.txt`. This is the final IR that includes the output from exception and reference count handling insertion passes. -We also have tests that verify the generate IR +We also have tests that verify the generated IR (`mypyc/test-data/irbuild-*.text`). ## Type-checking Mypyc @@ -290,7 +290,7 @@ under `mypyc/lib-rt`. ## Inspecting Generated C -It's often useful to inspect the C code genenerate by mypyc to debug +It's often useful to inspect the C code generated by mypyc to debug issues. Mypyc stores the generated C code as `build/__native.c`. Compiled native functions have the prefix `CPyDef_`, while wrapper functions used for calling functions from interpreted Python code have @@ -386,7 +386,7 @@ Test cases can also have a `[out]` section, which specifies the expected contents of stdout the test case should produce. New test cases should prefer assert statements to `[out]` sections. -### Debuggging Segfaults +### Debugging Segfaults If you experience a segfault, it's recommended to use a debugger that supports C, such as gdb or lldb, to look into the segfault. @@ -409,7 +409,7 @@ Program received signal SIGSEGV, Segmentation fault. ``` You must use `-n0 -s` to enable interactive input to the debugger. -Instad of `gdb`, you can also try `lldb` (especially on macOS). +Instead of `gdb`, you can also try `lldb` (especially on macOS). To get better C stack tracebacks and more assertions in the Python runtime, you can build Python in debug mode and use that to run tests, diff --git a/mypyc/irbuild/prebuildvisitor.py b/mypyc/irbuild/prebuildvisitor.py index 17f907d42111..5f178a290138 100644 --- a/mypyc/irbuild/prebuildvisitor.py +++ b/mypyc/irbuild/prebuildvisitor.py @@ -73,7 +73,7 @@ def __init__( self.decorators_to_remove: dict[FuncDef, list[int]] = decorators_to_remove # A mapping of import groups (a series of Import nodes with - # nothing inbetween) where each group is keyed by its first + # nothing in between) where each group is keyed by its first # import node. self.module_import_groups: dict[Import, list[Import]] = {} self._current_import_group: Import | None = None diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 6dfa7819e585..35598b24bce8 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -344,7 +344,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> f'hint: Use "pytest -n0 -s --mypyc-debug={debugger} -k " to run test in debugger' ) print("hint: You may need to build a debug version of Python first and use it") - print('hint: See also "Debuggging Segfaults" in mypyc/doc/dev-intro.md') + print('hint: See also "Debugging Segfaults" in mypyc/doc/dev-intro.md') copy_output_files(mypyc_output_dir) # Verify output. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 5c0d2d6f8e00..bf17c34b99a7 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -4270,7 +4270,7 @@ class Y(missing.Base): generated_kwargs_: float [case testDataclassTransform] -# dataclass_transform detection only works with sementic analysis. +# dataclass_transform detection only works with semantic analysis. # Test stubgen doesn't break too badly without it. from typing_extensions import dataclass_transform From 653fc9bb79a8e45dcd6bc89fa9f24c5d170deeca Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:01:05 +0100 Subject: [PATCH 1129/1617] Update Literal imports in tests (#18640) `Literal` was added to Python in 3.8. Replace most `typing_extensions` imports in tests. --- mypyc/test-data/run-misc.test | 4 +- test-data/unit/check-basic.test | 4 +- test-data/unit/check-enum.test | 33 ++- test-data/unit/check-expressions.test | 9 +- test-data/unit/check-final.test | 6 +- test-data/unit/check-incremental.test | 4 +- test-data/unit/check-isinstance.test | 2 +- test-data/unit/check-literal.test | 292 +++++++++---------- test-data/unit/check-narrowing.test | 98 +++---- test-data/unit/check-newsemanal.test | 7 +- test-data/unit/check-overloading.test | 3 +- test-data/unit/check-protocols.test | 4 +- test-data/unit/check-python310.test | 5 +- test-data/unit/check-python38.test | 9 +- test-data/unit/check-recursive-types.test | 3 +- test-data/unit/check-selftype.test | 6 +- test-data/unit/check-statements.test | 2 +- test-data/unit/check-type-aliases.test | 4 +- test-data/unit/check-typeddict.test | 12 +- test-data/unit/check-union-error-syntax.test | 12 +- test-data/unit/check-union-or-syntax.test | 3 +- test-data/unit/check-unreachable-code.test | 22 +- test-data/unit/deps-expressions.test | 2 +- test-data/unit/diff.test | 22 +- test-data/unit/fine-grained.test | 48 ++- test-data/unit/fixtures/set.pyi | 1 + test-data/unit/lib-stub/typing.pyi | 1 + test-data/unit/merge.test | 8 +- test-data/unit/pythoneval.test | 4 +- test-data/unit/semanal-literal.test | 8 +- 30 files changed, 291 insertions(+), 347 deletions(-) diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 2252f3aa104a..a3ebc3923003 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -612,8 +612,8 @@ for a in sorted(s): 9 8 72 [case testDummyTypes] -from typing import Tuple, List, Dict, NamedTuple -from typing_extensions import Literal, TypedDict, NewType +from typing import Tuple, List, Dict, Literal, NamedTuple +from typing_extensions import TypedDict, NewType class A: pass diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 13968bdfb885..4096f738bddf 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -406,8 +406,8 @@ class B(Enum): b = 10 [file b.py] -from typing import List, Optional, Union, Sequence, NamedTuple, Tuple, Type -from typing_extensions import Literal, Final, TypedDict +from typing import List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type +from typing_extensions import Final, TypedDict from enum import Enum import a class A: pass diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 4b7460696aec..6c111e05e33e 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -18,7 +18,7 @@ if int(): [case testEnumCreatedFromStringLiteral] from enum import Enum -from typing_extensions import Literal +from typing import Literal x: Literal['ANT BEE CAT DOG'] = 'ANT BEE CAT DOG' Animal = Enum('Animal', x) @@ -181,7 +181,7 @@ def infer_truth(truth: Truth) -> None: [case testEnumTruthyness] # mypy: warn-unreachable import enum -from typing_extensions import Literal +from typing import Literal class E(enum.Enum): zero = 0 @@ -213,7 +213,7 @@ def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: [case testEnumTruthynessCustomDunderBool] # mypy: warn-unreachable import enum -from typing_extensions import Literal +from typing import Literal class E(enum.Enum): zero = 0 @@ -247,7 +247,7 @@ def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: [case testEnumTruthynessStrEnum] # mypy: warn-unreachable import enum -from typing_extensions import Literal +from typing import Literal class E(enum.StrEnum): empty = "" @@ -726,7 +726,7 @@ reveal_type(Test.a) # N: Revealed type is "Literal[__main__.Test.a]?" [case testEnumAttributeAccessMatrix] from enum import Enum, IntEnum, IntFlag, Flag, EnumMeta, auto -from typing_extensions import Literal +from typing import Literal def is_x(val: Literal['x']) -> None: pass @@ -872,7 +872,7 @@ main:2: note: Revealed type is "Literal['foo']?" [case testEnumReachabilityChecksBasic] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): A = 1 @@ -924,7 +924,7 @@ reveal_type(y) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityChecksWithOrdering] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): _order_ = "A B" @@ -975,7 +975,8 @@ else: [case testEnumReachabilityChecksIndirect] from enum import Enum -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final class Foo(Enum): A = 1 @@ -1040,7 +1041,7 @@ else: [case testEnumReachabilityNoNarrowingForUnionMessiness] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): A = 1 @@ -1096,8 +1097,7 @@ reveal_type(x) # N: Revealed type is "Union[__main__.Foo, None]" [case testEnumReachabilityWithMultipleEnums] from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Foo(Enum): A = 1 @@ -1331,7 +1331,8 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingDirectConflict] # flags: --warn-unreachable from enum import Enum -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final class Foo(Enum): A = 1 @@ -1366,7 +1367,8 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingBigDisjoints] # flags: --warn-unreachable from enum import Enum -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final class Foo(Enum): A = 1 @@ -1488,8 +1490,7 @@ reveal_type(a._value_) # N: Revealed type is "Any" # as the full type, regardless of the amount of elements # the enum contains. from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Foo(Enum): A = 1 @@ -1507,7 +1508,7 @@ def f(x: Foo): [case testEnumTypeCompatibleWithLiteralUnion] from enum import Enum -from typing_extensions import Literal +from typing import Literal class E(Enum): A = 1 diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 68bfb24e288b..b64f15a4aaf0 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -985,8 +985,7 @@ main:4: error: "A" not callable -- assert_type() [case testAssertType] -from typing import assert_type, Any -from typing_extensions import Literal +from typing import assert_type, Any, Literal a: int = 1 returned = assert_type(a, int) reveal_type(returned) # N: Revealed type is "builtins.int" @@ -998,8 +997,7 @@ assert_type(42, int) # E: Expression is of type "Literal[42]", not "int" [builtins fixtures/tuple.pyi] [case testAssertTypeGeneric] -from typing import assert_type, TypeVar, Generic -from typing_extensions import Literal +from typing import assert_type, Literal, TypeVar, Generic T = TypeVar("T") def f(x: T) -> T: return x assert_type(f(1), int) @@ -2283,7 +2281,8 @@ def f(x: T) -> T: [case testStrictEqualityWithALiteral] # flags: --strict-equality -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final def returns_a_or_b() -> Literal['a', 'b']: ... diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 51ce0edc66c2..02c0b4c5face 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1187,7 +1187,8 @@ class Child(Parent): def __bar(self) -> None: ... [case testFinalWithoutBool] -from typing_extensions import final, Literal +from typing import Literal +from typing_extensions import final class A: pass @@ -1207,7 +1208,8 @@ reveal_type(C() and 42) # N: Revealed type is "Literal[42]?" [builtins fixtures/bool.pyi] [case testFinalWithoutBoolButWithLen] -from typing_extensions import final, Literal +from typing import Literal +from typing_extensions import final # Per Python data model, __len__ is called if __bool__ does not exist. # In a @final class, __bool__ would not exist. diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 6b888c0047c3..6b9a09435bcb 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5080,10 +5080,10 @@ plugins=/test-data/unit/plugins/config_data.py import mod reveal_type(mod.a) [file mod.py] -from typing_extensions import Literal +from typing import Literal a = 1 [file mod.py.2] -from typing_extensions import Literal +from typing import Literal a: Literal[2] = 2 [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 759d38445c55..49140bf52b8d 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -2378,7 +2378,7 @@ if isinstance(y, B): # flags: --warn-unreachable from abc import abstractmethod -from typing_extensions import Literal +from typing import Literal class A0: def f(self) -> Literal[0]: diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index c5d834374d0d..78ab872bbc0f 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -4,7 +4,7 @@ -- [case testLiteralInvalidString] -from typing_extensions import Literal +from typing import Literal def f1(x: 'A[') -> None: pass # E: Invalid type comment or annotation def g1(x: Literal['A[']) -> None: pass reveal_type(f1) # N: Revealed type is "def (x: Any)" @@ -22,13 +22,13 @@ reveal_type(i2) # N: Revealed type is "def (x: Literal['A|B'])" [out] [case testLiteralInvalidTypeComment] -from typing_extensions import Literal +from typing import Literal def f(x): # E: Syntax error in type comment "(A[) -> None" # type: (A[) -> None pass [case testLiteralInvalidTypeComment2] -from typing_extensions import Literal +from typing import Literal def f(x): # E: Invalid type comment or annotation # type: ("A[") -> None pass @@ -53,8 +53,7 @@ y = 43 [typing fixtures/typing-medium.pyi] [case testLiteralInsideOtherTypes] -from typing import Tuple -from typing_extensions import Literal +from typing import Literal, Tuple x: Tuple[1] # E: Invalid type: try using Literal[1] instead? def foo(x: Tuple[1]) -> None: ... # E: Invalid type: try using Literal[1] instead? @@ -68,8 +67,7 @@ reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal [out] [case testLiteralInsideOtherTypesTypeCommentsPython3] -from typing import Tuple, Optional -from typing_extensions import Literal +from typing import Literal, Tuple, Optional x = None # type: Optional[Tuple[1]] # E: Invalid type: try using Literal[1] instead? def foo(x): # E: Invalid type: try using Literal[1] instead? @@ -90,7 +88,7 @@ reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal from wrapper import * [file wrapper.pyi] -from typing_extensions import Literal +from typing import Literal alias_1 = Literal['a+b'] alias_2 = Literal['1+2'] @@ -153,7 +151,7 @@ reveal_type(expr_com_6) # N: Revealed type is "Literal['"foo"']" [out] [case testLiteralMixingUnicodeAndBytesPython3] -from typing_extensions import Literal +from typing import Literal a_ann: Literal[u"foo"] b_ann: Literal["foo"] @@ -217,8 +215,7 @@ accepts_bytes(c_alias) [out] [case testLiteralMixingUnicodeAndBytesPython3ForwardStrings] -from typing import TypeVar, Generic -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic a_unicode_wrapper: u"Literal[u'foo']" b_unicode_wrapper: u"Literal['foo']" @@ -282,8 +279,7 @@ reveal_type(c_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Liter [out] [case testLiteralUnicodeWeirdCharacters-skip_path_normalization] -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal a1: Literal["\x00\xAC\x62 \u2227 \u03bb(p)"] b1: Literal["\x00¬b ∧ λ(p)"] @@ -340,7 +336,7 @@ a1 = c3 # E: Incompatible types in assignment (expression has type "Literal['¬ [out] [case testLiteralRenamingImportWorks] -from typing_extensions import Literal as Foo +from typing import Literal as Foo x: Foo[3] reveal_type(x) # N: Revealed type is "Literal[3]" @@ -360,13 +356,13 @@ reveal_type(x) # N: Revealed type is "Literal[3]" reveal_type(y) # N: Revealed type is "Literal[4]" [file other_module.py] -from typing_extensions import Literal as Foo +from typing import Literal as Foo Bar = Foo[4] [builtins fixtures/tuple.pyi] [out] [case testLiteralRenamingImportNameConfusion] -from typing_extensions import Literal as Foo +from typing import Literal as Foo x: Foo["Foo"] reveal_type(x) # N: Revealed type is "Literal['Foo']" @@ -396,7 +392,7 @@ indirect = f() -- [case testLiteralBasicIntUsage] -from typing_extensions import Literal +from typing import Literal a1: Literal[4] b1: Literal[0x2a] @@ -435,7 +431,7 @@ reveal_type(f4) # N: Revealed type is "def (x: Literal[8]) -> Literal[8]" [out] [case testLiteralBasicBoolUsage] -from typing_extensions import Literal +from typing import Literal a1: Literal[True] b1: Literal[False] @@ -460,7 +456,7 @@ reveal_type(f2) # N: Revealed type is "def (x: Literal[False]) -> Literal[False [out] [case testLiteralBasicStrUsage] -from typing_extensions import Literal +from typing import Literal a: Literal[""] b: Literal[" foo bar "] @@ -489,7 +485,7 @@ reveal_type(f5) # N: Revealed type is "def (x: Literal['foo']) -> Literal['foo' [out] [case testLiteralBasicStrUsageSlashes-skip_path_normalization] -from typing_extensions import Literal +from typing import Literal a: Literal[r"foo\nbar"] b: Literal["foo\nbar"] @@ -503,7 +499,7 @@ main:7: note: Revealed type is "Literal['foo\nbar']" [case testLiteralBasicNoneUsage] # Note: Literal[None] and None are equivalent -from typing_extensions import Literal +from typing import Literal a: Literal[None] reveal_type(a) # N: Revealed type is "None" @@ -518,7 +514,7 @@ reveal_type(f3) # N: Revealed type is "def (x: None)" [out] [case testLiteralCallingUnionFunction] -from typing_extensions import Literal +from typing import Literal def func(x: Literal['foo', 'bar', ' foo ']) -> None: ... @@ -544,8 +540,7 @@ func(f) # E: Argument 1 to "func" has incompatible type "Literal['foo', 'bar', [out] [case testLiteralDisallowAny] -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal from missing_module import BadAlias # E: Cannot find implementation or library stub for module named "missing_module" \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports @@ -558,7 +553,7 @@ reveal_type(b) # N: Revealed type is "Any" [out] [case testLiteralDisallowActualTypes] -from typing_extensions import Literal +from typing import Literal a: Literal[int] # E: Parameter 1 of Literal[...] is invalid b: Literal[float] # E: Parameter 1 of Literal[...] is invalid @@ -574,7 +569,7 @@ reveal_type(d) # N: Revealed type is "Any" [case testLiteralDisallowFloatsAndComplex] -from typing_extensions import Literal +from typing import Literal a1: Literal[3.14] # E: Parameter 1 of Literal[...] cannot be of type "float" b1: 3.14 # E: Invalid type: float literals cannot be used as a type c1: Literal[3j] # E: Parameter 1 of Literal[...] cannot be of type "complex" @@ -597,7 +592,7 @@ d2: d2t # E: Variable "__main__.d2t" is not valid as a type \ [out] [case testLiteralDisallowComplexExpressions] -from typing_extensions import Literal +from typing import Literal def dummy() -> int: return 3 a: Literal[3 + 4] # E: Invalid type: Literal[...] cannot contain arbitrary expressions b: Literal[" foo ".trim()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions @@ -607,7 +602,7 @@ e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain a [out] [case testLiteralDisallowCollections] -from typing_extensions import Literal +from typing import Literal a: Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid b: Literal[{1, 2, 3}] # E: Invalid type: Literal[...] cannot contain arbitrary expressions c: {"a": 1, "b": 2} # E: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict \ @@ -618,7 +613,7 @@ d: {1, 2, 3} # E: Invalid type comment or annotation [typing fixtures/typing-full.pyi] [case testLiteralDisallowCollections2] -from typing_extensions import Literal +from typing import Literal a: (1, 2, 3) # E: Syntax error in type annotation \ # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) b: Literal[[1, 2, 3]] # E: Parameter 1 of Literal[...] is invalid @@ -626,7 +621,7 @@ c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid a [builtins fixtures/tuple.pyi] [case testLiteralDisallowCollectionsTypeAlias] -from typing_extensions import Literal +from typing import Literal at = Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid bt = {"a": 1, "b": 2} a: at @@ -637,7 +632,7 @@ b: bt # E: Variable "__main__.bt" is not valid as a ty [typing fixtures/typing-typeddict.pyi] [case testLiteralDisallowCollectionsTypeAlias2] -from typing_extensions import Literal +from typing import Literal at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type bt = {1, 2, 3} a: at # E: Variable "__main__.at" is not valid as a type \ @@ -645,11 +640,11 @@ a: at # E: Variable "__main__.at" is not valid as a ty b: bt # E: Variable "__main__.bt" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases [builtins fixtures/set.pyi] +[typing fixtures/typing-full.pyi] [out] [case testLiteralDisallowTypeVar] -from typing import TypeVar, Tuple -from typing_extensions import Literal +from typing import Literal, TypeVar, Tuple T = TypeVar('T') @@ -666,7 +661,7 @@ def foo(b: Literal[T]) -> Tuple[T]: pass # E: Parameter 1 of Literal[...] is i -- [case testLiteralMultipleValues] -from typing_extensions import Literal +from typing import Literal a: Literal[1, 2, 3] b: Literal["a", "b", "c"] c: Literal[1, "b", True, None] @@ -684,7 +679,7 @@ reveal_type(e) # N: Revealed type is "Union[None, None, None]" [out] [case testLiteralMultipleValuesExplicitTuple] -from typing_extensions import Literal +from typing import Literal # Unfortunately, it seems like typed_ast is unable to distinguish this from # Literal[1, 2, 3]. So we treat the two as being equivalent for now. a: Literal[1, 2, 3] @@ -696,7 +691,7 @@ reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2], Literal[3]] [case testLiteralNestedUsage] -from typing_extensions import Literal +from typing import Literal a: Literal[Literal[3], 4, Literal["foo"]] reveal_type(a) # N: Revealed type is "Union[Literal[3], Literal[4], Literal['foo']]" @@ -716,7 +711,7 @@ reveal_type(combined) # N: Revealed type is "Union[Literal['r'], Literal['w'], [out] [case testLiteralBiasTowardsAssumingForwardReference] -from typing_extensions import Literal +from typing import Literal a: "Foo" reveal_type(a) # N: Revealed type is "__main__.Foo" @@ -734,7 +729,7 @@ class Foo: pass [out] [case testLiteralBiasTowardsAssumingForwardReferenceForTypeAliases] -from typing_extensions import Literal +from typing import Literal a: "Foo" reveal_type(a) # N: Revealed type is "Literal[5]" @@ -756,7 +751,7 @@ Foo = Literal[5] [out] [case testLiteralBiasTowardsAssumingForwardReferencesForTypeComments] -from typing_extensions import Literal +from typing import Literal a: Foo reveal_type(a) # N: Revealed type is "__main__.Foo" @@ -779,7 +774,7 @@ class Foo: pass -- [case testLiteralCallingFunction] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> None: pass a: Literal[1] @@ -793,7 +788,7 @@ foo(c) # E: Argument 1 to "foo" has incompatible type "int"; expected "Literal[ [out] [case testLiteralCallingFunctionWithUnionLiteral] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[1, 2, 3]) -> None: pass a: Literal[1] @@ -809,7 +804,7 @@ foo(d) # E: Argument 1 to "foo" has incompatible type "int"; expected "Literal[ [out] [case testLiteralCallingFunctionWithStandardBase] -from typing_extensions import Literal +from typing import Literal def foo(x: int) -> None: pass a: Literal[1] @@ -823,8 +818,7 @@ foo(c) # E: Argument 1 to "foo" has incompatible type "Literal[4, 'foo']"; expe [out] [case testLiteralCheckSubtypingStrictOptional] -from typing import Any, NoReturn -from typing_extensions import Literal +from typing import Any, Literal, NoReturn lit: Literal[1] def f_lit(x: Literal[1]) -> None: pass @@ -848,8 +842,7 @@ f_lit(c) # E: Argument 1 to "f_lit" has incompatible type "None"; expected "Lite [case testLiteralCheckSubtypingNoStrictOptional] # flags: --no-strict-optional -from typing import Any, NoReturn -from typing_extensions import Literal +from typing import Any, Literal, NoReturn lit: Literal[1] def f_lit(x: Literal[1]) -> None: pass @@ -872,8 +865,7 @@ f_lit(c) [builtins fixtures/tuple.pyi] [case testLiteralCallingOverloadedFunction] -from typing import overload, Generic, TypeVar, Any -from typing_extensions import Literal +from typing import overload, Generic, Literal, TypeVar, Any T = TypeVar('T') class IOLike(Generic[T]): pass @@ -905,8 +897,7 @@ foo(d) [out] [case testLiteralVariance] -from typing import Generic, TypeVar -from typing_extensions import Literal +from typing import Generic, Literal, TypeVar T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -937,8 +928,7 @@ c2 = c3 [out] [case testLiteralInListAndSequence] -from typing import List, Sequence -from typing_extensions import Literal +from typing import List, Literal, Sequence def foo(x: List[Literal[1, 2]]) -> None: pass def bar(x: Sequence[Literal[1, 2]]) -> None: pass @@ -956,7 +946,7 @@ bar(b) # E: Argument 1 to "bar" has incompatible type "List[Literal[1, 2, 3]]"; [out] [case testLiteralRenamingDoesNotChangeTypeChecking] -from typing_extensions import Literal as Foo +from typing import Literal as Foo from other_module import Bar1, Bar2, c def func(x: Foo[15]) -> None: pass @@ -968,7 +958,7 @@ func(b) # E: Argument 1 to "func" has incompatible type "Literal[14]"; expected func(c) [file other_module.py] -from typing_extensions import Literal +from typing import Literal Bar1 = Literal[15] Bar2 = Literal[14] @@ -982,7 +972,7 @@ c: Literal[15] -- [case testLiteralInferredInAssignment] -from typing_extensions import Literal +from typing import Literal int1: Literal[1] = 1 int2 = 1 @@ -1016,7 +1006,7 @@ reveal_type(none3) # N: Revealed type is "None" [out] [case testLiteralInferredOnlyForActualLiterals] -from typing_extensions import Literal +from typing import Literal w: Literal[1] x: Literal["foo"] @@ -1057,7 +1047,7 @@ combined = h [out] [case testLiteralInferredTypeMustMatchExpected] -from typing_extensions import Literal +from typing import Literal a: Literal[1] = 2 # E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]") b: Literal["foo"] = "bar" # E: Incompatible types in assignment (expression has type "Literal['bar']", variable has type "Literal['foo']") @@ -1071,7 +1061,7 @@ f: Literal[True, 4] = False # E: Incompatible types in assignment (expre [out] [case testLiteralInferredInCall] -from typing_extensions import Literal +from typing import Literal def f_int_lit(x: Literal[1]) -> None: pass def f_int(x: int) -> None: pass @@ -1117,7 +1107,7 @@ f_none_lit(n1) [out] [case testLiteralInferredInReturnContext] -from typing_extensions import Literal +from typing import Literal def f1() -> int: return 1 @@ -1138,8 +1128,7 @@ def f5(x: Literal[2]) -> Literal[1]: [out] [case testLiteralInferredInListContext] -from typing import List -from typing_extensions import Literal +from typing import List, Literal a: List[Literal[1]] = [1, 1, 1] b = [1, 1, 1] @@ -1183,8 +1172,7 @@ bad: List[Literal[1, 2]] = [1, 2, 3] # E: List item 2 has incompatible type "Li [case testLiteralInferredInTupleContext] # Note: most of the 'are we handling context correctly' tests should have been # handled up above, so we keep things comparatively simple for tuples and dicts. -from typing import Tuple -from typing_extensions import Literal +from typing import Literal, Tuple a: Tuple[Literal[1], Literal[2]] = (1, 2) b: Tuple[int, Literal[1, 2], Literal[3], Tuple[Literal["foo"]]] = (1, 2, 3, ("foo",)) @@ -1197,8 +1185,7 @@ reveal_type(d) # N: Revealed type is "Tuple[builtins.int, builtins.int]" [out] [case testLiteralInferredInDictContext] -from typing import Dict -from typing_extensions import Literal +from typing import Dict, Literal a = {"x": 1, "y": 2} b: Dict[str, Literal[1, 2]] = {"x": 1, "y": 2} @@ -1210,8 +1197,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[builtins.str, builtins.int] [out] [case testLiteralInferredInOverloadContextBasic] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: Literal[1]) -> str: ... @@ -1239,8 +1225,7 @@ reveal_type(func(c)) # N: Revealed type is "builtins.object" [out] [case testLiteralOverloadProhibitUnsafeOverlaps] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func1(x: Literal[1]) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types @@ -1264,8 +1249,7 @@ def func3(x): pass [out] [case testLiteralInferredInOverloadContextUnionMath] -from typing import overload, Union -from typing_extensions import Literal +from typing import overload, Literal, Union class A: pass class B: pass @@ -1310,8 +1294,7 @@ reveal_type(func(f)) # E: No overload variant of "func" matches argument type " [case testLiteralInferredInOverloadContextUnionMathOverloadingReturnsBestType] # This test is a transliteration of check-overloading::testUnionMathOverloadingReturnsBestType -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def f(x: Literal[1, 2]) -> int: ... @@ -1333,8 +1316,7 @@ reveal_type(f(z)) # N: Revealed type is "builtins.int" \ [out] [case testLiteralInferredInOverloadContextWithTypevars] -from typing import TypeVar, overload, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, overload, Union T = TypeVar('T') @@ -1385,8 +1367,7 @@ reveal_type(f4(b)) # N: Revealed type is "builtins.str" [case testLiteralInferredInOverloadContextUnionMathTrickyOverload] # This test is a transliteration of check-overloading::testUnionMathTrickyOverload1 -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def f(x: Literal['a'], y: Literal['a']) -> int: ... @@ -1408,7 +1389,7 @@ f(x, y) # E: Argument 1 to "f" has incompatible type "Literal['a', 'b']"; expec --- [case testLiteralFallbackOperatorsWorkCorrectly] -from typing_extensions import Literal +from typing import Literal a: Literal[3] b: int @@ -1440,7 +1421,7 @@ reveal_type(b) # N: Revealed type is "builtins.int" [builtins fixtures/primitives.pyi] [case testLiteralFallbackInheritedMethodsWorkCorrectly] -from typing_extensions import Literal +from typing import Literal a: Literal['foo'] b: str @@ -1452,7 +1433,7 @@ reveal_type(a.strip()) # N: Revealed type is "builtins.str" [out] [case testLiteralFallbackMethodsDoNotCoerceToLiteral] -from typing_extensions import Literal +from typing import Literal a: Literal[3] b: int @@ -1500,10 +1481,9 @@ issubclass(int, indirect.Literal[3]) # E: Cannot use issubclass() with Literal [out] [case testLiteralErrorsWhenSubclassed] - -from typing_extensions import Literal -from typing_extensions import Literal as Renamed -import typing_extensions as indirect +from typing import Literal +from typing import Literal as Renamed +import typing as indirect Alias = Literal[3] @@ -1518,9 +1498,9 @@ class Bad4(Alias): pass # E: Invalid base class "Alias" # TODO: We don't seem to correctly handle invoking types like # 'Final' and 'Protocol' as well. When fixing this, also fix # those types? -from typing_extensions import Literal -from typing_extensions import Literal as Renamed -import typing_extensions as indirect +from typing import Literal +from typing import Literal as Renamed +import typing as indirect Alias = Literal[3] @@ -1542,8 +1522,7 @@ indirect.Literal() -- [case testLiteralAndGenericsWithSimpleFunctions] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar T = TypeVar('T') def foo(x: T) -> T: pass @@ -1573,8 +1552,7 @@ expects_int(foo(foo(a))) [out] [case testLiteralAndGenericWithUnion] -from typing import TypeVar, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, Union T = TypeVar('T') def identity(x: T) -> T: return x @@ -1585,8 +1563,7 @@ b: Union[int, Literal['foo']] = identity('bar') # E: Argument 1 to "identity" h [out] [case testLiteralAndGenericsNoMatch] -from typing import TypeVar, Union, List -from typing_extensions import Literal +from typing import Literal, TypeVar, Union, List def identity(x: T) -> T: return x @@ -1602,8 +1579,7 @@ z: Bad = identity([42]) # E: List item 0 has incompatible type "Literal[42]"; e [out] [case testLiteralAndGenericsWithSimpleClasses] -from typing import TypeVar, Generic -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic T = TypeVar('T') class Wrapper(Generic[T]): @@ -1639,8 +1615,7 @@ expects_literal_wrapper(Wrapper(5)) # E: Argument 1 to "Wrapper" has incompatib [out] [case testLiteralAndGenericsRespectsUpperBound] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar TLiteral = TypeVar('TLiteral', bound=Literal[3]) TInt = TypeVar('TInt', bound=int) @@ -1679,8 +1654,7 @@ reveal_type(func2(c)) # N: Revealed type is "builtins.int" [out] [case testLiteralAndGenericsRespectsValueRestriction] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar TLiteral = TypeVar('TLiteral', Literal[3], Literal['foo']) TNormal = TypeVar('TNormal', int, str) @@ -1736,8 +1710,7 @@ reveal_type(func2(s2)) # N: Revealed type is "builtins.str" [out] [case testLiteralAndGenericsWithOverloads] -from typing import TypeVar, overload, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, overload, Union @overload def func1(x: Literal[4]) -> Literal[19]: ... @@ -1762,8 +1735,7 @@ reveal_type(func1(identity(b))) # N: Revealed type is "builtins.int" -- [case testLiteralMeets] -from typing import TypeVar, List, Callable, Union, Optional -from typing_extensions import Literal +from typing import TypeVar, List, Literal, Callable, Union, Optional a: Callable[[Literal[1]], int] b: Callable[[Literal[2]], str] @@ -1809,8 +1781,7 @@ reveal_type(unify(f6)) # N: Revealed type is "None" [out] [case testLiteralMeetsWithStrictOptional] -from typing import TypeVar, Callable, Union -from typing_extensions import Literal +from typing import TypeVar, Callable, Literal, Union a: Callable[[Literal[1]], int] b: Callable[[Literal[2]], str] @@ -1835,8 +1806,7 @@ reveal_type(unify(func)) # N: Revealed type is "Never" -- [case testLiteralIntelligentIndexingTuples] -from typing import Tuple, NamedTuple, Optional, Final -from typing_extensions import Literal +from typing import Literal, Tuple, NamedTuple, Optional, Final class A: pass class B: pass @@ -1884,7 +1854,8 @@ tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression [builtins fixtures/slice.pyi] [case testLiteralIntelligentIndexingTypedDict] -from typing_extensions import Literal, TypedDict +from typing import Literal +from typing_extensions import TypedDict class Unrelated: pass u: Unrelated @@ -1922,8 +1893,8 @@ del d[c_key] # E: TypedDict "Outer" has no key "c" [out] [case testLiteralIntelligentIndexingUsingFinal] -from typing import Tuple, NamedTuple -from typing_extensions import Literal, Final, TypedDict +from typing import Literal, Tuple, NamedTuple +from typing_extensions import Final, TypedDict int_key_good: Final = 0 int_key_bad: Final = 3 @@ -1960,8 +1931,7 @@ c[str_key_bad] # E: TypedDict "MyDict" has no key "missing [out] [case testLiteralIntelligentIndexingTupleUnions] -from typing import Tuple, NamedTuple -from typing_extensions import Literal +from typing import Literal, Tuple, NamedTuple class A: pass class B: pass @@ -1990,7 +1960,8 @@ tup2[idx_bad] # E: Tuple index out of range [out] [case testLiteralIntelligentIndexingTypedDictUnions] -from typing_extensions import Literal, Final, TypedDict +from typing import Literal +from typing_extensions import Final, TypedDict class A: pass class B: pass @@ -2041,8 +2012,8 @@ del test[bad_keys] # E: Key "a" of TypedDict "Test" cannot be delet [out] [case testLiteralIntelligentIndexingMultiTypedDict] -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, Union +from typing_extensions import TypedDict class A: pass class B: pass @@ -2080,7 +2051,8 @@ reveal_type(x.get(bad_keys, 3)) # N: Revealed type is "builtins.object" -- [case testLiteralFinalInferredAsLiteral] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final var1: Final = 1 var2: Final = "foo" @@ -2135,7 +2107,8 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [out] [case testLiteralFinalDirectInstanceTypesSupersedeInferredLiteral] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final var1: Final[int] = 1 var2: Final[str] = "foo" @@ -2190,7 +2163,8 @@ force4(f.instancevar4) [out] [case testLiteralFinalDirectLiteralTypesForceLiteral] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final var1: Final[Literal[1]] = 1 var2: Final[Literal["foo"]] = "foo" @@ -2255,7 +2229,8 @@ reveal_type(var2) # N: Revealed type is "Tuple[Literal[0]?, None]" [builtins fixtures/tuple.pyi] [case testLiteralFinalErasureInMutableDatastructures2] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final var1: Final = [] var1.append(0) @@ -2273,7 +2248,8 @@ reveal_type(var3) # N: Revealed type is "builtins.list[Literal[0]]" [builtins fixtures/list.pyi] [case testLiteralFinalMismatchCausesError] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final var1: Final[Literal[4]] = 1 # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[4]") var2: Final[Literal['bad']] = "foo" # E: Incompatible types in assignment (expression has type "Literal['foo']", variable has type "Literal['bad']") @@ -2303,8 +2279,8 @@ Foo().instancevar1 = 10 # E: Cannot assign to final attribute "instancevar1" \ [out] [case testLiteralFinalGoesOnlyOneLevelDown] -from typing import Tuple -from typing_extensions import Final, Literal +from typing import Literal, Tuple +from typing_extensions import Final a: Final = 1 b: Final = (1, 2) @@ -2321,8 +2297,8 @@ force2(b) # ok [out] [case testLiteralFinalCollectionPropagation] -from typing import List -from typing_extensions import Final, Literal +from typing import List, Literal +from typing_extensions import Final a: Final = 1 implicit = [a] @@ -2351,7 +2327,8 @@ force2(reveal_type(direct[0])) # E: Argument 1 to "force2" has incompatible ty [out] [case testLiteralFinalStringTypesPython3] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final a: Final = u"foo" b: Final = "foo" @@ -2374,8 +2351,8 @@ force_bytes(reveal_type(c)) # N: Revealed type is "Literal[b'foo']" [out] [case testLiteralFinalPropagatesThroughGenerics] -from typing import TypeVar, Generic -from typing_extensions import Final, Literal +from typing import TypeVar, Generic, Literal +from typing_extensions import Final T = TypeVar('T') @@ -2430,8 +2407,8 @@ over_literal(reveal_type(WrapperClass(var3))) # N: Revealed type is "__main__. [out] [case testLiteralFinalUsedInLiteralType] - -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final a: Final[int] = 3 b: Final = 3 c: Final[Literal[3]] = 3 @@ -2445,7 +2422,8 @@ d_wrap: Literal[4, d] # E: Parameter 2 of Literal[...] is invalid [out] [case testLiteralWithFinalPropagation] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final a: Final = 3 b: Final = a @@ -2459,7 +2437,8 @@ expect_3(c) # E: Argument 1 to "expect_3" has incompatible type "int"; expected [out] [case testLiteralWithFinalPropagationIsNotLeaking] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final final_tuple_direct: Final = (2, 3) final_tuple_indirect: Final = final_tuple_direct @@ -2489,8 +2468,7 @@ expect_2(final_set_2.pop()) # E: Argument 1 to "expect_2" has incompatible type -- [case testLiteralWithEnumsBasic] - -from typing_extensions import Literal +from typing import Literal from enum import Enum class Color(Enum): @@ -2527,7 +2505,7 @@ reveal_type(r.func()) # N: Revealed type is "builtins.int" [out] [case testLiteralWithEnumsDefinedInClass] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Wrapper: @@ -2550,7 +2528,7 @@ reveal_type(r) # N: Revealed type is "Literal[__main__.Wrapper.Color.RED]" [out] [case testLiteralWithEnumsSimilarDefinitions] -from typing_extensions import Literal +from typing import Literal import mod_a import mod_b @@ -2585,7 +2563,7 @@ class Test(Enum): [out] [case testLiteralWithEnumsDeclaredUsingCallSyntax] -from typing_extensions import Literal +from typing import Literal from enum import Enum A = Enum('A', 'FOO BAR') @@ -2606,7 +2584,7 @@ reveal_type(d) # N: Revealed type is "Literal[__main__.D.FOO]" [out] [case testLiteralWithEnumsDerivedEnums] -from typing_extensions import Literal +from typing import Literal from enum import Enum, IntEnum, IntFlag, Flag def expects_int(x: int) -> None: pass @@ -2636,7 +2614,7 @@ expects_int(d) # E: Argument 1 to "expects_int" has incompatible type "Literal[ [out] [case testLiteralWithEnumsAliases] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Test(Enum): @@ -2651,7 +2629,8 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Test.FOO]" [out] [case testLiteralUsingEnumAttributesInLiteralContexts] -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final from enum import Enum class Test1(Enum): @@ -2685,7 +2664,8 @@ expects_test2_foo(final2) [out] [case testLiteralUsingEnumAttributeNamesInLiteralContexts] -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final from enum import Enum class Test1(Enum): @@ -2720,8 +2700,7 @@ reveal_type(Test5.FOO.name) # N: Revealed type is "Literal['FOO']?" [case testLiteralBinderLastValueErased] # mypy: strict-equality - -from typing_extensions import Literal +from typing import Literal def takes_three(x: Literal[3]) -> None: ... x: object @@ -2745,7 +2724,7 @@ def test() -> None: [builtins fixtures/bool.pyi] [case testUnaryOpLiteral] -from typing_extensions import Literal +from typing import Literal a: Literal[-2] = -2 b: Literal[-1] = -1 @@ -2765,7 +2744,8 @@ z: Literal[~0] = 0 # E: Invalid type: Literal[...] cannot contain arbitrary exp [builtins fixtures/ops.pyi] [case testNegativeIntLiteralWithFinal] -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final ONE: Final = 1 x: Literal[-1] = -ONE @@ -2780,7 +2760,7 @@ if bool(): [builtins fixtures/ops.pyi] [case testAliasForEnumTypeAsLiteral] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Foo(Enum): @@ -2811,9 +2791,7 @@ assert c.a is False [case testConditionalBoolLiteralUnionNarrowing] # flags: --warn-unreachable - -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Truth: def __bool__(self) -> Literal[True]: ... @@ -2875,8 +2853,8 @@ else: [case testLiteralAndInstanceSubtyping] # https://github.com/python/mypy/issues/7399 # https://github.com/python/mypy/issues/11232 -from typing import Tuple, Union -from typing_extensions import Literal, Final +from typing import Literal, Tuple, Union +from typing_extensions import Final x: bool @@ -2918,7 +2896,7 @@ def incorrect_return2() -> Union[Tuple[Literal[True], int], Tuple[Literal[False] [builtins fixtures/bool.pyi] [case testLiteralSubtypeContext] -from typing_extensions import Literal +from typing import Literal class A: foo: Literal['bar', 'spam'] @@ -2929,8 +2907,7 @@ reveal_type(B().foo) # N: Revealed type is "Literal['spam']" [builtins fixtures/tuple.pyi] [case testLiteralSubtypeContextNested] -from typing import List -from typing_extensions import Literal +from typing import List, Literal class A: foo: List[Literal['bar', 'spam']] @@ -2941,8 +2918,7 @@ reveal_type(B().foo) # N: Revealed type is "builtins.list[Union[Literal['bar'], [builtins fixtures/tuple.pyi] [case testLiteralSubtypeContextGeneric] -from typing_extensions import Literal -from typing import Generic, List, TypeVar +from typing import Generic, List, Literal, TypeVar T = TypeVar("T", bound=str) @@ -2959,8 +2935,7 @@ reveal_type(C().word) # N: Revealed type is "Literal['word']" [builtins fixtures/tuple.pyi] [case testLiteralTernaryUnionNarrowing] -from typing_extensions import Literal -from typing import Optional +from typing import Literal, Optional SEP = Literal["a", "b"] @@ -2983,8 +2958,7 @@ class C(Base): [builtins fixtures/primitives.pyi] [case testLiteralInsideAType] -from typing_extensions import Literal -from typing import Type, Union +from typing import Literal, Type, Union x: Type[Literal[1]] # E: Type[...] can't contain "Literal[...]" y: Type[Union[Literal[1], Literal[2]]] # E: Type[...] can't contain "Union[Literal[...], Literal[...]]" diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index feb1c951ad72..2cf6e709c3b4 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1,7 +1,7 @@ [case testNarrowingParentWithStrsBasic] from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import Literal, TypedDict +from typing import Literal, NamedTuple, Tuple, Union +from typing_extensions import TypedDict class Object1: key: Literal["A"] @@ -84,8 +84,8 @@ else: [case testNarrowingParentWithEnumsBasic] from enum import Enum from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import Literal, TypedDict +from typing import Literal, NamedTuple, Tuple, Union +from typing_extensions import TypedDict class Key(Enum): A = 1 @@ -238,8 +238,7 @@ else: [case testNarrowingParentMultipleKeys] # flags: --warn-unreachable from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -271,8 +270,8 @@ else: [case testNarrowingTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, Union +from typing_extensions import TypedDict class TypedDict1(TypedDict): key: Literal['A', 'C'] @@ -298,8 +297,8 @@ else: [case testNarrowingPartialTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, Union +from typing_extensions import TypedDict class TypedDict1(TypedDict, total=False): key: Literal['A', 'C'] @@ -324,8 +323,8 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingNestedTypedDicts] -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, Union +from typing_extensions import TypedDict class A(TypedDict): key: Literal['A'] @@ -353,8 +352,7 @@ if unknown['inner']['key'] == 'C': [case testNarrowingParentWithMultipleParents] from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -398,8 +396,8 @@ else: [case testNarrowingParentWithParentMixtures] from enum import Enum -from typing import Union, NamedTuple -from typing_extensions import Literal, TypedDict +from typing import Literal, Union, NamedTuple +from typing_extensions import TypedDict class Key(Enum): A = 1 @@ -448,8 +446,7 @@ else: [case testNarrowingParentWithProperties] from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -476,8 +473,7 @@ else: [case testNarrowingParentWithAny] from enum import Enum -from typing import Union, Any -from typing_extensions import Literal +from typing import Literal, Union, Any class Key(Enum): A = 1 @@ -501,8 +497,7 @@ else: [builtins fixtures/tuple.pyi] [case testNarrowingParentsHierarchy] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union from enum import Enum class Key(Enum): @@ -580,8 +575,8 @@ else: [case testNarrowingParentsHierarchyTypedDict] # flags: --warn-unreachable -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, Union +from typing_extensions import TypedDict from enum import Enum class Key(Enum): @@ -622,8 +617,8 @@ else: [case testNarrowingParentsHierarchyTypedDictWithStr] # flags: --warn-unreachable -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, Union +from typing_extensions import TypedDict class Parent1(TypedDict): model: Model1 @@ -657,8 +652,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingExprPropagation] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class A: tag: Literal['A'] @@ -679,7 +673,8 @@ if not (abo is None or abo.tag != "B"): [case testNarrowingEqualityFlipFlop] # flags: --warn-unreachable --strict-equality -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final from enum import Enum class State(Enum): @@ -744,7 +739,8 @@ def test3(switch: FlipFlopEnum) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitStrLiteral] -from typing_extensions import Literal, Final +from typing import Literal +from typing_extensions import Final A_final: Final = "A" A_literal: Literal["A"] @@ -790,8 +786,8 @@ reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B' [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitEnumLiteral] -from typing import Union -from typing_extensions import Literal, Final +from typing import Literal, Union +from typing_extensions import Final from enum import Enum class Foo(Enum): @@ -832,8 +828,7 @@ def bar(x: Union[SingletonFoo, Foo], y: SingletonFoo) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityDisabledForCustomEquality] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union from enum import Enum class Custom: @@ -875,8 +870,7 @@ else: [case testNarrowingEqualityDisabledForCustomEqualityChain] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Custom: def __eq__(self, other: object) -> bool: return True @@ -912,8 +906,7 @@ else: [case testNarrowingUnreachableCases] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union a: Literal[1] b: Literal[1, 2] @@ -960,8 +953,7 @@ else: [case testNarrowingUnreachableCases2] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union a: Literal[1, 2, 3, 4] b: Literal[1, 2, 3, 4] @@ -999,8 +991,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingLiteralTruthiness] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union str_or_false: Union[Literal[False], str] @@ -1133,8 +1124,7 @@ reveal_type(f(B)) # N: Revealed type is "__main__.B" [case testNarrowingLiteralIdentityCheck] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union str_or_false: Union[Literal[False], str] @@ -1174,8 +1164,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanIdentityCheck] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_val: bool @@ -1196,8 +1185,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanTruthiness] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_val: bool @@ -1217,8 +1205,7 @@ reveal_type(opt_bool_val) # N: Revealed type is "Union[builtins.bool, None]" [builtins fixtures/primitives.pyi] [case testNarrowingBooleanBoolOp] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_a: bool bool_b: bool @@ -1245,8 +1232,8 @@ reveal_type(x) # N: Revealed type is "builtins.bool" [builtins fixtures/primitives.pyi] [case testNarrowingTypedDictUsingEnumLiteral] -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, Union +from typing_extensions import TypedDict from enum import Enum class E(Enum): @@ -1306,8 +1293,8 @@ def f(t: Type[T], a: A, b: B) -> None: reveal_type(b) # N: Revealed type is "__main__.B" [case testNarrowingNestedUnionOfTypedDicts] -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, Union +from typing_extensions import TypedDict class A(TypedDict): tag: Literal["A"] @@ -1909,8 +1896,7 @@ reveal_type(x1) # N: Revealed type is "Any" [builtins fixtures/len.pyi] [case testNarrowingLenExplicitLiteralTypes] -from typing import Tuple, Union -from typing_extensions import Literal +from typing import Literal, Tuple, Union VarTuple = Union[ Tuple[int], diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 1eafd462aa51..a09d72f472de 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2528,8 +2528,8 @@ tmp/unittest/suite.pyi:6: error: Name "Iterable" is not defined tmp/unittest/suite.pyi:6: note: Did you forget to import it from "typing"? (Suggestion: "from typing import Iterable") [case testNewAnalyzerNewTypeSpecialCase] -from typing import NewType -from typing_extensions import Final, Literal +from typing import Literal, NewType +from typing_extensions import Final X = NewType('X', int) @@ -2777,7 +2777,8 @@ class C: reveal_type(C.A) # N: Revealed type is "def () -> a.A" [case testNewAnalyzerFinalLiteralInferredAsLiteralWithDeferral] -from typing_extensions import Final, Literal +from typing import Literal +from typing_extensions import Final defer: Yes diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 2092f99487b0..5c878e3d7338 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -5267,8 +5267,7 @@ tmp/lib.pyi:3: error: Name "overload" is not defined main:3: note: Revealed type is "Any" [case testLiteralSubtypeOverlap] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload class MyInt(int): ... diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 294bacb1b7d9..9813df63b1f6 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2767,8 +2767,8 @@ p: P = N(lambda a, b, c: 'foo') [builtins fixtures/property.pyi] [case testLiteralsAgainstProtocols] -from typing import SupportsInt, SupportsAbs, TypeVar -from typing_extensions import Literal, Final +from typing import Literal, SupportsInt, SupportsAbs, TypeVar +from typing_extensions import Final T = TypeVar('T') def abs(x: SupportsAbs[T]) -> T: ... diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index e10d0c76c717..f9317c5ba4b1 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1442,8 +1442,7 @@ def f(value: Literal[1] | Literal[2]) -> int: [typing fixtures/typing-medium.pyi] [case testMatchSequencePatternNegativeNarrowing] -from typing import Union, Sequence, Tuple -from typing_extensions import Literal +from typing import Literal, Union, Sequence, Tuple m1: Sequence[int | str] @@ -2476,7 +2475,7 @@ def nested_in_dict(d: dict[str, Any]) -> int: [case testMatchRebindsOuterFunctionName] # flags: --warn-unreachable -from typing_extensions import Literal +from typing import Literal def x() -> tuple[Literal["test"]]: ... diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 4add107baef4..3da30eaf82cc 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -385,8 +385,7 @@ reveal_type(z2) # E: Name "z2" is not defined # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] [case testWalrusConditionalTypeBinder] -from typing import Tuple, Union -from typing_extensions import Literal +from typing import Literal, Tuple, Union class Good: @property @@ -469,8 +468,7 @@ reveal_type(x) # N: Revealed type is "Literal[0]" [case testWalrusAssignmentAndConditionScopeForProperty] # flags: --warn-unreachable - -from typing_extensions import Literal +from typing import Literal class PropertyWrapper: @property @@ -497,8 +495,7 @@ reveal_type(y) # N: Revealed type is "Literal[False]" [case testWalrusAssignmentAndConditionScopeForFunction] # flags: --warn-unreachable - -from typing_extensions import Literal +from typing import Literal def f() -> str: ... diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index a00a31863771..7fcb620c49d9 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -936,8 +936,7 @@ if last is not None: [builtins fixtures/tuple.pyi] [case testRecursiveAliasLiteral] -from typing import Tuple -from typing_extensions import Literal +from typing import Literal, Tuple NotFilter = Tuple[Literal["not"], "NotFilter"] n: NotFilter diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 814007f0e144..1ac5924262b3 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -784,8 +784,7 @@ reveal_type(x) # N: Revealed type is "__main__.SubP[Any]" y: SubP[str] = SubP(use_str=True) [file lib.pyi] -from typing import TypeVar, Generic, overload, Tuple -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic, overload, Tuple T = TypeVar('T') class P(Generic[T]): @@ -809,8 +808,7 @@ xx = PFallBack(t) # E: Need type annotation for "xx" yy = PFallBackAny(t) # OK [file lib.pyi] -from typing import TypeVar, Generic, overload, Tuple, Any -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic, overload, Tuple, Any class PFallBack(Generic[T]): @overload diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 1650a6948c93..9f77100863be 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -1550,7 +1550,7 @@ class InvalidReturn3: [builtins fixtures/bool.pyi] [case testWithStmtBoolExitReturnOkay] -from typing_extensions import Literal +from typing import Literal class GoodReturn1: def __exit__(self, x, y, z) -> bool: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index c5915176a5ff..21832a0db079 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1050,8 +1050,8 @@ class C(Generic[T]): [builtins fixtures/classmethod.pyi] [case testRecursiveAliasTuple] -from typing_extensions import Literal, TypeAlias -from typing import Tuple, Union +from typing_extensions import TypeAlias +from typing import Literal, Tuple, Union Expr: TypeAlias = Union[ Tuple[Literal[123], int], diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index feea5e2dff0f..d03ea2d77e19 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -897,8 +897,8 @@ reveal_type(u(c, m_s_a)) # N: Revealed type is "Union[typing.Mapping[builtins.st [typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionUnambiguousCase] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, Any, cast +from typing_extensions import TypedDict A = TypedDict('A', {'@type': Literal['a-type'], 'a': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'b': int}) @@ -908,8 +908,8 @@ reveal_type(c) # N: Revealed type is "Union[TypedDict('__main__.A', {'@type': Li [builtins fixtures/dict.pyi] [case testTypedDictUnionAmbiguousCaseBothMatch] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, Any, cast +from typing_extensions import TypedDict A = TypedDict('A', {'@type': Literal['a-type'], 'value': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': str}) @@ -918,8 +918,8 @@ c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} [builtins fixtures/dict.pyi] [case testTypedDictUnionAmbiguousCaseNoMatch] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, Any, cast +from typing_extensions import TypedDict A = TypedDict('A', {'@type': Literal['a-type'], 'value': int}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': int}) diff --git a/test-data/unit/check-union-error-syntax.test b/test-data/unit/check-union-error-syntax.test index 2928cc312709..3c541173a891 100644 --- a/test-data/unit/check-union-error-syntax.test +++ b/test-data/unit/check-union-error-syntax.test @@ -30,32 +30,28 @@ x = 3 # E: Incompatible types in assignment (expression has type "int", variable [case testLiteralOrErrorSyntax] # flags: --python-version 3.10 --no-force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], Literal[2], str] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1, 2] | str") [builtins fixtures/tuple.pyi] [case testLiteralUnionErrorSyntax] # flags: --python-version 3.10 --force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], Literal[2], str] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Union[str, Literal[1, 2]]") [builtins fixtures/tuple.pyi] [case testLiteralOrNoneErrorSyntax] # flags: --python-version 3.10 --no-force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], None] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1] | None") [builtins fixtures/tuple.pyi] [case testLiteralOptionalErrorSyntax] # flags: --python-version 3.10 --force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], None] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Optional[Literal[1]]") [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index fcf679fff401..6250374ccbea 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -45,9 +45,10 @@ reveal_type(f) # N: Revealed type is "def (x: Union[__main__.A, __main__.B, __m [case testUnionOrSyntaxWithLiteral] # flags: --python-version 3.10 -from typing_extensions import Literal +from typing import Literal reveal_type(Literal[4] | str) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testUnionOrSyntaxWithBadOperator] # flags: --python-version 3.10 diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index e6818ab5c3c7..a40aa21ff26a 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1013,8 +1013,7 @@ def foo(x: T) -> T: [case testUnreachableFlagContextManagersNoSuppress] # flags: --warn-unreachable from contextlib import contextmanager -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Literal, Optional, Iterator, Any class DoesNotSuppress1: def __enter__(self) -> int: ... def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... @@ -1078,8 +1077,7 @@ def f_no_suppress_5() -> int: [case testUnreachableFlagContextManagersSuppressed] # flags: --warn-unreachable from contextlib import contextmanager -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Optional, Iterator, Literal, Any class DoesNotSuppress: def __enter__(self) -> int: ... @@ -1125,8 +1123,7 @@ def f_mix() -> int: # E: Missing return statement [case testUnreachableFlagContextManagersSuppressedNoStrictOptional] # flags: --warn-unreachable --no-strict-optional from contextlib import contextmanager -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Optional, Iterator, Literal, Any class DoesNotSuppress1: def __enter__(self) -> int: ... @@ -1167,8 +1164,7 @@ def f_suppress() -> int: # E: Missing return statement [case testUnreachableFlagContextAsyncManagersNoSuppress] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class DoesNotSuppress1: async def __aenter__(self) -> int: ... @@ -1233,8 +1229,7 @@ async def f_no_suppress_5() -> int: [case testUnreachableFlagContextAsyncManagersSuppressed] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class DoesNotSuppress: async def __aenter__(self) -> int: ... @@ -1280,8 +1275,7 @@ async def f_mix() -> int: # E: Missing return statement [case testUnreachableFlagContextAsyncManagersAbnormal] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class RegularManager: def __enter__(self) -> int: ... @@ -1380,7 +1374,7 @@ def f(t: T) -> None: [case testUnreachableLiteral] # flags: --warn-unreachable -from typing_extensions import Literal +from typing import Literal def nope() -> Literal[False]: ... @@ -1391,7 +1385,7 @@ def f() -> None: [case testUnreachableLiteralFrom__bool__] # flags: --warn-unreachable -from typing_extensions import Literal +from typing import Literal class Truth: def __bool__(self) -> Literal[True]: ... diff --git a/test-data/unit/deps-expressions.test b/test-data/unit/deps-expressions.test index ff8c875b66f0..fd5a4fe0ff9f 100644 --- a/test-data/unit/deps-expressions.test +++ b/test-data/unit/deps-expressions.test @@ -420,7 +420,7 @@ def g() -> None: -> m.g [case testLiteralDepsExpr] -from typing_extensions import Literal +from typing import Literal Alias = Literal[1] diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 70178b0366ba..1f1987183fe4 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1153,7 +1153,7 @@ __main__.Diff __main__.Diff.x [case testLiteralTriggersVar] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 1 y = 1 @@ -1171,7 +1171,7 @@ class C: self.same_instance: Literal[1] = 1 [file next.py] -from typing_extensions import Literal +from typing import Literal x = 1 y: Literal[1] = 1 @@ -1200,7 +1200,7 @@ __main__.y __main__.z [case testLiteralTriggersFunctions] -from typing_extensions import Literal +from typing import Literal def function_1() -> int: pass def function_2() -> Literal[1]: pass @@ -1264,7 +1264,7 @@ class C: def staticmethod_same_2(x: Literal[1]) -> None: pass [file next.py] -from typing_extensions import Literal +from typing import Literal def function_1() -> Literal[1]: pass def function_2() -> int: pass @@ -1354,7 +1354,7 @@ __main__.function_5 __main__.function_6 [case testLiteralTriggersProperty] -from typing_extensions import Literal +from typing import Literal class C: @property @@ -1367,7 +1367,7 @@ class C: def same(self) -> Literal[1]: pass [file next.py] -from typing_extensions import Literal +from typing import Literal class C: @property @@ -1384,8 +1384,7 @@ __main__.C.p1 __main__.C.p2 [case testLiteralsTriggersOverload] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: str) -> str: ... @@ -1417,8 +1416,7 @@ class C: pass [file next.py] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: str) -> str: ... @@ -1454,10 +1452,10 @@ __main__.C.method __main__.func [case testUnionOfLiterals] -from typing_extensions import Literal +from typing import Literal x: Literal[1, '2'] [file next.py] -from typing_extensions import Literal +from typing import Literal x: Literal[1, 2] [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 98e72e7b3be7..496178c40e8c 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -8715,10 +8715,10 @@ reveal_type(mod.x) [file mod.py] x = 1 [file mod.py.2] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 1 [file mod.py.3] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 2 [builtins fixtures/tuple.pyi] [out] @@ -8735,10 +8735,10 @@ foo(3) [file mod.py] def foo(x: int) -> None: pass [file mod.py.2] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> None: pass [file mod.py.3] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[4]) -> None: pass [builtins fixtures/tuple.pyi] [out] @@ -8752,10 +8752,10 @@ a: Alias = 1 [file mod.py] Alias = int [file mod.py.2] -from typing_extensions import Literal +from typing import Literal Alias = Literal[1] [file mod.py.3] -from typing_extensions import Literal +from typing import Literal Alias = Literal[2] [builtins fixtures/tuple.pyi] [out] @@ -8767,16 +8767,14 @@ main:2: error: Incompatible types in assignment (expression has type "Literal[1] from mod import foo reveal_type(foo(4)) [file mod.py] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def foo(x: int) -> str: ... @overload def foo(x: Literal['bar']) -> int: ... def foo(x): pass [file mod.py.2] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def foo(x: Literal[4]) -> Literal['foo']: ... @overload @@ -8792,7 +8790,7 @@ main:2: note: Revealed type is "Literal['foo']" [case testLiteralFineGrainedChainedDefinitions] from mod1 import foo -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(foo) [file mod1.py] @@ -8801,10 +8799,10 @@ foo = bar [file mod2.py] from mod3 import qux as bar [file mod3.py] -from typing_extensions import Literal +from typing import Literal qux: Literal[3] [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal qux: Literal[4] [builtins fixtures/tuple.pyi] [out] @@ -8813,7 +8811,7 @@ main:4: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expe [case testLiteralFineGrainedChainedAliases] from mod1 import Alias1 -from typing_extensions import Literal +from typing import Literal x: Alias1 def expect_3(x: Literal[3]) -> None: pass expect_3(x) @@ -8824,10 +8822,10 @@ Alias1 = Alias2 from mod3 import Alias3 Alias2 = Alias3 [file mod3.py] -from typing_extensions import Literal +from typing import Literal Alias3 = Literal[3] [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal Alias3 = Literal[4] [builtins fixtures/tuple.pyi] [out] @@ -8836,7 +8834,7 @@ main:5: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expe [case testLiteralFineGrainedChainedFunctionDefinitions] from mod1 import func1 -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(func1()) [file mod1.py] @@ -8845,10 +8843,10 @@ from mod2 import func2 as func1 from mod3 import func3 func2 = func3 [file mod3.py] -from typing_extensions import Literal +from typing import Literal def func3() -> Literal[3]: pass [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal def func3() -> Literal[4]: pass [builtins fixtures/tuple.pyi] [out] @@ -8867,7 +8865,7 @@ foo = func(bar) [file mod2.py] bar = 3 [file mod2.py.2] -from typing_extensions import Literal +from typing import Literal bar: Literal[3] = 3 [builtins fixtures/tuple.pyi] [out] @@ -8877,11 +8875,11 @@ main:2: note: Revealed type is "Literal[3]" [case testLiteralFineGrainedChainedViaFinal] from mod1 import foo -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(foo) [file mod1.py] -from typing_extensions import Final +from typing import Final from mod2 import bar foo: Final = bar [file mod2.py] @@ -8909,13 +8907,13 @@ reveal_type(foo) from mod2 import bar foo = bar() [file mod2.py] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal["foo"]: pass [file mod2.py.2] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal[u"foo"]: pass [file mod2.py.3] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal[b"foo"]: pass [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/fixtures/set.pyi b/test-data/unit/fixtures/set.pyi index 71d3bd2eee18..f757679a95f4 100644 --- a/test-data/unit/fixtures/set.pyi +++ b/test-data/unit/fixtures/set.pyi @@ -13,6 +13,7 @@ class tuple(Generic[T]): pass class function: pass class int: pass +class float: pass class str: pass class bool: pass class ellipsis: pass diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index 3cb164140883..86d542a918ee 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -23,6 +23,7 @@ NamedTuple = 0 Type = 0 ClassVar = 0 Final = 0 +Literal = 0 NoReturn = 0 Never = 0 NewType = 0 diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index eea6fe505b49..8c806623403b 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -1514,11 +1514,11 @@ TypeInfo<0>( [case testLiteralMerge] import target [file target.py] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> Literal['a']: pass bar: Literal[4] = 4 [file target.py.next] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal['3']) -> Literal['b']: pass bar: Literal[5] = 5 [builtins fixtures/tuple.pyi] @@ -1528,7 +1528,7 @@ MypyFile:1<0>( Import:1(target)) MypyFile:1<1>( tmp/target.py - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2<2>( foo Args( @@ -1546,7 +1546,7 @@ MypyFile:1<0>( Import:1(target)) MypyFile:1<1>( tmp/target.py - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2<2>( foo Args( diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 48d6ee04b514..0e0e2b1f344d 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1651,8 +1651,8 @@ f: m.F reveal_type(f) [file m.pyi] -from typing import Type, Callable -from typing_extensions import Literal, TypeAlias +from typing import Type, Callable, Literal +from typing_extensions import TypeAlias Foo = Literal[1, 2] reveal_type(Foo) diff --git a/test-data/unit/semanal-literal.test b/test-data/unit/semanal-literal.test index 4c100add6ec0..53191f692c8c 100644 --- a/test-data/unit/semanal-literal.test +++ b/test-data/unit/semanal-literal.test @@ -1,9 +1,9 @@ [case testLiteralSemanalBasicAssignment] -from typing_extensions import Literal +from typing import Literal foo: Literal[3] [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) AssignmentStmt:2( NameExpr(foo [__main__.foo]) TempNode:2( @@ -11,12 +11,12 @@ MypyFile:1( Literal[3])) [case testLiteralSemanalInFunction] -from typing_extensions import Literal +from typing import Literal def foo(a: Literal[1], b: Literal[" foo "]) -> Literal[True]: pass [builtins fixtures/bool.pyi] [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2( foo Args( From f74f81819e1dc523d031d991adc9632d846aa553 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 10 Feb 2025 05:26:49 +0100 Subject: [PATCH 1130/1617] Add option to selectively disable deprecation warnings (#18641) Suggested in https://github.com/python/mypy/pull/18192#issuecomment-2512775035 Fixes https://github.com/python/mypy/issues/18435 --- docs/source/command_line.rst | 20 +++++++++++ docs/source/config_file.rst | 10 ++++++ docs/source/error_code_list2.rst | 2 ++ mypy/checker.py | 4 +++ mypy/main.py | 9 +++++ mypy/options.py | 4 +++ mypy/typeanal.py | 4 +++ test-data/unit/check-deprecated.test | 51 ++++++++++++++++++++++++++++ 8 files changed, 104 insertions(+) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 3fee6431f8cd..7c469f6d5138 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -556,6 +556,26 @@ potentially problematic or redundant in some way. notes, causing mypy to eventually finish with a zero exit code. Features are considered deprecated when decorated with ``warnings.deprecated``. +.. option:: --deprecated-calls-exclude + + This flag allows to selectively disable :ref:`deprecated` warnings + for functions and methods defined in specific packages, modules, or classes. + Note that each exclude entry acts as a prefix. For example (assuming ``foo.A.func`` is deprecated): + + .. code-block:: python + + # mypy --enable-error-code deprecated + # --deprecated-calls-exclude=foo.A + import foo + + foo.A().func() # OK, the deprecated warning is ignored + + # file foo.py + from typing_extensions import deprecated + class A: + @deprecated("Use A.func2 instead") + def func(self): pass + .. _miscellaneous-strictness-flags: Miscellaneous strictness flags diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index e06303777ea9..57e88346faa9 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -666,6 +666,16 @@ section of the command line docs. Shows a warning when encountering any code inferred to be unreachable or redundant after performing type analysis. +.. confval:: deprecated_calls_exclude + + :type: comma-separated list of strings + + Selectively excludes functions and methods defined in specific packages, + modules, and classes from the :ref:`deprecated` error code. + This also applies to all submodules of packages (i.e. everything inside + a given prefix). Note, this option does not support per-file configuration, + the exclusions list is defined globally for all your code. + Suppressing errors ****************** diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 508574b36e09..dfe2e30874f7 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -243,6 +243,8 @@ locally. Features are considered deprecated when decorated with ``warnings.depr specified in `PEP 702 `_. Use the :option:`--report-deprecated-as-note ` option to turn all such errors into notes. +Use :option:`--deprecated-calls-exclude ` to hide warnings +for specific functions, classes and packages. .. note:: diff --git a/mypy/checker.py b/mypy/checker.py index 54ee53986f53..30d97d617e7e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7865,6 +7865,10 @@ def warn_deprecated(self, node: Node | None, context: Context) -> None: isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) and ((deprecated := node.deprecated) is not None) and not self.is_typeshed_stub + and not any( + node.fullname == p or node.fullname.startswith(f"{p}.") + for p in self.options.deprecated_calls_exclude + ) ): warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail warn(deprecated, context, code=codes.DEPRECATED) diff --git a/mypy/main.py b/mypy/main.py index 79147f8bf0bd..fb63cd865129 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -826,6 +826,14 @@ def add_invertible_flag( help="Report importing or using deprecated features as notes instead of errors", group=lint_group, ) + lint_group.add_argument( + "--deprecated-calls-exclude", + metavar="MODULE", + action="append", + default=[], + help="Disable deprecated warnings for functions/methods coming" + " from specific package, module, or class", + ) # Note: this group is intentionally added here even though we don't add # --strict to this group near the end. @@ -1369,6 +1377,7 @@ def set_strict_flags() -> None: ) validate_package_allow_list(options.untyped_calls_exclude) + validate_package_allow_list(options.deprecated_calls_exclude) options.process_error_codes(error_callback=parser.error) options.process_incomplete_features(error_callback=parser.error, warning_callback=print) diff --git a/mypy/options.py b/mypy/options.py index 4e5273774f26..d40a08107a7a 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -182,6 +182,10 @@ def __init__(self) -> None: # Report importing or using deprecated features as errors instead of notes. self.report_deprecated_as_note = False + # Allow deprecated calls from function coming from modules/packages + # in this list (each item effectively acts as a prefix match) + self.deprecated_calls_exclude: list[str] = [] + # Warn about unused '# type: ignore' comments self.warn_unused_ignores = False diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 06e3aef33d7f..9208630937e7 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -823,6 +823,10 @@ def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None: (deprecated := info.deprecated) and not self.is_typeshed_stub and not (self.api.type and (self.api.type.fullname == info.fullname)) + and not any( + info.fullname == p or info.fullname.startswith(f"{p}.") + for p in self.options.deprecated_calls_exclude + ) ): for imp in self.cur_mod_node.imports: if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names): diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index df9695332a5b..c6953122d788 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -797,5 +797,56 @@ def g(x: int) -> int: ... @overload def g(x: str) -> str: ... def g(x: Union[int, str]) -> Union[int, str]: ... +[builtins fixtures/tuple.pyi] + +[case testDeprecatedExclude] +# flags: --enable-error-code=deprecated --deprecated-calls-exclude=m.C --deprecated-calls-exclude=m.D --deprecated-calls-exclude=m.E.f --deprecated-calls-exclude=m.E.g --deprecated-calls-exclude=m.E.__add__ +from m import C, D, E + +[file m.py] +from typing import Union, overload +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: + def __init__(self) -> None: ... + +c: C +C() +C.__init__(c) + +class D: + @deprecated("use D.g instead") + def f(self) -> None: ... + + def g(self) -> None: ... + +D.f +D().f +D().f() + +class E: + @overload + def f(self, x: int) -> int: ... + @overload + def f(self, x: str) -> str: ... + @deprecated("use E.f2 instead") + def f(self, x: Union[int, str]) -> Union[int, str]: ... + + @deprecated("use E.h instead") + def g(self) -> None: ... + + @overload + @deprecated("no A + int") + def __add__(self, v: int) -> None: ... + @overload + def __add__(self, v: str) -> None: ... + def __add__(self, v: Union[int, str]) -> None: ... + +E().f(1) +E().f("x") +e = E() +e.g() +e + 1 [builtins fixtures/tuple.pyi] From f946af49b239dc33348f52d9acc18ae6a82c5d16 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 10 Feb 2025 09:59:58 +0000 Subject: [PATCH 1131/1617] Don't assume that for loop body index variable is always set (#18631) Fixes #18629. Fixes #16321. Fixes #8637. --- mypy/checker.py | 18 +++++++++++++++--- test-data/unit/check-inference.test | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 30d97d617e7e..36c673a1c330 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -585,12 +585,12 @@ def accept_loop( else_body: Statement | None = None, *, exit_condition: Expression | None = None, + on_enter_body: Callable[[], None] | None = None, ) -> None: """Repeatedly type check a loop body until the frame doesn't change.""" # The outer frame accumulates the results of all iterations: with self.binder.frame_context(can_skip=False, conditional_frame=True): - # Check for potential decreases in the number of partial types so as not to stop the # iteration too early: partials_old = sum(len(pts.map) for pts in self.partial_types) @@ -603,6 +603,9 @@ def accept_loop( while True: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): + if on_enter_body is not None: + on_enter_body() + self.accept(body) partials_new = sum(len(pts.map) for pts in self.partial_types) if (partials_new == partials_old) and not self.binder.last_pop_changed: @@ -615,6 +618,9 @@ def accept_loop( self.options.enabled_error_codes.add(codes.REDUNDANT_EXPR) if warn_unreachable or warn_redundant: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): + if on_enter_body is not None: + on_enter_body() + self.accept(body) # If exit_condition is set, assume it must be False on exit from the loop: @@ -5126,8 +5132,14 @@ def visit_for_stmt(self, s: ForStmt) -> None: iterator_type, item_type = self.analyze_iterable_item_type(s.expr) s.inferred_item_type = item_type s.inferred_iterator_type = iterator_type - self.analyze_index_variables(s.index, item_type, s.index_type is None, s) - self.accept_loop(s.body, s.else_body) + + self.accept_loop( + s.body, + s.else_body, + on_enter_body=lambda: self.analyze_index_variables( + s.index, item_type, s.index_type is None, s + ), + ) def analyze_async_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: """Analyse async iterable expression and return iterator and iterator item types.""" diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 473a3f9d3df6..d80181047dc8 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3894,3 +3894,21 @@ foo = [ ] reveal_type(foo) # N: Revealed type is "builtins.list[Tuple[builtins.int, typing.Sequence[builtins.str]]]" [builtins fixtures/tuple.pyi] + +[case testForLoopIndexVaribaleNarrowing1] +# flags: --local-partial-types +from typing import Union +x: Union[int, str] +x = "abc" +for x in list[int](): + reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testForLoopIndexVaribaleNarrowing2] +# flags: --enable-error-code=redundant-expr +from typing import Union +x: Union[int, str] +x = "abc" +for x in list[int](): + reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" From 64a5cccc84b487dfafaf520e6e9f5c6979cbc582 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:54:16 +0100 Subject: [PATCH 1132/1617] Update TypedDict imports in tests (2) (#18646) Followup to #18528 Replace most `typing_extensions.TypedDict` imports in tests with `typing.TypedDict`. --- mypyc/test-data/irbuild-dict.test | 4 +- mypyc/test-data/run-classes.test | 3 +- mypyc/test-data/run-dicts.test | 4 +- mypyc/test-data/run-functions.test | 4 +- mypyc/test-data/run-misc.test | 8 +- test-data/unit/check-basic.test | 9 +- test-data/unit/check-dataclasses.test | 3 +- test-data/unit/check-errorcodes.test | 11 ++- test-data/unit/check-formatting.test | 3 +- test-data/unit/check-functions.test | 7 +- test-data/unit/check-functools.test | 5 +- test-data/unit/check-incremental.test | 25 +++-- test-data/unit/check-inference.test | 3 +- test-data/unit/check-literal.test | 14 ++- test-data/unit/check-modules.test | 2 +- test-data/unit/check-narrowing.test | 51 +++++----- test-data/unit/check-newsemanal.test | 6 +- test-data/unit/check-overloading.test | 4 +- test-data/unit/check-python38.test | 4 +- test-data/unit/check-recursive-types.test | 4 +- test-data/unit/check-typeddict.test | 48 +++++----- test-data/unit/check-typevar-tuple.test | 28 +++--- test-data/unit/check-typevar-values.test | 18 ++-- test-data/unit/check-varargs.test | 92 +++++++++++++------ .../unit/fine-grained-follow-imports.test | 4 +- test-data/unit/fine-grained-suggest.test | 3 +- test-data/unit/fine-grained.test | 18 ++-- test-data/unit/fixtures/for.pyi | 1 + 28 files changed, 225 insertions(+), 161 deletions(-) diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 258bf953b09c..a71f5aa2d8a2 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -218,8 +218,7 @@ L0: return r2 [case testDictIterationMethods] -from typing import Dict, Union -from typing_extensions import TypedDict +from typing import Dict, TypedDict, Union class Person(TypedDict): name: str @@ -239,6 +238,7 @@ def typeddict(d: Person) -> None: for k, v in d.items(): if k == "name": name = v +[typing fixtures/typing-full.pyi] [out] def print_dict_methods(d1, d2): d1, d2 :: dict diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 60abf76be1e6..28e5b74a254b 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -78,7 +78,7 @@ assert hasattr(c, 'x') [case testTypedDictWithFields] import collections -from typing_extensions import TypedDict +from typing import TypedDict class C(TypedDict): x: collections.deque [file driver.py] @@ -86,6 +86,7 @@ from native import C from collections import deque print(C.__annotations__["x"] is deque) +[typing fixtures/typing-full.pyi] [out] True diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index d4f5b945309e..2a3be188ad00 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -95,8 +95,7 @@ assert get_content_set(od) == ({1, 3}, {2, 4}, {(1, 2), (3, 4)}) [typing fixtures/typing-full.pyi] [case testDictIterationMethodsRun] -from typing import Dict, Union -from typing_extensions import TypedDict +from typing import Dict, TypedDict, Union class ExtensionDict(TypedDict): python: str @@ -188,6 +187,7 @@ except TypeError as e: assert str(e) == "a tuple of length 2 expected" else: assert False +[typing fixtures/typing-full.pyi] [out] 1 3 diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index ac4894bad304..91a6103e31ae 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -1243,7 +1243,8 @@ def g() -> None: g() [case testUnpackKwargsCompiled] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -1254,6 +1255,7 @@ def foo(**kwargs: Unpack[Person]) -> None: # This is not really supported yet, just test that we behave reasonably. foo(name='Jennifer', age=38) +[typing fixtures/typing-full.pyi] [out] Jennifer diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index a3ebc3923003..94d8ffb41e4e 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -612,8 +612,8 @@ for a in sorted(s): 9 8 72 [case testDummyTypes] -from typing import Tuple, List, Dict, Literal, NamedTuple -from typing_extensions import TypedDict, NewType +from typing import Tuple, List, Dict, Literal, NamedTuple, TypedDict +from typing_extensions import NewType class A: pass @@ -664,6 +664,7 @@ except Exception as e: print(type(e).__name__) # ... but not that it is a valid literal value take_literal(10) +[typing fixtures/typing-full.pyi] [out] Lol(a=1, b=[]) 10 @@ -675,7 +676,7 @@ TypeError 10 [case testClassBasedTypedDict] -from typing_extensions import TypedDict +from typing import TypedDict class TD(TypedDict): a: int @@ -707,6 +708,7 @@ def test_non_total_typed_dict() -> None: d4 = TD4(a=1, b=2, c=3, d=4) assert d3['c'] == 3 assert d4['d'] == 4 +[typing fixtures/typing-full.pyi] [case testClassBasedNamedTuple] from typing import NamedTuple diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 4096f738bddf..375886733f3a 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -396,8 +396,7 @@ y = x # E: Incompatible types in assignment (expression has type "Dict[str, int] import b [file a.py] -from typing import NamedTuple -from typing_extensions import TypedDict +from typing import NamedTuple, TypedDict from enum import Enum class A: pass N = NamedTuple('N', [('x', int)]) @@ -406,8 +405,8 @@ class B(Enum): b = 10 [file b.py] -from typing import List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type -from typing_extensions import Final, TypedDict +from typing import List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type, TypedDict +from typing_extensions import Final from enum import Enum import a class A: pass @@ -464,8 +463,8 @@ def typeddict() -> Sequence[D]: a = (a.A(), A()) a.x # E: "Tuple[a.A, b.A]" has no attribute "x" - [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testReturnAnyFromFunctionDeclaredToReturnObject] # flags: --warn-return-any diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 9109b2b7c36d..887a9052d0b9 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1408,7 +1408,7 @@ class C: [case testDataclassFieldWithTypedDictUnpacking] from dataclasses import dataclass, field -from typing_extensions import TypedDict +from typing import TypedDict class FieldKwargs(TypedDict): repr: bool @@ -1421,6 +1421,7 @@ class Foo: reveal_type(Foo(bar=1.5)) # N: Revealed type is "__main__.Foo" [builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDataclassWithSlotsArg] # flags: --python-version 3.10 diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index af311b5334b0..45b9dced046d 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -449,7 +449,7 @@ y: Dict[int, int] = {1: ''} # E: Dict entry 0 has incompatible type "int": "str [builtins fixtures/dict.pyi] [case testErrorCodeTypedDict] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int class E(TypedDict): @@ -472,7 +472,7 @@ a['y'] # E: TypedDict "D" has no key "y" [typeddict-item] [typing fixtures/typing-typeddict.pyi] [case testErrorCodeTypedDictNoteIgnore] -from typing_extensions import TypedDict +from typing import TypedDict class A(TypedDict): one_commonpart: int two_commonparts: int @@ -484,7 +484,7 @@ not_exist = a['not_exist'] # type: ignore[typeddict-item] [typing fixtures/typing-typeddict.pyi] [case testErrorCodeTypedDictSubCodeIgnore] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int d: D = {'x': 1, 'y': 2} # type: ignore[typeddict-item] @@ -831,10 +831,11 @@ Foo = NamedTuple("Bar", []) # E: First argument to namedtuple() should be "Foo" [builtins fixtures/tuple.pyi] [case testTypedDictNameMismatch] -from typing_extensions import TypedDict +from typing import TypedDict Foo = TypedDict("Bar", {}) # E: First argument "Bar" to TypedDict() does not match variable name "Foo" [name-match] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTruthyBool] # flags: --enable-error-code truthy-bool --no-local-partial-types @@ -993,7 +994,7 @@ reveal_type(t) # N: Revealed type is "__main__.TensorType" [builtins fixtures/tuple.pyi] [case testNoteAboutChangedTypedDictErrorCode] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index 83ae9b526f22..62d1f0923540 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -542,7 +542,7 @@ x: Any [builtins fixtures/primitives.pyi] [case testFormatCallAccessorsIndices] -from typing_extensions import TypedDict +from typing import TypedDict class User(TypedDict): id: int @@ -554,6 +554,7 @@ u: User def f() -> str: ... '{[f()]}'.format(u) # E: Invalid index expression in format field accessor "[f()]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFormatCallFlags] from typing import Union diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 92a74a717893..a0a6e9d60920 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3399,8 +3399,7 @@ class Bar(Foo): [builtins fixtures/property.pyi] [case testNoCrashOnUnpackOverride] -from typing import Unpack -from typing_extensions import TypedDict +from typing import TypedDict, Unpack class Params(TypedDict): x: int @@ -3419,9 +3418,9 @@ class C(B): # N: def meth(*, x: int, y: str) -> None \ # N: Subclass: \ # N: def meth(*, x: int, y: int) -> None - ... -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testOverrideErrorLocationNamed] class B: diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 5bdc3ce7a352..53ddc96cbe19 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -433,7 +433,8 @@ def foo(cls3: Type[B[T]]): [builtins fixtures/tuple.pyi] [case testFunctoolsPartialTypedDictUnpack] -from typing_extensions import TypedDict, Unpack +from typing import TypedDict +from typing_extensions import Unpack from functools import partial class D1(TypedDict, total=False): @@ -509,8 +510,8 @@ def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None: partial(fn4, **d1)(a2="asdf") partial(fn4, **d1)(**a2good) partial(fn4, **d1)(**a2bad) # E: Argument "a2" to "fn4" has incompatible type "int"; expected "str" - [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFunctoolsPartialNestedGeneric] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 6b9a09435bcb..0c7e67e5444d 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5740,8 +5740,8 @@ import b b.xyz [file b.py] -from typing import NamedTuple, NewType -from typing_extensions import TypedDict, TypeAlias +from typing import NamedTuple, NewType, TypedDict +from typing_extensions import TypeAlias from enum import Enum from dataclasses import dataclass @@ -5777,6 +5777,7 @@ class C: n: N = N(NT1(c=1)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out2] tmp/a.py:2: error: "object" has no attribute "xyz" @@ -6079,7 +6080,8 @@ tmp/b.py:3: error: Incompatible types in assignment (expression has type "int", [case testUnpackKwargsSerialize] import m [file lib.py] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -6095,6 +6097,7 @@ foo(name='Jennifer', age=38) from lib import foo foo(name='Jennifer', age="38") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] tmp/m.py:2: error: Argument "age" to "foo" has incompatible type "str"; expected "int" @@ -6276,7 +6279,7 @@ import f # modify [file f.py] -from typing_extensions import TypedDict +from typing import TypedDict import c class D(TypedDict): x: c.C @@ -6297,6 +6300,7 @@ class C: ... class C: ... [file pb1.py.2] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] [out3] @@ -6464,8 +6468,7 @@ y: int = x [case testGenericTypedDictWithError] import b [file a.py] -from typing import Generic, TypeVar -from typing_extensions import TypedDict +from typing import Generic, TypeVar, TypedDict TValue = TypeVar("TValue") class Dict(TypedDict, Generic[TValue]): @@ -6487,6 +6490,7 @@ def f(d: Dict[TValue]) -> TValue: def g(d: Dict[TValue]) -> TValue: return d["y"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/b.py:6: error: TypedDict "a.Dict[TValue]" has no key "x" [out2] @@ -6588,9 +6592,10 @@ import counts import counts # touch [file counts.py] -from typing_extensions import TypedDict +from typing import TypedDict Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNoIncrementalCrashOnInvalidTypedDictFunc] import m @@ -6600,10 +6605,11 @@ import counts import counts # touch [file counts.py] -from typing_extensions import TypedDict +from typing import TypedDict def test() -> None: Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNoIncrementalCrashOnTypedDictMethod] import a @@ -6615,13 +6621,14 @@ from b import C x: C reveal_type(x.h) [file b.py] -from typing_extensions import TypedDict +from typing import TypedDict class C: def __init__(self) -> None: self.h: Hidden class Hidden(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] tmp/a.py:3: note: Revealed type is "TypedDict('b.C.Hidden@5', {'x': builtins.int})" diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index d80181047dc8..cb0b11bf013c 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1239,7 +1239,7 @@ class B: pass [out] [case testForStatementIndexNarrowing] -from typing_extensions import TypedDict +from typing import TypedDict class X(TypedDict): hourly: int @@ -1266,6 +1266,7 @@ for b in ("hourly", "daily"): reveal_type(b) # N: Revealed type is "builtins.str" reveal_type(b.upper()) # N: Revealed type is "builtins.str" [builtins fixtures/for.pyi] +[typing fixtures/typing-full.pyi] -- Regression tests diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 78ab872bbc0f..befcb3970299 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1854,8 +1854,7 @@ tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression [builtins fixtures/slice.pyi] [case testLiteralIntelligentIndexingTypedDict] -from typing import Literal -from typing_extensions import TypedDict +from typing import Literal, TypedDict class Unrelated: pass u: Unrelated @@ -1893,8 +1892,8 @@ del d[c_key] # E: TypedDict "Outer" has no key "c" [out] [case testLiteralIntelligentIndexingUsingFinal] -from typing import Literal, Tuple, NamedTuple -from typing_extensions import Final, TypedDict +from typing import Literal, Tuple, NamedTuple, TypedDict +from typing_extensions import Final int_key_good: Final = 0 int_key_bad: Final = 3 @@ -1960,8 +1959,8 @@ tup2[idx_bad] # E: Tuple index out of range [out] [case testLiteralIntelligentIndexingTypedDictUnions] -from typing import Literal -from typing_extensions import Final, TypedDict +from typing import Literal, TypedDict +from typing_extensions import Final class A: pass class B: pass @@ -2012,8 +2011,7 @@ del test[bad_keys] # E: Key "a" of TypedDict "Test" cannot be delet [out] [case testLiteralIntelligentIndexingMultiTypedDict] -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class A: pass class B: pass diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index bee0984c0c03..9e99a1ca5cf0 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2883,7 +2883,7 @@ CustomDict(foo="abc", bar="def") [file foo/__init__.py] [file foo/bar/__init__.py] [file foo/bar/custom_dict.py] -from typing_extensions import TypedDict +from typing import TypedDict CustomDict = TypedDict( "CustomDict", diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 2cf6e709c3b4..d9dda17b7b78 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1,7 +1,6 @@ [case testNarrowingParentWithStrsBasic] from dataclasses import dataclass -from typing import Literal, NamedTuple, Tuple, Union -from typing_extensions import TypedDict +from typing import Literal, NamedTuple, Tuple, TypedDict, Union class Object1: key: Literal["A"] @@ -80,12 +79,12 @@ if x5["key"] == "A": else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal['B'], 'foo': builtins.str})" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithEnumsBasic] from enum import Enum from dataclasses import dataclass -from typing import Literal, NamedTuple, Tuple, Union -from typing_extensions import TypedDict +from typing import Literal, NamedTuple, Tuple, TypedDict, Union class Key(Enum): A = 1 @@ -168,12 +167,12 @@ if x5["key"] is Key.A: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal[__main__.Key.A], 'foo': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal[__main__.Key.B], 'foo': builtins.str})" -[builtins fixtures/narrowing.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithIsInstanceBasic] from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import TypedDict +from typing import NamedTuple, Tuple, TypedDict, Union class Object1: key: int @@ -233,7 +232,8 @@ if isinstance(x5["key"], int): reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': builtins.str})" -[builtins fixtures/narrowing.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentMultipleKeys] # flags: --warn-unreachable @@ -270,8 +270,7 @@ else: [case testNarrowingTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class TypedDict1(TypedDict): key: Literal['A', 'C'] @@ -294,11 +293,11 @@ if x['key'] == 'D': else: reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingPartialTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class TypedDict1(TypedDict, total=False): key: Literal['A', 'C'] @@ -321,10 +320,10 @@ if x['key'] == 'D': else: reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingNestedTypedDicts] -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class A(TypedDict): key: Literal['A'] @@ -349,6 +348,7 @@ if unknown['inner']['key'] == 'C': reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.Y', {'inner': Union[TypedDict('__main__.B', {'key': Literal['B']}), TypedDict('__main__.C', {'key': Literal['C']})]})" reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.C', {'key': Literal['C']})" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithMultipleParents] from enum import Enum @@ -396,8 +396,7 @@ else: [case testNarrowingParentWithParentMixtures] from enum import Enum -from typing import Literal, Union, NamedTuple -from typing_extensions import TypedDict +from typing import Literal, Union, NamedTuple, TypedDict class Key(Enum): A = 1 @@ -575,8 +574,7 @@ else: [case testNarrowingParentsHierarchyTypedDict] # flags: --warn-unreachable -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union from enum import Enum class Key(Enum): @@ -613,12 +611,12 @@ if y["model"]["key"] is Key.C: else: reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})]" reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentsHierarchyTypedDictWithStr] # flags: --warn-unreachable -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class Parent1(TypedDict): model: Model1 @@ -650,6 +648,7 @@ else: reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})]" reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal['A']}), TypedDict('__main__.Model2', {'key': Literal['B']})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingExprPropagation] from typing import Literal, Union @@ -1232,8 +1231,7 @@ reveal_type(x) # N: Revealed type is "builtins.bool" [builtins fixtures/primitives.pyi] [case testNarrowingTypedDictUsingEnumLiteral] -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union from enum import Enum class E(Enum): @@ -1253,6 +1251,7 @@ def f(d: Union[Foo, Bar]) -> None: d['x'] reveal_type(d) # N: Revealed type is "TypedDict('__main__.Foo', {'tag': Literal[__main__.E.FOO], 'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingUsingMetaclass] from typing import Type @@ -1293,8 +1292,7 @@ def f(t: Type[T], a: A, b: B) -> None: reveal_type(b) # N: Revealed type is "__main__.B" [case testNarrowingNestedUnionOfTypedDicts] -from typing import Literal, Union -from typing_extensions import TypedDict +from typing import Literal, TypedDict, Union class A(TypedDict): tag: Literal["A"] @@ -1318,9 +1316,8 @@ elif abc["tag"] == "C": reveal_type(abc) # N: Revealed type is "TypedDict('__main__.C', {'tag': Literal['C'], 'c': builtins.int})" else: reveal_type(abc) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['B'], 'b': builtins.int})" - [builtins fixtures/primitives.pyi] - +[typing fixtures/typing-typeddict.pyi] [case testNarrowingRuntimeCover] from typing import Dict, List, Union diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index a09d72f472de..814e47f09634 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3233,15 +3233,15 @@ class User: self.name = name # E: Cannot assign to a method [case testNewAnalyzerMemberNameMatchesTypedDict] -from typing import Union, Any -from typing_extensions import TypedDict +from typing import TypedDict, Union, Any class T(TypedDict): b: b.T class b: T: Union[Any] -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerMemberNameMatchesNamedTuple] from typing import Union, Any, NamedTuple diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 5c878e3d7338..243568c54253 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -2691,8 +2691,7 @@ reveal_type(f(**{'a': 4, 'b': 4, 'c': 4})) # N: Revealed type is "builtins.tup [builtins fixtures/dict.pyi] [case testOverloadKwargsSelectionWithTypedDict] -from typing import overload, Tuple -from typing_extensions import TypedDict +from typing import overload, Tuple, TypedDict @overload def f(*, x: int) -> Tuple[int]: ... @overload @@ -2713,6 +2712,7 @@ reveal_type(f(**a)) # N: Revealed type is "Tuple[builtins.int]" reveal_type(f(**b)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" reveal_type(f(**c)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadVarargsAndKwargsSelection] from typing import overload, Any, Tuple, Dict diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 3da30eaf82cc..c8de09138b8f 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -675,7 +675,8 @@ main:16: note: def foo(cls, float, /) -> Any main:16: note: def foo(cls, a: str) -> Any [case testUnpackWithDuplicateNamePositionalOnly] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -683,6 +684,7 @@ class Person(TypedDict): def foo(name: str, /, **kwargs: Unpack[Person]) -> None: # Allowed ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testPossiblyUndefinedWithAssignmentExpr] # flags: --enable-error-code possibly-undefined diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index 7fcb620c49d9..00d5489e515a 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -879,13 +879,13 @@ def list_thing(transforming: InList[T]) -> T: reveal_type(list_thing([5])) # N: Revealed type is "builtins.list[builtins.int]" [case testRecursiveTypedDictWithList] -from typing import List -from typing_extensions import TypedDict +from typing import List, TypedDict Example = TypedDict("Example", {"rec": List["Example"]}) e: Example reveal_type(e) # N: Revealed type is "TypedDict('__main__.Example', {'rec': builtins.list[...]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testRecursiveNamedTupleWithList] from typing import List, NamedTuple diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index d03ea2d77e19..44f361286737 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -477,7 +477,7 @@ f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int, 'z [typing fixtures/typing-typeddict.pyi] [case testTypedDictWithSimpleProtocol] -from typing_extensions import Protocol, TypedDict +from typing import Protocol, TypedDict class StrObjectMap(Protocol): def __getitem__(self, key: str) -> object: ... @@ -505,8 +505,7 @@ main:17: note: Got: main:17: note: def __getitem__(self, str, /) -> object [case testTypedDictWithSimpleProtocolInference] -from typing_extensions import Protocol, TypedDict -from typing import TypeVar +from typing import Protocol, TypedDict, TypeVar T_co = TypeVar('T_co', covariant=True) T = TypeVar('T') @@ -897,8 +896,7 @@ reveal_type(u(c, m_s_a)) # N: Revealed type is "Union[typing.Mapping[builtins.st [typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionUnambiguousCase] -from typing import Union, Literal, Mapping, Any, cast -from typing_extensions import TypedDict +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'a': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'b': int}) @@ -906,20 +904,20 @@ B = TypedDict('B', {'@type': Literal['b-type'], 'b': int}) c: Union[A, B] = {'@type': 'a-type', 'a': 'Test'} reveal_type(c) # N: Revealed type is "Union[TypedDict('__main__.A', {'@type': Literal['a-type'], 'a': builtins.str}), TypedDict('__main__.B', {'@type': Literal['b-type'], 'b': builtins.int})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionAmbiguousCaseBothMatch] -from typing import Union, Literal, Mapping, Any, cast -from typing_extensions import TypedDict +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'value': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': str}) c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionAmbiguousCaseNoMatch] -from typing import Union, Literal, Mapping, Any, cast -from typing_extensions import TypedDict +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'value': int}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': int}) @@ -927,6 +925,7 @@ B = TypedDict('B', {'@type': Literal['b-type'], 'value': int}) c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \ # E: Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "Union[A, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Use dict literals @@ -1813,6 +1812,7 @@ class Point(TypedDict): p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithTypingProper] from typing import TypedDict @@ -2932,7 +2932,7 @@ foo({"foo": {"e": "foo"}}) # E: Type of TypedDict is ambiguous, none of ("A", " [typing fixtures/typing-typeddict.pyi] [case testTypedDictMissingEmptyKey] -from typing_extensions import TypedDict +from typing import TypedDict class A(TypedDict): my_attr_1: str @@ -3564,8 +3564,8 @@ class A(Generic[T]): [builtins fixtures/tuple.pyi] [case testNameUndefinedErrorDoesNotLoseUnpackedKWArgsInformation] -from typing import overload -from typing_extensions import TypedDict, Unpack +from typing import TypedDict, overload +from typing_extensions import Unpack class TD(TypedDict, total=False): x: int @@ -3600,10 +3600,11 @@ class B(A): reveal_type(B.f) # N: Revealed type is "def (self: __main__.B, **kwargs: Unpack[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: builtins.str})])" B().f(x=1.0) # E: Argument "x" to "f" of "B" has incompatible type "float"; expected "int" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnpackWithParamSpecInference] -from typing import TypeVar, ParamSpec, Callable -from typing_extensions import TypedDict, Unpack +from typing import TypedDict, TypeVar, ParamSpec, Callable +from typing_extensions import Unpack P = ParamSpec("P") R = TypeVar("R") @@ -3624,11 +3625,12 @@ class Test: def h(self, **params: Unpack[Params]) -> None: run(test2, other="yes", **params) run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testTypedDictUnpackSingleWithSubtypingNoCrash] -from typing import Callable -from typing_extensions import TypedDict, Unpack +from typing import Callable, TypedDict +from typing_extensions import Unpack class Kwargs(TypedDict): name: str @@ -3642,7 +3644,8 @@ class C: # TODO: it is an old question whether we should allow this, for now simply don't crash. class D(C): d = f -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictInlineNoOldStyleAlias] # flags: --enable-incomplete-feature=InlineTypedDict @@ -3806,7 +3809,8 @@ x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated [typing fixtures/typing-typeddict.pyi] [case testTypedDictReadOnlyMutate__ior__Statements] -from typing_extensions import ReadOnly, TypedDict +from typing import TypedDict +from typing_extensions import ReadOnly class TP(TypedDict): key: ReadOnly[str] @@ -3821,7 +3825,8 @@ x |= {"key": "a", "other": 1, "mutable": True} # E: ReadOnly TypedDict keys ("k [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictReadOnlyMutate__or__Statements] -from typing_extensions import ReadOnly, TypedDict +from typing import TypedDict +from typing_extensions import ReadOnly class TP(TypedDict): key: ReadOnly[str] @@ -4013,7 +4018,8 @@ reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'=: builtins.int, 'y': bu [typing fixtures/typing-typeddict.pyi] [case testTypedDictReadOnlyUnpack] -from typing_extensions import TypedDict, Unpack, ReadOnly +from typing import TypedDict +from typing_extensions import Unpack, ReadOnly class TD(TypedDict): x: ReadOnly[int] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 754151ffb559..c427a54ea664 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -1130,8 +1130,8 @@ nt2 = A(fn=bad, val=42) # E: Argument "fn" to "A" has incompatible type "Callab [builtins fixtures/tuple.pyi] [case testVariadicTypedDict] -from typing import Tuple, Callable, Generic, TypeVar -from typing_extensions import TypeVarTuple, Unpack, TypedDict +from typing import Tuple, Callable, Generic, TypedDict, TypeVar +from typing_extensions import TypeVarTuple, Unpack T = TypeVar("T") Ts = TypeVarTuple("Ts") @@ -1156,7 +1156,8 @@ reveal_type(td) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (buil def bad() -> int: ... td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]") -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFixedUnpackWithRegularInstance] from typing import Tuple, Generic, TypeVar @@ -2167,8 +2168,8 @@ reveal_type([f, h]) # N: Revealed type is "builtins.list[def (builtins.int, *Un [builtins fixtures/tuple.pyi] [case testTypeVarTupleBothUnpacksSimple] -from typing import Tuple -from typing_extensions import Unpack, TypeVarTuple, TypedDict +from typing import Tuple, TypedDict +from typing_extensions import Unpack, TypeVarTuple class Keywords(TypedDict): a: str @@ -2202,11 +2203,12 @@ def bad2( **kwargs: Unpack[Ints], # E: Unpack item in ** argument must be a TypedDict ) -> None: ... reveal_type(bad2) # N: Revealed type is "def (one: builtins.int, *args: Any, other: builtins.str =, **kwargs: Any)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarTupleBothUnpacksCallable] -from typing import Callable, Tuple -from typing_extensions import Unpack, TypedDict +from typing import Callable, Tuple, TypedDict +from typing_extensions import Unpack class Keywords(TypedDict): a: str @@ -2229,11 +2231,12 @@ reveal_type(bad2) # N: Revealed type is "def (*Any, **Unpack[TypedDict('__main_ bad3: Callable[[Unpack[Keywords], Unpack[Ints]], None] # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) \ # E: More than one Unpack in a type is not allowed reveal_type(bad3) # N: Revealed type is "def (*Any)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarTupleBothUnpacksApplication] -from typing import Callable, TypeVar, Optional -from typing_extensions import Unpack, TypeVarTuple, TypedDict +from typing import Callable, TypedDict, TypeVar, Optional +from typing_extensions import Unpack, TypeVarTuple class Keywords(TypedDict): a: str @@ -2262,7 +2265,8 @@ def test2( func(*args) # E: Missing named argument "a" \ # E: Missing named argument "b" return func(*args, **kwargs) -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackTupleSpecialCaseNoCrash] from typing import Tuple, TypeVar diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 500dd6be4ffa..36ab3af6d3e9 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -633,8 +633,7 @@ def f(x: S) -> None: h(x) [case testTypeVarWithTypedDictBoundInIndexExpression] -from typing import TypeVar -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar class Data(TypedDict): x: int @@ -645,11 +644,11 @@ T = TypeVar("T", bound=Data) def f(data: T) -> None: reveal_type(data["x"]) # N: Revealed type is "builtins.int" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarWithUnionTypedDictBoundInIndexExpression] -from typing import TypeVar, Union, Dict -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Dict class Data(TypedDict): x: int @@ -661,10 +660,10 @@ T = TypeVar("T", bound=Union[Data, Dict[str, str]]) def f(data: T) -> None: reveal_type(data["x"]) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarWithTypedDictValueInIndexExpression] -from typing import TypeVar, Union, Dict -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Dict class Data(TypedDict): x: int @@ -676,10 +675,10 @@ T = TypeVar("T", Data, Dict[str, str]) def f(data: T) -> None: _: Union[str, int] = data["x"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSelfTypeVarIndexExpr] -from typing import TypeVar, Union, Type -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Type T = TypeVar("T", bound="Indexable") @@ -697,6 +696,7 @@ class Indexable: def m(self: T) -> T: return self["foo"] [builtins fixtures/classmethod.pyi] +[typing fixtures/typing-full.pyi] [case testTypeVarWithValueDeferral] from typing import TypeVar, Callable diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 4405948367cb..65bbd8456d78 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -745,7 +745,8 @@ bar(*bad2) # E: Expected iterable as variadic argument -- Keyword arguments unpacking [case testUnpackKwargsReveal] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -754,9 +755,11 @@ def foo(arg: bool, **kwargs: Unpack[Person]) -> None: ... reveal_type(foo) # N: Revealed type is "def (arg: builtins.bool, **kwargs: Unpack[TypedDict('__main__.Person', {'name': builtins.str, 'age': builtins.int})])" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackOutsideOfKwargs] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str age: int @@ -768,6 +771,7 @@ def bar(x: int, *args: Unpack[Person]) -> None: # E: "Person" cannot be unpacke def baz(**kwargs: Unpack[Person]) -> None: # OK ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackWithoutTypedDict] from typing_extensions import Unpack @@ -777,7 +781,8 @@ def foo(**kwargs: Unpack[dict]) -> None: # E: Unpack item in ** argument must b [builtins fixtures/dict.pyi] [case testUnpackWithDuplicateKeywords] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -785,10 +790,11 @@ class Person(TypedDict): def foo(name: str, **kwargs: Unpack[Person]) -> None: # E: Overlap between argument names and ** TypedDict items: "name" ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackWithDuplicateKeywordKwargs] -from typing_extensions import Unpack, TypedDict -from typing import Dict, List +from typing_extensions import Unpack +from typing import Dict, List, TypedDict class Spec(TypedDict): args: List[int] @@ -797,9 +803,11 @@ def foo(**kwargs: Unpack[Spec]) -> None: # Allowed ... foo(args=[1], kwargs={"2": 3}) # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsNonIdentifier] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack Weird = TypedDict("Weird", {"@": int}) @@ -808,9 +816,11 @@ def foo(**kwargs: Unpack[Weird]) -> None: foo(**{"@": 42}) foo(**{"no": "way"}) # E: Argument 1 to "foo" has incompatible type "**Dict[str, str]"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsEmpty] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack Empty = TypedDict("Empty", {}) @@ -819,9 +829,11 @@ def foo(**kwargs: Unpack[Empty]) -> None: # N: "foo" defined here foo() foo(x=1) # E: Unexpected keyword argument "x" for "foo" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackTypedDictTotality] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Circle(TypedDict, total=True): radius: int @@ -841,9 +853,11 @@ def bar(**kwargs: Unpack[Square]): ... bar(side=12) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackUnexpectedKeyword] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict, total=False): name: str @@ -854,9 +868,11 @@ def foo(**kwargs: Unpack[Person]) -> None: # N: "foo" defined here foo(name='John', age=42, department='Sales') # E: Unexpected keyword argument "department" for "foo" foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKeywordTypes] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -867,9 +883,11 @@ def foo(**kwargs: Unpack[Person]): foo(name='John', age='42') # E: Argument "age" to "foo" has incompatible type "str"; expected "int" foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKeywordTypesTypedDict] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -884,9 +902,11 @@ def foo(**kwargs: Unpack[Person]) -> None: lp = LegacyPerson(name="test", age="42") foo(**lp) # E: Argument "age" to "foo" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFunctionBodyWithUnpackedKwargs] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -898,9 +918,11 @@ def foo(**kwargs: Unpack[Person]) -> int: department: str = kwargs['department'] # E: TypedDict "Person" has no key "department" return kwargs['age'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverrides] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -915,9 +937,11 @@ class SubBad(Base): # N: This violates the Liskov substitution principle \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverridesTypedDict] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -940,10 +964,11 @@ class SubBad(Base): # N: Subclass: \ # N: def foo(self, *, baz: int) -> None [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsGeneric] -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class Person(TypedDict, Generic[T]): @@ -953,10 +978,11 @@ class Person(TypedDict, Generic[T]): def foo(**kwargs: Unpack[Person[T]]) -> T: ... reveal_type(foo(name="test", value=42)) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsInference] -from typing import Generic, TypeVar, Protocol -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar, Protocol +from typing_extensions import Unpack T_contra = TypeVar("T_contra", contravariant=True) class CBPerson(Protocol[T_contra]): @@ -972,10 +998,11 @@ def test(cb: CBPerson[T]) -> T: ... def foo(*, name: str, value: int) -> None: ... reveal_type(test(foo)) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverload] -from typing import Any, overload -from typing_extensions import Unpack, TypedDict +from typing import TypedDict, Any, overload +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -994,9 +1021,11 @@ def foo(**kwargs: Any) -> Any: reveal_type(foo(sort="test", taste=999)) # N: Revealed type is "builtins.str" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsJoin] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -1008,10 +1037,11 @@ def bar(**kwargs: Unpack[Person]) -> None: ... reveal_type([foo, bar]) # N: Revealed type is "builtins.list[def (*, name: builtins.str, age: builtins.int)]" reveal_type([bar, foo]) # N: Revealed type is "builtins.list[def (*, name: builtins.str, age: builtins.int)]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsParamSpec] -from typing import Callable, Any, TypeVar, List -from typing_extensions import ParamSpec, Unpack, TypedDict +from typing import Callable, Any, TypedDict, TypeVar, List +from typing_extensions import ParamSpec, Unpack class Person(TypedDict): name: str @@ -1027,10 +1057,11 @@ def g(**kwargs: Unpack[Person]) -> int: ... reveal_type(g) # N: Revealed type is "def (*, name: builtins.str, age: builtins.int) -> builtins.list[builtins.int]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackGenericTypedDictImplicitAnyEnabled] -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class TD(TypedDict, Generic[T]): @@ -1041,11 +1072,12 @@ def foo(**kwds: Unpack[TD]) -> None: ... # Same as `TD[Any]` foo(key="yes", value=42) foo(key="yes", value="ok") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackGenericTypedDictImplicitAnyDisabled] # flags: --disallow-any-generics -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class TD(TypedDict, Generic[T]): @@ -1056,6 +1088,7 @@ def foo(**kwds: Unpack[TD]) -> None: ... # E: Missing type parameters for gener foo(key="yes", value=42) foo(key="yes", value="ok") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackNoCrashOnEmpty] from typing_extensions import Unpack @@ -1067,8 +1100,8 @@ class D: [builtins fixtures/dict.pyi] [case testUnpackInCallableType] -from typing import Callable -from typing_extensions import Unpack, TypedDict +from typing import Callable, TypedDict +from typing_extensions import Unpack class TD(TypedDict): key: str @@ -1080,3 +1113,4 @@ foo(key="yes", value="ok") bad: Callable[[*TD], None] # E: "TD" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/fine-grained-follow-imports.test b/test-data/unit/fine-grained-follow-imports.test index 22f2a7895cf9..d716a57123dc 100644 --- a/test-data/unit/fine-grained-follow-imports.test +++ b/test-data/unit/fine-grained-follow-imports.test @@ -831,8 +831,7 @@ class A: ... import trio [file trio/__init__.py.2] -from typing import TypeVar -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar import trio from . import abc as abc @@ -844,5 +843,6 @@ class C(TypedDict): import trio class A: ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test index 02373091ad54..0ed3be4055ea 100644 --- a/test-data/unit/fine-grained-suggest.test +++ b/test-data/unit/fine-grained-suggest.test @@ -159,13 +159,14 @@ def foo(): [case testSuggestInferTypedDict] # suggest: foo.foo [file foo.py] -from typing_extensions import TypedDict +from typing import TypedDict TD = TypedDict('TD', {'x': int}) def foo(): return bar() def bar() -> TD: ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] () -> foo.TD == diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 496178c40e8c..c06b9ccb97d7 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -3710,17 +3710,20 @@ b.py:4: error: Incompatible types in assignment (expression has type "str", vari [case testTypedDictUpdateReadOnly] import b [file a.py] -from typing_extensions import TypedDict, ReadOnly +from typing import TypedDict +from typing_extensions import ReadOnly Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=1, y=2) [file a.py.2] -from typing_extensions import TypedDict, ReadOnly +from typing import TypedDict +from typing_extensions import ReadOnly class Point(TypedDict): x: int y: ReadOnly[int] p = Point(x=1, y=2) [file a.py.3] -from typing_extensions import TypedDict, ReadOnly +from typing import TypedDict +from typing_extensions import ReadOnly Point = TypedDict('Point', {'x': ReadOnly[int], 'y': int}) p = Point(x=1, y=2) [file b.py] @@ -3729,6 +3732,7 @@ def foo(x: Point) -> None: x['x'] = 1 x['y'] = 2 [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:4: error: ReadOnly TypedDict key "y" TypedDict is mutated @@ -10060,14 +10064,14 @@ main:4: error: "C" expects no type arguments, but 2 given [case testUnpackKwargsUpdateFine] import m [file shared.py] -from typing_extensions import TypedDict +from typing import TypedDict class Person(TypedDict): name: str age: int [file shared.py.2] -from typing_extensions import TypedDict +from typing import TypedDict class Person(TypedDict): name: str @@ -10084,6 +10088,7 @@ from lib import foo foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == m.py:2: error: Argument "age" to "foo" has incompatible type "int"; expected "str" @@ -10389,7 +10394,7 @@ import n import m x: m.TD [file m.py] -from typing_extensions import TypedDict +from typing import TypedDict from f import A class TD(TypedDict): @@ -10402,6 +10407,7 @@ A = int [file f.py.2] A = str [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] m.py:5: error: Invalid statement in TypedDict definition; expected "field_name: field_type" == diff --git a/test-data/unit/fixtures/for.pyi b/test-data/unit/fixtures/for.pyi index 10f45e68cd7d..80c8242c2a5e 100644 --- a/test-data/unit/fixtures/for.pyi +++ b/test-data/unit/fixtures/for.pyi @@ -15,6 +15,7 @@ class function: pass class ellipsis: pass class bool: pass class int: pass # for convenience +class float: pass # for convenience class str: # for convenience def upper(self) -> str: ... From 24ecb27fe365550087484ec346f7de5bb2400e82 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Feb 2025 20:35:38 +0000 Subject: [PATCH 1133/1617] Fix crash on deferred supertype and setter override (#18649) Fixes https://github.com/python/mypy/issues/18648 The fix is straightforward, but unlike for getter I decided to not create any ad-hoc types during `last_pass`. --- mypy/checker.py | 22 +++++++++++++++------- test-data/unit/check-classes.test | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 36c673a1c330..462bd1f6d2e1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2147,16 +2147,11 @@ def check_method_override_for_base_with_name( # it can be checked for compatibility. original_type = get_proper_type(base_attr.type) original_node = base_attr.node - always_allow_covariant = False - if is_settable_property(defn) and ( - is_settable_property(original_node) or isinstance(original_node, Var) - ): - if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)): - always_allow_covariant = True - self.check_setter_type_override(defn, base_attr, base) # `original_type` can be partial if (e.g.) it is originally an # instance variable from an `__init__` block that becomes deferred. + supertype_ready = True if original_type is None or isinstance(original_type, PartialType): + supertype_ready = False if self.pass_num < self.last_pass: # If there are passes left, defer this node until next pass, # otherwise try reconstructing the method type from available information. @@ -2179,6 +2174,19 @@ def check_method_override_for_base_with_name( else: # Will always fail to typecheck below, since we know the node is a method original_type = NoneType() + + always_allow_covariant = False + if is_settable_property(defn) and ( + is_settable_property(original_node) or isinstance(original_node, Var) + ): + if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)): + # Unlike with getter, where we try to construct some fallback type in case of + # deferral during last_pass, we can't make meaningful setter checks if the + # supertype is not known precisely. + if supertype_ready: + always_allow_covariant = True + self.check_setter_type_override(defn, base_attr, base) + if isinstance(original_node, (FuncDef, OverloadedFuncDef)): original_class_or_static = original_node.is_class or original_node.is_static elif isinstance(original_node, Decorator): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 8a5af4ba1e0f..d48a27dbed03 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -8443,3 +8443,25 @@ class C: def x(self) -> None: pass [builtins fixtures/property.pyi] + +[case testPropertySetterSuperclassDeferred] +from typing import Callable, TypeVar + +class B: + def __init__(self) -> None: + self.foo = f() + +class C(B): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... + +T = TypeVar("T") +def deco(fn: Callable[[], list[T]]) -> Callable[[], T]: ... + +@deco +def f() -> list[str]: ... +[builtins fixtures/property.pyi] From 7548cd118848d0ce04392ac982ea012d392ea85f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:59:22 +0100 Subject: [PATCH 1134/1617] Update Final imports in tests (#18654) Replace most `typing_extensions.Final` and `typing_extensions.final` imports in tests with `typing.Final` and `typing.final`. --- mypyc/test-data/alwaysdefined.test | 5 +- mypyc/test-data/commandline.test | 3 +- mypyc/test-data/exceptions-freq.test | 2 +- mypyc/test-data/irbuild-constant-fold.test | 12 ++--- mypyc/test-data/irbuild-float.test | 2 +- mypyc/test-data/irbuild-i64.test | 2 +- mypyc/test-data/irbuild-int.test | 2 +- mypyc/test-data/irbuild-set.test | 4 +- mypyc/test-data/refcount.test | 2 +- mypyc/test-data/run-classes.test | 5 +- mypyc/test-data/run-floats.test | 6 +-- mypyc/test-data/run-i64.test | 11 ++--- mypyc/test-data/run-math.test | 3 +- mypyc/test-data/run-multimodule.test | 6 +-- mypyc/test-data/run-sets.test | 3 +- mypyc/test-data/run-tuples.test | 6 +-- mypyc/test-data/run-u8.test | 3 +- test-data/unit/check-basic.test | 3 +- test-data/unit/check-enum.test | 20 +++----- test-data/unit/check-expressions.test | 3 +- test-data/unit/check-final.test | 8 ++-- test-data/unit/check-formatting.test | 4 +- test-data/unit/check-literal.test | 56 ++++++++-------------- test-data/unit/check-narrowing.test | 12 ++--- test-data/unit/check-newsemanal.test | 6 +-- test-data/unit/check-protocols.test | 5 +- test-data/unit/check-python310.test | 3 +- test-data/unit/check-python38.test | 3 +- test-data/unit/check-typeddict.test | 36 ++++++-------- test-data/unit/fine-grained.test | 6 +-- test-data/unit/semanal-statements.test | 8 ++-- 31 files changed, 97 insertions(+), 153 deletions(-) diff --git a/mypyc/test-data/alwaysdefined.test b/mypyc/test-data/alwaysdefined.test index e8c44d8fc548..ecbc8c410d6d 100644 --- a/mypyc/test-data/alwaysdefined.test +++ b/mypyc/test-data/alwaysdefined.test @@ -166,8 +166,7 @@ IfConditionalAndNonConditional1: [x] IfConditionalAndNonConditional2: [] [case testAlwaysDefinedExpressions] -from typing import Dict, List, Set, Optional, cast -from typing_extensions import Final +from typing import Dict, Final, List, Set, Optional, cast import other @@ -307,7 +306,7 @@ def f() -> int: [file other.py] # Not compiled -from typing_extensions import Final +from typing import Final Y: Final = 3 diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index c5fb7e88dd1a..0c993d9ac336 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -105,8 +105,7 @@ def f(x: int) -> int: # cmd: test.py [file test.py] -from typing import List, Any, AsyncIterable -from typing_extensions import Final +from typing import Final, List, Any, AsyncIterable from mypy_extensions import trait, mypyc_attr from functools import singledispatch diff --git a/mypyc/test-data/exceptions-freq.test b/mypyc/test-data/exceptions-freq.test index a655eed44d90..b0e4cd6d35f7 100644 --- a/mypyc/test-data/exceptions-freq.test +++ b/mypyc/test-data/exceptions-freq.test @@ -97,7 +97,7 @@ L2: hot blocks: [0, 1] [case testRareBranch_freq] -from typing_extensions import Final +from typing import Final x: Final = str() diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 97b13ab337c7..cd953c84c541 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -187,7 +187,7 @@ L0: return 1 [case testIntConstantFoldingFinal] -from typing_extensions import Final +from typing import Final X: Final = 5 Y: Final = 2 + 4 @@ -203,7 +203,7 @@ L0: return 1 [case testIntConstantFoldingClassFinal] -from typing_extensions import Final +from typing import Final class C: X: Final = 5 @@ -222,7 +222,7 @@ L0: return 1 [case testFloatConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 1.5 N2: Final = 1.5 * 2 @@ -391,7 +391,7 @@ L2: return 1 [case testStrConstantFolding] -from typing_extensions import Final +from typing import Final S: Final = 'z' N: Final = 2 @@ -416,7 +416,7 @@ L0: return 1 [case testBytesConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 2 @@ -438,7 +438,7 @@ L0: return 1 [case testComplexConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 1 FLOAT_N: Final = 1.5 diff --git a/mypyc/test-data/irbuild-float.test b/mypyc/test-data/irbuild-float.test index 35e2eff62b86..d0fd32ffbdd7 100644 --- a/mypyc/test-data/irbuild-float.test +++ b/mypyc/test-data/irbuild-float.test @@ -219,7 +219,7 @@ L0: return r0 [case testFloatFinalConstant] -from typing_extensions import Final +from typing import Final X: Final = 123.0 Y: Final = -1.0 diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index a52de16f3a6c..c59e306b09df 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1677,7 +1677,7 @@ L2: return 1 [case testI64FinalConstants] -from typing_extensions import Final +from typing import Final from mypy_extensions import i64 A: Final = -1 diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 9082cc0136d9..bdf9127b722a 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -116,7 +116,7 @@ L0: return r0 [case testFinalConstantFolding] -from typing_extensions import Final +from typing import Final X: Final = -1 Y: Final = -(1 + 3*2) diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 6da3c26c42f7..c42a1fa74a75 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -628,7 +628,7 @@ L0: return r0 [case testOperatorInSetLiteral] -from typing_extensions import Final +from typing import Final CONST: Final = "daylily" non_const = 10 @@ -716,7 +716,7 @@ L0: return r14 [case testForSetLiteral] -from typing_extensions import Final +from typing import Final CONST: Final = 10 non_const = 20 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index e757b3684c79..c311f042ad5e 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -1339,7 +1339,7 @@ L0: return r2 [case testBorrowIntCompareFinal] -from typing_extensions import Final +from typing import Final X: Final = 10 diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 28e5b74a254b..127f67902b7d 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -711,8 +711,7 @@ Traceback (most recent call last): AttributeError: attribute 'x' of 'X' undefined [case testClassMethods] -from typing import ClassVar, Any -from typing_extensions import final +from typing import ClassVar, Any, final from mypy_extensions import mypyc_attr from interp import make_interpreted_subclass @@ -2543,7 +2542,7 @@ class Derived(Base): assert Derived()() == 1 [case testClassWithFinalAttribute] -from typing_extensions import Final +from typing import Final class C: A: Final = -1 diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index 2c101100549d..49620f6448c7 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -2,8 +2,7 @@ [case testFloatOps] from __future__ import annotations -from typing import Any, cast -from typing_extensions import Final +from typing import Final, Any, cast from testutil import assertRaises, float_vals, FLOAT_MAGIC import math @@ -348,8 +347,7 @@ def test_tuples() -> None: assert t2 == tuple([5.0, 1.5, -7.0, -113.0]) [case testFloatGlueMethodsAndInheritance] -from typing import Any -from typing_extensions import Final +from typing import Final, Any from mypy_extensions import trait diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 36567c949d79..0dcad465cc9a 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -517,11 +517,10 @@ def test_isinstance() -> None: assert narrow2("foobar") == 6 [case testI64ErrorValuesAndUndefined] -from typing import Any, Tuple +from typing import Any, Final, Tuple import sys from mypy_extensions import mypyc_attr, i64 -from typing_extensions import Final from testutil import assertRaises @@ -905,8 +904,7 @@ def test_undefined_native_int_tuple_via_any() -> None: assert o.t == (-13, 45) [case testI64DefaultArgValues] -from typing import Any, Iterator, Tuple -from typing_extensions import Final +from typing import Any, Final, Iterator, Tuple MAGIC: Final = -113 @@ -1206,7 +1204,7 @@ def test_magic_default() -> None: assert a(MAGIC) == MAGIC [case testI64UndefinedLocal] -from typing_extensions import Final +from typing import Final from mypy_extensions import i64, i32 @@ -1338,8 +1336,7 @@ def test_many_locals() -> None: assert a33 == 20 [case testI64GlueMethodsAndInheritance] -from typing import Any -from typing_extensions import Final +from typing import Final, Any from mypy_extensions import i64, trait diff --git a/mypyc/test-data/run-math.test b/mypyc/test-data/run-math.test index 266b4851575f..d3102290d2af 100644 --- a/mypyc/test-data/run-math.test +++ b/mypyc/test-data/run-math.test @@ -1,8 +1,7 @@ # Test cases for the math module (compile and run) [case testMathOps] -from typing import Any, Callable -from typing_extensions import Final +from typing import Any, Callable, Final import math from math import pi, e, tau, inf, nan from testutil import assertRaises, float_vals, assertDomainError, assertMathRangeError diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 5edd5688140e..11e898b45572 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -155,7 +155,7 @@ def f(c: C) -> int: c = cast(C, o) return a_global + c.x + c.f() + d.x + d.f() + 1 [file other.py] -from typing_extensions import Final +from typing import Final a_global: Final = int('5') class C: @@ -735,11 +735,11 @@ def foo() -> int: return X [file other.py] -from typing_extensions import Final +from typing import Final X: Final = 10 [file other.py.2] -from typing_extensions import Final +from typing import Final X: Final = 20 [file driver.py] diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test index 57d5cde65bb8..68edd1e6b77d 100644 --- a/mypyc/test-data/run-sets.test +++ b/mypyc/test-data/run-sets.test @@ -235,8 +235,7 @@ def test_frozen_sets_from_iterables() -> None: assert g4() == frozenset({11, 21, 31}) [case testPrecomputedFrozenSets] -from typing import Any -from typing_extensions import Final +from typing import Final, Any CONST: Final = "CONST" non_const = "non_const" diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index 0851c15e57fd..1f1b0bc9eae7 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -97,8 +97,7 @@ assert f(Sub(3, 2)) == 3 -- Ref: https://github.com/mypyc/mypyc/issues/924 [case testNamedTupleClassSyntax] -from typing import Dict, List, NamedTuple, Optional, Tuple, Union -from typing_extensions import final +from typing import Dict, List, NamedTuple, Optional, Tuple, Union, final class FuncIR: pass @@ -147,8 +146,7 @@ assert Record.__annotations__ == { }, Record.__annotations__ [case testTupleOps] -from typing import Tuple, List, Any, Optional -from typing_extensions import Final +from typing import Tuple, Final, List, Any, Optional def f() -> Tuple[()]: return () diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index cddb031e3352..c8580f05e31c 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -1,8 +1,7 @@ [case testU8BasicOps] -from typing import Any, Tuple +from typing import Any, Final, Tuple from mypy_extensions import u8, i16, i32, i64 -from typing_extensions import Final from testutil import assertRaises diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 375886733f3a..6ecbbdcc13eb 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -405,8 +405,7 @@ class B(Enum): b = 10 [file b.py] -from typing import List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type, TypedDict -from typing_extensions import Final +from typing import Final, List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type, TypedDict from enum import Enum import a class A: pass diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 6c111e05e33e..7b97f96f55b1 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -31,7 +31,7 @@ reveal_type(Animal.DOG) # N: Revealed type is "Literal[__main__.Animal.DOG]?" [case testEnumCreatedFromFinalValue] from enum import Enum -from typing_extensions import Final +from typing import Final x: Final['str'] = 'ANT BEE CAT DOG' Animal = Enum('Animal', x) @@ -975,8 +975,7 @@ else: [case testEnumReachabilityChecksIndirect] from enum import Enum -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal class Foo(Enum): A = 1 @@ -1130,8 +1129,7 @@ reveal_type(x3) # N: Revealed type is "Union[__main__.Foo, __main__.Bar]" [builtins fixtures/bool.pyi] [case testEnumReachabilityPEP484ExampleWithFinal] -from typing import Union -from typing_extensions import Final +from typing import Final, Union from enum import Enum class Empty(Enum): @@ -1176,8 +1174,7 @@ def process(response: Union[str, Reason] = '') -> str: [case testEnumReachabilityPEP484ExampleSingleton] -from typing import Union -from typing_extensions import Final +from typing import Final, Union from enum import Enum class Empty(Enum): @@ -1200,8 +1197,7 @@ def func(x: Union[int, None, Empty] = _empty) -> int: [builtins fixtures/primitives.pyi] [case testEnumReachabilityPEP484ExampleSingletonWithMethod] -from typing import Union -from typing_extensions import Final +from typing import Final, Union from enum import Enum class Empty(Enum): @@ -1331,8 +1327,7 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingDirectConflict] # flags: --warn-unreachable from enum import Enum -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal class Foo(Enum): A = 1 @@ -1367,8 +1362,7 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingBigDisjoints] # flags: --warn-unreachable from enum import Enum -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal class Foo(Enum): A = 1 diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index b64f15a4aaf0..8dd589937df8 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2281,8 +2281,7 @@ def f(x: T) -> T: [case testStrictEqualityWithALiteral] # flags: --strict-equality -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal def returns_a_or_b() -> Literal['a', 'b']: ... diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 02c0b4c5face..ce68b265a3c3 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1120,7 +1120,7 @@ class B: [out] [case testFinalInDeferredMethod] -from typing_extensions import Final +from typing import Final class A: def __init__(self) -> None: @@ -1187,8 +1187,7 @@ class Child(Parent): def __bar(self) -> None: ... [case testFinalWithoutBool] -from typing import Literal -from typing_extensions import final +from typing import Literal, final class A: pass @@ -1208,8 +1207,7 @@ reveal_type(C() and 42) # N: Revealed type is "Literal[42]?" [builtins fixtures/bool.pyi] [case testFinalWithoutBoolButWithLen] -from typing import Literal -from typing_extensions import final +from typing import Literal, final # Per Python data model, __len__ is called if __bool__ does not exist. # In a @final class, __bool__ would not exist. diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index 62d1f0923540..dce26b37dfc8 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -502,7 +502,7 @@ def better_snakecase(text: str) -> str: [builtins fixtures/primitives.pyi] [case testFormatCallFinal] -from typing_extensions import Final +from typing import Final FMT: Final = '{.x}, {:{:d}}' @@ -511,7 +511,7 @@ FMT.format(1, 2, 'no') # E: "int" has no attribute "x" \ [builtins fixtures/primitives.pyi] [case testFormatCallFinalChar] -from typing_extensions import Final +from typing import Final GOOD: Final = 'c' BAD: Final = 'no' diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index befcb3970299..0b2721e77624 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1892,8 +1892,7 @@ del d[c_key] # E: TypedDict "Outer" has no key "c" [out] [case testLiteralIntelligentIndexingUsingFinal] -from typing import Literal, Tuple, NamedTuple, TypedDict -from typing_extensions import Final +from typing import Final, Literal, Tuple, NamedTuple, TypedDict int_key_good: Final = 0 int_key_bad: Final = 3 @@ -1959,8 +1958,7 @@ tup2[idx_bad] # E: Tuple index out of range [out] [case testLiteralIntelligentIndexingTypedDictUnions] -from typing import Literal, TypedDict -from typing_extensions import Final +from typing import Final, Literal, TypedDict class A: pass class B: pass @@ -2049,8 +2047,7 @@ reveal_type(x.get(bad_keys, 3)) # N: Revealed type is "builtins.object" -- [case testLiteralFinalInferredAsLiteral] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal var1: Final = 1 var2: Final = "foo" @@ -2105,8 +2102,7 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [out] [case testLiteralFinalDirectInstanceTypesSupersedeInferredLiteral] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal var1: Final[int] = 1 var2: Final[str] = "foo" @@ -2161,8 +2157,7 @@ force4(f.instancevar4) [out] [case testLiteralFinalDirectLiteralTypesForceLiteral] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal var1: Final[Literal[1]] = 1 var2: Final[Literal["foo"]] = "foo" @@ -2217,7 +2212,7 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [out] [case testLiteralFinalErasureInMutableDatastructures1] -from typing_extensions import Final +from typing import Final var1: Final = [0, None] var2: Final = (0, None) @@ -2227,8 +2222,7 @@ reveal_type(var2) # N: Revealed type is "Tuple[Literal[0]?, None]" [builtins fixtures/tuple.pyi] [case testLiteralFinalErasureInMutableDatastructures2] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal var1: Final = [] var1.append(0) @@ -2246,8 +2240,7 @@ reveal_type(var3) # N: Revealed type is "builtins.list[Literal[0]]" [builtins fixtures/list.pyi] [case testLiteralFinalMismatchCausesError] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal var1: Final[Literal[4]] = 1 # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[4]") var2: Final[Literal['bad']] = "foo" # E: Incompatible types in assignment (expression has type "Literal['foo']", variable has type "Literal['bad']") @@ -2277,8 +2270,7 @@ Foo().instancevar1 = 10 # E: Cannot assign to final attribute "instancevar1" \ [out] [case testLiteralFinalGoesOnlyOneLevelDown] -from typing import Literal, Tuple -from typing_extensions import Final +from typing import Final, Literal, Tuple a: Final = 1 b: Final = (1, 2) @@ -2295,8 +2287,7 @@ force2(b) # ok [out] [case testLiteralFinalCollectionPropagation] -from typing import List, Literal -from typing_extensions import Final +from typing import Final, List, Literal a: Final = 1 implicit = [a] @@ -2325,8 +2316,7 @@ force2(reveal_type(direct[0])) # E: Argument 1 to "force2" has incompatible ty [out] [case testLiteralFinalStringTypesPython3] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal a: Final = u"foo" b: Final = "foo" @@ -2349,8 +2339,7 @@ force_bytes(reveal_type(c)) # N: Revealed type is "Literal[b'foo']" [out] [case testLiteralFinalPropagatesThroughGenerics] -from typing import TypeVar, Generic, Literal -from typing_extensions import Final +from typing import TypeVar, Generic, Final, Literal T = TypeVar('T') @@ -2405,8 +2394,7 @@ over_literal(reveal_type(WrapperClass(var3))) # N: Revealed type is "__main__. [out] [case testLiteralFinalUsedInLiteralType] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal a: Final[int] = 3 b: Final = 3 c: Final[Literal[3]] = 3 @@ -2420,8 +2408,7 @@ d_wrap: Literal[4, d] # E: Parameter 2 of Literal[...] is invalid [out] [case testLiteralWithFinalPropagation] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal a: Final = 3 b: Final = a @@ -2435,8 +2422,7 @@ expect_3(c) # E: Argument 1 to "expect_3" has incompatible type "int"; expected [out] [case testLiteralWithFinalPropagationIsNotLeaking] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal final_tuple_direct: Final = (2, 3) final_tuple_indirect: Final = final_tuple_direct @@ -2627,8 +2613,7 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Test.FOO]" [out] [case testLiteralUsingEnumAttributesInLiteralContexts] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal from enum import Enum class Test1(Enum): @@ -2662,8 +2647,7 @@ expects_test2_foo(final2) [out] [case testLiteralUsingEnumAttributeNamesInLiteralContexts] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal from enum import Enum class Test1(Enum): @@ -2742,8 +2726,7 @@ z: Literal[~0] = 0 # E: Invalid type: Literal[...] cannot contain arbitrary exp [builtins fixtures/ops.pyi] [case testNegativeIntLiteralWithFinal] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal ONE: Final = 1 x: Literal[-1] = -ONE @@ -2851,8 +2834,7 @@ else: [case testLiteralAndInstanceSubtyping] # https://github.com/python/mypy/issues/7399 # https://github.com/python/mypy/issues/11232 -from typing import Literal, Tuple, Union -from typing_extensions import Final +from typing import Final, Literal, Tuple, Union x: bool diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index d9dda17b7b78..1856ca26f736 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -672,8 +672,7 @@ if not (abo is None or abo.tag != "B"): [case testNarrowingEqualityFlipFlop] # flags: --warn-unreachable --strict-equality -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal from enum import Enum class State(Enum): @@ -738,8 +737,7 @@ def test3(switch: FlipFlopEnum) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitStrLiteral] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal A_final: Final = "A" A_literal: Literal["A"] @@ -785,8 +783,7 @@ reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B' [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitEnumLiteral] -from typing import Literal, Union -from typing_extensions import Final +from typing import Final, Literal, Union from enum import Enum class Foo(Enum): @@ -1549,8 +1546,7 @@ if len(x) == len(y) == 3: [builtins fixtures/len.pyi] [case testNarrowingLenFinal] -from typing import Tuple, Union -from typing_extensions import Final +from typing import Final, Tuple, Union VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 814e47f09634..9250f3cea0a6 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2528,8 +2528,7 @@ tmp/unittest/suite.pyi:6: error: Name "Iterable" is not defined tmp/unittest/suite.pyi:6: note: Did you forget to import it from "typing"? (Suggestion: "from typing import Iterable") [case testNewAnalyzerNewTypeSpecialCase] -from typing import Literal, NewType -from typing_extensions import Final +from typing import Final, Literal, NewType X = NewType('X', int) @@ -2777,8 +2776,7 @@ class C: reveal_type(C.A) # N: Revealed type is "def () -> a.A" [case testNewAnalyzerFinalLiteralInferredAsLiteralWithDeferral] -from typing import Literal -from typing_extensions import Final +from typing import Final, Literal defer: Yes diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 9813df63b1f6..1400f3b152ec 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2767,8 +2767,7 @@ p: P = N(lambda a, b, c: 'foo') [builtins fixtures/property.pyi] [case testLiteralsAgainstProtocols] -from typing import Literal, SupportsInt, SupportsAbs, TypeVar -from typing_extensions import Final +from typing import Final, Literal, SupportsInt, SupportsAbs, TypeVar T = TypeVar('T') def abs(x: SupportsAbs[T]) -> T: ... @@ -3970,7 +3969,7 @@ func(some_module) # E: Argument 1 to "func" has incompatible type Module; expec # N: Protocol member My.a expected settable variable, got read-only attribute [file some_module.py] -from typing_extensions import Final +from typing import Final a: Final = 1 [builtins fixtures/module.pyi] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index f9317c5ba4b1..0ba7ffc82eca 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1906,8 +1906,7 @@ match var: [builtins fixtures/tuple.pyi] [case testMatchNamedAndKeywordsAreTheSame] -from typing import Generic, TypeVar, Union -from typing_extensions import Final +from typing import Generic, Final, TypeVar, Union from dataclasses import dataclass T = TypeVar("T") diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index c8de09138b8f..f90baed0eb16 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -211,8 +211,7 @@ h(arg=0) # E: Unexpected keyword argument "arg" for "h" i(arg=0) # E: Unexpected keyword argument "arg" [case testWalrus] -from typing import NamedTuple, Optional, List -from typing_extensions import Final +from typing import Final, NamedTuple, Optional, List if a := 2: reveal_type(a) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 44f361286737..c2b734b4b923 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -2066,8 +2066,7 @@ v = {bad2: 2} # E: Missing key "num" for TypedDict "Value" \ [case testOperatorContainsNarrowsTypedDicts_unionWithList] from __future__ import annotations -from typing import assert_type, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union @final class D(TypedDict): @@ -2084,12 +2083,11 @@ else: assert_type(d_or_list, list[str]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_total] from __future__ import annotations -from typing import assert_type, Literal, TypedDict, TypeVar, Union -from typing_extensions import final +from typing import assert_type, final, Literal, TypedDict, TypeVar, Union @final class D1(TypedDict): @@ -2135,13 +2133,12 @@ def f(arg: TD) -> None: [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_final] # flags: --warn-unreachable from __future__ import annotations -from typing import assert_type, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union @final class DFinal(TypedDict): @@ -2179,12 +2176,11 @@ else: assert_type(d_union, DNotFinal) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_partialThroughTotalFalse] from __future__ import annotations -from typing import assert_type, Literal, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, Literal, TypedDict, Union @final class DTotal(TypedDict): @@ -2215,12 +2211,12 @@ else: assert_type(d, Union[DTotal, DNotTotal]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_partialThroughNotRequired] from __future__ import annotations -from typing import assert_type, Required, NotRequired, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union +from typing_extensions import Required, NotRequired @final class D1(TypedDict): @@ -2247,11 +2243,10 @@ else: assert_type(d, Union[D1, D2]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testCannotSubclassFinalTypedDict] -from typing import TypedDict -from typing_extensions import final +from typing import TypedDict, final @final class DummyTypedDict(TypedDict): @@ -2263,11 +2258,10 @@ class SubType(DummyTypedDict): # E: Cannot inherit from final class "DummyTypedD pass [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testCannotSubclassFinalTypedDictWithForwardDeclarations] -from typing import TypedDict -from typing_extensions import final +from typing import TypedDict, final @final class DummyTypedDict(TypedDict): @@ -2279,7 +2273,7 @@ class SubType(DummyTypedDict): # E: Cannot inherit from final class "DummyTypedD class ForwardDeclared: pass [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testTypedDictTypeNarrowingWithFinalKey] from typing import Final, Optional, TypedDict diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index c06b9ccb97d7..d2b1a8a92b80 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -8889,13 +8889,13 @@ foo: Final = bar [file mod2.py] from mod3 import qux as bar [file mod3.py] -from typing_extensions import Final +from typing import Final qux: Final = 3 [file mod3.py.2] -from typing_extensions import Final +from typing import Final qux: Final = 4 [file mod3.py.3] -from typing_extensions import Final +from typing import Final qux: Final[int] = 4 [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index c143805f4564..f828e2a3263f 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -1127,7 +1127,7 @@ MypyFile:1( IntExpr(1))))) [case testConstantFold1] -from typing_extensions import Final +from typing import Final add: Final = 15 + 47 add_mul: Final = (2 + 3) * 5 sub: Final = 7 - 11 @@ -1140,7 +1140,7 @@ lshift0: Final = 5 << 0 rshift0: Final = 13 >> 0 [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Final]) + ImportFrom:1(typing, [Final]) AssignmentStmt:2( NameExpr(add [__main__.add] = 62) OpExpr:2( @@ -1216,7 +1216,7 @@ MypyFile:1( Literal[13]?)) [case testConstantFold2] -from typing_extensions import Final +from typing import Final neg1: Final = -5 neg2: Final = --1 neg3: Final = -0 @@ -1231,7 +1231,7 @@ p3: Final = 0**0 s: Final = 'x' + 'y' [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Final]) + ImportFrom:1(typing, [Final]) AssignmentStmt:2( NameExpr(neg1 [__main__.neg1] = -5) UnaryExpr:2( From 1edb1d24fc25d2e3c9630830d943881ab37e39b5 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:34:36 +0100 Subject: [PATCH 1135/1617] Remove bogus TODO added in #18585 (#18603) Removes todo added in #18585 - such normalization is not technically correct when a tuple is used as a base class. --------- Co-authored-by: Ivan Levkivskyi --- mypy/checker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 462bd1f6d2e1..25ff3734c908 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8517,7 +8517,6 @@ def visit_type_var(self, t: TypeVarType) -> bool: def visit_tuple_type(self, t: TupleType, /) -> bool: # Exclude fallback to avoid bogus "need type annotation" errors - # TODO: Maybe erase plain tuples used as fallback in TupleType constructor? return self.query_types(t.items) From f7f6bc2062e24a4aa3c491f414174118a45b06ff Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:42:45 +0100 Subject: [PATCH 1136/1617] Add initial changelog for 1.16 (#18652) Create changelog entries for - #18510 - #18641 --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c772dc7c04..5cc87cae5065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,48 @@ ## Next Release -... +### Different Property Getter and Setter Types + +Mypy now supports using different types for property getter and setter. +```python +class A: + value: int + + @property + def f(self) -> int: + return self.value + @f.setter + def f(self, x: str | int) -> None: + try: + self.value = int(x) + except ValueError: + raise Exception(f"'{x}' is not a valid value for 'f'") +``` + +Contributed by Ivan Levkivskyi (PR [18510](https://github.com/python/mypy/pull/18510)) + +### Selectively Disable Deprecated Warnings + +It's now possible to selectively disable warnings generated from +[`warnings.deprecated`](https://docs.python.org/3/library/warnings.html#warnings.deprecated) +using the [`--deprecated-calls-exclude`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-deprecated-calls-exclude) +option. + +```python +# mypy --enable-error-code deprecated +# --deprecated-calls-exclude=foo.A +import foo + +foo.A().func() # OK, the deprecated warning is ignored + +# file foo.py +from typing_extensions import deprecated +class A: + @deprecated("Use A.func2 instead") + def func(self): pass +``` + +Contributed by Marc Mueller (PR [18641](https://github.com/python/mypy/pull/18641)) ## Mypy 1.15 From 562e9fa1426b07a318da794dc858e8ceadb0ffce Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:23:57 +0100 Subject: [PATCH 1137/1617] Update a few more imports in tests (#18655) --- mypyc/test-data/irbuild-match.test | 2 +- mypyc/test-data/run-classes.test | 2 +- mypyc/test-data/run-misc.test | 3 +-- mypyc/test-data/run-multimodule.test | 4 ++-- test-data/unit/check-deprecated.test | 28 ++++++++++++------------- test-data/unit/check-errorcodes.test | 2 +- test-data/unit/check-expressions.test | 12 +++++------ test-data/unit/check-modules.test | 3 +-- test-data/unit/check-namedtuple.test | 7 +++---- test-data/unit/check-protocols.test | 5 ++--- test-data/unit/check-selftype.test | 6 ++---- test-data/unit/check-semanal-error.test | 4 ++-- 12 files changed, 35 insertions(+), 43 deletions(-) diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index 57d9e5c22d40..28aff3dcfc45 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -1729,7 +1729,7 @@ L6: unreachable [case testMatchLiteralMatchArgs_python3_10] -from typing_extensions import Literal +from typing import Literal class Foo: __match_args__: tuple[Literal["foo"]] = ("foo",) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 127f67902b7d..5d7aadb15045 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -1068,7 +1068,7 @@ assert b.z is None assert not hasattr(b, 'bogus') [case testProtocol] -from typing_extensions import Protocol +from typing import Protocol class Proto(Protocol): def foo(self, x: int) -> None: diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 94d8ffb41e4e..a08be091bcc3 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -612,8 +612,7 @@ for a in sorted(s): 9 8 72 [case testDummyTypes] -from typing import Tuple, List, Dict, Literal, NamedTuple, TypedDict -from typing_extensions import NewType +from typing import Tuple, List, Dict, Literal, NamedTuple, NewType, TypedDict class A: pass diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 11e898b45572..5112e126169f 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -495,7 +495,7 @@ class Bar: bar(self) [file other.py] -from typing_extensions import TYPE_CHECKING +from typing import TYPE_CHECKING MYPY = False if MYPY: from native import Foo @@ -525,7 +525,7 @@ def f(c: 'C') -> int: return c.x [file other.py] -from typing_extensions import TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: from native import D diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index c6953122d788..6cc160fad81f 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -380,8 +380,8 @@ for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration [case testDeprecatedOverloadedInstanceMethods] # flags: --enable-error-code=deprecated -from typing import Iterator, Union -from typing_extensions import deprecated, overload +from typing import Iterator, Union, overload +from typing_extensions import deprecated class A: @overload @@ -429,8 +429,8 @@ b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead [case testDeprecatedOverloadedClassMethods] # flags: --enable-error-code=deprecated -from typing import Iterator, Union -from typing_extensions import deprecated, overload +from typing import Iterator, Union, overload +from typing_extensions import deprecated class A: @overload @@ -487,8 +487,8 @@ b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead [case testDeprecatedOverloadedStaticMethods] # flags: --enable-error-code=deprecated -from typing import Iterator, Union -from typing_extensions import deprecated, overload +from typing import Iterator, Union, overload +from typing_extensions import deprecated class A: @overload @@ -545,8 +545,8 @@ b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead [case testDeprecatedOverloadedSpecialMethods] # flags: --enable-error-code=deprecated -from typing import Iterator, Union -from typing_extensions import deprecated, overload +from typing import Iterator, Union, overload +from typing_extensions import deprecated class A: @overload @@ -671,8 +671,8 @@ C().g = "x" # E: function __main__.C.g is deprecated: use g2 instead \ [case testDeprecatedDescriptor] # flags: --enable-error-code=deprecated -from typing import Any, Optional, Union -from typing_extensions import deprecated, overload +from typing import Any, Optional, Union, overload +from typing_extensions import deprecated @deprecated("use E1 instead") class D1: @@ -725,8 +725,8 @@ c.d3 = "x" # E: overload def (self: __main__.D3, obj: __main__.C, value: builti [case testDeprecatedOverloadedFunction] # flags: --enable-error-code=deprecated -from typing import Union -from typing_extensions import deprecated, overload +from typing import Union, overload +from typing_extensions import deprecated @overload def f(x: int) -> int: ... @@ -788,8 +788,8 @@ m.g("x") [file m.py] -from typing import Union -from typing_extensions import deprecated, overload +from typing import Union, overload +from typing_extensions import deprecated @overload @deprecated("work with str instead") diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 45b9dced046d..6ec246fb3a13 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -652,7 +652,7 @@ def g() -> int: x: List[int] # type: ignore[name-defined] [case testErrorCodeProtocolProblemsIgnore] -from typing_extensions import Protocol +from typing import Protocol class P(Protocol): def f(self, x: str) -> None: ... diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 8dd589937df8..81eb4c7c0dc8 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1010,25 +1010,23 @@ y: Gen[Literal[1]] = assert_type(Gen(1), Gen[Literal[1]]) [builtins fixtures/tuple.pyi] [case testAssertTypeUncheckedFunction] -from typing import assert_type -from typing_extensions import Literal +from typing import Literal, assert_type def f(): x = 42 assert_type(x, Literal[42]) [out] -main:5: error: Expression is of type "Any", not "Literal[42]" -main:5: note: "assert_type" expects everything to be "Any" in unchecked functions +main:4: error: Expression is of type "Any", not "Literal[42]" +main:4: note: "assert_type" expects everything to be "Any" in unchecked functions [builtins fixtures/tuple.pyi] [case testAssertTypeUncheckedFunctionWithUntypedCheck] # flags: --check-untyped-defs -from typing import assert_type -from typing_extensions import Literal +from typing import Literal, assert_type def f(): x = 42 assert_type(x, Literal[42]) [out] -main:6: error: Expression is of type "int", not "Literal[42]" +main:5: error: Expression is of type "int", not "Literal[42]" [builtins fixtures/tuple.pyi] [case testAssertTypeNoPromoteUnion] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 9e99a1ca5cf0..87eb25a48cc2 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -519,8 +519,7 @@ def bar(x: Both, y: Both = ...) -> Both: [out] [case testEllipsisDefaultArgValueInNonStubsMethods] -from typing import Generic, TypeVar -from typing_extensions import Protocol +from typing import Generic, Protocol, TypeVar from abc import abstractmethod T = TypeVar('T') diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index a65a99cc25d0..b8a753b3c90a 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -69,8 +69,7 @@ a.y = 5 # E: Property "y" defined in "X" is read-only [case testTypingNamedTupleAttributesAreReadOnly] -from typing import NamedTuple -from typing_extensions import Protocol +from typing import NamedTuple, Protocol class HasX(Protocol): x: str @@ -82,8 +81,8 @@ a: HasX = A("foo") a.x = "bar" [builtins fixtures/tuple.pyi] [out] -main:10: error: Incompatible types in assignment (expression has type "A", variable has type "HasX") -main:10: note: Protocol member HasX.x expected settable variable, got read-only attribute +main:9: error: Incompatible types in assignment (expression has type "A", variable has type "HasX") +main:9: note: Protocol member HasX.x expected settable variable, got read-only attribute [case testNamedTupleCreateWithPositionalArguments] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 1400f3b152ec..a7124b7a83d3 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2339,8 +2339,7 @@ main:19: note: Protocol member AllSettable.b expected settable variable, got rea main:19: note: <2 more conflict(s) not shown> [case testProtocolsMoreConflictsNotShown] -from typing_extensions import Protocol -from typing import Generic, TypeVar +from typing import Generic, Protocol, TypeVar T = TypeVar('T') @@ -2862,7 +2861,7 @@ c1: SupportsClassGetItem = C() [case testNoneVsProtocol] # mypy: strict-optional -from typing_extensions import Protocol +from typing import Protocol class MyHashable(Protocol): def __hash__(self) -> int: ... diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 1ac5924262b3..4c49bd7093cd 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -856,8 +856,7 @@ BadSub().get_item() # E: Invalid self argument "BadSub" to attribute function " [builtins fixtures/list.pyi] [case testMixinAllowedWithProtocol] -from typing import TypeVar -from typing_extensions import Protocol +from typing import Protocol, TypeVar class Resource(Protocol): def close(self) -> int: ... @@ -908,8 +907,7 @@ class Bad: class CC(TweakFunc, Bad): pass # E: Definition of "func" in base class "TweakFunc" is incompatible with definition in base class "Bad" [case testBadClassLevelDecoratorHack] -from typing_extensions import Protocol -from typing import TypeVar, Any +from typing import Protocol, TypeVar, Any class FuncLike(Protocol): __call__: Any diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index d7ab272aed6c..52abbf09f1e5 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -137,8 +137,7 @@ def f() -> None: ... # E: Name "f" already defined (possibly by an import) [out] [case testRuntimeProtoTwoBases] -from typing_extensions import Protocol, runtime_checkable -from typing import TypeVar, Generic +from typing import TypeVar, Generic, Protocol, runtime_checkable T = TypeVar('T') @@ -151,6 +150,7 @@ class C: x: P[int] = C() [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testSemanalDoesNotLeakSyntheticTypes] # flags: --cache-fine-grained From a5abc776cba083ed80d3265adafa6a84344cd972 Mon Sep 17 00:00:00 2001 From: Mattias Ellert Date: Tue, 11 Feb 2025 13:29:02 +0100 Subject: [PATCH 1138/1617] GNU/Hurd returns empty string from getsockname() for AF_UNIX sockets (#18630) Build the socket name from directory name and name instead. This fixes existing failing tests on Debian GNU/Hurd: * mypy/test/testdaemon.py::DaemonSuite::* * mypy/test/testipc.py::IPCTests::* --- mypy/ipc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/ipc.py b/mypy/ipc.py index 991f9ac56652..b2046a47ab15 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -303,6 +303,10 @@ def cleanup(self) -> None: def connection_name(self) -> str: if sys.platform == "win32": return self.name + elif sys.platform == "gnu0": + # GNU/Hurd returns empty string from getsockname() + # for AF_UNIX sockets + return os.path.join(self.sock_directory, self.name) else: name = self.sock.getsockname() assert isinstance(name, str) From 9665c3278b19e06997087bf400db29b73b2fe368 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 12 Feb 2025 05:38:53 +0000 Subject: [PATCH 1139/1617] Delete old meet hack from checkmember.py (#18662) The hack to use `meet_types(original_type, itype)` to select a correct element from a union appeared before we added proper handling of unions in various places related to `checkmember.py`. This is error prone, since `meet_types()` is one of least precise type ops (for good and bad reasons), and results in obscure bugs, see e.g. https://github.com/python/mypy/issues/15600 This hack should not be needed anymore, now we have three-level information available everywhere we needed it: * `original_type` - as the name says, a type from which everything started. This is used for error messages and for plugin hooks. * `self_type` - a specific element of the union is the original type is a union. The name is because this is what will be ultimately used by `bind_self()` * `itype` the actual instance type where we look up the attribute (this will be e.g. a fallback if the `self_type` is not an instance) --- mypy/checker.py | 4 +-- mypy/checkmember.py | 74 ++++++++++++--------------------------------- 2 files changed, 21 insertions(+), 57 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 25ff3734c908..70df1575515c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4600,10 +4600,8 @@ def check_member_assignment( bound_method = analyze_decorator_or_funcbase_access( defn=dunder_set, itype=attribute_type, - info=attribute_type.type, - self_type=attribute_type, name="__set__", - mx=mx, + mx=mx.copy_modified(self_type=attribute_type), ) typ = map_instance_to_supertype(attribute_type, dunder_set.info) dunder_set_type = expand_type_by_instance(bound_method, typ) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 515f0c12c5b9..206a678a7d25 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -5,7 +5,7 @@ from collections.abc import Sequence from typing import TYPE_CHECKING, Callable, cast -from mypy import meet, message_registry, subtypes +from mypy import message_registry, subtypes from mypy.erasetype import erase_typevars from mypy.expandtype import ( expand_self_type, @@ -267,7 +267,9 @@ 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.copy_modified(self_type=aw_type), override_info + ) return not local_errors.has_new_errors() @@ -323,7 +325,7 @@ def analyze_instance_member_access( assert isinstance(getter, Decorator) if mx.is_lvalue and (len(items := method.items) > 1): mx.chk.warn_deprecated(items[1], mx.context) - return analyze_var(name, getter.var, typ, info, mx) + return analyze_var(name, getter.var, typ, mx) if mx.is_lvalue: mx.msg.cant_assign_to_method(mx.context) @@ -340,11 +342,8 @@ def analyze_instance_member_access( signature = method.type signature = freshen_all_functions_type_vars(signature) if not method.is_static: - # TODO: use proper treatment of special methods on unions instead - # of this hack here and below (i.e. mx.self_type). - dispatched_type = meet.meet_types(mx.original_type, typ) signature = check_self_arg( - signature, dispatched_type, method.is_class, mx.context, name, mx.msg + signature, mx.self_type, method.is_class, mx.context, name, mx.msg ) signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class) # TODO: should we skip these steps for static methods as well? @@ -536,7 +535,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, mx, implicit=implicit) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif isinstance(v, MypyFile): @@ -560,12 +559,7 @@ def analyze_member_var_access( # that the attribute exists if method and method.info.fullname != "builtins.object": bound_method = analyze_decorator_or_funcbase_access( - defn=method, - itype=itype, - info=info, - self_type=mx.self_type, - name=method_name, - mx=mx, + defn=method, itype=itype, name=method_name, mx=mx ) typ = map_instance_to_supertype(itype, method.info) getattr_type = get_proper_type(expand_type_by_instance(bound_method, typ)) @@ -592,12 +586,7 @@ def analyze_member_var_access( setattr_meth = info.get_method("__setattr__") if setattr_meth and setattr_meth.info.fullname != "builtins.object": bound_type = analyze_decorator_or_funcbase_access( - defn=setattr_meth, - itype=itype, - info=info, - self_type=mx.self_type, - name=name, - mx=mx.copy_modified(is_lvalue=False), + defn=setattr_meth, itype=itype, name=name, mx=mx.copy_modified(is_lvalue=False) ) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = get_proper_type(expand_type_by_instance(bound_type, typ)) @@ -683,10 +672,8 @@ def analyze_descriptor_access( bound_method = analyze_decorator_or_funcbase_access( defn=dunder_get, itype=descriptor_type, - info=descriptor_type.type, - self_type=descriptor_type, name="__get__", - mx=mx, + mx=mx.copy_modified(self_type=descriptor_type), ) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) @@ -762,13 +749,7 @@ def is_instance_var(var: Var) -> bool: def analyze_var( - name: str, - var: Var, - itype: Instance, - info: TypeInfo, - mx: MemberContext, - *, - implicit: bool = False, + name: str, var: Var, itype: Instance, mx: MemberContext, *, implicit: bool = False ) -> Type: """Analyze access to an attribute via a Var node. @@ -807,7 +788,9 @@ def analyze_var( if isinstance(typ, FunctionLike) and not typ.is_type_obj(): call_type = typ elif var.is_property: - call_type = get_proper_type(_analyze_member_access("__call__", typ, mx)) + call_type = get_proper_type( + _analyze_member_access("__call__", typ, mx.copy_modified(self_type=typ)) + ) else: call_type = typ @@ -823,20 +806,12 @@ def analyze_var( # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. functype: FunctionLike = call_type - # Use meet to narrow original_type to the dispatched type. - # For example, assume - # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) - # * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B) - # * x: Union[A1, B1] - # In `x.f`, when checking `x` against A1 we assume x is compatible with A - # and similarly for B1 when checking against B - dispatched_type = meet.meet_types(mx.original_type, itype) signature = freshen_all_functions_type_vars(functype) bound = get_proper_type(expand_self_type(var, signature, mx.original_type)) assert isinstance(bound, FunctionLike) signature = bound signature = check_self_arg( - signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg + signature, mx.self_type, var.is_classmethod, mx.context, name, mx.msg ) signature = bind_self(signature, mx.self_type, var.is_classmethod) expanded_signature = expand_type_by_instance(signature, itype) @@ -946,13 +921,9 @@ def check_self_arg( For example if the method is defined as: class A: def f(self: S) -> T: ... - then for 'x.f' we check that meet(type(x), A) <: S. If the method is overloaded, we - select only overloads items that satisfy this requirement. If there are no matching + then for 'x.f' we check that type(x) <: S. If the method is overloaded, we select + only overloads items that satisfy this requirement. If there are no matching overloads, an error is generated. - - Note: dispatched_arg_type uses a meet to select a relevant item in case if the - original type of 'x' is a union. This is done because several special methods - treat union types in ad-hoc manner, so we can't use MemberContext.self_type yet. """ items = functype.items if not items: @@ -1436,12 +1407,7 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P def analyze_decorator_or_funcbase_access( - defn: Decorator | FuncBase, - itype: Instance, - info: TypeInfo, - self_type: Type | None, - name: str, - mx: MemberContext, + defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext ) -> Type: """Analyzes the type behind method access. @@ -1449,9 +1415,9 @@ 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, mx) return bind_self( - function_type(defn, mx.chk.named_type("builtins.function")), original_type=self_type + function_type(defn, mx.chk.named_type("builtins.function")), original_type=mx.self_type ) From 44f82eff158315c86430e8fd14968234838f9692 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 11 Feb 2025 22:14:20 -0800 Subject: [PATCH 1140/1617] Fix regression for user config files (#18656) Fixes #18650 Slightly annoying to add a test for since it would clutter up user home directory --- mypy/defaults.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/defaults.py b/mypy/defaults.py index 67628d544edf..45ad6fe3076c 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -17,9 +17,10 @@ CONFIG_NAMES: Final = ["mypy.ini", ".mypy.ini"] SHARED_CONFIG_NAMES: Final = ["pyproject.toml", "setup.cfg"] -USER_CONFIG_FILES: Final = ["~/.config/mypy/config", "~/.mypy.ini"] +USER_CONFIG_FILES: list[str] = ["~/.config/mypy/config", "~/.mypy.ini"] if os.environ.get("XDG_CONFIG_HOME"): USER_CONFIG_FILES.insert(0, os.path.join(os.environ["XDG_CONFIG_HOME"], "mypy/config")) +USER_CONFIG_FILES = [os.path.expanduser(f) for f in USER_CONFIG_FILES] # This must include all reporters defined in mypy.report. This is defined here # to make reporter names available without importing mypy.report -- this speeds From 8bdc4af8524d1c799043283fab35a64365e68cf1 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:44:08 +0100 Subject: [PATCH 1141/1617] Add codespell to pre-commit config (#18645) Replaces #18387 Use codespell in pre-commit to detect spelling mistakes, see #18642. Ignore test and typeshed folders. --- .pre-commit-config.yaml | 7 +++++++ mypy/fastparse.py | 4 ++-- mypy/nodes.py | 12 ++++++------ mypy/stubinfo.py | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2319b3925bc..3d4896c95b3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,13 @@ repos: - id: check-github-workflows - id: check-github-actions - id: check-readthedocs + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + args: + - --ignore-words-list=HAX,ccompiler,ot,statics,whet,zar + exclude: ^(mypy/test/|mypy/typeshed/|mypyc/test-data/|test-data/).+$ - repo: https://github.com/rhysd/actionlint rev: v1.7.7 hooks: diff --git a/mypy/fastparse.py b/mypy/fastparse.py index a58ebbcaded1..b9a55613ec16 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -557,7 +557,7 @@ def from_operator(self, op: ast3.operator) -> str: ast3.Is: "is", ast3.IsNot: "is not", ast3.In: "in", - ast3.NotIn: "not in", + ast3.NotIn: "not in", # codespell:ignore notin } def from_comp_operator(self, op: ast3.cmpop) -> str: @@ -2169,7 +2169,7 @@ def visit_member_expr(self, e: MemberExpr) -> None: class FindYield(TraverserVisitor): - """Check if an AST contains yields or yield froms.""" + """Check if an AST contains yields or yield froms.""" # codespell:ignore froms def __init__(self) -> None: self.found = False diff --git a/mypy/nodes.py b/mypy/nodes.py index ff79c0494fc3..2b6bf25918d9 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -176,15 +176,15 @@ class Node(Context): __slots__ = () def __str__(self) -> str: - ans = self.accept(mypy.strconv.StrConv(options=Options())) - if ans is None: + a = self.accept(mypy.strconv.StrConv(options=Options())) + if a is None: return repr(self) - return ans + return a def str_with_options(self, options: Options) -> str: - ans = self.accept(mypy.strconv.StrConv(options=options)) - assert ans - return ans + a = self.accept(mypy.strconv.StrConv(options=options)) + assert a + return a def accept(self, visitor: NodeVisitor[T]) -> T: raise RuntimeError("Not implemented", type(self)) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 77426bb09b7b..91755b2b5041 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -97,7 +97,7 @@ def stub_distribution_name(module: str) -> str | None: "commctrl": "types-pywin32", "commonmark": "types-commonmark", "consolemenu": "types-console-menu", - "corus": "types-corus", + "corus": "types-corus", # codespell:ignore corus "cronlog": "types-python-crontab", "crontab": "types-python-crontab", "crontabs": "types-python-crontab", From 306ff8bfd35ca11b4307ab60f30a684b00fc2bd7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 12 Feb 2025 11:30:29 +0000 Subject: [PATCH 1142/1617] Fix handling of named tuples in class match pattern (#18663) Fixes https://github.com/python/mypy/issues/15299 The fix is straightforward, named tuples should be properly represented as tuples with fallback, not as instances. --- mypy/checkpattern.py | 15 ++++----------- test-data/unit/check-python310.test | 30 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 4b34c0ddb54b..2a8620482d87 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -54,7 +54,7 @@ get_proper_type, split_with_prefix_and_suffix, ) -from mypy.typevars import fill_typevars +from mypy.typevars import fill_typevars, fill_typevars_with_any from mypy.visitor import PatternVisitor self_match_type_names: Final = [ @@ -544,16 +544,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o) return self.early_non_match() if isinstance(type_info, TypeInfo): - any_type = AnyType(TypeOfAny.implementation_artifact) - args: list[Type] = [] - for tv in type_info.defn.type_vars: - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType(self.chk.named_generic_type("builtins.tuple", [any_type])) - ) - else: - args.append(any_type) - typ: Type = Instance(type_info, args) + typ: Type = fill_typevars_with_any(type_info) elif isinstance(type_info, TypeAlias): typ = type_info.target elif ( @@ -703,6 +694,8 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: def should_self_match(self, typ: Type) -> bool: typ = get_proper_type(typ) + if isinstance(typ, TupleType): + typ = typ.partial_fallback if isinstance(typ, Instance) and typ.type.get("__match_args__") is not None: # Named tuples and other subtypes of builtins that define __match_args__ # should not self match. diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 0ba7ffc82eca..016f50552a5f 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -770,6 +770,21 @@ match m: reveal_type(j) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] +[case testMatchSequencePatternCaptureNamedTuple] +from typing import NamedTuple + +class N(NamedTuple): + x: int + y: str + +a = N(1, "a") + +match a: + case [x, y]: + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + [case testMatchClassPatternCaptureGeneric] from typing import Generic, TypeVar @@ -2522,3 +2537,18 @@ def fn2(x: Some | int | str) -> None: case Some(value): # E: Incompatible types in capture pattern (pattern captures type "Union[int, str]", variable has type "Callable[[], str]") pass [builtins fixtures/dict.pyi] + +[case testMatchNamedTupleSequence] +from typing import Any, NamedTuple + +class T(NamedTuple): + t: list[Any] + +class K(NamedTuple): + k: int + +def f(t: T) -> None: + match t: + case T([K() as k]): + reveal_type(k) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.K]" +[builtins fixtures/tuple.pyi] From 2831eb1dcdc93ecf6c86116dec7e2e6dcffcb10a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 12 Feb 2025 19:49:20 +0000 Subject: [PATCH 1143/1617] Better handling of Any/object in variadic generics (#18643) Fixes https://github.com/python/mypy/issues/18407 Fixes https://github.com/python/mypy/issues/17184 Fixes https://github.com/python/mypy/issues/16567 There are three things here: * Allow erased variadic callables with non-empty prefix to be supertypes of the non-erased ones. This relaxes a bit callable subtyping in general, but IMO this makes sense, people who want to be strict should simply use `*args: object` instead. An alternative would be to track erased variadic callables explicitly, which is ugly and fragile. * Add important missing case in `subtypes.py` for `*Ts` w.r.t. `Any`/`object` that handles similar situations for variadic instances and tuples (here however there is nothing special about `Any` vs `object`). * I also fix inconsistency in join uncovered by the above two. The changes in `expandtype.py` are no-op, I just noticed potential danger while playing with this, so wanted to highlight it with comments for the future. --- mypy/erasetype.py | 8 ++ mypy/expandtype.py | 12 ++- mypy/join.py | 9 ++ mypy/subtypes.py | 47 ++++++++--- mypy/test/testtypes.py | 11 +-- test-data/unit/check-typevar-tuple.test | 105 ++++++++++++++++++++++-- 6 files changed, 164 insertions(+), 28 deletions(-) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 0e6a8bf8a829..6c47670d6687 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -203,6 +203,14 @@ def visit_tuple_type(self, t: TupleType) -> Type: return unpacked return result + def visit_callable_type(self, t: CallableType) -> Type: + result = super().visit_callable_type(t) + assert isinstance(result, ProperType) and isinstance(result, CallableType) + # Usually this is done in semanal_typeargs.py, but erasure can create + # a non-normal callable from normal one. + result.normalize_trivial_unpack() + return result + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: if self.erase_id(t.id): return t.tuple_fallback.copy_modified(args=[self.replacement]) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 8750da34d963..031f86e7dfff 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -226,6 +226,8 @@ def visit_instance(self, t: Instance) -> Type: if isinstance(arg, UnpackType): unpacked = get_proper_type(arg.type) if isinstance(unpacked, Instance): + # TODO: this and similar asserts below may be unsafe because get_proper_type() + # may be called during semantic analysis before all invalid types are removed. assert unpacked.type.fullname == "builtins.tuple" args = list(unpacked.args) return t.copy_modified(args=args) @@ -333,10 +335,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l var_arg_type = get_proper_type(var_arg.type) new_unpack: Type - if isinstance(var_arg_type, Instance): - # we have something like Unpack[Tuple[Any, ...]] - new_unpack = UnpackType(var_arg.type.accept(self)) - elif isinstance(var_arg_type, TupleType): + if isinstance(var_arg_type, TupleType): # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] expanded_tuple = var_arg_type.accept(self) assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) @@ -348,6 +347,11 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l fallback = var_arg_type.tuple_fallback expanded_items = self.expand_unpack(var_arg) new_unpack = UnpackType(TupleType(expanded_items, fallback)) + # Since get_proper_type() may be called in semanal.py before callable + # normalization happens, we need to also handle non-normal cases here. + elif isinstance(var_arg_type, Instance): + # we have something like Unpack[Tuple[Any, ...]] + new_unpack = UnpackType(var_arg.type.accept(self)) else: # We have invalid type in Unpack. This can happen when expanding aliases # to Callable[[*Invalid], Ret] diff --git a/mypy/join.py b/mypy/join.py index 9a13dfb42b64..ac01d11d11d6 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -299,6 +299,9 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: if self.s == t: return t + if isinstance(self.s, Instance) and is_subtype(t.upper_bound, self.s): + # TODO: should we do this more generally and for all TypeVarLikeTypes? + return self.s return self.default(self.s) def visit_unpack_type(self, t: UnpackType) -> UnpackType: @@ -350,6 +353,8 @@ def visit_instance(self, t: Instance) -> ProperType: return join_types(t, self.s) elif isinstance(self.s, LiteralType): return join_types(t, self.s) + elif isinstance(self.s, TypeVarTupleType) and is_subtype(self.s.upper_bound, t): + return t else: return self.default(self.s) @@ -562,6 +567,10 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: assert isinstance(fallback, Instance) items = self.join_tuples(self.s, t) if items is not None: + if len(items) == 1 and isinstance(item := items[0], UnpackType): + if isinstance(unpacked := get_proper_type(item.type), Instance): + # Avoid double-wrapping tuple[*tuple[X, ...]] + return unpacked return TupleType(items, fallback) else: # TODO: should this be a default fallback behaviour like for meet? diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 75cc7e25fde3..938be21201e9 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1,6 +1,6 @@ from __future__ import annotations -from collections.abc import Iterator +from collections.abc import Iterable, Iterator from contextlib import contextmanager from typing import Any, Callable, Final, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias @@ -414,6 +414,9 @@ def _is_subtype(self, left: Type, right: Type) -> bool: return is_proper_subtype(left, right, subtype_context=self.subtype_context) return is_subtype(left, right, subtype_context=self.subtype_context) + def _all_subtypes(self, lefts: Iterable[Type], rights: Iterable[Type]) -> bool: + return all(self._is_subtype(li, ri) for (li, ri) in zip(lefts, rights)) + # visit_x(left) means: is left (which is an instance of X) a subtype of right? def visit_unbound_type(self, left: UnboundType) -> bool: @@ -856,11 +859,25 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: # There are some items on the left that will never have a matching length # on the right. return False + left_prefix = left_unpack_index + left_suffix = len(left.items) - left_prefix - 1 left_unpack = left.items[left_unpack_index] assert isinstance(left_unpack, UnpackType) left_unpacked = get_proper_type(left_unpack.type) if not isinstance(left_unpacked, Instance): - # *Ts unpacks can't be split. + # *Ts unpack can't be split, except if it is all mapped to Anys or objects. + if self.is_top_type(right_item): + right_prefix_types, middle, right_suffix_types = split_with_prefix_and_suffix( + tuple(right.items), left_prefix, left_suffix + ) + if not all( + self.is_top_type(ri) or isinstance(ri, UnpackType) for ri in middle + ): + return False + # Also check the tails match as well. + return self._all_subtypes( + left.items[:left_prefix], right_prefix_types + ) and self._all_subtypes(left.items[-left_suffix:], right_suffix_types) return False assert left_unpacked.type.fullname == "builtins.tuple" left_item = left_unpacked.args[0] @@ -871,8 +888,6 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: # and then check subtyping for all finite overlaps. if not self._is_subtype(left_item, right_item): return False - left_prefix = left_unpack_index - left_suffix = len(left.items) - left_prefix - 1 max_overlap = max(0, right_prefix - left_prefix, right_suffix - left_suffix) for overlap in range(max_overlap + 1): repr_items = left.items[:left_prefix] + [left_item] * overlap @@ -883,6 +898,11 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: return False return True + def is_top_type(self, typ: Type) -> bool: + if not self.proper_subtype and isinstance(get_proper_type(typ), AnyType): + return True + return is_named_instance(typ, "builtins.object") + def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, Instance): @@ -1653,17 +1673,18 @@ def are_parameters_compatible( return True trivial_suffix = is_trivial_suffix(right) and not is_proper_subtype + trivial_vararg_suffix = False if ( - right.arg_kinds == [ARG_STAR] - and isinstance(get_proper_type(right.arg_types[0]), AnyType) + right.arg_kinds[-1:] == [ARG_STAR] + and isinstance(get_proper_type(right.arg_types[-1]), AnyType) and not is_proper_subtype + and all(k.is_positional(star=True) for k in left.arg_kinds) ): # Similar to how (*Any, **Any) is considered a supertype of all callables, we consider # (*Any) a supertype of all callables with positional arguments. This is needed in # particular because we often refuse to try type inference if actual type is not # a subtype of erased template type. - if all(k.is_positional() for k in left.arg_kinds) and ignore_pos_arg_names: - return True + trivial_vararg_suffix = True # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must @@ -1697,7 +1718,11 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N return not allow_partial_overlap and not trivial_suffix return not is_compat(right_arg.typ, left_arg.typ) - if _incompatible(left_star, right_star) or _incompatible(left_star2, right_star2): + if ( + _incompatible(left_star, right_star) + and not trivial_vararg_suffix + or _incompatible(left_star2, right_star2) + ): return False # Phase 1b: Check non-star args: for every arg right can accept, left must @@ -1727,8 +1752,8 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1c: Check var args. Right has an infinite series of optional positional # arguments. Get all further positional args of left, and make sure # they're more general than the corresponding member in right. - # TODO: are we handling UnpackType correctly here? - if right_star is not None: + # TODO: handle suffix in UnpackType (i.e. *args: *Tuple[Ts, X, Y]). + if right_star is not None and not trivial_vararg_suffix: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) assert right_by_position is not None diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 174441237ab4..a42519c64956 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -1021,7 +1021,7 @@ def test_variadic_tuple_joins(self) -> None: self.assert_join( self.tuple(self.fx.a, self.fx.a), self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(self.fx.a, self.fx.a), @@ -1049,12 +1049,12 @@ def test_variadic_tuple_joins(self) -> None: self.tuple( self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a ), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), @@ -1584,11 +1584,12 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 9 + ALLOWED_GET_PROPER_TYPES = 7 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: with open(mypy.expandtype.__file__) as f: code = f.read() - get_proper_type_count = len(re.findall("get_proper_type", code)) + get_proper_type_count = len(re.findall(r"get_proper_type\(", code)) + get_proper_type_count -= len(re.findall(r"get_proper_type\(\)", code)) assert get_proper_type_count == self.ALLOWED_GET_PROPER_TYPES diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index c427a54ea664..2cc84c8e6b15 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2309,18 +2309,21 @@ def higher_order(f: _CallableValue) -> None: ... def good1(*args: int) -> None: ... def good2(*args: str) -> int: ... -def bad1(a: str, b: int, /) -> None: ... -def bad2(c: bytes, *args: int) -> str: ... -def bad3(*, d: str) -> int: ... -def bad4(**kwargs: None) -> None: ... +# These are special-cased for *args: Any (as opposite to *args: object) +def ok1(a: str, b: int, /) -> None: ... +def ok2(c: bytes, *args: int) -> str: ... + +def bad1(*, d: str) -> int: ... +def bad2(**kwargs: None) -> None: ... higher_order(good1) higher_order(good2) -higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(ok1) +higher_order(ok2) + +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[VarArg(Any)], Any]" [builtins fixtures/tuple.pyi] [case testAliasToCallableWithUnpack2] @@ -2517,3 +2520,89 @@ x4: Foo[Unpack[tuple[str, ...]]] y4: Foo[Unpack[tuple[int, int]]] x4 is y4 # E: Non-overlapping identity check (left operand type: "Foo[Unpack[Tuple[str, ...]]]", right operand type: "Foo[int, int]") [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleErasureNormalized] +from typing import TypeVarTuple, Unpack, Generic, Union +from collections.abc import Callable + +Args = TypeVarTuple("Args") + +class Built(Generic[Unpack[Args]]): + pass + +def example( + fn: Union[Built[Unpack[Args]], Callable[[Unpack[Args]], None]] +) -> Built[Unpack[Args]]: ... + +@example +def command() -> None: + return +reveal_type(command) # N: Revealed type is "__main__.Built[()]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleSelfMappedPrefix] +from typing import TypeVarTuple, Generic, Unpack + +Ts = TypeVarTuple("Ts") +class Base(Generic[Unpack[Ts]]): + attr: tuple[Unpack[Ts]] + + @property + def prop(self) -> tuple[Unpack[Ts]]: + return self.attr + + def meth(self) -> tuple[Unpack[Ts]]: + return self.attr + +Ss = TypeVarTuple("Ss") +class Derived(Base[str, Unpack[Ss]]): + def test(self) -> None: + reveal_type(self.attr) # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]" + reveal_type(self.prop) # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]" + reveal_type(self.meth()) # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]" +[builtins fixtures/property.pyi] + +[case testTypeVarTupleProtocolPrefix] +from typing import Protocol, Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +class A(Protocol[Unpack[Ts]]): + def f(self, z: str, *args: Unpack[Ts]) -> None: ... + +class C: + def f(self, z: str, x: int) -> None: ... + +def f(x: A[Unpack[Ts]]) -> tuple[Unpack[Ts]]: ... + +reveal_type(f(C())) # N: Revealed type is "Tuple[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleHomogeneousCallableNormalized] +from typing import Generic, Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): + def foo(self, *args: Unpack[Ts]) -> None: ... + +c: C[Unpack[tuple[int, ...]]] +reveal_type(c.foo) # N: Revealed type is "def (*args: builtins.int)" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleJoinInstanceTypeVar] +from typing import Any, Unpack, TypeVarTuple, TypeVar + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +def join(x: T, y: T) -> T: ... +def test(xs: tuple[Unpack[Ts]], xsi: tuple[int, Unpack[Ts]]) -> None: + a: tuple[Any, ...] + reveal_type(join(xs, a)) # N: Revealed type is "builtins.tuple[Any, ...]" + reveal_type(join(a, xs)) # N: Revealed type is "builtins.tuple[Any, ...]" + aa: tuple[Unpack[tuple[Any, ...]]] + reveal_type(join(xs, aa)) # N: Revealed type is "builtins.tuple[Any, ...]" + reveal_type(join(aa, xs)) # N: Revealed type is "builtins.tuple[Any, ...]" + ai: tuple[int, Unpack[tuple[Any, ...]]] + reveal_type(join(xsi, ai)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" + reveal_type(join(ai, xsi)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" +[builtins fixtures/tuple.pyi] From 03cf35ce7e411dd63a502b558a014cbc26b378c5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 12 Feb 2025 20:05:12 +0000 Subject: [PATCH 1144/1617] Test case for fixed namedtuple method call (#18666) Fixes https://github.com/python/mypy/issues/15600 The issue was previously "fixed" because of another bug. Now that everything is properly fixed, we can add this "regression" test just in case. --- test-data/unit/check-namedtuple.test | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index b8a753b3c90a..22b149174541 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1505,3 +1505,17 @@ def g(x: Union[A, B, str]) -> Union[A, B, str]: # no errors should be raised above. [builtins fixtures/tuple.pyi] + +[case testNamedTupleUnionAnyMethodCall] +from collections import namedtuple +from typing import Any, Union + +T = namedtuple("T", ["x"]) + +class C(T): + def f(self) -> bool: + return True + +c: Union[C, Any] +reveal_type(c.f()) # N: Revealed type is "Union[builtins.bool, Any]" +[builtins fixtures/tuple.pyi] From b07d5f057abf28dfbda6fe1053a95c0e9aed9b7c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 13 Feb 2025 13:28:40 +0000 Subject: [PATCH 1145/1617] Allow lowered opts to be deleted in lowering trasnform (#18669) This only works for simple initialization ops and ops where the return value is ignored. This can be used for dummy init ops that are used to give hints to data flow analysis about lifetimes of values, when initialization is done via a pointer argument (e.g. `init_my_struct(®)` in C). --- mypyc/lower/registry.py | 10 ++++++---- mypyc/transform/ir_transform.py | 3 +++ mypyc/transform/lower.py | 4 +++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py index 3feedfc385ee..a20990fe39ae 100644 --- a/mypyc/lower/registry.py +++ b/mypyc/lower/registry.py @@ -1,20 +1,22 @@ from __future__ import annotations -from typing import Callable, Final +from typing import Callable, Final, Optional, TypeVar from mypyc.ir.ops import Value from mypyc.irbuild.ll_builder import LowLevelIRBuilder LowerFunc = Callable[[LowLevelIRBuilder, list[Value], int], Value] +LowerFuncOpt = Callable[[LowLevelIRBuilder, list[Value], int], Optional[Value]] +lowering_registry: Final[dict[str, LowerFuncOpt]] = {} -lowering_registry: Final[dict[str, LowerFunc]] = {} +LF = TypeVar("LF", LowerFunc, LowerFuncOpt) -def lower_primitive_op(name: str) -> Callable[[LowerFunc], LowerFunc]: +def lower_primitive_op(name: str) -> Callable[[LF], LF]: """Register a handler that generates low-level IR for a primitive op.""" - def wrapper(f: LowerFunc) -> LowerFunc: + def wrapper(f: LF) -> LF: assert name not in lowering_registry lowering_registry[name] = f return f diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py index a631bd7352b5..326a5baca1e7 100644 --- a/mypyc/transform/ir_transform.py +++ b/mypyc/transform/ir_transform.py @@ -119,6 +119,9 @@ def visit_unreachable(self, op: Unreachable) -> None: self.add(op) def visit_assign(self, op: Assign) -> Value | None: + if op.src in self.op_map and self.op_map[op.src] is None: + # Special case: allow removing register initialization assignments + return None return self.add(op) def visit_assign_multi(self, op: AssignMulti) -> Value | None: diff --git a/mypyc/transform/lower.py b/mypyc/transform/lower.py index b717657095f9..f5768242aff1 100644 --- a/mypyc/transform/lower.py +++ b/mypyc/transform/lower.py @@ -9,6 +9,8 @@ package. """ +from __future__ import annotations + from mypyc.ir.func_ir import FuncIR from mypyc.ir.ops import PrimitiveOp, Value from mypyc.irbuild.ll_builder import LowLevelIRBuilder @@ -25,7 +27,7 @@ def lower_ir(ir: FuncIR, options: CompilerOptions) -> None: class LoweringVisitor(IRTransform): - def visit_primitive_op(self, op: PrimitiveOp) -> Value: + def visit_primitive_op(self, op: PrimitiveOp) -> Value | None: # The lowering implementation functions of various primitive ops are stored # in a registry, which is populated using function decorators. The name # of op (such as "int_eq") is used as the key. From 0d01f180d208d5082ddf2baad0445c658c6dc85c Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 13 Feb 2025 18:49:29 +0300 Subject: [PATCH 1146/1617] Enable `strict_bytes` in self-check (#18670) --- mypy_self_check.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 7198a1f6f342..8b38cf7534a0 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -1,6 +1,7 @@ [mypy] strict = True +strict_bytes = True local_partial_types = True disallow_any_unimported = True show_traceback = True From 1ec3f447272b8719ed417277bb7ac771a5dae063 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Feb 2025 18:22:00 +0000 Subject: [PATCH 1147/1617] Fix instance vs tuple subtyping edge case (#18664) Previously a code path was introduced that made fallback a subtype of its tuple type for non-generic tuples, while the intention was to cover `tuple[Any, ...]` and similar. I add a unit test + some refactoring to make this mistake much harder in future. This may need to wait for https://github.com/python/mypy/pull/18663 to avoid "regressions" (the other fix needed to avoid "regressions" is already merged). --- mypy/subtypes.py | 56 ++++++++++++++++++++------------------- mypy/test/testsubtypes.py | 5 +++- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 938be21201e9..41bb4601e23f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -477,21 +477,17 @@ def visit_instance(self, left: Instance) -> bool: return self._is_subtype(left, unpacked) if left.type.has_base(right.partial_fallback.type.fullname): if not self.proper_subtype: - # Special case to consider Foo[*tuple[Any, ...]] (i.e. bare Foo) a - # subtype of Foo[], when Foo is user defined variadic tuple type. + # Special cases to consider: + # * Plain tuple[Any, ...] instance is a subtype of all tuple types. + # * Foo[*tuple[Any, ...]] (normalized) instance is a subtype of all + # tuples with fallback to Foo (e.g. for variadic NamedTuples). mapped = map_instance_to_supertype(left, right.partial_fallback.type) - for arg in map(get_proper_type, mapped.args): - if isinstance(arg, UnpackType): - unpacked = get_proper_type(arg.type) - if not isinstance(unpacked, Instance): - break - assert unpacked.type.fullname == "builtins.tuple" - if not isinstance(get_proper_type(unpacked.args[0]), AnyType): - break - elif not isinstance(arg, AnyType): - break - else: - return True + if is_erased_instance(mapped): + if ( + mapped.type.fullname == "builtins.tuple" + or mapped.type.has_type_var_tuple_type + ): + return True return False if isinstance(right, TypeVarTupleType): # tuple[Any, ...] is like Any in the world of tuples (see special case above). @@ -559,19 +555,8 @@ def visit_instance(self, left: Instance) -> bool: right_args = ( right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix ) - if not self.proper_subtype and t.args: - for arg in map(get_proper_type, t.args): - if isinstance(arg, UnpackType): - unpacked = get_proper_type(arg.type) - if not isinstance(unpacked, Instance): - break - assert unpacked.type.fullname == "builtins.tuple" - if not isinstance(get_proper_type(unpacked.args[0]), AnyType): - break - elif not isinstance(arg, AnyType): - break - else: - return True + if not self.proper_subtype and is_erased_instance(t): + return True if len(left_args) != len(right_args): return False type_params = zip(left_args, right_args, right.type.defn.type_vars) @@ -2176,3 +2161,20 @@ def erase_return_self_types(typ: Type, self_type: Instance) -> Type: ] ) return typ + + +def is_erased_instance(t: Instance) -> bool: + """Is this an instance where all args are Any types?""" + if not t.args: + return False + for arg in t.args: + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if not isinstance(unpacked, Instance): + return False + assert unpacked.type.fullname == "builtins.tuple" + if not isinstance(get_proper_type(unpacked.args[0]), AnyType): + return False + elif not isinstance(get_proper_type(arg), AnyType): + return False + return True diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 175074a2b140..b75c22bca7f7 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -4,7 +4,7 @@ from mypy.subtypes import is_subtype from mypy.test.helpers import Suite from mypy.test.typefixture import InterfaceTypeFixture, TypeFixture -from mypy.types import Instance, Type, UninhabitedType, UnpackType +from mypy.types import Instance, TupleType, Type, UninhabitedType, UnpackType class SubtypingSuite(Suite): @@ -274,6 +274,9 @@ def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None: Instance(self.fx.gvi, [UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))]), ) + def test_fallback_not_subtype_of_tuple(self) -> None: + self.assert_not_subtype(self.fx.a, TupleType([self.fx.b], fallback=self.fx.a)) + # IDEA: Maybe add these test cases (they are tested pretty well in type # checker tests already): # * more interface subtyping test cases From 555bfaeec257217906b0f1ef3b2e3bfb926e58f9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 14 Feb 2025 09:13:46 +0300 Subject: [PATCH 1148/1617] Enable `warn_unreachable` for `mypy` self-check (#18523) This check prooved to be useful, since it find a lot of dead / incorrect code. Closes #18079 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/build.py | 4 ++-- mypy/checker.py | 7 +++++-- mypy/checkexpr.py | 2 -- mypy/checkmember.py | 8 ++++---- mypy/constraints.py | 12 ++++------- mypy/errors.py | 40 +++++++++++------------------------- mypy/messages.py | 4 ---- mypy/nodes.py | 19 ++++++++++------- mypy/plugins/functools.py | 13 ++++++++++-- mypy/semanal.py | 5 ++--- mypy/server/astdiff.py | 8 ++++---- mypy/server/astmerge.py | 3 ++- mypy/server/deps.py | 3 ++- mypy/server/mergecheck.py | 7 ++++--- mypy/stubgen.py | 4 +--- mypy/stubtest.py | 6 ++++-- mypy/test/data.py | 5 +++-- mypy/test/testfinegrained.py | 1 - mypy/test/testmerge.py | 6 +----- mypy/test/testpep561.py | 35 ------------------------------- mypy/types.py | 21 +++---------------- mypy/util.py | 11 +++++----- mypy_self_check.ini | 4 ++++ 23 files changed, 85 insertions(+), 143 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index a7a76a51f958..f6272ed808cf 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1069,7 +1069,7 @@ def read_plugins_snapshot(manager: BuildManager) -> dict[str, str] | None: if snapshot is None: return None if not isinstance(snapshot, dict): - manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}") + manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}") # type: ignore[unreachable] return None return snapshot @@ -1285,7 +1285,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No if meta is None: return None if not isinstance(meta, dict): - manager.log(f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}") + manager.log(f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}") # type: ignore[unreachable] return None m = cache_meta_from_dict(meta, data_json) t2 = time.time() diff --git a/mypy/checker.py b/mypy/checker.py index 70df1575515c..04a286beef5e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -56,6 +56,7 @@ LITERAL_TYPE, MDEF, NOT_ABSTRACT, + SYMBOL_FUNCBASE_TYPES, AssertStmt, AssignmentExpr, AssignmentStmt, @@ -2865,7 +2866,7 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None: if sym.type is not None: return sym.type - if isinstance(sym.node, FuncBase): + if isinstance(sym.node, SYMBOL_FUNCBASE_TYPES): return self.function_type(sym.node) if isinstance(sym.node, TypeInfo): if sym.node.typeddict_type: @@ -4459,7 +4460,9 @@ def simple_rvalue(self, rvalue: Expression) -> bool: if isinstance(rvalue, (IntExpr, StrExpr, BytesExpr, FloatExpr, RefExpr)): return True if isinstance(rvalue, CallExpr): - if isinstance(rvalue.callee, RefExpr) and isinstance(rvalue.callee.node, FuncBase): + if isinstance(rvalue.callee, RefExpr) and isinstance( + rvalue.callee.node, SYMBOL_FUNCBASE_TYPES + ): typ = rvalue.callee.node.type if isinstance(typ, CallableType): return not typ.variables diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 963667188d6c..4078d447dab8 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2580,8 +2580,6 @@ def check_argument_types( for actual, actual_type, actual_kind, callee_arg_type, callee_arg_kind in zip( actuals, actual_types, actual_kinds, callee_arg_types, callee_arg_kinds ): - if actual_type is None: - continue # Some kind of error was already reported. # Check that a *arg is valid as varargs. expanded_actual = mapper.expand_actual_type( actual_type, diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 206a678a7d25..0994d0df400b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1095,10 +1095,10 @@ def analyze_class_attribute_access( t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( - isinstance(node.node, FuncBase) and node.node.is_class + isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class ) is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or ( - isinstance(node.node, FuncBase) and node.node.is_static + isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static ) t = get_proper_type(t) if isinstance(t, FunctionLike) and is_classmethod: @@ -1148,7 +1148,7 @@ def analyze_class_attribute_access( mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.from_error) else: - assert isinstance(node.node, FuncBase) + assert isinstance(node.node, SYMBOL_FUNCBASE_TYPES) typ = function_type(node.node, mx.named_type("builtins.function")) # Note: if we are accessing class method on class object, the cls argument is bound. # Annotated and/or explicit class methods go through other code paths above, for @@ -1427,7 +1427,7 @@ def is_valid_constructor(n: SymbolNode | None) -> bool: This includes normal functions, overloaded functions, and decorators that return a callable type. """ - if isinstance(n, FuncBase): + if isinstance(n, SYMBOL_FUNCBASE_TYPES): return True if isinstance(n, Decorator): return isinstance(get_proper_type(n.type), FunctionLike) diff --git a/mypy/constraints.py b/mypy/constraints.py index 3c0d08089722..d88b722aa1ce 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -127,12 +127,12 @@ def infer_constraints_for_callable( param_spec_arg_kinds = [] incomplete_star_mapping = False - for i, actuals in enumerate(formal_to_actual): + for i, actuals in enumerate(formal_to_actual): # TODO: isn't this `enumerate(arg_types)`? for actual in actuals: - if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): + if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): # type: ignore[unreachable] # We can't use arguments to infer ParamSpec constraint, if only some # are present in the current inference pass. - incomplete_star_mapping = True + incomplete_star_mapping = True # type: ignore[unreachable] break for i, actuals in enumerate(formal_to_actual): @@ -545,11 +545,7 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list for option in valid_options: if option in trivial_options: continue - if option is not None: - merged_option: list[Constraint] | None = [merge_with_any(c) for c in option] - else: - merged_option = None - merged_options.append(merged_option) + merged_options.append([merge_with_any(c) for c in option]) return any_constraints(list(merged_options), eager) # If normal logic didn't work, try excluding trivially unsatisfiable constraint (due to diff --git a/mypy/errors.py b/mypy/errors.py index f720cb04b16c..58ef17b69e96 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -11,7 +11,6 @@ from mypy import errorcodes as codes from mypy.error_formatter import ErrorFormatter from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes -from mypy.message_registry import ErrorMessage from mypy.options import Options from mypy.scope import Scope from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file @@ -1069,34 +1068,19 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: (file, -1, -1, -1, -1, "note", f'In class "{e.type}":', e.allow_dups, None) ) - if isinstance(e.message, ErrorMessage): - result.append( - ( - file, - e.line, - e.column, - e.end_line, - e.end_column, - e.severity, - e.message.value, - e.allow_dups, - e.code, - ) - ) - else: - result.append( - ( - file, - e.line, - e.column, - e.end_line, - e.end_column, - e.severity, - e.message, - e.allow_dups, - e.code, - ) + result.append( + ( + file, + e.line, + e.column, + e.end_line, + e.end_column, + e.severity, + e.message, + e.allow_dups, + e.code, ) + ) prev_import_context = e.import_ctx prev_function_or_member = e.function_or_member diff --git a/mypy/messages.py b/mypy/messages.py index 9315e77dfd98..25c4ed68ccb5 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2151,12 +2151,8 @@ def report_protocol_problems( is_module = False skip = [] if isinstance(subtype, TupleType): - if not isinstance(subtype.partial_fallback, Instance): - return subtype = subtype.partial_fallback elif isinstance(subtype, TypedDictType): - if not isinstance(subtype.fallback, Instance): - return subtype = subtype.fallback elif isinstance(subtype, TypeType): if not isinstance(subtype.item, Instance): diff --git a/mypy/nodes.py b/mypy/nodes.py index 2b6bf25918d9..5e6fe73a293e 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -176,10 +176,7 @@ class Node(Context): __slots__ = () def __str__(self) -> str: - a = self.accept(mypy.strconv.StrConv(options=Options())) - if a is None: - return repr(self) - return a + return self.accept(mypy.strconv.StrConv(options=Options())) def str_with_options(self, options: Options) -> str: a = self.accept(mypy.strconv.StrConv(options=options)) @@ -875,7 +872,9 @@ def deserialize(cls, data: JsonDict) -> FuncDef: # All types that are both SymbolNodes and FuncBases. See the FuncBase # docstring for the rationale. -SYMBOL_FUNCBASE_TYPES = (OverloadedFuncDef, FuncDef) +# See https://github.com/python/mypy/pull/13607#issuecomment-1236357236 +# TODO: we want to remove this at some point and just use `FuncBase` ideally. +SYMBOL_FUNCBASE_TYPES: Final = (OverloadedFuncDef, FuncDef) class Decorator(SymbolNode, Statement): @@ -2575,6 +2574,11 @@ def fullname(self) -> str: return self._fullname +# All types that are both SymbolNodes and Expressions. +# Use when common children of them are needed. +SYMBOL_NODE_EXPRESSION_TYPES: Final = (TypeVarLikeExpr,) + + class TypeVarExpr(TypeVarLikeExpr): """Type variable expression TypeVar(...). @@ -3273,7 +3277,7 @@ def get_method(self, name: str) -> FuncBase | Decorator | None: for cls in self.mro: if name in cls.names: node = cls.names[name].node - if isinstance(node, FuncBase): + if isinstance(node, SYMBOL_FUNCBASE_TYPES): return node elif isinstance(node, Decorator): # Two `if`s make `mypyc` happy return node @@ -4032,7 +4036,8 @@ def __str__(self) -> str: ): a.append(" " + str(key) + " : " + str(value)) else: - a.append(" ") + # Used in debugging: + a.append(" ") # type: ignore[unreachable] a = sorted(a) a.insert(0, "SymbolTable(") a[-1] += ")" diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 6a063174bfcb..c435dde7fde7 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -8,7 +8,16 @@ import mypy.plugin import mypy.semanal from mypy.argmap import map_actuals_to_formals -from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, NameExpr, Var +from mypy.nodes import ( + ARG_POS, + ARG_STAR2, + SYMBOL_FUNCBASE_TYPES, + ArgKind, + Argument, + CallExpr, + NameExpr, + Var, +) from mypy.plugins.common import add_method_to_class from mypy.typeops import get_all_type_vars from mypy.types import ( @@ -108,7 +117,7 @@ def _analyze_class(ctx: mypy.plugin.ClassDefContext) -> dict[str, _MethodInfo | for name in _ORDERING_METHODS: if name in cls.names and name not in comparison_methods: node = cls.names[name].node - if isinstance(node, FuncItem) and isinstance(node.type, CallableType): + if isinstance(node, SYMBOL_FUNCBASE_TYPES) and isinstance(node.type, CallableType): comparison_methods[name] = _MethodInfo(node.is_static, node.type) continue diff --git a/mypy/semanal.py b/mypy/semanal.py index b6e534d3c8b3..1a64731057e2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -86,6 +86,7 @@ REVEAL_LOCALS, REVEAL_TYPE, RUNTIME_PROTOCOL_DECOS, + SYMBOL_FUNCBASE_TYPES, TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, VARIANCE_NOT_READY, @@ -3082,8 +3083,6 @@ def visit_import_all(self, i: ImportAll) -> None: for name, node in m.names.items(): fullname = i_id + "." + name self.set_future_import_flags(fullname) - if node is None: - continue # if '__all__' exists, all nodes not included have had module_public set to # False, and we can skip checking '_' because it's been explicitly included. if node.module_public and (not name.startswith("_") or "__all__" in m.names): @@ -5719,7 +5718,7 @@ def visit_call_expr(self, expr: CallExpr) -> None: reveal_type_node = self.lookup("reveal_type", expr, suppress_errors=True) if ( reveal_type_node - and isinstance(reveal_type_node.node, FuncBase) + and isinstance(reveal_type_node.node, SYMBOL_FUNCBASE_TYPES) and reveal_type_node.fullname in IMPORTED_REVEAL_TYPE_NAMES ): reveal_imported = True diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 07bc6333ce88..1b0cc218ed16 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -58,9 +58,9 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' from mypy.expandtype import expand_type from mypy.nodes import ( + SYMBOL_FUNCBASE_TYPES, UNBOUND_IMPORTED, Decorator, - FuncBase, FuncDef, FuncItem, MypyFile, @@ -234,16 +234,16 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb The representation is nested tuples and dicts. Only externally visible attributes are included. """ - if isinstance(node, FuncBase): + if isinstance(node, SYMBOL_FUNCBASE_TYPES): # TODO: info if node.type: - signature = snapshot_type(node.type) + signature: tuple[object, ...] = snapshot_type(node.type) else: signature = snapshot_untyped_signature(node) impl: FuncDef | None = None if isinstance(node, FuncDef): impl = node - elif isinstance(node, OverloadedFuncDef) and node.impl: + elif node.impl: impl = node.impl.func if isinstance(node.impl, Decorator) else node.impl setter_type = None if isinstance(node, OverloadedFuncDef) and node.items: diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index bb5606758571..8cd574628bb8 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -51,6 +51,7 @@ from mypy.nodes import ( MDEF, + SYMBOL_NODE_EXPRESSION_TYPES, AssertTypeExpr, AssignmentStmt, Block, @@ -301,7 +302,7 @@ def visit_super_expr(self, node: SuperExpr) -> None: def visit_call_expr(self, node: CallExpr) -> None: super().visit_call_expr(node) - if isinstance(node.analyzed, SymbolNode): + if isinstance(node.analyzed, SYMBOL_NODE_EXPRESSION_TYPES): node.analyzed = self.fixup(node.analyzed) def visit_newtype_expr(self, node: NewTypeExpr) -> None: diff --git a/mypy/server/deps.py b/mypy/server/deps.py index f4e7b86abf63..b994a214f67a 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -87,6 +87,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a GDEF, LDEF, MDEF, + SYMBOL_FUNCBASE_TYPES, AssertTypeExpr, AssignmentStmt, AwaitExpr, @@ -501,7 +502,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: if isinstance(rvalue.callee.node, TypeInfo): # use actual __init__ as a dependency source init = rvalue.callee.node.get("__init__") - if init and isinstance(init.node, FuncBase): + if init and isinstance(init.node, SYMBOL_FUNCBASE_TYPES): fname = init.node.fullname else: fname = rvalue.callee.fullname diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index 6f044a5ea8b9..11e00213d05a 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -26,10 +26,11 @@ def check_consistency(o: object) -> None: continue fn = sym.fullname - # Skip None names, since they are ambiguous. + # Skip None and empty names, since they are ambiguous. # TODO: Everything should have a proper full name? - if fn is None: + if not fn: continue + # Skip stuff that should be expected to have duplicate names if isinstance(sym, (Var, Decorator)): continue @@ -37,7 +38,7 @@ def check_consistency(o: object) -> None: continue if fn not in m: - m[sym.fullname] = sym + m[fn] = sym continue # We have trouble and need to decide what to do about it. diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 60460ee1e330..881686adc5ed 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1504,9 +1504,7 @@ def is_blacklisted_path(path: str) -> bool: def normalize_path_separators(path: str) -> str: - if sys.platform == "win32": - return path.replace("\\", "/") - return path + return path.replace("\\", "/") if sys.platform == "win32" else path def collect_build_targets( diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 5d19c4777916..e2a6a06f6bf2 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -338,7 +338,8 @@ def verify_mypyfile( yield Error(object_path, "is not present at runtime", stub, runtime) return if not isinstance(runtime, types.ModuleType): - yield Error(object_path, "is not a module", stub, runtime) + # Can possibly happen: + yield Error(object_path, "is not a module", stub, runtime) # type: ignore[unreachable] return runtime_all_as_set: set[str] | None @@ -524,7 +525,8 @@ def verify_typeinfo( yield Error(object_path, "is not present at runtime", stub, runtime, stub_desc=repr(stub)) return if not isinstance(runtime, type): - yield Error(object_path, "is not a type", stub, runtime, stub_desc=repr(stub)) + # Yes, some runtime objects can be not types, no way to tell mypy about that. + yield Error(object_path, "is not a type", stub, runtime, stub_desc=repr(stub)) # type: ignore[unreachable] return yield from _verify_final(stub, runtime, object_path) diff --git a/mypy/test/data.py b/mypy/test/data.py index 50e452de4c0a..5b0ad84c0ba7 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -246,7 +246,7 @@ class DataDrivenTestCase(pytest.Item): """Holds parsed data-driven test cases, and handles directory setup and teardown.""" # Override parent member type - parent: DataSuiteCollector + parent: DataFileCollector input: list[str] output: list[str] # Output for the first pass @@ -277,7 +277,7 @@ class DataDrivenTestCase(pytest.Item): def __init__( self, - parent: DataSuiteCollector, + parent: DataFileCollector, suite: DataSuite, *, file: str, @@ -291,6 +291,7 @@ def __init__( data: str, line: int, ) -> None: + assert isinstance(parent, DataFileCollector) super().__init__(name, parent) self.suite = suite self.file = file diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index cb8672dfaf29..b098c1fb0ad2 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -75,7 +75,6 @@ def should_skip(self, testcase: DataDrivenTestCase) -> bool: def run_case(self, testcase: DataDrivenTestCase) -> None: if self.should_skip(testcase): pytest.skip() - return main_src = "\n".join(testcase.input) main_path = os.path.join(test_temp_dir, "main") diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 0582c9ed5882..51a4ff39dd9a 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -13,7 +13,6 @@ UNBOUND_IMPORTED, Expression, MypyFile, - Node, SymbolTable, SymbolTableNode, TypeInfo, @@ -172,10 +171,7 @@ def format_symbol_table_node(self, node: SymbolTableNode) -> str: if node.kind == UNBOUND_IMPORTED: return "UNBOUND_IMPORTED" return "None" - if isinstance(node.node, Node): - s = f"{str(type(node.node).__name__)}<{self.id_mapper.id(node.node)}>" - else: - s = f"? ({type(node.node)})" + s = f"{str(type(node.node).__name__)}<{self.id_mapper.id(node.node)}>" if ( isinstance(node.node, Var) and node.node.type diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index 4a5301d2cdb8..e3f729729f0b 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -173,38 +173,3 @@ def parse_mypy_args(line: str) -> list[str]: if not m: return [] # No args; mypy will spit out an error. return m.group(1).split() - - -def test_mypy_path_is_respected() -> None: - assert False - packages = "packages" - pkg_name = "a" - with tempfile.TemporaryDirectory() as temp_dir: - old_dir = os.getcwd() - os.chdir(temp_dir) - try: - # Create the pkg for files to go into - full_pkg_name = os.path.join(temp_dir, packages, pkg_name) - os.makedirs(full_pkg_name) - - # Create the empty __init__ file to declare a package - pkg_init_name = os.path.join(temp_dir, packages, pkg_name, "__init__.py") - open(pkg_init_name, "w", encoding="utf8").close() - - mypy_config_path = os.path.join(temp_dir, "mypy.ini") - with open(mypy_config_path, "w") as mypy_file: - mypy_file.write("[mypy]\n") - mypy_file.write(f"mypy_path = ./{packages}\n") - - with virtualenv() as venv: - venv_dir, python_executable = venv - - cmd_line_args = [] - if python_executable != sys.executable: - cmd_line_args.append(f"--python-executable={python_executable}") - cmd_line_args.extend(["--config-file", mypy_config_path, "--package", pkg_name]) - - out, err, returncode = mypy.api.run(cmd_line_args) - assert returncode == 0 - finally: - os.chdir(old_dir) diff --git a/mypy/types.py b/mypy/types.py index f700be887116..f9749945d9e9 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3320,12 +3320,7 @@ def visit_instance(self, t: Instance, /) -> str: return s def visit_type_var(self, t: TypeVarType, /) -> str: - if t.name is None: - # Anonymous type variable type (only numeric id). - s = f"`{t.id}" - else: - # Named type variable type. - s = f"{t.name}`{t.id}" + s = f"{t.name}`{t.id}" if self.id_mapper and t.upper_bound: s += f"(upper_bound={t.upper_bound.accept(self)})" if t.has_default(): @@ -3337,12 +3332,7 @@ def visit_param_spec(self, t: ParamSpecType, /) -> str: s = "" if t.prefix.arg_types: s += f"[{self.list_str(t.prefix.arg_types)}, **" - if t.name is None: - # Anonymous type variable type (only numeric id). - s += f"`{t.id}" - else: - # Named type variable type. - s += f"{t.name_with_suffix()}`{t.id}" + s += f"{t.name_with_suffix()}`{t.id}" if t.prefix.arg_types: s += "]" if t.has_default(): @@ -3379,12 +3369,7 @@ def visit_parameters(self, t: Parameters, /) -> str: return f"[{s}]" def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> str: - if t.name is None: - # Anonymous type variable type (only numeric id). - s = f"`{t.id}" - else: - # Named type variable type. - s = f"{t.name}`{t.id}" + s = f"{t.name}`{t.id}" if t.has_default(): s += f" = {t.default.accept(self)}" return s diff --git a/mypy/util.py b/mypy/util.py index f79d7113ca91..d3f49f74bbae 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -571,8 +571,7 @@ def hash_digest(data: bytes) -> str: def parse_gray_color(cup: bytes) -> str: """Reproduce a gray color in ANSI escape sequence""" - if sys.platform == "win32": - assert False, "curses is not available on Windows" + assert sys.platform != "win32", "curses is not available on Windows" set_color = "".join([cup[:-1].decode(), "m"]) gray = curses.tparm(set_color.encode("utf-8"), 1, 9).decode() return gray @@ -639,8 +638,7 @@ def initialize_win_colors(self) -> bool: # Windows ANSI escape sequences are only supported on Threshold 2 and above. # we check with an assert at runtime and an if check for mypy, as asserts do not # yet narrow platform - assert sys.platform == "win32" - if sys.platform == "win32": + if sys.platform == "win32": # needed to find win specific sys apis winver = sys.getwindowsversion() if ( winver.major < MINIMUM_WINDOWS_MAJOR_VT100 @@ -662,11 +660,12 @@ def initialize_win_colors(self) -> bool: ) self.initialize_vt100_colors() return True - return False + assert False, "Running not on Windows" def initialize_unix_colors(self) -> bool: """Return True if initialization was successful and we can use colors, False otherwise""" - if sys.platform == "win32" or not CURSES_ENABLED: + is_win = sys.platform == "win32" + if is_win or not CURSES_ENABLED: return False try: # setupterm wants a fd to potentially write an "initialization sequence". diff --git a/mypy_self_check.ini b/mypy_self_check.ini index 8b38cf7534a0..816e6321c06f 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -13,3 +13,7 @@ exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True + +[mypy-mypy.*] +# TODO: enable for `mypyc` and other files as well +warn_unreachable = True From 8fc8d26cb428d590155d699cd7b07d6a0fce843b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 14 Feb 2025 07:18:42 +0100 Subject: [PATCH 1149/1617] [mypyc] Optimize str.removeprefix and str.removesuffix (#18672) `str.removeprefix` and `str.removesuffix` were added in Python 3.9. --- mypyc/doc/str_operations.rst | 2 ++ mypyc/lib-rt/CPy.h | 2 ++ mypyc/lib-rt/str_ops.c | 20 ++++++++++++++++++++ mypyc/primitives/str_ops.py | 18 ++++++++++++++++++ mypyc/test-data/fixtures/ir.py | 2 ++ mypyc/test-data/run-strings.test | 8 +++++++- 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 9e94f1b6d7bb..ef109f5bca8a 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -32,6 +32,8 @@ Methods * ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` * ``s.join(x: Iterable)`` +* ``s.removeprefix(prefix: str)`` +* ``s.removesuffix(suffix: str)`` * ``s.replace(old: str, new: str)`` * ``s.replace(old: str, new: str, count: int)`` * ``s.split()`` diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index a240f20d31d8..5abe35fb689b 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -726,6 +726,8 @@ PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); bool CPyStr_Startswith(PyObject *self, PyObject *subobj); bool CPyStr_Endswith(PyObject *self, PyObject *subobj); +PyObject *CPyStr_Removeprefix(PyObject *self, PyObject *prefix); +PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix); bool CPyStr_IsTrue(PyObject *obj); Py_ssize_t CPyStr_Size_size_t(PyObject *str); PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors); diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 68026037502d..5b02dd33df31 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -164,6 +164,26 @@ bool CPyStr_Endswith(PyObject *self, PyObject *subobj) { return PyUnicode_Tailmatch(self, subobj, start, end, 1); } +PyObject *CPyStr_Removeprefix(PyObject *self, PyObject *prefix) { + Py_ssize_t end = PyUnicode_GET_LENGTH(self); + int match = PyUnicode_Tailmatch(self, prefix, 0, end, -1); + if (match) { + Py_ssize_t prefix_end = PyUnicode_GET_LENGTH(prefix); + return PyUnicode_Substring(self, prefix_end, end); + } + return Py_NewRef(self); +} + +PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix) { + Py_ssize_t end = PyUnicode_GET_LENGTH(self); + int match = PyUnicode_Tailmatch(self, suffix, 0, end, 1); + if (match) { + Py_ssize_t suffix_end = PyUnicode_GET_LENGTH(suffix); + return PyUnicode_Substring(self, 0, end - suffix_end); + } + return Py_NewRef(self); +} + /* This does a dodgy attempt to append in place */ PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) { PyUnicode_Append(&o1, o2); diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 0accffd86a17..65c60bff8c6e 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -118,6 +118,24 @@ error_kind=ERR_NEVER, ) +# str.removeprefix(str) +method_op( + name="removeprefix", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyStr_Removeprefix", + error_kind=ERR_NEVER, +) + +# str.removesuffix(str) +method_op( + name="removesuffix", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyStr_Removesuffix", + error_kind=ERR_NEVER, +) + # str.split(...) str_split_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive] str_split_functions = ["PyUnicode_Split", "PyUnicode_Split", "CPyStr_Split"] diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index be66307286fc..ffd425aab049 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -111,6 +111,8 @@ def startswith(self, x: str, start: int=..., end: int=...) -> bool: ... def endswith(self, x: str, start: int=..., end: int=...) -> bool: ... def replace(self, old: str, new: str, maxcount: int=...) -> str: ... def encode(self, encoding: str=..., errors: str=...) -> bytes: ... + def removeprefix(self, prefix: str, /) -> str: ... + def removesuffix(self, suffix: str, /) -> str: ... class float: def __init__(self, x: object) -> None: pass diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 1caddce9848d..69422cb824d4 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -20,9 +20,11 @@ def eq(x: str) -> int: return 2 def match(x: str, y: str) -> Tuple[bool, bool]: return (x.startswith(y), x.endswith(y)) +def remove_prefix_suffix(x: str, y: str) -> Tuple[str, str]: + return (x.removeprefix(y), x.removesuffix(y)) [file driver.py] -from native import f, g, tostr, booltostr, concat, eq, match +from native import f, g, tostr, booltostr, concat, eq, match, remove_prefix_suffix import sys assert f() == 'some string' @@ -44,6 +46,10 @@ assert match('abc', 'a') == (True, False) assert match('abc', 'c') == (False, True) assert match('', 'abc') == (False, False) +assert remove_prefix_suffix('', '') == ('', '') +assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc') +assert remove_prefix_suffix('abc', 'c') == ('abc', 'ab') + [case testStringOps] from typing import List, Optional From 763185d167f866e30bdb862471774cdde3317978 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Thu, 13 Feb 2025 22:20:28 -0800 Subject: [PATCH 1150/1617] Enable ruff FURB188: str.remove(pre|suf)fix (#18671) --- misc/upload-pypi.py | 9 +++------ mypy/find_sources.py | 3 +-- mypy/stubtest.py | 2 +- mypy/test/helpers.py | 3 +-- mypyc/irbuild/ll_builder.py | 6 ++---- pyproject.toml | 1 + 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py index c0ff1b2a075e..c9db475c14b4 100644 --- a/misc/upload-pypi.py +++ b/misc/upload-pypi.py @@ -34,10 +34,8 @@ def item_ok_for_pypi(name: str) -> bool: if not is_whl_or_tar(name): return False - if name.endswith(".tar.gz"): - name = name[:-7] - if name.endswith(".whl"): - name = name[:-4] + name = name.removesuffix(".tar.gz") + name = name.removesuffix(".whl") if name.endswith("wasm32"): return False @@ -123,8 +121,7 @@ def upload_to_pypi(version: str, dry_run: bool = True) -> None: assert re.match(r"v?[1-9]\.[0-9]+\.[0-9](\+\S+)?$", version) if "dev" in version: assert dry_run, "Must use --dry-run with dev versions of mypy" - if version.startswith("v"): - version = version[1:] + version = version.removeprefix("v") target_dir = tempfile.mkdtemp() dist = Path(target_dir) / "dist" diff --git a/mypy/find_sources.py b/mypy/find_sources.py index 783642960fb3..e9b05f0f2cc8 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -176,8 +176,7 @@ def _crawl_up_helper(self, dir: str) -> tuple[str, str] | None: return "", dir parent, name = os.path.split(dir) - if name.endswith("-stubs"): - name = name[:-6] # PEP-561 stub-only directory + name = name.removesuffix("-stubs") # PEP-561 stub-only directory # recurse if there's an __init__.py init_file = self.get_init_file(dir) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index e2a6a06f6bf2..599a24cf685d 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -641,7 +641,7 @@ def _verify_arg_name( return def strip_prefix(s: str, prefix: str) -> str: - return s[len(prefix) :] if s.startswith(prefix) else s + return s.removeprefix(prefix) if strip_prefix(stub_arg.variable.name, "__") == runtime_arg.name: return diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index d9013221116a..fcec68094e51 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -413,8 +413,7 @@ def check_test_output_files( testcase: DataDrivenTestCase, step: int, strip_prefix: str = "" ) -> None: for path, expected_content in testcase.output_files: - if path.startswith(strip_prefix): - path = path[len(strip_prefix) :] + path = path.removeprefix(strip_prefix) if not os.path.exists(path): raise AssertionError( "Expected file {} was not produced by test case{}".format( diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index e7c256331842..7219d5d5e708 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1345,8 +1345,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.translate_instance_contains(rreg, lreg, op, line) if is_fixed_width_rtype(ltype): if op in FIXED_WIDTH_INT_BINARY_OPS: - if op.endswith("="): - op = op[:-1] + op = op.removesuffix("=") if op != "//": op_id = int_op_to_id[op] else: @@ -1372,8 +1371,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.comparison_op(lreg, self.coerce(rreg, ltype, line), op_id, line) elif is_fixed_width_rtype(rtype): if op in FIXED_WIDTH_INT_BINARY_OPS: - if op.endswith("="): - op = op[:-1] + op = op.removesuffix("=") if op != "//": op_id = int_op_to_id[op] else: diff --git a/pyproject.toml b/pyproject.toml index 157c26385e4e..2eaca2d3ea88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -143,6 +143,7 @@ select = [ "UP", # pyupgrade "C4", # flake8-comprehensions "SIM201", "SIM202", "SIM222", "SIM223", # flake8-simplify + "FURB188", # use str.remove(pre|suf)fix "ISC001", # implicitly concatenated string "RET501", "RET502", # better return None handling ] From f404b16ea8c1dfbcedebd4190a1cf5d73dc82ea6 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 14 Feb 2025 10:18:12 +0300 Subject: [PATCH 1151/1617] Properly account for `member` and `nonmember` in `TypeInfo.enum_members` (#18559) Closes #18557 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/nodes.py | 24 ++++++++++++++++++++---- mypy/typeops.py | 2 +- test-data/unit/check-enum.test | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 5e6fe73a293e..6487ee4b745c 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3247,10 +3247,26 @@ def enum_members(self) -> list[str]: name for name, sym in self.names.items() if ( - isinstance(sym.node, Var) - and name not in EXCLUDED_ENUM_ATTRIBUTES - and not name.startswith("__") - and sym.node.has_explicit_value + ( + isinstance(sym.node, Var) + and name not in EXCLUDED_ENUM_ATTRIBUTES + and not name.startswith("__") + and sym.node.has_explicit_value + and not ( + isinstance( + typ := mypy.types.get_proper_type(sym.node.type), mypy.types.Instance + ) + and typ.type.fullname == "enum.nonmember" + ) + ) + or ( + isinstance(sym.node, Decorator) + and any( + dec.fullname == "enum.member" + for dec in sym.node.decorators + if isinstance(dec, RefExpr) + ) + ) ) ] diff --git a/mypy/typeops.py b/mypy/typeops.py index 1667e8431a17..ac0695a096a6 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -955,7 +955,7 @@ class Status(Enum): FAILURE = 2 UNKNOWN = 3 - ...and if we call `try_expanding_enum_to_union(Union[Color, Status], 'module.Color')`, + ...and if we call `try_expanding_sum_type_to_union(Union[Color, Status], 'module.Color')`, this function will return Literal[Color.RED, Color.BLUE, Color.YELLOW, Status]. """ typ = get_proper_type(typ) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 7b97f96f55b1..a3abf53e29ac 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1933,6 +1933,18 @@ class D(C): # E: Cannot extend enum with existing members: "C" \ x: int # E: Cannot assign to final name "x" [builtins fixtures/bool.pyi] +[case testEnumNotFinalWithMethodsAndUninitializedValuesStubMember] +# flags: --python-version 3.11 +# This was added in 3.11 +import lib + +[file lib.pyi] +from enum import Enum, member +class A(Enum): + @member + def x(self) -> None: ... +[builtins fixtures/bool.pyi] + [case testEnumLiteralValues] from enum import Enum @@ -2325,6 +2337,28 @@ def some_a(a: A): [builtins fixtures/dict.pyi] +[case testEnumMemberAndNonMemberSupport] +# flags: --python-version 3.11 --warn-unreachable +# This was added in 3.11 +from enum import Enum, member, nonmember + +class A(Enum): + x = 1 + y = member(2) + z = nonmember(3) + +def some_a(a: A): + if a is not A.x and a is not A.z: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.y]" + if a is not A.y and a is not A.z: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.x]" + if a is not A.x: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.y]" + if a is not A.y: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.x]" +[builtins fixtures/dict.pyi] + + [case testErrorOnAnnotatedMember] from enum import Enum From 49c3fa423de3ef3d670462f4083454ed97f18b0a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:20:34 +0100 Subject: [PATCH 1152/1617] [mypyc] Optimize str.rsplit (#18673) Use `PyUnicode_RSplit` to optimize `str.rsplit` calls. Although not present in the documentation, it's has actually part of the stable API since Python 3.2. https://github.com/python/cpython/blob/v3.13.2/Doc/data/stable_abi.dat#L799 https://github.com/python/cpython/blob/main/Include/unicodeobject.h#L841-L858 --- mypyc/doc/str_operations.rst | 3 +++ mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/str_ops.c | 9 +++++++++ mypyc/primitives/str_ops.py | 11 ++++++++++- mypyc/test-data/fixtures/ir.py | 3 ++- mypyc/test-data/run-strings.test | 17 +++++++++++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index ef109f5bca8a..1419d56b0647 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -36,6 +36,9 @@ Methods * ``s.removesuffix(suffix: str)`` * ``s.replace(old: str, new: str)`` * ``s.replace(old: str, new: str, count: int)`` +* ``s.rsplit()`` +* ``s.rsplit(sep: str)`` +* ``s.rsplit(sep: str, maxsplit: int)`` * ``s.split()`` * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 5abe35fb689b..93d79a37aaf8 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -721,6 +721,7 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { PyObject *CPyStr_Build(Py_ssize_t len, ...); PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index); PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split); +PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split); PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace); PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 5b02dd33df31..46458f9b57dc 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -142,6 +142,15 @@ PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) { return PyUnicode_Split(str, sep, temp_max_split); } +PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split) { + Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); + if (temp_max_split == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return NULL; + } + return PyUnicode_RSplit(str, sep, temp_max_split); +} + PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace) { Py_ssize_t temp_max_replace = CPyTagged_AsSsize_t(max_replace); diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 65c60bff8c6e..fed471cd9a4e 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -136,9 +136,10 @@ error_kind=ERR_NEVER, ) -# str.split(...) +# str.split(...) and str.rsplit(...) str_split_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive] str_split_functions = ["PyUnicode_Split", "PyUnicode_Split", "CPyStr_Split"] +str_rsplit_functions = ["PyUnicode_RSplit", "PyUnicode_RSplit", "CPyStr_RSplit"] str_split_constants: list[list[tuple[int, RType]]] = [ [(0, pointer_rprimitive), (-1, c_int_rprimitive)], [(-1, c_int_rprimitive)], @@ -153,6 +154,14 @@ extra_int_constants=str_split_constants[i], error_kind=ERR_MAGIC, ) + method_op( + name="rsplit", + arg_types=str_split_types[0 : i + 1], + return_type=list_rprimitive, + c_function_name=str_rsplit_functions[i], + extra_int_constants=str_split_constants[i], + error_kind=ERR_MAGIC, + ) # str.replace(old, new) method_op( diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index ffd425aab049..01f189b4f08b 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -102,7 +102,8 @@ def __getitem__(self, i: int) -> str: pass def __getitem__(self, i: slice) -> str: pass def __contains__(self, item: str) -> bool: pass def __iter__(self) -> Iterator[str]: ... - def split(self, sep: Optional[str] = None, max: Optional[int] = None) -> List[str]: pass + def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass + def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def strip (self, item: str) -> str: pass def join(self, x: Iterable[str]) -> str: pass def format(self, *args: Any, **kwargs: Any) -> str: ... diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 69422cb824d4..3998f6f7dbc4 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -61,6 +61,14 @@ def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) return s.split(sep) return s.split() +def do_rsplit(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: + if sep is not None: + if max_split is not None: + return s.rsplit(sep, max_split) + else: + return s.rsplit(sep) + return s.rsplit() + ss = "abc abcd abcde abcdef" def test_split() -> None: @@ -72,6 +80,15 @@ def test_split() -> None: assert do_split(ss, " ", 1) == ["abc", "abcd abcde abcdef"] assert do_split(ss, " ", 2) == ["abc", "abcd", "abcde abcdef"] +def test_rsplit() -> None: + assert do_rsplit(ss) == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, " ") == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, "-") == ["abc abcd abcde abcdef"] + assert do_rsplit(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, " ", 0) == ["abc abcd abcde abcdef"] + assert do_rsplit(ss, " ", 1) == ["abc abcd abcde", "abcdef"] # different to do_split + assert do_rsplit(ss, " ", 2) == ["abc abcd", "abcde", "abcdef"] # different to do_split + def getitem(s: str, index: int) -> str: return s[index] From d7f15bea30eaf3a86a63e308c4dde4a1c6d1c55b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:44:15 +0100 Subject: [PATCH 1153/1617] [ci] Use ubuntu-22.04-arm runners (#18676) The `actions/checkout` issue with the new Github arm runners is reportedly fixed on `ubuntu-22.04-arm`. Let's try and see how it goes. We can always switch back later when `ubuntu-24.04-arm` is fixed as well. Ref #18660 --- .github/workflows/test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c42550431bb1..30686804780b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: # the oldest and newest supported Python versions - name: Test suite with py39-ubuntu, mypyc-compiled python: '3.9' - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true @@ -44,31 +44,31 @@ jobs: tox_extra_args: "-n 4" - name: Test suite with py310-ubuntu python: '3.10' - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm toxenv: py tox_extra_args: "-n 4" - name: Test suite with py311-ubuntu, mypyc-compiled python: '3.11' - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled python: '3.12' - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py313-ubuntu, mypyc-compiled python: '3.13' - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true # - name: Test suite with py314-dev-ubuntu # python: '3.14-dev' - # os: ubuntu-24.04-arm + # os: ubuntu-22.04-arm # toxenv: py # tox_extra_args: "-n 4" # allow_failure: true From 8104d0198813a59dd26c8e9f50779533c60292b4 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:46:11 -0800 Subject: [PATCH 1154/1617] [mypyc] Support __del__ methods (#18519) Fixes mypyc/mypyc#1035 * Populating `.tp_finalize` if the user has defined `__del__`. * Calling `.tp_finalize` from `.tp_dealloc`. --- mypyc/codegen/emitclass.py | 53 ++++++++++++++++++++- mypyc/test-data/run-classes.test | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 79ae6abf1f60..c5191e5fb939 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -196,6 +196,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: setup_name = f"{name_prefix}_setup" new_name = f"{name_prefix}_new" + finalize_name = f"{name_prefix}_finalize" members_name = f"{name_prefix}_members" getseters_name = f"{name_prefix}_getseters" vtable_name = f"{name_prefix}_vtable" @@ -217,6 +218,10 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: fields["tp_dealloc"] = f"(destructor){name_prefix}_dealloc" fields["tp_traverse"] = f"(traverseproc){name_prefix}_traverse" fields["tp_clear"] = f"(inquiry){name_prefix}_clear" + # Populate .tp_finalize and generate a finalize method only if __del__ is defined for this class. + del_method = next((e.method for e in cl.vtable_entries if e.name == "__del__"), None) + if del_method: + fields["tp_finalize"] = f"(destructor){finalize_name}" if needs_getseters: fields["tp_getset"] = getseters_name fields["tp_methods"] = methods_name @@ -297,8 +302,11 @@ def emit_line() -> None: emit_line() generate_clear_for_class(cl, clear_name, emitter) emit_line() - generate_dealloc_for_class(cl, dealloc_name, clear_name, emitter) + generate_dealloc_for_class(cl, dealloc_name, clear_name, bool(del_method), emitter) emit_line() + if del_method: + generate_finalize_for_class(del_method, finalize_name, emitter) + emit_line() if cl.allow_interpreted_subclasses: shadow_vtable_name: str | None = generate_vtables( @@ -765,11 +773,19 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N def generate_dealloc_for_class( - cl: ClassIR, dealloc_func_name: str, clear_func_name: str, emitter: Emitter + cl: ClassIR, + dealloc_func_name: str, + clear_func_name: str, + has_tp_finalize: bool, + emitter: Emitter, ) -> None: emitter.emit_line("static void") emitter.emit_line(f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)") emitter.emit_line("{") + if has_tp_finalize: + emitter.emit_line("if (!PyObject_GC_IsFinalized((PyObject *)self)) {") + emitter.emit_line("Py_TYPE(self)->tp_finalize((PyObject *)self);") + emitter.emit_line("}") emitter.emit_line("PyObject_GC_UnTrack(self);") # The trashcan is needed to handle deep recursive deallocations emitter.emit_line(f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})") @@ -779,6 +795,39 @@ def generate_dealloc_for_class( emitter.emit_line("}") +def generate_finalize_for_class( + del_method: FuncIR, finalize_func_name: str, emitter: Emitter +) -> None: + emitter.emit_line("static void") + emitter.emit_line(f"{finalize_func_name}(PyObject *self)") + emitter.emit_line("{") + emitter.emit_line("PyObject *type, *value, *traceback;") + emitter.emit_line("PyErr_Fetch(&type, &value, &traceback);") + emitter.emit_line( + "{}{}{}(self);".format( + emitter.get_group_prefix(del_method.decl), + NATIVE_PREFIX, + del_method.cname(emitter.names), + ) + ) + emitter.emit_line("if (PyErr_Occurred() != NULL) {") + emitter.emit_line('PyObject *del_str = PyUnicode_FromString("__del__");') + emitter.emit_line( + "PyObject *del_method = (del_str == NULL) ? NULL : _PyType_Lookup(Py_TYPE(self), del_str);" + ) + # CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable + # However, the message is slightly different due to the way mypyc compiles classes. + # CPython interpreter prints: Exception ignored in: + # mypyc prints: Exception ignored in: + emitter.emit_line("PyErr_WriteUnraisable(del_method);") + emitter.emit_line("Py_XDECREF(del_method);") + emitter.emit_line("Py_XDECREF(del_str);") + emitter.emit_line("}") + # PyErr_Restore also clears exception raised in __del__. + emitter.emit_line("PyErr_Restore(type, value, traceback);") + emitter.emit_line("}") + + def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: emitter.emit_line(f"static PyMethodDef {name}[] = {{") for fn in cl.methods.values(): diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 5d7aadb15045..601d6d7a65a0 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2748,3 +2748,84 @@ def test_function(): assert(isinstance(d.bitems, BackwardDefinedClass)) assert(isinstance(d.fitem, ForwardDefinedClass)) assert(isinstance(d.fitems, ForwardDefinedClass)) + +[case testDel] +class A: + def __del__(self): + print("deleting A...") + +class B: + def __del__(self): + print("deleting B...") + +class C(B): + def __init__(self): + self.a = A() + + def __del__(self): + print("deleting C...") + super().__del__() + +class D(A): + pass + +[file driver.py] +import native +native.C() +native.D() + +[out] +deleting C... +deleting B... +deleting A... +deleting A... + +[case testDelCircular] +import dataclasses +import typing + +i: int = 1 + +@dataclasses.dataclass +class C: + var: typing.Optional["C"] = dataclasses.field(default=None) + + def __del__(self): + global i + print(f"deleting C{i}...") + i = i + 1 + +[file driver.py] +import native +import gc + +c1 = native.C() +c2 = native.C() +c1.var = c2 +c2.var = c1 +del c1 +del c2 +gc.collect() + +[out] +deleting C1... +deleting C2... + +[case testDelException] +# The error message in the expected output of this test does not match CPython's error message due to the way mypyc compiles Python classes. If the error message is fixed, the expected output of this test will also change. +class F: + def __del__(self): + if True: + raise Exception("e2") + +[file driver.py] +import native +f = native.F() +del f + +[out] +Exception ignored in: +Traceback (most recent call last): + File "native.py", line 5, in __del__ + raise Exception("e2") +Exception: e2 From 52c7735ff9e0a1e60c80a31bf6ffd0b0d0d7d8a9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 16 Feb 2025 08:58:37 +0100 Subject: [PATCH 1155/1617] Sync typeshed (#18683) Source commit: https://github.com/python/typeshed/commit/cc8ca939c0477a49fcce0554fa1743bd5c656a11 Partially revert https://github.com/python/typeshed/pull/13450 to fix mypyc runs. --- ...ially-revert-Clean-up-argparse-hacks.patch | 45 +++++ mypy/typeshed/stdlib/_decimal.pyi | 16 +- mypy/typeshed/stdlib/_socket.pyi | 2 +- mypy/typeshed/stdlib/argparse.pyi | 20 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 4 +- mypy/typeshed/stdlib/bdb.pyi | 9 +- mypy/typeshed/stdlib/builtins.pyi | 2 +- mypy/typeshed/stdlib/cmath.pyi | 16 +- mypy/typeshed/stdlib/decimal.pyi | 22 +- .../stdlib/email/_header_value_parser.pyi | 4 + mypy/typeshed/stdlib/enum.pyi | 6 +- mypy/typeshed/stdlib/http/server.pyi | 2 +- .../stdlib/importlib/resources/_common.pyi | 2 +- mypy/typeshed/stdlib/inspect.pyi | 6 +- mypy/typeshed/stdlib/ipaddress.pyi | 2 +- mypy/typeshed/stdlib/json/encoder.pyi | 10 +- mypy/typeshed/stdlib/json/scanner.pyi | 4 + mypy/typeshed/stdlib/math.pyi | 12 +- mypy/typeshed/stdlib/optparse.pyi | 41 ++-- mypy/typeshed/stdlib/os/__init__.pyi | 4 + mypy/typeshed/stdlib/posix.pyi | 1 + mypy/typeshed/stdlib/re.pyi | 38 ++-- mypy/typeshed/stdlib/shutil.pyi | 4 +- mypy/typeshed/stdlib/socket.pyi | 2 +- mypy/typeshed/stdlib/sre_constants.pyi | 188 +++++++++--------- mypy/typeshed/stdlib/sys/__init__.pyi | 1 + mypy/typeshed/stdlib/tkinter/ttk.pyi | 2 +- mypy/typeshed/stdlib/tokenize.pyi | 8 +- mypy/typeshed/stdlib/types.pyi | 1 + 29 files changed, 272 insertions(+), 202 deletions(-) create mode 100644 misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch diff --git a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch new file mode 100644 index 000000000000..d0b1aca381df --- /dev/null +++ b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch @@ -0,0 +1,45 @@ +From b5f2cc9633f9f6cd9326eee96a32efb3aff70701 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sat, 15 Feb 2025 20:11:06 +0100 +Subject: [PATCH] Partially revert Clean up argparse hacks + +--- + mypy/typeshed/stdlib/argparse.pyi | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi +index 029bfeefe..9dbd8c308 100644 +--- a/mypy/typeshed/stdlib/argparse.pyi ++++ b/mypy/typeshed/stdlib/argparse.pyi +@@ -2,7 +2,7 @@ import sys + from _typeshed import SupportsWrite, sentinel + from collections.abc import Callable, Generator, Iterable, Sequence + from re import Pattern +-from typing import IO, Any, ClassVar, Final, Generic, NoReturn, Protocol, TypeVar, overload ++from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload + from typing_extensions import Self, TypeAlias, deprecated + + __all__ = [ +@@ -38,7 +38,9 @@ ONE_OR_MORE: Final = "+" + OPTIONAL: Final = "?" + PARSER: Final = "A..." + REMAINDER: Final = "..." +-SUPPRESS: Final = "==SUPPRESS==" ++_SUPPRESS_T = NewType("_SUPPRESS_T", str) ++SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is ++# the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy + ZERO_OR_MORE: Final = "*" + _UNRECOGNIZED_ARGS_ATTR: Final = "_unrecognized_args" # undocumented + +@@ -81,7 +83,7 @@ class _ActionsContainer: + # more precisely, Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="], + # but using this would make it hard to annotate callers that don't use a + # literal argument and for subclasses to override this method. +- nargs: int | str | None = None, ++ nargs: int | str | _SUPPRESS_T | None = None, + const: Any = ..., + default: Any = ..., + type: _ActionType = ..., +-- +2.48.1 + diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index cdd0268a1bdf..06c0197dcf07 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -27,14 +27,14 @@ _TrapType: TypeAlias = type[DecimalException] __version__: Final[str] __libmpdec_version__: Final[str] -ROUND_DOWN: Final[str] -ROUND_HALF_UP: Final[str] -ROUND_HALF_EVEN: Final[str] -ROUND_CEILING: Final[str] -ROUND_FLOOR: Final[str] -ROUND_UP: Final[str] -ROUND_HALF_DOWN: Final[str] -ROUND_05UP: Final[str] +ROUND_DOWN: Final = "ROUND_DOWN" +ROUND_HALF_UP: Final = "ROUND_HALF_UP" +ROUND_HALF_EVEN: Final = "ROUND_HALF_EVEN" +ROUND_CEILING: Final = "ROUND_CEILING" +ROUND_FLOOR: Final = "ROUND_FLOOR" +ROUND_UP: Final = "ROUND_UP" +ROUND_HALF_DOWN: Final = "ROUND_HALF_DOWN" +ROUND_05UP: Final = "ROUND_05UP" HAVE_CONTEXTVAR: Final[bool] HAVE_THREADS: Final[bool] MAX_EMAX: Final[int] diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 4cf71cbcadfa..9be0c3f2e669 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -78,7 +78,7 @@ if sys.platform == "win32": SO_EXCLUSIVEADDRUSE: int if sys.platform != "win32": SO_REUSEPORT: int - if sys.platform != "darwin" or sys.version_info >= (3, 13): + if sys.platform != "darwin": SO_BINDTODEVICE: int if sys.platform != "win32" and sys.platform != "darwin": diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index b9652ec5f75a..9dbd8c308b59 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -33,15 +33,6 @@ _ActionT = TypeVar("_ActionT", bound=Action) _ArgumentParserT = TypeVar("_ArgumentParserT", bound=ArgumentParser) _N = TypeVar("_N") _ActionType: TypeAlias = Callable[[str], Any] | FileType | str -# more precisely, Literal["store", "store_const", "store_true", -# "store_false", "append", "append_const", "count", "help", "version", -# "extend"], but using this would make it hard to annotate callers -# that don't use a literal argument -_ActionStr: TypeAlias = str -# more precisely, Literal["?", "*", "+", "...", "A...", -# "==SUPPRESS=="], but using this would make it hard to annotate -# callers that don't use a literal argument -_NArgsStr: TypeAlias = str ONE_OR_MORE: Final = "+" OPTIONAL: Final = "?" @@ -51,7 +42,7 @@ _SUPPRESS_T = NewType("_SUPPRESS_T", str) SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is # the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy ZERO_OR_MORE: Final = "*" -_UNRECOGNIZED_ARGS_ATTR: Final[str] # undocumented +_UNRECOGNIZED_ARGS_ATTR: Final = "_unrecognized_args" # undocumented class ArgumentError(Exception): argument_name: str | None @@ -86,8 +77,13 @@ class _ActionsContainer: def add_argument( self, *name_or_flags: str, - action: _ActionStr | type[Action] = ..., - nargs: int | _NArgsStr | _SUPPRESS_T | None = None, + # str covers predefined actions ("store_true", "count", etc.) + # and user registered actions via the `register` method. + action: str | type[Action] = ..., + # more precisely, Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="], + # but using this would make it hard to annotate callers that don't use a + # literal argument and for subclasses to override this method. + nargs: int | str | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., type: _ActionType = ..., diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index a349e81d80e9..f6ee109915e0 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -79,6 +79,7 @@ if sys.version_info >= (3, 12): _FutureLike: TypeAlias = Future[_T] | Awaitable[_T] else: _FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] + _TaskYieldType: TypeAlias = Future[object] | None FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED @@ -347,7 +348,8 @@ else: *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[list[_T | BaseException]]: ... -def run_coroutine_threadsafe(coro: _FutureLike[_T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... +# unlike some asyncio apis, This does strict runtime checking of actually being a coroutine, not of any future-like. +def run_coroutine_threadsafe(coro: Coroutine[Any, Any, _T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... if sys.version_info >= (3, 10): def shield(arg: _FutureLike[_T]) -> Future[_T]: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index 75bfa91cc379..2004874a52b2 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ExcInfo, TraceFunction, Unused -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping +from contextlib import contextmanager from types import CodeType, FrameType, TracebackType from typing import IO, Any, Final, SupportsInt, TypeVar from typing_extensions import ParamSpec @@ -30,6 +31,10 @@ class Bdb: def __init__(self, skip: Iterable[str] | None = None) -> None: ... def canonic(self, filename: str) -> str: ... def reset(self) -> None: ... + if sys.version_info >= (3, 12): + @contextmanager + def set_enterframe(self, frame: FrameType) -> Iterator[None]: ... + def trace_dispatch(self, frame: FrameType, event: str, arg: Any) -> TraceFunction: ... def dispatch_line(self, frame: FrameType) -> TraceFunction: ... def dispatch_call(self, frame: FrameType, arg: None) -> TraceFunction: ... @@ -73,7 +78,7 @@ class Bdb: def get_file_breaks(self, filename: str) -> list[Breakpoint]: ... def get_all_breaks(self) -> list[Breakpoint]: ... def get_stack(self, f: FrameType | None, t: TracebackType | None) -> tuple[list[tuple[FrameType, int]], int]: ... - def format_stack_entry(self, frame_lineno: int, lprefix: str = ": ") -> str: ... + def format_stack_entry(self, frame_lineno: tuple[FrameType, int], lprefix: str = ": ") -> str: ... def run( self, cmd: str | CodeType, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None ) -> None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 6fb901b9f009..c278707c273f 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1197,7 +1197,7 @@ def ascii(obj: object, /) -> str: ... def bin(number: int | SupportsIndex, /) -> str: ... def breakpoint(*args: Any, **kws: Any) -> None: ... def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ... -def chr(i: int, /) -> str: ... +def chr(i: int | SupportsIndex, /) -> str: ... # We define this here instead of using os.PathLike to avoid import cycle issues. # See https://github.com/python/typeshed/pull/991#issuecomment-288160993 diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index fab9d10230f8..a08addcf5438 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -1,13 +1,13 @@ -from typing import SupportsComplex, SupportsFloat, SupportsIndex +from typing import Final, SupportsComplex, SupportsFloat, SupportsIndex from typing_extensions import TypeAlias -e: float -pi: float -inf: float -infj: complex -nan: float -nanj: complex -tau: float +e: Final[float] +pi: Final[float] +inf: Final[float] +infj: Final[complex] +nan: Final[float] +nanj: Final[complex] +tau: Final[float] _C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 7eb922c8a7ed..4ded21e0b017 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -65,7 +65,7 @@ class Underflow(Inexact, Rounded, Subnormal): ... class FloatOperation(DecimalException, TypeError): ... class Decimal: - def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... + def __new__(cls, value: _DecimalNew = "0", context: Context | None = None) -> Self: ... @classmethod def from_float(cls, f: float, /) -> Self: ... def __bool__(self) -> bool: ... @@ -163,12 +163,12 @@ class Decimal: def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... def __copy__(self) -> Self: ... def __deepcopy__(self, memo: Any, /) -> Self: ... - def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... + def __format__(self, specifier: str, context: Context | None = None, /) -> str: ... class Context: # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, # even settable attributes like `prec` and `rounding`, - # but that's inexpressable in the stub. + # but that's inexpressible in the stub. # Type checkers either ignore it or misinterpret it # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub prec: int @@ -181,14 +181,14 @@ class Context: flags: dict[_TrapType, bool] def __init__( self, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + prec: int | None = None, + rounding: str | None = None, + Emin: int | None = None, + Emax: int | None = None, + capitals: int | None = None, + clamp: int | None = None, + flags: dict[_TrapType, bool] | Container[_TrapType] | None = None, + traps: dict[_TrapType, bool] | Container[_TrapType] | None = None, ) -> None: ... def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... def clear_flags(self) -> None: ... diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index ff405a8b61d2..a4c2d8b1a92e 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Iterable, Iterator from email.errors import HeaderParseError, MessageDefect from email.policy import Policy @@ -21,6 +22,9 @@ NLSET: Final[set[str]] # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 SPECIALSNL: Final[set[str]] +if sys.version_info >= (3, 12): + def make_quoted_pairs(value: Any) -> str: ... + def quote_string(value: Any) -> str: ... rfc2047_matcher: Pattern[str] diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 3b6c325522d7..4a6287a712af 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -64,7 +64,11 @@ if sys.version_info >= (3, 11): def __init__(self, value: _EnumMemberT) -> None: ... class _EnumDict(dict[str, Any]): - def __init__(self) -> None: ... + if sys.version_info >= (3, 13): + def __init__(self, cls_name: str | None = None) -> None: ... + else: + def __init__(self) -> None: ... + def __setitem__(self, key: str, value: Any) -> None: ... if sys.version_info >= (3, 11): # See comment above `typing.MutableMapping.update` diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi index 07cde553c1df..b273e19c10cd 100644 --- a/mypy/typeshed/stdlib/http/server.pyi +++ b/mypy/typeshed/stdlib/http/server.pyi @@ -61,7 +61,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): client_address: _socket._RetAddress, server: socketserver.BaseServer, *, - directory: str | None = None, + directory: StrPath | None = None, ) -> None: ... def do_GET(self) -> None: ... def do_HEAD(self) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi index f04f70f25e23..f1056f62ed6e 100644 --- a/mypy/typeshed/stdlib/importlib/resources/_common.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi @@ -16,7 +16,7 @@ if sys.version_info >= (3, 11): Anchor: TypeAlias = Package def package_to_anchor( - func: Callable[[Anchor | None], Traversable] + func: Callable[[Anchor | None], Traversable], ) -> Callable[[Anchor | None, Anchor | None], Traversable]: ... @overload def files(anchor: Anchor | None = None) -> Traversable: ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index c6836c837eaa..43b3dd529887 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -370,7 +370,7 @@ if sys.version_info >= (3, 12): AGEN_CLOSED: Final = "AGEN_CLOSED" def getasyncgenstate( - agen: AsyncGenerator[Any, Any] + agen: AsyncGenerator[Any, Any], ) -> Literal["AGEN_CREATED", "AGEN_RUNNING", "AGEN_SUSPENDED", "AGEN_CLOSED"]: ... def getasyncgenlocals(agen: AsyncGeneratorType[Any, Any]) -> dict[str, Any]: ... @@ -590,7 +590,7 @@ GEN_SUSPENDED: Final = "GEN_SUSPENDED" GEN_CLOSED: Final = "GEN_CLOSED" def getgeneratorstate( - generator: Generator[Any, Any, Any] + generator: Generator[Any, Any, Any], ) -> Literal["GEN_CREATED", "GEN_RUNNING", "GEN_SUSPENDED", "GEN_CLOSED"]: ... CORO_CREATED: Final = "CORO_CREATED" @@ -599,7 +599,7 @@ CORO_SUSPENDED: Final = "CORO_SUSPENDED" CORO_CLOSED: Final = "CORO_CLOSED" def getcoroutinestate( - coroutine: Coroutine[Any, Any, Any] + coroutine: Coroutine[Any, Any, Any], ) -> Literal["CORO_CREATED", "CORO_RUNNING", "CORO_SUSPENDED", "CORO_CLOSED"]: ... def getgeneratorlocals(generator: Generator[Any, Any, Any]) -> dict[str, Any]: ... def getcoroutinelocals(coroutine: Coroutine[Any, Any, Any]) -> dict[str, Any]: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 0563ed9b00ba..e8e81abc6f79 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -18,7 +18,7 @@ def ip_network( address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int], strict: bool = True ) -> IPv4Network | IPv6Network: ... def ip_interface( - address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int] + address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int], ) -> IPv4Interface | IPv6Interface: ... class _IPAddressBase: diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index aa4a3bdf61d4..83b78666d4a7 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -2,11 +2,11 @@ from collections.abc import Callable, Iterator from re import Pattern from typing import Any, Final -ESCAPE: Final[Pattern[str]] -ESCAPE_ASCII: Final[Pattern[str]] -HAS_UTF8: Final[Pattern[bytes]] -ESCAPE_DCT: Final[dict[str, str]] -INFINITY: Final[float] +ESCAPE: Final[Pattern[str]] # undocumented +ESCAPE_ASCII: Final[Pattern[str]] # undocumented +HAS_UTF8: Final[Pattern[bytes]] # undocumented +ESCAPE_DCT: Final[dict[str, str]] # undocumented +INFINITY: Final[float] # undocumented def py_encode_basestring(s: str) -> str: ... # undocumented def py_encode_basestring_ascii(s: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/json/scanner.pyi b/mypy/typeshed/stdlib/json/scanner.pyi index f3b98996b752..68b42e92d295 100644 --- a/mypy/typeshed/stdlib/json/scanner.pyi +++ b/mypy/typeshed/stdlib/json/scanner.pyi @@ -1,3 +1,7 @@ from _json import make_scanner as make_scanner +from re import Pattern +from typing import Final __all__ = ["make_scanner"] + +NUMBER_RE: Final[Pattern[str]] # undocumented diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 2bb61e0669b4..86f71f27580a 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Iterable -from typing import Protocol, SupportsFloat, SupportsIndex, TypeVar, overload +from typing import Final, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload from typing_extensions import TypeAlias _T = TypeVar("_T") @@ -8,11 +8,11 @@ _T_co = TypeVar("_T_co", covariant=True) _SupportsFloatOrIndex: TypeAlias = SupportsFloat | SupportsIndex -e: float -pi: float -inf: float -nan: float -tau: float +e: Final[float] +pi: Final[float] +inf: Final[float] +nan: Final[float] +tau: Final[float] def acos(x: _SupportsFloatOrIndex, /) -> float: ... def acosh(x: _SupportsFloatOrIndex, /) -> float: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index ff5e83cf26db..56a4574bdba8 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,8 +1,8 @@ import builtins -from _typeshed import Incomplete, MaybeNone +from _typeshed import MaybeNone, SupportsWrite from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, ClassVar, Literal, NoReturn, overload +from typing import Any, ClassVar, Final, Literal, NoReturn, overload from typing_extensions import Self __all__ = [ @@ -24,10 +24,10 @@ __all__ = [ "BadOptionError", "check_choice", ] - -NO_DEFAULT: tuple[str, ...] -SUPPRESS_HELP: str -SUPPRESS_USAGE: str +# pytype is not happy with `NO_DEFAULT: Final = ("NO", "DEFAULT")` +NO_DEFAULT: Final[tuple[Literal["NO"], Literal["DEFAULT"]]] +SUPPRESS_HELP: Final = "SUPPRESSHELP" +SUPPRESS_USAGE: Final = "SUPPRESSUSAGE" # Can return complex, float, or int depending on the option's type def check_builtin(option: Option, opt: str, value: str) -> complex: ... @@ -274,13 +274,13 @@ class OptionParser(OptionContainer): def _add_version_option(self) -> None: ... def _create_option_list(self) -> None: ... def _get_all_options(self) -> list[Option]: ... - def _get_args(self, args: Iterable[Incomplete]) -> list[Incomplete]: ... + def _get_args(self, args: list[str] | None) -> list[str]: ... def _init_parsing_state(self) -> None: ... def _match_long_opt(self, opt: str) -> str: ... - def _populate_option_list(self, option_list: Iterable[Option], add_help: bool = True) -> None: ... - def _process_args(self, largs: list[Incomplete], rargs: list[Incomplete], values: Values) -> None: ... - def _process_long_opt(self, rargs: list[Incomplete], values) -> None: ... - def _process_short_opts(self, rargs: list[Incomplete], values) -> None: ... + def _populate_option_list(self, option_list: Iterable[Option] | None, add_help: bool = True) -> None: ... + def _process_args(self, largs: list[str], rargs: list[str], values: Values) -> None: ... + def _process_long_opt(self, rargs: list[str], values: Values) -> None: ... + def _process_short_opts(self, rargs: list[str], values: Values) -> None: ... @overload def add_option_group(self, opt_group: OptionGroup, /) -> OptionGroup: ... @overload @@ -299,14 +299,11 @@ class OptionParser(OptionContainer): def get_prog_name(self) -> str: ... def get_usage(self) -> str: ... def get_version(self) -> str: ... - @overload - def parse_args(self, args: None = None, values: Values | None = None) -> tuple[Values, list[str]]: ... - @overload - def parse_args(self, args: Sequence[AnyStr], values: Values | None = None) -> tuple[Values, list[AnyStr]]: ... - def print_usage(self, file: IO[str] | None = None) -> None: ... - def print_help(self, file: IO[str] | None = None) -> None: ... - def print_version(self, file: IO[str] | None = None) -> None: ... - def set_default(self, dest, value) -> None: ... - def set_defaults(self, **kwargs) -> None: ... - def set_process_default_values(self, process) -> None: ... - def set_usage(self, usage: str) -> None: ... + def parse_args(self, args: list[str] | None = None, values: Values | None = None) -> tuple[Values, list[str]]: ... + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_help(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_version(self, file: SupportsWrite[str] | None = None) -> None: ... + def set_default(self, dest: str, value: Any) -> None: ... # default value can be "any" type + def set_defaults(self, **kwargs: Any) -> None: ... # default values can be "any" type + def set_process_default_values(self, process: bool) -> None: ... + def set_usage(self, usage: str | None) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 64691b514a48..4a7c03632a67 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -240,6 +240,7 @@ if sys.platform == "linux" and sys.version_info >= (3, 12): "CLONE_VM", "setns", "unshare", + "PIDFD_NONBLOCK", ] if sys.platform == "linux" and sys.version_info >= (3, 10): __all__ += [ @@ -1603,6 +1604,9 @@ if sys.version_info >= (3, 9): if sys.platform == "linux": def pidfd_open(pid: int, flags: int = ...) -> int: ... +if sys.version_info >= (3, 12) and sys.platform == "linux": + PIDFD_NONBLOCK: Final = 2048 + if sys.version_info >= (3, 12) and sys.platform == "win32": def listdrives() -> list[str]: ... def listmounts(volume: str) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 7a4d6cb4bdbe..e7223842ace5 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -379,6 +379,7 @@ if sys.platform != "win32": CLONE_SYSVSEM as CLONE_SYSVSEM, CLONE_THREAD as CLONE_THREAD, CLONE_VM as CLONE_VM, + PIDFD_NONBLOCK as PIDFD_NONBLOCK, setns as setns, unshare as unshare, ) diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index b8fe2e9e1a46..fccdedae9436 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -4,7 +4,7 @@ import sre_constants import sys from _typeshed import MaybeNone, ReadableBuffer from collections.abc import Callable, Iterator, Mapping -from typing import Any, AnyStr, Generic, Literal, TypeVar, final, overload +from typing import Any, AnyStr, Final, Generic, Literal, TypeVar, final, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 9): @@ -224,25 +224,27 @@ class RegexFlag(enum.IntFlag): if sys.version_info >= (3, 11): NOFLAG = 0 -A = RegexFlag.A -ASCII = RegexFlag.ASCII -DEBUG = RegexFlag.DEBUG -I = RegexFlag.I -IGNORECASE = RegexFlag.IGNORECASE -L = RegexFlag.L -LOCALE = RegexFlag.LOCALE -M = RegexFlag.M -MULTILINE = RegexFlag.MULTILINE -S = RegexFlag.S -DOTALL = RegexFlag.DOTALL -X = RegexFlag.X -VERBOSE = RegexFlag.VERBOSE -U = RegexFlag.U -UNICODE = RegexFlag.UNICODE +A: Final = RegexFlag.A +ASCII: Final = RegexFlag.ASCII +DEBUG: Final = RegexFlag.DEBUG +I: Final = RegexFlag.I +IGNORECASE: Final = RegexFlag.IGNORECASE +L: Final = RegexFlag.L +LOCALE: Final = RegexFlag.LOCALE +M: Final = RegexFlag.M +MULTILINE: Final = RegexFlag.MULTILINE +S: Final = RegexFlag.S +DOTALL: Final = RegexFlag.DOTALL +X: Final = RegexFlag.X +VERBOSE: Final = RegexFlag.VERBOSE +U: Final = RegexFlag.U +UNICODE: Final = RegexFlag.UNICODE if sys.version_info < (3, 13): - T = RegexFlag.T - TEMPLATE = RegexFlag.TEMPLATE + T: Final = RegexFlag.T + TEMPLATE: Final = RegexFlag.TEMPLATE if sys.version_info >= (3, 11): + # pytype chokes on `NOFLAG: Final = RegexFlag.NOFLAG` with `LiteralValueError` + # mypy chokes on `NOFLAG: Final[Literal[RegexFlag.NOFLAG]]` with `Literal[...] is invalid` NOFLAG = RegexFlag.NOFLAG _FlagsType: TypeAlias = int | RegexFlag diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index dcff18d110bd..4a19a96a306c 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -83,7 +83,7 @@ class _RmtreeType(Protocol): self, path: StrOrBytesPath, ignore_errors: bool, - onerror: _OnErrorCallback, + onerror: _OnErrorCallback | None, *, onexc: None = None, dir_fd: int | None = None, @@ -95,7 +95,7 @@ class _RmtreeType(Protocol): path: StrOrBytesPath, ignore_errors: bool = False, *, - onerror: _OnErrorCallback, + onerror: _OnErrorCallback | None, onexc: None = None, dir_fd: int | None = None, ) -> None: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index f982c9b893d8..1c996ac32278 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -515,7 +515,7 @@ if sys.platform != "win32": "IPV6_RTHDRDSTOPTS", ] - if sys.platform != "darwin" or sys.version_info >= (3, 13): + if sys.platform != "darwin": from _socket import SO_BINDTODEVICE as SO_BINDTODEVICE __all__ += ["SO_BINDTODEVICE"] diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi index 383f0f7eb8bd..c41a52b26d5a 100644 --- a/mypy/typeshed/stdlib/sre_constants.pyi +++ b/mypy/typeshed/stdlib/sre_constants.pyi @@ -1,17 +1,17 @@ import sys from re import error as error -from typing import Any +from typing import Final from typing_extensions import Self -MAXGROUPS: int +MAXGROUPS: Final[int] -MAGIC: int +MAGIC: Final[int] class _NamedIntConstant(int): - name: Any + name: str def __new__(cls, value: int, name: str) -> Self: ... -MAXREPEAT: _NamedIntConstant +MAXREPEAT: Final[_NamedIntConstant] OPCODES: list[_NamedIntConstant] ATCODES: list[_NamedIntConstant] CHCODES: list[_NamedIntConstant] @@ -23,102 +23,104 @@ AT_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] AT_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] CH_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] CH_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] +# flags if sys.version_info < (3, 13): - SRE_FLAG_TEMPLATE: int -SRE_FLAG_IGNORECASE: int -SRE_FLAG_LOCALE: int -SRE_FLAG_MULTILINE: int -SRE_FLAG_DOTALL: int -SRE_FLAG_UNICODE: int -SRE_FLAG_VERBOSE: int -SRE_FLAG_DEBUG: int -SRE_FLAG_ASCII: int -SRE_INFO_PREFIX: int -SRE_INFO_LITERAL: int -SRE_INFO_CHARSET: int + SRE_FLAG_TEMPLATE: Final = 1 +SRE_FLAG_IGNORECASE: Final = 2 +SRE_FLAG_LOCALE: Final = 4 +SRE_FLAG_MULTILINE: Final = 8 +SRE_FLAG_DOTALL: Final = 16 +SRE_FLAG_UNICODE: Final = 32 +SRE_FLAG_VERBOSE: Final = 64 +SRE_FLAG_DEBUG: Final = 128 +SRE_FLAG_ASCII: Final = 256 +# flags for INFO primitive +SRE_INFO_PREFIX: Final = 1 +SRE_INFO_LITERAL: Final = 2 +SRE_INFO_CHARSET: Final = 4 # Stubgen above; manually defined constants below (dynamic at runtime) # from OPCODES -FAILURE: _NamedIntConstant -SUCCESS: _NamedIntConstant -ANY: _NamedIntConstant -ANY_ALL: _NamedIntConstant -ASSERT: _NamedIntConstant -ASSERT_NOT: _NamedIntConstant -AT: _NamedIntConstant -BRANCH: _NamedIntConstant +FAILURE: Final[_NamedIntConstant] +SUCCESS: Final[_NamedIntConstant] +ANY: Final[_NamedIntConstant] +ANY_ALL: Final[_NamedIntConstant] +ASSERT: Final[_NamedIntConstant] +ASSERT_NOT: Final[_NamedIntConstant] +AT: Final[_NamedIntConstant] +BRANCH: Final[_NamedIntConstant] if sys.version_info < (3, 11): - CALL: _NamedIntConstant -CATEGORY: _NamedIntConstant -CHARSET: _NamedIntConstant -BIGCHARSET: _NamedIntConstant -GROUPREF: _NamedIntConstant -GROUPREF_EXISTS: _NamedIntConstant -GROUPREF_IGNORE: _NamedIntConstant -IN: _NamedIntConstant -IN_IGNORE: _NamedIntConstant -INFO: _NamedIntConstant -JUMP: _NamedIntConstant -LITERAL: _NamedIntConstant -LITERAL_IGNORE: _NamedIntConstant -MARK: _NamedIntConstant -MAX_UNTIL: _NamedIntConstant -MIN_UNTIL: _NamedIntConstant -NOT_LITERAL: _NamedIntConstant -NOT_LITERAL_IGNORE: _NamedIntConstant -NEGATE: _NamedIntConstant -RANGE: _NamedIntConstant -REPEAT: _NamedIntConstant -REPEAT_ONE: _NamedIntConstant -SUBPATTERN: _NamedIntConstant -MIN_REPEAT_ONE: _NamedIntConstant + CALL: Final[_NamedIntConstant] +CATEGORY: Final[_NamedIntConstant] +CHARSET: Final[_NamedIntConstant] +BIGCHARSET: Final[_NamedIntConstant] +GROUPREF: Final[_NamedIntConstant] +GROUPREF_EXISTS: Final[_NamedIntConstant] +GROUPREF_IGNORE: Final[_NamedIntConstant] +IN: Final[_NamedIntConstant] +IN_IGNORE: Final[_NamedIntConstant] +INFO: Final[_NamedIntConstant] +JUMP: Final[_NamedIntConstant] +LITERAL: Final[_NamedIntConstant] +LITERAL_IGNORE: Final[_NamedIntConstant] +MARK: Final[_NamedIntConstant] +MAX_UNTIL: Final[_NamedIntConstant] +MIN_UNTIL: Final[_NamedIntConstant] +NOT_LITERAL: Final[_NamedIntConstant] +NOT_LITERAL_IGNORE: Final[_NamedIntConstant] +NEGATE: Final[_NamedIntConstant] +RANGE: Final[_NamedIntConstant] +REPEAT: Final[_NamedIntConstant] +REPEAT_ONE: Final[_NamedIntConstant] +SUBPATTERN: Final[_NamedIntConstant] +MIN_REPEAT_ONE: Final[_NamedIntConstant] if sys.version_info >= (3, 11): - ATOMIC_GROUP: _NamedIntConstant - POSSESSIVE_REPEAT: _NamedIntConstant - POSSESSIVE_REPEAT_ONE: _NamedIntConstant -RANGE_UNI_IGNORE: _NamedIntConstant -GROUPREF_LOC_IGNORE: _NamedIntConstant -GROUPREF_UNI_IGNORE: _NamedIntConstant -IN_LOC_IGNORE: _NamedIntConstant -IN_UNI_IGNORE: _NamedIntConstant -LITERAL_LOC_IGNORE: _NamedIntConstant -LITERAL_UNI_IGNORE: _NamedIntConstant -NOT_LITERAL_LOC_IGNORE: _NamedIntConstant -NOT_LITERAL_UNI_IGNORE: _NamedIntConstant -MIN_REPEAT: _NamedIntConstant -MAX_REPEAT: _NamedIntConstant + ATOMIC_GROUP: Final[_NamedIntConstant] + POSSESSIVE_REPEAT: Final[_NamedIntConstant] + POSSESSIVE_REPEAT_ONE: Final[_NamedIntConstant] +RANGE_UNI_IGNORE: Final[_NamedIntConstant] +GROUPREF_LOC_IGNORE: Final[_NamedIntConstant] +GROUPREF_UNI_IGNORE: Final[_NamedIntConstant] +IN_LOC_IGNORE: Final[_NamedIntConstant] +IN_UNI_IGNORE: Final[_NamedIntConstant] +LITERAL_LOC_IGNORE: Final[_NamedIntConstant] +LITERAL_UNI_IGNORE: Final[_NamedIntConstant] +NOT_LITERAL_LOC_IGNORE: Final[_NamedIntConstant] +NOT_LITERAL_UNI_IGNORE: Final[_NamedIntConstant] +MIN_REPEAT: Final[_NamedIntConstant] +MAX_REPEAT: Final[_NamedIntConstant] # from ATCODES -AT_BEGINNING: _NamedIntConstant -AT_BEGINNING_LINE: _NamedIntConstant -AT_BEGINNING_STRING: _NamedIntConstant -AT_BOUNDARY: _NamedIntConstant -AT_NON_BOUNDARY: _NamedIntConstant -AT_END: _NamedIntConstant -AT_END_LINE: _NamedIntConstant -AT_END_STRING: _NamedIntConstant -AT_LOC_BOUNDARY: _NamedIntConstant -AT_LOC_NON_BOUNDARY: _NamedIntConstant -AT_UNI_BOUNDARY: _NamedIntConstant -AT_UNI_NON_BOUNDARY: _NamedIntConstant +AT_BEGINNING: Final[_NamedIntConstant] +AT_BEGINNING_LINE: Final[_NamedIntConstant] +AT_BEGINNING_STRING: Final[_NamedIntConstant] +AT_BOUNDARY: Final[_NamedIntConstant] +AT_NON_BOUNDARY: Final[_NamedIntConstant] +AT_END: Final[_NamedIntConstant] +AT_END_LINE: Final[_NamedIntConstant] +AT_END_STRING: Final[_NamedIntConstant] +AT_LOC_BOUNDARY: Final[_NamedIntConstant] +AT_LOC_NON_BOUNDARY: Final[_NamedIntConstant] +AT_UNI_BOUNDARY: Final[_NamedIntConstant] +AT_UNI_NON_BOUNDARY: Final[_NamedIntConstant] # from CHCODES -CATEGORY_DIGIT: _NamedIntConstant -CATEGORY_NOT_DIGIT: _NamedIntConstant -CATEGORY_SPACE: _NamedIntConstant -CATEGORY_NOT_SPACE: _NamedIntConstant -CATEGORY_WORD: _NamedIntConstant -CATEGORY_NOT_WORD: _NamedIntConstant -CATEGORY_LINEBREAK: _NamedIntConstant -CATEGORY_NOT_LINEBREAK: _NamedIntConstant -CATEGORY_LOC_WORD: _NamedIntConstant -CATEGORY_LOC_NOT_WORD: _NamedIntConstant -CATEGORY_UNI_DIGIT: _NamedIntConstant -CATEGORY_UNI_NOT_DIGIT: _NamedIntConstant -CATEGORY_UNI_SPACE: _NamedIntConstant -CATEGORY_UNI_NOT_SPACE: _NamedIntConstant -CATEGORY_UNI_WORD: _NamedIntConstant -CATEGORY_UNI_NOT_WORD: _NamedIntConstant -CATEGORY_UNI_LINEBREAK: _NamedIntConstant -CATEGORY_UNI_NOT_LINEBREAK: _NamedIntConstant +CATEGORY_DIGIT: Final[_NamedIntConstant] +CATEGORY_NOT_DIGIT: Final[_NamedIntConstant] +CATEGORY_SPACE: Final[_NamedIntConstant] +CATEGORY_NOT_SPACE: Final[_NamedIntConstant] +CATEGORY_WORD: Final[_NamedIntConstant] +CATEGORY_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_NOT_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_LOC_WORD: Final[_NamedIntConstant] +CATEGORY_LOC_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_DIGIT: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_DIGIT: Final[_NamedIntConstant] +CATEGORY_UNI_SPACE: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_SPACE: Final[_NamedIntConstant] +CATEGORY_UNI_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_LINEBREAK: Final[_NamedIntConstant] diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index d11e64d109b5..4aa1699e8b42 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -396,6 +396,7 @@ def intern(string: str, /) -> str: ... if sys.version_info >= (3, 13): def _is_gil_enabled() -> bool: ... + def _clear_internal_caches() -> None: ... def is_finalizing() -> bool: ... def breakpointhook(*args: Any, **kwargs: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index dacef0620b22..5328e461ebdc 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1100,7 +1100,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): open: bool = ..., tags: str | list[str] | tuple[str, ...] = ..., ) -> None: ... - def move(self, item: str | int, parent: str, index: int) -> None: ... + def move(self, item: str | int, parent: str, index: int | Literal["end"]) -> None: ... reattach = move def next(self, item: str | int) -> str: ... # returning empty string means last item def parent(self, item: str | int) -> str: ... diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 7b68f791a8c0..a1c4b412da83 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -125,14 +125,16 @@ class Untokenizer: prev_col: int encoding: str | None def add_whitespace(self, start: _Position) -> None: ... + if sys.version_info >= (3, 13): + def add_backslash_continuation(self, start: _Position) -> None: ... + def untokenize(self, iterable: Iterable[_Token]) -> str: ... def compat(self, token: Sequence[int | str], iterable: Iterable[_Token]) -> None: ... if sys.version_info >= (3, 12): def escape_brackets(self, token: str) -> str: ... -# the docstring says "returns bytes" but is incorrect -- -# if the ENCODING token is missing, it skips the encode -def untokenize(iterable: Iterable[_Token]) -> Any: ... +# Returns str, unless the ENCODING token is present, in which case it returns bytes. +def untokenize(iterable: Iterable[_Token]) -> str | Any: ... def detect_encoding(readline: Callable[[], bytes | bytearray]) -> tuple[str, Sequence[bytes]]: ... def tokenize(readline: Callable[[], bytes | bytearray]) -> Generator[TokenInfo, None, None]: ... def generate_tokens(readline: Callable[[], str]) -> Generator[TokenInfo, None, None]: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index b294a0b2f8f7..d41ca0d1c367 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -640,6 +640,7 @@ if sys.version_info >= (3, 9): def __getitem__(self, typeargs: Any, /) -> GenericAlias: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... + def __mro_entries__(self, bases: Iterable[object], /) -> tuple[type, ...]: ... if sys.version_info >= (3, 11): @property def __unpacked__(self) -> bool: ... From eb2b5099253b5795593b4691ae6e00298db8fe8f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:06:50 +0100 Subject: [PATCH 1156/1617] [mypyc] Mark dict.setdefault as optimized (#18685) Support for `dict.setdefault` was added in https://github.com/python/mypy/commit/f463a3921fcd5cb360c12a84650880a2a92e0566 a few years ago. --- mypyc/doc/dict_operations.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypyc/doc/dict_operations.rst b/mypyc/doc/dict_operations.rst index e3104172133a..6858cd33e8a7 100644 --- a/mypyc/doc/dict_operations.rst +++ b/mypyc/doc/dict_operations.rst @@ -50,6 +50,8 @@ Methods * ``d.items()`` * ``d.copy()`` * ``d.clear()`` +* ``d.setdefault(key)`` +* ``d.setdefault(key, value)`` * ``d1.update(d2: dict)`` * ``d.update(x: Iterable)`` From 5202c9840265a9c8273f532a71a78462e3f53e39 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:32:34 +0100 Subject: [PATCH 1157/1617] [mypyc] Optimize str.splitlines (#18677) https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_Splitlines --- mypyc/doc/str_operations.rst | 2 ++ mypyc/primitives/str_ops.py | 13 +++++++++++++ mypyc/test-data/fixtures/ir.py | 1 + mypyc/test-data/run-strings.test | 12 ++++++++++++ 4 files changed, 28 insertions(+) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 1419d56b0647..f714d42fe553 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -42,6 +42,8 @@ Methods * ``s.split()`` * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` +* ``s.splitlines()`` +* ``s.splitlines(keepends: bool)`` * ``s1.startswith(s2: str)`` .. note:: diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index fed471cd9a4e..d7c76d1d3312 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -163,6 +163,19 @@ error_kind=ERR_MAGIC, ) +# str.splitlines(...) +str_splitlines_types: list[RType] = [str_rprimitive, bool_rprimitive] +str_splitlines_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], []] +for i in range(2): + method_op( + name="splitlines", + arg_types=str_splitlines_types[0 : i + 1], + return_type=list_rprimitive, + c_function_name="PyUnicode_Splitlines", + extra_int_constants=str_splitlines_constants[i], + error_kind=ERR_NEVER, + ) + # str.replace(old, new) method_op( name="replace", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 01f189b4f08b..0481747208bd 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -104,6 +104,7 @@ def __contains__(self, item: str) -> bool: pass def __iter__(self) -> Iterator[str]: ... def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass + def splitlines(self, keepends: bool = False) -> List[str]: ... def strip (self, item: str) -> str: pass def join(self, x: Iterable[str]) -> str: pass def format(self, *args: Any, **kwargs: Any) -> str: ... diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 3998f6f7dbc4..a18e61c940f8 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -89,6 +89,18 @@ def test_rsplit() -> None: assert do_rsplit(ss, " ", 1) == ["abc abcd abcde", "abcdef"] # different to do_split assert do_rsplit(ss, " ", 2) == ["abc abcd", "abcde", "abcdef"] # different to do_split +def splitlines(s: str, keepends: Optional[bool] = None) -> List[str]: + if keepends is not None: + return s.splitlines(keepends) + return s.splitlines() + +s_text = "This\nis\n\nsome\nlong\ntext.\n" + +def test_splitlines() -> None: + assert splitlines(s_text) == ["This", "is", "", "some", "long", "text."] + assert splitlines(s_text, False) == ["This", "is", "", "some", "long", "text."] + assert splitlines(s_text, True) == ["This\n", "is\n", "\n", "some\n", "long\n", "text.\n"] + def getitem(s: str, index: int) -> str: return s[index] From 0808624c67331f52c2d503ad8afe4f1087b0371c Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Tue, 18 Feb 2025 00:45:37 +0100 Subject: [PATCH 1158/1617] pythoncapi_compat: don't define Py_NULL if it is already defined (#18699) Fixes: #18698 This is a naive fix for the gcc 15 error when compiling for Python 3.12 --- mypyc/lib-rt/pythoncapi_compat.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index e534c1cbb7cc..f94e50a3479f 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -34,6 +34,7 @@ extern "C" { # define _Py_CAST(type, expr) ((type)(expr)) #endif +#ifndef _Py_NULL // Static inline functions should use _Py_NULL rather than using directly NULL // to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, // _Py_NULL is defined as nullptr. @@ -43,6 +44,7 @@ extern "C" { #else # define _Py_NULL NULL #endif +#endif // Cast argument to PyObject* type. #ifndef _PyObject_CAST From 09ac3baf7c32b5125d1b02dd48318097f7c71bc0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:46:32 +0100 Subject: [PATCH 1159/1617] [mypyc] Optimize str.startswith and str.endswith with tuple argument (#18678) --- mypyc/doc/str_operations.rst | 2 + mypyc/lib-rt/CPy.h | 4 +- mypyc/lib-rt/str_ops.c | 40 ++++++++++++++++++- mypyc/primitives/str_ops.py | 27 ++++++++++++- mypyc/test-data/fixtures/ir.py | 4 +- mypyc/test-data/irbuild-str.test | 67 ++++++++++++++++++++++++++++++++ mypyc/test-data/run-strings.test | 22 ++++++++++- 7 files changed, 157 insertions(+), 9 deletions(-) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index f714d42fe553..05612fc55213 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -31,6 +31,7 @@ Methods * ``s.encode(encoding: str)`` * ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` +* ``s1.endswith(t: tuple[str, ...])`` * ``s.join(x: Iterable)`` * ``s.removeprefix(prefix: str)`` * ``s.removesuffix(suffix: str)`` @@ -45,6 +46,7 @@ Methods * ``s.splitlines()`` * ``s.splitlines(keepends: bool)`` * ``s1.startswith(s2: str)`` +* ``s1.startswith(t: tuple[str, ...])`` .. note:: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 93d79a37aaf8..22ab0f253ed7 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -725,8 +725,8 @@ PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split); PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace); PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); -bool CPyStr_Startswith(PyObject *self, PyObject *subobj); -bool CPyStr_Endswith(PyObject *self, PyObject *subobj); +int CPyStr_Startswith(PyObject *self, PyObject *subobj); +int CPyStr_Endswith(PyObject *self, PyObject *subobj); PyObject *CPyStr_Removeprefix(PyObject *self, PyObject *prefix); PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix); bool CPyStr_IsTrue(PyObject *obj); diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 46458f9b57dc..86b36c511b71 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -161,15 +161,51 @@ PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, return PyUnicode_Replace(str, old_substr, new_substr, temp_max_replace); } -bool CPyStr_Startswith(PyObject *self, PyObject *subobj) { +int CPyStr_Startswith(PyObject *self, PyObject *subobj) { Py_ssize_t start = 0; Py_ssize_t end = PyUnicode_GET_LENGTH(self); + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + PyObject *substring = PyTuple_GET_ITEM(subobj, i); + if (!PyUnicode_Check(substring)) { + PyErr_Format(PyExc_TypeError, + "tuple for startswith must only contain str, " + "not %.100s", + Py_TYPE(substring)->tp_name); + return -1; + } + int result = PyUnicode_Tailmatch(self, substring, start, end, -1); + if (result) { + return 1; + } + } + return 0; + } return PyUnicode_Tailmatch(self, subobj, start, end, -1); } -bool CPyStr_Endswith(PyObject *self, PyObject *subobj) { +int CPyStr_Endswith(PyObject *self, PyObject *subobj) { Py_ssize_t start = 0; Py_ssize_t end = PyUnicode_GET_LENGTH(self); + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + PyObject *substring = PyTuple_GET_ITEM(subobj, i); + if (!PyUnicode_Check(substring)) { + PyErr_Format(PyExc_TypeError, + "tuple for endswith must only contain str, " + "not %.100s", + Py_TYPE(substring)->tp_name); + return -1; + } + int result = PyUnicode_Tailmatch(self, substring, start, end, 1); + if (result) { + return 1; + } + } + return 0; + } return PyUnicode_Tailmatch(self, subobj, start, end, 1); } diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index d7c76d1d3312..4c82fe11beec 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -15,6 +15,7 @@ object_rprimitive, pointer_rprimitive, str_rprimitive, + tuple_rprimitive, ) from mypyc.primitives.registry import ( ERR_NEG_INT, @@ -104,20 +105,42 @@ method_op( name="startswith", arg_types=[str_rprimitive, str_rprimitive], - return_type=bool_rprimitive, + return_type=c_int_rprimitive, c_function_name="CPyStr_Startswith", + truncated_type=bool_rprimitive, error_kind=ERR_NEVER, ) +# str.startswith(tuple) (return -1/0/1) +method_op( + name="startswith", + arg_types=[str_rprimitive, tuple_rprimitive], + return_type=c_int_rprimitive, + c_function_name="CPyStr_Startswith", + truncated_type=bool_rprimitive, + error_kind=ERR_NEG_INT, +) + # str.endswith(str) method_op( name="endswith", arg_types=[str_rprimitive, str_rprimitive], - return_type=bool_rprimitive, + return_type=c_int_rprimitive, c_function_name="CPyStr_Endswith", + truncated_type=bool_rprimitive, error_kind=ERR_NEVER, ) +# str.endswith(tuple) (return -1/0/1) +method_op( + name="endswith", + arg_types=[str_rprimitive, tuple_rprimitive], + return_type=c_int_rprimitive, + c_function_name="CPyStr_Endswith", + truncated_type=bool_rprimitive, + error_kind=ERR_NEG_INT, +) + # str.removeprefix(str) method_op( name="removeprefix", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 0481747208bd..50b6815d46a2 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -109,8 +109,8 @@ def strip (self, item: str) -> str: pass def join(self, x: Iterable[str]) -> str: pass def format(self, *args: Any, **kwargs: Any) -> str: ... def upper(self) -> str: ... - def startswith(self, x: str, start: int=..., end: int=...) -> bool: ... - def endswith(self, x: str, start: int=..., end: int=...) -> bool: ... + def startswith(self, x: Union[str, Tuple[str, ...]], start: int=..., end: int=...) -> bool: ... + def endswith(self, x: Union[str, Tuple[str, ...]], start: int=..., end: int=...) -> bool: ... def replace(self, old: str, new: str, maxcount: int=...) -> str: ... def encode(self, encoding: str=..., errors: str=...) -> bytes: ... def removeprefix(self, prefix: str, /) -> str: ... diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index af77a351fb62..9294d4c3d2e3 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -137,6 +137,73 @@ L4: L5: unreachable +[case testStrStartswithEndswithTuple] +from typing import Tuple + +def do_startswith(s1: str, s2: Tuple[str, ...]) -> bool: + return s1.startswith(s2) + +def do_endswith(s1: str, s2: Tuple[str, ...]) -> bool: + return s1.endswith(s2) + +def do_tuple_literal_args(s1: str) -> None: + x = s1.startswith(("a", "b")) + y = s1.endswith(("a", "b")) +[out] +def do_startswith(s1, s2): + s1 :: str + s2 :: tuple + r0 :: i32 + r1 :: bit + r2 :: bool +L0: + r0 = CPyStr_Startswith(s1, s2) + r1 = r0 >= 0 :: signed + r2 = truncate r0: i32 to builtins.bool + return r2 +def do_endswith(s1, s2): + s1 :: str + s2 :: tuple + r0 :: i32 + r1 :: bit + r2 :: bool +L0: + r0 = CPyStr_Endswith(s1, s2) + r1 = r0 >= 0 :: signed + r2 = truncate r0: i32 to builtins.bool + return r2 +def do_tuple_literal_args(s1): + s1, r0, r1 :: str + r2 :: tuple[str, str] + r3 :: object + r4 :: i32 + r5 :: bit + r6, x :: bool + r7, r8 :: str + r9 :: tuple[str, str] + r10 :: object + r11 :: i32 + r12 :: bit + r13, y :: bool +L0: + r0 = 'a' + r1 = 'b' + r2 = (r0, r1) + r3 = box(tuple[str, str], r2) + r4 = CPyStr_Startswith(s1, r3) + r5 = r4 >= 0 :: signed + r6 = truncate r4: i32 to builtins.bool + x = r6 + r7 = 'a' + r8 = 'b' + r9 = (r7, r8) + r10 = box(tuple[str, str], r9) + r11 = CPyStr_Endswith(s1, r10) + r12 = r11 >= 0 :: signed + r13 = truncate r11: i32 to builtins.bool + y = r13 + return 1 + [case testStrToBool] def is_true(x: str) -> bool: if x: diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index a18e61c940f8..f96824a1cad0 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -20,12 +20,20 @@ def eq(x: str) -> int: return 2 def match(x: str, y: str) -> Tuple[bool, bool]: return (x.startswith(y), x.endswith(y)) +def match_tuple(x: str, y: Tuple[str, ...]) -> Tuple[bool, bool]: + return (x.startswith(y), x.endswith(y)) +def match_tuple_literal_args(x: str, y: str, z: str) -> Tuple[bool, bool]: + return (x.startswith((y, z)), x.endswith((y, z))) def remove_prefix_suffix(x: str, y: str) -> Tuple[str, str]: return (x.removeprefix(y), x.removesuffix(y)) [file driver.py] -from native import f, g, tostr, booltostr, concat, eq, match, remove_prefix_suffix +from native import ( + f, g, tostr, booltostr, concat, eq, match, match_tuple, + match_tuple_literal_args, remove_prefix_suffix +) import sys +from testutil import assertRaises assert f() == 'some string' assert f() is sys.intern('some string') @@ -45,6 +53,18 @@ assert match('abc', '') == (True, True) assert match('abc', 'a') == (True, False) assert match('abc', 'c') == (False, True) assert match('', 'abc') == (False, False) +assert match_tuple('abc', ('d', 'e')) == (False, False) +assert match_tuple('abc', ('a', 'c')) == (True, True) +assert match_tuple('abc', ('a',)) == (True, False) +assert match_tuple('abc', ('c',)) == (False, True) +assert match_tuple('abc', ('x', 'y', 'z')) == (False, False) +assert match_tuple('abc', ('x', 'y', 'z', 'a', 'c')) == (True, True) +with assertRaises(TypeError, "tuple for startswith must only contain str"): + assert match_tuple('abc', (None,)) +with assertRaises(TypeError, "tuple for endswith must only contain str"): + assert match_tuple('abc', ('a', None)) +assert match_tuple_literal_args('abc', 'z', 'a') == (True, False) +assert match_tuple_literal_args('abc', 'z', 'c') == (False, True) assert remove_prefix_suffix('', '') == ('', '') assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc') From 3ef6ab10389959cc17bd541ef7d5ae23cb55140a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:37:22 +0100 Subject: [PATCH 1160/1617] [mypyc] Improve str.startswith and str.endswith with tuple argument (#18703) Followup to #18678 Missed that we can also use `bool_rprimitive` as return type with a value of `2` for errors. --- mypyc/lib-rt/str_ops.c | 4 +-- mypyc/primitives/str_ops.py | 14 ++++------ mypyc/test-data/irbuild-str.test | 48 +++++++++++--------------------- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 86b36c511b71..00759166df35 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -173,7 +173,7 @@ int CPyStr_Startswith(PyObject *self, PyObject *subobj) { "tuple for startswith must only contain str, " "not %.100s", Py_TYPE(substring)->tp_name); - return -1; + return 2; } int result = PyUnicode_Tailmatch(self, substring, start, end, -1); if (result) { @@ -197,7 +197,7 @@ int CPyStr_Endswith(PyObject *self, PyObject *subobj) { "tuple for endswith must only contain str, " "not %.100s", Py_TYPE(substring)->tp_name); - return -1; + return 2; } int result = PyUnicode_Tailmatch(self, substring, start, end, 1); if (result) { diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 4c82fe11beec..d573b8017aa8 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -111,14 +111,13 @@ error_kind=ERR_NEVER, ) -# str.startswith(tuple) (return -1/0/1) +# str.startswith(tuple) method_op( name="startswith", arg_types=[str_rprimitive, tuple_rprimitive], - return_type=c_int_rprimitive, + return_type=bool_rprimitive, c_function_name="CPyStr_Startswith", - truncated_type=bool_rprimitive, - error_kind=ERR_NEG_INT, + error_kind=ERR_MAGIC, ) # str.endswith(str) @@ -131,14 +130,13 @@ error_kind=ERR_NEVER, ) -# str.endswith(tuple) (return -1/0/1) +# str.endswith(tuple) method_op( name="endswith", arg_types=[str_rprimitive, tuple_rprimitive], - return_type=c_int_rprimitive, + return_type=bool_rprimitive, c_function_name="CPyStr_Endswith", - truncated_type=bool_rprimitive, - error_kind=ERR_NEG_INT, + error_kind=ERR_MAGIC, ) # str.removeprefix(str) diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 9294d4c3d2e3..352fb6cf72d9 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -153,55 +153,39 @@ def do_tuple_literal_args(s1: str) -> None: def do_startswith(s1, s2): s1 :: str s2 :: tuple - r0 :: i32 - r1 :: bit - r2 :: bool + r0 :: bool L0: r0 = CPyStr_Startswith(s1, s2) - r1 = r0 >= 0 :: signed - r2 = truncate r0: i32 to builtins.bool - return r2 + return r0 def do_endswith(s1, s2): s1 :: str s2 :: tuple - r0 :: i32 - r1 :: bit - r2 :: bool + r0 :: bool L0: r0 = CPyStr_Endswith(s1, s2) - r1 = r0 >= 0 :: signed - r2 = truncate r0: i32 to builtins.bool - return r2 + return r0 def do_tuple_literal_args(s1): s1, r0, r1 :: str r2 :: tuple[str, str] r3 :: object - r4 :: i32 - r5 :: bit - r6, x :: bool - r7, r8 :: str - r9 :: tuple[str, str] - r10 :: object - r11 :: i32 - r12 :: bit - r13, y :: bool + r4, x :: bool + r5, r6 :: str + r7 :: tuple[str, str] + r8 :: object + r9, y :: bool L0: r0 = 'a' r1 = 'b' r2 = (r0, r1) r3 = box(tuple[str, str], r2) r4 = CPyStr_Startswith(s1, r3) - r5 = r4 >= 0 :: signed - r6 = truncate r4: i32 to builtins.bool - x = r6 - r7 = 'a' - r8 = 'b' - r9 = (r7, r8) - r10 = box(tuple[str, str], r9) - r11 = CPyStr_Endswith(s1, r10) - r12 = r11 >= 0 :: signed - r13 = truncate r11: i32 to builtins.bool - y = r13 + x = r4 + r5 = 'a' + r6 = 'b' + r7 = (r5, r6) + r8 = box(tuple[str, str], r7) + r9 = CPyStr_Endswith(s1, r8) + y = r9 return 1 [case testStrToBool] From 87704cd235cca2058f244d303ee5b5918e95c62a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:58:52 +0100 Subject: [PATCH 1161/1617] [mypyc] Optimize str.partition and str.rpartition (#18702) `PyUnicode_Partition` and `PyUnicode_RPartition` are currently still missing from the docs but have been part of the stable API since Python 3.2. --- mypyc/doc/str_operations.rst | 2 ++ mypyc/primitives/str_ops.py | 18 ++++++++++++++++++ mypyc/test-data/fixtures/ir.py | 2 ++ mypyc/test-data/run-strings.test | 23 ++++++++++++++++++++--- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 05612fc55213..b2e632a8bbb6 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -33,10 +33,12 @@ Methods * ``s1.endswith(s2: str)`` * ``s1.endswith(t: tuple[str, ...])`` * ``s.join(x: Iterable)`` +* ``s.partition(sep: str)`` * ``s.removeprefix(prefix: str)`` * ``s.removesuffix(suffix: str)`` * ``s.replace(old: str, new: str)`` * ``s.replace(old: str, new: str, count: int)`` +* ``s.rpartition(sep: str)`` * ``s.rsplit()`` * ``s.rsplit(sep: str)`` * ``s.rsplit(sep: str, maxsplit: int)`` diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index d573b8017aa8..255728187604 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -197,6 +197,24 @@ error_kind=ERR_NEVER, ) +# str.partition(str) +method_op( + name="partition", + arg_types=[str_rprimitive, str_rprimitive], + return_type=tuple_rprimitive, + c_function_name="PyUnicode_Partition", + error_kind=ERR_MAGIC, +) + +# str.rpartition(str) +method_op( + name="rpartition", + arg_types=[str_rprimitive, str_rprimitive], + return_type=tuple_rprimitive, + c_function_name="PyUnicode_RPartition", + error_kind=ERR_MAGIC, +) + # str.replace(old, new) method_op( name="replace", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 50b6815d46a2..1c7346791c68 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -113,6 +113,8 @@ def startswith(self, x: Union[str, Tuple[str, ...]], start: int=..., end: int=.. def endswith(self, x: Union[str, Tuple[str, ...]], start: int=..., end: int=...) -> bool: ... def replace(self, old: str, new: str, maxcount: int=...) -> str: ... def encode(self, encoding: str=..., errors: str=...) -> bytes: ... + def partition(self, sep: str, /) -> Tuple[str, str, str]: ... + def rpartition(self, sep: str, /) -> Tuple[str, str, str]: ... def removeprefix(self, prefix: str, /) -> str: ... def removesuffix(self, suffix: str, /) -> str: ... diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index f96824a1cad0..94fcf84f085b 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -71,7 +71,8 @@ assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc') assert remove_prefix_suffix('abc', 'c') == ('abc', 'ab') [case testStringOps] -from typing import List, Optional +from typing import List, Optional, Tuple +from testutil import assertRaises def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: if sep is not None: @@ -121,11 +122,27 @@ def test_splitlines() -> None: assert splitlines(s_text, False) == ["This", "is", "", "some", "long", "text."] assert splitlines(s_text, True) == ["This\n", "is\n", "\n", "some\n", "long\n", "text.\n"] +s_partition = "Some long text" + +def partition(s: str, sep: str) -> Tuple[str, str, str]: + return s.partition(sep) + +def rpartition(s: str, sep: str) -> Tuple[str, str, str]: + return s.rpartition(sep) + +def test_partition() -> None: + assert partition(s_partition, " ") == ("Some", " ", "long text") + assert partition(s_partition, "Hello") == ("Some long text", "", "") + assert rpartition(s_partition, " ") == ("Some long", " ", "text") + assert rpartition(s_partition, "Hello") == ("", "", "Some long text") + with assertRaises(ValueError, "empty separator"): + partition(s_partition, "") + with assertRaises(ValueError, "empty separator"): + rpartition(s_partition, "") + def getitem(s: str, index: int) -> str: return s[index] -from testutil import assertRaises - s = "abc" def test_getitem() -> None: From f1a73cf3b39ad4e7953a20f9a17af6d614f2c2b9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 18 Feb 2025 19:20:15 +0100 Subject: [PATCH 1162/1617] [stubtest] Replace old typing_extensions imports in tests (#18691) --- mypy/test/teststubtest.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index f3199dae7f73..101b6f65c45a 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -48,6 +48,11 @@ def __getitem__(self, typeargs: Any) -> object: ... Generic: _SpecialForm = ... Protocol: _SpecialForm = ... Union: _SpecialForm = ... +ClassVar: _SpecialForm = ... + +Final = 0 +Literal = 0 +TypedDict = 0 class TypeVar: def __init__(self, name, covariant: bool = ..., contravariant: bool = ...) -> None: ... @@ -71,6 +76,12 @@ class Match(Generic[AnyStr]): ... class Sequence(Iterable[_T_co]): ... class Tuple(Sequence[_T_co]): ... class NamedTuple(tuple[Any, ...]): ... +class _TypedDict(Mapping[str, object]): + __required_keys__: ClassVar[frozenset[str]] + __optional_keys__: ClassVar[frozenset[str]] + __total__: ClassVar[bool] + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] def overload(func: _T) -> _T: ... def type_check_only(func: _T) -> _T: ... def final(func: _T) -> _T: ... @@ -95,6 +106,8 @@ def __ge__(self, __other: tuple[T_co, ...]) -> bool: pass class dict(Mapping[KT, VT]): ... +class frozenset(Generic[T]): ... + class function: pass class ellipsis: pass @@ -1373,7 +1386,7 @@ def spam(x=Flags4(0)): pass ) yield Case( stub=""" - from typing_extensions import Final, Literal + from typing import Final, Literal class BytesEnum(bytes, enum.Enum): a = b'foo' FOO: Literal[BytesEnum.a] @@ -1915,7 +1928,7 @@ def __init__(self, x): pass def test_good_literal(self) -> Iterator[Case]: yield Case( stub=r""" - from typing_extensions import Literal + from typing import Literal import enum class Color(enum.Enum): @@ -1947,7 +1960,7 @@ class Color(enum.Enum): @collect_cases def test_bad_literal(self) -> Iterator[Case]: - yield Case("from typing_extensions import Literal", "", None) # dummy case + yield Case("from typing import Literal", "", None) # dummy case yield Case( stub="INT_FLOAT_MISMATCH: Literal[1]", runtime="INT_FLOAT_MISMATCH = 1.0", @@ -1998,7 +2011,7 @@ def test_special_subtype(self) -> Iterator[Case]: ) yield Case( stub=""" - from typing_extensions import TypedDict + from typing import TypedDict class _Options(TypedDict): a: str @@ -2019,8 +2032,8 @@ class _Options(TypedDict): @collect_cases def test_runtime_typing_objects(self) -> Iterator[Case]: yield Case( - stub="from typing_extensions import Protocol, TypedDict", - runtime="from typing_extensions import Protocol, TypedDict", + stub="from typing import Protocol, TypedDict", + runtime="from typing import Protocol, TypedDict", error=None, ) yield Case( @@ -2385,8 +2398,8 @@ class A2: ... ) # The same is true for NamedTuples and TypedDicts: yield Case( - stub="from typing_extensions import NamedTuple, TypedDict", - runtime="from typing_extensions import NamedTuple, TypedDict", + stub="from typing import NamedTuple, TypedDict", + runtime="from typing import NamedTuple, TypedDict", error=None, ) yield Case( From 972bad2f343eb652362d1e1d09af2a34e496d004 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 18 Feb 2025 19:22:36 +0100 Subject: [PATCH 1163/1617] Add regression test for typing_extensions.Literal and mypy_extensions.TypedDict (#18694) #18640 removed almost all instances of `typing_extensions.Literal` in the tests. Re-add a simple regression test. Similarly at least one test case should import `mypy_extensions.TypedDict`. --- test-data/unit/check-literal.test | 10 ++++++++++ test-data/unit/check-typeddict.test | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 0b2721e77624..88c02f70488c 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -52,6 +52,16 @@ y: Literal[43] y = 43 [typing fixtures/typing-medium.pyi] +[case testLiteralFromTypingExtensionsWorks] +from typing_extensions import Literal + +x: Literal[42] +x = 43 # E: Incompatible types in assignment (expression has type "Literal[43]", variable has type "Literal[42]") + +y: Literal[43] +y = 43 +[builtins fixtures/tuple.pyi] + [case testLiteralInsideOtherTypes] from typing import Literal, Tuple diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index c2b734b4b923..c5ebed57bbcd 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3791,7 +3791,7 @@ x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated [typing fixtures/typing-typeddict.pyi] [case testTypedDictFromMypyExtensionsReadOnlyMutateMethods] -from typing import TypedDict +from mypy_extensions import TypedDict from typing_extensions import ReadOnly class TP(TypedDict): @@ -4120,7 +4120,7 @@ Func = TypedDict('Func', { [typing fixtures/typing-typeddict.pyi] [case testTypedDictNestedInClassAndInherited] -from typing_extensions import TypedDict +from typing import TypedDict class Base: class Params(TypedDict): From a26d8d040acdb346db9ae183e2e59a7641a3a05c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 19 Feb 2025 00:02:37 +0000 Subject: [PATCH 1164/1617] Add an option to exclude everything in .gitignore (#18696) Fixes https://github.com/python/mypy/issues/12505 This is (somewhat surprisingly) one of the most upvoted issues, and looks like a simple thing to add. I essentially do what other tools do, but optimize for how we work with sources discovery (to avoid performance issues). I am making this opt-in for now, we can change this later if needed. --- docs/source/command_line.rst | 4 ++++ docs/source/config_file.rst | 8 +++++++ mypy-requirements.txt | 1 + mypy/find_sources.py | 13 ++++++++++- mypy/main.py | 9 ++++++++ mypy/modulefinder.py | 45 ++++++++++++++++++++++++++++++++++++ mypy/options.py | 1 + pyproject.toml | 2 ++ test-data/unit/cmdline.test | 15 ++++++++++++ test-requirements.txt | 4 +++- 10 files changed, 100 insertions(+), 2 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 7c469f6d5138..2a54c1144171 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -81,6 +81,10 @@ for full details, see :ref:`running-mypy`. never recursively discover files with extensions other than ``.py`` or ``.pyi``. +.. option:: --exclude-gitignore + + This flag will add everything that matches ``.gitignore`` file(s) to :option:`--exclude`. + Optional arguments ****************** diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 57e88346faa9..abfe5bb21c62 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -288,6 +288,14 @@ section of the command line docs. See :ref:`using-a-pyproject-toml`. +.. confval:: exclude_gitignore + + :type: boolean + :default: False + + This flag will add everything that matches ``.gitignore`` file(s) to :confval:`exclude`. + This option may only be set in the global section (``[mypy]``). + .. confval:: namespace_packages :type: boolean diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 8d41a3fc7003..8965a70c13b7 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -2,4 +2,5 @@ # and the pins in setup.py typing_extensions>=4.6.0 mypy_extensions>=1.0.0 +pathspec>=0.9.0 tomli>=1.1.0; python_version<'3.11' diff --git a/mypy/find_sources.py b/mypy/find_sources.py index e9b05f0f2cc8..ececbf9c1cb8 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -8,7 +8,13 @@ from typing import Final from mypy.fscache import FileSystemCache -from mypy.modulefinder import PYTHON_EXTENSIONS, BuildSource, matches_exclude, mypy_path +from mypy.modulefinder import ( + PYTHON_EXTENSIONS, + BuildSource, + matches_exclude, + matches_gitignore, + mypy_path, +) from mypy.options import Options PY_EXTENSIONS: Final = tuple(PYTHON_EXTENSIONS) @@ -94,6 +100,7 @@ def __init__(self, fscache: FileSystemCache, options: Options) -> None: self.explicit_package_bases = get_explicit_package_bases(options) self.namespace_packages = options.namespace_packages self.exclude = options.exclude + self.exclude_gitignore = options.exclude_gitignore self.verbosity = options.verbosity def is_explicit_package_base(self, path: str) -> bool: @@ -113,6 +120,10 @@ def find_sources_in_dir(self, path: str) -> list[BuildSource]: if matches_exclude(subpath, self.exclude, self.fscache, self.verbosity >= 2): continue + if self.exclude_gitignore and matches_gitignore( + subpath, self.fscache, self.verbosity >= 2 + ): + continue if self.fscache.isdir(subpath): sub_sources = self.find_sources_in_dir(subpath) diff --git a/mypy/main.py b/mypy/main.py index fb63cd865129..77d8cefe9866 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1252,6 +1252,15 @@ def add_invertible_flag( "May be specified more than once, eg. --exclude a --exclude b" ), ) + add_invertible_flag( + "--exclude-gitignore", + default=False, + help=( + "Use .gitignore file(s) to exclude files from checking " + "(in addition to any explicit --exclude if present)" + ), + group=code_group, + ) code_group.add_argument( "-m", "--module", diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 61dbb6c61d1f..ca21cc6a7199 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -16,6 +16,9 @@ from typing import Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias +from pathspec import PathSpec +from pathspec.patterns.gitwildmatch import GitWildMatchPatternError + from mypy import pyinfo from mypy.errors import CompileError from mypy.fscache import FileSystemCache @@ -625,6 +628,12 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]: subpath, self.options.exclude, self.fscache, self.options.verbosity >= 2 ): continue + if ( + self.options + and self.options.exclude_gitignore + and matches_gitignore(subpath, self.fscache, self.options.verbosity >= 2) + ): + continue if self.fscache.isdir(subpath): # Only recurse into packages @@ -664,6 +673,42 @@ def matches_exclude( return False +def matches_gitignore(subpath: str, fscache: FileSystemCache, verbose: bool) -> bool: + dir, _ = os.path.split(subpath) + for gi_path, gi_spec in find_gitignores(dir): + relative_path = os.path.relpath(subpath, gi_path) + if fscache.isdir(relative_path): + relative_path = relative_path + "/" + if gi_spec.match_file(relative_path): + if verbose: + print( + f"TRACE: Excluding {relative_path} (matches .gitignore) in {gi_path}", + file=sys.stderr, + ) + return True + return False + + +@functools.lru_cache +def find_gitignores(dir: str) -> list[tuple[str, PathSpec]]: + parent_dir = os.path.dirname(dir) + if parent_dir == dir: + parent_gitignores = [] + else: + parent_gitignores = find_gitignores(parent_dir) + + gitignore = os.path.join(dir, ".gitignore") + if os.path.isfile(gitignore): + with open(gitignore) as f: + lines = f.readlines() + try: + return parent_gitignores + [(dir, PathSpec.from_lines("gitwildmatch", lines))] + except GitWildMatchPatternError: + print(f"error: could not parse {gitignore}", file=sys.stderr) + return parent_gitignores + return parent_gitignores + + def is_init_file(path: str) -> bool: return os.path.basename(path) in ("__init__.py", "__init__.pyi") diff --git a/mypy/options.py b/mypy/options.py index d40a08107a7a..c1047657dd77 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -136,6 +136,7 @@ def __init__(self) -> None: self.explicit_package_bases = False # File names, directory names or subpaths to avoid checking self.exclude: list[str] = [] + self.exclude_gitignore: bool = False # disallow_any options self.disallow_any_generics = False diff --git a/pyproject.toml b/pyproject.toml index 2eaca2d3ea88..5852d4cdd506 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires = [ # the following is from mypy-requirements.txt/setup.py "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", + "pathspec>=0.9.0", "tomli>=1.1.0; python_version<'3.11'", # the following is from build-requirements.txt "types-psutil", @@ -49,6 +50,7 @@ dependencies = [ # When changing this, also update build-system.requires and mypy-requirements.txt "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", + "pathspec>=0.9.0", "tomli>=1.1.0; python_version<'3.11'", ] dynamic = ["version"] diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index b9da5883c793..748a655d5a10 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1135,6 +1135,21 @@ b/bpkg.py:1: error: "int" not callable [out] c/cpkg.py:1: error: "int" not callable +[case testCmdlineExcludeGitignore] +# cmd: mypy --exclude-gitignore . +[file .gitignore] +abc +[file abc/apkg.py] +1() +[file b/.gitignore] +bpkg.* +[file b/bpkg.py] +1() +[file c/cpkg.py] +1() +[out] +c/cpkg.py:1: error: "int" not callable + [case testCmdlineCfgExclude] # cmd: mypy . [file mypy.ini] diff --git a/test-requirements.txt b/test-requirements.txt index e2a12655a1aa..51281f0e4c11 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.12 # by the following command: # # pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in @@ -30,6 +30,8 @@ nodeenv==1.9.1 # via pre-commit packaging==24.2 # via pytest +pathspec==0.12.1 + # via -r mypy-requirements.txt platformdirs==4.3.6 # via virtualenv pluggy==1.5.0 From f66741c5ef5d0b9c5e2083d092fbb09cdbc217ab Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Wed, 19 Feb 2025 03:05:43 -0800 Subject: [PATCH 1165/1617] Enable ruff SIM101 duplicate isinstance check (#18679) Enforce no unnecessary duplicate isinstance calls (that could be merged into a tuple call). --- mypy/stubtest.py | 11 +++++++++-- pyproject.toml | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 599a24cf685d..41b58cbbb636 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1534,8 +1534,15 @@ def is_probably_private(name: str) -> bool: def is_probably_a_function(runtime: Any) -> bool: return ( - isinstance(runtime, (types.FunctionType, types.BuiltinFunctionType)) - or isinstance(runtime, (types.MethodType, types.BuiltinMethodType)) + isinstance( + runtime, + ( + types.FunctionType, + types.BuiltinFunctionType, + types.MethodType, + types.BuiltinMethodType, + ), + ) or (inspect.ismethoddescriptor(runtime) and callable(runtime)) or (isinstance(runtime, types.MethodWrapperType) and callable(runtime)) ) diff --git a/pyproject.toml b/pyproject.toml index 5852d4cdd506..ce1326bc5818 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,6 +144,7 @@ select = [ "PGH004", # blanket noqa comments "UP", # pyupgrade "C4", # flake8-comprehensions + "SIM101", # merge duplicate isinstance calls "SIM201", "SIM202", "SIM222", "SIM223", # flake8-simplify "FURB188", # use str.remove(pre|suf)fix "ISC001", # implicitly concatenated string From 49e014ace2064a1f3c964f5b86ea34f28e6a27ab Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:06:35 +0100 Subject: [PATCH 1166/1617] [mypyc] Optimize str.__contains__ (#18705) https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_Contains --- mypyc/doc/str_operations.rst | 1 + mypyc/primitives/str_ops.py | 11 +++++++++++ mypyc/test-data/run-strings.test | 13 +++++++++++++ 3 files changed, 25 insertions(+) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index b2e632a8bbb6..a7e9ccc58cd1 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -21,6 +21,7 @@ Operators * Slicing (``s[n:m]``, ``s[n:]``, ``s[:m]``) * Comparisons (``==``, ``!=``) * Augmented assignment (``s1 += s2``) +* Containment (``s1 in s2``) .. _str-methods: diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 255728187604..aef3575d8eb4 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -84,6 +84,17 @@ error_kind=ERR_MAGIC, ) +# item in str +binary_op( + name="in", + arg_types=[str_rprimitive, str_rprimitive], + return_type=c_int_rprimitive, + c_function_name="PyUnicode_Contains", + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0], +) + # str.join(obj) method_op( name="join", diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 94fcf84f085b..7eadaeee0707 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -140,11 +140,24 @@ def test_partition() -> None: with assertRaises(ValueError, "empty separator"): rpartition(s_partition, "") +def contains(s: str, o: str) -> bool: + return o in s + def getitem(s: str, index: int) -> str: return s[index] s = "abc" +def test_contains() -> None: + assert contains(s, "a") is True + assert contains(s, "abc") is True + assert contains(s, "Hello") is False + assert contains(s, "bc") is True + assert contains(s, "abcd") is False + assert contains(s, "bb") is False + assert contains(s, "") is True + assert contains(s, " ") is False + def test_getitem() -> None: assert getitem(s, 0) == "a" assert getitem(s, 1) == "b" From 19e3fd4742e896707cb5b0d503582d1525a26eb9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:11:13 +0100 Subject: [PATCH 1167/1617] [mypyc] Optimize str.find and str.rfind (#18709) https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_Find --- mypyc/doc/str_operations.rst | 6 ++++++ mypyc/lib-rt/CPy.h | 2 ++ mypyc/lib-rt/str_ops.c | 23 +++++++++++++++++++++ mypyc/primitives/str_ops.py | 23 +++++++++++++++++++++ mypyc/test-data/fixtures/ir.py | 2 ++ mypyc/test-data/run-strings.test | 34 ++++++++++++++++++++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index a7e9ccc58cd1..5b18c0c927d6 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -33,12 +33,18 @@ Methods * ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` * ``s1.endswith(t: tuple[str, ...])`` +* ``s1.find(s2: str)`` +* ``s1.find(s2: str, start: int)`` +* ``s1.find(s2: str, start: int, end: int)`` * ``s.join(x: Iterable)`` * ``s.partition(sep: str)`` * ``s.removeprefix(prefix: str)`` * ``s.removesuffix(suffix: str)`` * ``s.replace(old: str, new: str)`` * ``s.replace(old: str, new: str, count: int)`` +* ``s1.rfind(s2: str)`` +* ``s1.rfind(s2: str, start: int)`` +* ``s1.rfind(s2: str, start: int, end: int)`` * ``s.rpartition(sep: str)`` * ``s.rsplit()`` * ``s.rsplit(sep: str)`` diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 22ab0f253ed7..1c8b59855fc7 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -720,6 +720,8 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { PyObject *CPyStr_Build(Py_ssize_t len, ...); PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index); +CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction); +CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction); PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split); PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split); PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace); diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 00759166df35..5b295f84440b 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -133,6 +133,29 @@ PyObject *CPyStr_Build(Py_ssize_t len, ...) { return res; } +CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction) { + CPyTagged end = PyUnicode_GET_LENGTH(str) << 1; + return CPyStr_FindWithEnd(str, substr, start, end, direction); +} + +CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction) { + Py_ssize_t temp_start = CPyTagged_AsSsize_t(start); + if (temp_start == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return CPY_INT_TAG; + } + Py_ssize_t temp_end = CPyTagged_AsSsize_t(end); + if (temp_end == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return CPY_INT_TAG; + } + Py_ssize_t index = PyUnicode_Find(str, substr, temp_start, temp_end, direction); + if (unlikely(index == -2)) { + return CPY_INT_TAG; + } + return index << 1; +} + PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) { Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); if (temp_max_split == -1 && PyErr_Occurred()) { diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index aef3575d8eb4..e4c644470ba4 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -95,6 +95,29 @@ ordering=[1, 0], ) +# str.find(...) and str.rfind(...) +str_find_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive] +str_find_functions = ["CPyStr_Find", "CPyStr_Find", "CPyStr_FindWithEnd"] +str_find_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []] +str_rfind_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []] +for i in range(len(str_find_types) - 1): + method_op( + name="find", + arg_types=str_find_types[0 : i + 2], + return_type=int_rprimitive, + c_function_name=str_find_functions[i], + extra_int_constants=str_find_constants[i] + [(1, c_int_rprimitive)], + error_kind=ERR_MAGIC, + ) + method_op( + name="rfind", + arg_types=str_find_types[0 : i + 2], + return_type=int_rprimitive, + c_function_name=str_find_functions[i], + extra_int_constants=str_rfind_constants[i] + [(-1, c_int_rprimitive)], + error_kind=ERR_MAGIC, + ) + # str.join(obj) method_op( name="join", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 1c7346791c68..38fecbc20c65 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -102,6 +102,8 @@ def __getitem__(self, i: int) -> str: pass def __getitem__(self, i: slice) -> str: pass def __contains__(self, item: str) -> bool: pass def __iter__(self) -> Iterator[str]: ... + def find(self, sub: str, start: Optional[int] = None, end: Optional[int] = None, /) -> int: ... + def rfind(self, sub: str, start: Optional[int] = None, end: Optional[int] = None, /) -> int: ... def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def splitlines(self, keepends: bool = False) -> List[str]: ... diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 7eadaeee0707..ce5c85059aed 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -146,6 +146,20 @@ def contains(s: str, o: str) -> bool: def getitem(s: str, index: int) -> str: return s[index] +def find(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: + if start is not None: + if end is not None: + return s.find(substr, start, end) + return s.find(substr, start) + return s.find(substr) + +def rfind(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: + if start is not None: + if end is not None: + return s.rfind(substr, start, end) + return s.rfind(substr, start) + return s.rfind(substr) + s = "abc" def test_contains() -> None: @@ -170,6 +184,26 @@ def test_getitem() -> None: with assertRaises(IndexError, "string index out of range"): getitem(s, -4) +def test_find() -> None: + s = "abcab" + assert find(s, "Hello") == -1 + assert find(s, "abc") == 0 + assert find(s, "b") == 1 + assert find(s, "b", 1) == 1 + assert find(s, "b", 1, 2) == 1 + assert find(s, "b", 3) == 4 + assert find(s, "b", 3, 5) == 4 + assert find(s, "b", 3, 4) == -1 + + assert rfind(s, "Hello") == -1 + assert rfind(s, "abc") == 0 + assert rfind(s, "b") == 4 + assert rfind(s, "b", 1) == 4 + assert rfind(s, "b", 1, 2) == 1 + assert rfind(s, "b", 3) == 4 + assert rfind(s, "b", 3, 5) == 4 + assert rfind(s, "b", 3, 4) == -1 + def str_to_int(s: str, base: Optional[int] = None) -> int: if base: return int(s, base) From 2aab1302f52376b25bb7af6435a0015b341c27bb Mon Sep 17 00:00:00 2001 From: Emma Smith Date: Wed, 19 Feb 2025 11:57:10 -0800 Subject: [PATCH 1168/1617] Update CREDITS to correct my name (#18710) --- CREDITS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index fb2fe155a9b8..cbe5954c81b2 100644 --- a/CREDITS +++ b/CREDITS @@ -15,7 +15,7 @@ Dropbox core team: Non-Dropbox core team members: - Ethan Smith + Emma Harper Smith Guido van Rossum Jelle Zijlstra Michael J. Sullivan From d87f0b2c960aba81bd23c9c232a15e1b20d06d9f Mon Sep 17 00:00:00 2001 From: Georg Date: Wed, 19 Feb 2025 22:47:37 +0100 Subject: [PATCH 1169/1617] [docs] Fix metaclass usage example (#18686) Fixes #18668 - Fixed the code example to _not_ include `Self` - Added a note about `Self` & metaclasses in Gotchas section with a link to the relevant PEP --- docs/source/metaclasses.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/source/metaclasses.rst b/docs/source/metaclasses.rst index a3ee25f16054..dd77a2f90ed8 100644 --- a/docs/source/metaclasses.rst +++ b/docs/source/metaclasses.rst @@ -34,12 +34,14 @@ Mypy supports the lookup of attributes in the metaclass: .. code-block:: python - from typing import ClassVar, Self + from typing import ClassVar, TypeVar + + S = TypeVar("S") class M(type): count: ClassVar[int] = 0 - def make(cls) -> Self: + def make(cls: type[S]) -> S: M.count += 1 return cls() @@ -55,9 +57,6 @@ Mypy supports the lookup of attributes in the metaclass: b: B = B.make() # metaclasses are inherited print(B.count + " objects were created") # Error: Unsupported operand types for + ("int" and "str") -.. note:: - In Python 3.10 and earlier, ``Self`` is available in ``typing_extensions``. - .. _limitations: Gotchas and limitations of metaclass support @@ -88,3 +87,6 @@ so it's better not to combine metaclasses and class hierarchies: such as ``class A(metaclass=f()): ...`` * Mypy does not and cannot understand arbitrary metaclass code. * Mypy only recognizes subclasses of :py:class:`type` as potential metaclasses. +* ``Self`` is not allowed as annotation in metaclasses as per `PEP 673`_. + +.. _PEP 673: https://peps.python.org/pep-0673/#valid-locations-for-self From d8bf6e2ec26a1bc392a0309a737d0300a42370a2 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Thu, 20 Feb 2025 14:06:50 -0800 Subject: [PATCH 1170/1617] Optimize mypy/solve.py with min instead of sort (#18688) The first value of a stable sort always equivalent to a linear min search (and uses less memory). --- mypy/solve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/solve.py b/mypy/solve.py index cac1a23c5a33..57988790a727 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -350,7 +350,7 @@ def test(x: U) -> U: ... # For convenience with current type application machinery, we use a stable # choice that prefers the original type variables (not polymorphic ones) in SCC. - best = sorted(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id))[0] + best = min(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id)) if isinstance(best, TypeVarType): return best.copy_modified(values=values, upper_bound=common_upper_bound) if is_trivial_bound(common_upper_bound_p, allow_tuple=True): From 2d3df02d7fa5cde617bf8dd4fb4748fe43598ec8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 21 Feb 2025 22:05:59 +0000 Subject: [PATCH 1171/1617] Add one more type-checking pass (#18717) This helps in rare cases, see discussion in https://github.com/python/mypy/pull/18674 --- mypy/checker.py | 2 +- test-data/unit/check-modules.test | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 04a286beef5e..d6a870a1ea22 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -216,7 +216,7 @@ T = TypeVar("T") -DEFAULT_LAST_PASS: Final = 1 # Pass numbers start at 0 +DEFAULT_LAST_PASS: Final = 2 # Pass numbers start at 0 # Maximum length of fixed tuple types inferred when narrowing from variadic tuples. MAX_PRECISE_TUPLE_SIZE: Final = 8 diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 87eb25a48cc2..000dae86131d 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -1388,14 +1388,14 @@ import b import b class C: def f1(self) -> None: - self.x2 + reveal_type(self.x2) def f2(self) -> None: self.x2 = b.b [file b.py] import a b = 1 + int() [out] -tmp/a.py:4: error: Cannot determine type of "x2" +tmp/a.py:4: note: Revealed type is "builtins.int" [case testErrorInPassTwo1] import b From 256cf68fb89099b35a3772d34a2652e6141e6558 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 21 Feb 2025 23:20:14 +0000 Subject: [PATCH 1172/1617] Only defer top-level functions (#18718) This makes deferral logic more robust and more consistent with fine-grained mode. I also: * Change some terminology, as "top function" is ambiguous: top-level function vs top of stack function. * Update some docs and type annotations to match actual behavior (e.g. we do not defer lambdas) See also https://github.com/python/mypy/pull/18674 for some more motivation. --- mypy/checker.py | 45 ++++++++++++++++------------- mypy/checkexpr.py | 4 +-- mypy/semanal.py | 4 +-- test-data/unit/check-inference.test | 32 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index d6a870a1ea22..b8d5bbd4fa2d 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -221,17 +221,17 @@ # Maximum length of fixed tuple types inferred when narrowing from variadic tuples. MAX_PRECISE_TUPLE_SIZE: Final = 8 -DeferredNodeType: _TypeAlias = Union[FuncDef, LambdaExpr, OverloadedFuncDef, Decorator] +DeferredNodeType: _TypeAlias = Union[FuncDef, OverloadedFuncDef, Decorator] FineGrainedDeferredNodeType: _TypeAlias = Union[FuncDef, MypyFile, OverloadedFuncDef] # A node which is postponed to be processed during the next pass. # In normal mode one can defer functions and methods (also decorated and/or overloaded) -# and lambda expressions. Nested functions can't be deferred -- only top-level functions +# but not lambda expressions. Nested functions can't be deferred -- only top-level functions # and methods of classes not defined within a function can be deferred. class DeferredNode(NamedTuple): node: DeferredNodeType - # And its TypeInfo (for semantic analysis self type handling + # And its TypeInfo (for semantic analysis self type handling) active_typeinfo: TypeInfo | None @@ -528,10 +528,7 @@ def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> else: self.recurse_into_functions = True with self.binder.top_frame_context(): - if isinstance(node, LambdaExpr): - self.expr_checker.accept(node) - else: - self.accept(node) + self.accept(node) def check_top_level(self, node: MypyFile) -> None: """Check only the top-level of a module, skipping function definitions.""" @@ -558,13 +555,13 @@ def defer_node(self, node: DeferredNodeType, enclosing_class: TypeInfo | None) - self.deferred_nodes.append(DeferredNode(node, enclosing_class)) def handle_cannot_determine_type(self, name: str, context: Context) -> None: - node = self.scope.top_non_lambda_function() + node = self.scope.top_level_function() if self.pass_num < self.last_pass and isinstance(node, FuncDef): # Don't report an error yet. Just defer. Note that we don't defer # lambdas because they are coupled to the surrounding function # through the binder and the inferred type of the lambda, so it # would get messy. - enclosing_class = self.scope.enclosing_class() + enclosing_class = self.scope.enclosing_class(node) self.defer_node(node, enclosing_class) # Set a marker so that we won't infer additional types in this # function. Any inferred types could be bogus, because there's at @@ -2156,7 +2153,14 @@ def check_method_override_for_base_with_name( if self.pass_num < self.last_pass: # If there are passes left, defer this node until next pass, # otherwise try reconstructing the method type from available information. - self.defer_node(defn, defn.info) + # For consistency, defer an enclosing top-level function (if any). + top_level = self.scope.top_level_function() + if isinstance(top_level, FuncDef): + self.defer_node(top_level, self.scope.enclosing_class(top_level)) + else: + # Specify enclosing class explicitly, as we check type override before + # entering e.g. decorators or overloads. + self.defer_node(defn, defn.info) return True elif isinstance(original_node, (FuncDef, OverloadedFuncDef)): original_type = self.function_type(original_node) @@ -4767,7 +4771,7 @@ def visit_return_stmt(self, s: ReturnStmt) -> None: self.binder.unreachable() def check_return_stmt(self, s: ReturnStmt) -> None: - defn = self.scope.top_function() + defn = self.scope.current_function() if defn is not None: if defn.is_generator: return_type = self.get_generator_return_type( @@ -4779,7 +4783,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: return_type = self.return_types[-1] return_type = get_proper_type(return_type) - is_lambda = isinstance(self.scope.top_function(), LambdaExpr) + is_lambda = isinstance(defn, LambdaExpr) if isinstance(return_type, UninhabitedType): # Avoid extra error messages for failed inference in lambdas if not is_lambda and not return_type.ambiguous: @@ -8554,14 +8558,15 @@ class CheckerScope: def __init__(self, module: MypyFile) -> None: self.stack = [module] - def top_function(self) -> FuncItem | None: + def current_function(self) -> FuncItem | None: for e in reversed(self.stack): if isinstance(e, FuncItem): return e return None - def top_non_lambda_function(self) -> FuncItem | None: - for e in reversed(self.stack): + def top_level_function(self) -> FuncItem | None: + """Return top-level non-lambda function.""" + for e in self.stack: if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr): return e return None @@ -8571,11 +8576,11 @@ def active_class(self) -> TypeInfo | None: return self.stack[-1] return None - def enclosing_class(self) -> TypeInfo | None: + def enclosing_class(self, func: FuncItem | None = None) -> TypeInfo | None: """Is there a class *directly* enclosing this function?""" - top = self.top_function() - assert top, "This method must be called from inside a function" - index = self.stack.index(top) + func = func or self.current_function() + assert func, "This method must be called from inside a function" + index = self.stack.index(func) assert index, "CheckerScope stack must always start with a module" enclosing = self.stack[index - 1] if isinstance(enclosing, TypeInfo): @@ -8589,7 +8594,7 @@ def active_self_type(self) -> Instance | TupleType | None: In particular, inside a function nested in method this returns None. """ info = self.active_class() - if not info and self.top_function(): + if not info and self.current_function(): info = self.enclosing_class() if info: return fill_typevars(info) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4078d447dab8..1017009ce7ab 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5523,7 +5523,7 @@ def visit_super_expr(self, e: SuperExpr) -> Type: if type_info in mro: index = mro.index(type_info) else: - method = self.chk.scope.top_function() + method = self.chk.scope.current_function() # Mypy explicitly allows supertype upper bounds (and no upper bound at all) # for annotating self-types. However, if such an annotation is used for # checking super() we will still get an error. So to be consistent, we also @@ -5598,7 +5598,7 @@ def _super_arg_types(self, e: SuperExpr) -> Type | tuple[Type, Type]: type_type: ProperType = TypeType(current_type) # Use the type of the self argument, in case it was annotated - method = self.chk.scope.top_function() + method = self.chk.scope.current_function() assert method is not None if method.arguments: instance_type: Type = method.arguments[0].variable.type or current_type diff --git a/mypy/semanal.py b/mypy/semanal.py index 1a64731057e2..a0cfdcce1e33 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3708,9 +3708,9 @@ def store_final_status(self, s: AssignmentStmt) -> None: cur_node = self.type.names.get(lval.name, None) if cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_final: assert self.function_stack - top_function = self.function_stack[-1] + current_function = self.function_stack[-1] if ( - top_function.name == "__init__" + current_function.name == "__init__" and cur_node.node.final_unset_in_class and not cur_node.node.final_set_in_init and not (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index cb0b11bf013c..ff351686dfc2 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3913,3 +3913,35 @@ x = "abc" for x in list[int](): reveal_type(x) # N: Revealed type is "builtins.int" reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNarrowInFunctionDefer] +from typing import Optional, Callable, TypeVar + +def top() -> None: + x: Optional[int] + assert x is not None + + def foo() -> None: + defer() + reveal_type(x) # N: Revealed type is "builtins.int" + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... + +[case testDeferMethodOfNestedClass] +from typing import Optional, Callable, TypeVar + +class Out: + def meth(self) -> None: + class In: + def meth(self) -> None: + reveal_type(defer()) # N: Revealed type is "builtins.int" + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... From 7914b2dc7b5c65d64a750ee4bd964b339025a571 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:56:36 +0000 Subject: [PATCH 1173/1617] Fix mypyc crash with enum type aliases (#18725) mypyc was crashing because it couldn't find the type in the type map. This PR adds a generic AnyType to the type map if an expression isn't in the map already. Tried actually changing mypy to accept these type alias expressions, but ran into problems with nested type aliases where the inner one doesn't have the "analyzed" value and ending up with wrong results. fixes https://github.com/mypyc/mypyc/issues/1064 --- mypyc/irbuild/main.py | 2 +- mypyc/irbuild/missingtypevisitor.py | 20 ++++++++++++++++++++ mypyc/irbuild/prebuildvisitor.py | 13 +++++++++++++ mypyc/test-data/irbuild-classes.test | 10 ++++++++++ mypyc/test-data/run-python312.test | 10 +++++++++- 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 mypyc/irbuild/missingtypevisitor.py diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 15928d939cbf..7cdc6b686778 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -73,7 +73,7 @@ def build_ir( for module in modules: # First pass to determine free symbols. - pbv = PreBuildVisitor(errors, module, singledispatch_info.decorators_to_remove) + pbv = PreBuildVisitor(errors, module, singledispatch_info.decorators_to_remove, types) module.accept(pbv) # Construct and configure builder objects (cyclic runtime dependency). diff --git a/mypyc/irbuild/missingtypevisitor.py b/mypyc/irbuild/missingtypevisitor.py new file mode 100644 index 000000000000..e655d270a4a4 --- /dev/null +++ b/mypyc/irbuild/missingtypevisitor.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from mypy.nodes import Expression, Node +from mypy.traverser import ExtendedTraverserVisitor +from mypy.types import AnyType, Type, TypeOfAny + + +class MissingTypesVisitor(ExtendedTraverserVisitor): + """AST visitor that can be used to add any missing types as a generic AnyType.""" + + def __init__(self, types: dict[Expression, Type]) -> None: + super().__init__() + self.types: dict[Expression, Type] = types + + def visit(self, o: Node) -> bool: + if isinstance(o, Expression) and o not in self.types: + self.types[o] = AnyType(TypeOfAny.special_form) + + # If returns True, will continue to nested nodes. + return True diff --git a/mypyc/irbuild/prebuildvisitor.py b/mypyc/irbuild/prebuildvisitor.py index 5f178a290138..e630fed0d85a 100644 --- a/mypyc/irbuild/prebuildvisitor.py +++ b/mypyc/irbuild/prebuildvisitor.py @@ -1,6 +1,7 @@ from __future__ import annotations from mypy.nodes import ( + AssignmentStmt, Block, Decorator, Expression, @@ -16,7 +17,9 @@ Var, ) from mypy.traverser import ExtendedTraverserVisitor +from mypy.types import Type from mypyc.errors import Errors +from mypyc.irbuild.missingtypevisitor import MissingTypesVisitor class PreBuildVisitor(ExtendedTraverserVisitor): @@ -39,6 +42,7 @@ def __init__( errors: Errors, current_file: MypyFile, decorators_to_remove: dict[FuncDef, list[int]], + types: dict[Expression, Type], ) -> None: super().__init__() # Dict from a function to symbols defined directly in the @@ -82,11 +86,20 @@ def __init__( self.current_file: MypyFile = current_file + self.missing_types_visitor = MissingTypesVisitor(types) + def visit(self, o: Node) -> bool: if not isinstance(o, Import): self._current_import_group = None return True + def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None: + # These are cases where mypy may not have types for certain expressions, + # but mypyc needs some form type to exist. + if stmt.is_alias_def: + stmt.rvalue.accept(self.missing_types_visitor) + return super().visit_assignment_stmt(stmt) + def visit_block(self, block: Block) -> None: self._current_import_group = None super().visit_block(block) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 605ab46181e2..ed7c167d8621 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1335,3 +1335,13 @@ def outer(): if True: class OtherInner: # E: Nested class definitions not supported pass + +[case testEnumClassAlias] +from enum import Enum +from typing import Literal, Union + +class SomeEnum(Enum): + AVALUE = "a" + +ALIAS = Literal[SomeEnum.AVALUE] +ALIAS2 = Union[Literal[SomeEnum.AVALUE], None] diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test index a5a3f058d1e2..5c0a807c375a 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -1,5 +1,6 @@ [case testPEP695Basics] -from typing import Any, TypeAliasType, cast +from enum import Enum +from typing import Any, Literal, TypeAliasType, cast from testutil import assertRaises @@ -188,6 +189,13 @@ type R = int | list[R] def test_recursive_type_alias() -> None: assert isinstance(R, TypeAliasType) assert getattr(R, "__value__") == (int | list[R]) + +class SomeEnum(Enum): + AVALUE = "a" + +type EnumLiteralAlias1 = Literal[SomeEnum.AVALUE] +type EnumLiteralAlias2 = Literal[SomeEnum.AVALUE] | None +EnumLiteralAlias3 = Literal[SomeEnum.AVALUE] | None [typing fixtures/typing-full.pyi] [case testPEP695GenericTypeAlias] From 66dde1497fae172988d577ef1a41ee0d89a47b56 Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Mon, 24 Feb 2025 09:03:00 -0800 Subject: [PATCH 1174/1617] stubgen: Fix valid type detection to allow pipe unions (#18726) `stubgen` has a regex which it uses to reject invalid types that are extracted from docstrings. It needed to be updated to support union shorthand: `str | int`. --- mypy/stubdoc.py | 2 +- mypy/test/teststubgen.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index e99204f3ade5..0da93b4e2477 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -21,7 +21,7 @@ Sig: _TypeAlias = tuple[str, str] -_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], .\"\']*(\.[a-zA-Z_][\w\[\], ]*)*$") +_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], .\"\'|]*(\.[a-zA-Z_][\w\[\], ]*)*$") _ARG_NAME_RE: Final = re.compile(r"\**[A-Za-z_][A-Za-z0-9_]*$") diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index dffa1aa80c5d..83693bebd91e 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1405,6 +1405,9 @@ def test_is_valid_type(self) -> None: assert is_valid_type("Literal[True]") assert is_valid_type("Literal[Color.RED]") assert is_valid_type("Literal[None]") + assert is_valid_type("str | int") + assert is_valid_type("dict[str, int] | int") + assert is_valid_type("tuple[str, ...]") assert is_valid_type( 'Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]' ) From 34f6f6ab0b6de5ba419817d2683891c27bd80249 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 25 Feb 2025 16:09:24 +0000 Subject: [PATCH 1175/1617] [mypyc] Fix order of steal/unborrow in tuple unpacking (#18732) Currently, although globally the refcount is correct, it may briefly touch 0 if a target of unpacking in unused, e.g. `_, _, last = some_tuple`. This can be prevented by placing steal before unborrow (which IMO should be the recommended way, if I understand the logic of these terms correctly). --- mypyc/ir/ops.py | 8 +++--- mypyc/irbuild/statement.py | 9 +++---- mypyc/test-data/irbuild-statements.test | 18 +++++++------ mypyc/test-data/refcount.test | 35 +++++++++++++++++++++---- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6a2e70aee6d7..0323d31d0605 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1532,12 +1532,12 @@ class Unborrow(RegisterOp): # t is a 2-tuple r0 = borrow t[0] r1 = borrow t[1] + keep_alive steal t r2 = unborrow r0 r3 = unborrow r1 - # now (r2, r3) represent the tuple as separate items, and the - # original tuple can be considered dead and available to be - # stolen - keep_alive steal t + # now (r2, r3) represent the tuple as separate items, that are + # managed again. (Note we need to steal before unborrow, to avoid + # refcount briefly touching zero if r2 or r3 are unused.) Be careful with this -- this can easily cause double freeing. """ diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index cdc1d54589eb..f5b65bedbbca 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -211,12 +211,11 @@ def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: and any(t.is_refcounted for t in rvalue_reg.type.types) ): n = len(first_lvalue.items) - for i in range(n): - target = builder.get_assignment_target(first_lvalue.items[i]) - rvalue_item = builder.add(TupleGet(rvalue_reg, i, borrow=True)) - rvalue_item = builder.add(Unborrow(rvalue_item)) - builder.assign(target, rvalue_item, line) + borrows = [builder.add(TupleGet(rvalue_reg, i, borrow=True)) for i in range(n)] builder.builder.keep_alive([rvalue_reg], steal=True) + for lvalue_item, rvalue_item in zip(first_lvalue.items, borrows): + rvalue_item = builder.add(Unborrow(rvalue_item)) + builder.assign(builder.get_assignment_target(lvalue_item), rvalue_item, line) builder.flush_keep_alives() return diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d5df984cfe4b..1f9336d32140 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -492,19 +492,21 @@ def from_any(a: Any) -> None: [out] def from_tuple(t): t :: tuple[int, object] - r0, r1 :: int - r2, x, r3, r4 :: object + r0 :: int + r1 :: object + r2 :: int + r3, x, r4 :: object r5, y :: int L0: r0 = borrow t[0] - r1 = unborrow r0 - r2 = box(int, r1) - x = r2 - r3 = borrow t[1] - r4 = unborrow r3 + r1 = borrow t[1] + keep_alive steal t + r2 = unborrow r0 + r3 = box(int, r2) + x = r3 + r4 = unborrow r1 r5 = unbox(int, r4) y = r5 - keep_alive steal t return 1 def from_any(a): a, r0, r1 :: object diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index c311f042ad5e..22153cff5a91 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -642,15 +642,15 @@ def g() -> Tuple[C, C]: [out] def f(): r0 :: tuple[__main__.C, __main__.C] - r1, r2, x, r3, r4, y :: __main__.C + r1, r2, r3, x, r4, y :: __main__.C r5, r6, r7 :: int L0: r0 = g() r1 = borrow r0[0] - r2 = unborrow r1 - x = r2 - r3 = borrow r0[1] - r4 = unborrow r3 + r2 = borrow r0[1] + r3 = unborrow r1 + x = r3 + r4 = unborrow r2 y = r4 r5 = borrow x.a r6 = borrow y.a @@ -800,6 +800,31 @@ L2: L3: return 1 +[case testTupleUnpackUnused] +from typing import Tuple + +def f(x: Tuple[str, int]) -> int: + a, xi = x + return 0 +[out] +def f(x): + x :: tuple[str, int] + r0 :: str + r1 :: int + r2, a :: str + r3, xi :: int +L0: + r0 = borrow x[0] + r1 = borrow x[1] + inc_ref x + r2 = unborrow r0 + a = r2 + dec_ref a + r3 = unborrow r1 + xi = r3 + dec_ref xi :: int + return 0 + [case testGetElementPtrLifeTime] from typing import List From 915c3c52c39f0aabd54ecf489c16d2254b330cb7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 25 Feb 2025 17:10:14 +0100 Subject: [PATCH 1176/1617] [ci] Switch back to ubuntu-24.04-arm runners (#18733) The `ubuntu-24.04-arm` runners were fixed a week ago. I haven't seen any new issue reports so it should be safe to switch back. Fixes #18660 --- .github/workflows/test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30686804780b..c42550431bb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: # the oldest and newest supported Python versions - name: Test suite with py39-ubuntu, mypyc-compiled python: '3.9' - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true @@ -44,31 +44,31 @@ jobs: tox_extra_args: "-n 4" - name: Test suite with py310-ubuntu python: '3.10' - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" - name: Test suite with py311-ubuntu, mypyc-compiled python: '3.11' - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled python: '3.12' - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - name: Test suite with py313-ubuntu, mypyc-compiled python: '3.13' - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true # - name: Test suite with py314-dev-ubuntu # python: '3.14-dev' - # os: ubuntu-22.04-arm + # os: ubuntu-24.04-arm # toxenv: py # tox_extra_args: "-n 4" # allow_failure: true From e93f06ceab81d8ff1f777c7587d04c339cfd5a16 Mon Sep 17 00:00:00 2001 From: jhance Date: Wed, 26 Feb 2025 14:05:22 -0800 Subject: [PATCH 1177/1617] Sync typeshed in preparation for release (#18741) --- .../stdlib/_frozen_importlib_external.pyi | 4 +-- mypy/typeshed/stdlib/codecs.pyi | 25 ++++++++++++-- mypy/typeshed/stdlib/compileall.pyi | 14 ++++---- mypy/typeshed/stdlib/email/__init__.pyi | 34 +++++++++++++++---- mypy/typeshed/stdlib/email/mime/message.pyi | 5 ++- mypy/typeshed/stdlib/email/mime/multipart.pyi | 7 ++-- 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi index d3127666da30..386cf20808e4 100644 --- a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi +++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi @@ -26,8 +26,8 @@ else: MAGIC_NUMBER: bytes -def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... -def source_from_cache(path: str) -> str: ... +def cache_from_source(path: StrPath, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... +def source_from_cache(path: StrPath) -> str: ... def decode_source(source_bytes: ReadableBuffer) -> str: ... def spec_from_file_location( name: str, diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index c6f517adb3cd..579d09c66a1b 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -3,8 +3,8 @@ from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO -from typing_extensions import Self +from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO, overload +from typing_extensions import Self, TypeAlias __all__ = [ "register", @@ -58,6 +58,21 @@ BOM32_LE: Final = b"\xff\xfe" BOM64_BE: Final = b"\x00\x00\xfe\xff" BOM64_LE: Final = b"\xff\xfe\x00\x00" +_BufferedEncoding: TypeAlias = Literal[ + "idna", + "raw-unicode-escape", + "unicode-escape", + "utf-16", + "utf-16-be", + "utf-16-le", + "utf-32", + "utf-32-be", + "utf-32-le", + "utf-7", + "utf-8", + "utf-8-sig", +] + class _WritableStream(Protocol): def write(self, data: bytes, /) -> object: ... def seek(self, offset: int, whence: int, /) -> object: ... @@ -94,6 +109,9 @@ class _IncrementalEncoder(Protocol): class _IncrementalDecoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalDecoder: ... +class _BufferedIncrementalDecoder(Protocol): + def __call__(self, errors: str = ...) -> BufferedIncrementalDecoder: ... + class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): _is_text_encoding: bool @property @@ -125,6 +143,9 @@ class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): def getencoder(encoding: str) -> _Encoder: ... def getdecoder(encoding: str) -> _Decoder: ... def getincrementalencoder(encoding: str) -> _IncrementalEncoder: ... +@overload +def getincrementaldecoder(encoding: _BufferedEncoding) -> _BufferedIncrementalDecoder: ... +@overload def getincrementaldecoder(encoding: str) -> _IncrementalDecoder: ... def getreader(encoding: str) -> _StreamReader: ... def getwriter(encoding: str) -> _StreamWriter: ... diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi index 9fb3608f2979..f35c584cedfb 100644 --- a/mypy/typeshed/stdlib/compileall.pyi +++ b/mypy/typeshed/stdlib/compileall.pyi @@ -25,7 +25,7 @@ if sys.version_info >= (3, 10): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... def compile_file( fullname: StrPath, ddir: StrPath | None = None, @@ -40,7 +40,7 @@ if sys.version_info >= (3, 10): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... elif sys.version_info >= (3, 9): def compile_dir( @@ -59,7 +59,7 @@ elif sys.version_info >= (3, 9): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... def compile_file( fullname: StrPath, ddir: StrPath | None = None, @@ -74,7 +74,7 @@ elif sys.version_info >= (3, 9): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... else: def compile_dir( @@ -88,7 +88,7 @@ else: optimize: int = -1, workers: int = 1, invalidation_mode: PycInvalidationMode | None = None, - ) -> int: ... + ) -> bool: ... def compile_file( fullname: StrPath, ddir: StrPath | None = None, @@ -98,7 +98,7 @@ else: legacy: bool = False, optimize: int = -1, invalidation_mode: PycInvalidationMode | None = None, - ) -> int: ... + ) -> bool: ... def compile_path( skip_curdir: bool = ..., @@ -108,4 +108,4 @@ def compile_path( legacy: bool = False, optimize: int = -1, invalidation_mode: PycInvalidationMode | None = None, -) -> int: ... +) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi index f564ced105bd..628ffb2b793a 100644 --- a/mypy/typeshed/stdlib/email/__init__.pyi +++ b/mypy/typeshed/stdlib/email/__init__.pyi @@ -1,7 +1,7 @@ from collections.abc import Callable from email.message import Message -from email.policy import Policy -from typing import IO +from email.policy import Policy, _MessageT +from typing import IO, overload from typing_extensions import TypeAlias # At runtime, listing submodules in __all__ without them being imported is @@ -31,7 +31,29 @@ __all__ = [ # noqa: F822 # Undefined names in __all__ _ParamType: TypeAlias = str | tuple[str | None, str | None, str] # noqa: Y047 _ParamsType: TypeAlias = str | None | tuple[str, str | None, str] # noqa: Y047 -def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... -def message_from_bytes(s: bytes | bytearray, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... -def message_from_file(fp: IO[str], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... -def message_from_binary_file(fp: IO[bytes], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... +@overload +def message_from_string(s: str) -> Message: ... +@overload +def message_from_string(s: str, _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_string(s: str, _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... +@overload +def message_from_bytes(s: bytes | bytearray) -> Message: ... +@overload +def message_from_bytes(s: bytes | bytearray, _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_bytes( + s: bytes | bytearray, _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT] +) -> _MessageT: ... +@overload +def message_from_file(fp: IO[str]) -> Message: ... +@overload +def message_from_file(fp: IO[str], _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_file(fp: IO[str], _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... +@overload +def message_from_binary_file(fp: IO[bytes]) -> Message: ... +@overload +def message_from_binary_file(fp: IO[bytes], _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_binary_file(fp: IO[bytes], _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... diff --git a/mypy/typeshed/stdlib/email/mime/message.pyi b/mypy/typeshed/stdlib/email/mime/message.pyi index 23cf58619ad9..2a5f46296150 100644 --- a/mypy/typeshed/stdlib/email/mime/message.pyi +++ b/mypy/typeshed/stdlib/email/mime/message.pyi @@ -1,8 +1,7 @@ -from email.message import Message from email.mime.nonmultipart import MIMENonMultipart -from email.policy import Policy +from email.policy import Policy, _MessageT __all__ = ["MIMEMessage"] class MIMEMessage(MIMENonMultipart): - def __init__(self, _msg: Message, _subtype: str = "rfc822", *, policy: Policy | None = None) -> None: ... + def __init__(self, _msg: _MessageT, _subtype: str = "rfc822", *, policy: Policy[_MessageT] | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/mime/multipart.pyi b/mypy/typeshed/stdlib/email/mime/multipart.pyi index 6163810ed94a..1c229f7436a8 100644 --- a/mypy/typeshed/stdlib/email/mime/multipart.pyi +++ b/mypy/typeshed/stdlib/email/mime/multipart.pyi @@ -1,8 +1,7 @@ from collections.abc import Sequence from email import _ParamsType -from email.message import Message from email.mime.base import MIMEBase -from email.policy import Policy +from email.policy import Policy, _MessageT __all__ = ["MIMEMultipart"] @@ -11,8 +10,8 @@ class MIMEMultipart(MIMEBase): self, _subtype: str = "mixed", boundary: str | None = None, - _subparts: Sequence[Message] | None = None, + _subparts: Sequence[_MessageT] | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT] | None = None, **_params: _ParamsType, ) -> None: ... From 5fcca776258d11bf8af095ab87a939cbf7612dd5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 28 Feb 2025 18:01:21 +0000 Subject: [PATCH 1178/1617] Process superclass methods before subclass methods in semanal (#18723) Fixes https://github.com/python/mypy/issues/7162 See also discussion in https://github.com/python/mypy/pull/18674 for another situation when this causes problems (deferrals). In general this problem is probably quite rare, but it bugs me, so I decided to go ahead with a simple and explicit (even though a bit ugly) solution. --- mypy/semanal_main.py | 63 ++++++++++++++++++++++++---- test-data/unit/check-classes.test | 7 ++-- test-data/unit/check-newsemanal.test | 18 ++++++++ 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index ded2a9412168..92a1c24b7b4c 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -26,7 +26,9 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import nullcontext +from itertools import groupby from typing import TYPE_CHECKING, Callable, Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias @@ -232,26 +234,66 @@ def process_top_levels(graph: Graph, scc: list[str], patches: Patches) -> None: final_iteration = not any_progress +def order_by_subclassing(targets: list[FullTargetInfo]) -> Iterator[FullTargetInfo]: + """Make sure that superclass methods are always processed before subclass methods. + + This algorithm is not very optimal, but it is simple and should work well for lists + that are already almost correctly ordered. + """ + + # First, group the targets by their TypeInfo (since targets are sorted by line, + # we know that each TypeInfo will appear as group key only once). + grouped = [(k, list(g)) for k, g in groupby(targets, key=lambda x: x[3])] + remaining_infos = {info for info, _ in grouped if info is not None} + + next_group = 0 + while grouped: + if next_group >= len(grouped): + # This should never happen, if there is an MRO cycle, it should be reported + # and fixed during top-level processing. + raise ValueError("Cannot order method targets by MRO") + next_info, group = grouped[next_group] + if next_info is None: + # Trivial case, not methods but functions, process them straight away. + yield from group + grouped.pop(next_group) + continue + if any(parent in remaining_infos for parent in next_info.mro[1:]): + # We cannot process this method group yet, try a next one. + next_group += 1 + continue + yield from group + grouped.pop(next_group) + remaining_infos.discard(next_info) + # Each time after processing a method group we should retry from start, + # since there may be some groups that are not blocked on parents anymore. + next_group = 0 + + def process_functions(graph: Graph, scc: list[str], patches: Patches) -> None: # Process functions. + all_targets = [] for module in scc: tree = graph[module].tree assert tree is not None - analyzer = graph[module].manager.semantic_analyzer # In principle, functions can be processed in arbitrary order, # but _methods_ must be processed in the order they are defined, # because some features (most notably partial types) depend on # order of definitions on self. # # There can be multiple generated methods per line. Use target - # name as the second sort key to get a repeatable sort order on - # Python 3.5, which doesn't preserve dictionary order. + # name as the second sort key to get a repeatable sort order. targets = sorted(get_all_leaf_targets(tree), key=lambda x: (x[1].line, x[0])) - for target, node, active_type in targets: - assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)) - process_top_level_function( - analyzer, graph[module], module, target, node, active_type, patches - ) + all_targets.extend( + [(module, target, node, active_type) for target, node, active_type in targets] + ) + + for module, target, node, active_type in order_by_subclassing(all_targets): + analyzer = graph[module].manager.semantic_analyzer + assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)) + process_top_level_function( + analyzer, graph[module], module, target, node, active_type, patches + ) def process_top_level_function( @@ -308,6 +350,11 @@ def process_top_level_function( str, Union[MypyFile, FuncDef, OverloadedFuncDef, Decorator], Optional[TypeInfo] ] +# Same as above but includes module as first item. +FullTargetInfo: _TypeAlias = tuple[ + str, str, Union[MypyFile, FuncDef, OverloadedFuncDef, Decorator], Optional[TypeInfo] +] + def get_all_leaf_targets(file: MypyFile) -> list[TargetInfo]: """Return all leaf targets in a symbol table (module-level and methods).""" diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index d48a27dbed03..06a863ad0499 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7007,11 +7007,10 @@ class C: [case testAttributeDefOrder2] class D(C): def g(self) -> None: - self.x = '' + self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") def f(self) -> None: - # https://github.com/python/mypy/issues/7162 - reveal_type(self.x) # N: Revealed type is "builtins.str" + reveal_type(self.x) # N: Revealed type is "builtins.int" class C: @@ -7025,7 +7024,7 @@ class E(C): def f(self) -> None: reveal_type(self.x) # N: Revealed type is "builtins.int" -[targets __main__, __main__, __main__.D.g, __main__.D.f, __main__.C.__init__, __main__.E.g, __main__.E.f] +[targets __main__, __main__, __main__.C.__init__, __main__.D.g, __main__.D.f, __main__.E.g, __main__.E.f] [case testNewReturnType1] class A: diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 9250f3cea0a6..b6756abafc49 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3256,3 +3256,21 @@ class b: x = x[1] # E: Cannot resolve name "x" (possible cyclic definition) y = 1[y] # E: Value of type "int" is not indexable \ # E: Cannot determine type of "y" + +[case testForwardBaseDeferAttr] +from typing import Optional, Callable, TypeVar + +class C(B): + def a(self) -> None: + reveal_type(self._foo) # N: Revealed type is "Union[builtins.int, None]" + self._foo = defer() + +class B: + def __init__(self) -> None: + self._foo: Optional[int] = None + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... From 27417ba430cee8808aeb2e0a723fa414420ae166 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 28 Feb 2025 20:15:48 +0000 Subject: [PATCH 1179/1617] Do not blindly undefer on leaving fuction (#18674) Fixes https://github.com/python/mypy/issues/16496 for real. The fix is trivial, just save and restore the previous value. --- mypy/checker.py | 4 +++- test-data/unit/check-inference.test | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index b8d5bbd4fa2d..ac4b24709783 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1108,6 +1108,7 @@ def check_func_item( """ self.dynamic_funcs.append(defn.is_dynamic() and not type_override) + enclosing_node_deferred = self.current_node_deferred with self.enter_partial_types(is_function=True): typ = self.function_type(defn) if type_override: @@ -1119,7 +1120,7 @@ def check_func_item( raise RuntimeError("Not supported") self.dynamic_funcs.pop() - self.current_node_deferred = False + self.current_node_deferred = enclosing_node_deferred if name == "__exit__": self.check__exit__return_type(defn) @@ -5341,6 +5342,7 @@ def check_for_untyped_decorator( self.options.disallow_untyped_decorators and is_typed_callable(func.type) and is_untyped_decorator(dec_type) + and not self.current_node_deferred ): self.msg.typed_function_untyped_decorator(func.name, dec_expr) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index ff351686dfc2..42b5a05ab39a 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3945,3 +3945,21 @@ def deco(fn: Callable[[], T]) -> Callable[[], T]: ... @deco def defer() -> int: ... + +[case testVariableDeferredWithNestedFunction] +from typing import Callable, TypeVar + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def f() -> None: + x = 1 + f() # defer current node + x = x + + def nested() -> None: + ... + + # The type below should not be Any. + reveal_type(x) # N: Revealed type is "builtins.int" From 9fe9525966811295452a19d4eea382c3fc07df94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 19:43:36 -0800 Subject: [PATCH 1180/1617] Sync typeshed (#18747) Source commit: https://github.com/python/typeshed/commit/0b13c1deb6d0b2cdc78b246da9a0863c87dd8424 --- mypy/typeshed/stdlib/_hashlib.pyi | 17 +- mypy/typeshed/stdlib/asyncio/__init__.pyi | 178 ----- mypy/typeshed/stdlib/builtins.pyi | 44 +- mypy/typeshed/stdlib/configparser.pyi | 103 +-- mypy/typeshed/stdlib/functools.pyi | 18 +- mypy/typeshed/stdlib/hashlib.pyi | 3 +- mypy/typeshed/stdlib/hmac.pyi | 4 +- mypy/typeshed/stdlib/importlib/readers.pyi | 14 +- mypy/typeshed/stdlib/pathlib.pyi | 6 +- mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi | 11 +- mypy/typeshed/stdlib/xml/dom/__init__.pyi | 137 ++-- mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi | 111 +-- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 667 +++++++++++++----- mypy/typeshed/stdlib/xml/dom/pulldom.pyi | 125 ++-- mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi | 69 +- .../stdlib/xml/etree/ElementInclude.pyi | 23 +- .../typeshed/stdlib/xml/etree/ElementPath.pyi | 31 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 124 ++-- mypy/typeshed/stdlib/xml/sax/_exceptions.pyi | 10 +- mypy/typeshed/stdlib/xml/sax/expatreader.pyi | 77 +- mypy/typeshed/stdlib/xml/sax/handler.pyi | 65 +- mypy/typeshed/stdlib/xml/sax/saxutils.pyi | 40 +- mypy/typeshed/stdlib/xml/sax/xmlreader.pyi | 115 +-- 23 files changed, 1159 insertions(+), 833 deletions(-) diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi index 5cf85e4cacaa..e91f2cdb331c 100644 --- a/mypy/typeshed/stdlib/_hashlib.pyi +++ b/mypy/typeshed/stdlib/_hashlib.pyi @@ -2,13 +2,26 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Callable from types import ModuleType -from typing import AnyStr, final, overload +from typing import AnyStr, Protocol, final, overload, type_check_only from typing_extensions import Self, TypeAlias -_DigestMod: TypeAlias = str | Callable[[], HASH] | ModuleType | None +_DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType | None openssl_md_meth_names: frozenset[str] +@type_check_only +class _HashObject(Protocol): + @property + def digest_size(self) -> int: ... + @property + def block_size(self) -> int: ... + @property + def name(self) -> str: ... + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, obj: ReadableBuffer, /) -> None: ... + class HASH: @property def digest_size(self) -> int: ... diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index 7c3ac6ede4fe..89a8143c5f7f 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -410,93 +410,6 @@ if sys.platform == "win32": "WindowsSelectorEventLoopPolicy", # from windows_events "WindowsProactorEventLoopPolicy", # from windows_events ) - elif sys.version_info >= (3, 10): - __all__ = ( - "BaseEventLoop", # from base_events - "Server", # from base_events - "coroutine", # from coroutines - "iscoroutinefunction", # from coroutines - "iscoroutine", # from coroutines - "AbstractEventLoopPolicy", # from events - "AbstractEventLoop", # from events - "AbstractServer", # from events - "Handle", # from events - "TimerHandle", # from events - "get_event_loop_policy", # from events - "set_event_loop_policy", # from events - "get_event_loop", # from events - "set_event_loop", # from events - "new_event_loop", # from events - "get_child_watcher", # from events - "set_child_watcher", # from events - "_set_running_loop", # from events - "get_running_loop", # from events - "_get_running_loop", # from events - "CancelledError", # from exceptions - "InvalidStateError", # from exceptions - "TimeoutError", # from exceptions - "IncompleteReadError", # from exceptions - "LimitOverrunError", # from exceptions - "SendfileNotAvailableError", # from exceptions - "Future", # from futures - "wrap_future", # from futures - "isfuture", # from futures - "Lock", # from locks - "Event", # from locks - "Condition", # from locks - "Semaphore", # from locks - "BoundedSemaphore", # from locks - "BaseProtocol", # from protocols - "Protocol", # from protocols - "DatagramProtocol", # from protocols - "SubprocessProtocol", # from protocols - "BufferedProtocol", # from protocols - "run", # from runners - "Queue", # from queues - "PriorityQueue", # from queues - "LifoQueue", # from queues - "QueueFull", # from queues - "QueueEmpty", # from queues - "StreamReader", # from streams - "StreamWriter", # from streams - "StreamReaderProtocol", # from streams - "open_connection", # from streams - "start_server", # from streams - "create_subprocess_exec", # from subprocess - "create_subprocess_shell", # from subprocess - "Task", # from tasks - "create_task", # from tasks - "FIRST_COMPLETED", # from tasks - "FIRST_EXCEPTION", # from tasks - "ALL_COMPLETED", # from tasks - "wait", # from tasks - "wait_for", # from tasks - "as_completed", # from tasks - "sleep", # from tasks - "gather", # from tasks - "shield", # from tasks - "ensure_future", # from tasks - "run_coroutine_threadsafe", # from tasks - "current_task", # from tasks - "all_tasks", # from tasks - "_register_task", # from tasks - "_unregister_task", # from tasks - "_enter_task", # from tasks - "_leave_task", # from tasks - "to_thread", # from threads - "BaseTransport", # from transports - "ReadTransport", # from transports - "WriteTransport", # from transports - "Transport", # from transports - "DatagramTransport", # from transports - "SubprocessTransport", # from transports - "SelectorEventLoop", # from windows_events - "ProactorEventLoop", # from windows_events - "IocpProactor", # from windows_events - "DefaultEventLoopPolicy", # from windows_events - "WindowsSelectorEventLoopPolicy", # from windows_events - "WindowsProactorEventLoopPolicy", # from windows_events - ) elif sys.version_info >= (3, 9): __all__ = ( "BaseEventLoop", # from base_events @@ -1059,97 +972,6 @@ else: "ThreadedChildWatcher", # from unix_events "DefaultEventLoopPolicy", # from unix_events ) - elif sys.version_info >= (3, 10): - __all__ = ( - "BaseEventLoop", # from base_events - "Server", # from base_events - "coroutine", # from coroutines - "iscoroutinefunction", # from coroutines - "iscoroutine", # from coroutines - "AbstractEventLoopPolicy", # from events - "AbstractEventLoop", # from events - "AbstractServer", # from events - "Handle", # from events - "TimerHandle", # from events - "get_event_loop_policy", # from events - "set_event_loop_policy", # from events - "get_event_loop", # from events - "set_event_loop", # from events - "new_event_loop", # from events - "get_child_watcher", # from events - "set_child_watcher", # from events - "_set_running_loop", # from events - "get_running_loop", # from events - "_get_running_loop", # from events - "CancelledError", # from exceptions - "InvalidStateError", # from exceptions - "TimeoutError", # from exceptions - "IncompleteReadError", # from exceptions - "LimitOverrunError", # from exceptions - "SendfileNotAvailableError", # from exceptions - "Future", # from futures - "wrap_future", # from futures - "isfuture", # from futures - "Lock", # from locks - "Event", # from locks - "Condition", # from locks - "Semaphore", # from locks - "BoundedSemaphore", # from locks - "BaseProtocol", # from protocols - "Protocol", # from protocols - "DatagramProtocol", # from protocols - "SubprocessProtocol", # from protocols - "BufferedProtocol", # from protocols - "run", # from runners - "Queue", # from queues - "PriorityQueue", # from queues - "LifoQueue", # from queues - "QueueFull", # from queues - "QueueEmpty", # from queues - "StreamReader", # from streams - "StreamWriter", # from streams - "StreamReaderProtocol", # from streams - "open_connection", # from streams - "start_server", # from streams - "open_unix_connection", # from streams - "start_unix_server", # from streams - "create_subprocess_exec", # from subprocess - "create_subprocess_shell", # from subprocess - "Task", # from tasks - "create_task", # from tasks - "FIRST_COMPLETED", # from tasks - "FIRST_EXCEPTION", # from tasks - "ALL_COMPLETED", # from tasks - "wait", # from tasks - "wait_for", # from tasks - "as_completed", # from tasks - "sleep", # from tasks - "gather", # from tasks - "shield", # from tasks - "ensure_future", # from tasks - "run_coroutine_threadsafe", # from tasks - "current_task", # from tasks - "all_tasks", # from tasks - "_register_task", # from tasks - "_unregister_task", # from tasks - "_enter_task", # from tasks - "_leave_task", # from tasks - "to_thread", # from threads - "BaseTransport", # from transports - "ReadTransport", # from transports - "WriteTransport", # from transports - "Transport", # from transports - "DatagramTransport", # from transports - "SubprocessTransport", # from transports - "SelectorEventLoop", # from unix_events - "AbstractChildWatcher", # from unix_events - "SafeChildWatcher", # from unix_events - "FastChildWatcher", # from unix_events - "PidfdChildWatcher", # from unix_events - "MultiLoopChildWatcher", # from unix_events - "ThreadedChildWatcher", # from unix_events - "DefaultEventLoopPolicy", # from unix_events - ) elif sys.version_info >= (3, 9): __all__ = ( "BaseEventLoop", # from base_events diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index c278707c273f..61114afb804d 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -10,7 +10,6 @@ from _typeshed import ( ConvertibleToFloat, ConvertibleToInt, FileDescriptorOrPath, - MaybeNone, OpenBinaryMode, OpenBinaryModeReading, OpenBinaryModeUpdating, @@ -94,9 +93,14 @@ _SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) _P = ParamSpec("_P") -_StartT = TypeVar("_StartT", covariant=True, default=Any) -_StopT = TypeVar("_StopT", covariant=True, default=Any) -_StepT = TypeVar("_StepT", covariant=True, default=Any) + +# Type variables for slice +_StartT_co = TypeVar("_StartT_co", covariant=True, default=Any) # slice -> slice[Any, Any, Any] +_StopT_co = TypeVar("_StopT_co", covariant=True, default=_StartT_co) # slice[A] -> slice[A, A, A] +# NOTE: step could differ from start and stop, (e.g. datetime/timedelta)l +# the default (start|stop) is chosen to cater to the most common case of int/index slices. +# FIXME: https://github.com/python/typing/issues/213 (replace step=start|stop with step=start&stop) +_StepT_co = TypeVar("_StepT_co", covariant=True, default=_StartT_co | _StopT_co) # slice[A,B] -> slice[A, B, A|B] class object: __doc__: str | None @@ -842,23 +846,35 @@ class bool(int): def __invert__(self) -> int: ... @final -class slice(Generic[_StartT, _StopT, _StepT]): +class slice(Generic[_StartT_co, _StopT_co, _StepT_co]): @property - def start(self) -> _StartT: ... + def start(self) -> _StartT_co: ... @property - def step(self) -> _StepT: ... + def step(self) -> _StepT_co: ... @property - def stop(self) -> _StopT: ... - @overload - def __new__(cls, stop: int | None, /) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... + def stop(self) -> _StopT_co: ... + # Note: __new__ overloads map `None` to `Any`, since users expect slice(x, None) + # to be compatible with slice(None, x). + # generic slice -------------------------------------------------------------------- @overload - def __new__( - cls, start: int | None, stop: int | None, step: int | None = None, / - ) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... + def __new__(cls, start: None, stop: None = None, step: None = None, /) -> slice[Any, Any, Any]: ... + # unary overloads ------------------------------------------------------------------ @overload def __new__(cls, stop: _T2, /) -> slice[Any, _T2, Any]: ... + # binary overloads ----------------------------------------------------------------- + @overload + def __new__(cls, start: _T1, stop: None, step: None = None, /) -> slice[_T1, Any, Any]: ... + @overload + def __new__(cls, start: None, stop: _T2, step: None = None, /) -> slice[Any, _T2, Any]: ... + @overload + def __new__(cls, start: _T1, stop: _T2, step: None = None, /) -> slice[_T1, _T2, Any]: ... + # ternary overloads ---------------------------------------------------------------- + @overload + def __new__(cls, start: None, stop: None, step: _T3, /) -> slice[Any, Any, _T3]: ... + @overload + def __new__(cls, start: _T1, stop: None, step: _T3, /) -> slice[_T1, Any, _T3]: ... @overload - def __new__(cls, start: _T1, stop: _T2, /) -> slice[_T1, _T2, Any]: ... + def __new__(cls, start: None, stop: _T2, step: _T3, /) -> slice[Any, _T2, _T3]: ... @overload def __new__(cls, start: _T1, stop: _T2, step: _T3, /) -> slice[_T1, _T2, _T3]: ... def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index a44dc2e1c035..8996c85d9a53 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -77,6 +77,19 @@ else: "MAX_INTERPOLATION_DEPTH", ] +if sys.version_info >= (3, 13): + class _UNNAMED_SECTION: ... + UNNAMED_SECTION: _UNNAMED_SECTION + + _SectionName: TypeAlias = str | _UNNAMED_SECTION + # A list of sections can only include an unnamed section if the parser was initialized with + # allow_unnamed_section=True. Any prevents users from having to use explicit + # type checks if allow_unnamed_section is False (the default). + _SectionNameList: TypeAlias = list[Any] +else: + _SectionName: TypeAlias = str + _SectionNameList: TypeAlias = list[str] + _Section: TypeAlias = Mapping[str, str] _Parser: TypeAlias = MutableMapping[str, _Section] _ConverterCallback: TypeAlias = Callable[[str], Any] @@ -87,17 +100,17 @@ DEFAULTSECT: Final = "DEFAULT" MAX_INTERPOLATION_DEPTH: Final = 10 class Interpolation: - def before_get(self, parser: _Parser, section: str, option: str, value: str, defaults: _Section) -> str: ... - def before_set(self, parser: _Parser, section: str, option: str, value: str) -> str: ... - def before_read(self, parser: _Parser, section: str, option: str, value: str) -> str: ... - def before_write(self, parser: _Parser, section: str, option: str, value: str) -> str: ... + def before_get(self, parser: _Parser, section: _SectionName, option: str, value: str, defaults: _Section) -> str: ... + def before_set(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... + def before_read(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... + def before_write(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... class BasicInterpolation(Interpolation): ... class ExtendedInterpolation(Interpolation): ... if sys.version_info < (3, 13): class LegacyInterpolation(Interpolation): - def before_get(self, parser: _Parser, section: str, option: str, value: str, vars: _Section) -> str: ... + def before_get(self, parser: _Parser, section: _SectionName, option: str, value: str, vars: _Section) -> str: ... class RawConfigParser(_Parser): _SECT_TMPL: ClassVar[str] # undocumented @@ -220,11 +233,11 @@ class RawConfigParser(_Parser): def __iter__(self) -> Iterator[str]: ... def __contains__(self, key: object) -> bool: ... def defaults(self) -> _Section: ... - def sections(self) -> list[str]: ... - def add_section(self, section: str) -> None: ... - def has_section(self, section: str) -> bool: ... - def options(self, section: str) -> list[str]: ... - def has_option(self, section: str, option: str) -> bool: ... + def sections(self) -> _SectionNameList: ... + def add_section(self, section: _SectionName) -> None: ... + def has_section(self, section: _SectionName) -> bool: ... + def options(self, section: _SectionName) -> list[str]: ... + def has_option(self, section: _SectionName, option: str) -> bool: ... def read(self, filenames: StrOrBytesPath | Iterable[StrOrBytesPath], encoding: str | None = None) -> list[str]: ... def read_file(self, f: Iterable[str], source: str | None = None) -> None: ... def read_string(self, string: str, source: str = "") -> None: ... @@ -234,26 +247,26 @@ class RawConfigParser(_Parser): # These get* methods are partially applied (with the same names) in # SectionProxy; the stubs should be kept updated together @overload - def getint(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> int: ... + def getint(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> int: ... @overload def getint( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> int | _T: ... @overload - def getfloat(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> float: ... + def getfloat(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> float: ... @overload def getfloat( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> float | _T: ... @overload - def getboolean(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool: ... + def getboolean(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool: ... @overload def getboolean( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> bool | _T: ... def _get_conv( self, - section: str, + section: _SectionName, option: str, conv: Callable[[str], _T], *, @@ -263,19 +276,19 @@ class RawConfigParser(_Parser): ) -> _T: ... # This is incompatible with MutableMapping so we ignore the type @overload # type: ignore[override] - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ... + def get(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ... @overload def get( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T ) -> str | _T | MaybeNone: ... @overload def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ... @overload - def items(self, section: str, raw: bool = False, vars: _Section | None = None) -> list[tuple[str, str]]: ... - def set(self, section: str, option: str, value: str | None = None) -> None: ... + def items(self, section: _SectionName, raw: bool = False, vars: _Section | None = None) -> list[tuple[str, str]]: ... + def set(self, section: _SectionName, option: str, value: str | None = None) -> None: ... def write(self, fp: SupportsWrite[str], space_around_delimiters: bool = True) -> None: ... - def remove_option(self, section: str, option: str) -> bool: ... - def remove_section(self, section: str) -> bool: ... + def remove_option(self, section: _SectionName, option: str) -> bool: ... + def remove_section(self, section: _SectionName) -> bool: ... def optionxform(self, optionstr: str) -> str: ... @property def converters(self) -> ConverterMapping: ... @@ -283,9 +296,11 @@ class RawConfigParser(_Parser): class ConfigParser(RawConfigParser): # This is incompatible with MutableMapping so we ignore the type @overload # type: ignore[override] - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str: ... + def get(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> str: ... @overload - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T) -> str | _T: ... + def get( + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T + ) -> str | _T: ... if sys.version_info < (3, 12): class SafeConfigParser(ConfigParser): ... # deprecated alias @@ -305,7 +320,14 @@ class SectionProxy(MutableMapping[str, str]): # This is incompatible with MutableMapping so we ignore the type @overload # type: ignore[override] def get( - self, option: str, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any + self, + option: str, + fallback: None = None, + *, + raw: bool = False, + vars: _Section | None = None, + _impl: Any | None = None, + **kwargs: Any, # passed to the underlying parser's get() method ) -> str | None: ... @overload def get( @@ -316,7 +338,7 @@ class SectionProxy(MutableMapping[str, str]): raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, - **kwargs: Any, + **kwargs: Any, # passed to the underlying parser's get() method ) -> str | _T: ... # These are partially-applied version of the methods with the same names in # RawConfigParser; the stubs should be kept updated together @@ -349,38 +371,38 @@ class Error(Exception): def __init__(self, msg: str = "") -> None: ... class NoSectionError(Error): - section: str - def __init__(self, section: str) -> None: ... + section: _SectionName + def __init__(self, section: _SectionName) -> None: ... class DuplicateSectionError(Error): - section: str + section: _SectionName source: str | None lineno: int | None - def __init__(self, section: str, source: str | None = None, lineno: int | None = None) -> None: ... + def __init__(self, section: _SectionName, source: str | None = None, lineno: int | None = None) -> None: ... class DuplicateOptionError(Error): - section: str + section: _SectionName option: str source: str | None lineno: int | None - def __init__(self, section: str, option: str, source: str | None = None, lineno: int | None = None) -> None: ... + def __init__(self, section: _SectionName, option: str, source: str | None = None, lineno: int | None = None) -> None: ... class NoOptionError(Error): - section: str + section: _SectionName option: str - def __init__(self, option: str, section: str) -> None: ... + def __init__(self, option: str, section: _SectionName) -> None: ... class InterpolationError(Error): - section: str + section: _SectionName option: str - def __init__(self, option: str, section: str, msg: str) -> None: ... + def __init__(self, option: str, section: _SectionName, msg: str) -> None: ... class InterpolationDepthError(InterpolationError): - def __init__(self, option: str, section: str, rawval: object) -> None: ... + def __init__(self, option: str, section: _SectionName, rawval: object) -> None: ... class InterpolationMissingOptionError(InterpolationError): reference: str - def __init__(self, option: str, section: str, rawval: object, reference: str) -> None: ... + def __init__(self, option: str, section: _SectionName, rawval: object, reference: str) -> None: ... class InterpolationSyntaxError(InterpolationError): ... @@ -403,9 +425,6 @@ class MissingSectionHeaderError(ParsingError): def __init__(self, filename: str, lineno: int, line: str) -> None: ... if sys.version_info >= (3, 13): - class _UNNAMED_SECTION: ... - UNNAMED_SECTION: _UNNAMED_SECTION - class MultilineContinuationError(ParsingError): lineno: int line: str diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 9957fa8f1634..10563e654b37 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,7 +1,7 @@ import sys import types from _typeshed import SupportsAllComparisons, SupportsItems -from collections.abc import Callable, Hashable, Iterable, Sequence, Sized +from collections.abc import Callable, Hashable, Iterable, Sized from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload from typing_extensions import ParamSpec, Self, TypeAlias @@ -97,26 +97,26 @@ if sys.version_info >= (3, 12): def update_wrapper( wrapper: Callable[_PWrapper, _RWrapper], wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( wrapper: Callable[_PWrapper, _RWrapper], wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index db6f8635054d..84666a7fa725 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -2,6 +2,7 @@ import sys from _blake2 import blake2b as blake2b, blake2s as blake2s from _hashlib import ( HASH, + _HashObject, openssl_md5 as md5, openssl_sha1 as sha1, openssl_sha224 as sha224, @@ -97,7 +98,7 @@ if sys.version_info >= (3, 11): def readable(self) -> bool: ... def file_digest( - fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], HASH], /, *, _bufsize: int = 262144 + fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _HashObject], /, *, _bufsize: int = 262144 ) -> HASH: ... # Legacy typing-only alias diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index efd649ec39a8..dfb574c177cd 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,12 +1,12 @@ import sys -from _hashlib import HASH as _HashlibHash +from _hashlib import _HashObject from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable from types import ModuleType from typing import AnyStr, overload from typing_extensions import TypeAlias -_DigestMod: TypeAlias = str | Callable[[], _HashlibHash] | ModuleType +_DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType trans_5C: bytes trans_36: bytes diff --git a/mypy/typeshed/stdlib/importlib/readers.pyi b/mypy/typeshed/stdlib/importlib/readers.pyi index 41d7af966d58..ceb3e731e7a5 100644 --- a/mypy/typeshed/stdlib/importlib/readers.pyi +++ b/mypy/typeshed/stdlib/importlib/readers.pyi @@ -5,12 +5,16 @@ import pathlib import sys import zipfile -from _typeshed import Incomplete, StrPath +from _typeshed import StrPath from collections.abc import Iterable, Iterator from io import BufferedReader from typing import Literal, NoReturn, TypeVar from typing_extensions import Never +if sys.version_info >= (3, 10): + from importlib._bootstrap_external import FileLoader + from zipimport import zipimporter + if sys.version_info >= (3, 11): import importlib.resources.abc as abc else: @@ -27,14 +31,14 @@ if sys.version_info >= (3, 10): class FileReader(abc.TraversableResources): path: pathlib.Path - def __init__(self, loader) -> None: ... + def __init__(self, loader: FileLoader) -> None: ... def resource_path(self, resource: StrPath) -> str: ... def files(self) -> pathlib.Path: ... class ZipReader(abc.TraversableResources): prefix: str - archive: Incomplete - def __init__(self, loader, module: str) -> None: ... + archive: str + def __init__(self, loader: zipimporter, module: str) -> None: ... def open_resource(self, resource: str) -> BufferedReader: ... def is_resource(self, path: StrPath) -> bool: ... def files(self) -> zipfile.Path: ... @@ -63,6 +67,6 @@ if sys.version_info >= (3, 10): class NamespaceReader(abc.TraversableResources): path: MultiplexedPath - def __init__(self, namespace_path) -> None: ... + def __init__(self, namespace_path: Iterable[str]) -> None: ... def resource_path(self, resource: str) -> str: ... def files(self) -> MultiplexedPath: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index bdca375f626d..e2a816ae1ca4 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -129,12 +129,10 @@ class Path(PurePath): def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ... if sys.version_info >= (3, 13): - def glob( - self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False - ) -> Generator[Self, None, None]: ... + def glob(self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False) -> Iterator[Self]: ... def rglob( self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False - ) -> Generator[Self, None, None]: ... + ) -> Iterator[Self]: ... elif sys.version_info >= (3, 12): def glob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... def rglob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... diff --git a/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi index 80fb73d23433..007df982e06a 100644 --- a/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi +++ b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi @@ -1,7 +1,10 @@ +from typing import Literal +from xml.dom.minidom import Node + class NodeFilter: - FILTER_ACCEPT: int - FILTER_REJECT: int - FILTER_SKIP: int + FILTER_ACCEPT: Literal[1] + FILTER_REJECT: Literal[2] + FILTER_SKIP: Literal[3] SHOW_ALL: int SHOW_ELEMENT: int @@ -16,4 +19,4 @@ class NodeFilter: SHOW_DOCUMENT_TYPE: int SHOW_DOCUMENT_FRAGMENT: int SHOW_NOTATION: int - def acceptNode(self, node) -> int: ... + def acceptNode(self, node: Node) -> int: ... diff --git a/mypy/typeshed/stdlib/xml/dom/__init__.pyi b/mypy/typeshed/stdlib/xml/dom/__init__.pyi index 8738015638a9..d9615f9aacfe 100644 --- a/mypy/typeshed/stdlib/xml/dom/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/dom/__init__.pyi @@ -1,69 +1,100 @@ -from typing import Any, Final +from typing import Any, Final, Literal from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation class Node: - ELEMENT_NODE: int - ATTRIBUTE_NODE: int - TEXT_NODE: int - CDATA_SECTION_NODE: int - ENTITY_REFERENCE_NODE: int - ENTITY_NODE: int - PROCESSING_INSTRUCTION_NODE: int - COMMENT_NODE: int - DOCUMENT_NODE: int - DOCUMENT_TYPE_NODE: int - DOCUMENT_FRAGMENT_NODE: int - NOTATION_NODE: int + ELEMENT_NODE: Literal[1] + ATTRIBUTE_NODE: Literal[2] + TEXT_NODE: Literal[3] + CDATA_SECTION_NODE: Literal[4] + ENTITY_REFERENCE_NODE: Literal[5] + ENTITY_NODE: Literal[6] + PROCESSING_INSTRUCTION_NODE: Literal[7] + COMMENT_NODE: Literal[8] + DOCUMENT_NODE: Literal[9] + DOCUMENT_TYPE_NODE: Literal[10] + DOCUMENT_FRAGMENT_NODE: Literal[11] + NOTATION_NODE: Literal[12] # ExceptionCode -INDEX_SIZE_ERR: Final[int] -DOMSTRING_SIZE_ERR: Final[int] -HIERARCHY_REQUEST_ERR: Final[int] -WRONG_DOCUMENT_ERR: Final[int] -INVALID_CHARACTER_ERR: Final[int] -NO_DATA_ALLOWED_ERR: Final[int] -NO_MODIFICATION_ALLOWED_ERR: Final[int] -NOT_FOUND_ERR: Final[int] -NOT_SUPPORTED_ERR: Final[int] -INUSE_ATTRIBUTE_ERR: Final[int] -INVALID_STATE_ERR: Final[int] -SYNTAX_ERR: Final[int] -INVALID_MODIFICATION_ERR: Final[int] -NAMESPACE_ERR: Final[int] -INVALID_ACCESS_ERR: Final[int] -VALIDATION_ERR: Final[int] +INDEX_SIZE_ERR: Final = 1 +DOMSTRING_SIZE_ERR: Final = 2 +HIERARCHY_REQUEST_ERR: Final = 3 +WRONG_DOCUMENT_ERR: Final = 4 +INVALID_CHARACTER_ERR: Final = 5 +NO_DATA_ALLOWED_ERR: Final = 6 +NO_MODIFICATION_ALLOWED_ERR: Final = 7 +NOT_FOUND_ERR: Final = 8 +NOT_SUPPORTED_ERR: Final = 9 +INUSE_ATTRIBUTE_ERR: Final = 10 +INVALID_STATE_ERR: Final = 11 +SYNTAX_ERR: Final = 12 +INVALID_MODIFICATION_ERR: Final = 13 +NAMESPACE_ERR: Final = 14 +INVALID_ACCESS_ERR: Final = 15 +VALIDATION_ERR: Final = 16 class DOMException(Exception): code: int def __init__(self, *args: Any, **kw: Any) -> None: ... def _get_code(self) -> int: ... -class IndexSizeErr(DOMException): ... -class DomstringSizeErr(DOMException): ... -class HierarchyRequestErr(DOMException): ... -class WrongDocumentErr(DOMException): ... -class InvalidCharacterErr(DOMException): ... -class NoDataAllowedErr(DOMException): ... -class NoModificationAllowedErr(DOMException): ... -class NotFoundErr(DOMException): ... -class NotSupportedErr(DOMException): ... -class InuseAttributeErr(DOMException): ... -class InvalidStateErr(DOMException): ... -class SyntaxErr(DOMException): ... -class InvalidModificationErr(DOMException): ... -class NamespaceErr(DOMException): ... -class InvalidAccessErr(DOMException): ... -class ValidationErr(DOMException): ... +class IndexSizeErr(DOMException): + code: Literal[1] + +class DomstringSizeErr(DOMException): + code: Literal[2] + +class HierarchyRequestErr(DOMException): + code: Literal[3] + +class WrongDocumentErr(DOMException): + code: Literal[4] + +class InvalidCharacterErr(DOMException): + code: Literal[5] + +class NoDataAllowedErr(DOMException): + code: Literal[6] + +class NoModificationAllowedErr(DOMException): + code: Literal[7] + +class NotFoundErr(DOMException): + code: Literal[8] + +class NotSupportedErr(DOMException): + code: Literal[9] + +class InuseAttributeErr(DOMException): + code: Literal[10] + +class InvalidStateErr(DOMException): + code: Literal[11] + +class SyntaxErr(DOMException): + code: Literal[12] + +class InvalidModificationErr(DOMException): + code: Literal[13] + +class NamespaceErr(DOMException): + code: Literal[14] + +class InvalidAccessErr(DOMException): + code: Literal[15] + +class ValidationErr(DOMException): + code: Literal[16] class UserDataHandler: - NODE_CLONED: int - NODE_IMPORTED: int - NODE_DELETED: int - NODE_RENAMED: int - -XML_NAMESPACE: Final[str] -XMLNS_NAMESPACE: Final[str] -XHTML_NAMESPACE: Final[str] + NODE_CLONED: Literal[1] + NODE_IMPORTED: Literal[2] + NODE_DELETED: Literal[3] + NODE_RENAMED: Literal[4] + +XML_NAMESPACE: Final = "http://www.w3.org/XML/1998/namespace" +XMLNS_NAMESPACE: Final = "http://www.w3.org/2000/xmlns/" +XHTML_NAMESPACE: Final = "http://www.w3.org/1999/xhtml" EMPTY_NAMESPACE: Final[None] EMPTY_PREFIX: Final[None] diff --git a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi index 45f0af7aa979..228ad07e15ad 100644 --- a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi @@ -1,7 +1,11 @@ -from _typeshed import Incomplete, ReadableBuffer, SupportsRead +from _typeshed import ReadableBuffer, SupportsRead from typing import Any, NoReturn -from xml.dom.minidom import Document, DOMImplementation, Node, TypeInfo +from typing_extensions import TypeAlias +from xml.dom.minidom import Document, DocumentFragment, DOMImplementation, Element, Node, TypeInfo from xml.dom.xmlbuilder import DOMBuilderFilter, Options +from xml.parsers.expat import XMLParserType + +_Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] # same as in pyexpat TEXT_NODE = Node.TEXT_NODE CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE @@ -10,45 +14,56 @@ FILTER_ACCEPT = DOMBuilderFilter.FILTER_ACCEPT FILTER_REJECT = DOMBuilderFilter.FILTER_REJECT FILTER_SKIP = DOMBuilderFilter.FILTER_SKIP FILTER_INTERRUPT = DOMBuilderFilter.FILTER_INTERRUPT -theDOMImplementation: DOMImplementation | None +theDOMImplementation: DOMImplementation class ElementInfo: - tagName: Incomplete - def __init__(self, tagName, model: Incomplete | None = None) -> None: ... - def getAttributeType(self, aname) -> TypeInfo: ... - def getAttributeTypeNS(self, namespaceURI, localName) -> TypeInfo: ... + tagName: str + def __init__(self, tagName: str, model: _Model | None = None) -> None: ... + def getAttributeType(self, aname: str) -> TypeInfo: ... + def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ... def isElementContent(self) -> bool: ... def isEmpty(self) -> bool: ... - def isId(self, aname) -> bool: ... - def isIdNS(self, euri, ename, auri, aname) -> bool: ... + def isId(self, aname: str) -> bool: ... + def isIdNS(self, euri: str, ename: str, auri: str, aname: str) -> bool: ... class ExpatBuilder: document: Document # Created in self.reset() - curNode: Incomplete # Created in self.reset() + curNode: DocumentFragment | Element | Document # Created in self.reset() def __init__(self, options: Options | None = None) -> None: ... - def createParser(self): ... - def getParser(self): ... + def createParser(self) -> XMLParserType: ... + def getParser(self) -> XMLParserType: ... def reset(self) -> None: ... - def install(self, parser) -> None: ... + def install(self, parser: XMLParserType) -> None: ... def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> Document: ... def parseString(self, string: str | ReadableBuffer) -> Document: ... - def start_doctype_decl_handler(self, doctypeName, systemId, publicId, has_internal_subset) -> None: ... + def start_doctype_decl_handler( + self, doctypeName: str, systemId: str | None, publicId: str | None, has_internal_subset: bool + ) -> None: ... def end_doctype_decl_handler(self) -> None: ... - def pi_handler(self, target, data) -> None: ... - def character_data_handler_cdata(self, data) -> None: ... - def character_data_handler(self, data) -> None: ... + def pi_handler(self, target: str, data: str) -> None: ... + def character_data_handler_cdata(self, data: str) -> None: ... + def character_data_handler(self, data: str) -> None: ... def start_cdata_section_handler(self) -> None: ... def end_cdata_section_handler(self) -> None: ... - def entity_decl_handler(self, entityName, is_parameter_entity, value, base, systemId, publicId, notationName) -> None: ... - def notation_decl_handler(self, notationName, base, systemId, publicId) -> None: ... - def comment_handler(self, data) -> None: ... - def external_entity_ref_handler(self, context, base, systemId, publicId) -> int: ... - def first_element_handler(self, name, attributes) -> None: ... - def start_element_handler(self, name, attributes) -> None: ... - def end_element_handler(self, name) -> None: ... - def element_decl_handler(self, name, model) -> None: ... - def attlist_decl_handler(self, elem, name, type, default, required) -> None: ... - def xml_decl_handler(self, version, encoding, standalone) -> None: ... + def entity_decl_handler( + self, + entityName: str, + is_parameter_entity: bool, + value: str | None, + base: str | None, + systemId: str, + publicId: str | None, + notationName: str | None, + ) -> None: ... + def notation_decl_handler(self, notationName: str, base: str | None, systemId: str, publicId: str | None) -> None: ... + def comment_handler(self, data: str) -> None: ... + def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ... + def first_element_handler(self, name: str, attributes: list[str]) -> None: ... + def start_element_handler(self, name: str, attributes: list[str]) -> None: ... + def end_element_handler(self, name: str) -> None: ... + def element_decl_handler(self, name: str, model: _Model) -> None: ... + def attlist_decl_handler(self, elem: str, name: str, type: str, default: str | None, required: bool) -> None: ... + def xml_decl_handler(self, version: str, encoding: str | None, standalone: int) -> None: ... class FilterVisibilityController: filter: DOMBuilderFilter @@ -57,7 +72,7 @@ class FilterVisibilityController: def acceptNode(self, node: Node) -> int: ... class FilterCrutch: - def __init__(self, builder) -> None: ... + def __init__(self, builder: ExpatBuilder) -> None: ... class Rejecter(FilterCrutch): def start_element_handler(self, *args: Any) -> None: ... @@ -68,33 +83,39 @@ class Skipper(FilterCrutch): def end_element_handler(self, *args: Any) -> None: ... class FragmentBuilder(ExpatBuilder): - fragment: Incomplete | None - originalDocument: Incomplete - context: Incomplete - def __init__(self, context, options: Options | None = None) -> None: ... + fragment: DocumentFragment | None + originalDocument: Document + context: Node + def __init__(self, context: Node, options: Options | None = None) -> None: ... + def reset(self) -> None: ... + def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> DocumentFragment: ... # type: ignore[override] + def parseString(self, string: ReadableBuffer | str) -> DocumentFragment: ... # type: ignore[override] + def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ... class Namespaces: - def createParser(self): ... - def install(self, parser) -> None: ... - def start_namespace_decl_handler(self, prefix, uri) -> None: ... - def start_element_handler(self, name, attributes) -> None: ... - def end_element_handler(self, name) -> None: ... + def createParser(self) -> XMLParserType: ... + def install(self, parser: XMLParserType) -> None: ... + def start_namespace_decl_handler(self, prefix: str | None, uri: str) -> None: ... + def start_element_handler(self, name: str, attributes: list[str]) -> None: ... + def end_element_handler(self, name: str) -> None: ... # only exists if __debug__ class ExpatBuilderNS(Namespaces, ExpatBuilder): ... class FragmentBuilderNS(Namespaces, FragmentBuilder): ... class ParseEscape(Exception): ... class InternalSubsetExtractor(ExpatBuilder): - subset: Any | None - def getSubset(self) -> Any | None: ... + subset: str | list[str] | None = None + def getSubset(self) -> str: ... def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> None: ... # type: ignore[override] def parseString(self, string: str | ReadableBuffer) -> None: ... # type: ignore[override] - def start_doctype_decl_handler(self, name, publicId, systemId, has_internal_subset) -> None: ... # type: ignore[override] + def start_doctype_decl_handler( # type: ignore[override] + self, name: str, publicId: str | None, systemId: str | None, has_internal_subset: bool + ) -> None: ... def end_doctype_decl_handler(self) -> NoReturn: ... - def start_element_handler(self, name, attrs) -> NoReturn: ... + def start_element_handler(self, name: str, attrs: list[str]) -> NoReturn: ... -def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True): ... -def parseString(string: str | ReadableBuffer, namespaces: bool = True): ... -def parseFragment(file, context, namespaces: bool = True): ... -def parseFragmentString(string: str, context, namespaces: bool = True): ... +def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True) -> Document: ... +def parseString(string: str | ReadableBuffer, namespaces: bool = True) -> Document: ... +def parseFragment(file: str | SupportsRead[ReadableBuffer | str], context: Node, namespaces: bool = True) -> DocumentFragment: ... +def parseFragmentString(string: str | ReadableBuffer, context: Node, namespaces: bool = True) -> DocumentFragment: ... def makeBuilder(options: Options) -> ExpatBuilderNS | ExpatBuilder: ... diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index d7da59a7ed4b..51bbf4993657 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -1,33 +1,92 @@ import sys import xml.dom +from _collections_abc import dict_keys, dict_values from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite -from typing import ClassVar, Literal, NoReturn, TypeVar, overload -from typing_extensions import Self -from xml.dom.minicompat import NodeList +from collections.abc import Iterable, Sequence +from types import TracebackType +from typing import Any, ClassVar, Generic, Literal, NoReturn, Protocol, TypeVar, overload +from typing_extensions import Self, TypeAlias +from xml.dom.minicompat import EmptyNodeList, NodeList from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS from xml.sax.xmlreader import XMLReader +_NSName: TypeAlias = tuple[str | None, str] + +# Entity can also have children, but it's not implemented the same way as the +# others, so is deliberately omitted here. +_NodesWithChildren: TypeAlias = DocumentFragment | Attr | Element | Document +_NodesThatAreChildren: TypeAlias = CDATASection | Comment | DocumentType | Element | Notation | ProcessingInstruction | Text + +_AttrChildren: TypeAlias = Text # Also EntityReference, but we don't implement it +_ElementChildren: TypeAlias = Element | ProcessingInstruction | Comment | Text | CDATASection +_EntityChildren: TypeAlias = Text # I think; documentation is a little unclear +_DocumentFragmentChildren: TypeAlias = Element | Text | CDATASection | ProcessingInstruction | Comment | Notation +_DocumentChildren: TypeAlias = Comment | DocumentType | Element | ProcessingInstruction + _N = TypeVar("_N", bound=Node) +_ChildNodeVar = TypeVar("_ChildNodeVar", bound=_NodesThatAreChildren) +_ChildNodePlusFragmentVar = TypeVar("_ChildNodePlusFragmentVar", bound=_NodesThatAreChildren | DocumentFragment) +_DocumentChildrenVar = TypeVar("_DocumentChildrenVar", bound=_DocumentChildren) +_ImportableNodeVar = TypeVar( + "_ImportableNodeVar", + bound=DocumentFragment + | Attr + | Element + | ProcessingInstruction + | CharacterData + | Text + | Comment + | CDATASection + | Entity + | Notation, +) + +class _DOMErrorHandler(Protocol): + def handleError(self, error: Exception) -> bool: ... + +class _UserDataHandler(Protocol): + def handle(self, operation: int, key: str, data: Any, src: Node, dst: Node) -> None: ... def parse( file: str | SupportsRead[ReadableBuffer | str], parser: XMLReader | None = None, bufsize: int | None = None ) -> Document: ... def parseString(string: str | ReadableBuffer, parser: XMLReader | None = None) -> Document: ... -def getDOMImplementation(features=None) -> DOMImplementation | None: ... +@overload +def getDOMImplementation(features: None = None) -> DOMImplementation: ... +@overload +def getDOMImplementation(features: str | Iterable[tuple[str, str | None]]) -> DOMImplementation | None: ... class Node(xml.dom.Node): - namespaceURI: str | None - parentNode: Incomplete - ownerDocument: Incomplete - nextSibling: Incomplete - previousSibling: Incomplete - prefix: Incomplete + parentNode: _NodesWithChildren | Entity | None + ownerDocument: Document | None + nextSibling: _NodesThatAreChildren | None + previousSibling: _NodesThatAreChildren | None + namespaceURI: str | None # non-null only for Element and Attr + prefix: str | None # non-null only for NS Element and Attr + + # These aren't defined on Node, but they exist on all Node subclasses + # and various methods of Node require them to exist. + childNodes: ( + NodeList[_DocumentFragmentChildren] + | NodeList[_AttrChildren] + | NodeList[_ElementChildren] + | NodeList[_DocumentChildren] + | NodeList[_EntityChildren] + | EmptyNodeList + ) + nodeType: ClassVar[Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]] + nodeName: str | None # only possibly None on DocumentType + + # Not defined on Node, but exist on all Node subclasses. + nodeValue: str | None # non-null for Attr, ProcessingInstruction, Text, Comment, and CDATASection + attributes: NamedNodeMap | None # non-null only for Element + @property - def firstChild(self) -> Node | None: ... + def firstChild(self) -> _NodesThatAreChildren | None: ... @property - def lastChild(self) -> Node | None: ... + def lastChild(self) -> _NodesThatAreChildren | None: ... @property - def localName(self) -> str | None: ... + def localName(self) -> str | None: ... # non-null only for Element and Attr def __bool__(self) -> Literal[True]: ... if sys.version_info >= (3, 9): @overload @@ -95,62 +154,125 @@ class Node(xml.dom.Node): ) -> bytes: ... def hasChildNodes(self) -> bool: ... - def insertBefore(self, newChild, refChild): ... - def appendChild(self, node: _N) -> _N: ... - def replaceChild(self, newChild, oldChild): ... - def removeChild(self, oldChild): ... - def normalize(self) -> None: ... - def cloneNode(self, deep): ... - def isSupported(self, feature, version): ... - def isSameNode(self, other): ... - def getInterface(self, feature): ... - def getUserData(self, key): ... - def setUserData(self, key, data, handler): ... - childNodes: Incomplete + def insertBefore( # type: ignore[misc] + self: _NodesWithChildren, # pyright: ignore[reportGeneralTypeIssues] + newChild: _ChildNodePlusFragmentVar, + refChild: _NodesThatAreChildren | None, + ) -> _ChildNodePlusFragmentVar: ... + def appendChild( # type: ignore[misc] + self: _NodesWithChildren, node: _ChildNodePlusFragmentVar # pyright: ignore[reportGeneralTypeIssues] + ) -> _ChildNodePlusFragmentVar: ... + @overload + def replaceChild( # type: ignore[misc] + self: _NodesWithChildren, newChild: DocumentFragment, oldChild: _ChildNodeVar + ) -> _ChildNodeVar | DocumentFragment: ... + @overload + def replaceChild( # type: ignore[misc] + self: _NodesWithChildren, newChild: _NodesThatAreChildren, oldChild: _ChildNodeVar + ) -> _ChildNodeVar | None: ... + def removeChild(self: _NodesWithChildren, oldChild: _ChildNodeVar) -> _ChildNodeVar: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def normalize(self: _NodesWithChildren) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def cloneNode(self, deep: bool) -> Self | None: ... + def isSupported(self, feature: str, version: str | None) -> bool: ... + def isSameNode(self, other: Node) -> bool: ... + def getInterface(self, feature: str) -> Self | None: ... + def getUserData(self, key: str) -> Any | None: ... + def setUserData(self, key: str, data: Any, handler: _UserDataHandler) -> Any: ... def unlink(self) -> None: ... def __enter__(self) -> Self: ... - def __exit__(self, et, ev, tb) -> None: ... + def __exit__(self, et: type[BaseException] | None, ev: BaseException | None, tb: TracebackType | None) -> None: ... + +_DFChildrenVar = TypeVar("_DFChildrenVar", bound=_DocumentFragmentChildren) +_DFChildrenPlusFragment = TypeVar("_DFChildrenPlusFragment", bound=_DocumentFragmentChildren | DocumentFragment) class DocumentFragment(Node): - nodeType: int - nodeName: str - nodeValue: Incomplete - attributes: Incomplete - parentNode: Incomplete - childNodes: Incomplete + nodeType: ClassVar[Literal[11]] + nodeName: Literal["#document-fragment"] + nodeValue: None + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_DocumentFragmentChildren] + @property + def firstChild(self) -> _DocumentFragmentChildren | None: ... + @property + def lastChild(self) -> _DocumentFragmentChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... def __init__(self) -> None: ... + def insertBefore( # type: ignore[override] + self, newChild: _DFChildrenPlusFragment, refChild: _DocumentFragmentChildren | None + ) -> _DFChildrenPlusFragment: ... + def appendChild(self, node: _DFChildrenPlusFragment) -> _DFChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild(self, newChild: DocumentFragment, oldChild: _DFChildrenVar) -> _DFChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _DocumentFragmentChildren, oldChild: _DFChildrenVar) -> _DFChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _DFChildrenVar) -> _DFChildrenVar: ... # type: ignore[override] + +_AttrChildrenVar = TypeVar("_AttrChildrenVar", bound=_AttrChildren) +_AttrChildrenPlusFragment = TypeVar("_AttrChildrenPlusFragment", bound=_AttrChildren | DocumentFragment) class Attr(Node): - name: str - nodeType: int - attributes: Incomplete - specified: bool - ownerElement: Incomplete + nodeType: ClassVar[Literal[2]] + nodeName: str # same as Attr.name + nodeValue: str # same as Attr.value + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_AttrChildren] + @property + def firstChild(self) -> _AttrChildren | None: ... + @property + def lastChild(self) -> _AttrChildren | None: ... + namespaceURI: str | None - childNodes: Incomplete - nodeName: Incomplete - nodeValue: str + prefix: str | None + @property + def localName(self) -> str: ... + + name: str value: str - prefix: Incomplete + specified: bool + ownerElement: Element | None + def __init__( - self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: Incomplete | None = None + self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: str | None = None ) -> None: ... def unlink(self) -> None: ... @property def isId(self) -> bool: ... @property - def schemaType(self): ... + def schemaType(self) -> TypeInfo: ... + def insertBefore(self, newChild: _AttrChildrenPlusFragment, refChild: _AttrChildren | None) -> _AttrChildrenPlusFragment: ... # type: ignore[override] + def appendChild(self, node: _AttrChildrenPlusFragment) -> _AttrChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild(self, newChild: DocumentFragment, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _AttrChildren, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _AttrChildrenVar) -> _AttrChildrenVar: ... # type: ignore[override] +# In the DOM, this interface isn't specific to Attr, but our implementation is +# because that's the only place we use it. class NamedNodeMap: - def __init__(self, attrs, attrsNS, ownerElement) -> None: ... - def item(self, index): ... - def items(self): ... - def itemsNS(self): ... - def __contains__(self, key): ... - def keys(self): ... - def keysNS(self): ... - def values(self): ... - def get(self, name: str, value: Incomplete | None = None): ... + def __init__(self, attrs: dict[str, Attr], attrsNS: dict[_NSName, Attr], ownerElement: Element) -> None: ... + @property + def length(self) -> int: ... + def item(self, index: int) -> Node | None: ... + def items(self) -> list[tuple[str, str]]: ... + def itemsNS(self) -> list[tuple[_NSName, str]]: ... + def __contains__(self, key: str | _NSName) -> bool: ... + def keys(self) -> dict_keys[str, Attr]: ... + def keysNS(self) -> dict_keys[_NSName, Attr]: ... + def values(self) -> dict_values[str, Attr]: ... + def get(self, name: str, value: Attr | None = None) -> Attr | None: ... __hash__: ClassVar[None] # type: ignore[assignment] def __len__(self) -> int: ... def __eq__(self, other: object) -> bool: ... @@ -158,135 +280,227 @@ class NamedNodeMap: def __gt__(self, other: NamedNodeMap) -> bool: ... def __le__(self, other: NamedNodeMap) -> bool: ... def __lt__(self, other: NamedNodeMap) -> bool: ... - def __getitem__(self, attname_or_tuple: tuple[str, str | None] | str): ... + def __getitem__(self, attname_or_tuple: _NSName | str) -> Attr: ... def __setitem__(self, attname: str, value: Attr | str) -> None: ... def getNamedItem(self, name: str) -> Attr | None: ... - def getNamedItemNS(self, namespaceURI: str, localName: str | None) -> Attr | None: ... + def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ... def removeNamedItem(self, name: str) -> Attr: ... - def removeNamedItemNS(self, namespaceURI: str, localName: str | None): ... - def setNamedItem(self, node: Attr) -> Attr: ... - def setNamedItemNS(self, node: Attr) -> Attr: ... - def __delitem__(self, attname_or_tuple: tuple[str, str | None] | str) -> None: ... - @property - def length(self) -> int: ... + def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr: ... + def setNamedItem(self, node: Attr) -> Attr | None: ... + def setNamedItemNS(self, node: Attr) -> Attr | None: ... + def __delitem__(self, attname_or_tuple: _NSName | str) -> None: ... AttributeList = NamedNodeMap class TypeInfo: - namespace: Incomplete | None - name: str - def __init__(self, namespace: Incomplete | None, name: str) -> None: ... + namespace: str | None + name: str | None + def __init__(self, namespace: Incomplete | None, name: str | None) -> None: ... + +_ElementChildrenVar = TypeVar("_ElementChildrenVar", bound=_ElementChildren) +_ElementChildrenPlusFragment = TypeVar("_ElementChildrenPlusFragment", bound=_ElementChildren | DocumentFragment) class Element(Node): - nodeType: int - nodeValue: Incomplete - schemaType: Incomplete - parentNode: Incomplete - tagName: str - nodeName: str - prefix: Incomplete + nodeType: ClassVar[Literal[1]] + nodeName: str # same as Element.tagName + nodeValue: None + @property + def attributes(self) -> NamedNodeMap: ... # type: ignore[override] + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: NodeList[_ElementChildren] + @property + def firstChild(self) -> _ElementChildren | None: ... + @property + def lastChild(self) -> _ElementChildren | None: ... + namespaceURI: str | None - childNodes: Incomplete - nextSibling: Incomplete + prefix: str | None + @property + def localName(self) -> str: ... + + schemaType: TypeInfo + tagName: str + def __init__( - self, tagName, namespaceURI: str | None = None, prefix: Incomplete | None = None, localName: Incomplete | None = None + self, tagName: str, namespaceURI: str | None = None, prefix: str | None = None, localName: str | None = None ) -> None: ... def unlink(self) -> None: ... def getAttribute(self, attname: str) -> str: ... - def getAttributeNS(self, namespaceURI: str, localName): ... + def getAttributeNS(self, namespaceURI: str | None, localName: str) -> str: ... def setAttribute(self, attname: str, value: str) -> None: ... - def setAttributeNS(self, namespaceURI: str, qualifiedName: str, value) -> None: ... - def getAttributeNode(self, attrname: str): ... - def getAttributeNodeNS(self, namespaceURI: str, localName): ... - def setAttributeNode(self, attr): ... - setAttributeNodeNS: Incomplete + def setAttributeNS(self, namespaceURI: str | None, qualifiedName: str, value: str) -> None: ... + def getAttributeNode(self, attrname: str) -> Attr | None: ... + def getAttributeNodeNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ... + def setAttributeNode(self, attr: Attr) -> Attr | None: ... + setAttributeNodeNS = setAttributeNode def removeAttribute(self, name: str) -> None: ... - def removeAttributeNS(self, namespaceURI: str, localName) -> None: ... - def removeAttributeNode(self, node): ... - removeAttributeNodeNS: Incomplete + def removeAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ... + def removeAttributeNode(self, node: Attr) -> Attr: ... + removeAttributeNodeNS = removeAttributeNode def hasAttribute(self, name: str) -> bool: ... - def hasAttributeNS(self, namespaceURI: str, localName) -> bool: ... + def hasAttributeNS(self, namespaceURI: str | None, localName: str) -> bool: ... def getElementsByTagName(self, name: str) -> NodeList[Element]: ... - def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ... + def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... def hasAttributes(self) -> bool: ... - def setIdAttribute(self, name) -> None: ... - def setIdAttributeNS(self, namespaceURI: str, localName) -> None: ... - def setIdAttributeNode(self, idAttr) -> None: ... - @property - def attributes(self) -> NamedNodeMap: ... + def setIdAttribute(self, name: str) -> None: ... + def setIdAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ... + def setIdAttributeNode(self, idAttr: Attr) -> None: ... + def insertBefore( # type: ignore[override] + self, newChild: _ElementChildrenPlusFragment, refChild: _ElementChildren | None + ) -> _ElementChildrenPlusFragment: ... + def appendChild(self, node: _ElementChildrenPlusFragment) -> _ElementChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild( + self, newChild: DocumentFragment, oldChild: _ElementChildrenVar + ) -> _ElementChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _ElementChildren, oldChild: _ElementChildrenVar) -> _ElementChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _ElementChildrenVar) -> _ElementChildrenVar: ... # type: ignore[override] class Childless: - attributes: Incomplete - childNodes: Incomplete - firstChild: Incomplete - lastChild: Incomplete - def appendChild(self, node) -> NoReturn: ... - def hasChildNodes(self) -> bool: ... - def insertBefore(self, newChild, refChild) -> NoReturn: ... - def removeChild(self, oldChild) -> NoReturn: ... + attributes: None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + def appendChild(self, node: _NodesThatAreChildren | DocumentFragment) -> NoReturn: ... + def hasChildNodes(self) -> Literal[False]: ... + def insertBefore( + self, newChild: _NodesThatAreChildren | DocumentFragment, refChild: _NodesThatAreChildren | None + ) -> NoReturn: ... + def removeChild(self, oldChild: _NodesThatAreChildren) -> NoReturn: ... def normalize(self) -> None: ... - def replaceChild(self, newChild, oldChild) -> NoReturn: ... + def replaceChild(self, newChild: _NodesThatAreChildren | DocumentFragment, oldChild: _NodesThatAreChildren) -> NoReturn: ... class ProcessingInstruction(Childless, Node): - nodeType: int - target: Incomplete - data: Incomplete - def __init__(self, target, data) -> None: ... - nodeValue: Incomplete - nodeName: Incomplete + nodeType: ClassVar[Literal[7]] + nodeName: str # same as ProcessingInstruction.target + nodeValue: str # same as ProcessingInstruction.data + attributes: None + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + target: str + data: str + + def __init__(self, target: str, data: str) -> None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CharacterData(Childless, Node): - ownerDocument: Incomplete - previousSibling: Incomplete + nodeValue: str + attributes: None + + childNodes: EmptyNodeList + nextSibling: _NodesThatAreChildren | None + previousSibling: _NodesThatAreChildren | None + + @property + def localName(self) -> None: ... + + ownerDocument: Document | None + data: str + def __init__(self) -> None: ... + @property + def length(self) -> int: ... def __len__(self) -> int: ... - data: str - nodeValue: Incomplete def substringData(self, offset: int, count: int) -> str: ... def appendData(self, arg: str) -> None: ... def insertData(self, offset: int, arg: str) -> None: ... def deleteData(self, offset: int, count: int) -> None: ... def replaceData(self, offset: int, count: int, arg: str) -> None: ... - @property - def length(self) -> int: ... class Text(CharacterData): - nodeType: int - nodeName: str - attributes: Incomplete - data: Incomplete + nodeType: ClassVar[Literal[3]] + nodeName: Literal["#text"] + nodeValue: str # same as CharacterData.data, the content of the text node + attributes: None + + parentNode: Attr | Element | DocumentFragment | None + nextSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None + previousSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + data: str def splitText(self, offset: int) -> Self: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... - def replaceWholeText(self, content) -> Self | None: ... + def replaceWholeText(self, content: str) -> Self | None: ... @property def isWhitespaceInElementContent(self) -> bool: ... @property def wholeText(self) -> str: ... class Comment(CharacterData): - nodeType: int - nodeName: str - def __init__(self, data) -> None: ... + nodeType: ClassVar[Literal[8]] + nodeName: Literal["#comment"] + nodeValue: str # same as CharacterData.data, the content of the comment + attributes: None + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + def __init__(self, data: str) -> None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CDATASection(Text): - nodeType: int - nodeName: str + nodeType: ClassVar[Literal[4]] # type: ignore[assignment] + nodeName: Literal["#cdata-section"] # type: ignore[assignment] + nodeValue: str # same as CharacterData.data, the content of the CDATA Section + attributes: None + + parentNode: Element | DocumentFragment | None + nextSibling: _DocumentFragmentChildren | _ElementChildren | None + previousSibling: _DocumentFragmentChildren | _ElementChildren | None + def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... -class ReadOnlySequentialNamedNodeMap: - def __init__(self, seq=()) -> None: ... +class ReadOnlySequentialNamedNodeMap(Generic[_N]): + def __init__(self, seq: Sequence[_N] = ()) -> None: ... def __len__(self) -> int: ... - def getNamedItem(self, name): ... - def getNamedItemNS(self, namespaceURI: str, localName): ... - def __getitem__(self, name_or_tuple): ... - def item(self, index): ... - def removeNamedItem(self, name) -> None: ... - def removeNamedItemNS(self, namespaceURI: str, localName) -> None: ... - def setNamedItem(self, node) -> None: ... - def setNamedItemNS(self, node) -> None: ... + def getNamedItem(self, name: str) -> _N | None: ... + def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> _N | None: ... + def __getitem__(self, name_or_tuple: str | _NSName) -> _N | None: ... + def item(self, index: int) -> _N | None: ... + def removeNamedItem(self, name: str) -> NoReturn: ... + def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> NoReturn: ... + def setNamedItem(self, node: Node) -> NoReturn: ... + def setNamedItemNS(self, node: Node) -> NoReturn: ... @property def length(self) -> int: ... @@ -295,38 +509,85 @@ class Identified: systemId: str | None class DocumentType(Identified, Childless, Node): - nodeType: int - nodeValue: Incomplete - name: Incomplete - internalSubset: Incomplete - entities: Incomplete - notations: Incomplete - nodeName: Incomplete - def __init__(self, qualifiedName: str) -> None: ... - def cloneNode(self, deep): ... + nodeType: ClassVar[Literal[10]] + nodeName: str | None # same as DocumentType.name + nodeValue: None + attributes: None + + parentNode: Document | None + nextSibling: _DocumentChildren | None + previousSibling: _DocumentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + name: str | None + internalSubset: str | None + entities: ReadOnlySequentialNamedNodeMap[Entity] + notations: ReadOnlySequentialNamedNodeMap[Notation] + + def __init__(self, qualifiedName: str | None) -> None: ... + def cloneNode(self, deep: bool) -> DocumentType | None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class Entity(Identified, Node): - attributes: Incomplete - nodeType: int - nodeValue: Incomplete - actualEncoding: Incomplete - encoding: Incomplete - version: Incomplete - nodeName: Incomplete - notationName: Incomplete - childNodes: Incomplete - def __init__(self, name, publicId, systemId, notation) -> None: ... - def appendChild(self, newChild) -> NoReturn: ... - def insertBefore(self, newChild, refChild) -> NoReturn: ... - def removeChild(self, oldChild) -> NoReturn: ... - def replaceChild(self, newChild, oldChild) -> NoReturn: ... + nodeType: ClassVar[Literal[6]] + nodeName: str # entity name + nodeValue: None + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_EntityChildren] + @property + def firstChild(self) -> _EntityChildren | None: ... + @property + def lastChild(self) -> _EntityChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + actualEncoding: str | None + encoding: str | None + version: str | None + notationName: str | None + + def __init__(self, name: str, publicId: str | None, systemId: str | None, notation: str | None) -> None: ... + def appendChild(self, newChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] + def insertBefore(self, newChild: _EntityChildren, refChild: _EntityChildren | None) -> NoReturn: ... # type: ignore[override] + def removeChild(self, oldChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] + def replaceChild(self, newChild: _EntityChildren, oldChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] class Notation(Identified, Childless, Node): - nodeType: int - nodeValue: Incomplete - nodeName: Incomplete - def __init__(self, name, publicId, systemId) -> None: ... + nodeType: ClassVar[Literal[12]] + nodeName: str # notation name + nodeValue: None + attributes: None + + parentNode: DocumentFragment | None + nextSibling: _DocumentFragmentChildren | None + previousSibling: _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + def __init__(self, name: str, publicId: str | None, systemId: str | None) -> None: ... class DOMImplementation(DOMImplementationLS): def hasFeature(self, feature: str, version: str | None) -> bool: ... @@ -335,53 +596,67 @@ class DOMImplementation(DOMImplementationLS): def getInterface(self, feature: str) -> Self | None: ... class ElementInfo: - tagName: Incomplete - def __init__(self, name) -> None: ... - def getAttributeType(self, aname): ... - def getAttributeTypeNS(self, namespaceURI: str, localName): ... - def isElementContent(self): ... - def isEmpty(self): ... - def isId(self, aname): ... - def isIdNS(self, namespaceURI: str, localName): ... + tagName: str + def __init__(self, name: str) -> None: ... + def getAttributeType(self, aname: str) -> TypeInfo: ... + def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ... + def isElementContent(self) -> bool: ... + def isEmpty(self) -> bool: ... + def isId(self, aname: str) -> bool: ... + def isIdNS(self, namespaceURI: str | None, localName: str) -> bool: ... + +_DocumentChildrenPlusFragment = TypeVar("_DocumentChildrenPlusFragment", bound=_DocumentChildren | DocumentFragment) class Document(Node, DocumentLS): - implementation: Incomplete - nodeType: int - nodeName: str - nodeValue: Incomplete - attributes: Incomplete - parentNode: Incomplete - previousSibling: Incomplete - nextSibling: Incomplete - actualEncoding: Incomplete + nodeType: ClassVar[Literal[9]] + nodeName: Literal["#document"] + nodeValue: None + attributes: None + + parentNode: None + previousSibling: None + nextSibling: None + childNodes: NodeList[_DocumentChildren] + @property + def firstChild(self) -> _DocumentChildren | None: ... + @property + def lastChild(self) -> _DocumentChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + implementation: DOMImplementation + actualEncoding: str | None encoding: str | None standalone: bool | None - version: Incomplete + version: str | None strictErrorChecking: bool - errorHandler: Incomplete - documentURI: Incomplete + errorHandler: _DOMErrorHandler | None + documentURI: str | None doctype: DocumentType | None - childNodes: Incomplete + documentElement: Element | None + def __init__(self) -> None: ... - def appendChild(self, node: _N) -> _N: ... - documentElement: Incomplete - def removeChild(self, oldChild): ... + def appendChild(self, node: _DocumentChildrenVar) -> _DocumentChildrenVar: ... # type: ignore[override] + def removeChild(self, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar: ... # type: ignore[override] def unlink(self) -> None: ... - def cloneNode(self, deep): ... + def cloneNode(self, deep: bool) -> Document | None: ... def createDocumentFragment(self) -> DocumentFragment: ... def createElement(self, tagName: str) -> Element: ... def createTextNode(self, data: str) -> Text: ... def createCDATASection(self, data: str) -> CDATASection: ... def createComment(self, data: str) -> Comment: ... - def createProcessingInstruction(self, target, data): ... - def createAttribute(self, qName) -> Attr: ... - def createElementNS(self, namespaceURI: str, qualifiedName: str): ... - def createAttributeNS(self, namespaceURI: str, qualifiedName: str) -> Attr: ... + def createProcessingInstruction(self, target: str, data: str) -> ProcessingInstruction: ... + def createAttribute(self, qName: str) -> Attr: ... + def createElementNS(self, namespaceURI: str | None, qualifiedName: str) -> Element: ... + def createAttributeNS(self, namespaceURI: str | None, qualifiedName: str) -> Attr: ... def getElementById(self, id: str) -> Element | None: ... def getElementsByTagName(self, name: str) -> NodeList[Element]: ... - def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ... + def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ... def isSupported(self, feature: str, version: str | None) -> bool: ... - def importNode(self, node, deep): ... + def importNode(self, node: _ImportableNodeVar, deep: bool) -> _ImportableNodeVar: ... if sys.version_info >= (3, 9): def writexml( self, @@ -402,4 +677,18 @@ class Document(Node, DocumentLS): encoding: Incomplete | None = None, ) -> None: ... - def renameNode(self, n, namespaceURI: str, name): ... + @overload + def renameNode(self, n: Element, namespaceURI: str, name: str) -> Element: ... + @overload + def renameNode(self, n: Attr, namespaceURI: str, name: str) -> Attr: ... + @overload + def renameNode(self, n: Element | Attr, namespaceURI: str, name: str) -> Element | Attr: ... + def insertBefore( + self, newChild: _DocumentChildrenPlusFragment, refChild: _DocumentChildren | None # type: ignore[override] + ) -> _DocumentChildrenPlusFragment: ... + @overload # type: ignore[override] + def replaceChild( + self, newChild: DocumentFragment, oldChild: _DocumentChildrenVar + ) -> _DocumentChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _DocumentChildren, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar | None: ... diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index 50250de5cb2f..d9458654c185 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,11 +1,12 @@ import sys -from _typeshed import Incomplete, SupportsRead -from collections.abc import Sequence -from typing import Final, Literal -from typing_extensions import TypeAlias -from xml.dom.minidom import Document, DOMImplementation, Element, Text +from _typeshed import Incomplete, Unused +from collections.abc import MutableSequence, Sequence +from typing import Final, Literal, NoReturn +from typing_extensions import Self, TypeAlias +from xml.dom.minidom import Comment, Document, DOMImplementation, Element, ProcessingInstruction, Text +from xml.sax import _SupportsReadClose from xml.sax.handler import ContentHandler -from xml.sax.xmlreader import XMLReader +from xml.sax.xmlreader import AttributesImpl, AttributesNSImpl, Locator, XMLReader START_ELEMENT: Final = "START_ELEMENT" END_ELEMENT: Final = "END_ELEMENT" @@ -16,79 +17,93 @@ PROCESSING_INSTRUCTION: Final = "PROCESSING_INSTRUCTION" IGNORABLE_WHITESPACE: Final = "IGNORABLE_WHITESPACE" CHARACTERS: Final = "CHARACTERS" +_NSName: TypeAlias = tuple[str | None, str] _DocumentFactory: TypeAlias = DOMImplementation | None -_Node: TypeAlias = Document | Element | Text -_Event: TypeAlias = tuple[ - Literal[ - Literal["START_ELEMENT"], - Literal["END_ELEMENT"], - Literal["COMMENT"], - Literal["START_DOCUMENT"], - Literal["END_DOCUMENT"], - Literal["PROCESSING_INSTRUCTION"], - Literal["IGNORABLE_WHITESPACE"], - Literal["CHARACTERS"], - ], - _Node, -] +_Event: TypeAlias = ( + tuple[Literal["START_ELEMENT"], Element] + | tuple[Literal["END_ELEMENT"], Element] + | tuple[Literal["COMMENT"], Comment] + | tuple[Literal["START_DOCUMENT"], Document] + | tuple[Literal["END_DOCUMENT"], Document] + | tuple[Literal["PROCESSING_INSTRUCTION"], ProcessingInstruction] + | tuple[Literal["IGNORABLE_WHITESPACE"], Text] + | tuple[Literal["CHARACTERS"], Text] +) class PullDOM(ContentHandler): document: Document | None documentFactory: _DocumentFactory - firstEvent: Incomplete - lastEvent: Incomplete - elementStack: Sequence[Incomplete] - pending_events: Sequence[Incomplete] + + # firstEvent is a list of length 2 + # firstEvent[0] is always None + # firstEvent[1] is None prior to any events, after which it's a + # list of length 2, where the first item is of type _Event + # and the second item is None. + firstEvent: list[Incomplete] + + # lastEvent is also a list of length 2. The second item is always None, + # and the first item is of type _Event + # This is a slight lie: The second item is sometimes temporarily what was just + # described for the type of lastEvent, after which lastEvent is always updated + # with `self.lastEvent = self.lastEvent[1]`. + lastEvent: list[Incomplete] + + elementStack: MutableSequence[Element | Document] + pending_events: ( + list[Sequence[tuple[Literal["COMMENT"], str] | tuple[Literal["PROCESSING_INSTRUCTION"], str, str] | None]] | None + ) def __init__(self, documentFactory: _DocumentFactory = None) -> None: ... - def pop(self) -> Element: ... - def setDocumentLocator(self, locator) -> None: ... - def startPrefixMapping(self, prefix, uri) -> None: ... - def endPrefixMapping(self, prefix) -> None: ... - def startElementNS(self, name, tagName, attrs) -> None: ... - def endElementNS(self, name, tagName) -> None: ... - def startElement(self, name, attrs) -> None: ... - def endElement(self, name) -> None: ... - def comment(self, s) -> None: ... - def processingInstruction(self, target, data) -> None: ... - def ignorableWhitespace(self, chars) -> None: ... - def characters(self, chars) -> None: ... + def pop(self) -> Element | Document: ... + def setDocumentLocator(self, locator: Locator) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ... + def endElementNS(self, name: _NSName, tagName: str | None) -> None: ... + def startElement(self, name: str, attrs: AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def comment(self, s: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def ignorableWhitespace(self, chars: str) -> None: ... + def characters(self, chars: str) -> None: ... def startDocument(self) -> None: ... - def buildDocument(self, uri, tagname): ... + def buildDocument(self, uri: str | None, tagname: str | None) -> Element: ... def endDocument(self) -> None: ... def clear(self) -> None: ... class ErrorHandler: - def warning(self, exception) -> None: ... - def error(self, exception) -> None: ... - def fatalError(self, exception) -> None: ... + def warning(self, exception: BaseException) -> None: ... + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... class DOMEventStream: - stream: SupportsRead[bytes] | SupportsRead[str] - parser: XMLReader + stream: _SupportsReadClose[bytes] | _SupportsReadClose[str] + parser: XMLReader # Set to none after .clear() is called bufsize: int - def __init__(self, stream: SupportsRead[bytes] | SupportsRead[str], parser: XMLReader, bufsize: int) -> None: ... - pulldom: Incomplete + pulldom: PullDOM + def __init__(self, stream: _SupportsReadClose[bytes] | _SupportsReadClose[str], parser: XMLReader, bufsize: int) -> None: ... if sys.version_info < (3, 11): - def __getitem__(self, pos): ... + def __getitem__(self, pos: Unused) -> _Event: ... - def __next__(self): ... - def __iter__(self): ... - def getEvent(self) -> _Event: ... - def expandNode(self, node: _Node) -> None: ... + def __next__(self) -> _Event: ... + def __iter__(self) -> Self: ... + def getEvent(self) -> _Event | None: ... + def expandNode(self, node: Document) -> None: ... def reset(self) -> None: ... def clear(self) -> None: ... class SAX2DOM(PullDOM): - def startElementNS(self, name, tagName, attrs) -> None: ... - def startElement(self, name, attrs) -> None: ... - def processingInstruction(self, target, data) -> None: ... - def ignorableWhitespace(self, chars) -> None: ... - def characters(self, chars) -> None: ... + def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ... + def startElement(self, name: str, attrs: AttributesImpl) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def ignorableWhitespace(self, chars: str) -> None: ... + def characters(self, chars: str) -> None: ... default_bufsize: int def parse( - stream_or_string: str | SupportsRead[bytes] | SupportsRead[str], parser: XMLReader | None = None, bufsize: int | None = None + stream_or_string: str | _SupportsReadClose[bytes] | _SupportsReadClose[str], + parser: XMLReader | None = None, + bufsize: int | None = None, ) -> DOMEventStream: ... def parseString(string: str, parser: XMLReader | None = None) -> DOMEventStream: ... diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index ab76d362e23f..6fb18bbc4eda 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -1,32 +1,9 @@ -from _typeshed import Incomplete, Unused +from _typeshed import SupportsRead from typing import Any, Literal, NoReturn -from typing_extensions import TypeAlias -from urllib.request import OpenerDirector -from xml.dom.expatbuilder import ExpatBuilder, ExpatBuilderNS -from xml.dom.minidom import Node +from xml.dom.minidom import Document, Node, _DOMErrorHandler __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"] -# UNKNOWN TYPES: -# - `Options.errorHandler`. -# The same as `_DOMBuilderErrorHandlerType`? -# Maybe `xml.sax.handler.ErrorHandler`? -# - Return type of DOMBuilder.getFeature(). -# We could get rid of the `Incomplete` if we knew more -# about `Options.errorHandler`. - -# ALIASES REPRESENTING MORE UNKNOWN TYPES: - -# probably the same as `Options.errorHandler`? -# Maybe `xml.sax.handler.ErrorHandler`? -_DOMBuilderErrorHandlerType: TypeAlias = Incomplete | None -# probably some kind of IO... -_DOMInputSourceCharacterStreamType: TypeAlias = Incomplete | None -# probably a string?? -_DOMInputSourceStringDataType: TypeAlias = Incomplete | None -# probably a string?? -_DOMInputSourceEncodingType: TypeAlias = Incomplete | None - class Options: namespaces: int namespace_declarations: bool @@ -45,37 +22,35 @@ class Options: charset_overrides_xml_encoding: bool infoset: bool supported_mediatypes_only: bool - errorHandler: Any | None - filter: DOMBuilderFilter | None # a guess, but seems likely + errorHandler: _DOMErrorHandler | None + filter: DOMBuilderFilter | None class DOMBuilder: - entityResolver: DOMEntityResolver | None # a guess, but seems likely - errorHandler: _DOMBuilderErrorHandlerType - filter: DOMBuilderFilter | None # a guess, but seems likely + entityResolver: DOMEntityResolver | None + errorHandler: _DOMErrorHandler | None + filter: DOMBuilderFilter | None ACTION_REPLACE: Literal[1] ACTION_APPEND_AS_CHILDREN: Literal[2] ACTION_INSERT_AFTER: Literal[3] ACTION_INSERT_BEFORE: Literal[4] + def __init__(self) -> None: ... def setFeature(self, name: str, state: int) -> None: ... def supportsFeature(self, name: str) -> bool: ... - def canSetFeature(self, name: str, state: int) -> bool: ... + def canSetFeature(self, name: str, state: Literal[1, 0]) -> bool: ... # getFeature could return any attribute from an instance of `Options` def getFeature(self, name: str) -> Any: ... - def parseURI(self, uri: str) -> ExpatBuilder | ExpatBuilderNS: ... - def parse(self, input: DOMInputSource) -> ExpatBuilder | ExpatBuilderNS: ... - # `input` and `cnode` argtypes for `parseWithContext` are unknowable - # as the function does nothing with them, and always raises an exception. - # But `input` is *probably* `DOMInputSource`? - def parseWithContext(self, input: Unused, cnode: Unused, action: Literal[1, 2, 3, 4]) -> NoReturn: ... + def parseURI(self, uri: str) -> Document: ... + def parse(self, input: DOMInputSource) -> Document: ... + def parseWithContext(self, input: DOMInputSource, cnode: Node, action: Literal[1, 2, 3, 4]) -> NoReturn: ... class DOMEntityResolver: def resolveEntity(self, publicId: str | None, systemId: str) -> DOMInputSource: ... class DOMInputSource: - byteStream: OpenerDirector | None - characterStream: _DOMInputSourceCharacterStreamType - stringData: _DOMInputSourceStringDataType - encoding: _DOMInputSourceEncodingType + byteStream: SupportsRead[bytes] | None + characterStream: SupportsRead[str] | None + stringData: str | None + encoding: str | None publicId: str | None systemId: str | None baseURI: str | None @@ -86,18 +61,14 @@ class DOMBuilderFilter: FILTER_SKIP: Literal[3] FILTER_INTERRUPT: Literal[4] whatToShow: int - def acceptNode(self, element: Unused) -> Literal[1]: ... - def startContainer(self, element: Unused) -> Literal[1]: ... + def acceptNode(self, element: Node) -> Literal[1, 2, 3, 4]: ... + def startContainer(self, element: Node) -> Literal[1, 2, 3, 4]: ... class DocumentLS: async_: bool def abort(self) -> NoReturn: ... - # `load()` and `loadXML()` always raise exceptions - # so the argtypes of `uri` and `source` are unknowable. - # `source` is *probably* `DOMInputSource`? - # `uri` is *probably* a str? (see DOMBuilder.parseURI()) - def load(self, uri: Unused) -> NoReturn: ... - def loadXML(self, source: Unused) -> NoReturn: ... + def load(self, uri: str) -> NoReturn: ... + def loadXML(self, source: str) -> NoReturn: ... def saveXML(self, snode: Node | None) -> str: ... class DOMImplementationLS: diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi index 5a15772ec2a9..10c305826453 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi @@ -1,9 +1,14 @@ import sys from _typeshed import FileDescriptorOrPath -from collections.abc import Callable -from typing import Final +from typing import Final, Literal, Protocol, overload from xml.etree.ElementTree import Element +class _Loader(Protocol): + @overload + def __call__(self, href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ... + @overload + def __call__(self, href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ... + XINCLUDE: Final[str] XINCLUDE_INCLUDE: Final[str] XINCLUDE_FALLBACK: Final[str] @@ -13,17 +18,15 @@ if sys.version_info >= (3, 9): class FatalIncludeError(SyntaxError): ... -def default_loader(href: FileDescriptorOrPath, parse: str, encoding: str | None = None) -> str | Element: ... +@overload +def default_loader(href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ... +@overload +def default_loader(href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ... -# TODO: loader is of type default_loader ie it takes a callable that has the -# same signature as default_loader. But default_loader has a keyword argument -# Which can't be represented using Callable... if sys.version_info >= (3, 9): - def include( - elem: Element, loader: Callable[..., str | Element] | None = None, base_url: str | None = None, max_depth: int | None = 6 - ) -> None: ... + def include(elem: Element, loader: _Loader | None = None, base_url: str | None = None, max_depth: int | None = 6) -> None: ... class LimitedRecursiveIncludeError(FatalIncludeError): ... else: - def include(elem: Element, loader: Callable[..., str | Element] | None = None) -> None: ... + def include(elem: Element, loader: _Loader | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi index c3f6207ea241..ebfb4f1ffbb9 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi @@ -1,6 +1,6 @@ -from collections.abc import Callable, Generator +from collections.abc import Callable, Generator, Iterable from re import Pattern -from typing import TypeVar +from typing import Any, Literal, TypeVar, overload from typing_extensions import TypeAlias from xml.etree.ElementTree import Element @@ -8,27 +8,34 @@ xpath_tokenizer_re: Pattern[str] _Token: TypeAlias = tuple[str, str] _Next: TypeAlias = Callable[[], _Token] -_Callback: TypeAlias = Callable[[_SelectorContext, list[Element]], Generator[Element, None, None]] +_Callback: TypeAlias = Callable[[_SelectorContext, Iterable[Element]], Generator[Element, None, None]] +_T = TypeVar("_T") def xpath_tokenizer(pattern: str, namespaces: dict[str, str] | None = None) -> Generator[_Token, None, None]: ... def get_parent_map(context: _SelectorContext) -> dict[Element, Element]: ... def prepare_child(next: _Next, token: _Token) -> _Callback: ... def prepare_star(next: _Next, token: _Token) -> _Callback: ... def prepare_self(next: _Next, token: _Token) -> _Callback: ... -def prepare_descendant(next: _Next, token: _Token) -> _Callback: ... +def prepare_descendant(next: _Next, token: _Token) -> _Callback | None: ... def prepare_parent(next: _Next, token: _Token) -> _Callback: ... -def prepare_predicate(next: _Next, token: _Token) -> _Callback: ... +def prepare_predicate(next: _Next, token: _Token) -> _Callback | None: ... -ops: dict[str, Callable[[_Next, _Token], _Callback]] +ops: dict[str, Callable[[_Next, _Token], _Callback | None]] class _SelectorContext: parent_map: dict[Element, Element] | None root: Element def __init__(self, root: Element) -> None: ... -_T = TypeVar("_T") - -def iterfind(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... -def find(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... -def findall(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... -def findtext(elem: Element, path: str, default: _T | None = None, namespaces: dict[str, str] | None = None) -> _T | str: ... +@overload +def iterfind( # type: ignore[overload-overlap] + elem: Element[Any], path: Literal[""], namespaces: dict[str, str] | None = None +) -> None: ... +@overload +def iterfind(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... +def find(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... +def findall(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... +@overload +def findtext(elem: Element[Any], path: str, default: None = None, namespaces: dict[str, str] | None = None) -> str | None: ... +@overload +def findtext(elem: Element[Any], path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 64ebbd3ee63f..4a9113868d7e 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -2,8 +2,9 @@ import sys from _collections_abc import dict_keys from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence -from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload +from typing import Any, Final, Generic, Literal, Protocol, SupportsIndex, TypeVar, overload, type_check_only from typing_extensions import TypeAlias, TypeGuard, deprecated +from xml.parsers.expat import XMLParserType __all__ = [ "C14NWriterTarget", @@ -78,13 +79,22 @@ def canonicalize( exclude_tags: Iterable[str] | None = None, ) -> None: ... -class Element: - tag: str +# The tag for Element can be set to the Comment or ProcessingInstruction +# functions defined in this module. _ElementCallable could be a recursive +# type, but defining it that way uncovered a bug in pytype. +_ElementCallable: TypeAlias = Callable[..., Element[Any]] +_CallableElement: TypeAlias = Element[_ElementCallable] + +_Tag = TypeVar("_Tag", default=str, bound=str | _ElementCallable) +_OtherTag = TypeVar("_OtherTag", default=str, bound=str | _ElementCallable) + +class Element(Generic[_Tag]): + tag: _Tag attrib: dict[str, str] text: str | None tail: str | None - def __init__(self, tag: str, attrib: dict[str, str] = ..., **extra: str) -> None: ... - def append(self, subelement: Element, /) -> None: ... + def __init__(self, tag: _Tag, attrib: dict[str, str] = {}, **extra: str) -> None: ... + def append(self, subelement: Element[Any], /) -> None: ... def clear(self) -> None: ... def extend(self, elements: Iterable[Element], /) -> None: ... def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... @@ -100,14 +110,17 @@ class Element: def insert(self, index: int, subelement: Element, /) -> None: ... def items(self) -> ItemsView[str, str]: ... def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ... + @overload + def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ... # type: ignore[overload-overlap] + @overload def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... def itertext(self) -> Generator[str, None, None]: ... def keys(self) -> dict_keys[str, str]: ... # makeelement returns the type of self in Python impl, but not in C impl - def makeelement(self, tag: str, attrib: dict[str, str], /) -> Element: ... + def makeelement(self, tag: _OtherTag, attrib: dict[str, str], /) -> Element[_OtherTag]: ... def remove(self, subelement: Element, /) -> None: ... def set(self, key: str, value: str, /) -> None: ... - def __copy__(self) -> Element: ... # returns the type of self in Python impl, but not in C impl + def __copy__(self) -> Element[_Tag]: ... # returns the type of self in Python impl, but not in C impl def __deepcopy__(self, memo: Any, /) -> Element: ... # Only exists in C impl def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... @overload @@ -130,8 +143,8 @@ class Element: def getiterator(self, tag: str | None = None) -> list[Element]: ... def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ... -def Comment(text: str | None = None) -> Element: ... -def ProcessingInstruction(target: str, text: str | None = None) -> Element: ... +def Comment(text: str | None = None) -> _CallableElement: ... +def ProcessingInstruction(target: str, text: str | None = None) -> _CallableElement: ... PI = ProcessingInstruction @@ -145,9 +158,11 @@ class QName: def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... -class ElementTree: +_Root = TypeVar("_Root", Element, Element | None, default=Element | None) + +class ElementTree(Generic[_Root]): def __init__(self, element: Element | None = None, file: _FileRead | None = None) -> None: ... - def getroot(self) -> Element | Any: ... + def getroot(self) -> _Root: ... def parse(self, source: _FileRead, parser: XMLParser | None = None) -> Element: ... def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ... if sys.version_info < (3, 9): @@ -159,6 +174,9 @@ class ElementTree: @overload def findtext(self, path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ... def findall(self, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... + @overload + def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ... # type: ignore[overload-overlap] + @overload def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... def write( self, @@ -166,18 +184,20 @@ class ElementTree: encoding: str | None = None, xml_declaration: bool | None = None, default_namespace: str | None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, short_empty_elements: bool = True, ) -> None: ... def write_c14n(self, file: _FileWriteC14N) -> None: ... +HTML_EMPTY: set[str] + def register_namespace(prefix: str, uri: str) -> None: ... @overload def tostring( element: Element, encoding: None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -187,7 +207,7 @@ def tostring( def tostring( element: Element, encoding: Literal["unicode"], - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -197,7 +217,7 @@ def tostring( def tostring( element: Element, encoding: str, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -207,7 +227,7 @@ def tostring( def tostringlist( element: Element, encoding: None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -217,7 +237,7 @@ def tostringlist( def tostringlist( element: Element, encoding: Literal["unicode"], - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -227,21 +247,23 @@ def tostringlist( def tostringlist( element: Element, encoding: str, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, short_empty_elements: bool = True, ) -> list[Any]: ... -def dump(elem: Element) -> None: ... +def dump(elem: Element | ElementTree[Any]) -> None: ... if sys.version_info >= (3, 9): - def indent(tree: Element | ElementTree, space: str = " ", level: int = 0) -> None: ... + def indent(tree: Element | ElementTree[Any], space: str = " ", level: int = 0) -> None: ... -def parse(source: _FileRead, parser: XMLParser | None = None) -> ElementTree: ... +def parse(source: _FileRead, parser: XMLParser[Any] | None = None) -> ElementTree[Element]: ... -class _IterParseIterator(Iterator[tuple[str, Any]]): - def __next__(self) -> tuple[str, Any]: ... +# This class is defined inside the body of iterparse +@type_check_only +class _IterParseIterator(Iterator[tuple[str, Element]], Protocol): + def __next__(self) -> tuple[str, Element]: ... if sys.version_info >= (3, 13): def close(self) -> None: ... if sys.version_info >= (3, 11): @@ -249,13 +271,13 @@ class _IterParseIterator(Iterator[tuple[str, Any]]): def iterparse(source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None) -> _IterParseIterator: ... -class XMLPullParser: - def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser | None = None) -> None: ... +_EventQueue: TypeAlias = tuple[str] | tuple[str, tuple[str, str]] | tuple[str, None] + +class XMLPullParser(Generic[_E]): + def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser[_E] | None = None) -> None: ... def feed(self, data: str | ReadableBuffer) -> None: ... def close(self) -> None: ... - # Second element in the tuple could be `Element`, `tuple[str, str]` or `None`. - # Use `Any` to avoid false-positive errors. - def read_events(self) -> Iterator[tuple[str, Any]]: ... + def read_events(self) -> Iterator[_EventQueue | tuple[str, _E]]: ... def flush(self) -> None: ... def XML(text: str | ReadableBuffer, parser: XMLParser | None = None) -> Element: ... @@ -281,12 +303,12 @@ class TreeBuilder: # comment_factory can take None because passing None to Comment is not an error def __init__( self, - element_factory: _ElementFactory | None = ..., + element_factory: _ElementFactory | None = None, *, - comment_factory: Callable[[str | None], Element] | None = ..., - pi_factory: Callable[[str, str | None], Element] | None = ..., - insert_comments: bool = ..., - insert_pis: bool = ..., + comment_factory: Callable[[str | None], Element[Any]] | None = None, + pi_factory: Callable[[str, str | None], Element[Any]] | None = None, + insert_comments: bool = False, + insert_pis: bool = False, ) -> None: ... insert_comments: bool insert_pis: bool @@ -298,8 +320,8 @@ class TreeBuilder: def start(self, tag: Any, attrs: dict[Any, Any], /) -> Element: ... def end(self, tag: str, /) -> Element: ... # These two methods have pos-only parameters in the C implementation - def comment(self, text: str | None, /) -> Element: ... - def pi(self, target: str, text: str | None = None, /) -> Element: ... + def comment(self, text: str | None, /) -> Element[Any]: ... + def pi(self, target: str, text: str | None = None, /) -> Element[Any]: ... class C14NWriterTarget: def __init__( @@ -321,13 +343,33 @@ class C14NWriterTarget: def comment(self, text: str) -> None: ... def pi(self, target: str, data: str) -> None: ... -class XMLParser: - parser: Any - target: Any +# The target type is tricky, because the implementation doesn't +# require any particular attribute to be present. This documents the attributes +# that can be present, but uncommenting any of them would require them. +class _Target(Protocol): + # start: Callable[str, dict[str, str], Any] | None + # end: Callable[[str], Any] | None + # start_ns: Callable[[str, str], Any] | None + # end_ns: Callable[[str], Any] | None + # data: Callable[[str], Any] | None + # comment: Callable[[str], Any] + # pi: Callable[[str, str], Any] | None + # close: Callable[[], Any] | None + ... + +_E = TypeVar("_E", default=Element) + +# This is generic because the return type of close() depends on the target. +# The default target is TreeBuilder, which returns Element. +# C14NWriterTarget does not implement a close method, so using it results +# in a type of XMLParser[None]. +class XMLParser(Generic[_E]): + parser: XMLParserType + target: _Target # TODO-what is entity used for??? - entity: Any + entity: dict[str, str] version: str - def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... - def close(self) -> Any: ... + def __init__(self, *, target: _Target | None = None, encoding: str | None = None) -> None: ... + def close(self) -> _E: ... def feed(self, data: str | ReadableBuffer, /) -> None: ... def flush(self) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi index 8a437a971f13..e9cc8856a9c8 100644 --- a/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi +++ b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi @@ -4,15 +4,15 @@ from xml.sax.xmlreader import Locator class SAXException(Exception): def __init__(self, msg: str, exception: Exception | None = None) -> None: ... def getMessage(self) -> str: ... - def getException(self) -> Exception: ... + def getException(self) -> Exception | None: ... def __getitem__(self, ix: object) -> NoReturn: ... class SAXParseException(SAXException): def __init__(self, msg: str, exception: Exception | None, locator: Locator) -> None: ... - def getColumnNumber(self) -> int: ... - def getLineNumber(self) -> int: ... - def getPublicId(self): ... - def getSystemId(self): ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int | None: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... class SAXNotRecognizedException(SAXException): ... class SAXNotSupportedException(SAXException): ... diff --git a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi index 0f7bda5872c0..6a68f52f0e99 100644 --- a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi @@ -1,53 +1,82 @@ import sys -from _typeshed import Unused -from xml.sax import xmlreader +from _typeshed import ReadableBuffer +from collections.abc import Mapping +from typing import Any, Literal, overload +from typing_extensions import TypeAlias +from xml.sax import _Source, xmlreader +from xml.sax.handler import _ContentHandlerProtocol + +if sys.version_info >= (3, 10): + from xml.sax.handler import LexicalHandler + +_BoolType: TypeAlias = Literal[0, 1] | bool version: str AttributesImpl = xmlreader.AttributesImpl AttributesNSImpl = xmlreader.AttributesNSImpl -class _ClosedParser: ... +class _ClosedParser: + ErrorColumnNumber: int + ErrorLineNumber: int class ExpatLocator(xmlreader.Locator): def __init__(self, parser: ExpatParser) -> None: ... - def getColumnNumber(self) -> int: ... + def getColumnNumber(self) -> int | None: ... def getLineNumber(self) -> int: ... - def getPublicId(self): ... - def getSystemId(self): ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): - def __init__(self, namespaceHandling: int = 0, bufsize: int = 65516) -> None: ... - def parse(self, source) -> None: ... - def prepareParser(self, source) -> None: ... - def setContentHandler(self, handler) -> None: ... - def getFeature(self, name: str): ... - def setFeature(self, name: str, state) -> None: ... - def getProperty(self, name: str): ... - def setProperty(self, name: str, value) -> None: ... + def __init__(self, namespaceHandling: _BoolType = 0, bufsize: int = 65516) -> None: ... + def parse(self, source: xmlreader.InputSource | _Source) -> None: ... + def prepareParser(self, source: xmlreader.InputSource) -> None: ... + def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ... + def getFeature(self, name: str) -> _BoolType: ... + def setFeature(self, name: str, state: _BoolType) -> None: ... + if sys.version_info >= (3, 10): + @overload + def getProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"]) -> LexicalHandler | None: ... + + @overload + def getProperty(self, name: Literal["http://www.python.org/sax/properties/interning-dict"]) -> dict[str, Any] | None: ... + @overload + def getProperty(self, name: Literal["http://xml.org/sax/properties/xml-string"]) -> bytes | None: ... + @overload + def getProperty(self, name: str) -> object: ... + if sys.version_info >= (3, 10): + @overload + def setProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"], value: LexicalHandler) -> None: ... + + @overload + def setProperty( + self, name: Literal["http://www.python.org/sax/properties/interning-dict"], value: dict[str, Any] + ) -> None: ... + @overload + def setProperty(self, name: str, value: object) -> None: ... if sys.version_info >= (3, 9): - def feed(self, data, isFinal: bool = False) -> None: ... + def feed(self, data: str | ReadableBuffer, isFinal: bool = False) -> None: ... else: - def feed(self, data, isFinal: int = 0) -> None: ... + def feed(self, data: str | ReadableBuffer, isFinal: _BoolType = 0) -> None: ... def flush(self) -> None: ... def close(self) -> None: ... def reset(self) -> None: ... def getColumnNumber(self) -> int | None: ... def getLineNumber(self) -> int: ... - def getPublicId(self): ... - def getSystemId(self): ... - def start_element(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... + def start_element(self, name: str, attrs: Mapping[str, str]) -> None: ... def end_element(self, name: str) -> None: ... - def start_element_ns(self, name: str, attrs) -> None: ... + def start_element_ns(self, name: str, attrs: Mapping[str, str]) -> None: ... def end_element_ns(self, name: str) -> None: ... def processing_instruction(self, target: str, data: str) -> None: ... def character_data(self, data: str) -> None: ... def start_namespace_decl(self, prefix: str | None, uri: str) -> None: ... def end_namespace_decl(self, prefix: str | None) -> None: ... - def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: Unused) -> None: ... - def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name) -> None: ... - def notation_decl(self, name, base, sysid, pubid) -> None: ... - def external_entity_ref(self, context, base, sysid, pubid): ... + def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: bool) -> None: ... + def unparsed_entity_decl(self, name: str, base: str | None, sysid: str, pubid: str | None, notation_name: str) -> None: ... + def notation_decl(self, name: str, base: str | None, sysid: str, pubid: str | None) -> None: ... + def external_entity_ref(self, context: str, base: str | None, sysid: str, pubid: str | None) -> int: ... def skipped_entity_handler(self, name: str, is_pe: bool) -> None: ... def create_parser(namespaceHandling: int = 0, bufsize: int = 65516) -> ExpatParser: ... diff --git a/mypy/typeshed/stdlib/xml/sax/handler.pyi b/mypy/typeshed/stdlib/xml/sax/handler.pyi index 7b7c69048efd..550911734596 100644 --- a/mypy/typeshed/stdlib/xml/sax/handler.pyi +++ b/mypy/typeshed/stdlib/xml/sax/handler.pyi @@ -1,14 +1,36 @@ import sys -from typing import NoReturn +from typing import Literal, NoReturn, Protocol, type_check_only from xml.sax import xmlreader version: str +@type_check_only +class _ErrorHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... + def warning(self, exception: BaseException) -> None: ... + class ErrorHandler: def error(self, exception: BaseException) -> NoReturn: ... def fatalError(self, exception: BaseException) -> NoReturn: ... def warning(self, exception: BaseException) -> None: ... +@type_check_only +class _ContentHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... + def startDocument(self) -> None: ... + def endDocument(self) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... + def characters(self, content: str) -> None: ... + def ignorableWhitespace(self, whitespace: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def skippedEntity(self, name: str) -> None: ... + class ContentHandler: def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... def startDocument(self) -> None: ... @@ -17,19 +39,28 @@ class ContentHandler: def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, whitespace: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... def skippedEntity(self, name: str) -> None: ... +@type_check_only +class _DTDHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + class DTDHandler: - def notationDecl(self, name, publicId, systemId): ... - def unparsedEntityDecl(self, name, publicId, systemId, ndata): ... + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + +@type_check_only +class _EntityResolverProtocol(Protocol): # noqa: Y046 # Protocol is not used + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... class EntityResolver: - def resolveEntity(self, publicId, systemId): ... + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... feature_namespaces: str feature_namespace_prefixes: str @@ -38,18 +69,18 @@ feature_validation: str feature_external_ges: str feature_external_pes: str all_features: list[str] -property_lexical_handler: str -property_declaration_handler: str -property_dom_node: str -property_xml_string: str -property_encoding: str -property_interning_dict: str +property_lexical_handler: Literal["http://xml.org/sax/properties/lexical-handler"] +property_declaration_handler: Literal["http://xml.org/sax/properties/declaration-handler"] +property_dom_node: Literal["http://xml.org/sax/properties/dom-node"] +property_xml_string: Literal["http://xml.org/sax/properties/xml-string"] +property_encoding: Literal["http://www.python.org/sax/properties/encoding"] +property_interning_dict: Literal["http://www.python.org/sax/properties/interning-dict"] all_properties: list[str] if sys.version_info >= (3, 10): class LexicalHandler: - def comment(self, content: str) -> object: ... - def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> object: ... - def endDTD(self) -> object: ... - def startCDATA(self) -> object: ... - def endCDATA(self) -> object: ... + def comment(self, content: str) -> None: ... + def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> None: ... + def endDTD(self) -> None: ... + def startCDATA(self) -> None: ... + def endCDATA(self) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi index 528f35963947..a29588faae2a 100644 --- a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi +++ b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi @@ -2,6 +2,7 @@ from _typeshed import SupportsWrite from codecs import StreamReaderWriter, StreamWriter from collections.abc import Mapping from io import RawIOBase, TextIOBase +from typing import Literal, NoReturn from xml.sax import _Source, handler, xmlreader def escape(data: str, entities: Mapping[str, str] = {}) -> str: ... @@ -15,23 +16,26 @@ class XMLGenerator(handler.ContentHandler): encoding: str = "iso-8859-1", short_empty_elements: bool = False, ) -> None: ... + def _qname(self, name: tuple[str | None, str]) -> str: ... def startDocument(self) -> None: ... def endDocument(self) -> None: ... def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, content: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... class XMLFilterBase(xmlreader.XMLReader): def __init__(self, parent: xmlreader.XMLReader | None = None) -> None: ... - def error(self, exception): ... - def fatalError(self, exception): ... - def warning(self, exception): ... + # ErrorHandler methods + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... + def warning(self, exception: BaseException) -> None: ... + # ContentHandler methods def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... def startDocument(self) -> None: ... def endDocument(self) -> None: ... @@ -39,22 +43,26 @@ class XMLFilterBase(xmlreader.XMLReader): def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, chars: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... def skippedEntity(self, name: str) -> None: ... - def notationDecl(self, name, publicId, systemId): ... - def unparsedEntityDecl(self, name, publicId, systemId, ndata): ... - def resolveEntity(self, publicId, systemId): ... - def parse(self, source: _Source) -> None: ... - def setLocale(self, locale): ... - def getFeature(self, name: str) -> object: ... - def setFeature(self, name: str, state: object) -> None: ... + # DTDHandler methods + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + # EntityResolver methods + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... + # XMLReader methods + def parse(self, source: xmlreader.InputSource | _Source) -> None: ... + def setLocale(self, locale: str) -> None: ... + def getFeature(self, name: str) -> Literal[1, 0] | bool: ... + def setFeature(self, name: str, state: Literal[1, 0] | bool) -> None: ... def getProperty(self, name: str) -> object: ... def setProperty(self, name: str, value: object) -> None: ... - def getParent(self) -> xmlreader.XMLReader: ... + # XMLFilter methods + def getParent(self) -> xmlreader.XMLReader | None: ... def setParent(self, parent: xmlreader.XMLReader) -> None: ... -def prepare_input_source(source, base=""): ... +def prepare_input_source(source: xmlreader.InputSource | _Source, base: str = "") -> xmlreader.InputSource: ... diff --git a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi index 2ccbc95bbef0..e7d04ddeadb8 100644 --- a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi @@ -1,87 +1,90 @@ +from _typeshed import ReadableBuffer from collections.abc import Mapping -from typing import overload +from typing import Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias -from xml.sax.handler import ContentHandler, DTDHandler, EntityResolver, ErrorHandler +from xml.sax import _Source, _SupportsReadClose +from xml.sax.handler import _ContentHandlerProtocol, _DTDHandlerProtocol, _EntityResolverProtocol, _ErrorHandlerProtocol class XMLReader: - def parse(self, source): ... - def getContentHandler(self) -> ContentHandler: ... - def setContentHandler(self, handler: ContentHandler) -> None: ... - def getDTDHandler(self) -> DTDHandler: ... - def setDTDHandler(self, handler: DTDHandler) -> None: ... - def getEntityResolver(self) -> EntityResolver: ... - def setEntityResolver(self, resolver: EntityResolver) -> None: ... - def getErrorHandler(self) -> ErrorHandler: ... - def setErrorHandler(self, handler: ErrorHandler) -> None: ... - def setLocale(self, locale): ... - def getFeature(self, name: str) -> object: ... - def setFeature(self, name: str, state: object) -> None: ... + def parse(self, source: InputSource | _Source) -> None: ... + def getContentHandler(self) -> _ContentHandlerProtocol: ... + def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ... + def getDTDHandler(self) -> _DTDHandlerProtocol: ... + def setDTDHandler(self, handler: _DTDHandlerProtocol) -> None: ... + def getEntityResolver(self) -> _EntityResolverProtocol: ... + def setEntityResolver(self, resolver: _EntityResolverProtocol) -> None: ... + def getErrorHandler(self) -> _ErrorHandlerProtocol: ... + def setErrorHandler(self, handler: _ErrorHandlerProtocol) -> None: ... + def setLocale(self, locale: str) -> None: ... + def getFeature(self, name: str) -> Literal[0, 1] | bool: ... + def setFeature(self, name: str, state: Literal[0, 1] | bool) -> None: ... def getProperty(self, name: str) -> object: ... def setProperty(self, name: str, value: object) -> None: ... class IncrementalParser(XMLReader): def __init__(self, bufsize: int = 65536) -> None: ... - def parse(self, source): ... - def feed(self, data): ... - def prepareParser(self, source): ... - def close(self): ... - def reset(self): ... + def parse(self, source: InputSource | _Source) -> None: ... + def feed(self, data: str | ReadableBuffer) -> None: ... + def prepareParser(self, source: InputSource) -> None: ... + def close(self) -> None: ... + def reset(self) -> None: ... class Locator: - def getColumnNumber(self): ... - def getLineNumber(self): ... - def getPublicId(self): ... - def getSystemId(self): ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int | None: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... class InputSource: def __init__(self, system_id: str | None = None) -> None: ... - def setPublicId(self, public_id): ... - def getPublicId(self): ... - def setSystemId(self, system_id): ... - def getSystemId(self): ... - def setEncoding(self, encoding): ... - def getEncoding(self): ... - def setByteStream(self, bytefile): ... - def getByteStream(self): ... - def setCharacterStream(self, charfile): ... - def getCharacterStream(self): ... + def setPublicId(self, public_id: str | None) -> None: ... + def getPublicId(self) -> str | None: ... + def setSystemId(self, system_id: str | None) -> None: ... + def getSystemId(self) -> str | None: ... + def setEncoding(self, encoding: str | None) -> None: ... + def getEncoding(self) -> str | None: ... + def setByteStream(self, bytefile: _SupportsReadClose[bytes] | None) -> None: ... + def getByteStream(self) -> _SupportsReadClose[bytes] | None: ... + def setCharacterStream(self, charfile: _SupportsReadClose[str] | None) -> None: ... + def getCharacterStream(self) -> _SupportsReadClose[str] | None: ... -class AttributesImpl: - def __init__(self, attrs: Mapping[str, str]) -> None: ... +_AttrKey = TypeVar("_AttrKey", default=str) + +class AttributesImpl(Generic[_AttrKey]): + def __init__(self, attrs: Mapping[_AttrKey, str]) -> None: ... def getLength(self) -> int: ... def getType(self, name: str) -> str: ... - def getValue(self, name: str) -> str: ... + def getValue(self, name: _AttrKey) -> str: ... def getValueByQName(self, name: str) -> str: ... - def getNameByQName(self, name: str) -> str: ... - def getQNameByName(self, name: str) -> str: ... - def getNames(self) -> list[str]: ... + def getNameByQName(self, name: str) -> _AttrKey: ... + def getQNameByName(self, name: _AttrKey) -> str: ... + def getNames(self) -> list[_AttrKey]: ... def getQNames(self) -> list[str]: ... def __len__(self) -> int: ... - def __getitem__(self, name: str) -> str: ... - def keys(self) -> list[str]: ... - def __contains__(self, name: str) -> bool: ... + def __getitem__(self, name: _AttrKey) -> str: ... + def keys(self) -> list[_AttrKey]: ... + def __contains__(self, name: _AttrKey) -> bool: ... @overload - def get(self, name: str, alternative: None = None) -> str | None: ... + def get(self, name: _AttrKey, alternative: None = None) -> str | None: ... @overload - def get(self, name: str, alternative: str) -> str: ... + def get(self, name: _AttrKey, alternative: str) -> str: ... def copy(self) -> Self: ... - def items(self) -> list[tuple[str, str]]: ... + def items(self) -> list[tuple[_AttrKey, str]]: ... def values(self) -> list[str]: ... _NSName: TypeAlias = tuple[str | None, str] -class AttributesNSImpl(AttributesImpl): +class AttributesNSImpl(AttributesImpl[_NSName]): def __init__(self, attrs: Mapping[_NSName, str], qnames: Mapping[_NSName, str]) -> None: ... - def getType(self, name: _NSName) -> str: ... # type: ignore[override] - def getValue(self, name: _NSName) -> str: ... # type: ignore[override] - def getNameByQName(self, name: str) -> _NSName: ... # type: ignore[override] - def getQNameByName(self, name: _NSName) -> str: ... # type: ignore[override] - def getNames(self) -> list[_NSName]: ... # type: ignore[override] - def __getitem__(self, name: _NSName) -> str: ... # type: ignore[override] - def keys(self) -> list[_NSName]: ... # type: ignore[override] - def __contains__(self, name: _NSName) -> bool: ... # type: ignore[override] - @overload # type: ignore[override] + def getValue(self, name: _NSName) -> str: ... + def getNameByQName(self, name: str) -> _NSName: ... + def getQNameByName(self, name: _NSName) -> str: ... + def getNames(self) -> list[_NSName]: ... + def __getitem__(self, name: _NSName) -> str: ... + def keys(self) -> list[_NSName]: ... + def __contains__(self, name: _NSName) -> bool: ... + @overload def get(self, name: _NSName, alternative: None = None) -> str | None: ... @overload def get(self, name: _NSName, alternative: str) -> str: ... - def items(self) -> list[tuple[_NSName, str]]: ... # type: ignore[override] + def items(self) -> list[tuple[_NSName, str]]: ... From b1ac0280f1175e81e90eb9d1650f0ebac85daa11 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Sat, 1 Mar 2025 19:50:21 -0800 Subject: [PATCH 1181/1617] stubtest: ignore setattr and delattr inherited from object (#18325) `__setattr__` and `__delattr__` from object are special cased by type checkers, so defining them on an inheriting class, even with the same signature, has a different meaning. This one is very similar to https://github.com/python/mypy/pull/18314 --- mypy/stubtest.py | 24 ++++++++++++++++++++---- mypy/test/teststubtest.py | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 41b58cbbb636..89af8e465464 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -574,9 +574,23 @@ def verify_typeinfo( # If it came from the metaclass, consider the runtime_attr to be MISSING # for a more accurate message - if runtime_attr is not MISSING and type(runtime) is not runtime: - if getattr(runtime_attr, "__objclass__", None) is type(runtime): - runtime_attr = MISSING + if ( + runtime_attr is not MISSING + and type(runtime) is not runtime + and getattr(runtime_attr, "__objclass__", None) is type(runtime) + ): + runtime_attr = MISSING + + # __setattr__ and __delattr__ on object are a special case, + # so if we only have these methods inherited from there, pretend that + # we don't have them. See python/typeshed#7385. + if ( + entry in ("__setattr__", "__delattr__") + and runtime_attr is not MISSING + and runtime is not object + and getattr(runtime_attr, "__objclass__", None) is object + ): + runtime_attr = MISSING # Do not error for an object missing from the stub # If the runtime object is a types.WrapperDescriptorType object @@ -1092,9 +1106,11 @@ def verify_funcitem( @verify.register(Missing) -def verify_none( +def verify_missing( stub: Missing, runtime: MaybeMissing[Any], object_path: list[str] ) -> Iterator[Error]: + if runtime is MISSING: + return yield Error(object_path, "is not present in stub", stub, runtime) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 101b6f65c45a..099e7605eea2 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1496,6 +1496,24 @@ def __call__(*args, **kwds): ... runtime="class ClassWithMetaclassOverride: ...", error="ClassWithMetaclassOverride.__call__", ) + # Test that we ignore object.__setattr__ and object.__delattr__ inheritance + yield Case( + stub=""" + from typing import Any + class FakeSetattrClass: + def __setattr__(self, name: str, value: Any, /) -> None: ... + """, + runtime="class FakeSetattrClass: ...", + error="FakeSetattrClass.__setattr__", + ) + yield Case( + stub=""" + class FakeDelattrClass: + def __delattr__(self, name: str, /) -> None: ... + """, + runtime="class FakeDelattrClass: ...", + error="FakeDelattrClass.__delattr__", + ) @collect_cases def test_missing_no_runtime_all(self) -> Iterator[Case]: From efc045e010bef3ed53d3ad20793bea3f582d3859 Mon Sep 17 00:00:00 2001 From: bzoracler <50305397+bzoracler@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:55:31 +1300 Subject: [PATCH 1182/1617] Restrict type of `AssignmentExpr.target` to `NameExpr` (#18714) Assignment expression targets can only be identifiers. From the [grammar](https://docs.python.org/3.14/reference/grammar.html): ``` assignment_expression: | NAME ':=' ~ expression ``` This [corresponds](https://github.com/python/typeshed/blob/ac8f2632ec37bb4a82ade0906e6ce9bdb33883d3/stdlib/ast.pyi#L834-L837) to the standard library AST node's `target` type: ```python class NamedExpr(expr): if sys.version_info >= (3, 10): __match_args__ = ("target", "value") target: Name ``` --- mypy/nodes.py | 2 +- mypy/treetransform.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 6487ee4b745c..10377eec07ba 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2075,7 +2075,7 @@ class AssignmentExpr(Expression): __match_args__ = ("target", "value") - def __init__(self, target: Expression, value: Expression) -> None: + def __init__(self, target: NameExpr, value: Expression) -> None: super().__init__() self.target = target self.value = value diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 3e5a7ef3f2ca..0abf98a52336 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -559,7 +559,7 @@ def visit_super_expr(self, node: SuperExpr) -> SuperExpr: return new def visit_assignment_expr(self, node: AssignmentExpr) -> AssignmentExpr: - return AssignmentExpr(self.expr(node.target), self.expr(node.value)) + return AssignmentExpr(self.duplicate_name(node.target), self.expr(node.value)) def visit_unary_expr(self, node: UnaryExpr) -> UnaryExpr: new = UnaryExpr(node.op, self.expr(node.expr)) From a067d84dcbdc16348ea37cb959f96b5ca46f2e39 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:15:59 -0800 Subject: [PATCH 1183/1617] stubtest: better checking of runtime args with dunder names (#18756) Fixes #15302, fixes #14560. Linking #18343 --- mypy/stubtest.py | 9 +++++---- mypy/test/teststubtest.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 89af8e465464..a0f886106715 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -654,10 +654,10 @@ def _verify_arg_name( if is_dunder(function_name, exclude_special=True): return - def strip_prefix(s: str, prefix: str) -> str: - return s.removeprefix(prefix) - - if strip_prefix(stub_arg.variable.name, "__") == runtime_arg.name: + if ( + stub_arg.variable.name == runtime_arg.name + or stub_arg.variable.name.removeprefix("__") == runtime_arg.name + ): return nonspecific_names = {"object", "args"} @@ -948,6 +948,7 @@ def _verify_signature( if ( runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY and (stub_arg.pos_only or stub_arg.variable.name.startswith("__")) + and not runtime_arg.name.startswith("__") and stub_arg.variable.name.strip("_") != "self" and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods ): diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 099e7605eea2..492897d33a4a 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -339,6 +339,21 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass """, error=None, ) + yield Case( + stub="""def dunder_name(__x: int) -> None: ...""", + runtime="""def dunder_name(__x: int) -> None: ...""", + error=None, + ) + yield Case( + stub="""def dunder_name_posonly(__x: int, /) -> None: ...""", + runtime="""def dunder_name_posonly(__x: int) -> None: ...""", + error=None, + ) + yield Case( + stub="""def dunder_name_bad(x: int) -> None: ...""", + runtime="""def dunder_name_bad(__x: int) -> None: ...""", + error="dunder_name_bad", + ) @collect_cases def test_arg_kind(self) -> Iterator[Case]: From f339f2ca564447aad3d658b8cb11c101c6af4221 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 7 Mar 2025 21:32:11 -0500 Subject: [PATCH 1184/1617] handle arg=None in stubgenc (#18768) resolves #18757 --- mypy/stubgenc.py | 20 ++++++++++++-------- mypy/test/teststubgen.py | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index b5bb4f8f727b..c673ea929dfa 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -6,6 +6,7 @@ from __future__ import annotations +import enum import glob import importlib import inspect @@ -211,6 +212,9 @@ def __get__(self) -> None: # noqa: PLE0302 pass +_Missing = enum.Enum("_Missing", "VALUE") + + class InspectionStubGenerator(BaseStubGenerator): """Stub generator that does not parse code. @@ -310,12 +314,12 @@ def get_annotation(key: str) -> str | None: # Add the arguments to the signature def add_args( - args: list[str], get_default_value: Callable[[int, str], object | None] + args: list[str], get_default_value: Callable[[int, str], object | _Missing] ) -> None: for i, arg in enumerate(args): # Check if the argument has a default value default_value = get_default_value(i, arg) - if default_value is not None: + if default_value is not _Missing.VALUE: if arg in annotations: argtype = annotations[arg] else: @@ -330,11 +334,11 @@ def add_args( else: arglist.append(ArgSig(arg, get_annotation(arg), default=False)) - def get_pos_default(i: int, _arg: str) -> Any | None: + def get_pos_default(i: int, _arg: str) -> Any | _Missing: if defaults and i >= len(args) - len(defaults): return defaults[i - (len(args) - len(defaults))] else: - return None + return _Missing.VALUE add_args(args, get_pos_default) @@ -345,11 +349,11 @@ def get_pos_default(i: int, _arg: str) -> Any | None: elif kwonlyargs: arglist.append(ArgSig("*")) - def get_kw_default(_i: int, arg: str) -> Any | None: - if kwonlydefaults: - return kwonlydefaults.get(arg) + def get_kw_default(_i: int, arg: str) -> Any | _Missing: + if kwonlydefaults and arg in kwonlydefaults: + return kwonlydefaults[arg] else: - return None + return _Missing.VALUE add_args(kwonlyargs, get_kw_default) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 83693bebd91e..55b2fddd0548 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -856,6 +856,30 @@ class TestClassVariableCls: assert_equal(gen.get_imports().splitlines(), ["from typing import ClassVar"]) assert_equal(output, ["class C:", " x: ClassVar[int] = ..."]) + def test_generate_c_type_none_default(self) -> None: + class TestClass: + def test(self, arg0=1, arg1=None) -> None: # type: ignore[no-untyped-def] + pass + + output: list[str] = [] + mod = ModuleType(TestClass.__module__, "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.is_c_module = False + gen.generate_function_stub( + "test", + TestClass.test, + output=output, + class_info=ClassInfo( + self_var="self", + cls=TestClass, + name="TestClass", + docstring=getattr(TestClass, "__doc__", None), + ), + ) + assert_equal( + output, ["def test(self, arg0: int = ..., arg1: Incomplete | None = ...) -> None: ..."] + ) + def test_non_c_generate_signature_with_kw_only_args(self) -> None: class TestClass: def test( From 830a0fa765a2cc6882c7d7ca25c03abecbf0bc31 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 8 Mar 2025 03:33:41 +0100 Subject: [PATCH 1185/1617] Improve docs on type narrowing (#18767) In #18766 I had overlooked the existence of TypeIs. IMHO this could have been prevent by a slightly better structuring of the docs: - add a list of all type narrowing techniques at the top - move the "Limitations" section to the bottom, because it's generic. Because it separated the three other techniques from TypeIs I had not read on below "Limitations" Co-authored-by: Tim Hoffmann --- docs/source/type_narrowing.rst | 78 +++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 697a1519a603..ccd16ffbc0a3 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -8,6 +8,15 @@ techniques which are supported by mypy. Type narrowing is when you convince a type checker that a broader type is actually more specific, for instance, that an object of type ``Shape`` is actually of the narrower type ``Square``. +The following type narrowing techniques are available: + +- :ref:`type-narrowing-expressions` +- :ref:`casts` +- :ref:`type-guards` +- :ref:`typeis` + + +.. _type-narrowing-expressions: Type narrowing expressions -------------------------- @@ -356,40 +365,6 @@ What happens here? The same will work with ``isinstance(x := a, float)`` as well. -Limitations ------------ - -Mypy's analysis is limited to individual symbols and it will not track -relationships between symbols. For example, in the following code -it's easy to deduce that if :code:`a` is None then :code:`b` must not be, -therefore :code:`a or b` will always be an instance of :code:`C`, -but Mypy will not be able to tell that: - -.. code-block:: python - - class C: - pass - - def f(a: C | None, b: C | None) -> C: - if a is not None or b is not None: - return a or b # Incompatible return value type (got "C | None", expected "C") - return C() - -Tracking these sort of cross-variable conditions in a type checker would add significant complexity -and performance overhead. - -You can use an ``assert`` to convince the type checker, override it with a :ref:`cast ` -or rewrite the function to be slightly more verbose: - -.. code-block:: python - - def f(a: C | None, b: C | None) -> C: - if a is not None: - return a - elif b is not None: - return b - return C() - .. _typeis: @@ -555,3 +530,38 @@ You can use the assignment expression operator ``:=`` with ``TypeIs`` to create reveal_type(x) # Revealed type is 'float' # x is narrowed to float in this block print(x + 1.0) + + +Limitations +----------- + +Mypy's analysis is limited to individual symbols and it will not track +relationships between symbols. For example, in the following code +it's easy to deduce that if :code:`a` is None then :code:`b` must not be, +therefore :code:`a or b` will always be an instance of :code:`C`, +but Mypy will not be able to tell that: + +.. code-block:: python + + class C: + pass + + def f(a: C | None, b: C | None) -> C: + if a is not None or b is not None: + return a or b # Incompatible return value type (got "C | None", expected "C") + return C() + +Tracking these sort of cross-variable conditions in a type checker would add significant complexity +and performance overhead. + +You can use an ``assert`` to convince the type checker, override it with a :ref:`cast ` +or rewrite the function to be slightly more verbose: + +.. code-block:: python + + def f(a: C | None, b: C | None) -> C: + if a is not None: + return a + elif b is not None: + return b + return C() From af5186e1bccdc3983a289bea962bb940ef6857f8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 10 Mar 2025 00:59:18 +0000 Subject: [PATCH 1186/1617] Consistently store settable property type (#18774) Fixes https://github.com/python/mypy/issues/18764 There are two things important to understand this PR: * First, mypy has a performance optimization - we store decorator/overload type during semantic analysis in certain "trivial" situations (to avoid deferrals). Otherwise, we infer the type of decorated function or an overload variant during type checking. * Second, for settable properties we store getter type in two places, as a `Var.type` of getter (as a decorator), and also in overall `OverloadedFuncDef.type`. The latter is ugly, but unfortunately it is hard to get rid of, since some code in multiple plugins rely on this. It turns out there are _three_ inconsistencies in how these two things interact (first one causes the actual crash): * For trivial settable properties (i.e. without extra decorators) when we store the type in `semanal.py` we only store it the second way (i.e. as `OverloadedFuncDef.type`). * For non-trivial settable properties (where getter and/or setter are themselves decorated), we only set the inferred type the first way (as `Var.type`). * When inferring setter type (unlike getter, that is handled correctly) we actually ignore any extra decorators (this is probably quire niche, but still inconsistent). Essentially I simply remove these inconsistencies. --- mypy/checker.py | 64 ++++++++++++++++++-------- mypy/semanal.py | 25 +++++++++-- test-data/unit/check-classes.test | 74 +++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 22 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index ac4b24709783..cd76eb1f916b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -654,23 +654,34 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: assert isinstance(defn.items[0], Decorator) self.visit_decorator(defn.items[0]) if defn.items[0].var.is_settable_property: + # TODO: here and elsewhere we assume setter immediately follows getter. assert isinstance(defn.items[1], Decorator) - self.visit_func_def(defn.items[1].func) - setter_type = self.function_type(defn.items[1].func) - assert isinstance(setter_type, CallableType) - if len(setter_type.arg_types) != 2: + # Perform a reduced visit just to infer the actual setter type. + self.visit_decorator_inner(defn.items[1], skip_first_item=True) + setter_type = get_proper_type(defn.items[1].var.type) + # Check if the setter can accept two positional arguments. + any_type = AnyType(TypeOfAny.special_form) + fallback_setter_type = CallableType( + arg_types=[any_type, any_type], + arg_kinds=[ARG_POS, ARG_POS], + arg_names=[None, None], + ret_type=any_type, + fallback=self.named_type("builtins.function"), + ) + if setter_type and not is_subtype(setter_type, fallback_setter_type): self.fail("Invalid property setter signature", defn.items[1].func) - any_type = AnyType(TypeOfAny.from_error) - setter_type = setter_type.copy_modified( - arg_types=[any_type, any_type], - arg_kinds=[ARG_POS, ARG_POS], - arg_names=[None, None], - ) + if not isinstance(setter_type, CallableType) or len(setter_type.arg_types) != 2: + # TODO: keep precise type for callables with tricky but valid signatures. + setter_type = fallback_setter_type defn.items[0].var.setter_type = setter_type - for fdef in defn.items: + for i, fdef in enumerate(defn.items): assert isinstance(fdef, Decorator) if defn.is_property: - self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) + assert isinstance(defn.items[0], Decorator) + settable = defn.items[0].var.is_settable_property + # Do not visit the second time the items we checked above. + if (settable and i > 1) or (not settable and i > 0): + self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) else: # Perform full check for real overloads to infer type of all decorated # overload variants. @@ -692,6 +703,13 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: item_types.append(item_type) if item_types: defn.type = Overloaded(item_types) + elif defn.type is None: + # We store the getter type as an overall overload type, as some + # code paths are getting property type this way. + assert isinstance(defn.items[0], Decorator) + var_type = get_proper_type(defn.items[0].var.type) + assert isinstance(var_type, CallableType) + defn.type = Overloaded([var_type]) # Check override validity after we analyzed current definition. if defn.info: found_method_base_classes = self.check_method_override(defn) @@ -5277,7 +5295,9 @@ def visit_decorator(self, e: Decorator) -> None: return self.visit_decorator_inner(e) - def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None: + def visit_decorator_inner( + self, e: Decorator, allow_empty: bool = False, skip_first_item: bool = False + ) -> None: if self.recurse_into_functions: with self.tscope.function_scope(e.func): self.check_func_item(e.func, name=e.func.name, allow_empty=allow_empty) @@ -5285,17 +5305,24 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None # Process decorators from the inside out to determine decorated signature, which # may be different from the declared signature. sig: Type = self.function_type(e.func) - for d in reversed(e.decorators): + non_trivial_decorator = False + # For settable properties skip the first decorator (that is @foo.setter). + for d in reversed(e.decorators[1:] if skip_first_item else e.decorators): + if refers_to_fullname(d, "abc.abstractmethod"): + # This is a hack to avoid spurious errors because of incomplete type + # of @abstractmethod in the test fixtures. + continue if refers_to_fullname(d, OVERLOAD_NAMES): if not allow_empty: self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) continue + non_trivial_decorator = True dec = self.expr_checker.accept(d) temp = self.temp_node(sig, context=d) fullname = None if isinstance(d, RefExpr): fullname = d.fullname or None - # if this is a expression like @b.a where b is an object, get the type of b + # if this is an expression like @b.a where b is an object, get the type of b, # so we can pass it the method hook in the plugins object_type: Type | None = None if fullname is None and isinstance(d, MemberExpr) and self.has_type(d.expr): @@ -5305,7 +5332,8 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None sig, t2 = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], e, callable_name=fullname, object_type=object_type ) - self.check_untyped_after_decorator(sig, e.func) + if non_trivial_decorator: + self.check_untyped_after_decorator(sig, e.func) sig = set_callable_name(sig, e.func) e.var.type = sig e.var.is_ready = True @@ -5314,8 +5342,8 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None if len([k for k in sig.arg_kinds if k.is_required()]) > 1: self.msg.fail("Too many arguments for property", e) self.check_incompatible_property_override(e) - # For overloaded functions we already checked override for overload as a whole. - if allow_empty: + # For overloaded functions/properties we already checked override for overload as a whole. + if allow_empty or skip_first_item: return if e.func.info and not e.func.is_dynamic() and not e.is_overload: found_method_base_classes = self.check_method_override(e) diff --git a/mypy/semanal.py b/mypy/semanal.py index a0cfdcce1e33..7acea5b2ab91 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1246,10 +1246,11 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: with self.overload_item_set(0): first_item.accept(self) + bare_setter_type = None if isinstance(first_item, Decorator) and first_item.func.is_property: # This is a property. first_item.func.is_overload = True - self.analyze_property_with_multi_part_definition(defn) + bare_setter_type = self.analyze_property_with_multi_part_definition(defn) typ = function_type(first_item.func, self.named_type("builtins.function")) assert isinstance(typ, CallableType) types = [typ] @@ -1283,6 +1284,11 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # * Put decorator everywhere, use "bare" types in overloads. defn.type = Overloaded(types) defn.type.line = defn.line + # In addition, we can set the getter/setter type for valid properties as some + # code paths may either use the above type, or var.type etc. of the first item. + if isinstance(first_item, Decorator) and bare_setter_type: + first_item.var.type = types[0] + first_item.var.setter_type = bare_setter_type if not defn.items: # It was not a real overload after all, but function redefinition. We've @@ -1502,19 +1508,25 @@ def process_static_or_class_method_in_overload(self, defn: OverloadedFuncDef) -> defn.is_class = class_status[0] defn.is_static = static_status[0] - def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) -> None: + def analyze_property_with_multi_part_definition( + self, defn: OverloadedFuncDef + ) -> CallableType | None: """Analyze a property defined using multiple methods (e.g., using @x.setter). Assume that the first method (@property) has already been analyzed. + Return bare setter type (without any other decorators applied), this may be used + by the caller for performance optimizations. """ defn.is_property = True items = defn.items first_item = defn.items[0] assert isinstance(first_item, Decorator) deleted_items = [] + bare_setter_type = None for i, item in enumerate(items[1:]): if isinstance(item, Decorator): - if len(item.decorators) >= 1: + item.func.accept(self) + if item.decorators: first_node = item.decorators[0] if isinstance(first_node, MemberExpr): if first_node.name == "setter": @@ -1522,6 +1534,11 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - first_item.var.is_settable_property = True # Get abstractness from the original definition. item.func.abstract_status = first_item.func.abstract_status + setter_func_type = function_type( + item.func, self.named_type("builtins.function") + ) + assert isinstance(setter_func_type, CallableType) + bare_setter_type = setter_func_type if first_node.name == "deleter": item.func.abstract_status = first_item.func.abstract_status for other_node in item.decorators[1:]: @@ -1530,7 +1547,6 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - self.fail( f"Only supported top decorator is @{first_item.func.name}.setter", item ) - item.func.accept(self) else: self.fail(f'Unexpected definition for property "{first_item.func.name}"', item) deleted_items.append(i + 1) @@ -1544,6 +1560,7 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - item.func.deprecated = ( f"function {item.fullname} is deprecated: {deprecated}" ) + return bare_setter_type def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> None: if self.is_class_scope(): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 06a863ad0499..70cd84dd21ac 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -8464,3 +8464,77 @@ def deco(fn: Callable[[], list[T]]) -> Callable[[], T]: ... @deco def f() -> list[str]: ... [builtins fixtures/property.pyi] + +[case testPropertySetterSuperclassDeferred2] +import a +[file a.py] +import b +class D(b.C): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "C" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... +[file b.py] +from a import D +class C: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... +[builtins fixtures/property.pyi] + +[case testPropertySetterDecorated] +from typing import Callable, TypeVar + +class B: + def __init__(self) -> None: + self.foo: str + self.bar: int + +class C(B): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B" defined the type as "str", \ + # N: override has type "int") + @deco + def foo(self, x: int, y: int) -> None: ... + + @property + def bar(self) -> int: ... + @bar.setter + @deco + def bar(self, x: int, y: int) -> None: ... + + @property + def baz(self) -> int: ... + @baz.setter + @deco_untyped + def baz(self, x: int) -> None: ... + +c: C +c.baz = "yes" # OK, because of untyped decorator + +T = TypeVar("T") +def deco(fn: Callable[[T, int, int], None]) -> Callable[[T, int], None]: ... +def deco_untyped(fn): ... +[builtins fixtures/property.pyi] + +[case testPropertyDeleterBodyChecked] +class C: + @property + def foo(self) -> int: ... + @foo.deleter + def foo(self) -> None: + 1() # E: "int" not callable + + @property + def bar(self) -> int: ... + @bar.setter + def bar(self, x: str) -> None: ... + @bar.deleter + def bar(self) -> None: + 1() # E: "int" not callable +[builtins fixtures/property.pyi] From 52907ac3771a713380cb2c733b8182bd7fe3756f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 10 Mar 2025 16:54:20 +0100 Subject: [PATCH 1187/1617] [mypyc] Replace internal _PyObject_CallMethodId calls (#18761) --- mypyc/lib-rt/bytes_ops.c | 6 +++- mypyc/lib-rt/dict_ops.c | 60 ++++++++++++++++++++++++++++++------ mypyc/lib-rt/misc_ops.c | 8 +++-- mypyc/lib-rt/pythonsupport.h | 1 - 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 5ddf3528211f..6ff34b021a9a 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -102,7 +102,11 @@ PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter) { return PyBytes_Join(sep, iter); } else { _Py_IDENTIFIER(join); - return _PyObject_CallMethodIdOneArg(sep, &PyId_join, iter); + PyObject *name = _PyUnicode_FromId(&PyId_join); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodOneArg(sep, name, iter); } } diff --git a/mypyc/lib-rt/dict_ops.c b/mypyc/lib-rt/dict_ops.c index b33233521afd..b102aba57307 100644 --- a/mypyc/lib-rt/dict_ops.c +++ b/mypyc/lib-rt/dict_ops.c @@ -208,7 +208,11 @@ PyObject *CPyDict_KeysView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictKeys_Type); } _Py_IDENTIFIER(keys); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_keys); + PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_ValuesView(PyObject *dict) { @@ -216,7 +220,11 @@ PyObject *CPyDict_ValuesView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictValues_Type); } _Py_IDENTIFIER(values); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_ItemsView(PyObject *dict) { @@ -224,7 +232,11 @@ PyObject *CPyDict_ItemsView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictItems_Type); } _Py_IDENTIFIER(items); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_Keys(PyObject *dict) { @@ -234,7 +246,11 @@ PyObject *CPyDict_Keys(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(keys); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_keys); + PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -253,7 +269,11 @@ PyObject *CPyDict_Values(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(values); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -272,7 +292,11 @@ PyObject *CPyDict_Items(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(items); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -289,7 +313,11 @@ char CPyDict_Clear(PyObject *dict) { PyDict_Clear(dict); } else { _Py_IDENTIFIER(clear); - PyObject *res = _PyObject_CallMethodIdNoArgs(dict, &PyId_clear); + PyObject *name = _PyUnicode_FromId(&PyId_clear); /* borrowed */ + if (name == NULL) { + return 0; + } + PyObject *res = PyObject_CallMethodNoArgs(dict, name); if (res == NULL) { return 0; } @@ -302,7 +330,11 @@ PyObject *CPyDict_Copy(PyObject *dict) { return PyDict_Copy(dict); } _Py_IDENTIFIER(copy); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_copy); + PyObject *name = _PyUnicode_FromId(&PyId_copy); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_GetKeysIter(PyObject *dict) { @@ -321,7 +353,11 @@ PyObject *CPyDict_GetItemsIter(PyObject *dict) { return dict; } _Py_IDENTIFIER(items); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -337,7 +373,11 @@ PyObject *CPyDict_GetValuesIter(PyObject *dict) { return dict; } _Py_IDENTIFIER(values); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index a674240d8940..d234138b2ff7 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -24,11 +24,15 @@ PyObject *CPyIter_Send(PyObject *iter, PyObject *val) { // Do a send, or a next if second arg is None. // (This behavior is to match the PEP 380 spec for yield from.) - _Py_IDENTIFIER(send); if (Py_IsNone(val)) { return CPyIter_Next(iter); } else { - return _PyObject_CallMethodIdOneArg(iter, &PyId_send, val); + _Py_IDENTIFIER(send); + PyObject *name = _PyUnicode_FromId(&PyId_send); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodOneArg(iter, name, val); } } diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index f35f8a1a6e4e..7019c12cf59a 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -17,7 +17,6 @@ #ifndef Py_BUILD_CORE #define Py_BUILD_CORE #endif -#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdOneArg #include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue #include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError #include "internal/pycore_setobject.h" // _PySet_Update From a4313e495673fbda2f97727819ff5f42f1fc6844 Mon Sep 17 00:00:00 2001 From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:20:45 -0700 Subject: [PATCH 1188/1617] [mypyc] Add efficient primitives for str.strip() etc. (#18742) Fixes mypyc/mypyc#1090. Copying cpython implementation for strip, lstrip and rstrip to `str_ops.c`. --- mypyc/lib-rt/CPy.h | 14 +++ mypyc/lib-rt/str_ops.c | 171 +++++++++++++++++++++++++++++++ mypyc/primitives/str_ops.py | 19 ++++ mypyc/test-data/fixtures/ir.py | 4 +- mypyc/test-data/irbuild-str.test | 23 +++++ mypyc/test-data/run-strings.test | 19 ++++ 6 files changed, 249 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 1c8b59855fc7..fda7ff4eb09c 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -717,6 +717,10 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { // Str operations +// Macros for strip type. These values are copied from CPython. +#define LEFTSTRIP 0 +#define RIGHTSTRIP 1 +#define BOTHSTRIP 2 PyObject *CPyStr_Build(Py_ssize_t len, ...); PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index); @@ -724,6 +728,16 @@ CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int dire CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction); PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split); PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split); +PyObject *_CPyStr_Strip(PyObject *self, int strip_type, PyObject *sep); +static inline PyObject *CPyStr_Strip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, BOTHSTRIP, sep); +} +static inline PyObject *CPyStr_LStrip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, LEFTSTRIP, sep); +} +static inline PyObject *CPyStr_RStrip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, RIGHTSTRIP, sep); +} PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace); PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 5b295f84440b..130840cf4e08 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -5,6 +5,59 @@ #include #include "CPy.h" +// Copied from cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +#define BLOOM_MASK unsigned long +#define BLOOM(mask, ch) ((mask & (1UL << ((ch) & (BLOOM_WIDTH - 1))))) +#if LONG_BIT >= 128 +#define BLOOM_WIDTH 128 +#elif LONG_BIT >= 64 +#define BLOOM_WIDTH 64 +#elif LONG_BIT >= 32 +#define BLOOM_WIDTH 32 +#else +#error "LONG_BIT is smaller than 32" +#endif + +// Copied from cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +// This is needed for str.strip("..."). +static inline BLOOM_MASK +make_bloom_mask(int kind, const void* ptr, Py_ssize_t len) +{ +#define BLOOM_UPDATE(TYPE, MASK, PTR, LEN) \ + do { \ + TYPE *data = (TYPE *)PTR; \ + TYPE *end = data + LEN; \ + Py_UCS4 ch; \ + for (; data != end; data++) { \ + ch = *data; \ + MASK |= (1UL << (ch & (BLOOM_WIDTH - 1))); \ + } \ + break; \ + } while (0) + + /* calculate simple bloom-style bitmask for a given unicode string */ + + BLOOM_MASK mask; + + mask = 0; + switch (kind) { + case PyUnicode_1BYTE_KIND: + BLOOM_UPDATE(Py_UCS1, mask, ptr, len); + break; + case PyUnicode_2BYTE_KIND: + BLOOM_UPDATE(Py_UCS2, mask, ptr, len); + break; + case PyUnicode_4BYTE_KIND: + BLOOM_UPDATE(Py_UCS4, mask, ptr, len); + break; + default: + Py_UNREACHABLE(); + } + return mask; + +#undef BLOOM_UPDATE +} + PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) { if (PyUnicode_READY(str) != -1) { if (CPyTagged_CheckShort(index)) { @@ -174,6 +227,124 @@ PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split) { return PyUnicode_RSplit(str, sep, temp_max_split); } +// This function has been copied from _PyUnicode_XStrip in cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +static PyObject *_PyStr_XStrip(PyObject *self, int striptype, PyObject *sepobj) { + const void *data; + int kind; + Py_ssize_t i, j, len; + BLOOM_MASK sepmask; + Py_ssize_t seplen; + + // This check is needed from Python 3.9 and earlier. + if (PyUnicode_READY(self) == -1 || PyUnicode_READY(sepobj) == -1) + return NULL; + + kind = PyUnicode_KIND(self); + data = PyUnicode_DATA(self); + len = PyUnicode_GET_LENGTH(self); + seplen = PyUnicode_GET_LENGTH(sepobj); + sepmask = make_bloom_mask(PyUnicode_KIND(sepobj), + PyUnicode_DATA(sepobj), + seplen); + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, i); + if (!BLOOM(sepmask, ch)) + break; + if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0) + break; + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS4 ch = PyUnicode_READ(kind, data, j); + if (!BLOOM(sepmask, ch)) + break; + if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0) + break; + j--; + } + + j++; + } + + return PyUnicode_Substring(self, i, j); +} + +// Copied from do_strip function in cpython.git/Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +PyObject *_CPyStr_Strip(PyObject *self, int strip_type, PyObject *sep) { + if (sep == NULL || sep == Py_None) { + Py_ssize_t len, i, j; + + // This check is needed from Python 3.9 and earlier. + if (PyUnicode_READY(self) == -1) + return NULL; + + len = PyUnicode_GET_LENGTH(self); + + if (PyUnicode_IS_ASCII(self)) { + const Py_UCS1 *data = PyUnicode_1BYTE_DATA(self); + + i = 0; + if (strip_type != RIGHTSTRIP) { + while (i < len) { + Py_UCS1 ch = data[i]; + if (!_Py_ascii_whitespace[ch]) + break; + i++; + } + } + + j = len; + if (strip_type != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS1 ch = data[j]; + if (!_Py_ascii_whitespace[ch]) + break; + j--; + } + j++; + } + } + else { + int kind = PyUnicode_KIND(self); + const void *data = PyUnicode_DATA(self); + + i = 0; + if (strip_type != RIGHTSTRIP) { + while (i < len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, i); + if (!Py_UNICODE_ISSPACE(ch)) + break; + i++; + } + } + + j = len; + if (strip_type != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS4 ch = PyUnicode_READ(kind, data, j); + if (!Py_UNICODE_ISSPACE(ch)) + break; + j--; + } + j++; + } + } + + return PyUnicode_Substring(self, i, j); + } + return _PyStr_XStrip(self, strip_type, sep); +} + PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace) { Py_ssize_t temp_max_replace = CPyTagged_AsSsize_t(max_replace); diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index e4c644470ba4..75d47b0f0e7a 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -135,6 +135,25 @@ var_arg_type=str_rprimitive, ) +# str.strip, str.lstrip, str.rstrip +for strip_prefix in ["l", "r", ""]: + method_op( + name=f"{strip_prefix}strip", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name=f"CPyStr_{strip_prefix.upper()}Strip", + error_kind=ERR_NEVER, + ) + method_op( + name=f"{strip_prefix}strip", + arg_types=[str_rprimitive], + return_type=str_rprimitive, + c_function_name=f"CPyStr_{strip_prefix.upper()}Strip", + # This 0 below is implicitly treated as NULL in C. + extra_int_constants=[(0, c_int_rprimitive)], + error_kind=ERR_NEVER, + ) + # str.startswith(str) method_op( name="startswith", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 38fecbc20c65..e651e7adc384 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -107,7 +107,9 @@ def rfind(self, sub: str, start: Optional[int] = None, end: Optional[int] = None def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass def splitlines(self, keepends: bool = False) -> List[str]: ... - def strip (self, item: str) -> str: pass + def strip (self, item: Optional[str] = None) -> str: pass + def lstrip(self, item: Optional[str] = None) -> str: pass + def rstrip(self, item: Optional[str] = None) -> str: pass def join(self, x: Iterable[str]) -> str: pass def format(self, *args: Any, **kwargs: Any) -> str: ... def upper(self) -> str: ... diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 352fb6cf72d9..ad495dddcb15 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -481,3 +481,26 @@ L0: keep_alive x r6 = unbox(int, r5) return r6 + +[case testStrip] +def do_strip(s: str) -> None: + s.lstrip("x") + s.strip("y") + s.rstrip("z") + s.lstrip() + s.strip() + s.rstrip() +[out] +def do_strip(s): + s, r0, r1, r2, r3, r4, r5, r6, r7, r8 :: str +L0: + r0 = 'x' + r1 = CPyStr_LStrip(s, r0) + r2 = 'y' + r3 = CPyStr_Strip(s, r2) + r4 = 'z' + r5 = CPyStr_RStrip(s, r4) + r6 = CPyStr_LStrip(s, 0) + r7 = CPyStr_Strip(s, 0) + r8 = CPyStr_RStrip(s, 0) + return 1 diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index ce5c85059aed..07122c2707ac 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -774,3 +774,22 @@ def test_surrogate() -> None: assert ord(f()) == 0xd800 assert ord("\udfff") == 0xdfff assert repr("foobar\x00\xab\ud912\U00012345") == r"'foobar\x00«\ud912𒍅'" + +[case testStrip] +def test_all_strips_default() -> None: + s = " a1\t" + assert s.lstrip() == "a1\t" + assert s.strip() == "a1" + assert s.rstrip() == " a1" +def test_all_strips() -> None: + s = "xxb2yy" + assert s.lstrip("xy") == "b2yy" + assert s.strip("xy") == "b2" + assert s.rstrip("xy") == "xxb2" +def test_unicode_whitespace() -> None: + assert "\u200A\u000D\u2009\u2020\u000Dtt\u0085\u000A".strip() == "\u2020\u000Dtt" +def test_unicode_range() -> None: + assert "\u2029 \U00107581 ".lstrip() == "\U00107581 " + assert "\u2029 \U0010AAAA\U00104444B\u205F ".strip() == "\U0010AAAA\U00104444B" + assert " \u3000\u205F ".strip() == "" + assert "\u2029 \U00102865\u205F ".rstrip() == "\u2029 \U00102865" From 662bbebef7ab4c958ebc2ff4f632792f075ede88 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 11 Mar 2025 11:08:01 +0000 Subject: [PATCH 1189/1617] Fix crash for callable with *args and suffix against Any (#18781) Fixes https://github.com/python/mypy/issues/18780 Fix is trivial: handle a missing case. Note I re-use `flatten_nested_tuples()` out of laziness. In theory, there should be at most one level of nesting at this point, after which we should put an assert (and IIRC we do something like this in other places). But I think it is not worth the effort here, as this is a quite niche edge case anyway. --- mypy/constraints.py | 5 ++++- mypy/types.py | 2 +- test-data/unit/check-typevar-tuple.test | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index d88b722aa1ce..e76f6cd639ad 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -53,6 +53,7 @@ UnionType, UnpackType, find_unpack_in_list, + flatten_nested_tuples, get_proper_type, has_recursive_types, has_type_vars, @@ -1347,7 +1348,9 @@ def visit_type_alias_type(self, template: TypeAliasType) -> list[Constraint]: def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> list[Constraint]: res: list[Constraint] = [] - for t in types: + # Some items may be things like `*Tuple[*Ts, T]` for example from callable types with + # suffix after *arg, so flatten them. + for t in flatten_nested_tuples(types): if isinstance(t, UnpackType): if isinstance(t.type, TypeVarTupleType): res.append(Constraint(t.type, self.direction, any_type)) diff --git a/mypy/types.py b/mypy/types.py index f9749945d9e9..9dd0ef8552b9 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3726,7 +3726,7 @@ def find_unpack_in_list(items: Sequence[Type]) -> int | None: return unpack_index -def flatten_nested_tuples(types: Sequence[Type]) -> list[Type]: +def flatten_nested_tuples(types: Iterable[Type]) -> list[Type]: """Recursively flatten TupleTypes nested with Unpack. For example this will transform diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 2cc84c8e6b15..57a96291b04a 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2606,3 +2606,15 @@ def test(xs: tuple[Unpack[Ts]], xsi: tuple[int, Unpack[Ts]]) -> None: reveal_type(join(xsi, ai)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" reveal_type(join(ai, xsi)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" [builtins fixtures/tuple.pyi] + +[case testTypeVarTupleInferAgainstAnyCallableSuffix] +from typing import Any, Callable, TypeVar, TypeVarTuple + +Ts = TypeVarTuple("Ts") +R = TypeVar("R") +def deco(func: Callable[[*Ts, int], R]) -> Callable[[*Ts], R]: + ... + +untyped: Any +reveal_type(deco(untyped)) # N: Revealed type is "def (*Any) -> Any" +[builtins fixtures/tuple.pyi] From 2b176ab032a47a9659f7deef3470ff8a64f87542 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:59:52 +0100 Subject: [PATCH 1190/1617] Update ruff to 0.9.10 (#18788) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d4896c95b3a..d466d4563aff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.10 hooks: - id: ruff args: [--exit-non-zero-on-fix] From 0f5ddd5e647d14afca93d3f41892b62af302e894 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 11 Mar 2025 17:07:06 +0000 Subject: [PATCH 1191/1617] Fix crash on decorated getter in settable property (#18787) Follow up for https://github.com/python/mypy/pull/18774 Fix for crash is trivial, properly handle getter the same way as setter. Note I also consistently handle callable instances. --- mypy/checker.py | 16 ++++++++-- mypy/semanal.py | 10 +++++-- test-data/unit/check-classes.test | 50 ++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index cd76eb1f916b..6d7e8fa215a1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -658,7 +658,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: assert isinstance(defn.items[1], Decorator) # Perform a reduced visit just to infer the actual setter type. self.visit_decorator_inner(defn.items[1], skip_first_item=True) - setter_type = get_proper_type(defn.items[1].var.type) + setter_type = defn.items[1].var.type # Check if the setter can accept two positional arguments. any_type = AnyType(TypeOfAny.special_form) fallback_setter_type = CallableType( @@ -670,6 +670,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: ) if setter_type and not is_subtype(setter_type, fallback_setter_type): self.fail("Invalid property setter signature", defn.items[1].func) + setter_type = self.extract_callable_type(setter_type, defn) if not isinstance(setter_type, CallableType) or len(setter_type.arg_types) != 2: # TODO: keep precise type for callables with tricky but valid signatures. setter_type = fallback_setter_type @@ -707,8 +708,17 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # We store the getter type as an overall overload type, as some # code paths are getting property type this way. assert isinstance(defn.items[0], Decorator) - var_type = get_proper_type(defn.items[0].var.type) - assert isinstance(var_type, CallableType) + var_type = self.extract_callable_type(defn.items[0].var.type, defn) + if not isinstance(var_type, CallableType): + # Construct a fallback type, invalid types should be already reported. + any_type = AnyType(TypeOfAny.special_form) + var_type = CallableType( + arg_types=[any_type], + arg_kinds=[ARG_POS], + arg_names=[None], + ret_type=any_type, + fallback=self.named_type("builtins.function"), + ) defn.type = Overloaded([var_type]) # Check override validity after we analyzed current definition. if defn.info: diff --git a/mypy/semanal.py b/mypy/semanal.py index 7acea5b2ab91..c48b65f0ee94 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1247,7 +1247,9 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: first_item.accept(self) bare_setter_type = None + is_property = False if isinstance(first_item, Decorator) and first_item.func.is_property: + is_property = True # This is a property. first_item.func.is_overload = True bare_setter_type = self.analyze_property_with_multi_part_definition(defn) @@ -1255,7 +1257,7 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: assert isinstance(typ, CallableType) types = [typ] else: - # This is an a normal overload. Find the item signatures, the + # This is a normal overload. Find the item signatures, the # implementation (if outside a stub), and any missing @overload # decorators. types, impl, non_overload_indexes = self.analyze_overload_sigs_and_impl(defn) @@ -1275,8 +1277,10 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: if types and not any( # If some overload items are decorated with other decorators, then # the overload type will be determined during type checking. - isinstance(it, Decorator) and len(it.decorators) > 1 - for it in defn.items + # Note: bare @property is removed in visit_decorator(). + isinstance(it, Decorator) + and len(it.decorators) > (1 if i > 0 or not is_property else 0) + for i, it in enumerate(defn.items) ): # TODO: should we enforce decorated overloads consistency somehow? # Some existing code uses both styles: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 70cd84dd21ac..0da0f7c3bbcd 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -8486,7 +8486,7 @@ class C: [builtins fixtures/property.pyi] [case testPropertySetterDecorated] -from typing import Callable, TypeVar +from typing import Callable, TypeVar, Generic class B: def __init__(self) -> None: @@ -8514,12 +8514,23 @@ class C(B): @deco_untyped def baz(self, x: int) -> None: ... + @property + def tricky(self) -> int: ... + @baz.setter + @deco_instance + def tricky(self, x: int) -> None: ... + c: C c.baz = "yes" # OK, because of untyped decorator +c.tricky = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "List[int]") T = TypeVar("T") def deco(fn: Callable[[T, int, int], None]) -> Callable[[T, int], None]: ... def deco_untyped(fn): ... + +class Wrapper(Generic[T]): + def __call__(self, s: T, x: list[int]) -> None: ... +def deco_instance(fn: Callable[[T, int], None]) -> Wrapper[T]: ... [builtins fixtures/property.pyi] [case testPropertyDeleterBodyChecked] @@ -8538,3 +8549,40 @@ class C: def bar(self) -> None: 1() # E: "int" not callable [builtins fixtures/property.pyi] + +[case testSettablePropertyGetterDecorated] +from typing import Callable, TypeVar, Generic + +class C: + @property + @deco + def foo(self, ok: int) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + + @property + @deco_instance + def bar(self, ok: int) -> int: ... + @bar.setter + def bar(self, x: int) -> None: ... + + @property + @deco_untyped + def baz(self) -> int: ... + @baz.setter + def baz(self, x: int) -> None: ... + +c: C +reveal_type(c.foo) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(c.bar) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(c.baz) # N: Revealed type is "Any" + +T = TypeVar("T") +R = TypeVar("R") +def deco(fn: Callable[[T, int], R]) -> Callable[[T], list[R]]: ... +def deco_untyped(fn): ... + +class Wrapper(Generic[T, R]): + def __call__(self, s: T) -> list[R]: ... +def deco_instance(fn: Callable[[T, int], R]) -> Wrapper[T, R]: ... +[builtins fixtures/property.pyi] From a8b723d57a72aa42039548e689605d0598d0a232 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 12 Mar 2025 12:47:22 +0000 Subject: [PATCH 1192/1617] [mypyc] Document that strip() etc. are optimized (#18793) (Also unrelated minor formatting tweak to frozenset docs.) --- mypyc/doc/frozenset_operations.rst | 2 +- mypyc/doc/str_operations.rst | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mypyc/doc/frozenset_operations.rst b/mypyc/doc/frozenset_operations.rst index a30b6a55c584..3d946a8fa9a3 100644 --- a/mypyc/doc/frozenset_operations.rst +++ b/mypyc/doc/frozenset_operations.rst @@ -1,7 +1,7 @@ .. _frozenset-ops: Native frozenset operations -====================== +=========================== These ``frozenset`` operations have fast, optimized implementations. Other frozenset operations use generic implementations that are often slower. diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 5b18c0c927d6..11828a4d128a 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -37,6 +37,8 @@ Methods * ``s1.find(s2: str, start: int)`` * ``s1.find(s2: str, start: int, end: int)`` * ``s.join(x: Iterable)`` +* ``s.lstrip()`` +* ``s.lstrip(chars: str)`` * ``s.partition(sep: str)`` * ``s.removeprefix(prefix: str)`` * ``s.removesuffix(suffix: str)`` @@ -49,6 +51,8 @@ Methods * ``s.rsplit()`` * ``s.rsplit(sep: str)`` * ``s.rsplit(sep: str, maxsplit: int)`` +* ``s.rstrip()`` +* ``s.rstrip(chars: str)`` * ``s.split()`` * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` @@ -56,6 +60,8 @@ Methods * ``s.splitlines(keepends: bool)`` * ``s1.startswith(s2: str)`` * ``s1.startswith(t: tuple[str, ...])`` +* ``s.strip()`` +* ``s.strip(chars: str)`` .. note:: From e37d92d6c2d1de92e74c365ee1240c67c94c24b3 Mon Sep 17 00:00:00 2001 From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:14:18 +0100 Subject: [PATCH 1193/1617] [mypyc] Support iterating over keys/values/items of dict-bound TypeVar and ParamSpec.kwargs (#18789) Fixes #18784. --- mypyc/irbuild/builder.py | 54 ++- mypyc/test-data/irbuild-generics.test | 638 +++++++++++++++++++++++++- mypyc/test-data/run-generics.test | 111 +++++ mypyc/test/test_run.py | 1 + 4 files changed, 779 insertions(+), 25 deletions(-) create mode 100644 mypyc/test-data/run-generics.test diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index aafa7f3a0976..d9d3c5ed9cd0 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -958,38 +958,44 @@ def get_dict_base_type(self, expr: Expression) -> list[Instance]: This is useful for dict subclasses like SymbolTable. """ - target_type = get_proper_type(self.types[expr]) + return self.get_dict_base_type_from_type(self.types[expr]) + + def get_dict_base_type_from_type(self, target_type: Type) -> list[Instance]: + target_type = get_proper_type(target_type) if isinstance(target_type, UnionType): - types = [get_proper_type(item) for item in target_type.items] + return [ + inner + for item in target_type.items + for inner in self.get_dict_base_type_from_type(item) + ] + if isinstance(target_type, TypeVarLikeType): + # Match behaviour of self.node_type + # We can only reach this point if `target_type` was a TypeVar(bound=dict[...]) + # or a ParamSpec. + return self.get_dict_base_type_from_type(target_type.upper_bound) + + if isinstance(target_type, TypedDictType): + target_type = target_type.fallback + dict_base = next( + base for base in target_type.type.mro if base.fullname == "typing.Mapping" + ) + elif isinstance(target_type, Instance): + dict_base = next( + base for base in target_type.type.mro if base.fullname == "builtins.dict" + ) else: - types = [target_type] - - dict_types = [] - for t in types: - if isinstance(t, TypedDictType): - t = t.fallback - dict_base = next(base for base in t.type.mro if base.fullname == "typing.Mapping") - else: - assert isinstance(t, Instance), t - dict_base = next(base for base in t.type.mro if base.fullname == "builtins.dict") - dict_types.append(map_instance_to_supertype(t, dict_base)) - return dict_types + assert False, f"Failed to extract dict base from {target_type}" + return [map_instance_to_supertype(target_type, dict_base)] def get_dict_key_type(self, expr: Expression) -> RType: dict_base_types = self.get_dict_base_type(expr) - if len(dict_base_types) == 1: - return self.type_to_rtype(dict_base_types[0].args[0]) - else: - rtypes = [self.type_to_rtype(t.args[0]) for t in dict_base_types] - return RUnion.make_simplified_union(rtypes) + rtypes = [self.type_to_rtype(t.args[0]) for t in dict_base_types] + return RUnion.make_simplified_union(rtypes) def get_dict_value_type(self, expr: Expression) -> RType: dict_base_types = self.get_dict_base_type(expr) - if len(dict_base_types) == 1: - return self.type_to_rtype(dict_base_types[0].args[1]) - else: - rtypes = [self.type_to_rtype(t.args[1]) for t in dict_base_types] - return RUnion.make_simplified_union(rtypes) + rtypes = [self.type_to_rtype(t.args[1]) for t in dict_base_types] + return RUnion.make_simplified_union(rtypes) def get_dict_item_type(self, expr: Expression) -> RType: key_type = self.get_dict_key_type(expr) diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 910148f80dda..feb7b9db20fb 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -151,7 +151,7 @@ L3: [case testParamSpec] -from typing import Callable, ParamSpec, TypeVar +from typing import Callable, ParamSpec P = ParamSpec("P") @@ -189,3 +189,639 @@ def f(x): x :: int L0: return x + +[case testTypeVarMappingBound] +# Dicts are special-cased for efficient iteration. +from typing import Dict, TypedDict, TypeVar, Union + +class TD(TypedDict): + foo: int + +M = TypeVar("M", bound=Dict[str, int]) +U = TypeVar("U", bound=Union[Dict[str, int], Dict[str, str]]) +T = TypeVar("T", bound=TD) + +def fn_mapping(m: M) -> None: + [x for x in m] + [x for x in m.values()] + {x for x in m.keys()} + {k: v for k, v in m.items()} + +def fn_union(m: U) -> None: + [x for x in m] + [x for x in m.values()] + {x for x in m.keys()} + {k: v for k, v in m.items()} + +def fn_typeddict(t: T) -> None: + [x for x in t] + [x for x in t.values()] + {x for x in t.keys()} + {k: v for k, v in t.items()} + +[typing fixtures/typing-full.pyi] +[out] +def fn_mapping(m): + m :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: short_int + r4 :: object + r5 :: tuple[bool, short_int, object] + r6 :: short_int + r7 :: bool + r8 :: object + r9, x :: str + r10 :: i32 + r11, r12, r13 :: bit + r14 :: list + r15 :: short_int + r16 :: native_int + r17 :: short_int + r18 :: object + r19 :: tuple[bool, short_int, object] + r20 :: short_int + r21 :: bool + r22 :: object + r23, x_2 :: int + r24 :: object + r25 :: i32 + r26, r27, r28 :: bit + r29 :: set + r30 :: short_int + r31 :: native_int + r32 :: short_int + r33 :: object + r34 :: tuple[bool, short_int, object] + r35 :: short_int + r36 :: bool + r37 :: object + r38, x_3 :: str + r39 :: i32 + r40, r41, r42 :: bit + r43 :: dict + r44 :: short_int + r45 :: native_int + r46 :: short_int + r47 :: object + r48 :: tuple[bool, short_int, object, object] + r49 :: short_int + r50 :: bool + r51, r52 :: object + r53 :: str + r54 :: int + k :: str + v :: int + r55 :: object + r56 :: i32 + r57, r58, r59 :: bit +L0: + r0 = PyList_New(0) + r1 = 0 + r2 = PyDict_Size(m) + r3 = r2 << 1 + r4 = CPyDict_GetKeysIter(m) +L1: + r5 = CPyDict_NextKey(r4, r1) + r6 = r5[1] + r1 = r6 + r7 = r5[0] + if r7 goto L2 else goto L4 :: bool +L2: + r8 = r5[2] + r9 = cast(str, r8) + x = r9 + r10 = PyList_Append(r0, x) + r11 = r10 >= 0 :: signed +L3: + r12 = CPyDict_CheckSize(m, r3) + goto L1 +L4: + r13 = CPy_NoErrOccurred() +L5: + r14 = PyList_New(0) + r15 = 0 + r16 = PyDict_Size(m) + r17 = r16 << 1 + r18 = CPyDict_GetValuesIter(m) +L6: + r19 = CPyDict_NextValue(r18, r15) + r20 = r19[1] + r15 = r20 + r21 = r19[0] + if r21 goto L7 else goto L9 :: bool +L7: + r22 = r19[2] + r23 = unbox(int, r22) + x_2 = r23 + r24 = box(int, x_2) + r25 = PyList_Append(r14, r24) + r26 = r25 >= 0 :: signed +L8: + r27 = CPyDict_CheckSize(m, r17) + goto L6 +L9: + r28 = CPy_NoErrOccurred() +L10: + r29 = PySet_New(0) + r30 = 0 + r31 = PyDict_Size(m) + r32 = r31 << 1 + r33 = CPyDict_GetKeysIter(m) +L11: + r34 = CPyDict_NextKey(r33, r30) + r35 = r34[1] + r30 = r35 + r36 = r34[0] + if r36 goto L12 else goto L14 :: bool +L12: + r37 = r34[2] + r38 = cast(str, r37) + x_3 = r38 + r39 = PySet_Add(r29, x_3) + r40 = r39 >= 0 :: signed +L13: + r41 = CPyDict_CheckSize(m, r32) + goto L11 +L14: + r42 = CPy_NoErrOccurred() +L15: + r43 = PyDict_New() + r44 = 0 + r45 = PyDict_Size(m) + r46 = r45 << 1 + r47 = CPyDict_GetItemsIter(m) +L16: + r48 = CPyDict_NextItem(r47, r44) + r49 = r48[1] + r44 = r49 + r50 = r48[0] + if r50 goto L17 else goto L19 :: bool +L17: + r51 = r48[2] + r52 = r48[3] + r53 = cast(str, r51) + r54 = unbox(int, r52) + k = r53 + v = r54 + r55 = box(int, v) + r56 = CPyDict_SetItem(r43, k, r55) + r57 = r56 >= 0 :: signed +L18: + r58 = CPyDict_CheckSize(m, r46) + goto L16 +L19: + r59 = CPy_NoErrOccurred() +L20: + return 1 +def fn_union(m): + m :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: short_int + r4 :: object + r5 :: tuple[bool, short_int, object] + r6 :: short_int + r7 :: bool + r8 :: object + r9, x :: str + r10 :: i32 + r11, r12, r13 :: bit + r14 :: list + r15 :: short_int + r16 :: native_int + r17 :: short_int + r18 :: object + r19 :: tuple[bool, short_int, object] + r20 :: short_int + r21 :: bool + r22 :: object + r23, x_2 :: union[int, str] + r24 :: i32 + r25, r26, r27 :: bit + r28 :: set + r29 :: short_int + r30 :: native_int + r31 :: short_int + r32 :: object + r33 :: tuple[bool, short_int, object] + r34 :: short_int + r35 :: bool + r36 :: object + r37, x_3 :: str + r38 :: i32 + r39, r40, r41 :: bit + r42 :: dict + r43 :: short_int + r44 :: native_int + r45 :: short_int + r46 :: object + r47 :: tuple[bool, short_int, object, object] + r48 :: short_int + r49 :: bool + r50, r51 :: object + r52 :: str + r53 :: union[int, str] + k :: str + v :: union[int, str] + r54 :: i32 + r55, r56, r57 :: bit +L0: + r0 = PyList_New(0) + r1 = 0 + r2 = PyDict_Size(m) + r3 = r2 << 1 + r4 = CPyDict_GetKeysIter(m) +L1: + r5 = CPyDict_NextKey(r4, r1) + r6 = r5[1] + r1 = r6 + r7 = r5[0] + if r7 goto L2 else goto L4 :: bool +L2: + r8 = r5[2] + r9 = cast(str, r8) + x = r9 + r10 = PyList_Append(r0, x) + r11 = r10 >= 0 :: signed +L3: + r12 = CPyDict_CheckSize(m, r3) + goto L1 +L4: + r13 = CPy_NoErrOccurred() +L5: + r14 = PyList_New(0) + r15 = 0 + r16 = PyDict_Size(m) + r17 = r16 << 1 + r18 = CPyDict_GetValuesIter(m) +L6: + r19 = CPyDict_NextValue(r18, r15) + r20 = r19[1] + r15 = r20 + r21 = r19[0] + if r21 goto L7 else goto L9 :: bool +L7: + r22 = r19[2] + r23 = cast(union[int, str], r22) + x_2 = r23 + r24 = PyList_Append(r14, x_2) + r25 = r24 >= 0 :: signed +L8: + r26 = CPyDict_CheckSize(m, r17) + goto L6 +L9: + r27 = CPy_NoErrOccurred() +L10: + r28 = PySet_New(0) + r29 = 0 + r30 = PyDict_Size(m) + r31 = r30 << 1 + r32 = CPyDict_GetKeysIter(m) +L11: + r33 = CPyDict_NextKey(r32, r29) + r34 = r33[1] + r29 = r34 + r35 = r33[0] + if r35 goto L12 else goto L14 :: bool +L12: + r36 = r33[2] + r37 = cast(str, r36) + x_3 = r37 + r38 = PySet_Add(r28, x_3) + r39 = r38 >= 0 :: signed +L13: + r40 = CPyDict_CheckSize(m, r31) + goto L11 +L14: + r41 = CPy_NoErrOccurred() +L15: + r42 = PyDict_New() + r43 = 0 + r44 = PyDict_Size(m) + r45 = r44 << 1 + r46 = CPyDict_GetItemsIter(m) +L16: + r47 = CPyDict_NextItem(r46, r43) + r48 = r47[1] + r43 = r48 + r49 = r47[0] + if r49 goto L17 else goto L19 :: bool +L17: + r50 = r47[2] + r51 = r47[3] + r52 = cast(str, r50) + r53 = cast(union[int, str], r51) + k = r52 + v = r53 + r54 = CPyDict_SetItem(r42, k, v) + r55 = r54 >= 0 :: signed +L18: + r56 = CPyDict_CheckSize(m, r45) + goto L16 +L19: + r57 = CPy_NoErrOccurred() +L20: + return 1 +def fn_typeddict(t): + t :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: short_int + r4 :: object + r5 :: tuple[bool, short_int, object] + r6 :: short_int + r7 :: bool + r8 :: object + r9, x :: str + r10 :: i32 + r11, r12, r13 :: bit + r14 :: list + r15 :: short_int + r16 :: native_int + r17 :: short_int + r18 :: object + r19 :: tuple[bool, short_int, object] + r20 :: short_int + r21 :: bool + r22, x_2 :: object + r23 :: i32 + r24, r25, r26 :: bit + r27 :: set + r28 :: short_int + r29 :: native_int + r30 :: short_int + r31 :: object + r32 :: tuple[bool, short_int, object] + r33 :: short_int + r34 :: bool + r35 :: object + r36, x_3 :: str + r37 :: i32 + r38, r39, r40 :: bit + r41 :: dict + r42 :: short_int + r43 :: native_int + r44 :: short_int + r45 :: object + r46 :: tuple[bool, short_int, object, object] + r47 :: short_int + r48 :: bool + r49, r50 :: object + r51, k :: str + v :: object + r52 :: i32 + r53, r54, r55 :: bit +L0: + r0 = PyList_New(0) + r1 = 0 + r2 = PyDict_Size(t) + r3 = r2 << 1 + r4 = CPyDict_GetKeysIter(t) +L1: + r5 = CPyDict_NextKey(r4, r1) + r6 = r5[1] + r1 = r6 + r7 = r5[0] + if r7 goto L2 else goto L4 :: bool +L2: + r8 = r5[2] + r9 = cast(str, r8) + x = r9 + r10 = PyList_Append(r0, x) + r11 = r10 >= 0 :: signed +L3: + r12 = CPyDict_CheckSize(t, r3) + goto L1 +L4: + r13 = CPy_NoErrOccurred() +L5: + r14 = PyList_New(0) + r15 = 0 + r16 = PyDict_Size(t) + r17 = r16 << 1 + r18 = CPyDict_GetValuesIter(t) +L6: + r19 = CPyDict_NextValue(r18, r15) + r20 = r19[1] + r15 = r20 + r21 = r19[0] + if r21 goto L7 else goto L9 :: bool +L7: + r22 = r19[2] + x_2 = r22 + r23 = PyList_Append(r14, x_2) + r24 = r23 >= 0 :: signed +L8: + r25 = CPyDict_CheckSize(t, r17) + goto L6 +L9: + r26 = CPy_NoErrOccurred() +L10: + r27 = PySet_New(0) + r28 = 0 + r29 = PyDict_Size(t) + r30 = r29 << 1 + r31 = CPyDict_GetKeysIter(t) +L11: + r32 = CPyDict_NextKey(r31, r28) + r33 = r32[1] + r28 = r33 + r34 = r32[0] + if r34 goto L12 else goto L14 :: bool +L12: + r35 = r32[2] + r36 = cast(str, r35) + x_3 = r36 + r37 = PySet_Add(r27, x_3) + r38 = r37 >= 0 :: signed +L13: + r39 = CPyDict_CheckSize(t, r30) + goto L11 +L14: + r40 = CPy_NoErrOccurred() +L15: + r41 = PyDict_New() + r42 = 0 + r43 = PyDict_Size(t) + r44 = r43 << 1 + r45 = CPyDict_GetItemsIter(t) +L16: + r46 = CPyDict_NextItem(r45, r42) + r47 = r46[1] + r42 = r47 + r48 = r46[0] + if r48 goto L17 else goto L19 :: bool +L17: + r49 = r46[2] + r50 = r46[3] + r51 = cast(str, r49) + k = r51 + v = r50 + r52 = CPyDict_SetItem(r41, k, v) + r53 = r52 >= 0 :: signed +L18: + r54 = CPyDict_CheckSize(t, r44) + goto L16 +L19: + r55 = CPy_NoErrOccurred() +L20: + return 1 + +[case testParamSpecComponentsAreUsable] +from typing import Callable, ParamSpec + +P = ParamSpec("P") + +def deco(func: Callable[P, int]) -> Callable[P, int]: + def inner(*args: P.args, **kwargs: P.kwargs) -> int: + can_listcomp = [x for x in args] + can_dictcomp = {k: v for k, v in kwargs.items()} + can_iter = list(kwargs) + can_use_keys = list(kwargs.keys()) + can_use_values = list(kwargs.values()) + return func(*args, **kwargs) + + return inner + +@deco +def f(x: int) -> int: + return x + +f(1) +[out] +def inner_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def inner_deco_obj.__call__(__mypyc_self__, args, kwargs): + __mypyc_self__ :: __main__.inner_deco_obj + args :: tuple + kwargs :: dict + r0 :: __main__.deco_env + r1 :: native_int + r2 :: list + r3 :: short_int + r4 :: native_int + r5 :: short_int + r6 :: bit + r7, x :: object + r8 :: bit + r9 :: short_int + can_listcomp :: list + r10 :: dict + r11 :: short_int + r12 :: native_int + r13 :: short_int + r14 :: object + r15 :: tuple[bool, short_int, object, object] + r16 :: short_int + r17 :: bool + r18, r19 :: object + r20, k :: str + v :: object + r21 :: i32 + r22, r23, r24 :: bit + can_dictcomp :: dict + r25, can_iter, r26, can_use_keys, r27, can_use_values :: list + r28 :: object + r29 :: list + r30 :: object + r31 :: dict + r32 :: i32 + r33 :: bit + r34 :: tuple + r35 :: object + r36 :: int +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = var_object_size args + r2 = PyList_New(r1) + r3 = 0 +L1: + r4 = var_object_size args + r5 = r4 << 1 + r6 = int_lt r3, r5 + if r6 goto L2 else goto L4 :: bool +L2: + r7 = CPySequenceTuple_GetItem(args, r3) + x = r7 + r8 = CPyList_SetItemUnsafe(r2, r3, x) +L3: + r9 = r3 + 2 + r3 = r9 + goto L1 +L4: + can_listcomp = r2 + r10 = PyDict_New() + r11 = 0 + r12 = PyDict_Size(kwargs) + r13 = r12 << 1 + r14 = CPyDict_GetItemsIter(kwargs) +L5: + r15 = CPyDict_NextItem(r14, r11) + r16 = r15[1] + r11 = r16 + r17 = r15[0] + if r17 goto L6 else goto L8 :: bool +L6: + r18 = r15[2] + r19 = r15[3] + r20 = cast(str, r18) + k = r20 + v = r19 + r21 = CPyDict_SetItem(r10, k, v) + r22 = r21 >= 0 :: signed +L7: + r23 = CPyDict_CheckSize(kwargs, r13) + goto L5 +L8: + r24 = CPy_NoErrOccurred() +L9: + can_dictcomp = r10 + r25 = PySequence_List(kwargs) + can_iter = r25 + r26 = CPyDict_Keys(kwargs) + can_use_keys = r26 + r27 = CPyDict_Values(kwargs) + can_use_values = r27 + r28 = r0.func + r29 = PyList_New(0) + r30 = CPyList_Extend(r29, args) + r31 = PyDict_New() + r32 = CPyDict_UpdateInDisplay(r31, kwargs) + r33 = r32 >= 0 :: signed + r34 = PyList_AsTuple(r29) + r35 = PyObject_Call(r28, r34, r31) + r36 = unbox(int, r35) + return r36 +def deco(func): + func :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.inner_deco_obj + r3 :: bool + inner :: object +L0: + r0 = deco_env() + r0.func = func; r1 = is_error + r2 = inner_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + inner = r2 + return inner +def f(x): + x :: int +L0: + return x diff --git a/mypyc/test-data/run-generics.test b/mypyc/test-data/run-generics.test new file mode 100644 index 000000000000..bc78a3b8ab86 --- /dev/null +++ b/mypyc/test-data/run-generics.test @@ -0,0 +1,111 @@ +[case testTypeVarMappingBound] +# Dicts are special-cased for efficient iteration. +from typing import Dict, TypedDict, TypeVar, Union + +class TD(TypedDict): + foo: int + +M = TypeVar("M", bound=Dict[str, int]) +U = TypeVar("U", bound=Union[Dict[str, int], Dict[str, str]]) +T = TypeVar("T", bound=TD) + +def fn_mapping(m: M) -> None: + print([x for x in m]) + print([x for x in m.values()]) + print([x for x in m.keys()]) + print({k: v for k, v in m.items()}) + +def fn_union(m: U) -> None: + print([x for x in m]) + print([x for x in m.values()]) + print([x for x in m.keys()]) + print({k: v for k, v in m.items()}) + +def fn_typeddict(t: T) -> None: + print([x for x in t]) + print([x for x in t.values()]) + print([x for x in t.keys()]) + print({k: v for k, v in t.items()}) + +fn_mapping({}) +print("=====") +fn_mapping({"a": 1, "b": 2}) +print("=====") + +fn_union({"a": 1, "b": 2}) +print("=====") +fn_union({"a": "1", "b": "2"}) +print("=====") + +orig: Union[Dict[str, int], Dict[str, str]] = {"a": 1, "b": 2} +fn_union(orig) +print("=====") + +td: TD = {"foo": 1} +fn_typeddict(td) +[typing fixtures/typing-full.pyi] +[out] +\[] +\[] +\[] +{} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['a', 'b'] +\['1', '2'] +\['a', 'b'] +{'a': '1', 'b': '2'} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['foo'] +\[1] +\['foo'] +{'foo': 1} + +[case testParamSpecComponentsAreUsable] +from typing import Callable +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +def deco(func: Callable[P, int]) -> Callable[P, int]: + def inner(*args: P.args, **kwargs: P.kwargs) -> int: + print([x for x in args]) + print({k: v for k, v in kwargs.items()}) + print(list(kwargs)) + print(list(kwargs.keys())) + print(list(kwargs.values())) + return func(*args, **kwargs) + + return inner + +@deco +def f(x: int, y: str) -> int: + return x + +assert f(1, 'a') == 1 +assert f(2, y='b') == 2 +[out] +\[1, 'a'] +{} +\[] +\[] +\[] +\[2] +{'y': 'b'} +\['y'] +\['y'] +\['b'] diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 35598b24bce8..f4798660079f 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -61,6 +61,7 @@ "run-classes.test", "run-traits.test", "run-generators.test", + "run-generics.test", "run-multimodule.test", "run-bench.test", "run-mypy-sim.test", From 6e218871aa55b8eb4f62c4c75210ce85e22ce3a2 Mon Sep 17 00:00:00 2001 From: Gene Parmesan Thomas <201852096+gopoto@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:30:19 -0700 Subject: [PATCH 1194/1617] Narrow tagged unions in match statements (#18791) Fixes #16286. --- This PR was generated by an AI system in collaboration with maintainers: @hauntsaninja --------- Signed-off-by: Gene Parmesan Thomas <201852096+gopoto@users.noreply.github.com> Signed-off-by: gopoto <201852096+gopoto@users.noreply.github.com> --- mypy/checker.py | 2 ++ test-data/unit/check-python310.test | 48 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 6d7e8fa215a1..c9e0dcec6bd0 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5527,6 +5527,8 @@ def visit_match_stmt(self, s: MatchStmt) -> None: pattern_map, else_map = conditional_types_to_typemaps( named_subject, pattern_type.type, pattern_type.rest_type ) + pattern_map = self.propagate_up_typemap_info(pattern_map) + else_map = self.propagate_up_typemap_info(else_map) self.remove_capture_conflicts(pattern_type.captures, inferred_types) self.push_type_map(pattern_map, from_assignment=False) if pattern_map: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 016f50552a5f..18554a3540e6 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -332,6 +332,54 @@ match [SubClass("a"), SubClass("b")]: reveal_type(rest) # N: Revealed type is "builtins.list[__main__.Example]" [builtins fixtures/tuple.pyi] +# Narrowing union-based values via a literal pattern on an indexed/attribute subject +# ------------------------------------------------------------------------------- +# Literal patterns against a union of types can be used to narrow the subject +# itself, not just the expression being matched. Previously, the patterns below +# failed to narrow the `d` variable, leading to errors for missing members; we +# now propagate the type information up to the parent. + +[case testMatchNarrowingUnionTypedDictViaIndex] +from typing import Literal, TypedDict + +class A(TypedDict): + tag: Literal["a"] + name: str + +class B(TypedDict): + tag: Literal["b"] + num: int + +d: A | B +match d["tag"]: + case "a": + reveal_type(d) # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['a'], 'name': builtins.str})" + reveal_type(d["name"]) # N: Revealed type is "builtins.str" + case "b": + reveal_type(d) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['b'], 'num': builtins.int})" + reveal_type(d["num"]) # N: Revealed type is "builtins.int" +[typing fixtures/typing-typeddict.pyi] + +[case testMatchNarrowingUnionClassViaAttribute] +from typing import Literal + +class A: + tag: Literal["a"] + name: str + +class B: + tag: Literal["b"] + num: int + +d: A | B +match d.tag: + case "a": + reveal_type(d) # N: Revealed type is "__main__.A" + reveal_type(d.name) # N: Revealed type is "builtins.str" + case "b": + reveal_type(d) # N: Revealed type is "__main__.B" + reveal_type(d.num) # N: Revealed type is "builtins.int" + [case testMatchSequenceUnion-skip] from typing import List, Union m: Union[List[List[str]], str] From bbd7a6cf5886ac7162f0f1cce5c34e89c807262e Mon Sep 17 00:00:00 2001 From: Paul Ganssle <67915935+pganssle-google@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:51:02 -0400 Subject: [PATCH 1195/1617] Support positional and keyword-only arguments in stubdoc (#18762) Currently the signature parsing logic fails when confronted with a `/` or a `*`, rather than recognizing them as demarcating positional-only and keyword-only arguments. This patch supports parsing signatures with these features, but doesn't pass this information along to the `ArgSig` or `FunctionSig` classes, since the information would not be used anyway. --- mypy/stubdoc.py | 51 +++++++++++-- mypy/test/teststubgen.py | 158 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 7 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 0da93b4e2477..617c5ecda408 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -175,6 +175,8 @@ def __init__(self, function_name: str) -> None: self.ret_type = "Any" self.found = False self.args: list[ArgSig] = [] + self.pos_only: int | None = None + self.keyword_only: int | None = None # Valid signatures found so far. self.signatures: list[FunctionSig] = [] @@ -252,15 +254,34 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.arg_type = self.accumulator self.state.pop() elif self.state[-1] == STATE_ARGUMENT_LIST: - self.arg_name = self.accumulator - if not ( - token.string == ")" and self.accumulator.strip() == "" - ) and not _ARG_NAME_RE.match(self.arg_name): - # Invalid argument name. - self.reset() - return + if self.accumulator == "*": + if self.keyword_only is not None: + # Error condition: cannot have * twice + self.reset() + return + self.keyword_only = len(self.args) + self.accumulator = "" + else: + if self.accumulator.startswith("*"): + self.keyword_only = len(self.args) + 1 + self.arg_name = self.accumulator + if not ( + token.string == ")" and self.accumulator.strip() == "" + ) and not _ARG_NAME_RE.match(self.arg_name): + # Invalid argument name. + self.reset() + return if token.string == ")": + if ( + self.state[-1] == STATE_ARGUMENT_LIST + and self.keyword_only is not None + and self.keyword_only == len(self.args) + and not self.arg_name + ): + # Error condition: * must be followed by arguments + self.reset() + return self.state.pop() # arg_name is empty when there are no args. e.g. func() @@ -280,6 +301,22 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.arg_type = None self.arg_default = None self.accumulator = "" + elif ( + token.type == tokenize.OP + and token.string == "/" + and self.state[-1] == STATE_ARGUMENT_LIST + ): + if token.string == "/": + if self.pos_only is not None or self.keyword_only is not None or not self.args: + # Error cases: + # - / shows up more than once + # - / shows up after * + # - / shows up before any arguments + self.reset() + return + self.pos_only = len(self.args) + self.state.append(STATE_ARGUMENT_TYPE) + self.accumulator = "" elif token.type == tokenize.OP and token.string == "->" and self.state[-1] == STATE_INIT: self.accumulator = "" diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 55b2fddd0548..43974cf8ec68 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -399,6 +399,164 @@ def test_infer_sig_from_docstring_bad_indentation(self) -> None: None, ) + def test_infer_sig_from_docstring_args_kwargs(self) -> None: + assert_equal( + infer_sig_from_docstring("func(*args, **kwargs) -> int", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="*args"), ArgSig(name="**kwargs")], + ret_type="int", + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(*args) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="*args")], ret_type="int")], + ) + + assert_equal( + infer_sig_from_docstring("func(**kwargs) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="**kwargs")], ret_type="int")], + ) + + @pytest.mark.xfail( + raises=AssertionError, reason="Arg and kwarg signature validation not implemented yet" + ) + def test_infer_sig_from_docstring_args_kwargs_errors(self) -> None: + # Double args + assert_equal(infer_sig_from_docstring("func(*args, *args2) -> int", "func"), []) + + # Double kwargs + assert_equal(infer_sig_from_docstring("func(**kw, **kw2) -> int", "func"), []) + + # args after kwargs + assert_equal(infer_sig_from_docstring("func(**kwargs, *args) -> int", "func"), []) + + def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(self, /) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="self")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(self, x, /) -> str", "func"), + [ + FunctionSig( + name="func", args=[ArgSig(name="self"), ArgSig(name="x")], ret_type="str" + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="int")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, *args) -> str", "func"), + [ + FunctionSig( + name="func", args=[ArgSig(name="x"), ArgSig(name="*args")], ret_type="str" + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, *, kwonly, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig(name="**kwargs")], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(*, x) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, *, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(*, x, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, *, kwonly, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig("**kwargs")], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(x, /, *, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y, *, z) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y"), ArgSig(name="z")], + ret_type="str", + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y, *, z, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ + ArgSig(name="x"), + ArgSig(name="y"), + ArgSig(name="z"), + ArgSig("**kwargs"), + ], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments_errors(self) -> None: + # / as first argument + assert_equal(infer_sig_from_docstring("func(/, x) -> str", "func"), []) + + # * as last argument + assert_equal(infer_sig_from_docstring("func(x, *) -> str", "func"), []) + + # / after * + assert_equal(infer_sig_from_docstring("func(x, *, /, y) -> str", "func"), []) + + # Two / + assert_equal(infer_sig_from_docstring("func(x, /, /, *, y) -> str", "func"), []) + + assert_equal(infer_sig_from_docstring("func(x, /, y, /, *, z) -> str", "func"), []) + + # Two * + assert_equal(infer_sig_from_docstring("func(x, /, *, *, y) -> str", "func"), []) + + assert_equal(infer_sig_from_docstring("func(x, /, *, y, *, z) -> str", "func"), []) + + # *args and * are not allowed + assert_equal(infer_sig_from_docstring("func(*args, *, kwonly) -> str", "func"), []) + def test_infer_arg_sig_from_anon_docstring(self) -> None: assert_equal( infer_arg_sig_from_anon_docstring("(*args, **kwargs)"), From 5c87e972d3c2c3c1f6229e1a655c4b903207dd32 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 16 Mar 2025 21:49:05 +0100 Subject: [PATCH 1196/1617] Fix dict.get issue for typeshed update (#18806) Fix for issue uncovered in #18803 --- mypy/partially_defined.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index da0bb517189a..38154cf697e1 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -45,7 +45,7 @@ from mypy.patterns import AsPattern, StarredPattern from mypy.reachability import ALWAYS_TRUE, infer_pattern_value from mypy.traverser import ExtendedTraverserVisitor -from mypy.types import Type, UninhabitedType +from mypy.types import Type, UninhabitedType, get_proper_type class BranchState: @@ -507,7 +507,8 @@ def visit_break_stmt(self, o: BreakStmt) -> None: self.tracker.skip_branch() def visit_expression_stmt(self, o: ExpressionStmt) -> None: - if isinstance(self.type_map.get(o.expr, None), (UninhabitedType, type(None))): + typ = self.type_map.get(o.expr) + if typ is None or isinstance(get_proper_type(typ), UninhabitedType): self.tracker.skip_branch() super().visit_expression_stmt(o) From 04a0fe8b8848df5c6585c1e060d8b55e429bc74d Mon Sep 17 00:00:00 2001 From: exertustfm <54768149+exertustfm@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:11:38 -0500 Subject: [PATCH 1197/1617] [mypyc] Add and implement primitive list.copy() (#18771) Closes https://github.com/mypyc/mypyc/issues/1092 --- mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/list_ops.c | 14 +++++++++ mypyc/primitives/list_ops.py | 9 ++++++ mypyc/test-data/fixtures/ir.py | 1 + mypyc/test-data/irbuild-lists.test | 12 ++++++++ mypyc/test-data/run-lists.test | 49 ++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index fda7ff4eb09c..7b192e747595 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -665,6 +665,7 @@ CPyTagged CPyList_Index(PyObject *list, PyObject *obj); PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size); PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq); PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); +PyObject *CPyList_Copy(PyObject *list); int CPySequence_Check(PyObject *obj); diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c index d297ece8f417..8388e1eea73a 100644 --- a/mypyc/lib-rt/list_ops.c +++ b/mypyc/lib-rt/list_ops.c @@ -29,6 +29,20 @@ PyObject *CPyList_Build(Py_ssize_t len, ...) { return res; } +PyObject *CPyList_Copy(PyObject *list) { + if(PyList_CheckExact(list)) { + return PyList_GetSlice(list, 0, PyList_GET_SIZE(list)); + } + _Py_IDENTIFIER(copy); + + PyObject *name = _PyUnicode_FromId(&PyId_copy); + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(list, name); +} + + PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); PyObject *result = PyList_GET_ITEM(list, n); diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index f3af17d3859e..a453e568f00f 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -262,6 +262,15 @@ error_kind=ERR_MAGIC, ) +# list.copy() +method_op( + name="copy", + arg_types=[list_rprimitive], + return_type=list_rprimitive, + c_function_name="CPyList_Copy", + error_kind=ERR_MAGIC, +) + # list * int binary_op( name="*", diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index e651e7adc384..b908b4c3fc1f 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -233,6 +233,7 @@ def sort(self) -> None: pass def reverse(self) -> None: pass def remove(self, o: _T) -> None: pass def index(self, o: _T) -> int: pass + def copy(self) -> List[_T]: pass class dict(Mapping[_K, _V]): @overload diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 56ad2d53b7eb..e2c656399821 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -182,6 +182,18 @@ L0: r1 = r0 << 1 return r1 +[case testListCopy] +from typing import List +from typing import Any +def f(a: List[Any]) -> List[Any]: + return a.copy() +[out] +def f(a): + a, r0 :: list +L0: + r0 = CPyList_Copy(a) + return r0 + [case testListAppend] from typing import List def f(a: List[int], x: int) -> None: diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test index 84d5ee121a20..3b2721093e0f 100644 --- a/mypyc/test-data/run-lists.test +++ b/mypyc/test-data/run-lists.test @@ -51,6 +51,55 @@ print(2, a) 1 [-1, 5] 2 [340282366920938463463374607431768211461, -170141183460469231731687303715884105736] +[case testListCopy] +from typing import List +from copysubclass import subc + +def test_list_copy() -> None: + l1 = [1, 2, 3, -4, 5] + l2 = l1.copy() + assert l1.copy() == l1 + assert l1.copy() == l2 + assert l1 == l2 + assert l1.copy() == l2.copy() + l1 = l2.copy() + assert l1 == l2 + assert l1.copy() == l2 + assert l1 == [1, 2, 3, -4, 5] + l2 = [1, 2, -3] + l1 = [] + assert l1.copy() == [] + assert l2.copy() != l1 + assert l2 == l2.copy() + l1 = l2 + assert l1.copy().copy() == l2.copy().copy().copy() + assert l1.copy() == l2.copy() + l1 == [1, 2, -3].copy() + assert l1 == l2 + l2 = [1, 2, 3].copy() + assert l2 != l1 + l1 = [1, 2, 3] + assert l1.copy() == l2.copy() + l3 = [1, 2 , 3, "abcdef"] + assert l3 == l3.copy() + l4 = ["abc", 5, 10] + l4 = l3.copy() + assert l4 == l3 + #subclass testing + l5: subc = subc([1, 2, 3]) + l6 = l5.copy() + assert l6 == l5 + l6 = [1, 2, "3", 4, 5] + l5 = subc([1,2,"3",4,5]) + assert l5.copy() == l6.copy() + l6 = l5.copy() + assert l5 == l6 + +[file copysubclass.py] +from typing import Any +class subc(list[Any]): + pass + [case testSieve] from typing import List From 34e8c7c5fa09516fbed37cad80b161906e97f53a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 19 Mar 2025 10:59:13 +0000 Subject: [PATCH 1198/1617] Add flag to allow more flexible variable redefinition (#18727) Infer union types for simple variables from multiple assignments, if the variable isn't annotated. The feature is enabled via `--allow-redefinition-new`. `--local-partial-types` must also be enabled. This is still experimental and has known issues, so it's not documented anywhere. It works well enough that it can be used for non-trivial experimentation, however. Closes #6233. Closes #6232. Closes #18568. Fixes #18619. In this example, the type of `x` is inferred as `int | str` when using the new behavior: ```py def f(i: int, s : str) -> int | str: if i > 5: x = i else: x = s # No longer an error reveal_type(x) # int | str return s ``` Here is a summary of how it works: * Assignment widens the inferred type of a variable and always narrows (when there is no annotation). * Simple variable lvalues are put into the binder on initial assignment when using the new feature. We need to be able to track whether a variable is defined or not to infer correct types (see #18619). * Assignment of `None` values are no longer special, and we don't use partial None if the feature is enabled for simple variables. * Lvalues other than simple variables (e.g. `self.x`) continue to work as in the past. Attribute types can't be widened, since they are externally visible and widening could cause confusion, but this is something we might relax in the future. Globals can be widened, however. This seems necessary for consistency. * If a loop body widens a variable type, we have to analyze the body again. However, we only do one extra pass, since the inferred type could be expanded without bound (consider `x = 0` outside loop and `x = [x]` within the loop body). * We first infer the type of an rvalue without using the lvalue type as context, as otherwise the type context would often prevent redefinition. If the rvalue type isn't valid for inference (e.g. list item type can't be inferred), we fall back to the lvalue type context. There are some other known bugs and limitations: * Annotated variables can't be freely redefined (but they can still be narrowed, of course). I may want to relax this in the future, but I'm not sure yet. * If there is a function definition between assignments to a variable, the inferred types may be incorrect. * There are few tests for `nonlocal` and some other features. We don't have good test coverage for deferrals, mypy daemon, and disabling strict optional. * Imported names can't be redefined in a consistent way. This needs further analysis. In self check the feature generates 6 additional errors, which all seem correct -- we infer more precise types, which will generate additional errors due to invariant containers and fixing false negatives. When type checking the largest internal codebase at Dropbox, this generated about 700 new errors, the vast majority of which seemed legitimate. Mostly they were due to inferring more precise types for variables that used to have `Any` types. I used a recent but not the latest version of the feature to type check the internal codebase. --- mypy/binder.py | 27 +- mypy/build.py | 6 +- mypy/checker.py | 222 ++++- mypy/checkexpr.py | 23 +- mypy/main.py | 17 +- mypy/nodes.py | 7 +- mypy/options.py | 5 + mypy/plugins/functools.py | 4 +- mypy/semanal.py | 11 +- test-data/unit/check-incremental.test | 11 + test-data/unit/check-python310.test | 38 + test-data/unit/check-redefine2.test | 1193 +++++++++++++++++++++++++ 12 files changed, 1510 insertions(+), 54 deletions(-) create mode 100644 test-data/unit/check-redefine2.test diff --git a/mypy/binder.py b/mypy/binder.py index 384bdca728b2..d3482d1dad4f 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -7,8 +7,9 @@ from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values -from mypy.literals import Key, literal, literal_hash, subkeys +from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash, subkeys from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var +from mypy.options import Options from mypy.subtypes import is_same_type, is_subtype from mypy.typeops import make_simplified_union from mypy.types import ( @@ -39,6 +40,7 @@ class CurrentType(NamedTuple): class Frame: """A Frame represents a specific point in the execution of a program. + It carries information about the current types of expressions at that point, arising either from assignments to those expressions or the result of isinstance checks and other type narrowing @@ -97,7 +99,7 @@ class A: # This maps an expression to a list of bound types for every item in the union type. type_assignments: Assigns | None = None - def __init__(self) -> None: + def __init__(self, options: Options) -> None: # Each frame gets an increasing, distinct id. self.next_id = 1 @@ -131,6 +133,11 @@ def __init__(self) -> None: self.break_frames: list[int] = [] self.continue_frames: list[int] = [] + # If True, initial assignment to a simple variable (e.g. "x", but not "x.y") + # is added to the binder. This allows more precise narrowing and more + # flexible inference of variable types (--allow-redefinition-new). + self.bind_all = options.allow_redefinition_new + def _get_id(self) -> int: self.next_id += 1 return self.next_id @@ -226,12 +233,20 @@ def update_from_options(self, frames: list[Frame]) -> bool: for key in keys: current_value = self._get(key) resulting_values = [f.types.get(key, current_value) for f in frames] - if any(x is None for x in resulting_values): + # Keys can be narrowed using two different semantics. The new semantics + # is enabled for plain variables when bind_all is true, and it allows + # variable types to be widened using subsequent assignments. This is + # tricky to support for instance attributes (primarily due to deferrals), + # so we don't use it for them. + old_semantics = not self.bind_all or extract_var_from_literal_hash(key) is None + if old_semantics and any(x is None for x in resulting_values): # We didn't know anything about key before # (current_value must be None), and we still don't # know anything about key in at least one possible frame. continue + resulting_values = [x for x in resulting_values if x is not None] + if all_reachable and all( x is not None and not x.from_assignment for x in resulting_values ): @@ -278,7 +293,11 @@ def update_from_options(self, frames: list[Frame]) -> bool: # still equivalent to such type). if isinstance(type, UnionType): type = collapse_variadic_union(type) - if isinstance(type, ProperType) and isinstance(type, UnionType): + if ( + old_semantics + and isinstance(type, ProperType) + and isinstance(type, UnionType) + ): # Simplify away any extra Any's that were added to the declared # type when popping a frame. simplified = UnionType.make_union( diff --git a/mypy/build.py b/mypy/build.py index f6272ed808cf..355ba861385e 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2240,8 +2240,10 @@ def semantic_analysis_pass1(self) -> None: # TODO: Do this while constructing the AST? self.tree.names = SymbolTable() if not self.tree.is_stub: - # Always perform some low-key variable renaming - self.tree.accept(LimitedVariableRenameVisitor()) + if not self.options.allow_redefinition_new: + # Perform some low-key variable renaming when assignments can't + # widen inferred types + self.tree.accept(LimitedVariableRenameVisitor()) if options.allow_redefinition: # Perform more renaming across the AST to allow variable redefinitions self.tree.accept(VariableRenameVisitor()) diff --git a/mypy/checker.py b/mypy/checker.py index c9e0dcec6bd0..2c15970b8b15 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -316,6 +316,9 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): # Vars for which partial type errors are already reported # (to avoid logically duplicate errors with different error context). partial_reported: set[Var] + # Short names of Var nodes whose previous inferred type has been widened via assignment. + # NOTE: The names might not be unique, they are only for debugging purposes. + widened_vars: list[str] globals: SymbolTable modules: dict[str, MypyFile] # Nodes that couldn't be checked because some types weren't available. We'll run @@ -376,7 +379,7 @@ def __init__( self.plugin = plugin self.tscope = Scope() self.scope = CheckerScope(tree) - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(options) self.globals = tree.names self.return_types = [] self.dynamic_funcs = [] @@ -384,6 +387,7 @@ def __init__( self.partial_reported = set() self.var_decl_frames = {} self.deferred_nodes = [] + self.widened_vars = [] self._type_maps = [{}] self.module_refs = set() self.pass_num = 0 @@ -430,7 +434,7 @@ def reset(self) -> None: # TODO: verify this is still actually worth it over creating new checkers self.partial_reported.clear() self.module_refs.clear() - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) self._type_maps[1:] = [] self._type_maps[0].clear() self.temp_type_map = None @@ -523,6 +527,7 @@ def check_second_pass( return True def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> None: + self.widened_vars = [] if isinstance(node, MypyFile): self.check_top_level(node) else: @@ -592,6 +597,10 @@ def accept_loop( # Check for potential decreases in the number of partial types so as not to stop the # iteration too early: partials_old = sum(len(pts.map) for pts in self.partial_types) + # Check if assignment widened the inferred type of a variable; in this case we + # need to iterate again (we only do one extra iteration, since this could go + # on without bound otherwise) + widened_old = len(self.widened_vars) # Disable error types that we cannot safely identify in intermediate iteration steps: warn_unreachable = self.options.warn_unreachable @@ -599,6 +608,7 @@ def accept_loop( self.options.warn_unreachable = False self.options.enabled_error_codes.discard(codes.REDUNDANT_EXPR) + iter = 1 while True: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): if on_enter_body is not None: @@ -606,9 +616,24 @@ def accept_loop( self.accept(body) partials_new = sum(len(pts.map) for pts in self.partial_types) - if (partials_new == partials_old) and not self.binder.last_pop_changed: + widened_new = len(self.widened_vars) + # Perform multiple iterations if something changed that might affect + # inferred types. Also limit the number of iterations. The limits are + # somewhat arbitrary, but they were chosen to 1) avoid slowdown from + # multiple iterations in common cases and 2) support common, valid use + # cases. Limits are needed since otherwise we could infer infinitely + # complex types. + if ( + (partials_new == partials_old) + and (not self.binder.last_pop_changed or iter > 3) + and (widened_new == widened_old or iter > 1) + ): break partials_old = partials_new + widened_old = widened_new + iter += 1 + if iter == 20: + raise RuntimeError("Too many iterations when checking a loop") # If necessary, reset the modified options and make up for the postponed error checks: self.options.warn_unreachable = warn_unreachable @@ -1218,7 +1243,7 @@ def check_func_def( original_typ = typ for item, typ in expanded: old_binder = self.binder - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) with self.binder.top_frame_context(): defn.expanded.append(item) @@ -1406,6 +1431,17 @@ def check_func_def( new_frame = self.binder.push_frame() new_frame.types[key] = narrowed_type self.binder.declarations[key] = old_binder.declarations[key] + + if self.options.allow_redefinition_new and not self.is_stub: + # Add formal argument types to the binder. + for arg in defn.arguments: + # TODO: Add these directly using a fast path (possibly "put") + v = arg.variable + if v.type is not None: + n = NameExpr(v.name) + n.node = v + self.binder.assign_type(n, v.type, v.type) + with self.scope.push_function(defn): # We suppress reachability warnings for empty generator functions # (return; yield) which have a "yield" that's unreachable by definition @@ -2591,7 +2627,7 @@ def visit_class_def(self, defn: ClassDef) -> None: self.fail(message_registry.CANNOT_INHERIT_FROM_FINAL.format(base.name), defn) with self.tscope.class_scope(defn.info), self.enter_partial_types(is_class=True): old_binder = self.binder - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) with self.binder.top_frame_context(): with self.scope.push_class(defn.info): self.accept(defn.defs) @@ -3249,7 +3285,9 @@ def check_assignment( return var = lvalue_type.var - if is_valid_inferred_type(rvalue_type, is_lvalue_final=var.is_final): + if is_valid_inferred_type( + rvalue_type, self.options, is_lvalue_final=var.is_final + ): partial_types = self.find_partial_types(var) if partial_types is not None: if not self.current_node_deferred: @@ -3295,7 +3333,8 @@ def check_assignment( # unpleasant, and a generalization of this would # be an improvement! if ( - is_literal_none(rvalue) + not self.options.allow_redefinition_new + and is_literal_none(rvalue) and isinstance(lvalue, NameExpr) and lvalue.kind == LDEF and isinstance(lvalue.node, Var) @@ -3315,7 +3354,12 @@ def check_assignment( lvalue_type = make_optional_type(lvalue_type) self.set_inferred_type(lvalue.node, lvalue, lvalue_type) - rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, context=rvalue) + rvalue_type, lvalue_type = self.check_simple_assignment( + lvalue_type, rvalue, context=rvalue, inferred=inferred, lvalue=lvalue + ) + # The above call may update inferred variable type. Prevent further + # inference. + inferred = None # Special case: only non-abstract non-protocol classes can be assigned to # variables with explicit type Type[A], where A is protocol or abstract. @@ -3348,6 +3392,9 @@ def check_assignment( and lvalue_type is not None ): lvalue.node.type = remove_instance_last_known_values(lvalue_type) + elif self.options.allow_redefinition_new and lvalue_type is not None: + # TODO: Can we use put() here? + self.binder.assign_type(lvalue, lvalue_type, lvalue_type) elif index_lvalue: self.check_indexed_assignment(index_lvalue, rvalue, lvalue) @@ -3429,7 +3476,9 @@ def try_infer_partial_generic_type_from_assignment( rvalue_type = self.expr_checker.accept(rvalue) rvalue_type = get_proper_type(rvalue_type) if isinstance(rvalue_type, Instance): - if rvalue_type.type == typ.type and is_valid_inferred_type(rvalue_type): + if rvalue_type.type == typ.type and is_valid_inferred_type( + rvalue_type, self.options + ): var.type = rvalue_type del partial_types[var] elif isinstance(rvalue_type, AnyType): @@ -4313,6 +4362,12 @@ def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, V self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, NameExpr): lvalue_type = self.expr_checker.analyze_ref_expr(lvalue, lvalue=True) + if ( + self.options.allow_redefinition_new + and isinstance(lvalue.node, Var) + and lvalue.node.is_inferred + ): + inferred = lvalue.node self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, (TupleExpr, ListExpr)): types = [ @@ -4353,14 +4408,19 @@ def infer_variable_type( if isinstance(init_type, DeletedType): self.msg.deleted_as_rvalue(init_type, context) elif ( - not is_valid_inferred_type(init_type, is_lvalue_final=name.is_final) + not is_valid_inferred_type( + init_type, + self.options, + is_lvalue_final=name.is_final, + is_lvalue_member=isinstance(lvalue, MemberExpr), + ) and not self.no_partial_types ): # We cannot use the type of the initialization expression for full type # inference (it's not specific enough), but we might be able to give # partial type which will be made more specific later. A partial type # gets generated in assignment like 'x = []' where item type is not known. - if not self.infer_partial_type(name, lvalue, init_type): + if name.name != "_" and not self.infer_partial_type(name, lvalue, init_type): self.msg.need_annotation_for_var(name, context, self.options.python_version) self.set_inference_error_fallback_type(name, lvalue, init_type) elif ( @@ -4380,10 +4440,16 @@ def infer_variable_type( init_type = strip_type(init_type) self.set_inferred_type(name, lvalue, init_type) + if self.options.allow_redefinition_new: + self.binder.assign_type(lvalue, init_type, init_type) def infer_partial_type(self, name: Var, lvalue: Lvalue, init_type: Type) -> bool: init_type = get_proper_type(init_type) - if isinstance(init_type, NoneType): + if isinstance(init_type, NoneType) and ( + isinstance(lvalue, MemberExpr) or not self.options.allow_redefinition_new + ): + # When using --allow-redefinition-new, None types aren't special + # when inferring simple variable types. partial_type = PartialType(None, name) elif isinstance(init_type, Instance): fullname = init_type.type.fullname @@ -4513,17 +4579,64 @@ def check_simple_assignment( rvalue_name: str = "expression", *, notes: list[str] | None = None, - ) -> Type: + lvalue: Expression | None = None, + inferred: Var | None = None, + ) -> tuple[Type, Type | None]: if self.is_stub and isinstance(rvalue, EllipsisExpr): # '...' is always a valid initializer in a stub. - return AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.special_form), lvalue_type else: always_allow_any = lvalue_type is not None and not isinstance( get_proper_type(lvalue_type), AnyType ) + if inferred is None or is_typeddict_type_context(lvalue_type): + type_context = lvalue_type + else: + type_context = None rvalue_type = self.expr_checker.accept( - rvalue, lvalue_type, always_allow_any=always_allow_any + rvalue, type_context=type_context, always_allow_any=always_allow_any ) + if ( + lvalue_type is not None + and type_context is None + and not is_valid_inferred_type(rvalue_type, self.options) + ): + # Inference in an empty type context didn't produce a valid type, so + # try using lvalue type as context instead. + rvalue_type = self.expr_checker.accept( + rvalue, type_context=lvalue_type, always_allow_any=always_allow_any + ) + if not is_valid_inferred_type(rvalue_type, self.options) and inferred is not None: + self.msg.need_annotation_for_var( + inferred, context, self.options.python_version + ) + rvalue_type = rvalue_type.accept(SetNothingToAny()) + + if ( + isinstance(lvalue, NameExpr) + and inferred is not None + and inferred.type is not None + and not inferred.is_final + ): + new_inferred = remove_instance_last_known_values(rvalue_type) + if not is_same_type(inferred.type, new_inferred): + # Should we widen the inferred type or the lvalue? Variables defined + # at module level or class bodies can't be widened in functions, or + # in another module. + if not self.refers_to_different_scope(lvalue): + lvalue_type = make_simplified_union([inferred.type, new_inferred]) + if not is_same_type(lvalue_type, inferred.type) and not isinstance( + inferred.type, PartialType + ): + # Widen the type to the union of original and new type. + self.widened_vars.append(inferred.name) + self.set_inferred_type(inferred, lvalue, lvalue_type) + self.binder.put(lvalue, rvalue_type) + # TODO: A bit hacky, maybe add a binder method that does put and + # updates declaration? + lit = literal_hash(lvalue) + if lit is not None: + self.binder.declarations[lit] = lvalue_type if ( isinstance(get_proper_type(lvalue_type), UnionType) # Skip literal types, as they have special logic (for better errors). @@ -4543,7 +4656,7 @@ def check_simple_assignment( not local_errors.has_new_errors() # Skip Any type, since it is special cased in binder. and not isinstance(get_proper_type(alt_rvalue_type), AnyType) - and is_valid_inferred_type(alt_rvalue_type) + and is_valid_inferred_type(alt_rvalue_type, self.options) and is_proper_subtype(alt_rvalue_type, rvalue_type) ): rvalue_type = alt_rvalue_type @@ -4563,7 +4676,19 @@ def check_simple_assignment( f"{lvalue_name} has type", notes=notes, ) - return rvalue_type + return rvalue_type, lvalue_type + + def refers_to_different_scope(self, name: NameExpr) -> bool: + if name.kind == LDEF: + # TODO: Consider reference to outer function as a different scope? + return False + elif self.scope.top_level_function() is not None: + # A non-local reference from within a function must refer to a different scope + return True + elif name.kind == GDEF and name.fullname.rpartition(".")[0] != self.tree.fullname: + # Reference to global definition from another module + return True + return False def check_member_assignment( self, @@ -4590,7 +4715,7 @@ def check_member_assignment( if (isinstance(instance_type, FunctionLike) and instance_type.is_type_obj()) or isinstance( instance_type, TypeType ): - rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) + rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context) return rvalue_type, attribute_type, True with self.msg.filter_errors(filter_deprecated=True): @@ -4601,7 +4726,7 @@ def check_member_assignment( if not isinstance(attribute_type, Instance): # TODO: support __set__() for union types. - rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) + rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context) return rvalue_type, attribute_type, use_binder mx = MemberContext( @@ -4620,7 +4745,7 @@ def check_member_assignment( # the return type of __get__. This doesn't match the python semantics, # (which allow you to override the descriptor with any value), but preserves # the type of accessing the attribute (even after the override). - rvalue_type = self.check_simple_assignment(get_type, rvalue, context) + rvalue_type, _ = self.check_simple_assignment(get_type, rvalue, context) return rvalue_type, get_type, use_binder dunder_set = attribute_type.type.get_method("__set__") @@ -4696,7 +4821,7 @@ def check_member_assignment( # and '__get__' type is narrower than '__set__', then we invoke the binder to narrow type # by this assignment. Technically, this is not safe, but in practice this is # what a user expects. - rvalue_type = self.check_simple_assignment(set_type, rvalue, context) + rvalue_type, _ = self.check_simple_assignment(set_type, rvalue, context) infer = is_subtype(rvalue_type, get_type) and is_subtype(get_type, set_type) return rvalue_type if infer else set_type, get_type, infer @@ -4726,6 +4851,19 @@ def check_indexed_assignment( if isinstance(res_type, UninhabitedType) and not res_type.ambiguous: self.binder.unreachable() + def replace_partial_type( + self, var: Var, new_type: Type, partial_types: dict[Var, Context] + ) -> None: + """Replace the partial type of var with a non-partial type.""" + var.type = new_type + del partial_types[var] + if self.options.allow_redefinition_new: + # When using --allow-redefinition-new, binder tracks all types of + # simple variables. + n = NameExpr(var.name) + n.node = var + self.binder.assign_type(n, new_type, new_type) + def try_infer_partial_type_from_indexed_assignment( self, lvalue: IndexExpr, rvalue: Expression ) -> None: @@ -4753,8 +4891,8 @@ def try_infer_partial_type_from_indexed_assignment( key_type = self.expr_checker.accept(lvalue.index) value_type = self.expr_checker.accept(rvalue) if ( - is_valid_inferred_type(key_type) - and is_valid_inferred_type(value_type) + is_valid_inferred_type(key_type, self.options) + and is_valid_inferred_type(value_type, self.options) and not self.current_node_deferred and not ( typename == "collections.defaultdict" @@ -4762,8 +4900,8 @@ def try_infer_partial_type_from_indexed_assignment( and not is_equivalent(value_type, var.type.value_type) ) ): - var.type = self.named_generic_type(typename, [key_type, value_type]) - del partial_types[var] + new_type = self.named_generic_type(typename, [key_type, value_type]) + self.replace_partial_type(var, new_type, partial_types) def type_requires_usage(self, typ: Type) -> tuple[str, ErrorCode] | None: """Some types require usage in all cases. The classic example is @@ -5087,8 +5225,13 @@ def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None: # try/except block. source = var.name if isinstance(var.node, Var): - var.node.type = DeletedType(source=source) - self.binder.cleanse(var) + new_type = DeletedType(source=source) + var.node.type = new_type + if self.options.allow_redefinition_new: + # TODO: Should we use put() here? + self.binder.assign_type(var, new_type, new_type) + if not self.options.allow_redefinition_new: + self.binder.cleanse(var) if s.else_body: self.accept(s.else_body) @@ -5488,11 +5631,13 @@ def visit_match_stmt(self, s: MatchStmt) -> None: # Create a dummy subject expression to handle cases where a match statement's subject # is not a literal value. This lets us correctly narrow types and check exhaustivity # This is hack! - id = s.subject.callee.fullname if isinstance(s.subject.callee, RefExpr) else "" - name = "dummy-match-" + id - v = Var(name) - named_subject = NameExpr(name) - named_subject.node = v + if s.subject_dummy is None: + id = s.subject.callee.fullname if isinstance(s.subject.callee, RefExpr) else "" + name = "dummy-match-" + id + v = Var(name) + s.subject_dummy = NameExpr(name) + s.subject_dummy.node = v + named_subject = s.subject_dummy else: named_subject = s.subject @@ -8515,7 +8660,9 @@ def _find_inplace_method(inst: Instance, method: str, operator: str) -> str | No return None -def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: +def is_valid_inferred_type( + typ: Type, options: Options, is_lvalue_final: bool = False, is_lvalue_member: bool = False +) -> bool: """Is an inferred type valid and needs no further refinement? Examples of invalid types include the None type (when we are not assigning @@ -8534,7 +8681,7 @@ def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: # type could either be NoneType or an Optional type, depending on # the context. This resolution happens in leave_partial_types when # we pop a partial types scope. - return is_lvalue_final + return is_lvalue_final or (not is_lvalue_member and options.allow_redefinition_new) elif isinstance(proper_type, UninhabitedType): return False return not typ.accept(InvalidInferredTypes()) @@ -9138,3 +9285,10 @@ def _ambiguous_enum_variants(types: list[Type]) -> set[str]: else: result.add("") return result + + +def is_typeddict_type_context(lvalue_type: Type | None) -> bool: + if lvalue_type is None: + return False + lvalue_proper = get_proper_type(lvalue_type) + return isinstance(lvalue_proper, TypedDictType) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 1017009ce7ab..80471a04469c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1117,8 +1117,7 @@ def try_infer_partial_type(self, e: CallExpr) -> None: typ = self.try_infer_partial_value_type_from_call(e, callee.name, var) # Var may be deleted from partial_types in try_infer_partial_value_type_from_call if typ is not None and var in partial_types: - var.type = typ - del partial_types[var] + self.chk.replace_partial_type(var, typ, partial_types) elif isinstance(callee.expr, IndexExpr) and isinstance(callee.expr.base, RefExpr): # Call 'x[y].method(...)'; may infer type of 'x' if it's a partial defaultdict. if callee.expr.analyzed is not None: @@ -1136,12 +1135,12 @@ def try_infer_partial_type(self, e: CallExpr) -> None: if value_type is not None: # Infer key type. key_type = self.accept(index) - if mypy.checker.is_valid_inferred_type(key_type): + if mypy.checker.is_valid_inferred_type(key_type, self.chk.options): # Store inferred partial type. assert partial_type.type is not None typename = partial_type.type.fullname - var.type = self.chk.named_generic_type(typename, [key_type, value_type]) - del partial_types[var] + new_type = self.chk.named_generic_type(typename, [key_type, value_type]) + self.chk.replace_partial_type(var, new_type, partial_types) def get_partial_var(self, ref: RefExpr) -> tuple[Var, dict[Var, Context]] | None: var = ref.node @@ -1176,7 +1175,7 @@ def try_infer_partial_value_type_from_call( and e.arg_kinds == [ARG_POS] ): item_type = self.accept(e.args[0]) - if mypy.checker.is_valid_inferred_type(item_type): + if mypy.checker.is_valid_inferred_type(item_type, self.chk.options): return self.chk.named_generic_type(typename, [item_type]) elif ( typename in self.container_args @@ -1188,7 +1187,7 @@ def try_infer_partial_value_type_from_call( arg_typename = arg_type.type.fullname if arg_typename in self.container_args[typename][methodname]: if all( - mypy.checker.is_valid_inferred_type(item_type) + mypy.checker.is_valid_inferred_type(item_type, self.chk.options) for item_type in arg_type.args ): return self.chk.named_generic_type(typename, list(arg_type.args)) @@ -5787,6 +5786,14 @@ def check_for_comp(self, e: GeneratorExpr | DictionaryComprehension) -> None: _, sequence_type = self.chk.analyze_async_iterable_item_type(sequence) else: _, sequence_type = self.chk.analyze_iterable_item_type(sequence) + if ( + isinstance(get_proper_type(sequence_type), UninhabitedType) + and isinstance(index, NameExpr) + and index.name == "_" + ): + # To preserve backward compatibility, avoid inferring Never for "_" + sequence_type = AnyType(TypeOfAny.special_form) + self.chk.analyze_index_variables(index, sequence_type, True, e) for condition in conditions: self.accept(condition) @@ -5830,7 +5837,7 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F else_map, e.else_expr, context=ctx, allow_none_return=allow_none_return ) - if not mypy.checker.is_valid_inferred_type(if_type): + if not mypy.checker.is_valid_inferred_type(if_type, self.chk.options): # Analyze the right branch disregarding the left branch. else_type = full_context_else_type # we want to keep the narrowest value of else_type for union'ing the branches diff --git a/mypy/main.py b/mypy/main.py index 77d8cefe9866..ad836a5ddc19 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -93,6 +93,13 @@ def main( stdout, stderr, options.hide_error_codes, hide_success=bool(options.output) ) + if options.allow_redefinition_new and not options.local_partial_types: + fail( + "error: --local-partial-types must be enabled if using --allow-redefinition-new", + stderr, + options, + ) + if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr): # Since --install-types performs user input, we want regular stdout and stderr. fail("error: --install-types not supported in this mode of running mypy", stderr, options) @@ -856,7 +863,15 @@ def add_invertible_flag( "--allow-redefinition", default=False, strict_flag=False, - help="Allow unconditional variable redefinition with a new type", + help="Allow restricted, unconditional variable redefinition with a new type", + group=strictness_group, + ) + + add_invertible_flag( + "--allow-redefinition-new", + default=False, + strict_flag=False, + help=argparse.SUPPRESS, # This is still very experimental group=strictness_group, ) diff --git a/mypy/nodes.py b/mypy/nodes.py index 10377eec07ba..ff31c3e27970 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1073,7 +1073,8 @@ def fullname(self) -> str: return self._fullname def __repr__(self) -> str: - return f"" + name = self.fullname or self.name + return f"" def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_var(self) @@ -1637,11 +1638,12 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class MatchStmt(Statement): - __slots__ = ("subject", "patterns", "guards", "bodies") + __slots__ = ("subject", "subject_dummy", "patterns", "guards", "bodies") __match_args__ = ("subject", "patterns", "guards", "bodies") subject: Expression + subject_dummy: NameExpr | None patterns: list[Pattern] guards: list[Expression | None] bodies: list[Block] @@ -1656,6 +1658,7 @@ def __init__( super().__init__() assert len(patterns) == len(guards) == len(bodies) self.subject = subject + self.subject_dummy = None self.patterns = patterns self.guards = guards self.bodies = bodies diff --git a/mypy/options.py b/mypy/options.py index c1047657dd77..27b583722568 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -22,6 +22,7 @@ class BuildType: PER_MODULE_OPTIONS: Final = { # Please keep this list sorted "allow_redefinition", + "allow_redefinition_new", "allow_untyped_globals", "always_false", "always_true", @@ -219,6 +220,10 @@ def __init__(self) -> None: # and the same nesting level as the initialization self.allow_redefinition = False + # Allow flexible variable redefinition with an arbitrary type, in different + # blocks and and at different nesting levels + self.allow_redefinition_new = False + # Prohibit equality, identity, and container checks for non-overlapping types. # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors. self.strict_equality = False diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index c435dde7fde7..25a8c83007ba 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -276,7 +276,7 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - for i, actuals in enumerate(formal_to_actual): if len(bound.arg_types) == len(fn_type.arg_types): arg_type = bound.arg_types[i] - if not mypy.checker.is_valid_inferred_type(arg_type): + if not mypy.checker.is_valid_inferred_type(arg_type, ctx.api.options): arg_type = fn_type.arg_types[i] # bit of a hack else: # TODO: I assume that bound and fn_type have the same arguments. It appears this isn't @@ -301,7 +301,7 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) - partial_names.append(fn_type.arg_names[i]) ret_type = bound.ret_type - if not mypy.checker.is_valid_inferred_type(ret_type): + if not mypy.checker.is_valid_inferred_type(ret_type, ctx.api.options): ret_type = fn_type.ret_type # same kind of hack as above partially_applied = fn_type.copy_modified( diff --git a/mypy/semanal.py b/mypy/semanal.py index c48b65f0ee94..a8a698c046f3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -658,6 +658,13 @@ def refresh_partial( def refresh_top_level(self, file_node: MypyFile) -> None: """Reanalyze a stale module top-level in fine-grained incremental mode.""" + if self.options.allow_redefinition_new and not self.options.local_partial_types: + n = TempNode(AnyType(TypeOfAny.special_form)) + n.line = 1 + n.column = 0 + n.end_line = 1 + n.end_column = 0 + self.fail("--local-partial-types must be enabled if using --allow-redefinition-new", n) self.recurse_into_functions = False self.add_implicit_module_attrs(file_node) for d in file_node.defs: @@ -4356,8 +4363,10 @@ def analyze_name_lvalue( else: lvalue.fullname = lvalue.name if self.is_func_scope(): - if unmangle(name) == "_": + if unmangle(name) == "_" and not self.options.allow_redefinition_new: # Special case for assignment to local named '_': always infer 'Any'. + # This isn't needed with --allow-redefinition-new, since arbitrary + # types can be assigned to '_' anyway. typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lvalue, typ) if is_final and self.is_final_redefinition(kind, name): diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 0c7e67e5444d..26ef6cb589ed 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6855,3 +6855,14 @@ from .lib import NT [builtins fixtures/tuple.pyi] [out] [out2] + +[case testNewRedefineAffectsCache] +# flags: --local-partial-types --allow-redefinition-new +# flags2: --local-partial-types +# flags3: --local-partial-types --allow-redefinition-new +x = 0 +if int(): + x = "" +[out] +[out2] +main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 18554a3540e6..3774abfc548b 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -2600,3 +2600,41 @@ def f(t: T) -> None: case T([K() as k]): reveal_type(k) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.K]" [builtins fixtures/tuple.pyi] + +[case testNewRedefineMatchBasics] +# flags: --allow-redefinition-new --local-partial-types + +def f1(x: int | str | list[bytes]) -> None: + match x: + case int(): + reveal_type(x) # N: Revealed type is "builtins.int" + case str(y): + reveal_type(y) # N: Revealed type is "builtins.str" + case [y]: + reveal_type(y) # N: Revealed type is "builtins.bytes" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.bytes]" + +[case testNewRedefineLoopWithMatch] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + while True: + x = object() + match x: + case str(y): + pass + case int(): + pass + if int(): + continue + +def f2() -> None: + for x in [""]: + match str(): + case "a": + y = "" + case "b": + y = 1 + return + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test new file mode 100644 index 000000000000..238b64399ce4 --- /dev/null +++ b/test-data/unit/check-redefine2.test @@ -0,0 +1,1193 @@ +-- Test cases for the redefinition of variable with a different type (new version). + +[case testNewRedefineLocalWithDifferentType] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + x = '' + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineConditionalLocalWithDifferentType] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + else: + x = '' + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineMergeConditionalLocal1] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + else: + x = '' + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if int(): + x = 0 + else: + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +[case testNewRedefineMergeConditionalLocal2] +# flags: --allow-redefinition-new --local-partial-types +def nested_ifs() -> None: + if int(): + if int(): + x = 0 + else: + x = '' + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + if int(): + x = None + else: + x = b"" + reveal_type(x) # N: Revealed type is "Union[None, builtins.bytes]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None, builtins.bytes]" + +[case testNewRedefineUninitializedCodePath1] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineUninitializedCodePath2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x: Union[int, str] = 0 + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineUninitializedCodePath3] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x = 0 + elif int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineUninitializedCodePath4] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x: Union[int, str] = 0 + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineUninitializedCodePath5] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + x = 0 + if int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +[case testNewRedefineUninitializedCodePath6] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +x: Union[str, None] + +def f1() -> None: + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" + +[case testNewRedefineGlobalVariableSimple] +# flags: --allow-redefinition-new --local-partial-types +if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" +else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f1() -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + global x + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineGlobalVariableNoneInit] +# flags: --allow-redefinition-new --local-partial-types +x = None + +def f() -> None: + global x + reveal_type(x) # N: Revealed type is "None" + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "None") + reveal_type(x) # N: Revealed type is "None" + +reveal_type(x) # N: Revealed type is "None" + +[case testNewRedefineParameterTypes] +# flags: --allow-redefinition-new --local-partial-types +from typing import Optional + +def f1(x: Optional[str] = None) -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" + if x is None: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2(*args: str, **kwargs: int) -> None: + reveal_type(args) # N: Revealed type is "builtins.tuple[builtins.str, ...]" + reveal_type(kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +class C: + def m(self) -> None: + reveal_type(self) # N: Revealed type is "__main__.C" +[builtins fixtures/dict.pyi] + + +[case testNewRedefineClassBody] +# flags: --allow-redefinition-new --local-partial-types +class C: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +reveal_type(C.x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineNestedFunctionBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + else: + x = "" + + def nested() -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if int(): + x = 0 + else: + x = "" + + def nested() -> None: + nonlocal x + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineLambdaBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = 0 + if int(): + x = None + f = lambda: reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(f) # N: Revealed type is "def () -> Union[builtins.int, None]" + if x is None: + x = "" + f = lambda: reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(f) # N: Revealed type is "def () -> Union[builtins.int, builtins.str]" + +[case testNewRedefineAssignmentExpression] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if x := int(): + reveal_type(x) # N: Revealed type is "builtins.int" + elif x := str(): + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if x := int(): + reveal_type(x) # N: Revealed type is "builtins.int" + elif x := str(): + reveal_type(x) # N: Revealed type is "builtins.str" + else: + pass + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f3() -> None: + if (x := int()) or (x := str()): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineOperatorAssignment] +# flags: --allow-redefinition-new --local-partial-types +class D: pass +class C: + def __add__(self, x: C) -> D: ... + +c = C() +if int(): + c += C() + reveal_type(c) # N: Revealed type is "__main__.D" +reveal_type(c) # N: Revealed type is "Union[__main__.C, __main__.D]" + +[case testNewRedefineImportFrom-xfail] +# flags: --allow-redefinition-new --local-partial-types +if int(): + from m import x +else: + # TODO: This could be useful to allow + from m import y as x # E: Incompatible import of "x" (imported name has type "str", local name has type "int") +reveal_type(x) # N: Revealed type is "builtins.int" + +if int(): + from m import y +else: + y = 1 +reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" + +[file m.py] +x = 1 +y = "" + +[case testNewRedefineImport] +# flags: --allow-redefinition-new --local-partial-types +if int(): + import m +else: + import m2 as m # E: Name "m" already defined (by an import) +m.x +m.y # E: Module has no attribute "y" + +[file m.py] +x = 1 + +[file m2.py] +y = "" +[builtins fixtures/module.pyi] + +[case testNewRedefineOptionalTypesSimple] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = None + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f2() -> None: + if int(): + x = None + elif int(): + x = "" + else: + x = 1 + reveal_type(x) # N: Revealed type is "Union[None, builtins.str, builtins.int]" + +def f3() -> None: + if int(): + x = None + else: + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f4() -> None: + x = None + reveal_type(x) # N: Revealed type is "None" + +y = None +if int(): + y = 1 +reveal_type(y) # N: Revealed type is "Union[None, builtins.int]" + +if int(): + z = None +elif int(): + z = 1 +else: + z = "" +reveal_type(z) # N: Revealed type is "Union[None, builtins.int, builtins.str]" + +[case testNewRedefinePartialTypeForInstanceVariable] +# flags: --allow-redefinition-new --local-partial-types +class C1: + def __init__(self) -> None: + self.x = None + if int(): + self.x = 1 + reveal_type(self.x) # N: Revealed type is "builtins.int" + reveal_type(self.x) # N: Revealed type is "Union[builtins.int, None]" + +reveal_type(C1().x) # N: Revealed type is "Union[builtins.int, None]" + +class C2: + def __init__(self) -> None: + self.x = [] + for i in [1, 2]: + self.x.append(i) + reveal_type(self.x) # N: Revealed type is "builtins.list[builtins.int]" + +reveal_type(C2().x) # N: Revealed type is "builtins.list[builtins.int]" + +class C3: + def __init__(self) -> None: + self.x = None + if int(): + self.x = 1 + else: + self.x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[int]") + reveal_type(self.x) # N: Revealed type is "Union[builtins.int, None]" + +reveal_type(C3().x) # N: Revealed type is "Union[builtins.int, None]" + +class C4: + def __init__(self) -> None: + self.x = [] + if int(): + self.x = [""] + reveal_type(self.x) # N: Revealed type is "builtins.list[builtins.str]" + +reveal_type(C4().x) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/list.pyi] + +[case testNewRedefinePartialGenericTypes] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f2() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + a = [""] + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + +def f3() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + a = [] + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f4() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + # Partial types are currently not supported on reassignment + a = [] + a.append("x") # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f5() -> None: + if int(): + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + else: + b = [""] + a = b + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" + +def f6() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + b = [""] + a = b + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/list.pyi] + +[case testNewRedefineFinalLiteral] +# flags: --allow-redefinition-new --local-partial-types +from typing import Final, Literal + +x: Final = "foo" +reveal_type(x) # N: Revealed type is "Literal['foo']?" +a: Literal["foo"] = x + +class B: + x: Final = "bar" + a: Literal["bar"] = x +reveal_type(B.x) # N: Revealed type is "Literal['bar']?" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineAnnotatedVariable] +# flags: --allow-redefinition-new --local-partial-types +from typing import Optional + +def f1() -> None: + x: int = 0 + if int(): + x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2(x: Optional[str]) -> None: + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.str" + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f3() -> None: + a: list[Optional[str]] = [""] + reveal_type(a) # N: Revealed type is "builtins.list[Union[builtins.str, None]]" + a = [""] + reveal_type(a) # N: Revealed type is "builtins.list[Union[builtins.str, None]]" + +class C: + x: Optional[str] + + def f(self) -> None: + if self.x is not None: + reveal_type(self.x) # N: Revealed type is "builtins.str" + else: + self.x = "" + reveal_type(self.x) # N: Revealed type is "builtins.str" + +[case testNewRedefineAnyType1] +# flags: --allow-redefinition-new --local-partial-types +def a(): pass + +def f1() -> None: + if int(): + x = "" + else: + x = a() + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Union[builtins.str, Any]" + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x = a() + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[Any, builtins.str]" + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f3() -> None: + x = 1 + x = a() + reveal_type(x) # N: Revealed type is "Any" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f4() -> None: + x = a() + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + x = a() + reveal_type(x) # N: Revealed type is "Any" + +def f5() -> None: + x = a() + if int(): + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + elif int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[Any, builtins.int, builtins.str]" + +def f6() -> None: + x = a() + if int(): + x = 1 + else: + x = "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f7() -> None: + x: int + x = a() + reveal_type(x) # N: Revealed type is "builtins.int" + +[case testNewRedefineAnyType2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Any + +def f1() -> None: + x: Any + x = int() + reveal_type(x) # N: Revealed type is "Any" + +def f2() -> None: + x: Any + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "Any" + else: + x = "" + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Any" + +def f3(x) -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Any" + +[case tetNewRedefineDel] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + del x + reveal_type(x) # N: Revealed type is "" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x = 0 + del x + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f3() -> None: + if int(): + x = 0 + else: + x = "" + del x + reveal_type(x) # N: Revealed type is "builtins.int" + +def f4() -> None: + while int(): + if int(): + x: int = 0 + else: + del x + reveal_type(x) # N: Revealed type is "builtins.int" + +def f5() -> None: + while int(): + if int(): + x = 0 + else: + del x + continue + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" +[case testNewRedefineWhileLoopSimple] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + while int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "builtins.int" + while int(): + x = None + reveal_type(x) # N: Revealed type is "None" + x = b"" + reveal_type(x) # N: Revealed type is "builtins.bytes" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.bytes]" + x = [1] + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + +[case testNewRedefineWhileLoopOptional] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = None + while int(): + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f2() -> None: + x = None + while int(): + reveal_type(x) # N: Revealed type is "None" \ + # N: Revealed type is "Union[None, builtins.str]" + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +[case testNewRedefineWhileLoopPartialType] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = [] + while int(): + x.append(1) + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testNewRedefineWhileLoopComplex1] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + while True: + try: + pass + except Exception as e: + continue +[builtins fixtures/exception.pyi] + +[case testNewRedefineWhileLoopComplex2] +# flags: --allow-redefinition-new --local-partial-types + +class C: + def __enter__(self) -> str: ... + def __exit__(self, *args) -> str: ... + +def f1() -> None: + while True: + with C() as x: + continue + +def f2() -> None: + while True: + from m import y + if int(): + continue + +[file m.py] +y = "" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineReturn] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + return + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + x = "" + else: + x = 0 + return + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineBreakAndContinue] +# flags: --allow-redefinition-new --local-partial-types +def b() -> None: + while int(): + x = "" + if int(): + x = 1 + break + reveal_type(x) # N: Revealed type is "builtins.str" + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +def c() -> None: + x = 0 + while int(): + reveal_type(x) # N: Revealed type is "builtins.int" \ + # N: Revealed type is "Union[builtins.int, builtins.str, None]" + if int(): + x = "" + continue + else: + x = None + reveal_type(x) # N: Revealed type is "None" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + +[case testNewRedefineUnderscore] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + if int(): + _ = 0 + reveal_type(_) # N: Revealed type is "builtins.int" + else: + _ = "" + reveal_type(_) # N: Revealed type is "builtins.str" + reveal_type(_) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineWithStatement] +# flags: --allow-redefinition-new --local-partial-types +class C: + def __enter__(self) -> int: ... + def __exit__(self, x, y, z): ... +class D: + def __enter__(self) -> str: ... + def __exit__(self, x, y, z): ... + +def f1() -> None: + with C() as x: + reveal_type(x) # N: Revealed type is "builtins.int" + with D() as x: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + with C() as x: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + with D() as x: + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineTryStatement] +# flags: --allow-redefinition-new --local-partial-types +class E(Exception): pass + +def g(): ... + +def f1() -> None: + try: + x = 1 + g() + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + except RuntimeError as e: + reveal_type(e) # N: Revealed type is "builtins.RuntimeError" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + except E as e: + reveal_type(e) # N: Revealed type is "__main__.E" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(e) # N: Revealed type is "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + try: + x = 1 + if int(): + x = "" + return + except Exception: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + return + reveal_type(x) # N: Revealed type is "builtins.int" + +def f3() -> None: + try: + x = 1 + if int(): + x = "" + return + finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" \ + # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "builtins.int" + +def f4() -> None: + while int(): + try: + x = 1 + if int(): + x = "" + break + if int(): + while int(): + if int(): + x = None + break + finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" \ + # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/exception.pyi] + +[case testNewRedefineRaiseStatement] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = "" + elif int(): + x = None + raise Exception() + else: + x = 1 + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + +def f2() -> None: + try: + x = 1 + if int(): + x = "" + raise Exception() + reveal_type(x) # N: Revealed type is "builtins.int" + except Exception: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/exception.pyi] + + +[case testNewRedefineMultipleAssignment] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x, y = 1, "" + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + x, y = None, 2 + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x, y = 1, "" + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + else: + x, y = None, 2 + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" + +[case testNewRedefineForLoopBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + for x in [""]: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + for x, y in [(1, "x")]: + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + else: + for x, y in [(None, 1)]: + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" +[builtins fixtures/for.pyi] + +[case testNewRedefineForLoop1] +# flags: --allow-redefinition-new --local-partial-types +def l() -> list[int]: + return [] + +def f1() -> None: + x = "" + for x in l(): + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + +def f2() -> None: + for x in [1, 2]: + x = [x] + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + +def f3() -> None: + for x in [1, 2]: + if int(): + x = "x" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/for.pyi] + +[case testNewRedefineForLoop2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Any + +def f(a: Any) -> None: + for d in a: + if isinstance(d["x"], str): + return +[builtins fixtures/isinstance.pyi] + +[case testNewRedefineForStatementIndexNarrowing] +# flags: --allow-redefinition-new --local-partial-types +from typing import TypedDict + +class X(TypedDict): + hourly: int + daily: int + +x: X +for a in ("hourly", "daily"): + reveal_type(a) # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]" + reveal_type(x[a]) # N: Revealed type is "builtins.int" + reveal_type(a.upper()) # N: Revealed type is "builtins.str" + c = a + reveal_type(c) # N: Revealed type is "builtins.str" + a = "monthly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = "yearly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = 1 + reveal_type(a) # N: Revealed type is "builtins.int" +reveal_type(a) # N: Revealed type is "builtins.int" + +b: str +for b in ("hourly", "daily"): + reveal_type(b) # N: Revealed type is "builtins.str" + reveal_type(b.upper()) # N: Revealed type is "builtins.str" +[builtins fixtures/for.pyi] +[typing fixtures/typing-full.pyi] + +[case testNewRedefineForLoopIndexWidening] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + if int(): + break + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f3() -> None: + if int(): + for x in [1]: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineVariableAnnotatedInLoop] +# flags: --allow-redefinition-new --local-partial-types --enable-error-code=redundant-expr +from typing import Optional + +def f1() -> None: + e: Optional[str] = None + for x in ["a"]: + if e is None and int(): + e = x + continue + elif e is not None and int(): + break + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + +def f2(e: Optional[str]) -> None: + for x in ["a"]: + if e is None and int(): + e = x + continue + elif e is not None and int(): + break + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + +[case testNewRedefineLoopAndPartialTypesSpecialCase] +# flags: --allow-redefinition-new --local-partial-types +def f() -> list[str]: + a = [] # type: ignore + o = [] + for line in ["x"]: + if int(): + continue + if int(): + a = [] + if int(): + a.append(line) + else: + o.append(line) + return o +[builtins fixtures/list.pyi] + +[case testNewRedefineFinalVariable] +# flags: --allow-redefinition-new --local-partial-types +from typing import Final + +x: Final = "foo" +x = 1 # E: Cannot assign to final name "x" \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + +class C: + y: Final = "foo" + y = 1 # E: Cannot assign to final name "y" \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + +[case testNewRedefineEnableUsingComment] +# flags: --local-partial-types +import a +import b + +[file a.py] +# mypy: allow-redefinition-new +if int(): + x = 0 +else: + x = "" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[file b.py] +if int(): + x = 0 +else: + x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(x) # N: Revealed type is "builtins.int" + +[case testNewRedefineWithoutLocalPartialTypes] +import a +import b + +[file a.py] +# mypy: local-partial-types, allow-redefinition-new +x = 0 +if int(): + x = "" + +[file b.py] +# mypy: allow-redefinition-new +x = 0 +if int(): + x = "" + +[out] +tmp/b.py:1: error: --local-partial-types must be enabled if using --allow-redefinition-new + +[case testNewRedefineNestedLoopInfiniteExpansion] +# flags: --allow-redefinition-new --local-partial-types +def a(): ... + +def f() -> None: + while int(): + x = a() + + while int(): + x = [x] + + reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]]]]]" + +[case testNewRedefinePartialNoneEmptyList] +# flags: --allow-redefinition-new --local-partial-types +def func() -> None: + l = None + + if int(): + l = [] # E: Need type annotation for "l" + l.append(1) + reveal_type(l) # N: Revealed type is "Union[None, builtins.list[Any]]" +[builtins fixtures/list.pyi] + +[case testNewRedefineNarrowingSpecialCase] +# flags: --allow-redefinition-new --local-partial-types --warn-unreachable +from typing import Any, Union + +def get() -> Union[tuple[Any, Any], tuple[None, None]]: ... + +def f() -> None: + x, _ = get() + reveal_type(x) # N: Revealed type is "Union[Any, None]" + if x and int(): + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Union[Any, None]" + if x and int(): + reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testNewRedefinePartialTypeForUnderscore] +# flags: --allow-redefinition-new --local-partial-types + +def t() -> tuple[int]: + return (42,) + +def f1() -> None: + # Underscore is slightly special to preserve backward compatibility + x, *_ = t() + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + x, *y = t() # E: Need type annotation for "y" (hint: "y: List[] = ...") + +def f3() -> None: + x, _ = 1, [] + +def f4() -> None: + a, b = 1, [] # E: Need type annotation for "b" (hint: "b: List[] = ...") +[builtins fixtures/tuple.pyi] + +[case testNewRedefineUseInferredTypedDictTypeForContext] +# flags: --allow-redefinition-new --local-partial-types +from typing import TypedDict + +class TD(TypedDict): + x: int + +def f() -> None: + td = TD(x=1) + if int(): + td = {"x": 5} + reveal_type(td) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.int})" +[typing fixtures/typing-typeddict.pyi] + +[case testNewRedefineEmptyGeneratorUsingUnderscore] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + gen = (_ for _ in ()) + reveal_type(gen) # N: Revealed type is "typing.Generator[Any, None, None]" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineCannotWidenImportedVariable] +# flags: --allow-redefinition-new --local-partial-types +import a +import b +reveal_type(a.x) # N: Revealed type is "builtins.str" + +[file a.py] +from b import x +if int(): + x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") + +[file b.py] +x = "a" + +[case testNewRedefineCannotWidenGlobalOrClassVariableWithMemberRef] +# flags: --allow-redefinition-new --local-partial-types +from typing import ClassVar +import a + +a.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(a.x) # N: Revealed type is "builtins.str" + +class C: + x = "" + y: ClassVar[str] = "" + +C.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(C.x) # N: Revealed type is "builtins.str" +C.y = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(C.y) # N: Revealed type is "builtins.str" + +[file a.py] +x = "a" + +[case testNewRedefineWidenGlobalInInitModule] +# flags: --allow-redefinition-new --local-partial-types +import pkg + +[file pkg/__init__.py] +x = 0 +if int(): + x = "" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" From 765a78fee1e35e10e2c5a28c02f2bc73cc1aa4a9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 19 Mar 2025 23:09:51 +0000 Subject: [PATCH 1199/1617] Inline a trivial function in checkmember.py (#18819) This is a pure refactoring so I am not waiting for a review. --- mypy/checkmember.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0994d0df400b..b1abd0020dc1 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -496,7 +496,8 @@ def analyze_member_var_access( original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. - v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) + node = info.get(name) + v = node.node if node else None mx.chk.warn_deprecated(v, mx.context) @@ -898,16 +899,6 @@ def visit_callable_type(self, t: CallableType) -> None: super().visit_callable_type(t) -def lookup_member_var_or_accessor(info: TypeInfo, name: str, is_lvalue: bool) -> SymbolNode | None: - """Find the attribute/accessor node that refers to a member of a type.""" - # TODO handle lvalues - node = info.get(name) - if node: - return node.node - else: - return None - - def check_self_arg( functype: FunctionLike, dispatched_arg_type: Type, From 045a6d86fd1f0f1bd7cdf652d0bd017eb1d1ef56 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 19 Mar 2025 19:02:24 -0700 Subject: [PATCH 1200/1617] stubtest: understand override (#18815) Fixes #18814 --- mypy/stubtest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a0f886106715..ab29d9dca4b8 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -858,7 +858,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg all_args: dict[str, list[tuple[nodes.Argument, int]]] = {} for func in map(_resolve_funcitem_from_decorator, stub.items): - assert func is not None + assert func is not None, "Failed to resolve decorated overload" args = maybe_strip_cls(stub.name, func.arguments) for index, arg in enumerate(args): # For positional-only args, we allow overloads to have different names for the same @@ -1330,6 +1330,7 @@ def apply_decorator_to_funcitem( if ( decorator.fullname in ("builtins.staticmethod", "abc.abstractmethod") or decorator.fullname in mypy.types.OVERLOAD_NAMES + or decorator.fullname in mypy.types.OVERRIDE_DECORATOR_NAMES or decorator.fullname in mypy.types.FINAL_DECORATOR_NAMES ): return func From 60f00f3d9e9dc965fe9860a264a5babea918571f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 20 Mar 2025 02:05:00 +0000 Subject: [PATCH 1201/1617] Do not pass message builder to analyze_member_access (#18818) This is a pure refactoring so I am not waiting for a review. --- mypy/checker.py | 4 ---- mypy/checkexpr.py | 7 ------- mypy/checkmember.py | 9 +-------- mypy/checkpattern.py | 2 -- 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2c15970b8b15..0e4cff2d4a73 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -781,7 +781,6 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab is_lvalue=False, is_super=False, is_operator=True, - msg=self.msg, original_type=inner_type, chk=self, ) @@ -4736,7 +4735,6 @@ def check_member_assignment( original_type=instance_type, context=context, self_type=None, - msg=self.msg, chk=self, ) get_type = analyze_descriptor_access(attribute_type, mx, assignment=True) @@ -6746,7 +6744,6 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=new_parent_type, chk=self, in_literal_context=False, @@ -8044,7 +8041,6 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=typ, chk=self, # This is not a real attribute lookup so don't mess with deferring nodes. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 80471a04469c..bda04f7c68c4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1505,7 +1505,6 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=object_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -1593,7 +1592,6 @@ def check_call( is_lvalue=False, is_super=False, is_operator=True, - msg=self.msg, original_type=original_type or callee, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3353,7 +3351,6 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type is_lvalue=is_lvalue, is_super=False, is_operator=False, - msg=self.msg, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3377,7 +3374,6 @@ def analyze_external_member_access( is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3872,7 +3868,6 @@ def check_method_call_by_name( is_lvalue=False, is_super=False, is_operator=True, - msg=self.msg, original_type=original_type, self_type=base_type, chk=self.chk, @@ -3967,7 +3962,6 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None: is_operator=True, original_type=base_type, context=context, - msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -5568,7 +5562,6 @@ def visit_super_expr(self, e: SuperExpr) -> Type: original_type=instance_type, override_info=base, context=e, - msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), ) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index b1abd0020dc1..e801116e5372 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -95,7 +95,6 @@ def __init__( is_operator: bool, original_type: Type, context: Context, - msg: MessageBuilder, chk: mypy.checker.TypeChecker, self_type: Type | None, module_symbol_table: SymbolTable | None = None, @@ -108,8 +107,8 @@ def __init__( self.original_type = original_type self.self_type = self_type or original_type self.context = context # Error context - self.msg = msg self.chk = chk + self.msg = chk.msg self.module_symbol_table = module_symbol_table self.no_deferral = no_deferral self.is_self = is_self @@ -123,7 +122,6 @@ def not_ready_callback(self, name: str, context: Context) -> None: def copy_modified( self, *, - messages: MessageBuilder | None = None, self_type: Type | None = None, is_lvalue: bool | None = None, original_type: Type | None = None, @@ -134,14 +132,11 @@ def copy_modified( is_operator=self.is_operator, original_type=self.original_type, context=self.context, - msg=self.msg, chk=self.chk, self_type=self.self_type, module_symbol_table=self.module_symbol_table, no_deferral=self.no_deferral, ) - if messages is not None: - mx.msg = messages if self_type is not None: mx.self_type = self_type if is_lvalue is not None: @@ -159,7 +154,6 @@ def analyze_member_access( is_lvalue: bool, is_super: bool, is_operator: bool, - msg: MessageBuilder, original_type: Type, chk: mypy.checker.TypeChecker, override_info: TypeInfo | None = None, @@ -198,7 +192,6 @@ def analyze_member_access( is_operator=is_operator, original_type=original_type, context=context, - msg=msg, chk=chk, self_type=self_type, module_symbol_table=module_symbol_table, diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 2a8620482d87..c71d83324694 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -598,7 +598,6 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=typ, chk=self.chk, ) @@ -664,7 +663,6 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=new_type, chk=self.chk, ) From cd422e098efcf2df82e4c42070e3dcc8180b53e0 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 20 Mar 2025 08:58:22 +0000 Subject: [PATCH 1202/1617] Move some functions from checkmember to typeops (#18820) There is no reason for these functions to be there. This actually allows removing some function-level imports. This is a pure refactoring so I am not waiting for a review. --------- Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- mypy/checker.py | 2 +- mypy/checkexpr.py | 9 ++-- mypy/checkmember.py | 102 +----------------------------------------- mypy/plugins/attrs.py | 11 ++--- mypy/typeops.py | 100 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 110 insertions(+), 114 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0e4cff2d4a73..62acfc9e3abe 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -17,7 +17,6 @@ analyze_decorator_or_funcbase_access, analyze_descriptor_access, analyze_member_access, - type_object_type, ) from mypy.checkpattern import PatternChecker from mypy.constraints import SUPERTYPE_OF @@ -168,6 +167,7 @@ try_getting_str_literals, try_getting_str_literals_from_type, tuple_fallback, + type_object_type, ) from mypy.types import ( ANY_STRATEGY, diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index bda04f7c68c4..6ae75daee98c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -15,12 +15,7 @@ import mypy.errorcodes as codes from mypy import applytype, erasetype, join, message_registry, nodes, operators, types from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals -from mypy.checkmember import ( - analyze_member_access, - freeze_all_type_vars, - type_object_type, - typeddict_callable, -) +from mypy.checkmember import analyze_member_access, typeddict_callable from mypy.checkstrformat import StringFormatterChecker from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars from mypy.errors import ErrorWatcher, report_internal_error @@ -138,6 +133,7 @@ erase_to_union_or_bound, false_only, fixup_partial_type, + freeze_all_type_vars, function_type, get_all_type_vars, get_type_vars, @@ -148,6 +144,7 @@ try_expanding_sum_type_to_union, try_getting_str_literals, tuple_fallback, + type_object_type, ) from mypy.types import ( LITERAL_TYPE_NAMES, diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e801116e5372..40a31cd7ba72 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -17,7 +17,6 @@ from mypy.nodes import ( ARG_POS, ARG_STAR, - ARG_STAR2, EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, ArgKind, @@ -29,7 +28,6 @@ MypyFile, NameExpr, OverloadedFuncDef, - SymbolNode, SymbolTable, TempNode, TypeAlias, @@ -41,14 +39,14 @@ from mypy.plugin import AttributeContext from mypy.typeops import ( bind_self, - class_callable, erase_to_bound, + freeze_all_type_vars, function_type, get_type_vars, make_simplified_union, supported_self_type, tuple_fallback, - type_object_type_from_function, + type_object_type, ) from mypy.types import ( AnyType, @@ -73,7 +71,6 @@ UnionType, get_proper_type, ) -from mypy.typetraverser import TypeTraverserVisitor if TYPE_CHECKING: # import for forward declaration only import mypy.checker @@ -881,17 +878,6 @@ def expand_self_type_if_needed( return t -def freeze_all_type_vars(member_type: Type) -> None: - member_type.accept(FreezeTypeVarsVisitor()) - - -class FreezeTypeVarsVisitor(TypeTraverserVisitor): - def visit_callable_type(self, t: CallableType) -> None: - for v in t.variables: - v.id.meta_level = 0 - super().visit_callable_type(t) - - def check_self_arg( functype: FunctionLike, dispatched_arg_type: Type, @@ -1319,77 +1305,6 @@ def typeddict_callable(info: TypeInfo, named_type: Callable[[str], Instance]) -> ) -def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: - """Return the type of a type object. - - For a generic type G with type variables T and S the type is generally of form - - Callable[..., G[T, S]] - - where ... are argument types for the __init__/__new__ method (without the self - argument). Also, the fallback type will be 'type' instead of 'function'. - """ - - # We take the type from whichever of __init__ and __new__ is first - # in the MRO, preferring __init__ if there is a tie. - init_method = info.get("__init__") - new_method = info.get("__new__") - if not init_method or not is_valid_constructor(init_method.node): - # Must be an invalid class definition. - return AnyType(TypeOfAny.from_error) - # There *should* always be a __new__ method except the test stubs - # lack it, so just copy init_method in that situation - new_method = new_method or init_method - if not is_valid_constructor(new_method.node): - # Must be an invalid class definition. - return AnyType(TypeOfAny.from_error) - - # The two is_valid_constructor() checks ensure this. - assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) - assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) - - init_index = info.mro.index(init_method.node.info) - new_index = info.mro.index(new_method.node.info) - - fallback = info.metaclass_type or named_type("builtins.type") - if init_index < new_index: - method: FuncBase | Decorator = init_method.node - is_new = False - elif init_index > new_index: - method = new_method.node - is_new = True - else: - if init_method.node.info.fullname == "builtins.object": - # Both are defined by object. But if we've got a bogus - # base class, we can't know for sure, so check for that. - if info.fallback_to_any: - # Construct a universal callable as the prototype. - any_type = AnyType(TypeOfAny.special_form) - sig = CallableType( - arg_types=[any_type, any_type], - arg_kinds=[ARG_STAR, ARG_STAR2], - arg_names=["_args", "_kwds"], - ret_type=any_type, - fallback=named_type("builtins.function"), - ) - return class_callable(sig, info, fallback, None, is_new=False) - - # Otherwise prefer __init__ in a tie. It isn't clear that this - # is the right thing, but __new__ caused problems with - # typeshed (#5647). - method = init_method.node - is_new = False - # Construct callable type based on signature of __init__. Adjust - # return type and insert type arguments. - if isinstance(method, FuncBase): - t = function_type(method, fallback) - else: - assert isinstance(method.type, ProperType) - assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this - t = method.type - return type_object_type_from_function(t, info, method.info, fallback, is_new) - - def analyze_decorator_or_funcbase_access( defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext ) -> Type: @@ -1403,16 +1318,3 @@ def analyze_decorator_or_funcbase_access( return bind_self( function_type(defn, mx.chk.named_type("builtins.function")), original_type=mx.self_type ) - - -def is_valid_constructor(n: SymbolNode | None) -> bool: - """Does this node represents a valid constructor method? - - This includes normal functions, overloaded functions, and decorators - that return a callable type. - """ - if isinstance(n, SYMBOL_FUNCBASE_TYPES): - return True - if isinstance(n, Decorator): - return isinstance(get_proper_type(n.type), FunctionLike) - return False diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 0c29d992c22e..b7b3821576ea 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -56,7 +56,12 @@ ) from mypy.server.trigger import make_wildcard_trigger from mypy.state import state -from mypy.typeops import get_type_vars, make_simplified_union, map_type_from_supertype +from mypy.typeops import ( + get_type_vars, + make_simplified_union, + map_type_from_supertype, + type_object_type, +) from mypy.types import ( AnyType, CallableType, @@ -726,8 +731,6 @@ def _parse_converter( ): converter_type = converter_expr.node.type elif isinstance(converter_expr.node, TypeInfo): - from mypy.checkmember import type_object_type # To avoid import cycle. - converter_type = type_object_type(converter_expr.node, ctx.api.named_type) elif ( isinstance(converter_expr, IndexExpr) @@ -736,8 +739,6 @@ def _parse_converter( and isinstance(converter_expr.base.node, TypeInfo) ): # The converter is a generic type. - from mypy.checkmember import type_object_type # To avoid import cycle. - converter_type = type_object_type(converter_expr.base.node, ctx.api.named_type) if isinstance(converter_type, CallableType): converter_type = apply_generic_arguments( diff --git a/mypy/typeops.py b/mypy/typeops.py index ac0695a096a6..06ecc0fb3fda 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -9,7 +9,7 @@ import itertools from collections.abc import Iterable, Sequence -from typing import Any, TypeVar, cast +from typing import Any, Callable, TypeVar, cast from mypy.copytype import copy_type from mypy.expandtype import expand_type, expand_type_by_instance @@ -27,6 +27,7 @@ FuncItem, OverloadedFuncDef, StrExpr, + SymbolNode, TypeInfo, Var, ) @@ -63,6 +64,7 @@ get_proper_type, get_proper_types, ) +from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars @@ -132,6 +134,90 @@ def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Typ return None +def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: + """Return the type of a type object. + + For a generic type G with type variables T and S the type is generally of form + + Callable[..., G[T, S]] + + where ... are argument types for the __init__/__new__ method (without the self + argument). Also, the fallback type will be 'type' instead of 'function'. + """ + + # We take the type from whichever of __init__ and __new__ is first + # in the MRO, preferring __init__ if there is a tie. + init_method = info.get("__init__") + new_method = info.get("__new__") + if not init_method or not is_valid_constructor(init_method.node): + # Must be an invalid class definition. + return AnyType(TypeOfAny.from_error) + # There *should* always be a __new__ method except the test stubs + # lack it, so just copy init_method in that situation + new_method = new_method or init_method + if not is_valid_constructor(new_method.node): + # Must be an invalid class definition. + return AnyType(TypeOfAny.from_error) + + # The two is_valid_constructor() checks ensure this. + assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) + assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) + + init_index = info.mro.index(init_method.node.info) + new_index = info.mro.index(new_method.node.info) + + fallback = info.metaclass_type or named_type("builtins.type") + if init_index < new_index: + method: FuncBase | Decorator = init_method.node + is_new = False + elif init_index > new_index: + method = new_method.node + is_new = True + else: + if init_method.node.info.fullname == "builtins.object": + # Both are defined by object. But if we've got a bogus + # base class, we can't know for sure, so check for that. + if info.fallback_to_any: + # Construct a universal callable as the prototype. + any_type = AnyType(TypeOfAny.special_form) + sig = CallableType( + arg_types=[any_type, any_type], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=["_args", "_kwds"], + ret_type=any_type, + fallback=named_type("builtins.function"), + ) + return class_callable(sig, info, fallback, None, is_new=False) + + # Otherwise prefer __init__ in a tie. It isn't clear that this + # is the right thing, but __new__ caused problems with + # typeshed (#5647). + method = init_method.node + is_new = False + # Construct callable type based on signature of __init__. Adjust + # return type and insert type arguments. + if isinstance(method, FuncBase): + t = function_type(method, fallback) + else: + assert isinstance(method.type, ProperType) + assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this + t = method.type + return type_object_type_from_function(t, info, method.info, fallback, is_new) + + +def is_valid_constructor(n: SymbolNode | None) -> bool: + """Does this node represents a valid constructor method? + + This includes normal functions, overloaded functions, and decorators + that return a callable type. + """ + if isinstance(n, SYMBOL_FUNCBASE_TYPES): + return True + if isinstance(n, Decorator): + return isinstance(get_proper_type(n.type), FunctionLike) + return False + + def type_object_type_from_function( signature: FunctionLike, info: TypeInfo, def_info: TypeInfo, fallback: Instance, is_new: bool ) -> FunctionLike: @@ -1070,6 +1156,17 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> list[TypeVarLikeType]: return [t] if self.include_all else [] +def freeze_all_type_vars(member_type: Type) -> None: + member_type.accept(FreezeTypeVarsVisitor()) + + +class FreezeTypeVarsVisitor(TypeTraverserVisitor): + def visit_callable_type(self, t: CallableType) -> None: + for v in t.variables: + v.id.meta_level = 0 + super().visit_callable_type(t) + + def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool: """Does this type have a custom special method such as __format__() or __eq__()? @@ -1152,7 +1249,6 @@ def get_protocol_member( ) -> Type | None: if member == "__call__" and class_obj: # Special case: class objects always have __call__ that is just the constructor. - from mypy.checkmember import type_object_type def named_type(fullname: str) -> Instance: return Instance(left.type.mro[-1], []) From b7185c94c96014fd08f1d80a6d33bd43b7ef667c Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 20 Mar 2025 21:10:21 +0100 Subject: [PATCH 1203/1617] Remove last occurrences of Python 2.7 (#18822) Update the documentation: change occurrences of Python 2.7, use Python 3.9 instead. --- docs/source/config_file.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index abfe5bb21c62..de51f0c796fd 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -432,7 +432,7 @@ Platform configuration Specifies the Python version used to parse and check the target program. The string should be in the format ``MAJOR.MINOR`` -- - for example ``2.7``. The default is the version of the Python + for example ``3.9``. The default is the version of the Python interpreter used to run mypy. This option may only be set in the global section (``[mypy]``). @@ -1196,7 +1196,7 @@ of your repo (or append it to the end of an existing ``pyproject.toml`` file) an # mypy global options: [tool.mypy] - python_version = "2.7" + python_version = "3.9" warn_return_any = true warn_unused_configs = true exclude = [ From c99973cc43c168cd2a8e3a3ad3ef087dcb5ad271 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 21 Mar 2025 00:49:18 +0000 Subject: [PATCH 1204/1617] Move one more function from checkmember.py (#18825) This is one last small refactoring before I start the actual non-trivial changes. --- mypy/checkexpr.py | 17 +++++++++++++++-- mypy/checkmember.py | 28 +--------------------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 6ae75daee98c..812121994fd7 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -15,7 +15,7 @@ import mypy.errorcodes as codes from mypy import applytype, erasetype, join, message_registry, nodes, operators, types from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals -from mypy.checkmember import analyze_member_access, typeddict_callable +from mypy.checkmember import analyze_member_access from mypy.checkstrformat import StringFormatterChecker from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars from mypy.errors import ErrorWatcher, report_internal_error @@ -957,7 +957,20 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType: Note it is not safe to move this to type_object_type() since it will crash on plugin-generated TypedDicts, that may not have the special_alias. """ - return typeddict_callable(info, self.named_type) + assert info.special_alias is not None + target = info.special_alias.target + assert isinstance(target, ProperType) and isinstance(target, TypedDictType) + expected_types = list(target.items.values()) + kinds = [ArgKind.ARG_NAMED] * len(expected_types) + names = list(target.items.keys()) + return CallableType( + expected_types, + kinds, + names, + target, + self.named_type("builtins.type"), + variables=info.defn.type_vars, + ) def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType: return CallableType( diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 40a31cd7ba72..0535486bfd4a 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -19,7 +19,6 @@ ARG_STAR, EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, - ArgKind, Context, Decorator, FuncBase, @@ -1094,7 +1093,7 @@ def analyze_class_attribute_access( if isinstance(node.node, TypeInfo): if node.node.typeddict_type: # We special-case TypedDict, because they don't define any constructor. - return typeddict_callable(node.node, mx.named_type) + return mx.chk.expr_checker.typeddict_callable(node.node) elif node.node.fullname == "types.NoneType": # We special case NoneType, because its stub definition is not related to None. return TypeType(NoneType()) @@ -1280,31 +1279,6 @@ class B(A[str]): pass return t -def typeddict_callable(info: TypeInfo, named_type: Callable[[str], Instance]) -> CallableType: - """Construct a reasonable type for a TypedDict type in runtime context. - - If it appears as a callee, it will be special-cased anyway, e.g. it is - also allowed to accept a single positional argument if it is a dict literal. - - Note it is not safe to move this to type_object_type() since it will crash - on plugin-generated TypedDicts, that may not have the special_alias. - """ - assert info.special_alias is not None - target = info.special_alias.target - assert isinstance(target, ProperType) and isinstance(target, TypedDictType) - expected_types = list(target.items.values()) - kinds = [ArgKind.ARG_NAMED] * len(expected_types) - names = list(target.items.keys()) - return CallableType( - expected_types, - kinds, - names, - target, - named_type("builtins.type"), - variables=info.defn.type_vars, - ) - - def analyze_decorator_or_funcbase_access( defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext ) -> Type: From e5546feec24e1ff536f5939872b8830c7791f2ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:52:19 -0700 Subject: [PATCH 1205/1617] Sync typeshed (#18803) Source commit: https://github.com/python/typeshed/commit/cdfb10c340c3df0f8b4112705e6e229b6ae269fd --- mypy/typeshed/stdlib/_socket.pyi | 4 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 11 +- mypy/typeshed/stdlib/asyncio/__init__.pyi | 2 + mypy/typeshed/stdlib/asyncio/base_events.pyi | 5 +- mypy/typeshed/stdlib/asyncio/events.pyi | 5 +- mypy/typeshed/stdlib/builtins.pyi | 39 +- mypy/typeshed/stdlib/contextlib.pyi | 10 +- mypy/typeshed/stdlib/distutils/cmd.pyi | 2 +- mypy/typeshed/stdlib/distutils/dist.pyi | 6 +- .../stdlib/distutils/fancy_getopt.pyi | 20 +- mypy/typeshed/stdlib/functools.pyi | 13 +- mypy/typeshed/stdlib/getopt.pyi | 18 +- .../stdlib/importlib/metadata/__init__.pyi | 2 +- mypy/typeshed/stdlib/importlib/readers.pyi | 4 +- mypy/typeshed/stdlib/inspect.pyi | 12 +- mypy/typeshed/stdlib/math.pyi | 26 +- .../stdlib/multiprocessing/connection.pyi | 18 +- mypy/typeshed/stdlib/pathlib.pyi | 16 +- mypy/typeshed/stdlib/shutil.pyi | 10 +- mypy/typeshed/stdlib/socketserver.pyi | 16 +- mypy/typeshed/stdlib/tarfile.pyi | 343 +++++++++--------- mypy/typeshed/stdlib/types.pyi | 27 +- mypy/typeshed/stdlib/typing.pyi | 14 +- mypy/typeshed/stdlib/typing_extensions.pyi | 3 +- mypy/typeshed/stdlib/unittest/case.pyi | 6 +- mypy/typeshed/stdlib/zipfile/__init__.pyi | 8 - .../stdlib/zipfile/_path/__init__.pyi | 84 ++--- 27 files changed, 402 insertions(+), 322 deletions(-) diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 9be0c3f2e669..649728257c1a 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -812,12 +812,12 @@ def getaddrinfo( type: int = ..., proto: int = ..., flags: int = ..., -) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... +) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]: ... def gethostbyname(hostname: str, /) -> str: ... def gethostbyname_ex(hostname: str, /) -> tuple[str, list[str], list[str]]: ... def gethostname() -> str: ... def gethostbyaddr(ip_address: str, /) -> tuple[str, list[str], list[str]]: ... -def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int, /) -> tuple[str, str]: ... +def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], flags: int, /) -> tuple[str, str]: ... def getprotobyname(protocolname: str, /) -> int: ... def getservbyname(servicename: str, protocolname: str = ..., /) -> int: ... def getservbyport(port: int, protocolname: str = ..., /) -> str: ... diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 7201819b25ed..2b56a4e97519 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -117,6 +117,12 @@ class SupportsSub(Protocol[_T_contra, _T_co]): class SupportsRSub(Protocol[_T_contra, _T_co]): def __rsub__(self, x: _T_contra, /) -> _T_co: ... +class SupportsMul(Protocol[_T_contra, _T_co]): + def __mul__(self, x: _T_contra, /) -> _T_co: ... + +class SupportsRMul(Protocol[_T_contra, _T_co]): + def __rmul__(self, x: _T_contra, /) -> _T_co: ... + class SupportsDivMod(Protocol[_T_contra, _T_co]): def __divmod__(self, other: _T_contra, /) -> _T_co: ... @@ -151,11 +157,8 @@ class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): def keys(self) -> Iterable[_KT]: ... def __getitem__(self, key: _KT, /) -> _VT_co: ... -# This protocol is currently under discussion. Use SupportsContainsAndGetItem -# instead, if you require the __contains__ method. -# See https://github.com/python/typeshed/issues/11822. +# stable class SupportsGetItem(Protocol[_KT_contra, _VT_co]): - def __contains__(self, x: Any, /) -> bool: ... def __getitem__(self, key: _KT_contra, /) -> _VT_co: ... # stable diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index 89a8143c5f7f..e47f640a1f9b 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -1,3 +1,5 @@ +# ruff: noqa: PLR5501 # This condition is so big, it's clearer to keep to platform condition in two blocks +# Can't NOQA on a specific line: https://github.com/plinss/flake8-noqa/issues/22 import sys from collections.abc import Awaitable, Coroutine, Generator from typing import Any, TypeVar diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index d410193a3379..9527e9d052aa 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -8,6 +8,7 @@ from asyncio.protocols import BaseProtocol from asyncio.tasks import Task from asyncio.transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from collections.abc import Callable, Iterable, Sequence +from concurrent.futures import Executor, ThreadPoolExecutor from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Literal, TypeVar, overload @@ -96,8 +97,8 @@ class BaseEventLoop(AbstractEventLoop): def call_soon_threadsafe( self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> Handle: ... - def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... - def set_default_executor(self, executor: Any) -> None: ... + def run_in_executor(self, executor: Executor | None, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... + def set_default_executor(self, executor: ThreadPoolExecutor) -> None: ... # type: ignore[override] # Network I/O methods returning Futures. async def getaddrinfo( self, diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index af1594524c45..a9f7d24237a4 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -9,6 +9,7 @@ from _asyncio import ( from _typeshed import FileDescriptorLike, ReadableBuffer, StrPath, Unused, WriteableBuffer from abc import ABCMeta, abstractmethod from collections.abc import Callable, Sequence +from concurrent.futures import Executor from contextvars import Context from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Literal, Protocol, TypeVar, overload @@ -188,9 +189,9 @@ class AbstractEventLoop: def call_soon_threadsafe(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ... @abstractmethod - def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... + def run_in_executor(self, executor: Executor | None, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... @abstractmethod - def set_default_executor(self, executor: Any) -> None: ... + def set_default_executor(self, executor: Executor) -> None: ... # Network I/O methods returning Futures. @abstractmethod async def getaddrinfo( diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 61114afb804d..dc8ddb8fe7a8 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1,4 +1,3 @@ -# ruff: noqa: PYI036 # This is the module declaring BaseException import _ast import _sitebuiltins import _typeshed @@ -88,8 +87,8 @@ _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") -_SupportsNextT = TypeVar("_SupportsNextT", bound=SupportsNext[Any], covariant=True) -_SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant=True) +_SupportsNextT_co = TypeVar("_SupportsNextT_co", bound=SupportsNext[Any], covariant=True) +_SupportsAnextT_co = TypeVar("_SupportsAnextT_co", bound=SupportsAnext[Any], covariant=True) _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) _P = ParamSpec("_P") @@ -772,7 +771,11 @@ class memoryview(Sequence[_I]): def __new__(cls, obj: ReadableBuffer) -> Self: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / + self, + exc_type: type[BaseException] | None, # noqa: PYI036 # This is the module declaring BaseException + exc_val: BaseException | None, + exc_tb: TracebackType | None, + /, ) -> None: ... @overload def cast(self, format: Literal["c", "@c"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bytes]: ... @@ -1042,7 +1045,7 @@ class dict(MutableMapping[_KT, _VT]): def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ... # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] - def get(self, key: _KT, /) -> _VT | None: ... + def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @overload def get(self, key: _KT, default: _VT, /) -> _VT: ... @overload @@ -1221,7 +1224,7 @@ class _PathLike(Protocol[AnyStr_co]): def __fspath__(self) -> AnyStr_co: ... if sys.version_info >= (3, 10): - def aiter(async_iterable: SupportsAiter[_SupportsAnextT], /) -> _SupportsAnextT: ... + def aiter(async_iterable: SupportsAiter[_SupportsAnextT_co], /) -> _SupportsAnextT_co: ... class _SupportsSynchronousAnext(Protocol[_AwaitableT_co]): def __anext__(self) -> _AwaitableT_co: ... @@ -1383,7 +1386,7 @@ class _GetItemIterable(Protocol[_T_co]): def __getitem__(self, i: int, /) -> _T_co: ... @overload -def iter(object: SupportsIter[_SupportsNextT], /) -> _SupportsNextT: ... +def iter(object: SupportsIter[_SupportsNextT_co], /) -> _SupportsNextT_co: ... @overload def iter(object: _GetItemIterable[_T], /) -> Iterator[_T]: ... @overload @@ -1590,17 +1593,17 @@ def print( *values: object, sep: str | None = " ", end: str | None = "\n", file: _SupportsWriteAndFlush[str] | None = None, flush: bool ) -> None: ... -_E = TypeVar("_E", contravariant=True) -_M = TypeVar("_M", contravariant=True) +_E_contra = TypeVar("_E_contra", contravariant=True) +_M_contra = TypeVar("_M_contra", contravariant=True) -class _SupportsPow2(Protocol[_E, _T_co]): - def __pow__(self, other: _E, /) -> _T_co: ... +class _SupportsPow2(Protocol[_E_contra, _T_co]): + def __pow__(self, other: _E_contra, /) -> _T_co: ... -class _SupportsPow3NoneOnly(Protocol[_E, _T_co]): - def __pow__(self, other: _E, modulo: None = None, /) -> _T_co: ... +class _SupportsPow3NoneOnly(Protocol[_E_contra, _T_co]): + def __pow__(self, other: _E_contra, modulo: None = None, /) -> _T_co: ... -class _SupportsPow3(Protocol[_E, _M, _T_co]): - def __pow__(self, other: _E, modulo: _M, /) -> _T_co: ... +class _SupportsPow3(Protocol[_E_contra, _M_contra, _T_co]): + def __pow__(self, other: _E_contra, modulo: _M_contra, /) -> _T_co: ... _SupportsSomeKindOfPow = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] @@ -1636,11 +1639,11 @@ def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> @overload def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> complex: ... @overload -def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] +def pow(base: _SupportsPow2[_E_contra, _T_co], exp: _E_contra, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload -def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] +def pow(base: _SupportsPow3NoneOnly[_E_contra, _T_co], exp: _E_contra, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload -def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... +def pow(base: _SupportsPow3[_E_contra, _M_contra, _T_co], exp: _E_contra, mod: _M_contra) -> _T_co: ... @overload def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... @overload diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index f57e7fa67036..08ac5a28b8b8 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -33,7 +33,7 @@ _T_co = TypeVar("_T_co", covariant=True) _T_io = TypeVar("_T_io", bound=IO[str] | None) _ExitT_co = TypeVar("_ExitT_co", covariant=True, bound=bool | None, default=bool | None) _F = TypeVar("_F", bound=Callable[..., Any]) -_G = TypeVar("_G", bound=Generator[Any, Any, Any] | AsyncGenerator[Any, Any], covariant=True) +_G_co = TypeVar("_G_co", bound=Generator[Any, Any, Any] | AsyncGenerator[Any, Any], covariant=True) _P = ParamSpec("_P") _SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) @@ -68,11 +68,11 @@ class ContextDecorator: def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManagerBase(Generic[_G]): +class _GeneratorContextManagerBase(Generic[_G_co]): # Ideally this would use ParamSpec, but that requires (*args, **kwargs), which this isn't. see #6676 - def __init__(self, func: Callable[..., _G], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... - gen: _G - func: Callable[..., _G] + def __init__(self, func: Callable[..., _G_co], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... + gen: _G_co + func: Callable[..., _G_co] args: tuple[Any, ...] kwds: dict[str, Any] diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index dcb423a49b09..a4e77ddf1388 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -30,7 +30,7 @@ _CommandT = TypeVar("_CommandT", bound=Command) _Ts = TypeVarTuple("_Ts") class Command: - dry_run: Literal[0, 1] # Exposed from __getattr_. Same as Distribution.dry_run + dry_run: bool | Literal[0, 1] # Exposed from __getattr_. Same as Distribution.dry_run distribution: Distribution # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 75fc7dbb388d..09f2b456d263 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -88,9 +88,9 @@ class Distribution: display_options: ClassVar[_OptionsList] display_option_names: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - verbose: Literal[0, 1] - dry_run: Literal[0, 1] - help: Literal[0, 1] + verbose: bool | Literal[0, 1] + dry_run: bool | Literal[0, 1] + help: bool | Literal[0, 1] command_packages: list[str] | None script_name: str | None script_args: list[str] | None diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi index c4d37419ed06..e66d8cc9f2c5 100644 --- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi +++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi @@ -1,10 +1,10 @@ from collections.abc import Iterable, Mapping +from getopt import _SliceableT, _StrSequenceT_co from re import Pattern from typing import Any, Final, overload from typing_extensions import TypeAlias _Option: TypeAlias = tuple[str, str | None, str] -_GR: TypeAlias = tuple[list[str], OptionDummy] longopt_pat: Final = r"[a-zA-Z](?:[a-zA-Z0-9-]*)" longopt_re: Final[Pattern[str]] @@ -15,15 +15,25 @@ class FancyGetopt: def __init__(self, option_table: list[_Option] | None = None) -> None: ... # TODO kinda wrong, `getopt(object=object())` is invalid @overload - def getopt(self, args: list[str] | None = None) -> _GR: ... + def getopt( + self, args: _SliceableT[_StrSequenceT_co] | None = None, object: None = None + ) -> tuple[_StrSequenceT_co, OptionDummy]: ... @overload - def getopt(self, args: list[str] | None, object: Any) -> list[str]: ... + def getopt( + self, args: _SliceableT[_StrSequenceT_co] | None, object: Any + ) -> _StrSequenceT_co: ... # object is an arbitrary non-slotted object def get_option_order(self) -> list[tuple[str, str]]: ... def generate_help(self, header: str | None = None) -> list[str]: ... +# Same note as FancyGetopt.getopt +@overload def fancy_getopt( - options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: list[str] | None -) -> list[str] | _GR: ... + options: list[_Option], negative_opt: Mapping[_Option, _Option], object: None, args: _SliceableT[_StrSequenceT_co] | None +) -> tuple[_StrSequenceT_co, OptionDummy]: ... +@overload +def fancy_getopt( + options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: _SliceableT[_StrSequenceT_co] | None +) -> _StrSequenceT_co: ... WS_TRANS: Final[dict[int, str]] diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 10563e654b37..f786167e322d 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -151,20 +151,25 @@ class partialmethod(Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +if sys.version_info >= (3, 11): + _RegType: TypeAlias = type[Any] | types.UnionType +else: + _RegType: TypeAlias = type[Any] + class _SingleDispatchCallable(Generic[_T]): registry: types.MappingProxyType[Any, Callable[..., _T]] def dispatch(self, cls: Any) -> Callable[..., _T]: ... # @fun.register(complex) # def _(arg, verbose=False): ... @overload - def register(self, cls: type[Any], func: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + def register(self, cls: _RegType, func: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... # @fun.register # def _(arg: int, verbose=False): @overload def register(self, cls: Callable[..., _T], func: None = None) -> Callable[..., _T]: ... # fun.register(int, lambda x: x) @overload - def register(self, cls: type[Any], func: Callable[..., _T]) -> Callable[..., _T]: ... + def register(self, cls: _RegType, func: Callable[..., _T]) -> Callable[..., _T]: ... def _clear_cache(self) -> None: ... def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... @@ -177,11 +182,11 @@ class singledispatchmethod(Generic[_T]): @property def __isabstractmethod__(self) -> bool: ... @overload - def register(self, cls: type[Any], method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + def register(self, cls: _RegType, method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... @overload def register(self, cls: Callable[..., _T], method: None = None) -> Callable[..., _T]: ... @overload - def register(self, cls: type[Any], method: Callable[..., _T]) -> Callable[..., _T]: ... + def register(self, cls: _RegType, method: Callable[..., _T]) -> Callable[..., _T]: ... def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... class cached_property(Generic[_T_co]): diff --git a/mypy/typeshed/stdlib/getopt.pyi b/mypy/typeshed/stdlib/getopt.pyi index bcc8d9750b19..c15db8122cfc 100644 --- a/mypy/typeshed/stdlib/getopt.pyi +++ b/mypy/typeshed/stdlib/getopt.pyi @@ -1,10 +1,22 @@ -from collections.abc import Iterable +from collections.abc import Iterable, Sequence +from typing import Protocol, TypeVar, overload, type_check_only + +_StrSequenceT_co = TypeVar("_StrSequenceT_co", covariant=True, bound=Sequence[str]) + +@type_check_only +class _SliceableT(Protocol[_StrSequenceT_co]): + @overload + def __getitem__(self, key: int, /) -> str: ... + @overload + def __getitem__(self, key: slice, /) -> _StrSequenceT_co: ... __all__ = ["GetoptError", "error", "getopt", "gnu_getopt"] -def getopt(args: list[str], shortopts: str, longopts: Iterable[str] | str = []) -> tuple[list[tuple[str, str]], list[str]]: ... +def getopt( + args: _SliceableT[_StrSequenceT_co], shortopts: str, longopts: Iterable[str] | str = [] +) -> tuple[list[tuple[str, str]], _StrSequenceT_co]: ... def gnu_getopt( - args: list[str], shortopts: str, longopts: Iterable[str] | str = [] + args: Sequence[str], shortopts: str, longopts: Iterable[str] | str = [] ) -> tuple[list[tuple[str, str]], list[str]]: ... class GetoptError(Exception): diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 5e26f8987277..8ab7a0c4a9e8 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -139,7 +139,7 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12): class Deprecated(Generic[_KT, _VT]): def __getitem__(self, name: _KT) -> _VT: ... @overload - def get(self, name: _KT) -> _VT | None: ... + def get(self, name: _KT, default: None = None) -> _VT | None: ... @overload def get(self, name: _KT, default: _T) -> _VT | _T: ... def __iter__(self) -> Iterator[_KT]: ... diff --git a/mypy/typeshed/stdlib/importlib/readers.pyi b/mypy/typeshed/stdlib/importlib/readers.pyi index ceb3e731e7a5..4a6c73921535 100644 --- a/mypy/typeshed/stdlib/importlib/readers.pyi +++ b/mypy/typeshed/stdlib/importlib/readers.pyi @@ -16,9 +16,9 @@ if sys.version_info >= (3, 10): from zipimport import zipimporter if sys.version_info >= (3, 11): - import importlib.resources.abc as abc + from importlib.resources import abc else: - import importlib.abc as abc + from importlib import abc if sys.version_info >= (3, 10): if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 43b3dd529887..229eb2135690 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -143,8 +143,8 @@ if sys.version_info >= (3, 11): _P = ParamSpec("_P") _T = TypeVar("_T") _F = TypeVar("_F", bound=Callable[..., Any]) -_T_cont = TypeVar("_T_cont", contravariant=True) -_V_cont = TypeVar("_V_cont", contravariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) +_V_contra = TypeVar("_V_contra", contravariant=True) # # Types and members @@ -228,11 +228,11 @@ def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGe @overload def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... -class _SupportsSet(Protocol[_T_cont, _V_cont]): - def __set__(self, instance: _T_cont, value: _V_cont, /) -> None: ... +class _SupportsSet(Protocol[_T_contra, _V_contra]): + def __set__(self, instance: _T_contra, value: _V_contra, /) -> None: ... -class _SupportsDelete(Protocol[_T_cont]): - def __delete__(self, instance: _T_cont, /) -> None: ... +class _SupportsDelete(Protocol[_T_contra]): + def __delete__(self, instance: _T_contra, /) -> None: ... def isasyncgen(object: object) -> TypeIs[AsyncGeneratorType[Any, Any]]: ... def istraceback(object: object) -> TypeIs[TracebackType]: ... diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 86f71f27580a..f73429cf6940 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -1,6 +1,7 @@ import sys +from _typeshed import SupportsMul, SupportsRMul from collections.abc import Iterable -from typing import Final, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload +from typing import Any, Final, Literal, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload from typing_extensions import TypeAlias _T = TypeVar("_T") @@ -99,10 +100,29 @@ elif sys.version_info >= (3, 9): def perm(n: SupportsIndex, k: SupportsIndex | None = None, /) -> int: ... def pow(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... + +_PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] +_NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20] +_LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0] # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed + +_MultiplicableT1 = TypeVar("_MultiplicableT1", bound=SupportsMul[Any, Any]) +_MultiplicableT2 = TypeVar("_MultiplicableT2", bound=SupportsMul[Any, Any]) + +class _SupportsProdWithNoDefaultGiven(SupportsMul[Any, Any], SupportsRMul[int, Any], Protocol): ... + +_SupportsProdNoDefaultT = TypeVar("_SupportsProdNoDefaultT", bound=_SupportsProdWithNoDefaultGiven) + +# This stub is based on the type stub for `builtins.sum`. +# Like `builtins.sum`, it cannot be precisely represented in a type stub +# without introducing many false positives. +# For more details on its limitations and false positives, see #13572. +# Instead, just like `builtins.sum`, we explicitly handle several useful cases. +@overload +def prod(iterable: Iterable[bool | _LiteralInteger], /, *, start: int = 1) -> int: ... # type: ignore[overload-overlap] @overload -def prod(iterable: Iterable[SupportsIndex], /, *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] +def prod(iterable: Iterable[_SupportsProdNoDefaultT], /) -> _SupportsProdNoDefaultT | Literal[1]: ... @overload -def prod(iterable: Iterable[_SupportsFloatOrIndex], /, *, start: _SupportsFloatOrIndex = 1) -> float: ... +def prod(iterable: Iterable[_MultiplicableT1], /, *, start: _MultiplicableT2) -> _MultiplicableT1 | _MultiplicableT2: ... def radians(x: _SupportsFloatOrIndex, /) -> float: ... def remainder(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... def sin(x: _SupportsFloatOrIndex, /) -> float: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 9998239d3119..cd4fa102c0f3 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -12,10 +12,10 @@ __all__ = ["Client", "Listener", "Pipe", "wait"] _Address: TypeAlias = str | tuple[str, int] # Defaulting to Any to avoid forcing generics on a lot of pre-existing code -_SendT = TypeVar("_SendT", contravariant=True, default=Any) -_RecvT = TypeVar("_RecvT", covariant=True, default=Any) +_SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=Any) +_RecvT_co = TypeVar("_RecvT_co", covariant=True, default=Any) -class _ConnectionBase(Generic[_SendT, _RecvT]): +class _ConnectionBase(Generic[_SendT_contra, _RecvT_co]): def __init__(self, handle: SupportsIndex, readable: bool = True, writable: bool = True) -> None: ... @property def closed(self) -> bool: ... # undocumented @@ -26,10 +26,10 @@ class _ConnectionBase(Generic[_SendT, _RecvT]): def fileno(self) -> int: ... def close(self) -> None: ... def send_bytes(self, buf: ReadableBuffer, offset: int = 0, size: int | None = None) -> None: ... - def send(self, obj: _SendT) -> None: ... + def send(self, obj: _SendT_contra) -> None: ... def recv_bytes(self, maxlength: int | None = None) -> bytes: ... def recv_bytes_into(self, buf: Any, offset: int = 0) -> int: ... - def recv(self) -> _RecvT: ... + def recv(self) -> _RecvT_co: ... def poll(self, timeout: float | None = 0.0) -> bool: ... def __enter__(self) -> Self: ... def __exit__( @@ -37,10 +37,10 @@ class _ConnectionBase(Generic[_SendT, _RecvT]): ) -> None: ... def __del__(self) -> None: ... -class Connection(_ConnectionBase[_SendT, _RecvT]): ... +class Connection(_ConnectionBase[_SendT_contra, _RecvT_co]): ... if sys.platform == "win32": - class PipeConnection(_ConnectionBase[_SendT, _RecvT]): ... + class PipeConnection(_ConnectionBase[_SendT_contra, _RecvT_co]): ... class Listener: def __init__( @@ -66,8 +66,8 @@ else: def answer_challenge(connection: Connection[Any, Any], authkey: bytes) -> None: ... def wait( - object_list: Iterable[Connection[_SendT, _RecvT] | socket.socket | int], timeout: float | None = None -) -> list[Connection[_SendT, _RecvT] | socket.socket | int]: ... + object_list: Iterable[Connection[_SendT_contra, _RecvT_co] | socket.socket | int], timeout: float | None = None +) -> list[Connection[_SendT_contra, _RecvT_co] | socket.socket | int]: ... def Client(address: _Address, family: str | None = None, authkey: bytes | None = None) -> Connection[Any, Any]: ... # N.B. Keep this in sync with multiprocessing.context.BaseContext.Pipe. diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index e2a816ae1ca4..a18aed4ba57a 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -16,7 +16,7 @@ from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWra from os import PathLike, stat_result from types import TracebackType from typing import IO, Any, BinaryIO, ClassVar, Literal, overload -from typing_extensions import Self, deprecated +from typing_extensions import Never, Self, deprecated if sys.version_info >= (3, 9): from types import GenericAlias @@ -226,9 +226,13 @@ class Path(PurePath): def open( self, mode: str, buffering: int = -1, encoding: str | None = None, errors: str | None = None, newline: str | None = None ) -> IO[Any]: ... - if sys.platform != "win32": - # These methods do "exist" on Windows, but they always raise NotImplementedError, - # so it's safer to pretend they don't exist + + # These methods do "exist" on Windows on <3.13, but they always raise NotImplementedError. + if sys.platform == "win32": + if sys.version_info < (3, 13): + def owner(self: Never) -> str: ... # type: ignore[misc] + def group(self: Never) -> str: ... # type: ignore[misc] + else: if sys.version_info >= (3, 13): def owner(self, *, follow_symlinks: bool = True) -> str: ... def group(self, *, follow_symlinks: bool = True) -> str: ... @@ -238,7 +242,9 @@ class Path(PurePath): # This method does "exist" on Windows on <3.12, but always raises NotImplementedError # On py312+, it works properly on Windows, as with all other platforms - if sys.platform != "win32" or sys.version_info >= (3, 12): + if sys.platform == "win32" and sys.version_info < (3, 12): + def is_mount(self: Never) -> bool: ... # type: ignore[misc] + else: def is_mount(self) -> bool: ... if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index 4a19a96a306c..0fe560fd9b6a 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -3,7 +3,7 @@ import sys from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite from collections.abc import Callable, Iterable, Sequence from tarfile import _TarfileFilter -from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload +from typing import Any, AnyStr, NamedTuple, NoReturn, Protocol, TypeVar, overload from typing_extensions import TypeAlias, deprecated __all__ = [ @@ -36,7 +36,6 @@ __all__ = [ ] _StrOrBytesPathT = TypeVar("_StrOrBytesPathT", bound=StrOrBytesPath) -_StrPathT = TypeVar("_StrPathT", bound=StrPath) # Return value of some functions that may either return a path-like object that was passed in or # a string _PathReturn: TypeAlias = Any @@ -185,8 +184,13 @@ else: @overload def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... +if sys.platform == "win32" and sys.version_info < (3, 12): + @overload + @deprecated("On Windows before Python 3.12, using a PathLike as `cmd` would always fail or return `None`.") + def which(cmd: os.PathLike[str], mode: int = 1, path: StrPath | None = None) -> NoReturn: ... + @overload -def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... +def which(cmd: StrPath, mode: int = 1, path: StrPath | None = None) -> str | None: ... @overload def which(cmd: bytes, mode: int = 1, path: StrPath | None = None) -> bytes | None: ... def make_archive( diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index ae6575d85082..061932f0fac7 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -38,29 +38,22 @@ _AfInetAddress: TypeAlias = tuple[str | bytes | bytearray, int] # address accep # This can possibly be generic at some point: class BaseServer: - address_family: int server_address: _Address - socket: _socket - allow_reuse_address: bool - request_queue_size: int - socket_type: int timeout: float | None RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler] def __init__( self, server_address: _Address, RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler] ) -> None: ... - def fileno(self) -> int: ... def handle_request(self) -> None: ... def serve_forever(self, poll_interval: float = 0.5) -> None: ... def shutdown(self) -> None: ... def server_close(self) -> None: ... def finish_request(self, request: _RequestType, client_address: _RetAddress) -> None: ... - def get_request(self) -> tuple[Any, Any]: ... + def get_request(self) -> tuple[Any, Any]: ... # Not implemented here, but expected to exist on subclasses def handle_error(self, request: _RequestType, client_address: _RetAddress) -> None: ... def handle_timeout(self) -> None: ... def process_request(self, request: _RequestType, client_address: _RetAddress) -> None: ... def server_activate(self) -> None: ... - def server_bind(self) -> None: ... def verify_request(self, request: _RequestType, client_address: _RetAddress) -> bool: ... def __enter__(self) -> Self: ... def __exit__( @@ -71,6 +64,11 @@ class BaseServer: def close_request(self, request: _RequestType) -> None: ... # undocumented class TCPServer(BaseServer): + address_family: int + socket: _socket + allow_reuse_address: bool + request_queue_size: int + socket_type: int if sys.version_info >= (3, 11): allow_reuse_port: bool server_address: _AfInetAddress @@ -80,7 +78,9 @@ class TCPServer(BaseServer): RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler], bind_and_activate: bool = True, ) -> None: ... + def fileno(self) -> int: ... def get_request(self) -> tuple[_socket, _RetAddress]: ... + def server_bind(self) -> None: ... class UDPServer(TCPServer): max_packet_size: ClassVar[int] diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 009aa9070aa8..6a00e070aee9 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -103,166 +103,6 @@ PAX_NAME_FIELDS: set[str] ENCODING: str -@overload -def open( - name: StrOrBytesPath | None = None, - mode: Literal["r", "r:*", "r:", "r:gz", "r:bz2", "r:xz"] = "r", - fileobj: IO[bytes] | None = None, - bufsize: int = 10240, - *, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None, - mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - *, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None = None, - *, - mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None, - mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - *, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - compresslevel: int = 9, -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None = None, - *, - mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - compresslevel: int = 9, -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None, - mode: Literal["x:xz", "w:xz"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - *, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None = None, - *, - mode: Literal["x:xz", "w:xz"], - fileobj: _Fileobj | None = None, - bufsize: int = 10240, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | ReadableBuffer | None = None, - *, - mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"], - fileobj: IO[bytes] | None = None, - bufsize: int = 10240, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - preset: int | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | WriteableBuffer | None = None, - *, - mode: Literal["w|", "w|gz", "w|bz2", "w|xz"], - fileobj: IO[bytes] | None = None, - bufsize: int = 10240, - format: int | None = ..., - tarinfo: type[TarInfo] | None = ..., - dereference: bool | None = ..., - ignore_zeros: bool | None = ..., - encoding: str | None = ..., - errors: str = ..., - pax_headers: Mapping[str, str] | None = ..., - debug: int | None = ..., - errorlevel: int | None = ..., - preset: int | None = ..., -) -> TarFile: ... - class ExFileObject(io.BufferedReader): def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ... @@ -325,14 +165,152 @@ class TarFile: self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... def __iter__(self) -> Iterator[TarInfo]: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None = None, + mode: Literal["r", "r:*", "r:", "r:gz", "r:bz2", "r:xz"] = "r", + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None = None, + *, + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None = None, + *, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["x:xz", "w:xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., + ) -> Self: ... + @overload @classmethod def open( cls, name: StrOrBytesPath | None = None, - mode: str = "r", - fileobj: IO[bytes] | None = None, # depends on mode + *, + mode: Literal["x:xz", "w:xz"], + fileobj: _Fileobj | None = None, bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | ReadableBuffer | None = None, *, + mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, format: int | None = ..., tarinfo: type[TarInfo] | None = ..., dereference: bool | None = ..., @@ -343,6 +321,45 @@ class TarFile: debug: int | None = ..., errorlevel: int | None = ..., ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None = None, + *, + mode: Literal["w|", "w|xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None = None, + *, + mode: Literal["w|gz", "w|bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, + ) -> Self: ... @classmethod def taropen( cls, @@ -501,6 +518,8 @@ class TarFile: ) -> TarInfo: ... def close(self) -> None: ... +open = TarFile.open + if sys.version_info >= (3, 9): def is_tarfile(name: StrOrBytesPath | IO[bytes]) -> bool: ... diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index d41ca0d1c367..849db3ece938 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -18,7 +18,7 @@ from importlib.machinery import ModuleSpec # pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload # noqa: Y022 -from typing_extensions import ParamSpec, Self, TypeVarTuple, deprecated +from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated __all__ = [ "FunctionType", @@ -615,8 +615,27 @@ def prepare_class( if sys.version_info >= (3, 12): def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... -# Actually a different type, but `property` is special and we want that too. -DynamicClassAttribute = property +# Does not actually inherit from property, but saying it does makes sure that +# pyright handles this class correctly. +class DynamicClassAttribute(property): + fget: Callable[[Any], Any] | None + fset: Callable[[Any, Any], object] | None # type: ignore[assignment] + fdel: Callable[[Any], object] | None # type: ignore[assignment] + overwrite_doc: bool + __isabstractmethod__: bool + def __init__( + self, + fget: Callable[[Any], Any] | None = None, + fset: Callable[[Any, Any], object] | None = None, + fdel: Callable[[Any], object] | None = None, + doc: str | None = None, + ) -> None: ... + def __get__(self, instance: Any, ownerclass: type | None = None) -> Any: ... + def __set__(self, instance: Any, value: Any) -> None: ... + def __delete__(self, instance: Any) -> None: ... + def getter(self, fget: Callable[[Any], Any]) -> DynamicClassAttribute: ... + def setter(self, fset: Callable[[Any, Any], object]) -> DynamicClassAttribute: ... + def deleter(self, fdel: Callable[[Any], object]) -> DynamicClassAttribute: ... _Fn = TypeVar("_Fn", bound=Callable[..., object]) _R = TypeVar("_R") @@ -631,7 +650,7 @@ def coroutine(func: _Fn) -> _Fn: ... if sys.version_info >= (3, 9): class GenericAlias: @property - def __origin__(self) -> type: ... + def __origin__(self) -> type | TypeAliasType: ... @property def __args__(self) -> tuple[Any, ...]: ... @property diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 7c1b171a730b..5875b6915762 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -510,15 +510,15 @@ class Awaitable(Protocol[_T_co]): def __await__(self) -> Generator[Any, Any, _T_co]: ... # Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter. -_SendT_contra_nd = TypeVar("_SendT_contra_nd", contravariant=True) -_ReturnT_co_nd = TypeVar("_ReturnT_co_nd", covariant=True) +_SendT_nd_contra = TypeVar("_SendT_nd_contra", contravariant=True) +_ReturnT_nd_co = TypeVar("_ReturnT_nd_co", covariant=True) -class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd]): +class Coroutine(Awaitable[_ReturnT_nd_co], Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co]): __name__: str __qualname__: str @abstractmethod - def send(self, value: _SendT_contra_nd, /) -> _YieldT_co: ... + def send(self, value: _SendT_nd_contra, /) -> _YieldT_co: ... @overload @abstractmethod def throw( @@ -534,9 +534,9 @@ class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, # The parameters correspond to Generator, but the 4th is the original type. @type_check_only class AwaitableGenerator( - Awaitable[_ReturnT_co_nd], - Generator[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd], - Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd, _S], + Awaitable[_ReturnT_nd_co], + Generator[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co], + Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co, _S], metaclass=ABCMeta, ): ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 33af1a388aa5..fd98722b10a8 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -1,5 +1,3 @@ -# Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self -# ruff: noqa: PYI034 import abc import sys import typing @@ -251,6 +249,7 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): @overload def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... # supposedly incompatible definitions of `__ior__` and `__or__`: + # Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self def __ior__(self, value: Self, /) -> Self: ... # type: ignore[misc] OrderedDict = _Alias() diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index a92f03f9745f..33cd556d2e3b 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -20,7 +20,7 @@ from typing import ( TypeVar, overload, ) -from typing_extensions import ParamSpec, Self, TypeAlias +from typing_extensions import Never, ParamSpec, Self, TypeAlias from warnings import WarningMessage if sys.version_info >= (3, 9): @@ -323,6 +323,10 @@ class TestCase: self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any], msg: object = None ) -> None: ... + if sys.version_info >= (3, 10): + # Runtime has *args, **kwargs, but will error if any are supplied + def __init_subclass__(cls, *args: Never, **kwargs: Never) -> None: ... + class FunctionTestCase(TestCase): def __init__( self, diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index 5b8f02f61bce..91bc051df686 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -362,14 +362,6 @@ else: def joinpath(self, *other: StrPath) -> Path: ... else: def joinpath(self, add: StrPath) -> Path: ... # undocumented - if sys.version_info >= (3, 12): - def glob(self, pattern: str) -> Iterator[Self]: ... - def rglob(self, pattern: str) -> Iterator[Self]: ... - def is_symlink(self) -> Literal[False]: ... - def relative_to(self, other: Path, *extra: StrPath) -> str: ... - def match(self, path_pattern: str) -> bool: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... def __truediv__(self, add: StrPath) -> Path: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi b/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi index a7248ba7ab72..4c7b39ec4c6c 100644 --- a/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi @@ -4,11 +4,9 @@ from collections.abc import Iterator, Sequence from io import TextIOWrapper from os import PathLike from typing import IO, Literal, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import Self from zipfile import ZipFile -_ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] - _ZF = TypeVar("_ZF", bound=ZipFile) if sys.version_info >= (3, 12): @@ -39,42 +37,29 @@ if sys.version_info >= (3, 12): def name(self) -> str: ... @property def parent(self) -> PathLike[str]: ... # undocumented - if sys.version_info >= (3, 10): - @property - def filename(self) -> PathLike[str]: ... # undocumented - if sys.version_info >= (3, 11): - @property - def suffix(self) -> str: ... - @property - def suffixes(self) -> list[str]: ... - @property - def stem(self) -> str: ... - - if sys.version_info >= (3, 9): - @overload - def open( - self, - mode: Literal["r", "w"] = "r", - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool = ..., - write_through: bool = ..., - *, - pwd: bytes | None = None, - ) -> TextIOWrapper: ... - @overload - def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... - else: - def open( - self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False - ) -> IO[bytes]: ... - - if sys.version_info >= (3, 10): - def iterdir(self) -> Iterator[Self]: ... - else: - def iterdir(self) -> Iterator[Path]: ... - + @property + def filename(self) -> PathLike[str]: ... # undocumented + @property + def suffix(self) -> str: ... + @property + def suffixes(self) -> list[str]: ... + @property + def stem(self) -> str: ... + @overload + def open( + self, + mode: Literal["r", "w"] = "r", + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = ..., + write_through: bool = ..., + *, + pwd: bytes | None = None, + ) -> TextIOWrapper: ... + @overload + def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... + def iterdir(self) -> Iterator[Self]: ... def is_dir(self) -> bool: ... def is_file(self) -> bool: ... def exists(self) -> bool: ... @@ -87,17 +72,12 @@ if sys.version_info >= (3, 12): write_through: bool = ..., ) -> str: ... def read_bytes(self) -> bytes: ... - if sys.version_info >= (3, 10): - def joinpath(self, *other: StrPath) -> Path: ... - else: - def joinpath(self, add: StrPath) -> Path: ... # undocumented - if sys.version_info >= (3, 12): - def glob(self, pattern: str) -> Iterator[Self]: ... - def rglob(self, pattern: str) -> Iterator[Self]: ... - def is_symlink(self) -> Literal[False]: ... - def relative_to(self, other: Path, *extra: StrPath) -> str: ... - def match(self, path_pattern: str) -> bool: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - + def joinpath(self, *other: StrPath) -> Path: ... + def glob(self, pattern: str) -> Iterator[Self]: ... + def rglob(self, pattern: str) -> Iterator[Self]: ... + def is_symlink(self) -> Literal[False]: ... + def relative_to(self, other: Path, *extra: StrPath) -> str: ... + def match(self, path_pattern: str) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... def __truediv__(self, add: StrPath) -> Path: ... From 5043b84cb62e637a7dd5efb840ec65a78f39cd9a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 24 Mar 2025 14:30:19 +0000 Subject: [PATCH 1206/1617] [mypyc] Add prototype feature to generate annotated html of compiled code (#18828) Now you can use `mypyc -a x.html foo.py` to compile `foo.py` and generate `x.html`, which contains annotated source code for `foo.py` with some inefficient operations highlighted with a red background. Right now this is close to minimal and only detects a few inefficient operations. I will improve this in follow-up PRs. The overall idea is similar to the Cython `-a` flag or `annotate=True`. Here is an example (scroll down a bit): https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html#primes The approach here differs in some ways from Cython. First, we only generate a single html file with annotations for all compiled files. I think this will make things easier when compiling a large number of modules. We'll probably need to add some navigation aids to the generated html eventually. Second, instead of showing the C code when encountering inefficient operations, we will generate (hopefully) easy-to-understand hints that explain what is slow (and perhaps why), without requiring any understanding of C. --- mypy/main.py | 5 + mypy/options.py | 3 + mypyc/annotate.py | 140 ++++++++++++++++++++++++++++ mypyc/build.py | 4 + mypyc/test-data/annotate-basic.test | 26 ++++++ mypyc/test/test_alwaysdefined.py | 2 +- mypyc/test/test_annotate.py | 48 ++++++++++ mypyc/test/testutil.py | 9 +- 8 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 mypyc/annotate.py create mode 100644 mypyc/test-data/annotate-basic.test create mode 100644 mypyc/test/test_annotate.py diff --git a/mypy/main.py b/mypy/main.py index ad836a5ddc19..eff1c538bac5 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1123,6 +1123,11 @@ def add_invertible_flag( dest=f"special-opts:{report_type}_report", ) + # Undocumented mypyc feature: generate annotated HTML source file + report_group.add_argument( + "-a", dest="mypyc_annotation_file", type=str, default=None, help=argparse.SUPPRESS + ) + other_group = parser.add_argument_group(title="Miscellaneous") other_group.add_argument("--quickstart-file", help=argparse.SUPPRESS) other_group.add_argument("--junit-xml", help="Write junit.xml to the given file") diff --git a/mypy/options.py b/mypy/options.py index 27b583722568..17fea6b0bf29 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -406,6 +406,9 @@ def __init__(self) -> None: # Sets custom output format self.output: str | None = None + # Output html file for mypyc -a + self.mypyc_annotation_file: str | None = None + def use_lowercase_names(self) -> bool: if self.python_version >= (3, 9): return not self.force_uppercase_builtins diff --git a/mypyc/annotate.py b/mypyc/annotate.py new file mode 100644 index 000000000000..0a7c5439b7ca --- /dev/null +++ b/mypyc/annotate.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +import os.path +import sys +from html import escape + +from mypy.build import BuildResult +from mypy.nodes import MypyFile +from mypy.util import FancyFormatter +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.module_ir import ModuleIR +from mypyc.ir.ops import CallC, LoadLiteral, Value + +CSS = """\ +.collapsible { + cursor: pointer; +} + +.content { + display: block; + margin-top: 10px; + margin-bottom: 10px; +} + +.hint { + display: inline; + border: 1px solid #ccc; + padding: 5px; +} +""" + +JS = """\ +document.querySelectorAll('.collapsible').forEach(function(collapsible) { + collapsible.addEventListener('click', function() { + const content = this.nextElementSibling; + if (content.style.display === 'none') { + content.style.display = 'block'; + } else { + content.style.display = 'none'; + } + }); +}); +""" + + +class AnnotatedSource: + def __init__(self, path: str, annotations: dict[int, list[str]]) -> None: + self.path = path + self.annotations = annotations + + +def generate_annotated_html( + html_fnam: str, result: BuildResult, modules: dict[str, ModuleIR] +) -> None: + annotations = [] + for mod, mod_ir in modules.items(): + path = result.graph[mod].path + tree = result.graph[mod].tree + assert tree is not None + annotations.append(generate_annotations(path or "", tree, mod_ir)) + html = generate_html_report(annotations) + with open(html_fnam, "w") as f: + f.write(html) + + formatter = FancyFormatter(sys.stdout, sys.stderr, False) + formatted = formatter.style(os.path.abspath(html_fnam), "none", underline=True, bold=True) + print(f"\nWrote {formatted} -- open in browser to view\n") + + +def generate_annotations(path: str, tree: MypyFile, ir: ModuleIR) -> AnnotatedSource: + anns = {} + for func_ir in ir.functions: + anns.update(function_annotations(func_ir)) + return AnnotatedSource(path, anns) + + +def function_annotations(func_ir: FuncIR) -> dict[int, list[str]]: + # TODO: check if func_ir.line is -1 + anns: dict[int, list[str]] = {} + for block in func_ir.blocks: + for op in block.ops: + if isinstance(op, CallC): + name = op.function_name + ann = None + if name == "CPyObject_GetAttr": + attr_name = get_str_literal(op.args[1]) + if attr_name: + ann = f'Get non-native attribute "{attr_name}".' + else: + ann = "Dynamic attribute lookup." + elif name == "PyNumber_Add": + ann = 'Generic "+" operation.' + if ann: + anns.setdefault(op.line, []).append(ann) + return anns + + +def get_str_literal(v: Value) -> str | None: + if isinstance(v, LoadLiteral) and isinstance(v.value, str): + return v.value + return None + + +def generate_html_report(sources: list[AnnotatedSource]) -> str: + html = [] + html.append("\n\n") + html.append(f"") + html.append("\n") + html.append("\n") + for src in sources: + html.append(f"

{src.path}

\n") + html.append("
")
+        anns = src.annotations
+        with open(src.path) as f:
+            lines = f.readlines()
+        for i, s in enumerate(lines):
+            s = escape(s)
+            line = i + 1
+            linenum = "%5d" % line
+            if line in anns:
+                hint = " ".join(anns[line])
+                s = colorize_line(linenum, s, hint_html=hint)
+            else:
+                s = linenum + "  " + s
+            html.append(s)
+        html.append("
") + + html.append("") + + html.append("\n") + return "".join(html) + + +def colorize_line(linenum: str, s: str, hint_html: str) -> str: + hint_prefix = " " * len(linenum) + " " + line_span = f'
{linenum} {s}
' + hint_div = f'
{hint_prefix}
{hint_html}
' + return f"{line_span}{hint_div}" diff --git a/mypyc/build.py b/mypyc/build.py index d0709fceb97d..cb05cda991d9 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -34,6 +34,7 @@ from mypy.main import process_options from mypy.options import Options from mypy.util import write_junit_xml +from mypyc.annotate import generate_annotated_html from mypyc.codegen import emitmodule from mypyc.common import RUNTIME_C_FILES, shared_lib_name from mypyc.errors import Errors @@ -253,6 +254,9 @@ def generate_c( if compiler_options.verbose: print(f"Compiled to C in {t2 - t1:.3f}s") + if options.mypyc_annotation_file: + generate_annotated_html(options.mypyc_annotation_file, result, modules) + return ctext, "\n".join(format_modules(modules)) diff --git a/mypyc/test-data/annotate-basic.test b/mypyc/test-data/annotate-basic.test new file mode 100644 index 000000000000..d5ea4d6ebd41 --- /dev/null +++ b/mypyc/test-data/annotate-basic.test @@ -0,0 +1,26 @@ +[case testAnnotateNonNativeAttribute] +def f(x): + return x.foo + +class C: + foo: int + +def g(x: C) -> int: + return x.foo +[out] +2: Get non-native attribute "foo". + +[case testAnnotateGenericAdd] +def f(x): + return x + 1 + +def g(x: int) -> int: + return x + 1 +[out] +2: Generic "+" operation. + +[case testAnnotateTwoOperationsOnLine] +def f(x): + return x.foo + 1 +[out] +2: Get non-native attribute "foo". Generic "+" operation. diff --git a/mypyc/test/test_alwaysdefined.py b/mypyc/test/test_alwaysdefined.py index d6c4214ba6a2..9f1487a89bfa 100644 --- a/mypyc/test/test_alwaysdefined.py +++ b/mypyc/test/test_alwaysdefined.py @@ -31,7 +31,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): try: - ir = build_ir_for_single_file2(testcase.input, options) + ir = build_ir_for_single_file2(testcase.input, options)[0] except CompileError as e: actual = e.messages else: diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py new file mode 100644 index 000000000000..5287c6be2546 --- /dev/null +++ b/mypyc/test/test_annotate.py @@ -0,0 +1,48 @@ +"""Test cases for annotating source code to highlight inefficiencies.""" + +from __future__ import annotations + +import os.path + +from mypy.errors import CompileError +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase +from mypyc.annotate import generate_annotations +from mypyc.test.testutil import ( + ICODE_GEN_BUILTINS, + MypycDataSuite, + assert_test_output, + build_ir_for_single_file2, + infer_ir_build_options_from_test_name, + remove_comment_lines, + use_custom_builtins, +) + +files = ["annotate-basic.test"] + + +class TestReport(MypycDataSuite): + files = files + base_path = test_temp_dir + optional_out = True + + def run_case(self, testcase: DataDrivenTestCase) -> None: + """Perform a runtime checking transformation test case.""" + options = infer_ir_build_options_from_test_name(testcase.name) + if options is None: + # Skipped test case + return + with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): + expected_output = remove_comment_lines(testcase.output) + try: + ir, tree = build_ir_for_single_file2(testcase.input, options) + except CompileError as e: + actual = e.messages + else: + annotations = generate_annotations("native.py", tree, ir) + actual = [] + for line, line_anns in annotations.annotations.items(): + s = " ".join(line_anns) + actual.append(f"{line}: {s}") + + assert_test_output(testcase, actual, "Invalid source code output", expected_output) diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 65a29c4b1218..82b052e39805 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -12,6 +12,7 @@ from mypy import build from mypy.errors import CompileError +from mypy.nodes import MypyFile from mypy.options import Options from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite @@ -93,12 +94,12 @@ def perform_test( def build_ir_for_single_file( input_lines: list[str], compiler_options: CompilerOptions | None = None ) -> list[FuncIR]: - return build_ir_for_single_file2(input_lines, compiler_options).functions + return build_ir_for_single_file2(input_lines, compiler_options)[0].functions def build_ir_for_single_file2( input_lines: list[str], compiler_options: CompilerOptions | None = None -) -> ModuleIR: +) -> tuple[ModuleIR, MypyFile]: program_text = "\n".join(input_lines) # By default generate IR compatible with the earliest supported Python C API. @@ -137,7 +138,9 @@ def build_ir_for_single_file2( module = list(modules.values())[0] for fn in module.functions: assert_func_ir_valid(fn) - return module + tree = result.graph[module.fullname].tree + assert tree is not None + return module, tree def update_testcase_output(testcase: DataDrivenTestCase, output: list[str]) -> None: From af96893db9cf4a95491c5b96b82257b894484e8f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 24 Mar 2025 17:24:24 +0000 Subject: [PATCH 1207/1617] [mypyc] Allow specifying annotate test case outputs using comments (#18834) This makes it more convenient to write source code annotation tests. --- mypyc/test-data/annotate-basic.test | 12 +++--------- mypyc/test/test_annotate.py | 11 +++++++++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mypyc/test-data/annotate-basic.test b/mypyc/test-data/annotate-basic.test index d5ea4d6ebd41..45db73a4ef64 100644 --- a/mypyc/test-data/annotate-basic.test +++ b/mypyc/test-data/annotate-basic.test @@ -1,26 +1,20 @@ [case testAnnotateNonNativeAttribute] def f(x): - return x.foo + return x.foo # A: Get non-native attribute "foo". class C: foo: int def g(x: C) -> int: return x.foo -[out] -2: Get non-native attribute "foo". [case testAnnotateGenericAdd] def f(x): - return x + 1 + return x + 1 # A: Generic "+" operation. def g(x: int) -> int: return x + 1 -[out] -2: Generic "+" operation. [case testAnnotateTwoOperationsOnLine] def f(x): - return x.foo + 1 -[out] -2: Get non-native attribute "foo". Generic "+" operation. + return x.foo + 1 # A: Get non-native attribute "foo". Generic "+" operation. diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py index 5287c6be2546..40b28195b5a5 100644 --- a/mypyc/test/test_annotate.py +++ b/mypyc/test/test_annotate.py @@ -34,6 +34,13 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) + + # Parse "# A: " comments. + for i, line in enumerate(testcase.input): + if "# A:" in line: + msg = line.rpartition("# A:")[2].strip() + expected_output.append(f"{i + 1}: {msg}") + try: ir, tree = build_ir_for_single_file2(testcase.input, options) except CompileError as e: @@ -41,8 +48,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: else: annotations = generate_annotations("native.py", tree, ir) actual = [] - for line, line_anns in annotations.annotations.items(): + for line_num, line_anns in annotations.annotations.items(): s = " ".join(line_anns) - actual.append(f"{line}: {s}") + actual.append(f"{line_num}: {s}") assert_test_output(testcase, actual, "Invalid source code output", expected_output) From 62d87095fc7a89cbf83444da55ae4a494feef59a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 24 Mar 2025 18:08:40 +0000 Subject: [PATCH 1208/1617] [mypyc] Display IR on annotate test failure (#18835) This makes it easier to figure out why a test is failing. Example output on failure: ``` =================================== FAILURES ==================================== ________________________ testAnnotateTwoOperationsOnLine ________________________ data: /Users/jukka/src/mypy/mypyc/test-data/annotate-basic.test:18: Failed: Invalid source code output (/Users/jukka/src/mypy/mypyc/test-data/annotate-basic.test, line 18) ----------------------------- Captured stdout call ------------------------------ Generated IR: def f(x): x :: object r0 :: str r1, r2, r3 :: object L0: r0 = 'foo' r1 = CPyObject_GetAttr(x, r0) r2 = object 1 r3 = PyNumber_Add(r1, r2) return r3 ----------------------------- Captured stderr call ------------------------------ Expected: main:2: Get non-native attribute "foo". Generic "+" operation.x (diff) Actual: main:2: Get non-native attribute "foo". Generic "+" operation. (diff) Alignment of first line difference: E: ...Generic "+" operation.x A: ...Generic "+" operation. ^ Update the test output using --update-data (implies -n0; you can additionally use the -k selector to update only specific tests) ``` --- mypyc/test/test_annotate.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py index 40b28195b5a5..f429fb28cd55 100644 --- a/mypyc/test/test_annotate.py +++ b/mypyc/test/test_annotate.py @@ -8,6 +8,7 @@ from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase from mypyc.annotate import generate_annotations +from mypyc.ir.pprint import format_func from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, MypycDataSuite, @@ -39,8 +40,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: for i, line in enumerate(testcase.input): if "# A:" in line: msg = line.rpartition("# A:")[2].strip() - expected_output.append(f"{i + 1}: {msg}") + expected_output.append(f"main:{i + 1}: {msg}") + ir = None try: ir, tree = build_ir_for_single_file2(testcase.input, options) except CompileError as e: @@ -50,6 +52,16 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: actual = [] for line_num, line_anns in annotations.annotations.items(): s = " ".join(line_anns) - actual.append(f"{line_num}: {s}") + actual.append(f"main:{line_num}: {s}") - assert_test_output(testcase, actual, "Invalid source code output", expected_output) + try: + assert_test_output(testcase, actual, "Invalid source code output", expected_output) + except BaseException: + if ir: + print("Generated IR:\n") + for fn in ir.functions: + if fn.name == "__top_level__": + continue + for s in format_func(fn): + print(s) + raise From df9ddfcacd46a9e388776103aebb4a5c0ec404ee Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 24 Mar 2025 20:08:57 +0000 Subject: [PATCH 1209/1617] Consolidate descriptor handling in checkmember.py (#18831) This is not a pure refactoring, but almost. Right now we are in a weird situation where we have two inconsistencies: * `__set__()` is handled in `checker.py` while `__get__()` is handled in `checkmember.py` * rules for when to use binder are slightly different between descriptors and settable properties. This PR fixes these two things. As a nice bonus we should get free support for unions in `__set__()`. --- mypy/checker.py | 120 ++++------------------------------------- mypy/checkexpr.py | 10 +++- mypy/checkmember.py | 126 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 128 insertions(+), 128 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 62acfc9e3abe..12afa4d3edf5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -12,12 +12,7 @@ import mypy.checkexpr from mypy import errorcodes as codes, join, message_registry, nodes, operators from mypy.binder import ConditionalTypeBinder, Frame, get_declaration -from mypy.checkmember import ( - MemberContext, - analyze_decorator_or_funcbase_access, - analyze_descriptor_access, - analyze_member_access, -) +from mypy.checkmember import analyze_member_access from mypy.checkpattern import PatternChecker from mypy.constraints import SUPERTYPE_OF from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values @@ -3233,7 +3228,7 @@ def check_assignment( ) else: self.try_infer_partial_generic_type_from_assignment(lvalue, rvalue, "=") - lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue) + lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue, rvalue) # If we're assigning to __getattr__ or similar methods, check that the signature is # valid. if isinstance(lvalue, NameExpr) and lvalue.node: @@ -4339,7 +4334,9 @@ def check_multi_assignment_from_iterable( else: self.msg.type_not_iterable(rvalue_type, context) - def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, Var | None]: + def check_lvalue( + self, lvalue: Lvalue, rvalue: Expression | None = None + ) -> tuple[Type | None, IndexExpr | None, Var | None]: lvalue_type = None index_lvalue = None inferred = None @@ -4357,7 +4354,7 @@ def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, V elif isinstance(lvalue, IndexExpr): index_lvalue = lvalue elif isinstance(lvalue, MemberExpr): - lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue, True) + lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue, True, rvalue) self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, NameExpr): lvalue_type = self.expr_checker.analyze_ref_expr(lvalue, lvalue=True) @@ -4704,12 +4701,8 @@ def check_member_assignment( Return the inferred rvalue_type, inferred lvalue_type, and whether to use the binder for this assignment. - - Note: this method exists here and not in checkmember.py, because we need to take - care about interaction between binder and __set__(). """ instance_type = get_proper_type(instance_type) - attribute_type = get_proper_type(attribute_type) # Descriptors don't participate in class-attribute access if (isinstance(instance_type, FunctionLike) and instance_type.is_type_obj()) or isinstance( instance_type, TypeType @@ -4721,107 +4714,16 @@ def check_member_assignment( get_lvalue_type = self.expr_checker.analyze_ordinary_member_access( lvalue, is_lvalue=False ) - use_binder = is_same_type(get_lvalue_type, attribute_type) - - if not isinstance(attribute_type, Instance): - # TODO: support __set__() for union types. - rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context) - return rvalue_type, attribute_type, use_binder - - mx = MemberContext( - is_lvalue=False, - is_super=False, - is_operator=False, - original_type=instance_type, - context=context, - self_type=None, - chk=self, - ) - get_type = analyze_descriptor_access(attribute_type, mx, assignment=True) - 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, - # (which allow you to override the descriptor with any value), but preserves - # the type of accessing the attribute (even after the override). - rvalue_type, _ = self.check_simple_assignment(get_type, rvalue, context) - return rvalue_type, get_type, use_binder - - dunder_set = attribute_type.type.get_method("__set__") - if dunder_set is None: - 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( - defn=dunder_set, - itype=attribute_type, - name="__set__", - mx=mx.copy_modified(self_type=attribute_type), - ) - typ = map_instance_to_supertype(attribute_type, dunder_set.info) - dunder_set_type = expand_type_by_instance(bound_method, typ) - - callable_name = self.expr_checker.method_fullname(attribute_type, "__set__") - dunder_set_type = self.expr_checker.transform_callee_type( - callable_name, - dunder_set_type, - [TempNode(instance_type, context=context), rvalue], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - ) - - # For non-overloaded setters, the result should be type-checked like a regular assignment. - # Hence, we first only try to infer the type by using the rvalue as type context. - type_context = rvalue - with self.msg.filter_errors(): - _, inferred_dunder_set_type = self.expr_checker.check_call( - dunder_set_type, - [TempNode(instance_type, context=context), type_context], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - callable_name=callable_name, - ) - - # And now we in fact type check the call, to show errors related to wrong arguments - # count, etc., replacing the type context for non-overloaded setters only. - inferred_dunder_set_type = get_proper_type(inferred_dunder_set_type) - if isinstance(inferred_dunder_set_type, CallableType): - type_context = TempNode(AnyType(TypeOfAny.special_form), context=context) - self.expr_checker.check_call( - dunder_set_type, - [TempNode(instance_type, context=context), type_context], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - callable_name=callable_name, - ) - - # Search for possible deprecations: - mx.chk.check_deprecated(dunder_set, mx.context) - mx.chk.warn_deprecated_overload_item( - dunder_set, mx.context, target=inferred_dunder_set_type, selftype=attribute_type - ) - # In the following cases, a message already will have been recorded in check_call. - if (not isinstance(inferred_dunder_set_type, CallableType)) or ( - len(inferred_dunder_set_type.arg_types) < 2 - ): - return AnyType(TypeOfAny.from_error), get_type, False - - set_type = inferred_dunder_set_type.arg_types[1] # Special case: if the rvalue_type is a subtype of both '__get__' and '__set__' types, # and '__get__' type is narrower than '__set__', then we invoke the binder to narrow type # by this assignment. Technically, this is not safe, but in practice this is # what a user expects. - rvalue_type, _ = self.check_simple_assignment(set_type, rvalue, context) - infer = is_subtype(rvalue_type, get_type) and is_subtype(get_type, set_type) - return rvalue_type if infer else set_type, get_type, infer + rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context) + infer = is_subtype(rvalue_type, get_lvalue_type) and is_subtype( + get_lvalue_type, attribute_type + ) + return rvalue_type if infer else attribute_type, attribute_type, infer def check_indexed_assignment( self, lvalue: IndexExpr, rvalue: Expression, context: Context diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 812121994fd7..0804917476a9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3334,8 +3334,13 @@ def visit_member_expr(self, e: MemberExpr, is_lvalue: bool = False) -> Type: self.chk.warn_deprecated(e.node, e) return narrowed - def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type: - """Analyse member expression or member lvalue.""" + def analyze_ordinary_member_access( + self, e: MemberExpr, is_lvalue: bool, rvalue: Expression | None = None + ) -> Type: + """Analyse member expression or member lvalue. + + An rvalue can be provided optionally to infer better setter type when is_lvalue is True. + """ if e.kind is not None: # This is a reference to a module attribute. return self.analyze_ref_expr(e) @@ -3366,6 +3371,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, + rvalue=rvalue, ) return member_type diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0535486bfd4a..ebc4fe8705ce 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -21,6 +21,7 @@ SYMBOL_FUNCBASE_TYPES, Context, Decorator, + Expression, FuncBase, FuncDef, IndexExpr, @@ -96,6 +97,7 @@ def __init__( module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, + rvalue: Expression | None = None, ) -> None: self.is_lvalue = is_lvalue self.is_super = is_super @@ -108,6 +110,9 @@ def __init__( self.module_symbol_table = module_symbol_table self.no_deferral = no_deferral self.is_self = is_self + if rvalue is not None: + assert is_lvalue + self.rvalue = rvalue def named_type(self, name: str) -> Instance: return self.chk.named_type(name) @@ -132,6 +137,7 @@ def copy_modified( self_type=self.self_type, module_symbol_table=self.module_symbol_table, no_deferral=self.no_deferral, + rvalue=self.rvalue, ) if self_type is not None: mx.self_type = self_type @@ -158,6 +164,7 @@ def analyze_member_access( module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, + rvalue: Expression | None = None, ) -> Type: """Return the type of attribute 'name' of 'typ'. @@ -176,11 +183,14 @@ def analyze_member_access( of 'original_type'. 'original_type' is always preserved as the 'typ' type used in the initial, non-recursive call. The 'self_type' is a component of 'original_type' to which generic self should be bound (a narrower type that has a fallback to instance). - Currently this is used only for union types. + Currently, this is used only for union types. - 'module_symbol_table' is passed to this function if 'typ' is actually a module + 'module_symbol_table' is passed to this function if 'typ' is actually a module, and we want to keep track of the available attributes of the module (since they are not available via the type object directly) + + 'rvalue' can be provided optionally to infer better setter type when is_lvalue is True, + most notably this helps for descriptors with overloaded __set__() method. """ mx = MemberContext( is_lvalue=is_lvalue, @@ -193,6 +203,7 @@ def analyze_member_access( module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self, + rvalue=rvalue, ) result = _analyze_member_access(name, typ, mx, override_info) possible_literal = get_proper_type(result) @@ -619,9 +630,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, *, assignment: bool = False -) -> Type: +def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: """Type check descriptor access. Arguments: @@ -629,7 +638,7 @@ def analyze_descriptor_access( (the type of ``f`` in ``a.f`` when ``f`` is a descriptor). mx: The current member access context. Return: - The return type of the appropriate ``__get__`` overload for the descriptor. + The return type of the appropriate ``__get__/__set__`` overload for the descriptor. """ instance_type = get_proper_type(mx.self_type) orig_descriptor_type = descriptor_type @@ -638,15 +647,24 @@ def analyze_descriptor_access( if isinstance(descriptor_type, UnionType): # Map the access over union types return make_simplified_union( - [ - analyze_descriptor_access(typ, mx, assignment=assignment) - 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 - if not descriptor_type.type.has_readable_member("__get__"): + if not mx.is_lvalue and not descriptor_type.type.has_readable_member("__get__"): + return orig_descriptor_type + + # We do this check first to accommodate for descriptors with only __set__ method. + # 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, + # (which allow you to override the descriptor with any value), but preserves + # the type of accessing the attribute (even after the override). + if mx.is_lvalue and descriptor_type.type.has_readable_member("__set__"): + return analyze_descriptor_assign(descriptor_type, mx) + + if mx.is_lvalue and not descriptor_type.type.has_readable_member("__get__"): + # This turned out to be not a descriptor after all. return orig_descriptor_type dunder_get = descriptor_type.type.get_method("__get__") @@ -703,11 +721,10 @@ def analyze_descriptor_access( callable_name=callable_name, ) - if not assignment: - mx.chk.check_deprecated(dunder_get, mx.context) - mx.chk.warn_deprecated_overload_item( - dunder_get, mx.context, target=inferred_dunder_get_type, selftype=descriptor_type - ) + mx.chk.check_deprecated(dunder_get, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_get, mx.context, target=inferred_dunder_get_type, selftype=descriptor_type + ) inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type) if isinstance(inferred_dunder_get_type, AnyType): @@ -726,6 +743,79 @@ def analyze_descriptor_access( return inferred_dunder_get_type.ret_type +def analyze_descriptor_assign(descriptor_type: Instance, mx: MemberContext) -> Type: + instance_type = get_proper_type(mx.self_type) + dunder_set = descriptor_type.type.get_method("__set__") + if dunder_set is None: + mx.chk.fail( + message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format( + descriptor_type.str_with_options(mx.msg.options) + ), + mx.context, + ) + return AnyType(TypeOfAny.from_error) + + bound_method = analyze_decorator_or_funcbase_access( + defn=dunder_set, + itype=descriptor_type, + name="__set__", + mx=mx.copy_modified(is_lvalue=False, self_type=descriptor_type), + ) + typ = map_instance_to_supertype(descriptor_type, dunder_set.info) + dunder_set_type = expand_type_by_instance(bound_method, typ) + + callable_name = mx.chk.expr_checker.method_fullname(descriptor_type, "__set__") + rvalue = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + dunder_set_type = mx.chk.expr_checker.transform_callee_type( + callable_name, + dunder_set_type, + [TempNode(instance_type, context=mx.context), rvalue], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + ) + + # For non-overloaded setters, the result should be type-checked like a regular assignment. + # Hence, we first only try to infer the type by using the rvalue as type context. + type_context = rvalue + with mx.msg.filter_errors(): + _, inferred_dunder_set_type = mx.chk.expr_checker.check_call( + dunder_set_type, + [TempNode(instance_type, context=mx.context), type_context], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + callable_name=callable_name, + ) + + # And now we in fact type check the call, to show errors related to wrong arguments + # count, etc., replacing the type context for non-overloaded setters only. + inferred_dunder_set_type = get_proper_type(inferred_dunder_set_type) + if isinstance(inferred_dunder_set_type, CallableType): + type_context = TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + mx.chk.expr_checker.check_call( + dunder_set_type, + [TempNode(instance_type, context=mx.context), type_context], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + callable_name=callable_name, + ) + + # Search for possible deprecations: + mx.chk.check_deprecated(dunder_set, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_set, mx.context, target=inferred_dunder_set_type, selftype=descriptor_type + ) + + # In the following cases, a message already will have been recorded in check_call. + if (not isinstance(inferred_dunder_set_type, CallableType)) or ( + len(inferred_dunder_set_type.arg_types) < 2 + ): + return AnyType(TypeOfAny.from_error) + return inferred_dunder_set_type.arg_types[1] + + def is_instance_var(var: Var) -> bool: """Return if var is an instance variable according to PEP 526.""" return ( @@ -810,6 +900,7 @@ def analyze_var( # A property cannot have an overloaded type => the cast is fine. assert isinstance(expanded_signature, CallableType) if var.is_settable_property and mx.is_lvalue and var.setter_type is not None: + # TODO: use check_call() to infer better type, same as for __set__(). result = expanded_signature.arg_types[0] else: result = expanded_signature.ret_type @@ -822,7 +913,7 @@ def analyze_var( result = AnyType(TypeOfAny.special_form) fullname = f"{var.info.fullname}.{name}" hook = mx.chk.plugin.get_attribute_hook(fullname) - if result and not mx.is_lvalue and not implicit: + if result and not (implicit or var.info.is_protocol and is_instance_var(var)): result = analyze_descriptor_access(result, mx) if hook: result = hook( @@ -1075,6 +1166,7 @@ def analyze_class_attribute_access( result = add_class_tvars( t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars ) + # __set__ is not called on class objects. if not mx.is_lvalue: result = analyze_descriptor_access(result, mx) From de3bec4c128e48c37508d264bf21bdfd5869ddf6 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Mon, 24 Mar 2025 21:12:02 +0100 Subject: [PATCH 1210/1617] add `scipy-stubs` as non-typeshed stub package (#18832) SciPy itself has no `py.typed` and barely any stubs, so I wrote [`scipy-stubs`](https://github.com/scipy/scipy-stubs). Recently, it has been accepted as an official scipy project. This stubs-only package is *complete* (no `untyped`) and *valid* (according to mypy, stubtest, pyright, basedmypy and basedpyright), and carefully annotated (by humans). And for what it's worth, it's also on the list of `mypy_primer` projects. I'm open to any feedback, questions, and ideas in general; no need to hold back :) --- mypy/stubinfo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 91755b2b5041..97a59425f418 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -291,6 +291,7 @@ def stub_distribution_name(module: str) -> str | None: # for additions here "pandas": "pandas-stubs", # https://github.com/pandas-dev/pandas-stubs "lxml": "lxml-stubs", # https://github.com/lxml/lxml-stubs + "scipy": "scipy-stubs", # https://github.com/scipy/scipy-stubs } From f6295899f4bcda61dc4017fabdb9c85c844bcc53 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:01:37 -0700 Subject: [PATCH 1211/1617] Drop pkg_resources from stubinfo (#18840) Fixes #18839 --- mypy/stubinfo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 97a59425f418..33064c9d3067 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -47,7 +47,6 @@ def stub_distribution_name(module: str) -> str | None: "mock": "types-mock", "OpenSSL": "types-pyOpenSSL", "paramiko": "types-paramiko", - "pkg_resources": "types-setuptools", "polib": "types-polib", "pycurl": "types-pycurl", "pymysql": "types-PyMySQL", From b1be379f88e1e3735e04931f8253bbde4b387602 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 26 Mar 2025 16:56:30 +0000 Subject: [PATCH 1212/1617] [mypyc] Detect more issues when generating annotated HTML (#18838) Add new heuristics to detect various potential performance issues when using `-a foo.html`. The issues include various generic (non-specialized) operations, uses of `functools` and `itertools`, and slow `isinstance` checks that use runtime-checkable protocols. Implement a mypy AST visitor that is used to detect some issues that would be harder to detect when analyzing the generated IR. Support annotation priorities so that if multiple annotations are generated for a line, only the highest-priority ones are shown. This is a bit crude but useful, since often multiple heuristics are triggered by some inefficient code, and duplicate annotations would be verbose and sometimes confusing. --- mypyc/annotate.py | 241 +++++++++++++++++++++++-- mypyc/irbuild/ll_builder.py | 2 +- mypyc/test-data/annotate-basic.test | 267 +++++++++++++++++++++++++++- mypyc/test-data/fixtures/ir.py | 1 + mypyc/test/test_annotate.py | 10 +- mypyc/test/testutil.py | 7 +- 6 files changed, 499 insertions(+), 29 deletions(-) diff --git a/mypyc/annotate.py b/mypyc/annotate.py index 0a7c5439b7ca..3368a68832bd 100644 --- a/mypyc/annotate.py +++ b/mypyc/annotate.py @@ -1,15 +1,90 @@ +"""Generate source code formatted as HTML, with bottlenecks annotated and highlighted. + +Various heuristics are used to detect common issues that cause slower than +expected performance. +""" + from __future__ import annotations import os.path import sys from html import escape +from typing import Final from mypy.build import BuildResult -from mypy.nodes import MypyFile +from mypy.nodes import ( + CallExpr, + Expression, + ForStmt, + FuncDef, + LambdaExpr, + MemberExpr, + MypyFile, + NameExpr, + Node, + RefExpr, + TupleExpr, + TypeInfo, + Var, +) +from mypy.traverser import TraverserVisitor +from mypy.types import AnyType, Instance, ProperType, Type, TypeOfAny, get_proper_type from mypy.util import FancyFormatter from mypyc.ir.func_ir import FuncIR from mypyc.ir.module_ir import ModuleIR -from mypyc.ir.ops import CallC, LoadLiteral, Value +from mypyc.ir.ops import CallC, LoadLiteral, LoadStatic, Value + + +class Annotation: + """HTML annotation for compiled source code""" + + def __init__(self, message: str, priority: int = 1) -> None: + # Message as HTML that describes an issue and/or how to fix it. + # Multiple messages on a line may be concatenated. + self.message = message + # If multiple annotations are generated for a single line, only report + # the highest-priority ones. Some use cases generate multiple annotations, + # and this can be used to reduce verbosity by hiding the lower-priority + # ones. + self.priority = priority + + +op_hints: Final = { + "PyNumber_Add": Annotation('Generic "+" operation.'), + "PyNumber_Subtract": Annotation('Generic "-" operation.'), + "PyNumber_Multiply": Annotation('Generic "*" operation.'), + "PyNumber_TrueDivide": Annotation('Generic "/" operation.'), + "PyNumber_FloorDivide": Annotation('Generic "//" operation.'), + "PyNumber_Positive": Annotation('Generic unary "+" operation.'), + "PyNumber_Negative": Annotation('Generic unary "-" operation.'), + "PyNumber_And": Annotation('Generic "&" operation.'), + "PyNumber_Or": Annotation('Generic "|" operation.'), + "PyNumber_Xor": Annotation('Generic "^" operation.'), + "PyNumber_Lshift": Annotation('Generic "<<" operation.'), + "PyNumber_Rshift": Annotation('Generic ">>" operation.'), + "PyNumber_Invert": Annotation('Generic "~" operation.'), + "PyObject_Call": Annotation("Generic call operation."), + "PyObject_RichCompare": Annotation("Generic comparison operation."), + "PyObject_GetItem": Annotation("Generic indexing operation."), + "PyObject_SetItem": Annotation("Generic indexed assignment."), +} + +stdlib_hints: Final = { + "functools.partial": Annotation( + '"functools.partial" is inefficient in compiled code.', priority=2 + ), + "itertools.chain": Annotation( + '"itertools.chain" is inefficient in compiled code (hint: replace with for loops).', + priority=2, + ), + "itertools.groupby": Annotation( + '"itertools.groupby" is inefficient in compiled code.', priority=2 + ), + "itertools.islice": Annotation( + '"itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range).', + priority=2, + ), +} CSS = """\ .collapsible { @@ -44,7 +119,9 @@ class AnnotatedSource: - def __init__(self, path: str, annotations: dict[int, list[str]]) -> None: + """Annotations for a single compiled source file.""" + + def __init__(self, path: str, annotations: dict[int, list[Annotation]]) -> None: self.path = path self.annotations = annotations @@ -57,7 +134,7 @@ def generate_annotated_html( path = result.graph[mod].path tree = result.graph[mod].tree assert tree is not None - annotations.append(generate_annotations(path or "", tree, mod_ir)) + annotations.append(generate_annotations(path or "", tree, mod_ir, result.types)) html = generate_html_report(annotations) with open(html_fnam, "w") as f: f.write(html) @@ -67,40 +144,172 @@ def generate_annotated_html( print(f"\nWrote {formatted} -- open in browser to view\n") -def generate_annotations(path: str, tree: MypyFile, ir: ModuleIR) -> AnnotatedSource: +def generate_annotations( + path: str, tree: MypyFile, ir: ModuleIR, type_map: dict[Expression, Type] +) -> AnnotatedSource: anns = {} for func_ir in ir.functions: - anns.update(function_annotations(func_ir)) + anns.update(function_annotations(func_ir, tree)) + visitor = ASTAnnotateVisitor(type_map) + for defn in tree.defs: + defn.accept(visitor) + anns.update(visitor.anns) return AnnotatedSource(path, anns) -def function_annotations(func_ir: FuncIR) -> dict[int, list[str]]: +def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Annotation]]: + """Generate annotations based on mypyc IR.""" # TODO: check if func_ir.line is -1 - anns: dict[int, list[str]] = {} + anns: dict[int, list[Annotation]] = {} for block in func_ir.blocks: for op in block.ops: if isinstance(op, CallC): name = op.function_name - ann = None + ann: str | Annotation | None = None if name == "CPyObject_GetAttr": attr_name = get_str_literal(op.args[1]) - if attr_name: + if attr_name == "__prepare__": + # These attributes are internal to mypyc/CPython, and the user has + # little control over them. + ann = None + elif attr_name: ann = f'Get non-native attribute "{attr_name}".' else: ann = "Dynamic attribute lookup." - elif name == "PyNumber_Add": - ann = 'Generic "+" operation.' + elif name == "PyObject_VectorcallMethod": + method_name = get_str_literal(op.args[0]) + if method_name: + ann = f'Call non-native method "{method_name}".' + else: + ann = "Dynamic method call." + elif name in op_hints: + ann = op_hints[name] + elif name in ("CPyDict_GetItem", "CPyDict_SetItem"): + if ( + isinstance(op.args[0], LoadStatic) + and isinstance(op.args[1], LoadLiteral) + and func_ir.name != "__top_level__" + ): + load = op.args[0] + name = str(op.args[1].value) + sym = tree.names.get(name) + if ( + sym + and sym.node + and load.namespace == "static" + and load.identifier == "globals" + ): + if sym.node.fullname in stdlib_hints: + ann = stdlib_hints[sym.node.fullname] + elif isinstance(sym.node, Var): + ann = ( + f'Access global "{name}" through namespace ' + + "dictionary (hint: access is faster if you can make it Final)." + ) + else: + ann = f'Access "{name}" through global namespace dictionary.' if ann: + if isinstance(ann, str): + ann = Annotation(ann) anns.setdefault(op.line, []).append(ann) return anns +class ASTAnnotateVisitor(TraverserVisitor): + """Generate annotations from mypy AST and inferred types.""" + + def __init__(self, type_map: dict[Expression, Type]) -> None: + self.anns: dict[int, list[Annotation]] = {} + self.func_depth = 0 + self.type_map = type_map + + def visit_func_def(self, o: FuncDef, /) -> None: + if self.func_depth > 0: + self.annotate( + o, + "A nested function object is allocated each time statement is executed. " + + "A module-level function would be faster.", + ) + self.func_depth += 1 + super().visit_func_def(o) + self.func_depth -= 1 + + def visit_for_stmt(self, o: ForStmt, /) -> None: + typ = self.get_type(o.expr) + if isinstance(typ, AnyType): + self.annotate(o.expr, 'For loop uses generic operations (iterable has type "Any").') + elif isinstance(typ, Instance) and typ.type.fullname in ( + "typing.Iterable", + "typing.Iterator", + "typing.Sequence", + "typing.MutableSequence", + ): + self.annotate( + o.expr, + f'For loop uses generic operations (iterable has the abstract type "{typ.type.fullname}").', + ) + super().visit_for_stmt(o) + + def visit_name_expr(self, o: NameExpr, /) -> None: + if ann := stdlib_hints.get(o.fullname): + self.annotate(o, ann) + + def visit_member_expr(self, o: MemberExpr, /) -> None: + super().visit_member_expr(o) + if ann := stdlib_hints.get(o.fullname): + self.annotate(o, ann) + + def visit_call_expr(self, o: CallExpr, /) -> None: + super().visit_call_expr(o) + if ( + isinstance(o.callee, RefExpr) + and o.callee.fullname == "builtins.isinstance" + and len(o.args) == 2 + ): + arg = o.args[1] + self.check_isinstance_arg(arg) + + def check_isinstance_arg(self, arg: Expression) -> None: + if isinstance(arg, RefExpr): + if isinstance(arg.node, TypeInfo) and arg.node.is_protocol: + self.annotate( + arg, f'Expensive isinstance() check against protocol "{arg.node.name}".' + ) + elif isinstance(arg, TupleExpr): + for item in arg.items: + self.check_isinstance_arg(item) + + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: + self.annotate( + o, + "A new object is allocated for lambda each time it is evaluated. " + + "A module-level function would be faster.", + ) + super().visit_lambda_expr(o) + + def annotate(self, o: Node, ann: str | Annotation) -> None: + if isinstance(ann, str): + ann = Annotation(ann) + self.anns.setdefault(o.line, []).append(ann) + + def get_type(self, e: Expression) -> ProperType: + t = self.type_map.get(e) + if t: + return get_proper_type(t) + return AnyType(TypeOfAny.unannotated) + + def get_str_literal(v: Value) -> str | None: if isinstance(v, LoadLiteral) and isinstance(v.value, str): return v.value return None +def get_max_prio(anns: list[Annotation]) -> list[Annotation]: + max_prio = max(a.priority for a in anns) + return [a for a in anns if a.priority == max_prio] + + def generate_html_report(sources: list[AnnotatedSource]) -> str: html = [] html.append("\n\n") @@ -110,15 +319,17 @@ def generate_html_report(sources: list[AnnotatedSource]) -> str: for src in sources: html.append(f"

{src.path}

\n") html.append("
")
-        anns = src.annotations
+        src_anns = src.annotations
         with open(src.path) as f:
             lines = f.readlines()
         for i, s in enumerate(lines):
             s = escape(s)
             line = i + 1
             linenum = "%5d" % line
-            if line in anns:
-                hint = " ".join(anns[line])
+            if line in src_anns:
+                anns = get_max_prio(src_anns[line])
+                ann_strs = [a.message for a in anns]
+                hint = " ".join(ann_strs)
                 s = colorize_line(linenum, s, hint_html=hint)
             else:
                 s = linenum + "  " + s
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 7219d5d5e708..6bc1eb9d0493 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -1161,7 +1161,7 @@ def gen_method_call(
         """Generate either a native or Python method call."""
         # If we have *args, then fallback to Python method call.
         if arg_kinds is not None and any(kind.is_star() for kind in arg_kinds):
-            return self.py_method_call(base, name, arg_values, base.line, arg_kinds, arg_names)
+            return self.py_method_call(base, name, arg_values, line, arg_kinds, arg_names)
 
         # If the base type is one of ours, do a MethodCall
         if (
diff --git a/mypyc/test-data/annotate-basic.test b/mypyc/test-data/annotate-basic.test
index 45db73a4ef64..23e9ae8814ca 100644
--- a/mypyc/test-data/annotate-basic.test
+++ b/mypyc/test-data/annotate-basic.test
@@ -1,20 +1,275 @@
 [case testAnnotateNonNativeAttribute]
-def f(x):
+from typing import Any
+
+def f1(x):
+    return x.foo  # A: Get non-native attribute "foo".
+
+def f2(x: Any) -> object:
     return x.foo  # A: Get non-native attribute "foo".
 
 class C:
     foo: int
 
-def g(x: C) -> int:
+def f3(x: C) -> int:
     return x.foo
 
-[case testAnnotateGenericAdd]
-def f(x):
+[case testAnnotateGenericBinaryOperations]
+def generic_add(x):
     return x + 1  # A: Generic "+" operation.
 
-def g(x: int) -> int:
-    return x + 1
+def generic_sub(x):
+    return x - 1  # A: Generic "-" operation.
+
+def generic_mul(x):
+    return x * 1  # A: Generic "*" operation.
+
+def generic_div(x):
+    return x / 1  # A: Generic "/" operation.
+
+def generic_floor_div(x):
+    return x // 1  # A: Generic "//" operation.
+
+def generic_unary_plus(x):
+    return +x  # A: Generic unary "+" operation.
+
+def generic_unary_minus(x):
+    return -x  # A: Generic unary "-" operation.
+
+def native_int_ops(x: int, y: int) -> int:
+    a = x + 1 - y
+    return x * a // y
+
+[case testAnnotateGenericBitwiseOperations]
+def generic_and(x):
+    return x & 1  # A: Generic "&" operation.
+
+def generic_or(x):
+    return x | 1  # A: Generic "|" operation.
+
+def generic_xor(x):
+    return x ^ 1  # A: Generic "^" operation.
+
+def generic_left_shift(x):
+    return x << 1  # A: Generic "<<" operation.
+
+def generic_right_shift(x):
+    return x >> 1  # A: Generic ">>" operation.
+
+def generic_invert(x):
+    return ~x  # A: Generic "~" operation.
+
+def native_int_ops(x: int, y: int) -> int:
+    a = (x & 1) << y
+    return (x | a) >> (y ^ 1)
+
+[case testAnnotateGenericComparisonOperations]
+def generic_eq(x, y):
+    return x == y  # A: Generic comparison operation.
+
+def generic_ne(x, y):
+    return x != y  # A: Generic comparison operation.
+
+def generic_lt(x, y):
+    return x < y  # A: Generic comparison operation.
+
+def generic_le(x, y):
+    return x <= y  # A: Generic comparison operation.
+
+def generic_gt(x, y):
+    return x > y  # A: Generic comparison operation.
+
+def generic_ge(x, y):
+    return x >= y  # A: Generic comparison operation.
+
+def int_comparisons(x: int, y: int) -> int:
+    if x == y:
+        return 0
+    if x < y:
+        return 1
+    if x > y:
+        return 2
+    return 3
 
 [case testAnnotateTwoOperationsOnLine]
 def f(x):
     return x.foo + 1  # A: Get non-native attribute "foo". Generic "+" operation.
+
+[case testAnnotateNonNativeMethod]
+from typing import Any
+
+def f1(x):
+    return x.foo()  # A: Call non-native method "foo".
+
+def f2(x: Any) -> None:
+    x.foo(1)  # A: Call non-native method "foo".
+    x.foo(a=1)  # A: Call non-native method "foo".
+    t = (1, 'x')
+    x.foo(*t)  # A: Get non-native attribute "foo". Generic call operation.
+    d = {"a": 1}
+    x.foo(*d)  # A: Get non-native attribute "foo". Generic call operation.
+
+class C:
+    def foo(self) -> int:
+        return 0
+
+def g(c: C) -> int:
+    return c.foo()
+
+[case testAnnotateGlobalVariableAccess]
+from typing import Final
+import nonnative
+
+x = 0
+y: Final = 0
+
+def read() -> int:
+    return x  # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final).
+
+def assign(a: int) -> None:
+    global x
+    x = a  # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final).
+
+def read_final() -> int:
+    return y
+
+def read_nonnative() -> int:
+    return nonnative.z  # A: Get non-native attribute "z".
+
+[file nonnative.py]
+z = 2
+
+[case testAnnotateNestedFunction]
+def f1() -> None:
+    def g() -> None:  # A: A nested function object is allocated each time statement is executed. A module-level function would be faster.
+        pass
+
+    g()
+
+def f2() -> int:
+    l = lambda: 1  # A: A new object is allocated for lambda each time it is evaluated. A module-level function would be faster.
+    return l()
+
+[case testAnnotateGetSetItem]
+from typing import List, Dict
+
+def f1(x, y):
+    return x[y]  # A: Generic indexing operation.
+
+def f2(x, y, z):
+    x[y] = z  # A: Generic indexed assignment.
+
+def list_get_item(x: List[int], y: int) -> int:
+    return x[y]
+
+def list_set_item(x: List[int], y: int) -> None:
+    x[y] = 5
+
+def dict_get_item(d: Dict[str, str]) -> str:
+    return d['x']
+
+def dict_set_item(d: Dict[str, str]) -> None:
+    d['x'] = 'y'
+
+[case testAnnotateStrMethods]
+def startswith(x: str) -> bool:
+    return x.startswith('foo')
+
+def islower(x: str) -> bool:
+    return x.islower()  # A: Call non-native method "islower".
+
+[case testAnnotateSpecificStdlibFeatures]
+import functools
+import itertools
+from functools import partial
+from itertools import chain, groupby, islice
+
+def f(x: int, y: int) -> None: pass
+
+def use_partial1() -> None:
+    p = partial(f, 1)  # A: "functools.partial" is inefficient in compiled code.
+    p(2)
+
+def use_partial2() -> None:
+    p = functools.partial(f, 1)  # A: "functools.partial" is inefficient in compiled code.
+    p(2)
+
+def use_chain1() -> None:
+    for x in chain([1, 3], [4, 5]):  # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops).
+        pass
+
+def use_chain2() -> None:
+    for x in itertools.chain([1, 3], [4, 5]):  # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops).
+        pass
+
+def use_groupby1() -> None:
+    for a, b in groupby([('A', 'B')]):  # A: "itertools.groupby" is inefficient in compiled code.
+        pass
+
+def use_groupby2() -> None:
+    for a, b in itertools.groupby([('A', 'B')]):  # A: "itertools.groupby" is inefficient in compiled code.
+        pass
+
+def use_islice() -> None:
+    for x in islice([1, 2, 3], 1, 2):  # A: "itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range).
+        pass
+
+[case testAnnotateGenericForLoop]
+from typing import Iterable, Sequence, Iterator, List
+
+def f1(a):
+    for x in a:  # A: For loop uses generic operations (iterable has type "Any").
+        pass
+
+def f2(a: Iterable[str]) -> None:
+    for x in a:  # A: For loop uses generic operations (iterable has the abstract type "typing.Iterable").
+        pass
+
+def f3(a: Sequence[str]) -> None:
+    for x in a:  # A: For loop uses generic operations (iterable has the abstract type "typing.Sequence").
+        pass
+
+def f4(a: Iterator[str]) -> None:
+    for x in a:  # A: For loop uses generic operations (iterable has the abstract type "typing.Iterator").
+        pass
+
+def good1(a: List[str]) -> None:
+    for x in a:
+        pass
+
+class C:
+    def __iter__(self) -> Iterator[str]:
+        assert False
+
+def good2(a: List[str]) -> None:
+    for x in a:
+        pass
+
+[case testAnnotateIsinstance]
+from typing import Protocol, runtime_checkable, Union
+
+@runtime_checkable
+class P(Protocol):
+    def foo(self) -> None: ...
+
+class C: pass
+
+class D(C):
+    def bar(self) -> None: pass
+
+def bad1(x: object) -> bool:
+    return isinstance(x, P)  # A: Expensive isinstance() check against protocol "P".
+
+def bad2(x: object) -> bool:
+    return isinstance(x, (str, P))  # A: Expensive isinstance() check against protocol "P".
+
+def good1(x: C) -> bool:
+    if isinstance(x, D):
+        x.bar()
+    return isinstance(x, D)
+
+def good2(x: Union[int, str]) -> int:
+    if isinstance(x, int):
+        return x + 1
+    else:
+        return int(x + "1")
+[typing fixtures/typing-full.pyi]
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index b908b4c3fc1f..16a3bfdbb9c8 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -121,6 +121,7 @@ def partition(self, sep: str, /) -> Tuple[str, str, str]: ...
     def rpartition(self, sep: str, /) -> Tuple[str, str, str]: ...
     def removeprefix(self, prefix: str, /) -> str: ...
     def removesuffix(self, suffix: str, /) -> str: ...
+    def islower(self) -> bool: ...
 
 class float:
     def __init__(self, x: object) -> None: pass
diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py
index f429fb28cd55..bb4941064bdb 100644
--- a/mypyc/test/test_annotate.py
+++ b/mypyc/test/test_annotate.py
@@ -7,7 +7,7 @@
 from mypy.errors import CompileError
 from mypy.test.config import test_temp_dir
 from mypy.test.data import DataDrivenTestCase
-from mypyc.annotate import generate_annotations
+from mypyc.annotate import generate_annotations, get_max_prio
 from mypyc.ir.pprint import format_func
 from mypyc.test.testutil import (
     ICODE_GEN_BUILTINS,
@@ -44,14 +44,16 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
 
             ir = None
             try:
-                ir, tree = build_ir_for_single_file2(testcase.input, options)
+                ir, tree, type_map = build_ir_for_single_file2(testcase.input, options)
             except CompileError as e:
                 actual = e.messages
             else:
-                annotations = generate_annotations("native.py", tree, ir)
+                annotations = generate_annotations("native.py", tree, ir, type_map)
                 actual = []
                 for line_num, line_anns in annotations.annotations.items():
-                    s = " ".join(line_anns)
+                    anns = get_max_prio(line_anns)
+                    str_anns = [a.message for a in anns]
+                    s = " ".join(str_anns)
                     actual.append(f"main:{line_num}: {s}")
 
             try:
diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py
index 82b052e39805..1961c47e85ee 100644
--- a/mypyc/test/testutil.py
+++ b/mypyc/test/testutil.py
@@ -12,11 +12,12 @@
 
 from mypy import build
 from mypy.errors import CompileError
-from mypy.nodes import MypyFile
+from mypy.nodes import Expression, MypyFile
 from mypy.options import Options
 from mypy.test.config import test_temp_dir
 from mypy.test.data import DataDrivenTestCase, DataSuite
 from mypy.test.helpers import assert_string_arrays_equal
+from mypy.types import Type
 from mypyc.analysis.ircheck import assert_func_ir_valid
 from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE
 from mypyc.errors import Errors
@@ -99,7 +100,7 @@ def build_ir_for_single_file(
 
 def build_ir_for_single_file2(
     input_lines: list[str], compiler_options: CompilerOptions | None = None
-) -> tuple[ModuleIR, MypyFile]:
+) -> tuple[ModuleIR, MypyFile, dict[Expression, Type]]:
     program_text = "\n".join(input_lines)
 
     # By default generate IR compatible with the earliest supported Python C API.
@@ -140,7 +141,7 @@ def build_ir_for_single_file2(
         assert_func_ir_valid(fn)
     tree = result.graph[module.fullname].tree
     assert tree is not None
-    return module, tree
+    return module, tree, result.types
 
 
 def update_testcase_output(testcase: DataDrivenTestCase, output: list[str]) -> None:

From 16f134e9550f625d5711595c1cb59c8e04180a76 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 27 Mar 2025 15:43:54 +0100
Subject: [PATCH 1213/1617] [mypyc] Optimize builtins.repr (#18844)

https://docs.python.org/3/c-api/object.html#c.PyObject_Repr
---
 mypyc/doc/str_operations.rst     |  2 ++
 mypyc/primitives/int_ops.py      | 38 ++++++++++++++++----------------
 mypyc/primitives/str_ops.py      |  9 ++++++++
 mypyc/test-data/run-strings.test | 26 ++++++++++++++++++++--
 4 files changed, 54 insertions(+), 21 deletions(-)

diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst
index 11828a4d128a..4a7aff00f2ad 100644
--- a/mypyc/doc/str_operations.rst
+++ b/mypyc/doc/str_operations.rst
@@ -12,6 +12,8 @@ Construction
 * String literal
 * ``str(x: int)``
 * ``str(x: object)``
+* ``repr(x: int)``
+* ``repr(x: object)``
 
 Operators
 ---------
diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py
index 657578d20046..9b8b48da602d 100644
--- a/mypyc/primitives/int_ops.py
+++ b/mypyc/primitives/int_ops.py
@@ -77,25 +77,25 @@
         error_kind=ERR_MAGIC,
     )
 
-# str(int)
-int_to_str_op = function_op(
-    name="builtins.str",
-    arg_types=[int_rprimitive],
-    return_type=str_rprimitive,
-    c_function_name="CPyTagged_Str",
-    error_kind=ERR_MAGIC,
-    priority=2,
-)
-
-# We need a specialization for str on bools also since the int one is wrong...
-function_op(
-    name="builtins.str",
-    arg_types=[bool_rprimitive],
-    return_type=str_rprimitive,
-    c_function_name="CPyBool_Str",
-    error_kind=ERR_MAGIC,
-    priority=3,
-)
+for name in ("builtins.str", "builtins.repr"):
+    # str(int) and repr(int)
+    int_to_str_op = function_op(
+        name=name,
+        arg_types=[int_rprimitive],
+        return_type=str_rprimitive,
+        c_function_name="CPyTagged_Str",
+        error_kind=ERR_MAGIC,
+        priority=2,
+    )
+    # We need a specialization for str on bools also since the int one is wrong...
+    function_op(
+        name=name,
+        arg_types=[bool_rprimitive],
+        return_type=str_rprimitive,
+        c_function_name="CPyBool_Str",
+        error_kind=ERR_MAGIC,
+        priority=3,
+    )
 
 
 def int_binary_primitive(
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index 75d47b0f0e7a..ded339b9672c 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -38,6 +38,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# repr(obj)
+function_op(
+    name="builtins.repr",
+    arg_types=[object_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="PyObject_Repr",
+    error_kind=ERR_MAGIC,
+)
+
 # str1 + str2
 binary_op(
     name="+",
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index 07122c2707ac..9183b45b036a 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -2,6 +2,11 @@
 
 [case testStrBasics]
 from typing import Tuple
+class A:
+    def __str__(self) -> str:
+        return "A-str"
+    def __repr__(self) -> str:
+        return "A-repr"
 def f() -> str:
     return 'some string'
 def g() -> str:
@@ -10,6 +15,14 @@ def tostr(x: int) -> str:
     return str(x)
 def booltostr(x: bool) -> str:
     return str(x)
+def clstostr(x: A) -> str:
+    return str(x)
+def torepr(x: int) -> str:
+    return repr(x)
+def booltorepr(x: bool) -> str:
+    return repr(x)
+def clstorepr(x: A) -> str:
+    return repr(x)
 def concat(x: str, y: str) -> str:
     return x + y
 def eq(x: str) -> int:
@@ -29,8 +42,9 @@ def remove_prefix_suffix(x: str, y: str) -> Tuple[str, str]:
 
 [file driver.py]
 from native import (
-    f, g, tostr, booltostr, concat, eq, match, match_tuple,
-    match_tuple_literal_args, remove_prefix_suffix
+    f, g, A, tostr, booltostr, clstostr, concat, eq, match, match_tuple,
+    match_tuple_literal_args, remove_prefix_suffix,
+    torepr, booltorepr, clstorepr
 )
 import sys
 from testutil import assertRaises
@@ -42,12 +56,20 @@ assert tostr(57) == '57'
 assert concat('foo', 'bar') == 'foobar'
 assert booltostr(True) == 'True'
 assert booltostr(False) == 'False'
+assert clstostr(A()) == "A-str"
 assert eq('foo') == 0
 assert eq('zar') == 1
 assert eq('bar') == 2
 
+assert torepr(57) == '57'
+assert booltorepr(True) == 'True'
+assert booltorepr(False) == 'False'
+assert clstorepr(A()) == "A-repr"
+
 assert int(tostr(0)) == 0
 assert int(tostr(20)) == 20
+assert int(torepr(0)) == 0
+assert int(torepr(20)) == 20
 assert match('', '') == (True, True)
 assert match('abc', '') == (True, True)
 assert match('abc', 'a') == (True, False)

From 98e3faf880a6e8133217ddbd95d26613d20cec6e Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Fri, 28 Mar 2025 14:58:08 +0000
Subject: [PATCH 1214/1617] [mypyc] Support for annotating classes to be native
 or not (native_class=True/False) (#18802)

---
 mypyc/irbuild/prepare.py             |  2 +-
 mypyc/irbuild/util.py                | 70 ++++++++++++++++++++++++----
 mypyc/test-data/fixtures/ir.py       |  2 +
 mypyc/test-data/irbuild-classes.test | 25 ++++++++++
 mypyc/test-data/run-classes.test     | 53 +++++++++++++++++++++
 5 files changed, 143 insertions(+), 9 deletions(-)

diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index b6cd632e475f..e014d97fedd9 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -88,7 +88,7 @@ def build_type_map(
             is_abstract=cdef.info.is_abstract,
             is_final_class=cdef.info.is_final,
         )
-        class_ir.is_ext_class = is_extension_class(cdef)
+        class_ir.is_ext_class = is_extension_class(module.path, cdef, errors)
         if class_ir.is_ext_class:
             class_ir.deletable = cdef.info.deletable_attributes.copy()
         # If global optimizations are disabled, turn of tracking of class children
diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py
index 43ee547f8b4f..939c543c85a2 100644
--- a/mypyc/irbuild/util.py
+++ b/mypyc/irbuild/util.py
@@ -29,6 +29,7 @@
 )
 from mypy.semanal import refers_to_fullname
 from mypy.types import FINAL_DECORATOR_NAMES
+from mypyc.errors import Errors
 
 DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"}
 
@@ -125,15 +126,68 @@ def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]:
     return attrs
 
 
-def is_extension_class(cdef: ClassDef) -> bool:
-    if any(
-        not is_trait_decorator(d)
-        and not is_dataclass_decorator(d)
-        and not get_mypyc_attr_call(d)
-        and not is_final_decorator(d)
-        for d in cdef.decorators
-    ):
+def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool:
+    # Check for @mypyc_attr(native_class=True/False) decorator.
+    explicit_native_class = get_explicit_native_class(path, cdef, errors)
+
+    # Classes with native_class=False are explicitly marked as non extension.
+    if explicit_native_class is False:
         return False
+
+    implicit_extension_class = is_implicit_extension_class(cdef)
+
+    # Classes with native_class=True should be extension classes, but they might
+    # not be able to be due to other reasons. Print an error in that case.
+    if explicit_native_class is True and not implicit_extension_class:
+        errors.error(
+            "Class is marked as native_class=True but it can't be a native class", path, cdef.line
+        )
+
+    return implicit_extension_class
+
+
+def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool | None:
+    """Return value of @mypyc_attr(native_class=True/False) decorator.
+
+    Look for a @mypyc_attr decorator with native_class=True/False and return
+    the value assigned or None if it doesn't exist. Other values are an error.
+    """
+
+    for d in cdef.decorators:
+        mypyc_attr_call = get_mypyc_attr_call(d)
+        if not mypyc_attr_call:
+            continue
+
+        for i, name in enumerate(mypyc_attr_call.arg_names):
+            if name != "native_class":
+                continue
+
+            arg = mypyc_attr_call.args[i]
+            if not isinstance(arg, NameExpr):
+                errors.error("native_class must be used with True or False only", path, cdef.line)
+                return None
+
+            if arg.name == "False":
+                return False
+            elif arg.name == "True":
+                return True
+            else:
+                errors.error("native_class must be used with True or False only", path, cdef.line)
+                return None
+    return None
+
+
+def is_implicit_extension_class(cdef: ClassDef) -> bool:
+    for d in cdef.decorators:
+        # Classes that have any decorator other than supported decorators, are not extension classes
+        if (
+            not is_trait_decorator(d)
+            and not is_dataclass_decorator(d)
+            and not get_mypyc_attr_call(d)
+            and not is_final_decorator(d)
+        ):
+            return False
+
     if cdef.info.typeddict_type:
         return False
     if cdef.info.is_named_tuple:
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 16a3bfdbb9c8..2058e4f7be14 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -352,8 +352,10 @@ def next(i: Iterator[_T]) -> _T: pass
 def next(i: Iterator[_T], default: _T) -> _T: pass
 def hash(o: object) -> int: ...
 def globals() -> Dict[str, Any]: ...
+def hasattr(obj: object, name: str) -> bool: ...
 def getattr(obj: object, name: str, default: Any = None) -> Any: ...
 def setattr(obj: object, name: str, value: Any) -> None: ...
+def delattr(obj: object, name: str) -> None: ...
 def enumerate(x: Iterable[_T]) -> Iterator[Tuple[int, _T]]: ...
 @overload
 def zip(x: Iterable[_T], y: Iterable[_S]) -> Iterator[Tuple[_T, _S]]: ...
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index ed7c167d8621..972146bcb0b4 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1345,3 +1345,28 @@ class SomeEnum(Enum):
 
 ALIAS = Literal[SomeEnum.AVALUE]
 ALIAS2 = Union[Literal[SomeEnum.AVALUE], None]
+
+[case testMypycAttrNativeClassErrors]
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class AnnontatedNonExtensionClass:
+    pass
+
+@mypyc_attr(native_class=False)
+class DerivedExplicitNonNativeClass(AnnontatedNonExtensionClass):
+    pass
+
+
+def decorator(cls):
+    return cls
+
+@mypyc_attr(native_class=True)
+@decorator
+class NonNativeClassContradiction():  # E: Class is marked as native_class=True but it can't be a native class
+    pass
+
+
+@mypyc_attr(native_class="yes")
+class BadUse():  # E: native_class must be used with True or False only
+    pass
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 601d6d7a65a0..edf9e6bf1906 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2829,3 +2829,56 @@ Traceback (most recent call last):
   File "native.py", line 5, in __del__
     raise Exception("e2")
 Exception: e2
+
+[case testMypycAttrNativeClass]
+from mypy_extensions import mypyc_attr
+from testutil import assertRaises
+
+@mypyc_attr(native_class=False)
+class AnnontatedNonExtensionClass:
+    pass
+
+class DerivedClass(AnnontatedNonExtensionClass):
+    pass
+
+class ImplicitExtensionClass():
+    pass
+
+@mypyc_attr(native_class=True)
+class AnnotatedExtensionClass():
+    pass
+
+def test_function():
+    setattr(AnnontatedNonExtensionClass, 'attr_class', 5)
+    assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == True)
+    assert(getattr(AnnontatedNonExtensionClass, 'attr_class') == 5)
+    delattr(AnnontatedNonExtensionClass, 'attr_class')
+    assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == False)
+
+    inst = AnnontatedNonExtensionClass()
+    setattr(inst, 'attr_instance', 6)
+    assert(hasattr(inst, 'attr_instance') == True)
+    assert(getattr(inst, 'attr_instance') == 6)
+    delattr(inst, 'attr_instance')
+    assert(hasattr(inst, 'attr_instance') == False)
+
+    setattr(DerivedClass, 'attr_class', 5)
+    assert(hasattr(DerivedClass, 'attr_class') == True)
+    assert(getattr(DerivedClass, 'attr_class') == 5)
+    delattr(DerivedClass, 'attr_class')
+    assert(hasattr(DerivedClass, 'attr_class') == False)
+
+    derived_inst = DerivedClass()
+    setattr(derived_inst, 'attr_instance', 6)
+    assert(hasattr(derived_inst, 'attr_instance') == True)
+    assert(getattr(derived_inst, 'attr_instance') == 6)
+    delattr(derived_inst, 'attr_instance')
+    assert(hasattr(derived_inst, 'attr_instance') == False)
+
+    ext_inst = ImplicitExtensionClass()
+    with assertRaises(AttributeError):
+        setattr(ext_inst, 'attr_instance', 6)
+
+    explicit_ext_inst = AnnotatedExtensionClass()
+    with assertRaises(AttributeError):
+        setattr(explicit_ext_inst, 'attr_instance', 6)

From 8a87503ba1d88048d831ee71f97595d5188017da Mon Sep 17 00:00:00 2001
From: Aaron Gokaslan 
Date: Fri, 28 Mar 2025 07:58:57 -0700
Subject: [PATCH 1215/1617] Enable ruff FURB None rules (#18687)

---
 pyproject.toml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/pyproject.toml b/pyproject.toml
index ce1326bc5818..6d0584f0003c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -146,6 +146,8 @@ select = [
   "C4",      # flake8-comprehensions
   "SIM101",  # merge duplicate isinstance calls
   "SIM201", "SIM202", "SIM222", "SIM223",  # flake8-simplify
+  "FURB168", # Prefer is operator over isinstance for None checks
+  "FURB169", # Do not use is comparison with type(None). Use None
   "FURB188", # use str.remove(pre|suf)fix
   "ISC001",  # implicitly concatenated string
   "RET501", "RET502",  # better return None handling

From 836019a625072665904447e7612ca7c3ada73d62 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 28 Mar 2025 16:53:18 +0100
Subject: [PATCH 1216/1617] Update project metadata for PEP 639 (#18821)

Setuptools `v77` was released today which adds full support for PEP 639.
https://setuptools.pypa.io/en/latest/history.html#v77-0-0
https://peps.python.org/pep-0639/

The relevant project metadata changes
```diff
 ...
-License: MIT
+License-Expression: MIT
 ...
-Classifier: License :: OSI Approved :: MIT License
 ...
 License-File: LICENSE
+License-File: mypy/typeshed/LICENSE
 ...
```
---
 .github/workflows/test.yml | 4 ++--
 pyproject.toml             | 6 +++---
 test-requirements.in       | 2 +-
 test-requirements.txt      | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c42550431bb1..279f7f48d45d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -167,7 +167,7 @@ jobs:
         echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))'
         echo os.cpu_count; python -c 'import os; print(os.cpu_count())'
         echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))'
-        pip install setuptools==75.1.0 tox==4.21.2
+        pip install tox==4.21.2
 
     - name: Compiled with mypyc
       if: ${{ matrix.test_mypyc }}
@@ -230,7 +230,7 @@ jobs:
           default: 3.11.1
           command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');"
       - name: Install tox
-        run: pip install setuptools==75.1.0 tox==4.21.2
+        run: pip install tox==4.21.2
       - name: Setup tox environment
         run: tox run -e py --notest
       - name: Test
diff --git a/pyproject.toml b/pyproject.toml
index 6d0584f0003c..d264ac3749a9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,7 +3,7 @@ requires = [
     # NOTE: this needs to be kept in sync with mypy-requirements.txt
     # and build-requirements.txt, because those are both needed for
     # self-typechecking :/
-    "setuptools >= 75.1.0",
+    "setuptools >= 77.0.3",
     # the following is from mypy-requirements.txt/setup.py
     "typing_extensions>=4.6.0",
     "mypy_extensions>=1.0.0",
@@ -30,12 +30,12 @@ features such as type inference, gradual typing, generics and union
 types.
 """, content-type = "text/x-rst"}
 authors = [{name = "Jukka Lehtosalo", email = "jukka.lehtosalo@iki.fi"}]
-license = {text = "MIT"}
+license = "MIT"
+license-files = ["LICENSE", "mypy/typeshed/LICENSE"]
 classifiers = [
   "Development Status :: 5 - Production/Stable",
   "Environment :: Console",
   "Intended Audience :: Developers",
-  "License :: OSI Approved :: MIT License",
   "Programming Language :: Python :: 3",
   "Programming Language :: Python :: 3.9",
   "Programming Language :: Python :: 3.10",
diff --git a/test-requirements.in b/test-requirements.in
index 666dd9fc082c..6e4e792bb6b1 100644
--- a/test-requirements.in
+++ b/test-requirements.in
@@ -10,6 +10,6 @@ psutil>=4.0
 pytest>=8.1.0
 pytest-xdist>=1.34.0
 pytest-cov>=2.10.0
-setuptools>=75.1.0
+setuptools>=77.0.3
 tomli>=1.1.0  # needed even on py311+ so the self check passes with --python-version 3.9
 pre_commit>=3.5.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 51281f0e4c11..eb34795fa842 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -63,5 +63,5 @@ virtualenv==20.29.1
     # via pre-commit
 
 # The following packages are considered to be unsafe in a requirements file:
-setuptools==75.8.0
+setuptools==77.0.3
     # via -r test-requirements.in

From 4b1a2558e8afe9429f7baaf7dc593c978f63eb09 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 28 Mar 2025 16:01:44 +0000
Subject: [PATCH 1217/1617] [mypyc] Various improvements to annotated html
 generation (#18848)

Detect additional performance issues, such as calling decorated
functions and constructing instances of non-native classes.

Silence some non-actionable annotations where the performance impact is
minimal, and/or the user likely doesn't learn anything useful from the
annotation.
---
 mypyc/annotate.py                   | 176 +++++++++++++++++++----
 mypyc/build.py                      |   4 +-
 mypyc/codegen/emitmodule.py         |   6 +-
 mypyc/test-data/annotate-basic.test | 212 +++++++++++++++++++++++++++-
 mypyc/test/test_annotate.py         |   8 +-
 mypyc/test/test_run.py              |   2 +-
 mypyc/test/testutil.py              |  12 +-
 7 files changed, 370 insertions(+), 50 deletions(-)

diff --git a/mypyc/annotate.py b/mypyc/annotate.py
index 3368a68832bd..6736ca63c9e8 100644
--- a/mypyc/annotate.py
+++ b/mypyc/annotate.py
@@ -13,19 +13,31 @@
 
 from mypy.build import BuildResult
 from mypy.nodes import (
+    AssignmentStmt,
     CallExpr,
+    ClassDef,
+    Decorator,
+    DictionaryComprehension,
     Expression,
     ForStmt,
     FuncDef,
+    GeneratorExpr,
+    IndexExpr,
     LambdaExpr,
     MemberExpr,
     MypyFile,
+    NamedTupleExpr,
     NameExpr,
+    NewTypeExpr,
     Node,
+    OpExpr,
     RefExpr,
     TupleExpr,
+    TypedDictExpr,
     TypeInfo,
+    TypeVarExpr,
     Var,
+    WithStmt,
 )
 from mypy.traverser import TraverserVisitor
 from mypy.types import AnyType, Instance, ProperType, Type, TypeOfAny, get_proper_type
@@ -33,6 +45,7 @@
 from mypyc.ir.func_ir import FuncIR
 from mypyc.ir.module_ir import ModuleIR
 from mypyc.ir.ops import CallC, LoadLiteral, LoadStatic, Value
+from mypyc.irbuild.mapper import Mapper
 
 
 class Annotation:
@@ -71,18 +84,21 @@ def __init__(self, message: str, priority: int = 1) -> None:
 
 stdlib_hints: Final = {
     "functools.partial": Annotation(
-        '"functools.partial" is inefficient in compiled code.', priority=2
+        '"functools.partial" is inefficient in compiled code.', priority=3
     ),
     "itertools.chain": Annotation(
         '"itertools.chain" is inefficient in compiled code (hint: replace with for loops).',
-        priority=2,
+        priority=3,
     ),
     "itertools.groupby": Annotation(
-        '"itertools.groupby" is inefficient in compiled code.', priority=2
+        '"itertools.groupby" is inefficient in compiled code.', priority=3
     ),
     "itertools.islice": Annotation(
         '"itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range).',
-        priority=2,
+        priority=3,
+    ),
+    "copy.deepcopy": Annotation(
+        '"copy.deepcopy" tends to be slow. Make a shallow copy if possible.', priority=2
     ),
 }
 
@@ -127,14 +143,16 @@ def __init__(self, path: str, annotations: dict[int, list[Annotation]]) -> None:
 
 
 def generate_annotated_html(
-    html_fnam: str, result: BuildResult, modules: dict[str, ModuleIR]
+    html_fnam: str, result: BuildResult, modules: dict[str, ModuleIR], mapper: Mapper
 ) -> None:
     annotations = []
     for mod, mod_ir in modules.items():
         path = result.graph[mod].path
         tree = result.graph[mod].tree
         assert tree is not None
-        annotations.append(generate_annotations(path or "", tree, mod_ir, result.types))
+        annotations.append(
+            generate_annotations(path or "", tree, mod_ir, result.types, mapper)
+        )
     html = generate_html_report(annotations)
     with open(html_fnam, "w") as f:
         f.write(html)
@@ -145,15 +163,18 @@ def generate_annotated_html(
 
 
 def generate_annotations(
-    path: str, tree: MypyFile, ir: ModuleIR, type_map: dict[Expression, Type]
+    path: str, tree: MypyFile, ir: ModuleIR, type_map: dict[Expression, Type], mapper: Mapper
 ) -> AnnotatedSource:
     anns = {}
     for func_ir in ir.functions:
         anns.update(function_annotations(func_ir, tree))
-    visitor = ASTAnnotateVisitor(type_map)
+    visitor = ASTAnnotateVisitor(type_map, mapper)
     for defn in tree.defs:
         defn.accept(visitor)
     anns.update(visitor.anns)
+    for line in visitor.ignored_lines:
+        if line in anns:
+            del anns[line]
     return AnnotatedSource(path, anns)
 
 
@@ -168,18 +189,28 @@ def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Anno
                 ann: str | Annotation | None = None
                 if name == "CPyObject_GetAttr":
                     attr_name = get_str_literal(op.args[1])
-                    if attr_name == "__prepare__":
-                        # These attributes are internal to mypyc/CPython, and the user has
-                        # little control over them.
+                    if attr_name in ("__prepare__", "GeneratorExit", "StopIteration"):
+                        # These attributes are internal to mypyc/CPython, and/or accessed
+                        # implicitly in generated code. The user has little control over
+                        # them.
                         ann = None
                     elif attr_name:
                         ann = f'Get non-native attribute "{attr_name}".'
                     else:
                         ann = "Dynamic attribute lookup."
+                elif name == "PyObject_SetAttr":
+                    attr_name = get_str_literal(op.args[1])
+                    if attr_name == "__mypyc_attrs__":
+                        # This is set implicitly and can't be avoided.
+                        ann = None
+                    elif attr_name:
+                        ann = f'Set non-native attribute "{attr_name}".'
+                    else:
+                        ann = "Dynamic attribute set."
                 elif name == "PyObject_VectorcallMethod":
                     method_name = get_str_literal(op.args[0])
                     if method_name:
-                        ann = f'Call non-native method "{method_name}".'
+                        ann = f'Call non-native method "{method_name}" (it may be defined in a non-native class, or decorated).'
                     else:
                         ann = "Dynamic method call."
                 elif name in op_hints:
@@ -218,10 +249,12 @@ def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Anno
 class ASTAnnotateVisitor(TraverserVisitor):
     """Generate annotations from mypy AST and inferred types."""
 
-    def __init__(self, type_map: dict[Expression, Type]) -> None:
+    def __init__(self, type_map: dict[Expression, Type], mapper: Mapper) -> None:
         self.anns: dict[int, list[Annotation]] = {}
+        self.ignored_lines: set[int] = set()
         self.func_depth = 0
         self.type_map = type_map
+        self.mapper = mapper
 
     def visit_func_def(self, o: FuncDef, /) -> None:
         if self.func_depth > 0:
@@ -235,21 +268,84 @@ def visit_func_def(self, o: FuncDef, /) -> None:
         self.func_depth -= 1
 
     def visit_for_stmt(self, o: ForStmt, /) -> None:
-        typ = self.get_type(o.expr)
-        if isinstance(typ, AnyType):
-            self.annotate(o.expr, 'For loop uses generic operations (iterable has type "Any").')
-        elif isinstance(typ, Instance) and typ.type.fullname in (
-            "typing.Iterable",
-            "typing.Iterator",
-            "typing.Sequence",
-            "typing.MutableSequence",
-        ):
-            self.annotate(
-                o.expr,
-                f'For loop uses generic operations (iterable has the abstract type "{typ.type.fullname}").',
-            )
+        self.check_iteration([o.expr], "For loop")
         super().visit_for_stmt(o)
 
+    def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None:
+        self.check_iteration(o.sequences, "Comprehension")
+        super().visit_dictionary_comprehension(o)
+
+    def visit_generator_expr(self, o: GeneratorExpr, /) -> None:
+        self.check_iteration(o.sequences, "Comprehension or generator")
+        super().visit_generator_expr(o)
+
+    def check_iteration(self, expressions: list[Expression], kind: str) -> None:
+        for expr in expressions:
+            typ = self.get_type(expr)
+            if isinstance(typ, AnyType):
+                self.annotate(expr, f'{kind} uses generic operations (iterable has type "Any").')
+            elif isinstance(typ, Instance) and typ.type.fullname in (
+                "typing.Iterable",
+                "typing.Iterator",
+                "typing.Sequence",
+                "typing.MutableSequence",
+            ):
+                self.annotate(
+                    expr,
+                    f'{kind} uses generic operations (iterable has the abstract type "{typ.type.fullname}").',
+                )
+
+    def visit_class_def(self, o: ClassDef, /) -> None:
+        super().visit_class_def(o)
+        if self.func_depth == 0:
+            # Don't complain about base classes at top level
+            for base in o.base_type_exprs:
+                self.ignored_lines.add(base.line)
+
+            for s in o.defs.body:
+                if isinstance(s, AssignmentStmt):
+                    # Don't complain about attribute initializers
+                    self.ignored_lines.add(s.line)
+                elif isinstance(s, Decorator):
+                    # Don't complain about decorator definitions that generate some
+                    # dynamic operations. This is a bit heavy-handed.
+                    self.ignored_lines.add(s.func.line)
+
+    def visit_with_stmt(self, o: WithStmt, /) -> None:
+        for expr in o.expr:
+            if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr):
+                node = expr.callee.node
+                if isinstance(node, Decorator):
+                    if any(
+                        isinstance(d, RefExpr)
+                        and d.node
+                        and d.node.fullname == "contextlib.contextmanager"
+                        for d in node.decorators
+                    ):
+                        self.annotate(
+                            expr,
+                            f'"{node.name}" uses @contextmanager, which is slow '
+                            + "in compiled code. Use a native class with "
+                            + '"__enter__" and "__exit__" methods instead.',
+                            priority=3,
+                        )
+        super().visit_with_stmt(o)
+
+    def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None:
+        special_form = False
+        if self.func_depth == 0:
+            analyzed: Expression | None = o.rvalue
+            if isinstance(o.rvalue, (CallExpr, IndexExpr, OpExpr)):
+                analyzed = o.rvalue.analyzed
+            if o.is_alias_def or isinstance(
+                analyzed, (TypeVarExpr, NamedTupleExpr, TypedDictExpr, NewTypeExpr)
+            ):
+                special_form = True
+            if special_form:
+                # TODO: Ignore all lines if multi-line
+                self.ignored_lines.add(o.line)
+        super().visit_assignment_stmt(o)
+
     def visit_name_expr(self, o: NameExpr, /) -> None:
         if ann := stdlib_hints.get(o.fullname):
             self.annotate(o, ann)
@@ -268,6 +364,30 @@ def visit_call_expr(self, o: CallExpr, /) -> None:
         ):
             arg = o.args[1]
             self.check_isinstance_arg(arg)
+        elif isinstance(o.callee, RefExpr) and isinstance(o.callee.node, TypeInfo):
+            info = o.callee.node
+            class_ir = self.mapper.type_to_ir.get(info)
+            if (class_ir and not class_ir.is_ext_class) or (
+                class_ir is None and not info.fullname.startswith("builtins.")
+            ):
+                self.annotate(
+                    o, f'Creating an instance of non-native class "{info.name}" ' + "is slow.", 2
+                )
+            elif class_ir and class_ir.is_augmented:
+                self.annotate(
+                    o,
+                    f'Class "{info.name}" is only partially native, and '
+                    + "constructing an instance is slow.",
+                    2,
+                )
+        elif isinstance(o.callee, RefExpr) and isinstance(o.callee.node, Decorator):
+            decorator = o.callee.node
+            if self.mapper.is_native_ref_expr(o.callee):
+                self.annotate(
+                    o,
+                    f'Calling a decorated function ("{decorator.name}") is inefficient, even if it\'s native.',
+                    2,
+                )
 
     def check_isinstance_arg(self, arg: Expression) -> None:
         if isinstance(arg, RefExpr):
@@ -287,9 +407,9 @@ def visit_lambda_expr(self, o: LambdaExpr, /) -> None:
         )
         super().visit_lambda_expr(o)
 
-    def annotate(self, o: Node, ann: str | Annotation) -> None:
+    def annotate(self, o: Node, ann: str | Annotation, priority: int = 1) -> None:
         if isinstance(ann, str):
-            ann = Annotation(ann)
+            ann = Annotation(ann, priority=priority)
         self.anns.setdefault(o.line, []).append(ann)
 
     def get_type(self, e: Expression) -> ProperType:
diff --git a/mypyc/build.py b/mypyc/build.py
index cb05cda991d9..1a74d4692d17 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -242,7 +242,7 @@ def generate_c(
         print(f"Parsed and typechecked in {t1 - t0:.3f}s")
 
     errors = Errors(options)
-    modules, ctext = emitmodule.compile_modules_to_c(
+    modules, ctext, mapper = emitmodule.compile_modules_to_c(
         result, compiler_options=compiler_options, errors=errors, groups=groups
     )
     t2 = time.time()
@@ -255,7 +255,7 @@ def generate_c(
         print(f"Compiled to C in {t2 - t1:.3f}s")
 
     if options.mypyc_annotation_file:
-        generate_annotated_html(options.mypyc_annotation_file, result, modules)
+        generate_annotated_html(options.mypyc_annotation_file, result, modules, mapper)
 
     return ctext, "\n".join(format_modules(modules))
 
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 1ec3064eb5b9..713fa5c51fa1 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -397,7 +397,7 @@ def load_scc_from_cache(
 
 def compile_modules_to_c(
     result: BuildResult, compiler_options: CompilerOptions, errors: Errors, groups: Groups
-) -> tuple[ModuleIRs, list[FileContents]]:
+) -> tuple[ModuleIRs, list[FileContents], Mapper]:
     """Compile Python module(s) to the source of Python C extension modules.
 
     This generates the source code for the "shared library" module
@@ -427,12 +427,12 @@ def compile_modules_to_c(
 
     modules = compile_modules_to_ir(result, mapper, compiler_options, errors)
     if errors.num_errors > 0:
-        return {}, []
+        return {}, [], Mapper({})
 
     ctext = compile_ir_to_c(groups, modules, result, mapper, compiler_options)
     write_cache(modules, result, group_map, ctext)
 
-    return modules, [ctext[name] for _, name in groups]
+    return modules, [ctext[name] for _, name in groups], mapper
 
 
 def generate_function_declaration(fn: FuncIR, emitter: Emitter) -> None:
diff --git a/mypyc/test-data/annotate-basic.test b/mypyc/test-data/annotate-basic.test
index 23e9ae8814ca..c9e1c4b64a32 100644
--- a/mypyc/test-data/annotate-basic.test
+++ b/mypyc/test-data/annotate-basic.test
@@ -7,12 +7,23 @@ def f1(x):
 def f2(x: Any) -> object:
     return x.foo  # A: Get non-native attribute "foo".
 
+def f3(x):
+    x.bar = 1  # A: Set non-native attribute "bar".
+
 class C:
     foo: int
 
-def f3(x: C) -> int:
+    def method(self) -> int:
+        return self.foo
+
+def good1(x: C) -> int:
     return x.foo
 
+[case testAnnotateMethod]
+class C:
+    def method(self, x):
+        return x + "y"  # A: Generic "+" operation.
+
 [case testAnnotateGenericBinaryOperations]
 def generic_add(x):
     return x + 1  # A: Generic "+" operation.
@@ -98,11 +109,11 @@ def f(x):
 from typing import Any
 
 def f1(x):
-    return x.foo()  # A: Call non-native method "foo".
+    return x.foo()  # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated).
 
 def f2(x: Any) -> None:
-    x.foo(1)  # A: Call non-native method "foo".
-    x.foo(a=1)  # A: Call non-native method "foo".
+    x.foo(1)  # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated).
+    x.foo(a=1)  # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated).
     t = (1, 'x')
     x.foo(*t)  # A: Get non-native attribute "foo". Generic call operation.
     d = {"a": 1}
@@ -175,7 +186,7 @@ def startswith(x: str) -> bool:
     return x.startswith('foo')
 
 def islower(x: str) -> bool:
-    return x.islower()  # A: Call non-native method "islower".
+    return x.islower()  # A: Call non-native method "islower" (it may be defined in a non-native class, or decorated).
 
 [case testAnnotateSpecificStdlibFeatures]
 import functools
@@ -244,6 +255,24 @@ def good2(a: List[str]) -> None:
     for x in a:
         pass
 
+[case testAnnotateGenericComprehensionOrGenerator]
+from typing import List, Iterable
+
+def f1(a):
+    return [x for x in a]  # A: Comprehension or generator uses generic operations (iterable has type "Any").
+
+def f2(a: Iterable[int]):
+    return {x for x in a}  # A: Comprehension or generator uses generic operations (iterable has the abstract type "typing.Iterable").
+
+def f3(a):
+    return {x: 1 for x in a}  # A: Comprehension uses generic operations (iterable has type "Any").
+
+def f4(a):
+    return (x for x in a)  # A: Comprehension or generator uses generic operations (iterable has type "Any").
+
+def good1(a: List[int]) -> List[int]:
+    return [x + 1 for x in a]
+
 [case testAnnotateIsinstance]
 from typing import Protocol, runtime_checkable, Union
 
@@ -273,3 +302,176 @@ def good2(x: Union[int, str]) -> int:
     else:
         return int(x + "1")
 [typing fixtures/typing-full.pyi]
+
+[case testAnnotateDeepcopy]
+from typing import Any
+import copy
+
+def f(x: Any) -> Any:
+    return copy.deepcopy(x)  # A: "copy.deepcopy" tends to be slow. Make a shallow copy if possible.
+
+[case testAnnotateContextManager]
+from typing import Iterator
+from contextlib import contextmanager
+
+@contextmanager
+def slow_ctx_manager() -> Iterator[None]:
+    yield
+
+class FastCtxManager:
+    def __enter__(self) -> None: pass
+    def __exit__(self, a, b, c) -> None: pass
+
+def f1(x) -> None:
+    with slow_ctx_manager():  # A: "slow_ctx_manager" uses @contextmanager, which is slow in compiled code. Use a native class with "__enter__" and "__exit__" methods instead.
+        x.foo  # A: Get non-native attribute "foo".
+
+def f2(x) -> None:
+    with FastCtxManager():
+        x.foo  # A: Get non-native attribute "foo".
+
+[case testAnnotateAvoidNoiseAtTopLevel]
+from typing import Final
+
+class C(object):
+    x = "s"
+    y: Final = 1
+
+x = "s"
+y: Final = 1
+
+def f1() -> None:
+    x = object  # A: Get non-native attribute "object".
+
+[case testAnnotateCreateNonNativeInstance]
+from typing import NamedTuple
+from dataclasses import dataclass
+
+from nonnative import C
+
+def f1() -> None:
+    c = C()  # A: Creating an instance of non-native class "C" is slow.
+    c.foo()  # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated).
+
+class NT(NamedTuple):
+    x: int
+    y: str
+
+def f2() -> int:
+    o = NT(1, "x")  # A: Creating an instance of non-native class "NT" is slow.
+    return o.x
+
+def f3() -> int:
+    o = NT(x=1, y="x")  # A: Creating an instance of non-native class "NT" is slow.
+    a, b = o
+    return a
+
+@dataclass
+class D:
+    x: int
+
+def f4() -> int:
+    o = D(1)  # A: Class "D" is only partially native, and constructing an instance is slow.
+    return o.x
+
+class Nat:
+    x: int
+
+class Deriv(Nat):
+    def __init__(self, y: int) -> None:
+        self.y = y
+
+def good1() -> int:
+    n = Nat()
+    d = Deriv(y=1)
+    return n.x + d.x + d.y
+
+[file nonnative.py]
+class C:
+    def foo(self) -> None: pass
+
+[case testAnnotateGetAttrAndSetAttrBuiltins]
+def f1(x, s: str):
+    return getattr("x", s)  # A: Dynamic attribute lookup.
+
+def f2(x, s: str):
+    setattr(x, s, None)  # A: Dynamic attribute set.
+
+[case testAnnotateSpecialAssignments]
+from typing import TypeVar, NamedTuple, List, TypedDict, NewType
+
+# Even though these are slow, we don't complain about them since there is generally
+# no better way (and at module top level these are very unlikely to be bottlenecks)
+A = List[int]
+T = TypeVar("T", bound=List[int])
+NT = NamedTuple("NT", [("x", List[int])])
+TD = TypedDict("TD", {"x": List[int]})
+New = NewType("New", List[int])
+[typing fixtures/typing-full.pyi]
+
+[case testAnnotateCallDecoratedNativeFunctionOrMethod]
+from typing import TypeVar, Callable, Any
+
+F = TypeVar("F", bound=Callable[..., Any])
+
+def mydeco(f: F) -> F:
+    return f
+
+@mydeco
+def d(x: int) -> int:
+    return x
+
+def f1() -> int:
+    return d(1)  # A: Calling a decorated function ("d") is inefficient, even if it's native.
+
+class C:
+    @mydeco
+    def d(self) -> None:
+        pass
+
+
+def f2() -> None:
+    c = C()
+    c.d()  # A: Call non-native method "d" (it may be defined in a non-native class, or decorated).
+
+[case testAnnotateCallDifferentKindsOfMethods]
+from abc import ABC, abstractmethod
+
+class C:
+    @staticmethod
+    def s() -> None: ...
+
+    @classmethod
+    def c(cls) -> None: ...
+
+    @property
+    def p(self) -> int:
+        return 0
+
+    @property
+    def p2(self) -> int:
+        return 0
+
+    @p2.setter
+    def p2(self, x: int) -> None:
+        pass
+
+def f1() -> int:
+    c = C()
+    c.s()
+    c.c()
+    c.p2 = 1
+    return c.p + c.p2
+
+class A(ABC):
+    @abstractmethod
+    def m(self) -> int:
+        raise NotImplementedError  # A: Get non-native attribute "NotImplementedError".
+
+class D(A):
+    def m(self) -> int:
+        return 1
+
+def f2() -> int:
+    d = D()
+    return d.m()
diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py
index bb4941064bdb..4a9a2c1a1b93 100644
--- a/mypyc/test/test_annotate.py
+++ b/mypyc/test/test_annotate.py
@@ -44,13 +44,15 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
 
             ir = None
             try:
-                ir, tree, type_map = build_ir_for_single_file2(testcase.input, options)
+                ir, tree, type_map, mapper = build_ir_for_single_file2(testcase.input, options)
             except CompileError as e:
                 actual = e.messages
             else:
-                annotations = generate_annotations("native.py", tree, ir, type_map)
+                annotations = generate_annotations("native.py", tree, ir, type_map, mapper)
                 actual = []
-                for line_num, line_anns in annotations.annotations.items():
+                for line_num, line_anns in sorted(
+                    annotations.annotations.items(), key=lambda it: it[0]
+                ):
                     anns = get_max_prio(line_anns)
                     str_anns = [a.message for a in anns]
                     s = " ".join(str_anns)
diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py
index f4798660079f..e5b7e2421433 100644
--- a/mypyc/test/test_run.py
+++ b/mypyc/test/test_run.py
@@ -251,7 +251,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
                 alt_lib_path=".",
             )
             errors = Errors(options)
-            ir, cfiles = emitmodule.compile_modules_to_c(
+            ir, cfiles, _ = emitmodule.compile_modules_to_c(
                 result, compiler_options=compiler_options, errors=errors, groups=groups
             )
             if errors.num_errors:
diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py
index 1961c47e85ee..7b56b8aa0dec 100644
--- a/mypyc/test/testutil.py
+++ b/mypyc/test/testutil.py
@@ -100,7 +100,7 @@ def build_ir_for_single_file(
 
 def build_ir_for_single_file2(
     input_lines: list[str], compiler_options: CompilerOptions | None = None
-) -> tuple[ModuleIR, MypyFile, dict[Expression, Type]]:
+) -> tuple[ModuleIR, MypyFile, dict[Expression, Type], Mapper]:
     program_text = "\n".join(input_lines)
 
     # By default generate IR compatible with the earliest supported Python C API.
@@ -125,13 +125,9 @@ def build_ir_for_single_file2(
         raise CompileError(result.errors)
 
     errors = Errors(options)
+    mapper = Mapper({"__main__": None})
     modules = build_ir(
-        [result.files["__main__"]],
-        result.graph,
-        result.types,
-        Mapper({"__main__": None}),
-        compiler_options,
-        errors,
+        [result.files["__main__"]], result.graph, result.types, mapper, compiler_options, errors
     )
     if errors.num_errors:
         raise CompileError(errors.new_messages())
@@ -141,7 +137,7 @@ def build_ir_for_single_file2(
         assert_func_ir_valid(fn)
     tree = result.graph[module.fullname].tree
     assert tree is not None
-    return module, tree, result.types
+    return module, tree, result.types, mapper
 
 
 def update_testcase_output(testcase: DataDrivenTestCase, output: list[str]) -> None:

From 6badb4a09102a558e6cd0eb8bb8a947be21c1b25 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 29 Mar 2025 04:30:33 +0100
Subject: [PATCH 1218/1617] Admit that **kwargs mapping subtypes may have no
 direct type parameters (#18850)

Fixes #13675. I don't know why this check was ever needed (since
#11151), but it doesn't seem correct.
---
 mypy/argmap.py                   | 6 ++----
 test-data/unit/check-kwargs.test | 2 +-
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/mypy/argmap.py b/mypy/argmap.py
index 8db78b5413e8..a1c4ef72ea40 100644
--- a/mypy/argmap.py
+++ b/mypy/argmap.py
@@ -249,10 +249,8 @@ def expand_actual_type(
                     formal_name = (set(actual_type.items.keys()) - self.kwargs_used).pop()
                 self.kwargs_used.add(formal_name)
                 return actual_type.items[formal_name]
-            elif (
-                isinstance(actual_type, Instance)
-                and len(actual_type.args) > 1
-                and is_subtype(actual_type, self.context.mapping_type)
+            elif isinstance(actual_type, Instance) and is_subtype(
+                actual_type, self.context.mapping_type
             ):
                 # Only `Mapping` type can be unpacked with `**`.
                 # Other types will produce an error somewhere else.
diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test
index 3a8c7f5ba454..1418f9c3d184 100644
--- a/test-data/unit/check-kwargs.test
+++ b/test-data/unit/check-kwargs.test
@@ -345,7 +345,7 @@ from typing import Mapping
 class MappingSubclass(Mapping[str, str]): pass
 def f(**kwargs: 'A') -> None: pass
 d: MappingSubclass
-f(**d)
+f(**d)  # E: Argument 1 to "f" has incompatible type "**MappingSubclass"; expected "A"
 class A: pass
 [builtins fixtures/dict.pyi]
 

From 9e2198f8591ba95e3fd207d18d8918b783aa06c4 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 31 Mar 2025 01:01:10 +0100
Subject: [PATCH 1219/1617] Fix crash on type inference against non-normal
 callables (#18858)

Fixes https://github.com/python/mypy/issues/17755

Fix is trivial, so not really waiting for review. Btw I found few other
places where we do not normalize callables. TBH I already forgot when we
actually _need_ to normalize, but I don't want to just blanket add
normalization, as it may be a relatively expensive function. If we will
hit another similar crash, I will add more normalization accordingly
(similar to what I did with kwargs unpacking).
---
 mypy/constraints.py                     |  4 ++--
 mypy/types.py                           |  2 +-
 test-data/unit/check-typevar-tuple.test | 10 ++++++++++
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/mypy/constraints.py b/mypy/constraints.py
index e76f6cd639ad..079f6536ee20 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -1063,11 +1063,11 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]:
         # using e.g. callback protocols.
         # TODO: check that callables match? Ideally we should not infer constraints
         # callables that can never be subtypes of one another in given direction.
-        template = template.with_unpacked_kwargs()
+        template = template.with_unpacked_kwargs().with_normalized_var_args()
         extra_tvars = False
         if isinstance(self.actual, CallableType):
             res: list[Constraint] = []
-            cactual = self.actual.with_unpacked_kwargs()
+            cactual = self.actual.with_unpacked_kwargs().with_normalized_var_args()
             param_spec = template.param_spec()
 
             template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type
diff --git a/mypy/types.py b/mypy/types.py
index 9dd0ef8552b9..41a958ae93cc 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -2210,7 +2210,7 @@ def with_normalized_var_args(self) -> Self:
                     new_unpack = nested_unpacked.args[0]
                 else:
                     if not isinstance(nested_unpacked, TypeVarTupleType):
-                        # We found a non-nomralized tuple type, this means this method
+                        # We found a non-normalized tuple type, this means this method
                         # is called during semantic analysis (e.g. from get_proper_type())
                         # there is no point in normalizing callables at this stage.
                         return self
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 57a96291b04a..d364439f22e9 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -2618,3 +2618,13 @@ def deco(func: Callable[[*Ts, int], R]) -> Callable[[*Ts], R]:
 untyped: Any
 reveal_type(deco(untyped))  # N: Revealed type is "def (*Any) -> Any"
 [builtins fixtures/tuple.pyi]
+
+[case testNoCrashOnNonNormalUnpackInCallable]
+from typing import Callable, Unpack, TypeVar
+
+T = TypeVar("T")
+def fn(f: Callable[[*tuple[T]], int]) -> Callable[[*tuple[T]], int]: ...
+
+def test(*args: Unpack[tuple[T]]) -> int: ...
+reveal_type(fn(test))  # N: Revealed type is "def [T] (T`1) -> builtins.int"
+[builtins fixtures/tuple.pyi]

From c6af00ff0330d7a940abdafb46f4f7519f6b2d18 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 31 Mar 2025 02:29:50 +0200
Subject: [PATCH 1220/1617] Fix crash on multiple unpacks in a bare type
 application (#18857)

Fixes #18856. This should be done by `TypeAnalyzer.anal_array` but is
not - semanal only invokes its own wrapper around `anal_type`

---------

Co-authored-by: Ivan Levkivskyi 
---
 mypy/semanal.py                     |  2 ++
 mypy/typeanal.py                    |  2 +-
 test-data/unit/check-python312.test | 13 +++++++++++++
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index a8a698c046f3..6aa5977c110f 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -6071,6 +6071,8 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
                 return None
             types.append(analyzed)
 
+        if allow_unpack:
+            types = self.type_analyzer().check_unpacks_in_list(types)
         if has_param_spec and num_args == 1 and types:
             first_arg = get_proper_type(types[0])
             single_any = len(types) == 1 and isinstance(first_arg, AnyType)
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index 9208630937e7..7bf21709b863 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -2006,7 +2006,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]:
 
         if num_unpacks > 1:
             assert final_unpack is not None
-            self.fail("More than one Unpack in a type is not allowed", final_unpack)
+            self.fail("More than one Unpack in a type is not allowed", final_unpack.type)
         return new_items
 
     def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType:
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index ba4104a50048..2f3d5e08dab3 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2029,3 +2029,16 @@ def foo() -> None:
 class Z: ...  # E: Name "Z" already defined on line 2
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
+
+[case testPEP695MultipleUnpacksInBareApplicationNoCrash]
+# https://github.com/python/mypy/issues/18856
+class A[*Ts]: ...
+
+A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+a: A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ...  # E: More than one Unpack in a type is not allowed
+
+tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]

From 7846464a2c8ab1f6fa50dbd081f0addf31e7ab3b Mon Sep 17 00:00:00 2001
From: Tim Ruffing 
Date: Mon, 31 Mar 2025 15:25:39 +0200
Subject: [PATCH 1221/1617] nit: Fix wrong example code in comment (#18860)

"posix" is not a valid value for sys.platform

Found when researching about
https://github.com/python/typing/issues/1732.
I hope it's okay to send a trivial PR. If you don't want to bother with
this, feel free to close this.
---
 mypy/reachability.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/reachability.py b/mypy/reachability.py
index e69a857553d5..5d170b5071db 100644
--- a/mypy/reachability.py
+++ b/mypy/reachability.py
@@ -221,7 +221,7 @@ def consider_sys_platform(expr: Expression, platform: str) -> int:
     Return ALWAYS_TRUE, ALWAYS_FALSE, or TRUTH_VALUE_UNKNOWN.
     """
     # Cases supported:
-    # - sys.platform == 'posix'
+    # - sys.platform == 'linux'
     # - sys.platform != 'win32'
     # - sys.platform.startswith('win')
     if isinstance(expr, ComparisonExpr):

From 4629de44c1fdbfc0d77ffaa5458c64481ba6976c Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 31 Mar 2025 14:46:49 +0100
Subject: [PATCH 1222/1617] Use checkmember.py to check variable overrides
 (#18847)

Fixes https://github.com/python/mypy/issues/5803
Fixes https://github.com/python/mypy/issues/18695
Fixes https://github.com/python/mypy/issues/17513
Fixes https://github.com/python/mypy/issues/13194
Fixes https://github.com/python/mypy/issues/12126

This is the first PR towards https://github.com/python/mypy/issues/7724,
some notes:
* I add a new generic `suppress_errors` flag to `MemberContext` mostly
as a performance optimization, but it should also be handy in the
following PRs
* I noticed some inconsistencies with how we handle variable inference
(e.g. we don't infer type at all if rvalue type is not compatible with
superclass type). After all I decided to remove some of them, as it
makes implementation of this PR simpler.
* I added a bunch of TODOs, most of those will be addressed in following
PRs.
* A while ago we agreed that an explicit `Callable[...]` annotation in
class body means how the type looks on an _instance_, but the override
check used to handle this inconsistently (I add few `reveal_type()`s to
tests to illustrate this).
---
 mypy/checker.py                       | 273 +++++++++++++-------------
 mypy/checkmember.py                   |  85 ++++----
 test-data/unit/check-classes.test     | 110 ++++++++---
 test-data/unit/check-functions.test   |   4 +-
 test-data/unit/check-incremental.test |   4 -
 test-data/unit/check-namedtuple.test  |   2 +-
 test-data/unit/check-protocols.test   |   2 +-
 test-data/unit/check-selftype.test    |   4 +-
 test-data/unit/fine-grained.test      |   4 +-
 9 files changed, 274 insertions(+), 214 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 12afa4d3edf5..04f1f23362e2 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -12,13 +12,19 @@
 import mypy.checkexpr
 from mypy import errorcodes as codes, join, message_registry, nodes, operators
 from mypy.binder import ConditionalTypeBinder, Frame, get_declaration
-from mypy.checkmember import analyze_member_access
+from mypy.checkmember import (
+    MemberContext,
+    analyze_class_attribute_access,
+    analyze_instance_member_access,
+    analyze_member_access,
+    is_instance_var,
+)
 from mypy.checkpattern import PatternChecker
 from mypy.constraints import SUPERTYPE_OF
 from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values
 from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode
 from mypy.errors import Errors, ErrorWatcher, report_internal_error
-from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance
+from mypy.expandtype import expand_self_type, expand_type
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash
 from mypy.maptype import map_instance_to_supertype
 from mypy.meet import is_overlapping_erased_types, is_overlapping_types, meet_types
@@ -3256,16 +3262,6 @@ def check_assignment(
                     if active_class and dataclasses_plugin.is_processed_dataclass(active_class):
                         self.fail(message_registry.DATACLASS_POST_INIT_MUST_BE_A_FUNCTION, rvalue)
 
-            # Defer PartialType's super type checking.
-            if (
-                isinstance(lvalue, RefExpr)
-                and not (isinstance(lvalue_type, PartialType) and lvalue_type.type is None)
-                and not (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__")
-            ):
-                if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue):
-                    # We hit an error on this line; don't check for any others
-                    return
-
             if isinstance(lvalue, MemberExpr) and lvalue.name == "__match_args__":
                 self.fail(message_registry.CANNOT_MODIFY_MATCH_ARGS, lvalue)
 
@@ -3297,12 +3293,6 @@ def check_assignment(
                         # Try to infer a partial type. No need to check the return value, as
                         # an error will be reported elsewhere.
                         self.infer_partial_type(lvalue_type.var, lvalue, rvalue_type)
-                    # Handle None PartialType's super type checking here, after it's resolved.
-                    if isinstance(lvalue, RefExpr) and self.check_compatibility_all_supers(
-                        lvalue, lvalue_type, rvalue
-                    ):
-                        # We hit an error on this line; don't check for any others
-                        return
                 elif (
                     is_literal_none(rvalue)
                     and isinstance(lvalue, NameExpr)
@@ -3394,7 +3384,7 @@ def check_assignment(
                 self.check_indexed_assignment(index_lvalue, rvalue, lvalue)
 
             if inferred:
-                type_context = self.get_variable_type_context(inferred)
+                type_context = self.get_variable_type_context(inferred, rvalue)
                 rvalue_type = self.expr_checker.accept(rvalue, type_context=type_context)
                 if not (
                     inferred.is_final
@@ -3404,15 +3394,33 @@ def check_assignment(
                     rvalue_type = remove_instance_last_known_values(rvalue_type)
                 self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue)
             self.check_assignment_to_slots(lvalue)
+            if isinstance(lvalue, RefExpr) and not (
+                isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__"
+            ):
+                # We check override here at the end after storing the inferred type, since
+                # override check will try to access the current attribute via symbol tables
+                # (like a regular attribute access).
+                self.check_compatibility_all_supers(lvalue, rvalue)
 
     # (type, operator) tuples for augmented assignments supported with partial types
     partial_type_augmented_ops: Final = {("builtins.list", "+"), ("builtins.set", "|")}
 
-    def get_variable_type_context(self, inferred: Var) -> Type | None:
+    def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type | None:
         type_contexts = []
         if inferred.info:
             for base in inferred.info.mro[1:]:
-                base_type, base_node = self.lvalue_type_from_base(inferred, base)
+                if inferred.name not in base.names:
+                    continue
+                # For inference within class body, get supertype attribute as it would look on
+                # a class object for lambdas overriding methods, etc.
+                base_node = base.names[inferred.name].node
+                base_type, _ = self.lvalue_type_from_base(
+                    inferred,
+                    base,
+                    is_class=is_method(base_node)
+                    or isinstance(base_node, Var)
+                    and not is_instance_var(base_node),
+                )
                 if (
                     base_type
                     and not (isinstance(base_node, Var) and base_node.invalid_partial_type)
@@ -3479,15 +3487,21 @@ def try_infer_partial_generic_type_from_assignment(
                 var.type = fill_typevars_with_any(typ.type)
                 del partial_types[var]
 
-    def check_compatibility_all_supers(
-        self, lvalue: RefExpr, lvalue_type: Type | None, rvalue: Expression
-    ) -> bool:
+    def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) -> None:
         lvalue_node = lvalue.node
         # Check if we are a class variable with at least one base class
         if (
             isinstance(lvalue_node, Var)
-            and lvalue.kind in (MDEF, None)
-            and len(lvalue_node.info.bases) > 0  # None for Vars defined via self
+            # If we have explicit annotation, there is no point in checking the override
+            # for each assignment, so we check only for the first one.
+            # TODO: for some reason annotated attributes on self are stored as inferred vars.
+            and (
+                lvalue_node.line == lvalue.line
+                or lvalue_node.is_inferred
+                and not lvalue_node.explicit_self_type
+            )
+            and lvalue.kind in (MDEF, None)  # None for Vars defined via self
+            and len(lvalue_node.info.bases) > 0
         ):
             for base in lvalue_node.info.mro[1:]:
                 tnode = base.names.get(lvalue_node.name)
@@ -3503,6 +3517,21 @@ def check_compatibility_all_supers(
             direct_bases = lvalue_node.info.direct_base_classes()
             last_immediate_base = direct_bases[-1] if direct_bases else None
 
+            # The historical behavior for inferred vars was to compare rvalue type against
+            # the type declared in a superclass. To preserve this behavior, we temporarily
+            # store the rvalue type on the variable.
+            actual_lvalue_type = None
+            if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
+                rvalue_type = self.expr_checker.accept(rvalue, lvalue_node.type)
+                actual_lvalue_type = lvalue_node.type
+                lvalue_node.type = rvalue_type
+            lvalue_type, _ = self.lvalue_type_from_base(lvalue_node, lvalue_node.info)
+            if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
+                lvalue_node.type = actual_lvalue_type
+
+            if not lvalue_type:
+                return
+
             for base in lvalue_node.info.mro[1:]:
                 # The type of "__slots__" and some other attributes usually doesn't need to
                 # be compatible with a base class. We'll still check the type of "__slots__"
@@ -3523,7 +3552,6 @@ def check_compatibility_all_supers(
                 if base_type:
                     assert base_node is not None
                     if not self.check_compatibility_super(
-                        lvalue,
                         lvalue_type,
                         rvalue,
                         base,
@@ -3533,7 +3561,7 @@ def check_compatibility_all_supers(
                     ):
                         # Only show one error per variable; even if other
                         # base classes are also incompatible
-                        return True
+                        return
                     if lvalue_type and custom_setter:
                         base_type, _ = self.lvalue_type_from_base(
                             lvalue_node, base, setter_type=True
@@ -3545,96 +3573,49 @@ def check_compatibility_all_supers(
                             self.msg.incompatible_setter_override(
                                 lvalue, lvalue_type, base_type, base
                             )
-                            return True
+                            return
                     if base is last_immediate_base:
                         # At this point, the attribute was found to be compatible with all
                         # immediate parents.
                         break
-        return False
 
     def check_compatibility_super(
         self,
-        lvalue: RefExpr,
-        lvalue_type: Type | None,
+        compare_type: Type,
         rvalue: Expression,
         base: TypeInfo,
         base_type: Type,
         base_node: Node,
         always_allow_covariant: bool,
     ) -> bool:
-        lvalue_node = lvalue.node
-        assert isinstance(lvalue_node, Var)
-
-        # Do not check whether the rvalue is compatible if the
-        # lvalue had a type defined; this is handled by other
-        # parts, and all we have to worry about in that case is
-        # that lvalue is compatible with the base class.
-        compare_node = None
-        if lvalue_type:
-            compare_type = lvalue_type
-            compare_node = lvalue.node
-        else:
-            compare_type = self.expr_checker.accept(rvalue, base_type)
-            if isinstance(rvalue, NameExpr):
-                compare_node = rvalue.node
-                if isinstance(compare_node, Decorator):
-                    compare_node = compare_node.func
-
-        base_type = get_proper_type(base_type)
-        compare_type = get_proper_type(compare_type)
-        if compare_type:
-            if isinstance(base_type, CallableType) and isinstance(compare_type, CallableType):
-                base_static = is_node_static(base_node)
-                compare_static = is_node_static(compare_node)
-
-                # In case compare_static is unknown, also check
-                # if 'definition' is set. The most common case for
-                # this is with TempNode(), where we lose all
-                # information about the real rvalue node (but only get
-                # the rvalue type)
-                if compare_static is None and compare_type.definition:
-                    compare_static = is_node_static(compare_type.definition)
-
-                # Compare against False, as is_node_static can return None
-                if base_static is False and compare_static is False:
-                    # Class-level function objects and classmethods become bound
-                    # methods: the former to the instance, the latter to the
-                    # class
-                    base_type = bind_self(base_type, self.scope.active_self_type())
-                    compare_type = bind_self(compare_type, self.scope.active_self_type())
-
-                # If we are a static method, ensure to also tell the
-                # lvalue it now contains a static method
-                if base_static and compare_static:
-                    lvalue_node.is_staticmethod = True
-
+        # TODO: check __set__() type override for custom descriptors.
+        # TODO: for descriptors check also class object access override.
+        ok = self.check_subtype(
+            compare_type,
+            base_type,
+            rvalue,
+            message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT,
+            "expression has type",
+            f'base class "{base.name}" defined the type as',
+        )
+        if (
+            ok
+            and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes
+            and self.is_writable_attribute(base_node)
+            and not always_allow_covariant
+        ):
             ok = self.check_subtype(
-                compare_type,
                 base_type,
+                compare_type,
                 rvalue,
-                message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT,
-                "expression has type",
+                message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE,
                 f'base class "{base.name}" defined the type as',
+                "expression has type",
             )
-            if (
-                ok
-                and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes
-                and self.is_writable_attribute(base_node)
-                and not always_allow_covariant
-            ):
-                ok = self.check_subtype(
-                    base_type,
-                    compare_type,
-                    rvalue,
-                    message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE,
-                    f'base class "{base.name}" defined the type as',
-                    "expression has type",
-                )
-            return ok
-        return True
+        return ok
 
     def lvalue_type_from_base(
-        self, expr_node: Var, base: TypeInfo, setter_type: bool = False
+        self, expr_node: Var, base: TypeInfo, setter_type: bool = False, is_class: bool = False
     ) -> tuple[Type | None, SymbolNode | None]:
         """Find a type for a variable name in base class.
 
@@ -3647,49 +3628,41 @@ def lvalue_type_from_base(
         expr_name = expr_node.name
         base_var = base.names.get(expr_name)
 
-        if not base_var:
-            return None, None
-        base_node = base_var.node
-        base_type = base_var.type
-        if isinstance(base_node, Var) and base_type is not None:
-            base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info))
-        if isinstance(base_node, Decorator):
-            base_node = base_node.func
-            base_type = base_node.type
-
-        if not base_type:
+        # TODO: defer current node if the superclass node is not ready.
+        if (
+            not base_var
+            or not base_var.type
+            or isinstance(base_var.type, PartialType)
+            and base_var.type.type is not None
+        ):
             return None, None
-        if not has_no_typevars(base_type):
-            self_type = self.scope.active_self_type()
-            assert self_type is not None, "Internal error: base lookup outside class"
-            if isinstance(self_type, TupleType):
-                instance = tuple_fallback(self_type)
-            else:
-                instance = self_type
-            itype = map_instance_to_supertype(instance, base)
-            base_type = expand_type_by_instance(base_type, itype)
-
-        base_type = get_proper_type(base_type)
-        if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef):
-            # If we are a property, return the Type of the return
-            # value, not the Callable
-            if base_node.is_property:
-                base_type = get_proper_type(base_type.ret_type)
-        if isinstance(base_type, FunctionLike) and isinstance(base_node, OverloadedFuncDef):
-            # Same for properties with setter
-            if base_node.is_property:
-                if setter_type:
-                    assert isinstance(base_node.items[0], Decorator)
-                    base_type = base_node.items[0].var.setter_type
-                    # This flag is True only for custom properties, so it is safe to assert.
-                    assert base_type is not None
-                    base_type = self.bind_and_map_method(base_var, base_type, expr_node.info, base)
-                    assert isinstance(base_type, CallableType)
-                    base_type = get_proper_type(base_type.arg_types[0])
-                else:
-                    base_type = base_type.items[0].ret_type
 
-        return base_type, base_node
+        self_type = self.scope.current_self_type()
+        assert self_type is not None, "Internal error: base lookup outside class"
+        if isinstance(self_type, TupleType):
+            instance = tuple_fallback(self_type)
+        else:
+            instance = self_type
+
+        mx = MemberContext(
+            is_lvalue=setter_type,
+            is_super=False,
+            is_operator=mypy.checkexpr.is_operator_method(expr_name),
+            original_type=self_type,
+            context=expr_node,
+            chk=self,
+            suppress_errors=True,
+        )
+        # TODO: we should not filter "cannot determine type" errors here.
+        with self.msg.filter_errors(filter_deprecated=True):
+            if is_class:
+                fallback = instance.type.metaclass_type or mx.named_type("builtins.type")
+                base_type = analyze_class_attribute_access(
+                    instance, expr_name, mx, mcs_fallback=fallback, override_info=base
+                )
+            else:
+                base_type = analyze_instance_member_access(expr_name, instance, mx, base)
+        return base_type, base_var.node
 
     def check_compatibility_classvar_super(
         self, node: Var, base: TypeInfo, base_node: Node | None
@@ -4515,6 +4488,7 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         refers to the variable (lvalue). If var is None, do nothing.
         """
         if var and not self.current_node_deferred:
+            # TODO: should we also set 'is_ready = True' here?
             var.type = type
             var.is_inferred = True
             if var not in self.var_decl_frames:
@@ -4525,12 +4499,16 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
                 if lvalue.def_var is not None:
                     self.inferred_attribute_types[lvalue.def_var] = type
             self.store_type(lvalue, type)
+            p_type = get_proper_type(type)
+            if isinstance(p_type, CallableType) and is_node_static(p_type.definition):
+                # TODO: handle aliases to class methods (similarly).
+                var.is_staticmethod = True
 
     def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         """Store best known type for variable if type inference failed.
 
         If a program ignores error on type inference error, the variable should get some
-        inferred type so that if can used later on in the program. Example:
+        inferred type so that it can used later on in the program. Example:
 
           x = []  # type: ignore
           x.append(1)   # Should be ok!
@@ -8687,6 +8665,13 @@ def active_self_type(self) -> Instance | TupleType | None:
             return fill_typevars(info)
         return None
 
+    def current_self_type(self) -> Instance | TupleType | None:
+        """Same as active_self_type() but handle functions nested in methods."""
+        for item in reversed(self.stack):
+            if isinstance(item, TypeInfo):
+                return fill_typevars(item)
+        return None
+
     @contextmanager
     def push_function(self, item: FuncItem) -> Iterator[None]:
         self.stack.append(item)
@@ -9190,3 +9175,11 @@ def is_typeddict_type_context(lvalue_type: Type | None) -> bool:
         return False
     lvalue_proper = get_proper_type(lvalue_type)
     return isinstance(lvalue_proper, TypedDictType)
+
+
+def is_method(node: SymbolNode | None) -> bool:
+    if isinstance(node, OverloadedFuncDef):
+        return not node.is_property
+    if isinstance(node, Decorator):
+        return not node.var.is_property
+    return isinstance(node, FuncDef)
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index ebc4fe8705ce..44a20341807b 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -93,11 +93,12 @@ def __init__(
         original_type: Type,
         context: Context,
         chk: mypy.checker.TypeChecker,
-        self_type: Type | None,
+        self_type: Type | None = None,
         module_symbol_table: SymbolTable | None = None,
         no_deferral: bool = False,
         is_self: bool = False,
         rvalue: Expression | None = None,
+        suppress_errors: bool = False,
     ) -> None:
         self.is_lvalue = is_lvalue
         self.is_super = is_super
@@ -113,6 +114,7 @@ def __init__(
         if rvalue is not None:
             assert is_lvalue
         self.rvalue = rvalue
+        self.suppress_errors = suppress_errors
 
     def named_type(self, name: str) -> Instance:
         return self.chk.named_type(name)
@@ -120,6 +122,10 @@ def named_type(self, name: str) -> Instance:
     def not_ready_callback(self, name: str, context: Context) -> None:
         self.chk.handle_cannot_determine_type(name, context)
 
+    def fail(self, msg: str) -> None:
+        if not self.suppress_errors:
+            self.msg.fail(msg, self.context)
+
     def copy_modified(
         self,
         *,
@@ -138,6 +144,7 @@ def copy_modified(
             module_symbol_table=self.module_symbol_table,
             no_deferral=self.no_deferral,
             rvalue=self.rvalue,
+            suppress_errors=self.suppress_errors,
         )
         if self_type is not None:
             mx.self_type = self_type
@@ -165,6 +172,7 @@ def analyze_member_access(
     no_deferral: bool = False,
     is_self: bool = False,
     rvalue: Expression | None = None,
+    suppress_errors: bool = False,
 ) -> Type:
     """Return the type of attribute 'name' of 'typ'.
 
@@ -191,6 +199,11 @@ def analyze_member_access(
 
     'rvalue' can be provided optionally to infer better setter type when is_lvalue is True,
     most notably this helps for descriptors with overloaded __set__() method.
+
+    'suppress_errors' will skip any logic that is only needed to generate error messages.
+    Note that this more of a performance optimization, one should not rely on this to not
+    show any messages, as some may be show e.g. by callbacks called here,
+    use msg.filter_errors(), if needed.
     """
     mx = MemberContext(
         is_lvalue=is_lvalue,
@@ -204,6 +217,7 @@ def analyze_member_access(
         no_deferral=no_deferral,
         is_self=is_self,
         rvalue=rvalue,
+        suppress_errors=suppress_errors,
     )
     result = _analyze_member_access(name, typ, mx, override_info)
     possible_literal = get_proper_type(result)
@@ -251,7 +265,8 @@ def _analyze_member_access(
             )
         return _analyze_member_access(name, typ.upper_bound, mx, override_info)
     elif isinstance(typ, DeletedType):
-        mx.msg.deleted_as_rvalue(typ, mx.context)
+        if not mx.suppress_errors:
+            mx.msg.deleted_as_rvalue(typ, mx.context)
         return AnyType(TypeOfAny.from_error)
     return report_missing_attribute(mx.original_type, typ, name, mx)
 
@@ -280,6 +295,8 @@ def report_missing_attribute(
     mx: MemberContext,
     override_info: TypeInfo | None = None,
 ) -> Type:
+    if mx.suppress_errors:
+        return AnyType(TypeOfAny.from_error)
     error_code = 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):
@@ -297,7 +314,7 @@ def analyze_instance_member_access(
     if name == "__init__" and not mx.is_super:
         # Accessing __init__ in statically typed code would compromise
         # type safety unless used via super().
-        mx.msg.fail(message_registry.CANNOT_ACCESS_INIT, mx.context)
+        mx.fail(message_registry.CANNOT_ACCESS_INIT)
         return AnyType(TypeOfAny.from_error)
 
     # The base object has an instance type.
@@ -310,13 +327,14 @@ def analyze_instance_member_access(
         state.find_occurrences
         and info.name == state.find_occurrences[0]
         and name == state.find_occurrences[1]
+        and not mx.suppress_errors
     ):
         mx.msg.note("Occurrence of '{}.{}'".format(*state.find_occurrences), mx.context)
 
     # Look up the member. First look up the method dictionary.
     method = info.get_method(name)
     if method and not isinstance(method, Decorator):
-        if mx.is_super:
+        if mx.is_super and not mx.suppress_errors:
             validate_super_call(method, mx)
 
         if method.is_property:
@@ -327,7 +345,7 @@ def analyze_instance_member_access(
                 mx.chk.warn_deprecated(items[1], mx.context)
             return analyze_var(name, getter.var, typ, mx)
 
-        if mx.is_lvalue:
+        if mx.is_lvalue and not mx.suppress_errors:
             mx.msg.cant_assign_to_method(mx.context)
         if not isinstance(method, OverloadedFuncDef):
             signature = function_type(method, mx.named_type("builtins.function"))
@@ -361,7 +379,6 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None:
     unsafe_super = False
     if isinstance(node, FuncDef) and node.is_trivial_body:
         unsafe_super = True
-        impl = node
     elif isinstance(node, OverloadedFuncDef):
         if node.impl:
             impl = node.impl if isinstance(node.impl, FuncDef) else node.impl.func
@@ -505,7 +522,7 @@ def analyze_member_var_access(
     if isinstance(vv, Decorator):
         # The associated Var node of a decorator contains the type.
         v = vv.var
-        if mx.is_super:
+        if mx.is_super and not mx.suppress_errors:
             validate_super_call(vv.func, mx)
 
     if isinstance(vv, TypeInfo):
@@ -603,7 +620,7 @@ def analyze_member_var_access(
         if not itype.extra_attrs.mod_name:
             return itype.extra_attrs.attrs[name]
 
-    if mx.is_super:
+    if mx.is_super and not mx.suppress_errors:
         mx.msg.undefined_in_superclass(name, mx.context)
         return AnyType(TypeOfAny.from_error)
     else:
@@ -669,11 +686,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(
+        mx.fail(
             message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
                 descriptor_type.str_with_options(mx.msg.options)
-            ),
-            mx.context,
+            )
         )
         return AnyType(TypeOfAny.from_error)
 
@@ -732,11 +748,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
         return inferred_dunder_get_type
 
     if not isinstance(inferred_dunder_get_type, CallableType):
-        mx.msg.fail(
+        mx.fail(
             message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format(
                 descriptor_type.str_with_options(mx.msg.options)
-            ),
-            mx.context,
+            )
         )
         return AnyType(TypeOfAny.from_error)
 
@@ -747,11 +762,10 @@ def analyze_descriptor_assign(descriptor_type: Instance, mx: MemberContext) -> T
     instance_type = get_proper_type(mx.self_type)
     dunder_set = descriptor_type.type.get_method("__set__")
     if dunder_set is None:
-        mx.chk.fail(
+        mx.fail(
             message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(
                 descriptor_type.str_with_options(mx.msg.options)
-            ),
-            mx.context,
+            ).value
         )
         return AnyType(TypeOfAny.from_error)
 
@@ -851,11 +865,11 @@ def analyze_var(
     if typ:
         if isinstance(typ, PartialType):
             return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context)
-        if mx.is_lvalue and var.is_property and not var.is_settable_property:
-            # TODO allow setting attributes in subclass (although it is probably an error)
-            mx.msg.read_only_property(name, itype.type, mx.context)
-        if mx.is_lvalue and var.is_classvar:
-            mx.msg.cant_assign_to_classvar(name, mx.context)
+        if mx.is_lvalue and not mx.suppress_errors:
+            if var.is_property and not var.is_settable_property:
+                mx.msg.read_only_property(name, itype.type, mx.context)
+            if var.is_classvar:
+                mx.msg.cant_assign_to_classvar(name, mx.context)
         t = freshen_all_functions_type_vars(typ)
         t = expand_self_type_if_needed(t, mx, var, original_itype)
         t = expand_type_by_instance(t, itype)
@@ -875,11 +889,10 @@ def analyze_var(
                 call_type = typ
 
         if isinstance(call_type, FunctionLike) and not call_type.is_type_obj():
-            if mx.is_lvalue:
-                if var.is_property:
-                    if not var.is_settable_property:
-                        mx.msg.read_only_property(name, itype.type, mx.context)
-                else:
+            if mx.is_lvalue and not mx.suppress_errors:
+                if var.is_property and not var.is_settable_property:
+                    mx.msg.read_only_property(name, itype.type, mx.context)
+                elif not var.is_property:
                     mx.msg.cant_assign_to_method(mx.context)
 
             if not var.is_staticmethod:
@@ -1073,22 +1086,20 @@ def analyze_class_attribute_access(
 
     is_decorated = isinstance(node.node, Decorator)
     is_method = is_decorated or isinstance(node.node, FuncBase)
-    if mx.is_lvalue:
+    if mx.is_lvalue and not mx.suppress_errors:
         if is_method:
             mx.msg.cant_assign_to_method(mx.context)
         if isinstance(node.node, TypeInfo):
-            mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)
+            mx.fail(message_registry.CANNOT_ASSIGN_TO_TYPE)
 
     # Refuse class attribute access if slot defined
     if info.slots and name in info.slots:
-        mx.msg.fail(message_registry.CLASS_VAR_CONFLICTS_SLOTS.format(name), mx.context)
+        mx.fail(message_registry.CLASS_VAR_CONFLICTS_SLOTS.format(name))
 
     # If a final attribute was declared on `self` in `__init__`, then it
     # can't be accessed on the class object.
     if node.implicit and isinstance(node.node, Var) and node.node.is_final:
-        mx.msg.fail(
-            message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(node.node.name), mx.context
-        )
+        mx.fail(message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(node.node.name))
 
     # An assignment to final attribute on class object is also always an error,
     # independently of types.
@@ -1146,7 +1157,7 @@ def analyze_class_attribute_access(
                         message = message_registry.GENERIC_CLASS_VAR_ACCESS
                     else:
                         message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
-                    mx.msg.fail(message, mx.context)
+                    mx.fail(message)
             t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True)
             # Erase non-mapped variables, but keep mapped ones, even if there is an error.
             # In the above example this means that we infer following types:
@@ -1176,9 +1187,7 @@ def analyze_class_attribute_access(
         return AnyType(TypeOfAny.special_form)
 
     if isinstance(node.node, TypeVarExpr):
-        mx.msg.fail(
-            message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name, name), mx.context
-        )
+        mx.fail(message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name, name))
         return AnyType(TypeOfAny.from_error)
 
     # TODO: some logic below duplicates analyze_ref_expr in checkexpr.py
@@ -1267,7 +1276,7 @@ def analyze_typeddict_access(
                 typ, mx.context.index, setitem=True
             )
             assigned_readonly_keys = typ.readonly_keys & key_names
-            if assigned_readonly_keys:
+            if assigned_readonly_keys and not mx.suppress_errors:
                 mx.msg.readonly_keys_mutated(assigned_readonly_keys, context=mx.context)
         else:
             # It can also be `a.__setitem__(...)` direct call.
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 0da0f7c3bbcd..e9667db3086e 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -145,7 +145,7 @@ class Base:
         pass
 
 class Derived(Base):
-    __hash__ = 1  # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]")
+    __hash__ = 1  # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[], int]")
 
 [case testOverridePartialAttributeWithMethod]
 # This was crashing: https://github.com/python/mypy/issues/11686.
@@ -4453,7 +4453,7 @@ class A:
     def a(self) -> None: pass
     b = 1
 class B(A):
-    a = 1  # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]")
+    a = 1  # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[], None]")
     def b(self) -> None: pass  # E: Signature of "b" incompatible with supertype "A" \
                                # N:      Superclass: \
                                # N:          int \
@@ -4546,20 +4546,20 @@ main:7: error: Incompatible types in assignment (expression has type "Callable[[
 [case testClassSpec]
 from typing import Callable
 class A():
-    b = None  # type: Callable[[A, int], int]
+    b = None  # type: Callable[[int], int]
 class B(A):
     def c(self, a: int) -> int: pass
     b = c
+reveal_type(A().b)  # N: Revealed type is "def (builtins.int) -> builtins.int"
+reveal_type(B().b)  # N: Revealed type is "def (a: builtins.int) -> builtins.int"
 
 [case testClassSpecError]
 from typing import Callable
 class A():
-    b = None  # type: Callable[[A, int], int]
+    b = None  # type: Callable[[int], int]
 class B(A):
     def c(self, a: str) -> int: pass
-    b = c
-[out]
-main:6: error: Incompatible types in assignment (expression has type "Callable[[str], int]", base class "A" defined the type as "Callable[[int], int]")
+    b = c  # E: Incompatible types in assignment (expression has type "Callable[[str], int]", base class "A" defined the type as "Callable[[int], int]")
 
 [case testClassStaticMethod]
 class A():
@@ -4581,10 +4581,11 @@ class A():
 class B(A):
     @staticmethod
     def b(a: str) -> None: pass
-    c = b
+    c = b  # E: Incompatible types in assignment (expression has type "Callable[[str], None]", base class "A" defined the type as "Callable[[int], None]")
+a: A
+reveal_type(a.a)  # N: Revealed type is "def (a: builtins.int)"
+reveal_type(a.c)  # N: Revealed type is "def (a: builtins.int)"
 [builtins fixtures/staticmethod.pyi]
-[out]
-main:8: error: Incompatible types in assignment (expression has type "Callable[[str], None]", base class "A" defined the type as "Callable[[int], None]")
 
 [case testClassStaticMethodSubclassing]
 class A:
@@ -4649,22 +4650,20 @@ class B(A):
 class A:
     x = 1
 class B(A):
-    x = "a"
+    x = "a"  # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
 class C(B):
-    x = object()
-[out]
-main:4: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
-main:6: error: Incompatible types in assignment (expression has type "object", base class "A" defined the type as "int")
+    x = object()  # E: Incompatible types in assignment (expression has type "object", base class "B" defined the type as "str")
 
 [case testClassOneErrorPerLine]
 class A:
-  x = 1
+    x = 1
 class B(A):
-  x = ""
-  x = 1.0
-[out]
-main:4: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
-main:5: error: Incompatible types in assignment (expression has type "float", base class "A" defined the type as "int")
+    x: str = ""  # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
+    x = 1.0  # E: Incompatible types in assignment (expression has type "float", variable has type "str")
+class BInfer(A):
+    x = ""  # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
+    x = 1.0  # E: Incompatible types in assignment (expression has type "float", variable has type "str") \
+             # E: Incompatible types in assignment (expression has type "float", base class "A" defined the type as "int")
 
 [case testClassIgnoreType_RedefinedAttributeAndGrandparentAttributeTypesNotIgnored]
 class A:
@@ -4672,8 +4671,7 @@ class A:
 class B(A):
     x = ''  # type: ignore
 class C(B):
-    x = ''  # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
-[out]
+    x = ''
 
 [case testClassIgnoreType_RedefinedAttributeTypeIgnoredInChildren]
 class A:
@@ -4682,7 +4680,6 @@ class B(A):
     x = ''  # type: ignore
 class C(B):
     x = ''  # type: ignore
-[out]
 
 [case testInvalidMetaclassStructure]
 class X(type): pass
@@ -8586,3 +8583,68 @@ class Wrapper(Generic[T, R]):
     def __call__(self, s: T) -> list[R]: ...
 def deco_instance(fn: Callable[[T, int], R]) -> Wrapper[T, R]: ...
 [builtins fixtures/property.pyi]
+
+[case testOverridePropertyWithDescriptor]
+from typing import Any
+
+class StrProperty:
+    def __get__(self, instance: Any, owner: Any) -> str: ...
+
+class Base:
+    @property
+    def id(self) -> str: ...
+
+class BadBase:
+    @property
+    def id(self) -> int: ...
+
+class Derived(Base):
+    id = StrProperty()
+
+class BadDerived(BadBase):
+    id = StrProperty()  # E: Incompatible types in assignment (expression has type "str", base class "BadBase" defined the type as "int")
+[builtins fixtures/property.pyi]
+
+[case testLambdaInOverrideInference]
+class B:
+    def f(self, x: int) -> int: ...
+class C(B):
+    f = lambda s, x: x
+
+reveal_type(C().f)  # N: Revealed type is "def (x: builtins.int) -> builtins.int"
+
+[case testGenericDecoratorInOverrideInference]
+from typing import Any, Callable, TypeVar
+from typing_extensions import ParamSpec, Concatenate
+
+P = ParamSpec("P")
+T = TypeVar("T")
+def wrap(f: Callable[Concatenate[Any, P], T]) -> Callable[Concatenate[Any, P], T]: ...
+
+class Base:
+    def g(self, a: int) -> int:
+        return a + 1
+
+class Derived(Base):
+    def _g(self, a: int) -> int:
+        return a + 2
+    g = wrap(_g)
+
+reveal_type(Derived().g)  # N: Revealed type is "def (a: builtins.int) -> builtins.int"
+[builtins fixtures/paramspec.pyi]
+
+[case testClassVarOverrideWithSubclass]
+class A: ...
+class B(A): ...
+class AA:
+    cls = A
+class BB(AA):
+    cls = B
+
+[case testSelfReferenceWithinMethodFunction]
+class B:
+    x: str
+class C(B):
+    def meth(self) -> None:
+        def cb() -> None:
+            self.x: int = 1  # E: Incompatible types in assignment (expression has type "int", base class "B" defined the type as "str")
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index a0a6e9d60920..9d22619590e3 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3528,8 +3528,8 @@ class Parent:
     def method_with(self, param: str) -> "Parent": ...
 
 class Child(Parent):
-    method_without: Callable[["Child"], "Child"]
-    method_with: Callable[["Child", str], "Child"]  # E: Incompatible types in assignment (expression has type "Callable[[str], Child]", base class "Parent" defined the type as "Callable[[Arg(str, 'param')], Parent]")
+    method_without: Callable[[], "Child"]
+    method_with: Callable[[str], "Child"]  # E: Incompatible types in assignment (expression has type "Callable[[str], Child]", base class "Parent" defined the type as "Callable[[Arg(str, 'param')], Parent]")
 [builtins fixtures/tuple.pyi]
 
 [case testDistinctFormattingUnion]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 26ef6cb589ed..9d5902246ae5 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -5235,11 +5235,7 @@ class Sub(Base):
 
 [builtins fixtures/property.pyi]
 [out]
-tmp/a.py:3: error: Cannot determine type of "foo"
-tmp/a.py:4: error: Cannot determine type of "foo"
 [out2]
-tmp/a.py:3: error: Cannot determine type of "foo"
-tmp/a.py:4: error: Cannot determine type of "foo"
 
 [case testRedefinitionClass]
 import b
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
index 22b149174541..3ac669eb93a3 100644
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -548,7 +548,7 @@ b = B._make([''])  # type: B
 [case testNamedTupleIncompatibleRedefinition]
 from typing import NamedTuple
 class Crash(NamedTuple):
-    count: int  # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]")
+    count: int  # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]")
 [builtins fixtures/tuple.pyi]
 
 [case testNamedTupleInClassNamespace]
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index a7124b7a83d3..34e3f3e88080 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -332,7 +332,7 @@ class MyHashable(Protocol):
 
 class C(MyHashable):
     __my_hash__ = None  # E: Incompatible types in assignment \
-(expression has type "None", base class "MyHashable" defined the type as "Callable[[MyHashable], int]")
+(expression has type "None", base class "MyHashable" defined the type as "Callable[[], int]")
 
 [case testProtocolsWithNoneAndStrictOptional]
 from typing import Protocol
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 4c49bd7093cd..03229ccc92e2 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -1803,7 +1803,7 @@ class C:
     def bar(self) -> Self: ...
     foo: Callable[[S, Self], Tuple[Self, S]]
 
-reveal_type(C().foo)  # N: Revealed type is "def [S] (S`1, __main__.C) -> Tuple[__main__.C, S`1]"
+reveal_type(C().foo)  # N: Revealed type is "def [S] (S`2, __main__.C) -> Tuple[__main__.C, S`2]"
 reveal_type(C().foo(42, C()))  # N: Revealed type is "Tuple[__main__.C, builtins.int]"
 class This: ...
 [builtins fixtures/tuple.pyi]
@@ -1899,7 +1899,7 @@ class C:
 
 class D(C): ...
 
-reveal_type(D.f)  # N: Revealed type is "def [T] (T`1) -> T`1"
+reveal_type(D.f)  # N: Revealed type is "def [T] (T`3) -> T`3"
 reveal_type(D().f)  # N: Revealed type is "def () -> __main__.D"
 
 [case testTypingSelfOnSuperTypeVarValues]
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index d2b1a8a92b80..df244b3135e9 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -4520,9 +4520,9 @@ x = 0
 x = ''
 [builtins fixtures/tuple.pyi]
 [out]
-b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]")
+b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]")
 ==
-b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]")
+b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]")
 
 [case testReprocessEllipses1]
 import a

From 12aa6423a6a6b5140b91578c9beea5921f2ffd08 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Mon, 31 Mar 2025 16:20:42 +0200
Subject: [PATCH 1223/1617] [mypyc] Optimize list.__add__, list.__iadd__,
 tuple.__add__ (#18845)

https://docs.python.org/3/c-api/sequence.html#c.PySequence_Concat
https://docs.python.org/3/c-api/sequence.html#c.PySequence_InPlaceConcat
---
 mypyc/doc/list_operations.rst      |  1 +
 mypyc/doc/tuple_operations.rst     |  1 +
 mypyc/primitives/list_ops.py       | 18 ++++++++++++++++
 mypyc/primitives/tuple_ops.py      | 11 +++++++++-
 mypyc/test-data/fixtures/ir.py     | 10 ++++++++-
 mypyc/test-data/irbuild-lists.test | 26 +++++++++++++++++++++++
 mypyc/test-data/irbuild-tuple.test | 34 ++++++++++++++++++++++++++++++
 mypyc/test-data/run-lists.test     | 24 +++++++++++++++++++++
 mypyc/test-data/run-tuples.test    |  9 +++++++-
 9 files changed, 131 insertions(+), 3 deletions(-)

diff --git a/mypyc/doc/list_operations.rst b/mypyc/doc/list_operations.rst
index 5993c0a656bd..378568865501 100644
--- a/mypyc/doc/list_operations.rst
+++ b/mypyc/doc/list_operations.rst
@@ -32,6 +32,7 @@ Operators
 
 * ``lst[n]`` (get item by integer index)
 * ``lst[n:m]``, ``lst[n:]``, ``lst[:m]``, ``lst[:]`` (slicing)
+* ``lst1 + lst2``, ``lst += iter``
 * ``lst * n``, ``n * lst``
 * ``obj in lst``
 
diff --git a/mypyc/doc/tuple_operations.rst b/mypyc/doc/tuple_operations.rst
index fca9e63fc210..ed603fa9982d 100644
--- a/mypyc/doc/tuple_operations.rst
+++ b/mypyc/doc/tuple_operations.rst
@@ -21,6 +21,7 @@ Operators
 
 * ``tup[n]`` (integer index)
 * ``tup[n:m]``, ``tup[n:]``, ``tup[:m]`` (slicing)
+* ``tup1 + tup2``
 
 Statements
 ----------
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index a453e568f00f..5cc8b3c0d1c6 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -271,6 +271,24 @@
     error_kind=ERR_MAGIC,
 )
 
+# list + list
+binary_op(
+    name="+",
+    arg_types=[list_rprimitive, list_rprimitive],
+    return_type=list_rprimitive,
+    c_function_name="PySequence_Concat",
+    error_kind=ERR_MAGIC,
+)
+
+# list += list
+binary_op(
+    name="+=",
+    arg_types=[list_rprimitive, object_rprimitive],
+    return_type=list_rprimitive,
+    c_function_name="PySequence_InPlaceConcat",
+    error_kind=ERR_MAGIC,
+)
+
 # list * int
 binary_op(
     name="*",
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index 0ea0243dc18b..f28d4ca5ec7a 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -15,7 +15,7 @@
     object_rprimitive,
     tuple_rprimitive,
 )
-from mypyc.primitives.registry import custom_op, function_op, load_address_op, method_op
+from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op
 
 # Get the 'builtins.tuple' type object.
 load_address_op(name="builtins.tuple", type=object_rprimitive, src="PyTuple_Type")
@@ -74,6 +74,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# tuple + tuple
+binary_op(
+    name="+",
+    arg_types=[tuple_rprimitive, tuple_rprimitive],
+    return_type=tuple_rprimitive,
+    c_function_name="PySequence_Concat",
+    error_kind=ERR_MAGIC,
+)
+
 # tuple[begin:end]
 tuple_slice_op = custom_op(
     arg_types=[tuple_rprimitive, int_rprimitive, int_rprimitive],
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 2058e4f7be14..e82c79459709 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -208,6 +208,10 @@ def __getitem__(self, i: slice) -> Tuple[T_co, ...]: pass
     def __len__(self) -> int: pass
     def __iter__(self) -> Iterator[T_co]: ...
     def __contains__(self, item: object) -> int: ...
+    @overload
+    def __add__(self, value: Tuple[T_co, ...], /) -> Tuple[T_co, ...]: ...
+    @overload
+    def __add__(self, value: Tuple[_T, ...], /) -> Tuple[T_co | _T, ...]: ...
 
 class function: pass
 
@@ -224,7 +228,11 @@ def __rmul__(self, i: int) -> List[_T]: pass
     def __iter__(self) -> Iterator[_T]: pass
     def __len__(self) -> int: pass
     def __contains__(self, item: object) -> int: ...
-    def __add__(self, x: List[_T]) -> List[_T]: ...
+    @overload
+    def __add__(self, value: List[_T], /) -> List[_T]: ...
+    @overload
+    def __add__(self, value: List[_S], /) -> List[_S | _T]: ...
+    def __iadd__(self, value: Iterable[_T], /) -> List[_T]: ...  # type: ignore[misc]
     def append(self, x: _T) -> None: pass
     def pop(self, i: int = -1) -> _T: pass
     def count(self, _T) -> int: pass
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index e2c656399821..b7ba1a783bb7 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -145,6 +145,32 @@ L0:
     x = r10
     return 1
 
+[case testListAdd]
+from typing import List
+def f(a: List[int], b: List[int]) -> None:
+    c = a + b
+[out]
+def f(a, b):
+    a, b, r0, c :: list
+L0:
+    r0 = PySequence_Concat(a, b)
+    c = r0
+    return 1
+
+[case testListIAdd]
+from typing import List, Any
+def f(a: List[int], b: Any) -> None:
+    a += b
+[out]
+def f(a, b):
+    a :: list
+    b :: object
+    r0 :: list
+L0:
+    r0 = PySequence_InPlaceConcat(a, b)
+    a = r0
+    return 1
+
 [case testListMultiply]
 from typing import List
 def f(a: List[int]) -> None:
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index abb180dde89b..e7280bb3b552 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -384,3 +384,37 @@ L3:
 L4:
     a = r1
     return 1
+
+[case testTupleAdd]
+from typing import Tuple
+def f(a: Tuple[int, ...], b: Tuple[int, ...]) -> None:
+    c = a + b
+    d = a + (1, 2)
+def g(a: Tuple[int, int], b: Tuple[int, int]) -> None:
+    c = a + b
+[out]
+def f(a, b):
+    a, b, r0, c :: tuple
+    r1 :: tuple[int, int]
+    r2 :: object
+    r3, d :: tuple
+L0:
+    r0 = PySequence_Concat(a, b)
+    c = r0
+    r1 = (2, 4)
+    r2 = box(tuple[int, int], r1)
+    r3 = PySequence_Concat(a, r2)
+    d = r3
+    return 1
+def g(a, b):
+    a, b :: tuple[int, int]
+    r0, r1 :: object
+    r2 :: tuple
+    r3, c :: tuple[int, int, int, int]
+L0:
+    r0 = box(tuple[int, int], a)
+    r1 = box(tuple[int, int], b)
+    r2 = PySequence_Concat(r0, r1)
+    r3 = unbox(tuple[int, int, int, int], r2)
+    c = r3
+    return 1
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 3b2721093e0f..84168f7254f5 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -267,6 +267,9 @@ print(g())
 7
 
 [case testListOps]
+from typing import Any, cast
+from testutil import assertRaises
+
 def test_slicing() -> None:
     # Use dummy adds to avoid constant folding
     zero = int()
@@ -289,6 +292,27 @@ def test_slicing() -> None:
     assert s[long_int:] == []
     assert s[-long_int:-1] == ["f", "o", "o", "b", "a"]
 
+def in_place_add(l2: Any) -> list[Any]:
+    l1 = [1, 2]
+    l1 += l2
+    return l1
+
+def test_add() -> None:
+    res = [1, 2, 3, 4]
+    assert [1, 2] + [3, 4] == res
+    with assertRaises(TypeError, 'can only concatenate list (not "tuple") to list'):
+        assert [1, 2] + cast(Any, (3, 4)) == res
+    l1 = [1, 2]
+    id_l1 = id(l1)
+    l1 += [3, 4]
+    assert l1 == res
+    assert id_l1 == id(l1)
+    assert in_place_add([3, 4]) == res
+    assert in_place_add((3, 4)) == res
+    assert in_place_add({3, 4}) == res
+    assert in_place_add({3: "", 4: ""}) == res
+    assert in_place_add(range(3, 5)) == res
+
 [case testOperatorInExpression]
 
 def tuple_in_int0(i: int) -> bool:
diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index 1f1b0bc9eae7..afd3a956b871 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -146,7 +146,8 @@ assert Record.__annotations__ == {
 }, Record.__annotations__
 
 [case testTupleOps]
-from typing import Tuple, Final, List, Any, Optional
+from typing import Tuple, Final, List, Any, Optional, cast
+from testutil import assertRaises
 
 def f() -> Tuple[()]:
     return ()
@@ -254,3 +255,9 @@ TUPLE: Final[Tuple[str, ...]] = ('x', 'y')
 def test_final_boxed_tuple() -> None:
     t = TUPLE
     assert t == ('x', 'y')
+
+def test_add() -> None:
+    res = (1, 2, 3, 4)
+    assert (1, 2) + (3, 4) == res
+    with assertRaises(TypeError, 'can only concatenate tuple (not "list") to tuple'):
+        assert (1, 2) + cast(Any, [3, 4]) == res

From 4fb187f4054f3ee1f2d585320ebe7a8876b65151 Mon Sep 17 00:00:00 2001
From: sobolevn 
Date: Mon, 31 Mar 2025 19:08:43 +0300
Subject: [PATCH 1224/1617] Always use `.enum_members` to find enum members
 (#18675)

Closes #18565

This fixes the problem with `nonmember` and `member` special cases,
however, it required me to change one test case.

See `testEnumReachabilityPEP484ExampleSingletonWithMethod` change,
because in runtime `token` is not a member by default, at least in
recent python versions. Proof:

```python
# 3.14
>>> from enum import Enum
>>> class Empty(Enum):
...     token = lambda x: x
...
>>> Empty.token
 at 0x101251250>
>>> Empty.token.value
```

and

```python
# 3.11
>>> from enum import Enum
>>> class Empty(Enum):
...     token = lambda x: x
...
>>> Empty.token
 at 0x104757600>
>>> Empty.token.value
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'function' object has no attribute 'value'
```

So, I had to add `member()` there to make the test pass.
---
 mypy/checker.py                |  6 +--
 mypy/nodes.py                  | 75 ++++++++++++++++++++++------------
 test-data/unit/check-enum.test | 73 +++++++++++++++++++++++++++++++--
 3 files changed, 121 insertions(+), 33 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 04f1f23362e2..2195c10e2fec 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2788,10 +2788,8 @@ def check_enum(self, defn: ClassDef) -> None:
         self.check_enum_new(defn)
 
     def check_final_enum(self, defn: ClassDef, base: TypeInfo) -> None:
-        for sym in base.names.values():
-            if self.is_final_enum_value(sym):
-                self.fail(f'Cannot extend enum with existing members: "{base.name}"', defn)
-                break
+        if base.enum_members:
+            self.fail(f'Cannot extend enum with existing members: "{base.name}"', defn)
 
     def is_final_enum_value(self, sym: SymbolTableNode) -> bool:
         if isinstance(sym.node, (FuncBase, Decorator)):
diff --git a/mypy/nodes.py b/mypy/nodes.py
index ff31c3e27970..45c59e0c765e 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -14,7 +14,7 @@
 
 import mypy.strconv
 from mypy.options import Options
-from mypy.util import is_typeshed_file, short_type
+from mypy.util import is_sunder, is_typeshed_file, short_type
 from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor
 
 if TYPE_CHECKING:
@@ -3246,32 +3246,55 @@ def protocol_members(self) -> list[str]:
 
     @property
     def enum_members(self) -> list[str]:
-        return [
-            name
-            for name, sym in self.names.items()
-            if (
-                (
-                    isinstance(sym.node, Var)
-                    and name not in EXCLUDED_ENUM_ATTRIBUTES
-                    and not name.startswith("__")
-                    and sym.node.has_explicit_value
-                    and not (
-                        isinstance(
-                            typ := mypy.types.get_proper_type(sym.node.type), mypy.types.Instance
-                        )
+        # TODO: cache the results?
+        members = []
+        for name, sym in self.names.items():
+            # Case 1:
+            #
+            # class MyEnum(Enum):
+            #     @member
+            #     def some(self): ...
+            if isinstance(sym.node, Decorator):
+                if any(
+                    dec.fullname == "enum.member"
+                    for dec in sym.node.decorators
+                    if isinstance(dec, RefExpr)
+                ):
+                    members.append(name)
+                    continue
+            # Case 2:
+            #
+            # class MyEnum(Enum):
+            #     x = 1
+            #
+            # Case 3:
+            #
+            # class MyEnum(Enum):
+            #     class Other: ...
+            elif isinstance(sym.node, (Var, TypeInfo)):
+                if (
+                    # TODO: properly support ignored names from `_ignore_`
+                    name in EXCLUDED_ENUM_ATTRIBUTES
+                    or is_sunder(name)
+                    or name.startswith("__")  # dunder and private
+                ):
+                    continue  # name is excluded
+
+                if isinstance(sym.node, Var):
+                    if not sym.node.has_explicit_value:
+                        continue  # unannotated value not a member
+
+                    typ = mypy.types.get_proper_type(sym.node.type)
+                    if isinstance(
+                        typ, mypy.types.FunctionLike
+                    ) or (  # explicit `@member` is required
+                        isinstance(typ, mypy.types.Instance)
                         and typ.type.fullname == "enum.nonmember"
-                    )
-                )
-                or (
-                    isinstance(sym.node, Decorator)
-                    and any(
-                        dec.fullname == "enum.member"
-                        for dec in sym.node.decorators
-                        if isinstance(dec, RefExpr)
-                    )
-                )
-            )
-        ]
+                    ):
+                        continue  # name is not a member
+
+                members.append(name)
+        return members
 
     def __getitem__(self, name: str) -> SymbolTableNode:
         n = self.get(name)
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index a3abf53e29ac..72e22f2fae94 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -1197,16 +1197,20 @@ def func(x: Union[int, None, Empty] = _empty) -> int:
 [builtins fixtures/primitives.pyi]
 
 [case testEnumReachabilityPEP484ExampleSingletonWithMethod]
+# flags: --python-version 3.11
 from typing import Final, Union
-from enum import Enum
+from enum import Enum, member
 
 class Empty(Enum):
-    token = lambda x: x
+    # note, that without `member` we cannot tell that `token` is a member:
+    token = member(lambda x: x)
 
     def f(self) -> int:
         return 1
 
 _empty = Empty.token
+reveal_type(_empty)  # N: Revealed type is "__main__.Empty"
+reveal_type(Empty.f) # N: Revealed type is "def (self: __main__.Empty) -> builtins.int"
 
 def func(x: Union[int, None, Empty] = _empty) -> int:
     boom = x + 42       # E: Unsupported left operand type for + ("None") \
@@ -1615,6 +1619,65 @@ class ErrorIntFlagWithoutValue(NonEmptyIntFlag):  # E: Cannot extend enum with e
     pass
 [builtins fixtures/bool.pyi]
 
+[case testEnumImplicitlyFinalForSubclassingWithCallableMember]
+# flags: --python-version 3.11
+from enum import Enum, IntEnum, Flag, IntFlag, member
+
+class NonEmptyEnum(Enum):
+    @member
+    def call(self) -> None: ...
+class NonEmptyIntEnum(IntEnum):
+    @member
+    def call(self) -> None: ...
+class NonEmptyFlag(Flag):
+    @member
+    def call(self) -> None: ...
+class NonEmptyIntFlag(IntFlag):
+    @member
+    def call(self) -> None: ...
+
+class ErrorEnumWithoutValue(NonEmptyEnum):  # E: Cannot extend enum with existing members: "NonEmptyEnum"
+    pass
+class ErrorIntEnumWithoutValue(NonEmptyIntEnum):  # E: Cannot extend enum with existing members: "NonEmptyIntEnum"
+    pass
+class ErrorFlagWithoutValue(NonEmptyFlag):  # E: Cannot extend enum with existing members: "NonEmptyFlag"
+    pass
+class ErrorIntFlagWithoutValue(NonEmptyIntFlag):  # E: Cannot extend enum with existing members: "NonEmptyIntFlag"
+    pass
+[builtins fixtures/bool.pyi]
+
+[case testEnumCanExtendEnumsWithNonMembers]
+# flags: --python-version 3.11
+from enum import Enum, IntEnum, Flag, IntFlag, nonmember
+
+class NonEmptyEnum(Enum):
+    x = nonmember(1)
+class NonEmptyIntEnum(IntEnum):
+    x = nonmember(1)
+class NonEmptyFlag(Flag):
+    x = nonmember(1)
+class NonEmptyIntFlag(IntFlag):
+    x = nonmember(1)
+
+class ErrorEnumWithoutValue(NonEmptyEnum):
+    pass
+class ErrorIntEnumWithoutValue(NonEmptyIntEnum):
+    pass
+class ErrorFlagWithoutValue(NonEmptyFlag):
+    pass
+class ErrorIntFlagWithoutValue(NonEmptyIntFlag):
+    pass
+[builtins fixtures/bool.pyi]
+
+[case testLambdaIsNotEnumMember]
+from enum import Enum
+
+class My(Enum):
+    x = lambda a: a
+
+class Other(My): ...
+[builtins fixtures/bool.pyi]
+
 [case testSubclassingNonFinalEnums]
 from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta
 
@@ -1839,6 +1902,10 @@ from enum import Enum
 class A(Enum):
     class Inner: pass
 class B(A): pass  # E: Cannot extend enum with existing members: "A"
+
+class A1(Enum):
+    class __Inner: pass
+class B1(A1): pass
 [builtins fixtures/bool.pyi]
 
 [case testEnumFinalSpecialProps]
@@ -1922,7 +1989,7 @@ from enum import Enum
 class A(Enum):  # E: Detected enum "lib.A" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \
                 # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members
     x: int
-class B(A):  # E: Cannot extend enum with existing members: "A"
+class B(A):
     x = 1    # E: Cannot override writable attribute "x" with a final one
 
 class C(Enum):

From a35e3c03973cb41bca3f3c00f029d9a7685e0a3d Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 1 Apr 2025 19:05:00 +0200
Subject: [PATCH 1225/1617] Prevent crash when enum/typeddict call is stored as
 a class attribute (#18861)

Fixes #18736. Includes same fix for TypedDict (also crashes on master)
and NamedTuple (does not crash as it rejects MemberExpr before setting
.analyzed, so just for the sake of consistency)
---
 mypy/semanal.py                      | 17 ++++++++++++-----
 test-data/unit/check-enum.test       | 12 ++++++++++++
 test-data/unit/check-namedtuple.test | 11 +++++++++++
 test-data/unit/check-typeddict.test  | 13 +++++++++++++
 4 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 6aa5977c110f..60d4f1bde9f8 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -3464,8 +3464,9 @@ def record_special_form_lvalue(self, s: AssignmentStmt) -> None:
     def analyze_enum_assign(self, s: AssignmentStmt) -> bool:
         """Check if s defines an Enum."""
         if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.analyzed, EnumCallExpr):
-            # Already analyzed enum -- nothing to do here.
-            return True
+            # This is an analyzed enum definition.
+            # It is valid iff it can be stored correctly, failures were already reported.
+            return self._is_single_name_assignment(s)
         return self.enum_call_analyzer.process_enum_call(s, self.is_func_scope())
 
     def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool:
@@ -3474,7 +3475,9 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool:
             if s.rvalue.analyzed.info.tuple_type and not has_placeholder(
                 s.rvalue.analyzed.info.tuple_type
             ):
-                return True  # This is a valid and analyzed named tuple definition, nothing to do here.
+                # This is an analyzed named tuple definition.
+                # It is valid iff it can be stored correctly, failures were already reported.
+                return self._is_single_name_assignment(s)
         if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)):
             return False
         lvalue = s.lvalues[0]
@@ -3515,8 +3518,9 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool:
             if s.rvalue.analyzed.info.typeddict_type and not has_placeholder(
                 s.rvalue.analyzed.info.typeddict_type
             ):
-                # This is a valid and analyzed typed dict definition, nothing to do here.
-                return True
+                # This is an analyzed typed dict definition.
+                # It is valid iff it can be stored correctly, failures were already reported.
+                return self._is_single_name_assignment(s)
         if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)):
             return False
         lvalue = s.lvalues[0]
@@ -3540,6 +3544,9 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool:
                 self.setup_alias_type_vars(defn)
             return True
 
+    def _is_single_name_assignment(self, s: AssignmentStmt) -> bool:
+        return len(s.lvalues) == 1 and isinstance(s.lvalues[0], NameExpr)
+
     def analyze_lvalues(self, s: AssignmentStmt) -> None:
         # We cannot use s.type, because analyze_simple_literal_type() will set it.
         explicit = s.unanalyzed_type is not None
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index 72e22f2fae94..cc9048db18dc 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -2512,3 +2512,15 @@ def list_vals(e: Type[T]) -> list[T]:
 
 reveal_type(list_vals(Choices))  # N: Revealed type is "builtins.list[__main__.Choices]"
 [builtins fixtures/enum.pyi]
+
+[case testEnumAsClassMemberNoCrash]
+# https://github.com/python/mypy/issues/18736
+from enum import Enum
+
+class Base:
+    def __init__(self, namespace: tuple[str, ...]) -> None:
+        # Not a bug: trigger defer
+        names = [name for name in namespace if fail]  # E: Name "fail" is not defined
+        self.o = Enum("o", names)  # E: Enum type as attribute is not supported \
+                                   # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
index 3ac669eb93a3..13f977a1e463 100644
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -1519,3 +1519,14 @@ class C(T):
 c: Union[C, Any]
 reveal_type(c.f())  # N: Revealed type is "Union[builtins.bool, Any]"
 [builtins fixtures/tuple.pyi]
+
+[case testNamedTupleAsClassMemberNoCrash]
+# https://github.com/python/mypy/issues/18736
+from collections import namedtuple
+
+class Base:
+    def __init__(self, namespace: tuple[str, ...]) -> None:
+        # Not a bug: trigger defer
+        names = [name for name in namespace if fail]  # E: Name "fail" is not defined
+        self.n = namedtuple("n", names)  # E: NamedTuple type as an attribute is not supported
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index c5ebed57bbcd..48bfa4bdba49 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -4138,3 +4138,16 @@ Derived.Params(name="Robert")
 DerivedOverride.Params(name="Robert")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
+
+[case testEnumAsClassMemberNoCrash]
+# https://github.com/python/mypy/issues/18736
+from typing import TypedDict
+
+class Base:
+    def __init__(self, namespace: dict[str, str]) -> None:
+        # Not a bug: trigger defer
+        names = {n: n for n in namespace if fail}  # E: Name "fail" is not defined
+        self.d = TypedDict("d", names)  # E: TypedDict type as attribute is not supported \
+                                        # E: TypedDict() expects a dictionary literal as the second argument
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]

From 715b9822ddd48865b78a848922eab8714196eb60 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 1 Apr 2025 18:06:52 +0100
Subject: [PATCH 1226/1617] Handle union types when binding self (#18867)

Currently we only bind self if the type is callable, but we actually
should do this for all callable items in a union.

This use case is probably quite niche (since adding an annotation makes
a variable an instance variable, and we rarely infer unions). I found it
when looking at `checkmember`-related issues it was easy to handle it. I
also use this opportunity to refactor and add comments to
`analyze_var()`.
---
 mypy/checkmember.py                | 85 ++++++++++++++++++------------
 test-data/unit/check-classvar.test |  9 ++++
 2 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 44a20341807b..5071709613c9 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -870,15 +870,13 @@ def analyze_var(
                 mx.msg.read_only_property(name, itype.type, mx.context)
             if var.is_classvar:
                 mx.msg.cant_assign_to_classvar(name, mx.context)
-        t = freshen_all_functions_type_vars(typ)
-        t = expand_self_type_if_needed(t, mx, var, original_itype)
-        t = expand_type_by_instance(t, itype)
-        freeze_all_type_vars(t)
-        result = t
-        typ = get_proper_type(typ)
+        # This is the most common case for variables, so start with this.
+        result = expand_without_binding(typ, var, itype, original_itype, mx)
 
+        # A non-None value indicates that we should actually bind self for this variable.
         call_type: ProperType | None = None
         if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator):
+            typ = get_proper_type(typ)
             if isinstance(typ, FunctionLike) and not typ.is_type_obj():
                 call_type = typ
             elif var.is_property:
@@ -888,37 +886,23 @@ def analyze_var(
             else:
                 call_type = typ
 
+        # Bound variables with callable types are treated like methods
+        # (these are usually method aliases like __rmul__ = __mul__).
         if isinstance(call_type, FunctionLike) and not call_type.is_type_obj():
-            if mx.is_lvalue and not mx.suppress_errors:
-                if var.is_property and not var.is_settable_property:
-                    mx.msg.read_only_property(name, itype.type, mx.context)
-                elif not var.is_property:
-                    mx.msg.cant_assign_to_method(mx.context)
-
-            if not var.is_staticmethod:
-                # Class-level function objects and classmethods become bound methods:
-                # the former to the instance, the latter to the class.
-                functype: FunctionLike = call_type
-                signature = freshen_all_functions_type_vars(functype)
-                bound = get_proper_type(expand_self_type(var, signature, mx.original_type))
-                assert isinstance(bound, FunctionLike)
-                signature = bound
-                signature = check_self_arg(
-                    signature, mx.self_type, var.is_classmethod, mx.context, name, mx.msg
-                )
-                signature = bind_self(signature, mx.self_type, var.is_classmethod)
-                expanded_signature = expand_type_by_instance(signature, itype)
-                freeze_all_type_vars(expanded_signature)
-                if var.is_property:
-                    # A property cannot have an overloaded type => the cast is fine.
-                    assert isinstance(expanded_signature, CallableType)
-                    if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
-                        # TODO: use check_call() to infer better type, same as for __set__().
-                        result = expanded_signature.arg_types[0]
-                    else:
-                        result = expanded_signature.ret_type
+            if mx.is_lvalue and not var.is_property and not mx.suppress_errors:
+                mx.msg.cant_assign_to_method(mx.context)
+
+        # Bind the self type for each callable component (when needed).
+        if call_type and not var.is_staticmethod:
+            bound_items = []
+            for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]:
+                p_ct = get_proper_type(ct)
+                if isinstance(p_ct, FunctionLike) and not p_ct.is_type_obj():
+                    item = expand_and_bind_callable(p_ct, var, itype, name, mx)
                 else:
-                    result = expanded_signature
+                    item = expand_without_binding(ct, var, itype, original_itype, mx)
+                bound_items.append(item)
+            result = UnionType.make_union(bound_items)
     else:
         if not var.is_ready and not mx.no_deferral:
             mx.not_ready_callback(var.name, mx.context)
@@ -937,6 +921,37 @@ def analyze_var(
     return result
 
 
+def expand_without_binding(
+    typ: Type, var: Var, itype: Instance, original_itype: Instance, mx: MemberContext
+) -> Type:
+    typ = freshen_all_functions_type_vars(typ)
+    typ = expand_self_type_if_needed(typ, mx, var, original_itype)
+    expanded = expand_type_by_instance(typ, itype)
+    freeze_all_type_vars(expanded)
+    return expanded
+
+
+def expand_and_bind_callable(
+    functype: FunctionLike, var: Var, itype: Instance, name: str, mx: MemberContext
+) -> Type:
+    functype = freshen_all_functions_type_vars(functype)
+    typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
+    assert isinstance(typ, FunctionLike)
+    typ = check_self_arg(typ, mx.self_type, var.is_classmethod, mx.context, name, mx.msg)
+    typ = bind_self(typ, mx.self_type, var.is_classmethod)
+    expanded = expand_type_by_instance(typ, itype)
+    freeze_all_type_vars(expanded)
+    if not var.is_property:
+        return expanded
+    # TODO: a decorated property can result in Overloaded here.
+    assert isinstance(expanded, CallableType)
+    if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
+        # TODO: use check_call() to infer better type, same as for __set__().
+        return expanded.arg_types[0]
+    else:
+        return expanded.ret_type
+
+
 def expand_self_type_if_needed(
     t: Type, mx: MemberContext, var: Var, itype: Instance, is_class: bool = False
 ) -> Type:
diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test
index 918926627bfd..63bbd7471bc8 100644
--- a/test-data/unit/check-classvar.test
+++ b/test-data/unit/check-classvar.test
@@ -334,3 +334,12 @@ class C:
 c:C
 c.foo()  # E: Too few arguments \
          # N: "foo" is considered instance variable, to make it class variable use ClassVar[...]
+
+[case testClassVarUnionBoundOnInstance]
+from typing import Union, Callable, ClassVar
+
+class C:
+    def f(self) -> int: ...
+    g: ClassVar[Union[Callable[[C], int], int]] = f
+
+reveal_type(C().g)  # N: Revealed type is "Union[def () -> builtins.int, builtins.int]"

From d0e27fc76aa6db7cc3afcf03de36f85051c2b1c4 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 1 Apr 2025 22:24:01 +0200
Subject: [PATCH 1227/1617] Reject duplicate `ParamSpec.{args,kwargs}` at call
 site (#18854)

Fixes #18035
---
 mypy/checkexpr.py                             | 33 ++++++++------
 .../unit/check-parameter-specification.test   | 43 +++++++++++++++++++
 2 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 0804917476a9..12480cf9ab93 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2357,7 +2357,8 @@ def check_argument_count(
 
         # Check for too many or few values for formals.
         for i, kind in enumerate(callee.arg_kinds):
-            if kind.is_required() and not formal_to_actual[i] and not is_unexpected_arg_error:
+            mapped_args = formal_to_actual[i]
+            if kind.is_required() and not mapped_args and not is_unexpected_arg_error:
                 # No actual for a mandatory formal
                 if kind.is_positional():
                     self.msg.too_few_arguments(callee, context, actual_names)
@@ -2368,28 +2369,36 @@ def check_argument_count(
                     self.msg.missing_named_argument(callee, context, argname)
                 ok = False
             elif not kind.is_star() and is_duplicate_mapping(
-                formal_to_actual[i], actual_types, actual_kinds
+                mapped_args, actual_types, actual_kinds
             ):
                 if self.chk.in_checked_function() or isinstance(
-                    get_proper_type(actual_types[formal_to_actual[i][0]]), TupleType
+                    get_proper_type(actual_types[mapped_args[0]]), TupleType
                 ):
                     self.msg.duplicate_argument_value(callee, i, context)
                     ok = False
             elif (
                 kind.is_named()
-                and formal_to_actual[i]
-                and actual_kinds[formal_to_actual[i][0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2]
+                and mapped_args
+                and actual_kinds[mapped_args[0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2]
             ):
                 # Positional argument when expecting a keyword argument.
                 self.msg.too_many_positional_arguments(callee, context)
                 ok = False
-            elif (
-                callee.param_spec() is not None
-                and not formal_to_actual[i]
-                and callee.special_sig != "partial"
-            ):
-                self.msg.too_few_arguments(callee, context, actual_names)
-                ok = False
+            elif callee.param_spec() is not None:
+                if not mapped_args and callee.special_sig != "partial":
+                    self.msg.too_few_arguments(callee, context, actual_names)
+                    ok = False
+                elif len(mapped_args) > 1:
+                    paramspec_entries = sum(
+                        isinstance(get_proper_type(actual_types[k]), ParamSpecType)
+                        for k in mapped_args
+                    )
+                    if actual_kinds[mapped_args[0]] == nodes.ARG_STAR and paramspec_entries > 1:
+                        self.msg.fail("ParamSpec.args should only be passed once", context)
+                        ok = False
+                    if actual_kinds[mapped_args[0]] == nodes.ARG_STAR2 and paramspec_entries > 1:
+                        self.msg.fail("ParamSpec.kwargs should only be passed once", context)
+                        ok = False
         return ok
 
     def check_for_extra_actual_arguments(
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
index 5530bc0ecbf9..6f01b15e11f6 100644
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -2560,3 +2560,46 @@ def fn(f: MiddlewareFactory[P]) -> Capture[P]: ...
 
 reveal_type(fn(ServerErrorMiddleware))  # N: Revealed type is "__main__.Capture[[handler: Union[builtins.str, None] =, debug: builtins.bool =]]"
 [builtins fixtures/paramspec.pyi]
+
+[case testRunParamSpecDuplicateArgsKwargs]
+from typing_extensions import ParamSpec, Concatenate
+from typing import Callable, Union
+
+_P = ParamSpec("_P")
+
+def run(predicate: Callable[_P, None], *args: _P.args, **kwargs: _P.kwargs) -> None:
+    predicate(*args, *args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(*args, **kwargs, **kwargs)  # E: ParamSpec.kwargs should only be passed once
+    predicate(*args, *args, **kwargs, **kwargs)  # E: ParamSpec.args should only be passed once \
+                                                 # E: ParamSpec.kwargs should only be passed once
+    copy_args = args
+    copy_kwargs = kwargs
+    predicate(*args, *copy_args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(*copy_args, *args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(*args, **copy_kwargs, **kwargs)  # E: ParamSpec.kwargs should only be passed once
+    predicate(*args, **kwargs, **copy_kwargs)  # E: ParamSpec.kwargs should only be passed once
+
+def run2(predicate: Callable[Concatenate[int, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None:
+    predicate(*args, *args, **kwargs)  # E: ParamSpec.args should only be passed once \
+                                       # E: Argument 1 has incompatible type "*_P.args"; expected "int"
+    predicate(*args, **kwargs, **kwargs)  # E: ParamSpec.kwargs should only be passed once \
+                                          # E: Argument 1 has incompatible type "*_P.args"; expected "int"
+    predicate(1, *args, *args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(1, *args, **kwargs, **kwargs)  # E: ParamSpec.kwargs should only be passed once
+    predicate(1, *args, *args, **kwargs, **kwargs)  # E: ParamSpec.args should only be passed once \
+                                                    # E: ParamSpec.kwargs should only be passed once
+    copy_args = args
+    copy_kwargs = kwargs
+    predicate(1, *args, *copy_args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(1, *copy_args, *args, **kwargs)  # E: ParamSpec.args should only be passed once
+    predicate(1, *args, **copy_kwargs, **kwargs)  # E: ParamSpec.kwargs should only be passed once
+    predicate(1, *args, **kwargs, **copy_kwargs)  # E: ParamSpec.kwargs should only be passed once
+
+def run3(predicate: Callable[Concatenate[int, str, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None:
+    base_ok: tuple[int, str]
+    predicate(*base_ok, *args, **kwargs)
+    base_bad: tuple[Union[int, str], ...]
+    predicate(*base_bad, *args, **kwargs)  # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "int" \
+                                           # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "str" \
+                                           # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "_P.args"
+[builtins fixtures/paramspec.pyi]

From a10c6f1b7ef2d2cc223ec50f62d721fc6f96b170 Mon Sep 17 00:00:00 2001
From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com>
Date: Wed, 2 Apr 2025 10:55:52 +0200
Subject: [PATCH 1228/1617] Add setup.py to selfcheck (#18609)

mypy's setup.py is used as inspiration for other Setuptools-using
projects that want to produce mypyc compiled binary wheels. Therefore it
should also be typechecked and held to a higher standard.
---
 runtests.py | 9 +++++++++
 setup.py    | 8 ++++----
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/runtests.py b/runtests.py
index 9863e8491500..75389c6c56bb 100755
--- a/runtests.py
+++ b/runtests.py
@@ -65,6 +65,15 @@
         "-p",
         "mypyc",
     ],
+    # Type check setup.py as well
+    "self-packaging": [
+        executable,
+        "-m",
+        "mypy",
+        "--config-file",
+        "mypy_self_check.ini",
+        "setup.py",
+    ],
     # Lint
     "lint": ["pre-commit", "run", "--all-files"],
     # Fast test cases only (this is the bulk of the test suite)
diff --git a/setup.py b/setup.py
index e995068b4c5d..12cc1aad4d72 100644
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,7 @@ def is_list_of_setuptools_extension(items: list[Any]) -> TypeGuard[list[Extensio
     return all(isinstance(item, Extension) for item in items)
 
 
-def find_package_data(base, globs, root="mypy"):
+def find_package_data(base: str, globs: list[str], root: str = "mypy") -> list[str]:
     """Find all interesting data files, for setup(package_data=)
 
     Arguments:
@@ -52,13 +52,13 @@ def find_package_data(base, globs, root="mypy"):
 
 
 class CustomPythonBuild(build_py):
-    def pin_version(self):
+    def pin_version(self) -> None:
         path = os.path.join(self.build_lib, "mypy")
         self.mkpath(path)
         with open(os.path.join(path, "version.py"), "w") as stream:
             stream.write(f'__version__ = "{version}"\n')
 
-    def run(self):
+    def run(self) -> None:
         self.execute(self.pin_version, ())
         build_py.run(self)
 
@@ -153,10 +153,10 @@ def run(self):
         # our Appveyor builds run out of memory sometimes.
         multi_file=sys.platform == "win32" or force_multifile,
     )
-    assert is_list_of_setuptools_extension(ext_modules), "Expected mypycify to use setuptools"
 
 else:
     ext_modules = []
 
+assert is_list_of_setuptools_extension(ext_modules), "Expected mypycify to use setuptools"
 
 setup(version=version, ext_modules=ext_modules, cmdclass=cmdclass)

From d6cb14f330ea913102137a3e1a6c44f19808cba2 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 2 Apr 2025 13:13:20 +0100
Subject: [PATCH 1229/1617] Fix descriptor overload selection (#18868)

Fixes https://github.com/python/mypy/issues/15921

I know there were previously concerns about performance of
`check_self_arg()`, but note that the code path where I add it only
affects descriptors and `__getattr__`/`__setattr__`, so I think it
should be OK.
---
 mypy/checkmember.py               | 24 +++++++++++++++++-----
 test-data/unit/check-classes.test | 34 ++++++++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 5071709613c9..015ee14e798f 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -42,6 +42,7 @@
     erase_to_bound,
     freeze_all_type_vars,
     function_type,
+    get_all_type_vars,
     get_type_vars,
     make_simplified_union,
     supported_self_type,
@@ -604,7 +605,10 @@ def analyze_member_var_access(
             setattr_meth = info.get_method("__setattr__")
             if setattr_meth and setattr_meth.info.fullname != "builtins.object":
                 bound_type = analyze_decorator_or_funcbase_access(
-                    defn=setattr_meth, itype=itype, name=name, mx=mx.copy_modified(is_lvalue=False)
+                    defn=setattr_meth,
+                    itype=itype,
+                    name="__setattr__",
+                    mx=mx.copy_modified(is_lvalue=False),
                 )
                 typ = map_instance_to_supertype(itype, setattr_meth.info)
                 setattr_type = get_proper_type(expand_type_by_instance(bound_type, typ))
@@ -1031,7 +1035,16 @@ def f(self: S) -> T: ...
             selfarg = get_proper_type(item.arg_types[0])
             # This level of erasure matches the one in checker.check_func_def(),
             # better keep these two checks consistent.
-            if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))):
+            if subtypes.is_subtype(
+                dispatched_arg_type,
+                erase_typevars(erase_to_bound(selfarg)),
+                # This is to work around the fact that erased ParamSpec and TypeVarTuple
+                # callables are not always compatible with non-erased ones both ways.
+                always_covariant=any(
+                    not isinstance(tv, TypeVarType) for tv in get_all_type_vars(selfarg)
+                ),
+                ignore_pos_arg_names=True,
+            ):
                 new_items.append(item)
             elif isinstance(selfarg, ParamSpecType):
                 # TODO: This is not always right. What's the most reasonable thing to do here?
@@ -1164,6 +1177,7 @@ def analyze_class_attribute_access(
             def_vars = set(node.node.info.defn.type_vars)
             if not node.node.is_classvar and node.node.info.self_type:
                 def_vars.add(node.node.info.self_type)
+            # TODO: should we include ParamSpec etc. here (i.e. use get_all_type_vars)?
             typ_vars = set(get_type_vars(t))
             if def_vars & typ_vars:
                 # Exception: access on Type[...], including first argument of class methods is OK.
@@ -1405,6 +1419,6 @@ def analyze_decorator_or_funcbase_access(
     """
     if isinstance(defn, Decorator):
         return analyze_var(name, defn.var, itype, mx)
-    return bind_self(
-        function_type(defn, mx.chk.named_type("builtins.function")), original_type=mx.self_type
-    )
+    typ = function_type(defn, mx.chk.named_type("builtins.function"))
+    typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg)
+    return bind_self(typ, original_type=mx.self_type, is_classmethod=defn.is_class)
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index e9667db3086e..559088f34a31 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -3135,7 +3135,8 @@ from typing import Any
 class Test:
     def __setattr__() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument? # E: Invalid signature "Callable[[], None]" for "__setattr__"
 t = Test()
-t.crash = 'test'  # E: "Test" has no attribute "crash"
+t.crash = 'test'  # E: Attribute function "__setattr__" with type "Callable[[], None]" does not accept self argument \
+                  # E: "Test" has no attribute "crash"
 
 class A:
     def __setattr__(self): ...  # E: Invalid signature "Callable[[A], Any]" for "__setattr__"
@@ -8648,3 +8649,34 @@ class C(B):
     def meth(self) -> None:
         def cb() -> None:
             self.x: int = 1  # E: Incompatible types in assignment (expression has type "int", base class "B" defined the type as "str")
+
+[case testOverloadedDescriptorSelected]
+from typing import Generic, TypeVar, Any, overload
+
+T_co = TypeVar("T_co", covariant=True)
+class Field(Generic[T_co]):
+    @overload
+    def __get__(self: Field[bool], instance: None, owner: Any) -> BoolField: ...
+    @overload
+    def __get__(self: Field[int], instance: None, owner: Any) -> NumField: ...
+    @overload
+    def __get__(self: Field[Any], instance: None, owner: Any) -> AnyField[T_co]: ...
+    @overload
+    def __get__(self, instance: Any, owner: Any) -> T_co: ...
+
+    def __get__(self, instance: Any, owner: Any) -> Any:
+        pass
+
+class BoolField(Field[bool]): ...
+class NumField(Field[int]): ...
+class AnyField(Field[T_co]): ...
+class Custom: ...
+
+class Fields:
+    bool_f: Field[bool]
+    int_f: Field[int]
+    custom_f: Field[Custom]
+
+reveal_type(Fields.bool_f)  # N: Revealed type is "__main__.BoolField"
+reveal_type(Fields.int_f)  # N: Revealed type is "__main__.NumField"
+reveal_type(Fields.custom_f)  # N: Revealed type is "__main__.AnyField[__main__.Custom]"

From b6a662c3e639bf47a51b93a3e3198e7de25af424 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 2 Apr 2025 19:44:29 +0100
Subject: [PATCH 1230/1617] Use checkmember.py to check method override
 (#18870)

This is a second "large" PR towards
https://github.com/python/mypy/issues/7724. Here I actually expect a
smaller fallout than for variables, since methods are usually less
tricky, but let's see.
---
 mypy/checker.py                        | 171 ++++++++-----------------
 mypy/checkmember.py                    |   7 +-
 test-data/unit/check-classes.test      |  10 +-
 test-data/unit/check-functions.test    |  12 +-
 test-data/unit/check-plugin-attrs.test |   8 +-
 test-data/unit/check-selftype.test     |  18 +--
 test-data/unit/fixtures/module.pyi     |   3 +-
 7 files changed, 80 insertions(+), 149 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 2195c10e2fec..3b48f66fc3b5 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2134,40 +2134,17 @@ def check_method_or_accessor_override_for_base(
                         return None
         return found_base_method
 
-    def check_setter_type_override(
-        self, defn: OverloadedFuncDef, base_attr: SymbolTableNode, base: TypeInfo
-    ) -> None:
+    def check_setter_type_override(self, defn: OverloadedFuncDef, base: TypeInfo) -> None:
         """Check override of a setter type of a mutable attribute.
 
         Currently, this should be only called when either base node or the current node
         is a custom settable property (i.e. where setter type is different from getter type).
         Note that this check is contravariant.
         """
-        base_node = base_attr.node
-        assert isinstance(base_node, (OverloadedFuncDef, Var))
-        original_type, is_original_setter = get_raw_setter_type(base_node)
-        if isinstance(base_node, Var):
-            expanded_type = map_type_from_supertype(original_type, defn.info, base)
-            original_type = get_proper_type(
-                expand_self_type(base_node, expanded_type, fill_typevars(defn.info))
-            )
-        else:
-            assert isinstance(original_type, ProperType)
-            assert isinstance(original_type, CallableType)
-            original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
-            assert isinstance(original_type, CallableType)
-            if is_original_setter:
-                original_type = original_type.arg_types[0]
-            else:
-                original_type = original_type.ret_type
-
-        typ, is_setter = get_raw_setter_type(defn)
-        assert isinstance(typ, ProperType) and isinstance(typ, CallableType)
-        typ = bind_self(typ, self.scope.active_self_type())
-        if is_setter:
-            typ = typ.arg_types[0]
-        else:
-            typ = typ.ret_type
+        typ, _ = self.node_type_from_base(defn, defn.info, setter_type=True)
+        original_type, _ = self.node_type_from_base(defn, base, setter_type=True)
+        # The caller should handle deferrals.
+        assert typ is not None and original_type is not None
 
         if not is_subtype(original_type, typ):
             self.msg.incompatible_setter_override(defn.items[1], typ, original_type, base)
@@ -2192,28 +2169,19 @@ def check_method_override_for_base_with_name(
             context = defn.func
 
         # Construct the type of the overriding method.
-        # TODO: this logic is much less complete than similar one in checkmember.py
         if isinstance(defn, (FuncDef, OverloadedFuncDef)):
-            typ: Type = self.function_type(defn)
             override_class_or_static = defn.is_class or defn.is_static
-            override_class = defn.is_class
         else:
-            assert defn.var.is_ready
-            assert defn.var.type is not None
-            typ = defn.var.type
             override_class_or_static = defn.func.is_class or defn.func.is_static
-            override_class = defn.func.is_class
-        typ = get_proper_type(typ)
-        if isinstance(typ, FunctionLike) and not is_static(context):
-            typ = bind_self(typ, self.scope.active_self_type(), is_classmethod=override_class)
-        # Map the overridden method type to subtype context so that
-        # it can be checked for compatibility.
-        original_type = get_proper_type(base_attr.type)
+        typ, _ = self.node_type_from_base(defn, defn.info)
+        assert typ is not None
+
         original_node = base_attr.node
         # `original_type` can be partial if (e.g.) it is originally an
         # instance variable from an `__init__` block that becomes deferred.
         supertype_ready = True
-        if original_type is None or isinstance(original_type, PartialType):
+        original_type, _ = self.node_type_from_base(defn, base, name_override=name)
+        if original_type is None:
             supertype_ready = False
             if self.pass_num < self.last_pass:
                 # If there are passes left, defer this node until next pass,
@@ -2255,7 +2223,7 @@ def check_method_override_for_base_with_name(
                 # supertype is not known precisely.
                 if supertype_ready:
                     always_allow_covariant = True
-                    self.check_setter_type_override(defn, base_attr, base)
+                    self.check_setter_type_override(defn, base)
 
         if isinstance(original_node, (FuncDef, OverloadedFuncDef)):
             original_class_or_static = original_node.is_class or original_node.is_static
@@ -2265,41 +2233,24 @@ def check_method_override_for_base_with_name(
         else:
             original_class_or_static = False  # a variable can't be class or static
 
-        if isinstance(original_type, FunctionLike):
-            original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
-            if original_node and is_property(original_node):
-                original_type = get_property_type(original_type)
-
-        if isinstance(original_node, Var):
-            expanded_type = map_type_from_supertype(original_type, defn.info, base)
-            expanded_type = expand_self_type(
-                original_node, expanded_type, fill_typevars(defn.info)
-            )
-            original_type = get_proper_type(expanded_type)
+        typ = get_proper_type(typ)
+        original_type = get_proper_type(original_type)
 
-        if is_property(defn):
-            inner: FunctionLike | None
-            if isinstance(typ, FunctionLike):
-                inner = typ
-            else:
-                inner = self.extract_callable_type(typ, context)
-            if inner is not None:
-                typ = inner
-                typ = get_property_type(typ)
-                if (
-                    isinstance(original_node, Var)
-                    and not original_node.is_final
-                    and (not original_node.is_property or original_node.is_settable_property)
-                    and isinstance(defn, Decorator)
-                ):
-                    # We only give an error where no other similar errors will be given.
-                    if not isinstance(original_type, AnyType):
-                        self.msg.fail(
-                            "Cannot override writeable attribute with read-only property",
-                            # Give an error on function line to match old behaviour.
-                            defn.func,
-                            code=codes.OVERRIDE,
-                        )
+        if (
+            is_property(defn)
+            and isinstance(original_node, Var)
+            and not original_node.is_final
+            and (not original_node.is_property or original_node.is_settable_property)
+            and isinstance(defn, Decorator)
+        ):
+            # We only give an error where no other similar errors will be given.
+            if not isinstance(original_type, AnyType):
+                self.msg.fail(
+                    "Cannot override writeable attribute with read-only property",
+                    # Give an error on function line to match old behaviour.
+                    defn.func,
+                    code=codes.OVERRIDE,
+                )
 
         if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
             pass
@@ -3412,7 +3363,7 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
                 # For inference within class body, get supertype attribute as it would look on
                 # a class object for lambdas overriding methods, etc.
                 base_node = base.names[inferred.name].node
-                base_type, _ = self.lvalue_type_from_base(
+                base_type, _ = self.node_type_from_base(
                     inferred,
                     base,
                     is_class=is_method(base_node)
@@ -3523,7 +3474,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                 rvalue_type = self.expr_checker.accept(rvalue, lvalue_node.type)
                 actual_lvalue_type = lvalue_node.type
                 lvalue_node.type = rvalue_type
-            lvalue_type, _ = self.lvalue_type_from_base(lvalue_node, lvalue_node.info)
+            lvalue_type, _ = self.node_type_from_base(lvalue_node, lvalue_node.info)
             if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
                 lvalue_node.type = actual_lvalue_type
 
@@ -3542,7 +3493,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                 if is_private(lvalue_node.name):
                     continue
 
-                base_type, base_node = self.lvalue_type_from_base(lvalue_node, base)
+                base_type, base_node = self.node_type_from_base(lvalue_node, base)
                 custom_setter = is_custom_settable_property(base_node)
                 if isinstance(base_type, PartialType):
                     base_type = None
@@ -3561,7 +3512,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                         # base classes are also incompatible
                         return
                     if lvalue_type and custom_setter:
-                        base_type, _ = self.lvalue_type_from_base(
+                        base_type, _ = self.node_type_from_base(
                             lvalue_node, base, setter_type=True
                         )
                         # Setter type for a custom property must be ready if
@@ -3612,10 +3563,16 @@ def check_compatibility_super(
             )
         return ok
 
-    def lvalue_type_from_base(
-        self, expr_node: Var, base: TypeInfo, setter_type: bool = False, is_class: bool = False
+    def node_type_from_base(
+        self,
+        node: SymbolNode,
+        base: TypeInfo,
+        *,
+        setter_type: bool = False,
+        is_class: bool = False,
+        name_override: str | None = None,
     ) -> tuple[Type | None, SymbolNode | None]:
-        """Find a type for a variable name in base class.
+        """Find a type for a name in base class.
 
         Return the type found and the corresponding node defining the name or None
         for both if the name is not defined in base or the node type is not known (yet).
@@ -3623,15 +3580,16 @@ def lvalue_type_from_base(
         If setter_type is True, return setter types for settable properties (otherwise the
         getter type is returned).
         """
-        expr_name = expr_node.name
-        base_var = base.names.get(expr_name)
+        name = name_override or node.name
+        base_node = base.names.get(name)
 
         # TODO: defer current node if the superclass node is not ready.
         if (
-            not base_var
-            or not base_var.type
-            or isinstance(base_var.type, PartialType)
-            and base_var.type.type is not None
+            not base_node
+            or isinstance(base_node.node, Var)
+            and not base_node.type
+            or isinstance(base_node.type, PartialType)
+            and base_node.type.type is not None
         ):
             return None, None
 
@@ -3645,9 +3603,9 @@ def lvalue_type_from_base(
         mx = MemberContext(
             is_lvalue=setter_type,
             is_super=False,
-            is_operator=mypy.checkexpr.is_operator_method(expr_name),
+            is_operator=mypy.checkexpr.is_operator_method(name),
             original_type=self_type,
-            context=expr_node,
+            context=node,
             chk=self,
             suppress_errors=True,
         )
@@ -3656,11 +3614,11 @@ def lvalue_type_from_base(
             if is_class:
                 fallback = instance.type.metaclass_type or mx.named_type("builtins.type")
                 base_type = analyze_class_attribute_access(
-                    instance, expr_name, mx, mcs_fallback=fallback, override_info=base
+                    instance, name, mx, mcs_fallback=fallback, override_info=base
                 )
             else:
-                base_type = analyze_instance_member_access(expr_name, instance, mx, base)
-        return base_type, base_var.node
+                base_type = analyze_instance_member_access(name, instance, mx, base)
+        return base_type, base_node.node
 
     def check_compatibility_classvar_super(
         self, node: Var, base: TypeInfo, base_node: Node | None
@@ -8965,29 +8923,6 @@ def is_custom_settable_property(defn: SymbolNode | None) -> bool:
     return not is_same_type(get_property_type(get_proper_type(var.type)), setter_type)
 
 
-def get_raw_setter_type(defn: OverloadedFuncDef | Var) -> tuple[Type, bool]:
-    """Get an effective original setter type for a node.
-
-    For a variable it is simply its type. For a property it is the type
-    of the setter method (if not None), or the getter method (used as fallback
-    for the plugin generated properties).
-    Return the type and a flag indicating that we didn't fall back to getter.
-    """
-    if isinstance(defn, Var):
-        # This function should not be called if the var is not ready.
-        assert defn.type is not None
-        return defn.type, True
-    first_item = defn.items[0]
-    assert isinstance(first_item, Decorator)
-    var = first_item.var
-    # This function may be called on non-custom properties, so we need
-    # to handle the situation when it is synthetic (plugin generated).
-    if var.setter_type is not None:
-        return var.setter_type, True
-    assert var.type is not None
-    return var.type, False
-
-
 def get_property_type(t: ProperType) -> ProperType:
     if isinstance(t, CallableType):
         return get_proper_type(t.ret_type)
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 015ee14e798f..dfb141aa415c 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -560,6 +560,8 @@ def analyze_member_var_access(
     elif isinstance(v, MypyFile):
         mx.chk.module_refs.add(v.fullname)
         return mx.chk.expr_checker.module_type(v)
+    elif isinstance(v, TypeVarExpr):
+        return mx.chk.named_type("typing.TypeVar")
     elif (
         not v
         and name not in ["__getattr__", "__setattr__", "__getattribute__"]
@@ -884,9 +886,8 @@ def analyze_var(
             if isinstance(typ, FunctionLike) and not typ.is_type_obj():
                 call_type = typ
             elif var.is_property:
-                call_type = get_proper_type(
-                    _analyze_member_access("__call__", typ, mx.copy_modified(self_type=typ))
-                )
+                deco_mx = mx.copy_modified(original_type=typ, self_type=typ, is_lvalue=False)
+                call_type = get_proper_type(_analyze_member_access("__call__", typ, deco_mx))
             else:
                 call_type = typ
 
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 559088f34a31..65a6a0c9c0a8 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -7982,25 +7982,25 @@ class Parent:
 class Child(Parent):
     def foo(self, val: int) -> int:  # E: Signature of "foo" incompatible with supertype "Parent" \
                                      # N:      Superclass: \
-                                     # N:          None \
+                                     # N:           \
                                      # N:      Subclass: \
                                      # N:          def foo(self, val: int) -> int
         return val
     def bar(self, val: str) -> str:  # E: Signature of "bar" incompatible with supertype "Parent" \
                                      # N:      Superclass: \
-                                     # N:          None \
+                                     # N:          def __init__(self) -> bar \
                                      # N:      Subclass: \
                                      # N:          def bar(self, val: str) -> str
         return val
     def baz(self, val: float) -> float:  # E: Signature of "baz" incompatible with supertype "Parent" \
                                          # N:      Superclass: \
-                                         # N:          None \
+                                         # N:          Module \
                                          # N:      Subclass: \
                                          # N:          def baz(self, val: float) -> float
         return val
     def foobar(self) -> bool:  # E: Signature of "foobar" incompatible with supertype "Parent" \
                                # N:      Superclass: \
-                               # N:          None \
+                               # N:          TypeVar \
                                # N:      Subclass: \
                                # N:          def foobar(self) -> bool
         return False
@@ -8013,6 +8013,8 @@ a: int = child.foo(1)
 b: str = child.bar("abc")
 c: float = child.baz(3.4)
 d: bool = child.foobar()
+[builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testGenericTupleTypeCreation]
 from typing import Generic, Tuple, TypeVar
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index 9d22619590e3..8f48d50fc8ec 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -2819,6 +2819,8 @@ class Child(Base):
     @decorator
     def foo(self) -> int:
         return 42
+reveal_type(Child().foo)  # N: Revealed type is "builtins.int"
+Child().foo = 1  # E: Property "foo" defined in "Child" is read-only
 
 reveal_type(Child().foo)  # N: Revealed type is "builtins.int"
 
@@ -2835,15 +2837,13 @@ class not_a_decorator:
     def __init__(self, fn): ...
 
 class BadChild2(Base):
+    # Override error not shown as accessing 'foo' on BadChild2 returns Any.
     @property
     @not_a_decorator
-    def foo(self) -> int:  # E: "not_a_decorator" not callable \
-                           # E: Signature of "foo" incompatible with supertype "Base" \
-                           # N:      Superclass: \
-                           # N:          int \
-                           # N:      Subclass: \
-                           # N:          not_a_decorator
+    def foo(self) -> int:
         return 42
+reveal_type(BadChild2().foo)  # E: "not_a_decorator" not callable \
+                              # N: Revealed type is "Any"
 [builtins fixtures/property.pyi]
 
 [case explicitOverride]
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index 0c653d608187..2bc144defcb8 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -990,10 +990,10 @@ class C(A, B): pass
 @attr.s
 class D(A): pass
 
-reveal_type(A.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool"
-reveal_type(B.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool"
-reveal_type(C.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool"
-reveal_type(D.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`8, other: _AT`8) -> builtins.bool"
+reveal_type(A.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`29, other: _AT`29) -> builtins.bool"
+reveal_type(B.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`30, other: _AT`30) -> builtins.bool"
+reveal_type(C.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`31, other: _AT`31) -> builtins.bool"
+reveal_type(D.__lt__)  # N: Revealed type is "def [_AT] (self: _AT`32, other: _AT`32) -> builtins.bool"
 
 A() < A()
 B() < B()
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 03229ccc92e2..ffa1a369e883 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -160,12 +160,7 @@ class C(A[int]):
     def f(self) -> int: ...
 
 class D(A[str]):
-    def f(self) -> int: ...  # E: Signature of "f" incompatible with supertype "A" \
-                             # N:      Superclass:            \
-                             # N:          @overload          \
-                             # N:          def f(self) -> str \
-                             # N:      Subclass:              \
-                             # N:          def f(self) -> int
+    def f(self) -> int: ...  # E: Return type "int" of "f" incompatible with return type "str" in supertype "A"
 
 class E(A[T]):
     def f(self) -> int: ...  # E: Signature of "f" incompatible with supertype "A" \
@@ -201,7 +196,6 @@ class I(A[int]):
 class J(A[int]):
     def f(self, arg) -> int: ...  # E: Signature of "f" incompatible with supertype "A" \
                                   # N:      Superclass:            \
-                                  # N:          @overload          \
                                   # N:          def f(self) -> int \
                                   # N:      Subclass:              \
                                   # N:          def f(self, arg: Any) -> int
@@ -224,12 +218,10 @@ class B(A[int]):
     def f(self, s: int) -> int: ...
 
 class C(A[None]):
-    def f(self, s: int) -> int: ...  # E: Signature of "f" incompatible with supertype "A" \
-                                     # N:      Superclass:            \
-                                     # N:          @overload          \
-                                     # N:          def f(self, s: None) -> None \
-                                     # N:      Subclass:              \
-                                     # N:          def f(self, s: int) -> int
+    def f(self, s: int) -> int: ...  # E: Return type "int" of "f" incompatible with return type "None" in supertype "A" \
+                                     # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "None" \
+                                     # N: This violates the Liskov substitution principle \
+                                     # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
 [builtins fixtures/tuple.pyi]
 
 [case testSelfTypeOverrideCompatibilityTypeVar]
diff --git a/test-data/unit/fixtures/module.pyi b/test-data/unit/fixtures/module.pyi
index 47408befd5ce..92f78a42f92f 100644
--- a/test-data/unit/fixtures/module.pyi
+++ b/test-data/unit/fixtures/module.pyi
@@ -4,13 +4,14 @@ from types import ModuleType
 T = TypeVar('T')
 S = TypeVar('S')
 
-class list(Generic[T], Sequence[T]): pass
+class list(Generic[T], Sequence[T]): pass  # type: ignore
 
 class object:
     def __init__(self) -> None: pass
 class type: pass
 class function: pass
 class int: pass
+class float: pass
 class str: pass
 class bool: pass
 class tuple(Generic[T]): pass

From 6b686615dd9fba32af3395d5eeefe2812997c7be Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Wed, 2 Apr 2025 21:57:15 -0400
Subject: [PATCH 1231/1617] Warn about unused `type: ignore` comments when
 error code is disabled (#18849)

Fixes #11059
---
 mypy/errors.py                       |  9 ++++++---
 test-data/unit/check-errorcodes.test | 13 +++++++++++++
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/mypy/errors.py b/mypy/errors.py
index 58ef17b69e96..c9510ae5f1eb 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -506,10 +506,13 @@ def add_error_info(self, info: ErrorInfo) -> None:
                 # line == end_line for most nodes, so we only loop once.
                 for scope_line in lines:
                     if self.is_ignored_error(scope_line, info, self.ignored_lines[file]):
+                        err_code = info.code or codes.MISC
+                        if not self.is_error_code_enabled(err_code):
+                            # Error code is disabled - don't mark the current
+                            # "type: ignore" comment as used.
+                            return
                         # Annotation requests us to ignore all errors on this line.
-                        self.used_ignored_lines[file][scope_line].append(
-                            (info.code or codes.MISC).code
-                        )
+                        self.used_ignored_lines[file][scope_line].append(err_code.code)
                         return
             if file in self.ignored_files:
                 return
diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test
index 6ec246fb3a13..21112b7d85a2 100644
--- a/test-data/unit/check-errorcodes.test
+++ b/test-data/unit/check-errorcodes.test
@@ -105,6 +105,19 @@ x # type: ignore[name-defined, attr-defined] # E: Unused "type: ignore[attr-defi
 # flags: --warn-unused-ignores
 "x" # type: ignore[name-defined] # E: Unused "type: ignore" comment  [unused-ignore]
 
+[case testErrorCodeWarnUnusedIgnores7_WarnWhenErrorCodeDisabled]
+# flags: --warn-unused-ignores --disable-error-code name-defined
+x              # type: ignore                              # E: Unused "type: ignore" comment  [unused-ignore]
+x              # type: ignore[name-defined]                # E: Unused "type: ignore" comment  [unused-ignore]
+"x".foobar(y)  # type: ignore[name-defined, attr-defined]  # E: Unused "type: ignore[name-defined]" comment  [unused-ignore]
+
+[case testErrorCodeWarnUnusedIgnores8_IgnoreUnusedIgnore]
+# flags: --warn-unused-ignores --disable-error-code name-defined
+"x"  # type: ignore[unused-ignore]
+"x"  # type: ignore[name-defined, unused-ignore]
+"x"  # type: ignore[xyz, unused-ignore]
+x    # type: ignore[name-defined, unused-ignore]
+
 [case testErrorCodeMissingWhenRequired]
 # flags: --enable-error-code ignore-without-code
 "x" # type: ignore # E: "type: ignore" comment without error code  [ignore-without-code]

From 1214a74a33548f497ac941e71e1452153f99a94c Mon Sep 17 00:00:00 2001
From: Aaron Gokaslan 
Date: Thu, 3 Apr 2025 06:35:43 -0700
Subject: [PATCH 1232/1617] Enable FURB187 - avoid reverse list copy (#18716)

Enable a ruff rule that avoid copies in list reversal. Use builtin
reverse method.
The builtin does an efficient stride reversal and avoids accidental
copies.
This is extra helpful since the changed reverses are for perf
optimization.
---
 mypy/semanal_main.py       | 2 +-
 mypyc/analysis/dataflow.py | 2 +-
 pyproject.toml             | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py
index 92a1c24b7b4c..2e0d901d5864 100644
--- a/mypy/semanal_main.py
+++ b/mypy/semanal_main.py
@@ -181,7 +181,7 @@ def process_top_levels(graph: Graph, scc: list[str], patches: Patches) -> None:
 
     # Reverse order of the scc so the first modules in the original list will be
     # be processed first. This helps with performance.
-    scc = list(reversed(scc))
+    scc = list(reversed(scc))  # noqa: FURB187 intentional copy
 
     # Initialize ASTs and symbol tables.
     for id in scc:
diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py
index 26b58e224634..0657261e7a8f 100644
--- a/mypyc/analysis/dataflow.py
+++ b/mypyc/analysis/dataflow.py
@@ -542,7 +542,7 @@ def run_analysis(
     # Set up initial state for worklist algorithm.
     worklist = list(blocks)
     if not backward:
-        worklist = worklist[::-1]  # Reverse for a small performance improvement
+        worklist.reverse()  # Reverse for a small performance improvement
     workset = set(worklist)
     before: dict[BasicBlock, set[T]] = {}
     after: dict[BasicBlock, set[T]] = {}
diff --git a/pyproject.toml b/pyproject.toml
index d264ac3749a9..ddc28f458d50 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -148,6 +148,7 @@ select = [
   "SIM201", "SIM202", "SIM222", "SIM223",  # flake8-simplify
   "FURB168", # Prefer is operator over isinstance for None checks
   "FURB169", # Do not use is comparison with type(None). Use None
+  "FURB187", # avoid list reverse copy
   "FURB188", # use str.remove(pre|suf)fix
   "ISC001",  # implicitly concatenated string
   "RET501", "RET502",  # better return None handling

From fcabf19782c95372753e148629c962e3c9218b09 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 4 Apr 2025 11:43:52 +0100
Subject: [PATCH 1233/1617] Use checkmember.py to check multiple inheritance
 (#18876)

This is the third "major" PR towards
https://github.com/python/mypy/issues/7724

This one is mostly straightforward. I tried to preserve the existing
logic about mutable overrides (to minimize fallout), as currently we
e.g. don't use the covariant mutable override error code here. In future
we can separately "synchronize" mutable override logic across variable
override, method override, and multiple inheritance code paths (as
currently all three are subtly different).
---
 mypy/checker.py                        | 141 +++++++++----------------
 test-data/unit/check-abstract.test     |   5 +-
 test-data/unit/check-plugin-attrs.test |   1 +
 3 files changed, 50 insertions(+), 97 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 3b48f66fc3b5..1b10710118df 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -24,7 +24,7 @@
 from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values
 from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode
 from mypy.errors import Errors, ErrorWatcher, report_internal_error
-from mypy.expandtype import expand_self_type, expand_type
+from mypy.expandtype import expand_type
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash
 from mypy.maptype import map_instance_to_supertype
 from mypy.meet import is_overlapping_erased_types, is_overlapping_types, meet_types
@@ -161,7 +161,6 @@
     is_literal_type_like,
     is_singleton_type,
     make_simplified_union,
-    map_type_from_supertype,
     true_only,
     try_expanding_sum_type_to_union,
     try_getting_int_literals_from_type,
@@ -2141,8 +2140,8 @@ def check_setter_type_override(self, defn: OverloadedFuncDef, base: TypeInfo) ->
         is a custom settable property (i.e. where setter type is different from getter type).
         Note that this check is contravariant.
         """
-        typ, _ = self.node_type_from_base(defn, defn.info, setter_type=True)
-        original_type, _ = self.node_type_from_base(defn, base, setter_type=True)
+        typ, _ = self.node_type_from_base(defn.name, defn.info, defn, setter_type=True)
+        original_type, _ = self.node_type_from_base(defn.name, base, defn, setter_type=True)
         # The caller should handle deferrals.
         assert typ is not None and original_type is not None
 
@@ -2173,14 +2172,14 @@ def check_method_override_for_base_with_name(
             override_class_or_static = defn.is_class or defn.is_static
         else:
             override_class_or_static = defn.func.is_class or defn.func.is_static
-        typ, _ = self.node_type_from_base(defn, defn.info)
+        typ, _ = self.node_type_from_base(defn.name, defn.info, defn)
         assert typ is not None
 
         original_node = base_attr.node
         # `original_type` can be partial if (e.g.) it is originally an
         # instance variable from an `__init__` block that becomes deferred.
         supertype_ready = True
-        original_type, _ = self.node_type_from_base(defn, base, name_override=name)
+        original_type, _ = self.node_type_from_base(name, base, defn)
         if original_type is None:
             supertype_ready = False
             if self.pass_num < self.last_pass:
@@ -2321,51 +2320,6 @@ def check_method_override_for_base_with_name(
             )
         return False
 
-    def bind_and_map_method(
-        self, sym: SymbolTableNode, typ: FunctionLike, sub_info: TypeInfo, super_info: TypeInfo
-    ) -> FunctionLike:
-        """Bind self-type and map type variables for a method.
-
-        Arguments:
-            sym: a symbol that points to method definition
-            typ: method type on the definition
-            sub_info: class where the method is used
-            super_info: class where the method was defined
-        """
-        if isinstance(sym.node, (FuncDef, OverloadedFuncDef, Decorator)) and not is_static(
-            sym.node
-        ):
-            if isinstance(sym.node, Decorator):
-                is_class_method = sym.node.func.is_class
-            else:
-                is_class_method = sym.node.is_class
-
-            mapped_typ = cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))
-            active_self_type = fill_typevars(sub_info)
-            if isinstance(mapped_typ, Overloaded):
-                # If we have an overload, filter to overloads that match the self type.
-                # This avoids false positives for concrete subclasses of generic classes,
-                # see testSelfTypeOverrideCompatibility for an example.
-                filtered_items = []
-                for item in mapped_typ.items:
-                    if not item.arg_types:
-                        filtered_items.append(item)
-                    item_arg = item.arg_types[0]
-                    if isinstance(item_arg, TypeVarType):
-                        item_arg = item_arg.upper_bound
-                    if is_subtype(active_self_type, item_arg):
-                        filtered_items.append(item)
-                # If we don't have any filtered_items, maybe it's always a valid override
-                # of the superclass? However if you get to that point you're in murky type
-                # territory anyway, so we just preserve the type and have the behaviour match
-                # that of older versions of mypy.
-                if filtered_items:
-                    mapped_typ = Overloaded(filtered_items)
-
-            return bind_self(mapped_typ, active_self_type, is_class_method)
-        else:
-            return cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))
-
     def get_op_other_domain(self, tp: FunctionLike) -> Type | None:
         if isinstance(tp, CallableType):
             if tp.arg_kinds and tp.arg_kinds[0] == ARG_POS:
@@ -2882,6 +2836,7 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
                     self.check_compatibility(name, base, base2, typ)
 
     def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None:
+        # TODO: this duplicates both checkmember.py and analyze_ref_expr(), delete.
         if sym.type is not None:
             return sym.type
         if isinstance(sym.node, SYMBOL_FUNCBASE_TYPES):
@@ -2901,7 +2856,6 @@ def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None:
                 # Suppress any errors, they will be given when analyzing the corresponding node.
                 # Here we may have incorrect options and location context.
                 return self.expr_checker.alias_type_in_runtime_context(sym.node, ctx=sym.node)
-        # TODO: handle more node kinds here.
         return None
 
     def check_compatibility(
@@ -2932,50 +2886,47 @@ class C(B, A[int]): ...  # this is unsafe because...
             return
         first = base1.names[name]
         second = base2.names[name]
-        first_type = get_proper_type(self.determine_type_of_member(first))
-        second_type = get_proper_type(self.determine_type_of_member(second))
+        # Specify current_class explicitly as this function is called after leaving the class.
+        first_type, _ = self.node_type_from_base(name, base1, ctx, current_class=ctx)
+        second_type, _ = self.node_type_from_base(name, base2, ctx, current_class=ctx)
 
         # TODO: use more principled logic to decide is_subtype() vs is_equivalent().
         # We should rely on mutability of superclass node, not on types being Callable.
         # (in particular handle settable properties with setter type different from getter).
 
-        # start with the special case that Instance can be a subtype of FunctionLike
-        call = None
-        if isinstance(first_type, Instance):
-            call = find_member("__call__", first_type, first_type, is_operator=True)
-        if call and isinstance(second_type, FunctionLike):
-            second_sig = self.bind_and_map_method(second, second_type, ctx, base2)
-            ok = is_subtype(call, second_sig, ignore_pos_arg_names=True)
-        elif isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike):
-            if first_type.is_type_obj() and second_type.is_type_obj():
+        p_first_type = get_proper_type(first_type)
+        p_second_type = get_proper_type(second_type)
+        if isinstance(p_first_type, FunctionLike) and isinstance(p_second_type, FunctionLike):
+            if p_first_type.is_type_obj() and p_second_type.is_type_obj():
                 # For class objects only check the subtype relationship of the classes,
                 # since we allow incompatible overrides of '__init__'/'__new__'
                 ok = is_subtype(
-                    left=fill_typevars_with_any(first_type.type_object()),
-                    right=fill_typevars_with_any(second_type.type_object()),
+                    left=fill_typevars_with_any(p_first_type.type_object()),
+                    right=fill_typevars_with_any(p_second_type.type_object()),
                 )
             else:
-                # First bind/map method types when necessary.
-                first_sig = self.bind_and_map_method(first, first_type, ctx, base1)
-                second_sig = self.bind_and_map_method(second, second_type, ctx, base2)
-                ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True)
+                assert first_type and second_type
+                ok = is_subtype(first_type, second_type, ignore_pos_arg_names=True)
         elif first_type and second_type:
-            if isinstance(first.node, Var):
-                first_type = get_proper_type(map_type_from_supertype(first_type, ctx, base1))
-                first_type = expand_self_type(first.node, first_type, fill_typevars(ctx))
-            if isinstance(second.node, Var):
-                second_type = get_proper_type(map_type_from_supertype(second_type, ctx, base2))
-                second_type = expand_self_type(second.node, second_type, fill_typevars(ctx))
-            ok = is_equivalent(first_type, second_type)
-            if not ok:
-                second_node = base2[name].node
+            if second.node is not None and not self.is_writable_attribute(second.node):
+                ok = is_subtype(first_type, second_type)
+            else:
+                ok = is_equivalent(first_type, second_type)
+            if ok:
                 if (
-                    isinstance(second_type, FunctionLike)
-                    and second_node is not None
-                    and is_property(second_node)
+                    first.node
+                    and second.node
+                    and self.is_writable_attribute(second.node)
+                    and is_property(first.node)
+                    and isinstance(first.node, Decorator)
+                    and not isinstance(p_second_type, AnyType)
                 ):
-                    second_type = get_property_type(second_type)
-                    ok = is_subtype(first_type, second_type)
+                    self.msg.fail(
+                        f'Cannot override writeable attribute "{name}" in base "{base2.name}"'
+                        f' with read-only property in base "{base1.name}"',
+                        ctx,
+                        code=codes.OVERRIDE,
+                    )
         else:
             if first_type is None:
                 self.msg.cannot_determine_type_in_base(name, base1.name, ctx)
@@ -3364,8 +3315,9 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
                 # a class object for lambdas overriding methods, etc.
                 base_node = base.names[inferred.name].node
                 base_type, _ = self.node_type_from_base(
-                    inferred,
+                    inferred.name,
                     base,
+                    inferred,
                     is_class=is_method(base_node)
                     or isinstance(base_node, Var)
                     and not is_instance_var(base_node),
@@ -3474,7 +3426,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                 rvalue_type = self.expr_checker.accept(rvalue, lvalue_node.type)
                 actual_lvalue_type = lvalue_node.type
                 lvalue_node.type = rvalue_type
-            lvalue_type, _ = self.node_type_from_base(lvalue_node, lvalue_node.info)
+            lvalue_type, _ = self.node_type_from_base(lvalue_node.name, lvalue_node.info, lvalue)
             if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
                 lvalue_node.type = actual_lvalue_type
 
@@ -3493,7 +3445,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                 if is_private(lvalue_node.name):
                     continue
 
-                base_type, base_node = self.node_type_from_base(lvalue_node, base)
+                base_type, base_node = self.node_type_from_base(lvalue_node.name, base, lvalue)
                 custom_setter = is_custom_settable_property(base_node)
                 if isinstance(base_type, PartialType):
                     base_type = None
@@ -3513,7 +3465,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                         return
                     if lvalue_type and custom_setter:
                         base_type, _ = self.node_type_from_base(
-                            lvalue_node, base, setter_type=True
+                            lvalue_node.name, base, lvalue, setter_type=True
                         )
                         # Setter type for a custom property must be ready if
                         # the getter type is ready.
@@ -3565,12 +3517,13 @@ def check_compatibility_super(
 
     def node_type_from_base(
         self,
-        node: SymbolNode,
+        name: str,
         base: TypeInfo,
+        context: Context,
         *,
         setter_type: bool = False,
         is_class: bool = False,
-        name_override: str | None = None,
+        current_class: TypeInfo | None = None,
     ) -> tuple[Type | None, SymbolNode | None]:
         """Find a type for a name in base class.
 
@@ -3580,20 +3533,22 @@ def node_type_from_base(
         If setter_type is True, return setter types for settable properties (otherwise the
         getter type is returned).
         """
-        name = name_override or node.name
         base_node = base.names.get(name)
 
         # TODO: defer current node if the superclass node is not ready.
         if (
             not base_node
-            or isinstance(base_node.node, Var)
+            or isinstance(base_node.node, (Var, Decorator))
             and not base_node.type
             or isinstance(base_node.type, PartialType)
             and base_node.type.type is not None
         ):
             return None, None
 
-        self_type = self.scope.current_self_type()
+        if current_class is None:
+            self_type = self.scope.current_self_type()
+        else:
+            self_type = fill_typevars(current_class)
         assert self_type is not None, "Internal error: base lookup outside class"
         if isinstance(self_type, TupleType):
             instance = tuple_fallback(self_type)
@@ -3605,7 +3560,7 @@ def node_type_from_base(
             is_super=False,
             is_operator=mypy.checkexpr.is_operator_method(name),
             original_type=self_type,
-            context=node,
+            context=context,
             chk=self,
             suppress_errors=True,
         )
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
index 3b0b9c520b75..455ee3c5265b 100644
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -990,7 +990,6 @@ class Mixin:
 class C(Mixin, A):
     pass
 [builtins fixtures/property.pyi]
-[out]
 
 [case testMixinSubtypedProperty]
 class X:
@@ -1006,7 +1005,6 @@ class Mixin:
 class C(Mixin, A):
     pass
 [builtins fixtures/property.pyi]
-[out]
 
 [case testMixinTypedPropertyReversed]
 class A:
@@ -1015,10 +1013,9 @@ class A:
         return "no"
 class Mixin:
     foo = "foo"
-class C(A, Mixin): # E: Definition of "foo" in base class "A" is incompatible with definition in base class "Mixin"
+class C(A, Mixin): # E: Cannot override writeable attribute "foo" in base "Mixin" with read-only property in base "A"
     pass
 [builtins fixtures/property.pyi]
-[out]
 
 -- Special cases
 -- -------------
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index 2bc144defcb8..c44854b7fc42 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -1836,6 +1836,7 @@ class B:
 class AB(A, B):
     pass
 [builtins fixtures/plugin_attrs.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testAttrsForwardReferenceInTypeVarBound]
 from typing import TypeVar, Generic

From 772187f5e0bb4f91fd0afd151b042ec390023089 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 4 Apr 2025 19:26:20 +0200
Subject: [PATCH 1234/1617] Sync typeshed (#18880)

Source commit:

https://github.com/python/typeshed/commit/616ca7db2c1aad26b23523d7e5edea668d3619e5

This is the last typeshed commit with support for Python 3.8!

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: AlexWaygood 
---
 mypy/stubgenc.py                              |   2 +-
 mypy/typeshed/stdlib/_typeshed/__init__.pyi   |   5 +-
 mypy/typeshed/stdlib/asyncio/sslproto.pyi     |   2 +-
 mypy/typeshed/stdlib/asyncio/transports.pyi   |   6 +-
 mypy/typeshed/stdlib/heapq.pyi                |   4 +-
 mypy/typeshed/stdlib/http/server.pyi          |   3 +-
 .../stdlib/importlib/resources/__init__.pyi   |   6 +-
 .../stdlib/importlib/resources/_common.pyi    |   4 +-
 .../importlib/resources/_functional.pyi       |   4 +-
 mypy/typeshed/stdlib/inspect.pyi              |   8 +-
 mypy/typeshed/stdlib/logging/config.pyi       |   7 +-
 mypy/typeshed/stdlib/pkgutil.pyi              |  12 +-
 mypy/typeshed/stdlib/statistics.pyi           |  29 ++--
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |   2 +-
 mypy/typeshed/stdlib/types.pyi                |   2 +
 mypy/typeshed/stdlib/typing.pyi               |   8 +-
 mypy/typeshed/stdlib/typing_extensions.pyi    | 136 ++++++++++++------
 mypy/typeshed/stdlib/warnings.pyi             |  17 ++-
 18 files changed, 162 insertions(+), 95 deletions(-)

diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py
index c673ea929dfa..b03a88cf6f43 100755
--- a/mypy/stubgenc.py
+++ b/mypy/stubgenc.py
@@ -765,7 +765,7 @@ def generate_property_stub(
 
     def get_type_fullname(self, typ: type) -> str:
         """Given a type, return a string representation"""
-        if typ is Any:  # type: ignore[comparison-overlap]
+        if typ is Any:
             return "Any"
         typename = getattr(typ, "__qualname__", typ.__name__)
         module_name = self.get_obj_module(typ)
diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
index 2b56a4e97519..99d21b67360a 100644
--- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi
+++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
@@ -3,6 +3,7 @@
 # See the README.md file in this directory for more information.
 
 import sys
+import typing_extensions
 from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized
 from dataclasses import Field
 from os import PathLike
@@ -328,9 +329,9 @@ class structseq(Generic[_T_co]):
     # The second parameter will accept a dict of any kind without raising an exception,
     # but only has any meaning if you supply it a dict where the keys are strings.
     # https://github.com/python/typeshed/pull/6560#discussion_r767149830
-    def __new__(cls: type[Self], sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> Self: ...
+    def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> typing_extensions.Self: ...
     if sys.version_info >= (3, 13):
-        def __replace__(self: Self, **kwargs: Any) -> Self: ...
+        def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ...
 
 # Superset of typing.AnyStr that also includes LiteralString
 AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString)  # noqa: Y001
diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi
index ded1933dd659..ab102f124c2e 100644
--- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi
+++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi
@@ -76,7 +76,7 @@ class _SSLProtocolTransport(transports._FlowControlMixin, transports.Transport):
     def get_extra_info(self, name: str, default: Any | None = None) -> dict[str, Any]: ...
     @property
     def _protocol_paused(self) -> bool: ...
-    def write(self, data: bytes | bytearray | memoryview) -> None: ...
+    def write(self, data: bytes | bytearray | memoryview[Any]) -> None: ...  # any memoryview format or shape
     def can_write_eof(self) -> Literal[False]: ...
     if sys.version_info >= (3, 11):
         def get_write_buffer_limits(self) -> tuple[int, int]: ...
diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi
index c28ae234f2cc..bce54897f18f 100644
--- a/mypy/typeshed/stdlib/asyncio/transports.pyi
+++ b/mypy/typeshed/stdlib/asyncio/transports.pyi
@@ -24,8 +24,10 @@ class WriteTransport(BaseTransport):
     def set_write_buffer_limits(self, high: int | None = None, low: int | None = None) -> None: ...
     def get_write_buffer_size(self) -> int: ...
     def get_write_buffer_limits(self) -> tuple[int, int]: ...
-    def write(self, data: bytes | bytearray | memoryview) -> None: ...
-    def writelines(self, list_of_data: Iterable[bytes | bytearray | memoryview]) -> None: ...
+    def write(self, data: bytes | bytearray | memoryview[Any]) -> None: ...  # any memoryview format or shape
+    def writelines(
+        self, list_of_data: Iterable[bytes | bytearray | memoryview[Any]]
+    ) -> None: ...  # any memoryview format or shape
     def write_eof(self) -> None: ...
     def can_write_eof(self) -> bool: ...
     def abort(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/heapq.pyi b/mypy/typeshed/stdlib/heapq.pyi
index 7a3aa8b442a5..220c41f303fb 100644
--- a/mypy/typeshed/stdlib/heapq.pyi
+++ b/mypy/typeshed/stdlib/heapq.pyi
@@ -1,6 +1,6 @@
 from _heapq import *
 from _typeshed import SupportsRichComparison
-from collections.abc import Callable, Iterable
+from collections.abc import Callable, Generator, Iterable
 from typing import Any, Final, TypeVar
 
 __all__ = ["heappush", "heappop", "heapify", "heapreplace", "merge", "nlargest", "nsmallest", "heappushpop"]
@@ -11,7 +11,7 @@ __about__: Final[str]
 
 def merge(
     *iterables: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None, reverse: bool = False
-) -> Iterable[_S]: ...
+) -> Generator[_S]: ...
 def nlargest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ...
 def nsmallest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ...
 def _heapify_max(heap: list[Any], /) -> None: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi
index b273e19c10cd..1a6fde6000d9 100644
--- a/mypy/typeshed/stdlib/http/server.pyi
+++ b/mypy/typeshed/stdlib/http/server.pyi
@@ -6,6 +6,7 @@ import sys
 from _typeshed import StrPath, SupportsRead, SupportsWrite
 from collections.abc import Mapping, Sequence
 from typing import Any, AnyStr, BinaryIO, ClassVar
+from typing_extensions import deprecated
 
 __all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"]
 
@@ -72,7 +73,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
     def guess_type(self, path: StrPath) -> str: ...  # undocumented
 
 def executable(path: StrPath) -> bool: ...  # undocumented
-
+@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
 class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
     cgi_directories: list[str]
     have_fork: bool  # undocumented
diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
index f82df8c591fa..a30e6cdce5c6 100644
--- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
@@ -4,7 +4,7 @@ from collections.abc import Iterator
 from contextlib import AbstractContextManager
 from pathlib import Path
 from types import ModuleType
-from typing import Any, BinaryIO, TextIO
+from typing import Any, BinaryIO, Literal, TextIO
 from typing_extensions import TypeAlias
 
 if sys.version_info >= (3, 11):
@@ -51,14 +51,14 @@ else:
     def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ...
     def read_binary(package: Package, resource: Resource) -> bytes: ...
     def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ...
-    def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ...
+    def path(package: Package, resource: Resource) -> AbstractContextManager[Path, Literal[False]]: ...
     def is_resource(package: Package, name: str) -> bool: ...
     def contents(package: Package) -> Iterator[str]: ...
 
 if sys.version_info >= (3, 11):
     from importlib.resources._common import as_file as as_file
 elif sys.version_info >= (3, 9):
-    def as_file(path: Traversable) -> AbstractContextManager[Path]: ...
+    def as_file(path: Traversable) -> AbstractContextManager[Path, Literal[False]]: ...
 
 if sys.version_info >= (3, 11):
     from importlib.resources._common import files as files
diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi
index f1056f62ed6e..d6a9436544dc 100644
--- a/mypy/typeshed/stdlib/importlib/resources/_common.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi
@@ -7,7 +7,7 @@ if sys.version_info >= (3, 11):
     from contextlib import AbstractContextManager
     from importlib.abc import ResourceReader, Traversable
     from pathlib import Path
-    from typing import overload
+    from typing import Literal, overload
     from typing_extensions import TypeAlias, deprecated
 
     Package: TypeAlias = str | types.ModuleType
@@ -39,4 +39,4 @@ if sys.version_info >= (3, 11):
         def get_package(package: Package) -> types.ModuleType: ...
 
     def from_package(package: types.ModuleType) -> Traversable: ...
-    def as_file(path: Traversable) -> AbstractContextManager[Path]: ...
+    def as_file(path: Traversable) -> AbstractContextManager[Path, Literal[False]]: ...
diff --git a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi
index 97e46bdf0a53..50f3405f9a00 100644
--- a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi
@@ -8,7 +8,7 @@ if sys.version_info >= (3, 13):
     from importlib.resources._common import Anchor
     from io import TextIOWrapper
     from pathlib import Path
-    from typing import BinaryIO, overload
+    from typing import BinaryIO, Literal, overload
     from typing_extensions import Unpack
 
     def open_binary(anchor: Anchor, *path_names: StrPath) -> BinaryIO: ...
@@ -25,6 +25,6 @@ if sys.version_info >= (3, 13):
     ) -> str: ...
     @overload
     def read_text(anchor: Anchor, *path_names: StrPath, encoding: str | None, errors: str | None = "strict") -> str: ...
-    def path(anchor: Anchor, *path_names: StrPath) -> AbstractContextManager[Path]: ...
+    def path(anchor: Anchor, *path_names: StrPath) -> AbstractContextManager[Path, Literal[False]]: ...
     def is_resource(anchor: Anchor, *path_names: StrPath) -> bool: ...
     def contents(anchor: Anchor, *path_names: StrPath) -> Iterator[str]: ...
diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi
index 229eb2135690..5bebe9bf4482 100644
--- a/mypy/typeshed/stdlib/inspect.pyi
+++ b/mypy/typeshed/stdlib/inspect.pyi
@@ -345,12 +345,12 @@ class Signature:
 
 if sys.version_info >= (3, 10):
     def get_annotations(
-        obj: Callable[..., object] | type[Any] | ModuleType,
+        obj: Callable[..., object] | type[object] | ModuleType,  # any callable, class, or module
         *,
-        globals: Mapping[str, Any] | None = None,
-        locals: Mapping[str, Any] | None = None,
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
         eval_str: bool = False,
-    ) -> dict[str, Any]: ...
+    ) -> dict[str, Any]: ...  # values are type expressions
 
 # The name is the same as the enum's name in CPython
 class _ParameterKind(enum.IntEnum):
diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi
index 5c444e66c4c7..000ba1ebb06e 100644
--- a/mypy/typeshed/stdlib/logging/config.pyi
+++ b/mypy/typeshed/stdlib/logging/config.pyi
@@ -4,7 +4,7 @@ from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence
 from configparser import RawConfigParser
 from re import Pattern
 from threading import Thread
-from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload
+from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload, type_check_only
 from typing_extensions import Required, TypeAlias
 
 from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level
@@ -14,17 +14,20 @@ RESET_ERROR: Final[int]  # undocumented
 IDENTIFIER: Final[Pattern[str]]  # undocumented
 
 if sys.version_info >= (3, 11):
+    @type_check_only
     class _RootLoggerConfiguration(TypedDict, total=False):
         level: _Level
         filters: Sequence[str | _FilterType]
         handlers: Sequence[str]
 
 else:
+    @type_check_only
     class _RootLoggerConfiguration(TypedDict, total=False):
         level: _Level
         filters: Sequence[str]
         handlers: Sequence[str]
 
+@type_check_only
 class _LoggerConfiguration(_RootLoggerConfiguration, TypedDict, total=False):
     propagate: bool
 
@@ -32,6 +35,7 @@ _FormatterConfigurationTypedDict = TypedDict(
     "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False
 )
 
+@type_check_only
 class _FilterConfigurationTypedDict(TypedDict):
     name: str
 
@@ -43,6 +47,7 @@ _FilterConfiguration: TypeAlias = _FilterConfigurationTypedDict | dict[str, Any]
 # Handler config can have additional keys even when not providing a custom factory so we just use `dict`.
 _HandlerConfiguration: TypeAlias = dict[str, Any]
 
+@type_check_only
 class _DictConfigArgs(TypedDict, total=False):
     version: Required[Literal[1]]
     formatters: dict[str, _FormatterConfiguration]
diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi
index 7e7fa4fda9a1..59d70779c72f 100644
--- a/mypy/typeshed/stdlib/pkgutil.pyi
+++ b/mypy/typeshed/stdlib/pkgutil.pyi
@@ -1,5 +1,5 @@
 import sys
-from _typeshed import SupportsRead
+from _typeshed import StrOrBytesPath, SupportsRead
 from _typeshed.importlib import LoaderProtocol, MetaPathFinderProtocol, PathEntryFinderProtocol
 from collections.abc import Callable, Iterable, Iterator
 from typing import IO, Any, NamedTuple, TypeVar
@@ -31,21 +31,21 @@ def extend_path(path: _PathT, name: str) -> _PathT: ...
 
 if sys.version_info < (3, 12):
     class ImpImporter:
-        def __init__(self, path: str | None = None) -> None: ...
+        def __init__(self, path: StrOrBytesPath | None = None) -> None: ...
 
     class ImpLoader:
-        def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ...
+        def __init__(self, fullname: str, file: IO[str], filename: StrOrBytesPath, etc: tuple[str, str, int]) -> None: ...
 
 @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
 def find_loader(fullname: str) -> LoaderProtocol | None: ...
-def get_importer(path_item: str) -> PathEntryFinderProtocol | None: ...
+def get_importer(path_item: StrOrBytesPath) -> PathEntryFinderProtocol | None: ...
 @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
 def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
 def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ...
-def iter_modules(path: Iterable[str] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ...
+def iter_modules(path: Iterable[StrOrBytesPath] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ...
 def read_code(stream: SupportsRead[bytes]) -> Any: ...  # undocumented
 def walk_packages(
-    path: Iterable[str] | None = None, prefix: str = "", onerror: Callable[[str], object] | None = None
+    path: Iterable[StrOrBytesPath] | None = None, prefix: str = "", onerror: Callable[[str], object] | None = None
 ) -> Iterator[ModuleInfo]: ...
 def get_data(package: str, resource: str) -> bytes | None: ...
 
diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi
index c8ecbbceab1a..9418bdea9d6d 100644
--- a/mypy/typeshed/stdlib/statistics.pyi
+++ b/mypy/typeshed/stdlib/statistics.pyi
@@ -3,7 +3,7 @@ from _typeshed import SupportsRichComparisonT
 from collections.abc import Callable, Hashable, Iterable, Sequence
 from decimal import Decimal
 from fractions import Fraction
-from typing import Any, Literal, NamedTuple, SupportsFloat, TypeVar
+from typing import Literal, NamedTuple, SupportsFloat, SupportsIndex, TypeVar
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -38,6 +38,9 @@ _NumberT = TypeVar("_NumberT", float, Decimal, Fraction)
 # Used in mode, multimode
 _HashableT = TypeVar("_HashableT", bound=Hashable)
 
+# Used in NormalDist.samples and kde_random
+_Seed: TypeAlias = int | float | str | bytes | bytearray  # noqa: Y041
+
 class StatisticsError(ValueError): ...
 
 if sys.version_info >= (3, 11):
@@ -89,7 +92,7 @@ class NormalDist:
     def variance(self) -> float: ...
     @classmethod
     def from_samples(cls, data: Iterable[SupportsFloat]) -> Self: ...
-    def samples(self, n: int, *, seed: Any | None = None) -> list[float]: ...
+    def samples(self, n: SupportsIndex, *, seed: _Seed | None = None) -> list[float]: ...
     def pdf(self, x: float) -> float: ...
     def cdf(self, x: float) -> float: ...
     def inv_cdf(self, p: float) -> float: ...
@@ -98,15 +101,15 @@ class NormalDist:
     if sys.version_info >= (3, 9):
         def zscore(self, x: float) -> float: ...
 
-    def __eq__(self, x2: object) -> bool: ...
-    def __add__(self, x2: float | NormalDist) -> NormalDist: ...
-    def __sub__(self, x2: float | NormalDist) -> NormalDist: ...
-    def __mul__(self, x2: float) -> NormalDist: ...
-    def __truediv__(self, x2: float) -> NormalDist: ...
-    def __pos__(self) -> NormalDist: ...
-    def __neg__(self) -> NormalDist: ...
+    def __eq__(x1, x2: object) -> bool: ...
+    def __add__(x1, x2: float | NormalDist) -> NormalDist: ...
+    def __sub__(x1, x2: float | NormalDist) -> NormalDist: ...
+    def __mul__(x1, x2: float) -> NormalDist: ...
+    def __truediv__(x1, x2: float) -> NormalDist: ...
+    def __pos__(x1) -> NormalDist: ...
+    def __neg__(x1) -> NormalDist: ...
     __radd__ = __add__
-    def __rsub__(self, x2: float | NormalDist) -> NormalDist: ...
+    def __rsub__(x1, x2: float | NormalDist) -> NormalDist: ...
     __rmul__ = __mul__
     def __hash__(self) -> int: ...
 
@@ -153,9 +156,5 @@ if sys.version_info >= (3, 13):
         data: Sequence[float], h: float, kernel: _Kernel = "normal", *, cumulative: bool = False
     ) -> Callable[[float], float]: ...
     def kde_random(
-        data: Sequence[float],
-        h: float,
-        kernel: _Kernel = "normal",
-        *,
-        seed: int | float | str | bytes | bytearray | None = None,  # noqa: Y041
+        data: Sequence[float], h: float, kernel: _Kernel = "normal", *, seed: _Seed | None = None
     ) -> Callable[[], float]: ...
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index 751de523bf7a..73c1e0400fe8 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -265,7 +265,7 @@ else:
         GraphicsExpose = "13"
         Gravity = "24"
         KeyPress = "2"
-        Key = "2"
+        Key = KeyPress
         KeyRelease = "3"
         Keymap = "11"
         Leave = "8"
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index 849db3ece938..542979d4afc5 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -687,6 +687,8 @@ if sys.version_info >= (3, 10):
     class UnionType:
         @property
         def __args__(self) -> tuple[Any, ...]: ...
+        @property
+        def __parameters__(self) -> tuple[Any, ...]: ...
         def __or__(self, value: Any, /) -> UnionType: ...
         def __ror__(self, value: Any, /) -> UnionType: ...
         def __eq__(self, value: object, /) -> bool: ...
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index 5875b6915762..bc8f342ef46b 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -130,8 +130,7 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 13):
     __all__ += ["get_protocol_members", "is_protocol", "NoDefault", "TypeIs", "ReadOnly"]
 
-Any = object()
-
+class Any: ...
 class _Final: ...
 
 def final(f: _T) -> _T: ...
@@ -950,6 +949,9 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta):
     # so we only add it to the stub on 3.12+
     if sys.version_info >= (3, 12):
         __orig_bases__: ClassVar[tuple[Any, ...]]
+    if sys.version_info >= (3, 13):
+        __readonly_keys__: ClassVar[frozenset[str]]
+        __mutable_keys__: ClassVar[frozenset[str]]
 
     def copy(self) -> typing_extensions.Self: ...
     # Using Never so that only calls using mypy plugin hook that specialize the signature
@@ -957,7 +959,7 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta):
     def setdefault(self, k: _Never, default: object) -> object: ...
     # Mypy plugin hook for 'pop' expects that 'default' has a type variable type.
     def pop(self, k: _Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
-    def update(self: _T, m: _T, /) -> None: ...
+    def update(self, m: typing_extensions.Self, /) -> None: ...
     def __delitem__(self, k: _Never) -> None: ...
     def items(self) -> dict_items[str, object]: ...
     def keys(self) -> dict_keys[str, object]: ...
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index fd98722b10a8..f3b7b8ddf5b1 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -1,9 +1,11 @@
 import abc
+import enum
 import sys
 import typing
 from _collections_abc import dict_items, dict_keys, dict_values
-from _typeshed import IdentityFunction
+from _typeshed import IdentityFunction, Incomplete, Unused
 from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager
+from types import ModuleType
 from typing import (  # noqa: Y022,Y037,Y038,Y039
     IO as IO,
     TYPE_CHECKING as TYPE_CHECKING,
@@ -68,9 +70,10 @@ if sys.version_info >= (3, 10):
 if sys.version_info >= (3, 9):
     from types import GenericAlias
 
+# Please keep order the same as at runtime.
 __all__ = [
+    # Super-special typing primitives.
     "Any",
-    "Buffer",
     "ClassVar",
     "Concatenate",
     "Final",
@@ -83,14 +86,16 @@ __all__ = [
     "TypeVar",
     "TypeVarTuple",
     "Unpack",
+    # ABCs (from collections.abc).
     "Awaitable",
     "AsyncIterator",
     "AsyncIterable",
     "Coroutine",
     "AsyncGenerator",
     "AsyncContextManager",
-    "CapsuleType",
+    "Buffer",
     "ChainMap",
+    # Concrete collection types.
     "ContextManager",
     "Counter",
     "Deque",
@@ -98,20 +103,34 @@ __all__ = [
     "NamedTuple",
     "OrderedDict",
     "TypedDict",
-    "SupportsIndex",
+    # Structural checks, a.k.a. protocols.
     "SupportsAbs",
-    "SupportsRound",
     "SupportsBytes",
     "SupportsComplex",
     "SupportsFloat",
+    "SupportsIndex",
     "SupportsInt",
+    "SupportsRound",
+    # One-off things.
     "Annotated",
     "assert_never",
     "assert_type",
+    "clear_overloads",
     "dataclass_transform",
     "deprecated",
+    "Doc",
+    "evaluate_forward_ref",
+    "get_overloads",
     "final",
+    "Format",
+    "get_annotations",
+    "get_args",
+    "get_origin",
+    "get_original_bases",
+    "get_protocol_members",
+    "get_type_hints",
     "IntVar",
+    "is_protocol",
     "is_typeddict",
     "Literal",
     "NewType",
@@ -124,18 +143,18 @@ __all__ = [
     "Text",
     "TypeAlias",
     "TypeAliasType",
+    "TypeForm",
     "TypeGuard",
+    "TypeIs",
     "TYPE_CHECKING",
     "Never",
     "NoReturn",
+    "ReadOnly",
     "Required",
     "NotRequired",
-    "clear_overloads",
-    "get_args",
-    "get_origin",
-    "get_original_bases",
-    "get_overloads",
-    "get_type_hints",
+    "NoDefault",
+    "NoExtraItems",
+    # Pure aliases, have always been in typing
     "AbstractSet",
     "AnyStr",
     "BinaryIO",
@@ -143,7 +162,6 @@ __all__ = [
     "Collection",
     "Container",
     "Dict",
-    "Doc",
     "ForwardRef",
     "FrozenSet",
     "Generator",
@@ -161,7 +179,6 @@ __all__ = [
     "MutableMapping",
     "MutableSequence",
     "MutableSet",
-    "NoDefault",
     "Optional",
     "Pattern",
     "Reversible",
@@ -173,12 +190,10 @@ __all__ = [
     "Union",
     "ValuesView",
     "cast",
-    "get_protocol_members",
-    "is_protocol",
     "no_type_check",
     "no_type_check_decorator",
-    "ReadOnly",
-    "TypeIs",
+    # Added dynamically
+    "CapsuleType",
 ]
 
 _T = typing.TypeVar("_T")
@@ -234,7 +249,7 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta):
     def setdefault(self, k: Never, default: object) -> object: ...
     # Mypy plugin hook for 'pop' expects that 'default' has a type variable type.
     def pop(self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
-    def update(self: _T, m: _T, /) -> None: ...
+    def update(self, m: Self, /) -> None: ...
     def items(self) -> dict_items[str, object]: ...
     def keys(self) -> dict_keys[str, object]: ...
     def values(self) -> dict_values[str, object]: ...
@@ -382,33 +397,11 @@ if sys.version_info >= (3, 12):
         SupportsIndex as SupportsIndex,
         SupportsInt as SupportsInt,
         SupportsRound as SupportsRound,
-        TypeAliasType as TypeAliasType,
         override as override,
     )
 else:
     def override(arg: _F, /) -> _F: ...
     def get_original_bases(cls: type, /) -> tuple[Any, ...]: ...
-    @final
-    class TypeAliasType:
-        def __init__(
-            self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
-        ) -> None: ...
-        @property
-        def __value__(self) -> Any: ...
-        @property
-        def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ...
-        @property
-        def __parameters__(self) -> tuple[Any, ...]: ...
-        @property
-        def __name__(self) -> str: ...
-        # It's writable on types, but not on instances of TypeAliasType.
-        @property
-        def __module__(self) -> str | None: ...  # type: ignore[override]
-        # Returns typing._GenericAlias, which isn't stubbed.
-        def __getitem__(self, parameters: Any) -> Any: ...
-        if sys.version_info >= (3, 10):
-            def __or__(self, right: Any) -> _SpecialForm: ...
-            def __ror__(self, left: Any) -> _SpecialForm: ...
 
     # mypy and pyright object to this being both ABC and Protocol.
     # At runtime it inherits from ABC and is not a Protocol, but it is on the
@@ -569,8 +562,71 @@ else:
     ReadOnly: _SpecialForm
     TypeIs: _SpecialForm
 
+# TypeAliasType was added in Python 3.12, but had significant changes in 3.14.
+if sys.version_info >= (3, 14):
+    from typing import TypeAliasType as TypeAliasType
+else:
+    @final
+    class TypeAliasType:
+        def __init__(
+            self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
+        ) -> None: ...  # value is a type expression
+        @property
+        def __value__(self) -> Any: ...  # a type expression
+        @property
+        def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ...
+        @property
+        # `__parameters__` can include special forms if a `TypeVarTuple` was
+        # passed as a `type_params` element to the constructor method.
+        def __parameters__(self) -> tuple[TypeVar | ParamSpec | Any, ...]: ...
+        @property
+        def __name__(self) -> str: ...
+        # It's writable on types, but not on instances of TypeAliasType.
+        @property
+        def __module__(self) -> str | None: ...  # type: ignore[override]
+        # Returns typing._GenericAlias, which isn't stubbed.
+        def __getitem__(self, parameters: Incomplete | tuple[Incomplete, ...]) -> Any: ...
+        def __init_subclass__(cls, *args: Unused, **kwargs: Unused) -> NoReturn: ...
+        if sys.version_info >= (3, 10):
+            def __or__(self, right: Any) -> _SpecialForm: ...
+            def __ror__(self, left: Any) -> _SpecialForm: ...
+
+# PEP 727
 class Doc:
     documentation: str
     def __init__(self, documentation: str, /) -> None: ...
     def __hash__(self) -> int: ...
     def __eq__(self, other: object) -> bool: ...
+
+# PEP 728
+class _NoExtraItemsType: ...
+
+NoExtraItems: _NoExtraItemsType
+
+# PEP 747
+TypeForm: _SpecialForm
+
+class Format(enum.IntEnum):
+    VALUE = 1
+    FORWARDREF = 2
+    STRING = 3
+
+# PEP 649/749
+def get_annotations(
+    obj: Callable[..., object] | type[object] | ModuleType,  # any callable, class, or module
+    *,
+    globals: Mapping[str, Any] | None = None,  # value types depend on the key
+    locals: Mapping[str, Any] | None = None,  # value types depend on the key
+    eval_str: bool = False,
+    format: Format = Format.VALUE,  # noqa: Y011
+) -> dict[str, Any]: ...  # values are type expressions
+def evaluate_forward_ref(
+    forward_ref: ForwardRef,
+    *,
+    owner: Callable[..., object] | type[object] | ModuleType | None = None,  # any callable, class, or module
+    globals: Mapping[str, Any] | None = None,  # value types depend on the key
+    locals: Mapping[str, Any] | None = None,  # value types depend on the key
+    type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
+    format: Format = Format.VALUE,  # noqa: Y011
+    _recursive_guard: Container[str] = ...,
+) -> Any: ...  # str if format is Format.STRING, otherwise a type expression
diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi
index 533a36817506..49c98cb07540 100644
--- a/mypy/typeshed/stdlib/warnings.pyi
+++ b/mypy/typeshed/stdlib/warnings.pyi
@@ -3,8 +3,8 @@ import sys
 from _warnings import warn as warn, warn_explicit as warn_explicit
 from collections.abc import Sequence
 from types import ModuleType, TracebackType
-from typing import Any, Generic, Literal, TextIO, TypeVar, overload
-from typing_extensions import LiteralString, TypeAlias
+from typing import Any, Generic, Literal, TextIO, overload
+from typing_extensions import LiteralString, TypeAlias, TypeVar
 
 __all__ = [
     "warn",
@@ -21,7 +21,8 @@ if sys.version_info >= (3, 13):
     __all__ += ["deprecated"]
 
 _T = TypeVar("_T")
-_W = TypeVar("_W", bound=list[WarningMessage] | None)
+_W_co = TypeVar("_W_co", bound=list[WarningMessage] | None, default=list[WarningMessage] | None, covariant=True)
+
 if sys.version_info >= (3, 14):
     _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"]
 else:
@@ -66,7 +67,7 @@ class WarningMessage:
         source: Any | None = None,
     ) -> None: ...
 
-class catch_warnings(Generic[_W]):
+class catch_warnings(Generic[_W_co]):
     if sys.version_info >= (3, 11):
         @overload
         def __init__(
@@ -92,7 +93,7 @@ class catch_warnings(Generic[_W]):
         ) -> None: ...
         @overload
         def __init__(
-            self: catch_warnings[list[WarningMessage] | None],
+            self,
             *,
             record: bool,
             module: ModuleType | None = None,
@@ -109,11 +110,9 @@ class catch_warnings(Generic[_W]):
             self: catch_warnings[list[WarningMessage]], *, record: Literal[True], module: ModuleType | None = None
         ) -> None: ...
         @overload
-        def __init__(
-            self: catch_warnings[list[WarningMessage] | None], *, record: bool, module: ModuleType | None = None
-        ) -> None: ...
+        def __init__(self, *, record: bool, module: ModuleType | None = None) -> None: ...
 
-    def __enter__(self) -> _W: ...
+    def __enter__(self) -> _W_co: ...
     def __exit__(
         self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
     ) -> None: ...

From 4f284a3eb390d77f9d69a24c7c2a24095063ffc8 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 4 Apr 2025 20:36:43 +0100
Subject: [PATCH 1235/1617] Add shared checker interface to break import cycle
 (#18878)

The import cycle is not a real one as `if TYPE_CHECKING: ...` is used,
but it would become much bigger if I start using `checkmember` in
`subtypes`, essentially it would be one huge import cycle during
self-checking. So I decided to do something similar we did for semantic
analyzer.

---------

Co-authored-by: Ivan Levkivskyi 
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypy/checker.py        |  91 ++---------
 mypy/checker_shared.py | 349 +++++++++++++++++++++++++++++++++++++++++
 mypy/checkexpr.py      |   5 +-
 mypy/checkmember.py    |  14 +-
 mypy/checkpattern.py   |  10 +-
 mypy/checkstrformat.py |  37 ++---
 6 files changed, 384 insertions(+), 122 deletions(-)
 create mode 100644 mypy/checker_shared.py

diff --git a/mypy/checker.py b/mypy/checker.py
index 1b10710118df..7d0b41c516e1 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -12,6 +12,7 @@
 import mypy.checkexpr
 from mypy import errorcodes as codes, join, message_registry, nodes, operators
 from mypy.binder import ConditionalTypeBinder, Frame, get_declaration
+from mypy.checker_shared import CheckerScope, TypeCheckerSharedApi, TypeRange
 from mypy.checkmember import (
     MemberContext,
     analyze_class_attribute_access,
@@ -126,7 +127,7 @@
 from mypy.operators import flip_ops, int_op_to_method, neg_ops
 from mypy.options import PRECISE_TUPLE_TYPES, Options
 from mypy.patterns import AsPattern, StarredPattern
-from mypy.plugin import CheckerPluginInterface, Plugin
+from mypy.plugin import Plugin
 from mypy.plugins import dataclasses as dataclasses_plugin
 from mypy.scope import Scope
 from mypy.semanal import is_trivial_body, refers_to_fullname, set_callable_name
@@ -258,13 +259,6 @@ class FineGrainedDeferredNode(NamedTuple):
 TypeMap: _TypeAlias = Optional[dict[Expression, Type]]
 
 
-# An object that represents either a precise type or a type with an upper bound;
-# it is important for correct type inference with isinstance.
-class TypeRange(NamedTuple):
-    item: Type
-    is_upper_bound: bool  # False => precise type
-
-
 # Keeps track of partial types in a single scope. In fine-grained incremental
 # mode partial types initially defined at the top level cannot be completed in
 # a function, and we use the 'is_function' attribute to enforce this.
@@ -274,7 +268,7 @@ class PartialTypeScope(NamedTuple):
     is_local: bool
 
 
-class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
+class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi):
     """Mypy type checker.
 
     Type check mypy source files that have been semantically analyzed.
@@ -301,7 +295,7 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
     # Helper for managing conditional types
     binder: ConditionalTypeBinder
     # Helper for type checking expressions
-    expr_checker: mypy.checkexpr.ExpressionChecker
+    _expr_checker: mypy.checkexpr.ExpressionChecker
 
     pattern_checker: PatternChecker
 
@@ -416,14 +410,18 @@ def __init__(
         self.allow_abstract_call = False
 
         # Child checker objects for specific AST node types
-        self.expr_checker = mypy.checkexpr.ExpressionChecker(
+        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, options)
 
+    @property
+    def expr_checker(self) -> mypy.checkexpr.ExpressionChecker:
+        return self._expr_checker
+
     @property
     def type_context(self) -> list[Type | None]:
-        return self.expr_checker.type_context
+        return self._expr_checker.type_context
 
     def reset(self) -> None:
         """Cleanup stale state that might be left over from a typechecking run.
@@ -8527,75 +8525,6 @@ def is_node_static(node: Node | None) -> bool | None:
     return None
 
 
-class CheckerScope:
-    # We keep two stacks combined, to maintain the relative order
-    stack: list[TypeInfo | FuncItem | MypyFile]
-
-    def __init__(self, module: MypyFile) -> None:
-        self.stack = [module]
-
-    def current_function(self) -> FuncItem | None:
-        for e in reversed(self.stack):
-            if isinstance(e, FuncItem):
-                return e
-        return None
-
-    def top_level_function(self) -> FuncItem | None:
-        """Return top-level non-lambda function."""
-        for e in self.stack:
-            if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr):
-                return e
-        return None
-
-    def active_class(self) -> TypeInfo | None:
-        if isinstance(self.stack[-1], TypeInfo):
-            return self.stack[-1]
-        return None
-
-    def enclosing_class(self, func: FuncItem | None = None) -> TypeInfo | None:
-        """Is there a class *directly* enclosing this function?"""
-        func = func or self.current_function()
-        assert func, "This method must be called from inside a function"
-        index = self.stack.index(func)
-        assert index, "CheckerScope stack must always start with a module"
-        enclosing = self.stack[index - 1]
-        if isinstance(enclosing, TypeInfo):
-            return enclosing
-        return None
-
-    def active_self_type(self) -> Instance | TupleType | None:
-        """An instance or tuple type representing the current class.
-
-        This returns None unless we are in class body or in a method.
-        In particular, inside a function nested in method this returns None.
-        """
-        info = self.active_class()
-        if not info and self.current_function():
-            info = self.enclosing_class()
-        if info:
-            return fill_typevars(info)
-        return None
-
-    def current_self_type(self) -> Instance | TupleType | None:
-        """Same as active_self_type() but handle functions nested in methods."""
-        for item in reversed(self.stack):
-            if isinstance(item, TypeInfo):
-                return fill_typevars(item)
-        return None
-
-    @contextmanager
-    def push_function(self, item: FuncItem) -> Iterator[None]:
-        self.stack.append(item)
-        yield
-        self.stack.pop()
-
-    @contextmanager
-    def push_class(self, info: TypeInfo) -> Iterator[None]:
-        self.stack.append(info)
-        yield
-        self.stack.pop()
-
-
 TKey = TypeVar("TKey")
 TValue = TypeVar("TValue")
 
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
new file mode 100644
index 000000000000..6c62af50466c
--- /dev/null
+++ b/mypy/checker_shared.py
@@ -0,0 +1,349 @@
+"""Shared definitions used by different parts of type checker."""
+
+from __future__ import annotations
+
+from abc import abstractmethod
+from collections.abc import Iterator, Sequence
+from contextlib import contextmanager
+from typing import NamedTuple, overload
+
+from mypy_extensions import trait
+
+from mypy.errorcodes import ErrorCode
+from mypy.errors import ErrorWatcher
+from mypy.message_registry import ErrorMessage
+from mypy.nodes import (
+    ArgKind,
+    Context,
+    Expression,
+    FuncItem,
+    LambdaExpr,
+    MypyFile,
+    Node,
+    RefExpr,
+    TypeAlias,
+    TypeInfo,
+    Var,
+)
+from mypy.plugin import CheckerPluginInterface, Plugin
+from mypy.types import (
+    CallableType,
+    Instance,
+    LiteralValue,
+    Overloaded,
+    PartialType,
+    TupleType,
+    Type,
+    TypedDictType,
+    TypeType,
+)
+from mypy.typevars import fill_typevars
+
+
+# An object that represents either a precise type or a type with an upper bound;
+# it is important for correct type inference with isinstance.
+class TypeRange(NamedTuple):
+    item: Type
+    is_upper_bound: bool  # False => precise type
+
+
+@trait
+class ExpressionCheckerSharedApi:
+    @abstractmethod
+    def accept(
+        self,
+        node: Expression,
+        type_context: Type | None = None,
+        allow_none_return: bool = False,
+        always_allow_any: bool = False,
+        is_callee: bool = False,
+    ) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    def module_type(self, node: MypyFile) -> Instance:
+        raise NotImplementedError
+
+    @abstractmethod
+    def check_call(
+        self,
+        callee: Type,
+        args: list[Expression],
+        arg_kinds: list[ArgKind],
+        context: Context,
+        arg_names: Sequence[str | None] | None = None,
+        callable_node: Expression | None = None,
+        callable_name: str | None = None,
+        object_type: Type | None = None,
+        original_type: Type | None = None,
+    ) -> tuple[Type, Type]:
+        raise NotImplementedError
+
+    @abstractmethod
+    def transform_callee_type(
+        self,
+        callable_name: str | None,
+        callee: Type,
+        args: list[Expression],
+        arg_kinds: list[ArgKind],
+        context: Context,
+        arg_names: Sequence[str | None] | None = None,
+        object_type: Type | None = None,
+    ) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    def method_fullname(self, object_type: Type, method_name: str) -> str | None:
+        raise NotImplementedError
+
+    @abstractmethod
+    def check_method_call_by_name(
+        self,
+        method: str,
+        base_type: Type,
+        args: list[Expression],
+        arg_kinds: list[ArgKind],
+        context: Context,
+        original_type: Type | None = None,
+    ) -> tuple[Type, Type]:
+        raise NotImplementedError
+
+    @abstractmethod
+    def alias_type_in_runtime_context(
+        self, alias: TypeAlias, *, ctx: Context, alias_definition: bool = False
+    ) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    def visit_typeddict_index_expr(
+        self, td_type: TypedDictType, index: Expression, setitem: bool = False
+    ) -> tuple[Type, set[str]]:
+        raise NotImplementedError
+
+    @abstractmethod
+    def typeddict_callable(self, info: TypeInfo) -> CallableType:
+        raise NotImplementedError
+
+    @abstractmethod
+    def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type:
+        raise NotImplementedError
+
+
+@trait
+class TypeCheckerSharedApi(CheckerPluginInterface):
+    plugin: Plugin
+    module_refs: set[str]
+    scope: CheckerScope
+    checking_missing_await: bool
+
+    @property
+    @abstractmethod
+    def expr_checker(self) -> ExpressionCheckerSharedApi:
+        raise NotImplementedError
+
+    @abstractmethod
+    def named_type(self, name: str) -> Instance:
+        raise NotImplementedError
+
+    @abstractmethod
+    def lookup_typeinfo(self, fullname: str) -> TypeInfo:
+        raise NotImplementedError
+
+    @abstractmethod
+    def lookup_type(self, node: Expression) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    def handle_cannot_determine_type(self, name: str, context: Context) -> None:
+        raise NotImplementedError
+
+    @abstractmethod
+    def handle_partial_var_type(
+        self, typ: PartialType, is_lvalue: bool, node: Var, context: Context
+    ) -> Type:
+        raise NotImplementedError
+
+    @overload
+    @abstractmethod
+    def check_subtype(
+        self,
+        subtype: Type,
+        supertype: Type,
+        context: Context,
+        msg: str,
+        subtype_label: str | None = None,
+        supertype_label: str | None = None,
+        *,
+        notes: list[str] | None = None,
+        code: ErrorCode | None = None,
+        outer_context: Context | None = None,
+    ) -> bool: ...
+
+    @overload
+    @abstractmethod
+    def check_subtype(
+        self,
+        subtype: Type,
+        supertype: Type,
+        context: Context,
+        msg: ErrorMessage,
+        subtype_label: str | None = None,
+        supertype_label: str | None = None,
+        *,
+        notes: list[str] | None = None,
+        outer_context: Context | None = None,
+    ) -> bool: ...
+
+    # Unfortunately, mypyc doesn't support abstract overloads yet.
+    @abstractmethod
+    def check_subtype(
+        self,
+        subtype: Type,
+        supertype: Type,
+        context: Context,
+        msg: str | ErrorMessage,
+        subtype_label: str | None = None,
+        supertype_label: str | None = None,
+        *,
+        notes: list[str] | None = None,
+        code: ErrorCode | None = None,
+        outer_context: Context | None = None,
+    ) -> bool:
+        raise NotImplementedError
+
+    @abstractmethod
+    def get_final_context(self) -> bool:
+        raise NotImplementedError
+
+    @overload
+    @abstractmethod
+    def conditional_types_with_intersection(
+        self,
+        expr_type: Type,
+        type_ranges: list[TypeRange] | None,
+        ctx: Context,
+        default: None = None,
+    ) -> tuple[Type | None, Type | None]: ...
+
+    @overload
+    @abstractmethod
+    def conditional_types_with_intersection(
+        self, expr_type: Type, type_ranges: list[TypeRange] | None, ctx: Context, default: Type
+    ) -> tuple[Type, Type]: ...
+
+    # Unfortunately, mypyc doesn't support abstract overloads yet.
+    @abstractmethod
+    def conditional_types_with_intersection(
+        self,
+        expr_type: Type,
+        type_ranges: list[TypeRange] | None,
+        ctx: Context,
+        default: Type | None = None,
+    ) -> tuple[Type | None, Type | None]:
+        raise NotImplementedError
+
+    @abstractmethod
+    def check_deprecated(self, node: Node | None, context: Context) -> None:
+        raise NotImplementedError
+
+    @abstractmethod
+    def warn_deprecated(self, node: Node | None, context: Context) -> None:
+        raise NotImplementedError
+
+    @abstractmethod
+    def warn_deprecated_overload_item(
+        self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None
+    ) -> None:
+        raise NotImplementedError
+
+    @abstractmethod
+    def type_is_iterable(self, type: Type) -> bool:
+        raise NotImplementedError
+
+    @abstractmethod
+    def iterable_item_type(
+        self, it: Instance | CallableType | TypeType | Overloaded, context: Context
+    ) -> Type:
+        raise NotImplementedError
+
+    @abstractmethod
+    @contextmanager
+    def checking_await_set(self) -> Iterator[None]:
+        raise NotImplementedError
+
+    @abstractmethod
+    def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None:
+        raise NotImplementedError
+
+
+class CheckerScope:
+    # We keep two stacks combined, to maintain the relative order
+    stack: list[TypeInfo | FuncItem | MypyFile]
+
+    def __init__(self, module: MypyFile) -> None:
+        self.stack = [module]
+
+    def current_function(self) -> FuncItem | None:
+        for e in reversed(self.stack):
+            if isinstance(e, FuncItem):
+                return e
+        return None
+
+    def top_level_function(self) -> FuncItem | None:
+        """Return top-level non-lambda function."""
+        for e in self.stack:
+            if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr):
+                return e
+        return None
+
+    def active_class(self) -> TypeInfo | None:
+        if isinstance(self.stack[-1], TypeInfo):
+            return self.stack[-1]
+        return None
+
+    def enclosing_class(self, func: FuncItem | None = None) -> TypeInfo | None:
+        """Is there a class *directly* enclosing this function?"""
+        func = func or self.current_function()
+        assert func, "This method must be called from inside a function"
+        index = self.stack.index(func)
+        assert index, "CheckerScope stack must always start with a module"
+        enclosing = self.stack[index - 1]
+        if isinstance(enclosing, TypeInfo):
+            return enclosing
+        return None
+
+    def active_self_type(self) -> Instance | TupleType | None:
+        """An instance or tuple type representing the current class.
+
+        This returns None unless we are in class body or in a method.
+        In particular, inside a function nested in method this returns None.
+        """
+        info = self.active_class()
+        if not info and self.current_function():
+            info = self.enclosing_class()
+        if info:
+            return fill_typevars(info)
+        return None
+
+    def current_self_type(self) -> Instance | TupleType | None:
+        """Same as active_self_type() but handle functions nested in methods."""
+        for item in reversed(self.stack):
+            if isinstance(item, TypeInfo):
+                return fill_typevars(item)
+        return None
+
+    @contextmanager
+    def push_function(self, item: FuncItem) -> Iterator[None]:
+        self.stack.append(item)
+        yield
+        self.stack.pop()
+
+    @contextmanager
+    def push_class(self, info: TypeInfo) -> Iterator[None]:
+        self.stack.append(info)
+        yield
+        self.stack.pop()
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 12480cf9ab93..099e151dd33d 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -15,6 +15,7 @@
 import mypy.errorcodes as codes
 from mypy import applytype, erasetype, join, message_registry, nodes, operators, types
 from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals
+from mypy.checker_shared import ExpressionCheckerSharedApi
 from mypy.checkmember import analyze_member_access
 from mypy.checkstrformat import StringFormatterChecker
 from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
@@ -296,7 +297,7 @@ class UseReverse(enum.Enum):
 USE_REVERSE_NEVER: Final = UseReverse.NEVER
 
 
-class ExpressionChecker(ExpressionVisitor[Type]):
+class ExpressionChecker(ExpressionVisitor[Type], ExpressionCheckerSharedApi):
     """Expression type checker.
 
     This class works closely together with checker.TypeChecker.
@@ -338,7 +339,7 @@ def __init__(
         # TODO: refactor this to use a pattern similar to one in
         # multiassign_from_union, or maybe even combine the two?
         self.type_overrides: dict[Expression, Type] = {}
-        self.strfrm_checker = StringFormatterChecker(self, self.chk, self.msg)
+        self.strfrm_checker = StringFormatterChecker(self.chk, self.msg)
 
         self.resolved_type = {}
 
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index dfb141aa415c..2152e309b1df 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -3,9 +3,10 @@
 from __future__ import annotations
 
 from collections.abc import Sequence
-from typing import TYPE_CHECKING, Callable, cast
+from typing import Callable, cast
 
-from mypy import message_registry, subtypes
+from mypy import message_registry, state, subtypes
+from mypy.checker_shared import TypeCheckerSharedApi
 from mypy.erasetype import erase_typevars
 from mypy.expandtype import (
     expand_self_type,
@@ -73,11 +74,6 @@
     get_proper_type,
 )
 
-if TYPE_CHECKING:  # import for forward declaration only
-    import mypy.checker
-
-from mypy import state
-
 
 class MemberContext:
     """Information and objects needed to type check attribute access.
@@ -93,7 +89,7 @@ def __init__(
         is_operator: bool,
         original_type: Type,
         context: Context,
-        chk: mypy.checker.TypeChecker,
+        chk: TypeCheckerSharedApi,
         self_type: Type | None = None,
         module_symbol_table: SymbolTable | None = None,
         no_deferral: bool = False,
@@ -165,7 +161,7 @@ def analyze_member_access(
     is_super: bool,
     is_operator: bool,
     original_type: Type,
-    chk: mypy.checker.TypeChecker,
+    chk: TypeCheckerSharedApi,
     override_info: TypeInfo | None = None,
     in_literal_context: bool = False,
     self_type: Type | None = None,
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index c71d83324694..4cf7c1ca7862 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -5,8 +5,8 @@
 from collections import defaultdict
 from typing import Final, NamedTuple
 
-import mypy.checker
 from mypy import message_registry
+from mypy.checker_shared import TypeCheckerSharedApi, TypeRange
 from mypy.checkmember import analyze_member_access
 from mypy.expandtype import expand_type_by_instance
 from mypy.join import join_types
@@ -91,7 +91,7 @@ class PatternChecker(PatternVisitor[PatternType]):
     """
 
     # Some services are provided by a TypeChecker instance.
-    chk: mypy.checker.TypeChecker
+    chk: TypeCheckerSharedApi
     # This is shared with TypeChecker, but stored also here for convenience.
     msg: MessageBuilder
     # Currently unused
@@ -112,7 +112,7 @@ class PatternChecker(PatternVisitor[PatternType]):
     options: Options
 
     def __init__(
-        self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin, options: Options
+        self, chk: TypeCheckerSharedApi, msg: MessageBuilder, plugin: Plugin, options: Options
     ) -> None:
         self.chk = chk
         self.msg = msg
@@ -802,7 +802,7 @@ def get_var(expr: Expression) -> Var:
     return node
 
 
-def get_type_range(typ: Type) -> mypy.checker.TypeRange:
+def get_type_range(typ: Type) -> TypeRange:
     typ = get_proper_type(typ)
     if (
         isinstance(typ, Instance)
@@ -810,7 +810,7 @@ def get_type_range(typ: Type) -> mypy.checker.TypeRange:
         and isinstance(typ.last_known_value.value, bool)
     ):
         typ = typ.last_known_value
-    return mypy.checker.TypeRange(typ, is_upper_bound=False)
+    return TypeRange(typ, is_upper_bound=False)
 
 
 def is_uninhabited(typ: Type) -> bool:
diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py
index 289961523b1d..45075bd37552 100644
--- a/mypy/checkstrformat.py
+++ b/mypy/checkstrformat.py
@@ -14,11 +14,15 @@
 
 import re
 from re import Match, Pattern
-from typing import TYPE_CHECKING, Callable, Final, Union, cast
+from typing import Callable, Final, Union, cast
 from typing_extensions import TypeAlias as _TypeAlias
 
 import mypy.errorcodes as codes
+from mypy import message_registry
+from mypy.checker_shared import TypeCheckerSharedApi
 from mypy.errors import Errors
+from mypy.maptype import map_instance_to_supertype
+from mypy.messages import MessageBuilder
 from mypy.nodes import (
     ARG_NAMED,
     ARG_POS,
@@ -41,6 +45,9 @@
     TempNode,
     TupleExpr,
 )
+from mypy.parse import parse
+from mypy.subtypes import is_subtype
+from mypy.typeops import custom_special_method
 from mypy.types import (
     AnyType,
     Instance,
@@ -57,18 +64,6 @@
     get_proper_types,
 )
 
-if TYPE_CHECKING:
-    # break import cycle only needed for mypy
-    import mypy.checker
-    import mypy.checkexpr
-
-from mypy import message_registry
-from mypy.maptype import map_instance_to_supertype
-from mypy.messages import MessageBuilder
-from mypy.parse import parse
-from mypy.subtypes import is_subtype
-from mypy.typeops import custom_special_method
-
 FormatStringExpr: _TypeAlias = Union[StrExpr, BytesExpr]
 Checkers: _TypeAlias = tuple[Callable[[Expression], None], Callable[[Type], bool]]
 MatchMap: _TypeAlias = dict[tuple[int, int], Match[str]]  # span -> match
@@ -299,21 +294,13 @@ class StringFormatterChecker:
     """
 
     # Some services are provided by a TypeChecker instance.
-    chk: mypy.checker.TypeChecker
+    chk: TypeCheckerSharedApi
     # This is shared with TypeChecker, but stored also here for convenience.
     msg: MessageBuilder
-    # Some services are provided by a ExpressionChecker instance.
-    exprchk: mypy.checkexpr.ExpressionChecker
 
-    def __init__(
-        self,
-        exprchk: mypy.checkexpr.ExpressionChecker,
-        chk: mypy.checker.TypeChecker,
-        msg: MessageBuilder,
-    ) -> None:
+    def __init__(self, chk: TypeCheckerSharedApi, msg: MessageBuilder) -> None:
         """Construct an expression type checker."""
         self.chk = chk
-        self.exprchk = exprchk
         self.msg = msg
 
     def check_str_format_call(self, call: CallExpr, format_value: str) -> None:
@@ -618,7 +605,7 @@ def apply_field_accessors(
         # TODO: fix column to point to actual start of the format specifier _within_ string.
         temp_ast.line = ctx.line
         temp_ast.column = ctx.column
-        self.exprchk.accept(temp_ast)
+        self.chk.expr_checker.accept(temp_ast)
         return temp_ast
 
     def validate_and_transform_accessors(
@@ -685,7 +672,7 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi
         """Check the types of the 'replacements' in a string interpolation
         expression: str % replacements.
         """
-        self.exprchk.accept(expr)
+        self.chk.expr_checker.accept(expr)
         specifiers = parse_conversion_specifiers(expr.value)
         has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr)
         if has_mapping_keys is None:

From e867132134c7b8046ebae2d6e1fa9fc184b9e9d7 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 5 Apr 2025 01:06:29 +0100
Subject: [PATCH 1236/1617] Allow omitting implementation for abstract
 overloads (#18882)

Fixes https://github.com/python/mypy/issues/11488

This is a little quality of life improvement. Implementation is
straightforward. I also update mypyc to give an error instead of
crashing.
---
 mypy/checkmember.py                 |  3 +++
 mypy/semanal.py                     |  9 ++++++++-
 mypyc/irbuild/prepare.py            |  8 ++++++--
 test-data/unit/check-functions.test | 25 +++++++++++++++++++++++++
 4 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 2152e309b1df..1a76372d4731 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -380,6 +380,9 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None:
         if node.impl:
             impl = node.impl if isinstance(node.impl, FuncDef) else node.impl.func
             unsafe_super = impl.is_trivial_body
+        elif not node.is_property and node.items:
+            assert isinstance(node.items[0], Decorator)
+            unsafe_super = node.items[0].func.is_trivial_body
     if unsafe_super:
         mx.msg.unsafe_super(node.name, node.info.name, mx.context)
 
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 60d4f1bde9f8..6d0a62070c8e 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1461,8 +1461,15 @@ def handle_missing_overload_implementation(self, defn: OverloadedFuncDef) -> Non
                         item.func.abstract_status = IS_ABSTRACT
                     else:
                         item.abstract_status = IS_ABSTRACT
+            elif all(
+                isinstance(item, Decorator) and item.func.abstract_status == IS_ABSTRACT
+                for item in defn.items
+            ):
+                # Since there is no implementation, it can't be called via super().
+                if defn.items:
+                    assert isinstance(defn.items[0], Decorator)
+                    defn.items[0].func.is_trivial_body = True
             else:
-                # TODO: also allow omitting an implementation for abstract methods in ABCs?
                 self.fail(
                     "An overloaded function outside a stub file must have an implementation",
                     defn,
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index e014d97fedd9..98ff348d8c30 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -382,8 +382,12 @@ def prepare_methods_and_attributes(
 
             # Handle case for regular function overload
             else:
-                assert node.node.impl
-                prepare_method_def(ir, module_name, cdef, mapper, node.node.impl, options)
+                if not node.node.impl:
+                    errors.error(
+                        "Overloads without implementation are not supported", path, cdef.line
+                    )
+                else:
+                    prepare_method_def(ir, module_name, cdef, mapper, node.node.impl, options)
 
     if ir.builtin_base:
         ir.attributes.clear()
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index 8f48d50fc8ec..bd59dfbdfd5e 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3541,3 +3541,28 @@ def f(x: Callable[[Arg(int, 'x')], None]) -> None: pass
 y: Callable[[Union[int, str]], None]
 f(y)  # E: Argument 1 to "f" has incompatible type "Callable[[Union[int, str]], None]"; expected "Callable[[Arg(int, 'x')], None]"
 [builtins fixtures/tuple.pyi]
+
+[case testAbstractOverloadsWithoutImplementationAllowed]
+from abc import abstractmethod
+from typing import overload, Union
+
+class Foo:
+    @overload
+    @abstractmethod
+    def foo(self, value: int) -> int:
+        ...
+    @overload
+    @abstractmethod
+    def foo(self, value: str) -> str:
+        ...
+
+class Bar(Foo):
+    @overload
+    def foo(self, value: int) -> int:
+        ...
+    @overload
+    def foo(self, value: str) -> str:
+        ...
+
+    def foo(self, value: Union[int, str]) -> Union[int, str]:
+        return super().foo(value)  # E: Call to abstract method "foo" of "Foo" with trivial body via super() is unsafe

From 67b70ce00ee9f2da1c49e75a044aad7ab7c137ff Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 5 Apr 2025 03:40:47 +0200
Subject: [PATCH 1237/1617] Treat `TypedDict` (old-style) aliases as regular
 `TypedDict`s (#18852)

Fixes #18692.

This PR makes mypy recognize old-style aliases to TypedDict types:

```python
Alias = SomeTypedDict
ExplicitAlias: TypeAlias = SomeTypedDict
```

Still doesn't support generic no_args aliases:

```python
from typing import Generic, TypedDict, TypeVar

_T = TypeVar("_T")

class TD(TypedDict, Generic[_T]):
    foo: _T

Alias = TD
# but works with
OtherAlias = TD[_T]
```

that's because `no_args` aliases are handled in code in several places
and all of them expect such an alias to have `Instance` target.
---
 mypy/semanal_typeddict.py           |  71 ++++++++++---------
 test-data/unit/check-typeddict.test | 103 ++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 31 deletions(-)

diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py
index 0d6a0b7ff87f..8bf073d30f71 100644
--- a/mypy/semanal_typeddict.py
+++ b/mypy/semanal_typeddict.py
@@ -30,6 +30,7 @@
     StrExpr,
     TempNode,
     TupleExpr,
+    TypeAlias,
     TypedDictExpr,
     TypeInfo,
 )
@@ -50,6 +51,7 @@
     TypedDictType,
     TypeOfAny,
     TypeVarLikeType,
+    get_proper_type,
 )
 
 TPDICT_CLASS_ERROR: Final = (
@@ -137,23 +139,18 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N
                     typeddict_bases_set.add("TypedDict")
                 else:
                     self.fail('Duplicate base class "TypedDict"', defn)
-            elif isinstance(expr, RefExpr) and self.is_typeddict(expr):
-                assert expr.fullname
-                if expr.fullname not in typeddict_bases_set:
-                    typeddict_bases_set.add(expr.fullname)
+            elif (
+                isinstance(expr, RefExpr)
+                and self.is_typeddict(expr)
+                or isinstance(expr, IndexExpr)
+                and self.is_typeddict(expr.base)
+            ):
+                info = self._parse_typeddict_base(expr, defn)
+                if info.fullname not in typeddict_bases_set:
+                    typeddict_bases_set.add(info.fullname)
                     typeddict_bases.append(expr)
                 else:
-                    assert isinstance(expr.node, TypeInfo)
-                    self.fail(f'Duplicate base class "{expr.node.name}"', defn)
-            elif isinstance(expr, IndexExpr) and self.is_typeddict(expr.base):
-                assert isinstance(expr.base, RefExpr)
-                assert expr.base.fullname
-                if expr.base.fullname not in typeddict_bases_set:
-                    typeddict_bases_set.add(expr.base.fullname)
-                    typeddict_bases.append(expr)
-                else:
-                    assert isinstance(expr.base.node, TypeInfo)
-                    self.fail(f'Duplicate base class "{expr.base.node.name}"', defn)
+                    self.fail(f'Duplicate base class "{info.name}"', defn)
             else:
                 self.fail("All bases of a new TypedDict must be TypedDict types", defn)
 
@@ -190,22 +187,13 @@ def add_keys_and_types_from_base(
         readonly_keys: set[str],
         ctx: Context,
     ) -> None:
+        info = self._parse_typeddict_base(base, ctx)
         base_args: list[Type] = []
-        if isinstance(base, RefExpr):
-            assert isinstance(base.node, TypeInfo)
-            info = base.node
-        elif isinstance(base, IndexExpr):
-            assert isinstance(base.base, RefExpr)
-            assert isinstance(base.base.node, TypeInfo)
-            info = base.base.node
+        if isinstance(base, IndexExpr):
             args = self.analyze_base_args(base, ctx)
             if args is None:
                 return
             base_args = args
-        else:
-            assert isinstance(base, CallExpr)
-            assert isinstance(base.analyzed, TypedDictExpr)
-            info = base.analyzed.info
 
         assert info.typeddict_type is not None
         base_typed_dict = info.typeddict_type
@@ -231,6 +219,26 @@ def add_keys_and_types_from_base(
         required_keys.update(base_typed_dict.required_keys)
         readonly_keys.update(base_typed_dict.readonly_keys)
 
+    def _parse_typeddict_base(self, base: Expression, ctx: Context) -> TypeInfo:
+        if isinstance(base, RefExpr):
+            if isinstance(base.node, TypeInfo):
+                return base.node
+            elif isinstance(base.node, TypeAlias):
+                # Only old TypeAlias / plain assignment, PEP695 `type` stmt
+                # cannot be used as a base class
+                target = get_proper_type(base.node.target)
+                assert isinstance(target, TypedDictType)
+                return target.fallback.type
+            else:
+                assert False
+        elif isinstance(base, IndexExpr):
+            assert isinstance(base.base, RefExpr)
+            return self._parse_typeddict_base(base.base, ctx)
+        else:
+            assert isinstance(base, CallExpr)
+            assert isinstance(base.analyzed, TypedDictExpr)
+            return base.analyzed.info
+
     def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
         """Analyze arguments of base type expressions as types.
 
@@ -527,7 +535,7 @@ def parse_typeddict_args(
                 return "", [], [], True, [], False
         dictexpr = args[1]
         tvar_defs = self.api.get_and_bind_all_tvars([t for k, t in dictexpr.items])
-        res = self.parse_typeddict_fields_with_types(dictexpr.items, call)
+        res = self.parse_typeddict_fields_with_types(dictexpr.items)
         if res is None:
             # One of the types is not ready, defer.
             return None
@@ -536,7 +544,7 @@ def parse_typeddict_args(
         return args[0].value, items, types, total, tvar_defs, ok
 
     def parse_typeddict_fields_with_types(
-        self, dict_items: list[tuple[Expression | None, Expression]], context: Context
+        self, dict_items: list[tuple[Expression | None, Expression]]
     ) -> tuple[list[str], list[Type], bool] | None:
         """Parse typed dict items passed as pairs (name expression, type expression).
 
@@ -609,10 +617,11 @@ def build_typeddict_typeinfo(
     # Helpers
 
     def is_typeddict(self, expr: Expression) -> bool:
-        return (
-            isinstance(expr, RefExpr)
-            and isinstance(expr.node, TypeInfo)
+        return isinstance(expr, RefExpr) and (
+            isinstance(expr.node, TypeInfo)
             and expr.node.typeddict_type is not None
+            or isinstance(expr.node, TypeAlias)
+            and isinstance(get_proper_type(expr.node.target), TypedDictType)
         )
 
     def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None:
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index 48bfa4bdba49..47c8a71ba0e3 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -4151,3 +4151,106 @@ class Base:
                                         # E: TypedDict() expects a dictionary literal as the second argument
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAlias]
+from typing import NotRequired, TypedDict
+from typing_extensions import TypeAlias
+
+class Base(TypedDict):
+    foo: int
+
+Base1 = Base
+class Child1(Base1):
+    bar: NotRequired[int]
+c11: Child1 = {"foo": 0}
+c12: Child1 = {"foo": 0, "bar": 1}
+c13: Child1 = {"foo": 0, "bar": 1, "baz": "error"}  # E: Extra key "baz" for TypedDict "Child1"
+
+Base2: TypeAlias = Base
+class Child2(Base2):
+    bar: NotRequired[int]
+c21: Child2 = {"foo": 0}
+c22: Child2 = {"foo": 0, "bar": 1}
+c23: Child2 = {"foo": 0, "bar": 1, "baz": "error"}  # E: Extra key "baz" for TypedDict "Child2"
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAliasInheritance]
+from typing import TypedDict
+from typing_extensions import TypeAlias
+
+class A(TypedDict):
+    x: str
+class B(TypedDict):
+    y: int
+
+B1 = B
+B2: TypeAlias = B
+
+class C(A, B1):
+    pass
+c1: C = {"y": 1}  # E: Missing key "x" for TypedDict "C"
+c2: C = {"x": "x", "y": 2}
+c3: C = {"x": 1, "y": 2}  # E: Incompatible types (expression has type "int", TypedDict item "x" has type "str")
+
+class D(A, B2):
+    pass
+d1: D = {"y": 1}  # E: Missing key "x" for TypedDict "D"
+d2: D = {"x": "x", "y": 2}
+d3: D = {"x": 1, "y": 2}  # E: Incompatible types (expression has type "int", TypedDict item "x" has type "str")
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAliasDuplicateBases]
+from typing import TypedDict
+from typing_extensions import TypeAlias
+
+class A(TypedDict):
+    x: str
+
+A1 = A
+A2 = A
+A3: TypeAlias = A
+
+class E(A1, A2): pass  # E: Duplicate base class "A"
+class F(A1, A3): pass # E: Duplicate base class "A"
+class G(A, A1): pass # E: Duplicate base class "A"
+
+class H(A, list): pass  # E: All bases of a new TypedDict must be TypedDict types
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAliasGeneric]
+from typing import Generic, TypedDict, TypeVar
+from typing_extensions import TypeAlias
+
+_T = TypeVar("_T")
+
+class A(Generic[_T], TypedDict):
+    x: _T
+
+# This is by design - no_args aliases are only supported for instances
+A0 = A
+class B(A0[str]):  # E: Bad number of arguments for type alias, expected 0, given 1
+    y: int
+
+A1 = A[_T]
+A2: TypeAlias = A[_T]
+Aint = A[int]
+
+class C(A1[_T]):
+    y: str
+c1: C[int] = {"x": 0, "y": "a"}
+c2: C[int] = {"x": "no", "y": "a"}  # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int")
+
+class D(A2[_T]):
+    y: str
+d1: D[int] = {"x": 0, "y": "a"}
+d2: D[int] = {"x": "no", "y": "a"}  # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int")
+
+class E(Aint):
+    y: str
+e1: E = {"x": 0, "y": "a"}
+e2: E = {"x": "no", "y": "a"}
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]

From e9fa89b2a43af3a2df0a5f5006772e60e9cf8ecc Mon Sep 17 00:00:00 2001
From: Thomas Mattone <43917226+Luunynliny@users.noreply.github.com>
Date: Sun, 6 Apr 2025 03:40:26 +0200
Subject: [PATCH 1238/1617] Fix error message when returning long tuple with
 type mismatch (#18881)

---
 mypy/messages.py                 |  2 +-
 test-data/unit/check-tuples.test | 77 ++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/mypy/messages.py b/mypy/messages.py
index 25c4ed68ccb5..2e07d7f63498 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -2439,7 +2439,7 @@ def generate_incompatible_tuple_error(
         error_cnt = 0
         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 not is_subtype(rhs_t, lhs_t):
                 if error_cnt < 3:
                     notes.append(
                         "Expression tuple item {} has type {}; {} expected; ".format(
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
index d675a35c4aae..3424d053fe42 100644
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -1607,6 +1607,83 @@ t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3
 
 [builtins fixtures/tuple.pyi]
 
+[case testPropertyLongTupleReturnTypeMismatchUnion]
+from typing import Tuple, Union
+class A:
+    a: str
+    b: str
+    c: str
+    d: str
+    e: str
+    f: str
+    g: Union[str, int]
+    h: Union[str, float]
+    i: Union[str, None]
+    j: Union[str, None]
+    k: Union[str, None]
+    l: Union[str, None]
+
+    @property
+    def x(self) -> Tuple[str, str, str, str, str, str, str, str, str, str, str, str]:
+        return (
+            self.a,
+            self.b,
+            self.c,
+            self.d,
+            self.e,
+            self.f,
+            self.g,
+            self.h,
+            self.i,
+            self.j,
+            self.k,
+            self.l,
+        )
+[out]
+main:18: error: Incompatible return value type (6 tuple items are incompatible; 3 items are omitted)
+main:18: note: Expression tuple item 6 has type "Union[str, int]"; "str" expected;
+main:18: note: Expression tuple item 7 has type "Union[str, float]"; "str" expected;
+main:18: note: Expression tuple item 8 has type "Optional[str]"; "str" expected;
+[builtins fixtures/property.pyi]
+
+[case testPropertyLongTupleReturnTypeMismatchUnionWiderExpected]
+from typing import Tuple, Union
+class A:
+    a: str
+    b: str
+    c: str
+    d: str
+    e: str
+    f: str
+    g: str
+    h: str
+    i: str
+    j: str
+    k: str
+    l: Union[float, int]
+
+    @property
+    def x(self) -> Tuple[Union[str, int], Union[str, float], int, Union[str, None], Union[str, None], Union[str, None], str, str, str, str, str, str]:
+        return (
+            self.a,
+            self.b,
+            self.c,
+            self.d,
+            self.e,
+            self.f,
+            self.g,
+            self.h,
+            self.i,
+            self.j,
+            self.k,
+            self.l,
+        )
+[out]
+main:18: error: Incompatible return value type (2 tuple items are incompatible)
+main:18: note: Expression tuple item 2 has type "str"; "int" expected;
+main:18: note: Expression tuple item 11 has type "Union[float, int]"; "str" expected;
+[builtins fixtures/property.pyi]
+
 [case testTupleWithStarExpr]
 from typing import Tuple, List
 points = (1, "test")  # type: Tuple[int, str]

From 749f2584da9425173d68eb220db7e92aa13ad8ea Mon Sep 17 00:00:00 2001
From: sobolevn 
Date: Mon, 7 Apr 2025 16:01:30 +0300
Subject: [PATCH 1239/1617] Do not add `kw_only` dataclass fields to
 `__match_args__` (#18892)

In runtime python does not add fields that have `kw_only` marker:
-
https://github.com/python/cpython/blob/895d983b5c9716aaaab34d14d278084b9b6730d8/Lib/dataclasses.py#L1174-L1177
-
https://github.com/python/cpython/blob/895d983b5c9716aaaab34d14d278084b9b6730d8/Lib/dataclasses.py#L411-L417

See:

```python
>>> import dataclasses
>>> @dataclasses.dataclass(kw_only=True)
... class A:
...     a: int
...
>>> print(A.__match_args__)
()
```

Closes https://github.com/python/mypy/issues/18863
---
 mypy/plugins/dataclasses.py           |  4 +++-
 test-data/unit/check-dataclasses.test | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index 90c983b0bacd..b46b42f78866 100644
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -381,7 +381,9 @@ def transform(self) -> bool:
         ):
             str_type = self._api.named_type("builtins.str")
             literals: list[Type] = [
-                LiteralType(attr.name, str_type) for attr in attributes if attr.is_in_init
+                LiteralType(attr.name, str_type)
+                for attr in attributes
+                if attr.is_in_init and not attr.kw_only
             ]
             match_args_type = TupleType(literals, self._api.named_type("builtins.tuple"))
             add_attribute_to_class(self._api, self._cls, "__match_args__", match_args_type)
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index 887a9052d0b9..048ac831dd25 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -1847,6 +1847,22 @@ e: Empty
 reveal_type(e.__match_args__)  # N: Revealed type is "Tuple[()]"
 [builtins fixtures/dataclasses.pyi]
 
+[case testDataclassWithMatchArgsAndKwOnly]
+# flags: --python-version 3.10
+from dataclasses import dataclass, field
+@dataclass(kw_only=True)
+class One:
+    a: int
+    b: str
+reveal_type(One.__match_args__)  # N: Revealed type is "Tuple[()]"
+
+@dataclass(kw_only=True)
+class Two:
+    a: int = field(kw_only=False)
+    b: str
+reveal_type(Two.__match_args__)  # N: Revealed type is "Tuple[Literal['a']]"
+[builtins fixtures/dataclasses.pyi]
+
 [case testDataclassWithoutMatchArgs]
 # flags: --python-version 3.10
 from dataclasses import dataclass

From 3330b421f89a5b76e3ec87f317af990e225c8f15 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 8 Apr 2025 14:26:08 +0200
Subject: [PATCH 1240/1617] Fix ForwardRef comparison in test for Python 3.14
 (#18885)

As a result of https://github.com/python/cpython/pull/129465,
`ForwardRef` only compare true in Python 3.14 if all attributes match.
---
 mypyc/test-data/run-tuples.test | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index afd3a956b871..5e19ab92b82f 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -127,16 +127,24 @@ class Inextensible(NamedTuple):
     x: int
 
 [file driver.py]
-from typing import ForwardRef, Optional
+import sys
+from typing import Optional
 from native import ClassIR, FuncIR, Record
 
+if sys.version_info >= (3, 14):
+    from test.support import EqualToForwardRef
+    type_forward_ref = EqualToForwardRef
+else:
+    from typing import ForwardRef
+    type_forward_ref = ForwardRef
+
 assert Record.__annotations__ == {
     'st_mtime': float,
     'st_size': int,
     'is_borrowed': bool,
     'hash': str,
     'python_path': tuple,
-    'type': ForwardRef('ClassIR'),
+    'type': type_forward_ref('ClassIR'),
     'method': FuncIR,
     'shadow_method': type,
     'classes': dict,

From c7ea0112a3d4cd5db64d11769a6532dec5be5d0a Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 8 Apr 2025 14:38:25 +0200
Subject: [PATCH 1241/1617] [mypyc] Optimize list.__imul__, tuple.__mul__
 (#18887)

https://docs.python.org/3/c-api/sequence.html#c.PySequence_Repeat
https://docs.python.org/3/c-api/sequence.html#c.PySequence_InPlaceRepeat
---
 mypyc/doc/list_operations.rst      |  2 +-
 mypyc/doc/tuple_operations.rst     |  1 +
 mypyc/lib-rt/CPy.h                 |  1 +
 mypyc/lib-rt/list_ops.c            |  8 +++++++
 mypyc/primitives/list_ops.py       |  9 ++++++++
 mypyc/primitives/tuple_ops.py      | 18 +++++++++++++++
 mypyc/test-data/fixtures/ir.py     |  3 +++
 mypyc/test-data/irbuild-lists.test | 12 ++++++++++
 mypyc/test-data/irbuild-tuple.test | 35 ++++++++++++++++++++++++++++++
 mypyc/test-data/run-lists.test     |  7 ++++++
 mypyc/test-data/run-tuples.test    |  9 ++++++++
 11 files changed, 104 insertions(+), 1 deletion(-)

diff --git a/mypyc/doc/list_operations.rst b/mypyc/doc/list_operations.rst
index 378568865501..bb4681266cab 100644
--- a/mypyc/doc/list_operations.rst
+++ b/mypyc/doc/list_operations.rst
@@ -33,7 +33,7 @@ Operators
 * ``lst[n]`` (get item by integer index)
 * ``lst[n:m]``, ``lst[n:]``, ``lst[:m]``, ``lst[:]`` (slicing)
 * ``lst1 + lst2``, ``lst += iter``
-* ``lst * n``, ``n * lst``
+* ``lst * n``, ``n * lst``, ``lst *= n``
 * ``obj in lst``
 
 Statements
diff --git a/mypyc/doc/tuple_operations.rst b/mypyc/doc/tuple_operations.rst
index ed603fa9982d..4c9da9b894af 100644
--- a/mypyc/doc/tuple_operations.rst
+++ b/mypyc/doc/tuple_operations.rst
@@ -22,6 +22,7 @@ Operators
 * ``tup[n]`` (integer index)
 * ``tup[n:m]``, ``tup[n:]``, ``tup[:m]`` (slicing)
 * ``tup1 + tup2``
+* ``tup * n``, ``n * tup``
 
 Statements
 ----------
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 7b192e747595..aeb559a50a7a 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -664,6 +664,7 @@ int CPyList_Remove(PyObject *list, PyObject *obj);
 CPyTagged CPyList_Index(PyObject *list, PyObject *obj);
 PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size);
 PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq);
+PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size);
 PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
 PyObject *CPyList_Copy(PyObject *list);
 int CPySequence_Check(PyObject *obj);
diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index 8388e1eea73a..b47fcec8ffe9 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -331,6 +331,14 @@ PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq) {
     return CPySequence_Multiply(seq, t_size);
 }
 
+PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size) {
+    Py_ssize_t size = CPyTagged_AsSsize_t(t_size);
+    if (size == -1 && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PySequence_InPlaceRepeat(seq, size);
+}
+
 PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) {
     if (likely(PyList_CheckExact(obj)
                && CPyTagged_CheckShort(start) && CPyTagged_CheckShort(end))) {
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 5cc8b3c0d1c6..6063fdfd680e 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -307,6 +307,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# list *= int
+binary_op(
+    name="*=",
+    arg_types=[list_rprimitive, int_rprimitive],
+    return_type=list_rprimitive,
+    c_function_name="CPySequence_InPlaceMultiply",
+    error_kind=ERR_MAGIC,
+)
+
 # list[begin:end]
 list_slice_op = custom_op(
     arg_types=[list_rprimitive, int_rprimitive, int_rprimitive],
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index f28d4ca5ec7a..a9bbaa80fb5c 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -83,6 +83,24 @@
     error_kind=ERR_MAGIC,
 )
 
+# tuple * int
+binary_op(
+    name="*",
+    arg_types=[tuple_rprimitive, int_rprimitive],
+    return_type=tuple_rprimitive,
+    c_function_name="CPySequence_Multiply",
+    error_kind=ERR_MAGIC,
+)
+
+# int * tuple
+binary_op(
+    name="*",
+    arg_types=[int_rprimitive, tuple_rprimitive],
+    return_type=tuple_rprimitive,
+    c_function_name="CPySequence_RMultiply",
+    error_kind=ERR_MAGIC,
+)
+
 # tuple[begin:end]
 tuple_slice_op = custom_op(
     arg_types=[tuple_rprimitive, int_rprimitive, int_rprimitive],
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index e82c79459709..4e9b917ad03b 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -212,6 +212,8 @@ def __contains__(self, item: object) -> int: ...
     def __add__(self, value: Tuple[T_co, ...], /) -> Tuple[T_co, ...]: ...
     @overload
     def __add__(self, value: Tuple[_T, ...], /) -> Tuple[T_co | _T, ...]: ...
+    def __mul__(self, value: int, /) -> Tuple[T_co, ...]: ...
+    def __rmul__(self, value: int, /) -> Tuple[T_co, ...]: ...
 
 class function: pass
 
@@ -225,6 +227,7 @@ def __setitem__(self, i: int, o: _T) -> None: pass
     def __delitem__(self, i: int) -> None: pass
     def __mul__(self, i: int) -> List[_T]: pass
     def __rmul__(self, i: int) -> List[_T]: pass
+    def __imul__(self, i: int) -> List[_T]: ...
     def __iter__(self) -> Iterator[_T]: pass
     def __len__(self) -> int: pass
     def __contains__(self, item: object) -> int: ...
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index b7ba1a783bb7..c2e2df133fc5 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -194,6 +194,18 @@ L0:
     b = r4
     return 1
 
+[case testListIMultiply]
+from typing import List
+def f(a: List[int]) -> None:
+    a *= 2
+[out]
+def f(a):
+    a, r0 :: list
+L0:
+    r0 = CPySequence_InPlaceMultiply(a, 4)
+    a = r0
+    return 1
+
 [case testListLen]
 from typing import List
 def f(a: List[int]) -> int:
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index e7280bb3b552..582391ff6f98 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -418,3 +418,38 @@ L0:
     r3 = unbox(tuple[int, int, int, int], r2)
     c = r3
     return 1
+
+[case testTupleMultiply]
+from typing import Tuple
+def f(a: Tuple[int]) -> None:
+    b = a * 2
+    c = 3 * (2,)
+def g(a: Tuple[int, ...]) -> None:
+    b = a * 2
+[out]
+def f(a):
+    a :: tuple[int]
+    r0 :: object
+    r1 :: tuple
+    r2, b :: tuple[int, int]
+    r3 :: tuple[int]
+    r4 :: object
+    r5 :: tuple
+    r6, c :: tuple[int, int, int]
+L0:
+    r0 = box(tuple[int], a)
+    r1 = CPySequence_Multiply(r0, 4)
+    r2 = unbox(tuple[int, int], r1)
+    b = r2
+    r3 = (4)
+    r4 = box(tuple[int], r3)
+    r5 = CPySequence_RMultiply(6, r4)
+    r6 = unbox(tuple[int, int, int], r5)
+    c = r6
+    return 1
+def g(a):
+    a, r0, b :: tuple
+L0:
+    r0 = CPySequence_Multiply(a, 4)
+    b = r0
+    return 1
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 84168f7254f5..b6d9a811d910 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -313,6 +313,13 @@ def test_add() -> None:
     assert in_place_add({3: "", 4: ""}) == res
     assert in_place_add(range(3, 5)) == res
 
+def test_multiply() -> None:
+    l1 = [1]
+    assert l1 * 3 == [1, 1, 1]
+    assert 3 * l1 == [1, 1, 1]
+    l1 *= 3
+    assert l1 == [1, 1, 1]
+
 [case testOperatorInExpression]
 
 def tuple_in_int0(i: int) -> bool:
diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index 5e19ab92b82f..1437eaef2aa5 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -269,3 +269,12 @@ def test_add() -> None:
     assert (1, 2) + (3, 4) == res
     with assertRaises(TypeError, 'can only concatenate tuple (not "list") to tuple'):
         assert (1, 2) + cast(Any, [3, 4]) == res
+
+def multiply(a: Tuple[Any, ...], b: int) -> Tuple[Any, ...]:
+    return a * b
+
+def test_multiply() -> None:
+    res = (1, 1, 1)
+    assert (1,) * 3 == res
+    assert 3 * (1,) == res
+    assert multiply((1,), 3) == res

From c3ed5e02a687baa2e8fdbeaab67e4de8d45c2d03 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 9 Apr 2025 15:19:16 +0200
Subject: [PATCH 1242/1617] Flatten union before contracting literals when
 checking subtyping (#18898)

Fixes #18896
---
 mypy/subtypes.py                  |  5 ++++-
 mypy/typeops.py                   |  2 ++
 test-data/unit/check-literal.test | 22 ++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 41bb4601e23f..71b8b0ba59f5 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -67,6 +67,7 @@
     UnionType,
     UnpackType,
     find_unpack_in_list,
+    flatten_nested_unions,
     get_proper_type,
     is_named_instance,
     split_with_prefix_and_suffix,
@@ -327,7 +328,9 @@ def _is_subtype(
             and isinstance(left, Instance)
             and (left.type.is_enum or left.type.fullname == "builtins.bool")
         ):
-            right = UnionType(mypy.typeops.try_contracting_literals_in_union(right.items))
+            right = UnionType(
+                mypy.typeops.try_contracting_literals_in_union(flatten_nested_unions(right.items))
+            )
             if proper_subtype:
                 is_subtype_of_item = any(
                     is_proper_subtype(orig_left, item, subtype_context=subtype_context)
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 06ecc0fb3fda..bcf946900563 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -1069,6 +1069,8 @@ class Status(Enum):
 def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]:
     """Contracts any literal types back into a sum type if possible.
 
+    Requires a flattened union and does not descend into children.
+
     Will replace the first instance of the literal with the sum type and
     remove all others.
 
diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
index 88c02f70488c..f36eff28f33f 100644
--- a/test-data/unit/check-literal.test
+++ b/test-data/unit/check-literal.test
@@ -2765,6 +2765,28 @@ reveal_type(x)  # N: Revealed type is "Literal[__main__.Foo.A]"
 reveal_type(y)  # N: Revealed type is "Literal[__main__.Foo.A]"
 [builtins fixtures/tuple.pyi]
 
+[case testLiteralUnionEnumAliasAssignable]
+from enum import Enum
+from typing import Literal, Union
+
+class E(Enum):
+    A = 'a'
+    B = 'b'
+    C = 'c'
+
+A = Literal[E.A]
+B = Literal[E.B, E.C]
+
+def f(x: Union[A, B]) -> None: ...
+def f2(x: Union[A, Literal[E.B, E.C]]) -> None: ...
+def f3(x: Union[Literal[E.A], B]) -> None: ...
+
+def main(x: E) -> None:
+    f(x)
+    f2(x)
+    f3(x)
+[builtins fixtures/tuple.pyi]
+
 [case testStrictEqualityLiteralTrueVsFalse]
 # mypy: strict-equality
 

From a4e79ea19506948fd43bf5c14bbf8e2a0ad7158a Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 9 Apr 2025 15:28:14 +0200
Subject: [PATCH 1243/1617] [mypyc] Add basic optimization for sorted (#18902)

Ref: https://github.com/mypyc/mypyc/issues/1089
---
 mypyc/doc/native_operations.rst    |  1 +
 mypyc/lib-rt/CPy.h                 |  1 +
 mypyc/lib-rt/list_ops.c            | 12 ++++++++++++
 mypyc/primitives/list_ops.py       |  9 +++++++++
 mypyc/test-data/fixtures/ir.py     |  1 +
 mypyc/test-data/irbuild-lists.test | 22 ++++++++++++++++++++++
 mypyc/test-data/run-lists.test     | 22 ++++++++++++++++++++++
 7 files changed, 68 insertions(+)

diff --git a/mypyc/doc/native_operations.rst b/mypyc/doc/native_operations.rst
index 2587e982feac..3255dbedd98a 100644
--- a/mypyc/doc/native_operations.rst
+++ b/mypyc/doc/native_operations.rst
@@ -36,6 +36,7 @@ Functions
 * ``delattr(obj, name)``
 * ``slice(start, stop, step)``
 * ``globals()``
+* ``sorted(obj)``
 
 Method decorators
 -----------------
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index aeb559a50a7a..1f0cf4dd63d6 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -662,6 +662,7 @@ int CPyList_Insert(PyObject *list, CPyTagged index, PyObject *value);
 PyObject *CPyList_Extend(PyObject *o1, PyObject *o2);
 int CPyList_Remove(PyObject *list, PyObject *obj);
 CPyTagged CPyList_Index(PyObject *list, PyObject *obj);
+PyObject *CPySequence_Sort(PyObject *seq);
 PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size);
 PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq);
 PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size);
diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index b47fcec8ffe9..4dddb2249f06 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -319,6 +319,18 @@ CPyTagged CPyList_Index(PyObject *list, PyObject *obj) {
     return index << 1;
 }
 
+PyObject *CPySequence_Sort(PyObject *seq) {
+    PyObject *newlist = PySequence_List(seq);
+    if (newlist == NULL)
+        return NULL;
+    int res = PyList_Sort(newlist);
+    if (res < 0) {
+        Py_DECREF(newlist);
+        return NULL;
+    }
+    return newlist;
+}
+
 PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size) {
     Py_ssize_t size = CPyTagged_AsSsize_t(t_size);
     if (size == -1 && PyErr_Occurred()) {
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 6063fdfd680e..99df6fe0dc9c 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -27,6 +27,15 @@
 # Get the 'builtins.list' type object.
 load_address_op(name="builtins.list", type=object_rprimitive, src="PyList_Type")
 
+# sorted(obj)
+function_op(
+    name="builtins.sorted",
+    arg_types=[object_rprimitive],
+    return_type=list_rprimitive,
+    c_function_name="CPySequence_Sort",
+    error_kind=ERR_MAGIC,
+)
+
 # list(obj)
 to_list = function_op(
     name="builtins.list",
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 4e9b917ad03b..1b92590a5fd4 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -384,6 +384,7 @@ def pow(base: __SupportsPow2[T_contra, T_co], exp: T_contra, mod: None = None) -
 def pow(base: __SupportsPow3NoneOnly[T_contra, T_co], exp: T_contra, mod: None = None) -> T_co: ...
 @overload
 def pow(base: __SupportsPow3[T_contra, _M, T_co], exp: T_contra, mod: _M) -> T_co: ...
+def sorted(iterable: Iterable[_T]) -> list[_T]: ...
 def exit() -> None: ...
 def min(x: _T, y: _T) -> _T: ...
 def max(x: _T, y: _T) -> _T: ...
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index c2e2df133fc5..2435b5aee350 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -561,3 +561,25 @@ L3:
     goto L1
 L4:
     return 1
+
+[case testSorted]
+from typing import List, Any
+def list_sort(a: List[int]) -> None:
+    a.sort()
+def sort_iterable(a: Any) -> None:
+    sorted(a)
+[out]
+def list_sort(a):
+    a :: list
+    r0 :: i32
+    r1 :: bit
+L0:
+    r0 = PyList_Sort(a)
+    r1 = r0 >= 0 :: signed
+    return 1
+def sort_iterable(a):
+    a :: object
+    r0 :: list
+L0:
+    r0 = CPySequence_Sort(a)
+    return 1
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index b6d9a811d910..07c6d7735f10 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -489,3 +489,25 @@ def test_index_with_literal() -> None:
     assert d is d2
     d = a[-2].d
     assert d is d1
+
+[case testSorted]
+from typing import List
+
+def test_list_sort() -> None:
+    l1 = [2, 1, 3]
+    id_l1 = id(l1)
+    l1.sort()
+    assert l1 == [1, 2, 3]
+    assert id_l1 == id(l1)
+
+def test_sorted() -> None:
+    res = [1, 2, 3]
+    l1 = [2, 1, 3]
+    id_l1 = id(l1)
+    s_l1 = sorted(l1)
+    assert s_l1 == res
+    assert id_l1 != id(s_l1)
+    assert l1 == [2, 1, 3]
+    assert sorted((2, 1, 3)) == res
+    assert sorted({2, 1, 3}) == res
+    assert sorted({2: "", 1: "", 3: ""}) == res

From bb01516f768b8086e0a815ef7a5a1861973978b4 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 11 Apr 2025 11:30:57 +0300
Subject: [PATCH 1244/1617] [pre-commit.ci] pre-commit autoupdate (#18899)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit


updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.10 →
v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.4)
- [github.com/python-jsonschema/check-jsonschema: 0.31.0 →
0.32.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.0...0.32.1)
- [github.com/woodruffw/zizmor-pre-commit: v1.0.1 →
v1.5.2](https://github.com/woodruffw/zizmor-pre-commit/compare/v1.0.1...v1.5.2)


Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d466d4563aff..3b323f03b99c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -11,12 +11,12 @@ repos:
       - id: black
         exclude: '^(test-data/)'
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.9.10
+    rev: v0.11.4
     hooks:
       - id: ruff
         args: [--exit-non-zero-on-fix]
   - repo: https://github.com/python-jsonschema/check-jsonschema
-    rev: 0.31.0
+    rev: 0.32.1
     hooks:
       - id: check-github-workflows
       - id: check-github-actions
@@ -43,7 +43,7 @@ repos:
           # but the integration only works if shellcheck is installed
           - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.10.0"
   - repo: https://github.com/woodruffw/zizmor-pre-commit
-    rev: v1.0.1
+    rev: v1.5.2
     hooks:
       - id: zizmor
   - repo: local

From b5c95d84b4ac2940618251ed3ec48f46ba0662a0 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 11 Apr 2025 10:31:20 +0200
Subject: [PATCH 1245/1617] Exclude irrelevant members in
 `narrow_declared_type` from union overlapping with enum (#18897)

Fixes #18895.

The original implementation of that block was introduced as a
performance optimization in #12032. It's in fact incorrect: it produces
overly optimistic meets, assuming that *any* match among union items
makes them *all* relevant. As discussed in #18895, this actually results
in unexpected `meet` behaviour, as demonstrated by

```python
from enum import Enum
from typing_extensions import TypeIs, Literal

class Model(str, Enum):
    A = 'a'
    B = 'a'

def is_model_a(model: str) -> TypeIs[Literal[Model.A, "foo"]]:
    return True
def handle(model: Model) -> None:
    if is_model_a(model):
        reveal_type(model)  # N: Revealed type is "Union[Literal[__main__.Model.A], Literal['foo']]"


def is_int_or_list(model: object) -> TypeIs[int | list[int]]:
    return True
def compare(x: int | str) -> None:
    if is_int_or_list(x):
        reveal_type(x)  # N: Revealed type is "builtins.int"
```

This patch restores filtering of union members, but keeps it running
before the expensive `is_overlapping_types` check involving expansion.
---
 mypy/meet.py                        |  7 ++++++-
 test-data/unit/check-typeguard.test | 30 +++++++++++++++++++++++++++++
 test-data/unit/check-typeis.test    | 17 ++++++++++++++++
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/mypy/meet.py b/mypy/meet.py
index b5262f87c0bd..add0785f5e71 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -143,7 +143,12 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
             ]
         )
     if is_enum_overlapping_union(declared, narrowed):
-        return original_narrowed
+        # Quick check before reaching `is_overlapping_types`. If it's enum/literal overlap,
+        # avoid full expansion and make it faster.
+        assert isinstance(narrowed, UnionType)
+        return make_simplified_union(
+            [narrow_declared_type(declared, x) for x in narrowed.relevant_items()]
+        )
     elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True):
         if state.strict_optional:
             return UninhabitedType()
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index 71c4473fbfaa..00bf7d211927 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -803,3 +803,33 @@ def test() -> None:
         return
     reveal_type(x)  # N: Revealed type is "builtins.list[__main__.C]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeGuardedTypeDoesNotLeak]
+# https://github.com/python/mypy/issues/18895
+from enum import Enum
+from typing import Literal, Union
+from typing_extensions import TypeGuard
+
+class Model(str, Enum):
+    A1 = 'model_a1'
+    A2 = 'model_a2'
+    B = 'model_b'
+
+MODEL_A = Literal[Model.A1, Model.A2]
+MODEL_B = Literal[Model.B]
+
+def is_model_a(model: str) -> TypeGuard[MODEL_A]:
+    return True
+
+def is_model_b(model: str) -> TypeGuard[MODEL_B]:
+    return True
+
+def process_model(model: Union[MODEL_A, MODEL_B]) -> int:
+    return 42
+
+def handle(model: Model) -> int:
+    if is_model_a(model) or is_model_b(model):
+        reveal_type(model)  # N: Revealed type is "__main__.Model"
+        return process_model(model)
+    return 0
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
index e70c71a4b62e..8cdcf8634788 100644
--- a/test-data/unit/check-typeis.test
+++ b/test-data/unit/check-typeis.test
@@ -936,3 +936,20 @@ def func(arg: Any) -> None:
     if is_dataclass(arg):
         reveal_type(arg)  # N: Revealed type is "Union[Type[__main__.DataclassInstance], __main__.DataclassInstance]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeIsEnumOverlappingUnionExcludesIrrelevant]
+from enum import Enum
+from typing import Literal
+from typing_extensions import TypeIs
+
+class Model(str, Enum):
+    A = 'a'
+    B = 'a'
+
+def is_model_a(model: str) -> TypeIs[Literal[Model.A, "foo"]]:
+    return True
+
+def handle(model: Model) -> None:
+    if is_model_a(model):
+        reveal_type(model)  # N: Revealed type is "Literal[__main__.Model.A]"
+[builtins fixtures/tuple.pyi]

From 54975a008f229941c29b4df5ecd755d4f5166d71 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Fri, 11 Apr 2025 13:01:53 -0700
Subject: [PATCH 1246/1617] Fix incremental issue with namespace packages
 (option 1) (#18907)

Fixes #12664

A root cause is there is this stateful `_update_ns_ancestors` thing in
`modulefinder`, so if things get called in the wrong order, you can get
incorrect results.

See also the logic in `all_imported_modules_in_file` where we've fixed
several bugs like this previously, like #13124 and #10937

As a result of (seemingly accidentally) reusing imports across modules,
we can end up in a situation where the namespace gets added as a
dependency to all other modules and so on the cached run we attempt to
find namespace before package, which does not work

I am not sure this `imports` code path is even needed, so I will open an
alternate PR, see #18908.

Relevant history:
- https://github.com/python/mypy/pull/6582
- https://github.com/python/mypy/pull/6179

I can't write a good test for this because it requires something in
site_packages, but here's a minimal repro:
```
set -eux
rm -rf repro
mkdir repro
cd repro

SITEPACK=env/site-packages
mkdir -p $SITEPACK

mkdir $SITEPACK/ruamel
mkdir $SITEPACK/ruamel/yaml

printf 'from ruamel.yaml.main import *' > $SITEPACK/ruamel/yaml/__init__.py
printf 'import ruamel.yaml' > $SITEPACK/ruamel/yaml/main.py
printf '' > $SITEPACK/ruamel/yaml/py.typed

printf 'import ruamel.yaml' > a.py
printf 'import a' > main.py

rm -rf .mypy_cache
PYTHONPATH=$SITEPACK mypy main.py
PYTHONPATH=$SITEPACK mypy main.py
```
---
 mypy/semanal_main.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py
index 2e0d901d5864..00d795c64e44 100644
--- a/mypy/semanal_main.py
+++ b/mypy/semanal_main.py
@@ -389,6 +389,7 @@ def semantic_analyze_target(
     analyzer.global_decls = [set()]
     analyzer.nonlocal_decls = [set()]
     analyzer.globals = tree.names
+    analyzer.imports = set()
     analyzer.progress = False
     with state.wrap_context(check_blockers=False):
         refresh_node = node

From 1ba23f19e7831b9591b20b176b9896d86e6ef6d7 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 12 Apr 2025 00:33:24 +0200
Subject: [PATCH 1247/1617] Traverse module ancestors when traversing reachable
 graph nodes during dmypy update (#18906)

Fixes #18396. Fixes #17652. Hopefully fixes #15486 (but not enough info
to reproduce the original problem).

See discussion in #18396. This PR forces collecting all ancestors of all
modules during dep graph traversal in incremental update.

Ancestors are included in `load_graph`, which means not traversing them
during update results in some modules being erroneously treated as
deleted:


https://github.com/python/mypy/blob/a4e79ea19506948fd43bf5c14bbf8e2a0ad7158a/mypy/build.py#L3141-L3146
---
 mypy/dmypy_server.py       | 14 +++++++++++---
 test-data/unit/daemon.test | 18 ++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py
index d73487efe3bc..33e9e07477ca 100644
--- a/mypy/dmypy_server.py
+++ b/mypy/dmypy_server.py
@@ -620,6 +620,9 @@ def fine_grained_increment_follow_imports(
         t1 = time.time()
         manager.log(f"fine-grained increment: find_changed: {t1 - t0:.3f}s")
 
+        # Track all modules encountered so far. New entries for all dependencies
+        # are added below by other module finding methods below. All dependencies
+        # in graph but not in `seen` are considered deleted at the end of this method.
         seen = {source.module for source in sources}
 
         # Find changed modules reachable from roots (or in roots) already in graph.
@@ -736,7 +739,9 @@ def find_reachable_changed_modules(
         Args:
             roots: modules where to start search from
             graph: module graph to use for the search
-            seen: modules we've seen before that won't be visited (mutated here!!)
+            seen: modules we've seen before that won't be visited (mutated here!!).
+                  Needed to accumulate all modules encountered during update and remove
+                  everything that no longer exists.
             changed_paths: which paths have changed (stop search here and return any found)
 
         Return (encountered reachable changed modules,
@@ -756,7 +761,8 @@ def find_reachable_changed_modules(
                 changed.append((nxt.module, nxt.path))
             elif nxt.module in graph:
                 state = graph[nxt.module]
-                for dep in state.dependencies:
+                ancestors = state.ancestors or []
+                for dep in state.dependencies + ancestors:
                     if dep not in seen:
                         seen.add(dep)
                         worklist.append(BuildSource(graph[dep].path, graph[dep].id, followed=True))
@@ -775,7 +781,9 @@ def find_added_suppressed(
         """Find suppressed modules that have been added (and not included in seen).
 
         Args:
-            seen: reachable modules we've seen before (mutated here!!)
+            seen: reachable modules we've seen before (mutated here!!).
+                  Needed to accumulate all modules encountered during update and remove
+                  everything that no longer exists.
 
         Return suppressed, added modules.
         """
diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test
index 7dfddd8f74df..19ffce0927ab 100644
--- a/test-data/unit/daemon.test
+++ b/test-data/unit/daemon.test
@@ -647,3 +647,21 @@ b: str
 from demo.test import a
 [file demo/test.py]
 a: int
+
+[case testDaemonImportAncestors]
+$ dmypy run test.py
+Daemon started
+test.py:2: error: Unsupported operand types for + ("int" and "str")  [operator]
+Found 1 error in 1 file (checked 1 source file)
+== Return code: 1
+$ dmypy run test.py
+test.py:2: error: Unsupported operand types for + ("int" and "str")  [operator]
+Found 1 error in 1 file (checked 1 source file)
+== Return code: 1
+$ dmypy run test.py
+test.py:2: error: Unsupported operand types for + ("int" and "str")  [operator]
+Found 1 error in 1 file (checked 1 source file)
+== Return code: 1
+[file test.py]
+from xml.etree.ElementTree import Element
+1 + 'a'

From 616e1865c1e0024d181ac915d2f1576113ed2cd8 Mon Sep 17 00:00:00 2001
From: Ageev Maxim 
Date: Sat, 12 Apr 2025 18:57:44 +0300
Subject: [PATCH 1248/1617] Docs: remove a note about `from __future__ import
 annotations` (#18915)

Co-authored-by: sobolevn 
---
 docs/source/runtime_troubles.rst | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst
index b61f0048dd0a..edc375e26485 100644
--- a/docs/source/runtime_troubles.rst
+++ b/docs/source/runtime_troubles.rst
@@ -8,10 +8,9 @@ version of Python considers legal code. This section describes these scenarios
 and explains how to get your code running again. Generally speaking, we have
 three tools at our disposal:
 
-* Use of ``from __future__ import annotations`` (:pep:`563`)
-  (this behaviour may eventually be made the default in a future Python version)
 * Use of string literal types or type comments
 * Use of ``typing.TYPE_CHECKING``
+* Use of ``from __future__ import annotations`` (:pep:`563`)
 
 We provide a description of these before moving onto discussion of specific
 problems you may encounter.

From a3ce6d5307e99a1b6c181eaa7c5cf134c53b7d8b Mon Sep 17 00:00:00 2001
From: Nazrawi Demeke 
Date: Mon, 14 Apr 2025 23:47:30 +0100
Subject: [PATCH 1249/1617] Fix swapped errors for frozen/non-frozen dataclass
 inheritance (#18918)

There is a mix-up in error messages related to frozen and non-frozen
dataclass inheritance.
---
 mypy/plugins/dataclasses.py           | 4 ++--
 test-data/unit/check-dataclasses.test | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index b46b42f78866..2b4982a36bb6 100644
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -359,12 +359,12 @@ def transform(self) -> bool:
 
         if decorator_arguments["frozen"]:
             if any(not parent["frozen"] for parent in parent_decorator_arguments):
-                self._api.fail("Cannot inherit frozen dataclass from a non-frozen one", info)
+                self._api.fail("Frozen dataclass cannot inherit from a non-frozen dataclass", info)
             self._propertize_callables(attributes, settable=False)
             self._freeze(attributes)
         else:
             if any(parent["frozen"] for parent in parent_decorator_arguments):
-                self._api.fail("Cannot inherit non-frozen dataclass from a frozen one", info)
+                self._api.fail("Non-frozen dataclass cannot inherit from a frozen dataclass", info)
             self._propertize_callables(attributes)
 
         if decorator_arguments["slots"]:
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index 048ac831dd25..dbcb4c82072c 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -260,7 +260,7 @@ class FrozenBase:
     pass
 
 @dataclass
-class BadNormalDerived(FrozenBase):  # E: Cannot inherit non-frozen dataclass from a frozen one
+class BadNormalDerived(FrozenBase):  # E: Non-frozen dataclass cannot inherit from a frozen dataclass
     pass
 
 @dataclass
@@ -268,7 +268,7 @@ class NormalBase:
     pass
 
 @dataclass(frozen=True)
-class BadFrozenDerived(NormalBase):  # E: Cannot inherit frozen dataclass from a non-frozen one
+class BadFrozenDerived(NormalBase):  # E: Frozen dataclass cannot inherit from a non-frozen dataclass
     pass
 
 [builtins fixtures/dataclasses.pyi]

From df600551675363efd1447214e09a8fb1b60bb746 Mon Sep 17 00:00:00 2001
From: Carter Dodd 
Date: Thu, 17 Apr 2025 03:51:20 -0500
Subject: [PATCH 1250/1617] Allow deeper recursion in mypy daemon, better error
 reporting (#17707)

Fixes #17706

Handles recursion error during parse of too complex expressions,
differentiates from internal recursion errors by attempting to unparse
the ast. If builtin ast.unparse fails, then the error is ignored.
Otherwise, re-raises.
---
 mypy/dmypy/client.py |  5 +++++
 mypy/fastparse.py    | 24 ++++++++++++++++++++++++
 mypy/main.py         |  3 ++-
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
index 8ca4f1bd7ea2..9839f793582d 100644
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -20,6 +20,7 @@
 from mypy.dmypy_os import alive, kill
 from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send
 from mypy.ipc import IPCClient, IPCException
+from mypy.main import RECURSION_LIMIT
 from mypy.util import check_python_version, get_terminal_width, should_force_color
 from mypy.version import __version__
 
@@ -268,6 +269,10 @@ class BadStatus(Exception):
 def main(argv: list[str]) -> None:
     """The code is top-down."""
     check_python_version("dmypy")
+
+    # set recursion limit consistent with mypy/main.py
+    sys.setrecursionlimit(RECURSION_LIMIT)
+
     args = parser.parse_args(argv)
     if not args.action:
         parser.print_usage()
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index b9a55613ec16..a81241ec191a 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -239,6 +239,29 @@ def parse(
             strip_function_bodies=strip_function_bodies,
             path=fnam,
         ).visit(ast)
+
+    except RecursionError as e:
+        # For very complex expressions it is possible to hit recursion limit
+        # before reaching a leaf node.
+        # Should reject at top level instead at bottom, since bottom would already
+        # be at the threshold of the recursion limit, and may fail again later.
+        # E.G. x1+x2+x3+...+xn -> BinOp(left=BinOp(left=BinOp(left=...
+        try:
+            # But to prove that is the cause of this particular recursion error,
+            # try to walk the tree using builtin visitor
+            ast3.NodeVisitor().visit(ast)
+        except RecursionError:
+            errors.report(
+                -1, -1, "Source expression too complex to parse", blocker=False, code=codes.MISC
+            )
+
+            tree = MypyFile([], [], False, {})
+
+        else:
+            # re-raise original recursion error if it *can* be unparsed,
+            # maybe this is some other issue that shouldn't be silenced/misdirected
+            raise e
+
     except SyntaxError as e:
         message = e.msg
         if feature_version > sys.version_info.minor and message.startswith("invalid syntax"):
@@ -406,6 +429,7 @@ def visit(self, node: AST | None) -> Any:
             method = "visit_" + node.__class__.__name__
             visitor = getattr(self, method)
             self.visitor_cache[typeobj] = visitor
+
         return visitor(node)
 
     def set_line(self, node: N, n: AstNode) -> N:
diff --git a/mypy/main.py b/mypy/main.py
index eff1c538bac5..e5afb05e873b 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -42,6 +42,7 @@
 
 orig_stat: Final = os.stat
 MEM_PROFILE: Final = False  # If True, dump memory profile
+RECURSION_LIMIT: Final = 2**14
 
 
 def stat_proxy(path: str) -> os.stat_result:
@@ -76,7 +77,7 @@ def main(
     util.check_python_version("mypy")
     t0 = time.time()
     # To log stat() calls: os.stat = stat_proxy
-    sys.setrecursionlimit(2**14)
+    sys.setrecursionlimit(RECURSION_LIMIT)
     if args is None:
         args = sys.argv[1:]
 

From 9a52273b9b8f1dd969ad8a69dba3cbd308d0af0a Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 17 Apr 2025 15:07:04 +0200
Subject: [PATCH 1251/1617] Fix PEP 695 type alias with mix of type args (PEP
 696) (#18919)

Fix an issue where TypeVar defaults wouldn't be applied to PEP 695 type
aliases.
Fixes #18921
---
 mypy/semanal.py                     |  2 +-
 test-data/unit/check-python313.test | 21 ++++++++++++++++++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 6d0a62070c8e..586094b7a6fe 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5591,7 +5591,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
                 self.msg.unimported_type_becomes_any("Type alias target", res, s)
                 res = make_any_non_unimported(res)
             eager = self.is_func_scope()
-            if isinstance(res, ProperType) and isinstance(res, Instance) and not res.args:
+            if isinstance(res, ProperType) and isinstance(res, Instance):
                 fix_instance(res, self.fail, self.note, disallow_any=False, options=self.options)
             alias_node = TypeAlias(
                 res,
diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test
index 2729ad3e21d1..f020b1602b99 100644
--- a/test-data/unit/check-python313.test
+++ b/test-data/unit/check-python313.test
@@ -219,7 +219,7 @@ def func_a1(
     reveal_type(b)  # N: Revealed type is "builtins.dict[builtins.float, builtins.str]"
     reveal_type(c)  # N: Revealed type is "builtins.dict[builtins.float, builtins.float]"
     reveal_type(d)  # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
-[builtins fixtures/tuple.pyi]
+[builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
 
 [case testPEP695TypeParameterDefaultTypeAlias2]
@@ -255,3 +255,22 @@ def func_c1(
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
+
+[case testPEP695TypeParameterDefaultTypeAlias4]
+# flags: --disallow-any-generics
+class A[L = int, M = str]: ...
+TD1 = A[float]
+type TD2 = A[float]
+
+def func_d1(
+    a: TD1,
+    b: TD1[float],  # E: Bad number of arguments for type alias, expected 0, given 1
+    c: TD2,
+    d: TD2[float],  # E: Bad number of arguments for type alias, expected 0, given 1
+) -> None:
+    reveal_type(a)  # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
+    reveal_type(b)  # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
+    reveal_type(c)  # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
+    reveal_type(d)  # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]

From 82d94776153d1f09fdff193c58b379e6c03add54 Mon Sep 17 00:00:00 2001
From: lenayoung8 <153099057+lenayoung8@users.noreply.github.com>
Date: Thu, 17 Apr 2025 09:23:59 -0400
Subject: [PATCH 1252/1617] Clarified strict documentation (#18903)

Fixes #18760

This documentation change basically clarifies strict's behavior as
described in the issue, adding precedence of the strict flag with
respect to other error-checking flags

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 docs/source/command_line.rst | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index 2a54c1144171..b455e287017e 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -749,8 +749,19 @@ of the above sections.
 
 .. option:: --strict
 
-    This flag mode enables all optional error checking flags.  You can see the
-    list of flags enabled by strict mode in the full :option:`mypy --help` output.
+    This flag mode enables a defined subset of optional error-checking flags.
+    This subset primarily includes checks for inadvertent type unsoundness (i.e
+    strict will catch type errors as long as intentional methods like type ignore
+    or casting were not used.)
+
+    Note: the :option:`--warn-unreachable` flag
+    is not automatically enabled by the strict flag.
+
+    The strict flag does not take precedence over other strict-related flags.
+    Directly specifying a flag of alternate behavior will override the
+    behavior of strict, regardless of the order in which they are passed.
+    You can see the list of flags enabled by strict mode in the full
+    :option:`mypy --help` output.
 
     Note: the exact list of flags enabled by running :option:`--strict` may change
     over time.

From 454989f7c085d5a7f86ad7ed9da0f2614ca41d83 Mon Sep 17 00:00:00 2001
From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com>
Date: Thu, 17 Apr 2025 06:44:58 -0700
Subject: [PATCH 1253/1617] [mypyc] Using UnboundedType to access class object
 of a type annotation. (#18874)

Fixes mypyc/mypyc#1087.

This fix handles cases where type annotation is nested inside an
imported module (like an inner class) or imported from a different
module.
---
 mypyc/irbuild/classdef.py        |  4 +--
 mypyc/irbuild/function.py        | 44 ++++++++++++++++++++++++++++----
 mypyc/test-data/commandline.test | 28 ++++++++++++++++++++
 mypyc/test-data/run-classes.test |  5 ++++
 4 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 01224adb8a00..1e53df92fcfe 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -634,7 +634,7 @@ def add_non_ext_class_attr_ann(
             if builder.current_module == type_info.module_name and stmt.line < type_info.line:
                 typ = builder.load_str(type_info.fullname)
             else:
-                typ = load_type(builder, type_info, stmt.line)
+                typ = load_type(builder, type_info, stmt.unanalyzed_type, stmt.line)
 
     if typ is None:
         # FIXME: if get_type_info is not provided, don't fall back to stmt.type?
@@ -650,7 +650,7 @@ def add_non_ext_class_attr_ann(
             # actually a forward reference due to the __annotations__ future?
             typ = builder.load_str(stmt.unanalyzed_type.original_str_expr)
         elif isinstance(ann_type, Instance):
-            typ = load_type(builder, ann_type.type, stmt.line)
+            typ = load_type(builder, ann_type.type, stmt.unanalyzed_type, stmt.line)
         else:
             typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line))
 
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index dd996985e43d..ef9ec845f8f6 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -29,7 +29,7 @@
     TypeInfo,
     Var,
 )
-from mypy.types import CallableType, get_proper_type
+from mypy.types import CallableType, Type, UnboundType, get_proper_type
 from mypyc.common import LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME
 from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
 from mypyc.ir.func_ir import (
@@ -802,15 +802,49 @@ def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget:
     return builder.add_local_reg(fdef, object_rprimitive)
 
 
-def load_type(builder: IRBuilder, typ: TypeInfo, line: int) -> Value:
+# This function still does not support the following imports.
+# import json as _json
+# from json import decoder
+# Using either _json.JSONDecoder or decoder.JSONDecoder as a type hint for a dataclass field will fail.
+# See issue mypyc/mypyc#1099.
+def load_type(builder: IRBuilder, typ: TypeInfo, unbounded_type: Type | None, line: int) -> Value:
+    # typ.fullname contains the module where the class object was defined. However, it is possible
+    # that the class object's module was not imported in the file currently being compiled. So, we
+    # use unbounded_type.name (if provided by caller) to load the class object through one of the
+    # imported modules.
+    # Example: for `json.JSONDecoder`, typ.fullname is `json.decoder.JSONDecoder` but the Python
+    # file may import `json` not `json.decoder`.
+    # Another corner case: The Python file being compiled imports mod1 and has a type hint
+    # `mod1.OuterClass.InnerClass`. But, mod1/__init__.py might import OuterClass like this:
+    # `from mod2.mod3 import OuterClass`. In this case, typ.fullname is
+    # `mod2.mod3.OuterClass.InnerClass` and `unbounded_type.name` is `mod1.OuterClass.InnerClass`.
+    # So, we must use unbounded_type.name to load the class object.
+    # See issue mypyc/mypyc#1087.
+    load_attr_path = (
+        unbounded_type.name if isinstance(unbounded_type, UnboundType) else typ.fullname
+    ).removesuffix(f".{typ.name}")
     if typ in builder.mapper.type_to_ir:
         class_ir = builder.mapper.type_to_ir[typ]
         class_obj = builder.builder.get_native_type(class_ir)
     elif typ.fullname in builtin_names:
         builtin_addr_type, src = builtin_names[typ.fullname]
         class_obj = builder.add(LoadAddress(builtin_addr_type, src, line))
-    elif typ.module_name in builder.imports:
-        loaded_module = builder.load_module(typ.module_name)
+    # This elif-condition finds the longest import that matches the load_attr_path.
+    elif module_name := max(
+        (i for i in builder.imports if load_attr_path == i or load_attr_path.startswith(f"{i}.")),
+        default="",
+        key=len,
+    ):
+        # Load the imported module.
+        loaded_module = builder.load_module(module_name)
+        # Recursively load attributes of the imported module. These may be submodules, classes or
+        # any other object.
+        for attr in (
+            load_attr_path.removeprefix(f"{module_name}.").split(".")
+            if load_attr_path != module_name
+            else []
+        ):
+            loaded_module = builder.py_get_attr(loaded_module, attr, line)
         class_obj = builder.builder.get_attr(
             loaded_module, typ.name, object_rprimitive, line, borrow=False
         )
@@ -1039,7 +1073,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None:
         )
         registry = load_singledispatch_registry(builder, dispatch_func_obj, line)
         for typ in types:
-            loaded_type = load_type(builder, typ, line)
+            loaded_type = load_type(builder, typ, None, line)
             builder.primitive_op(dict_set_item_op, [registry, loaded_type, to_insert], line)
         dispatch_cache = builder.builder.get_attr(
             dispatch_func_obj, "dispatch_cache", dict_rprimitive, line
diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test
index 0c993d9ac336..ae0be03eb66b 100644
--- a/mypyc/test-data/commandline.test
+++ b/mypyc/test-data/commandline.test
@@ -261,3 +261,31 @@ print("imported foo")
 importing...
 imported foo
 done
+
+[case testImportFromInitPy]
+# cmd: foo.py
+import foo
+
+[file pkg2/__init__.py]
+
+[file pkg2/mod2.py]
+class A:
+    class B:
+        pass
+
+[file pkg1/__init__.py]
+from pkg2.mod2 import A
+
+[file foo.py]
+import pkg1
+from typing import TypedDict
+
+class Eggs(TypedDict):
+    obj1: pkg1.A.B
+
+print(type(Eggs(obj1=pkg1.A.B())["obj1"]).__name__)
+print(type(Eggs(obj1=pkg1.A.B())["obj1"]).__module__)
+
+[out]
+B
+pkg2.mod2
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index edf9e6bf1906..f8720383d7fb 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -78,17 +78,22 @@ assert hasattr(c, 'x')
 
 [case testTypedDictWithFields]
 import collections
+import json
 from typing import TypedDict
 class C(TypedDict):
     x: collections.deque
+    spam: json.JSONDecoder
 [file driver.py]
 from native import C
 from collections import deque
+from json import JSONDecoder
 
 print(C.__annotations__["x"] is deque)
+print(C.__annotations__["spam"] is JSONDecoder)
 [typing fixtures/typing-full.pyi]
 [out]
 True
+True
 
 [case testClassWithDeletableAttributes]
 from typing import Any, cast

From 99e26883370f843c539077624fe8981d64d8d92f Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Fri, 18 Apr 2025 15:31:45 -0700
Subject: [PATCH 1254/1617] Make some parse errors non-blocking (#18941)

I made the argument explicit and left blocker=True for some ones I
didn't check
---
 mypy/fastparse.py                   | 58 ++++++++++++++++++++++++-----
 test-data/unit/check-fastparse.test | 14 +++----
 2 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index a81241ec191a..aed04c6f2eb9 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -404,7 +404,7 @@ def __init__(
     def note(self, msg: str, line: int, column: int) -> None:
         self.errors.report(line, column, msg, severity="note", code=codes.SYNTAX)
 
-    def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None:
+    def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool) -> None:
         if blocker or not self.options.ignore_errors:
             # Make sure self.errors reflects any type ignores that we have parsed
             self.errors.set_file_ignored_lines(
@@ -945,7 +945,12 @@ def do_func_def(
                 ):
                     if n.returns:
                         # PEP 484 disallows both type annotations and type comments
-                        self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset)
+                        self.fail(
+                            message_registry.DUPLICATE_TYPE_SIGNATURES,
+                            lineno,
+                            n.col_offset,
+                            blocker=False,
+                        )
                     arg_types = [
                         (
                             a.type_annotation
@@ -957,7 +962,12 @@ def do_func_def(
                 else:
                     # PEP 484 disallows both type annotations and type comments
                     if n.returns or any(a.type_annotation is not None for a in args):
-                        self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset)
+                        self.fail(
+                            message_registry.DUPLICATE_TYPE_SIGNATURES,
+                            lineno,
+                            n.col_offset,
+                            blocker=False,
+                        )
                     translated_args: list[Type] = TypeConverter(
                         self.errors, line=lineno, override_column=n.col_offset
                     ).translate_expr_list(func_type_ast.argtypes)
@@ -972,7 +982,7 @@ def do_func_def(
             except SyntaxError:
                 stripped_type = n.type_comment.split("#", 2)[0].strip()
                 err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type)
-                self.fail(err_msg, lineno, n.col_offset)
+                self.fail(err_msg, lineno, n.col_offset, blocker=False)
                 if n.type_comment and n.type_comment[0] not in ["(", "#"]:
                     self.note(
                         "Suggestion: wrap argument types in parentheses", lineno, n.col_offset
@@ -994,7 +1004,12 @@ def do_func_def(
         func_type = None
         if any(arg_types) or return_type:
             if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types):
-                self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset)
+                self.fail(
+                    message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS,
+                    lineno,
+                    n.col_offset,
+                    blocker=False,
+                )
             elif len(arg_types) > len(arg_kinds):
                 self.fail(
                     message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS,
@@ -1121,7 +1136,12 @@ def make_argument(
             annotation = arg.annotation
             type_comment = arg.type_comment
             if annotation is not None and type_comment is not None:
-                self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, arg.lineno, arg.col_offset)
+                self.fail(
+                    message_registry.DUPLICATE_TYPE_SIGNATURES,
+                    arg.lineno,
+                    arg.col_offset,
+                    blocker=False,
+                )
             arg_type = None
             if annotation is not None:
                 arg_type = TypeConverter(self.errors, line=arg.lineno).visit(annotation)
@@ -1142,7 +1162,7 @@ def make_argument(
         return argument
 
     def fail_arg(self, msg: str, arg: ast3.arg) -> None:
-        self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset)
+        self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset, blocker=True)
 
     # ClassDef(identifier name,
     #  expr* bases,
@@ -1188,18 +1208,21 @@ def validate_type_param(self, type_param: ast_TypeVar) -> None:
                 message_registry.TYPE_VAR_YIELD_EXPRESSION_IN_BOUND,
                 type_param.lineno,
                 type_param.col_offset,
+                blocker=True,
             )
         if isinstance(incorrect_expr, ast3.NamedExpr):
             self.fail(
                 message_registry.TYPE_VAR_NAMED_EXPRESSION_IN_BOUND,
                 type_param.lineno,
                 type_param.col_offset,
+                blocker=True,
             )
         if isinstance(incorrect_expr, ast3.Await):
             self.fail(
                 message_registry.TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND,
                 type_param.lineno,
                 type_param.col_offset,
+                blocker=True,
             )
 
     def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]:
@@ -1814,11 +1837,26 @@ def validate_type_alias(self, n: ast_TypeAlias) -> None:
         if incorrect_expr is None:
             return
         if isinstance(incorrect_expr, (ast3.Yield, ast3.YieldFrom)):
-            self.fail(message_registry.TYPE_ALIAS_WITH_YIELD_EXPRESSION, n.lineno, n.col_offset)
+            self.fail(
+                message_registry.TYPE_ALIAS_WITH_YIELD_EXPRESSION,
+                n.lineno,
+                n.col_offset,
+                blocker=True,
+            )
         if isinstance(incorrect_expr, ast3.NamedExpr):
-            self.fail(message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION, n.lineno, n.col_offset)
+            self.fail(
+                message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION,
+                n.lineno,
+                n.col_offset,
+                blocker=True,
+            )
         if isinstance(incorrect_expr, ast3.Await):
-            self.fail(message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION, n.lineno, n.col_offset)
+            self.fail(
+                message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION,
+                n.lineno,
+                n.col_offset,
+                blocker=True,
+            )
 
     # TypeAlias(identifier name, type_param* type_params, expr value)
     def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt:
diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test
index 534967b1edbf..f93e4fe07218 100644
--- a/test-data/unit/check-fastparse.test
+++ b/test-data/unit/check-fastparse.test
@@ -241,37 +241,37 @@ assert 1, f()  # E: Name "f" is not defined
 
 [case testFastParserConsistentFunctionTypes]
 
-def f(x, y, z):
+def f1(x, y, z):
   # type: (int, int, int) -> int
   pass
 
-def f(x,  # type: int  # E: Function has duplicate type signatures
+def f2(x,  # type: int  # E: Function has duplicate type signatures
       y,  # type: int
       z   # type: int
     ):
     # type: (int, int, int) -> int
     pass
 
-def f(x,  # type: int
+def f3(x,  # type: int
       y,  # type: int
       z   # type: int
     ):
     # type: (...) -> int
     pass
 
-def f(x, y, z):
+def f4(x, y, z):
   # type: (int, int, int) -> int
   pass
 
-def f(x) -> int:  # E: Function has duplicate type signatures
+def f5(x) -> int:  # E: Function has duplicate type signatures
   # type: (int) -> int
   pass
 
-def f(x: int, y: int, z: int):
+def f6(x: int, y: int, z: int):
   # type: (...) -> int
   pass
 
-def f(x: int):  # E: Function has duplicate type signatures
+def f7(x: int):  # E: Function has duplicate type signatures
   # type: (int) -> int
   pass
 

From 057f8ad6b5298ae44e11c2e3592b1b14cd21b414 Mon Sep 17 00:00:00 2001
From: Anthony Sottile 
Date: Mon, 21 Apr 2025 23:17:51 -0400
Subject: [PATCH 1255/1617] use is_same_type when determining if a cast is
 redundant (#18588)

while working on #18540 (which my original prototype based the code on
`warn-redundant-casts`) I noticed the suggestion
[here](https://github.com/python/mypy/issues/18540#issuecomment-2615574503)
would probably make sense to apply to redundant-cast as well!

I also made sure to include the example from the [original
implementation](https://github.com/python/mypy/pull/1705#issue-159944226)
just to make sure I wasn't regressing that as well since it seemed
related.
---
 mypy/checkexpr.py                  |  4 ++--
 test-data/unit/check-warnings.test | 26 ++++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 099e151dd33d..e7c2cba3fc55 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4696,8 +4696,8 @@ def visit_cast_expr(self, expr: CastExpr) -> Type:
         options = self.chk.options
         if (
             options.warn_redundant_casts
-            and not isinstance(get_proper_type(target_type), AnyType)
-            and source_type == target_type
+            and not is_same_type(target_type, AnyType(TypeOfAny.special_form))
+            and is_same_type(source_type, target_type)
         ):
             self.msg.redundant_cast(target_type, expr)
         if options.disallow_any_unimported and has_any_from_unimported_type(target_type):
diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test
index 90f40777d6b7..895b16e5e3c3 100644
--- a/test-data/unit/check-warnings.test
+++ b/test-data/unit/check-warnings.test
@@ -42,6 +42,32 @@ a: Any
 b = cast(Any, a)
 [builtins fixtures/list.pyi]
 
+[case testCastToObjectNotRedunant]
+# flags: --warn-redundant-casts
+from typing import cast
+
+a = 1
+b = cast(object, 1)
+
+[case testCastFromLiteralRedundant]
+# flags: --warn-redundant-casts
+from typing import cast
+
+cast(int, 1)
+[out]
+main:4: error: Redundant cast to "int"
+
+[case testCastFromUnionOfAnyOk]
+# flags: --warn-redundant-casts
+from typing import Any, cast, Union
+
+x = Any
+y = Any
+z = Any
+
+def f(q: Union[x, y, z]) -> None:
+    cast(Union[x, y], q)
+
 -- Unused 'type: ignore' comments
 -- ------------------------------
 

From c2749716b21e319277b49d7196c11f0c32a3c6eb Mon Sep 17 00:00:00 2001
From: "Michael J. Sullivan" 
Date: Tue, 22 Apr 2025 02:20:15 -0700
Subject: [PATCH 1256/1617] [mypyc] Support yields while values are live
 (#16305)

Also support await while temporary values are live.

---------

Co-authored-by: Jukka Lehtosalo 
---
 mypyc/analysis/dataflow.py          |  20 +++--
 mypyc/codegen/emitmodule.py         |  11 +++
 mypyc/ir/class_ir.py                |   7 ++
 mypyc/ir/ops.py                     | 132 +++++++++++++++++++++++++++-
 mypyc/irbuild/function.py           |   1 +
 mypyc/irbuild/generator.py          |   2 +
 mypyc/irbuild/statement.py          |   2 +-
 mypyc/test-data/run-async.test      | 104 ++++++++++++++++++++--
 mypyc/test-data/run-generators.test |  17 ++++
 mypyc/transform/spill.py            | 102 +++++++++++++++++++++
 mypyc/transform/uninit.py           |  13 ++-
 11 files changed, 392 insertions(+), 19 deletions(-)
 create mode 100644 mypyc/transform/spill.py

diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py
index 0657261e7a8f..db62ef1700fa 100644
--- a/mypyc/analysis/dataflow.py
+++ b/mypyc/analysis/dataflow.py
@@ -17,6 +17,7 @@
     Cast,
     ComparisonOp,
     ControlOp,
+    DecRef,
     Extend,
     Float,
     FloatComparisonOp,
@@ -25,6 +26,7 @@
     GetAttr,
     GetElementPtr,
     Goto,
+    IncRef,
     InitStatic,
     Integer,
     IntOp,
@@ -77,12 +79,11 @@ def __str__(self) -> str:
         return f"exits: {exits}\nsucc: {self.succ}\npred: {self.pred}"
 
 
-def get_cfg(blocks: list[BasicBlock]) -> CFG:
+def get_cfg(blocks: list[BasicBlock], *, use_yields: bool = False) -> CFG:
     """Calculate basic block control-flow graph.
 
-    The result is a dictionary like this:
-
-         basic block index -> (successors blocks, predecesssor blocks)
+    If use_yields is set, then we treat returns inserted by yields as gotos
+    instead of exits.
     """
     succ_map = {}
     pred_map: dict[BasicBlock, list[BasicBlock]] = {}
@@ -92,7 +93,10 @@ def get_cfg(blocks: list[BasicBlock]) -> CFG:
             isinstance(op, ControlOp) for op in block.ops[:-1]
         ), "Control-flow ops must be at the end of blocks"
 
-        succ = list(block.terminator.targets())
+        if use_yields and isinstance(block.terminator, Return) and block.terminator.yield_target:
+            succ = [block.terminator.yield_target]
+        else:
+            succ = list(block.terminator.targets())
         if not succ:
             exits.add(block)
 
@@ -474,6 +478,12 @@ def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]:
     def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
         return non_trivial_sources(op), set()
 
+    def visit_inc_ref(self, op: IncRef) -> GenAndKill[Value]:
+        return set(), set()
+
+    def visit_dec_ref(self, op: DecRef) -> GenAndKill[Value]:
+        return set(), set()
+
 
 def analyze_live_regs(blocks: list[BasicBlock], cfg: CFG) -> AnalysisResult[Value]:
     """Calculate live registers at each CFG location.
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 713fa5c51fa1..b8a19ac1d669 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -61,6 +61,7 @@
 from mypyc.transform.flag_elimination import do_flag_elimination
 from mypyc.transform.lower import lower_ir
 from mypyc.transform.refcount import insert_ref_count_opcodes
+from mypyc.transform.spill import insert_spills
 from mypyc.transform.uninit import insert_uninit_checks
 
 # All of the modules being compiled are divided into "groups". A group
@@ -228,6 +229,12 @@ def compile_scc_to_ir(
     if errors.num_errors > 0:
         return modules
 
+    env_user_functions = {}
+    for module in modules.values():
+        for cls in module.classes:
+            if cls.env_user_function:
+                env_user_functions[cls.env_user_function] = cls
+
     for module in modules.values():
         for fn in module.functions:
             # Insert uninit checks.
@@ -236,6 +243,10 @@ def compile_scc_to_ir(
             insert_exception_handling(fn)
             # Insert refcount handling.
             insert_ref_count_opcodes(fn)
+
+            if fn in env_user_functions:
+                insert_spills(fn, env_user_functions[fn])
+
             # Switch to lower abstraction level IR.
             lower_ir(fn, compiler_options)
             # Perform optimizations.
diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py
index 94181e115145..d18f15f667c8 100644
--- a/mypyc/ir/class_ir.py
+++ b/mypyc/ir/class_ir.py
@@ -196,6 +196,9 @@ def __init__(
         # value of an attribute is the same as the error value.
         self.bitmap_attrs: list[str] = []
 
+        # If this is a generator environment class, what is the actual method for it
+        self.env_user_function: FuncIR | None = None
+
     def __repr__(self) -> str:
         return (
             "ClassIR("
@@ -394,6 +397,7 @@ def serialize(self) -> JsonDict:
             "_always_initialized_attrs": sorted(self._always_initialized_attrs),
             "_sometimes_initialized_attrs": sorted(self._sometimes_initialized_attrs),
             "init_self_leak": self.init_self_leak,
+            "env_user_function": self.env_user_function.id if self.env_user_function else None,
         }
 
     @classmethod
@@ -446,6 +450,9 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR:
         ir._always_initialized_attrs = set(data["_always_initialized_attrs"])
         ir._sometimes_initialized_attrs = set(data["_sometimes_initialized_attrs"])
         ir.init_self_leak = data["init_self_leak"]
+        ir.env_user_function = (
+            ctx.functions[data["env_user_function"]] if data["env_user_function"] else None
+        )
 
         return ir
 
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 0323d31d0605..eec9c34a965e 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -236,6 +236,10 @@ def can_raise(self) -> bool:
     def sources(self) -> list[Value]:
         """All the values the op may read."""
 
+    @abstractmethod
+    def set_sources(self, new: list[Value]) -> None:
+        """Rewrite the sources of an op"""
+
     def stolen(self) -> list[Value]:
         """Return arguments that have a reference count stolen by this op"""
         return []
@@ -272,6 +276,9 @@ def __init__(self, dest: Register, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         return [self.src]
 
@@ -302,6 +309,9 @@ def __init__(self, dest: Register, src: list[Value], line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return self.src.copy()
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.src = new[:]
+
     def stolen(self) -> list[Value]:
         return []
 
@@ -343,6 +353,9 @@ def __repr__(self) -> str:
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_goto(self)
 
@@ -403,6 +416,9 @@ def set_target(self, i: int, new: BasicBlock) -> None:
     def sources(self) -> list[Value]:
         return [self.value]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.value,) = new
+
     def invert(self) -> None:
         self.negated = not self.negated
 
@@ -415,13 +431,23 @@ class Return(ControlOp):
 
     error_kind = ERR_NEVER
 
-    def __init__(self, value: Value, line: int = -1) -> None:
+    def __init__(
+        self, value: Value, line: int = -1, *, yield_target: BasicBlock | None = None
+    ) -> None:
         super().__init__(line)
         self.value = value
+        # If this return is created by a yield, keep track of the next
+        # basic block. This doesn't affect the code we generate but
+        # can feed into analysis that need to understand the
+        # *original* CFG.
+        self.yield_target = yield_target
 
     def sources(self) -> list[Value]:
         return [self.value]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.value,) = new
+
     def stolen(self) -> list[Value]:
         return [self.value]
 
@@ -453,6 +479,9 @@ def __init__(self, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_unreachable(self)
 
@@ -495,6 +524,9 @@ def __init__(self, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_inc_ref(self)
 
@@ -520,6 +552,9 @@ def __repr__(self) -> str:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_dec_ref(self)
 
@@ -545,6 +580,9 @@ def __init__(self, fn: FuncDecl, args: Sequence[Value], line: int) -> None:
     def sources(self) -> list[Value]:
         return list(self.args.copy())
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.args = new[:]
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_call(self)
 
@@ -573,6 +611,9 @@ def __init__(self, obj: Value, method: str, args: list[Value], line: int = -1) -
     def sources(self) -> list[Value]:
         return self.args.copy() + [self.obj]
 
+    def set_sources(self, new: list[Value]) -> None:
+        *self.args, self.obj = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_method_call(self)
 
@@ -651,6 +692,9 @@ def __init__(self, args: list[Value], desc: PrimitiveDescription, line: int = -1
     def sources(self) -> list[Value]:
         return self.args
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.args = new[:]
+
     def stolen(self) -> list[Value]:
         steals = self.desc.steals
         if isinstance(steals, list):
@@ -686,6 +730,9 @@ def __init__(
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_error_value(self)
 
@@ -718,6 +765,9 @@ def __init__(self, value: LiteralValue, rtype: RType) -> None:
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_literal(self)
 
@@ -742,6 +792,9 @@ def __init__(self, obj: Value, attr: str, line: int, *, borrow: bool = False) ->
     def sources(self) -> list[Value]:
         return [self.obj]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.obj,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_get_attr(self)
 
@@ -774,6 +827,9 @@ def mark_as_initializer(self) -> None:
     def sources(self) -> list[Value]:
         return [self.obj, self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.obj, self.src = new
+
     def stolen(self) -> list[Value]:
         return [self.src]
 
@@ -827,6 +883,9 @@ def __init__(
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_static(self)
 
@@ -856,6 +915,9 @@ def __init__(
     def sources(self) -> list[Value]:
         return [self.value]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.value,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_init_static(self)
 
@@ -885,6 +947,9 @@ def sources(self) -> list[Value]:
     def stolen(self) -> list[Value]:
         return self.items.copy()
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.items = new[:]
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_tuple_set(self)
 
@@ -906,6 +971,9 @@ def __init__(self, src: Value, index: int, line: int = -1, *, borrow: bool = Fal
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_tuple_get(self)
 
@@ -929,6 +997,9 @@ def __init__(self, src: Value, typ: RType, line: int, *, borrow: bool = False) -
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         if self.is_borrowed:
             return []
@@ -962,6 +1033,9 @@ def __init__(self, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         return [self.src]
 
@@ -988,6 +1062,9 @@ def __init__(self, src: Value, typ: RType, line: int) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_unbox(self)
 
@@ -1020,6 +1097,9 @@ def __init__(self, class_name: str, value: str | Value | None, line: int) -> Non
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_raise_standard_error(self)
 
@@ -1066,7 +1146,10 @@ def __init__(
             assert error_kind == ERR_NEVER
 
     def sources(self) -> list[Value]:
-        return self.args
+        return self.args[:]
+
+    def set_sources(self, new: list[Value]) -> None:
+        self.args = new[:]
 
     def stolen(self) -> list[Value]:
         if isinstance(self.steals, list):
@@ -1099,6 +1182,9 @@ def __init__(self, src: Value, dst_type: RType, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         return []
 
@@ -1130,6 +1216,9 @@ def __init__(self, src: Value, dst_type: RType, signed: bool, line: int = -1) ->
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         return []
 
@@ -1157,6 +1246,9 @@ def __init__(self, type: RType, identifier: str, line: int = -1, ann: object = N
     def sources(self) -> list[Value]:
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        assert not new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_global(self)
 
@@ -1213,6 +1305,9 @@ def __init__(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1)
     def sources(self) -> list[Value]:
         return [self.lhs, self.rhs]
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.lhs, self.rhs = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_int_op(self)
 
@@ -1276,6 +1371,9 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.lhs, self.rhs]
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.lhs, self.rhs = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_comparison_op(self)
 
@@ -1309,6 +1407,9 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.lhs, self.rhs]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.lhs, self.rhs) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_float_op(self)
 
@@ -1331,6 +1432,9 @@ def __init__(self, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_float_neg(self)
 
@@ -1359,6 +1463,9 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.lhs, self.rhs]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.lhs, self.rhs) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_float_comparison_op(self)
 
@@ -1390,6 +1497,9 @@ def __init__(self, type: RType, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_mem(self)
 
@@ -1415,6 +1525,9 @@ def __init__(self, type: RType, dest: Value, src: Value, line: int = -1) -> None
     def sources(self) -> list[Value]:
         return [self.src, self.dest]
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.src, self.dest = new
+
     def stolen(self) -> list[Value]:
         return [self.src]
 
@@ -1441,6 +1554,9 @@ def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> N
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_get_element_ptr(self)
 
@@ -1469,6 +1585,12 @@ def sources(self) -> list[Value]:
         else:
             return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        if new:
+            assert isinstance(new[0], Register)
+            assert len(new) == 1
+            self.src = new[0]
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_address(self)
 
@@ -1513,6 +1635,9 @@ def stolen(self) -> list[Value]:
             return self.src.copy()
         return []
 
+    def set_sources(self, new: list[Value]) -> None:
+        self.src = new[:]
+
     def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_keep_alive(self)
 
@@ -1553,6 +1678,9 @@ def __init__(self, src: Value, line: int = -1) -> None:
     def sources(self) -> list[Value]:
         return [self.src]
 
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
     def stolen(self) -> list[Value]:
         return []
 
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index ef9ec845f8f6..70e494f063b8 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -270,6 +270,7 @@ def c() -> None:
         # Re-enter the FuncItem and visit the body of the function this time.
         builder.enter(fn_info)
         setup_env_for_generator_class(builder)
+
         load_outer_envs(builder, builder.fn_info.generator_class)
         top_level = builder.top_level_fn_info()
         if (
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 92f9abff467c..bc61c4493d55 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -181,6 +181,8 @@ def add_helper_to_generator_class(
     )
     fn_info.generator_class.ir.methods["__mypyc_generator_helper__"] = helper_fn_ir
     builder.functions.append(helper_fn_ir)
+    fn_info.env_class.env_user_function = helper_fn_ir
+
     return helper_fn_decl
 
 
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index f5b65bedbbca..b109d925558b 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -905,7 +905,7 @@ def emit_yield(builder: IRBuilder, val: Value, line: int) -> Value:
     next_label = len(cls.continuation_blocks)
     cls.continuation_blocks.append(next_block)
     builder.assign(cls.next_label_target, Integer(next_label), line)
-    builder.add(Return(retval))
+    builder.add(Return(retval, yield_target=next_block))
     builder.activate_block(next_block)
 
     add_raise_exception_blocks_to_generator_class(builder, line)
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 8488632e6574..89d661900de0 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -1,6 +1,6 @@
 # async test cases (compile and run)
 
-[case testAsync]
+[case testRunAsyncBasics]
 import asyncio
 
 async def h() -> int:
@@ -11,19 +11,110 @@ async def g() -> int:
     return await h()
 
 async def f() -> int:
-    return await g()
+    return await g() + 2
+
+async def f2() -> int:
+    x = 0
+    for i in range(2):
+        x += i + await f() + await g()
+    return x
+
+def test_1() -> None:
+    result = asyncio.run(f())
+    assert result == 3
+
+def test_2() -> None:
+    result = asyncio.run(f2())
+    assert result == 9
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
+# eh, we could use the real type but it doesn't seem important
+def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
 
-[file driver.py]
-from native import f
+[case testRunAsyncAwaitInVariousPositions]
+from typing import cast, Any
+
 import asyncio
 
-result = asyncio.run(f())
-assert result == 1
+async def one() -> int:
+    await asyncio.sleep(0.0)
+    return int() + 1
+
+async def true() -> bool:
+    return bool(int() + await one())
+
+async def branch_await() -> int:
+    if bool(int() + 1) == await true():
+        return 3
+    return 2
+
+async def branch_await_not() -> int:
+    if bool(int() + 1) == (not await true()):
+        return 3
+    return 2
+
+def test_branch() -> None:
+    assert asyncio.run(branch_await()) == 3
+    assert asyncio.run(branch_await_not()) == 2
+
+async def assign_multi() -> int:
+    _, x = int(), await one()
+    return x + 1
+
+def test_assign_multi() -> None:
+    assert asyncio.run(assign_multi()) == 2
+
+class C:
+    def __init__(self, s: str) -> None:
+        self.s = s
+
+    def concat(self, s: str) -> str:
+        return self.s + s
+
+async def concat(s: str, t: str) -> str:
+    await one()
+    return s + t
+
+def concat2(x: str, y: str) -> str:
+    return x + y
+
+async def call1(s: str) -> str:
+    return concat2(str(int()), await concat(s, "a"))
+
+async def call2(s: str) -> str:
+    return await concat(str(int()), await concat(s, "b"))
+
+def test_call() -> None:
+    assert asyncio.run(call1("foo")) == "0fooa"
+    assert asyncio.run(call2("foo")) == "0foob"
+
+async def method_call(s: str) -> str:
+    return C("<").concat(await concat(s, ">"))
+
+def test_method_call() -> None:
+    assert asyncio.run(method_call("foo")) == ""
+
+class D:
+    def __init__(self, a: str, b: str) -> None:
+        self.a = a
+        self.b = b
+
+async def construct(s: str) -> str:
+    c = D(await concat(s, "!"), await concat(s, "?"))
+    return c.a + c.b
+
+def test_construct() -> None:
+    assert asyncio.run(construct("foo")) == "foo!foo?"
+
+[file asyncio/__init__.pyi]
+async def sleep(t: float) -> None: ...
+# eh, we could use the real type but it doesn't seem important
+def run(x: object) -> object: ...
+
+[typing fixtures/typing-full.pyi]
 
 [case testAsyncWith]
 from testutil import async_val
@@ -68,7 +159,6 @@ yields, val = run_generator(async_return())
 assert yields == ('foo',)
 assert val == 'test', val
 
-
 [case testAsyncFor]
 from typing import AsyncIterable, List, Set, Dict
 
diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test
index 7e9804c49582..2e55ded76f74 100644
--- a/mypyc/test-data/run-generators.test
+++ b/mypyc/test-data/run-generators.test
@@ -680,3 +680,20 @@ def test_basic() -> None:
     with context:
         assert context.x == 1
     assert context.x == 0
+
+
+[case testYieldSpill]
+from typing import Generator
+from testutil import run_generator
+
+def f() -> int:
+    return 1
+
+def yield_spill() -> Generator[str, int, int]:
+    return f() + (yield "foo")
+
+def test_basic() -> None:
+    x = run_generator(yield_spill(), [2])
+    yields, val = x
+    assert yields == ('foo',)
+    assert val == 3, val
diff --git a/mypyc/transform/spill.py b/mypyc/transform/spill.py
new file mode 100644
index 000000000000..331f1d3c1536
--- /dev/null
+++ b/mypyc/transform/spill.py
@@ -0,0 +1,102 @@
+"""Insert spills for values that are live across yields."""
+
+from __future__ import annotations
+
+from mypyc.analysis.dataflow import AnalysisResult, analyze_live_regs, get_cfg
+from mypyc.common import TEMP_ATTR_NAME
+from mypyc.ir.class_ir import ClassIR
+from mypyc.ir.func_ir import FuncIR
+from mypyc.ir.ops import (
+    BasicBlock,
+    Branch,
+    DecRef,
+    GetAttr,
+    IncRef,
+    LoadErrorValue,
+    Register,
+    SetAttr,
+    Value,
+)
+
+
+def insert_spills(ir: FuncIR, env: ClassIR) -> None:
+    cfg = get_cfg(ir.blocks, use_yields=True)
+    live = analyze_live_regs(ir.blocks, cfg)
+    entry_live = live.before[ir.blocks[0], 0]
+
+    entry_live = {op for op in entry_live if not (isinstance(op, Register) and op.is_arg)}
+    # TODO: Actually for now, no Registers at all -- we keep the manual spills
+    entry_live = {op for op in entry_live if not isinstance(op, Register)}
+
+    ir.blocks = spill_regs(ir.blocks, env, entry_live, live)
+
+
+def spill_regs(
+    blocks: list[BasicBlock], env: ClassIR, to_spill: set[Value], live: AnalysisResult[Value]
+) -> list[BasicBlock]:
+    for op in blocks[0].ops:
+        if isinstance(op, GetAttr) and op.attr == "__mypyc_env__":
+            env_reg = op
+            break
+    else:
+        raise AssertionError("could not find __mypyc_env__")
+
+    spill_locs = {}
+    for i, val in enumerate(to_spill):
+        name = f"{TEMP_ATTR_NAME}2_{i}"
+        env.attributes[name] = val.type
+        spill_locs[val] = name
+
+    for block in blocks:
+        ops = block.ops
+        block.ops = []
+
+        for i, op in enumerate(ops):
+            to_decref = []
+
+            if isinstance(op, IncRef) and op.src in spill_locs:
+                raise AssertionError("not sure what to do with an incref of a spill...")
+            if isinstance(op, DecRef) and op.src in spill_locs:
+                # When we decref a spilled value, we turn that into
+                # NULLing out the attribute, but only if the spilled
+                # value is not live *when we include yields in the
+                # CFG*. (The original decrefs are computed without that.)
+                #
+                # We also skip a decref is the env register is not
+                # live. That should only happen when an exception is
+                # being raised, so everything should be handled there.
+                if op.src not in live.after[block, i] and env_reg in live.after[block, i]:
+                    # Skip the DecRef but null out the spilled location
+                    null = LoadErrorValue(op.src.type)
+                    block.ops.extend([null, SetAttr(env_reg, spill_locs[op.src], null, op.line)])
+                continue
+
+            if (
+                any(src in spill_locs for src in op.sources())
+                # N.B: IS_ERROR should be before a spill happens
+                # XXX: but could we have a regular branch?
+                and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
+            ):
+                new_sources: list[Value] = []
+                for src in op.sources():
+                    if src in spill_locs:
+                        read = GetAttr(env_reg, spill_locs[src], op.line)
+                        block.ops.append(read)
+                        new_sources.append(read)
+                        if src.type.is_refcounted:
+                            to_decref.append(read)
+                    else:
+                        new_sources.append(src)
+
+                op.set_sources(new_sources)
+
+            block.ops.append(op)
+
+            for dec in to_decref:
+                block.ops.append(DecRef(dec))
+
+            if op in spill_locs:
+                # XXX: could we set uninit?
+                block.ops.append(SetAttr(env_reg, spill_locs[op], op, op.line))
+
+    return blocks
diff --git a/mypyc/transform/uninit.py b/mypyc/transform/uninit.py
index 6bf71ac4a8bc..45b403588f8e 100644
--- a/mypyc/transform/uninit.py
+++ b/mypyc/transform/uninit.py
@@ -69,14 +69,19 @@ def split_blocks_at_uninits(
                     and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
                     and not isinstance(op, LoadAddress)
                 ):
-                    new_block, error_block = BasicBlock(), BasicBlock()
-                    new_block.error_handler = error_block.error_handler = cur_block.error_handler
-                    new_blocks += [error_block, new_block]
-
                     if src not in init_registers_set:
                         init_registers.append(src)
                         init_registers_set.add(src)
 
+                    # XXX: if src.name is empty, it should be a
+                    # temp... and it should be OK??
+                    if not src.name:
+                        continue
+
+                    new_block, error_block = BasicBlock(), BasicBlock()
+                    new_block.error_handler = error_block.error_handler = cur_block.error_handler
+                    new_blocks += [error_block, new_block]
+
                     if not src.type.error_overlap:
                         cur_block.ops.append(
                             Branch(

From 380cb8de74ee239489d1c3ae961eaee2d9a19de5 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 23 Apr 2025 00:18:11 +0200
Subject: [PATCH 1257/1617] Update math error messages for 3.14 (2) (#18949)

Followup to #18534

Some more error messages for math functions were changed for Python
3.14, see https://github.com/python/cpython/pull/129497.

Fixes `mypyc/test/test_run.py::TestRun::run-math.test::testMathOps`
---
 mypyc/lib-rt/float_ops.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/mypyc/lib-rt/float_ops.c b/mypyc/lib-rt/float_ops.c
index 48ebc44431da..319065742559 100644
--- a/mypyc/lib-rt/float_ops.c
+++ b/mypyc/lib-rt/float_ops.c
@@ -34,6 +34,15 @@ static double CPy_MathExpectedPositiveInputError(double x) {
     return CPY_FLOAT_ERROR;
 }
 
+static double CPy_MathExpectedFiniteInput(double x) {
+    char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL);
+    if (buf) {
+        PyErr_Format(PyExc_ValueError, "expected a finite input, got %s", buf);
+        PyMem_Free(buf);
+    }
+    return CPY_FLOAT_ERROR;
+}
+
 double CPyFloat_FromTagged(CPyTagged x) {
     if (CPyTagged_CheckShort(x)) {
         return CPyTagged_ShortAsSsize_t(x);
@@ -48,7 +57,11 @@ double CPyFloat_FromTagged(CPyTagged x) {
 double CPyFloat_Sin(double x) {
     double v = sin(x);
     if (unlikely(isnan(v)) && !isnan(x)) {
+#if CPY_3_14_FEATURES
+        return CPy_MathExpectedFiniteInput(x);
+#else
         return CPy_DomainError();
+#endif
     }
     return v;
 }
@@ -56,14 +69,22 @@ double CPyFloat_Sin(double x) {
 double CPyFloat_Cos(double x) {
     double v = cos(x);
     if (unlikely(isnan(v)) && !isnan(x)) {
+#if CPY_3_14_FEATURES
+        return CPy_MathExpectedFiniteInput(x);
+#else
         return CPy_DomainError();
+#endif
     }
     return v;
 }
 
 double CPyFloat_Tan(double x) {
     if (unlikely(isinf(x))) {
+#if CPY_3_14_FEATURES
+        return CPy_MathExpectedFiniteInput(x);
+#else
         return CPy_DomainError();
+#endif
     }
     return tan(x);
 }

From 6646ee077a7ce936fdc34d2aafcf971fabfb597f Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 23 Apr 2025 13:50:29 +0100
Subject: [PATCH 1258/1617] [mypyc] Add hidden flag to skip the generation of C
 files (#18955)

This can be useful when debugging mypyc issues. For example, you can
manually add some debug prints to the generated C and rerun mypyc
with `--skip-c-gen`. Now mypyc will build the C code again, with your
manual changes included (this assumes everything else is the same as
during the previous run).

I'm not planning to advertise this as an end-user feature.
---
 mypy/main.py           |  5 +++++
 mypy/options.py        |  3 +++
 mypyc/build.py         |  3 ++-
 mypyc/doc/dev-intro.md | 19 +++++++++++++++++++
 4 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/mypy/main.py b/mypy/main.py
index e5afb05e873b..4d9cec63bbc1 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1128,6 +1128,11 @@ def add_invertible_flag(
     report_group.add_argument(
         "-a", dest="mypyc_annotation_file", type=str, default=None, help=argparse.SUPPRESS
     )
+    # Hidden mypyc feature: do not write any C files (keep existing ones and assume they exist).
+    # This can be useful when debugging mypyc bugs.
+    report_group.add_argument(
+        "--skip-c-gen", dest="mypyc_skip_c_generation", action="store_true", help=argparse.SUPPRESS
+    )
 
     other_group = parser.add_argument_group(title="Miscellaneous")
     other_group.add_argument("--quickstart-file", help=argparse.SUPPRESS)
diff --git a/mypy/options.py b/mypy/options.py
index 17fea6b0bf29..c086dfc8aea3 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -408,6 +408,9 @@ def __init__(self) -> None:
 
         # Output html file for mypyc -a
         self.mypyc_annotation_file: str | None = None
+        # Skip writing C output files, but perform all other steps of a build (allows
+        # preserving manual tweaks to generated C file)
+        self.mypyc_skip_c_generation = False
 
     def use_lowercase_names(self) -> bool:
         if self.python_version >= (3, 9):
diff --git a/mypyc/build.py b/mypyc/build.py
index 1a74d4692d17..3bc38cb4dd90 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -452,7 +452,8 @@ def mypyc_build(
         cfilenames = []
         for cfile, ctext in cfiles:
             cfile = os.path.join(compiler_options.target_dir, cfile)
-            write_file(cfile, ctext)
+            if not options.mypyc_skip_c_generation:
+                write_file(cfile, ctext)
             if os.path.splitext(cfile)[1] == ".c":
                 cfilenames.append(cfile)
 
diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md
index 5f6c064dac37..5b248214a3eb 100644
--- a/mypyc/doc/dev-intro.md
+++ b/mypyc/doc/dev-intro.md
@@ -386,6 +386,25 @@ Test cases can also have a `[out]` section, which specifies the
 expected contents of stdout the test case should produce. New test
 cases should prefer assert statements to `[out]` sections.
 
+### Adding Debug Prints and Editing Generated C
+
+Sometimes it's helpful to add some debug prints or other debugging helpers
+to the generated C code. You can run mypyc using `--skip-c-gen` to skip the C
+generation step, so all manual changes to C files are preserved. Here is
+an example of how to use the workflow:
+
+* Compile some file you want to debug: `python -m mypyc foo.py`.
+* Add debug prints to the generated C in `build/__native.c`.
+* Run the same compilation command line again, but add `--skip-c-gen`:
+  `python -m mypyc --skip-c-gen foo.py`. This will only rebuild the
+  binaries.
+* Run the compiled code, including your changes: `python -c 'import foo'`.
+  You should now see the output from the debug prints you added.
+
+This can also be helpful if you want to quickly experiment with different
+implementation techniques, without having to first figure out how to
+modify mypyc to generate the desired C code.
+
 ### Debugging Segfaults
 
 If you experience a segfault, it's recommended to use a debugger that supports

From 4c5b03d6fba69e29d3f4086b40a7cdfbfce4b4f5 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 23 Apr 2025 16:36:40 +0100
Subject: [PATCH 1259/1617] [mypyc] Add some async tests (#18956)

Improve test coverage.
---
 mypyc/test-data/run-async.test | 79 +++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 2 deletions(-)

diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 89d661900de0..3ee39a613284 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -3,6 +3,8 @@
 [case testRunAsyncBasics]
 import asyncio
 
+from testutil import assertRaises
+
 async def h() -> int:
     return 1
 
@@ -19,14 +21,57 @@ async def f2() -> int:
         x += i + await f() + await g()
     return x
 
-def test_1() -> None:
+def test_simple_call() -> None:
     result = asyncio.run(f())
     assert result == 3
 
-def test_2() -> None:
+def test_multiple_awaits_in_expression() -> None:
     result = asyncio.run(f2())
     assert result == 9
 
+class MyError(Exception):
+    pass
+
+async def exc1() -> None:
+    await asyncio.sleep(0)
+    raise MyError()
+
+async def exc2() -> None:
+    await asyncio.sleep(0)
+    raise MyError()
+
+async def exc3() -> None:
+    await exc1()
+
+async def exc4() -> None:
+    await exc2()
+
+async def exc5() -> int:
+    try:
+        await exc1()
+    except MyError:
+        return 3
+    return 4
+
+async def exc6() -> int:
+    try:
+        await exc4()
+    except MyError:
+        return 3
+    return 4
+
+def test_exception() -> None:
+    with assertRaises(MyError):
+        asyncio.run(exc1())
+    with assertRaises(MyError):
+        asyncio.run(exc2())
+    with assertRaises(MyError):
+        asyncio.run(exc3())
+    with assertRaises(MyError):
+        asyncio.run(exc4())
+    assert asyncio.run(exc5()) == 3
+    assert asyncio.run(exc6()) == 3
+
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
 # eh, we could use the real type but it doesn't seem important
@@ -261,3 +306,33 @@ async def x() -> None:
 import asyncio
 import native
 asyncio.run(native.x())
+
+[case testRunAsyncSpecialCases]
+import asyncio
+
+async def t() -> tuple[int, str, str]:
+    return (1, "x", "y")
+
+async def f() -> tuple[int, str, str]:
+    return await t()
+
+def test_tuple_return() -> None:
+    result = asyncio.run(f())
+    assert result == (1, "x", "y")
+
+async def e() -> ValueError:
+    return ValueError("foo")
+
+async def g() -> ValueError:
+    return await e()
+
+def test_exception_return() -> None:
+    result = asyncio.run(g())
+    assert isinstance(result, ValueError)
+
+[file asyncio/__init__.pyi]
+async def sleep(t: float) -> None: ...
+# eh, we could use the real type but it doesn't seem important
+def run(x: object) -> object: ...
+
+[typing fixtures/typing-full.pyi]

From 281ee30811bde342970b49e81a9f8b9691cce1fb Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 23 Apr 2025 17:31:02 +0100
Subject: [PATCH 1260/1617] [mypyc] Fix reference count of spilled register in
 async def (#18957)

Fix segfault caused by an extra decref related to SetAttr, which steals
one of the operands. Reference count of a stolen op source must not
be decremented.

Add some tests that check that we don't leak memory in async functions.
---
 mypyc/test-data/run-async.test | 119 +++++++++++++++++++++++++++++++++
 mypyc/transform/spill.py       |   3 +-
 2 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 3ee39a613284..ee2018192ad4 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -119,10 +119,20 @@ class C:
     def concat(self, s: str) -> str:
         return self.s + s
 
+async def make_c(s: str) -> C:
+    await one()
+    return C(s)
+
 async def concat(s: str, t: str) -> str:
     await one()
     return s + t
 
+async def set_attr(s: str) -> None:
+    (await make_c("xyz")).s = await concat(s, "!")
+
+def test_set_attr() -> None:
+    asyncio.run(set_attr("foo"))  # Just check that it compiles and runs
+
 def concat2(x: str, y: str) -> str:
     return x + y
 
@@ -161,6 +171,7 @@ def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
 
+
 [case testAsyncWith]
 from testutil import async_val
 
@@ -336,3 +347,111 @@ async def sleep(t: float) -> None: ...
 def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
+
+[case testRunAsyncRefCounting]
+import asyncio
+import gc
+
+def assert_no_leaks(fn, max_new):
+    # Warm-up, in case asyncio allocates something on first use
+    asyncio.run(fn())
+
+    gc.collect()
+    old_objs = gc.get_objects()
+
+    for i in range(10):
+        asyncio.run(fn())
+
+    gc.collect()
+    new_objs = gc.get_objects()
+
+    delta = len(new_objs) - len(old_objs)
+    # Often a few persistent objects get allocated, which may be unavoidable.
+    # The main thing we care about is that each iteration does not leak an
+    # additional object.
+    assert delta <= max_new, delta
+
+async def concat_one(x: str) -> str:
+    return x + "1"
+
+async def foo(n: int) -> str:
+    s = ""
+    while len(s) < n:
+        s = await concat_one(s)
+    return s
+
+def test_trivial() -> None:
+    assert_no_leaks(lambda: foo(1000), 5)
+
+async def make_list(a: list[int]) -> list[int]:
+    await concat_one("foobar")
+    return [a[0]]
+
+async def spill() -> list[int]:
+    a: list[int] = []
+    for i in range(5):
+        await asyncio.sleep(0.0001)
+        a = (await make_list(a + [1])) + a + (await make_list(a + [2]))
+    return a
+
+async def bar(n: int) -> None:
+    for i in range(n):
+        await spill()
+
+def test_spilled() -> None:
+    assert_no_leaks(lambda: bar(40), 2)
+
+async def raise_deep(n: int) -> str:
+    if n == 0:
+        await asyncio.sleep(0.0001)
+        raise TypeError(str(n))
+    else:
+        if n == 2:
+            await asyncio.sleep(0.0001)
+        return await raise_deep(n - 1)
+
+async def maybe_raise(n: int) -> str:
+    if n % 3 == 0:
+        await raise_deep(5)
+    elif n % 29 == 0:
+        await asyncio.sleep(0.0001)
+    return str(n)
+
+async def exc(n: int) -> list[str]:
+    a = []
+    for i in range(n):
+        try:
+            a.append(str(int()) + await maybe_raise(n))
+        except TypeError:
+            a.append(str(int() + 5))
+    return a
+
+def test_exception() -> None:
+    assert_no_leaks(lambda: exc(50), 2)
+
+class C:
+    def __init__(self, s: str) -> None:
+        self.s = s
+
+async def id(c: C) -> C:
+    return c
+
+async def stolen_helper(c: C, s: str) -> str:
+    await asyncio.sleep(0.0001)
+    (await id(c)).s = await concat_one(s)
+    await asyncio.sleep(0.0001)
+    return c.s
+
+async def stolen(n: int) -> int:
+    for i in range(n):
+        c = C(str(i))
+        s = await stolen_helper(c, str(i + 2))
+        assert s == str(i + 2) + "1"
+    return n
+
+def test_stolen() -> None:
+    assert_no_leaks(lambda: stolen(100), 2)
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...
+async def sleep(t: float) -> None: ...
diff --git a/mypyc/transform/spill.py b/mypyc/transform/spill.py
index 331f1d3c1536..e2fb3e290ee4 100644
--- a/mypyc/transform/spill.py
+++ b/mypyc/transform/spill.py
@@ -78,12 +78,13 @@ def spill_regs(
                 and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
             ):
                 new_sources: list[Value] = []
+                stolen = op.stolen()
                 for src in op.sources():
                     if src in spill_locs:
                         read = GetAttr(env_reg, spill_locs[src], op.line)
                         block.ops.append(read)
                         new_sources.append(read)
-                        if src.type.is_refcounted:
+                        if src.type.is_refcounted and src not in stolen:
                             to_decref.append(read)
                     else:
                         new_sources.append(src)

From bbca30b7a45d9768e2f0826ee0a130baf35195e8 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 24 Apr 2025 11:59:26 +0100
Subject: [PATCH 1261/1617] [mypyc] Fix spilling values with overlapping error
 values (#18961)

Update spilling transform for async and generator functions to not
require a defined attribute bitfield. We now treat temporary spilled
values with overlapping error values as always defined. I'm not sure if
this would be safe to do for reference counted values, so we only do
this in cases where it's clearly fine.
---
 mypyc/test-data/run-async.test | 93 ++++++++++++++++++++++++++++++++++
 mypyc/transform/spill.py       |  4 ++
 2 files changed, 97 insertions(+)

diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index ee2018192ad4..3cbfb072278e 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -455,3 +455,96 @@ def test_stolen() -> None:
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
 async def sleep(t: float) -> None: ...
+
+[case testRunAsyncMiscTypesInEnvironment]
+import asyncio
+
+from mypy_extensions import i64, i32, i16, u8
+
+async def inc_float(x: float) -> float:
+    return x + 1.0
+
+async def inc_i64(x: i64) -> i64:
+    return x + 1
+
+async def inc_i32(x: i32) -> i32:
+    return x + 1
+
+async def inc_i16(x: i16) -> i16:
+    return x + 1
+
+async def inc_u8(x: u8) -> u8:
+    return x + 1
+
+async def inc_tuple(x: tuple[i64, float]) -> tuple[i64, float]:
+    return x[0] + 1, x[1] + 1.5
+
+async def neg_bool(b: bool) -> bool:
+    return not b
+
+async def float_ops(x: float) -> float:
+    n = x
+    n = await inc_float(n)
+    n = float("0.5") + await inc_float(n)
+    return n
+
+def test_float() -> None:
+    assert asyncio.run(float_ops(2.5)) == 5.0
+
+async def i64_ops(x: i64) -> i64:
+    n = x
+    n = await inc_i64(n)
+    n = i64("1") + await inc_i64(n)
+    return n
+
+def test_i64() -> None:
+    assert asyncio.run(i64_ops(2)) == 5
+
+async def i32_ops(x: i32) -> i32:
+    n = x
+    n = await inc_i32(n)
+    n = i32("1") + await inc_i32(n)
+    return n
+
+def test_i32() -> None:
+    assert asyncio.run(i32_ops(3)) == 6
+
+async def i16_ops(x: i16) -> i16:
+    n = x
+    n = await inc_i16(n)
+    n = i16("1") + await inc_i16(n)
+    return n
+
+def test_i16() -> None:
+    assert asyncio.run(i16_ops(4)) == 7
+
+async def u8_ops(x: u8) -> u8:
+    n = x
+    n = await inc_u8(n)
+    n = u8("1") + await inc_u8(n)
+    return n
+
+def test_u8() -> None:
+    assert asyncio.run(u8_ops(5)) == 8
+
+async def tuple_ops(x: tuple[i64, float]) -> tuple[i64, float]:
+    n = x
+    n = await inc_tuple(n)
+    m = ((i64("1"), float("0.5")), await inc_tuple(n))
+    return m[1]
+
+def test_tuple() -> None:
+    assert asyncio.run(tuple_ops((1, 2.5))) == (3, 5.5)
+
+async def bool_ops(x: bool) -> bool:
+    n = x
+    n = await neg_bool(n)
+    m = (bool("1"), await neg_bool(n))
+    return m[0] and m[1]
+
+def test_bool() -> None:
+    assert asyncio.run(bool_ops(True)) is True
+    assert asyncio.run(bool_ops(False)) is False
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...
diff --git a/mypyc/transform/spill.py b/mypyc/transform/spill.py
index e2fb3e290ee4..3c014ca2c0da 100644
--- a/mypyc/transform/spill.py
+++ b/mypyc/transform/spill.py
@@ -45,6 +45,10 @@ def spill_regs(
     for i, val in enumerate(to_spill):
         name = f"{TEMP_ATTR_NAME}2_{i}"
         env.attributes[name] = val.type
+        if val.type.error_overlap:
+            # We can safely treat as always initialized, since the type has no pointers.
+            # This way we also don't need to manage the defined attribute bitfield.
+            env._always_initialized_attrs.add(name)
         spill_locs[val] = name
 
     for block in blocks:

From 1ea9373a2eeed13b50dae92e9e73500a79a5c14f Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 24 Apr 2025 13:22:44 +0100
Subject: [PATCH 1262/1617] [mypyc] Add more comments about overlapping error
 values (#18963)

This is a pretty tricky feature, so let's document it better.
---
 mypyc/ir/class_ir.py           | 11 ++++++++---
 mypyc/ir/rtypes.py             | 17 +++++++++++++++--
 mypyc/test-data/run-async.test | 13 +++++++++++++
 3 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py
index d18f15f667c8..c88b9b0c7afc 100644
--- a/mypyc/ir/class_ir.py
+++ b/mypyc/ir/class_ir.py
@@ -180,7 +180,12 @@ def __init__(
         self.attrs_with_defaults: set[str] = set()
 
         # Attributes that are always initialized in __init__ or class body
-        # (inferred in mypyc.analysis.attrdefined using interprocedural analysis)
+        # (inferred in mypyc.analysis.attrdefined using interprocedural analysis).
+        # These can never raise AttributeError when accessed. If an attribute
+        # is *not* always initialized, we normally use the error value for
+        # an undefined value. If the attribute byte has an overlapping error value
+        # (the error_overlap attribute is true for the RType), we use a bitmap
+        # to track if the attribute is defined instead (see bitmap_attrs).
         self._always_initialized_attrs: set[str] = set()
 
         # Attributes that are sometimes initialized in __init__
@@ -191,8 +196,8 @@ def __init__(
 
         # Definedness of these attributes is backed by a bitmap. Index in the list
         # indicates the bit number. Includes inherited attributes. We need the
-        # bitmap for types such as native ints that can't have a dedicated error
-        # value that doesn't overlap a valid value. The bitmap is used if the
+        # bitmap for types such as native ints (i64 etc.) that can't have a dedicated
+        # error value that doesn't overlap a valid value. The bitmap is used if the
         # value of an attribute is the same as the error value.
         self.bitmap_attrs: list[str] = []
 
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index d5cc7a209491..60a56065006f 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -58,8 +58,21 @@ class RType:
     # to checking for error value as the return value of a function.
     #
     # For example, no i64 value can be reserved for error value, so we
-    # pick an arbitrary value (e.g. -113) to signal error, but this is
-    # also a valid non-error value.
+    # pick an arbitrary value (-113) to signal error, but this is
+    # also a valid non-error value. The chosen value is rare as a
+    # normal, non-error value, so most of the time we can avoid calling
+    # PyErr_Occurred() when checking for errors raised by called
+    # functions.
+    #
+    # This also means that if an attribute with this type might be
+    # undefined, we can't just rely on the error value to signal this.
+    # Instead, we add a bitfield to keep track whether attributes with
+    # "error overlap" have a value. If there is no value, AttributeError
+    # is raised on attribute read. Parameters with default values also
+    # use the bitfield trick to indicate whether the caller passed a
+    # value. (If we can determine that an attribute is "always defined",
+    # we never raise an AttributeError and don't need the bitfield
+    # entry.)
     error_overlap = False
 
     @abstractmethod
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 3cbfb072278e..58b690a944af 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -457,6 +457,19 @@ def run(x: object) -> object: ...
 async def sleep(t: float) -> None: ...
 
 [case testRunAsyncMiscTypesInEnvironment]
+# Here we test that values of various kinds of types can be spilled to the
+# environment. In particular, types with "overlapping error values" such as
+# i64 can be tricky, since they require extra work to support undefined
+# attribute values (which raise AttributeError when accessed). For these,
+# the object struct has a bitfield which keeps track of whether certain
+# attributes have an assigned value.
+#
+# In practice we mark these attributes as "always defined", which causes these
+# checks to be skipped on attribute access, and thus we don't require the
+# bitfield to exist.
+#
+# See the comment of RType.error_overlap for more information.
+
 import asyncio
 
 from mypy_extensions import i64, i32, i16, u8

From 6aec4b8617c6041649213152a32581e0f7812c0c Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 26 Apr 2025 10:25:23 +0200
Subject: [PATCH 1263/1617] Fix typo in testPropertySetterDecorated (#18946)

Ref https://github.com/python/mypy/pull/18787#discussion_r2051824223
---
 test-data/unit/check-classes.test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 65a6a0c9c0a8..38afc5cd4301 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -8516,7 +8516,7 @@ class C(B):
 
     @property
     def tricky(self) -> int: ...
-    @baz.setter
+    @tricky.setter
     @deco_instance
     def tricky(self, x: int) -> None: ...
 

From 7b4f6311e29452cc8d4ddb78331d0047c8b17e93 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 26 Apr 2025 20:15:51 +0200
Subject: [PATCH 1264/1617] Add missing branch for `is_subtype(TypeType,
 Overload)` (#18975)

Fixes #18974. This simply adds a missed branch - we do support
`is_subtype(Overload, TypeType)` but not another type order.
---
 mypy/subtypes.py                           |  5 +++++
 test-data/unit/check-assert-type-fail.test | 16 ++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 71b8b0ba59f5..84fda7955d75 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -1091,6 +1091,11 @@ def visit_type_type(self, left: TypeType) -> bool:
         right = self.right
         if isinstance(right, TypeType):
             return self._is_subtype(left.item, right.item)
+        if isinstance(right, Overloaded) and right.is_type_obj():
+            # Same as in other direction: if it's a constructor callable, all
+            # items should belong to the same class' constructor, so it's enough
+            # to check one of them.
+            return self._is_subtype(left, right.items[0])
         if isinstance(right, CallableType):
             if self.proper_subtype and not right.is_type_obj():
                 # We can't accept `Type[X]` as a *proper* subtype of Callable[P, X]
diff --git a/test-data/unit/check-assert-type-fail.test b/test-data/unit/check-assert-type-fail.test
index 89b3a863f8c7..514650649641 100644
--- a/test-data/unit/check-assert-type-fail.test
+++ b/test-data/unit/check-assert-type-fail.test
@@ -31,3 +31,19 @@ def f(si: arr.array[int]):
 from typing import assert_type, Callable
 def myfunc(arg: int) -> None: pass
 assert_type(myfunc, Callable[[int], None])  # E: Expression is of type "Callable[[Arg(int, 'arg')], None]", not "Callable[[int], None]"
+
+[case testAssertTypeOverload]
+from typing import assert_type, overload
+
+class Foo:
+    @overload
+    def __new__(cls, x: int) -> Foo: ...
+    @overload
+    def __new__(cls, x: str) -> Foo: ...
+    def __new__(cls, x: "int | str") -> Foo:
+        return cls(0)
+
+assert_type(Foo, type[Foo])
+A = Foo
+assert_type(A, type[Foo])
+[builtins fixtures/tuple.pyi]

From 70ee798a3cc61903a0bb2846456e9c3bb5e9c55e Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 30 Apr 2025 10:20:34 +0100
Subject: [PATCH 1265/1617] Local forward refs should precede global forward
 refs (#19000)

Fixes https://github.com/python/mypy/issues/18988

This should be a minimal change to restore backwards compatibility for
an edge case with forward references.
---
 mypy/semanal.py                     |  9 +++++++++
 test-data/unit/check-python312.test | 23 ++++++++++++++++++++---
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 586094b7a6fe..1b592e722cb4 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -6382,6 +6382,8 @@ class C:
             if node.name not in self.globals:
                 return True
             global_node = self.globals[node.name]
+            if not self.is_textually_before_class(global_node.node):
+                return True
             return not self.is_type_like(global_node.node)
         return False
 
@@ -6409,6 +6411,13 @@ def is_textually_before_statement(self, node: SymbolNode) -> bool:
         else:
             return line_diff > 0
 
+    def is_textually_before_class(self, node: SymbolNode | None) -> bool:
+        """Similar to above, but check if a node is defined before current class."""
+        assert self.type is not None
+        if node is None:
+            return False
+        return node.line < self.type.defn.line
+
     def is_overloaded_item(self, node: SymbolNode, statement: Statement) -> bool:
         """Check whether the function belongs to the overloaded variants"""
         if isinstance(node, OverloadedFuncDef) and isinstance(statement, FuncDef):
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 2f3d5e08dab3..2244548ea969 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2004,15 +2004,18 @@ reveal_type(x.related_resources)  # N: Revealed type is "__main__.ResourceRule"
 
 [case testPEP695TypeAliasRecursiveOuterClass]
 class A:
-    type X = X
+    type X = X  # E: Cannot resolve name "X" (possible cyclic definition)
 class X: ...
 
+class AA:
+    XX = XX  # OK, we allow this as a special case.
+class XX: ...
+
 class Y: ...
 class B:
     type Y = Y
 
-x: A.X
-reveal_type(x)  # N: Revealed type is "__main__.X"
+reveal_type(AA.XX)  # N: Revealed type is "def () -> __main__.XX"
 y: B.Y
 reveal_type(y)  # N: Revealed type is "__main__.Y"
 [builtins fixtures/tuple.pyi]
@@ -2042,3 +2045,17 @@ tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type i
 b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
+
+[case testForwardNestedPrecedesForwardGlobal]
+from typing import NewType
+
+class W[T]: pass
+
+class R:
+    class M(W[Action.V], type):
+        FOO = R.Action.V(0)
+    class Action(metaclass=M):
+        V = NewType('V', int)
+
+class Action:
+    pass

From c724a6a806655f94d0c705a7121e3d671eced96d Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 30 Apr 2025 16:35:27 +0100
Subject: [PATCH 1266/1617] Do not narrow types to Never with binder (#18972)

Fixes https://github.com/python/mypy/issues/18967
Fixes https://github.com/python/mypy/issues/16494
Fixes https://github.com/python/mypy/issues/15793
Fixes https://github.com/python/mypy/issues/12949

As you can see from updated test cases, it is kind of gray area, so
whether we go this way will depend on the `mypy_primer` results (and
also potentially on Dropbox internal code bases, where the above issue
may cause problems).
---
 mypy/checkexpr.py                    |  8 +++++++-
 test-data/unit/check-isinstance.test |  4 ++--
 test-data/unit/check-narrowing.test  | 13 ++++++++++++-
 test-data/unit/check-python310.test  |  2 +-
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index e7c2cba3fc55..d59c20c4605a 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6297,7 +6297,13 @@ def narrow_type_from_binder(
                     known_type, restriction, prohibit_none_typevar_overlap=True
                 ):
                     return None
-                return narrow_declared_type(known_type, restriction)
+                narrowed = narrow_declared_type(known_type, restriction)
+                if isinstance(get_proper_type(narrowed), UninhabitedType):
+                    # If we hit this case, it means that we can't reliably mark the code as
+                    # unreachable, but the resulting type can't be expressed in type system.
+                    # Falling back to restriction is more intuitive in most cases.
+                    return restriction
+                return narrowed
         return known_type
 
     def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool:
diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test
index 49140bf52b8d..058db1ea8197 100644
--- a/test-data/unit/check-isinstance.test
+++ b/test-data/unit/check-isinstance.test
@@ -1812,9 +1812,9 @@ reveal_type(fm)  # N: Revealed type is "__main__.FooMetaclass"
 if issubclass(fm, Foo):
     reveal_type(fm)  # N: Revealed type is "Type[__main__.Foo]"
 if issubclass(fm, Bar):
-    reveal_type(fm)  # N: Revealed type is "Never"
+    reveal_type(fm)  # N: Revealed type is "Type[__main__.Bar]"
 if issubclass(fm, Baz):
-    reveal_type(fm)  # N: Revealed type is "Never"
+    reveal_type(fm)  # N: Revealed type is "Type[__main__.Baz]"
 [builtins fixtures/isinstance.pyi]
 
 [case testIsinstanceAndNarrowTypeVariable]
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 1856ca26f736..dc2cfd46d9ad 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -1284,7 +1284,7 @@ def f(t: Type[T], a: A, b: B) -> None:
         reveal_type(a)  # N: Revealed type is "__main__.A"
 
     if type(b) is t:
-        reveal_type(b)  # N: Revealed type is "Never"
+        reveal_type(b)  # N: Revealed type is "T`-1"
     else:
         reveal_type(b)  # N: Revealed type is "__main__.B"
 
@@ -2413,3 +2413,14 @@ def foo(x: T) -> T:
     reveal_type(x)  # N: Revealed type is "T`-1"
     return x
 [builtins fixtures/isinstance.pyi]
+
+[case testDoNotNarrowToNever]
+def any():
+    return 1
+
+def f() -> None:
+    x = "a"
+    x = any()
+    assert isinstance(x, int)
+    reveal_type(x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/isinstance.pyi]
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 3774abfc548b..c2e2e5bddb34 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1347,7 +1347,7 @@ m: str
 
 match m:
     case a if a := 1:  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
-        reveal_type(a)  # N: Revealed type is "Never"
+        reveal_type(a)  # N: Revealed type is "Literal[1]?"
 
 [case testMatchAssigningPatternGuard]
 m: str

From 7f5a8dd0ad5e0f6707fc26669f8d7fc26a0f5ec8 Mon Sep 17 00:00:00 2001
From: Joren Hammudoglu 
Date: Thu, 1 May 2025 04:42:50 +0200
Subject: [PATCH 1267/1617] Prioritize `.pyi` from `-stubs` packages over
 bundled `.pyi` (#19001)



This fixes the import resolution order for stubs from a `-stubs` package
and stubs bundled with a `py.typed` package, and fixes #18997.

Besides the unit tests, the effectiveness of this fix is also
demonstrated at
https://github.com/jorenham/mypy-pep561-numpy-issue#mypy-jorenhamfix-18997-with-numtype

After investigating a bit more, it looks like mypy's incorrect
prioritization of stubs was limited to `__init__.pyi`. I confirmed this
by adding `reveal_type(np.dtypes.StringDType())` to the `main.pyi` in
https://github.com/jorenham/mypy-pep561-numpy-issue. With `mypy==1.15.0`
installed, it correctly showed `numpy.dtypes.StringDType` *without*
NumType, and `numpy.dtypes.StringDType[Never]` *with* NumType installed.
So both #18997 and this PR only apply to `__init__.pyi`.
---
 mypy/modulefinder.py                              | 15 +++++++++------
 mypy/test/testmodulefinder.py                     |  6 ++++++
 .../modulefinder-site-packages/foo-stubs/qux.pyi  |  1 +
 .../pkg_typed_w_stubs-stubs/__init__.pyi          |  1 +
 .../pkg_typed_w_stubs-stubs/spam.pyi              |  1 +
 .../pkg_typed_w_stubs/__init__.py                 |  1 +
 .../pkg_typed_w_stubs/__init__.pyi                |  1 +
 .../pkg_typed_w_stubs/py.typed                    |  0
 .../pkg_typed_w_stubs/spam.py                     |  1 +
 .../pkg_typed_w_stubs/spam.pyi                    |  1 +
 10 files changed, 22 insertions(+), 6 deletions(-)
 create mode 100644 test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py
 create mode 100644 test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi

diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
index ca21cc6a7199..836557590623 100644
--- a/mypy/modulefinder.py
+++ b/mypy/modulefinder.py
@@ -506,21 +506,24 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
             dir_prefix = base_dir
             for _ in range(len(components) - 1):
                 dir_prefix = os.path.dirname(dir_prefix)
+
+            # Stubs-only packages always take precedence over py.typed packages
+            path_stubs = f"{base_path}-stubs{sepinit}.pyi"
+            if fscache.isfile_case(path_stubs, dir_prefix):
+                if verify and not verify_module(fscache, id, path_stubs, dir_prefix):
+                    near_misses.append((path_stubs, dir_prefix))
+                else:
+                    return path_stubs
+
             # Prefer package over module, i.e. baz/__init__.py* over baz.py*.
             for extension in PYTHON_EXTENSIONS:
                 path = base_path + sepinit + extension
-                path_stubs = base_path + "-stubs" + sepinit + extension
                 if fscache.isfile_case(path, dir_prefix):
                     has_init = True
                     if verify and not verify_module(fscache, id, path, dir_prefix):
                         near_misses.append((path, dir_prefix))
                         continue
                     return path
-                elif fscache.isfile_case(path_stubs, dir_prefix):
-                    if verify and not verify_module(fscache, id, path_stubs, dir_prefix):
-                        near_misses.append((path_stubs, dir_prefix))
-                        continue
-                    return path_stubs
 
             # In namespace mode, register a potential namespace package
             if self.options and self.options.namespace_packages:
diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py
index 65d9a66c5fa0..d4ee3af041c5 100644
--- a/mypy/test/testmodulefinder.py
+++ b/mypy/test/testmodulefinder.py
@@ -195,6 +195,9 @@ def test__packages_with_ns(self) -> None:
             ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")),
             ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")),
             ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
+            # Regular package with py.typed, bundled stubs, and external stubs-only package
+            ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")),
+            ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")),
             # Regular package without py.typed
             ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
             ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
@@ -250,6 +253,9 @@ def test__packages_without_ns(self) -> None:
             ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")),
             ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")),
             ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
+            # Regular package with py.typed, bundled stubs, and external stubs-only package
+            ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")),
+            ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")),
             # Regular package without py.typed
             ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
             ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
diff --git a/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi
new file mode 100644
index 000000000000..5605b1454039
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi
@@ -0,0 +1 @@
+qux_var: int
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi
new file mode 100644
index 000000000000..579a7556fdd1
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi
@@ -0,0 +1 @@
+pkg_typed_w_stubs_var: str = ...
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi
new file mode 100644
index 000000000000..e3ef9cce5905
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi
@@ -0,0 +1 @@
+spam_var: str
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py
new file mode 100644
index 000000000000..11fa3635a2c7
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py
@@ -0,0 +1 @@
+pkg_typed_w_stubs_var = "pkg_typed_w_stubs"
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi
new file mode 100644
index 000000000000..3a03f395d014
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi
@@ -0,0 +1 @@
+pkg_typed_w_stubs_var: object
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py
new file mode 100644
index 000000000000..0aff1579b57f
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py
@@ -0,0 +1 @@
+spam_var = "spam"
diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi
new file mode 100644
index 000000000000..8eca196a7981
--- /dev/null
+++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi
@@ -0,0 +1 @@
+spam_var: object

From 51f80f80a5cf9603c43543b56f8e32ad717a2ec1 Mon Sep 17 00:00:00 2001
From: Joren Hammudoglu 
Date: Thu, 1 May 2025 17:12:38 +0200
Subject: [PATCH 1268/1617] support for `strict_bytes` in stubtest (#19002)

I confirmed that this fixes #18744
---
 mypy/main.py     |  4 +---
 mypy/options.py  | 10 ++++++++++
 mypy/stubtest.py |  1 +
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/mypy/main.py b/mypy/main.py
index 4d9cec63bbc1..9ea189f675eb 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1449,9 +1449,7 @@ def set_strict_flags() -> None:
         process_cache_map(parser, special_opts, options)
 
     # Process --strict-bytes
-    if options.strict_bytes:
-        options.disable_bytearray_promotion = True
-        options.disable_memoryview_promotion = True
+    options.process_strict_bytes()
 
     # An explicitly specified cache_fine_grained implies local_partial_types
     # (because otherwise the cache is not compatible with dmypy)
diff --git a/mypy/options.py b/mypy/options.py
index c086dfc8aea3..52afd27211ed 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -466,6 +466,16 @@ def process_incomplete_features(
             if feature in COMPLETE_FEATURES:
                 warning_callback(f"Warning: {feature} is already enabled by default")
 
+    def process_strict_bytes(self) -> None:
+        # Sync `--strict-bytes` and `--disable-{bytearray,memoryview}-promotion`
+        if self.strict_bytes:
+            # backwards compatibility
+            self.disable_bytearray_promotion = True
+            self.disable_memoryview_promotion = True
+        elif self.disable_bytearray_promotion and self.disable_memoryview_promotion:
+            # forwards compatibility
+            self.strict_bytes = True
+
     def apply_changes(self, changes: dict[str, object]) -> Options:
         # Note: effects of this method *must* be idempotent.
         new_options = Options()
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index ab29d9dca4b8..6c90913885c9 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -2003,6 +2003,7 @@ def warning_callback(msg: str) -> None:
     options.process_incomplete_features(
         error_callback=error_callback, warning_callback=warning_callback
     )
+    options.process_strict_bytes()
 
     try:
         modules = build_stubs(modules, options, find_submodules=not args.check_typeshed)

From 25b1bb8f24b2cdbac20fecdbd5a5b1deff884295 Mon Sep 17 00:00:00 2001
From: Arnav Jain <116742881+arnav-jain1@users.noreply.github.com>
Date: Thu, 1 May 2025 08:36:09 -0700
Subject: [PATCH 1269/1617] Bug fix of __r__ being used under
 the same ____ hook (#18995)

Fixes #18945 (This was a feature request that turned out to be a bug)

Essentially, I was trying to create a custom plugin and implement
__add__. In the process I discovered that __radd__ would implicitly be
called if __add__ has an error but it would be called under the same
__add__ hook (instead of radd) which is bad for non-commutative
operations. This has been fixed.
I also added a test for it.
---
 mypy/checkexpr.py                       |  2 +-
 test-data/unit/check-custom-plugin.test | 16 ++++++++++++++++
 test-data/unit/plugins/magic_method.py  | 24 ++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 test-data/unit/plugins/magic_method.py

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index d59c20c4605a..ba2d38b6f528 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4096,7 +4096,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
         results = []
         for name, method, obj, arg in variants:
             with self.msg.filter_errors(save_filtered_errors=True) as local_errors:
-                result = self.check_method_call(op_name, obj, method, [arg], [ARG_POS], context)
+                result = self.check_method_call(name, obj, method, [arg], [ARG_POS], context)
             if local_errors.has_new_errors():
                 errors.append(local_errors.filtered_errors())
                 results.append(result)
diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test
index feb135bee165..72b60c874656 100644
--- a/test-data/unit/check-custom-plugin.test
+++ b/test-data/unit/check-custom-plugin.test
@@ -1110,3 +1110,19 @@ plugins = """
   /test-data/unit/plugins/method_in_decorator.py,
 """
 [out]
+
+
+
+[case magicMethodReverse]
+# flags: --config-file tmp/mypy.ini
+from typing import Literal
+
+op1: Literal[3] = 3
+op2: Literal[4] = 4
+c = op1 + op2
+reveal_type(c) # N: Revealed type is "Literal[7]"
+
+[file mypy.ini]
+\[mypy]
+plugins=/test-data/unit/plugins/magic_method.py
+[builtins fixtures/ops.pyi]
diff --git a/test-data/unit/plugins/magic_method.py b/test-data/unit/plugins/magic_method.py
new file mode 100644
index 000000000000..fc220ab44748
--- /dev/null
+++ b/test-data/unit/plugins/magic_method.py
@@ -0,0 +1,24 @@
+from mypy.types import LiteralType, AnyType, TypeOfAny, Type
+from mypy.plugin import Plugin, MethodContext
+from typing import Callable, Optional
+
+# If radd exists, there shouldn't be an error. If it doesn't exist, then there will be an error
+def type_add(ctx: MethodContext) -> Type:
+    ctx.api.fail("fail", ctx.context)
+    return AnyType(TypeOfAny.from_error)
+
+def type_radd(ctx: MethodContext) -> Type:
+    return LiteralType(7, fallback=ctx.api.named_generic_type('builtins.int', []))
+
+
+class TestPlugin(Plugin):
+
+    def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]:
+        if fullname == 'builtins.int.__add__':
+            return type_add
+        if fullname == 'builtins.int.__radd__':
+            return type_radd
+        return None
+
+def plugin(version: str) -> type[TestPlugin]:
+    return TestPlugin

From 1af7b2c42d1360e97ceb2b0db4255ac8a417110f Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 1 May 2025 17:56:25 +0200
Subject: [PATCH 1270/1617] Check superclass compatibility of untyped methods
 if `--check-untyped-defs` is set (#18970)

This PR enables superclass compatibility checks for untyped methods when
`--check-untyped-defs` is set. IMO this behavior is correct as
`--check-untyped-defs` is essentially "treat everything as if there were
`: Any` annotations on all arguments", hence checking arg count and
names is sound.

This PR, however, allows `@override` on classes that have Any fallback
as those are often coming from unfollowed imports. This PR started as an
attempt to reject `@override` on untyped defs not found in superclass,
but I think it's better to just run all compatibility checks if the flag
is enabled.
---
 mypy/checker.py                     |  8 ++++-
 test-data/unit/check-classes.test   | 46 ++++++++++++++++++++++++++++-
 test-data/unit/check-functions.test | 33 +++++++++++++++++++++
 3 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 7d0b41c516e1..2d82d74cc197 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -750,6 +750,9 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                 defn.is_explicit_override
                 and not found_method_base_classes
                 and found_method_base_classes is not None
+                # If the class has Any fallback, we can't be certain that a method
+                # is really missing - it might come from unfollowed import.
+                and not defn.info.fallback_to_any
             ):
                 self.msg.no_overridable_method(defn.name, defn)
             self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl)
@@ -5285,12 +5288,15 @@ def visit_decorator_inner(
         # For overloaded functions/properties we already checked override for overload as a whole.
         if allow_empty or skip_first_item:
             return
-        if e.func.info and not e.func.is_dynamic() and not e.is_overload:
+        if e.func.info and not e.is_overload:
             found_method_base_classes = self.check_method_override(e)
             if (
                 e.func.is_explicit_override
                 and not found_method_base_classes
                 and found_method_base_classes is not None
+                # If the class has Any fallback, we can't be certain that a method
+                # is really missing - it might come from unfollowed import.
+                and not e.func.info.fallback_to_any
             ):
                 self.msg.no_overridable_method(e.func.name, e.func)
             self.check_explicit_override_decorator(e.func, found_method_base_classes)
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 38afc5cd4301..e0ea00aee361 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -6651,7 +6651,51 @@ from typing import TypeVar, Tuple, Callable
 T = TypeVar('T')
 def deco(f: Callable[..., T]) -> Callable[..., Tuple[T, int]]: ...
 [builtins fixtures/tuple.pyi]
-[out]
+
+[case testOverrideWithUntypedNotChecked]
+class Parent:
+    def foo(self, x):
+        ...
+    def bar(self, x):
+        ...
+    def baz(self, x: int) -> str:
+        return ""
+
+class Child(Parent):
+    def foo(self, y):  # OK: names not checked
+        ...
+    def bar(self, x, y):
+        ...
+    def baz(self, x, y):
+        return ""
+[builtins fixtures/tuple.pyi]
+
+[case testOverrideWithUntypedCheckedWithCheckUntypedDefs]
+# flags: --check-untyped-defs
+class Parent:
+    def foo(self, x):
+        ...
+    def bar(self, x):
+        ...
+    def baz(self, x: int) -> str:
+        return ""
+
+class Child(Parent):
+    def foo(self, y):  # OK: names not checked
+        ...
+    def bar(self, x, y) -> None:  # E: Signature of "bar" incompatible with supertype "Parent" \
+                                  # N:      Superclass: \
+                                  # N:          def bar(self, x: Any) -> Any \
+                                  # N:      Subclass: \
+                                  # N:          def bar(self, x: Any, y: Any) -> None
+        ...
+    def baz(self, x, y):  # E: Signature of "baz" incompatible with supertype "Parent" \
+                          # N:      Superclass: \
+                          # N:          def baz(self, x: int) -> str \
+                          # N:      Subclass: \
+                          # N:          def baz(self, x: Any, y: Any) -> Any
+        return ""
+[builtins fixtures/tuple.pyi]
 
 [case testOptionalDescriptorsBinder]
 from typing import Type, TypeVar, Optional
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index bd59dfbdfd5e..ac93c6c20354 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3285,6 +3285,39 @@ class C(B):
     def __f(self, y: int) -> str: pass  # OK
 [typing fixtures/typing-override.pyi]
 
+[case testOverrideUntypedDef]
+# flags: --python-version 3.12
+from typing import override
+
+class Parent: pass
+
+class Child(Parent):
+    @override
+    def foo(self, y): pass  # E: Method "foo" is marked as an override, but no base method was found with this name
+
+[typing fixtures/typing-override.pyi]
+
+[case testOverrideOnUnknownBaseClass]
+# flags: --python-version 3.12
+from typing import overload, override
+
+from unknown import UnknownParent  # type: ignore[import-not-found]
+
+class UnknownChild(UnknownParent):
+    @override
+    def foo(self, y): pass  # OK
+    @override
+    def bar(self, y: str) -> None: pass  # OK
+
+    @override
+    @overload
+    def baz(self, y: str) -> None: ...
+    @override
+    @overload
+    def baz(self, y: int) -> None: ...
+    def baz(self, y: str | int) -> None: ...
+[typing fixtures/typing-override.pyi]
+
 [case testCallableProperty]
 from typing import Callable
 

From 4b46d09f4e74df0feea38e879e25ac4d100ecde2 Mon Sep 17 00:00:00 2001
From: Alexey Makridenko 
Date: Thu, 1 May 2025 18:40:12 +0200
Subject: [PATCH 1271/1617] [stubgen] Fix `TypeAlias` handling (#18960)

Fixes #18905

`TypeAlias` is an unanalyzed type, but is also an alias. So I changed a
little bit of checking in the `visit_assignment_stmt` method.

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypy/stubgen.py             | 27 ++++++++++++++++++++-------
 test-data/unit/stubgen.test | 13 +++++++++++++
 2 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index 881686adc5ed..ba0a3f9dade6 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -920,13 +920,20 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
                     continue
             if (
                 isinstance(lvalue, NameExpr)
-                and not self.is_private_name(lvalue.name)
-                # it is never an alias with explicit annotation
-                and not o.unanalyzed_type
                 and self.is_alias_expression(o.rvalue)
+                and not self.is_private_name(lvalue.name)
             ):
-                self.process_typealias(lvalue, o.rvalue)
-                continue
+                is_explicit_type_alias = (
+                    o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias"
+                )
+                if is_explicit_type_alias:
+                    self.process_typealias(lvalue, o.rvalue, is_explicit_type_alias=True)
+                    continue
+
+                if not o.unanalyzed_type:
+                    self.process_typealias(lvalue, o.rvalue)
+                    continue
+
             if isinstance(lvalue, (TupleExpr, ListExpr)):
                 items = lvalue.items
                 if isinstance(o.unanalyzed_type, TupleType):  # type: ignore[misc]
@@ -1139,9 +1146,15 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool:
         else:
             return False
 
-    def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None:
+    def process_typealias(
+        self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False
+    ) -> None:
         p = AliasPrinter(self)
-        self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n")
+        if is_explicit_type_alias:
+            self.import_tracker.require_name("TypeAlias")
+            self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n")
+        else:
+            self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n")
         self.record_name(lvalue.name)
         self._vars[-1].append(lvalue.name)
 
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
index bf17c34b99a7..86d33e3af51d 100644
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -1544,6 +1544,19 @@ from typing import TypeVar
 T = TypeVar('T')
 alias = Union[T, List[T]]
 
+[case testExplicitTypeAlias]
+from typing import TypeAlias
+
+explicit_alias: TypeAlias = tuple[int, str]
+implicit_alias = list[int]
+
+[out]
+from typing import TypeAlias
+
+explicit_alias: TypeAlias = tuple[int, str]
+implicit_alias = list[int]
+
+
 [case testEllipsisAliasPreserved]
 
 alias = Tuple[int, ...]

From 43e8130dde92f2c210181fd816746adef5eecdd7 Mon Sep 17 00:00:00 2001
From: Anthony Sottile 
Date: Thu, 1 May 2025 23:12:33 -0400
Subject: [PATCH 1272/1617] dmypy suggest can now suggest through
 contextmanager-based decorators (#18948)

---
 mypy/suggestions.py                      | 18 ++++++---
 test-data/unit/fine-grained-suggest.test | 49 ++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index 16e630bf8c6e..f27ad7cdb637 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -52,6 +52,7 @@
     SymbolNode,
     SymbolTable,
     TypeInfo,
+    Var,
     reverse_builtin_aliases,
 )
 from mypy.options import Options
@@ -59,7 +60,7 @@
 from mypy.server.update import FineGrainedBuildManager
 from mypy.state import state
 from mypy.traverser import TraverserVisitor
-from mypy.typeops import make_simplified_union
+from mypy.typeops import bind_self, make_simplified_union
 from mypy.types import (
     AnyType,
     CallableType,
@@ -638,15 +639,20 @@ def find_node_by_file_and_line(self, file: str, line: int) -> tuple[str, SymbolN
     def extract_from_decorator(self, node: Decorator) -> FuncDef | None:
         for dec in node.decorators:
             typ = None
-            if isinstance(dec, RefExpr) and isinstance(dec.node, FuncDef):
-                typ = dec.node.type
+            if isinstance(dec, RefExpr) and isinstance(dec.node, (Var, FuncDef)):
+                typ = get_proper_type(dec.node.type)
             elif (
                 isinstance(dec, CallExpr)
                 and isinstance(dec.callee, RefExpr)
-                and isinstance(dec.callee.node, FuncDef)
-                and isinstance(dec.callee.node.type, CallableType)
+                and isinstance(dec.callee.node, (Decorator, FuncDef, Var))
+                and isinstance((call_tp := get_proper_type(dec.callee.node.type)), CallableType)
             ):
-                typ = get_proper_type(dec.callee.node.type.ret_type)
+                typ = get_proper_type(call_tp.ret_type)
+
+            if isinstance(typ, Instance):
+                call_method = typ.type.get_method("__call__")
+                if isinstance(call_method, FuncDef) and isinstance(call_method.type, FunctionLike):
+                    typ = bind_self(call_method.type, None)
 
             if not isinstance(typ, FunctionLike):
                 return None
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index 0ed3be4055ea..2539886229cf 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -602,6 +602,55 @@ def bar() -> None:
 (str) -> str
 ==
 
+[case testSuggestInferFuncDecorator5]
+# suggest: foo.foo1
+# suggest: foo.foo2
+# suggest: foo.foo3
+[file foo.py]
+from __future__ import annotations
+
+from typing import TypeVar, Generator, Callable
+
+F = TypeVar('F')
+
+# simplified `@contextmanager
+class _impl:
+    def __call__(self, f: F) -> F: return f
+def contextmanager(gen: Callable[[], Generator[None, None, None]]) -> Callable[[], _impl]: return _impl
+
+@contextmanager
+def gen() -> Generator[None, None, None]:
+    yield
+
+@gen()
+def foo1(x):
+    return x
+
+foo1('hi')
+
+inst = gen()
+
+@inst
+def foo2(x):
+    return x
+
+foo2('hello')
+
+ref = gen
+
+@ref()
+def foo3(x):
+    return x
+
+foo3('hello hello')
+
+[builtins fixtures/isinstancelist.pyi]
+[out]
+(str) -> str
+(str) -> str
+(str) -> str
+==
+
 [case testSuggestFlexAny1]
 # suggest: --flex-any=0.4 m.foo
 # suggest: --flex-any=0.7 m.foo

From c0218a4b13a5501ff71ef5deeaa6c4df345c4852 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 2 May 2025 13:04:54 +0100
Subject: [PATCH 1273/1617] [mypyc] Refactor IR building for generator
 functions (#19008)

The code was pretty hard to follow, since there were many conditional
code paths in a very long function that handles both normal functions,
nested functions and generators.

Move much of the generator-specific code to a helper function. This
required moving some functionality to helper functions to avoid code
duplication. Also pass a function as an argument to avoid a dependency
cycle.

This is quite a big refactoring, and it's easier to follow by looking at
the individual commits in this PR.
---
 mypyc/irbuild/builder.py   |  34 +++++++-
 mypyc/irbuild/env_class.py |  35 ++++++++
 mypyc/irbuild/function.py  | 174 ++++++++++---------------------------
 mypyc/irbuild/generator.py |  67 +++++++++++++-
 4 files changed, 174 insertions(+), 136 deletions(-)

diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index d9d3c5ed9cd0..72a5ff4099df 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -1169,7 +1169,7 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None:
                     return None
             return res
 
-    def enter(self, fn_info: FuncInfo | str = "") -> None:
+    def enter(self, fn_info: FuncInfo | str = "", *, ret_type: RType = none_rprimitive) -> None:
         if isinstance(fn_info, str):
             fn_info = FuncInfo(name=fn_info)
         self.builder = LowLevelIRBuilder(self.errors, self.options)
@@ -1179,7 +1179,7 @@ def enter(self, fn_info: FuncInfo | str = "") -> None:
         self.runtime_args.append([])
         self.fn_info = fn_info
         self.fn_infos.append(self.fn_info)
-        self.ret_types.append(none_rprimitive)
+        self.ret_types.append(ret_type)
         if fn_info.is_generator:
             self.nonlocal_control.append(GeneratorNonlocalControl())
         else:
@@ -1219,10 +1219,9 @@ def enter_method(
             self_type: If not None, override default type of the implicit 'self'
                 argument (by default, derive type from class_ir)
         """
-        self.enter(fn_info)
+        self.enter(fn_info, ret_type=ret_type)
         self.function_name_stack.append(name)
         self.class_ir_stack.append(class_ir)
-        self.ret_types[-1] = ret_type
         if self_type is None:
             self_type = RInstance(class_ir)
         self.add_argument(SELF_NAME, self_type)
@@ -1498,3 +1497,30 @@ def create_type_params(
         builder.init_type_var(tv, type_param.name, line)
         tvs.append(tv)
     return tvs
+
+
+def calculate_arg_defaults(
+    builder: IRBuilder,
+    fn_info: FuncInfo,
+    func_reg: Value | None,
+    symtable: dict[SymbolNode, SymbolTarget],
+) -> None:
+    """Calculate default argument values and store them.
+
+    They are stored in statics for top level functions and in
+    the function objects for nested functions (while constants are
+    still stored computed on demand).
+    """
+    fitem = fn_info.fitem
+    for arg in fitem.arguments:
+        # Constant values don't get stored but just recomputed
+        if arg.initializer and not is_constant(arg.initializer):
+            value = builder.coerce(
+                builder.accept(arg.initializer), symtable[arg.variable].type, arg.line
+            )
+            if not fn_info.is_nested:
+                name = fitem.fullname + "." + arg.variable.name
+                builder.add(InitStatic(value, name, builder.module_name))
+            else:
+                assert func_reg is not None
+                builder.add(SetAttr(func_reg, arg.variable.name, value, arg.line))
diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py
index aa223fe20176..ab786fe71dda 100644
--- a/mypyc/irbuild/env_class.py
+++ b/mypyc/irbuild/env_class.py
@@ -191,6 +191,41 @@ def add_args_to_env(
                 builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign)
 
 
+def add_vars_to_env(builder: IRBuilder) -> None:
+    """Add relevant local variables and nested functions to the environment class.
+
+    Add all variables and functions that are declared/defined within current
+    function and are referenced in functions nested within this one to this
+    function's environment class so the nested functions can reference
+    them even if they are declared after the nested function's definition.
+    Note that this is done before visiting the body of the function.
+    """
+    env_for_func: FuncInfo | ImplicitClass = builder.fn_info
+    if builder.fn_info.is_generator:
+        env_for_func = builder.fn_info.generator_class
+    elif builder.fn_info.is_nested or builder.fn_info.in_non_ext:
+        env_for_func = builder.fn_info.callable_class
+
+    if builder.fn_info.fitem in builder.free_variables:
+        # Sort the variables to keep things deterministic
+        for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name):
+            if isinstance(var, Var):
+                rtype = builder.type_to_rtype(var.type)
+                builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False)
+
+    if builder.fn_info.fitem in builder.encapsulating_funcs:
+        for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
+            if isinstance(nested_fn, FuncDef):
+                # The return type is 'object' instead of an RInstance of the
+                # callable class because differently defined functions with
+                # the same name and signature across conditional blocks
+                # will generate different callable classes, so the callable
+                # class that gets instantiated must be generic.
+                builder.add_var_to_env_class(
+                    nested_fn, object_rprimitive, env_for_func, reassign=False
+                )
+
+
 def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: ImplicitClass) -> None:
     """Enable calling a nested function (with a callable class) recursively.
 
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index 70e494f063b8..cb9a1a3dc4a3 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -25,7 +25,6 @@
     FuncItem,
     LambdaExpr,
     OverloadedFuncDef,
-    SymbolNode,
     TypeInfo,
     Var,
 )
@@ -44,7 +43,6 @@
 from mypyc.ir.ops import (
     BasicBlock,
     GetAttr,
-    InitStatic,
     Integer,
     LoadAddress,
     LoadLiteral,
@@ -62,31 +60,22 @@
     int_rprimitive,
     object_rprimitive,
 )
-from mypyc.irbuild.builder import IRBuilder, SymbolTarget, gen_arg_defaults
+from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults
 from mypyc.irbuild.callable_class import (
     add_call_to_callable_class,
     add_get_to_callable_class,
     instantiate_callable_class,
     setup_callable_class,
 )
-from mypyc.irbuild.context import FuncInfo, ImplicitClass
+from mypyc.irbuild.context import FuncInfo
 from mypyc.irbuild.env_class import (
+    add_vars_to_env,
     finalize_env_class,
     load_env_registers,
-    load_outer_envs,
     setup_env_class,
-    setup_func_for_recursive_call,
-)
-from mypyc.irbuild.generator import (
-    add_methods_to_generator_class,
-    add_raise_exception_blocks_to_generator_class,
-    create_switch_for_generator_class,
-    gen_generator_func,
-    populate_switch_for_generator_class,
-    setup_env_for_generator_class,
 )
+from mypyc.irbuild.generator import gen_generator_func, gen_generator_func_body
 from mypyc.irbuild.targets import AssignmentTarget
-from mypyc.irbuild.util import is_constant
 from mypyc.primitives.dict_ops import dict_get_method_with_none, dict_new_op, dict_set_item_op
 from mypyc.primitives.generic_ops import py_setattr_op
 from mypyc.primitives.misc_ops import register_function
@@ -235,123 +224,77 @@ def c() -> None:
         func_name = singledispatch_main_func_name(name)
     else:
         func_name = name
-    builder.enter(
-        FuncInfo(
-            fitem=fitem,
-            name=func_name,
-            class_name=class_name,
-            namespace=gen_func_ns(builder),
-            is_nested=is_nested,
-            contains_nested=contains_nested,
-            is_decorated=is_decorated,
-            in_non_ext=in_non_ext,
-            add_nested_funcs_to_env=add_nested_funcs_to_env,
-        )
+
+    fn_info = FuncInfo(
+        fitem=fitem,
+        name=func_name,
+        class_name=class_name,
+        namespace=gen_func_ns(builder),
+        is_nested=is_nested,
+        contains_nested=contains_nested,
+        is_decorated=is_decorated,
+        in_non_ext=in_non_ext,
+        add_nested_funcs_to_env=add_nested_funcs_to_env,
     )
+    is_generator = fn_info.is_generator
+    builder.enter(fn_info, ret_type=sig.ret_type)
 
     # Functions that contain nested functions need an environment class to store variables that
     # are free in their nested functions. Generator functions need an environment class to
     # store a variable denoting the next instruction to be executed when the __next__ function
     # is called, along with all the variables inside the function itself.
-    if builder.fn_info.contains_nested or builder.fn_info.is_generator:
+    if contains_nested or is_generator:
         setup_env_class(builder)
 
-    if builder.fn_info.is_nested or builder.fn_info.in_non_ext:
+    if is_nested or in_non_ext:
         setup_callable_class(builder)
 
-    if builder.fn_info.is_generator:
-        # Do a first-pass and generate a function that just returns a generator object.
-        gen_generator_func(builder)
-        args, _, blocks, ret_type, fn_info = builder.leave()
-        func_ir, func_reg = gen_func_ir(
-            builder, args, blocks, sig, fn_info, cdef, is_singledispatch
+    if is_generator:
+        # First generate a function that just constructs and returns a generator object.
+        func_ir, func_reg = gen_generator_func(
+            builder,
+            lambda args, blocks, fn_info: gen_func_ir(
+                builder, args, blocks, sig, fn_info, cdef, is_singledispatch
+            ),
         )
 
         # Re-enter the FuncItem and visit the body of the function this time.
-        builder.enter(fn_info)
-        setup_env_for_generator_class(builder)
-
-        load_outer_envs(builder, builder.fn_info.generator_class)
-        top_level = builder.top_level_fn_info()
-        if (
-            builder.fn_info.is_nested
-            and isinstance(fitem, FuncDef)
-            and top_level
-            and top_level.add_nested_funcs_to_env
-        ):
-            setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class)
-        create_switch_for_generator_class(builder)
-        add_raise_exception_blocks_to_generator_class(builder, fitem.line)
+        gen_generator_func_body(builder, fn_info, sig, func_reg)
     else:
-        load_env_registers(builder)
-        gen_arg_defaults(builder)
+        func_ir, func_reg = gen_func_body(builder, sig, cdef, is_singledispatch)
 
-    if builder.fn_info.contains_nested and not builder.fn_info.is_generator:
-        finalize_env_class(builder)
+    if is_singledispatch:
+        # add the generated main singledispatch function
+        builder.functions.append(func_ir)
+        # create the dispatch function
+        assert isinstance(fitem, FuncDef)
+        return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig)
 
-    builder.ret_types[-1] = sig.ret_type
+    return func_ir, func_reg
 
-    # Add all variables and functions that are declared/defined within this
-    # function and are referenced in functions nested within this one to this
-    # function's environment class so the nested functions can reference
-    # them even if they are declared after the nested function's definition.
-    # Note that this is done before visiting the body of this function.
-
-    env_for_func: FuncInfo | ImplicitClass = builder.fn_info
-    if builder.fn_info.is_generator:
-        env_for_func = builder.fn_info.generator_class
-    elif builder.fn_info.is_nested or builder.fn_info.in_non_ext:
-        env_for_func = builder.fn_info.callable_class
-
-    if builder.fn_info.fitem in builder.free_variables:
-        # Sort the variables to keep things deterministic
-        for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name):
-            if isinstance(var, Var):
-                rtype = builder.type_to_rtype(var.type)
-                builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False)
-
-    if builder.fn_info.fitem in builder.encapsulating_funcs:
-        for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
-            if isinstance(nested_fn, FuncDef):
-                # The return type is 'object' instead of an RInstance of the
-                # callable class because differently defined functions with
-                # the same name and signature across conditional blocks
-                # will generate different callable classes, so the callable
-                # class that gets instantiated must be generic.
-                builder.add_var_to_env_class(
-                    nested_fn, object_rprimitive, env_for_func, reassign=False
-                )
 
-    builder.accept(fitem.body)
+def gen_func_body(
+    builder: IRBuilder, sig: FuncSignature, cdef: ClassDef | None, is_singledispatch: bool
+) -> tuple[FuncIR, Value | None]:
+    load_env_registers(builder)
+    gen_arg_defaults(builder)
+    if builder.fn_info.contains_nested:
+        finalize_env_class(builder)
+    add_vars_to_env(builder)
+    builder.accept(builder.fn_info.fitem.body)
     builder.maybe_add_implicit_return()
 
-    if builder.fn_info.is_generator:
-        populate_switch_for_generator_class(builder)
-
     # Hang on to the local symbol table for a while, since we use it
     # to calculate argument defaults below.
     symtable = builder.symtables[-1]
 
     args, _, blocks, ret_type, fn_info = builder.leave()
 
-    if fn_info.is_generator:
-        add_methods_to_generator_class(builder, fn_info, sig, args, blocks, fitem.is_coroutine)
-    else:
-        func_ir, func_reg = gen_func_ir(
-            builder, args, blocks, sig, fn_info, cdef, is_singledispatch
-        )
+    func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, fn_info, cdef, is_singledispatch)
 
     # Evaluate argument defaults in the surrounding scope, since we
     # calculate them *once* when the function definition is evaluated.
     calculate_arg_defaults(builder, fn_info, func_reg, symtable)
-
-    if is_singledispatch:
-        # add the generated main singledispatch function
-        builder.functions.append(func_ir)
-        # create the dispatch function
-        assert isinstance(fitem, FuncDef)
-        return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig)
-
     return func_ir, func_reg
 
 
@@ -512,33 +455,6 @@ def handle_non_ext_method(
     builder.add_to_non_ext_dict(non_ext, name, func_reg, fdef.line)
 
 
-def calculate_arg_defaults(
-    builder: IRBuilder,
-    fn_info: FuncInfo,
-    func_reg: Value | None,
-    symtable: dict[SymbolNode, SymbolTarget],
-) -> None:
-    """Calculate default argument values and store them.
-
-    They are stored in statics for top level functions and in
-    the function objects for nested functions (while constants are
-    still stored computed on demand).
-    """
-    fitem = fn_info.fitem
-    for arg in fitem.arguments:
-        # Constant values don't get stored but just recomputed
-        if arg.initializer and not is_constant(arg.initializer):
-            value = builder.coerce(
-                builder.accept(arg.initializer), symtable[arg.variable].type, arg.line
-            )
-            if not fn_info.is_nested:
-                name = fitem.fullname + "." + arg.variable.name
-                builder.add(InitStatic(value, name, builder.module_name))
-            else:
-                assert func_reg is not None
-                builder.add(SetAttr(func_reg, arg.variable.name, value, arg.line))
-
-
 def gen_func_ns(builder: IRBuilder) -> str:
     """Generate a namespace for a nested function using its outer function names."""
     return "_".join(
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index bc61c4493d55..74c8d27a6324 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -10,7 +10,9 @@
 
 from __future__ import annotations
 
-from mypy.nodes import ARG_OPT, Var
+from typing import Callable
+
+from mypy.nodes import ARG_OPT, FuncDef, Var
 from mypyc.common import ENV_ATTR_NAME, NEXT_LABEL_ATTR_NAME, SELF_NAME
 from mypyc.ir.class_ir import ClassIR
 from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg
@@ -31,13 +33,16 @@
     Value,
 )
 from mypyc.ir.rtypes import RInstance, int_rprimitive, object_rprimitive
-from mypyc.irbuild.builder import IRBuilder, gen_arg_defaults
+from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults
 from mypyc.irbuild.context import FuncInfo, GeneratorClass
 from mypyc.irbuild.env_class import (
     add_args_to_env,
+    add_vars_to_env,
     finalize_env_class,
     load_env_registers,
     load_outer_env,
+    load_outer_envs,
+    setup_func_for_recursive_call,
 )
 from mypyc.irbuild.nonlocalcontrol import ExceptNonlocalControl
 from mypyc.primitives.exc_ops import (
@@ -49,13 +54,69 @@
 )
 
 
-def gen_generator_func(builder: IRBuilder) -> None:
+def gen_generator_func(
+    builder: IRBuilder,
+    gen_func_ir: Callable[
+        [list[Register], list[BasicBlock], FuncInfo], tuple[FuncIR, Value | None]
+    ],
+) -> tuple[FuncIR, Value | None]:
+    """Generate IR for generator function that returns generator object."""
     setup_generator_class(builder)
     load_env_registers(builder)
     gen_arg_defaults(builder)
     finalize_env_class(builder)
     builder.add(Return(instantiate_generator_class(builder)))
 
+    args, _, blocks, ret_type, fn_info = builder.leave()
+    func_ir, func_reg = gen_func_ir(args, blocks, fn_info)
+    return func_ir, func_reg
+
+
+def gen_generator_func_body(
+    builder: IRBuilder, fn_info: FuncInfo, sig: FuncSignature, func_reg: Value | None
+) -> None:
+    """Generate IR based on the body of a generator function.
+
+    Add "__next__", "__iter__" and other generator methods to the generator
+    class that implements the function (each function gets a separate class).
+
+    Return the symbol table for the body.
+    """
+    builder.enter(fn_info, ret_type=sig.ret_type)
+    setup_env_for_generator_class(builder)
+
+    load_outer_envs(builder, builder.fn_info.generator_class)
+    top_level = builder.top_level_fn_info()
+    fitem = fn_info.fitem
+    if (
+        builder.fn_info.is_nested
+        and isinstance(fitem, FuncDef)
+        and top_level
+        and top_level.add_nested_funcs_to_env
+    ):
+        setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class)
+    create_switch_for_generator_class(builder)
+    add_raise_exception_blocks_to_generator_class(builder, fitem.line)
+
+    add_vars_to_env(builder)
+
+    builder.accept(fitem.body)
+    builder.maybe_add_implicit_return()
+
+    populate_switch_for_generator_class(builder)
+
+    # Hang on to the local symbol table, since the caller will use it
+    # to calculate argument defaults.
+    symtable = builder.symtables[-1]
+
+    args, _, blocks, ret_type, fn_info = builder.leave()
+
+    add_methods_to_generator_class(builder, fn_info, sig, args, blocks, fitem.is_coroutine)
+
+    # Evaluate argument defaults in the surrounding scope, since we
+    # calculate them *once* when the function definition is evaluated.
+    calculate_arg_defaults(builder, fn_info, func_reg, symtable)
+
 
 def instantiate_generator_class(builder: IRBuilder) -> Value:
     fitem = builder.fn_info.fitem

From daf89223e8d4cfc85fd47318d270c381eebb2cc1 Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Fri, 2 May 2025 16:30:55 +0100
Subject: [PATCH 1274/1617] Add a few more tests for mypyc_attr native_class
 (dunder methods and metaclasses) (#18999)

---
 mypyc/test-data/irbuild-classes.test | 11 ++++
 mypyc/test-data/run-classes.test     | 75 ++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 972146bcb0b4..94971640a094 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1370,3 +1370,14 @@ class NonNativeClassContradiction():  # E: Class is marked as native_class=True
 @mypyc_attr(native_class="yes")
 class BadUse():  # E: native_class must be used with True or False only
     pass
+
+[case testMypycAttrNativeClassMetaError]
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=True)
+class M(type):  # E: Inheriting from most builtin types is unimplemented
+    pass
+
+@mypyc_attr(native_class=True)
+class A(metaclass=M):  # E: Class is marked as native_class=True but it can't be a native class
+    pass
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index f8720383d7fb..97bc063dd8ea 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2887,3 +2887,78 @@ def test_function():
     explicit_ext_inst = AnnotatedExtensionClass()
     with assertRaises(AttributeError):
         setattr(explicit_ext_inst, 'attr_instance', 6)
+
+[case testMypycAttrNativeClassDunder]
+from mypy_extensions import mypyc_attr
+from typing import Generic, Optional, TypeVar
+
+_T = TypeVar("_T")
+
+get_count = set_count = del_count = 0
+
+@mypyc_attr(native_class=False)
+class Bar(Generic[_T]):
+    # Note the lack of __deletable__
+    def __init__(self) -> None:
+        self.value: str = 'start'
+    def __get__(self, instance: _T, owner: Optional[type[_T]] = None) -> str:
+        global get_count
+        get_count += 1
+        return self.value
+    def __set__(self, instance: _T, value: str) -> None:
+        global set_count
+        set_count += 1
+        self.value = value
+    def __delete__(self, instance: _T) -> None:
+        global del_count
+        del_count += 1
+        del self.value
+
+@mypyc_attr(native_class=False)
+class Foo(object):
+    bar: Bar = Bar()
+
+[file driver.py]
+import native
+
+f = native.Foo()
+assert(hasattr(f, 'bar'))
+assert(native.get_count == 1)
+assert(f.bar == 'start')
+assert(native.get_count == 2)
+f.bar = 'test'
+assert(f.bar == 'test')
+assert(native.set_count == 1)
+del f.bar
+assert(not hasattr(f, 'bar'))
+assert(native.del_count == 1)
+
+[case testMypycAttrNativeClassMeta]
+from mypy_extensions import mypyc_attr
+from typing import ClassVar, TypeVar
+
+_T = TypeVar("_T")
+
+@mypyc_attr(native_class=False)
+class M(type):
+    count: ClassVar[int] = 0
+    def make(cls: type[_T]) -> _T:
+        M.count += 1
+        return cls()
+
+# implicit native_class=False
+# see testMypycAttrNativeClassMetaError for when trying to set it True
+class A(metaclass=M):
+    pass
+
+[file driver.py]
+import native
+
+a: native.A = native.A.make()
+assert(native.A.count == 1)
+
+class B(native.A):
+    pass
+
+b: B = B.make()
+assert(B.count == 2)

From d68ea3549c51c4b224f8dbdf44558df8af523e91 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 3 May 2025 12:44:47 +0200
Subject: [PATCH 1275/1617] Fix argparse for Python 3.14 (#19020)

https://github.com/python/cpython/pull/132323 added an optional `color`
argument to ArgumentParser. As a side effect the help formatters are now
called with two new keyword arguments `prefix_chars` and `color`. Add
`**kwargs` to the custom `AugmentedHelpFormatter` and pass it through to
the super class.
---
 mypy/dmypy/client.py | 4 ++--
 mypy/main.py         | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
index 9839f793582d..4791fe337f09 100644
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -29,8 +29,8 @@
 
 
 class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter):
-    def __init__(self, prog: str) -> None:
-        super().__init__(prog=prog, max_help_position=30)
+    def __init__(self, prog: str, **kwargs: Any) -> None:
+        super().__init__(prog=prog, max_help_position=30, **kwargs)
 
 
 parser = argparse.ArgumentParser(
diff --git a/mypy/main.py b/mypy/main.py
index 9ea189f675eb..c1e4f989ab98 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -249,8 +249,8 @@ def show_messages(
 
 # Make the help output a little less jarring.
 class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter):
-    def __init__(self, prog: str) -> None:
-        super().__init__(prog=prog, max_help_position=28)
+    def __init__(self, prog: str, **kwargs: Any) -> None:
+        super().__init__(prog=prog, max_help_position=28, **kwargs)
 
     def _fill_text(self, text: str, width: int, indent: str) -> str:
         if "\n" in text:

From e7405c90da87b5f0584aa61592ed3c638f501f9f Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sun, 4 May 2025 17:31:37 +0200
Subject: [PATCH 1276/1617] Enable colored output for argparse help in Python
 3.14 (#19021)

Support for colored output was just merged in cpython. Maybe a bit early
to add it here already but it doesn't hurt either.

| Current | With color |
| --- | --- |
| Without color | With color |

The color output can be disable using various environment variables.

https://docs.python.org/3.14/using/cmdline.html#using-on-controlling-color
---
 mypy/dmypy/client.py | 3 +++
 mypy/main.py         | 2 ++
 mypy/stubgen.py      | 2 ++
 mypy/stubtest.py     | 2 ++
 4 files changed, 9 insertions(+)

diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
index 4791fe337f09..90c3062bcbe5 100644
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -36,6 +36,9 @@ def __init__(self, prog: str, **kwargs: Any) -> None:
 parser = argparse.ArgumentParser(
     prog="dmypy", description="Client for mypy daemon mode", fromfile_prefix_chars="@"
 )
+if sys.version_info >= (3, 14):
+    parser.color = True  # Set as init arg in 3.14
+
 parser.set_defaults(action=None)
 parser.add_argument(
     "--status-file", default=DEFAULT_STATUS_FILE, help="status file to retrieve daemon details"
diff --git a/mypy/main.py b/mypy/main.py
index c1e4f989ab98..7bd7215bbe2a 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -491,6 +491,8 @@ def process_options(
         stdout=stdout,
         stderr=stderr,
     )
+    if sys.version_info >= (3, 14):
+        parser.color = True  # Set as init arg in 3.14
 
     strict_flag_names: list[str] = []
     strict_flag_assignments: list[tuple[str, bool]] = []
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index ba0a3f9dade6..3173bfdf9f5c 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -1851,6 +1851,8 @@ def parse_options(args: list[str]) -> Options:
     parser = argparse.ArgumentParser(
         prog="stubgen", usage=HEADER, description=DESCRIPTION, fromfile_prefix_chars="@"
     )
+    if sys.version_info >= (3, 14):
+        parser.color = True  # Set as init arg in 3.14
 
     parser.add_argument(
         "--ignore-errors",
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 6c90913885c9..ea09dac8ec95 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -2084,6 +2084,8 @@ def parse_options(args: list[str]) -> _Arguments:
     parser = argparse.ArgumentParser(
         description="Compares stubs to objects introspected from the runtime."
     )
+    if sys.version_info >= (3, 14):
+        parser.color = True  # Set as init arg in 3.14
     parser.add_argument("modules", nargs="*", help="Modules to test")
     parser.add_argument(
         "--concise",

From db752bbb46b395be603651810b74531a44d369e2 Mon Sep 17 00:00:00 2001
From: A5rocks 
Date: Mon, 5 May 2025 19:21:25 -0400
Subject: [PATCH 1277/1617] Mark varargs as pos-only (#19022)

Fixes https://github.com/python/mypy/issues/19019. This PR marks `*args`
as positional only, as you cannot pass the argument by its name.

---------

Co-authored-by: Anthony Sottile 
---
 mypy/argmap.py                    | 2 +-
 test-data/unit/check-varargs.test | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/mypy/argmap.py b/mypy/argmap.py
index a1c4ef72ea40..28fad1f093dd 100644
--- a/mypy/argmap.py
+++ b/mypy/argmap.py
@@ -78,7 +78,7 @@ def map_actuals_to_formals(
         elif actual_kind.is_named():
             assert actual_names is not None, "Internal error: named kinds without names given"
             name = actual_names[ai]
-            if name in formal_names:
+            if name in formal_names and formal_kinds[formal_names.index(name)] != nodes.ARG_STAR:
                 formal_to_actual[formal_names.index(name)].append(ai)
             elif nodes.ARG_STAR2 in formal_kinds:
                 formal_to_actual[formal_kinds.index(nodes.ARG_STAR2)].append(ai)
diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test
index 65bbd8456d78..c59f07e92a4e 100644
--- a/test-data/unit/check-varargs.test
+++ b/test-data/unit/check-varargs.test
@@ -145,6 +145,14 @@ f(*it1, 1, *it2, 2)  # E: Argument 3 to "f" has incompatible type "*Tuple[str]";
 f(*it1, '') # E: Argument 2 to "f" has incompatible type "str"; expected "int"
 [builtins fixtures/for.pyi]
 
+[case testCallVarArgsWithMatchingNamedArgument]
+def foo(*args: int) -> None: ...  # N: "foo" defined here
+foo(args=1)  # E: Unexpected keyword argument "args" for "foo"
+
+def bar(*args: int, **kwargs: str) -> None: ...
+bar(args=1)  # E: Argument "args" to "bar" has incompatible type "int"; expected "str"
+[builtins fixtures/for.pyi]
+
 
 -- Calling varargs function + type inference
 -- -----------------------------------------

From 6d860cf40d0c2df1a2b4083047d1c3dc58af75fb Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 6 May 2025 01:45:19 +0200
Subject: [PATCH 1278/1617] Allow accessing `__init__` on final classes and
 when `__init__` is final (#19035)

Fixes #19033.
---
 mypy/checkmember.py             | 20 +++++++++++---------
 test-data/unit/check-final.test | 21 +++++++++++++++++++++
 2 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 1a76372d4731..d5d1f862a9d9 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -308,18 +308,21 @@ def report_missing_attribute(
 def analyze_instance_member_access(
     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
-        # type safety unless used via super().
-        mx.fail(message_registry.CANNOT_ACCESS_INIT)
-        return AnyType(TypeOfAny.from_error)
-
-    # The base object has an instance type.
-
     info = typ.type
     if override_info:
         info = override_info
 
+    method = info.get_method(name)
+
+    if name == "__init__" and not mx.is_super and not info.is_final:
+        if not method or not method.is_final:
+            # Accessing __init__ in statically typed code would compromise
+            # type safety unless used via super() or the method/class is final.
+            mx.fail(message_registry.CANNOT_ACCESS_INIT)
+            return AnyType(TypeOfAny.from_error)
+
+    # The base object has an instance type.
+
     if (
         state.find_occurrences
         and info.name == state.find_occurrences[0]
@@ -329,7 +332,6 @@ def analyze_instance_member_access(
         mx.msg.note("Occurrence of '{}.{}'".format(*state.find_occurrences), mx.context)
 
     # Look up the member. First look up the method dictionary.
-    method = info.get_method(name)
     if method and not isinstance(method, Decorator):
         if mx.is_super and not mx.suppress_errors:
             validate_super_call(method, mx)
diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test
index ce68b265a3c3..4b0bab45d16c 100644
--- a/test-data/unit/check-final.test
+++ b/test-data/unit/check-final.test
@@ -1229,3 +1229,24 @@ reveal_type(B() and 42)  # N: Revealed type is "Literal[42]?"
 reveal_type(C() and 42)  # N: Revealed type is "__main__.C"
 
 [builtins fixtures/bool.pyi]
+
+[case testCanAccessFinalClassInit]
+from typing import final
+
+@final
+class FinalClass:
+    pass
+
+def check_final_class() -> None:
+    new_instance = FinalClass()
+    new_instance.__init__()
+
+class FinalInit:
+    @final
+    def __init__(self) -> None:
+        pass
+
+def check_final_init() -> None:
+    new_instance = FinalInit()
+    new_instance.__init__()
+[builtins fixtures/tuple.pyi]

From bd1f51ac4de637ab1cbe9f633cdd401d0b520112 Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Tue, 6 May 2025 10:13:57 +0100
Subject: [PATCH 1279/1617] [mypyc] Show the reason why a class can't be a
 native class (#19016)

---
 mypyc/irbuild/util.py                | 29 +++++++++++++++++++---------
 mypyc/test-data/irbuild-classes.test |  4 ++--
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py
index 939c543c85a2..757b49c68c83 100644
--- a/mypyc/irbuild/util.py
+++ b/mypyc/irbuild/util.py
@@ -134,13 +134,15 @@ def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool:
     if explicit_native_class is False:
         return False
 
-    implicit_extension_class = is_implicit_extension_class(cdef)
+    implicit_extension_class, reason = is_implicit_extension_class(cdef)
 
     # Classes with native_class=True should be extension classes, but they might
     # not be able to be due to other reasons. Print an error in that case.
     if explicit_native_class is True and not implicit_extension_class:
         errors.error(
-            "Class is marked as native_class=True but it can't be a native class", path, cdef.line
+            f"Class is marked as native_class=True but it can't be a native class. {reason}",
+            path,
+            cdef.line,
         )
 
     return implicit_extension_class
@@ -177,28 +179,37 @@ def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool
     return None
 
 
-def is_implicit_extension_class(cdef: ClassDef) -> bool:
+def is_implicit_extension_class(cdef: ClassDef) -> tuple[bool, str]:
+    """Check if class can be extension class and return a user-friendly reason it can't be one."""
+
     for d in cdef.decorators:
-        # Classes that have any decorator other than supported decorators, are not extension classes
         if (
             not is_trait_decorator(d)
             and not is_dataclass_decorator(d)
             and not get_mypyc_attr_call(d)
             and not is_final_decorator(d)
         ):
-            return False
+            return (
+                False,
+                "Classes that have decorators other than supported decorators"
+                " can't be native classes.",
+            )
 
     if cdef.info.typeddict_type:
-        return False
+        return False, "TypedDict classes can't be native classes."
     if cdef.info.is_named_tuple:
-        return False
+        return False, "NamedTuple classes can't be native classes."
     if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in (
         "abc.ABCMeta",
         "typing.TypingMeta",
         "typing.GenericMeta",
     ):
-        return False
-    return True
+        return (
+            False,
+            "Classes with a metaclass other than ABCMeta, TypingMeta or"
+            " GenericMeta can't be native classes.",
+        )
+    return True, ""
 
 
 def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef:
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 94971640a094..9d564a552a05 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1363,7 +1363,7 @@ def decorator(cls):
 
 @mypyc_attr(native_class=True)
 @decorator
-class NonNativeClassContradiction():  # E: Class is marked as native_class=True but it can't be a native class
+class NonNativeClassContradiction():  # E: Class is marked as native_class=True but it can't be a native class. Classes that have decorators other than supported decorators can't be native classes.
     pass
 
 
@@ -1379,5 +1379,5 @@ class M(type):  # E: Inheriting from most builtin types is unimplemented
     pass
 
 @mypyc_attr(native_class=True)
-class A(metaclass=M):  # E: Class is marked as native_class=True but it can't be a native class
+class A(metaclass=M):  # E: Class is marked as native_class=True but it can't be a native class. Classes with a metaclass other than ABCMeta, TypingMeta or GenericMeta can't be native classes.
     pass

From 61b36646b8f58fb4eda5ff1edc6ff38a6d230b59 Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Tue, 6 May 2025 19:03:32 +0100
Subject: [PATCH 1280/1617] [mypyc] Add internal import for
 _PyUnicode_CheckConsistency for py 3.13 (#19045)

In debug build of Python 3.13, mypyc fails to build due to
`_PyUnicode_CheckConsistency` being moved to internal. Let's include
this, but only for Python 3.13 and only for debug builds. Technically,
the assert doesn't need to be behind a Py_DEBUG, but just in case...
---
 mypyc/lib-rt/str_ops.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c
index 130840cf4e08..49fcbb8c6876 100644
--- a/mypyc/lib-rt/str_ops.c
+++ b/mypyc/lib-rt/str_ops.c
@@ -5,6 +5,12 @@
 #include 
 #include "CPy.h"
 
+// The _PyUnicode_CheckConsistency definition has been moved to the internal API
+// https://github.com/python/cpython/pull/106398
+#if defined(Py_DEBUG) && defined(CPY_3_13_FEATURES)
+#include "internal/pycore_unicodeobject.h"
+#endif
+
 // Copied from cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e.
 #define BLOOM_MASK unsigned long
 #define BLOOM(mask, ch)     ((mask &  (1UL << ((ch) & (BLOOM_WIDTH - 1)))))
@@ -182,7 +188,9 @@ PyObject *CPyStr_Build(Py_ssize_t len, ...) {
         assert(res_offset == PyUnicode_GET_LENGTH(res));
     }
 
+#ifdef Py_DEBUG
     assert(_PyUnicode_CheckConsistency(res, 1));
+#endif
     return res;
 }
 

From f3367260eaeee9cb67ca2bd4ca3929e9c0d7a9af Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 6 May 2025 22:16:32 +0200
Subject: [PATCH 1281/1617] Only consider meta variables in ambiguous "any of"
 constraints (#18986)

Sometimes our constraints builder needs to express "at least one of
these constraints must be true". Sometimes it's trivial, but sometimes
they have nothing in common - in that case we fall back to forgetting
all of them and (usually) inferring `Never`.

This PR extends the fallback handling logic by one more rule: before
giving up, we try restricting the constraints to meta variables only.
This means that when we try to express `T :> str || U :> str` in the
following setup:

```
from typing import TypeVar

T = TypeVar("T")
U = TypeVar("U")

class Foo(Generic[T]):
    def func(self, arg: T | U) -> None: ...
```

we won't ignore both and fall back to `Never` but will pick `U :> str`
instead. The reason for this heuristic is that function-scoped typevars
are definitely something we're trying to infer, while everything else is
usually out of our control, any other type variable should stay intact.

There are other places where constraint builder may emit restrictions on
type variables it does not control, handling them consistently
everywhere is left as an exercise to the reader.

This isn't safe in general case - it might be that another typevar
satisfies the constraints but the chosen one doesn't. However, this
shouldn't make any existing inference worse: if we used to infer `Never`
and it worked, then anything else should almost definitely work as well.

See the added testcase for motivation: currently `mypy` fails to handle
`Mapping.get` with default without return type context when `Mapping`
has a type variable as the second argument.
https://mypy-play.net/?mypy=1.15.0&python=3.12&flags=strict&gist=2f9493548082e66b77750655d3a90218

This is a prerequisite of #18976 - that inference change makes the
problem solved here occur more often.
---
 mypy/constraints.py                 | 22 ++++++++++++++++++++--
 test-data/unit/check-inference.test | 16 ++++++++++++++++
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/mypy/constraints.py b/mypy/constraints.py
index 079f6536ee20..8e7a30e05ffb 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -512,7 +512,7 @@ def handle_recursive_union(template: UnionType, actual: Type, direction: int) ->
     ) or infer_constraints(UnionType.make_union(type_var_items), actual, direction)
 
 
-def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list[Constraint]:
+def any_constraints(options: list[list[Constraint] | None], *, eager: bool) -> list[Constraint]:
     """Deduce what we can from a collection of constraint lists.
 
     It's a given that at least one of the lists must be satisfied. A
@@ -547,7 +547,7 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list
                 if option in trivial_options:
                     continue
                 merged_options.append([merge_with_any(c) for c in option])
-            return any_constraints(list(merged_options), eager)
+            return any_constraints(list(merged_options), eager=eager)
 
     # If normal logic didn't work, try excluding trivially unsatisfiable constraint (due to
     # upper bounds) from each option, and comparing them again.
@@ -555,6 +555,14 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list
     if filtered_options != options:
         return any_constraints(filtered_options, eager=eager)
 
+    # Try harder: if that didn't work, try to strip typevars that aren't meta vars.
+    # Note this is what we would always do, but unfortunately some callers may not
+    # set the meta var status correctly (for historical reasons), so we use this as
+    # a fallback only.
+    filtered_options = [exclude_non_meta_vars(o) for o in options]
+    if filtered_options != options:
+        return any_constraints(filtered_options, eager=eager)
+
     # Otherwise, there are either no valid options or multiple, inconsistent valid
     # options. Give up and deduce nothing.
     return []
@@ -569,6 +577,7 @@ def filter_satisfiable(option: list[Constraint] | None) -> list[Constraint] | No
     """
     if not option:
         return option
+
     satisfiable = []
     for c in option:
         if isinstance(c.origin_type_var, TypeVarType) and c.origin_type_var.values:
@@ -583,6 +592,15 @@ def filter_satisfiable(option: list[Constraint] | None) -> list[Constraint] | No
     return satisfiable
 
 
+def exclude_non_meta_vars(option: list[Constraint] | None) -> list[Constraint] | None:
+    # If we had an empty list, keep it intact
+    if not option:
+        return option
+    # However, if none of the options actually references meta vars, better remove
+    # this constraint entirely.
+    return [c for c in option if c.type_var.is_meta_var()] or None
+
+
 def is_same_constraints(x: list[Constraint], y: list[Constraint]) -> bool:
     for c1 in x:
         if not any(is_same_constraint(c1, c2) for c2 in y):
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 42b5a05ab39a..25565946158e 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -3963,3 +3963,19 @@ def f() -> None:
 
     # The type below should not be Any.
     reveal_type(x)  # N: Revealed type is "builtins.int"
+
+[case testInferenceMappingTypeVarGet]
+from typing import Generic, TypeVar, Union
+
+_T = TypeVar("_T")
+_K = TypeVar("_K")
+_V = TypeVar("_V")
+
+class Mapping(Generic[_K, _V]):
+    def get(self, key: _K, default: Union[_V, _T]) -> Union[_V, _T]: ...
+
+def check(mapping: Mapping[str, _T]) -> None:
+    ok1 = mapping.get("", "")
+    reveal_type(ok1)  # N: Revealed type is "Union[_T`-1, builtins.str]"
+    ok2: Union[_T, str] = mapping.get("", "")
+[builtins fixtures/tuple.pyi]

From 79ff4ff3cedd6ca2786bdf8a8d531e944c51758a Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 7 May 2025 17:59:50 +0200
Subject: [PATCH 1282/1617] Do not cache module lookup results that may become
 invalid in future (#19044)

Fixes #19037. See my analysis in #19037 for motivation. As a side note,
with this patch the second run behaves as expected (without it mypy
tries to reanalyze a lot of modules during the 2nd run, now it only
rechecks the inputs with errors).
---
 mypy/modulefinder.py                          | 59 ++++++++++++-------
 mypy/test/testpep561.py                       |  7 ++-
 .../typedpkg_ns_nested/pyproject.toml         | 11 ++++
 .../typedpkg_ns/a/__init__.py                 |  0
 .../typedpkg_ns_nested/typedpkg_ns/a/py.typed |  0
 .../typedpkg_ns/b/__init__.py                 |  0
 test-data/unit/pep561.test                    | 20 +++++++
 7 files changed, 74 insertions(+), 23 deletions(-)
 create mode 100644 test-data/packages/typedpkg_ns_nested/pyproject.toml
 create mode 100644 test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/__init__.py
 create mode 100644 test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/py.typed
 create mode 100644 test-data/packages/typedpkg_ns_nested/typedpkg_ns/b/__init__.py

diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
index 836557590623..3040276dea6d 100644
--- a/mypy/modulefinder.py
+++ b/mypy/modulefinder.py
@@ -320,13 +320,21 @@ def find_module(self, id: str, *, fast_path: bool = False) -> ModuleSearchResult
                 use_typeshed = self._typeshed_has_version(id)
             elif top_level in self.stdlib_py_versions:
                 use_typeshed = self._typeshed_has_version(top_level)
-            self.results[id] = self._find_module(id, use_typeshed)
-            if (
-                not (fast_path or (self.options is not None and self.options.fast_module_lookup))
-                and self.results[id] is ModuleNotFoundReason.NOT_FOUND
-                and self._can_find_module_in_parent_dir(id)
-            ):
-                self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY
+            result, should_cache = self._find_module(id, use_typeshed)
+            if should_cache:
+                if (
+                    not (
+                        fast_path or (self.options is not None and self.options.fast_module_lookup)
+                    )
+                    and result is ModuleNotFoundReason.NOT_FOUND
+                    and self._can_find_module_in_parent_dir(id)
+                ):
+                    self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY
+                else:
+                    self.results[id] = result
+                return self.results[id]
+            else:
+                return result
         return self.results[id]
 
     def _typeshed_has_version(self, module: str) -> bool:
@@ -384,11 +392,16 @@ def _can_find_module_in_parent_dir(self, id: str) -> bool:
         while any(is_init_file(file) for file in os.listdir(working_dir)):
             working_dir = os.path.dirname(working_dir)
             parent_search.search_paths = SearchPaths((working_dir,), (), (), ())
-            if not isinstance(parent_search._find_module(id, False), ModuleNotFoundReason):
+            if not isinstance(parent_search._find_module(id, False)[0], ModuleNotFoundReason):
                 return True
         return False
 
-    def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
+    def _find_module(self, id: str, use_typeshed: bool) -> tuple[ModuleSearchResult, bool]:
+        """Try to find a module in all available sources.
+
+        Returns:
+            ``(result, can_be_cached)`` pair.
+        """
         fscache = self.fscache
 
         # Fast path for any modules in the current source set.
@@ -424,7 +437,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
             else None
         )
         if p:
-            return p
+            return p, True
 
         # If we're looking for a module like 'foo.bar.baz', it's likely that most of the
         # many elements of lib_path don't even have a subdirectory 'foo/bar'.  Discover
@@ -444,6 +457,9 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
             for component in (components[0], components[0] + "-stubs")
             for package_dir in self.find_lib_path_dirs(component, self.search_paths.package_path)
         }
+        # Caching FOUND_WITHOUT_TYPE_HINTS is not always safe. That causes issues with
+        # typed subpackages in namespace packages.
+        can_cache_any_result = True
         for pkg_dir in self.search_paths.package_path:
             if pkg_dir not in candidate_package_dirs:
                 continue
@@ -475,6 +491,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
             if isinstance(non_stub_match, ModuleNotFoundReason):
                 if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS:
                     found_possible_third_party_missing_type_hints = True
+                    can_cache_any_result = False
             else:
                 third_party_inline_dirs.append(non_stub_match)
                 self._update_ns_ancestors(components, non_stub_match)
@@ -513,7 +530,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
                 if verify and not verify_module(fscache, id, path_stubs, dir_prefix):
                     near_misses.append((path_stubs, dir_prefix))
                 else:
-                    return path_stubs
+                    return path_stubs, True
 
             # Prefer package over module, i.e. baz/__init__.py* over baz.py*.
             for extension in PYTHON_EXTENSIONS:
@@ -523,7 +540,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
                     if verify and not verify_module(fscache, id, path, dir_prefix):
                         near_misses.append((path, dir_prefix))
                         continue
-                    return path
+                    return path, True
 
             # In namespace mode, register a potential namespace package
             if self.options and self.options.namespace_packages:
@@ -541,7 +558,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
                     if verify and not verify_module(fscache, id, path, dir_prefix):
                         near_misses.append((path, dir_prefix))
                         continue
-                    return path
+                    return path, True
 
         # In namespace mode, re-check those entries that had 'verify'.
         # Assume search path entries xxx, yyy and zzz, and we're
@@ -570,7 +587,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
                 for path, dir_prefix in near_misses
             ]
             index = levels.index(max(levels))
-            return near_misses[index][0]
+            return near_misses[index][0], True
 
         # Finally, we may be asked to produce an ancestor for an
         # installed package with a py.typed marker that is a
@@ -578,12 +595,12 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
         # if we would otherwise return "not found".
         ancestor = self.ns_ancestors.get(id)
         if ancestor is not None:
-            return ancestor
+            return ancestor, True
 
         approved_dist_name = stub_distribution_name(id)
         if approved_dist_name:
             if len(components) == 1:
-                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
+                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED, True
             # If we're a missing submodule of an already installed approved stubs, we don't want to
             # error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND.
             for i in range(1, len(components)):
@@ -591,14 +608,14 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
                 if stub_distribution_name(parent_id) == approved_dist_name:
                     break
             else:
-                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
+                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED, True
             if self.find_module(parent_id) is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED:
-                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
-            return ModuleNotFoundReason.NOT_FOUND
+                return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED, True
+            return ModuleNotFoundReason.NOT_FOUND, True
 
         if found_possible_third_party_missing_type_hints:
-            return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
-        return ModuleNotFoundReason.NOT_FOUND
+            return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS, can_cache_any_result
+        return ModuleNotFoundReason.NOT_FOUND, True
 
     def find_modules_recursive(self, module: str) -> list[BuildSource]:
         module_path = self.find_module(module, fast_path=True)
diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py
index e3f729729f0b..0afb69bc0c99 100644
--- a/mypy/test/testpep561.py
+++ b/mypy/test/testpep561.py
@@ -145,8 +145,11 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
                     output.append(line[len(test_temp_dir + os.sep) :].rstrip("\r\n"))
                 else:
                     # Normalize paths so that the output is the same on Windows and Linux/macOS.
-                    line = line.replace(test_temp_dir + os.sep, test_temp_dir + "/")
-                    output.append(line.rstrip("\r\n"))
+                    # Yes, this is naive: replace all slashes preceding first colon, if any.
+                    path, *rest = line.split(":", maxsplit=1)
+                    if rest:
+                        path = path.replace(os.sep, "/")
+                    output.append(":".join([path, *rest]).rstrip("\r\n"))
             iter_count = "" if i == 0 else f" on iteration {i + 1}"
             expected = testcase.output if i == 0 else testcase.output2.get(i + 1, [])
 
diff --git a/test-data/packages/typedpkg_ns_nested/pyproject.toml b/test-data/packages/typedpkg_ns_nested/pyproject.toml
new file mode 100644
index 000000000000..b5bf038b8e14
--- /dev/null
+++ b/test-data/packages/typedpkg_ns_nested/pyproject.toml
@@ -0,0 +1,11 @@
+[project]
+name = 'typedpkg_namespace.nested'
+version = '0.1'
+description = 'Two namespace packages, one of them typed'
+
+[tool.hatch.build]
+include = ["**/*.py", "**/*.pyi", "**/py.typed"]
+
+[build-system]
+requires = ["hatchling==1.18"]
+build-backend = "hatchling.build"
diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/__init__.py b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/py.typed b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/py.typed
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/b/__init__.py b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/b/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test
index fb303a8fb5ec..314befa11b94 100644
--- a/test-data/unit/pep561.test
+++ b/test-data/unit/pep561.test
@@ -213,3 +213,23 @@ from typedpkg_ns.a.bbb import bf
 [file dummy.py.2]
 [out]
 [out2]
+
+[case testTypedNamespaceSubpackage]
+# pkgs: typedpkg_ns_nested
+import our
+[file our/__init__.py]
+import our.bar
+import our.foo
+[file our/bar.py]
+from typedpkg_ns.b import Something
+[file our/foo.py]
+import typedpkg_ns.a
+
+[file dummy.py.2]
+
+[out]
+our/bar.py:1: error: Skipping analyzing "typedpkg_ns.b": module is installed, but missing library stubs or py.typed marker
+our/bar.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+[out2]
+our/bar.py:1: error: Skipping analyzing "typedpkg_ns.b": module is installed, but missing library stubs or py.typed marker
+our/bar.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

From 501a07b45af8e44eda665e53526fc590dc5a014e Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Thu, 8 May 2025 19:29:32 +0100
Subject: [PATCH 1283/1617] Revert "Update project metadata for PEP 639
 (#18821)" (#19052)

This reverts commit 836019a625072665904447e7612ca7c3ada73d62.

Sadly, upgrading setuptools can cause some issues downstream. This is
the case with Dropbox's internal codebase. Let's wait a bit longer
before upgrading requirements and pyproject.toml file.
---
 .github/workflows/test.yml | 4 ++--
 pyproject.toml             | 6 +++---
 test-requirements.in       | 2 +-
 test-requirements.txt      | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 279f7f48d45d..c42550431bb1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -167,7 +167,7 @@ jobs:
         echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))'
         echo os.cpu_count; python -c 'import os; print(os.cpu_count())'
         echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))'
-        pip install tox==4.21.2
+        pip install setuptools==75.1.0 tox==4.21.2
 
     - name: Compiled with mypyc
       if: ${{ matrix.test_mypyc }}
@@ -230,7 +230,7 @@ jobs:
           default: 3.11.1
           command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');"
       - name: Install tox
-        run: pip install tox==4.21.2
+        run: pip install setuptools==75.1.0 tox==4.21.2
       - name: Setup tox environment
         run: tox run -e py --notest
       - name: Test
diff --git a/pyproject.toml b/pyproject.toml
index ddc28f458d50..8a1177f60009 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,7 +3,7 @@ requires = [
     # NOTE: this needs to be kept in sync with mypy-requirements.txt
     # and build-requirements.txt, because those are both needed for
     # self-typechecking :/
-    "setuptools >= 77.0.3",
+    "setuptools >= 75.1.0",
     # the following is from mypy-requirements.txt/setup.py
     "typing_extensions>=4.6.0",
     "mypy_extensions>=1.0.0",
@@ -30,12 +30,12 @@ features such as type inference, gradual typing, generics and union
 types.
 """, content-type = "text/x-rst"}
 authors = [{name = "Jukka Lehtosalo", email = "jukka.lehtosalo@iki.fi"}]
-license = "MIT"
-license-files = ["LICENSE", "mypy/typeshed/LICENSE"]
+license = {text = "MIT"}
 classifiers = [
   "Development Status :: 5 - Production/Stable",
   "Environment :: Console",
   "Intended Audience :: Developers",
+  "License :: OSI Approved :: MIT License",
   "Programming Language :: Python :: 3",
   "Programming Language :: Python :: 3.9",
   "Programming Language :: Python :: 3.10",
diff --git a/test-requirements.in b/test-requirements.in
index 6e4e792bb6b1..666dd9fc082c 100644
--- a/test-requirements.in
+++ b/test-requirements.in
@@ -10,6 +10,6 @@ psutil>=4.0
 pytest>=8.1.0
 pytest-xdist>=1.34.0
 pytest-cov>=2.10.0
-setuptools>=77.0.3
+setuptools>=75.1.0
 tomli>=1.1.0  # needed even on py311+ so the self check passes with --python-version 3.9
 pre_commit>=3.5.0
diff --git a/test-requirements.txt b/test-requirements.txt
index eb34795fa842..51281f0e4c11 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -63,5 +63,5 @@ virtualenv==20.29.1
     # via pre-commit
 
 # The following packages are considered to be unsafe in a requirements file:
-setuptools==77.0.3
+setuptools==75.8.0
     # via -r test-requirements.in

From a3aac7102b5c9f74d000d857a25e7ab044be62a2 Mon Sep 17 00:00:00 2001
From: sobolevn 
Date: Sat, 10 May 2025 10:15:04 +0300
Subject: [PATCH 1284/1617] Add special support for `@django.cached_property`
 needed in `django-stubs` (#18959)

Hi!

We, in `django-stubs`, have a lot of usages of `@cached_property`
decorator that is a part of `django`:
https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.functional.cached_property

All usages of it we have to add to `subtest/allowlist.txt`, which is not
great. In typing we reuse `@functools.cached_property` to have all the
benefits of its inference:
https://github.com/typeddjango/django-stubs/blob/ee8e8b11c37866969ff0406be20591a067dfa983/django-stubs/utils/functional.pyi#L3-L4

But, `stubtest` is not happy with this move: because in runtime objects
have `django.utils.functional.cached_property` type and we see the
following error:

```

error: django.http.response.HttpResponse.text is inconsistent, cannot reconcile @property on stub with runtime object
Stub: in file /home/runner/work/django-stubs/django-stubs/django-stubs/http/response.pyi:106
def (self: django.http.response.HttpResponse) -> builtins.str
Runtime:

```

So, we add all `@django.utils.functional.cached_property` usages to our
`allowlist.txt`. There are LOTS of entries there:
https://github.com/typeddjango/django-stubs/blob/ee8e8b11c37866969ff0406be20591a067dfa983/scripts/stubtest/allowlist.txt#L158-L425

Moreover, we have to always tell about this problem to new contributors
on review :(

That's why I propose to special case this as we do with other
`property`-likes.
I've tested locally and it works perfectly. I don't want to complicate
the CI with `django` installation and special tests. So, I added `#
pragma: no cover` to indicate that it is not tested.
---
 mypy/stubtest.py | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index ea09dac8ec95..733504e8c234 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -1256,6 +1256,19 @@ def verify_paramspecexpr(
         return
 
 
+def _is_django_cached_property(runtime: Any) -> bool:  # pragma: no cover
+    # This is a special case for
+    # https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.functional.cached_property
+    # This is needed in `django-stubs` project:
+    # https://github.com/typeddjango/django-stubs
+    if type(runtime).__name__ != "cached_property":
+        return False
+    try:
+        return bool(runtime.func)
+    except Exception:
+        return False
+
+
 def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[str]:
     assert stub.func.is_property
     if isinstance(runtime, property):
@@ -1264,6 +1277,9 @@ def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[s
     if isinstance(runtime, functools.cached_property):
         yield from _verify_final_method(stub.func, runtime.func, MISSING)
         return
+    if _is_django_cached_property(runtime):
+        yield from _verify_final_method(stub.func, runtime.func, MISSING)
+        return
     if inspect.isdatadescriptor(runtime):
         # It's enough like a property...
         return

From c6c6e41b36af837563aa139d251ee7ad41250671 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 10 May 2025 23:28:28 +0100
Subject: [PATCH 1285/1617] Speed up bind_self() in trivial cases (#19024)

See https://github.com/python/mypy/issues/18991 for context.

We can skip all of logic in `check_self_arg()` and 90% of logic in
`bind_self()` for methods with trivial `self`/`cls` (i.e. if first
argument has no explicit annotation and there is no `Self` in
signature).

Locally I see 3-4% performance improvement (for self-check with
non-compiled mypy).
---
 mypy/checkmember.py | 108 ++++++++++++++++++++++++++++++++++++++------
 mypy/nodes.py       |  30 +++++++++++-
 mypy/semanal.py     |   1 +
 3 files changed, 123 insertions(+), 16 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index d5d1f862a9d9..cc104fed0752 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -3,7 +3,7 @@
 from __future__ import annotations
 
 from collections.abc import Sequence
-from typing import Callable, cast
+from typing import Callable, TypeVar, cast
 
 from mypy import message_registry, state, subtypes
 from mypy.checker_shared import TypeCheckerSharedApi
@@ -18,6 +18,7 @@
 from mypy.nodes import (
     ARG_POS,
     ARG_STAR,
+    ARG_STAR2,
     EXCLUDED_ENUM_ATTRIBUTES,
     SYMBOL_FUNCBASE_TYPES,
     Context,
@@ -359,10 +360,13 @@ def analyze_instance_member_access(
             signature = method.type
         signature = freshen_all_functions_type_vars(signature)
         if not method.is_static:
-            signature = check_self_arg(
-                signature, mx.self_type, method.is_class, mx.context, name, mx.msg
-            )
-            signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
+            if isinstance(method, (FuncDef, OverloadedFuncDef)) and method.is_trivial_self:
+                signature = bind_self_fast(signature, mx.self_type)
+            else:
+                signature = check_self_arg(
+                    signature, mx.self_type, method.is_class, mx.context, name, mx.msg
+                )
+                signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
         # TODO: should we skip these steps for static methods as well?
         # Since generic static methods should not be allowed.
         typ = map_instance_to_supertype(typ, method.info)
@@ -521,9 +525,11 @@ def analyze_member_var_access(
     mx.chk.warn_deprecated(v, mx.context)
 
     vv = v
+    is_trivial_self = False
     if isinstance(vv, Decorator):
         # The associated Var node of a decorator contains the type.
         v = vv.var
+        is_trivial_self = vv.func.is_trivial_self and not vv.decorators
         if mx.is_super and not mx.suppress_errors:
             validate_super_call(vv.func, mx)
 
@@ -555,7 +561,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, mx, implicit=implicit)
+        return analyze_var(name, v, itype, mx, implicit=implicit, is_trivial_self=is_trivial_self)
     elif isinstance(v, FuncDef):
         assert False, "Did not expect a function"
     elif isinstance(v, MypyFile):
@@ -850,7 +856,13 @@ def is_instance_var(var: Var) -> bool:
 
 
 def analyze_var(
-    name: str, var: Var, itype: Instance, mx: MemberContext, *, implicit: bool = False
+    name: str,
+    var: Var,
+    itype: Instance,
+    mx: MemberContext,
+    *,
+    implicit: bool = False,
+    is_trivial_self: bool = False,
 ) -> Type:
     """Analyze access to an attribute via a Var node.
 
@@ -858,6 +870,7 @@ def analyze_var(
     itype is the instance type in which attribute should be looked up
     original_type is the type of E in the expression E.var
     if implicit is True, the original Var was created as an assignment to self
+    if is_trivial_self is True, we can use fast path for bind_self().
     """
     # Found a member variable.
     original_itype = itype
@@ -904,7 +917,7 @@ def analyze_var(
             for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]:
                 p_ct = get_proper_type(ct)
                 if isinstance(p_ct, FunctionLike) and not p_ct.is_type_obj():
-                    item = expand_and_bind_callable(p_ct, var, itype, name, mx)
+                    item = expand_and_bind_callable(p_ct, var, itype, name, mx, is_trivial_self)
                 else:
                     item = expand_without_binding(ct, var, itype, original_itype, mx)
                 bound_items.append(item)
@@ -938,13 +951,21 @@ def expand_without_binding(
 
 
 def expand_and_bind_callable(
-    functype: FunctionLike, var: Var, itype: Instance, name: str, mx: MemberContext
+    functype: FunctionLike,
+    var: Var,
+    itype: Instance,
+    name: str,
+    mx: MemberContext,
+    is_trivial_self: bool,
 ) -> Type:
     functype = freshen_all_functions_type_vars(functype)
     typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
     assert isinstance(typ, FunctionLike)
-    typ = check_self_arg(typ, mx.self_type, var.is_classmethod, mx.context, name, mx.msg)
-    typ = bind_self(typ, mx.self_type, var.is_classmethod)
+    if is_trivial_self:
+        typ = bind_self_fast(typ, mx.self_type)
+    else:
+        typ = check_self_arg(typ, mx.self_type, var.is_classmethod, mx.context, name, mx.msg)
+        typ = bind_self(typ, mx.self_type, var.is_classmethod)
     expanded = expand_type_by_instance(typ, itype)
     freeze_all_type_vars(expanded)
     if not var.is_property:
@@ -1203,10 +1224,22 @@ def analyze_class_attribute_access(
             isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
         )
         t = get_proper_type(t)
-        if isinstance(t, FunctionLike) and is_classmethod:
+        is_trivial_self = False
+        if isinstance(node.node, Decorator):
+            # Use fast path if there are trivial decorators like @classmethod or @property
+            is_trivial_self = node.node.func.is_trivial_self and not node.node.decorators
+        elif isinstance(node.node, (FuncDef, OverloadedFuncDef)):
+            is_trivial_self = node.node.is_trivial_self
+        if isinstance(t, FunctionLike) and is_classmethod and not is_trivial_self:
             t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
         result = add_class_tvars(
-            t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars
+            t,
+            isuper,
+            is_classmethod,
+            is_staticmethod,
+            mx.self_type,
+            original_vars=original_vars,
+            is_trivial_self=is_trivial_self,
         )
         # __set__ is not called on class objects.
         if not mx.is_lvalue:
@@ -1255,7 +1288,7 @@ def analyze_class_attribute_access(
         # Annotated and/or explicit class methods go through other code paths above, for
         # unannotated implicit class methods we do this here.
         if node.node.is_class:
-            typ = bind_self(typ, is_classmethod=True)
+            typ = bind_self_fast(typ)
         return apply_class_attr_hook(mx, hook, typ)
 
 
@@ -1342,6 +1375,7 @@ def add_class_tvars(
     is_staticmethod: bool,
     original_type: Type,
     original_vars: Sequence[TypeVarLikeType] | None = None,
+    is_trivial_self: bool = False,
 ) -> Type:
     """Instantiate type variables during analyze_class_attribute_access,
     e.g T and Q in the following:
@@ -1362,6 +1396,7 @@ class B(A[str]): pass
         original_type: The value of the type B in the expression B.foo() or the corresponding
             component in case of a union (this is used to bind the self-types)
         original_vars: Type variables of the class callable on which the method was accessed
+        is_trivial_self: if True, we can use fast path for bind_self().
     Returns:
         Expanded method type with added type variables (when needed).
     """
@@ -1383,7 +1418,10 @@ class B(A[str]): pass
         tvars = original_vars if original_vars is not None else []
         t = freshen_all_functions_type_vars(t)
         if is_classmethod:
-            t = bind_self(t, original_type, is_classmethod=True)
+            if is_trivial_self:
+                t = bind_self_fast(t, original_type)
+            else:
+                t = bind_self(t, original_type, is_classmethod=True)
         if is_classmethod or is_staticmethod:
             assert isuper is not None
             t = expand_type_by_instance(t, isuper)
@@ -1422,5 +1460,45 @@ def analyze_decorator_or_funcbase_access(
     if isinstance(defn, Decorator):
         return analyze_var(name, defn.var, itype, mx)
     typ = function_type(defn, mx.chk.named_type("builtins.function"))
+    is_trivial_self = False
+    if isinstance(defn, Decorator):
+        # Use fast path if there are trivial decorators like @classmethod or @property
+        is_trivial_self = defn.func.is_trivial_self and not defn.decorators
+    elif isinstance(defn, (FuncDef, OverloadedFuncDef)):
+        is_trivial_self = defn.is_trivial_self
+    if is_trivial_self:
+        return bind_self_fast(typ, mx.self_type)
     typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg)
     return bind_self(typ, original_type=mx.self_type, is_classmethod=defn.is_class)
+
+
+F = TypeVar("F", bound=FunctionLike)
+
+
+def bind_self_fast(method: F, original_type: Type | None = None) -> F:
+    """Return a copy of `method`, with the type of its first parameter (usually
+    self or cls) bound to original_type.
+
+    This is a faster version of mypy.typeops.bind_self() that can be used for methods
+    with trivial self/cls annotations.
+    """
+    if isinstance(method, Overloaded):
+        items = [bind_self_fast(c, original_type) for c in method.items]
+        return cast(F, Overloaded(items))
+    assert isinstance(method, CallableType)
+    if not method.arg_types:
+        # Invalid method, return something.
+        return cast(F, method)
+    if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
+        # See typeops.py for details.
+        return cast(F, method)
+    original_type = get_proper_type(original_type)
+    if isinstance(original_type, CallableType) and original_type.is_type_obj():
+        original_type = TypeType.make_normalized(original_type.ret_type)
+    res = method.copy_modified(
+        arg_types=method.arg_types[1:],
+        arg_kinds=method.arg_kinds[1:],
+        arg_names=method.arg_names[1:],
+        bound_args=[original_type],
+    )
+    return cast(F, res)
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 45c59e0c765e..584e56667944 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -550,7 +550,7 @@ class OverloadedFuncDef(FuncBase, SymbolNode, Statement):
     Overloaded variants must be consecutive in the source file.
     """
 
-    __slots__ = ("items", "unanalyzed_items", "impl", "deprecated")
+    __slots__ = ("items", "unanalyzed_items", "impl", "deprecated", "_is_trivial_self")
 
     items: list[OverloadPart]
     unanalyzed_items: list[OverloadPart]
@@ -563,6 +563,7 @@ def __init__(self, items: list[OverloadPart]) -> None:
         self.unanalyzed_items = items.copy()
         self.impl = None
         self.deprecated = None
+        self._is_trivial_self: bool | None = None
         if items:
             # TODO: figure out how to reliably set end position (we don't know the impl here).
             self.set_line(items[0].line, items[0].column)
@@ -576,6 +577,27 @@ def name(self) -> str:
             assert self.impl is not None
             return self.impl.name
 
+    @property
+    def is_trivial_self(self) -> bool:
+        """Check we can use bind_self() fast path for this overload.
+
+        This will return False if at least one overload:
+          * Has an explicit self annotation, or Self in signature.
+          * Has a non-trivial decorator.
+        """
+        if self._is_trivial_self is not None:
+            return self._is_trivial_self
+        for item in self.items:
+            if isinstance(item, FuncDef):
+                if not item.is_trivial_self:
+                    self._is_trivial_self = False
+                    return False
+            elif item.decorators or not item.func.is_trivial_self:
+                self._is_trivial_self = False
+                return False
+        self._is_trivial_self = True
+        return True
+
     def accept(self, visitor: StatementVisitor[T]) -> T:
         return visitor.visit_overloaded_func_def(self)
 
@@ -747,6 +769,7 @@ def is_dynamic(self) -> bool:
     "is_decorated",
     "is_conditional",
     "is_trivial_body",
+    "is_trivial_self",
     "is_mypy_only",
 ]
 
@@ -771,6 +794,7 @@ class FuncDef(FuncItem, SymbolNode, Statement):
         "abstract_status",
         "original_def",
         "is_trivial_body",
+        "is_trivial_self",
         "is_mypy_only",
         # Present only when a function is decorated with @typing.dataclass_transform or similar
         "dataclass_transform_spec",
@@ -804,6 +828,10 @@ def __init__(
         self.dataclass_transform_spec: DataclassTransformSpec | None = None
         self.docstring: str | None = None
         self.deprecated: str | None = None
+        # This is used to simplify bind_self() logic in trivial cases (which are
+        # the majority). In cases where self is not annotated and there are no Self
+        # in the signature we can simply drop the first argument.
+        self.is_trivial_self = False
 
     @property
     def name(self) -> str:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 1b592e722cb4..89bb5ab97c2a 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1085,6 +1085,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type:
                         assert self.type is not None and self.type.self_type is not None
                         leading_type: Type = self.type.self_type
                     else:
+                        func.is_trivial_self = True
                         leading_type = fill_typevars(info)
                     if func.is_class or func.name == "__new__":
                         leading_type = self.class_type(leading_type)

From fe8ca3bb10fcac2ed29fa01e00a0aaf5fcb5d277 Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 10 May 2025 16:16:42 -0700
Subject: [PATCH 1286/1617] Add the capacity to run individual tests and test
 files to runtests.py (#19069)

I have added the capacity to run individual tests and files by
specifying them to runtests.py, removing the burden of remembering the
correct arguments to pytest. I have updated the contributor
documentation accordingly.
---
 CONTRIBUTING.md |  8 ++++++--
 runtests.py     | 23 +++++++++++++++++++----
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e782158ba21f..8d7dd2d1e886 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -76,10 +76,14 @@ python runtests.py self
 # or equivalently:
 python -m mypy --config-file mypy_self_check.ini -p mypy
 
-# Run a single test from the test suite
-pytest -n0 -k 'test_name'
+# Run a single test from the test suite (uses pytest substring expression matching)
+python runtests.py test_name
+# or equivalently:
+pytest -n0 -k test_name
 
 # Run all test cases in the "test-data/unit/check-dataclasses.test" file
+python runtests.py check-dataclasses.test
+# or equivalently:
 pytest mypy/test/testcheck.py::TypeCheckSuite::check-dataclasses.test
 
 # Run the formatters and linters
diff --git a/runtests.py b/runtests.py
index 75389c6c56bb..3f49107f3ce0 100755
--- a/runtests.py
+++ b/runtests.py
@@ -111,7 +111,13 @@
 
 def run_cmd(name: str) -> int:
     status = 0
-    cmd = cmds[name]
+    if name in cmds:
+        cmd = cmds[name]
+    else:
+        if name.endswith(".test"):
+            cmd = ["pytest", f"mypy/test/testcheck.py::TypeCheckSuite::{name}"]
+        else:
+            cmd = ["pytest", "-n0", "-k", name]
     print(f"run {name}: {cmd}")
     proc = subprocess.run(cmd, stderr=subprocess.STDOUT)
     if proc.returncode:
@@ -144,13 +150,22 @@ def main() -> None:
     prog, *args = argv
 
     if not set(args).issubset(cmds):
-        print("usage:", prog, " ".join(f"[{k}]" for k in cmds))
+        print(
+            "usage:",
+            prog,
+            " ".join(f"[{k}]" for k in cmds),
+            "[names of individual tests and files...]",
+        )
         print()
         print(
             "Run the given tests. If given no arguments, run everything except"
-            + " pytest-extra and mypyc-extra."
+            + " pytest-extra and mypyc-extra. Unrecognized arguments will be"
+            + " interpreted as individual test names / substring expressions"
+            + " (or, if they end in .test, individual test files)"
+            + " and this script will try to run them."
         )
-        exit(1)
+        if "-h" in args or "--help" in args:
+            exit(1)
 
     if not args:
         args = DEFAULT_COMMANDS.copy()

From 18a66959c4ac2ba94b29397aed26bc35192bc013 Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 10 May 2025 16:19:21 -0700
Subject: [PATCH 1287/1617] Re-add documentation for formatting mypy --help
 text  (#19063)

Fixes #15555

This re-adds some rather-innocuous documentation to main about how to
write command line flag descriptions, which were on a wiki page that was
subsequently destroyed (rendering the link in the comment dead).
---
 mypy/main.py | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/mypy/main.py b/mypy/main.py
index 7bd7215bbe2a..b2abf06897de 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -535,8 +535,27 @@ def add_invertible_flag(
     # their `dest` prefixed with `special-opts:`, which will cause them to be
     # parsed into the separate special_opts namespace object.
 
-    # Note: we have a style guide for formatting the mypy --help text. See
-    # https://github.com/python/mypy/wiki/Documentation-Conventions
+    # Our style guide for formatting the output of running `mypy --help`:
+    # Flags:
+    # 1.  The flag help text should start with a capital letter but never end with a period.
+    # 2.  Keep the flag help text brief -- ideally just a single sentence.
+    # 3.  All flags must be a part of a group, unless the flag is deprecated or suppressed.
+    # 4.  Avoid adding new flags to the "miscellaneous" groups -- instead add them to an
+    #     existing group or, if applicable, create a new group. Feel free to move existing
+    #     flags to a new group: just be sure to also update the documentation to match.
+    #
+    # Groups:
+    # 1.  The group title and description should start with a capital letter.
+    # 2.  The first sentence of a group description should be written in the bare infinitive.
+    #     Tip: try substituting the group title and description into the following sentence:
+    #     > {group_title}: these flags will {group_description}
+    #     Feel free to add subsequent sentences that add additional details.
+    # 3.  If you cannot think of a meaningful description for a new group, omit it entirely.
+    #     (E.g. see the "miscellaneous" sections).
+    # 4.  The group description should end with a period (unless the last line is a link). If you
+    #     do end the group description with a link, omit the 'http://' prefix. (Some links are too
+    #     long and will break up into multiple lines if we include that prefix, so for consistency
+    #     we omit the prefix on all links.)
 
     general_group = parser.add_argument_group(title="Optional arguments")
     general_group.add_argument(

From 64b0a571e78c098316d69ebe280d8ea0d96a2737 Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 10 May 2025 16:23:25 -0700
Subject: [PATCH 1288/1617] Add some functionality to misc/perf_compare.py
 (#18471)

While working on determining the performance impact of #17875, I
discovered misc/perf_compare.py. I extended its functionality slightly,
introducing an -r flag to check a foreign repo instead of mypy itself. I
also added a --dont-setup flag, to save time on recompiling mypy. I
added several helpful printouts: the standard deviations of the samples,
a line at the bottom listing the total time taken by the whole
benchmarking program. I improved the cli documentation with the
docstring, better descriptions, and an epilog reminding you to probably
specify master.

As a caveat, I should flag: when running the benchmark on the foreign
repo, it typically spits out a lot of errors, presumably because the
libraries aren't installed. That makes sense to me, though, and seems
fine.

Another caveat I'll flag on the off-chance it's important: on my
machine, this script seems constitutionally incapable of deleting the
tmpdirs it makes. It fails with a permissions error. But it was like
that when I got it.
---
 misc/perf_compare.py | 107 +++++++++++++++++++++++++++++++++----------
 1 file changed, 84 insertions(+), 23 deletions(-)

diff --git a/misc/perf_compare.py b/misc/perf_compare.py
index ef9976b8e2eb..025d4065561e 100644
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -9,7 +9,7 @@
  * Create a temp clone of the mypy repo for each target commit to measure
  * Checkout a target commit in each of the clones
  * Compile mypyc in each of the clones *in parallel*
- * Create another temp clone of the mypy repo as the code to check
+ * Create another temp clone of the first provided revision (or, with -r, a foreign repo) as the code to check
  * Self check with each of the compiled mypys N times
  * Report the average runtimes and relative performance
  * Remove the temp clones
@@ -44,13 +44,15 @@ def build_mypy(target_dir: str) -> None:
     subprocess.run(cmd, env=env, check=True, cwd=target_dir)
 
 
-def clone(target_dir: str, commit: str | None) -> None:
-    heading(f"Cloning mypy to {target_dir}")
-    repo_dir = os.getcwd()
+def clone(target_dir: str, commit: str | None, repo_source: str | None = None) -> None:
+    source_name = repo_source or "mypy"
+    heading(f"Cloning {source_name} to {target_dir}")
+    if repo_source is None:
+        repo_source = os.getcwd()
     if os.path.isdir(target_dir):
         print(f"{target_dir} exists: deleting")
         shutil.rmtree(target_dir)
-    subprocess.run(["git", "clone", repo_dir, target_dir], check=True)
+    subprocess.run(["git", "clone", repo_source, target_dir], check=True)
     if commit:
         subprocess.run(["git", "checkout", commit], check=True, cwd=target_dir)
 
@@ -64,7 +66,7 @@ def edit_python_file(fnam: str) -> None:
 
 
 def run_benchmark(
-    compiled_dir: str, check_dir: str, *, incremental: bool, code: str | None
+    compiled_dir: str, check_dir: str, *, incremental: bool, code: str | None, foreign: bool | None
 ) -> float:
     cache_dir = os.path.join(compiled_dir, ".mypy_cache")
     if os.path.isdir(cache_dir) and not incremental:
@@ -76,6 +78,8 @@ def run_benchmark(
     cmd = [sys.executable, "-m", "mypy"]
     if code:
         cmd += ["-c", code]
+    elif foreign:
+        pass
     else:
         cmd += ["--config-file", os.path.join(abschk, "mypy_self_check.ini")]
         cmd += glob.glob(os.path.join(abschk, "mypy/*.py"))
@@ -86,18 +90,33 @@ def run_benchmark(
             edit_python_file(os.path.join(abschk, "mypy/test/testcheck.py"))
     t0 = time.time()
     # Ignore errors, since some commits being measured may generate additional errors.
-    subprocess.run(cmd, cwd=compiled_dir, env=env)
+    if foreign:
+        subprocess.run(cmd, cwd=check_dir, env=env)
+    else:
+        subprocess.run(cmd, cwd=compiled_dir, env=env)
     return time.time() - t0
 
 
 def main() -> None:
-    parser = argparse.ArgumentParser()
+    whole_program_time_0 = time.time()
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__,
+        epilog="Remember: you usually want the first argument to this command to be 'master'.",
+    )
     parser.add_argument(
         "--incremental",
         default=False,
         action="store_true",
         help="measure incremental run (fully cached)",
     )
+    parser.add_argument(
+        "--dont-setup",
+        default=False,
+        action="store_true",
+        help="don't make the clones or compile mypy, just run the performance measurement benchmark "
+        + "(this will fail unless the clones already exist, such as from a previous run that was canceled before it deleted them)",
+    )
     parser.add_argument(
         "--num-runs",
         metavar="N",
@@ -112,6 +131,15 @@ def main() -> None:
         type=int,
         help="set maximum number of parallel builds (default=8)",
     )
+    parser.add_argument(
+        "-r",
+        metavar="FOREIGN_REPOSITORY",
+        default=None,
+        type=str,
+        help="measure time to typecheck the project at FOREIGN_REPOSITORY instead of mypy self-check; "
+        + "the provided value must be the URL or path of a git repo "
+        + "(note that this script will take no special steps to *install* the foreign repo, so you will probably get a lot of missing import errors)",
+    )
     parser.add_argument(
         "-c",
         metavar="CODE",
@@ -119,35 +147,49 @@ def main() -> None:
         type=str,
         help="measure time to type check Python code fragment instead of mypy self-check",
     )
-    parser.add_argument("commit", nargs="+", help="git revision to measure (e.g. branch name)")
+    parser.add_argument(
+        "commit",
+        nargs="+",
+        help="git revision(s), e.g. branch name or commit id, to measure the performance of",
+    )
     args = parser.parse_args()
     incremental: bool = args.incremental
+    dont_setup: bool = args.dont_setup
     commits = args.commit
     num_runs: int = args.num_runs + 1
     max_workers: int = args.j
     code: str | None = args.c
+    foreign_repo: str | None = args.r
 
     if not (os.path.isdir(".git") and os.path.isdir("mypyc")):
-        sys.exit("error: Run this the mypy repo root")
+        sys.exit("error: You must run this script from the mypy repo root")
 
     target_dirs = []
     for i, commit in enumerate(commits):
         target_dir = f"mypy.{i}.tmpdir"
         target_dirs.append(target_dir)
-        clone(target_dir, commit)
+        if not dont_setup:
+            clone(target_dir, commit)
 
-    self_check_dir = "mypy.self.tmpdir"
-    clone(self_check_dir, commits[0])
+    if foreign_repo:
+        check_dir = "mypy.foreign.tmpdir"
+        if not dont_setup:
+            clone(check_dir, None, foreign_repo)
+    else:
+        check_dir = "mypy.self.tmpdir"
+        if not dont_setup:
+            clone(check_dir, commits[0])
 
-    heading("Compiling mypy")
-    print("(This will take a while...)")
+    if not dont_setup:
+        heading("Compiling mypy")
+        print("(This will take a while...)")
 
-    with ThreadPoolExecutor(max_workers=max_workers) as executor:
-        futures = [executor.submit(build_mypy, target_dir) for target_dir in target_dirs]
-        for future in as_completed(futures):
-            future.result()
+        with ThreadPoolExecutor(max_workers=max_workers) as executor:
+            futures = [executor.submit(build_mypy, target_dir) for target_dir in target_dirs]
+            for future in as_completed(futures):
+                future.result()
 
-    print(f"Finished compiling mypy ({len(commits)} builds)")
+        print(f"Finished compiling mypy ({len(commits)} builds)")
 
     heading("Performing measurements")
 
@@ -160,7 +202,13 @@ def main() -> None:
         items = list(enumerate(commits))
         random.shuffle(items)
         for i, commit in items:
-            tt = run_benchmark(target_dirs[i], self_check_dir, incremental=incremental, code=code)
+            tt = run_benchmark(
+                target_dirs[i],
+                check_dir,
+                incremental=incremental,
+                code=code,
+                foreign=bool(foreign_repo),
+            )
             # Don't record the first warm-up run
             if n > 0:
                 print(f"{commit}: t={tt:.3f}s")
@@ -171,15 +219,28 @@ def main() -> None:
     first = -1.0
     for commit in commits:
         tt = statistics.mean(results[commit])
+        # pstdev (instead of stdev) is used here primarily to accommodate the case where num_runs=1
+        s = statistics.pstdev(results[commit]) if len(results[commit]) > 1 else 0
         if first < 0:
             delta = "0.0%"
             first = tt
         else:
             d = (tt / first) - 1
             delta = f"{d:+.1%}"
-        print(f"{commit:<25} {tt:.3f}s ({delta})")
+        print(f"{commit:<25} {tt:.3f}s ({delta}) | stdev {s:.3f}s ")
+
+    t = int(time.time() - whole_program_time_0)
+    total_time_taken_formatted = ", ".join(
+        f"{v} {n if v==1 else n+'s'}"
+        for v, n in ((t // 3600, "hour"), (t // 60 % 60, "minute"), (t % 60, "second"))
+        if v
+    )
+    print(
+        "Total time taken by the whole benchmarking program (including any setup):",
+        total_time_taken_formatted,
+    )
 
-    shutil.rmtree(self_check_dir)
+    shutil.rmtree(check_dir)
     for target_dir in target_dirs:
         shutil.rmtree(target_dir)
 

From 1a2dccf1ef10b392f86226f6d72929ac6e03908d Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra 
Date: Mon, 12 May 2025 03:00:41 -0700
Subject: [PATCH 1289/1617] Fix stubtest tests on 3.14 (#19074)

The annotations-related ones are due to PEP 649/749. `__classdictcell__`
is from PEP 695 (Python 3.12); not 100% sure what changed there.
---
 mypy/stubtest.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 733504e8c234..39b27a1f1ed3 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -1494,6 +1494,7 @@ def verify_typealias(
         "__loader__",
         "__spec__",
         "__annotations__",
+        "__annotate__",
         "__path__",  # mypy adds __path__ to packages, but C packages don't have it
         "__getattr__",  # resulting behaviour might be typed explicitly
         # Created by `warnings.warn`, does not make much sense to have in stubs:
@@ -1510,6 +1511,9 @@ def verify_typealias(
         # Special attributes
         "__dict__",
         "__annotations__",
+        "__annotate__",
+        "__annotations_cache__",
+        "__annotate_func__",
         "__text_signature__",
         "__weakref__",
         "__hash__",
@@ -1518,6 +1522,7 @@ def verify_typealias(
         "__vectorcalloffset__",  # undocumented implementation detail of the vectorcall protocol
         "__firstlineno__",
         "__static_attributes__",
+        "__classdictcell__",
         # isinstance/issubclass hooks that type-checkers don't usually care about
         "__instancecheck__",
         "__subclasshook__",

From 52975f6aff842bdaf11c94b75cb75dd0618603d1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 12 May 2025 14:31:45 +0100
Subject: [PATCH 1290/1617] Ignore a few override errors in typeshed (#19079)

I was seeing errors like these in our Bazel-based mypy configuration,
which may type check typeshed in some non-standard way:
```
mypy/typeshed/stdlib/tempfile.pyi:390: error: Argument 1 of "writelines" is incompatible with supertype "_IOBase"; supertype defines the argument type as "Iterable[Buffer]"  [override]
mypy/typeshed/stdlib/tempfile.pyi:390: note: This violates the Liskov substitution principle
mypy/typeshed/stdlib/tempfile.pyi:390: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
mypy/typeshed/stdlib/tkinter/ttk.pyi:565: error: Signature of "forget" incompatible with supertype "Pack"  [override]
mypy/typeshed/stdlib/tkinter/ttk.pyi:565: note:      Superclass:
mypy/typeshed/stdlib/tkinter/ttk.pyi:565: note:          def pack_forget(self) -> None
mypy/typeshed/stdlib/tkinter/ttk.pyi:565: note:      Subclass:
mypy/typeshed/stdlib/tkinter/ttk.pyi:565: note:          def forget(self, tab_id: Any) -> None
```

I'm just merging this directly to the mypy repo so that I can move
forward more quickly, as this is needed to unblock the public release.
---
 mypy/typeshed/stdlib/tempfile.pyi    | 2 +-
 mypy/typeshed/stdlib/tkinter/ttk.pyi | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi
index 0c19d56fc7a6..d2677603bc47 100644
--- a/mypy/typeshed/stdlib/tempfile.pyi
+++ b/mypy/typeshed/stdlib/tempfile.pyi
@@ -387,7 +387,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase):
     def write(self: SpooledTemporaryFile[bytes], s: ReadableBuffer) -> int: ...
     @overload
     def write(self, s: AnyStr) -> int: ...
-    @overload
+    @overload  #  type: ignore[override]
     def writelines(self: SpooledTemporaryFile[str], iterable: Iterable[str]) -> None: ...
     @overload
     def writelines(self: SpooledTemporaryFile[bytes], iterable: Iterable[ReadableBuffer]) -> None: ...
diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi
index 5328e461ebdc..ab3c010938be 100644
--- a/mypy/typeshed/stdlib/tkinter/ttk.pyi
+++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi
@@ -562,7 +562,7 @@ class Notebook(Widget):
         compound: tkinter._Compound = ...,
         underline: int = ...,
     ) -> None: ...
-    def forget(self, tab_id) -> None: ...
+    def forget(self, tab_id) -> None: ...  # type: ignore[override]
     def hide(self, tab_id) -> None: ...
     def identify(self, x: int, y: int) -> str: ...
     def index(self, tab_id): ...

From ca609acabdc94ee973a53d62b8dcb7e55c789aec Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 12 May 2025 15:46:24 +0100
Subject: [PATCH 1291/1617] Empty commit to trigger wheel builds


From addcff2d14877a00263068c8648ea12d53391b53 Mon Sep 17 00:00:00 2001
From: Nick Pope 
Date: Mon, 12 May 2025 18:49:59 +0100
Subject: [PATCH 1292/1617] Make stubtest ignore `__slotnames__` (#19077)

This is a cached list of names of slots added by the `copyreg` module.

See https://github.com/typeddjango/django-stubs/pull/2584 for a case of
these cropping up in `django-stubs`.
---
 mypy/stubtest.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 39b27a1f1ed3..f9e6f7d337be 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -1541,6 +1541,7 @@ def verify_typealias(
         "__getinitargs__",
         "__reduce_ex__",
         "__reduce__",
+        "__slotnames__",  # Cached names of slots added by `copyreg` module.
         # ctypes weirdness
         "__ctype_be__",
         "__ctype_le__",

From 81f62852384f34c5d5650b38afaa7e3c2bcde8a6 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 13 May 2025 01:18:44 +0200
Subject: [PATCH 1293/1617] Fall back to Incomplete if we are unable to
 determine the module name (#19084)

Fixes crash in #19031, but the support of `cvar` in SWIG remains an open
question.
---
 mypy/stubgenc.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py
index b03a88cf6f43..b675079dd8dd 100755
--- a/mypy/stubgenc.py
+++ b/mypy/stubgenc.py
@@ -769,7 +769,11 @@ def get_type_fullname(self, typ: type) -> str:
             return "Any"
         typename = getattr(typ, "__qualname__", typ.__name__)
         module_name = self.get_obj_module(typ)
-        assert module_name is not None, typ
+        if module_name is None:
+            # This should not normally happen, but some types may resist our
+            # introspection attempts too hard. See
+            # https://github.com/python/mypy/issues/19031
+            return "_typeshed.Incomplete"
         if module_name != "builtins":
             typename = f"{module_name}.{typename}"
         return typename

From 0b65f215996401264a68a3a06f3fbcd19915a9a5 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 13 May 2025 04:57:39 +0200
Subject: [PATCH 1294/1617] Admit that Final variables are never redefined
 (#19083)

Fixes #19080. There is no point applying our heuristics if the variable
is declared Final - it is not reassigned anywhere.

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
---
 mypy/checker.py                 |  4 ++++
 test-data/unit/check-final.test | 22 ++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/mypy/checker.py b/mypy/checker.py
index 2d82d74cc197..758a860abf18 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1557,6 +1557,10 @@ def is_var_redefined_in_outer_context(self, v: Var, after_line: int) -> bool:
         Note that this doesn't do a full CFG analysis but uses a line number based
         heuristic that isn't correct in some (rare) cases.
         """
+        if v.is_final:
+            # Final vars are definitely never reassigned.
+            return False
+
         outers = self.tscope.outer_functions()
         if not outers:
             # Top-level function -- outer context is top level, and we can't reason about
diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test
index 4b0bab45d16c..d78c2a8e57f2 100644
--- a/test-data/unit/check-final.test
+++ b/test-data/unit/check-final.test
@@ -1250,3 +1250,25 @@ def check_final_init() -> None:
     new_instance = FinalInit()
     new_instance.__init__()
 [builtins fixtures/tuple.pyi]
+
+[case testNarrowingOfFinalPersistsInFunctions]
+from typing import Final, Union
+
+def _init() -> Union[int, None]:
+    return 0
+
+FOO: Final = _init()
+
+class Example:
+
+    if FOO is not None:
+        reveal_type(FOO)  # N: Revealed type is "builtins.int"
+
+        def fn(self) -> int:
+            return FOO
+
+if FOO is not None:
+    reveal_type(FOO)  # N: Revealed type is "builtins.int"
+
+    def func() -> int:
+        return FOO

From 772cd0cebed6884636de0019e43caa06dbaa39ba Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Tue, 13 May 2025 02:13:08 -0700
Subject: [PATCH 1295/1617] Add --strict-bytes to --strict (#19049)

This is a check that ensures static correctness, so it is useful to have
in --strict. Unlike making this the default behavior eventually in 2.0,
which we are also planning to do, it can be added to --strict
immediately due to --strict having looser backwards-compatibility
requirements (or so I interpret --strict's
[documentation](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-strict)).
This PR also includes tests, for --strict and also for no flags.
---
 mypy/main.py                    |  2 +-
 mypy_self_check.ini             |  1 -
 test-data/unit/check-flags.test | 17 +++++++++++++++++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/mypy/main.py b/mypy/main.py
index b2abf06897de..6ebf32ded6e1 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -917,7 +917,7 @@ def add_invertible_flag(
     add_invertible_flag(
         "--strict-bytes",
         default=False,
-        strict_flag=False,
+        strict_flag=True,
         help="Disable treating bytearray and memoryview as subtypes of bytes",
         group=strictness_group,
     )
diff --git a/mypy_self_check.ini b/mypy_self_check.ini
index 816e6321c06f..8bf7a514f481 100644
--- a/mypy_self_check.ini
+++ b/mypy_self_check.ini
@@ -1,7 +1,6 @@
 [mypy]
 
 strict = True
-strict_bytes = True
 local_partial_types = True
 disallow_any_unimported = True
 show_traceback = True
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
index 2a75b465099b..f628fdd68ce8 100644
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -2408,6 +2408,23 @@ f(bytearray(b"asdf"))
 f(memoryview(b"asdf"))
 [builtins fixtures/primitives.pyi]
 
+[case testStrictBytesDisabledByDefault]
+# TODO: probably change this default in Mypy v2.0, with https://github.com/python/mypy/pull/18371
+# (this would also obsolete the testStrictBytesEnabledByStrict test, below)
+def f(x: bytes) -> None: ...
+f(bytearray(b"asdf"))
+f(memoryview(b"asdf"))
+[builtins fixtures/primitives.pyi]
+
+[case testStrictBytesEnabledByStrict]
+# flags: --strict --disable-error-code type-arg
+# The type-arg thing is just work around the primitives.pyi isinstance Tuple not having type parameters,
+#   which isn't important for this.
+def f(x: bytes) -> None: ...
+f(bytearray(b"asdf"))  # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes"
+f(memoryview(b"asdf"))  # E: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes"
+[builtins fixtures/primitives.pyi]
+
 [case testNoCrashFollowImportsForStubs]
 # flags: --config-file tmp/mypy.ini
 {**{"x": "y"}}

From 9e45dadcf6d8dbab36f83d9df94a706c0b4f9207 Mon Sep 17 00:00:00 2001
From: Valentin Stanciu <250871+svalentin@users.noreply.github.com>
Date: Tue, 13 May 2025 15:51:39 +0100
Subject: [PATCH 1296/1617] Clear more data in TypeChecker.reset() instead of
 asserting (#19087)

Running mypy daemon on internal Dropbox codebase can cause an
AssertionError:

```sh
version: 1.16.0+dev.ca609acabdc94ee973a53d62b8dcb7e55c789aec
Daemon crashed!
Traceback (most recent call last):
  File "mypy/dmypy_server.py", line 237, in serve
  File "mypy/dmypy_server.py", line 286, in run_command
  File "mypy/dmypy_server.py", line 364, in cmd_check
  File "mypy/dmypy_server.py", line 428, in check
  File "mypy/dmypy_server.py", line 517, in initialize_fine_grained
  File "mypy/server/update.py", line 265, in update
  File "mypy/server/update.py", line 367, in update_one
  File "mypy/server/update.py", line 432, in update_module
  File "mypy/server/update.py", line 672, in update_module_isolated
  File "mypy/build.py", line 2410, in finish_passes
  File "mypy/build.py", line 2417, in free_state
  File "mypy/checker.py", line 443, in reset
AssertionError
```

Let's convert these asserts in reset() to actual cleanup. I see no
reason not to clean them up.
It also seems safe for this particular crash, since in File
"mypy/build.py", line 2417, in free_state
right after this line there's `self._type_checker = None`. So even if we
wer not to call reset() everything would still be correct.


Alternatively, we can just reset everything by calling `__init__` with
original args:
```py
self.__init__(self.errors, self.modules, self.options, self.tree, self.path, self.plugin, self.expr_checker.per_line_checking_time_ns)
```
---
 mypy/checker.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 758a860abf18..aceb0291926a 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -437,12 +437,10 @@ def reset(self) -> None:
         self._type_maps[0].clear()
         self.temp_type_map = None
         self.expr_checker.reset()
-
-        assert self.inferred_attribute_types is None
-        assert self.partial_types == []
-        assert self.deferred_nodes == []
-        assert len(self.scope.stack) == 1
-        assert self.partial_types == []
+        self.deferred_nodes = []
+        self.partial_types = []
+        self.inferred_attribute_types = None
+        self.scope = CheckerScope(self.tree)
 
     def check_first_pass(self) -> None:
         """Type check the entire file, but defer functions with unresolved references.

From a0307b53ec18887c50c9bcc8345d4f92a34e4cdd Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 15 May 2025 11:03:30 +0200
Subject: [PATCH 1297/1617] Extend special case for context-based typevar
 inference to typevar unions in return position (#18976)

* Fixes #17221.
* Fixes #17654.
* Fixes #17553.
* Fixes #17536.
* Fixes #16659.
* Fixes #16267.
* Fixes #15755.
* Fixes #15150.
* Fixes #14664.
* Incidentally improves error message in #12156.
* Fixes #12092.
* Fixes #11985.
* Improves #11455 (but the problem with union `TypeVar | SomeFixedType`
reported in comments there remains).
* Fixes #10426.

When using context, we can perform some overly optimistic inference when
return type is `T1 | T2`. This breaks in important case of
`builtins.min` when `default` and `key` are passed, essentially making
them always incompatible. This is not the most principled approach, but
let's see the primer results.

This resolves quite a few issues (some of them duplicates, but some -
substantially different), `min` problem was a very popular one... Diff
run: https://github.com/sterliakov/mypy-issues/issues/30
---
 mypy/checkexpr.py                           |  7 ++++++-
 test-data/unit/check-inference-context.test | 15 +++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index ba2d38b6f528..ec64669c1cd0 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2013,7 +2013,12 @@ def infer_function_type_arguments_using_context(
             #     variables in an expression are inferred at the same time.
             #     (And this is hard, also we need to be careful with lambdas that require
             #     two passes.)
-        if isinstance(ret_type, TypeVarType):
+        proper_ret = get_proper_type(ret_type)
+        if (
+            isinstance(proper_ret, TypeVarType)
+            or isinstance(proper_ret, UnionType)
+            and all(isinstance(get_proper_type(u), TypeVarType) for u in proper_ret.items)
+        ):
             # Another special case: the return type is a type variable. If it's unrestricted,
             # we could infer a too general type for the type variable if we use context,
             # and this could result in confusing and spurious type errors elsewhere.
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index 17ae6d9934b7..20f534d60978 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -1495,3 +1495,18 @@ def g(b: Optional[str]) -> None:
         z: Callable[[], str] = lambda: reveal_type(b)  # N: Revealed type is "builtins.str"
         f2(lambda: reveal_type(b))  # N: Revealed type is "builtins.str"
         lambda: reveal_type(b)  # N: Revealed type is "builtins.str"
+
+[case testInferenceContextReturningTypeVarUnion]
+from collections.abc import Callable, Iterable
+from typing import TypeVar, Union
+
+_T1 = TypeVar("_T1")
+_T2 = TypeVar("_T2")
+
+def mymin(
+    iterable: Iterable[_T1], /, *, key: Callable[[_T1], int], default: _T2
+) -> Union[_T1, _T2]: ...
+
+def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]:
+    return mymin(paths, key=key, default=None)
+[builtins fixtures/tuple.pyi]

From 644a20cebc89fcc06dca41099e217d474f2e1aba Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Thu, 15 May 2025 13:07:42 -0700
Subject: [PATCH 1298/1617] Update dmypy/client.py:  Enable ANSI color codes
 for windows cmd (#19088)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

I still use windows cmd, and the color codes emitted by dmypy do not
work on there. instead printing a bunch of codes like ←[37m
←[39;49;00ms. However, for whatever reason you can fix this simply by
calling os.system("") once. (The main mypy program works fine,
presumably because it makes an os system call somewhere before it
prints.)

I did not write a test of this, as that seems difficult and unnecessary.
Instead, I manually tested it, and it worked great.
---
 mypy/dmypy/client.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
index 90c3062bcbe5..b34e9bf8ced2 100644
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -562,6 +562,10 @@ def check_output(
 
     Call sys.exit() unless the status code is zero.
     """
+    if os.name == "nt":
+        # Enable ANSI color codes for Windows cmd using this strange workaround
+        # ( see https://github.com/python/cpython/issues/74261 )
+        os.system("")
     if "error" in response:
         fail(response["error"])
     try:

From 8ed16d1fb233b7484dea1da9c396574e16ba8138 Mon Sep 17 00:00:00 2001
From: Sebastian Rittau 
Date: Fri, 16 May 2025 18:59:00 +0200
Subject: [PATCH 1299/1617] stubgen: Don't generate `Incomplete | None = None`
 argument annotation (#19097)

Fixes #19096
---
 mypy/stubgen.py             | 10 +++-------
 test-data/unit/stubgen.test | 25 ++++++++++---------------
 2 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index 3173bfdf9f5c..e51469b5ab7d 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -565,7 +565,7 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]:
             default = "..."
             if arg_.initializer:
                 if not typename:
-                    typename = self.get_str_type_of_node(arg_.initializer, True, False)
+                    typename = self.get_str_type_of_node(arg_.initializer, can_be_incomplete=False)
                 potential_default, valid = self.get_str_default_of_node(arg_.initializer)
                 if valid and len(potential_default) <= 200:
                     default = potential_default
@@ -1305,9 +1305,7 @@ def is_private_member(self, fullname: str) -> bool:
         parts = fullname.split(".")
         return any(self.is_private_name(part) for part in parts)
 
-    def get_str_type_of_node(
-        self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True
-    ) -> str:
+    def get_str_type_of_node(self, rvalue: Expression, *, can_be_incomplete: bool = True) -> str:
         rvalue = self.maybe_unwrap_unary_expr(rvalue)
 
         if isinstance(rvalue, IntExpr):
@@ -1327,9 +1325,7 @@ def get_str_type_of_node(
                 return "complex"
         if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"):
             return "bool"
-        if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None":
-            return f"{self.add_name('_typeshed.Incomplete')} | None"
-        if can_be_any:
+        if can_be_incomplete:
             return self.add_name("_typeshed.Incomplete")
         else:
             return ""
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
index 86d33e3af51d..5ff458736436 100644
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -30,12 +30,10 @@ def g(b=-1, c=0): ...
 def f(a, b: int = 2) -> None: ...
 def g(b: int = -1, c: int = 0) -> None: ...
 
-[case testDefaultArgNone]
+[case testFuncDefaultArgNone]
 def f(x=None): ...
 [out]
-from _typeshed import Incomplete
-
-def f(x: Incomplete | None = None) -> None: ...
+def f(x=None) -> None: ...
 
 [case testDefaultArgBool]
 def f(x=True, y=False): ...
@@ -1379,7 +1377,7 @@ async def f(a):
 [out]
 async def f(a) -> None: ...
 
-[case testInferOptionalOnlyFunc]
+[case testMethodDefaultArgNone]
 class A:
     x = None
     def __init__(self, a=None):
@@ -1391,8 +1389,8 @@ from _typeshed import Incomplete
 
 class A:
     x: Incomplete
-    def __init__(self, a: Incomplete | None = None) -> None: ...
-    def method(self, a: Incomplete | None = None) -> None: ...
+    def __init__(self, a=None) -> None: ...
+    def method(self, a=None) -> None: ...
 
 [case testAnnotationImportsFrom]
 import foo
@@ -2618,32 +2616,29 @@ class A(metaclass=abc.ABCMeta):
     @abc.abstractmethod
     def x(self): ...
 
-[case testClassWithNameIncompleteOrOptional]
+[case testClassWithNameIncomplete]
 Y = object()
 
-def g(x=None): pass
+def g():
+    yield 1
 
 x = g()
 
 class Incomplete:
     pass
 
-def Optional():
-    return 0
-
 [out]
 from _typeshed import Incomplete as _Incomplete
+from collections.abc import Generator
 
 Y: _Incomplete
 
-def g(x: _Incomplete | None = None) -> None: ...
+def g() -> Generator[_Incomplete]: ...
 
 x: _Incomplete
 
 class Incomplete: ...
 
-def Optional(): ...
-
 [case testExportedNameImported]
 # modules: main a b
 from a import C

From 93ff49da8453384f445ab22d32366baf96ed0f6f Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 19 May 2025 16:13:59 +0200
Subject: [PATCH 1300/1617] Support several more constructs in stubgen's
 AliasPrinter (#18888)

Fixes #18886. Added implementations for UnaryExpr, SetExpr and
SliceExpr, and also fallback to _Incomplete for several other constructs
that can sensibly appear in a dataclass field definition.
---
 mypy/stubgen.py             | 47 +++++++++++++++++++++++++++++++++++++
 test-data/unit/stubgen.test | 29 +++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index e51469b5ab7d..f074a34d5c64 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -78,17 +78,21 @@
     Block,
     BytesExpr,
     CallExpr,
+    CastExpr,
     ClassDef,
     ComparisonExpr,
     ComplexExpr,
+    ConditionalExpr,
     Decorator,
     DictExpr,
+    DictionaryComprehension,
     EllipsisExpr,
     Expression,
     ExpressionStmt,
     FloatExpr,
     FuncBase,
     FuncDef,
+    GeneratorExpr,
     IfStmt,
     Import,
     ImportAll,
@@ -96,13 +100,16 @@
     IndexExpr,
     IntExpr,
     LambdaExpr,
+    ListComprehension,
     ListExpr,
     MemberExpr,
     MypyFile,
     NameExpr,
     OpExpr,
     OverloadedFuncDef,
+    SetComprehension,
     SetExpr,
+    SliceExpr,
     StarExpr,
     Statement,
     StrExpr,
@@ -355,6 +362,9 @@ def visit_tuple_expr(self, node: TupleExpr) -> str:
     def visit_list_expr(self, node: ListExpr) -> str:
         return f"[{', '.join(n.accept(self) for n in node.items)}]"
 
+    def visit_set_expr(self, node: SetExpr) -> str:
+        return f"{{{', '.join(n.accept(self) for n in node.items)}}}"
+
     def visit_dict_expr(self, o: DictExpr) -> str:
         dict_items = []
         for key, value in o.items:
@@ -369,6 +379,18 @@ def visit_ellipsis(self, node: EllipsisExpr) -> str:
     def visit_op_expr(self, o: OpExpr) -> str:
         return f"{o.left.accept(self)} {o.op} {o.right.accept(self)}"
 
+    def visit_unary_expr(self, o: UnaryExpr, /) -> str:
+        return f"{o.op}{o.expr.accept(self)}"
+
+    def visit_slice_expr(self, o: SliceExpr, /) -> str:
+        blocks = [
+            o.begin_index.accept(self) if o.begin_index is not None else "",
+            o.end_index.accept(self) if o.end_index is not None else "",
+        ]
+        if o.stride is not None:
+            blocks.append(o.stride.accept(self))
+        return ":".join(blocks)
+
     def visit_star_expr(self, o: StarExpr) -> str:
         return f"*{o.expr.accept(self)}"
 
@@ -376,6 +398,31 @@ def visit_lambda_expr(self, o: LambdaExpr) -> str:
         # TODO: Required for among other things dataclass.field default_factory
         return self.stubgen.add_name("_typeshed.Incomplete")
 
+    def _visit_unsupported_expr(self, o: object) -> str:
+        # Something we do not understand.
+        return self.stubgen.add_name("_typeshed.Incomplete")
+
+    def visit_comparison_expr(self, o: ComparisonExpr) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_cast_expr(self, o: CastExpr) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_conditional_expr(self, o: ConditionalExpr) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_list_comprehension(self, o: ListComprehension) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_set_comprehension(self, o: SetComprehension) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> str:
+        return self._visit_unsupported_expr(o)
+
+    def visit_generator_expr(self, o: GeneratorExpr) -> str:
+        return self._visit_unsupported_expr(o)
+
 
 def find_defined_names(file: MypyFile) -> set[str]:
     finder = DefinitionFinder()
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
index 5ff458736436..717137cbd251 100644
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -4277,6 +4277,35 @@ class Y(missing.Base):
     generated_kwargs: float
     generated_kwargs_: float
 
+[case testDataclassAliasPrinterVariations_semanal]
+from dataclasses import dataclass, field
+
+@dataclass
+class X:
+    a: int = field(default=-1)
+    b: set[int] = field(default={0})
+    c: list[int] = field(default=[x for x in range(5)])
+    d: dict[int, int] = field(default={x: x for x in range(5)})
+    e: tuple[int, int] = field(default=(1, 2, 3)[1:])
+    f: tuple[int, int] = field(default=(1, 2, 3)[:2])
+    g: tuple[int, int] = field(default=(1, 2, 3)[::2])
+    h: tuple[int] = field(default=(1, 2, 3)[1::2])
+
+[out]
+from _typeshed import Incomplete
+from dataclasses import dataclass, field
+
+@dataclass
+class X:
+    a: int = field(default=-1)
+    b: set[int] = field(default={0})
+    c: list[int] = field(default=Incomplete)
+    d: dict[int, int] = field(default=Incomplete)
+    e: tuple[int, int] = field(default=(1, 2, 3)[1:])
+    f: tuple[int, int] = field(default=(1, 2, 3)[:2])
+    g: tuple[int, int] = field(default=(1, 2, 3)[::2])
+    h: tuple[int] = field(default=(1, 2, 3)[1::2])
+
 [case testDataclassTransform]
 # dataclass_transform detection only works with semantic analysis.
 # Test stubgen doesn't break too badly without it.

From 301c3b604a2823aeb3b976272e38adb112848acf Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Mon, 19 May 2025 07:25:57 -0700
Subject: [PATCH 1301/1617] Emit a friendlier warning on invalid exclude regex,
 instead of a stacktrace (#19102)

If an invalid exclude is used, the error message

```
Traceback (most recent call last):
  File "", line 198, in _run_module_as_main
  File "", line 88, in _run_code
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\__main__.py", line 37, in 
    console_entry()
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\__main__.py", line 15, in console_entry
    main()
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\main.py", line 89, in main
    sources, options = process_options(args, stdout=stdout, stderr=stderr, fscache=fscache)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\main.py", line 1531, in process_options
    targets = create_source_list(special_opts.files, options, fscache)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\find_sources.py", line 48, in create_source_list
    sub_sources = finder.find_sources_in_dir(path)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\find_sources.py", line 121, in find_sources_in_dir
    if matches_exclude(subpath, self.exclude, self.fscache, self.verbosity >= 2):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\files\gits\wyattscarpenter\!!! contributory forks\mypy\mypy\modulefinder.py", line 687, in matches_exclude
    if re.search(exclude, subpath_str):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\__init__.py", line 177, in search
    return _compile(pattern, flags).search(string)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\__init__.py", line 307, in _compile
    p = _compiler.compile(pattern, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\_compiler.py", line 750, in compile
    p = _parser.parse(p, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\_parser.py", line 979, in parse
    p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\_parser.py", line 460, in _parse_sub
    itemsappend(_parse(source, state, verbose, nested + 1,
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\_parser.py", line 544, in _parse
    code = _escape(source, this, state)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\wyatt\AppData\Local\Programs\Python\Python312\Lib\re\_parser.py", line 443, in _escape
    raise source.error("bad escape %s" % escape, len(escape))
re.error: bad escape \p at position 2
```

now looks like this:

```
error: The exclude ..\publish is an invalid regular expression, because: bad escape \p at position 2
(Hint: use / as a path separator, even if you're on Windows!)
For more information on Python's flavor of regex, see: https://docs.python.org/3/library/re.html
```

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypy/modulefinder.py | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
index 3040276dea6d..4cbeed9d14ff 100644
--- a/mypy/modulefinder.py
+++ b/mypy/modulefinder.py
@@ -684,12 +684,27 @@ def matches_exclude(
     if fscache.isdir(subpath):
         subpath_str += "/"
     for exclude in excludes:
-        if re.search(exclude, subpath_str):
-            if verbose:
-                print(
-                    f"TRACE: Excluding {subpath_str} (matches pattern {exclude})", file=sys.stderr
+        try:
+            if re.search(exclude, subpath_str):
+                if verbose:
+                    print(
+                        f"TRACE: Excluding {subpath_str} (matches pattern {exclude})",
+                        file=sys.stderr,
+                    )
+                return True
+        except re.error as e:
+            print(
+                f"error: The exclude {exclude} is an invalid regular expression, because: {e}"
+                + (
+                    "\n(Hint: use / as a path separator, even if you're on Windows!)"
+                    if "\\" in exclude
+                    else ""
                 )
-            return True
+                + "\nFor more information on Python's flavor of regex, see:"
+                + " https://docs.python.org/3/library/re.html",
+                file=sys.stderr,
+            )
+            sys.exit(2)
     return False
 
 
@@ -786,7 +801,8 @@ def default_lib_path(
             print(
                 "error: --custom-typeshed-dir does not point to a valid typeshed ({})".format(
                     custom_typeshed_dir
-                )
+                ),
+                file=sys.stderr,
             )
             sys.exit(2)
     else:

From a9bb7378d8229d20d03dcd29f7d07c7238b67188 Mon Sep 17 00:00:00 2001
From: Chad Dombrova 
Date: Mon, 19 May 2025 18:39:01 -0700
Subject: [PATCH 1302/1617] stubgenc: add support for including class and
 property docstrings (#17964)



Prior to this change passing `--include-docstrings` did not generate
docstrings for classes or properties, only functions. This PR brings
c-extensions up to parity with pure-python modules.

I used this feature to generate stubs for this project:
https://github.com/LumaPictures/cg-stubs/blob/master/usd/stubs/pxr/Usd/__init__.pyi


---
 mypy/stubdoc.py                               |  9 +++--
 mypy/stubgenc.py                              | 36 ++++++++++++-------
 mypy/stubutil.py                              |  3 +-
 .../pybind11_fixtures/__init__.pyi            |  5 ++-
 .../pybind11_fixtures/demo.pyi                | 33 ++++++++++++++---
 5 files changed, 64 insertions(+), 22 deletions(-)

diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py
index 617c5ecda408..89db6cb3378f 100644
--- a/mypy/stubdoc.py
+++ b/mypy/stubdoc.py
@@ -78,6 +78,7 @@ class FunctionSig(NamedTuple):
     args: list[ArgSig]
     ret_type: str | None
     type_args: str = ""  # TODO implement in stubgenc and remove the default
+    docstring: str | None = None
 
     def is_special_method(self) -> bool:
         return bool(
@@ -110,6 +111,7 @@ def format_sig(
         is_async: bool = False,
         any_val: str | None = None,
         docstring: str | None = None,
+        include_docstrings: bool = False,
     ) -> str:
         args: list[str] = []
         for arg in self.args:
@@ -144,8 +146,11 @@ def format_sig(
 
         prefix = "async " if is_async else ""
         sig = f"{indent}{prefix}def {self.name}{self.type_args}({', '.join(args)}){retfield}:"
-        if docstring:
-            suffix = f"\n{indent}    {mypy.util.quote_docstring(docstring)}"
+        # if this object has a docstring it's probably produced by a SignatureGenerator, so it
+        # takes precedence over the passed docstring, which acts as a fallback.
+        doc = (self.docstring or docstring) if include_docstrings else None
+        if doc:
+            suffix = f"\n{indent}    {mypy.util.quote_docstring(doc)}"
         else:
             suffix = " ..."
         return f"{sig}{suffix}"
diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py
index b675079dd8dd..e64dbcdd9d40 100755
--- a/mypy/stubgenc.py
+++ b/mypy/stubgenc.py
@@ -38,6 +38,7 @@
     infer_method_arg_types,
     infer_method_ret_type,
 )
+from mypy.util import quote_docstring
 
 
 class ExternalSignatureGenerator(SignatureGenerator):
@@ -649,8 +650,7 @@ def generate_function_stub(
                 if inferred[0].args and inferred[0].args[0].name == "cls":
                     decorators.append("@classmethod")
 
-        if docstring:
-            docstring = self._indent_docstring(docstring)
+        docstring = self._indent_docstring(ctx.docstring) if ctx.docstring else None
         output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring))
         self._fix_iter(ctx, inferred, output)
 
@@ -754,9 +754,14 @@ def generate_property_stub(
             )
         else:  # regular property
             if readonly:
+                docstring = self._indent_docstring(ctx.docstring) if ctx.docstring else None
                 ro_properties.append(f"{self._indent}@property")
-                sig = FunctionSig(name, [ArgSig("self")], inferred_type)
-                ro_properties.append(sig.format_sig(indent=self._indent))
+                sig = FunctionSig(name, [ArgSig("self")], inferred_type, docstring=docstring)
+                ro_properties.append(
+                    sig.format_sig(
+                        indent=self._indent, include_docstrings=self._include_docstrings
+                    )
+                )
             else:
                 if inferred_type is None:
                     inferred_type = self.add_name("_typeshed.Incomplete")
@@ -875,8 +880,17 @@ def generate_class_stub(
             bases_str = "(%s)" % ", ".join(bases)
         else:
             bases_str = ""
-        if types or static_properties or rw_properties or methods or ro_properties:
+
+        if class_info.docstring and self._include_docstrings:
+            doc = quote_docstring(self._indent_docstring(class_info.docstring))
+            doc = f"    {self._indent}{doc}"
+            docstring = doc.splitlines(keepends=False)
+        else:
+            docstring = []
+
+        if docstring or types or static_properties or rw_properties or methods or ro_properties:
             output.append(f"{self._indent}class {class_name}{bases_str}:")
+            output.extend(docstring)
             for line in types:
                 if (
                     output
@@ -886,14 +900,10 @@ def generate_class_stub(
                 ):
                     output.append("")
                 output.append(line)
-            for line in static_properties:
-                output.append(line)
-            for line in rw_properties:
-                output.append(line)
-            for line in methods:
-                output.append(line)
-            for line in ro_properties:
-                output.append(line)
+            output.extend(static_properties)
+            output.extend(rw_properties)
+            output.extend(methods)
+            output.extend(ro_properties)
         else:
             output.append(f"{self._indent}class {class_name}{bases_str}: ...")
 
diff --git a/mypy/stubutil.py b/mypy/stubutil.py
index fecd9b29d57d..a3c0f9b7b277 100644
--- a/mypy/stubutil.py
+++ b/mypy/stubutil.py
@@ -803,7 +803,8 @@ def format_func_def(
                 signature.format_sig(
                     indent=self._indent,
                     is_async=is_coroutine,
-                    docstring=docstring if self._include_docstrings else None,
+                    docstring=docstring,
+                    include_docstrings=self._include_docstrings,
                 )
             )
         return lines
diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi
index db04bccab028..0eeb788d4278 100644
--- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi
+++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi
@@ -38,7 +38,10 @@ class TestStruct:
     def __init__(self, *args, **kwargs) -> None:
         """Initialize self.  See help(type(self)) for accurate signature."""
     @property
-    def field_readonly(self) -> int: ...
+    def field_readonly(self) -> int:
+        """some docstring
+        (arg0: pybind11_fixtures.TestStruct) -> int
+        """
 
 def func_incomplete_signature(*args, **kwargs):
     """func_incomplete_signature() -> dummy_sub_namespace::HasNoBinding"""
diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi
index 1be0bc905a43..6e285f202f1a 100644
--- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi
+++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi
@@ -5,6 +5,11 @@ __version__: str
 
 class Point:
     class AngleUnit:
+        """Members:
+
+          radian
+
+          degree"""
         __members__: ClassVar[dict] = ...  # read-only
         __entries: ClassVar[dict] = ...
         degree: ClassVar[Point.AngleUnit] = ...
@@ -22,11 +27,23 @@ class Point:
         def __ne__(self, other: object) -> bool:
             """__ne__(self: object, other: object) -> bool"""
         @property
-        def name(self) -> str: ...
+        def name(self) -> str:
+            """name(self: handle) -> str
+
+            name(self: handle) -> str
+            """
         @property
-        def value(self) -> int: ...
+        def value(self) -> int:
+            """(arg0: pybind11_fixtures.demo.Point.AngleUnit) -> int"""
 
     class LengthUnit:
+        """Members:
+
+          mm
+
+          pixel
+
+          inch"""
         __members__: ClassVar[dict] = ...  # read-only
         __entries: ClassVar[dict] = ...
         inch: ClassVar[Point.LengthUnit] = ...
@@ -45,9 +62,14 @@ class Point:
         def __ne__(self, other: object) -> bool:
             """__ne__(self: object, other: object) -> bool"""
         @property
-        def name(self) -> str: ...
+        def name(self) -> str:
+            """name(self: handle) -> str
+
+            name(self: handle) -> str
+            """
         @property
-        def value(self) -> int: ...
+        def value(self) -> int:
+            """(arg0: pybind11_fixtures.demo.Point.LengthUnit) -> int"""
     angle_unit: ClassVar[Point.AngleUnit] = ...
     length_unit: ClassVar[Point.LengthUnit] = ...
     x_axis: ClassVar[Point] = ...  # read-only
@@ -94,7 +116,8 @@ class Point:
         2. distance_to(self: pybind11_fixtures.demo.Point, other: pybind11_fixtures.demo.Point) -> float
         """
     @property
-    def length(self) -> float: ...
+    def length(self) -> float:
+        """(arg0: pybind11_fixtures.demo.Point) -> float"""
 
 def answer() -> int:
     '''answer() -> int

From 9f53138a082e8449dcb3773947b107c6639982ab Mon Sep 17 00:00:00 2001
From: Charulata <11500589+charulatalodha@users.noreply.github.com>
Date: Tue, 20 May 2025 11:02:49 -0400
Subject: [PATCH 1303/1617] Syntax error messages capitalization (#19114)

Fixes #https://github.com/python/mypy/issues/19107

Cause of Issue:
```ast3``` parser raises exception with error message which sometimes non-standardized.

eg :
```
error: expected an indented block after function definition on line 1
[syntax]
```

Resolution:
- standardize the exception error message before report generation for syntax exceptions specifically

Contributors:
- Me and [@aman
](https://github.com/amansomething)

---------

Co-authored-by: Charulata Lodha 
Co-authored-by: amansomething 
---
 mypy/fastparse.py                         |  4 +-
 mypy/test/teststubtest.py                 |  2 +-
 test-data/unit/check-basic.test           |  2 +-
 test-data/unit/check-columns.test         |  2 +-
 test-data/unit/check-errorcodes.test      |  4 +-
 test-data/unit/check-expressions.test     |  2 +-
 test-data/unit/check-fastparse.test       |  4 +-
 test-data/unit/check-ignore.test          |  4 +-
 test-data/unit/check-newsyntax.test       |  4 +-
 test-data/unit/check-statements.test      |  2 +-
 test-data/unit/cmdline.test               |  8 +-
 test-data/unit/fine-grained-blockers.test | 76 +++++++++---------
 test-data/unit/fine-grained-suggest.test  |  4 +-
 test-data/unit/parse-errors.test          | 96 +++++++++++------------
 test-data/unit/parse.test                 | 12 +--
 test-data/unit/semanal-errors.test        | 60 +++++++-------
 test-data/unit/semanal-statements.test    |  4 +-
 17 files changed, 146 insertions(+), 144 deletions(-)

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index aed04c6f2eb9..6c59f44829bb 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -270,7 +270,9 @@ def parse(
         errors.report(
             e.lineno if e.lineno is not None else -1,
             e.offset,
-            message,
+            re.sub(
+                r"^(\s*\w)", lambda m: m.group(1).upper(), message
+            ),  # Standardizing error message
             blocker=True,
             code=codes.SYNTAX,
         )
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index 492897d33a4a..7925f2a6bd3e 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -2581,7 +2581,7 @@ def test_mypy_build(self) -> None:
         output = run_stubtest(stub="+", runtime="", options=[])
         assert output == (
             "error: not checking stubs due to failed mypy compile:\n{}.pyi:1: "
-            "error: invalid syntax  [syntax]\n".format(TEST_MODULE_NAME)
+            "error: Invalid syntax  [syntax]\n".format(TEST_MODULE_NAME)
         )
 
         output = run_stubtest(stub="def f(): ...\ndef f(): ...", runtime="", options=[])
diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test
index 6ecbbdcc13eb..3f2164bf5a24 100644
--- a/test-data/unit/check-basic.test
+++ b/test-data/unit/check-basic.test
@@ -289,7 +289,7 @@ x in 1,  # E: Unsupported right operand type for in ("int")
 [case testTrailingCommaInIfParsing]
 if x in 1, : pass
 [out]
-main:1: error: invalid syntax
+main:1: error: Invalid syntax
 
 [case testInitReturnTypeError]
 class C:
diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test
index 8f91d99a0576..c18313bbc24f 100644
--- a/test-data/unit/check-columns.test
+++ b/test-data/unit/check-columns.test
@@ -4,7 +4,7 @@
 f()
 1 +
 [out]
-main:2:5: error: invalid syntax
+main:2:5: error: Invalid syntax
 
 [case testColumnsNestedFunctions]
 import typing
diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test
index 21112b7d85a2..0cd6dc081629 100644
--- a/test-data/unit/check-errorcodes.test
+++ b/test-data/unit/check-errorcodes.test
@@ -33,9 +33,9 @@ reveal_type(1) # N: Revealed type is "Literal[1]?"
 [case testErrorCodeSyntaxError]
 1 ''
 [out]
-main:1: error: invalid syntax  [syntax]
+main:1: error: Invalid syntax  [syntax]
 [out version==3.10.0]
-main:1: error: invalid syntax. Perhaps you forgot a comma?  [syntax]
+main:1: error: Invalid syntax. Perhaps you forgot a comma?  [syntax]
 
 [case testErrorCodeSyntaxError2]
 def f(): # E: Type signature has too many arguments  [syntax]
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index 81eb4c7c0dc8..a3b15a3b1da4 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1861,7 +1861,7 @@ None < None  # E: Unsupported left operand type for < ("None")
 
 [case testDictWithStarExpr]
 
-b = {'z': 26, *a}  # E: invalid syntax
+b = {'z': 26, *a}  # E: Invalid syntax
 [builtins fixtures/dict.pyi]
 
 [case testDictWithStarStarExpr]
diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test
index f93e4fe07218..80d314333ddc 100644
--- a/test-data/unit/check-fastparse.test
+++ b/test-data/unit/check-fastparse.test
@@ -1,6 +1,6 @@
 [case testFastParseSyntaxError]
 
-1 +  # E: invalid syntax
+1 +  # E: Invalid syntax
 
 [case testFastParseTypeCommentSyntaxError]
 
@@ -158,7 +158,7 @@ def f(a,        # type: A
 
 [case testFastParsePerArgumentAnnotationsWithAnnotatedBareStar]
 
-def f(*, # type: int  # E: bare * has associated type comment
+def f(*, # type: int  # E: Bare * has associated type comment
       x  # type: str
       ):
       # type: (...) -> int
diff --git a/test-data/unit/check-ignore.test b/test-data/unit/check-ignore.test
index fa451f373e70..a4234e7a37a1 100644
--- a/test-data/unit/check-ignore.test
+++ b/test-data/unit/check-ignore.test
@@ -38,7 +38,7 @@ from m import a # type: ignore
 [file m.py]
 +
 [out]
-tmp/m.py:1: error: invalid syntax
+tmp/m.py:1: error: Invalid syntax
 
 [case testIgnoreAppliesOnlyToMissing]
 import a # type: ignore
@@ -59,7 +59,7 @@ from m import * # type: ignore
 [file m.py]
 +
 [out]
-tmp/m.py:1: error: invalid syntax
+tmp/m.py:1: error: Invalid syntax
 
 [case testIgnoreAssignmentTypeError]
 x = 1
diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test
index 3ed4c6d3d8e2..a696eb2932fe 100644
--- a/test-data/unit/check-newsyntax.test
+++ b/test-data/unit/check-newsyntax.test
@@ -1,5 +1,5 @@
 [case testNewSyntaxSyntaxError]
-x: int: int  # E: invalid syntax
+x: int: int  # E: Invalid syntax
 [out]
 
 [case testNewSyntaxBasics]
@@ -126,4 +126,4 @@ reveal_type(f'{1}') # N: Revealed type is "builtins.str"
 # flags: --python-version 3.99
 x *** x this is what future python looks like public static void main String[] args await goto exit
 [out]
-main:2: error: invalid syntax; you likely need to run mypy using Python 3.99 or newer
+main:2: error: Invalid syntax; you likely need to run mypy using Python 3.99 or newer
diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test
index 9f77100863be..b0b673f696e1 100644
--- a/test-data/unit/check-statements.test
+++ b/test-data/unit/check-statements.test
@@ -1311,7 +1311,7 @@ def f() -> Iterator[List[int]]:
 
 [case testYieldFromNotAppliedToNothing]
 def h():
-    yield from  # E: invalid syntax
+    yield from  # E: Invalid syntax
 [out]
 
 [case testYieldFromAndYieldTogether]
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index 748a655d5a10..fb2e0c01fe0e 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -896,7 +896,7 @@ some_file.py:11: error: Argument 1 to "some_interesting_method" of
 [file some_file.py]
 it_looks_like_we_started_typing_something_but_then. = did_not_notice(an_extra_dot)
 [out]
-some_file.py:1: error: invalid syntax  [syntax]
+some_file.py:1: error: Invalid syntax  [syntax]
     ...ooks_like_we_started_typing_something_but_then. = did_not_notice(an_ex...
                                                         ^
 == Return code: 2
@@ -1035,15 +1035,15 @@ public static void main(String[] args)
 [file pkg/y.py]
 x: str = 0
 [out]
-pkg/x.py:1: error: invalid syntax
+pkg/x.py:1: error: Invalid syntax
 Found 1 error in 1 file (errors prevented further checking)
 == Return code: 2
 [out version>=3.10]
-pkg/x.py:1: error: invalid syntax. Perhaps you forgot a comma?
+pkg/x.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 Found 1 error in 1 file (errors prevented further checking)
 == Return code: 2
 [out version>=3.10.3]
-pkg/x.py:1: error: invalid syntax
+pkg/x.py:1: error: Invalid syntax
 Found 1 error in 1 file (errors prevented further checking)
 == Return code: 2
 
diff --git a/test-data/unit/fine-grained-blockers.test b/test-data/unit/fine-grained-blockers.test
index 33dedd887114..8e16da053d6a 100644
--- a/test-data/unit/fine-grained-blockers.test
+++ b/test-data/unit/fine-grained-blockers.test
@@ -19,13 +19,13 @@ def f(x: int) -> None: pass
 def f() -> None: pass
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 main:2: error: Missing positional argument "x" in call to "f"
 ==
 [out version>=3.10]
 ==
-a.py:1: error: expected ':'
+a.py:1: error: Expected ':'
 ==
 main:2: error: Missing positional argument "x" in call to "f"
 ==
@@ -44,7 +44,7 @@ def f(x: int) -> None: pass
 def f() -> None: pass
 [out]
 ==
-a.py:1: error: invalid syntax  [syntax]
+a.py:1: error: Invalid syntax  [syntax]
     def f(x: int) ->
                     ^
 ==
@@ -54,7 +54,7 @@ main:3: error: Missing positional argument "x" in call to "f"  [call-arg]
 ==
 [out version>=3.10]
 ==
-a.py:1: error: expected ':'  [syntax]
+a.py:1: error: Expected ':'  [syntax]
     def f(x: int) ->
                    ^
 ==
@@ -77,16 +77,16 @@ def f(x: int
 def f(x: int) -> None: pass
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
-a.py:2: error: invalid syntax
+a.py:2: error: Invalid syntax
 ==
 main:2: error: Missing positional argument "x" in call to "f"
 [out version>=3.10]
 ==
-a.py:1: error: expected ':'
+a.py:1: error: Expected ':'
 ==
-a.py:2: error: expected ':'
+a.py:2: error: Expected ':'
 ==
 main:2: error: Missing positional argument "x" in call to "f"
 
@@ -124,7 +124,7 @@ def f() -> None: pass
 main:3: error: Too many arguments for "f"
 main:5: error: Too many arguments for "f"
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 main:3: error: Too many arguments for "f"
 main:5: error: Too many arguments for "f"
@@ -132,7 +132,7 @@ main:5: error: Too many arguments for "f"
 main:3: error: Too many arguments for "f"
 main:5: error: Too many arguments for "f"
 ==
-a.py:1: error: expected ':'
+a.py:1: error: Expected ':'
 ==
 main:3: error: Too many arguments for "f"
 main:5: error: Too many arguments for "f"
@@ -153,12 +153,12 @@ class C:
     def f(self, x: int) -> None: pass
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 main:5: error: Missing positional argument "x" in call to "f" of "C"
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 main:5: error: Missing positional argument "x" in call to "f" of "C"
 
@@ -173,14 +173,14 @@ def f() -> None: pass
 main:1: error: Cannot find implementation or library stub for module named "a"
 main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 main:2: error: Too many arguments for "f"
 [out version==3.10.0]
 main:1: error: Cannot find implementation or library stub for module named "a"
 main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 main:2: error: Too many arguments for "f"
 
@@ -208,7 +208,7 @@ a.f()
 def g() -> None: pass
 [out]
 ==
-b.py:1: error: invalid syntax
+b.py:1: error: Invalid syntax
 ==
 
 [case testModifyTwoFilesOneWithBlockingError2]
@@ -235,7 +235,7 @@ def f() -> None: pass
 b.g()
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 
 [case testBlockingErrorRemainsUnfixed]
@@ -254,16 +254,16 @@ import b
 b.f()
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 a.py:2: error: Missing positional argument "x" in call to "f"
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 a.py:2: error: Missing positional argument "x" in call to "f"
 
@@ -303,9 +303,9 @@ def g() -> None: pass
 a.f(1)
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 a.py:3: error: Too many arguments for "g"
 b.py:3: error: Too many arguments for "f"
@@ -325,14 +325,14 @@ x x
 [delete a.py.3]
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 main:1: error: Cannot find implementation or library stub for module named "a"
 main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
 b.py:1: error: Cannot find implementation or library stub for module named "a"
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 main:1: error: Cannot find implementation or library stub for module named "a"
 main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
@@ -353,14 +353,14 @@ x x
 [delete a.py.3]
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 b.py:1: error: Cannot find implementation or library stub for module named "a"
 b.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
 main:1: error: Cannot find implementation or library stub for module named "a"
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 b.py:1: error: Cannot find implementation or library stub for module named "a"
 b.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
@@ -382,17 +382,17 @@ a.f()
 [builtins fixtures/module.pyi]
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 b.py:2: error: Module has no attribute "f"
 b.py:3: error: "int" not callable
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 b.py:2: error: Module has no attribute "f"
 b.py:3: error: "int" not callable
@@ -408,12 +408,12 @@ import blocker
 def f() -> None: pass
 [out]
 ==
-/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax
+/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax
 ==
 a.py:1: error: "int" not callable
 [out version==3.10.0]
 ==
-/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax. Perhaps you forgot a comma?
+/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 a.py:1: error: "int" not callable
 
@@ -485,16 +485,16 @@ import sys
 [builtins fixtures/tuple.pyi]
 [out]
 ==
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
-/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax
+/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax
 ==
 a.py:2: error: "int" not callable
 [out version==3.10.0]
 ==
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
-/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax. Perhaps you forgot a comma?
+/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 a.py:2: error: "int" not callable
 
@@ -511,12 +511,12 @@ x = 1
 def f() -> int:
     return 0
 [out]
-a.py:1: error: invalid syntax
+a.py:1: error: Invalid syntax
 ==
 b.py:2: error: Incompatible return value type (got "str", expected "int")
 ==
 [out version==3.10.0]
-a.py:1: error: invalid syntax. Perhaps you forgot a comma?
+a.py:1: error: Invalid syntax. Perhaps you forgot a comma?
 ==
 b.py:2: error: Incompatible return value type (got "str", expected "int")
 ==
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index 2539886229cf..ba6006300a4c 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -1035,10 +1035,10 @@ def foo():
 
 (
 [out]
-foo.py:4: error: unexpected EOF while parsing
+foo.py:4: error: Unexpected EOF while parsing
 Command 'suggest' is only valid after a 'check' command (that produces no parse errors)
 ==
-foo.py:4: error: unexpected EOF while parsing
+foo.py:4: error: Unexpected EOF while parsing
 [out version>=3.10]
 foo.py:4: error: '(' was never closed
 Command 'suggest' is only valid after a 'check' command (that produces no parse errors)
diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test
index 33c2a6ddf5c0..a192cc02d0cc 100644
--- a/test-data/unit/parse-errors.test
+++ b/test-data/unit/parse-errors.test
@@ -12,102 +12,102 @@
 def f()
   pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testUnexpectedIndent]
 1
  2
 [out]
-file:2: error: unexpected indent
+file:2: error: Unexpected indent
 
 [case testInconsistentIndent]
 if x:
   1
    1
 [out]
-file:3: error: unexpected indent
+file:3: error: Unexpected indent
 
 [case testInconsistentIndent2]
 if x:
    1
   1
 [out]
-file:3: error: unindent does not match any outer indentation level
+file:3: error: Unindent does not match any outer indentation level
 
 [case testInvalidBinaryOp]
 1>
 a*
 a+1*
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testDoubleStar]
 **a
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testMissingSuperClass]
 class A(:
   pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testUnexpectedEof]
 if 1:
 [out]
-file:1: error: expected an indented block
+file:1: error: Expected an indented block
 
 [case testInvalidKeywordArguments1]
 f(x=y, z)
 [out]
-file:1: error: positional argument follows keyword argument
+file:1: error: Positional argument follows keyword argument
 
 [case testInvalidKeywordArguments2]
 f(**x, y)
 [out]
-file:1: error: positional argument follows keyword argument unpacking
+file:1: error: Positional argument follows keyword argument unpacking
 
 [case testInvalidBareAsteriskAndVarArgs2]
 def f(*x: A, *) -> None: pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidBareAsteriskAndVarArgs3]
 def f(*, *x: A) -> None: pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidBareAsteriskAndVarArgs4]
 def f(*, **x: A) -> None: pass
 [out]
-file:1: error: named arguments must follow bare *
+file:1: error: Named arguments must follow bare *
 
 [case testInvalidBareAsterisk1]
 def f(*) -> None: pass
 [out]
-file:1: error: named arguments must follow bare *
+file:1: error: Named arguments must follow bare *
 
 [case testInvalidBareAsterisk2]
 def f(x, *) -> None: pass
 [out]
-file:1: error: named arguments must follow bare *
+file:1: error: Named arguments must follow bare *
 
 [case testInvalidFuncDefArgs1]
 def f(x = y, x): pass
 [out]
-file:1: error: non-default argument follows default argument
+file:1: error: Non-default argument follows default argument
 
 [case testInvalidFuncDefArgs3]
 def f(**x, y):
    pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidFuncDefArgs4]
 def f(**x, y=x):
     pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidTypeComment]
 0
@@ -154,7 +154,7 @@ file:2: error: Syntax error in type comment "A B"
 [case testMissingBracket]
 def foo(
 [out]
-file:1: error: unexpected EOF while parsing
+file:1: error: Unexpected EOF while parsing
 [out version>=3.10]
 file:1: error: '(' was never closed
 
@@ -288,153 +288,153 @@ file:1: error: Missing parentheses in call to 'print'. Did you mean print(1)?
 [case testInvalidConditionInConditionalExpression]
 1 if 2, 3 else 4
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidConditionInConditionalExpression2]
 1 if x for y in z else 4
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidConditionInConditionalExpression3]
 1 if x else for y in z
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testYieldFromNotRightParameter]
 def f():
     yield from
 [out]
-file:2: error: invalid syntax
+file:2: error: Invalid syntax
 
 [case testYieldFromAfterReturn]
 def f():
     return yield from h()
 [out]
-file:2: error: invalid syntax
+file:2: error: Invalid syntax
 
 [case testImportDotModule]
 import .x
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testImportDot]
 import .
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidFunctionName]
 def while(): pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testInvalidEllipsis1]
 ...0
 ..._
 ...a
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testBlockStatementInSingleLineIf]
 if 1: if 2: pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testBlockStatementInSingleLineIf2]
 if 1: while 2: pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testBlockStatementInSingleLineIf3]
 if 1: for x in y: pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testUnexpectedEllipsis]
 a = a...
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testParseErrorBeforeUnicodeLiteral]
 x u'y'
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testParseErrorInExtendedSlicing]
 x[:,
 [out]
-file:1: error: unexpected EOF while parsing
+file:1: error: Unexpected EOF while parsing
 
 [case testParseErrorInExtendedSlicing2]
 x[:,::
 [out]
-file:1: error: unexpected EOF while parsing
+file:1: error: Unexpected EOF while parsing
 
 [case testParseErrorInExtendedSlicing3]
 x[:,:
 [out]
-file:1: error: unexpected EOF while parsing
+file:1: error: Unexpected EOF while parsing
 
 [case testInvalidEncoding]
 # foo
 # coding: uft-8
 [out]
-file:0: error: unknown encoding: uft-8
+file:0: error: Unknown encoding: uft-8
 
 [case testInvalidEncoding2]
 # coding=Uft.8
 [out]
-file:0: error: unknown encoding: Uft.8
+file:0: error: Unknown encoding: Uft.8
 
 [case testInvalidEncoding3]
 #!/usr/bin python
 # vim: set fileencoding=uft8 :
 [out]
-file:0: error: unknown encoding: uft8
+file:0: error: Unknown encoding: uft8
 
 [case testDoubleEncoding]
 # coding: uft8
 # coding: utf8
 # The first coding cookie should be used and fail.
 [out]
-file:0: error: unknown encoding: uft8
+file:0: error: Unknown encoding: uft8
 
 [case testDoubleEncoding2]
 # Again the first cookie should be used and fail.
 # coding: uft8
 # coding: utf8
 [out]
-file:0: error: unknown encoding: uft8
+file:0: error: Unknown encoding: uft8
 
 [case testLongLiteralInPython3]
 2L
 0x2L
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testPython2LegacyInequalityInPython3]
 1 <> 2
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testLambdaInListComprehensionInPython3]
 ([ 0 for x in 1, 2 if 3 ])
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testTupleArgListInPython3]
 def f(x, (y, z)): pass
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testBackquoteInPython3]
 `1 + 2`
 [out]
-file:1: error: invalid syntax
+file:1: error: Invalid syntax
 
 [case testSmartQuotes]
 foo = ‘bar’
 [out]
-file:1: error: invalid character '‘' (U+2018)
+file:1: error: Invalid character '‘' (U+2018)
 
 [case testExceptCommaInPython3]
 try:
@@ -442,4 +442,4 @@ try:
 except KeyError, IndexError:
     pass
 [out]
-file:3: error: invalid syntax
+file:3: error: Invalid syntax
diff --git a/test-data/unit/parse.test b/test-data/unit/parse.test
index 943ca49081f1..fa1d797fada4 100644
--- a/test-data/unit/parse.test
+++ b/test-data/unit/parse.test
@@ -932,20 +932,20 @@ MypyFile:1(
 [case testNotAsBinaryOp]
 x not y
 [out]
-main:1: error: invalid syntax
+main:1: error: Invalid syntax
 [out version==3.10.0]
-main:1: error: invalid syntax. Perhaps you forgot a comma?
+main:1: error: Invalid syntax. Perhaps you forgot a comma?
 
 [case testNotIs]
-x not is y # E: invalid syntax
+x not is y # E: Invalid syntax
 [out]
 
 [case testBinaryNegAsBinaryOp]
 1 ~ 2
 [out]
-main:1: error: invalid syntax
+main:1: error: Invalid syntax
 [out version==3.10.0]
-main:1: error: invalid syntax. Perhaps you forgot a comma?
+main:1: error: Invalid syntax. Perhaps you forgot a comma?
 
 [case testSliceInList39]
 # flags: --python-version 3.9
@@ -3211,7 +3211,7 @@ MypyFile:1(
 [case testParseExtendedSlicing4]
 m[*index, :]
 [out]
-main:1: error: invalid syntax
+main:1: error: Invalid syntax
 [out version>=3.11]
 MypyFile:1(
   ExpressionStmt:1(
diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test
index 52c658c97c3b..fa5cec795931 100644
--- a/test-data/unit/semanal-errors.test
+++ b/test-data/unit/semanal-errors.test
@@ -361,84 +361,84 @@ main:2: error: "yield" outside function
 [case testInvalidLvalues1]
 1 = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 [out version>=3.10]
-main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues2]
 (1) = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 [out version>=3.10]
-main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues3]
 (1, 1) = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 
 [case testInvalidLvalues4]
 [1, 1] = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 
 [case testInvalidLvalues6]
 x = y = z = 1  # ok
 x, (y, 1) = 1
 [out]
-main:2: error: cannot assign to literal
+main:2: error: Cannot assign to literal
 
 [case testInvalidLvalues7]
 x, [y, 1] = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 
 [case testInvalidLvalues8]
 x, [y, [z, 1]] = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 
 [case testInvalidLvalues9]
 x, (y) = 1 # ok
 x, (y, (z, z)) = 1 # ok
 x, (y, (z, 1)) = 1
 [out]
-main:3: error: cannot assign to literal
+main:3: error: Cannot assign to literal
 
 [case testInvalidLvalues10]
 x + x = 1
 [out]
-main:1: error: cannot assign to operator
+main:1: error: Cannot assign to operator
 [out version>=3.10]
-main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to expression here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues11]
 -x = 1
 [out]
-main:1: error: cannot assign to operator
+main:1: error: Cannot assign to operator
 [out version>=3.10]
-main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to expression here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues12]
 1.1 = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 [out version>=3.10]
-main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues13]
 'x' = 1
 [out]
-main:1: error: cannot assign to literal
+main:1: error: Cannot assign to literal
 [out version>=3.10]
-main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalues14]
 x() = 1
 [out]
-main:1: error: cannot assign to function call
+main:1: error: Cannot assign to function call
 [out version>=3.10]
-main:1: error: cannot assign to function call here. Maybe you meant '==' instead of '='?
+main:1: error: Cannot assign to function call here. Maybe you meant '==' instead of '='?
 
 [case testTwoStarExpressions]
 a, *b, *c = 1
@@ -492,15 +492,15 @@ main:2: error: can't use starred expression here
 x = 1
 del x(1)
 [out]
-main:2: error: cannot delete function call
+main:2: error: Cannot delete function call
 
 [case testInvalidDel2]
 x = 1
 del x + 1
 [out]
-main:2: error: cannot delete operator
+main:2: error: Cannot delete operator
 [out version>=3.10]
-main:2: error: cannot delete expression
+main:2: error: Cannot delete expression
 
 [case testInvalidDel3]
 del z     # E: Name "z" is not defined
@@ -897,9 +897,9 @@ import typing
 def f(): pass
 f() = 1 # type: int
 [out]
-main:3: error: cannot assign to function call
+main:3: error: Cannot assign to function call
 [out version>=3.10]
-main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='?
+main:3: error: Cannot assign to function call here. Maybe you meant '==' instead of '='?
 
 [case testIndexedAssignmentWithTypeDeclaration]
 import typing
@@ -975,9 +975,9 @@ x, y = 1, 2 # type: int # E: Tuple type expected for multiple variables
 a = 1
 a() = None # type: int
 [out]
-main:2: error: cannot assign to function call
+main:2: error: Cannot assign to function call
 [out version>=3.10]
-main:2: error: cannot assign to function call here. Maybe you meant '==' instead of '='?
+main:2: error: Cannot assign to function call here. Maybe you meant '==' instead of '='?
 
 [case testInvalidLvalueWithExplicitType2]
 a = 1
@@ -1299,7 +1299,7 @@ main:2: note: Did you forget to import it from "typing"? (Suggestion: "from typi
 def f(): pass
 with f() as 1: pass
 [out]
-main:2: error: cannot assign to literal
+main:2: error: Cannot assign to literal
 
 [case testInvalidTypeAnnotation]
 import typing
@@ -1313,9 +1313,9 @@ import typing
 def f() -> None:
     f() = 1  # type: int
 [out]
-main:3: error: cannot assign to function call
+main:3: error: Cannot assign to function call
 [out version>=3.10]
-main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='?
+main:3: error: Cannot assign to function call here. Maybe you meant '==' instead of '='?
 
 [case testInvalidReferenceToAttributeOfOuterClass]
 class A:
diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test
index f828e2a3263f..a2e8691733ef 100644
--- a/test-data/unit/semanal-statements.test
+++ b/test-data/unit/semanal-statements.test
@@ -557,9 +557,9 @@ MypyFile:1(
 def f(x, y) -> None:
     del x, y + 1
 [out]
-main:2: error: cannot delete operator
+main:2: error: Cannot delete operator
 [out version>=3.10]
-main:2: error: cannot delete expression
+main:2: error: Cannot delete expression
 
 [case testTry]
 class c: pass

From 3f50e3caad3b18b93a1758bb4ba8a491955531e9 Mon Sep 17 00:00:00 2001
From: Alexey Makridenko 
Date: Wed, 21 May 2025 14:52:09 +0200
Subject: [PATCH 1304/1617] stubgen: add import for `types` in `__exit__`
 method signature (#19120)

Fixes #17037

Add import for `types` in `__exit__` method signature
---
 mypy/stubgen.py             | 5 +++++
 test-data/unit/stubgen.test | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index f074a34d5c64..ece22ba235bf 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -633,6 +633,11 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]:
             new_args = infer_method_arg_types(
                 ctx.name, ctx.class_info.self_var, [arg.name for arg in args]
             )
+
+            if ctx.name == "__exit__":
+                self.import_tracker.add_import("types")
+                self.import_tracker.require_name("types")
+
             if new_args is not None:
                 args = new_args
 
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
index 717137cbd251..b4c66c2e5853 100644
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -3777,6 +3777,8 @@ class MatchNames:
     def __exit__(self, type, value, traceback): ...
 
 [out]
+import types
+
 class MismatchNames:
     def __exit__(self, tp: type[BaseException] | None, val: BaseException | None, tb: types.TracebackType | None) -> None: ...
 

From a8ec8939ce5a8ba332ec428bec8c4b7ef8c42344 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 23 May 2025 04:41:41 +0200
Subject: [PATCH 1305/1617] Forbid `.pop` of `Readonly` `NotRequired` TypedDict
 items (#19133)

Fixes #19130. We already have these checks for `del
typed_dict["readonly_notrequired_key"]`, this just aligns `.pop()` logic
with that.
---
 mypy/plugins/default.py             | 2 +-
 test-data/unit/check-typeddict.test | 6 +++++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py
index 81d2f19dc17b..2002a4f06093 100644
--- a/mypy/plugins/default.py
+++ b/mypy/plugins/default.py
@@ -316,7 +316,7 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type:
 
         value_types = []
         for key in keys:
-            if key in ctx.type.required_keys:
+            if key in ctx.type.required_keys or key in ctx.type.readonly_keys:
                 ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, key_expr)
 
             value_type = ctx.type.items.get(key)
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index 47c8a71ba0e3..cae90d56c3a6 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -3760,20 +3760,24 @@ del x["optional_key"]  # E: Key "optional_key" of TypedDict "TP" cannot be delet
 [typing fixtures/typing-typeddict.pyi]
 
 [case testTypedDictReadOnlyMutateMethods]
-from typing import ReadOnly, TypedDict
+from typing import ReadOnly, NotRequired, TypedDict
 
 class TP(TypedDict):
     key: ReadOnly[str]
+    optional_key: ReadOnly[NotRequired[str]]
     other: ReadOnly[int]
     mutable: bool
 
 x: TP
 reveal_type(x.pop("key"))  # N: Revealed type is "builtins.str" \
                            # E: Key "key" of TypedDict "TP" cannot be deleted
+reveal_type(x.pop("optional_key"))  # N: Revealed type is "builtins.str" \
+                                    # E: Key "optional_key" of TypedDict "TP" cannot be deleted
 
 
 x.update({"key": "abc", "other": 1, "mutable": True})  # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated
 x.setdefault("key", "abc")  # E: ReadOnly TypedDict key "key" TypedDict is mutated
+x.setdefault("optional_key", "foo")  # E: ReadOnly TypedDict key "optional_key" TypedDict is mutated
 x.setdefault("other", 1)  # E: ReadOnly TypedDict key "other" TypedDict is mutated
 x.setdefault("mutable", False)  # ok
 [builtins fixtures/dict.pyi]

From 27d118b053d5eb5ef374dfb5a681daf2b3475ebb Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Tue, 27 May 2025 02:55:24 -0700
Subject: [PATCH 1306/1617] Fix nondeterministic type checking by making join
 between TypeType and TypeVar commute (#19149)

Fixes #18125

Unhandled cases in `default` seem fairly dangerous
---
 mypy/join.py           | 2 ++
 mypy/test/testtypes.py | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/mypy/join.py b/mypy/join.py
index ac01d11d11d6..fcfc6cbaa0e7 100644
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -635,6 +635,8 @@ def default(self, typ: Type) -> ProperType:
         typ = get_proper_type(typ)
         if isinstance(typ, Instance):
             return object_from_instance(typ)
+        elif isinstance(typ, TypeType):
+            return self.default(typ.item)
         elif isinstance(typ, UnboundType):
             return AnyType(TypeOfAny.special_form)
         elif isinstance(typ, TupleType):
diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py
index a42519c64956..63d8840fa217 100644
--- a/mypy/test/testtypes.py
+++ b/mypy/test/testtypes.py
@@ -1064,6 +1064,10 @@ def test_variadic_tuple_joins(self) -> None:
             self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a),
         )
 
+    def test_join_type_type_type_var(self) -> None:
+        self.assert_join(self.fx.type_a, self.fx.t, self.fx.o)
+        self.assert_join(self.fx.t, self.fx.type_a, self.fx.o)
+
     # There are additional test cases in check-inference.test.
 
     # TODO: Function types + varargs and default args.

From 05d3e5f17a4a3c1d4a9c723cdf1e2558a1bae770 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 27 May 2025 16:10:06 +0100
Subject: [PATCH 1307/1617] Document --allow-redefinition-new (#19153)

The feature was introduced in #18727.
---
 docs/source/command_line.rst | 50 ++++++++++++++++++++++++++++++++++--
 docs/source/config_file.rst  | 39 ++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index b455e287017e..dfed280d12ed 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -593,12 +593,58 @@ of the above sections.
     This flag causes mypy to suppress errors caused by not being able to fully
     infer the types of global and class variables.
 
-.. option:: --allow-redefinition
+.. option:: --allow-redefinition-new
 
     By default, mypy won't allow a variable to be redefined with an
-    unrelated type. This flag enables redefinition of a variable with an
+    unrelated type. This *experimental* flag enables the redefinition of
+    unannotated variables with an arbitrary type. You will also need to enable
+    :option:`--local-partial-types `.
+    Example:
+
+    .. code-block:: python
+
+        def maybe_convert(n: int, b: bool) -> int | str:
+            if b:
+                x = str(n)  # Assign "str"
+            else:
+                x = n       # Assign "int"
+            # Type of "x" is "int | str" here.
+            return x
+
+    Without the new flag, mypy only supports inferring optional types
+    (``X | None``) from multiple assignments. With this option enabled,
+    mypy can infer arbitrary union types.
+
+    This also enables an unannotated variable to have different types in different
+    code locations:
+
+    .. code-block:: python
+
+        if check():
+            for x in range(n):
+                # Type of "x" is "int" here.
+                ...
+        else:
+            for x in ['a', 'b']:
+                # Type of "x" is "str" here.
+                ...
+
+    Note: We are planning to turn this flag on by default in a future mypy
+    release, along with :option:`--local-partial-types `.
+    The feature is still experimental, and the semantics may still change.
+
+.. option:: --allow-redefinition
+
+    This is an older variant of
+    :option:`--allow-redefinition-new `.
+    This flag enables redefinition of a variable with an
     arbitrary type *in some contexts*: only redefinitions within the
     same block and nesting depth as the original definition are allowed.
+
+    We have no plans to remove this flag, but we expect that
+    :option:`--allow-redefinition-new `
+    will replace this flag for new use cases eventually.
+
     Example where this can be useful:
 
     .. code-block:: python
diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst
index de51f0c796fd..9f23617b9481 100644
--- a/docs/source/config_file.rst
+++ b/docs/source/config_file.rst
@@ -713,6 +713,44 @@ section of the command line docs.
     Causes mypy to suppress errors caused by not being able to fully
     infer the types of global and class variables.
 
+.. confval:: allow_redefinition_new
+
+    :type: boolean
+    :default: False
+
+    By default, mypy won't allow a variable to be redefined with an
+    unrelated type. This *experimental* flag enables the redefinition of
+    unannotated variables with an arbitrary type. You will also need to enable
+    :confval:`local_partial_types`.
+    Example:
+
+    .. code-block:: python
+
+        def maybe_convert(n: int, b: bool) -> int | str:
+            if b:
+                x = str(n)  # Assign "str"
+            else:
+                x = n       # Assign "int"
+            # Type of "x" is "int | str" here.
+            return x
+
+    This also enables an unannotated variable to have different types in different
+    code locations:
+
+    .. code-block:: python
+
+        if check():
+            for x in range(n):
+                # Type of "x" is "int" here.
+                ...
+        else:
+            for x in ['a', 'b']:
+                # Type of "x" is "str" here.
+                ...
+
+    Note: We are planning to turn this flag on by default in a future mypy
+    release, along with :confval:`local_partial_types`.
+
 .. confval:: allow_redefinition
 
     :type: boolean
@@ -746,6 +784,7 @@ section of the command line docs.
 
     Disallows inferring variable type for ``None`` from two assignments in different scopes.
     This is always implicitly enabled when using the :ref:`mypy daemon `.
+    This will be enabled by default in a future mypy release.
 
 .. confval:: disable_error_code
 

From 546feafe31aba20c97739f54491039a5640851a8 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 27 May 2025 17:12:13 +0200
Subject: [PATCH 1308/1617] Bump version to 1.17.0+dev (#19070)

The release branch has been cut:
https://github.com/python/mypy/tree/release-1.16
---
 mypy/version.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/version.py b/mypy/version.py
index ffebfb7aa9ad..21d23758c6dc 100644
--- a/mypy/version.py
+++ b/mypy/version.py
@@ -8,7 +8,7 @@
 # - Release versions have the form "1.2.3".
 # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440).
 # - Before 1.0 we had the form "0.NNN".
-__version__ = "1.16.0+dev"
+__version__ = "1.17.0+dev"
 base_version = __version__
 
 mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

From 50734e9d74e0986bfe1a295f1dfee1c566f9ec25 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 27 May 2025 16:59:29 +0100
Subject: [PATCH 1309/1617] [mypyc] Improve documentation of native and
 non-native classes (#19154)

Also discuss `mypyc_attr(native_class=<...>)`.
---
 mypyc/doc/native_classes.rst | 82 ++++++++++++++++++++++++++++++++----
 1 file changed, 74 insertions(+), 8 deletions(-)

diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst
index 7f892de3e239..dbcf238b78d5 100644
--- a/mypyc/doc/native_classes.rst
+++ b/mypyc/doc/native_classes.rst
@@ -48,11 +48,13 @@ can be assigned to (similar to using ``__slots__``)::
 Inheritance
 -----------
 
-Only single inheritance is supported (except for :ref:`traits
-`). Most non-native classes can't be used as base
-classes.
+Only single inheritance is supported from native classes (except for
+:ref:`traits `). Most non-native extension classes can't
+be used as base classes, but regular Python classes can be used as
+base classes unless they use unsupported metaclasses (see below for
+more about this).
 
-These non-native classes can be used as base classes of native
+These non-native extension classes can be used as base classes of native
 classes:
 
 * ``object``
@@ -63,8 +65,6 @@ classes:
 * ``IndexError``
 * ``LookupError``
 * ``UserWarning``
-* ``typing.NamedTuple``
-* ``enum.Enum``
 
 By default, a non-native class can't inherit a native class, and you
 can't inherit from a native class outside the compilation unit that
@@ -89,6 +89,15 @@ You need to install ``mypy-extensions`` to use ``@mypyc_attr``:
 
     pip install --upgrade mypy-extensions
 
+Additionally, mypyc recognizes these base classes as special, and
+understands how they alter the behavior of classes (including native
+classes) that subclass them:
+
+* ``typing.NamedTuple``
+* ``typing.Generic``
+* ``typing.Protocol``
+* ``enum.Enum``
+
 Class variables
 ---------------
 
@@ -145,7 +154,8 @@ behavior is too dynamic. You can use these metaclasses, however:
 .. note::
 
    If a class definition uses an unsupported metaclass, *mypyc
-   compiles the class into a regular Python class*.
+   compiles the class into a regular Python class* (non-native
+   class).
 
 Class decorators
 ----------------
@@ -165,7 +175,63 @@ efficient as pure native classes.
 .. note::
 
    If a class definition uses an unsupported class decorator, *mypyc
-   compiles the class into a regular Python class*.
+   compiles the class into a regular Python class* (non-native class).
+
+Defining non-native classes
+---------------------------
+
+You can use the ``@mypy_extensions.mypyc_attr(...)`` class decorator
+with an argument ``native_class=False`` to explicitly define normal
+Python classes (non-native classes)::
+
+    from mypy_extensions import mypyc_attr
+
+    @mypyc_attr(native_class=False)
+    class NonNative:
+        def __init__(self) -> None:
+            self.attr = 1
+
+    setattr(NonNative, "extra", 1)  # Ok
+
+This only has an effect in classes compiled using mypyc. Non-native
+classes are significantly less efficient than native classes, but they
+are sometimes necessary to work around the limitations of native classes.
+
+Non-native classes can use arbitrary metaclasses and class decorators,
+and they support flexible multiple inheritance.  Mypyc will still
+generate a compile-time error if you try to assign to a method, or an
+attribute that is not defined in a class body, since these are static
+type errors detected by mypy::
+
+    o = NonNative()
+    o.extra = "x"  # Static type error: "extra" not defined
+
+However, these operations still work at runtime, including in modules
+that are not compiled using mypyc. You can also use ``setattr`` and
+``getattr`` for dynamic access of arbitrary attributes. Expressions
+with an ``Any`` type are also not type checked statically, allowing
+access to arbitrary attributes::
+
+    a: Any = o
+    a.extra = "x"  # Ok
+
+    setattr(o, "extra", "y")  # Also ok
+
+Implicit non-native classes
+---------------------------
+
+If a compiled class uses an unsupported metaclass or an unsupported
+class decorator, it will implicitly be a non-native class, as
+discussed above. You can still use ``@mypyc_attr(native_class=False)``
+to explicitly mark it as a non-native class.
+
+Explicit native classes
+-----------------------
+
+You can use ``@mypyc_attr(native_class=True)`` to explicitly declare a
+class as a native class. It will be a compile-time error if mypyc
+can't compile the class as a native class. You can use this to avoid
+accidentally defining implicit non-native classes.
 
 Deleting attributes
 -------------------

From 750a5790c63fddc8bab838643949c13845e9a037 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 27 May 2025 17:36:52 +0100
Subject: [PATCH 1310/1617] [mypyc] Fix incref/decref on free-threaded builds
 (#19127)

Fix C compile errors on free-threaded builds. We can't (easily) access
the reference count value directly, so always use the C API functions
when on a free-threaded build.

Work on mypyc/mypyc#1038.
---
 mypyc/lib-rt/mypyc_util.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h
index 80019d23bb06..64bf025aec27 100644
--- a/mypyc/lib-rt/mypyc_util.h
+++ b/mypyc/lib-rt/mypyc_util.h
@@ -31,6 +31,8 @@
 // Here just for consistency
 #define CPy_XDECREF(p) Py_XDECREF(p)
 
+#ifndef Py_GIL_DISABLED
+
 // The *_NO_IMM operations below perform refcount manipulation for
 // non-immortal objects (Python 3.12 and later).
 //
@@ -60,6 +62,14 @@ static inline void CPy_XDECREF_NO_IMM(PyObject *op)
 #define CPy_DECREF_NO_IMM(op) CPy_DECREF_NO_IMM((PyObject *)(op))
 #define CPy_XDECREF_NO_IMM(op) CPy_XDECREF_NO_IMM((PyObject *)(op))
 
+#else
+
+#define CPy_INCREF_NO_IMM(op) CPy_INCREF(op)
+#define CPy_DECREF_NO_IMM(op) CPy_DECREF(op)
+#define CPy_XDECREF_NO_IMM(op) CPy_XDECREF(op)
+
+#endif
+
 // Tagged integer -- our representation of Python 'int' objects.
 // Small enough integers are represented as unboxed integers (shifted
 // left by 1); larger integers (larger than 63 bits on a 64-bit

From dfd2f28303da9616a1174cacae72c05e48b1e742 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 27 May 2025 17:45:56 +0100
Subject: [PATCH 1311/1617] [mypyc] Refactor extension module C generation and
 generated C (#19126)

Split a large function and extract module execution to a new C function
in preparation for supporting multi-phase init.

There are no changes in behavior.
---
 mypyc/codegen/emitmodule.py | 99 ++++++++++++++++++++++++-------------
 1 file changed, 66 insertions(+), 33 deletions(-)

diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index b8a19ac1d669..a3970b9c181e 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -867,8 +867,16 @@ def generate_globals_init(self, emitter: Emitter) -> None:
 
     def generate_module_def(self, emitter: Emitter, module_name: str, module: ModuleIR) -> None:
         """Emit the PyModuleDef struct for a module and the module init function."""
-        # Emit module methods
         module_prefix = emitter.names.private_name(module_name)
+        self.emit_module_exec_func(emitter, module_name, module_prefix, module)
+        self.emit_module_methods(emitter, module_name, module_prefix, module)
+        self.emit_module_def_struct(emitter, module_name, module_prefix)
+        self.emit_module_init_func(emitter, module_name, module_prefix)
+
+    def emit_module_methods(
+        self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
+    ) -> None:
+        """Emit module methods (the static PyMethodDef table)."""
         emitter.emit_line(f"static PyMethodDef {module_prefix}module_methods[] = {{")
         for fn in module.functions:
             if fn.class_name is not None or fn.name == TOP_LEVEL_NAME:
@@ -888,7 +896,10 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
         emitter.emit_line("};")
         emitter.emit_line()
 
-        # Emit module definition struct
+    def emit_module_def_struct(
+        self, emitter: Emitter, module_name: str, module_prefix: str
+    ) -> None:
+        """Emit the static module definition struct (PyModuleDef)."""
         emitter.emit_lines(
             f"static struct PyModuleDef {module_prefix}module = {{",
             "PyModuleDef_HEAD_INIT,",
@@ -900,36 +911,22 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
             "};",
         )
         emitter.emit_line()
-        # Emit module init function. If we are compiling just one module, this
-        # will be the C API init function. If we are compiling 2+ modules, we
-        # generate a shared library for the modules and shims that call into
-        # the shared library, and in this case we use an internal module
-        # initialized function that will be called by the shim.
-        if not self.use_shared_lib:
-            declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)"
-        else:
-            declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)"
-        emitter.emit_lines(declaration, "{")
-        emitter.emit_line("PyObject* modname = NULL;")
-        # Store the module reference in a static and return it when necessary.
-        # This is separate from the *global* reference to the module that will
-        # be populated when it is imported by a compiled module. We want that
-        # reference to only be populated when the module has been successfully
-        # imported, whereas this we want to have to stop a circular import.
-        module_static = self.module_internal_static_name(module_name, emitter)
 
-        emitter.emit_lines(
-            f"if ({module_static}) {{",
-            f"Py_INCREF({module_static});",
-            f"return {module_static};",
-            "}",
-        )
+    def emit_module_exec_func(
+        self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
+    ) -> None:
+        """Emit the module init function.
 
-        emitter.emit_lines(
-            f"{module_static} = PyModule_Create(&{module_prefix}module);",
-            f"if (unlikely({module_static} == NULL))",
-            "    goto fail;",
-        )
+        If we are compiling just one module, this will be the C API init
+        function. If we are compiling 2+ modules, we generate a shared
+        library for the modules and shims that call into the shared
+        library, and in this case we use an internal module initialized
+        function that will be called by the shim.
+        """
+        declaration = f"static int {module_prefix}_exec(PyObject *module)"
+        module_static = self.module_internal_static_name(module_name, emitter)
+        emitter.emit_lines(declaration, "{")
+        emitter.emit_line("PyObject* modname = NULL;")
         emitter.emit_line(
             f'modname = PyObject_GetAttrString((PyObject *){module_static}, "__name__");'
         )
@@ -959,8 +956,9 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
 
         emitter.emit_lines("Py_DECREF(modname);")
 
-        emitter.emit_line(f"return {module_static};")
-        emitter.emit_lines("fail:", f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
+        emitter.emit_line("return 0;")
+        emitter.emit_lines("fail:")
+        emitter.emit_lines(f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
         for name, typ in module.final_names:
             static_name = emitter.static_name(name, module_name)
             emitter.emit_dec_ref(static_name, typ, is_xdec=True)
@@ -970,9 +968,44 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
         # so we have to decref them
         for t in type_structs:
             emitter.emit_line(f"Py_CLEAR({t});")
-        emitter.emit_line("return NULL;")
+        emitter.emit_line("return -1;")
         emitter.emit_line("}")
 
+    def emit_module_init_func(
+        self, emitter: Emitter, module_name: str, module_prefix: str
+    ) -> None:
+        if not self.use_shared_lib:
+            declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)"
+        else:
+            declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)"
+        emitter.emit_lines(declaration, "{")
+
+        exec_func = f"{module_prefix}_exec"
+
+        # Store the module reference in a static and return it when necessary.
+        # This is separate from the *global* reference to the module that will
+        # be populated when it is imported by a compiled module. We want that
+        # reference to only be populated when the module has been successfully
+        # imported, whereas this we want to have to stop a circular import.
+        module_static = self.module_internal_static_name(module_name, emitter)
+
+        emitter.emit_lines(
+            f"if ({module_static}) {{",
+            f"Py_INCREF({module_static});",
+            f"return {module_static};",
+            "}",
+        )
+
+        emitter.emit_lines(
+            f"{module_static} = PyModule_Create(&{module_prefix}module);",
+            f"if (unlikely({module_static} == NULL))",
+            "    goto fail;",
+        )
+        emitter.emit_lines(f"if ({exec_func}({module_static}) != 0)", "    goto fail;")
+        emitter.emit_line(f"return {module_static};")
+        emitter.emit_lines("fail:", "return NULL;")
+        emitter.emit_lines("}")
+
     def generate_top_level_call(self, module: ModuleIR, emitter: Emitter) -> None:
         """Generate call to function representing module top level."""
         # Optimization: we tend to put the top level last, so reverse iterate

From 33d1eedc059f3caf46a1fdc416ded689fdf0efd0 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 27 May 2025 21:13:22 +0200
Subject: [PATCH 1312/1617] Sync typeshed (#18930)

Source commit:

https://github.com/python/typeshed/commit/45c0e52b302f1debd002f82f85647b7b0c9b2755

Typeshed has dropped support for Python 3.8 now! Merge only when mypy
can drop support for `--python-version 3.8` as well.

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: AlexWaygood 
---
 ...ially-revert-Clean-up-argparse-hacks.patch |  10 +-
 ...e-of-LiteralString-in-builtins-13743.patch |  31 +-
 ...redundant-inheritances-from-Iterator.patch | 162 ++---
 ...ert-sum-literal-integer-change-13961.patch |   8 +-
 .../0001-Revert-typeshed-ctypes-change.patch  |  10 +-
 mypy/typeshed/stdlib/VERSIONS                 |  12 +-
 mypy/typeshed/stdlib/__main__.pyi             |   4 +-
 mypy/typeshed/stdlib/_ast.pyi                 |  20 +-
 mypy/typeshed/stdlib/_asyncio.pyi             |  31 +-
 mypy/typeshed/stdlib/_blake2.pyi              | 113 ++--
 mypy/typeshed/stdlib/_codecs.pyi              |  22 +-
 mypy/typeshed/stdlib/_collections_abc.pyi     |   4 +-
 mypy/typeshed/stdlib/_compression.pyi         |   8 +-
 mypy/typeshed/stdlib/_contextvars.pyi         |  15 +-
 mypy/typeshed/stdlib/_csv.pyi                 |   9 +-
 mypy/typeshed/stdlib/_ctypes.pyi              |  26 +-
 mypy/typeshed/stdlib/_curses.pyi              |  29 +-
 mypy/typeshed/stdlib/_decimal.pyi             |   5 +
 mypy/typeshed/stdlib/_dummy_thread.pyi        |  33 --
 mypy/typeshed/stdlib/_dummy_threading.pyi     |  56 --
 .../stdlib/_frozen_importlib_external.pyi     |  12 +-
 mypy/typeshed/stdlib/_hashlib.pyi             |  79 ++-
 mypy/typeshed/stdlib/_io.pyi                  |  40 +-
 mypy/typeshed/stdlib/_pickle.pyi              |  15 +-
 mypy/typeshed/stdlib/_pydecimal.pyi           |   4 +
 mypy/typeshed/stdlib/_queue.pyi               |   8 +-
 mypy/typeshed/stdlib/_socket.pyi              |  96 +--
 mypy/typeshed/stdlib/_ssl.pyi                 |   2 +
 mypy/typeshed/stdlib/_tracemalloc.pyi         |   6 +-
 mypy/typeshed/stdlib/_typeshed/__init__.pyi   |  23 +-
 .../_typeshed/_type_checker_internals.pyi     |  89 +++
 mypy/typeshed/stdlib/_weakrefset.pyi          |   8 +-
 mypy/typeshed/stdlib/aifc.pyi                 |  14 +-
 mypy/typeshed/stdlib/annotationlib.pyi        | 132 +++++
 mypy/typeshed/stdlib/argparse.pyi             |  76 ++-
 mypy/typeshed/stdlib/array.pyi                |  13 +-
 mypy/typeshed/stdlib/ast.pyi                  | 300 +++++-----
 mypy/typeshed/stdlib/asyncio/__init__.pyi     | 197 +------
 mypy/typeshed/stdlib/asyncio/base_events.pyi  |   7 +-
 mypy/typeshed/stdlib/asyncio/events.pyi       |  57 +-
 mypy/typeshed/stdlib/asyncio/futures.pyi      |   8 +-
 mypy/typeshed/stdlib/asyncio/graph.pyi        |  26 +
 mypy/typeshed/stdlib/asyncio/locks.pyi        |  30 +-
 mypy/typeshed/stdlib/asyncio/queues.pyi       |   7 +-
 mypy/typeshed/stdlib/asyncio/tasks.pyi        |   4 +-
 mypy/typeshed/stdlib/asyncio/unix_events.pyi  |  37 +-
 mypy/typeshed/stdlib/base64.pyi               |   4 -
 mypy/typeshed/stdlib/bdb.pyi                  |  17 +-
 mypy/typeshed/stdlib/builtins.pyi             | 281 +++++----
 mypy/typeshed/stdlib/bz2.pyi                  |  46 +-
 mypy/typeshed/stdlib/code.pyi                 |  18 +-
 mypy/typeshed/stdlib/codeop.pyi               |   6 +-
 mypy/typeshed/stdlib/collections/__init__.pyi | 101 ++--
 mypy/typeshed/stdlib/colorsys.pyi             |   2 +-
 mypy/typeshed/stdlib/compileall.pyi           |  26 +-
 mypy/typeshed/stdlib/compression/__init__.pyi |   0
 .../stdlib/compression/_common/__init__.pyi   |   0
 .../stdlib/compression/_common/_streams.pyi   |  25 +
 .../stdlib/compression/bz2/__init__.pyi       |   1 +
 .../stdlib/compression/gzip/__init__.pyi      |   1 +
 .../stdlib/compression/lzma/__init__.pyi      |   1 +
 .../stdlib/compression/zlib/__init__.pyi      |   1 +
 .../stdlib/concurrent/futures/__init__.pyi    |  22 +-
 .../stdlib/concurrent/futures/_base.pyi       |  47 +-
 .../stdlib/concurrent/futures/interpreter.pyi | 102 ++++
 .../stdlib/concurrent/futures/process.pyi     |  54 +-
 .../stdlib/concurrent/futures/thread.pyi      |  94 ++-
 mypy/typeshed/stdlib/configparser.pyi         |  35 +-
 mypy/typeshed/stdlib/contextlib.pyi           |  13 +-
 mypy/typeshed/stdlib/csv.pyi                  |   4 +-
 mypy/typeshed/stdlib/ctypes/__init__.pyi      | 170 ++++--
 mypy/typeshed/stdlib/ctypes/wintypes.pyi      |  12 +-
 mypy/typeshed/stdlib/curses/__init__.pyi      |   5 -
 mypy/typeshed/stdlib/dataclasses.pyi          | 134 ++++-
 mypy/typeshed/stdlib/datetime.pyi             |  40 +-
 mypy/typeshed/stdlib/decimal.pyi              |   8 +
 mypy/typeshed/stdlib/difflib.pyi              |  14 +-
 mypy/typeshed/stdlib/dis.pyi                  |  85 ++-
 mypy/typeshed/stdlib/distutils/cmd.pyi        |   4 +-
 .../stdlib/distutils/command/bdist_msi.pyi    |   3 +-
 .../stdlib/distutils/command/config.pyi       |   4 +-
 .../stdlib/distutils/command/register.pyi     |   3 +-
 mypy/typeshed/stdlib/distutils/dist.pyi       |   4 +-
 .../stdlib/distutils/fancy_getopt.pyi         |   2 +-
 mypy/typeshed/stdlib/dummy_threading.pyi      |   2 -
 mypy/typeshed/stdlib/email/__init__.pyi       |   3 +-
 .../stdlib/email/_header_value_parser.pyi     |   9 +-
 mypy/typeshed/stdlib/email/_policybase.pyi    |  31 +-
 mypy/typeshed/stdlib/email/errors.pyi         |   2 +-
 mypy/typeshed/stdlib/email/feedparser.pyi     |   5 +-
 mypy/typeshed/stdlib/email/generator.pyi      |   2 +-
 mypy/typeshed/stdlib/email/message.pyi        |  38 +-
 mypy/typeshed/stdlib/email/mime/message.pyi   |   3 +-
 mypy/typeshed/stdlib/email/mime/multipart.pyi |   3 +-
 mypy/typeshed/stdlib/email/mime/text.pyi      |   2 +-
 mypy/typeshed/stdlib/email/parser.pyi         |  17 +-
 mypy/typeshed/stdlib/email/policy.pyi         |  14 +-
 mypy/typeshed/stdlib/email/utils.pyi          |   4 +-
 mypy/typeshed/stdlib/encodings/__init__.pyi   |   3 +-
 .../stdlib/encodings/mac_centeuro.pyi         |  21 -
 .../stdlib/encodings/raw_unicode_escape.pyi   |  21 +-
 .../stdlib/encodings/unicode_escape.pyi       |  21 +-
 mypy/typeshed/stdlib/enum.pyi                 |  15 +-
 mypy/typeshed/stdlib/fcntl.pyi                |  10 +-
 mypy/typeshed/stdlib/filecmp.pyi              |   7 +-
 mypy/typeshed/stdlib/fileinput.pyi            |   8 +-
 mypy/typeshed/stdlib/fnmatch.pyi              |   6 +
 mypy/typeshed/stdlib/fractions.pyi            |  18 +-
 mypy/typeshed/stdlib/ftplib.pyi               |  49 +-
 mypy/typeshed/stdlib/functools.pyi            | 103 ++--
 mypy/typeshed/stdlib/gc.pyi                   |   6 +-
 mypy/typeshed/stdlib/getpass.pyi              |   8 +-
 mypy/typeshed/stdlib/gzip.pyi                 |  10 +-
 mypy/typeshed/stdlib/hashlib.pyi              |  34 +-
 mypy/typeshed/stdlib/hmac.pyi                 |  13 +-
 mypy/typeshed/stdlib/http/__init__.pyi        |   9 +-
 mypy/typeshed/stdlib/http/client.pyi          |  11 +-
 mypy/typeshed/stdlib/http/cookies.pyi         |   8 +-
 mypy/typeshed/stdlib/http/server.pyi          |  56 +-
 mypy/typeshed/stdlib/imaplib.pyi              |  68 ++-
 mypy/typeshed/stdlib/importlib/abc.pyi        |  84 +--
 .../stdlib/importlib/metadata/__init__.pyi    |   9 +-
 .../stdlib/importlib/resources/__init__.pyi   |  28 +-
 mypy/typeshed/stdlib/inspect.pyi              |  63 +-
 mypy/typeshed/stdlib/io.pyi                   |  15 +-
 mypy/typeshed/stdlib/ipaddress.pyi            |  39 +-
 mypy/typeshed/stdlib/itertools.pyi            |   7 +-
 mypy/typeshed/stdlib/keyword.pyi              |  15 +-
 mypy/typeshed/stdlib/linecache.pyi            |   6 +-
 mypy/typeshed/stdlib/logging/__init__.pyi     |  67 +--
 mypy/typeshed/stdlib/logging/handlers.pyi     | 130 ++--
 mypy/typeshed/stdlib/lzma.pyi                 |   7 +-
 mypy/typeshed/stdlib/mailbox.pyi              |  13 +-
 mypy/typeshed/stdlib/marshal.pyi              |  20 +-
 mypy/typeshed/stdlib/math.pyi                 |  19 +-
 .../stdlib/multiprocessing/managers.pyi       |   8 +-
 mypy/typeshed/stdlib/multiprocessing/pool.pyi |   9 +-
 .../stdlib/multiprocessing/queues.pyi         |  11 +-
 .../multiprocessing/resource_tracker.pyi      |   3 +
 .../stdlib/multiprocessing/shared_memory.pyi  |   7 +-
 mypy/typeshed/stdlib/nntplib.pyi              |   5 -
 mypy/typeshed/stdlib/nt.pyi                   |   4 +-
 mypy/typeshed/stdlib/nturl2path.pyi           |  14 +-
 mypy/typeshed/stdlib/numbers.pyi              |   3 +-
 mypy/typeshed/stdlib/opcode.pyi               |  18 +-
 mypy/typeshed/stdlib/optparse.pyi             |   2 +-
 mypy/typeshed/stdlib/os/__init__.pyi          | 102 ++--
 .../{pathlib.pyi => pathlib/__init__.pyi}     |  65 +-
 mypy/typeshed/stdlib/pathlib/types.pyi        |   8 +
 mypy/typeshed/stdlib/pdb.pyi                  |  74 ++-
 mypy/typeshed/stdlib/pkgutil.pyi              |  18 +-
 mypy/typeshed/stdlib/platform.pyi             |  56 +-
 mypy/typeshed/stdlib/plistlib.pyi             |  34 +-
 mypy/typeshed/stdlib/posix.pyi                |  17 +-
 mypy/typeshed/stdlib/pstats.pyi               |  39 +-
 mypy/typeshed/stdlib/pydoc.pyi                |  11 +-
 mypy/typeshed/stdlib/queue.pyi                |   7 +-
 mypy/typeshed/stdlib/random.pyi               |  25 +-
 mypy/typeshed/stdlib/re.pyi                   |  10 +-
 mypy/typeshed/stdlib/shutil.pyi               |  26 +-
 mypy/typeshed/stdlib/signal.pyi               |   5 +-
 mypy/typeshed/stdlib/smtplib.pyi              |  25 +-
 mypy/typeshed/stdlib/socket.pyi               | 156 ++---
 mypy/typeshed/stdlib/sqlite3/__init__.pyi     |   4 +-
 mypy/typeshed/stdlib/ssl.pyi                  |  25 +-
 mypy/typeshed/stdlib/statistics.pyi           |   4 +-
 .../{string.pyi => string/__init__.pyi}       |  11 +-
 mypy/typeshed/stdlib/string/templatelib.pyi   |  28 +
 mypy/typeshed/stdlib/subprocess.pyi           | 553 +-----------------
 mypy/typeshed/stdlib/sunau.pyi                |   4 -
 mypy/typeshed/stdlib/symtable.pyi             |   8 +-
 mypy/typeshed/stdlib/sys/__init__.pyi         |  24 +-
 mypy/typeshed/stdlib/tarfile.pyi              |  77 ++-
 mypy/typeshed/stdlib/tempfile.pyi             |  13 +-
 mypy/typeshed/stdlib/threading.pyi            |  67 ++-
 mypy/typeshed/stdlib/time.pyi                 |   2 +-
 mypy/typeshed/stdlib/tkinter/__init__.pyi     | 323 +++++-----
 mypy/typeshed/stdlib/tkinter/colorchooser.pyi |  16 +-
 mypy/typeshed/stdlib/tkinter/commondialog.pyi |   6 +-
 mypy/typeshed/stdlib/tkinter/dialog.pyi       |   7 +-
 mypy/typeshed/stdlib/tkinter/dnd.pyi          |   4 +-
 mypy/typeshed/stdlib/tkinter/filedialog.pyi   |  42 +-
 mypy/typeshed/stdlib/tkinter/font.pyi         |   3 +-
 mypy/typeshed/stdlib/tkinter/messagebox.pyi   |  13 +-
 mypy/typeshed/stdlib/tkinter/ttk.pyi          |  26 +-
 mypy/typeshed/stdlib/token.pyi                |   8 +
 mypy/typeshed/stdlib/tokenize.pyi             |   5 +-
 mypy/typeshed/stdlib/tomllib.pyi              |  20 +-
 mypy/typeshed/stdlib/trace.pyi                |   6 +-
 mypy/typeshed/stdlib/traceback.pyi            |   5 +-
 mypy/typeshed/stdlib/tracemalloc.pyi          |  15 +-
 mypy/typeshed/stdlib/types.pyi                |  77 +--
 mypy/typeshed/stdlib/typing.pyi               | 297 ++++++----
 mypy/typeshed/stdlib/typing_extensions.pyi    | 249 ++++----
 mypy/typeshed/stdlib/unittest/async_case.pyi  |   4 +-
 mypy/typeshed/stdlib/unittest/case.pyi        |  58 +-
 mypy/typeshed/stdlib/unittest/mock.pyi        |  13 +-
 mypy/typeshed/stdlib/urllib/parse.pyi         |  61 +-
 mypy/typeshed/stdlib/urllib/request.pyi       | 196 ++++---
 mypy/typeshed/stdlib/urllib/response.pyi      |   7 +-
 mypy/typeshed/stdlib/uuid.pyi                 |  36 +-
 mypy/typeshed/stdlib/venv/__init__.pyi        |  30 +-
 mypy/typeshed/stdlib/wave.pyi                 |   9 +-
 mypy/typeshed/stdlib/weakref.pyi              |  38 +-
 mypy/typeshed/stdlib/winsound.pyi             |  10 +
 mypy/typeshed/stdlib/xml/dom/minidom.pyi      | 128 ++--
 .../stdlib/xml/etree/ElementInclude.pyi       |  13 +-
 .../typeshed/stdlib/xml/etree/ElementTree.pyi |  17 +-
 mypy/typeshed/stdlib/xml/sax/expatreader.pyi  |   6 +-
 mypy/typeshed/stdlib/zipfile/__init__.pyi     |  34 +-
 mypy/typeshed/stdlib/zoneinfo/__init__.pyi    |  51 +-
 test-data/unit/pythoneval.test                |   8 +-
 212 files changed, 3879 insertions(+), 3993 deletions(-)
 delete mode 100644 mypy/typeshed/stdlib/_dummy_thread.pyi
 delete mode 100644 mypy/typeshed/stdlib/_dummy_threading.pyi
 create mode 100644 mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
 create mode 100644 mypy/typeshed/stdlib/annotationlib.pyi
 create mode 100644 mypy/typeshed/stdlib/asyncio/graph.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/_common/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/_common/_streams.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/bz2/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/gzip/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/lzma/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/zlib/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
 delete mode 100644 mypy/typeshed/stdlib/dummy_threading.pyi
 delete mode 100644 mypy/typeshed/stdlib/encodings/mac_centeuro.pyi
 rename mypy/typeshed/stdlib/{pathlib.pyi => pathlib/__init__.pyi} (86%)
 create mode 100644 mypy/typeshed/stdlib/pathlib/types.pyi
 rename mypy/typeshed/stdlib/{string.pyi => string/__init__.pyi} (88%)
 create mode 100644 mypy/typeshed/stdlib/string/templatelib.pyi

diff --git a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
index d0b1aca381df..f76818d10cba 100644
--- a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
+++ b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
@@ -1,4 +1,4 @@
-From b5f2cc9633f9f6cd9326eee96a32efb3aff70701 Mon Sep 17 00:00:00 2001
+From 05f351f6a37fe8b73c698c348bf6aa5108363049 Mon Sep 17 00:00:00 2001
 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
 Date: Sat, 15 Feb 2025 20:11:06 +0100
 Subject: [PATCH] Partially revert Clean up argparse hacks
@@ -8,7 +8,7 @@ Subject: [PATCH] Partially revert Clean up argparse hacks
  1 file changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
-index 029bfeefe..9dbd8c308 100644
+index 95ad6c7da..79e6cfde1 100644
 --- a/mypy/typeshed/stdlib/argparse.pyi
 +++ b/mypy/typeshed/stdlib/argparse.pyi
 @@ -2,7 +2,7 @@ import sys
@@ -20,7 +20,7 @@ index 029bfeefe..9dbd8c308 100644
  from typing_extensions import Self, TypeAlias, deprecated
  
  __all__ = [
-@@ -38,7 +38,9 @@ ONE_OR_MORE: Final = "+"
+@@ -36,7 +36,9 @@ ONE_OR_MORE: Final = "+"
  OPTIONAL: Final = "?"
  PARSER: Final = "A..."
  REMAINDER: Final = "..."
@@ -31,7 +31,7 @@ index 029bfeefe..9dbd8c308 100644
  ZERO_OR_MORE: Final = "*"
  _UNRECOGNIZED_ARGS_ATTR: Final = "_unrecognized_args"  # undocumented
  
-@@ -81,7 +83,7 @@ class _ActionsContainer:
+@@ -79,7 +81,7 @@ class _ActionsContainer:
          # more precisely, Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="],
          # but using this would make it hard to annotate callers that don't use a
          # literal argument and for subclasses to override this method.
@@ -41,5 +41,5 @@ index 029bfeefe..9dbd8c308 100644
          default: Any = ...,
          type: _ActionType = ...,
 -- 
-2.48.1
+2.49.0
 
diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
index 91e255242ee9..9d0cb5271e7d 100644
--- a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
+++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
@@ -1,4 +1,4 @@
-From b4259edd94188f9e4cc77a22e768eea183a32053 Mon Sep 17 00:00:00 2001
+From e6995c91231e1915eba43a29a22dd4cbfaf9e08e Mon Sep 17 00:00:00 2001
 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
 Date: Mon, 26 Sep 2022 12:55:07 -0700
 Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
@@ -8,10 +8,10 @@ Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
  1 file changed, 1 insertion(+), 99 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index 63c53a5f6..d55042b56 100644
+index 00728f42d..ea77a730f 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
-@@ -63,7 +63,6 @@ from typing import (  # noqa: Y022
+@@ -63,7 +63,6 @@ from typing import (  # noqa: Y022,UP035
  from typing_extensions import (  # noqa: Y023
      Concatenate,
      Literal,
@@ -19,7 +19,7 @@ index 63c53a5f6..d55042b56 100644
      ParamSpec,
      Self,
      TypeAlias,
-@@ -438,31 +437,16 @@ class str(Sequence[str]):
+@@ -453,31 +452,16 @@ class str(Sequence[str]):
      def __new__(cls, object: object = ...) -> Self: ...
      @overload
      def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
@@ -51,7 +51,7 @@ index 63c53a5f6..d55042b56 100644
      def format(self, *args: object, **kwargs: object) -> str: ...
      def format_map(self, mapping: _FormatMapMapping, /) -> str: ...
      def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-@@ -478,99 +462,35 @@ class str(Sequence[str]):
+@@ -493,98 +477,34 @@ class str(Sequence[str]):
      def isspace(self) -> bool: ...
      def istitle(self) -> bool: ...
      def isupper(self) -> bool: ...
@@ -89,16 +89,15 @@ index 63c53a5f6..d55042b56 100644
 -        ) -> LiteralString: ...
 -        @overload
          def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ...  # type: ignore[misc]
-     if sys.version_info >= (3, 9):
--        @overload
--        def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
--        @overload
-         def removeprefix(self, prefix: str, /) -> str: ...  # type: ignore[misc]
--        @overload
--        def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ...
--        @overload
-         def removesuffix(self, suffix: str, /) -> str: ...  # type: ignore[misc]
  
+-    @overload
+-    def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
+-    @overload
+     def removeprefix(self, prefix: str, /) -> str: ...  # type: ignore[misc]
+-    @overload
+-    def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ...
+-    @overload
+     def removesuffix(self, suffix: str, /) -> str: ...  # type: ignore[misc]
      def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
      def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
 -    @overload
@@ -151,7 +150,7 @@ index 63c53a5f6..d55042b56 100644
      def zfill(self, width: SupportsIndex, /) -> str: ...  # type: ignore[misc]
      @staticmethod
      @overload
-@@ -581,39 +501,21 @@ class str(Sequence[str]):
+@@ -595,39 +515,21 @@ class str(Sequence[str]):
      @staticmethod
      @overload
      def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...
@@ -193,5 +192,5 @@ index 63c53a5f6..d55042b56 100644
      def __getnewargs__(self) -> tuple[str]: ...
  
 -- 
-2.47.0
+2.49.0
 
diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
index ef1d9f4d3fa3..5b30a63f1318 100644
--- a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
+++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
@@ -1,4 +1,4 @@
-From abc5225e3c69d7ae8f3388c87260fe496efaecac Mon Sep 17 00:00:00 2001
+From 363d69b366695fea117631d30c348e36b9a5a99d Mon Sep 17 00:00:00 2001
 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
 Date: Sat, 21 Dec 2024 22:36:38 +0100
 Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
@@ -15,7 +15,7 @@ Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
  7 files changed, 34 insertions(+), 34 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
-index 89cdff6cc..1397e579d 100644
+index 4544680cc..19a2d12d8 100644
 --- a/mypy/typeshed/stdlib/_asyncio.pyi
 +++ b/mypy/typeshed/stdlib/_asyncio.pyi
 @@ -1,6 +1,6 @@
@@ -24,90 +24,90 @@ index 89cdff6cc..1397e579d 100644
 -from collections.abc import Awaitable, Callable, Coroutine, Generator
 +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable
  from contextvars import Context
- from types import FrameType
+ from types import FrameType, GenericAlias
  from typing import Any, Literal, TextIO, TypeVar
-@@ -13,7 +13,7 @@ _T = TypeVar("_T")
+@@ -10,7 +10,7 @@ _T = TypeVar("_T")
  _T_co = TypeVar("_T_co", covariant=True)
  _TaskYieldType: TypeAlias = Future[object] | None
-
+ 
 -class Future(Awaitable[_T]):
 +class Future(Awaitable[_T], Iterable[_T]):
      _state: str
      @property
      def _exception(self) -> BaseException | None: ...
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index b75e34fc5..526406acc 100644
+index ea77a730f..900c4c93f 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
-@@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]):
-     if sys.version_info >= (3, 9):
-         def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
-
+@@ -1170,7 +1170,7 @@ class frozenset(AbstractSet[_T_co]):
+     def __hash__(self) -> int: ...
+     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+ 
 -class enumerate(Generic[_T]):
 +class enumerate(Iterator[tuple[int, _T]]):
      def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> tuple[int, _T]: ...
-@@ -1324,7 +1324,7 @@ else:
-
+@@ -1366,7 +1366,7 @@ else:
+ 
  exit: _sitebuiltins.Quitter
-
+ 
 -class filter(Generic[_T]):
 +class filter(Iterator[_T]):
      @overload
      def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ...
      @overload
-@@ -1389,7 +1389,7 @@ license: _sitebuiltins._Printer
-
+@@ -1431,7 +1431,7 @@ license: _sitebuiltins._Printer
+ 
  def locals() -> dict[str, Any]: ...
-
+ 
 -class map(Generic[_S]):
 +class map(Iterator[_S]):
-     @overload
-     def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ...
-     @overload
-@@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
-
+     # 3.14 adds `strict` argument.
+     if sys.version_info >= (3, 14):
+         @overload
+@@ -1734,7 +1734,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
+ 
  quit: _sitebuiltins.Quitter
-
+ 
 -class reversed(Generic[_T]):
 +class reversed(Iterator[_T]):
      @overload
      def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ...  # type: ignore[misc]
      @overload
-@@ -1693,7 +1693,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
+@@ -1795,7 +1795,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
  @overload
  def vars(object: Any = ..., /) -> dict[str, Any]: ...
-
+ 
 -class zip(Generic[_T_co]):
 +class zip(Iterator[_T_co]):
      if sys.version_info >= (3, 10):
          @overload
          def __new__(cls, *, strict: bool = ...) -> zip[Any]: ...
 diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi
-index 4a82de638..ef93129d6 100644
+index 2c8e7109c..4ed0ab1d8 100644
 --- a/mypy/typeshed/stdlib/csv.pyi
 +++ b/mypy/typeshed/stdlib/csv.pyi
 @@ -25,7 +25,7 @@ else:
      from _csv import _reader as Reader, _writer as Writer
-
+ 
  from _typeshed import SupportsWrite
 -from collections.abc import Collection, Iterable, Mapping, Sequence
 +from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
+ from types import GenericAlias
  from typing import Any, Generic, Literal, TypeVar, overload
  from typing_extensions import Self
-
-@@ -75,7 +75,7 @@ class excel(Dialect): ...
+@@ -73,7 +73,7 @@ class excel(Dialect): ...
  class excel_tab(excel): ...
  class unix_dialect(Dialect): ...
-
+ 
 -class DictReader(Generic[_T]):
 +class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]):
      fieldnames: Sequence[_T] | None
      restkey: _T | None
      restval: str | Any | None
 diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi
-index bf6daad0a..1e6aa78e2 100644
+index 948b39ea1..1d5f9cf00 100644
 --- a/mypy/typeshed/stdlib/fileinput.pyi
 +++ b/mypy/typeshed/stdlib/fileinput.pyi
 @@ -1,8 +1,8 @@
@@ -115,27 +115,27 @@ index bf6daad0a..1e6aa78e2 100644
  from _typeshed import AnyStr_co, StrOrBytesPath
 -from collections.abc import Callable, Iterable
 +from collections.abc import Callable, Iterable, Iterator
- from types import TracebackType
+ from types import GenericAlias, TracebackType
 -from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload
 +from typing import IO, Any, AnyStr, Literal, Protocol, overload
  from typing_extensions import Self, TypeAlias
-
- if sys.version_info >= (3, 9):
-@@ -107,7 +107,7 @@ def fileno() -> int: ...
+ 
+ __all__ = [
+@@ -104,7 +104,7 @@ def fileno() -> int: ...
  def isfirstline() -> bool: ...
  def isstdin() -> bool: ...
-
+ 
 -class FileInput(Generic[AnyStr]):
 +class FileInput(Iterator[AnyStr]):
      if sys.version_info >= (3, 10):
          # encoding and errors are added
          @overload
 diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi
-index 55b0814ac..675533d44 100644
+index d0085dd72..7d05b1318 100644
 --- a/mypy/typeshed/stdlib/itertools.pyi
 +++ b/mypy/typeshed/stdlib/itertools.pyi
-@@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object]
-
+@@ -27,7 +27,7 @@ _Predicate: TypeAlias = Callable[[_T], object]
+ 
  # Technically count can take anything that implements a number protocol and has an add method
  # but we can't enforce the add method
 -class count(Generic[_N]):
@@ -143,144 +143,144 @@ index 55b0814ac..675533d44 100644
      @overload
      def __new__(cls) -> count[int]: ...
      @overload
-@@ -39,12 +39,12 @@ class count(Generic[_N]):
+@@ -37,12 +37,12 @@ class count(Generic[_N]):
      def __next__(self) -> _N: ...
      def __iter__(self) -> Self: ...
-
+ 
 -class cycle(Generic[_T]):
 +class cycle(Iterator[_T]):
      def __new__(cls, iterable: Iterable[_T], /) -> Self: ...
      def __next__(self) -> _T: ...
      def __iter__(self) -> Self: ...
-
+ 
 -class repeat(Generic[_T]):
 +class repeat(Iterator[_T]):
      @overload
      def __new__(cls, object: _T) -> Self: ...
      @overload
-@@ -53,7 +53,7 @@ class repeat(Generic[_T]):
+@@ -51,7 +51,7 @@ class repeat(Generic[_T]):
      def __iter__(self) -> Self: ...
      def __length_hint__(self) -> int: ...
-
+ 
 -class accumulate(Generic[_T]):
 +class accumulate(Iterator[_T]):
      @overload
      def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ...
      @overload
-@@ -61,7 +61,7 @@ class accumulate(Generic[_T]):
+@@ -59,7 +59,7 @@ class accumulate(Generic[_T]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
 -class chain(Generic[_T]):
 +class chain(Iterator[_T]):
      def __new__(cls, *iterables: Iterable[_T]) -> Self: ...
      def __next__(self) -> _T: ...
      def __iter__(self) -> Self: ...
-@@ -71,22 +71,22 @@ class chain(Generic[_T]):
-     if sys.version_info >= (3, 9):
-         def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
-
+@@ -68,22 +68,22 @@ class chain(Generic[_T]):
+     def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ...
+     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+ 
 -class compress(Generic[_T]):
 +class compress(Iterator[_T]):
      def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
 -class dropwhile(Generic[_T]):
 +class dropwhile(Iterator[_T]):
      def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
 -class filterfalse(Generic[_T]):
 +class filterfalse(Iterator[_T]):
      def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
 -class groupby(Generic[_T_co, _S_co]):
 +class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ...
      @overload
-@@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]):
+@@ -91,7 +91,7 @@ class groupby(Generic[_T_co, _S_co]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ...
-
+ 
 -class islice(Generic[_T]):
 +class islice(Iterator[_T]):
      @overload
      def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ...
      @overload
-@@ -102,19 +102,19 @@ class islice(Generic[_T]):
+@@ -99,19 +99,19 @@ class islice(Generic[_T]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
 -class starmap(Generic[_T_co]):
 +class starmap(Iterator[_T_co]):
      def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
-
+ 
 -class takewhile(Generic[_T]):
 +class takewhile(Iterator[_T]):
      def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
-
+ 
  def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ...
-
+ 
 -class zip_longest(Generic[_T_co]):
 +class zip_longest(Iterator[_T_co]):
      # one iterable (fillvalue doesn't matter)
      @overload
      def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ...
-@@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]):
+@@ -189,7 +189,7 @@ class zip_longest(Generic[_T_co]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
-
+ 
 -class product(Generic[_T_co]):
 +class product(Iterator[_T_co]):
      @overload
      def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ...
      @overload
-@@ -277,7 +277,7 @@ class product(Generic[_T_co]):
+@@ -274,7 +274,7 @@ class product(Generic[_T_co]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
-
+ 
 -class permutations(Generic[_T_co]):
 +class permutations(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ...
      @overload
-@@ -291,7 +291,7 @@ class permutations(Generic[_T_co]):
+@@ -288,7 +288,7 @@ class permutations(Generic[_T_co]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
-
+ 
 -class combinations(Generic[_T_co]):
 +class combinations(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ...
      @overload
-@@ -305,7 +305,7 @@ class combinations(Generic[_T_co]):
+@@ -302,7 +302,7 @@ class combinations(Generic[_T_co]):
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
-
+ 
 -class combinations_with_replacement(Generic[_T_co]):
 +class combinations_with_replacement(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ...
      @overload
-@@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]):
+@@ -317,13 +317,13 @@ class combinations_with_replacement(Generic[_T_co]):
      def __next__(self) -> _T_co: ...
-
+ 
  if sys.version_info >= (3, 10):
 -    class pairwise(Generic[_T_co]):
 +    class pairwise(Iterator[_T_co]):
          def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ...
          def __iter__(self) -> Self: ...
          def __next__(self) -> _T_co: ...
-
+ 
  if sys.version_info >= (3, 12):
 -    class batched(Generic[_T_co]):
 +    class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]):
@@ -288,37 +288,37 @@ index 55b0814ac..675533d44 100644
              def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ...
          else:
 diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi
-index 2937d45e3..93197e5d4 100644
+index b79f9e773..f276372d0 100644
 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi
 +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi
-@@ -1,5 +1,5 @@
- import sys
+@@ -1,4 +1,4 @@
 -from collections.abc import Callable, Iterable, Mapping
 +from collections.abc import Callable, Iterable, Iterator, Mapping
  from multiprocessing.context import DefaultContext, Process
- from types import TracebackType
+ from types import GenericAlias, TracebackType
  from typing import Any, Final, Generic, TypeVar
-@@ -37,7 +37,7 @@ class MapResult(ApplyResult[list[_T]]):
+@@ -32,7 +32,7 @@ class MapResult(ApplyResult[list[_T]]):
          error_callback: Callable[[BaseException], object] | None,
      ) -> None: ...
-
+ 
 -class IMapIterator(Generic[_T]):
 +class IMapIterator(Iterator[_T]):
      def __init__(self, pool: Pool) -> None: ...
      def __iter__(self) -> Self: ...
      def next(self, timeout: float | None = None) -> _T: ...
 diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-index b83516b4d..724bc3166 100644
+index 5d3c2330b..ab783dbde 100644
 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
 +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-@@ -397,7 +397,7 @@ class Connection:
+@@ -399,7 +399,7 @@ class Connection:
          self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, /
      ) -> Literal[False]: ...
-
+ 
 -class Cursor:
 +class Cursor(Iterator[Any]):
      arraysize: int
      @property
      def connection(self) -> Connection: ...
---
-2.47.1
+-- 
+2.49.0
+
diff --git a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch
index 331628af1424..559e32569f2b 100644
--- a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch
+++ b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch
@@ -1,4 +1,4 @@
-From 58c6a6ab863c1c38e95ccafaf13792ed9c00e499 Mon Sep 17 00:00:00 2001
+From 16b0b50ec77e470f24145071acde5274a1de53a0 Mon Sep 17 00:00:00 2001
 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
 Date: Sat, 29 Oct 2022 12:47:21 -0700
 Subject: [PATCH] Revert sum literal integer change (#13961)
@@ -19,10 +19,10 @@ within mypy, I might pursue upstreaming this in typeshed.
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index ea9f8c894..a6065cc67 100644
+index 900c4c93f..d874edd8f 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
-@@ -1653,7 +1653,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit
+@@ -1782,7 +1782,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit
  # without creating many false-positive errors (see #7578).
  # Instead, we special-case the most common examples of this: bool and literal integers.
  @overload
@@ -32,5 +32,5 @@ index ea9f8c894..a6065cc67 100644
  def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ...
  @overload
 -- 
-2.46.0
+2.49.0
 
diff --git a/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch
index 27066bf3c25b..c16f5ebaa92e 100644
--- a/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch
+++ b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch
@@ -1,4 +1,4 @@
-From 61a490091d7c941780919660dc4fdfa88ae6474a Mon Sep 17 00:00:00 2001
+From 85c0cfb55c6211c2a47c3f45d2ff28fa76f8204b Mon Sep 17 00:00:00 2001
 From: AlexWaygood 
 Date: Mon, 1 May 2023 20:34:55 +0100
 Subject: [PATCH] Revert typeshed ctypes change Since the plugin provides
@@ -11,10 +11,10 @@ Subject: [PATCH] Revert typeshed ctypes change Since the plugin provides
  1 file changed, 1 insertion(+), 5 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi
-index 60bbc51d9..cf9cb81a4 100644
+index 944685646..dc8c7b2ca 100644
 --- a/mypy/typeshed/stdlib/_ctypes.pyi
 +++ b/mypy/typeshed/stdlib/_ctypes.pyi
-@@ -169,11 +169,7 @@ class Array(_CData, Generic[_CT]):
+@@ -289,11 +289,7 @@ class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
      def _type_(self) -> type[_CT]: ...
      @_type_.setter
      def _type_(self, value: type[_CT]) -> None: ...
@@ -25,8 +25,8 @@ index 60bbc51d9..cf9cb81a4 100644
 -    def raw(self, value: ReadableBuffer) -> None: ...
 +    raw: bytes  # Note: only available if _CT == c_char
      value: Any  # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
-     # TODO These methods cannot be annotated correctly at the moment.
+     # TODO: These methods cannot be annotated correctly at the moment.
      # All of these "Any"s stand for the array's element type, but it's not possible to use _CT
 -- 
-2.39.3 (Apple Git-146)
+2.49.0
 
diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS
index 3c6898dc1a77..1ecd8af64559 100644
--- a/mypy/typeshed/stdlib/VERSIONS
+++ b/mypy/typeshed/stdlib/VERSIONS
@@ -28,7 +28,7 @@ _bz2: 3.3-
 _codecs: 3.0-
 _collections_abc: 3.3-
 _compat_pickle: 3.1-
-_compression: 3.5-
+_compression: 3.5-3.13
 _contextvars: 3.7-
 _csv: 3.0-
 _ctypes: 3.0-
@@ -36,8 +36,6 @@ _curses: 3.0-
 _curses_panel: 3.0-
 _dbm: 3.0-
 _decimal: 3.3-
-_dummy_thread: 3.0-3.8
-_dummy_threading: 3.0-3.8
 _frozen_importlib: 3.0-
 _frozen_importlib_external: 3.5-
 _gdbm: 3.0-
@@ -80,6 +78,7 @@ _weakrefset: 3.0-
 _winapi: 3.3-
 abc: 3.0-
 aifc: 3.0-3.12
+annotationlib: 3.14-
 antigravity: 3.0-
 argparse: 3.0-
 array: 3.0-
@@ -88,6 +87,7 @@ asynchat: 3.0-3.11
 asyncio: 3.4-
 asyncio.exceptions: 3.8-
 asyncio.format_helpers: 3.7-
+asyncio.graph: 3.14-
 asyncio.mixins: 3.10-
 asyncio.runners: 3.7-
 asyncio.staggered: 3.8-
@@ -119,7 +119,9 @@ collections: 3.0-
 collections.abc: 3.3-
 colorsys: 3.0-
 compileall: 3.0-
+compression: 3.14-
 concurrent: 3.2-
+concurrent.futures.interpreter: 3.14-
 configparser: 3.0-
 contextlib: 3.0-
 contextvars: 3.7-
@@ -140,7 +142,6 @@ distutils: 3.0-3.11
 distutils.command.bdist_msi: 3.0-3.10
 distutils.command.bdist_wininst: 3.0-3.9
 doctest: 3.0-
-dummy_threading: 3.0-3.8
 email: 3.0-
 encodings: 3.0-
 encodings.cp1125: 3.4-
@@ -148,7 +149,6 @@ encodings.cp273: 3.4-
 encodings.cp858: 3.2-
 encodings.koi8_t: 3.5-
 encodings.kz1048: 3.5-
-encodings.mac_centeuro: 3.0-3.8
 ensurepip: 3.0-
 enum: 3.4-
 errno: 3.0-
@@ -230,6 +230,7 @@ os: 3.0-
 ossaudiodev: 3.0-3.12
 parser: 3.0-3.9
 pathlib: 3.4-
+pathlib.types: 3.14-
 pdb: 3.0-
 pickle: 3.0-
 pickletools: 3.0-
@@ -282,6 +283,7 @@ ssl: 3.0-
 stat: 3.0-
 statistics: 3.4-
 string: 3.0-
+string.templatelib: 3.14-
 stringprep: 3.0-
 struct: 3.0-
 subprocess: 3.0-
diff --git a/mypy/typeshed/stdlib/__main__.pyi b/mypy/typeshed/stdlib/__main__.pyi
index e27843e53382..5b0f74feb261 100644
--- a/mypy/typeshed/stdlib/__main__.pyi
+++ b/mypy/typeshed/stdlib/__main__.pyi
@@ -1,3 +1 @@
-from typing import Any
-
-def __getattr__(name: str) -> Any: ...
+def __getattr__(name: str): ...  # incomplete module
diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi
index 8dc1bcbea32c..00c6b357f7d8 100644
--- a/mypy/typeshed/stdlib/_ast.pyi
+++ b/mypy/typeshed/stdlib/_ast.pyi
@@ -111,13 +111,20 @@ from ast import (
 from typing import Literal
 
 if sys.version_info >= (3, 12):
-    from ast import ParamSpec as ParamSpec, TypeVar as TypeVar, TypeVarTuple as TypeVarTuple, type_param as type_param
+    from ast import (
+        ParamSpec as ParamSpec,
+        TypeAlias as TypeAlias,
+        TypeVar as TypeVar,
+        TypeVarTuple as TypeVarTuple,
+        type_param as type_param,
+    )
 
 if sys.version_info >= (3, 11):
     from ast import TryStar as TryStar
 
 if sys.version_info >= (3, 10):
     from ast import (
+        Match as Match,
         MatchAs as MatchAs,
         MatchClass as MatchClass,
         MatchMapping as MatchMapping,
@@ -130,17 +137,6 @@ if sys.version_info >= (3, 10):
         pattern as pattern,
     )
 
-if sys.version_info < (3, 9):
-    from ast import (
-        AugLoad as AugLoad,
-        AugStore as AugStore,
-        ExtSlice as ExtSlice,
-        Index as Index,
-        Param as Param,
-        Suite as Suite,
-        slice as slice,
-    )
-
 PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192]
 PyCF_ONLY_AST: Literal[1024]
 PyCF_TYPE_COMMENTS: Literal[4096]
diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
index 1397e579d53b..19a2d12d878c 100644
--- a/mypy/typeshed/stdlib/_asyncio.pyi
+++ b/mypy/typeshed/stdlib/_asyncio.pyi
@@ -2,13 +2,10 @@ import sys
 from asyncio.events import AbstractEventLoop
 from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable
 from contextvars import Context
-from types import FrameType
+from types import FrameType, GenericAlias
 from typing import Any, Literal, TextIO, TypeVar
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 _T_co = TypeVar("_T_co", covariant=True)
 _TaskYieldType: TypeAlias = Future[object] | None
@@ -29,11 +26,7 @@ class Future(Awaitable[_T], Iterable[_T]):
     @property
     def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ...
     def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ...
-    if sys.version_info >= (3, 9):
-        def cancel(self, msg: Any | None = None) -> bool: ...
-    else:
-        def cancel(self) -> bool: ...
-
+    def cancel(self, msg: Any | None = None) -> bool: ...
     def cancelled(self) -> bool: ...
     def done(self) -> bool: ...
     def result(self) -> _T: ...
@@ -45,15 +38,12 @@ class Future(Awaitable[_T], Iterable[_T]):
     def __await__(self) -> Generator[Any, None, _T]: ...
     @property
     def _loop(self) -> AbstractEventLoop: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 if sys.version_info >= (3, 12):
     _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co]
-elif sys.version_info >= (3, 9):
-    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co]
 else:
-    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co]
+    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co]
 
 # mypy and pyright complain that a subclass of an invariant class shouldn't be covariant.
 # While this is true in general, here it's sort-of okay to have a covariant subclass,
@@ -99,13 +89,8 @@ class Task(Future[_T_co]):  # type: ignore[type-var]  # pyright: ignore[reportIn
     if sys.version_info >= (3, 11):
         def cancelling(self) -> int: ...
         def uncancel(self) -> int: ...
-    if sys.version_info < (3, 9):
-        @classmethod
-        def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ...
-        @classmethod
-        def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 def get_event_loop() -> AbstractEventLoop: ...
 def get_running_loop() -> AbstractEventLoop: ...
@@ -118,3 +103,7 @@ def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ...
 
 if sys.version_info >= (3, 12):
     def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ...
+
+if sys.version_info >= (3, 14):
+    def future_discard_from_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ...
+    def future_add_to_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ...
diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi
index 3d17cb59c79b..d578df55c2fa 100644
--- a/mypy/typeshed/stdlib/_blake2.pyi
+++ b/mypy/typeshed/stdlib/_blake2.pyi
@@ -1,4 +1,3 @@
-import sys
 from _typeshed import ReadableBuffer
 from typing import ClassVar, final
 from typing_extensions import Self
@@ -21,44 +20,24 @@ class blake2b:
     block_size: int
     digest_size: int
     name: str
-    if sys.version_info >= (3, 9):
-        def __new__(
-            cls,
-            data: ReadableBuffer = b"",
-            /,
-            *,
-            digest_size: int = 64,
-            key: ReadableBuffer = b"",
-            salt: ReadableBuffer = b"",
-            person: ReadableBuffer = b"",
-            fanout: int = 1,
-            depth: int = 1,
-            leaf_size: int = 0,
-            node_offset: int = 0,
-            node_depth: int = 0,
-            inner_size: int = 0,
-            last_node: bool = False,
-            usedforsecurity: bool = True,
-        ) -> Self: ...
-    else:
-        def __new__(
-            cls,
-            data: ReadableBuffer = b"",
-            /,
-            *,
-            digest_size: int = 64,
-            key: ReadableBuffer = b"",
-            salt: ReadableBuffer = b"",
-            person: ReadableBuffer = b"",
-            fanout: int = 1,
-            depth: int = 1,
-            leaf_size: int = 0,
-            node_offset: int = 0,
-            node_depth: int = 0,
-            inner_size: int = 0,
-            last_node: bool = False,
-        ) -> Self: ...
-
+    def __new__(
+        cls,
+        data: ReadableBuffer = b"",
+        /,
+        *,
+        digest_size: int = 64,
+        key: ReadableBuffer = b"",
+        salt: ReadableBuffer = b"",
+        person: ReadableBuffer = b"",
+        fanout: int = 1,
+        depth: int = 1,
+        leaf_size: int = 0,
+        node_offset: int = 0,
+        node_depth: int = 0,
+        inner_size: int = 0,
+        last_node: bool = False,
+        usedforsecurity: bool = True,
+    ) -> Self: ...
     def copy(self) -> Self: ...
     def digest(self) -> bytes: ...
     def hexdigest(self) -> str: ...
@@ -73,44 +52,24 @@ class blake2s:
     block_size: int
     digest_size: int
     name: str
-    if sys.version_info >= (3, 9):
-        def __new__(
-            cls,
-            data: ReadableBuffer = b"",
-            /,
-            *,
-            digest_size: int = 32,
-            key: ReadableBuffer = b"",
-            salt: ReadableBuffer = b"",
-            person: ReadableBuffer = b"",
-            fanout: int = 1,
-            depth: int = 1,
-            leaf_size: int = 0,
-            node_offset: int = 0,
-            node_depth: int = 0,
-            inner_size: int = 0,
-            last_node: bool = False,
-            usedforsecurity: bool = True,
-        ) -> Self: ...
-    else:
-        def __new__(
-            cls,
-            data: ReadableBuffer = b"",
-            /,
-            *,
-            digest_size: int = 32,
-            key: ReadableBuffer = b"",
-            salt: ReadableBuffer = b"",
-            person: ReadableBuffer = b"",
-            fanout: int = 1,
-            depth: int = 1,
-            leaf_size: int = 0,
-            node_offset: int = 0,
-            node_depth: int = 0,
-            inner_size: int = 0,
-            last_node: bool = False,
-        ) -> Self: ...
-
+    def __new__(
+        cls,
+        data: ReadableBuffer = b"",
+        /,
+        *,
+        digest_size: int = 32,
+        key: ReadableBuffer = b"",
+        salt: ReadableBuffer = b"",
+        person: ReadableBuffer = b"",
+        fanout: int = 1,
+        depth: int = 1,
+        leaf_size: int = 0,
+        node_offset: int = 0,
+        node_depth: int = 0,
+        inner_size: int = 0,
+        last_node: bool = False,
+        usedforsecurity: bool = True,
+    ) -> Self: ...
     def copy(self) -> Self: ...
     def digest(self) -> bytes: ...
     def hexdigest(self) -> str: ...
diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi
index 11c5d58a855b..89f97edb9ba8 100644
--- a/mypy/typeshed/stdlib/_codecs.pyi
+++ b/mypy/typeshed/stdlib/_codecs.pyi
@@ -81,26 +81,12 @@ def escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> t
 def escape_encode(data: bytes, errors: str | None = None, /) -> tuple[bytes, int]: ...
 def latin_1_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
 def latin_1_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
-
-if sys.version_info >= (3, 9):
-    def raw_unicode_escape_decode(
-        data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /
-    ) -> tuple[str, int]: ...
-
-else:
-    def raw_unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
-
+def raw_unicode_escape_decode(
+    data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /
+) -> tuple[str, int]: ...
 def raw_unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
 def readbuffer_encode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[bytes, int]: ...
-
-if sys.version_info >= (3, 9):
-    def unicode_escape_decode(
-        data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /
-    ) -> tuple[str, int]: ...
-
-else:
-    def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
-
+def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ...
 def unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
 def utf_16_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
 def utf_16_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi
index 8bac0ce1dca3..b099bdd98f3c 100644
--- a/mypy/typeshed/stdlib/_collections_abc.pyi
+++ b/mypy/typeshed/stdlib/_collections_abc.pyi
@@ -1,7 +1,7 @@
 import sys
 from abc import abstractmethod
 from types import MappingProxyType
-from typing import (  # noqa: Y022,Y038
+from typing import (  # noqa: Y022,Y038,UP035
     AbstractSet as Set,
     AsyncGenerator as AsyncGenerator,
     AsyncIterable as AsyncIterable,
@@ -61,7 +61,7 @@ __all__ = [
     "MutableSequence",
 ]
 if sys.version_info < (3, 14):
-    from typing import ByteString as ByteString  # noqa: Y057
+    from typing import ByteString as ByteString  # noqa: Y057,UP035
 
     __all__ += ["ByteString"]
 
diff --git a/mypy/typeshed/stdlib/_compression.pyi b/mypy/typeshed/stdlib/_compression.pyi
index a41a8142cc3a..80d38b4db824 100644
--- a/mypy/typeshed/stdlib/_compression.pyi
+++ b/mypy/typeshed/stdlib/_compression.pyi
@@ -1,4 +1,6 @@
-from _typeshed import WriteableBuffer
+# _compression is replaced by compression._common._streams on Python 3.14+ (PEP-784)
+
+from _typeshed import Incomplete, WriteableBuffer
 from collections.abc import Callable
 from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
 from typing import Any, Protocol
@@ -16,9 +18,9 @@ class DecompressReader(RawIOBase):
     def __init__(
         self,
         fp: _Reader,
-        decomp_factory: Callable[..., object],
+        decomp_factory: Callable[..., Incomplete],
         trailing_error: type[Exception] | tuple[type[Exception], ...] = (),
-        **decomp_args: Any,
+        **decomp_args: Any,  # These are passed to decomp_factory.
     ) -> None: ...
     def readinto(self, b: WriteableBuffer) -> int: ...
     def read(self, size: int = -1) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi
index c7d0814b3cb4..e2e2e4df9d08 100644
--- a/mypy/typeshed/stdlib/_contextvars.pyi
+++ b/mypy/typeshed/stdlib/_contextvars.pyi
@@ -1,11 +1,9 @@
 import sys
 from collections.abc import Callable, Iterator, Mapping
+from types import GenericAlias, TracebackType
 from typing import Any, ClassVar, Generic, TypeVar, final, overload
 from typing_extensions import ParamSpec, Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 _D = TypeVar("_D")
 _P = ParamSpec("_P")
@@ -27,8 +25,7 @@ class ContextVar(Generic[_T]):
     def get(self, default: _D, /) -> _D | _T: ...
     def set(self, value: _T, /) -> Token[_T]: ...
     def reset(self, token: Token[_T], /) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @final
 class Token(Generic[_T]):
@@ -38,8 +35,12 @@ class Token(Generic[_T]):
     def old_value(self) -> Any: ...  # returns either _T or MISSING, but that's hard to express
     MISSING: ClassVar[object]
     __hash__: ClassVar[None]  # type: ignore[assignment]
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    if sys.version_info >= (3, 14):
+        def __enter__(self) -> Self: ...
+        def __exit__(
+            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
+        ) -> None: ...
 
 def copy_context() -> Context: ...
 
diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi
index aa9fc538417e..ecea4878907c 100644
--- a/mypy/typeshed/stdlib/_csv.pyi
+++ b/mypy/typeshed/stdlib/_csv.pyi
@@ -2,7 +2,7 @@ import csv
 import sys
 from _typeshed import SupportsWrite
 from collections.abc import Iterable
-from typing import Any, Final, type_check_only
+from typing import Any, Final, Literal, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __version__: Final[str]
@@ -15,9 +15,10 @@ if sys.version_info >= (3, 12):
     QUOTE_STRINGS: Final = 4
     QUOTE_NOTNULL: Final = 5
 
-# Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC`
-# However, using literals in situations like these can cause false-positives (see #7258)
-_QuotingType: TypeAlias = int
+if sys.version_info >= (3, 12):
+    _QuotingType: TypeAlias = Literal[0, 1, 2, 3, 4, 5]
+else:
+    _QuotingType: TypeAlias = Literal[0, 1, 2, 3]
 
 class Error(Exception): ...
 
diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi
index 2977bf5afa94..dc8c7b2ca945 100644
--- a/mypy/typeshed/stdlib/_ctypes.pyi
+++ b/mypy/typeshed/stdlib/_ctypes.pyi
@@ -4,12 +4,10 @@ from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from abc import abstractmethod
 from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
 from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
+from types import GenericAlias
 from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 _CT = TypeVar("_CT", bound=_CData)
 
@@ -133,18 +131,23 @@ class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType):
     def __getitem__(self, key: slice, /) -> list[Any]: ...
     def __setitem__(self, key: int, value: Any, /) -> None: ...
 
-@overload
-def POINTER(type: None, /) -> type[c_void_p]: ...
-@overload
-def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
-def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
+if sys.version_info < (3, 14):
+    @overload
+    def POINTER(type: None, /) -> type[c_void_p]: ...
+    @overload
+    def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
+    def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
 
 # This class is not exposed. It calls itself _ctypes.CArgObject.
 @final
 @type_check_only
 class _CArgObject: ...
 
-def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ...
+if sys.version_info >= (3, 14):
+    def byref(obj: _CData | _CDataType, offset: int = 0, /) -> _CArgObject: ...
+
+else:
+    def byref(obj: _CData | _CDataType, offset: int = 0) -> _CArgObject: ...
 
 _ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType]
 _PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
@@ -288,7 +291,7 @@ class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
     def _type_(self, value: type[_CT]) -> None: ...
     raw: bytes  # Note: only available if _CT == c_char
     value: Any  # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
-    # TODO These methods cannot be annotated correctly at the moment.
+    # TODO: These methods cannot be annotated correctly at the moment.
     # All of these "Any"s stand for the array's element type, but it's not possible to use _CT
     # here, because of a special feature of ctypes.
     # By default, when accessing an element of an Array[_CT], the returned object has type _CT.
@@ -313,8 +316,7 @@ class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
     # Can't inherit from Sized because the metaclass conflict between
     # Sized and _CData prevents using _CDataMeta.
     def __len__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 def addressof(obj: _CData | _CDataType, /) -> int: ...
 def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi
index 52c5185727e7..d7820c72c090 100644
--- a/mypy/typeshed/stdlib/_curses.pyi
+++ b/mypy/typeshed/stdlib/_curses.pyi
@@ -95,13 +95,14 @@ BUTTON4_DOUBLE_CLICKED: int
 BUTTON4_PRESSED: int
 BUTTON4_RELEASED: int
 BUTTON4_TRIPLE_CLICKED: int
-# Darwin ncurses doesn't provide BUTTON5_* constants
-if sys.version_info >= (3, 10) and sys.platform != "darwin":
-    BUTTON5_PRESSED: int
-    BUTTON5_RELEASED: int
-    BUTTON5_CLICKED: int
-    BUTTON5_DOUBLE_CLICKED: int
-    BUTTON5_TRIPLE_CLICKED: int
+# Darwin ncurses doesn't provide BUTTON5_* constants prior to 3.12.10 and 3.13.3
+if sys.version_info >= (3, 10):
+    if sys.version_info >= (3, 12) or sys.platform != "darwin":
+        BUTTON5_PRESSED: int
+        BUTTON5_RELEASED: int
+        BUTTON5_CLICKED: int
+        BUTTON5_DOUBLE_CLICKED: int
+        BUTTON5_TRIPLE_CLICKED: int
 BUTTON_ALT: int
 BUTTON_CTRL: int
 BUTTON_SHIFT: int
@@ -292,11 +293,8 @@ def erasechar() -> bytes: ...
 def filter() -> None: ...
 def flash() -> None: ...
 def flushinp() -> None: ...
-
-if sys.version_info >= (3, 9):
-    def get_escdelay() -> int: ...
-    def get_tabsize() -> int: ...
-
+def get_escdelay() -> int: ...
+def get_tabsize() -> int: ...
 def getmouse() -> tuple[int, int, int, int, int]: ...
 def getsyx() -> tuple[int, int]: ...
 def getwin(file: SupportsRead[bytes], /) -> window: ...
@@ -341,11 +339,8 @@ def resetty() -> None: ...
 def resize_term(nlines: int, ncols: int, /) -> None: ...
 def resizeterm(nlines: int, ncols: int, /) -> None: ...
 def savetty() -> None: ...
-
-if sys.version_info >= (3, 9):
-    def set_escdelay(ms: int, /) -> None: ...
-    def set_tabsize(size: int, /) -> None: ...
-
+def set_escdelay(ms: int, /) -> None: ...
+def set_tabsize(size: int, /) -> None: ...
 def setsyx(y: int, x: int, /) -> None: ...
 def setupterm(term: str | None = None, fd: int = -1) -> None: ...
 def start_color() -> None: ...
diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi
index 06c0197dcf07..fd0e6e6ac091 100644
--- a/mypy/typeshed/stdlib/_decimal.pyi
+++ b/mypy/typeshed/stdlib/_decimal.pyi
@@ -41,6 +41,8 @@ MAX_EMAX: Final[int]
 MAX_PREC: Final[int]
 MIN_EMIN: Final[int]
 MIN_ETINY: Final[int]
+if sys.version_info >= (3, 14):
+    IEEE_CONTEXT_MAX_BITS: Final[int]
 
 def setcontext(context: Context, /) -> None: ...
 def getcontext() -> Context: ...
@@ -62,6 +64,9 @@ if sys.version_info >= (3, 11):
 else:
     def localcontext(ctx: Context | None = None) -> _ContextManager: ...
 
+if sys.version_info >= (3, 14):
+    def IEEEContext(bits: int, /) -> Context: ...
+
 DefaultContext: Context
 BasicContext: Context
 ExtendedContext: Context
diff --git a/mypy/typeshed/stdlib/_dummy_thread.pyi b/mypy/typeshed/stdlib/_dummy_thread.pyi
deleted file mode 100644
index 1182e53c66c3..000000000000
--- a/mypy/typeshed/stdlib/_dummy_thread.pyi
+++ /dev/null
@@ -1,33 +0,0 @@
-from collections.abc import Callable
-from types import TracebackType
-from typing import Any, NoReturn, overload
-from typing_extensions import TypeVarTuple, Unpack
-
-__all__ = ["error", "start_new_thread", "exit", "get_ident", "allocate_lock", "interrupt_main", "LockType", "RLock"]
-
-_Ts = TypeVarTuple("_Ts")
-
-TIMEOUT_MAX: int
-error = RuntimeError
-
-@overload
-def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> None: ...
-@overload
-def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> None: ...
-def exit() -> NoReturn: ...
-def get_ident() -> int: ...
-def allocate_lock() -> LockType: ...
-def stack_size(size: int | None = None) -> int: ...
-
-class LockType:
-    locked_status: bool
-    def acquire(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ...
-    def __enter__(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ...
-    def __exit__(self, typ: type[BaseException] | None, val: BaseException | None, tb: TracebackType | None) -> None: ...
-    def release(self) -> bool: ...
-    def locked(self) -> bool: ...
-
-class RLock(LockType):
-    def release(self) -> None: ...  # type: ignore[override]
-
-def interrupt_main() -> None: ...
diff --git a/mypy/typeshed/stdlib/_dummy_threading.pyi b/mypy/typeshed/stdlib/_dummy_threading.pyi
deleted file mode 100644
index 1b66fb414d7a..000000000000
--- a/mypy/typeshed/stdlib/_dummy_threading.pyi
+++ /dev/null
@@ -1,56 +0,0 @@
-from _threading_local import local as local
-from _typeshed import ProfileFunction, TraceFunction
-from threading import (
-    TIMEOUT_MAX as TIMEOUT_MAX,
-    Barrier as Barrier,
-    BoundedSemaphore as BoundedSemaphore,
-    BrokenBarrierError as BrokenBarrierError,
-    Condition as Condition,
-    Event as Event,
-    ExceptHookArgs as ExceptHookArgs,
-    Lock as Lock,
-    RLock as RLock,
-    Semaphore as Semaphore,
-    Thread as Thread,
-    ThreadError as ThreadError,
-    Timer as Timer,
-    _DummyThread as _DummyThread,
-    _RLock as _RLock,
-    excepthook as excepthook,
-)
-
-__all__ = [
-    "get_ident",
-    "active_count",
-    "Condition",
-    "current_thread",
-    "enumerate",
-    "main_thread",
-    "TIMEOUT_MAX",
-    "Event",
-    "Lock",
-    "RLock",
-    "Semaphore",
-    "BoundedSemaphore",
-    "Thread",
-    "Barrier",
-    "BrokenBarrierError",
-    "Timer",
-    "ThreadError",
-    "setprofile",
-    "settrace",
-    "local",
-    "stack_size",
-    "ExceptHookArgs",
-    "excepthook",
-]
-
-def active_count() -> int: ...
-def current_thread() -> Thread: ...
-def currentThread() -> Thread: ...
-def get_ident() -> int: ...
-def enumerate() -> list[Thread]: ...
-def main_thread() -> Thread: ...
-def settrace(func: TraceFunction) -> None: ...
-def setprofile(func: ProfileFunction | None) -> None: ...
-def stack_size(size: int | None = None) -> int: ...
diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
index 386cf20808e4..edad50a8d858 100644
--- a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
+++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
@@ -36,7 +36,10 @@ def spec_from_file_location(
     loader: LoaderProtocol | None = None,
     submodule_search_locations: list[str] | None = ...,
 ) -> importlib.machinery.ModuleSpec | None: ...
-
+@deprecated(
+    "Deprecated as of Python 3.6: Use site configuration instead. "
+    "Future versions of Python may not enable this finder by default."
+)
 class WindowsRegistryFinder(importlib.abc.MetaPathFinder):
     if sys.version_info < (3, 12):
         @classmethod
@@ -118,6 +121,13 @@ class FileLoader:
 class SourceFileLoader(importlib.abc.FileLoader, FileLoader, importlib.abc.SourceLoader, SourceLoader):  # type: ignore[misc]  # incompatible method arguments in base classes
     def set_data(self, path: str, data: ReadableBuffer, *, _mode: int = 0o666) -> None: ...
     def path_stats(self, path: str) -> Mapping[str, Any]: ...
+    def source_to_code(  # type: ignore[override]  # incompatible with InspectLoader.source_to_code
+        self,
+        data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive,
+        path: ReadableBuffer | StrPath,
+        *,
+        _optimize: int = -1,
+    ) -> types.CodeType: ...
 
 class SourcelessFileLoader(importlib.abc.FileLoader, FileLoader, _LoaderBasics):
     def get_code(self, fullname: str) -> types.CodeType | None: ...
diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi
index e91f2cdb331c..746b1657e2db 100644
--- a/mypy/typeshed/stdlib/_hashlib.pyi
+++ b/mypy/typeshed/stdlib/_hashlib.pyi
@@ -37,53 +37,42 @@ class HASH:
 if sys.version_info >= (3, 10):
     class UnsupportedDigestmodError(ValueError): ...
 
-if sys.version_info >= (3, 9):
-    class HASHXOF(HASH):
-        def digest(self, length: int) -> bytes: ...  # type: ignore[override]
-        def hexdigest(self, length: int) -> str: ...  # type: ignore[override]
+class HASHXOF(HASH):
+    def digest(self, length: int) -> bytes: ...  # type: ignore[override]
+    def hexdigest(self, length: int) -> str: ...  # type: ignore[override]
 
-    @final
-    class HMAC:
-        @property
-        def digest_size(self) -> int: ...
-        @property
-        def block_size(self) -> int: ...
-        @property
-        def name(self) -> str: ...
-        def copy(self) -> Self: ...
-        def digest(self) -> bytes: ...
-        def hexdigest(self) -> str: ...
-        def update(self, msg: ReadableBuffer) -> None: ...
-
-    @overload
-    def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
-    @overload
-    def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
-    def get_fips_mode() -> int: ...
-    def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ...
-    def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-    def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
-    def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
-
-else:
-    def new(name: str, string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_md5(string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_sha1(string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_sha224(string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_sha256(string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_sha384(string: ReadableBuffer = b"") -> HASH: ...
-    def openssl_sha512(string: ReadableBuffer = b"") -> HASH: ...
+@final
+class HMAC:
+    @property
+    def digest_size(self) -> int: ...
+    @property
+    def block_size(self) -> int: ...
+    @property
+    def name(self) -> str: ...
+    def copy(self) -> Self: ...
+    def digest(self) -> bytes: ...
+    def hexdigest(self) -> str: ...
+    def update(self, msg: ReadableBuffer) -> None: ...
 
+@overload
+def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
+@overload
+def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
+def get_fips_mode() -> int: ...
+def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ...
+def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
+def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
 def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ...
 def pbkdf2_hmac(
     hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None
diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi
index 54efd3199760..c77d75287c25 100644
--- a/mypy/typeshed/stdlib/_io.pyi
+++ b/mypy/typeshed/stdlib/_io.pyi
@@ -88,9 +88,36 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]
     def readlines(self, size: int | None = None, /) -> list[bytes]: ...
     def seek(self, pos: int, whence: int = 0, /) -> int: ...
 
-class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]  # incompatible definitions of methods in the base classes
-    raw: RawIOBase
-    def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
+class _BufferedReaderStream(Protocol):
+    def read(self, n: int = ..., /) -> bytes: ...
+    # Optional: def readall(self) -> bytes: ...
+    def readinto(self, b: memoryview, /) -> int | None: ...
+    def seek(self, pos: int, whence: int, /) -> int: ...
+    def tell(self) -> int: ...
+    def truncate(self, size: int, /) -> int: ...
+    def flush(self) -> object: ...
+    def close(self) -> object: ...
+    @property
+    def closed(self) -> bool: ...
+    def readable(self) -> bool: ...
+    def seekable(self) -> bool: ...
+
+    # The following methods just pass through to the underlying stream. Since
+    # not all streams support them, they are marked as optional here, and will
+    # raise an AttributeError if called on a stream that does not support them.
+
+    # @property
+    # def name(self) -> Any: ...  # Type is inconsistent between the various I/O types.
+    # @property
+    # def mode(self) -> str: ...
+    # def fileno(self) -> int: ...
+    # def isatty(self) -> bool: ...
+
+_BufferedReaderStreamT = TypeVar("_BufferedReaderStreamT", bound=_BufferedReaderStream, default=_BufferedReaderStream)
+
+class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedReaderStreamT]):  # type: ignore[misc]  # incompatible definitions of methods in the base classes
+    raw: _BufferedReaderStreamT
+    def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 8192) -> None: ...
     def peek(self, size: int = 0, /) -> bytes: ...
     def seek(self, target: int, whence: int = 0, /) -> int: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
@@ -111,8 +138,8 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore
     def peek(self, size: int = 0, /) -> bytes: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
-class BufferedRWPair(BufferedIOBase, _BufferedIOBase):
-    def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
+class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT]):
+    def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
     def peek(self, size: int = 0, /) -> bytes: ...
 
 class _TextIOBase(_IOBase):
@@ -131,8 +158,7 @@ class _TextIOBase(_IOBase):
 @type_check_only
 class _WrappedBuffer(Protocol):
     # "name" is wrapped by TextIOWrapper. Its type is inconsistent between
-    # the various I/O types, see the comments on TextIOWrapper.name and
-    # TextIO.name.
+    # the various I/O types.
     @property
     def name(self) -> Any: ...
     @property
diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi
index 50bbb6bc16cd..8e8afb600efa 100644
--- a/mypy/typeshed/stdlib/_pickle.pyi
+++ b/mypy/typeshed/stdlib/_pickle.pyi
@@ -1,4 +1,3 @@
-import sys
 from _typeshed import ReadableBuffer, SupportsWrite
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from pickle import PickleBuffer as PickleBuffer
@@ -75,10 +74,9 @@ class Pickler:
     def memo(self, value: PicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ...
     def dump(self, obj: Any, /) -> None: ...
     def clear_memo(self) -> None: ...
-    if sys.version_info >= (3, 13):
-        def persistent_id(self, obj: Any, /) -> Any: ...
-    else:
-        persistent_id: Callable[[Any], Any]
+
+    # this method has no default implementation for Python < 3.13
+    def persistent_id(self, obj: Any, /) -> Any: ...
 
 @type_check_only
 class UnpicklerMemoProxy:
@@ -101,7 +99,6 @@ class Unpickler:
     def memo(self, value: UnpicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ...
     def load(self) -> Any: ...
     def find_class(self, module_name: str, global_name: str, /) -> Any: ...
-    if sys.version_info >= (3, 13):
-        def persistent_load(self, pid: Any, /) -> Any: ...
-    else:
-        persistent_load: Callable[[Any], Any]
+
+    # this method has no default implementation for Python < 3.13
+    def persistent_load(self, pid: Any, /) -> Any: ...
diff --git a/mypy/typeshed/stdlib/_pydecimal.pyi b/mypy/typeshed/stdlib/_pydecimal.pyi
index faff626ac0ba..a6723f749da6 100644
--- a/mypy/typeshed/stdlib/_pydecimal.pyi
+++ b/mypy/typeshed/stdlib/_pydecimal.pyi
@@ -1,5 +1,6 @@
 # This is a slight lie, the implementations aren't exactly identical
 # However, in all likelihood, the differences are inconsequential
+import sys
 from _decimal import *
 
 __all__ = [
@@ -41,3 +42,6 @@ __all__ = [
     "HAVE_THREADS",
     "HAVE_CONTEXTVAR",
 ]
+
+if sys.version_info >= (3, 14):
+    __all__ += ["IEEEContext", "IEEE_CONTEXT_MAX_BITS"]
diff --git a/mypy/typeshed/stdlib/_queue.pyi b/mypy/typeshed/stdlib/_queue.pyi
index 0d4caea7442e..f98397b132ab 100644
--- a/mypy/typeshed/stdlib/_queue.pyi
+++ b/mypy/typeshed/stdlib/_queue.pyi
@@ -1,9 +1,6 @@
-import sys
+from types import GenericAlias
 from typing import Any, Generic, TypeVar
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 
 class Empty(Exception): ...
@@ -16,5 +13,4 @@ class SimpleQueue(Generic[_T]):
     def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ...
     def put_nowait(self, item: _T) -> None: ...
     def qsize(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi
index 649728257c1a..06a8a2ba5fa0 100644
--- a/mypy/typeshed/stdlib/_socket.pyi
+++ b/mypy/typeshed/stdlib/_socket.pyi
@@ -78,7 +78,7 @@ if sys.platform == "win32":
     SO_EXCLUSIVEADDRUSE: int
 if sys.platform != "win32":
     SO_REUSEPORT: int
-    if sys.platform != "darwin":
+    if sys.platform != "darwin" or sys.version_info >= (3, 13):
         SO_BINDTODEVICE: int
 
 if sys.platform != "win32" and sys.platform != "darwin":
@@ -192,7 +192,7 @@ if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "lin
     IPPROTO_BIP: int  # Not FreeBSD either
     IPPROTO_MOBILE: int  # Not FreeBSD either
     IPPROTO_VRRP: int  # Not FreeBSD either
-if sys.version_info >= (3, 9) and sys.platform == "linux":
+if sys.platform == "linux":
     # Availability: Linux >= 2.6.20, FreeBSD >= 10.1
     IPPROTO_UDPLITE: int
 if sys.version_info >= (3, 10) and sys.platform == "linux":
@@ -229,6 +229,28 @@ if sys.platform != "win32":
     IP_RECVOPTS: int
     IP_RECVRETOPTS: int
     IP_RETOPTS: int
+if sys.version_info >= (3, 14):
+    IP_RECVTTL: int
+
+    if sys.platform == "win32" or sys.platform == "linux":
+        IPV6_RECVERR: int
+        IP_RECVERR: int
+        SO_ORIGINAL_DST: int
+
+    if sys.platform == "win32":
+        SOL_RFCOMM: int
+        SO_BTH_ENCRYPT: int
+        SO_BTH_MTU: int
+        SO_BTH_MTU_MAX: int
+        SO_BTH_MTU_MIN: int
+        TCP_QUICKACK: int
+
+    if sys.platform == "linux":
+        CAN_RAW_ERR_FILTER: int
+        IP_FREEBIND: int
+        IP_RECVORIGDSTADDR: int
+        VMADDR_CID_LOCAL: int
+
 if sys.platform != "win32" and sys.platform != "darwin":
     IP_TRANSPARENT: int
 if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11):
@@ -250,29 +272,26 @@ IPV6_RECVTCLASS: int
 IPV6_TCLASS: int
 IPV6_UNICAST_HOPS: int
 IPV6_V6ONLY: int
-if sys.version_info >= (3, 9) or sys.platform != "darwin":
-    IPV6_DONTFRAG: int
-    IPV6_HOPLIMIT: int
-    IPV6_HOPOPTS: int
-    IPV6_PKTINFO: int
-    IPV6_RECVRTHDR: int
-    IPV6_RTHDR: int
+IPV6_DONTFRAG: int
+IPV6_HOPLIMIT: int
+IPV6_HOPOPTS: int
+IPV6_PKTINFO: int
+IPV6_RECVRTHDR: int
+IPV6_RTHDR: int
 if sys.platform != "win32":
     IPV6_RTHDR_TYPE_0: int
-    if sys.version_info >= (3, 9) or sys.platform != "darwin":
-        IPV6_DSTOPTS: int
-        IPV6_NEXTHOP: int
-        IPV6_PATHMTU: int
-        IPV6_RECVDSTOPTS: int
-        IPV6_RECVHOPLIMIT: int
-        IPV6_RECVHOPOPTS: int
-        IPV6_RECVPATHMTU: int
-        IPV6_RECVPKTINFO: int
-        IPV6_RTHDRDSTOPTS: int
+    IPV6_DSTOPTS: int
+    IPV6_NEXTHOP: int
+    IPV6_PATHMTU: int
+    IPV6_RECVDSTOPTS: int
+    IPV6_RECVHOPLIMIT: int
+    IPV6_RECVHOPOPTS: int
+    IPV6_RECVPATHMTU: int
+    IPV6_RECVPKTINFO: int
+    IPV6_RTHDRDSTOPTS: int
 
 if sys.platform != "win32" and sys.platform != "linux":
-    if sys.version_info >= (3, 9) or sys.platform != "darwin":
-        IPV6_USE_MIN_MTU: int
+    IPV6_USE_MIN_MTU: int
 
 EAI_AGAIN: int
 EAI_BADFLAGS: int
@@ -414,16 +433,10 @@ if sys.platform == "linux":
 if sys.platform == "linux":
     # Availability: Linux >= 3.6
     CAN_RAW_FD_FRAMES: int
-
-if sys.platform == "linux" and sys.version_info >= (3, 9):
     # Availability: Linux >= 4.1
     CAN_RAW_JOIN_FILTERS: int
-
-if sys.platform == "linux":
     # Availability: Linux >= 2.6.25
     CAN_ISOTP: int
-
-if sys.platform == "linux" and sys.version_info >= (3, 9):
     # Availability: Linux >= 5.4
     CAN_J1939: int
 
@@ -566,18 +579,16 @@ if sys.platform == "linux":
     SO_VM_SOCKETS_BUFFER_MIN_SIZE: int
     VM_SOCKETS_INVALID_VERSION: int  # undocumented
 
-if sys.platform != "win32" or sys.version_info >= (3, 9):
-    # Documented as only available on BSD, macOS, but empirically sometimes
-    # available on Windows
-    if sys.platform != "linux":
-        AF_LINK: int
+# Documented as only available on BSD, macOS, but empirically sometimes
+# available on Windows
+if sys.platform != "linux":
+    AF_LINK: int
 
 has_ipv6: bool
 
 if sys.platform != "darwin" and sys.platform != "linux":
-    if sys.platform != "win32" or sys.version_info >= (3, 9):
-        BDADDR_ANY: str
-        BDADDR_LOCAL: str
+    BDADDR_ANY: str
+    BDADDR_LOCAL: str
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
     HCI_FILTER: int  # not in NetBSD or DragonFlyBSD
@@ -649,8 +660,7 @@ if sys.platform == "darwin":
     SYSPROTO_CONTROL: int
 
 if sys.platform != "darwin" and sys.platform != "linux":
-    if sys.version_info >= (3, 9) or sys.platform != "win32":
-        AF_BLUETOOTH: int
+    AF_BLUETOOTH: int
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
     # Linux and some BSD support is explicit in the docs
@@ -659,10 +669,9 @@ if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "lin
     BTPROTO_L2CAP: int
     BTPROTO_SCO: int  # not in FreeBSD
 if sys.platform != "darwin" and sys.platform != "linux":
-    if sys.version_info >= (3, 9) or sys.platform != "win32":
-        BTPROTO_RFCOMM: int
+    BTPROTO_RFCOMM: int
 
-if sys.version_info >= (3, 9) and sys.platform == "linux":
+if sys.platform == "linux":
     UDPLITE_RECV_CSCOV: int
     UDPLITE_SEND_CSCOV: int
 
@@ -842,6 +851,11 @@ if sys.platform != "win32":
 
 def if_nameindex() -> list[tuple[int, str]]: ...
 def if_nametoindex(oname: str, /) -> int: ...
-def if_indextoname(index: int, /) -> str: ...
+
+if sys.version_info >= (3, 14):
+    def if_indextoname(if_index: int, /) -> str: ...
+
+else:
+    def if_indextoname(index: int, /) -> str: ...
 
 CAPI: CapsuleType
diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi
index e39ab5eb6de8..7ab880e4def7 100644
--- a/mypy/typeshed/stdlib/_ssl.pyi
+++ b/mypy/typeshed/stdlib/_ssl.pyi
@@ -283,6 +283,8 @@ HAS_TLSv1: bool
 HAS_TLSv1_1: bool
 HAS_TLSv1_2: bool
 HAS_TLSv1_3: bool
+if sys.version_info >= (3, 14):
+    HAS_PHA: bool
 
 # version info
 OPENSSL_VERSION_NUMBER: int
diff --git a/mypy/typeshed/stdlib/_tracemalloc.pyi b/mypy/typeshed/stdlib/_tracemalloc.pyi
index b1aeb710233e..e9720f46692c 100644
--- a/mypy/typeshed/stdlib/_tracemalloc.pyi
+++ b/mypy/typeshed/stdlib/_tracemalloc.pyi
@@ -1,4 +1,3 @@
-import sys
 from collections.abc import Sequence
 from tracemalloc import _FrameTuple, _TraceTuple
 
@@ -9,9 +8,6 @@ def get_traceback_limit() -> int: ...
 def get_traced_memory() -> tuple[int, int]: ...
 def get_tracemalloc_memory() -> int: ...
 def is_tracing() -> bool: ...
-
-if sys.version_info >= (3, 9):
-    def reset_peak() -> None: ...
-
+def reset_peak() -> None: ...
 def start(nframe: int = 1, /) -> None: ...
 def stop() -> None: ...
diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
index 99d21b67360a..c37d55a7d9ec 100644
--- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi
+++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
@@ -3,7 +3,6 @@
 # See the README.md file in this directory for more information.
 
 import sys
-import typing_extensions
 from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized
 from dataclasses import Field
 from os import PathLike
@@ -23,7 +22,7 @@ from typing import (
     final,
     overload,
 )
-from typing_extensions import Buffer, LiteralString, TypeAlias
+from typing_extensions import Buffer, LiteralString, Self as _Self, TypeAlias
 
 _KT = TypeVar("_KT")
 _KT_co = TypeVar("_KT_co", covariant=True)
@@ -329,9 +328,9 @@ class structseq(Generic[_T_co]):
     # The second parameter will accept a dict of any kind without raising an exception,
     # but only has any meaning if you supply it a dict where the keys are strings.
     # https://github.com/python/typeshed/pull/6560#discussion_r767149830
-    def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> typing_extensions.Self: ...
+    def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> _Self: ...
     if sys.version_info >= (3, 13):
-        def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ...
+        def __replace__(self, **kwargs: Any) -> _Self: ...
 
 # Superset of typing.AnyStr that also includes LiteralString
 AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString)  # noqa: Y001
@@ -354,7 +353,10 @@ class DataclassInstance(Protocol):
     __dataclass_fields__: ClassVar[dict[str, Field[Any]]]
 
 # Anything that can be passed to the int/float constructors
-ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc
+if sys.version_info >= (3, 14):
+    ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex
+else:
+    ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc
 ConvertibleToFloat: TypeAlias = str | ReadableBuffer | SupportsFloat | SupportsIndex
 
 # A few classes updated from Foo(str, Enum) to Foo(StrEnum). This is a convenience so these
@@ -365,3 +367,14 @@ else:
     from enum import Enum
 
     class StrEnum(str, Enum): ...
+
+# Objects that appear in annotations or in type expressions.
+# Similar to PEP 747's TypeForm but a little broader.
+AnnotationForm: TypeAlias = Any
+
+if sys.version_info >= (3, 14):
+    from annotationlib import Format
+
+    # These return annotations, which can be arbitrary objects
+    AnnotateFunc: TypeAlias = Callable[[Format], dict[str, AnnotationForm]]
+    EvaluateFunc: TypeAlias = Callable[[Format], AnnotationForm]
diff --git a/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi b/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
new file mode 100644
index 000000000000..feb22aae0073
--- /dev/null
+++ b/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
@@ -0,0 +1,89 @@
+# Internals used by some type checkers.
+#
+# Don't use this module directly. It is only for type checkers to use.
+
+import sys
+import typing_extensions
+from _collections_abc import dict_items, dict_keys, dict_values
+from abc import ABCMeta
+from collections.abc import Awaitable, Generator, Iterable, Mapping
+from typing import Any, ClassVar, Generic, TypeVar, overload
+from typing_extensions import Never
+
+_T = TypeVar("_T")
+
+# Used for an undocumented mypy feature. Does not exist at runtime.
+promote = object()
+
+# Fallback type providing methods and attributes that appear on all `TypedDict` types.
+# N.B. Keep this mostly in sync with typing_extensions._TypedDict/mypy_extensions._TypedDict
+class TypedDictFallback(Mapping[str, object], metaclass=ABCMeta):
+    __total__: ClassVar[bool]
+    __required_keys__: ClassVar[frozenset[str]]
+    __optional_keys__: ClassVar[frozenset[str]]
+    # __orig_bases__ sometimes exists on <3.12, but not consistently,
+    # so we only add it to the stub on 3.12+
+    if sys.version_info >= (3, 12):
+        __orig_bases__: ClassVar[tuple[Any, ...]]
+    if sys.version_info >= (3, 13):
+        __readonly_keys__: ClassVar[frozenset[str]]
+        __mutable_keys__: ClassVar[frozenset[str]]
+
+    def copy(self) -> typing_extensions.Self: ...
+    # Using Never so that only calls using mypy plugin hook that specialize the signature
+    # can go through.
+    def setdefault(self, k: Never, default: object) -> object: ...
+    # Mypy plugin hook for 'pop' expects that 'default' has a type variable type.
+    def pop(self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
+    def update(self, m: typing_extensions.Self, /) -> None: ...
+    def __delitem__(self, k: Never) -> None: ...
+    def items(self) -> dict_items[str, object]: ...
+    def keys(self) -> dict_keys[str, object]: ...
+    def values(self) -> dict_values[str, object]: ...
+    @overload
+    def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    @overload
+    def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    @overload
+    def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    @overload
+    def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    # supposedly incompatible definitions of __or__ and __ior__
+    def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
+
+# Fallback type providing methods and attributes that appear on all `NamedTuple` types.
+class NamedTupleFallback(tuple[Any, ...]):
+    _field_defaults: ClassVar[dict[str, Any]]
+    _fields: ClassVar[tuple[str, ...]]
+    # __orig_bases__ sometimes exists on <3.12, but not consistently
+    # So we only add it to the stub on 3.12+.
+    if sys.version_info >= (3, 12):
+        __orig_bases__: ClassVar[tuple[Any, ...]]
+
+    @overload
+    def __init__(self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ...
+    @overload
+    @typing_extensions.deprecated(
+        "Creating a typing.NamedTuple using keyword arguments is deprecated and support will be removed in Python 3.15"
+    )
+    def __init__(self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ...
+    @classmethod
+    def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ...
+    def _asdict(self) -> dict[str, Any]: ...
+    def _replace(self, **kwargs: Any) -> typing_extensions.Self: ...
+    if sys.version_info >= (3, 13):
+        def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ...
+
+# Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter.
+_S = TypeVar("_S")
+_YieldT_co = TypeVar("_YieldT_co", covariant=True)
+_SendT_nd_contra = TypeVar("_SendT_nd_contra", contravariant=True)
+_ReturnT_nd_co = TypeVar("_ReturnT_nd_co", covariant=True)
+
+# The parameters correspond to Generator, but the 4th is the original type.
+class AwaitableGenerator(
+    Awaitable[_ReturnT_nd_co],
+    Generator[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co],
+    Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co, _S],
+    metaclass=ABCMeta,
+): ...
diff --git a/mypy/typeshed/stdlib/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi
index b55318528208..dad1ed7a4fb5 100644
--- a/mypy/typeshed/stdlib/_weakrefset.pyi
+++ b/mypy/typeshed/stdlib/_weakrefset.pyi
@@ -1,11 +1,8 @@
-import sys
 from collections.abc import Iterable, Iterator, MutableSet
+from types import GenericAlias
 from typing import Any, ClassVar, TypeVar, overload
 from typing_extensions import Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["WeakSet"]
 
 _S = TypeVar("_S")
@@ -48,5 +45,4 @@ class WeakSet(MutableSet[_T]):
     def union(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ...
     def __or__(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ...
     def isdisjoint(self, other: Iterable[_T]) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/aifc.pyi b/mypy/typeshed/stdlib/aifc.pyi
index 05bf53986b29..bfe12c6af2b0 100644
--- a/mypy/typeshed/stdlib/aifc.pyi
+++ b/mypy/typeshed/stdlib/aifc.pyi
@@ -1,12 +1,8 @@
-import sys
 from types import TracebackType
 from typing import IO, Any, Literal, NamedTuple, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Error", "open"]
-else:
-    __all__ = ["Error", "open", "openfp"]
+__all__ = ["Error", "open"]
 
 class Error(Exception): ...
 
@@ -81,11 +77,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Aifc_read: ...
 def open(f: _File, mode: Literal["w", "wb"]) -> Aifc_write: ...
 @overload
 def open(f: _File, mode: str | None = None) -> Any: ...
-
-if sys.version_info < (3, 9):
-    @overload
-    def openfp(f: _File, mode: Literal["r", "rb"]) -> Aifc_read: ...
-    @overload
-    def openfp(f: _File, mode: Literal["w", "wb"]) -> Aifc_write: ...
-    @overload
-    def openfp(f: _File, mode: str | None = None) -> Any: ...
diff --git a/mypy/typeshed/stdlib/annotationlib.pyi b/mypy/typeshed/stdlib/annotationlib.pyi
new file mode 100644
index 000000000000..7590c632d785
--- /dev/null
+++ b/mypy/typeshed/stdlib/annotationlib.pyi
@@ -0,0 +1,132 @@
+import sys
+from typing import Literal
+
+if sys.version_info >= (3, 14):
+    import enum
+    import types
+    from _typeshed import AnnotateFunc, AnnotationForm, EvaluateFunc, SupportsItems
+    from collections.abc import Mapping
+    from typing import Any, ParamSpec, TypeVar, TypeVarTuple, final, overload
+    from warnings import deprecated
+
+    __all__ = [
+        "Format",
+        "ForwardRef",
+        "call_annotate_function",
+        "call_evaluate_function",
+        "get_annotate_from_class_namespace",
+        "get_annotations",
+        "annotations_to_string",
+        "type_repr",
+    ]
+
+    class Format(enum.IntEnum):
+        VALUE = 1
+        VALUE_WITH_FAKE_GLOBALS = 2
+        FORWARDREF = 3
+        STRING = 4
+
+    @final
+    class ForwardRef:
+        __forward_is_argument__: bool
+        __forward_is_class__: bool
+        __forward_module__: str | None
+        def __init__(
+            self, arg: str, *, module: str | None = None, owner: object = None, is_argument: bool = True, is_class: bool = False
+        ) -> None: ...
+        @overload
+        def evaluate(
+            self,
+            *,
+            globals: dict[str, Any] | None = None,
+            locals: Mapping[str, Any] | None = None,
+            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None,
+            owner: object = None,
+            format: Literal[Format.STRING],
+        ) -> str: ...
+        @overload
+        def evaluate(
+            self,
+            *,
+            globals: dict[str, Any] | None = None,
+            locals: Mapping[str, Any] | None = None,
+            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None,
+            owner: object = None,
+            format: Literal[Format.FORWARDREF],
+        ) -> AnnotationForm | ForwardRef: ...
+        @overload
+        def evaluate(
+            self,
+            *,
+            globals: dict[str, Any] | None = None,
+            locals: Mapping[str, Any] | None = None,
+            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None,
+            owner: object = None,
+            format: Format = Format.VALUE,  # noqa: Y011
+        ) -> AnnotationForm: ...
+        @deprecated("Use ForwardRef.evaluate() or typing.evaluate_forward_ref() instead.")
+        def _evaluate(
+            self,
+            globalns: dict[str, Any] | None,
+            localns: Mapping[str, Any] | None,
+            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ...,
+            *,
+            recursive_guard: frozenset[str],
+        ) -> AnnotationForm: ...
+        @property
+        def __forward_arg__(self) -> str: ...
+        @property
+        def __forward_code__(self) -> types.CodeType: ...
+        def __eq__(self, other: object) -> bool: ...
+        def __hash__(self) -> int: ...
+        def __or__(self, other: Any) -> types.UnionType: ...
+        def __ror__(self, other: Any) -> types.UnionType: ...
+
+    @overload
+    def call_evaluate_function(evaluate: EvaluateFunc, format: Literal[Format.STRING], *, owner: object = None) -> str: ...
+    @overload
+    def call_evaluate_function(
+        evaluate: EvaluateFunc, format: Literal[Format.FORWARDREF], *, owner: object = None
+    ) -> AnnotationForm | ForwardRef: ...
+    @overload
+    def call_evaluate_function(evaluate: EvaluateFunc, format: Format, *, owner: object = None) -> AnnotationForm: ...
+    @overload
+    def call_annotate_function(
+        annotate: AnnotateFunc, format: Literal[Format.STRING], *, owner: object = None
+    ) -> dict[str, str]: ...
+    @overload
+    def call_annotate_function(
+        annotate: AnnotateFunc, format: Literal[Format.FORWARDREF], *, owner: object = None
+    ) -> dict[str, AnnotationForm | ForwardRef]: ...
+    @overload
+    def call_annotate_function(annotate: AnnotateFunc, format: Format, *, owner: object = None) -> dict[str, AnnotationForm]: ...
+    def get_annotate_from_class_namespace(obj: Mapping[str, object]) -> AnnotateFunc | None: ...
+    @overload
+    def get_annotations(
+        obj: Any,  # any object with __annotations__ or __annotate__
+        *,
+        globals: dict[str, object] | None = None,
+        locals: Mapping[str, object] | None = None,
+        eval_str: bool = False,
+        format: Literal[Format.STRING],
+    ) -> dict[str, str]: ...
+    @overload
+    def get_annotations(
+        obj: Any,
+        *,
+        globals: dict[str, object] | None = None,
+        locals: Mapping[str, object] | None = None,
+        eval_str: bool = False,
+        format: Literal[Format.FORWARDREF],
+    ) -> dict[str, AnnotationForm | ForwardRef]: ...
+    @overload
+    def get_annotations(
+        obj: Any,
+        *,
+        globals: dict[str, object] | None = None,
+        locals: Mapping[str, object] | None = None,
+        eval_str: bool = False,
+        format: Format = Format.VALUE,  # noqa: Y011
+    ) -> dict[str, AnnotationForm]: ...
+    def type_repr(value: object) -> str: ...
+    def annotations_to_string(annotations: SupportsItems[str, object]) -> dict[str, str]: ...
diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
index 9dbd8c308b59..79e6cfde12ff 100644
--- a/mypy/typeshed/stdlib/argparse.pyi
+++ b/mypy/typeshed/stdlib/argparse.pyi
@@ -17,6 +17,7 @@ __all__ = [
     "MetavarTypeHelpFormatter",
     "Namespace",
     "Action",
+    "BooleanOptionalAction",
     "ONE_OR_MORE",
     "OPTIONAL",
     "PARSER",
@@ -25,9 +26,6 @@ __all__ = [
     "ZERO_OR_MORE",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["BooleanOptionalAction"]
-
 _T = TypeVar("_T")
 _ActionT = TypeVar("_ActionT", bound=Action)
 _ArgumentParserT = TypeVar("_ArgumentParserT", bound=ArgumentParser)
@@ -127,6 +125,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
     fromfile_prefix_chars: str | None
     add_help: bool
     allow_abbrev: bool
+    exit_on_error: bool
+
+    if sys.version_info >= (3, 14):
+        suggest_on_error: bool
+        color: bool
 
     # undocumented
     _positionals: _ArgumentGroup
@@ -134,7 +137,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
     _subparsers: _ArgumentGroup | None
 
     # Note: the constructor arguments are also used in _SubParsersAction.add_parser.
-    if sys.version_info >= (3, 9):
+    if sys.version_info >= (3, 14):
         def __init__(
             self,
             prog: str | None = None,
@@ -150,6 +153,9 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
             add_help: bool = True,
             allow_abbrev: bool = True,
             exit_on_error: bool = True,
+            *,
+            suggest_on_error: bool = False,
+            color: bool = False,
         ) -> None: ...
     else:
         def __init__(
@@ -166,6 +172,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
             conflict_handler: str = "error",
             add_help: bool = True,
             allow_abbrev: bool = True,
+            exit_on_error: bool = True,
         ) -> None: ...
 
     @overload
@@ -274,7 +281,21 @@ class HelpFormatter:
         def __init__(self, formatter: HelpFormatter, parent: Self | None, heading: str | None = None) -> None: ...
         def format_help(self) -> str: ...
 
-    def __init__(self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(
+            self,
+            prog: str,
+            indent_increment: int = 2,
+            max_help_position: int = 24,
+            width: int | None = None,
+            prefix_chars: str = "-",
+            color: bool = False,
+        ) -> None: ...
+    else:
+        def __init__(
+            self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None
+        ) -> None: ...
+
     def _indent(self) -> None: ...
     def _dedent(self) -> None: ...
     def _add_item(self, func: Callable[..., str], args: Iterable[Any]) -> None: ...
@@ -354,8 +375,7 @@ class Action(_AttributeHolder):
     def __call__(
         self, parser: ArgumentParser, namespace: Namespace, values: str | Sequence[Any] | None, option_string: str | None = None
     ) -> None: ...
-    if sys.version_info >= (3, 9):
-        def format_usage(self) -> str: ...
+    def format_usage(self) -> str: ...
 
 if sys.version_info >= (3, 12):
     class BooleanOptionalAction(Action):
@@ -420,7 +440,7 @@ if sys.version_info >= (3, 12):
                 metavar: str | tuple[str, ...] | None = sentinel,
             ) -> None: ...
 
-elif sys.version_info >= (3, 9):
+else:
     class BooleanOptionalAction(Action):
         @overload
         def __init__(
@@ -454,14 +474,30 @@ class Namespace(_AttributeHolder):
     def __eq__(self, other: object) -> bool: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
 
-class FileType:
-    # undocumented
-    _mode: str
-    _bufsize: int
-    _encoding: str | None
-    _errors: str | None
-    def __init__(self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None) -> None: ...
-    def __call__(self, string: str) -> IO[Any]: ...
+if sys.version_info >= (3, 14):
+    @deprecated("Deprecated in Python 3.14; Simply open files after parsing arguments")
+    class FileType:
+        # undocumented
+        _mode: str
+        _bufsize: int
+        _encoding: str | None
+        _errors: str | None
+        def __init__(
+            self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None
+        ) -> None: ...
+        def __call__(self, string: str) -> IO[Any]: ...
+
+else:
+    class FileType:
+        # undocumented
+        _mode: str
+        _bufsize: int
+        _encoding: str | None
+        _errors: str | None
+        def __init__(
+            self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None
+        ) -> None: ...
+        def __call__(self, string: str) -> IO[Any]: ...
 
 # undocumented
 class _ArgumentGroup(_ActionsContainer):
@@ -691,7 +727,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
 
     # Note: `add_parser` accepts all kwargs of `ArgumentParser.__init__`. It also
     # accepts its own `help` and `aliases` kwargs.
-    if sys.version_info >= (3, 13):
+    if sys.version_info >= (3, 14):
         def add_parser(
             self,
             name: str,
@@ -713,13 +749,16 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
             add_help: bool = ...,
             allow_abbrev: bool = ...,
             exit_on_error: bool = ...,
+            suggest_on_error: bool = False,
+            color: bool = False,
             **kwargs: Any,  # Accepting any additional kwargs for custom parser classes
         ) -> _ArgumentParserT: ...
-    elif sys.version_info >= (3, 9):
+    elif sys.version_info >= (3, 13):
         def add_parser(
             self,
             name: str,
             *,
+            deprecated: bool = False,
             help: str | None = ...,
             aliases: Sequence[str] = ...,
             # Kwargs from ArgumentParser constructor
@@ -758,6 +797,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
             conflict_handler: str = ...,
             add_help: bool = ...,
             allow_abbrev: bool = ...,
+            exit_on_error: bool = ...,
             **kwargs: Any,  # Accepting any additional kwargs for custom parser classes
         ) -> _ArgumentParserT: ...
 
diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi
index 19ec8c1e78f9..bd96c9bc2d31 100644
--- a/mypy/typeshed/stdlib/array.pyi
+++ b/mypy/typeshed/stdlib/array.pyi
@@ -1,14 +1,10 @@
 import sys
 from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite
-from collections.abc import Iterable
-
-# pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence
-from typing import Any, ClassVar, Literal, MutableSequence, SupportsIndex, TypeVar, overload  # noqa: Y022
+from collections.abc import Iterable, MutableSequence
+from types import GenericAlias
+from typing import Any, ClassVar, Literal, SupportsIndex, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 12):
-    from types import GenericAlias
-
 _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"]
 _FloatTypeCode: TypeAlias = Literal["f", "d"]
 _UnicodeTypeCode: TypeAlias = Literal["u"]
@@ -60,9 +56,6 @@ class array(MutableSequence[_T]):
     def tofile(self, f: SupportsWrite[bytes], /) -> None: ...
     def tolist(self) -> list[_T]: ...
     def tounicode(self) -> str: ...
-    if sys.version_info < (3, 9):
-        def fromstring(self, buffer: str | ReadableBuffer, /) -> None: ...
-        def tostring(self) -> bytes: ...
 
     __hash__: ClassVar[None]  # type: ignore[assignment]
     def __len__(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index 7a4438a33fbc..f26ec4d1a08b 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -1,3 +1,4 @@
+import builtins
 import os
 import sys
 import typing_extensions
@@ -7,19 +8,13 @@ from _ast import (
     PyCF_TYPE_COMMENTS as PyCF_TYPE_COMMENTS,
 )
 from _typeshed import ReadableBuffer, Unused
-from collections.abc import Iterable, Iterator
+from collections.abc import Iterable, Iterator, Sequence
 from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload
 from typing_extensions import Self, Unpack, deprecated
 
 if sys.version_info >= (3, 13):
     from _ast import PyCF_OPTIMIZED_AST as PyCF_OPTIMIZED_AST
 
-# Alias used for fields that must always be valid identifiers
-# A string `x` counts as a valid identifier if both the following are True
-# (1) `x.isidentifier()` evaluates to `True`
-# (2) `keyword.iskeyword(x)` evaluates to `False`
-_Identifier: typing_extensions.TypeAlias = str
-
 # Used for node end positions in constructor keyword arguments
 _EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None)
 
@@ -111,7 +106,7 @@ class FunctionDef(stmt):
         __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params")
     elif sys.version_info >= (3, 10):
         __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment")
-    name: _Identifier
+    name: str
     args: arguments
     body: list[stmt]
     decorator_list: list[expr]
@@ -122,7 +117,7 @@ class FunctionDef(stmt):
     if sys.version_info >= (3, 13):
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt] = ...,
             decorator_list: list[expr] = ...,
@@ -135,7 +130,7 @@ class FunctionDef(stmt):
         @overload
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -147,7 +142,7 @@ class FunctionDef(stmt):
         @overload
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -160,7 +155,7 @@ class FunctionDef(stmt):
     else:
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -173,13 +168,14 @@ class FunctionDef(stmt):
         def __replace__(
             self,
             *,
-            name: _Identifier = ...,
+            name: str = ...,
             args: arguments = ...,
             body: list[stmt] = ...,
             decorator_list: list[expr] = ...,
             returns: expr | None = ...,
             type_comment: str | None = ...,
             type_params: list[type_param] = ...,
+            **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
 class AsyncFunctionDef(stmt):
@@ -187,7 +183,7 @@ class AsyncFunctionDef(stmt):
         __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params")
     elif sys.version_info >= (3, 10):
         __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment")
-    name: _Identifier
+    name: str
     args: arguments
     body: list[stmt]
     decorator_list: list[expr]
@@ -198,7 +194,7 @@ class AsyncFunctionDef(stmt):
     if sys.version_info >= (3, 13):
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt] = ...,
             decorator_list: list[expr] = ...,
@@ -211,7 +207,7 @@ class AsyncFunctionDef(stmt):
         @overload
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -223,7 +219,7 @@ class AsyncFunctionDef(stmt):
         @overload
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -236,7 +232,7 @@ class AsyncFunctionDef(stmt):
     else:
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             args: arguments,
             body: list[stmt],
             decorator_list: list[expr],
@@ -249,13 +245,14 @@ class AsyncFunctionDef(stmt):
         def __replace__(
             self,
             *,
-            name: _Identifier = ...,
+            name: str = ...,
             args: arguments = ...,
-            body: list[stmt],
-            decorator_list: list[expr],
-            returns: expr | None,
-            type_comment: str | None,
-            type_params: list[type_param],
+            body: list[stmt] = ...,
+            decorator_list: list[expr] = ...,
+            returns: expr | None = ...,
+            type_comment: str | None = ...,
+            type_params: list[type_param] = ...,
+            **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
 class ClassDef(stmt):
@@ -263,7 +260,7 @@ class ClassDef(stmt):
         __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params")
     elif sys.version_info >= (3, 10):
         __match_args__ = ("name", "bases", "keywords", "body", "decorator_list")
-    name: _Identifier
+    name: str
     bases: list[expr]
     keywords: list[keyword]
     body: list[stmt]
@@ -273,7 +270,7 @@ class ClassDef(stmt):
     if sys.version_info >= (3, 13):
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             bases: list[expr] = ...,
             keywords: list[keyword] = ...,
             body: list[stmt] = ...,
@@ -284,7 +281,7 @@ class ClassDef(stmt):
     elif sys.version_info >= (3, 12):
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             bases: list[expr],
             keywords: list[keyword],
             body: list[stmt],
@@ -295,7 +292,7 @@ class ClassDef(stmt):
     else:
         def __init__(
             self,
-            name: _Identifier,
+            name: str,
             bases: list[expr],
             keywords: list[keyword],
             body: list[stmt],
@@ -307,12 +304,12 @@ class ClassDef(stmt):
         def __replace__(
             self,
             *,
-            name: _Identifier,
-            bases: list[expr],
-            keywords: list[keyword],
-            body: list[stmt],
-            decorator_list: list[expr],
-            type_params: list[type_param],
+            name: str = ...,
+            bases: list[expr] = ...,
+            keywords: list[keyword] = ...,
+            body: list[stmt] = ...,
+            decorator_list: list[expr] = ...,
+            type_params: list[type_param] = ...,
             **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
@@ -383,7 +380,7 @@ if sys.version_info >= (3, 12):
             ) -> None: ...
 
         if sys.version_info >= (3, 14):
-            def __replace__(
+            def __replace__(  # type: ignore[override]
                 self,
                 *,
                 name: Name = ...,
@@ -546,7 +543,9 @@ class While(stmt):
         def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(
+            self, *, test: expr = ..., body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes]
+        ) -> Self: ...
 
 class If(stmt):
     if sys.version_info >= (3, 10):
@@ -731,7 +730,7 @@ class Assert(stmt):
     def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, test: expr, msg: expr | None, **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, test: expr = ..., msg: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class Import(stmt):
     if sys.version_info >= (3, 10):
@@ -774,26 +773,26 @@ class ImportFrom(stmt):
 class Global(stmt):
     if sys.version_info >= (3, 10):
         __match_args__ = ("names",)
-    names: list[_Identifier]
+    names: list[str]
     if sys.version_info >= (3, 13):
-        def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+        def __init__(self, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
     else:
-        def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ...
+        def __init__(self, names: list[str], **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class Nonlocal(stmt):
     if sys.version_info >= (3, 10):
         __match_args__ = ("names",)
-    names: list[_Identifier]
+    names: list[str]
     if sys.version_info >= (3, 13):
-        def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+        def __init__(self, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
     else:
-        def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ...
+        def __init__(self, names: list[str], **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class Expr(stmt):
     if sys.version_info >= (3, 10):
@@ -1065,6 +1064,37 @@ class JoinedStr(expr):
     if sys.version_info >= (3, 14):
         def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
+if sys.version_info >= (3, 14):
+    class TemplateStr(expr):
+        __match_args__ = ("values",)
+        values: list[expr]
+        def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+        def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+
+    class Interpolation(expr):
+        __match_args__ = ("value", "str", "conversion", "format_spec")
+        value: expr
+        str: builtins.str
+        conversion: int
+        format_spec: builtins.str | None = None
+        def __init__(
+            self,
+            value: expr = ...,
+            str: builtins.str = ...,
+            conversion: int = ...,
+            format_spec: builtins.str | None = ...,
+            **kwargs: Unpack[_Attributes],
+        ) -> None: ...
+        def __replace__(
+            self,
+            *,
+            value: expr = ...,
+            str: builtins.str = ...,
+            conversion: int = ...,
+            format_spec: builtins.str | None = ...,
+            **kwargs: Unpack[_Attributes],
+        ) -> Self: ...
+
 class Constant(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("value", "kind")
@@ -1084,13 +1114,13 @@ class Attribute(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("value", "attr", "ctx")
     value: expr
-    attr: _Identifier
+    attr: str
     ctx: expr_context  # Not present in Python < 3.13 if not passed to `__init__`
-    def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, value: expr, attr: str, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(
-            self, *, value: expr = ..., attr: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]
+            self, *, value: expr = ..., attr: str = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
 class Subscript(expr):
@@ -1119,12 +1149,12 @@ class Starred(expr):
 class Name(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("id", "ctx")
-    id: _Identifier
+    id: str
     ctx: expr_context  # Not present in Python < 3.13 if not passed to `__init__`
-    def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, id: str, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, id: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, id: str = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class List(expr):
     if sys.version_info >= (3, 10):
@@ -1144,8 +1174,7 @@ class Tuple(expr):
         __match_args__ = ("elts", "ctx")
     elts: list[expr]
     ctx: expr_context  # Not present in Python < 3.13 if not passed to `__init__`
-    if sys.version_info >= (3, 9):
-        dims: list[expr]
+    dims: list[expr]
     if sys.version_info >= (3, 13):
         def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
     else:
@@ -1155,16 +1184,10 @@ class Tuple(expr):
         def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 @deprecated("Deprecated since Python 3.9.")
-class slice(AST): ...  # deprecated and moved to ast.py for >= (3, 9)
-
-if sys.version_info >= (3, 9):
-    _Slice: typing_extensions.TypeAlias = expr
-    _SliceAttributes: typing_extensions.TypeAlias = _Attributes
-else:
-    # alias for use with variables named slice
-    _Slice: typing_extensions.TypeAlias = slice
+class slice(AST): ...
 
-    class _SliceAttributes(TypedDict): ...
+_Slice: typing_extensions.TypeAlias = expr
+_SliceAttributes: typing_extensions.TypeAlias = _Attributes
 
 class Slice(_Slice):
     if sys.version_info >= (3, 10):
@@ -1187,37 +1210,26 @@ class Slice(_Slice):
         ) -> Self: ...
 
 @deprecated("Deprecated since Python 3.9. Use ast.Tuple instead.")
-class ExtSlice(slice):  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
-    if sys.version_info >= (3, 9):
-        def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_SliceAttributes]) -> Tuple: ...  # type: ignore[misc]
-    else:
-        dims: list[slice]
-        def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ...
+class ExtSlice(slice):
+    def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_SliceAttributes]) -> Tuple: ...  # type: ignore[misc]
 
 @deprecated("Deprecated since Python 3.9. Use the index value directly instead.")
-class Index(slice):  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
-    if sys.version_info >= (3, 9):
-        def __new__(cls, value: expr, **kwargs: Unpack[_SliceAttributes]) -> expr: ...  # type: ignore[misc]
-    else:
-        value: expr
-        def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ...
+class Index(slice):
+    def __new__(cls, value: expr, **kwargs: Unpack[_SliceAttributes]) -> expr: ...  # type: ignore[misc]
 
 class expr_context(AST): ...
 
 @deprecated("Deprecated since Python 3.9. Unused in Python 3.")
-class AugLoad(expr_context): ...  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
+class AugLoad(expr_context): ...
 
 @deprecated("Deprecated since Python 3.9. Unused in Python 3.")
-class AugStore(expr_context): ...  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
+class AugStore(expr_context): ...
 
 @deprecated("Deprecated since Python 3.9. Unused in Python 3.")
-class Param(expr_context): ...  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
+class Param(expr_context): ...
 
 @deprecated("Deprecated since Python 3.9. Unused in Python 3.")
-class Suite(mod):  # deprecated and moved to ast.py if sys.version_info >= (3, 9)
-    if sys.version_info < (3, 9):
-        body: list[stmt]
-        def __init__(self, body: list[stmt]) -> None: ...
+class Suite(mod): ...
 
 class Load(expr_context): ...
 class Store(expr_context): ...
@@ -1290,30 +1302,23 @@ class ExceptHandler(excepthandler):
     if sys.version_info >= (3, 10):
         __match_args__ = ("type", "name", "body")
     type: expr | None
-    name: _Identifier | None
+    name: str | None
     body: list[stmt]
     if sys.version_info >= (3, 13):
         def __init__(
-            self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes]
+            self, type: expr | None = None, name: str | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes]
         ) -> None: ...
     else:
         @overload
-        def __init__(
-            self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes]
-        ) -> None: ...
+        def __init__(self, type: expr | None, name: str | None, body: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ...
         @overload
         def __init__(
-            self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes]
+            self, type: expr | None = None, name: str | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes]
         ) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(
-            self,
-            *,
-            type: expr | None = ...,
-            name: _Identifier | None = ...,
-            body: list[stmt] = ...,
-            **kwargs: Unpack[_Attributes],
+            self, *, type: expr | None = ..., name: str | None = ..., body: list[stmt] = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
 class arguments(AST):
@@ -1394,21 +1399,16 @@ class arg(AST):
     end_col_offset: int | None
     if sys.version_info >= (3, 10):
         __match_args__ = ("arg", "annotation", "type_comment")
-    arg: _Identifier
+    arg: str
     annotation: expr | None
     type_comment: str | None
     def __init__(
-        self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes]
+        self, arg: str, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes]
     ) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(
-            self,
-            *,
-            arg: _Identifier = ...,
-            annotation: expr | None = ...,
-            type_comment: str | None = ...,
-            **kwargs: Unpack[_Attributes],
+            self, *, arg: str = ..., annotation: expr | None = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
 class keyword(AST):
@@ -1418,15 +1418,15 @@ class keyword(AST):
     end_col_offset: int | None
     if sys.version_info >= (3, 10):
         __match_args__ = ("arg", "value")
-    arg: _Identifier | None
+    arg: str | None
     value: expr
     @overload
-    def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, arg: str | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ...
     @overload
-    def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, arg: str | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, arg: _Identifier | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, arg: str | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class alias(AST):
     lineno: int
@@ -1436,11 +1436,11 @@ class alias(AST):
     if sys.version_info >= (3, 10):
         __match_args__ = ("name", "asname")
     name: str
-    asname: _Identifier | None
-    def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
+    asname: str | None
+    def __init__(self, name: str, asname: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, name: str = ..., asname: _Identifier | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, name: str = ..., asname: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class withitem(AST):
     if sys.version_info >= (3, 10):
@@ -1515,22 +1515,18 @@ if sys.version_info >= (3, 10):
         __match_args__ = ("keys", "patterns", "rest")
         keys: list[expr]
         patterns: list[pattern]
-        rest: _Identifier | None
+        rest: str | None
         if sys.version_info >= (3, 13):
             def __init__(
                 self,
                 keys: list[expr] = ...,
                 patterns: list[pattern] = ...,
-                rest: _Identifier | None = None,
+                rest: str | None = None,
                 **kwargs: Unpack[_Attributes[int]],
             ) -> None: ...
         else:
             def __init__(
-                self,
-                keys: list[expr],
-                patterns: list[pattern],
-                rest: _Identifier | None = None,
-                **kwargs: Unpack[_Attributes[int]],
+                self, keys: list[expr], patterns: list[pattern], rest: str | None = None, **kwargs: Unpack[_Attributes[int]]
             ) -> None: ...
 
         if sys.version_info >= (3, 14):
@@ -1539,7 +1535,7 @@ if sys.version_info >= (3, 10):
                 *,
                 keys: list[expr] = ...,
                 patterns: list[pattern] = ...,
-                rest: _Identifier | None = ...,
+                rest: str | None = ...,
                 **kwargs: Unpack[_Attributes[int]],
             ) -> Self: ...
 
@@ -1547,14 +1543,14 @@ if sys.version_info >= (3, 10):
         __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns")
         cls: expr
         patterns: list[pattern]
-        kwd_attrs: list[_Identifier]
+        kwd_attrs: list[str]
         kwd_patterns: list[pattern]
         if sys.version_info >= (3, 13):
             def __init__(
                 self,
                 cls: expr,
                 patterns: list[pattern] = ...,
-                kwd_attrs: list[_Identifier] = ...,
+                kwd_attrs: list[str] = ...,
                 kwd_patterns: list[pattern] = ...,
                 **kwargs: Unpack[_Attributes[int]],
             ) -> None: ...
@@ -1563,7 +1559,7 @@ if sys.version_info >= (3, 10):
                 self,
                 cls: expr,
                 patterns: list[pattern],
-                kwd_attrs: list[_Identifier],
+                kwd_attrs: list[str],
                 kwd_patterns: list[pattern],
                 **kwargs: Unpack[_Attributes[int]],
             ) -> None: ...
@@ -1574,30 +1570,30 @@ if sys.version_info >= (3, 10):
                 *,
                 cls: expr = ...,
                 patterns: list[pattern] = ...,
-                kwd_attrs: list[_Identifier] = ...,
+                kwd_attrs: list[str] = ...,
                 kwd_patterns: list[pattern] = ...,
                 **kwargs: Unpack[_Attributes[int]],
             ) -> Self: ...
 
     class MatchStar(pattern):
         __match_args__ = ("name",)
-        name: _Identifier | None
-        def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+        name: str | None
+        def __init__(self, name: str | None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
-            def __replace__(self, *, name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ...
+            def __replace__(self, *, name: str | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ...
 
     class MatchAs(pattern):
         __match_args__ = ("pattern", "name")
         pattern: _Pattern | None
-        name: _Identifier | None
+        name: str | None
         def __init__(
-            self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]]
+            self, pattern: _Pattern | None = None, name: str | None = None, **kwargs: Unpack[_Attributes[int]]
         ) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
-                self, *, pattern: _Pattern | None = ..., name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]
+                self, *, pattern: _Pattern | None = ..., name: str | None = ..., **kwargs: Unpack[_Attributes[int]]
             ) -> Self: ...
 
     class MatchOr(pattern):
@@ -1639,25 +1635,21 @@ if sys.version_info >= (3, 12):
             __match_args__ = ("name", "bound", "default_value")
         else:
             __match_args__ = ("name", "bound")
-        name: _Identifier
+        name: str
         bound: expr | None
         if sys.version_info >= (3, 13):
             default_value: expr | None
             def __init__(
-                self,
-                name: _Identifier,
-                bound: expr | None = None,
-                default_value: expr | None = None,
-                **kwargs: Unpack[_Attributes[int]],
+                self, name: str, bound: expr | None = None, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]
             ) -> None: ...
         else:
-            def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+            def __init__(self, name: str, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
                 self,
                 *,
-                name: _Identifier = ...,
+                name: str = ...,
                 bound: expr | None = ...,
                 default_value: expr | None = ...,
                 **kwargs: Unpack[_Attributes[int]],
@@ -1668,18 +1660,16 @@ if sys.version_info >= (3, 12):
             __match_args__ = ("name", "default_value")
         else:
             __match_args__ = ("name",)
-        name: _Identifier
+        name: str
         if sys.version_info >= (3, 13):
             default_value: expr | None
-            def __init__(
-                self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]
-            ) -> None: ...
+            def __init__(self, name: str, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
         else:
-            def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+            def __init__(self, name: str, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
-                self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]]
+                self, *, name: str = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]]
             ) -> Self: ...
 
     class TypeVarTuple(type_param):
@@ -1687,23 +1677,20 @@ if sys.version_info >= (3, 12):
             __match_args__ = ("name", "default_value")
         else:
             __match_args__ = ("name",)
-        name: _Identifier
+        name: str
         if sys.version_info >= (3, 13):
             default_value: expr | None
-            def __init__(
-                self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]
-            ) -> None: ...
+            def __init__(self, name: str, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
         else:
-            def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+            def __init__(self, name: str, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
-                self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]]
+                self, *, name: str = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]]
             ) -> Self: ...
 
 class _ABC(type):
-    if sys.version_info >= (3, 9):
-        def __init__(cls, *args: Unused) -> None: ...
+    def __init__(cls, *args: Unused) -> None: ...
 
 if sys.version_info < (3, 14):
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
@@ -1894,14 +1881,11 @@ if sys.version_info >= (3, 13):
         show_empty: bool = False,
     ) -> str: ...
 
-elif sys.version_info >= (3, 9):
+else:
     def dump(
         node: AST, annotate_fields: bool = True, include_attributes: bool = False, *, indent: int | str | None = None
     ) -> str: ...
 
-else:
-    def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ...
-
 def copy_location(new_node: _T, old_node: AST) -> _T: ...
 def fix_missing_locations(node: _T) -> _T: ...
 def increment_lineno(node: _T, n: int = 1) -> _T: ...
@@ -1915,8 +1899,12 @@ if sys.version_info >= (3, 14):
     def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ...
 
 class NodeVisitor:
+    # All visit methods below can be overwritten by subclasses and return an
+    # arbitrary value, which is passed to the caller.
     def visit(self, node: AST) -> Any: ...
     def generic_visit(self, node: AST) -> Any: ...
+    # The following visit methods are not defined on NodeVisitor, but can
+    # be implemented by subclasses and are called during a visit if defined.
     def visit_Module(self, node: Module) -> Any: ...
     def visit_Interactive(self, node: Interactive) -> Any: ...
     def visit_Expression(self, node: Expression) -> Any: ...
@@ -2059,8 +2047,10 @@ class NodeTransformer(NodeVisitor):
     #       The usual return type is AST | None, but Iterable[AST]
     #       is also allowed in some cases -- this needs to be mapped.
 
-if sys.version_info >= (3, 9):
-    def unparse(ast_obj: AST) -> str: ...
+def unparse(ast_obj: AST) -> str: ...
 
-if sys.version_info >= (3, 9):
+if sys.version_info >= (3, 14):
+    def main(args: Sequence[str] | None = None) -> None: ...
+
+else:
     def main() -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi
index e47f640a1f9b..f9118608060e 100644
--- a/mypy/typeshed/stdlib/asyncio/__init__.pyi
+++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi
@@ -18,10 +18,11 @@ from .runners import *
 from .streams import *
 from .subprocess import *
 from .tasks import *
+from .threads import *
 from .transports import *
 
-if sys.version_info >= (3, 9):
-    from .threads import *
+if sys.version_info >= (3, 14):
+    from .graph import *
 
 if sys.version_info >= (3, 11):
     from .taskgroups import *
@@ -34,6 +35,7 @@ else:
 
 if sys.platform == "win32":
     if sys.version_info >= (3, 14):
+
         __all__ = (
             "BaseEventLoop",  # from base_events
             "Server",  # from base_events
@@ -62,6 +64,13 @@ if sys.platform == "win32":
             "Future",  # from futures
             "wrap_future",  # from futures
             "isfuture",  # from futures
+            "future_discard_from_awaited_by",  # from futures
+            "future_add_to_awaited_by",  # from futures
+            "capture_call_graph",  # from graph
+            "format_call_graph",  # from graph
+            "print_call_graph",  # from graph
+            "FrameCallGraphEntry",  # from graph
+            "FutureCallGraph",  # from graph
             "Lock",  # from locks
             "Event",  # from locks
             "Condition",  # from locks
@@ -412,7 +421,7 @@ if sys.platform == "win32":
             "WindowsSelectorEventLoopPolicy",  # from windows_events
             "WindowsProactorEventLoopPolicy",  # from windows_events
         )
-    elif sys.version_info >= (3, 9):
+    else:
         __all__ = (
             "BaseEventLoop",  # from base_events
             "Server",  # from base_events
@@ -499,91 +508,6 @@ if sys.platform == "win32":
             "WindowsSelectorEventLoopPolicy",  # from windows_events
             "WindowsProactorEventLoopPolicy",  # from windows_events
         )
-    else:
-        __all__ = (
-            "BaseEventLoop",  # from base_events
-            "coroutine",  # from coroutines
-            "iscoroutinefunction",  # from coroutines
-            "iscoroutine",  # from coroutines
-            "AbstractEventLoopPolicy",  # from events
-            "AbstractEventLoop",  # from events
-            "AbstractServer",  # from events
-            "Handle",  # from events
-            "TimerHandle",  # from events
-            "get_event_loop_policy",  # from events
-            "set_event_loop_policy",  # from events
-            "get_event_loop",  # from events
-            "set_event_loop",  # from events
-            "new_event_loop",  # from events
-            "get_child_watcher",  # from events
-            "set_child_watcher",  # from events
-            "_set_running_loop",  # from events
-            "get_running_loop",  # from events
-            "_get_running_loop",  # from events
-            "CancelledError",  # from exceptions
-            "InvalidStateError",  # from exceptions
-            "TimeoutError",  # from exceptions
-            "IncompleteReadError",  # from exceptions
-            "LimitOverrunError",  # from exceptions
-            "SendfileNotAvailableError",  # from exceptions
-            "Future",  # from futures
-            "wrap_future",  # from futures
-            "isfuture",  # from futures
-            "Lock",  # from locks
-            "Event",  # from locks
-            "Condition",  # from locks
-            "Semaphore",  # from locks
-            "BoundedSemaphore",  # from locks
-            "BaseProtocol",  # from protocols
-            "Protocol",  # from protocols
-            "DatagramProtocol",  # from protocols
-            "SubprocessProtocol",  # from protocols
-            "BufferedProtocol",  # from protocols
-            "run",  # from runners
-            "Queue",  # from queues
-            "PriorityQueue",  # from queues
-            "LifoQueue",  # from queues
-            "QueueFull",  # from queues
-            "QueueEmpty",  # from queues
-            "StreamReader",  # from streams
-            "StreamWriter",  # from streams
-            "StreamReaderProtocol",  # from streams
-            "open_connection",  # from streams
-            "start_server",  # from streams
-            "create_subprocess_exec",  # from subprocess
-            "create_subprocess_shell",  # from subprocess
-            "Task",  # from tasks
-            "create_task",  # from tasks
-            "FIRST_COMPLETED",  # from tasks
-            "FIRST_EXCEPTION",  # from tasks
-            "ALL_COMPLETED",  # from tasks
-            "wait",  # from tasks
-            "wait_for",  # from tasks
-            "as_completed",  # from tasks
-            "sleep",  # from tasks
-            "gather",  # from tasks
-            "shield",  # from tasks
-            "ensure_future",  # from tasks
-            "run_coroutine_threadsafe",  # from tasks
-            "current_task",  # from tasks
-            "all_tasks",  # from tasks
-            "_register_task",  # from tasks
-            "_unregister_task",  # from tasks
-            "_enter_task",  # from tasks
-            "_leave_task",  # from tasks
-            "BaseTransport",  # from transports
-            "ReadTransport",  # from transports
-            "WriteTransport",  # from transports
-            "Transport",  # from transports
-            "DatagramTransport",  # from transports
-            "SubprocessTransport",  # from transports
-            "SelectorEventLoop",  # from windows_events
-            "ProactorEventLoop",  # from windows_events
-            "IocpProactor",  # from windows_events
-            "DefaultEventLoopPolicy",  # from windows_events
-            "WindowsSelectorEventLoopPolicy",  # from windows_events
-            "WindowsProactorEventLoopPolicy",  # from windows_events
-        )
 else:
     if sys.version_info >= (3, 14):
         __all__ = (
@@ -614,6 +538,13 @@ else:
             "Future",  # from futures
             "wrap_future",  # from futures
             "isfuture",  # from futures
+            "future_discard_from_awaited_by",  # from futures
+            "future_add_to_awaited_by",  # from futures
+            "capture_call_graph",  # from graph
+            "format_call_graph",  # from graph
+            "print_call_graph",  # from graph
+            "FrameCallGraphEntry",  # from graph
+            "FutureCallGraph",  # from graph
             "Lock",  # from locks
             "Event",  # from locks
             "Condition",  # from locks
@@ -974,7 +905,7 @@ else:
             "ThreadedChildWatcher",  # from unix_events
             "DefaultEventLoopPolicy",  # from unix_events
         )
-    elif sys.version_info >= (3, 9):
+    else:
         __all__ = (
             "BaseEventLoop",  # from base_events
             "Server",  # from base_events
@@ -1065,94 +996,6 @@ else:
             "ThreadedChildWatcher",  # from unix_events
             "DefaultEventLoopPolicy",  # from unix_events
         )
-    else:
-        __all__ = (
-            "BaseEventLoop",  # from base_events
-            "coroutine",  # from coroutines
-            "iscoroutinefunction",  # from coroutines
-            "iscoroutine",  # from coroutines
-            "AbstractEventLoopPolicy",  # from events
-            "AbstractEventLoop",  # from events
-            "AbstractServer",  # from events
-            "Handle",  # from events
-            "TimerHandle",  # from events
-            "get_event_loop_policy",  # from events
-            "set_event_loop_policy",  # from events
-            "get_event_loop",  # from events
-            "set_event_loop",  # from events
-            "new_event_loop",  # from events
-            "get_child_watcher",  # from events
-            "set_child_watcher",  # from events
-            "_set_running_loop",  # from events
-            "get_running_loop",  # from events
-            "_get_running_loop",  # from events
-            "CancelledError",  # from exceptions
-            "InvalidStateError",  # from exceptions
-            "TimeoutError",  # from exceptions
-            "IncompleteReadError",  # from exceptions
-            "LimitOverrunError",  # from exceptions
-            "SendfileNotAvailableError",  # from exceptions
-            "Future",  # from futures
-            "wrap_future",  # from futures
-            "isfuture",  # from futures
-            "Lock",  # from locks
-            "Event",  # from locks
-            "Condition",  # from locks
-            "Semaphore",  # from locks
-            "BoundedSemaphore",  # from locks
-            "BaseProtocol",  # from protocols
-            "Protocol",  # from protocols
-            "DatagramProtocol",  # from protocols
-            "SubprocessProtocol",  # from protocols
-            "BufferedProtocol",  # from protocols
-            "run",  # from runners
-            "Queue",  # from queues
-            "PriorityQueue",  # from queues
-            "LifoQueue",  # from queues
-            "QueueFull",  # from queues
-            "QueueEmpty",  # from queues
-            "StreamReader",  # from streams
-            "StreamWriter",  # from streams
-            "StreamReaderProtocol",  # from streams
-            "open_connection",  # from streams
-            "start_server",  # from streams
-            "open_unix_connection",  # from streams
-            "start_unix_server",  # from streams
-            "create_subprocess_exec",  # from subprocess
-            "create_subprocess_shell",  # from subprocess
-            "Task",  # from tasks
-            "create_task",  # from tasks
-            "FIRST_COMPLETED",  # from tasks
-            "FIRST_EXCEPTION",  # from tasks
-            "ALL_COMPLETED",  # from tasks
-            "wait",  # from tasks
-            "wait_for",  # from tasks
-            "as_completed",  # from tasks
-            "sleep",  # from tasks
-            "gather",  # from tasks
-            "shield",  # from tasks
-            "ensure_future",  # from tasks
-            "run_coroutine_threadsafe",  # from tasks
-            "current_task",  # from tasks
-            "all_tasks",  # from tasks
-            "_register_task",  # from tasks
-            "_unregister_task",  # from tasks
-            "_enter_task",  # from tasks
-            "_leave_task",  # from tasks
-            "BaseTransport",  # from transports
-            "ReadTransport",  # from transports
-            "WriteTransport",  # from transports
-            "Transport",  # from transports
-            "DatagramTransport",  # from transports
-            "SubprocessTransport",  # from transports
-            "SelectorEventLoop",  # from unix_events
-            "AbstractChildWatcher",  # from unix_events
-            "SafeChildWatcher",  # from unix_events
-            "FastChildWatcher",  # from unix_events
-            "MultiLoopChildWatcher",  # from unix_events
-            "ThreadedChildWatcher",  # from unix_events
-            "DefaultEventLoopPolicy",  # from unix_events
-        )
 
 _T_co = TypeVar("_T_co", covariant=True)
 
diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi
index 9527e9d052aa..cad7dde40b01 100644
--- a/mypy/typeshed/stdlib/asyncio/base_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi
@@ -15,10 +15,7 @@ from typing import IO, Any, Literal, TypeVar, overload
 from typing_extensions import TypeAlias, TypeVarTuple, Unpack
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
-if sys.version_info >= (3, 9):
-    __all__ = ("BaseEventLoop", "Server")
-else:
-    __all__ = ("BaseEventLoop",)
+__all__ = ("BaseEventLoop", "Server")
 
 _T = TypeVar("_T")
 _Ts = TypeVarTuple("_Ts")
@@ -485,7 +482,7 @@ class BaseEventLoop(AbstractEventLoop):
     def set_debug(self, enabled: bool) -> None: ...
     if sys.version_info >= (3, 12):
         async def shutdown_default_executor(self, timeout: float | None = None) -> None: ...
-    elif sys.version_info >= (3, 9):
+    else:
         async def shutdown_default_executor(self) -> None: ...
 
     def __del__(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi
index a9f7d24237a4..af43d2f5937d 100644
--- a/mypy/typeshed/stdlib/asyncio/events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/events.pyi
@@ -21,7 +21,9 @@ from .futures import Future
 from .protocols import BaseProtocol
 from .tasks import Task
 from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport
-from .unix_events import AbstractChildWatcher
+
+if sys.version_info < (3, 14):
+    from .unix_events import AbstractChildWatcher
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.version_info >= (3, 14):
@@ -138,27 +140,19 @@ class AbstractEventLoop:
     @abstractmethod
     async def shutdown_asyncgens(self) -> None: ...
     # Methods scheduling callbacks.  All these return Handles.
-    if sys.version_info >= (3, 9):  # "context" added in 3.9.10/3.10.2
-        @abstractmethod
-        def call_soon(
-            self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
-        ) -> Handle: ...
-        @abstractmethod
-        def call_later(
-            self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
-        ) -> TimerHandle: ...
-        @abstractmethod
-        def call_at(
-            self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
-        ) -> TimerHandle: ...
-    else:
-        @abstractmethod
-        def call_soon(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ...
-        @abstractmethod
-        def call_later(self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ...
-        @abstractmethod
-        def call_at(self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ...
-
+    # "context" added in 3.9.10/3.10.2 for call_*
+    @abstractmethod
+    def call_soon(
+        self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
+    ) -> Handle: ...
+    @abstractmethod
+    def call_later(
+        self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
+    ) -> TimerHandle: ...
+    @abstractmethod
+    def call_at(
+        self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
+    ) -> TimerHandle: ...
     @abstractmethod
     def time(self) -> float: ...
     # Future methods
@@ -179,15 +173,11 @@ class AbstractEventLoop:
     @abstractmethod
     def get_task_factory(self) -> _TaskFactory | None: ...
     # Methods for interacting with threads
-    if sys.version_info >= (3, 9):  # "context" added in 3.9.10/3.10.2
-        @abstractmethod
-        def call_soon_threadsafe(
-            self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
-        ) -> Handle: ...
-    else:
-        @abstractmethod
-        def call_soon_threadsafe(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ...
-
+    # "context" added in 3.9.10/3.10.2
+    @abstractmethod
+    def call_soon_threadsafe(
+        self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None
+    ) -> Handle: ...
     @abstractmethod
     def run_in_executor(self, executor: Executor | None, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ...
     @abstractmethod
@@ -607,9 +597,8 @@ class AbstractEventLoop:
     def get_debug(self) -> bool: ...
     @abstractmethod
     def set_debug(self, enabled: bool) -> None: ...
-    if sys.version_info >= (3, 9):
-        @abstractmethod
-        async def shutdown_default_executor(self) -> None: ...
+    @abstractmethod
+    async def shutdown_default_executor(self) -> None: ...
 
 class AbstractEventLoopPolicy:
     @abstractmethod
diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi
index cb2785012fb2..644d2d0e94ca 100644
--- a/mypy/typeshed/stdlib/asyncio/futures.pyi
+++ b/mypy/typeshed/stdlib/asyncio/futures.pyi
@@ -1,3 +1,4 @@
+import sys
 from _asyncio import Future as Future
 from concurrent.futures._base import Future as _ConcurrentFuture
 from typing import Any, TypeVar
@@ -6,7 +7,12 @@ from typing_extensions import TypeIs
 from .events import AbstractEventLoop
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
-__all__ = ("Future", "wrap_future", "isfuture")
+if sys.version_info >= (3, 14):
+    from _asyncio import future_add_to_awaited_by, future_discard_from_awaited_by
+
+    __all__ = ("Future", "wrap_future", "isfuture", "future_discard_from_awaited_by", "future_add_to_awaited_by")
+else:
+    __all__ = ("Future", "wrap_future", "isfuture")
 
 _T = TypeVar("_T")
 
diff --git a/mypy/typeshed/stdlib/asyncio/graph.pyi b/mypy/typeshed/stdlib/asyncio/graph.pyi
new file mode 100644
index 000000000000..cb2cf0174995
--- /dev/null
+++ b/mypy/typeshed/stdlib/asyncio/graph.pyi
@@ -0,0 +1,26 @@
+from _typeshed import SupportsWrite
+from asyncio import Future
+from dataclasses import dataclass
+from types import FrameType
+from typing import Any, overload
+
+__all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph")
+
+@dataclass(frozen=True)
+class FrameCallGraphEntry:
+    frame: FrameType
+
+@dataclass(frozen=True)
+class FutureCallGraph:
+    future: Future[Any]
+    call_stack: tuple[FrameCallGraphEntry, ...]
+    awaited_by: tuple[FutureCallGraph, ...]
+
+@overload
+def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
+@overload
+def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
+def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ...
+def print_call_graph(
+    future: Future[Any] | None = None, /, *, file: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None
+) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi
index 4eef69dee5c3..17390b0c5a0e 100644
--- a/mypy/typeshed/stdlib/asyncio/locks.pyi
+++ b/mypy/typeshed/stdlib/asyncio/locks.pyi
@@ -2,7 +2,7 @@ import enum
 import sys
 from _typeshed import Unused
 from collections import deque
-from collections.abc import Callable, Generator
+from collections.abc import Callable
 from types import TracebackType
 from typing import Any, Literal, TypeVar
 from typing_extensions import Self
@@ -23,29 +23,11 @@ else:
 
 _T = TypeVar("_T")
 
-if sys.version_info >= (3, 9):
-    class _ContextManagerMixin:
-        async def __aenter__(self) -> None: ...
-        async def __aexit__(
-            self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
-        ) -> None: ...
-
-else:
-    class _ContextManager:
-        def __init__(self, lock: Lock | Semaphore) -> None: ...
-        def __enter__(self) -> None: ...
-        def __exit__(self, *args: Unused) -> None: ...
-
-    class _ContextManagerMixin:
-        # Apparently this exists to *prohibit* use as a context manager.
-        # def __enter__(self) -> NoReturn: ... see: https://github.com/python/typing/issues/1043
-        # def __exit__(self, *args: Any) -> None: ...
-        def __iter__(self) -> Generator[Any, None, _ContextManager]: ...
-        def __await__(self) -> Generator[Any, None, _ContextManager]: ...
-        async def __aenter__(self) -> None: ...
-        async def __aexit__(
-            self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
-        ) -> None: ...
+class _ContextManagerMixin:
+    async def __aenter__(self) -> None: ...
+    async def __aexit__(
+        self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
+    ) -> None: ...
 
 class Lock(_ContextManagerMixin, _LoopBoundMixin):
     _waiters: deque[Future[Any]] | None
diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi
index d287fe779297..63cd98f53da3 100644
--- a/mypy/typeshed/stdlib/asyncio/queues.pyi
+++ b/mypy/typeshed/stdlib/asyncio/queues.pyi
@@ -1,10 +1,8 @@
 import sys
 from asyncio.events import AbstractEventLoop
+from types import GenericAlias
 from typing import Any, Generic, TypeVar
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 if sys.version_info >= (3, 10):
     from .mixins import _LoopBoundMixin
 else:
@@ -48,8 +46,7 @@ class Queue(Generic[_T], _LoopBoundMixin):  # noqa: Y059
     def get_nowait(self) -> _T: ...
     async def join(self) -> None: ...
     def task_done(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, type: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, type: Any, /) -> GenericAlias: ...
     if sys.version_info >= (3, 13):
         def shutdown(self, immediate: bool = False) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi
index f6ee109915e0..e42151213e69 100644
--- a/mypy/typeshed/stdlib/asyncio/tasks.pyi
+++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi
@@ -407,10 +407,8 @@ else:
 
 if sys.version_info >= (3, 12):
     _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co]
-elif sys.version_info >= (3, 9):
-    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co]
 else:
-    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co]
+    _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co]
 
 def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ...
 
diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
index abf5d7ffd699..79f99fbe37f0 100644
--- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
@@ -30,7 +30,7 @@ if sys.platform != "win32":
             "DefaultEventLoopPolicy",
             "EventLoop",
         )
-    elif sys.version_info >= (3, 9):
+    else:
         # adds PidfdChildWatcher
         __all__ = (
             "SelectorEventLoop",
@@ -42,16 +42,6 @@ if sys.platform != "win32":
             "ThreadedChildWatcher",
             "DefaultEventLoopPolicy",
         )
-    else:
-        __all__ = (
-            "SelectorEventLoop",
-            "AbstractChildWatcher",
-            "SafeChildWatcher",
-            "FastChildWatcher",
-            "MultiLoopChildWatcher",
-            "ThreadedChildWatcher",
-            "DefaultEventLoopPolicy",
-        )
 
 # This is also technically not available on Win,
 # but other parts of typeshed need this definition.
@@ -239,16 +229,15 @@ if sys.platform != "win32":
             def remove_child_handler(self, pid: int) -> bool: ...
             def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
 
-        if sys.version_info >= (3, 9):
-            class PidfdChildWatcher(AbstractChildWatcher):
-                def __enter__(self) -> Self: ...
-                def __exit__(
-                    self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
-                ) -> None: ...
-                def is_active(self) -> bool: ...
-                def close(self) -> None: ...
-                def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
-                def add_child_handler(
-                    self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
-                ) -> None: ...
-                def remove_child_handler(self, pid: int) -> bool: ...
+        class PidfdChildWatcher(AbstractChildWatcher):
+            def __enter__(self) -> Self: ...
+            def __exit__(
+                self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None
+            ) -> None: ...
+            def is_active(self) -> bool: ...
+            def close(self) -> None: ...
+            def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+            def add_child_handler(
+                self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
+            ) -> None: ...
+            def remove_child_handler(self, pid: int) -> bool: ...
diff --git a/mypy/typeshed/stdlib/base64.pyi b/mypy/typeshed/stdlib/base64.pyi
index 8be4cfe69de0..279d74a94ebe 100644
--- a/mypy/typeshed/stdlib/base64.pyi
+++ b/mypy/typeshed/stdlib/base64.pyi
@@ -56,10 +56,6 @@ def encode(input: IO[bytes], output: IO[bytes]) -> None: ...
 def encodebytes(s: ReadableBuffer) -> bytes: ...
 def decodebytes(s: ReadableBuffer) -> bytes: ...
 
-if sys.version_info < (3, 9):
-    def encodestring(s: ReadableBuffer) -> bytes: ...
-    def decodestring(s: ReadableBuffer) -> bytes: ...
-
 if sys.version_info >= (3, 13):
     def z85encode(s: ReadableBuffer) -> bytes: ...
     def z85decode(s: str | ReadableBuffer) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi
index 2004874a52b2..b73f894093ce 100644
--- a/mypy/typeshed/stdlib/bdb.pyi
+++ b/mypy/typeshed/stdlib/bdb.pyi
@@ -3,13 +3,14 @@ from _typeshed import ExcInfo, TraceFunction, Unused
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from contextlib import contextmanager
 from types import CodeType, FrameType, TracebackType
-from typing import IO, Any, Final, SupportsInt, TypeVar
-from typing_extensions import ParamSpec
+from typing import IO, Any, Final, Literal, SupportsInt, TypeVar
+from typing_extensions import ParamSpec, TypeAlias
 
 __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
 
 _T = TypeVar("_T")
 _P = ParamSpec("_P")
+_Backend: TypeAlias = Literal["settrace", "monitoring"]
 
 # A union of code-object flags at runtime.
 # The exact values of code-object flags are implementation details,
@@ -28,7 +29,12 @@ class Bdb:
     stopframe: FrameType | None
     returnframe: FrameType | None
     stoplineno: int
-    def __init__(self, skip: Iterable[str] | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        backend: _Backend
+        def __init__(self, skip: Iterable[str] | None = None, backend: _Backend = "settrace") -> None: ...
+    else:
+        def __init__(self, skip: Iterable[str] | None = None) -> None: ...
+
     def canonic(self, filename: str) -> str: ...
     def reset(self) -> None: ...
     if sys.version_info >= (3, 12):
@@ -85,6 +91,11 @@ class Bdb:
     def runeval(self, expr: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> None: ...
     def runctx(self, cmd: str | CodeType, globals: dict[str, Any] | None, locals: Mapping[str, Any] | None) -> None: ...
     def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> _T | None: ...
+    if sys.version_info >= (3, 14):
+        def start_trace(self) -> None: ...
+        def stop_trace(self) -> None: ...
+        def disable_current_event(self) -> None: ...
+        def restart_events(self) -> None: ...
 
 class Breakpoint:
     next: int
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index dc8ddb8fe7a8..d874edd8f83a 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -5,6 +5,7 @@ import sys
 import types
 from _collections_abc import dict_items, dict_keys, dict_values
 from _typeshed import (
+    AnnotationForm,
     AnyStr_co,
     ConvertibleToFloat,
     ConvertibleToInt,
@@ -32,11 +33,11 @@ from _typeshed import (
 )
 from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized
 from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
-from types import CellType, CodeType, TracebackType
+from types import CellType, CodeType, GenericAlias, TracebackType
 
 # mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping}
 # are imported from collections.abc in builtins.pyi
-from typing import (  # noqa: Y022
+from typing import (  # noqa: Y022,UP035
     IO,
     Any,
     BinaryIO,
@@ -71,8 +72,8 @@ from typing_extensions import (  # noqa: Y023
     deprecated,
 )
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
+if sys.version_info >= (3, 14):
+    from _typeshed import AnnotateFunc
 
 _T = TypeVar("_T")
 _I = TypeVar("_I", default=int)
@@ -217,6 +218,9 @@ class type:
         def __ror__(self, value: Any, /) -> types.UnionType: ...
     if sys.version_info >= (3, 12):
         __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
+    __annotations__: dict[str, AnnotationForm]
+    if sys.version_info >= (3, 14):
+        __annotate__: AnnotateFunc | None
 
 class super:
     @overload
@@ -376,10 +380,8 @@ class float:
     def __rpow__(self, value: float, mod: None = None, /) -> Any: ...
     def __getnewargs__(self) -> tuple[float]: ...
     def __trunc__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __ceil__(self) -> int: ...
-        def __floor__(self) -> int: ...
-
+    def __ceil__(self) -> int: ...
+    def __floor__(self) -> int: ...
     @overload
     def __round__(self, ndigits: None = None, /) -> int: ...
     @overload
@@ -397,6 +399,9 @@ class float:
     def __abs__(self) -> float: ...
     def __hash__(self) -> int: ...
     def __bool__(self) -> bool: ...
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ...
 
 class complex:
     # Python doesn't currently accept SupportsComplex for the second argument
@@ -432,6 +437,9 @@ class complex:
     def __bool__(self) -> bool: ...
     if sys.version_info >= (3, 11):
         def __complex__(self) -> complex: ...
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def from_number(cls, number: complex | SupportsComplex | SupportsFloat | SupportsIndex, /) -> Self: ...
 
 class _FormatMapMapping(Protocol):
     def __getitem__(self, key: str, /) -> Any: ...
@@ -478,10 +486,9 @@ class str(Sequence[str]):
         def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ...  # type: ignore[misc]
     else:
         def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ...  # type: ignore[misc]
-    if sys.version_info >= (3, 9):
-        def removeprefix(self, prefix: str, /) -> str: ...  # type: ignore[misc]
-        def removesuffix(self, suffix: str, /) -> str: ...  # type: ignore[misc]
 
+    def removeprefix(self, prefix: str, /) -> str: ...  # type: ignore[misc]
+    def removesuffix(self, suffix: str, /) -> str: ...  # type: ignore[misc]
     def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
     def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
     def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ...  # type: ignore[misc]
@@ -568,10 +575,8 @@ class bytes(Sequence[int]):
     def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ...
     def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ...
     def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ...
-    if sys.version_info >= (3, 9):
-        def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ...
-        def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ...
-
+    def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ...
+    def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ...
     def rfind(
         self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
     ) -> int: ...
@@ -673,10 +678,8 @@ class bytearray(MutableSequence[int]):
     def partition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ...
     def pop(self, index: int = -1, /) -> int: ...
     def remove(self, value: int, /) -> None: ...
-    if sys.version_info >= (3, 9):
-        def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ...
-        def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ...
-
+    def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ...
+    def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ...
     def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ...
     def rfind(
         self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
@@ -911,12 +914,12 @@ class tuple(Sequence[_T_co]):
     def __rmul__(self, value: SupportsIndex, /) -> tuple[_T_co, ...]: ...
     def count(self, value: Any, /) -> int: ...
     def index(self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # Doesn't exist at runtime, but deleting this breaks mypy and pyright. See:
 # https://github.com/python/typeshed/issues/7580
 # https://github.com/python/mypy/issues/8240
+# Obsolete, use types.FunctionType instead.
 @final
 @type_check_only
 class function:
@@ -930,8 +933,10 @@ class function:
     def __globals__(self) -> dict[str, Any]: ...
     __name__: str
     __qualname__: str
-    __annotations__: dict[str, Any]
-    __kwdefaults__: dict[str, Any]
+    __annotations__: dict[str, AnnotationForm]
+    if sys.version_info >= (3, 14):
+        __annotate__: AnnotateFunc | None
+    __kwdefaults__: dict[str, Any] | None
     if sys.version_info >= (3, 10):
         @property
         def __builtins__(self) -> dict[str, Any]: ...
@@ -939,6 +944,26 @@ class function:
         __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
 
     __module__: str
+    if sys.version_info >= (3, 13):
+        def __new__(
+            cls,
+            code: CodeType,
+            globals: dict[str, Any],
+            name: str | None = None,
+            argdefs: tuple[object, ...] | None = None,
+            closure: tuple[CellType, ...] | None = None,
+            kwdefaults: dict[str, object] | None = None,
+        ) -> Self: ...
+    else:
+        def __new__(
+            cls,
+            code: CodeType,
+            globals: dict[str, Any],
+            name: str | None = None,
+            argdefs: tuple[object, ...] | None = None,
+            closure: tuple[CellType, ...] | None = None,
+        ) -> Self: ...
+
     # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any.
     def __get__(self, instance: object, owner: type | None = None, /) -> Any: ...
 
@@ -994,8 +1019,7 @@ class list(MutableSequence[_T]):
     def __lt__(self, value: list[_T], /) -> bool: ...
     def __le__(self, value: list[_T], /) -> bool: ...
     def __eq__(self, value: object, /) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class dict(MutableMapping[_KT, _VT]):
     # __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics
@@ -1064,21 +1088,20 @@ class dict(MutableMapping[_KT, _VT]):
     def __eq__(self, value: object, /) -> bool: ...
     def __reversed__(self) -> Iterator[_KT]: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
-        @overload
-        def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
-        @overload
-        def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
-        @overload
-        def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
-        @overload
-        def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
-        # dict.__ior__ should be kept roughly in line with MutableMapping.update()
-        @overload  # type: ignore[misc]
-        def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
-        @overload
-        def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    @overload
+    def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
+    @overload
+    def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
+    @overload
+    def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
+    @overload
+    def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
+    # dict.__ior__ should be kept roughly in line with MutableMapping.update()
+    @overload  # type: ignore[misc]
+    def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
+    @overload
+    def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
 
 class set(MutableSet[_T]):
     @overload
@@ -1117,8 +1140,7 @@ class set(MutableSet[_T]):
     def __gt__(self, value: AbstractSet[object], /) -> bool: ...
     def __eq__(self, value: object, /) -> bool: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class frozenset(AbstractSet[_T_co]):
     @overload
@@ -1146,15 +1168,13 @@ class frozenset(AbstractSet[_T_co]):
     def __gt__(self, value: AbstractSet[object], /) -> bool: ...
     def __eq__(self, value: object, /) -> bool: ...
     def __hash__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class enumerate(Iterator[tuple[int, _T]]):
     def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> tuple[int, _T]: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @final
 class range(Sequence[int]):
@@ -1199,6 +1219,9 @@ class property:
     def getter(self, fget: Callable[[Any], Any], /) -> property: ...
     def setter(self, fset: Callable[[Any, Any], None], /) -> property: ...
     def deleter(self, fdel: Callable[[Any], None], /) -> property: ...
+    @overload
+    def __get__(self, instance: None, owner: type, /) -> Self: ...
+    @overload
     def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ...
     def __set__(self, instance: Any, value: Any, /) -> None: ...
     def __delete__(self, instance: Any, /) -> None: ...
@@ -1409,48 +1432,108 @@ license: _sitebuiltins._Printer
 def locals() -> dict[str, Any]: ...
 
 class map(Iterator[_S]):
-    @overload
-    def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ...
-    @overload
-    def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ...
-    @overload
-    def __new__(
-        cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /
-    ) -> Self: ...
-    @overload
-    def __new__(
-        cls,
-        func: Callable[[_T1, _T2, _T3, _T4], _S],
-        iterable: Iterable[_T1],
-        iter2: Iterable[_T2],
-        iter3: Iterable[_T3],
-        iter4: Iterable[_T4],
-        /,
-    ) -> Self: ...
-    @overload
-    def __new__(
-        cls,
-        func: Callable[[_T1, _T2, _T3, _T4, _T5], _S],
-        iterable: Iterable[_T1],
-        iter2: Iterable[_T2],
-        iter3: Iterable[_T3],
-        iter4: Iterable[_T4],
-        iter5: Iterable[_T5],
-        /,
-    ) -> Self: ...
-    @overload
-    def __new__(
-        cls,
-        func: Callable[..., _S],
-        iterable: Iterable[Any],
-        iter2: Iterable[Any],
-        iter3: Iterable[Any],
-        iter4: Iterable[Any],
-        iter5: Iterable[Any],
-        iter6: Iterable[Any],
-        /,
-        *iterables: Iterable[Any],
-    ) -> Self: ...
+    # 3.14 adds `strict` argument.
+    if sys.version_info >= (3, 14):
+        @overload
+        def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /, *, strict: bool = False) -> Self: ...
+        @overload
+        def __new__(
+            cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = False
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[[_T1, _T2, _T3], _S],
+            iterable: Iterable[_T1],
+            iter2: Iterable[_T2],
+            iter3: Iterable[_T3],
+            /,
+            *,
+            strict: bool = False,
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[[_T1, _T2, _T3, _T4], _S],
+            iterable: Iterable[_T1],
+            iter2: Iterable[_T2],
+            iter3: Iterable[_T3],
+            iter4: Iterable[_T4],
+            /,
+            *,
+            strict: bool = False,
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[[_T1, _T2, _T3, _T4, _T5], _S],
+            iterable: Iterable[_T1],
+            iter2: Iterable[_T2],
+            iter3: Iterable[_T3],
+            iter4: Iterable[_T4],
+            iter5: Iterable[_T5],
+            /,
+            *,
+            strict: bool = False,
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[..., _S],
+            iterable: Iterable[Any],
+            iter2: Iterable[Any],
+            iter3: Iterable[Any],
+            iter4: Iterable[Any],
+            iter5: Iterable[Any],
+            iter6: Iterable[Any],
+            /,
+            *iterables: Iterable[Any],
+            strict: bool = False,
+        ) -> Self: ...
+    else:
+        @overload
+        def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ...
+        @overload
+        def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ...
+        @overload
+        def __new__(
+            cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[[_T1, _T2, _T3, _T4], _S],
+            iterable: Iterable[_T1],
+            iter2: Iterable[_T2],
+            iter3: Iterable[_T3],
+            iter4: Iterable[_T4],
+            /,
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[[_T1, _T2, _T3, _T4, _T5], _S],
+            iterable: Iterable[_T1],
+            iter2: Iterable[_T2],
+            iter3: Iterable[_T3],
+            iter4: Iterable[_T4],
+            iter5: Iterable[_T5],
+            /,
+        ) -> Self: ...
+        @overload
+        def __new__(
+            cls,
+            func: Callable[..., _S],
+            iterable: Iterable[Any],
+            iter2: Iterable[Any],
+            iter3: Iterable[Any],
+            iter4: Iterable[Any],
+            iter5: Iterable[Any],
+            iter6: Iterable[Any],
+            /,
+            *iterables: Iterable[Any],
+        ) -> Self: ...
+
     def __iter__(self) -> Self: ...
     def __next__(self) -> _S: ...
 
@@ -2005,27 +2088,27 @@ if sys.version_info >= (3, 11):
         def exceptions(self) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]: ...
         @overload
         def subgroup(
-            self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
+            self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
         ) -> ExceptionGroup[_ExceptionT] | None: ...
         @overload
         def subgroup(
-            self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], /
+            self, matcher_value: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], /
         ) -> BaseExceptionGroup[_BaseExceptionT] | None: ...
         @overload
         def subgroup(
-            self, condition: Callable[[_BaseExceptionT_co | Self], bool], /
+            self, matcher_value: Callable[[_BaseExceptionT_co | Self], bool], /
         ) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ...
         @overload
         def split(
-            self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
+            self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
         ) -> tuple[ExceptionGroup[_ExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ...
         @overload
         def split(
-            self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], /
+            self, matcher_value: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], /
         ) -> tuple[BaseExceptionGroup[_BaseExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ...
         @overload
         def split(
-            self, condition: Callable[[_BaseExceptionT_co | Self], bool], /
+            self, matcher_value: Callable[[_BaseExceptionT_co | Self], bool], /
         ) -> tuple[BaseExceptionGroup[_BaseExceptionT_co] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ...
         # In reality it is `NonEmptySequence`:
         @overload
@@ -2042,17 +2125,19 @@ if sys.version_info >= (3, 11):
         # We accept a narrower type, but that's OK.
         @overload  # type: ignore[override]
         def subgroup(
-            self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
+            self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
         ) -> ExceptionGroup[_ExceptionT] | None: ...
         @overload
-        def subgroup(self, condition: Callable[[_ExceptionT_co | Self], bool], /) -> ExceptionGroup[_ExceptionT_co] | None: ...
+        def subgroup(
+            self, matcher_value: Callable[[_ExceptionT_co | Self], bool], /
+        ) -> ExceptionGroup[_ExceptionT_co] | None: ...
         @overload  # type: ignore[override]
         def split(
-            self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
+            self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], /
         ) -> tuple[ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None]: ...
         @overload
         def split(
-            self, condition: Callable[[_ExceptionT_co | Self], bool], /
+            self, matcher_value: Callable[[_ExceptionT_co | Self], bool], /
         ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ...
 
 if sys.version_info >= (3, 13):
diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi
index 2f869f9697f4..0f9d00fbc633 100644
--- a/mypy/typeshed/stdlib/bz2.pyi
+++ b/mypy/typeshed/stdlib/bz2.pyi
@@ -1,18 +1,21 @@
-import _compression
 import sys
 from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor
-from _compression import BaseStream
 from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from collections.abc import Iterable
-from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, overload
+from typing import IO, Literal, Protocol, SupportsIndex, TextIO, overload
 from typing_extensions import Self, TypeAlias
 
+if sys.version_info >= (3, 14):
+    from compression._common._streams import BaseStream, _Reader
+else:
+    from _compression import BaseStream, _Reader
+
 __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"]
 
 # The following attributes and methods are optional:
 # def fileno(self) -> int: ...
 # def close(self) -> object: ...
-class _ReadableFileobj(_compression._Reader, Protocol): ...
+class _ReadableFileobj(_Reader, Protocol): ...
 
 class _WritableFileobj(Protocol):
     def write(self, b: bytes, /) -> object: ...
@@ -94,33 +97,14 @@ def open(
 
 class BZ2File(BaseStream, IO[bytes]):
     def __enter__(self) -> Self: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __init__(self, filename: _WritableFileobj, mode: _WriteBinaryMode, *, compresslevel: int = 9) -> None: ...
-        @overload
-        def __init__(self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", *, compresslevel: int = 9) -> None: ...
-        @overload
-        def __init__(
-            self, filename: StrOrBytesPath, mode: _ReadBinaryMode | _WriteBinaryMode = "r", *, compresslevel: int = 9
-        ) -> None: ...
-    else:
-        @overload
-        def __init__(
-            self, filename: _WritableFileobj, mode: _WriteBinaryMode, buffering: Any | None = None, compresslevel: int = 9
-        ) -> None: ...
-        @overload
-        def __init__(
-            self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", buffering: Any | None = None, compresslevel: int = 9
-        ) -> None: ...
-        @overload
-        def __init__(
-            self,
-            filename: StrOrBytesPath,
-            mode: _ReadBinaryMode | _WriteBinaryMode = "r",
-            buffering: Any | None = None,
-            compresslevel: int = 9,
-        ) -> None: ...
-
+    @overload
+    def __init__(self, filename: _WritableFileobj, mode: _WriteBinaryMode, *, compresslevel: int = 9) -> None: ...
+    @overload
+    def __init__(self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", *, compresslevel: int = 9) -> None: ...
+    @overload
+    def __init__(
+        self, filename: StrOrBytesPath, mode: _ReadBinaryMode | _WriteBinaryMode = "r", *, compresslevel: int = 9
+    ) -> None: ...
     def read(self, size: int | None = -1) -> bytes: ...
     def read1(self, size: int = -1) -> bytes: ...
     def readline(self, size: SupportsIndex = -1) -> bytes: ...  # type: ignore[override]
diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi
index 54971f3ae93c..0b13c8a5016d 100644
--- a/mypy/typeshed/stdlib/code.pyi
+++ b/mypy/typeshed/stdlib/code.pyi
@@ -1,15 +1,15 @@
 import sys
-from codeop import CommandCompiler
-from collections.abc import Callable, Mapping
+from codeop import CommandCompiler, compile_command as compile_command
+from collections.abc import Callable
 from types import CodeType
 from typing import Any
 
 __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", "compile_command"]
 
 class InteractiveInterpreter:
-    locals: Mapping[str, Any]  # undocumented
+    locals: dict[str, Any]  # undocumented
     compile: CommandCompiler  # undocumented
-    def __init__(self, locals: Mapping[str, Any] | None = None) -> None: ...
+    def __init__(self, locals: dict[str, Any] | None = None) -> None: ...
     def runsource(self, source: str, filename: str = "", symbol: str = "single") -> bool: ...
     def runcode(self, code: CodeType) -> None: ...
     if sys.version_info >= (3, 13):
@@ -25,11 +25,11 @@ class InteractiveConsole(InteractiveInterpreter):
     filename: str  # undocumented
     if sys.version_info >= (3, 13):
         def __init__(
-            self, locals: Mapping[str, Any] | None = None, filename: str = "", *, local_exit: bool = False
+            self, locals: dict[str, Any] | None = None, filename: str = "", *, local_exit: bool = False
         ) -> None: ...
         def push(self, line: str, filename: str | None = None) -> bool: ...
     else:
-        def __init__(self, locals: Mapping[str, Any] | None = None, filename: str = "") -> None: ...
+        def __init__(self, locals: dict[str, Any] | None = None, filename: str = "") -> None: ...
         def push(self, line: str) -> bool: ...
 
     def interact(self, banner: str | None = None, exitmsg: str | None = None) -> None: ...
@@ -40,7 +40,7 @@ if sys.version_info >= (3, 13):
     def interact(
         banner: str | None = None,
         readfunc: Callable[[str], str] | None = None,
-        local: Mapping[str, Any] | None = None,
+        local: dict[str, Any] | None = None,
         exitmsg: str | None = None,
         local_exit: bool = False,
     ) -> None: ...
@@ -49,8 +49,6 @@ else:
     def interact(
         banner: str | None = None,
         readfunc: Callable[[str], str] | None = None,
-        local: Mapping[str, Any] | None = None,
+        local: dict[str, Any] | None = None,
         exitmsg: str | None = None,
     ) -> None: ...
-
-def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ...
diff --git a/mypy/typeshed/stdlib/codeop.pyi b/mypy/typeshed/stdlib/codeop.pyi
index cfe52e9b35de..8e311343eb89 100644
--- a/mypy/typeshed/stdlib/codeop.pyi
+++ b/mypy/typeshed/stdlib/codeop.pyi
@@ -3,7 +3,11 @@ from types import CodeType
 
 __all__ = ["compile_command", "Compile", "CommandCompiler"]
 
-def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ...
+if sys.version_info >= (3, 14):
+    def compile_command(source: str, filename: str = "", symbol: str = "single", flags: int = 0) -> CodeType | None: ...
+
+else:
+    def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ...
 
 class Compile:
     flags: int
diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi
index 0f99b5c3c67e..b9e4f84ec0b6 100644
--- a/mypy/typeshed/stdlib/collections/__init__.pyi
+++ b/mypy/typeshed/stdlib/collections/__init__.pyi
@@ -1,12 +1,10 @@
 import sys
 from _collections_abc import dict_items, dict_keys, dict_values
 from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT
+from types import GenericAlias
 from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload
 from typing_extensions import Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 if sys.version_info >= (3, 10):
     from collections.abc import (
         Callable,
@@ -93,20 +91,19 @@ class UserDict(MutableMapping[_KT, _VT]):
     @classmethod
     @overload
     def fromkeys(cls, iterable: Iterable[_T], value: _S) -> UserDict[_T, _S]: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ...
-        @overload
-        def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ...
-        @overload
-        def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ...
-        @overload
-        def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ...
-        # UserDict.__ior__ should be kept roughly in line with MutableMapping.update()
-        @overload  # type: ignore[misc]
-        def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
-        @overload
-        def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
+    @overload
+    def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ...
+    @overload
+    def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ...
+    @overload
+    def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ...
+    @overload
+    def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ...
+    # UserDict.__ior__ should be kept roughly in line with MutableMapping.update()
+    @overload  # type: ignore[misc]
+    def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
+    @overload
+    def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
     if sys.version_info >= (3, 12):
         @overload
         def get(self, key: _KT, default: None = None) -> _VT | None: ...
@@ -213,10 +210,8 @@ class UserString(Sequence[UserString]):
     def lstrip(self, chars: str | None = None) -> Self: ...
     maketrans = str.maketrans
     def partition(self, sep: str) -> tuple[str, str, str]: ...
-    if sys.version_info >= (3, 9):
-        def removeprefix(self, prefix: str | UserString, /) -> Self: ...
-        def removesuffix(self, suffix: str | UserString, /) -> Self: ...
-
+    def removeprefix(self, prefix: str | UserString, /) -> Self: ...
+    def removesuffix(self, suffix: str | UserString, /) -> Self: ...
     def replace(self, old: str | UserString, new: str | UserString, maxsplit: int = -1) -> Self: ...
     def rfind(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
     def rindex(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
@@ -271,8 +266,7 @@ class deque(MutableSequence[_T]):
     def __gt__(self, value: deque[_T], /) -> bool: ...
     def __ge__(self, value: deque[_T], /) -> bool: ...
     def __eq__(self, value: object, /) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class Counter(dict[_T, int], Generic[_T]):
     @overload
@@ -387,15 +381,14 @@ class OrderedDict(dict[_KT, _VT]):
     @overload
     def pop(self, key: _KT, default: _T) -> _VT | _T: ...
     def __eq__(self, value: object, /) -> bool: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, value: dict[_KT, _VT], /) -> Self: ...
-        @overload
-        def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ...
-        @overload
-        def __ror__(self, value: dict[_KT, _VT], /) -> Self: ...
-        @overload
-        def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ...  # type: ignore[misc]
+    @overload
+    def __or__(self, value: dict[_KT, _VT], /) -> Self: ...
+    @overload
+    def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ...
+    @overload
+    def __ror__(self, value: dict[_KT, _VT], /) -> Self: ...
+    @overload
+    def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ...  # type: ignore[misc]
 
 class defaultdict(dict[_KT, _VT]):
     default_factory: Callable[[], _VT] | None
@@ -435,15 +428,14 @@ class defaultdict(dict[_KT, _VT]):
     def __missing__(self, key: _KT, /) -> _VT: ...
     def __copy__(self) -> Self: ...
     def copy(self) -> Self: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, value: dict[_KT, _VT], /) -> Self: ...
-        @overload
-        def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ...
-        @overload
-        def __ror__(self, value: dict[_KT, _VT], /) -> Self: ...
-        @overload
-        def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ...  # type: ignore[misc]
+    @overload
+    def __or__(self, value: dict[_KT, _VT], /) -> Self: ...
+    @overload
+    def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ...
+    @overload
+    def __ror__(self, value: dict[_KT, _VT], /) -> Self: ...
+    @overload
+    def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ...  # type: ignore[misc]
 
 class ChainMap(MutableMapping[_KT, _VT]):
     maps: list[MutableMapping[_KT, _VT]]
@@ -488,17 +480,16 @@ class ChainMap(MutableMapping[_KT, _VT]):
     @classmethod
     @overload
     def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> ChainMap[_T, _S]: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, other: Mapping[_KT, _VT]) -> Self: ...
-        @overload
-        def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ...
-        @overload
-        def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ...
-        @overload
-        def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ...
-        # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update()
-        @overload  # type: ignore[misc]
-        def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
-        @overload
-        def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
+    @overload
+    def __or__(self, other: Mapping[_KT, _VT]) -> Self: ...
+    @overload
+    def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ...
+    @overload
+    def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ...
+    @overload
+    def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ...
+    # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update()
+    @overload  # type: ignore[misc]
+    def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
+    @overload
+    def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
diff --git a/mypy/typeshed/stdlib/colorsys.pyi b/mypy/typeshed/stdlib/colorsys.pyi
index 443ee828ebfe..7842f80284ef 100644
--- a/mypy/typeshed/stdlib/colorsys.pyi
+++ b/mypy/typeshed/stdlib/colorsys.pyi
@@ -7,7 +7,7 @@ def hls_to_rgb(h: float, l: float, s: float) -> tuple[float, float, float]: ...
 def rgb_to_hsv(r: float, g: float, b: float) -> tuple[float, float, float]: ...
 def hsv_to_rgb(h: float, s: float, v: float) -> tuple[float, float, float]: ...
 
-# TODO undocumented
+# TODO: undocumented
 ONE_SIXTH: float
 ONE_THIRD: float
 TWO_THIRD: float
diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi
index f35c584cedfb..a599b1b23540 100644
--- a/mypy/typeshed/stdlib/compileall.pyi
+++ b/mypy/typeshed/stdlib/compileall.pyi
@@ -42,7 +42,7 @@ if sys.version_info >= (3, 10):
         hardlink_dupes: bool = False,
     ) -> bool: ...
 
-elif sys.version_info >= (3, 9):
+else:
     def compile_dir(
         dir: StrPath,
         maxlevels: int | None = None,
@@ -76,30 +76,6 @@ elif sys.version_info >= (3, 9):
         hardlink_dupes: bool = False,
     ) -> bool: ...
 
-else:
-    def compile_dir(
-        dir: StrPath,
-        maxlevels: int = 10,
-        ddir: StrPath | None = None,
-        force: bool = False,
-        rx: _SupportsSearch | None = None,
-        quiet: int = 0,
-        legacy: bool = False,
-        optimize: int = -1,
-        workers: int = 1,
-        invalidation_mode: PycInvalidationMode | None = None,
-    ) -> bool: ...
-    def compile_file(
-        fullname: StrPath,
-        ddir: StrPath | None = None,
-        force: bool = False,
-        rx: _SupportsSearch | None = None,
-        quiet: int = 0,
-        legacy: bool = False,
-        optimize: int = -1,
-        invalidation_mode: PycInvalidationMode | None = None,
-    ) -> bool: ...
-
 def compile_path(
     skip_curdir: bool = ...,
     maxlevels: int = 0,
diff --git a/mypy/typeshed/stdlib/compression/__init__.pyi b/mypy/typeshed/stdlib/compression/__init__.pyi
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/mypy/typeshed/stdlib/compression/_common/__init__.pyi b/mypy/typeshed/stdlib/compression/_common/__init__.pyi
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/mypy/typeshed/stdlib/compression/_common/_streams.pyi b/mypy/typeshed/stdlib/compression/_common/_streams.pyi
new file mode 100644
index 000000000000..6303a9b1d460
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/_common/_streams.pyi
@@ -0,0 +1,25 @@
+from _typeshed import Incomplete, WriteableBuffer
+from collections.abc import Callable
+from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
+from typing import Any, Protocol
+
+BUFFER_SIZE = DEFAULT_BUFFER_SIZE
+
+class _Reader(Protocol):
+    def read(self, n: int, /) -> bytes: ...
+    def seekable(self) -> bool: ...
+    def seek(self, n: int, /) -> Any: ...
+
+class BaseStream(BufferedIOBase): ...
+
+class DecompressReader(RawIOBase):
+    def __init__(
+        self,
+        fp: _Reader,
+        decomp_factory: Callable[..., Incomplete],  # Consider backporting changes to _compression
+        trailing_error: type[Exception] | tuple[type[Exception], ...] = (),
+        **decomp_args: Any,  # These are passed to decomp_factory.
+    ) -> None: ...
+    def readinto(self, b: WriteableBuffer) -> int: ...
+    def read(self, size: int = -1) -> bytes: ...
+    def seek(self, offset: int, whence: int = 0) -> int: ...
diff --git a/mypy/typeshed/stdlib/compression/bz2/__init__.pyi b/mypy/typeshed/stdlib/compression/bz2/__init__.pyi
new file mode 100644
index 000000000000..9ddc39f27c28
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/bz2/__init__.pyi
@@ -0,0 +1 @@
+from bz2 import *
diff --git a/mypy/typeshed/stdlib/compression/gzip/__init__.pyi b/mypy/typeshed/stdlib/compression/gzip/__init__.pyi
new file mode 100644
index 000000000000..9422a735c590
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/gzip/__init__.pyi
@@ -0,0 +1 @@
+from gzip import *
diff --git a/mypy/typeshed/stdlib/compression/lzma/__init__.pyi b/mypy/typeshed/stdlib/compression/lzma/__init__.pyi
new file mode 100644
index 000000000000..936c3813db4f
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/lzma/__init__.pyi
@@ -0,0 +1 @@
+from lzma import *
diff --git a/mypy/typeshed/stdlib/compression/zlib/__init__.pyi b/mypy/typeshed/stdlib/compression/zlib/__init__.pyi
new file mode 100644
index 000000000000..78d176c03ee8
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/zlib/__init__.pyi
@@ -0,0 +1 @@
+from zlib import *
diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
index 68fd0bc5acb4..dd1f6da80c4d 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
@@ -16,7 +16,27 @@ from ._base import (
 from .process import ProcessPoolExecutor as ProcessPoolExecutor
 from .thread import ThreadPoolExecutor as ThreadPoolExecutor
 
-if sys.version_info >= (3, 13):
+if sys.version_info >= (3, 14):
+    from .interpreter import InterpreterPoolExecutor as InterpreterPoolExecutor
+
+    __all__ = (
+        "FIRST_COMPLETED",
+        "FIRST_EXCEPTION",
+        "ALL_COMPLETED",
+        "CancelledError",
+        "TimeoutError",
+        "InvalidStateError",
+        "BrokenExecutor",
+        "Future",
+        "Executor",
+        "wait",
+        "as_completed",
+        "ProcessPoolExecutor",
+        "ThreadPoolExecutor",
+        "InterpreterPoolExecutor",
+    )
+
+elif sys.version_info >= (3, 13):
     __all__ = (
         "FIRST_COMPLETED",
         "FIRST_EXCEPTION",
diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
index 0c019457902b..fbf07a3fc78f 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
@@ -1,15 +1,12 @@
 import sys
 import threading
 from _typeshed import Unused
-from collections.abc import Callable, Collection, Iterable, Iterator
+from collections.abc import Callable, Iterable, Iterator
 from logging import Logger
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar
 from typing_extensions import ParamSpec, Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 FIRST_COMPLETED: Final = "FIRST_COMPLETED"
 FIRST_EXCEPTION: Final = "FIRST_EXCEPTION"
 ALL_COMPLETED: Final = "ALL_COMPLETED"
@@ -53,23 +50,25 @@ class Future(Generic[_T]):
     def set_result(self, result: _T) -> None: ...
     def exception(self, timeout: float | None = None) -> BaseException | None: ...
     def set_exception(self, exception: BaseException | None) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class Executor:
-    if sys.version_info >= (3, 9):
-        def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ...
-    else:
-        def submit(self, fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ...
-
-    def map(
-        self, fn: Callable[..., _T], *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1
-    ) -> Iterator[_T]: ...
-    if sys.version_info >= (3, 9):
-        def shutdown(self, wait: bool = True, *, cancel_futures: bool = False) -> None: ...
+    def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ...
+    if sys.version_info >= (3, 14):
+        def map(
+            self,
+            fn: Callable[..., _T],
+            *iterables: Iterable[Any],
+            timeout: float | None = None,
+            chunksize: int = 1,
+            buffersize: int | None = None,
+        ) -> Iterator[_T]: ...
     else:
-        def shutdown(self, wait: bool = True) -> None: ...
+        def map(
+            self, fn: Callable[..., _T], *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1
+        ) -> Iterator[_T]: ...
 
+    def shutdown(self, wait: bool = True, *, cancel_futures: bool = False) -> None: ...
     def __enter__(self) -> Self: ...
     def __exit__(
         self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
@@ -91,15 +90,9 @@ class DoneAndNotDoneFutures(NamedTuple, Generic[_T]):
     done: set[Future[_T]]
     not_done: set[Future[_T]]
 
-if sys.version_info >= (3, 9):
-    def wait(
-        fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED"
-    ) -> DoneAndNotDoneFutures[_T]: ...
-
-else:
-    def wait(
-        fs: Collection[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED"
-    ) -> DoneAndNotDoneFutures[_T]: ...
+def wait(
+    fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED"
+) -> DoneAndNotDoneFutures[_T]: ...
 
 class _Waiter:
     event: threading.Event
diff --git a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
new file mode 100644
index 000000000000..c1a29e6b0552
--- /dev/null
+++ b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
@@ -0,0 +1,102 @@
+import sys
+from collections.abc import Callable, Mapping
+from concurrent.futures import ThreadPoolExecutor
+from typing import Final, Literal, Protocol, overload, type_check_only
+from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack
+
+_Task: TypeAlias = tuple[bytes, Literal["function", "script"]]
+
+@type_check_only
+class _TaskFunc(Protocol):
+    @overload
+    def __call__(self, fn: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> tuple[bytes, Literal["function"]]: ...
+    @overload
+    def __call__(self, fn: str) -> tuple[bytes, Literal["script"]]: ...
+
+_Ts = TypeVarTuple("_Ts")
+_P = ParamSpec("_P")
+_R = TypeVar("_R")
+
+# A `type.simplenamespace` with `__name__` attribute.
+@type_check_only
+class _HasName(Protocol):
+    __name__: str
+
+# `_interpreters.exec` technically gives us a simple namespace.
+@type_check_only
+class _ExcInfo(Protocol):
+    formatted: str
+    msg: str
+    type: _HasName
+
+if sys.version_info >= (3, 14):
+    from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext
+
+    from _interpreters import InterpreterError
+
+    class ExecutionFailed(InterpreterError):
+        def __init__(self, excinfo: _ExcInfo) -> None: ...  #  type: ignore[override]
+
+    UNBOUND: Final = 2
+
+    class WorkerContext(ThreadWorkerContext):
+        # Parent class doesn't have `shared` argument,
+        @overload  #  type: ignore[override]
+        @classmethod
+        def prepare(
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object]
+        ) -> tuple[Callable[[], Self], _TaskFunc]: ...
+        @overload  #  type: ignore[override]
+        @classmethod
+        def prepare(
+            cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object]
+        ) -> tuple[Callable[[], Self], _TaskFunc]: ...
+        def __init__(
+            self, initdata: tuple[bytes, Literal["function", "script"]], shared: Mapping[str, object] | None = None
+        ) -> None: ...  #  type: ignore[override]
+        def __del__(self) -> None: ...
+        def run(self, task: _Task) -> None: ...  #  type: ignore[override]
+
+    class BrokenInterpreterPool(BrokenThreadPool): ...
+
+    class InterpreterPoolExecutor(ThreadPoolExecutor):
+        BROKEN: type[BrokenInterpreterPool]
+
+        @overload  #  type: ignore[override]
+        @classmethod
+        def prepare_context(
+            cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object]
+        ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ...
+        @overload  #  type: ignore[override]
+        @classmethod
+        def prepare_context(
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object]
+        ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ...
+        @overload
+        def __init__(
+            self,
+            max_workers: int | None = None,
+            thread_name_prefix: str = "",
+            initializer: Callable[[], object] | None = None,
+            initargs: tuple[()] = (),
+            shared: Mapping[str, object] | None = None,
+        ) -> None: ...
+        @overload
+        def __init__(
+            self,
+            max_workers: int | None = None,
+            thread_name_prefix: str = "",
+            *,
+            initializer: Callable[[Unpack[_Ts]], object],
+            initargs: tuple[Unpack[_Ts]],
+            shared: Mapping[str, object] | None = None,
+        ) -> None: ...
+        @overload
+        def __init__(
+            self,
+            max_workers: int | None,
+            thread_name_prefix: str,
+            initializer: Callable[[Unpack[_Ts]], object],
+            initargs: tuple[Unpack[_Ts]],
+            shared: Mapping[str, object] | None = None,
+        ) -> None: ...
diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi
index 97dc261be7ed..607990100369 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi
@@ -84,7 +84,7 @@ class _SafeQueue(Queue[Future[Any]]):
             pending_work_items: dict[int, _WorkItem[Any]],
             thread_wakeup: _ThreadWakeup,
         ) -> None: ...
-    elif sys.version_info >= (3, 9):
+    else:
         def __init__(
             self,
             max_size: int | None = 0,
@@ -94,10 +94,6 @@ class _SafeQueue(Queue[Future[Any]]):
             shutdown_lock: Lock,
             thread_wakeup: _ThreadWakeup,
         ) -> None: ...
-    else:
-        def __init__(
-            self, max_size: int | None = 0, *, ctx: BaseContext, pending_work_items: dict[int, _WorkItem[Any]]
-        ) -> None: ...
 
     def _on_queue_feeder_error(self, e: Exception, obj: _CallItem) -> None: ...
 
@@ -135,27 +131,26 @@ else:
         initargs: tuple[Unpack[_Ts]],
     ) -> None: ...
 
-if sys.version_info >= (3, 9):
-    class _ExecutorManagerThread(Thread):
-        thread_wakeup: _ThreadWakeup
-        shutdown_lock: Lock
-        executor_reference: ref[Any]
-        processes: MutableMapping[int, Process]
-        call_queue: Queue[_CallItem]
-        result_queue: SimpleQueue[_ResultItem]
-        work_ids_queue: Queue[int]
-        pending_work_items: dict[int, _WorkItem[Any]]
-        def __init__(self, executor: ProcessPoolExecutor) -> None: ...
-        def run(self) -> None: ...
-        def add_call_item_to_queue(self) -> None: ...
-        def wait_result_broken_or_wakeup(self) -> tuple[Any, bool, str]: ...
-        def process_result_item(self, result_item: int | _ResultItem) -> None: ...
-        def is_shutting_down(self) -> bool: ...
-        def terminate_broken(self, cause: str) -> None: ...
-        def flag_executor_shutting_down(self) -> None: ...
-        def shutdown_workers(self) -> None: ...
-        def join_executor_internals(self) -> None: ...
-        def get_n_children_alive(self) -> int: ...
+class _ExecutorManagerThread(Thread):
+    thread_wakeup: _ThreadWakeup
+    shutdown_lock: Lock
+    executor_reference: ref[Any]
+    processes: MutableMapping[int, Process]
+    call_queue: Queue[_CallItem]
+    result_queue: SimpleQueue[_ResultItem]
+    work_ids_queue: Queue[int]
+    pending_work_items: dict[int, _WorkItem[Any]]
+    def __init__(self, executor: ProcessPoolExecutor) -> None: ...
+    def run(self) -> None: ...
+    def add_call_item_to_queue(self) -> None: ...
+    def wait_result_broken_or_wakeup(self) -> tuple[Any, bool, str]: ...
+    def process_result_item(self, result_item: int | _ResultItem) -> None: ...
+    def is_shutting_down(self) -> bool: ...
+    def terminate_broken(self, cause: str) -> None: ...
+    def flag_executor_shutting_down(self) -> None: ...
+    def shutdown_workers(self) -> None: ...
+    def join_executor_internals(self) -> None: ...
+    def get_n_children_alive(self) -> int: ...
 
 _system_limits_checked: bool
 _system_limited: bool | None
@@ -238,7 +233,10 @@ class ProcessPoolExecutor(Executor):
             initializer: Callable[[Unpack[_Ts]], object],
             initargs: tuple[Unpack[_Ts]],
         ) -> None: ...
-    if sys.version_info >= (3, 9):
-        def _start_executor_manager_thread(self) -> None: ...
 
+    def _start_executor_manager_thread(self) -> None: ...
     def _adjust_process_count(self) -> None: ...
+
+    if sys.version_info >= (3, 14):
+        def kill_workers(self) -> None: ...
+        def terminate_workers(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
index d1b7858eae02..22df0dca5a3f 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
@@ -2,8 +2,9 @@ import queue
 import sys
 from collections.abc import Callable, Iterable, Mapping, Set as AbstractSet
 from threading import Lock, Semaphore, Thread
-from typing import Any, Generic, TypeVar, overload
-from typing_extensions import TypeVarTuple, Unpack
+from types import GenericAlias
+from typing import Any, Generic, Protocol, TypeVar, overload, type_check_only
+from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack
 from weakref import ref
 
 from ._base import BrokenExecutor, Executor, Future
@@ -16,31 +17,73 @@ _global_shutdown_lock: Lock
 
 def _python_exit() -> None: ...
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _S = TypeVar("_S")
 
-class _WorkItem(Generic[_S]):
-    future: Future[_S]
-    fn: Callable[..., _S]
-    args: Iterable[Any]
-    kwargs: Mapping[str, Any]
-    def __init__(self, future: Future[_S], fn: Callable[..., _S], args: Iterable[Any], kwargs: Mapping[str, Any]) -> None: ...
-    def run(self) -> None: ...
-    if sys.version_info >= (3, 9):
+_Task: TypeAlias = tuple[Callable[..., Any], tuple[Any, ...], dict[str, Any]]
+
+_C = TypeVar("_C", bound=Callable[..., object])
+_KT = TypeVar("_KT", bound=str)
+_VT = TypeVar("_VT")
+
+@type_check_only
+class _ResolveTaskFunc(Protocol):
+    def __call__(
+        self, func: _C, args: tuple[Unpack[_Ts]], kwargs: dict[_KT, _VT]
+    ) -> tuple[_C, tuple[Unpack[_Ts]], dict[_KT, _VT]]: ...
+
+if sys.version_info >= (3, 14):
+    class WorkerContext:
+        @overload
+        @classmethod
+        def prepare(
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]
+        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+        @overload
+        @classmethod
+        def prepare(
+            cls, initializer: Callable[[], object], initargs: tuple[()]
+        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+        @overload
+        def __init__(self, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]) -> None: ...
+        @overload
+        def __init__(self, initializer: Callable[[], object], initargs: tuple[()]) -> None: ...
+        def initialize(self) -> None: ...
+        def finalize(self) -> None: ...
+        def run(self, task: _Task) -> None: ...
+
+if sys.version_info >= (3, 14):
+    class _WorkItem(Generic[_S]):
+        future: Future[Any]
+        task: _Task
+        def __init__(self, future: Future[Any], task: _Task) -> None: ...
+        def run(self, ctx: WorkerContext) -> None: ...
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+
+    def _worker(executor_reference: ref[Any], ctx: WorkerContext, work_queue: queue.SimpleQueue[Any]) -> None: ...
+
+else:
+    class _WorkItem(Generic[_S]):
+        future: Future[_S]
+        fn: Callable[..., _S]
+        args: Iterable[Any]
+        kwargs: Mapping[str, Any]
+        def __init__(self, future: Future[_S], fn: Callable[..., _S], args: Iterable[Any], kwargs: Mapping[str, Any]) -> None: ...
+        def run(self) -> None: ...
         def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
-def _worker(
-    executor_reference: ref[Any],
-    work_queue: queue.SimpleQueue[Any],
-    initializer: Callable[[Unpack[_Ts]], object],
-    initargs: tuple[Unpack[_Ts]],
-) -> None: ...
+    def _worker(
+        executor_reference: ref[Any],
+        work_queue: queue.SimpleQueue[Any],
+        initializer: Callable[[Unpack[_Ts]], object],
+        initargs: tuple[Unpack[_Ts]],
+    ) -> None: ...
 
 class BrokenThreadPool(BrokenExecutor): ...
 
 class ThreadPoolExecutor(Executor):
+    if sys.version_info >= (3, 14):
+        BROKEN: type[BrokenThreadPool]
+
     _max_workers: int
     _idle_semaphore: Semaphore
     _threads: AbstractSet[Thread]
@@ -51,6 +94,19 @@ class ThreadPoolExecutor(Executor):
     _initializer: Callable[..., None] | None
     _initargs: tuple[Any, ...]
     _work_queue: queue.SimpleQueue[_WorkItem[Any]]
+
+    if sys.version_info >= (3, 14):
+        @overload
+        @classmethod
+        def prepare_context(
+            cls, initializer: Callable[[], object], initargs: tuple[()]
+        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+        @overload
+        @classmethod
+        def prepare_context(
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]
+        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+
     @overload
     def __init__(
         self,
diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi
index 8996c85d9a53..15c564c02589 100644
--- a/mypy/typeshed/stdlib/configparser.pyi
+++ b/mypy/typeshed/stdlib/configparser.pyi
@@ -5,7 +5,33 @@ from re import Pattern
 from typing import Any, ClassVar, Final, Literal, TypeVar, overload
 from typing_extensions import TypeAlias
 
-if sys.version_info >= (3, 13):
+if sys.version_info >= (3, 14):
+    __all__ = (
+        "NoSectionError",
+        "DuplicateOptionError",
+        "DuplicateSectionError",
+        "NoOptionError",
+        "InterpolationError",
+        "InterpolationDepthError",
+        "InterpolationMissingOptionError",
+        "InterpolationSyntaxError",
+        "ParsingError",
+        "MissingSectionHeaderError",
+        "MultilineContinuationError",
+        "UnnamedSectionDisabledError",
+        "InvalidWriteError",
+        "ConfigParser",
+        "RawConfigParser",
+        "Interpolation",
+        "BasicInterpolation",
+        "ExtendedInterpolation",
+        "SectionProxy",
+        "ConverterMapping",
+        "DEFAULTSECT",
+        "MAX_INTERPOLATION_DEPTH",
+        "UNNAMED_SECTION",
+    )
+elif sys.version_info >= (3, 13):
     __all__ = (
         "NoSectionError",
         "DuplicateOptionError",
@@ -429,3 +455,10 @@ if sys.version_info >= (3, 13):
         lineno: int
         line: str
         def __init__(self, filename: str, lineno: int, line: str) -> None: ...
+
+if sys.version_info >= (3, 14):
+    class UnnamedSectionDisabledError(Error):
+        msg: Final = "Support for UNNAMED_SECTION is disabled."
+        def __init__(self) -> None: ...
+
+    class InvalidWriteError(Error): ...
diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi
index 08ac5a28b8b8..4663b448c79c 100644
--- a/mypy/typeshed/stdlib/contextlib.pyi
+++ b/mypy/typeshed/stdlib/contextlib.pyi
@@ -81,14 +81,9 @@ class _GeneratorContextManager(
     AbstractContextManager[_T_co, bool | None],
     ContextDecorator,
 ):
-    if sys.version_info >= (3, 9):
-        def __exit__(
-            self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
-        ) -> bool | None: ...
-    else:
-        def __exit__(
-            self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
-        ) -> bool | None: ...
+    def __exit__(
+        self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
+    ) -> bool | None: ...
 
 def contextmanager(func: Callable[_P, Iterator[_T_co]]) -> Callable[_P, _GeneratorContextManager[_T_co]]: ...
 
@@ -184,7 +179,7 @@ class AsyncExitStack(_BaseExitStack[_ExitT_co], metaclass=abc.ABCMeta):
     async def __aenter__(self) -> Self: ...
     async def __aexit__(
         self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /
-    ) -> bool: ...
+    ) -> _ExitT_co: ...
 
 if sys.version_info >= (3, 10):
     class nullcontext(AbstractContextManager[_T, None], AbstractAsyncContextManager[_T, None]):
diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi
index ef93129d6546..4ed0ab1d83b8 100644
--- a/mypy/typeshed/stdlib/csv.pyi
+++ b/mypy/typeshed/stdlib/csv.pyi
@@ -26,12 +26,10 @@ else:
 
 from _typeshed import SupportsWrite
 from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
+from types import GenericAlias
 from typing import Any, Generic, Literal, TypeVar, overload
 from typing_extensions import Self
 
-if sys.version_info >= (3, 12):
-    from types import GenericAlias
-
 __all__ = [
     "QUOTE_MINIMAL",
     "QUOTE_ALL",
diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi
index 4f44975d657f..68b75b86def1 100644
--- a/mypy/typeshed/stdlib/ctypes/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi
@@ -1,6 +1,5 @@
 import sys
 from _ctypes import (
-    POINTER as POINTER,
     RTLD_GLOBAL as RTLD_GLOBAL,
     RTLD_LOCAL as RTLD_LOCAL,
     Array as Array,
@@ -19,14 +18,14 @@ from _ctypes import (
     alignment as alignment,
     byref as byref,
     get_errno as get_errno,
-    pointer as pointer,
     resize as resize,
     set_errno as set_errno,
     sizeof as sizeof,
 )
 from _typeshed import StrPath
 from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure
-from typing import Any, ClassVar, Generic, TypeVar, type_check_only
+from types import GenericAlias
+from typing import Any, ClassVar, Generic, Literal, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, deprecated
 
 if sys.platform == "win32":
@@ -35,12 +34,22 @@ if sys.platform == "win32":
 if sys.version_info >= (3, 11):
     from ctypes._endian import BigEndianUnion as BigEndianUnion, LittleEndianUnion as LittleEndianUnion
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
-_T = TypeVar("_T")
-_DLLT = TypeVar("_DLLT", bound=CDLL)
 _CT = TypeVar("_CT", bound=_CData)
+_T = TypeVar("_T", default=Any)
+_DLLT = TypeVar("_DLLT", bound=CDLL)
+
+if sys.version_info >= (3, 14):
+    @overload
+    @deprecated("ctypes.POINTER with string")
+    def POINTER(cls: str) -> type[Any]: ...
+    @overload
+    def POINTER(cls: None) -> type[c_void_p]: ...
+    @overload
+    def POINTER(cls: type[_CT]) -> type[_Pointer[_CT]]: ...
+    def pointer(obj: _CT) -> _Pointer[_CT]: ...
+
+else:
+    from _ctypes import POINTER as POINTER, pointer as pointer
 
 DEFAULT_MODE: int
 
@@ -92,8 +101,7 @@ class LibraryLoader(Generic[_DLLT]):
     def __getattr__(self, name: str) -> _DLLT: ...
     def __getitem__(self, name: str) -> _DLLT: ...
     def LoadLibrary(self, name: str) -> _DLLT: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 cdll: LibraryLoader[CDLL]
 if sys.platform == "win32":
@@ -151,14 +159,12 @@ c_buffer = create_string_buffer
 
 def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ...
 @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-def SetPointerType(
-    pointer: type[_Pointer[Any]], cls: Any  # noqa: F811  # Redefinition of unused `pointer` from line 22
-) -> None: ...
+def SetPointerType(pointer: type[_Pointer[Any]], cls: Any) -> None: ...
 def ARRAY(typ: _CT, len: int) -> Array[_CT]: ...  # Soft Deprecated, no plans to remove
 
 if sys.platform == "win32":
     def DllCanUnloadNow() -> int: ...
-    def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ...  # TODO not documented
+    def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ...  # TODO: not documented
 
     # Actually just an instance of _NamedFuncPointer (aka _CDLLFuncPointer),
     # but we want to set a more specific __call__
@@ -191,73 +197,121 @@ if sys.platform == "win32":
 
 def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ...
 
-class c_byte(_SimpleCData[int]): ...
+class py_object(_CanCastTo, _SimpleCData[_T]):
+    _type_: ClassVar[Literal["O"]]
+
+class c_bool(_SimpleCData[bool]):
+    _type_: ClassVar[Literal["?"]]
+    def __init__(self, value: bool = ...) -> None: ...
+
+class c_byte(_SimpleCData[int]):
+    _type_: ClassVar[Literal["b"]]
+
+class c_ubyte(_SimpleCData[int]):
+    _type_: ClassVar[Literal["B"]]
+
+class c_short(_SimpleCData[int]):
+    _type_: ClassVar[Literal["h"]]
+
+class c_ushort(_SimpleCData[int]):
+    _type_: ClassVar[Literal["H"]]
+
+class c_long(_SimpleCData[int]):
+    _type_: ClassVar[Literal["l"]]
+
+class c_ulong(_SimpleCData[int]):
+    _type_: ClassVar[Literal["L"]]
+
+class c_int(_SimpleCData[int]):  # can be an alias for c_long
+    _type_: ClassVar[Literal["i", "l"]]
+
+class c_uint(_SimpleCData[int]):  # can be an alias for c_ulong
+    _type_: ClassVar[Literal["I", "L"]]
+
+class c_longlong(_SimpleCData[int]):  # can be an alias for c_long
+    _type_: ClassVar[Literal["q", "l"]]
+
+class c_ulonglong(_SimpleCData[int]):  # can be an alias for c_ulong
+    _type_: ClassVar[Literal["Q", "L"]]
+
+c_int8 = c_byte
+c_uint8 = c_ubyte
+
+class c_int16(_SimpleCData[int]):  # can be an alias for c_short or c_int
+    _type_: ClassVar[Literal["h", "i"]]
+
+class c_uint16(_SimpleCData[int]):  # can be an alias for c_ushort or c_uint
+    _type_: ClassVar[Literal["H", "I"]]
+
+class c_int32(_SimpleCData[int]):  # can be an alias for c_int or c_long
+    _type_: ClassVar[Literal["i", "l"]]
+
+class c_uint32(_SimpleCData[int]):  # can be an alias for c_uint or c_ulong
+    _type_: ClassVar[Literal["I", "L"]]
+
+class c_int64(_SimpleCData[int]):  # can be an alias for c_long or c_longlong
+    _type_: ClassVar[Literal["l", "q"]]
+
+class c_uint64(_SimpleCData[int]):  # can be an alias for c_ulong or c_ulonglong
+    _type_: ClassVar[Literal["L", "Q"]]
+
+class c_ssize_t(_SimpleCData[int]):  # alias for c_int, c_long, or c_longlong
+    _type_: ClassVar[Literal["i", "l", "q"]]
+
+class c_size_t(_SimpleCData[int]):  # alias for c_uint, c_ulong, or c_ulonglong
+    _type_: ClassVar[Literal["I", "L", "Q"]]
+
+class c_float(_SimpleCData[float]):
+    _type_: ClassVar[Literal["f"]]
+
+class c_double(_SimpleCData[float]):
+    _type_: ClassVar[Literal["d"]]
+
+class c_longdouble(_SimpleCData[float]):  # can be an alias for c_double
+    _type_: ClassVar[Literal["d", "g"]]
+
+if sys.version_info >= (3, 14):
+    class c_float_complex(_SimpleCData[complex]):
+        _type_: ClassVar[Literal["E"]]
+
+    class c_double_complex(_SimpleCData[complex]):
+        _type_: ClassVar[Literal["C"]]
+
+    class c_longdouble_complex(_SimpleCData[complex]):
+        _type_: ClassVar[Literal["F"]]
 
 class c_char(_SimpleCData[bytes]):
+    _type_: ClassVar[Literal["c"]]
     def __init__(self, value: int | bytes | bytearray = ...) -> None: ...
 
 class c_char_p(_PointerLike, _SimpleCData[bytes | None]):
+    _type_: ClassVar[Literal["z"]]
     def __init__(self, value: int | bytes | None = ...) -> None: ...
     @classmethod
     def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
 
-class c_double(_SimpleCData[float]): ...
-class c_longdouble(_SimpleCData[float]): ...  # can be an alias for c_double
-class c_float(_SimpleCData[float]): ...
-class c_int(_SimpleCData[int]): ...  # can be an alias for c_long
-class c_long(_SimpleCData[int]): ...
-class c_longlong(_SimpleCData[int]): ...  # can be an alias for c_long
-class c_short(_SimpleCData[int]): ...
-class c_size_t(_SimpleCData[int]): ...  # alias for c_uint, c_ulong, or c_ulonglong
-class c_ssize_t(_SimpleCData[int]): ...  # alias for c_int, c_long, or c_longlong
-class c_ubyte(_SimpleCData[int]): ...
-class c_uint(_SimpleCData[int]): ...  # can be an alias for c_ulong
-class c_ulong(_SimpleCData[int]): ...
-class c_ulonglong(_SimpleCData[int]): ...  # can be an alias for c_ulong
-class c_ushort(_SimpleCData[int]): ...
-
 class c_void_p(_PointerLike, _SimpleCData[int | None]):
+    _type_: ClassVar[Literal["P"]]
     @classmethod
     def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
 
 c_voidp = c_void_p  # backwards compatibility (to a bug)
 
-class c_wchar(_SimpleCData[str]): ...
-
-c_int8 = c_byte
-
-# these are actually dynamic aliases for c_short, c_int, c_long, or c_longlong
-class c_int16(_SimpleCData[int]): ...
-class c_int32(_SimpleCData[int]): ...
-class c_int64(_SimpleCData[int]): ...
-
-c_uint8 = c_ubyte
-
-# these are actually dynamic aliases for c_ushort, c_uint, c_ulong, or c_ulonglong
-class c_uint16(_SimpleCData[int]): ...
-class c_uint32(_SimpleCData[int]): ...
-class c_uint64(_SimpleCData[int]): ...
+class c_wchar(_SimpleCData[str]):
+    _type_: ClassVar[Literal["u"]]
 
 class c_wchar_p(_PointerLike, _SimpleCData[str | None]):
+    _type_: ClassVar[Literal["Z"]]
     def __init__(self, value: int | str | None = ...) -> None: ...
     @classmethod
     def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
 
-class c_bool(_SimpleCData[bool]):
-    def __init__(self, value: bool = ...) -> None: ...
-
 if sys.platform == "win32":
-    class HRESULT(_SimpleCData[int]): ...  # TODO undocumented
+    class HRESULT(_SimpleCData[int]):  # TODO: undocumented
+        _type_: ClassVar[Literal["l"]]
 
 if sys.version_info >= (3, 12):
     # At runtime, this is an alias for either c_int32 or c_int64,
-    # which are themselves an alias for one of c_short, c_int, c_long, or c_longlong
+    # which are themselves an alias for one of c_int, c_long, or c_longlong
     # This covers all our bases.
-    c_time_t: type[c_int32 | c_int64 | c_short | c_int | c_long | c_longlong]
-
-class py_object(_CanCastTo, _SimpleCData[_T]): ...
-
-if sys.version_info >= (3, 14):
-    class c_float_complex(_SimpleCData[complex]): ...
-    class c_double_complex(_SimpleCData[complex]): ...
-    class c_longdouble_complex(_SimpleCData[complex]): ...
+    c_time_t: type[c_int32 | c_int64 | c_int | c_long | c_longlong]
diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
index e938d8f22957..63f117787aa0 100644
--- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi
+++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
@@ -1,10 +1,10 @@
+import sys
 from _ctypes import _CArgObject, _CField
 from ctypes import (
     Array,
     Structure,
     _Pointer,
     _SimpleCData,
-    c_byte,
     c_char,
     c_char_p,
     c_double,
@@ -24,7 +24,15 @@ from ctypes import (
 from typing import Any, TypeVar
 from typing_extensions import Self, TypeAlias
 
-BYTE = c_byte
+if sys.version_info >= (3, 12):
+    from ctypes import c_ubyte
+
+    BYTE = c_ubyte
+else:
+    from ctypes import c_byte
+
+    BYTE = c_byte
+
 WORD = c_ushort
 DWORD = c_ulong
 CHAR = c_char
diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi
index edc64a00cd39..5c157fd7c2f6 100644
--- a/mypy/typeshed/stdlib/curses/__init__.pyi
+++ b/mypy/typeshed/stdlib/curses/__init__.pyi
@@ -23,11 +23,6 @@ COLOR_PAIRS: int
 
 def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ...
 
-# typeshed used the name _CursesWindow for the underlying C class before
-# it was mapped to the name 'window' in 3.8.
-# Kept here as a legacy alias in case any third-party code is relying on it.
-_CursesWindow = window
-
 # At runtime this class is unexposed and calls itself curses.ncurses_version.
 # That name would conflict with the actual curses.ncurses_version, which is
 # an instance of this class.
diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi
index 3d89b830352b..bba76c1af1b4 100644
--- a/mypy/typeshed/stdlib/dataclasses.pyi
+++ b/mypy/typeshed/stdlib/dataclasses.pyi
@@ -4,11 +4,9 @@ import types
 from _typeshed import DataclassInstance
 from builtins import type as Type  # alias to avoid name clashes with fields named "type"
 from collections.abc import Callable, Iterable, Mapping
-from typing import Any, Generic, Literal, Protocol, TypeVar, overload
-from typing_extensions import Never, TypeAlias, TypeIs
-
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
+from types import GenericAlias
+from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only
+from typing_extensions import Never, TypeIs
 
 _T = TypeVar("_T")
 _T_co = TypeVar("_T_co", covariant=True)
@@ -33,6 +31,25 @@ if sys.version_info >= (3, 10):
 
 _DataclassT = TypeVar("_DataclassT", bound=DataclassInstance)
 
+@type_check_only
+class _DataclassFactory(Protocol):
+    def __call__(
+        self,
+        cls: type[_T],
+        /,
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        match_args: bool = True,
+        kw_only: bool = False,
+        slots: bool = False,
+        weakref_slot: bool = False,
+    ) -> type[_T]: ...
+
 # define _MISSING_TYPE as an enum within the type stubs,
 # even though that is not really its type at runtime
 # this allows us to use Literal[_MISSING_TYPE.MISSING]
@@ -116,8 +133,27 @@ class Field(Generic[_T]):
     init: bool
     compare: bool
     metadata: types.MappingProxyType[Any, Any]
+
+    if sys.version_info >= (3, 14):
+        doc: str | None
+
     if sys.version_info >= (3, 10):
         kw_only: bool | Literal[_MISSING_TYPE.MISSING]
+
+    if sys.version_info >= (3, 14):
+        def __init__(
+            self,
+            default: _T,
+            default_factory: Callable[[], _T],
+            init: bool,
+            repr: bool,
+            hash: bool | None,
+            compare: bool,
+            metadata: Mapping[Any, Any],
+            kw_only: bool,
+            doc: str | None,
+        ) -> None: ...
+    elif sys.version_info >= (3, 10):
         def __init__(
             self,
             default: _T,
@@ -142,12 +178,52 @@ class Field(Generic[_T]):
         ) -> None: ...
 
     def __set_name__(self, owner: Type[Any], name: str) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # NOTE: Actual return type is 'Field[_T]', but we want to help type checkers
 # to understand the magic that happens at runtime.
-if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 14):
+    @overload  # `default` and `default_factory` are optional and mutually exclusive.
+    def field(
+        *,
+        default: _T,
+        default_factory: Literal[_MISSING_TYPE.MISSING] = ...,
+        init: bool = True,
+        repr: bool = True,
+        hash: bool | None = None,
+        compare: bool = True,
+        metadata: Mapping[Any, Any] | None = None,
+        kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
+        doc: str | None = None,
+    ) -> _T: ...
+    @overload
+    def field(
+        *,
+        default: Literal[_MISSING_TYPE.MISSING] = ...,
+        default_factory: Callable[[], _T],
+        init: bool = True,
+        repr: bool = True,
+        hash: bool | None = None,
+        compare: bool = True,
+        metadata: Mapping[Any, Any] | None = None,
+        kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
+        doc: str | None = None,
+    ) -> _T: ...
+    @overload
+    def field(
+        *,
+        default: Literal[_MISSING_TYPE.MISSING] = ...,
+        default_factory: Literal[_MISSING_TYPE.MISSING] = ...,
+        init: bool = True,
+        repr: bool = True,
+        hash: bool | None = None,
+        compare: bool = True,
+        metadata: Mapping[Any, Any] | None = None,
+        kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
+        doc: str | None = None,
+    ) -> Any: ...
+
+elif sys.version_info >= (3, 10):
     @overload  # `default` and `default_factory` are optional and mutually exclusive.
     def field(
         *,
@@ -232,24 +308,36 @@ def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstan
 
 class FrozenInstanceError(AttributeError): ...
 
-if sys.version_info >= (3, 9):
-    _InitVarMeta: TypeAlias = type
-else:
-    class _InitVarMeta(type):
-        # Not used, instead `InitVar.__class_getitem__` is called.
-        # pyright (not unreasonably) thinks this is an invalid use of InitVar.
-        def __getitem__(self, params: Any) -> InitVar[Any]: ...  # pyright: ignore[reportInvalidTypeForm]
-
-class InitVar(Generic[_T], metaclass=_InitVarMeta):
+class InitVar(Generic[_T], metaclass=type):
     type: Type[_T]
     def __init__(self, type: Type[_T]) -> None: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ...  # pyright: ignore[reportInvalidTypeForm]
-        @overload
-        def __class_getitem__(cls, type: Any) -> InitVar[Any]: ...  # pyright: ignore[reportInvalidTypeForm]
+    @overload
+    def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ...  # pyright: ignore[reportInvalidTypeForm]
+    @overload
+    def __class_getitem__(cls, type: Any) -> InitVar[Any]: ...  # pyright: ignore[reportInvalidTypeForm]
+
+if sys.version_info >= (3, 14):
+    def make_dataclass(
+        cls_name: str,
+        fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]],
+        *,
+        bases: tuple[type, ...] = (),
+        namespace: dict[str, Any] | None = None,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        match_args: bool = True,
+        kw_only: bool = False,
+        slots: bool = False,
+        weakref_slot: bool = False,
+        module: str | None = None,
+        decorator: _DataclassFactory = ...,
+    ) -> type: ...
 
-if sys.version_info >= (3, 12):
+elif sys.version_info >= (3, 12):
     def make_dataclass(
         cls_name: str,
         fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]],
diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi
index 4907bf4607c8..37d6a06dfff9 100644
--- a/mypy/typeshed/stdlib/datetime.pyi
+++ b/mypy/typeshed/stdlib/datetime.pyi
@@ -6,7 +6,7 @@ from typing_extensions import CapsuleType, Self, TypeAlias, deprecated
 
 if sys.version_info >= (3, 11):
     __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC")
-elif sys.version_info >= (3, 9):
+else:
     __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR")
 
 MINYEAR: Final = 1
@@ -39,18 +39,17 @@ class timezone(tzinfo):
 if sys.version_info >= (3, 11):
     UTC: timezone
 
-if sys.version_info >= (3, 9):
-    # This class calls itself datetime.IsoCalendarDate. It's neither
-    # NamedTuple nor structseq.
-    @final
-    @type_check_only
-    class _IsoCalendarDate(tuple[int, int, int]):
-        @property
-        def year(self) -> int: ...
-        @property
-        def week(self) -> int: ...
-        @property
-        def weekday(self) -> int: ...
+# This class calls itself datetime.IsoCalendarDate. It's neither
+# NamedTuple nor structseq.
+@final
+@type_check_only
+class _IsoCalendarDate(tuple[int, int, int]):
+    @property
+    def year(self) -> int: ...
+    @property
+    def week(self) -> int: ...
+    @property
+    def weekday(self) -> int: ...
 
 class date:
     min: ClassVar[date]
@@ -74,6 +73,11 @@ class date:
     @property
     def day(self) -> int: ...
     def ctime(self) -> str: ...
+
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def strptime(cls, date_string: str, format: str, /) -> Self: ...
+
     # On <3.12, the name of the parameter in the pure-Python implementation
     # didn't match the name in the C implementation,
     # meaning it is only *safe* to pass it as a keyword argument on 3.12+
@@ -106,10 +110,7 @@ class date:
     def __hash__(self) -> int: ...
     def weekday(self) -> int: ...
     def isoweekday(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def isocalendar(self) -> _IsoCalendarDate: ...
-    else:
-        def isocalendar(self) -> tuple[int, int, int]: ...
+    def isocalendar(self) -> _IsoCalendarDate: ...
 
 class time:
     min: ClassVar[time]
@@ -146,6 +147,11 @@ class time:
     def isoformat(self, timespec: str = ...) -> str: ...
     @classmethod
     def fromisoformat(cls, time_string: str, /) -> Self: ...
+
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def strptime(cls, date_string: str, format: str, /) -> Self: ...
+
     # On <3.12, the name of the parameter in the pure-Python implementation
     # didn't match the name in the C implementation,
     # meaning it is only *safe* to pass it as a keyword argument on 3.12+
diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi
index 4ded21e0b017..b85c00080092 100644
--- a/mypy/typeshed/stdlib/decimal.pyi
+++ b/mypy/typeshed/stdlib/decimal.pyi
@@ -1,4 +1,5 @@
 import numbers
+import sys
 from _decimal import (
     HAVE_CONTEXTVAR as HAVE_CONTEXTVAR,
     HAVE_THREADS as HAVE_THREADS,
@@ -28,6 +29,9 @@ from types import TracebackType
 from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
+if sys.version_info >= (3, 14):
+    from _decimal import IEEE_CONTEXT_MAX_BITS as IEEE_CONTEXT_MAX_BITS, IEEEContext as IEEEContext
+
 _Decimal: TypeAlias = Decimal | int
 _DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int]
 _ComparableNum: TypeAlias = Decimal | float | numbers.Rational
@@ -66,6 +70,10 @@ class FloatOperation(DecimalException, TypeError): ...
 
 class Decimal:
     def __new__(cls, value: _DecimalNew = "0", context: Context | None = None) -> Self: ...
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def from_number(cls, number: Decimal | float, /) -> Self: ...
+
     @classmethod
     def from_float(cls, f: float, /) -> Self: ...
     def __bool__(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi
index 50154d785c2f..18583a3acfe9 100644
--- a/mypy/typeshed/stdlib/difflib.pyi
+++ b/mypy/typeshed/stdlib/difflib.pyi
@@ -1,10 +1,7 @@
-import sys
 from collections.abc import Callable, Iterable, Iterator, Sequence
+from types import GenericAlias
 from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "get_close_matches",
     "ndiff",
@@ -43,19 +40,14 @@ class SequenceMatcher(Generic[_T]):
     def set_seqs(self, a: Sequence[_T], b: Sequence[_T]) -> None: ...
     def set_seq1(self, a: Sequence[_T]) -> None: ...
     def set_seq2(self, b: Sequence[_T]) -> None: ...
-    if sys.version_info >= (3, 9):
-        def find_longest_match(self, alo: int = 0, ahi: int | None = None, blo: int = 0, bhi: int | None = None) -> Match: ...
-    else:
-        def find_longest_match(self, alo: int, ahi: int, blo: int, bhi: int) -> Match: ...
-
+    def find_longest_match(self, alo: int = 0, ahi: int | None = None, blo: int = 0, bhi: int | None = None) -> Match: ...
     def get_matching_blocks(self) -> list[Match]: ...
     def get_opcodes(self) -> list[tuple[Literal["replace", "delete", "insert", "equal"], int, int, int, int]]: ...
     def get_grouped_opcodes(self, n: int = 3) -> Iterable[list[tuple[str, int, int, int, int]]]: ...
     def ratio(self) -> float: ...
     def quick_ratio(self) -> float: ...
     def real_quick_ratio(self) -> float: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @overload
 def get_close_matches(word: AnyStr, possibilities: Iterable[AnyStr], n: int = 3, cutoff: float = 0.6) -> list[AnyStr]: ...
diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi
index cb69eac89c92..86b6d01e3120 100644
--- a/mypy/typeshed/stdlib/dis.pyi
+++ b/mypy/typeshed/stdlib/dis.pyi
@@ -106,11 +106,40 @@ class Instruction(_Instruction):
         def jump_target(self) -> int: ...
         @property
         def is_jump_target(self) -> bool: ...
+    if sys.version_info >= (3, 14):
+        @staticmethod
+        def make(
+            opname: str,
+            arg: int | None,
+            argval: Any,
+            argrepr: str,
+            offset: int,
+            start_offset: int,
+            starts_line: bool,
+            line_number: int | None,
+            label: int | None = None,
+            positions: Positions | None = None,
+            cache_info: list[tuple[str, int, Any]] | None = None,
+        ) -> Instruction: ...
 
 class Bytecode:
     codeobj: types.CodeType
     first_line: int
-    if sys.version_info >= (3, 13):
+    if sys.version_info >= (3, 14):
+        show_positions: bool
+        # 3.14 added `show_positions`
+        def __init__(
+            self,
+            x: _HaveCodeType | str,
+            *,
+            first_line: int | None = None,
+            current_offset: int | None = None,
+            show_caches: bool = False,
+            adaptive: bool = False,
+            show_offsets: bool = False,
+            show_positions: bool = False,
+        ) -> None: ...
+    elif sys.version_info >= (3, 13):
         show_offsets: bool
         # 3.13 added `show_offsets`
         def __init__(
@@ -156,7 +185,39 @@ def findlinestarts(code: _HaveCodeType) -> Iterator[tuple[int, int]]: ...
 def pretty_flags(flags: int) -> str: ...
 def code_info(x: _HaveCodeType | str) -> str: ...
 
-if sys.version_info >= (3, 13):
+if sys.version_info >= (3, 14):
+    # 3.14 added `show_positions`
+    def dis(
+        x: _HaveCodeType | str | bytes | bytearray | None = None,
+        *,
+        file: IO[str] | None = None,
+        depth: int | None = None,
+        show_caches: bool = False,
+        adaptive: bool = False,
+        show_offsets: bool = False,
+        show_positions: bool = False,
+    ) -> None: ...
+    def disassemble(
+        co: _HaveCodeType,
+        lasti: int = -1,
+        *,
+        file: IO[str] | None = None,
+        show_caches: bool = False,
+        adaptive: bool = False,
+        show_offsets: bool = False,
+        show_positions: bool = False,
+    ) -> None: ...
+    def distb(
+        tb: types.TracebackType | None = None,
+        *,
+        file: IO[str] | None = None,
+        show_caches: bool = False,
+        adaptive: bool = False,
+        show_offsets: bool = False,
+        show_positions: bool = False,
+    ) -> None: ...
+
+elif sys.version_info >= (3, 13):
     # 3.13 added `show_offsets`
     def dis(
         x: _HaveCodeType | str | bytes | bytearray | None = None,
@@ -184,10 +245,6 @@ if sys.version_info >= (3, 13):
         adaptive: bool = False,
         show_offsets: bool = False,
     ) -> None: ...
-    # 3.13 made `show_cache` `None` by default
-    def get_instructions(
-        x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool | None = None, adaptive: bool = False
-    ) -> Iterator[Instruction]: ...
 
 elif sys.version_info >= (3, 11):
     # 3.11 added `show_caches` and `adaptive`
@@ -205,9 +262,6 @@ elif sys.version_info >= (3, 11):
     def distb(
         tb: types.TracebackType | None = None, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False
     ) -> None: ...
-    def get_instructions(
-        x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool = False, adaptive: bool = False
-    ) -> Iterator[Instruction]: ...
 
 else:
     def dis(
@@ -215,6 +269,19 @@ else:
     ) -> None: ...
     def disassemble(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ...
     def distb(tb: types.TracebackType | None = None, *, file: IO[str] | None = None) -> None: ...
+
+if sys.version_info >= (3, 13):
+    # 3.13 made `show_cache` `None` by default
+    def get_instructions(
+        x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool | None = None, adaptive: bool = False
+    ) -> Iterator[Instruction]: ...
+
+elif sys.version_info >= (3, 11):
+    def get_instructions(
+        x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool = False, adaptive: bool = False
+    ) -> Iterator[Instruction]: ...
+
+else:
     def get_instructions(x: _HaveCodeType, *, first_line: int | None = None) -> Iterator[Instruction]: ...
 
 def show_code(co: _HaveCodeType, *, file: IO[str] | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi
index a4e77ddf1388..7f97bc3a2c9e 100644
--- a/mypy/typeshed/stdlib/distutils/cmd.pyi
+++ b/mypy/typeshed/stdlib/distutils/cmd.pyi
@@ -1,4 +1,4 @@
-from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused
+from _typeshed import BytesPath, StrOrBytesPath, StrPath, Unused
 from abc import abstractmethod
 from collections.abc import Callable, Iterable
 from distutils.command.bdist import bdist
@@ -226,4 +226,4 @@ class Command:
         level: Unused = 1,
     ) -> None: ...
     def ensure_finalized(self) -> None: ...
-    def dump_options(self, header: Incomplete | None = None, indent: str = "") -> None: ...
+    def dump_options(self, header=None, indent: str = "") -> None: ...
diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi
index baeee7d3eccb..d677f81d1425 100644
--- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi
+++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi
@@ -21,8 +21,7 @@ if sys.platform == "win32":
         boolean_options: ClassVar[list[str]]
         all_versions: Incomplete
         other_version: str
-        if sys.version_info >= (3, 9):
-            def __init__(self, *args, **kw) -> None: ...
+        def __init__(self, *args, **kw) -> None: ...
         bdist_dir: Incomplete
         plat_name: Incomplete
         keep_temp: int
diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi
index 562ff3a5271f..381e8e466bf1 100644
--- a/mypy/typeshed/stdlib/distutils/command/config.pyi
+++ b/mypy/typeshed/stdlib/distutils/command/config.pyi
@@ -1,4 +1,4 @@
-from _typeshed import Incomplete, StrOrBytesPath
+from _typeshed import StrOrBytesPath
 from collections.abc import Sequence
 from re import Pattern
 from typing import ClassVar, Final, Literal
@@ -81,4 +81,4 @@ class config(Command):
         self, header: str, include_dirs: Sequence[str] | None = None, library_dirs: Sequence[str] | None = None, lang: str = "c"
     ) -> bool: ...
 
-def dump_file(filename: StrOrBytesPath, head: Incomplete | None = None) -> None: ...
+def dump_file(filename: StrOrBytesPath, head=None) -> None: ...
diff --git a/mypy/typeshed/stdlib/distutils/command/register.pyi b/mypy/typeshed/stdlib/distutils/command/register.pyi
index cf98e178a9ba..c3bd62aaa7aa 100644
--- a/mypy/typeshed/stdlib/distutils/command/register.pyi
+++ b/mypy/typeshed/stdlib/distutils/command/register.pyi
@@ -1,4 +1,3 @@
-from _typeshed import Incomplete
 from collections.abc import Callable
 from typing import Any, ClassVar
 
@@ -18,4 +17,4 @@ class register(PyPIRCCommand):
     def verify_metadata(self) -> None: ...
     def send_metadata(self) -> None: ...
     def build_post_data(self, action): ...
-    def post_to_server(self, data, auth: Incomplete | None = None): ...
+    def post_to_server(self, data, auth=None): ...
diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi
index 09f2b456d263..412b94131b54 100644
--- a/mypy/typeshed/stdlib/distutils/dist.pyi
+++ b/mypy/typeshed/stdlib/distutils/dist.pyi
@@ -112,9 +112,7 @@ class Distribution:
     command_obj: Incomplete
     have_run: Incomplete
     want_user_cfg: bool
-    def dump_option_dicts(
-        self, header: Incomplete | None = None, commands: Incomplete | None = None, indent: str = ""
-    ) -> None: ...
+    def dump_option_dicts(self, header=None, commands=None, indent: str = "") -> None: ...
     def find_config_files(self): ...
     commands: Incomplete
     def parse_command_line(self): ...
diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi
index e66d8cc9f2c5..f3fa2a1255a6 100644
--- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi
+++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi
@@ -13,7 +13,7 @@ longopt_xlate: Final[dict[int, int]]
 
 class FancyGetopt:
     def __init__(self, option_table: list[_Option] | None = None) -> None: ...
-    # TODO kinda wrong, `getopt(object=object())` is invalid
+    # TODO: kinda wrong, `getopt(object=object())` is invalid
     @overload
     def getopt(
         self, args: _SliceableT[_StrSequenceT_co] | None = None, object: None = None
diff --git a/mypy/typeshed/stdlib/dummy_threading.pyi b/mypy/typeshed/stdlib/dummy_threading.pyi
deleted file mode 100644
index 757cb8d4bd4c..000000000000
--- a/mypy/typeshed/stdlib/dummy_threading.pyi
+++ /dev/null
@@ -1,2 +0,0 @@
-from _dummy_threading import *
-from _dummy_threading import __all__ as __all__
diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi
index 628ffb2b793a..53f8c350b01e 100644
--- a/mypy/typeshed/stdlib/email/__init__.pyi
+++ b/mypy/typeshed/stdlib/email/__init__.pyi
@@ -1,6 +1,7 @@
 from collections.abc import Callable
+from email._policybase import _MessageT
 from email.message import Message
-from email.policy import Policy, _MessageT
+from email.policy import Policy
 from typing import IO, overload
 from typing_extensions import TypeAlias
 
diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
index a4c2d8b1a92e..a8abfead9217 100644
--- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi
+++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
@@ -17,12 +17,13 @@ TOKEN_ENDS: Final[set[str]]
 ASPECIALS: Final[set[str]]
 ATTRIBUTE_ENDS: Final[set[str]]
 EXTENDED_ATTRIBUTE_ENDS: Final[set[str]]
-# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+# Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 NLSET: Final[set[str]]
-# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+# Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 SPECIALSNL: Final[set[str]]
 
-if sys.version_info >= (3, 12):
+if sys.version_info >= (3, 10):
+    # Added in Python 3.10.17, 3.11.12, 3.12.9, 3.13.2 (may still be backported to 3.9)
     def make_quoted_pairs(value: Any) -> str: ...
 
 def quote_string(value: Any) -> str: ...
@@ -349,7 +350,7 @@ ListSeparator: Final[ValueTerminal]
 RouteComponentMarker: Final[ValueTerminal]
 
 def get_fws(value: str) -> tuple[WhiteSpaceTerminal, str]: ...
-def get_encoded_word(value: str) -> tuple[EncodedWord, str]: ...
+def get_encoded_word(value: str, terminal_type: str = "vtext") -> tuple[EncodedWord, str]: ...
 def get_unstructured(value: str) -> UnstructuredTokenList: ...
 def get_qp_ctext(value: str) -> tuple[WhiteSpaceTerminal, str]: ...
 def get_qcontent(value: str) -> tuple[ValueTerminal, str]: ...
diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi
index f5dbbd96da14..0fb890d424b1 100644
--- a/mypy/typeshed/stdlib/email/_policybase.pyi
+++ b/mypy/typeshed/stdlib/email/_policybase.pyi
@@ -2,12 +2,13 @@ from abc import ABCMeta, abstractmethod
 from email.errors import MessageDefect
 from email.header import Header
 from email.message import Message
-from typing import Generic, Protocol, TypeVar, type_check_only
+from typing import Any, Generic, Protocol, TypeVar, type_check_only
 from typing_extensions import Self
 
 __all__ = ["Policy", "Compat32", "compat32"]
 
-_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
+_MessageT = TypeVar("_MessageT", bound=Message[Any, Any], default=Message[str, str])
+_MessageT_co = TypeVar("_MessageT_co", covariant=True, bound=Message[Any, Any], default=Message[str, str])
 
 @type_check_only
 class _MessageFactory(Protocol[_MessageT]):
@@ -16,14 +17,14 @@ class _MessageFactory(Protocol[_MessageT]):
 # Policy below is the only known direct subclass of _PolicyBase. We therefore
 # assume that the __init__ arguments and attributes of _PolicyBase are
 # the same as those of Policy.
-class _PolicyBase(Generic[_MessageT]):
+class _PolicyBase(Generic[_MessageT_co]):
     max_line_length: int | None
     linesep: str
     cte_type: str
     raise_on_defect: bool
     mangle_from_: bool
-    message_factory: _MessageFactory[_MessageT] | None
-    # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+    message_factory: _MessageFactory[_MessageT_co] | None
+    # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
     verify_generated_headers: bool
 
     def __init__(
@@ -34,8 +35,8 @@ class _PolicyBase(Generic[_MessageT]):
         cte_type: str = "8bit",
         raise_on_defect: bool = False,
         mangle_from_: bool = ...,  # default depends on sub-class
-        message_factory: _MessageFactory[_MessageT] | None = None,
-        # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+        message_factory: _MessageFactory[_MessageT_co] | None = None,
+        # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
         verify_generated_headers: bool = True,
     ) -> None: ...
     def clone(
@@ -46,15 +47,17 @@ class _PolicyBase(Generic[_MessageT]):
         cte_type: str = ...,
         raise_on_defect: bool = ...,
         mangle_from_: bool = ...,
-        message_factory: _MessageFactory[_MessageT] | None = ...,
-        # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+        message_factory: _MessageFactory[_MessageT_co] | None = ...,
+        # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
         verify_generated_headers: bool = ...,
     ) -> Self: ...
     def __add__(self, other: Policy) -> Self: ...
 
-class Policy(_PolicyBase[_MessageT], metaclass=ABCMeta):
-    def handle_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ...
-    def register_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ...
+class Policy(_PolicyBase[_MessageT_co], metaclass=ABCMeta):
+    # Every Message object has a `defects` attribute, so the following
+    # methods will work for any Message object.
+    def handle_defect(self, obj: Message[Any, Any], defect: MessageDefect) -> None: ...
+    def register_defect(self, obj: Message[Any, Any], defect: MessageDefect) -> None: ...
     def header_max_count(self, name: str) -> int | None: ...
     @abstractmethod
     def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ...
@@ -67,11 +70,11 @@ class Policy(_PolicyBase[_MessageT], metaclass=ABCMeta):
     @abstractmethod
     def fold_binary(self, name: str, value: str) -> bytes: ...
 
-class Compat32(Policy[_MessageT]):
+class Compat32(Policy[_MessageT_co]):
     def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ...
     def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ...
     def header_fetch_parse(self, name: str, value: str) -> str | Header: ...  # type: ignore[override]
     def fold(self, name: str, value: str) -> str: ...
     def fold_binary(self, name: str, value: str) -> bytes: ...
 
-compat32: Compat32[Message]
+compat32: Compat32[Message[str, str]]
diff --git a/mypy/typeshed/stdlib/email/errors.pyi b/mypy/typeshed/stdlib/email/errors.pyi
index f105576c5ee4..b501a5866556 100644
--- a/mypy/typeshed/stdlib/email/errors.pyi
+++ b/mypy/typeshed/stdlib/email/errors.pyi
@@ -7,7 +7,7 @@ class BoundaryError(MessageParseError): ...
 class MultipartConversionError(MessageError, TypeError): ...
 class CharsetError(MessageError): ...
 
-# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+# Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 class HeaderWriteError(MessageError): ...
 
 class MessageDefect(ValueError):
diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi
index 8c268ca1ae18..d9279e9cd996 100644
--- a/mypy/typeshed/stdlib/email/feedparser.pyi
+++ b/mypy/typeshed/stdlib/email/feedparser.pyi
@@ -1,12 +1,11 @@
 from collections.abc import Callable
+from email._policybase import _MessageT
 from email.message import Message
 from email.policy import Policy
-from typing import Generic, TypeVar, overload
+from typing import Generic, overload
 
 __all__ = ["FeedParser", "BytesFeedParser"]
 
-_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
-
 class FeedParser(Generic[_MessageT]):
     @overload
     def __init__(self: FeedParser[Message], _factory: None = None, *, policy: Policy[Message] = ...) -> None: ...
diff --git a/mypy/typeshed/stdlib/email/generator.pyi b/mypy/typeshed/stdlib/email/generator.pyi
index dfa0604a20a9..d30e686299fa 100644
--- a/mypy/typeshed/stdlib/email/generator.pyi
+++ b/mypy/typeshed/stdlib/email/generator.pyi
@@ -7,7 +7,7 @@ from typing_extensions import Self
 __all__ = ["Generator", "DecodedGenerator", "BytesGenerator"]
 
 # By default, generators do not have a message policy.
-_MessageT = TypeVar("_MessageT", bound=Message, default=Any)
+_MessageT = TypeVar("_MessageT", bound=Message[Any, Any], default=Any)
 
 class Generator(Generic[_MessageT]):
     maxheaderlen: int | None
diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi
index ebad05a1cf7b..e4d14992168a 100644
--- a/mypy/typeshed/stdlib/email/message.pyi
+++ b/mypy/typeshed/stdlib/email/message.pyi
@@ -12,12 +12,12 @@ __all__ = ["Message", "EmailMessage"]
 
 _T = TypeVar("_T")
 # Type returned by Policy.header_fetch_parse, often str or Header.
-_HeaderT = TypeVar("_HeaderT", default=str)
-_HeaderParamT = TypeVar("_HeaderParamT", default=str)
+_HeaderT_co = TypeVar("_HeaderT_co", covariant=True, default=str)
+_HeaderParamT_contra = TypeVar("_HeaderParamT_contra", contravariant=True, default=str)
 # Represents headers constructed by HeaderRegistry. Those are sub-classes
 # of BaseHeader and another header type.
-_HeaderRegistryT = TypeVar("_HeaderRegistryT", default=Any)
-_HeaderRegistryParamT = TypeVar("_HeaderRegistryParamT", default=Any)
+_HeaderRegistryT_co = TypeVar("_HeaderRegistryT_co", covariant=True, default=Any)
+_HeaderRegistryParamT_contra = TypeVar("_HeaderRegistryParamT_contra", contravariant=True, default=Any)
 
 _PayloadType: TypeAlias = Message | str
 _EncodedPayloadType: TypeAlias = Message | bytes
@@ -30,7 +30,7 @@ class _SupportsEncodeToPayload(Protocol):
 class _SupportsDecodeToPayload(Protocol):
     def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ...
 
-class Message(Generic[_HeaderT, _HeaderParamT]):
+class Message(Generic[_HeaderT_co, _HeaderParamT_contra]):
     # The policy attributes and arguments in this class and its subclasses
     # would ideally use Policy[Self], but this is not possible.
     policy: Policy[Any]  # undocumented
@@ -76,22 +76,22 @@ class Message(Generic[_HeaderT, _HeaderParamT]):
     # This is important for protocols using __getitem__, like SupportsKeysAndGetItem
     # Morally, the return type should be `AnyOf[_HeaderType, None]`,
     # so using "the Any trick" instead.
-    def __getitem__(self, name: str) -> _HeaderT | MaybeNone: ...
-    def __setitem__(self, name: str, val: _HeaderParamT) -> None: ...
+    def __getitem__(self, name: str) -> _HeaderT_co | MaybeNone: ...
+    def __setitem__(self, name: str, val: _HeaderParamT_contra) -> None: ...
     def __delitem__(self, name: str) -> None: ...
     def keys(self) -> list[str]: ...
-    def values(self) -> list[_HeaderT]: ...
-    def items(self) -> list[tuple[str, _HeaderT]]: ...
+    def values(self) -> list[_HeaderT_co]: ...
+    def items(self) -> list[tuple[str, _HeaderT_co]]: ...
     @overload
-    def get(self, name: str, failobj: None = None) -> _HeaderT | None: ...
+    def get(self, name: str, failobj: None = None) -> _HeaderT_co | None: ...
     @overload
-    def get(self, name: str, failobj: _T) -> _HeaderT | _T: ...
+    def get(self, name: str, failobj: _T) -> _HeaderT_co | _T: ...
     @overload
-    def get_all(self, name: str, failobj: None = None) -> list[_HeaderT] | None: ...
+    def get_all(self, name: str, failobj: None = None) -> list[_HeaderT_co] | None: ...
     @overload
-    def get_all(self, name: str, failobj: _T) -> list[_HeaderT] | _T: ...
+    def get_all(self, name: str, failobj: _T) -> list[_HeaderT_co] | _T: ...
     def add_header(self, _name: str, _value: str, **_params: _ParamsType) -> None: ...
-    def replace_header(self, _name: str, _value: _HeaderParamT) -> None: ...
+    def replace_header(self, _name: str, _value: _HeaderParamT_contra) -> None: ...
     def get_content_type(self) -> str: ...
     def get_content_maintype(self) -> str: ...
     def get_content_subtype(self) -> str: ...
@@ -144,18 +144,18 @@ class Message(Generic[_HeaderT, _HeaderParamT]):
         replace: bool = False,
     ) -> None: ...
     # The following two methods are undocumented, but a source code comment states that they are public API
-    def set_raw(self, name: str, value: _HeaderParamT) -> None: ...
-    def raw_items(self) -> Iterator[tuple[str, _HeaderT]]: ...
+    def set_raw(self, name: str, value: _HeaderParamT_contra) -> None: ...
+    def raw_items(self) -> Iterator[tuple[str, _HeaderT_co]]: ...
 
-class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]):
+class MIMEPart(Message[_HeaderRegistryT_co, _HeaderRegistryParamT_contra]):
     def __init__(self, policy: Policy[Any] | None = None) -> None: ...
-    def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ...
+    def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT_co] | None: ...
     def attach(self, payload: Self) -> None: ...  # type: ignore[override]
     # The attachments are created via type(self) in the attach method. It's theoretically
     # possible to sneak other attachment types into a MIMEPart instance, but could cause
     # cause unforseen consequences.
     def iter_attachments(self) -> Iterator[Self]: ...
-    def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ...
+    def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT_co]]: ...
     def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ...
     def set_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> None: ...
     def make_related(self, boundary: str | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/email/mime/message.pyi b/mypy/typeshed/stdlib/email/mime/message.pyi
index 2a5f46296150..a1e370e2eab5 100644
--- a/mypy/typeshed/stdlib/email/mime/message.pyi
+++ b/mypy/typeshed/stdlib/email/mime/message.pyi
@@ -1,5 +1,6 @@
+from email._policybase import _MessageT
 from email.mime.nonmultipart import MIMENonMultipart
-from email.policy import Policy, _MessageT
+from email.policy import Policy
 
 __all__ = ["MIMEMessage"]
 
diff --git a/mypy/typeshed/stdlib/email/mime/multipart.pyi b/mypy/typeshed/stdlib/email/mime/multipart.pyi
index 1c229f7436a8..fb9599edbcb8 100644
--- a/mypy/typeshed/stdlib/email/mime/multipart.pyi
+++ b/mypy/typeshed/stdlib/email/mime/multipart.pyi
@@ -1,7 +1,8 @@
 from collections.abc import Sequence
 from email import _ParamsType
+from email._policybase import _MessageT
 from email.mime.base import MIMEBase
-from email.policy import Policy, _MessageT
+from email.policy import Policy
 
 __all__ = ["MIMEMultipart"]
 
diff --git a/mypy/typeshed/stdlib/email/mime/text.pyi b/mypy/typeshed/stdlib/email/mime/text.pyi
index 74d5ef4c5cae..edfa67a09242 100644
--- a/mypy/typeshed/stdlib/email/mime/text.pyi
+++ b/mypy/typeshed/stdlib/email/mime/text.pyi
@@ -1,5 +1,5 @@
+from email._policybase import Policy
 from email.mime.nonmultipart import MIMENonMultipart
-from email.policy import Policy
 
 __all__ = ["MIMEText"]
 
diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi
index a1a57b4eef4b..a4924a6cbd88 100644
--- a/mypy/typeshed/stdlib/email/parser.pyi
+++ b/mypy/typeshed/stdlib/email/parser.pyi
@@ -1,20 +1,21 @@
 from _typeshed import SupportsRead
 from collections.abc import Callable
+from email._policybase import _MessageT
 from email.feedparser import BytesFeedParser as BytesFeedParser, FeedParser as FeedParser
 from email.message import Message
 from email.policy import Policy
 from io import _WrappedBuffer
-from typing import Generic, TypeVar, overload
+from typing import Generic, overload
 
 __all__ = ["Parser", "HeaderParser", "BytesParser", "BytesHeaderParser", "FeedParser", "BytesFeedParser"]
 
-_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
-
 class Parser(Generic[_MessageT]):
     @overload
-    def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ...) -> None: ...
+    def __init__(self: Parser[Message[str, str]], _class: None = None) -> None: ...
     @overload
-    def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ...
+    def __init__(self, _class: None = None, *, policy: Policy[_MessageT]) -> None: ...
+    @overload
+    def __init__(self, _class: Callable[[], _MessageT] | None, *, policy: Policy[_MessageT] = ...) -> None: ...
     def parse(self, fp: SupportsRead[str], headersonly: bool = False) -> _MessageT: ...
     def parsestr(self, text: str, headersonly: bool = False) -> _MessageT: ...
 
@@ -25,9 +26,9 @@ class HeaderParser(Parser[_MessageT]):
 class BytesParser(Generic[_MessageT]):
     parser: Parser[_MessageT]
     @overload
-    def __init__(
-        self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ...
-    ) -> None: ...
+    def __init__(self: BytesParser[Message[str, str]], _class: None = None) -> None: ...
+    @overload
+    def __init__(self, _class: None = None, *, policy: Policy[_MessageT]) -> None: ...
     @overload
     def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ...
     def parse(self, fp: _WrappedBuffer, headersonly: bool = False) -> _MessageT: ...
diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi
index 5b145bcf2318..35c999919eed 100644
--- a/mypy/typeshed/stdlib/email/policy.pyi
+++ b/mypy/typeshed/stdlib/email/policy.pyi
@@ -1,14 +1,12 @@
 from collections.abc import Callable
-from email._policybase import Compat32 as Compat32, Policy as Policy, _MessageFactory, compat32 as compat32
+from email._policybase import Compat32 as Compat32, Policy as Policy, _MessageFactory, _MessageT, compat32 as compat32
 from email.contentmanager import ContentManager
-from email.message import EmailMessage, Message
-from typing import Any, TypeVar, overload
+from email.message import EmailMessage
+from typing import Any, overload
 from typing_extensions import Self
 
 __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"]
 
-_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
-
 class EmailPolicy(Policy[_MessageT]):
     utf8: bool
     refold_source: str
@@ -24,7 +22,7 @@ class EmailPolicy(Policy[_MessageT]):
         raise_on_defect: bool = ...,
         mangle_from_: bool = ...,
         message_factory: None = None,
-        # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+        # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
         verify_generated_headers: bool = ...,
         utf8: bool = ...,
         refold_source: str = ...,
@@ -41,7 +39,7 @@ class EmailPolicy(Policy[_MessageT]):
         raise_on_defect: bool = ...,
         mangle_from_: bool = ...,
         message_factory: _MessageFactory[_MessageT] | None = ...,
-        # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+        # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
         verify_generated_headers: bool = ...,
         utf8: bool = ...,
         refold_source: str = ...,
@@ -62,7 +60,7 @@ class EmailPolicy(Policy[_MessageT]):
         raise_on_defect: bool = ...,
         mangle_from_: bool = ...,
         message_factory: _MessageFactory[_MessageT] | None = ...,
-        # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+        # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
         verify_generated_headers: bool = ...,
         utf8: bool = ...,
         refold_source: str = ...,
diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi
index dc3eecb5ef7f..efc32a7abce2 100644
--- a/mypy/typeshed/stdlib/email/utils.pyi
+++ b/mypy/typeshed/stdlib/email/utils.pyi
@@ -30,11 +30,11 @@ _PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None
 def quote(str: str) -> str: ...
 def unquote(str: str) -> str: ...
 
-# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+# `strict` parameter added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 def parseaddr(addr: str | list[str], *, strict: bool = True) -> tuple[str, str]: ...
 def formataddr(pair: tuple[str | None, str], charset: str | Charset = "utf-8") -> str: ...
 
-# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5
+# `strict` parameter added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 def getaddresses(fieldvalues: Iterable[str], *, strict: bool = True) -> list[tuple[str, str]]: ...
 @overload
 def parsedate(data: None) -> None: ...
diff --git a/mypy/typeshed/stdlib/encodings/__init__.pyi b/mypy/typeshed/stdlib/encodings/__init__.pyi
index 2e83f0f65a71..12ec6792d49b 100644
--- a/mypy/typeshed/stdlib/encodings/__init__.pyi
+++ b/mypy/typeshed/stdlib/encodings/__init__.pyi
@@ -1,4 +1,3 @@
-from _typeshed import Incomplete
 from codecs import CodecInfo
 
 class CodecRegistryError(LookupError, SystemError): ...
@@ -7,4 +6,4 @@ def normalize_encoding(encoding: str | bytes) -> str: ...
 def search_function(encoding: str) -> CodecInfo | None: ...
 
 # Needed for submodules
-def __getattr__(name: str) -> Incomplete: ...
+def __getattr__(name: str): ...  # incomplete module
diff --git a/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi b/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi
deleted file mode 100644
index f62195662ce9..000000000000
--- a/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi
+++ /dev/null
@@ -1,21 +0,0 @@
-import codecs
-from _codecs import _EncodingMap
-from _typeshed import ReadableBuffer
-
-class Codec(codecs.Codec):
-    def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
-    def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
-
-class IncrementalEncoder(codecs.IncrementalEncoder):
-    def encode(self, input: str, final: bool = False) -> bytes: ...
-
-class IncrementalDecoder(codecs.IncrementalDecoder):
-    def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
-
-class StreamWriter(Codec, codecs.StreamWriter): ...
-class StreamReader(Codec, codecs.StreamReader): ...
-
-def getregentry() -> codecs.CodecInfo: ...
-
-decoding_table: str
-encoding_table: _EncodingMap
diff --git a/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi
index 74abb4623fab..2887739468f2 100644
--- a/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi
+++ b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi
@@ -1,5 +1,4 @@
 import codecs
-import sys
 from _typeshed import ReadableBuffer
 
 class Codec(codecs.Codec):
@@ -7,28 +6,18 @@ class Codec(codecs.Codec):
     @staticmethod
     def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
     # At runtime, this is codecs.raw_unicode_escape_decode
-    if sys.version_info >= (3, 9):
-        @staticmethod
-        def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ...
-    else:
-        @staticmethod
-        def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
+    @staticmethod
+    def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ...
 
 class IncrementalEncoder(codecs.IncrementalEncoder):
     def encode(self, input: str, final: bool = False) -> bytes: ...
 
-if sys.version_info >= (3, 9):
-    class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
-        def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ...
-
-else:
-    class IncrementalDecoder(codecs.IncrementalDecoder):
-        def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ...
+class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
+    def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ...
 
 class StreamWriter(Codec, codecs.StreamWriter): ...
 
 class StreamReader(Codec, codecs.StreamReader):
-    if sys.version_info >= (3, 9):
-        def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ...  # type: ignore[override]
+    def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ...  # type: ignore[override]
 
 def getregentry() -> codecs.CodecInfo: ...
diff --git a/mypy/typeshed/stdlib/encodings/unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi
index 1e942f57916e..ceaa39a3859a 100644
--- a/mypy/typeshed/stdlib/encodings/unicode_escape.pyi
+++ b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi
@@ -1,5 +1,4 @@
 import codecs
-import sys
 from _typeshed import ReadableBuffer
 
 class Codec(codecs.Codec):
@@ -7,28 +6,18 @@ class Codec(codecs.Codec):
     @staticmethod
     def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
     # At runtime, this is codecs.unicode_escape_decode
-    if sys.version_info >= (3, 9):
-        @staticmethod
-        def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ...
-    else:
-        @staticmethod
-        def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
+    @staticmethod
+    def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ...
 
 class IncrementalEncoder(codecs.IncrementalEncoder):
     def encode(self, input: str, final: bool = False) -> bytes: ...
 
-if sys.version_info >= (3, 9):
-    class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
-        def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ...
-
-else:
-    class IncrementalDecoder(codecs.IncrementalDecoder):
-        def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ...
+class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
+    def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ...
 
 class StreamWriter(Codec, codecs.StreamWriter): ...
 
 class StreamReader(Codec, codecs.StreamReader):
-    if sys.version_info >= (3, 9):
-        def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ...  # type: ignore[override]
+    def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ...  # type: ignore[override]
 
 def getregentry() -> codecs.CodecInfo: ...
diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi
index 4a6287a712af..26f198867113 100644
--- a/mypy/typeshed/stdlib/enum.pyi
+++ b/mypy/typeshed/stdlib/enum.pyi
@@ -100,20 +100,13 @@ class EnumMeta(type):
             _simple: bool = False,
             **kwds: Any,
         ) -> _typeshed.Self: ...
-    elif sys.version_info >= (3, 9):
+    else:
         def __new__(
             metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict, **kwds: Any
         ) -> _typeshed.Self: ...
-    else:
-        def __new__(metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict) -> _typeshed.Self: ...
-
-    if sys.version_info >= (3, 9):
-        @classmethod
-        def __prepare__(metacls, cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict: ...  # type: ignore[override]
-    else:
-        @classmethod
-        def __prepare__(metacls, cls: str, bases: tuple[type, ...]) -> _EnumDict: ...  # type: ignore[override]
 
+    @classmethod
+    def __prepare__(metacls, cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict: ...  # type: ignore[override]
     def __iter__(self: type[_EnumMemberT]) -> Iterator[_EnumMemberT]: ...
     def __reversed__(self: type[_EnumMemberT]) -> Iterator[_EnumMemberT]: ...
     if sys.version_info >= (3, 12):
@@ -306,6 +299,7 @@ if sys.version_info >= (3, 11):
         def __or__(self, other: int) -> Self: ...
         def __and__(self, other: int) -> Self: ...
         def __xor__(self, other: int) -> Self: ...
+        def __invert__(self) -> Self: ...
         __ror__ = __or__
         __rand__ = __and__
         __rxor__ = __xor__
@@ -316,6 +310,7 @@ else:
         def __or__(self, other: int) -> Self: ...
         def __and__(self, other: int) -> Self: ...
         def __xor__(self, other: int) -> Self: ...
+        def __invert__(self) -> Self: ...
         __ror__ = __or__
         __rand__ = __and__
         __rxor__ = __xor__
diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi
index 71078b3b4579..2fe64eb53201 100644
--- a/mypy/typeshed/stdlib/fcntl.pyi
+++ b/mypy/typeshed/stdlib/fcntl.pyi
@@ -26,8 +26,7 @@ if sys.platform != "win32":
     if sys.platform == "darwin":
         F_FULLFSYNC: int
         F_NOCACHE: int
-        if sys.version_info >= (3, 9):
-            F_GETPATH: int
+        F_GETPATH: int
     if sys.platform == "linux":
         F_SETLKW64: int
         F_SETSIG: int
@@ -43,10 +42,9 @@ if sys.platform != "win32":
         F_SEAL_SEAL: int
         F_SEAL_SHRINK: int
         F_SEAL_WRITE: int
-        if sys.version_info >= (3, 9):
-            F_OFD_GETLK: Final[int]
-            F_OFD_SETLK: Final[int]
-            F_OFD_SETLKW: Final[int]
+        F_OFD_GETLK: Final[int]
+        F_OFD_SETLK: Final[int]
+        F_OFD_SETLKW: Final[int]
 
         if sys.version_info >= (3, 10):
             F_GETPIPE_SZ: int
diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi
index cb7b94596077..a2a2b235fdad 100644
--- a/mypy/typeshed/stdlib/filecmp.pyi
+++ b/mypy/typeshed/stdlib/filecmp.pyi
@@ -1,11 +1,9 @@
 import sys
 from _typeshed import GenericPath, StrOrBytesPath
 from collections.abc import Callable, Iterable, Sequence
+from types import GenericAlias
 from typing import Any, AnyStr, Final, Generic, Literal
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"]
 
 DEFAULT_IGNORES: list[str]
@@ -62,7 +60,6 @@ class dircmp(Generic[AnyStr]):
     def phase3(self) -> None: ...
     def phase4(self) -> None: ...
     def phase4_closure(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 def clear_cache() -> None: ...
diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi
index 1e6aa78e2607..1d5f9cf00f36 100644
--- a/mypy/typeshed/stdlib/fileinput.pyi
+++ b/mypy/typeshed/stdlib/fileinput.pyi
@@ -1,13 +1,10 @@
 import sys
 from _typeshed import AnyStr_co, StrOrBytesPath
 from collections.abc import Callable, Iterable, Iterator
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import IO, Any, AnyStr, Literal, Protocol, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "input",
     "close",
@@ -199,8 +196,7 @@ class FileInput(Iterator[AnyStr]):
     def fileno(self) -> int: ...
     def isfirstline(self) -> bool: ...
     def isstdin(self) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 if sys.version_info >= (3, 10):
     def hook_compressed(
diff --git a/mypy/typeshed/stdlib/fnmatch.pyi b/mypy/typeshed/stdlib/fnmatch.pyi
index 7051c999c430..345c4576497d 100644
--- a/mypy/typeshed/stdlib/fnmatch.pyi
+++ b/mypy/typeshed/stdlib/fnmatch.pyi
@@ -1,9 +1,15 @@
+import sys
 from collections.abc import Iterable
 from typing import AnyStr
 
 __all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
+if sys.version_info >= (3, 14):
+    __all__ += ["filterfalse"]
 
 def fnmatch(name: AnyStr, pat: AnyStr) -> bool: ...
 def fnmatchcase(name: AnyStr, pat: AnyStr) -> bool: ...
 def filter(names: Iterable[AnyStr], pat: AnyStr) -> list[AnyStr]: ...
 def translate(pat: str) -> str: ...
+
+if sys.version_info >= (3, 14):
+    def filterfalse(names: Iterable[AnyStr], pat: AnyStr) -> list[AnyStr]: ...
diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi
index aaa3a22087fc..83592eb58336 100644
--- a/mypy/typeshed/stdlib/fractions.pyi
+++ b/mypy/typeshed/stdlib/fractions.pyi
@@ -1,24 +1,13 @@
 import sys
 from collections.abc import Callable
 from decimal import Decimal
-from numbers import Integral, Rational, Real
+from numbers import Rational, Real
 from typing import Any, Literal, Protocol, SupportsIndex, overload
 from typing_extensions import Self, TypeAlias
 
 _ComparableNum: TypeAlias = int | float | Decimal | Real
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Fraction"]
-else:
-    __all__ = ["Fraction", "gcd"]
-    @overload
-    def gcd(a: int, b: int) -> int: ...
-    @overload
-    def gcd(a: Integral, b: int) -> Integral: ...
-    @overload
-    def gcd(a: int, b: Integral) -> Integral: ...
-    @overload
-    def gcd(a: Integral, b: Integral) -> Integral: ...
+__all__ = ["Fraction"]
 
 class _ConvertibleToIntegerRatio(Protocol):
     def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ...
@@ -156,3 +145,6 @@ class Fraction(Rational):
     @property
     def imag(self) -> Literal[0]: ...
     def conjugate(self) -> Fraction: ...
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def from_number(cls, number: float | Rational | _ConvertibleToIntegerRatio) -> Self: ...
diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi
index 3693d7c52a26..44bc2165fe0e 100644
--- a/mypy/typeshed/stdlib/ftplib.pyi
+++ b/mypy/typeshed/stdlib/ftplib.pyi
@@ -41,29 +41,17 @@ class FTP:
         self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
     ) -> None: ...
     source_address: tuple[str, int] | None
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            host: str = "",
-            user: str = "",
-            passwd: str = "",
-            acct: str = "",
-            timeout: float | None = ...,
-            source_address: tuple[str, int] | None = None,
-            *,
-            encoding: str = "utf-8",
-        ) -> None: ...
-    else:
-        def __init__(
-            self,
-            host: str = "",
-            user: str = "",
-            passwd: str = "",
-            acct: str = "",
-            timeout: float | None = ...,
-            source_address: tuple[str, int] | None = None,
-        ) -> None: ...
-
+    def __init__(
+        self,
+        host: str = "",
+        user: str = "",
+        passwd: str = "",
+        acct: str = "",
+        timeout: float | None = ...,
+        source_address: tuple[str, int] | None = None,
+        *,
+        encoding: str = "utf-8",
+    ) -> None: ...
     def connect(
         self, host: str = "", port: int = 0, timeout: float = -999, source_address: tuple[str, int] | None = None
     ) -> str: ...
@@ -131,7 +119,7 @@ class FTP_TLS(FTP):
             source_address: tuple[str, int] | None = None,
             encoding: str = "utf-8",
         ) -> None: ...
-    elif sys.version_info >= (3, 9):
+    else:
         def __init__(
             self,
             host: str = "",
@@ -146,19 +134,6 @@ class FTP_TLS(FTP):
             *,
             encoding: str = "utf-8",
         ) -> None: ...
-    else:
-        def __init__(
-            self,
-            host: str = "",
-            user: str = "",
-            passwd: str = "",
-            acct: str = "",
-            keyfile: str | None = None,
-            certfile: str | None = None,
-            context: SSLContext | None = None,
-            timeout: float | None = ...,
-            source_address: tuple[str, int] | None = None,
-        ) -> None: ...
     ssl_version: int
     keyfile: str | None
     certfile: str | None
diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi
index f786167e322d..e31399fb8705 100644
--- a/mypy/typeshed/stdlib/functools.pyi
+++ b/mypy/typeshed/stdlib/functools.pyi
@@ -2,12 +2,10 @@ import sys
 import types
 from _typeshed import SupportsAllComparisons, SupportsItems
 from collections.abc import Callable, Hashable, Iterable, Sized
-from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload
+from types import GenericAlias
+from typing import Any, Final, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload
 from typing_extensions import ParamSpec, Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "update_wrapper",
     "wraps",
@@ -22,11 +20,9 @@ __all__ = [
     "singledispatch",
     "cached_property",
     "singledispatchmethod",
+    "cache",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["cache"]
-
 _T = TypeVar("_T")
 _T_co = TypeVar("_T_co", covariant=True)
 _S = TypeVar("_S")
@@ -35,10 +31,16 @@ _RWrapped = TypeVar("_RWrapped")
 _PWrapper = ParamSpec("_PWrapper")
 _RWrapper = TypeVar("_RWrapper")
 
+if sys.version_info >= (3, 14):
+    @overload
+    def reduce(function: Callable[[_T, _S], _T], iterable: Iterable[_S], /, initial: _T) -> _T: ...
+
+else:
+    @overload
+    def reduce(function: Callable[[_T, _S], _T], iterable: Iterable[_S], initial: _T, /) -> _T: ...
+
 @overload
-def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T, /) -> _T: ...
-@overload
-def reduce(function: Callable[[_T, _T], _T], sequence: Iterable[_T], /) -> _T: ...
+def reduce(function: Callable[[_T, _T], _T], iterable: Iterable[_T], /) -> _T: ...
 
 class _CacheInfo(NamedTuple):
     hits: int
@@ -46,10 +48,9 @@ class _CacheInfo(NamedTuple):
     maxsize: int | None
     currsize: int
 
-if sys.version_info >= (3, 9):
-    class _CacheParameters(TypedDict):
-        maxsize: int
-        typed: bool
+class _CacheParameters(TypedDict):
+    maxsize: int
+    typed: bool
 
 @final
 class _lru_cache_wrapper(Generic[_T]):
@@ -57,9 +58,7 @@ class _lru_cache_wrapper(Generic[_T]):
     def __call__(self, *args: Hashable, **kwargs: Hashable) -> _T: ...
     def cache_info(self) -> _CacheInfo: ...
     def cache_clear(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def cache_parameters(self) -> _CacheParameters: ...
-
+    def cache_parameters(self) -> _CacheParameters: ...
     def __copy__(self) -> _lru_cache_wrapper[_T]: ...
     def __deepcopy__(self, memo: Any, /) -> _lru_cache_wrapper[_T]: ...
 
@@ -68,19 +67,33 @@ def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Calla
 @overload
 def lru_cache(maxsize: Callable[..., _T], typed: bool = False) -> _lru_cache_wrapper[_T]: ...
 
-if sys.version_info >= (3, 12):
-    WRAPPER_ASSIGNMENTS: tuple[
-        Literal["__module__"],
-        Literal["__name__"],
-        Literal["__qualname__"],
-        Literal["__doc__"],
-        Literal["__annotations__"],
-        Literal["__type_params__"],
+if sys.version_info >= (3, 14):
+    WRAPPER_ASSIGNMENTS: Final[
+        tuple[
+            Literal["__module__"],
+            Literal["__name__"],
+            Literal["__qualname__"],
+            Literal["__doc__"],
+            Literal["__annotate__"],
+            Literal["__type_params__"],
+        ]
+    ]
+elif sys.version_info >= (3, 12):
+    WRAPPER_ASSIGNMENTS: Final[
+        tuple[
+            Literal["__module__"],
+            Literal["__name__"],
+            Literal["__qualname__"],
+            Literal["__doc__"],
+            Literal["__annotations__"],
+            Literal["__type_params__"],
+        ]
     ]
 else:
-    WRAPPER_ASSIGNMENTS: tuple[
-        Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"]
+    WRAPPER_ASSIGNMENTS: Final[
+        tuple[Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"]]
     ]
+
 WRAPPER_UPDATES: tuple[Literal["__dict__"]]
 
 class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]):
@@ -93,7 +106,20 @@ class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]):
 class _Wrapper(Generic[_PWrapped, _RWrapped]):
     def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ...
 
-if sys.version_info >= (3, 12):
+if sys.version_info >= (3, 14):
+    def update_wrapper(
+        wrapper: Callable[_PWrapper, _RWrapper],
+        wrapped: Callable[_PWrapped, _RWrapped],
+        assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotate__", "__type_params__"),
+        updated: Iterable[str] = ("__dict__",),
+    ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ...
+    def wraps(
+        wrapped: Callable[_PWrapped, _RWrapped],
+        assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotate__", "__type_params__"),
+        updated: Iterable[str] = ("__dict__",),
+    ) -> _Wrapper[_PWrapped, _RWrapped]: ...
+
+elif sys.version_info >= (3, 12):
     def update_wrapper(
         wrapper: Callable[_PWrapper, _RWrapper],
         wrapped: Callable[_PWrapped, _RWrapped],
@@ -131,8 +157,7 @@ class partial(Generic[_T]):
     def keywords(self) -> dict[str, Any]: ...
     def __new__(cls, func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> Self: ...
     def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # With protocols, this could change into a generic protocol that defines __get__ and returns _T
 _Descriptor: TypeAlias = Any
@@ -148,8 +173,7 @@ class partialmethod(Generic[_T]):
     def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ...
     @property
     def __isabstractmethod__(self) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 if sys.version_info >= (3, 11):
     _RegType: TypeAlias = type[Any] | types.UnionType
@@ -200,12 +224,9 @@ class cached_property(Generic[_T_co]):
     def __set_name__(self, owner: type[Any], name: str) -> None: ...
     # __set__ is not defined at runtime, but @cached_property is designed to be settable
     def __set__(self, instance: object, value: _T_co) -> None: ...  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
-
-if sys.version_info >= (3, 9):
-    def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
+def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ...
 def _make_key(
     args: tuple[Hashable, ...],
     kwds: SupportsItems[Any, Any],
@@ -216,3 +237,11 @@ def _make_key(
     type: Any = ...,
     len: Callable[[Sized], int] = ...,
 ) -> Hashable: ...
+
+if sys.version_info >= (3, 14):
+    @final
+    class _PlaceholderType: ...
+
+    Placeholder: Final[_PlaceholderType]
+
+    __all__ += ["Placeholder"]
diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi
index 9d34e0d6213a..06fb6b47c2d1 100644
--- a/mypy/typeshed/stdlib/gc.pyi
+++ b/mypy/typeshed/stdlib/gc.pyi
@@ -1,4 +1,3 @@
-import sys
 from collections.abc import Callable
 from typing import Any, Final, Literal
 from typing_extensions import TypeAlias
@@ -28,10 +27,7 @@ def get_referrers(*objs: Any) -> list[Any]: ...
 def get_stats() -> list[dict[str, Any]]: ...
 def get_threshold() -> tuple[int, int, int]: ...
 def is_tracked(obj: Any, /) -> bool: ...
-
-if sys.version_info >= (3, 9):
-    def is_finalized(obj: Any, /) -> bool: ...
-
+def is_finalized(obj: Any, /) -> bool: ...
 def isenabled() -> bool: ...
 def set_debug(flags: int, /) -> None: ...
 def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ..., /) -> None: ...
diff --git a/mypy/typeshed/stdlib/getpass.pyi b/mypy/typeshed/stdlib/getpass.pyi
index 6104e0dedfee..bb3013dfbf39 100644
--- a/mypy/typeshed/stdlib/getpass.pyi
+++ b/mypy/typeshed/stdlib/getpass.pyi
@@ -1,8 +1,14 @@
+import sys
 from typing import TextIO
 
 __all__ = ["getpass", "getuser", "GetPassWarning"]
 
-def getpass(prompt: str = "Password: ", stream: TextIO | None = None) -> str: ...
+if sys.version_info >= (3, 14):
+    def getpass(prompt: str = "Password: ", stream: TextIO | None = None, *, echo_char: str | None = None) -> str: ...
+
+else:
+    def getpass(prompt: str = "Password: ", stream: TextIO | None = None) -> str: ...
+
 def getuser() -> str: ...
 
 class GetPassWarning(UserWarning): ...
diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi
index b7fb40fbd82e..883456b1ddc3 100644
--- a/mypy/typeshed/stdlib/gzip.pyi
+++ b/mypy/typeshed/stdlib/gzip.pyi
@@ -1,4 +1,3 @@
-import _compression
 import sys
 import zlib
 from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath
@@ -6,6 +5,11 @@ from io import FileIO, TextIOWrapper
 from typing import Final, Literal, Protocol, overload
 from typing_extensions import TypeAlias
 
+if sys.version_info >= (3, 14):
+    from compression._common._streams import BaseStream, DecompressReader
+else:
+    from _compression import BaseStream, DecompressReader
+
 __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
 
 _ReadBinaryMode: TypeAlias = Literal["r", "rb"]
@@ -84,7 +88,7 @@ class _PaddedFile:
 
 class BadGzipFile(OSError): ...
 
-class GzipFile(_compression.BaseStream):
+class GzipFile(BaseStream):
     myfileobj: FileIO | None
     mode: object
     name: str
@@ -153,7 +157,7 @@ class GzipFile(_compression.BaseStream):
     def seek(self, offset: int, whence: int = 0) -> int: ...
     def readline(self, size: int | None = -1) -> bytes: ...
 
-class _GzipReader(_compression.DecompressReader):
+class _GzipReader(DecompressReader):
     def __init__(self, fp: _ReadableFileobj) -> None: ...
 
 def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi
index 84666a7fa725..b32c0e992574 100644
--- a/mypy/typeshed/stdlib/hashlib.pyi
+++ b/mypy/typeshed/stdlib/hashlib.pyi
@@ -5,16 +5,22 @@ from _hashlib import (
     _HashObject,
     openssl_md5 as md5,
     openssl_sha1 as sha1,
+    openssl_sha3_224 as sha3_224,
+    openssl_sha3_256 as sha3_256,
+    openssl_sha3_384 as sha3_384,
+    openssl_sha3_512 as sha3_512,
     openssl_sha224 as sha224,
     openssl_sha256 as sha256,
     openssl_sha384 as sha384,
     openssl_sha512 as sha512,
+    openssl_shake_128 as shake_128,
+    openssl_shake_256 as shake_256,
     pbkdf2_hmac as pbkdf2_hmac,
     scrypt as scrypt,
 )
 from _typeshed import ReadableBuffer
 from collections.abc import Callable, Set as AbstractSet
-from typing import Protocol, type_check_only
+from typing import Protocol
 
 if sys.version_info >= (3, 11):
     __all__ = (
@@ -60,31 +66,7 @@ else:
         "pbkdf2_hmac",
     )
 
-if sys.version_info >= (3, 9):
-    def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> HASH: ...
-    from _hashlib import (
-        openssl_sha3_224 as sha3_224,
-        openssl_sha3_256 as sha3_256,
-        openssl_sha3_384 as sha3_384,
-        openssl_sha3_512 as sha3_512,
-        openssl_shake_128 as shake_128,
-        openssl_shake_256 as shake_256,
-    )
-
-else:
-    @type_check_only
-    class _VarLenHash(HASH):
-        def digest(self, length: int) -> bytes: ...  # type: ignore[override]
-        def hexdigest(self, length: int) -> str: ...  # type: ignore[override]
-
-    def new(name: str, data: ReadableBuffer = b"") -> HASH: ...
-    # At runtime these aren't functions but classes imported from _sha3
-    def sha3_224(string: ReadableBuffer = b"") -> HASH: ...
-    def sha3_256(string: ReadableBuffer = b"") -> HASH: ...
-    def sha3_384(string: ReadableBuffer = b"") -> HASH: ...
-    def sha3_512(string: ReadableBuffer = b"") -> HASH: ...
-    def shake_128(string: ReadableBuffer = b"") -> _VarLenHash: ...
-    def shake_256(string: ReadableBuffer = b"") -> _VarLenHash: ...
+def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> HASH: ...
 
 algorithms_guaranteed: AbstractSet[str]
 algorithms_available: AbstractSet[str]
diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi
index dfb574c177cd..300ed9eb26d8 100644
--- a/mypy/typeshed/stdlib/hmac.pyi
+++ b/mypy/typeshed/stdlib/hmac.pyi
@@ -1,9 +1,8 @@
-import sys
-from _hashlib import _HashObject
+from _hashlib import _HashObject, compare_digest as compare_digest
 from _typeshed import ReadableBuffer, SizedBuffer
 from collections.abc import Callable
 from types import ModuleType
-from typing import AnyStr, overload
+from typing import overload
 from typing_extensions import TypeAlias
 
 _DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType
@@ -32,11 +31,3 @@ class HMAC:
     def copy(self) -> HMAC: ...
 
 def digest(key: SizedBuffer, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ...
-
-if sys.version_info >= (3, 9):
-    from _hashlib import compare_digest as compare_digest
-else:
-    @overload
-    def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
-    @overload
-    def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi
index ef413a349125..f60c3909736d 100644
--- a/mypy/typeshed/stdlib/http/__init__.pyi
+++ b/mypy/typeshed/stdlib/http/__init__.pyi
@@ -19,8 +19,7 @@ class HTTPStatus(IntEnum):
     CONTINUE = 100
     SWITCHING_PROTOCOLS = 101
     PROCESSING = 102
-    if sys.version_info >= (3, 9):
-        EARLY_HINTS = 103
+    EARLY_HINTS = 103
 
     OK = 200
     CREATED = 201
@@ -66,16 +65,14 @@ class HTTPStatus(IntEnum):
         RANGE_NOT_SATISFIABLE = 416
     REQUESTED_RANGE_NOT_SATISFIABLE = 416
     EXPECTATION_FAILED = 417
-    if sys.version_info >= (3, 9):
-        IM_A_TEAPOT = 418
+    IM_A_TEAPOT = 418
     MISDIRECTED_REQUEST = 421
     if sys.version_info >= (3, 13):
         UNPROCESSABLE_CONTENT = 422
     UNPROCESSABLE_ENTITY = 422
     LOCKED = 423
     FAILED_DEPENDENCY = 424
-    if sys.version_info >= (3, 9):
-        TOO_EARLY = 425
+    TOO_EARLY = 425
     UPGRADE_REQUIRED = 426
     PRECONDITION_REQUIRED = 428
     TOO_MANY_REQUESTS = 429
diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi
index cd2fc4f5a652..5c35dff28d43 100644
--- a/mypy/typeshed/stdlib/http/client.pyi
+++ b/mypy/typeshed/stdlib/http/client.pyi
@@ -5,6 +5,7 @@ import sys
 import types
 from _typeshed import MaybeNone, ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer
 from collections.abc import Callable, Iterable, Iterator, Mapping
+from email._policybase import _MessageT
 from socket import socket
 from typing import BinaryIO, Literal, TypeVar, overload
 from typing_extensions import Self, TypeAlias
@@ -33,7 +34,6 @@ __all__ = [
 
 _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | ReadableBuffer
 _T = TypeVar("_T")
-_MessageT = TypeVar("_MessageT", bound=email.message.Message)
 _HeaderValue: TypeAlias = ReadableBuffer | str | int
 
 HTTP_PORT: int
@@ -44,8 +44,7 @@ HTTPS_PORT: int
 CONTINUE: Literal[100]
 SWITCHING_PROTOCOLS: Literal[101]
 PROCESSING: Literal[102]
-if sys.version_info >= (3, 9):
-    EARLY_HINTS: Literal[103]
+EARLY_HINTS: Literal[103]
 
 OK: Literal[200]
 CREATED: Literal[201]
@@ -91,16 +90,14 @@ if sys.version_info >= (3, 13):
     RANGE_NOT_SATISFIABLE: Literal[416]
 REQUESTED_RANGE_NOT_SATISFIABLE: Literal[416]
 EXPECTATION_FAILED: Literal[417]
-if sys.version_info >= (3, 9):
-    IM_A_TEAPOT: Literal[418]
+IM_A_TEAPOT: Literal[418]
 MISDIRECTED_REQUEST: Literal[421]
 if sys.version_info >= (3, 13):
     UNPROCESSABLE_CONTENT: Literal[422]
 UNPROCESSABLE_ENTITY: Literal[422]
 LOCKED: Literal[423]
 FAILED_DEPENDENCY: Literal[424]
-if sys.version_info >= (3, 9):
-    TOO_EARLY: Literal[425]
+TOO_EARLY: Literal[425]
 UPGRADE_REQUIRED: Literal[426]
 PRECONDITION_REQUIRED: Literal[428]
 TOO_MANY_REQUESTS: Literal[429]
diff --git a/mypy/typeshed/stdlib/http/cookies.pyi b/mypy/typeshed/stdlib/http/cookies.pyi
index c4af5256b5d8..4df12e3125d4 100644
--- a/mypy/typeshed/stdlib/http/cookies.pyi
+++ b/mypy/typeshed/stdlib/http/cookies.pyi
@@ -1,11 +1,8 @@
-import sys
 from collections.abc import Iterable, Mapping
+from types import GenericAlias
 from typing import Any, Generic, TypeVar, overload
 from typing_extensions import TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
 
 _DataType: TypeAlias = str | Mapping[str, str | Morsel[Any]]
@@ -44,8 +41,7 @@ class Morsel(dict[str, Any], Generic[_T]):
     def OutputString(self, attrs: list[str] | None = None) -> str: ...
     def __eq__(self, morsel: object) -> bool: ...
     def __setitem__(self, K: str, V: Any) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class BaseCookie(dict[str, Morsel[_T]], Generic[_T]):
     def __init__(self, input: _DataType | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi
index 1a6fde6000d9..429bb65bb0ef 100644
--- a/mypy/typeshed/stdlib/http/server.pyi
+++ b/mypy/typeshed/stdlib/http/server.pyi
@@ -3,12 +3,25 @@ import email.message
 import io
 import socketserver
 import sys
-from _typeshed import StrPath, SupportsRead, SupportsWrite
-from collections.abc import Mapping, Sequence
-from typing import Any, AnyStr, BinaryIO, ClassVar
-from typing_extensions import deprecated
+from _ssl import _PasswordType
+from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite
+from collections.abc import Callable, Iterable, Mapping, Sequence
+from ssl import Purpose, SSLContext
+from typing import Any, AnyStr, BinaryIO, ClassVar, Protocol, type_check_only
+from typing_extensions import Self, deprecated
 
-__all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"]
+if sys.version_info >= (3, 14):
+    __all__ = [
+        "HTTPServer",
+        "ThreadingHTTPServer",
+        "HTTPSServer",
+        "ThreadingHTTPSServer",
+        "BaseHTTPRequestHandler",
+        "SimpleHTTPRequestHandler",
+        "CGIHTTPRequestHandler",
+    ]
+else:
+    __all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"]
 
 class HTTPServer(socketserver.TCPServer):
     server_name: str
@@ -16,6 +29,39 @@ class HTTPServer(socketserver.TCPServer):
 
 class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): ...
 
+if sys.version_info >= (3, 14):
+    @type_check_only
+    class _SSLModule(Protocol):
+        @staticmethod
+        def create_default_context(
+            purpose: Purpose = ...,
+            *,
+            cafile: StrOrBytesPath | None = None,
+            capath: StrOrBytesPath | None = None,
+            cadata: str | ReadableBuffer | None = None,
+        ) -> SSLContext: ...
+
+    class HTTPSServer(HTTPServer):
+        ssl: _SSLModule
+        certfile: StrOrBytesPath
+        keyfile: StrOrBytesPath | None
+        password: _PasswordType | None
+        alpn_protocols: Iterable[str]
+        def __init__(
+            self,
+            server_address: socketserver._AfInetAddress,
+            RequestHandlerClass: Callable[[Any, _socket._RetAddress, Self], socketserver.BaseRequestHandler],
+            bind_and_activate: bool = True,
+            *,
+            certfile: StrOrBytesPath,
+            keyfile: StrOrBytesPath | None = None,
+            password: _PasswordType | None = None,
+            alpn_protocols: Iterable[str] | None = None,
+        ) -> None: ...
+        def server_activate(self) -> None: ...
+
+    class ThreadingHTTPSServer(socketserver.ThreadingMixIn, HTTPSServer): ...
+
 class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
     client_address: tuple[str, int]
     close_connection: bool
diff --git a/mypy/typeshed/stdlib/imaplib.pyi b/mypy/typeshed/stdlib/imaplib.pyi
index 6a4d8b2e720a..536985a592b7 100644
--- a/mypy/typeshed/stdlib/imaplib.pyi
+++ b/mypy/typeshed/stdlib/imaplib.pyi
@@ -1,16 +1,16 @@
 import subprocess
 import sys
 import time
-from _typeshed import ReadableBuffer, SizedBuffer
+from _typeshed import ReadableBuffer, SizedBuffer, Unused
 from builtins import list as _list  # conflicts with a method named "list"
-from collections.abc import Callable
+from collections.abc import Callable, Generator
 from datetime import datetime
 from re import Pattern
 from socket import socket as _socket
 from ssl import SSLContext, SSLSocket
 from types import TracebackType
 from typing import IO, Any, Literal, SupportsAbs, SupportsInt
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, deprecated
 
 __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple", "Int2AP", "ParseFlags", "Time2Internaldate", "IMAP4_SSL"]
 
@@ -40,18 +40,19 @@ class IMAP4:
     welcome: bytes
     capabilities: tuple[str, ...]
     PROTOCOL_VERSION: str
-    if sys.version_info >= (3, 9):
-        def __init__(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ...
-        def open(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ...
+    def __init__(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ...
+    def open(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        @property
+        @deprecated("IMAP4.file is unsupported, can cause errors, and may be removed.")
+        def file(self) -> IO[str] | IO[bytes]: ...
     else:
-        def __init__(self, host: str = "", port: int = 143) -> None: ...
-        def open(self, host: str = "", port: int = 143) -> None: ...
+        file: IO[str] | IO[bytes]
 
     def __getattr__(self, attr: str) -> Any: ...
     host: str
     port: int
     sock: _socket
-    file: IO[str] | IO[bytes]
     def read(self, size: int) -> bytes: ...
     def readline(self) -> bytes: ...
     def send(self, data: ReadableBuffer) -> None: ...
@@ -77,6 +78,9 @@ class IMAP4:
     def getannotation(self, mailbox: str, entry: str, attribute: str) -> _CommandResults: ...
     def getquota(self, root: str) -> _CommandResults: ...
     def getquotaroot(self, mailbox: str) -> _CommandResults: ...
+    if sys.version_info >= (3, 14):
+        def idle(self, duration: float | None = None) -> Idler: ...
+
     def list(self, directory: str = '""', pattern: str = "*") -> tuple[str, _AnyResponseData]: ...
     def login(self, user: str, password: str) -> tuple[Literal["OK"], _list[bytes]]: ...
     def login_cram_md5(self, user: str, password: str) -> _CommandResults: ...
@@ -101,12 +105,19 @@ class IMAP4:
     def thread(self, threading_algorithm: str, charset: str, *search_criteria: str) -> _CommandResults: ...
     def uid(self, command: str, *args: str) -> _CommandResults: ...
     def unsubscribe(self, mailbox: str) -> _CommandResults: ...
-    if sys.version_info >= (3, 9):
-        def unselect(self) -> _CommandResults: ...
-
+    def unselect(self) -> _CommandResults: ...
     def xatom(self, name: str, *args: str) -> _CommandResults: ...
     def print_log(self) -> None: ...
 
+if sys.version_info >= (3, 14):
+    class Idler:
+        def __init__(self, imap: IMAP4, duration: float | None = None) -> None: ...
+        def __enter__(self) -> Self: ...
+        def __exit__(self, exc_type: object, exc_val: Unused, exc_tb: Unused) -> Literal[False]: ...
+        def __iter__(self) -> Self: ...
+        def __next__(self) -> tuple[str, float | None]: ...
+        def burst(self, interval: float = 0.1) -> Generator[tuple[str, float | None]]: ...
+
 class IMAP4_SSL(IMAP4):
     if sys.version_info < (3, 12):
         keyfile: str
@@ -115,16 +126,6 @@ class IMAP4_SSL(IMAP4):
         def __init__(
             self, host: str = "", port: int = 993, *, ssl_context: SSLContext | None = None, timeout: float | None = None
         ) -> None: ...
-    elif sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            host: str = "",
-            port: int = 993,
-            keyfile: str | None = None,
-            certfile: str | None = None,
-            ssl_context: SSLContext | None = None,
-            timeout: float | None = None,
-        ) -> None: ...
     else:
         def __init__(
             self,
@@ -133,27 +134,32 @@ class IMAP4_SSL(IMAP4):
             keyfile: str | None = None,
             certfile: str | None = None,
             ssl_context: SSLContext | None = None,
+            timeout: float | None = None,
         ) -> None: ...
     sslobj: SSLSocket
-    file: IO[Any]
-    if sys.version_info >= (3, 9):
-        def open(self, host: str = "", port: int | None = 993, timeout: float | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        @property
+        @deprecated("IMAP4_SSL.file is unsupported, can cause errors, and may be removed.")
+        def file(self) -> IO[Any]: ...
     else:
-        def open(self, host: str = "", port: int | None = 993) -> None: ...
+        file: IO[Any]
 
+    def open(self, host: str = "", port: int | None = 993, timeout: float | None = None) -> None: ...
     def ssl(self) -> SSLSocket: ...
 
 class IMAP4_stream(IMAP4):
     command: str
     def __init__(self, command: str) -> None: ...
-    file: IO[Any]
+    if sys.version_info >= (3, 14):
+        @property
+        @deprecated("IMAP4_stream.file is unsupported, can cause errors, and may be removed.")
+        def file(self) -> IO[Any]: ...
+    else:
+        file: IO[Any]
     process: subprocess.Popen[bytes]
     writefile: IO[Any]
     readfile: IO[Any]
-    if sys.version_info >= (3, 9):
-        def open(self, host: str | None = None, port: int | None = None, timeout: float | None = None) -> None: ...
-    else:
-        def open(self, host: str | None = None, port: int | None = None) -> None: ...
+    def open(self, host: str | None = None, port: int | None = None, timeout: float | None = None) -> None: ...
 
 class _Authenticator:
     mech: Callable[[bytes], bytes | bytearray | memoryview | str | None]
diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi
index 588377d7d871..3016a3a43b36 100644
--- a/mypy/typeshed/stdlib/importlib/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/abc.pyi
@@ -8,6 +8,7 @@ from importlib import _bootstrap_external
 from importlib.machinery import ModuleSpec
 from io import BufferedReader
 from typing import IO, Any, Literal, Protocol, overload, runtime_checkable
+from typing_extensions import deprecated
 
 if sys.version_info >= (3, 11):
     __all__ = [
@@ -38,6 +39,7 @@ else:
 if sys.version_info < (3, 12):
     class Finder(metaclass=ABCMeta): ...
 
+@deprecated("Deprecated as of Python 3.7: Use importlib.resources.abc.TraversableResources instead.")
 class ResourceLoader(Loader):
     @abstractmethod
     def get_data(self, path: str) -> bytes: ...
@@ -58,6 +60,7 @@ class ExecutionLoader(InspectLoader):
     def get_filename(self, fullname: str) -> str: ...
 
 class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta):  # type: ignore[misc]  # incompatible definitions of source_to_code in the base classes
+    @deprecated("Deprecated as of Python 3.3: Use importlib.resources.abc.SourceLoader.path_stats instead.")
     def path_mtime(self, path: str) -> float: ...
     def set_data(self, path: str, data: bytes) -> None: ...
     def get_source(self, fullname: str) -> str | None: ...
@@ -125,49 +128,48 @@ class ResourceReader(metaclass=ABCMeta):
     @abstractmethod
     def contents(self) -> Iterator[str]: ...
 
-if sys.version_info >= (3, 9):
-    @runtime_checkable
-    class Traversable(Protocol):
-        @abstractmethod
-        def is_dir(self) -> bool: ...
-        @abstractmethod
-        def is_file(self) -> bool: ...
-        @abstractmethod
-        def iterdir(self) -> Iterator[Traversable]: ...
-        if sys.version_info >= (3, 11):
-            @abstractmethod
-            def joinpath(self, *descendants: str) -> Traversable: ...
-        else:
-            @abstractmethod
-            def joinpath(self, child: str, /) -> Traversable: ...
-
-        # The documentation and runtime protocol allows *args, **kwargs arguments,
-        # but this would mean that all implementers would have to support them,
-        # which is not the case.
-        @overload
-        @abstractmethod
-        def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
-        @overload
+@runtime_checkable
+class Traversable(Protocol):
+    @abstractmethod
+    def is_dir(self) -> bool: ...
+    @abstractmethod
+    def is_file(self) -> bool: ...
+    @abstractmethod
+    def iterdir(self) -> Iterator[Traversable]: ...
+    if sys.version_info >= (3, 11):
         @abstractmethod
-        def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
-        @property
+        def joinpath(self, *descendants: str) -> Traversable: ...
+    else:
         @abstractmethod
-        def name(self) -> str: ...
-        if sys.version_info >= (3, 10):
-            def __truediv__(self, child: str, /) -> Traversable: ...
-        else:
-            @abstractmethod
-            def __truediv__(self, child: str, /) -> Traversable: ...
+        def joinpath(self, child: str, /) -> Traversable: ...
 
+    # The documentation and runtime protocol allows *args, **kwargs arguments,
+    # but this would mean that all implementers would have to support them,
+    # which is not the case.
+    @overload
+    @abstractmethod
+    def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
+    @overload
+    @abstractmethod
+    def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
+    @property
+    @abstractmethod
+    def name(self) -> str: ...
+    if sys.version_info >= (3, 10):
+        def __truediv__(self, child: str, /) -> Traversable: ...
+    else:
         @abstractmethod
-        def read_bytes(self) -> bytes: ...
-        @abstractmethod
-        def read_text(self, encoding: str | None = None) -> str: ...
+        def __truediv__(self, child: str, /) -> Traversable: ...
 
-    class TraversableResources(ResourceReader):
-        @abstractmethod
-        def files(self) -> Traversable: ...
-        def open_resource(self, resource: str) -> BufferedReader: ...
-        def resource_path(self, resource: Any) -> str: ...
-        def is_resource(self, path: str) -> bool: ...
-        def contents(self) -> Iterator[str]: ...
+    @abstractmethod
+    def read_bytes(self) -> bytes: ...
+    @abstractmethod
+    def read_text(self, encoding: str | None = None) -> str: ...
+
+class TraversableResources(ResourceReader):
+    @abstractmethod
+    def files(self) -> Traversable: ...
+    def open_resource(self, resource: str) -> BufferedReader: ...
+    def resource_path(self, resource: Any) -> str: ...
+    def is_resource(self, path: str) -> bool: ...
+    def contents(self) -> Iterator[str]: ...
diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
index 8ab7a0c4a9e8..15d8b50b09d2 100644
--- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
@@ -71,11 +71,10 @@ class EntryPoint(_EntryPointBase):
     def load(self) -> Any: ...  # Callable[[], Any] or an importable module
     @property
     def extras(self) -> list[str]: ...
-    if sys.version_info >= (3, 9):
-        @property
-        def module(self) -> str: ...
-        @property
-        def attr(self) -> str: ...
+    @property
+    def module(self) -> str: ...
+    @property
+    def attr(self) -> str: ...
     if sys.version_info >= (3, 10):
         dist: ClassVar[Distribution | None]
         def matches(
diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
index a30e6cdce5c6..2cf6366b6cb3 100644
--- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
@@ -2,6 +2,7 @@ import os
 import sys
 from collections.abc import Iterator
 from contextlib import AbstractContextManager
+from importlib.abc import Traversable
 from pathlib import Path
 from types import ModuleType
 from typing import Any, BinaryIO, Literal, TextIO
@@ -12,13 +13,18 @@ if sys.version_info >= (3, 11):
 else:
     Package: TypeAlias = str | ModuleType
 
-if sys.version_info >= (3, 9):
-    from importlib.abc import Traversable
-
-__all__ = ["Package", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"]
-
-if sys.version_info >= (3, 9):
-    __all__ += ["as_file", "files"]
+__all__ = [
+    "Package",
+    "as_file",
+    "contents",
+    "files",
+    "is_resource",
+    "open_binary",
+    "open_text",
+    "path",
+    "read_binary",
+    "read_text",
+]
 
 if sys.version_info >= (3, 10):
     __all__ += ["ResourceReader"]
@@ -31,11 +37,12 @@ if sys.version_info < (3, 11):
 elif sys.version_info < (3, 13):
     Resource: TypeAlias = str
 
-if sys.version_info >= (3, 13):
+if sys.version_info >= (3, 12):
     from importlib.resources._common import Anchor as Anchor
 
     __all__ += ["Anchor"]
 
+if sys.version_info >= (3, 13):
     from importlib.resources._functional import (
         contents as contents,
         is_resource as is_resource,
@@ -57,13 +64,12 @@ else:
 
 if sys.version_info >= (3, 11):
     from importlib.resources._common import as_file as as_file
-elif sys.version_info >= (3, 9):
+else:
     def as_file(path: Traversable) -> AbstractContextManager[Path, Literal[False]]: ...
 
 if sys.version_info >= (3, 11):
     from importlib.resources._common import files as files
-
-elif sys.version_info >= (3, 9):
+else:
     def files(package: Package) -> Traversable: ...
 
 if sys.version_info >= (3, 10):
diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi
index 5bebe9bf4482..e19c2a634aa0 100644
--- a/mypy/typeshed/stdlib/inspect.pyi
+++ b/mypy/typeshed/stdlib/inspect.pyi
@@ -2,7 +2,7 @@ import dis
 import enum
 import sys
 import types
-from _typeshed import StrPath
+from _typeshed import AnnotationForm, StrPath
 from collections import OrderedDict
 from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet
 from types import (
@@ -28,6 +28,9 @@ from types import (
 from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload
 from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs
 
+if sys.version_info >= (3, 14):
+    from annotationlib import Format
+
 if sys.version_info >= (3, 11):
     __all__ = [
         "ArgInfo",
@@ -139,6 +142,8 @@ if sys.version_info >= (3, 11):
             "getasyncgenstate",
             "BufferFlags",
         ]
+    if sys.version_info >= (3, 14):
+        __all__ += ["CO_HAS_DOCSTRING", "CO_METHOD", "ispackage"]
 
 _P = ParamSpec("_P")
 _T = TypeVar("_T")
@@ -172,6 +177,9 @@ CO_COROUTINE: Final = 128
 CO_ITERABLE_COROUTINE: Final = 256
 CO_ASYNC_GENERATOR: Final = 512
 TPFLAGS_IS_ABSTRACT: Final = 1048576
+if sys.version_info >= (3, 14):
+    CO_HAS_DOCSTRING: Final = 67108864
+    CO_METHOD: Final = 134217728
 
 modulesbyfile: dict[str, Any]
 
@@ -199,6 +207,11 @@ def getmodulename(path: StrPath) -> str | None: ...
 def ismodule(object: object) -> TypeIs[ModuleType]: ...
 def isclass(object: object) -> TypeIs[type[Any]]: ...
 def ismethod(object: object) -> TypeIs[MethodType]: ...
+
+if sys.version_info >= (3, 14):
+    # Not TypeIs because it does not return True for all modules
+    def ispackage(object: object) -> TypeGuard[ModuleType]: ...
+
 def isfunction(object: object) -> TypeIs[FunctionType]: ...
 
 if sys.version_info >= (3, 12):
@@ -294,7 +307,18 @@ _IntrospectableCallable: TypeAlias = Callable[..., Any]
 #
 # Introspecting callables with the Signature object
 #
-if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 14):
+    def signature(
+        obj: _IntrospectableCallable,
+        *,
+        follow_wrapped: bool = True,
+        globals: Mapping[str, Any] | None = None,
+        locals: Mapping[str, Any] | None = None,
+        eval_str: bool = False,
+        annotation_format: Format = Format.VALUE,  # noqa: Y011
+    ) -> Signature: ...
+
+elif sys.version_info >= (3, 10):
     def signature(
         obj: _IntrospectableCallable,
         *,
@@ -323,7 +347,19 @@ class Signature:
     def bind_partial(self, *args: Any, **kwargs: Any) -> BoundArguments: ...
     def replace(self, *, parameters: Sequence[Parameter] | type[_void] | None = ..., return_annotation: Any = ...) -> Self: ...
     __replace__ = replace
-    if sys.version_info >= (3, 10):
+    if sys.version_info >= (3, 14):
+        @classmethod
+        def from_callable(
+            cls,
+            obj: _IntrospectableCallable,
+            *,
+            follow_wrapped: bool = True,
+            globals: Mapping[str, Any] | None = None,
+            locals: Mapping[str, Any] | None = None,
+            eval_str: bool = False,
+            annotation_format: Format = Format.VALUE,  # noqa: Y011
+        ) -> Self: ...
+    elif sys.version_info >= (3, 10):
         @classmethod
         def from_callable(
             cls,
@@ -337,20 +373,24 @@ class Signature:
     else:
         @classmethod
         def from_callable(cls, obj: _IntrospectableCallable, *, follow_wrapped: bool = True) -> Self: ...
-    if sys.version_info >= (3, 13):
+    if sys.version_info >= (3, 14):
+        def format(self, *, max_width: int | None = None, quote_annotation_strings: bool = True) -> str: ...
+    elif sys.version_info >= (3, 13):
         def format(self, *, max_width: int | None = None) -> str: ...
 
     def __eq__(self, other: object) -> bool: ...
     def __hash__(self) -> int: ...
 
-if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 14):
+    from annotationlib import get_annotations as get_annotations
+elif sys.version_info >= (3, 10):
     def get_annotations(
         obj: Callable[..., object] | type[object] | ModuleType,  # any callable, class, or module
         *,
         globals: Mapping[str, Any] | None = None,  # value types depend on the key
         locals: Mapping[str, Any] | None = None,  # value types depend on the key
         eval_str: bool = False,
-    ) -> dict[str, Any]: ...  # values are type expressions
+    ) -> dict[str, AnnotationForm]: ...  # values are type expressions
 
 # The name is the same as the enum's name in CPython
 class _ParameterKind(enum.IntEnum):
@@ -461,7 +501,13 @@ class ArgInfo(NamedTuple):
     locals: dict[str, Any]
 
 def getargvalues(frame: FrameType) -> ArgInfo: ...
-def formatannotation(annotation: object, base_module: str | None = None) -> str: ...
+
+if sys.version_info >= (3, 14):
+    def formatannotation(annotation: object, base_module: str | None = None, *, quote_annotation_strings: bool = True) -> str: ...
+
+else:
+    def formatannotation(annotation: object, base_module: str | None = None) -> str: ...
+
 def formatannotationrelativeto(object: object) -> Callable[[object], str]: ...
 
 if sys.version_info < (3, 11):
@@ -616,8 +662,7 @@ class Attribute(NamedTuple):
 
 def classify_class_attrs(cls: type) -> list[Attribute]: ...
 
-if sys.version_info >= (3, 9):
-    class ClassFoundException(Exception): ...
+class ClassFoundException(Exception): ...
 
 if sys.version_info >= (3, 12):
     class BufferFlags(enum.IntFlag):
diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi
index 5c26cb245a2f..1313df183d36 100644
--- a/mypy/typeshed/stdlib/io.pyi
+++ b/mypy/typeshed/stdlib/io.pyi
@@ -20,7 +20,7 @@ from _io import (
     open as open,
     open_code as open_code,
 )
-from typing import Final
+from typing import Final, Protocol, TypeVar
 
 __all__ = [
     "BlockingIOError",
@@ -44,11 +44,17 @@ __all__ = [
     "SEEK_END",
 ]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["Reader", "Writer"]
+
 if sys.version_info >= (3, 11):
     from _io import text_encoding as text_encoding
 
     __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"]
 
+_T_co = TypeVar("_T_co", covariant=True)
+_T_contra = TypeVar("_T_contra", contravariant=True)
+
 SEEK_SET: Final = 0
 SEEK_CUR: Final = 1
 SEEK_END: Final = 2
@@ -58,3 +64,10 @@ class IOBase(_IOBase, metaclass=abc.ABCMeta): ...
 class RawIOBase(_RawIOBase, IOBase): ...
 class BufferedIOBase(_BufferedIOBase, IOBase): ...
 class TextIOBase(_TextIOBase, IOBase): ...
+
+if sys.version_info >= (3, 14):
+    class Reader(Protocol[_T_co]):
+        def read(self, size: int = ..., /) -> _T_co: ...
+
+    class Writer(Protocol[_T_contra]):
+        def write(self, data: _T_contra, /) -> int: ...
diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi
index e8e81abc6f79..9df6bab7c167 100644
--- a/mypy/typeshed/stdlib/ipaddress.pyi
+++ b/mypy/typeshed/stdlib/ipaddress.pyi
@@ -28,17 +28,16 @@ class _IPAddressBase:
     def exploded(self) -> str: ...
     @property
     def reverse_pointer(self) -> str: ...
-    @property
-    def version(self) -> int: ...
+    if sys.version_info < (3, 14):
+        @property
+        def version(self) -> int: ...
 
 class _BaseAddress(_IPAddressBase):
     def __add__(self, other: int) -> Self: ...
     def __hash__(self) -> int: ...
     def __int__(self) -> int: ...
     def __sub__(self, other: int) -> Self: ...
-    if sys.version_info >= (3, 9):
-        def __format__(self, fmt: str) -> str: ...
-
+    def __format__(self, fmt: str) -> str: ...
     def __eq__(self, other: object) -> bool: ...
     def __lt__(self, other: Self) -> bool: ...
     if sys.version_info >= (3, 11):
@@ -106,10 +105,14 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]):
     def hostmask(self) -> _A: ...
 
 class _BaseV4:
-    @property
-    def version(self) -> Literal[4]: ...
-    @property
-    def max_prefixlen(self) -> Literal[32]: ...
+    if sys.version_info >= (3, 14):
+        version: Final = 4
+        max_prefixlen: Final = 32
+    else:
+        @property
+        def version(self) -> Literal[4]: ...
+        @property
+        def max_prefixlen(self) -> Literal[32]: ...
 
 class IPv4Address(_BaseV4, _BaseAddress):
     def __init__(self, address: object) -> None: ...
@@ -153,10 +156,14 @@ class IPv4Interface(IPv4Address):
     def with_prefixlen(self) -> str: ...
 
 class _BaseV6:
-    @property
-    def version(self) -> Literal[6]: ...
-    @property
-    def max_prefixlen(self) -> Literal[128]: ...
+    if sys.version_info >= (3, 14):
+        version: Final = 6
+        max_prefixlen: Final = 128
+    else:
+        @property
+        def version(self) -> Literal[6]: ...
+        @property
+        def max_prefixlen(self) -> Literal[128]: ...
 
 class IPv6Address(_BaseV6, _BaseAddress):
     def __init__(self, address: object) -> None: ...
@@ -184,10 +191,8 @@ class IPv6Address(_BaseV6, _BaseAddress):
     def sixtofour(self) -> IPv4Address | None: ...
     @property
     def teredo(self) -> tuple[IPv4Address, IPv4Address] | None: ...
-    if sys.version_info >= (3, 9):
-        @property
-        def scope_id(self) -> str | None: ...
-
+    @property
+    def scope_id(self) -> str | None: ...
     def __hash__(self) -> int: ...
     def __eq__(self, other: object) -> bool: ...
 
diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi
index 675533d44a68..7d05b1318680 100644
--- a/mypy/typeshed/stdlib/itertools.pyi
+++ b/mypy/typeshed/stdlib/itertools.pyi
@@ -1,12 +1,10 @@
 import sys
 from _typeshed import MaybeNone
 from collections.abc import Callable, Iterable, Iterator
+from types import GenericAlias
 from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 _S = TypeVar("_S")
 _N = TypeVar("_N", int, float, SupportsFloat, SupportsInt, SupportsIndex, SupportsComplex)
@@ -68,8 +66,7 @@ class chain(Iterator[_T]):
     @classmethod
     # We use type[Any] and not type[_S] to not lose the type inference from __iterable
     def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class compress(Iterator[_T]):
     def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ...
diff --git a/mypy/typeshed/stdlib/keyword.pyi b/mypy/typeshed/stdlib/keyword.pyi
index 960dfd2fa155..6b8bdad6beb6 100644
--- a/mypy/typeshed/stdlib/keyword.pyi
+++ b/mypy/typeshed/stdlib/keyword.pyi
@@ -1,11 +1,7 @@
-import sys
 from collections.abc import Sequence
 from typing import Final
 
-if sys.version_info >= (3, 9):
-    __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"]
-else:
-    __all__ = ["iskeyword", "kwlist"]
+__all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"]
 
 def iskeyword(s: str, /) -> bool: ...
 
@@ -13,9 +9,8 @@ def iskeyword(s: str, /) -> bool: ...
 # type it as a sequence
 kwlist: Final[Sequence[str]]
 
-if sys.version_info >= (3, 9):
-    def issoftkeyword(s: str, /) -> bool: ...
+def issoftkeyword(s: str, /) -> bool: ...
 
-    # a list at runtime, but you're not meant to mutate it;
-    # type it as a sequence
-    softkwlist: Final[Sequence[str]]
+# a list at runtime, but you're not meant to mutate it;
+# type it as a sequence
+softkwlist: Final[Sequence[str]]
diff --git a/mypy/typeshed/stdlib/linecache.pyi b/mypy/typeshed/stdlib/linecache.pyi
index 2e050e13b621..5379a21e7d12 100644
--- a/mypy/typeshed/stdlib/linecache.pyi
+++ b/mypy/typeshed/stdlib/linecache.pyi
@@ -1,12 +1,8 @@
-import sys
 from collections.abc import Callable
 from typing import Any
 from typing_extensions import TypeAlias
 
-if sys.version_info >= (3, 9):
-    __all__ = ["getline", "clearcache", "checkcache", "lazycache"]
-else:
-    __all__ = ["getline", "clearcache", "checkcache"]
+__all__ = ["getline", "clearcache", "checkcache", "lazycache"]
 
 _ModuleGlobals: TypeAlias = dict[str, Any]
 _ModuleMetadata: TypeAlias = tuple[int, float | None, list[str], str]
diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi
index 9a4827a8f626..e555f74a81af 100644
--- a/mypy/typeshed/stdlib/logging/__init__.pyi
+++ b/mypy/typeshed/stdlib/logging/__init__.pyi
@@ -6,13 +6,10 @@ from io import TextIOWrapper
 from re import Pattern
 from string import Template
 from time import struct_time
-from types import FrameType, TracebackType
+from types import FrameType, GenericAlias, TracebackType
 from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload
 from typing_extensions import Self, TypeAlias, deprecated
 
-if sys.version_info >= (3, 11):
-    from types import GenericAlias
-
 __all__ = [
     "BASIC_FORMAT",
     "BufferingFormatter",
@@ -273,10 +270,7 @@ class Formatter:
     datefmt: str | None  # undocumented
     _style: PercentStyle  # undocumented
     default_time_format: str
-    if sys.version_info >= (3, 9):
-        default_msec_format: str | None
-    else:
-        default_msec_format: str
+    default_msec_format: str | None
 
     if sys.version_info >= (3, 10):
         def __init__(
@@ -577,37 +571,20 @@ if sys.version_info >= (3, 11):
     def getLevelNamesMapping() -> dict[str, int]: ...
 
 def makeLogRecord(dict: Mapping[str, object]) -> LogRecord: ...
-
-if sys.version_info >= (3, 9):
-    def basicConfig(
-        *,
-        filename: StrPath | None = ...,
-        filemode: str = ...,
-        format: str = ...,
-        datefmt: str | None = ...,
-        style: _FormatStyle = ...,
-        level: _Level | None = ...,
-        stream: SupportsWrite[str] | None = ...,
-        handlers: Iterable[Handler] | None = ...,
-        force: bool | None = ...,
-        encoding: str | None = ...,
-        errors: str | None = ...,
-    ) -> None: ...
-
-else:
-    def basicConfig(
-        *,
-        filename: StrPath | None = ...,
-        filemode: str = ...,
-        format: str = ...,
-        datefmt: str | None = ...,
-        style: _FormatStyle = ...,
-        level: _Level | None = ...,
-        stream: SupportsWrite[str] | None = ...,
-        handlers: Iterable[Handler] | None = ...,
-        force: bool = ...,
-    ) -> None: ...
-
+def basicConfig(
+    *,
+    filename: StrPath | None = ...,
+    filemode: str = ...,
+    format: str = ...,
+    datefmt: str | None = ...,
+    style: _FormatStyle = ...,
+    level: _Level | None = ...,
+    stream: SupportsWrite[str] | None = ...,
+    handlers: Iterable[Handler] | None = ...,
+    force: bool | None = ...,
+    encoding: str | None = ...,
+    errors: str | None = ...,
+) -> None: ...
 def shutdown(handlerList: Sequence[Any] = ...) -> None: ...  # handlerList is undocumented
 def setLoggerClass(klass: type[Logger]) -> None: ...
 def captureWarnings(capture: bool) -> None: ...
@@ -633,14 +610,10 @@ class FileHandler(StreamHandler[TextIOWrapper]):
     mode: str  # undocumented
     encoding: str | None  # undocumented
     delay: bool  # undocumented
-    if sys.version_info >= (3, 9):
-        errors: str | None  # undocumented
-        def __init__(
-            self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None
-        ) -> None: ...
-    else:
-        def __init__(self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False) -> None: ...
-
+    errors: str | None  # undocumented
+    def __init__(
+        self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None
+    ) -> None: ...
     def _open(self) -> TextIOWrapper: ...  # undocumented
 
 class NullHandler(Handler): ...
diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi
index d594d6569a7e..9636b81dc4f3 100644
--- a/mypy/typeshed/stdlib/logging/handlers.pyi
+++ b/mypy/typeshed/stdlib/logging/handlers.pyi
@@ -8,7 +8,9 @@ from logging import FileHandler, Handler, LogRecord
 from re import Pattern
 from socket import SocketKind, socket
 from threading import Thread
+from types import TracebackType
 from typing import Any, ClassVar, Final, Protocol, TypeVar
+from typing_extensions import Self
 
 _T = TypeVar("_T")
 
@@ -22,54 +24,34 @@ SYSLOG_TCP_PORT: Final[int]
 class WatchedFileHandler(FileHandler):
     dev: int  # undocumented
     ino: int  # undocumented
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None
-        ) -> None: ...
-    else:
-        def __init__(self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False) -> None: ...
-
+    def __init__(
+        self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None
+    ) -> None: ...
     def _statstream(self) -> None: ...  # undocumented
     def reopenIfNeeded(self) -> None: ...
 
 class BaseRotatingHandler(FileHandler):
     namer: Callable[[str], str] | None
     rotator: Callable[[str, str], None] | None
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False, errors: str | None = None
-        ) -> None: ...
-    else:
-        def __init__(self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False) -> None: ...
-
+    def __init__(
+        self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False, errors: str | None = None
+    ) -> None: ...
     def rotation_filename(self, default_name: str) -> str: ...
     def rotate(self, source: str, dest: str) -> None: ...
 
 class RotatingFileHandler(BaseRotatingHandler):
     maxBytes: int  # undocumented
     backupCount: int  # undocumented
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            filename: StrPath,
-            mode: str = "a",
-            maxBytes: int = 0,
-            backupCount: int = 0,
-            encoding: str | None = None,
-            delay: bool = False,
-            errors: str | None = None,
-        ) -> None: ...
-    else:
-        def __init__(
-            self,
-            filename: StrPath,
-            mode: str = "a",
-            maxBytes: int = 0,
-            backupCount: int = 0,
-            encoding: str | None = None,
-            delay: bool = False,
-        ) -> None: ...
-
+    def __init__(
+        self,
+        filename: StrPath,
+        mode: str = "a",
+        maxBytes: int = 0,
+        backupCount: int = 0,
+        encoding: str | None = None,
+        delay: bool = False,
+        errors: str | None = None,
+    ) -> None: ...
     def doRollover(self) -> None: ...
     def shouldRollover(self, record: LogRecord) -> int: ...  # undocumented
 
@@ -83,32 +65,18 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
     dayOfWeek: int  # undocumented
     rolloverAt: int  # undocumented
     extMatch: Pattern[str]  # undocumented
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            filename: StrPath,
-            when: str = "h",
-            interval: int = 1,
-            backupCount: int = 0,
-            encoding: str | None = None,
-            delay: bool = False,
-            utc: bool = False,
-            atTime: datetime.time | None = None,
-            errors: str | None = None,
-        ) -> None: ...
-    else:
-        def __init__(
-            self,
-            filename: StrPath,
-            when: str = "h",
-            interval: int = 1,
-            backupCount: int = 0,
-            encoding: str | None = None,
-            delay: bool = False,
-            utc: bool = False,
-            atTime: datetime.time | None = None,
-        ) -> None: ...
-
+    def __init__(
+        self,
+        filename: StrPath,
+        when: str = "h",
+        interval: int = 1,
+        backupCount: int = 0,
+        encoding: str | None = None,
+        delay: bool = False,
+        utc: bool = False,
+        atTime: datetime.time | None = None,
+        errors: str | None = None,
+    ) -> None: ...
     def doRollover(self) -> None: ...
     def shouldRollover(self, record: LogRecord) -> int: ...  # undocumented
     def computeRollover(self, currentTime: int) -> int: ...  # undocumented
@@ -155,13 +123,10 @@ class SysLogHandler(Handler):
     LOG_CRON: int
     LOG_AUTHPRIV: int
     LOG_FTP: int
-
-    if sys.version_info >= (3, 9):
-        LOG_NTP: int
-        LOG_SECURITY: int
-        LOG_CONSOLE: int
-        LOG_SOLCRON: int
-
+    LOG_NTP: int
+    LOG_SECURITY: int
+    LOG_CONSOLE: int
+    LOG_SOLCRON: int
     LOG_LOCAL0: int
     LOG_LOCAL1: int
     LOG_LOCAL2: int
@@ -179,9 +144,19 @@ class SysLogHandler(Handler):
     priority_names: ClassVar[dict[str, int]]  # undocumented
     facility_names: ClassVar[dict[str, int]]  # undocumented
     priority_map: ClassVar[dict[str, str]]  # undocumented
-    def __init__(
-        self, address: tuple[str, int] | str = ("localhost", 514), facility: str | int = 1, socktype: SocketKind | None = None
-    ) -> None: ...
+    if sys.version_info >= (3, 14):
+        timeout: float | None
+        def __init__(
+            self,
+            address: tuple[str, int] | str = ("localhost", 514),
+            facility: str | int = 1,
+            socktype: SocketKind | None = None,
+            timeout: float | None = None,
+        ) -> None: ...
+    else:
+        def __init__(
+            self, address: tuple[str, int] | str = ("localhost", 514), facility: str | int = 1, socktype: SocketKind | None = None
+        ) -> None: ...
     if sys.version_info >= (3, 11):
         def createSocket(self) -> None: ...
 
@@ -191,7 +166,7 @@ class SysLogHandler(Handler):
 class NTEventLogHandler(Handler):
     def __init__(self, appname: str, dllname: str | None = None, logtype: str = "Application") -> None: ...
     def getEventCategory(self, record: LogRecord) -> int: ...
-    # TODO correct return value?
+    # TODO: correct return value?
     def getEventType(self, record: LogRecord) -> int: ...
     def getMessageID(self, record: LogRecord) -> int: ...
 
@@ -248,8 +223,7 @@ class HTTPHandler(Handler):
         context: ssl.SSLContext | None = None,
     ) -> None: ...
     def mapLogRecord(self, record: LogRecord) -> dict[str, Any]: ...
-    if sys.version_info >= (3, 9):
-        def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ...  # undocumented
+    def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ...  # undocumented
 
 class _QueueLike(Protocol[_T]):
     def get(self) -> _T: ...
@@ -275,3 +249,9 @@ class QueueListener:
     def stop(self) -> None: ...
     def enqueue_sentinel(self) -> None: ...
     def handle(self, record: LogRecord) -> None: ...
+
+    if sys.version_info >= (3, 14):
+        def __enter__(self) -> Self: ...
+        def __exit__(
+            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
+        ) -> None: ...
diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi
index 2f0279f5986b..b066d222466b 100644
--- a/mypy/typeshed/stdlib/lzma.pyi
+++ b/mypy/typeshed/stdlib/lzma.pyi
@@ -1,4 +1,4 @@
-from _compression import BaseStream
+import sys
 from _lzma import (
     CHECK_CRC32 as CHECK_CRC32,
     CHECK_CRC64 as CHECK_CRC64,
@@ -38,6 +38,11 @@ from _typeshed import ReadableBuffer, StrOrBytesPath
 from typing import IO, Literal, TextIO, overload
 from typing_extensions import Self, TypeAlias
 
+if sys.version_info >= (3, 14):
+    from compression._common._streams import BaseStream
+else:
+    from _compression import BaseStream
+
 __all__ = [
     "CHECK_NONE",
     "CHECK_CRC32",
diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi
index a98a00a42853..ff605c0661fb 100644
--- a/mypy/typeshed/stdlib/mailbox.pyi
+++ b/mypy/typeshed/stdlib/mailbox.pyi
@@ -4,13 +4,11 @@ import sys
 from _typeshed import StrPath, SupportsNoArgReadline, SupportsRead
 from abc import ABCMeta, abstractmethod
 from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
-from types import TracebackType
+from email._policybase import _MessageT
+from types import GenericAlias, TracebackType
 from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "Mailbox",
     "Maildir",
@@ -32,7 +30,6 @@ __all__ = [
 ]
 
 _T = TypeVar("_T")
-_MessageT = TypeVar("_MessageT", bound=Message)
 
 class _SupportsReadAndReadline(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ...
 
@@ -101,8 +98,7 @@ class Mailbox(Generic[_MessageT]):
     def unlock(self) -> None: ...
     @abstractmethod
     def close(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class Maildir(Mailbox[MaildirMessage]):
     colon: str
@@ -251,8 +247,7 @@ class _ProxyFile(Generic[AnyStr]):
     def flush(self) -> None: ...
     @property
     def closed(self) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class _PartialFile(_ProxyFile[AnyStr]):
     def __init__(self, f: IO[AnyStr], start: int | None = None, stop: int | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/marshal.pyi b/mypy/typeshed/stdlib/marshal.pyi
index 6ab202637dda..46c421e4ce30 100644
--- a/mypy/typeshed/stdlib/marshal.pyi
+++ b/mypy/typeshed/stdlib/marshal.pyi
@@ -2,10 +2,10 @@ import builtins
 import sys
 import types
 from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite
-from typing import Any
+from typing import Any, Final
 from typing_extensions import TypeAlias
 
-version: int
+version: Final[int]
 
 _Marshallable: TypeAlias = (
     # handled in w_object() in marshal.c
@@ -28,14 +28,22 @@ _Marshallable: TypeAlias = (
     | ReadableBuffer
 )
 
-if sys.version_info >= (3, 13):
+if sys.version_info >= (3, 14):
+    def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 5, /, *, allow_code: bool = True) -> None: ...
+    def dumps(value: _Marshallable, version: int = 5, /, *, allow_code: bool = True) -> bytes: ...
+
+elif sys.version_info >= (3, 13):
     def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /, *, allow_code: bool = True) -> None: ...
-    def load(file: SupportsRead[bytes], /, *, allow_code: bool = True) -> Any: ...
     def dumps(value: _Marshallable, version: int = 4, /, *, allow_code: bool = True) -> bytes: ...
-    def loads(bytes: ReadableBuffer, /, *, allow_code: bool = True) -> Any: ...
 
 else:
     def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /) -> None: ...
-    def load(file: SupportsRead[bytes], /) -> Any: ...
     def dumps(value: _Marshallable, version: int = 4, /) -> bytes: ...
+
+if sys.version_info >= (3, 13):
+    def load(file: SupportsRead[bytes], /, *, allow_code: bool = True) -> Any: ...
+    def loads(bytes: ReadableBuffer, /, *, allow_code: bool = True) -> Any: ...
+
+else:
+    def load(file: SupportsRead[bytes], /) -> Any: ...
     def loads(bytes: ReadableBuffer, /) -> Any: ...
diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi
index f73429cf6940..9e77f0cd7e06 100644
--- a/mypy/typeshed/stdlib/math.pyi
+++ b/mypy/typeshed/stdlib/math.pyi
@@ -61,13 +61,7 @@ def fmod(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ...
 def frexp(x: _SupportsFloatOrIndex, /) -> tuple[float, int]: ...
 def fsum(seq: Iterable[_SupportsFloatOrIndex], /) -> float: ...
 def gamma(x: _SupportsFloatOrIndex, /) -> float: ...
-
-if sys.version_info >= (3, 9):
-    def gcd(*integers: SupportsIndex) -> int: ...
-
-else:
-    def gcd(x: SupportsIndex, y: SupportsIndex, /) -> int: ...
-
+def gcd(*integers: SupportsIndex) -> int: ...
 def hypot(*coordinates: _SupportsFloatOrIndex) -> float: ...
 def isclose(
     a: _SupportsFloatOrIndex,
@@ -80,10 +74,7 @@ def isinf(x: _SupportsFloatOrIndex, /) -> bool: ...
 def isfinite(x: _SupportsFloatOrIndex, /) -> bool: ...
 def isnan(x: _SupportsFloatOrIndex, /) -> bool: ...
 def isqrt(n: SupportsIndex, /) -> int: ...
-
-if sys.version_info >= (3, 9):
-    def lcm(*integers: SupportsIndex) -> int: ...
-
+def lcm(*integers: SupportsIndex) -> int: ...
 def ldexp(x: _SupportsFloatOrIndex, i: int, /) -> float: ...
 def lgamma(x: _SupportsFloatOrIndex, /) -> float: ...
 def log(x: _SupportsFloatOrIndex, base: _SupportsFloatOrIndex = ...) -> float: ...
@@ -95,7 +86,7 @@ def modf(x: _SupportsFloatOrIndex, /) -> tuple[float, float]: ...
 if sys.version_info >= (3, 12):
     def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /, *, steps: SupportsIndex | None = None) -> float: ...
 
-elif sys.version_info >= (3, 9):
+else:
     def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ...
 
 def perm(n: SupportsIndex, k: SupportsIndex | None = None, /) -> int: ...
@@ -140,9 +131,7 @@ class _SupportsTrunc(Protocol[_T_co]):
     def __trunc__(self) -> _T_co: ...
 
 def trunc(x: _SupportsTrunc[_T], /) -> _T: ...
-
-if sys.version_info >= (3, 9):
-    def ulp(x: _SupportsFloatOrIndex, /) -> float: ...
+def ulp(x: _SupportsFloatOrIndex, /) -> float: ...
 
 if sys.version_info >= (3, 13):
     def fma(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, z: _SupportsFloatOrIndex, /) -> float: ...
diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
index ad5697e0ab1c..50e4f1c1fe66 100644
--- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
@@ -3,7 +3,7 @@ import sys
 import threading
 from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT
 from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
@@ -15,9 +15,6 @@ from .util import Finalize as _Finalize
 
 __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"]
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 _T = TypeVar("_T")
 _KT = TypeVar("_KT")
 _VT = TypeVar("_VT")
@@ -59,8 +56,7 @@ class ValueProxy(BaseProxy, Generic[_T]):
     def get(self) -> _T: ...
     def set(self, value: _T) -> None: ...
     value: _T
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 if sys.version_info >= (3, 13):
     class _BaseDictProxy(BaseProxy, MutableMapping[_KT, _VT]):
diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi
index 93197e5d4265..f276372d0903 100644
--- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi
@@ -1,13 +1,9 @@
-import sys
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from multiprocessing.context import DefaultContext, Process
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import Any, Final, Generic, TypeVar
 from typing_extensions import Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["Pool", "ThreadPool"]
 
 _S = TypeVar("_S")
@@ -21,8 +17,7 @@ class ApplyResult(Generic[_T]):
     def wait(self, timeout: float | None = None) -> None: ...
     def ready(self) -> bool: ...
     def successful(self) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # alias created during issue #17805
 AsyncResult = ApplyResult
diff --git a/mypy/typeshed/stdlib/multiprocessing/queues.pyi b/mypy/typeshed/stdlib/multiprocessing/queues.pyi
index 581a46ea0bc8..a6b00d744c42 100644
--- a/mypy/typeshed/stdlib/multiprocessing/queues.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/queues.pyi
@@ -1,9 +1,7 @@
 import sys
+from types import GenericAlias
 from typing import Any, Generic, TypeVar
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["Queue", "SimpleQueue", "JoinableQueue"]
 
 _T = TypeVar("_T")
@@ -31,11 +29,8 @@ class JoinableQueue(Queue[_T]):
 
 class SimpleQueue(Generic[_T]):
     def __init__(self, *, ctx: Any = ...) -> None: ...
-    if sys.version_info >= (3, 9):
-        def close(self) -> None: ...
-
+    def close(self) -> None: ...
     def empty(self) -> bool: ...
     def get(self) -> _T: ...
     def put(self, obj: _T) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi
index 61da7fdf1ceb..cb2f27a62861 100644
--- a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi
@@ -1,3 +1,4 @@
+import sys
 from _typeshed import FileDescriptorOrPath
 from collections.abc import Sized
 
@@ -8,6 +9,8 @@ class ResourceTracker:
     def ensure_running(self) -> None: ...
     def register(self, name: Sized, rtype: str) -> None: ...
     def unregister(self, name: Sized, rtype: str) -> None: ...
+    if sys.version_info >= (3, 12):
+        def __del__(self) -> None: ...
 
 _resource_tracker: ResourceTracker
 ensure_running = _resource_tracker.ensure_running
diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi
index b63cedf85867..1a12812c27e4 100644
--- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi
@@ -1,11 +1,9 @@
 import sys
 from collections.abc import Iterable
+from types import GenericAlias
 from typing import Any, Generic, TypeVar, overload
 from typing_extensions import Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["SharedMemory", "ShareableList"]
 
 _SLT = TypeVar("_SLT", int, float, bool, str, bytes, None)
@@ -40,5 +38,4 @@ class ShareableList(Generic[_SLT]):
     def format(self) -> str: ...
     def count(self, value: _SLT) -> int: ...
     def index(self, value: _SLT) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi
index 85dfbff1cb50..1fb1e79f69a1 100644
--- a/mypy/typeshed/stdlib/nntplib.pyi
+++ b/mypy/typeshed/stdlib/nntplib.pyi
@@ -1,7 +1,6 @@
 import datetime
 import socket
 import ssl
-import sys
 from _typeshed import Unused
 from builtins import list as _list  # conflicts with a method named "list"
 from collections.abc import Iterable
@@ -98,10 +97,6 @@ class NNTP:
     def over(
         self, message_spec: None | str | _list[Any] | tuple[Any, ...], *, file: _File = None
     ) -> tuple[str, _list[tuple[int, dict[str, str]]]]: ...
-    if sys.version_info < (3, 9):
-        def xgtitle(self, group: str, *, file: _File = None) -> tuple[str, _list[tuple[str, str]]]: ...
-        def xpath(self, id: Any) -> tuple[str, str]: ...
-
     def date(self) -> tuple[str, datetime.datetime]: ...
     def post(self, data: bytes | Iterable[bytes]) -> str: ...
     def ihave(self, message_id: Any, data: bytes | Iterable[bytes]) -> str: ...
diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi
index e1d57d09a9bd..3ed8f8af379b 100644
--- a/mypy/typeshed/stdlib/nt.pyi
+++ b/mypy/typeshed/stdlib/nt.pyi
@@ -89,14 +89,14 @@ if sys.platform == "win32":
         umask as umask,
         uname_result as uname_result,
         unlink as unlink,
+        unsetenv as unsetenv,
         urandom as urandom,
         utime as utime,
         waitpid as waitpid,
+        waitstatus_to_exitcode as waitstatus_to_exitcode,
         write as write,
     )
 
-    if sys.version_info >= (3, 9):
-        from os import unsetenv as unsetenv, waitstatus_to_exitcode as waitstatus_to_exitcode
     if sys.version_info >= (3, 11):
         from os import EX_OK as EX_OK
     if sys.version_info >= (3, 12):
diff --git a/mypy/typeshed/stdlib/nturl2path.pyi b/mypy/typeshed/stdlib/nturl2path.pyi
index b8ad8d682155..c38a359469d2 100644
--- a/mypy/typeshed/stdlib/nturl2path.pyi
+++ b/mypy/typeshed/stdlib/nturl2path.pyi
@@ -1,2 +1,12 @@
-def url2pathname(url: str) -> str: ...
-def pathname2url(p: str) -> str: ...
+import sys
+from typing_extensions import deprecated
+
+if sys.version_info >= (3, 14):
+    @deprecated("nturl2path module was deprecated since Python 3.14")
+    def url2pathname(url: str) -> str: ...
+    @deprecated("nturl2path module was deprecated since Python 3.14")
+    def pathname2url(p: str) -> str: ...
+
+else:
+    def url2pathname(url: str) -> str: ...
+    def pathname2url(p: str) -> str: ...
diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi
index f2bca4e58bc5..02d469ce0ee5 100644
--- a/mypy/typeshed/stdlib/numbers.pyi
+++ b/mypy/typeshed/stdlib/numbers.pyi
@@ -7,7 +7,6 @@
 # (since type checkers don't see `complex` as a subtype of `numbers.Complex`,
 # nor `float` as a subtype of `numbers.Real`, etc.)
 
-from _typeshed import Incomplete
 from abc import ABCMeta, abstractmethod
 from typing import ClassVar, Literal, Protocol, overload
 
@@ -166,7 +165,7 @@ class Integral(Rational, _IntegralLike):
     def __int__(self) -> int: ...
     def __index__(self) -> int: ...
     @abstractmethod
-    def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ...
+    def __pow__(self, exponent, modulus=None) -> _IntegralLike: ...
     @abstractmethod
     def __lshift__(self, other) -> _IntegralLike: ...
     @abstractmethod
diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi
index f9f76962f876..a5a3a79c323b 100644
--- a/mypy/typeshed/stdlib/opcode.pyi
+++ b/mypy/typeshed/stdlib/opcode.pyi
@@ -23,23 +23,7 @@ else:
 if sys.version_info >= (3, 13):
     __all__ += ["hasjump"]
 
-if sys.version_info >= (3, 9):
-    cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]]
-else:
-    cmp_op: tuple[
-        Literal["<"],
-        Literal["<="],
-        Literal["=="],
-        Literal["!="],
-        Literal[">"],
-        Literal[">="],
-        Literal["in"],
-        Literal["not in"],
-        Literal["is"],
-        Literal["is not"],
-        Literal["exception match"],
-        Literal["BAD"],
-    ]
+cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]]
 hasconst: list[int]
 hasname: list[int]
 hasjrel: list[int]
diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi
index 56a4574bdba8..8b7fcd82e5a5 100644
--- a/mypy/typeshed/stdlib/optparse.pyi
+++ b/mypy/typeshed/stdlib/optparse.pyi
@@ -239,7 +239,7 @@ class Values:
     # __getattr__ doesn't exist, but anything passed as a default to __init__
     # is set on the instance.
     def __getattr__(self, name: str) -> Any: ...
-    # TODO mypy infers -> object for __getattr__ if __setattr__ has `value: object`
+    # TODO: mypy infers -> object for __getattr__ if __setattr__ has `value: object`
     def __setattr__(self, name: str, value: Any, /) -> None: ...
     def __eq__(self, other: object) -> bool: ...
 
diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi
index 4a7c03632a67..5286c76d1b06 100644
--- a/mypy/typeshed/stdlib/os/__init__.pyi
+++ b/mypy/typeshed/stdlib/os/__init__.pyi
@@ -24,7 +24,7 @@ from builtins import OSError
 from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, Sequence
 from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
 from subprocess import Popen
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import (
     IO,
     Any,
@@ -44,9 +44,6 @@ from typing_extensions import Self, TypeAlias, Unpack, deprecated
 
 from . import path as _path
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "F_OK",
     "O_APPEND",
@@ -155,14 +152,16 @@ __all__ = [
     "umask",
     "uname_result",
     "unlink",
+    "unsetenv",
     "urandom",
     "utime",
     "waitpid",
+    "waitstatus_to_exitcode",
     "walk",
     "write",
 ]
-if sys.version_info >= (3, 9):
-    __all__ += ["waitstatus_to_exitcode"]
+if sys.version_info >= (3, 14):
+    __all__ += ["readinto"]
 if sys.platform == "darwin" and sys.version_info >= (3, 12):
     __all__ += ["PRIO_DARWIN_BG", "PRIO_DARWIN_NONUI", "PRIO_DARWIN_PROCESS", "PRIO_DARWIN_THREAD"]
 if sys.platform == "darwin" and sys.version_info >= (3, 10):
@@ -194,6 +193,7 @@ if sys.platform == "linux":
         "O_PATH",
         "O_RSYNC",
         "O_TMPFILE",
+        "P_PIDFD",
         "RTLD_DEEPBIND",
         "SCHED_BATCH",
         "SCHED_IDLE",
@@ -206,9 +206,12 @@ if sys.platform == "linux":
         "getxattr",
         "listxattr",
         "memfd_create",
+        "pidfd_open",
         "removexattr",
         "setxattr",
     ]
+if sys.platform == "linux" and sys.version_info >= (3, 14):
+    __all__ += ["SCHED_DEADLINE", "SCHED_NORMAL"]
 if sys.platform == "linux" and sys.version_info >= (3, 13):
     __all__ += [
         "POSIX_SPAWN_CLOSEFROM",
@@ -256,8 +259,6 @@ if sys.platform == "linux" and sys.version_info >= (3, 10):
         "eventfd_write",
         "splice",
     ]
-if sys.platform == "linux" and sys.version_info >= (3, 9):
-    __all__ += ["P_PIDFD", "pidfd_open"]
 if sys.platform == "win32":
     __all__ += [
         "O_BINARY",
@@ -280,6 +281,8 @@ if sys.platform != "win32":
         "CLD_CONTINUED",
         "CLD_DUMPED",
         "CLD_EXITED",
+        "CLD_KILLED",
+        "CLD_STOPPED",
         "CLD_TRAPPED",
         "EX_CANTCREAT",
         "EX_CONFIG",
@@ -431,8 +434,6 @@ if sys.platform != "win32" and sys.version_info >= (3, 11):
     __all__ += ["login_tty"]
 if sys.platform != "win32" and sys.version_info >= (3, 10):
     __all__ += ["O_FSYNC"]
-if sys.platform != "win32" and sys.version_info >= (3, 9):
-    __all__ += ["CLD_KILLED", "CLD_STOPPED"]
 if sys.platform != "darwin" and sys.platform != "win32":
     __all__ += [
         "POSIX_FADV_DONTNEED",
@@ -486,8 +487,6 @@ if sys.platform != "win32" or sys.version_info >= (3, 12):
     __all__ += ["get_blocking", "set_blocking"]
 if sys.platform != "win32" or sys.version_info >= (3, 11):
     __all__ += ["EX_OK"]
-if sys.platform != "win32" or sys.version_info >= (3, 9):
-    __all__ += ["unsetenv"]
 
 # This unnecessary alias is to work around various errors
 path = _path
@@ -550,7 +549,7 @@ if sys.platform != "win32":
     P_PGID: int
     P_ALL: int
 
-    if sys.platform == "linux" and sys.version_info >= (3, 9):
+    if sys.platform == "linux":
         P_PIDFD: int
 
     WEXITED: int
@@ -561,10 +560,8 @@ if sys.platform != "win32":
     CLD_DUMPED: int
     CLD_TRAPPED: int
     CLD_CONTINUED: int
-
-    if sys.version_info >= (3, 9):
-        CLD_KILLED: int
-        CLD_STOPPED: int
+    CLD_KILLED: int
+    CLD_STOPPED: int
 
     SCHED_OTHER: int
     SCHED_FIFO: int
@@ -577,6 +574,10 @@ if sys.platform == "linux":
     SCHED_IDLE: int
     SCHED_RESET_ON_FORK: int
 
+if sys.version_info >= (3, 14) and sys.platform == "linux":
+    SCHED_DEADLINE: int
+    SCHED_NORMAL: int
+
 if sys.platform != "win32":
     RTLD_LAZY: int
     RTLD_NOW: int
@@ -698,29 +699,14 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]):
     decodekey: _EnvironCodeFunc[AnyStr]
     encodevalue: _EnvironCodeFunc[AnyStr]
     decodevalue: _EnvironCodeFunc[AnyStr]
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            data: MutableMapping[AnyStr, AnyStr],
-            encodekey: _EnvironCodeFunc[AnyStr],
-            decodekey: _EnvironCodeFunc[AnyStr],
-            encodevalue: _EnvironCodeFunc[AnyStr],
-            decodevalue: _EnvironCodeFunc[AnyStr],
-        ) -> None: ...
-    else:
-        putenv: Callable[[AnyStr, AnyStr], object]
-        unsetenv: Callable[[AnyStr, AnyStr], object]
-        def __init__(
-            self,
-            data: MutableMapping[AnyStr, AnyStr],
-            encodekey: _EnvironCodeFunc[AnyStr],
-            decodekey: _EnvironCodeFunc[AnyStr],
-            encodevalue: _EnvironCodeFunc[AnyStr],
-            decodevalue: _EnvironCodeFunc[AnyStr],
-            putenv: Callable[[AnyStr, AnyStr], object],
-            unsetenv: Callable[[AnyStr, AnyStr], object],
-        ) -> None: ...
-
+    def __init__(
+        self,
+        data: MutableMapping[AnyStr, AnyStr],
+        encodekey: _EnvironCodeFunc[AnyStr],
+        decodekey: _EnvironCodeFunc[AnyStr],
+        encodevalue: _EnvironCodeFunc[AnyStr],
+        decodevalue: _EnvironCodeFunc[AnyStr],
+    ) -> None: ...
     def setdefault(self, key: AnyStr, value: AnyStr) -> AnyStr: ...
     def copy(self) -> dict[AnyStr, AnyStr]: ...
     def __delitem__(self, key: AnyStr) -> None: ...
@@ -728,16 +714,15 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]):
     def __setitem__(self, key: AnyStr, value: AnyStr) -> None: ...
     def __iter__(self) -> Iterator[AnyStr]: ...
     def __len__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __or__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ...
-        def __ror__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ...
-        # We use @overload instead of a Union for reasons similar to those given for
-        # overloading MutableMapping.update in stdlib/typing.pyi
-        # The type: ignore is needed due to incompatible __or__/__ior__ signatures
-        @overload  # type: ignore[misc]
-        def __ior__(self, other: Mapping[AnyStr, AnyStr]) -> Self: ...
-        @overload
-        def __ior__(self, other: Iterable[tuple[AnyStr, AnyStr]]) -> Self: ...
+    def __or__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ...
+    def __ror__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ...
+    # We use @overload instead of a Union for reasons similar to those given for
+    # overloading MutableMapping.update in stdlib/typing.pyi
+    # The type: ignore is needed due to incompatible __or__/__ior__ signatures
+    @overload  # type: ignore[misc]
+    def __ior__(self, other: Mapping[AnyStr, AnyStr]) -> Self: ...
+    @overload
+    def __ior__(self, other: Iterable[tuple[AnyStr, AnyStr]]) -> Self: ...
 
 environ: _Environ[str]
 if sys.platform != "win32":
@@ -900,8 +885,7 @@ class DirEntry(Generic[AnyStr]):
     def is_symlink(self) -> bool: ...
     def stat(self, *, follow_symlinks: bool = True) -> stat_result: ...
     def __fspath__(self) -> AnyStr: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
     if sys.version_info >= (3, 12):
         def is_junction(self) -> bool: ...
 
@@ -1024,9 +1008,7 @@ if sys.platform != "win32":
 
 else:
     def putenv(name: str, value: str, /) -> None: ...
-
-    if sys.version_info >= (3, 9):
-        def unsetenv(name: str, /) -> None: ...
+    def unsetenv(name: str, /) -> None: ...
 
 _Opener: TypeAlias = Callable[[str, int], int]
 
@@ -1175,6 +1157,9 @@ if sys.platform != "win32":
     def readv(fd: int, buffers: SupportsLenAndGetItem[WriteableBuffer], /) -> int: ...
     def writev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], /) -> int: ...
 
+if sys.version_info >= (3, 14):
+    def readinto(fd: int, buffer: ReadableBuffer, /) -> int: ...
+
 @final
 class terminal_size(structseq[int], tuple[int, int]):
     if sys.version_info >= (3, 10):
@@ -1598,11 +1583,10 @@ if sys.platform == "linux":
     def memfd_create(name: str, flags: int = ...) -> int: ...
     def copy_file_range(src: int, dst: int, count: int, offset_src: int | None = ..., offset_dst: int | None = ...) -> int: ...
 
-if sys.version_info >= (3, 9):
-    def waitstatus_to_exitcode(status: int) -> int: ...
+def waitstatus_to_exitcode(status: int) -> int: ...
 
-    if sys.platform == "linux":
-        def pidfd_open(pid: int, flags: int = ...) -> int: ...
+if sys.platform == "linux":
+    def pidfd_open(pid: int, flags: int = ...) -> int: ...
 
 if sys.version_info >= (3, 12) and sys.platform == "linux":
     PIDFD_NONBLOCK: Final = 2048
diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib/__init__.pyi
similarity index 86%
rename from mypy/typeshed/stdlib/pathlib.pyi
rename to mypy/typeshed/stdlib/pathlib/__init__.pyi
index a18aed4ba57a..b84fc69313a1 100644
--- a/mypy/typeshed/stdlib/pathlib.pyi
+++ b/mypy/typeshed/stdlib/pathlib/__init__.pyi
@@ -14,15 +14,17 @@ from _typeshed import (
 from collections.abc import Callable, Generator, Iterator, Sequence
 from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
 from os import PathLike, stat_result
-from types import TracebackType
-from typing import IO, Any, BinaryIO, ClassVar, Literal, overload
+from types import GenericAlias, TracebackType
+from typing import IO, Any, BinaryIO, ClassVar, Literal, TypeVar, overload
 from typing_extensions import Never, Self, deprecated
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
+_PathT = TypeVar("_PathT", bound=PurePath)
 
 __all__ = ["PurePath", "PurePosixPath", "PureWindowsPath", "Path", "PosixPath", "WindowsPath"]
 
+if sys.version_info >= (3, 14):
+    from pathlib.types import PathInfo
+
 if sys.version_info >= (3, 13):
     __all__ += ["UnsupportedOperation"]
 
@@ -66,9 +68,11 @@ class PurePath(PathLike[str]):
     def as_uri(self) -> str: ...
     def is_absolute(self) -> bool: ...
     def is_reserved(self) -> bool: ...
-    if sys.version_info >= (3, 12):
+    if sys.version_info >= (3, 14):
+        def is_relative_to(self, other: StrPath) -> bool: ...
+    elif sys.version_info >= (3, 12):
         def is_relative_to(self, other: StrPath, /, *_deprecated: StrPath) -> bool: ...
-    elif sys.version_info >= (3, 9):
+    else:
         def is_relative_to(self, *other: StrPath) -> bool: ...
 
     if sys.version_info >= (3, 12):
@@ -76,22 +80,22 @@ class PurePath(PathLike[str]):
     else:
         def match(self, path_pattern: str) -> bool: ...
 
-    if sys.version_info >= (3, 12):
+    if sys.version_info >= (3, 14):
+        def relative_to(self, other: StrPath, *, walk_up: bool = False) -> Self: ...
+    elif sys.version_info >= (3, 12):
         def relative_to(self, other: StrPath, /, *_deprecated: StrPath, walk_up: bool = False) -> Self: ...
     else:
         def relative_to(self, *other: StrPath) -> Self: ...
 
     def with_name(self, name: str) -> Self: ...
-    if sys.version_info >= (3, 9):
-        def with_stem(self, stem: str) -> Self: ...
-
+    def with_stem(self, stem: str) -> Self: ...
     def with_suffix(self, suffix: str) -> Self: ...
     def joinpath(self, *other: StrPath) -> Self: ...
     @property
     def parents(self) -> Sequence[Self]: ...
     @property
     def parent(self) -> Self: ...
-    if sys.version_info >= (3, 9) and sys.version_info < (3, 11):
+    if sys.version_info < (3, 11):
         def __class_getitem__(cls, type: Any) -> GenericAlias: ...
 
     if sys.version_info >= (3, 12):
@@ -159,17 +163,25 @@ class Path(PurePath):
     def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def copy(self, target: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> None: ...
-        def copytree(
-            self,
-            target: StrPath,
-            *,
-            follow_symlinks: bool = True,
-            preserve_metadata: bool = False,
-            dirs_exist_ok: bool = False,
-            ignore: Callable[[Self], bool] | None = None,
-            on_error: Callable[[OSError], object] | None = None,
-        ) -> None: ...
+
+        @property
+        def info(self) -> PathInfo: ...
+        @overload
+        def move_into(self, target_dir: _PathT) -> _PathT: ...  # type: ignore[overload-overlap]
+        @overload
+        def move_into(self, target_dir: StrPath) -> Self: ...  # type: ignore[overload-overlap]
+        @overload
+        def move(self, target: _PathT) -> _PathT: ...  # type: ignore[overload-overlap]
+        @overload
+        def move(self, target: StrPath) -> Self: ...  # type: ignore[overload-overlap]
+        @overload
+        def copy_into(self, target_dir: _PathT, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> _PathT: ...  # type: ignore[overload-overlap]
+        @overload
+        def copy_into(self, target_dir: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> Self: ...  # type: ignore[overload-overlap]
+        @overload
+        def copy(self, target: _PathT, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> _PathT: ...  # type: ignore[overload-overlap]
+        @overload
+        def copy(self, target: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> Self: ...  # type: ignore[overload-overlap]
 
     # Adapted from builtins.open
     # Text mode: always returns a TextIOWrapper
@@ -247,8 +259,7 @@ class Path(PurePath):
     else:
         def is_mount(self) -> bool: ...
 
-    if sys.version_info >= (3, 9):
-        def readlink(self) -> Self: ...
+    def readlink(self) -> Self: ...
 
     if sys.version_info >= (3, 10):
         def rename(self, target: StrPath) -> Self: ...
@@ -259,9 +270,6 @@ class Path(PurePath):
 
     def resolve(self, strict: bool = False) -> Self: ...
     def rmdir(self) -> None: ...
-    if sys.version_info >= (3, 14):
-        def delete(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ...
-
     def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ...
     if sys.version_info >= (3, 10):
         def hardlink_to(self, target: StrOrBytesPath) -> None: ...
@@ -292,9 +300,6 @@ class Path(PurePath):
             self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ...
         ) -> Iterator[tuple[Self, list[str], list[str]]]: ...
 
-    if sys.version_info >= (3, 14):
-        def rmtree(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ...
-
 class PosixPath(Path, PurePosixPath): ...
 class WindowsPath(Path, PureWindowsPath): ...
 
diff --git a/mypy/typeshed/stdlib/pathlib/types.pyi b/mypy/typeshed/stdlib/pathlib/types.pyi
new file mode 100644
index 000000000000..9f9a650846de
--- /dev/null
+++ b/mypy/typeshed/stdlib/pathlib/types.pyi
@@ -0,0 +1,8 @@
+from typing import Protocol, runtime_checkable
+
+@runtime_checkable
+class PathInfo(Protocol):
+    def exists(self, *, follow_symlinks: bool = True) -> bool: ...
+    def is_dir(self, *, follow_symlinks: bool = True) -> bool: ...
+    def is_file(self, *, follow_symlinks: bool = True) -> bool: ...
+    def is_symlink(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi
index 61e8b7176e84..ad69fcab16de 100644
--- a/mypy/typeshed/stdlib/pdb.pyi
+++ b/mypy/typeshed/stdlib/pdb.pyi
@@ -1,17 +1,21 @@
 import signal
 import sys
-from bdb import Bdb
+from bdb import Bdb, _Backend
 from cmd import Cmd
 from collections.abc import Callable, Iterable, Mapping, Sequence
 from inspect import _SourceObjectType
+from linecache import _ModuleGlobals
 from types import CodeType, FrameType, TracebackType
-from typing import IO, Any, ClassVar, Final, TypeVar
-from typing_extensions import ParamSpec, Self
+from typing import IO, Any, ClassVar, Final, Literal, TypeVar
+from typing_extensions import ParamSpec, Self, TypeAlias
 
 __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"]
+if sys.version_info >= (3, 14):
+    __all__ += ["set_default_backend", "get_default_backend"]
 
 _T = TypeVar("_T")
 _P = ParamSpec("_P")
+_Mode: TypeAlias = Literal["inline", "cli"]
 
 line_prefix: str  # undocumented
 
@@ -21,7 +25,16 @@ def run(statement: str, globals: dict[str, Any] | None = None, locals: Mapping[s
 def runeval(expression: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> Any: ...
 def runctx(statement: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> None: ...
 def runcall(func: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> _T | None: ...
-def set_trace(*, header: str | None = None) -> None: ...
+
+if sys.version_info >= (3, 14):
+    def set_default_backend(backend: _Backend) -> None: ...
+    def get_default_backend() -> _Backend: ...
+    def set_trace(*, header: str | None = None, commands: Iterable[str] | None = None) -> None: ...
+    async def set_trace_async(*, header: str | None = None, commands: Iterable[str] | None = None) -> None: ...
+
+else:
+    def set_trace(*, header: str | None = None) -> None: ...
+
 def post_mortem(t: TracebackType | None = None) -> None: ...
 def pm() -> None: ...
 
@@ -47,15 +60,35 @@ class Pdb(Bdb, Cmd):
     curindex: int
     curframe: FrameType | None
     curframe_locals: Mapping[str, Any]
-    def __init__(
-        self,
-        completekey: str = "tab",
-        stdin: IO[str] | None = None,
-        stdout: IO[str] | None = None,
-        skip: Iterable[str] | None = None,
-        nosigint: bool = False,
-        readrc: bool = True,
-    ) -> None: ...
+    if sys.version_info >= (3, 14):
+        mode: _Mode | None
+        colorize: bool
+        def __init__(
+            self,
+            completekey: str = "tab",
+            stdin: IO[str] | None = None,
+            stdout: IO[str] | None = None,
+            skip: Iterable[str] | None = None,
+            nosigint: bool = False,
+            readrc: bool = True,
+            mode: _Mode | None = None,
+            backend: _Backend | None = None,
+            colorize: bool = False,
+        ) -> None: ...
+    else:
+        def __init__(
+            self,
+            completekey: str = "tab",
+            stdin: IO[str] | None = None,
+            stdout: IO[str] | None = None,
+            skip: Iterable[str] | None = None,
+            nosigint: bool = False,
+            readrc: bool = True,
+        ) -> None: ...
+    if sys.version_info >= (3, 14):
+        def set_trace(self, frame: FrameType | None = None, *, commands: Iterable[str] | None = None) -> None: ...
+        async def set_trace_async(self, frame: FrameType | None = None, *, commands: Iterable[str] | None = None) -> None: ...
+
     def forget(self) -> None: ...
     def setup(self, f: FrameType | None, tb: TracebackType | None) -> None: ...
     if sys.version_info < (3, 11):
@@ -75,14 +108,25 @@ class Pdb(Bdb, Cmd):
     def handle_command_def(self, line: str) -> bool: ...
     def defaultFile(self) -> str: ...
     def lineinfo(self, identifier: str) -> tuple[None, None, None] | tuple[str, str, int]: ...
-    def checkline(self, filename: str, lineno: int) -> int: ...
+    if sys.version_info >= (3, 14):
+        def checkline(self, filename: str, lineno: int, module_globals: _ModuleGlobals | None = None) -> int: ...
+    else:
+        def checkline(self, filename: str, lineno: int) -> int: ...
+
     def _getval(self, arg: str) -> object: ...
-    def print_stack_trace(self) -> None: ...
+    if sys.version_info >= (3, 14):
+        def print_stack_trace(self, count: int | None = None) -> None: ...
+    else:
+        def print_stack_trace(self) -> None: ...
+
     def print_stack_entry(self, frame_lineno: tuple[FrameType, int], prompt_prefix: str = "\n-> ") -> None: ...
     def lookupmodule(self, filename: str) -> str | None: ...
     if sys.version_info < (3, 11):
         def _runscript(self, filename: str) -> None: ...
 
+    if sys.version_info >= (3, 14):
+        def complete_multiline_names(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ...
+
     if sys.version_info >= (3, 13):
         def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ...
 
diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi
index 59d70779c72f..e764d08e79f8 100644
--- a/mypy/typeshed/stdlib/pkgutil.pyi
+++ b/mypy/typeshed/stdlib/pkgutil.pyi
@@ -8,8 +8,6 @@ from typing_extensions import deprecated
 __all__ = [
     "get_importer",
     "iter_importers",
-    "get_loader",
-    "find_loader",
     "walk_packages",
     "iter_modules",
     "get_data",
@@ -17,6 +15,8 @@ __all__ = [
     "extend_path",
     "ModuleInfo",
 ]
+if sys.version_info < (3, 14):
+    __all__ += ["get_loader", "find_loader"]
 if sys.version_info < (3, 12):
     __all__ += ["ImpImporter", "ImpLoader"]
 
@@ -36,11 +36,13 @@ if sys.version_info < (3, 12):
     class ImpLoader:
         def __init__(self, fullname: str, file: IO[str], filename: StrOrBytesPath, etc: tuple[str, str, int]) -> None: ...
 
-@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
-def find_loader(fullname: str) -> LoaderProtocol | None: ...
+if sys.version_info < (3, 14):
+    @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
+    def find_loader(fullname: str) -> LoaderProtocol | None: ...
+    @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
+    def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
+
 def get_importer(path_item: StrOrBytesPath) -> PathEntryFinderProtocol | None: ...
-@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
-def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
 def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ...
 def iter_modules(path: Iterable[StrOrBytesPath] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ...
 def read_code(stream: SupportsRead[bytes]) -> Any: ...  # undocumented
@@ -48,6 +50,4 @@ def walk_packages(
     path: Iterable[StrOrBytesPath] | None = None, prefix: str = "", onerror: Callable[[str], object] | None = None
 ) -> Iterator[ModuleInfo]: ...
 def get_data(package: str, resource: str) -> bytes | None: ...
-
-if sys.version_info >= (3, 9):
-    def resolve_name(name: str) -> Any: ...
+def resolve_name(name: str) -> Any: ...
diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi
index 73393eada02c..fbc73c6c9177 100644
--- a/mypy/typeshed/stdlib/platform.pyi
+++ b/mypy/typeshed/stdlib/platform.pyi
@@ -15,40 +15,29 @@ def java_ver(
 def system_alias(system: str, release: str, version: str) -> tuple[str, str, str]: ...
 def architecture(executable: str = sys.executable, bits: str = "", linkage: str = "") -> tuple[str, str]: ...
 
-if sys.version_info >= (3, 9):
-    # This class is not exposed. It calls itself platform.uname_result_base.
-    # At runtime it only has 5 fields.
-    @type_check_only
-    class _uname_result_base(NamedTuple):
-        system: str
-        node: str
-        release: str
-        version: str
-        machine: str
-        # This base class doesn't have this field at runtime, but claiming it
-        # does is the least bad way to handle the situation. Nobody really
-        # sees this class anyway. See #13068
-        processor: str
-
-    # uname_result emulates a 6-field named tuple, but the processor field
-    # is lazily evaluated rather than being passed in to the constructor.
-    class uname_result(_uname_result_base):
-        if sys.version_info >= (3, 10):
-            __match_args__ = ("system", "node", "release", "version", "machine")  # pyright: ignore[reportAssignmentType]
+# This class is not exposed. It calls itself platform.uname_result_base.
+# At runtime it only has 5 fields.
+@type_check_only
+class _uname_result_base(NamedTuple):
+    system: str
+    node: str
+    release: str
+    version: str
+    machine: str
+    # This base class doesn't have this field at runtime, but claiming it
+    # does is the least bad way to handle the situation. Nobody really
+    # sees this class anyway. See #13068
+    processor: str
 
-        def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ...
-        @property
-        def processor(self) -> str: ...
+# uname_result emulates a 6-field named tuple, but the processor field
+# is lazily evaluated rather than being passed in to the constructor.
+class uname_result(_uname_result_base):
+    if sys.version_info >= (3, 10):
+        __match_args__ = ("system", "node", "release", "version", "machine")  # pyright: ignore[reportAssignmentType]
 
-else:
-    # On 3.8, uname_result is actually just a regular NamedTuple.
-    class uname_result(NamedTuple):
-        system: str
-        node: str
-        release: str
-        version: str
-        machine: str
-        processor: str
+    def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ...
+    @property
+    def processor(self) -> str: ...
 
 def uname() -> uname_result: ...
 def system() -> str: ...
@@ -93,3 +82,6 @@ if sys.version_info >= (3, 13):
         is_emulator: bool = False,
     ) -> AndroidVer: ...
     def ios_ver(system: str = "", release: str = "", model: str = "", is_simulator: bool = False) -> IOSVersionInfo: ...
+
+if sys.version_info >= (3, 14):
+    def invalidate_caches() -> None: ...
diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi
index 72b5398f0a52..8b39b4217eae 100644
--- a/mypy/typeshed/stdlib/plistlib.pyi
+++ b/mypy/typeshed/stdlib/plistlib.pyi
@@ -3,12 +3,10 @@ from _typeshed import ReadableBuffer
 from collections.abc import Mapping, MutableMapping
 from datetime import datetime
 from enum import Enum
-from typing import IO, Any, ClassVar
+from typing import IO, Any
 from typing_extensions import Self
 
 __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"]
-if sys.version_info < (3, 9):
-    __all__ += ["readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "Data"]
 
 class PlistFormat(Enum):
     FMT_XML = 1
@@ -32,28 +30,12 @@ if sys.version_info >= (3, 13):
         aware_datetime: bool = False,
     ) -> Any: ...
 
-elif sys.version_info >= (3, 9):
+else:
     def load(fp: IO[bytes], *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ...) -> Any: ...
     def loads(
         value: ReadableBuffer, *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ...
     ) -> Any: ...
 
-else:
-    def load(
-        fp: IO[bytes],
-        *,
-        fmt: PlistFormat | None = None,
-        use_builtin_types: bool = True,
-        dict_type: type[MutableMapping[str, Any]] = ...,
-    ) -> Any: ...
-    def loads(
-        value: ReadableBuffer,
-        *,
-        fmt: PlistFormat | None = None,
-        use_builtin_types: bool = True,
-        dict_type: type[MutableMapping[str, Any]] = ...,
-    ) -> Any: ...
-
 if sys.version_info >= (3, 13):
     def dump(
         value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime,
@@ -90,18 +72,6 @@ else:
         sort_keys: bool = True,
     ) -> bytes: ...
 
-if sys.version_info < (3, 9):
-    def readPlist(pathOrFile: str | IO[bytes]) -> Any: ...
-    def writePlist(value: Mapping[str, Any], pathOrFile: str | IO[bytes]) -> None: ...
-    def readPlistFromBytes(data: ReadableBuffer) -> Any: ...
-    def writePlistToBytes(value: Mapping[str, Any]) -> bytes: ...
-
-if sys.version_info < (3, 9):
-    class Data:
-        data: bytes
-        def __init__(self, data: bytes) -> None: ...
-        __hash__: ClassVar[None]  # type: ignore[assignment]
-
 class UID:
     data: int
     def __init__(self, data: int) -> None: ...
diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi
index e7223842ace5..6d0d76ab8217 100644
--- a/mypy/typeshed/stdlib/posix.pyi
+++ b/mypy/typeshed/stdlib/posix.pyi
@@ -6,6 +6,8 @@ if sys.platform != "win32":
         CLD_CONTINUED as CLD_CONTINUED,
         CLD_DUMPED as CLD_DUMPED,
         CLD_EXITED as CLD_EXITED,
+        CLD_KILLED as CLD_KILLED,
+        CLD_STOPPED as CLD_STOPPED,
         CLD_TRAPPED as CLD_TRAPPED,
         EX_CANTCREAT as EX_CANTCREAT,
         EX_CONFIG as EX_CONFIG,
@@ -220,13 +222,11 @@ if sys.platform != "win32":
         wait3 as wait3,
         wait4 as wait4,
         waitpid as waitpid,
+        waitstatus_to_exitcode as waitstatus_to_exitcode,
         write as write,
         writev as writev,
     )
 
-    if sys.version_info >= (3, 9):
-        from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode
-
     if sys.version_info >= (3, 10):
         from os import O_FSYNC as O_FSYNC
 
@@ -250,6 +250,12 @@ if sys.platform != "win32":
             timerfd_settime_ns as timerfd_settime_ns,
         )
 
+    if sys.version_info >= (3, 14):
+        from os import readinto as readinto
+
+    if sys.version_info >= (3, 14) and sys.platform == "linux":
+        from os import SCHED_DEADLINE as SCHED_DEADLINE, SCHED_NORMAL as SCHED_NORMAL
+
     if sys.platform != "linux":
         from os import O_EXLOCK as O_EXLOCK, O_SHLOCK as O_SHLOCK, chflags as chflags, lchflags as lchflags, lchmod as lchmod
 
@@ -330,6 +336,7 @@ if sys.platform != "win32":
             O_PATH as O_PATH,
             O_RSYNC as O_RSYNC,
             O_TMPFILE as O_TMPFILE,
+            P_PIDFD as P_PIDFD,
             RTLD_DEEPBIND as RTLD_DEEPBIND,
             SCHED_BATCH as SCHED_BATCH,
             SCHED_IDLE as SCHED_IDLE,
@@ -342,13 +349,11 @@ if sys.platform != "win32":
             getxattr as getxattr,
             listxattr as listxattr,
             memfd_create as memfd_create,
+            pidfd_open as pidfd_open,
             removexattr as removexattr,
             setxattr as setxattr,
         )
 
-        if sys.version_info >= (3, 9):
-            from os import P_PIDFD as P_PIDFD, pidfd_open as pidfd_open
-
         if sys.version_info >= (3, 10):
             from os import (
                 EFD_CLOEXEC as EFD_CLOEXEC,
diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi
index d41fa202cf77..c4dee1f6b8f6 100644
--- a/mypy/typeshed/stdlib/pstats.pyi
+++ b/mypy/typeshed/stdlib/pstats.pyi
@@ -2,6 +2,7 @@ import sys
 from _typeshed import StrOrBytesPath
 from collections.abc import Iterable
 from cProfile import Profile as _cProfile
+from dataclasses import dataclass
 from profile import Profile
 from typing import IO, Any, Literal, overload
 from typing_extensions import Self, TypeAlias
@@ -11,10 +12,7 @@ if sys.version_info >= (3, 11):
 else:
     from enum import Enum
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"]
-else:
-    __all__ = ["Stats", "SortKey"]
+__all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"]
 
 _Selector: TypeAlias = str | float | int
 
@@ -42,23 +40,20 @@ else:
         STDNAME = "stdname"
         TIME = "time"
 
-if sys.version_info >= (3, 9):
-    from dataclasses import dataclass
-
-    @dataclass(unsafe_hash=True)
-    class FunctionProfile:
-        ncalls: str
-        tottime: float
-        percall_tottime: float
-        cumtime: float
-        percall_cumtime: float
-        file_name: str
-        line_number: int
+@dataclass(unsafe_hash=True)
+class FunctionProfile:
+    ncalls: str
+    tottime: float
+    percall_tottime: float
+    cumtime: float
+    percall_cumtime: float
+    file_name: str
+    line_number: int
 
-    @dataclass(unsafe_hash=True)
-    class StatsProfile:
-        total_tt: float
-        func_profiles: dict[str, FunctionProfile]
+@dataclass(unsafe_hash=True)
+class StatsProfile:
+    total_tt: float
+    func_profiles: dict[str, FunctionProfile]
 
 _SortArgDict: TypeAlias = dict[str, tuple[tuple[tuple[int, int], ...], str]]
 
@@ -85,9 +80,7 @@ class Stats:
     def strip_dirs(self) -> Self: ...
     def calc_callees(self) -> None: ...
     def eval_print_amount(self, sel: _Selector, list: list[str], msg: str) -> tuple[list[str], str]: ...
-    if sys.version_info >= (3, 9):
-        def get_stats_profile(self) -> StatsProfile: ...
-
+    def get_stats_profile(self) -> StatsProfile: ...
     def get_print_list(self, sel_list: Iterable[_Selector]) -> tuple[int, list[str]]: ...
     def print_stats(self, *amount: _Selector) -> Self: ...
     def print_callees(self, *amount: _Selector) -> Self: ...
diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi
index 144f782acad5..f14b9d1bb699 100644
--- a/mypy/typeshed/stdlib/pydoc.pyi
+++ b/mypy/typeshed/stdlib/pydoc.pyi
@@ -6,7 +6,7 @@ from collections.abc import Callable, Container, Mapping, MutableMapping
 from reprlib import Repr
 from types import MethodType, ModuleType, TracebackType
 from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar
-from typing_extensions import TypeGuard
+from typing_extensions import TypeGuard, deprecated
 
 __all__ = ["help"]
 
@@ -31,7 +31,14 @@ def stripid(text: str) -> str: ...
 def allmethods(cl: type) -> MutableMapping[str, MethodType]: ...
 def visiblename(name: str, all: Container[str] | None = None, obj: object = None) -> bool: ...
 def classify_class_attrs(object: object) -> list[tuple[str, str, type, str]]: ...
-def ispackage(path: str) -> bool: ...
+
+if sys.version_info >= (3, 13):
+    @deprecated("Deprecated in Python 3.13.")
+    def ispackage(path: str) -> bool: ...
+
+else:
+    def ispackage(path: str) -> bool: ...
+
 def source_synopsis(file: IO[AnyStr]) -> AnyStr | None: ...
 def synopsis(filename: str, cache: MutableMapping[str, tuple[int, str]] = {}) -> str | None: ...
 
diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi
index 4fb49cb6102b..f5d9179e079d 100644
--- a/mypy/typeshed/stdlib/queue.pyi
+++ b/mypy/typeshed/stdlib/queue.pyi
@@ -1,11 +1,9 @@
 import sys
 from _queue import Empty as Empty, SimpleQueue as SimpleQueue
 from threading import Condition, Lock
+from types import GenericAlias
 from typing import Any, Generic, TypeVar
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = ["Empty", "Full", "Queue", "PriorityQueue", "LifoQueue", "SimpleQueue"]
 if sys.version_info >= (3, 13):
     __all__ += ["ShutDown"]
@@ -47,8 +45,7 @@ class Queue(Generic[_T]):
     def qsize(self) -> int: ...
     def _qsize(self) -> int: ...
     def task_done(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class PriorityQueue(Queue[_T]):
     queue: list[_T]
diff --git a/mypy/typeshed/stdlib/random.pyi b/mypy/typeshed/stdlib/random.pyi
index e7320369c377..83e37113a941 100644
--- a/mypy/typeshed/stdlib/random.pyi
+++ b/mypy/typeshed/stdlib/random.pyi
@@ -30,10 +30,9 @@ __all__ = [
     "getrandbits",
     "choices",
     "SystemRandom",
+    "randbytes",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["randbytes"]
 if sys.version_info >= (3, 12):
     __all__ += ["binomialvariate"]
 
@@ -41,25 +40,16 @@ _T = TypeVar("_T")
 
 class Random(_random.Random):
     VERSION: ClassVar[int]
-    if sys.version_info >= (3, 9):
-        def __init__(self, x: int | float | str | bytes | bytearray | None = None) -> None: ...  # noqa: Y041
-    else:
-        def __init__(self, x: Any = None) -> None: ...
+    def __init__(self, x: int | float | str | bytes | bytearray | None = None) -> None: ...  # noqa: Y041
     # Using other `seed` types is deprecated since 3.9 and removed in 3.11
     # Ignore Y041, since random.seed doesn't treat int like a float subtype. Having an explicit
     # int better documents conventional usage of random.seed.
-    if sys.version_info >= (3, 9):
-        def seed(self, a: int | float | str | bytes | bytearray | None = None, version: int = 2) -> None: ...  # type: ignore[override]  # noqa: Y041
-    else:
-        def seed(self, a: Any = None, version: int = 2) -> None: ...
-
+    def seed(self, a: int | float | str | bytes | bytearray | None = None, version: int = 2) -> None: ...  # type: ignore[override]  # noqa: Y041
     def getstate(self) -> tuple[Any, ...]: ...
     def setstate(self, state: tuple[Any, ...]) -> None: ...
     def randrange(self, start: int, stop: int | None = None, step: int = 1) -> int: ...
     def randint(self, a: int, b: int) -> int: ...
-    if sys.version_info >= (3, 9):
-        def randbytes(self, n: int) -> bytes: ...
-
+    def randbytes(self, n: int) -> bytes: ...
     def choice(self, seq: SupportsLenAndGetItem[_T]) -> _T: ...
     def choices(
         self,
@@ -75,12 +65,10 @@ class Random(_random.Random):
         def shuffle(self, x: MutableSequence[Any], random: Callable[[], float] | None = None) -> None: ...
     if sys.version_info >= (3, 11):
         def sample(self, population: Sequence[_T], k: int, *, counts: Iterable[int] | None = None) -> list[_T]: ...
-    elif sys.version_info >= (3, 9):
+    else:
         def sample(
             self, population: Sequence[_T] | AbstractSet[_T], k: int, *, counts: Iterable[int] | None = None
         ) -> list[_T]: ...
-    else:
-        def sample(self, population: Sequence[_T] | AbstractSet[_T], k: int) -> list[_T]: ...
 
     def uniform(self, a: float, b: float) -> float: ...
     def triangular(self, low: float = 0.0, high: float = 1.0, mode: float | None = None) -> float: ...
@@ -137,5 +125,4 @@ weibullvariate = _inst.weibullvariate
 getstate = _inst.getstate
 setstate = _inst.setstate
 getrandbits = _inst.getrandbits
-if sys.version_info >= (3, 9):
-    randbytes = _inst.randbytes
+randbytes = _inst.randbytes
diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi
index fccdedae9436..f25a0a376704 100644
--- a/mypy/typeshed/stdlib/re.pyi
+++ b/mypy/typeshed/stdlib/re.pyi
@@ -4,12 +4,10 @@ import sre_constants
 import sys
 from _typeshed import MaybeNone, ReadableBuffer
 from collections.abc import Callable, Iterator, Mapping
+from types import GenericAlias
 from typing import Any, AnyStr, Final, Generic, Literal, TypeVar, final, overload
 from typing_extensions import TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "match",
     "fullmatch",
@@ -117,8 +115,7 @@ class Match(Generic[AnyStr]):
     def __getitem__(self, key: int | str, /) -> AnyStr | MaybeNone: ...
     def __copy__(self) -> Match[AnyStr]: ...
     def __deepcopy__(self, memo: Any, /) -> Match[AnyStr]: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @final
 class Pattern(Generic[AnyStr]):
@@ -197,8 +194,7 @@ class Pattern(Generic[AnyStr]):
     def __deepcopy__(self, memo: Any, /) -> Pattern[AnyStr]: ...
     def __eq__(self, value: object, /) -> bool: ...
     def __hash__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # ----- re variables and constants -----
 
diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi
index 0fe560fd9b6a..ea2c29d4625f 100644
--- a/mypy/typeshed/stdlib/shutil.pyi
+++ b/mypy/typeshed/stdlib/shutil.pyi
@@ -1,6 +1,6 @@
 import os
 import sys
-from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite
+from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, MaybeNone, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite
 from collections.abc import Callable, Iterable, Sequence
 from tarfile import _TarfileFilter
 from typing import Any, AnyStr, NamedTuple, NoReturn, Protocol, TypeVar, overload
@@ -36,9 +36,8 @@ __all__ = [
 ]
 
 _StrOrBytesPathT = TypeVar("_StrOrBytesPathT", bound=StrOrBytesPath)
-# Return value of some functions that may either return a path-like object that was passed in or
-# a string
-_PathReturn: TypeAlias = Any
+_StrPathT = TypeVar("_StrPathT", bound=StrPath)
+_BytesPathT = TypeVar("_BytesPathT", bound=BytesPath)
 
 class Error(OSError): ...
 class SameFileError(Error): ...
@@ -52,23 +51,23 @@ def copyfile(src: StrOrBytesPath, dst: _StrOrBytesPathT, *, follow_symlinks: boo
 def copymode(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ...
 def copystat(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ...
 @overload
-def copy(src: StrPath, dst: StrPath, *, follow_symlinks: bool = True) -> _PathReturn: ...
+def copy(src: StrPath, dst: _StrPathT, *, follow_symlinks: bool = True) -> _StrPathT | str: ...
 @overload
-def copy(src: BytesPath, dst: BytesPath, *, follow_symlinks: bool = True) -> _PathReturn: ...
+def copy(src: BytesPath, dst: _BytesPathT, *, follow_symlinks: bool = True) -> _BytesPathT | bytes: ...
 @overload
-def copy2(src: StrPath, dst: StrPath, *, follow_symlinks: bool = True) -> _PathReturn: ...
+def copy2(src: StrPath, dst: _StrPathT, *, follow_symlinks: bool = True) -> _StrPathT | str: ...
 @overload
-def copy2(src: BytesPath, dst: BytesPath, *, follow_symlinks: bool = True) -> _PathReturn: ...
+def copy2(src: BytesPath, dst: _BytesPathT, *, follow_symlinks: bool = True) -> _BytesPathT | bytes: ...
 def ignore_patterns(*patterns: StrPath) -> Callable[[Any, list[str]], set[str]]: ...
 def copytree(
     src: StrPath,
-    dst: StrPath,
+    dst: _StrPathT,
     symlinks: bool = False,
     ignore: None | Callable[[str, list[str]], Iterable[str]] | Callable[[StrPath, list[str]], Iterable[str]] = None,
     copy_function: Callable[[str, str], object] = ...,
     ignore_dangling_symlinks: bool = False,
     dirs_exist_ok: bool = False,
-) -> _PathReturn: ...
+) -> _StrPathT: ...
 
 _OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, ExcInfo], object]
 _OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object]
@@ -129,12 +128,7 @@ _CopyFn: TypeAlias = Callable[[str, str], object] | Callable[[StrPath, StrPath],
 # N.B. shutil.move appears to take bytes arguments, however,
 # this does not work when dst is (or is within) an existing directory.
 # (#6832)
-if sys.version_info >= (3, 9):
-    def move(src: StrPath, dst: StrPath, copy_function: _CopyFn = ...) -> _PathReturn: ...
-
-else:
-    # See https://bugs.python.org/issue32689
-    def move(src: str, dst: StrPath, copy_function: _CopyFn = ...) -> _PathReturn: ...
+def move(src: StrPath, dst: _StrPathT, copy_function: _CopyFn = ...) -> _StrPathT | str | MaybeNone: ...
 
 class _ntuple_diskusage(NamedTuple):
     total: int
diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi
index 8fc853b25cc1..d50565d1c8ac 100644
--- a/mypy/typeshed/stdlib/signal.pyi
+++ b/mypy/typeshed/stdlib/signal.pyi
@@ -183,6 +183,5 @@ def valid_signals() -> set[Signals]: ...
 def raise_signal(signalnum: _SIGNUM, /) -> None: ...
 def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = ...) -> int: ...
 
-if sys.version_info >= (3, 9):
-    if sys.platform == "linux":
-        def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = ..., /) -> None: ...
+if sys.platform == "linux":
+    def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = ..., /) -> None: ...
diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi
index a762427bcab3..609b3e6426c4 100644
--- a/mypy/typeshed/stdlib/smtplib.pyi
+++ b/mypy/typeshed/stdlib/smtplib.pyi
@@ -185,20 +185,11 @@ class SMTP_SSL(SMTP):
 LMTP_PORT: int
 
 class LMTP(SMTP):
-    if sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            host: str = "",
-            port: int = 2003,
-            local_hostname: str | None = None,
-            source_address: _SourceAddress | None = None,
-            timeout: float = ...,
-        ) -> None: ...
-    else:
-        def __init__(
-            self,
-            host: str = "",
-            port: int = 2003,
-            local_hostname: str | None = None,
-            source_address: _SourceAddress | None = None,
-        ) -> None: ...
+    def __init__(
+        self,
+        host: str = "",
+        port: int = 2003,
+        local_hostname: str | None = None,
+        source_address: _SourceAddress | None = None,
+        timeout: float = ...,
+    ) -> None: ...
diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi
index 1c996ac32278..1ee006235ee6 100644
--- a/mypy/typeshed/stdlib/socket.pyi
+++ b/mypy/typeshed/stdlib/socket.pyi
@@ -53,12 +53,18 @@ from _socket import (
     IPPROTO_TCP as IPPROTO_TCP,
     IPPROTO_UDP as IPPROTO_UDP,
     IPV6_CHECKSUM as IPV6_CHECKSUM,
+    IPV6_DONTFRAG as IPV6_DONTFRAG,
+    IPV6_HOPLIMIT as IPV6_HOPLIMIT,
+    IPV6_HOPOPTS as IPV6_HOPOPTS,
     IPV6_JOIN_GROUP as IPV6_JOIN_GROUP,
     IPV6_LEAVE_GROUP as IPV6_LEAVE_GROUP,
     IPV6_MULTICAST_HOPS as IPV6_MULTICAST_HOPS,
     IPV6_MULTICAST_IF as IPV6_MULTICAST_IF,
     IPV6_MULTICAST_LOOP as IPV6_MULTICAST_LOOP,
+    IPV6_PKTINFO as IPV6_PKTINFO,
+    IPV6_RECVRTHDR as IPV6_RECVRTHDR,
     IPV6_RECVTCLASS as IPV6_RECVTCLASS,
+    IPV6_RTHDR as IPV6_RTHDR,
     IPV6_TCLASS as IPV6_TCLASS,
     IPV6_UNICAST_HOPS as IPV6_UNICAST_HOPS,
     IPV6_V6ONLY as IPV6_V6ONLY,
@@ -195,12 +201,18 @@ __all__ = [
     "IPPROTO_TCP",
     "IPPROTO_UDP",
     "IPV6_CHECKSUM",
+    "IPV6_DONTFRAG",
+    "IPV6_HOPLIMIT",
+    "IPV6_HOPOPTS",
     "IPV6_JOIN_GROUP",
     "IPV6_LEAVE_GROUP",
     "IPV6_MULTICAST_HOPS",
     "IPV6_MULTICAST_IF",
     "IPV6_MULTICAST_LOOP",
+    "IPV6_PKTINFO",
+    "IPV6_RECVRTHDR",
     "IPV6_RECVTCLASS",
+    "IPV6_RTHDR",
     "IPV6_TCLASS",
     "IPV6_UNICAST_HOPS",
     "IPV6_V6ONLY",
@@ -335,18 +347,6 @@ if sys.platform == "win32":
         "MSG_MCAST",
     ]
 
-if sys.platform != "darwin" or sys.version_info >= (3, 9):
-    from _socket import (
-        IPV6_DONTFRAG as IPV6_DONTFRAG,
-        IPV6_HOPLIMIT as IPV6_HOPLIMIT,
-        IPV6_HOPOPTS as IPV6_HOPOPTS,
-        IPV6_PKTINFO as IPV6_PKTINFO,
-        IPV6_RECVRTHDR as IPV6_RECVRTHDR,
-        IPV6_RTHDR as IPV6_RTHDR,
-    )
-
-    __all__ += ["IPV6_DONTFRAG", "IPV6_HOPLIMIT", "IPV6_HOPOPTS", "IPV6_PKTINFO", "IPV6_RECVRTHDR", "IPV6_RTHDR"]
-
 if sys.platform == "darwin":
     from _socket import PF_SYSTEM as PF_SYSTEM, SYSPROTO_CONTROL as SYSPROTO_CONTROL
 
@@ -490,41 +490,39 @@ if sys.platform != "win32":
         "MSG_NOSIGNAL",
     ]
 
-    if sys.platform != "darwin" or sys.version_info >= (3, 9):
-        from _socket import (
-            IPV6_DSTOPTS as IPV6_DSTOPTS,
-            IPV6_NEXTHOP as IPV6_NEXTHOP,
-            IPV6_PATHMTU as IPV6_PATHMTU,
-            IPV6_RECVDSTOPTS as IPV6_RECVDSTOPTS,
-            IPV6_RECVHOPLIMIT as IPV6_RECVHOPLIMIT,
-            IPV6_RECVHOPOPTS as IPV6_RECVHOPOPTS,
-            IPV6_RECVPATHMTU as IPV6_RECVPATHMTU,
-            IPV6_RECVPKTINFO as IPV6_RECVPKTINFO,
-            IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS,
-        )
+    from _socket import (
+        IPV6_DSTOPTS as IPV6_DSTOPTS,
+        IPV6_NEXTHOP as IPV6_NEXTHOP,
+        IPV6_PATHMTU as IPV6_PATHMTU,
+        IPV6_RECVDSTOPTS as IPV6_RECVDSTOPTS,
+        IPV6_RECVHOPLIMIT as IPV6_RECVHOPLIMIT,
+        IPV6_RECVHOPOPTS as IPV6_RECVHOPOPTS,
+        IPV6_RECVPATHMTU as IPV6_RECVPATHMTU,
+        IPV6_RECVPKTINFO as IPV6_RECVPKTINFO,
+        IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS,
+    )
 
-        __all__ += [
-            "IPV6_DSTOPTS",
-            "IPV6_NEXTHOP",
-            "IPV6_PATHMTU",
-            "IPV6_RECVDSTOPTS",
-            "IPV6_RECVHOPLIMIT",
-            "IPV6_RECVHOPOPTS",
-            "IPV6_RECVPATHMTU",
-            "IPV6_RECVPKTINFO",
-            "IPV6_RTHDRDSTOPTS",
-        ]
+    __all__ += [
+        "IPV6_DSTOPTS",
+        "IPV6_NEXTHOP",
+        "IPV6_PATHMTU",
+        "IPV6_RECVDSTOPTS",
+        "IPV6_RECVHOPLIMIT",
+        "IPV6_RECVHOPOPTS",
+        "IPV6_RECVPATHMTU",
+        "IPV6_RECVPKTINFO",
+        "IPV6_RTHDRDSTOPTS",
+    ]
 
-    if sys.platform != "darwin":
+    if sys.platform != "darwin" or sys.version_info >= (3, 13):
         from _socket import SO_BINDTODEVICE as SO_BINDTODEVICE
 
         __all__ += ["SO_BINDTODEVICE"]
 
 if sys.platform != "darwin" and sys.platform != "linux":
-    if sys.platform != "win32" or sys.version_info >= (3, 9):
-        from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM
+    from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM
 
-        __all__ += ["BDADDR_ANY", "BDADDR_LOCAL", "BTPROTO_RFCOMM"]
+    __all__ += ["BDADDR_ANY", "BDADDR_LOCAL", "BTPROTO_RFCOMM"]
 
 if sys.platform == "darwin" and sys.version_info >= (3, 10):
     from _socket import TCP_KEEPALIVE as TCP_KEEPALIVE
@@ -777,7 +775,7 @@ if sys.platform == "linux":
 
         __all__ += ["CAN_RAW_ERR_FILTER"]
 
-if sys.platform == "linux" and sys.version_info >= (3, 9):
+if sys.platform == "linux":
     from _socket import (
         CAN_J1939 as CAN_J1939,
         CAN_RAW_JOIN_FILTERS as CAN_RAW_JOIN_FILTERS,
@@ -959,14 +957,13 @@ if sys.version_info >= (3, 12):
 
         __all__ += ["PF_DIVERT", "AF_DIVERT"]
 
-if sys.platform != "win32" and sys.version_info >= (3, 9):
+if sys.platform != "win32":
     __all__ += ["send_fds", "recv_fds"]
 
-if sys.platform != "win32" or sys.version_info >= (3, 9):
-    if sys.platform != "linux":
-        __all__ += ["AF_LINK"]
-    if sys.platform != "darwin" and sys.platform != "linux":
-        __all__ += ["AF_BLUETOOTH"]
+if sys.platform != "linux":
+    __all__ += ["AF_LINK"]
+if sys.platform != "darwin" and sys.platform != "linux":
+    __all__ += ["AF_BLUETOOTH"]
 
 if sys.platform == "win32" and sys.version_info >= (3, 12):
     __all__ += ["AF_HYPERV"]
@@ -980,6 +977,7 @@ if sys.platform != "win32" and sys.platform != "linux":
         IPPROTO_HELLO as IPPROTO_HELLO,
         IPPROTO_IPCOMP as IPPROTO_IPCOMP,
         IPPROTO_XTP as IPPROTO_XTP,
+        IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU,
         LOCAL_PEERCRED as LOCAL_PEERCRED,
         SCM_CREDS as SCM_CREDS,
     )
@@ -992,6 +990,7 @@ if sys.platform != "win32" and sys.platform != "linux":
         "IPPROTO_HELLO",
         "IPPROTO_IPCOMP",
         "IPPROTO_XTP",
+        "IPV6_USE_MIN_MTU",
         "LOCAL_PEERCRED",
         "SCM_CREDS",
         "AI_DEFAULT",
@@ -999,10 +998,6 @@ if sys.platform != "win32" and sys.platform != "linux":
         "AI_V4MAPPED_CFG",
         "MSG_EOF",
     ]
-    if sys.platform != "darwin" or sys.version_info >= (3, 9):
-        from _socket import IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU
-
-        __all__ += ["IPV6_USE_MIN_MTU"]
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
     from _socket import (
@@ -1028,6 +1023,39 @@ if sys.platform != "linux":
 
     __all__ += ["IPPROTO_GGP", "IPPROTO_IPV4", "IPPROTO_MAX", "IPPROTO_ND", "IP_RECVDSTADDR", "SO_USELOOPBACK"]
 
+if sys.version_info >= (3, 14):
+    from _socket import IP_RECVTTL as IP_RECVTTL
+
+    __all__ += ["IP_RECVTTL"]
+
+    if sys.platform == "win32" or sys.platform == "linux":
+        from _socket import IP_RECVERR as IP_RECVERR, IPV6_RECVERR as IPV6_RECVERR, SO_ORIGINAL_DST as SO_ORIGINAL_DST
+
+        __all__ += ["IP_RECVERR", "IPV6_RECVERR", "SO_ORIGINAL_DST"]
+
+    if sys.platform == "win32":
+        from _socket import (
+            SO_BTH_ENCRYPT as SO_BTH_ENCRYPT,
+            SO_BTH_MTU as SO_BTH_MTU,
+            SO_BTH_MTU_MAX as SO_BTH_MTU_MAX,
+            SO_BTH_MTU_MIN as SO_BTH_MTU_MIN,
+            SOL_RFCOMM as SOL_RFCOMM,
+            TCP_QUICKACK as TCP_QUICKACK,
+        )
+
+        __all__ += ["SOL_RFCOMM", "SO_BTH_ENCRYPT", "SO_BTH_MTU", "SO_BTH_MTU_MAX", "SO_BTH_MTU_MIN", "TCP_QUICKACK"]
+
+    if sys.platform == "linux":
+        from _socket import (
+            CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER,
+            IP_FREEBIND as IP_FREEBIND,
+            IP_RECVORIGDSTADDR as IP_RECVORIGDSTADDR,
+            SO_ORIGINAL_DST as SO_ORIGINAL_DST,
+            VMADDR_CID_LOCAL as VMADDR_CID_LOCAL,
+        )
+
+        __all__ += ["CAN_RAW_ERR_FILTER", "IP_FREEBIND", "IP_RECVORIGDSTADDR", "VMADDR_CID_LOCAL"]
+
 # Re-exported from errno
 EBADF: int
 EAGAIN: int
@@ -1084,11 +1112,10 @@ class AddressFamily(IntEnum):
         AF_NETLINK = 16
         AF_VSOCK = 40
         AF_QIPCRTR = 42
-    if sys.platform != "win32" or sys.version_info >= (3, 9):
-        if sys.platform != "linux":
-            AF_LINK = 33
-        if sys.platform != "darwin" and sys.platform != "linux":
-            AF_BLUETOOTH = 32
+    if sys.platform != "linux":
+        AF_LINK = 33
+    if sys.platform != "darwin" and sys.platform != "linux":
+        AF_BLUETOOTH = 32
     if sys.platform == "win32" and sys.version_info >= (3, 12):
         AF_HYPERV = 34
     if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12):
@@ -1140,12 +1167,10 @@ if sys.platform == "linux":
     AF_VSOCK = AddressFamily.AF_VSOCK
     AF_QIPCRTR = AddressFamily.AF_QIPCRTR
 
-if sys.platform != "win32" or sys.version_info >= (3, 9):
-    if sys.platform != "linux":
-        AF_LINK = AddressFamily.AF_LINK
-    if sys.platform != "darwin" and sys.platform != "linux":
-        AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH
-
+if sys.platform != "linux":
+    AF_LINK = AddressFamily.AF_LINK
+if sys.platform != "darwin" and sys.platform != "linux":
+    AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH
 if sys.platform == "win32" and sys.version_info >= (3, 12):
     AF_HYPERV = AddressFamily.AF_HYPERV
 if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12):
@@ -1352,11 +1377,10 @@ class socket(_socket.socket):
 def fromfd(fd: SupportsIndex, family: AddressFamily | int, type: SocketKind | int, proto: int = 0) -> socket: ...
 
 if sys.platform != "win32":
-    if sys.version_info >= (3, 9):
-        def send_fds(
-            sock: socket, buffers: Iterable[ReadableBuffer], fds: Iterable[int], flags: Unused = 0, address: Unused = None
-        ) -> int: ...
-        def recv_fds(sock: socket, bufsize: int, maxfds: int, flags: int = 0) -> tuple[bytes, list[int], int, Any]: ...
+    def send_fds(
+        sock: socket, buffers: Iterable[ReadableBuffer], fds: Iterable[int], flags: Unused = 0, address: Unused = None
+    ) -> int: ...
+    def recv_fds(sock: socket, bufsize: int, maxfds: int, flags: int = 0) -> tuple[bytes, list[int], int, Any]: ...
 
 if sys.platform == "win32":
     def fromshare(info: bytes) -> socket: ...
diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
index 724bc3166fd0..ab783dbde121 100644
--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
+++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
@@ -60,12 +60,14 @@ from sqlite3.dbapi2 import (
     sqlite_version as sqlite_version,
     sqlite_version_info as sqlite_version_info,
     threadsafety as threadsafety,
-    version_info as version_info,
 )
 from types import TracebackType
 from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
+if sys.version_info < (3, 14):
+    from sqlite3.dbapi2 import version_info as version_info
+
 if sys.version_info >= (3, 12):
     from sqlite3.dbapi2 import (
         LEGACY_TRANSACTION_CONTROL as LEGACY_TRANSACTION_CONTROL,
diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi
index 388e521c1ef5..9fbf5e8dfa84 100644
--- a/mypy/typeshed/stdlib/ssl.pyi
+++ b/mypy/typeshed/stdlib/ssl.pyi
@@ -28,7 +28,7 @@ from _ssl import (
 from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from collections.abc import Callable, Iterable
 from typing import Any, Literal, NamedTuple, TypedDict, overload, type_check_only
-from typing_extensions import Never, Self, TypeAlias
+from typing_extensions import Never, Self, TypeAlias, deprecated
 
 if sys.version_info >= (3, 13):
     from _ssl import HAS_PSK as HAS_PSK
@@ -369,7 +369,12 @@ class SSLSocket(socket.socket):
     def compression(self) -> str | None: ...
     def get_channel_binding(self, cb_type: str = "tls-unique") -> bytes | None: ...
     def selected_alpn_protocol(self) -> str | None: ...
-    def selected_npn_protocol(self) -> str | None: ...
+    if sys.version_info >= (3, 10):
+        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        def selected_npn_protocol(self) -> str | None: ...
+    else:
+        def selected_npn_protocol(self) -> str | None: ...
+
     def accept(self) -> tuple[SSLSocket, socket._RetAddress]: ...
     def unwrap(self) -> socket.socket: ...
     def version(self) -> str | None: ...
@@ -434,7 +439,12 @@ class SSLContext(_SSLContext):
     def set_default_verify_paths(self) -> None: ...
     def set_ciphers(self, cipherlist: str, /) -> None: ...
     def set_alpn_protocols(self, alpn_protocols: Iterable[str]) -> None: ...
-    def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ...
+    if sys.version_info >= (3, 10):
+        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ...
+    else:
+        def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ...
+
     def set_servername_callback(self, server_name_callback: _SrvnmeCbType | None) -> None: ...
     def load_dh_params(self, path: str, /) -> None: ...
     def set_ecdh_curve(self, name: str, /) -> None: ...
@@ -475,7 +485,12 @@ class SSLObject:
     @overload
     def getpeercert(self, binary_form: bool) -> _PeerCertRetType: ...
     def selected_alpn_protocol(self) -> str | None: ...
-    def selected_npn_protocol(self) -> str | None: ...
+    if sys.version_info >= (3, 10):
+        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        def selected_npn_protocol(self) -> str | None: ...
+    else:
+        def selected_npn_protocol(self) -> str | None: ...
+
     def cipher(self) -> tuple[str, str, int] | None: ...
     def shared_ciphers(self) -> list[tuple[str, str, int]] | None: ...
     def compression(self) -> str | None: ...
@@ -512,8 +527,6 @@ SSL_ERROR_ZERO_RETURN: SSLErrorNumber  # undocumented
 
 def get_protocol_name(protocol_code: int) -> str: ...
 
-if sys.version_info < (3, 9):
-    AF_INET: int
 PEM_FOOTER: str
 PEM_HEADER: str
 SOCK_STREAM: int
diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi
index 9418bdea9d6d..6d7d3fbb4956 100644
--- a/mypy/typeshed/stdlib/statistics.pyi
+++ b/mypy/typeshed/stdlib/statistics.pyi
@@ -98,9 +98,7 @@ class NormalDist:
     def inv_cdf(self, p: float) -> float: ...
     def overlap(self, other: NormalDist) -> float: ...
     def quantiles(self, n: int = 4) -> list[float]: ...
-    if sys.version_info >= (3, 9):
-        def zscore(self, x: float) -> float: ...
-
+    def zscore(self, x: float) -> float: ...
     def __eq__(x1, x2: object) -> bool: ...
     def __add__(x1, x2: float | NormalDist) -> NormalDist: ...
     def __sub__(x1, x2: float | NormalDist) -> NormalDist: ...
diff --git a/mypy/typeshed/stdlib/string.pyi b/mypy/typeshed/stdlib/string/__init__.pyi
similarity index 88%
rename from mypy/typeshed/stdlib/string.pyi
rename to mypy/typeshed/stdlib/string/__init__.pyi
index 35a76e9c8628..da752327d3f7 100644
--- a/mypy/typeshed/stdlib/string.pyi
+++ b/mypy/typeshed/stdlib/string/__init__.pyi
@@ -3,7 +3,7 @@ from _typeshed import StrOrLiteralStr
 from collections.abc import Iterable, Mapping, Sequence
 from re import Pattern, RegexFlag
 from typing import Any, ClassVar, overload
-from typing_extensions import LiteralString, TypeAlias
+from typing_extensions import LiteralString
 
 __all__ = [
     "ascii_letters",
@@ -32,14 +32,7 @@ whitespace: LiteralString
 
 def capwords(s: StrOrLiteralStr, sep: StrOrLiteralStr | None = None) -> StrOrLiteralStr: ...
 
-if sys.version_info >= (3, 9):
-    _TemplateMetaclass: TypeAlias = type
-else:
-    class _TemplateMetaclass(type):
-        pattern: ClassVar[str]
-        def __init__(cls, name: str, bases: tuple[type, ...], dct: dict[str, Any]) -> None: ...
-
-class Template(metaclass=_TemplateMetaclass):
+class Template(metaclass=type):
     template: str
     delimiter: ClassVar[str]
     idpattern: ClassVar[str]
diff --git a/mypy/typeshed/stdlib/string/templatelib.pyi b/mypy/typeshed/stdlib/string/templatelib.pyi
new file mode 100644
index 000000000000..01b95377a49c
--- /dev/null
+++ b/mypy/typeshed/stdlib/string/templatelib.pyi
@@ -0,0 +1,28 @@
+from collections.abc import Iterator
+from typing import Any, Literal, final
+
+__all__ = ["Interpolation", "Template"]
+
+@final
+class Template:  # TODO: consider making `Template` generic on `TypeVarTuple`
+    strings: tuple[str, ...]
+    interpolations: tuple[Interpolation, ...]
+
+    def __new__(cls, *args: str | Interpolation) -> Template: ...
+    def __iter__(self) -> Iterator[str | Interpolation]: ...
+    def __add__(self, other: Template | str) -> Template: ...
+    @property
+    def values(self) -> tuple[Any, ...]: ...  # Tuple of interpolation values, which can have any type
+
+@final
+class Interpolation:
+    value: Any  # TODO: consider making `Interpolation` generic in runtime
+    expression: str
+    conversion: Literal["a", "r", "s"] | None
+    format_spec: str
+
+    __match_args__ = ("value", "expression", "conversion", "format_spec")
+
+    def __new__(
+        cls, value: Any, expression: str, conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
+    ) -> Interpolation: ...
diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi
index fef35b56945a..8b72e2ec7ae2 100644
--- a/mypy/typeshed/stdlib/subprocess.pyi
+++ b/mypy/typeshed/stdlib/subprocess.pyi
@@ -1,13 +1,10 @@
 import sys
 from _typeshed import MaybeNone, ReadableBuffer, StrOrBytesPath
 from collections.abc import Callable, Collection, Iterable, Mapping, Sequence
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import IO, Any, AnyStr, Final, Generic, Literal, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "Popen",
     "PIPE",
@@ -87,8 +84,7 @@ class CompletedProcess(Generic[_T]):
     stderr: _T
     def __init__(self, args: _CMD, returncode: int, stdout: _T | None = None, stderr: _T | None = None) -> None: ...
     def check_returncode(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 if sys.version_info >= (3, 11):
     # 3.11 adds "process_group" argument
@@ -500,7 +496,7 @@ elif sys.version_info >= (3, 10):
         pipesize: int = -1,
     ) -> CompletedProcess[Any]: ...
 
-elif sys.version_info >= (3, 9):
+else:
     # 3.9 adds arguments "user", "group", "extra_groups" and "umask"
     @overload
     def run(
@@ -696,177 +692,6 @@ elif sys.version_info >= (3, 9):
         umask: int = -1,
     ) -> CompletedProcess[Any]: ...
 
-else:
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: str | None = None,
-        errors: str | None = None,
-        input: str | None = None,
-        text: Literal[True],
-        timeout: float | None = None,
-    ) -> CompletedProcess[str]: ...
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: str,
-        errors: str | None = None,
-        input: str | None = None,
-        text: bool | None = None,
-        timeout: float | None = None,
-    ) -> CompletedProcess[str]: ...
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: str | None = None,
-        errors: str,
-        input: str | None = None,
-        text: bool | None = None,
-        timeout: float | None = None,
-    ) -> CompletedProcess[str]: ...
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        *,
-        universal_newlines: Literal[True],
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        # where the *real* keyword only args start
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: str | None = None,
-        errors: str | None = None,
-        input: str | None = None,
-        text: bool | None = None,
-        timeout: float | None = None,
-    ) -> CompletedProcess[str]: ...
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: Literal[False] | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: None = None,
-        errors: None = None,
-        input: ReadableBuffer | None = None,
-        text: Literal[False] | None = None,
-        timeout: float | None = None,
-    ) -> CompletedProcess[bytes]: ...
-    @overload
-    def run(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        capture_output: bool = False,
-        check: bool = False,
-        encoding: str | None = None,
-        errors: str | None = None,
-        input: _InputString | None = None,
-        text: bool | None = None,
-        timeout: float | None = None,
-    ) -> CompletedProcess[Any]: ...
-
 # Same args as Popen.__init__
 if sys.version_info >= (3, 11):
     # 3.11 adds "process_group" argument
@@ -931,8 +756,7 @@ elif sys.version_info >= (3, 10):
         pipesize: int = -1,
     ) -> int: ...
 
-elif sys.version_info >= (3, 9):
-    # 3.9 adds arguments "user", "group", "extra_groups" and "umask"
+else:
     def call(
         args: _CMD,
         bufsize: int = -1,
@@ -961,31 +785,6 @@ elif sys.version_info >= (3, 9):
         umask: int = -1,
     ) -> int: ...
 
-else:
-    def call(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        encoding: str | None = None,
-        timeout: float | None = None,
-        text: bool | None = None,
-    ) -> int: ...
-
 # Same args as Popen.__init__
 if sys.version_info >= (3, 11):
     # 3.11 adds "process_group" argument
@@ -1050,8 +849,7 @@ elif sys.version_info >= (3, 10):
         pipesize: int = -1,
     ) -> int: ...
 
-elif sys.version_info >= (3, 9):
-    # 3.9 adds arguments "user", "group", "extra_groups" and "umask"
+else:
     def check_call(
         args: _CMD,
         bufsize: int = -1,
@@ -1080,31 +878,6 @@ elif sys.version_info >= (3, 9):
         umask: int = -1,
     ) -> int: ...
 
-else:
-    def check_call(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stdout: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        timeout: float | None = ...,
-        *,
-        encoding: str | None = None,
-        text: bool | None = None,
-    ) -> int: ...
-
 if sys.version_info >= (3, 11):
     # 3.11 adds "process_group" argument
     @overload
@@ -1479,8 +1252,7 @@ elif sys.version_info >= (3, 10):
         pipesize: int = -1,
     ) -> Any: ...  # morally: -> str | bytes
 
-elif sys.version_info >= (3, 9):
-    # 3.9 adds arguments "user", "group", "extra_groups" and "umask"
+else:
     @overload
     def check_output(
         args: _CMD,
@@ -1657,159 +1429,6 @@ elif sys.version_info >= (3, 9):
         umask: int = -1,
     ) -> Any: ...  # morally: -> str | bytes
 
-else:
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: str | None = None,
-        errors: str | None = None,
-        text: Literal[True],
-    ) -> str: ...
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: str,
-        errors: str | None = None,
-        text: bool | None = None,
-    ) -> str: ...
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: str | None = None,
-        errors: str,
-        text: bool | None = None,
-    ) -> str: ...
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        *,
-        universal_newlines: Literal[True],
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        # where the real keyword only ones start
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: str | None = None,
-        errors: str | None = None,
-        text: bool | None = None,
-    ) -> str: ...
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: Literal[False] | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: None = None,
-        errors: None = None,
-        text: Literal[False] | None = None,
-    ) -> bytes: ...
-    @overload
-    def check_output(
-        args: _CMD,
-        bufsize: int = -1,
-        executable: StrOrBytesPath | None = None,
-        stdin: _FILE = None,
-        stderr: _FILE = None,
-        preexec_fn: Callable[[], Any] | None = None,
-        close_fds: bool = True,
-        shell: bool = False,
-        cwd: StrOrBytesPath | None = None,
-        env: _ENV | None = None,
-        universal_newlines: bool | None = None,
-        startupinfo: Any = None,
-        creationflags: int = 0,
-        restore_signals: bool = True,
-        start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        *,
-        timeout: float | None = None,
-        input: _InputString | None = ...,
-        encoding: str | None = None,
-        errors: str | None = None,
-        text: bool | None = None,
-    ) -> Any: ...  # morally: -> str | bytes
-
 PIPE: Final[int]
 STDOUT: Final[int]
 DEVNULL: Final[int]
@@ -2223,8 +1842,7 @@ class Popen(Generic[AnyStr]):
             umask: int = -1,
             pipesize: int = -1,
         ) -> None: ...
-    elif sys.version_info >= (3, 9):
-        # user, group, extra_groups, umask were added in 3.9
+    else:
         @overload
         def __init__(
             self: Popen[str],
@@ -2400,163 +2018,11 @@ class Popen(Generic[AnyStr]):
             extra_groups: Iterable[str | int] | None = None,
             umask: int = -1,
         ) -> None: ...
-    else:
-        @overload
-        def __init__(
-            self: Popen[str],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            universal_newlines: bool | None = None,
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            *,
-            text: bool | None = None,
-            encoding: str,
-            errors: str | None = None,
-        ) -> None: ...
-        @overload
-        def __init__(
-            self: Popen[str],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            universal_newlines: bool | None = None,
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            *,
-            text: bool | None = None,
-            encoding: str | None = None,
-            errors: str,
-        ) -> None: ...
-        @overload
-        def __init__(
-            self: Popen[str],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            *,
-            universal_newlines: Literal[True],
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            # where the *real* keyword only args start
-            text: bool | None = None,
-            encoding: str | None = None,
-            errors: str | None = None,
-        ) -> None: ...
-        @overload
-        def __init__(
-            self: Popen[str],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            universal_newlines: bool | None = None,
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            *,
-            text: Literal[True],
-            encoding: str | None = None,
-            errors: str | None = None,
-        ) -> None: ...
-        @overload
-        def __init__(
-            self: Popen[bytes],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            universal_newlines: Literal[False] | None = None,
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            *,
-            text: Literal[False] | None = None,
-            encoding: None = None,
-            errors: None = None,
-        ) -> None: ...
-        @overload
-        def __init__(
-            self: Popen[Any],
-            args: _CMD,
-            bufsize: int = -1,
-            executable: StrOrBytesPath | None = None,
-            stdin: _FILE | None = None,
-            stdout: _FILE | None = None,
-            stderr: _FILE | None = None,
-            preexec_fn: Callable[[], Any] | None = None,
-            close_fds: bool = True,
-            shell: bool = False,
-            cwd: StrOrBytesPath | None = None,
-            env: _ENV | None = None,
-            universal_newlines: bool | None = None,
-            startupinfo: Any | None = None,
-            creationflags: int = 0,
-            restore_signals: bool = True,
-            start_new_session: bool = False,
-            pass_fds: Collection[int] = (),
-            *,
-            text: bool | None = None,
-            encoding: str | None = None,
-            errors: str | None = None,
-        ) -> None: ...
 
     def poll(self) -> int | None: ...
     def wait(self, timeout: float | None = None) -> int: ...
     # morally the members of the returned tuple should be optional
-    # TODO this should allow ReadableBuffer for Popen[bytes], but adding
+    # TODO: this should allow ReadableBuffer for Popen[bytes], but adding
     # overloads for that runs into a mypy bug (python/mypy#14070).
     def communicate(self, input: AnyStr | None = None, timeout: float | None = None) -> tuple[AnyStr, AnyStr]: ...
     def send_signal(self, sig: int) -> None: ...
@@ -2567,8 +2033,7 @@ class Popen(Generic[AnyStr]):
         self, exc_type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
     ) -> None: ...
     def __del__(self) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # The result really is always a str.
 if sys.version_info >= (3, 11):
diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi
index 9b051e82b64b..d81645cb5687 100644
--- a/mypy/typeshed/stdlib/sunau.pyi
+++ b/mypy/typeshed/stdlib/sunau.pyi
@@ -1,4 +1,3 @@
-import sys
 from _typeshed import Unused
 from typing import IO, Any, Literal, NamedTuple, NoReturn, overload
 from typing_extensions import Self, TypeAlias
@@ -81,6 +80,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Au_read: ...
 def open(f: _File, mode: Literal["w", "wb"]) -> Au_write: ...
 @overload
 def open(f: _File, mode: str | None = None) -> Any: ...
-
-if sys.version_info < (3, 9):
-    openfp = open
diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi
index ee0a1eb2f1cb..d5f2be04b600 100644
--- a/mypy/typeshed/stdlib/symtable.pyi
+++ b/mypy/typeshed/stdlib/symtable.pyi
@@ -36,9 +36,6 @@ class SymbolTable:
     def is_optimized(self) -> bool: ...
     def is_nested(self) -> bool: ...
     def has_children(self) -> bool: ...
-    if sys.version_info < (3, 9):
-        def has_exec(self) -> bool: ...
-
     def get_identifiers(self) -> dict_keys[str, int]: ...
     def lookup(self, name: str) -> Symbol: ...
     def get_symbols(self) -> list[Symbol]: ...
@@ -52,9 +49,8 @@ class Function(SymbolTable):
     def get_nonlocals(self) -> tuple[str, ...]: ...
 
 class Class(SymbolTable):
-    if sys.version_info < (3, 16):
-        @deprecated("deprecated in Python 3.14, will be removed in Python 3.16")
-        def get_methods(self) -> tuple[str, ...]: ...
+    @deprecated("deprecated in Python 3.14, will be removed in Python 3.16")
+    def get_methods(self) -> tuple[str, ...]: ...
 
 class Symbol:
     def __init__(
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index 4aa1699e8b42..ce06551f975a 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -1,5 +1,5 @@
 import sys
-from _typeshed import MaybeNone, OptExcInfo, ProfileFunction, TraceFunction, structseq
+from _typeshed import MaybeNone, OptExcInfo, ProfileFunction, StrOrBytesPath, TraceFunction, structseq
 from _typeshed.importlib import MetaPathFinderProtocol, PathEntryFinderProtocol
 from builtins import object as _object
 from collections.abc import AsyncGenerator, Callable, Sequence
@@ -46,8 +46,7 @@ path: list[str]
 path_hooks: list[Callable[[str], PathEntryFinderProtocol]]
 path_importer_cache: dict[str, PathEntryFinderProtocol | None]
 platform: LiteralString
-if sys.version_info >= (3, 9):
-    platlibdir: str
+platlibdir: str
 prefix: str
 pycache_prefix: str | None
 ps1: object
@@ -97,7 +96,7 @@ flags: _flags
 # This can be re-visited when typeshed drops support for 3.10,
 # at which point all supported versions will include int_max_str_digits
 # in all patch versions.
-# 3.8 and 3.9 are 15 or 16-tuple
+# 3.9 is 15 or 16-tuple
 # 3.10 is 16 or 17-tuple
 # 3.11+ is an 18-tuple.
 @final
@@ -185,7 +184,7 @@ class _flags(_UninstantiableStructseq, tuple[int, ...]):
     # Whether or not this exists on lower versions of Python
     # may depend on which patch release you're using
     # (it was backported to all Python versions on 3.8+ as a security fix)
-    # Added in: 3.8.14, 3.9.14, 3.10.7
+    # Added in: 3.9.14, 3.10.7
     # and present in all versions of 3.11 and later.
     @property
     def int_max_str_digits(self) -> int: ...
@@ -397,6 +396,7 @@ def intern(string: str, /) -> str: ...
 if sys.version_info >= (3, 13):
     def _is_gil_enabled() -> bool: ...
     def _clear_internal_caches() -> None: ...
+    def _is_interned(string: str, /) -> bool: ...
 
 def is_finalizing() -> bool: ...
 def breakpointhook(*args: Any, **kwargs: Any) -> Any: ...
@@ -410,14 +410,6 @@ def setrecursionlimit(limit: int, /) -> None: ...
 def setswitchinterval(interval: float, /) -> None: ...
 def gettotalrefcount() -> int: ...  # Debug builds only
 
-if sys.version_info < (3, 9):
-    def getcheckinterval() -> int: ...  # deprecated
-    def setcheckinterval(n: int, /) -> None: ...  # deprecated
-
-if sys.version_info < (3, 9):
-    # An 11-tuple or None
-    def callstats() -> tuple[int, int, int, int, int, int, int, int, int, int, int] | None: ...
-
 # Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily.
 @type_check_only
 class UnraisableHookArgs(Protocol):
@@ -456,7 +448,7 @@ if sys.platform == "win32":
 def get_coroutine_origin_tracking_depth() -> int: ...
 def set_coroutine_origin_tracking_depth(depth: int) -> None: ...
 
-# The following two functions were added in 3.11.0, 3.10.7, 3.9.14, and 3.8.14,
+# The following two functions were added in 3.11.0, 3.10.7, and 3.9.14,
 # as part of the response to CVE-2020-10735
 def set_int_max_str_digits(maxdigits: int) -> None: ...
 def get_int_max_str_digits() -> int: ...
@@ -478,3 +470,7 @@ if sys.version_info >= (3, 12):
     from . import _monitoring
 
     monitoring = _monitoring
+
+if sys.version_info >= (3, 14):
+    def is_remote_debug_enabled() -> bool: ...
+    def remote_exec(pid: int, script: StrOrBytesPath) -> None: ...
diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi
index 6a00e070aee9..31094f87872d 100644
--- a/mypy/typeshed/stdlib/tarfile.pyi
+++ b/mypy/typeshed/stdlib/tarfile.pyi
@@ -7,7 +7,7 @@ from collections.abc import Callable, Iterable, Iterator, Mapping
 from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj
 from types import TracebackType
 from typing import IO, ClassVar, Literal, Protocol, overload
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, deprecated
 
 __all__ = [
     "TarFile",
@@ -304,6 +304,25 @@ class TarFile:
     ) -> Self: ...
     @overload
     @classmethod
+    def open(
+        cls,
+        name: StrOrBytesPath | ReadableBuffer | None,
+        mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"],
+        fileobj: _Fileobj | None = None,
+        bufsize: int = 10240,
+        *,
+        format: int | None = ...,
+        tarinfo: type[TarInfo] | None = ...,
+        dereference: bool | None = ...,
+        ignore_zeros: bool | None = ...,
+        encoding: str | None = ...,
+        errors: str = ...,
+        pax_headers: Mapping[str, str] | None = ...,
+        debug: int | None = ...,
+        errorlevel: int | None = ...,
+    ) -> Self: ...
+    @overload
+    @classmethod
     def open(
         cls,
         name: StrOrBytesPath | ReadableBuffer | None = None,
@@ -323,6 +342,25 @@ class TarFile:
     ) -> Self: ...
     @overload
     @classmethod
+    def open(
+        cls,
+        name: StrOrBytesPath | WriteableBuffer | None,
+        mode: Literal["w|", "w|xz"],
+        fileobj: _Fileobj | None = None,
+        bufsize: int = 10240,
+        *,
+        format: int | None = ...,
+        tarinfo: type[TarInfo] | None = ...,
+        dereference: bool | None = ...,
+        ignore_zeros: bool | None = ...,
+        encoding: str | None = ...,
+        errors: str = ...,
+        pax_headers: Mapping[str, str] | None = ...,
+        debug: int | None = ...,
+        errorlevel: int | None = ...,
+    ) -> Self: ...
+    @overload
+    @classmethod
     def open(
         cls,
         name: StrOrBytesPath | WriteableBuffer | None = None,
@@ -342,6 +380,26 @@ class TarFile:
     ) -> Self: ...
     @overload
     @classmethod
+    def open(
+        cls,
+        name: StrOrBytesPath | WriteableBuffer | None,
+        mode: Literal["w|gz", "w|bz2"],
+        fileobj: _Fileobj | None = None,
+        bufsize: int = 10240,
+        *,
+        format: int | None = ...,
+        tarinfo: type[TarInfo] | None = ...,
+        dereference: bool | None = ...,
+        ignore_zeros: bool | None = ...,
+        encoding: str | None = ...,
+        errors: str = ...,
+        pax_headers: Mapping[str, str] | None = ...,
+        debug: int | None = ...,
+        errorlevel: int | None = ...,
+        compresslevel: int = 9,
+    ) -> Self: ...
+    @overload
+    @classmethod
     def open(
         cls,
         name: StrOrBytesPath | WriteableBuffer | None = None,
@@ -520,11 +578,7 @@ class TarFile:
 
 open = TarFile.open
 
-if sys.version_info >= (3, 9):
-    def is_tarfile(name: StrOrBytesPath | IO[bytes]) -> bool: ...
-
-else:
-    def is_tarfile(name: StrOrBytesPath) -> bool: ...
+def is_tarfile(name: StrOrBytesPath | IO[bytes]) -> bool: ...
 
 class TarError(Exception): ...
 class ReadError(TarError): ...
@@ -568,7 +622,6 @@ class TarInfo:
     offset: int
     offset_data: int
     sparse: bytes | None
-    tarfile: TarFile | None
     mode: int
     type: bytes
     linkname: str
@@ -578,6 +631,16 @@ class TarInfo:
     gname: str
     pax_headers: Mapping[str, str]
     def __init__(self, name: str = "") -> None: ...
+    if sys.version_info >= (3, 13):
+        @property
+        @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.16")
+        def tarfile(self) -> TarFile | None: ...
+        @tarfile.setter
+        @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.16")
+        def tarfile(self, tarfile: TarFile | None) -> None: ...
+    else:
+        tarfile: TarFile | None
+
     @classmethod
     def frombuf(cls, buf: bytes | bytearray, encoding: str, errors: str) -> Self: ...
     @classmethod
diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi
index d2677603bc47..ea6e057e410d 100644
--- a/mypy/typeshed/stdlib/tempfile.pyi
+++ b/mypy/typeshed/stdlib/tempfile.pyi
@@ -13,13 +13,10 @@ from _typeshed import (
     WriteableBuffer,
 )
 from collections.abc import Iterable, Iterator
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import IO, Any, AnyStr, Generic, Literal, overload
 from typing_extensions import Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "NamedTemporaryFile",
     "TemporaryFile",
@@ -387,7 +384,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase):
     def write(self: SpooledTemporaryFile[bytes], s: ReadableBuffer) -> int: ...
     @overload
     def write(self, s: AnyStr) -> int: ...
-    @overload  #  type: ignore[override]
+    @overload  # type: ignore[override]
     def writelines(self: SpooledTemporaryFile[str], iterable: Iterable[str]) -> None: ...
     @overload
     def writelines(self: SpooledTemporaryFile[bytes], iterable: Iterable[ReadableBuffer]) -> None: ...
@@ -399,8 +396,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase):
     def seekable(self) -> bool: ...
     def writable(self) -> bool: ...
     def __next__(self) -> AnyStr: ...  # type: ignore[override]
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class TemporaryDirectory(Generic[AnyStr]):
     name: AnyStr
@@ -458,8 +454,7 @@ class TemporaryDirectory(Generic[AnyStr]):
     def cleanup(self) -> None: ...
     def __enter__(self) -> AnyStr: ...
     def __exit__(self, exc: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 # The overloads overlap, but they should still work fine.
 @overload
diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi
index efeea69d0234..d31351754d05 100644
--- a/mypy/typeshed/stdlib/threading.pyi
+++ b/mypy/typeshed/stdlib/threading.pyi
@@ -3,8 +3,10 @@ import sys
 from _thread import _excepthook, _ExceptHookArgs, get_native_id as get_native_id
 from _typeshed import ProfileFunction, TraceFunction
 from collections.abc import Callable, Iterable, Mapping
+from contextvars import ContextVar
 from types import TracebackType
 from typing import Any, TypeVar, final
+from typing_extensions import deprecated
 
 _T = TypeVar("_T")
 
@@ -44,9 +46,11 @@ if sys.version_info >= (3, 12):
 _profile_hook: ProfileFunction | None
 
 def active_count() -> int: ...
-def activeCount() -> int: ...  # deprecated alias for active_count()
+@deprecated("Use active_count() instead")
+def activeCount() -> int: ...
 def current_thread() -> Thread: ...
-def currentThread() -> Thread: ...  # deprecated alias for current_thread()
+@deprecated("Use current_thread() instead")
+def currentThread() -> Thread: ...
 def get_ident() -> int: ...
 def enumerate() -> list[Thread]: ...
 def main_thread() -> Thread: ...
@@ -73,29 +77,44 @@ class Thread:
     @property
     def ident(self) -> int | None: ...
     daemon: bool
-    def __init__(
-        self,
-        group: None = None,
-        target: Callable[..., object] | None = None,
-        name: str | None = None,
-        args: Iterable[Any] = (),
-        kwargs: Mapping[str, Any] | None = None,
-        *,
-        daemon: bool | None = None,
-    ) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(
+            self,
+            group: None = None,
+            target: Callable[..., object] | None = None,
+            name: str | None = None,
+            args: Iterable[Any] = (),
+            kwargs: Mapping[str, Any] | None = None,
+            *,
+            daemon: bool | None = None,
+            context: ContextVar[Any] | None = None,
+        ) -> None: ...
+    else:
+        def __init__(
+            self,
+            group: None = None,
+            target: Callable[..., object] | None = None,
+            name: str | None = None,
+            args: Iterable[Any] = (),
+            kwargs: Mapping[str, Any] | None = None,
+            *,
+            daemon: bool | None = None,
+        ) -> None: ...
+
     def start(self) -> None: ...
     def run(self) -> None: ...
     def join(self, timeout: float | None = None) -> None: ...
     @property
     def native_id(self) -> int | None: ...  # only available on some platforms
     def is_alive(self) -> bool: ...
-    if sys.version_info < (3, 9):
-        def isAlive(self) -> bool: ...
-    # the following methods are all deprecated
-    def getName(self) -> str: ...
-    def setName(self, name: str) -> None: ...
+    @deprecated("Get the daemon attribute instead")
     def isDaemon(self) -> bool: ...
+    @deprecated("Set the daemon attribute instead")
     def setDaemon(self, daemonic: bool) -> None: ...
+    @deprecated("Use the name attribute instead")
+    def getName(self) -> str: ...
+    @deprecated("Use the name attribute instead")
+    def setName(self, name: str) -> None: ...
 
 class _DummyThread(Thread):
     def __init__(self) -> None: ...
@@ -112,6 +131,9 @@ class _RLock:
     __enter__ = acquire
     def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
 
+    if sys.version_info >= (3, 14):
+        def locked(self) -> bool: ...
+
 RLock = _thread.RLock  # Actually a function at runtime.
 
 class Condition:
@@ -126,7 +148,8 @@ class Condition:
     def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ...
     def notify(self, n: int = 1) -> None: ...
     def notify_all(self) -> None: ...
-    def notifyAll(self) -> None: ...  # deprecated alias for notify_all()
+    @deprecated("Use notify_all() instead")
+    def notifyAll(self) -> None: ...
 
 class Semaphore:
     _value: int
@@ -134,16 +157,14 @@ class Semaphore:
     def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
     def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
     def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def release(self, n: int = 1) -> None: ...
-    else:
-        def release(self) -> None: ...
+    def release(self, n: int = 1) -> None: ...
 
 class BoundedSemaphore(Semaphore): ...
 
 class Event:
     def is_set(self) -> bool: ...
-    def isSet(self) -> bool: ...  # deprecated alias for is_set()
+    @deprecated("Use is_set() instead")
+    def isSet(self) -> bool: ...
     def set(self) -> None: ...
     def clear(self) -> None: ...
     def wait(self, timeout: float | None = None) -> bool: ...
diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi
index 71cdc4d78fdc..6d2538ea7e3e 100644
--- a/mypy/typeshed/stdlib/time.pyi
+++ b/mypy/typeshed/stdlib/time.pyi
@@ -31,7 +31,7 @@ if sys.platform == "darwin":
         CLOCK_UPTIME_RAW_APPROX: int
         CLOCK_MONOTONIC_RAW_APPROX: int
 
-if sys.version_info >= (3, 9) and sys.platform == "linux":
+if sys.platform == "linux":
     CLOCK_TAI: int
 
 # Constructor takes an iterable of any type, of length between 9 and 11 elements.
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index 73c1e0400fe8..c153ca499898 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -13,140 +13,139 @@ if sys.version_info >= (3, 11):
 else:
     from enum import Enum
 
-if sys.version_info >= (3, 9):
-    __all__ = [
-        "TclError",
-        "NO",
-        "FALSE",
-        "OFF",
-        "YES",
-        "TRUE",
-        "ON",
-        "N",
-        "S",
-        "W",
-        "E",
-        "NW",
-        "SW",
-        "NE",
-        "SE",
-        "NS",
-        "EW",
-        "NSEW",
-        "CENTER",
-        "NONE",
-        "X",
-        "Y",
-        "BOTH",
-        "LEFT",
-        "TOP",
-        "RIGHT",
-        "BOTTOM",
-        "RAISED",
-        "SUNKEN",
-        "FLAT",
-        "RIDGE",
-        "GROOVE",
-        "SOLID",
-        "HORIZONTAL",
-        "VERTICAL",
-        "NUMERIC",
-        "CHAR",
-        "WORD",
-        "BASELINE",
-        "INSIDE",
-        "OUTSIDE",
-        "SEL",
-        "SEL_FIRST",
-        "SEL_LAST",
-        "END",
-        "INSERT",
-        "CURRENT",
-        "ANCHOR",
-        "ALL",
-        "NORMAL",
-        "DISABLED",
-        "ACTIVE",
-        "HIDDEN",
-        "CASCADE",
-        "CHECKBUTTON",
-        "COMMAND",
-        "RADIOBUTTON",
-        "SEPARATOR",
-        "SINGLE",
-        "BROWSE",
-        "MULTIPLE",
-        "EXTENDED",
-        "DOTBOX",
-        "UNDERLINE",
-        "PIESLICE",
-        "CHORD",
-        "ARC",
-        "FIRST",
-        "LAST",
-        "BUTT",
-        "PROJECTING",
-        "ROUND",
-        "BEVEL",
-        "MITER",
-        "MOVETO",
-        "SCROLL",
-        "UNITS",
-        "PAGES",
-        "TkVersion",
-        "TclVersion",
-        "READABLE",
-        "WRITABLE",
-        "EXCEPTION",
-        "EventType",
-        "Event",
-        "NoDefaultRoot",
-        "Variable",
-        "StringVar",
-        "IntVar",
-        "DoubleVar",
-        "BooleanVar",
-        "mainloop",
-        "getint",
-        "getdouble",
-        "getboolean",
-        "Misc",
-        "CallWrapper",
-        "XView",
-        "YView",
-        "Wm",
-        "Tk",
-        "Tcl",
-        "Pack",
-        "Place",
-        "Grid",
-        "BaseWidget",
-        "Widget",
-        "Toplevel",
-        "Button",
-        "Canvas",
-        "Checkbutton",
-        "Entry",
-        "Frame",
-        "Label",
-        "Listbox",
-        "Menu",
-        "Menubutton",
-        "Message",
-        "Radiobutton",
-        "Scale",
-        "Scrollbar",
-        "Text",
-        "OptionMenu",
-        "Image",
-        "PhotoImage",
-        "BitmapImage",
-        "image_names",
-        "image_types",
-        "Spinbox",
-        "LabelFrame",
-        "PanedWindow",
-    ]
+__all__ = [
+    "TclError",
+    "NO",
+    "FALSE",
+    "OFF",
+    "YES",
+    "TRUE",
+    "ON",
+    "N",
+    "S",
+    "W",
+    "E",
+    "NW",
+    "SW",
+    "NE",
+    "SE",
+    "NS",
+    "EW",
+    "NSEW",
+    "CENTER",
+    "NONE",
+    "X",
+    "Y",
+    "BOTH",
+    "LEFT",
+    "TOP",
+    "RIGHT",
+    "BOTTOM",
+    "RAISED",
+    "SUNKEN",
+    "FLAT",
+    "RIDGE",
+    "GROOVE",
+    "SOLID",
+    "HORIZONTAL",
+    "VERTICAL",
+    "NUMERIC",
+    "CHAR",
+    "WORD",
+    "BASELINE",
+    "INSIDE",
+    "OUTSIDE",
+    "SEL",
+    "SEL_FIRST",
+    "SEL_LAST",
+    "END",
+    "INSERT",
+    "CURRENT",
+    "ANCHOR",
+    "ALL",
+    "NORMAL",
+    "DISABLED",
+    "ACTIVE",
+    "HIDDEN",
+    "CASCADE",
+    "CHECKBUTTON",
+    "COMMAND",
+    "RADIOBUTTON",
+    "SEPARATOR",
+    "SINGLE",
+    "BROWSE",
+    "MULTIPLE",
+    "EXTENDED",
+    "DOTBOX",
+    "UNDERLINE",
+    "PIESLICE",
+    "CHORD",
+    "ARC",
+    "FIRST",
+    "LAST",
+    "BUTT",
+    "PROJECTING",
+    "ROUND",
+    "BEVEL",
+    "MITER",
+    "MOVETO",
+    "SCROLL",
+    "UNITS",
+    "PAGES",
+    "TkVersion",
+    "TclVersion",
+    "READABLE",
+    "WRITABLE",
+    "EXCEPTION",
+    "EventType",
+    "Event",
+    "NoDefaultRoot",
+    "Variable",
+    "StringVar",
+    "IntVar",
+    "DoubleVar",
+    "BooleanVar",
+    "mainloop",
+    "getint",
+    "getdouble",
+    "getboolean",
+    "Misc",
+    "CallWrapper",
+    "XView",
+    "YView",
+    "Wm",
+    "Tk",
+    "Tcl",
+    "Pack",
+    "Place",
+    "Grid",
+    "BaseWidget",
+    "Widget",
+    "Toplevel",
+    "Button",
+    "Canvas",
+    "Checkbutton",
+    "Entry",
+    "Frame",
+    "Label",
+    "Listbox",
+    "Menu",
+    "Menubutton",
+    "Message",
+    "Radiobutton",
+    "Scale",
+    "Scrollbar",
+    "Text",
+    "OptionMenu",
+    "Image",
+    "PhotoImage",
+    "BitmapImage",
+    "image_names",
+    "image_types",
+    "Spinbox",
+    "LabelFrame",
+    "PanedWindow",
+]
 
 # Using anything from tkinter.font in this file means that 'import tkinter'
 # seems to also load tkinter.font. That's not how it actually works, but
@@ -287,7 +286,7 @@ else:
 
 _W = TypeVar("_W", bound=Misc)
 # Events considered covariant because you should never assign to event.widget.
-_W_co = TypeVar("_W_co", covariant=True, bound=Misc)
+_W_co = TypeVar("_W_co", covariant=True, bound=Misc, default=Misc)
 
 class Event(Generic[_W_co]):
     serial: int
@@ -313,7 +312,7 @@ class Event(Generic[_W_co]):
 def NoDefaultRoot() -> None: ...
 
 class Variable:
-    def __init__(self, master: Misc | None = None, value: Incomplete | None = None, name: str | None = None) -> None: ...
+    def __init__(self, master: Misc | None = None, value=None, name: str | None = None) -> None: ...
     def set(self, value) -> None: ...
     initialize = set
     def get(self): ...
@@ -380,7 +379,7 @@ class Misc:
     children: dict[str, Widget]
     def destroy(self) -> None: ...
     def deletecommand(self, name: str) -> None: ...
-    def tk_strictMotif(self, boolean: Incomplete | None = None): ...
+    def tk_strictMotif(self, boolean=None): ...
     def tk_bisque(self) -> None: ...
     def tk_setPalette(self, *args, **kw) -> None: ...
     def wait_variable(self, name: str | Variable = "PY_VAR") -> None: ...
@@ -443,15 +442,15 @@ class Misc:
     ) -> None: ...
     def option_clear(self) -> None: ...
     def option_get(self, name, className): ...
-    def option_readfile(self, fileName, priority: Incomplete | None = None) -> None: ...
+    def option_readfile(self, fileName, priority=None) -> None: ...
     def selection_clear(self, **kw) -> None: ...
     def selection_get(self, **kw): ...
     def selection_handle(self, command, **kw) -> None: ...
     def selection_own(self, **kw) -> None: ...
     def selection_own_get(self, **kw): ...
     def send(self, interp, cmd, *args): ...
-    def lower(self, belowThis: Incomplete | None = None) -> None: ...
-    def tkraise(self, aboveThis: Incomplete | None = None) -> None: ...
+    def lower(self, belowThis=None) -> None: ...
+    def tkraise(self, aboveThis=None) -> None: ...
     lift = tkraise
     if sys.version_info >= (3, 11):
         def info_patchlevel(self) -> _VersionInfoType: ...
@@ -889,29 +888,23 @@ class Wm:
     @overload
     def wm_geometry(self, newGeometry: str) -> None: ...
     geometry = wm_geometry
-    def wm_grid(
-        self,
-        baseWidth: Incomplete | None = None,
-        baseHeight: Incomplete | None = None,
-        widthInc: Incomplete | None = None,
-        heightInc: Incomplete | None = None,
-    ): ...
+    def wm_grid(self, baseWidth=None, baseHeight=None, widthInc=None, heightInc=None): ...
     grid = wm_grid
-    def wm_group(self, pathName: Incomplete | None = None): ...
+    def wm_group(self, pathName=None): ...
     group = wm_group
-    def wm_iconbitmap(self, bitmap: Incomplete | None = None, default: Incomplete | None = None): ...
+    def wm_iconbitmap(self, bitmap=None, default=None): ...
     iconbitmap = wm_iconbitmap
     def wm_iconify(self) -> None: ...
     iconify = wm_iconify
-    def wm_iconmask(self, bitmap: Incomplete | None = None): ...
+    def wm_iconmask(self, bitmap=None): ...
     iconmask = wm_iconmask
-    def wm_iconname(self, newName: Incomplete | None = None) -> str: ...
+    def wm_iconname(self, newName=None) -> str: ...
     iconname = wm_iconname
     def wm_iconphoto(self, default: bool, image1: _PhotoImageLike | str, /, *args: _PhotoImageLike | str) -> None: ...
     iconphoto = wm_iconphoto
     def wm_iconposition(self, x: int | None = None, y: int | None = None) -> tuple[int, int] | None: ...
     iconposition = wm_iconposition
-    def wm_iconwindow(self, pathName: Incomplete | None = None): ...
+    def wm_iconwindow(self, pathName=None): ...
     iconwindow = wm_iconwindow
     def wm_manage(self, widget) -> None: ...
     manage = wm_manage
@@ -978,6 +971,7 @@ class Tk(Misc, Wm):
         sync: bool = False,
         use: str | None = None,
     ) -> None: ...
+    # Keep this in sync with ttktheme.ThemedTk. See issue #13858
     @overload
     def configure(
         self,
@@ -1453,8 +1447,8 @@ class Canvas(Widget, XView, YView):
     @overload
     def tag_bind(self, tagOrId: str | int, *, func: str, add: Literal["", "+"] | bool | None = None) -> None: ...
     def tag_unbind(self, tagOrId: str | int, sequence: str, funcid: str | None = None) -> None: ...
-    def canvasx(self, screenx, gridspacing: Incomplete | None = None): ...
-    def canvasy(self, screeny, gridspacing: Incomplete | None = None): ...
+    def canvasx(self, screenx, gridspacing=None): ...
+    def canvasy(self, screeny, gridspacing=None): ...
     @overload
     def coords(self, tagOrId: str | int, /) -> list[float]: ...
     @overload
@@ -2462,7 +2456,7 @@ class Listbox(Widget, XView, YView):
     select_set = selection_set
     def size(self) -> int: ...  # type: ignore[override]
     def itemcget(self, index: str | int, option): ...
-    def itemconfigure(self, index: str | int, cnf: Incomplete | None = None, **kw): ...
+    def itemconfigure(self, index: str | int, cnf=None, **kw): ...
     itemconfig = itemconfigure
 
 class Menu(Widget):
@@ -3142,7 +3136,7 @@ class Scrollbar(Widget):
     @overload
     def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ...
     config = configure
-    def activate(self, index: Incomplete | None = None): ...
+    def activate(self, index=None): ...
     def delta(self, deltax: int, deltay: int) -> float: ...
     def fraction(self, x: int, y: int) -> float: ...
     def identify(self, x: int, y: int) -> Literal["arrow1", "arrow2", "slider", "trough1", "trough2", ""]: ...
@@ -3625,7 +3619,7 @@ class Text(Widget, XView, YView):
     def yview_pickplace(self, *what): ...  # deprecated
 
 class _setit:
-    def __init__(self, var, value, callback: Incomplete | None = None) -> None: ...
+    def __init__(self, var, value, callback=None) -> None: ...
     def __call__(self, *args) -> None: ...
 
 # manual page: tk_optionMenu
@@ -3663,9 +3657,7 @@ class _PhotoImageLike(_Image): ...
 class Image(_Image):
     name: Incomplete
     tk: _tkinter.TkappType
-    def __init__(
-        self, imgtype, name: Incomplete | None = None, cnf={}, master: Misc | _tkinter.TkappType | None = None, **kw
-    ) -> None: ...
+    def __init__(self, imgtype, name=None, cnf={}, master: Misc | _tkinter.TkappType | None = None, **kw) -> None: ...
     def __del__(self) -> None: ...
     def __setitem__(self, key, value) -> None: ...
     def __getitem__(self, key): ...
@@ -3736,6 +3728,7 @@ class PhotoImage(Image, _PhotoImageLike):
         self,
         data: (
             str
+            | bytes
             | list[str]
             | list[list[str]]
             | list[tuple[str, ...]]
@@ -3743,7 +3736,7 @@ class PhotoImage(Image, _PhotoImageLike):
             | tuple[list[str], ...]
             | tuple[tuple[str, ...], ...]
         ),
-        to: tuple[int, int] | None = None,
+        to: tuple[int, int] | tuple[int, int, int, int] | None = None,
     ) -> None: ...
     if sys.version_info >= (3, 13):
         def read(
@@ -3790,7 +3783,7 @@ class BitmapImage(Image, _BitmapImageLike):
     # This should be kept in sync with PIL.ImageTK.BitmapImage.__init__()
     def __init__(
         self,
-        name: Incomplete | None = None,
+        name=None,
         cnf: dict[str, Any] = {},
         master: Misc | _tkinter.TkappType | None = None,
         *,
@@ -3924,7 +3917,7 @@ class Spinbox(Widget, XView):
     def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ...
     config = configure
     def bbox(self, index) -> tuple[int, int, int, int] | None: ...  # type: ignore[override]
-    def delete(self, first, last: Incomplete | None = None) -> Literal[""]: ...
+    def delete(self, first, last=None) -> Literal[""]: ...
     def get(self) -> str: ...
     def icursor(self, index): ...
     def identify(self, x: int, y: int) -> Literal["", "buttondown", "buttonup", "entry"]: ...
@@ -3938,7 +3931,7 @@ class Spinbox(Widget, XView):
     def selection(self, *args) -> tuple[int, ...]: ...
     def selection_adjust(self, index): ...
     def selection_clear(self): ...  # type: ignore[override]
-    def selection_element(self, element: Incomplete | None = None): ...
+    def selection_element(self, element=None): ...
     def selection_from(self, index: int) -> None: ...
     def selection_present(self) -> None: ...
     def selection_range(self, start: int, end: int) -> None: ...
@@ -4081,7 +4074,7 @@ class PanedWindow(Widget):
     def sash_mark(self, index): ...
     def sash_place(self, index, x, y): ...
     def panecget(self, child, option): ...
-    def paneconfigure(self, tagOrId, cnf: Incomplete | None = None, **kw): ...
+    def paneconfigure(self, tagOrId, cnf=None, **kw): ...
     paneconfig: Incomplete
     def panes(self): ...
 
diff --git a/mypy/typeshed/stdlib/tkinter/colorchooser.pyi b/mypy/typeshed/stdlib/tkinter/colorchooser.pyi
index 09bc8cbb4f1e..d0d6de842656 100644
--- a/mypy/typeshed/stdlib/tkinter/colorchooser.pyi
+++ b/mypy/typeshed/stdlib/tkinter/colorchooser.pyi
@@ -1,20 +1,12 @@
-import sys
 from tkinter import Misc
 from tkinter.commondialog import Dialog
 from typing import ClassVar
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Chooser", "askcolor"]
+__all__ = ["Chooser", "askcolor"]
 
 class Chooser(Dialog):
     command: ClassVar[str]
 
-if sys.version_info >= (3, 9):
-    def askcolor(
-        color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ...
-    ) -> tuple[None, None] | tuple[tuple[int, int, int], str]: ...
-
-else:
-    def askcolor(
-        color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ...
-    ) -> tuple[None, None] | tuple[tuple[float, float, float], str]: ...
+def askcolor(
+    color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ...
+) -> tuple[None, None] | tuple[tuple[int, int, int], str]: ...
diff --git a/mypy/typeshed/stdlib/tkinter/commondialog.pyi b/mypy/typeshed/stdlib/tkinter/commondialog.pyi
index d06c08df5b76..d5fc2f05ceec 100644
--- a/mypy/typeshed/stdlib/tkinter/commondialog.pyi
+++ b/mypy/typeshed/stdlib/tkinter/commondialog.pyi
@@ -1,14 +1,12 @@
-import sys
 from _typeshed import Incomplete
 from collections.abc import Mapping
 from typing import ClassVar
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Dialog"]
+__all__ = ["Dialog"]
 
 class Dialog:
     command: ClassVar[str | None]
     master: Incomplete | None
     options: Mapping[str, Incomplete]
-    def __init__(self, master: Incomplete | None = None, **options) -> None: ...
+    def __init__(self, master=None, **options) -> None: ...
     def show(self, **options): ...
diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi
index b7d74c0fa71e..971b64f09125 100644
--- a/mypy/typeshed/stdlib/tkinter/dialog.pyi
+++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi
@@ -1,16 +1,13 @@
-import sys
-from _typeshed import Incomplete
 from collections.abc import Mapping
 from tkinter import Widget
 from typing import Any, Final
 
-if sys.version_info >= (3, 9):
-    __all__ = ["Dialog"]
+__all__ = ["Dialog"]
 
 DIALOG_ICON: Final = "questhead"
 
 class Dialog(Widget):
     widgetName: str
     num: int
-    def __init__(self, master: Incomplete | None = None, cnf: Mapping[str, Any] = {}, **kw) -> None: ...
+    def __init__(self, master=None, cnf: Mapping[str, Any] = {}, **kw) -> None: ...
     def destroy(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi
index d806be74068e..fe2961701c61 100644
--- a/mypy/typeshed/stdlib/tkinter/dnd.pyi
+++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi
@@ -1,9 +1,7 @@
-import sys
 from tkinter import Event, Misc, Tk, Widget
 from typing import ClassVar, Protocol
 
-if sys.version_info >= (3, 9):
-    __all__ = ["dnd_start", "DndHandler"]
+__all__ = ["dnd_start", "DndHandler"]
 
 class _DndSource(Protocol):
     def dnd_end(self, target: Widget | None, event: Event[Misc] | None, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi
index 03f89cfbe3e6..af033dae97c3 100644
--- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi
+++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi
@@ -1,25 +1,23 @@
-import sys
 from _typeshed import Incomplete, StrOrBytesPath
 from collections.abc import Iterable
 from tkinter import Button, Entry, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog
 from typing import IO, ClassVar, Literal
 
-if sys.version_info >= (3, 9):
-    __all__ = [
-        "FileDialog",
-        "LoadFileDialog",
-        "SaveFileDialog",
-        "Open",
-        "SaveAs",
-        "Directory",
-        "askopenfilename",
-        "asksaveasfilename",
-        "askopenfilenames",
-        "askopenfile",
-        "askopenfiles",
-        "asksaveasfile",
-        "askdirectory",
-    ]
+__all__ = [
+    "FileDialog",
+    "LoadFileDialog",
+    "SaveFileDialog",
+    "Open",
+    "SaveAs",
+    "Directory",
+    "askopenfilename",
+    "asksaveasfilename",
+    "askopenfilenames",
+    "askopenfile",
+    "askopenfiles",
+    "asksaveasfile",
+    "askdirectory",
+]
 
 dialogstates: dict[Incomplete, tuple[Incomplete, Incomplete]]
 
@@ -40,21 +38,21 @@ class FileDialog:
     filter_button: Button
     cancel_button: Button
     def __init__(
-        self, master, title: Incomplete | None = None
+        self, master, title=None
     ) -> None: ...  # title is usually a str or None, but e.g. int doesn't raise en exception either
     how: Incomplete | None
-    def go(self, dir_or_file=".", pattern: str = "*", default: str = "", key: Incomplete | None = None): ...
-    def quit(self, how: Incomplete | None = None) -> None: ...
+    def go(self, dir_or_file=".", pattern: str = "*", default: str = "", key=None): ...
+    def quit(self, how=None) -> None: ...
     def dirs_double_event(self, event) -> None: ...
     def dirs_select_event(self, event) -> None: ...
     def files_double_event(self, event) -> None: ...
     def files_select_event(self, event) -> None: ...
     def ok_event(self, event) -> None: ...
     def ok_command(self) -> None: ...
-    def filter_command(self, event: Incomplete | None = None) -> None: ...
+    def filter_command(self, event=None) -> None: ...
     def get_filter(self): ...
     def get_selection(self): ...
-    def cancel_command(self, event: Incomplete | None = None) -> None: ...
+    def cancel_command(self, event=None) -> None: ...
     def set_filter(self, dir, pat) -> None: ...
     def set_selection(self, file) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi
index 3b73f982c4ca..cab97490be34 100644
--- a/mypy/typeshed/stdlib/tkinter/font.pyi
+++ b/mypy/typeshed/stdlib/tkinter/font.pyi
@@ -5,8 +5,7 @@ import tkinter
 from typing import Any, ClassVar, Final, Literal, TypedDict, overload
 from typing_extensions import TypeAlias, Unpack
 
-if sys.version_info >= (3, 9):
-    __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"]
+__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"]
 
 NORMAL: Final = "normal"
 ROMAN: Final = "roman"
diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
index 5cdfe512f9b7..902fab62ac05 100644
--- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi
+++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
@@ -1,18 +1,7 @@
-import sys
 from tkinter.commondialog import Dialog
 from typing import ClassVar, Final
 
-if sys.version_info >= (3, 9):
-    __all__ = [
-        "showinfo",
-        "showwarning",
-        "showerror",
-        "askquestion",
-        "askokcancel",
-        "askyesno",
-        "askyesnocancel",
-        "askretrycancel",
-    ]
+__all__ = ["showinfo", "showwarning", "showerror", "askquestion", "askokcancel", "askyesno", "askyesnocancel", "askretrycancel"]
 
 ERROR: Final = "error"
 INFO: Final = "info"
diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi
index ab3c010938be..50b9cd8f9bcd 100644
--- a/mypy/typeshed/stdlib/tkinter/ttk.pyi
+++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi
@@ -35,7 +35,7 @@ __all__ = [
 ]
 
 def tclobjs_to_py(adict: dict[Any, Any]) -> dict[Any, Any]: ...
-def setup_master(master: Incomplete | None = None): ...
+def setup_master(master=None): ...
 
 _Padding: TypeAlias = (
     tkinter._ScreenUnits
@@ -52,14 +52,14 @@ class Style:
     master: Incomplete
     tk: _tkinter.TkappType
     def __init__(self, master: tkinter.Misc | None = None) -> None: ...
-    def configure(self, style, query_opt: Incomplete | None = None, **kw): ...
-    def map(self, style, query_opt: Incomplete | None = None, **kw): ...
-    def lookup(self, style, option, state: Incomplete | None = None, default: Incomplete | None = None): ...
-    def layout(self, style, layoutspec: Incomplete | None = None): ...
+    def configure(self, style, query_opt=None, **kw): ...
+    def map(self, style, query_opt=None, **kw): ...
+    def lookup(self, style, option, state=None, default=None): ...
+    def layout(self, style, layoutspec=None): ...
     def element_create(self, elementname, etype, *args, **kw) -> None: ...
     def element_names(self): ...
     def element_options(self, elementname): ...
-    def theme_create(self, themename, parent: Incomplete | None = None, settings: Incomplete | None = None) -> None: ...
+    def theme_create(self, themename, parent=None, settings=None) -> None: ...
     def theme_settings(self, themename, settings) -> None: ...
     def theme_names(self) -> tuple[str, ...]: ...
     @overload
@@ -68,10 +68,10 @@ class Style:
     def theme_use(self, themename: None = None) -> str: ...
 
 class Widget(tkinter.Widget):
-    def __init__(self, master: tkinter.Misc | None, widgetname, kw: Incomplete | None = None) -> None: ...
+    def __init__(self, master: tkinter.Misc | None, widgetname, kw=None) -> None: ...
     def identify(self, x: int, y: int) -> str: ...
-    def instate(self, statespec, callback: Incomplete | None = None, *args, **kw): ...
-    def state(self, statespec: Incomplete | None = None): ...
+    def instate(self, statespec, callback=None, *args, **kw): ...
+    def state(self, statespec=None): ...
 
 class Button(Widget):
     def __init__(
@@ -567,8 +567,8 @@ class Notebook(Widget):
     def identify(self, x: int, y: int) -> str: ...
     def index(self, tab_id): ...
     def insert(self, pos, child, **kw) -> None: ...
-    def select(self, tab_id: Incomplete | None = None): ...
-    def tab(self, tab_id, option: Incomplete | None = None, **kw): ...
+    def select(self, tab_id=None): ...
+    def tab(self, tab_id, option=None, **kw): ...
     def tabs(self): ...
     def enable_traversal(self) -> None: ...
 
@@ -617,8 +617,8 @@ class Panedwindow(Widget, tkinter.PanedWindow):
     def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ...
     forget: Incomplete
     def insert(self, pos, child, **kw) -> None: ...
-    def pane(self, pane, option: Incomplete | None = None, **kw): ...
-    def sashpos(self, index, newpos: Incomplete | None = None): ...
+    def pane(self, pane, option=None, **kw): ...
+    def sashpos(self, index, newpos=None): ...
 
 PanedWindow = Panedwindow
 
diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi
index 741ce5b035b7..7c13b15d95b7 100644
--- a/mypy/typeshed/stdlib/token.pyi
+++ b/mypy/typeshed/stdlib/token.pyi
@@ -78,6 +78,9 @@ if sys.version_info >= (3, 10):
 if sys.version_info >= (3, 12):
     __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START", "EXACT_TOKEN_TYPES"]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"]
+
 ENDMARKER: int
 NAME: int
 NUMBER: int
@@ -155,6 +158,11 @@ if sys.version_info >= (3, 12):
     FSTRING_MIDDLE: int
     FSTRING_START: int
 
+if sys.version_info >= (3, 14):
+    TSTRING_START: int
+    TSTRING_MIDDLE: int
+    TSTRING_END: int
+
 def ISTERMINAL(x: int) -> bool: ...
 def ISNONTERMINAL(x: int) -> bool: ...
 def ISEOF(x: int) -> bool: ...
diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi
index a1c4b412da83..b658740a1ad7 100644
--- a/mypy/typeshed/stdlib/tokenize.pyi
+++ b/mypy/typeshed/stdlib/tokenize.pyi
@@ -93,6 +93,9 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 13):
     __all__ += ["TokenError", "open"]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"]
+
 cookie_re: Pattern[str]
 blank_re: Pattern[bytes]
 
@@ -125,7 +128,7 @@ class Untokenizer:
     prev_col: int
     encoding: str | None
     def add_whitespace(self, start: _Position) -> None: ...
-    if sys.version_info >= (3, 13):
+    if sys.version_info >= (3, 12):
         def add_backslash_continuation(self, start: _Position) -> None: ...
 
     def untokenize(self, iterable: Iterable[_Token]) -> str: ...
diff --git a/mypy/typeshed/stdlib/tomllib.pyi b/mypy/typeshed/stdlib/tomllib.pyi
index d559568b912b..c160ffc38bfd 100644
--- a/mypy/typeshed/stdlib/tomllib.pyi
+++ b/mypy/typeshed/stdlib/tomllib.pyi
@@ -1,10 +1,26 @@
+import sys
 from _typeshed import SupportsRead
 from collections.abc import Callable
-from typing import Any
+from typing import Any, overload
+from typing_extensions import deprecated
 
 __all__ = ("loads", "load", "TOMLDecodeError")
 
-class TOMLDecodeError(ValueError): ...
+if sys.version_info >= (3, 14):
+    class TOMLDecodeError(ValueError):
+        msg: str
+        doc: str
+        pos: int
+        lineno: int
+        colno: int
+        @overload
+        def __init__(self, msg: str, doc: str, pos: int) -> None: ...
+        @overload
+        @deprecated("Deprecated in Python 3.14; Please set 'msg', 'doc' and 'pos' arguments only.")
+        def __init__(self, msg: str | type = ..., doc: str | type = ..., pos: int | type = ..., *args: Any) -> None: ...
+
+else:
+    class TOMLDecodeError(ValueError): ...
 
 def load(fp: SupportsRead[bytes], /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ...
 def loads(s: str, /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ...
diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi
index 04390f119195..7e7cc1e9ac54 100644
--- a/mypy/typeshed/stdlib/trace.pyi
+++ b/mypy/typeshed/stdlib/trace.pyi
@@ -75,11 +75,7 @@ class Trace:
     def runctx(
         self, cmd: str | types.CodeType, globals: Mapping[str, Any] | None = None, locals: Mapping[str, Any] | None = None
     ) -> None: ...
-    if sys.version_info >= (3, 9):
-        def runfunc(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ...
-    else:
-        def runfunc(self, func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ...
-
+    def runfunc(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ...
     def file_module_function_of(self, frame: types.FrameType) -> _FileModuleFunction: ...
     def globaltrace_trackcallers(self, frame: types.FrameType, why: str, arg: Any) -> None: ...
     def globaltrace_countfuncs(self, frame: types.FrameType, why: str, arg: Any) -> None: ...
diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi
index 4f132d51c617..4553dbd08384 100644
--- a/mypy/typeshed/stdlib/traceback.pyi
+++ b/mypy/typeshed/stdlib/traceback.pyi
@@ -27,6 +27,9 @@ __all__ = [
     "walk_tb",
 ]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["print_list"]
+
 _FrameSummaryTuple: TypeAlias = tuple[str, int, str, str | None]
 
 def print_tb(tb: TracebackType | None, limit: int | None = None, file: SupportsWrite[str] | None = None) -> None: ...
@@ -81,8 +84,6 @@ def print_stack(f: FrameType | None = None, limit: int | None = None, file: Supp
 def extract_tb(tb: TracebackType | None, limit: int | None = None) -> StackSummary: ...
 def extract_stack(f: FrameType | None = None, limit: int | None = None) -> StackSummary: ...
 def format_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple]) -> list[str]: ...
-
-# undocumented
 def print_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple], file: SupportsWrite[str] | None = None) -> None: ...
 
 if sys.version_info >= (3, 13):
diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi
index e721e414138b..05d98ae127d8 100644
--- a/mypy/typeshed/stdlib/tracemalloc.pyi
+++ b/mypy/typeshed/stdlib/tracemalloc.pyi
@@ -69,10 +69,7 @@ class Frame:
         def __ge__(self, other: Frame, NotImplemented: Any = ...) -> bool: ...
         def __le__(self, other: Frame, NotImplemented: Any = ...) -> bool: ...
 
-if sys.version_info >= (3, 9):
-    _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]]
-else:
-    _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple]]
+_TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]]
 
 class Trace:
     @property
@@ -86,13 +83,9 @@ class Trace:
     def __hash__(self) -> int: ...
 
 class Traceback(Sequence[Frame]):
-    if sys.version_info >= (3, 9):
-        @property
-        def total_nframe(self) -> int | None: ...
-        def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ...
-    else:
-        def __init__(self, frames: Sequence[_FrameTuple]) -> None: ...
-
+    @property
+    def total_nframe(self) -> int | None: ...
+    def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ...
     def format(self, limit: int | None = None, most_recent_first: bool = False) -> list[str]: ...
     @overload
     def __getitem__(self, index: SupportsIndex) -> Frame: ...
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index 542979d4afc5..1163d71d2c95 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -1,5 +1,5 @@
 import sys
-from _typeshed import MaybeNone, SupportsKeysAndGetItem
+from _typeshed import AnnotationForm, MaybeNone, SupportsKeysAndGetItem
 from _typeshed.importlib import LoaderProtocol
 from collections.abc import (
     AsyncGenerator,
@@ -11,15 +11,17 @@ from collections.abc import (
     Iterable,
     Iterator,
     KeysView,
+    Mapping,
     MutableSequence,
     ValuesView,
 )
 from importlib.machinery import ModuleSpec
-
-# pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping
-from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload  # noqa: Y022
+from typing import Any, ClassVar, Literal, TypeVar, final, overload
 from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated
 
+if sys.version_info >= (3, 14):
+    from _typeshed import AnnotateFunc
+
 __all__ = [
     "FunctionType",
     "LambdaType",
@@ -47,11 +49,9 @@ __all__ = [
     "WrapperDescriptorType",
     "resolve_bases",
     "CellType",
+    "GenericAlias",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["GenericAlias"]
-
 if sys.version_info >= (3, 10):
     __all__ += ["EllipsisType", "NoneType", "NotImplementedType", "UnionType"]
 
@@ -80,7 +80,9 @@ class FunctionType:
     def __globals__(self) -> dict[str, Any]: ...
     __name__: str
     __qualname__: str
-    __annotations__: dict[str, Any]
+    __annotations__: dict[str, AnnotationForm]
+    if sys.version_info >= (3, 14):
+        __annotate__: AnnotateFunc | None
     __kwdefaults__: dict[str, Any] | None
     if sys.version_info >= (3, 10):
         @property
@@ -320,11 +322,10 @@ class MappingProxyType(Mapping[_KT, _VT_co]):
     def get(self, key: _KT, /) -> _VT_co | None: ...
     @overload
     def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
-        def __reversed__(self) -> Iterator[_KT]: ...
-        def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
-        def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __reversed__(self) -> Iterator[_KT]: ...
+    def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
+    def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
 
 class SimpleNamespace:
     __hash__: ClassVar[None]  # type: ignore[assignment]
@@ -356,6 +357,10 @@ class ModuleType:
     # Redeclaring `__doc__` here helps some type checkers understand that `__doc__` is available
     # as an implicit global in all modules, similar to `__name__`, `__file__`, `__spec__`, etc.
     __doc__: str | None
+    __annotations__: dict[str, AnnotationForm]
+    if sys.version_info >= (3, 14):
+        __annotate__: AnnotateFunc | None
+
     def __init__(self, name: str, doc: str | None = ...) -> None: ...
     # __getattr__ doesn't exist at runtime,
     # but having it here in typeshed makes dynamic imports
@@ -425,8 +430,7 @@ class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]):
     @overload
     async def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ...
     def aclose(self) -> Coroutine[Any, Any, None]: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @final
 class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]):
@@ -647,30 +651,29 @@ def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Await
 @overload
 def coroutine(func: _Fn) -> _Fn: ...
 
-if sys.version_info >= (3, 9):
-    class GenericAlias:
-        @property
-        def __origin__(self) -> type | TypeAliasType: ...
+class GenericAlias:
+    @property
+    def __origin__(self) -> type | TypeAliasType: ...
+    @property
+    def __args__(self) -> tuple[Any, ...]: ...
+    @property
+    def __parameters__(self) -> tuple[Any, ...]: ...
+    def __new__(cls, origin: type, args: Any, /) -> Self: ...
+    def __getitem__(self, typeargs: Any, /) -> GenericAlias: ...
+    def __eq__(self, value: object, /) -> bool: ...
+    def __hash__(self) -> int: ...
+    def __mro_entries__(self, bases: Iterable[object], /) -> tuple[type, ...]: ...
+    if sys.version_info >= (3, 11):
         @property
-        def __args__(self) -> tuple[Any, ...]: ...
+        def __unpacked__(self) -> bool: ...
         @property
-        def __parameters__(self) -> tuple[Any, ...]: ...
-        def __new__(cls, origin: type, args: Any, /) -> Self: ...
-        def __getitem__(self, typeargs: Any, /) -> GenericAlias: ...
-        def __eq__(self, value: object, /) -> bool: ...
-        def __hash__(self) -> int: ...
-        def __mro_entries__(self, bases: Iterable[object], /) -> tuple[type, ...]: ...
-        if sys.version_info >= (3, 11):
-            @property
-            def __unpacked__(self) -> bool: ...
-            @property
-            def __typing_unpacked_tuple_args__(self) -> tuple[Any, ...] | None: ...
-        if sys.version_info >= (3, 10):
-            def __or__(self, value: Any, /) -> UnionType: ...
-            def __ror__(self, value: Any, /) -> UnionType: ...
-
-        # GenericAlias delegates attr access to `__origin__`
-        def __getattr__(self, name: str) -> Any: ...
+        def __typing_unpacked_tuple_args__(self) -> tuple[Any, ...] | None: ...
+    if sys.version_info >= (3, 10):
+        def __or__(self, value: Any, /) -> UnionType: ...
+        def __ror__(self, value: Any, /) -> UnionType: ...
+
+    # GenericAlias delegates attr access to `__origin__`
+    def __getattr__(self, name: str) -> Any: ...
 
 if sys.version_info >= (3, 10):
     @final
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index bc8f342ef46b..5aa85543ed2c 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -13,6 +13,7 @@ from types import (
     BuiltinFunctionType,
     CodeType,
     FunctionType,
+    GenericAlias,
     MethodDescriptorType,
     MethodType,
     MethodWrapperType,
@@ -22,13 +23,17 @@ from types import (
 )
 from typing_extensions import Never as _Never, ParamSpec as _ParamSpec, deprecated
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
+if sys.version_info >= (3, 14):
+    from _typeshed import EvaluateFunc
+
+    from annotationlib import Format
+
 if sys.version_info >= (3, 10):
     from types import UnionType
 
 __all__ = [
     "AbstractSet",
+    "Annotated",
     "Any",
     "AnyStr",
     "AsyncContextManager",
@@ -36,7 +41,7 @@ __all__ = [
     "AsyncIterable",
     "AsyncIterator",
     "Awaitable",
-    "ByteString",
+    "BinaryIO",
     "Callable",
     "ChainMap",
     "ClassVar",
@@ -49,10 +54,12 @@ __all__ = [
     "Deque",
     "Dict",
     "Final",
+    "ForwardRef",
     "FrozenSet",
     "Generator",
     "Generic",
     "Hashable",
+    "IO",
     "ItemsView",
     "Iterable",
     "Iterator",
@@ -61,12 +68,16 @@ __all__ = [
     "Literal",
     "Mapping",
     "MappingView",
+    "Match",
     "MutableMapping",
     "MutableSequence",
     "MutableSet",
     "NamedTuple",
     "NewType",
+    "NoReturn",
     "Optional",
+    "OrderedDict",
+    "Pattern",
     "Protocol",
     "Reversible",
     "Sequence",
@@ -80,6 +91,7 @@ __all__ = [
     "SupportsInt",
     "SupportsRound",
     "Text",
+    "TextIO",
     "Tuple",
     "Type",
     "TypeVar",
@@ -96,13 +108,13 @@ __all__ = [
     "no_type_check_decorator",
     "overload",
     "runtime_checkable",
-    "ForwardRef",
-    "NoReturn",
-    "OrderedDict",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["Annotated", "BinaryIO", "IO", "Match", "Pattern", "TextIO"]
+if sys.version_info < (3, 14):
+    __all__ += ["ByteString"]
+
+if sys.version_info >= (3, 14):
+    __all__ += ["evaluate_forward_ref"]
 
 if sys.version_info >= (3, 10):
     __all__ += ["Concatenate", "ParamSpec", "ParamSpecArgs", "ParamSpecKwargs", "TypeAlias", "TypeGuard", "is_typeddict"]
@@ -130,6 +142,10 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 13):
     __all__ += ["get_protocol_members", "is_protocol", "NoDefault", "TypeIs", "ReadOnly"]
 
+# We can't use this name here because it leads to issues with mypy, likely
+# due to an import cycle. Below instead we use Any with a comment.
+# from _typeshed import AnnotationForm
+
 class Any: ...
 class _Final: ...
 
@@ -139,9 +155,9 @@ class TypeVar:
     @property
     def __name__(self) -> str: ...
     @property
-    def __bound__(self) -> Any | None: ...
+    def __bound__(self) -> Any | None: ...  # AnnotationForm
     @property
-    def __constraints__(self) -> tuple[Any, ...]: ...
+    def __constraints__(self) -> tuple[Any, ...]: ...  # AnnotationForm
     @property
     def __covariant__(self) -> bool: ...
     @property
@@ -151,46 +167,64 @@ class TypeVar:
         def __infer_variance__(self) -> bool: ...
     if sys.version_info >= (3, 13):
         @property
-        def __default__(self) -> Any: ...
+        def __default__(self) -> Any: ...  # AnnotationForm
     if sys.version_info >= (3, 13):
         def __new__(
             cls,
             name: str,
-            *constraints: Any,
-            bound: Any | None = None,
+            *constraints: Any,  # AnnotationForm
+            bound: Any | None = None,  # AnnotationForm
             contravariant: bool = False,
             covariant: bool = False,
             infer_variance: bool = False,
-            default: Any = ...,
+            default: Any = ...,  # AnnotationForm
         ) -> Self: ...
     elif sys.version_info >= (3, 12):
         def __new__(
             cls,
             name: str,
-            *constraints: Any,
-            bound: Any | None = None,
+            *constraints: Any,  # AnnotationForm
+            bound: Any | None = None,  # AnnotationForm
             covariant: bool = False,
             contravariant: bool = False,
             infer_variance: bool = False,
         ) -> Self: ...
     elif sys.version_info >= (3, 11):
         def __new__(
-            cls, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False
+            cls,
+            name: str,
+            *constraints: Any,  # AnnotationForm
+            bound: Any | None = None,  # AnnotationForm
+            covariant: bool = False,
+            contravariant: bool = False,
         ) -> Self: ...
     else:
         def __init__(
-            self, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False
+            self,
+            name: str,
+            *constraints: Any,  # AnnotationForm
+            bound: Any | None = None,  # AnnotationForm
+            covariant: bool = False,
+            contravariant: bool = False,
         ) -> None: ...
     if sys.version_info >= (3, 10):
-        def __or__(self, right: Any) -> _SpecialForm: ...
-        def __ror__(self, left: Any) -> _SpecialForm: ...
+        def __or__(self, right: Any) -> _SpecialForm: ...  # AnnotationForm
+        def __ror__(self, left: Any) -> _SpecialForm: ...  # AnnotationForm
     if sys.version_info >= (3, 11):
         def __typing_subst__(self, arg: Any) -> Any: ...
     if sys.version_info >= (3, 13):
         def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
         def has_default(self) -> bool: ...
+    if sys.version_info >= (3, 14):
+        @property
+        def evaluate_bound(self) -> EvaluateFunc | None: ...
+        @property
+        def evaluate_constraints(self) -> EvaluateFunc | None: ...
+        @property
+        def evaluate_default(self) -> EvaluateFunc | None: ...
 
 # Used for an undocumented mypy feature. Does not exist at runtime.
+# Obsolete, use _typeshed._type_checker_internals.promote instead.
 _promote = object()
 
 # N.B. Keep this definition in sync with typing_extensions._SpecialForm
@@ -203,7 +237,6 @@ class _SpecialForm(_Final):
 
 Union: _SpecialForm
 Generic: _SpecialForm
-# Protocol is only present in 3.8 and later, but mypy needs it unconditionally
 Protocol: _SpecialForm
 Callable: _SpecialForm
 Type: _SpecialForm
@@ -231,10 +264,10 @@ if sys.version_info >= (3, 11):
         def __name__(self) -> str: ...
         if sys.version_info >= (3, 13):
             @property
-            def __default__(self) -> Any: ...
+            def __default__(self) -> Any: ...  # AnnotationForm
             def has_default(self) -> bool: ...
         if sys.version_info >= (3, 13):
-            def __new__(cls, name: str, *, default: Any = ...) -> Self: ...
+            def __new__(cls, name: str, *, default: Any = ...) -> Self: ...  # AnnotationForm
         elif sys.version_info >= (3, 12):
             def __new__(cls, name: str) -> Self: ...
         else:
@@ -243,6 +276,9 @@ if sys.version_info >= (3, 11):
         def __iter__(self) -> Any: ...
         def __typing_subst__(self, arg: Never) -> Never: ...
         def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
+        if sys.version_info >= (3, 14):
+            @property
+            def evaluate_default(self) -> EvaluateFunc | None: ...
 
 if sys.version_info >= (3, 10):
     @final
@@ -274,7 +310,7 @@ if sys.version_info >= (3, 10):
         @property
         def __name__(self) -> str: ...
         @property
-        def __bound__(self) -> Any | None: ...
+        def __bound__(self) -> Any | None: ...  # AnnotationForm
         @property
         def __covariant__(self) -> bool: ...
         @property
@@ -284,35 +320,45 @@ if sys.version_info >= (3, 10):
             def __infer_variance__(self) -> bool: ...
         if sys.version_info >= (3, 13):
             @property
-            def __default__(self) -> Any: ...
+            def __default__(self) -> Any: ...  # AnnotationForm
         if sys.version_info >= (3, 13):
             def __new__(
                 cls,
                 name: str,
                 *,
-                bound: Any | None = None,
+                bound: Any | None = None,  # AnnotationForm
                 contravariant: bool = False,
                 covariant: bool = False,
                 infer_variance: bool = False,
-                default: Any = ...,
+                default: Any = ...,  # AnnotationForm
             ) -> Self: ...
         elif sys.version_info >= (3, 12):
             def __new__(
                 cls,
                 name: str,
                 *,
-                bound: Any | None = None,
+                bound: Any | None = None,  # AnnotationForm
                 contravariant: bool = False,
                 covariant: bool = False,
                 infer_variance: bool = False,
             ) -> Self: ...
         elif sys.version_info >= (3, 11):
             def __new__(
-                cls, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False
+                cls,
+                name: str,
+                *,
+                bound: Any | None = None,  # AnnotationForm
+                contravariant: bool = False,
+                covariant: bool = False,
             ) -> Self: ...
         else:
             def __init__(
-                self, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False
+                self,
+                name: str,
+                *,
+                bound: Any | None = None,  # AnnotationForm
+                contravariant: bool = False,
+                covariant: bool = False,
             ) -> None: ...
 
         @property
@@ -327,13 +373,16 @@ if sys.version_info >= (3, 10):
         def __ror__(self, left: Any) -> _SpecialForm: ...
         if sys.version_info >= (3, 13):
             def has_default(self) -> bool: ...
+        if sys.version_info >= (3, 14):
+            @property
+            def evaluate_default(self) -> EvaluateFunc | None: ...
 
     Concatenate: _SpecialForm
     TypeAlias: _SpecialForm
     TypeGuard: _SpecialForm
 
     class NewType:
-        def __init__(self, name: str, tp: Any) -> None: ...
+        def __init__(self, name: str, tp: Any) -> None: ...  # AnnotationForm
         if sys.version_info >= (3, 11):
             @staticmethod
             def __call__(x: _T, /) -> _T: ...
@@ -386,8 +435,7 @@ ChainMap = _Alias()
 
 OrderedDict = _Alias()
 
-if sys.version_info >= (3, 9):
-    Annotated: _SpecialForm
+Annotated: _SpecialForm
 
 # Predefined type variables.
 AnyStr = TypeVar("AnyStr", str, bytes)  # noqa: Y001
@@ -531,6 +579,7 @@ class Coroutine(Awaitable[_ReturnT_nd_co], Generic[_YieldT_co, _SendT_nd_contra,
 
 # NOTE: This type does not exist in typing.py or PEP 484 but mypy needs it to exist.
 # The parameters correspond to Generator, but the 4th is the original type.
+# Obsolete, use _typeshed._type_checker_internals.AwaitableGenerator instead.
 @type_check_only
 class AwaitableGenerator(
     Awaitable[_ReturnT_nd_co],
@@ -858,20 +907,25 @@ _get_type_hints_obj_allowed_types: typing_extensions.TypeAlias = (  # noqa: Y042
     | MethodDescriptorType
 )
 
-if sys.version_info >= (3, 9):
+if sys.version_info >= (3, 14):
     def get_type_hints(
         obj: _get_type_hints_obj_allowed_types,
         globalns: dict[str, Any] | None = None,
         localns: Mapping[str, Any] | None = None,
         include_extras: bool = False,
-    ) -> dict[str, Any]: ...
+        *,
+        format: Format | None = None,
+    ) -> dict[str, Any]: ...  # AnnotationForm
 
 else:
     def get_type_hints(
-        obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None
-    ) -> dict[str, Any]: ...
+        obj: _get_type_hints_obj_allowed_types,
+        globalns: dict[str, Any] | None = None,
+        localns: Mapping[str, Any] | None = None,
+        include_extras: bool = False,
+    ) -> dict[str, Any]: ...  # AnnotationForm
 
-def get_args(tp: Any) -> tuple[Any, ...]: ...
+def get_args(tp: Any) -> tuple[Any, ...]: ...  # AnnotationForm
 
 if sys.version_info >= (3, 10):
     @overload
@@ -879,15 +933,10 @@ if sys.version_info >= (3, 10):
     @overload
     def get_origin(tp: UnionType) -> type[UnionType]: ...
 
-if sys.version_info >= (3, 9):
-    @overload
-    def get_origin(tp: GenericAlias) -> type: ...
-    @overload
-    def get_origin(tp: Any) -> Any | None: ...
-
-else:
-    def get_origin(tp: Any) -> Any | None: ...
-
+@overload
+def get_origin(tp: GenericAlias) -> type: ...
+@overload
+def get_origin(tp: Any) -> Any | None: ...  # AnnotationForm
 @overload
 def cast(typ: type[_T], val: Any) -> _T: ...
 @overload
@@ -898,7 +947,7 @@ def cast(typ: object, val: Any) -> Any: ...
 if sys.version_info >= (3, 11):
     def reveal_type(obj: _T, /) -> _T: ...
     def assert_never(arg: Never, /) -> Never: ...
-    def assert_type(val: _T, typ: Any, /) -> _T: ...
+    def assert_type(val: _T, typ: Any, /) -> _T: ...  # AnnotationForm
     def clear_overloads() -> None: ...
     def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ...
     def dataclass_transform(
@@ -913,9 +962,8 @@ if sys.version_info >= (3, 11):
 
 # Type constructors
 
+# Obsolete, will be changed to a function. Use _typeshed._type_checker_internals.NamedTupleFallback instead.
 class NamedTuple(tuple[Any, ...]):
-    if sys.version_info < (3, 9):
-        _field_types: ClassVar[dict[str, type]]
     _field_defaults: ClassVar[dict[str, Any]]
     _fields: ClassVar[tuple[str, ...]]
     # __orig_bases__ sometimes exists on <3.12, but not consistently
@@ -939,12 +987,12 @@ class NamedTuple(tuple[Any, ...]):
 
 # Internal mypy fallback type for all typed dicts (does not exist at runtime)
 # N.B. Keep this mostly in sync with typing_extensions._TypedDict/mypy_extensions._TypedDict
+# Obsolete, use _typeshed._type_checker_internals.TypedDictFallback instead.
 @type_check_only
 class _TypedDict(Mapping[str, object], metaclass=ABCMeta):
     __total__: ClassVar[bool]
-    if sys.version_info >= (3, 9):
-        __required_keys__: ClassVar[frozenset[str]]
-        __optional_keys__: ClassVar[frozenset[str]]
+    __required_keys__: ClassVar[frozenset[str]]
+    __optional_keys__: ClassVar[frozenset[str]]
     # __orig_bases__ sometimes exists on <3.12, but not consistently,
     # so we only add it to the stub on 3.12+
     if sys.version_info >= (3, 12):
@@ -964,73 +1012,81 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta):
     def items(self) -> dict_items[str, object]: ...
     def keys(self) -> dict_keys[str, object]: ...
     def values(self) -> dict_values[str, object]: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
-        @overload
-        def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
-        @overload
-        def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
-        @overload
-        def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
-        # supposedly incompatible definitions of __or__ and __ior__
-        def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
+    @overload
+    def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    @overload
+    def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    @overload
+    def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    @overload
+    def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    # supposedly incompatible definitions of __or__ and __ior__
+    def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
+
+if sys.version_info >= (3, 14):
+    from annotationlib import ForwardRef as ForwardRef
+
+    def evaluate_forward_ref(
+        forward_ref: ForwardRef,
+        *,
+        owner: object = None,
+        globals: dict[str, Any] | None = None,
+        locals: Mapping[str, Any] | None = None,
+        type_params: tuple[TypeVar, ParamSpec, TypeVarTuple] | None = None,
+        format: Format | None = None,
+    ) -> Any: ...  # AnnotationForm
+
+else:
+    @final
+    class ForwardRef(_Final):
+        __forward_arg__: str
+        __forward_code__: CodeType
+        __forward_evaluated__: bool
+        __forward_value__: Any | None  # AnnotationForm
+        __forward_is_argument__: bool
+        __forward_is_class__: bool
+        __forward_module__: Any | None
 
-@final
-class ForwardRef(_Final):
-    __forward_arg__: str
-    __forward_code__: CodeType
-    __forward_evaluated__: bool
-    __forward_value__: Any | None
-    __forward_is_argument__: bool
-    __forward_is_class__: bool
-    __forward_module__: Any | None
-    if sys.version_info >= (3, 9):
-        # The module and is_class arguments were added in later Python 3.9 versions.
         def __init__(self, arg: str, is_argument: bool = True, module: Any | None = None, *, is_class: bool = False) -> None: ...
-    else:
-        def __init__(self, arg: str, is_argument: bool = True) -> None: ...
 
-    if sys.version_info >= (3, 13):
-        @overload
-        @deprecated(
-            "Failing to pass a value to the 'type_params' parameter of ForwardRef._evaluate() is deprecated, "
-            "as it leads to incorrect behaviour when evaluating a stringified annotation "
-            "that references a PEP 695 type parameter. It will be disallowed in Python 3.15."
-        )
-        def _evaluate(
-            self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, *, recursive_guard: frozenset[str]
-        ) -> Any | None: ...
-        @overload
-        def _evaluate(
-            self,
-            globalns: dict[str, Any] | None,
-            localns: Mapping[str, Any] | None,
-            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...],
-            *,
-            recursive_guard: frozenset[str],
-        ) -> Any | None: ...
-    elif sys.version_info >= (3, 12):
-        def _evaluate(
-            self,
-            globalns: dict[str, Any] | None,
-            localns: Mapping[str, Any] | None,
-            type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None,
-            *,
-            recursive_guard: frozenset[str],
-        ) -> Any | None: ...
-    elif sys.version_info >= (3, 9):
-        def _evaluate(
-            self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, recursive_guard: frozenset[str]
-        ) -> Any | None: ...
-    else:
-        def _evaluate(self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None) -> Any | None: ...
+        if sys.version_info >= (3, 13):
+            @overload
+            @deprecated(
+                "Failing to pass a value to the 'type_params' parameter of ForwardRef._evaluate() is deprecated, "
+                "as it leads to incorrect behaviour when evaluating a stringified annotation "
+                "that references a PEP 695 type parameter. It will be disallowed in Python 3.15."
+            )
+            def _evaluate(
+                self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, *, recursive_guard: frozenset[str]
+            ) -> Any | None: ...  # AnnotationForm
+            @overload
+            def _evaluate(
+                self,
+                globalns: dict[str, Any] | None,
+                localns: Mapping[str, Any] | None,
+                type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...],
+                *,
+                recursive_guard: frozenset[str],
+            ) -> Any | None: ...  # AnnotationForm
+        elif sys.version_info >= (3, 12):
+            def _evaluate(
+                self,
+                globalns: dict[str, Any] | None,
+                localns: Mapping[str, Any] | None,
+                type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None,
+                *,
+                recursive_guard: frozenset[str],
+            ) -> Any | None: ...  # AnnotationForm
+        else:
+            def _evaluate(
+                self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, recursive_guard: frozenset[str]
+            ) -> Any | None: ...  # AnnotationForm
 
-    def __eq__(self, other: object) -> bool: ...
-    def __hash__(self) -> int: ...
-    if sys.version_info >= (3, 11):
-        def __or__(self, other: Any) -> _SpecialForm: ...
-        def __ror__(self, other: Any) -> _SpecialForm: ...
+        def __eq__(self, other: object) -> bool: ...
+        def __hash__(self) -> int: ...
+        if sys.version_info >= (3, 11):
+            def __or__(self, other: Any) -> _SpecialForm: ...
+            def __ror__(self, other: Any) -> _SpecialForm: ...
 
 if sys.version_info >= (3, 10):
     def is_typeddict(tp: object) -> bool: ...
@@ -1043,19 +1099,22 @@ if sys.version_info >= (3, 12):
     class TypeAliasType:
         def __new__(cls, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()) -> Self: ...
         @property
-        def __value__(self) -> Any: ...
+        def __value__(self) -> Any: ...  # AnnotationForm
         @property
         def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ...
         @property
-        def __parameters__(self) -> tuple[Any, ...]: ...
+        def __parameters__(self) -> tuple[Any, ...]: ...  # AnnotationForm
         @property
         def __name__(self) -> str: ...
         # It's writable on types, but not on instances of TypeAliasType.
         @property
         def __module__(self) -> str | None: ...  # type: ignore[override]
-        def __getitem__(self, parameters: Any) -> GenericAlias: ...
+        def __getitem__(self, parameters: Any) -> GenericAlias: ...  # AnnotationForm
         def __or__(self, right: Any) -> _SpecialForm: ...
         def __ror__(self, left: Any) -> _SpecialForm: ...
+        if sys.version_info >= (3, 14):
+            @property
+            def evaluate_value(self) -> EvaluateFunc: ...
 
 if sys.version_info >= (3, 13):
     def is_protocol(tp: type, /) -> bool: ...
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index f3b7b8ddf5b1..37f8e8ba6a4b 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -1,62 +1,63 @@
 import abc
 import enum
 import sys
-import typing
 from _collections_abc import dict_items, dict_keys, dict_values
-from _typeshed import IdentityFunction, Incomplete, Unused
-from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager
-from types import ModuleType
-from typing import (  # noqa: Y022,Y037,Y038,Y039
-    IO as IO,
-    TYPE_CHECKING as TYPE_CHECKING,
-    AbstractSet as AbstractSet,
-    Any as Any,
-    AnyStr as AnyStr,
+from _typeshed import AnnotationForm, IdentityFunction, Incomplete, Unused
+from collections.abc import (
     AsyncGenerator as AsyncGenerator,
     AsyncIterable as AsyncIterable,
     AsyncIterator as AsyncIterator,
     Awaitable as Awaitable,
-    BinaryIO as BinaryIO,
-    Callable as Callable,
-    ChainMap as ChainMap,
-    ClassVar as ClassVar,
     Collection as Collection,
     Container as Container,
     Coroutine as Coroutine,
-    Counter as Counter,
-    DefaultDict as DefaultDict,
-    Deque as Deque,
-    Dict as Dict,
-    ForwardRef as ForwardRef,
-    FrozenSet as FrozenSet,
     Generator as Generator,
-    Generic as Generic,
     Hashable as Hashable,
     ItemsView as ItemsView,
     Iterable as Iterable,
     Iterator as Iterator,
     KeysView as KeysView,
-    List as List,
     Mapping as Mapping,
     MappingView as MappingView,
-    Match as Match,
     MutableMapping as MutableMapping,
     MutableSequence as MutableSequence,
     MutableSet as MutableSet,
-    NoReturn as NoReturn,
-    Optional as Optional,
-    Pattern as Pattern,
     Reversible as Reversible,
     Sequence as Sequence,
-    Set as Set,
     Sized as Sized,
+    ValuesView as ValuesView,
+)
+from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager
+from re import Match as Match, Pattern as Pattern
+from types import GenericAlias, ModuleType
+from typing import (  # noqa: Y022,Y037,Y038,Y039,UP035
+    IO as IO,
+    TYPE_CHECKING as TYPE_CHECKING,
+    AbstractSet as AbstractSet,
+    Any as Any,
+    AnyStr as AnyStr,
+    BinaryIO as BinaryIO,
+    Callable as Callable,
+    ChainMap as ChainMap,
+    ClassVar as ClassVar,
+    Counter as Counter,
+    DefaultDict as DefaultDict,
+    Deque as Deque,
+    Dict as Dict,
+    ForwardRef as ForwardRef,
+    FrozenSet as FrozenSet,
+    Generic as Generic,
+    List as List,
+    NoReturn as NoReturn,
+    Optional as Optional,
+    Set as Set,
     Text as Text,
     TextIO as TextIO,
     Tuple as Tuple,
     Type as Type,
     TypedDict as TypedDict,
+    TypeVar as _TypeVar,
     Union as Union,
-    ValuesView as ValuesView,
     _Alias,
     cast as cast,
     no_type_check as no_type_check,
@@ -67,8 +68,6 @@ from typing import (  # noqa: Y022,Y037,Y038,Y039
 
 if sys.version_info >= (3, 10):
     from types import UnionType
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
 
 # Please keep order the same as at runtime.
 __all__ = [
@@ -196,10 +195,10 @@ __all__ = [
     "CapsuleType",
 ]
 
-_T = typing.TypeVar("_T")
-_F = typing.TypeVar("_F", bound=Callable[..., Any])
-_TC = typing.TypeVar("_TC", bound=type[object])
-_T_co = typing.TypeVar("_T_co", covariant=True)  # Any type covariant containers.
+_T = _TypeVar("_T")
+_F = _TypeVar("_F", bound=Callable[..., Any])
+_TC = _TypeVar("_TC", bound=type[object])
+_T_co = _TypeVar("_T_co", covariant=True)  # Any type covariant containers.
 
 class _Final: ...  # This should be imported from typing but that breaks pytype
 
@@ -242,7 +241,7 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta):
     __mutable_keys__: ClassVar[frozenset[str]]
     # PEP 728
     __closed__: ClassVar[bool]
-    __extra_items__: ClassVar[Any]
+    __extra_items__: ClassVar[AnnotationForm]
     def copy(self) -> Self: ...
     # Using Never so that only calls using mypy plugin hook that specialize the signature
     # can go through.
@@ -254,41 +253,39 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta):
     def keys(self) -> dict_keys[str, object]: ...
     def values(self) -> dict_values[str, object]: ...
     def __delitem__(self, k: Never) -> None: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def __or__(self, value: Self, /) -> Self: ...
-        @overload
-        def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
-        @overload
-        def __ror__(self, value: Self, /) -> Self: ...
-        @overload
-        def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
-        # supposedly incompatible definitions of `__ior__` and `__or__`:
-        # Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self
-        def __ior__(self, value: Self, /) -> Self: ...  # type: ignore[misc]
+    @overload
+    def __or__(self, value: Self, /) -> Self: ...
+    @overload
+    def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    @overload
+    def __ror__(self, value: Self, /) -> Self: ...
+    @overload
+    def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    # supposedly incompatible definitions of `__ior__` and `__or__`:
+    # Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self
+    def __ior__(self, value: Self, /) -> Self: ...  # type: ignore[misc]
 
 OrderedDict = _Alias()
 
-def get_type_hints(
-    obj: Callable[..., Any],
-    globalns: dict[str, Any] | None = None,
-    localns: Mapping[str, Any] | None = None,
-    include_extras: bool = False,
-) -> dict[str, Any]: ...
-def get_args(tp: Any) -> tuple[Any, ...]: ...
+if sys.version_info >= (3, 13):
+    from typing import get_type_hints as get_type_hints
+else:
+    def get_type_hints(
+        obj: Any, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = False
+    ) -> dict[str, AnnotationForm]: ...
+
+def get_args(tp: AnnotationForm) -> tuple[AnnotationForm, ...]: ...
 
 if sys.version_info >= (3, 10):
     @overload
     def get_origin(tp: UnionType) -> type[UnionType]: ...
 
-if sys.version_info >= (3, 9):
-    @overload
-    def get_origin(tp: GenericAlias) -> type: ...
-
+@overload
+def get_origin(tp: GenericAlias) -> type: ...
 @overload
 def get_origin(tp: ParamSpecArgs | ParamSpecKwargs) -> ParamSpec: ...
 @overload
-def get_origin(tp: Any) -> Any | None: ...
+def get_origin(tp: AnnotationForm) -> AnnotationForm | None: ...
 
 Annotated: _SpecialForm
 _AnnotatedAlias: Any  # undocumented
@@ -344,7 +341,7 @@ else:
     Never: _SpecialForm
     def reveal_type(obj: _T, /) -> _T: ...
     def assert_never(arg: Never, /) -> Never: ...
-    def assert_type(val: _T, typ: Any, /) -> _T: ...
+    def assert_type(val: _T, typ: AnnotationForm, /) -> _T: ...
     def clear_overloads() -> None: ...
     def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ...
 
@@ -364,8 +361,6 @@ else:
     ) -> IdentityFunction: ...
 
     class NamedTuple(tuple[Any, ...]):
-        if sys.version_info < (3, 9):
-            _field_types: ClassVar[dict[str, type]]
         _field_defaults: ClassVar[dict[str, Any]]
         _fields: ClassVar[tuple[str, ...]]
         __orig_bases__: ClassVar[tuple[Any, ...]]
@@ -379,7 +374,7 @@ else:
         def _replace(self, **kwargs: Any) -> Self: ...
 
     class NewType:
-        def __init__(self, name: str, tp: Any) -> None: ...
+        def __init__(self, name: str, tp: AnnotationForm) -> None: ...
         def __call__(self, obj: _T, /) -> _T: ...
         __supertype__: type | NewType
         if sys.version_info >= (3, 10):
@@ -486,9 +481,9 @@ else:
         @property
         def __name__(self) -> str: ...
         @property
-        def __bound__(self) -> Any | None: ...
+        def __bound__(self) -> AnnotationForm | None: ...
         @property
-        def __constraints__(self) -> tuple[Any, ...]: ...
+        def __constraints__(self) -> tuple[AnnotationForm, ...]: ...
         @property
         def __covariant__(self) -> bool: ...
         @property
@@ -496,15 +491,15 @@ else:
         @property
         def __infer_variance__(self) -> bool: ...
         @property
-        def __default__(self) -> Any: ...
+        def __default__(self) -> AnnotationForm: ...
         def __init__(
             self,
             name: str,
-            *constraints: Any,
-            bound: Any | None = None,
+            *constraints: AnnotationForm,
+            bound: AnnotationForm | None = None,
             covariant: bool = False,
             contravariant: bool = False,
-            default: Any = ...,
+            default: AnnotationForm = ...,
             infer_variance: bool = False,
         ) -> None: ...
         def has_default(self) -> bool: ...
@@ -520,7 +515,7 @@ else:
         @property
         def __name__(self) -> str: ...
         @property
-        def __bound__(self) -> Any | None: ...
+        def __bound__(self) -> AnnotationForm | None: ...
         @property
         def __covariant__(self) -> bool: ...
         @property
@@ -528,15 +523,15 @@ else:
         @property
         def __infer_variance__(self) -> bool: ...
         @property
-        def __default__(self) -> Any: ...
+        def __default__(self) -> AnnotationForm: ...
         def __init__(
             self,
             name: str,
             *,
-            bound: None | type[Any] | str = None,
+            bound: None | AnnotationForm | str = None,
             contravariant: bool = False,
             covariant: bool = False,
-            default: Any = ...,
+            default: AnnotationForm = ...,
         ) -> None: ...
         @property
         def args(self) -> ParamSpecArgs: ...
@@ -553,8 +548,8 @@ else:
         @property
         def __name__(self) -> str: ...
         @property
-        def __default__(self) -> Any: ...
-        def __init__(self, name: str, *, default: Any = ...) -> None: ...
+        def __default__(self) -> AnnotationForm: ...
+        def __init__(self, name: str, *, default: AnnotationForm = ...) -> None: ...
         def __iter__(self) -> Any: ...  # Unpack[Self]
         def has_default(self) -> bool: ...
         def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
@@ -569,23 +564,23 @@ else:
     @final
     class TypeAliasType:
         def __init__(
-            self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
-        ) -> None: ...  # value is a type expression
+            self, name: str, value: AnnotationForm, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
+        ) -> None: ...
         @property
-        def __value__(self) -> Any: ...  # a type expression
+        def __value__(self) -> AnnotationForm: ...
         @property
         def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ...
         @property
         # `__parameters__` can include special forms if a `TypeVarTuple` was
         # passed as a `type_params` element to the constructor method.
-        def __parameters__(self) -> tuple[TypeVar | ParamSpec | Any, ...]: ...
+        def __parameters__(self) -> tuple[TypeVar | ParamSpec | AnnotationForm, ...]: ...
         @property
         def __name__(self) -> str: ...
         # It's writable on types, but not on instances of TypeAliasType.
         @property
         def __module__(self) -> str | None: ...  # type: ignore[override]
         # Returns typing._GenericAlias, which isn't stubbed.
-        def __getitem__(self, parameters: Incomplete | tuple[Incomplete, ...]) -> Any: ...
+        def __getitem__(self, parameters: Incomplete | tuple[Incomplete, ...]) -> AnnotationForm: ...
         def __init_subclass__(cls, *args: Unused, **kwargs: Unused) -> NoReturn: ...
         if sys.version_info >= (3, 10):
             def __or__(self, right: Any) -> _SpecialForm: ...
@@ -606,27 +601,75 @@ NoExtraItems: _NoExtraItemsType
 # PEP 747
 TypeForm: _SpecialForm
 
-class Format(enum.IntEnum):
-    VALUE = 1
-    FORWARDREF = 2
-    STRING = 3
-
 # PEP 649/749
-def get_annotations(
-    obj: Callable[..., object] | type[object] | ModuleType,  # any callable, class, or module
-    *,
-    globals: Mapping[str, Any] | None = None,  # value types depend on the key
-    locals: Mapping[str, Any] | None = None,  # value types depend on the key
-    eval_str: bool = False,
-    format: Format = Format.VALUE,  # noqa: Y011
-) -> dict[str, Any]: ...  # values are type expressions
-def evaluate_forward_ref(
-    forward_ref: ForwardRef,
-    *,
-    owner: Callable[..., object] | type[object] | ModuleType | None = None,  # any callable, class, or module
-    globals: Mapping[str, Any] | None = None,  # value types depend on the key
-    locals: Mapping[str, Any] | None = None,  # value types depend on the key
-    type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
-    format: Format = Format.VALUE,  # noqa: Y011
-    _recursive_guard: Container[str] = ...,
-) -> Any: ...  # str if format is Format.STRING, otherwise a type expression
+if sys.version_info >= (3, 14):
+    from typing import evaluate_forward_ref as evaluate_forward_ref
+
+    from annotationlib import Format as Format, get_annotations as get_annotations
+else:
+    class Format(enum.IntEnum):
+        VALUE = 1
+        VALUE_WITH_FAKE_GLOBALS = 2
+        FORWARDREF = 3
+        STRING = 4
+
+    @overload
+    def get_annotations(
+        obj: Any,  # any object with __annotations__ or __annotate__
+        *,
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        eval_str: bool = False,
+        format: Literal[Format.STRING],
+    ) -> dict[str, str]: ...
+    @overload
+    def get_annotations(
+        obj: Any,  # any object with __annotations__ or __annotate__
+        *,
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        eval_str: bool = False,
+        format: Literal[Format.FORWARDREF],
+    ) -> dict[str, AnnotationForm | ForwardRef]: ...
+    @overload
+    def get_annotations(
+        obj: Any,  # any object with __annotations__ or __annotate__
+        *,
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        eval_str: bool = False,
+        format: Format = Format.VALUE,  # noqa: Y011
+    ) -> dict[str, AnnotationForm]: ...
+    @overload
+    def evaluate_forward_ref(
+        forward_ref: ForwardRef,
+        *,
+        owner: Callable[..., object] | type[object] | ModuleType | None = None,  # any callable, class, or module
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
+        format: Literal[Format.STRING],
+        _recursive_guard: Container[str] = ...,
+    ) -> str: ...
+    @overload
+    def evaluate_forward_ref(
+        forward_ref: ForwardRef,
+        *,
+        owner: Callable[..., object] | type[object] | ModuleType | None = None,  # any callable, class, or module
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
+        format: Literal[Format.FORWARDREF],
+        _recursive_guard: Container[str] = ...,
+    ) -> AnnotationForm | ForwardRef: ...
+    @overload
+    def evaluate_forward_ref(
+        forward_ref: ForwardRef,
+        *,
+        owner: Callable[..., object] | type[object] | ModuleType | None = None,  # any callable, class, or module
+        globals: Mapping[str, Any] | None = None,  # value types depend on the key
+        locals: Mapping[str, Any] | None = None,  # value types depend on the key
+        type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
+        format: Format = Format.VALUE,  # noqa: Y011
+        _recursive_guard: Container[str] = ...,
+    ) -> AnnotationForm: ...
diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi
index 565dd91c0fda..0b3fb9122c7b 100644
--- a/mypy/typeshed/stdlib/unittest/async_case.pyi
+++ b/mypy/typeshed/stdlib/unittest/async_case.pyi
@@ -21,5 +21,5 @@ class IsolatedAsyncioTestCase(TestCase):
     def addAsyncCleanup(self, func: Callable[_P, Awaitable[object]], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ...
     if sys.version_info >= (3, 11):
         async def enterAsyncContext(self, cm: AbstractAsyncContextManager[_T]) -> _T: ...
-    if sys.version_info >= (3, 9):
-        def __del__(self) -> None: ...
+
+    def __del__(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi
index 33cd556d2e3b..89bcabf104c2 100644
--- a/mypy/typeshed/stdlib/unittest/case.pyi
+++ b/mypy/typeshed/stdlib/unittest/case.pyi
@@ -5,27 +5,12 @@ from _typeshed import SupportsDunderGE, SupportsDunderGT, SupportsDunderLE, Supp
 from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Set as AbstractSet
 from contextlib import AbstractContextManager
 from re import Pattern
-from types import TracebackType
-from typing import (
-    Any,
-    AnyStr,
-    ClassVar,
-    Final,
-    Generic,
-    NamedTuple,
-    NoReturn,
-    Protocol,
-    SupportsAbs,
-    SupportsRound,
-    TypeVar,
-    overload,
-)
+from types import GenericAlias, TracebackType
+from typing import Any, AnyStr, Final, Generic, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload
 from typing_extensions import Never, ParamSpec, Self, TypeAlias
+from unittest._log import _AssertLogsContext, _LoggingWatcher
 from warnings import WarningMessage
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 if sys.version_info >= (3, 10):
     from types import UnionType
 
@@ -33,6 +18,7 @@ _T = TypeVar("_T")
 _S = TypeVar("_S", bound=SupportsSub[Any, Any])
 _E = TypeVar("_E", bound=BaseException)
 _FT = TypeVar("_FT", bound=Callable[..., Any])
+_SB = TypeVar("_SB", str, bytes, bytearray)
 _P = ParamSpec("_P")
 
 DIFF_OMITTED: Final[str]
@@ -58,29 +44,6 @@ class _AssertRaisesBaseContext(_BaseTestCaseContext):
     # but it's not possible to construct an overload which expresses that
     def handle(self, name: str, args: list[Any], kwargs: dict[str, Any]) -> Any: ...
 
-if sys.version_info >= (3, 9):
-    from unittest._log import _AssertLogsContext, _LoggingWatcher
-else:
-    # Unused dummy for _AssertLogsContext. Starting with Python 3.10,
-    # this is generic over the logging watcher, but in lower versions
-    # the watcher is hard-coded.
-    _L = TypeVar("_L")
-
-    class _LoggingWatcher(NamedTuple):
-        records: list[logging.LogRecord]
-        output: list[str]
-
-    class _AssertLogsContext(_BaseTestCaseContext, Generic[_L]):
-        LOGGING_FORMAT: ClassVar[str]
-        logger_name: str
-        level: int
-        msg: None
-        def __init__(self, test_case: TestCase, logger_name: str, level: int) -> None: ...
-        def __enter__(self) -> _LoggingWatcher: ...
-        def __exit__(
-            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None
-        ) -> bool | None: ...
-
 def addModuleCleanup(function: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ...
 def doModuleCleanups() -> None: ...
 
@@ -327,6 +290,16 @@ class TestCase:
         # Runtime has *args, **kwargs, but will error if any are supplied
         def __init_subclass__(cls, *args: Never, **kwargs: Never) -> None: ...
 
+    if sys.version_info >= (3, 14):
+        def assertIsSubclass(self, cls: type, superclass: type | tuple[type, ...], msg: Any = None) -> None: ...
+        def assertNotIsSubclass(self, cls: type, superclass: type | tuple[type, ...], msg: Any = None) -> None: ...
+        def assertHasAttr(self, obj: object, name: str, msg: Any = None) -> None: ...
+        def assertNotHasAttr(self, obj: object, name: str, msg: Any = None) -> None: ...
+        def assertStartsWith(self, s: _SB, prefix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ...
+        def assertNotStartsWith(self, s: _SB, prefix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ...
+        def assertEndsWith(self, s: _SB, suffix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ...
+        def assertNotEndsWith(self, s: _SB, suffix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ...
+
 class FunctionTestCase(TestCase):
     def __init__(
         self,
@@ -345,8 +318,7 @@ class _AssertRaisesContext(_AssertRaisesBaseContext, Generic[_E]):
     def __exit__(
         self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None
     ) -> bool: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class _AssertWarnsContext(_AssertRaisesBaseContext):
     warning: WarningMessage
diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi
index 4b32f15095d6..9e353900f2d7 100644
--- a/mypy/typeshed/stdlib/unittest/mock.pyi
+++ b/mypy/typeshed/stdlib/unittest/mock.pyi
@@ -1,4 +1,5 @@
 import sys
+from _typeshed import MaybeNone
 from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, Sequence
 from contextlib import _GeneratorContextManager
 from types import TracebackType
@@ -51,9 +52,6 @@ else:
         "seal",
     )
 
-if sys.version_info < (3, 9):
-    __version__: Final[str]
-
 FILTER_DIR: Any
 
 class _SentinelObject:
@@ -72,16 +70,13 @@ _CallValue: TypeAlias = str | tuple[Any, ...] | Mapping[str, Any] | _ArgsKwargs
 
 class _Call(tuple[Any, ...]):
     def __new__(
-        cls, value: _CallValue = (), name: str | None = "", parent: Any | None = None, two: bool = False, from_kall: bool = True
+        cls, value: _CallValue = (), name: str | None = "", parent: _Call | None = None, two: bool = False, from_kall: bool = True
     ) -> Self: ...
-    name: Any
-    parent: Any
-    from_kall: Any
     def __init__(
         self,
         value: _CallValue = (),
         name: str | None = None,
-        parent: Any | None = None,
+        parent: _Call | None = None,
         two: bool = False,
         from_kall: bool = True,
     ) -> None: ...
@@ -165,7 +160,7 @@ class NonCallableMock(Base, Any):
     side_effect: Any
     called: bool
     call_count: int
-    call_args: Any
+    call_args: _Call | MaybeNone
     call_args_list: _CallList
     mock_calls: _CallList
     def _format_mock_call_signature(self, args: Any, kwargs: Any) -> str: ...
diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi
index 785bb9678ec7..a5ed616d25af 100644
--- a/mypy/typeshed/stdlib/urllib/parse.pyi
+++ b/mypy/typeshed/stdlib/urllib/parse.pyi
@@ -1,11 +1,9 @@
 import sys
-from collections.abc import Callable, Iterable, Mapping, Sequence
-from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload
+from collections.abc import Iterable, Mapping, Sequence
+from types import GenericAlias
+from typing import Any, AnyStr, Generic, Literal, NamedTuple, Protocol, overload, type_check_only
 from typing_extensions import TypeAlias
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "urlparse",
     "urlunparse",
@@ -55,8 +53,7 @@ class _NetlocResultMixinBase(Generic[AnyStr]):
     def hostname(self) -> AnyStr | None: ...
     @property
     def port(self) -> int | None: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): ...
 class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): ...
@@ -127,13 +124,7 @@ def quote_from_bytes(bs: bytes | bytearray, safe: str | Iterable[int] = "/") ->
 def quote_plus(string: str, safe: str | Iterable[int] = "", encoding: str | None = None, errors: str | None = None) -> str: ...
 @overload
 def quote_plus(string: bytes | bytearray, safe: str | Iterable[int] = "") -> str: ...
-
-if sys.version_info >= (3, 9):
-    def unquote(string: str | bytes, encoding: str = "utf-8", errors: str = "replace") -> str: ...
-
-else:
-    def unquote(string: str, encoding: str = "utf-8", errors: str = "replace") -> str: ...
-
+def unquote(string: str | bytes, encoding: str = "utf-8", errors: str = "replace") -> str: ...
 def unquote_to_bytes(string: str | bytes | bytearray) -> bytes: ...
 def unquote_plus(string: str, encoding: str = "utf-8", errors: str = "replace") -> str: ...
 @overload
@@ -141,38 +132,32 @@ def urldefrag(url: str) -> DefragResult: ...
 @overload
 def urldefrag(url: bytes | bytearray | None) -> DefragResultBytes: ...
 
-_Q = TypeVar("_Q", bound=str | Iterable[int])
+# The values are passed through `str()` (unless they are bytes), so anything is valid.
 _QueryType: TypeAlias = (
-    Mapping[Any, Any] | Mapping[Any, Sequence[Any]] | Sequence[tuple[Any, Any]] | Sequence[tuple[Any, Sequence[Any]]]
+    Mapping[str, object]
+    | Mapping[bytes, object]
+    | Mapping[str | bytes, object]
+    | Mapping[str, Sequence[object]]
+    | Mapping[bytes, Sequence[object]]
+    | Mapping[str | bytes, Sequence[object]]
+    | Sequence[tuple[str | bytes, object]]
+    | Sequence[tuple[str | bytes, Sequence[object]]]
 )
 
-@overload
-def urlencode(
-    query: _QueryType,
-    doseq: bool = False,
-    safe: str = "",
-    encoding: str | None = None,
-    errors: str | None = None,
-    quote_via: Callable[[AnyStr, str, str, str], str] = ...,
-) -> str: ...
-@overload
-def urlencode(
-    query: _QueryType,
-    doseq: bool,
-    safe: _Q,
-    encoding: str | None = None,
-    errors: str | None = None,
-    quote_via: Callable[[AnyStr, _Q, str, str], str] = ...,
-) -> str: ...
-@overload
+@type_check_only
+class _QuoteVia(Protocol):
+    @overload
+    def __call__(self, string: str, safe: str | bytes, encoding: str, errors: str, /) -> str: ...
+    @overload
+    def __call__(self, string: bytes, safe: str | bytes, /) -> str: ...
+
 def urlencode(
     query: _QueryType,
     doseq: bool = False,
-    *,
-    safe: _Q,
+    safe: str | bytes = "",
     encoding: str | None = None,
     errors: str | None = None,
-    quote_via: Callable[[AnyStr, _Q, str, str], str] = ...,
+    quote_via: _QuoteVia = ...,
 ) -> str: ...
 def urljoin(base: AnyStr, url: AnyStr | None, allow_fragments: bool = True) -> AnyStr: ...
 @overload
diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi
index ad4f91fc31ae..d8fc5e0d8f48 100644
--- a/mypy/typeshed/stdlib/urllib/request.pyi
+++ b/mypy/typeshed/stdlib/urllib/request.pyi
@@ -7,7 +7,7 @@ from http.client import HTTPConnection, HTTPMessage, HTTPResponse
 from http.cookiejar import CookieJar
 from re import Pattern
 from typing import IO, Any, ClassVar, NoReturn, Protocol, TypeVar, overload
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 from urllib.error import HTTPError as HTTPError
 from urllib.response import addclosehook, addinfourl
 
@@ -43,10 +43,10 @@ __all__ = [
     "getproxies",
     "urlretrieve",
     "urlcleanup",
-    "URLopener",
-    "FancyURLopener",
     "HTTPSHandler",
 ]
+if sys.version_info < (3, 14):
+    __all__ += ["URLopener", "FancyURLopener"]
 
 _T = TypeVar("_T")
 _UrlopenRet: TypeAlias = Any
@@ -72,11 +72,16 @@ else:
 def install_opener(opener: OpenerDirector) -> None: ...
 def build_opener(*handlers: BaseHandler | Callable[[], BaseHandler]) -> OpenerDirector: ...
 
-if sys.platform == "win32":
-    from nturl2path import pathname2url as pathname2url, url2pathname as url2pathname
+if sys.version_info >= (3, 14):
+    def url2pathname(url: str, *, require_scheme: bool = False, resolve_host: bool = False) -> str: ...
+    def pathname2url(pathname: str, *, add_scheme: bool = False) -> str: ...
+
 else:
-    def url2pathname(pathname: str) -> str: ...
-    def pathname2url(pathname: str) -> str: ...
+    if sys.platform == "win32":
+        from nturl2path import pathname2url as pathname2url, url2pathname as url2pathname
+    else:
+        def url2pathname(pathname: str) -> str: ...
+        def pathname2url(pathname: str) -> str: ...
 
 def getproxies() -> dict[str, str]: ...
 def getproxies_environment() -> dict[str, str]: ...
@@ -175,7 +180,7 @@ class HTTPCookieProcessor(BaseHandler):
 class ProxyHandler(BaseHandler):
     def __init__(self, proxies: dict[str, str] | None = None) -> None: ...
     def proxy_open(self, req: Request, proxy: str, type: str) -> _UrlopenRet | None: ...  # undocumented
-    # TODO add a method for every (common) proxy protocol
+    # TODO: add a method for every (common) proxy protocol
 
 class HTTPPasswordMgr:
     def add_password(self, realm: str, uri: str | Sequence[str], user: str, passwd: str) -> None: ...
@@ -318,91 +323,94 @@ def urlretrieve(
 ) -> tuple[str, HTTPMessage]: ...
 def urlcleanup() -> None: ...
 
-class URLopener:
-    version: ClassVar[str]
-    def __init__(self, proxies: dict[str, str] | None = None, **x509: str) -> None: ...
-    def open(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...
-    def open_unknown(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...
-    def retrieve(
-        self,
-        url: str,
-        filename: str | None = None,
-        reporthook: Callable[[int, int, int], object] | None = None,
-        data: ReadableBuffer | None = None,
-    ) -> tuple[str, Message | None]: ...
-    def addheader(self, *args: tuple[str, str]) -> None: ...  # undocumented
-    def cleanup(self) -> None: ...  # undocumented
-    def close(self) -> None: ...  # undocumented
-    def http_error(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: bytes | None = None
-    ) -> _UrlopenRet: ...  # undocumented
-    def http_error_default(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage
-    ) -> _UrlopenRet: ...  # undocumented
-    def open_data(self, url: str, data: ReadableBuffer | None = None) -> addinfourl: ...  # undocumented
-    def open_file(self, url: str) -> addinfourl: ...  # undocumented
-    def open_ftp(self, url: str) -> addinfourl: ...  # undocumented
-    def open_http(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...  # undocumented
-    def open_https(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...  # undocumented
-    def open_local_file(self, url: str) -> addinfourl: ...  # undocumented
-    def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ...  # undocumented
-    def __del__(self) -> None: ...
-
-class FancyURLopener(URLopener):
-    def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ...
-    def get_user_passwd(self, host: str, realm: str, clear_cache: int = 0) -> tuple[str, str]: ...  # undocumented
-    def http_error_301(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
-    def http_error_302(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
-    def http_error_303(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
-    def http_error_307(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
-    if sys.version_info >= (3, 11):
-        def http_error_308(
+if sys.version_info < (3, 14):
+    @deprecated("Deprecated since Python 3.3; Removed in 3.14; Use newer urlopen functions and methods.")
+    class URLopener:
+        version: ClassVar[str]
+        def __init__(self, proxies: dict[str, str] | None = None, **x509: str) -> None: ...
+        def open(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...
+        def open_unknown(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...
+        def retrieve(
+            self,
+            url: str,
+            filename: str | None = None,
+            reporthook: Callable[[int, int, int], object] | None = None,
+            data: ReadableBuffer | None = None,
+        ) -> tuple[str, Message | None]: ...
+        def addheader(self, *args: tuple[str, str]) -> None: ...  # undocumented
+        def cleanup(self) -> None: ...  # undocumented
+        def close(self) -> None: ...  # undocumented
+        def http_error(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: bytes | None = None
+        ) -> _UrlopenRet: ...  # undocumented
+        def http_error_default(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage
+        ) -> _UrlopenRet: ...  # undocumented
+        def open_data(self, url: str, data: ReadableBuffer | None = None) -> addinfourl: ...  # undocumented
+        def open_file(self, url: str) -> addinfourl: ...  # undocumented
+        def open_ftp(self, url: str) -> addinfourl: ...  # undocumented
+        def open_http(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...  # undocumented
+        def open_https(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ...  # undocumented
+        def open_local_file(self, url: str) -> addinfourl: ...  # undocumented
+        def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ...  # undocumented
+        def __del__(self) -> None: ...
+
+    @deprecated("Deprecated since Python 3.3; Removed in 3.14; Use newer urlopen functions and methods.")
+    class FancyURLopener(URLopener):
+        def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ...
+        def get_user_passwd(self, host: str, realm: str, clear_cache: int = 0) -> tuple[str, str]: ...  # undocumented
+        def http_error_301(
             self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
         ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
-
-    def http_error_401(
-        self,
-        url: str,
-        fp: IO[bytes],
-        errcode: int,
-        errmsg: str,
-        headers: HTTPMessage,
-        data: ReadableBuffer | None = None,
-        retry: bool = False,
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def http_error_407(
-        self,
-        url: str,
-        fp: IO[bytes],
-        errcode: int,
-        errmsg: str,
-        headers: HTTPMessage,
-        data: ReadableBuffer | None = None,
-        retry: bool = False,
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def http_error_default(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage
-    ) -> addinfourl: ...  # undocumented
-    def redirect_internal(
-        self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def retry_http_basic_auth(
-        self, url: str, realm: str, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def retry_https_basic_auth(
-        self, url: str, realm: str, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def retry_proxy_http_basic_auth(
-        self, url: str, realm: str, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | None: ...  # undocumented
-    def retry_proxy_https_basic_auth(
-        self, url: str, realm: str, data: ReadableBuffer | None = None
-    ) -> _UrlopenRet | None: ...  # undocumented
+        def http_error_302(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
+        def http_error_303(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
+        def http_error_307(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
+        if sys.version_info >= (3, 11):
+            def http_error_308(
+                self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None
+            ) -> _UrlopenRet | addinfourl | None: ...  # undocumented
+
+        def http_error_401(
+            self,
+            url: str,
+            fp: IO[bytes],
+            errcode: int,
+            errmsg: str,
+            headers: HTTPMessage,
+            data: ReadableBuffer | None = None,
+            retry: bool = False,
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def http_error_407(
+            self,
+            url: str,
+            fp: IO[bytes],
+            errcode: int,
+            errmsg: str,
+            headers: HTTPMessage,
+            data: ReadableBuffer | None = None,
+            retry: bool = False,
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def http_error_default(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage
+        ) -> addinfourl: ...  # undocumented
+        def redirect_internal(
+            self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def retry_http_basic_auth(
+            self, url: str, realm: str, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def retry_https_basic_auth(
+            self, url: str, realm: str, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def retry_proxy_http_basic_auth(
+            self, url: str, realm: str, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | None: ...  # undocumented
+        def retry_proxy_https_basic_auth(
+            self, url: str, realm: str, data: ReadableBuffer | None = None
+        ) -> _UrlopenRet | None: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/urllib/response.pyi b/mypy/typeshed/stdlib/urllib/response.pyi
index bbec4cacc750..65df9cdff58f 100644
--- a/mypy/typeshed/stdlib/urllib/response.pyi
+++ b/mypy/typeshed/stdlib/urllib/response.pyi
@@ -1,4 +1,3 @@
-import sys
 import tempfile
 from _typeshed import ReadableBuffer
 from collections.abc import Callable, Iterable
@@ -34,10 +33,8 @@ class addinfo(addbase):
 class addinfourl(addinfo):
     url: str
     code: int | None
-    if sys.version_info >= (3, 9):
-        @property
-        def status(self) -> int | None: ...
-
+    @property
+    def status(self) -> int | None: ...
     def __init__(self, fp: IO[bytes], headers: Message, url: str, code: int | None = None) -> None: ...
     def geturl(self) -> str: ...
     def getcode(self) -> int | None: ...
diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi
index 1be7a5ef009f..99ac6eb223ef 100644
--- a/mypy/typeshed/stdlib/uuid.pyi
+++ b/mypy/typeshed/stdlib/uuid.pyi
@@ -1,8 +1,8 @@
 import builtins
 import sys
-from _typeshed import Unused
 from enum import Enum
-from typing_extensions import TypeAlias
+from typing import Final
+from typing_extensions import LiteralString, TypeAlias
 
 _FieldsType: TypeAlias = tuple[int, int, int, int, int, int]
 
@@ -65,14 +65,14 @@ class UUID:
     def __ge__(self, other: UUID) -> bool: ...
     def __hash__(self) -> builtins.int: ...
 
-if sys.version_info >= (3, 9):
-    def getnode() -> int: ...
-
-else:
-    def getnode(*, getters: Unused = None) -> int: ...  # undocumented
-
+def getnode() -> int: ...
 def uuid1(node: int | None = None, clock_seq: int | None = None) -> UUID: ...
 
+if sys.version_info >= (3, 14):
+    def uuid6(node: int | None = None, clock_seq: int | None = None) -> UUID: ...
+    def uuid7() -> UUID: ...
+    def uuid8(a: int | None = None, b: int | None = None, c: int | None = None) -> UUID: ...
+
 if sys.version_info >= (3, 12):
     def uuid3(namespace: UUID, name: str | bytes) -> UUID: ...
 
@@ -87,14 +87,18 @@ if sys.version_info >= (3, 12):
 else:
     def uuid5(namespace: UUID, name: str) -> UUID: ...
 
-NAMESPACE_DNS: UUID
-NAMESPACE_URL: UUID
-NAMESPACE_OID: UUID
-NAMESPACE_X500: UUID
-RESERVED_NCS: str
-RFC_4122: str
-RESERVED_MICROSOFT: str
-RESERVED_FUTURE: str
+if sys.version_info >= (3, 14):
+    NIL: Final[UUID]
+    MAX: Final[UUID]
+
+NAMESPACE_DNS: Final[UUID]
+NAMESPACE_URL: Final[UUID]
+NAMESPACE_OID: Final[UUID]
+NAMESPACE_X500: Final[UUID]
+RESERVED_NCS: Final[LiteralString]
+RFC_4122: Final[LiteralString]
+RESERVED_MICROSOFT: Final[LiteralString]
+RESERVED_FUTURE: Final[LiteralString]
 
 if sys.version_info >= (3, 12):
     def main() -> None: ...
diff --git a/mypy/typeshed/stdlib/venv/__init__.pyi b/mypy/typeshed/stdlib/venv/__init__.pyi
index 0490c35b44f2..0f71f0e073f5 100644
--- a/mypy/typeshed/stdlib/venv/__init__.pyi
+++ b/mypy/typeshed/stdlib/venv/__init__.pyi
@@ -6,8 +6,7 @@ from types import SimpleNamespace
 
 logger: logging.Logger
 
-if sys.version_info >= (3, 9):
-    CORE_VENV_DEPS: tuple[str, ...]
+CORE_VENV_DEPS: tuple[str, ...]
 
 class EnvBuilder:
     system_site_packages: bool
@@ -30,17 +29,6 @@ class EnvBuilder:
             *,
             scm_ignore_files: Iterable[str] = ...,
         ) -> None: ...
-    elif sys.version_info >= (3, 9):
-        def __init__(
-            self,
-            system_site_packages: bool = False,
-            clear: bool = False,
-            symlinks: bool = False,
-            upgrade: bool = False,
-            with_pip: bool = False,
-            prompt: str | None = None,
-            upgrade_deps: bool = False,
-        ) -> None: ...
     else:
         def __init__(
             self,
@@ -50,6 +38,7 @@ class EnvBuilder:
             upgrade: bool = False,
             with_pip: bool = False,
             prompt: str | None = None,
+            upgrade_deps: bool = False,
         ) -> None: ...
 
     def create(self, env_dir: StrOrBytesPath) -> None: ...
@@ -65,8 +54,7 @@ class EnvBuilder:
     def post_setup(self, context: SimpleNamespace) -> None: ...
     def replace_variables(self, text: str, context: SimpleNamespace) -> str: ...  # undocumented
     def install_scripts(self, context: SimpleNamespace, path: str) -> None: ...
-    if sys.version_info >= (3, 9):
-        def upgrade_dependencies(self, context: SimpleNamespace) -> None: ...
+    def upgrade_dependencies(self, context: SimpleNamespace) -> None: ...
     if sys.version_info >= (3, 13):
         def create_git_ignore_file(self, context: SimpleNamespace) -> None: ...
 
@@ -83,17 +71,6 @@ if sys.version_info >= (3, 13):
         scm_ignore_files: Iterable[str] = ...,
     ) -> None: ...
 
-elif sys.version_info >= (3, 9):
-    def create(
-        env_dir: StrOrBytesPath,
-        system_site_packages: bool = False,
-        clear: bool = False,
-        symlinks: bool = False,
-        with_pip: bool = False,
-        prompt: str | None = None,
-        upgrade_deps: bool = False,
-    ) -> None: ...
-
 else:
     def create(
         env_dir: StrOrBytesPath,
@@ -102,6 +79,7 @@ else:
         symlinks: bool = False,
         with_pip: bool = False,
         prompt: str | None = None,
+        upgrade_deps: bool = False,
     ) -> None: ...
 
 def main(args: Sequence[str] | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi
index 9319d5347c79..ddc6f6bd02a5 100644
--- a/mypy/typeshed/stdlib/wave.pyi
+++ b/mypy/typeshed/stdlib/wave.pyi
@@ -1,12 +1,8 @@
-import sys
 from _typeshed import ReadableBuffer, Unused
 from typing import IO, Any, BinaryIO, Final, Literal, NamedTuple, NoReturn, overload
 from typing_extensions import Self, TypeAlias, deprecated
 
-if sys.version_info >= (3, 9):
-    __all__ = ["open", "Error", "Wave_read", "Wave_write"]
-else:
-    __all__ = ["open", "openfp", "Error", "Wave_read", "Wave_write"]
+__all__ = ["open", "Error", "Wave_read", "Wave_write"]
 
 _File: TypeAlias = str | IO[bytes]
 
@@ -80,6 +76,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Wave_read: ...
 def open(f: _File, mode: Literal["w", "wb"]) -> Wave_write: ...
 @overload
 def open(f: _File, mode: str | None = None) -> Any: ...
-
-if sys.version_info < (3, 9):
-    openfp = open
diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi
index 05a7b2bcda66..593eb4615c8f 100644
--- a/mypy/typeshed/stdlib/weakref.pyi
+++ b/mypy/typeshed/stdlib/weakref.pyi
@@ -1,14 +1,11 @@
-import sys
 from _typeshed import SupportsKeysAndGetItem
 from _weakref import getweakrefcount as getweakrefcount, getweakrefs as getweakrefs, proxy as proxy
 from _weakrefset import WeakSet as WeakSet
 from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping
+from types import GenericAlias
 from typing import Any, ClassVar, Generic, TypeVar, final, overload
 from typing_extensions import ParamSpec, Self
 
-if sys.version_info >= (3, 9):
-    from types import GenericAlias
-
 __all__ = [
     "ref",
     "proxy",
@@ -61,8 +58,7 @@ class ReferenceType(Generic[_T]):  # "weakref"
     def __call__(self) -> _T | None: ...
     def __eq__(self, value: object, /) -> bool: ...
     def __hash__(self) -> int: ...
-    if sys.version_info >= (3, 9):
-        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 ref = ReferenceType
 
@@ -123,14 +119,13 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]):
     def update(self, other: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ...
     @overload
     def update(self, other: None = None, /, **kwargs: _VT) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ...
-        def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ...
-        # WeakValueDictionary.__ior__ should be kept roughly in line with MutableMapping.update()
-        @overload  # type: ignore[misc]
-        def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
-        @overload
-        def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
+    def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ...
+    def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ...
+    # WeakValueDictionary.__ior__ should be kept roughly in line with MutableMapping.update()
+    @overload  # type: ignore[misc]
+    def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
+    @overload
+    def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
 
 class KeyedRef(ref[_T], Generic[_KT, _T]):
     key: _KT
@@ -177,14 +172,13 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]):
     def update(self, dict: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ...
     @overload
     def update(self, dict: None = None, /, **kwargs: _VT) -> None: ...
-    if sys.version_info >= (3, 9):
-        def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ...
-        def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ...
-        # WeakKeyDictionary.__ior__ should be kept roughly in line with MutableMapping.update()
-        @overload  # type: ignore[misc]
-        def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
-        @overload
-        def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
+    def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ...
+    def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ...
+    # WeakKeyDictionary.__ior__ should be kept roughly in line with MutableMapping.update()
+    @overload  # type: ignore[misc]
+    def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ...
+    @overload
+    def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
 
 class finalize(Generic[_P, _T]):
     def __init__(self, obj: _T, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ...
diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi
index a20e81f94f98..39dfa7b8b9c4 100644
--- a/mypy/typeshed/stdlib/winsound.pyi
+++ b/mypy/typeshed/stdlib/winsound.pyi
@@ -13,12 +13,22 @@ if sys.platform == "win32":
     SND_NODEFAULT: Final = 2
     SND_NOSTOP: Final = 16
     SND_NOWAIT: Final = 8192
+    if sys.version_info >= (3, 14):
+        SND_SENTRY: Final = 524288
+        SND_SYNC: Final = 0
+        SND_SYSTEM: Final = 2097152
 
     MB_ICONASTERISK: Final = 64
     MB_ICONEXCLAMATION: Final = 48
     MB_ICONHAND: Final = 16
     MB_ICONQUESTION: Final = 32
     MB_OK: Final = 0
+    if sys.version_info >= (3, 14):
+        MB_ICONERROR: Final = 16
+        MB_ICONINFORMATION: Final = 64
+        MB_ICONSTOP: Final = 16
+        MB_ICONWARNING: Final = 48
+
     def Beep(frequency: int, duration: int) -> None: ...
     # Can actually accept anything ORed with 4, and if not it's definitely str, but that's inexpressible
     @overload
diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
index 51bbf4993657..ab2ef87e38a8 100644
--- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
@@ -1,4 +1,3 @@
-import sys
 import xml.dom
 from _collections_abc import dict_keys, dict_values
 from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite
@@ -88,71 +87,39 @@ class Node(xml.dom.Node):
     @property
     def localName(self) -> str | None: ...  # non-null only for Element and Attr
     def __bool__(self) -> Literal[True]: ...
-    if sys.version_info >= (3, 9):
-        @overload
-        def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ...
-        @overload
-        def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str = "\t",
-            newl: str = "\n",
-            # Handle any case where encoding is not provided or where it is passed with None
-            encoding: None = None,
-            standalone: bool | None = None,
-        ) -> str: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str,
-            newl: str,
-            # Handle cases where encoding is passed as str *positionally*
-            encoding: str,
-            standalone: bool | None = None,
-        ) -> bytes: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str = "\t",
-            newl: str = "\n",
-            # Handle all cases where encoding is passed as a keyword argument; because standalone
-            # comes after, it will also have to be a keyword arg if encoding is
-            *,
-            encoding: str,
-            standalone: bool | None = None,
-        ) -> bytes: ...
-    else:
-        @overload
-        def toxml(self, encoding: str) -> bytes: ...
-        @overload
-        def toxml(self, encoding: None = None) -> str: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str = "\t",
-            newl: str = "\n",
-            # Handle any case where encoding is not provided or where it is passed with None
-            encoding: None = None,
-        ) -> str: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str,
-            newl: str,
-            # Handle cases where encoding is passed as str *positionally*
-            encoding: str,
-        ) -> bytes: ...
-        @overload
-        def toprettyxml(
-            self,
-            indent: str = "\t",
-            newl: str = "\n",
-            # Handle all cases where encoding is passed as a keyword argument
-            *,
-            encoding: str,
-        ) -> bytes: ...
-
+    @overload
+    def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ...
+    @overload
+    def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ...
+    @overload
+    def toprettyxml(
+        self,
+        indent: str = "\t",
+        newl: str = "\n",
+        # Handle any case where encoding is not provided or where it is passed with None
+        encoding: None = None,
+        standalone: bool | None = None,
+    ) -> str: ...
+    @overload
+    def toprettyxml(
+        self,
+        indent: str,
+        newl: str,
+        # Handle cases where encoding is passed as str *positionally*
+        encoding: str,
+        standalone: bool | None = None,
+    ) -> bytes: ...
+    @overload
+    def toprettyxml(
+        self,
+        indent: str = "\t",
+        newl: str = "\n",
+        # Handle all cases where encoding is passed as a keyword argument; because standalone
+        # comes after, it will also have to be a keyword arg if encoding is
+        *,
+        encoding: str,
+        standalone: bool | None = None,
+    ) -> bytes: ...
     def hasChildNodes(self) -> bool: ...
     def insertBefore(  # type: ignore[misc]
         self: _NodesWithChildren,  # pyright: ignore[reportGeneralTypeIssues]
@@ -657,26 +624,15 @@ class Document(Node, DocumentLS):
     def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ...
     def isSupported(self, feature: str, version: str | None) -> bool: ...
     def importNode(self, node: _ImportableNodeVar, deep: bool) -> _ImportableNodeVar: ...
-    if sys.version_info >= (3, 9):
-        def writexml(
-            self,
-            writer: SupportsWrite[str],
-            indent: str = "",
-            addindent: str = "",
-            newl: str = "",
-            encoding: str | None = None,
-            standalone: bool | None = None,
-        ) -> None: ...
-    else:
-        def writexml(
-            self,
-            writer: SupportsWrite[str],
-            indent: str = "",
-            addindent: str = "",
-            newl: str = "",
-            encoding: Incomplete | None = None,
-        ) -> None: ...
-
+    def writexml(
+        self,
+        writer: SupportsWrite[str],
+        indent: str = "",
+        addindent: str = "",
+        newl: str = "",
+        encoding: str | None = None,
+        standalone: bool | None = None,
+    ) -> None: ...
     @overload
     def renameNode(self, n: Element, namespaceURI: str, name: str) -> Element: ...
     @overload
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
index 10c305826453..8f20ee15a14e 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
@@ -1,4 +1,3 @@
-import sys
 from _typeshed import FileDescriptorOrPath
 from typing import Final, Literal, Protocol, overload
 from xml.etree.ElementTree import Element
@@ -13,8 +12,7 @@ XINCLUDE: Final[str]
 XINCLUDE_INCLUDE: Final[str]
 XINCLUDE_FALLBACK: Final[str]
 
-if sys.version_info >= (3, 9):
-    DEFAULT_MAX_INCLUSION_DEPTH: Final = 6
+DEFAULT_MAX_INCLUSION_DEPTH: Final = 6
 
 class FatalIncludeError(SyntaxError): ...
 
@@ -22,11 +20,6 @@ class FatalIncludeError(SyntaxError): ...
 def default_loader(href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ...
 @overload
 def default_loader(href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ...
+def include(elem: Element, loader: _Loader | None = None, base_url: str | None = None, max_depth: int | None = 6) -> None: ...
 
-if sys.version_info >= (3, 9):
-    def include(elem: Element, loader: _Loader | None = None, base_url: str | None = None, max_depth: int | None = 6) -> None: ...
-
-    class LimitedRecursiveIncludeError(FatalIncludeError): ...
-
-else:
-    def include(elem: Element, loader: _Loader | None = None) -> None: ...
+class LimitedRecursiveIncludeError(FatalIncludeError): ...
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
index 4a9113868d7e..4c55a1a7452e 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
@@ -15,6 +15,7 @@ __all__ = [
     "canonicalize",
     "fromstring",
     "fromstringlist",
+    "indent",
     "iselement",
     "iterparse",
     "parse",
@@ -34,9 +35,6 @@ __all__ = [
     "register_namespace",
 ]
 
-if sys.version_info >= (3, 9):
-    __all__ += ["indent"]
-
 _T = TypeVar("_T")
 _FileRead: TypeAlias = FileDescriptorOrPath | SupportsRead[bytes] | SupportsRead[str]
 _FileWriteC14N: TypeAlias = FileDescriptorOrPath | SupportsWrite[bytes]
@@ -138,9 +136,6 @@ class Element(Generic[_Tag]):
     # Doesn't really exist in earlier versions, where __len__ is called implicitly instead
     @deprecated("Testing an element's truth value is deprecated.")
     def __bool__(self) -> bool: ...
-    if sys.version_info < (3, 9):
-        def getchildren(self) -> list[Element]: ...
-        def getiterator(self, tag: str | None = None) -> list[Element]: ...
 
 def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ...
 def Comment(text: str | None = None) -> _CallableElement: ...
@@ -165,9 +160,6 @@ class ElementTree(Generic[_Root]):
     def getroot(self) -> _Root: ...
     def parse(self, source: _FileRead, parser: XMLParser | None = None) -> Element: ...
     def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ...
-    if sys.version_info < (3, 9):
-        def getiterator(self, tag: str | None = None) -> list[Element]: ...
-
     def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ...
     @overload
     def findtext(self, path: str, default: None = None, namespaces: dict[str, str] | None = None) -> str | None: ...
@@ -254,10 +246,7 @@ def tostringlist(
     short_empty_elements: bool = True,
 ) -> list[Any]: ...
 def dump(elem: Element | ElementTree[Any]) -> None: ...
-
-if sys.version_info >= (3, 9):
-    def indent(tree: Element | ElementTree[Any], space: str = "  ", level: int = 0) -> None: ...
-
+def indent(tree: Element | ElementTree[Any], space: str = "  ", level: int = 0) -> None: ...
 def parse(source: _FileRead, parser: XMLParser[Any] | None = None) -> ElementTree[Element]: ...
 
 # This class is defined inside the body of iterparse
@@ -366,7 +355,7 @@ _E = TypeVar("_E", default=Element)
 class XMLParser(Generic[_E]):
     parser: XMLParserType
     target: _Target
-    # TODO-what is entity used for???
+    # TODO: what is entity used for???
     entity: dict[str, str]
     version: str
     def __init__(self, *, target: _Target | None = None, encoding: str | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
index 6a68f52f0e99..012d6c03e121 100644
--- a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
@@ -53,11 +53,7 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
     ) -> None: ...
     @overload
     def setProperty(self, name: str, value: object) -> None: ...
-    if sys.version_info >= (3, 9):
-        def feed(self, data: str | ReadableBuffer, isFinal: bool = False) -> None: ...
-    else:
-        def feed(self, data: str | ReadableBuffer, isFinal: _BoolType = 0) -> None: ...
-
+    def feed(self, data: str | ReadableBuffer, isFinal: bool = False) -> None: ...
     def flush(self) -> None: ...
     def close(self) -> None: ...
     def reset(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi
index 91bc051df686..ede732c0f86a 100644
--- a/mypy/typeshed/stdlib/zipfile/__init__.pyi
+++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi
@@ -30,7 +30,6 @@ _DateTuple = tuple[int, int, int, int, int, int]  # noqa: Y026
 _ZipFileMode = Literal["r", "w", "x", "a"]  # noqa: Y026
 
 _ReadWriteMode: TypeAlias = Literal["r", "w"]
-_ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"]
 
 class BadZipFile(Exception): ...
 
@@ -321,25 +320,20 @@ else:
             @property
             def stem(self) -> str: ...
 
-        if sys.version_info >= (3, 9):
-            @overload
-            def open(
-                self,
-                mode: Literal["r", "w"] = "r",
-                encoding: str | None = None,
-                errors: str | None = None,
-                newline: str | None = None,
-                line_buffering: bool = ...,
-                write_through: bool = ...,
-                *,
-                pwd: bytes | None = None,
-            ) -> TextIOWrapper: ...
-            @overload
-            def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ...
-        else:
-            def open(
-                self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False
-            ) -> IO[bytes]: ...
+        @overload
+        def open(
+            self,
+            mode: Literal["r", "w"] = "r",
+            encoding: str | None = None,
+            errors: str | None = None,
+            newline: str | None = None,
+            line_buffering: bool = ...,
+            write_through: bool = ...,
+            *,
+            pwd: bytes | None = None,
+        ) -> TextIOWrapper: ...
+        @overload
+        def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ...
 
         if sys.version_info >= (3, 10):
             def iterdir(self) -> Iterator[Self]: ...
diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
index fb21b00c45dc..35381758a1b7 100644
--- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
+++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
@@ -1,35 +1,28 @@
-import sys
 from collections.abc import Iterable
 from datetime import datetime, timedelta, tzinfo
 from typing_extensions import Self
+from zoneinfo._common import ZoneInfoNotFoundError as ZoneInfoNotFoundError, _IOBytes
+from zoneinfo._tzpath import (
+    TZPATH as TZPATH,
+    InvalidTZPathWarning as InvalidTZPathWarning,
+    available_timezones as available_timezones,
+    reset_tzpath as reset_tzpath,
+)
 
-# TODO: remove this version check
-# In theory we shouldn't need this version check. Pyright complains about the imports
-# from zoneinfo.* when run on 3.8 and 3.7 without this. Updates to typeshed's
-# pyright test script are probably needed, see #11189
-if sys.version_info >= (3, 9):
-    from zoneinfo._common import ZoneInfoNotFoundError as ZoneInfoNotFoundError, _IOBytes
-    from zoneinfo._tzpath import (
-        TZPATH as TZPATH,
-        InvalidTZPathWarning as InvalidTZPathWarning,
-        available_timezones as available_timezones,
-        reset_tzpath as reset_tzpath,
-    )
+__all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"]
 
-    __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"]
+class ZoneInfo(tzinfo):
+    @property
+    def key(self) -> str: ...
+    def __new__(cls, key: str) -> Self: ...
+    @classmethod
+    def no_cache(cls, key: str) -> Self: ...
+    @classmethod
+    def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ...
+    @classmethod
+    def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ...
+    def tzname(self, dt: datetime | None, /) -> str | None: ...
+    def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ...
+    def dst(self, dt: datetime | None, /) -> timedelta | None: ...
 
-    class ZoneInfo(tzinfo):
-        @property
-        def key(self) -> str: ...
-        def __new__(cls, key: str) -> Self: ...
-        @classmethod
-        def no_cache(cls, key: str) -> Self: ...
-        @classmethod
-        def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ...
-        @classmethod
-        def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ...
-        def tzname(self, dt: datetime | None, /) -> str | None: ...
-        def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ...
-        def dst(self, dt: datetime | None, /) -> timedelta | None: ...
-
-    def __dir__() -> list[str]: ...
+def __dir__() -> list[str]: ...
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 0e0e2b1f344d..8c442a23d80a 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -254,7 +254,7 @@ reveal_type(open('x', mode))
 [out]
 _program.py:1: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]"
 _program.py:2: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]"
-_program.py:3: note: Revealed type is "_io.BufferedReader"
+_program.py:3: note: Revealed type is "_io.BufferedReader[_io._BufferedReaderStream]"
 _program.py:5: note: Revealed type is "typing.IO[Any]"
 
 [case testOpenReturnTypeInferenceSpecialCases]
@@ -263,8 +263,8 @@ reveal_type(open(file='x', mode='rb'))
 mode = 'rb'
 reveal_type(open(mode=mode, file='r'))
 [out]
-_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "_io.BufferedReader"
-_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "_io.BufferedReader"
+_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "_io.BufferedReader[_io._BufferedReaderStream]"
+_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "_io.BufferedReader[_io._BufferedReaderStream]"
 _testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is "typing.IO[Any]"
 
 [case testPathOpenReturnTypeInference]
@@ -278,7 +278,7 @@ reveal_type(p.open(mode))
 [out]
 _program.py:3: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]"
 _program.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]"
-_program.py:5: note: Revealed type is "_io.BufferedReader"
+_program.py:5: note: Revealed type is "_io.BufferedReader[_io._BufferedReaderStream]"
 _program.py:7: note: Revealed type is "typing.IO[Any]"
 
 [case testPathOpenReturnTypeInferenceSpecialCases]

From f328ad6ab81fba3c8470e98f8e6795813a89f810 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Tue, 27 May 2025 20:55:32 -0700
Subject: [PATCH 1313/1617] Fix nondeterministic type checking caused by
 nonassociativity of joins (#19147)

I thought about doing this in `join_type_list`, but most callers look
like they do have some deterministic order.

Fixes #19121 (torchvision case only, haven't looked at xarray)

Fixes #16979 (OP case only, bzoracler case fixed by #18402)
---
 mypy/solve.py                             | 39 ++++++++++++++--------
 test-data/unit/check-generics.test        | 40 +++++++++++++++++++++++
 test-data/unit/check-recursive-types.test |  4 +--
 3 files changed, 68 insertions(+), 15 deletions(-)

diff --git a/mypy/solve.py b/mypy/solve.py
index 57988790a727..023a32dbd04b 100644
--- a/mypy/solve.py
+++ b/mypy/solve.py
@@ -9,7 +9,7 @@
 from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op
 from mypy.expandtype import expand_type
 from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort
-from mypy.join import join_types
+from mypy.join import join_type_list
 from mypy.meet import meet_type_list, meet_types
 from mypy.subtypes import is_subtype
 from mypy.typeops import get_all_type_vars
@@ -247,10 +247,16 @@ def solve_iteratively(
     return solutions
 
 
+def _join_sorted_key(t: Type) -> int:
+    t = get_proper_type(t)
+    if isinstance(t, UnionType):
+        return -1
+    return 0
+
+
 def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
     """Solve constraints by finding by using meets of upper bounds, and joins of lower bounds."""
-    bottom: Type | None = None
-    top: Type | None = None
+
     candidate: Type | None = None
 
     # Filter out previous results of failed inference, they will only spoil the current pass...
@@ -267,19 +273,26 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
         candidate.ambiguous = True
         return candidate
 
+    bottom: Type | None = None
+    top: Type | None = None
+
     # Process each bound separately, and calculate the lower and upper
     # bounds based on constraints. Note that we assume that the constraint
     # targets do not have constraint references.
-    for target in lowers:
-        if bottom is None:
-            bottom = target
-        else:
-            if type_state.infer_unions:
-                # This deviates from the general mypy semantics because
-                # recursive types are union-heavy in 95% of cases.
-                bottom = UnionType.make_union([bottom, target])
-            else:
-                bottom = join_types(bottom, target)
+    if type_state.infer_unions:
+        # This deviates from the general mypy semantics because
+        # recursive types are union-heavy in 95% of cases.
+        bottom = UnionType.make_union(list(lowers))
+    else:
+        # The order of lowers is non-deterministic.
+        # We attempt to sort lowers because joins are non-associative. For instance:
+        # join(join(int, str), int | str) == join(object, int | str) == object
+        # join(int, join(str, int | str)) == join(int, int | str)    == int | str
+        # Note that joins in theory should be commutative, but in practice some bugs mean this is
+        # also a source of non-deterministic type checking results.
+        sorted_lowers = sorted(lowers, key=_join_sorted_key)
+        if sorted_lowers:
+            bottom = join_type_list(sorted_lowers)
 
     for target in uppers:
         if top is None:
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 767b55efcac2..35357f8c930f 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -3563,3 +3563,43 @@ def foo(x: T):
     reveal_type(C)  # N: Revealed type is "Overload(def [T, S] (x: builtins.int, y: S`-1) -> __main__.C[__main__.Int[S`-1]], def [T, S] (x: builtins.str, y: S`-1) -> __main__.C[__main__.Str[S`-1]])"
     reveal_type(C(0, x))  # N: Revealed type is "__main__.C[__main__.Int[T`-1]]"
     reveal_type(C("yes", x))  # N: Revealed type is "__main__.C[__main__.Str[T`-1]]"
+
+[case testDeterminismFromJoinOrderingInSolver]
+# Used to fail non-deterministically
+# https://github.com/python/mypy/issues/19121
+from __future__ import annotations
+from typing import Generic, Iterable, Iterator, Self, TypeVar
+
+_T1 = TypeVar("_T1")
+_T2 = TypeVar("_T2")
+_T3 = TypeVar("_T3")
+_T_co = TypeVar("_T_co", covariant=True)
+
+class Base(Iterable[_T1]):
+    def __iter__(self) -> Iterator[_T1]: ...
+class A(Base[_T1]): ...
+class B(Base[_T1]): ...
+class C(Base[_T1]): ...
+class D(Base[_T1]): ...
+class E(Base[_T1]): ...
+
+class zip2(Generic[_T_co]):
+    def __new__(
+        cls,
+        iter1: Iterable[_T1],
+        iter2: Iterable[_T2],
+        iter3: Iterable[_T3],
+    ) -> zip2[tuple[_T1, _T2, _T3]]: ...
+    def __iter__(self) -> Self: ...
+    def __next__(self) -> _T_co: ...
+
+def draw(
+    colors1: A[str] | B[str] | C[int] | D[int | str],
+    colors2: A[str] | B[str] | C[int] | D[int | str],
+    colors3: A[str] | B[str] | C[int] | D[int | str],
+) -> None:
+    for c1, c2, c3 in zip2(colors1, colors2, colors3):
+        reveal_type(c1)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+        reveal_type(c2)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+        reveal_type(c3)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test
index 00d5489e515a..7f6e181a16ca 100644
--- a/test-data/unit/check-recursive-types.test
+++ b/test-data/unit/check-recursive-types.test
@@ -54,7 +54,7 @@ reveal_type(flatten([1, [2, [3]]]))  # N: Revealed type is "builtins.list[builti
 
 class Bad: ...
 x: Nested[int] = [1, [2, [3]]]
-x = [1, [Bad()]]  # E: List item 0 has incompatible type "Bad"; expected "Union[int, Nested[int]]"
+x = [1, [Bad()]]  # E: List item 1 has incompatible type "List[Bad]"; expected "Union[int, Nested[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testRecursiveAliasGenericInferenceNested]
@@ -605,7 +605,7 @@ class NT(NamedTuple, Generic[T]):
 class A: ...
 class B(A): ...
 
-nti: NT[int] = NT(key=0, value=NT(key=1, value=A()))  # E: Argument "value" to "NT" has incompatible type "A"; expected "Union[int, NT[int]]"
+nti: NT[int] = NT(key=0, value=NT(key=1, value=A()))  # E: Argument "value" to "NT" has incompatible type "NT[A]"; expected "Union[int, NT[int]]"
 reveal_type(nti)  # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]"
 
 nta: NT[A]

From 3801b7fef670eae73a72b903e93330d3e2dbdbd5 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 28 May 2025 10:35:12 +0200
Subject: [PATCH 1314/1617] Drop support for --python-version 3.8 (#19157)

Drop last remaining support for Python 3.8. Support for running with 3.8
was removed in #17492 already. This PR removes the option to use 3.8
with `--python-version` since the type stubs only support 3.9+, see
https://github.com/python/mypy/pull/18930.
---
 mypy/defaults.py                              |  2 +-
 mypy/modulefinder.py                          |  4 +-
 mypy/nodes.py                                 | 12 ----
 mypy/semanal.py                               | 26 -------
 mypy/typeanal.py                              | 70 ++-----------------
 mypyc/test-data/run-misc.test                 |  9 ---
 mypyc/test/testutil.py                        |  2 +-
 test-data/unit/check-annotated.test           | 10 +--
 test-data/unit/check-columns.test             |  4 +-
 test-data/unit/check-dataclasses.test         |  1 -
 test-data/unit/check-errorcodes.test          | 29 ++++----
 test-data/unit/check-flags.test               | 32 ++++-----
 test-data/unit/check-functions.test           |  8 ---
 test-data/unit/check-functools.test           |  4 +-
 test-data/unit/check-generic-alias.test       | 51 --------------
 test-data/unit/check-generics.test            | 13 ++--
 test-data/unit/check-lowercase.test           | 22 +++---
 test-data/unit/check-python39.test            |  2 -
 test-data/unit/check-type-aliases.test        | 13 ++--
 .../check-type-object-type-inference.test     |  1 -
 test-data/unit/check-unreachable-code.test    |  4 +-
 test-data/unit/cmdline.test                   | 21 +++---
 test-data/unit/daemon.test                    |  2 +-
 test-data/unit/fine-grained.test              |  6 +-
 test-data/unit/parse.test                     | 11 ++-
 25 files changed, 86 insertions(+), 273 deletions(-)

diff --git a/mypy/defaults.py b/mypy/defaults.py
index 45ad6fe3076c..58a74a478b16 100644
--- a/mypy/defaults.py
+++ b/mypy/defaults.py
@@ -10,7 +10,7 @@
 
 # Earliest Python 3.x version supported via --python-version 3.x. To run
 # mypy, at least version PYTHON3_VERSION is needed.
-PYTHON3_VERSION_MIN: Final = (3, 8)  # Keep in sync with typeshed's python support
+PYTHON3_VERSION_MIN: Final = (3, 9)  # Keep in sync with typeshed's python support
 
 CACHE_DIR: Final = ".mypy_cache"
 
diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
index 4cbeed9d14ff..d159736078eb 100644
--- a/mypy/modulefinder.py
+++ b/mypy/modulefinder.py
@@ -995,6 +995,6 @@ def parse_version(version: str) -> tuple[int, int]:
 
 def typeshed_py_version(options: Options) -> tuple[int, int]:
     """Return Python version used for checking whether module supports typeshed."""
-    # Typeshed no longer covers Python 3.x versions before 3.8, so 3.8 is
+    # Typeshed no longer covers Python 3.x versions before 3.9, so 3.9 is
     # the earliest we can support.
-    return max(options.python_version, (3, 8))
+    return max(options.python_version, (3, 9))
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 584e56667944..c990cf8ec3f9 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -148,18 +148,6 @@ def set_line(
     "builtins.frozenset": "typing.FrozenSet",
 }
 
-_nongen_builtins: Final = {"builtins.tuple": "typing.Tuple", "builtins.enumerate": ""}
-_nongen_builtins.update((name, alias) for alias, name in type_aliases.items())
-# Drop OrderedDict from this for backward compatibility
-del _nongen_builtins["collections.OrderedDict"]
-# HACK: consequence of hackily treating LiteralString as an alias for str
-del _nongen_builtins["builtins.str"]
-
-
-def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]:
-    # After 3.9 with pep585 generic builtins are allowed
-    return _nongen_builtins if python_version < (3, 9) else {}
-
 
 RUNTIME_PROTOCOL_DECOS: Final = (
     "typing.runtime_checkable",
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 89bb5ab97c2a..c5f4443588f8 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -184,7 +184,6 @@
     YieldExpr,
     YieldFromExpr,
     get_member_expr_fullname,
-    get_nongen_builtins,
     implicit_module_attrs,
     is_final_node,
     type_aliases,
@@ -247,7 +246,6 @@
     find_self_type,
     fix_instance,
     has_any_from_unimported_type,
-    no_subscript_builtin_alias,
     type_constructors,
     validate_instance,
 )
@@ -5996,30 +5994,6 @@ def analyze_type_application(self, expr: IndexExpr) -> None:
         expr.analyzed = TypeApplication(base, types)
         expr.analyzed.line = expr.line
         expr.analyzed.column = expr.column
-        # Types list, dict, set are not subscriptable, prohibit this if
-        # subscripted either via type alias...
-        if isinstance(base, RefExpr) and isinstance(base.node, TypeAlias):
-            alias = base.node
-            target = get_proper_type(alias.target)
-            if isinstance(target, Instance):
-                name = target.type.fullname
-                if (
-                    alias.no_args
-                    and name  # this avoids bogus errors for already reported aliases
-                    in get_nongen_builtins(self.options.python_version)
-                    and not self.is_stub_file
-                    and not alias.normalized
-                ):
-                    self.fail(no_subscript_builtin_alias(name, propose_alt=False), expr)
-        # ...or directly.
-        else:
-            n = self.lookup_type_node(base)
-            if (
-                n
-                and n.fullname in get_nongen_builtins(self.options.python_version)
-                and not self.is_stub_file
-            ):
-                self.fail(no_subscript_builtin_alias(n.fullname, propose_alt=False), expr)
 
     def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
         """Analyze type arguments (index) in a type application.
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index 7bf21709b863..40e62e04740d 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -47,7 +47,6 @@
     Var,
     check_arg_kinds,
     check_arg_names,
-    get_nongen_builtins,
 )
 from mypy.options import INLINE_TYPEDDICT, Options
 from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface
@@ -136,12 +135,6 @@
     "mypy_extensions.KwArg": ARG_STAR2,
 }
 
-GENERIC_STUB_NOT_AT_RUNTIME_TYPES: Final = {
-    "queue.Queue",
-    "builtins._PathLike",
-    "asyncio.futures.Future",
-}
-
 SELF_TYPE_NAMES: Final = {"typing.Self", "typing_extensions.Self"}
 
 
@@ -186,17 +179,6 @@ def analyze_type_alias(
     return res, analyzer.aliases_used
 
 
-def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str:
-    class_name = name.split(".")[-1]
-    msg = f'"{class_name}" is not subscriptable'
-    # This should never be called if the python_version is 3.9 or newer
-    nongen_builtins = get_nongen_builtins((3, 8))
-    replacement = nongen_builtins[name]
-    if replacement and propose_alt:
-        msg += f', use "{replacement}" instead'
-    return msg
-
-
 class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
     """Semantic analyzer for types.
 
@@ -360,14 +342,6 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
             hook = self.plugin.get_type_analyze_hook(fullname)
             if hook is not None:
                 return hook(AnalyzeTypeContext(t, t, self))
-            if (
-                fullname in get_nongen_builtins(self.options.python_version)
-                and t.args
-                and not self.always_allow_new_syntax
-            ):
-                self.fail(
-                    no_subscript_builtin_alias(fullname, propose_alt=not self.defining_alias), t
-                )
             tvar_def = self.tvar_scope.get_binding(sym)
             if isinstance(sym.node, ParamSpecExpr):
                 if tvar_def is None:
@@ -2033,44 +2007,14 @@ def get_omitted_any(
     unexpanded_type: Type | None = None,
 ) -> AnyType:
     if disallow_any:
-        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).
-            alternative = nongen_builtins[fullname]
-            fail(
-                message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative),
-                typ,
-                code=codes.TYPE_ARG,
-            )
-        else:
-            typ = unexpanded_type or orig_type
-            type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options)
+        typ = unexpanded_type or orig_type
+        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)),
-                typ,
-                code=codes.TYPE_ARG,
-            )
-            base_type = get_proper_type(orig_type)
-            base_fullname = (
-                base_type.type.fullname if isinstance(base_type, Instance) else fullname
-            )
-            # Ideally, we'd check whether the type is quoted or `from __future__ annotations`
-            # is set before issuing this note
-            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(
-                    "Subscripting classes that are not generic at runtime may require "
-                    "escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html"
-                    "#not-generic-runtime",
-                    typ,
-                    code=codes.TYPE_ARG,
-                )
+        fail(
+            message_registry.BARE_GENERIC.format(quote_type_string(type_str)),
+            typ,
+            code=codes.TYPE_ARG,
+        )
 
         any_type = AnyType(TypeOfAny.from_error, line=typ.line, column=typ.column)
     else:
diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test
index a08be091bcc3..f12d6618681a 100644
--- a/mypyc/test-data/run-misc.test
+++ b/mypyc/test-data/run-misc.test
@@ -984,15 +984,6 @@ elif sys.version_info[:2] == (3, 10):
 elif sys.version_info[:2] == (3, 9):
     def version() -> int:
         return 9
-elif sys.version_info[:2] == (3, 8):
-    def version() -> int:
-        return 8
-elif sys.version_info[:2] == (3, 7):
-    def version() -> int:
-        return 7
-elif sys.version_info[:2] == (3, 6):
-    def version() -> int:
-        return 6
 else:
     raise Exception("we don't support this version yet!")
 
diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py
index 7b56b8aa0dec..80a06204bb9d 100644
--- a/mypyc/test/testutil.py
+++ b/mypyc/test/testutil.py
@@ -111,7 +111,7 @@ def build_ir_for_single_file2(
     options.hide_error_codes = True
     options.use_builtins_fixtures = True
     options.strict_optional = True
-    options.python_version = compiler_options.python_version or (3, 8)
+    options.python_version = compiler_options.python_version or (3, 9)
     options.export_types = True
     options.preserve_asts = True
     options.allow_empty_bodies = True
diff --git a/test-data/unit/check-annotated.test b/test-data/unit/check-annotated.test
index 47fe33bfb42a..54d9715a3897 100644
--- a/test-data/unit/check-annotated.test
+++ b/test-data/unit/check-annotated.test
@@ -144,15 +144,7 @@ def f4(a: Annotated[T, "metadata"]):
 reveal_type(f4)  # N: Revealed type is "def [T] (a: T`-1) -> Any"
 [builtins fixtures/tuple.pyi]
 
-[case testSliceAnnotated39]
-# flags: --python-version 3.9
-from typing_extensions import Annotated
-a: Annotated[int, 1:2]
-reveal_type(a)  # N: Revealed type is "builtins.int"
-[builtins fixtures/tuple.pyi]
-
-[case testSliceAnnotated38]
-# flags: --python-version 3.8
+[case testSliceAnnotated]
 from typing_extensions import Annotated
 a: Annotated[int, 1:2]
 reveal_type(a)  # N: Revealed type is "builtins.int"
diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test
index c18313bbc24f..5d8f55ec598c 100644
--- a/test-data/unit/check-columns.test
+++ b/test-data/unit/check-columns.test
@@ -261,10 +261,10 @@ class D(A):
                               # N:5:          def f(self) -> None
 
 [case testColumnMissingTypeParameters]
-# flags: --python-version 3.8 --disallow-any-generics
+# flags: --disallow-any-generics
 from typing import List, Callable
 def f(x: List) -> None: pass # E:10: Missing type parameters for generic type "List"
-def g(x: list) -> None: pass # E:10: Implicit generic "Any". Use "typing.List" and specify generic parameters
+def g(x: list) -> None: pass # E:10: Missing type parameters for generic type "List"
 if int():
     c: Callable # E:8: Missing type parameters for generic type "Callable"
 [builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index dbcb4c82072c..a3f46292e712 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -1911,7 +1911,6 @@ SecondClass().SECOND_CONST = 42  # E: Cannot assign to final attribute "SECOND_C
 [builtins fixtures/dataclasses.pyi]
 
 [case testDataclassFieldsProtocol]
-# flags: --python-version 3.9
 from dataclasses import dataclass
 from typing import Any, Protocol
 
diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test
index 0cd6dc081629..c07a161823da 100644
--- a/test-data/unit/check-errorcodes.test
+++ b/test-data/unit/check-errorcodes.test
@@ -341,10 +341,10 @@ a: A
 a.x = ''  # E: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
 
 [case testErrorCodeMissingTypeArg]
-# flags: --python-version 3.8 --disallow-any-generics
+# flags: --disallow-any-generics
 from typing import List, TypeVar
 x: List  # E: Missing type parameters for generic type "List"  [type-arg]
-y: list  # E: Implicit generic "Any". Use "typing.List" and specify generic parameters  [type-arg]
+y: list  # E: Missing type parameters for generic type "List"  [type-arg]
 T = TypeVar('T')
 L = List[List[T]]
 z: L  # E: Missing type parameters for generic type "L"  [type-arg]
@@ -970,22 +970,21 @@ def f(arg: int) -> int:
 def f(arg: str) -> str:
     ...
 
-[case testSliceInDict39]
-# flags: --python-version 3.9 --show-column-numbers
-from typing import Dict
-b: Dict[int, x:y]
-c: Dict[x:y]
+[case testSliceInDictBuiltin]
+# flags: --show-column-numbers
+b: dict[int, x:y]
+c: dict[x:y]
 
 [builtins fixtures/dict.pyi]
 [out]
-main:3:14: error: Invalid type comment or annotation  [valid-type]
-main:3:14: note: did you mean to use ',' instead of ':' ?
-main:4:4: error: "dict" expects 2 type arguments, but 1 given  [type-arg]
-main:4:9: error: Invalid type comment or annotation  [valid-type]
-main:4:9: note: did you mean to use ',' instead of ':' ?
-
-[case testSliceInDict38]
-# flags: --python-version 3.8 --show-column-numbers
+main:2:14: error: Invalid type comment or annotation  [valid-type]
+main:2:14: note: did you mean to use ',' instead of ':' ?
+main:3:4: error: "dict" expects 2 type arguments, but 1 given  [type-arg]
+main:3:9: error: Invalid type comment or annotation  [valid-type]
+main:3:9: note: did you mean to use ',' instead of ':' ?
+
+[case testSliceInDictTyping]
+# flags: --show-column-numbers
 from typing import Dict
 b: Dict[int, x:y]
 c: Dict[x:y]
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
index f628fdd68ce8..ae126fb5e603 100644
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -1501,16 +1501,14 @@ GroupsDict = Dict[str, GroupDataDict]  # type: ignore
 
 
 [case testCheckDisallowAnyGenericsStubOnly]
-# flags: --disallow-any-generics --python-version 3.8
+# flags: --disallow-any-generics
 from asyncio import Future
 from queue import Queue
 x: Future[str]
 y: Queue[int]
 
-p: Future  # E: Missing type parameters for generic type "Future" \
-           # N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime
-q: Queue  # E: Missing type parameters for generic type "Queue" \
-          # N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime
+p: Future  # E: Missing type parameters for generic type "Future"
+q: Queue  # E: Missing type parameters for generic type "Queue"
 [file asyncio/__init__.pyi]
 from asyncio.futures import Future as Future
 [file asyncio/futures.pyi]
@@ -1524,28 +1522,28 @@ class Queue(Generic[_T]): ...
 [builtins fixtures/async_await.pyi]
 [typing fixtures/typing-full.pyi]
 
-[case testDisallowAnyGenericsBuiltinTuplePre39]
-# flags: --disallow-any-generics --python-version 3.8
+[case testDisallowAnyGenericsBuiltinTuple]
+# flags: --disallow-any-generics
 s = tuple([1, 2, 3])
-def f(t: tuple) -> None: pass  # E: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters
+def f(t: tuple) -> None: pass  # E: Missing type parameters for generic type "tuple"
 [builtins fixtures/tuple.pyi]
 
-[case testDisallowAnyGenericsBuiltinListPre39]
-# flags: --disallow-any-generics --python-version 3.8
+[case testDisallowAnyGenericsBuiltinList]
+# flags: --disallow-any-generics
 l = list([1, 2, 3])
-def f(t: list) -> None: pass  # E: Implicit generic "Any". Use "typing.List" and specify generic parameters
+def f(t: list) -> None: pass  # E: Missing type parameters for generic type "List"
 [builtins fixtures/list.pyi]
 
-[case testDisallowAnyGenericsBuiltinSetPre39]
-# flags: --disallow-any-generics --python-version 3.8
+[case testDisallowAnyGenericsBuiltinSet]
+# flags: --disallow-any-generics
 l = set({1, 2, 3})
-def f(s: set) -> None: pass  # E: Implicit generic "Any". Use "typing.Set" and specify generic parameters
+def f(s: set) -> None: pass  # E: Missing type parameters for generic type "Set"
 [builtins fixtures/set.pyi]
 
-[case testDisallowAnyGenericsBuiltinDictPre39]
-# flags: --disallow-any-generics --python-version 3.8
+[case testDisallowAnyGenericsBuiltinDict]
+# flags: --disallow-any-generics
 l = dict([('a', 1)])
-def f(d: dict) -> None: pass  # E: Implicit generic "Any". Use "typing.Dict" and specify generic parameters
+def f(d: dict) -> None: pass  # E: Missing type parameters for generic type "Dict"
 [builtins fixtures/dict.pyi]
 
 [case testCheckDefaultAllowAnyGeneric]
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index ac93c6c20354..fd4cd86d1a93 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -1827,7 +1827,6 @@ def Arg(x, y): pass
 F = Callable[[Arg(int, 'x')], int]  # E: Invalid argument constructor "__main__.Arg"
 
 [case testCallableParsingFromExpr]
-# flags: --python-version 3.9
 from typing import Callable, List
 from mypy_extensions import Arg, VarArg, KwArg
 import mypy_extensions
@@ -1858,13 +1857,6 @@ Q = Callable[[Arg(int, type=int)], int]  # E: Invalid type alias: expression is
 R = Callable[[Arg(int, 'x', name='y')], int]  # E: Invalid type alias: expression is not a valid type \
                                               # E: Value of type "int" is not indexable \
                                               # E: "Arg" gets multiple values for keyword argument "name"
-
-
-
-
-
-
-
 [builtins fixtures/dict.pyi]
 
 [case testCallableParsing]
diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test
index 53ddc96cbe19..08f82fe78d73 100644
--- a/test-data/unit/check-functools.test
+++ b/test-data/unit/check-functools.test
@@ -289,11 +289,10 @@ p1("a", "b")  # TODO: false negative
 [builtins fixtures/dict.pyi]
 
 [case testFunctoolsPartialTypeGuard]
-# flags: --python-version 3.8
 import functools
 from typing_extensions import TypeGuard
 
-def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ...  # E: "list" is not subscriptable, use "typing.List" instead
+def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ...
 
 reveal_type(functools.partial(is_str_list, [1, 2, 3]))  # N: Revealed type is "functools.partial[builtins.bool]"
 reveal_type(functools.partial(is_str_list, [1, 2, 3])())  # N: Revealed type is "builtins.bool"
@@ -580,7 +579,6 @@ def bar(f: S) -> S:
 [builtins fixtures/primitives.pyi]
 
 [case testFunctoolsPartialAbstractType]
-# flags: --python-version 3.9
 from abc import ABC, abstractmethod
 from functools import partial
 
diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test
index 3ae815a5cd48..14c7738f48ae 100644
--- a/test-data/unit/check-generic-alias.test
+++ b/test-data/unit/check-generic-alias.test
@@ -1,48 +1,5 @@
 -- Test cases for generic aliases
 
-[case testGenericBuiltinWarning]
-# flags: --python-version 3.8
-t1: list
-t2: list[int]  # E: "list" is not subscriptable, use "typing.List" instead
-t3: list[str]  # E: "list" is not subscriptable, use "typing.List" instead
-
-t4: tuple
-t5: tuple[int]  # E: "tuple" is not subscriptable, use "typing.Tuple" instead
-t6: tuple[int, str]  # E: "tuple" is not subscriptable, use "typing.Tuple" instead
-t7: tuple[int, ...]  # E: Unexpected "..." \
-                     # E: "tuple" is not subscriptable, use "typing.Tuple" instead
-
-t8: dict = {}
-t9: dict[int, str]  # E: "dict" is not subscriptable, use "typing.Dict" instead
-
-t10: type
-t11: type[int]  # E: "type" expects no type arguments, but 1 given
-[builtins fixtures/dict.pyi]
-
-
-[case testGenericBuiltinSetWarning]
-# flags: --python-version 3.8
-t1: set
-t2: set[int]  # E: "set" is not subscriptable, use "typing.Set" instead
-[builtins fixtures/set.pyi]
-
-
-[case testGenericCollectionsWarning]
-# flags: --python-version 3.8
-import collections
-
-t01: collections.deque
-t02: collections.deque[int]  # E: "deque" is not subscriptable, use "typing.Deque" instead
-t03: collections.defaultdict
-t04: collections.defaultdict[int, str]  # E: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead
-t05: collections.OrderedDict
-t06: collections.OrderedDict[int, str]
-t07: collections.Counter
-t08: collections.Counter[int]  # E: "Counter" is not subscriptable, use "typing.Counter" instead
-t09: collections.ChainMap
-t10: collections.ChainMap[int, str]  # E: "ChainMap" is not subscriptable, use "typing.ChainMap" instead
-
-
 [case testGenericBuiltinFutureAnnotations]
 from __future__ import annotations
 t1: list
@@ -80,7 +37,6 @@ t10: collections.ChainMap[int, str]
 
 
 [case testGenericAliasBuiltinsReveal]
-# flags: --python-version 3.9
 t1: list
 t2: list[int]
 t3: list[str]
@@ -113,7 +69,6 @@ reveal_type(t11)  # N: Revealed type is "Type[builtins.int]"
 
 
 [case testGenericAliasBuiltinsSetReveal]
-# flags: --python-version 3.9
 t1: set
 t2: set[int]
 t3: set[str]
@@ -125,7 +80,6 @@ reveal_type(t3)  # N: Revealed type is "builtins.set[builtins.str]"
 
 
 [case testGenericAliasCollectionsReveal]
-# flags: --python-version 3.9
 import collections
 
 t1: collections.deque[int]
@@ -143,7 +97,6 @@ reveal_type(t5)  # N: Revealed type is "collections.ChainMap[builtins.int, built
 
 
 [case testGenericAliasCollectionsABCReveal]
-# flags: --python-version 3.9
 import collections.abc
 
 t01: collections.abc.Awaitable[int]
@@ -213,8 +166,6 @@ t09: Tuple[int, ...] = (1, 2, 3)
 
 
 [case testGenericBuiltinTuple]
-# flags: --python-version 3.9
-
 t01: tuple = ()
 t02: tuple[int] = (1, )
 t03: tuple[int, str] = (1, 'a')
@@ -230,8 +181,6 @@ t10: Tuple[int, ...] = t09
 [builtins fixtures/tuple.pyi]
 
 [case testTypeAliasWithBuiltinTuple]
-# flags: --python-version 3.9
-
 A = tuple[int, ...]
 a: A = ()
 b: A = (1, 2, 3)
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 35357f8c930f..68434a9f885d 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -515,9 +515,8 @@ Alias[int]("a")  # E: Argument 1 to "Node" has incompatible type "str"; expected
 [out]
 
 [case testTypeApplicationCrash]
-# flags: --python-version 3.8
 import types
-type[int] # this was crashing, see #2302 (comment)  # E: The type "Type[type]" is not generic and not indexable
+type[int]
 [builtins fixtures/tuple.pyi]
 
 
@@ -1130,11 +1129,10 @@ Bad = A[int] # type: ignore
 reveal_type(Bad) # N: Revealed type is "Any"
 [out]
 
-[case testNoSubscriptionOfBuiltinAliases]
-# flags: --python-version 3.8
+[case testSubscriptionOfBuiltinAliases]
 from typing import List, TypeVar
 
-list[int]() # E: "list" is not subscriptable
+list[int]()
 
 ListAlias = List
 def fun() -> ListAlias[int]:
@@ -1143,11 +1141,10 @@ def fun() -> ListAlias[int]:
 reveal_type(fun())  # N: Revealed type is "builtins.list[builtins.int]"
 
 BuiltinAlias = list
-BuiltinAlias[int]() # E: "list" is not subscriptable
+BuiltinAlias[int]()
 
-#check that error is reported only once, and type is still stored
 T = TypeVar('T')
-BadGenList = list[T] # E: "list" is not subscriptable
+BadGenList = list[T]
 
 reveal_type(BadGenList[int]()) # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(BadGenList()) # N: Revealed type is "builtins.list[Any]"
diff --git a/test-data/unit/check-lowercase.test b/test-data/unit/check-lowercase.test
index ab6d68929f8e..51a833614a33 100644
--- a/test-data/unit/check-lowercase.test
+++ b/test-data/unit/check-lowercase.test
@@ -1,64 +1,64 @@
 
 [case testTupleLowercaseSettingOff]
-# flags: --python-version 3.9 --force-uppercase-builtins
+# 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: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --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
+# 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: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --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
+# 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: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --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
+# 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: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --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]
 
 [case testTypeLowercaseSettingOff]
-# flags: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --no-force-uppercase-builtins
 x: type[type]
 y: int
 
 y = x  # E: Incompatible types in assignment (expression has type "type[type]", variable has type "int")
 
 [case testLowercaseSettingOnTypeAnnotationHint]
-# flags: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --no-force-uppercase-builtins
 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: --python-version 3.9 --no-force-uppercase-builtins
+# flags: --no-force-uppercase-builtins
 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])"
diff --git a/test-data/unit/check-python39.test b/test-data/unit/check-python39.test
index e17bf1e7ab5b..86a9126ff483 100644
--- a/test-data/unit/check-python39.test
+++ b/test-data/unit/check-python39.test
@@ -19,8 +19,6 @@ reveal_type(f)  # N: Revealed type is "def (builtins.int) -> builtins.str"
 [builtins fixtures/list.pyi]
 
 [case testStarredExpressionsInForLoop]
-# flags: --python-version 3.9
-
 a = b = c = [1, 2, 3]
 for x in *a, *b, *c:
     reveal_type(x)  # N: Revealed type is "builtins.int"
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
index 21832a0db079..db314b136515 100644
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -1002,14 +1002,11 @@ B = List[C[U]]
 y: B[int]
 y_bad: B[str]  # E: Type argument "str" of "B" must be a subtype of "int"
 
-[case testTupleWithDifferentArgsPy38]
-# flags: --python-version 3.8
-NotYet1 = tuple[float]  # E: "tuple" is not subscriptable
-NotYet2 = tuple[float, float]  # E: "tuple" is not subscriptable
-NotYet3 = tuple[float, ...]  # E: Unexpected "..." \
-                             # E: "tuple" is not subscriptable
-NotYet4 = tuple[float, float, ...]  # E: Unexpected "..." \
-                                    # E: "tuple" is not subscriptable
+[case testTupleWithDifferentArgs]
+Alias1 = tuple[float]
+Alias2 = tuple[float, float]
+Alias3 = tuple[float, ...]
+Alias4 = tuple[float, float, ...]  # E: Unexpected "..."
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithDifferentArgsStub]
diff --git a/test-data/unit/check-type-object-type-inference.test b/test-data/unit/check-type-object-type-inference.test
index 5a4afa0c9248..cc3a5514904d 100644
--- a/test-data/unit/check-type-object-type-inference.test
+++ b/test-data/unit/check-type-object-type-inference.test
@@ -1,5 +1,4 @@
 [case testInferTupleType]
-# flags: --python-version 3.9
 from typing import TypeVar, Generic, Type
 from abc import abstractmethod
 import types  # Explicitly bring in stubs for 'types'
diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test
index a40aa21ff26a..6821b74b8b6d 100644
--- a/test-data/unit/check-unreachable-code.test
+++ b/test-data/unit/check-unreachable-code.test
@@ -798,7 +798,7 @@ def baz(x: int) -> int:
 [builtins fixtures/exception.pyi]
 
 [case testUnreachableFlagIgnoresSemanticAnalysisUnreachable]
-# flags: --warn-unreachable --python-version 3.8 --platform win32 --always-false FOOBAR
+# flags: --warn-unreachable --python-version 3.9 --platform win32 --always-false FOOBAR
 import sys
 from typing import TYPE_CHECKING
 
@@ -828,7 +828,7 @@ if sys.version_info == (2, 7):
 else:
     reveal_type(x)  # N: Revealed type is "builtins.int"
 
-if sys.version_info == (3, 8):
+if sys.version_info == (3, 9):
     reveal_type(x)  # N: Revealed type is "builtins.int"
 else:
     reveal_type(x)
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index fb2e0c01fe0e..012e1e6b7fe6 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -385,7 +385,7 @@ main.py:1: error: Cannot find implementation or library stub for module named "a
 \[tool.mypy]
 python_version = 3.10
 [out]
-pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.8 or higher). You may need to put quotes around your Python version
+pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.9 or higher). You may need to put quotes around your Python version
 == Return code: 0
 
 [case testPythonVersionTooOld10]
@@ -397,13 +397,13 @@ python_version = 1.0
 mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 3)
 == Return code: 0
 
-[case testPythonVersionTooOld37]
+[case testPythonVersionTooOld38]
 # cmd: mypy -c pass
 [file mypy.ini]
 \[mypy]
-python_version = 3.7
+python_version = 3.8
 [out]
-mypy.ini: [mypy]: python_version: Python 3.7 is not supported (must be 3.8 or higher)
+mypy.ini: [mypy]: python_version: Python 3.8 is not supported (must be 3.9 or higher)
 == Return code: 0
 
 [case testPythonVersionTooNew40]
@@ -426,18 +426,18 @@ usage: mypy [-h] [-v] [-V] [more options; see below]
 mypy: error: Mypy no longer supports checking Python 2 code. Consider pinning to mypy<0.980 if you need to check Python 2 code.
 == Return code: 2
 
-[case testPythonVersionAccepted38]
+[case testPythonVersionAccepted39]
 # cmd: mypy -c pass
 [file mypy.ini]
 \[mypy]
-python_version = 3.8
+python_version = 3.9
 [out]
 
-[case testPythonVersionAccepted311]
+[case testPythonVersionAccepted313]
 # cmd: mypy -c pass
 [file mypy.ini]
 \[mypy]
-python_version = 3.11
+python_version = 3.13
 [out]
 
 -- This should be a dumping ground for tests of plugins that are sensitive to
@@ -469,17 +469,16 @@ int_pow.py:10: note: Revealed type is "builtins.int"
 int_pow.py:11: note: Revealed type is "Any"
 == Return code: 0
 
-[case testDisallowAnyGenericsBuiltinCollectionsPre39]
+[case testDisallowAnyGenericsBuiltinCollections]
 # cmd: mypy m.py
 [file mypy.ini]
 \[mypy]
-python_version = 3.8
 \[mypy-m]
 disallow_any_generics = True
 [file m.py]
 def j(s: frozenset) -> None: pass
 [out]
-m.py:1: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters
+m.py:1: error: Missing type parameters for generic type "FrozenSet"
 
 [case testDisallowAnyGenericsTypingCollections]
 # cmd: mypy m.py
diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test
index 19ffce0927ab..ad3b51b27dfb 100644
--- a/test-data/unit/daemon.test
+++ b/test-data/unit/daemon.test
@@ -420,7 +420,7 @@ a: int
 a: str
 
 [case testDaemonGetType]
-$ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.8
+$ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.9
 Daemon started
 $ dmypy inspect foo:1:2:3:4
 Command "inspect" is only valid after a "check" command (that produces no parse errors)
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index df244b3135e9..b1ab9e235117 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -10233,7 +10233,7 @@ class Base(Protocol):
 main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe
 
 [case testPrettyMessageSorting]
-# flags: --python-version 3.8 --pretty
+# flags: --pretty
 import a
 
 [file a.py]
@@ -10248,14 +10248,14 @@ object + 1
 1()
 
 [out]
-b.py:1: error: Unsupported left operand type for + ("Type[object]")
+b.py:1: error: Unsupported left operand type for + ("type[object]")
     object + 1
     ^~~~~~~~~~
 a.py:1: error: Unsupported operand types for + ("int" and "str")
     1 + ''
         ^~
 ==
-b.py:1: error: Unsupported left operand type for + ("Type[object]")
+b.py:1: error: Unsupported left operand type for + ("type[object]")
     object + 1
     ^~~~~~~~~~
 b.py:2: error: "int" not callable
diff --git a/test-data/unit/parse.test b/test-data/unit/parse.test
index fa1d797fada4..82065c95faf8 100644
--- a/test-data/unit/parse.test
+++ b/test-data/unit/parse.test
@@ -947,18 +947,17 @@ main:1: error: Invalid syntax
 [out version==3.10.0]
 main:1: error: Invalid syntax. Perhaps you forgot a comma?
 
-[case testSliceInList39]
-# flags: --python-version 3.9
+[case testSliceInList]
 x = [1, 2][1:2]
 [out]
 MypyFile:1(
-  AssignmentStmt:2(
+  AssignmentStmt:1(
     NameExpr(x)
-    IndexExpr:2(
-      ListExpr:2(
+    IndexExpr:1(
+      ListExpr:1(
         IntExpr(1)
         IntExpr(2))
-      SliceExpr:2(
+      SliceExpr:1(
         IntExpr(1)
         IntExpr(2)))))
 

From 29e125f858e75bb9ba6f0c389718bc14075e866a Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Wed, 28 May 2025 02:20:38 -0700
Subject: [PATCH 1315/1617] Fix nondeterministic type checking caused by
 nonassociative of None joins (#19158)

Fixes https://github.com/python/mypy/issues/19121 (xarray case)

See #19147 for context

The ordering of the union is still nondeterministic. We could solve this
by change the solver to use `dict[Type, None` instead of `set[Type]`
since dicts are ordered. But doing so could paper over further bad
solving from nonassociativity or noncommutativity
---
 mypy/solve.py                      |  2 ++
 test-data/unit/check-generics.test | 13 +++++++++++++
 test-data/unit/check-varargs.test  |  2 +-
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/mypy/solve.py b/mypy/solve.py
index 023a32dbd04b..098d926bc789 100644
--- a/mypy/solve.py
+++ b/mypy/solve.py
@@ -250,6 +250,8 @@ def solve_iteratively(
 def _join_sorted_key(t: Type) -> int:
     t = get_proper_type(t)
     if isinstance(t, UnionType):
+        return -2
+    if isinstance(t, NoneType):
         return -1
     return 0
 
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 68434a9f885d..af2217e32b63 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -3599,4 +3599,17 @@ def draw(
         reveal_type(c1)  # N: Revealed type is "Union[builtins.int, builtins.str]"
         reveal_type(c2)  # N: Revealed type is "Union[builtins.int, builtins.str]"
         reveal_type(c3)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+
+def takes_int_str_none(x: int | str | None) -> None: ...
+
+def draw_none(
+    colors1: A[str] | B[str] | C[int] | D[None],
+    colors2: A[str] | B[str] | C[int] | D[None],
+    colors3: A[str] | B[str] | C[int] | D[None],
+) -> None:
+    for c1, c2, c3 in zip2(colors1, colors2, colors3):
+        # TODO: can't do reveal type because the union order is not deterministic
+        takes_int_str_none(c1)
+        takes_int_str_none(c2)
+        takes_int_str_none(c3)
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test
index c59f07e92a4e..2e93c761b0be 100644
--- a/test-data/unit/check-varargs.test
+++ b/test-data/unit/check-varargs.test
@@ -631,7 +631,7 @@ T = TypeVar('T')
 def f(*args: T) -> T: ...
 reveal_type(f(*(1, None)))  # N: Revealed type is "Union[Literal[1]?, None]"
 reveal_type(f(1, *(None, 1)))  # N: Revealed type is "Union[Literal[1]?, None]"
-reveal_type(f(1, *(1, None)))  # N: Revealed type is "Union[builtins.int, None]"
+reveal_type(f(1, *(1, None)))  # N: Revealed type is "Union[Literal[1]?, None]"
 [builtins fixtures/tuple.pyi]
 
 

From 9b5b41053c85f20419458301fc5b5df19dbaf3fb Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 28 May 2025 10:23:30 +0100
Subject: [PATCH 1316/1617] [mypyc] Add comment about incref/decref and
 free-threaded builds (#19155)

Follow-up to #19127.
---
 mypyc/lib-rt/mypyc_util.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h
index 64bf025aec27..27a11ab9f581 100644
--- a/mypyc/lib-rt/mypyc_util.h
+++ b/mypyc/lib-rt/mypyc_util.h
@@ -38,6 +38,9 @@
 //
 // Py_INCREF and other CPython operations check for immortality. This
 // can be expensive when we know that an object cannot be immortal.
+//
+// This optimization cannot be performed in free-threaded mode so we
+// fall back to just calling the normal incref/decref operations.
 
 static inline void CPy_INCREF_NO_IMM(PyObject *op)
 {

From 537fc55382fb4ac07ec49c78b7e312ffc383c052 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Kwieci=C5=84ski?=
 <64413721+Kwieeciol@users.noreply.github.com>
Date: Wed, 28 May 2025 12:17:49 +0200
Subject: [PATCH 1317/1617] Fix example to use correct method of Stack (#19123)

This PR updates the `generics.rst` documentation to correct a method
call in the `Stack` usage example. Previously, the example incorrectly
used `.append('x')` on a `Stack[str]` instance, which is not a valid
method for the `Stack` class.
---
 docs/source/generics.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/source/generics.rst b/docs/source/generics.rst
index 15538dea13bf..5d787d32b005 100644
--- a/docs/source/generics.rst
+++ b/docs/source/generics.rst
@@ -93,7 +93,7 @@ Using ``Stack`` is similar to built-in container types:
    stack.push('x')
 
    stack2: Stack[str] = Stack()
-   stack2.append('x')
+   stack2.push('x')
 
 Construction of instances of generic types is type checked (Python 3.12 syntax):
 

From 568a63862c332b7c1b52311b997a31231f193d8e Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 28 May 2025 14:32:05 +0100
Subject: [PATCH 1318/1617] Allow enum members to have type objects as values
 (#19160)

Type objects as enum values are supported at runtime.

Fixes #19151.
---
 mypy/nodes.py                       |  4 ++--
 test-data/unit/check-python310.test | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/mypy/nodes.py b/mypy/nodes.py
index c990cf8ec3f9..fae0bb1cc61f 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3301,8 +3301,8 @@ def enum_members(self) -> list[str]:
                         continue  # unannotated value not a member
 
                     typ = mypy.types.get_proper_type(sym.node.type)
-                    if isinstance(
-                        typ, mypy.types.FunctionLike
+                    if (
+                        isinstance(typ, mypy.types.FunctionLike) and not typ.is_type_obj()
                     ) or (  # explicit `@member` is required
                         isinstance(typ, mypy.types.Instance)
                         and typ.type.fullname == "enum.nonmember"
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index c2e2e5bddb34..af3982f6accd 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -2638,3 +2638,24 @@ def f2() -> None:
                 return
     reveal_type(y) # N: Revealed type is "builtins.str"
 [builtins fixtures/list.pyi]
+
+[case testEnumTypeObjectMember]
+import enum
+from typing import NoReturn
+
+def assert_never(x: NoReturn) -> None: ...
+
+class ValueType(enum.Enum):
+    INT = int
+    STR = str
+
+value_type: ValueType = ValueType.INT
+
+match value_type:
+    case ValueType.INT:
+        pass
+    case ValueType.STR:
+        pass
+    case _:
+        assert_never(value_type)
+[builtins fixtures/tuple.pyi]

From c197d985fb32b645539a1767de96eff998285b95 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 29 May 2025 10:40:42 +0100
Subject: [PATCH 1319/1617] Revert "Infer correct types with overloads of
 `Type[Guard | Is]`  (#19161)

This reverts commit 43ea203e566901510dbdd59e8907fcddb2a8ee70 (#17678).

The commit caused a regression (#19139). If we can't fix the regression
soon enough, reverting the original change temporarily will at least
unblock the mypy public release. The reverted PR can be merged again
once the regression is fixed.
---
 mypy/checker.py                     |  24 +-----
 mypy/checkexpr.py                   |  83 +++----------------
 test-data/unit/check-typeguard.test |  56 -------------
 test-data/unit/check-typeis.test    | 119 ----------------------------
 4 files changed, 14 insertions(+), 268 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index aceb0291926a..9c389cccd95f 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6160,31 +6160,15 @@ def find_isinstance_check_helper(
                         # considered "always right" (i.e. even if the types are not overlapping).
                         # Also note that a care must be taken to unwrap this back at read places
                         # where we use this to narrow down declared type.
-                        with self.msg.filter_errors(), self.local_type_map():
-                            # `node.callee` can be an `overload`ed function,
-                            # we need to resolve the real `overload` case.
-                            _, real_func = self.expr_checker.check_call(
-                                get_proper_type(self.lookup_type(node.callee)),
-                                node.args,
-                                node.arg_kinds,
-                                node,
-                                node.arg_names,
-                            )
-                        real_func = get_proper_type(real_func)
-                        if not isinstance(real_func, CallableType) or not (
-                            real_func.type_guard or real_func.type_is
-                        ):
-                            return {}, {}
-
-                        if real_func.type_guard is not None:
-                            return {expr: TypeGuardedType(real_func.type_guard)}, {}
+                        if node.callee.type_guard is not None:
+                            return {expr: TypeGuardedType(node.callee.type_guard)}, {}
                         else:
-                            assert real_func.type_is is not None
+                            assert node.callee.type_is is not None
                             return conditional_types_to_typemaps(
                                 expr,
                                 *self.conditional_types_with_intersection(
                                     self.lookup_type(expr),
-                                    [TypeRange(real_func.type_is, is_upper_bound=False)],
+                                    [TypeRange(node.callee.type_is, is_upper_bound=False)],
                                     expr,
                                 ),
                             )
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index ec64669c1cd0..ace8f09bee48 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2925,37 +2925,16 @@ def infer_overload_return_type(
             elif all_same_types([erase_type(typ) for typ in return_types]):
                 self.chk.store_types(type_maps[0])
                 return erase_type(return_types[0]), erase_type(inferred_types[0])
-            return self.check_call(
-                callee=AnyType(TypeOfAny.special_form),
-                args=args,
-                arg_kinds=arg_kinds,
-                arg_names=arg_names,
-                context=context,
-                callable_name=callable_name,
-                object_type=object_type,
-            )
-        elif not all_same_type_narrowers(matches):
-            # This is an example of how overloads can be:
-            #
-            # @overload
-            # def is_int(obj: float) -> TypeGuard[float]: ...
-            # @overload
-            # def is_int(obj: int) -> TypeGuard[int]: ...
-            #
-            # x: Any
-            # if is_int(x):
-            #     reveal_type(x)  # N: int | float
-            #
-            # So, we need to check that special case.
-            return self.check_call(
-                callee=self.combine_function_signatures(cast("list[ProperType]", matches)),
-                args=args,
-                arg_kinds=arg_kinds,
-                arg_names=arg_names,
-                context=context,
-                callable_name=callable_name,
-                object_type=object_type,
-            )
+            else:
+                return self.check_call(
+                    callee=AnyType(TypeOfAny.special_form),
+                    args=args,
+                    arg_kinds=arg_kinds,
+                    arg_names=arg_names,
+                    context=context,
+                    callable_name=callable_name,
+                    object_type=object_type,
+                )
         else:
             # Success! No ambiguity; return the first match.
             self.chk.store_types(type_maps[0])
@@ -3170,8 +3149,6 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call
         new_args: list[list[Type]] = [[] for _ in range(len(callables[0].arg_types))]
         new_kinds = list(callables[0].arg_kinds)
         new_returns: list[Type] = []
-        new_type_guards: list[Type] = []
-        new_type_narrowers: list[Type] = []
 
         too_complex = False
         for target in callables:
@@ -3198,25 +3175,8 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call
             for i, arg in enumerate(target.arg_types):
                 new_args[i].append(arg)
             new_returns.append(target.ret_type)
-            if target.type_guard:
-                new_type_guards.append(target.type_guard)
-            if target.type_is:
-                new_type_narrowers.append(target.type_is)
-
-        if new_type_guards and new_type_narrowers:
-            # They cannot be defined at the same time,
-            # declaring this function as too complex!
-            too_complex = True
-            union_type_guard = None
-            union_type_is = None
-        else:
-            union_type_guard = make_simplified_union(new_type_guards) if new_type_guards else None
-            union_type_is = (
-                make_simplified_union(new_type_narrowers) if new_type_narrowers else None
-            )
 
         union_return = make_simplified_union(new_returns)
-
         if too_complex:
             any = AnyType(TypeOfAny.special_form)
             return callables[0].copy_modified(
@@ -3226,8 +3186,6 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call
                 ret_type=union_return,
                 variables=variables,
                 implicit=True,
-                type_guard=union_type_guard,
-                type_is=union_type_is,
             )
 
         final_args = []
@@ -3241,8 +3199,6 @@ def combine_function_signatures(self, types: list[ProperType]) -> AnyType | Call
             ret_type=union_return,
             variables=variables,
             implicit=True,
-            type_guard=union_type_guard,
-            type_is=union_type_is,
         )
 
     def erased_signature_similarity(
@@ -6599,25 +6555,6 @@ def all_same_types(types: list[Type]) -> bool:
     return all(is_same_type(t, types[0]) for t in types[1:])
 
 
-def all_same_type_narrowers(types: list[CallableType]) -> bool:
-    if len(types) <= 1:
-        return True
-
-    type_guards: list[Type] = []
-    type_narrowers: list[Type] = []
-
-    for typ in types:
-        if typ.type_guard:
-            type_guards.append(typ.type_guard)
-        if typ.type_is:
-            type_narrowers.append(typ.type_is)
-    if type_guards and type_narrowers:
-        # Some overloads declare `TypeGuard` and some declare `TypeIs`,
-        # we cannot handle this in a union.
-        return False
-    return all_same_types(type_guards) and all_same_types(type_narrowers)
-
-
 def merge_typevars_in_callables_by_name(
     callables: Sequence[CallableType],
 ) -> tuple[list[CallableType], list[TypeVarType]]:
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index 00bf7d211927..94aa7ec6ffb8 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -731,62 +731,6 @@ assert a(x=x)
 reveal_type(x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/tuple.pyi]
 
-[case testTypeGuardInOverloads]
-from typing import Any, overload, Union
-from typing_extensions import TypeGuard
-
-@overload
-def func1(x: str) -> TypeGuard[str]:
-    ...
-
-@overload
-def func1(x: int) -> TypeGuard[int]:
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Any):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.str, builtins.int]"
-    else:
-        reveal_type(val)  # N: Revealed type is "Any"
-
-def func3(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-    else:
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-
-def func4(val: int):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "builtins.int"
-    else:
-        reveal_type(val)  # N: Revealed type is "builtins.int"
-[builtins fixtures/tuple.pyi]
-
-[case testTypeIsInOverloadsSameReturn]
-from typing import Any, overload, Union
-from typing_extensions import TypeGuard
-
-@overload
-def func1(x: str) -> TypeGuard[str]:
-    ...
-
-@overload
-def func1(x: int) -> TypeGuard[str]:
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "builtins.str"
-    else:
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-[builtins fixtures/tuple.pyi]
-
 [case testTypeGuardRestrictAwaySingleInvariant]
 from typing import List
 from typing_extensions import TypeGuard
diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
index 8cdcf8634788..356b1abfdf63 100644
--- a/test-data/unit/check-typeis.test
+++ b/test-data/unit/check-typeis.test
@@ -818,125 +818,6 @@ accept_typeguard(typeguard)
 
 [builtins fixtures/tuple.pyi]
 
-[case testTypeIsInOverloads]
-from typing import Any, overload, Union
-from typing_extensions import TypeIs
-
-@overload
-def func1(x: str) -> TypeIs[str]:
-    ...
-
-@overload
-def func1(x: int) -> TypeIs[int]:
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Any):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.str, builtins.int]"
-    else:
-        reveal_type(val)  # N: Revealed type is "Any"
-
-def func3(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-    else:
-        reveal_type(val)
-
-def func4(val: int):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "builtins.int"
-    else:
-        reveal_type(val)
-[builtins fixtures/tuple.pyi]
-
-[case testTypeIsInOverloadsSameReturn]
-from typing import Any, overload, Union
-from typing_extensions import TypeIs
-
-@overload
-def func1(x: str) -> TypeIs[str]:
-    ...
-
-@overload
-def func1(x: int) -> TypeIs[str]:  # type: ignore
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "builtins.str"
-    else:
-        reveal_type(val)  # N: Revealed type is "builtins.int"
-[builtins fixtures/tuple.pyi]
-
-[case testTypeIsInOverloadsUnionizeError]
-from typing import Any, overload, Union
-from typing_extensions import TypeIs, TypeGuard
-
-@overload
-def func1(x: str) -> TypeIs[str]:
-    ...
-
-@overload
-def func1(x: int) -> TypeGuard[int]:
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-    else:
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-[builtins fixtures/tuple.pyi]
-
-[case testTypeIsInOverloadsUnionizeError2]
-from typing import Any, overload, Union
-from typing_extensions import TypeIs, TypeGuard
-
-@overload
-def func1(x: int) -> TypeGuard[int]:
-    ...
-
-@overload
-def func1(x: str) -> TypeIs[str]:
-    ...
-
-def func1(x: Any) -> Any:
-    return True
-
-def func2(val: Union[int, str]):
-    if func1(val):
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-    else:
-        reveal_type(val)  # N: Revealed type is "Union[builtins.int, builtins.str]"
-[builtins fixtures/tuple.pyi]
-
-[case testTypeIsLikeIsDataclass]
-from typing import Any, overload, Union, Type
-from typing_extensions import TypeIs
-
-class DataclassInstance: ...
-
-@overload
-def is_dataclass(obj: type) -> TypeIs[Type[DataclassInstance]]: ...
-@overload
-def is_dataclass(obj: object) -> TypeIs[Union[DataclassInstance, Type[DataclassInstance]]]: ...
-
-def is_dataclass(obj: Union[type, object]) -> bool:
-    return False
-
-def func(arg: Any) -> None:
-    if is_dataclass(arg):
-        reveal_type(arg)  # N: Revealed type is "Union[Type[__main__.DataclassInstance], __main__.DataclassInstance]"
-[builtins fixtures/tuple.pyi]
-
 [case testTypeIsEnumOverlappingUnionExcludesIrrelevant]
 from enum import Enum
 from typing import Literal

From 8c772c75287323374dead4c4ce4c7ee0d2732c46 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 29 May 2025 12:36:32 +0100
Subject: [PATCH 1320/1617] Add changelog for 1.16 (#19138)

Related to #18739.
---
 CHANGELOG.md | 415 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 405 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5cc87cae5065..01d58ce6a1b3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,32 +2,173 @@
 
 ## Next Release
 
+## Mypy 1.16
+
+We’ve just uploaded mypy 1.16 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
+Mypy is a static type checker for Python. This release includes new features and bug fixes.
+You can install it as follows:
+
+    python3 -m pip install -U mypy
+
+You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io).
+
 ### Different Property Getter and Setter Types
 
-Mypy now supports using different types for property getter and setter.
+Mypy now supports using different types for a property getter and setter:
+
 ```python
 class A:
-    value: int
+    _value: int
 
     @property
-    def f(self) -> int:
-        return self.value
-    @f.setter
-    def f(self, x: str | int) -> None:
+    def foo(self) -> int:
+        return self._value
+
+    @foo.setter
+    def foo(self, x: str | int) -> None:
         try:
-            self.value = int(x)
+            self._value = int(x)
         except ValueError:
-            raise Exception(f"'{x}' is not a valid value for 'f'")
+            raise Exception(f"'{x}' is not a valid value for 'foo'")
 ```
+This was contributed by Ivan Levkivskyi (PR [18510](https://github.com/python/mypy/pull/18510)).
+
+### Flexible Variable Redefinitions (Experimental)
+
+Mypy now allows unannotated variables to be freely redefined with
+different types when using the experimental `--allow-redefinition-new`
+flag. You will also need to enable `--local-partial-types`. Mypy will
+now infer a union type when different types are assigned to a
+variable:
 
-Contributed by Ivan Levkivskyi (PR [18510](https://github.com/python/mypy/pull/18510))
+```py
+# mypy: allow-redefinition-new, local-partial-types
+
+def f(n: int, b: bool) -> int | str:
+    if b:
+        x = n
+    else:
+        x = str(n)
+    # Type of 'x' is int | str here.
+    return x
+```
+
+Without the new flag, mypy only supports inferring optional types (`X
+| None`) from multiple assignments, but now mypy can infer arbitrary
+union types.
+
+An unannotated variable can now also have different types in different
+code locations:
+
+```py
+# mypy: allow-redefinition-new, local-partial-types
+...
+
+if cond():
+    for x in range(n):
+        # Type of 'x' is 'int' here
+        ...
+else:
+    for x in ['a', 'b']:
+        # Type of 'x' is 'str' here
+        ...
+```
+
+We are planning to turn this flag on by default in mypy 2.0, along
+with `--local-partial-types`. The feature is still experimental and
+has known issues, and the semantics may still change in the
+future. You may need to update or add type annotations when switching
+to the new behavior, but if you encounter anything unexpected, please
+create a GitHub issue.
+
+This was contributed by Jukka Lehtosalo
+(PR [18727](https://github.com/python/mypy/pull/18727), PR [19153](https://github.com/python/mypy/pull/19153)).
+
+### Stricter Type Checking with Imprecise Types
+
+Mypy can now detect additional errors in code that uses `Any` types or has missing function annotations.
+
+When calling `dict.get(x, None)` on an object of type `dict[str, Any]`, this
+now results in an optional type (in the past it was `Any`):
+
+```python
+def f(d: dict[str, Any]) -> int:
+    # Error: Return value has type "Any | None" but expected "int"
+    return d.get("x", None)
+```
+
+Type narrowing using assignments can result in more precise types in
+the presence of `Any` types:
+
+```python
+def foo(): ...
+
+def bar(n: int) -> None:
+    x = foo()
+    # Type of 'x' is 'Any' here
+    if n > 5:
+        x = str(n)
+        # Type of 'x' is 'str' here
+```
+
+When using `--check-untyped-defs`, unannotated overrides are now
+checked more strictly against superclass definitions.
+
+Related PRs:
+
+ * Use union types instead of join in binder (Ivan Levkivskyi, PR [18538](https://github.com/python/mypy/pull/18538))
+ * Check superclass compatibility of untyped methods if `--check-untyped-defs` is set (Stanislav Terliakov, PR [18970](https://github.com/python/mypy/pull/18970))
+
+### Improvements to Attribute Resolution
+
+This release includes several fixes to inconsistent resolution of attribute, method and descriptor types.
+
+ * Consolidate descriptor handling (Ivan Levkivskyi, PR [18831](https://github.com/python/mypy/pull/18831))
+ * Make multiple inheritance checking use common semantics (Ivan Levkivskyi, PR [18876](https://github.com/python/mypy/pull/18876))
+ * Make method override checking use common semantics  (Ivan Levkivskyi, PR [18870](https://github.com/python/mypy/pull/18870))
+ * Fix descriptor overload selection (Ivan Levkivskyi, PR [18868](https://github.com/python/mypy/pull/18868))
+ * Handle union types when binding `self` (Ivan Levkivskyi, PR [18867](https://github.com/python/mypy/pull/18867))
+ * Make variable override checking use common semantics (Ivan Levkivskyi, PR [18847](https://github.com/python/mypy/pull/18847))
+ * Make descriptor handling behave consistently (Ivan Levkivskyi, PR [18831](https://github.com/python/mypy/pull/18831))
+
+### Make Implementation for Abstract Overloads Optional
+
+The implementation can now be omitted for abstract overloaded methods,
+even outside stubs:
+
+```py
+from abc import abstractmethod
+from typing import overload
+
+class C:
+    @abstractmethod
+    @overload
+    def foo(self, x: int) -> int: ...
+
+    @abstractmethod
+    @overload
+    def foo(self, x: str) -> str: ...
+
+    # No implementation required for "foo"
+```
+
+This was contributed by Ivan Levkivskyi (PR [18882](https://github.com/python/mypy/pull/18882)).
+
+### Option to Exclude Everything in .gitignore
+
+You can now use `--exclude-gitignore` to exclude everything in a
+`.gitignore` file from the mypy build. This behaves similar to
+excluding the paths using `--exclude`. We might enable this by default
+in a future mypy release.
+
+This was contributed by Ivan Levkivskyi (PR [18696](https://github.com/python/mypy/pull/18696)).
 
 ### Selectively Disable Deprecated Warnings
 
 It's now possible to selectively disable warnings generated from
 [`warnings.deprecated`](https://docs.python.org/3/library/warnings.html#warnings.deprecated)
 using the [`--deprecated-calls-exclude`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-deprecated-calls-exclude)
-option.
+option:
 
 ```python
 # mypy --enable-error-code deprecated
@@ -35,16 +176,269 @@ option.
 import foo
 
 foo.A().func()  # OK, the deprecated warning is ignored
+```
 
+```python
 # file foo.py
+
 from typing_extensions import deprecated
+
 class A:
     @deprecated("Use A.func2 instead")
     def func(self): pass
+
+    ...
 ```
 
 Contributed by Marc Mueller (PR [18641](https://github.com/python/mypy/pull/18641))
 
+### Annotating Native/Non-Native Classes in Mypyc
+
+You can now declare a class as a non-native class when compiling with
+mypyc. Unlike native classes, which are extension classes and have an
+immutable structure, non-native classes are normal Python classes at
+runtime and are fully dynamic.  Example:
+
+```python
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class NonNativeClass:
+    ...
+
+o = NonNativeClass()
+
+# Ok, even if attribute "foo" not declared in class body
+setattr(o, "foo", 1)
+```
+
+Classes are native by default in compiled modules, but classes that
+use certain features (such as most metaclasses) are implicitly
+non-native.
+
+You can also explicitly declare a class as native. In this case mypyc
+will generate an error if it can't compile the class as a native
+class, instead of falling back to a non-native class:
+
+```python
+from mypy_extensions import mypyc_attr
+from foo import MyMeta
+
+# Error: Unsupported metaclass for a native class
+@mypyc_attr(native_class=True)
+class C(metaclass=MyMeta):
+    ...
+```
+
+Since native classes are significantly more efficient that non-native
+classes, you may want to ensure that certain classes always compiled
+as native classes.
+
+This feature was contributed by Valentin Stanciu (PR [18802](https://github.com/python/mypy/pull/18802)).
+
+### Mypyc Fixes and Improvements
+
+ * Improve documentation of native and non-native classes (Jukka Lehtosalo, PR [19154](https://github.com/python/mypy/pull/19154))
+ * Fix compilation when using Python 3.13 debug build (Valentin Stanciu, PR [19045](https://github.com/python/mypy/pull/19045))
+ * Show the reason why a class can't be a native class (Valentin Stanciu, PR [19016](https://github.com/python/mypy/pull/19016))
+ * Support await/yield while temporary values are live (Michael J. Sullivan, PR [16305](https://github.com/python/mypy/pull/16305))
+ * Fix spilling values with overlapping error values (Jukka Lehtosalo, PR [18961](https://github.com/python/mypy/pull/18961))
+ * Fix reference count of spilled register in async def (Jukka Lehtosalo, PR [18957](https://github.com/python/mypy/pull/18957))
+ * Add basic optimization for `sorted` (Marc Mueller, PR [18902](https://github.com/python/mypy/pull/18902))
+ * Fix access of class object in a type annotation (Advait Dixit, PR [18874](https://github.com/python/mypy/pull/18874))
+ * Optimize `list.__imul__` and `tuple.__mul__ `(Marc Mueller, PR [18887](https://github.com/python/mypy/pull/18887))
+ * Optimize `list.__add__`, `list.__iadd__` and `tuple.__add__` (Marc Mueller, PR [18845](https://github.com/python/mypy/pull/18845))
+ * Add and implement primitive `list.copy()` (exertustfm, PR [18771](https://github.com/python/mypy/pull/18771))
+ * Optimize `builtins.repr` (Marc Mueller, PR [18844](https://github.com/python/mypy/pull/18844))
+ * Support iterating over keys/values/items of dict-bound TypeVar and ParamSpec.kwargs (Stanislav Terliakov, PR [18789](https://github.com/python/mypy/pull/18789))
+ * Add efficient primitives for `str.strip()` etc. (Advait Dixit, PR [18742](https://github.com/python/mypy/pull/18742))
+ * Document that `strip()` etc. are optimized (Jukka Lehtosalo, PR [18793](https://github.com/python/mypy/pull/18793))
+ * Fix mypyc crash with enum type aliases (Valentin Stanciu, PR [18725](https://github.com/python/mypy/pull/18725))
+ * Optimize `str.find` and `str.rfind` (Marc Mueller, PR [18709](https://github.com/python/mypy/pull/18709))
+ * Optimize `str.__contains__` (Marc Mueller, PR [18705](https://github.com/python/mypy/pull/18705))
+ * Fix order of steal/unborrow in tuple unpacking (Ivan Levkivskyi, PR [18732](https://github.com/python/mypy/pull/18732))
+ * Optimize `str.partition` and `str.rpartition` (Marc Mueller, PR [18702](https://github.com/python/mypy/pull/18702))
+ * Optimize `str.startswith` and `str.endswith` with tuple argument (Marc Mueller, PR [18678](https://github.com/python/mypy/pull/18678))
+ * Improve `str.startswith` and `str.endswith` with tuple argument (Marc Mueller, PR [18703](https://github.com/python/mypy/pull/18703))
+ * `pythoncapi_compat`: don't define Py_NULL if it is already defined (Michael R. Crusoe, PR [18699](https://github.com/python/mypy/pull/18699))
+ * Optimize `str.splitlines` (Marc Mueller, PR [18677](https://github.com/python/mypy/pull/18677))
+ * Mark `dict.setdefault` as optimized (Marc Mueller, PR [18685](https://github.com/python/mypy/pull/18685))
+ * Support `__del__` methods (Advait Dixit, PR [18519](https://github.com/python/mypy/pull/18519))
+ * Optimize `str.rsplit` (Marc Mueller, PR [18673](https://github.com/python/mypy/pull/18673))
+ * Optimize `str.removeprefix` and `str.removesuffix` (Marc Mueller, PR [18672](https://github.com/python/mypy/pull/18672))
+ * Recognize literal types in `__match_args__` (Stanislav Terliakov, PR [18636](https://github.com/python/mypy/pull/18636))
+ * Fix non extension classes with attribute annotations using forward references (Valentin Stanciu, PR [18577](https://github.com/python/mypy/pull/18577))
+ * Use lower-case generic types such as `list[t]` in documentation (Jukka Lehtosalo, PR [18576](https://github.com/python/mypy/pull/18576))
+ * Improve support for `frozenset` (Marc Mueller, PR [18571](https://github.com/python/mypy/pull/18571))
+ * Fix wheel build for cp313-win (Marc Mueller, PR [18560](https://github.com/python/mypy/pull/18560))
+ * Reduce impact of immortality (introduced in Python 3.12) on reference counting performance (Jukka Lehtosalo, PR [18459](https://github.com/python/mypy/pull/18459))
+ * Update math error messages for 3.14 (Marc Mueller, PR [18534](https://github.com/python/mypy/pull/18534))
+ * Update math error messages for 3.14 (2) (Marc Mueller, PR [18949](https://github.com/python/mypy/pull/18949))
+ * Replace deprecated `_PyLong_new` with `PyLongWriter` API (Marc Mueller, PR [18532](https://github.com/python/mypy/pull/18532))
+
+### Fixes to Crashes
+
+ * Traverse module ancestors when traversing reachable graph nodes during dmypy update (Stanislav Terliakov, PR [18906](https://github.com/python/mypy/pull/18906))
+ * Fix crash on multiple unpacks in a bare type application (Stanislav Terliakov, PR [18857](https://github.com/python/mypy/pull/18857))
+ * Prevent crash when enum/TypedDict call is stored as a class attribute (Stanislav Terliakov, PR [18861](https://github.com/python/mypy/pull/18861))
+ * Fix crash on multiple unpacks in a bare type application (Stanislav Terliakov, PR [18857](https://github.com/python/mypy/pull/18857))
+ * Fix crash on type inference against non-normal callables (Ivan Levkivskyi, PR [18858](https://github.com/python/mypy/pull/18858))
+ * Fix crash on decorated getter in settable property (Ivan Levkivskyi, PR [18787](https://github.com/python/mypy/pull/18787))
+ * Fix crash on callable with `*args` and suffix against Any (Ivan Levkivskyi, PR [18781](https://github.com/python/mypy/pull/18781))
+ * Fix crash on deferred supertype and setter override (Ivan Levkivskyi, PR [18649](https://github.com/python/mypy/pull/18649))
+ * Fix crashes on incorrectly detected recursive aliases (Ivan Levkivskyi, PR [18625](https://github.com/python/mypy/pull/18625))
+ * Report that `NamedTuple` and `dataclass` are incompatile instead of crashing (Christoph Tyralla, PR [18633](https://github.com/python/mypy/pull/18633))
+ * Fix mypy daemon crash (Valentin Stanciu, PR [19087](https://github.com/python/mypy/pull/19087))
+
+### Performance Improvements
+
+These are specific to mypy. Mypyc-related performance improvements are discussed elsewhere.
+
+ * Speed up binding `self` in trivial cases (Ivan Levkivskyi, PR [19024](https://github.com/python/mypy/pull/19024))
+ * Small constraint solver optimization (Aaron Gokaslan, PR [18688](https://github.com/python/mypy/pull/18688))
+
+### Documentation Updates
+
+ * Improve documentation of `--strict` (lenayoung8, PR [18903](https://github.com/python/mypy/pull/18903))
+ * Remove a note about `from __future__ import annotations` (Ageev Maxim, PR [18915](https://github.com/python/mypy/pull/18915))
+ * Improve documentation on type narrowing (Tim Hoffmann, PR [18767](https://github.com/python/mypy/pull/18767))
+ * Fix metaclass usage example (Georg, PR [18686](https://github.com/python/mypy/pull/18686))
+ * Update documentation on `extra_checks` flag (Ivan Levkivskyi, PR [18537](https://github.com/python/mypy/pull/18537))
+
+### Stubgen Improvements
+
+ * Fix `TypeAlias` handling (Alexey Makridenko, PR [18960](https://github.com/python/mypy/pull/18960))
+ * Handle `arg=None` in C extension modules (Anthony Sottile, PR [18768](https://github.com/python/mypy/pull/18768))
+ * Fix valid type detection to allow pipe unions (Chad Dombrova, PR [18726](https://github.com/python/mypy/pull/18726))
+ * Include simple decorators in stub files (Marc Mueller, PR [18489](https://github.com/python/mypy/pull/18489))
+ * Support positional and keyword-only arguments in stubdoc (Paul Ganssle, PR [18762](https://github.com/python/mypy/pull/18762))
+ * Fall back to `Incomplete` if we are unable to determine the module name (Stanislav Terliakov, PR [19084](https://github.com/python/mypy/pull/19084))
+
+### Stubtest Improvements
+
+ * Make stubtest ignore `__slotnames__` (Nick Pope, PR [19077](https://github.com/python/mypy/pull/19077))
+ * Fix stubtest tests on 3.14 (Jelle Zijlstra, PR [19074](https://github.com/python/mypy/pull/19074))
+ * Support for `strict_bytes` in stubtest (Joren Hammudoglu, PR [19002](https://github.com/python/mypy/pull/19002))
+ * Understand override (Shantanu, PR [18815](https://github.com/python/mypy/pull/18815))
+ * Better checking of runtime arguments with dunder names (Shantanu, PR [18756](https://github.com/python/mypy/pull/18756))
+ * Ignore setattr and delattr inherited from object (Stephen Morton, PR [18325](https://github.com/python/mypy/pull/18325))
+
+### Miscellaneous Fixes and Improvements
+
+ * Add `--strict-bytes` to `--strict` (wyattscarpenter, PR [19049](https://github.com/python/mypy/pull/19049))
+ * Admit that Final variables are never redefined (Stanislav Terliakov, PR [19083](https://github.com/python/mypy/pull/19083))
+ * Add special support for `@django.cached_property` needed in `django-stubs` (sobolevn, PR [18959](https://github.com/python/mypy/pull/18959))
+ * Do not narrow types to `Never` with binder (Ivan Levkivskyi, PR [18972](https://github.com/python/mypy/pull/18972))
+ * Local forward references should precede global forward references (Ivan Levkivskyi, PR [19000](https://github.com/python/mypy/pull/19000))
+ * Do not cache module lookup results in incremental mode that may become invalid (Stanislav Terliakov, PR [19044](https://github.com/python/mypy/pull/19044))
+ * Only consider meta variables in ambiguous "any of" constraints (Stanislav Terliakov, PR [18986](https://github.com/python/mypy/pull/18986))
+ * Allow accessing `__init__` on final classes and when `__init__` is final (Stanislav Terliakov, PR [19035](https://github.com/python/mypy/pull/19035))
+ * Treat varargs as positional-only (A5rocks, PR [19022](https://github.com/python/mypy/pull/19022))
+ * Enable colored output for argparse help in Python 3.14 (Marc Mueller, PR [19021](https://github.com/python/mypy/pull/19021))
+ * Fix argparse for Python 3.14 (Marc Mueller, PR [19020](https://github.com/python/mypy/pull/19020))
+ * `dmypy suggest` can now suggest through contextmanager-based decorators (Anthony Sottile, PR [18948](https://github.com/python/mypy/pull/18948))
+ * Fix `__r__` being used under the same `____` hook (Arnav Jain, PR [18995](https://github.com/python/mypy/pull/18995))
+ * Prioritize `.pyi` from `-stubs` packages over bundled `.pyi` (Joren Hammudoglu, PR [19001](https://github.com/python/mypy/pull/19001))
+ * Fix missing subtype check case for `type[T]` (Stanislav Terliakov, PR [18975](https://github.com/python/mypy/pull/18975))
+ * Fixes to the detection of redundant casts (Anthony Sottile, PR [18588](https://github.com/python/mypy/pull/18588))
+ * Make some parse errors non-blocking (Shantanu, PR [18941](https://github.com/python/mypy/pull/18941))
+ * Fix PEP 695 type alias with a mix of type arguments (PEP 696) (Marc Mueller, PR [18919](https://github.com/python/mypy/pull/18919))
+ * Allow deeper recursion in mypy daemon, better error reporting (Carter Dodd, PR [17707](https://github.com/python/mypy/pull/17707))
+ * Fix swapped errors for frozen/non-frozen dataclass inheritance (Nazrawi Demeke, PR [18918](https://github.com/python/mypy/pull/18918))
+ * Fix incremental issue with namespace packages (Shantanu, PR [18907](https://github.com/python/mypy/pull/18907))
+ * Exclude irrelevant members when narrowing union overlapping with enum (Stanislav Terliakov, PR [18897](https://github.com/python/mypy/pull/18897))
+ * Flatten union before contracting literals when checking subtyping (Stanislav Terliakov, PR [18898](https://github.com/python/mypy/pull/18898))
+ * Do not add `kw_only` dataclass fields to `__match_args__` (sobolevn, PR [18892](https://github.com/python/mypy/pull/18892))
+ * Fix error message when returning long tuple with type mismatch (Thomas Mattone, PR [18881](https://github.com/python/mypy/pull/18881))
+ * Treat `TypedDict` (old-style) aliases as regular `TypedDict`s (Stanislav Terliakov, PR [18852](https://github.com/python/mypy/pull/18852))
+ * Warn about unused `type: ignore` comments when error code is disabled (Brian Schubert, PR [18849](https://github.com/python/mypy/pull/18849))
+ * Reject duplicate `ParamSpec.{args,kwargs}` at call site (Stanislav Terliakov, PR [18854](https://github.com/python/mypy/pull/18854))
+ * Make detection of enum members more consistent (sobolevn, PR [18675](https://github.com/python/mypy/pull/18675))
+ * Admit that `**kwargs` mapping subtypes may have no direct type parameters (Stanislav Terliakov, PR [18850](https://github.com/python/mypy/pull/18850))
+ * Don't suggest `types-setuptools` for `pkg_resources` (Shantanu, PR [18840](https://github.com/python/mypy/pull/18840))
+ * Suggest `scipy-stubs` for `scipy` as non-typeshed stub package (Joren Hammudoglu, PR [18832](https://github.com/python/mypy/pull/18832))
+ * Narrow tagged unions in match statements (Gene Parmesan Thomas, PR [18791](https://github.com/python/mypy/pull/18791))
+ * Consistently store settable property type (Ivan Levkivskyi, PR [18774](https://github.com/python/mypy/pull/18774))
+ * Do not blindly undefer on leaving function (Ivan Levkivskyi, PR [18674](https://github.com/python/mypy/pull/18674))
+ * Process superclass methods before subclass methods in semanal (Ivan Levkivskyi, PR [18723](https://github.com/python/mypy/pull/18723))
+ * Only defer top-level functions (Ivan Levkivskyi, PR [18718](https://github.com/python/mypy/pull/18718))
+ * Add one more type-checking pass (Ivan Levkivskyi, PR [18717](https://github.com/python/mypy/pull/18717))
+ * Properly account for `member` and `nonmember` in enums (sobolevn, PR [18559](https://github.com/python/mypy/pull/18559))
+ * Fix instance vs tuple subtyping edge case (Ivan Levkivskyi, PR [18664](https://github.com/python/mypy/pull/18664))
+ * Improve handling of Any/object in variadic generics (Ivan Levkivskyi, PR [18643](https://github.com/python/mypy/pull/18643))
+ * Fix handling of named tuples in class match pattern (Ivan Levkivskyi, PR [18663](https://github.com/python/mypy/pull/18663))
+ * Fix regression for user config files (Shantanu, PR [18656](https://github.com/python/mypy/pull/18656))
+ * Fix dmypy socket issue on GNU/Hurd (Mattias Ellert, PR [18630](https://github.com/python/mypy/pull/18630))
+ * Don't assume that for loop body index variable is always set (Jukka Lehtosalo, PR [18631](https://github.com/python/mypy/pull/18631))
+ * Fix overlap check for variadic generics (Ivan Levkivskyi, PR [18638](https://github.com/python/mypy/pull/18638))
+ * Improve support for `functools.partial` of overloaded callable protocol (Shantanu, PR [18639](https://github.com/python/mypy/pull/18639))
+ * Allow lambdas in `except*` clauses (Stanislav Terliakov, PR [18620](https://github.com/python/mypy/pull/18620))
+ * Fix trailing commas in many multiline string options in `pyproject.toml` (sobolevn, PR [18624](https://github.com/python/mypy/pull/18624))
+ * Allow trailing commas for `files` setting in `mypy.ini` and `setup.ini` (sobolevn, PR [18621](https://github.com/python/mypy/pull/18621))
+ * Fix "not callable" issue for `@dataclass(frozen=True)` with `Final` attr (sobolevn, PR [18572](https://github.com/python/mypy/pull/18572))
+ * Add missing TypedDict special case when checking member access (Stanislav Terliakov, PR [18604](https://github.com/python/mypy/pull/18604))
+ * Use lower case `list` and `dict` in invariance notes (Jukka Lehtosalo, PR [18594](https://github.com/python/mypy/pull/18594))
+ * Fix inference when class and instance match protocol (Ivan Levkivskyi, PR [18587](https://github.com/python/mypy/pull/18587))
+ * Remove support for `builtins.Any` (Marc Mueller, PR [18578](https://github.com/python/mypy/pull/18578))
+ * Update the overlapping check for tuples to account for NamedTuples (A5rocks, PR [18564](https://github.com/python/mypy/pull/18564))
+ * Fix `@deprecated` (PEP 702) with normal overloaded methods (Christoph Tyralla, PR [18477](https://github.com/python/mypy/pull/18477))
+ * Start propagating end columns/lines for `type-arg` errors (A5rocks, PR [18533](https://github.com/python/mypy/pull/18533))
+ * Improve handling of `type(x) is Foo` checks (Stanislav Terliakov, PR [18486](https://github.com/python/mypy/pull/18486))
+ * Suggest `typing.Literal` for exit-return error messages (Marc Mueller, PR [18541](https://github.com/python/mypy/pull/18541))
+ * Allow redefinitions in except/else/finally (Stanislav Terliakov, PR [18515](https://github.com/python/mypy/pull/18515))
+ * Disallow setting Python version using inline config (Shantanu, PR [18497](https://github.com/python/mypy/pull/18497))
+ * Improve type inference in tuple multiplication plugin (Shantanu, PR [18521](https://github.com/python/mypy/pull/18521))
+ * Add missing line number to `yield from` with wrong type (Stanislav Terliakov, PR [18518](https://github.com/python/mypy/pull/18518))
+ * Hint at argument names when formatting callables with compatible return types in error messages (Stanislav Terliakov, PR [18495](https://github.com/python/mypy/pull/18495))
+ * Add better naming and improve compatibility for ad hoc intersections of instances (Christoph Tyralla, PR [18506](https://github.com/python/mypy/pull/18506))
+
+### Acknowledgements
+
+Thanks to all mypy contributors who contributed to this release:
+
+- A5rocks
+- Aaron Gokaslan
+- Advait Dixit
+- Ageev Maxim
+- Alexey Makridenko
+- Ali Hamdan
+- Anthony Sottile
+- Arnav Jain
+- Brian Schubert
+- bzoracler
+- Carter Dodd
+- Chad Dombrova
+- Christoph Tyralla
+- Dimitri Papadopoulos Orfanos
+- Emma Smith
+- exertustfm
+- Gene Parmesan Thomas
+- Georg
+- Ivan Levkivskyi
+- Jared Hance
+- Jelle Zijlstra
+- Joren Hammudoglu
+- lenayoung8
+- Marc Mueller
+- Mattias Ellert
+- Michael J. Sullivan
+- Michael R. Crusoe
+- Nazrawi Demeke
+- Nick Pope
+- Paul Ganssle
+- Shantanu
+- sobolevn
+- Stanislav Terliakov
+- Stephen Morton
+- Thomas Mattone
+- Tim Hoffmann
+- Tim Ruffing
+- Valentin Stanciu
+- Wesley Collin Wright
+- wyattscarpenter
+
+I’d also like to thank my employer, Dropbox, for supporting mypy development.
+
 ## Mypy 1.15
 
 We’ve just uploaded mypy 1.15 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
@@ -408,6 +802,7 @@ This was contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/
 
 ### Other Notables Fixes and Improvements
 
+ * Allow enum members to have type objects as values (Jukka Lehtosalo, PR [19160](https://github.com/python/mypy/pull/19160))
  * Show `Protocol` `__call__` for arguments with incompatible types (MechanicalConstruct, PR [18214](https://github.com/python/mypy/pull/18214))
  * Make join and meet symmetric with `strict_optional` (MechanicalConstruct, PR [18227](https://github.com/python/mypy/pull/18227))
  * Preserve block unreachablility when checking function definitions with constrained TypeVars (Brian Schubert, PR [18217](https://github.com/python/mypy/pull/18217))

From 6551bce250af1414530406dd979f0401c3403fa1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 29 May 2025 17:37:11 +0100
Subject: [PATCH 1321/1617] [mypyc] Enable partial, unsafe support for
 free-threading (#19167)

Enable multi-phase init when using a free-threaded (no-GIL) CPython
build so we can enable proper multihreading.

Work on mypyc/mypyc#1104. Work on mypyc/mypyc#1038.

The implementation is still quite incomplete. We are missing
synchronization in various places, so race conditions can cause
segfaults. Only single-module compilation units are supported for now.

Here's a toy benchmark I used to check that free threading works and can
improve performance:
```
import sys
import threading
import time

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

NTHREADS = 6
print(f"Using {NTHREADS} threads")
print(f"{sys._is_gil_enabled()=}")

t0 = time.time()

threads = []
for i in range(NTHREADS):
    t = threading.Thread(target=lambda: fib(36))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print()
print('elapsed time:', time.time() - t0)
```
---
 mypyc/codegen/emitmodule.py | 51 +++++++++++++++++++++++++++++++++----
 mypyc/common.py             |  4 +++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index a3970b9c181e..8474be62579d 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -7,6 +7,7 @@
 
 import json
 import os
+import sys
 from collections.abc import Iterable
 from typing import Optional, TypeVar
 
@@ -38,6 +39,7 @@
 )
 from mypyc.codegen.literals import Literals
 from mypyc.common import (
+    IS_FREE_THREADED,
     MODULE_PREFIX,
     PREFIX,
     RUNTIME_C_FILES,
@@ -513,6 +515,9 @@ def __init__(
         self.use_shared_lib = group_name is not None
         self.compiler_options = compiler_options
         self.multi_file = compiler_options.multi_file
+        # Multi-phase init is needed to enable free-threading. In the future we'll
+        # probably want to enable it always, but we'll wait until it's stable.
+        self.multi_phase_init = IS_FREE_THREADED
 
     @property
     def group_suffix(self) -> str:
@@ -869,10 +874,31 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
         """Emit the PyModuleDef struct for a module and the module init function."""
         module_prefix = emitter.names.private_name(module_name)
         self.emit_module_exec_func(emitter, module_name, module_prefix, module)
+        if self.multi_phase_init:
+            self.emit_module_def_slots(emitter, module_prefix)
         self.emit_module_methods(emitter, module_name, module_prefix, module)
         self.emit_module_def_struct(emitter, module_name, module_prefix)
         self.emit_module_init_func(emitter, module_name, module_prefix)
 
+    def emit_module_def_slots(self, emitter: Emitter, module_prefix: str) -> None:
+        name = f"{module_prefix}_slots"
+        exec_name = f"{module_prefix}_exec"
+
+        emitter.emit_line(f"static PyModuleDef_Slot {name}[] = {{")
+        emitter.emit_line(f"{{Py_mod_exec, {exec_name}}},")
+        if sys.version_info >= (3, 12):
+            # Multiple interpreter support requires not using any C global state,
+            # which we don't support yet.
+            emitter.emit_line(
+                "{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},"
+            )
+        if sys.version_info >= (3, 13):
+            # Declare support for free-threading to enable experimentation,
+            # even if we don't properly support it.
+            emitter.emit_line("{Py_mod_gil, Py_MOD_GIL_NOT_USED},")
+        emitter.emit_line("{0, NULL},")
+        emitter.emit_line("};")
+
     def emit_module_methods(
         self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
     ) -> None:
@@ -905,11 +931,15 @@ def emit_module_def_struct(
             "PyModuleDef_HEAD_INIT,",
             f'"{module_name}",',
             "NULL, /* docstring */",
-            "-1,       /* size of per-interpreter state of the module,",
-            "             or -1 if the module keeps state in global variables. */",
-            f"{module_prefix}module_methods",
-            "};",
+            "0,       /* size of per-interpreter state of the module */",
+            f"{module_prefix}module_methods,",
         )
+        if self.multi_phase_init:
+            slots_name = f"{module_prefix}_slots"
+            emitter.emit_line(f"{slots_name}, /* m_slots */")
+        else:
+            emitter.emit_line("NULL,")
+        emitter.emit_line("};")
         emitter.emit_line()
 
     def emit_module_exec_func(
@@ -927,6 +957,8 @@ def emit_module_exec_func(
         module_static = self.module_internal_static_name(module_name, emitter)
         emitter.emit_lines(declaration, "{")
         emitter.emit_line("PyObject* modname = NULL;")
+        if self.multi_phase_init:
+            emitter.emit_line(f"{module_static} = module;")
         emitter.emit_line(
             f'modname = PyObject_GetAttrString((PyObject *){module_static}, "__name__");'
         )
@@ -958,7 +990,10 @@ def emit_module_exec_func(
 
         emitter.emit_line("return 0;")
         emitter.emit_lines("fail:")
-        emitter.emit_lines(f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
+        if self.multi_phase_init:
+            emitter.emit_lines(f"{module_static} = NULL;", "Py_CLEAR(modname);")
+        else:
+            emitter.emit_lines(f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
         for name, typ in module.final_names:
             static_name = emitter.static_name(name, module_name)
             emitter.emit_dec_ref(static_name, typ, is_xdec=True)
@@ -980,6 +1015,12 @@ def emit_module_init_func(
             declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)"
         emitter.emit_lines(declaration, "{")
 
+        if self.multi_phase_init:
+            def_name = f"{module_prefix}module"
+            emitter.emit_line(f"return PyModuleDef_Init(&{def_name});")
+            emitter.emit_line("}")
+            return
+
         exec_func = f"{module_prefix}_exec"
 
         # Store the module reference in a static and return it when necessary.
diff --git a/mypyc/common.py b/mypyc/common.py
index 992376472086..b5506eed89c2 100644
--- a/mypyc/common.py
+++ b/mypyc/common.py
@@ -88,6 +88,10 @@
 # some details in the PEP are out of date.
 HAVE_IMMORTAL: Final = sys.version_info >= (3, 12)
 
+# Are we running on a free-threaded build (GIL disabled)? This implies that
+# we are on Python 3.13 or later.
+IS_FREE_THREADED: Final = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
+
 
 JsonDict = dict[str, Any]
 

From 95a09c8ce36c9f640fe097fd2879b6b48816dec6 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 29 May 2025 23:02:17 +0200
Subject: [PATCH 1322/1617] Start testing Python 3.14 (#19164)

---
 .github/workflows/docs.yml    |  2 +-
 .github/workflows/test.yml    | 23 ++++++++++++-----------
 mypyc/test-data/run-misc.test |  5 ++++-
 tox.ini                       |  4 +++-
 4 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 3f945b84b7f0..3e78bf51913e 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -41,7 +41,7 @@ jobs:
         with:
           python-version: '3.12'
       - name: Install tox
-        run: pip install tox==4.21.2
+        run: pip install tox==4.26.0
       - name: Setup tox environment
         run: tox run -e ${{ env.TOXENV }} --notest
       - name: Test
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c42550431bb1..97fb7755563b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -47,12 +47,11 @@ jobs:
           os: ubuntu-24.04-arm
           toxenv: py
           tox_extra_args: "-n 4"
-        - name: Test suite with py311-ubuntu, mypyc-compiled
+        - name: Test suite with py311-ubuntu
           python: '3.11'
           os: ubuntu-24.04-arm
           toxenv: py
           tox_extra_args: "-n 4"
-          test_mypyc: true
         - name: Test suite with py312-ubuntu, mypyc-compiled
           python: '3.12'
           os: ubuntu-24.04-arm
@@ -66,13 +65,13 @@ jobs:
           tox_extra_args: "-n 4"
           test_mypyc: true
 
-        # - name: Test suite with py314-dev-ubuntu
-        #   python: '3.14-dev'
-        #   os: ubuntu-24.04-arm
-        #   toxenv: py
-        #   tox_extra_args: "-n 4"
-        #   allow_failure: true
-        #   test_mypyc: true
+        - name: Test suite with py314-dev-ubuntu
+          python: '3.14-dev'
+          os: ubuntu-24.04-arm
+          toxenv: py
+          tox_extra_args: "-n 4"
+          # allow_failure: true
+          test_mypyc: true
 
         - name: mypyc runtime tests with py39-macos
           python: '3.9.21'
@@ -115,6 +114,8 @@ jobs:
       FORCE_COLOR: ${{ !(startsWith(matrix.os, 'windows-') && startsWith(matrix.toxenv, 'py')) && 1 || 0 }}
       # Tox
       PY_COLORS: 1
+      # Python -- Disable argparse help colors (3.14+)
+      PYTHON_COLORS: 0
       # Mypy (see https://github.com/python/mypy/issues/7771)
       TERM: xterm-color
       MYPY_FORCE_COLOR: 1
@@ -167,7 +168,7 @@ jobs:
         echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))'
         echo os.cpu_count; python -c 'import os; print(os.cpu_count())'
         echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))'
-        pip install setuptools==75.1.0 tox==4.21.2
+        pip install setuptools==75.1.0 tox==4.26.0
 
     - name: Compiled with mypyc
       if: ${{ matrix.test_mypyc }}
@@ -230,7 +231,7 @@ jobs:
           default: 3.11.1
           command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');"
       - name: Install tox
-        run: pip install setuptools==75.1.0 tox==4.21.2
+        run: pip install setuptools==75.1.0 tox==4.26.0
       - name: Setup tox environment
         run: tox run -e py --notest
       - name: Test
diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test
index f12d6618681a..f6a1c744cade 100644
--- a/mypyc/test-data/run-misc.test
+++ b/mypyc/test-data/run-misc.test
@@ -969,7 +969,10 @@ print(z)
 [case testCheckVersion]
 import sys
 
-if sys.version_info[:2] == (3, 13):
+if sys.version_info[:2] == (3, 14):
+    def version() -> int:
+        return 14
+elif sys.version_info[:2] == (3, 13):
     def version() -> int:
         return 13
 elif sys.version_info[:2] == (3, 12):
diff --git a/tox.ini b/tox.ini
index a505950521fa..65f67aba42a2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,6 +8,7 @@ envlist =
     py311,
     py312,
     py313,
+    py314,
     docs,
     lint,
     type,
@@ -16,10 +17,11 @@ isolated_build = true
 [testenv]
 description = run the test driver with {basepython}
 passenv =
-    PYTEST_XDIST_WORKER_COUNT
     PROGRAMDATA
     PROGRAMFILES(X86)
     PYTEST_ADDOPTS
+    PYTEST_XDIST_WORKER_COUNT
+    PYTHON_COLORS
 deps =
     -r test-requirements.txt
     # This is a bit of a hack, but ensures the faster-cache path is tested in CI

From 409d294dc1745d30f958631b2ddebdc9a7d262ff Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 30 May 2025 13:25:10 +0200
Subject: [PATCH 1323/1617] Remove force_uppercase_builtins default from test
 helpers (#19173)

Mypy only supports Python 3.9+. Update the tests to use lowercase names
for PEP 585 generics in error messages. A followup can consider
deprecating `--force-uppercase-builtins` and making it a no-op.
---
 mypy/test/helpers.py                          |  10 +-
 mypy/test/testcheck.py                        |   2 -
 mypy/test/testcmdline.py                      |   2 -
 mypy/test/testmerge.py                        |   1 -
 mypy/test/testparse.py                        |   1 -
 mypy/test/testpythoneval.py                   |   1 -
 mypy/test/testsemanal.py                      |   1 -
 mypy/test/testtransform.py                    |   1 -
 mypy/test/testtypegen.py                      |   1 -
 mypy/test/testtypes.py                        |  19 +-
 test-data/unit/check-abstract.test            |  18 +-
 test-data/unit/check-annotated.test           |   2 +-
 test-data/unit/check-async-await.test         |  10 +-
 test-data/unit/check-basic.test               |  24 +-
 test-data/unit/check-class-namedtuple.test    |  52 +--
 test-data/unit/check-classes.test             | 192 ++++++------
 test-data/unit/check-columns.test             |  18 +-
 test-data/unit/check-ctypes.test              |   8 +-
 test-data/unit/check-custom-plugin.test       |   2 +-
 test-data/unit/check-dataclass-transform.test |  12 +-
 test-data/unit/check-dataclasses.test         |  32 +-
 test-data/unit/check-deprecated.test          |   2 +-
 test-data/unit/check-dynamic-typing.test      |   8 +-
 test-data/unit/check-enum.test                |  20 +-
 test-data/unit/check-errorcodes.test          |  10 +-
 test-data/unit/check-expressions.test         |  52 +--
 test-data/unit/check-final.test               |   4 +-
 test-data/unit/check-flags.test               |  54 ++--
 test-data/unit/check-formatting.test          |   4 +-
 test-data/unit/check-functions.test           |  44 +--
 test-data/unit/check-functools.test           |   8 +-
 test-data/unit/check-generic-alias.test       |  14 +-
 test-data/unit/check-generic-subtyping.test   |   4 +-
 test-data/unit/check-generics.test            |  72 ++---
 test-data/unit/check-incremental.test         |  18 +-
 test-data/unit/check-inference-context.test   |  22 +-
 test-data/unit/check-inference.test           | 200 ++++++------
 test-data/unit/check-inline-config.test       |   2 +-
 test-data/unit/check-isinstance.test          | 142 ++++-----
 test-data/unit/check-kwargs.test              |  26 +-
 test-data/unit/check-literal.test             |  66 ++--
 test-data/unit/check-modules.test             |   8 +-
 test-data/unit/check-namedtuple.test          | 142 ++++-----
 test-data/unit/check-narrowing.test           | 220 ++++++-------
 test-data/unit/check-newsemanal.test          |  64 ++--
 test-data/unit/check-newsyntax.test           |   2 +-
 test-data/unit/check-newtype.test             |   4 +-
 test-data/unit/check-optional.test            |  10 +-
 test-data/unit/check-overloading.test         |  60 ++--
 .../unit/check-parameter-specification.test   |  26 +-
 test-data/unit/check-plugin-attrs.test        |  62 ++--
 test-data/unit/check-protocols.test           |  84 ++---
 test-data/unit/check-python310.test           |  56 ++--
 test-data/unit/check-python311.test           |  16 +-
 test-data/unit/check-python312.test           |  20 +-
 test-data/unit/check-python313.test           |   8 +-
 test-data/unit/check-python38.test            |   8 +-
 test-data/unit/check-recursive-types.test     |  58 ++--
 test-data/unit/check-redefine.test            |   2 +-
 test-data/unit/check-redefine2.test           |   4 +-
 test-data/unit/check-selftype.test            |  88 +++---
 test-data/unit/check-serialize.test           |  36 +--
 test-data/unit/check-statements.test          |   6 +-
 test-data/unit/check-tuples.test              | 216 ++++++-------
 test-data/unit/check-type-aliases.test        |  48 +--
 .../check-type-object-type-inference.test     |  16 +-
 test-data/unit/check-typeddict.test           |  66 ++--
 test-data/unit/check-typeguard.test           |   8 +-
 test-data/unit/check-typeis.test              |   6 +-
 test-data/unit/check-typevar-defaults.test    |  38 +--
 test-data/unit/check-typevar-tuple.test       | 296 +++++++++---------
 test-data/unit/check-typevar-values.test      |   2 +-
 test-data/unit/check-union-or-syntax.test     |   4 +-
 test-data/unit/check-unions.test              |  52 +--
 test-data/unit/check-varargs.test             |  84 ++---
 test-data/unit/check-warnings.test            |   2 +-
 test-data/unit/cmdline.test                   |   8 +-
 test-data/unit/fine-grained-inspect.test      |   2 +-
 test-data/unit/fine-grained-python312.test    |   4 +-
 test-data/unit/fine-grained.test              |  66 ++--
 test-data/unit/merge.test                     |  16 +-
 test-data/unit/parse.test                     |   8 +-
 test-data/unit/pythoneval.test                |  78 ++---
 test-data/unit/semanal-classes.test           |   6 +-
 test-data/unit/semanal-namedtuple.test        |  30 +-
 test-data/unit/semanal-typealiases.test       |   8 +-
 test-data/unit/semanal-types.test             |  10 +-
 test-data/unit/typexport-basic.test           |  10 +-
 88 files changed, 1617 insertions(+), 1632 deletions(-)

diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py
index fcec68094e51..ae432ff6981b 100644
--- a/mypy/test/helpers.py
+++ b/mypy/test/helpers.py
@@ -258,11 +258,12 @@ def local_sys_path_set() -> Iterator[None]:
 
 
 def testfile_pyversion(path: str) -> tuple[int, int]:
-    m = re.search(r"python3([0-9]+)\.test$", path)
-    if m:
-        return 3, int(m.group(1))
+    if m := re.search(r"python3([0-9]+)\.test$", path):
+        # For older unsupported version like python38,
+        # default to that earliest supported version.
+        return max((3, int(m.group(1))), defaults.PYTHON3_VERSION_MIN)
     else:
-        return defaults.PYTHON3_VERSION
+        return defaults.PYTHON3_VERSION_MIN
 
 
 def normalize_error_messages(messages: list[str]) -> list[str]:
@@ -353,7 +354,6 @@ def parse_options(
         options = Options()
         options.error_summary = False
         options.hide_error_codes = True
-        options.force_uppercase_builtins = True
         options.force_union_syntax = True
 
     # Allow custom python version to override testfile_pyversion.
diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py
index e6415ddff906..fb2eb3a75b9b 100644
--- a/mypy/test/testcheck.py
+++ b/mypy/test/testcheck.py
@@ -136,8 +136,6 @@ 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 "union-error" not in testcase.file:
             options.force_union_syntax = True
 
diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py
index 9bc02d319964..11d229042978 100644
--- a/mypy/test/testcmdline.py
+++ b/mypy/test/testcmdline.py
@@ -61,8 +61,6 @@ 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")
     if "--no-force-union-syntax" not in args:
         args.append("--force-union-syntax")
     # Type check the program.
diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py
index 51a4ff39dd9a..c2c75f60be29 100644
--- a/mypy/test/testmerge.py
+++ b/mypy/test/testmerge.py
@@ -102,7 +102,6 @@ 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
diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py
index 074ccfb379d0..027ca4dd2887 100644
--- a/mypy/test/testparse.py
+++ b/mypy/test/testparse.py
@@ -38,7 +38,6 @@ 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"):
diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py
index 32c07087292e..6d22aca07da7 100644
--- a/mypy/test/testpythoneval.py
+++ b/mypy/test/testpythoneval.py
@@ -52,7 +52,6 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None
         "--no-error-summary",
         "--hide-error-codes",
         "--allow-empty-bodies",
-        "--force-uppercase-builtins",
         "--test-env",  # Speeds up some checks
     ]
     interpreter = python3_path
diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py
index a544e1f91829..741c03fc2dc2 100644
--- a/mypy/test/testsemanal.py
+++ b/mypy/test/testsemanal.py
@@ -44,7 +44,6 @@ def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Opti
     options.semantic_analysis_only = True
     options.show_traceback = True
     options.python_version = PYTHON3_VERSION
-    options.force_uppercase_builtins = True
     return options
 
 
diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py
index 9388dca02c7a..48a3eeed2115 100644
--- a/mypy/test/testtransform.py
+++ b/mypy/test/testtransform.py
@@ -38,7 +38,6 @@ def test_transform(testcase: DataDrivenTestCase) -> None:
         options.use_builtins_fixtures = True
         options.semantic_analysis_only = True
         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
         )
diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py
index 4933bd3522a0..42d831beeecc 100644
--- a/mypy/test/testtypegen.py
+++ b/mypy/test/testtypegen.py
@@ -35,7 +35,6 @@ 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,
diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py
index 63d8840fa217..0fe41bc28ecd 100644
--- a/mypy/test/testtypes.py
+++ b/mypy/test/testtypes.py
@@ -23,7 +23,6 @@
     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
@@ -130,17 +129,13 @@ def test_callable_type_with_var_args(self) -> None:
         )
         assert_equal(str(c3), "def (X? =, *Y?) -> Any")
 
-    def test_tuple_type_upper(self) -> None:
-        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(
-            TupleType(
-                [self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple
-            ).str_with_options(options),
-            "Tuple[X?, Any]",
-        )
+    def test_tuple_type_str(self) -> None:
+        t1 = TupleType([], self.fx.std_tuple)
+        assert_equal(str(t1), "tuple[()]")
+        t2 = TupleType([self.x], self.fx.std_tuple)
+        assert_equal(str(t2), "tuple[X?]")
+        t3 = TupleType([self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple)
+        assert_equal(str(t3), "tuple[X?, Any]")
 
     def test_type_variable_binding(self) -> None:
         assert_equal(
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
index 455ee3c5265b..2fed3425c8d4 100644
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -191,8 +191,8 @@ def f(cls: Type[A]) -> A:
 def g() -> A:
     return A()  # E: Cannot instantiate abstract class "A" with abstract attribute "m"
 
-f(A)  # E: Only concrete class can be given where "Type[A]" is expected
-f(B)  # E: Only concrete class can be given where "Type[A]" is expected
+f(A)  # E: Only concrete class can be given where "type[A]" is expected
+f(B)  # E: Only concrete class can be given where "type[A]" is expected
 f(C)  # OK
 x: Type[B]
 f(x)  # OK
@@ -207,7 +207,7 @@ class Class:
     def method(self) -> None:
         pass
 
-my_dict_init: Dict[int, Type[Class]] = {0: Class}  # E: Only concrete class can be given where "Tuple[int, Type[Class]]" is expected
+my_dict_init: Dict[int, Type[Class]] = {0: Class}  # E: Only concrete class can be given where "tuple[int, type[Class]]" is expected
 
 class Child(Class):
     def method(self) -> None: ...
@@ -235,7 +235,7 @@ Alias = A
 GoodAlias = C
 Alias()  # E: Cannot instantiate abstract class "A" with abstract attribute "m"
 GoodAlias()
-f(Alias)  # E: Only concrete class can be given where "Type[A]" is expected
+f(Alias)  # E: Only concrete class can be given where "type[A]" is expected
 f(GoodAlias)
 [out]
 
@@ -255,18 +255,18 @@ class C(B):
 var: Type[A]
 var()
 if int():
-    var = A # E: Can only assign concrete classes to a variable of type "Type[A]"
+    var = A # E: Can only assign concrete classes to a variable of type "type[A]"
 if int():
-    var = B # E: Can only assign concrete classes to a variable of type "Type[A]"
+    var = B # E: Can only assign concrete classes to a variable of type "type[A]"
 if int():
     var = C # OK
 
 var_old = None # type: Type[A] # Old syntax for variable annotations
 var_old()
 if int():
-    var_old = A # E: Can only assign concrete classes to a variable of type "Type[A]"
+    var_old = A # E: Can only assign concrete classes to a variable of type "type[A]"
 if int():
-    var_old = B # E: Can only assign concrete classes to a variable of type "Type[A]"
+    var_old = B # E: Can only assign concrete classes to a variable of type "type[A]"
 if int():
     var_old = C # OK
 
@@ -277,7 +277,7 @@ class D(A):
     def __new__(cls) -> "D": ...
     def __new__(cls, a=None) -> "D": ...
 if int():
-    var = D # E: Can only assign concrete classes to a variable of type "Type[A]"
+    var = D # E: Can only assign concrete classes to a variable of type "type[A]"
 [out]
 
 [case testInstantiationAbstractsInTypeForClassMethods]
diff --git a/test-data/unit/check-annotated.test b/test-data/unit/check-annotated.test
index 54d9715a3897..24f4a1d945c6 100644
--- a/test-data/unit/check-annotated.test
+++ b/test-data/unit/check-annotated.test
@@ -105,7 +105,7 @@ from typing_extensions import Annotated
 T = TypeVar('T')
 Alias = Annotated[Tuple[T, T], ...]
 x: Alias[int]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testAnnotatedAliasGenericUnion]
diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test
index 0ef08e5a0775..979da62aca92 100644
--- a/test-data/unit/check-async-await.test
+++ b/test-data/unit/check-async-await.test
@@ -163,7 +163,7 @@ async def f() -> None:
 [builtins fixtures/async_await.pyi]
 [typing fixtures/typing-async.pyi]
 [out]
-main:4: error: "List[int]" has no attribute "__aiter__" (not async iterable)
+main:4: error: "list[int]" has no attribute "__aiter__" (not async iterable)
 
 [case testAsyncForErrorNote]
 
@@ -502,7 +502,7 @@ async def gen() -> AsyncGenerator[int, str]:
 
 async def h() -> None:
     g = gen()
-    await g.asend(())  # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "Tuple[()]"; expected "str"
+    await g.asend(())  # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "tuple[()]"; expected "str"
     reveal_type(await g.asend('hello'))  # N: Revealed type is "builtins.int"
 
 [builtins fixtures/dict.pyi]
@@ -913,9 +913,9 @@ async def test(x: Sub[D], tx: Type[Sub[D]]) -> None:
     unknown2: Awaitable[Any]
     d: C = unknown2  # E: Incompatible types in assignment (expression has type "Awaitable[Any]", variable has type "C")
 
-    # The notes are not show for Type[...] (because awaiting them will not work)
-    tx.x  # E: "Type[Sub[D]]" has no attribute "x"
-    a2: C = tx  # E: Incompatible types in assignment (expression has type "Type[Sub[D]]", variable has type "C")
+    # The notes are not show for type[...] (because awaiting them will not work)
+    tx.x  # E: "type[Sub[D]]" has no attribute "x"
+    a2: C = tx  # E: Incompatible types in assignment (expression has type "type[Sub[D]]", variable has type "C")
 
 class F:
     def __await__(self: T) -> Generator[Any, Any, T]: ...
diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test
index 3f2164bf5a24..07ed5fd77082 100644
--- a/test-data/unit/check-basic.test
+++ b/test-data/unit/check-basic.test
@@ -378,7 +378,7 @@ reveal_type(b)  # N: Revealed type is "Literal[False]"
 from typing import List
 x: List[int]
 y: List[float]
-y = x # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") \
+y = x # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[float]") \
      # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/list.pyi]
@@ -387,7 +387,7 @@ y = x # E: Incompatible types in assignment (expression has type "List[int]", va
 from typing import Dict
 x: Dict[str, int]
 y: Dict[str, float]
-y = x # E: Incompatible types in assignment (expression has type "Dict[str, int]", variable has type "Dict[str, float]") \
+y = x # E: Incompatible types in assignment (expression has type "dict[str, int]", variable has type "dict[str, float]") \
      # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Mapping" instead, which is covariant in the value type
 [builtins fixtures/dict.pyi]
@@ -420,7 +420,7 @@ def foo() -> Optional[A]:
 
 def bar() -> List[A]:
     l = [a.A()]
-    return l  # E: Incompatible return value type (got "List[a.A]", expected "List[b.A]")
+    return l  # E: Incompatible return value type (got "list[a.A]", expected "list[b.A]")
 
 def baz() -> Union[A, int]:
     b = True
@@ -431,37 +431,37 @@ def spam() -> Optional[A]:
 
 def eggs() -> Sequence[A]:
     x = [a.A()]
-    return x  # E: Incompatible return value type (got "List[a.A]", expected "Sequence[b.A]")
+    return x  # E: Incompatible return value type (got "list[a.A]", expected "Sequence[b.A]")
 
 def eggs2() -> Sequence[N]:
     x = [a.N(0)]
-    return x  # E: Incompatible return value type (got "List[a.N]", expected "Sequence[b.N]")
+    return x  # E: Incompatible return value type (got "list[a.N]", expected "Sequence[b.N]")
 
 def asdf1() -> Sequence[Tuple[a.A, A]]:
     x = [(a.A(), a.A())]
-    return x  # E: Incompatible return value type (got "List[Tuple[a.A, a.A]]", expected "Sequence[Tuple[a.A, b.A]]")
+    return x  # E: Incompatible return value type (got "list[tuple[a.A, a.A]]", expected "Sequence[tuple[a.A, b.A]]")
 
 def asdf2() -> Sequence[Tuple[A, a.A]]:
     x = [(a.A(), a.A())]
-    return x  # E: Incompatible return value type (got "List[Tuple[a.A, a.A]]", expected "Sequence[Tuple[b.A, a.A]]")
+    return x  # E: Incompatible return value type (got "list[tuple[a.A, a.A]]", expected "Sequence[tuple[b.A, a.A]]")
 
 def arg() -> Tuple[A, A]:
-    return A()  # E: Incompatible return value type (got "A", expected "Tuple[A, A]")
+    return A()  # E: Incompatible return value type (got "A", expected "tuple[A, A]")
 
 def types() -> Sequence[Type[A]]:
     x = [a.A]
-    return x  # E: Incompatible return value type (got "List[Type[a.A]]", expected "Sequence[Type[b.A]]")
+    return x  # E: Incompatible return value type (got "list[type[a.A]]", expected "Sequence[type[b.A]]")
 
 def literal() -> Sequence[Literal[B.b]]:
     x = [a.B.b]  # type: List[Literal[a.B.b]]
-    return x  # E: Incompatible return value type (got "List[Literal[a.B.b]]", expected "Sequence[Literal[b.B.b]]")
+    return x  # E: Incompatible return value type (got "list[Literal[a.B.b]]", expected "Sequence[Literal[b.B.b]]")
 
 def typeddict() -> Sequence[D]:
     x = [{'x': 0}]  # type: List[a.D]
-    return x  # E: Incompatible return value type (got "List[a.D]", expected "Sequence[b.D]")
+    return x  # E: Incompatible return value type (got "list[a.D]", expected "Sequence[b.D]")
 
 a = (a.A(), A())
-a.x  # E: "Tuple[a.A, b.A]" has no attribute "x"
+a.x  # E: "tuple[a.A, b.A]" has no attribute "x"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
 
diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test
index fd564c7e96cb..fe8a1551f81b 100644
--- a/test-data/unit/check-class-namedtuple.test
+++ b/test-data/unit/check-class-namedtuple.test
@@ -187,9 +187,9 @@ t: Tuple[int, str]
 if int():
     b = a  # E: Incompatible types in assignment (expression has type "A", variable has type "B")
 if int():
-    a = t  # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A")
+    a = t  # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "A")
 if int():
-    b = t  # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B")
+    b = t  # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "B")
 if int():
     t = a
 if int():
@@ -212,7 +212,7 @@ a = l[0]
 (i,) = l[0]
 i, i = l[0]  # E: Need more than 1 value to unpack (2 expected)
 l = [A(1)]
-a = (1,)  # E: Incompatible types in assignment (expression has type "Tuple[int]", \
+a = (1,)  # E: Incompatible types in assignment (expression has type "tuple[int]", \
                variable has type "A")
 [builtins fixtures/list.pyi]
 
@@ -223,7 +223,7 @@ class MyNamedTuple(NamedTuple):
     a: int
     b: str
 
-MyNamedTuple.x # E: "Type[MyNamedTuple]" has no attribute "x"
+MyNamedTuple.x # E: "type[MyNamedTuple]" has no attribute "x"
 [builtins fixtures/tuple.pyi]
 
 [case testNewNamedTupleEmptyItems]
@@ -281,7 +281,7 @@ class X(NamedTuple):
     y: str
 
 x: X
-reveal_type(x._replace())  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]"
+reveal_type(x._replace())  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]"
 x._replace(x=5)
 x._replace(y=5)  # E: Argument "y" to "_replace" of "X" has incompatible type "int"; expected "str"
 [builtins fixtures/tuple.pyi]
@@ -293,7 +293,7 @@ class X(NamedTuple):
     x: int
     y: str
 
-reveal_type(X._fields)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(X._fields)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 reveal_type(X._field_types)  # N: Revealed type is "builtins.dict[builtins.str, Any]"
 reveal_type(X._field_defaults)  # N: Revealed type is "builtins.dict[builtins.str, Any]"
 
@@ -324,7 +324,7 @@ class Y(NamedTuple):
     x: int
     y: str
 
-reveal_type([X(3, 'b'), Y(1, 'a')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
+reveal_type([X(3, 'b'), Y(1, 'a')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
 
 [builtins fixtures/list.pyi]
 
@@ -335,8 +335,8 @@ class X(NamedTuple):
     x: int
     y: str
 
-reveal_type([(3, 'b'), X(1, 'a')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
-reveal_type([X(1, 'a'), (3, 'b')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
+reveal_type([(3, 'b'), X(1, 'a')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
+reveal_type([X(1, 'a'), (3, 'b')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
 
 [builtins fixtures/list.pyi]
 
@@ -386,8 +386,8 @@ class X(NamedTuple):
     x: int
     y: int = 2
 
-reveal_type(X(1))  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.X]"
-reveal_type(X(1, 2))  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.X]"
+reveal_type(X(1))  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.X]"
+reveal_type(X(1, 2))  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.X]"
 
 X(1, 'a')  # E: Argument 2 to "X" has incompatible type "str"; expected "int"
 X(1, z=3)  # E: Unexpected keyword argument "z" for "X"
@@ -396,14 +396,14 @@ class HasNone(NamedTuple):
     x: int
     y: Optional[int] = None
 
-reveal_type(HasNone(1))  # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]"
+reveal_type(HasNone(1))  # N: Revealed type is "tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]"
 
 class Parameterized(NamedTuple):
     x: int
     y: List[int] = [1] + [2]
     z: List[int] = []
 
-reveal_type(Parameterized(1))  # N: Revealed type is "Tuple[builtins.int, builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.Parameterized]"
+reveal_type(Parameterized(1))  # N: Revealed type is "tuple[builtins.int, builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.Parameterized]"
 Parameterized(1, ['not an int'])  # E: List item 0 has incompatible type "str"; expected "int"
 
 class Default:
@@ -412,8 +412,8 @@ class Default:
 class UserDefined(NamedTuple):
     x: Default = Default()
 
-reveal_type(UserDefined())  # N: Revealed type is "Tuple[__main__.Default, fallback=__main__.UserDefined]"
-reveal_type(UserDefined(Default()))  # N: Revealed type is "Tuple[__main__.Default, fallback=__main__.UserDefined]"
+reveal_type(UserDefined())  # N: Revealed type is "tuple[__main__.Default, fallback=__main__.UserDefined]"
+reveal_type(UserDefined(Default()))  # N: Revealed type is "tuple[__main__.Default, fallback=__main__.UserDefined]"
 UserDefined(1)  # E: Argument 1 to "UserDefined" has incompatible type "int"; expected "Default"
 
 [builtins fixtures/list.pyi]
@@ -425,7 +425,7 @@ class HasNone(NamedTuple):
     x: int
     y: Optional[int] = None
 
-reveal_type(HasNone(1))  # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]"
+reveal_type(HasNone(1))  # N: Revealed type is "tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]"
 HasNone(None)  # E: Argument 1 to "HasNone" has incompatible type "None"; expected "int"
 HasNone(1, y=None)
 HasNone(1, y=2)
@@ -463,7 +463,7 @@ class Y(X):
         self.y
         return self.x
 
-reveal_type(Y('a'))  # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.Y]"
+reveal_type(Y('a'))  # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.Y]"
 Y(y=1, x='1').method()
 
 class CallsBaseInit(X):
@@ -511,7 +511,7 @@ class Overloader(NamedTuple):
 
 reveal_type(Overloader(1).method('string'))  # N: Revealed type is "builtins.str"
 reveal_type(Overloader(1).method(1))  # N: Revealed type is "builtins.int"
-Overloader(1).method(('tuple',))  # E: No overload variant of "method" of "Overloader" matches argument type "Tuple[str]" \
+Overloader(1).method(('tuple',))  # E: No overload variant of "method" of "Overloader" matches argument type "tuple[str]" \
                                   # N: Possible overload variants: \
                                   # N:     def method(self, y: str) -> str \
                                   # N:     def method(self, y: int) -> int
@@ -528,7 +528,7 @@ class Base(NamedTuple):
         reveal_type(self)  # N: Revealed type is "T`-1"
         return self
     def good_override(self) -> int:
-        reveal_type(self)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Base]"
+        reveal_type(self)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.Base]"
         reveal_type(self[0])  # N: Revealed type is "builtins.int"
         self[0] = 3  # E: Unsupported target for indexed assignment ("Base")
         reveal_type(self.x)  # N: Revealed type is "builtins.int"
@@ -538,14 +538,14 @@ class Base(NamedTuple):
                               # E: No overload variant of "__getitem__" of "tuple" matches argument type "TypeVar" \
                               # N: Possible overload variants: \
                               # N:     def __getitem__(self, int, /) -> int \
-                              # N:     def __getitem__(self, slice, /) -> Tuple[int, ...]
+                              # N:     def __getitem__(self, slice, /) -> tuple[int, ...]
         return self.x
     def bad_override(self) -> int:
         return self.x
 
 class Child(Base):
     def new_method(self) -> int:
-        reveal_type(self)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Child]"
+        reveal_type(self)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.Child]"
         reveal_type(self[0])  # N: Revealed type is "builtins.int"
         self[0] = 3  # E: Unsupported target for indexed assignment ("Child")
         reveal_type(self.x)  # N: Revealed type is "builtins.int"
@@ -560,8 +560,8 @@ class Child(Base):
 def takes_base(base: Base) -> int:
     return base.x
 
-reveal_type(Base(1).copy())  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Base]"
-reveal_type(Child(1).copy())  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Child]"
+reveal_type(Base(1).copy())  # N: Revealed type is "tuple[builtins.int, fallback=__main__.Base]"
+reveal_type(Child(1).copy())  # N: Revealed type is "tuple[builtins.int, fallback=__main__.Child]"
 reveal_type(Base(1).good_override())  # N: Revealed type is "builtins.int"
 reveal_type(Child(1).good_override())  # N: Revealed type is "builtins.int"
 reveal_type(Base(1).bad_override())  # N: Revealed type is "builtins.int"
@@ -635,8 +635,8 @@ class HasClassMethod(NamedTuple):
 
     @classmethod
     def new(cls, f: str) -> 'HasClassMethod':
-        reveal_type(cls)  # N: Revealed type is "Type[Tuple[builtins.str, fallback=__main__.HasClassMethod]]"
-        reveal_type(HasClassMethod)  # N: Revealed type is "def (x: builtins.str) -> Tuple[builtins.str, fallback=__main__.HasClassMethod]"
+        reveal_type(cls)  # N: Revealed type is "type[tuple[builtins.str, fallback=__main__.HasClassMethod]]"
+        reveal_type(HasClassMethod)  # N: Revealed type is "def (x: builtins.str) -> tuple[builtins.str, fallback=__main__.HasClassMethod]"
         return cls(x=f)
 
 [builtins fixtures/classmethod.pyi]
@@ -661,7 +661,7 @@ class HasStaticMethod(NamedTuple):
 
     @property
     def size(self) -> int:
-        reveal_type(self)  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.HasStaticMethod]"
+        reveal_type(self)  # N: Revealed type is "tuple[builtins.str, fallback=__main__.HasStaticMethod]"
         return 4
 
 [builtins fixtures/property.pyi]
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index e0ea00aee361..93b575e25309 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -151,19 +151,19 @@ class Derived(Base):
 # This was crashing: https://github.com/python/mypy/issues/11686.
 class Base:
     def __init__(self, arg: int):
-        self.partial_type = []  # E: Need type annotation for "partial_type" (hint: "partial_type: List[] = ...")
+        self.partial_type = []  # E: Need type annotation for "partial_type" (hint: "partial_type: list[] = ...")
         self.force_deferral = []
 
     # Force inference of the `force_deferral` attribute in `__init__` to be
     # deferred to a later pass by providing a definition in another context,
     # which means `partial_type` remains only partially inferred.
-    force_deferral = []  # E: Need type annotation for "force_deferral" (hint: "force_deferral: List[] = ...")
+    force_deferral = []  # E: Need type annotation for "force_deferral" (hint: "force_deferral: list[] = ...")
 
 
 class Derived(Base):
     def partial_type(self) -> int:  # E: Signature of "partial_type" incompatible with supertype "Base" \
                                     # N:      Superclass: \
-                                    # N:          List[Any] \
+                                    # N:          list[Any] \
                                     # N:      Subclass: \
                                     # N:          def partial_type(self) -> int
         ...
@@ -1162,7 +1162,7 @@ b = A.x # type: B # E: Incompatible types in assignment (expression has type "A"
 [case testAccessingUndefinedAttributeViaClass]
 import typing
 class A: pass
-A.x # E: "Type[A]" has no attribute "x"
+A.x # E: "type[A]" has no attribute "x"
 
 [case testAccessingUndefinedAttributeViaClassWithOverloadedInit]
 from foo import *
@@ -1173,7 +1173,7 @@ class A:
     def __init__(self): pass
     @overload
     def __init__(self, x): pass
-A.x # E: "Type[A]" has no attribute "x"
+A.x # E: "type[A]" has no attribute "x"
 
 [case testAccessMethodOfClassWithOverloadedInit]
 from foo import *
@@ -1227,7 +1227,7 @@ import typing
 class A:
     class B: pass
 A.B = None  # E: Cannot assign to a type \
-            # E: Incompatible types in assignment (expression has type "None", variable has type "Type[B]")
+            # E: Incompatible types in assignment (expression has type "None", variable has type "type[B]")
 [targets __main__]
 
 [case testAccessingClassAttributeWithTypeInferenceIssue]
@@ -1243,7 +1243,7 @@ class C:
 x = C.x
 [builtins fixtures/list.pyi]
 [out]
-main:2: error: Need type annotation for "x" (hint: "x: List[] = ...")
+main:2: error: Need type annotation for "x" (hint: "x: list[] = ...")
 
 [case testAccessingGenericClassAttribute]
 from typing import Generic, TypeVar
@@ -1510,7 +1510,7 @@ class C:
     cls(1)      # E: Too many arguments for "C"
     cls.bar()
     cls.bar(1)  # E: Too many arguments for "bar" of "C"
-    cls.bozo()  # E: "Type[C]" has no attribute "bozo"
+    cls.bozo()  # E: "type[C]" has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -1521,7 +1521,7 @@ class C:
   def foo(cls) -> None: pass
 C.foo()
 C.foo(1)  # E: Too many arguments for "foo" of "C"
-C.bozo()  # E: "Type[C]" has no attribute "bozo"
+C.bozo()  # E: "type[C]" has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 
 [case testClassMethodCalledOnInstance]
@@ -1531,7 +1531,7 @@ class C:
   def foo(cls) -> None: pass
 C().foo()
 C().foo(1)  # E: Too many arguments for "foo" of "C"
-C.bozo()    # E: "Type[C]" has no attribute "bozo"
+C.bozo()    # E: "type[C]" has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 
 [case testClassMethodMayCallAbstractMethod]
@@ -1791,12 +1791,12 @@ class D:
     def __get__(self, inst: Base, own: Type[Base]) -> str: pass
 [builtins fixtures/bool.pyi]
 [out]
-main:4: error: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "Type[Base]"
+main:4: error: Argument 2 to "__get__" of "D" has incompatible type "type[A]"; expected "type[Base]"
 main:4: note: Revealed type is "d.D"
-main:5: error: No overload variant of "__get__" of "D" matches argument types "A", "Type[A]"
+main:5: error: No overload variant of "__get__" of "D" matches argument types "A", "type[A]"
 main:5: note: Possible overload variants:
-main:5: note:     def __get__(self, inst: None, own: Type[Base]) -> D
-main:5: note:     def __get__(self, inst: Base, own: Type[Base]) -> str
+main:5: note:     def __get__(self, inst: None, own: type[Base]) -> D
+main:5: note:     def __get__(self, inst: Base, own: type[Base]) -> str
 main:5: note: Revealed type is "Any"
 
 [case testAccessingGenericNonDataDescriptor]
@@ -1890,10 +1890,10 @@ class D(Generic[T, V]):
     def __get__(self, inst: T, own: Type[T]) -> V: pass
 [builtins fixtures/bool.pyi]
 [out]
-main:4: error: No overload variant of "__get__" of "D" matches argument types "None", "Type[A]"
+main:4: error: No overload variant of "__get__" of "D" matches argument types "None", "type[A]"
 main:4: note: Possible overload variants:
 main:4: note:     def __get__(self, inst: None, own: None) -> D[A, int]
-main:4: note:     def __get__(self, inst: A, own: Type[A]) -> int
+main:4: note:     def __get__(self, inst: A, own: type[A]) -> int
 main:4: note: Revealed type is "Any"
 
 [case testAccessingNonDataDescriptorSubclass]
@@ -2052,7 +2052,7 @@ class D:
     def __get__(self, inst: Any, own: str) -> Any: pass
 class A:
     f = D()
-A().f  # E: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "str"
+A().f  # E: Argument 2 to "__get__" of "D" has incompatible type "type[A]"; expected "str"
 
 [case testDescriptorGetSetDifferentTypes]
 from typing import Any
@@ -2616,7 +2616,7 @@ from typing import TypeVar, Type
 class Real(type):
     def __add__(self, other: FractionChild) -> str: ...
 class Fraction(Real):
-    def __radd__(self, other: Type['A']) -> Real: ...  # E: Signatures of "__radd__" of "Fraction" and "__add__" of "Type[A]" are unsafely overlapping
+    def __radd__(self, other: Type['A']) -> Real: ...  # E: Signatures of "__radd__" of "Fraction" and "__add__" of "type[A]" are unsafely overlapping
 class FractionChild(Fraction): pass
 
 class A(metaclass=Real): pass
@@ -3243,7 +3243,7 @@ class C:
 def f(x: type) -> None: pass
 def g(x: int) -> None: pass
 f(C)
-g(C) # E: Argument 1 to "g" has incompatible type "Type[C]"; expected "int"
+g(C) # E: Argument 1 to "g" has incompatible type "type[C]"; expected "int"
 [builtins fixtures/__new__.pyi]
 
 [case testClassWith__new__AndCompatibilityWithType2]
@@ -3254,7 +3254,7 @@ class C:
 def f(x: type) -> None: pass
 def g(x: int) -> None: pass
 f(C)
-g(C) # E: Argument 1 to "g" has incompatible type "Type[C]"; expected "int"
+g(C) # E: Argument 1 to "g" has incompatible type "type[C]"; expected "int"
 [builtins fixtures/__new__.pyi]
 
 [case testGenericClassWith__new__]
@@ -3339,7 +3339,7 @@ class B:
 [case testClassVsInstanceDisambiguation]
 class A: pass
 def f(x: A) -> None: pass
-f(A) # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "A"
+f(A) # E: Argument 1 to "f" has incompatible type "type[A]"; expected "A"
 [out]
 
 -- TODO
@@ -3393,7 +3393,7 @@ class A(Generic[T]):
 class B(Generic[T]):
     a: Type[A[T]] = A
 
-reveal_type(B[int]().a) # N: Revealed type is "Type[__main__.A[builtins.int]]"
+reveal_type(B[int]().a) # N: Revealed type is "type[__main__.A[builtins.int]]"
 B[int]().a('hi') # E: Argument 1 to "A" has incompatible type "str"; expected "int"
 
 class C(Generic[T]):
@@ -3548,7 +3548,7 @@ class User: pass
 def new_user(user_class: Type[User]):
     return user_class()
 def foo(arg: Type[int]):
-    new_user(arg)  # E: Argument 1 to "new_user" has incompatible type "Type[int]"; expected "Type[User]"
+    new_user(arg)  # E: Argument 1 to "new_user" has incompatible type "type[int]"; expected "type[User]"
 [out]
 
 [case testTypeUsingTypeCUnionOverload]
@@ -3587,8 +3587,8 @@ def foo(arg: Type[Any]):
     arg.new_member_name = 42
     # Member access is ok and types as Any
     reveal_type(x)  # N: Revealed type is "Any"
-    # But Type[Any] is distinct from Any
-    y: int = arg  # E: Incompatible types in assignment (expression has type "Type[Any]", variable has type "int")
+    # But type[Any] is distinct from Any
+    y: int = arg  # E: Incompatible types in assignment (expression has type "type[Any]", variable has type "int")
 [out]
 
 [case testTypeUsingTypeCTypeAnyMemberFallback]
@@ -3629,7 +3629,7 @@ def process(cls: Type[User]):
     obj = cls()
     reveal_type(cls.bar(obj))  # N: Revealed type is "builtins.int"
     cls.mro()  # Defined in class type
-    cls.error  # E: "Type[User]" has no attribute "error"
+    cls.error  # E: "type[User]" has no attribute "error"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -3646,7 +3646,7 @@ def process(cls: Type[Union[BasicUser, ProUser]]):
     obj = cls()
     cls.bar(obj)
     cls.mro()  # Defined in class type
-    cls.error  # E: Item "type" of "Union[Type[BasicUser], Type[ProUser]]" has no attribute "error"
+    cls.error  # E: Item "type" of "Union[type[BasicUser], type[ProUser]]" has no attribute "error"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -3662,7 +3662,7 @@ def process(cls: Type[U]):
     obj = cls()
     reveal_type(cls.bar(obj))  # N: Revealed type is "builtins.int"
     cls.mro()  # Defined in class type
-    cls.error  # E: "Type[U]" has no attribute "error"
+    cls.error  # E: "type[U]" has no attribute "error"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -3681,14 +3681,14 @@ def process(cls: Type[U]):
     obj = cls()
     cls.bar(obj)
     cls.mro()  # Defined in class type
-    cls.error  # E: "Type[U]" has no attribute "error"
+    cls.error  # E: "type[U]" has no attribute "error"
 [builtins fixtures/classmethod.pyi]
 [out]
 
 [case testTypeUsingTypeCErrorUnsupportedType]
 from typing import Type, Tuple
 def foo(arg: Type[Tuple[int]]):
-    arg()  # E: Cannot instantiate type "Type[Tuple[int]]"
+    arg()  # E: Cannot instantiate type "Type[tuple[int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeUsingTypeCOverloadedClass]
@@ -3732,7 +3732,7 @@ def f(a: T): pass
 [case testTypeUsingTypeCTuple]
 from typing import Type, Tuple
 def f(a: Type[Tuple[int, int]]):
-    a()  # E: Cannot instantiate type "Type[Tuple[int, int]]"
+    a()  # E: Cannot instantiate type "Type[tuple[int, int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeUsingTypeCNamedTuple]
@@ -3755,7 +3755,7 @@ def foo(c: Type[C], d: Type[D]) -> None:
 
 [builtins fixtures/list.pyi]
 [out]
-main:7: note: Revealed type is "builtins.list[Type[__main__.B]]"
+main:7: note: Revealed type is "builtins.list[type[__main__.B]]"
 
 [case testTypeEquivalentTypeAny]
 from typing import Type, Any
@@ -3892,9 +3892,9 @@ def f(a: int) -> Type[User]:
 def f(a: str) -> User:
     return User()
 
-reveal_type(f(User()))  # N: Revealed type is "Type[foo.User]"
+reveal_type(f(User()))  # N: Revealed type is "type[foo.User]"
 reveal_type(f(User))  # N: Revealed type is "foo.User"
-reveal_type(f(3))  # N: Revealed type is "Type[foo.User]"
+reveal_type(f(3))  # N: Revealed type is "type[foo.User]"
 reveal_type(f("hi"))  # N: Revealed type is "foo.User"
 [builtins fixtures/classmethod.pyi]
 [out]
@@ -3934,7 +3934,7 @@ def f(a: type) -> None: pass
 
 f(3)  # E: No overload variant of "f" matches argument type "int" \
       # N: Possible overload variants: \
-      # N:     def f(a: Type[User]) -> None \
+      # N:     def f(a: type[User]) -> None \
       # N:     def f(a: type) -> None
 [builtins fixtures/classmethod.pyi]
 [out]
@@ -3954,7 +3954,7 @@ def f(a: int) -> None: pass
 f(User)
 f(User())  # E: No overload variant of "f" matches argument type "User" \
            # N: Possible overload variants: \
-           # N:     def f(a: Type[User]) -> None \
+           # N:     def f(a: type[User]) -> None \
            # N:     def f(a: int) -> None
 [builtins fixtures/classmethod.pyi]
 [out]
@@ -3976,10 +3976,10 @@ def f(a: Type[B]) -> None: pass
 @overload
 def f(a: int) -> None: pass
 
-f(A)  # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "Type[B]"
+f(A)  # E: Argument 1 to "f" has incompatible type "type[A]"; expected "type[B]"
 f(B)
 f(C)
-f(AType)  # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "Type[B]"
+f(AType)  # E: Argument 1 to "f" has incompatible type "type[A]"; expected "type[B]"
 f(BType)
 f(CType)
 [builtins fixtures/classmethod.pyi]
@@ -4208,7 +4208,7 @@ class User:
 
 u = User()
 
-reveal_type(type(u))  # N: Revealed type is "Type[__main__.User]"
+reveal_type(type(u))  # N: Revealed type is "type[__main__.User]"
 reveal_type(type(u).test_class_method())  # N: Revealed type is "builtins.int"
 reveal_type(type(u).test_static_method())  # N: Revealed type is "builtins.str"
 type(u).test_instance_method()  # E: Missing positional argument "self" in call to "test_instance_method" of "User"
@@ -4227,8 +4227,8 @@ def f2(func: A) -> A:
 
 u = User()
 
-reveal_type(f1(u))  # N: Revealed type is "Type[__main__.User]"
-reveal_type(f2(type)(u))  # N: Revealed type is "Type[__main__.User]"
+reveal_type(f1(u))  # N: Revealed type is "type[__main__.User]"
+reveal_type(f2(type)(u))  # N: Revealed type is "type[__main__.User]"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -4240,7 +4240,7 @@ def fake1(a: object) -> type:
 def fake2(a: int) -> type:
     return User
 
-reveal_type(type(User()))  # N: Revealed type is "Type[__main__.User]"
+reveal_type(type(User()))  # N: Revealed type is "type[__main__.User]"
 reveal_type(fake1(User()))  # N: Revealed type is "builtins.type"
 reveal_type(fake2(3))  # N: Revealed type is "builtins.type"
 [builtins fixtures/classmethod.pyi]
@@ -4292,7 +4292,7 @@ int.__eq__(3, 4)
 [builtins fixtures/args.pyi]
 [out]
 main:33: error: Too few arguments for "__eq__" of "int"
-main:33: error: Unsupported operand types for == ("int" and "Type[int]")
+main:33: error: Unsupported operand types for == ("int" and "type[int]")
 
 [case testDupBaseClasses]
 class A:
@@ -4694,7 +4694,7 @@ class M:
 
 class A(metaclass=M): pass  # E: Metaclasses not inheriting from "type" are not supported
 
-A.x  # E: "Type[A]" has no attribute "x"
+A.x  # E: "type[A]" has no attribute "x"
 
 [case testMetaclassTypeReveal]
 from typing import Type
@@ -4704,7 +4704,7 @@ class M(type):
 class A(metaclass=M): pass
 
 def f(TA: Type[A]):
-    reveal_type(TA)  # N: Revealed type is "Type[__main__.A]"
+    reveal_type(TA)  # N: Revealed type is "type[__main__.A]"
     reveal_type(TA.x)  # N: Revealed type is "builtins.int"
 
 [case testMetaclassConflictingInstanceVars]
@@ -4757,7 +4757,7 @@ class A(metaclass=M): pass
 class B(A): pass
 
 def f(TB: Type[B]):
-    reveal_type(TB)  # N: Revealed type is "Type[__main__.B]"
+    reveal_type(TB)  # N: Revealed type is "type[__main__.B]"
     reveal_type(TB.x)  # N: Revealed type is "builtins.int"
 
 [case testMetaclassAsAny]
@@ -4898,7 +4898,7 @@ class Concrete(metaclass=Meta):
     pass
 
 reveal_type(Concrete + X())  # N: Revealed type is "builtins.str"
-Concrete + "hello"  # E: Unsupported operand types for + ("Type[Concrete]" and "str")
+Concrete + "hello"  # E: Unsupported operand types for + ("type[Concrete]" and "str")
 
 [case testMetaclassOperatorTypeVar]
 from typing import Type, TypeVar
@@ -5008,7 +5008,7 @@ class A(metaclass=M): # E: Invalid metaclass "M"
 class B(metaclass=MM): # E: Invalid metaclass "MM"
     y = 0
 reveal_type(A.y) # N: Revealed type is "builtins.int"
-A.x # E: "Type[A]" has no attribute "x"
+A.x # E: "type[A]" has no attribute "x"
 
 [case testAnyAsBaseOfMetaclass]
 from typing import Any, Type
@@ -5023,7 +5023,7 @@ class A(metaclass=MM):
 
 def h(a: Type[A], b: Type[object]) -> None:
     h(a, a)
-    h(b, a) # E: Argument 1 to "h" has incompatible type "Type[object]"; expected "Type[A]"
+    h(b, a) # E: Argument 1 to "h" has incompatible type "type[object]"; expected "type[A]"
     a.f(1) # E: Too many arguments for "f" of "A"
     reveal_type(a.y) # N: Revealed type is "builtins.int"
 
@@ -5048,9 +5048,9 @@ TTA = TypeVar('TTA', bound='Type[A]')
 TM = TypeVar('TM', bound='M')
 
 class M(type):
-    def g1(cls: 'Type[A]') -> A: pass #  E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M"
-    def g2(cls: Type[TA]) -> TA: pass #  E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M"
-    def g3(cls: TTA) -> TTA: pass #  E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M"
+    def g1(cls: 'Type[A]') -> A: pass  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M"
+    def g2(cls: Type[TA]) -> TA: pass  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M"
+    def g3(cls: TTA) -> TTA: pass  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M"
     def g4(cls: TM) -> TM: pass
 m: M
 
@@ -5065,23 +5065,23 @@ reveal_type(A.g4)  # N: Revealed type is "def () -> def () -> __main__.A"
 class B(metaclass=M):
     def foo(self): pass
 
-B.g1  # E: Invalid self argument "Type[B]" to attribute function "g1" with type "Callable[[Type[A]], A]"
-B.g2  # E: Invalid self argument "Type[B]" to attribute function "g2" with type "Callable[[Type[TA]], TA]"
-B.g3  # E: Invalid self argument "Type[B]" to attribute function "g3" with type "Callable[[TTA], TTA]"
+B.g1  # E: Invalid self argument "type[B]" to attribute function "g1" with type "Callable[[type[A]], A]"
+B.g2  # E: Invalid self argument "type[B]" to attribute function "g2" with type "Callable[[type[TA]], TA]"
+B.g3  # E: Invalid self argument "type[B]" to attribute function "g3" with type "Callable[[TTA], TTA]"
 reveal_type(B.g4)  # N: Revealed type is "def () -> def () -> __main__.B"
 
 # 4 examples of unsoundness - instantiation, classmethod, staticmethod and ClassVar:
 
-ta: Type[A] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "Type[A]")
+ta: Type[A] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "type[A]")
 a: A = ta()
 reveal_type(ta.g1)  # N: Revealed type is "def () -> __main__.A"
 reveal_type(ta.g2)  # N: Revealed type is "def () -> __main__.A"
-reveal_type(ta.g3)  # N: Revealed type is "def () -> Type[__main__.A]"
-reveal_type(ta.g4)  # N: Revealed type is "def () -> Type[__main__.A]"
+reveal_type(ta.g3)  # N: Revealed type is "def () -> type[__main__.A]"
+reveal_type(ta.g4)  # N: Revealed type is "def () -> type[__main__.A]"
 
 x: M = ta
-x.g1  # E: Invalid self argument "M" to attribute function "g1" with type "Callable[[Type[A]], A]"
-x.g2  # E: Invalid self argument "M" to attribute function "g2" with type "Callable[[Type[TA]], TA]"
+x.g1  # E: Invalid self argument "M" to attribute function "g1" with type "Callable[[type[A]], A]"
+x.g2  # E: Invalid self argument "M" to attribute function "g2" with type "Callable[[type[TA]], TA]"
 x.g3  # E: Invalid self argument "M" to attribute function "g3" with type "Callable[[TTA], TTA]"
 reveal_type(x.g4)  # N: Revealed type is "def () -> __main__.M"
 
@@ -5094,7 +5094,7 @@ class Class(metaclass=M):
     def f1(cls: Type[Class]) -> None: pass
     @classmethod
     def f2(cls: M) -> None: pass
-cl: Type[Class] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Class]")
+cl: Type[Class] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "type[Class]")
 reveal_type(cl.f1)  # N: Revealed type is "def ()"
 reveal_type(cl.f2)  # N: Revealed type is "def ()"
 x1: M = cl
@@ -5102,14 +5102,14 @@ x1: M = cl
 class Static(metaclass=M):
     @staticmethod
     def f() -> None: pass
-s: Type[Static] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Static]")
+s: Type[Static] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "type[Static]")
 reveal_type(s.f)  # N: Revealed type is "def ()"
 x2: M = s
 
 from typing import ClassVar
 class Cvar(metaclass=M):
     x = 1  # type: ClassVar[int]
-cv: Type[Cvar] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Cvar]")
+cv: Type[Cvar] = m  # E: Incompatible types in assignment (expression has type "M", variable has type "type[Cvar]")
 cv.x
 x3: M = cv
 
@@ -5178,7 +5178,7 @@ def test() -> None:
     N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition) \
                                     # N: Recursive types are not allowed at function scope
     n: N
-    reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N@4]"
+    reveal_type(n) # N: Revealed type is "tuple[Any, fallback=__main__.N@4]"
 [builtins fixtures/tuple.pyi]
 
 [case testCrashOnSelfRecursiveTypedDictVar]
@@ -5231,7 +5231,7 @@ class NameInfo(NamedTuple):
 def parse_ast(name_dict: NameDict) -> None:
     if isinstance(name_dict[''], int):
         pass
-    reveal_type(name_dict['test']) # N: Revealed type is "Tuple[builtins.bool, fallback=__main__.NameInfo]"
+    reveal_type(name_dict['test']) # N: Revealed type is "tuple[builtins.bool, fallback=__main__.NameInfo]"
 [builtins fixtures/isinstancelist.pyi]
 [typing fixtures/typing-medium.pyi]
 
@@ -5387,7 +5387,7 @@ class Bar(NamedTuple):
 
 def foo(node: Node) -> int:
     x = node
-    reveal_type(node) # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.Foo], Tuple[builtins.int, fallback=__main__.Bar]]"
+    reveal_type(node) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.Foo], tuple[builtins.int, fallback=__main__.Bar]]"
     return x.x
 [builtins fixtures/tuple.pyi]
 [out]
@@ -5465,9 +5465,9 @@ ForwardUnion = Union['TP', int]
 class TP(NamedTuple('TP', [('x', int)])): pass
 
 def f(x: ForwardUnion) -> None:
-  reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.TP], builtins.int]"
+  reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.TP], builtins.int]"
   if isinstance(x, TP):
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.TP]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.TP]"
 [builtins fixtures/isinstance.pyi]
 [out]
 
@@ -5498,8 +5498,8 @@ y: NM
 y1 = NM(x=[])
 reveal_type(x) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})"
 reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})"
-reveal_type(y) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]"
-reveal_type(y1) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]"
+reveal_type(y) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]"
+reveal_type(y1) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 [out]
@@ -5667,7 +5667,7 @@ class C1(six.with_metaclass(M), object): pass  # E: Unsupported dynamic base cla
 class C2(C1, six.with_metaclass(M)): pass  # E: Unsupported dynamic base class "six.with_metaclass"
 class C3(six.with_metaclass(A)): pass  # E: Metaclasses not inheriting from "type" are not supported
 @six.add_metaclass(A)  # E: Metaclasses not inheriting from "type" are not supported  \
-    # E: Argument 1 to "add_metaclass" has incompatible type "Type[A]"; expected "Type[type]"
+    # E: Argument 1 to "add_metaclass" has incompatible type "type[A]"; expected "type[type]"
 
 class D3(A): pass
 class C4(six.with_metaclass(M), metaclass=M): pass  # E: Multiple metaclass definitions
@@ -5886,7 +5886,7 @@ T = TypeVar('T')
 class C(Any):
     def bar(self: T) -> Type[T]: pass
     def foo(self) -> None:
-        reveal_type(self.bar()) # N: Revealed type is "Type[__main__.C]"
+        reveal_type(self.bar()) # N: Revealed type is "type[__main__.C]"
         reveal_type(self.bar().__name__) # N: Revealed type is "builtins.str"
 [builtins fixtures/type.pyi]
 [out]
@@ -5904,7 +5904,7 @@ def decorate_forward_ref() -> Callable[[Type[A]], Type[A]]:
 @decorate(11)
 class A: pass
 
-@decorate  # E: Argument 1 to "decorate" has incompatible type "Type[A2]"; expected "int"
+@decorate  # E: Argument 1 to "decorate" has incompatible type "type[A2]"; expected "int"
 class A2: pass
 
 [case testClassDecoratorIncorrect]
@@ -6076,7 +6076,7 @@ d: D
 reveal_type(d.normal)  # N: Revealed type is "builtins.int"
 reveal_type(d.dynamic)  # N: Revealed type is "__main__.Descr"
 reveal_type(D.other)  # N: Revealed type is "builtins.int"
-D.dynamic  # E: "Type[D]" has no attribute "dynamic"
+D.dynamic  # E: "type[D]" has no attribute "dynamic"
 [out]
 
 [case testSelfDescriptorAssign]
@@ -6463,7 +6463,7 @@ class Sub(a.Base):
                               # N:      Superclass: \
                               # N:          int \
                               # N:      Subclass: \
-                              # N:          def x(*Any, **Any) -> Tuple[int, int]
+                              # N:          def x(*Any, **Any) -> tuple[int, int]
 
 [file a.py]
 import b
@@ -6489,7 +6489,7 @@ class Sub(a.Base):
                               # N:      Superclass: \
                               # N:          int \
                               # N:      Subclass: \
-                              # N:          def x(*Any, **Any) -> Tuple[int, int]
+                              # N:          def x(*Any, **Any) -> tuple[int, int]
 
 [file a.py]
 import b
@@ -6570,7 +6570,7 @@ class A(b.B):
     @c.deco
     def meth(self) -> int:
         y = super().meth()
-        reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+        reveal_type(y)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
         return 0
 [file b.py]
 from a import A
@@ -6629,7 +6629,7 @@ class A(b.B):
     @c.deco
     def meth(self) -> int:
         y = super().meth()
-        reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+        reveal_type(y)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
         reveal_type(other.x)  # N: Revealed type is "builtins.int"
         return 0
 
@@ -6878,7 +6878,7 @@ class C: ...
 x: Union[C, Type[C]]
 
 if isinstance(x, type) and issubclass(x, C):
-    reveal_type(x)  # N: Revealed type is "Type[__main__.C]"
+    reveal_type(x)  # N: Revealed type is "type[__main__.C]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIsInstanceTypeByAssert]
@@ -6902,11 +6902,11 @@ class C(Generic[T]):
     def meth(self, cls: Type[T]) -> None:
         if not issubclass(cls, Sub):
             return
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Sub]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Sub]"
     def other(self, cls: Type[T]) -> None:
         if not issubclass(cls, Sub):
             return
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Sub]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Sub]"
 
 [builtins fixtures/isinstancelist.pyi]
 
@@ -6954,9 +6954,9 @@ class C(B):
     def __init__(self, a: int) -> None:
         self.c = a
 a = A(1) # E: Cannot instantiate abstract class "A" with abstract attribute "__init__"
-A.c # E: "Type[A]" has no attribute "c"
+A.c # E: "type[A]" has no attribute "c"
 b = B(2) # E: Cannot instantiate abstract class "B" with abstract attribute "__init__"
-B.c # E: "Type[B]" has no attribute "c"
+B.c # E: "type[B]" has no attribute "c"
 c = C(3)
 c.c
 C.c
@@ -7159,7 +7159,7 @@ class A:
 N = NamedTuple('N', [('x', int)])
 class B(A, N): pass
 
-reveal_type(A())  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.B]"
+reveal_type(A())  # N: Revealed type is "tuple[builtins.int, fallback=__main__.B]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewReturnType8]
@@ -7333,7 +7333,7 @@ class B(Generic[T]):
 
 class C(B[T]):
     def __init__(self) -> None:
-        self.x: List[T]  # E: Incompatible types in assignment (expression has type "List[T]", base class "B" defined the type as "T")
+        self.x: List[T]  # E: Incompatible types in assignment (expression has type "list[T]", base class "B" defined the type as "T")
 [builtins fixtures/list.pyi]
 
 [case testGenericOverrideGenericChained]
@@ -7350,7 +7350,7 @@ class B(A[Tuple[T, S]]): ...
 class C(B[int, T]):
     def __init__(self) -> None:
         # TODO: error message could be better.
-        self.x: Tuple[str, T]  # E: Incompatible types in assignment (expression has type "Tuple[str, T]", base class "A" defined the type as "Tuple[int, T]")
+        self.x: Tuple[str, T]  # E: Incompatible types in assignment (expression has type "tuple[str, T]", base class "A" defined the type as "tuple[int, T]")
 [builtins fixtures/tuple.pyi]
 
 [case testInitSubclassWrongType]
@@ -7489,7 +7489,7 @@ class C:
     def meth(cls): ...
 
 reveal_type(C.meth)  # N: Revealed type is "def () -> Any"
-reveal_type(C.__new__)  # N: Revealed type is "def (cls: Type[__main__.C]) -> Any"
+reveal_type(C.__new__)  # N: Revealed type is "def (cls: type[__main__.C]) -> Any"
 [builtins fixtures/classmethod.pyi]
 
 [case testOverrideGenericSelfClassMethod]
@@ -7532,7 +7532,7 @@ class Foo:
 
     @classmethod
     def bar(cls):
-        cls.baz()  # E: "Type[Foo]" has no attribute "baz"
+        cls.baz()  # E: "type[Foo]" has no attribute "baz"
 
 class C(Generic[T]):
     x: T
@@ -7595,14 +7595,14 @@ TypeT1 = TypeVar("TypeT1", bound=Type[Base])
 class C1:
     def method(self, other: type) -> int:
         if issubclass(other, Base):
-            reveal_type(other)  # N: Revealed type is "Type[__main__.Base]"
+            reveal_type(other)  # N: Revealed type is "type[__main__.Base]"
             return other.field
         return 0
 
 class C2(Generic[TypeT]):
     def method(self, other: TypeT) -> int:
         if issubclass(other, Base):
-            reveal_type(other)  # N: Revealed type is "Type[__main__.Base]"
+            reveal_type(other)  # N: Revealed type is "type[__main__.Base]"
             return other.field
         return 0
 
@@ -7837,7 +7837,7 @@ class Foo:
 
 reveal_type(Foo.foo)  # N: Revealed type is "builtins.int"
 reveal_type(Foo.bar)  # N: Revealed type is "Any"
-reveal_type(Foo.baz)  # E: "Type[Foo]" has no attribute "baz" \
+reveal_type(Foo.baz)  # E: "type[Foo]" has no attribute "baz" \
                       # N: Revealed type is "Any"
 
 [file mod.py]
@@ -8070,9 +8070,9 @@ class C(Tuple[T, S]):
     def foo(self, arg: T) -> S: ...
 
 cis: C[int, str]
-reveal_type(cis)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C[builtins.int, builtins.str]]"
+reveal_type(cis)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C[builtins.int, builtins.str]]"
 cii = C(0, 1)
-reveal_type(cii)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.C[builtins.int, builtins.int]]"
+reveal_type(cii)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.C[builtins.int, builtins.int]]"
 reveal_type(cis.foo)  # N: Revealed type is "def (arg: builtins.int) -> builtins.str"
 [builtins fixtures/tuple.pyi]
 
@@ -8084,7 +8084,7 @@ class C(Tuple[T, T]): ...
 class D(C[List[T]]): ...
 
 di: D[int]
-reveal_type(di)  # N: Revealed type is "Tuple[builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.D[builtins.int]]"
+reveal_type(di)  # N: Revealed type is "tuple[builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.D[builtins.int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testOverrideAttrWithSettableProperty]
@@ -8473,7 +8473,7 @@ class C(B[List[T]]): ...
 a = C[str]()
 a.foo = ["foo", "bar"]
 reveal_type(a.foo)  # N: Revealed type is "builtins.int"
-a.foo = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "List[str]")
+a.foo = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "list[str]")
 reveal_type(a.foo)  # N: Revealed type is "builtins.int"
 [builtins fixtures/property.pyi]
 
@@ -8566,7 +8566,7 @@ class C(B):
 
 c: C
 c.baz = "yes"  # OK, because of untyped decorator
-c.tricky = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "List[int]")
+c.tricky = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "list[int]")
 
 T = TypeVar("T")
 def deco(fn: Callable[[T, int, int], None]) -> Callable[[T, int], None]: ...
diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test
index 5d8f55ec598c..c822c7c44f41 100644
--- a/test-data/unit/check-columns.test
+++ b/test-data/unit/check-columns.test
@@ -47,13 +47,13 @@ aaa: str
 h(x=1, y=aaa, z=2) # E:10: Argument "y" to "h" has incompatible type "str"; expected "int"
 a: A
 ff(a.x) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int"
-ff([1]) # E:4: Argument 1 to "ff" has incompatible type "List[int]"; expected "int"
+ff([1]) # E:4: Argument 1 to "ff" has incompatible type "list[int]"; expected "int"
 # TODO: Different column in Python 3.8+
-#ff([1 for x in [1]]) # Argument 1 to "ff" has incompatible type "List[int]"; expected "int"
-ff({1: 2}) # E:4: Argument 1 to "ff" has incompatible type "Dict[int, int]"; expected "int"
+#ff([1 for x in [1]]) # Argument 1 to "ff" has incompatible type "list[int]"; expected "int"
+ff({1: 2}) # E:4: Argument 1 to "ff" has incompatible type "dict[int, int]"; expected "int"
 ff(1.1) # E:4: Argument 1 to "ff" has incompatible type "float"; expected "int"
 # TODO: Different column in Python 3.8+
-#ff( ( 1, 1)) # Argument 1 to "ff" has incompatible type "Tuple[int, int]"; expected "int"
+#ff( ( 1, 1)) # Argument 1 to "ff" has incompatible type "tuple[int, int]"; expected "int"
 ff(-a) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int"
 ff(a + 1) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int"
 ff(a < 1) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int"
@@ -69,9 +69,9 @@ def f(*x: int) -> None: pass
 def g(**x: int) -> None: pass
 
 a = ['']
-f(*a)  # E:4: Argument 1 to "f" has incompatible type "*List[str]"; expected "int"
+f(*a)  # E:4: Argument 1 to "f" has incompatible type "*list[str]"; expected "int"
 b = {'x': 'y'}
-g(**b) # E:5: Argument 1 to "g" has incompatible type "**Dict[str, str]"; expected "int"
+g(**b) # E:5: Argument 1 to "g" has incompatible type "**dict[str, str]"; expected "int"
 [builtins fixtures/dict.pyi]
 
 [case testColumnsMultipleStatementsPerLine]
@@ -183,7 +183,7 @@ if int():
 
 [case testColumnNeedTypeAnnotation]
 if 1:
-    x = [] # E:5: Need type annotation for "x" (hint: "x: List[] = ...")
+    x = [] # E:5: Need type annotation for "x" (hint: "x: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testColumnCallToUntypedFunction]
@@ -216,7 +216,7 @@ x = None
 
 [case testColumnInvalidIndexing]
 from typing import List
-([1]['']) # E:6: Invalid index type "str" for "List[int]"; expected type "int"
+([1]['']) # E:6: Invalid index type "str" for "list[int]"; expected type "int"
 (1[1]) # E:2: Value of type "int" is not indexable
 def f() -> None:
     1[1] = 1 # E:5: Unsupported target for indexed assignment ("int")
@@ -264,7 +264,7 @@ class D(A):
 # flags: --disallow-any-generics
 from typing import List, Callable
 def f(x: List) -> None: pass # E:10: Missing type parameters for generic type "List"
-def g(x: list) -> None: pass # E:10: Missing type parameters for generic type "List"
+def g(x: list) -> None: pass # E:10: Missing type parameters for generic type "list"
 if int():
     c: Callable # E:8: Missing type parameters for generic type "Callable"
 [builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test
index 1e58ebc77d0f..a0a5c44b2ba5 100644
--- a/test-data/unit/check-ctypes.test
+++ b/test-data/unit/check-ctypes.test
@@ -16,7 +16,7 @@ a[2] = MyCInt(42)
 a[3] = b"bytes"  # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \
                  # N: Possible overload variants: \
                  # N:     def __setitem__(self, int, Union[c_int, int], /) -> None \
-                 # N:     def __setitem__(self, slice, List[Union[c_int, int]], /) -> None
+                 # N:     def __setitem__(self, slice, list[Union[c_int, int]], /) -> None
 for x in a:
     reveal_type(x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/floatdict.pyi]
@@ -40,12 +40,12 @@ mya[0] = 42
 mya[1] = ctypes.c_int(42)  # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "c_int" \
                            # N: Possible overload variants: \
                            # N:     def __setitem__(self, int, Union[MyCInt, int], /) -> None \
-                           # N:     def __setitem__(self, slice, List[Union[MyCInt, int]], /) -> None
+                           # N:     def __setitem__(self, slice, list[Union[MyCInt, int]], /) -> None
 mya[2] = MyCInt(42)
 mya[3] = b"bytes"  # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \
                    # N: Possible overload variants: \
                    # N:     def __setitem__(self, int, Union[MyCInt, int], /) -> None \
-                   # N:     def __setitem__(self, slice, List[Union[MyCInt, int]], /) -> None
+                   # N:     def __setitem__(self, slice, list[Union[MyCInt, int]], /) -> None
 for myx in mya:
     reveal_type(myx)  # N: Revealed type is "__main__.MyCInt"
 
@@ -74,7 +74,7 @@ mya[2] = MyCInt(42)
 mya[3] = b"bytes"  # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \
                    # N: Possible overload variants: \
                    # N:     def __setitem__(self, int, Union[MyCInt, int, c_uint], /) -> None \
-                   # N:     def __setitem__(self, slice, List[Union[MyCInt, int, c_uint]], /) -> None
+                   # N:     def __setitem__(self, slice, list[Union[MyCInt, int, c_uint]], /) -> None
 for myx in mya:
     reveal_type(myx)  # N: Revealed type is "Union[__main__.MyCInt, builtins.int]"
 [builtins fixtures/floatdict.pyi]
diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test
index 72b60c874656..0c157510cb34 100644
--- a/test-data/unit/check-custom-plugin.test
+++ b/test-data/unit/check-custom-plugin.test
@@ -752,7 +752,7 @@ plugins=/test-data/unit/plugins/common_api_incremental.py
 [out]
 [out2]
 tmp/a.py:3: note: Revealed type is "builtins.str"
-tmp/a.py:4: error: "Type[Base]" has no attribute "__magic__"
+tmp/a.py:4: error: "type[Base]" has no attribute "__magic__"
 
 [case testArgKindsMethod]
 # flags: --config-file tmp/mypy.ini
diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test
index 8213f8df282a..7c534914aa2d 100644
--- a/test-data/unit/check-dataclass-transform.test
+++ b/test-data/unit/check-dataclass-transform.test
@@ -505,7 +505,7 @@ class FunctionModel:
         integer_: tuple
 
 FunctionModel(string_="abc", integer_=1)
-FunctionModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+FunctionModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "FunctionModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
@@ -528,7 +528,7 @@ class FunctionModel:
         integer_: int
 
 FunctionModel(string_="abc", integer_=1)
-FunctionModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+FunctionModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "FunctionModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
@@ -551,7 +551,7 @@ class BaseClassModel(ModelBase):
         integer_: tuple
 
 BaseClassModel(string_="abc", integer_=1)
-BaseClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+BaseClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "BaseClassModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
@@ -573,7 +573,7 @@ class BaseClassModel(ModelBase):
         integer_: int
 
 BaseClassModel(string_="abc", integer_=1)
-BaseClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+BaseClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "BaseClassModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
@@ -598,7 +598,7 @@ class MetaClassModel(ModelBaseWithMeta):
         integer_: tuple
 
 MetaClassModel(string_="abc", integer_=1)
-MetaClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+MetaClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "MetaClassModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
@@ -623,7 +623,7 @@ class MetaClassModel(ModelBaseWithMeta):
         integer_: int
 
 MetaClassModel(string_="abc", integer_=1)
-MetaClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int"
+MetaClassModel(string_="abc", integer_=tuple())  # E: Argument "integer_" to "MetaClassModel" has incompatible type "tuple[Never, ...]"; expected "int"
 
 [typing fixtures/typing-full.pyi]
 [builtins fixtures/dataclasses.pyi]
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index a3f46292e712..8117e3a96938 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -549,7 +549,7 @@ class A:
 
     @classmethod
     def foo(cls, x: Union[int, str]) -> Union[int, str]:
-        reveal_type(cls)            # N: Revealed type is "Type[__main__.A]"
+        reveal_type(cls)            # N: Revealed type is "type[__main__.A]"
         reveal_type(cls.other())    # N: Revealed type is "builtins.str"
         return x
 
@@ -700,7 +700,7 @@ class A(Generic[T]):
     return self.z[0]
 
   def problem(self) -> T:
-    return self.z  # E: Incompatible return value type (got "List[T]", expected "T")
+    return self.z  # E: Incompatible return value type (got "list[T]", expected "T")
 
 reveal_type(A)  # N: Revealed type is "def [T] (x: T`1, y: T`1, z: builtins.list[T`1]) -> __main__.A[T`1]"
 A(1, 2, ["a", "b"])  # E: Cannot infer type argument 1 of "A"
@@ -836,7 +836,7 @@ class A(Generic[T]):
 
   @classmethod
   def foo(cls) -> None:
-      reveal_type(cls)  # N: Revealed type is "Type[__main__.A[T`1]]"
+      reveal_type(cls)  # N: Revealed type is "type[__main__.A[T`1]]"
       cls.x  # E: Access to generic instance variables via class is ambiguous
 
   @classmethod
@@ -936,7 +936,7 @@ T = TypeVar('T', bound='A')
 class A:
     @classmethod
     def make(cls: Type[T]) -> T:
-        reveal_type(cls)  # N: Revealed type is "Type[T`-1]"
+        reveal_type(cls)  # N: Revealed type is "type[T`-1]"
         reveal_type(cls())  # N: Revealed type is "T`-1"
         return cls()
 [builtins fixtures/dataclasses.pyi]
@@ -1386,7 +1386,7 @@ class Foo:
     bar: float = field(**{"repr": False})
 [out]
 main:6: error: Unpacking **kwargs in "field()" is not supported
-main:6: error: No overload variant of "field" matches argument type "Dict[str, bool]"
+main:6: error: No overload variant of "field" matches argument type "dict[str, bool]"
 main:6: note: Possible overload variants:
 main:6: note:     def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T
 main:6: note:     def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T
@@ -1520,14 +1520,14 @@ class Some:
     y: str
     z: bool
 
-reveal_type(Some.__slots__)  # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]"
+reveal_type(Some.__slots__)  # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.str]"
 
 @dataclass(slots=True)
 class Other:
     x: int
     y: str
 
-reveal_type(Other.__slots__)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(Other.__slots__)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 
 
 @dataclass
@@ -1535,7 +1535,7 @@ class NoSlots:
     x: int
     y: str
 
-NoSlots.__slots__  # E: "Type[NoSlots]" has no attribute "__slots__"
+NoSlots.__slots__  # E: "type[NoSlots]" has no attribute "__slots__"
 [builtins fixtures/dataclasses.pyi]
 
 
@@ -1834,17 +1834,17 @@ class One:
     bar: int
     baz: str
 o: One
-reveal_type(o.__match_args__)  # N: Revealed type is "Tuple[Literal['bar'], Literal['baz']]"
+reveal_type(o.__match_args__)  # N: Revealed type is "tuple[Literal['bar'], Literal['baz']]"
 @dataclass(match_args=True)
 class Two:
     bar: int
 t: Two
-reveal_type(t.__match_args__)  # N: Revealed type is "Tuple[Literal['bar']]"
+reveal_type(t.__match_args__)  # N: Revealed type is "tuple[Literal['bar']]"
 @dataclass
 class Empty:
     ...
 e: Empty
-reveal_type(e.__match_args__)  # N: Revealed type is "Tuple[()]"
+reveal_type(e.__match_args__)  # N: Revealed type is "tuple[()]"
 [builtins fixtures/dataclasses.pyi]
 
 [case testDataclassWithMatchArgsAndKwOnly]
@@ -1854,13 +1854,13 @@ from dataclasses import dataclass, field
 class One:
     a: int
     b: str
-reveal_type(One.__match_args__)  # N: Revealed type is "Tuple[()]"
+reveal_type(One.__match_args__)  # N: Revealed type is "tuple[()]"
 
 @dataclass(kw_only=True)
 class Two:
     a: int = field(kw_only=False)
     b: str
-reveal_type(Two.__match_args__)  # N: Revealed type is "Tuple[Literal['a']]"
+reveal_type(Two.__match_args__)  # N: Revealed type is "tuple[Literal['a']]"
 [builtins fixtures/dataclasses.pyi]
 
 [case testDataclassWithoutMatchArgs]
@@ -2097,7 +2097,7 @@ a_or_b: Union[A[int], B]
 _ = replace(a_or_b, x=42, y=True, init_var=42)
 _ = replace(a_or_b, x=42, y=True)  # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]"
 _ = replace(a_or_b, x=42, y=True, z='42', init_var=42)  # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected "Never"
-_ = replace(a_or_b, x=42, y=True, w={}, init_var=42)  # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never"
+_ = replace(a_or_b, x=42, y=True, w={}, init_var=42)  # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "dict[Never, Never]"; expected "Never"
 _ = replace(a_or_b, y=42, init_var=42)  # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool"
 
 [builtins fixtures/tuple.pyi]
@@ -2202,7 +2202,7 @@ from dataclasses import is_dataclass, replace
 def f(x: object) -> None:
   _ = replace(x)  # E: Value of type variable "_DataclassT" of "replace" cannot be "object"
   if is_dataclass(x):
-    _ = replace(x)  # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, Type[DataclassInstance]]"
+    _ = replace(x)  # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, type[DataclassInstance]]"
     if not isinstance(x, type):
       _ = replace(x)
 
@@ -2423,7 +2423,7 @@ main:7: note:      Superclass:
 main:7: note:          def __post_init__(self: Test, y: str) -> None
 main:7: note:      Subclass:
 main:7: note:          @classmethod
-main:7: note:          def __post_init__(cls: Type[Test]) -> None
+main:7: note:          def __post_init__(cls: type[Test]) -> None
 
 [case testPostInitStaticMethod]
 from dataclasses import dataclass, InitVar
diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test
index 6cc160fad81f..e1173ac425ba 100644
--- a/test-data/unit/check-deprecated.test
+++ b/test-data/unit/check-deprecated.test
@@ -113,7 +113,7 @@ class C: ...
 c: C  # E: class __main__.C is deprecated: use C2 instead
 C()  # E: class __main__.C is deprecated: use C2 instead
 C.missing()  # E: class __main__.C is deprecated: use C2 instead \
-             # E: "Type[C]" has no attribute "missing"
+             # E: "type[C]" has no attribute "missing"
 C.__init__(c)  # E: class __main__.C is deprecated: use C2 instead
 C(1)  # E: class __main__.C is deprecated: use C2 instead \
       # E: Too many arguments for "C"
diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test
index ffab5afeda3e..166073dd1553 100644
--- a/test-data/unit/check-dynamic-typing.test
+++ b/test-data/unit/check-dynamic-typing.test
@@ -279,7 +279,7 @@ t2: Tuple[A, A]
 d: Any
 
 if int():
-    t2 = (d, d, d)  # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[A, A]")
+    t2 = (d, d, d)  # E: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[A, A]")
 if int():
     t2 = (d, d)
 
@@ -571,7 +571,7 @@ a: A
 
 A(a)   # E: Missing positional argument "b" in call to "A"
 if int():
-    f1 = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "Callable[[A], A]")
+    f1 = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "Callable[[A], A]")
 
 A(a, a)
 if int():
@@ -599,8 +599,8 @@ t5: Tuple[Any, Any, Any]
 
 def f(): t1, t2, t3, t4, t5 # Prevent redefinition
 
-t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[Any, Any]")
-t5 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[Any, Any, Any]")
+t3 = t5 # E: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[Any, Any]")
+t5 = t4 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[Any, Any, Any]")
 
 t1 = t1
 t1 = t2
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index cc9048db18dc..1a07e4527527 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -601,10 +601,10 @@ T = Enum('T', keyword='a b')  # E: Unexpected keyword argument "keyword"
 U = Enum('U', *['a'])  # E: Unexpected arguments to Enum()
 V = Enum('U', **{'a': 1})  # E: Unexpected arguments to Enum()
 W = Enum('W', 'a b')
-W.c  # E: "Type[W]" has no attribute "c"
+W.c  # E: "type[W]" has no attribute "c"
 X = Enum('Something', 'a b')  # E: String argument 1 "Something" to enum.Enum(...) does not match variable name "X"
 reveal_type(X.a)  # N: Revealed type is "Literal[__main__.Something@23.a]?"
-X.asdf  # E: "Type[Something@23]" has no attribute "asdf"
+X.asdf  # E: "type[Something@23]" has no attribute "asdf"
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-medium.pyi]
@@ -931,7 +931,7 @@ class Foo(Enum):
     A = 1
     B = 2
 
-Foo._order_  # E: "Type[Foo]" has no attribute "_order_"
+Foo._order_  # E: "type[Foo]" has no attribute "_order_"
 
 x: Literal[Foo.A, Foo.B]
 if x is Foo.A:
@@ -946,7 +946,7 @@ class Bar(Enum):
     A = 1
     B = 2
 
-Bar.__order__  # E: "Type[Bar]" has no attribute "__order__"
+Bar.__order__  # E: "type[Bar]" has no attribute "__order__"
 
 y: Literal[Bar.A, Bar.B]
 if y is Bar.A:
@@ -2024,7 +2024,7 @@ class A(Enum):
 reveal_type(A.str.value)  # N: Revealed type is "Literal['foo']?"
 reveal_type(A.int.value)  # N: Revealed type is "Literal[1]?"
 reveal_type(A.bool.value)  # N: Revealed type is "Literal[False]?"
-reveal_type(A.tuple.value)  # N: Revealed type is "Tuple[Literal[1]?]"
+reveal_type(A.tuple.value)  # N: Revealed type is "tuple[Literal[1]?]"
 [builtins fixtures/tuple.pyi]
 
 [case testFinalWithPrivateAssignment]
@@ -2244,7 +2244,7 @@ from enum import Enum
 class C(Enum):
     _ignore_ = 'X'
 
-C._ignore_ # E: "Type[C]" has no attribute "_ignore_"
+C._ignore_ # E: "type[C]" has no attribute "_ignore_"
 [builtins fixtures/enum.pyi]
 
 [case testCanOverrideDunderAttributes]
@@ -2302,7 +2302,7 @@ class A(Some, Enum):
 from enum import Enum
 
 class Mixed(Enum):
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     b = None
 
     def check(self) -> None:
@@ -2319,8 +2319,8 @@ class Mixed(Enum):
                 pass
 
 class AllPartialList(Enum):
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
-    b = []  # E: Need type annotation for "b" (hint: "b: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
+    b = []  # E: Need type annotation for "b" (hint: "b: list[] = ...")
 
     def check(self) -> None:
         reveal_type(self.value)  # N: Revealed type is "builtins.list[Any]"
@@ -2335,7 +2335,7 @@ class MyEnum(Enum):
     __my_dict = {A: "ham", B: "spam"}
 
 # TODO: change the next line to use MyEnum._MyEnum__my_dict when mypy implements name mangling
-x: MyEnum = MyEnum.__my_dict  # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum")
+x: MyEnum = MyEnum.__my_dict  # E: Incompatible types in assignment (expression has type "dict[int, str]", variable has type "MyEnum")
 [builtins fixtures/enum.pyi]
 
 [case testEnumWithPrivateAttributeReachability]
diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test
index c07a161823da..d6e3366401dd 100644
--- a/test-data/unit/check-errorcodes.test
+++ b/test-data/unit/check-errorcodes.test
@@ -272,7 +272,7 @@ from typing import TypeVar
 T = TypeVar('T')
 def f() -> T: pass # E: A function returning TypeVar should receive at least one argument containing the same TypeVar  [type-var]
 x = f()  # E: Need type annotation for "x"  [var-annotated]
-y = []  # E: Need type annotation for "y" (hint: "y: List[] = ...")  [var-annotated]
+y = []  # E: Need type annotation for "y" (hint: "y: list[] = ...")  [var-annotated]
 [builtins fixtures/list.pyi]
 
 [case testErrorCodeBadOverride]
@@ -344,7 +344,7 @@ a.x = ''  # E: Incompatible types in assignment (expression has type "str", vari
 # flags: --disallow-any-generics
 from typing import List, TypeVar
 x: List  # E: Missing type parameters for generic type "List"  [type-arg]
-y: list  # E: Missing type parameters for generic type "List"  [type-arg]
+y: list  # E: Missing type parameters for generic type "list"  [type-arg]
 T = TypeVar('T')
 L = List[List[T]]
 z: L  # E: Missing type parameters for generic type "L"  [type-arg]
@@ -397,7 +397,7 @@ def g():
 [case testErrorCodeIndexing]
 from typing import Dict
 x: Dict[int, int]
-x['']  # E: Invalid index type "str" for "Dict[int, int]"; expected type "int"  [index]
+x['']  # E: Invalid index type "str" for "dict[int, int]"; expected type "int"  [index]
 1['']  # E: Value of type "int" is not indexable  [index]
 1[''] = 1  # E: Unsupported target for indexed assignment ("int")  [index]
 [builtins fixtures/dict.pyi]
@@ -1071,12 +1071,12 @@ class C(abc.ABC):
 
 T = TypeVar("T")
 def test(tp: Type[T]) -> T: ...
-test(C)  # E: Only concrete class can be given where "Type[C]" is expected  [type-abstract]
+test(C)  # E: Only concrete class can be given where "type[C]" is expected  [type-abstract]
 
 class D(C):
     @abc.abstractmethod
     def bar(self) -> None: ...
-cls: Type[C] = D  # E: Can only assign concrete classes to a variable of type "Type[C]"  [type-abstract]
+cls: Type[C] = D  # E: Can only assign concrete classes to a variable of type "type[C]"  [type-abstract]
 
 [case testUncheckedAnnotationCodeShown]
 def f():
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index a3b15a3b1da4..a0302fcd1943 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -750,17 +750,17 @@ i = 8
 f = 8.0
 d = Decimal(8)
 
-reveal_type(divmod(i, i))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
-reveal_type(divmod(f, i))  # N: Revealed type is "Tuple[builtins.float, builtins.float]"
-reveal_type(divmod(d, i))  # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]"
+reveal_type(divmod(i, i))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
+reveal_type(divmod(f, i))  # N: Revealed type is "tuple[builtins.float, builtins.float]"
+reveal_type(divmod(d, i))  # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]"
 
-reveal_type(divmod(i, f))  # N: Revealed type is "Tuple[builtins.float, builtins.float]"
-reveal_type(divmod(f, f))  # N: Revealed type is "Tuple[builtins.float, builtins.float]"
+reveal_type(divmod(i, f))  # N: Revealed type is "tuple[builtins.float, builtins.float]"
+reveal_type(divmod(f, f))  # N: Revealed type is "tuple[builtins.float, builtins.float]"
 divmod(d, f)  # E: Unsupported operand types for divmod ("Decimal" and "float")
 
-reveal_type(divmod(i, d))  # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]"
+reveal_type(divmod(i, d))  # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]"
 divmod(f, d)  # E: Unsupported operand types for divmod ("float" and "Decimal")
-reveal_type(divmod(d, d))  # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]"
+reveal_type(divmod(d, d))  # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]"
 
 # Now some bad calls
 divmod()  # E: "divmod" expects 2 arguments \
@@ -1378,7 +1378,7 @@ class B: pass
 [out]
 main:5: error: Key expression in dictionary comprehension has incompatible type "A"; expected type "B"
 main:5: error: Value expression in dictionary comprehension has incompatible type "B"; expected type "A"
-main:6: error: Incompatible types in assignment (expression has type "Dict[A, B]", variable has type "A")
+main:6: error: Incompatible types in assignment (expression has type "dict[A, B]", variable has type "A")
 
 
 [case testDictionaryComprehensionWithNonDirectMapping]
@@ -1661,13 +1661,13 @@ d1 = dict(a=1, b=2) # type: Dict[str, int]
 d2 = dict(a=1, b='') # type: Dict[str, int] # E: Dict entry 1 has incompatible type "str": "str"; expected "str": "int"
 d3 = dict(a=1) # type: Dict[int, int] # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "int"
 d4 = dict(a=1, b=1)
-d4.xyz # E: "Dict[str, int]" has no attribute "xyz"
+d4.xyz # E: "dict[str, int]" has no attribute "xyz"
 d5 = dict(a=1, b='') # type: Dict[str, Any]
 [builtins fixtures/dict.pyi]
 
 [case testDictWithoutKeywordArgs]
 from typing import Dict
-d = dict() # E: Need type annotation for "d" (hint: "d: Dict[, ] = ...")
+d = dict() # E: Need type annotation for "d" (hint: "d: dict[, ] = ...")
 d2 = dict() # type: Dict[int, str]
 dict(undefined) # E: Name "undefined" is not defined
 [builtins fixtures/dict.pyi]
@@ -1675,8 +1675,8 @@ dict(undefined) # E: Name "undefined" is not defined
 [case testDictFromList]
 from typing import Dict
 d = dict([(1, 'x'), (2, 'y')])
-d() # E: "Dict[int, str]" not callable
-d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "Tuple[int, str]"; expected "Tuple[str, str]"
+d() # E: "dict[int, str]" not callable
+d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "tuple[int, str]"; expected "tuple[str, str]"
 [builtins fixtures/dict.pyi]
 
 [case testDictFromIterableAndKeywordArg]
@@ -1684,10 +1684,10 @@ from typing import Dict
 it = [('x', 1)]
 
 d = dict(it, x=1)
-d() # E: "Dict[str, int]" not callable
+d() # E: "dict[str, int]" not callable
 
 d2 = dict(it, x='')
-d2() # E: "Dict[str, object]" not callable
+d2() # E: "dict[str, object]" not callable
 
 d3 = dict(it, x='') # type: Dict[str, int] # E: Argument "x" to "dict" has incompatible type "str"; expected "int"
 [builtins fixtures/dict.pyi]
@@ -1699,7 +1699,7 @@ dict(it, x='y') # E: Keyword argument only valid with "str" key type in call to
 
 [case testDictFromIterableAndKeywordArg3]
 d = dict([], x=1)
-d() # E: "Dict[str, int]" not callable
+d() # E: "dict[str, int]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testDictFromIterableAndStarStarArgs]
@@ -1708,20 +1708,20 @@ it = [('x', 1)]
 
 kw = {'x': 1}
 d = dict(it, **kw)
-d() # E: "Dict[str, int]" not callable
+d() # E: "dict[str, int]" not callable
 
 kw2 = {'x': ''}
 d2 = dict(it, **kw2)
-d2() # E: "Dict[str, object]" not callable
+d2() # E: "dict[str, object]" not callable
 
-d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**Dict[str, str]"; expected "int"
+d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**dict[str, str]"; expected "int"
 [builtins fixtures/dict.pyi]
 
 [case testDictFromIterableAndStarStarArgs2]
 it = [(1, 'x')]
 kw = {'x': 'y'}
 d = dict(it, **kw) # E: Keyword argument only valid with "str" key type in call to "dict"
-d() # E: "Dict[int, str]" not callable
+d() # E: "dict[int, str]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testUserDefinedClassNamedDict]
@@ -1880,7 +1880,7 @@ c = {**b}
 d = {**a, **b, 'c': 3}
 e = {1: 'a', **a}  # E: Cannot infer type argument 1 of  \
                    # N: Try assigning the literal to a variable annotated as dict[, ]
-f = {**b}  # type: Dict[int, int]  # E: Unpacked dict entry 0 has incompatible type "Dict[str, int]"; expected "SupportsKeysAndGetItem[int, int]"
+f = {**b}  # type: Dict[int, int]  # E: Unpacked dict entry 0 has incompatible type "dict[str, int]"; expected "SupportsKeysAndGetItem[int, int]"
 g = {**Thing()}
 h = {**a, **Thing()}
 i = {**Thing()}  # type: Dict[int, int]  # E: Unpacked dict entry 0 has incompatible type "Thing"; expected "SupportsKeysAndGetItem[int, int]" \
@@ -1938,8 +1938,8 @@ class B: ...
 [builtins fixtures/dict.pyi]
 
 [case testTypeAnnotationNeededMultipleAssignment]
-x, y = [], [] # E: Need type annotation for "x" (hint: "x: List[] = ...") \
-            # E: Need type annotation for "y" (hint: "y: List[] = ...")
+x, y = [], [] # E: Need type annotation for "x" (hint: "x: list[] = ...") \
+            # E: Need type annotation for "y" (hint: "y: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testStrictEqualityEq]
@@ -2169,7 +2169,7 @@ class CustomMeta(type):
 class Normal: ...
 class Custom(metaclass=CustomMeta): ...
 
-Normal == int()  # E: Non-overlapping equality check (left operand type: "Type[Normal]", right operand type: "int")
+Normal == int()  # E: Non-overlapping equality check (left operand type: "type[Normal]", right operand type: "int")
 Normal == Normal
 Custom == int()
 [builtins fixtures/bool.pyi]
@@ -2194,7 +2194,7 @@ class Bad: ...
 subclasses: List[Type[C]]
 object in subclasses
 D in subclasses
-Bad in subclasses  # E: Non-overlapping container check (element type: "Type[Bad]", container item type: "Type[C]")
+Bad in subclasses  # E: Non-overlapping container check (element type: "type[Bad]", container item type: "type[C]")
 [builtins fixtures/list.pyi]
 [typing fixtures/typing-full.pyi]
 
@@ -2216,7 +2216,7 @@ exp: List[Meta]
 
 A in exp
 B in exp
-C in exp  # E: Non-overlapping container check (element type: "Type[C]", container item type: "Meta")
+C in exp  # E: Non-overlapping container check (element type: "type[C]", container item type: "Meta")
 
 o in exp
 a in exp
@@ -2391,7 +2391,7 @@ assert a == b
 
 R2 = Dict[int, R2]
 c: R2
-assert a == c  # E: Non-overlapping equality check (left operand type: "Dict[str, R]", right operand type: "Dict[int, R2]")
+assert a == c  # E: Non-overlapping equality check (left operand type: "dict[str, R]", right operand type: "dict[int, R2]")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
 
diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test
index d78c2a8e57f2..d23199dc8b33 100644
--- a/test-data/unit/check-final.test
+++ b/test-data/unit/check-final.test
@@ -41,7 +41,7 @@ class C:
     def __init__(self, x: Tuple[int, Any]) -> None:
         self.x: Final = x
         self.y: Final[float] = 1
-reveal_type(C((1, 2)).x)  # N: Revealed type is "Tuple[builtins.int, Any]"
+reveal_type(C((1, 2)).x)  # N: Revealed type is "tuple[builtins.int, Any]"
 reveal_type(C((1, 2)).y)  # N: Revealed type is "builtins.float"
 [builtins fixtures/tuple.pyi]
 [out]
@@ -251,7 +251,7 @@ class C(Generic[T]):
         self.x: Final = x
         self.y: Final = 1
 
-reveal_type(C((1, 2)).x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(C((1, 2)).x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 C.x  # E: Cannot access final instance attribute "x" on class object \
      # E: Access to generic instance variables via class is ambiguous
 C.y  # E: Cannot access final instance attribute "y" on class object
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
index ae126fb5e603..bb64bb44d282 100644
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -979,9 +979,9 @@ def foo(l: List[Unchecked]) -> List[Unchecked]:
     return l
 [builtins fixtures/list.pyi]
 [out]
-main:5: error: Return type becomes "List[Any]" due to an unfollowed import
-main:5: error: Argument 1 to "foo" becomes "List[Any]" due to an unfollowed import
-main:6: error: Type of variable becomes "List[Any]" due to an unfollowed import
+main:5: error: Return type becomes "list[Any]" due to an unfollowed import
+main:5: error: Argument 1 to "foo" becomes "list[Any]" due to an unfollowed import
+main:6: error: Type of variable becomes "list[Any]" due to an unfollowed import
 
 [case testDisallowImplicitAnyInherit]
 # flags: --ignore-missing-imports --disallow-any-unimported
@@ -991,7 +991,7 @@ from typing import List
 class C(Unchecked): # E: Base type Unchecked becomes "Any" due to an unfollowed import
     pass
 
-class A(List[Unchecked]): # E: Base type becomes "List[Any]" due to an unfollowed import
+class A(List[Unchecked]): # E: Base type becomes "list[Any]" due to an unfollowed import
     pass
 [builtins fixtures/list.pyi]
 
@@ -1000,7 +1000,7 @@ class A(List[Unchecked]): # E: Base type becomes "List[Any]" due to an unfollowe
 from missing import Unchecked
 from typing import List
 
-X = List[Unchecked]  # E: Type alias target becomes "List[Any]" due to an unfollowed import
+X = List[Unchecked]  # E: Type alias target becomes "list[Any]" due to an unfollowed import
 
 def f(x: X) -> None:
     pass
@@ -1013,7 +1013,7 @@ from typing import List, cast
 
 
 foo = [1, 2, 3]
-cast(List[Unchecked], foo)  # E: Target type of cast becomes "List[Any]" due to an unfollowed import
+cast(List[Unchecked], foo)  # E: Target type of cast becomes "list[Any]" due to an unfollowed import
 cast(Unchecked, foo)  # E: Target type of cast becomes "Any" due to an unfollowed import
 [builtins fixtures/list.pyi]
 
@@ -1026,7 +1026,7 @@ Point = NamedTuple('Point', [('x', List[Unchecked]),
                              ('y', Unchecked)])
 [builtins fixtures/list.pyi]
 [out]
-main:5: error: NamedTuple type becomes "Tuple[List[Any], Any]" due to an unfollowed import
+main:5: error: NamedTuple type becomes "tuple[list[Any], Any]" due to an unfollowed import
 
 [case testDisallowImplicitAnyTypeVarConstraints]
 # flags: --ignore-missing-imports --disallow-any-unimported
@@ -1037,7 +1037,7 @@ T = TypeVar('T', Unchecked, List[Unchecked], str)
 [builtins fixtures/list.pyi]
 [out]
 main:5: error: Constraint 1 becomes "Any" due to an unfollowed import
-main:5: error: Constraint 2 becomes "List[Any]" due to an unfollowed import
+main:5: error: Constraint 2 becomes "list[Any]" due to an unfollowed import
 
 [case testDisallowImplicitAnyNewType]
 # flags: --ignore-missing-imports --disallow-any-unimported
@@ -1045,7 +1045,7 @@ from typing import NewType, List
 from missing import Unchecked
 
 Baz = NewType('Baz', Unchecked)  # E: Argument 2 to NewType(...) must be subclassable (got "Any")
-Bar = NewType('Bar', List[Unchecked])  # E: Argument 2 to NewType(...) becomes "List[Any]" due to an unfollowed import
+Bar = NewType('Bar', List[Unchecked])  # E: Argument 2 to NewType(...) becomes "list[Any]" due to an unfollowed import
 
 [builtins fixtures/list.pyi]
 
@@ -1058,7 +1058,7 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]:
     return f()
 [builtins fixtures/list.pyi]
 [out]
-main:5: error: Return type becomes "Tuple[Any]" due to an unfollowed import
+main:5: error: Return type becomes "tuple[Any]" due to an unfollowed import
 main:5: error: Argument 1 to "foo" becomes "Callable[[], Any]" due to an unfollowed import
 
 [case testDisallowImplicitAnySubclassingExplicitAny]
@@ -1096,7 +1096,7 @@ def f(m: M) -> M: pass  # no error
 from typing import List, TypedDict
 from x import Unchecked
 
-M = TypedDict('M', {'x': str, 'y': List[Unchecked]})  # E: Type of a TypedDict key becomes "List[Any]" due to an unfollowed import
+M = TypedDict('M', {'x': str, 'y': List[Unchecked]})  # E: Type of a TypedDict key becomes "list[Any]" due to an unfollowed import
 
 def f(m: M) -> M: pass  # no error
 [builtins fixtures/dict.pyi]
@@ -1170,10 +1170,10 @@ def d3(f) -> Callable[[Any], List[str]]: pass
 def f(i: int, s: str) -> None:  # E: Type of decorated function contains type "Any" ("Callable[[int, Any], Any]")
     pass
 @d2
-def g(i: int) -> None:  # E: Type of decorated function contains type "Any" ("Callable[[int], List[Any]]")
+def g(i: int) -> None:  # E: Type of decorated function contains type "Any" ("Callable[[int], list[Any]]")
     pass
 @d3
-def h(i: int) -> None:  # E: Type of decorated function contains type "Any" ("Callable[[Any], List[str]]")
+def h(i: int) -> None:  # E: Type of decorated function contains type "Any" ("Callable[[Any], list[str]]")
     pass
 [builtins fixtures/list.pyi]
 
@@ -1260,9 +1260,9 @@ def g(s: List[Any]) -> None:
 
 f(0)
 
-# type of list below is inferred with expected type of "List[Any]", so that becomes it's type
-# instead of List[str]
-g([''])  # E: Expression type contains "Any" (has type "List[Any]")
+# type of list below is inferred with expected type of "list[Any]", so that becomes it's type
+# instead of list[str]
+g([''])  # E: Expression type contains "Any" (has type "list[Any]")
 [builtins fixtures/list.pyi]
 
 [case testDisallowAnyExprAllowsAnyInCast]
@@ -1293,8 +1293,8 @@ n = Foo().g  # type: Any  # E: Expression has type "Any"
 from typing import List
 
 l: List = []
-l.append(1)  # E: Expression type contains "Any" (has type "List[Any]")
-k = l[0]  # E: Expression type contains "Any" (has type "List[Any]")  # E: Expression has type "Any"
+l.append(1)  # E: Expression type contains "Any" (has type "list[Any]")
+k = l[0]  # E: Expression type contains "Any" (has type "list[Any]")  # E: Expression has type "Any"
 [builtins fixtures/list.pyi]
 
 [case testDisallowAnyExprTypeVar]
@@ -1531,19 +1531,19 @@ def f(t: tuple) -> None: pass  # E: Missing type parameters for generic type "tu
 [case testDisallowAnyGenericsBuiltinList]
 # flags: --disallow-any-generics
 l = list([1, 2, 3])
-def f(t: list) -> None: pass  # E: Missing type parameters for generic type "List"
+def f(t: list) -> None: pass  # E: Missing type parameters for generic type "list"
 [builtins fixtures/list.pyi]
 
 [case testDisallowAnyGenericsBuiltinSet]
 # flags: --disallow-any-generics
 l = set({1, 2, 3})
-def f(s: set) -> None: pass  # E: Missing type parameters for generic type "Set"
+def f(s: set) -> None: pass  # E: Missing type parameters for generic type "set"
 [builtins fixtures/set.pyi]
 
 [case testDisallowAnyGenericsBuiltinDict]
 # flags: --disallow-any-generics
 l = dict([('a', 1)])
-def f(d: dict) -> None: pass  # E: Missing type parameters for generic type "Dict"
+def f(d: dict) -> None: pass  # E: Missing type parameters for generic type "dict"
 [builtins fixtures/dict.pyi]
 
 [case testCheckDefaultAllowAnyGeneric]
@@ -2012,7 +2012,7 @@ def h(l: List[List]) -> None: pass   # E: Missing type parameters for generic ty
 def i(l: List[List[List[List]]]) -> None: pass  # E: Missing type parameters for generic type "List"
 def j() -> List: pass  # E: Missing type parameters for generic type "List"
 
-x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 y: List = []  # E: Missing type parameters for generic type "List"
 [builtins fixtures/list.pyi]
 
@@ -2270,7 +2270,7 @@ untyped_calls_exclude = foo, bar.A
 import tests.foo
 import bar
 [file bar.py]
-x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 [file tests/__init__.py]
 [file tests/foo.py]
 x = []  # OK
@@ -2450,13 +2450,13 @@ cb(fn)
 x: int = ""  # E: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
 list(1)  # E: No overload variant of "list" matches argument type "int"  [call-overload] \
          # N: Possible overload variants: \
-         # N:     def [T] __init__(self) -> List[T] \
-         # N:     def [T] __init__(self, x: Iterable[T]) -> List[T] \
+         # N:     def [T] __init__(self) -> list[T] \
+         # N:     def [T] __init__(self, x: Iterable[T]) -> list[T] \
          # N: See https://mypy.rtfd.io/en/stable/_refs.html#code-call-overload for more info
 list(2)  # E: No overload variant of "list" matches argument type "int"  [call-overload] \
          # N: Possible overload variants: \
-         # N:     def [T] __init__(self) -> List[T] \
-         # N:     def [T] __init__(self, x: Iterable[T]) -> List[T]
+         # N:     def [T] __init__(self) -> list[T] \
+         # N:     def [T] __init__(self, x: Iterable[T]) -> list[T]
 [builtins fixtures/list.pyi]
 
 [case testNestedGenericInAliasDisallow]
diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test
index dce26b37dfc8..b5b37f8d2976 100644
--- a/test-data/unit/check-formatting.test
+++ b/test-data/unit/check-formatting.test
@@ -150,8 +150,8 @@ di: Dict[int, int]
 '%(a)' % 1  # E: Format requires a mapping (expression has type "int", expected type for mapping is "SupportsKeysAndGetItem[str, Any]")
 '%()d' % a
 '%()d' % ds
-'%()d' % do  # E: Format requires a mapping (expression has type "Dict[object, int]", expected type for mapping is "SupportsKeysAndGetItem[str, Any]")
-b'%()d' % ds  # E: Format requires a mapping (expression has type "Dict[str, int]", expected type for mapping is "SupportsKeysAndGetItem[bytes, Any]")
+'%()d' % do  # E: Format requires a mapping (expression has type "dict[object, int]", expected type for mapping is "SupportsKeysAndGetItem[str, Any]")
+b'%()d' % ds  # E: Format requires a mapping (expression has type "dict[str, int]", expected type for mapping is "SupportsKeysAndGetItem[bytes, Any]")
 '%()s' % StringThing()
 b'%()s' % BytesThing()
 [builtins fixtures/primitives.pyi]
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index fd4cd86d1a93..4ef8e47e763a 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -369,7 +369,7 @@ t: type
 a: A
 
 if int():
-    a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A")
+    a = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "A")
 if int():
     t = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type")
 if int():
@@ -464,10 +464,10 @@ def f(x: C) -> C: pass
 from typing import Any, Callable, List
 def f(fields: List[Callable[[Any], Any]]): pass
 class C: pass
-f([C])  # E: List item 0 has incompatible type "Type[C]"; expected "Callable[[Any], Any]"
+f([C])  # E: List item 0 has incompatible type "type[C]"; expected "Callable[[Any], Any]"
 class D:
     def __init__(self, a, b): pass
-f([D])  # E: List item 0 has incompatible type "Type[D]"; expected "Callable[[Any], Any]"
+f([D])  # E: List item 0 has incompatible type "type[D]"; expected "Callable[[Any], Any]"
 [builtins fixtures/list.pyi]
 
 [case testSubtypingTypeTypeAsCallable]
@@ -483,7 +483,7 @@ class A: pass
 x: Callable[..., A]
 y: Type[A]
 if int():
-    y = x  # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "Type[A]")
+    y = x  # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "type[A]")
 
 -- Default argument values
 -- -----------------------
@@ -945,7 +945,7 @@ def f(x): pass
 def faulty(c: Callable[[int], None]) -> Callable[[tuple[int, int]], None]:
     return lambda x: None
 
-@faulty  # E: Argument 1 to "faulty" has incompatible type "Callable[[Tuple[int, int]], None]"; expected "Callable[[int], None]"
+@faulty  # E: Argument 1 to "faulty" has incompatible type "Callable[[tuple[int, int]], None]"; expected "Callable[[int], None]"
 @faulty  # E: Argument 1 to "faulty" has incompatible type "Callable[[str], None]"; expected "Callable[[int], None]"
 def g(x: str) -> None:
     return None
@@ -1614,11 +1614,11 @@ if g(C()):
     def f(x: B) -> B: pass
 
 [case testRedefineFunctionDefinedAsVariableInitializedToEmptyList]
-f = [] # E: Need type annotation for "f" (hint: "f: List[] = ...")
+f = [] # E: Need type annotation for "f" (hint: "f: list[] = ...")
 if object():
     def f(): pass # E: Incompatible redefinition
-f()  # E: "List[Any]" not callable
-f(1)  # E: "List[Any]" not callable
+f()  # E: "list[Any]" not callable
+f(1)  # E: "list[Any]" not callable
 [builtins fixtures/list.pyi]
 
 [case testDefineConditionallyAsImportedAndDecorated]
@@ -2111,7 +2111,7 @@ f(x=1, y="hello", z=[])
 from typing import Dict
 def f(x, **kwargs): # type: (...) -> None
     success_dict_type = kwargs # type: Dict[str, str]
-    failure_dict_type = kwargs # type: Dict[int, str] # E: Incompatible types in assignment (expression has type "Dict[str, Any]", variable has type "Dict[int, str]")
+    failure_dict_type = kwargs # type: Dict[int, str] # E: Incompatible types in assignment (expression has type "dict[str, Any]", variable has type "dict[int, str]")
 f(1, thing_in_kwargs=["hey"])
 [builtins fixtures/dict.pyi]
 [out]
@@ -2120,7 +2120,7 @@ f(1, thing_in_kwargs=["hey"])
 from typing import Tuple, Any
 def f(x, *args): # type: (...) -> None
     success_tuple_type = args # type: Tuple[Any, ...]
-    fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "None")
+    fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type "tuple[Any, ...]", variable has type "None")
 f(1, "hello")
 [builtins fixtures/tuple.pyi]
 [out]
@@ -2447,7 +2447,7 @@ def make_list() -> List[T]: pass
 
 l: List[int] = make_list()
 
-bad = make_list()  # E: Need type annotation for "bad" (hint: "bad: List[] = ...")
+bad = make_list()  # E: Need type annotation for "bad" (hint: "bad: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testAnonymousArgumentError]
@@ -2494,26 +2494,26 @@ def fn(
 from typing import Union, Dict, List
 def f() -> List[Union[str, int]]:
     x = ['a']
-    return x # E: Incompatible return value type (got "List[str]", expected "List[Union[str, int]]") \
+    return x # E: Incompatible return value type (got "list[str]", expected "list[Union[str, int]]") \
       # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
       # N: Consider using "Sequence" instead, which is covariant \
-      # N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]"
+      # N: Perhaps you need a type annotation for "x"? Suggestion: "list[Union[str, int]]"
 
 def g() -> Dict[str, Union[str, int]]:
     x = {'a': 'a'}
-    return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[str, Union[str, int]]") \
+    return x # E: Incompatible return value type (got "dict[str, str]", expected "dict[str, Union[str, int]]") \
       # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
       # N: Consider using "Mapping" instead, which is covariant in the value type \
-      # N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]"
+      # N: Perhaps you need a type annotation for "x"? Suggestion: "dict[str, Union[str, int]]"
 
 def h() -> Dict[Union[str, int], str]:
     x = {'a': 'a'}
-    return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[Union[str, int], str]") \
-# N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[Union[str, int], str]"
+    return x # E: Incompatible return value type (got "dict[str, str]", expected "dict[Union[str, int], str]") \
+# N: Perhaps you need a type annotation for "x"? Suggestion: "dict[Union[str, int], str]"
 
 def i() -> List[Union[int, float]]:
     x: List[int] = [1]
-    return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") \
+    return x # E: Incompatible return value type (got "list[int]", expected "list[Union[int, float]]") \
       # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
       # N: Consider using "Sequence" instead, which is covariant
 
@@ -2523,11 +2523,11 @@ def i() -> List[Union[int, float]]:
 from typing import Union, List
 def f() -> List[Union[int, float]]:
     x = ['a']
-    return x # E: Incompatible return value type (got "List[str]", expected "List[Union[int, float]]")
+    return x # E: Incompatible return value type (got "list[str]", expected "list[Union[int, float]]")
 
 def g() -> List[Union[str, int]]:
     x = ('a', 2)
-    return x # E: Incompatible return value type (got "Tuple[str, int]", expected "List[Union[str, int]]")
+    return x # E: Incompatible return value type (got "tuple[str, int]", expected "list[Union[str, int]]")
 
 [builtins fixtures/list.pyi]
 
@@ -2535,7 +2535,7 @@ def g() -> List[Union[str, int]]:
 from typing import Union, Dict, List
 def f() -> Dict[str, Union[str, int]]:
     x = {'a': 'a', 'b': 2}
-    return x # E: Incompatible return value type (got "Dict[str, object]", expected "Dict[str, Union[str, int]]")
+    return x # E: Incompatible return value type (got "dict[str, object]", expected "dict[str, Union[str, int]]")
 
 def g() -> Dict[str, Union[str, int]]:
     x: Dict[str, Union[str, int]] = {'a': 'a', 'b': 2}
@@ -2543,7 +2543,7 @@ def g() -> Dict[str, Union[str, int]]:
 
 def h() -> List[Union[str, int]]:
     x = ['a', 2]
-    return x # E: Incompatible return value type (got "List[object]", expected "List[Union[str, int]]")
+    return x # E: Incompatible return value type (got "list[object]", expected "list[Union[str, int]]")
 
 def i() -> List[Union[str, int]]:
     x: List[Union[str, int]] = ['a', 2]
diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test
index 08f82fe78d73..ebfddf7d9562 100644
--- a/test-data/unit/check-functools.test
+++ b/test-data/unit/check-functools.test
@@ -213,8 +213,8 @@ functools.partial(foo, 1, "a", "b", "c", d="a")  # E: Argument 3 to "foo" has in
 def bar(*a: bytes, **k: int):
     p1("a", 2, 3, 4, d="a", **k)
     p1("a", d="a", **k)
-    p1("a", **k)  # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str"
-    p1(**k)  # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str"
+    p1("a", **k)  # E: Argument 2 to "foo" has incompatible type "**dict[str, int]"; expected "str"
+    p1(**k)  # E: Argument 1 to "foo" has incompatible type "**dict[str, int]"; expected "str"
     p1(*a)  # E: Expected iterable as variadic argument
 
 
@@ -382,7 +382,7 @@ T = TypeVar("T")
 def generic(string: str, integer: int, resulting_type: Type[T]) -> T: ...
 
 p: partial[str] = partial(generic, resulting_type=str)
-q: partial[bool] = partial(generic, resulting_type=str)  # E: Argument "resulting_type" to "generic" has incompatible type "Type[str]"; expected "Type[bool]"
+q: partial[bool] = partial(generic, resulting_type=str)  # E: Argument "resulting_type" to "generic" has incompatible type "type[str]"; expected "type[bool]"
 
 pc: Callable[..., str] = partial(generic, resulting_type=str)
 qc: Callable[..., bool] = partial(generic, resulting_type=str)  # E: Incompatible types in assignment (expression has type "partial[str]", variable has type "Callable[..., bool]") \
@@ -531,7 +531,7 @@ reveal_type(first_kw(args=[1]))  # N: Revealed type is "builtins.int"
 # TODO: this is indeed invalid, but the error is incomprehensible.
 first_kw([1])  # E: Too many positional arguments for "get" \
                # E: Too few arguments for "get" \
-               # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int"
+               # E: Argument 1 to "get" has incompatible type "list[int]"; expected "int"
 [builtins fixtures/list.pyi]
 
 [case testFunctoolsPartialHigherOrder]
diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test
index 14c7738f48ae..678950a1e18b 100644
--- a/test-data/unit/check-generic-alias.test
+++ b/test-data/unit/check-generic-alias.test
@@ -57,14 +57,14 @@ reveal_type(t2)  # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(t3)  # N: Revealed type is "builtins.list[builtins.str]"
 reveal_type(t4)  # N: Revealed type is "builtins.tuple[Any, ...]"
 # TODO: ideally these would reveal builtins.tuple
-reveal_type(t5)  # N: Revealed type is "Tuple[builtins.int]"
-reveal_type(t6)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(t5)  # N: Revealed type is "tuple[builtins.int]"
+reveal_type(t6)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 # TODO: this is incorrect, see #9522
 reveal_type(t7)  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 reveal_type(t8)  # N: Revealed type is "builtins.dict[Any, Any]"
 reveal_type(t9)  # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
 reveal_type(t10)  # N: Revealed type is "builtins.type"
-reveal_type(t11)  # N: Revealed type is "Type[builtins.int]"
+reveal_type(t11)  # N: Revealed type is "type[builtins.int]"
 [builtins fixtures/dict.pyi]
 
 
@@ -184,11 +184,11 @@ t10: Tuple[int, ...] = t09
 A = tuple[int, ...]
 a: A = ()
 b: A = (1, 2, 3)
-c: A = ('x', 'y')  # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[int, ...]")
+c: A = ('x', 'y')  # E: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[int, ...]")
 
 B = tuple[int, str]
 x: B = (1, 'x')
-y: B = ('x', 1)  # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "Tuple[int, str]")
+y: B = ('x', 1)  # E: Incompatible types in assignment (expression has type "tuple[str, int]", variable has type "tuple[int, str]")
 
 reveal_type(tuple[int, ...]())  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/tuple.pyi]
@@ -196,7 +196,7 @@ reveal_type(tuple[int, ...]())  # N: Revealed type is "builtins.tuple[builtins.i
 [case testTypeAliasWithBuiltinTupleInStub]
 import m
 reveal_type(m.a)  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
-reveal_type(m.b)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(m.b)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [file m.pyi]
 A = tuple[int, ...]
@@ -210,7 +210,7 @@ import m
 reveal_type(m.a)  # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(m.b)  # N: Revealed type is "builtins.list[builtins.list[builtins.int]]"
 m.C  # has complex representation, ignored
-reveal_type(m.d)  # N: Revealed type is "Type[builtins.str]"
+reveal_type(m.d)  # N: Revealed type is "type[builtins.str]"
 
 [file m.pyi]
 A = list[int]
diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test
index 89465869f09d..f65ef3975852 100644
--- a/test-data/unit/check-generic-subtyping.test
+++ b/test-data/unit/check-generic-subtyping.test
@@ -279,9 +279,9 @@ class C(A):
 [out]
 main:11: error: Signature of "f" incompatible with supertype "A"
 main:11: note:      Superclass:
-main:11: note:          def [T, S] f(self, x: List[T], y: List[S]) -> None
+main:11: note:          def [T, S] f(self, x: list[T], y: list[S]) -> None
 main:11: note:      Subclass:
-main:11: note:          def [T] f(self, x: List[T], y: List[T]) -> None
+main:11: note:          def [T] f(self, x: list[T], y: list[T]) -> None
 
 [case testOverrideGenericMethodInNonGenericClassGeneralize]
 from typing import TypeVar
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index af2217e32b63..89693a6a7be0 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -458,7 +458,7 @@ import types
 a: A
 class A: pass
 a[A]()  # E: Value of type "A" is not indexable
-A[A]()  # E: The type "Type[A]" is not generic and not indexable
+A[A]()  # E: The type "type[A]" is not generic and not indexable
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
@@ -729,7 +729,7 @@ l.meth().append(1)
 reveal_type(l.meth()) # N: Revealed type is "builtins.list[builtins.int]"
 l.meth().append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int"
 
-ListedNode[str]([]).x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "List[str]")
+ListedNode[str]([]).x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[str]")
 
 [builtins fixtures/list.pyi]
 
@@ -751,10 +751,10 @@ def f_bad(x: T) -> D[T]:
     return D(1)  # Error, see out
 
 L[int]().append(Node((1, 1)))
-L[int]().append(5) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "Node[Tuple[int, int]]"
+L[int]().append(5) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "Node[tuple[int, int]]"
 
 x = D((1, 1)) # type: D[int]
-y = D(5) # type: D[int] # E: Argument 1 to "D" has incompatible type "int"; expected "Tuple[int, int]"
+y = D(5) # type: D[int] # E: Argument 1 to "D" has incompatible type "int"; expected "tuple[int, int]"
 
 def f(x: T) -> D[T]:
     return D((x, x))
@@ -762,7 +762,7 @@ reveal_type(f('a'))  # N: Revealed type is "__main__.D[builtins.str]"
 
 [builtins fixtures/list.pyi]
 [out]
-main:15: error: Argument 1 to "D" has incompatible type "int"; expected "Tuple[T, T]"
+main:15: error: Argument 1 to "D" has incompatible type "int"; expected "tuple[T, T]"
 
 [case testGenericTypeAliasesSubclassingBad]
 
@@ -838,8 +838,8 @@ reveal_type(x) # N: Revealed type is "builtins.int"
 def f2(x: IntTP[T]) -> IntTP[T]:
     return x
 
-f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, Never]"
-reveal_type(f2((1, 'x'))) # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "tuple[int, int, int]"; expected "tuple[int, Never]"
+reveal_type(f2((1, 'x'))) # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [builtins fixtures/for.pyi]
 
@@ -878,8 +878,8 @@ T = TypeVar('T', int, bool)
 Vec = List[Tuple[T, T]]
 
 vec = []  # type: Vec[bool]
-vec.append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "Tuple[bool, bool]"
-reveal_type(vec[0]) # N: Revealed type is "Tuple[builtins.bool, builtins.bool]"
+vec.append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "tuple[bool, bool]"
+reveal_type(vec[0]) # N: Revealed type is "tuple[builtins.bool, builtins.bool]"
 
 def fun1(v: Vec[T]) -> T:
     return v[0][0]
@@ -887,10 +887,10 @@ def fun2(v: Vec[T], scale: T) -> Vec[T]:
     return v
 
 reveal_type(fun1([(1, 1)])) # N: Revealed type is "builtins.int"
-fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "List[Tuple[bool, bool]]"
+fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "list[tuple[bool, bool]]"
 fun1([(1, 'x')]) # E: Cannot infer type argument 1 of "fun1"
 
-reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int]]"
+reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int]]"
 fun2([('x', 'x')], 'x') # E: Value of type variable "T" of "fun2" cannot be "str"
 
 [builtins fixtures/list.pyi]
@@ -903,7 +903,7 @@ T = TypeVar('T')
 n: TupledNode[int]
 n.x = 1
 n.y = (1, 1)
-n.y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, int]")
+n.y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, int]")
 
 def f(x: Node[T, T]) -> TupledNode[T]:
     return Node(x.x, (x.x, x.x))
@@ -935,7 +935,7 @@ def int_tf(m: int) -> Transform[int, str]:
     return transform
 
 var: Transform[int, str]
-reveal_type(var)  # N: Revealed type is "def (builtins.int, builtins.int) -> Tuple[builtins.int, builtins.str]"
+reveal_type(var)  # N: Revealed type is "def (builtins.int, builtins.int) -> tuple[builtins.int, builtins.str]"
 [file lib.py]
 from typing import Callable, TypeVar, Tuple
 
@@ -966,9 +966,9 @@ NewAlias = Alias[int, int, S, S]
 class C: pass
 
 x: NewAlias[str]
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, builtins.str, builtins.str]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, builtins.str, builtins.str]]"
 y: Alias[int, str, C, C]
-reveal_type(y)  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, __main__.C, __main__.C]]"
+reveal_type(y)  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, __main__.C, __main__.C]]"
 
 [file mod.py]
 from typing import TypeVar, List, Tuple
@@ -1223,7 +1223,7 @@ class C(A[S, B[T, int]], B[U, A[int, T]]):
     pass
 
 c = C[object, int, str]()
-reveal_type(c.m()) # N: Revealed type is "Tuple[builtins.str, __main__.A[builtins.int, builtins.int]]"
+reveal_type(c.m()) # N: Revealed type is "tuple[builtins.str, __main__.A[builtins.int, builtins.int]]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -1770,7 +1770,7 @@ T = TypeVar('T')
 class C(Generic[T]):
     def __init__(self) -> None: pass
 x = C # type: Callable[[], C[int]]
-y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "Type[C[T]]", variable has type "Callable[[], int]")
+y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "type[C[T]]", variable has type "Callable[[], int]")
 
 -- Special cases
 -- -------------
@@ -1964,8 +1964,8 @@ class C(Generic[T]):
 class D(C[Tuple[T, T]]): ...
 class E(D[str]): ...
 
-reveal_type(E.get())  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
-reveal_type(E().get())  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(E.get())  # N: Revealed type is "tuple[builtins.str, builtins.str]"
+reveal_type(E().get())  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 [builtins fixtures/classmethod.pyi]
 
 [case testGenericClassMethodExpansionReplacingTypeVar]
@@ -2013,10 +2013,10 @@ class C(Generic[T]):
 class D(C[Tuple[T, S]]): ...
 class E(D[S, str]): ...
 
-reveal_type(D.make_one)  # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2]) -> __main__.C[Tuple[T`1, S`2]]"
-reveal_type(D[int, str].make_one)  # N: Revealed type is "def (x: Tuple[builtins.int, builtins.str]) -> __main__.C[Tuple[builtins.int, builtins.str]]"
-reveal_type(E.make_one)  # N: Revealed type is "def [S] (x: Tuple[S`1, builtins.str]) -> __main__.C[Tuple[S`1, builtins.str]]"
-reveal_type(E[int].make_one)  # N: Revealed type is "def (x: Tuple[builtins.int, builtins.str]) -> __main__.C[Tuple[builtins.int, builtins.str]]"
+reveal_type(D.make_one)  # N: Revealed type is "def [T, S] (x: tuple[T`1, S`2]) -> __main__.C[tuple[T`1, S`2]]"
+reveal_type(D[int, str].make_one)  # N: Revealed type is "def (x: tuple[builtins.int, builtins.str]) -> __main__.C[tuple[builtins.int, builtins.str]]"
+reveal_type(E.make_one)  # N: Revealed type is "def [S] (x: tuple[S`1, builtins.str]) -> __main__.C[tuple[S`1, builtins.str]]"
+reveal_type(E[int].make_one)  # N: Revealed type is "def (x: tuple[builtins.int, builtins.str]) -> __main__.C[tuple[builtins.int, builtins.str]]"
 [builtins fixtures/classmethod.pyi]
 
 [case testGenericClassClsNonGeneric]
@@ -2161,7 +2161,7 @@ class Sub(Base[str]): ...
 Sub.make_some(1)  # E: No overload variant of "make_some" of "Base" matches argument type "int" \
                   # N: Possible overload variants: \
                   # N:     def make_some(cls, item: str) -> Sub \
-                  # N:     def make_some(cls, item: str, n: int) -> Tuple[Sub, ...]
+                  # N:     def make_some(cls, item: str, n: int) -> tuple[Sub, ...]
 [builtins fixtures/classmethod.pyi]
 
 [case testNoGenericAccessOnImplicitAttributes]
@@ -2191,11 +2191,11 @@ class A(Generic[T]):
 
 class B(A[T], Generic[T, S]):
     def meth(self) -> None:
-        reveal_type(A[T].foo)  # N: Revealed type is "def () -> Tuple[T`1, __main__.A[T`1]]"
+        reveal_type(A[T].foo)  # N: Revealed type is "def () -> tuple[T`1, __main__.A[T`1]]"
     @classmethod
     def other(cls) -> None:
-        reveal_type(cls.foo)  # N: Revealed type is "def () -> Tuple[T`1, __main__.B[T`1, S`2]]"
-reveal_type(B.foo)  # N: Revealed type is "def [T, S] () -> Tuple[T`1, __main__.B[T`1, S`2]]"
+        reveal_type(cls.foo)  # N: Revealed type is "def () -> tuple[T`1, __main__.B[T`1, S`2]]"
+reveal_type(B.foo)  # N: Revealed type is "def [T, S] () -> tuple[T`1, __main__.B[T`1, S`2]]"
 [builtins fixtures/classmethod.pyi]
 
 [case testGenericClassAlternativeConstructorPrecise2]
@@ -2211,7 +2211,7 @@ class Base(Generic[T]):
 class Sub(Base[T]):
     ...
 
-reveal_type(Sub.make_pair('yes'))  # N: Revealed type is "Tuple[__main__.Sub[builtins.str], __main__.Sub[builtins.str]]"
+reveal_type(Sub.make_pair('yes'))  # N: Revealed type is "tuple[__main__.Sub[builtins.str], __main__.Sub[builtins.str]]"
 Sub[int].make_pair('no')  # E: Argument 1 to "make_pair" of "Base" has incompatible type "str"; expected "int"
 [builtins fixtures/classmethod.pyi]
 
@@ -3029,7 +3029,7 @@ def dec(f: Callable[[T], S], g: Callable[[T], U]) -> Callable[[T], Tuple[S, U]]:
 def id(x: V) -> V:
     ...
 
-reveal_type(dec(id, id))  # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]"
+reveal_type(dec(id, id))  # N: Revealed type is "def [T] (T`1) -> tuple[T`1, T`1]"
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericSecondary]
@@ -3123,7 +3123,7 @@ reveal_type(dec1(lambda x: 1))  # N: Revealed type is "def (builtins.int) -> bui
 reveal_type(dec5(lambda x: x))  # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
 reveal_type(dec3(lambda x: x))  # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]"
 reveal_type(dec4(lambda x: x))  # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24"
-dec4_bound(lambda x: x)  # E: Value of type variable "I" of "dec4_bound" cannot be "List[T]"
+dec4_bound(lambda x: x)  # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecBasicInList]
@@ -3142,7 +3142,7 @@ def either(x: U, y: U) -> U: ...
 def pair(x: U, y: V) -> Tuple[U, V]: ...
 reveal_type(dec(id))  # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (x: T`5, y: T`5) -> builtins.list[T`5]"
-reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[Tuple[U`-1, V`-2]]"
+reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[tuple[U`-1, V`-2]]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecBasicDeList]
@@ -3179,7 +3179,7 @@ def either(x: U, y: U) -> U: ...
 def pair(x: U, y: V) -> Tuple[U, V]: ...
 reveal_type(dec(id))  # N: Revealed type is "def () -> def [T] (T`2) -> T`2"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (y: T`5) -> def (T`5) -> T`5"
-reveal_type(dec(pair))  # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]"
+reveal_type(dec(pair))  # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`8) -> tuple[T`8, V`-2]"
 reveal_type(dec(dec))  # N: Revealed type is "def () -> def [T, P, S] (def (T`-1, *P.args, **P.kwargs) -> S`-3) -> def (*P.args, **P.kwargs) -> def (T`-1) -> S`-3"
 [builtins fixtures/list.pyi]
 
@@ -3200,7 +3200,7 @@ def either(x: U) -> Callable[[U], U]: ...
 def pair(x: U) -> Callable[[V], Tuple[V, U]]: ...
 reveal_type(dec(id))  # N: Revealed type is "def [T] (T`3) -> T`3"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (T`6, x: T`6) -> T`6"
-reveal_type(dec(pair))  # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> Tuple[T`9, U`-1]"
+reveal_type(dec(pair))  # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> tuple[T`9, U`-1]"
 # This is counter-intuitive but looks correct, dec matches itself only if P can be empty
 reveal_type(dec(dec))  # N: Revealed type is "def [T, S] (T`13, f: def () -> def (T`13) -> S`14) -> S`14"
 [builtins fixtures/list.pyi]
@@ -3337,7 +3337,7 @@ def pair(x: U, y: V) -> Tuple[U, V]: ...
 
 reveal_type(dec(id))  # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (T`5, T`5) -> builtins.list[T`5]"
-reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]"
+reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[tuple[U`-1, V`-2]]"
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericVariadicBasicDeList]
@@ -3376,7 +3376,7 @@ def pair(x: U, y: V) -> Tuple[U, V]: ...
 
 reveal_type(dec(id))  # N: Revealed type is "def () -> def [T] (T`2) -> T`2"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (T`5) -> def (T`5) -> T`5"
-reveal_type(dec(pair))  # N: Revealed type is "def [V] (V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]"
+reveal_type(dec(pair))  # N: Revealed type is "def [V] (V`-2) -> def [T] (T`8) -> tuple[T`8, V`-2]"
 reveal_type(dec(dec))  # N: Revealed type is "def () -> def [T, Ts, S] (def (T`-1, *Unpack[Ts`-2]) -> S`-3) -> def (*Unpack[Ts`-2]) -> def (T`-1) -> S`-3"
 [builtins fixtures/list.pyi]
 
@@ -3398,7 +3398,7 @@ def pair(x: U) -> Callable[[V], Tuple[V, U]]: ...
 
 reveal_type(dec(id))  # N: Revealed type is "def [T] (T`3) -> T`3"
 reveal_type(dec(either))  # N: Revealed type is "def [T] (T`6, T`6) -> T`6"
-reveal_type(dec(pair))  # N: Revealed type is "def [T, U] (T`9, U`-1) -> Tuple[T`9, U`-1]"
+reveal_type(dec(pair))  # N: Revealed type is "def [T, U] (T`9, U`-1) -> tuple[T`9, U`-1]"
 # This is counter-intuitive but looks correct, dec matches itself only if Ts is empty
 reveal_type(dec(dec))  # N: Revealed type is "def [T, S] (T`13, def () -> def (T`13) -> S`14) -> S`14"
 [builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 9d5902246ae5..a8116d9cf78a 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -3517,7 +3517,7 @@ class M(type):
     y: int
 [out]
 [out2]
-tmp/a.py:2: error: "Type[B]" has no attribute "x"
+tmp/a.py:2: error: "type[B]" has no attribute "x"
 
 [case testIncrementalLotsOfInheritance]
 import a
@@ -4654,7 +4654,7 @@ B = TypedDict('B', {'x': A})
 [typing fixtures/typing-typeddict.pyi]
 [out]
 [out2]
-tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]"
+tmp/a.py:3: note: Revealed type is "tuple[TypedDict('other.B', {'x': tuple[..., fallback=lib.A]}), fallback=lib.A]"
 
 [case testFollowImportSkipNotInvalidatedOnPresent]
 # flags: --follow-imports=skip
@@ -5123,7 +5123,7 @@ NT = NamedTuple('BadName', [('x', int)])
 tmp/b.py:2: error: First argument to namedtuple() should be "NT", not "BadName"
 [out2]
 tmp/b.py:2: error: First argument to namedtuple() should be "NT", not "BadName"
-tmp/a.py:3: note: Revealed type is "Tuple[builtins.int, fallback=b.NT]"
+tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.NT]"
 
 [case testNewAnalyzerIncrementalBrokenNamedTupleNested]
 
@@ -5164,7 +5164,7 @@ class C:
 [builtins fixtures/tuple.pyi]
 [out]
 [out2]
-tmp/a.py:3: note: Revealed type is "Tuple[builtins.int, fallback=b.C.Hidden@5]"
+tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.C.Hidden@5]"
 
 [case testIncrementalNodeCreatedFromGetattr]
 import a
@@ -5314,7 +5314,7 @@ reveal_type(Foo().x)
 [builtins fixtures/isinstance.pyi]
 [out]
 [out2]
-tmp/b.py:2: note: Revealed type is "a."
+tmp/b.py:2: note: Revealed type is "a."
 
 [case testIsInstanceAdHocIntersectionIncrementalIsInstanceChange]
 import c
@@ -5845,9 +5845,9 @@ reveal_type(a.n)
 [out]
 [out2]
 [out3]
-tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
-tmp/c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+tmp/c.py:7: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 
 [case testTupleTypeUpdateNonRecursiveToRecursiveCoarse]
 import c
@@ -5878,7 +5878,7 @@ def f(x: a.N) -> None:
 [out]
 [out2]
 [out3]
-tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
 
 [case testTypeAliasUpdateNonRecursiveToRecursiveCoarse]
@@ -5910,7 +5910,7 @@ def f(x: a.N) -> None:
 [out]
 [out2]
 [out3]
-tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int], None], builtins.int]"
+tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int], None], builtins.int]"
 tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
 
 [case testTypedDictUpdateNonRecursiveToRecursiveCoarse]
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index 20f534d60978..0aa67b2bf7f3 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -372,7 +372,7 @@ ao: List[object]
 a: A
 def f(): a, aa, ao # Prevent redefinition
 
-a = [] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A")
+a = [] # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "A")
 
 aa = []
 ao = []
@@ -424,7 +424,7 @@ class B(A): pass
 [case testLocalVariableInferenceFromEmptyList]
 import typing
 def f() -> None:
-    a = []     # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []     # E: Need type annotation for "a" (hint: "a: list[] = ...")
     b = [None]
     c = [B()]
     if int():
@@ -437,14 +437,14 @@ class B: pass
 [case testNestedListExpressions]
 # flags: --no-strict-optional
 from typing import List
-aao = None # type: List[List[object]]
-aab = None # type: List[List[B]]
-ab = None # type: List[B]
+aao = None # type: list[list[object]]
+aab = None # type: list[list[B]]
+ab = None # type: list[B]
 b = None # type: B
 o = None # type: object
 def f(): aao, aab # Prevent redefinition
 
-aao = [[o], ab] # E: List item 1 has incompatible type "List[B]"; expected "List[object]"
+aao = [[o], ab] # E: List item 1 has incompatible type "list[B]"; expected "list[object]"
 aab = [[], [o]] # E: List item 0 has incompatible type "object"; expected "B"
 
 aao = [[None], [b], [], [o]]
@@ -733,7 +733,7 @@ class B: pass
 
 m = map(g, [A()])
 b = m # type: List[B]
-a = m # type: List[A] # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]")
+a = m # type: List[A] # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]")
 [builtins fixtures/list.pyi]
 
 
@@ -756,9 +756,9 @@ if int():
 if int():
     b = b or [C()]
 if int():
-    a = a or b # E: Incompatible types in assignment (expression has type "Union[List[A], List[B]]", variable has type "List[A]")
+    a = a or b # E: Incompatible types in assignment (expression has type "Union[list[A], list[B]]", variable has type "list[A]")
 if int():
-    b = b or c # E: Incompatible types in assignment (expression has type "Union[List[B], List[C]]", variable has type "List[B]")
+    b = b or c # E: Incompatible types in assignment (expression has type "Union[list[B], list[C]]", variable has type "list[B]")
 [builtins fixtures/list.pyi]
 
 
@@ -814,7 +814,7 @@ s: List[str]
 if int():
     i = i = []
 if int():
-    i = s = [] # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]")
+    i = s = [] # E: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int]")
 [builtins fixtures/list.pyi]
 
 [case testContextForAttributeDeclaredInInit]
@@ -842,7 +842,7 @@ T = TypeVar('T')
 def f(x: Union[List[T], str]) -> None: pass
 f([1])
 f('')
-f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[List[Never], str]"
+f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[list[Never], str]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIgnoringInferenceContext]
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 25565946158e..a98597e6e320 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -275,14 +275,14 @@ from typing import Type
 
 class Foo: ...
 A: Type[Foo] = Foo
-a, b = Foo  # E: "Type[Foo]" object is not iterable
-c, d = A  # E: "Type[Foo]" object is not iterable
+a, b = Foo  # E: "type[Foo]" object is not iterable
+c, d = A  # E: "type[Foo]" object is not iterable
 
 class Meta(type): ...
 class Bar(metaclass=Meta): ...
 B: Type[Bar] = Bar
-e, f = Bar  # E: "Type[Bar]" object is not iterable
-g, h = B  # E: "Type[Bar]" object is not iterable
+e, f = Bar  # E: "type[Bar]" object is not iterable
+g, h = B  # E: "type[Bar]" object is not iterable
 
 reveal_type(a)  # E: Cannot determine type of "a"  # N: Revealed type is "Any"
 reveal_type(b)  # E: Cannot determine type of "b"  # N: Revealed type is "Any"
@@ -330,8 +330,8 @@ a, b, c = Foo
 d, e, f = A
 g, h, i = B
 j, k, l = C
-m, n, o = D  # E: "Type[Baz]" object is not iterable
-p, q, r = E  # E: "Type[Spam]" object is not iterable
+m, n, o = D  # E: "type[Baz]" object is not iterable
+p, q, r = E  # E: "type[Spam]" object is not iterable
 s, t, u = Eggs
 v, w, x = F
 y, z, aa = G
@@ -553,7 +553,7 @@ if int():
     b = id(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B")
     a = id(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A")
 if int():
-    a = id(c) # E: Incompatible types in assignment (expression has type "Tuple[A, object]", variable has type "A")
+    a = id(c) # E: Incompatible types in assignment (expression has type "tuple[A, object]", variable has type "A")
 
 if int():
     a = id(a)
@@ -843,7 +843,7 @@ if int():
     l = [A()]
 lb = [b]
 if int():
-    l = lb # E: Incompatible types in assignment (expression has type "List[bool]", variable has type "List[A]")
+    l = lb # E: Incompatible types in assignment (expression has type "list[bool]", variable has type "list[A]")
 [builtins fixtures/for.pyi]
 
 [case testGenericFunctionWithTypeTypeAsCallable]
@@ -871,15 +871,15 @@ f(1, 1)() # E: "int" not callable
 
 def g(x: Union[T, List[T]]) -> List[T]: pass
 def h(x: List[str]) -> None: pass
-g('a')() # E: "List[str]" not callable
+g('a')() # E: "list[str]" not callable
 
 # The next line is a case where there are multiple ways to satisfy a constraint
-# involving a Union. Either T = List[str] or T = str would turn out to be valid,
+# involving a Union. Either T = list[str] or T = str would turn out to be valid,
 # but mypy doesn't know how to branch on these two options (and potentially have
 # to backtrack later) and defaults to T = Never. The result is an
 # awkward error message. Either a better error message, or simply accepting the
 # call, would be preferable here.
-g(['a']) # E: Argument 1 to "g" has incompatible type "List[str]"; expected "List[Never]"
+g(['a']) # E: Argument 1 to "g" has incompatible type "list[str]"; expected "list[Never]"
 
 h(g(['a']))
 
@@ -888,7 +888,7 @@ a = [1]
 b = ['b']
 i(a, a, b)
 i(b, a, b)
-i(a, b, b) # E: Argument 1 to "i" has incompatible type "List[int]"; expected "List[str]"
+i(a, b, b) # E: Argument 1 to "i" has incompatible type "list[int]"; expected "list[str]"
 [builtins fixtures/list.pyi]
 
 [case testCallableListJoinInference]
@@ -972,7 +972,7 @@ from typing import TypeVar, Union, List
 T = TypeVar('T')
 def f() -> List[T]: pass
 d1 = f() # type: Union[List[int], str]
-d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "Union[int, str]")
+d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "Union[int, str]")
 def g(x: T) -> List[T]: pass
 d3 = g(1) # type: Union[List[int], List[str]]
 [builtins fixtures/list.pyi]
@@ -988,7 +988,7 @@ a = k2
 if int():
     a = k2
 if int():
-    a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T@k1]], List[Union[T@k1, int]]]", variable has type "Callable[[S, List[T@k2]], List[Union[T@k2, int]]]")
+    a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, list[T@k1]], list[Union[T@k1, int]]]", variable has type "Callable[[S, list[T@k2]], list[Union[T@k2, int]]]")
 b = k1
 if int():
     b = k1
@@ -1041,7 +1041,7 @@ d = {a:b}
 if int():
     d = d_ab()
 if int():
-    d = d_aa() # E: Incompatible types in assignment (expression has type "Dict[A, A]", variable has type "Dict[A, B]")
+    d = d_aa() # E: Incompatible types in assignment (expression has type "dict[A, A]", variable has type "dict[A, B]")
 [builtins fixtures/dict.pyi]
 
 [case testSetLiteral]
@@ -1056,7 +1056,7 @@ if int():
 if int():
     s = s_i()
 if int():
-    s = s_s() # E: Incompatible types in assignment (expression has type "Set[str]", variable has type "Set[int]")
+    s = s_s() # E: Incompatible types in assignment (expression has type "set[str]", variable has type "set[int]")
 [builtins fixtures/set.pyi]
 
 [case testSetWithStarExpr]
@@ -1391,14 +1391,14 @@ from typing import List, Callable
 li = [1]
 l = lambda: li
 f1 = l # type: Callable[[], List[int]]
-f2 = l # type: Callable[[], List[str]] # E: Incompatible types in assignment (expression has type "Callable[[], List[int]]", variable has type "Callable[[], List[str]]")
+f2 = l # type: Callable[[], List[str]] # E: Incompatible types in assignment (expression has type "Callable[[], list[int]]", variable has type "Callable[[], list[str]]")
 [builtins fixtures/list.pyi]
 
 [case testInferLambdaType2]
 from typing import List, Callable
 l = lambda: [B()]
 f1 = l # type: Callable[[], List[B]]
-f2 = l # type: Callable[[], List[A]] # E: Incompatible types in assignment (expression has type "Callable[[], List[B]]", variable has type "Callable[[], List[A]]")
+f2 = l # type: Callable[[], List[A]] # E: Incompatible types in assignment (expression has type "Callable[[], list[B]]", variable has type "Callable[[], list[A]]")
 
 class A: pass
 class B: pass
@@ -1491,7 +1491,7 @@ o: List[object]
 a2 = a or []
 if int():
     a = a2
-    a2 = o # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[A]")
+    a2 = o # E: Incompatible types in assignment (expression has type "list[object]", variable has type "list[A]")
 class A: pass
 [builtins fixtures/list.pyi]
 
@@ -1535,7 +1535,7 @@ if int():
     a = x2
 if int():
     a = x3 \
-     # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \
+     # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") \
      # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/list.pyi]
@@ -1558,7 +1558,7 @@ if int():
     a = x2
 if int():
     a = x3 \
-     # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \
+     # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") \
      # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/list.pyi]
@@ -1582,28 +1582,28 @@ a.append(0)  # E: Argument 1 to "append" of "list" has incompatible type "int";
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndNotAnnotated]
-a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndReadBeforeAppend]
-a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
 if a: pass
-a.xyz  # E: "List[Any]" has no attribute "xyz"
+a.xyz  # E: "list[Any]" has no attribute "xyz"
 a.append('')
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndIncompleteTypeInAppend]
-a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...")
+a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...")
 a.append([])
-a()  # E: "List[Any]" not callable
+a()  # E: "list[Any]" not callable
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndMultipleAssignment]
 a, b = [], []
 a.append(1)
 b.append('')
-a() # E: "List[int]" not callable
-b() # E: "List[str]" not callable
+a() # E: "list[int]" not callable
+b() # E: "list[str]" not callable
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyInFunction]
@@ -1615,7 +1615,7 @@ def f() -> None:
 
 [case testInferListInitializedToEmptyAndNotAnnotatedInFunction]
 def f() -> None:
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
 
 def g() -> None: pass
 
@@ -1625,9 +1625,9 @@ a.append(1)
 
 [case testInferListInitializedToEmptyAndReadBeforeAppendInFunction]
 def f() -> None:
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     if a: pass
-    a.xyz  # E: "List[Any]" has no attribute "xyz"
+    a.xyz  # E: "list[Any]" has no attribute "xyz"
     a.append('')
 [builtins fixtures/list.pyi]
 
@@ -1640,7 +1640,7 @@ class A:
 
 [case testInferListInitializedToEmptyAndNotAnnotatedInClassBody]
 class A:
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
 
 class B:
     a = []
@@ -1658,7 +1658,7 @@ class A:
 [case testInferListInitializedToEmptyAndNotAnnotatedInMethod]
 class A:
     def f(self) -> None:
-        a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+        a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyInMethodViaAttribute]
@@ -1675,7 +1675,7 @@ from typing import List
 
 class A:
     def __init__(self) -> None:
-        self.x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...")
+        self.x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...")
 
 class B(A):
     @property
@@ -1704,27 +1704,27 @@ a.add('')  # E: Argument 1 to "add" of "set" has incompatible type "str"; expect
 [case testInferDictInitializedToEmpty]
 a = {}
 a[1] = ''
-a() # E: "Dict[int, str]" not callable
+a() # E: "dict[int, str]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testInferDictInitializedToEmptyUsingUpdate]
 a = {}
 a.update({'': 42})
-a() # E: "Dict[str, int]" not callable
+a() # E: "dict[str, int]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testInferDictInitializedToEmptyUsingUpdateError]
-a = {}  # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
-a.update([1, 2])  # E: Argument 1 to "update" of "dict" has incompatible type "List[int]"; expected "SupportsKeysAndGetItem[Any, Any]" \
+a = {}  # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
+a.update([1, 2])  # E: Argument 1 to "update" of "dict" has incompatible type "list[int]"; expected "SupportsKeysAndGetItem[Any, Any]" \
                   # N: "list" is missing following "SupportsKeysAndGetItem" protocol member: \
                   # N:     keys
-a()  # E: "Dict[Any, Any]" not callable
+a()  # E: "dict[Any, Any]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testInferDictInitializedToEmptyAndIncompleteTypeInUpdate]
-a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
 a[1] = {}
-b = {} # E: Need type annotation for "b" (hint: "b: Dict[, ] = ...")
+b = {} # E: Need type annotation for "b" (hint: "b: dict[, ] = ...")
 b[{}] = 1
 [builtins fixtures/dict.pyi]
 
@@ -1754,8 +1754,8 @@ def f(blocks: object):
     to_process = []
     to_process = list(blocks) # E: No overload variant of "list" matches argument type "object" \
                               # N: Possible overload variants: \
-                              # N:     def [T] __init__(self) -> List[T] \
-                              # N:     def [T] __init__(self, x: Iterable[T]) -> List[T]
+                              # N:     def [T] __init__(self) -> list[T] \
+                              # N:     def [T] __init__(self, x: Iterable[T]) -> list[T]
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndAssigned]
@@ -1776,9 +1776,9 @@ if bool():
     d = {1: 'x'}
 reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
 
-dd = {} # E: Need type annotation for "dd" (hint: "dd: Dict[, ] = ...")
+dd = {} # E: Need type annotation for "dd" (hint: "dd: dict[, ] = ...")
 if bool():
-    dd = [1] # E: Incompatible types in assignment (expression has type "List[int]", variable has type "Dict[Any, Any]")
+    dd = [1] # E: Incompatible types in assignment (expression has type "list[int]", variable has type "dict[Any, Any]")
 reveal_type(dd) # N: Revealed type is "builtins.dict[Any, Any]"
 [builtins fixtures/dict.pyi]
 
@@ -1796,27 +1796,27 @@ reveal_type(oo) # N: Revealed type is "collections.OrderedDict[builtins.int, bui
 [builtins fixtures/dict.pyi]
 
 [case testEmptyCollectionAssignedToVariableTwiceIncremental]
-x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...")
+x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...")
 y = x
 x = []
 reveal_type(x) # N: Revealed type is "builtins.list[Any]"
-d = {} # E: Need type annotation for "d" (hint: "d: Dict[, ] = ...")
+d = {} # E: Need type annotation for "d" (hint: "d: dict[, ] = ...")
 z = d
 d = {}
 reveal_type(d) # N: Revealed type is "builtins.dict[Any, Any]"
 [builtins fixtures/dict.pyi]
 [out2]
-main:1: error: Need type annotation for "x" (hint: "x: List[] = ...")
+main:1: error: Need type annotation for "x" (hint: "x: list[] = ...")
 main:4: note: Revealed type is "builtins.list[Any]"
-main:5: error: Need type annotation for "d" (hint: "d: Dict[, ] = ...")
+main:5: error: Need type annotation for "d" (hint: "d: dict[, ] = ...")
 main:8: note: Revealed type is "builtins.dict[Any, Any]"
 
 [case testEmptyCollectionAssignedToVariableTwiceNoReadIncremental]
-x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...")
+x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...")
 x = []
 [builtins fixtures/list.pyi]
 [out2]
-main:1: error: Need type annotation for "x" (hint: "x: List[] = ...")
+main:1: error: Need type annotation for "x" (hint: "x: list[] = ...")
 
 [case testInferAttributeInitializedToEmptyAndAssigned]
 class C:
@@ -1856,7 +1856,7 @@ reveal_type(C().a)  # N: Revealed type is "Union[builtins.int, None]"
 [case testInferAttributeInitializedToEmptyNonSelf]
 class C:
     def __init__(self) -> None:
-        self.a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+        self.a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
         if bool():
             a = self
             a.a = [1]
@@ -1867,7 +1867,7 @@ reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
 [case testInferAttributeInitializedToEmptyAndAssignedOtherMethod]
 class C:
     def __init__(self) -> None:
-        self.a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+        self.a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     def meth(self) -> None:
         self.a = [1]
 reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
@@ -1876,7 +1876,7 @@ reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
 [case testInferAttributeInitializedToEmptyAndAppendedOtherMethod]
 class C:
     def __init__(self) -> None:
-        self.a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+        self.a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     def meth(self) -> None:
         self.a.append(1)
 reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
@@ -1885,7 +1885,7 @@ reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
 [case testInferAttributeInitializedToEmptyAndAssignedItemOtherMethod]
 class C:
     def __init__(self) -> None:
-        self.a = {}  # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+        self.a = {}  # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
     def meth(self) -> None:
         self.a[0] = 'yes'
 reveal_type(C().a)  # N: Revealed type is "builtins.dict[Any, Any]"
@@ -1901,7 +1901,7 @@ reveal_type(C().a)  # N: Revealed type is "None"
 
 [case testInferAttributeInitializedToEmptyAndAssignedClassBody]
 class C:
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     def __init__(self) -> None:
         self.a = [1]
 reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
@@ -1909,7 +1909,7 @@ reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
 
 [case testInferAttributeInitializedToEmptyAndAppendedClassBody]
 class C:
-    a = []  # E: Need type annotation for "a" (hint: "a: List[] = ...")
+    a = []  # E: Need type annotation for "a" (hint: "a: list[] = ...")
     def __init__(self) -> None:
         self.a.append(1)
 reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
@@ -1917,7 +1917,7 @@ reveal_type(C().a)  # N: Revealed type is "builtins.list[Any]"
 
 [case testInferAttributeInitializedToEmptyAndAssignedItemClassBody]
 class C:
-    a = {}  # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+    a = {}  # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
     def __init__(self) -> None:
         self.a[0] = 'yes'
 reveal_type(C().a)  # N: Revealed type is "builtins.dict[Any, Any]"
@@ -2042,7 +2042,7 @@ x.append('') # E: Argument 1 to "append" of "list" has incompatible type "str";
 x = None
 if object():
     # Promote from partial None to partial list.
-    x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+    x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
     x
 [builtins fixtures/list.pyi]
 
@@ -2051,7 +2051,7 @@ def f() -> None:
     x = None
     if object():
         # Promote from partial None to partial list.
-        x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+        x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 [builtins fixtures/list.pyi]
 [out]
 
@@ -2131,7 +2131,7 @@ class A:
 [case testPartialTypeErrorSpecialCase2]
 # This used to crash.
 class A:
-    x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+    x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
     def f(self) -> None:
         for a in self.x:
             pass
@@ -2257,7 +2257,7 @@ def g(d: Dict[str, int]) -> None: pass
 def f() -> None:
     x = {}
     x[1] = y
-    g(x) # E: Argument 1 to "g" has incompatible type "Dict[int, str]"; expected "Dict[str, int]"
+    g(x) # E: Argument 1 to "g" has incompatible type "dict[int, str]"; expected "dict[str, int]"
     x[1] = 1 # E: Incompatible types in assignment (expression has type "int", target has type "str")
     x[1] = ''
 y = ''
@@ -2271,7 +2271,7 @@ def f() -> None:
     x = {}
     y
     x[1] = 1
-    g(x) # E: Argument 1 to "g" has incompatible type "Dict[int, int]"; expected "Dict[str, int]"
+    g(x) # E: Argument 1 to "g" has incompatible type "dict[int, int]"; expected "dict[str, int]"
 y = ''
 [builtins fixtures/dict.pyi]
 [out]
@@ -2290,7 +2290,7 @@ def f() -> None:
     y = o
     x = []
     x.append(y)
-    x() # E: "List[int]" not callable
+    x() # E: "list[int]" not callable
 o = 1
 [builtins fixtures/list.pyi]
 [out]
@@ -2300,16 +2300,16 @@ def f() -> None:
     y = o
     x = {}
     x[''] = y
-    x() # E: "Dict[str, int]" not callable
+    x() # E: "dict[str, int]" not callable
 o = 1
 [builtins fixtures/dict.pyi]
 [out]
 
 [case testMultipassAndPartialTypesSpecialCase3]
 def f() -> None:
-    x = {} # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+    x = {} # E: Need type annotation for "x" (hint: "x: dict[, ] = ...")
     y = o
-    z = {} # E: Need type annotation for "z" (hint: "z: Dict[, ] = ...")
+    z = {} # E: Need type annotation for "z" (hint: "z: dict[, ] = ...")
 o = 1
 [builtins fixtures/dict.pyi]
 [out]
@@ -2390,7 +2390,7 @@ b: Union[str, tuple]
 def f(): pass
 def g(x: Union[int, str]): pass
 c = a if f() else b
-g(c) # E: Argument 1 to "g" has incompatible type "Union[int, str, Tuple[Any, ...]]"; expected "Union[int, str]"
+g(c) # E: Argument 1 to "g" has incompatible type "Union[int, str, tuple[Any, ...]]"; expected "Union[int, str]"
 [builtins fixtures/tuple.pyi]
 
 [case testUnificationMultipleInheritance]
@@ -2429,58 +2429,58 @@ a2.foo2()
 [case testUnificationEmptyListLeft]
 def f(): pass
 a = [] if f() else [0]
-a() # E: "List[int]" not callable
+a() # E: "list[int]" not callable
 [builtins fixtures/list.pyi]
 
 [case testUnificationEmptyListRight]
 def f(): pass
 a = [0] if f() else []
-a() # E: "List[int]" not callable
+a() # E: "list[int]" not callable
 [builtins fixtures/list.pyi]
 
 [case testUnificationEmptyListLeftInContext]
 from typing import List
 def f(): pass
-a = [] if f() else [0] # type: List[int]
-a() # E: "List[int]" not callable
+a = [] if f() else [0] # type: list[int]
+a() # E: "list[int]" not callable
 [builtins fixtures/list.pyi]
 
 [case testUnificationEmptyListRightInContext]
 # TODO Find an example that really needs the context
 from typing import List
 def f(): pass
-a = [0] if f() else [] # type: List[int]
-a() # E: "List[int]" not callable
+a = [0] if f() else [] # type: list[int]
+a() # E: "list[int]" not callable
 [builtins fixtures/list.pyi]
 
 [case testUnificationEmptySetLeft]
 def f(): pass
 a = set() if f() else {0}
-a() # E: "Set[int]" not callable
+a() # E: "set[int]" not callable
 [builtins fixtures/set.pyi]
 
 [case testUnificationEmptyDictLeft]
 def f(): pass
 a = {} if f() else {0: 0}
-a() # E: "Dict[int, int]" not callable
+a() # E: "dict[int, int]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testUnificationEmptyDictRight]
 def f(): pass
 a = {0: 0} if f() else {}
-a() # E: "Dict[int, int]" not callable
+a() # E: "dict[int, int]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testUnificationDictWithEmptyListLeft]
 def f(): pass
 a = {0: []} if f() else {0: [0]}
-a() # E: "Dict[int, List[int]]" not callable
+a() # E: "dict[int, list[int]]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testUnificationDictWithEmptyListRight]
 def f(): pass
 a = {0: [0]} if f() else {0: []}
-a() # E: "Dict[int, List[int]]" not callable
+a() # E: "dict[int, list[int]]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testMisguidedSetItem]
@@ -2489,7 +2489,7 @@ T = TypeVar('T')
 class C(Sequence[T], Generic[T]): pass
 C[0] = 0
 [out]
-main:4: error: Unsupported target for indexed assignment ("Type[C[T]]")
+main:4: error: Unsupported target for indexed assignment ("type[C[T]]")
 main:4: error: Invalid type: try using Literal[0] instead?
 
 [case testNoCrashOnPartialMember]
@@ -2497,7 +2497,7 @@ main:4: error: Invalid type: try using Literal[0] instead?
 class C:
     x = None
     def __init__(self) -> None:
-        self.x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+        self.x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 [builtins fixtures/list.pyi]
 [out]
 
@@ -2676,7 +2676,7 @@ class A:
 [case testLocalPartialTypesWithClassAttributeInitializedToEmptyDict]
 # flags: --local-partial-types
 class A:
-    x = {}  # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+    x = {}  # E: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 
     def f(self) -> None:
         self.x[0] = ''
@@ -2699,7 +2699,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]"
 
 [case testLocalPartialTypesWithGlobalInitializedToEmptyList2]
 # flags: --local-partial-types
-a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...")
+a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...")
 
 def f() -> None:
     a.append(1)
@@ -2710,7 +2710,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[Any]"
 
 [case testLocalPartialTypesWithGlobalInitializedToEmptyList3]
 # flags: --local-partial-types
-a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...")
+a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...")
 
 def f():
     a.append(1)
@@ -2732,7 +2732,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
 
 [case testLocalPartialTypesWithGlobalInitializedToEmptyDict2]
 # flags: --local-partial-types
-a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
 
 def f() -> None:
     a[0] = ''
@@ -2743,7 +2743,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]"
 
 [case testLocalPartialTypesWithGlobalInitializedToEmptyDict3]
 # flags: --local-partial-types
-a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
 
 def f():
     a[0] = ''
@@ -3306,7 +3306,7 @@ class A:
         s = self
         s.y['x'].append(1)
 
-x = {} # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+x = {} # E: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 x['x'].append(1)
 
 y = defaultdict(list)  # E: Need type annotation for "y"
@@ -3539,13 +3539,13 @@ class P:
 class M:
     x: List[str]
 class C(P, M):
-    x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+    x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 reveal_type(C.x)  # N: Revealed type is "builtins.list[Any]"
 [builtins fixtures/list.pyi]
 
 [case testNoPartialInSupertypeAsContext]
 class A:
-    args = {}  # E: Need type annotation for "args" (hint: "args: Dict[, ] = ...")
+    args = {}  # E: Need type annotation for "args" (hint: "args: dict[, ] = ...")
     def f(self) -> None:
         value = {1: "Hello"}
         class B(A):
@@ -3606,7 +3606,7 @@ S = TypeVar('S')
 
 def f(x: Callable[[T, S], None]) -> Tuple[T, S]: ...
 def g(*x: int) -> None: ...
-reveal_type(f(g))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(g))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testCallableInferenceAgainstCallableStarVsPos]
@@ -3620,7 +3620,7 @@ class Call(Protocol[T, S]):
 
 def f(x: Call[T, S]) -> Tuple[T, S]: ...
 def g(*x: int) -> None: ...
-reveal_type(f(g))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(g))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testCallableInferenceAgainstCallableNamedVsStar]
@@ -3634,7 +3634,7 @@ class Call(Protocol[T, S]):
 
 def f(x: Call[T, S]) -> Tuple[T, S]: ...
 def g(**kwargs: int) -> None: ...
-reveal_type(f(g))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(g))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testCallableInferenceAgainstCallableStarVsNamed]
@@ -3648,7 +3648,7 @@ class Call(Protocol[T, S]):
 
 def f(x: Call[T, S]) -> Tuple[T, S]: ...
 def g(**kwargs: int) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(g))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testCallableInferenceAgainstCallableNamedVsNamed]
@@ -3664,7 +3664,7 @@ def f(x: Call[T, S]) -> Tuple[T, S]: ...
 
 # Note: order of names is different w.r.t. protocol
 def g(*, y: int, x: str) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[builtins.str, builtins.int]"
+reveal_type(f(g))  # N: Revealed type is "tuple[builtins.str, builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testCallableInferenceAgainstCallablePosOnlyVsNamed]
@@ -3679,7 +3679,7 @@ class Call(Protocol[T]):
 def f(x: Call[T]) -> Tuple[T, T]: ...
 
 def g(__x: str) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[Never, Never]" \
+reveal_type(f(g))  # N: Revealed type is "tuple[Never, Never]" \
                    # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" \
                    # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]"
 [builtins fixtures/list.pyi]
@@ -3696,7 +3696,7 @@ class Call(Protocol[T]):
 def f(x: Call[T]) -> Tuple[T, T]: ...
 
 def g(*, x: str) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[Never, Never]" \
+reveal_type(f(g))  # N: Revealed type is "tuple[Never, Never]" \
                    # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" \
                    # N: "Call[Never].__call__" has type "Callable[[Never], None]"
 [builtins fixtures/list.pyi]
@@ -3713,7 +3713,7 @@ class Call(Protocol[T]):
 def f(x: Call[T]) -> Tuple[T, T]: ...
 
 def g(**x: str) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[Never, Never]" \
+reveal_type(f(g))  # N: Revealed type is "tuple[Never, Never]" \
                    # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" \
                    # N: "Call[Never].__call__" has type "Callable[[Never], None]"
 [builtins fixtures/list.pyi]
@@ -3730,7 +3730,7 @@ class Call(Protocol[T]):
 def f(x: Call[T]) -> Tuple[T, T]: ...
 
 def g(*args: str) -> None: pass
-reveal_type(f(g))  # N: Revealed type is "Tuple[Never, Never]" \
+reveal_type(f(g))  # N: Revealed type is "tuple[Never, Never]" \
                    # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" \
                    # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]"
 [builtins fixtures/list.pyi]
@@ -3873,7 +3873,7 @@ def a2(check: bool, a: B[str]) -> None:
     reveal_type(a if check else {})  # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
 
 def a3() -> None:
-    a = {}  # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...")
+    a = {}  # E: Need type annotation for "a" (hint: "a: dict[, ] = ...")
     b = {1: {}}  # E: Need type annotation for "b"
     c = {1: {}, 2: {"key": {}}}  # E: Need type annotation for "c"
     reveal_type(a)  # N: Revealed type is "builtins.dict[Any, Any]"
@@ -3893,7 +3893,7 @@ foo = [
     (1, ("a", "b")),
     (2, []),
 ]
-reveal_type(foo)  # N: Revealed type is "builtins.list[Tuple[builtins.int, typing.Sequence[builtins.str]]]"
+reveal_type(foo)  # N: Revealed type is "builtins.list[tuple[builtins.int, typing.Sequence[builtins.str]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testForLoopIndexVaribaleNarrowing1]
diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test
index c81dcac94afd..8a306b1dfac0 100644
--- a/test-data/unit/check-inline-config.test
+++ b/test-data/unit/check-inline-config.test
@@ -61,7 +61,7 @@ import a
 [file a.py]
 # mypy: allow-any-generics, disallow-untyped-globals
 
-x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
 
 from typing import List
 def foo() -> List:
diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test
index 058db1ea8197..fe08d2cfc699 100644
--- a/test-data/unit/check-isinstance.test
+++ b/test-data/unit/check-isinstance.test
@@ -423,16 +423,16 @@ def f(x: Union[List[int], List[str], int]) -> None:
 
         # type of a?
         reveal_type(x) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]"
-        x + 1 # E: Unsupported operand types for + ("List[int]" and "int") \
-              # E: Unsupported operand types for + ("List[str]" and "int") \
-              # N: Left operand is of type "Union[List[int], List[str]]"
+        x + 1 # E: Unsupported operand types for + ("list[int]" and "int") \
+              # E: Unsupported operand types for + ("list[str]" and "int") \
+              # N: Left operand is of type "Union[list[int], list[str]]"
     else:
         x[0] # E: Value of type "int" is not indexable
         x + 1
-    x[0] # E: Value of type "Union[List[int], List[str], int]" is not indexable
-    x + 1 # E: Unsupported operand types for + ("List[int]" and "int") \
-          # E: Unsupported operand types for + ("List[str]" and "int") \
-          # N: Left operand is of type "Union[List[int], List[str], int]"
+    x[0] # E: Value of type "Union[list[int], list[str], int]" is not indexable
+    x + 1 # E: Unsupported operand types for + ("list[int]" and "int") \
+          # E: Unsupported operand types for + ("list[str]" and "int") \
+          # N: Left operand is of type "Union[list[int], list[str], int]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testUnionListIsinstance2]
@@ -696,12 +696,12 @@ while bool():
     else:
         x + [1]
     x + 'a'           # E: Unsupported operand types for + ("int" and "str") \
-                      # E: Unsupported operand types for + ("List[int]" and "str") \
-                      # N: Left operand is of type "Union[int, str, List[int]]"
+                      # E: Unsupported operand types for + ("list[int]" and "str") \
+                      # N: Left operand is of type "Union[int, str, list[int]]"
 
-x + [1]               # E: Unsupported operand types for + ("int" and "List[int]") \
-                      # E: Unsupported operand types for + ("str" and "List[int]") \
-                      # N: Left operand is of type "Union[int, str, List[int]]"
+x + [1]               # E: Unsupported operand types for + ("int" and "list[int]") \
+                      # E: Unsupported operand types for + ("str" and "list[int]") \
+                      # N: Left operand is of type "Union[int, str, list[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIsInstanceThreeUnion2]
@@ -715,10 +715,10 @@ while bool():
         x + 'a'
         break
     x + [1]
-    x + 'a'           # E: Unsupported operand types for + ("List[int]" and "str")
-x + [1]               # E: Unsupported operand types for + ("int" and "List[int]") \
-                      # E: Unsupported operand types for + ("str" and "List[int]") \
-                      # N: Left operand is of type "Union[int, str, List[int]]"
+    x + 'a'           # E: Unsupported operand types for + ("list[int]" and "str")
+x + [1]               # E: Unsupported operand types for + ("int" and "list[int]") \
+                      # E: Unsupported operand types for + ("str" and "list[int]") \
+                      # N: Left operand is of type "Union[int, str, list[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIsInstanceThreeUnion3]
@@ -736,9 +736,9 @@ while bool():
         break
     x + [1]           # These lines aren't reached because x was an int
     x + 'a'
-x + [1]               # E: Unsupported operand types for + ("int" and "List[int]") \
-                      # E: Unsupported operand types for + ("str" and "List[int]") \
-                      # N: Left operand is of type "Union[int, str, List[int]]"
+x + [1]               # E: Unsupported operand types for + ("int" and "list[int]") \
+                      # E: Unsupported operand types for + ("str" and "list[int]") \
+                      # N: Left operand is of type "Union[int, str, list[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testRemovingTypeRepeatedly]
@@ -1520,7 +1520,7 @@ class Z(X): pass
 
 a: Union[Type[Y], Type[Z]]
 if issubclass(a, X):
-    reveal_type(a)  # N: Revealed type is "Union[Type[__main__.Y], Type[__main__.Z]]"
+    reveal_type(a)  # N: Revealed type is "Union[type[__main__.Y], type[__main__.Z]]"
 else:
     reveal_type(a)  # unreachable block
 [builtins fixtures/isinstancelist.pyi]
@@ -1529,20 +1529,20 @@ else:
 from typing import Union, List, Tuple, Dict, Type
 def f(x: Union[Type[int], Type[str], Type[List]]) -> None:
     if issubclass(x, (str, (int,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str]"
         x()[1]  # E: Value of type "Union[int, str]" is not indexable
     else:
-        reveal_type(x)  # N: Revealed type is "Type[builtins.list[Any]]"
+        reveal_type(x)  # N: Revealed type is "type[builtins.list[Any]]"
         reveal_type(x())  # N: Revealed type is "builtins.list[Any]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
     if issubclass(x, (str, (list,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.str, builtins.list[Any]]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
 [builtins fixtures/isinstancelist.pyi]
 
@@ -1551,20 +1551,20 @@ from typing import Union, List, Tuple, Dict, Type
 
 def f(x: Type[Union[int, str, List]]) -> None:
     if issubclass(x, (str, (int,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str]"
         x()[1]  # E: Value of type "Union[int, str]" is not indexable
     else:
-        reveal_type(x)  # N: Revealed type is "Type[builtins.list[Any]]"
+        reveal_type(x)  # N: Revealed type is "type[builtins.list[Any]]"
         reveal_type(x())  # N: Revealed type is "builtins.list[Any]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
     if issubclass(x, (str, (list,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.str, builtins.list[Any]]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
 [builtins fixtures/isinstancelist.pyi]
 
@@ -1572,23 +1572,23 @@ def f(x: Type[Union[int, str, List]]) -> None:
 from typing import Union, List, Tuple, Dict, Type
 
 def f(x: Type[Union[int, str, List]]) -> None:
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
     if issubclass(x, (str, (int,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str]"
         x()[1]  # E: Value of type "Union[int, str]" is not indexable
     else:
-        reveal_type(x)  # N: Revealed type is "Type[builtins.list[Any]]"
+        reveal_type(x)  # N: Revealed type is "type[builtins.list[Any]]"
         reveal_type(x())  # N: Revealed type is "builtins.list[Any]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
     if issubclass(x, (str, (list,))):
-        reveal_type(x)  # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]"
+        reveal_type(x)  # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]"
         reveal_type(x())  # N: Revealed type is "Union[builtins.str, builtins.list[Any]]"
         x()[1]
-    reveal_type(x)  # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]"
+    reveal_type(x)  # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]"
     reveal_type(x())  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
 [builtins fixtures/isinstancelist.pyi]
 
@@ -1603,7 +1603,7 @@ class GoblinAmbusher(Goblin):
 
 def test_issubclass(cls: Type[Goblin]) -> None:
     if issubclass(cls, GoblinAmbusher):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
         cls.level
         cls.job
         ga = cls()
@@ -1611,9 +1611,9 @@ def test_issubclass(cls: Type[Goblin]) -> None:
         ga.job
         ga.job = "Warrior"  # E: Cannot assign to class variable "job" via instance
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Goblin]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Goblin]"
         cls.level
-        cls.job  # E: "Type[Goblin]" has no attribute "job"
+        cls.job  # E: "type[Goblin]" has no attribute "job"
         g = cls()
         g.level = 15
         g.job  # E: "Goblin" has no attribute "job"
@@ -1632,14 +1632,14 @@ class GoblinAmbusher(Goblin):
 
 def test_issubclass(cls: Type[Mob]) -> None:
     if issubclass(cls, Goblin):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Goblin]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Goblin]"
         cls.level
-        cls.job  # E: "Type[Goblin]" has no attribute "job"
+        cls.job  # E: "type[Goblin]" has no attribute "job"
         g = cls()
         g.level = 15
         g.job  # E: "Goblin" has no attribute "job"
         if issubclass(cls, GoblinAmbusher):
-            reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+            reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
             cls.level
             cls.job
             g = cls()
@@ -1647,14 +1647,14 @@ def test_issubclass(cls: Type[Mob]) -> None:
             g.job
             g.job = 'Warrior' # E: Cannot assign to class variable "job" via instance
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Mob]"
-        cls.job  # E: "Type[Mob]" has no attribute "job"
-        cls.level  # E: "Type[Mob]" has no attribute "level"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Mob]"
+        cls.job  # E: "type[Mob]" has no attribute "job"
+        cls.level  # E: "type[Mob]" has no attribute "level"
         m = cls()
         m.level = 15  # E: "Mob" has no attribute "level"
         m.job  # E: "Mob" has no attribute "job"
         if issubclass(cls, GoblinAmbusher):
-            reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+            reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
             cls.job
             cls.level
             ga = cls()
@@ -1663,7 +1663,7 @@ def test_issubclass(cls: Type[Mob]) -> None:
             ga.job = 'Warrior' # E: Cannot assign to class variable "job" via instance
 
     if issubclass(cls, GoblinAmbusher):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
         cls.level
         cls.job
         ga = cls()
@@ -1688,29 +1688,29 @@ class GoblinDigger(Goblin):
 
 def test_issubclass(cls: Type[Mob]) -> None:
     if issubclass(cls, (Goblin, GoblinAmbusher)):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Goblin]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Goblin]"
         cls.level
-        cls.job  # E: "Type[Goblin]" has no attribute "job"
+        cls.job  # E: "type[Goblin]" has no attribute "job"
         g = cls()
         g.level = 15
         g.job  # E: "Goblin" has no attribute "job"
         if issubclass(cls, GoblinAmbusher):
             cls.level
-            reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+            reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
             cls.job
             ga = cls()
             ga.level = 15
             ga.job
             ga.job = "Warrior"  # E: Cannot assign to class variable "job" via instance
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.Mob]"
-        cls.job  # E: "Type[Mob]" has no attribute "job"
-        cls.level  # E: "Type[Mob]" has no attribute "level"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.Mob]"
+        cls.job  # E: "type[Mob]" has no attribute "job"
+        cls.level  # E: "type[Mob]" has no attribute "level"
         m = cls()
         m.level = 15  # E: "Mob" has no attribute "level"
         m.job  # E: "Mob" has no attribute "job"
         if issubclass(cls, GoblinAmbusher):
-            reveal_type(cls)  # N: Revealed type is "Type[__main__.GoblinAmbusher]"
+            reveal_type(cls)  # N: Revealed type is "type[__main__.GoblinAmbusher]"
             cls.job
             cls.level
             ga = cls()
@@ -1719,7 +1719,7 @@ def test_issubclass(cls: Type[Mob]) -> None:
             ga.job = "Warrior"  # E: Cannot assign to class variable "job" via instance
 
     if issubclass(cls, (GoblinDigger, GoblinAmbusher)):
-        reveal_type(cls)  # N: Revealed type is "Union[Type[__main__.GoblinDigger], Type[__main__.GoblinAmbusher]]"
+        reveal_type(cls)  # N: Revealed type is "Union[type[__main__.GoblinDigger], type[__main__.GoblinAmbusher]]"
         cls.level
         cls.job
         g = cls()
@@ -1736,14 +1736,14 @@ class MyIntList(List[int]): pass
 
 def f(cls: Type[object]) -> None:
     if issubclass(cls, MyList):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.MyList]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.MyList]"
         cls()[0]
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[builtins.object]"
+        reveal_type(cls)  # N: Revealed type is "type[builtins.object]"
         cls()[0]  # E: Value of type "object" is not indexable
 
     if issubclass(cls, MyIntList):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.MyIntList]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.MyIntList]"
         cls()[0] + 1
 [builtins fixtures/isinstancelist.pyi]
 
@@ -1795,7 +1795,7 @@ class Bar: ...
 fm: FooMetaclass
 reveal_type(fm)  # N: Revealed type is "__main__.FooMetaclass"
 if issubclass(fm, Foo):
-    reveal_type(fm)  # N: Revealed type is "Type[__main__.Foo]"
+    reveal_type(fm)  # N: Revealed type is "type[__main__.Foo]"
 if issubclass(fm, Bar):
     reveal_type(fm)  # N: Revealed type is "None"
 [builtins fixtures/isinstance.pyi]
@@ -1810,11 +1810,11 @@ class Baz: ...
 fm: FooMetaclass
 reveal_type(fm)  # N: Revealed type is "__main__.FooMetaclass"
 if issubclass(fm, Foo):
-    reveal_type(fm)  # N: Revealed type is "Type[__main__.Foo]"
+    reveal_type(fm)  # N: Revealed type is "type[__main__.Foo]"
 if issubclass(fm, Bar):
-    reveal_type(fm)  # N: Revealed type is "Type[__main__.Bar]"
+    reveal_type(fm)  # N: Revealed type is "type[__main__.Bar]"
 if issubclass(fm, Baz):
-    reveal_type(fm)  # N: Revealed type is "Type[__main__.Baz]"
+    reveal_type(fm)  # N: Revealed type is "type[__main__.Baz]"
 [builtins fixtures/isinstance.pyi]
 
 [case testIsinstanceAndNarrowTypeVariable]
@@ -1861,10 +1861,10 @@ def f(x: T) -> T:
 from typing import Type
 def f(x: Type[int]) -> None:
     if isinstance(x, type):
-        reveal_type(x) # N: Revealed type is "Type[builtins.int]"
+        reveal_type(x) # N: Revealed type is "type[builtins.int]"
     else:
         reveal_type(x)  # Unreachable
-    reveal_type(x) # N: Revealed type is "Type[builtins.int]"
+    reveal_type(x) # N: Revealed type is "type[builtins.int]"
 [builtins fixtures/isinstance.pyi]
 
 [case testIsinstanceVariableSubstitution]
@@ -1899,15 +1899,15 @@ from typing import Type
 issubclass() # E: Missing positional arguments "x", "t" in call to "issubclass"
 y: Type[object]
 if issubclass(): # E: Missing positional arguments "x", "t" in call to "issubclass"
-    reveal_type(y) # N: Revealed type is "Type[builtins.object]"
+    reveal_type(y) # N: Revealed type is "type[builtins.object]"
 if issubclass(y): # E: Missing positional argument "t" in call to "issubclass"
-    reveal_type(y) # N: Revealed type is "Type[builtins.object]"
+    reveal_type(y) # N: Revealed type is "type[builtins.object]"
 
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIsInstanceTooManyArgs]
 isinstance(1, 1, 1) # E: Too many arguments for "isinstance" \
-         # E: Argument 2 to "isinstance" has incompatible type "int"; expected "Union[type, Tuple[Any, ...]]"
+         # E: Argument 2 to "isinstance" has incompatible type "int"; expected "Union[type, tuple[Any, ...]]"
 x: object
 if isinstance(x, str, 1): # E: Too many arguments for "isinstance"
     reveal_type(x) # N: Revealed type is "builtins.object"
@@ -2291,7 +2291,7 @@ def bar(x: Union[List[str], List[int], None]) -> None:
 from typing import Union, List, Tuple
 
 def f(var: Union[List[str], Tuple[str, str], str]) -> None:
-    reveal_type(var)  # N: Revealed type is "Union[builtins.list[builtins.str], Tuple[builtins.str, builtins.str], builtins.str]"
+    reveal_type(var)  # N: Revealed type is "Union[builtins.list[builtins.str], tuple[builtins.str, builtins.str], builtins.str]"
     if isinstance(var, (list, *(str, int))):
         reveal_type(var)  # N: Revealed type is "Union[builtins.list[builtins.str], builtins.str]"
 [builtins fixtures/isinstancelist.pyi]
@@ -2638,13 +2638,13 @@ class C:
 
 x: Type[A]
 if issubclass(x, B):
-    reveal_type(x)        # N: Revealed type is "Type[__main__.]"
+    reveal_type(x)        # N: Revealed type is "type[__main__.]"
     if issubclass(x, C):  # E: Subclass of "A", "B", and "C" cannot exist: would have incompatible method signatures
         reveal_type(x)    # E: Statement is unreachable
     else:
-        reveal_type(x)    # N: Revealed type is "Type[__main__.]"
+        reveal_type(x)    # N: Revealed type is "type[__main__.]"
 else:
-    reveal_type(x)        # N: Revealed type is "Type[__main__.A]"
+    reveal_type(x)        # N: Revealed type is "type[__main__.A]"
 [builtins fixtures/isinstance.pyi]
 
 [case testTypeEqualsCheck]
diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test
index 1418f9c3d184..689553445e9d 100644
--- a/test-data/unit/check-kwargs.test
+++ b/test-data/unit/check-kwargs.test
@@ -263,8 +263,8 @@ f(A(), z=A()) # E: Unexpected keyword argument "z" for "f"
 from typing import Dict, Any
 def f( **kwargs: 'A') -> None:
     d1 = kwargs # type: Dict[str, A]
-    d2 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "Dict[str, A]", variable has type "Dict[A, Any]")
-    d3 = kwargs # type: Dict[Any, str] # E: Incompatible types in assignment (expression has type "Dict[str, A]", variable has type "Dict[Any, str]")
+    d2 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "dict[str, A]", variable has type "dict[A, Any]")
+    d3 = kwargs # type: Dict[Any, str] # E: Incompatible types in assignment (expression has type "dict[str, A]", variable has type "dict[Any, str]")
 class A: pass
 [builtins fixtures/dict.pyi]
 [out]
@@ -274,7 +274,7 @@ from typing import Dict, Any
 def f(**kwargs) -> None:
     d1 = kwargs # type: Dict[str, A]
     d2 = kwargs # type: Dict[str, str]
-    d3 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "Dict[str, Any]", variable has type "Dict[A, Any]")
+    d3 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "dict[str, Any]", variable has type "dict[A, Any]")
 class A: pass
 [builtins fixtures/dict.pyi]
 [out]
@@ -301,9 +301,9 @@ d: Dict[str, A]
 f(**d)
 f(x=A(), **d)
 d2: Dict[str, B]
-f(**d2)         # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A"
-f(x=A(), **d2)  # E: Argument 2 to "f" has incompatible type "**Dict[str, B]"; expected "A"
-f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A"
+f(**d2)         # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A"
+f(x=A(), **d2)  # E: Argument 2 to "f" has incompatible type "**dict[str, B]"; expected "A"
+f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A"
 [builtins fixtures/dict.pyi]
 
 [case testKwargsAllowedInDunderCall]
@@ -369,7 +369,7 @@ def f(a: 'A', b: 'B') -> None: pass
 d: Dict[str, Any]
 f(**d)
 d2: Dict[str, A]
-f(**d2) # E: Argument 1 to "f" has incompatible type "**Dict[str, A]"; expected "B"
+f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, A]"; expected "B"
 class A: pass
 class B: pass
 [builtins fixtures/dict.pyi]
@@ -438,15 +438,15 @@ def f(a: int) -> None:
     pass
 
 s = ('',)
-f(*s) # E: Argument 1 to "f" has incompatible type "*Tuple[str]"; expected "int"
+f(*s) # E: Argument 1 to "f" has incompatible type "*tuple[str]"; expected "int"
 
 a = {'': 0}
-f(a) # E: Argument 1 to "f" has incompatible type "Dict[str, int]"; expected "int"
+f(a) # E: Argument 1 to "f" has incompatible type "dict[str, int]"; expected "int"
 f(**a) # okay
 
 b = {'': ''}
-f(b) # E: Argument 1 to "f" has incompatible type "Dict[str, str]"; expected "int"
-f(**b) # E: Argument 1 to "f" has incompatible type "**Dict[str, str]"; expected "int"
+f(b) # E: Argument 1 to "f" has incompatible type "dict[str, str]"; expected "int"
+f(**b) # E: Argument 1 to "f" has incompatible type "**dict[str, str]"; expected "int"
 
 c = {0: 0}
 f(**c) # E: Keywords must be strings
@@ -491,7 +491,7 @@ def g(arg: int = 0, **kwargs: object) -> None:
 
 d = {} # type: Dict[str, object]
 f(**d)
-g(**d)  # E: Argument 1 to "g" has incompatible type "**Dict[str, object]"; expected "int"
+g(**d)  # E: Argument 1 to "g" has incompatible type "**dict[str, object]"; expected "int"
 
 m = {} # type: Mapping[str, object]
 f(**m)
@@ -565,5 +565,5 @@ main:36: error: Argument 1 to "foo" has incompatible type "**A[str, str]"; expec
 main:37: error: Argument 1 to "foo" has incompatible type "**B[str, str]"; expected "float"
 main:38: error: Argument after ** must be a mapping, not "C[str, float]"
 main:39: error: Argument after ** must be a mapping, not "D"
-main:41: error: Argument 1 to "foo" has incompatible type "**Dict[str, str]"; expected "float"
+main:41: error: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "float"
 [builtins fixtures/dict.pyi]
diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
index f36eff28f33f..d91b257b0096 100644
--- a/test-data/unit/check-literal.test
+++ b/test-data/unit/check-literal.test
@@ -70,9 +70,9 @@ def foo(x: Tuple[1]) -> None: ...   # E: Invalid type: try using Literal[1] inst
 
 y: Tuple[Literal[2]]
 def bar(x: Tuple[Literal[2]]) -> None: ...
-reveal_type(x)                      # N: Revealed type is "Tuple[Any]"
-reveal_type(y)                      # N: Revealed type is "Tuple[Literal[2]]"
-reveal_type(bar)                    # N: Revealed type is "def (x: Tuple[Literal[2]])"
+reveal_type(x)                      # N: Revealed type is "tuple[Any]"
+reveal_type(y)                      # N: Revealed type is "tuple[Literal[2]]"
+reveal_type(bar)                    # N: Revealed type is "def (x: tuple[Literal[2]])"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -88,9 +88,9 @@ y = None  # type: Optional[Tuple[Literal[2]]]
 def bar(x):
     # type: (Tuple[Literal[2]]) -> None
     pass
-reveal_type(x)                      # N: Revealed type is "Union[Tuple[Any], None]"
-reveal_type(y)                      # N: Revealed type is "Union[Tuple[Literal[2]], None]"
-reveal_type(bar)                    # N: Revealed type is "def (x: Tuple[Literal[2]])"
+reveal_type(x)                      # N: Revealed type is "Union[tuple[Any], None]"
+reveal_type(y)                      # N: Revealed type is "Union[tuple[Literal[2]], None]"
+reveal_type(bar)                    # N: Revealed type is "def (x: tuple[Literal[2]])"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -946,12 +946,12 @@ def bar(x: Sequence[Literal[1, 2]]) -> None: pass
 a: List[Literal[1]]
 b: List[Literal[1, 2, 3]]
 
-foo(a)  # E: Argument 1 to "foo" has incompatible type "List[Literal[1]]"; expected "List[Literal[1, 2]]" \
+foo(a)  # E: Argument 1 to "foo" has incompatible type "list[Literal[1]]"; expected "list[Literal[1, 2]]" \
         # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
         # N: Consider using "Sequence" instead, which is covariant
-foo(b)  # E: Argument 1 to "foo" has incompatible type "List[Literal[1, 2, 3]]"; expected "List[Literal[1, 2]]"
+foo(b)  # E: Argument 1 to "foo" has incompatible type "list[Literal[1, 2, 3]]"; expected "list[Literal[1, 2]]"
 bar(a)
-bar(b)  # E: Argument 1 to "bar" has incompatible type "List[Literal[1, 2, 3]]"; expected "Sequence[Literal[1, 2]]"
+bar(b)  # E: Argument 1 to "bar" has incompatible type "list[Literal[1, 2, 3]]"; expected "Sequence[Literal[1, 2]]"
 [builtins fixtures/list.pyi]
 [out]
 
@@ -1186,10 +1186,10 @@ from typing import Literal, Tuple
 
 a: Tuple[Literal[1], Literal[2]] = (1, 2)
 b: Tuple[int, Literal[1, 2], Literal[3], Tuple[Literal["foo"]]] = (1, 2, 3, ("foo",))
-c: Tuple[Literal[1], Literal[2]] = (2, 1)  # E: Incompatible types in assignment (expression has type "Tuple[Literal[2], Literal[1]]", variable has type "Tuple[Literal[1], Literal[2]]")
+c: Tuple[Literal[1], Literal[2]] = (2, 1)  # E: Incompatible types in assignment (expression has type "tuple[Literal[2], Literal[1]]", variable has type "tuple[Literal[1], Literal[2]]")
 d = (1, 2)
 
-reveal_type(d)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(d)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 
 [builtins fixtures/tuple.pyi]
 [out]
@@ -1477,13 +1477,13 @@ Alias = Literal[3]
 
 isinstance(3, Literal[3])           # E: Cannot use isinstance() with Literal type
 isinstance(3, Alias)                # E: Cannot use isinstance() with Literal type \
-                                    # E: Argument 2 to "isinstance" has incompatible type ""; expected "Union[type, Tuple[Any, ...]]"
+                                    # E: Argument 2 to "isinstance" has incompatible type ""; expected "Union[type, tuple[Any, ...]]"
 isinstance(3, Renamed[3])           # E: Cannot use isinstance() with Literal type
 isinstance(3, indirect.Literal[3])  # E: Cannot use isinstance() with Literal type
 
 issubclass(int, Literal[3])           # E: Cannot use issubclass() with Literal type
 issubclass(int, Alias)                # E: Cannot use issubclass() with Literal type \
-                                      # E: Argument 2 to "issubclass" has incompatible type ""; expected "Union[type, Tuple[Any, ...]]"
+                                      # E: Argument 2 to "issubclass" has incompatible type ""; expected "Union[type, tuple[Any, ...]]"
 issubclass(int, Renamed[3])           # E: Cannot use issubclass() with Literal type
 issubclass(int, indirect.Literal[3])  # E: Cannot use issubclass() with Literal type
 [builtins fixtures/isinstancelist.pyi]
@@ -1842,8 +1842,8 @@ reveal_type(tup1[idx3])       # N: Revealed type is "__main__.D"
 reveal_type(tup1[idx4])       # N: Revealed type is "__main__.E"
 reveal_type(tup1[idx_neg1])   # N: Revealed type is "__main__.E"
 tup1[idx5]                    # E: Tuple index out of range
-reveal_type(tup1[idx2:idx4])  # N: Revealed type is "Tuple[Union[__main__.C, None], __main__.D]"
-reveal_type(tup1[::idx2])     # N: Revealed type is "Tuple[__main__.A, Union[__main__.C, None], __main__.E]"
+reveal_type(tup1[idx2:idx4])  # N: Revealed type is "tuple[Union[__main__.C, None], __main__.D]"
+reveal_type(tup1[::idx2])     # N: Revealed type is "tuple[__main__.A, Union[__main__.C, None], __main__.E]"
 if tup1[idx2] is not None:
     reveal_type(tup1[idx2])   # N: Revealed type is "Union[__main__.C, None]"
 if tup1[idx_final] is not None:
@@ -1858,9 +1858,9 @@ reveal_type(tup2[idx3])       # N: Revealed type is "__main__.D"
 reveal_type(tup2[idx4])       # N: Revealed type is "__main__.E"
 reveal_type(tup2[idx_neg1])   # N: Revealed type is "__main__.E"
 tup2[idx5]                    # E: Tuple index out of range
-reveal_type(tup2[idx2:idx4])  # N: Revealed type is "Tuple[__main__.C, __main__.D]"
-reveal_type(tup2[::idx2])     # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E]"
-tup3: Tup2Class = tup2[:]     # E: Incompatible types in assignment (expression has type "Tuple[A, B, C, D, E]", variable has type "Tup2Class")
+reveal_type(tup2[idx2:idx4])  # N: Revealed type is "tuple[__main__.C, __main__.D]"
+reveal_type(tup2[::idx2])     # N: Revealed type is "tuple[__main__.A, __main__.C, __main__.E]"
+tup3: Tup2Class = tup2[:]     # E: Incompatible types in assignment (expression has type "tuple[A, B, C, D, E]", variable has type "Tup2Class")
 [builtins fixtures/slice.pyi]
 
 [case testLiteralIntelligentIndexingTypedDict]
@@ -1956,13 +1956,13 @@ Tup2Class = NamedTuple('Tup2Class', [('a', A), ('b', B), ('c', C), ('d', D), ('e
 tup2: Tup2Class
 
 reveal_type(tup1[idx1])         # N: Revealed type is "Union[__main__.B, __main__.C]"
-reveal_type(tup1[idx1:idx2])    # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]"
-reveal_type(tup1[0::idx1])      # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]"
+reveal_type(tup1[idx1:idx2])    # N: Revealed type is "Union[tuple[__main__.B, __main__.C], tuple[__main__.B, __main__.C, __main__.D], tuple[__main__.C], tuple[__main__.C, __main__.D]]"
+reveal_type(tup1[0::idx1])      # N: Revealed type is "Union[tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], tuple[__main__.A, __main__.C, __main__.E]]"
 tup1[idx_bad]                   # E: Tuple index out of range
 
 reveal_type(tup2[idx1])         # N: Revealed type is "Union[__main__.B, __main__.C]"
-reveal_type(tup2[idx1:idx2])    # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]"
-reveal_type(tup2[0::idx1])      # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]"
+reveal_type(tup2[idx1:idx2])    # N: Revealed type is "Union[tuple[__main__.B, __main__.C], tuple[__main__.B, __main__.C, __main__.D], tuple[__main__.C], tuple[__main__.C, __main__.D]]"
+reveal_type(tup2[0::idx1])      # N: Revealed type is "Union[tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], tuple[__main__.A, __main__.C, __main__.E]]"
 tup2[idx_bad]                   # E: Tuple index out of range
 [builtins fixtures/slice.pyi]
 [out]
@@ -2228,7 +2228,7 @@ var1: Final = [0, None]
 var2: Final = (0, None)
 
 reveal_type(var1)  # N: Revealed type is "builtins.list[Union[builtins.int, None]]"
-reveal_type(var2)  # N: Revealed type is "Tuple[Literal[0]?, None]"
+reveal_type(var2)  # N: Revealed type is "tuple[Literal[0]?, None]"
 [builtins fixtures/tuple.pyi]
 
 [case testLiteralFinalErasureInMutableDatastructures2]
@@ -2289,7 +2289,7 @@ def force1(x: Literal[1]) -> None: pass
 def force2(x: Tuple[Literal[1], Literal[2]]) -> None: pass
 
 reveal_type(a)          # N: Revealed type is "Literal[1]?"
-reveal_type(b)          # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?]"
+reveal_type(b)          # N: Revealed type is "tuple[Literal[1]?, Literal[2]?]"
 
 force1(a)  # ok
 force2(b)  # ok
@@ -2308,7 +2308,7 @@ def force1(x: List[Literal[1]]) -> None: pass
 def force2(x: Literal[1]) -> None: pass
 
 reveal_type(implicit)            # N: Revealed type is "builtins.list[builtins.int]"
-force1(reveal_type(implicit))    # E: Argument 1 to "force1" has incompatible type "List[int]"; expected "List[Literal[1]]" \
+force1(reveal_type(implicit))    # E: Argument 1 to "force1" has incompatible type "list[int]"; expected "list[Literal[1]]" \
                                  # N: Revealed type is "builtins.list[builtins.int]"
 force2(reveal_type(implicit[0])) # E: Argument 1 to "force2" has incompatible type "int"; expected "Literal[1]" \
                                  # N: Revealed type is "builtins.int"
@@ -2318,7 +2318,7 @@ force1(reveal_type(explicit))    # N: Revealed type is "builtins.list[Literal[1]
 force2(reveal_type(explicit[0])) # N: Revealed type is "Literal[1]"
 
 reveal_type(direct)              # N: Revealed type is "builtins.list[builtins.int]"
-force1(reveal_type(direct))      # E: Argument 1 to "force1" has incompatible type "List[int]"; expected "List[Literal[1]]" \
+force1(reveal_type(direct))      # E: Argument 1 to "force1" has incompatible type "list[int]"; expected "list[Literal[1]]" \
                                  # N: Revealed type is "builtins.list[builtins.int]"
 force2(reveal_type(direct[0]))   # E: Argument 1 to "force2" has incompatible type "int"; expected "Literal[1]" \
                                  # N: Revealed type is "builtins.int"
@@ -2876,7 +2876,7 @@ def f() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
     else:
         return (False, 'oops')
 
-reveal_type(f())  # N: Revealed type is "Union[Tuple[Literal[True], builtins.int], Tuple[Literal[False], builtins.str]]"
+reveal_type(f())  # N: Revealed type is "Union[tuple[Literal[True], builtins.int], tuple[Literal[False], builtins.str]]"
 
 def does_work() -> Tuple[Literal[1]]:
     x: Final = (1,)
@@ -2888,23 +2888,23 @@ def also_works() -> Tuple[Literal[1]]:
 
 def invalid_literal_value() -> Tuple[Literal[1]]:
     x: Final = (2,)
-    return x  # E: Incompatible return value type (got "Tuple[int]", expected "Tuple[Literal[1]]")
+    return x  # E: Incompatible return value type (got "tuple[int]", expected "tuple[Literal[1]]")
 
 def invalid_literal_type() -> Tuple[Literal[1]]:
     x: Final = (True,)
-    return x  # E: Incompatible return value type (got "Tuple[bool]", expected "Tuple[Literal[1]]")
+    return x  # E: Incompatible return value type (got "tuple[bool]", expected "tuple[Literal[1]]")
 
 def incorrect_return1() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
     if x:
-        return (False, 5)  # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
+        return (False, 5)  # E: Incompatible return value type (got "tuple[bool, int]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]")
     else:
-        return (True, 'oops')  # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
+        return (True, 'oops')  # E: Incompatible return value type (got "tuple[bool, str]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]")
 
 def incorrect_return2() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
     if x:
-        return (bool(), 5)  # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
+        return (bool(), 5)  # E: Incompatible return value type (got "tuple[bool, int]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]")
     else:
-        return (bool(), 'oops')  # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
+        return (bool(), 'oops')  # E: Incompatible return value type (got "tuple[bool, str]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]")
 [builtins fixtures/bool.pyi]
 
 [case testLiteralSubtypeContext]
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
index 000dae86131d..dcc64f0924c4 100644
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -423,7 +423,7 @@ import typing
 __all__ = [1, 2, 3]
 [builtins fixtures/module_all.pyi]
 [out]
-main:2: error: Type of __all__ must be "Sequence[str]", not "List[int]"
+main:2: error: Type of __all__ must be "Sequence[str]", not "list[int]"
 
 [case testUnderscoreExportedValuesInImportAll]
 import typing
@@ -666,9 +666,9 @@ import mod
 X: Type[mod.A]
 Y: Type[mod.B]
 from mod import B as X
-from mod import A as Y  # E: Incompatible import of "Y" (imported name has type "Type[A]", local name has type "Type[B]")
+from mod import A as Y  # E: Incompatible import of "Y" (imported name has type "type[A]", local name has type "type[B]")
 
-import mod as X  # E: Incompatible import of "X" (imported name has type "object", local name has type "Type[A]")
+import mod as X  # E: Incompatible import of "X" (imported name has type "object", local name has type "type[A]")
 
 [file mod.py]
 class A: ...
@@ -1049,7 +1049,7 @@ class z: pass
 [out]
 main:2: error: Incompatible import of "x" (imported name has type "str", local name has type "int")
 main:2: error: Incompatible import of "y" (imported name has type "Callable[[], str]", local name has type "Callable[[], int]")
-main:2: error: Incompatible import of "z" (imported name has type "Type[b.z]", local name has type "Type[a.z]")
+main:2: error: Incompatible import of "z" (imported name has type "type[b.z]", local name has type "type[a.z]")
 
 -- Misc
 
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
index 13f977a1e463..45de2a9e50ae 100644
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -305,9 +305,9 @@ t: Tuple[int, str]
 if int():
     b = a  # E: Incompatible types in assignment (expression has type "A", variable has type "B")
 if int():
-    a = t  # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A")
+    a = t  # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "A")
 if int():
-    b = t  # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B")
+    b = t  # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "B")
 if int():
     t = a
 if int():
@@ -332,14 +332,14 @@ if int():
 if int():
     l = [A(1)]
 if int():
-    a = (1,)  # E: Incompatible types in assignment (expression has type "Tuple[int]", \
+    a = (1,)  # E: Incompatible types in assignment (expression has type "tuple[int]", \
                    variable has type "A")
 [builtins fixtures/list.pyi]
 
 [case testNamedTupleMissingClassAttribute]
 import collections
 MyNamedTuple = collections.namedtuple('MyNamedTuple', ['spam', 'eggs'])
-MyNamedTuple.x # E: "Type[MyNamedTuple]" has no attribute "x"
+MyNamedTuple.x # E: "type[MyNamedTuple]" has no attribute "x"
 
 [builtins fixtures/list.pyi]
 
@@ -376,7 +376,7 @@ from collections import namedtuple
 
 X = namedtuple('X', ['x', 'y'])
 x: X
-reveal_type(x._replace())  # N: Revealed type is "Tuple[Any, Any, fallback=__main__.X]"
+reveal_type(x._replace())  # N: Revealed type is "tuple[Any, Any, fallback=__main__.X]"
 x._replace(y=5)
 x._replace(x=3)
 x._replace(x=3, y=5)
@@ -401,7 +401,7 @@ from typing import NamedTuple
 
 X = NamedTuple('X', [('x', int), ('y', str)])
 x: X
-reveal_type(x._replace())  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]"
+reveal_type(x._replace())  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]"
 x._replace(x=5)
 x._replace(y=5)  # E: Argument "y" to "_replace" of "X" has incompatible type "int"; expected "str"
 [builtins fixtures/tuple.pyi]
@@ -410,7 +410,7 @@ x._replace(y=5)  # E: Argument "y" to "_replace" of "X" has incompatible type "i
 from typing import NamedTuple
 
 X = NamedTuple('X', [('x', int), ('y', str)])
-reveal_type(X._make([5, 'a']))  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]"
+reveal_type(X._make([5, 'a']))  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]"
 X._make('a b')  # E: Argument 1 to "_make" of "X" has incompatible type "str"; expected "Iterable[Any]"
 
 -- # FIX: not a proper class method
@@ -424,7 +424,7 @@ X._make('a b')  # E: Argument 1 to "_make" of "X" has incompatible type "str"; e
 from typing import NamedTuple
 
 X = NamedTuple('X', [('x', int), ('y', str)])
-reveal_type(X._fields)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(X._fields)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testNamedTupleSource]
@@ -450,7 +450,7 @@ from typing import NamedTuple
 
 X = NamedTuple('X', [('x', int), ('y', str)])
 Y = NamedTuple('Y', [('x', int), ('y', str)])
-reveal_type([X(3, 'b'), Y(1, 'a')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
+reveal_type([X(3, 'b'), Y(1, 'a')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
 
 [builtins fixtures/list.pyi]
 
@@ -458,8 +458,8 @@ reveal_type([X(3, 'b'), Y(1, 'a')])  # N: Revealed type is "builtins.list[Tuple[
 from typing import NamedTuple, Tuple
 
 X = NamedTuple('X', [('x', int), ('y', str)])
-reveal_type([(3, 'b'), X(1, 'a')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
-reveal_type([X(1, 'a'), (3, 'b')])  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
+reveal_type([(3, 'b'), X(1, 'a')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
+reveal_type([X(1, 'a'), (3, 'b')])  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
 
 [builtins fixtures/list.pyi]
 
@@ -519,14 +519,14 @@ a = B('').member()
 [case testNamedTupleSelfTypeReplace]
 from typing import NamedTuple, TypeVar
 A = NamedTuple('A', [('x', str)])
-reveal_type(A('hello')._replace(x=''))  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.A]"
+reveal_type(A('hello')._replace(x=''))  # N: Revealed type is "tuple[builtins.str, fallback=__main__.A]"
 a: A
 a = A('hello')._replace(x='')
 
 class B(A):
     pass
 
-reveal_type(B('hello')._replace(x=''))  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.B]"
+reveal_type(B('hello')._replace(x=''))  # N: Revealed type is "tuple[builtins.str, fallback=__main__.B]"
 b: B
 b = B('hello')._replace(x='')
 [builtins fixtures/tuple.pyi]
@@ -534,13 +534,13 @@ b = B('hello')._replace(x='')
 [case testNamedTupleSelfTypeMake]
 from typing import NamedTuple, TypeVar
 A = NamedTuple('A', [('x', str)])
-reveal_type(A._make(['']))  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.A]"
+reveal_type(A._make(['']))  # N: Revealed type is "tuple[builtins.str, fallback=__main__.A]"
 a = A._make([''])  # type: A
 
 class B(A):
     pass
 
-reveal_type(B._make(['']))  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.B]"
+reveal_type(B._make(['']))  # N: Revealed type is "tuple[builtins.str, fallback=__main__.B]"
 b = B._make([''])  # type: B
 
 [builtins fixtures/list.pyi]
@@ -559,7 +559,7 @@ class C:
         A = NamedTuple('A', [('x', int)])
     def g(self):
         A = NamedTuple('A', [('y', int)])
-C.A  # E: "Type[C]" has no attribute "A"
+C.A  # E: "type[C]" has no attribute "A"
 [builtins fixtures/tuple.pyi]
 
 [case testNamedTupleInFunction]
@@ -603,8 +603,8 @@ def f(x: a.X) -> None:
     reveal_type(x)
 [builtins fixtures/tuple.pyi]
 [out]
-tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.X]"
-tmp/b.py:6: note: Revealed type is "Tuple[Any, fallback=a.X]"
+tmp/b.py:4: note: Revealed type is "tuple[Any, fallback=a.X]"
+tmp/b.py:6: note: Revealed type is "tuple[Any, fallback=a.X]"
 
 [case testNamedTupleWithImportCycle2]
 import a
@@ -623,8 +623,8 @@ def f(x: a.N) -> None:
         reveal_type(x)
 [builtins fixtures/tuple.pyi]
 [out]
-tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.N]"
-tmp/b.py:7: note: Revealed type is "Tuple[Any, fallback=a.N]"
+tmp/b.py:4: note: Revealed type is "tuple[Any, fallback=a.N]"
+tmp/b.py:7: note: Revealed type is "tuple[Any, fallback=a.N]"
 
 [case testSimpleSelfReferentialNamedTuple]
 from typing import NamedTuple
@@ -676,7 +676,7 @@ def test() -> None:
                                                # N: Recursive types are not allowed at function scope
         ])
     n: Node
-    reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node@4]"
+    reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node@4]"
 [builtins fixtures/tuple.pyi]
 
 [case testSelfRefNT2]
@@ -693,7 +693,7 @@ def test() -> None:
         y: int
 
     n: A
-    reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A@4]"
+    reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A@4]"
 [builtins fixtures/tuple.pyi]
 
 [case testSelfRefNT3]
@@ -711,10 +711,10 @@ def test() -> None:
         ])
     n: B
     m: A
-    reveal_type(n.x) # N: Revealed type is "Tuple[Any, builtins.int]"
+    reveal_type(n.x) # N: Revealed type is "tuple[Any, builtins.int]"
     reveal_type(m[0]) # N: Revealed type is "builtins.str"
     lst = [m, n]
-    reveal_type(lst[0]) # N: Revealed type is "Tuple[builtins.object, builtins.object]"
+    reveal_type(lst[0]) # N: Revealed type is "tuple[builtins.object, builtins.object]"
 [builtins fixtures/tuple.pyi]
 
 [case testSelfRefNT4]
@@ -739,7 +739,7 @@ from typing import NamedTuple
 
 def test() -> None:
     B = NamedTuple('B', [
-            ('x', A),  # E: Cannot resolve name "A" (possible cyclic definition)  \
+            ('x', A),  # E: Cannot resolve name "A" (possible cyclic definition) \
                        # N: Recursive types are not allowed at function scope \
                        # E: Name "A" is used before definition
             ('y', int),
@@ -750,8 +750,8 @@ def test() -> None:
         ])
     n: A
     def f(m: B) -> None: pass
-    reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[Any, builtins.int, fallback=__main__.B@4], fallback=__main__.A@8]"
-    reveal_type(f) # N: Revealed type is "def (m: Tuple[Any, builtins.int, fallback=__main__.B@4])"
+    reveal_type(n) # N: Revealed type is "tuple[builtins.str, tuple[Any, builtins.int, fallback=__main__.B@4], fallback=__main__.A@8]"
+    reveal_type(f) # N: Revealed type is "def (m: tuple[Any, builtins.int, fallback=__main__.B@4])"
 [builtins fixtures/tuple.pyi]
 
 [case testRecursiveNamedTupleInBases]
@@ -765,13 +765,13 @@ def test() -> None:
     class B(NamedTuple('B', [('val', object)])): pass
 
     exp: Exp
-    reveal_type(exp)  # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]"
+    reveal_type(exp)  # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]"
     if isinstance(exp, A):
-        reveal_type(exp[0][0])  # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]"
-        reveal_type(exp.attr[0])  # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]"
+        reveal_type(exp[0][0])  # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]"
+        reveal_type(exp.attr[0])  # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]"
     if isinstance(exp, B):
         reveal_type(exp.val)  # N: Revealed type is "builtins.object"
-    reveal_type(A([B(1), B(2)]))  # N: Revealed type is "Tuple[builtins.list[Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]], fallback=__main__.A@5]"
+    reveal_type(A([B(1), B(2)]))  # N: Revealed type is "tuple[builtins.list[Union[Any, tuple[builtins.object, fallback=__main__.B@6]]], fallback=__main__.A@5]"
 [builtins fixtures/isinstancelist.pyi]
 [out]
 
@@ -786,7 +786,7 @@ from b import tp
 x: tp
 reveal_type(x.x)  # N: Revealed type is "builtins.int"
 
-reveal_type(tp)  # N: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=b.tp]"
+reveal_type(tp)  # N: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=b.tp]"
 tp('x')  # E: Argument 1 to "tp" has incompatible type "str"; expected "int"
 
 [file b.py]
@@ -809,7 +809,7 @@ def test() -> None:
         pass
 
     hc = HelpCommand(subcommands=[])
-    reveal_type(hc)  # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.HelpCommand@7]"
+    reveal_type(hc)  # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.HelpCommand@7]"
 [builtins fixtures/list.pyi]
 [out]
 
@@ -840,7 +840,7 @@ class D(NamedTuple):
     def f(cls) -> None: pass
 
 d: Type[D]
-d.g()  # E: "Type[D]" has no attribute "g"
+d.g()  # E: "type[D]" has no attribute "g"
 d.f()
 [builtins fixtures/classmethod.pyi]
 
@@ -902,7 +902,7 @@ class Parent(NamedTuple):
 class Child(Parent):
     pass
 
-reveal_type(Child.class_method())  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.Child]"
+reveal_type(Child.class_method())  # N: Revealed type is "tuple[builtins.str, fallback=__main__.Child]"
 [builtins fixtures/classmethod.pyi]
 
 [case testNamedTupleAsConditionalStrictOptionalDisabled]
@@ -942,10 +942,10 @@ class MyTupleB(NamedTuple):
     field_2: MyBaseTuple
 
 u: MyTupleUnion
-reveal_type(u.field_1)  # N: Revealed type is "typing.Mapping[Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]"
-reveal_type(u.field_2)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]"
-reveal_type(u[0])  # N: Revealed type is "typing.Mapping[Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]"
-reveal_type(u[1])  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]"
+reveal_type(u.field_1)  # N: Revealed type is "typing.Mapping[tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]"
+reveal_type(u.field_2)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]"
+reveal_type(u[0])  # N: Revealed type is "typing.Mapping[tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]"
+reveal_type(u[1])  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]"
 [builtins fixtures/tuple.pyi]
 
 [case testAssignNamedTupleAsAttribute]
@@ -965,8 +965,8 @@ from typing import NamedTuple
 
 N = NamedTuple('N', [])
 n: N
-reveal_type(N)  # N: Revealed type is "def () -> Tuple[(), fallback=__main__.N]"
-reveal_type(n)  # N: Revealed type is "Tuple[(), fallback=__main__.N]"
+reveal_type(N)  # N: Revealed type is "def () -> tuple[(), fallback=__main__.N]"
+reveal_type(n)  # N: Revealed type is "tuple[(), fallback=__main__.N]"
 [builtins fixtures/tuple.pyi]
 
 [case testNamedTupleWrongfile]
@@ -1027,11 +1027,11 @@ print_namedtuple(b5)  # ok
 print_namedtuple(b6)  # ok
 
 print_namedtuple(1)  # E: Argument 1 to "print_namedtuple" has incompatible type "int"; expected "NamedTuple"
-print_namedtuple(('bar',))  # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str]"; expected "NamedTuple"
-print_namedtuple((1, 2))  # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple"
-print_namedtuple((b1,))  # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[Bar]"; expected "NamedTuple"
+print_namedtuple(('bar',))  # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[str]"; expected "NamedTuple"
+print_namedtuple((1, 2))  # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[int, int]"; expected "NamedTuple"
+print_namedtuple((b1,))  # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[Bar]"; expected "NamedTuple"
 t: Tuple[str, ...]
-print_namedtuple(t)  # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str, ...]"; expected "NamedTuple"
+print_namedtuple(t)  # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[str, ...]"; expected "NamedTuple"
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
@@ -1074,9 +1074,9 @@ def good6() -> NamedTuple:
 def bad1() -> NamedTuple:
     return 1  # E: Incompatible return value type (got "int", expected "NamedTuple")
 def bad2() -> NamedTuple:
-    return ()  # E: Incompatible return value type (got "Tuple[()]", expected "NamedTuple")
+    return ()  # E: Incompatible return value type (got "tuple[()]", expected "NamedTuple")
 def bad3() -> NamedTuple:
-    return (1, 2)  # E: Incompatible return value type (got "Tuple[int, int]", expected "NamedTuple")
+    return (1, 2)  # E: Incompatible return value type (got "tuple[int, int]", expected "NamedTuple")
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
@@ -1090,14 +1090,14 @@ C = NamedTuple("C", [("x", Literal[True, False])])
 T = Tuple[Literal[True, False]]
 
 # Was error here:
-# Incompatible types in assignment (expression has type "List[C]", variable has type "List[C]")
+# Incompatible types in assignment (expression has type "list[C]", variable has type "list[C]")
 x: List[C] = [C(True)]
 
 t: T
 
 # Was error here:
-# Incompatible types in assignment (expression has type "List[Tuple[bool]]",
-# variable has type "List[Tuple[Union[Literal[True], Literal[False]]]]")
+# Incompatible types in assignment (expression has type "list[tuple[bool]]",
+# variable has type "list[tuple[Union[Literal[True], Literal[False]]]]")
 y: List[T] = [t]
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
@@ -1114,22 +1114,22 @@ class C(NamedTuple):
 
 def foo(c: C) -> None:
     if c:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]"
     else:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]"
 
 def bar(c: C) -> None:
     if not c:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]"
     else:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]"
 
 class C1(NamedTuple):
     x: int
 
 def foo1(c: C1) -> None:
     if c:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C1]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C1]"
     else:
         c  # E: Statement is unreachable
 
@@ -1137,7 +1137,7 @@ def bar1(c: C1) -> None:
     if not c:
         c  # E: Statement is unreachable
     else:
-        reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C1]"
+        reveal_type(c)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C1]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
 
@@ -1162,7 +1162,7 @@ class One(NamedTuple):
     bar: int
     baz: str
 o: One
-reveal_type(o.__match_args__)  # N: Revealed type is "Tuple[Literal['bar'], Literal['baz']]"
+reveal_type(o.__match_args__)  # N: Revealed type is "tuple[Literal['bar'], Literal['baz']]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
 
@@ -1202,11 +1202,11 @@ class NT(NamedTuple, Generic[T]):
     value: T
 
 nts: NT[str]
-reveal_type(nts)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]"
+reveal_type(nts)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]"
 reveal_type(nts.value)  # N: Revealed type is "builtins.str"
 
 nti = NT(key=0, value=0)
-reveal_type(nti)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
+reveal_type(nti)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
 reveal_type(nti.value)  # N: Revealed type is "builtins.int"
 
 NT[str](key=0, value=0)  # E: Argument "value" to "NT" has incompatible type "int"; expected "str"
@@ -1224,8 +1224,8 @@ class NT(NamedTuple, Generic[T]):
 Alias = NT[List[T]]
 
 an: Alias[str]
-reveal_type(an)  # N: Revealed type is "Tuple[builtins.int, builtins.list[builtins.str], fallback=__main__.NT[builtins.list[builtins.str]]]"
-Alias[str](key=0, value=0)  # E: Argument "value" to "NT" has incompatible type "int"; expected "List[str]"
+reveal_type(an)  # N: Revealed type is "tuple[builtins.int, builtins.list[builtins.str], fallback=__main__.NT[builtins.list[builtins.str]]]"
+Alias[str](key=0, value=0)  # E: Argument "value" to "NT" has incompatible type "int"; expected "list[str]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
 
@@ -1261,7 +1261,7 @@ nts: NT[str]
 reveal_type(nts.foo())  # N: Revealed type is "builtins.str"
 
 nti = NT.from_value(1)
-reveal_type(nti)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
+reveal_type(nti)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
 NT[str].from_value(1)  # E: Argument 1 to "from_value" of "NT" has incompatible type "int"; expected "str"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
@@ -1279,7 +1279,7 @@ nti: NT[int]
 
 def foo(x: Tuple[int, ...]) -> None: ...
 foo(nti)
-foo(nts)  # E: Argument 1 to "foo" has incompatible type "NT[str]"; expected "Tuple[int, ...]"
+foo(nts)  # E: Argument 1 to "foo" has incompatible type "NT[str]"; expected "tuple[int, ...]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
 
@@ -1297,10 +1297,10 @@ x: Tuple[int, ...]
 
 S = TypeVar("S")
 def foo(x: S, y: S) -> S: ...
-reveal_type(foo(nti, nti))  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
+reveal_type(foo(nti, nti))  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
 
-reveal_type(foo(nti, nts))  # N: Revealed type is "Tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]"
-reveal_type(foo(nts, nti))  # N: Revealed type is "Tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]"
+reveal_type(foo(nti, nts))  # N: Revealed type is "tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]"
+reveal_type(foo(nts, nti))  # N: Revealed type is "tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]"
 
 reveal_type(foo(nti, x))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 reveal_type(foo(nts, x))  # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]"
@@ -1314,13 +1314,13 @@ from typing import NamedTuple, TypeVar
 
 T = TypeVar("T")
 NT = NamedTuple("NT", [("key", int), ("value", T)])
-reveal_type(NT)  # N: Revealed type is "def [T] (key: builtins.int, value: T`1) -> Tuple[builtins.int, T`1, fallback=__main__.NT[T`1]]"
+reveal_type(NT)  # N: Revealed type is "def [T] (key: builtins.int, value: T`1) -> tuple[builtins.int, T`1, fallback=__main__.NT[T`1]]"
 
 nts: NT[str]
-reveal_type(nts)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]"
+reveal_type(nts)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]"
 
 nti = NT(key=0, value=0)
-reveal_type(nti)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
+reveal_type(nti)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]"
 NT[str](key=0, value=0)  # E: Argument "value" to "NT" has incompatible type "int"; expected "str"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
@@ -1362,7 +1362,7 @@ class NT(NamedTuple, Generic[T]):
             return self._replace()
 
 class SNT(NT[int]): ...
-reveal_type(SNT("test", 42).meth())  # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.SNT]"
+reveal_type(SNT("test", 42).meth())  # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.SNT]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
 
@@ -1500,7 +1500,7 @@ def g(x: Union[A, B, str]) -> Union[A, B, str]:
     if isinstance(x, str):
         return x
     else:
-        reveal_type(x)  # N: Revealed type is "Union[Tuple[Tuple[builtins.str, fallback=__main__.AKey], fallback=__main__.A], Tuple[Tuple[builtins.str, fallback=__main__.BKey], fallback=__main__.B]]"
+        reveal_type(x)  # N: Revealed type is "Union[tuple[tuple[builtins.str, fallback=__main__.AKey], fallback=__main__.A], tuple[tuple[builtins.str, fallback=__main__.BKey], fallback=__main__.B]]"
         return x._replace()
 
 # no errors should be raised above.
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index dc2cfd46d9ad..4afed0e3ec86 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -53,24 +53,24 @@ else:
 
 x3: Union[NamedTuple1, NamedTuple2]
 if x3.key == "A":
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
     reveal_type(x3.key)     # N: Revealed type is "Literal['A']"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
     reveal_type(x3.key)     # N: Revealed type is "Literal['B']"
 if x3[0] == "A":
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
     reveal_type(x3[0])      # N: Revealed type is "Literal['A']"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
     reveal_type(x3[0])      # N: Revealed type is "Literal['B']"
 
 x4: Union[Tuple1, Tuple2]
 if x4[0] == "A":
-    reveal_type(x4)         # N: Revealed type is "Tuple[Literal['A'], builtins.int]"
+    reveal_type(x4)         # N: Revealed type is "tuple[Literal['A'], builtins.int]"
     reveal_type(x4[0])      # N: Revealed type is "Literal['A']"
 else:
-    reveal_type(x4)         # N: Revealed type is "Tuple[Literal['B'], builtins.str]"
+    reveal_type(x4)         # N: Revealed type is "tuple[Literal['B'], builtins.str]"
     reveal_type(x4[0])      # N: Revealed type is "Literal['B']"
 
 x5: Union[TypedDict1, TypedDict2]
@@ -142,24 +142,24 @@ else:
 
 x3: Union[NamedTuple1, NamedTuple2]
 if x3.key is Key.A:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
     reveal_type(x3.key)     # N: Revealed type is "Literal[__main__.Key.A]"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
     reveal_type(x3.key)     # N: Revealed type is "Literal[__main__.Key.B]"
 if x3[0] is Key.A:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
     reveal_type(x3[0])      # N: Revealed type is "Literal[__main__.Key.A]"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
     reveal_type(x3[0])      # N: Revealed type is "Literal[__main__.Key.B]"
 
 x4: Union[Tuple1, Tuple2]
 if x4[0] is Key.A:
-    reveal_type(x4)         # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int]"
+    reveal_type(x4)         # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int]"
     reveal_type(x4[0])      # N: Revealed type is "Literal[__main__.Key.A]"
 else:
-    reveal_type(x4)         # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str]"
+    reveal_type(x4)         # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str]"
     reveal_type(x4[0])      # N: Revealed type is "Literal[__main__.Key.B]"
 
 x5: Union[TypedDict1, TypedDict2]
@@ -213,19 +213,19 @@ else:
 
 x3: Union[NamedTuple1, NamedTuple2]
 if isinstance(x3.key, int):
-    reveal_type(x3)         # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]"
 if isinstance(x3[0], int):
-    reveal_type(x3)         # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]"
+    reveal_type(x3)         # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]"
 else:
-    reveal_type(x3)         # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]"
+    reveal_type(x3)         # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]"
 
 x4: Union[Tuple1, Tuple2]
 if isinstance(x4[0], int):
-    reveal_type(x4)         # N: Revealed type is "Tuple[builtins.int]"
+    reveal_type(x4)         # N: Revealed type is "tuple[builtins.int]"
 else:
-    reveal_type(x4)         # N: Revealed type is "Tuple[builtins.str]"
+    reveal_type(x4)         # N: Revealed type is "tuple[builtins.str]"
 
 x5: Union[TypedDict1, TypedDict2]
 if isinstance(x5["key"], int):
@@ -414,7 +414,7 @@ ok_mixture: Union[KeyedObject, KeyedNamedTuple]
 if ok_mixture.key is Key.A:
     reveal_type(ok_mixture)             # N: Revealed type is "__main__.KeyedObject"
 else:
-    reveal_type(ok_mixture)             # N: Revealed type is "Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]"
+    reveal_type(ok_mixture)             # N: Revealed type is "tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]"
 
 impossible_mixture: Union[KeyedObject, KeyedTypedDict]
 if impossible_mixture.key is Key.A:     # E: Item "KeyedTypedDict" of "Union[KeyedObject, KeyedTypedDict]" has no attribute "key"
@@ -431,15 +431,15 @@ weird_mixture: Union[KeyedTypedDict, KeyedNamedTuple]
 if weird_mixture["key"] is Key.B:       # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \
                                         # N: Possible overload variants: \
                                         # N:     def __getitem__(self, int, /) -> Literal[Key.C] \
-                                        # N:     def __getitem__(self, slice, /) -> Tuple[Literal[Key.C], ...]
-    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
+                                        # N:     def __getitem__(self, slice, /) -> tuple[Literal[Key.C], ...]
+    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
 else:
-    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
+    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
 
 if weird_mixture[0] is Key.B:           # E: TypedDict key must be a string literal; expected one of ("key")
-    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
+    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
 else:
-    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
+    reveal_type(weird_mixture)          # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
@@ -1106,7 +1106,7 @@ T = TypeVar("T", A, B)
 
 def f(cls: Type[T]) -> T:
     if issubclass(cls, A):
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.A]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.A]"
         x: bool
         if x:
             return A()
@@ -1260,14 +1260,14 @@ class C: pass
 
 def f(t: Type[C]) -> None:
     if type(t) is M:
-        reveal_type(t)  # N: Revealed type is "Type[__main__.C]"
+        reveal_type(t)  # N: Revealed type is "type[__main__.C]"
     else:
-        reveal_type(t)  # N: Revealed type is "Type[__main__.C]"
+        reveal_type(t)  # N: Revealed type is "type[__main__.C]"
     if type(t) is not M:
-        reveal_type(t)  # N: Revealed type is "Type[__main__.C]"
+        reveal_type(t)  # N: Revealed type is "type[__main__.C]"
     else:
-        reveal_type(t)  # N: Revealed type is "Type[__main__.C]"
-    reveal_type(t)  # N: Revealed type is "Type[__main__.C]"
+        reveal_type(t)  # N: Revealed type is "type[__main__.C]"
+    reveal_type(t)  # N: Revealed type is "type[__main__.C]"
 
 [case testNarrowingUsingTypeVar]
 from typing import Type, TypeVar
@@ -1502,14 +1502,14 @@ from typing import Tuple
 
 x: Tuple[int, ...]
 if len(x) == 3:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
 else:
     reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 
 if len(x) != 3:
     reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 else:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenTypeUnaffected]
@@ -1541,8 +1541,8 @@ VarTuple = Union[Tuple[int, int], Tuple[int, int, int]]
 x: VarTuple
 y: VarTuple
 if len(x) == len(y) == 3:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
-    reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenFinal]
@@ -1553,7 +1553,7 @@ VarTuple = Union[Tuple[int, int], Tuple[int, int, int]]
 x: VarTuple
 fin: Final = 3
 if len(x) == fin:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenGreaterThan]
@@ -1563,24 +1563,24 @@ VarTuple = Union[Tuple[int], Tuple[int, int], Tuple[int, int, int]]
 
 x: VarTuple
 if len(x) > 1:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int]"
 
 if len(x) < 2:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int]"
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 
 if len(x) >= 2:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int]"
 
 if len(x) <= 2:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenBothSidesUnionTuples]
@@ -1595,9 +1595,9 @@ VarTuple = Union[
 
 x: VarTuple
 if 2 <= len(x) <= 3:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenGreaterThanHomogeneousTupleShort]
@@ -1608,9 +1608,9 @@ VarTuple = Tuple[int, ...]
 
 x: VarTuple
 if len(x) < 3:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[()], tuple[builtins.int], tuple[builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
+    reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
 reveal_type(x)  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/len.pyi]
 
@@ -1633,9 +1633,9 @@ from typing import Tuple
 
 x: Tuple[int, ...]
 if 1 < len(x) < 4:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[()], tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]]"
 reveal_type(x)  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/len.pyi]
 
@@ -1647,12 +1647,12 @@ x: Union[Tuple[int, int], Tuple[int, int, int]]
 if len(x) >= 4:
     reveal_type(x) # E: Statement is unreachable
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 
 if len(x) < 2:
     reveal_type(x) # E: Statement is unreachable
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenMixedTypes]
@@ -1661,17 +1661,17 @@ from typing import Tuple, List, Union
 x: Union[Tuple[int, int], Tuple[int, int, int], List[int]]
 a = b = c = 0
 if len(x) == 3:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]"
     a, b, c = x
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.list[builtins.int]]"
     a, b = x
 
 if len(x) != 3:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.list[builtins.int]]"
     a, b = x
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]"
     a, b, c = x
 [builtins fixtures/len.pyi]
 
@@ -1682,14 +1682,14 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 def foo(x: Tuple[int, Unpack[Ts], str]) -> None:
     if len(x) == 5:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
 
     if len(x) != 5:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenTypeVarTupleGreaterThan]
@@ -1699,17 +1699,17 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 def foo(x: Tuple[int, Unpack[Ts], str]) -> None:
     if len(x) > 5:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
         reveal_type(x[5])  # N: Revealed type is "builtins.object"
         reveal_type(x[-6])  # N: Revealed type is "builtins.object"
         reveal_type(x[-1])  # N: Revealed type is "builtins.str"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
 
     if len(x) < 5:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
         x[5]  # E: Tuple index out of range \
               # N: Variadic tuple can have length 5
         x[-6]  # E: Tuple index out of range \
@@ -1730,23 +1730,23 @@ def foo(x: Tuple[int, Unpack[Ts], str]) -> None:
     if len(x) == 1:
         reveal_type(x)  # E: Statement is unreachable
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
 
     if len(x) != 1:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     else:
         reveal_type(x)  # E: Statement is unreachable
 
 def bar(x: Tuple[int, Unpack[Ts], str]) -> None:
     if len(x) >= 2:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     else:
         reveal_type(x)  # E: Statement is unreachable
 
     if len(x) < 2:
         reveal_type(x)  # E: Statement is unreachable
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenVariadicTupleEquals]
@@ -1755,14 +1755,14 @@ from typing_extensions import Unpack
 
 def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None:
     if len(x) == 4:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 
     if len(x) != 4:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenVariadicTupleGreaterThan]
@@ -1771,16 +1771,16 @@ from typing_extensions import Unpack
 
 def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None:
     if len(x) > 3:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], Tuple[builtins.int, builtins.float, builtins.str]]"
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.str], tuple[builtins.int, builtins.float, builtins.str]]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 
     if len(x) < 3:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenVariadicTupleUnreachable]
@@ -1792,23 +1792,23 @@ def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None:
     if len(x) == 1:
         reveal_type(x)  # E: Statement is unreachable
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 
     if len(x) != 1:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
     else:
         reveal_type(x)  # E: Statement is unreachable
 
 def bar(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None:
     if len(x) >= 2:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
     else:
         reveal_type(x)  # E: Statement is unreachable
 
     if len(x) < 2:
         reveal_type(x)  # E: Statement is unreachable
     else:
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenBareExpressionPrecise]
@@ -1817,7 +1817,7 @@ from typing import Tuple
 
 x: Tuple[int, ...]
 assert x
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenBareExpressionTypeVarTuple]
@@ -1836,9 +1836,9 @@ from typing import Tuple, Optional
 
 x: Optional[Tuple[int, ...]]
 if x:
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[()], None]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[()], None]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenBareExpressionWithNoneImprecise]
@@ -1857,14 +1857,14 @@ from typing import Any
 
 x: Any
 if isinstance(x, (list, tuple)) and len(x) == 0:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[()], builtins.list[Any]]"
 else:
     reveal_type(x)  # N: Revealed type is "Any"
 reveal_type(x)  # N: Revealed type is "Any"
 
 x1: Any
 if isinstance(x1, (list, tuple)) and len(x1) > 1:
-    reveal_type(x1)  # N: Revealed type is "Union[Tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]], builtins.list[Any]]"
+    reveal_type(x1)  # N: Revealed type is "Union[tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]], builtins.list[Any]]"
 else:
     reveal_type(x1)  # N: Revealed type is "Any"
 reveal_type(x1)  # N: Revealed type is "Any"
@@ -1875,7 +1875,7 @@ from typing import Any
 
 x: Any
 if isinstance(x, (list, tuple)) and len(x) == 0:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[()], builtins.list[Any]]"
 else:
     reveal_type(x)  # N: Revealed type is "Any"
 reveal_type(x)  # N: Revealed type is "Any"
@@ -1900,15 +1900,15 @@ x: VarTuple
 
 supported: Literal[2]
 if len(x) == supported:
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 
 not_supported_yet: Literal[2, 3]
 if len(x) == not_supported_yet:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenUnionOfVariadicTuples]
@@ -1916,7 +1916,7 @@ from typing import Tuple, Union
 
 x: Union[Tuple[int, ...], Tuple[str, ...]]
 if len(x) == 2:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]"
 else:
     reveal_type(x)  # N: Revealed type is "Union[builtins.tuple[builtins.int, ...], builtins.tuple[builtins.str, ...]]"
 [builtins fixtures/len.pyi]
@@ -1934,9 +1934,9 @@ class Point3D(NamedTuple):
 
 x: Union[Point2D, Point3D]
 if len(x) == 2:
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Point2D]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Point2D]"
 else:
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenTupleSubclass]
@@ -1947,7 +1947,7 @@ class Ints(Tuple[int, ...]):
 
 x: Ints
 if len(x) == 2:
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Ints]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Ints]"
     reveal_type(x.size)  # N: Revealed type is "builtins.int"
 else:
     reveal_type(x)  # N: Revealed type is "__main__.Ints"
@@ -1991,15 +1991,15 @@ x: Union[Tuple[int, int], Tuple[int, int, int]]
 
 n: int
 if len(x) == n:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 
 a: Any
 if len(x) == a:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 else:
-    reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
+    reveal_type(x)  # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingLenUnionWithUnreachable]
@@ -2012,7 +2012,7 @@ def f(x: Union[int, Sequence[int]]) -> None:
         and isinstance(x[0], int)
         and isinstance(x[1], int)
     ):
-        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+        reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/len.pyi]
 
 [case testNarrowingIsSubclassNoneType1]
@@ -2020,9 +2020,9 @@ from typing import Type, Union
 
 def f(cls: Type[Union[None, int]]) -> None:
     if issubclass(cls, int):
-        reveal_type(cls)  # N: Revealed type is "Type[builtins.int]"
+        reveal_type(cls)  # N: Revealed type is "type[builtins.int]"
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[None]"
+        reveal_type(cls)  # N: Revealed type is "type[None]"
 [builtins fixtures/isinstance.pyi]
 
 [case testNarrowingIsSubclassNoneType2]
@@ -2030,9 +2030,9 @@ from typing import Type, Union
 
 def f(cls: Type[Union[None, int]]) -> None:
     if issubclass(cls, type(None)):
-        reveal_type(cls)  # N: Revealed type is "Type[None]"
+        reveal_type(cls)  # N: Revealed type is "type[None]"
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[builtins.int]"
+        reveal_type(cls)  # N: Revealed type is "type[builtins.int]"
 [builtins fixtures/isinstance.pyi]
 
 [case testNarrowingIsSubclassNoneType3]
@@ -2042,9 +2042,9 @@ NoneType_ = type(None)
 
 def f(cls: Type[Union[None, int]]) -> None:
     if issubclass(cls, NoneType_):
-        reveal_type(cls)  # N: Revealed type is "Type[None]"
+        reveal_type(cls)  # N: Revealed type is "type[None]"
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[builtins.int]"
+        reveal_type(cls)  # N: Revealed type is "type[builtins.int]"
 [builtins fixtures/isinstance.pyi]
 
 [case testNarrowingIsSubclassNoneType4]
@@ -2055,9 +2055,9 @@ from typing import Type, Union
 
 def f(cls: Type[Union[None, int]]) -> None:
     if issubclass(cls, NoneType):
-        reveal_type(cls)  # N: Revealed type is "Type[None]"
+        reveal_type(cls)  # N: Revealed type is "type[None]"
     else:
-        reveal_type(cls)  # N: Revealed type is "Type[builtins.int]"
+        reveal_type(cls)  # N: Revealed type is "type[builtins.int]"
 [builtins fixtures/isinstance.pyi]
 
 [case testNarrowingIsInstanceNoIntersectionWithFinalTypeAndNoneType]
@@ -2351,7 +2351,7 @@ while f():
     y = 1
 reveal_type(y)  # N: Revealed type is "Union[builtins.int, None]"
 
-z = []  # E: Need type annotation for "z" (hint: "z: List[] = ...")
+z = []  # E: Need type annotation for "z" (hint: "z: list[] = ...")
 def g() -> None:
     for i in range(2):
         while f():
@@ -2361,7 +2361,7 @@ def g() -> None:
 
 class A:
     def g(self) -> None:
-        z = []  # E: Need type annotation for "z" (hint: "z: List[] = ...")
+        z = []  # E: Need type annotation for "z" (hint: "z: list[] = ...")
         for i in range(2):
             while f():
                 if z:
diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test
index b6756abafc49..1d489d54409f 100644
--- a/test-data/unit/check-newsemanal.test
+++ b/test-data/unit/check-newsemanal.test
@@ -863,8 +863,8 @@ In = NamedTuple('In', [('s', str), ('t', Other)])
 Out = NamedTuple('Out', [('x', In), ('y', Other)])
 o: Out
 i: In
-reveal_type(o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
-reveal_type(o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]"
+reveal_type(o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
+reveal_type(o.x)  # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]"
 reveal_type(o.y)  # N: Revealed type is "__main__.Other"
 reveal_type(o.x.t)  # N: Revealed type is "__main__.Other"
 reveal_type(i.t)  # N: Revealed type is "__main__.Other"
@@ -880,8 +880,8 @@ class Out(NamedTuple):
     x: In
     y: Other
 
-reveal_type(o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
-reveal_type(o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]"
+reveal_type(o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
+reveal_type(o.x)  # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]"
 reveal_type(o.y)  # N: Revealed type is "__main__.Other"
 reveal_type(o.x.t)  # N: Revealed type is "__main__.Other"
 reveal_type(i.t)  # N: Revealed type is "__main__.Other"
@@ -898,8 +898,8 @@ from typing import NamedTuple
 o: C.Out
 i: C.In
 
-reveal_type(o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]"
-reveal_type(o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]"
+reveal_type(o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]"
+reveal_type(o.x)  # N: Revealed type is "tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]"
 reveal_type(o.y)  # N: Revealed type is "__main__.C.Other"
 reveal_type(o.x.t)  # N: Revealed type is "__main__.C.Other"
 reveal_type(i.t)  # N: Revealed type is "__main__.C.Other"
@@ -917,8 +917,8 @@ from typing import NamedTuple
 o: C.Out
 i: C.In
 
-reveal_type(o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]"
-reveal_type(o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]"
+reveal_type(o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]"
+reveal_type(o.x)  # N: Revealed type is "tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]"
 reveal_type(o.y)  # N: Revealed type is "__main__.C.Other"
 reveal_type(o.x.t)  # N: Revealed type is "__main__.C.Other"
 reveal_type(i.t)  # N: Revealed type is "__main__.C.Other"
@@ -944,8 +944,8 @@ class C:
         self.o: Out
 
 c = C()
-reveal_type(c.o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6], __main__.Other@7, fallback=__main__.C.Out@5]"
-reveal_type(c.o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6]"
+reveal_type(c.o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6], __main__.Other@7, fallback=__main__.C.Out@5]"
+reveal_type(c.o.x)  # N: Revealed type is "tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewAnalyzerNamedTupleClassNestedMethod]
@@ -964,16 +964,16 @@ class C:
         self.o: Out
 
 c = C()
-reveal_type(c.o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9], __main__.Other@12, fallback=__main__.C.Out@5]"
-reveal_type(c.o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]"
-reveal_type(c.o.method())  # N: Revealed type is "Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]"
+reveal_type(c.o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9], __main__.Other@12, fallback=__main__.C.Out@5]"
+reveal_type(c.o.x)  # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]"
+reveal_type(c.o.method())  # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewAnalyzerNamedTupleClassForwardMethod]
 from typing import NamedTuple
 
 n: NT
-reveal_type(n.get_other())  # N: Revealed type is "Tuple[builtins.str, fallback=__main__.Other]"
+reveal_type(n.get_other())  # N: Revealed type is "tuple[builtins.str, fallback=__main__.Other]"
 reveal_type(n.get_other().s)  # N: Revealed type is "builtins.str"
 
 class NT(NamedTuple):
@@ -995,8 +995,8 @@ class SubO(Out): pass
 
 o: SubO
 
-reveal_type(SubO._make)  # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]"
-reveal_type(o._replace(y=Other()))  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]"
+reveal_type(SubO._make)  # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]"
+reveal_type(o._replace(y=Other()))  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewAnalyzerNamedTupleBaseClass]
@@ -1009,10 +1009,10 @@ class Out(NamedTuple('Out', [('x', In), ('y', Other)])):
     pass
 
 o: Out
-reveal_type(o)  # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
-reveal_type(o.x)  # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]"
+reveal_type(o)  # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
+reveal_type(o.x)  # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]"
 reveal_type(o.x.t)  # N: Revealed type is "__main__.Other"
-reveal_type(Out._make)  # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
+reveal_type(Out._make)  # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewAnalyzerIncompleteRefShadowsBuiltin1]
@@ -1078,7 +1078,7 @@ from b import C
 import a
 [file a.py]
 C = 1
-from b import C  # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int")
+from b import C  # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int")
 
 [file b.py]
 import a
@@ -1092,7 +1092,7 @@ import a
 C = 1
 MYPY = False
 if MYPY:  # Tweak processing order
-    from b import *  # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int")
+    from b import *  # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int")
 
 [file b.py]
 import a
@@ -1104,7 +1104,7 @@ class B: ...
 import a
 [file a.py]
 C = 1
-from b import *  # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int")
+from b import *  # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int")
 
 [file b.py]
 MYPY = False
@@ -1432,7 +1432,7 @@ from a import x
 
 class B(List[B], Generic[T]): pass
 T = TypeVar('T')
-reveal_type(x)  # N: Revealed type is "b.B[Tuple[builtins.int, builtins.int]]"
+reveal_type(x)  # N: Revealed type is "b.B[tuple[builtins.int, builtins.int]]"
 [builtins fixtures/list.pyi]
 
 [case testNewAnalyzerAliasToNotReadyClassInGeneric]
@@ -1449,7 +1449,7 @@ from a import x
 
 class B(List[B]): pass
 
-reveal_type(x)  # N: Revealed type is "Tuple[b.B, b.B]"
+reveal_type(x)  # N: Revealed type is "tuple[b.B, b.B]"
 [builtins fixtures/list.pyi]
 
 [case testNewAnalyzerAliasToNotReadyClassDoubleGeneric]
@@ -1570,7 +1570,7 @@ import a
 [file a.py]
 from b import B
 def func() -> B: ...
-reveal_type(func())  # N: Revealed type is "builtins.list[Tuple[b.C, b.C]]"
+reveal_type(func())  # N: Revealed type is "builtins.list[tuple[b.C, b.C]]"
 
 [file b.py]
 from typing import List, Tuple
@@ -1597,7 +1597,7 @@ abl: List[Tuple[A, B]]
 abd = {a: b for a, b in abl}
 x: Dict[B, A] = {a: b for a, b in abl} # E: Key expression in dictionary comprehension has incompatible type "A"; expected type "B" \
   # E: Value expression in dictionary comprehension has incompatible type "B"; expected type "A"
-y: A = {a: b for a, b in abl} # E: Incompatible types in assignment (expression has type "Dict[A, B]", variable has type "A")
+y: A = {a: b for a, b in abl} # E: Incompatible types in assignment (expression has type "dict[A, B]", variable has type "A")
 class A: pass
 class B: pass
 [builtins fixtures/dict.pyi]
@@ -1840,7 +1840,7 @@ x.extend(y)
 import b
 [file a.py]
 from b import x
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [file b.py]
 import a
 x = (1, 2)
@@ -1850,7 +1850,7 @@ x = (1, 2)
 import a
 [file a.py]
 from b import x
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [file b.py]
 import a
 x = (1, 2)
@@ -1974,7 +1974,7 @@ S = TypeVar('S', bound='Tuple[G[A], ...]')
 
 class GG(Generic[S]): pass
 
-g: GG[Tuple[G[B], G[C]]] # E: Type argument "Tuple[G[B], G[C]]" of "GG" must be a subtype of "Tuple[G[A], ...]" \
+g: GG[Tuple[G[B], G[C]]] # E: Type argument "tuple[G[B], G[C]]" of "GG" must be a subtype of "tuple[G[A], ...]" \
                          # E: Type argument "B" of "G" must be a subtype of "A" \
                          # E: Type argument "C" of "G" must be a subtype of "A"
 
@@ -2176,7 +2176,7 @@ def test() -> None:
     reveal_type(y.x)  # N: Revealed type is "builtins.int"
     reveal_type(y[0])  # N: Revealed type is "builtins.int"
     x: A
-    reveal_type(x)  # N: Revealed type is "__main__.G@7[Tuple[builtins.int, fallback=__main__.C@5]]"
+    reveal_type(x)  # N: Revealed type is "__main__.G@7[tuple[builtins.int, fallback=__main__.C@5]]"
 [builtins fixtures/list.pyi]
 
 [case testNewAnalyzerDuplicateTypeVar]
@@ -2314,7 +2314,7 @@ from typing import cast, NamedTuple
 
 x = cast('C', None)
 
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]"
 reveal_type(x.x)  # N: Revealed type is "builtins.int"
 
 C = NamedTuple('C', [('x', int)])
@@ -2746,7 +2746,7 @@ class C(Generic[T]):
     pass
 
 C = C[int]  # E: Cannot assign to a type \
-            # E: Incompatible types in assignment (expression has type "Type[C[int]]", variable has type "Type[C[T]]")
+            # E: Incompatible types in assignment (expression has type "type[C[int]]", variable has type "type[C[T]]")
 x: C
 reveal_type(x) # N: Revealed type is "__main__.C[Any]"
 
diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test
index a696eb2932fe..df36a1ce4dd2 100644
--- a/test-data/unit/check-newsyntax.test
+++ b/test-data/unit/check-newsyntax.test
@@ -21,7 +21,7 @@ from typing import Dict, Any
 d: Dict[int, str] = {}
 d[42] = 'ab'
 d[42] = 42  # E: Incompatible types in assignment (expression has type "int", target has type "str")
-d['ab'] = 'ab'  # E: Invalid index type "str" for "Dict[int, str]"; expected type "int"
+d['ab'] = 'ab'  # E: Invalid index type "str" for "dict[int, str]"; expected type "int"
 [builtins fixtures/dict.pyi]
 [out]
 
diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test
index a0a30079f062..f7219e721222 100644
--- a/test-data/unit/check-newtype.test
+++ b/test-data/unit/check-newtype.test
@@ -44,7 +44,7 @@ main:12: error: Argument 1 to "TcpPacketId" has incompatible type "int"; expecte
 from typing import NewType, Tuple
 TwoTuple = NewType('TwoTuple', Tuple[int, str])
 a = TwoTuple((3, "a"))
-b = TwoTuple(("a", 3))  # E: Argument 1 to "TwoTuple" has incompatible type "Tuple[str, int]"; expected "Tuple[int, str]"
+b = TwoTuple(("a", 3))  # E: Argument 1 to "TwoTuple" has incompatible type "tuple[str, int]"; expected "tuple[int, str]"
 
 reveal_type(a[0])  # N: Revealed type is "builtins.int"
 reveal_type(a[1])  # N: Revealed type is "builtins.str"
@@ -291,7 +291,7 @@ Foo = NewType('Foo', Union[int, float])  # E: Argument 2 to NewType(...) must be
 
 [case testNewTypeWithTypeTypeFails]
 from typing import NewType, Type
-Foo = NewType('Foo', Type[int])  # E: Argument 2 to NewType(...) must be subclassable (got "Type[int]")
+Foo = NewType('Foo', Type[int])  # E: Argument 2 to NewType(...) must be subclassable (got "type[int]")
 a = Foo(type(3))
 [builtins fixtures/args.pyi]
 [out]
diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test
index 5ed4c15f470e..679906b0e00e 100644
--- a/test-data/unit/check-optional.test
+++ b/test-data/unit/check-optional.test
@@ -201,7 +201,7 @@ x.append(1)  # E: Argument 1 to "append" of "list" has incompatible type "int";
 [case testInferNonOptionalListType]
 x = []
 x.append(1)
-x()  # E: "List[int]" not callable
+x()  # E: "list[int]" not callable
 [builtins fixtures/list.pyi]
 
 [case testInferOptionalDictKeyValueTypes]
@@ -209,13 +209,13 @@ x = {None: None}
 x["bar"] = 1
 [builtins fixtures/dict.pyi]
 [out]
-main:2: error: Invalid index type "str" for "Dict[None, None]"; expected type "None"
+main:2: error: Invalid index type "str" for "dict[None, None]"; expected type "None"
 main:2: error: Incompatible types in assignment (expression has type "int", target has type "None")
 
 [case testInferNonOptionalDictType]
 x = {}
 x["bar"] = 1
-x()  # E: "Dict[str, int]" not callable
+x()  # E: "dict[str, int]" not callable
 [builtins fixtures/dict.pyi]
 
 [case testNoneClassVariable]
@@ -781,7 +781,7 @@ asdf(x)
 \[mypy-a]
 strict_optional = False
 [out]
-main:4: error: Argument 1 to "asdf" has incompatible type "List[str]"; expected "List[Optional[str]]"
+main:4: error: Argument 1 to "asdf" has incompatible type "list[str]"; expected "list[Optional[str]]"
 main:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
 main:4: note: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/list.pyi]
@@ -978,7 +978,7 @@ def f23(b: bool) -> None:
 
 def f1(b: bool) -> None:
     if b:
-        x = []  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+        x = []  # E: Need type annotation for "x" (hint: "x: list[] = ...")
     else:
         x = None
 
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index 243568c54253..0ccc8a2a353c 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -620,7 +620,7 @@ t: type
 a: A
 
 if int():
-    a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A")
+    a = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "A")
     t = A
 
 class A:
@@ -811,7 +811,7 @@ n = 1
 m = 1
 n = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
 m = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
-f(list_object) # E: Argument 1 to "f" has incompatible type "List[object]"; expected "List[int]"
+f(list_object) # E: Argument 1 to "f" has incompatible type "list[object]"; expected "list[int]"
 [builtins fixtures/list.pyi]
 
 [case testOverlappingOverloadSignatures]
@@ -1147,7 +1147,7 @@ def f(x: str) -> None: pass
 f(1.1)
 f('')
 f(1)
-f(()) # E: No overload variant of "f" matches argument type "Tuple[()]" \
+f(()) # E: No overload variant of "f" matches argument type "tuple[()]" \
       # N: Possible overload variants: \
       # N:     def f(x: float) -> None \
       # N:     def f(x: str) -> None
@@ -1216,13 +1216,13 @@ from typing import overload
 def f(x: int, y: str) -> int: pass
 @overload
 def f(*x: str) -> str: pass
-f(*(1,))() # E: No overload variant of "f" matches argument type "Tuple[int]" \
+f(*(1,))() # E: No overload variant of "f" matches argument type "tuple[int]" \
            # N: Possible overload variants: \
            # N:     def f(x: int, y: str) -> int \
            # N:     def f(*x: str) -> str
 f(*('',))() # E: "str" not callable
 f(*(1, ''))() # E: "int" not callable
-f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "Tuple[int, str, int]" \
+f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "tuple[int, str, int]" \
                  # N: Possible overload variants: \
                  # N:     def f(x: int, y: str) -> int \
                  # N:     def f(*x: str) -> str
@@ -1239,7 +1239,7 @@ def f(x: int, y: List[int] = None) -> int: pass
 def f(x: int, y: List[str] = None) -> int: pass
 f(y=[1], x=0)() # E: "int" not callable
 f(y=[''], x=0)() # E: "int" not callable
-a = f(y=[['']], x=0) # E: List item 0 has incompatible type "List[str]"; expected "int"
+a = f(y=[['']], x=0) # E: List item 0 has incompatible type "list[str]"; expected "int"
 reveal_type(a)  # N: Revealed type is "builtins.int"
 [builtins fixtures/list.pyi]
 
@@ -1299,7 +1299,7 @@ def g(x: U, y: V) -> None:
     f(y) # E: No overload variant of "f" matches argument type "V" \
          # N: Possible overload variants: \
          # N:     def [T: str] f(x: T) -> T \
-         # N:     def [T: str] f(x: List[T]) -> None
+         # N:     def [T: str] f(x: list[T]) -> None
     a = f([x])
     reveal_type(a)  # N: Revealed type is "None"
     f([y]) # E: Value of type variable "T" of "f" cannot be "V"
@@ -1414,11 +1414,11 @@ main:17: note: Revealed type is "builtins.int"
 main:18: note: Revealed type is "builtins.str"
 main:19: note: Revealed type is "Any"
 main:20: note: Revealed type is "Union[builtins.int, builtins.str]"
-main:21: error: Argument 1 to "foo" has incompatible type "List[bool]"; expected "List[int]"
+main:21: error: Argument 1 to "foo" has incompatible type "list[bool]"; expected "list[int]"
 main:21: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
 main:21: note: Consider using "Sequence" instead, which is covariant
-main:22: error: Argument 1 to "foo" has incompatible type "List[object]"; expected "List[int]"
-main:23: error: Argument 1 to "foo" has incompatible type "List[Union[int, str]]"; expected "List[int]"
+main:22: error: Argument 1 to "foo" has incompatible type "list[object]"; expected "list[int]"
+main:23: error: Argument 1 to "foo" has incompatible type "list[Union[int, str]]"; expected "list[int]"
 
 [case testOverloadAgainstEmptyCollections]
 from typing import overload, List
@@ -1482,7 +1482,7 @@ class A(Generic[T]):
 
 b = A()  # type: A[Tuple[int, int]]
 b.f((0, 0))
-b.f((0, '')) # E: Argument 1 to "f" of "A" has incompatible type "Tuple[int, str]"; expected "Tuple[int, int]"
+b.f((0, '')) # E: Argument 1 to "f" of "A" has incompatible type "tuple[int, str]"; expected "tuple[int, int]"
 [builtins fixtures/tuple.pyi]
 
 [case testSingleOverloadStub]
@@ -1554,14 +1554,14 @@ def f(x: int, y: Tuple[str, ...]) -> None: pass
 @overload
 def f(x: int, y: str) -> None: pass
 f(1, ('2', '3'))
-f(1, (2, '3')) # E: Argument 2 to "f" has incompatible type "Tuple[int, str]"; expected "Tuple[str, ...]"
+f(1, (2, '3')) # E: Argument 2 to "f" has incompatible type "tuple[int, str]"; expected "tuple[str, ...]"
 f(1, ('2',))
 f(1, '2')
-f(1, (2, 3)) # E: Argument 2 to "f" has incompatible type "Tuple[int, int]"; expected "Tuple[str, ...]"
+f(1, (2, 3)) # E: Argument 2 to "f" has incompatible type "tuple[int, int]"; expected "tuple[str, ...]"
 x = ('2', '3')  # type: Tuple[str, ...]
 f(1, x)
 y = (2, 3)  # type: Tuple[int, ...]
-f(1, y) # E: Argument 2 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[str, ...]"
+f(1, y) # E: Argument 2 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[str, ...]"
 [builtins fixtures/tuple.pyi]
 
 [case testCallableSpecificOverload]
@@ -2539,7 +2539,7 @@ x: List[int]
 reveal_type(foo(*x))  # N: Revealed type is "__main__.C"
 
 y: List[str]
-foo(*y)  # E: No overload variant of "foo" matches argument type "List[str]" \
+foo(*y)  # E: No overload variant of "foo" matches argument type "list[str]" \
          # N: Possible overload variants: \
          # N:     def foo(x: int) -> A \
          # N:     def foo(x: int, y: int) -> B \
@@ -2626,8 +2626,8 @@ def f(*xs: int) -> Tuple[int, ...]: ...
 def f(*args): pass
 
 i: int
-reveal_type(f(i))           # N: Revealed type is "Tuple[builtins.int]"
-reveal_type(f(i, i))        # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(i))           # N: Revealed type is "tuple[builtins.int]"
+reveal_type(f(i, i))        # N: Revealed type is "tuple[builtins.int, builtins.int]"
 reveal_type(f(i, i, i))     # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 
 reveal_type(f(*[]))         # N: Revealed type is "builtins.tuple[builtins.int, ...]"
@@ -2648,8 +2648,8 @@ def f(*args): pass
 
 i: int
 reveal_type(f(*()))         # N: Revealed type is "builtins.tuple[builtins.int, ...]"
-reveal_type(f(*(i,)))       # N: Revealed type is "Tuple[builtins.int]"
-reveal_type(f(*(i, i)))     # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(*(i,)))       # N: Revealed type is "tuple[builtins.int]"
+reveal_type(f(*(i, i)))     # N: Revealed type is "tuple[builtins.int, builtins.int]"
 reveal_type(f(*(i, i, i)))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/tuple.pyi]
 
@@ -2668,8 +2668,8 @@ C = NamedTuple('C', [('a', int), ('b', int), ('c', int)])
 a: A
 b: B
 c: C
-reveal_type(f(*a))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
-reveal_type(f(*b))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(*a))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
+reveal_type(f(*b))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 reveal_type(f(*c))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/tuple.pyi]
 
@@ -2708,8 +2708,8 @@ a: A
 b: B
 c: C
 
-reveal_type(f(**a))  # N: Revealed type is "Tuple[builtins.int]"
-reveal_type(f(**b))  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(f(**a))  # N: Revealed type is "tuple[builtins.int]"
+reveal_type(f(**b))  # N: Revealed type is "tuple[builtins.int, builtins.int]"
 reveal_type(f(**c))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
@@ -3497,12 +3497,12 @@ def t_is_same_bound(arg1: T1, arg2: S) -> Tuple[T1, S]:
     x3: Union[List[S], List[Tuple[S, T1]]]
     y3: S
     Dummy[T1]().foo(x3, y3)  # E: Cannot infer type argument 1 of "foo" of "Dummy" \
-                             # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[List[S], List[Tuple[S, T1]]]"; expected "List[Tuple[T1, Any]]"
+                             # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[list[S], list[tuple[S, T1]]]"; expected "list[tuple[T1, Any]]"
 
     x4: Union[List[int], List[Tuple[C, int]]]
     y4: int
     reveal_type(Dummy[C]().foo(x4, y4))  # N: Revealed type is "Union[builtins.int, __main__.C]"
-    Dummy[A]().foo(x4, y4)               # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[List[int], List[Tuple[C, int]]]"; expected "List[Tuple[A, int]]"
+    Dummy[A]().foo(x4, y4)               # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[list[int], list[tuple[C, int]]]"; expected "list[tuple[A, int]]"
 
     return arg1, arg2
 
@@ -4264,7 +4264,7 @@ class Wrapper:
 
     @classmethod    # E: Overloaded function implementation cannot produce return type of signature 1
     def foo(cls, x: Union[int, str]) -> str:
-        reveal_type(cls)          # N: Revealed type is "Type[__main__.Wrapper]"
+        reveal_type(cls)          # N: Revealed type is "type[__main__.Wrapper]"
         reveal_type(cls.other())  # N: Revealed type is "builtins.str"
         return "..."
 
@@ -4589,10 +4589,10 @@ class Child(Parent):
     def child_only(self) -> int: pass
 
 x: Union[int, str]
-reveal_type(Parent.foo(3))                  # N: Revealed type is "Type[__main__.Parent]"
-reveal_type(Child.foo(3))                   # N: Revealed type is "Type[__main__.Child]"
+reveal_type(Parent.foo(3))                  # N: Revealed type is "type[__main__.Parent]"
+reveal_type(Child.foo(3))                   # N: Revealed type is "type[__main__.Child]"
 reveal_type(Child.foo("..."))               # N: Revealed type is "builtins.str"
-reveal_type(Child.foo(x))                   # N: Revealed type is "Union[Type[__main__.Child], builtins.str]"
+reveal_type(Child.foo(x))                   # N: Revealed type is "Union[type[__main__.Child], builtins.str]"
 reveal_type(Child.foo(3)().child_only())    # N: Revealed type is "builtins.int"
 [builtins fixtures/classmethod.pyi]
 
@@ -5079,7 +5079,7 @@ a = multiple_plausible(Other())  # E: No overload variant of "multiple_plausible
                                  # N:     def multiple_plausible(x: str) -> str
 reveal_type(a)                   # N: Revealed type is "Any"
 
-b = single_plausible(Other)      # E: Argument 1 to "single_plausible" has incompatible type "Type[Other]"; expected "Type[int]"
+b = single_plausible(Other)      # E: Argument 1 to "single_plausible" has incompatible type "type[Other]"; expected "type[int]"
 reveal_type(b)                   # N: Revealed type is "builtins.int"
 
 c = single_plausible([Other()])  # E: List item 0 has incompatible type "Other"; expected "str"
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
index 6f01b15e11f6..085f6fe59809 100644
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -1276,8 +1276,8 @@ def c3(f: Callable[P, int], *args, **kwargs) -> int: ...
 # It is ok to define,
 def c4(f: Callable[P, int], *args: int, **kwargs: str) -> int:
     # but not ok to call:
-    f(*args, **kwargs)  # E: Argument 1 has incompatible type "*Tuple[int, ...]"; expected "P.args" \
-                        # E: Argument 2 has incompatible type "**Dict[str, str]"; expected "P.kwargs"
+    f(*args, **kwargs)  # E: Argument 1 has incompatible type "*tuple[int, ...]"; expected "P.args" \
+                        # E: Argument 2 has incompatible type "**dict[str, str]"; expected "P.kwargs"
     return 1
 
 def f1(f: Callable[P, int], *args, **kwargs: P.kwargs) -> int: ...  # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
@@ -1306,8 +1306,8 @@ def c3(f: Callable[Concatenate[int, P], int], *args, **kwargs) -> int: ...
 # It is ok to define,
 def c4(f: Callable[Concatenate[int, P], int], *args: int, **kwargs: str) -> int:
     # but not ok to call:
-    f(1, *args, **kwargs)  # E: Argument 2 has incompatible type "*Tuple[int, ...]"; expected "P.args" \
-                           # E: Argument 3 has incompatible type "**Dict[str, str]"; expected "P.kwargs"
+    f(1, *args, **kwargs)  # E: Argument 2 has incompatible type "*tuple[int, ...]"; expected "P.args" \
+                           # E: Argument 3 has incompatible type "**dict[str, str]"; expected "P.kwargs"
     return 1
 
 def f1(f: Callable[Concatenate[int, P], int], *args, **kwargs: P.kwargs) -> int: ...  # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs"
@@ -2409,19 +2409,19 @@ def run2(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwar
     func2 = partial(func, **kwargs)
     p = [""]
     func2(1, *p)  # E: Too few arguments \
-                  # E: Argument 2 has incompatible type "*List[str]"; expected "P.args"
+                  # E: Argument 2 has incompatible type "*list[str]"; expected "P.args"
     func2(1, 2, *p)  # E: Too few arguments \
                      # E: Argument 2 has incompatible type "int"; expected "P.args" \
-                     # E: Argument 3 has incompatible type "*List[str]"; expected "P.args"
-    func2(1, *args, *p)  # E: Argument 3 has incompatible type "*List[str]"; expected "P.args"
-    func2(1, *p, *args)  # E: Argument 2 has incompatible type "*List[str]"; expected "P.args"
+                     # E: Argument 3 has incompatible type "*list[str]"; expected "P.args"
+    func2(1, *args, *p)  # E: Argument 3 has incompatible type "*list[str]"; expected "P.args"
+    func2(1, *p, *args)  # E: Argument 2 has incompatible type "*list[str]"; expected "P.args"
     return func2(1, *args)
 
 def run3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T:
     func2 = partial(func, 1, *args)
     d = {"":""}
     func2(**d)  # E: Too few arguments \
-                # E: Argument 1 has incompatible type "**Dict[str, str]"; expected "P.kwargs"
+                # E: Argument 1 has incompatible type "**dict[str, str]"; expected "P.kwargs"
     return func2(**kwargs)
 
 def run4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T:
@@ -2474,7 +2474,7 @@ def run(func: Callable[Concatenate[int, str, P], T], *args: P.args, **kwargs: P.
     func2(*args_prefix)  # E: Too few arguments
     func2(*args, *args_prefix)  # E: Argument 1 has incompatible type "*P.args"; expected "int" \
                                 # E: Argument 1 has incompatible type "*P.args"; expected "str" \
-                                # E: Argument 2 has incompatible type "*Tuple[int, str]"; expected "P.args"
+                                # E: Argument 2 has incompatible type "*tuple[int, str]"; expected "P.args"
     return func2(*args_prefix, *args)
 
 [builtins fixtures/paramspec.pyi]
@@ -2599,7 +2599,7 @@ def run3(predicate: Callable[Concatenate[int, str, _P], None], *args: _P.args, *
     base_ok: tuple[int, str]
     predicate(*base_ok, *args, **kwargs)
     base_bad: tuple[Union[int, str], ...]
-    predicate(*base_bad, *args, **kwargs)  # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "int" \
-                                           # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "str" \
-                                           # E: Argument 1 has incompatible type "*Tuple[Union[int, str], ...]"; expected "_P.args"
+    predicate(*base_bad, *args, **kwargs)  # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "int" \
+                                           # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "str" \
+                                           # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "_P.args"
 [builtins fixtures/paramspec.pyi]
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index c44854b7fc42..6415b5104296 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -31,7 +31,7 @@ class A:
 reveal_type(A)  # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A"
 A(1, [2])
 A(1, [2], '3', 4)
-A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
+A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
 A(1, [2], '3', 4, 5)  # E: Too many arguments for "A"
 [builtins fixtures/list.pyi]
 
@@ -49,7 +49,7 @@ class A:
 reveal_type(A)  # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A"
 A(1, [2])
 A(1, [2], '3', 4)
-A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
+A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
 A(1, [2], '3', 4, 5)  # E: Too many arguments for "A"
 [builtins fixtures/list.pyi]
 
@@ -67,7 +67,7 @@ class A:
 reveal_type(A)  # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A"
 A(1, [2])
 A(1, [2], '3', 4)
-A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
+A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str"
 A(1, [2], '3', 4, 5)  # E: Too many arguments for "A"
 [builtins fixtures/list.pyi]
 
@@ -120,7 +120,7 @@ class A:
 reveal_type(A)  # N: Revealed type is "def (a: Any, b: builtins.list[builtins.int], c: Any =, d: Any =) -> __main__.A"
 A(1, [2])
 A(1, [2], '3', 4)
-A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]"
+A(1, 2, 3, 4)  # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]"
 A(1, [2], '3', 4, 5)  # E: Too many arguments for "A"
 [builtins fixtures/list.pyi]
 
@@ -463,7 +463,7 @@ class A(Generic[T]):
     def bar(self) -> T:
         return self.x[0]
     def problem(self) -> T:
-        return self.x  # E: Incompatible return value type (got "List[T]", expected "T")
+        return self.x  # E: Incompatible return value type (got "list[T]", expected "T")
 reveal_type(A) # N: Revealed type is "def [T] (x: builtins.list[T`1], y: T`1) -> __main__.A[T`1]"
 a = A([1], 2)
 reveal_type(a)  # N: Revealed type is "__main__.A[builtins.int]"
@@ -495,7 +495,7 @@ class A(Generic[T]):
     def bar(self) -> T:
         return self.x[0]
     def problem(self) -> T:
-        return self.x  # E: Incompatible return value type (got "List[T]", expected "T")
+        return self.x  # E: Incompatible return value type (got "list[T]", expected "T")
 reveal_type(A) # N: Revealed type is "def [T] (x: typing.Iterable[T`1], y: T`1) -> __main__.A[T`1]"
 a1 = A([1], 2)
 reveal_type(a1)  # N: Revealed type is "__main__.A[builtins.int]"
@@ -668,7 +668,7 @@ class A(Generic[T]):
     x: Optional[T]
     @classmethod
     def clsmeth(cls) -> None:
-       reveal_type(cls)  # N: Revealed type is "Type[__main__.A[T`1]]"
+       reveal_type(cls)  # N: Revealed type is "type[__main__.A[T`1]]"
 
 [builtins fixtures/classmethod.pyi]
 
@@ -723,7 +723,7 @@ class A:
     b: str = attr.ib()
     @classmethod
     def new(cls) -> A:
-       reveal_type(cls)  # N: Revealed type is "Type[__main__.A]"
+       reveal_type(cls)  # N: Revealed type is "type[__main__.A]"
        return cls(6, 'hello')
     @classmethod
     def bad(cls) -> A:
@@ -758,7 +758,7 @@ class A:
 
     @classmethod
     def foo(cls, x: Union[int, str]) -> Union[int, str]:
-        reveal_type(cls)            # N: Revealed type is "Type[__main__.A]"
+        reveal_type(cls)            # N: Revealed type is "type[__main__.A]"
         reveal_type(cls.other())    # N: Revealed type is "builtins.str"
         return x
 
@@ -1207,7 +1207,7 @@ def my_factory() -> int:
     return 7
 @attr.s
 class A:
-    x: int = attr.ib(factory=list)  # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "int")
+    x: int = attr.ib(factory=list)  # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "int")
     y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
 [builtins fixtures/list.pyi]
 
@@ -1518,7 +1518,7 @@ class A:
     b: int = attr.ib()
     c: str = attr.ib()
 
-reveal_type(A.__attrs_attrs__)  # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
+reveal_type(A.__attrs_attrs__)  # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
 reveal_type(A.__attrs_attrs__[0])  # N: Revealed type is "attr.Attribute[builtins.int]"
 reveal_type(A.__attrs_attrs__.b)  # N: Revealed type is "attr.Attribute[builtins.int]"
 A.__attrs_attrs__.x  # E: "____main___A_AttrsAttributes__" has no attribute "x"
@@ -1533,7 +1533,7 @@ class A:
     b = attr.ib()
     c = attr.ib()
 
-reveal_type(A.__attrs_attrs__)  # N: Revealed type is "Tuple[attr.Attribute[Any], attr.Attribute[Any], fallback=__main__.A.____main___A_AttrsAttributes__]"
+reveal_type(A.__attrs_attrs__)  # N: Revealed type is "tuple[attr.Attribute[Any], attr.Attribute[Any], fallback=__main__.A.____main___A_AttrsAttributes__]"
 reveal_type(A.__attrs_attrs__[0])  # N: Revealed type is "attr.Attribute[Any]"
 reveal_type(A.__attrs_attrs__.b)  # N: Revealed type is "attr.Attribute[Any]"
 A.__attrs_attrs__.x  # E: "____main___A_AttrsAttributes__" has no attribute "x"
@@ -1548,7 +1548,7 @@ class A:
     b: int
     c: str
 
-reveal_type(A.__attrs_attrs__)  # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
+reveal_type(A.__attrs_attrs__)  # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
 reveal_type(A.__attrs_attrs__[0])  # N: Revealed type is "attr.Attribute[builtins.int]"
 reveal_type(A.__attrs_attrs__.b)  # N: Revealed type is "attr.Attribute[builtins.int]"
 A.__attrs_attrs__.x  # E: "____main___A_AttrsAttributes__" has no attribute "x"
@@ -1576,8 +1576,8 @@ def takes_attrs_instance(inst: AttrsInstance) -> None:
 takes_attrs_cls(A)
 takes_attrs_instance(A(1, ""))
 
-takes_attrs_cls(A(1, ""))  # E: Argument 1 to "takes_attrs_cls" has incompatible type "A"; expected "Type[AttrsInstance]"
-takes_attrs_instance(A)  # E: Argument 1 to "takes_attrs_instance" has incompatible type "Type[A]"; expected "AttrsInstance" # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object
+takes_attrs_cls(A(1, ""))  # E: Argument 1 to "takes_attrs_cls" has incompatible type "A"; expected "type[AttrsInstance]"
+takes_attrs_instance(A)  # E: Argument 1 to "takes_attrs_instance" has incompatible type "type[A]"; expected "AttrsInstance" # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object
 [builtins fixtures/plugin_attrs.pyi]
 
 [case testAttrsFields]
@@ -1589,7 +1589,7 @@ class A:
     b: int
     c: str
 
-reveal_type(f(A))  # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
+reveal_type(f(A))  # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
 reveal_type(f(A)[0])  # N: Revealed type is "attr.Attribute[builtins.int]"
 reveal_type(f(A).b)  # N: Revealed type is "attr.Attribute[builtins.int]"
 f(A).x  # E: "____main___A_AttrsAttributes__" has no attribute "x"
@@ -1613,7 +1613,7 @@ class A:
 TA = TypeVar('TA', bound=A)
 
 def f(t: TA) -> None:
-    reveal_type(fields(t))  # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
+    reveal_type(fields(t))  # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]"
     reveal_type(fields(t)[0])  # N: Revealed type is "attr.Attribute[builtins.int]"
     reveal_type(fields(t).b)  # N: Revealed type is "attr.Attribute[builtins.int]"
     fields(t).x  # E: "____main___A_AttrsAttributes__" has no attribute "x"
@@ -1632,8 +1632,8 @@ class A:
 if has(A):
     fields(A)
 else:
-    fields(A)  # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected "Type[AttrsInstance]"
-fields(None)  # E: Argument 1 to "fields" has incompatible type "None"; expected "Type[AttrsInstance]"
+    fields(A)  # E: Argument 1 to "fields" has incompatible type "type[A]"; expected "type[AttrsInstance]"
+fields(None)  # E: Argument 1 to "fields" has incompatible type "None"; expected "type[AttrsInstance]"
 fields(cast(Any, 42))
 fields(cast(Type[Any], 43))
 
@@ -1651,8 +1651,8 @@ class A:
         b, c = bc
         self.__attrs_init__(b, c)
 
-reveal_type(A)  # N: Revealed type is "def (bc: Tuple[builtins.int, builtins.str]) -> __main__.A"
-reveal_type(A.__init__)  # N: Revealed type is "def (self: __main__.A, bc: Tuple[builtins.int, builtins.str])"
+reveal_type(A)  # N: Revealed type is "def (bc: tuple[builtins.int, builtins.str]) -> __main__.A"
+reveal_type(A.__init__)  # N: Revealed type is "def (self: __main__.A, bc: tuple[builtins.int, builtins.str])"
 reveal_type(A.__attrs_init__)  # N: Revealed type is "def (self: __main__.A, b: builtins.int, c: builtins.str)"
 
 [builtins fixtures/plugin_attrs.pyi]
@@ -1729,14 +1729,14 @@ class Some:
     y: str
     z: bool
 
-reveal_type(Some.__slots__)  # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]"
+reveal_type(Some.__slots__)  # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.str]"
 
 @dataclass(slots=True)
 class Other:
     x: int
     y: str
 
-reveal_type(Other.__slots__)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(Other.__slots__)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 
 
 @dataclass
@@ -1744,7 +1744,7 @@ class NoSlots:
     x: int
     y: str
 
-NoSlots.__slots__  # E: "Type[NoSlots]" has no attribute "__slots__"
+NoSlots.__slots__  # E: "type[NoSlots]" has no attribute "__slots__"
 [builtins fixtures/plugin_attrs.pyi]
 
 [case testAttrsWithMatchArgs]
@@ -1759,8 +1759,8 @@ class ToMatch:
     z: int = attr.field(kw_only=True)
     i: int = attr.field(init=False)
 
-reveal_type(ToMatch(x=1, y=2, z=3).__match_args__)  # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]"
-reveal_type(ToMatch(1, 2, z=3).__match_args__)      # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]"
+reveal_type(ToMatch(x=1, y=2, z=3).__match_args__)  # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]"
+reveal_type(ToMatch(1, 2, z=3).__match_args__)      # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]"
 [builtins fixtures/plugin_attrs.pyi]
 
 [case testAttrsWithMatchArgsDefaultCase]
@@ -1773,7 +1773,7 @@ class ToMatch1:
     y: int
 
 t1: ToMatch1
-reveal_type(t1.__match_args__)  # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]"
+reveal_type(t1.__match_args__)  # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]"
 
 @attr.define
 class ToMatch2:
@@ -1781,7 +1781,7 @@ class ToMatch2:
     y: int
 
 t2: ToMatch2
-reveal_type(t2.__match_args__)  # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]"
+reveal_type(t2.__match_args__)  # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]"
 [builtins fixtures/plugin_attrs.pyi]
 
 [case testAttrsWithMatchArgsOverrideExisting]
@@ -1796,7 +1796,7 @@ class ToMatch:
     y: int
 
 # It works the same way runtime does:
-reveal_type(ToMatch(x=1, y=2).__match_args__)  # N: Revealed type is "Tuple[Literal['a']?, Literal['b']?]"
+reveal_type(ToMatch(x=1, y=2).__match_args__)  # N: Revealed type is "tuple[Literal['a']?, Literal['b']?]"
 
 @attr.s(auto_attribs=True)
 class WithoutMatch:
@@ -1804,7 +1804,7 @@ class WithoutMatch:
     x: int
     y: int
 
-reveal_type(WithoutMatch(x=1, y=2).__match_args__)  # N: Revealed type is "Tuple[Literal['a']?, Literal['b']?]"
+reveal_type(WithoutMatch(x=1, y=2).__match_args__)  # N: Revealed type is "tuple[Literal['a']?, Literal['b']?]"
 [builtins fixtures/plugin_attrs.pyi]
 
 [case testAttrsWithMatchArgsOldVersion]
@@ -2172,7 +2172,7 @@ class B:
 a_or_b: A[int] | B
 a2 = attrs.evolve(a_or_b, x=42, y=True)
 a2 = attrs.evolve(a_or_b, x=42, y=True, z='42')  # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected "Never"
-a2 = attrs.evolve(a_or_b, x=42, y=True, w={})  # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never"
+a2 = attrs.evolve(a_or_b, x=42, y=True, w={})  # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "dict[Never, Never]"; expected "Never"
 
 [builtins fixtures/plugin_attrs.pyi]
 
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 34e3f3e88080..7f11774fbfff 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -715,7 +715,7 @@ c: C
 var: P2[int, int] = c
 var2: P2[int, str] = c # E: Incompatible types in assignment (expression has type "C", variable has type "P2[int, str]") \
                        # N: Following member(s) of "C" have conflicts: \
-                       # N:     attr2: expected "Tuple[int, str]", got "Tuple[int, int]"
+                       # N:     attr2: expected "tuple[int, str]", got "tuple[int, int]"
 
 class D(Generic[T]):
     attr1: T
@@ -973,7 +973,7 @@ class B:
 t: P1
 t = A() # E: Incompatible types in assignment (expression has type "A", variable has type "P1") \
         # N: Following member(s) of "A" have conflicts: \
-        # N:     attr1: expected "Sequence[P2]", got "List[B]"
+        # N:     attr1: expected "Sequence[P2]", got "list[B]"
 [builtins fixtures/list.pyi]
 
 [case testMutuallyRecursiveProtocolsTypesWithSubteMismatchWriteable]
@@ -1607,13 +1607,13 @@ def f(cls: Type[P]) -> P:
 def g() -> P:
     return P()  # E: Cannot instantiate protocol class "P"
 
-f(P)  # E: Only concrete class can be given where "Type[P]" is expected
+f(P)  # E: Only concrete class can be given where "type[P]" is expected
 f(B)  # OK
 f(C)  # OK
 x: Type[P1]
 xbad: Type[Pbad]
 f(x)  # OK
-f(xbad)  # E: Argument 1 to "f" has incompatible type "Type[Pbad]"; expected "Type[P]"
+f(xbad)  # E: Argument 1 to "f" has incompatible type "type[Pbad]"; expected "type[P]"
 
 [case testInstantiationProtocolInTypeForAliases]
 from typing import Type, Protocol
@@ -1631,7 +1631,7 @@ Alias = P
 GoodAlias = C
 Alias()  # E: Cannot instantiate protocol class "P"
 GoodAlias()
-f(Alias)  # E: Only concrete class can be given where "Type[P]" is expected
+f(Alias)  # E: Only concrete class can be given where "type[P]" is expected
 f(GoodAlias)
 
 [case testInstantiationProtocolInTypeForVariables]
@@ -1648,14 +1648,14 @@ class C:
 var: Type[P]
 var()
 if int():
-    var = P # E: Can only assign concrete classes to a variable of type "Type[P]"
+    var = P # E: Can only assign concrete classes to a variable of type "type[P]"
     var = B # OK
     var = C # OK
 
 var_old = None # type: Type[P] # Old syntax for variable annotations
 var_old()
 if int():
-    var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]"
+    var_old = P # E: Can only assign concrete classes to a variable of type "type[P]"
     var_old = B # OK
     var_old = C # OK
 
@@ -1825,7 +1825,7 @@ def f(x: MyProto[int]) -> None:
 f(t)  # OK
 
 y: MyProto[str]
-y = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "MyProto[str]")
+y = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "MyProto[str]")
 [builtins fixtures/isinstancelist.pyi]
 
 [case testBasicNamedTupleStructuralSubtyping]
@@ -1943,7 +1943,7 @@ class Actual:
     def __call__(self, arg: int) -> str: pass
 
 def fun(cb: Callable[[T], S]) -> Tuple[T, S]: pass
-reveal_type(fun(Actual())) # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(fun(Actual())) # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 -- Standard protocol types (SupportsInt, Sized, etc.)
@@ -2439,9 +2439,9 @@ cls: Type[Union[C, E]]
 issubclass(cls, PBad)  # E: Only protocols that don't have non-method members can be used with issubclass() \
                        # N: Protocol "PBad" has non-method member(s): x
 if issubclass(cls, P):
-    reveal_type(cls)  # N: Revealed type is "Type[__main__.C]"
+    reveal_type(cls)  # N: Revealed type is "type[__main__.C]"
 else:
-    reveal_type(cls)  # N: Revealed type is "Type[__main__.E]"
+    reveal_type(cls)  # N: Revealed type is "type[__main__.E]"
 
 @runtime_checkable
 class POverload(Protocol):
@@ -2491,7 +2491,7 @@ def call(x: int, y: str) -> Tuple[int, str]: ...
 def func(caller: Caller[T, S]) -> Tuple[T, S]:
     pass
 
-reveal_type(func(call))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(func(call))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -2531,7 +2531,7 @@ def func(caller: Caller) -> None:
     pass
 
 func(call)
-func(bad)  # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" \
+func(bad)  # E: Argument 1 to "func" has incompatible type "Callable[[T], tuple[T, T]]"; expected "Caller" \
            # N: "Caller.__call__" has type "Callable[[Arg(int, 'x')], int]"
 [builtins fixtures/tuple.pyi]
 [out]
@@ -2711,7 +2711,7 @@ class A(Protocol[T, S]):
 
 def f() -> int: ...
 def test(func: A[T, S]) -> Tuple[T, S]: ...
-reveal_type(test(f))  # N: Revealed type is "Tuple[builtins.str, builtins.int]"
+reveal_type(test(f))  # N: Revealed type is "tuple[builtins.str, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testProtocolsAlwaysABCs]
@@ -2917,7 +2917,7 @@ class Blooper:
         reveal_type([self, x])  # N: Revealed type is "builtins.list[builtins.object]"
 
 class Gleemer:
-    flap = []  # E: Need type annotation for "flap" (hint: "flap: List[] = ...")
+    flap = []  # E: Need type annotation for "flap" (hint: "flap: list[] = ...")
 
     def gleem(self, x: Flapper) -> None:
         reveal_type([self, x])  # N: Revealed type is "builtins.list[builtins.object]"
@@ -3287,7 +3287,7 @@ class C:
 def foo(t: Template) -> None: ...
 foo(B())  # E: Argument 1 to "foo" has incompatible type "B"; expected "Template" \
           # N: Following member(s) of "B" have conflicts: \
-          # N:     Meta: expected "Type[__main__.Template.Meta]", got "Type[__main__.B.Meta]"
+          # N:     Meta: expected "type[__main__.Template.Meta]", got "type[__main__.B.Meta]"
 foo(C())  # OK
 
 [case testProtocolClassObjectAttribute]
@@ -3308,10 +3308,10 @@ class D:
 def test(arg: P) -> None: ...
 test(A)  # OK
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     foo: expected "int", got "str"
-test(D)  # E: Argument 1 to "test" has incompatible type "Type[D]"; expected "P" \
+test(D)  # E: Argument 1 to "test" has incompatible type "type[D]"; expected "P" \
          # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "D"
 
 [case testProtocolClassObjectClassVarRejected]
@@ -3324,7 +3324,7 @@ class B:
     foo: ClassVar[int]
 
 def test(arg: P) -> None: ...
-test(B)  # E: Argument 1 to "test" has incompatible type "Type[B]"; expected "P" \
+test(B)  # E: Argument 1 to "test" has incompatible type "type[B]"; expected "P" \
          # N: ClassVar protocol member P.foo can never be matched by a class object
 
 [case testProtocolClassObjectPropertyRejected]
@@ -3344,11 +3344,11 @@ class D:
 
 def test(arg: P) -> None: ...
 # TODO: skip type mismatch diagnostics in this case.
-test(B)  # E: Argument 1 to "test" has incompatible type "Type[B]"; expected "P" \
+test(B)  # E: Argument 1 to "test" has incompatible type "type[B]"; expected "P" \
          # N: Following member(s) of "B" have conflicts: \
          # N:     foo: expected "int", got "Callable[[B], int]" \
          # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "B"
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "C"
 test(D)  # OK
 [builtins fixtures/property.pyi]
@@ -3366,7 +3366,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo(obj: Any) -> int \
@@ -3386,7 +3386,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo(obj: B) -> int \
@@ -3420,7 +3420,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         @overload \
@@ -3448,7 +3448,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo() -> int \
@@ -3471,7 +3471,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo() -> int \
@@ -3495,12 +3495,12 @@ class C(AA[str]): ...
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
-         # N:         def foo(obj: Any) -> List[int] \
+         # N:         def foo(obj: Any) -> list[int] \
          # N:     Got: \
-         # N:         def foo(self: A[List[str]]) -> List[str]
+         # N:         def foo(self: A[list[str]]) -> list[str]
 [builtins fixtures/list.pyi]
 
 [case testProtocolClassObjectGenericClassMethod]
@@ -3520,12 +3520,12 @@ class C(AA[str]): ...
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
-         # N:         def foo() -> List[int] \
+         # N:         def foo() -> list[int] \
          # N:     Got: \
-         # N:         def foo() -> List[str]
+         # N:         def foo() -> list[str]
 [builtins fixtures/isinstancelist.pyi]
 
 [case testProtocolClassObjectSelfTypeInstanceMethod]
@@ -3542,7 +3542,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def [T] foo(arg: T) -> T \
@@ -3565,7 +3565,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo() -> B \
@@ -3589,7 +3589,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: "C" has constructor incompatible with "__call__" of "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
@@ -3611,7 +3611,7 @@ class C:
 
 def test(arg: P) -> None: ...
 test(B)  # OK
-test(C)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(C)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: "C" has constructor incompatible with "__call__" of "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
@@ -3635,7 +3635,7 @@ class C:
     def __call__(self, el: str) -> None:
         return None
 
-p: P = C  # E: Incompatible types in assignment (expression has type "Type[C]", variable has type "P") \
+p: P = C  # E: Incompatible types in assignment (expression has type "type[C]", variable has type "P") \
           # N: Following member(s) of "C" have conflicts: \
           # N:     Expected: \
           # N:         def __call__(app: int) -> Callable[[str], None] \
@@ -3667,10 +3667,10 @@ c: Type[C]
 d: Type[D]
 test(a)  # OK
 test(b)  # OK
-test(c)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(c)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     foo: expected "int", got "str"
-test(d)  # E: Argument 1 to "test" has incompatible type "Type[D]"; expected "P" \
+test(d)  # E: Argument 1 to "test" has incompatible type "type[D]"; expected "P" \
          # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "D"
 
 [case testProtocolTypeTypeInstanceMethod]
@@ -3688,7 +3688,7 @@ def test(arg: P) -> None: ...
 b: Type[B]
 c: Type[C]
 test(b)  # OK
-test(c)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(c)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo(cls: Any) -> int \
@@ -3712,7 +3712,7 @@ def test(arg: P) -> None: ...
 b: Type[B]
 c: Type[C]
 test(b)  # OK
-test(c)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(c)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def foo() -> int \
@@ -3736,7 +3736,7 @@ def test(arg: P) -> None: ...
 b: Type[B]
 c: Type[C]
 test(b)  # OK
-test(c)  # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \
+test(c)  # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
          # N: Following member(s) of "C" have conflicts: \
          # N:     Expected: \
          # N:         def [T] foo(arg: T) -> T \
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index af3982f6accd..fdf6b25f3591 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -240,7 +240,7 @@ match m:
         reveal_type(a)  # N: Revealed type is "builtins.int"
         reveal_type(b)  # N: Revealed type is "builtins.str"
         reveal_type(c)  # N: Revealed type is "builtins.bool"
-        reveal_type(m)  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]"
+        reveal_type(m)  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]"
 [builtins fixtures/list.pyi]
 
 [case testMatchSequencePatternTupleTooLong]
@@ -270,7 +270,7 @@ m: Tuple[object, object]
 
 match m:
     case [1, "str"]:
-        reveal_type(m)  # N: Revealed type is "Tuple[Literal[1], Literal['str']]"
+        reveal_type(m)  # N: Revealed type is "tuple[Literal[1], Literal['str']]"
 [builtins fixtures/list.pyi]
 
 [case testMatchSequencePatternTupleStarred]
@@ -282,7 +282,7 @@ match m:
         reveal_type(a)  # N: Revealed type is "builtins.int"
         reveal_type(b)  # N: Revealed type is "builtins.list[builtins.str]"
         reveal_type(c)  # N: Revealed type is "builtins.bool"
-        reveal_type(m)  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]"
+        reveal_type(m)  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]"
 [builtins fixtures/list.pyi]
 
 [case testMatchSequencePatternTupleStarredUnion]
@@ -294,13 +294,13 @@ match m:
         reveal_type(a)  # N: Revealed type is "builtins.int"
         reveal_type(b)  # N: Revealed type is "builtins.list[Union[builtins.str, builtins.float]]"
         reveal_type(c)  # N: Revealed type is "builtins.bool"
-        reveal_type(m)  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.float, builtins.bool]"
+        reveal_type(m)  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.float, builtins.bool]"
 [builtins fixtures/list.pyi]
 
 [case testMatchSequencePatternTupleStarredTooShort]
 from typing import Tuple
 m: Tuple[int]
-reveal_type(m)  # N: Revealed type is "Tuple[builtins.int]"
+reveal_type(m)  # N: Revealed type is "tuple[builtins.int]"
 
 match m:
     case [a, *b, c]:
@@ -326,7 +326,7 @@ class Example:
 SubClass: type[Example]
 
 match [SubClass("a"), SubClass("b")]:
-    case [SubClass(value), *rest]:  # E: Expected type in class pattern; found "Type[__main__.Example]"
+    case [SubClass(value), *rest]:  # E: Expected type in class pattern; found "type[__main__.Example]"
         reveal_type(value)  # E: Cannot determine type of "value" \
                             # N: Revealed type is "Any"
         reveal_type(rest)  # N: Revealed type is "builtins.list[__main__.Example]"
@@ -1519,43 +1519,43 @@ m2: Tuple[int | str]
 
 match m2:
     case (int(),):
-        reveal_type(m2)  # N: Revealed type is "Tuple[builtins.int]"
+        reveal_type(m2)  # N: Revealed type is "tuple[builtins.int]"
     case r2:
-        reveal_type(m2)  # N: Revealed type is "Tuple[builtins.str]"
+        reveal_type(m2)  # N: Revealed type is "tuple[builtins.str]"
 
 m3: Tuple[Union[int, str]]
 
 match m3:
     case (1,):
-        reveal_type(m3)  # N: Revealed type is "Tuple[Literal[1]]"
+        reveal_type(m3)  # N: Revealed type is "tuple[Literal[1]]"
     case r2:
-        reveal_type(m3)  # N: Revealed type is "Tuple[Union[builtins.int, builtins.str]]"
+        reveal_type(m3)  # N: Revealed type is "tuple[Union[builtins.int, builtins.str]]"
 
 m4: Tuple[Literal[1], int]
 
 match m4:
     case (1, 5):
-        reveal_type(m4)  # N: Revealed type is "Tuple[Literal[1], Literal[5]]"
+        reveal_type(m4)  # N: Revealed type is "tuple[Literal[1], Literal[5]]"
     case (1, 6):
-        reveal_type(m4)  # N: Revealed type is "Tuple[Literal[1], Literal[6]]"
+        reveal_type(m4)  # N: Revealed type is "tuple[Literal[1], Literal[6]]"
     case _:
-        reveal_type(m4)  # N: Revealed type is "Tuple[Literal[1], builtins.int]"
+        reveal_type(m4)  # N: Revealed type is "tuple[Literal[1], builtins.int]"
 
 m5: Tuple[Literal[1, 2], Literal["a", "b"]]
 
 match m5:
     case (1, str()):
-        reveal_type(m5)  # N: Revealed type is "Tuple[Literal[1], Union[Literal['a'], Literal['b']]]"
+        reveal_type(m5)  # N: Revealed type is "tuple[Literal[1], Union[Literal['a'], Literal['b']]]"
     case _:
-        reveal_type(m5)  # N: Revealed type is "Tuple[Literal[2], Union[Literal['a'], Literal['b']]]"
+        reveal_type(m5)  # N: Revealed type is "tuple[Literal[2], Union[Literal['a'], Literal['b']]]"
 
 m6: Tuple[Literal[1, 2], Literal["a", "b"]]
 
 match m6:
     case (1, "a"):
-        reveal_type(m6)  # N: Revealed type is "Tuple[Literal[1], Literal['a']]"
+        reveal_type(m6)  # N: Revealed type is "tuple[Literal[1], Literal['a']]"
     case _:
-        reveal_type(m6)  # N: Revealed type is "Tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]"
+        reveal_type(m6)  # N: Revealed type is "tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1896,9 +1896,9 @@ class AnnAssign(stmt):
    value: str
    simple: int
 
-reveal_type(AST.__match_args__)  # N: Revealed type is "Tuple[()]"
-reveal_type(stmt.__match_args__)  # N: Revealed type is "Tuple[()]"
-reveal_type(AnnAssign.__match_args__)  # N: Revealed type is "Tuple[Literal['target']?, Literal['annotation']?, Literal['value']?, Literal['simple']?]"
+reveal_type(AST.__match_args__)  # N: Revealed type is "tuple[()]"
+reveal_type(stmt.__match_args__)  # N: Revealed type is "tuple[()]"
+reveal_type(AnnAssign.__match_args__)  # N: Revealed type is "tuple[Literal['target']?, Literal['annotation']?, Literal['value']?, Literal['simple']?]"
 
 AnnAssign.__match_args__ = ('a', 'b', 'c', 'd')  # E: Cannot assign to "__match_args__"
 __match_args__ = 0
@@ -2041,12 +2041,12 @@ S = TypeVar("S", int, str)
 
 def my_func(pairs: Iterable[tuple[S, S]]) -> None:
     for pair in pairs:
-        reveal_type(pair)  # N: Revealed type is "Tuple[builtins.int, builtins.int]" \
-                           # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+        reveal_type(pair)  # N: Revealed type is "tuple[builtins.int, builtins.int]" \
+                           # N: Revealed type is "tuple[builtins.str, builtins.str]"
         match pair:
             case _:
-                reveal_type(pair)  # N: Revealed type is "Tuple[builtins.int, builtins.int]" \
-                                   # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+                reveal_type(pair)  # N: Revealed type is "tuple[builtins.int, builtins.int]" \
+                                   # N: Revealed type is "tuple[builtins.str, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testPossiblyUndefinedMatch]
@@ -2236,7 +2236,7 @@ def match_stmt_error4(x: Optional[list[str]]) -> None:
     if x is None:
         x = ["a"]
     def nested() -> list[str]:
-        return x  # E: Incompatible return value type (got "Optional[List[str]]", expected "List[str]")
+        return x  # E: Incompatible return value type (got "Optional[list[str]]", expected "list[str]")
     match ["a"]:
         case [*x]:
             pass
@@ -2542,8 +2542,8 @@ from typing import Literal
 def x() -> tuple[Literal["test"]]: ...
 
 match x():
-    case (x,) if x == "test":  # E: Incompatible types in capture pattern (pattern captures type "Literal['test']", variable has type "Callable[[], Tuple[Literal['test']]]")
-        reveal_type(x)  # N: Revealed type is "def () -> Tuple[Literal['test']]"
+    case (x,) if x == "test":  # E: Incompatible types in capture pattern (pattern captures type "Literal['test']", variable has type "Callable[[], tuple[Literal['test']]]")
+        reveal_type(x)  # N: Revealed type is "def () -> tuple[Literal['test']]"
     case foo:
         foo
 
@@ -2598,7 +2598,7 @@ class K(NamedTuple):
 def f(t: T) -> None:
     match t:
         case T([K() as k]):
-            reveal_type(k)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.K]"
+            reveal_type(k)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.K]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewRedefineMatchBasics]
diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test
index c6d42660403e..09c8d6082365 100644
--- a/test-data/unit/check-python311.test
+++ b/test-data/unit/check-python311.test
@@ -81,9 +81,9 @@ reveal_type(coro)  # N: Revealed type is "def () -> typing.Coroutine[Any, Any, t
 [case testTypeVarTupleNewSyntaxAnnotations]
 Ints = tuple[int, int, int]
 x: tuple[str, *Ints]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.int, builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.int, builtins.int]"
 y: tuple[int, *tuple[int, ...]]
-reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
+reveal_type(y)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleNewSyntaxGenerics]
@@ -95,8 +95,8 @@ class C(Generic[T, *Ts]):
     attr: tuple[int, *Ts, str]
 
     def test(self) -> None:
-        reveal_type(self.attr)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`2], builtins.str]"
-        self.attr = ci  # E: Incompatible types in assignment (expression has type "C[*Tuple[int, ...]]", variable has type "Tuple[int, *Ts, str]")
+        reveal_type(self.attr)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`2], builtins.str]"
+        self.attr = ci  # E: Incompatible types in assignment (expression has type "C[*tuple[int, ...]]", variable has type "tuple[int, *Ts, str]")
     def meth(self, *args: *Ts) -> T: ...
 
 ci: C[*tuple[int, ...]]
@@ -135,11 +135,11 @@ myclass1 = MyClass(float)
 reveal_type(myclass1)  # N: Revealed type is "__main__.MyClass[builtins.float, None]"
 myclass2 = MyClass(float, float)
 reveal_type(myclass2)  # N: Revealed type is "__main__.MyClass[builtins.float, builtins.float]"
-myclass3 = MyClass(float, float, float)  # E: No overload variant of "MyClass" matches argument types "Type[float]", "Type[float]", "Type[float]" \
+myclass3 = MyClass(float, float, float)  # E: No overload variant of "MyClass" matches argument types "type[float]", "type[float]", "type[float]" \
                                          # N: Possible overload variants: \
                                          # N:     def [T1, T2] __init__(self) -> MyClass[None, None] \
-                                         # N:     def [T1, T2] __init__(self, Type[T1], /) -> MyClass[T1, None] \
-                                         # N:     def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2]
+                                         # N:     def [T1, T2] __init__(self, type[T1], /) -> MyClass[T1, None] \
+                                         # N:     def [T1, T2] __init__(type[T1], type[T2], /) -> MyClass[T1, T2]
 reveal_type(myclass3)  # N: Revealed type is "Any"
 [builtins fixtures/tuple.pyi]
 
@@ -169,7 +169,7 @@ x3: Alias3[int]  # E: Bad number of arguments for type alias, expected 0, given
 reveal_type(x3)  # N: Revealed type is "def (*Any) -> builtins.int"
 
 IntList = List[int]
-Alias4 = Callable[[*IntList], int]  # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple)
+Alias4 = Callable[[*IntList], int]  # E: "list[int]" cannot be unpacked (must be tuple or TypeVarTuple)
 x4: Alias4[int]  # E: Bad number of arguments for type alias, expected 0, given 1
 reveal_type(x4)  # N: Revealed type is "def (*Any) -> builtins.int"
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 2244548ea969..70ab59eb28e4 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -90,10 +90,10 @@ reveal_type(ident('x'))  # N: Revealed type is "builtins.str"
 a: TV  # E: Name "TV" is not defined
 
 def tup[T, S](x: T, y: S) -> tuple[T, S]:
-    reveal_type((x, y))  # N: Revealed type is "Tuple[T`-1, S`-2]"
+    reveal_type((x, y))  # N: Revealed type is "tuple[T`-1, S`-2]"
     return (x, y)
 
-reveal_type(tup(1, 'x'))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(tup(1, 'x'))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testPEP695GenericClassSyntax]
@@ -910,10 +910,10 @@ reveal_type(f)  # N: Revealed type is "def (builtins.str, Union[builtins.int, No
 
 [case testPEP695TypeVarTuple]
 def f[*Ts](t: tuple[*Ts]) -> tuple[*Ts]:
-    reveal_type(t)  # N: Revealed type is "Tuple[Unpack[Ts`-1]]"
+    reveal_type(t)  # N: Revealed type is "tuple[Unpack[Ts`-1]]"
     return t
 
-reveal_type(f((1, 'x')))  # N: Revealed type is "Tuple[Literal[1]?, Literal['x']?]"
+reveal_type(f((1, 'x')))  # N: Revealed type is "tuple[Literal[1]?, Literal['x']?]"
 a: tuple[int, ...]
 reveal_type(f(a))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 
@@ -933,7 +933,7 @@ from typing import Callable
 type C[*Ts] = tuple[*Ts, int]
 
 a: C[str, None]
-reveal_type(a)  # N: Revealed type is "Tuple[builtins.str, None, builtins.int]"
+reveal_type(a)  # N: Revealed type is "tuple[builtins.str, None, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testPEP695IncrementalFunction]
@@ -1370,8 +1370,8 @@ class C:
 class D(C):
     pass
 
-reveal_type(C.m(1))  # N: Revealed type is "Tuple[__main__.C, builtins.int]"
-reveal_type(D.m(1))  # N: Revealed type is "Tuple[__main__.D, builtins.int]"
+reveal_type(C.m(1))  # N: Revealed type is "tuple[__main__.C, builtins.int]"
+reveal_type(D.m(1))  # N: Revealed type is "tuple[__main__.D, builtins.int]"
 
 class E[T]:
     def m(self) -> Self:
@@ -1384,9 +1384,9 @@ class F[T](E[T]):
     pass
 
 reveal_type(E[int]().m())  # N: Revealed type is "__main__.E[builtins.int]"
-reveal_type(E[int]().mm(b'x'))  # N: Revealed type is "Tuple[__main__.E[builtins.int], builtins.bytes]"
+reveal_type(E[int]().mm(b'x'))  # N: Revealed type is "tuple[__main__.E[builtins.int], builtins.bytes]"
 reveal_type(F[str]().m())  # N: Revealed type is "__main__.F[builtins.str]"
-reveal_type(F[str]().mm(b'x'))  # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]"
+reveal_type(F[str]().mm(b'x'))  # N: Revealed type is "tuple[__main__.F[builtins.str], builtins.bytes]"
 [builtins fixtures/tuple.pyi]
 
 [case testPEP695CallAlias]
@@ -1487,7 +1487,7 @@ class C:
 reveal_type(C.a)  # N: Revealed type is "builtins.int"
 reveal_type(C.b)  # N: Revealed type is "Union[builtins.list[builtins.str], None]"
 
-C.A = str  # E: Incompatible types in assignment (expression has type "Type[str]", variable has type "TypeAliasType")
+C.A = str  # E: Incompatible types in assignment (expression has type "type[str]", variable has type "TypeAliasType")
 
 x: C.A
 y: C.B[int]
diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test
index f020b1602b99..65604754cc0f 100644
--- a/test-data/unit/check-python313.test
+++ b/test-data/unit/check-python313.test
@@ -14,7 +14,7 @@ def f2[**P1 = [int, str]](a: Callable[P1, None]) -> Callable[P1, None]: ...
 reveal_type(f2)  # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)"
 
 def f3[*Ts1 = *tuple[int, str]](a: tuple[*Ts1]) -> tuple[*Ts1]: ...
-reveal_type(f3)  # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] (a: Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]) -> Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]"
+reveal_type(f3)  # N: Revealed type is "def [Ts1 = Unpack[tuple[builtins.int, builtins.str]]] (a: tuple[Unpack[Ts1`-1 = Unpack[tuple[builtins.int, builtins.str]]]]) -> tuple[Unpack[Ts1`-1 = Unpack[tuple[builtins.int, builtins.str]]]]"
 
 
 class ClassA1[T1 = int]: ...
@@ -23,7 +23,7 @@ class ClassA3[*Ts1 = *tuple[int, str]]: ...
 
 reveal_type(ClassA1)  # N: Revealed type is "def [T1 = builtins.int] () -> __main__.ClassA1[T1`1 = builtins.int]"
 reveal_type(ClassA2)  # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] () -> __main__.ClassA2[P1`1 = [builtins.int, builtins.str]]"
-reveal_type(ClassA3)  # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[Tuple[builtins.int, builtins.str]]]]"
+reveal_type(ClassA3)  # N: Revealed type is "def [Ts1 = Unpack[tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[tuple[builtins.int, builtins.str]]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testPEP695TypeParameterDefaultValid]
@@ -141,7 +141,7 @@ reveal_type(func_b1(2))  # N: Revealed type is "def (builtins.int, builtins.str)
 
 def func_c1[*Ts = *tuple[int, str]](x: int | Callable[[*Ts], None]) -> tuple[*Ts]: ...
 # reveal_type(func_c1(callback1))  # Revealed type is "Tuple[str]"  # TODO
-reveal_type(func_c1(2))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(func_c1(2))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testPEP695TypeParameterDefaultClass1]
@@ -251,7 +251,7 @@ def func_c1(
     b: TC1[float],
 ) -> None:
     # reveal_type(a)  # Revealed type is "Tuple[builtins.int, builtins.str]"  # TODO
-    reveal_type(b)  # N: Revealed type is "Tuple[builtins.float]"
+    reveal_type(b)  # N: Revealed type is "tuple[builtins.float]"
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test
index f90baed0eb16..dd3f793fd02b 100644
--- a/test-data/unit/check-python38.test
+++ b/test-data/unit/check-python38.test
@@ -404,9 +404,9 @@ else:
 def get_things() -> Union[Tuple[Good], Tuple[Bad]]: ...
 
 if (things := get_things())[0].is_good:
-    reveal_type(things)  # N: Revealed type is "Tuple[__main__.Good]"
+    reveal_type(things)  # N: Revealed type is "tuple[__main__.Good]"
 else:
-    reveal_type(things)  # N: Revealed type is "Tuple[__main__.Bad]"
+    reveal_type(things)  # N: Revealed type is "tuple[__main__.Bad]"
 [builtins fixtures/list.pyi]
 
 [case testWalrusConditionalTypeCheck]
@@ -443,7 +443,7 @@ reveal_type(maybe_str)  # N: Revealed type is "Union[builtins.str, None]"
 from typing import List
 
 def check_partial_list() -> None:
-    if (x := []):  # E: Need type annotation for "x" (hint: "x: List[] = ...")
+    if (x := []):  # E: Need type annotation for "x" (hint: "x: list[] = ...")
         pass
 
     y: List[str]
@@ -790,7 +790,7 @@ dct: Dict[str, int] = {"a": "b", **other}
 main:5: error: Dict entry 0 has incompatible type "str": "str"; expected "str": "int"
     dct: Dict[str, int] = {"a": "b", **other}
                            ^~~~~~~~
-main:5: error: Unpacked dict entry 1 has incompatible type "Dict[str, str]"; expected "SupportsKeysAndGetItem[str, int]"
+main:5: error: Unpacked dict entry 1 has incompatible type "dict[str, str]"; expected "SupportsKeysAndGetItem[str, int]"
     dct: Dict[str, int] = {"a": "b", **other}
                                        ^~~~~
 
diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test
index 7f6e181a16ca..7ed5ea53c27e 100644
--- a/test-data/unit/check-recursive-types.test
+++ b/test-data/unit/check-recursive-types.test
@@ -12,7 +12,7 @@ if isinstance(x, list):
     x = x[0]
 
 class Bad: ...
-x = ["foo", {"bar": [Bad()]}]  # E: List item 0 has incompatible type "Bad"; expected "Union[str, List[JSON], Dict[str, JSON]]"
+x = ["foo", {"bar": [Bad()]}]  # E: List item 0 has incompatible type "Bad"; expected "Union[str, list[JSON], dict[str, JSON]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testRecursiveAliasBasicGenericSubtype]
@@ -54,7 +54,7 @@ reveal_type(flatten([1, [2, [3]]]))  # N: Revealed type is "builtins.list[builti
 
 class Bad: ...
 x: Nested[int] = [1, [2, [3]]]
-x = [1, [Bad()]]  # E: List item 1 has incompatible type "List[Bad]"; expected "Union[int, Nested[int]]"
+x = [1, [Bad()]]  # E: List item 1 has incompatible type "list[Bad]"; expected "Union[int, Nested[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testRecursiveAliasGenericInferenceNested]
@@ -95,7 +95,7 @@ A = Union[B, int]
 B = Callable[[C], int]
 C = Type[A]
 x: A
-reveal_type(x)  # N: Revealed type is "Union[def (Union[Type[def (...) -> builtins.int], Type[builtins.int]]) -> builtins.int, builtins.int]"
+reveal_type(x)  # N: Revealed type is "Union[def (Union[type[def (...) -> builtins.int], type[builtins.int]]) -> builtins.int, builtins.int]"
 
 [case testRecursiveAliasesProhibited-skip]
 from typing import Type, Callable, Union
@@ -161,7 +161,7 @@ y: C
 reveal_type(y.x)  # N: Revealed type is "builtins.int"
 reveal_type(y[0])  # N: Revealed type is "builtins.int"
 x: A
-reveal_type(x)  # N: Revealed type is "__main__.G[Tuple[builtins.int, fallback=__main__.C]]"
+reveal_type(x)  # N: Revealed type is "__main__.G[tuple[builtins.int, fallback=__main__.C]]"
 [builtins fixtures/list.pyi]
 
 [case testRecursiveAliasViaBaseClassImported]
@@ -189,7 +189,7 @@ class A(NamedTuple('A', [('attr', List[Exp])])): pass
 class B(NamedTuple('B', [('val', object)])): pass
 
 def my_eval(exp: Exp) -> int:
-    reveal_type(exp) # N: Revealed type is "Union[Tuple[builtins.list[...], fallback=__main__.A], Tuple[builtins.object, fallback=__main__.B]]"
+    reveal_type(exp) # N: Revealed type is "Union[tuple[builtins.list[...], fallback=__main__.A], tuple[builtins.object, fallback=__main__.B]]"
     if isinstance(exp, A):
         my_eval(exp[0][0])
         return my_eval(exp.attr[0])
@@ -413,7 +413,7 @@ S = Type[S]  # E: Type[...] can't contain "Type[...]"
 U = Type[Union[int, U]]  # E: Type[...] can't contain "Union[Type[...], Type[...]]" \
                          # E: Type[...] can't contain "Type[...]"
 x: U
-reveal_type(x)  # N: Revealed type is "Type[Any]"
+reveal_type(x)  # N: Revealed type is "type[Any]"
 
 D = List[F[List[T]]]  # E: Invalid recursive alias: type variable nesting on right hand side
 F = D[T]  # Error reported above
@@ -427,9 +427,9 @@ from typing import NamedTuple, Optional
 
 NT = NamedTuple("NT", [("x", Optional[NT]), ("y", int)])
 nt: NT
-reveal_type(nt)  # N: Revealed type is "Tuple[Union[..., None], builtins.int, fallback=__main__.NT]"
-reveal_type(nt.x)  # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
-reveal_type(nt[0])  # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
+reveal_type(nt)  # N: Revealed type is "tuple[Union[..., None], builtins.int, fallback=__main__.NT]"
+reveal_type(nt.x)  # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
+reveal_type(nt[0])  # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
 y: str
 if nt.x is not None:
     y = nt.x[0]  # E: Incompatible types in assignment (expression has type "Optional[NT]", variable has type "str")
@@ -440,9 +440,9 @@ from typing import NamedTuple, TypeVar, Tuple
 
 NT = NamedTuple("NT", [("x", NT), ("y", int)])
 nt: NT
-reveal_type(nt)  # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]"
-reveal_type(nt.x)  # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]"
-reveal_type(nt[0])  # N: Revealed type is "Tuple[Tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]"
+reveal_type(nt)  # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.NT]"
+reveal_type(nt.x)  # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.NT]"
+reveal_type(nt[0])  # N: Revealed type is "tuple[tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]"
 y: str
 if nt.x is not None:
     y = nt.x[0]  # E: Incompatible types in assignment (expression has type "NT", variable has type "str")
@@ -464,9 +464,9 @@ class NT(NamedTuple):
     y: int
 
 nt: NT
-reveal_type(nt)  # N: Revealed type is "Tuple[Union[..., None], builtins.int, fallback=__main__.NT]"
-reveal_type(nt.x)  # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
-reveal_type(nt[0])  # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
+reveal_type(nt)  # N: Revealed type is "tuple[Union[..., None], builtins.int, fallback=__main__.NT]"
+reveal_type(nt.x)  # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
+reveal_type(nt[0])  # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]"
 y: str
 if nt.x is not None:
     y = nt.x[0]  # E: Incompatible types in assignment (expression has type "Optional[NT]", variable has type "str")
@@ -491,7 +491,7 @@ class B(Tuple[B, int]):
     x: int
 C = NewType("C", B)
 b, _ = x
-reveal_type(b)  # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.B]"
+reveal_type(b)  # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.B]"
 reveal_type(b.x)  # N: Revealed type is "builtins.int"
 
 y: CNT
@@ -516,13 +516,13 @@ class B(NamedTuple):
     y: int
 
 n: A
-reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Tuple[..., builtins.int, fallback=__main__.B], ...], fallback=__main__.A]"
+reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[tuple[..., builtins.int, fallback=__main__.B], ...], fallback=__main__.A]"
 
 T = TypeVar("T")
 S = TypeVar("S")
 def foo(arg: Tuple[T, S]) -> Union[T, S]: ...
 x = foo(n)
-y: str = x  # E: Incompatible types in assignment (expression has type "Union[str, Tuple[B, ...]]", variable has type "str")
+y: str = x  # E: Incompatible types in assignment (expression has type "Union[str, tuple[B, ...]]", variable has type "str")
 [builtins fixtures/tuple.pyi]
 
 [case testMutuallyRecursiveNamedTuplesJoin]
@@ -535,7 +535,7 @@ class B(NamedTuple):
 A = NamedTuple('A', [('x', str), ('y', B)])
 n: B
 m: A
-s: str = n.x  # E: Incompatible types in assignment (expression has type "Tuple[A, int]", variable has type "str")
+s: str = n.x  # E: Incompatible types in assignment (expression has type "tuple[A, int]", variable has type "str")
 reveal_type(m[0]) # N: Revealed type is "builtins.str"
 lst = [m, n]
 
@@ -567,7 +567,7 @@ n = n.y.x
 
 t: Tuple[str, B]
 t = n
-t = m  # E: Incompatible types in assignment (expression has type "B", variable has type "Tuple[str, B]")
+t = m  # E: Incompatible types in assignment (expression has type "B", variable has type "tuple[str, B]")
 [builtins fixtures/tuple.pyi]
 
 [case testMutuallyRecursiveNamedTuplesCalls]
@@ -578,8 +578,8 @@ B = NamedTuple('B', [('x', A), ('y', int)])
 A = NamedTuple('A', [('x', str), ('y', 'B')])
 n: A
 def f(m: B) -> None: pass
-reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[..., builtins.int, fallback=__main__.B], fallback=__main__.A]"
-reveal_type(f) # N: Revealed type is "def (m: Tuple[Tuple[builtins.str, ..., fallback=__main__.A], builtins.int, fallback=__main__.B])"
+reveal_type(n) # N: Revealed type is "tuple[builtins.str, tuple[..., builtins.int, fallback=__main__.B], fallback=__main__.A]"
+reveal_type(f) # N: Revealed type is "def (m: tuple[tuple[builtins.str, ..., fallback=__main__.A], builtins.int, fallback=__main__.B])"
 f(n)  # E: Argument 1 to "f" has incompatible type "A"; expected "B"
 [builtins fixtures/tuple.pyi]
 
@@ -591,7 +591,7 @@ def foo() -> None:
               # N: Recursive types are not allowed at function scope
         y: int
     b: B
-    reveal_type(b)  # N: Revealed type is "Tuple[Any, builtins.int, fallback=__main__.B@3]"
+    reveal_type(b)  # N: Revealed type is "tuple[Any, builtins.int, fallback=__main__.B@3]"
 [builtins fixtures/tuple.pyi]
 
 [case testBasicRecursiveGenericNamedTuple]
@@ -606,7 +606,7 @@ class A: ...
 class B(A): ...
 
 nti: NT[int] = NT(key=0, value=NT(key=1, value=A()))  # E: Argument "value" to "NT" has incompatible type "NT[A]"; expected "Union[int, NT[int]]"
-reveal_type(nti)  # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]"
+reveal_type(nti)  # N: Revealed type is "tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]"
 
 nta: NT[A]
 ntb: NT[B]
@@ -807,11 +807,11 @@ Tree2 = Union[str, Tuple[Tree2, Tree2]]
 Tree3 = Union[str, Tuple[Tree3, Tree3, Tree3]]
 
 def test1() -> Tree1:
-    return 42  # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree1]]")
+    return 42  # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree1]]")
 def test2() -> Tree2:
-    return 42  # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree2, Tree2]]")
+    return 42  # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree2, Tree2]]")
 def test3() -> Tree3:
-    return 42  # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree3, Tree3, Tree3]]")
+    return 42  # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree3, Tree3, Tree3]]")
 [builtins fixtures/tuple.pyi]
 
 [case testRecursiveDoubleUnionNoCrash]
@@ -892,7 +892,7 @@ from typing import List, NamedTuple
 
 Example = NamedTuple("Example", [("rec", List["Example"])])
 e: Example
-reveal_type(e)  # N: Revealed type is "Tuple[builtins.list[...], fallback=__main__.Example]"
+reveal_type(e)  # N: Revealed type is "tuple[builtins.list[...], fallback=__main__.Example]"
 [builtins fixtures/tuple.pyi]
 
 [case testRecursiveBoundFunctionScopeNoCrash]
@@ -932,7 +932,7 @@ x: A[int, str]
 
 *_, last = x
 if last is not None:
-    reveal_type(last)  # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[Tuple[builtins.int, builtins.str, Union[..., None]], None]]"
+    reveal_type(last)  # N: Revealed type is "tuple[builtins.int, builtins.str, Union[tuple[builtins.int, builtins.str, Union[..., None]], None]]"
 [builtins fixtures/tuple.pyi]
 
 [case testRecursiveAliasLiteral]
diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test
index aaec94b546f5..7ddfdd0f8a4f 100644
--- a/test-data/unit/check-redefine.test
+++ b/test-data/unit/check-redefine.test
@@ -323,7 +323,7 @@ def f() -> None:
 def f() -> None:
     class x: pass
     x = 1 # E: Cannot assign to a type \
-          # E: Incompatible types in assignment (expression has type "int", variable has type "Type[x]")
+          # E: Incompatible types in assignment (expression has type "int", variable has type "type[x]")
     y = 1
     class y: pass # E: Name "y" already defined on line 5
 
diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test
index 238b64399ce4..fa831008fbae 100644
--- a/test-data/unit/check-redefine2.test
+++ b/test-data/unit/check-redefine2.test
@@ -1118,13 +1118,13 @@ def f1() -> None:
     reveal_type(x) # N: Revealed type is "builtins.int"
 
 def f2() -> None:
-    x, *y = t() # E: Need type annotation for "y" (hint: "y: List[] = ...")
+    x, *y = t() # E: Need type annotation for "y" (hint: "y: list[] = ...")
 
 def f3() -> None:
     x, _ = 1, []
 
 def f4() -> None:
-    a, b = 1, [] # E: Need type annotation for "b" (hint: "b: List[] = ...")
+    a, b = 1, [] # E: Need type annotation for "b" (hint: "b: list[] = ...")
 [builtins fixtures/tuple.pyi]
 
 [case testNewRedefineUseInferredTypedDictTypeForContext]
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index ffa1a369e883..5f337f773e6f 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -55,7 +55,7 @@ class A:
             return A()  # E: Incompatible return value type (got "A", expected "T")
         elif A():
             return B()  # E: Incompatible return value type (got "B", expected "T")
-        reveal_type(_type(self))  # N: Revealed type is "Type[T`-1]"
+        reveal_type(_type(self))  # N: Revealed type is "type[T`-1]"
         return reveal_type(_type(self)())  # N: Revealed type is "T`-1"
 
 class B(A):
@@ -306,7 +306,7 @@ class A:
 
     @classmethod
     def new(cls: Type[T], factory: Callable[[T], T]) -> T:
-        reveal_type(cls)   # N: Revealed type is "Type[T`-1]"
+        reveal_type(cls)   # N: Revealed type is "type[T`-1]"
         reveal_type(cls())   # N: Revealed type is "T`-1"
         cls(2)  # E: Too many arguments for "A"
         return cls()
@@ -413,7 +413,7 @@ class A:
         return self
 
     @classmethod
-    def cfoo(cls: Type[T]) -> T:  # E: The erased type of self "Type[builtins.str]" is not a supertype of its class "Type[__main__.A]"
+    def cfoo(cls: Type[T]) -> T:  # E: The erased type of self "type[builtins.str]" is not a supertype of its class "type[__main__.A]"
         return cls()
 
 Q = TypeVar('Q', bound='B')
@@ -441,7 +441,7 @@ class D:
         return self
 
     @classmethod
-    def cfoo(cls: Type[Q]) -> Q:  # E: The erased type of self "Type[__main__.B]" is not a supertype of its class "Type[__main__.D]"
+    def cfoo(cls: Type[Q]) -> Q:  # E: The erased type of self "type[__main__.B]" is not a supertype of its class "type[__main__.D]"
         return cls()
 
 [builtins fixtures/classmethod.pyi]
@@ -473,10 +473,10 @@ class A:
         pass
 
 class B:
-    def __new__(cls: Type[T]) -> T:  # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]"
+    def __new__(cls: Type[T]) -> T:  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]"
         return cls()
 
-    def __init_subclass__(cls: Type[T]) -> None:  # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]"
+    def __init_subclass__(cls: Type[T]) -> None:  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]"
         pass
 
 class C:
@@ -487,19 +487,19 @@ class C:
         pass
 
 class D:
-    def __new__(cls: D) -> D:  # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]"
+    def __new__(cls: D) -> D:  # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]"
         return cls
 
-    def __init_subclass__(cls: D) -> None:  # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]"
+    def __init_subclass__(cls: D) -> None:  # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]"
         pass
 
 class E:
     def __new__(cls) -> E:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.E]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.E]"
         return cls()
 
     def __init_subclass__(cls) -> None:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.E]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.E]"
 
 [case testSelfTypeNew_explicit]
 from typing import TypeVar, Type
@@ -516,11 +516,11 @@ class A:
 
 class B:
     @staticmethod
-    def __new__(cls: Type[T]) -> T:  # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]"
+    def __new__(cls: Type[T]) -> T:  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]"
         return cls()
 
     @classmethod
-    def __init_subclass__(cls: Type[T]) -> None:  # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]"
+    def __init_subclass__(cls: Type[T]) -> None:  # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]"
         pass
 
 class C:
@@ -534,22 +534,22 @@ class C:
 
 class D:
     @staticmethod
-    def __new__(cls: D) -> D:  # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]"
+    def __new__(cls: D) -> D:  # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]"
         return cls
 
     @classmethod
-    def __init_subclass__(cls: D) -> None:  # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]"
+    def __init_subclass__(cls: D) -> None:  # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]"
         pass
 
 class E:
     @staticmethod
     def __new__(cls) -> E:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.E]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.E]"
         return cls()
 
     @classmethod
     def __init_subclass__(cls) -> None:
-        reveal_type(cls)  # N: Revealed type is "Type[__main__.E]"
+        reveal_type(cls)  # N: Revealed type is "type[__main__.E]"
 
 [builtins fixtures/classmethod.pyi]
 
@@ -608,13 +608,13 @@ class B(A):
     pass
 
 reveal_type(A().g)  # N: Revealed type is "builtins.int"
-reveal_type(A().gt)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.A]"
+reveal_type(A().gt)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.A]"
 reveal_type(A().f())  # N: Revealed type is "builtins.int"
-reveal_type(A().ft())  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.A]"
+reveal_type(A().ft())  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.A]"
 reveal_type(B().g)  # N: Revealed type is "builtins.int"
-reveal_type(B().gt)  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.B]"
+reveal_type(B().gt)  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.B]"
 reveal_type(B().f())  # N: Revealed type is "builtins.int"
-reveal_type(B().ft())  # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.B]"
+reveal_type(B().ft())  # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.B]"
 
 [builtins fixtures/property.pyi]
 
@@ -645,9 +645,9 @@ reveal_type(Y.gt)  # N: Revealed type is "def (x: builtins.int) -> __main__.Y"
 reveal_type(Y.f())  # N: Revealed type is "builtins.int"
 reveal_type(Y.ft())  # N: Revealed type is "def (x: builtins.int) -> __main__.Y"
 reveal_type(X1.g)  # N: Revealed type is "builtins.int"
-reveal_type(X1.gt)  # N: Revealed type is "Type[__main__.X]"
+reveal_type(X1.gt)  # N: Revealed type is "type[__main__.X]"
 reveal_type(X1.f())  # N: Revealed type is "builtins.int"
-reveal_type(X1.ft())  # N: Revealed type is "Type[__main__.X]"
+reveal_type(X1.ft())  # N: Revealed type is "type[__main__.X]"
 
 [builtins fixtures/property.pyi]
 
@@ -703,9 +703,9 @@ class C(Generic[T]):
 class DI(C[int]): ...
 class DS(C[str]): ...
 
-DI().from_item()  # E: Invalid self argument "Type[DI]" to class attribute function "from_item" with type "Callable[[Type[C[str]]], None]"
+DI().from_item()  # E: Invalid self argument "type[DI]" to class attribute function "from_item" with type "Callable[[type[C[str]]], None]"
 DS().from_item()
-DI.from_item()  # E: Invalid self argument "Type[DI]" to attribute function "from_item" with type "Callable[[Type[C[str]]], None]"
+DI.from_item()  # E: Invalid self argument "type[DI]" to attribute function "from_item" with type "Callable[[type[C[str]]], None]"
 DS.from_item()
 [builtins fixtures/classmethod.pyi]
 
@@ -723,7 +723,7 @@ class C(Generic[T]):
 
 ci: C[int]
 cs: C[str]
-reveal_type(ci.from_item)  # N: Revealed type is "def (item: Tuple[builtins.int])"
+reveal_type(ci.from_item)  # N: Revealed type is "def (item: tuple[builtins.int])"
 reveal_type(cs.from_item)  # N: Revealed type is "def (item: builtins.str)"
 [builtins fixtures/tuple.pyi]
 
@@ -844,7 +844,7 @@ class Sub(Base[List[int]]): ...
 class BadSub(Base[int]): ...
 
 reveal_type(Sub().get_item())  # N: Revealed type is "builtins.int"
-BadSub().get_item()  # E: Invalid self argument "BadSub" to attribute function "get_item" with type "Callable[[Base[List[S]]], S]"
+BadSub().get_item()  # E: Invalid self argument "BadSub" to attribute function "get_item" with type "Callable[[Base[list[S]]], S]"
 [builtins fixtures/list.pyi]
 
 [case testMixinAllowedWithProtocol]
@@ -963,7 +963,7 @@ c: Lnk[int, float] = Lnk()
 
 d: Lnk[str, float] = b >> c  # OK
 e: Lnk[str, Tuple[int, float]] = a >> (b, c)  # OK
-f: Lnk[str, Tuple[float, int]] = a >> (c, b) # E: Unsupported operand types for >> ("Lnk[str, Tuple[str, int]]" and "Tuple[Lnk[int, float], Lnk[str, int]]")
+f: Lnk[str, Tuple[float, int]] = a >> (c, b) # E: Unsupported operand types for >> ("Lnk[str, tuple[str, int]]" and "tuple[Lnk[int, float], Lnk[str, int]]")
 [builtins fixtures/tuple.pyi]
 
 [case testSelfTypeMutuallyExclusiveRestrictions]
@@ -1019,7 +1019,7 @@ class Bad(metaclass=Meta):
     pass
 
 Good.do_x()
-Bad.do_x()  # E: Invalid self argument "Type[Bad]" to attribute function "do_x" with type "Callable[[Type[T]], T]"
+Bad.do_x()  # E: Invalid self argument "type[Bad]" to attribute function "do_x" with type "Callable[[type[T]], T]"
 
 [case testSelfTypeProtocolClassmethodMatch]
 from typing import Type, TypeVar, Protocol
@@ -1120,7 +1120,7 @@ class C(Generic[T]):
 
 class D(Generic[V]):
     def f(self) -> None:
-        reveal_type(C[Tuple[V, str]]().magic())  # N: Revealed type is "Tuple[Tuple[V`1, builtins.str], V`1, builtins.str]"
+        reveal_type(C[Tuple[V, str]]().magic())  # N: Revealed type is "tuple[tuple[V`1, builtins.str], V`1, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testSelfTypeOnUnion]
@@ -1414,7 +1414,7 @@ class C(Generic[T]):
 
     def f(self) -> None:
         for x, y in Z(self.a, self.b):
-            reveal_type((x, y))  # N: Revealed type is "Tuple[T`1, builtins.str]"
+            reveal_type((x, y))  # N: Revealed type is "tuple[T`1, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testEnumerateReturningSelfFromIter]
@@ -1508,7 +1508,7 @@ from typing import Self, TypeVar, Tuple
 T = TypeVar("T")
 class C:
     def meth(self: T) -> Tuple[Self, T]: ...  # E: Method cannot have explicit self annotation and Self type
-reveal_type(C().meth())  # N: Revealed type is "Tuple[Never, __main__.C]"
+reveal_type(C().meth())  # N: Revealed type is "tuple[Never, __main__.C]"
 [builtins fixtures/property.pyi]
 
 [case testTypingSelfProperty]
@@ -1571,7 +1571,7 @@ Pairs = List[Tuple[T, T]]
 class C(Generic[T]):
     def pairs(self) -> Pairs[Self]: ...
 class D(C[T]): ...
-reveal_type(D[int]().pairs())  # N: Revealed type is "builtins.list[Tuple[__main__.D[builtins.int], __main__.D[builtins.int]]]"
+reveal_type(D[int]().pairs())  # N: Revealed type is "builtins.list[tuple[__main__.D[builtins.int], __main__.D[builtins.int]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypingSelfOverrideVar]
@@ -1609,11 +1609,11 @@ class C(Generic[T]):
     def __init__(self, val: T) -> None: ...
     @classmethod
     def pair(cls, val: T) -> Tuple[Self, Self]:
-        return (cls(val), C(val))  # E: Incompatible return value type (got "Tuple[Self, C[T]]", expected "Tuple[Self, Self]")
+        return (cls(val), C(val))  # E: Incompatible return value type (got "tuple[Self, C[T]]", expected "tuple[Self, Self]")
 
 class D(C[int]): pass
-reveal_type(C.pair(42))  # N: Revealed type is "Tuple[__main__.C[builtins.int], __main__.C[builtins.int]]"
-reveal_type(D.pair("no"))  # N: Revealed type is "Tuple[__main__.D, __main__.D]" \
+reveal_type(C.pair(42))  # N: Revealed type is "tuple[__main__.C[builtins.int], __main__.C[builtins.int]]"
+reveal_type(D.pair("no"))  # N: Revealed type is "tuple[__main__.D, __main__.D]" \
     # E: Argument 1 to "pair" of "C" has incompatible type "str"; expected "int"
 [builtins fixtures/classmethod.pyi]
 
@@ -1630,8 +1630,8 @@ class D(C[int]): ...
 
 c: C[int]
 d: D
-reveal_type(c.meth("test"))  # N: Revealed type is "Tuple[__main__.C[builtins.int], builtins.str, builtins.int]"
-reveal_type(d.meth("test"))  # N: Revealed type is "Tuple[__main__.D, builtins.str, builtins.int]"
+reveal_type(c.meth("test"))  # N: Revealed type is "tuple[__main__.C[builtins.int], builtins.str, builtins.int]"
+reveal_type(d.meth("test"))  # N: Revealed type is "tuple[__main__.D, builtins.str, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypingSelfRecursiveInit]
@@ -1781,8 +1781,8 @@ class C:
     def bar(self) -> Self: ...
     def foo(self, x: S) -> Tuple[Self, S]: ...
 
-reveal_type(C.foo)  # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> Tuple[Self`1, S`2]"
-reveal_type(C().foo(42))  # N: Revealed type is "Tuple[__main__.C, builtins.int]"
+reveal_type(C.foo)  # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> tuple[Self`1, S`2]"
+reveal_type(C().foo(42))  # N: Revealed type is "tuple[__main__.C, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypingSelfTypeVarClashAttr]
@@ -1795,8 +1795,8 @@ class C:
     def bar(self) -> Self: ...
     foo: Callable[[S, Self], Tuple[Self, S]]
 
-reveal_type(C().foo)  # N: Revealed type is "def [S] (S`2, __main__.C) -> Tuple[__main__.C, S`2]"
-reveal_type(C().foo(42, C()))  # N: Revealed type is "Tuple[__main__.C, builtins.int]"
+reveal_type(C().foo)  # N: Revealed type is "def [S] (S`2, __main__.C) -> tuple[__main__.C, S`2]"
+reveal_type(C().foo(42, C()))  # N: Revealed type is "tuple[__main__.C, builtins.int]"
 class This: ...
 [builtins fixtures/tuple.pyi]
 
@@ -2105,9 +2105,9 @@ class C(Tuple[int, str]):
         return reveal_type(self.y)  # N: Revealed type is "Self`0"
 
 c: C
-reveal_type(c.x)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]"
-reveal_type(c.y)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]"
-reveal_type(C.y)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]"
+reveal_type(c.x)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]"
+reveal_type(c.y)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]"
+reveal_type(C.y)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]"
 C.x  # E: Access to generic instance variables via class is ambiguous
 [builtins fixtures/classmethod.pyi]
 
diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test
index 042a962be9b3..63d9ccfc80cb 100644
--- a/test-data/unit/check-serialize.test
+++ b/test-data/unit/check-serialize.test
@@ -547,7 +547,7 @@ class A(Tuple[int, str]):
 [builtins fixtures/tuple.pyi]
 [out2]
 tmp/a.py:3: error: Too many arguments for "f" of "A"
-tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [case testSerializeVariableLengthTupleBaseClass]
 import a
@@ -565,7 +565,7 @@ class A(Tuple[int, ...]):
 [builtins fixtures/tuple.pyi]
 [out2]
 tmp/a.py:3: error: Too many arguments for "f" of "A"
-tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.int]"
+tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.int]"
 
 [case testSerializePlainTupleBaseClass]
 import a
@@ -583,7 +583,7 @@ class A(tuple):
 [builtins fixtures/tuple.pyi]
 [out2]
 tmp/a.py:3: error: Too many arguments for "f" of "A"
-tmp/a.py:4: note: Revealed type is "Tuple[Any, Any]"
+tmp/a.py:4: note: Revealed type is "tuple[Any, Any]"
 
 [case testSerializeNamedTupleBaseClass]
 import a
@@ -602,8 +602,8 @@ class A(NamedTuple('N', [('x', int), ('y', str)])):
 [builtins fixtures/tuple.pyi]
 [out2]
 tmp/a.py:3: error: Too many arguments for "f" of "A"
-tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.str]"
-tmp/a.py:5: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.str]"
+tmp/a.py:5: note: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [case testSerializeAnyBaseClass]
 import a
@@ -727,13 +727,13 @@ class C:
         self.c = A
 [builtins fixtures/tuple.pyi]
 [out1]
-main:2: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]"
-main:3: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]"
-main:4: note: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]"
 [out2]
-main:2: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]"
-main:3: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]"
-main:4: note: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]"
+main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]"
 
 --
 -- Strict optional
@@ -941,9 +941,9 @@ N = NamedTuple('N', [('x', int)])
 x: N
 [builtins fixtures/tuple.pyi]
 [out2]
-tmp/a.py:5: error: Incompatible types in assignment (expression has type "Tuple[int]", variable has type "N")
-tmp/a.py:6: error: Incompatible types in assignment (expression has type "Tuple[int]", variable has type "N")
-tmp/a.py:9: note: Revealed type is "Tuple[builtins.int, fallback=b.N]"
+tmp/a.py:5: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "N")
+tmp/a.py:6: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "N")
+tmp/a.py:9: note: Revealed type is "tuple[builtins.int, fallback=b.N]"
 tmp/a.py:10: note: Revealed type is "builtins.int"
 tmp/a.py:11: error: Argument "x" to "N" has incompatible type "str"; expected "int"
 
@@ -993,9 +993,9 @@ tmp/a.py:9: note: Revealed type is "b.DD"
 tmp/a.py:10: note: Revealed type is "Any"
 tmp/a.py:11: note: Revealed type is "Union[builtins.int, builtins.str]"
 tmp/a.py:12: note: Revealed type is "builtins.list[builtins.int]"
-tmp/a.py:13: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+tmp/a.py:13: note: Revealed type is "tuple[builtins.int, builtins.str]"
 tmp/a.py:14: note: Revealed type is "def (builtins.int) -> builtins.str"
-tmp/a.py:15: note: Revealed type is "Type[builtins.int]"
+tmp/a.py:15: note: Revealed type is "type[builtins.int]"
 tmp/a.py:17: note: Revealed type is "def (*Any, **Any) -> builtins.str"
 tmp/a.py:19: note: Revealed type is "builtins.type"
 
@@ -1010,9 +1010,9 @@ X = TypeVar('X')
 Y = Tuple[X, str]
 [builtins fixtures/tuple.pyi]
 [out1]
-main:4: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+main:4: note: Revealed type is "tuple[builtins.int, builtins.str]"
 [out2]
-main:4: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+main:4: note: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [case testSerializeTuple]
 # Don't repreat types tested by testSerializeTypeAliases here.
diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test
index b0b673f696e1..9ab68b32472d 100644
--- a/test-data/unit/check-statements.test
+++ b/test-data/unit/check-statements.test
@@ -1305,7 +1305,7 @@ def g() -> Iterator[List[int]]:
     yield [2, 3, 4]
 def f() -> Iterator[List[int]]:
     yield from g()
-    yield from [1, 2, 3]  # E: Incompatible types in "yield from" (actual type "int", expected type "List[int]")
+    yield from [1, 2, 3]  # E: Incompatible types in "yield from" (actual type "int", expected type "list[int]")
 [builtins fixtures/for.pyi]
 [out]
 
@@ -1476,7 +1476,7 @@ with A():
 with A() as a:  # type: Tuple[int, int]
     pass
 
-with A() as b:  # type: Tuple[int, str]  # E: Incompatible types in assignment (expression has type "Tuple[int, int]", variable has type "Tuple[int, str]")
+with A() as b:  # type: Tuple[int, str]  # E: Incompatible types in assignment (expression has type "tuple[int, int]", variable has type "tuple[int, str]")
     pass
 
 with A() as (c, d):  # type: int, int
@@ -2029,7 +2029,7 @@ cs: List[B]
 if int():
     *bs, b = bs
 if int():
-    *bs, c = cs  # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]")
+    *bs, c = cs  # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]")
     if int():
         *ns, c = cs
 if int():
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
index 3424d053fe42..f118eec4f266 100644
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -11,15 +11,15 @@ t4: Tuple[A, B]
 t5: Tuple[B, A]
 
 if int():
-    t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[B]", variable has type "Tuple[A]")
+    t1 = t2 # E: Incompatible types in assignment (expression has type "tuple[B]", variable has type "tuple[A]")
 if int():
-    t1 = t3 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]")
+    t1 = t3 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A]")
 if int():
-    t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "Tuple[A, A]")
+    t3 = t1 # E: Incompatible types in assignment (expression has type "tuple[A]", variable has type "tuple[A, A]")
 if int():
-    t3 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[A, A]")
+    t3 = t4 # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "tuple[A, A]")
 if int():
-    t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, A]")
+    t3 = t5 # E: Incompatible types in assignment (expression has type "tuple[B, A]", variable has type "tuple[A, A]")
 
 # Ok
 if int():
@@ -44,10 +44,10 @@ t2: Tuple[A, B]
 t3: Tuple[B, A]
 
 if int():
-    t2 = t1  # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]")
-    t2 = t3  # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, B]")
-    t3 = t1  # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[B, A]")
-    t3 = t2  # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[B, A]")
+    t2 = t1  # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]")
+    t2 = t3  # E: Incompatible types in assignment (expression has type "tuple[B, A]", variable has type "tuple[A, B]")
+    t3 = t1  # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[B, A]")
+    t3 = t2  # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "tuple[B, A]")
 
     t1 = t2
     t1 = t3
@@ -63,11 +63,11 @@ a, o = None, None # type: (A, object)
 t = None # type: Tuple[A, A]
 
 if int():
-    a = t # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "A")
+    a = t # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "A")
 if int():
-    t = o # E: Incompatible types in assignment (expression has type "object", variable has type "Tuple[A, A]")
+    t = o # E: Incompatible types in assignment (expression has type "object", variable has type "tuple[A, A]")
 if int():
-    t = a # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, A]")
+    t = a # E: Incompatible types in assignment (expression has type "A", variable has type "tuple[A, A]")
 # TODO: callable types + tuples
 
 # Ok
@@ -85,7 +85,7 @@ t1: Tuple[A, Tuple[A, A]]
 t2: Tuple[B, Tuple[B, B]]
 
 if int():
-    t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]")
+    t2 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, tuple[A, A]]", variable has type "tuple[B, tuple[B, B]]")
 if int():
     t1 = t2
 
@@ -99,7 +99,7 @@ t1: Tuple[A, Tuple[A, A]]
 t2: Tuple[B, Tuple[B, B]]
 
 if int():
-    t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]")
+    t2 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, tuple[A, A]]", variable has type "tuple[B, tuple[B, B]]")
 if int():
     t1 = t2
 
@@ -139,18 +139,18 @@ def takes_tuple_aa(t: tuple[A, A]): ...
 
 takes_tuple_aa(tuple_aa)
 takes_tuple_aa(Tuple_aa)
-takes_tuple_aa(tuple_obj)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
-takes_tuple_aa(Tuple_obj)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
-takes_tuple_aa(tuple_obj_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
-takes_tuple_aa(Tuple_obj_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
-takes_tuple_aa(tuple_obj_two)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
-takes_tuple_aa(Tuple_obj_two)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
+takes_tuple_aa(tuple_obj)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, ...]"; expected "tuple[A, A]"
+takes_tuple_aa(Tuple_obj)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, ...]"; expected "tuple[A, A]"
+takes_tuple_aa(tuple_obj_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object]"; expected "tuple[A, A]"
+takes_tuple_aa(Tuple_obj_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object]"; expected "tuple[A, A]"
+takes_tuple_aa(tuple_obj_two)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, object]"; expected "tuple[A, A]"
+takes_tuple_aa(Tuple_obj_two)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, object]"; expected "tuple[A, A]"
 takes_tuple_aa(tuple_any_implicit)
 takes_tuple_aa(Tuple_any_implicit)
 takes_tuple_aa(tuple_any)
 takes_tuple_aa(Tuple_any)
-takes_tuple_aa(tuple_any_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
-takes_tuple_aa(Tuple_any_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
+takes_tuple_aa(tuple_any_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[Any]"; expected "tuple[A, A]"
+takes_tuple_aa(Tuple_any_one)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[Any]"; expected "tuple[A, A]"
 takes_tuple_aa(tuple_any_two)
 takes_tuple_aa(Tuple_any_two)
 
@@ -175,22 +175,22 @@ takes_tuple_any_implicit(Tuple_any_two)
 
 def takes_tuple_any_one(t: tuple[Any]): ...
 
-takes_tuple_any_one(tuple_aa)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
-takes_tuple_any_one(Tuple_aa)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
-takes_tuple_any_one(tuple_obj)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
-takes_tuple_any_one(Tuple_obj)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
+takes_tuple_any_one(tuple_aa)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[A, A]"; expected "tuple[Any]"
+takes_tuple_any_one(Tuple_aa)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[A, A]"; expected "tuple[Any]"
+takes_tuple_any_one(tuple_obj)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, ...]"; expected "tuple[Any]"
+takes_tuple_any_one(Tuple_obj)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, ...]"; expected "tuple[Any]"
 takes_tuple_any_one(tuple_obj_one)
 takes_tuple_any_one(Tuple_obj_one)
-takes_tuple_any_one(tuple_obj_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
-takes_tuple_any_one(Tuple_obj_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
+takes_tuple_any_one(tuple_obj_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, object]"; expected "tuple[Any]"
+takes_tuple_any_one(Tuple_obj_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, object]"; expected "tuple[Any]"
 takes_tuple_any_one(tuple_any_implicit)
 takes_tuple_any_one(Tuple_any_implicit)
 takes_tuple_any_one(tuple_any)
 takes_tuple_any_one(Tuple_any)
 takes_tuple_any_one(tuple_any_one)
 takes_tuple_any_one(Tuple_any_one)
-takes_tuple_any_one(tuple_any_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"
-takes_tuple_any_one(Tuple_any_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"
+takes_tuple_any_one(tuple_any_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[Any, Any]"; expected "tuple[Any]"
+takes_tuple_any_one(Tuple_any_two)  # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[Any, Any]"; expected "tuple[Any]"
 
 class A: pass
 [builtins fixtures/tuple.pyi]
@@ -229,15 +229,15 @@ def takes_tuple_aa(t: Tuple[A, A]): ...
 takes_tuple_aa(inst_tuple_aa)
 takes_tuple_aa(inst_tuple_aa_subclass)
 takes_tuple_aa(inst_tuple_any_subclass)
-takes_tuple_aa(inst_tuple_any_one_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "Tuple[A, A]"
+takes_tuple_aa(inst_tuple_any_one_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "tuple[A, A]"
 takes_tuple_aa(inst_tuple_any_two_subclass)
-takes_tuple_aa(inst_tuple_obj_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "Tuple[A, A]"
-takes_tuple_aa(inst_tuple_obj_one_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "Tuple[A, A]"
-takes_tuple_aa(inst_tuple_obj_two_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "Tuple[A, A]"
+takes_tuple_aa(inst_tuple_obj_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "tuple[A, A]"
+takes_tuple_aa(inst_tuple_obj_one_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "tuple[A, A]"
+takes_tuple_aa(inst_tuple_obj_two_subclass)  # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "tuple[A, A]"
 
 def takes_tuple_aa_subclass(t: tuple_aa_subclass): ...
 
-takes_tuple_aa_subclass(inst_tuple_aa)  # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "Tuple[A, A]"; expected "tuple_aa_subclass"
+takes_tuple_aa_subclass(inst_tuple_aa)  # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple[A, A]"; expected "tuple_aa_subclass"
 takes_tuple_aa_subclass(inst_tuple_aa_subclass)
 takes_tuple_aa_subclass(inst_tuple_any_subclass)  # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_subclass"; expected "tuple_aa_subclass"
 takes_tuple_aa_subclass(inst_tuple_any_one_subclass)  # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_one_subclass"; expected "tuple_aa_subclass"
@@ -271,15 +271,15 @@ t3 = None # type: Tuple[A, B]
 a, b, c = None, None, None # type: (A, B, C)
 
 if int():
-    t2 = ()        # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[A]")
+    t2 = ()        # E: Incompatible types in assignment (expression has type "tuple[()]", variable has type "tuple[A]")
 if int():
-    t2 = (a, a)    # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]")
+    t2 = (a, a)    # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A]")
 if int():
-    t3 = (a, a)    # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]")
+    t3 = (a, a)    # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]")
 if int():
-    t3 = (b, b)    # E: Incompatible types in assignment (expression has type "Tuple[B, B]", variable has type "Tuple[A, B]")
+    t3 = (b, b)    # E: Incompatible types in assignment (expression has type "tuple[B, B]", variable has type "tuple[A, B]")
 if int():
-    t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "Tuple[A, B, A]", variable has type "Tuple[A, B]")
+    t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "tuple[A, B, A]", variable has type "tuple[A, B]")
 
 t1 = ()
 t1 = (a,)
@@ -389,9 +389,9 @@ class B: pass
 t: Tuple[A, B]
 n = 0
 
-t[0] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]")
-t[2] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]")
-t[n] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]")
+t[0] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]")
+t[2] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]")
+t[n] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]")
 [builtins fixtures/tuple.pyi]
 
 
@@ -626,8 +626,8 @@ d, e = f, g, h = 1, 1 # E: Need more than 2 values to unpack (3 expected)
 [case testAssignmentToStarMissingAnnotation]
 from typing import List
 t = 1, 2
-a, b, *c = 1, 2  # E: Need type annotation for "c" (hint: "c: List[] = ...")
-aa, bb, *cc = t  # E: Need type annotation for "cc" (hint: "cc: List[] = ...")
+a, b, *c = 1, 2  # E: Need type annotation for "c" (hint: "c: list[] = ...")
+aa, bb, *cc = t  # E: Need type annotation for "cc" (hint: "cc: list[] = ...")
 [builtins fixtures/list.pyi]
 
 [case testAssignmentToStarAnnotation]
@@ -636,7 +636,7 @@ from typing import List
 li, lo = None, None # type: List[int], List[object]
 a, b, *c = 1, 2  # type: int, int, List[int]
 if int():
-    c = lo  # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[int]")
+    c = lo  # E: Incompatible types in assignment (expression has type "list[object]", variable has type "list[int]")
 if int():
     c = li
 [builtins fixtures/list.pyi]
@@ -707,7 +707,7 @@ if int():
     a, *na = ta
     if int():
         na = la
-        na = a  # E: Incompatible types in assignment (expression has type "A", variable has type "List[A]")
+        na = a  # E: Incompatible types in assignment (expression has type "A", variable has type "list[A]")
 
 class A: pass
 [builtins fixtures/list.pyi]
@@ -719,7 +719,7 @@ li: List[int]
 la: List[A]
 a, *l = A(), A()
 if int():
-    l = li  # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]")
+    l = li  # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]")
 if int():
     l = la
 [builtins fixtures/list.pyi]
@@ -734,7 +734,7 @@ li: List[int]
 la: List[A]
 a, *l = [A(), A()]
 if int():
-    l = li  # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]")
+    l = li  # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]")
 if int():
     l = la
 [builtins fixtures/list.pyi]
@@ -747,7 +747,7 @@ la: List[A]
 ta: Tuple[A, A, A]
 a, *l = ta
 if int():
-    l = li  # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]")
+    l = li  # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]")
 if int():
     l = la
 
@@ -761,7 +761,7 @@ li: List[int]
 la: List[A]
 a, *l = la
 if int():
-    l = li  # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]")
+    l = li  # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]")
 if int():
     l = la
 
@@ -835,17 +835,17 @@ if int():
 if int():
     t, c2 = (a2, b2), c2
 if int():
-    t, c2 = (a2, a2), c2  # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]")
+    t, c2 = (a2, a2), c2  # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]")
 if int():
-    t = a1, a1, a1  # E: Incompatible types in assignment (expression has type "Tuple[A, A, A]", variable has type "Tuple[A, B]")
+    t = a1, a1, a1  # E: Incompatible types in assignment (expression has type "tuple[A, A, A]", variable has type "tuple[A, B]")
 if int():
-    t = a1  # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, B]")
+    t = a1  # E: Incompatible types in assignment (expression has type "A", variable has type "tuple[A, B]")
 if int():
     a2, a2, a2 = t  # E: Need more than 2 values to unpack (3 expected)
 if int():
     a2, = t  # E: Too many values to unpack (1 expected, 2 provided)
 if int():
-    a2 = t  # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "A")
+    a2 = t  # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "A")
 
 class A: pass
 class B: pass
@@ -864,10 +864,10 @@ def f(x: 'A') -> None: pass
 
 a: A
 
-(a, a) + a  # E: Unsupported operand types for + ("Tuple[A, A]" and "A")
-a + (a, a)  # E: Unsupported operand types for + ("A" and "Tuple[A, A]")
-f((a, a))   # E: Argument 1 to "f" has incompatible type "Tuple[A, A]"; expected "A"
-(a, a).foo  # E: "Tuple[A, A]" has no attribute "foo"
+(a, a) + a  # E: Unsupported operand types for + ("tuple[A, A]" and "A")
+a + (a, a)  # E: Unsupported operand types for + ("A" and "tuple[A, A]")
+f((a, a))   # E: Argument 1 to "f" has incompatible type "tuple[A, A]"; expected "A"
+(a, a).foo  # E: "tuple[A, A]" has no attribute "foo"
 [builtins fixtures/tuple.pyi]
 
 [case testLargeTuplesInErrorMessages]
@@ -879,7 +879,7 @@ class LongTypeName:
     def __add__(self, x: 'LongTypeName') -> 'LongTypeName': pass
 [builtins fixtures/tuple.pyi]
 [out]
-main:3: error: Unsupported operand types for + ("LongTypeName" and "Tuple[LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName]")
+main:3: error: Unsupported operand types for + ("LongTypeName" and "tuple[LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName]")
 
 
 -- Tuple methods
@@ -899,7 +899,7 @@ if int():
     i = t.__str__()  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
 if int():
     i = s in t       # E: Incompatible types in assignment (expression has type "bool", variable has type "int")
-t.foo            # E: "Tuple[int, str]" has no attribute "foo"
+t.foo            # E: "tuple[int, str]" has no attribute "foo"
 
 if int():
     i = t.__len__()
@@ -1036,7 +1036,7 @@ from typing import TypeVar, Generic, Tuple
 T = TypeVar('T')
 class Test(Generic[T], Tuple[T]): pass
 x = Test() # type: Test[int]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Test[builtins.int]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.Test[builtins.int]]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -1064,7 +1064,7 @@ tb = () # type: Tuple[B, ...]
 fa(ta)
 fa(tb)
 fb(tb)
-fb(ta) # E: Argument 1 to "fb" has incompatible type "Tuple[A, ...]"; expected "Tuple[B, ...]"
+fb(ta) # E: Argument 1 to "fb" has incompatible type "tuple[A, ...]"; expected "tuple[B, ...]"
 [builtins fixtures/tuple.pyi]
 
 [case testSubtypingFixedAndVariableLengthTuples]
@@ -1080,8 +1080,8 @@ fa(aa)
 fa(ab)
 fa(bb)
 fb(bb)
-fb(ab) # E: Argument 1 to "fb" has incompatible type "Tuple[A, B]"; expected "Tuple[B, ...]"
-fb(aa) # E: Argument 1 to "fb" has incompatible type "Tuple[A, A]"; expected "Tuple[B, ...]"
+fb(ab) # E: Argument 1 to "fb" has incompatible type "tuple[A, B]"; expected "tuple[B, ...]"
+fb(aa) # E: Argument 1 to "fb" has incompatible type "tuple[A, A]"; expected "tuple[B, ...]"
 [builtins fixtures/tuple.pyi]
 
 [case testSubtypingTupleIsContainer]
@@ -1102,7 +1102,7 @@ a = ()
 
 a = (1, 2)
 b = (*a, '')
-reveal_type(b)  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]"
+reveal_type(b)  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithStarExpr2]
@@ -1115,7 +1115,7 @@ reveal_type(b)  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 # flags: --enable-incomplete-feature=PreciseTupleTypes
 a = [1]
 b = (0, *a)
-reveal_type(b)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
+reveal_type(b)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithStarExpr3]
@@ -1130,9 +1130,9 @@ reveal_type(c)  # N: Revealed type is "builtins.tuple[builtins.str, ...]"
 # flags: --enable-incomplete-feature=PreciseTupleTypes
 a = ['']
 b = (0, *a)
-reveal_type(b)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]"
+reveal_type(b)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]"
 c = (*a, '')
-reveal_type(c)  # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]"
+reveal_type(c)  # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithStarExpr4]
@@ -1159,13 +1159,13 @@ class B: pass
 
 def f(x: Union[B, Tuple[A, A]]) -> None:
     if isinstance(x, tuple):
-        reveal_type(x) # N: Revealed type is "Tuple[__main__.A, __main__.A]"
+        reveal_type(x) # N: Revealed type is "tuple[__main__.A, __main__.A]"
     else:
         reveal_type(x) # N: Revealed type is "__main__.B"
 
 def g(x: Union[str, Tuple[str, str]]) -> None:
     if isinstance(x, tuple):
-        reveal_type(x) # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+        reveal_type(x) # N: Revealed type is "tuple[builtins.str, builtins.str]"
     else:
         reveal_type(x) # N: Revealed type is "builtins.str"
 
@@ -1178,19 +1178,19 @@ from typing import Tuple, Union
 Pair = Tuple[int, int]
 Variant = Union[int, Pair]
 def tuplify(v: Variant) -> None:
-    reveal_type(v) # N: Revealed type is "Union[builtins.int, Tuple[builtins.int, builtins.int]]"
+    reveal_type(v) # N: Revealed type is "Union[builtins.int, tuple[builtins.int, builtins.int]]"
     if not isinstance(v, tuple):
         reveal_type(v) # N: Revealed type is "builtins.int"
         v = (v, v)
-        reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.int]"
-    reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+        reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.int]"
+    reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.int]"
     reveal_type(v[0]) # N: Revealed type is "builtins.int"
 
 Pair2 = Tuple[int, str]
 Variant2 = Union[int, Pair2]
 def tuplify2(v: Variant2) -> None:
     if isinstance(v, tuple):
-        reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+        reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.str]"
     else:
         reveal_type(v) # N: Revealed type is "builtins.int"
 [builtins fixtures/tuple.pyi]
@@ -1200,10 +1200,10 @@ def tuplify2(v: Variant2) -> None:
 from typing import Tuple, Union
 
 def good(blah: Union[Tuple[int, int], int]) -> None:
-    reveal_type(blah) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.int]"
+    reveal_type(blah) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.int]"
     if isinstance(blah, tuple):
-        reveal_type(blah) # N: Revealed type is "Tuple[builtins.int, builtins.int]"
-    reveal_type(blah) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.int]"
+        reveal_type(blah) # N: Revealed type is "tuple[builtins.int, builtins.int]"
+    reveal_type(blah) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.int]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -1223,7 +1223,7 @@ def g(x: T) -> Tuple[T, T]:
     return (x, x)
 
 z = 1
-x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[B1, B2]"
+x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "tuple[B1, B2]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -1374,13 +1374,13 @@ reveal_type(join(subtup, tup2))  # N: Revealed type is "builtins.tuple[builtins.
 [case testTupleWithUndersizedContext]
 a = ([1], 'x')
 if int():
-    a = ([], 'x', 1)  # E: Incompatible types in assignment (expression has type "Tuple[List[Never], str, int]", variable has type "Tuple[List[int], str]")
+    a = ([], 'x', 1)  # E: Incompatible types in assignment (expression has type "tuple[list[Never], str, int]", variable has type "tuple[list[int], str]")
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithOversizedContext]
 a = (1, [1], 'x')
 if int():
-    a = (1, [])  # E: Incompatible types in assignment (expression has type "Tuple[int, List[int]]", variable has type "Tuple[int, List[int], str]")
+    a = (1, [])  # E: Incompatible types in assignment (expression has type "tuple[int, list[int]]", variable has type "tuple[int, list[int], str]")
 [builtins fixtures/tuple.pyi]
 
 [case testTupleWithoutContext]
@@ -1405,7 +1405,7 @@ def f(a: Tuple) -> None: pass
 f(())
 f((1,))
 f(('', ''))
-f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Any, ...]"
+f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[Any, ...]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleSingleton]
@@ -1413,9 +1413,9 @@ f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Any, .
 from typing import Tuple
 def f(a: Tuple[()]) -> None: pass
 f(())
-f((1,))  # E: Argument 1 to "f" has incompatible type "Tuple[int]"; expected "Tuple[()]"
-f(('', ''))  # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[()]"
-f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[()]"
+f((1,))  # E: Argument 1 to "f" has incompatible type "tuple[int]"; expected "tuple[()]"
+f(('', ''))  # E: Argument 1 to "f" has incompatible type "tuple[str, str]"; expected "tuple[()]"
+f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[()]"
 [builtins fixtures/tuple.pyi]
 
 [case testNonliteralTupleIndex]
@@ -1426,7 +1426,7 @@ reveal_type(t[x])  # N: Revealed type is "Union[builtins.int, builtins.str]"
 t[y]  # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \
       # N: Possible overload variants: \
       # N:     def __getitem__(self, int, /) -> Union[int, str] \
-      # N:     def __getitem__(self, slice, /) -> Tuple[Union[int, str], ...]
+      # N:     def __getitem__(self, slice, /) -> tuple[Union[int, str], ...]
 
 [builtins fixtures/tuple.pyi]
 
@@ -1467,7 +1467,7 @@ class C(Tuple[int, str]):
     def f(cls) -> None: pass
 
 t: Type[C]
-t.g()  # E: "Type[C]" has no attribute "g"
+t.g()  # E: "type[C]" has no attribute "g"
 t.f()
 [builtins fixtures/classmethod.pyi]
 
@@ -1475,7 +1475,7 @@ t.f()
 from typing import Tuple
 
 def foo(o: CallableTuple) -> int:
-    reveal_type(o)  # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple]"
+    reveal_type(o)  # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple]"
     return o(1, 2)
 
 class CallableTuple(Tuple[str, int]):
@@ -1489,7 +1489,7 @@ from typing import Generic, Tuple, TypeVar
 T = TypeVar('T')
 
 def foo(o: CallableTuple[int]) -> int:
-    reveal_type(o)  # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple[builtins.int]]"
+    reveal_type(o)  # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple[builtins.int]]"
     reveal_type(o.count(3))  # N: Revealed type is "builtins.int"
     return o(1, 2)
 
@@ -1520,7 +1520,7 @@ from typing import Iterable, Tuple
 x: Iterable[int] = ()
 y: Tuple[int, int] = (1, 2)
 x = y
-reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleOverlapDifferentTuples]
@@ -1532,9 +1532,9 @@ possibles: Tuple[int, Tuple[A]]
 x: Optional[Tuple[B]]
 
 if x in possibles:
-    reveal_type(x) # N: Revealed type is "Tuple[__main__.B]"
+    reveal_type(x) # N: Revealed type is "tuple[__main__.B]"
 else:
-    reveal_type(x) # N: Revealed type is "Union[Tuple[__main__.B], None]"
+    reveal_type(x) # N: Revealed type is "Union[tuple[__main__.B], None]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1546,7 +1546,7 @@ reveal_type(tup[0])  # N: Revealed type is "builtins.int"
 reveal_type(tup[1])  # N: Revealed type is "Union[builtins.str, builtins.int]"
 reveal_type(tup[2])  # E: Tuple index out of range \
                      # N: Revealed type is "Union[Any, builtins.str]"
-reveal_type(tup[:])  # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], Tuple[builtins.int, builtins.int, builtins.str]]"
+reveal_type(tup[:])  # N: Revealed type is "Union[tuple[builtins.int, builtins.str], tuple[builtins.int, builtins.int, builtins.str]]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1558,7 +1558,7 @@ reveal_type(tup[0])  # N: Revealed type is "builtins.int"
 reveal_type(tup[1])  # N: Revealed type is "Union[builtins.str, builtins.int]"
 reveal_type(tup[2])  # E: Tuple index out of range \
                      # N: Revealed type is "Union[Any, builtins.int]"
-reveal_type(tup[:])  # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], builtins.list[builtins.int]]"
+reveal_type(tup[:])  # N: Revealed type is "Union[tuple[builtins.int, builtins.str], builtins.list[builtins.int]]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1566,7 +1566,7 @@ reveal_type(tup[:])  # N: Revealed type is "Union[Tuple[builtins.int, builtins.s
 a = (1, "foo", 3)
 b = ("bar", 7)
 
-reveal_type(a + b)  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.int, builtins.str, builtins.int]"
+reveal_type(a + b)  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.int, builtins.str, builtins.int]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1586,7 +1586,7 @@ t1: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") # E:
                                                                            # N: Expression tuple item 10 has type "str"; "int" expected;
 
 # short tuple initializer assignment
-t2: Tuple[int, ...] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, ...]")
+t2: Tuple[int, ...] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "tuple[int, int, str, int]", variable has type "tuple[int, ...]")
 
 # long initializer assignment with few mismatches, no ellipsis
 t3: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "str", "str") # E: Incompatible types in assignment (2 tuple items are incompatible) \
@@ -1600,10 +1600,10 @@ t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3
                                                                                                                              # N: Expression tuple item 10 has type "str"; "int" expected;
 
 # short tuple initializer assignment, no ellipsis
-t5: Tuple[int, int] = (1, 2, "s", 4)  # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, int]")
+t5: Tuple[int, int] = (1, 2, "s", 4)  # E: Incompatible types in assignment (expression has type "tuple[int, int, str, int]", variable has type "tuple[int, int]")
 
 # long initializer assignment with mismatched pairs
-t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>])
+t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type tuple[int, int, ... <15 more items>], variable has type tuple[int, int, ... <10 more items>])
 
 [builtins fixtures/tuple.pyi]
 
@@ -1731,11 +1731,11 @@ x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same
 [case testMultiplyTupleByIntegerLiteral]
 from typing import Tuple
 t = ('',) * 2
-reveal_type(t)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(t)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 t2 = ('',) * -1
-reveal_type(t2)  # N: Revealed type is "Tuple[()]"
+reveal_type(t2)  # N: Revealed type is "tuple[()]"
 t3 = ('', 1) * 2
-reveal_type(t3)  # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]"
+reveal_type(t3)  # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.str, builtins.int]"
 def f() -> Tuple[str, ...]:
     return ('', )
 reveal_type(f() * 2)  # N: Revealed type is "builtins.tuple[builtins.str, ...]"
@@ -1746,18 +1746,18 @@ from typing import Tuple
 
 def f() -> Tuple[()]: ...
 
-reveal_type(f)    # N: Revealed type is "def () -> Tuple[()]"
-reveal_type(f())  # N: Revealed type is "Tuple[()]"
+reveal_type(f)    # N: Revealed type is "def () -> tuple[()]"
+reveal_type(f())  # N: Revealed type is "tuple[()]"
 [builtins fixtures/tuple.pyi]
 
 [case testMultiplyTupleByIntegerLiteralReverse]
 from typing import Tuple
 t = 2 * ('',)
-reveal_type(t)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(t)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 t2 = -1 * ('',)
-reveal_type(t2)  # N: Revealed type is "Tuple[()]"
+reveal_type(t2)  # N: Revealed type is "tuple[()]"
 t3 = 2 * ('', 1)
-reveal_type(t3)  # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]"
+reveal_type(t3)  # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.str, builtins.int]"
 def f() -> Tuple[str, ...]:
     return ('', )
 reveal_type(2 * f())  # N: Revealed type is "builtins.tuple[builtins.str, ...]"
@@ -1803,7 +1803,7 @@ def zip(i): ...
 
 def g(t: Tuple):
     reveal_type(zip(*t))  # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]"
-    reveal_type(zip(t))  # N: Revealed type is "typing.Iterator[Tuple[Any]]"
+    reveal_type(zip(t))  # N: Revealed type is "typing.Iterator[tuple[Any]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTupleSubclassSlice]
@@ -1813,5 +1813,5 @@ class A: ...
 
 class tuple_aa_subclass(Tuple[A, A]): ...
 
-inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:]  # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "tuple_aa_subclass")
+inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:]  # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple_aa_subclass")
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
index db314b136515..5f7646c62e96 100644
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -12,7 +12,7 @@ U = Union[int, str]
 def f(x: U) -> None: pass
 f(1)
 f('')
-f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]"
+f(()) # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "Union[int, str]"
 [targets __main__, __main__.f]
 [builtins fixtures/tuple.pyi]
 
@@ -21,7 +21,7 @@ from typing import Tuple
 T = Tuple[int, str]
 def f(x: T) -> None: pass
 f((1, 'x'))
-f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[int, str]"
+f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[int, str]"
 [targets __main__, __main__.f]
 [builtins fixtures/tuple.pyi]
 
@@ -64,7 +64,7 @@ from _m import U
 def f(x: U) -> None: pass
 f(1)
 f('x')
-f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]"
+f(()) # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "Union[int, str]"
 [file _m.py]
 from typing import Union
 U = Union[int, str]
@@ -168,11 +168,11 @@ f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "str"
 from typing import Tuple, Callable
 EmptyTuple = Tuple[()]
 x: EmptyTuple
-reveal_type(x)  # N: Revealed type is "Tuple[()]"
+reveal_type(x)  # N: Revealed type is "tuple[()]"
 
 EmptyTupleCallable = Callable[[Tuple[()]], None]
 f: EmptyTupleCallable
-reveal_type(f)  # N: Revealed type is "def (Tuple[()])"
+reveal_type(f)  # N: Revealed type is "def (tuple[()])"
 [builtins fixtures/list.pyi]
 
 [case testForwardTypeAlias]
@@ -188,7 +188,7 @@ from typing import TypeVar, Tuple
 def f(p: 'Alias[str]') -> None:
     pass
 
-reveal_type(f) # N: Revealed type is "def (p: Tuple[builtins.int, builtins.str])"
+reveal_type(f) # N: Revealed type is "def (p: tuple[builtins.int, builtins.str])"
 T = TypeVar('T')
 Alias = Tuple[int, T]
 [builtins fixtures/tuple.pyi]
@@ -375,25 +375,25 @@ class Cls:
 
 A1('no')  # E: Argument 1 to "C" has incompatible type "str"; expected "int"
 a1 = A1(1)
-reveal_type(a1)  # N: Revealed type is "Tuple[builtins.int, fallback=nt.C]"
+reveal_type(a1)  # N: Revealed type is "tuple[builtins.int, fallback=nt.C]"
 
 A2(0)  # E: Argument 1 to "D" has incompatible type "int"; expected "str"
 a2 = A2('yes')
-reveal_type(a2)  # N: Revealed type is "Tuple[builtins.str, fallback=nt.D]"
+reveal_type(a2)  # N: Revealed type is "tuple[builtins.str, fallback=nt.D]"
 
 a3 = A3()
-reveal_type(a3)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=nt.E]"
+reveal_type(a3)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=nt.E]"
 
 Cls.A1('no')  # E: Argument 1 has incompatible type "str"; expected "int"
 ca1 = Cls.A1(1)
-reveal_type(ca1)  # N: Revealed type is "Tuple[builtins.int, fallback=nt.C]"
+reveal_type(ca1)  # N: Revealed type is "tuple[builtins.int, fallback=nt.C]"
 
 Cls.A2(0)  # E: Argument 1 has incompatible type "int"; expected "str"
 ca2 = Cls.A2('yes')
-reveal_type(ca2)  # N: Revealed type is "Tuple[builtins.str, fallback=nt.D]"
+reveal_type(ca2)  # N: Revealed type is "tuple[builtins.str, fallback=nt.D]"
 
 ca3 = Cls.A3()
-reveal_type(ca3)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=nt.E]"
+reveal_type(ca3)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=nt.E]"
 [file nt.pyi]
 from typing import NamedTuple, Tuple
 
@@ -927,29 +927,29 @@ p = Parent()
 c = Child()
 
 NormalImplicit = 4   # E: Cannot assign multiple types to name "NormalImplicit" without an explicit "Type[...]" annotation \
-                     # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+                     # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 NormalExplicit = 4   # E: Cannot assign multiple types to name "NormalExplicit" without an explicit "Type[...]" annotation \
-                     # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+                     # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 SpecialImplicit = 4  # E: Cannot assign multiple types to name "SpecialImplicit" without an explicit "Type[...]" annotation
 SpecialExplicit = 4  # E: Cannot assign multiple types to name "SpecialExplicit" without an explicit "Type[...]" annotation
 
-Parent.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
-Parent.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+Parent.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
+Parent.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 Parent.SpecialImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "")
 Parent.SpecialExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "")
 
-Child.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
-Child.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+Child.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
+Child.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 Child.SpecialImplicit = 4
 Child.SpecialExplicit = 4
 
-p.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
-p.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+p.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
+p.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 p.SpecialImplicit = 4
 p.SpecialExplicit = 4
 
-c.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
-c.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
+c.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
+c.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 c.SpecialImplicit = 4
 c.SpecialExplicit = 4
 [builtins fixtures/tuple.pyi]
@@ -1105,7 +1105,7 @@ reveal_type(t3)  # N: Revealed type is "Any"
 
 T4 = TypeAliasType("T4")  # E: Missing positional argument "value" in call to "TypeAliasType"
 T5 = TypeAliasType("T5", int, str)  # E: Too many positional arguments for "TypeAliasType" \
-                                    # E: Argument 3 to "TypeAliasType" has incompatible type "Type[str]"; expected "Tuple[Union[TypeVar?, ParamSpec?, TypeVarTuple?], ...]"
+                                    # E: Argument 3 to "TypeAliasType" has incompatible type "type[str]"; expected "tuple[Union[TypeVar?, ParamSpec?, TypeVarTuple?], ...]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
@@ -1136,7 +1136,7 @@ VariadicAlias1 = TypeAliasType("VariadicAlias1", Tuple[Unpack[Ts]], type_params=
 VariadicAlias2 = TypeAliasType("VariadicAlias2", Tuple[Unpack[Ts], K], type_params=(Ts, K))
 VariadicAlias3 = TypeAliasType("VariadicAlias3", Callable[[Unpack[Ts]], int], type_params=(Ts,))
 xv: VariadicAlias1[int, str] = (1, 'a')
-yv: VariadicAlias1[str, int] = (1, 'a')  # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "Tuple[str, int]")
+yv: VariadicAlias1[str, int] = (1, 'a')  # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "tuple[str, int]")
 zv: VariadicAlias2[int, str] = (1, 'a')
 def int_in_int_out(x: int) -> int: return x
 wv: VariadicAlias3[int] = int_in_int_out
diff --git a/test-data/unit/check-type-object-type-inference.test b/test-data/unit/check-type-object-type-inference.test
index cc3a5514904d..b410815664d1 100644
--- a/test-data/unit/check-type-object-type-inference.test
+++ b/test-data/unit/check-type-object-type-inference.test
@@ -17,25 +17,25 @@ class F:
 def g(f: F):
     f.f(int).e(7)
     f.f(tuple[int,str])
-    f.f(tuple[int,str]).e('x')  # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, str]"
-    f.f(tuple[int,str]).e( (7,8) )  # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, str]"
+    f.f(tuple[int,str]).e('x')  # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[int, str]"
+    f.f(tuple[int,str]).e( (7,8) )  # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, int]"; expected "tuple[int, str]"
     f.f(tuple[int,str]).e( (7,'x') )  # OK
-    reveal_type(f.f(tuple[int,str]).e)  # N: Revealed type is "def (t: Tuple[builtins.int, builtins.str]) -> builtins.str"
+    reveal_type(f.f(tuple[int,str]).e)  # N: Revealed type is "def (t: tuple[builtins.int, builtins.str]) -> builtins.str"
 
 def h(f: F):
     f.f(int).e(7)
     f.f(tuple)
-    f.f(tuple).e('y') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[Any, ...]"
+    f.f(tuple).e('y') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[Any, ...]"
     f.f(tuple).e( (8,'y') )  # OK
     reveal_type(f.f(tuple).e)  # N: Revealed type is "def (t: builtins.tuple[Any, ...]) -> builtins.str"
 
 def i(f: F):
     f.f(tuple[int,tuple[int,str]])
-    f.f(tuple[int,tuple[int,str]]).e('z')  # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, Tuple[int, str]]"
-    f.f(tuple[int,tuple[int,str]]).e( (8,9) )  # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, Tuple[int, str]]"
-    f.f(tuple[int,tuple[int,str]]).e( (17, (28, 29)) )  # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, Tuple[int, int]]"; expected "Tuple[int, Tuple[int, str]]"
+    f.f(tuple[int,tuple[int,str]]).e('z')  # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[int, tuple[int, str]]"
+    f.f(tuple[int,tuple[int,str]]).e( (8,9) )  # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, int]"; expected "tuple[int, tuple[int, str]]"
+    f.f(tuple[int,tuple[int,str]]).e( (17, (28, 29)) )  # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, tuple[int, int]]"; expected "tuple[int, tuple[int, str]]"
     f.f(tuple[int,tuple[int,str]]).e( (27,(28,'z')) )  # OK
-    reveal_type(f.f(tuple[int,tuple[int,str]]).e)  # N: Revealed type is "def (t: Tuple[builtins.int, Tuple[builtins.int, builtins.str]]) -> builtins.str"
+    reveal_type(f.f(tuple[int,tuple[int,str]]).e)  # N: Revealed type is "def (t: tuple[builtins.int, tuple[builtins.int, builtins.str]]) -> builtins.str"
 
 x = tuple[int,str][str]  # False negative
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index cae90d56c3a6..f9d7ce7fc975 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -443,7 +443,7 @@ reveal_type(D(x=[]))  # N: Revealed type is "TypedDict('__main__.D', {'x': built
 from typing import Dict, MutableMapping, TypedDict
 Point = TypedDict('Point', {'x': int, 'y': int})
 def as_dict(p: Point) -> Dict[str, int]:
-    return p  # E: Incompatible return value type (got "Point", expected "Dict[str, int]")
+    return p  # E: Incompatible return value type (got "Point", expected "dict[str, int]")
 def as_mutable_mapping(p: Point) -> MutableMapping[str, object]:
     return p  # E: Incompatible return value type (got "Point", expected "MutableMapping[str, object]")
 [builtins fixtures/dict.pyi]
@@ -470,9 +470,9 @@ c: C
 def f(a: A) -> None: pass
 
 l = [a, b]  # Join generates an anonymous TypedDict
-f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int})]"; expected "A"
+f(l) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x': int})]"; expected "A"
 ll = [b, c]
-f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int, 'z': str})]"; expected "A"
+f(ll) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x': int, 'z': str})]"; expected "A"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -826,7 +826,7 @@ class C:
         A = TypedDict('A', {'x': int})
     def g(self):
         A = TypedDict('A', {'y': int})
-C.A  # E: "Type[C]" has no attribute "A"
+C.A  # E: "type[C]" has no attribute "A"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -923,7 +923,7 @@ A = TypedDict('A', {'@type': Literal['a-type'], 'value': int})
 B = TypedDict('B', {'@type': Literal['b-type'], 'value': int})
 
 c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'}  # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
-                                                       # E: Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "Union[A, B]")
+                                                       # E: Incompatible types in assignment (expression has type "dict[str, str]", variable has type "Union[A, B]")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -1231,9 +1231,9 @@ c: C
 def f(a: A) -> None: pass
 
 l = [a, b]  # Join generates an anonymous TypedDict
-f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int})]"; expected "A"
+f(l) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x'?: int})]"; expected "A"
 ll = [b, c]
-f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int, 'z'?: str})]"; expected "A"
+f(ll) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x'?: int, 'z'?: str})]"; expected "A"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -1694,7 +1694,7 @@ a.clear() # E: "A" has no attribute "clear"
 a.setdefault('invalid', 1) # E: TypedDict "A" has no key "invalid"
 reveal_type(a.setdefault('x', 1)) # N: Revealed type is "builtins.int"
 reveal_type(a.setdefault('y', [])) # N: Revealed type is "builtins.list[builtins.int]"
-a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "List[int]"
+a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "list[int]"
 x = ''
 a.setdefault(x, 1) # E: Expected TypedDict key to be string literal
 alias = a.setdefault
@@ -1709,7 +1709,7 @@ a.update({'z': 1}) # E: Unexpected TypedDict key "z"
 a.update({'z': 1, 'zz': 1}) # E: Unexpected TypedDict keys ("z", "zz")
 a.update({'z': 1, 'x': 1}) # E: Expected TypedDict key "x" but found keys ("z", "x")
 d = {'x': 1}
-a.update(d) # E: Argument 1 to "update" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'x'?: int, 'y'?: List[int]})"
+a.update(d) # E: Argument 1 to "update" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'x'?: int, 'y'?: list[int]})"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -1724,7 +1724,7 @@ b: B
 reveal_type(a.pop('x')) # N: Revealed type is "builtins.int"
 reveal_type(a.pop('y', [])) # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(a.pop('x', '')) # N: Revealed type is "Union[builtins.int, Literal['']?]"
-reveal_type(a.pop('x', (1, 2))) # N: Revealed type is "Union[builtins.int, Tuple[Literal[1]?, Literal[2]?]]"
+reveal_type(a.pop('x', (1, 2))) # N: Revealed type is "Union[builtins.int, tuple[Literal[1]?, Literal[2]?]]"
 a.pop('invalid', '') # E: TypedDict "A" has no key "invalid"
 b.pop('x') # E: Key "x" of TypedDict "B" cannot be deleted
 x = ''
@@ -1863,7 +1863,7 @@ class Config(TypedDict):
 x: Dict[str, str]
 y: Config
 
-x == y  # E: Non-overlapping equality check (left operand type: "Dict[str, str]", right operand type: "Config")
+x == y  # E: Non-overlapping equality check (left operand type: "dict[str, str]", right operand type: "Config")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -1893,7 +1893,7 @@ class Config(TypedDict, total=False):
 x: Dict[str, str]
 y: Config
 
-x == y  # E: Non-overlapping equality check (left operand type: "Dict[str, str]", right operand type: "Config")
+x == y  # E: Non-overlapping equality check (left operand type: "dict[str, str]", right operand type: "Config")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -1906,7 +1906,7 @@ class Config(TypedDict):
     b: str
 
 x: Config
-x == {}  # E: Non-overlapping equality check (left operand type: "Config", right operand type: "Dict[Never, Never]")
+x == {}  # E: Non-overlapping equality check (left operand type: "Config", right operand type: "dict[Never, Never]")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -2797,11 +2797,11 @@ Alias = TD[List[T]]
 
 ad: Alias[str]
 reveal_type(ad)  # N: Revealed type is "TypedDict('__main__.TD', {'key': builtins.int, 'value': builtins.list[builtins.str]})"
-Alias[str](key=0, value=0)  # E: Incompatible types (expression has type "int", TypedDict item "value" has type "List[str]")
+Alias[str](key=0, value=0)  # E: Incompatible types (expression has type "int", TypedDict item "value" has type "list[str]")
 
 # Generic aliases are *always* filled with Any, so this is different from TD(...) call.
 Alias(key=0, value=0)  # E: Missing type parameters for generic type "Alias" \
-                       # E: Incompatible types (expression has type "int", TypedDict item "value" has type "List[Any]")
+                       # E: Incompatible types (expression has type "int", TypedDict item "value" has type "list[Any]")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -2902,7 +2902,7 @@ def method(message: Response) -> None: ...
 method({'type': 'a', 'value': True})  # OK
 method({'type': 'b', 'value': 'abc'})  # OK
 method({'type': 'a', 'value': 'abc'})  # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
-                                       # E: Argument 1 to "method" has incompatible type "Dict[str, str]"; expected "Union[A, B]"
+                                       # E: Argument 1 to "method" has incompatible type "dict[str, str]"; expected "Union[A, B]"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -2921,7 +2921,7 @@ class D(TypedDict, total=False):
 def foo(data: Union[A, B]) -> None: ...
 foo({"foo": {"c": "foo"}})  # OK
 foo({"foo": {"e": "foo"}})  # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
-                            # E: Argument 1 to "foo" has incompatible type "Dict[str, Dict[str, str]]"; expected "Union[A, B]"
+                            # E: Argument 1 to "foo" has incompatible type "dict[str, dict[str, str]]"; expected "Union[A, B]"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -3196,7 +3196,7 @@ class Bar(TypedDict):
     pass
 
 foo: Dict[str, Any] = {}
-bar: Bar = {**foo}  # E: Unsupported type "Dict[str, Any]" for ** expansion in TypedDict
+bar: Bar = {**foo}  # E: Unsupported type "dict[str, Any]" for ** expansion in TypedDict
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -3326,7 +3326,7 @@ d1: Dict[str, int]
 d2: Dict[int, str]
 
 reveal_type(foo1 | d1)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
-foo1 | d2  # E: Unsupported operand types for | ("Foo" and "Dict[int, str]")
+foo1 | d2  # E: Unsupported operand types for | ("Foo" and "dict[int, str]")
 
 
 class Bar(TypedDict):
@@ -3344,7 +3344,7 @@ reveal_type(bar | {'key': 'a', 'value': 1})  # N: Revealed type is "builtins.dic
 
 reveal_type(bar | foo1)  # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})"
 reveal_type(bar | d1)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
-bar | d2  # E: Unsupported operand types for | ("Bar" and "Dict[int, str]")
+bar | d2  # E: Unsupported operand types for | ("Bar" and "dict[int, str]")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict-iror.pyi]
 
@@ -3363,11 +3363,11 @@ foo | SubDict()
 main:7: error: No overload variant of "__or__" of "TypedDict" matches argument type "int"
 main:7: note: Possible overload variants:
 main:7: note:     def __or__(self, TypedDict({'key'?: int}), /) -> Foo
-main:7: note:     def __or__(self, Dict[str, Any], /) -> Dict[str, object]
+main:7: note:     def __or__(self, dict[str, Any], /) -> dict[str, object]
 main:10: error: No overload variant of "__ror__" of "dict" matches argument type "Foo"
 main:10: note: Possible overload variants:
-main:10: note:     def __ror__(self, Dict[Any, Any], /) -> Dict[Any, Any]
-main:10: note:     def [T, T2] __ror__(self, Dict[T, T2], /) -> Dict[Union[Any, T], Union[Any, T2]]
+main:10: note:     def __ror__(self, dict[Any, Any], /) -> dict[Any, Any]
+main:10: note:     def [T, T2] __ror__(self, dict[T, T2], /) -> dict[Union[Any, T], Union[Any, T2]]
 [builtins fixtures/dict-full.pyi]
 [typing fixtures/typing-typeddict-iror.pyi]
 
@@ -3388,7 +3388,7 @@ d1: Dict[str, int]
 d2: Dict[int, str]
 
 reveal_type(d1 | foo)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
-d2 | foo  # E: Unsupported operand types for | ("Dict[int, str]" and "Foo")
+d2 | foo  # E: Unsupported operand types for | ("dict[int, str]" and "Foo")
 1 | foo  # E: Unsupported left operand type for | ("int")
 
 
@@ -3406,7 +3406,7 @@ reveal_type({'value': 1} | bar)  # N: Revealed type is "builtins.dict[builtins.s
 reveal_type({'key': 'a', 'value': 1} | bar)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
 
 reveal_type(d1 | bar)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
-d2 | bar  # E: Unsupported operand types for | ("Dict[int, str]" and "Bar")
+d2 | bar  # E: Unsupported operand types for | ("dict[int, str]" and "Bar")
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict-iror.pyi]
 
@@ -3427,8 +3427,8 @@ foo |= {'b': 2}  # E: Unexpected TypedDict key "b"
 d1: Dict[str, int]
 d2: Dict[int, str]
 
-foo |= d1  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int})"
-foo |= d2  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int})"
+foo |= d1  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'key'?: int})"
+foo |= d2  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[int, str]"; expected "TypedDict({'key'?: int})"
 
 
 class Bar(TypedDict):
@@ -3442,8 +3442,8 @@ bar |= {'key': 'a', 'value': 'a', 'b': 'a'}  # E: Expected TypedDict keys ("key"
                                              # E: Incompatible types (expression has type "str", TypedDict item "key" has type "int")
 
 bar |= foo
-bar |= d1  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int, 'value'?: str})"
-bar |= d2  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})"
+bar |= d1  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'key'?: int, 'value'?: str})"
+bar |= d2  # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict-iror.pyi]
 
@@ -3526,7 +3526,7 @@ class Point(TypedDict, total=False):
     y: int
 
 def func(cls: Type[Point]) -> None:
-    reveal_type(cls)  # N: Revealed type is "Type[TypedDict('__main__.Point', {'x': builtins.int, 'y'?: builtins.int})]"
+    reveal_type(cls)  # N: Revealed type is "type[TypedDict('__main__.Point', {'x': builtins.int, 'y'?: builtins.int})]"
     cls(x=1, y=2)
     cls(1, 2)  # E: Too many positional arguments
     cls(x=1)
@@ -3550,7 +3550,7 @@ class A(Generic[T]):
         self.a = a
 
     def func(self) -> T:
-        reveal_type(self.a)  # N: Revealed type is "Type[T`1]"
+        reveal_type(self.a)  # N: Revealed type is "type[T`1]"
         self.a(x=1, y=2)
         self.a(y=2)  # E: Missing named argument "x"
         return self.a(x=1)
@@ -3863,7 +3863,7 @@ tp: TP = {**r, **m}
 tp1: TP = {**tp, **m}
 tp2: TP = {**r, **m}
 tp3: TP = {**tp, **r}
-tp4: TP = {**tp, **d}  # E: Unsupported type "Dict[str, object]" for ** expansion in TypedDict
+tp4: TP = {**tp, **d}  # E: Unsupported type "dict[str, object]" for ** expansion in TypedDict
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
@@ -3984,7 +3984,7 @@ def accepts_dict(d: Dict[str, object]): ...
 x: TP
 accepts_mapping(x)
 accepts_mutable_mapping(x)  # E: Argument 1 to "accepts_mutable_mapping" has incompatible type "TP"; expected "MutableMapping[str, object]"
-accepts_dict(x)  # E: Argument 1 to "accepts_dict" has incompatible type "TP"; expected "Dict[str, object]"
+accepts_dict(x)  # E: Argument 1 to "accepts_dict" has incompatible type "TP"; expected "dict[str, object]"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index 94aa7ec6ffb8..e17a7f80e756 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -84,7 +84,7 @@ T = TypeVar('T')
 def is_two_element_tuple(a: Tuple[T, ...]) -> TypeGuard[Tuple[T, T]]: pass
 def main(a: Tuple[T, ...]):
     if is_two_element_tuple(a):
-        reveal_type(a)  # N: Revealed type is "Tuple[T`-1, T`-1]"
+        reveal_type(a)  # N: Revealed type is "tuple[T`-1, T`-1]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeGuardPassedAsTypeVarIsBool]
@@ -258,7 +258,7 @@ def main1(a: object) -> None:
 
     ta = (a,)
     if is_float(*ta):  # E: Type guard requires positional argument
-        reveal_type(ta)  # N: Revealed type is "Tuple[builtins.object]"
+        reveal_type(ta)  # N: Revealed type is "tuple[builtins.object]"
         reveal_type(a)  # N: Revealed type is "builtins.object"
 
     la = [a]
@@ -452,7 +452,7 @@ def g(x: object) -> None: ...
 def test(x: List[object]) -> None:
     if not(f(x) or isinstance(x, A)):
         return
-    g(reveal_type(x))  # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]"
+    g(reveal_type(x))  # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeGuardMultipleCondition-xfail]
@@ -615,7 +615,7 @@ def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeGuard[Tuple[_T, _T]]:
 def func(names: Tuple[str, ...]):
     reveal_type(names)  # N: Revealed type is "builtins.tuple[builtins.str, ...]"
     if is_two_element_tuple(names):
-        reveal_type(names)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+        reveal_type(names)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeGuardErroneousDefinitionFails]
diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
index 356b1abfdf63..bb8beac72c3a 100644
--- a/test-data/unit/check-typeis.test
+++ b/test-data/unit/check-typeis.test
@@ -454,7 +454,7 @@ def g(x: object) -> None: ...
 def test(x: List[Any]) -> None:
     if not(f(x) or isinstance(x, A)):
         return
-    g(reveal_type(x))  # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]"
+    g(reveal_type(x))  # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeIsMultipleCondition]
@@ -640,7 +640,7 @@ def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeIs[Tuple[_T, _T]]:
 def func(names: Tuple[str, ...]):
     reveal_type(names)  # N: Revealed type is "builtins.tuple[builtins.str, ...]"
     if is_two_element_tuple(names):
-        reveal_type(names)  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+        reveal_type(names)  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeIsErroneousDefinitionFails]
@@ -761,7 +761,7 @@ def f(x: str) -> TypeIs[int]:  # E: Narrowed type "int" is not a subtype of inpu
 
 T = TypeVar('T')
 
-def g(x: List[T]) -> TypeIs[Sequence[T]]:  # E: Narrowed type "Sequence[T]" is not a subtype of input type "List[T]"
+def g(x: List[T]) -> TypeIs[Sequence[T]]:  # E: Narrowed type "Sequence[T]" is not a subtype of input type "list[T]"
     pass
 
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test
index 93d20eb26f6e..33a639eee580 100644
--- a/test-data/unit/check-typevar-defaults.test
+++ b/test-data/unit/check-typevar-defaults.test
@@ -13,7 +13,7 @@ def f2(a: Callable[P1, None]) -> Callable[P1, None]: ...
 reveal_type(f2)  # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)"
 
 def f3(a: Tuple[Unpack[Ts1]]) -> Tuple[Unpack[Ts1]]: ...
-reveal_type(f3)  # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] (a: Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]) -> Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]"
+reveal_type(f3)  # N: Revealed type is "def [Ts1 = Unpack[tuple[builtins.int, builtins.str]]] (a: tuple[Unpack[Ts1`-1 = Unpack[tuple[builtins.int, builtins.str]]]]) -> tuple[Unpack[Ts1`-1 = Unpack[tuple[builtins.int, builtins.str]]]]"
 
 
 class ClassA1(Generic[T1]): ...
@@ -22,7 +22,7 @@ class ClassA3(Generic[Unpack[Ts1]]): ...
 
 reveal_type(ClassA1)  # N: Revealed type is "def [T1 = builtins.int] () -> __main__.ClassA1[T1`1 = builtins.int]"
 reveal_type(ClassA2)  # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] () -> __main__.ClassA2[P1`1 = [builtins.int, builtins.str]]"
-reveal_type(ClassA3)  # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[Tuple[builtins.int, builtins.str]]]]"
+reveal_type(ClassA3)  # N: Revealed type is "def [Ts1 = Unpack[tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[tuple[builtins.int, builtins.str]]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarDefaultsValid]
@@ -181,7 +181,7 @@ reveal_type(func_b1(2))  # N: Revealed type is "def (builtins.int, builtins.str)
 
 def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: ...
 # reveal_type(func_c1(callback1))  # Revealed type is "Tuple[str]"  # TODO
-reveal_type(func_c1(2))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(func_c1(2))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarDefaultsClass1]
@@ -544,11 +544,11 @@ def func_a2(
     d: TA2[float, float, float],
     e: TA2[float, float, float, float],  # E: Bad number of arguments for type alias, expected between 1 and 3, given 4
 ) -> None:
-    reveal_type(a)  # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]"
-    reveal_type(b)  # N: Revealed type is "Tuple[builtins.float, builtins.int, builtins.str]"
-    reveal_type(c)  # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.str]"
-    reveal_type(d)  # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]"
-    reveal_type(e)  # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]"
+    reveal_type(a)  # N: Revealed type is "tuple[Any, builtins.int, builtins.str]"
+    reveal_type(b)  # N: Revealed type is "tuple[builtins.float, builtins.int, builtins.str]"
+    reveal_type(c)  # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.str]"
+    reveal_type(d)  # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.float]"
+    reveal_type(e)  # N: Revealed type is "tuple[Any, builtins.int, builtins.str]"
 
 TA3 = Union[Dict[T1, T2], List[T3]]
 
@@ -574,11 +574,11 @@ def func_a4(
     d: TA4[float, float, float],
     e: TA4[float, float, float, float],  # E: Bad number of arguments for type alias, expected between 2 and 3, given 4
 ) -> None:
-    reveal_type(a)  # N: Revealed type is "Tuple[Any, Any, builtins.int]"
-    reveal_type(b)  # N: Revealed type is "Tuple[Any, Any, builtins.int]"
-    reveal_type(c)  # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.int]"
-    reveal_type(d)  # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]"
-    reveal_type(e)  # N: Revealed type is "Tuple[Any, Any, builtins.int]"
+    reveal_type(a)  # N: Revealed type is "tuple[Any, Any, builtins.int]"
+    reveal_type(b)  # N: Revealed type is "tuple[Any, Any, builtins.int]"
+    reveal_type(c)  # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.int]"
+    reveal_type(d)  # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.float]"
+    reveal_type(e)  # N: Revealed type is "tuple[Any, Any, builtins.int]"
 [builtins fixtures/dict.pyi]
 
 [case testTypeVarDefaultsTypeAlias2]
@@ -638,7 +638,7 @@ def func_c1(
     b: TC1[float],
 ) -> None:
     # reveal_type(a)  # Revealed type is "Tuple[builtins.int, builtins.str]"  # TODO
-    reveal_type(b)  # N: Revealed type is "Tuple[builtins.float]"
+    reveal_type(b)  # N: Revealed type is "tuple[builtins.float]"
 
 TC2 = Tuple[T3, Unpack[Ts3]]
 
@@ -649,7 +649,7 @@ def func_c2(
 ) -> None:
     # reveal_type(a)  # Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]"  # TODO
     # reveal_type(b)  # Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]"  # TODO
-    reveal_type(c)  # N: Revealed type is "Tuple[builtins.int]"
+    reveal_type(c)  # N: Revealed type is "tuple[builtins.int]"
 
 TC3 = Tuple[T3, Unpack[Ts4]]
 
@@ -659,8 +659,8 @@ def func_c3(
     c: TC3[int, Unpack[Tuple[float]]],
 ) -> None:
     # reveal_type(a)  # Revealed type is "Tuple[builtins.str]"  # TODO
-    reveal_type(b)  # N: Revealed type is "Tuple[builtins.int]"
-    reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, builtins.float]"
+    reveal_type(b)  # N: Revealed type is "tuple[builtins.int]"
+    reveal_type(c)  # N: Revealed type is "tuple[builtins.int, builtins.float]"
 
 TC4 = Tuple[T1, Unpack[Ts1], T3]
 
@@ -669,9 +669,9 @@ def func_c4(
     b: TC4[int],
     c: TC4[int, float],
 ) -> None:
-    reveal_type(a)  # N: Revealed type is "Tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]"
+    reveal_type(a)  # N: Revealed type is "tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]"
     # reveal_type(b)  # Revealed type is "Tuple[builtins.int, builtins.str]"  # TODO
-    reveal_type(c)  # N: Revealed type is "Tuple[builtins.int, builtins.float]"
+    reveal_type(c)  # N: Revealed type is "tuple[builtins.int, builtins.float]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarDefaultsTypeAliasRecursive1]
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index d364439f22e9..41e90c3f8506 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -13,17 +13,17 @@ args2: Tuple[bool, str] = (False, 'y')
 args3: Tuple[int, str, bool] = (2, 'z', True)
 varargs: Tuple[int, ...] = (1, 2, 3)
 
-reveal_type(f(args))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(f(args))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 reveal_type(f(varargs))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 
-f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Never, ...]"
+f(0)  # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[Never, ...]"
 
 def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]:
     return a
 
-reveal_type(g(args, args))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
-reveal_type(g(args, args2))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(g(args, args))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
+reveal_type(g(args, args2))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 reveal_type(g(args, args3))  # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]"
 reveal_type(g(any, any))  # N: Revealed type is "builtins.tuple[Any, ...]"
 [builtins fixtures/tuple.pyi]
@@ -54,21 +54,21 @@ f_args: Tuple[int, str]
 f_args2: Tuple[int]
 f_args3: Tuple[int, str, bool]
 
-reveal_type(f(f_args))  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
-reveal_type(f(f_args2))  # N: Revealed type is "Tuple[builtins.str]"
-reveal_type(f(f_args3))  # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.bool]"
-f(empty)  # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Tuple[int]"
-f(bad_args)  # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[int, str]"
+reveal_type(f(f_args))  # N: Revealed type is "tuple[builtins.str, builtins.str]"
+reveal_type(f(f_args2))  # N: Revealed type is "tuple[builtins.str]"
+reveal_type(f(f_args3))  # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.bool]"
+f(empty)  # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "tuple[int]"
+f(bad_args)  # E: Argument 1 to "f" has incompatible type "tuple[str, str]"; expected "tuple[int, str]"
 
 # The reason for error in subtle: actual can be empty, formal cannot.
-reveal_type(f(var_len_tuple))  # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]]]" \
-                               # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]"
+reveal_type(f(var_len_tuple))  # N: Revealed type is "tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]]]" \
+                               # E: Argument 1 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]]]"
 
 g_args: Tuple[str, int]
-reveal_type(g(g_args))  # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+reveal_type(g(g_args))  # N: Revealed type is "tuple[builtins.str, builtins.str]"
 
 h_args: Tuple[bool, int, str, int, str, object]
-reveal_type(h(h_args))  # N: Revealed type is "Tuple[builtins.str, builtins.int]"
+reveal_type(h(h_args))  # N: Revealed type is "tuple[builtins.str, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleChaining]
@@ -91,8 +91,8 @@ def h(a: Tuple[bool, int, Unpack[Ts], str, object]) -> Tuple[str, Unpack[Ts]]:
     return x
 
 args: Tuple[bool, int, str, int, str, object]
-reveal_type(g(args))  # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.int]"
-reveal_type(h(args))  # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.int]"
+reveal_type(g(args))  # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.int]"
+reveal_type(h(args))  # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleGenericClassDefn]
@@ -147,7 +147,7 @@ def foo(t: Variadic[int, Unpack[Ts], object]) -> Tuple[int, Unpack[Ts]]:
     ...
 
 v: Variadic[int, str, bool, object]
-reveal_type(foo(v))  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]"
+reveal_type(foo(v))  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleGenericClassWithMethods]
@@ -166,7 +166,7 @@ class Variadic(Generic[T, Unpack[Ts], S]):
         ...
 
 v: Variadic[float, str, bool, object]
-reveal_type(v.foo(0))  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]"
+reveal_type(v.foo(0))  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleIsNotValidAliasTarget]
@@ -306,7 +306,7 @@ def prefix_tuple(
     ...
 
 z = prefix_tuple(x=0, y=(True, 'a'))
-reveal_type(z)  # N: Revealed type is "Tuple[builtins.int, builtins.bool, builtins.str]"
+reveal_type(z)  # N: Revealed type is "tuple[builtins.int, builtins.bool, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTuplePep646TypeVarTupleUnpacking]
@@ -333,7 +333,7 @@ process_batch_channels(x)
 y: Array[Batch, Channels]
 process_batch_channels(y)
 z: Array[Batch]
-process_batch_channels(z)  # E: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, Unpack[Tuple[Any, ...]], Channels]"
+process_batch_channels(z)  # E: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, Unpack[tuple[Any, ...]], Channels]"
 
 u: Array[Unpack[Tuple[Any, ...]]]
 
@@ -356,11 +356,11 @@ Ts2 = TypeVarTuple("Ts2")
 def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one Unpack in a type is not allowed
 
     ...
-reveal_type(bad)  # N: Revealed type is "def [Ts, Ts2] (x: Tuple[builtins.int, Unpack[Ts`-1], builtins.str])"
+reveal_type(bad)  # N: Revealed type is "def [Ts, Ts2] (x: tuple[builtins.int, Unpack[Ts`-1], builtins.str])"
 
 def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None:  # E: More than one Unpack in a type is not allowed
     ...
-reveal_type(bad2)  # N: Revealed type is "def (x: Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])"
+reveal_type(bad2)  # N: Revealed type is "def (x: tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTuplePep646TypeVarStarArgsBasic]
@@ -370,23 +370,23 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 
 def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
-    reveal_type(args)  # N: Revealed type is "Tuple[Unpack[Ts`-1]]"
-    reveal_type(args_to_tuple(1, *args))  # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1]]"
-    reveal_type(args_to_tuple(*args, 'a'))  # N: Revealed type is "Tuple[Unpack[Ts`-1], Literal['a']?]"
-    reveal_type(args_to_tuple(1, *args, 'a'))  # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1], Literal['a']?]"
+    reveal_type(args)  # N: Revealed type is "tuple[Unpack[Ts`-1]]"
+    reveal_type(args_to_tuple(1, *args))  # N: Revealed type is "tuple[Literal[1]?, Unpack[Ts`-1]]"
+    reveal_type(args_to_tuple(*args, 'a'))  # N: Revealed type is "tuple[Unpack[Ts`-1], Literal['a']?]"
+    reveal_type(args_to_tuple(1, *args, 'a'))  # N: Revealed type is "tuple[Literal[1]?, Unpack[Ts`-1], Literal['a']?]"
     args_to_tuple(*args, *args)  # E: Passing multiple variadic unpacks in a call is not supported
     ok = (1, 'a')
-    reveal_type(args_to_tuple(*ok, *ok))  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.int, builtins.str]"
+    reveal_type(args_to_tuple(*ok, *ok))  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.int, builtins.str]"
     if int():
         return args
     else:
         return args_to_tuple(*args)
 
-reveal_type(args_to_tuple(1, 'a'))  # N: Revealed type is "Tuple[Literal[1]?, Literal['a']?]"
+reveal_type(args_to_tuple(1, 'a'))  # N: Revealed type is "tuple[Literal[1]?, Literal['a']?]"
 vt: Tuple[int, ...]
-reveal_type(args_to_tuple(1, *vt))  # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]]]"
-reveal_type(args_to_tuple(*vt, 'a'))  # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]"
-reveal_type(args_to_tuple(1, *vt, 'a'))  # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]"
+reveal_type(args_to_tuple(1, *vt))  # N: Revealed type is "tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]]]"
+reveal_type(args_to_tuple(*vt, 'a'))  # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]"
+reveal_type(args_to_tuple(1, *vt, 'a'))  # N: Revealed type is "tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]"
 args_to_tuple(*vt, *vt)  # E: Passing multiple variadic unpacks in a call is not supported
 [builtins fixtures/tuple.pyi]
 
@@ -398,34 +398,34 @@ Ts = TypeVarTuple("Ts")
 
 def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
     with_prefix_suffix(*args)  # E: Too few arguments for "with_prefix_suffix" \
-                               # E: Argument 1 to "with_prefix_suffix" has incompatible type "*Tuple[Unpack[Ts]]"; expected "bool"
+                               # E: Argument 1 to "with_prefix_suffix" has incompatible type "*tuple[Unpack[Ts]]"; expected "bool"
     new_args = (True, "foo", *args, 5)
     with_prefix_suffix(*new_args)
     return args
 
 def with_prefix_suffix(*args: Unpack[Tuple[bool, str, Unpack[Ts], int]]) -> Tuple[bool, str, Unpack[Ts], int]:
-    reveal_type(args)  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
-    reveal_type(args_to_tuple(*args))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
-    reveal_type(args_to_tuple(1, *args, 'a'))  # N: Revealed type is "Tuple[Literal[1]?, builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int, Literal['a']?]"
+    reveal_type(args)  # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
+    reveal_type(args_to_tuple(*args))  # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
+    reveal_type(args_to_tuple(1, *args, 'a'))  # N: Revealed type is "tuple[Literal[1]?, builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int, Literal['a']?]"
     return args
 
-reveal_type(with_prefix_suffix(True, "bar", "foo", 5))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]"
-reveal_type(with_prefix_suffix(True, "bar", 5))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, builtins.int]"
+reveal_type(with_prefix_suffix(True, "bar", "foo", 5))  # N: Revealed type is "tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]"
+reveal_type(with_prefix_suffix(True, "bar", 5))  # N: Revealed type is "tuple[builtins.bool, builtins.str, builtins.int]"
 
 with_prefix_suffix(True, "bar", "foo", 1.0)  # E: Argument 4 to "with_prefix_suffix" has incompatible type "float"; expected "int"
 with_prefix_suffix(True, "bar")  # E: Too few arguments for "with_prefix_suffix"
 
 t = (True, "bar", "foo", 5)
-reveal_type(with_prefix_suffix(*t))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, builtins.str, builtins.int]"
-reveal_type(with_prefix_suffix(True, *("bar", "foo"), 5))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]"
+reveal_type(with_prefix_suffix(*t))  # N: Revealed type is "tuple[builtins.bool, builtins.str, builtins.str, builtins.int]"
+reveal_type(with_prefix_suffix(True, *("bar", "foo"), 5))  # N: Revealed type is "tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]"
 
-reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[builtins.tuple[builtins.str, ...]], builtins.int]"
+reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5))  # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[builtins.tuple[builtins.str, ...]], builtins.int]"
 
 bad_t = (True, "bar")
 with_prefix_suffix(*bad_t)  # E: Too few arguments for "with_prefix_suffix"
 
 def foo(*args: Unpack[Ts]) -> None:
-    reveal_type(with_prefix_suffix(True, "bar", *args, 5))  # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
+    reveal_type(with_prefix_suffix(True, "bar", *args, 5))  # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTuplePep646TypeVarStarArgsFixedLengthTuple]
@@ -433,7 +433,7 @@ from typing import Tuple
 from typing_extensions import Unpack
 
 def foo(*args: Unpack[Tuple[int, str]]) -> None:
-    reveal_type(args)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+    reveal_type(args)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 foo(0, "foo")
 foo(0, 1)  # E: Argument 2 to "foo" has incompatible type "int"; expected "str"
@@ -444,15 +444,15 @@ foo()  # E: Too few arguments for "foo"
 foo(*(0, "foo"))
 
 def foo2(*args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None:
-    reveal_type(args)  # N: Revealed type is "Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]"
+    reveal_type(args)  # N: Revealed type is "tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]"
 
 # It is hard to normalize callable types in definition, because there is deep relation between `FuncDef.type`
 # and `FuncDef.arguments`, therefore various typeops need to be sure to normalize Callable types before using them.
-reveal_type(foo2)  # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])"
+reveal_type(foo2)  # N: Revealed type is "def (*args: Unpack[tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])"
 
 class C:
     def foo2(self, *args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: ...
-reveal_type(C().foo2)  # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])"
+reveal_type(C().foo2)  # N: Revealed type is "def (*args: Unpack[tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTuplePep646TypeVarStarArgsVariableLengthTuple]
@@ -466,7 +466,7 @@ foo(0, 1, 2)
 foo(0, 1, "bar")  # E: Argument 3 to "foo" has incompatible type "str"; expected "int"
 
 def foo2(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]) -> None:
-    reveal_type(args)  # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.bool, builtins.bool]"
+    reveal_type(args)  # N: Revealed type is "tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.bool, builtins.bool]"
     reveal_type(args[1])  # N: Revealed type is "builtins.int"
 
 def foo3(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], str, float]]) -> None:
@@ -480,7 +480,7 @@ def foo3(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], str, float]]) -> None
     reveal_type(args[-3])  # N: Revealed type is "Union[builtins.str, builtins.int]"
     args[-4]  # E: Tuple index out of range \
               # N: Variadic tuple can have length 3
-    reveal_type(args[::-1])  # N: Revealed type is "Tuple[builtins.float, builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.str]"
+    reveal_type(args[::-1])  # N: Revealed type is "tuple[builtins.float, builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.str]"
     args[::2]  # E: Ambiguous slice of a variadic tuple
     args[:2]  # E: Ambiguous slice of a variadic tuple
 
@@ -490,8 +490,8 @@ def foo4(*args: Unpack[Tuple[str, Unpack[Ts], bool, bool]]) -> None:
 
 foo2("bar", 1, 2, 3, False, True)
 foo2(0, 1, 2, 3, False, True)  # E: Argument 1 to "foo2" has incompatible type "int"; expected "str"
-foo2("bar", "bar", 2, 3, False, True)  # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]"
-foo2("bar", 1, 2, 3, 4, True)  # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]"
+foo2("bar", "bar", 2, 3, False, True)  # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[tuple[Unpack[tuple[int, ...]], bool, bool]]"
+foo2("bar", 1, 2, 3, 4, True)  # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[tuple[Unpack[tuple[int, ...]], bool, bool]]"
 foo2(*("bar", 1, 2, 3, False, True))
 [builtins fixtures/tuple.pyi]
 
@@ -553,7 +553,7 @@ from typing import Callable, Tuple, TypeVar
 from typing_extensions import Unpack, TypeVarTuple
 
 x: Callable[[str, Unpack[Tuple[int, ...]], bool], None]
-reveal_type(x)  # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])"
+reveal_type(x)  # N: Revealed type is "def (builtins.str, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])"
 
 T = TypeVar("T")
 S = TypeVar("S")
@@ -562,7 +562,7 @@ A = Callable[[T, Unpack[Ts], S], int]
 y: A[int, str, bool]
 reveal_type(y)  # N: Revealed type is "def (builtins.int, builtins.str, builtins.bool) -> builtins.int"
 z: A[Unpack[Tuple[int, ...]]]
-reveal_type(z)  # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]) -> builtins.int"
+reveal_type(z)  # N: Revealed type is "def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]) -> builtins.int"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTuplePep646CallableInvalidSyntax]
@@ -584,7 +584,7 @@ from typing_extensions import ParamSpec
 x: Callable[[str, *Tuple[int, ...]], None]
 reveal_type(x)  # N: Revealed type is "def (builtins.str, *builtins.int)"
 y: Callable[[str, *Tuple[int, ...], bool], None]
-reveal_type(y)  # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])"
+reveal_type(y)  # N: Revealed type is "def (builtins.str, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])"
 
 P = ParamSpec("P")
 class C(Generic[P]): ...
@@ -659,7 +659,7 @@ Ts = TypeVarTuple("Ts")
 
 A = List[Tuple[T, Unpack[Ts], T]]
 x: A[int, str, str]
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, builtins.str, builtins.int]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, builtins.str, builtins.int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasBasicCallable]
@@ -700,7 +700,7 @@ Ts = TypeVarTuple("Ts")
 Start = Tuple[int, str]
 A = List[Tuple[T, Unpack[Ts], S]]
 x: A[Unpack[Start], int]
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, builtins.int]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, builtins.int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasUnpackFixedTupleTarget]
@@ -714,7 +714,7 @@ Ts = TypeVarTuple("Ts")
 Prefix = Tuple[int, int]
 A = Tuple[Unpack[Prefix], Unpack[Ts]]
 x: A[str, str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasMultipleUnpacks]
@@ -727,7 +727,7 @@ class G(Generic[Unpack[Ts]]): ...
 
 A = Tuple[Unpack[Ts], Unpack[Us]]  # E: More than one Unpack in a type is not allowed
 x: A[int, str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 B = Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one Unpack in a type is not allowed
 y: B[int, str]
@@ -748,7 +748,7 @@ class G(Generic[Unpack[Ts]]): ...
 
 A = List[Tuple[T, Unpack[Ts], T]]
 x: A
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]"
 
 B = Callable[[T, Unpack[Ts]], int]
 y: B
@@ -770,7 +770,7 @@ class G(Generic[Unpack[Ts]]): ...
 
 A = List[Tuple[T, Unpack[Ts], S]]
 x: A[int]  # E: Bad number of arguments for type alias, expected at least 2, given 1
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]"
 
 B = Callable[[T, S, Unpack[Ts]], int]
 y: B[int]  # E: Bad number of arguments for type alias, expected at least 2, given 1
@@ -789,11 +789,11 @@ Ts = TypeVarTuple("Ts")
 
 A = Tuple[Unpack[Ts], Optional[A[Unpack[Ts]]]]
 x: A[int, str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[..., None]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str, Union[..., None]]"
 
 *_, last = x
 if last is not None:
-    reveal_type(last)  # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[Tuple[builtins.int, builtins.str, Union[..., None]], None]]"
+    reveal_type(last)  # N: Revealed type is "tuple[builtins.int, builtins.str, Union[tuple[builtins.int, builtins.str, Union[..., None]], None]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasUpperBoundCheck]
@@ -823,7 +823,7 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 A = Tuple[int, Unpack[Ts], str]
 x: A[()]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasVariadicTupleArg]
@@ -836,7 +836,7 @@ A = Tuple[int, Unpack[Ts]]
 B = A[str, Unpack[Ts]]
 C = B[Unpack[Tuple[bool, ...]]]
 x: C
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.str, Unpack[builtins.tuple[builtins.bool, ...]]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str, Unpack[builtins.tuple[builtins.bool, ...]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasVariadicTupleArgGeneric]
@@ -849,7 +849,7 @@ Ts = TypeVarTuple("Ts")
 A = Tuple[int, Unpack[Ts]]
 B = A[Unpack[Tuple[T, ...]]]
 x: B[str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasVariadicTupleArgSplit]
@@ -863,10 +863,10 @@ Ts = TypeVarTuple("Ts")
 A = Tuple[T, Unpack[Ts], S, T]
 
 x: A[int, Unpack[Tuple[bool, ...]], str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.bool, ...]], builtins.str, builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.bool, ...]], builtins.str, builtins.int]"
 
 y: A[Unpack[Tuple[bool, ...]]]
-reveal_type(y)  # N: Revealed type is "Tuple[builtins.bool, Unpack[builtins.tuple[builtins.bool, ...]], builtins.bool, builtins.bool]"
+reveal_type(y)  # N: Revealed type is "tuple[builtins.bool, Unpack[builtins.tuple[builtins.bool, ...]], builtins.bool, builtins.bool]"
 [builtins fixtures/tuple.pyi]
 
 [case testBanPathologicalRecursiveTuples]
@@ -881,7 +881,7 @@ y: B
 z: C
 reveal_type(x)  # N: Revealed type is "Any"
 reveal_type(y)  # N: Revealed type is "Any"
-reveal_type(z)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
+reveal_type(z)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
 
 [builtins fixtures/tuple.pyi]
 
@@ -1009,7 +1009,7 @@ Ints = Tuple[int, int]
 c: C[Unpack[Ints]]
 reveal_type(c.prefix)  # N: Revealed type is "builtins.int"
 reveal_type(c.suffix)  # N: Revealed type is "builtins.int"
-reveal_type(c.middle)  # N: Revealed type is "Tuple[()]"
+reveal_type(c.middle)  # N: Revealed type is "tuple[()]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicUnpackItemInInstanceArguments]
@@ -1079,12 +1079,12 @@ class A(Tuple[Unpack[Ts]]):
     fn: Callable[[Unpack[Ts]], None]
 
 x: A[int]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, fallback=__main__.A[builtins.int]]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.A[builtins.int]]"
 reveal_type(x[0])  # N: Revealed type is "builtins.int"
 reveal_type(x.fn)  # N: Revealed type is "def (builtins.int)"
 
 y: A[int, str]
-reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
+reveal_type(y)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
 reveal_type(y[0])  # N: Revealed type is "builtins.int"
 reveal_type(y.fn)  # N: Revealed type is "def (builtins.int, builtins.str)"
 
@@ -1094,7 +1094,7 @@ reveal_type(z[0])  # N: Revealed type is "builtins.int"
 reveal_type(z.fn)  # N: Revealed type is "def (*builtins.int)"
 
 t: A[int, Unpack[Tuple[int, str]], str]
-reveal_type(t)  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]"
+reveal_type(t)  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str, builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]"
 reveal_type(t[0])  # N: Revealed type is "builtins.int"
 reveal_type(t.fn)  # N: Revealed type is "def (builtins.int, builtins.int, builtins.str, builtins.str)"
 [builtins fixtures/tuple.pyi]
@@ -1110,20 +1110,20 @@ class A(NamedTuple, Generic[Unpack[Ts], T]):
     val: T
 
 y: A[int, str]
-reveal_type(y)  # N: Revealed type is "Tuple[def (builtins.int), builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
+reveal_type(y)  # N: Revealed type is "tuple[def (builtins.int), builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
 reveal_type(y[0])  # N: Revealed type is "def (builtins.int)"
 reveal_type(y.fn)  # N: Revealed type is "def (builtins.int)"
 
 z: A[Unpack[Tuple[int, ...]]]
-reveal_type(z)  # N: Revealed type is "Tuple[def (*builtins.int), builtins.int, fallback=__main__.A[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]"
+reveal_type(z)  # N: Revealed type is "tuple[def (*builtins.int), builtins.int, fallback=__main__.A[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]"
 reveal_type(z.fn)  # N: Revealed type is "def (*builtins.int)"
 
 t: A[int, Unpack[Tuple[int, str]], str]
-reveal_type(t)  # N: Revealed type is "Tuple[def (builtins.int, builtins.int, builtins.str), builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]"
+reveal_type(t)  # N: Revealed type is "tuple[def (builtins.int, builtins.int, builtins.str), builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]"
 
 def test(x: int, y: str) -> None: ...
 nt = A(fn=test, val=42)
-reveal_type(nt)  # N: Revealed type is "Tuple[def (builtins.int, builtins.str), builtins.int, fallback=__main__.A[builtins.int, builtins.str, builtins.int]]"
+reveal_type(nt)  # N: Revealed type is "tuple[def (builtins.int, builtins.str), builtins.int, fallback=__main__.A[builtins.int, builtins.str, builtins.int]]"
 
 def bad() -> int: ...
 nt2 = A(fn=bad, val=42)  # E: Argument "fn" to "A" has incompatible type "Callable[[], int]"; expected "Callable[[], None]"
@@ -1200,9 +1200,9 @@ Alias = Tuple[int, Unpack[Ts], str]
 
 A = Union[int, str]
 x: List[Alias[int, Unpack[A], str]]  # E: "Union[int, str]" cannot be unpacked (must be tuple or TypeVarTuple)
-reveal_type(x)  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str, builtins.str]]"
+reveal_type(x)  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str, builtins.str]]"
 y: List[Alias[int, Unpack[Undefined], str]]  # E: Name "Undefined" is not defined
-reveal_type(y)  # N: Revealed type is "builtins.list[Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str]]"
+reveal_type(y)  # N: Revealed type is "builtins.list[tuple[builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasForwardRefToFixedUnpack]
@@ -1215,7 +1215,7 @@ Ts = TypeVarTuple("Ts")
 Alias = Tuple[T, Unpack[Ts], S]
 x: Alias[int, Unpack[Other]]
 Other = Tuple[int, str]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicAliasForwardRefToVariadicUnpack]
@@ -1228,7 +1228,7 @@ Ts = TypeVarTuple("Ts")
 Alias = Tuple[T, Unpack[Ts], S]
 x: Alias[int, Unpack[Other]]
 Other = Tuple[int, ...]
-reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]"
+reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicInstanceStrictPrefixSuffixCheck]
@@ -1271,7 +1271,7 @@ class A(Tuple[Unpack[TP]]): ...
 
 def test(d: A[int, str]) -> None:
     if isinstance(d, A):
-        reveal_type(d)  # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
+        reveal_type(d)  # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]"
     else:
         reveal_type(d)  # E: Statement is unreachable
 
@@ -1315,7 +1315,7 @@ f2(t1)
 f2(t2)
 f2(t3)
 f2(t4)
-f2(t5)  # E: Argument 1 to "f2" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]]]"
+f2(t5)  # E: Argument 1 to "f2" has incompatible type "tuple[int, ...]"; expected "tuple[float, Unpack[tuple[float, ...]]]"
 
 f2(tl)
 f2(tr)
@@ -1324,16 +1324,16 @@ f3(t1)
 f3(t2)
 f3(t3)
 f3(t4)
-f3(t5)  # E: Argument 1 to "f3" has incompatible type "Tuple[int, ...]"; expected "Tuple[Unpack[Tuple[float, ...]], float]"
+f3(t5)  # E: Argument 1 to "f3" has incompatible type "tuple[int, ...]"; expected "tuple[Unpack[tuple[float, ...]], float]"
 
 f3(tl)
 f3(tr)
 
 f4(t1)
-f4(t2)  # E: Argument 1 to "f4" has incompatible type "Tuple[int, Unpack[Tuple[int, ...]]]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]"
-f4(t3)  # E: Argument 1 to "f4" has incompatible type "Tuple[Unpack[Tuple[int, ...]], int]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]"
+f4(t2)  # E: Argument 1 to "f4" has incompatible type "tuple[int, Unpack[tuple[int, ...]]]"; expected "tuple[float, Unpack[tuple[float, ...]], float]"
+f4(t3)  # E: Argument 1 to "f4" has incompatible type "tuple[Unpack[tuple[int, ...]], int]"; expected "tuple[float, Unpack[tuple[float, ...]], float]"
 f4(t4)
-f4(t5)  # E: Argument 1 to "f4" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]"
+f4(t5)  # E: Argument 1 to "f4" has incompatible type "tuple[int, ...]"; expected "tuple[float, Unpack[tuple[float, ...]], float]"
 
 f4(tl)
 f4(tr)
@@ -1350,7 +1350,7 @@ T = TypeVar("T")
 def f(x: Tuple[int, Unpack[Tuple[T, ...]]]) -> T: ...
 
 vt0: Tuple[int, ...]
-f(vt0)  # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]"
+f(vt0)  # E: Argument 1 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]]]"
 
 vt1: Tuple[Unpack[Tuple[int, ...]], int]
 reveal_type(f(vt1))  # N: Revealed type is "builtins.int"
@@ -1358,12 +1358,12 @@ reveal_type(f(vt1))  # N: Revealed type is "builtins.int"
 S = TypeVar("S")
 Ts = TypeVarTuple("Ts")
 def g(x: Tuple[T, Unpack[Ts], S]) -> Tuple[T, Unpack[Ts], S]: ...
-g(vt0)  # E: Argument 1 to "g" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]], int]"
+g(vt0)  # E: Argument 1 to "g" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]], int]"
 
 U = TypeVar("U")
 def h(x: List[Tuple[T, S, U]]) -> Tuple[T, S, U]: ...
 vt2: Tuple[Unpack[Tuple[int, ...]], int]
-vt2 = h(reveal_type([]))  # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, builtins.int]]"
+vt2 = h(reveal_type([]))  # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicSelfTypeErasure]
@@ -1395,7 +1395,7 @@ fii(C())  # E: Argument 1 to "fii" has incompatible type "C"; expected "B[int, i
 fii(D())  # E: Argument 1 to "fii" has incompatible type "D"; expected "B[int, int]"
 fis(C())
 fis(D())  # E: Argument 1 to "fis" has incompatible type "D"; expected "B[int, str]"
-fiv(C())  # E: Argument 1 to "fiv" has incompatible type "C"; expected "B[Unpack[Tuple[int, ...]]]"
+fiv(C())  # E: Argument 1 to "fiv" has incompatible type "C"; expected "B[Unpack[tuple[int, ...]]]"
 fiv(D())
 [builtins fixtures/tuple.pyi]
 
@@ -1417,14 +1417,14 @@ civ: C[Unpack[Tuple[int, ...]]]
 
 fii(cii)
 fii(cis)  # E: Argument 1 to "fii" has incompatible type "C[int, str]"; expected "B[int, int]"
-fii(civ)  # E: Argument 1 to "fii" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int]"
+fii(civ)  # E: Argument 1 to "fii" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, int]"
 
 fis(cii)  # E: Argument 1 to "fis" has incompatible type "C[int, int]"; expected "B[int, str]"
 fis(cis)
-fis(civ)  # E: Argument 1 to "fis" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, str]"
+fis(civ)  # E: Argument 1 to "fis" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, str]"
 
 fiv(cii)
-fiv(cis)  # E: Argument 1 to "fiv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]"
+fiv(cis)  # E: Argument 1 to "fiv" has incompatible type "C[int, str]"; expected "B[Unpack[tuple[int, ...]]]"
 fiv(civ)
 [builtins fixtures/tuple.pyi]
 
@@ -1447,10 +1447,10 @@ civ: C[Unpack[Tuple[int, ...]]]
 
 ff(cii)
 ff(cis)  # E: Argument 1 to "ff" has incompatible type "C[int, str]"; expected "B[int, int, int]"
-ff(civ)  # E: Argument 1 to "ff" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int, int]"
+ff(civ)  # E: Argument 1 to "ff" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, int, int]"
 
 fv(cii)
-fv(cis)  # E: Argument 1 to "fv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]"
+fv(cis)  # E: Argument 1 to "fv" has incompatible type "C[int, str]"; expected "B[Unpack[tuple[int, ...]]]"
 fv(civ)
 [builtins fixtures/tuple.pyi]
 
@@ -1486,17 +1486,17 @@ class C3(B[int, Unpack[Ts], T]): ...
 class C4(B[Unpack[Tuple[T, ...]]]): ...
 
 c1: C1
-reveal_type(c1.meth())  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(c1.meth())  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 c2f: C2[int, str]
 c2v: C2[Unpack[Tuple[int, ...]]]
-reveal_type(c2f.meth())  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(c2f.meth())  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 reveal_type(c2v.meth())  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
 
 c3f: C3[int, str]
 c3v: C3[Unpack[Tuple[int, ...]]]
-reveal_type(c3f.meth())  # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]"
-reveal_type(c3v.meth())  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]"
+reveal_type(c3f.meth())  # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]"
+reveal_type(c3v.meth())  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]"
 
 c4: C4[int]
 reveal_type(c4.meth())  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
@@ -1649,9 +1649,9 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 def foo(arg: Tuple[int, Unpack[Ts], str]) -> None:
     x = *arg,
-    reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
+    reveal_type(x)  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]"
     y = 1, *arg, 2
-    reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[Ts`-1], builtins.str, builtins.int]"
+    reveal_type(y)  # N: Revealed type is "tuple[builtins.int, builtins.int, Unpack[Ts`-1], builtins.str, builtins.int]"
     z = (*arg, *arg)
     reveal_type(z)  # N: Revealed type is "builtins.tuple[builtins.object, ...]"
 [builtins fixtures/tuple.pyi]
@@ -1667,14 +1667,14 @@ b: Tuple[int, Unpack[Tuple[float, ...]], str]
 x = *a,
 reveal_type(x)  # N: Revealed type is "builtins.tuple[builtins.float, ...]"
 y = 1, *a, 2
-reveal_type(y)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]"
+reveal_type(y)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]"
 z = (*a, *a)
 reveal_type(z)  # N: Revealed type is "builtins.tuple[builtins.float, ...]"
 
 x2 = *b,
-reveal_type(x2)  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
+reveal_type(x2)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]"
 y2 = 1, *b, 2
-reveal_type(y2)  # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str, builtins.int]"
+reveal_type(y2)  # N: Revealed type is "tuple[builtins.int, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str, builtins.int]"
 z2 = (*b, *b)
 reveal_type(z2)  # N: Revealed type is "builtins.tuple[builtins.object, ...]"
 [builtins fixtures/tuple.pyi]
@@ -1714,16 +1714,16 @@ from typing_extensions import TypeVarTuple, Unpack
 vtf: Tuple[float, ...]
 vt: Tuple[int, Unpack[Tuple[float, ...]], int]
 
-reveal_type(vt + (1, 2))  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]"
-reveal_type((1, 2) + vt)  # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]"
+reveal_type(vt + (1, 2))  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]"
+reveal_type((1, 2) + vt)  # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]"
 reveal_type(vt + vt)  # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.float], ...]"
-reveal_type(vtf + (1, 2))  # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]"
-reveal_type((1, 2) + vtf)  # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]"
+reveal_type(vtf + (1, 2))  # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]"
+reveal_type((1, 2) + vtf)  # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]"
 
 Ts = TypeVarTuple("Ts")
 def foo(arg: Tuple[int, Unpack[Ts], str]) -> None:
-    reveal_type(arg + (1, 2))  # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str, Literal[1]?, Literal[2]?]"
-    reveal_type((1, 2) + arg)  # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[Ts`-1], builtins.str]"
+    reveal_type(arg + (1, 2))  # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str, Literal[1]?, Literal[2]?]"
+    reveal_type((1, 2) + arg)  # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[Ts`-1], builtins.str]"
     reveal_type(arg + arg)  # N: Revealed type is "builtins.tuple[builtins.object, ...]"
 [builtins fixtures/tuple.pyi]
 
@@ -1807,7 +1807,7 @@ def add(self: Tuple[T, ...], other: Tuple[T, ...]) -> Tuple[T, ...]:
 def add(self: Any, other: Any) -> Any:
     ...
 def test(a: Tuple[int, str], b: Tuple[bool], c: Tuple[bool, ...]):
-    reveal_type(add(a, b))  # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]"
+    reveal_type(add(a, b))  # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]"
     reveal_type(add(b, c))  # N: Revealed type is "builtins.tuple[builtins.bool, ...]"
 [builtins fixtures/tuple.pyi]
 
@@ -1923,7 +1923,7 @@ def foo(func: Callable[[Unpack[Args]], T], *args: Unpack[Args]) -> T:
    return submit(func, *args)
 
 def foo2(func: Callable[[Unpack[Args]], T], *args: Unpack[Args2]) -> T:
-   return submit(func, *args)  # E: Argument 2 to "submit" has incompatible type "*Tuple[Unpack[Args2]]"; expected "Unpack[Args]"
+   return submit(func, *args)  # E: Argument 2 to "submit" has incompatible type "*tuple[Unpack[Args2]]"; expected "Unpack[Args]"
 
 def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T:
    return submit(func, 1, *args)
@@ -2015,12 +2015,12 @@ from typing_extensions import TypeVarTuple, Unpack
 Ts = TypeVarTuple("Ts")
 class B(Generic[Unpack[Ts]]):
     def __init__(self, x: Tuple[Unpack[Ts]], *args: Unpack[Ts]) -> None: ...
-reveal_type(B)  # N: Revealed type is "def [Ts] (x: Tuple[Unpack[Ts`1]], *args: Unpack[Ts`1]) -> __main__.B[Unpack[Ts`1]]"
+reveal_type(B)  # N: Revealed type is "def [Ts] (x: tuple[Unpack[Ts`1]], *args: Unpack[Ts`1]) -> __main__.B[Unpack[Ts`1]]"
 
 T = TypeVar("T")
 S = TypeVar("S")
 class C(B[T, S]): ...
-reveal_type(C)  # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2], T`1, S`2) -> __main__.C[T`1, S`2]"
+reveal_type(C)  # N: Revealed type is "def [T, S] (x: tuple[T`1, S`2], T`1, S`2) -> __main__.C[T`1, S`2]"
 [builtins fixtures/tuple.pyi]
 
 [case testVariadicClassGenericSelf]
@@ -2035,13 +2035,13 @@ class B(Generic[Unpack[Ts]]):
     def on_pair(self: B[T, S]) -> Tuple[T, S]: ...
 
 b1: B[int]
-reveal_type(b1.on_pair())  # E: Invalid self argument "B[int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \
-                           # N: Revealed type is "Tuple[Never, Never]"
+reveal_type(b1.on_pair())  # E: Invalid self argument "B[int]" to attribute function "on_pair" with type "Callable[[B[T, S]], tuple[T, S]]" \
+                           # N: Revealed type is "tuple[Never, Never]"
 b2: B[int, str]
-reveal_type(b2.on_pair())  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(b2.on_pair())  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 b3: B[int, str, int]
-reveal_type(b3.on_pair())  # E: Invalid self argument "B[int, str, int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \
-                           # N: Revealed type is "Tuple[Never, Never]"
+reveal_type(b3.on_pair())  # E: Invalid self argument "B[int, str, int]" to attribute function "on_pair" with type "Callable[[B[T, S]], tuple[T, S]]" \
+                           # N: Revealed type is "tuple[Never, Never]"
 
 class C(B[T, S]): ...
 c: C[int, str]
@@ -2084,9 +2084,9 @@ Ts = TypeVarTuple("Ts")
 class B(Generic[Unpack[Ts]]):
     items: Tuple[Unpack[Ts]]
 
-reveal_type(B)  # N: Revealed type is "def [Ts] (items: Tuple[Unpack[Ts`1]]) -> __main__.B[Unpack[Ts`1]]"
+reveal_type(B)  # N: Revealed type is "def [Ts] (items: tuple[Unpack[Ts`1]]) -> __main__.B[Unpack[Ts`1]]"
 b = B((1, "yes"))
-reveal_type(b.items)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(b.items)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 T = TypeVar("T")
 S = TypeVar("S")
@@ -2096,9 +2096,9 @@ class C(B[T, S]):
     first: T
     second: S
 
-reveal_type(C)  # N: Revealed type is "def [T, S] (items: Tuple[T`1, S`2], first: T`1, second: S`2) -> __main__.C[T`1, S`2]"
+reveal_type(C)  # N: Revealed type is "def [T, S] (items: tuple[T`1, S`2], first: T`1, second: S`2) -> __main__.C[T`1, S`2]"
 c = C((1, "yes"), 2, "no")
-reveal_type(c.items)  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(c.items)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 reveal_type(c.first)  # N: Revealed type is "builtins.int"
 reveal_type(c.second)  # N: Revealed type is "builtins.str"
 [builtins fixtures/dataclasses.pyi]
@@ -2127,17 +2127,17 @@ class Good:
     def meth(self, __x: int, y: str) -> None: ...
 
 g: Good
-reveal_type(get_items(g))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
-reveal_type(match(g))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+reveal_type(get_items(g))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
+reveal_type(match(g))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
 b: Bad
-get_items(b)  # E: Argument 1 to "get_items" has incompatible type "Bad"; expected "P[Unpack[Tuple[Never, ...]]]" \
+get_items(b)  # E: Argument 1 to "get_items" has incompatible type "Bad"; expected "P[Unpack[tuple[Never, ...]]]" \
               # N: Following member(s) of "Bad" have conflicts: \
               # N:     Expected: \
-              # N:         def items(self) -> Tuple[Never, ...] \
+              # N:         def items(self) -> tuple[Never, ...] \
               # N:     Got: \
-              # N:         def items(self) -> List[int]
-match(b)  # E: Argument 1 to "match" has incompatible type "Bad"; expected "PC[Unpack[Tuple[Never, ...]]]" \
+              # N:         def items(self) -> list[int]
+match(b)  # E: Argument 1 to "match" has incompatible type "Bad"; expected "PC[Unpack[tuple[Never, ...]]]" \
           # N: Following member(s) of "Bad" have conflicts: \
           # N:     Expected: \
           # N:         def meth(self, *args: Never) -> None \
@@ -2161,10 +2161,10 @@ from typing import Callable, Tuple
 
 f: Callable[[int, *Tuple[str, ...], int], None]
 g: Callable[[int, *Tuple[str, ...], int], None]
-reveal_type([f, g])  # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.int]])]"
+reveal_type([f, g])  # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.int]])]"
 
 h: Callable[[int, *Tuple[str, ...], str], None]
-reveal_type([f, h])  # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], Never]])]"
+reveal_type([f, h])  # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.str, ...]], Never]])]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleBothUnpacksSimple]
@@ -2219,7 +2219,7 @@ cb: Callable[[Unpack[Ints], Unpack[Keywords]], None]
 reveal_type(cb)  # N: Revealed type is "def (*builtins.int, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])"
 
 cb2: Callable[[int, Unpack[Ints], int, Unpack[Keywords]], None]
-reveal_type(cb2)  # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]], **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])"
+reveal_type(cb2)  # N: Revealed type is "def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]], **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])"
 cb2(1, 2, 3, a="a", b="b")
 cb2(1, a="a", b="b")  # E: Too few arguments
 cb2(1, 2, 3, a="a")  # E: Missing named argument "b"
@@ -2283,7 +2283,7 @@ keys: Tuple[Unpack[Tuple[int, ...]]]
 foo(keys, 1)
 foo(*keys, 1)
 
-bar(keys, 1)  # E: Argument 1 to "bar" has incompatible type "Tuple[Unpack[Tuple[int, ...]]]"; expected "int"
+bar(keys, 1)  # E: Argument 1 to "bar" has incompatible type "tuple[Unpack[tuple[int, ...]]]"; expected "int"
 bar(*keys, 1)  # OK
 
 reveal_type(baz(keys, 1))  # N: Revealed type is "builtins.object"
@@ -2293,7 +2293,7 @@ reveal_type(baz(*keys, 1))  # N: Revealed type is "builtins.int"
 [case testVariadicTupleContextNoCrash]
 from typing import Tuple, Unpack
 
-x: Tuple[int, Unpack[Tuple[int, ...]]] = ()  # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[int, Unpack[Tuple[int, ...]]]")
+x: Tuple[int, Unpack[Tuple[int, ...]]] = ()  # E: Incompatible types in assignment (expression has type "tuple[()]", variable has type "tuple[int, Unpack[tuple[int, ...]]]")
 y: Tuple[int, Unpack[Tuple[int, ...]]] = (1, 2)
 z: Tuple[int, Unpack[Tuple[int, ...]]] = (1,)
 w: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *[2, 3, 4])
@@ -2339,10 +2339,10 @@ def bad3(*, d: str) -> int: ...
 def bad4(**kwargs: None) -> None: ...
 
 higher_order(good)
-higher_order(bad1)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
-higher_order(bad2)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
-higher_order(bad3)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
-higher_order(bad4)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
+higher_order(bad1)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[int, str, VarArg(Unpack[tuple[Unpack[tuple[Any, ...]], int]])], Any]"
+higher_order(bad2)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[int, str, VarArg(Unpack[tuple[Unpack[tuple[Any, ...]], int]])], Any]"
+higher_order(bad3)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[tuple[Unpack[tuple[Any, ...]], int]])], Any]"
+higher_order(bad4)  # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[tuple[Unpack[tuple[Any, ...]], int]])], Any]"
 [builtins fixtures/tuple.pyi]
 
 [case testAliasToCallableWithUnpackInvalid]
@@ -2381,7 +2381,7 @@ def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
     ...
 
 def a2(x: Array[int, str]) -> None:
-    reveal_type(func(x, 2, "Hello"))  # N: Revealed type is "Tuple[builtins.int, builtins.str]"
+    reveal_type(func(x, 2, "Hello"))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
     reveal_type(func(x, 2))           # E: Cannot infer type argument 1 of "func" \
                                       # N: Revealed type is "builtins.tuple[Any, ...]"
     reveal_type(func(x, 2, "Hello", True))   # E: Cannot infer type argument 1 of "func" \
@@ -2429,8 +2429,8 @@ Ts = TypeVarTuple("Ts")
 @cm
 def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ...
 
-reveal_type(test)  # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __main__.CM[Tuple[Unpack[Ts`-1]]]"
-reveal_type(test(1, 2, 3))  # N: Revealed type is "__main__.CM[Tuple[Literal[1]?, Literal[2]?, Literal[3]?]]"
+reveal_type(test)  # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __main__.CM[tuple[Unpack[Ts`-1]]]"
+reveal_type(test(1, 2, 3))  # N: Revealed type is "__main__.CM[tuple[Literal[1]?, Literal[2]?, Literal[3]?]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleAgainstParamSpecActualFailedNoCrash]
@@ -2444,7 +2444,7 @@ class CM(Generic[R]): ...
 def cm(fn: Callable[P, List[R]]) -> Callable[P, CM[R]]: ...
 
 Ts = TypeVarTuple("Ts")
-@cm  # E: Argument 1 to "cm" has incompatible type "Callable[[VarArg(Unpack[Ts])], Tuple[Unpack[Ts]]]"; expected "Callable[[VarArg(Never)], List[Never]]"
+@cm  # E: Argument 1 to "cm" has incompatible type "Callable[[VarArg(Unpack[Ts])], tuple[Unpack[Ts]]]"; expected "Callable[[VarArg(Never)], list[Never]]"
 def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ...
 
 reveal_type(test)  # N: Revealed type is "def (*args: Never) -> __main__.CM[Never]"
@@ -2465,7 +2465,7 @@ Ts = TypeVarTuple("Ts")
 @cm
 def test(x: T, *args: Unpack[Ts]) -> Tuple[T, Unpack[Ts]]: ...
 
-reveal_type(test)  # N: Revealed type is "def [T, Ts] (builtins.list[T`2], *args: Unpack[Ts`-2]) -> __main__.CM[Tuple[T`2, Unpack[Ts`-2]]]"
+reveal_type(test)  # N: Revealed type is "def [T, Ts] (builtins.list[T`2], *args: Unpack[Ts`-2]) -> __main__.CM[tuple[T`2, Unpack[Ts`-2]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testMixingTypeVarTupleAndParamSpec]
@@ -2506,7 +2506,7 @@ class Foo(Generic[Unpack[Ts]]):
 
 x1: Foo[Unpack[tuple[int, ...]]]
 y1: Foo[Unpack[tuple[str, ...]]]
-x1 is y1  # E: Non-overlapping identity check (left operand type: "Foo[Unpack[Tuple[int, ...]]]", right operand type: "Foo[Unpack[Tuple[str, ...]]]")
+x1 is y1  # E: Non-overlapping identity check (left operand type: "Foo[Unpack[tuple[int, ...]]]", right operand type: "Foo[Unpack[tuple[str, ...]]]")
 
 x2: Foo[Unpack[tuple[int, ...]]]
 y2: Foo[Unpack[tuple[int, ...]]]
@@ -2518,7 +2518,7 @@ x3 is y3
 
 x4: Foo[Unpack[tuple[str, ...]]]
 y4: Foo[Unpack[tuple[int, int]]]
-x4 is y4  # E: Non-overlapping identity check (left operand type: "Foo[Unpack[Tuple[str, ...]]]", right operand type: "Foo[int, int]")
+x4 is y4  # E: Non-overlapping identity check (left operand type: "Foo[Unpack[tuple[str, ...]]]", right operand type: "Foo[int, int]")
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleErasureNormalized]
@@ -2557,9 +2557,9 @@ class Base(Generic[Unpack[Ts]]):
 Ss = TypeVarTuple("Ss")
 class Derived(Base[str, Unpack[Ss]]):
     def test(self) -> None:
-        reveal_type(self.attr)  # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]"
-        reveal_type(self.prop)  # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]"
-        reveal_type(self.meth())  # N: Revealed type is "Tuple[builtins.str, Unpack[Ss`1]]"
+        reveal_type(self.attr)  # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]"
+        reveal_type(self.prop)  # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]"
+        reveal_type(self.meth())  # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]"
 [builtins fixtures/property.pyi]
 
 [case testTypeVarTupleProtocolPrefix]
@@ -2574,7 +2574,7 @@ class C:
 
 def f(x: A[Unpack[Ts]]) -> tuple[Unpack[Ts]]: ...
 
-reveal_type(f(C()))  # N: Revealed type is "Tuple[builtins.int]"
+reveal_type(f(C()))  # N: Revealed type is "tuple[builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleHomogeneousCallableNormalized]
@@ -2603,8 +2603,8 @@ def test(xs: tuple[Unpack[Ts]], xsi: tuple[int, Unpack[Ts]]) -> None:
     reveal_type(join(xs, aa))  # N: Revealed type is "builtins.tuple[Any, ...]"
     reveal_type(join(aa, xs))  # N: Revealed type is "builtins.tuple[Any, ...]"
     ai: tuple[int, Unpack[tuple[Any, ...]]]
-    reveal_type(join(xsi, ai))  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
-    reveal_type(join(ai, xsi))  # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
+    reveal_type(join(xsi, ai))  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
+    reveal_type(join(ai, xsi))  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeVarTupleInferAgainstAnyCallableSuffix]
diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test
index 36ab3af6d3e9..ab2956374c12 100644
--- a/test-data/unit/check-typevar-values.test
+++ b/test-data/unit/check-typevar-values.test
@@ -20,7 +20,7 @@ if int():
     i = f(1)
     s = f('')
     o = f(1) \
-      # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") \
+      # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[object]") \
       # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
       # N: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test
index 6250374ccbea..924c12658851 100644
--- a/test-data/unit/check-union-or-syntax.test
+++ b/test-data/unit/check-union-or-syntax.test
@@ -109,7 +109,7 @@ b: X  # E: Variable "__main__.X" is not valid as a type \
 from __future__ import annotations
 from typing import List
 T = int | str  # E: Invalid type alias: expression is not a valid type \
-               # E: Unsupported left operand type for | ("Type[int]")
+               # E: Unsupported left operand type for | ("type[int]")
 class C(List[int | str]):  # E: Type expected within [...] \
                            # E: Invalid base class "List"
     pass
@@ -181,7 +181,7 @@ def f(x: int | str | C) -> None:
 
 def g(x: int | str | tuple[int, str] | C) -> None:
     if isinstance(x, int | str | tuple):
-        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, Tuple[builtins.int, builtins.str]]"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, tuple[builtins.int, builtins.str]]"
     else:
         reveal_type(x)  # N: Revealed type is "__main__.C"
 [builtins fixtures/isinstance_python3_10.pyi]
diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test
index 8e92b6a91e8a..f8c894a7957b 100644
--- a/test-data/unit/check-unions.test
+++ b/test-data/unit/check-unions.test
@@ -347,7 +347,7 @@ C = NamedTuple('C', [('x', int)])
 
 def foo(a: Union[A, B, C]):
     if isinstance(a, (B, C)):
-        reveal_type(a) # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.B], Tuple[builtins.int, fallback=__main__.C]]"
+        reveal_type(a) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.B], tuple[builtins.int, fallback=__main__.C]]"
         a.x
         a.y # E: Item "B" of "Union[B, C]" has no attribute "y" \
             # E: Item "C" of "Union[B, C]" has no attribute "y"
@@ -378,20 +378,20 @@ t_s: Type[str]
 t_a: Type[Any]
 
 # Two identical items
-reveal_type(u(t_o, t_o)) # N: Revealed type is "Type[builtins.object]"
-reveal_type(u(t_s, t_s)) # N: Revealed type is "Type[builtins.str]"
-reveal_type(u(t_a, t_a)) # N: Revealed type is "Type[Any]"
+reveal_type(u(t_o, t_o)) # N: Revealed type is "type[builtins.object]"
+reveal_type(u(t_s, t_s)) # N: Revealed type is "type[builtins.str]"
+reveal_type(u(t_a, t_a)) # N: Revealed type is "type[Any]"
 reveal_type(u(type, type)) # N: Revealed type is "def (x: builtins.object) -> builtins.type"
 
 # One type, other non-type
-reveal_type(u(t_s, 1)) # N: Revealed type is "Union[builtins.int, Type[builtins.str]]"
-reveal_type(u(1, t_s)) # N: Revealed type is "Union[Type[builtins.str], builtins.int]"
+reveal_type(u(t_s, 1)) # N: Revealed type is "Union[builtins.int, type[builtins.str]]"
+reveal_type(u(1, t_s)) # N: Revealed type is "Union[type[builtins.str], builtins.int]"
 reveal_type(u(type, 1)) # N: Revealed type is "Union[builtins.int, def (x: builtins.object) -> builtins.type]"
 reveal_type(u(1, type)) # N: Revealed type is "Union[def (x: builtins.object) -> builtins.type, builtins.int]"
-reveal_type(u(t_a, 1)) # N: Revealed type is "Union[builtins.int, Type[Any]]"
-reveal_type(u(1, t_a)) # N: Revealed type is "Union[Type[Any], builtins.int]"
-reveal_type(u(t_o, 1)) # N: Revealed type is "Union[builtins.int, Type[builtins.object]]"
-reveal_type(u(1, t_o)) # N: Revealed type is "Union[Type[builtins.object], builtins.int]"
+reveal_type(u(t_a, 1)) # N: Revealed type is "Union[builtins.int, type[Any]]"
+reveal_type(u(1, t_a)) # N: Revealed type is "Union[type[Any], builtins.int]"
+reveal_type(u(t_o, 1)) # N: Revealed type is "Union[builtins.int, type[builtins.object]]"
+reveal_type(u(1, t_o)) # N: Revealed type is "Union[type[builtins.object], builtins.int]"
 
 [case testSimplifyingUnionWithTypeTypes2]
 from typing import TypeVar, Union, Type, Any
@@ -414,12 +414,12 @@ reveal_type(u(t_a, object())) # N: Revealed type is "builtins.object"
 reveal_type(u(object(), t_a)) # N: Revealed type is "builtins.object"
 
 # Union between type objects
-reveal_type(u(t_o, t_a)) # N: Revealed type is "Union[Type[Any], Type[builtins.object]]"
-reveal_type(u(t_a, t_o)) # N: Revealed type is "Union[Type[builtins.object], Type[Any]]"
-reveal_type(u(t_s, t_o)) # N: Revealed type is "Type[builtins.object]"
-reveal_type(u(t_o, t_s)) # N: Revealed type is "Type[builtins.object]"
-reveal_type(u(t_o, type)) # N: Revealed type is "Type[builtins.object]"
-reveal_type(u(type, t_o)) # N: Revealed type is "Type[builtins.object]"
+reveal_type(u(t_o, t_a)) # N: Revealed type is "Union[type[Any], type[builtins.object]]"
+reveal_type(u(t_a, t_o)) # N: Revealed type is "Union[type[builtins.object], type[Any]]"
+reveal_type(u(t_s, t_o)) # N: Revealed type is "type[builtins.object]"
+reveal_type(u(t_o, t_s)) # N: Revealed type is "type[builtins.object]"
+reveal_type(u(t_o, type)) # N: Revealed type is "type[builtins.object]"
+reveal_type(u(type, t_o)) # N: Revealed type is "type[builtins.object]"
 reveal_type(u(t_a, t)) # N: Revealed type is "builtins.type"
 reveal_type(u(t, t_a)) # N: Revealed type is "builtins.type"
 # The following should arguably not be simplified, but it's unclear how to fix then
@@ -444,8 +444,8 @@ t_a: Type[A]
 reveal_type(u(M(*a), t_a)) # N: Revealed type is "__main__.M"
 reveal_type(u(t_a, M(*a))) # N: Revealed type is "__main__.M"
 
-reveal_type(u(M2(*a), t_a)) # N: Revealed type is "Union[Type[__main__.A], __main__.M2]"
-reveal_type(u(t_a, M2(*a))) # N: Revealed type is "Union[__main__.M2, Type[__main__.A]]"
+reveal_type(u(M2(*a), t_a)) # N: Revealed type is "Union[type[__main__.A], __main__.M2]"
+reveal_type(u(t_a, M2(*a))) # N: Revealed type is "Union[__main__.M2, type[__main__.A]]"
 
 [case testSimplifyUnionWithCallable]
 from typing import TypeVar, Union, Any, Callable
@@ -772,7 +772,7 @@ good: Union[Tuple[int, int], Tuple[str, str]]
 x, y = t = good
 reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
 reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]"
-reveal_type(t) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]"
+reveal_type(t) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -783,7 +783,7 @@ good: Union[Tuple[int, int], Tuple[str, str]]
 t = x, y = good
 reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
 reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]"
-reveal_type(t) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]"
+reveal_type(t) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]"
 [builtins fixtures/tuple.pyi]
 [out]
 
@@ -934,7 +934,7 @@ a: Any
 d: Dict[str, Tuple[List[Tuple[str, str]], str]]
 x, _ = d.get(a, (None, None))
 
-for y in x: pass # E: Item "None" of "Optional[List[Tuple[str, str]]]" has no attribute "__iter__" (not iterable)
+for y in x: pass # E: Item "None" of "Optional[list[tuple[str, str]]]" has no attribute "__iter__" (not iterable)
 if x:
     for s, t in x:
         reveal_type(s) # N: Revealed type is "builtins.str"
@@ -949,7 +949,7 @@ x = None
 d: Dict[str, Tuple[List[Tuple[str, str]], str]]
 x, _ = d.get(a, (None, None))
 
-for y in x: pass # E: Item "None" of "Optional[List[Tuple[str, str]]]" has no attribute "__iter__" (not iterable)
+for y in x: pass # E: Item "None" of "Optional[list[tuple[str, str]]]" has no attribute "__iter__" (not iterable)
 if x:
     for s, t in x:
         reveal_type(s) # N: Revealed type is "builtins.str"
@@ -963,7 +963,7 @@ x: object
 a: Any
 d: Dict[str, Tuple[List[Tuple[str, str]], str]]
 x, _ = d.get(a, (None, None))
-reveal_type(x) # N: Revealed type is "Union[builtins.list[Tuple[builtins.str, builtins.str]], None]"
+reveal_type(x) # N: Revealed type is "Union[builtins.list[tuple[builtins.str, builtins.str]], None]"
 
 if x:
     for y in x: pass
@@ -976,7 +976,7 @@ from typing import Dict, Tuple, List, Any
 a: Any
 d: Dict[str, Tuple[List[Tuple[str, str]], str]]
 x, _ = d.get(a, ([], ""))
-reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.str, builtins.str]]"
+reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.str, builtins.str]]"
 
 for y in x: pass
 [builtins fixtures/dict.pyi]
@@ -1048,7 +1048,7 @@ class Boop(Enum):
 def do_thing_with_enums(enums: Union[List[Enum], Enum]) -> None: ...
 
 boop: List[Boop] = []
-do_thing_with_enums(boop)  # E: Argument 1 to "do_thing_with_enums" has incompatible type "List[Boop]"; expected "Union[List[Enum], Enum]" \
+do_thing_with_enums(boop)  # E: Argument 1 to "do_thing_with_enums" has incompatible type "list[Boop]"; expected "Union[list[Enum], Enum]" \
                            # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
                            # N: Consider using "Sequence" instead, which is covariant
 [builtins fixtures/isinstancelist.pyi]
@@ -1253,7 +1253,7 @@ class B:
     field_2: Mapped[str] = Mapped('2')
 
 mix: Union[Type[A], Type[B]] = A
-reveal_type(mix)  # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]"
+reveal_type(mix)  # N: Revealed type is "Union[type[__main__.A], type[__main__.B]]"
 reveal_type(mix.field_1)  # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(mix().field_1)  # N: Revealed type is "builtins.int"
 [builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test
index 2e93c761b0be..680021a166f2 100644
--- a/test-data/unit/check-varargs.test
+++ b/test-data/unit/check-varargs.test
@@ -11,8 +11,8 @@ def f( *b: 'B') -> None:
     ab: Tuple[B, ...]
     ac: Tuple[C, ...]
     if int():
-        b = ac # E: Incompatible types in assignment (expression has type "Tuple[C, ...]", variable has type "Tuple[B, ...]")
-        ac = b # E: Incompatible types in assignment (expression has type "Tuple[B, ...]", variable has type "Tuple[C, ...]")
+        b = ac # E: Incompatible types in assignment (expression has type "tuple[C, ...]", variable has type "tuple[B, ...]")
+        ac = b # E: Incompatible types in assignment (expression has type "tuple[B, ...]", variable has type "tuple[C, ...]")
         b = ab
         ab = b
 
@@ -121,7 +121,7 @@ T4 = TypeVar('T4')
 def f(a: T1, b: T2, c: T3, d: T4) -> Tuple[T1, T2, T3, T4]: ...
 x: Tuple[int, str]
 y: Tuple[float, bool]
-reveal_type(f(*x, *y)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.float, builtins.bool]"
+reveal_type(f(*x, *y)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.float, builtins.bool]"
 [builtins fixtures/list.pyi]
 
 [case testCallVarargsFunctionWithIterableAndPositional]
@@ -141,7 +141,7 @@ it1 = (1, 2)
 it2 = ('',)
 f(*it1, 1, 2)
 f(*it1, 1, *it1, 2)
-f(*it1, 1, *it2, 2)  # E: Argument 3 to "f" has incompatible type "*Tuple[str]"; expected "int"
+f(*it1, 1, *it2, 2)  # E: Argument 3 to "f" has incompatible type "*tuple[str]"; expected "int"
 f(*it1, '') # E: Argument 2 to "f" has incompatible type "str"; expected "int"
 [builtins fixtures/for.pyi]
 
@@ -243,7 +243,7 @@ ab: List[B]
 a: A
 b: B
 
-f(*aa)  # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B"
+f(*aa)  # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B"
 f(a, *ab) # Ok
 f(a, b)
 (cast(Any, f))(*aa)     # IDEA: Move to check-dynamic?
@@ -262,9 +262,9 @@ b: B
 c: C
 cc: CC
 
-f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B, B]"; expected "C"
-f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, C]"; expected "A"
-f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "*Tuple[B, B]"; expected "C"
+f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B, B]"; expected "C"
+f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*tuple[B, B, C]"; expected "A"
+f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "*tuple[B, B]"; expected "C"
 f(b, *(b, c)) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
 f(*(a, b))    # E: Missing positional arguments "b", "c" in call to "f"
 f(*(a, b, c, c)) # E: Too many arguments for "f"
@@ -308,13 +308,13 @@ aa: List[A]
 ab: List[B]
 a: A
 b: B
-f(*aa)           # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B"
-f(a, *aa)        # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B"
+f(*aa)           # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B"
+f(a, *aa)        # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B"
 f(b, *ab)        # E: Argument 1 to "f" has incompatible type "B"; expected "A"
 f(a, a, *ab)     # E: Argument 2 to "f" has incompatible type "A"; expected "B"
-f(a, b, *aa)     # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B"
+f(a, b, *aa)     # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B"
 f(b, b, *ab)     # E: Argument 1 to "f" has incompatible type "B"; expected "A"
-g(*ab)           # E: Argument 1 to "g" has incompatible type "*List[B]"; expected "A"
+g(*ab)           # E: Argument 1 to "g" has incompatible type "*list[B]"; expected "A"
 f(a, *ab)
 f(a, b, *ab)
 f(a, b, b, *ab)
@@ -334,14 +334,14 @@ b: B
 c: C
 cc: CC
 
-f(*(b, b, b))   # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, B]"; expected "A"
-f(*(a, a, b))   # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "B"
-f(*(a, b, a))   # E: Argument 1 to "f" has incompatible type "*Tuple[A, B, A]"; expected "B"
-f(a, *(a, b))   # E: Argument 2 to "f" has incompatible type "*Tuple[A, B]"; expected "B"
+f(*(b, b, b))   # E: Argument 1 to "f" has incompatible type "*tuple[B, B, B]"; expected "A"
+f(*(a, a, b))   # E: Argument 1 to "f" has incompatible type "*tuple[A, A, B]"; expected "B"
+f(*(a, b, a))   # E: Argument 1 to "f" has incompatible type "*tuple[A, B, A]"; expected "B"
+f(a, *(a, b))   # E: Argument 2 to "f" has incompatible type "*tuple[A, B]"; expected "B"
 f(b, *(b, b))   # E: Argument 1 to "f" has incompatible type "B"; expected "A"
 f(b, b, *(b,))  # E: Argument 1 to "f" has incompatible type "B"; expected "A"
 f(a, a, *(b,))  # E: Argument 2 to "f" has incompatible type "A"; expected "B"
-f(a, b, *(a,))  # E: Argument 3 to "f" has incompatible type "*Tuple[A]"; expected "B"
+f(a, b, *(a,))  # E: Argument 3 to "f" has incompatible type "*tuple[A]"; expected "B"
 f(*())          # E: Too few arguments for "f"
 f(*(a, b, b))
 f(a, *(b, b))
@@ -384,7 +384,7 @@ class B(A): pass
 aa: List[A]
 ab: List[B]
 
-g(*aa) # E: Argument 1 to "g" has incompatible type "*List[A]"; expected "B"
+g(*aa) # E: Argument 1 to "g" has incompatible type "*list[A]"; expected "B"
 f(*aa)
 f(*ab)
 g(*ab)
@@ -401,10 +401,10 @@ class B: pass
 
 a, b = None, None # type: (A, B)
 f(*())        # E: Too few arguments for "f"
-f(a, *[a])    # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "Optional[B]" \
-              # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B"
-f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B"
-f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "Optional[B]"
+f(a, *[a])    # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "Optional[B]" \
+              # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B"
+f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B"
+f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, A, B]"; expected "Optional[B]"
 f(*(a,))
 f(*(a, b))
 f(*(a, b, b, b))
@@ -420,7 +420,7 @@ f(x=1, *[2])
 [builtins fixtures/list.pyi]
 [out]
 main:3: error: "f" gets multiple values for keyword argument "x"
-main:3: error: Argument 1 to "f" has incompatible type "*List[int]"; expected "str"
+main:3: error: Argument 1 to "f" has incompatible type "*list[int]"; expected "str"
 
 [case testVarArgsAfterKeywordArgInCall2]
 # see: mypy issue #2729
@@ -429,7 +429,7 @@ f(y='x', *[1])
 [builtins fixtures/list.pyi]
 [out]
 main:3: error: "f" gets multiple values for keyword argument "y"
-main:3: error: Argument 1 to "f" has incompatible type "*List[int]"; expected "str"
+main:3: error: Argument 1 to "f" has incompatible type "*list[int]"; expected "str"
 
 [case testVarArgsAfterKeywordArgInCall3]
 def f(x: int, y: str) -> None: pass
@@ -543,15 +543,15 @@ b: B
 aa: List[A]
 
 if int():
-    a, b = f(*aa)    # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B"
+    a, b = f(*aa)    # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B"
 if int():
-    b, b = f(*aa)    # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B"
+    b, b = f(*aa)    # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B"
 if int():
     a, a = f(b, *aa) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
 if int():
-    b, b = f(b, *aa) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B"
+    b, b = f(b, *aa) # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B"
 if int():
-    b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B"
+    b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B"
 if int():
     a, b = f(a, *a)  # E: Expected iterable as variadic argument
 if int():
@@ -579,11 +579,11 @@ a: A
 b: B
 
 if int():
-    a, a = f(*(a, b))   # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A"
+    a, a = f(*(a, b))   # E: Argument 1 to "f" has incompatible type "*tuple[A, B]"; expected "A"
 if int():
     b, b = f(a, *(b,))  # E: Argument 1 to "f" has incompatible type "A"; expected "B"
 if int():
-    a, a = f(*(a, b))   # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A"
+    a, a = f(*(a, b))   # E: Argument 1 to "f" has incompatible type "*tuple[A, B]"; expected "A"
 if int():
     b, b = f(a, *(b,))  # E: Argument 1 to "f" has incompatible type "A"; expected "B"
 if int():
@@ -612,11 +612,11 @@ class A: pass
 class B: pass
 
 if int():
-    a, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A")
+    a, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "list[A]", variable has type "A")
 if int():
-    aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A")
+    aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "A")
 if int():
-    ab, aa = G().f(*[a]) # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B"
+    ab, aa = G().f(*[a]) # E: Argument 1 to "f" of "G" has incompatible type "*list[A]"; expected "B"
 if int():
     ao, ao = G().f(*[a])
 if int():
@@ -686,15 +686,15 @@ a = {'a': [1, 2]}
 b = {'b': ['c', 'd']}
 c = {'c': 1.0}
 d = {'d': 1}
-f(a) # E: Argument 1 to "f" has incompatible type "Dict[str, List[int]]"; expected "Dict[str, Sequence[int]]" \
+f(a) # E: Argument 1 to "f" has incompatible type "dict[str, list[int]]"; expected "dict[str, Sequence[int]]" \
      # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Mapping" instead, which is covariant in the value type
-f(b) # E: Argument 1 to "f" has incompatible type "Dict[str, List[str]]"; expected "Dict[str, Sequence[int]]"
+f(b) # E: Argument 1 to "f" has incompatible type "dict[str, list[str]]"; expected "dict[str, Sequence[int]]"
 g(c)
-g(d) # E: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "Dict[str, float]" \
+g(d) # E: Argument 1 to "g" has incompatible type "dict[str, int]"; expected "dict[str, float]" \
      # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Mapping" instead, which is covariant in the value type
-h(c) # E: Argument 1 to "h" has incompatible type "Dict[str, float]"; expected "Dict[str, int]"
+h(c) # E: Argument 1 to "h" has incompatible type "dict[str, float]"; expected "dict[str, int]"
 h(d)
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-medium.pyi]
@@ -703,13 +703,13 @@ h(d)
 from typing import List, Union
 def f(numbers: List[Union[int, float]]) -> None: pass
 a = [1, 2]
-f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Union[int, float]]" \
+f(a) # E: Argument 1 to "f" has incompatible type "list[int]"; expected "list[Union[int, float]]" \
      # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \
      # N: Consider using "Sequence" instead, which is covariant
 x = [1]
 y = ['a']
 if int():
-    x = y # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]")
+    x = y # E: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int]")
 [builtins fixtures/list.pyi]
 
 [case testInvariantTypeConfusingNames]
@@ -720,8 +720,8 @@ def f(x: Listener) -> None: pass
 def g(y: DictReader) -> None: pass
 a = [1, 2]
 b = {'b': 1}
-f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "Listener"
-g(b) # E: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "DictReader"
+f(a) # E: Argument 1 to "f" has incompatible type "list[int]"; expected "Listener"
+g(b) # E: Argument 1 to "g" has incompatible type "dict[str, int]"; expected "DictReader"
 [builtins fixtures/dict.pyi]
 
 [case testInvariantTypeConfusingNames2]
@@ -822,7 +822,7 @@ Weird = TypedDict("Weird", {"@": int})
 def foo(**kwargs: Unpack[Weird]) -> None:
     reveal_type(kwargs["@"])  # N: Revealed type is "builtins.int"
 foo(**{"@": 42})
-foo(**{"no": "way"})  # E: Argument 1 to "foo" has incompatible type "**Dict[str, str]"; expected "int"
+foo(**{"no": "way"})  # E: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "int"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
 
diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test
index 895b16e5e3c3..a2d201fa301d 100644
--- a/test-data/unit/check-warnings.test
+++ b/test-data/unit/check-warnings.test
@@ -207,7 +207,7 @@ def g() -> Any: pass
 def f() -> typ: return g()
 [builtins fixtures/tuple.pyi]
 [out]
-main:11: error: Returning Any from function declared to return "Tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int]"
+main:11: error: Returning Any from function declared to return "tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int]"
 
 [case testReturnAnySilencedFromTypedFunction]
 # flags: --warn-return-any
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index 012e1e6b7fe6..c65f55620d67 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -478,7 +478,7 @@ disallow_any_generics = True
 [file m.py]
 def j(s: frozenset) -> None: pass
 [out]
-m.py:1: error: Missing type parameters for generic type "FrozenSet"
+m.py:1: error: Missing type parameters for generic type "frozenset"
 
 [case testDisallowAnyGenericsTypingCollections]
 # cmd: mypy m.py
@@ -525,7 +525,7 @@ strict_optional = True
 ignore_errors = False
 [out]
 a/b/c/d/e/__init__.py:2: error: Missing type parameters for generic type "List"
-a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "List[Any]"
+a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "list[Any]"
 
 [case testMissingFile]
 # cmd: mypy nope.py
@@ -650,7 +650,7 @@ def foo() -> str:
     return 9
 [out]
 s4.py:2: error: Incompatible return value type (got "int", expected "str")
-s3.py:2: error: Incompatible return value type (got "List[int]", expected "int")
+s3.py:2: error: Incompatible return value type (got "list[int]", expected "int")
 s1.py:2: error: Incompatible return value type (got "int", expected "str")
 
 [case testShadowFileWithPretty]
@@ -830,7 +830,7 @@ x = []  # type: List[float]
 y = []  # type: List[int]
 x = y
 [out]
-bad.py:4: error: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]")
+bad.py:4: error: Incompatible types in assignment (expression has type "list[int]", variable has type "list[float]")
 bad.py:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
 bad.py:4: note: Consider using "Sequence" instead, which is covariant
 Found 1 error in 1 file (checked 1 source file)
diff --git a/test-data/unit/fine-grained-inspect.test b/test-data/unit/fine-grained-inspect.test
index 0e05769370a2..5caa1a94387b 100644
--- a/test-data/unit/fine-grained-inspect.test
+++ b/test-data/unit/fine-grained-inspect.test
@@ -23,7 +23,7 @@ NameExpr -> "C[T]"
 MemberExpr -> "T"
 NameExpr -> "C[T]"
 MemberExpr -> "T"
-12:5:12:5 -> "Type[foo.C[builtins.int]]"
+12:5:12:5 -> "type[foo.C[builtins.int]]"
 12:5:12:9 -> "foo.C[builtins.int]"
 12:1:12:10 -> "builtins.int"
 CallExpr:12:5:12:9 -> "C[int]"
diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test
index 2cb2148a66fe..b85b5bd3e320 100644
--- a/test-data/unit/fine-grained-python312.test
+++ b/test-data/unit/fine-grained-python312.test
@@ -74,8 +74,8 @@ from builtins import tuple as B
 [typing fixtures/typing-full.pyi]
 [out]
 ==
-main:3: error: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int, str]")
-main:4: error: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, str]")
+main:3: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]")
+main:4: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]")
 
 [case testPEP695NestedGenericClassMethodUpdated]
 from a import f
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index b1ab9e235117..670ab42e1983 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -1340,7 +1340,7 @@ class A:
 [out]
 ==
 -- This is a bad error message
-main:7: error: Argument 1 to "use" has incompatible type "Type[A]"; expected "Callable[[], A]"
+main:7: error: Argument 1 to "use" has incompatible type "type[A]"; expected "Callable[[], A]"
 
 [case testConstructorSignatureChanged3]
 from a import C
@@ -2674,9 +2674,9 @@ def g() -> None: pass
 def g() -> int: pass
 [builtins fixtures/dict.pyi]
 [out]
-main:7: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+main:7: error: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 ==
-main:7: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+main:7: error: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 
 [case testRefreshPartialTypeInClass]
 import a
@@ -2692,9 +2692,9 @@ def g() -> None: pass
 def g() -> int: pass
 [builtins fixtures/dict.pyi]
 [out]
-main:5: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+main:5: error: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 ==
-main:5: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...")
+main:5: error: Need type annotation for "x" (hint: "x: dict[, ] = ...")
 
 [case testRefreshPartialTypeInferredAttributeIndex]
 from c import C
@@ -2833,7 +2833,7 @@ class M(type):
 ==
 a.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "str")
 ==
-a.py:4: error: "Type[C]" has no attribute "x"
+a.py:4: error: "type[C]" has no attribute "x"
 ==
 
 [case testMetaclassAttributesDirect]
@@ -2862,7 +2862,7 @@ class M(type):
 ==
 a.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str")
 ==
-a.py:3: error: "Type[C]" has no attribute "x"
+a.py:3: error: "type[C]" has no attribute "x"
 ==
 
 [case testMetaclassOperators]
@@ -2886,7 +2886,7 @@ class M(type):
         pass
 [out]
 ==
-a.py:4: error: Unsupported operand types for + ("Type[C]" and "Type[C]")
+a.py:4: error: Unsupported operand types for + ("type[C]" and "type[C]")
 
 [case testMetaclassOperatorsDirect]
 import a
@@ -2907,7 +2907,7 @@ class M(type):
     def __add__(self, other: M) -> M:
         pass
 [out]
-a.py:3: error: Unsupported operand types for + ("Type[C]" and "Type[C]")
+a.py:3: error: Unsupported operand types for + ("type[C]" and "type[C]")
 ==
 
 [case testFineMetaclassUpdate]
@@ -2931,7 +2931,7 @@ class B(metaclass=c.M): pass
 class M(type):
     pass
 [out]
-a.py:6: error: Argument 1 to "f" has incompatible type "Type[B]"; expected "M"
+a.py:6: error: Argument 1 to "f" has incompatible type "type[B]"; expected "M"
 ==
 
 [case testFineMetaclassRecalculation]
@@ -2990,7 +2990,7 @@ class M(type):
     x: int
 [out]
 ==
-a.py:3: error: "Type[B]" has no attribute "x"
+a.py:3: error: "type[B]" has no attribute "x"
 
 [case testFineMetaclassRemoveFromClass2]
 import a
@@ -3015,7 +3015,7 @@ class M(type):
     x: int
 [out]
 ==
-a.py:3: error: Argument 1 to "test" has incompatible type "Type[B]"; expected "M"
+a.py:3: error: Argument 1 to "test" has incompatible type "type[B]"; expected "M"
 
 [case testBadMetaclassCorrected]
 import a
@@ -3052,7 +3052,7 @@ class C(metaclass=c.M):
 class M(type):
     x: int
 [out]
-a.py:3: error: "Type[C]" has no attribute "x"
+a.py:3: error: "type[C]" has no attribute "x"
 ==
 
 [case testIndirectSubclassReferenceMetaclass]
@@ -3088,7 +3088,7 @@ class M(type):
 ==
 a.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str")
 ==
-b.py:2: error: "Type[D]" has no attribute "x"
+b.py:2: error: "type[D]" has no attribute "x"
 ==
 
 [case testMetaclassDeletion]
@@ -3407,8 +3407,8 @@ lol(b.x)
 [builtins fixtures/tuple.pyi]
 [out]
 ==
-c.py:7: error: Argument 1 to "lol" has incompatible type "M"; expected "Tuple[Tuple[int]]"
-c.py:9: error: Argument 1 to "lol" has incompatible type "M"; expected "Tuple[Tuple[int]]"
+c.py:7: error: Argument 1 to "lol" has incompatible type "M"; expected "tuple[tuple[int]]"
+c.py:9: error: Argument 1 to "lol" has incompatible type "M"; expected "tuple[tuple[int]]"
 
 [case testNamedTupleUpdate4]
 import b
@@ -3522,9 +3522,9 @@ reveal_type(a.n)
 [out]
 ==
 ==
-c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
-c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+c.py:7: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 
 [case testTupleTypeUpdateNonRecursiveToRecursiveFine]
 import c
@@ -3555,7 +3555,7 @@ def f(x: a.N) -> None:
 [out]
 ==
 ==
-c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
+c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
 c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
 
 [case testTypeAliasUpdateNonRecursiveToRecursiveFine]
@@ -3587,7 +3587,7 @@ def f(x: a.N) -> None:
 [out]
 ==
 ==
-c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int], None], builtins.int]"
+c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int], None], builtins.int]"
 c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
 
 [case testTypedDictRefresh]
@@ -5422,7 +5422,7 @@ class C(Enum):
 [typing fixtures/typing-medium.pyi]
 [out]
 ==
-a.py:5: error: "Type[C]" has no attribute "Y"
+a.py:5: error: "type[C]" has no attribute "Y"
 
 [case testClassBasedEnumPropagation2]
 import a
@@ -5522,7 +5522,7 @@ C = Enum('C', 'X')
 [typing fixtures/typing-medium.pyi]
 [out]
 ==
-a.py:5: error: "Type[C]" has no attribute "Y"
+a.py:5: error: "type[C]" has no attribute "Y"
 
 [case testFuncBasedEnumPropagation2]
 import a
@@ -6167,7 +6167,7 @@ class C:
         pass
 [out]
 ==
-a.py:6: error: Argument 1 to "func" has incompatible type "Type[C]"; expected "Callable[[int], Any]"
+a.py:6: error: Argument 1 to "func" has incompatible type "type[C]"; expected "Callable[[int], Any]"
 
 [case testDunderNewDefine]
 import a
@@ -6781,7 +6781,7 @@ class M(type):
     x: int
 [out]
 ==
-a.py:4: error: Argument 1 to "func" has incompatible type "Type[B]"; expected "P"
+a.py:4: error: Argument 1 to "func" has incompatible type "type[B]"; expected "P"
 
 [case testProtocolVsProtocolSubUpdated]
 import a
@@ -7509,7 +7509,7 @@ def g() -> Tuple[str, str]: pass
 [builtins fixtures/tuple.pyi]
 [out]
 ==
-main:5: error: Incompatible return value type (got "List[str]", expected "List[int]")
+main:5: error: Incompatible return value type (got "list[str]", expected "list[int]")
 
 [case testUnpackInExpression1-only_when_nocache]
 from typing import Tuple, List
@@ -7532,8 +7532,8 @@ def t() -> Tuple[str]: ...
 [builtins fixtures/list.pyi]
 [out]
 ==
-main:5: error: Incompatible return value type (got "Tuple[int, str]", expected "Tuple[int, int]")
-main:8: error: List item 1 has incompatible type "Tuple[str]"; expected "int"
+main:5: error: Incompatible return value type (got "tuple[int, str]", expected "tuple[int, int]")
+main:8: error: List item 1 has incompatible type "tuple[str]"; expected "int"
 
 [case testUnpackInExpression2-only_when_nocache]
 from typing import Set
@@ -7553,7 +7553,7 @@ def t() -> Tuple[str]: pass
 [builtins fixtures/set.pyi]
 [out]
 ==
-main:5: error: Argument 2 to  has incompatible type "*Tuple[str]"; expected "int"
+main:5: error: Argument 2 to  has incompatible type "*tuple[str]"; expected "int"
 
 [case testUnpackInExpression3-only_when_nocache]
 from typing import Dict
@@ -7573,7 +7573,7 @@ def d() -> Dict[int, int]: pass
 [builtins fixtures/dict.pyi]
 [out]
 ==
-main:5: error: Unpacked dict entry 1 has incompatible type "Dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]"
+main:5: error: Unpacked dict entry 1 has incompatible type "dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]"
 
 [case testAwaitAndAsyncDef-only_when_nocache]
 from a import g
@@ -7814,7 +7814,7 @@ class B:
 [builtins fixtures/list.pyi]
 [out]
 ==
-main:6: error: Incompatible types in assignment (expression has type "List[str]", base class "B" defined the type as "List[int]")
+main:6: error: Incompatible types in assignment (expression has type "list[str]", base class "B" defined the type as "list[int]")
 
 [case testLiskovFineVariableCleanDefInMethodNested-only_when_nocache]
 from b import B
@@ -8060,7 +8060,7 @@ A = NamedTuple('A', F)  # type: ignore
 [builtins fixtures/list.pyi]
 [out]
 ==
-b.py:3: note: Revealed type is "Tuple[(), fallback=a.A]"
+b.py:3: note: Revealed type is "tuple[(), fallback=a.A]"
 
 [case testImportOnTopOfAlias1]
 from a import A
@@ -8100,7 +8100,7 @@ def A(x: str) -> str: pass
 [builtins fixtures/list.pyi]
 [out]
 ==
-a.py:4: error: Incompatible import of "A" (imported name has type "Callable[[str], str]", local name has type "Type[List[Any]]")
+a.py:4: error: Incompatible import of "A" (imported name has type "Callable[[str], str]", local name has type "type[list[Any]]")
 
 [case testFakeOverloadCrash]
 import b
@@ -9937,7 +9937,7 @@ x = 0  # Arbitrary change to trigger reprocessing
 [builtins fixtures/dict.pyi]
 [out]
 ==
-a.py:3: note: Revealed type is "Tuple[Literal[1]?, Literal['x']?]"
+a.py:3: note: Revealed type is "tuple[Literal[1]?, Literal['x']?]"
 
 [case testVariadicClassFineUpdateRegularToVariadic]
 from typing import Any
diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test
index 8c806623403b..7463571b76b4 100644
--- a/test-data/unit/merge.test
+++ b/test-data/unit/merge.test
@@ -671,12 +671,12 @@ TypeInfo<2>(
     _NT<6>
     __annotations__<7> (builtins.dict[builtins.str<8>, Any]<9>)
     __doc__<10> (builtins.str<8>)
-    __match_args__<11> (Tuple[Literal['x']])
+    __match_args__<11> (tuple[Literal['x']])
     __new__<12>
     _asdict<13>
     _field_defaults<14> (builtins.dict[builtins.str<8>, Any]<9>)
     _field_types<15> (builtins.dict[builtins.str<8>, Any]<9>)
-    _fields<16> (Tuple[builtins.str<8>])
+    _fields<16> (tuple[builtins.str<8>])
     _make<17>
     _replace<18>
     _source<19> (builtins.str<8>)
@@ -695,12 +695,12 @@ TypeInfo<2>(
     _NT<6>
     __annotations__<7> (builtins.dict[builtins.str<8>, Any]<9>)
     __doc__<10> (builtins.str<8>)
-    __match_args__<11> (Tuple[Literal['x'], Literal['y']])
+    __match_args__<11> (tuple[Literal['x'], Literal['y']])
     __new__<12>
     _asdict<13>
     _field_defaults<14> (builtins.dict[builtins.str<8>, Any]<9>)
     _field_types<15> (builtins.dict[builtins.str<8>, Any]<9>)
-    _fields<16> (Tuple[builtins.str<8>, builtins.str<8>])
+    _fields<16> (tuple[builtins.str<8>, builtins.str<8>])
     _make<17>
     _replace<18>
     _source<19> (builtins.str<8>)
@@ -736,7 +736,7 @@ TypeInfo<2>(
     _asdict<12>
     _field_defaults<13> (builtins.dict[builtins.str<8>, Any]<9>)
     _field_types<14> (builtins.dict[builtins.str<8>, Any]<9>)
-    _fields<15> (Tuple[builtins.str<8>])
+    _fields<15> (tuple[builtins.str<8>])
     _make<16>
     _replace<17>
     _source<18> (builtins.str<8>)
@@ -759,7 +759,7 @@ TypeInfo<2>(
     _asdict<12>
     _field_defaults<13> (builtins.dict[builtins.str<8>, Any]<9>)
     _field_types<14> (builtins.dict[builtins.str<8>, Any]<9>)
-    _fields<15> (Tuple[builtins.str<8>, builtins.str<8>])
+    _fields<15> (tuple[builtins.str<8>, builtins.str<8>])
     _make<16>
     _replace<17>
     _source<18> (builtins.str<8>)
@@ -795,10 +795,10 @@ class A: pass
 a: Type[A]
 [out]
 ## target
-NameExpr:3: Type[target.A<0>]
+NameExpr:3: type[target.A<0>]
 ==>
 ## target
-NameExpr:3: Type[target.A<0>]
+NameExpr:3: type[target.A<0>]
 
 [case testTypeVar_types]
 import target
diff --git a/test-data/unit/parse.test b/test-data/unit/parse.test
index 82065c95faf8..b1c0918365a6 100644
--- a/test-data/unit/parse.test
+++ b/test-data/unit/parse.test
@@ -548,7 +548,7 @@ MypyFile:1(
       NameExpr(x)
       NameExpr(y))
     NameExpr(z)
-    Tuple[int?, a?[c?]]))
+    tuple[int?, a?[c?]]))
 
 [case testMultipleVarDef2]
 (xx, z, i) = 1 # type: (a[c], Any, int)
@@ -560,7 +560,7 @@ MypyFile:1(
       NameExpr(z)
       NameExpr(i))
     IntExpr(1)
-    Tuple[a?[c?], Any?, int?]))
+    tuple[a?[c?], Any?, int?]))
 
 [case testMultipleVarDef3]
 (xx, (z, i)) = 1 # type: (a[c], (Any, int))
@@ -573,7 +573,7 @@ MypyFile:1(
         NameExpr(z)
         NameExpr(i)))
     IntExpr(1)
-    Tuple[a?[c?], Tuple[Any?, int?]]))
+    tuple[a?[c?], tuple[Any?, int?]]))
 
 [case testAnnotateAssignmentViaSelf]
 class A:
@@ -617,7 +617,7 @@ MypyFile:1(
     TupleExpr:2(
       IntExpr(1)
       IntExpr(2))
-    Tuple[foo?, bar?]))
+    tuple[foo?, bar?]))
 
 [case testWhitespaceAndCommentAnnotation]
 x = 1#type:int
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 8c442a23d80a..081d21f14857 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -626,8 +626,8 @@ a + 1
 [out]
 _testMapStr.py:4: error: No overload variant of "__add__" of "list" matches argument type "int"
 _testMapStr.py:4: note: Possible overload variants:
-_testMapStr.py:4: note:     def __add__(self, List[str], /) -> List[str]
-_testMapStr.py:4: note:     def [_S] __add__(self, List[_S], /) -> List[Union[_S, str]]
+_testMapStr.py:4: note:     def __add__(self, list[str], /) -> list[str]
+_testMapStr.py:4: note:     def [_S] __add__(self, list[_S], /) -> list[Union[_S, str]]
 
 [case testRelativeImport]
 import typing
@@ -762,7 +762,7 @@ def p(t: Tuple[str, ...]) -> None:
 ''.startswith(('x', b'y'))
 [out]
 _program.py:6: error: "str" not callable
-_program.py:8: error: Argument 1 to "startswith" of "str" has incompatible type "Tuple[str, bytes]"; expected "Union[str, Tuple[str, ...]]"
+_program.py:8: error: Argument 1 to "startswith" of "str" has incompatible type "tuple[str, bytes]"; expected "Union[str, tuple[str, ...]]"
 
 [case testMultiplyTupleByInteger]
 n = 4
@@ -771,8 +771,8 @@ t + 1
 [out]
 _program.py:3: error: No overload variant of "__add__" of "tuple" matches argument type "int"
 _program.py:3: note: Possible overload variants:
-_program.py:3: note:     def __add__(self, Tuple[str, ...], /) -> Tuple[str, ...]
-_program.py:3: note:     def [_T] __add__(self, Tuple[_T, ...], /) -> Tuple[Union[str, _T], ...]
+_program.py:3: note:     def __add__(self, tuple[str, ...], /) -> tuple[str, ...]
+_program.py:3: note:     def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Union[str, _T], ...]
 
 [case testMultiplyTupleByIntegerReverse]
 n = 4
@@ -781,8 +781,8 @@ t + 1
 [out]
 _program.py:3: error: No overload variant of "__add__" of "tuple" matches argument type "int"
 _program.py:3: note: Possible overload variants:
-_program.py:3: note:     def __add__(self, Tuple[str, ...], /) -> Tuple[str, ...]
-_program.py:3: note:     def [_T] __add__(self, Tuple[_T, ...], /) -> Tuple[Union[str, _T], ...]
+_program.py:3: note:     def __add__(self, tuple[str, ...], /) -> tuple[str, ...]
+_program.py:3: note:     def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Union[str, _T], ...]
 
 [case testDictWithKeywordArgs]
 from typing import Dict, Any, List
@@ -794,7 +794,7 @@ d4 = dict(a=1, b='') # type: Dict[str, Any]
 result = dict(x=[], y=[]) # type: Dict[str, List[str]]
 [out]
 _program.py:3: error: Dict entry 1 has incompatible type "str": "str"; expected "str": "int"
-_program.py:5: error: "Dict[str, int]" has no attribute "xyz"
+_program.py:5: error: "dict[str, int]" has no attribute "xyz"
 
 [case testDefaultDict]
 # flags: --new-type-inference
@@ -823,11 +823,11 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]):
 MyDDict(dict)['0']
 MyDDict(dict)[0]
 [out]
-_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[_T]]"; expected "Optional[Callable[[], str]]"
+_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "type[list[_T]]"; expected "Optional[Callable[[], str]]"
 _program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int"
 _program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str")
-_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[Never]]"; expected "defaultdict[int, List[Never]]"
-_program.py:24: error: Invalid index type "str" for "MyDDict[Dict[Never, Never]]"; expected type "int"
+_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, list[Never]]"; expected "defaultdict[int, list[Never]]"
+_program.py:24: error: Invalid index type "str" for "MyDDict[dict[Never, Never]]"; expected type "int"
 
 [case testCollectionsAliases]
 import typing as t
@@ -1005,7 +1005,7 @@ a[0] = 'x', 1
 a[1] = 2, 'y'
 a[:] = [('z', 3)]
 [out]
-_program.py:4: error: Incompatible types in assignment (expression has type "Tuple[int, str]", target has type "Tuple[str, int]")
+_program.py:4: error: Incompatible types in assignment (expression has type "tuple[int, str]", target has type "tuple[str, int]")
 
 [case testContextManager]
 import contextlib
@@ -1194,8 +1194,8 @@ other = 4 + get_c_type() + 5
 reveal_type(res)
 reveal_type(other)
 [out]
-_testMetaclassOpAccess.py:21: note: Revealed type is "Type[_testMetaclassOpAccess.A]"
-_testMetaclassOpAccess.py:22: note: Revealed type is "Type[_testMetaclassOpAccess.C]"
+_testMetaclassOpAccess.py:21: note: Revealed type is "type[_testMetaclassOpAccess.A]"
+_testMetaclassOpAccess.py:22: note: Revealed type is "type[_testMetaclassOpAccess.C]"
 
 [case testMetaclassOpAccessUnion]
 from typing import Type, Union
@@ -1285,8 +1285,8 @@ class C:
     __slots__: List[int] = []
 [out]
 _testInvalidSlots.py:3: error: Invalid type for "__slots__" (actual type "int", expected type "Union[str, Iterable[str]]")
-_testInvalidSlots.py:5: error: Invalid type for "__slots__" (actual type "Tuple[int, int]", expected type "Union[str, Iterable[str]]")
-_testInvalidSlots.py:7: error: Invalid type for "__slots__" (actual type "List[int]", expected type "Union[str, Iterable[str]]")
+_testInvalidSlots.py:5: error: Invalid type for "__slots__" (actual type "tuple[int, int]", expected type "Union[str, Iterable[str]]")
+_testInvalidSlots.py:7: error: Invalid type for "__slots__" (actual type "list[int]", expected type "Union[str, Iterable[str]]")
 
 [case testDictWithStarStarSpecialCase]
 from typing import Dict
@@ -1297,7 +1297,7 @@ def f() -> Dict[int, str]:
 def d() -> Dict[int, int]:
     return {}
 [out]
-_testDictWithStarStarSpecialCase.py:4: error: Unpacked dict entry 1 has incompatible type "Dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]"
+_testDictWithStarStarSpecialCase.py:4: error: Unpacked dict entry 1 has incompatible type "dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]"
 
 [case testLoadsOfOverloads]
 from typing import overload, Any, TypeVar, Iterable, List, Dict, Callable, Union
@@ -1357,7 +1357,7 @@ X = namedtuple('X', ['a', 'b'])
 x = X(a=1, b='s')
 
 [out]
-_testNamedTupleNew.py:12: note: Revealed type is "Tuple[builtins.int, fallback=_testNamedTupleNew.Child]"
+_testNamedTupleNew.py:12: note: Revealed type is "tuple[builtins.int, fallback=_testNamedTupleNew.Child]"
 
 [case testNamedTupleTypeInheritanceSpecialCase]
 from typing import NamedTuple, Tuple
@@ -1383,7 +1383,7 @@ _testNamedTupleTypeInheritanceSpecialCase.py:8: note: Revealed type is "builtins
 _testNamedTupleTypeInheritanceSpecialCase.py:9: note: Revealed type is "builtins.tuple[builtins.str, ...]"
 _testNamedTupleTypeInheritanceSpecialCase.py:10: note: Revealed type is "builtins.dict[builtins.str, Any]"
 _testNamedTupleTypeInheritanceSpecialCase.py:17: error: Argument 1 to "accepts_named_tuple" has incompatible type "int"; expected "NamedTuple"
-_testNamedTupleTypeInheritanceSpecialCase.py:18: error: Argument 1 to "accepts_named_tuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple"
+_testNamedTupleTypeInheritanceSpecialCase.py:18: error: Argument 1 to "accepts_named_tuple" has incompatible type "tuple[int, int]"; expected "NamedTuple"
 
 [case testNewAnalyzerBasicTypeshed_newsemanal]
 from typing import Dict, List, Tuple
@@ -1424,8 +1424,8 @@ frozenset({1}) == [1]  # Error
 {1: 2}.values() == {2}  # Error
 {1: 2}.keys() == [1]  # OK
 [out]
-_testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "FrozenSet[int]", right operand type: "List[int]")
-_testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "dict_values[int, int]", right operand type: "Set[int]")
+_testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "frozenset[int]", right operand type: "list[int]")
+_testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "dict_values[int, int]", right operand type: "set[int]")
 
 [case testUnreachableWithStdlibContextManagers]
 # mypy: warn-unreachable, strict-optional
@@ -1551,7 +1551,7 @@ if isinstance(obj, Hashable):
 if isinstance(obj, Awaitable):
     reveal_type(obj)
 [out]
-_testSpecialTypingProtocols.py:6: note: Revealed type is "Tuple[builtins.int]"
+_testSpecialTypingProtocols.py:6: note: Revealed type is "tuple[builtins.int]"
 _testSpecialTypingProtocols.py:8: error: Statement is unreachable
 
 [case testTypeshedRecursiveTypesExample]
@@ -1632,8 +1632,8 @@ def foo(x: T) -> T:
     return x
 [out]
 _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"
-_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[type[builtins.int], builtins.str]"
 
 [case testTypeAliasWithNewStyleUnionInStub]
 import m
@@ -1686,12 +1686,12 @@ CU4: TypeAlias = int | Callable[[str | bool], str]
 [out]
 m.pyi:5: note: Revealed type is "typing._SpecialForm"
 m.pyi:22: note: Revealed type is "typing._SpecialForm"
-_testTypeAliasWithNewStyleUnionInStub.py:3: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnionInStub.py:5: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnionInStub.py:7: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnionInStub.py:9: note: Revealed type is "Union[Type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnionInStub.py:11: note: Revealed type is "Union[builtins.str, Type[builtins.int]]"
-_testTypeAliasWithNewStyleUnionInStub.py:13: note: Revealed type is "Union[builtins.str, Type[builtins.int]]"
+_testTypeAliasWithNewStyleUnionInStub.py:3: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnionInStub.py:5: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnionInStub.py:7: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnionInStub.py:9: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnionInStub.py:11: note: Revealed type is "Union[builtins.str, type[builtins.int]]"
+_testTypeAliasWithNewStyleUnionInStub.py:13: note: Revealed type is "Union[builtins.str, type[builtins.int]]"
 
 [case testEnumNameWorkCorrectlyOn311]
 # flags: --python-version 3.11
@@ -1727,11 +1727,11 @@ D: TypeAlias = str | int
 _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type
 _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("GenericAlias")
 _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type
-_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Unsupported left operand type for | ("Type[str]")
+_testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Unsupported left operand type for | ("type[str]")
 _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type
-_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]")
+_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("type[str]")
 _testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type
-_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("Type[str]")
+_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("type[str]")
 
 [case testTypedDictUnionGetFull]
 from typing import Dict
@@ -1780,15 +1780,15 @@ WrongEllipsis = tuple[float, float, ...] | str  # Error
 
 reveal_type(tuple[int, str]((1, "x")))
 [out]
-_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "Union[builtins.str, Tuple[builtins.float, builtins.float, builtins.str]]"
-_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "Union[Tuple[builtins.float], builtins.str]"
+_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "Union[builtins.str, tuple[builtins.float, builtins.float, builtins.str]]"
+_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "Union[tuple[builtins.float], builtins.str]"
 _testTupleWithDifferentArgsPy310.py:17: note: Revealed type is "Union[builtins.tuple[builtins.float, ...], builtins.str]"
-_testTupleWithDifferentArgsPy310.py:18: note: Revealed type is "Tuple[builtins.float, builtins.str]"
+_testTupleWithDifferentArgsPy310.py:18: note: Revealed type is "tuple[builtins.float, builtins.str]"
 _testTupleWithDifferentArgsPy310.py:19: note: Revealed type is "builtins.tuple[builtins.float, ...]"
-_testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]"
+_testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
 _testTupleWithDifferentArgsPy310.py:26: error: Invalid type: try using Literal[1] instead?
 _testTupleWithDifferentArgsPy310.py:27: error: Unexpected "..."
-_testTupleWithDifferentArgsPy310.py:29: note: Revealed type is "Tuple[builtins.int, builtins.str]"
+_testTupleWithDifferentArgsPy310.py:29: note: Revealed type is "tuple[builtins.int, builtins.str]"
 
 [case testEnumIterMetaInference]
 import socket
@@ -1930,7 +1930,7 @@ Foo().__dict__ = {}
 _testInferenceOfDunderDictOnClassObjects.py:2: note: Revealed type is "types.MappingProxyType[builtins.str, Any]"
 _testInferenceOfDunderDictOnClassObjects.py:3: note: Revealed type is "builtins.dict[builtins.str, Any]"
 _testInferenceOfDunderDictOnClassObjects.py:4: error: Property "__dict__" defined in "type" is read-only
-_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[Never, Never]", variable has type "MappingProxyType[str, Any]")
+_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "MappingProxyType[str, Any]")
 
 [case testTypeVarTuple]
 # flags: --python-version=3.11
diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test
index b14358509f85..7022da01eeaf 100644
--- a/test-data/unit/semanal-classes.test
+++ b/test-data/unit/semanal-classes.test
@@ -472,7 +472,7 @@ MypyFile:1(
         Args(
           Var(cls)
           Var(z))
-        def (cls: Type[__main__.A], z: builtins.int) -> builtins.str
+        def (cls: type[__main__.A], z: builtins.int) -> builtins.str
         Class
         Block:3(
           PassStmt:3())))))
@@ -492,7 +492,7 @@ MypyFile:1(
         f
         Args(
           Var(cls))
-        def (cls: Type[__main__.A]) -> builtins.str
+        def (cls: type[__main__.A]) -> builtins.str
         Class
         Block:3(
           PassStmt:3())))))
@@ -583,7 +583,7 @@ MypyFile:1(
   ClassDef:2(
     A
     TupleType(
-      Tuple[builtins.int, builtins.str])
+      tuple[builtins.int, builtins.str])
     BaseType(
       builtins.tuple[Union[builtins.int, builtins.str], ...])
     PassStmt:2()))
diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test
index 16944391da86..62bd87f1995a 100644
--- a/test-data/unit/semanal-namedtuple.test
+++ b/test-data/unit/semanal-namedtuple.test
@@ -10,10 +10,10 @@ MypyFile:1(
   ImportFrom:1(collections, [namedtuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[Any]))
+    NamedTupleExpr:2(N, tuple[Any]))
   FuncDef:3(
     f
-    def () -> Tuple[Any, fallback=__main__.N]
+    def () -> tuple[Any, fallback=__main__.N]
     Block:3(
       PassStmt:3())))
 
@@ -27,10 +27,10 @@ MypyFile:1(
   ImportFrom:1(collections, [namedtuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[Any, Any]))
+    NamedTupleExpr:2(N, tuple[Any, Any]))
   FuncDef:3(
     f
-    def () -> Tuple[Any, Any, fallback=__main__.N]
+    def () -> tuple[Any, Any, fallback=__main__.N]
     Block:3(
       PassStmt:3())))
 
@@ -44,10 +44,10 @@ MypyFile:1(
   ImportFrom:1(collections, [namedtuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[Any, Any]))
+    NamedTupleExpr:2(N, tuple[Any, Any]))
   FuncDef:3(
     f
-    def () -> Tuple[Any, Any, fallback=__main__.N]
+    def () -> tuple[Any, Any, fallback=__main__.N]
     Block:3(
       PassStmt:3())))
 
@@ -61,10 +61,10 @@ MypyFile:1(
   ImportFrom:1(collections, [namedtuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[Any, Any]))
+    NamedTupleExpr:2(N, tuple[Any, Any]))
   FuncDef:3(
     f
-    def () -> Tuple[Any, Any, fallback=__main__.N]
+    def () -> tuple[Any, Any, fallback=__main__.N]
     Block:3(
       PassStmt:3())))
 
@@ -78,7 +78,7 @@ MypyFile:1(
   ImportFrom:1(typing, [NamedTuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str])))
+    NamedTupleExpr:2(N, tuple[builtins.int, builtins.str])))
 
 [case testNamedTupleWithTupleFieldNamesWithItemTypes]
 from typing import NamedTuple
@@ -90,7 +90,7 @@ MypyFile:1(
   ImportFrom:1(typing, [NamedTuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str])))
+    NamedTupleExpr:2(N, tuple[builtins.int, builtins.str])))
 
 [case testNamedTupleBaseClass]
 from collections import namedtuple
@@ -102,11 +102,11 @@ MypyFile:1(
   ImportFrom:1(collections, [namedtuple])
   AssignmentStmt:2(
     NameExpr(N* [__main__.N])
-    NamedTupleExpr:2(N, Tuple[Any]))
+    NamedTupleExpr:2(N, tuple[Any]))
   ClassDef:3(
     A
     TupleType(
-      Tuple[Any, fallback=__main__.N])
+      tuple[Any, fallback=__main__.N])
     BaseType(
       __main__.N)
     PassStmt:3()))
@@ -121,7 +121,7 @@ MypyFile:1(
   ClassDef:2(
     A
     TupleType(
-      Tuple[Any, fallback=__main__.N@2])
+      tuple[Any, fallback=__main__.N@2])
     BaseType(
       __main__.N@2)
     PassStmt:2()))
@@ -136,7 +136,7 @@ MypyFile:1(
   ClassDef:2(
     A
     TupleType(
-      Tuple[builtins.int, fallback=__main__.N@2])
+      tuple[builtins.int, fallback=__main__.N@2])
     BaseType(
       __main__.N@2)
     PassStmt:2()))
@@ -239,7 +239,7 @@ MypyFile:1(
   ClassDef:4(
     A
     TupleType(
-      Tuple[builtins.int, fallback=__main__.N@4])
+      tuple[builtins.int, fallback=__main__.N@4])
     Decorators(
       NameExpr(final [typing.final]))
     BaseType(
diff --git a/test-data/unit/semanal-typealiases.test b/test-data/unit/semanal-typealiases.test
index 88d234134350..e2c1c4863157 100644
--- a/test-data/unit/semanal-typealiases.test
+++ b/test-data/unit/semanal-typealiases.test
@@ -177,12 +177,12 @@ MypyFile:1(
   ImportFrom:1(typing, [Tuple])
   AssignmentStmt:2(
     NameExpr(T* [__main__.T])
-    TypeAliasExpr(Tuple[builtins.int, builtins.str]))
+    TypeAliasExpr(tuple[builtins.int, builtins.str]))
   FuncDef:3(
     f
     Args(
       Var(x))
-    def (x: Tuple[builtins.int, builtins.str])
+    def (x: tuple[builtins.int, builtins.str])
     Block:3(
       PassStmt:3())))
 
@@ -439,8 +439,8 @@ MypyFile:1(
   ImportFrom:1(typing, [Union, Tuple, Any])
   AssignmentStmt:2(
     NameExpr(A* [__main__.A])
-    TypeAliasExpr(Union[builtins.int, Tuple[builtins.int, Any]]))
+    TypeAliasExpr(Union[builtins.int, tuple[builtins.int, Any]]))
   AssignmentStmt:3(
     NameExpr(a [__main__.a])
     IntExpr(1)
-    Union[builtins.int, Tuple[builtins.int, Any]]))
+    Union[builtins.int, tuple[builtins.int, Any]]))
diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test
index 83c44738f055..a91d334af146 100644
--- a/test-data/unit/semanal-types.test
+++ b/test-data/unit/semanal-types.test
@@ -163,7 +163,7 @@ MypyFile:1(
     TupleExpr:4(
       NameExpr(None [builtins.None])
       NameExpr(None [builtins.None]))
-    Tuple[__main__.A, __main__.B])
+    tuple[__main__.A, __main__.B])
   AssignmentStmt:5(
     NameExpr(x* [__main__.x])
     TupleExpr:5(
@@ -366,7 +366,7 @@ MypyFile:1(
   ExpressionStmt:2(
     CastExpr:2(
       NameExpr(None [builtins.None])
-      Tuple[builtins.int, builtins.str])))
+      tuple[builtins.int, builtins.str])))
 
 [case testCastToFunctionType]
 from typing import Callable, cast
@@ -493,7 +493,7 @@ MypyFile:1(
     f
     Args(
       Var(x))
-    def [t] (x: Tuple[builtins.int, t`-1])
+    def [t] (x: tuple[builtins.int, t`-1])
     Block:4(
       PassStmt:4())))
 
@@ -694,11 +694,11 @@ MypyFile:1(
   AssignmentStmt:3(
     NameExpr(t1 [__main__.t1])
     NameExpr(None [builtins.None])
-    Tuple[builtins.object])
+    tuple[builtins.object])
   AssignmentStmt:4(
     NameExpr(t2 [__main__.t2])
     NameExpr(None [builtins.None])
-    Tuple[builtins.int, builtins.object]))
+    tuple[builtins.int, builtins.object]))
 
 [case testVariableLengthTuple]
 from typing import Tuple
diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test
index 512b572801d2..77e7763824d6 100644
--- a/test-data/unit/typexport-basic.test
+++ b/test-data/unit/typexport-basic.test
@@ -214,7 +214,7 @@ f(
   B())
 [builtins fixtures/tuple-simple.pyi]
 [out]
-CallExpr(6) : Tuple[A, B]
+CallExpr(6) : tuple[A, B]
 CallExpr(7) : A
 CallExpr(8) : B
 
@@ -294,8 +294,8 @@ import typing
 x = ()
 [builtins fixtures/primitives.pyi]
 [out]
-NameExpr(2) : Tuple[()]
-TupleExpr(2) : Tuple[()]
+NameExpr(2) : tuple[()]
+TupleExpr(2) : tuple[()]
 
 [case testInferTwoTypes]
 ## NameExpr
@@ -313,8 +313,8 @@ def f() -> None:
     x = ()
 [builtins fixtures/primitives.pyi]
 [out]
-NameExpr(3) : Tuple[()]
-TupleExpr(3) : Tuple[()]
+NameExpr(3) : tuple[()]
+TupleExpr(3) : tuple[()]
 
 
 -- Basic generics

From e57ece0705609ed42e62088f801c1d48b4652325 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 30 May 2025 20:20:32 +0200
Subject: [PATCH 1324/1617] Use more lower case builtins in error messages
 (#19177)

---
 mypy/messages.py                         |  2 +-
 mypy/semanal.py                          |  2 +-
 mypy/suggestions.py                      |  3 ---
 test-data/unit/check-classes.test        |  4 +--
 test-data/unit/check-generics.test       |  2 +-
 test-data/unit/check-type-aliases.test   | 14 +++++-----
 test-data/unit/fine-grained-suggest.test | 34 ++++++++++++------------
 7 files changed, 29 insertions(+), 32 deletions(-)

diff --git a/mypy/messages.py b/mypy/messages.py
index 2e07d7f63498..5457cea04a18 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -1784,7 +1784,7 @@ def reveal_locals(self, type_map: dict[str, Type | None], context: Context) -> N
 
     def unsupported_type_type(self, item: Type, context: Context) -> None:
         self.fail(
-            f'Cannot instantiate type "Type[{format_type_bare(item, self.options)}]"', context
+            f'Cannot instantiate type "type[{format_type_bare(item, self.options)}]"', context
         )
 
     def redundant_cast(self, typ: Type, context: Context) -> None:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index c5f4443588f8..855c279756e8 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -3985,7 +3985,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
             if isinstance(existing.node, TypeAlias) and not s.is_alias_def:
                 self.fail(
                     'Cannot assign multiple types to name "{}"'
-                    ' without an explicit "Type[...]" annotation'.format(lvalue.name),
+                    ' without an explicit "type[...]" annotation'.format(lvalue.name),
                     lvalue,
                 )
             return False
diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index f27ad7cdb637..a662dd7b98e9 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -53,7 +53,6 @@
     SymbolTable,
     TypeInfo,
     Var,
-    reverse_builtin_aliases,
 )
 from mypy.options import Options
 from mypy.plugin import FunctionContext, MethodContext, Plugin
@@ -830,8 +829,6 @@ def visit_instance(self, t: Instance) -> str:
         s = t.type.fullname or t.type.name or None
         if s is None:
             return ""
-        if s in reverse_builtin_aliases:
-            s = reverse_builtin_aliases[s]
 
         mod_obj = split_target(self.graph, s)
         assert mod_obj
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 93b575e25309..f8b841185fc6 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -3688,7 +3688,7 @@ def process(cls: Type[U]):
 [case testTypeUsingTypeCErrorUnsupportedType]
 from typing import Type, Tuple
 def foo(arg: Type[Tuple[int]]):
-    arg()  # E: Cannot instantiate type "Type[tuple[int]]"
+    arg()  # E: Cannot instantiate type "type[tuple[int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeUsingTypeCOverloadedClass]
@@ -3732,7 +3732,7 @@ def f(a: T): pass
 [case testTypeUsingTypeCTuple]
 from typing import Type, Tuple
 def f(a: Type[Tuple[int, int]]):
-    a()  # E: Cannot instantiate type "Type[tuple[int, int]]"
+    a()  # E: Cannot instantiate type "type[tuple[int, int]]"
 [builtins fixtures/tuple.pyi]
 
 [case testTypeUsingTypeCNamedTuple]
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 89693a6a7be0..dbc39d79d921 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -1019,7 +1019,7 @@ class C:
     if int():
         a = B
     if int():
-        b = int  # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation
+        b = int  # E: Cannot assign multiple types to name "b" without an explicit "type[...]" annotation
     if int():
         c = int
     def f(self, x: a) -> None: pass  # E: Variable "__main__.C.a" is not valid as a type \
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
index 5f7646c62e96..718d730132ae 100644
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -73,7 +73,7 @@ U = Union[int, str]
 [case testProhibitReassigningAliases]
 A = float
 if int():
-    A = int  # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation
+    A = int  # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation
 [out]
 
 [case testProhibitReassigningSubscriptedAliases]
@@ -81,7 +81,7 @@ from typing import Callable
 A = Callable[[], float]
 if int():
     A = Callable[[], int] \
-      # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \
+      # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation \
       # E: Value of type "int" is not indexable
       # the second error is because of `Callable = 0` in lib-stub/typing.pyi
 [builtins fixtures/list.pyi]
@@ -93,7 +93,7 @@ T = TypeVar('T')
 
 A = Tuple[T, T]
 if int():
-    A = Union[T, int]  # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation
+    A = Union[T, int]  # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
@@ -926,12 +926,12 @@ class Child(Parent): pass
 p = Parent()
 c = Child()
 
-NormalImplicit = 4   # E: Cannot assign multiple types to name "NormalImplicit" without an explicit "Type[...]" annotation \
+NormalImplicit = 4   # E: Cannot assign multiple types to name "NormalImplicit" without an explicit "type[...]" annotation \
                      # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
-NormalExplicit = 4   # E: Cannot assign multiple types to name "NormalExplicit" without an explicit "Type[...]" annotation \
+NormalExplicit = 4   # E: Cannot assign multiple types to name "NormalExplicit" without an explicit "type[...]" annotation \
                      # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
-SpecialImplicit = 4  # E: Cannot assign multiple types to name "SpecialImplicit" without an explicit "Type[...]" annotation
-SpecialExplicit = 4  # E: Cannot assign multiple types to name "SpecialExplicit" without an explicit "Type[...]" annotation
+SpecialImplicit = 4  # E: Cannot assign multiple types to name "SpecialImplicit" without an explicit "type[...]" annotation
+SpecialExplicit = 4  # E: Cannot assign multiple types to name "SpecialExplicit" without an explicit "type[...]" annotation
 
 Parent.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
 Parent.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]")
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index ba6006300a4c..7034b5e48943 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -17,8 +17,8 @@ def bar() -> None:
 [out]
 bar.py:3: (str)
 bar.py:4: (arg=str)
-bar.py:6: (*typing.List[str])
-bar.py:8: (**typing.Dict[str, str])
+bar.py:6: (*list[str])
+bar.py:8: (**dict[str, str])
 ==
 
 [case testSuggestCallsitesStep2]
@@ -41,8 +41,8 @@ def bar() -> None:
 ==
 bar.py:3: (str)
 bar.py:4: (arg=str)
-bar.py:6: (*typing.List[str])
-bar.py:8: (**typing.Dict[str, str])
+bar.py:6: (*list[str])
+bar.py:8: (**dict[str, str])
 
 [case testMaxGuesses]
 # suggest: foo.foo
@@ -691,8 +691,8 @@ No guesses that match criteria!
 (int, int) -> Any
 No guesses that match criteria!
 ==
-(typing.List[Any]) -> int
-(typing.List[Any]) -> int
+(list[Any]) -> int
+(list[Any]) -> int
 
 
 [case testSuggestFlexAny2]
@@ -965,7 +965,7 @@ def g(): ...
 z = foo(f(), g())
 [builtins fixtures/isinstancelist.pyi]
 [out]
-(foo.List[Any], UNKNOWN) -> Tuple[foo.List[Any], Any]
+(list[Any], UNKNOWN) -> Tuple[list[Any], Any]
 ==
 
 [case testSuggestBadImport]
@@ -1007,11 +1007,11 @@ spam({'x': 5})
 
 [builtins fixtures/dict.pyi]
 [out]
-() -> typing.Dict[str, int]
-() -> typing.Dict[Any, Any]
-() -> foo:List[typing.Dict[str, int]]
-() -> foo.List[int]
-(typing.Dict[str, int]) -> None
+() -> dict[str, int]
+() -> dict[Any, Any]
+() -> list[dict[str, int]]
+() -> list[int]
+(dict[str, int]) -> None
 ==
 
 [case testSuggestWithErrors]
@@ -1161,18 +1161,18 @@ tuple1(t)
 [out]
 (int, int) -> int
 (int, int) -> int
-(int) -> foo.List[int]
-(foo.List[int]) -> int
+(int) -> list[int]
+(list[int]) -> int
 (Union[int, str]) -> None
 (Callable[[int], int]) -> int
 (Callable[[float], int]) -> int
 (Optional[int]) -> None
 (Union[None, int, str]) -> None
-(Optional[foo.List[int]]) -> int
-(Union[foo.Set[int], foo.List[int]]) -> None
+(Optional[list[int]]) -> int
+(Union[set[int], list[int]]) -> None
 (Optional[int]) -> None
 (Optional[Any]) -> None
-(foo.Dict[int, int]) -> None
+(dict[int, int]) -> None
 (Tuple[int, int]) -> None
 ==
 

From 7a32bc1ab15777e71c42df10b87d7ea1bd0f0864 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 30 May 2025 20:21:27 +0200
Subject: [PATCH 1325/1617] Update test requirements (#19163)

---
 test-requirements.txt | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/test-requirements.txt b/test-requirements.txt
index 51281f0e4c11..bcdf02319306 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,64 +4,64 @@
 #
 #    pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in
 #
-attrs==25.1.0
+attrs==25.3.0
     # via -r test-requirements.in
 cfgv==3.4.0
     # via pre-commit
-coverage==7.6.10
+coverage==7.8.2
     # via pytest-cov
 distlib==0.3.9
     # via virtualenv
 execnet==2.1.1
     # via pytest-xdist
-filelock==3.17.0
+filelock==3.18.0
     # via
     #   -r test-requirements.in
     #   virtualenv
-identify==2.6.6
+identify==2.6.12
     # via pre-commit
-iniconfig==2.0.0
+iniconfig==2.1.0
     # via pytest
-lxml==5.3.0 ; python_version < "3.14"
+lxml==5.4.0 ; python_version < "3.14"
     # via -r test-requirements.in
-mypy-extensions==1.0.0
+mypy-extensions==1.1.0
     # via -r mypy-requirements.txt
 nodeenv==1.9.1
     # via pre-commit
-packaging==24.2
+packaging==25.0
     # via pytest
 pathspec==0.12.1
     # via -r mypy-requirements.txt
-platformdirs==4.3.6
+platformdirs==4.3.8
     # via virtualenv
-pluggy==1.5.0
+pluggy==1.6.0
     # via pytest
-pre-commit==4.1.0
+pre-commit==4.2.0
     # via -r test-requirements.in
-psutil==6.1.1
+psutil==7.0.0
     # via -r test-requirements.in
-pytest==8.3.4
+pytest==8.3.5
     # via
     #   -r test-requirements.in
     #   pytest-cov
     #   pytest-xdist
-pytest-cov==6.0.0
+pytest-cov==6.1.1
     # via -r test-requirements.in
-pytest-xdist==3.6.1
+pytest-xdist==3.7.0
     # via -r test-requirements.in
 pyyaml==6.0.2
     # via pre-commit
 tomli==2.2.1
     # via -r test-requirements.in
-types-psutil==6.1.0.20241221
+types-psutil==7.0.0.20250516
     # via -r build-requirements.txt
-types-setuptools==75.8.0.20250110
+types-setuptools==80.8.0.20250521
     # via -r build-requirements.txt
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via -r mypy-requirements.txt
-virtualenv==20.29.1
+virtualenv==20.31.2
     # via pre-commit
 
 # The following packages are considered to be unsafe in a requirements file:
-setuptools==75.8.0
+setuptools==80.9.0
     # via -r test-requirements.in

From 39570250e35151e387e6e1daf08b4f4e4262726e Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 30 May 2025 22:30:50 +0100
Subject: [PATCH 1326/1617] Use checkmember.py to check protocol subtyping
 (#18943)

Fixes https://github.com/python/mypy/issues/18024
Fixes https://github.com/python/mypy/issues/18706
Fixes https://github.com/python/mypy/issues/17734
Fixes https://github.com/python/mypy/issues/15097
Fixes https://github.com/python/mypy/issues/14814
Fixes https://github.com/python/mypy/issues/14806
Fixes https://github.com/python/mypy/issues/14259
Fixes https://github.com/python/mypy/issues/13041
Fixes https://github.com/python/mypy/issues/11993
Fixes https://github.com/python/mypy/issues/9585
Fixes https://github.com/python/mypy/issues/9266
Fixes https://github.com/python/mypy/issues/9202
Fixes https://github.com/python/mypy/issues/5481

This is a fourth "major" PR toward
https://github.com/python/mypy/issues/7724. This is one is
watershed/crux of the whole series (but to set correct expectations,
there are almost a dozen smaller follow-up/clean-up PRs in the
pipeline).

The core of the idea is to set current type-checker as part of the
global state. There are however some details:
* There are cases where we call `is_subtype()` before type-checking. For
now, I fall back to old logic in this cases. In follow up PRs we may
switch to using type-checker instances before type checking phase (this
requires some care).
* This increases typeops import cycle by a few modules, but
unfortunately this is inevitable.
* This PR increases potential for infinite recursion in protocols. To
mitigate I add: one legitimate fix for `__call__`, and one temporary
hack for `freshen_all_functions_type_vars` (to reduce performance
impact).
* Finally I change semantics for method access on class objects to match
the one in old `find_member()`. Now we will expand type by instance, so
we have something like this:
  ```python
  class B(Generic[T]):
      def foo(self, x: T) -> T: ...
  class C(B[str]): ...
  reveal_type(C.foo)  # def (self: B[str], x: str) -> str
  ```
FWIW, I am not even 100% sure this is correct, it seems to me we _may_
keep the method generic. But in any case what we do currently is
definitely wrong (we infer a _non-generic_ `def (x: T) -> T`).

---------

Co-authored-by: hauntsaninja 
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
---
 .github/workflows/mypy_primer.yml   |  1 +
 mypy/checker.py                     |  5 +-
 mypy/checker_state.py               | 30 +++++++++++
 mypy/checkmember.py                 | 56 +++++++++----------
 mypy/messages.py                    |  9 +++-
 mypy/plugin.py                      |  8 +--
 mypy/subtypes.py                    | 84 +++++++++++++++++++++++++++--
 test-data/unit/check-generics.test  | 15 ++++++
 test-data/unit/check-protocols.test | 45 ++++++++++++++++
 test-data/unit/check-python312.test |  3 +-
 test-data/unit/check-typeddict.test |  2 +-
 11 files changed, 213 insertions(+), 45 deletions(-)
 create mode 100644 mypy/checker_state.py

diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml
index ee868484751e..532e77a0cacb 100644
--- a/.github/workflows/mypy_primer.yml
+++ b/.github/workflows/mypy_primer.yml
@@ -67,6 +67,7 @@ jobs:
             --debug \
             --additional-flags="--debug-serialize" \
             --output concise \
+            --show-speed-regression \
             | tee diff_${{ matrix.shard-index }}.txt
           ) || [ $? -eq 1 ]
       - if: ${{ matrix.shard-index == 0 }}
diff --git a/mypy/checker.py b/mypy/checker.py
index 9c389cccd95f..2612bcc1defb 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -13,6 +13,7 @@
 from mypy import errorcodes as codes, join, message_registry, nodes, operators
 from mypy.binder import ConditionalTypeBinder, Frame, get_declaration
 from mypy.checker_shared import CheckerScope, TypeCheckerSharedApi, TypeRange
+from mypy.checker_state import checker_state
 from mypy.checkmember import (
     MemberContext,
     analyze_class_attribute_access,
@@ -453,7 +454,7 @@ def check_first_pass(self) -> None:
         Deferred functions will be processed by check_second_pass().
         """
         self.recurse_into_functions = True
-        with state.strict_optional_set(self.options.strict_optional):
+        with state.strict_optional_set(self.options.strict_optional), checker_state.set(self):
             self.errors.set_file(
                 self.path, self.tree.fullname, scope=self.tscope, options=self.options
             )
@@ -494,7 +495,7 @@ def check_second_pass(
         This goes through deferred nodes, returning True if there were any.
         """
         self.recurse_into_functions = True
-        with state.strict_optional_set(self.options.strict_optional):
+        with state.strict_optional_set(self.options.strict_optional), checker_state.set(self):
             if not todo and not self.deferred_nodes:
                 return False
             self.errors.set_file(
diff --git a/mypy/checker_state.py b/mypy/checker_state.py
new file mode 100644
index 000000000000..9b988ad18ba4
--- /dev/null
+++ b/mypy/checker_state.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from collections.abc import Iterator
+from contextlib import contextmanager
+from typing import Final
+
+from mypy.checker_shared import TypeCheckerSharedApi
+
+# This is global mutable state. Don't add anything here unless there's a very
+# good reason.
+
+
+class TypeCheckerState:
+    # Wrap this in a class since it's faster that using a module-level attribute.
+
+    def __init__(self, type_checker: TypeCheckerSharedApi | None) -> None:
+        # Value varies by file being processed
+        self.type_checker = type_checker
+
+    @contextmanager
+    def set(self, value: TypeCheckerSharedApi) -> Iterator[None]:
+        saved = self.type_checker
+        self.type_checker = value
+        try:
+            yield
+        finally:
+            self.type_checker = saved
+
+
+checker_state: Final = TypeCheckerState(type_checker=None)
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index cc104fed0752..b89452d90392 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -97,6 +97,7 @@ def __init__(
         is_self: bool = False,
         rvalue: Expression | None = None,
         suppress_errors: bool = False,
+        preserve_type_var_ids: bool = False,
     ) -> None:
         self.is_lvalue = is_lvalue
         self.is_super = is_super
@@ -113,6 +114,10 @@ def __init__(
             assert is_lvalue
         self.rvalue = rvalue
         self.suppress_errors = suppress_errors
+        # This attribute is only used to preserve old protocol member access logic.
+        # It is needed to avoid infinite recursion in cases involving self-referential
+        # generic methods, see find_member() for details. Do not use for other purposes!
+        self.preserve_type_var_ids = preserve_type_var_ids
 
     def named_type(self, name: str) -> Instance:
         return self.chk.named_type(name)
@@ -143,6 +148,7 @@ def copy_modified(
             no_deferral=self.no_deferral,
             rvalue=self.rvalue,
             suppress_errors=self.suppress_errors,
+            preserve_type_var_ids=self.preserve_type_var_ids,
         )
         if self_type is not None:
             mx.self_type = self_type
@@ -232,8 +238,6 @@ def analyze_member_access(
 def _analyze_member_access(
     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)
@@ -358,7 +362,8 @@ def analyze_instance_member_access(
                 return AnyType(TypeOfAny.special_form)
             assert isinstance(method.type, Overloaded)
             signature = method.type
-        signature = freshen_all_functions_type_vars(signature)
+        if not mx.preserve_type_var_ids:
+            signature = freshen_all_functions_type_vars(signature)
         if not method.is_static:
             if isinstance(method, (FuncDef, OverloadedFuncDef)) and method.is_trivial_self:
                 signature = bind_self_fast(signature, mx.self_type)
@@ -943,7 +948,8 @@ def analyze_var(
 def expand_without_binding(
     typ: Type, var: Var, itype: Instance, original_itype: Instance, mx: MemberContext
 ) -> Type:
-    typ = freshen_all_functions_type_vars(typ)
+    if not mx.preserve_type_var_ids:
+        typ = freshen_all_functions_type_vars(typ)
     typ = expand_self_type_if_needed(typ, mx, var, original_itype)
     expanded = expand_type_by_instance(typ, itype)
     freeze_all_type_vars(expanded)
@@ -958,7 +964,8 @@ def expand_and_bind_callable(
     mx: MemberContext,
     is_trivial_self: bool,
 ) -> Type:
-    functype = freshen_all_functions_type_vars(functype)
+    if not mx.preserve_type_var_ids:
+        functype = freshen_all_functions_type_vars(functype)
     typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
     assert isinstance(typ, FunctionLike)
     if is_trivial_self:
@@ -1056,10 +1063,12 @@ def f(self: S) -> T: ...
             return functype
         else:
             selfarg = get_proper_type(item.arg_types[0])
-            # This level of erasure matches the one in checker.check_func_def(),
-            # better keep these two checks consistent.
-            if subtypes.is_subtype(
+            # This matches similar special-casing in bind_self(), see more details there.
+            self_callable = name == "__call__" and isinstance(selfarg, CallableType)
+            if self_callable or subtypes.is_subtype(
                 dispatched_arg_type,
+                # This level of erasure matches the one in checker.check_func_def(),
+                # better keep these two checks consistent.
                 erase_typevars(erase_to_bound(selfarg)),
                 # This is to work around the fact that erased ParamSpec and TypeVarTuple
                 # callables are not always compatible with non-erased ones both ways.
@@ -1220,9 +1229,6 @@ def analyze_class_attribute_access(
         is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
             isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class
         )
-        is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
-            isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
-        )
         t = get_proper_type(t)
         is_trivial_self = False
         if isinstance(node.node, Decorator):
@@ -1236,8 +1242,7 @@ def analyze_class_attribute_access(
             t,
             isuper,
             is_classmethod,
-            is_staticmethod,
-            mx.self_type,
+            mx,
             original_vars=original_vars,
             is_trivial_self=is_trivial_self,
         )
@@ -1372,8 +1377,7 @@ def add_class_tvars(
     t: ProperType,
     isuper: Instance | None,
     is_classmethod: bool,
-    is_staticmethod: bool,
-    original_type: Type,
+    mx: MemberContext,
     original_vars: Sequence[TypeVarLikeType] | None = None,
     is_trivial_self: bool = False,
 ) -> Type:
@@ -1392,9 +1396,6 @@ class B(A[str]): pass
         isuper: Current instance mapped to the superclass where method was defined, this
             is usually done by map_instance_to_supertype()
         is_classmethod: True if this method is decorated with @classmethod
-        is_staticmethod: True if this method is decorated with @staticmethod
-        original_type: The value of the type B in the expression B.foo() or the corresponding
-            component in case of a union (this is used to bind the self-types)
         original_vars: Type variables of the class callable on which the method was accessed
         is_trivial_self: if True, we can use fast path for bind_self().
     Returns:
@@ -1416,14 +1417,14 @@ class B(A[str]): pass
     # (i.e. appear in the return type of the class object on which the method was accessed).
     if isinstance(t, CallableType):
         tvars = original_vars if original_vars is not None else []
-        t = freshen_all_functions_type_vars(t)
+        if not mx.preserve_type_var_ids:
+            t = freshen_all_functions_type_vars(t)
         if is_classmethod:
             if is_trivial_self:
-                t = bind_self_fast(t, original_type)
+                t = bind_self_fast(t, mx.self_type)
             else:
-                t = bind_self(t, original_type, is_classmethod=True)
-        if is_classmethod or is_staticmethod:
-            assert isuper is not None
+                t = bind_self(t, mx.self_type, is_classmethod=True)
+        if isuper is not None:
             t = expand_type_by_instance(t, isuper)
         freeze_all_type_vars(t)
         return t.copy_modified(variables=list(tvars) + list(t.variables))
@@ -1432,14 +1433,7 @@ class B(A[str]): pass
             [
                 cast(
                     CallableType,
-                    add_class_tvars(
-                        item,
-                        isuper,
-                        is_classmethod,
-                        is_staticmethod,
-                        original_type,
-                        original_vars=original_vars,
-                    ),
+                    add_class_tvars(item, isuper, is_classmethod, mx, original_vars=original_vars),
                 )
                 for item in t.items
             ]
diff --git a/mypy/messages.py b/mypy/messages.py
index 5457cea04a18..366c4a82fd98 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -2220,8 +2220,13 @@ def report_protocol_problems(
                 exp = get_proper_type(exp)
                 got = get_proper_type(got)
                 setter_suffix = " setter type" if is_lvalue else ""
-                if not isinstance(exp, (CallableType, Overloaded)) or not isinstance(
-                    got, (CallableType, Overloaded)
+                if (
+                    not isinstance(exp, (CallableType, Overloaded))
+                    or not isinstance(got, (CallableType, Overloaded))
+                    # If expected type is a type object, it means it is a nested class.
+                    # Showing constructor signature in errors would be confusing in this case,
+                    # since we don't check the signature, only subclassing of type objects.
+                    or exp.is_type_obj()
                 ):
                     self.note(
                         "{}: expected{} {}, got {}".format(
diff --git a/mypy/plugin.py b/mypy/plugin.py
index 39841d5b907a..de075866d613 100644
--- a/mypy/plugin.py
+++ b/mypy/plugin.py
@@ -119,14 +119,13 @@ class C: pass
 from __future__ import annotations
 
 from abc import abstractmethod
-from typing import Any, Callable, NamedTuple, TypeVar
+from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar
 
 from mypy_extensions import mypyc_attr, trait
 
 from mypy.errorcodes import ErrorCode
 from mypy.lookup import lookup_fully_qualified
 from mypy.message_registry import ErrorMessage
-from mypy.messages import MessageBuilder
 from mypy.nodes import (
     ArgKind,
     CallExpr,
@@ -138,7 +137,6 @@ class C: pass
     TypeInfo,
 )
 from mypy.options import Options
-from mypy.tvar_scope import TypeVarLikeScope
 from mypy.types import (
     CallableType,
     FunctionLike,
@@ -149,6 +147,10 @@ class C: pass
     UnboundType,
 )
 
+if TYPE_CHECKING:
+    from mypy.messages import MessageBuilder
+    from mypy.tvar_scope import TypeVarLikeScope
+
 
 @trait
 class TypeAnalyzerPluginInterface:
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 84fda7955d75..8d72e44d0eda 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -8,6 +8,7 @@
 import mypy.applytype
 import mypy.constraints
 import mypy.typeops
+from mypy.checker_state import checker_state
 from mypy.erasetype import erase_type
 from mypy.expandtype import (
     expand_self_type,
@@ -26,6 +27,7 @@
     COVARIANT,
     INVARIANT,
     VARIANCE_NOT_READY,
+    Context,
     Decorator,
     FuncBase,
     OverloadedFuncDef,
@@ -717,8 +719,7 @@ def visit_callable_type(self, left: CallableType) -> bool:
         elif isinstance(right, Instance):
             if right.type.is_protocol and "__call__" in right.type.protocol_members:
                 # OK, a callable can implement a protocol with a `__call__` member.
-                # TODO: we should probably explicitly exclude self-types in this case.
-                call = find_member("__call__", right, left, is_operator=True)
+                call = find_member("__call__", right, right, is_operator=True)
                 assert call is not None
                 if self._is_subtype(left, call):
                     if len(right.type.protocol_members) == 1:
@@ -954,7 +955,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
         if isinstance(right, Instance):
             if right.type.is_protocol and "__call__" in right.type.protocol_members:
                 # same as for CallableType
-                call = find_member("__call__", right, left, is_operator=True)
+                call = find_member("__call__", right, right, is_operator=True)
                 assert call is not None
                 if self._is_subtype(left, call):
                     if len(right.type.protocol_members) == 1:
@@ -1266,14 +1267,87 @@ def find_member(
     is_operator: bool = False,
     class_obj: bool = False,
     is_lvalue: bool = False,
+) -> Type | None:
+    type_checker = checker_state.type_checker
+    if type_checker is None:
+        # Unfortunately, there are many scenarios where someone calls is_subtype() before
+        # type checking phase. In this case we fallback to old (incomplete) logic.
+        # TODO: reduce number of such cases (e.g. semanal_typeargs, post-semanal plugins).
+        return find_member_simple(
+            name, itype, subtype, is_operator=is_operator, class_obj=class_obj, is_lvalue=is_lvalue
+        )
+
+    # We don't use ATTR_DEFINED error code below (since missing attributes can cause various
+    # other error codes), instead we perform quick node lookup with all the fallbacks.
+    info = itype.type
+    sym = info.get(name)
+    node = sym.node if sym else None
+    if not node:
+        name_not_found = True
+        if (
+            name not in ["__getattr__", "__setattr__", "__getattribute__"]
+            and not is_operator
+            and not class_obj
+            and itype.extra_attrs is None  # skip ModuleType.__getattr__
+        ):
+            for method_name in ("__getattribute__", "__getattr__"):
+                method = info.get_method(method_name)
+                if method and method.info.fullname != "builtins.object":
+                    name_not_found = False
+                    break
+        if name_not_found:
+            if info.fallback_to_any or class_obj and info.meta_fallback_to_any:
+                return AnyType(TypeOfAny.special_form)
+            if itype.extra_attrs and name in itype.extra_attrs.attrs:
+                return itype.extra_attrs.attrs[name]
+            return None
+
+    from mypy.checkmember import (
+        MemberContext,
+        analyze_class_attribute_access,
+        analyze_instance_member_access,
+    )
+
+    mx = MemberContext(
+        is_lvalue=is_lvalue,
+        is_super=False,
+        is_operator=is_operator,
+        original_type=itype,
+        self_type=subtype,
+        context=Context(),  # all errors are filtered, but this is a required argument
+        chk=type_checker,
+        suppress_errors=True,
+        # This is needed to avoid infinite recursion in situations involving protocols like
+        #     class P(Protocol[T]):
+        #         def combine(self, other: P[S]) -> P[Tuple[T, S]]: ...
+        # Normally we call freshen_all_functions_type_vars() during attribute access,
+        # to avoid type variable id collisions, but for protocols this means we can't
+        # use the assumption stack, that will grow indefinitely.
+        # TODO: find a cleaner solution that doesn't involve massive perf impact.
+        preserve_type_var_ids=True,
+    )
+    with type_checker.msg.filter_errors(filter_deprecated=True):
+        if class_obj:
+            fallback = itype.type.metaclass_type or mx.named_type("builtins.type")
+            return analyze_class_attribute_access(itype, name, mx, mcs_fallback=fallback)
+        else:
+            return analyze_instance_member_access(name, itype, mx, info)
+
+
+def find_member_simple(
+    name: str,
+    itype: Instance,
+    subtype: Type,
+    *,
+    is_operator: bool = False,
+    class_obj: bool = False,
+    is_lvalue: bool = False,
 ) -> Type | None:
     """Find the type of member by 'name' in 'itype's TypeInfo.
 
     Find the member type after applying type arguments from 'itype', and binding
     'self' to 'subtype'. Return None if member was not found.
     """
-    # TODO: this code shares some logic with checkmember.analyze_member_access,
-    # consider refactoring.
     info = itype.type
     method = info.get_method(name)
     if method:
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index dbc39d79d921..809c3c4eca48 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -3561,6 +3561,21 @@ def foo(x: T):
     reveal_type(C(0, x))  # N: Revealed type is "__main__.C[__main__.Int[T`-1]]"
     reveal_type(C("yes", x))  # N: Revealed type is "__main__.C[__main__.Str[T`-1]]"
 
+[case testInstanceMethodBoundOnClass]
+from typing import TypeVar, Generic
+
+T = TypeVar("T")
+class B(Generic[T]):
+    def foo(self) -> T: ...
+class C(B[T]): ...
+class D(C[int]): ...
+
+reveal_type(B.foo)  # N: Revealed type is "def [T] (self: __main__.B[T`1]) -> T`1"
+reveal_type(B[int].foo)  # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int"
+reveal_type(C.foo)  # N: Revealed type is "def [T] (self: __main__.B[T`1]) -> T`1"
+reveal_type(C[int].foo)  # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int"
+reveal_type(D.foo)  # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int"
+
 [case testDeterminismFromJoinOrderingInSolver]
 # Used to fail non-deterministically
 # https://github.com/python/mypy/issues/19121
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 7f11774fbfff..5e34d5223907 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4460,3 +4460,48 @@ f2(a4)  # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \
         # N:     foo: expected "B1", got "str" \
         # N:     foo: expected setter type "C1", got "str"
 [builtins fixtures/property.pyi]
+
+[case testProtocolImplementationWithDescriptors]
+from typing import Any, Protocol
+
+class Descr:
+    def __get__(self, inst: Any, owner: Any) -> int: ...
+
+class DescrBad:
+    def __get__(self, inst: Any, owner: Any) -> str: ...
+
+class Proto(Protocol):
+    x: int
+
+class C:
+    x = Descr()
+
+class CBad:
+    x = DescrBad()
+
+a: Proto = C()
+b: Proto = CBad()  # E: Incompatible types in assignment (expression has type "CBad", variable has type "Proto") \
+                   # N: Following member(s) of "CBad" have conflicts: \
+                   # N:     x: expected "int", got "str"
+
+[case testProtocolCheckDefersNode]
+from typing import Any, Callable, Protocol
+
+class Proto(Protocol):
+    def f(self) -> int:
+        ...
+
+def defer(f: Callable[[Any], int]) -> Callable[[Any], str]:
+    ...
+
+def bad() -> Proto:
+    return Impl()  # E: Incompatible return value type (got "Impl", expected "Proto") \
+                   # N: Following member(s) of "Impl" have conflicts: \
+                   # N:     Expected: \
+                   # N:         def f(self) -> int \
+                   # N:     Got: \
+                   # N:         def f() -> str \
+
+class Impl:
+    @defer
+    def f(self) -> int: ...
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 70ab59eb28e4..315c13ab762b 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -246,6 +246,7 @@ class Invariant[T]:
 inv1: Invariant[float] = Invariant[int]([1])  # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[float]")
 inv2: Invariant[int] = Invariant[float]([1])  # E: Incompatible types in assignment (expression has type "Invariant[float]", variable has type "Invariant[int]")
 [builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testPEP695InferVarianceCalculateOnDemand]
 class Covariant[T]:
@@ -1635,8 +1636,8 @@ class M[T: (int, str)](NamedTuple):
 c: M[int]
 d: M[str]
 e: M[bool]  # E: Value of type variable "T" of "M" cannot be "bool"
-
 [builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testPEP695GenericTypedDict]
 from typing import TypedDict
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index f9d7ce7fc975..4ac69321a250 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -2780,7 +2780,7 @@ class TD(TypedDict):
 
 reveal_type(TD.__iter__)  # N: Revealed type is "def (typing._TypedDict) -> typing.Iterator[builtins.str]"
 reveal_type(TD.__annotations__)  # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]"
-reveal_type(TD.values)  # N: Revealed type is "def (self: typing.Mapping[T`1, T_co`2]) -> typing.Iterable[T_co`2]"
+reveal_type(TD.values)  # N: Revealed type is "def (self: typing.Mapping[builtins.str, builtins.object]) -> typing.Iterable[builtins.object]"
 [builtins fixtures/dict-full.pyi]
 [typing fixtures/typing-typeddict.pyi]
 

From 057508b4cb405fbc22e26b44f764b142352fcce5 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 31 May 2025 00:42:34 +0200
Subject: [PATCH 1327/1617] Use PEP 604 syntax for TypeStrVisitor (#19179)

---
 mypy/types.py                    | 10 ++++++----
 test-data/unit/fine-grained.test |  4 ++--
 test-data/unit/pythoneval.test   | 12 ++++++------
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/mypy/types.py b/mypy/types.py
index 41a958ae93cc..5b8302de1ea1 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -3489,8 +3489,9 @@ def visit_literal_type(self, t: LiteralType, /) -> str:
         return f"Literal[{t.value_repr()}]"
 
     def visit_union_type(self, t: UnionType, /) -> str:
-        s = self.list_str(t.items)
-        return f"Union[{s}]"
+        use_or_syntax = self.options.use_or_syntax()
+        s = self.list_str(t.items, use_or_syntax=use_or_syntax)
+        return s if use_or_syntax else f"Union[{s}]"
 
     def visit_partial_type(self, t: PartialType, /) -> str:
         if t.type is None:
@@ -3523,14 +3524,15 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> str:
     def visit_unpack_type(self, t: UnpackType, /) -> str:
         return f"Unpack[{t.type.accept(self)}]"
 
-    def list_str(self, a: Iterable[Type]) -> str:
+    def list_str(self, a: Iterable[Type], *, use_or_syntax: bool = False) -> str:
         """Convert items of an array to strings (pretty-print types)
         and join the results with commas.
         """
         res = []
         for t in a:
             res.append(t.accept(self))
-        return ", ".join(res)
+        sep = ", " if not use_or_syntax else " | "
+        return sep.join(res)
 
 
 class TrivialSyntheticTypeTranslator(TypeTranslator, SyntheticTypeVisitor[Type]):
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 670ab42e1983..5df62c80168b 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -10436,14 +10436,14 @@ D = "y"
 C = str
 D = int
 [out]
-a.py:4: note: Revealed type is "Union[builtins.int, builtins.str]"
+a.py:4: note: Revealed type is "builtins.int | builtins.str"
 ==
 a.py:2: error: Unsupported left operand type for | ("str")
 a.py:3: error: Variable "a.A" is not valid as a type
 a.py:3: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
 a.py:4: note: Revealed type is "A?"
 ==
-a.py:4: note: Revealed type is "Union[builtins.str, builtins.int]"
+a.py:4: note: Revealed type is "builtins.str | builtins.int"
 
 [case testUnionOfSimilarCallablesCrash]
 import b
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 081d21f14857..3cd509d44290 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1632,8 +1632,8 @@ def foo(x: T) -> T:
     return x
 [out]
 _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"
-_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[type[builtins.int], builtins.str]"
-_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[type[builtins.int], builtins.str]"
+_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "type[builtins.int] | builtins.str"
+_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "type[builtins.int] | builtins.str"
 
 [case testTypeAliasWithNewStyleUnionInStub]
 import m
@@ -1711,7 +1711,7 @@ reveal_type(e.foo)
 reveal_type(E.Y.foo)
 [out]
 _testEnumNameWorkCorrectlyOn311.py:11: note: Revealed type is "builtins.str"
-_testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Union[Literal[1]?, Literal[2]?]"
+_testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Literal[1]? | Literal[2]?"
 _testEnumNameWorkCorrectlyOn311.py:13: note: Revealed type is "Literal['X']?"
 _testEnumNameWorkCorrectlyOn311.py:14: note: Revealed type is "builtins.int"
 _testEnumNameWorkCorrectlyOn311.py:15: note: Revealed type is "builtins.int"
@@ -1780,9 +1780,9 @@ WrongEllipsis = tuple[float, float, ...] | str  # Error
 
 reveal_type(tuple[int, str]((1, "x")))
 [out]
-_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "Union[builtins.str, tuple[builtins.float, builtins.float, builtins.str]]"
-_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "Union[tuple[builtins.float], builtins.str]"
-_testTupleWithDifferentArgsPy310.py:17: note: Revealed type is "Union[builtins.tuple[builtins.float, ...], builtins.str]"
+_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "builtins.str | tuple[builtins.float, builtins.float, builtins.str]"
+_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "tuple[builtins.float] | builtins.str"
+_testTupleWithDifferentArgsPy310.py:17: note: Revealed type is "builtins.tuple[builtins.float, ...] | builtins.str"
 _testTupleWithDifferentArgsPy310.py:18: note: Revealed type is "tuple[builtins.float, builtins.str]"
 _testTupleWithDifferentArgsPy310.py:19: note: Revealed type is "builtins.tuple[builtins.float, ...]"
 _testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"

From 68233f6ff328f35c7ac26a7efdd325ae1248468b Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 31 May 2025 05:10:48 +0200
Subject: [PATCH 1328/1617] Include walrus assignments in conditional inference
 (#19038)

Fixes #19036.
---
 mypy/checker.py                     | 23 +++++++-
 test-data/unit/check-inference.test | 92 +++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 2612bcc1defb..e83473492f01 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6513,7 +6513,7 @@ def refine_parent_types(self, expr: Expression, expr_type: Type) -> Mapping[Expr
             # and create function that will try replaying the same lookup
             # operation against arbitrary types.
             if isinstance(expr, MemberExpr):
-                parent_expr = collapse_walrus(expr.expr)
+                parent_expr = self._propagate_walrus_assignments(expr.expr, output)
                 parent_type = self.lookup_type_or_none(parent_expr)
                 member_name = expr.name
 
@@ -6536,9 +6536,10 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None:
                         return member_type
 
             elif isinstance(expr, IndexExpr):
-                parent_expr = collapse_walrus(expr.base)
+                parent_expr = self._propagate_walrus_assignments(expr.base, output)
                 parent_type = self.lookup_type_or_none(parent_expr)
 
+                self._propagate_walrus_assignments(expr.index, output)
                 index_type = self.lookup_type_or_none(expr.index)
                 if index_type is None:
                     return output
@@ -6612,6 +6613,24 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None:
             expr = parent_expr
             expr_type = output[parent_expr] = make_simplified_union(new_parent_types)
 
+    def _propagate_walrus_assignments(
+        self, expr: Expression, type_map: dict[Expression, Type]
+    ) -> Expression:
+        """Add assignments from walrus expressions to inferred types.
+
+        Only considers nested assignment exprs, does not recurse into other types.
+        This may be added later if necessary by implementing a dedicated visitor.
+        """
+        if isinstance(expr, AssignmentExpr):
+            if isinstance(expr.value, AssignmentExpr):
+                self._propagate_walrus_assignments(expr.value, type_map)
+            assigned_type = self.lookup_type_or_none(expr.value)
+            parent_expr = collapse_walrus(expr)
+            if assigned_type is not None:
+                type_map[parent_expr] = assigned_type
+            return parent_expr
+        return expr
+
     def refine_identity_comparison_expression(
         self,
         operands: list[Expression],
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index a98597e6e320..381f73ed9862 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -3979,3 +3979,95 @@ def check(mapping: Mapping[str, _T]) -> None:
     reveal_type(ok1)  # N: Revealed type is "Union[_T`-1, builtins.str]"
     ok2: Union[_T, str] = mapping.get("", "")
 [builtins fixtures/tuple.pyi]
+
+[case testInferWalrusAssignmentAttrInCondition]
+class Foo:
+    def __init__(self, value: bool) -> None:
+        self.value = value
+
+def check_and(maybe: bool) -> None:
+    foo = None
+    if maybe and (foo := Foo(True)).value:
+        reveal_type(foo)  # N: Revealed type is "__main__.Foo"
+    else:
+        reveal_type(foo)  # N: Revealed type is "Union[__main__.Foo, None]"
+
+def check_and_nested(maybe: bool) -> None:
+    foo = None
+    bar = None
+    baz = None
+    if maybe and (foo := (bar := (baz := Foo(True)))).value:
+        reveal_type(foo)  # N: Revealed type is "__main__.Foo"
+        reveal_type(bar)  # N: Revealed type is "__main__.Foo"
+        reveal_type(baz)  # N: Revealed type is "__main__.Foo"
+    else:
+        reveal_type(foo)  # N: Revealed type is "Union[__main__.Foo, None]"
+        reveal_type(bar)  # N: Revealed type is "Union[__main__.Foo, None]"
+        reveal_type(baz)  # N: Revealed type is "Union[__main__.Foo, None]"
+
+def check_or(maybe: bool) -> None:
+    foo = None
+    if maybe or (foo := Foo(True)).value:
+        reveal_type(foo)  # N: Revealed type is "Union[__main__.Foo, None]"
+    else:
+        reveal_type(foo)  # N: Revealed type is "__main__.Foo"
+
+def check_or_nested(maybe: bool) -> None:
+    foo = None
+    bar = None
+    baz = None
+    if maybe and (foo := (bar := (baz := Foo(True)))).value:
+        reveal_type(foo)  # N: Revealed type is "__main__.Foo"
+        reveal_type(bar)  # N: Revealed type is "__main__.Foo"
+        reveal_type(baz)  # N: Revealed type is "__main__.Foo"
+    else:
+        reveal_type(foo)  # N: Revealed type is "Union[__main__.Foo, None]"
+        reveal_type(bar)  # N: Revealed type is "Union[__main__.Foo, None]"
+        reveal_type(baz)  # N: Revealed type is "Union[__main__.Foo, None]"
+
+[case testInferWalrusAssignmentIndexInCondition]
+def check_and(maybe: bool) -> None:
+    foo = None
+    bar = None
+    if maybe and (foo := [1])[(bar := 0)]:
+        reveal_type(foo)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(bar)  # N: Revealed type is "builtins.int"
+    else:
+        reveal_type(foo)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(bar)  # N: Revealed type is "Union[builtins.int, None]"
+
+def check_and_nested(maybe: bool) -> None:
+    foo = None
+    bar = None
+    baz = None
+    if maybe and (foo := (bar := (baz := [1])))[0]:
+        reveal_type(foo)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(bar)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(baz)  # N: Revealed type is "builtins.list[builtins.int]"
+    else:
+        reveal_type(foo)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(bar)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(baz)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+
+def check_or(maybe: bool) -> None:
+    foo = None
+    bar = None
+    if maybe or (foo := [1])[(bar := 0)]:
+        reveal_type(foo)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(bar)  # N: Revealed type is "Union[builtins.int, None]"
+    else:
+        reveal_type(foo)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(bar)  # N: Revealed type is "builtins.int"
+
+def check_or_nested(maybe: bool) -> None:
+    foo = None
+    bar = None
+    baz = None
+    if maybe or (foo := (bar := (baz := [1])))[0]:
+        reveal_type(foo)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(bar)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+        reveal_type(baz)  # N: Revealed type is "Union[builtins.list[builtins.int], None]"
+    else:
+        reveal_type(foo)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(bar)  # N: Revealed type is "builtins.list[builtins.int]"
+        reveal_type(baz)  # N: Revealed type is "builtins.list[builtins.int]"

From a16521f719d2be91d470b23959c6f1429206d4a4 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 1 Jun 2025 02:07:23 +0100
Subject: [PATCH 1329/1617] Infer constraints eagerly if actual is Any (#19190)

Fixes https://github.com/python/mypy/issues/8829

This case is more common in 1.16 due to some changes in binder, so it
would be good to fix it. My fix may be a bit naive, but if `mypy_primer`
looks good, I think it should be OK.
---
 mypy/constraints.py                 |  2 +-
 test-data/unit/check-inference.test | 10 ++++++++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/mypy/constraints.py b/mypy/constraints.py
index 8e7a30e05ffb..b1f3a8b180e1 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -416,7 +416,7 @@ def _infer_constraints(
                 infer_constraints_if_possible(t_item, actual, direction)
                 for t_item in template.items
             ],
-            eager=False,
+            eager=isinstance(actual, AnyType),
         )
         if result:
             return result
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 381f73ed9862..4ae5ddb00b18 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -4071,3 +4071,13 @@ def check_or_nested(maybe: bool) -> None:
         reveal_type(foo)  # N: Revealed type is "builtins.list[builtins.int]"
         reveal_type(bar)  # N: Revealed type is "builtins.list[builtins.int]"
         reveal_type(baz)  # N: Revealed type is "builtins.list[builtins.int]"
+
+[case testInferOptionalAgainstAny]
+from typing import Any, Optional, TypeVar
+
+a: Any
+oa: Optional[Any]
+T = TypeVar("T")
+def f(x: Optional[T]) -> T: ...
+reveal_type(f(a))  # N: Revealed type is "Any"
+reveal_type(f(oa))  # N: Revealed type is "Any"

From 0ea84886e5a6decdb46b5bae93c4452a0a6ca6bb Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Sat, 31 May 2025 18:08:40 -0700
Subject: [PATCH 1330/1617] Fix nondeterministic type checking by making join
 with explicit Protocol and type promotion commute (#18402)

Fixes #16979 (bzoracler case only, OP case fixed by #19147)

See https://github.com/python/mypy/issues/16979#issuecomment-1982283536
---
 mypy/join.py                        | 21 +++++++++++--
 test-data/unit/check-inference.test | 47 +++++++++++++++++++++++++++++
 test-data/unit/check-protocols.test | 24 +++++++++++++++
 3 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/mypy/join.py b/mypy/join.py
index fcfc6cbaa0e7..65cc3bef66a4 100644
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -8,7 +8,7 @@
 import mypy.typeops
 from mypy.expandtype import expand_type
 from mypy.maptype import map_instance_to_supertype
-from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY
+from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY, TypeInfo
 from mypy.state import state
 from mypy.subtypes import (
     SubtypeContext,
@@ -168,9 +168,20 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType:
         # Compute the "best" supertype of t when joined with s.
         # The definition of "best" may evolve; for now it is the one with
         # the longest MRO.  Ties are broken by using the earlier base.
-        best: ProperType | None = None
+
+        # Go over both sets of bases in case there's an explicit Protocol base. This is important
+        # to ensure commutativity of join (although in cases where both classes have relevant
+        # Protocol bases this maybe might still not be commutative)
+        base_types: dict[TypeInfo, None] = {}  # dict to deduplicate but preserve order
         for base in t.type.bases:
-            mapped = map_instance_to_supertype(t, base.type)
+            base_types[base.type] = None
+        for base in s.type.bases:
+            if base.type.is_protocol and is_subtype(t, base):
+                base_types[base.type] = None
+
+        best: ProperType | None = None
+        for base_type in base_types:
+            mapped = map_instance_to_supertype(t, base_type)
             res = self.join_instances(mapped, s)
             if best is None or is_better(res, best):
                 best = res
@@ -662,6 +673,10 @@ def is_better(t: Type, s: Type) -> bool:
     if isinstance(t, Instance):
         if not isinstance(s, Instance):
             return True
+        if t.type.is_protocol != s.type.is_protocol:
+            if t.type.fullname != "builtins.object" and s.type.fullname != "builtins.object":
+                # mro of protocol is not really relevant
+                return not t.type.is_protocol
         # Use len(mro) as a proxy for the better choice.
         if len(t.type.mro) > len(s.type.mro):
             return True
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 4ae5ddb00b18..4a3930533954 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -3888,6 +3888,53 @@ def a4(x: List[str], y: List[Never]) -> None:
     z1[1].append("asdf")  # E: "object" has no attribute "append"
 [builtins fixtures/dict.pyi]
 
+
+[case testDeterminismCommutativityWithJoinInvolvingProtocolBaseAndPromotableType]
+# flags: --python-version 3.11
+# Regression test for https://github.com/python/mypy/issues/16979#issuecomment-1982246306
+from __future__ import annotations
+
+from typing import Any, Generic, Protocol, TypeVar, overload, cast
+from typing_extensions import Never
+
+T = TypeVar("T")
+U = TypeVar("U")
+
+class _SupportsCompare(Protocol):
+    def __lt__(self, other: Any, /) -> bool:
+        return True
+
+class Comparable(_SupportsCompare):
+    pass
+
+comparable: Comparable = Comparable()
+
+from typing import _promote
+
+class floatlike:
+    def __lt__(self, other: floatlike, /) -> bool: ...
+
+@_promote(floatlike)
+class intlike:
+    def __lt__(self, other: intlike, /) -> bool: ...
+
+
+class A(Generic[T, U]):
+    @overload
+    def __init__(self: A[T, T], a: T, b: T, /) -> None: ...  # type: ignore[overload-overlap]
+    @overload
+    def __init__(self: A[T, U], a: T, b: U, /) -> Never: ...
+    def __init__(self, *a) -> None: ...
+
+def join(a: T, b: T) -> T: ...
+
+reveal_type(join(intlike(), comparable))  # N: Revealed type is "__main__._SupportsCompare"
+reveal_type(join(comparable, intlike()))  # N: Revealed type is "__main__._SupportsCompare"
+reveal_type(A(intlike(), comparable))  # N: Revealed type is "__main__.A[__main__._SupportsCompare, __main__._SupportsCompare]"
+reveal_type(A(comparable, intlike()))  # N: Revealed type is "__main__.A[__main__._SupportsCompare, __main__._SupportsCompare]"
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-medium.pyi]
+
 [case testTupleJoinFallbackInference]
 foo = [
     (1, ("a", "b")),
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 5e34d5223907..934f48a5e9c3 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4461,6 +4461,30 @@ f2(a4)  # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \
         # N:     foo: expected setter type "C1", got "str"
 [builtins fixtures/property.pyi]
 
+
+[case testExplicitProtocolJoinPreference]
+from typing import Protocol, TypeVar
+
+T = TypeVar("T")
+
+class Proto1(Protocol):
+    def foo(self) -> int: ...
+class Proto2(Proto1):
+    def bar(self) -> str: ...
+class Proto3(Proto2):
+    def baz(self) -> str: ...
+
+class Base: ...
+
+class A(Base, Proto3): ...
+class B(Base, Proto3): ...
+
+def join(a: T, b: T) -> T: ...
+
+def main(a: A, b: B) -> None:
+    reveal_type(join(a, b))  # N: Revealed type is "__main__.Proto3"
+    reveal_type(join(b, a))  # N: Revealed type is "__main__.Proto3"
+
 [case testProtocolImplementationWithDescriptors]
 from typing import Any, Protocol
 

From 5fbfff97f2197b38321363f4d294f47009a28139 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 1 Jun 2025 02:30:37 +0100
Subject: [PATCH 1331/1617] Tighten metaclass __call__ handling in protocols
 (#19191)

Fixes https://github.com/python/mypy/issues/19184

This fixes an (edge-case) regression introduced in 1.16. Fix is
straightforward, only ignore `__call__` if it comes from an _actual_
metaclass.
---
 mypy/constraints.py                 |  4 ++--
 mypy/nodes.py                       |  4 ++--
 mypy/typeops.py                     |  2 +-
 test-data/unit/check-protocols.test | 22 ++++++++++++++++++++++
 4 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/mypy/constraints.py b/mypy/constraints.py
index b1f3a8b180e1..293618556203 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -1066,8 +1066,8 @@ def infer_constraints_from_protocol_members(
                     inst, erase_typevars(temp), ignore_pos_arg_names=True
                 ):
                     continue
-            # This exception matches the one in subtypes.py, see PR #14121 for context.
-            if member == "__call__" and instance.type.is_metaclass():
+            # This exception matches the one in typeops.py, see PR #14121 for context.
+            if member == "__call__" and instance.type.is_metaclass(precise=True):
                 continue
             res.extend(infer_constraints(temp, inst, self.direction))
             if mypy.subtypes.IS_SETTABLE in mypy.subtypes.get_member_flags(member, protocol):
diff --git a/mypy/nodes.py b/mypy/nodes.py
index fae0bb1cc61f..7db32240c33e 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3359,11 +3359,11 @@ def calculate_metaclass_type(self) -> mypy.types.Instance | None:
                 return c
         return None
 
-    def is_metaclass(self) -> bool:
+    def is_metaclass(self, *, precise: bool = False) -> bool:
         return (
             self.has_base("builtins.type")
             or self.fullname == "abc.ABCMeta"
-            or self.fallback_to_any
+            or (self.fallback_to_any and not precise)
         )
 
     def has_base(self, fullname: str) -> bool:
diff --git a/mypy/typeops.py b/mypy/typeops.py
index bcf946900563..3715081ae173 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -1257,7 +1257,7 @@ def named_type(fullname: str) -> Instance:
 
         return type_object_type(left.type, named_type)
 
-    if member == "__call__" and left.type.is_metaclass():
+    if member == "__call__" and left.type.is_metaclass(precise=True):
         # Special case: we want to avoid falling back to metaclass __call__
         # if constructor signature didn't match, this can cause many false negatives.
         return None
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 934f48a5e9c3..f2b8fc7a0e14 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4529,3 +4529,25 @@ def bad() -> Proto:
 class Impl:
     @defer
     def f(self) -> int: ...
+
+[case testInferCallableProtoWithAnySubclass]
+from typing import Any, Generic, Protocol, TypeVar
+
+T = TypeVar("T", covariant=True)
+
+Unknown: Any
+class Mock(Unknown):
+    def __init__(self, **kwargs: Any) -> None: ...
+    def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
+
+class Factory(Protocol[T]):
+    def __call__(self, **kwargs: Any) -> T: ...
+
+
+class Test(Generic[T]):
+    def __init__(self, f: Factory[T]) -> None:
+        ...
+
+t = Test(Mock())
+reveal_type(t)  # N: Revealed type is "__main__.Test[Any]"
+[builtins fixtures/dict.pyi]

From 21d30904eb4d97a897588f95f58c7d56a07b1c7f Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sun, 1 Jun 2025 19:31:38 +0200
Subject: [PATCH 1332/1617] Further cleanup after dropping Python 3.8 (#19197)

---
 mypy/checkexpr.py                         |  2 +-
 mypy/semanal.py                           | 17 ++---------------
 mypy/typeanal.py                          | 12 ++++--------
 mypy/types.py                             |  3 +++
 mypyc/irbuild/classdef.py                 |  4 ++--
 test-data/unit/check-selftype.test        |  2 --
 test-data/unit/check-union-or-syntax.test |  8 ++------
 7 files changed, 14 insertions(+), 34 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index ace8f09bee48..fc0acf55be19 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4420,7 +4420,7 @@ def visit_index_with_type(
         elif isinstance(left_type, FunctionLike) and left_type.is_type_obj():
             if left_type.type_object().is_enum:
                 return self.visit_enum_index_expr(left_type.type_object(), e.index, e)
-            elif self.chk.options.python_version >= (3, 9) and (
+            elif (
                 left_type.type_object().type_vars
                 or left_type.type_object().fullname == "builtins.type"
             ):
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 855c279756e8..5cd58966f619 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -265,6 +265,7 @@
     TPDICT_NAMES,
     TYPE_ALIAS_NAMES,
     TYPE_CHECK_ONLY_NAMES,
+    TYPE_NAMES,
     TYPE_VAR_LIKE_NAMES,
     TYPED_NAMEDTUPLE_NAMES,
     UNPACK_TYPE_NAMES,
@@ -1116,21 +1117,7 @@ def is_expected_self_type(self, typ: Type, is_classmethod: bool) -> bool:
                 return self.is_expected_self_type(typ.item, is_classmethod=False)
             if isinstance(typ, UnboundType):
                 sym = self.lookup_qualified(typ.name, typ, suppress_errors=True)
-                if (
-                    sym is not None
-                    and (
-                        sym.fullname == "typing.Type"
-                        or (
-                            sym.fullname == "builtins.type"
-                            and (
-                                self.is_stub_file
-                                or self.is_future_flag_set("annotations")
-                                or self.options.python_version >= (3, 9)
-                            )
-                        )
-                    )
-                    and typ.args
-                ):
+                if sym is not None and sym.fullname in TYPE_NAMES and typ.args:
                     return self.is_expected_self_type(typ.args[0], is_classmethod=False)
             return False
         if isinstance(typ, TypeVarType):
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index 40e62e04740d..a8d5f1b304fe 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -65,7 +65,9 @@
     FINAL_TYPE_NAMES,
     LITERAL_TYPE_NAMES,
     NEVER_NAMES,
+    TUPLE_NAMES,
     TYPE_ALIAS_NAMES,
+    TYPE_NAMES,
     UNPACK_TYPE_NAMES,
     AnyType,
     BoolTypeQuery,
@@ -607,10 +609,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
                         code=codes.VALID_TYPE,
                     )
             return AnyType(TypeOfAny.from_error)
-        elif fullname == "typing.Tuple" or (
-            fullname == "builtins.tuple"
-            and (self.always_allow_new_syntax or self.options.python_version >= (3, 9))
-        ):
+        elif fullname in TUPLE_NAMES:
             # Tuple is special because it is involved in builtin import cycle
             # and may be not ready when used.
             sym = self.api.lookup_fully_qualified_or_none("builtins.tuple")
@@ -645,10 +644,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
             return make_optional_type(item)
         elif fullname == "typing.Callable":
             return self.analyze_callable_type(t)
-        elif fullname == "typing.Type" or (
-            fullname == "builtins.type"
-            and (self.always_allow_new_syntax or self.options.python_version >= (3, 9))
-        ):
+        elif fullname in TYPE_NAMES:
             if len(t.args) == 0:
                 if fullname == "typing.Type":
                     any_type = self.get_omitted_any(t)
diff --git a/mypy/types.py b/mypy/types.py
index 5b8302de1ea1..d2094cd15774 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -84,6 +84,9 @@
         TypeVisitor as TypeVisitor,
     )
 
+TUPLE_NAMES: Final = ("builtins.tuple", "typing.Tuple")
+TYPE_NAMES: Final = ("builtins.type", "typing.Type")
+
 TYPE_VAR_LIKE_NAMES: Final = (
     "typing.TypeVar",
     "typing_extensions.TypeVar",
diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 1e53df92fcfe..13121707773a 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -564,11 +564,11 @@ def find_non_ext_metaclass(builder: IRBuilder, cdef: ClassDef, bases: Value) ->
     if cdef.metaclass:
         declared_metaclass = builder.accept(cdef.metaclass)
     else:
-        if cdef.info.typeddict_type is not None and builder.options.capi_version >= (3, 9):
+        if cdef.info.typeddict_type is not None:
             # In Python 3.9, the metaclass for class-based TypedDict is typing._TypedDictMeta.
             # We can't easily calculate it generically, so special case it.
             return builder.get_module_attr("typing", "_TypedDictMeta", cdef.line)
-        elif cdef.info.is_named_tuple and builder.options.capi_version >= (3, 9):
+        elif cdef.info.is_named_tuple:
             # In Python 3.9, the metaclass for class-based NamedTuple is typing.NamedTupleMeta.
             # We can't easily calculate it generically, so special case it.
             return builder.get_module_attr("typing", "NamedTupleMeta", cdef.line)
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 5f337f773e6f..cb7e5a9fac71 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -1707,7 +1707,6 @@ class C:
 [builtins fixtures/classmethod.pyi]
 
 [case testTypingSelfRedundantAllowed_pep585]
-# flags: --python-version 3.9
 from typing import Self
 
 class C:
@@ -1742,7 +1741,6 @@ class C:
 [builtins fixtures/classmethod.pyi]
 
 [case testTypingSelfRedundantWarning_pep585]
-# flags: --python-version 3.9
 # mypy: enable-error-code="redundant-self"
 
 from typing import Self
diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test
index 924c12658851..35af44c62800 100644
--- a/test-data/unit/check-union-or-syntax.test
+++ b/test-data/unit/check-union-or-syntax.test
@@ -67,8 +67,7 @@ x: List[int | str]
 reveal_type(x)  # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]"
 [builtins fixtures/list.pyi]
 
-[case testUnionOrSyntaxWithQuotedFunctionTypesPre310]
-# flags: --python-version 3.9
+[case testUnionOrSyntaxWithQuotedFunctionTypes]
 from typing import Union
 def f(x: 'Union[int, str, None]') -> 'Union[int, None]':
     reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, None]"
@@ -80,8 +79,7 @@ def g(x: "int | str | None") -> "int | None":
     return 42
 reveal_type(g)  # N: Revealed type is "def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]"
 
-[case testUnionOrSyntaxWithQuotedVariableTypesPre310]
-# flags: --python-version 3.9
+[case testUnionOrSyntaxWithQuotedVariableTypes]
 y: "int | str" = 42
 reveal_type(y)  # N: Revealed type is "Union[builtins.int, builtins.str]"
 
@@ -137,7 +135,6 @@ x: int | None
 x: int | None  # E: X | Y syntax for unions requires Python 3.10
 
 [case testUnionOrSyntaxInStubFile]
-# flags: --python-version 3.9
 from lib import x
 [file lib.pyi]
 x: int | None
@@ -187,7 +184,6 @@ def g(x: int | str | tuple[int, str] | C) -> None:
 [builtins fixtures/isinstance_python3_10.pyi]
 
 [case testUnionOrSyntaxInIsinstanceNotSupported]
-# flags: --python-version 3.9
 from typing import Union
 def f(x: Union[int, str, None]) -> None:
     if isinstance(x, int | str):

From 04afa499f936f671ee3f5a53edf3f5c8df18e76c Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sun, 1 Jun 2025 19:32:07 +0200
Subject: [PATCH 1333/1617] Sync typeshed (#19194)

Source commit:

https://github.com/python/typeshed/commit/5a3c495d2f6fa9b68cd99f39feba4426e4d17ea9

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: AlexWaygood 
---
 mypy/fastparse.py                             |   1 -
 mypy/typeshed/stdlib/_asyncio.pyi             |   1 +
 mypy/typeshed/stdlib/_ctypes.pyi              |   2 +
 mypy/typeshed/stdlib/_curses.pyi              |   3 +
 mypy/typeshed/stdlib/_heapq.pyi               |  12 +-
 mypy/typeshed/stdlib/_imp.pyi                 |   2 +
 mypy/typeshed/stdlib/_posixsubprocess.pyi     |  79 ++++++++-----
 mypy/typeshed/stdlib/_thread.pyi              |   5 +
 mypy/typeshed/stdlib/_tkinter.pyi             |   2 +-
 mypy/typeshed/stdlib/_typeshed/__init__.pyi   |   3 -
 mypy/typeshed/stdlib/argparse.pyi             |   8 +-
 mypy/typeshed/stdlib/ast.pyi                  |  36 ++++--
 mypy/typeshed/stdlib/asyncio/__init__.pyi     |  16 ++-
 mypy/typeshed/stdlib/asyncio/events.pyi       |  38 ++++--
 mypy/typeshed/stdlib/asyncio/unix_events.pyi  |  31 ++---
 .../stdlib/asyncio/windows_events.pyi         |  38 ++++--
 mypy/typeshed/stdlib/builtins.pyi             |  33 ++++--
 .../stdlib/concurrent/futures/interpreter.pyi |   4 +-
 mypy/typeshed/stdlib/ctypes/__init__.pyi      |  20 +++-
 mypy/typeshed/stdlib/ctypes/util.pyi          |   3 +
 mypy/typeshed/stdlib/ctypes/wintypes.pyi      |   9 ++
 mypy/typeshed/stdlib/dataclasses.pyi          |  55 ++++++++-
 mypy/typeshed/stdlib/enum.pyi                 |   6 +-
 mypy/typeshed/stdlib/errno.pyi                |   3 +
 mypy/typeshed/stdlib/faulthandler.pyi         |   4 +
 mypy/typeshed/stdlib/importlib/abc.pyi        | 110 ++++++++++--------
 mypy/typeshed/stdlib/importlib/machinery.pyi  |  23 ++++
 .../stdlib/importlib/resources/__init__.pyi   |  10 +-
 .../stdlib/importlib/resources/_common.pyi    |   2 +-
 .../stdlib/importlib/resources/abc.pyi        |  73 ++++++++++--
 mypy/typeshed/stdlib/importlib/util.pyi       |  24 +++-
 mypy/typeshed/stdlib/logging/__init__.pyi     |   3 +
 .../stdlib/multiprocessing/forkserver.pyi     |  28 +++--
 .../stdlib/multiprocessing/managers.pyi       |  63 +++++++++-
 .../stdlib/multiprocessing/popen_fork.pyi     |   3 +
 .../stdlib/multiprocessing/reduction.pyi      |   3 +-
 mypy/typeshed/stdlib/multiprocessing/util.pyi |  20 +++-
 mypy/typeshed/stdlib/pyexpat/errors.pyi       |   2 +
 mypy/typeshed/stdlib/select.pyi               |   2 +
 mypy/typeshed/stdlib/shutil.pyi               |  11 +-
 mypy/typeshed/stdlib/socketserver.pyi         |   5 +-
 mypy/typeshed/stdlib/sre_constants.pyi        |   2 +
 mypy/typeshed/stdlib/string/__init__.pyi      |   7 +-
 mypy/typeshed/stdlib/string/templatelib.pyi   |   3 +
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |   2 +-
 mypy/typeshed/stdlib/turtle.pyi               |  39 ++++++-
 mypy/typeshed/stdlib/types.pyi                |  11 +-
 mypy/typeshed/stdlib/typing.pyi               |  10 +-
 mypy/typeshed/stdlib/typing_extensions.pyi    |  29 ++++-
 mypy/typeshed/stdlib/xml/sax/__init__.pyi     |  19 ++-
 mypy/typeshed/stdlib/zipfile/__init__.pyi     |  25 +++-
 mypy/typeshed/stdlib/zipimport.pyi            |  14 ++-
 52 files changed, 741 insertions(+), 216 deletions(-)

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index 6c59f44829bb..e2af2198cdfd 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -2060,7 +2060,6 @@ def visit_Constant(self, n: Constant) -> Type:
             contents = bytes_to_human_readable_repr(val)
             return RawExpressionType(contents, "builtins.bytes", self.line, column=n.col_offset)
         # Everything else is invalid.
-        return self.invalid_type(n)
 
     # UnaryOp(op, operand)
     def visit_UnaryOp(self, n: UnaryOp) -> Type:
diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
index 19a2d12d878c..5253e967e5a3 100644
--- a/mypy/typeshed/stdlib/_asyncio.pyi
+++ b/mypy/typeshed/stdlib/_asyncio.pyi
@@ -107,3 +107,4 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 14):
     def future_discard_from_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ...
     def future_add_to_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ...
+    def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ...
diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi
index dc8c7b2ca945..e134066f0bcf 100644
--- a/mypy/typeshed/stdlib/_ctypes.pyi
+++ b/mypy/typeshed/stdlib/_ctypes.pyi
@@ -75,6 +75,8 @@ class _CData:
     _objects: Mapping[Any, int] | None
     def __buffer__(self, flags: int, /) -> memoryview: ...
     def __ctypes_from_outparam__(self, /) -> Self: ...
+    if sys.version_info >= (3, 14):
+        __pointer_type__: type
 
 # this is a union of all the subclasses of _CData, which is useful because of
 # the methods that are present on each of those subclasses which are not present
diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi
index d7820c72c090..f21a9ca60270 100644
--- a/mypy/typeshed/stdlib/_curses.pyi
+++ b/mypy/typeshed/stdlib/_curses.pyi
@@ -304,6 +304,9 @@ def has_colors() -> bool: ...
 if sys.version_info >= (3, 10):
     def has_extended_color_support() -> bool: ...
 
+if sys.version_info >= (3, 14):
+    def assume_default_colors(fg: int, bg: int, /) -> None: ...
+
 def has_ic() -> bool: ...
 def has_il() -> bool: ...
 def has_key(key: int, /) -> bool: ...
diff --git a/mypy/typeshed/stdlib/_heapq.pyi b/mypy/typeshed/stdlib/_heapq.pyi
index 9f731bf91eef..3363fbcd7e74 100644
--- a/mypy/typeshed/stdlib/_heapq.pyi
+++ b/mypy/typeshed/stdlib/_heapq.pyi
@@ -1,11 +1,19 @@
+import sys
 from typing import Any, Final, TypeVar
 
-_T = TypeVar("_T")
+_T = TypeVar("_T")  # list items must be comparable
 
 __about__: Final[str]
 
-def heapify(heap: list[Any], /) -> None: ...
+def heapify(heap: list[Any], /) -> None: ...  # list items must be comparable
 def heappop(heap: list[_T], /) -> _T: ...
 def heappush(heap: list[_T], item: _T, /) -> None: ...
 def heappushpop(heap: list[_T], item: _T, /) -> _T: ...
 def heapreplace(heap: list[_T], item: _T, /) -> _T: ...
+
+if sys.version_info >= (3, 14):
+    def heapify_max(heap: list[Any], /) -> None: ...  # list items must be comparable
+    def heappop_max(heap: list[_T], /) -> _T: ...
+    def heappush_max(heap: list[_T], item: _T, /) -> None: ...
+    def heappushpop_max(heap: list[_T], item: _T, /) -> _T: ...
+    def heapreplace_max(heap: list[_T], item: _T, /) -> _T: ...
diff --git a/mypy/typeshed/stdlib/_imp.pyi b/mypy/typeshed/stdlib/_imp.pyi
index de3549a91da5..c12c26d08ba2 100644
--- a/mypy/typeshed/stdlib/_imp.pyi
+++ b/mypy/typeshed/stdlib/_imp.pyi
@@ -5,6 +5,8 @@ from importlib.machinery import ModuleSpec
 from typing import Any
 
 check_hash_based_pycs: str
+if sys.version_info >= (3, 14):
+    pyc_magic_number_token: int
 
 def source_hash(key: int, source: ReadableBuffer) -> bytes: ...
 def create_builtin(spec: ModuleSpec, /) -> types.ModuleType: ...
diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi
index df05dcd80be8..dd74e316e899 100644
--- a/mypy/typeshed/stdlib/_posixsubprocess.pyi
+++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi
@@ -4,29 +4,56 @@ from collections.abc import Callable, Sequence
 from typing import SupportsIndex
 
 if sys.platform != "win32":
-    def fork_exec(
-        args: Sequence[StrOrBytesPath] | None,
-        executable_list: Sequence[bytes],
-        close_fds: bool,
-        pass_fds: tuple[int, ...],
-        cwd: str,
-        env: Sequence[bytes] | None,
-        p2cread: int,
-        p2cwrite: int,
-        c2pread: int,
-        c2pwrite: int,
-        errread: int,
-        errwrite: int,
-        errpipe_read: int,
-        errpipe_write: int,
-        restore_signals: int,
-        call_setsid: int,
-        pgid_to_set: int,
-        gid: SupportsIndex | None,
-        extra_groups: list[int] | None,
-        uid: SupportsIndex | None,
-        child_umask: int,
-        preexec_fn: Callable[[], None],
-        allow_vfork: bool,
-        /,
-    ) -> int: ...
+    if sys.version_info >= (3, 14):
+        def fork_exec(
+            args: Sequence[StrOrBytesPath] | None,
+            executable_list: Sequence[bytes],
+            close_fds: bool,
+            pass_fds: tuple[int, ...],
+            cwd: str,
+            env: Sequence[bytes] | None,
+            p2cread: int,
+            p2cwrite: int,
+            c2pread: int,
+            c2pwrite: int,
+            errread: int,
+            errwrite: int,
+            errpipe_read: int,
+            errpipe_write: int,
+            restore_signals: int,
+            call_setsid: int,
+            pgid_to_set: int,
+            gid: SupportsIndex | None,
+            extra_groups: list[int] | None,
+            uid: SupportsIndex | None,
+            child_umask: int,
+            preexec_fn: Callable[[], None],
+            /,
+        ) -> int: ...
+    else:
+        def fork_exec(
+            args: Sequence[StrOrBytesPath] | None,
+            executable_list: Sequence[bytes],
+            close_fds: bool,
+            pass_fds: tuple[int, ...],
+            cwd: str,
+            env: Sequence[bytes] | None,
+            p2cread: int,
+            p2cwrite: int,
+            c2pread: int,
+            c2pwrite: int,
+            errread: int,
+            errwrite: int,
+            errpipe_read: int,
+            errpipe_write: int,
+            restore_signals: bool,
+            call_setsid: bool,
+            pgid_to_set: int,
+            gid: SupportsIndex | None,
+            extra_groups: list[int] | None,
+            uid: SupportsIndex | None,
+            child_umask: int,
+            preexec_fn: Callable[[], None],
+            allow_vfork: bool,
+            /,
+        ) -> int: ...
diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi
index 378ac2423757..9cfbe55b4fe3 100644
--- a/mypy/typeshed/stdlib/_thread.pyi
+++ b/mypy/typeshed/stdlib/_thread.pyi
@@ -18,6 +18,8 @@ class RLock:
     def release(self) -> None: ...
     __enter__ = acquire
     def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
+    if sys.version_info >= (3, 14):
+        def locked(self) -> bool: ...
 
 if sys.version_info >= (3, 13):
     @final
@@ -105,6 +107,9 @@ _excepthook: Callable[[_ExceptHookArgs], Any]
 if sys.version_info >= (3, 12):
     def daemon_threads_allowed() -> bool: ...
 
+if sys.version_info >= (3, 14):
+    def set_name(name: str) -> None: ...
+
 class _local:
     def __getattribute__(self, name: str, /) -> Any: ...
     def __setattr__(self, name: str, value: Any, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi
index 4206a2114f95..08eb00ca442b 100644
--- a/mypy/typeshed/stdlib/_tkinter.pyi
+++ b/mypy/typeshed/stdlib/_tkinter.pyi
@@ -77,7 +77,7 @@ class TkappType:
     def globalgetvar(self, *args, **kwargs): ...
     def globalsetvar(self, *args, **kwargs): ...
     def globalunsetvar(self, *args, **kwargs): ...
-    def interpaddr(self): ...
+    def interpaddr(self) -> int: ...
     def loadtk(self) -> None: ...
     def mainloop(self, threshold: int = 0, /): ...
     def quit(self): ...
diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
index c37d55a7d9ec..f322244016dd 100644
--- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi
+++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
@@ -298,9 +298,6 @@ class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol):
 
 class SizedBuffer(Sized, Buffer, Protocol): ...
 
-# for compatibility with third-party stubs that may use this
-_BufferWithLen: TypeAlias = SizedBuffer  # not stable  # noqa: Y047
-
 ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
 OptExcInfo: TypeAlias = ExcInfo | tuple[None, None, None]
 
diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
index 79e6cfde12ff..c22777e45436 100644
--- a/mypy/typeshed/stdlib/argparse.pyi
+++ b/mypy/typeshed/stdlib/argparse.pyi
@@ -283,13 +283,7 @@ class HelpFormatter:
 
     if sys.version_info >= (3, 14):
         def __init__(
-            self,
-            prog: str,
-            indent_increment: int = 2,
-            max_help_position: int = 24,
-            width: int | None = None,
-            prefix_chars: str = "-",
-            color: bool = False,
+            self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, color: bool = False
         ) -> None: ...
     else:
         def __init__(
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index f26ec4d1a08b..af9d20d086b3 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -1095,20 +1095,28 @@ if sys.version_info >= (3, 14):
             **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
+if sys.version_info >= (3, 10):
+    from types import EllipsisType
+
+    _ConstantValue: typing_extensions.TypeAlias = str | bytes | bool | int | float | complex | None | EllipsisType
+else:
+    # Rely on builtins.ellipsis
+    _ConstantValue: typing_extensions.TypeAlias = str | bytes | bool | int | float | complex | None | ellipsis  # noqa: F821
+
 class Constant(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("value", "kind")
-    value: Any  # None, str, bytes, bool, int, float, complex, Ellipsis
+    value: _ConstantValue
     kind: str | None
     if sys.version_info < (3, 14):
         # Aliases for value, for backwards compatibility
-        s: Any
-        n: int | float | complex
+        s: _ConstantValue
+        n: _ConstantValue
 
-    def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, value: _ConstantValue, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
-        def __replace__(self, *, value: Any = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
+        def __replace__(self, *, value: _ConstantValue = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class Attribute(expr):
     if sys.version_info >= (3, 10):
@@ -1429,15 +1437,19 @@ class keyword(AST):
         def __replace__(self, *, arg: str | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
 
 class alias(AST):
-    lineno: int
-    col_offset: int
-    end_lineno: int | None
-    end_col_offset: int | None
-    if sys.version_info >= (3, 10):
-        __match_args__ = ("name", "asname")
     name: str
     asname: str | None
-    def __init__(self, name: str, asname: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
+    if sys.version_info >= (3, 10):
+        lineno: int
+        col_offset: int
+        end_lineno: int | None
+        end_col_offset: int | None
+    if sys.version_info >= (3, 10):
+        __match_args__ = ("name", "asname")
+    if sys.version_info >= (3, 10):
+        def __init__(self, name: str, asname: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
+    else:
+        def __init__(self, name: str, asname: str | None = None) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(self, *, name: str = ..., asname: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ...
diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi
index f9118608060e..68e44a88face 100644
--- a/mypy/typeshed/stdlib/asyncio/__init__.pyi
+++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi
@@ -41,12 +41,14 @@ if sys.platform == "win32":
             "Server",  # from base_events
             "iscoroutinefunction",  # from coroutines
             "iscoroutine",  # from coroutines
-            "AbstractEventLoopPolicy",  # from events
+            "_AbstractEventLoopPolicy",  # from events
             "AbstractEventLoop",  # from events
             "AbstractServer",  # from events
             "Handle",  # from events
             "TimerHandle",  # from events
+            "_get_event_loop_policy",  # from events
             "get_event_loop_policy",  # from events
+            "_set_event_loop_policy",  # from events
             "set_event_loop_policy",  # from events
             "get_event_loop",  # from events
             "set_event_loop",  # from events
@@ -132,9 +134,9 @@ if sys.platform == "win32":
             "SelectorEventLoop",  # from windows_events
             "ProactorEventLoop",  # from windows_events
             "IocpProactor",  # from windows_events
-            "DefaultEventLoopPolicy",  # from windows_events
-            "WindowsSelectorEventLoopPolicy",  # from windows_events
-            "WindowsProactorEventLoopPolicy",  # from windows_events
+            "_DefaultEventLoopPolicy",  # from windows_events
+            "_WindowsSelectorEventLoopPolicy",  # from windows_events
+            "_WindowsProactorEventLoopPolicy",  # from windows_events
             "EventLoop",  # from windows_events
         )
     elif sys.version_info >= (3, 13):
@@ -515,12 +517,14 @@ else:
             "Server",  # from base_events
             "iscoroutinefunction",  # from coroutines
             "iscoroutine",  # from coroutines
-            "AbstractEventLoopPolicy",  # from events
+            "_AbstractEventLoopPolicy",  # from events
             "AbstractEventLoop",  # from events
             "AbstractServer",  # from events
             "Handle",  # from events
             "TimerHandle",  # from events
+            "_get_event_loop_policy",  # from events
             "get_event_loop_policy",  # from events
+            "_set_event_loop_policy",  # from events
             "set_event_loop_policy",  # from events
             "get_event_loop",  # from events
             "set_event_loop",  # from events
@@ -606,7 +610,7 @@ else:
             "DatagramTransport",  # from transports
             "SubprocessTransport",  # from transports
             "SelectorEventLoop",  # from unix_events
-            "DefaultEventLoopPolicy",  # from unix_events
+            "_DefaultEventLoopPolicy",  # from unix_events
             "EventLoop",  # from unix_events
         )
     elif sys.version_info >= (3, 13):
diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi
index af43d2f5937d..688ef3ed0879 100644
--- a/mypy/typeshed/stdlib/asyncio/events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/events.pyi
@@ -28,12 +28,14 @@ if sys.version_info < (3, 14):
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.version_info >= (3, 14):
     __all__ = (
-        "AbstractEventLoopPolicy",
+        "_AbstractEventLoopPolicy",
         "AbstractEventLoop",
         "AbstractServer",
         "Handle",
         "TimerHandle",
+        "_get_event_loop_policy",
         "get_event_loop_policy",
+        "_set_event_loop_policy",
         "set_event_loop_policy",
         "get_event_loop",
         "set_event_loop",
@@ -600,7 +602,7 @@ class AbstractEventLoop:
     @abstractmethod
     async def shutdown_default_executor(self) -> None: ...
 
-class AbstractEventLoopPolicy:
+class _AbstractEventLoopPolicy:
     @abstractmethod
     def get_event_loop(self) -> AbstractEventLoop: ...
     @abstractmethod
@@ -622,13 +624,33 @@ class AbstractEventLoopPolicy:
             @abstractmethod
             def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ...
 
-class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta):
-    def get_event_loop(self) -> AbstractEventLoop: ...
-    def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ...
-    def new_event_loop(self) -> AbstractEventLoop: ...
+if sys.version_info < (3, 14):
+    AbstractEventLoopPolicy = _AbstractEventLoopPolicy
+
+if sys.version_info >= (3, 14):
+    class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy, metaclass=ABCMeta):
+        def get_event_loop(self) -> AbstractEventLoop: ...
+        def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ...
+        def new_event_loop(self) -> AbstractEventLoop: ...
+
+else:
+    class BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy, metaclass=ABCMeta):
+        def get_event_loop(self) -> AbstractEventLoop: ...
+        def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ...
+        def new_event_loop(self) -> AbstractEventLoop: ...
+
+if sys.version_info >= (3, 14):
+    def _get_event_loop_policy() -> _AbstractEventLoopPolicy: ...
+    def _set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ...
+    @deprecated("Deprecated as of Python 3.14; will be removed in Python 3.16")
+    def get_event_loop_policy() -> _AbstractEventLoopPolicy: ...
+    @deprecated("Deprecated as of Python 3.14; will be removed in Python 3.16")
+    def set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ...
+
+else:
+    def get_event_loop_policy() -> _AbstractEventLoopPolicy: ...
+    def set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ...
 
-def get_event_loop_policy() -> AbstractEventLoopPolicy: ...
-def set_event_loop_policy(policy: AbstractEventLoopPolicy | None) -> None: ...
 def set_event_loop(loop: AbstractEventLoop | None) -> None: ...
 def new_event_loop() -> AbstractEventLoop: ...
 
diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
index 79f99fbe37f0..49f200dcdcae 100644
--- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
@@ -7,8 +7,8 @@ from socket import socket
 from typing import Literal
 from typing_extensions import Self, TypeVarTuple, Unpack, deprecated
 
+from . import events
 from .base_events import Server, _ProtocolFactory, _SSLContext
-from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy
 from .selector_events import BaseSelectorEventLoop
 
 _Ts = TypeVarTuple("_Ts")
@@ -16,7 +16,7 @@ _Ts = TypeVarTuple("_Ts")
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.platform != "win32":
     if sys.version_info >= (3, 14):
-        __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy", "EventLoop")
+        __all__ = ("SelectorEventLoop", "_DefaultEventLoopPolicy", "EventLoop")
     elif sys.version_info >= (3, 13):
         # Adds EventLoop
         __all__ = (
@@ -57,7 +57,7 @@ if sys.version_info < (3, 14):
             @abstractmethod
             def remove_child_handler(self, pid: int) -> bool: ...
             @abstractmethod
-            def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+            def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
             @abstractmethod
             def close(self) -> None: ...
             @abstractmethod
@@ -78,7 +78,7 @@ if sys.version_info < (3, 14):
             @abstractmethod
             def remove_child_handler(self, pid: int) -> bool: ...
             @abstractmethod
-            def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+            def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
             @abstractmethod
             def close(self) -> None: ...
             @abstractmethod
@@ -98,7 +98,7 @@ if sys.platform != "win32":
             class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta):
                 def close(self) -> None: ...
                 def is_active(self) -> bool: ...
-                def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+                def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
             @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
             class SafeChildWatcher(BaseChildWatcher):
@@ -128,7 +128,7 @@ if sys.platform != "win32":
             class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta):
                 def close(self) -> None: ...
                 def is_active(self) -> bool: ...
-                def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+                def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
             class SafeChildWatcher(BaseChildWatcher):
                 def __enter__(self) -> Self: ...
@@ -166,8 +166,10 @@ if sys.platform != "win32":
                 cleanup_socket: bool = True,
             ) -> Server: ...
 
-    class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy):
-        if sys.version_info < (3, 14):
+    if sys.version_info >= (3, 14):
+        class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy): ...
+    else:
+        class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
             if sys.version_info >= (3, 12):
                 @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
                 def get_child_watcher(self) -> AbstractChildWatcher: ...
@@ -179,7 +181,10 @@ if sys.platform != "win32":
 
     SelectorEventLoop = _UnixSelectorEventLoop
 
-    DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
+    if sys.version_info >= (3, 14):
+        _DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
+    else:
+        DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
 
     if sys.version_info >= (3, 13):
         EventLoop = SelectorEventLoop
@@ -198,7 +203,7 @@ if sys.platform != "win32":
                     self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
                 ) -> None: ...
                 def remove_child_handler(self, pid: int) -> bool: ...
-                def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+                def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
         else:
             class MultiLoopChildWatcher(AbstractChildWatcher):
@@ -212,7 +217,7 @@ if sys.platform != "win32":
                     self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
                 ) -> None: ...
                 def remove_child_handler(self, pid: int) -> bool: ...
-                def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+                def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
     if sys.version_info < (3, 14):
         class ThreadedChildWatcher(AbstractChildWatcher):
@@ -227,7 +232,7 @@ if sys.platform != "win32":
                 self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
             ) -> None: ...
             def remove_child_handler(self, pid: int) -> bool: ...
-            def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+            def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
         class PidfdChildWatcher(AbstractChildWatcher):
             def __enter__(self) -> Self: ...
@@ -236,7 +241,7 @@ if sys.platform != "win32":
             ) -> None: ...
             def is_active(self) -> bool: ...
             def close(self) -> None: ...
-            def attach_loop(self, loop: AbstractEventLoop | None) -> None: ...
+            def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
             def add_child_handler(
                 self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts]
             ) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi
index 2ffc2eccb228..b454aca1f262 100644
--- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi
@@ -8,7 +8,17 @@ from . import events, futures, proactor_events, selector_events, streams, window
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.platform == "win32":
-    if sys.version_info >= (3, 13):
+    if sys.version_info >= (3, 14):
+        __all__ = (
+            "SelectorEventLoop",
+            "ProactorEventLoop",
+            "IocpProactor",
+            "_DefaultEventLoopPolicy",
+            "_WindowsSelectorEventLoopPolicy",
+            "_WindowsProactorEventLoopPolicy",
+            "EventLoop",
+        )
+    elif sys.version_info >= (3, 13):
         # 3.13 added `EventLoop`.
         __all__ = (
             "SelectorEventLoop",
@@ -85,17 +95,27 @@ if sys.platform == "win32":
 
     SelectorEventLoop = _WindowsSelectorEventLoop
 
-    class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
-        _loop_factory: ClassVar[type[SelectorEventLoop]]
-        if sys.version_info < (3, 14):
+    if sys.version_info >= (3, 14):
+        class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
+            _loop_factory: ClassVar[type[SelectorEventLoop]]
+
+        class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
+            _loop_factory: ClassVar[type[ProactorEventLoop]]
+
+    else:
+        class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
+            _loop_factory: ClassVar[type[SelectorEventLoop]]
             def get_child_watcher(self) -> NoReturn: ...
             def set_child_watcher(self, watcher: Any) -> NoReturn: ...
 
-    class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
-        _loop_factory: ClassVar[type[ProactorEventLoop]]
-        def get_child_watcher(self) -> NoReturn: ...
-        def set_child_watcher(self, watcher: Any) -> NoReturn: ...
+        class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
+            _loop_factory: ClassVar[type[ProactorEventLoop]]
+            def get_child_watcher(self) -> NoReturn: ...
+            def set_child_watcher(self, watcher: Any) -> NoReturn: ...
 
-    DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy
+    if sys.version_info >= (3, 14):
+        _DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy
+    else:
+        DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy
     if sys.version_info >= (3, 13):
         EventLoop = ProactorEventLoop
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index d874edd8f83a..6e983ef9ef29 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -6,7 +6,6 @@ import types
 from _collections_abc import dict_items, dict_keys, dict_values
 from _typeshed import (
     AnnotationForm,
-    AnyStr_co,
     ConvertibleToFloat,
     ConvertibleToInt,
     FileDescriptorOrPath,
@@ -33,6 +32,7 @@ from _typeshed import (
 )
 from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized
 from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
+from os import PathLike
 from types import CellType, CodeType, GenericAlias, TracebackType
 
 # mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping}
@@ -153,6 +153,9 @@ class staticmethod(Generic[_P, _R_co]):
         @property
         def __wrapped__(self) -> Callable[_P, _R_co]: ...
         def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ...
+    if sys.version_info >= (3, 14):
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+        __annotate__: AnnotateFunc | None
 
 class classmethod(Generic[_T, _P, _R_co]):
     @property
@@ -169,6 +172,9 @@ class classmethod(Generic[_T, _P, _R_co]):
         __qualname__: str
         @property
         def __wrapped__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ...
+    if sys.version_info >= (3, 14):
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+        __annotate__: AnnotateFunc | None
 
 class type:
     # object.__base__ is None. Otherwise, it would be a type.
@@ -324,7 +330,11 @@ class int:
     def __trunc__(self) -> int: ...
     def __ceil__(self) -> int: ...
     def __floor__(self) -> int: ...
-    def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ...
+    if sys.version_info >= (3, 14):
+        def __round__(self, ndigits: SupportsIndex | None = None, /) -> int: ...
+    else:
+        def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ...
+
     def __getnewargs__(self) -> tuple[int]: ...
     def __eq__(self, value: object, /) -> bool: ...
     def __ne__(self, value: object, /) -> bool: ...
@@ -740,6 +750,8 @@ class bytearray(MutableSequence[int]):
     def __alloc__(self) -> int: ...
     def __buffer__(self, flags: int, /) -> memoryview: ...
     def __release_buffer__(self, buffer: memoryview, /) -> None: ...
+    if sys.version_info >= (3, 14):
+        def resize(self, size: int, /) -> None: ...
 
 _IntegerFormats: TypeAlias = Literal[
     "b", "B", "@b", "@B", "h", "H", "@h", "@H", "i", "I", "@i", "@I", "l", "L", "@l", "@L", "q", "Q", "@q", "@Q", "P", "@P"
@@ -817,6 +829,8 @@ class memoryview(Sequence[_I]):
     # See https://github.com/python/cpython/issues/125420
     index: ClassVar[None]  # type: ignore[assignment]
     count: ClassVar[None]  # type: ignore[assignment]
+    if sys.version_info >= (3, 14):
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 @final
 class bool(int):
@@ -848,7 +862,7 @@ class bool(int):
     @overload
     def __rxor__(self, value: int, /) -> int: ...
     def __getnewargs__(self) -> tuple[int]: ...
-    @deprecated("Will throw an error in Python 3.14. Use `not` for logical negation of bools instead.")
+    @deprecated("Will throw an error in Python 3.16. Use `not` for logical negation of bools instead.")
     def __invert__(self) -> int: ...
 
 @final
@@ -1241,11 +1255,6 @@ def breakpoint(*args: Any, **kws: Any) -> None: ...
 def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ...
 def chr(i: int | SupportsIndex, /) -> str: ...
 
-# We define this here instead of using os.PathLike to avoid import cycle issues.
-# See https://github.com/python/typeshed/pull/991#issuecomment-288160993
-class _PathLike(Protocol[AnyStr_co]):
-    def __fspath__(self) -> AnyStr_co: ...
-
 if sys.version_info >= (3, 10):
     def aiter(async_iterable: SupportsAiter[_SupportsAnextT_co], /) -> _SupportsAnextT_co: ...
 
@@ -1266,7 +1275,7 @@ if sys.version_info >= (3, 10):
 @overload
 def compile(
     source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive,
-    filename: str | ReadableBuffer | _PathLike[Any],
+    filename: str | ReadableBuffer | PathLike[Any],
     mode: str,
     flags: Literal[0],
     dont_inherit: bool = False,
@@ -1277,7 +1286,7 @@ def compile(
 @overload
 def compile(
     source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive,
-    filename: str | ReadableBuffer | _PathLike[Any],
+    filename: str | ReadableBuffer | PathLike[Any],
     mode: str,
     *,
     dont_inherit: bool = False,
@@ -1287,7 +1296,7 @@ def compile(
 @overload
 def compile(
     source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive,
-    filename: str | ReadableBuffer | _PathLike[Any],
+    filename: str | ReadableBuffer | PathLike[Any],
     mode: str,
     flags: Literal[1024],
     dont_inherit: bool = False,
@@ -1298,7 +1307,7 @@ def compile(
 @overload
 def compile(
     source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive,
-    filename: str | ReadableBuffer | _PathLike[Any],
+    filename: str | ReadableBuffer | PathLike[Any],
     mode: str,
     flags: int,
     dont_inherit: bool = False,
diff --git a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
index c1a29e6b0552..9c1078983d8c 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
@@ -1,7 +1,7 @@
 import sys
 from collections.abc import Callable, Mapping
 from concurrent.futures import ThreadPoolExecutor
-from typing import Final, Literal, Protocol, overload, type_check_only
+from typing import Literal, Protocol, overload, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack
 
 _Task: TypeAlias = tuple[bytes, Literal["function", "script"]]
@@ -37,8 +37,6 @@ if sys.version_info >= (3, 14):
     class ExecutionFailed(InterpreterError):
         def __init__(self, excinfo: _ExcInfo) -> None: ...  #  type: ignore[override]
 
-    UNBOUND: Final = 2
-
     class WorkerContext(ThreadWorkerContext):
         # Parent class doesn't have `shared` argument,
         @overload  #  type: ignore[override]
diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi
index 68b75b86def1..0b14bd856784 100644
--- a/mypy/typeshed/stdlib/ctypes/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi
@@ -31,6 +31,9 @@ from typing_extensions import Self, TypeAlias, deprecated
 if sys.platform == "win32":
     from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error
 
+    if sys.version_info >= (3, 14):
+        from _ctypes import COMError as COMError
+
 if sys.version_info >= (3, 11):
     from ctypes._endian import BigEndianUnion as BigEndianUnion, LittleEndianUnion as LittleEndianUnion
 
@@ -197,8 +200,13 @@ if sys.platform == "win32":
 
 def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ...
 
+if sys.version_info >= (3, 14):
+    def memoryview_at(ptr: _CVoidConstPLike, size: int, readonly: bool = False) -> memoryview: ...
+
 class py_object(_CanCastTo, _SimpleCData[_T]):
     _type_: ClassVar[Literal["O"]]
+    if sys.version_info >= (3, 14):
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 class c_bool(_SimpleCData[bool]):
     _type_: ClassVar[Literal["?"]]
@@ -270,16 +278,16 @@ class c_double(_SimpleCData[float]):
 class c_longdouble(_SimpleCData[float]):  # can be an alias for c_double
     _type_: ClassVar[Literal["d", "g"]]
 
-if sys.version_info >= (3, 14):
-    class c_float_complex(_SimpleCData[complex]):
-        _type_: ClassVar[Literal["E"]]
-
+if sys.version_info >= (3, 14) and sys.platform != "win32":
     class c_double_complex(_SimpleCData[complex]):
-        _type_: ClassVar[Literal["C"]]
+        _type_: ClassVar[Literal["D"]]
 
-    class c_longdouble_complex(_SimpleCData[complex]):
+    class c_float_complex(_SimpleCData[complex]):
         _type_: ClassVar[Literal["F"]]
 
+    class c_longdouble_complex(_SimpleCData[complex]):
+        _type_: ClassVar[Literal["G"]]
+
 class c_char(_SimpleCData[bytes]):
     _type_: ClassVar[Literal["c"]]
     def __init__(self, value: int | bytes | bytearray = ...) -> None: ...
diff --git a/mypy/typeshed/stdlib/ctypes/util.pyi b/mypy/typeshed/stdlib/ctypes/util.pyi
index 316f7a2b3e2f..4f18c1d8db34 100644
--- a/mypy/typeshed/stdlib/ctypes/util.pyi
+++ b/mypy/typeshed/stdlib/ctypes/util.pyi
@@ -5,4 +5,7 @@ def find_library(name: str) -> str | None: ...
 if sys.platform == "win32":
     def find_msvcrt() -> str | None: ...
 
+if sys.version_info >= (3, 14):
+    def dllist() -> list[str]: ...
+
 def test() -> None: ...
diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
index 63f117787aa0..e9ed0df24dd1 100644
--- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi
+++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
@@ -83,6 +83,15 @@ HACCEL = HANDLE
 HBITMAP = HANDLE
 HBRUSH = HANDLE
 HCOLORSPACE = HANDLE
+if sys.version_info >= (3, 14):
+    HCONV = HANDLE
+    HCONVLIST = HANDLE
+    HCURSOR = HANDLE
+    HDDEDATA = HANDLE
+    HDROP = HANDLE
+    HFILE = INT
+    HRESULT = LONG
+    HSZ = HANDLE
 HDC = HANDLE
 HDESK = HANDLE
 HDWP = HANDLE
diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi
index bba76c1af1b4..c76b0b0e61e2 100644
--- a/mypy/typeshed/stdlib/dataclasses.pyi
+++ b/mypy/typeshed/stdlib/dataclasses.pyi
@@ -71,14 +71,28 @@ def asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, An
 def astuple(obj: DataclassInstance) -> tuple[Any, ...]: ...
 @overload
 def astuple(obj: DataclassInstance, *, tuple_factory: Callable[[list[Any]], _T]) -> _T: ...
-@overload
-def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ...
-@overload
-def dataclass(cls: type[_T], /) -> type[_T]: ...
 
 if sys.version_info >= (3, 11):
     @overload
     def dataclass(
+        cls: type[_T],
+        /,
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        match_args: bool = True,
+        kw_only: bool = False,
+        slots: bool = False,
+        weakref_slot: bool = False,
+    ) -> type[_T]: ...
+    @overload
+    def dataclass(
+        cls: None = None,
+        /,
         *,
         init: bool = True,
         repr: bool = True,
@@ -95,6 +109,23 @@ if sys.version_info >= (3, 11):
 elif sys.version_info >= (3, 10):
     @overload
     def dataclass(
+        cls: type[_T],
+        /,
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        match_args: bool = True,
+        kw_only: bool = False,
+        slots: bool = False,
+    ) -> type[_T]: ...
+    @overload
+    def dataclass(
+        cls: None = None,
+        /,
         *,
         init: bool = True,
         repr: bool = True,
@@ -110,6 +141,20 @@ elif sys.version_info >= (3, 10):
 else:
     @overload
     def dataclass(
+        cls: type[_T],
+        /,
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+    ) -> type[_T]: ...
+    @overload
+    def dataclass(
+        cls: None = None,
+        /,
         *,
         init: bool = True,
         repr: bool = True,
@@ -308,7 +353,7 @@ def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstan
 
 class FrozenInstanceError(AttributeError): ...
 
-class InitVar(Generic[_T], metaclass=type):
+class InitVar(Generic[_T]):
     type: Type[_T]
     def __init__(self, type: Type[_T]) -> None: ...
     @overload
diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi
index 26f198867113..327b135459a0 100644
--- a/mypy/typeshed/stdlib/enum.pyi
+++ b/mypy/typeshed/stdlib/enum.pyi
@@ -53,6 +53,7 @@ _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum])
 # >>> Enum('Foo', names={'RED': 1, 'YELLOW': 2})
 # 
 _EnumNames: TypeAlias = str | Iterable[str] | Iterable[Iterable[str | Any]] | Mapping[str, Any]
+_Signature: TypeAlias = Any  # TODO: Unable to import Signature from inspect module
 
 if sys.version_info >= (3, 11):
     class nonmember(Generic[_EnumMemberT]):
@@ -166,6 +167,9 @@ class EnumMeta(type):
     if sys.version_info >= (3, 12):
         @overload
         def __call__(cls: type[_EnumMemberT], value: Any, *values: Any) -> _EnumMemberT: ...
+    if sys.version_info >= (3, 14):
+        @property
+        def __signature__(cls) -> _Signature: ...
 
     _member_names_: list[str]  # undocumented
     _member_map_: dict[str, Enum]  # undocumented
@@ -212,7 +216,7 @@ class Enum(metaclass=EnumMeta):
     if sys.version_info >= (3, 11):
         def __copy__(self) -> Self: ...
         def __deepcopy__(self, memo: Any) -> Self: ...
-    if sys.version_info >= (3, 12):
+    if sys.version_info >= (3, 12) and sys.version_info < (3, 14):
         @classmethod
         def __signature__(cls) -> str: ...
 
diff --git a/mypy/typeshed/stdlib/errno.pyi b/mypy/typeshed/stdlib/errno.pyi
index 84d2b44a6a61..3ba8b66d2865 100644
--- a/mypy/typeshed/stdlib/errno.pyi
+++ b/mypy/typeshed/stdlib/errno.pyi
@@ -170,6 +170,9 @@ if sys.platform != "win32" and sys.platform != "darwin":
     ENOMEDIUM: int
     ERFKILL: int
 
+    if sys.version_info >= (3, 14):
+        EHWPOISON: int
+
 if sys.platform == "win32":
     # All of these are undocumented
     WSABASEERR: int
diff --git a/mypy/typeshed/stdlib/faulthandler.pyi b/mypy/typeshed/stdlib/faulthandler.pyi
index 320a8b6fad15..8f93222c9936 100644
--- a/mypy/typeshed/stdlib/faulthandler.pyi
+++ b/mypy/typeshed/stdlib/faulthandler.pyi
@@ -4,6 +4,10 @@ from _typeshed import FileDescriptorLike
 def cancel_dump_traceback_later() -> None: ...
 def disable() -> None: ...
 def dump_traceback(file: FileDescriptorLike = ..., all_threads: bool = ...) -> None: ...
+
+if sys.version_info >= (3, 14):
+    def dump_c_stack(file: FileDescriptorLike = ...) -> None: ...
+
 def dump_traceback_later(timeout: float, repeat: bool = ..., file: FileDescriptorLike = ..., exit: bool = ...) -> None: ...
 def enable(file: FileDescriptorLike = ..., all_threads: bool = ...) -> None: ...
 def is_enabled() -> bool: ...
diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi
index 3016a3a43b36..cf0fd0807b7b 100644
--- a/mypy/typeshed/stdlib/importlib/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/abc.pyi
@@ -113,63 +113,71 @@ class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader
     def get_filename(self, name: str | None = None) -> str: ...
     def load_module(self, name: str | None = None) -> types.ModuleType: ...
 
-class ResourceReader(metaclass=ABCMeta):
-    @abstractmethod
-    def open_resource(self, resource: str) -> IO[bytes]: ...
-    @abstractmethod
-    def resource_path(self, resource: str) -> str: ...
-    if sys.version_info >= (3, 10):
+if sys.version_info < (3, 11):
+    class ResourceReader(metaclass=ABCMeta):
         @abstractmethod
-        def is_resource(self, path: str) -> bool: ...
-    else:
+        def open_resource(self, resource: str) -> IO[bytes]: ...
         @abstractmethod
-        def is_resource(self, name: str) -> bool: ...
+        def resource_path(self, resource: str) -> str: ...
+        if sys.version_info >= (3, 10):
+            @abstractmethod
+            def is_resource(self, path: str) -> bool: ...
+        else:
+            @abstractmethod
+            def is_resource(self, name: str) -> bool: ...
 
-    @abstractmethod
-    def contents(self) -> Iterator[str]: ...
+        @abstractmethod
+        def contents(self) -> Iterator[str]: ...
 
-@runtime_checkable
-class Traversable(Protocol):
-    @abstractmethod
-    def is_dir(self) -> bool: ...
-    @abstractmethod
-    def is_file(self) -> bool: ...
-    @abstractmethod
-    def iterdir(self) -> Iterator[Traversable]: ...
-    if sys.version_info >= (3, 11):
+    @runtime_checkable
+    class Traversable(Protocol):
         @abstractmethod
-        def joinpath(self, *descendants: str) -> Traversable: ...
-    else:
+        def is_dir(self) -> bool: ...
         @abstractmethod
-        def joinpath(self, child: str, /) -> Traversable: ...
-
-    # The documentation and runtime protocol allows *args, **kwargs arguments,
-    # but this would mean that all implementers would have to support them,
-    # which is not the case.
-    @overload
-    @abstractmethod
-    def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
-    @overload
-    @abstractmethod
-    def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
-    @property
-    @abstractmethod
-    def name(self) -> str: ...
-    if sys.version_info >= (3, 10):
-        def __truediv__(self, child: str, /) -> Traversable: ...
-    else:
+        def is_file(self) -> bool: ...
+        @abstractmethod
+        def iterdir(self) -> Iterator[Traversable]: ...
+        if sys.version_info >= (3, 11):
+            @abstractmethod
+            def joinpath(self, *descendants: str) -> Traversable: ...
+        else:
+            @abstractmethod
+            def joinpath(self, child: str, /) -> Traversable: ...
+
+        # The documentation and runtime protocol allows *args, **kwargs arguments,
+        # but this would mean that all implementers would have to support them,
+        # which is not the case.
+        @overload
+        @abstractmethod
+        def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
+        @overload
         @abstractmethod
-        def __truediv__(self, child: str, /) -> Traversable: ...
+        def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
+        @property
+        @abstractmethod
+        def name(self) -> str: ...
+        if sys.version_info >= (3, 10):
+            def __truediv__(self, child: str, /) -> Traversable: ...
+        else:
+            @abstractmethod
+            def __truediv__(self, child: str, /) -> Traversable: ...
 
-    @abstractmethod
-    def read_bytes(self) -> bytes: ...
-    @abstractmethod
-    def read_text(self, encoding: str | None = None) -> str: ...
+        @abstractmethod
+        def read_bytes(self) -> bytes: ...
+        @abstractmethod
+        def read_text(self, encoding: str | None = None) -> str: ...
 
-class TraversableResources(ResourceReader):
-    @abstractmethod
-    def files(self) -> Traversable: ...
-    def open_resource(self, resource: str) -> BufferedReader: ...
-    def resource_path(self, resource: Any) -> str: ...
-    def is_resource(self, path: str) -> bool: ...
-    def contents(self) -> Iterator[str]: ...
+    class TraversableResources(ResourceReader):
+        @abstractmethod
+        def files(self) -> Traversable: ...
+        def open_resource(self, resource: str) -> BufferedReader: ...
+        def resource_path(self, resource: Any) -> str: ...
+        def is_resource(self, path: str) -> bool: ...
+        def contents(self) -> Iterator[str]: ...
+
+elif sys.version_info < (3, 14):
+    from importlib.resources.abc import (
+        ResourceReader as ResourceReader,
+        Traversable as Traversable,
+        TraversableResources as TraversableResources,
+    )
diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi
index bb1a6f93d0e0..767046b70a3d 100644
--- a/mypy/typeshed/stdlib/importlib/machinery.pyi
+++ b/mypy/typeshed/stdlib/importlib/machinery.pyi
@@ -16,5 +16,28 @@ from importlib._bootstrap_external import (
 
 if sys.version_info >= (3, 11):
     from importlib._bootstrap_external import NamespaceLoader as NamespaceLoader
+if sys.version_info >= (3, 14):
+    from importlib._bootstrap_external import AppleFrameworkLoader as AppleFrameworkLoader
 
 def all_suffixes() -> list[str]: ...
+
+if sys.version_info >= (3, 14):
+    __all__ = [
+        "AppleFrameworkLoader",
+        "BYTECODE_SUFFIXES",
+        "BuiltinImporter",
+        "DEBUG_BYTECODE_SUFFIXES",
+        "EXTENSION_SUFFIXES",
+        "ExtensionFileLoader",
+        "FileFinder",
+        "FrozenImporter",
+        "ModuleSpec",
+        "NamespaceLoader",
+        "OPTIMIZED_BYTECODE_SUFFIXES",
+        "PathFinder",
+        "SOURCE_SUFFIXES",
+        "SourceFileLoader",
+        "SourcelessFileLoader",
+        "WindowsRegistryFinder",
+        "all_suffixes",
+    ]
diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
index 2cf6366b6cb3..e672a619bd17 100644
--- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi
@@ -2,12 +2,16 @@ import os
 import sys
 from collections.abc import Iterator
 from contextlib import AbstractContextManager
-from importlib.abc import Traversable
 from pathlib import Path
 from types import ModuleType
 from typing import Any, BinaryIO, Literal, TextIO
 from typing_extensions import TypeAlias
 
+if sys.version_info >= (3, 11):
+    from importlib.resources.abc import Traversable
+else:
+    from importlib.abc import Traversable
+
 if sys.version_info >= (3, 11):
     from importlib.resources._common import Package as Package
 else:
@@ -72,5 +76,7 @@ if sys.version_info >= (3, 11):
 else:
     def files(package: Package) -> Traversable: ...
 
-if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 11):
+    from importlib.resources.abc import ResourceReader as ResourceReader
+elif sys.version_info >= (3, 10):
     from importlib.abc import ResourceReader as ResourceReader
diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi
index d6a9436544dc..3dd961bb657b 100644
--- a/mypy/typeshed/stdlib/importlib/resources/_common.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi
@@ -5,7 +5,7 @@ if sys.version_info >= (3, 11):
     import types
     from collections.abc import Callable
     from contextlib import AbstractContextManager
-    from importlib.abc import ResourceReader, Traversable
+    from importlib.resources.abc import ResourceReader, Traversable
     from pathlib import Path
     from typing import Literal, overload
     from typing_extensions import TypeAlias, deprecated
diff --git a/mypy/typeshed/stdlib/importlib/resources/abc.pyi b/mypy/typeshed/stdlib/importlib/resources/abc.pyi
index ad80605f7c71..fe0fe64dba0d 100644
--- a/mypy/typeshed/stdlib/importlib/resources/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/abc.pyi
@@ -1,14 +1,69 @@
 import sys
+from abc import ABCMeta, abstractmethod
+from collections.abc import Iterator
+from io import BufferedReader
+from typing import IO, Any, Literal, Protocol, overload, runtime_checkable
 
 if sys.version_info >= (3, 11):
-    # These are all actually defined in this file on 3.11+,
-    # and re-exported from importlib.abc,
-    # but it's much less code duplication for typeshed if we pretend that they're still defined
-    # in importlib.abc on 3.11+, and re-exported from this file
-    from importlib.abc import (
-        ResourceReader as ResourceReader,
-        Traversable as Traversable,
-        TraversableResources as TraversableResources,
-    )
+    class ResourceReader(metaclass=ABCMeta):
+        @abstractmethod
+        def open_resource(self, resource: str) -> IO[bytes]: ...
+        @abstractmethod
+        def resource_path(self, resource: str) -> str: ...
+        if sys.version_info >= (3, 10):
+            @abstractmethod
+            def is_resource(self, path: str) -> bool: ...
+        else:
+            @abstractmethod
+            def is_resource(self, name: str) -> bool: ...
+
+        @abstractmethod
+        def contents(self) -> Iterator[str]: ...
+
+    @runtime_checkable
+    class Traversable(Protocol):
+        @abstractmethod
+        def is_dir(self) -> bool: ...
+        @abstractmethod
+        def is_file(self) -> bool: ...
+        @abstractmethod
+        def iterdir(self) -> Iterator[Traversable]: ...
+        if sys.version_info >= (3, 11):
+            @abstractmethod
+            def joinpath(self, *descendants: str) -> Traversable: ...
+        else:
+            @abstractmethod
+            def joinpath(self, child: str, /) -> Traversable: ...
+
+        # The documentation and runtime protocol allows *args, **kwargs arguments,
+        # but this would mean that all implementers would have to support them,
+        # which is not the case.
+        @overload
+        @abstractmethod
+        def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
+        @overload
+        @abstractmethod
+        def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
+        @property
+        @abstractmethod
+        def name(self) -> str: ...
+        if sys.version_info >= (3, 10):
+            def __truediv__(self, child: str, /) -> Traversable: ...
+        else:
+            @abstractmethod
+            def __truediv__(self, child: str, /) -> Traversable: ...
+
+        @abstractmethod
+        def read_bytes(self) -> bytes: ...
+        @abstractmethod
+        def read_text(self, encoding: str | None = None) -> str: ...
+
+    class TraversableResources(ResourceReader):
+        @abstractmethod
+        def files(self) -> Traversable: ...
+        def open_resource(self, resource: str) -> BufferedReader: ...
+        def resource_path(self, resource: Any) -> str: ...
+        def is_resource(self, path: str) -> bool: ...
+        def contents(self) -> Iterator[str]: ...
 
     __all__ = ["ResourceReader", "Traversable", "TraversableResources"]
diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi
index cc1c98ae4d0e..370a08623842 100644
--- a/mypy/typeshed/stdlib/importlib/util.pyi
+++ b/mypy/typeshed/stdlib/importlib/util.pyi
@@ -1,4 +1,3 @@
-import importlib.abc
 import importlib.machinery
 import sys
 import types
@@ -12,6 +11,7 @@ from importlib._bootstrap_external import (
     source_from_cache as source_from_cache,
     spec_from_file_location as spec_from_file_location,
 )
+from importlib.abc import Loader
 from typing_extensions import ParamSpec
 
 _P = ParamSpec("_P")
@@ -24,10 +24,26 @@ if sys.version_info < (3, 12):
 def resolve_name(name: str, package: str | None) -> str: ...
 def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None: ...
 
-class LazyLoader(importlib.abc.Loader):
-    def __init__(self, loader: importlib.abc.Loader) -> None: ...
+class LazyLoader(Loader):
+    def __init__(self, loader: Loader) -> None: ...
     @classmethod
-    def factory(cls, loader: importlib.abc.Loader) -> Callable[..., LazyLoader]: ...
+    def factory(cls, loader: Loader) -> Callable[..., LazyLoader]: ...
     def exec_module(self, module: types.ModuleType) -> None: ...
 
 def source_hash(source_bytes: ReadableBuffer) -> bytes: ...
+
+if sys.version_info >= (3, 14):
+    __all__ = [
+        "LazyLoader",
+        "Loader",
+        "MAGIC_NUMBER",
+        "cache_from_source",
+        "decode_source",
+        "find_spec",
+        "module_from_spec",
+        "resolve_name",
+        "source_from_cache",
+        "source_hash",
+        "spec_from_file_location",
+        "spec_from_loader",
+    ]
diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi
index e555f74a81af..24529bd48d6a 100644
--- a/mypy/typeshed/stdlib/logging/__init__.pyi
+++ b/mypy/typeshed/stdlib/logging/__init__.pyi
@@ -373,6 +373,9 @@ class LoggerAdapter(Generic[_L]):
     else:
         extra: Mapping[str, object]
 
+    if sys.version_info >= (3, 13):
+        merge_extra: bool
+
     def process(self, msg: Any, kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]: ...
     def debug(
         self,
diff --git a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi
index 31b982856355..c4af295d2316 100644
--- a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi
@@ -1,3 +1,4 @@
+import sys
 from _typeshed import FileDescriptorLike, Unused
 from collections.abc import Sequence
 from struct import Struct
@@ -14,13 +15,26 @@ class ForkServer:
     def connect_to_new_process(self, fds: Sequence[int]) -> tuple[int, int]: ...
     def ensure_running(self) -> None: ...
 
-def main(
-    listener_fd: int | None,
-    alive_r: FileDescriptorLike,
-    preload: Sequence[str],
-    main_path: str | None = None,
-    sys_path: Unused = None,
-) -> None: ...
+if sys.version_info >= (3, 14):
+    def main(
+        listener_fd: int | None,
+        alive_r: FileDescriptorLike,
+        preload: Sequence[str],
+        main_path: str | None = None,
+        sys_path: list[str] | None = None,
+        *,
+        authkey_r: int | None = None,
+    ) -> None: ...
+
+else:
+    def main(
+        listener_fd: int | None,
+        alive_r: FileDescriptorLike,
+        preload: Sequence[str],
+        main_path: str | None = None,
+        sys_path: Unused = None,
+    ) -> None: ...
+
 def read_signed(fd: int) -> Any: ...
 def write_signed(fd: int, n: int) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
index 50e4f1c1fe66..b0ccac41b925 100644
--- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
@@ -2,7 +2,17 @@ import queue
 import sys
 import threading
 from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT
-from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence
+from collections.abc import (
+    Callable,
+    Iterable,
+    Iterator,
+    Mapping,
+    MutableMapping,
+    MutableSequence,
+    MutableSet,
+    Sequence,
+    Set as AbstractSet,
+)
 from types import GenericAlias, TracebackType
 from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload
 from typing_extensions import Self, TypeAlias
@@ -18,6 +28,7 @@ __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryMana
 _T = TypeVar("_T")
 _KT = TypeVar("_KT")
 _VT = TypeVar("_VT")
+_S = TypeVar("_S")
 
 class Namespace:
     def __init__(self, **kwds: Any) -> None: ...
@@ -111,6 +122,51 @@ else:
         def items(self) -> list[tuple[_KT, _VT]]: ...  # type: ignore[override]
         def values(self) -> list[_VT]: ...  # type: ignore[override]
 
+if sys.version_info >= (3, 14):
+    class _BaseSetProxy(BaseProxy, MutableSet[_T]):
+        __builtins__: ClassVar[dict[str, Any]]
+        # Copied from builtins.set
+        def add(self, element: _T, /) -> None: ...
+        def copy(self) -> set[_T]: ...
+        def clear(self) -> None: ...
+        def difference(self, *s: Iterable[Any]) -> set[_T]: ...
+        def difference_update(self, *s: Iterable[Any]) -> None: ...
+        def discard(self, element: _T, /) -> None: ...
+        def intersection(self, *s: Iterable[Any]) -> set[_T]: ...
+        def intersection_update(self, *s: Iterable[Any]) -> None: ...
+        def isdisjoint(self, s: Iterable[Any], /) -> bool: ...
+        def issubset(self, s: Iterable[Any], /) -> bool: ...
+        def issuperset(self, s: Iterable[Any], /) -> bool: ...
+        def pop(self) -> _T: ...
+        def remove(self, element: _T, /) -> None: ...
+        def symmetric_difference(self, s: Iterable[_T], /) -> set[_T]: ...
+        def symmetric_difference_update(self, s: Iterable[_T], /) -> None: ...
+        def union(self, *s: Iterable[_S]) -> set[_T | _S]: ...
+        def update(self, *s: Iterable[_T]) -> None: ...
+        def __len__(self) -> int: ...
+        def __contains__(self, o: object, /) -> bool: ...
+        def __iter__(self) -> Iterator[_T]: ...
+        def __and__(self, value: AbstractSet[object], /) -> set[_T]: ...
+        def __iand__(self, value: AbstractSet[object], /) -> Self: ...
+        def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
+        def __ior__(self, value: AbstractSet[_T], /) -> Self: ...  # type: ignore[override,misc]
+        def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ...
+        def __isub__(self, value: AbstractSet[object], /) -> Self: ...
+        def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
+        def __ixor__(self, value: AbstractSet[_T], /) -> Self: ...  # type: ignore[override,misc]
+        def __le__(self, value: AbstractSet[object], /) -> bool: ...
+        def __lt__(self, value: AbstractSet[object], /) -> bool: ...
+        def __ge__(self, value: AbstractSet[object], /) -> bool: ...
+        def __gt__(self, value: AbstractSet[object], /) -> bool: ...
+        def __eq__(self, value: object, /) -> bool: ...
+        def __rand__(self, value: AbstractSet[object], /) -> set[_T]: ...
+        def __ror__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...  # type: ignore[misc]
+        def __rsub__(self, value: AbstractSet[_T], /) -> set[_T]: ...
+        def __rxor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...  # type: ignore[misc]
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+
+    class SetProxy(_BaseSetProxy[_T]): ...
+
 class BaseListProxy(BaseProxy, MutableSequence[_T]):
     __builtins__: ClassVar[dict[str, Any]]
     def __len__(self) -> int: ...
@@ -273,6 +329,11 @@ class SyncManager(BaseManager):
     def list(self, sequence: Sequence[_T], /) -> ListProxy[_T]: ...
     @overload
     def list(self) -> ListProxy[Any]: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        def set(self, iterable: Iterable[_T], /) -> SetProxy[_T]: ...
+        @overload
+        def set(self) -> SetProxy[Any]: ...
 
 class RemoteError(Exception): ...
 
diff --git a/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi b/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi
index 4fcbfd99a8d0..5e53b055cc79 100644
--- a/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi
@@ -18,6 +18,9 @@ if sys.platform != "win32":
         def duplicate_for_child(self, fd: int) -> int: ...
         def poll(self, flag: int = 1) -> int | None: ...
         def wait(self, timeout: float | None = None) -> int | None: ...
+        if sys.version_info >= (3, 14):
+            def interrupt(self) -> None: ...
+
         def terminate(self) -> None: ...
         def kill(self) -> None: ...
         def close(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi
index 942e92ce530e..490ae195c20e 100644
--- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi
@@ -43,7 +43,8 @@ if sys.platform == "win32":
         def detach(self) -> int: ...
 
 else:
-    ACKNOWLEDGE: Final[bool]
+    if sys.version_info < (3, 14):
+        ACKNOWLEDGE: Final[bool]
 
     def recvfds(sock: socket, size: int) -> list[int]: ...
     def send_handle(conn: HasFileno, handle: int, destination_pid: Unused) -> None: ...
diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi
index d5b6384afd5e..ecb4a7ddec7d 100644
--- a/mypy/typeshed/stdlib/multiprocessing/util.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi
@@ -1,3 +1,4 @@
+import sys
 import threading
 from _typeshed import ConvertibleToInt, Incomplete, Unused
 from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence
@@ -22,14 +23,19 @@ __all__ = [
     "SUBWARNING",
 ]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["warn"]
+
 _T = TypeVar("_T")
 _R_co = TypeVar("_R_co", default=Any, covariant=True)
 
-NOTSET: Final[int]
-SUBDEBUG: Final[int]
-DEBUG: Final[int]
-INFO: Final[int]
-SUBWARNING: Final[int]
+NOTSET: Final = 0
+SUBDEBUG: Final = 5
+DEBUG: Final = 10
+INFO: Final = 20
+SUBWARNING: Final = 25
+if sys.version_info >= (3, 14):
+    WARNING: Final = 30
 
 LOGGER_NAME: Final[str]
 DEFAULT_LOGGING_FORMAT: Final[str]
@@ -37,6 +43,10 @@ DEFAULT_LOGGING_FORMAT: Final[str]
 def sub_debug(msg: object, *args: object) -> None: ...
 def debug(msg: object, *args: object) -> None: ...
 def info(msg: object, *args: object) -> None: ...
+
+if sys.version_info >= (3, 14):
+    def warn(msg: object, *args: object) -> None: ...
+
 def sub_warning(msg: object, *args: object) -> None: ...
 def get_logger() -> Logger: ...
 def log_to_stderr(level: _LoggingLevel | None = None) -> Logger: ...
diff --git a/mypy/typeshed/stdlib/pyexpat/errors.pyi b/mypy/typeshed/stdlib/pyexpat/errors.pyi
index cae4da089161..493ae0345604 100644
--- a/mypy/typeshed/stdlib/pyexpat/errors.pyi
+++ b/mypy/typeshed/stdlib/pyexpat/errors.pyi
@@ -49,3 +49,5 @@ if sys.version_info >= (3, 11):
     XML_ERROR_INVALID_ARGUMENT: Final[LiteralString]
     XML_ERROR_NO_BUFFER: Final[LiteralString]
     XML_ERROR_AMPLIFICATION_LIMIT_BREACH: Final[LiteralString]
+if sys.version_info >= (3, 14):
+    XML_ERROR_NOT_STARTED: Final[LiteralString]
diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi
index 42941b9e41fa..023547390273 100644
--- a/mypy/typeshed/stdlib/select.pyi
+++ b/mypy/typeshed/stdlib/select.pyi
@@ -148,6 +148,8 @@ if sys.platform == "linux":
     EPOLLWRBAND: int
     EPOLLWRNORM: int
     EPOLL_CLOEXEC: int
+    if sys.version_info >= (3, 14):
+        EPOLLWAKEUP: int
 
 if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32":
     # Solaris only
diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi
index ea2c29d4625f..c66d8fa128be 100644
--- a/mypy/typeshed/stdlib/shutil.pyi
+++ b/mypy/typeshed/stdlib/shutil.pyi
@@ -18,7 +18,6 @@ __all__ = [
     "rmtree",
     "Error",
     "SpecialFileError",
-    "ExecError",
     "make_archive",
     "get_archive_formats",
     "register_archive_format",
@@ -34,6 +33,8 @@ __all__ = [
     "SameFileError",
     "disk_usage",
 ]
+if sys.version_info < (3, 14):
+    __all__ += ["ExecError"]
 
 _StrOrBytesPathT = TypeVar("_StrOrBytesPathT", bound=StrOrBytesPath)
 _StrPathT = TypeVar("_StrPathT", bound=StrPath)
@@ -42,7 +43,13 @@ _BytesPathT = TypeVar("_BytesPathT", bound=BytesPath)
 class Error(OSError): ...
 class SameFileError(Error): ...
 class SpecialFileError(OSError): ...
-class ExecError(OSError): ...
+
+if sys.version_info >= (3, 14):
+    ExecError = RuntimeError  # Deprecated in Python 3.14; removal scheduled for Python 3.16
+
+else:
+    class ExecError(OSError): ...
+
 class ReadError(OSError): ...
 class RegistryError(Exception): ...
 
diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi
index 061932f0fac7..f321d14a792b 100644
--- a/mypy/typeshed/stdlib/socketserver.pyi
+++ b/mypy/typeshed/stdlib/socketserver.pyi
@@ -35,6 +35,7 @@ if sys.platform != "win32":
 _RequestType: TypeAlias = _socket | tuple[bytes, _socket]
 _AfUnixAddress: TypeAlias = str | ReadableBuffer  # address acceptable for an AF_UNIX socket
 _AfInetAddress: TypeAlias = tuple[str | bytes | bytearray, int]  # address acceptable for an AF_INET socket
+_AfInet6Address: TypeAlias = tuple[str | bytes | bytearray, int, int, int]  # address acceptable for an AF_INET6 socket
 
 # This can possibly be generic at some point:
 class BaseServer:
@@ -71,10 +72,10 @@ class TCPServer(BaseServer):
     socket_type: int
     if sys.version_info >= (3, 11):
         allow_reuse_port: bool
-    server_address: _AfInetAddress
+    server_address: _AfInetAddress | _AfInet6Address
     def __init__(
         self,
-        server_address: _AfInetAddress,
+        server_address: _AfInetAddress | _AfInet6Address,
         RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler],
         bind_and_activate: bool = True,
     ) -> None: ...
diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi
index c41a52b26d5a..a3921aa0fc3b 100644
--- a/mypy/typeshed/stdlib/sre_constants.pyi
+++ b/mypy/typeshed/stdlib/sre_constants.pyi
@@ -23,6 +23,8 @@ AT_LOCALE: dict[_NamedIntConstant, _NamedIntConstant]
 AT_UNICODE: dict[_NamedIntConstant, _NamedIntConstant]
 CH_LOCALE: dict[_NamedIntConstant, _NamedIntConstant]
 CH_UNICODE: dict[_NamedIntConstant, _NamedIntConstant]
+if sys.version_info >= (3, 14):
+    CH_NEGATE: dict[_NamedIntConstant, _NamedIntConstant]
 # flags
 if sys.version_info < (3, 13):
     SRE_FLAG_TEMPLATE: Final = 1
diff --git a/mypy/typeshed/stdlib/string/__init__.pyi b/mypy/typeshed/stdlib/string/__init__.pyi
index da752327d3f7..29fe27f39b80 100644
--- a/mypy/typeshed/stdlib/string/__init__.pyi
+++ b/mypy/typeshed/stdlib/string/__init__.pyi
@@ -32,12 +32,15 @@ whitespace: LiteralString
 
 def capwords(s: StrOrLiteralStr, sep: StrOrLiteralStr | None = None) -> StrOrLiteralStr: ...
 
-class Template(metaclass=type):
+class Template:
     template: str
     delimiter: ClassVar[str]
     idpattern: ClassVar[str]
     braceidpattern: ClassVar[str | None]
-    flags: ClassVar[RegexFlag]
+    if sys.version_info >= (3, 14):
+        flags: ClassVar[RegexFlag | None]
+    else:
+        flags: ClassVar[RegexFlag]
     pattern: ClassVar[Pattern[str]]
     def __init__(self, template: str) -> None: ...
     def substitute(self, mapping: Mapping[str, object] = {}, /, **kwds: object) -> str: ...
diff --git a/mypy/typeshed/stdlib/string/templatelib.pyi b/mypy/typeshed/stdlib/string/templatelib.pyi
index 01b95377a49c..324447f5f34c 100644
--- a/mypy/typeshed/stdlib/string/templatelib.pyi
+++ b/mypy/typeshed/stdlib/string/templatelib.pyi
@@ -1,4 +1,5 @@
 from collections.abc import Iterator
+from types import GenericAlias
 from typing import Any, Literal, final
 
 __all__ = ["Interpolation", "Template"]
@@ -11,6 +12,7 @@ class Template:  # TODO: consider making `Template` generic on `TypeVarTuple`
     def __new__(cls, *args: str | Interpolation) -> Template: ...
     def __iter__(self) -> Iterator[str | Interpolation]: ...
     def __add__(self, other: Template | str) -> Template: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
     @property
     def values(self) -> tuple[Any, ...]: ...  # Tuple of interpolation values, which can have any type
 
@@ -26,3 +28,4 @@ class Interpolation:
     def __new__(
         cls, value: Any, expression: str, conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
     ) -> Interpolation: ...
+    def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index c153ca499898..2a4657f86ce1 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -1021,7 +1021,7 @@ class Tk(Misc, Wm):
     def globalgetvar(self, *args, **kwargs): ...
     def globalsetvar(self, *args, **kwargs): ...
     def globalunsetvar(self, *args, **kwargs): ...
-    def interpaddr(self): ...
+    def interpaddr(self) -> int: ...
     def loadtk(self) -> None: ...
     def record(self, script, /): ...
     if sys.version_info < (3, 11):
diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi
index a2ab728de943..9c62c64e718a 100644
--- a/mypy/typeshed/stdlib/turtle.pyi
+++ b/mypy/typeshed/stdlib/turtle.pyi
@@ -1,5 +1,7 @@
 import sys
-from collections.abc import Callable, Sequence
+from _typeshed import StrPath
+from collections.abc import Callable, Generator, Sequence
+from contextlib import contextmanager
 from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar
 from typing import Any, ClassVar, Literal, TypedDict, overload
 from typing_extensions import Self, TypeAlias
@@ -128,6 +130,9 @@ __all__ = [
     "Terminator",
 ]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["fill", "no_animation", "poly", "save"]
+
 if sys.version_info >= (3, 12):
     __all__ += ["teleport"]
 
@@ -231,6 +236,10 @@ class TurtleScreen(TurtleScreenBase):
     def delay(self, delay: None = None) -> int: ...
     @overload
     def delay(self, delay: int) -> None: ...
+    if sys.version_info >= (3, 14):
+        @contextmanager
+        def no_animation(self) -> Generator[None]: ...
+
     def update(self) -> None: ...
     def window_width(self) -> int: ...
     def window_height(self) -> int: ...
@@ -249,6 +258,8 @@ class TurtleScreen(TurtleScreenBase):
     # Looks like if self.cv is not a ScrolledCanvas, this could return a tuple as well
     @overload
     def screensize(self, canvwidth: int, canvheight: int, bg: _Color | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        def save(self, filename: StrPath, *, overwrite: bool = False) -> None: ...
     onscreenclick = onclick
     resetscreen = reset
     clearscreen = clear
@@ -428,12 +439,20 @@ class RawTurtle(TPen, TNavigator):  # type: ignore[misc]  # Conflicting methods
     def clearstamp(self, stampid: int | tuple[int, ...]) -> None: ...
     def clearstamps(self, n: int | None = None) -> None: ...
     def filling(self) -> bool: ...
+    if sys.version_info >= (3, 14):
+        @contextmanager
+        def fill(self) -> Generator[None]: ...
+
     def begin_fill(self) -> None: ...
     def end_fill(self) -> None: ...
     def dot(self, size: int | None = None, *color: _Color) -> None: ...
     def write(
         self, arg: object, move: bool = False, align: str = "left", font: tuple[str, int, str] = ("Arial", 8, "normal")
     ) -> None: ...
+    if sys.version_info >= (3, 14):
+        @contextmanager
+        def poly(self) -> Generator[None]: ...
+
     def begin_poly(self) -> None: ...
     def end_poly(self) -> None: ...
     def get_poly(self) -> _PolygonCoords | None: ...
@@ -516,6 +535,11 @@ def tracer(n: int, delay: int | None = None) -> None: ...
 def delay(delay: None = None) -> int: ...
 @overload
 def delay(delay: int) -> None: ...
+
+if sys.version_info >= (3, 14):
+    @contextmanager
+    def no_animation() -> Generator[None]: ...
+
 def update() -> None: ...
 def window_width() -> int: ...
 def window_height() -> int: ...
@@ -534,6 +558,9 @@ def screensize(canvwidth: None = None, canvheight: None = None, bg: None = None)
 @overload
 def screensize(canvwidth: int, canvheight: int, bg: _Color | None = None) -> None: ...
 
+if sys.version_info >= (3, 14):
+    def save(filename: StrPath, *, overwrite: bool = False) -> None: ...
+
 onscreenclick = onclick
 resetscreen = reset
 clearscreen = clear
@@ -705,10 +732,20 @@ def stamp() -> Any: ...
 def clearstamp(stampid: int | tuple[int, ...]) -> None: ...
 def clearstamps(n: int | None = None) -> None: ...
 def filling() -> bool: ...
+
+if sys.version_info >= (3, 14):
+    @contextmanager
+    def fill() -> Generator[None]: ...
+
 def begin_fill() -> None: ...
 def end_fill() -> None: ...
 def dot(size: int | None = None, *color: _Color) -> None: ...
 def write(arg: object, move: bool = False, align: str = "left", font: tuple[str, int, str] = ("Arial", 8, "normal")) -> None: ...
+
+if sys.version_info >= (3, 14):
+    @contextmanager
+    def poly() -> Generator[None]: ...
+
 def begin_poly() -> None: ...
 def end_poly() -> None: ...
 def get_poly() -> _PolygonCoords | None: ...
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index 1163d71d2c95..d9f8e8756833 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -151,7 +151,7 @@ class CodeType:
     def co_firstlineno(self) -> int: ...
     if sys.version_info >= (3, 10):
         @property
-        @deprecated("Will be removed in Python 3.14. Use the co_lines() method instead.")
+        @deprecated("Will be removed in Python 3.15. Use the co_lines() method instead.")
         def co_lnotab(self) -> bytes: ...
     else:
         @property
@@ -171,6 +171,8 @@ class CodeType:
         @property
         def co_qualname(self) -> str: ...
         def co_positions(self) -> Iterable[tuple[int | None, int | None, int | None, int | None]]: ...
+    if sys.version_info >= (3, 14):
+        def co_branches(self) -> Iterator[tuple[int, int, int]]: ...
 
     if sys.version_info >= (3, 11):
         def __new__(
@@ -480,6 +482,10 @@ class MethodType:
     def __qualname__(self) -> str: ...  # inherited from the added function
     def __new__(cls, func: Callable[..., Any], instance: object, /) -> Self: ...
     def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
+
+    if sys.version_info >= (3, 13):
+        def __get__(self, instance: object, owner: type | None = None, /) -> Self: ...
+
     def __eq__(self, value: object, /) -> bool: ...
     def __hash__(self) -> int: ...
 
@@ -580,6 +586,9 @@ class FrameType:
     f_trace_lines: bool
     f_trace_opcodes: bool
     def clear(self) -> None: ...
+    if sys.version_info >= (3, 14):
+        @property
+        def f_generator(self) -> GeneratorType[Any, Any, Any] | CoroutineType[Any, Any, Any] | None: ...
 
 @final
 class GetSetDescriptorType:
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index 5aa85543ed2c..79ab9eee924f 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -797,11 +797,15 @@ class MutableMapping(Mapping[_KT, _VT]):
     # -- weakref.WeakValueDictionary.__ior__
     # -- weakref.WeakKeyDictionary.__ior__
     @overload
-    def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ...
+    def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ...
     @overload
-    def update(self, m: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ...
+    def update(self: Mapping[str, _VT], m: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ...
     @overload
-    def update(self, **kwargs: _VT) -> None: ...
+    def update(self, m: Iterable[tuple[_KT, _VT]], /) -> None: ...
+    @overload
+    def update(self: Mapping[str, _VT], m: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ...
+    @overload
+    def update(self: Mapping[str, _VT], **kwargs: _VT) -> None: ...
 
 Text = str
 
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index 37f8e8ba6a4b..07cd57ebc18f 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -110,6 +110,8 @@ __all__ = [
     "SupportsIndex",
     "SupportsInt",
     "SupportsRound",
+    "Reader",
+    "Writer",
     # One-off things.
     "Annotated",
     "assert_never",
@@ -136,6 +138,7 @@ __all__ = [
     "overload",
     "override",
     "Protocol",
+    "Sentinel",
     "reveal_type",
     "runtime",
     "runtime_checkable",
@@ -199,6 +202,7 @@ _T = _TypeVar("_T")
 _F = _TypeVar("_F", bound=Callable[..., Any])
 _TC = _TypeVar("_TC", bound=type[object])
 _T_co = _TypeVar("_T_co", covariant=True)  # Any type covariant containers.
+_T_contra = _TypeVar("_T_contra", contravariant=True)
 
 class _Final: ...  # This should be imported from typing but that breaks pytype
 
@@ -446,6 +450,19 @@ else:
         @abc.abstractmethod
         def __round__(self, ndigits: int, /) -> _T_co: ...
 
+if sys.version_info >= (3, 14):
+    from io import Reader as Reader, Writer as Writer
+else:
+    @runtime_checkable
+    class Reader(Protocol[_T_co]):
+        @abc.abstractmethod
+        def read(self, size: int = ..., /) -> _T_co: ...
+
+    @runtime_checkable
+    class Writer(Protocol[_T_contra]):
+        @abc.abstractmethod
+        def write(self, data: _T_contra, /) -> int: ...
+
 if sys.version_info >= (3, 13):
     from types import CapsuleType as CapsuleType
     from typing import (
@@ -670,6 +687,16 @@ else:
         globals: Mapping[str, Any] | None = None,  # value types depend on the key
         locals: Mapping[str, Any] | None = None,  # value types depend on the key
         type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None,
-        format: Format = Format.VALUE,  # noqa: Y011
+        format: Format | None = None,
         _recursive_guard: Container[str] = ...,
     ) -> AnnotationForm: ...
+
+# PEP 661
+class Sentinel:
+    def __init__(self, name: str, repr: str | None = None) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __or__(self, other: Any) -> UnionType: ...  # other can be any type form legal for unions
+        def __ror__(self, other: Any) -> UnionType: ...  # other can be any type form legal for unions
+    else:
+        def __or__(self, other: Any) -> _SpecialForm: ...  # other can be any type form legal for unions
+        def __ror__(self, other: Any) -> _SpecialForm: ...  # other can be any type form legal for unions
diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
index a2eecc5a7864..ebe92d28c74d 100644
--- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
@@ -1,3 +1,4 @@
+import sys
 from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co
 from collections.abc import Iterable
 from typing import Protocol
@@ -10,7 +11,7 @@ from xml.sax._exceptions import (
     SAXReaderNotAvailable as SAXReaderNotAvailable,
 )
 from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler
-from xml.sax.xmlreader import XMLReader
+from xml.sax.xmlreader import InputSource as InputSource, XMLReader
 
 class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]):
     def close(self) -> None: ...
@@ -23,3 +24,19 @@ def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ...
 def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ...
 def parseString(string: ReadableBuffer | str, handler: ContentHandler, errorHandler: ErrorHandler | None = ...) -> None: ...
 def _create_parser(parser_name: str) -> XMLReader: ...
+
+if sys.version_info >= (3, 14):
+    __all__ = [
+        "ContentHandler",
+        "ErrorHandler",
+        "InputSource",
+        "SAXException",
+        "SAXNotRecognizedException",
+        "SAXNotSupportedException",
+        "SAXParseException",
+        "SAXReaderNotAvailable",
+        "default_parser_list",
+        "make_parser",
+        "parse",
+        "parseString",
+    ]
diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi
index ede732c0f86a..27c1ef0246c7 100644
--- a/mypy/typeshed/stdlib/zipfile/__init__.pyi
+++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi
@@ -24,6 +24,9 @@ __all__ = [
     "LargeZipFile",
 ]
 
+if sys.version_info >= (3, 14):
+    __all__ += ["ZIP_ZSTANDARD"]
+
 # TODO: use TypeAlias for these two when mypy bugs are fixed
 # https://github.com/python/mypy/issues/16581
 _DateTuple = tuple[int, int, int, int, int, int]  # noqa: Y026
@@ -251,6 +254,9 @@ class ZipFile:
     ) -> None: ...
     if sys.version_info >= (3, 11):
         def mkdir(self, zinfo_or_directory_name: str | ZipInfo, mode: int = 0o777) -> None: ...
+    if sys.version_info >= (3, 14):
+        @property
+        def data_offset(self) -> int | None: ...
 
     def __del__(self) -> None: ...
 
@@ -361,10 +367,21 @@ else:
 
 def is_zipfile(filename: StrOrBytesPath | _SupportsReadSeekTell) -> bool: ...
 
-ZIP_STORED: Final[int]
-ZIP_DEFLATED: Final[int]
 ZIP64_LIMIT: Final[int]
 ZIP_FILECOUNT_LIMIT: Final[int]
 ZIP_MAX_COMMENT: Final[int]
-ZIP_BZIP2: Final[int]
-ZIP_LZMA: Final[int]
+
+ZIP_STORED: Final = 0
+ZIP_DEFLATED: Final = 8
+ZIP_BZIP2: Final = 12
+ZIP_LZMA: Final = 14
+if sys.version_info >= (3, 14):
+    ZIP_ZSTANDARD: Final = 93
+
+DEFAULT_VERSION: Final[int]
+ZIP64_VERSION: Final[int]
+BZIP2_VERSION: Final[int]
+LZMA_VERSION: Final[int]
+if sys.version_info >= (3, 14):
+    ZSTANDARD_VERSION: Final[int]
+MAX_EXTRACT_VERSION: Final[int]
diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi
index 3e94c681b7a2..4aab318e7c71 100644
--- a/mypy/typeshed/stdlib/zipimport.pyi
+++ b/mypy/typeshed/stdlib/zipimport.pyi
@@ -1,10 +1,14 @@
 import sys
 from _typeshed import StrOrBytesPath
-from importlib.abc import ResourceReader
 from importlib.machinery import ModuleSpec
 from types import CodeType, ModuleType
 from typing_extensions import deprecated
 
+if sys.version_info >= (3, 10):
+    from importlib.readers import ZipReader
+else:
+    from importlib.abc import ResourceReader
+
 if sys.version_info >= (3, 10):
     from _frozen_importlib_external import _LoaderBasics
 else:
@@ -29,7 +33,13 @@ class zipimporter(_LoaderBasics):
     def get_code(self, fullname: str) -> CodeType: ...
     def get_data(self, pathname: str) -> bytes: ...
     def get_filename(self, fullname: str) -> str: ...
-    def get_resource_reader(self, fullname: str) -> ResourceReader | None: ...  # undocumented
+    if sys.version_info >= (3, 14):
+        def get_resource_reader(self, fullname: str) -> ZipReader: ...  # undocumented
+    elif sys.version_info >= (3, 10):
+        def get_resource_reader(self, fullname: str) -> ZipReader | None: ...  # undocumented
+    else:
+        def get_resource_reader(self, fullname: str) -> ResourceReader | None: ...  # undocumented
+
     def get_source(self, fullname: str) -> str | None: ...
     def is_package(self, fullname: str) -> bool: ...
     @deprecated("Deprecated since 3.10; use exec_module() instead")

From c1ff950d201245e9f3acbe51dc1e227fd81187d1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 2 Jun 2025 13:25:41 +0100
Subject: [PATCH 1334/1617] [mypyc] Test function nesting with async functions
 (#19203)

This only adds tests.
---
 mypyc/test-data/run-async.test | 82 ++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 58b690a944af..11ce67077270 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -561,3 +561,85 @@ def test_bool() -> None:
 
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
+
+[case testRunAsyncNestedFunctions]
+import asyncio
+from typing import cast, Iterator
+
+from testutil import assertRaises
+
+def normal_contains_async_def(x: int) -> int:
+    async def f(y: int) -> int:
+        return x + y
+
+    return 5 + cast(int, asyncio.run(f(6)))
+
+def test_def_contains_async_def() -> None:
+    assert normal_contains_async_def(3) == 14
+
+async def inc(x: int) -> int:
+    return x + 1
+
+async def async_def_contains_normal(x: int) -> int:
+    def nested(y: int, z: int) -> int:
+        return x + y + z
+
+    a = x
+    a += nested((await inc(3)), (await inc(4)))
+    return a
+
+def test_async_def_contains_normal() -> None:
+    assert normal_contains_async_def(2) == (2 + 2 + 4 + 5)
+
+async def async_def_contains_async_def(x: int) -> int:
+    async def f(y: int) -> int:
+        return (await inc(x)) + (await inc(y))
+
+    return (await f(1)) + (await f(2))
+
+def test_async_def_contains_async_def() -> None:
+    assert asyncio.run(async_def_contains_async_def(3)) == (3 + 1 + 1 + 1) + (3 + 1 + 2 + 1)
+
+async def async_def_contains_generator(x: int) -> tuple[int, int, int]:
+    def gen(y: int) -> Iterator[int]:
+        yield x + 1
+        yield x + y
+
+    it = gen(4)
+    res = x + 10, next(it), next(it)
+
+    with assertRaises(StopIteration):
+        next(it)
+
+    return res
+
+def test_async_def_contains_generator() -> None:
+    assert asyncio.run(async_def_contains_generator(3)) == (13, 4, 7)
+
+def generator_contains_async_def(x: int) -> Iterator[int]:
+    async def f(y: int) -> int:
+        return (await inc(x)) + (await inc(y))
+
+    yield cast(int, asyncio.run(f(2)))
+    yield cast(int, asyncio.run(f(3)))
+    yield x + 10
+
+def test_generator_contains_async_def() -> None:
+    assert list(generator_contains_async_def(5)) == [6 + 3, 6 + 4, 15]
+
+async def async_def_contains_two_nested_functions(x: int, y: int) -> tuple[int, int]:
+    def f(a: int) -> int:
+        return x + a
+
+    def g(b: int, c: int) -> int:
+        return y + b + c
+
+    return (await inc(f(3))), (await inc(g(4, 10)))
+
+def test_async_def_contains_two_nested_functions() -> None:
+    assert asyncio.run(async_def_contains_two_nested_functions(5, 7)) == (
+        (5 + 3 + 1), (7 + 4 + 10 + 1)
+    )
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...

From 1b4ef345603f224b942efd2dc9175913b35c5a74 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Mon, 2 Jun 2025 15:54:56 +0200
Subject: [PATCH 1335/1617] Add classifier for Python 3.14 (#19199)

Similar to https://github.com/python/mypy/pull/17891 add the classifier
for 3.14. The tests all pass and the next release is unlikely to happen
before 3.14.0b3. Note though, not all features might be supported just
yet.
---
 pyproject.toml              | 1 +
 test-data/unit/cmdline.test | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 8a1177f60009..1870e0931407 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,6 +42,7 @@ classifiers = [
   "Programming Language :: Python :: 3.11",
   "Programming Language :: Python :: 3.12",
   "Programming Language :: Python :: 3.13",
+  "Programming Language :: Python :: 3.14",
   "Topic :: Software Development",
   "Typing :: Typed",
 ]
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index c65f55620d67..2db4451adc9a 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -433,11 +433,11 @@ mypy: error: Mypy no longer supports checking Python 2 code. Consider pinning to
 python_version = 3.9
 [out]
 
-[case testPythonVersionAccepted313]
+[case testPythonVersionAccepted314]
 # cmd: mypy -c pass
 [file mypy.ini]
 \[mypy]
-python_version = 3.13
+python_version = 3.14
 [out]
 
 -- This should be a dumping ground for tests of plugins that are sensitive to

From 1e372f402782a3030321b4b4359ed8b0a10992af Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 2 Jun 2025 15:07:15 +0100
Subject: [PATCH 1336/1617] [mypyc] Don't simplify module prefixes if using
 separate compilation (#19206)

Mypyc shortens module prefixes to generate nicer, shorter C names.
However, this assumes that we know all possible module prefixes. When
doing separate compilation, we only have access to a subset of possible
module prefixes, so there's no good way to shorten module prefixes while
keeping names unique.
---
 mypyc/codegen/emitmodule.py |  5 ++++-
 mypyc/namegen.py            | 16 +++++++++++++---
 mypyc/test/test_namegen.py  | 14 ++++++++++++++
 3 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 8474be62579d..36cc57fa2af6 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -306,7 +306,10 @@ def compile_ir_to_c(
         for source in sources
     }
 
-    names = NameGenerator([[source.module for source in sources] for sources, _ in groups])
+    names = NameGenerator(
+        [[source.module for source in sources] for sources, _ in groups],
+        separate=compiler_options.separate,
+    )
 
     # Generate C code for each compilation group. Each group will be
     # compiled into a separate extension module.
diff --git a/mypyc/namegen.py b/mypyc/namegen.py
index 5f57fa9a70ed..1e0553102175 100644
--- a/mypyc/namegen.py
+++ b/mypyc/namegen.py
@@ -34,20 +34,30 @@ class NameGenerator:
 
     The generated should be internal to a build and thus the mapping is
     arbitrary. Just generating names '1', '2', ... would be correct,
-    though not very usable.
+    though not very usable. The generated names may be visible in CPU
+    profiles and when debugging using native debuggers.
     """
 
-    def __init__(self, groups: Iterable[list[str]]) -> None:
+    def __init__(self, groups: Iterable[list[str]], *, separate: bool = False) -> None:
         """Initialize with a list of modules in each compilation group.
 
         The names of modules are used to shorten names referring to
         modules, for convenience. Arbitrary module
         names are supported for generated names, but uncompiled modules
         will use long names.
+
+        If separate is True, assume separate compilation. This implies
+        that we don't have knowledge of all sources that will be linked
+        together. In this case we won't trim module prefixes, since we
+        don't have enough information to determine common module prefixes.
         """
         self.module_map: dict[str, str] = {}
         for names in groups:
-            self.module_map.update(make_module_translation_map(names))
+            if not separate:
+                self.module_map.update(make_module_translation_map(names))
+            else:
+                for name in names:
+                    self.module_map[name] = name + "."
         self.translations: dict[tuple[str, str], str] = {}
         self.used_names: set[str] = set()
 
diff --git a/mypyc/test/test_namegen.py b/mypyc/test/test_namegen.py
index f88edbd00dce..a4688747037f 100644
--- a/mypyc/test/test_namegen.py
+++ b/mypyc/test/test_namegen.py
@@ -52,3 +52,17 @@ def test_name_generator(self) -> None:
         assert g.private_name("foo", "C_x_y") == "foo___C_x_y"
         assert g.private_name("foo", "C_x_y") == "foo___C_x_y"
         assert g.private_name("foo", "___") == "foo______3_"
+
+        g = NameGenerator([["foo.zar"]])
+        assert g.private_name("foo.zar", "f") == "f"
+
+    def test_name_generator_with_separate(self) -> None:
+        g = NameGenerator([["foo", "foo.zar"]], separate=True)
+        assert g.private_name("foo", "f") == "foo___f"
+        assert g.private_name("foo", "C.x.y") == "foo___C___x___y"
+        assert g.private_name("foo.zar", "C.x.y") == "foo___zar___C___x___y"
+        assert g.private_name("foo", "C.x_y") == "foo___C___x_y"
+        assert g.private_name("foo", "___") == "foo______3_"
+
+        g = NameGenerator([["foo.zar"]], separate=True)
+        assert g.private_name("foo.zar", "f") == "foo___zar___f"

From e50b401a8afdffbf4164c68b6391434972508b73 Mon Sep 17 00:00:00 2001
From: Advait Dixit <48302999+advait-dixit@users.noreply.github.com>
Date: Mon, 2 Jun 2025 07:20:25 -0700
Subject: [PATCH 1337/1617] [mypyc] Fixing condition for handling user-defined
 __del__ (#19188)

Fixes #19175.

Conditions for generating and invoking `del` method were not consistent.

As things currently stand, this pull request fixes the crash. However, for classes that derive from Python built-ins, user-defined `__del__` will not be invoked.
---
 mypyc/codegen/emitclass.py       |  6 +++---
 mypyc/test-data/run-classes.test | 21 +++++++++++++++++++++
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index c5191e5fb939..9cb9074b9fc4 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -304,9 +304,6 @@ def emit_line() -> None:
         emit_line()
         generate_dealloc_for_class(cl, dealloc_name, clear_name, bool(del_method), emitter)
         emit_line()
-        if del_method:
-            generate_finalize_for_class(del_method, finalize_name, emitter)
-            emit_line()
 
         if cl.allow_interpreted_subclasses:
             shadow_vtable_name: str | None = generate_vtables(
@@ -317,6 +314,9 @@ def emit_line() -> None:
             shadow_vtable_name = None
         vtable_name = generate_vtables(cl, vtable_setup_name, vtable_name, emitter, shadow=False)
         emit_line()
+    if del_method:
+        generate_finalize_for_class(del_method, finalize_name, emitter)
+        emit_line()
     if needs_getseters:
         generate_getseter_declarations(cl, emitter)
         emit_line()
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 97bc063dd8ea..288f281c0a94 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2754,6 +2754,21 @@ def test_function():
     assert(isinstance(d.fitem, ForwardDefinedClass))
     assert(isinstance(d.fitems, ForwardDefinedClass))
 
+[case testDelForDictSubclass-xfail]
+# The crash in issue mypy#19175 is fixed.
+# But, for classes that derive from built-in Python classes, user-defined __del__ method is not
+# being invoked.
+class DictSubclass(dict):
+    def __del__(self):
+        print("deleting DictSubclass...")
+
+[file driver.py]
+import native
+native.DictSubclass()
+
+[out]
+deleting DictSubclass...
+
 [case testDel]
 class A:
     def __del__(self):
@@ -2774,6 +2789,12 @@ class C(B):
 class D(A):
     pass
 
+# Just make sure that this class compiles (see issue mypy#19175). testDelForDictSubclass tests for
+# correct output.
+class NormDict(dict):
+    def __del__(self) -> None:
+        pass
+
 [file driver.py]
 import native
 native.C()

From 4934c2b0a6827595514f5957a3d3a71db9de2cc6 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 2 Jun 2025 16:09:09 +0100
Subject: [PATCH 1338/1617] [mypyc] Merge generator and environment classes in
 simple cases (#19207)

Mypyc used to always compile a generator or an async def into two
classes: a generator and an environment. Now we combine these two
classes in simple cases where it's clearly okay to do it (when there is
no nesting). We could probably extend it to other use cases as well,
including some nested functions, but this is a start.

This improves performance by reducing the number of instances that will
be allocated. Also access to the environment object is slightly faster,
though this is probably relatively minor. This helps calling async defs
in particular, since they typically yield only a single value, so the
objects are not used much until they are freed. Also generators that
only yield a small number of values benefit from this.

The existing test cases provide decent test coverage. I previously added
some additional tests in anticipation of this change.

This also reduces the amount of C code generated when compiling async
defs and generators.

This speeds up this micro-benchmark on the order of 20%:
```py
import asyncio
from time import time

async def inc(x: int) -> int:
    x = 1
    return x + 1


async def bench(n: int) -> int:
    x = 0
    for i in range(n):
        x = await inc(x)
    return x

asyncio.run(bench(1000))

t0 = time()
asyncio.run(bench(1000 * 1000 * 200))
print(time() - t0)
```
---
 mypyc/irbuild/context.py   |  5 ++++
 mypyc/irbuild/env_class.py |  3 ++-
 mypyc/irbuild/function.py  |  4 ++-
 mypyc/irbuild/generator.py | 55 +++++++++++++++++++++++++-------------
 mypyc/transform/spill.py   | 12 ++++++---
 5 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/mypyc/irbuild/context.py b/mypyc/irbuild/context.py
index a740f0b821d9..8d35c0ce2599 100644
--- a/mypyc/irbuild/context.py
+++ b/mypyc/irbuild/context.py
@@ -95,6 +95,11 @@ def curr_env_reg(self) -> Value:
         assert self._curr_env_reg is not None
         return self._curr_env_reg
 
+    def can_merge_generator_and_env_classes(self) -> bool:
+        # In simple cases we can place the environment into the generator class,
+        # instead of having two separate classes.
+        return self.is_generator and not self.is_nested and not self.contains_nested
+
 
 class ImplicitClass:
     """Contains information regarding implicitly generated classes.
diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py
index ab786fe71dda..b0909f86686a 100644
--- a/mypyc/irbuild/env_class.py
+++ b/mypyc/irbuild/env_class.py
@@ -58,7 +58,8 @@ class is generated, the function environment has not yet been
 
 def finalize_env_class(builder: IRBuilder) -> None:
     """Generate, instantiate, and set up the environment of an environment class."""
-    instantiate_env_class(builder)
+    if not builder.fn_info.can_merge_generator_and_env_classes():
+        instantiate_env_class(builder)
 
     # Iterate through the function arguments and replace local definitions (using registers)
     # that were previously added to the environment with references to the function's
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index cb9a1a3dc4a3..dbebc350bb6c 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -243,7 +243,9 @@ def c() -> None:
     # are free in their nested functions. Generator functions need an environment class to
     # store a variable denoting the next instruction to be executed when the __next__ function
     # is called, along with all the variables inside the function itself.
-    if contains_nested or is_generator:
+    if contains_nested or (
+        is_generator and not builder.fn_info.can_merge_generator_and_env_classes()
+    ):
         setup_env_class(builder)
 
     if is_nested or in_non_ext:
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 74c8d27a6324..9dea0ee5f7c2 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -64,8 +64,14 @@ def gen_generator_func(
     setup_generator_class(builder)
     load_env_registers(builder)
     gen_arg_defaults(builder)
-    finalize_env_class(builder)
-    builder.add(Return(instantiate_generator_class(builder)))
+    if builder.fn_info.can_merge_generator_and_env_classes():
+        gen = instantiate_generator_class(builder)
+        builder.fn_info._curr_env_reg = gen
+        finalize_env_class(builder)
+    else:
+        finalize_env_class(builder)
+        gen = instantiate_generator_class(builder)
+    builder.add(Return(gen))
 
     args, _, blocks, ret_type, fn_info = builder.leave()
     func_ir, func_reg = gen_func_ir(args, blocks, fn_info)
@@ -122,22 +128,27 @@ def instantiate_generator_class(builder: IRBuilder) -> Value:
     fitem = builder.fn_info.fitem
     generator_reg = builder.add(Call(builder.fn_info.generator_class.ir.ctor, [], fitem.line))
 
-    # Get the current environment register. If the current function is nested, then the
-    # generator class gets instantiated from the callable class' '__call__' method, and hence
-    # we use the callable class' environment register. Otherwise, we use the original
-    # function's environment register.
-    if builder.fn_info.is_nested:
-        curr_env_reg = builder.fn_info.callable_class.curr_env_reg
+    if builder.fn_info.can_merge_generator_and_env_classes():
+        # Set the generator instance to the initial state (zero).
+        zero = Integer(0)
+        builder.add(SetAttr(generator_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line))
     else:
-        curr_env_reg = builder.fn_info.curr_env_reg
-
-    # Set the generator class' environment attribute to point at the environment class
-    # defined in the current scope.
-    builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line))
-
-    # Set the generator class' environment class' NEXT_LABEL_ATTR_NAME attribute to 0.
-    zero = Integer(0)
-    builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line))
+        # Get the current environment register. If the current function is nested, then the
+        # generator class gets instantiated from the callable class' '__call__' method, and hence
+        # we use the callable class' environment register. Otherwise, we use the original
+        # function's environment register.
+        if builder.fn_info.is_nested:
+            curr_env_reg = builder.fn_info.callable_class.curr_env_reg
+        else:
+            curr_env_reg = builder.fn_info.curr_env_reg
+
+        # Set the generator class' environment attribute to point at the environment class
+        # defined in the current scope.
+        builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line))
+
+        # Set the generator instance's environment to the initial state (zero).
+        zero = Integer(0)
+        builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line))
     return generator_reg
 
 
@@ -145,7 +156,10 @@ def setup_generator_class(builder: IRBuilder) -> ClassIR:
     name = f"{builder.fn_info.namespaced_name()}_gen"
 
     generator_class_ir = ClassIR(name, builder.module_name, is_generated=True)
-    generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class)
+    if builder.fn_info.can_merge_generator_and_env_classes():
+        builder.fn_info.env_class = generator_class_ir
+    else:
+        generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class)
     generator_class_ir.mro = [generator_class_ir]
 
     builder.classes.append(generator_class_ir)
@@ -392,7 +406,10 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None:
     cls.send_arg_reg = exc_arg
 
     cls.self_reg = builder.read(self_target, fitem.line)
-    cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1])
+    if builder.fn_info.can_merge_generator_and_env_classes():
+        cls.curr_env_reg = cls.self_reg
+    else:
+        cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1])
 
     # Define a variable representing the label to go to the next time
     # the '__next__' function of the generator is called, and add it
diff --git a/mypyc/transform/spill.py b/mypyc/transform/spill.py
index 3c014ca2c0da..d92dd661e7eb 100644
--- a/mypyc/transform/spill.py
+++ b/mypyc/transform/spill.py
@@ -28,18 +28,24 @@ def insert_spills(ir: FuncIR, env: ClassIR) -> None:
     # TODO: Actually for now, no Registers at all -- we keep the manual spills
     entry_live = {op for op in entry_live if not isinstance(op, Register)}
 
-    ir.blocks = spill_regs(ir.blocks, env, entry_live, live)
+    ir.blocks = spill_regs(ir.blocks, env, entry_live, live, ir.arg_regs[0])
 
 
 def spill_regs(
-    blocks: list[BasicBlock], env: ClassIR, to_spill: set[Value], live: AnalysisResult[Value]
+    blocks: list[BasicBlock],
+    env: ClassIR,
+    to_spill: set[Value],
+    live: AnalysisResult[Value],
+    self_reg: Register,
 ) -> list[BasicBlock]:
+    env_reg: Value
     for op in blocks[0].ops:
         if isinstance(op, GetAttr) and op.attr == "__mypyc_env__":
             env_reg = op
             break
     else:
-        raise AssertionError("could not find __mypyc_env__")
+        # Environment has been merged into generator object
+        env_reg = self_reg
 
     spill_locs = {}
     for i, val in enumerate(to_spill):

From 5727d33012d5ce786423f2abb1e091fb54a70976 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 3 Jun 2025 08:18:05 +0100
Subject: [PATCH 1339/1617] Fix crash on invalid property inside its own body
 (#19208)

Fixes https://github.com/python/mypy/issues/19205
---
 mypy/checkmember.py               |  4 ++++
 test-data/unit/check-classes.test | 13 +++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index b89452d90392..86e1dc06fc25 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -981,6 +981,10 @@ def expand_and_bind_callable(
     assert isinstance(expanded, CallableType)
     if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
         # TODO: use check_call() to infer better type, same as for __set__().
+        if not expanded.arg_types:
+            # This can happen when accessing invalid property from its own body,
+            # error will be reported elsewhere.
+            return AnyType(TypeOfAny.from_error)
         return expanded.arg_types[0]
     else:
         return expanded.ret_type
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index f8b841185fc6..054ba0708ce3 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -8726,3 +8726,16 @@ class Fields:
 reveal_type(Fields.bool_f)  # N: Revealed type is "__main__.BoolField"
 reveal_type(Fields.int_f)  # N: Revealed type is "__main__.NumField"
 reveal_type(Fields.custom_f)  # N: Revealed type is "__main__.AnyField[__main__.Custom]"
+
+[case testRecursivePropertyWithInvalidSetterNoCrash]
+class NoopPowerResource:
+    _hardware_type: int
+
+    @property
+    def hardware_type(self) -> int:
+        return self._hardware_type
+
+    @hardware_type.setter
+    def hardware_type(self) -> None:  # E: Invalid property setter signature
+        self.hardware_type = None  # Note: intentionally recursive
+[builtins fixtures/property.pyi]

From 1f339c035aaafd618ee3f78b638d0929b2f3e470 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Tue, 3 Jun 2025 05:59:42 -0700
Subject: [PATCH 1340/1617] Add regression test for dataclass typeguard
 (#19214)

Closes #19139
---
 test-data/unit/check-dataclasses.test |  9 +++++++++
 test-data/unit/check-typeguard.test   | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index 8117e3a96938..cfd14ff07b3f 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -2609,3 +2609,12 @@ class B2(B1):  # E: A NamedTuple cannot be a dataclass
     pass
 
 [builtins fixtures/tuple.pyi]
+
+[case testDataclassesTypeGuard]
+import dataclasses
+
+raw_target: object
+
+if isinstance(raw_target, type) and dataclasses.is_dataclass(raw_target):
+    reveal_type(raw_target)  # N: Revealed type is "type[dataclasses.DataclassInstance]"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index e17a7f80e756..0b512962b8d1 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -777,3 +777,22 @@ def handle(model: Model) -> int:
         return process_model(model)
     return 0
 [builtins fixtures/tuple.pyi]
+
+
+[case testOverloadedTypeGuardType]
+from __future__ import annotations
+from typing_extensions import TypeIs, Never, overload
+
+class X: ...
+
+@overload  # E: An overloaded function outside a stub file must have an implementation
+def is_xlike(obj: Never) -> TypeIs[X | type[X]]: ...  # type: ignore
+@overload
+def is_xlike(obj: type) -> TypeIs[type[X]]: ...
+@overload
+def is_xlike(obj: object) -> TypeIs[X | type[X]]: ...
+
+raw_target: object
+if isinstance(raw_target, type) and is_xlike(raw_target):
+    reveal_type(raw_target)  # N: Revealed type is "type[__main__.X]"
+[builtins fixtures/tuple.pyi]

From 29d8f06d5449d24b7d446762afe0e73467173298 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 3 Jun 2025 18:25:02 +0100
Subject: [PATCH 1341/1617] Fix crash on partial type used as context (#19216)

Fixes https://github.com/python/mypy/issues/19213
---
 mypy/checker.py                     |  4 +++-
 test-data/unit/check-inference.test | 21 +++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index e83473492f01..5201037242ac 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -3427,7 +3427,9 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
             # store the rvalue type on the variable.
             actual_lvalue_type = None
             if lvalue_node.is_inferred and not lvalue_node.explicit_self_type:
-                rvalue_type = self.expr_checker.accept(rvalue, lvalue_node.type)
+                # Don't use partial types as context, similar to regular code path.
+                ctx = lvalue_node.type if not isinstance(lvalue_node.type, PartialType) else None
+                rvalue_type = self.expr_checker.accept(rvalue, ctx)
                 actual_lvalue_type = lvalue_node.type
                 lvalue_node.type = rvalue_type
             lvalue_type, _ = self.node_type_from_base(lvalue_node.name, lvalue_node.info, lvalue)
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 4a3930533954..b563eef0f8aa 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -4128,3 +4128,24 @@ T = TypeVar("T")
 def f(x: Optional[T]) -> T: ...
 reveal_type(f(a))  # N: Revealed type is "Any"
 reveal_type(f(oa))  # N: Revealed type is "Any"
+
+[case testNoCrashOnPartialTypeAsContext]
+from typing import overload, TypeVar, Optional, Protocol
+
+T = TypeVar("T")
+class DbManager(Protocol):
+    @overload
+    def get(self, key: str) -> Optional[T]:
+        pass
+
+    @overload
+    def get(self, key: str, default: T) -> T:
+        pass
+
+class Foo:
+    def __init__(self, db: DbManager, bar: bool) -> None:
+        if bar:
+            self.qux = db.get("qux")
+        else:
+            self.qux = {}  # E: Need type annotation for "qux" (hint: "qux: dict[, ] = ...")
+[builtins fixtures/dict.pyi]

From 5a0fa556be741270e25c95a923ef8450d28dd448 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 3 Jun 2025 19:37:25 +0100
Subject: [PATCH 1342/1617] Narrow type variable bounds in binder (#19183)

Fixes https://github.com/python/mypy/issues/5720
Fixes https://github.com/python/mypy/issues/8556
Fixes https://github.com/python/mypy/issues/9778
Fixes https://github.com/python/mypy/issues/10003
Fixes https://github.com/python/mypy/issues/10817
Fixes https://github.com/python/mypy/issues/11163
Fixes https://github.com/python/mypy/issues/11664
Fixes https://github.com/python/mypy/issues/12882
Fixes https://github.com/python/mypy/issues/13426
Fixes https://github.com/python/mypy/issues/13462
Fixes https://github.com/python/mypy/issues/14941
Fixes https://github.com/python/mypy/issues/15151
Fixes https://github.com/python/mypy/issues/19166

This handles a (surprisingly) common edge case. The charges in
`bind_self()` and `bind_self_fast()` are tricky. I got a few "Redundant
cast" errors there, which seemed good, but then I realized that
attribute access etc. on a type variable go through slow `PyObject`
paths, so I am actually forcing `CallableType` instead of
`F(bound=CallableType)` there, since these are performance-critical
functions.
---
 mypy/checkmember.py                  | 17 ++++++------
 mypy/expandtype.py                   |  2 +-
 mypy/join.py                         |  4 ++-
 mypy/meet.py                         | 13 +++++++++-
 mypy/subtypes.py                     |  9 ++++++-
 mypy/typeops.py                      |  6 ++---
 mypy/types.py                        |  5 ++++
 mypyc/test-data/run-classes.test     | 28 ++++++++++++++++++++
 test-data/unit/check-classes.test    | 17 +++++-------
 test-data/unit/check-isinstance.test | 34 ++++++++++++++++++++----
 test-data/unit/check-narrowing.test  | 39 ++++++++++++++++++++++++++++
 test-data/unit/check-typeguard.test  | 17 ++++++++++++
 12 files changed, 161 insertions(+), 30 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 86e1dc06fc25..be89c2f09a80 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1484,19 +1484,20 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F:
         items = [bind_self_fast(c, original_type) for c in method.items]
         return cast(F, Overloaded(items))
     assert isinstance(method, CallableType)
-    if not method.arg_types:
+    func: CallableType = method
+    if not func.arg_types:
         # Invalid method, return something.
-        return cast(F, method)
-    if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
+        return method
+    if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
         # See typeops.py for details.
-        return cast(F, method)
+        return method
     original_type = get_proper_type(original_type)
     if isinstance(original_type, CallableType) and original_type.is_type_obj():
         original_type = TypeType.make_normalized(original_type.ret_type)
-    res = method.copy_modified(
-        arg_types=method.arg_types[1:],
-        arg_kinds=method.arg_kinds[1:],
-        arg_names=method.arg_names[1:],
+    res = func.copy_modified(
+        arg_types=func.arg_types[1:],
+        arg_kinds=func.arg_kinds[1:],
+        arg_names=func.arg_names[1:],
         bound_args=[original_type],
     )
     return cast(F, res)
diff --git a/mypy/expandtype.py b/mypy/expandtype.py
index 031f86e7dfff..d27105f48ed3 100644
--- a/mypy/expandtype.py
+++ b/mypy/expandtype.py
@@ -122,7 +122,7 @@ def freshen_function_type_vars(callee: F) -> F:
     """Substitute fresh type variables for generic function type variables."""
     if isinstance(callee, CallableType):
         if not callee.is_generic():
-            return cast(F, callee)
+            return callee
         tvs = []
         tvmap: dict[TypeVarId, Type] = {}
         for v in callee.variables:
diff --git a/mypy/join.py b/mypy/join.py
index 65cc3bef66a4..a012a633dfa3 100644
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -298,7 +298,9 @@ def visit_erased_type(self, t: ErasedType) -> ProperType:
 
     def visit_type_var(self, t: TypeVarType) -> ProperType:
         if isinstance(self.s, TypeVarType) and self.s.id == t.id:
-            return self.s
+            if self.s.upper_bound == t.upper_bound:
+                return self.s
+            return self.s.copy_modified(upper_bound=join_types(self.s.upper_bound, t.upper_bound))
         else:
             return self.default(self.s)
 
diff --git a/mypy/meet.py b/mypy/meet.py
index add0785f5e71..7a44feabc10c 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -50,6 +50,7 @@
     find_unpack_in_list,
     get_proper_type,
     get_proper_types,
+    has_type_vars,
     is_named_instance,
     split_with_prefix_and_suffix,
 )
@@ -149,6 +150,14 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
         return make_simplified_union(
             [narrow_declared_type(declared, x) for x in narrowed.relevant_items()]
         )
+    elif (
+        isinstance(declared, TypeVarType)
+        and not has_type_vars(original_narrowed)
+        and is_subtype(original_narrowed, declared.upper_bound)
+    ):
+        # We put this branch early to get T(bound=Union[A, B]) instead of
+        # Union[T(bound=A), T(bound=B)] that will be confusing for users.
+        return declared.copy_modified(upper_bound=original_narrowed)
     elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True):
         if state.strict_optional:
             return UninhabitedType()
@@ -777,7 +786,9 @@ def visit_erased_type(self, t: ErasedType) -> ProperType:
 
     def visit_type_var(self, t: TypeVarType) -> ProperType:
         if isinstance(self.s, TypeVarType) and self.s.id == t.id:
-            return self.s
+            if self.s.upper_bound == t.upper_bound:
+                return self.s
+            return self.s.copy_modified(upper_bound=self.meet(self.s.upper_bound, t.upper_bound))
         else:
             return self.default(self.s)
 
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 8d72e44d0eda..15c8014c0f3f 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -632,7 +632,14 @@ def visit_instance(self, left: Instance) -> bool:
     def visit_type_var(self, left: TypeVarType) -> bool:
         right = self.right
         if isinstance(right, TypeVarType) and left.id == right.id:
-            return True
+            # Fast path for most common case.
+            if left.upper_bound == right.upper_bound:
+                return True
+            # Corner case for self-types in classes generic in type vars
+            # with value restrictions.
+            if left.id.is_self():
+                return True
+            return self._is_subtype(left.upper_bound, right.upper_bound)
         if left.values and self._is_subtype(UnionType.make_union(left.values), right):
             return True
         return self._is_subtype(left.upper_bound, self.right)
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 3715081ae173..da2796ff5dec 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -415,10 +415,10 @@ class B(A): pass
             ]
         return cast(F, Overloaded(items))
     assert isinstance(method, CallableType)
-    func = method
+    func: CallableType = method
     if not func.arg_types:
         # Invalid method, return something.
-        return cast(F, func)
+        return method
     if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
         # The signature is of the form 'def foo(*args, ...)'.
         # In this case we shouldn't drop the first arg,
@@ -427,7 +427,7 @@ class B(A): pass
 
         # In the case of **kwargs we should probably emit an error, but
         # for now we simply skip it, to avoid crashes down the line.
-        return cast(F, func)
+        return method
     self_param_type = get_proper_type(func.arg_types[0])
 
     variables: Sequence[TypeVarLikeType]
diff --git a/mypy/types.py b/mypy/types.py
index d2094cd15774..d83b320106ab 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -461,6 +461,11 @@ def __init__(self, type_guard: Type) -> None:
     def __repr__(self) -> str:
         return f"TypeGuard({self.type_guard})"
 
+    # This may hide some real bugs, but it is convenient for various "synthetic"
+    # visitors, similar to RequiredType and ReadOnlyType below.
+    def accept(self, visitor: TypeVisitor[T]) -> T:
+        return self.type_guard.accept(visitor)
+
 
 class RequiredType(Type):
     """Required[T] or NotRequired[T]. Only usable at top-level of a TypedDict definition."""
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 288f281c0a94..b98f1989da51 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2983,3 +2983,31 @@ class B(native.A):
 
 b: B = B.make()
 assert(B.count == 2)
+
+[case testTypeVarNarrowing]
+from typing import TypeVar
+
+class B:
+    def __init__(self, x: int) -> None:
+        self.x = x
+class C(B):
+    def __init__(self, x: int, y: str) -> None:
+        self.x = x
+        self.y = y
+
+T = TypeVar("T", bound=B)
+def f(x: T) -> T:
+    if isinstance(x, C):
+        print("C", x.y)
+        return x
+    print("B", x.x)
+    return x
+
+[file driver.py]
+from native import f, B, C
+
+f(B(1))
+f(C(1, "yes"))
+[out]
+B 1
+C yes
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 054ba0708ce3..9c95458361fd 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -6891,10 +6891,11 @@ reveal_type(i.x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testIsInstanceTypeTypeVar]
-from typing import Type, TypeVar, Generic
+from typing import Type, TypeVar, Generic, ClassVar
 
 class Base: ...
-class Sub(Base): ...
+class Sub(Base):
+    other: ClassVar[int]
 
 T = TypeVar('T', bound=Base)
 
@@ -6902,13 +6903,9 @@ class C(Generic[T]):
     def meth(self, cls: Type[T]) -> None:
         if not issubclass(cls, Sub):
             return
-        reveal_type(cls)  # N: Revealed type is "type[__main__.Sub]"
-    def other(self, cls: Type[T]) -> None:
-        if not issubclass(cls, Sub):
-            return
-        reveal_type(cls)  # N: Revealed type is "type[__main__.Sub]"
-
-[builtins fixtures/isinstancelist.pyi]
+        reveal_type(cls)  # N: Revealed type is "type[T`1]"
+        reveal_type(cls.other)  # N: Revealed type is "builtins.int"
+[builtins fixtures/isinstance.pyi]
 
 [case testIsInstanceTypeSubclass]
 from typing import Type, Optional
@@ -7602,7 +7599,7 @@ class C1:
 class C2(Generic[TypeT]):
     def method(self, other: TypeT) -> int:
         if issubclass(other, Base):
-            reveal_type(other)  # N: Revealed type is "type[__main__.Base]"
+            reveal_type(other)  # N: Revealed type is "TypeT`1"
             return other.field
         return 0
 
diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test
index fe08d2cfc699..640fc10915d1 100644
--- a/test-data/unit/check-isinstance.test
+++ b/test-data/unit/check-isinstance.test
@@ -1821,19 +1821,23 @@ if issubclass(fm, Baz):
 from typing import TypeVar
 
 class A: pass
-class B(A): pass
+class B(A):
+    attr: int
 
 T = TypeVar('T', bound=A)
 
 def f(x: T) -> None:
     if isinstance(x, B):
-        reveal_type(x) # N: Revealed type is "__main__.B"
+        reveal_type(x) # N: Revealed type is "T`-1"
+        reveal_type(x.attr)  # N: Revealed type is "builtins.int"
     else:
         reveal_type(x) # N: Revealed type is "T`-1"
+        x.attr  # E: "T" has no attribute "attr"
     reveal_type(x) # N: Revealed type is "T`-1"
+    x.attr  # E: "T" has no attribute "attr"
 [builtins fixtures/isinstance.pyi]
 
-[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound]
+[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound1]
 from typing import Union, TypeVar
 
 class A:
@@ -1845,9 +1849,11 @@ T = TypeVar("T", bound=Union[A, B])
 
 def f(x: T) -> T:
     if isinstance(x, A):
-        reveal_type(x)      # N: Revealed type is "__main__.A"
+        reveal_type(x)      # N: Revealed type is "T`-1"
         x.a
-        x.b                 # E: "A" has no attribute "b"
+        x.b                 # E: "T" has no attribute "b"
+        if bool():
+            return x
     else:
         reveal_type(x)      # N: Revealed type is "T`-1"
         x.a                 # E: "T" has no attribute "a"
@@ -1857,6 +1863,24 @@ def f(x: T) -> T:
     return x
 [builtins fixtures/isinstance.pyi]
 
+[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound2]
+from typing import Union, TypeVar
+
+class A:
+    a: int
+class B:
+    b: int
+
+T = TypeVar("T", bound=Union[A, B])
+
+def f(x: T) -> T:
+    if isinstance(x, A):
+        return x
+    x.a # E: "T" has no attribute "a"
+    x.b # OK
+    return x
+[builtins fixtures/isinstance.pyi]
+
 [case testIsinstanceAndTypeType]
 from typing import Type
 def f(x: Type[int]) -> None:
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 4afed0e3ec86..36b2ced075d2 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2424,3 +2424,42 @@ def f() -> None:
     assert isinstance(x, int)
     reveal_type(x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/isinstance.pyi]
+
+[case testNarrowTypeVarBoundType]
+from typing import Type, TypeVar
+
+class A: ...
+class B(A):
+    other: int
+
+T = TypeVar("T", bound=A)
+def test(cls: Type[T]) -> T:
+    if issubclass(cls, B):
+        reveal_type(cls)  # N: Revealed type is "type[T`-1]"
+        reveal_type(cls().other)  # N: Revealed type is "builtins.int"
+        return cls()
+    return cls()
+[builtins fixtures/isinstance.pyi]
+
+[case testNarrowTypeVarBoundUnion]
+from typing import TypeVar
+
+class A:
+    x: int
+class B:
+    x: str
+
+T = TypeVar("T")
+def test(x: T) -> T:
+    if not isinstance(x, (A, B)):
+        return x
+    reveal_type(x)  # N: Revealed type is "T`-1"
+    reveal_type(x.x)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+    if isinstance(x, A):
+        reveal_type(x)  # N: Revealed type is "T`-1"
+        reveal_type(x.x)  # N: Revealed type is "builtins.int"
+        return x
+    reveal_type(x)  # N: Revealed type is "T`-1"
+    reveal_type(x.x)  # N: Revealed type is "builtins.str"
+    return x
+[builtins fixtures/isinstance.pyi]
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index 0b512962b8d1..c43eead67876 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -778,6 +778,23 @@ def handle(model: Model) -> int:
     return 0
 [builtins fixtures/tuple.pyi]
 
+[case testTypeGuardRestrictTypeVarUnion]
+from typing import Union, TypeVar
+from typing_extensions import TypeGuard
+
+class A:
+    x: int
+class B:
+    x: str
+
+def is_b(x: object) -> TypeGuard[B]: ...
+
+T = TypeVar("T")
+def test(x: T) -> T:
+    if isinstance(x, A) or is_b(x):
+        reveal_type(x.x)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+    return x
+[builtins fixtures/isinstance.pyi]
 
 [case testOverloadedTypeGuardType]
 from __future__ import annotations

From 413671554c77ad2e2c0f262cac752f0e27b25e8b Mon Sep 17 00:00:00 2001
From: Donal Burns <56016914+Don-Burns@users.noreply.github.com>
Date: Wed, 4 Jun 2025 03:55:36 +0100
Subject: [PATCH 1343/1617] Add flag to raise error if match statement does not
 match exaustively (#19144)

Fixes https://github.com/python/mypy/issues/19136

Change is to add a mode to catch when a match statement is not handling
all cases exhaustively, similar to what pyright does by default.
After discussion on #19136 I put it behind a new flag that is not
enabled by default.
I updated docs to include information on the new flag also.

Please let me know if anything is not following standards, in particular
I wasn't sure what to name this new flag to be descriptive while
following existing flag naming style.

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Co-authored-by: hauntsaninja 
---
 docs/source/command_line.rst        |   1 +
 docs/source/error_code_list2.rst    |  41 ++++++++
 docs/source/literal_types.rst       |   4 +
 mypy/checker.py                     |   6 ++
 mypy/errorcodes.py                  |   6 ++
 mypy/messages.py                    |  10 ++
 test-data/unit/check-python310.test | 158 ++++++++++++++++++++++++++++
 7 files changed, 226 insertions(+)

diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index dfed280d12ed..390f2ac196be 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -845,6 +845,7 @@ of the above sections.
         x = 'a string'
         x.trim()  # error: "str" has no attribute "trim"  [attr-defined]
 
+
 .. _configuring-error-messages:
 
 Configuring error messages
diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst
index dfe2e30874f7..141aa4490c0b 100644
--- a/docs/source/error_code_list2.rst
+++ b/docs/source/error_code_list2.rst
@@ -612,3 +612,44 @@ Example:
     # mypy: disallow-any-explicit
     from typing import Any
     x: Any = 1  # Error: Explicit "Any" type annotation  [explicit-any]
+
+
+.. _code-exhaustive-match:
+
+Check that match statements match exhaustively [match-exhaustive]
+-----------------------------------------------------------------------
+
+If enabled with :option:`--enable-error-code exhaustive-match `,
+mypy generates an error if a match statement does not match all possible cases/types.
+
+
+Example:
+
+.. code-block:: python
+
+        import enum
+
+
+        class Color(enum.Enum):
+            RED = 1
+            BLUE = 2
+
+        val: Color = Color.RED
+
+        # OK without --enable-error-code exhaustive-match
+        match val:
+            case Color.RED:
+                print("red")
+
+        # With --enable-error-code exhaustive-match
+        # Error: Match statement has unhandled case for values of type "Literal[Color.BLUE]"
+        match val:
+            case Color.RED:
+                print("red")
+
+        # OK with or without --enable-error-code exhaustive-match, since all cases are handled
+        match val:
+            case Color.RED:
+                print("red")
+            case _:
+                print("other")
diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst
index 877ab5de9087..e449589ddb4d 100644
--- a/docs/source/literal_types.rst
+++ b/docs/source/literal_types.rst
@@ -468,6 +468,10 @@ If we forget to handle one of the cases, mypy will generate an error:
       assert_never(direction)  # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn"
 
 Exhaustiveness checking is also supported for match statements (Python 3.10 and later).
+For match statements specifically, inexhaustive matches can be caught
+without needing to use ``assert_never`` by using
+:option:`--enable-error-code exhaustive-match `.
+
 
 Extra Enum checks
 *****************
diff --git a/mypy/checker.py b/mypy/checker.py
index 5201037242ac..885949820341 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -5455,6 +5455,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
             inferred_types = self.infer_variable_types_from_type_maps(type_maps)
 
             # The second pass narrows down the types and type checks bodies.
+            unmatched_types: TypeMap = None
             for p, g, b in zip(s.patterns, s.guards, s.bodies):
                 current_subject_type = self.expr_checker.narrow_type_from_binder(
                     named_subject, subject_type
@@ -5511,6 +5512,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
                     else:
                         self.accept(b)
                 self.push_type_map(else_map, from_assignment=False)
+                unmatched_types = else_map
+
+            if unmatched_types is not None:
+                for typ in list(unmatched_types.values()):
+                    self.msg.match_statement_inexhaustive_match(typ, s)
 
             # This is needed due to a quirk in frame_context. Without it types will stay narrowed
             # after the match.
diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py
index 8f650aa30605..c22308e4a754 100644
--- a/mypy/errorcodes.py
+++ b/mypy/errorcodes.py
@@ -264,6 +264,12 @@ def __hash__(self) -> int:
     "General",
     default_enabled=False,
 )
+EXHAUSTIVE_MATCH: Final = ErrorCode(
+    "exhaustive-match",
+    "Reject match statements that are not exhaustive",
+    "General",
+    default_enabled=False,
+)
 
 # Syntax errors are often blocking.
 SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General")
diff --git a/mypy/messages.py b/mypy/messages.py
index 366c4a82fd98..86778f58a359 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -2491,6 +2491,16 @@ def type_parameters_should_be_declared(self, undeclared: list[str], context: Con
             code=codes.VALID_TYPE,
         )
 
+    def match_statement_inexhaustive_match(self, typ: Type, context: Context) -> None:
+        type_str = format_type(typ, self.options)
+        msg = f"Match statement has unhandled case for values of type {type_str}"
+        self.fail(msg, context, code=codes.EXHAUSTIVE_MATCH)
+        self.note(
+            "If match statement is intended to be non-exhaustive, add `case _: pass`",
+            context,
+            code=codes.EXHAUSTIVE_MATCH,
+        )
+
 
 def quote_type_string(type_string: str) -> str:
     """Quotes a type representation for use in messages."""
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index fdf6b25f3591..0695bd0380cb 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -2639,6 +2639,164 @@ def f2() -> None:
     reveal_type(y) # N: Revealed type is "builtins.str"
 [builtins fixtures/list.pyi]
 
+[case testExhaustiveMatchNoFlag]
+
+a: int = 5
+match a:
+    case 1:
+        pass
+    case _:
+        pass
+
+b: str = "hello"
+match b:
+    case "bye":
+        pass
+    case _:
+        pass
+
+[case testNonExhaustiveMatchNoFlag]
+
+a: int = 5
+match a:
+    case 1:
+        pass
+
+b: str = "hello"
+match b:
+    case "bye":
+        pass
+
+
+[case testExhaustiveMatchWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+a: int = 5
+match a:
+    case 1:
+        pass
+    case _:
+        pass
+
+b: str = "hello"
+match b:
+    case "bye":
+        pass
+    case _:
+        pass
+
+[case testNonExhaustiveMatchWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+a: int = 5
+match a: # E: Match statement has unhandled case for values of type "int" \
+         # N: If match statement is intended to be non-exhaustive, add `case _: pass`
+    case 1:
+        pass
+
+b: str = "hello"
+match b: # E: Match statement has unhandled case for values of type "str" \
+         # N: If match statement is intended to be non-exhaustive, add `case _: pass`
+    case "bye":
+        pass
+[case testNonExhaustiveMatchEnumWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+import enum
+
+class Color(enum.Enum):
+    RED = 1
+    BLUE = 2
+    GREEN = 3
+
+val: Color = Color.RED
+
+match val: # E: Match statement has unhandled case for values of type "Literal[Color.GREEN]" \
+           # N: If match statement is intended to be non-exhaustive, add `case _: pass`
+    case Color.RED:
+        a = "red"
+    case Color.BLUE:
+        a= "blue"
+[builtins fixtures/enum.pyi]
+
+[case testExhaustiveMatchEnumWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+import enum
+
+class Color(enum.Enum):
+    RED = 1
+    BLUE = 2
+
+val: Color = Color.RED
+
+match val:
+    case Color.RED:
+        a = "red"
+    case Color.BLUE:
+        a= "blue"
+[builtins fixtures/enum.pyi]
+
+[case testNonExhaustiveMatchEnumMultipleMissingMatchesWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+import enum
+
+class Color(enum.Enum):
+    RED = 1
+    BLUE = 2
+    GREEN = 3
+
+val: Color = Color.RED
+
+match val: # E: Match statement has unhandled case for values of type "Literal[Color.BLUE, Color.GREEN]" \
+           # N: If match statement is intended to be non-exhaustive, add `case _: pass`
+    case Color.RED:
+        a = "red"
+[builtins fixtures/enum.pyi]
+
+[case testExhaustiveMatchEnumFallbackWithFlag]
+# flags: --enable-error-code exhaustive-match
+
+import enum
+
+class Color(enum.Enum):
+    RED = 1
+    BLUE = 2
+    GREEN = 3
+
+val: Color = Color.RED
+
+match val:
+    case Color.RED:
+        a = "red"
+    case _:
+        a = "other"
+[builtins fixtures/enum.pyi]
+
+# Fork of testMatchNarrowingUnionTypedDictViaIndex to check behaviour with exhaustive match flag
+[case testExhaustiveMatchNarrowingUnionTypedDictViaIndex]
+# flags: --enable-error-code exhaustive-match
+
+from typing import Literal, TypedDict
+
+class A(TypedDict):
+    tag: Literal["a"]
+    name: str
+
+class B(TypedDict):
+    tag: Literal["b"]
+    num: int
+
+d: A | B
+match d["tag"]: # E: Match statement has unhandled case for values of type "Literal['b']" \
+                # N: If match statement is intended to be non-exhaustive, add `case _: pass` \
+                # E: Match statement has unhandled case for values of type "B"
+    case "a":
+        reveal_type(d)  # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['a'], 'name': builtins.str})"
+        reveal_type(d["name"])  # N: Revealed type is "builtins.str"
+[typing fixtures/typing-typeddict.pyi]
+
 [case testEnumTypeObjectMember]
 import enum
 from typing import NoReturn

From dd1f2a3b8b6965e8a8700998ef38f558c36efaa4 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Tue, 3 Jun 2025 20:00:00 -0700
Subject: [PATCH 1344/1617] Remove --show-speed-regression in primer (#19226)

It's too noisy. We added it to benchmark a specific PR
---
 .github/workflows/mypy_primer.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml
index 532e77a0cacb..ee868484751e 100644
--- a/.github/workflows/mypy_primer.yml
+++ b/.github/workflows/mypy_primer.yml
@@ -67,7 +67,6 @@ jobs:
             --debug \
             --additional-flags="--debug-serialize" \
             --output concise \
-            --show-speed-regression \
             | tee diff_${{ matrix.shard-index }}.txt
           ) || [ $? -eq 1 ]
       - if: ${{ matrix.shard-index == 0 }}

From 71942c0bf7e2ce340092ac7e98352823755d4d63 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 4 Jun 2025 10:17:39 +0100
Subject: [PATCH 1345/1617] [mypyc] Use non-tagged integer for generator label
 (#19218)

Also consider it as always defined to generate simpler code.

This appears to speed up a simple benchmark by 3%, but it could be
noise. This reduces the volume of generated code -- the line count of a
small compiled program with a few async functions was reduced by 5%.
---
 mypyc/irbuild/builder.py   | 9 ++++++++-
 mypyc/irbuild/generator.py | 4 ++--
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 72a5ff4099df..75e059a5b570 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -1300,12 +1300,19 @@ def node_type(self, node: Expression) -> RType:
         return self.type_to_rtype(mypy_type)
 
     def add_var_to_env_class(
-        self, var: SymbolNode, rtype: RType, base: FuncInfo | ImplicitClass, reassign: bool = False
+        self,
+        var: SymbolNode,
+        rtype: RType,
+        base: FuncInfo | ImplicitClass,
+        reassign: bool = False,
+        always_defined: bool = False,
     ) -> AssignmentTarget:
         # First, define the variable name as an attribute of the environment class, and then
         # construct a target for that attribute.
         name = remangle_redefinition_name(var.name)
         self.fn_info.env_class.attributes[name] = rtype
+        if always_defined:
+            self.fn_info.env_class.attrs_with_defaults.add(name)
         attr_target = AssignmentTargetAttr(base.curr_env_reg, name)
 
         if reassign:
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 9dea0ee5f7c2..e9e6ac6fa548 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -32,7 +32,7 @@
     Unreachable,
     Value,
 )
-from mypyc.ir.rtypes import RInstance, int_rprimitive, object_rprimitive
+from mypyc.ir.rtypes import RInstance, int32_rprimitive, object_rprimitive
 from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults
 from mypyc.irbuild.context import FuncInfo, GeneratorClass
 from mypyc.irbuild.env_class import (
@@ -415,7 +415,7 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None:
     # the '__next__' function of the generator is called, and add it
     # as an attribute to the environment class.
     cls.next_label_target = builder.add_var_to_env_class(
-        Var(NEXT_LABEL_ATTR_NAME), int_rprimitive, cls, reassign=False
+        Var(NEXT_LABEL_ATTR_NAME), int32_rprimitive, cls, reassign=False, always_defined=True
     )
 
     # Add arguments from the original generator function to the

From ab61ec2399137d0a6b5f2f1efdc953471972e8d1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 4 Jun 2025 13:31:32 +0100
Subject: [PATCH 1346/1617] [mypyc] Free coroutine after await encounters
 StopIteration (#19231)

Previously the awaited coroutine could stay alive until the coroutine
that performed the await was freed, delaying object reclamation. The
reference counting analysis doesn't understand registers spilled to the
environment, so we need to manually clear the value.

Consider code like this:
```
async def foo() -> None:
    await bar()
    await zar()
```
Previously, the `bar()` coroutine was only freed at end of `foo()`. Now
we release it before `await zar()`, as expected.
---
 mypyc/irbuild/statement.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index b109d925558b..16a0483a8729 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -940,6 +940,10 @@ def emit_yield_from_or_await(
     # If it wasn't, this reraises the exception.
     builder.activate_block(stop_block)
     builder.assign(result, builder.call_c(check_stop_op, [], line), line)
+    # Clear the spilled iterator/coroutine so that it will be freed.
+    # Otherwise, the freeing of the spilled register would likely be delayed.
+    err = builder.add(LoadErrorValue(object_rprimitive))
+    builder.assign(iter_reg, err, line)
     builder.goto(done_block)
 
     builder.activate_block(main_block)

From f295bb8507f0b4ea1f136cb500808518f9a9851e Mon Sep 17 00:00:00 2001
From: "Michael J. Sullivan" 
Date: Wed, 4 Jun 2025 08:03:55 -0700
Subject: [PATCH 1347/1617] Avoid spurious non-overlapping eq error with
 metaclass with `__eq__` (#19220)

Currently, doing an `==` on a `type[Foo]` where `Foo` has a metaclass
that defines `__eq__` will spuriously produce a non-overlapping equality
error, because `custom_special_method`, a helper used in the check,
does not consider the `TypeType` case.

Fix that.
---
 mypy/typeops.py                       | 4 ++++
 test-data/unit/check-expressions.test | 8 ++++++++
 2 files changed, 12 insertions(+)

diff --git a/mypy/typeops.py b/mypy/typeops.py
index da2796ff5dec..b4abb246af07 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -1190,6 +1190,10 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool
     if isinstance(typ, FunctionLike) and typ.is_type_obj():
         # Look up __method__ on the metaclass for class objects.
         return custom_special_method(typ.fallback, name, check_all)
+    if isinstance(typ, TypeType) and isinstance(typ.item, Instance):
+        if typ.item.type.metaclass_type:
+            # Look up __method__ on the metaclass for class objects.
+            return custom_special_method(typ.item.type.metaclass_type, name, check_all)
     if isinstance(typ, AnyType):
         # Avoid false positives in uncertain cases.
         return True
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index a0302fcd1943..f3c00627892e 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -2172,6 +2172,14 @@ class Custom(metaclass=CustomMeta): ...
 Normal == int()  # E: Non-overlapping equality check (left operand type: "type[Normal]", right operand type: "int")
 Normal == Normal
 Custom == int()
+
+n: type[Normal] = Normal
+c: type[Custom] = Custom
+
+n == int()  # E: Non-overlapping equality check (left operand type: "type[Normal]", right operand type: "int")
+n == n
+c == int()
+
 [builtins fixtures/bool.pyi]
 
 [case testCustomContainsCheckStrictEquality]

From dcd79c4d5dc6a4c8638f00ddae21af3e30ef32fa Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 4 Jun 2025 16:51:57 +0100
Subject: [PATCH 1348/1617] [mypyc] Make some generated classes implicitly
 final (#19235)

Classes used for generators, async functions and nested functions are
now final. This may slightly improve performance when using separate
compilation.
---
 mypyc/irbuild/callable_class.py | 2 +-
 mypyc/irbuild/env_class.py      | 5 ++++-
 mypyc/irbuild/generator.py      | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py
index 599dbb81f767..c7c3c7677cda 100644
--- a/mypyc/irbuild/callable_class.py
+++ b/mypyc/irbuild/callable_class.py
@@ -55,7 +55,7 @@ class for the nested function.
     # Define the actual callable class ClassIR, and set its
     # environment to point at the previously defined environment
     # class.
-    callable_class_ir = ClassIR(name, builder.module_name, is_generated=True)
+    callable_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True)
 
     # The functools @wraps decorator attempts to call setattr on
     # nested functions, so we create a dict for these nested
diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py
index b0909f86686a..9e72f7efcf94 100644
--- a/mypyc/irbuild/env_class.py
+++ b/mypyc/irbuild/env_class.py
@@ -43,7 +43,10 @@ class is generated, the function environment has not yet been
     containing a nested function.
     """
     env_class = ClassIR(
-        f"{builder.fn_info.namespaced_name()}_env", builder.module_name, is_generated=True
+        f"{builder.fn_info.namespaced_name()}_env",
+        builder.module_name,
+        is_generated=True,
+        is_final_class=True,
     )
     env_class.attributes[SELF_NAME] = RInstance(env_class)
     if builder.fn_info.is_nested:
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index e9e6ac6fa548..ef538ee95949 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -155,7 +155,7 @@ def instantiate_generator_class(builder: IRBuilder) -> Value:
 def setup_generator_class(builder: IRBuilder) -> ClassIR:
     name = f"{builder.fn_info.namespaced_name()}_gen"
 
-    generator_class_ir = ClassIR(name, builder.module_name, is_generated=True)
+    generator_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True)
     if builder.fn_info.can_merge_generator_and_env_classes():
         builder.fn_info.env_class = generator_class_ir
     else:

From 5610a231c8586c0d3c41322b60bb1250629b93e4 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 4 Jun 2025 18:58:13 +0200
Subject: [PATCH 1349/1617] Provide a better fallback value for the
 python_version option (#19162)

Followup to https://github.com/python/mypy/pull/19157. After dropping
support for an old Python version, mypy should assume the next oldest
one instead of the current interpreter version.
---
 mypy/config_parser.py       | 13 ++++++++++++-
 test-data/unit/cmdline.test | 14 ++++++++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/mypy/config_parser.py b/mypy/config_parser.py
index 0e033471d2e9..e5c0dc893c76 100644
--- a/mypy/config_parser.py
+++ b/mypy/config_parser.py
@@ -28,6 +28,14 @@
 _INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES]
 
 
+class VersionTypeError(argparse.ArgumentTypeError):
+    """Provide a fallback value if the Python version is unsupported."""
+
+    def __init__(self, *args: Any, fallback: tuple[int, int]) -> None:
+        self.fallback = fallback
+        super().__init__(*args)
+
+
 def parse_version(v: str | float) -> tuple[int, int]:
     m = re.match(r"\A(\d)\.(\d+)\Z", str(v))
     if not m:
@@ -44,7 +52,7 @@ def parse_version(v: str | float) -> tuple[int, int]:
             if isinstance(v, float):
                 msg += ". You may need to put quotes around your Python version"
 
-            raise argparse.ArgumentTypeError(msg)
+            raise VersionTypeError(msg, fallback=defaults.PYTHON3_VERSION_MIN)
     else:
         raise argparse.ArgumentTypeError(
             f"Python major version '{major}' out of range (must be 3)"
@@ -548,6 +556,9 @@ def parse_section(
                     continue
                 try:
                     v = ct(section.get(key))
+                except VersionTypeError as err_version:
+                    print(f"{prefix}{key}: {err_version}", file=stderr)
+                    v = err_version.fallback
                 except argparse.ArgumentTypeError as err:
                     print(f"{prefix}{key}: {err}", file=stderr)
                     continue
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index 2db4451adc9a..aa0c8916ba0f 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -440,6 +440,20 @@ python_version = 3.9
 python_version = 3.14
 [out]
 
+[case testPythonVersionFallback]
+# cmd: mypy main.py
+[file main.py]
+import sys
+if sys.version_info == (3, 9):  # Update here when bumping the min Python version!
+    reveal_type("good")
+[file mypy.ini]
+\[mypy]
+python_version = 3.8
+[out]
+mypy.ini: [mypy]: python_version: Python 3.8 is not supported (must be 3.9 or higher)
+main.py:3: note: Revealed type is "Literal['good']?"
+== Return code: 0
+
 -- This should be a dumping ground for tests of plugins that are sensitive to
 -- typeshed changes.
 [case testTypeshedSensitivePlugins]

From 9fd55aa62ad27712b871e8110b8e55e63c93b24e Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 4 Jun 2025 18:09:52 +0100
Subject: [PATCH 1350/1617] [mypyc] Add note about using non-native class to
 subclass built-in types (#19236)

Without the note, it's not clear what's the easiest way forward. Also
add a doc link.

Test that subclassing a built-in exception type actually works as
suggested by the note.
---
 mypyc/irbuild/prepare.py             | 10 ++++++
 mypyc/test-data/commandline.test     |  4 ++-
 mypyc/test-data/irbuild-classes.test |  4 ++-
 mypyc/test-data/run-classes.test     | 47 ++++++++++++++++++++++++++++
 4 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 98ff348d8c30..65951999dcf9 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -298,6 +298,16 @@ def prepare_class_def(
                 errors.error(
                     "Inheriting from most builtin types is unimplemented", path, cdef.line
                 )
+                errors.note(
+                    "Potential workaround: @mypy_extensions.mypyc_attr(native_class=False)",
+                    path,
+                    cdef.line,
+                )
+                errors.note(
+                    "https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes",
+                    path,
+                    cdef.line,
+                )
 
     # Set up the parent class
     bases = [mapper.type_to_ir[base.type] for base in info.bases if base.type in mapper.type_to_ir]
diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test
index ae0be03eb66b..77c2e08bcf34 100644
--- a/mypyc/test-data/commandline.test
+++ b/mypyc/test-data/commandline.test
@@ -138,7 +138,9 @@ Foo.lol = 50  # E: Only class variables defined as ClassVar can be assigned to
 def decorator(x: Any) -> Any:
     return x
 
-class NeverMetaclass(type):  # E: Inheriting from most builtin types is unimplemented
+class NeverMetaclass(type):  # E: Inheriting from most builtin types is unimplemented \
+                             # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \
+                             # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes
     pass
 
 class Concrete1:
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 9d564a552a05..fa4708f02e0b 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1375,7 +1375,9 @@ class BadUse():  # E: native_class must be used with True or False only
 from mypy_extensions import mypyc_attr
 
 @mypyc_attr(native_class=True)
-class M(type):  # E: Inheriting from most builtin types is unimplemented
+class M(type):  # E: Inheriting from most builtin types is unimplemented \
+                # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \
+                # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes
     pass
 
 @mypyc_attr(native_class=True)
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index b98f1989da51..fd486980ef16 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -934,6 +934,53 @@ def welp() -> int:
 from native import welp
 assert welp() == 35
 
+[case testSubclassUnsupportedException]
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class MyError(ZeroDivisionError):
+    pass
+
+@mypyc_attr(native_class=False)
+class MyError2(ZeroDivisionError):
+    def __init__(self, s: str) -> None:
+        super().__init__(s + "!")
+        self.x = s.upper()
+
+def f() -> None:
+    raise MyError("foobar")
+
+def test_non_native_exception_subclass_basics() -> None:
+    e = MyError()
+    assert isinstance(e, MyError)
+    assert isinstance(e, ZeroDivisionError)
+    assert isinstance(e, Exception)
+
+    e = MyError("x")
+    assert repr(e) == "MyError('x')"
+
+    e2 = MyError2("ab")
+    assert repr(e2) == "MyError2('ab!')", repr(e2)
+    assert e2.x == "AB"
+
+def test_raise_non_native_exception_subclass_1() -> None:
+    try:
+        f()
+    except MyError:
+        x = True
+    else:
+        assert False
+    assert x
+
+def test_raise_non_native_exception_subclass_2() -> None:
+    try:
+        f()
+    except ZeroDivisionError:
+        x = True
+    else:
+        assert False
+    assert x
+
 [case testSubclassPy]
 from b import B, V
 class A(B):

From 9ded5b19182a5f8baea5c34ba88374702b9396b3 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 5 Jun 2025 15:09:47 +0200
Subject: [PATCH 1351/1617] Deprecated --force-uppercase-builtins flag (#19176)

Use lowercase builtins for error messages, Mypy only supports 3.9+. This
PR deprecates the `--force-uppercase-builtins` flag and makes it a
no-op. Followup to https://github.com/python/mypy/pull/19173.
---
 CHANGELOG.md                        |  6 ++++
 docs/source/command_line.rst        |  5 ----
 docs/source/config_file.rst         |  8 ------
 mypy/main.py                        |  4 +++
 mypy/messages.py                    | 28 ++++++------------
 mypy/options.py                     | 11 ++++++--
 mypy/types.py                       | 11 ++------
 test-data/unit/check-lowercase.test | 44 +++++------------------------
 8 files changed, 36 insertions(+), 81 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01d58ce6a1b3..b09916919d8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 
 ## 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.
+
+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/)).
diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index 390f2ac196be..697e0fb69eed 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -937,11 +937,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 9f23617b9481..b4f134f26cb1 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
diff --git a/mypy/main.py b/mypy/main.py
index 6ebf32ded6e1..16e9e035bf2e 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 86778f58a359..8a90ae433dbc 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} = ...")'
 
@@ -2424,8 +2421,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),
@@ -2610,10 +2606,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
@@ -2624,7 +2617,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)}]"
@@ -2660,11 +2653,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():
@@ -2736,8 +2725,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 52afd27211ed..4a89ef529c07 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,9 +415,12 @@ def __init__(self) -> None:
         self.mypyc_skip_c_generation = False
 
     def use_lowercase_names(self) -> bool:
-        if self.python_version >= (3, 9):
-            return not self.force_uppercase_builtins
-        return False
+        warnings.warn(
+            "options.use_lowercase_names() is deprecated and will be removed in a future version",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return True
 
     def use_or_syntax(self) -> bool:
         if self.python_version >= (3, 10):
diff --git a/mypy/types.py b/mypy/types.py
index d83b320106ab..b598a6116136 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -3463,12 +3463,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:
@@ -3511,11 +3510,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 51a833614a33..d19500327255 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 b147d11b06b5a68d9a235d11885842cbbd701399 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 15:18:27 +0200
Subject: [PATCH 1352/1617] Move dataclass kw_only fields to the end of the
 signature (#19018)

Fixes #19017. Fixes #17731.

This is a rather naive change: python does that at runtime. `kw_only`
args can be in any order, and non-kwonly args should remain sorted as-is
(stable sort). I don't understand why this was only done in presence of
a parent dataclass - AFAIC kwonly fields work that way since `kw_only`
was introduced in py3.10.

The test I changed was invalid and asserted a false positive to the best
of my knowledge.
---
 mypy/plugins/dataclasses.py                   |  5 +--
 test-data/unit/check-dataclass-transform.test |  2 +-
 test-data/unit/check-dataclasses.test         | 41 ++++++++++++++++---
 3 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index 2b4982a36bb6..99d4ef56a540 100644
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -546,7 +546,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
         # in the parent. We can implement this via a dict without disrupting the attr order
         # because dicts preserve insertion order in Python 3.7+.
         found_attrs: dict[str, DataclassAttribute] = {}
-        found_dataclass_supertype = False
         for info in reversed(cls.info.mro[1:-1]):
             if "dataclass_tag" in info.metadata and "dataclass" not in info.metadata:
                 # We haven't processed the base class yet. Need another pass.
@@ -556,7 +555,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
 
             # Each class depends on the set of attributes in its dataclass ancestors.
             self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname))
-            found_dataclass_supertype = True
 
             for data in info.metadata["dataclass"]["attributes"]:
                 name: str = data["name"]
@@ -720,8 +718,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
             )
 
         all_attrs = list(found_attrs.values())
-        if found_dataclass_supertype:
-            all_attrs.sort(key=lambda a: a.kw_only)
+        all_attrs.sort(key=lambda a: a.kw_only)
 
         # Third, ensure that arguments without a default don't follow
         # arguments that have a default and that the KW_ONLY sentinel
diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test
index 7c534914aa2d..89b8dc88c98f 100644
--- a/test-data/unit/check-dataclass-transform.test
+++ b/test-data/unit/check-dataclass-transform.test
@@ -265,7 +265,7 @@ class Foo:
 
 Foo(a=5, b_=1)  # E: Unexpected keyword argument "a" for "Foo"
 Foo(a_=1, b_=1, noinit=1)  # E: Unexpected keyword argument "noinit" for "Foo"
-Foo(1, 2, 3)  # E: Too many positional arguments for "Foo"
+Foo(1, 2, 3) # (a, b, unused1)
 foo = Foo(1, 2, kwonly=3)
 reveal_type(foo.noinit)  # N: Revealed type is "builtins.int"
 reveal_type(foo.unused1)  # N: Revealed type is "builtins.int"
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index cfd14ff07b3f..ded390067de0 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -460,14 +460,16 @@ from dataclasses import dataclass, field, KW_ONLY
 class Application:
     _: KW_ONLY
     name: str = 'Unnamed'
-    rating: int = field(kw_only=False)  # E: Attributes without a default cannot follow attributes with one
+    rating: int = field(kw_only=False)
 
 Application(name='name', rating=5)
-Application()  # E: Missing positional argument "name" in call to "Application"
-Application('name')  # E: Too many positional arguments for "Application" # E: Too few arguments for "Application"
-Application('name', 123)  # E: Too many positional arguments for "Application"
-Application('name', rating=123)  # E: Too many positional arguments for "Application"
-
+Application()  # E: Missing positional argument "rating" in call to "Application"
+Application(123)
+Application('name')  # E: Argument 1 to "Application" has incompatible type "str"; expected "int"
+Application('name', 123)  # E: Too many positional arguments for "Application" \
+                          # E: Argument 1 to "Application" has incompatible type "str"; expected "int" \
+                          # E: Argument 2 to "Application" has incompatible type "int"; expected "str"
+Application(123, rating=123)  # E: "Application" gets multiple values for keyword argument "rating"
 [builtins fixtures/dataclasses.pyi]
 
 [case testDataclassesOrderingKwOnlyWithSentinelAndSubclass]
@@ -2618,3 +2620,30 @@ raw_target: object
 if isinstance(raw_target, type) and dataclasses.is_dataclass(raw_target):
     reveal_type(raw_target)  # N: Revealed type is "type[dataclasses.DataclassInstance]"
 [builtins fixtures/tuple.pyi]
+
+[case testDataclassKwOnlyArgsLast]
+from dataclasses import dataclass, field
+
+@dataclass
+class User:
+    id: int = field(kw_only=True)
+    name: str
+
+User("Foo", id=0)
+[builtins fixtures/tuple.pyi]
+
+[case testDataclassKwOnlyArgsDefaultAllowedNonLast]
+from dataclasses import dataclass, field
+
+@dataclass
+class User:
+    id: int = field(kw_only=True, default=0)
+    name: str
+
+User()  # E: Missing positional argument "name" in call to "User"
+User("")
+User(0)  # E: Argument 1 to "User" has incompatible type "int"; expected "str"
+User("", 0)  # E: Too many positional arguments for "User"
+User("", id=0)
+User("", name="")  # E: "User" gets multiple values for keyword argument "name"
+[builtins fixtures/tuple.pyi]

From faac7804edd93c0bc643099861e2ba4bf422e444 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 15:20:48 +0200
Subject: [PATCH 1353/1617] Support type aliases, `NamedTuple` and `TypedDict`
 in constrained TypeVar defaults (#18884)

Fixes #18862. Fixes #17686.
---
 mypy/checker.py                            |   2 +-
 mypy/checkexpr.py                          |   2 +-
 test-data/unit/check-python312.test        |  24 +++++
 test-data/unit/check-python313.test        |  16 ++++
 test-data/unit/check-typevar-defaults.test | 103 ++++++++++++++++++++-
 5 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 885949820341..2737216cf637 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2664,7 +2664,7 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None:
                 continue
             if not is_subtype(tv.default, tv.upper_bound):
                 self.fail("TypeVar default must be a subtype of the bound type", tv)
-            if tv.values and not any(tv.default == value for value in tv.values):
+            if tv.values and not any(is_same_type(tv.default, value) for value in tv.values):
                 self.fail("TypeVar default must be one of the constraint types", tv)
 
     def check_enum(self, defn: ClassDef) -> None:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index fc0acf55be19..969713edb1a7 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6171,7 +6171,7 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
         ):
             if not is_subtype(p_default, e.upper_bound):
                 self.chk.fail("TypeVar default must be a subtype of the bound type", e)
-            if e.values and not any(p_default == value for value in e.values):
+            if e.values and not any(is_same_type(p_default, value) for value in e.values):
                 self.chk.fail("TypeVar default must be one of the constraint types", e)
         return AnyType(TypeOfAny.special_form)
 
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 315c13ab762b..bfd6334b5077 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2060,3 +2060,27 @@ class R:
 
 class Action:
     pass
+
+[case testPEP695TypeVarConstraintsDefaultAliases]
+from typing import Generic
+from typing_extensions import TypeVar
+
+type K = int
+type V = int
+type L = list[int]
+
+T1 = TypeVar("T1", str, K, default=K)
+T2 = TypeVar("T2", str, K, default=V)
+T3 = TypeVar("T3", str, L, default=L)
+
+class A1(Generic[T1]):
+    x: T1
+class A2(Generic[T2]):
+    x: T2
+class A3(Generic[T3]):
+    x: T3
+
+reveal_type(A1().x)  # N: Revealed type is "builtins.int"
+reveal_type(A2().x)  # N: Revealed type is "builtins.int"
+reveal_type(A3().x)  # N: Revealed type is "builtins.list[builtins.int]"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test
index 65604754cc0f..b46ae0fecfc4 100644
--- a/test-data/unit/check-python313.test
+++ b/test-data/unit/check-python313.test
@@ -274,3 +274,19 @@ def func_d1(
     reveal_type(d)  # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
+
+[case testTypeVarConstraintsDefaultAliasesInline]
+type K = int
+type V = int
+
+class A1[T: (str, int) = K]:
+    x: T
+class A2[T: (str, K) = K]:
+    x: T
+class A3[T: (str, K) = V]:
+    x: T
+
+reveal_type(A1().x)  # N: Revealed type is "builtins.int"
+reveal_type(A2().x)  # N: Revealed type is "builtins.int"
+reveal_type(A3().x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test
index 33a639eee580..22270e17787e 100644
--- a/test-data/unit/check-typevar-defaults.test
+++ b/test-data/unit/check-typevar-defaults.test
@@ -729,8 +729,6 @@ class C(Generic[_I]): pass
 t: type[C] | int = C
 [builtins fixtures/tuple.pyi]
 
-
-
 [case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault]
 from typing_extensions import TypeVar
 from typing import Generic, Union
@@ -749,3 +747,104 @@ MyA = A[T1, int]
 a: MyA = A(None, 10)
 reveal_type(a.a)  # N: Revealed type is "Union[builtins.int, None]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeVarConstraintsDefaultAliasesTypeAliasType]
+from typing import Generic
+from typing_extensions import TypeAliasType, TypeVar
+
+K = TypeAliasType("K", int)
+V = TypeAliasType("V", int)
+L = TypeAliasType("L", list[int])
+T1 = TypeVar("T1", str, K, default=K)
+T2 = TypeVar("T2", str, K, default=V)
+T3 = TypeVar("T3", str, L, default=L)
+
+class A1(Generic[T1]):
+    x: T1
+class A2(Generic[T2]):
+    x: T2
+class A3(Generic[T3]):
+    x: T3
+
+reveal_type(A1().x)  # N: Revealed type is "builtins.int"
+reveal_type(A2().x)  # N: Revealed type is "builtins.int"
+reveal_type(A3().x)  # N: Revealed type is "builtins.list[builtins.int]"
+[builtins fixtures/tuple.pyi]
+
+[case testTypeVarConstraintsDefaultAliasesImplicitAlias]
+from typing_extensions import TypeVar
+
+K = int
+V = int
+L = list[int]
+T1 = TypeVar("T1", str, K, default=K)
+T2 = TypeVar("T2", str, K, default=V)
+T3 = TypeVar("T3", str, L, default=L)
+[builtins fixtures/tuple.pyi]
+
+[case testTypeVarConstraintsDefaultAliasesExplicitAlias]
+from typing_extensions import TypeAlias, TypeVar
+
+K: TypeAlias = int
+V: TypeAlias = int
+L: TypeAlias = list[int]
+T1 = TypeVar("T1", str, K, default=K)
+T2 = TypeVar("T2", str, K, default=V)
+T3 = TypeVar("T3", str, L, default=L)
+[builtins fixtures/tuple.pyi]
+
+[case testTypeVarConstraintsDefaultSpecialTypes]
+from typing import Generic, NamedTuple
+from typing_extensions import TypedDict, TypeVar
+
+class TD(TypedDict):
+    foo: str
+
+class NT(NamedTuple):
+    foo: str
+
+T1 = TypeVar("T1", str, TD, default=TD)
+T2 = TypeVar("T2", str, NT, default=NT)
+
+class A1(Generic[T1]):
+    x: T1
+class A2(Generic[T2]):
+    x: T2
+
+reveal_type(A1().x)  # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.str})"
+reveal_type(A2().x)  # N: Revealed type is "tuple[builtins.str, fallback=__main__.NT]"
+[builtins fixtures/tuple.pyi]
+
+[case testTypeVarConstraintsDefaultSpecialTypesGeneric]
+from typing import Generic, NamedTuple
+from typing_extensions import TypedDict, TypeVar
+
+T = TypeVar("T")
+
+class TD(TypedDict, Generic[T]):
+    foo: T
+class TD2(TD[int]): pass
+class TD3(TD[int]):
+    bar: str
+
+class NT(NamedTuple, Generic[T]):
+    foo: T
+class NT2(NT[int]): pass
+
+T1 = TypeVar("T1", str, TD[int], default=TD[int])
+T2 = TypeVar("T2", str, NT[int], default=NT[int])
+T3 = TypeVar("T3", str, TD2, default=TD[int])
+T4 = TypeVar("T4", str, TD3, default=TD[int])  # E: TypeVar default must be one of the constraint types
+T5 = TypeVar("T5", str, NT2, default=NT[int])  # E: TypeVar default must be one of the constraint types
+
+class A1(Generic[T1]):
+    x: T1
+class A2(Generic[T2]):
+    x: T2
+class A3(Generic[T3]):
+    x: T3
+
+reveal_type(A1().x)  # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
+reveal_type(A2().x)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.NT[builtins.int]]"
+reveal_type(A3().x)  # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
+[builtins fixtures/tuple.pyi]

From b6f2ea326fdb18c180f2f89e81e5018f1e8f734f Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 5 Jun 2025 15:09:36 +0100
Subject: [PATCH 1354/1617] Handle assignment of bound methods in class bodies
 (#19233)

Fixes https://github.com/python/mypy/issues/18438
Fixes https://github.com/python/mypy/issues/19146

Surprisingly, a very small change is sufficient to replicate Python
runtime behavior for all the important cases (see `checkmember.py`). I
also replace the `bound_args` argument of `CallableType`, that was
mostly unused, with a flag (as suggested by @JukkaL) and make sure it is
properly set/preserved everywhere.
---
 mypy/checker.py                       |  2 +-
 mypy/checkexpr.py                     |  2 +-
 mypy/checkmember.py                   |  4 +--
 mypy/fixup.py                         |  3 --
 mypy/messages.py                      |  4 +--
 mypy/server/astdiff.py                |  1 +
 mypy/typeops.py                       |  3 +-
 mypy/types.py                         | 20 ++++++-------
 test-data/unit/check-classes.test     |  2 +-
 test-data/unit/check-functions.test   | 42 +++++++++++++++++++++++++++
 test-data/unit/check-incremental.test | 24 +++++++++++++++
 test-data/unit/fine-grained.test      | 24 +++++++++++++++
 12 files changed, 110 insertions(+), 21 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 2737216cf637..578f6f778273 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2449,7 +2449,7 @@ def erase_override(t: Type) -> Type:
                     if not is_subtype(original_arg_type, erase_override(override_arg_type)):
                         context: Context = node
                         if isinstance(node, FuncDef) and not node.is_property:
-                            arg_node = node.arguments[i + len(override.bound_args)]
+                            arg_node = node.arguments[i + override.bound()]
                             if arg_node.line != -1:
                                 context = arg_node
                         self.msg.argument_incompatible_with_supertype(
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 969713edb1a7..e0c7e829309c 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4975,7 +4975,7 @@ def apply_type_arguments_to_callable(
                         tp.fallback,
                         name="tuple",
                         definition=tp.definition,
-                        bound_args=tp.bound_args,
+                        is_bound=tp.is_bound,
                     )
                 self.msg.incompatible_type_application(
                     min_arg_count, len(type_vars), len(args), ctx
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index be89c2f09a80..50eaf42a9934 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -921,7 +921,7 @@ def analyze_var(
             bound_items = []
             for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]:
                 p_ct = get_proper_type(ct)
-                if isinstance(p_ct, FunctionLike) and not p_ct.is_type_obj():
+                if isinstance(p_ct, FunctionLike) and (not p_ct.bound() or var.is_property):
                     item = expand_and_bind_callable(p_ct, var, itype, name, mx, is_trivial_self)
                 else:
                     item = expand_without_binding(ct, var, itype, original_itype, mx)
@@ -1498,6 +1498,6 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F:
         arg_types=func.arg_types[1:],
         arg_kinds=func.arg_kinds[1:],
         arg_names=func.arg_names[1:],
-        bound_args=[original_type],
+        is_bound=True,
     )
     return cast(F, res)
diff --git a/mypy/fixup.py b/mypy/fixup.py
index 8e7cd40544bf..0e9c186fd42a 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -271,9 +271,6 @@ def visit_callable_type(self, ct: CallableType) -> None:
             ct.ret_type.accept(self)
         for v in ct.variables:
             v.accept(self)
-        for arg in ct.bound_args:
-            if arg:
-                arg.accept(self)
         if ct.type_guard is not None:
             ct.type_guard.accept(self)
         if ct.type_is is not None:
diff --git a/mypy/messages.py b/mypy/messages.py
index 8a90ae433dbc..9c4c141c4a79 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -644,8 +644,8 @@ def incompatible_argument(
         callee_name = callable_name(callee)
         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], self.options)
+            if object_type is not None:
+                base = format_type(object_type, self.options)
             else:
                 base = extract_type(name)
 
diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py
index 1b0cc218ed16..16a0d882a8aa 100644
--- a/mypy/server/astdiff.py
+++ b/mypy/server/astdiff.py
@@ -460,6 +460,7 @@ def visit_callable_type(self, typ: CallableType) -> SnapshotItem:
             typ.is_type_obj(),
             typ.is_ellipsis_args,
             snapshot_types(typ.variables),
+            typ.is_bound,
         )
 
     def normalize_callable_variables(self, typ: CallableType) -> CallableType:
diff --git a/mypy/typeops.py b/mypy/typeops.py
index b4abb246af07..e8087a1713ff 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -185,6 +185,7 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
                     arg_kinds=[ARG_STAR, ARG_STAR2],
                     arg_names=["_args", "_kwds"],
                     ret_type=any_type,
+                    is_bound=True,
                     fallback=named_type("builtins.function"),
                 )
                 return class_callable(sig, info, fallback, None, is_new=False)
@@ -479,7 +480,7 @@ class B(A): pass
         arg_kinds=func.arg_kinds[1:],
         arg_names=func.arg_names[1:],
         variables=variables,
-        bound_args=[original_type],
+        is_bound=True,
     )
     return cast(F, res)
 
diff --git a/mypy/types.py b/mypy/types.py
index b598a6116136..47a59291df52 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1605,6 +1605,9 @@ def with_name(self, name: str) -> FunctionLike:
     def get_name(self) -> str | None:
         pass
 
+    def bound(self) -> bool:
+        return bool(self.items) and self.items[0].is_bound
+
 
 class FormalArgument(NamedTuple):
     name: str | None
@@ -1834,8 +1837,7 @@ class CallableType(FunctionLike):
         # 'dict' and 'partial' for a `functools.partial` evaluation)
         "from_type_type",  # Was this callable generated by analyzing Type[...]
         # instantiation?
-        "bound_args",  # Bound type args, mostly unused but may be useful for
-        # tools that consume mypy ASTs
+        "is_bound",  # Is this a bound method?
         "def_extras",  # Information about original definition we want to serialize.
         # This is used for more detailed error messages.
         "type_guard",  # T, if -> TypeGuard[T] (ret_type is bool in this case).
@@ -1863,7 +1865,7 @@ def __init__(
         implicit: bool = False,
         special_sig: str | None = None,
         from_type_type: bool = False,
-        bound_args: Sequence[Type | None] = (),
+        is_bound: bool = False,
         def_extras: dict[str, Any] | None = None,
         type_guard: Type | None = None,
         type_is: Type | None = None,
@@ -1896,9 +1898,7 @@ def __init__(
         self.from_type_type = from_type_type
         self.from_concatenate = from_concatenate
         self.imprecise_arg_kinds = imprecise_arg_kinds
-        if not bound_args:
-            bound_args = ()
-        self.bound_args = bound_args
+        self.is_bound = is_bound
         if def_extras:
             self.def_extras = def_extras
         elif isinstance(definition, FuncDef):
@@ -1935,7 +1935,7 @@ def copy_modified(
         implicit: Bogus[bool] = _dummy,
         special_sig: Bogus[str | None] = _dummy,
         from_type_type: Bogus[bool] = _dummy,
-        bound_args: Bogus[list[Type | None]] = _dummy,
+        is_bound: Bogus[bool] = _dummy,
         def_extras: Bogus[dict[str, Any]] = _dummy,
         type_guard: Bogus[Type | None] = _dummy,
         type_is: Bogus[Type | None] = _dummy,
@@ -1960,7 +1960,7 @@ def copy_modified(
             implicit=implicit if implicit is not _dummy else self.implicit,
             special_sig=special_sig if special_sig is not _dummy else self.special_sig,
             from_type_type=from_type_type if from_type_type is not _dummy else self.from_type_type,
-            bound_args=bound_args if bound_args is not _dummy else self.bound_args,
+            is_bound=is_bound if is_bound is not _dummy else self.is_bound,
             def_extras=def_extras if def_extras is not _dummy else dict(self.def_extras),
             type_guard=type_guard if type_guard is not _dummy else self.type_guard,
             type_is=type_is if type_is is not _dummy else self.type_is,
@@ -2285,7 +2285,7 @@ def serialize(self) -> JsonDict:
             "variables": [v.serialize() for v in self.variables],
             "is_ellipsis_args": self.is_ellipsis_args,
             "implicit": self.implicit,
-            "bound_args": [(None if t is None else t.serialize()) for t in self.bound_args],
+            "is_bound": self.is_bound,
             "def_extras": dict(self.def_extras),
             "type_guard": self.type_guard.serialize() if self.type_guard is not None else None,
             "type_is": (self.type_is.serialize() if self.type_is is not None else None),
@@ -2308,7 +2308,7 @@ def deserialize(cls, data: JsonDict) -> CallableType:
             variables=[cast(TypeVarLikeType, deserialize_type(v)) for v in data["variables"]],
             is_ellipsis_args=data["is_ellipsis_args"],
             implicit=data["implicit"],
-            bound_args=[(None if t is None else deserialize_type(t)) for t in data["bound_args"]],
+            is_bound=data["is_bound"],
             def_extras=data["def_extras"],
             type_guard=(
                 deserialize_type(data["type_guard"]) if data["type_guard"] is not None else None
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 9c95458361fd..dc421cbd43b9 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -4292,7 +4292,7 @@ int.__eq__(3, 4)
 [builtins fixtures/args.pyi]
 [out]
 main:33: error: Too few arguments for "__eq__" of "int"
-main:33: error: Unsupported operand types for == ("int" and "type[int]")
+main:33: error: Unsupported operand types for == ("type[int]" and "type[int]")
 
 [case testDupBaseClasses]
 class A:
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index 4ef8e47e763a..f86d4ed76350 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3591,3 +3591,45 @@ class Bar(Foo):
 
     def foo(self, value: Union[int, str]) -> Union[int, str]:
         return super().foo(value)  # E: Call to abstract method "foo" of "Foo" with trivial body via super() is unsafe
+
+[case testBoundMethodsAssignedInClassBody]
+from typing import Callable
+
+class A:
+    def f(self, x: int) -> str:
+        pass
+    @classmethod
+    def g(cls, x: int) -> str:
+        pass
+    @staticmethod
+    def h(x: int) -> str:
+        pass
+    attr: Callable[[int], str]
+
+class C:
+    x1 = A.f
+    x2 = A.g
+    x3 = A().f
+    x4 = A().g
+    x5 = A.h
+    x6 = A().h
+    x7 = A().attr
+
+reveal_type(C.x1)  # N: Revealed type is "def (self: __main__.A, x: builtins.int) -> builtins.str"
+reveal_type(C.x2)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C.x3)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C.x4)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C.x5)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C.x6)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C.x7)  # N: Revealed type is "def (builtins.int) -> builtins.str"
+
+reveal_type(C().x1)  # E: Invalid self argument "C" to attribute function "x1" with type "Callable[[A, int], str]" \
+                     # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x2)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x3)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x4)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x5)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x6)  # N: Revealed type is "def (x: builtins.int) -> builtins.str"
+reveal_type(C().x7)  # E: Invalid self argument "C" to attribute function "x7" with type "Callable[[int], str]" \
+                     # N: Revealed type is "def () -> builtins.str"
+[builtins fixtures/classmethod.pyi]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index a8116d9cf78a..4c170ec4753f 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -6862,3 +6862,27 @@ if int():
 [out]
 [out2]
 main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int")
+
+[case testMethodMakeBoundIncremental]
+from a import A
+a = A()
+a.f()
+[file a.py]
+class B:
+    def f(self, s: A) -> int: ...
+
+def f(s: A) -> int: ...
+
+class A:
+    f = f
+[file a.py.2]
+class B:
+    def f(self, s: A) -> int: ...
+
+def f(s: A) -> int: ...
+
+class A:
+    f = B().f
+[out]
+[out2]
+main:3: error: Too few arguments
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 5df62c80168b..ddb1b7266a57 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -11217,3 +11217,27 @@ class A:
 [out]
 ==
 main:3: error: Property "f" defined in "A" is read-only
+
+[case testMethodMakeBoundFineGrained]
+from a import A
+a = A()
+a.f()
+[file a.py]
+class B:
+    def f(self, s: A) -> int: ...
+
+def f(s: A) -> int: ...
+
+class A:
+    f = f
+[file a.py.2]
+class B:
+    def f(self, s: A) -> int: ...
+
+def f(s: A) -> int: ...
+
+class A:
+    f = B().f
+[out]
+==
+main:3: error: Too few arguments

From 85d0e14cde26d3f8d279ff3152d1d3948b76ae7a Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 16:24:34 +0200
Subject: [PATCH 1355/1617] Make infer_condition_value recognize the whole
 truth table (#18944)

Fixes #18901.
---
 mypy/reachability.py                       |  49 ++++++----
 test-data/unit/check-unreachable-code.test | 100 ++++++++++++++++++---
 test-data/unit/fixtures/ops.pyi            |   2 +-
 3 files changed, 119 insertions(+), 32 deletions(-)

diff --git a/mypy/reachability.py b/mypy/reachability.py
index 5d170b5071db..132c269e96af 100644
--- a/mypy/reachability.py
+++ b/mypy/reachability.py
@@ -115,31 +115,44 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
     MYPY_TRUE if true under mypy and false at runtime, MYPY_FALSE if
     false under mypy and true at runtime, else TRUTH_VALUE_UNKNOWN.
     """
+    if isinstance(expr, UnaryExpr) and expr.op == "not":
+        positive = infer_condition_value(expr.expr, options)
+        return inverted_truth_mapping[positive]
+
     pyversion = options.python_version
     name = ""
-    negated = False
-    alias = expr
-    if isinstance(alias, UnaryExpr):
-        if alias.op == "not":
-            expr = alias.expr
-            negated = True
+
     result = TRUTH_VALUE_UNKNOWN
     if isinstance(expr, NameExpr):
         name = expr.name
     elif isinstance(expr, MemberExpr):
         name = expr.name
-    elif isinstance(expr, OpExpr) and expr.op in ("and", "or"):
+    elif isinstance(expr, OpExpr):
+        if expr.op not in ("or", "and"):
+            return TRUTH_VALUE_UNKNOWN
+
         left = infer_condition_value(expr.left, options)
-        if (left in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == "and") or (
-            left in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == "or"
-        ):
-            # Either `True and ` or `False or `: the result will
-            # always be the right-hand-side.
-            return infer_condition_value(expr.right, options)
-        else:
-            # The result will always be the left-hand-side (e.g. ALWAYS_* or
-            # TRUTH_VALUE_UNKNOWN).
-            return left
+        right = infer_condition_value(expr.right, options)
+        results = {left, right}
+        if expr.op == "or":
+            if ALWAYS_TRUE in results:
+                return ALWAYS_TRUE
+            elif MYPY_TRUE in results:
+                return MYPY_TRUE
+            elif left == right == MYPY_FALSE:
+                return MYPY_FALSE
+            elif results <= {ALWAYS_FALSE, MYPY_FALSE}:
+                return ALWAYS_FALSE
+        elif expr.op == "and":
+            if ALWAYS_FALSE in results:
+                return ALWAYS_FALSE
+            elif MYPY_FALSE in results:
+                return MYPY_FALSE
+            elif left == right == ALWAYS_TRUE:
+                return ALWAYS_TRUE
+            elif results <= {ALWAYS_TRUE, MYPY_TRUE}:
+                return MYPY_TRUE
+        return TRUTH_VALUE_UNKNOWN
     else:
         result = consider_sys_version_info(expr, pyversion)
         if result == TRUTH_VALUE_UNKNOWN:
@@ -155,8 +168,6 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
             result = ALWAYS_TRUE
         elif name in options.always_false:
             result = ALWAYS_FALSE
-    if negated:
-        result = inverted_truth_mapping[result]
     return result
 
 
diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test
index 6821b74b8b6d..368431127b76 100644
--- a/test-data/unit/check-unreachable-code.test
+++ b/test-data/unit/check-unreachable-code.test
@@ -481,25 +481,101 @@ import typing
 def make() -> bool: pass
 PY2 = PY3 = make()
 
-a = PY2 and 's'
-b = PY3 and 's'
-c = PY2 or 's'
-d = PY3 or 's'
-e = (PY2 or PY3) and 's'
-f = (PY3 or PY2) and 's'
-g = (PY2 or PY3) or 's'
-h = (PY3 or PY2) or 's'
+a = PY2 and str()
+b = PY3 and str()
+c = PY2 or str()
+d = PY3 or str()
+e = (PY2 or PY3) and str()
+f = (PY3 or PY2) and str()
+g = (PY2 or PY3) or str()
+h = (PY3 or PY2) or str()
 reveal_type(a)  # N: Revealed type is "builtins.bool"
-reveal_type(b)  # N: Revealed type is "Literal['s']"
-reveal_type(c)  # N: Revealed type is "Literal['s']"
+reveal_type(b)  # N: Revealed type is "builtins.str"
+reveal_type(c)  # N: Revealed type is "builtins.str"
 reveal_type(d)  # N: Revealed type is "builtins.bool"
-reveal_type(e)  # N: Revealed type is "Literal['s']"
-reveal_type(f)  # N: Revealed type is "Literal['s']"
+reveal_type(e)  # N: Revealed type is "builtins.str"
+reveal_type(f)  # N: Revealed type is "builtins.str"
 reveal_type(g)  # N: Revealed type is "builtins.bool"
 reveal_type(h)  # N: Revealed type is "builtins.bool"
 [builtins fixtures/ops.pyi]
 [out]
 
+[case testConditionalValuesBinaryOps]
+# flags: --platform linux
+import sys
+
+t_and_t = (sys.platform == 'linux' and sys.platform == 'linux') and str()
+t_or_t = (sys.platform == 'linux' or sys.platform == 'linux') and str()
+t_and_f = (sys.platform == 'linux' and sys.platform == 'windows') and str()
+t_or_f = (sys.platform == 'linux' or sys.platform == 'windows') and str()
+f_and_t = (sys.platform == 'windows' and sys.platform == 'linux') and str()
+f_or_t = (sys.platform == 'windows' or sys.platform == 'linux') and str()
+f_and_f = (sys.platform == 'windows' and sys.platform == 'windows') and str()
+f_or_f = (sys.platform == 'windows' or sys.platform == 'windows') and str()
+reveal_type(t_and_t) # N: Revealed type is "builtins.str"
+reveal_type(t_or_t) # N: Revealed type is "builtins.str"
+reveal_type(f_and_t) # N: Revealed type is "builtins.bool"
+reveal_type(f_or_t) # N: Revealed type is "builtins.str"
+reveal_type(t_and_f) # N: Revealed type is "builtins.bool"
+reveal_type(t_or_f) # N: Revealed type is "builtins.str"
+reveal_type(f_and_f) # N: Revealed type is "builtins.bool"
+reveal_type(f_or_f) # N: Revealed type is "builtins.bool"
+[builtins fixtures/ops.pyi]
+
+[case testConditionalValuesNegation]
+# flags: --platform linux
+import sys
+
+not_t = not sys.platform == 'linux' and str()
+not_f = not sys.platform == 'windows' and str()
+not_and_t = not (sys.platform == 'linux' and sys.platform == 'linux') and str()
+not_and_f = not (sys.platform == 'linux' and sys.platform == 'windows') and str()
+not_or_t = not (sys.platform == 'linux' or sys.platform == 'linux') and str()
+not_or_f = not (sys.platform == 'windows' or sys.platform == 'windows') and str()
+reveal_type(not_t) # N: Revealed type is "builtins.bool"
+reveal_type(not_f) # N: Revealed type is "builtins.str"
+reveal_type(not_and_t) # N: Revealed type is "builtins.bool"
+reveal_type(not_and_f) # N: Revealed type is "builtins.str"
+reveal_type(not_or_t) # N: Revealed type is "builtins.bool"
+reveal_type(not_or_f) # N: Revealed type is "builtins.str"
+[builtins fixtures/ops.pyi]
+
+[case testConditionalValuesUnsupportedOps]
+# flags: --platform linux
+import sys
+
+unary_minus = -(sys.platform == 'linux') and str()
+binary_minus = ((sys.platform == 'linux') - (sys.platform == 'linux')) and str()
+reveal_type(unary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]"
+reveal_type(binary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]"
+[builtins fixtures/ops.pyi]
+
+[case testMypyFalseValuesInBinaryOps_no_empty]
+# flags: --platform linux
+import sys
+from typing import TYPE_CHECKING
+
+MYPY = 0
+
+if TYPE_CHECKING and sys.platform == 'linux':
+    def foo1() -> int: ...
+if sys.platform == 'linux' and TYPE_CHECKING:
+    def foo2() -> int: ...
+if MYPY and sys.platform == 'linux':
+    def foo3() -> int: ...
+if sys.platform == 'linux' and MYPY:
+    def foo4() -> int: ...
+
+if TYPE_CHECKING or sys.platform == 'linux':
+    def bar1() -> int: ...  # E: Missing return statement
+if sys.platform == 'linux' or TYPE_CHECKING:
+    def bar2() -> int: ...  # E: Missing return statement
+if MYPY or sys.platform == 'linux':
+    def bar3() -> int: ...  # E: Missing return statement
+if sys.platform == 'linux' or MYPY:
+    def bar4() -> int: ...  # E: Missing return statement
+[builtins fixtures/ops.pyi]
+
 [case testShortCircuitAndWithConditionalAssignment]
 # flags: --platform linux
 import sys
diff --git a/test-data/unit/fixtures/ops.pyi b/test-data/unit/fixtures/ops.pyi
index df3b163166ad..67bc74b35c51 100644
--- a/test-data/unit/fixtures/ops.pyi
+++ b/test-data/unit/fixtures/ops.pyi
@@ -25,7 +25,7 @@ class tuple(Sequence[Tco]):
 class function: pass
 
 class str:
-    def __init__(self, x: 'int') -> None: pass
+    def __init__(self, x: 'int' = ...) -> None: pass
     def __add__(self, x: 'str') -> 'str': pass
     def __eq__(self, x: object) -> bool: pass
     def startswith(self, x: 'str') -> bool: pass

From cb0d5b57d71f57654f30b3841c6a68e6a252ef9f Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 5 Jun 2025 16:27:49 +0200
Subject: [PATCH 1356/1617] Add initial changelog entries for 1.17 (#19200)

---
 CHANGELOG.md | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b09916919d8a..a1470b7d50c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,27 @@
 
 ## Next Release
 
+### Remove Support for targeting Python 3.8
+
+Mypy now requires `--python-version 3.9` or greater. Support for only Python 3.8 is
+fully removed now. Given an unsupported version, mypy will default to the oldest
+supported one, currently 3.9.
+
+This change is necessary because typeshed stopped supporting Python 3.8 after it
+reached its End of Life in October 2024.
+
+Contributed by Marc Mueller
+(PR [19157](https://github.com/python/mypy/pull/19157), PR [19162](https://github.com/python/mypy/pull/19162)).
+
+### Initial Support for Python 3.14
+
+Mypy is now tested on 3.14 and mypyc works with 3.14.0b3 and later.
+Mypyc compiled wheels of mypy itself will be available for new versions after 3.14.0rc1 is released.
+
+Note that not all new features might be supported just yet.
+
+Contributed by Marc Mueller (PR [19164](https://github.com/python/mypy/pull/19164))
+
 ### 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.

From f90227519d62c4507d7e33db2f018ca6da3a6170 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 16:30:36 +0200
Subject: [PATCH 1357/1617] Erase stray typevars in functools.partial generic
 (#18954)

Fixes #18953. Fixes #15215. Refs #17461.

When the function passed to `partial` is generic and has generic params
in the return type, we must erase them, otherwise they become orphan and
cannot be used later. This only applies to `partial[...]` generic param
and not to the underlying "exact" callable stored internally as the
latter remains generic.

The ultimate fix would be to implement #17620 so that we stop caring
about `partial[...]` generic param, but this should improve usability
(but causes false negatives).
---
 mypy/plugins/functools.py           |  7 ++-
 test-data/unit/check-functools.test | 70 +++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py
index 25a8c83007ba..c8b370f15e6d 100644
--- a/mypy/plugins/functools.py
+++ b/mypy/plugins/functools.py
@@ -8,6 +8,7 @@
 import mypy.plugin
 import mypy.semanal
 from mypy.argmap import map_actuals_to_formals
+from mypy.erasetype import erase_typevars
 from mypy.nodes import (
     ARG_POS,
     ARG_STAR2,
@@ -312,7 +313,11 @@ def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) -
         special_sig="partial",
     )
 
-    ret = ctx.api.named_generic_type(PARTIAL, [ret_type])
+    # Do not leak typevars from generic functions - they cannot be usable.
+    # Keep them in the wrapped callable, but avoid `partial[SomeStrayTypeVar]`
+    erased_ret_type = erase_typevars(ret_type, [tv.id for tv in fn_type.variables])
+
+    ret = ctx.api.named_generic_type(PARTIAL, [erased_ret_type])
     ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied)
     if partially_applied.param_spec():
         assert ret.extra_attrs is not None  # copy_with_extra_attr above ensures this
diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test
index ebfddf7d9562..fa2cacda275d 100644
--- a/test-data/unit/check-functools.test
+++ b/test-data/unit/check-functools.test
@@ -656,3 +656,73 @@ def f(x: P):
     # TODO: but this is incorrect, predating the functools.partial plugin
     reveal_type(partial(x, "a")())  # N: Revealed type is "builtins.int"
 [builtins fixtures/tuple.pyi]
+
+[case testFunctoolsPartialTypeVarErasure]
+from typing import Callable, TypeVar, Union
+from typing_extensions import ParamSpec, TypeVarTuple, Unpack
+from functools import partial
+
+def use_int_callable(x: Callable[[int], int]) -> None:
+    pass
+def use_func_callable(
+    x: Callable[
+        [Callable[[int], None]],
+        Callable[[int], None],
+    ],
+) -> None:
+    pass
+
+Tc = TypeVar("Tc", int, str)
+Tb = TypeVar("Tb", bound=Union[int, str])
+P = ParamSpec("P")
+Ts = TypeVarTuple("Ts")
+
+def func_b(a: Tb, b: str) -> Tb:
+    return a
+def func_c(a: Tc, b: str) -> Tc:
+    return a
+
+def func_fn(fn: Callable[P, Tc], b: str) -> Callable[P, Tc]:
+    return fn
+def func_fn_unpack(fn: Callable[[Unpack[Ts]], Tc], b: str) -> Callable[[Unpack[Ts]], Tc]:
+    return fn
+
+# We should not leak stray typevars that aren't in scope:
+reveal_type(partial(func_b, b=""))  # N: Revealed type is "functools.partial[Any]"
+reveal_type(partial(func_c, b=""))  # N: Revealed type is "functools.partial[Any]"
+reveal_type(partial(func_fn, b=""))  # N: Revealed type is "functools.partial[def (*Any, **Any) -> Any]"
+reveal_type(partial(func_fn_unpack, b=""))  # N: Revealed type is "functools.partial[def (*Any) -> Any]"
+
+use_int_callable(partial(func_b, b=""))
+use_func_callable(partial(func_b, b=""))
+use_int_callable(partial(func_c, b=""))
+use_func_callable(partial(func_c, b=""))
+use_int_callable(partial(func_fn, b=""))  # E: Argument 1 to "use_int_callable" has incompatible type "partial[Callable[[VarArg(Any), KwArg(Any)], Any]]"; expected "Callable[[int], int]" \
+                                          # N: "partial[Callable[[VarArg(Any), KwArg(Any)], Any]].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Callable[[VarArg(Any), KwArg(Any)], Any]]"
+use_func_callable(partial(func_fn, b=""))
+use_int_callable(partial(func_fn_unpack, b=""))  # E: Argument 1 to "use_int_callable" has incompatible type "partial[Callable[[VarArg(Any)], Any]]"; expected "Callable[[int], int]" \
+                                                 # N: "partial[Callable[[VarArg(Any)], Any]].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Callable[[VarArg(Any)], Any]]"
+use_func_callable(partial(func_fn_unpack, b=""))
+
+# But we should not erase typevars that aren't bound by function
+# passed to `partial`:
+
+def outer_b(arg: Tb) -> None:
+
+    def inner(a: Tb, b: str) -> Tb:
+        return a
+
+    reveal_type(partial(inner, b=""))  # N: Revealed type is "functools.partial[Tb`-1]"
+    use_int_callable(partial(inner, b=""))  # E: Argument 1 to "use_int_callable" has incompatible type "partial[Tb]"; expected "Callable[[int], int]" \
+                                            # N: "partial[Tb].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Tb]"
+
+def outer_c(arg: Tc) -> None:
+
+    def inner(a: Tc, b: str) -> Tc:
+        return a
+
+    reveal_type(partial(inner, b=""))  # N: Revealed type is "functools.partial[builtins.int]" \
+                                       # N: Revealed type is "functools.partial[builtins.str]"
+    use_int_callable(partial(inner, b=""))  # E: Argument 1 to "use_int_callable" has incompatible type "partial[str]"; expected "Callable[[int], int]" \
+                                            # N: "partial[str].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], str]"
+[builtins fixtures/tuple.pyi]

From ce6355e1f17078ab5f3e581e507dd84479758d3d Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 16:54:48 +0200
Subject: [PATCH 1358/1617] Fix type extraction from `isinstance` checks
 (#19223)

Fixes #19221. Instead of trying to use the first (maybe) overload item
and erase it, just use the underlying type with Any-filled typevars
---
 mypy/checker.py                     | 10 +++--
 test-data/unit/check-narrowing.test | 57 +++++++++++++++++++++++++++++
 test-data/unit/check-typeddict.test |  2 +-
 3 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 578f6f778273..1812af939665 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -7697,9 +7697,13 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None:
         types: list[TypeRange] = []
         for typ in all_types:
             if isinstance(typ, FunctionLike) and typ.is_type_obj():
-                # Type variables may be present -- erase them, which is the best
-                # we can do (outside disallowing them here).
-                erased_type = erase_typevars(typ.items[0].ret_type)
+                # If a type is generic, `isinstance` can only narrow its variables to Any.
+                any_parameterized = fill_typevars_with_any(typ.type_object())
+                # Tuples may have unattended type variables among their items
+                if isinstance(any_parameterized, TupleType):
+                    erased_type = erase_typevars(any_parameterized)
+                else:
+                    erased_type = any_parameterized
                 types.append(TypeRange(erased_type, is_upper_bound=False))
             elif isinstance(typ, TypeType):
                 # Type[A] means "any type that is a subtype of A" rather than "precisely type A"
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 36b2ced075d2..a5c8f53b9726 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2463,3 +2463,60 @@ def test(x: T) -> T:
     reveal_type(x.x)  # N: Revealed type is "builtins.str"
     return x
 [builtins fixtures/isinstance.pyi]
+
+[case testIsinstanceNarrowingWithSelfTypes]
+from typing import Generic, TypeVar, overload
+
+T = TypeVar("T")
+
+class A(Generic[T]):
+    def __init__(self: A[int]) -> None:
+        pass
+
+def check_a(obj: "A[T] | str") -> None:
+    reveal_type(obj)  # N: Revealed type is "Union[__main__.A[T`-1], builtins.str]"
+    if isinstance(obj, A):
+        reveal_type(obj)  # N: Revealed type is "__main__.A[T`-1]"
+    else:
+        reveal_type(obj)  # N: Revealed type is "builtins.str"
+
+
+class B(Generic[T]):
+    @overload
+    def __init__(self, x: T) -> None: ...
+    @overload
+    def __init__(self: B[int]) -> None: ...
+    def __init__(self, x: "T | None" = None) -> None:
+        pass
+
+def check_b(obj: "B[T] | str") -> None:
+    reveal_type(obj)  # N: Revealed type is "Union[__main__.B[T`-1], builtins.str]"
+    if isinstance(obj, B):
+        reveal_type(obj)  # N: Revealed type is "__main__.B[T`-1]"
+    else:
+        reveal_type(obj)  # N: Revealed type is "builtins.str"
+
+
+class C(Generic[T]):
+    @overload
+    def __init__(self: C[int]) -> None: ...
+    @overload
+    def __init__(self, x: T) -> None: ...
+    def __init__(self, x: "T | None" = None) -> None:
+        pass
+
+def check_c(obj: "C[T] | str") -> None:
+    reveal_type(obj)  # N: Revealed type is "Union[__main__.C[T`-1], builtins.str]"
+    if isinstance(obj, C):
+        reveal_type(obj)  # N: Revealed type is "__main__.C[T`-1]"
+    else:
+        reveal_type(obj)  # N: Revealed type is "builtins.str"
+
+
+class D(tuple[T], Generic[T]): ...
+
+def check_d(arg: D[T]) -> None:
+    if not isinstance(arg, D):
+        return
+    reveal_type(arg)  # N: Revealed type is "tuple[T`-1, fallback=__main__.D[Any]]"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index 4ac69321a250..6bcc6e20328b 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -810,7 +810,7 @@ from typing import TypedDict
 D = TypedDict('D', {'x': int})
 d: object
 if isinstance(d, D):   # E: Cannot use isinstance() with TypedDict type
-    reveal_type(d)     # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.int})"
+    reveal_type(d)     # N: Revealed type is "__main__.D"
 issubclass(object, D)  # E: Cannot use issubclass() with TypedDict type
 [builtins fixtures/isinstancelist.pyi]
 [typing fixtures/typing-typeddict.pyi]

From b025bda885027aa12965135e376a060d7582df06 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 5 Jun 2025 17:11:54 +0200
Subject: [PATCH 1359/1617] Remove last unreachable block from mypyc code
 (#19086)

Supplements #19050. The old version did certainly contain unreachable
code, as otherwise that branch would have crashed, there is no
`type_args` attribute. This block was introduced in #17027 - probably as
a future-proof expansion to generic primitive operations, but it makes
no sense now, this logic can be added back should it become necessary.
---
 mypyc/ir/pprint.py | 14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py
index ac0e791290ab..6c96a21e473b 100644
--- a/mypyc/ir/pprint.py
+++ b/mypyc/ir/pprint.py
@@ -220,19 +220,7 @@ def visit_call_c(self, op: CallC) -> str:
             return self.format("%r = %s(%s)", op, op.function_name, args_str)
 
     def visit_primitive_op(self, op: PrimitiveOp) -> str:
-        args = []
-        arg_index = 0
-        type_arg_index = 0
-        for arg_type in zip(op.desc.arg_types):
-            if arg_type:
-                args.append(self.format("%r", op.args[arg_index]))
-                arg_index += 1
-            else:
-                assert op.type_args
-                args.append(self.format("%r", op.type_args[type_arg_index]))
-                type_arg_index += 1
-
-        args_str = ", ".join(args)
+        args_str = ", ".join(self.format("%r", arg) for arg in op.args)
         if op.is_void:
             return self.format("%s %s", op.desc.name, args_str)
         else:

From 55c4067a22e69b8c5e386f80821fa6d969b126a3 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Fri, 6 Jun 2025 15:54:12 +0200
Subject: [PATCH 1360/1617] Avoid false `unreachable`, `redundant-expr`, and
 `redundant-casts` warnings in loops more robustly and efficiently, and avoid
 multiple `revealed type` notes for the same line. (#19118)

Fixes #18606
Closes #18511
Improves #18991
Fixes #19170

This change is an improvement over 9685171. Besides fixing the
regressions reported in #18606 and #19170 and removing the duplicates
reported in #18511, it should significantly reduce the performance
regression reported in #18991. At least running `Measure-command {python
runtests.py self}` on my computer (with removed cache) is 10 % faster.
---
 mypy/checker.py                         | 57 +++++++++++++++-------
 mypy/errors.py                          | 58 ++++++++++++++++++++++-
 test-data/unit/check-inference.test     |  4 +-
 test-data/unit/check-narrowing.test     | 63 +++++++++++++++++++++++--
 test-data/unit/check-redefine2.test     | 11 ++---
 test-data/unit/check-typevar-tuple.test |  2 +-
 6 files changed, 162 insertions(+), 33 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 1812af939665..d6eac718f008 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -25,7 +25,7 @@
 from mypy.constraints import SUPERTYPE_OF
 from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values
 from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode
-from mypy.errors import Errors, ErrorWatcher, report_internal_error
+from mypy.errors import Errors, ErrorWatcher, LoopErrorWatcher, report_internal_error
 from mypy.expandtype import expand_type
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash
 from mypy.maptype import map_instance_to_supertype
@@ -599,19 +599,27 @@ def accept_loop(
             # on without bound otherwise)
             widened_old = len(self.widened_vars)
 
-            # Disable error types that we cannot safely identify in intermediate iteration steps:
-            warn_unreachable = self.options.warn_unreachable
-            warn_redundant = codes.REDUNDANT_EXPR in self.options.enabled_error_codes
-            self.options.warn_unreachable = False
-            self.options.enabled_error_codes.discard(codes.REDUNDANT_EXPR)
-
+            # one set of `unreachable`, `redundant-expr`, and `redundant-casts` errors
+            # per iteration step:
+            uselessness_errors = []
+            # one set of unreachable line numbers per iteration step:
+            unreachable_lines = []
+            # one set of revealed types per line where `reveal_type` is used (each
+            # created set can grow during the iteration):
+            revealed_types = defaultdict(set)
             iter = 1
             while True:
                 with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1):
                     if on_enter_body is not None:
                         on_enter_body()
 
-                    self.accept(body)
+                    with LoopErrorWatcher(self.msg.errors) as watcher:
+                        self.accept(body)
+                    uselessness_errors.append(watcher.uselessness_errors)
+                    unreachable_lines.append(watcher.unreachable_lines)
+                    for key, values in watcher.revealed_types.items():
+                        revealed_types[key].update(values)
+
                 partials_new = sum(len(pts.map) for pts in self.partial_types)
                 widened_new = len(self.widened_vars)
                 # Perform multiple iterations if something changed that might affect
@@ -632,16 +640,29 @@ def accept_loop(
                 if iter == 20:
                     raise RuntimeError("Too many iterations when checking a loop")
 
-            # If necessary, reset the modified options and make up for the postponed error checks:
-            self.options.warn_unreachable = warn_unreachable
-            if warn_redundant:
-                self.options.enabled_error_codes.add(codes.REDUNDANT_EXPR)
-            if warn_unreachable or warn_redundant:
-                with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1):
-                    if on_enter_body is not None:
-                        on_enter_body()
-
-                    self.accept(body)
+            # Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
+            # errors that could not be ruled out in any iteration step:
+            persistent_uselessness_errors = set()
+            for candidate in set(itertools.chain(*uselessness_errors)):
+                if all(
+                    (candidate in errors) or (candidate[2] in lines)
+                    for errors, lines in zip(uselessness_errors, unreachable_lines)
+                ):
+                    persistent_uselessness_errors.add(candidate)
+            for error_info in persistent_uselessness_errors:
+                context = Context(line=error_info[2], column=error_info[3])
+                context.end_line = error_info[4]
+                context.end_column = error_info[5]
+                self.msg.fail(error_info[1], context, code=error_info[0])
+
+            #  Report all types revealed in at least one iteration step:
+            for note_info, types in revealed_types.items():
+                sorted_ = sorted(types, key=lambda typ: typ.lower())
+                revealed = sorted_[0] if len(types) == 1 else f"Union[{', '.join(sorted_)}]"
+                context = Context(line=note_info[1], column=note_info[2])
+                context.end_line = note_info[3]
+                context.end_column = note_info[4]
+                self.note(f'Revealed type is "{revealed}"', context)
 
             # If exit_condition is set, assume it must be False on exit from the loop:
             if exit_condition:
diff --git a/mypy/errors.py b/mypy/errors.py
index c9510ae5f1eb..6aa19ed7c5a0 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -6,7 +6,7 @@
 from collections import defaultdict
 from collections.abc import Iterable
 from typing import Callable, Final, NoReturn, Optional, TextIO, TypeVar
-from typing_extensions import Literal, TypeAlias as _TypeAlias
+from typing_extensions import Literal, Self, TypeAlias as _TypeAlias
 
 from mypy import errorcodes as codes
 from mypy.error_formatter import ErrorFormatter
@@ -179,7 +179,7 @@ def __init__(
         self._filter_deprecated = filter_deprecated
         self._filtered: list[ErrorInfo] | None = [] if save_filtered_errors else None
 
-    def __enter__(self) -> ErrorWatcher:
+    def __enter__(self) -> Self:
         self.errors._watchers.append(self)
         return self
 
@@ -220,6 +220,60 @@ def filtered_errors(self) -> list[ErrorInfo]:
         return self._filtered
 
 
+class LoopErrorWatcher(ErrorWatcher):
+    """Error watcher that filters and separately collects `unreachable` errors,
+    `redundant-expr` and `redundant-casts` errors, and revealed types when analysing
+    loops iteratively to help avoid making too-hasty reports."""
+
+    # Meaning of the tuple items: ErrorCode, message, line, column, end_line, end_column:
+    uselessness_errors: set[tuple[ErrorCode, str, int, int, int, int]]
+
+    # Meaning of the tuple items: function_or_member, line, column, end_line, end_column:
+    revealed_types: dict[tuple[str | None, int, int, int, int], set[str]]
+
+    # Not only the lines where the error report occurs but really all unreachable lines:
+    unreachable_lines: set[int]
+
+    def __init__(
+        self,
+        errors: Errors,
+        *,
+        filter_errors: bool | Callable[[str, ErrorInfo], bool] = False,
+        save_filtered_errors: bool = False,
+        filter_deprecated: bool = False,
+    ) -> None:
+        super().__init__(
+            errors,
+            filter_errors=filter_errors,
+            save_filtered_errors=save_filtered_errors,
+            filter_deprecated=filter_deprecated,
+        )
+        self.uselessness_errors = set()
+        self.unreachable_lines = set()
+        self.revealed_types = defaultdict(set)
+
+    def on_error(self, file: str, info: ErrorInfo) -> bool:
+
+        if info.code in (codes.UNREACHABLE, codes.REDUNDANT_EXPR, codes.REDUNDANT_CAST):
+            self.uselessness_errors.add(
+                (info.code, info.message, info.line, info.column, info.end_line, info.end_column)
+            )
+            if info.code == codes.UNREACHABLE:
+                self.unreachable_lines.update(range(info.line, info.end_line + 1))
+            return True
+
+        if info.code == codes.MISC and info.message.startswith("Revealed type is "):
+            key = info.function_or_member, info.line, info.column, info.end_line, info.end_column
+            types = info.message.split('"')[1]
+            if types.startswith("Union["):
+                self.revealed_types[key].update(types[6:-1].split(", "))
+            else:
+                self.revealed_types[key].add(types)
+            return True
+
+        return super().on_error(file, info)
+
+
 class Errors:
     """Container for compile errors.
 
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index b563eef0f8aa..856d430a544c 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -343,7 +343,7 @@ for var2 in [g, h, i, j, k, l]:
     reveal_type(var2)  # N: Revealed type is "Union[builtins.int, builtins.str]"
 
 for var3 in [m, n, o, p, q, r]:
-    reveal_type(var3)  # N: Revealed type is "Union[builtins.int, Any]"
+    reveal_type(var3)  # N: Revealed type is "Union[Any, builtins.int]"
 
 T = TypeVar("T", bound=Type[Foo])
 
@@ -1247,7 +1247,7 @@ class X(TypedDict):
 
 x: X
 for a in ("hourly", "daily"):
-    reveal_type(a)  # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]"
+    reveal_type(a)  # N: Revealed type is "Union[Literal['daily']?, Literal['hourly']?]"
     reveal_type(x[a])  # N: Revealed type is "builtins.int"
     reveal_type(a.upper())  # N: Revealed type is "builtins.str"
     c = a
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index a5c8f53b9726..6febe253d316 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2346,8 +2346,7 @@ def f() -> bool: ...
 
 y = None
 while f():
-    reveal_type(y)  # N: Revealed type is "None" \
-                    # N: Revealed type is "Union[builtins.int, None]"
+    reveal_type(y)  # N: Revealed type is "Union[builtins.int, None]"
     y = 1
 reveal_type(y)  # N: Revealed type is "Union[builtins.int, None]"
 
@@ -2370,7 +2369,42 @@ class A:
 
 [builtins fixtures/primitives.pyi]
 
-[case testAvoidFalseUnreachableInLoop]
+[case testPersistentUnreachableLinesNestedInInpersistentUnreachableLines]
+# flags: --warn-unreachable --python-version 3.11
+
+x = None
+y = None
+while True:
+    if x is not None:
+        if y is not None:
+            reveal_type(y)  # E: Statement is unreachable
+    x = 1
+
+[builtins fixtures/bool.pyi]
+
+[case testAvoidFalseRedundantCastInLoops]
+# flags: --warn-redundant-casts
+
+from typing import Callable, cast, Union
+
+ProcessorReturnValue = Union[str, int]
+Processor = Callable[[str], ProcessorReturnValue]
+
+def main_cast(p: Processor) -> None:
+    ed: ProcessorReturnValue
+    ed = cast(str, ...)
+    while True:
+        ed = p(cast(str, ed))
+
+def main_no_cast(p: Processor) -> None:
+    ed: ProcessorReturnValue
+    ed = cast(str, ...)
+    while True:
+        ed = p(ed)  # E: Argument 1 has incompatible type "Union[str, int]"; expected "str"
+
+[builtins fixtures/bool.pyi]
+
+[case testAvoidFalseUnreachableInLoop1]
 # flags: --warn-unreachable --python-version 3.11
 
 def f() -> int | None: ...
@@ -2383,6 +2417,29 @@ while x is not None or b():
 
 [builtins fixtures/bool.pyi]
 
+[case testAvoidFalseUnreachableInLoop2]
+# flags: --warn-unreachable --python-version 3.11
+
+y = None
+while y is None:
+    if y is None:
+        y = []
+    y.append(1)
+
+[builtins fixtures/list.pyi]
+
+[case testAvoidFalseUnreachableInLoop3]
+# flags: --warn-unreachable --python-version 3.11
+
+xs: list[int | None]
+y = None
+for x in xs:
+    if x is not None:
+        if y is None:
+            y = {}  # E: Need type annotation for "y" (hint: "y: Dict[, ] = ...")
+
+[builtins fixtures/list.pyi]
+
 [case testAvoidFalseRedundantExprInLoop]
 # flags: --enable-error-code redundant-expr --python-version 3.11
 
diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test
index fa831008fbae..1062be6976c0 100644
--- a/test-data/unit/check-redefine2.test
+++ b/test-data/unit/check-redefine2.test
@@ -628,8 +628,7 @@ def f1() -> None:
 def f2() -> None:
     x = None
     while int():
-        reveal_type(x) # N: Revealed type is "None" \
-                       # N: Revealed type is "Union[None, builtins.str]"
+        reveal_type(x) # N: Revealed type is "Union[builtins.str, None]"
         if int():
             x = ""
     reveal_type(x) # N: Revealed type is "Union[None, builtins.str]"
@@ -709,8 +708,7 @@ def b() -> None:
 def c() -> None:
     x = 0
     while int():
-        reveal_type(x) # N: Revealed type is "builtins.int" \
-                       # N: Revealed type is "Union[builtins.int, builtins.str, None]"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, None]"
         if int():
             x = ""
             continue
@@ -810,8 +808,7 @@ def f4() -> None:
                         x = None
                         break
         finally:
-            reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" \
-                # N: Revealed type is "Union[builtins.int, None]"
+            reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]"
         reveal_type(x) # N: Revealed type is "Union[builtins.int, None]"
 [builtins fixtures/exception.pyi]
 
@@ -927,7 +924,7 @@ class X(TypedDict):
 
 x: X
 for a in ("hourly", "daily"):
-    reveal_type(a)  # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]"
+    reveal_type(a)  # N: Revealed type is "Union[Literal['daily']?, Literal['hourly']?]"
     reveal_type(x[a])  # N: Revealed type is "builtins.int"
     reveal_type(a.upper())  # N: Revealed type is "builtins.str"
     c = a
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 41e90c3f8506..0f69d0a56f47 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -989,7 +989,7 @@ from typing_extensions import Unpack
 
 def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[float, ...]], bool]]) -> None:
     for x in xs:
-        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.float]"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.float, builtins.int]"
 [builtins fixtures/tuple.pyi]
 
 [case testFixedUnpackItemInInstanceArguments]

From a573a4047c87f001fd411cf765410604d96fd55a Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Sat, 7 Jun 2025 01:34:56 +0200
Subject: [PATCH 1361/1617] Fix a minor merge conflict caused by #19118
 (#19246)

---
 test-data/unit/check-narrowing.test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 6febe253d316..47ad62248fe0 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2436,7 +2436,7 @@ y = None
 for x in xs:
     if x is not None:
         if y is None:
-            y = {}  # E: Need type annotation for "y" (hint: "y: Dict[, ] = ...")
+            y = {}  # E: Need type annotation for "y" (hint: "y: dict[, ] = ...")
 
 [builtins fixtures/list.pyi]
 

From 325f776733b3f1818b2df7611cedb7dc33f0f065 Mon Sep 17 00:00:00 2001
From: Mikhail Golubev 
Date: Sat, 7 Jun 2025 02:38:37 +0300
Subject: [PATCH 1362/1617] Display FQN for imported base classes in errors
 about incompatible overrides (#19115)

Fixes #19112
---
 mypy/checker.py                     |  2 +-
 test-data/unit/check-classes.test   |  2 +-
 test-data/unit/check-functions.test | 22 ++++++++++++++++++++++
 test-data/unit/check-modules.test   |  4 ++--
 test-data/unit/fine-grained.test    | 24 ++++++++++++------------
 5 files changed, 38 insertions(+), 16 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index d6eac718f008..6929543db24e 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2285,7 +2285,7 @@ def check_method_override_for_base_with_name(
                 original_type,
                 defn.name,
                 name,
-                base.name,
+                base.name if base.module_name == self.tree.fullname else base.fullname,
                 original_class_or_static,
                 override_class_or_static,
                 context,
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index dc421cbd43b9..c75ede7cc6d5 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -397,7 +397,7 @@ class A:
   def __eq__(self, other: A) -> bool: pass  # Fail
 [builtins fixtures/plugin_attrs.pyi]
 [out]
-main:2: error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"
+main:2: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object"
 main:2: note: This violates the Liskov substitution principle
 main:2: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
 main:2: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index f86d4ed76350..4b980f102c52 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3592,6 +3592,28 @@ class Bar(Foo):
     def foo(self, value: Union[int, str]) -> Union[int, str]:
         return super().foo(value)  # E: Call to abstract method "foo" of "Foo" with trivial body via super() is unsafe
 
+[case fullNamesOfImportedBaseClassesDisplayed]
+from a import A
+
+class B(A):
+    def f(self, x: str) -> None:  # E: Argument 1 of "f" is incompatible with supertype "a.A"; supertype defines the argument type as "int" \
+                                  # N: This violates the Liskov substitution principle \
+                                  # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
+        ...
+    def g(self, x: str) -> None:  # E: Signature of "g" incompatible with supertype "a.A" \
+                                  # N:      Superclass: \
+                                  # N:          def g(self) -> None \
+                                  # N:      Subclass: \
+                                  # N:          def g(self, x: str) -> None
+        ...
+
+[file a.py]
+class A:
+    def f(self, x: int) -> None:
+        ...
+    def g(self) -> None:
+        ...
+
 [case testBoundMethodsAssignedInClassBody]
 from typing import Callable
 
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
index dcc64f0924c4..5ae4b4e57176 100644
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -3206,13 +3206,13 @@ class Bar(Foo):
     def frobnicate(self, *args: int) -> None: pass # type: ignore[override] # I know
 [builtins fixtures/dict.pyi]
 [out1]
-tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo"
+tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "a.Foo"
 tmp/b.py:3: note:      Superclass:
 tmp/b.py:3: note:          def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any
 tmp/b.py:3: note:      Subclass:
 tmp/b.py:3: note:          def frobnicate(self) -> None
 [out2]
-tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo"
+tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "a.Foo"
 tmp/b.py:3: note:      Superclass:
 tmp/b.py:3: note:          def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any
 tmp/b.py:3: note:      Subclass:
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index ddb1b7266a57..7e34a2352dd6 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -1051,9 +1051,9 @@ class A:
 [file n.py.3]
 [out]
 ==
-main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A"
 ==
-main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A"
 
 [case testModifyBaseClassMethodCausingInvalidOverride]
 import m
@@ -1067,7 +1067,7 @@ class A:
     def f(self) -> int: pass
 [out]
 ==
-main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A"
 
 [case testAddBaseClassAttributeCausingErrorInSubclass]
 import m
@@ -1974,11 +1974,11 @@ class B:
 class B:
     def foo(self) -> int: return 12
 [out]
-a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B"
 ==
-a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B"
 ==
-a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B"
 ==
 
 [case testPreviousErrorInMethodSemanal1]
@@ -7337,7 +7337,7 @@ class Parent:
     def f(self, arg: Any) -> Any: ...
 [out]
 ==
-main:4: error: Signature of "f" incompatible with supertype "Parent"
+main:4: error: Signature of "f" incompatible with supertype "b.Parent"
 main:4: note:      Superclass:
 main:4: note:          @overload
 main:4: note:          def f(self, arg: int) -> int
@@ -7380,7 +7380,7 @@ class Parent:
     def f(self, arg: Any) -> Any: ...
 [out]
 ==
-main:4: error: Signature of "f" incompatible with supertype "Parent"
+main:4: error: Signature of "f" incompatible with supertype "b.Parent"
 main:4: note:      Superclass:
 main:4: note:          @overload
 main:4: note:          def f(self, arg: int) -> int
@@ -7765,7 +7765,7 @@ def deco(f: F) -> F:
 [out]
 main:7: error: Unsupported operand types for + ("str" and "int")
 ==
-main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B"
+main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "b.B"
 
 [case testLiskovFineVariableClean-only_when_nocache]
 import b
@@ -7870,7 +7870,7 @@ def deco(f: F) -> F:
     pass
 [out]
 ==
-main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B"
+main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "b.B"
 
 [case testAddAbstractMethod]
 from b import D
@@ -8518,7 +8518,7 @@ class D:
 ==
 ==
 a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
-a.py:3: error: Signature of "meth" incompatible with supertype "C"
+a.py:3: error: Signature of "meth" incompatible with supertype "c.C"
 a.py:3: note:      Superclass:
 a.py:3: note:          @overload
 a.py:3: note:          def meth(self, x: int) -> int
@@ -8565,7 +8565,7 @@ class D:
 ==
 ==
 a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
-a.py:3: error: Signature of "meth" incompatible with supertype "C"
+a.py:3: error: Signature of "meth" incompatible with supertype "c.C"
 a.py:3: note:      Superclass:
 a.py:3: note:          @overload
 a.py:3: note:          def meth(x: int) -> int

From 4c825e9cc2c777769c244b83bb2c0342c0658c16 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 7 Jun 2025 23:59:23 +0100
Subject: [PATCH 1363/1617] Fix properties with setters after deleters (#19248)

Fixes https://github.com/python/mypy/issues/19224

Note we must add an additional attribute on `OverloadedFuncDef` since
decorator expressions are not serialized.
---
 mypy/checker.py                   | 10 ++++------
 mypy/checkmember.py               |  4 ++--
 mypy/nodes.py                     | 24 +++++++++++++++++++++++-
 mypy/semanal.py                   |  1 +
 test-data/unit/check-classes.test | 20 ++++++++++++++++++++
 5 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 6929543db24e..49f1bc15f583 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -697,11 +697,9 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
             assert isinstance(defn.items[0], Decorator)
             self.visit_decorator(defn.items[0])
             if defn.items[0].var.is_settable_property:
-                # TODO: here and elsewhere we assume setter immediately follows getter.
-                assert isinstance(defn.items[1], Decorator)
                 # Perform a reduced visit just to infer the actual setter type.
-                self.visit_decorator_inner(defn.items[1], skip_first_item=True)
-                setter_type = defn.items[1].var.type
+                self.visit_decorator_inner(defn.setter, skip_first_item=True)
+                setter_type = defn.setter.var.type
                 # Check if the setter can accept two positional arguments.
                 any_type = AnyType(TypeOfAny.special_form)
                 fallback_setter_type = CallableType(
@@ -712,7 +710,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     fallback=self.named_type("builtins.function"),
                 )
                 if setter_type and not is_subtype(setter_type, fallback_setter_type):
-                    self.fail("Invalid property setter signature", defn.items[1].func)
+                    self.fail("Invalid property setter signature", defn.setter.func)
                 setter_type = self.extract_callable_type(setter_type, defn)
                 if not isinstance(setter_type, CallableType) or len(setter_type.arg_types) != 2:
                     # TODO: keep precise type for callables with tricky but valid signatures.
@@ -2171,7 +2169,7 @@ def check_setter_type_override(self, defn: OverloadedFuncDef, base: TypeInfo) ->
         assert typ is not None and original_type is not None
 
         if not is_subtype(original_type, typ):
-            self.msg.incompatible_setter_override(defn.items[1], typ, original_type, base)
+            self.msg.incompatible_setter_override(defn.setter, typ, original_type, base)
 
     def check_method_override_for_base_with_name(
         self, defn: FuncDef | OverloadedFuncDef | Decorator, name: str, base: TypeInfo
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 50eaf42a9934..beb3c1397c11 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -345,8 +345,8 @@ def analyze_instance_member_access(
             assert isinstance(method, OverloadedFuncDef)
             getter = method.items[0]
             assert isinstance(getter, Decorator)
-            if mx.is_lvalue and (len(items := method.items) > 1):
-                mx.chk.warn_deprecated(items[1], mx.context)
+            if mx.is_lvalue and getter.var.is_settable_property:
+                mx.chk.warn_deprecated(method.setter, mx.context)
             return analyze_var(name, getter.var, typ, mx)
 
         if mx.is_lvalue and not mx.suppress_errors:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 7db32240c33e..2cec4852f31c 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -538,12 +538,20 @@ class OverloadedFuncDef(FuncBase, SymbolNode, Statement):
     Overloaded variants must be consecutive in the source file.
     """
 
-    __slots__ = ("items", "unanalyzed_items", "impl", "deprecated", "_is_trivial_self")
+    __slots__ = (
+        "items",
+        "unanalyzed_items",
+        "impl",
+        "deprecated",
+        "setter_index",
+        "_is_trivial_self",
+    )
 
     items: list[OverloadPart]
     unanalyzed_items: list[OverloadPart]
     impl: OverloadPart | None
     deprecated: str | None
+    setter_index: int | None
 
     def __init__(self, items: list[OverloadPart]) -> None:
         super().__init__()
@@ -551,6 +559,7 @@ def __init__(self, items: list[OverloadPart]) -> None:
         self.unanalyzed_items = items.copy()
         self.impl = None
         self.deprecated = None
+        self.setter_index = None
         self._is_trivial_self: bool | None = None
         if items:
             # TODO: figure out how to reliably set end position (we don't know the impl here).
@@ -586,6 +595,17 @@ def is_trivial_self(self) -> bool:
         self._is_trivial_self = True
         return True
 
+    @property
+    def setter(self) -> Decorator:
+        # Do some consistency checks first.
+        first_item = self.items[0]
+        assert isinstance(first_item, Decorator)
+        assert first_item.var.is_settable_property
+        assert self.setter_index is not None
+        item = self.items[self.setter_index]
+        assert isinstance(item, Decorator)
+        return item
+
     def accept(self, visitor: StatementVisitor[T]) -> T:
         return visitor.visit_overloaded_func_def(self)
 
@@ -598,6 +618,7 @@ def serialize(self) -> JsonDict:
             "impl": None if self.impl is None else self.impl.serialize(),
             "flags": get_flags(self, FUNCBASE_FLAGS),
             "deprecated": self.deprecated,
+            "setter_index": self.setter_index,
         }
 
     @classmethod
@@ -618,6 +639,7 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef:
         res._fullname = data["fullname"]
         set_flags(res, data["flags"])
         res.deprecated = data["deprecated"]
+        res.setter_index = data["setter_index"]
         # NOTE: res.info will be set in the fixup phase.
         return res
 
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 5cd58966f619..d70abe911fea 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1543,6 +1543,7 @@ def analyze_property_with_multi_part_definition(
                             )
                             assert isinstance(setter_func_type, CallableType)
                             bare_setter_type = setter_func_type
+                            defn.setter_index = i + 1
                         if first_node.name == "deleter":
                             item.func.abstract_status = first_item.func.abstract_status
                         for other_node in item.decorators[1:]:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index c75ede7cc6d5..c7136509729e 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -8736,3 +8736,23 @@ class NoopPowerResource:
     def hardware_type(self) -> None:  # E: Invalid property setter signature
         self.hardware_type = None  # Note: intentionally recursive
 [builtins fixtures/property.pyi]
+
+[case testPropertyAllowsDeleterBeforeSetter]
+class C:
+    @property
+    def foo(self) -> str: ...
+    @foo.deleter
+    def foo(self) -> None: ...
+    @foo.setter
+    def foo(self, val: int) -> None: ...
+
+    @property
+    def bar(self) -> int: ...
+    @bar.deleter
+    def bar(self) -> None: ...
+    @bar.setter
+    def bar(self, value: int, val: int) -> None: ...  # E: Invalid property setter signature
+
+C().foo = "no"  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
+C().bar = "fine"
+[builtins fixtures/property.pyi]

From f1b496c0f93fe16c07538e4d825a92373c2d7015 Mon Sep 17 00:00:00 2001
From: Guy Wilson 
Date: Sun, 8 Jun 2025 00:01:05 -0700
Subject: [PATCH 1364/1617] Fix help message url for "None and Optional
 handling" section (#19252)

Fixes #19251
---
 mypy/main.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/main.py b/mypy/main.py
index 16e9e035bf2e..a407a88d3ac1 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -785,7 +785,7 @@ def add_invertible_flag(
         title="None and Optional handling",
         description="Adjust how values of type 'None' are handled. For more context on "
         "how mypy handles values of type 'None', see: "
-        "https://mypy.readthedocs.io/en/stable/kinds_of_types.html#no-strict-optional",
+        "https://mypy.readthedocs.io/en/stable/kinds_of_types.html#optional-types-and-the-none-type",
     )
     add_invertible_flag(
         "--implicit-optional",

From 3456684de31f32a65df70f30acda0ddff75086e3 Mon Sep 17 00:00:00 2001
From: Alexey Makridenko 
Date: Sun, 8 Jun 2025 14:22:00 +0200
Subject: [PATCH 1365/1617] stubgen: add test case for handling `Incomplete`
 return types (#19253)

Closes #16658
---
 test-data/unit/stubgen.test | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
index b4c66c2e5853..161f14e8aea7 100644
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -4744,3 +4744,15 @@ class DCMeta(type): ...
 
 class DC(metaclass=DCMeta):
     x: str
+
+
+[case testIncompleteReturn]
+from _typeshed import Incomplete
+
+def polar(*args, **kwargs) -> Incomplete:
+    ...
+
+[out]
+from _typeshed import Incomplete
+
+def polar(*args, **kwargs) -> Incomplete: ...

From 1778d666d9828229bee793b1f7b8ee474971cc67 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 9 Jun 2025 15:33:53 +0100
Subject: [PATCH 1366/1617] Add script that prints compiled files when self
 compiling (#19260)

Patch various things and run setup.py to get compilation targets without
compiling anything. This can be useful for setting up a custom way of
compiling mypy/mypyc.
---
 misc/self_compile_info.py | 45 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 misc/self_compile_info.py

diff --git a/misc/self_compile_info.py b/misc/self_compile_info.py
new file mode 100644
index 000000000000..f413eb489165
--- /dev/null
+++ b/misc/self_compile_info.py
@@ -0,0 +1,45 @@
+"""Print list of files compiled when compiling self (mypy and mypyc)."""
+
+import argparse
+import sys
+from typing import Any
+
+import setuptools
+
+import mypyc.build
+
+
+class FakeExtension:
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        pass
+
+
+def fake_mypycify(args: list[str], **kwargs: Any) -> list[FakeExtension]:
+    for target in sorted(args):
+        if not target.startswith("-"):
+            print(target)
+    return [FakeExtension()]
+
+
+def fake_setup(*args: Any, **kwargs: Any) -> Any:
+    pass
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(
+        description="Print list of files compiled when compiling self. Run in repository root."
+    )
+    parser.parse_args()
+
+    # Prepare fake state for running setup.py.
+    mypyc.build.mypycify = fake_mypycify  # type: ignore[assignment]
+    setuptools.Extension = FakeExtension  # type: ignore[misc, assignment]
+    setuptools.setup = fake_setup
+    sys.argv = [sys.argv[0], "--use-mypyc"]
+
+    # Run setup.py at the root of the repository.
+    import setup  # noqa: F401
+
+
+if __name__ == "__main__":
+    main()

From ac511d6c4615ca45a9404ed3eb53ea7092b0aca1 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 9 Jun 2025 15:35:53 +0100
Subject: [PATCH 1367/1617] Clean-up and move operator access to checkmember.py
 (#19250)

Fixes https://github.com/python/mypy/issues/5136
Fixes https://github.com/python/mypy/issues/5491

This is a fifth "major" PR toward
https://github.com/python/mypy/issues/7724. Although it would be
impractical to move all the operator special-casing to `checkmember.py`,
this does two things:
* Removes known inconsistencies in operator handling
* Adds a much more complete `has_operator()` helper that can be a
starting point for future performance optimizations
---
 mypy/checkexpr.py                   | 91 +++++++++--------------------
 mypy/checkmember.py                 | 54 +++++++++++++++++
 mypy/types.py                       |  5 ++
 test-data/unit/check-typeddict.test | 13 ++---
 4 files changed, 94 insertions(+), 69 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index e0c7e829309c..e7c5c8cc02c2 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -16,7 +16,7 @@
 from mypy import applytype, erasetype, join, message_registry, nodes, operators, types
 from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals
 from mypy.checker_shared import ExpressionCheckerSharedApi
-from mypy.checkmember import analyze_member_access
+from mypy.checkmember import analyze_member_access, has_operator
 from mypy.checkstrformat import StringFormatterChecker
 from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
 from mypy.errors import ErrorWatcher, report_internal_error
@@ -3834,13 +3834,16 @@ def check_method_call_by_name(
         arg_kinds: list[ArgKind],
         context: Context,
         original_type: Type | None = None,
+        self_type: Type | None = None,
     ) -> tuple[Type, Type]:
         """Type check a call to a named method on an object.
 
         Return tuple (result type, inferred method type). The 'original_type'
-        is used for error messages.
+        is used for error messages. The self_type is to bind self in methods
+        (see analyze_member_access for more details).
         """
         original_type = original_type or base_type
+        self_type = self_type or base_type
         # Unions are special-cased to allow plugins to act on each element of the union.
         base_type = get_proper_type(base_type)
         if isinstance(base_type, UnionType):
@@ -3856,7 +3859,7 @@ def check_method_call_by_name(
             is_super=False,
             is_operator=True,
             original_type=original_type,
-            self_type=base_type,
+            self_type=self_type,
             chk=self.chk,
             in_literal_context=self.is_literal_context(),
         )
@@ -3933,11 +3936,8 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None:
             """Looks up the given operator and returns the corresponding type,
             if it exists."""
 
-            # This check is an important performance optimization,
-            # even though it is mostly a subset of
-            # analyze_member_access.
-            # TODO: Find a way to remove this call without performance implications.
-            if not self.has_member(base_type, op_name):
+            # This check is an important performance optimization.
+            if not has_operator(base_type, op_name, self.named_type):
                 return None
 
             with self.msg.filter_errors() as w:
@@ -4097,14 +4097,8 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
                 errors.append(local_errors.filtered_errors())
                 results.append(result)
             else:
-                # In theory, we should never enter this case, but it seems
-                # we sometimes do, when dealing with Type[...]? E.g. see
-                # check-classes.testTypeTypeComparisonWorks.
-                #
-                # This is probably related to the TODO in lookup_operator(...)
-                # up above.
-                #
-                # TODO: Remove this extra case
+                # Although we should not need this case anymore, we keep it just in case, as
+                # otherwise we will get a crash if we introduce inconsistency in checkmember.py
                 return result
 
         self.msg.add_errors(errors[0])
@@ -4365,13 +4359,19 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type:
         return self.visit_index_with_type(left_type, e)
 
     def visit_index_with_type(
-        self, left_type: Type, e: IndexExpr, original_type: ProperType | None = None
+        self,
+        left_type: Type,
+        e: IndexExpr,
+        original_type: ProperType | None = None,
+        self_type: Type | None = None,
     ) -> Type:
         """Analyze type of an index expression for a given type of base expression.
 
-        The 'original_type' is used for error messages (currently used for union types).
+        The 'original_type' is used for error messages (currently used for union types). The
+        'self_type' is to bind self in methods (see analyze_member_access for more details).
         """
         index = e.index
+        self_type = self_type or left_type
         left_type = get_proper_type(left_type)
 
         # Visit the index, just to make sure we have a type for it available
@@ -4426,16 +4426,22 @@ def visit_index_with_type(
             ):
                 return self.named_type("types.GenericAlias")
 
-        if isinstance(left_type, TypeVarType) and not self.has_member(
-            left_type.upper_bound, "__getitem__"
-        ):
-            return self.visit_index_with_type(left_type.upper_bound, e, original_type)
+        if isinstance(left_type, TypeVarType):
+            return self.visit_index_with_type(
+                left_type.values_or_bound(), e, original_type, left_type
+            )
         elif isinstance(left_type, Instance) and left_type.type.fullname == "typing._SpecialForm":
             # Allow special forms to be indexed and used to create union types
             return self.named_type("typing._SpecialForm")
         else:
             result, method_type = self.check_method_call_by_name(
-                "__getitem__", left_type, [e.index], [ARG_POS], e, original_type=original_type
+                "__getitem__",
+                left_type,
+                [e.index],
+                [ARG_POS],
+                e,
+                original_type=original_type,
+                self_type=self_type,
             )
             e.method_type = method_type
             return result
@@ -5995,45 +6001,6 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool:
             or isinstance(typ, ParamSpecType)
         )
 
-    def has_member(self, typ: Type, member: str) -> bool:
-        """Does type have member with the given name?"""
-        # TODO: refactor this to use checkmember.analyze_member_access, otherwise
-        # these two should be carefully kept in sync.
-        # This is much faster than analyze_member_access, though, and so using
-        # it first as a filter is important for performance.
-        typ = get_proper_type(typ)
-
-        if isinstance(typ, TypeVarType):
-            typ = get_proper_type(typ.upper_bound)
-        if isinstance(typ, TupleType):
-            typ = tuple_fallback(typ)
-        if isinstance(typ, LiteralType):
-            typ = typ.fallback
-        if isinstance(typ, Instance):
-            return typ.type.has_readable_member(member)
-        if isinstance(typ, FunctionLike) and typ.is_type_obj():
-            return typ.fallback.type.has_readable_member(member)
-        elif isinstance(typ, AnyType):
-            return True
-        elif isinstance(typ, UnionType):
-            result = all(self.has_member(x, member) for x in typ.relevant_items())
-            return result
-        elif isinstance(typ, TypeType):
-            # Type[Union[X, ...]] is always normalized to Union[Type[X], ...],
-            # so we don't need to care about unions here.
-            item = typ.item
-            if isinstance(item, TypeVarType):
-                item = get_proper_type(item.upper_bound)
-            if isinstance(item, TupleType):
-                item = tuple_fallback(item)
-            if isinstance(item, Instance) and item.type.metaclass_type is not None:
-                return self.has_member(item.type.metaclass_type, member)
-            if isinstance(item, AnyType):
-                return True
-            return False
-        else:
-            return False
-
     def not_ready_callback(self, name: str, context: Context) -> None:
         """Called when we can't infer the type of a variable because it's not ready yet.
 
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index beb3c1397c11..edbce190f94c 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1501,3 +1501,57 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F:
         is_bound=True,
     )
     return cast(F, res)
+
+
+def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool:
+    """Does type have operator with the given name?
+
+    Note: this follows the rules for operator access, in particular:
+    * __getattr__ is not considered
+    * for class objects we only look in metaclass
+    * instance level attributes (i.e. extra_attrs) are not considered
+    """
+    # This is much faster than analyze_member_access, and so using
+    # it first as a filter is important for performance. This is mostly relevant
+    # in situations where we can't expect that method is likely present,
+    # e.g. for __OP__ vs __rOP__.
+    typ = get_proper_type(typ)
+
+    if isinstance(typ, TypeVarLikeType):
+        typ = typ.values_or_bound()
+    if isinstance(typ, AnyType):
+        return True
+    if isinstance(typ, UnionType):
+        return all(has_operator(x, op_method, named_type) for x in typ.relevant_items())
+    if isinstance(typ, FunctionLike) and typ.is_type_obj():
+        return typ.fallback.type.has_readable_member(op_method)
+    if isinstance(typ, TypeType):
+        # Type[Union[X, ...]] is always normalized to Union[Type[X], ...],
+        # so we don't need to care about unions here, but we need to care about
+        # Type[T], where upper bound of T is a union.
+        item = typ.item
+        if isinstance(item, TypeVarType):
+            item = item.values_or_bound()
+        if isinstance(item, UnionType):
+            return all(meta_has_operator(x, op_method, named_type) for x in item.relevant_items())
+        return meta_has_operator(item, op_method, named_type)
+    return instance_fallback(typ, named_type).type.has_readable_member(op_method)
+
+
+def instance_fallback(typ: ProperType, named_type: Callable[[str], Instance]) -> Instance:
+    if isinstance(typ, Instance):
+        return typ
+    if isinstance(typ, TupleType):
+        return tuple_fallback(typ)
+    if isinstance(typ, (LiteralType, TypedDictType)):
+        return typ.fallback
+    return named_type("builtins.object")
+
+
+def meta_has_operator(item: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool:
+    item = get_proper_type(item)
+    if isinstance(item, AnyType):
+        return True
+    item = instance_fallback(item, named_type)
+    meta = item.type.metaclass_type or named_type("builtins.type")
+    return meta.type.has_readable_member(op_method)
diff --git a/mypy/types.py b/mypy/types.py
index 47a59291df52..8ecd2ccf52d9 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -615,6 +615,11 @@ def has_default(self) -> bool:
         t = get_proper_type(self.default)
         return not (isinstance(t, AnyType) and t.type_of_any == TypeOfAny.from_omitted_generics)
 
+    def values_or_bound(self) -> ProperType:
+        if isinstance(self, TypeVarType) and self.values:
+            return UnionType(self.values)
+        return get_proper_type(self.upper_bound)
+
 
 class TypeVarType(TypeVarLikeType):
     """Type that refers to a type variable."""
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index 6bcc6e20328b..e9eacaf0c7fa 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -3358,16 +3358,13 @@ foo: Foo = {'key': 1}
 foo | 1
 
 class SubDict(dict): ...
-foo | SubDict()
+reveal_type(foo | SubDict())
 [out]
 main:7: error: No overload variant of "__or__" of "TypedDict" matches argument type "int"
 main:7: note: Possible overload variants:
 main:7: note:     def __or__(self, TypedDict({'key'?: int}), /) -> Foo
 main:7: note:     def __or__(self, dict[str, Any], /) -> dict[str, object]
-main:10: error: No overload variant of "__ror__" of "dict" matches argument type "Foo"
-main:10: note: Possible overload variants:
-main:10: note:     def __ror__(self, dict[Any, Any], /) -> dict[Any, Any]
-main:10: note:     def [T, T2] __ror__(self, dict[T, T2], /) -> dict[Union[Any, T], Union[Any, T2]]
+main:10: note: Revealed type is "builtins.dict[builtins.str, builtins.object]"
 [builtins fixtures/dict-full.pyi]
 [typing fixtures/typing-typeddict-iror.pyi]
 
@@ -3389,8 +3386,10 @@ d2: Dict[int, str]
 
 reveal_type(d1 | foo)  # N: Revealed type is "builtins.dict[builtins.str, builtins.object]"
 d2 | foo  # E: Unsupported operand types for | ("dict[int, str]" and "Foo")
-1 | foo  # E: Unsupported left operand type for | ("int")
-
+1 | foo  # E: No overload variant of "__ror__" of "TypedDict" matches argument type "int" \
+         # N: Possible overload variants: \
+         # N:     def __ror__(self, TypedDict({'key'?: int}), /) -> Foo \
+         # N:     def __ror__(self, dict[str, Any], /) -> dict[str, object]
 
 class Bar(TypedDict):
     key: int

From 929377ac57fb1b4466d5dcc871648b56e41e583b Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 9 Jun 2025 15:36:25 +0100
Subject: [PATCH 1368/1617] Refactor/unify access to static attributes (#19254)

Fixes https://github.com/python/mypy/issues/3832
Fixes https://github.com/python/mypy/issues/5723
Fixes https://github.com/python/mypy/issues/17174
Improves https://github.com/python/mypy/issues/7217

This is a sixth "major" PR toward
https://github.com/python/mypy/issues/7724. Previously access to
"static" attributes (like type aliases, class objects) was duplicated in
four places:
* In `analyze_ref_expr()`
* In `determine_type_of_member()` (for modules as subtypes of protocols)
* In instance attribute access logic
* In class attribute logic

Most of these were somewhat incomplete and/or inconsistent, this PR
unifies all four (there is still tiny duplication because I decided to
limit the number of deferrals, i.e. preserve the existing logic in this
respect). Some notable things that are not pure refactoring:
* Previously we disabled access to type variables as class attributes.
This was inconsistent with plain references and instance attributes that
just return `Instance("typing.TypeVar")`.
* Instance access plugins were only applied on `TypeInfo`s and
`TypeAlias`es, now they are applied always.
* Previously arguments kinds were sometimes not correct for TypedDict
class objects with non-required keys.
* I tweaked `TypeOfAny` in couple places to be more logical.
---
 mypy/checker.py                          |  24 -----
 mypy/checker_shared.py                   |  24 +++--
 mypy/checkexpr.py                        | 117 +++++++++++++----------
 mypy/checkmember.py                      |  56 +++--------
 mypy/message_registry.py                 |   1 -
 mypyc/test-data/fixtures/typing-full.pyi |   4 +-
 mypyc/test-data/run-functions.test       |   1 +
 test-data/unit/check-classes.test        |   5 +-
 test-data/unit/check-dataclasses.test    |  19 ++++
 test-data/unit/check-modules.test        |   5 +-
 test-data/unit/check-newsemanal.test     |   1 +
 test-data/unit/check-redefine.test       |   2 +
 test-data/unit/check-typeddict.test      |  14 +++
 test-data/unit/check-typevar-values.test |   5 +-
 test-data/unit/fixtures/exception.pyi    |   1 +
 15 files changed, 141 insertions(+), 138 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 49f1bc15f583..27b71b957efc 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -117,7 +117,6 @@
     TypeAlias,
     TypeAliasStmt,
     TypeInfo,
-    TypeVarExpr,
     UnaryExpr,
     Var,
     WhileStmt,
@@ -2858,29 +2857,6 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
                 if name in base2.names and base2 not in base.mro:
                     self.check_compatibility(name, base, base2, typ)
 
-    def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None:
-        # TODO: this duplicates both checkmember.py and analyze_ref_expr(), delete.
-        if sym.type is not None:
-            return sym.type
-        if isinstance(sym.node, SYMBOL_FUNCBASE_TYPES):
-            return self.function_type(sym.node)
-        if isinstance(sym.node, TypeInfo):
-            if sym.node.typeddict_type:
-                # We special-case TypedDict, because they don't define any constructor.
-                return self.expr_checker.typeddict_callable(sym.node)
-            else:
-                return type_object_type(sym.node, self.named_type)
-        if isinstance(sym.node, TypeVarExpr):
-            # Use of TypeVars is rejected in an expression/runtime context, so
-            # we don't need to check supertype compatibility for them.
-            return AnyType(TypeOfAny.special_form)
-        if isinstance(sym.node, TypeAlias):
-            with self.msg.filter_errors():
-                # Suppress any errors, they will be given when analyzing the corresponding node.
-                # Here we may have incorrect options and location context.
-                return self.expr_checker.alias_type_in_runtime_context(sym.node, ctx=sym.node)
-        return None
-
     def check_compatibility(
         self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: TypeInfo
     ) -> None:
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index 6c62af50466c..2ab4548edfaf 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -21,7 +21,7 @@
     MypyFile,
     Node,
     RefExpr,
-    TypeAlias,
+    SymbolNode,
     TypeInfo,
     Var,
 )
@@ -64,10 +64,6 @@ def accept(
     def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
         raise NotImplementedError
 
-    @abstractmethod
-    def module_type(self, node: MypyFile) -> Instance:
-        raise NotImplementedError
-
     @abstractmethod
     def check_call(
         self,
@@ -112,12 +108,6 @@ def check_method_call_by_name(
     ) -> tuple[Type, Type]:
         raise NotImplementedError
 
-    @abstractmethod
-    def alias_type_in_runtime_context(
-        self, alias: TypeAlias, *, ctx: Context, alias_definition: bool = False
-    ) -> Type:
-        raise NotImplementedError
-
     @abstractmethod
     def visit_typeddict_index_expr(
         self, td_type: TypedDictType, index: Expression, setitem: bool = False
@@ -125,11 +115,19 @@ def visit_typeddict_index_expr(
         raise NotImplementedError
 
     @abstractmethod
-    def typeddict_callable(self, info: TypeInfo) -> CallableType:
+    def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type:
         raise NotImplementedError
 
     @abstractmethod
-    def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type:
+    def analyze_static_reference(
+        self,
+        node: SymbolNode,
+        ctx: Context,
+        is_lvalue: bool,
+        *,
+        include_modules: bool = True,
+        suppress_errors: bool = False,
+    ) -> Type:
         raise NotImplementedError
 
 
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index e7c5c8cc02c2..b8b08547349d 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -7,7 +7,7 @@
 import time
 from collections import defaultdict
 from collections.abc import Iterable, Iterator, Sequence
-from contextlib import contextmanager
+from contextlib import contextmanager, nullcontext
 from typing import Callable, ClassVar, Final, Optional, cast, overload
 from typing_extensions import TypeAlias as _TypeAlias, assert_never
 
@@ -94,6 +94,7 @@
     TypedDictExpr,
     TypeInfo,
     TypeVarExpr,
+    TypeVarLikeExpr,
     TypeVarTupleExpr,
     UnaryExpr,
     Var,
@@ -173,6 +174,7 @@
     TypeOfAny,
     TypeType,
     TypeVarId,
+    TypeVarLikeType,
     TypeVarTupleType,
     TypeVarType,
     UnboundType,
@@ -377,9 +379,8 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
             result = self.analyze_var_ref(node, e)
             if isinstance(result, PartialType):
                 result = self.chk.handle_partial_var_type(result, lvalue, node, e)
-        elif isinstance(node, FuncDef):
-            # Reference to a global function.
-            result = function_type(node, self.named_type("builtins.function"))
+        elif isinstance(node, Decorator):
+            result = self.analyze_var_ref(node.var, e)
         elif isinstance(node, OverloadedFuncDef):
             if node.type is None:
                 if self.chk.in_checked_function() and node.items:
@@ -387,16 +388,15 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
                 result = AnyType(TypeOfAny.from_error)
             else:
                 result = node.type
-        elif isinstance(node, TypeInfo):
-            # Reference to a type object.
-            if node.typeddict_type:
-                # We special-case TypedDict, because they don't define any constructor.
-                result = self.typeddict_callable(node)
-            elif node.fullname == "types.NoneType":
-                # We special case NoneType, because its stub definition is not related to None.
-                result = TypeType(NoneType())
-            else:
-                result = type_object_type(node, self.named_type)
+        elif isinstance(node, (FuncDef, TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)):
+            result = self.analyze_static_reference(node, e, e.is_alias_rvalue or lvalue)
+        else:
+            if isinstance(node, PlaceholderNode):
+                assert False, f"PlaceholderNode {node.fullname!r} leaked to checker"
+            # Unknown reference; use any type implicitly to avoid
+            # generating extra type errors.
+            result = AnyType(TypeOfAny.from_error)
+        if isinstance(node, TypeInfo):
             if isinstance(result, CallableType) and isinstance(  # type: ignore[misc]
                 result.ret_type, Instance
             ):
@@ -408,30 +408,56 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
                 # This is the type in a type[] expression, so substitute type
                 # variables with Any.
                 result = erasetype.erase_typevars(result)
-        elif isinstance(node, MypyFile):
-            # Reference to a module object.
-            result = self.module_type(node)
-        elif isinstance(node, Decorator):
-            result = self.analyze_var_ref(node.var, e)
+        assert result is not None
+        return result
+
+    def analyze_static_reference(
+        self,
+        node: SymbolNode,
+        ctx: Context,
+        is_lvalue: bool,
+        *,
+        include_modules: bool = True,
+        suppress_errors: bool = False,
+    ) -> Type:
+        """
+        This is the version of analyze_ref_expr() that doesn't do any deferrals.
+
+        This function can be used by member access to "static" attributes. For example,
+        when accessing module attributes in protocol checks, or accessing attributes of
+        special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object.
+        # TODO: merge with analyze_ref_expr() when we are confident about performance.
+        """
+        if isinstance(node, (Var, Decorator, OverloadedFuncDef)):
+            return node.type or AnyType(TypeOfAny.special_form)
+        elif isinstance(node, FuncDef):
+            return function_type(node, self.named_type("builtins.function"))
+        elif isinstance(node, TypeInfo):
+            # Reference to a type object.
+            if node.typeddict_type:
+                # We special-case TypedDict, because they don't define any constructor.
+                return self.typeddict_callable(node)
+            elif node.fullname == "types.NoneType":
+                # We special case NoneType, because its stub definition is not related to None.
+                return TypeType(NoneType())
+            else:
+                return type_object_type(node, self.named_type)
         elif isinstance(node, TypeAlias):
             # Something that refers to a type alias appears in runtime context.
             # Note that we suppress bogus errors for alias redefinitions,
             # they are already reported in semanal.py.
-            result = self.alias_type_in_runtime_context(
-                node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue
-            )
+            with self.msg.filter_errors() if suppress_errors else nullcontext():
+                return self.alias_type_in_runtime_context(
+                    node, ctx=ctx, alias_definition=is_lvalue
+                )
         elif isinstance(node, TypeVarExpr):
             return self.named_type("typing.TypeVar")
         elif isinstance(node, (ParamSpecExpr, TypeVarTupleExpr)):
-            result = self.object_type()
-        else:
-            if isinstance(node, PlaceholderNode):
-                assert False, f"PlaceholderNode {node.fullname!r} leaked to checker"
-            # Unknown reference; use any type implicitly to avoid
-            # generating extra type errors.
-            result = AnyType(TypeOfAny.from_error)
-        assert result is not None
-        return result
+            return self.object_type()
+        elif isinstance(node, MypyFile):
+            # Reference to a module object.
+            return self.module_type(node) if include_modules else AnyType(TypeOfAny.special_form)
+        return AnyType(TypeOfAny.from_error)
 
     def analyze_var_ref(self, var: Var, context: Context) -> Type:
         if var.type:
@@ -459,20 +485,21 @@ def module_type(self, node: MypyFile) -> Instance:
             # Fall back to a dummy 'object' type instead to
             # avoid a crash.
             result = self.named_type("builtins.object")
-        module_attrs = {}
+        module_attrs: dict[str, Type] = {}
         immutable = set()
         for name, n in node.names.items():
             if not n.module_public:
                 continue
             if isinstance(n.node, Var) and n.node.is_final:
                 immutable.add(name)
-            typ = self.chk.determine_type_of_member(n)
-            if typ:
-                module_attrs[name] = typ
+            if n.node is None:
+                module_attrs[name] = AnyType(TypeOfAny.from_error)
             else:
                 # TODO: what to do about nested module references?
                 # They are non-trivial because there may be import cycles.
-                module_attrs[name] = AnyType(TypeOfAny.special_form)
+                module_attrs[name] = self.analyze_static_reference(
+                    n.node, n.node, False, include_modules=False, suppress_errors=True
+                )
         result.extra_attrs = ExtraAttrs(module_attrs, immutable, node.fullname)
         return result
 
@@ -961,19 +988,11 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType:
         assert info.special_alias is not None
         target = info.special_alias.target
         assert isinstance(target, ProperType) and isinstance(target, TypedDictType)
-        expected_types = list(target.items.values())
-        kinds = [ArgKind.ARG_NAMED] * len(expected_types)
-        names = list(target.items.keys())
-        return CallableType(
-            expected_types,
-            kinds,
-            names,
-            target,
-            self.named_type("builtins.type"),
-            variables=info.defn.type_vars,
-        )
+        return self.typeddict_callable_from_context(target, info.defn.type_vars)
 
-    def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType:
+    def typeddict_callable_from_context(
+        self, callee: TypedDictType, variables: Sequence[TypeVarLikeType] | None = None
+    ) -> CallableType:
         return CallableType(
             list(callee.items.values()),
             [
@@ -983,6 +1002,8 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType
             list(callee.items.keys()),
             callee,
             self.named_type("builtins.type"),
+            variables=variables,
+            is_bound=True,
         )
 
     def check_typeddict_call_with_kwargs(
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index edbce190f94c..502251b3960c 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -34,7 +34,7 @@
     TempNode,
     TypeAlias,
     TypeInfo,
-    TypeVarExpr,
+    TypeVarLikeExpr,
     Var,
     is_final_node,
 )
@@ -49,7 +49,6 @@
     make_simplified_union,
     supported_self_type,
     tuple_fallback,
-    type_object_type,
 )
 from mypy.types import (
     AnyType,
@@ -537,24 +536,20 @@ def analyze_member_var_access(
         is_trivial_self = vv.func.is_trivial_self and not vv.decorators
         if mx.is_super and not mx.suppress_errors:
             validate_super_call(vv.func, mx)
+    if isinstance(v, FuncDef):
+        assert False, "Did not expect a function"
+    if isinstance(v, MypyFile):
+        mx.chk.module_refs.add(v.fullname)
 
-    if isinstance(vv, TypeInfo):
+    if isinstance(vv, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)):
         # If the associated variable is a TypeInfo synthesize a Var node for
         # the purposes of type checking.  This enables us to type check things
-        # like accessing class attributes on an inner class.
-        v = Var(name, type=type_object_type(vv, mx.named_type))
-        v.info = info
-
-    if isinstance(vv, TypeAlias):
-        # Similar to the above TypeInfo case, we allow using
-        # qualified type aliases in runtime context if it refers to an
-        # instance type. For example:
+        # like accessing class attributes on an inner class. Similar we allow
+        # using qualified type aliases in runtime context. For example:
         #     class C:
         #         A = List[int]
         #     x = C.A() <- this is OK
-        typ = mx.chk.expr_checker.alias_type_in_runtime_context(
-            vv, ctx=mx.context, alias_definition=mx.is_lvalue
-        )
+        typ = mx.chk.expr_checker.analyze_static_reference(vv, mx.context, mx.is_lvalue)
         v = Var(name, type=typ)
         v.info = info
 
@@ -567,13 +562,6 @@ def analyze_member_var_access(
             check_final_member(name, info, mx.msg, mx.context)
 
         return analyze_var(name, v, itype, mx, implicit=implicit, is_trivial_self=is_trivial_self)
-    elif isinstance(v, FuncDef):
-        assert False, "Did not expect a function"
-    elif isinstance(v, MypyFile):
-        mx.chk.module_refs.add(v.fullname)
-        return mx.chk.expr_checker.module_type(v)
-    elif isinstance(v, TypeVarExpr):
-        return mx.chk.named_type("typing.TypeVar")
     elif (
         not v
         and name not in ["__getattr__", "__setattr__", "__getattribute__"]
@@ -1259,29 +1247,9 @@ def analyze_class_attribute_access(
         mx.not_ready_callback(name, mx.context)
         return AnyType(TypeOfAny.special_form)
 
-    if isinstance(node.node, TypeVarExpr):
-        mx.fail(message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name, name))
-        return AnyType(TypeOfAny.from_error)
-
-    # TODO: some logic below duplicates analyze_ref_expr in checkexpr.py
-    if isinstance(node.node, TypeInfo):
-        if node.node.typeddict_type:
-            # We special-case TypedDict, because they don't define any constructor.
-            return mx.chk.expr_checker.typeddict_callable(node.node)
-        elif node.node.fullname == "types.NoneType":
-            # We special case NoneType, because its stub definition is not related to None.
-            return TypeType(NoneType())
-        else:
-            return type_object_type(node.node, mx.named_type)
-
-    if isinstance(node.node, MypyFile):
-        # Reference to a module object.
-        return mx.named_type("types.ModuleType")
-
-    if isinstance(node.node, TypeAlias):
-        return mx.chk.expr_checker.alias_type_in_runtime_context(
-            node.node, ctx=mx.context, alias_definition=mx.is_lvalue
-        )
+    if isinstance(node.node, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)):
+        # TODO: should we apply class plugin here (similar to instance access)?
+        return mx.chk.expr_checker.analyze_static_reference(node.node, mx.context, mx.is_lvalue)
 
     if is_decorated:
         assert isinstance(node.node, Decorator)
diff --git a/mypy/message_registry.py b/mypy/message_registry.py
index 0c7464246990..609f968a8c65 100644
--- a/mypy/message_registry.py
+++ b/mypy/message_registry.py
@@ -188,7 +188,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
 
 # TypeVar
 INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}'
-CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression'
 INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"'
 INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}'
 INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"'
diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi
index 6b6aba6802b1..d37129bc2e0b 100644
--- a/mypyc/test-data/fixtures/typing-full.pyi
+++ b/mypyc/test-data/fixtures/typing-full.pyi
@@ -12,12 +12,14 @@ class GenericMeta(type): pass
 
 class _SpecialForm:
     def __getitem__(self, index): ...
+class TypeVar:
+    def __init__(self, name, *args, bound=None): ...
+    def __or__(self, other): ...
 
 cast = 0
 overload = 0
 Any = object()
 Optional = 0
-TypeVar = 0
 Generic = 0
 Protocol = 0
 Tuple = 0
diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test
index 91a6103e31ae..46f343fa3798 100644
--- a/mypyc/test-data/run-functions.test
+++ b/mypyc/test-data/run-functions.test
@@ -1286,6 +1286,7 @@ def bar() -> None:
     print(inner.__dict__)  # type: ignore
 
 bar()
+[typing fixtures/typing-full.pyi]
 [out]
 {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': }
 
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index c7136509729e..23c0d4ccf316 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -7902,8 +7902,7 @@ class Foo:
     from mod import meth2  # E: Unsupported class scoped import
     from mod import T
 
-reveal_type(Foo.T)  # E: Type variable "Foo.T" cannot be used as an expression \
-                    # N: Revealed type is "Any"
+reveal_type(Foo.T)  # N: Revealed type is "typing.TypeVar"
 
 [file mod.pyi]
 from typing import Any, TypeVar, overload
@@ -7915,6 +7914,8 @@ def meth1(self: Any, y: str) -> str: ...
 
 T = TypeVar("T")
 def meth2(self: Any, y: T) -> T: ...
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testNewAndInitNoReturn]
 from typing import NoReturn
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index ded390067de0..30d8497c9cd2 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -2647,3 +2647,22 @@ User("", 0)  # E: Too many positional arguments for "User"
 User("", id=0)
 User("", name="")  # E: "User" gets multiple values for keyword argument "name"
 [builtins fixtures/tuple.pyi]
+
+[case testDataclassDefaultFactoryTypedDict]
+from dataclasses import dataclass, field
+from mypy_extensions import TypedDict
+
+class Person(TypedDict, total=False):
+    name: str
+
+@dataclass
+class Job:
+    person: Person = field(default_factory=Person)
+
+class PersonBad(TypedDict):
+    name: str
+
+@dataclass
+class JobBad:
+    person: PersonBad = field(default_factory=PersonBad)  # E: Argument "default_factory" to "field" has incompatible type "type[PersonBad]"; expected "Callable[[], PersonBad]"
+[builtins fixtures/dict.pyi]
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
index 5ae4b4e57176..858024e7daf2 100644
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -1582,8 +1582,8 @@ def f() -> types.ModuleType:
     return types
 reveal_type(f())  # N: Revealed type is "types.ModuleType"
 reveal_type(types)  # N: Revealed type is "types.ModuleType"
-
 [builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testClassImportAccessedInMethod]
 class C:
@@ -1997,6 +1997,7 @@ from typing import TypeVar
 T = TypeVar('T')
 def whatever(x: T) -> T: pass
 [builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testModuleAliasToQualifiedImport2]
 import mod
@@ -2012,8 +2013,8 @@ from typing import TypeVar
 T = TypeVar('T')
 def whatever(x: T) -> T: pass
 [file othermod.py]
-
 [builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testModuleLevelGetattr]
 import has_getattr
diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test
index 1d489d54409f..61bf08018722 100644
--- a/test-data/unit/check-newsemanal.test
+++ b/test-data/unit/check-newsemanal.test
@@ -2805,6 +2805,7 @@ def get() -> int: ...
 import typing
 t = typing.typevar('t') # E: Module has no attribute "typevar"
 [builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testNewAnalyzerImportFromTopLevelFunction]
 import a.b  # This works at runtime
diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test
index 7ddfdd0f8a4f..4bcbaf50298d 100644
--- a/test-data/unit/check-redefine.test
+++ b/test-data/unit/check-redefine.test
@@ -351,6 +351,7 @@ def f() -> None:
     n = 1
     import typing as n  # E: Incompatible import of "n" (imported name has type Module, local name has type "int")
 [builtins fixtures/module.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testRedefineLocalWithTypeAnnotation]
 # flags: --allow-redefinition
@@ -547,6 +548,7 @@ try:
 except Exception as typing:
     pass
 [builtins fixtures/exception.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testRedefiningUnderscoreFunctionIsntAnError]
 def _(arg):
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index e9eacaf0c7fa..a068a63274ca 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -4257,3 +4257,17 @@ e1: E = {"x": 0, "y": "a"}
 e2: E = {"x": "no", "y": "a"}
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAliasAsInstanceAttribute]
+from typing import TypedDict
+
+class Dicts:
+    class TF(TypedDict, total=False):
+        user_id: int
+    TotalFalse = TF
+
+dicts = Dicts()
+reveal_type(dicts.TF)  # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})"
+reveal_type(dicts.TotalFalse)  # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})"
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]
diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test
index ab2956374c12..1be75c0f4706 100644
--- a/test-data/unit/check-typevar-values.test
+++ b/test-data/unit/check-typevar-values.test
@@ -592,11 +592,10 @@ class C:
     def f(self, x: T) -> T:
         L = List[S]
         y: L[C.T] = [x]
-        C.T  # E: Type variable "C.T" cannot be used as an expression
-        A = C.T  # E: Type variable "C.T" cannot be used as an expression
+        reveal_type(C.T)  # N: Revealed type is "typing.TypeVar"
         return y[0]
-
 [builtins fixtures/list.pyi]
+[typing fixtures/typing-full.pyi]
 
 [case testTypeVarWithAnyTypeBound]
 # flags: --follow-imports=skip
diff --git a/test-data/unit/fixtures/exception.pyi b/test-data/unit/fixtures/exception.pyi
index 08496e4e5934..963192cc86ab 100644
--- a/test-data/unit/fixtures/exception.pyi
+++ b/test-data/unit/fixtures/exception.pyi
@@ -12,6 +12,7 @@ class list: pass
 class dict: pass
 class function: pass
 class int: pass
+class float: pass
 class str: pass
 class bool: pass
 class ellipsis: pass

From fe91422e56e38c0ec67fccdd5f589dbeb03f3b52 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Tue, 10 Jun 2025 00:48:39 -0400
Subject: [PATCH 1369/1617] Disallow `ClassVar` in type aliases (#19263)

---
 mypy/typeanal.py                       | 4 ++++
 test-data/unit/check-type-aliases.test | 7 +++++++
 2 files changed, 11 insertions(+)

diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index a8d5f1b304fe..f4b12c1c978d 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -675,6 +675,10 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
                     t,
                     code=codes.VALID_TYPE,
                 )
+            if self.defining_alias:
+                self.fail(
+                    "ClassVar[...] can't be used inside a type alias", t, code=codes.VALID_TYPE
+                )
             if len(t.args) == 0:
                 return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column)
             if len(t.args) != 1:
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
index 718d730132ae..5bbb503a578a 100644
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -1311,3 +1311,10 @@ class Bar(Generic[T]):
 x: Bar[int]
 reveal_type(x.var.bar)  # N: Revealed type is "__main__.Bar[builtins.int]"
 [builtins fixtures/tuple.pyi]
+
+[case testExplicitTypeAliasClassVarProhibited]
+from typing import ClassVar
+from typing_extensions import TypeAlias
+
+Foo: TypeAlias = ClassVar[int]  # E: ClassVar[...] can't be used inside a type alias
+[builtins fixtures/tuple.pyi]

From 183fc96ce4faab203668963110f4bb7277e8e3d2 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 10 Jun 2025 16:03:09 +0100
Subject: [PATCH 1370/1617] [mypyc] Make generated generator helper method
 internal (#19268)

Add a flag to FuncIR to allow functions to be marked as internal. Don't
generate wrapper functions/methods that allow calls from Python for
internal methods, since these are internal implementation details.

This has these benefits:
 * Internal functions are private and don't pollute public namespaces.
* Signatures of generated functions can use arbitrary C types (e.g.
arbitrary pointer types), even those that can't be passed to/from
Python.
 * We generate less C code (fewer wrapper functions).
---
 mypyc/codegen/emitclass.py   |  2 +-
 mypyc/codegen/emitmodule.py  |  4 ++--
 mypyc/codegen/emitwrapper.py |  1 +
 mypyc/ir/func_ir.py          | 10 ++++++++++
 mypyc/irbuild/generator.py   |  6 +++++-
 5 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index 9cb9074b9fc4..da3d14f9dafe 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -831,7 +831,7 @@ def generate_finalize_for_class(
 def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None:
     emitter.emit_line(f"static PyMethodDef {name}[] = {{")
     for fn in cl.methods.values():
-        if fn.decl.is_prop_setter or fn.decl.is_prop_getter:
+        if fn.decl.is_prop_setter or fn.decl.is_prop_getter or fn.internal:
             continue
         emitter.emit_line(f'{{"{fn.name}",')
         emitter.emit_line(f" (PyCFunction){PREFIX}{fn.cname(emitter.names)},")
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 36cc57fa2af6..1ee2ee2aadd8 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -455,7 +455,7 @@ def generate_function_declaration(fn: FuncIR, emitter: Emitter) -> None:
     emitter.context.declarations[emitter.native_function_name(fn.decl)] = HeaderDeclaration(
         f"{native_function_header(fn.decl, emitter)};", needs_export=True
     )
-    if fn.name != TOP_LEVEL_NAME:
+    if fn.name != TOP_LEVEL_NAME and not fn.internal:
         if is_fastcall_supported(fn, emitter.capi_version):
             emitter.context.declarations[PREFIX + fn.cname(emitter.names)] = HeaderDeclaration(
                 f"{wrapper_function_header(fn, emitter.names)};"
@@ -571,7 +571,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
             for fn in module.functions:
                 emitter.emit_line()
                 generate_native_function(fn, emitter, self.source_paths[module_name], module_name)
-                if fn.name != TOP_LEVEL_NAME:
+                if fn.name != TOP_LEVEL_NAME and not fn.internal:
                     emitter.emit_line()
                     if is_fastcall_supported(fn, emitter.capi_version):
                         generate_wrapper_function(
diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py
index 1918c946772c..cd1684255855 100644
--- a/mypyc/codegen/emitwrapper.py
+++ b/mypyc/codegen/emitwrapper.py
@@ -61,6 +61,7 @@ def wrapper_function_header(fn: FuncIR, names: NameGenerator) -> str:
 
     See comment above for a summary of the arguments.
     """
+    assert not fn.internal
     return (
         "PyObject *{prefix}{name}("
         "PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames)"
diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py
index bf21816fb07a..beef8def7f43 100644
--- a/mypyc/ir/func_ir.py
+++ b/mypyc/ir/func_ir.py
@@ -140,6 +140,7 @@ def __init__(
         is_prop_setter: bool = False,
         is_prop_getter: bool = False,
         implicit: bool = False,
+        internal: bool = False,
     ) -> None:
         self.name = name
         self.class_name = class_name
@@ -160,6 +161,9 @@ def __init__(
         # Currently only supported for property getters/setters
         self.implicit = implicit
 
+        # If True, only direct C level calls are supported (no wrapper function)
+        self.internal = internal
+
         # This is optional because this will be set to the line number when the corresponding
         # FuncIR is created
         self._line: int | None = None
@@ -204,6 +208,7 @@ def serialize(self) -> JsonDict:
             "is_prop_setter": self.is_prop_setter,
             "is_prop_getter": self.is_prop_getter,
             "implicit": self.implicit,
+            "internal": self.internal,
         }
 
     # TODO: move this to FuncIR?
@@ -226,6 +231,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl:
             data["is_prop_setter"],
             data["is_prop_getter"],
             data["implicit"],
+            data["internal"],
         )
 
 
@@ -287,6 +293,10 @@ def fullname(self) -> str:
     def id(self) -> str:
         return self.decl.id
 
+    @property
+    def internal(self) -> bool:
+        return self.decl.internal
+
     def cname(self, names: NameGenerator) -> str:
         return self.decl.cname(names)
 
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index ef538ee95949..782cb4319757 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -249,7 +249,11 @@ def add_helper_to_generator_class(
         sig.ret_type,
     )
     helper_fn_decl = FuncDecl(
-        "__mypyc_generator_helper__", fn_info.generator_class.ir.name, builder.module_name, sig
+        "__mypyc_generator_helper__",
+        fn_info.generator_class.ir.name,
+        builder.module_name,
+        sig,
+        internal=True,
     )
     helper_fn_ir = FuncIR(
         helper_fn_decl, arg_regs, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name

From c998d21a8b5229efa89175c6827c25dda2c5bbf6 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Tue, 10 Jun 2025 08:31:18 -0700
Subject: [PATCH 1371/1617] Add regression test for narrowing union of mixins
 (#19266)

For https://github.com/python/mypy/issues/16413
---
 test-data/unit/check-narrowing.test | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 47ad62248fe0..3590d1cf2f26 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2577,3 +2577,26 @@ def check_d(arg: D[T]) -> None:
         return
     reveal_type(arg)  # N: Revealed type is "tuple[T`-1, fallback=__main__.D[Any]]"
 [builtins fixtures/tuple.pyi]
+
+
+[case testNarrowingUnionMixins]
+class Base: ...
+
+class FooMixin:
+    def foo(self) -> None: ...
+
+class BarMixin:
+    def bar(self) -> None: ...
+
+def baz(item: Base) -> None:
+    if not isinstance(item, (FooMixin, BarMixin)):
+        raise
+
+    reveal_type(item)  # N: Revealed type is "Union[__main__., __main__.]"
+    if isinstance(item, FooMixin):
+        reveal_type(item)  # N: Revealed type is "__main__.FooMixin"
+        item.foo()
+    else:
+        reveal_type(item)  # N: Revealed type is "__main__."
+        item.bar()
+[builtins fixtures/isinstance.pyi]

From dc42e288cc8eb8ae409a6e678873353a84dabc59 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 10 Jun 2025 17:28:30 +0100
Subject: [PATCH 1372/1617] Fix and simplify error de-duplication (#19247)

Fixes https://github.com/python/mypy/issues/19240
Fixes https://github.com/python/mypy/issues/13517
Fixes https://github.com/python/mypy/issues/17791

Existing de-duplication logic is both complicated and fragile. By
specifying a `parent_error` for a note explicitly, we can do everything
in a more robust and simple way. In addition, this new argument makes
error code matching simpler.
---
 mypy/checker.py                               |  13 +-
 mypy/checkexpr.py                             |   8 +-
 mypy/errors.py                                | 150 +++++---------
 mypy/messages.py                              | 189 ++++++++----------
 mypy/plugin.py                                |   3 +-
 mypy/typeanal.py                              |   5 +-
 test-data/unit/check-classes.test             |  22 ++
 test-data/unit/check-functions.test           |  39 ++++
 test-data/unit/check-narrowing.test           |   7 -
 test-data/unit/check-protocols.test           |  25 +++
 .../fine-grained-dataclass-transform.test     |   1 -
 11 files changed, 233 insertions(+), 229 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 27b71b957efc..63126851793f 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -25,7 +25,7 @@
 from mypy.constraints import SUPERTYPE_OF
 from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values
 from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode
-from mypy.errors import Errors, ErrorWatcher, LoopErrorWatcher, report_internal_error
+from mypy.errors import ErrorInfo, Errors, ErrorWatcher, LoopErrorWatcher, report_internal_error
 from mypy.expandtype import expand_type
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash
 from mypy.maptype import map_instance_to_supertype
@@ -7181,7 +7181,7 @@ def check_subtype(
         if extra_info:
             msg = msg.with_additional_msg(" (" + ", ".join(extra_info) + ")")
 
-        self.fail(msg, context)
+        error = self.fail(msg, context)
         for note in notes:
             self.msg.note(note, context, code=msg.code)
         if note_msg:
@@ -7192,7 +7192,7 @@ def check_subtype(
             and supertype.type.is_protocol
             and isinstance(subtype, (CallableType, Instance, TupleType, TypedDictType))
         ):
-            self.msg.report_protocol_problems(subtype, supertype, context, code=msg.code)
+            self.msg.report_protocol_problems(subtype, supertype, context, parent_error=error)
         if isinstance(supertype, CallableType) and isinstance(subtype, Instance):
             call = find_member("__call__", subtype, subtype, is_operator=True)
             if call:
@@ -7521,12 +7521,11 @@ def temp_node(self, t: Type, context: Context | None = None) -> TempNode:
 
     def fail(
         self, msg: str | ErrorMessage, context: Context, *, code: ErrorCode | None = None
-    ) -> None:
+    ) -> ErrorInfo:
         """Produce an error message."""
         if isinstance(msg, ErrorMessage):
-            self.msg.fail(msg.value, context, code=msg.code)
-            return
-        self.msg.fail(msg, context, code=code)
+            return self.msg.fail(msg.value, context, code=msg.code)
+        return self.msg.fail(msg, context, code=code)
 
     def note(
         self,
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index b8b08547349d..edc3ac70fa54 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2668,7 +2668,7 @@ def check_arg(
         elif self.has_abstract_type_part(caller_type, callee_type):
             self.msg.concrete_only_call(callee_type, context)
         elif not is_subtype(caller_type, callee_type, options=self.chk.options):
-            code = self.msg.incompatible_argument(
+            error = self.msg.incompatible_argument(
                 n,
                 m,
                 callee,
@@ -2679,10 +2679,12 @@ def check_arg(
                 outer_context=outer_context,
             )
             self.msg.incompatible_argument_note(
-                original_caller_type, callee_type, context, code=code
+                original_caller_type, callee_type, context, parent_error=error
             )
             if not self.msg.prefer_simple_messages():
-                self.chk.check_possible_missing_await(caller_type, callee_type, context, code)
+                self.chk.check_possible_missing_await(
+                    caller_type, callee_type, context, error.code
+                )
 
     def check_overload_call(
         self,
diff --git a/mypy/errors.py b/mypy/errors.py
index 6aa19ed7c5a0..7a173f16d196 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -38,8 +38,6 @@
     codes.OVERRIDE,
 }
 
-allowed_duplicates: Final = ["@overload", "Got:", "Expected:", "Expected setter type:"]
-
 BASE_RTD_URL: Final = "https://mypy.rtfd.io/en/stable/_refs.html#code"
 
 # Keep track of the original error code when the error code of a message is changed.
@@ -93,9 +91,6 @@ class ErrorInfo:
     # Only report this particular messages once per program.
     only_once = False
 
-    # Do not remove duplicate copies of this message (ignored if only_once is True).
-    allow_dups = False
-
     # Actual origin of the error message as tuple (path, line number, end line number)
     # If end line number is unknown, use line number.
     origin: tuple[str, Iterable[int]]
@@ -107,6 +102,10 @@ class ErrorInfo:
     # by mypy daemon)
     hidden = False
 
+    # For notes, specifies (optionally) the error this note is attached to. This is used to
+    # simplify error code matching and de-duplication logic for complex multi-line notes.
+    parent_error: ErrorInfo | None = None
+
     def __init__(
         self,
         import_ctx: list[tuple[str, int]],
@@ -124,10 +123,10 @@ def __init__(
         code: ErrorCode | None,
         blocker: bool,
         only_once: bool,
-        allow_dups: bool,
         origin: tuple[str, Iterable[int]] | None = None,
         target: str | None = None,
         priority: int = 0,
+        parent_error: ErrorInfo | None = None,
     ) -> None:
         self.import_ctx = import_ctx
         self.file = file
@@ -143,17 +142,17 @@ def __init__(
         self.code = code
         self.blocker = blocker
         self.only_once = only_once
-        self.allow_dups = allow_dups
         self.origin = origin or (file, [line])
         self.target = target
         self.priority = priority
+        if parent_error is not None:
+            assert severity == "note", "Only notes can specify parent errors"
+        self.parent_error = parent_error
 
 
 # Type used internally to represent errors:
-#   (path, line, column, end_line, end_column, severity, message, allow_dups, code)
-ErrorTuple: _TypeAlias = tuple[
-    Optional[str], int, int, int, int, str, str, bool, Optional[ErrorCode]
-]
+#   (path, line, column, end_line, end_column, severity, message, code)
+ErrorTuple: _TypeAlias = tuple[Optional[str], int, int, int, int, str, str, Optional[ErrorCode]]
 
 
 class ErrorWatcher:
@@ -446,12 +445,12 @@ def report(
         severity: str = "error",
         file: str | None = None,
         only_once: bool = False,
-        allow_dups: bool = False,
         origin_span: Iterable[int] | None = None,
         offset: int = 0,
         end_line: int | None = None,
         end_column: int | None = None,
-    ) -> None:
+        parent_error: ErrorInfo | None = None,
+    ) -> ErrorInfo:
         """Report message at the given line using the current error context.
 
         Args:
@@ -463,10 +462,10 @@ def report(
             severity: 'error' or 'note'
             file: if non-None, override current file as context
             only_once: if True, only report this exact message once per build
-            allow_dups: if True, allow duplicate copies of this message (ignored if only_once)
             origin_span: if non-None, override current context as origin
                          (type: ignores have effect here)
             end_line: if non-None, override current context as end
+            parent_error: an error this note is attached to (for notes only).
         """
         if self.scope:
             type = self.scope.current_type_name()
@@ -496,6 +495,7 @@ def report(
         if end_line is None:
             end_line = line
 
+        code = code or (parent_error.code if parent_error else None)
         code = code or (codes.MISC if not blocker else None)
 
         info = ErrorInfo(
@@ -513,11 +513,12 @@ def report(
             code=code,
             blocker=blocker,
             only_once=only_once,
-            allow_dups=allow_dups,
             origin=(self.file, origin_span),
             target=self.current_target(),
+            parent_error=parent_error,
         )
         self.add_error_info(info)
+        return info
 
     def _add_error_info(self, file: str, info: ErrorInfo) -> None:
         assert file not in self.flushed_files
@@ -616,7 +617,6 @@ def add_error_info(self, info: ErrorInfo) -> None:
                 code=None,
                 blocker=False,
                 only_once=False,
-                allow_dups=False,
             )
             self._add_error_info(file, note)
         if (
@@ -645,7 +645,6 @@ def add_error_info(self, info: ErrorInfo) -> None:
                 code=info.code,
                 blocker=False,
                 only_once=True,
-                allow_dups=False,
                 priority=20,
             )
             self._add_error_info(file, info)
@@ -685,7 +684,6 @@ def report_hidden_errors(self, info: ErrorInfo) -> None:
             code=None,
             blocker=False,
             only_once=True,
-            allow_dups=False,
             origin=info.origin,
             target=info.target,
         )
@@ -788,7 +786,6 @@ def generate_unused_ignore_errors(self, file: str) -> None:
                 code=codes.UNUSED_IGNORE,
                 blocker=False,
                 only_once=False,
-                allow_dups=False,
             )
             self._add_error_info(file, info)
 
@@ -840,7 +837,6 @@ def generate_ignore_without_code_errors(
                 code=codes.IGNORE_WITHOUT_CODE,
                 blocker=False,
                 only_once=False,
-                allow_dups=False,
             )
             self._add_error_info(file, info)
 
@@ -907,17 +903,7 @@ def format_messages(
         severity 'error').
         """
         a: list[str] = []
-        for (
-            file,
-            line,
-            column,
-            end_line,
-            end_column,
-            severity,
-            message,
-            allow_dups,
-            code,
-        ) in error_tuples:
+        for file, line, column, end_line, end_column, severity, message, code in error_tuples:
             s = ""
             if file is not None:
                 if self.options.show_column_numbers and line >= 0 and column >= 0:
@@ -972,8 +958,8 @@ def file_messages(self, path: str, formatter: ErrorFormatter | None = None) -> l
 
         error_info = self.error_info_map[path]
         error_info = [info for info in error_info if not info.hidden]
-        error_tuples = self.render_messages(self.sort_messages(error_info))
-        error_tuples = self.remove_duplicates(error_tuples)
+        error_info = self.remove_duplicates(self.sort_messages(error_info))
+        error_tuples = self.render_messages(error_info)
 
         if formatter is not None:
             errors = create_errors(error_tuples)
@@ -1025,7 +1011,7 @@ def targets(self) -> set[str]:
     def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
         """Translate the messages into a sequence of tuples.
 
-        Each tuple is of form (path, line, col, severity, message, allow_dups, code).
+        Each tuple is of form (path, line, col, severity, message, code).
         The rendered sequence includes information about error contexts.
         The path item may be None. If the line item is negative, the
         line number is not defined for the tuple.
@@ -1054,9 +1040,7 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
                     # Remove prefix to ignore from path (if present) to
                     # simplify path.
                     path = remove_path_prefix(path, self.ignore_prefix)
-                    result.append(
-                        (None, -1, -1, -1, -1, "note", fmt.format(path, line), e.allow_dups, None)
-                    )
+                    result.append((None, -1, -1, -1, -1, "note", fmt.format(path, line), None))
                     i -= 1
 
             file = self.simplify_path(e.file)
@@ -1067,22 +1051,10 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
             elif e.function_or_member != prev_function_or_member or e.type != prev_type:
                 if e.function_or_member is None:
                     if e.type is None:
-                        result.append(
-                            (file, -1, -1, -1, -1, "note", "At top level:", e.allow_dups, None)
-                        )
+                        result.append((file, -1, -1, -1, -1, "note", "At top level:", None))
                     else:
                         result.append(
-                            (
-                                file,
-                                -1,
-                                -1,
-                                -1,
-                                -1,
-                                "note",
-                                f'In class "{e.type}":',
-                                e.allow_dups,
-                                None,
-                            )
+                            (file, -1, -1, -1, -1, "note", f'In class "{e.type}":', None)
                         )
                 else:
                     if e.type is None:
@@ -1095,7 +1067,6 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
                                 -1,
                                 "note",
                                 f'In function "{e.function_or_member}":',
-                                e.allow_dups,
                                 None,
                             )
                         )
@@ -1111,32 +1082,17 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
                                 'In member "{}" of class "{}":'.format(
                                     e.function_or_member, e.type
                                 ),
-                                e.allow_dups,
                                 None,
                             )
                         )
             elif e.type != prev_type:
                 if e.type is None:
-                    result.append(
-                        (file, -1, -1, -1, -1, "note", "At top level:", e.allow_dups, None)
-                    )
+                    result.append((file, -1, -1, -1, -1, "note", "At top level:", None))
                 else:
-                    result.append(
-                        (file, -1, -1, -1, -1, "note", f'In class "{e.type}":', e.allow_dups, None)
-                    )
+                    result.append((file, -1, -1, -1, -1, "note", f'In class "{e.type}":', None))
 
             result.append(
-                (
-                    file,
-                    e.line,
-                    e.column,
-                    e.end_line,
-                    e.end_column,
-                    e.severity,
-                    e.message,
-                    e.allow_dups,
-                    e.code,
-                )
+                (file, e.line, e.column, e.end_line, e.end_column, e.severity, e.message, e.code)
             )
 
             prev_import_context = e.import_ctx
@@ -1198,40 +1154,24 @@ def sort_within_context(self, errors: list[ErrorInfo]) -> list[ErrorInfo]:
             result.extend(a)
         return result
 
-    def remove_duplicates(self, errors: list[ErrorTuple]) -> list[ErrorTuple]:
-        """Remove duplicates from a sorted error list."""
-        res: list[ErrorTuple] = []
-        i = 0
-        while i < len(errors):
-            dup = False
-            # Use slightly special formatting for member conflicts reporting.
-            conflicts_notes = False
-            j = i - 1
-            # Find duplicates, unless duplicates are allowed.
-            if not errors[i][7]:
-                while j >= 0 and errors[j][0] == errors[i][0]:
-                    if errors[j][6].strip() == "Got:":
-                        conflicts_notes = True
-                    j -= 1
-                j = i - 1
-                while j >= 0 and errors[j][0] == errors[i][0] and errors[j][1] == errors[i][1]:
-                    if (
-                        errors[j][5] == errors[i][5]
-                        and
-                        # Allow duplicate notes in overload conflicts reporting.
-                        not (
-                            (errors[i][5] == "note" and errors[i][6].strip() in allowed_duplicates)
-                            or (errors[i][6].strip().startswith("def ") and conflicts_notes)
-                        )
-                        and errors[j][6] == errors[i][6]
-                    ):  # ignore column
-                        dup = True
-                        break
-                    j -= 1
-            if not dup:
-                res.append(errors[i])
-            i += 1
-        return res
+    def remove_duplicates(self, errors: list[ErrorInfo]) -> list[ErrorInfo]:
+        filtered_errors = []
+        seen_by_line: defaultdict[int, set[tuple[str, str]]] = defaultdict(set)
+        removed = set()
+        for err in errors:
+            if err.parent_error is not None:
+                # Notes with specified parent are removed together with error below.
+                filtered_errors.append(err)
+            elif (err.severity, err.message) not in seen_by_line[err.line]:
+                filtered_errors.append(err)
+                seen_by_line[err.line].add((err.severity, err.message))
+            else:
+                removed.add(err)
+        return [
+            err
+            for err in filtered_errors
+            if err.parent_error is None or err.parent_error not in removed
+        ]
 
 
 class CompileError(Exception):
@@ -1380,7 +1320,7 @@ def create_errors(error_tuples: list[ErrorTuple]) -> list[MypyError]:
     latest_error_at_location: dict[_ErrorLocation, MypyError] = {}
 
     for error_tuple in error_tuples:
-        file_path, line, column, _, _, severity, message, _, errorcode = error_tuple
+        file_path, line, column, _, _, severity, message, errorcode = error_tuple
         if file_path is None:
             continue
 
diff --git a/mypy/messages.py b/mypy/messages.py
index 9c4c141c4a79..46ade80df61d 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -230,9 +230,9 @@ def report(
         file: str | None = None,
         origin: Context | None = None,
         offset: int = 0,
-        allow_dups: bool = False,
         secondary_context: Context | None = None,
-    ) -> None:
+        parent_error: ErrorInfo | None = None,
+    ) -> ErrorInfo:
         """Report an error or note (unless disabled).
 
         Note that context controls where error is reported, while origin controls
@@ -267,7 +267,7 @@ def span_from_context(ctx: Context) -> Iterable[int]:
             assert origin_span is not None
             origin_span = itertools.chain(origin_span, span_from_context(secondary_context))
 
-        self.errors.report(
+        return self.errors.report(
             context.line if context else -1,
             context.column if context else -1,
             msg,
@@ -278,7 +278,7 @@ def span_from_context(ctx: Context) -> Iterable[int]:
             end_line=context.end_line if context else -1,
             end_column=context.end_column if context else -1,
             code=code,
-            allow_dups=allow_dups,
+            parent_error=parent_error,
         )
 
     def fail(
@@ -288,18 +288,11 @@ def fail(
         *,
         code: ErrorCode | None = None,
         file: str | None = None,
-        allow_dups: bool = False,
         secondary_context: Context | None = None,
-    ) -> None:
+    ) -> ErrorInfo:
         """Report an error message (unless disabled)."""
-        self.report(
-            msg,
-            context,
-            "error",
-            code=code,
-            file=file,
-            allow_dups=allow_dups,
-            secondary_context=secondary_context,
+        return self.report(
+            msg, context, "error", code=code, file=file, secondary_context=secondary_context
         )
 
     def note(
@@ -309,10 +302,10 @@ def note(
         file: str | None = None,
         origin: Context | None = None,
         offset: int = 0,
-        allow_dups: bool = False,
         *,
         code: ErrorCode | None = None,
         secondary_context: Context | None = None,
+        parent_error: ErrorInfo | None = None,
     ) -> None:
         """Report a note (unless disabled)."""
         self.report(
@@ -322,9 +315,9 @@ def note(
             file=file,
             origin=origin,
             offset=offset,
-            allow_dups=allow_dups,
             code=code,
             secondary_context=secondary_context,
+            parent_error=parent_error,
         )
 
     def note_multiline(
@@ -333,7 +326,6 @@ def note_multiline(
         context: Context,
         file: str | None = None,
         offset: int = 0,
-        allow_dups: bool = False,
         code: ErrorCode | None = None,
         *,
         secondary_context: Context | None = None,
@@ -346,7 +338,6 @@ def note_multiline(
                 "note",
                 file=file,
                 offset=offset,
-                allow_dups=allow_dups,
                 code=code,
                 secondary_context=secondary_context,
             )
@@ -574,7 +565,7 @@ def unsupported_operand_types(
         context: Context,
         *,
         code: ErrorCode = codes.OPERATOR,
-    ) -> None:
+    ) -> ErrorInfo:
         """Report unsupported operand types for a binary operation.
 
         Types can be Type objects or strings.
@@ -595,7 +586,7 @@ def unsupported_operand_types(
             msg = f"Unsupported operand types for {op} (likely involving Union)"
         else:
             msg = f"Unsupported operand types for {op} ({left_str} and {right_str})"
-        self.fail(msg, context, code=code)
+        return self.fail(msg, context, code=code)
 
     def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None:
         if self.are_type_names_disabled():
@@ -627,7 +618,7 @@ def incompatible_argument(
         object_type: Type | None,
         context: Context,
         outer_context: Context,
-    ) -> ErrorCode | None:
+    ) -> ErrorInfo:
         """Report an error about an incompatible argument type.
 
         The argument type is arg_type, argument number is n and the
@@ -655,27 +646,24 @@ def incompatible_argument(
                     if name.startswith(f'"{variant}" of'):
                         if op == "in" or variant != method:
                             # Reversed order of base/argument.
-                            self.unsupported_operand_types(
+                            return self.unsupported_operand_types(
                                 op, arg_type, base, context, code=codes.OPERATOR
                             )
                         else:
-                            self.unsupported_operand_types(
+                            return self.unsupported_operand_types(
                                 op, base, arg_type, context, code=codes.OPERATOR
                             )
-                        return codes.OPERATOR
 
             if name.startswith('"__getitem__" of'):
-                self.invalid_index_type(
+                return self.invalid_index_type(
                     arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX
                 )
-                return codes.INDEX
 
             if name.startswith('"__setitem__" of'):
                 if n == 1:
-                    self.invalid_index_type(
+                    return self.invalid_index_type(
                         arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX
                     )
-                    return codes.INDEX
                 else:
                     arg_type_str, callee_type_str = format_type_distinctly(
                         arg_type, callee.arg_types[n - 1], options=self.options
@@ -686,8 +674,7 @@ def incompatible_argument(
                     error_msg = (
                         message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT.with_additional_msg(info)
                     )
-                    self.fail(error_msg.value, context, code=error_msg.code)
-                    return error_msg.code
+                    return self.fail(error_msg.value, context, code=error_msg.code)
 
             target = f"to {name} "
 
@@ -841,18 +828,18 @@ def incompatible_argument(
                 code = codes.TYPEDDICT_ITEM
             else:
                 code = codes.ARG_TYPE
-        self.fail(msg, context, code=code)
+        error = self.fail(msg, context, code=code)
         if notes:
             for note_msg in notes:
                 self.note(note_msg, context, code=code)
-        return code
+        return error
 
     def incompatible_argument_note(
         self,
         original_caller_type: ProperType,
         callee_type: ProperType,
         context: Context,
-        code: ErrorCode | None,
+        parent_error: ErrorInfo,
     ) -> None:
         if self.prefer_simple_messages():
             return
@@ -861,26 +848,28 @@ def incompatible_argument_note(
         ):
             if isinstance(callee_type, Instance) and callee_type.type.is_protocol:
                 self.report_protocol_problems(
-                    original_caller_type, callee_type, context, code=code
+                    original_caller_type, callee_type, context, parent_error=parent_error
                 )
             if isinstance(callee_type, UnionType):
                 for item in callee_type.items:
                     item = get_proper_type(item)
                     if isinstance(item, Instance) and item.type.is_protocol:
                         self.report_protocol_problems(
-                            original_caller_type, item, context, code=code
+                            original_caller_type, item, context, parent_error=parent_error
                         )
         if isinstance(callee_type, CallableType) and isinstance(original_caller_type, Instance):
             call = find_member(
                 "__call__", original_caller_type, original_caller_type, is_operator=True
             )
             if call:
-                self.note_call(original_caller_type, call, context, code=code)
+                self.note_call(original_caller_type, call, context, code=parent_error.code)
         if isinstance(callee_type, Instance) and callee_type.type.is_protocol:
             call = find_member("__call__", callee_type, callee_type, is_operator=True)
             if call:
-                self.note_call(callee_type, call, context, code=code)
-        self.maybe_note_concatenate_pos_args(original_caller_type, callee_type, context, code)
+                self.note_call(callee_type, call, context, code=parent_error.code)
+        self.maybe_note_concatenate_pos_args(
+            original_caller_type, callee_type, context, parent_error.code
+        )
 
     def maybe_note_concatenate_pos_args(
         self,
@@ -922,11 +911,11 @@ def invalid_index_type(
         context: Context,
         *,
         code: ErrorCode,
-    ) -> None:
+    ) -> ErrorInfo:
         index_str, expected_str = format_type_distinctly(
             index_type, expected_type, options=self.options
         )
-        self.fail(
+        return self.fail(
             "Invalid index type {} for {}; expected type {}".format(
                 index_str, base_str, expected_str
             ),
@@ -1193,16 +1182,16 @@ def signature_incompatible_with_supertype(
         original: ProperType,
         override: ProperType,
     ) -> None:
-        code = codes.OVERRIDE
         target = self.override_target(name, name_in_super, supertype)
-        self.fail(f'Signature of "{name}" incompatible with {target}', context, code=code)
+        error = self.fail(
+            f'Signature of "{name}" incompatible with {target}', context, code=codes.OVERRIDE
+        )
 
         original_str, override_str = format_type_distinctly(
             original, override, options=self.options, bare=True
         )
 
         INCLUDE_DECORATOR = True  # Include @classmethod and @staticmethod decorators, if any
-        ALLOW_DUPS = True  # Allow duplicate notes, needed when signatures are duplicates
         ALIGN_OFFSET = 1  # One space, to account for the difference between error and note
         OFFSET = 4  # Four spaces, so that notes will look like this:
         # error: Signature of "f" incompatible with supertype "A"
@@ -1210,69 +1199,49 @@ def signature_incompatible_with_supertype(
         # note:          def f(self) -> str
         # note:      Subclass:
         # note:          def f(self, x: str) -> None
-        self.note(
-            "Superclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
-        )
+        self.note("Superclass:", context, offset=ALIGN_OFFSET + OFFSET, parent_error=error)
         if isinstance(original, (CallableType, Overloaded)):
             self.pretty_callable_or_overload(
                 original,
                 context,
                 offset=ALIGN_OFFSET + 2 * OFFSET,
                 add_class_or_static_decorator=INCLUDE_DECORATOR,
-                allow_dups=ALLOW_DUPS,
-                code=code,
+                parent_error=error,
             )
         else:
-            self.note(
-                original_str,
-                context,
-                offset=ALIGN_OFFSET + 2 * OFFSET,
-                allow_dups=ALLOW_DUPS,
-                code=code,
-            )
+            self.note(original_str, context, offset=ALIGN_OFFSET + 2 * OFFSET, parent_error=error)
 
-        self.note(
-            "Subclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
-        )
+        self.note("Subclass:", context, offset=ALIGN_OFFSET + OFFSET, parent_error=error)
         if isinstance(override, (CallableType, Overloaded)):
             self.pretty_callable_or_overload(
                 override,
                 context,
                 offset=ALIGN_OFFSET + 2 * OFFSET,
                 add_class_or_static_decorator=INCLUDE_DECORATOR,
-                allow_dups=ALLOW_DUPS,
-                code=code,
+                parent_error=error,
             )
         else:
-            self.note(
-                override_str,
-                context,
-                offset=ALIGN_OFFSET + 2 * OFFSET,
-                allow_dups=ALLOW_DUPS,
-                code=code,
-            )
+            self.note(override_str, context, offset=ALIGN_OFFSET + 2 * OFFSET, parent_error=error)
 
     def pretty_callable_or_overload(
         self,
         tp: CallableType | Overloaded,
         context: Context,
         *,
+        parent_error: ErrorInfo,
         offset: int = 0,
         add_class_or_static_decorator: bool = False,
-        allow_dups: bool = False,
-        code: ErrorCode | None = None,
     ) -> None:
         if isinstance(tp, CallableType):
             if add_class_or_static_decorator:
                 decorator = pretty_class_or_static_decorator(tp)
                 if decorator is not None:
-                    self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code)
+                    self.note(decorator, context, offset=offset, parent_error=parent_error)
             self.note(
                 pretty_callable(tp, self.options),
                 context,
                 offset=offset,
-                allow_dups=allow_dups,
-                code=code,
+                parent_error=parent_error,
             )
         elif isinstance(tp, Overloaded):
             self.pretty_overload(
@@ -1280,8 +1249,7 @@ def pretty_callable_or_overload(
                 context,
                 offset,
                 add_class_or_static_decorator=add_class_or_static_decorator,
-                allow_dups=allow_dups,
-                code=code,
+                parent_error=parent_error,
             )
 
     def argument_incompatible_with_supertype(
@@ -1533,14 +1501,14 @@ def incompatible_self_argument(
     def incompatible_conditional_function_def(
         self, defn: FuncDef, old_type: FunctionLike, new_type: FunctionLike
     ) -> None:
-        self.fail("All conditional function variants must have identical signatures", defn)
+        error = self.fail("All conditional function variants must have identical signatures", defn)
         if isinstance(old_type, (CallableType, Overloaded)) and isinstance(
             new_type, (CallableType, Overloaded)
         ):
             self.note("Original:", defn)
-            self.pretty_callable_or_overload(old_type, defn, offset=4)
+            self.pretty_callable_or_overload(old_type, defn, offset=4, parent_error=error)
             self.note("Redefinition:", defn)
-            self.pretty_callable_or_overload(new_type, defn, offset=4)
+            self.pretty_callable_or_overload(new_type, defn, offset=4, parent_error=error)
 
     def cannot_instantiate_abstract_class(
         self, class_name: str, abstract_attributes: dict[str, bool], context: Context
@@ -2120,7 +2088,7 @@ def report_protocol_problems(
         supertype: Instance,
         context: Context,
         *,
-        code: ErrorCode | None,
+        parent_error: ErrorInfo,
     ) -> None:
         """Report possible protocol conflicts between 'subtype' and 'supertype'.
 
@@ -2184,7 +2152,7 @@ def report_protocol_problems(
                         subtype.type.name, supertype.type.name
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             else:
                 self.note(
@@ -2192,9 +2160,9 @@ def report_protocol_problems(
                         subtype.type.name, supertype.type.name, plural_s(missing)
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
-                self.note(", ".join(missing), context, offset=OFFSET, code=code)
+                self.note(", ".join(missing), context, offset=OFFSET, parent_error=parent_error)
         elif len(missing) > MAX_ITEMS or len(missing) == len(supertype.type.protocol_members):
             # This is an obviously wrong type: too many missing members
             return
@@ -2212,7 +2180,11 @@ def report_protocol_problems(
             or supertype.type.has_param_spec_type
         ):
             type_name = format_type(subtype, self.options, module_names=True)
-            self.note(f"Following member(s) of {type_name} have conflicts:", context, code=code)
+            self.note(
+                f"Following member(s) of {type_name} have conflicts:",
+                context,
+                parent_error=parent_error,
+            )
             for name, got, exp, is_lvalue in conflict_types[:MAX_ITEMS]:
                 exp = get_proper_type(exp)
                 got = get_proper_type(got)
@@ -2233,45 +2205,56 @@ def report_protocol_problems(
                         ),
                         context,
                         offset=OFFSET,
-                        code=code,
+                        parent_error=parent_error,
                     )
                     if is_lvalue and is_subtype(got, exp, options=self.options):
                         self.note(
                             "Setter types should behave contravariantly",
                             context,
                             offset=OFFSET,
-                            code=code,
+                            parent_error=parent_error,
                         )
                 else:
                     self.note(
-                        "Expected{}:".format(setter_suffix), context, offset=OFFSET, code=code
+                        "Expected{}:".format(setter_suffix),
+                        context,
+                        offset=OFFSET,
+                        parent_error=parent_error,
                     )
                     if isinstance(exp, CallableType):
                         self.note(
                             pretty_callable(exp, self.options, skip_self=class_obj or is_module),
                             context,
                             offset=2 * OFFSET,
-                            code=code,
+                            parent_error=parent_error,
                         )
                     else:
                         assert isinstance(exp, Overloaded)
                         self.pretty_overload(
-                            exp, context, 2 * OFFSET, code=code, skip_self=class_obj or is_module
+                            exp,
+                            context,
+                            2 * OFFSET,
+                            parent_error=parent_error,
+                            skip_self=class_obj or is_module,
                         )
-                    self.note("Got:", context, offset=OFFSET, code=code)
+                    self.note("Got:", context, offset=OFFSET, parent_error=parent_error)
                     if isinstance(got, CallableType):
                         self.note(
                             pretty_callable(got, self.options, skip_self=class_obj or is_module),
                             context,
                             offset=2 * OFFSET,
-                            code=code,
+                            parent_error=parent_error,
                         )
                     else:
                         assert isinstance(got, Overloaded)
                         self.pretty_overload(
-                            got, context, 2 * OFFSET, code=code, skip_self=class_obj or is_module
+                            got,
+                            context,
+                            2 * OFFSET,
+                            parent_error=parent_error,
+                            skip_self=class_obj or is_module,
                         )
-            self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=code)
+            self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=parent_error.code)
 
         # Report flag conflicts (i.e. settable vs read-only etc.)
         conflict_flags = get_bad_protocol_flags(subtype, supertype, class_obj=class_obj)
@@ -2282,7 +2265,7 @@ def report_protocol_problems(
                         supertype.type.name, name
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             if not class_obj and IS_CLASSVAR in superflags and IS_CLASSVAR not in subflags:
                 self.note(
@@ -2290,14 +2273,14 @@ def report_protocol_problems(
                         supertype.type.name, name
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             if IS_SETTABLE in superflags and IS_SETTABLE not in subflags:
                 self.note(
                     "Protocol member {}.{} expected settable variable,"
                     " got read-only attribute".format(supertype.type.name, name),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags:
                 self.note(
@@ -2305,7 +2288,7 @@ def report_protocol_problems(
                         supertype.type.name, name
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             if (
                 class_obj
@@ -2316,7 +2299,7 @@ def report_protocol_problems(
                     "Only class variables allowed for class object access on protocols,"
                     ' {} is an instance variable of "{}"'.format(name, subtype.type.name),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
             if class_obj and IS_CLASSVAR in superflags:
                 self.note(
@@ -2324,9 +2307,9 @@ def report_protocol_problems(
                         supertype.type.name, name
                     ),
                     context,
-                    code=code,
+                    parent_error=parent_error,
                 )
-        self.print_more(conflict_flags, context, OFFSET, MAX_ITEMS, code=code)
+        self.print_more(conflict_flags, context, OFFSET, MAX_ITEMS, code=parent_error.code)
 
     def pretty_overload(
         self,
@@ -2334,25 +2317,23 @@ def pretty_overload(
         context: Context,
         offset: int,
         *,
+        parent_error: ErrorInfo,
         add_class_or_static_decorator: bool = False,
-        allow_dups: bool = False,
-        code: ErrorCode | None = None,
         skip_self: bool = False,
     ) -> None:
         for item in tp.items:
-            self.note("@overload", context, offset=offset, allow_dups=allow_dups, code=code)
+            self.note("@overload", context, offset=offset, parent_error=parent_error)
 
             if add_class_or_static_decorator:
                 decorator = pretty_class_or_static_decorator(item)
                 if decorator is not None:
-                    self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code)
+                    self.note(decorator, context, offset=offset, parent_error=parent_error)
 
             self.note(
                 pretty_callable(item, self.options, skip_self=skip_self),
                 context,
                 offset=offset,
-                allow_dups=allow_dups,
-                code=code,
+                parent_error=parent_error,
             )
 
     def print_more(
diff --git a/mypy/plugin.py b/mypy/plugin.py
index de075866d613..831721eb193c 100644
--- a/mypy/plugin.py
+++ b/mypy/plugin.py
@@ -124,6 +124,7 @@ class C: pass
 from mypy_extensions import mypyc_attr, trait
 
 from mypy.errorcodes import ErrorCode
+from mypy.errors import ErrorInfo
 from mypy.lookup import lookup_fully_qualified
 from mypy.message_registry import ErrorMessage
 from mypy.nodes import (
@@ -240,7 +241,7 @@ def type_context(self) -> list[Type | None]:
     @abstractmethod
     def fail(
         self, msg: str | ErrorMessage, ctx: Context, /, *, code: ErrorCode | None = None
-    ) -> None:
+    ) -> ErrorInfo | None:
         """Emit an error message at given location."""
         raise NotImplementedError
 
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index f4b12c1c978d..eeb5d3c52ac6 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -9,6 +9,7 @@
 
 from mypy import errorcodes as codes, message_registry, nodes
 from mypy.errorcodes import ErrorCode
+from mypy.errors import ErrorInfo
 from mypy.expandtype import expand_type
 from mypy.message_registry import (
     INVALID_PARAM_SPEC_LOCATION,
@@ -1994,7 +1995,9 @@ def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType:
 
 
 class MsgCallback(Protocol):
-    def __call__(self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None) -> None: ...
+    def __call__(
+        self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None
+    ) -> ErrorInfo | None: ...
 
 
 def get_omitted_any(
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 23c0d4ccf316..f4bbaf41dc47 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -8738,6 +8738,28 @@ class NoopPowerResource:
         self.hardware_type = None  # Note: intentionally recursive
 [builtins fixtures/property.pyi]
 
+[case testOverrideErrorReportingNoDuplicates]
+from typing import Callable, TypeVar
+
+def nested() -> None:
+    class B:
+        def meth(self, x: str) -> int: ...
+    class C(B):
+        def meth(self) -> str:  # E: Signature of "meth" incompatible with supertype "B" \
+                # N:      Superclass: \
+                # N:          def meth(self, x: str) -> int \
+                # N:      Subclass: \
+                # N:          def meth(self) -> str
+            pass
+    x = defer()
+
+T = TypeVar("T")
+def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ...
+
+@deco
+def defer() -> int: ...
+[builtins fixtures/list.pyi]
+
 [case testPropertyAllowsDeleterBeforeSetter]
 class C:
     @property
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index 4b980f102c52..ceb7af433dce 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3655,3 +3655,42 @@ reveal_type(C().x6)  # N: Revealed type is "def (x: builtins.int) -> builtins.st
 reveal_type(C().x7)  # E: Invalid self argument "C" to attribute function "x7" with type "Callable[[int], str]" \
                      # N: Revealed type is "def () -> builtins.str"
 [builtins fixtures/classmethod.pyi]
+
+[case testFunctionRedefinitionDeferred]
+from typing import Callable, TypeVar
+
+def outer() -> None:
+    if bool():
+        def inner() -> str: ...
+    else:
+        def inner() -> int: ...  # E: All conditional function variants must have identical signatures \
+                                 # N: Original: \
+                                 # N:     def inner() -> str \
+                                 # N: Redefinition: \
+                                 # N:     def inner() -> int
+    x = defer()
+
+T = TypeVar("T")
+def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ...
+
+@deco
+def defer() -> int: ...
+[builtins fixtures/list.pyi]
+
+[case testCheckFunctionErrorContextDuplicateDeferred]
+# flags: --show-error-context
+from typing import Callable, TypeVar
+
+def a() -> None:
+    def b() -> None:
+        1 + ""
+        x = defer()
+
+T = TypeVar("T")
+def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ...
+
+@deco
+def defer() -> int: ...
+[out]
+main: note: In function "a":
+main:6: error: Unsupported operand types for + ("int" and "str")
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 3590d1cf2f26..3778c5276576 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2366,7 +2366,6 @@ class A:
                 if z:
                     z[0] + "v"  # E: Unsupported operand types for + ("int" and "str")
                 z.append(1)
-
 [builtins fixtures/primitives.pyi]
 
 [case testPersistentUnreachableLinesNestedInInpersistentUnreachableLines]
@@ -2379,7 +2378,6 @@ while True:
         if y is not None:
             reveal_type(y)  # E: Statement is unreachable
     x = 1
-
 [builtins fixtures/bool.pyi]
 
 [case testAvoidFalseRedundantCastInLoops]
@@ -2401,7 +2399,6 @@ def main_no_cast(p: Processor) -> None:
     ed = cast(str, ...)
     while True:
         ed = p(ed)  # E: Argument 1 has incompatible type "Union[str, int]"; expected "str"
-
 [builtins fixtures/bool.pyi]
 
 [case testAvoidFalseUnreachableInLoop1]
@@ -2414,7 +2411,6 @@ x: int | None
 x = 1
 while x is not None or b():
     x = f()
-
 [builtins fixtures/bool.pyi]
 
 [case testAvoidFalseUnreachableInLoop2]
@@ -2425,7 +2421,6 @@ while y is None:
     if y is None:
         y = []
     y.append(1)
-
 [builtins fixtures/list.pyi]
 
 [case testAvoidFalseUnreachableInLoop3]
@@ -2437,7 +2432,6 @@ for x in xs:
     if x is not None:
         if y is None:
             y = {}  # E: Need type annotation for "y" (hint: "y: dict[, ] = ...")
-
 [builtins fixtures/list.pyi]
 
 [case testAvoidFalseRedundantExprInLoop]
@@ -2450,7 +2444,6 @@ x: int | None
 x = 1
 while x is not None and b():
     x = f()
-
 [builtins fixtures/primitives.pyi]
 
 [case testNarrowingTypeVarMultiple]
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index f2b8fc7a0e14..4d7f46e5de2b 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4551,3 +4551,28 @@ class Test(Generic[T]):
 t = Test(Mock())
 reveal_type(t)  # N: Revealed type is "__main__.Test[Any]"
 [builtins fixtures/dict.pyi]
+
+[case testProtocolErrorReportingNoDuplicates]
+from typing import Callable, Protocol, TypeVar
+
+class P(Protocol):
+    def meth(self) -> int: ...
+
+class C:
+    def meth(self) -> str: ...
+
+def foo() -> None:
+    c: P = C()  # E: Incompatible types in assignment (expression has type "C", variable has type "P") \
+                # N: Following member(s) of "C" have conflicts: \
+                # N:     Expected: \
+                # N:         def meth(self) -> int \
+                # N:     Got: \
+                # N:         def meth(self) -> str
+    x = defer()
+
+T = TypeVar("T")
+def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ...
+
+@deco
+def defer() -> int: ...
+[builtins fixtures/list.pyi]
diff --git a/test-data/unit/fine-grained-dataclass-transform.test b/test-data/unit/fine-grained-dataclass-transform.test
index 89628256fda5..76ffeeb347c7 100644
--- a/test-data/unit/fine-grained-dataclass-transform.test
+++ b/test-data/unit/fine-grained-dataclass-transform.test
@@ -88,7 +88,6 @@ class A(Dataclass):
 main:7: error: Unexpected keyword argument "x" for "B"
 builtins.pyi:14: note: "B" defined here
 main:7: error: Unexpected keyword argument "y" for "B"
-builtins.pyi:14: note: "B" defined here
 ==
 
 [case frozenInheritanceViaDefault]

From 8667022a7ee2d7a5af5e840214c05add62e67d31 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Tue, 10 Jun 2025 17:48:05 -0400
Subject: [PATCH 1373/1617] Fix missing error context for unpacking assignment
 involving star expression (#19258)

Fixes #19257
---
 mypy/checker.py                  | 2 +-
 test-data/unit/check-lists.test  | 9 +++++++++
 test-data/unit/check-tuples.test | 9 +++++++++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 63126851793f..fc9733117a0a 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -3840,7 +3840,7 @@ def check_assignment_to_multiple_lvalues(
                 if rvalue_needed > 0:
                     rvalues = (
                         rvalues[0:iterable_start]
-                        + [TempNode(iterable_type) for i in range(rvalue_needed)]
+                        + [TempNode(iterable_type, context=rval) for _ in range(rvalue_needed)]
                         + rvalues[iterable_end + 1 :]
                     )
 
diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test
index 77acdafd3319..ee3115421e40 100644
--- a/test-data/unit/check-lists.test
+++ b/test-data/unit/check-lists.test
@@ -94,3 +94,12 @@ def foo(x: object) -> None:
         [reveal_type(x) for x in [1, 2, 3]]  # N: Revealed type is "builtins.int"
 
 [builtins fixtures/isinstancelist.pyi]
+
+[case testUnpackAssignmentWithStarExpr]
+a: A
+b: list[B]
+if int():
+    (a,) = [*b]  # E: Incompatible types in assignment (expression has type "B", variable has type "A")
+
+class A: pass
+class B: pass
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
index f118eec4f266..615ba129dad5 100644
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -618,6 +618,15 @@ u, v, w = r, s = 1, 1 # E: Need more than 2 values to unpack (3 expected)
 d, e = f, g, h = 1, 1 # E: Need more than 2 values to unpack (3 expected)
 [builtins fixtures/tuple.pyi]
 
+[case testUnpackAssignmentWithStarExpr]
+a: A
+b: list[B]
+if int():
+    (a,) = (*b,)  # E: Incompatible types in assignment (expression has type "B", variable has type "A")
+
+class A: pass
+class B: pass
+
 
 -- Assignment to starred expressions
 -- ---------------------------------

From f20988875ad7e02844c53231829f404c4a26f70c Mon Sep 17 00:00:00 2001
From: A5rocks 
Date: Wed, 11 Jun 2025 13:57:31 +0900
Subject: [PATCH 1374/1617] Single underscore is not a sunder (#19273)

Fixes https://github.com/python/mypy/issues/19271.
---
 mypy/util.py                   |  2 +-
 test-data/unit/check-enum.test | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/mypy/util.py b/mypy/util.py
index d3f49f74bbae..d7ff2a367fa2 100644
--- a/mypy/util.py
+++ b/mypy/util.py
@@ -66,7 +66,7 @@ def is_dunder(name: str, exclude_special: bool = False) -> bool:
 
 
 def is_sunder(name: str) -> bool:
-    return not is_dunder(name) and name.startswith("_") and name.endswith("_")
+    return not is_dunder(name) and name.startswith("_") and name.endswith("_") and name != "_"
 
 
 def split_module_names(mod_name: str) -> list[str]:
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index 1a07e4527527..1ab8109eda75 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -2524,3 +2524,18 @@ class Base:
         self.o = Enum("o", names)  # E: Enum type as attribute is not supported \
                                    # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
 [builtins fixtures/tuple.pyi]
+
+[case testSingleUnderscoreNameEnumMember]
+# flags: --warn-unreachable
+
+# https://github.com/python/mypy/issues/19271
+from enum import Enum
+
+class Things(Enum):
+    _ = "under score"
+
+def check(thing: Things) -> None:
+    if thing is Things._:
+        return None
+    return None  # E: Statement is unreachable
+[builtins fixtures/enum.pyi]

From 75f7a2579179380b07f8bc4fc56e6b6cb1523647 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 11 Jun 2025 11:22:54 +0100
Subject: [PATCH 1375/1617] [mypyc] Support overriding the group name used in
 output files (#19272)

By default, when compiling more than one file, a shared library
is generated with a file name derived from the sha of compiled modules
(i.e. the group name). This is also used in the names of generated
.c and .h files. Add experimental `group_name` argument to `mypycify`
that allows overriding this. This can be useful when integrating
mypyc to a build system, as this makes the names of output files
more predictable.
---
 mypyc/build.py         | 17 +++++++++++++++--
 mypyc/options.py       |  7 +++++++
 mypyc/test/test_run.py |  2 +-
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/mypyc/build.py b/mypyc/build.py
index 3bc38cb4dd90..ab7ba5393614 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -357,6 +357,7 @@ def construct_groups(
     sources: list[BuildSource],
     separate: bool | list[tuple[list[str], str | None]],
     use_shared_lib: bool,
+    group_name_override: str | None,
 ) -> emitmodule.Groups:
     """Compute Groups given the input source list and separate configs.
 
@@ -386,7 +387,10 @@ def construct_groups(
     # Generate missing names
     for i, (group, name) in enumerate(groups):
         if use_shared_lib and not name:
-            name = group_name([source.module for source in group])
+            if group_name_override is not None:
+                name = group_name_override
+            else:
+                name = group_name([source.module for source in group])
         groups[i] = (group, name)
 
     return groups
@@ -432,7 +436,10 @@ def mypyc_build(
         or always_use_shared_lib
     )
 
-    groups = construct_groups(mypyc_sources, separate, use_shared_lib)
+    groups = construct_groups(mypyc_sources, separate, use_shared_lib, compiler_options.group_name)
+
+    if compiler_options.group_name is not None:
+        assert len(groups) == 1, "If using custom group_name, only one group is expected"
 
     # We let the test harness just pass in the c file contents instead
     # so that it can do a corner-cutting version without full stubs.
@@ -477,6 +484,7 @@ def mypycify(
     target_dir: str | None = None,
     include_runtime_files: bool | None = None,
     strict_dunder_typing: bool = False,
+    group_name: str | None = None,
 ) -> list[Extension]:
     """Main entry point to building using mypyc.
 
@@ -519,6 +527,10 @@ def mypycify(
         strict_dunder_typing: If True, force dunder methods to have the return type
                               of the method strictly, which can lead to more
                               optimization opportunities. Defaults to False.
+        group_name: If set, override the default group name derived from
+                    the hash of module names. This is used for the names of the
+                    output C files and the shared library. This is only supported
+                    if there is a single group. [Experimental]
     """
 
     # Figure out our configuration
@@ -530,6 +542,7 @@ def mypycify(
         target_dir=target_dir,
         include_runtime_files=include_runtime_files,
         strict_dunder_typing=strict_dunder_typing,
+        group_name=group_name,
     )
 
     # Generate all the actual important C code
diff --git a/mypyc/options.py b/mypyc/options.py
index 24e68163bb11..51114926f6b2 100644
--- a/mypyc/options.py
+++ b/mypyc/options.py
@@ -15,6 +15,7 @@ def __init__(
         capi_version: tuple[int, int] | None = None,
         python_version: tuple[int, int] | None = None,
         strict_dunder_typing: bool = False,
+        group_name: str | None = None,
     ) -> None:
         self.strip_asserts = strip_asserts
         self.multi_file = multi_file
@@ -38,3 +39,9 @@ def __init__(
         # will assume the return type of the method strictly, which can lead to
         # more optimization opportunities.
         self.strict_dunders_typing = strict_dunder_typing
+        # Override the automatic group name derived from the hash of module names.
+        # This affects the names of generated .c, .h and shared library files.
+        # This is only supported when compiling exactly one group, and a shared
+        # library is generated (with shims). This can be used to make the output
+        # file names more predictable.
+        self.group_name = group_name
diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py
index e5b7e2421433..b96c4241f30d 100644
--- a/mypyc/test/test_run.py
+++ b/mypyc/test/test_run.py
@@ -235,7 +235,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
             else False
         )
 
-        groups = construct_groups(sources, separate, len(module_names) > 1)
+        groups = construct_groups(sources, separate, len(module_names) > 1, None)
 
         try:
             compiler_options = CompilerOptions(

From 61cbe0c3cada14eb3638de8becff8988ae8cb1db Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 11 Jun 2025 14:32:51 +0100
Subject: [PATCH 1376/1617] Fix couple inconsistencies in protocols vs TypeType
 (#19267)

---
 mypy/checker.py                     |  2 +-
 mypy/messages.py                    | 11 ++++++++---
 mypy/subtypes.py                    |  4 ++--
 test-data/unit/check-protocols.test | 26 ++++++++++++++++++++++++++
 4 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index fc9733117a0a..0639340d30bb 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -7190,7 +7190,7 @@ def check_subtype(
         if (
             isinstance(supertype, Instance)
             and supertype.type.is_protocol
-            and isinstance(subtype, (CallableType, Instance, TupleType, TypedDictType))
+            and isinstance(subtype, (CallableType, Instance, TupleType, TypedDictType, TypeType))
         ):
             self.msg.report_protocol_problems(subtype, supertype, context, parent_error=error)
         if isinstance(supertype, CallableType) and isinstance(subtype, Instance):
diff --git a/mypy/messages.py b/mypy/messages.py
index 46ade80df61d..01414f1c7f2b 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -3090,9 +3090,14 @@ def get_bad_protocol_flags(
     assert right.type.is_protocol
     all_flags: list[tuple[str, set[int], set[int]]] = []
     for member in right.type.protocol_members:
-        if find_member(member, left, left):
-            item = (member, get_member_flags(member, left), get_member_flags(member, right))
-            all_flags.append(item)
+        if find_member(member, left, left, class_obj=class_obj):
+            all_flags.append(
+                (
+                    member,
+                    get_member_flags(member, left, class_obj=class_obj),
+                    get_member_flags(member, right),
+                )
+            )
     bad_flags = []
     for name, subflags, superflags in all_flags:
         if (
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 15c8014c0f3f..acb41609fdc5 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -1319,8 +1319,8 @@ def find_member(
         is_lvalue=is_lvalue,
         is_super=False,
         is_operator=is_operator,
-        original_type=itype,
-        self_type=subtype,
+        original_type=TypeType.make_normalized(itype) if class_obj else itype,
+        self_type=TypeType.make_normalized(subtype) if class_obj else subtype,
         context=Context(),  # all errors are filtered, but this is a required argument
         chk=type_checker,
         suppress_errors=True,
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 4d7f46e5de2b..f330aa4ecc02 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4552,6 +4552,32 @@ t = Test(Mock())
 reveal_type(t)  # N: Revealed type is "__main__.Test[Any]"
 [builtins fixtures/dict.pyi]
 
+[case testProtocolClassObjectDescriptor]
+from typing import Any, Protocol, overload
+
+class Desc:
+    @overload
+    def __get__(self, instance: None, owner: Any) -> Desc: ...
+    @overload
+    def __get__(self, instance: object, owner: Any) -> int: ...
+    def __get__(self, instance, owner):
+        pass
+
+class HasDesc(Protocol):
+    attr: Desc
+
+class HasInt(Protocol):
+    attr: int
+
+class C:
+    attr = Desc()
+
+x: HasInt = C()
+y: HasDesc = C
+z: HasInt = C  # E: Incompatible types in assignment (expression has type "type[C]", variable has type "HasInt") \
+               # N: Following member(s) of "C" have conflicts: \
+               # N:     attr: expected "int", got "Desc"
+
 [case testProtocolErrorReportingNoDuplicates]
 from typing import Callable, Protocol, TypeVar
 

From a7de7ac2e678f00ddda0fd30f3fe1feb846c4d20 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 11 Jun 2025 17:33:31 +0100
Subject: [PATCH 1377/1617] [mypyc] Derive .c file name from full module name
 if using multi_file (#19278)

Don't shorten the module name prefixes, so that the names of output
files are more predictable. This can help if integrating with build
systems, and it arguably also makes it easier to inspect the output
manually.

Now the file name could be `__native_pkg___mod.c` instead of
`__native_mod.c` for the module `pkg.mod`.
---
 mypyc/codegen/emitmodule.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 1ee2ee2aadd8..f914bfd6345d 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -582,7 +582,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
                             fn, emitter, self.source_paths[module_name], module_name
                         )
             if multi_file:
-                name = f"__native_{emitter.names.private_name(module_name)}.c"
+                name = f"__native_{exported_name(module_name)}.c"
                 file_contents.append((name, "".join(emitter.fragments)))
 
         # The external header file contains type declarations while

From 8241059c14f99ad750ae3ac0de6a4795bf990f61 Mon Sep 17 00:00:00 2001
From: johnthagen 
Date: Wed, 11 Jun 2025 15:55:47 -0400
Subject: [PATCH 1378/1617] Fix `exhaustive-match` error code in title (#19276)

Fix docs typo in

- #19144

Reference

- https://github.com/python/mypy/issues/13597#issuecomment-2962338749
---
 docs/source/error_code_list2.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst
index 141aa4490c0b..784c2ad72819 100644
--- a/docs/source/error_code_list2.rst
+++ b/docs/source/error_code_list2.rst
@@ -616,7 +616,7 @@ Example:
 
 .. _code-exhaustive-match:
 
-Check that match statements match exhaustively [match-exhaustive]
+Check that match statements match exhaustively [exhaustive-match]
 -----------------------------------------------------------------------
 
 If enabled with :option:`--enable-error-code exhaustive-match `,

From 6c3c4a7d0b9e1d302d15b85e05f91c5f6a620404 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 13 Jun 2025 17:24:13 +0200
Subject: [PATCH 1379/1617] Bind self-types in checkmember for decorated
 classmethods (#19025)

Fixes #19023. Fixes #18993.
---
 mypy/checkmember.py                |  8 +++++-
 test-data/unit/check-selftype.test | 41 ++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 502251b3960c..0c2c92fc6904 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1230,7 +1230,7 @@ def analyze_class_attribute_access(
             is_trivial_self = node.node.is_trivial_self
         if isinstance(t, FunctionLike) and is_classmethod and not is_trivial_self:
             t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
-        result = add_class_tvars(
+        t = add_class_tvars(
             t,
             isuper,
             is_classmethod,
@@ -1238,6 +1238,12 @@ def analyze_class_attribute_access(
             original_vars=original_vars,
             is_trivial_self=is_trivial_self,
         )
+        if is_decorated and not is_staticmethod:
+            t = expand_self_type_if_needed(
+                t, mx, cast(Decorator, node.node).var, itype, is_class=is_classmethod
+            )
+
+        result = t
         # __set__ is not called on class objects.
         if not mx.is_lvalue:
             result = analyze_descriptor_access(result, mx)
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index cb7e5a9fac71..12d6133ec83f 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2219,3 +2219,44 @@ class B:
 class C(A, B):  # OK: both methods take Self
     pass
 [builtins fixtures/tuple.pyi]
+
+[case testSelfInFuncDecoratedClassmethod]
+from collections.abc import Callable
+from typing import Self, TypeVar
+
+T = TypeVar("T")
+
+def debug(make: Callable[[type[T]], T]) -> Callable[[type[T]], T]:
+    return make
+
+class Foo:
+    @classmethod
+    @debug
+    def make(cls) -> Self:
+        return cls()
+
+class Bar(Foo): ...
+
+reveal_type(Foo.make())  # N: Revealed type is "__main__.Foo"
+reveal_type(Foo().make())  # N: Revealed type is "__main__.Foo"
+reveal_type(Bar.make())  # N: Revealed type is "__main__.Bar"
+reveal_type(Bar().make())  # N: Revealed type is "__main__.Bar"
+[builtins fixtures/tuple.pyi]
+
+[case testSelfInClassDecoratedClassmethod]
+from typing import Callable, Generic, TypeVar, Self
+
+T = TypeVar("T")
+
+class W(Generic[T]):
+    def __init__(self, fn: Callable[..., T]) -> None: ...
+    def __call__(self) -> T: ...
+
+class Check:
+    @W
+    def foo(self) -> Self:
+        ...
+
+reveal_type(Check.foo())  # N: Revealed type is "def () -> __main__.Check"
+reveal_type(Check().foo())  # N: Revealed type is "__main__.Check"
+[builtins fixtures/tuple.pyi]

From 2996c914708d4a4d6896a9a9d6da8a8fdee243ed Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 13 Jun 2025 17:25:30 +0200
Subject: [PATCH 1380/1617] Preserve literals when joining Literal and Instance
 with matching last_known_value (#19279)

Discovered in #19225.
---
 mypy/join.py                      |  2 ++
 test-data/unit/check-literal.test | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/mypy/join.py b/mypy/join.py
index a012a633dfa3..099df02680f0 100644
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -625,6 +625,8 @@ def visit_literal_type(self, t: LiteralType) -> ProperType:
             if self.s.fallback.type.is_enum and t.fallback.type.is_enum:
                 return mypy.typeops.make_simplified_union([self.s, t])
             return join_types(self.s.fallback, t.fallback)
+        elif isinstance(self.s, Instance) and self.s.last_known_value == t:
+            return t
         else:
             return join_types(self.s, t.fallback)
 
diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
index d91b257b0096..f995332643af 100644
--- a/test-data/unit/check-literal.test
+++ b/test-data/unit/check-literal.test
@@ -2976,3 +2976,22 @@ x: Type[Literal[1]]  # E: Type[...] can't contain "Literal[...]"
 y: Type[Union[Literal[1], Literal[2]]]  # E: Type[...] can't contain "Union[Literal[...], Literal[...]]"
 z: Type[Literal[1, 2]]  # E: Type[...] can't contain "Union[Literal[...], Literal[...]]"
 [builtins fixtures/tuple.pyi]
+
+[case testJoinLiteralAndInstance]
+from typing import Generic, TypeVar, Literal
+
+T = TypeVar("T")
+
+class A(Generic[T]): ...
+
+def f(a: A[T], t: T) -> T: ...
+def g(a: T, t: A[T]) -> T: ...
+
+def check(obj: A[Literal[1]]) -> None:
+    reveal_type(f(obj, 1))  # N: Revealed type is "Literal[1]"
+    reveal_type(f(obj, ''))  # E: Cannot infer type argument 1 of "f" \
+                             # N: Revealed type is "Any"
+    reveal_type(g(1, obj))  # N: Revealed type is "Literal[1]"
+    reveal_type(g('', obj))  # E: Cannot infer type argument 1 of "g" \
+                             # N: Revealed type is "Any"
+[builtins fixtures/tuple.pyi]

From 2ba79cba94c8a416c16877c1532932662ea20d40 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 13 Jun 2025 17:36:36 +0200
Subject: [PATCH 1381/1617] Use union of current context and left side for
 right side narrowing of binary ops (#19249)

Fixes #12001. Fixes #6898. Fixes #15368. Improves #17790 and #11508.

When encountering `a {and,or} b`, we used to use `a` as primary context
for `b` inference. This results in weird errors when `a` and `b` are
completely unrelated, and in many cases return type/assignment type
context can do much better. Inferring to union should be harmless in
most cases, so use union of `a` and current context instead.
---
 mypy/checkexpr.py                           | 14 +++++++++++++-
 test-data/unit/check-inference-context.test | 21 +++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index edc3ac70fa54..4ca55e1679e4 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -4288,7 +4288,9 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
         ):
             self.msg.unreachable_right_operand(e.op, e.right)
 
-        right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type)
+        right_type = self.analyze_cond_branch(
+            right_map, e.right, self._combined_context(expanded_left_type)
+        )
 
         if left_map is None and right_map is None:
             return UninhabitedType()
@@ -5919,6 +5921,16 @@ def analyze_cond_branch(
             self.chk.push_type_map(map)
             return self.accept(node, type_context=context, allow_none_return=allow_none_return)
 
+    def _combined_context(self, ty: Type | None) -> Type | None:
+        ctx_items = []
+        if ty is not None:
+            ctx_items.append(ty)
+        if self.type_context and self.type_context[-1] is not None:
+            ctx_items.append(self.type_context[-1])
+        if ctx_items:
+            return make_simplified_union(ctx_items)
+        return None
+
     #
     # Helpers
     #
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index 0aa67b2bf7f3..67ae22a369b1 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -1510,3 +1510,24 @@ def mymin(
 def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]:
     return mymin(paths, key=key, default=None)
 [builtins fixtures/tuple.pyi]
+
+[case testBinaryOpInferenceContext]
+from typing import Literal, TypeVar
+
+T = TypeVar("T")
+
+def identity(x: T) -> T:
+    return x
+
+def check1(use: bool, val: str) -> "str | Literal[True]":
+    return use or identity(val)
+
+def check2(use: bool, val: str) -> "str | bool":
+    return use or identity(val)
+
+def check3(use: bool, val: str) -> "str | Literal[False]":
+    return use and identity(val)
+
+def check4(use: bool, val: str) -> "str | bool":
+    return use and identity(val)
+[builtins fixtures/tuple.pyi]

From 4373e057219f4a8f511eeb8678b2863d532885e1 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 13 Jun 2025 17:27:37 +0100
Subject: [PATCH 1382/1617] Fix tests in master (#19293)

---
 mypy/checkmember.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 0c2c92fc6904..1633eaf52983 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1221,6 +1221,9 @@ def analyze_class_attribute_access(
         is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
             isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class
         )
+        is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
+            isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
+        )
         t = get_proper_type(t)
         is_trivial_self = False
         if isinstance(node.node, Decorator):

From 5081c59b9c0c7ebe7070c62a4aeaf3d0de203a24 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 14 Jun 2025 00:21:35 +0100
Subject: [PATCH 1383/1617] Handle corner case: protocol vs classvar vs
 descriptor (#19277)

Ref https://github.com/python/mypy/issues/19274

This is a bit ugly. But I propose to have this "hot-fix" until we have a
proper overhaul of instance vs class variables. To be clear: attribute
access already works correctly (on both `P` and `Type[P]`), but
subtyping returns false because of
```python
                elif (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags):
                    return False
```
---
 docs/source/protocols.rst           | 47 +++++++++++++++++++++++++++++
 mypy/subtypes.py                    | 12 +++++++-
 test-data/unit/check-protocols.test | 44 +++++++++++++++++++++++++++
 3 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst
index ed8d94f62ef1..258cd4b0de56 100644
--- a/docs/source/protocols.rst
+++ b/docs/source/protocols.rst
@@ -352,6 +352,53 @@ the parameters are positional-only. Example (using the legacy syntax for generic
    copy_a = copy_b  # OK
    copy_b = copy_a  # Also OK
 
+Binding of types in protocol attributes
+***************************************
+
+All protocol attributes annotations are treated as externally visible types
+of those attributes. This means that for example callables are not bound,
+and descriptors are not invoked:
+
+.. code-block:: python
+
+   from typing import Callable, Protocol, overload
+
+   class Integer:
+       @overload
+       def __get__(self, instance: None, owner: object) -> Integer: ...
+       @overload
+       def __get__(self, instance: object, owner: object) -> int: ...
+       # 
+
+   class Example(Protocol):
+       foo: Callable[[object], int]
+       bar: Integer
+
+   ex: Example
+   reveal_type(ex.foo)  # Revealed type is Callable[[object], int]
+   reveal_type(ex.bar)  # Revealed type is Integer
+
+In other words, protocol attribute types are handled as they would appear in a
+``self`` attribute annotation in a regular class. If you want some protocol
+attributes to be handled as though they were defined at class level, you should
+declare them explicitly using ``ClassVar[...]``. Continuing previous example:
+
+.. code-block:: python
+
+   from typing import ClassVar
+
+   class OtherExample(Protocol):
+       # This style is *not recommended*, but may be needed to reuse
+       # some complex callable types. Otherwise use regular methods.
+       foo: ClassVar[Callable[[object], int]]
+       # This may be needed to mimic descriptor access on Type[...] types,
+       # otherwise use a plain "bar: int" style.
+       bar: ClassVar[Integer]
+
+   ex2: OtherExample
+   reveal_type(ex2.foo)  # Revealed type is Callable[[], int]
+   reveal_type(ex2.bar)  # Revealed type is int
+
 .. _predefined_protocols_reference:
 
 Predefined protocol reference
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index acb41609fdc5..a5e6938615e7 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -1457,7 +1457,8 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set
         flags = {IS_VAR}
         if not v.is_final:
             flags.add(IS_SETTABLE)
-        if v.is_classvar:
+        # TODO: define cleaner rules for class vs instance variables.
+        if v.is_classvar and not is_descriptor(v.type):
             flags.add(IS_CLASSVAR)
         if class_obj and v.is_inferred:
             flags.add(IS_CLASSVAR)
@@ -1465,6 +1466,15 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set
     return set()
 
 
+def is_descriptor(typ: Type | None) -> bool:
+    typ = get_proper_type(typ)
+    if isinstance(typ, Instance):
+        return typ.type.get("__get__") is not None
+    if isinstance(typ, UnionType):
+        return all(is_descriptor(item) for item in typ.relevant_items())
+    return False
+
+
 def find_node_type(
     node: Var | FuncBase,
     itype: Instance,
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index f330aa4ecc02..c6c2c5f8da98 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4602,3 +4602,47 @@ def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ...
 @deco
 def defer() -> int: ...
 [builtins fixtures/list.pyi]
+
+[case testProtocolClassValDescriptor]
+from typing import Any, Protocol, overload, ClassVar, Type
+
+class Desc:
+    @overload
+    def __get__(self, instance: None, owner: object) -> Desc: ...
+    @overload
+    def __get__(self, instance: object, owner: object) -> int: ...
+    def __get__(self, instance, owner):
+        pass
+
+class P(Protocol):
+    x: ClassVar[Desc]
+
+class C:
+    x = Desc()
+
+t: P = C()
+reveal_type(t.x)  # N: Revealed type is "builtins.int"
+tt: Type[P] = C
+reveal_type(tt.x)  # N: Revealed type is "__main__.Desc"
+
+bad: P = C  # E: Incompatible types in assignment (expression has type "type[C]", variable has type "P") \
+            # N: Following member(s) of "C" have conflicts: \
+            # N:     x: expected "int", got "Desc"
+
+[case testProtocolClassValCallable]
+from typing import Any, Protocol, overload, ClassVar, Type, Callable
+
+class P(Protocol):
+    foo: Callable[[object], int]
+    bar: ClassVar[Callable[[object], int]]
+
+class C:
+    foo: Callable[[object], int]
+    bar: ClassVar[Callable[[object], int]]
+
+t: P = C()
+reveal_type(t.foo)  # N: Revealed type is "def (builtins.object) -> builtins.int"
+reveal_type(t.bar)  # N: Revealed type is "def () -> builtins.int"
+tt: Type[P] = C
+reveal_type(tt.foo)  # N: Revealed type is "def (builtins.object) -> builtins.int"
+reveal_type(tt.bar)  # N: Revealed type is "def (builtins.object) -> builtins.int"

From 28f06004745049d81d8803e3f893f694d8a25ed6 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 16 Jun 2025 23:59:15 +0100
Subject: [PATCH 1384/1617] Support properties with generic setters (#19298)

This is yet another followup for `checkmember` work.

This handles a niche use case, but it is still used in the wild, and
this restores parity between logic for descriptors with `__set__()` and
properties with custom setters.
---
 mypy/checkmember.py                |  8 +++++++-
 test-data/unit/check-generics.test | 29 +++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 1633eaf52983..8f62fee699c0 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -968,7 +968,13 @@ def expand_and_bind_callable(
     # TODO: a decorated property can result in Overloaded here.
     assert isinstance(expanded, CallableType)
     if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
-        # TODO: use check_call() to infer better type, same as for __set__().
+        if expanded.variables:
+            type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context)
+            _, inferred_expanded = mx.chk.expr_checker.check_call(
+                expanded, [type_ctx], [ARG_POS], mx.context
+            )
+            expanded = get_proper_type(inferred_expanded)
+            assert isinstance(expanded, CallableType)
         if not expanded.arg_types:
             # This can happen when accessing invalid property from its own body,
             # error will be reported elsewhere.
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 809c3c4eca48..8839dfb954f4 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -3628,3 +3628,32 @@ def draw_none(
         takes_int_str_none(c2)
         takes_int_str_none(c3)
 [builtins fixtures/tuple.pyi]
+
+[case testPropertyWithGenericSetter]
+from typing import TypeVar
+
+class B: ...
+class C(B): ...
+T = TypeVar("T", bound=B)
+
+class Test:
+    @property
+    def foo(self) -> list[C]: ...
+    @foo.setter
+    def foo(self, val: list[T]) -> None: ...
+
+t1: Test
+t2: Test
+
+lb: list[B]
+lc: list[C]
+li: list[int]
+
+t1.foo = lb
+t1.foo = lc
+t1.foo = li  # E: Value of type variable "T" of "foo" of "Test" cannot be "int"
+
+t2.foo = [B()]
+t2.foo = [C()]
+t2.foo = [1]  # E: Value of type variable "T" of "foo" of "Test" cannot be "int"
+[builtins fixtures/property.pyi]

From ed88d823f1fd2c572d8600d2c461bff1a86237c1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 17 Jun 2025 10:56:08 +0100
Subject: [PATCH 1385/1617] Skip existing when retrying upload-pypi.py (#19305)

The upload script has been pretty flaky for me. Now rerunning skips the
wheels that have already been uploaded, so the upload should always
eventually finish after enough retries.

Work on #19174.
---
 misc/upload-pypi.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py
index c9db475c14b4..8ea86bbea584 100644
--- a/misc/upload-pypi.py
+++ b/misc/upload-pypi.py
@@ -108,7 +108,7 @@ def tmp_twine() -> Iterator[Path]:
 def upload_dist(dist: Path, dry_run: bool = True) -> None:
     with tmp_twine() as twine:
         files = [item for item in dist.iterdir() if item_ok_for_pypi(item.name)]
-        cmd: list[Any] = [twine, "upload"]
+        cmd: list[Any] = [twine, "upload", "--skip-existing"]
         cmd += files
         if dry_run:
             print("[dry run] " + " ".join(map(str, cmd)))

From 4cda52d4be08d52d0aec741dc7c0812b50136ec6 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 18 Jun 2025 19:40:00 +0200
Subject: [PATCH 1386/1617] Include tuple fallback in constraints built from
 tuple types (#19100)

Fixes #19093.
---
 mypy/constraints.py                     |  5 +++++
 test-data/unit/check-typevar-tuple.test | 16 ++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/mypy/constraints.py b/mypy/constraints.py
index 293618556203..9eeea3cb2c26 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -1335,6 +1335,11 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]:
                     res.extend(
                         infer_constraints(template_items[i], actual_items[i], self.direction)
                     )
+            res.extend(
+                infer_constraints(
+                    template.partial_fallback, actual.partial_fallback, self.direction
+                )
+            )
             return res
         elif isinstance(actual, AnyType):
             return self.infer_against_any(template.items, actual)
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 0f69d0a56f47..862fd9ff5fb0 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -2628,3 +2628,19 @@ def fn(f: Callable[[*tuple[T]], int]) -> Callable[[*tuple[T]], int]: ...
 def test(*args: Unpack[tuple[T]]) -> int: ...
 reveal_type(fn(test))  # N: Revealed type is "def [T] (T`1) -> builtins.int"
 [builtins fixtures/tuple.pyi]
+
+[case testConstraintsIncludeTupleFallback]
+from typing import Generic, TypeVar
+from typing_extensions import TypeVarTuple, Unpack
+
+T = TypeVar("T")
+Ts = TypeVarTuple("Ts")
+_FT = TypeVar("_FT", bound=type)
+
+def identity(smth: _FT) -> _FT:
+    return smth
+
+@identity
+class S(tuple[Unpack[Ts]], Generic[T, Unpack[Ts]]):
+    def f(self, x: T, /) -> T: ...
+[builtins fixtures/tuple.pyi]

From ce5f12726f1555be2de3794c740235cc2492f50f Mon Sep 17 00:00:00 2001
From: Anthony Sottile 
Date: Wed, 18 Jun 2025 13:43:03 -0400
Subject: [PATCH 1387/1617] support Callable / callable Protocols in suggest
 decorator unwarpping (#19072)

Resolves #18940

while I feel like just accepting any identity function would be good
enough I expanded the check to explicitly allow Callables and Protocol
callables

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypy/suggestions.py                      | 14 ++++++++++-
 test-data/unit/fine-grained-suggest.test | 32 ++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index a662dd7b98e9..673076729ffa 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -229,6 +229,18 @@ def is_implicit_any(typ: Type) -> bool:
     return isinstance(typ, AnyType) and not is_explicit_any(typ)
 
 
+def _arg_accepts_function(typ: ProperType) -> bool:
+    return (
+        # TypeVar / Callable
+        isinstance(typ, (TypeVarType, CallableType))
+        or
+        # Protocol with __call__
+        isinstance(typ, Instance)
+        and typ.type.is_protocol
+        and typ.type.get_method("__call__") is not None
+    )
+
+
 class SuggestionEngine:
     """Engine for finding call sites and suggesting signatures."""
 
@@ -658,7 +670,7 @@ def extract_from_decorator(self, node: Decorator) -> FuncDef | None:
             for ct in typ.items:
                 if not (
                     len(ct.arg_types) == 1
-                    and isinstance(ct.arg_types[0], TypeVarType)
+                    and _arg_accepts_function(get_proper_type(ct.arg_types[0]))
                     and ct.arg_types[0] == ct.ret_type
                 ):
                     return None
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index 7034b5e48943..f2db85c05f18 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -651,6 +651,38 @@ foo3('hello hello')
 (str) -> str
 ==
 
+[case testSuggestInferFuncDecorator6]
+# suggest: foo.f
+[file foo.py]
+from __future__ import annotations
+
+from typing import Callable, Protocol, TypeVar
+from typing_extensions import ParamSpec
+
+P = ParamSpec('P')
+R = TypeVar('R')
+R_co = TypeVar('R_co', covariant=True)
+
+class Proto(Protocol[P, R_co]):
+    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R_co: ...
+
+def dec1(f: Callable[P, R]) -> Callable[P, R]: ...
+def dec2(f: Callable[..., R]) -> Callable[..., R]: ...
+def dec3(f: Proto[P, R_co]) -> Proto[P, R_co]: ...
+
+@dec1
+@dec2
+@dec3
+def f(x):
+    return x
+
+f('hi')
+
+[builtins fixtures/isinstancelist.pyi]
+[out]
+(str) -> str
+==
+
 [case testSuggestFlexAny1]
 # suggest: --flex-any=0.4 m.foo
 # suggest: --flex-any=0.7 m.foo

From 5c196d1e3801ba57bac3461882ea318fa1ada7e9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 18 Jun 2025 10:50:12 -0700
Subject: [PATCH 1388/1617] Sync typeshed (#19301)

Source commit:

https://github.com/python/typeshed/commit/ecd5141cc036366cc9e3ca371096d6a14b0ccd13
---
 mypy/typeshed/stdlib/VERSIONS                 |   1 +
 mypy/typeshed/stdlib/_hashlib.pyi             |  70 +++++++++--
 mypy/typeshed/stdlib/_socket.pyi              |   3 +-
 mypy/typeshed/stdlib/_zstd.pyi                |  96 ++++++++++++++
 mypy/typeshed/stdlib/asyncio/tasks.pyi        |  26 ++--
 mypy/typeshed/stdlib/bz2.pyi                  |  11 +-
 .../stdlib/compression/_common/_streams.pyi   |   3 +-
 .../stdlib/compression/zstd/__init__.pyi      |  87 +++++++++++++
 .../stdlib/compression/zstd/_zstdfile.pyi     | 117 ++++++++++++++++++
 .../stdlib/email/_header_value_parser.pyi     |   7 +-
 mypy/typeshed/stdlib/fractions.pyi            |  35 ++++--
 mypy/typeshed/stdlib/genericpath.pyi          |   7 +-
 mypy/typeshed/stdlib/gzip.pyi                 |  13 +-
 mypy/typeshed/stdlib/lzma.pyi                 |   9 +-
 mypy/typeshed/stdlib/ntpath.pyi               |  17 ++-
 mypy/typeshed/stdlib/posixpath.pyi            |  20 ++-
 mypy/typeshed/stdlib/socket.pyi               |   4 +
 mypy/typeshed/stdlib/tarfile.pyi              |  17 ++-
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |   4 +-
 mypy/typeshed/stdlib/typing_extensions.pyi    |   2 +-
 mypy/typeshed/stdlib/zoneinfo/__init__.pyi    |  10 +-
 21 files changed, 482 insertions(+), 77 deletions(-)
 create mode 100644 mypy/typeshed/stdlib/_zstd.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/zstd/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi

diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS
index 1ecd8af64559..c86bbb314667 100644
--- a/mypy/typeshed/stdlib/VERSIONS
+++ b/mypy/typeshed/stdlib/VERSIONS
@@ -76,6 +76,7 @@ _warnings: 3.0-
 _weakref: 3.0-
 _weakrefset: 3.0-
 _winapi: 3.3-
+_zstd: 3.14-
 abc: 3.0-
 aifc: 3.0-3.12
 annotationlib: 3.14-
diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi
index 746b1657e2db..8b7ef52cdffd 100644
--- a/mypy/typeshed/stdlib/_hashlib.pyi
+++ b/mypy/typeshed/stdlib/_hashlib.pyi
@@ -60,19 +60,63 @@ def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
 def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
 def get_fips_mode() -> int: ...
 def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ...
-def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
-def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
-def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
+
+if sys.version_info >= (3, 13):
+    def new(
+        name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_md5(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha1(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha224(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha256(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha384(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha512(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha3_224(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha3_256(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha3_384(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_sha3_512(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASH: ...
+    def openssl_shake_128(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASHXOF: ...
+    def openssl_shake_256(
+        data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None
+    ) -> HASHXOF: ...
+
+else:
+    def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
+    def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
+    def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
+
 def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ...
 def pbkdf2_hmac(
     hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None
diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi
index 06a8a2ba5fa0..41fdce87ec14 100644
--- a/mypy/typeshed/stdlib/_socket.pyi
+++ b/mypy/typeshed/stdlib/_socket.pyi
@@ -229,6 +229,8 @@ if sys.platform != "win32":
     IP_RECVOPTS: int
     IP_RECVRETOPTS: int
     IP_RETOPTS: int
+if sys.version_info >= (3, 13) and sys.platform == "linux":
+    CAN_RAW_ERR_FILTER: int
 if sys.version_info >= (3, 14):
     IP_RECVTTL: int
 
@@ -246,7 +248,6 @@ if sys.version_info >= (3, 14):
         TCP_QUICKACK: int
 
     if sys.platform == "linux":
-        CAN_RAW_ERR_FILTER: int
         IP_FREEBIND: int
         IP_RECVORIGDSTADDR: int
         VMADDR_CID_LOCAL: int
diff --git a/mypy/typeshed/stdlib/_zstd.pyi b/mypy/typeshed/stdlib/_zstd.pyi
new file mode 100644
index 000000000000..0648d898448b
--- /dev/null
+++ b/mypy/typeshed/stdlib/_zstd.pyi
@@ -0,0 +1,96 @@
+from _typeshed import ReadableBuffer
+from collections.abc import Mapping
+from compression.zstd import CompressionParameter, DecompressionParameter
+from typing import Final, Literal, final
+from typing_extensions import Self, TypeAlias
+
+ZSTD_CLEVEL_DEFAULT: Final = 3
+ZSTD_DStreamOutSize: Final = 131072
+ZSTD_btlazy2: Final = 6
+ZSTD_btopt: Final = 7
+ZSTD_btultra: Final = 8
+ZSTD_btultra2: Final = 9
+ZSTD_c_chainLog: Final = 103
+ZSTD_c_checksumFlag: Final = 201
+ZSTD_c_compressionLevel: Final = 100
+ZSTD_c_contentSizeFlag: Final = 200
+ZSTD_c_dictIDFlag: Final = 202
+ZSTD_c_enableLongDistanceMatching: Final = 160
+ZSTD_c_hashLog: Final = 102
+ZSTD_c_jobSize: Final = 401
+ZSTD_c_ldmBucketSizeLog: Final = 163
+ZSTD_c_ldmHashLog: Final = 161
+ZSTD_c_ldmHashRateLog: Final = 164
+ZSTD_c_ldmMinMatch: Final = 162
+ZSTD_c_minMatch: Final = 105
+ZSTD_c_nbWorkers: Final = 400
+ZSTD_c_overlapLog: Final = 402
+ZSTD_c_searchLog: Final = 104
+ZSTD_c_strategy: Final = 107
+ZSTD_c_targetLength: Final = 106
+ZSTD_c_windowLog: Final = 101
+ZSTD_d_windowLogMax: Final = 100
+ZSTD_dfast: Final = 2
+ZSTD_fast: Final = 1
+ZSTD_greedy: Final = 3
+ZSTD_lazy: Final = 4
+ZSTD_lazy2: Final = 5
+
+_ZstdCompressorContinue: TypeAlias = Literal[0]
+_ZstdCompressorFlushBlock: TypeAlias = Literal[1]
+_ZstdCompressorFlushFrame: TypeAlias = Literal[2]
+
+@final
+class ZstdCompressor:
+    CONTINUE: Final = 0
+    FLUSH_BLOCK: Final = 1
+    FLUSH_FRAME: Final = 2
+    def __init__(
+        self, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
+    ) -> None: ...
+    def compress(
+        self, /, data: ReadableBuffer, mode: _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 0
+    ) -> bytes: ...
+    def flush(self, /, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 2) -> bytes: ...
+    @property
+    def last_mode(self) -> _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame: ...
+
+@final
+class ZstdDecompressor:
+    def __init__(self, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> None: ...
+    def decompress(self, /, data: ReadableBuffer, max_length: int = -1) -> bytes: ...
+    @property
+    def eof(self) -> bool: ...
+    @property
+    def needs_input(self) -> bool: ...
+    @property
+    def unused_data(self) -> bytes: ...
+
+@final
+class ZstdDict:
+    def __init__(self, dict_content: bytes, /, *, is_raw: bool = False) -> None: ...
+    def __len__(self, /) -> int: ...
+    @property
+    def as_digested_dict(self) -> tuple[Self, int]: ...
+    @property
+    def as_prefix(self) -> tuple[Self, int]: ...
+    @property
+    def as_undigested_dict(self) -> tuple[Self, int]: ...
+    @property
+    def dict_content(self) -> bytes: ...
+    @property
+    def dict_id(self) -> int: ...
+
+class ZstdError(Exception): ...
+
+def finalize_dict(
+    custom_dict_bytes: bytes, samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, compression_level: int, /
+) -> bytes: ...
+def get_frame_info(frame_buffer: ReadableBuffer) -> tuple[int, int]: ...
+def get_frame_size(frame_buffer: ReadableBuffer) -> int: ...
+def get_param_bounds(parameter: int, is_compress: bool) -> tuple[int, int]: ...
+def set_parameter_types(c_parameter_type: type[CompressionParameter], d_parameter_type: type[DecompressionParameter]) -> None: ...
+def train_dict(samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, /) -> bytes: ...
+
+zstd_version: Final[str]
+zstd_version_number: Final[int]
diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi
index e42151213e69..a088e95af653 100644
--- a/mypy/typeshed/stdlib/asyncio/tasks.pyi
+++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi
@@ -423,6 +423,25 @@ if sys.version_info >= (3, 12):
 else:
     def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ...
 
+if sys.version_info >= (3, 14):
+    def eager_task_factory(
+        loop: AbstractEventLoop | None,
+        coro: _TaskCompatibleCoro[_T_co],
+        *,
+        name: str | None = None,
+        context: Context | None = None,
+        eager_start: bool = True,
+    ) -> Task[_T_co]: ...
+
+elif sys.version_info >= (3, 12):
+    def eager_task_factory(
+        loop: AbstractEventLoop | None,
+        coro: _TaskCompatibleCoro[_T_co],
+        *,
+        name: str | None = None,
+        context: Context | None = None,
+    ) -> Task[_T_co]: ...
+
 if sys.version_info >= (3, 12):
     _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True)
 
@@ -451,10 +470,3 @@ if sys.version_info >= (3, 12):
     def create_eager_task_factory(
         custom_task_constructor: _CustomTaskConstructor[_TaskT_co],
     ) -> _EagerTaskFactoryType[_TaskT_co]: ...
-    def eager_task_factory(
-        loop: AbstractEventLoop | None,
-        coro: _TaskCompatibleCoro[_T_co],
-        *,
-        name: str | None = None,
-        context: Context | None = None,
-    ) -> Task[_T_co]: ...
diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi
index 0f9d00fbc633..dce6187a2da1 100644
--- a/mypy/typeshed/stdlib/bz2.pyi
+++ b/mypy/typeshed/stdlib/bz2.pyi
@@ -2,7 +2,8 @@ import sys
 from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor
 from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from collections.abc import Iterable
-from typing import IO, Literal, Protocol, SupportsIndex, TextIO, overload
+from io import TextIOWrapper
+from typing import IO, Literal, Protocol, SupportsIndex, overload
 from typing_extensions import Self, TypeAlias
 
 if sys.version_info >= (3, 14):
@@ -48,7 +49,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> TextIO: ...
+) -> TextIOWrapper: ...
 @overload
 def open(
     filename: _WritableFileobj,
@@ -66,7 +67,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> TextIO: ...
+) -> TextIOWrapper: ...
 @overload
 def open(
     filename: StrOrBytesPath,
@@ -84,7 +85,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> TextIO: ...
+) -> TextIOWrapper: ...
 @overload
 def open(
     filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj,
@@ -93,7 +94,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> BZ2File | TextIO: ...
+) -> BZ2File | TextIOWrapper: ...
 
 class BZ2File(BaseStream, IO[bytes]):
     def __enter__(self) -> Self: ...
diff --git a/mypy/typeshed/stdlib/compression/_common/_streams.pyi b/mypy/typeshed/stdlib/compression/_common/_streams.pyi
index 6303a9b1d460..b8463973ec67 100644
--- a/mypy/typeshed/stdlib/compression/_common/_streams.pyi
+++ b/mypy/typeshed/stdlib/compression/_common/_streams.pyi
@@ -1,10 +1,11 @@
 from _typeshed import Incomplete, WriteableBuffer
 from collections.abc import Callable
 from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
-from typing import Any, Protocol
+from typing import Any, Protocol, type_check_only
 
 BUFFER_SIZE = DEFAULT_BUFFER_SIZE
 
+@type_check_only
 class _Reader(Protocol):
     def read(self, n: int, /) -> bytes: ...
     def seekable(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/compression/zstd/__init__.pyi b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi
new file mode 100644
index 000000000000..24a9633c488e
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi
@@ -0,0 +1,87 @@
+import enum
+from _typeshed import ReadableBuffer
+from collections.abc import Iterable, Mapping
+from compression.zstd._zstdfile import ZstdFile, open
+from typing import Final, final
+
+import _zstd
+from _zstd import ZstdCompressor, ZstdDecompressor, ZstdDict, ZstdError, get_frame_size, zstd_version
+
+__all__ = (
+    # compression.zstd
+    "COMPRESSION_LEVEL_DEFAULT",
+    "compress",
+    "CompressionParameter",
+    "decompress",
+    "DecompressionParameter",
+    "finalize_dict",
+    "get_frame_info",
+    "Strategy",
+    "train_dict",
+    # compression.zstd._zstdfile
+    "open",
+    "ZstdFile",
+    # _zstd
+    "get_frame_size",
+    "zstd_version",
+    "zstd_version_info",
+    "ZstdCompressor",
+    "ZstdDecompressor",
+    "ZstdDict",
+    "ZstdError",
+)
+
+zstd_version_info: Final[tuple[int, int, int]]
+COMPRESSION_LEVEL_DEFAULT: Final = _zstd.ZSTD_CLEVEL_DEFAULT
+
+class FrameInfo:
+    decompressed_size: int
+    dictionary_id: int
+    def __init__(self, decompressed_size: int, dictionary_id: int) -> None: ...
+
+def get_frame_info(frame_buffer: ReadableBuffer) -> FrameInfo: ...
+def train_dict(samples: Iterable[ReadableBuffer], dict_size: int) -> ZstdDict: ...
+def finalize_dict(zstd_dict: ZstdDict, /, samples: Iterable[ReadableBuffer], dict_size: int, level: int) -> ZstdDict: ...
+def compress(
+    data: ReadableBuffer, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
+) -> bytes: ...
+def decompress(data: ReadableBuffer, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> bytes: ...
+@final
+class CompressionParameter(enum.IntEnum):
+    compression_level = _zstd.ZSTD_c_compressionLevel
+    window_log = _zstd.ZSTD_c_windowLog
+    hash_log = _zstd.ZSTD_c_hashLog
+    chain_log = _zstd.ZSTD_c_chainLog
+    search_log = _zstd.ZSTD_c_searchLog
+    min_match = _zstd.ZSTD_c_minMatch
+    target_length = _zstd.ZSTD_c_targetLength
+    strategy = _zstd.ZSTD_c_strategy
+    enable_long_distance_matching = _zstd.ZSTD_c_enableLongDistanceMatching
+    ldm_hash_log = _zstd.ZSTD_c_ldmHashLog
+    ldm_min_match = _zstd.ZSTD_c_ldmMinMatch
+    ldm_bucket_size_log = _zstd.ZSTD_c_ldmBucketSizeLog
+    ldm_hash_rate_log = _zstd.ZSTD_c_ldmHashRateLog
+    content_size_flag = _zstd.ZSTD_c_contentSizeFlag
+    checksum_flag = _zstd.ZSTD_c_checksumFlag
+    dict_id_flag = _zstd.ZSTD_c_dictIDFlag
+    nb_workers = _zstd.ZSTD_c_nbWorkers
+    job_size = _zstd.ZSTD_c_jobSize
+    overlap_log = _zstd.ZSTD_c_overlapLog
+    def bounds(self) -> tuple[int, int]: ...
+
+@final
+class DecompressionParameter(enum.IntEnum):
+    window_log_max = _zstd.ZSTD_d_windowLogMax
+    def bounds(self) -> tuple[int, int]: ...
+
+@final
+class Strategy(enum.IntEnum):
+    fast = _zstd.ZSTD_fast
+    dfast = _zstd.ZSTD_dfast
+    greedy = _zstd.ZSTD_greedy
+    lazy = _zstd.ZSTD_lazy
+    lazy2 = _zstd.ZSTD_lazy2
+    btlazy2 = _zstd.ZSTD_btlazy2
+    btopt = _zstd.ZSTD_btopt
+    btultra = _zstd.ZSTD_btultra
+    btultra2 = _zstd.ZSTD_btultra2
diff --git a/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi
new file mode 100644
index 000000000000..045b2d35acfe
--- /dev/null
+++ b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi
@@ -0,0 +1,117 @@
+from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsWrite, WriteableBuffer
+from collections.abc import Mapping
+from compression._common import _streams
+from compression.zstd import ZstdDict
+from io import TextIOWrapper, _WrappedBuffer
+from typing import Literal, overload, type_check_only
+from typing_extensions import TypeAlias
+
+from _zstd import ZstdCompressor, _ZstdCompressorFlushBlock, _ZstdCompressorFlushFrame
+
+__all__ = ("ZstdFile", "open")
+
+_ReadBinaryMode: TypeAlias = Literal["r", "rb"]
+_WriteBinaryMode: TypeAlias = Literal["w", "wb", "x", "xb", "a", "ab"]
+_ReadTextMode: TypeAlias = Literal["rt"]
+_WriteTextMode: TypeAlias = Literal["wt", "xt", "at"]
+
+@type_check_only
+class _FileBinaryRead(_streams._Reader):
+    def close(self) -> None: ...
+
+@type_check_only
+class _FileBinaryWrite(SupportsWrite[bytes]):
+    def close(self) -> None: ...
+
+class ZstdFile(_streams.BaseStream):
+    FLUSH_BLOCK = ZstdCompressor.FLUSH_BLOCK
+    FLUSH_FRAME = ZstdCompressor.FLUSH_FRAME
+
+    @overload
+    def __init__(
+        self,
+        file: StrOrBytesPath | _FileBinaryRead,
+        /,
+        mode: _ReadBinaryMode = "r",
+        *,
+        level: None = None,
+        options: Mapping[int, int] | None = None,
+        zstd_dict: ZstdDict | None = None,
+    ) -> None: ...
+    @overload
+    def __init__(
+        self,
+        file: StrOrBytesPath | _FileBinaryWrite,
+        /,
+        mode: _WriteBinaryMode,
+        *,
+        level: int | None = None,
+        options: Mapping[int, int] | None = None,
+        zstd_dict: ZstdDict | None = None,
+    ) -> None: ...
+    def write(self, data: ReadableBuffer, /) -> int: ...
+    def flush(self, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 1) -> bytes: ...  # type: ignore[override]
+    def read(self, size: int | None = -1) -> bytes: ...
+    def read1(self, size: int | None = -1) -> bytes: ...
+    def readinto(self, b: WriteableBuffer) -> int: ...
+    def readinto1(self, b: WriteableBuffer) -> int: ...
+    def readline(self, size: int | None = -1) -> bytes: ...
+    def seek(self, offset: int, whence: int = 0) -> int: ...
+    def peek(self, size: int = -1) -> bytes: ...
+    @property
+    def name(self) -> str | bytes: ...
+    @property
+    def mode(self) -> Literal["rb", "wb"]: ...
+
+@overload
+def open(
+    file: StrOrBytesPath | _FileBinaryRead,
+    /,
+    mode: _ReadBinaryMode = "rb",
+    *,
+    level: None = None,
+    options: Mapping[int, int] | None = None,
+    zstd_dict: ZstdDict | None = None,
+    encoding: str | None = None,
+    errors: str | None = None,
+    newline: str | None = None,
+) -> ZstdFile: ...
+@overload
+def open(
+    file: StrOrBytesPath | _FileBinaryWrite,
+    /,
+    mode: _WriteBinaryMode,
+    *,
+    level: int | None = None,
+    options: Mapping[int, int] | None = None,
+    zstd_dict: ZstdDict | None = None,
+    encoding: str | None = None,
+    errors: str | None = None,
+    newline: str | None = None,
+) -> ZstdFile: ...
+@overload
+def open(
+    file: StrOrBytesPath | _WrappedBuffer,
+    /,
+    mode: _ReadTextMode,
+    *,
+    level: None = None,
+    options: Mapping[int, int] | None = None,
+    zstd_dict: ZstdDict | None = None,
+    encoding: str | None = None,
+    errors: str | None = None,
+    newline: str | None = None,
+) -> TextIOWrapper: ...
+@overload
+def open(
+    file: StrOrBytesPath | _WrappedBuffer,
+    /,
+    mode: _WriteTextMode,
+    *,
+    level: int | None = None,
+    options: Mapping[int, int] | None = None,
+    zstd_dict: ZstdDict | None = None,
+    encoding: str | None = None,
+    errors: str | None = None,
+    newline: str | None = None,
+) -> TextIOWrapper: ...
diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
index a8abfead9217..95ada186c4ec 100644
--- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi
+++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
@@ -1,4 +1,3 @@
-import sys
 from collections.abc import Iterable, Iterator
 from email.errors import HeaderParseError, MessageDefect
 from email.policy import Policy
@@ -22,10 +21,8 @@ NLSET: Final[set[str]]
 # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5
 SPECIALSNL: Final[set[str]]
 
-if sys.version_info >= (3, 10):
-    # Added in Python 3.10.17, 3.11.12, 3.12.9, 3.13.2 (may still be backported to 3.9)
-    def make_quoted_pairs(value: Any) -> str: ...
-
+# Added in Python 3.9.23, 3.10.17, 3.11.12, 3.12.9, 3.13.2
+def make_quoted_pairs(value: Any) -> str: ...
 def quote_string(value: Any) -> str: ...
 
 rfc2047_matcher: Pattern[str]
diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi
index 83592eb58336..16259fcfadc7 100644
--- a/mypy/typeshed/stdlib/fractions.pyi
+++ b/mypy/typeshed/stdlib/fractions.pyi
@@ -107,16 +107,31 @@ class Fraction(Rational):
     def __rdivmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ...
     @overload
     def __rdivmod__(a, b: float) -> tuple[float, Fraction]: ...
-    @overload
-    def __pow__(a, b: int) -> Fraction: ...
-    @overload
-    def __pow__(a, b: float | Fraction) -> float: ...
-    @overload
-    def __pow__(a, b: complex) -> complex: ...
-    @overload
-    def __rpow__(b, a: float | Fraction) -> float: ...
-    @overload
-    def __rpow__(b, a: complex) -> complex: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        def __pow__(a, b: int, modulo: None = None) -> Fraction: ...
+        @overload
+        def __pow__(a, b: float | Fraction, modulo: None = None) -> float: ...
+        @overload
+        def __pow__(a, b: complex, modulo: None = None) -> complex: ...
+    else:
+        @overload
+        def __pow__(a, b: int) -> Fraction: ...
+        @overload
+        def __pow__(a, b: float | Fraction) -> float: ...
+        @overload
+        def __pow__(a, b: complex) -> complex: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        def __rpow__(b, a: float | Fraction, modulo: None = None) -> float: ...
+        @overload
+        def __rpow__(b, a: complex, modulo: None = None) -> complex: ...
+    else:
+        @overload
+        def __rpow__(b, a: float | Fraction) -> float: ...
+        @overload
+        def __rpow__(b, a: complex) -> complex: ...
+
     def __pos__(a) -> Fraction: ...
     def __neg__(a) -> Fraction: ...
     def __abs__(a) -> Fraction: ...
diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi
index 9d87c48fd520..3caed77a661a 100644
--- a/mypy/typeshed/stdlib/genericpath.pyi
+++ b/mypy/typeshed/stdlib/genericpath.pyi
@@ -2,7 +2,7 @@ import os
 import sys
 from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRichComparisonT
 from collections.abc import Sequence
-from typing import Literal, overload
+from typing import Literal, NewType, overload
 from typing_extensions import LiteralString
 
 __all__ = [
@@ -17,6 +17,7 @@ __all__ = [
     "samefile",
     "sameopenfile",
     "samestat",
+    "ALLOW_MISSING",
 ]
 if sys.version_info >= (3, 12):
     __all__ += ["islink"]
@@ -57,3 +58,7 @@ if sys.version_info >= (3, 13):
     def isjunction(path: StrOrBytesPath) -> bool: ...
     def isdevdrive(path: StrOrBytesPath) -> bool: ...
     def lexists(path: StrOrBytesPath) -> bool: ...
+
+# Added in Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.4
+_AllowMissingType = NewType("_AllowMissingType", object)
+ALLOW_MISSING: _AllowMissingType
diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi
index 883456b1ddc3..34ae92b4d8ed 100644
--- a/mypy/typeshed/stdlib/gzip.pyi
+++ b/mypy/typeshed/stdlib/gzip.pyi
@@ -1,6 +1,6 @@
 import sys
 import zlib
-from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath
+from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath, WriteableBuffer
 from io import FileIO, TextIOWrapper
 from typing import Final, Literal, Protocol, overload
 from typing_extensions import TypeAlias
@@ -157,8 +157,17 @@ class GzipFile(BaseStream):
     def seek(self, offset: int, whence: int = 0) -> int: ...
     def readline(self, size: int | None = -1) -> bytes: ...
 
+    if sys.version_info >= (3, 14):
+        def readinto(self, b: WriteableBuffer) -> int: ...
+        def readinto1(self, b: WriteableBuffer) -> int: ...
+
 class _GzipReader(DecompressReader):
     def __init__(self, fp: _ReadableFileobj) -> None: ...
 
-def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ...
+if sys.version_info >= (3, 14):
+    def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float = 0) -> bytes: ...
+
+else:
+    def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ...
+
 def decompress(data: ReadableBuffer) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi
index b066d222466b..b7ef607b75cb 100644
--- a/mypy/typeshed/stdlib/lzma.pyi
+++ b/mypy/typeshed/stdlib/lzma.pyi
@@ -35,7 +35,8 @@ from _lzma import (
     is_check_supported as is_check_supported,
 )
 from _typeshed import ReadableBuffer, StrOrBytesPath
-from typing import IO, Literal, TextIO, overload
+from io import TextIOWrapper
+from typing import IO, Literal, overload
 from typing_extensions import Self, TypeAlias
 
 if sys.version_info >= (3, 14):
@@ -144,7 +145,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> TextIO: ...
+) -> TextIOWrapper: ...
 @overload
 def open(
     filename: StrOrBytesPath,
@@ -157,7 +158,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> TextIO: ...
+) -> TextIOWrapper: ...
 @overload
 def open(
     filename: _PathOrFile,
@@ -170,7 +171,7 @@ def open(
     encoding: str | None = None,
     errors: str | None = None,
     newline: str | None = None,
-) -> LZMAFile | TextIO: ...
+) -> LZMAFile | TextIOWrapper: ...
 def compress(
     data: ReadableBuffer, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = None
 ) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi
index ebe305ef708c..074df075b972 100644
--- a/mypy/typeshed/stdlib/ntpath.pyi
+++ b/mypy/typeshed/stdlib/ntpath.pyi
@@ -1,6 +1,8 @@
 import sys
 from _typeshed import BytesPath, StrOrBytesPath, StrPath
 from genericpath import (
+    ALLOW_MISSING as ALLOW_MISSING,
+    _AllowMissingType,
     commonprefix as commonprefix,
     exists as exists,
     getatime as getatime,
@@ -89,6 +91,7 @@ __all__ = [
     "sameopenfile",
     "samestat",
     "commonpath",
+    "ALLOW_MISSING",
 ]
 if sys.version_info >= (3, 12):
     __all__ += ["isjunction", "splitroot"]
@@ -108,16 +111,10 @@ def join(path: StrPath, /, *paths: StrPath) -> str: ...
 def join(path: BytesPath, /, *paths: BytesPath) -> bytes: ...
 
 if sys.platform == "win32":
-    if sys.version_info >= (3, 10):
-        @overload
-        def realpath(path: PathLike[AnyStr], *, strict: bool = False) -> AnyStr: ...
-        @overload
-        def realpath(path: AnyStr, *, strict: bool = False) -> AnyStr: ...
-    else:
-        @overload
-        def realpath(path: PathLike[AnyStr]) -> AnyStr: ...
-        @overload
-        def realpath(path: AnyStr) -> AnyStr: ...
+    @overload
+    def realpath(path: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ...
+    @overload
+    def realpath(path: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ...
 
 else:
     realpath = abspath
diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi
index 3313667f1781..84e1b1e028bd 100644
--- a/mypy/typeshed/stdlib/posixpath.pyi
+++ b/mypy/typeshed/stdlib/posixpath.pyi
@@ -2,6 +2,8 @@ import sys
 from _typeshed import AnyOrLiteralStr, BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath
 from collections.abc import Iterable
 from genericpath import (
+    ALLOW_MISSING as ALLOW_MISSING,
+    _AllowMissingType,
     commonprefix as commonprefix,
     exists as exists,
     getatime as getatime,
@@ -61,6 +63,7 @@ __all__ = [
     "relpath",
     "commonpath",
 ]
+__all__ += ["ALLOW_MISSING"]
 if sys.version_info >= (3, 12):
     __all__ += ["isjunction", "splitroot"]
 if sys.version_info >= (3, 13):
@@ -122,19 +125,10 @@ def join(a: LiteralString, /, *paths: LiteralString) -> LiteralString: ...
 def join(a: StrPath, /, *paths: StrPath) -> str: ...
 @overload
 def join(a: BytesPath, /, *paths: BytesPath) -> bytes: ...
-
-if sys.version_info >= (3, 10):
-    @overload
-    def realpath(filename: PathLike[AnyStr], *, strict: bool = False) -> AnyStr: ...
-    @overload
-    def realpath(filename: AnyStr, *, strict: bool = False) -> AnyStr: ...
-
-else:
-    @overload
-    def realpath(filename: PathLike[AnyStr]) -> AnyStr: ...
-    @overload
-    def realpath(filename: AnyStr) -> AnyStr: ...
-
+@overload
+def realpath(filename: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ...
+@overload
+def realpath(filename: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ...
 @overload
 def relpath(path: LiteralString, start: LiteralString | None = None) -> LiteralString: ...
 @overload
diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi
index 1ee006235ee6..b4fa4381a72c 100644
--- a/mypy/typeshed/stdlib/socket.pyi
+++ b/mypy/typeshed/stdlib/socket.pyi
@@ -773,6 +773,10 @@ if sys.platform == "linux":
     if sys.version_info < (3, 11):
         from _socket import CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER
 
+        __all__ += ["CAN_RAW_ERR_FILTER"]
+    if sys.version_info >= (3, 13):
+        from _socket import CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER
+
         __all__ += ["CAN_RAW_ERR_FILTER"]
 
 if sys.platform == "linux":
diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi
index 31094f87872d..a18ef0b823f9 100644
--- a/mypy/typeshed/stdlib/tarfile.pyi
+++ b/mypy/typeshed/stdlib/tarfile.pyi
@@ -38,6 +38,8 @@ if sys.version_info >= (3, 12):
         "AbsolutePathError",
         "LinkOutsideDestinationError",
     ]
+if sys.version_info >= (3, 13):
+    __all__ += ["LinkFallbackError"]
 
 _FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None]
 _TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction
@@ -550,7 +552,14 @@ class TarFile:
         filter: _TarfileFilter | None = ...,
     ) -> None: ...
     def _extract_member(
-        self, tarinfo: TarInfo, targetpath: str, set_attrs: bool = True, numeric_owner: bool = False
+        self,
+        tarinfo: TarInfo,
+        targetpath: str,
+        set_attrs: bool = True,
+        numeric_owner: bool = False,
+        *,
+        filter_function: _FilterFunction | None = None,
+        extraction_root: str | None = None,
     ) -> None: ...  # undocumented
     def extractfile(self, member: str | TarInfo) -> IO[bytes] | None: ...
     def makedir(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
@@ -559,6 +568,9 @@ class TarFile:
     def makefifo(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
     def makedev(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
     def makelink(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
+    def makelink_with_filter(
+        self, tarinfo: TarInfo, targetpath: StrOrBytesPath, filter_function: _FilterFunction, extraction_root: str
+    ) -> None: ...  # undocumented
     def chown(self, tarinfo: TarInfo, targetpath: StrOrBytesPath, numeric_owner: bool) -> None: ...  # undocumented
     def chmod(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
     def utime(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ...  # undocumented
@@ -607,6 +619,9 @@ class AbsoluteLinkError(FilterError):
 class LinkOutsideDestinationError(FilterError):
     def __init__(self, tarinfo: TarInfo, path: str) -> None: ...
 
+class LinkFallbackError(FilterError):
+    def __init__(self, tarinfo: TarInfo, path: str) -> None: ...
+
 def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
 def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
 def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index 2a4657f86ce1..db0e34d737a6 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -4,7 +4,7 @@ from _typeshed import Incomplete, MaybeNone, StrOrBytesPath
 from collections.abc import Callable, Iterable, Mapping, Sequence
 from tkinter.constants import *
 from tkinter.font import _FontDescription
-from types import TracebackType
+from types import GenericAlias, TracebackType
 from typing import Any, ClassVar, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated
 
@@ -308,6 +308,8 @@ class Event(Generic[_W_co]):
     type: EventType
     widget: _W_co
     delta: int
+    if sys.version_info >= (3, 14):
+        def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
 def NoDefaultRoot() -> None: ...
 
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index 07cd57ebc18f..3f7c25712081 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -697,6 +697,6 @@ class Sentinel:
     if sys.version_info >= (3, 14):
         def __or__(self, other: Any) -> UnionType: ...  # other can be any type form legal for unions
         def __ror__(self, other: Any) -> UnionType: ...  # other can be any type form legal for unions
-    else:
+    elif sys.version_info >= (3, 10):
         def __or__(self, other: Any) -> _SpecialForm: ...  # other can be any type form legal for unions
         def __ror__(self, other: Any) -> _SpecialForm: ...  # other can be any type form legal for unions
diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
index 35381758a1b7..e9f54fbf2a26 100644
--- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
+++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
@@ -1,3 +1,4 @@
+import sys
 from collections.abc import Iterable
 from datetime import datetime, timedelta, tzinfo
 from typing_extensions import Self
@@ -17,8 +18,13 @@ class ZoneInfo(tzinfo):
     def __new__(cls, key: str) -> Self: ...
     @classmethod
     def no_cache(cls, key: str) -> Self: ...
-    @classmethod
-    def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ...
+    if sys.version_info >= (3, 12):
+        @classmethod
+        def from_file(cls, file_obj: _IOBytes, /, key: str | None = None) -> Self: ...
+    else:
+        @classmethod
+        def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ...
+
     @classmethod
     def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ...
     def tzname(self, dt: datetime | None, /) -> str | None: ...

From b62957b992ec1da37c42ad37e8ed23fc51644b3e Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Wed, 18 Jun 2025 14:17:03 -0400
Subject: [PATCH 1389/1617] Fix missing error when redeclaring type variable in
 nested generic class (#18883)

Closes #10479

Fixes a case where mypy doesn't warn about an inner generic class using
a type variable by the same name as an outer generic class if the inner
generic class doesn't declare the type variable using `Generic`,
`Protocol`, or PEP-695 syntax.
---
 mypy/message_registry.py           | 4 ++++
 mypy/semanal.py                    | 8 ++++++++
 mypy/typeanal.py                   | 7 ++-----
 test-data/unit/semanal-errors.test | 6 ++++++
 4 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/mypy/message_registry.py b/mypy/message_registry.py
index 609f968a8c65..3c7745876f87 100644
--- a/mypy/message_registry.py
+++ b/mypy/message_registry.py
@@ -353,6 +353,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
     "TypeVar constraint type cannot be parametrized by type variables", codes.MISC
 )
 
+TYPE_VAR_REDECLARED_IN_NESTED_CLASS: Final = ErrorMessage(
+    'Type variable "{}" is bound by an outer class', codes.VALID_TYPE
+)
+
 TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage(
     "Yield expression cannot be used within a type alias", codes.SYNTAX
 )
diff --git a/mypy/semanal.py b/mypy/semanal.py
index d70abe911fea..704aa91d1d12 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2374,6 +2374,14 @@ def tvar_defs_from_tvars(
             tvar_expr.default = tvar_expr.default.accept(
                 TypeVarDefaultTranslator(self, tvar_expr.name, context)
             )
+            # PEP-695 type variables that are redeclared in an inner scope are warned
+            # about elsewhere.
+            if not tvar_expr.is_new_style and not self.tvar_scope.allow_binding(
+                tvar_expr.fullname
+            ):
+                self.fail(
+                    message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(name), context
+                )
             tvar_def = self.tvar_scope.bind_new(name, tvar_expr)
             if last_tvar_name_with_default is not None and not tvar_def.has_default():
                 self.msg.tvar_without_default_type(
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index eeb5d3c52ac6..b0d11759303c 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -1844,11 +1844,8 @@ def bind_function_type_variables(
         defs = []
         for name, tvar in typevars:
             if not self.tvar_scope.allow_binding(tvar.fullname):
-                self.fail(
-                    f'Type variable "{name}" is bound by an outer class',
-                    defn,
-                    code=codes.VALID_TYPE,
-                )
+                err_msg = message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(name)
+                self.fail(err_msg.value, defn, code=err_msg.code)
             binding = self.tvar_scope.bind_new(name, tvar)
             defs.append(binding)
 
diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test
index fa5cec795931..1e760799828a 100644
--- a/test-data/unit/semanal-errors.test
+++ b/test-data/unit/semanal-errors.test
@@ -1015,6 +1015,12 @@ class A(Generic[T]):
           # E: Free type variable expected in Generic[...]
 [out]
 
+[case testRedeclaredTypeVarWithinNestedGenericClass]
+from typing import Generic, Iterable, TypeVar
+T = TypeVar('T')
+class A(Generic[T]):
+    class B(Iterable[T]): pass  # E: Type variable "T" is bound by an outer class
+
 [case testIncludingGenericTwiceInBaseClassList]
 from typing import Generic, TypeVar
 T = TypeVar('T')

From a48ffed250cc19efee6394eb906e8c7d70288dc5 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Wed, 18 Jun 2025 14:25:16 -0400
Subject: [PATCH 1390/1617] Show name of type variable in "Cannot infer type
 argument" message (#19290)

Fixes #19289

The type argument index currently shown can be wrong if other type
arguments were substituted during an earlier pass.

Given:
```python
def foo[T1, T2](
    a: T1,
    b: T2,
    c: Callable[[T2], T2],
) -> tuple[T1, T2]: ...

def bar(y: float) -> float: ...

reveal_type(foo(1, None, bar))  # Expect T1=int, T2=
```
Before:
```
main.py:9: error: Cannot infer type argument 1 of "foo"  [misc]
main.py:9: note: Revealed type is "tuple[builtins.int, Any]"
```
After:
```
main.py:9: error: Cannot infer type argument to type parameter "T2" of "foo"  [misc]
main.py:9: note: Revealed type is "tuple[builtins.int, Any]"
```
---
 mypy/checkexpr.py                             |  4 ++--
 mypy/messages.py                              |  9 ++++---
 test-data/unit/check-dataclasses.test         |  2 +-
 test-data/unit/check-expressions.test         |  4 ++--
 test-data/unit/check-generics.test            |  6 ++---
 test-data/unit/check-inference-context.test   |  2 +-
 test-data/unit/check-inference.test           | 24 +++++++++++++++----
 test-data/unit/check-literal.test             |  4 ++--
 test-data/unit/check-overloading.test         |  6 ++---
 .../unit/check-parameter-specification.test   |  2 +-
 test-data/unit/check-plugin-attrs.test        |  4 ++--
 test-data/unit/check-protocols.test           |  4 ++--
 test-data/unit/check-typevar-tuple.test       |  8 +++----
 13 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 4ca55e1679e4..26cb2a35794b 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2338,10 +2338,10 @@ def apply_inferred_arguments(
         # Report error if some of the variables could not be solved. In that
         # case assume that all variables have type Any to avoid extra
         # bogus error messages.
-        for i, inferred_type in enumerate(inferred_args):
+        for inferred_type, tv in zip(inferred_args, callee_type.variables):
             if not inferred_type or has_erased_component(inferred_type):
                 # Could not infer a non-trivial type for a type variable.
-                self.msg.could_not_infer_type_arguments(callee_type, i + 1, context)
+                self.msg.could_not_infer_type_arguments(callee_type, tv, context)
                 inferred_args = [AnyType(TypeOfAny.from_error)] * len(inferred_args)
         # Apply the inferred types to the function type. In this case the
         # return type must be CallableType, since we give the right number of type
diff --git a/mypy/messages.py b/mypy/messages.py
index 01414f1c7f2b..13a4facc82b0 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -1370,11 +1370,14 @@ def incompatible_type_application(
             self.fail(f"Type application has too few types ({s})", context)
 
     def could_not_infer_type_arguments(
-        self, callee_type: CallableType, n: int, context: Context
+        self, callee_type: CallableType, tv: TypeVarLikeType, context: Context
     ) -> None:
         callee_name = callable_name(callee_type)
-        if callee_name is not None and n > 0:
-            self.fail(f"Cannot infer type argument {n} of {callee_name}", context)
+        if callee_name is not None:
+            self.fail(
+                f"Cannot infer value of type parameter {format_type(tv, self.options)} of {callee_name}",
+                context,
+            )
             if callee_name == "":
                 # Invariance in key type causes more of these errors than we would want.
                 self.note(
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index 30d8497c9cd2..2ead202bd6af 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -705,7 +705,7 @@ class A(Generic[T]):
     return self.z  # E: Incompatible return value type (got "list[T]", expected "T")
 
 reveal_type(A)  # N: Revealed type is "def [T] (x: T`1, y: T`1, z: builtins.list[T`1]) -> __main__.A[T`1]"
-A(1, 2, ["a", "b"])  # E: Cannot infer type argument 1 of "A"
+A(1, 2, ["a", "b"])  # E: Cannot infer value of type parameter "T" of "A"
 a = A(1, 2, [1, 2])
 reveal_type(a)  # N: Revealed type is "__main__.A[builtins.int]"
 reveal_type(a.x)  # N: Revealed type is "builtins.int"
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index f3c00627892e..33271a3cc04c 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1878,7 +1878,7 @@ a = {'a': 1}
 b = {'z': 26, **a}
 c = {**b}
 d = {**a, **b, 'c': 3}
-e = {1: 'a', **a}  # E: Cannot infer type argument 1 of  \
+e = {1: 'a', **a}  # E: Cannot infer value of type parameter "KT" of  \
                    # N: Try assigning the literal to a variable annotated as dict[, ]
 f = {**b}  # type: Dict[int, int]  # E: Unpacked dict entry 0 has incompatible type "dict[str, int]"; expected "SupportsKeysAndGetItem[int, int]"
 g = {**Thing()}
@@ -1893,7 +1893,7 @@ i = {**Thing()}  # type: Dict[int, int]  # E: Unpacked dict entry 0 has incompat
                  # N:         def keys(self) -> Iterable[int] \
                  # N:     Got: \
                  # N:         def keys(self) -> Iterable[str]
-j = {1: 'a', **Thing()}  # E: Cannot infer type argument 1 of  \
+j = {1: 'a', **Thing()}  # E: Cannot infer value of type parameter "KT" of  \
                          # N: Try assigning the literal to a variable annotated as dict[, ]
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-medium.pyi]
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 8839dfb954f4..0be9d918c69f 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -584,7 +584,7 @@ def func2(x: SameNode[T]) -> SameNode[T]:
     return x
 reveal_type(func2) # N: Revealed type is "def [T] (x: __main__.Node[T`-1, T`-1]) -> __main__.Node[T`-1, T`-1]"
 
-func2(Node(1, 'x')) # E: Cannot infer type argument 1 of "func2"
+func2(Node(1, 'x')) # E: Cannot infer value of type parameter "T" of "func2"
 y = func2(Node('x', 'x'))
 reveal_type(y) # N: Revealed type is "__main__.Node[builtins.str, builtins.str]"
 
@@ -888,7 +888,7 @@ def fun2(v: Vec[T], scale: T) -> Vec[T]:
 
 reveal_type(fun1([(1, 1)])) # N: Revealed type is "builtins.int"
 fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "list[tuple[bool, bool]]"
-fun1([(1, 'x')]) # E: Cannot infer type argument 1 of "fun1"
+fun1([(1, 'x')]) # E: Cannot infer value of type parameter "T" of "fun1"
 
 reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int]]"
 fun2([('x', 'x')], 'x') # E: Value of type variable "T" of "fun2" cannot be "str"
@@ -909,7 +909,7 @@ def f(x: Node[T, T]) -> TupledNode[T]:
     return Node(x.x, (x.x, x.x))
 
 f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Node[Never, Never]"
-f(Node(1, 'x')) # E: Cannot infer type argument 1 of "f"
+f(Node(1, 'x')) # E: Cannot infer value of type parameter "T" of "f"
 reveal_type(Node('x', 'x')) # N: Revealed type is "a.Node[builtins.str, builtins.str]"
 
 [file a.py]
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index 67ae22a369b1..ff726530cf9f 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -1009,7 +1009,7 @@ class D(C): ...
 
 def f(x: List[T], y: List[T]) -> List[T]: ...
 
-f([C()], [D()]) # E: Cannot infer type argument 1 of "f"
+f([C()], [D()]) # E: Cannot infer value of type parameter "T" of "f"
 [builtins fixtures/list.pyi]
 
 [case testInferTypeVariableFromTwoGenericTypes3]
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 856d430a544c..90cb7d3799cf 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -693,8 +693,8 @@ class A(Generic[T]): pass
 class B: pass
 
 
-f(ao, ab) # E: Cannot infer type argument 1 of "f"
-f(ab, ao) # E: Cannot infer type argument 1 of "f"
+f(ao, ab) # E: Cannot infer value of type parameter "T" of "f"
+f(ab, ao) # E: Cannot infer value of type parameter "T" of "f"
 f(ao, ao)
 f(ab, ab)
 
@@ -3774,8 +3774,8 @@ reveal_type(f(x, []))  # N: Revealed type is "builtins.str"
 reveal_type(f(["yes"], []))  # N: Revealed type is "builtins.str"
 
 empty: List[NoReturn]
-f(x, empty)  # E: Cannot infer type argument 1 of "f"
-f(["no"], empty)  # E: Cannot infer type argument 1 of "f"
+f(x, empty)  # E: Cannot infer value of type parameter "T" of "f"
+f(["no"], empty)  # E: Cannot infer value of type parameter "T" of "f"
 [builtins fixtures/list.pyi]
 
 [case testInferenceWorksWithEmptyCollectionsUnion]
@@ -4149,3 +4149,19 @@ class Foo:
         else:
             self.qux = {}  # E: Need type annotation for "qux" (hint: "qux: dict[, ] = ...")
 [builtins fixtures/dict.pyi]
+
+[case testConstraintSolvingFailureShowsCorrectArgument]
+from typing import Callable, TypeVar
+
+T1 = TypeVar('T1')
+T2 = TypeVar('T2')
+def foo(
+    a: T1,
+    b: T2,
+    c: Callable[[T2], T2],
+) -> tuple[T1, T2]: ...
+
+def bar(y: float) -> float: ...
+
+foo(1, None, bar)  # E: Cannot infer value of type parameter "T2" of "foo"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
index f995332643af..3c9290b8dbbb 100644
--- a/test-data/unit/check-literal.test
+++ b/test-data/unit/check-literal.test
@@ -2989,9 +2989,9 @@ def g(a: T, t: A[T]) -> T: ...
 
 def check(obj: A[Literal[1]]) -> None:
     reveal_type(f(obj, 1))  # N: Revealed type is "Literal[1]"
-    reveal_type(f(obj, ''))  # E: Cannot infer type argument 1 of "f" \
+    reveal_type(f(obj, ''))  # E: Cannot infer value of type parameter "T" of "f" \
                              # N: Revealed type is "Any"
     reveal_type(g(1, obj))  # N: Revealed type is "Literal[1]"
-    reveal_type(g('', obj))  # E: Cannot infer type argument 1 of "g" \
+    reveal_type(g('', obj))  # E: Cannot infer value of type parameter "T" of "g" \
                              # N: Revealed type is "Any"
 [builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index 0ccc8a2a353c..e427d5b21d40 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -3370,10 +3370,10 @@ def wrapper() -> None:
     obj2: Union[W1[A], W2[B]]
 
     reveal_type(foo(obj2))  # N: Revealed type is "Union[__main__.A, __main__.B]"
-    bar(obj2)  # E: Cannot infer type argument 1 of "bar"
+    bar(obj2)  # E: Cannot infer value of type parameter "T" of "bar"
 
     b1_overload: A = foo(obj2)  # E: Incompatible types in assignment (expression has type "Union[A, B]", variable has type "A")
-    b1_union: A    = bar(obj2)  # E: Cannot infer type argument 1 of "bar"
+    b1_union: A    = bar(obj2)  # E: Cannot infer value of type parameter "T" of "bar"
 
 [case testOverloadingInferUnionReturnWithObjectTypevarReturn]
 from typing import overload, Union, TypeVar, Generic
@@ -3496,7 +3496,7 @@ def t_is_same_bound(arg1: T1, arg2: S) -> Tuple[T1, S]:
     # The arguments in the tuple are swapped
     x3: Union[List[S], List[Tuple[S, T1]]]
     y3: S
-    Dummy[T1]().foo(x3, y3)  # E: Cannot infer type argument 1 of "foo" of "Dummy" \
+    Dummy[T1]().foo(x3, y3)  # E: Cannot infer value of type parameter "S" of "foo" of "Dummy" \
                              # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[list[S], list[tuple[S, T1]]]"; expected "list[tuple[T1, Any]]"
 
     x4: Union[List[int], List[Tuple[C, int]]]
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
index 085f6fe59809..e53c45b5b512 100644
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -2135,7 +2135,7 @@ def d(f: Callable[P, None], fn: Callable[Concatenate[Callable[P, None], P], None
 
 reveal_type(d(a, f1))  # N: Revealed type is "def (i: builtins.int)"
 reveal_type(d(a, f2))  # N: Revealed type is "def (i: builtins.int)"
-reveal_type(d(b, f1))  # E: Cannot infer type argument 1 of "d" \
+reveal_type(d(b, f1))  # E: Cannot infer value of type parameter "P" of "d" \
                        # N: Revealed type is "def (*Any, **Any)"
 reveal_type(d(b, f2))  # N: Revealed type is "def (builtins.int)"
 [builtins fixtures/paramspec.pyi]
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index 6415b5104296..00bec13ab16d 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -470,8 +470,8 @@ reveal_type(a)  # N: Revealed type is "__main__.A[builtins.int]"
 reveal_type(a.x)  # N: Revealed type is "builtins.list[builtins.int]"
 reveal_type(a.y)  # N: Revealed type is "builtins.int"
 
-A(['str'], 7)  # E: Cannot infer type argument 1 of "A"
-A([1], '2')  # E: Cannot infer type argument 1 of "A"
+A(['str'], 7)  # E: Cannot infer value of type parameter "T" of "A"
+A([1], '2')  # E: Cannot infer value of type parameter "T" of "A"
 
 [builtins fixtures/list.pyi]
 
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index c6c2c5f8da98..79207c9aad56 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4217,10 +4217,10 @@ def g2(a: Input[bytes], b: Output[bytes]) -> None:
     f(a, b)
 
 def g3(a: Input[str], b: Output[bytes]) -> None:
-    f(a, b)  # E: Cannot infer type argument 1 of "f"
+    f(a, b)  # E: Cannot infer value of type parameter "AnyStr" of "f"
 
 def g4(a: Input[bytes], b: Output[str]) -> None:
-    f(a, b)  # E: Cannot infer type argument 1 of "f"
+    f(a, b)  # E: Cannot infer value of type parameter "AnyStr" of "f"
 
 [builtins fixtures/tuple.pyi]
 
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 862fd9ff5fb0..c0c826d09c9e 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -2372,9 +2372,9 @@ def pointwise_multiply(x: Array[Unpack[Ts]], y: Array[Unpack[Ts]]) -> Array[Unpa
 
 def a1(x: Array[int], y: Array[str], z: Array[int, str]) -> None:
     reveal_type(pointwise_multiply(x, x))  # N: Revealed type is "__main__.Array[builtins.int]"
-    reveal_type(pointwise_multiply(x, y))  # E: Cannot infer type argument 1 of "pointwise_multiply" \
+    reveal_type(pointwise_multiply(x, y))  # E: Cannot infer value of type parameter "Ts" of "pointwise_multiply" \
                                            # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]"
-    reveal_type(pointwise_multiply(x, z))  # E: Cannot infer type argument 1 of "pointwise_multiply" \
+    reveal_type(pointwise_multiply(x, z))  # E: Cannot infer value of type parameter "Ts" of "pointwise_multiply" \
                                            # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]"
 
 def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
@@ -2382,9 +2382,9 @@ def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]:
 
 def a2(x: Array[int, str]) -> None:
     reveal_type(func(x, 2, "Hello"))  # N: Revealed type is "tuple[builtins.int, builtins.str]"
-    reveal_type(func(x, 2))           # E: Cannot infer type argument 1 of "func" \
+    reveal_type(func(x, 2))           # E: Cannot infer value of type parameter "Ts" of "func" \
                                       # N: Revealed type is "builtins.tuple[Any, ...]"
-    reveal_type(func(x, 2, "Hello", True))   # E: Cannot infer type argument 1 of "func" \
+    reveal_type(func(x, 2, "Hello", True))   # E: Cannot infer value of type parameter "Ts" of "func" \
                                              # N: Revealed type is "builtins.tuple[Any, ...]"
 [builtins fixtures/tuple.pyi]
 

From ae778ccd7dea64f4d3756eb23a127aa9ea275e49 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 19 Jun 2025 00:32:19 +0200
Subject: [PATCH 1391/1617] Fix `dmypy suggest` interaction with `__new__`
 (#18966)

Fixes #18964. `__new__` is special - it has `is_static` set in
`semanal.py`, but still has first `cls` argument. This PR tells `dmypy
suggest` about that.
---
 mypy/checker.py                          |  4 ++--
 mypy/nodes.py                            | 11 +++++++++++
 mypy/semanal.py                          |  4 ++--
 mypy/suggestions.py                      |  2 +-
 mypy/typeops.py                          |  2 +-
 test-data/unit/fine-grained-suggest.test | 20 ++++++++++++++++++++
 6 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 0639340d30bb..10683327284b 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1362,7 +1362,7 @@ def check_func_def(
                 if typ.type_is:
                     arg_index = 0
                     # For methods and classmethods, we want the second parameter
-                    if ref_type is not None and (not defn.is_static or defn.name == "__new__"):
+                    if ref_type is not None and defn.has_self_or_cls_argument:
                         arg_index = 1
                     if arg_index < len(typ.arg_types) and not is_subtype(
                         typ.type_is, typ.arg_types[arg_index]
@@ -1382,7 +1382,7 @@ def check_func_def(
                         isinstance(defn, FuncDef)
                         and ref_type is not None
                         and i == 0
-                        and (not defn.is_static or defn.name == "__new__")
+                        and defn.has_self_or_cls_argument
                         and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]
                     ):
                         if defn.is_class or defn.name == "__new__":
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 2cec4852f31c..1b6884f04bf5 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -508,6 +508,8 @@ def __init__(self) -> None:
         self.info = FUNC_NO_INFO
         self.is_property = False
         self.is_class = False
+        # Is this a `@staticmethod` (explicit or implicit)?
+        # Note: use has_self_or_cls_argument to check if there is `self` or `cls` argument
         self.is_static = False
         self.is_final = False
         self.is_explicit_override = False
@@ -524,6 +526,15 @@ def name(self) -> str:
     def fullname(self) -> str:
         return self._fullname
 
+    @property
+    def has_self_or_cls_argument(self) -> bool:
+        """If used as a method, does it have an argument for method binding (`self`, `cls`)?
+
+        This is true for `__new__` even though `__new__` does not undergo method binding,
+        because we still usually assume that `cls` corresponds to the enclosing class.
+        """
+        return not self.is_static or self.name == "__new__"
+
 
 OverloadPart: _TypeAlias = Union["FuncDef", "Decorator"]
 
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 704aa91d1d12..8f9d1c4f35d6 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1069,7 +1069,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type:
         functype = func.type
         if func.name == "__new__":
             func.is_static = True
-        if not func.is_static or func.name == "__new__":
+        if func.has_self_or_cls_argument:
             if func.name in ["__init_subclass__", "__class_getitem__"]:
                 func.is_class = True
             if not func.arguments:
@@ -1602,7 +1602,7 @@ def analyze_function_body(self, defn: FuncItem) -> None:
                 # The first argument of a non-static, non-class method is like 'self'
                 # (though the name could be different), having the enclosing class's
                 # instance type.
-                if is_method and (not defn.is_static or defn.name == "__new__") and defn.arguments:
+                if is_method and defn.has_self_or_cls_argument and defn.arguments:
                     if not defn.is_class:
                         defn.arguments[0].variable.is_self = True
                     else:
diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index 673076729ffa..cfd7413860ec 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -486,7 +486,7 @@ def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature:
         if self.no_errors and orig_errors:
             raise SuggestionFailure("Function does not typecheck.")
 
-        is_method = bool(node.info) and not node.is_static
+        is_method = bool(node.info) and node.has_self_or_cls_argument
 
         with state.strict_optional_set(graph[mod].options.strict_optional):
             guesses = self.get_guesses(
diff --git a/mypy/typeops.py b/mypy/typeops.py
index e8087a1713ff..aaa3f91a0798 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -890,7 +890,7 @@ def callable_type(
     fdef: FuncItem, fallback: Instance, ret_type: Type | None = None
 ) -> CallableType:
     # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal
-    if fdef.info and (not fdef.is_static or fdef.name == "__new__") and fdef.arg_names:
+    if fdef.info and fdef.has_self_or_cls_argument and fdef.arg_names:
         self_type: Type = fill_typevars(fdef.info)
         if fdef.is_class or fdef.name == "__new__":
             self_type = TypeType.make_normalized(self_type)
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index f2db85c05f18..3a696ce19c63 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -795,6 +795,26 @@ def bar(iany) -> None:
 (int) -> None
 ==
 
+[case testSuggestNewInit]
+# suggest: foo.F.__init__
+# suggest: foo.F.__new__
+[file foo.py]
+class F:
+    def __new__(cls, t):
+        return super().__new__(cls)
+
+    def __init__(self, t):
+        self.t = t
+
+[file bar.py]
+from foo import F
+def bar(iany) -> None:
+    F(0)
+[out]
+(int) -> None
+(int) -> Any
+==
+
 [case testSuggestColonBasic]
 # suggest: tmp/foo.py:1
 # suggest: tmp/bar/baz.py:2

From 9f455bd9f8f32e166ba748055aafba5f30a21714 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 19 Jun 2025 00:23:49 +0100
Subject: [PATCH 1392/1617] Generalize class/static method and property alias
 support (#19297)

Fixes https://github.com/python/mypy/issues/6700

This is another followup for the `checkmember` work. Currently, we only
support non-instance method aliasing in few very specific cases. I am
making this support general.
---
 mypy/checker.py                    | 36 +++++++++++++++++-----
 mypy/checkmember.py                | 18 +++++++----
 test-data/unit/check-callable.test | 48 ++++++++++++++++++++++++++++++
 test-data/unit/check-classes.test  | 17 +++++++++++
 4 files changed, 106 insertions(+), 13 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 10683327284b..9d02bcac8471 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -4400,9 +4400,9 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         refers to the variable (lvalue). If var is None, do nothing.
         """
         if var and not self.current_node_deferred:
-            # TODO: should we also set 'is_ready = True' here?
             var.type = type
             var.is_inferred = True
+            var.is_ready = True
             if var not in self.var_decl_frames:
                 # Used for the hack to improve optional type inference in conditionals
                 self.var_decl_frames[var] = {frame.id for frame in self.binder.frames}
@@ -4412,9 +4412,23 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
                     self.inferred_attribute_types[lvalue.def_var] = type
             self.store_type(lvalue, type)
             p_type = get_proper_type(type)
-            if isinstance(p_type, CallableType) and is_node_static(p_type.definition):
-                # TODO: handle aliases to class methods (similarly).
-                var.is_staticmethod = True
+            definition = None
+            if isinstance(p_type, CallableType):
+                definition = p_type.definition
+            elif isinstance(p_type, Overloaded):
+                # Randomly select first item, if items are different, there will
+                # be an error during semantic analysis.
+                definition = p_type.items[0].definition
+            if definition:
+                if is_node_static(definition):
+                    var.is_staticmethod = True
+                elif is_classmethod_node(definition):
+                    var.is_classmethod = True
+                elif is_property(definition):
+                    var.is_property = True
+                    if isinstance(p_type, Overloaded):
+                        # TODO: in theory we can have a property with a deleter only.
+                        var.is_settable_property = True
 
     def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         """Store best known type for variable if type inference failed.
@@ -8531,15 +8545,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
         return t.copy_modified(args=[a.accept(self) for a in t.args])
 
 
+def is_classmethod_node(node: Node | None) -> bool | None:
+    """Find out if a node describes a classmethod."""
+    if isinstance(node, FuncDef):
+        return node.is_class
+    if isinstance(node, Var):
+        return node.is_classmethod
+    return None
+
+
 def is_node_static(node: Node | None) -> bool | None:
     """Find out if a node describes a static function method."""
-
     if isinstance(node, FuncDef):
         return node.is_static
-
     if isinstance(node, Var):
         return node.is_staticmethod
-
     return None
 
 
@@ -8786,6 +8806,8 @@ def is_static(func: FuncBase | Decorator) -> bool:
 
 
 def is_property(defn: SymbolNode) -> bool:
+    if isinstance(defn, FuncDef):
+        return defn.is_property
     if isinstance(defn, Decorator):
         return defn.func.is_property
     if isinstance(defn, OverloadedFuncDef):
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 8f62fee699c0..2a8d09808cfb 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -371,8 +371,6 @@ def analyze_instance_member_access(
                     signature, mx.self_type, method.is_class, mx.context, name, mx.msg
                 )
                 signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
-        # TODO: should we skip these steps for static methods as well?
-        # Since generic static methods should not be allowed.
         typ = map_instance_to_supertype(typ, method.info)
         member_type = expand_type_by_instance(signature, typ)
         freeze_all_type_vars(member_type)
@@ -1224,8 +1222,11 @@ def analyze_class_attribute_access(
             #     C[int].x -> int
             t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars})
 
-        is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
-            isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class
+        is_classmethod = (
+            (is_decorated and cast(Decorator, node.node).func.is_class)
+            or (isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class)
+            or isinstance(node.node, Var)
+            and node.node.is_classmethod
         )
         is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
             isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
@@ -1237,7 +1238,12 @@ def analyze_class_attribute_access(
             is_trivial_self = node.node.func.is_trivial_self and not node.node.decorators
         elif isinstance(node.node, (FuncDef, OverloadedFuncDef)):
             is_trivial_self = node.node.is_trivial_self
-        if isinstance(t, FunctionLike) and is_classmethod and not is_trivial_self:
+        if (
+            isinstance(t, FunctionLike)
+            and is_classmethod
+            and not is_trivial_self
+            and not t.bound()
+        ):
             t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
         t = add_class_tvars(
             t,
@@ -1406,7 +1412,7 @@ class B(A[str]): pass
         tvars = original_vars if original_vars is not None else []
         if not mx.preserve_type_var_ids:
             t = freshen_all_functions_type_vars(t)
-        if is_classmethod:
+        if is_classmethod and not t.is_bound:
             if is_trivial_self:
                 t = bind_self_fast(t, mx.self_type)
             else:
diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test
index 39e6c4fa3ff1..23db0bf50a4e 100644
--- a/test-data/unit/check-callable.test
+++ b/test-data/unit/check-callable.test
@@ -587,6 +587,54 @@ class C(B):
 class B: ...
 [builtins fixtures/classmethod.pyi]
 
+[case testClassMethodAliasInClass]
+from typing import overload
+
+class C:
+    @classmethod
+    def foo(cls) -> int: ...
+
+    bar = foo
+
+    @overload
+    @classmethod
+    def foo2(cls, x: int) -> int: ...
+    @overload
+    @classmethod
+    def foo2(cls, x: str) -> str: ...
+    @classmethod
+    def foo2(cls, x):
+        ...
+
+    bar2 = foo2
+
+reveal_type(C.bar)  # N: Revealed type is "def () -> builtins.int"
+reveal_type(C().bar)  # N: Revealed type is "def () -> builtins.int"
+reveal_type(C.bar2)  # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)"
+reveal_type(C().bar2)  # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)"
+[builtins fixtures/classmethod.pyi]
+
+[case testPropertyAliasInClassBody]
+class A:
+    @property
+    def f(self) -> int: ...
+
+    g = f
+
+    @property
+    def f2(self) -> int: ...
+    @f2.setter
+    def f2(self, val: int) -> None: ...
+
+    g2 = f2
+
+reveal_type(A().g)  # N: Revealed type is "builtins.int"
+reveal_type(A().g2)  # N: Revealed type is "builtins.int"
+A().g = 1  # E: Property "g" defined in "A" is read-only
+A().g2 = 1
+A().g2 = "no"  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
+[builtins fixtures/property.pyi]
+
 [case testCallableUnionCallback]
 from typing import Union, Callable, TypeVar
 
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index f4bbaf41dc47..3d99ccb302c6 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -4588,6 +4588,23 @@ reveal_type(a.a)  # N: Revealed type is "def (a: builtins.int)"
 reveal_type(a.c)  # N: Revealed type is "def (a: builtins.int)"
 [builtins fixtures/staticmethod.pyi]
 
+[case testClassStaticMethodIndirectOverloaded]
+from typing import overload
+class A:
+    @overload
+    @staticmethod
+    def a(x: int) -> int: ...
+    @overload
+    @staticmethod
+    def a(x: str) -> str: ...
+    @staticmethod
+    def a(x):
+        ...
+    c = a
+reveal_type(A.c)  # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)"
+reveal_type(A().c)  # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)"
+[builtins fixtures/staticmethod.pyi]
+
 [case testClassStaticMethodSubclassing]
 class A:
     @staticmethod

From d52ce3b3702bc7c194d6c14bca7affad2596e84b Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 19 Jun 2025 00:26:51 +0100
Subject: [PATCH 1393/1617] Fix constructor type for subclasses of Any (#19295)

Fixes https://github.com/python/mypy/issues/9815
Fixes https://github.com/python/mypy/issues/10848
Fixes https://github.com/python/mypy/issues/17781

Also discovered this while working on `checkmember` stuff. Previously,
return type of the type object was the class where `__init__()` was
defined (if there was an `Any` somewhere in MRO). And since we use
return type for attribute access on type objects, it went completely
sideways. Fix is simple (we accidentally used `info` instead of
`def_info` in one place).
---
 mypy/typeops.py                   |  8 ++++----
 test-data/unit/check-classes.test | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/mypy/typeops.py b/mypy/typeops.py
index aaa3f91a0798..e84be19465cc 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -125,7 +125,8 @@ def tuple_fallback(typ: TupleType) -> Instance:
     )
 
 
-def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Type | None:
+def get_self_type(func: CallableType, def_info: TypeInfo) -> Type | None:
+    default_self = fill_typevars(def_info)
     if isinstance(get_proper_type(func.ret_type), UninhabitedType):
         return func.ret_type
     elif func.arg_types and func.arg_types[0] != default_self and func.arg_kinds[0] == ARG_POS:
@@ -227,9 +228,8 @@ def type_object_type_from_function(
     # self-types only in the defining class, similar to __new__ (but not exactly the same,
     # see comment in class_callable below). This is mostly useful for annotating library
     # classes such as subprocess.Popen.
-    default_self = fill_typevars(info)
     if not is_new and not info.is_newtype:
-        orig_self_types = [get_self_type(it, default_self) for it in signature.items]
+        orig_self_types = [get_self_type(it, def_info) for it in signature.items]
     else:
         orig_self_types = [None] * len(signature.items)
 
@@ -245,7 +245,7 @@ def type_object_type_from_function(
     # We need to map B's __init__ to the type (List[T]) -> None.
     signature = bind_self(
         signature,
-        original_type=default_self,
+        original_type=fill_typevars(info),
         is_classmethod=is_new,
         # Explicit instance self annotations have special handling in class_callable(),
         # we don't need to bind any type variables in them if they are generic.
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 3d99ccb302c6..1b3de53567d1 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -8796,3 +8796,21 @@ class C:
 C().foo = "no"  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
 C().bar = "fine"
 [builtins fixtures/property.pyi]
+
+[case testCorrectConstructorTypeWithAnyFallback]
+from typing import Generic, TypeVar
+
+class B(Unknown):  # type: ignore
+    def __init__(self) -> None: ...
+class C(B): ...
+
+reveal_type(C)  # N: Revealed type is "def () -> __main__.C"
+
+T = TypeVar("T")
+class BG(Generic[T], Unknown):  # type: ignore
+    def __init__(self) -> None: ...
+class CGI(BG[int]): ...
+class CGT(BG[T]): ...
+
+reveal_type(CGI)  # N: Revealed type is "def () -> __main__.CGI"
+reveal_type(CGT)  # N: Revealed type is "def [T] () -> __main__.CGT[T`1]"

From ad570933924b3810ba61d2e4a13eac596f74672b Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 19 Jun 2025 17:27:14 +0100
Subject: [PATCH 1394/1617] Cleanup generic class variable access (#19292)

Fixes https://github.com/python/mypy/issues/5144
Fixes https://github.com/python/mypy/issues/15223

This is related to the work on
https://github.com/python/mypy/issues/7724

Now that all attribute access goes through `checkmember.py` there is not
much benefit in giving an error at the definition site, especially that
it prohibits some valid (and common) use cases, see comments in
https://github.com/python/mypy/issues/5144. While looking at this I
discovered a bunch of defects in the _implementation_, that I also fix
(I am keeping unsafe self-type related logic as is):
* We used to erase type vars of the definition class instead of the use
class. This caused type variables leaks.
* The erasure was inconsistent, so that in some cases we silently erased
type variables to `Any` even in allowed use cases
* `TypeVarTuple` and `ParamSpec` were not handled as equal to regular
type variables (I guess because of old problems with erasing them)
---
 mypy/checkmember.py                     | 30 ++++++++++++++-----------
 mypy/message_registry.py                |  1 -
 mypy/semanal.py                         |  8 -------
 test-data/unit/check-classvar.test      | 23 ++++++++++++++++---
 test-data/unit/check-selftype.test      | 22 ++++++++++++++++++
 test-data/unit/check-typevar-tuple.test | 22 ++++++++++++++++++
 test-data/unit/semanal-classvar.test    | 11 ---------
 7 files changed, 81 insertions(+), 36 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 2a8d09808cfb..5b5580a648a8 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -45,7 +45,6 @@
     freeze_all_type_vars,
     function_type,
     get_all_type_vars,
-    get_type_vars,
     make_simplified_union,
     supported_self_type,
     tuple_fallback,
@@ -1196,31 +1195,36 @@ def analyze_class_attribute_access(
 
         if isinstance(node.node, Var):
             assert isuper is not None
+            object_type = get_proper_type(mx.self_type)
             # Check if original variable type has type variables. For example:
             #     class C(Generic[T]):
             #         x: T
             #     C.x  # Error, ambiguous access
             #     C[int].x  # Also an error, since C[int] is same as C at runtime
             # Exception is Self type wrapped in ClassVar, that is safe.
+            prohibit_self = not node.node.is_classvar
             def_vars = set(node.node.info.defn.type_vars)
-            if not node.node.is_classvar and node.node.info.self_type:
+            if prohibit_self and node.node.info.self_type:
                 def_vars.add(node.node.info.self_type)
-            # TODO: should we include ParamSpec etc. here (i.e. use get_all_type_vars)?
-            typ_vars = set(get_type_vars(t))
-            if def_vars & typ_vars:
-                # Exception: access on Type[...], including first argument of class methods is OK.
-                if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit:
-                    if node.node.is_classvar:
-                        message = message_registry.GENERIC_CLASS_VAR_ACCESS
-                    else:
-                        message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
-                    mx.fail(message)
+            # Exception: access on Type[...], including first argument of class methods is OK.
+            prohibit_generic = not isinstance(object_type, TypeType) or node.implicit
+            if prohibit_generic and def_vars & set(get_all_type_vars(t)):
+                if node.node.is_classvar:
+                    message = message_registry.GENERIC_CLASS_VAR_ACCESS
+                else:
+                    message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS
+                mx.fail(message)
             t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True)
+            t = expand_type_by_instance(t, isuper)
             # Erase non-mapped variables, but keep mapped ones, even if there is an error.
             # In the above example this means that we infer following types:
             #     C.x -> Any
             #     C[int].x -> int
-            t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars})
+            if prohibit_generic:
+                erase_vars = set(itype.type.defn.type_vars)
+                if prohibit_self and itype.type.self_type:
+                    erase_vars.add(itype.type.self_type)
+                t = erase_typevars(t, {tv.id for tv in erase_vars})
 
         is_classmethod = (
             (is_decorated and cast(Decorator, node.node).func.is_class)
diff --git a/mypy/message_registry.py b/mypy/message_registry.py
index 3c7745876f87..381aedfca059 100644
--- a/mypy/message_registry.py
+++ b/mypy/message_registry.py
@@ -253,7 +253,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
     'Cannot override class variable (previously declared on base class "{}") with instance '
     "variable"
 )
-CLASS_VAR_WITH_TYPEVARS: Final = "ClassVar cannot contain type variables"
 CLASS_VAR_WITH_GENERIC_SELF: Final = "ClassVar cannot contain Self type in generic classes"
 CLASS_VAR_OUTSIDE_OF_CLASS: Final = "ClassVar can only be used for assignments in class body"
 
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 8f9d1c4f35d6..87aef2595caf 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5079,14 +5079,6 @@ def check_classvar(self, s: AssignmentStmt) -> None:
                 node.is_classvar = True
             analyzed = self.anal_type(s.type)
             assert self.type is not None
-            if analyzed is not None and set(get_type_vars(analyzed)) & set(
-                self.type.defn.type_vars
-            ):
-                # This means that we have a type var defined inside of a ClassVar.
-                # This is not allowed by PEP526.
-                # See https://github.com/python/mypy/issues/11538
-
-                self.fail(message_registry.CLASS_VAR_WITH_TYPEVARS, s)
             if (
                 analyzed is not None
                 and self.type.self_type in get_type_vars(analyzed)
diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test
index 63bbd7471bc8..8384e5624793 100644
--- a/test-data/unit/check-classvar.test
+++ b/test-data/unit/check-classvar.test
@@ -285,7 +285,7 @@ main:3: error: Cannot assign to class variable "x" via instance
 from typing import ClassVar, Generic, TypeVar
 T = TypeVar('T')
 class A(Generic[T]):
-    x: ClassVar[T]  # E: ClassVar cannot contain type variables
+    x: ClassVar[T]  # Error reported at access site
     @classmethod
     def foo(cls) -> T:
         return cls.x  # OK
@@ -308,7 +308,7 @@ from typing import ClassVar, Generic, Tuple, TypeVar, Union, Type
 T = TypeVar('T')
 U = TypeVar('U')
 class A(Generic[T, U]):
-    x: ClassVar[Union[T, Tuple[U, Type[U]]]]  # E: ClassVar cannot contain type variables
+    x: ClassVar[Union[T, Tuple[U, Type[U]]]]  # Error reported at access site
     @classmethod
     def foo(cls) -> Union[T, Tuple[U, Type[U]]]:
         return cls.x  # OK
@@ -319,7 +319,9 @@ A[int, str].x  # E: Access to generic class variables is ambiguous
 
 class Bad(A[int, str]):
     pass
-Bad.x  # E: Access to generic class variables is ambiguous
+reveal_type(Bad.x)  # E: Access to generic class variables is ambiguous \
+                    # N: Revealed type is "Union[builtins.int, tuple[builtins.str, type[builtins.str]]]"
+reveal_type(Bad().x)  # N: Revealed type is "Union[builtins.int, tuple[builtins.str, type[builtins.str]]]"
 
 class Good(A[int, str]):
     x = 42
@@ -343,3 +345,18 @@ class C:
     g: ClassVar[Union[Callable[[C], int], int]] = f
 
 reveal_type(C().g)  # N: Revealed type is "Union[def () -> builtins.int, builtins.int]"
+
+[case testGenericSubclassAccessNoLeak]
+from typing import ClassVar, Generic, TypeVar
+
+T = TypeVar("T")
+class B(Generic[T]):
+    x: T
+    y: ClassVar[T]
+
+class C(B[T]): ...
+
+reveal_type(C.x)  # E: Access to generic instance variables via class is ambiguous \
+                  # N: Revealed type is "Any"
+reveal_type(C.y)  # E: Access to generic class variables is ambiguous \
+                  # N: Revealed type is "Any"
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 12d6133ec83f..88ca53c8ed66 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2220,6 +2220,28 @@ class C(A, B):  # OK: both methods take Self
     pass
 [builtins fixtures/tuple.pyi]
 
+[case testSelfTypeClassMethodNotSilentlyErased]
+from typing import Self, Optional
+
+class X:
+    _inst: Optional[Self] = None
+    @classmethod
+    def default(cls) -> Self:
+        reveal_type(cls._inst)  # N: Revealed type is "Union[Self`0, None]"
+        if cls._inst is None:
+            cls._inst = cls()
+        return cls._inst
+
+reveal_type(X._inst)  # E: Access to generic instance variables via class is ambiguous \
+                      # N: Revealed type is "Union[__main__.X, None]"
+reveal_type(X()._inst)  # N: Revealed type is "Union[__main__.X, None]"
+
+class Y(X): ...
+reveal_type(Y._inst)  # E: Access to generic instance variables via class is ambiguous \
+                      # N: Revealed type is "Union[__main__.Y, None]"
+reveal_type(Y()._inst)  # N: Revealed type is "Union[__main__.Y, None]"
+[builtins fixtures/tuple.pyi]
+
 [case testSelfInFuncDecoratedClassmethod]
 from collections.abc import Callable
 from typing import Self, TypeVar
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index c0c826d09c9e..f44758f7b51b 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -2629,6 +2629,28 @@ def test(*args: Unpack[tuple[T]]) -> int: ...
 reveal_type(fn(test))  # N: Revealed type is "def [T] (T`1) -> builtins.int"
 [builtins fixtures/tuple.pyi]
 
+[case testNoGenericTypeVarTupleClassVarAccess]
+from typing import Generic, Tuple, TypeVarTuple, Unpack
+
+Ts = TypeVarTuple("Ts")
+class C(Generic[Unpack[Ts]]):
+    x: Tuple[Unpack[Ts]]
+
+reveal_type(C.x)  # E: Access to generic instance variables via class is ambiguous \
+                  # N: Revealed type is "builtins.tuple[Any, ...]"
+
+class Bad(C[int, int]):
+    pass
+reveal_type(Bad.x)  # E: Access to generic instance variables via class is ambiguous \
+                    # N: Revealed type is "tuple[builtins.int, builtins.int]"
+reveal_type(Bad().x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
+
+class Good(C[int, int]):
+    x = (1, 1)
+reveal_type(Good.x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
+reveal_type(Good().x)  # N: Revealed type is "tuple[builtins.int, builtins.int]"
+[builtins fixtures/tuple.pyi]
+
 [case testConstraintsIncludeTupleFallback]
 from typing import Generic, TypeVar
 from typing_extensions import TypeVarTuple, Unpack
diff --git a/test-data/unit/semanal-classvar.test b/test-data/unit/semanal-classvar.test
index a7bcec0324dc..8add559bdd27 100644
--- a/test-data/unit/semanal-classvar.test
+++ b/test-data/unit/semanal-classvar.test
@@ -207,14 +207,3 @@ class B:
         pass
 [out]
 main:4: error: ClassVar can only be used for assignments in class body
-
-[case testClassVarWithTypeVariable]
-from typing import ClassVar, TypeVar, Generic, List
-
-T = TypeVar('T')
-
-class Some(Generic[T]):
-    error: ClassVar[T]  # E: ClassVar cannot contain type variables
-    nested: ClassVar[List[List[T]]]  # E: ClassVar cannot contain type variables
-    ok: ClassVar[int]
-[out]

From 96fcd59585d1460440967f5dd6bdc82d43703187 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 19 Jun 2025 22:57:43 +0100
Subject: [PATCH 1395/1617] Re-widen custom properties after narrowing (#19296)

Fixes https://github.com/python/mypy/issues/10399

This is another smaller cleanup for
https://github.com/python/mypy/issues/7724. The current logic as
documented is IMO correct, for attributes (either properties or custom
descriptors) with setter type different from getter type, we narrow the
attribute type in an assignment if:
* The attribute is "normalizing", i.e. getter type is a subtype of
setter type (e.g. `Sequence[Employee]` is normalized to `tuple[Employee,
...]`)
* The given r.h.s. type in the assignment is a subtype of getter type
(and thus transitively the setter as well), e.g. `tuple[Manager, ...]`
vs `tuple[Employee, ...]` in the example above.

The problem was that this logic was implemented too literally, as a
result assignments that didn't satisfy these two rules were simply
ignored (thus making previous narrowing incorrectly "sticky"). In fact,
we also need to re-widen previously narrowed types whenever second
condition is not satisfied.

(I also decided to rename one variable name to make it more obvious.)

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypy/checker.py                     | 18 ++++++++----------
 test-data/unit/check-narrowing.test | 19 +++++++++++++++++++
 2 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 9d02bcac8471..bfacf7f882e0 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -4594,7 +4594,7 @@ def check_member_assignment(
         self,
         lvalue: MemberExpr,
         instance_type: Type,
-        attribute_type: Type,
+        set_lvalue_type: Type,
         rvalue: Expression,
         context: Context,
     ) -> tuple[Type, Type, bool]:
@@ -4611,23 +4611,21 @@ def check_member_assignment(
         if (isinstance(instance_type, FunctionLike) and instance_type.is_type_obj()) or isinstance(
             instance_type, TypeType
         ):
-            rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context)
-            return rvalue_type, attribute_type, True
+            rvalue_type, _ = self.check_simple_assignment(set_lvalue_type, rvalue, context)
+            return rvalue_type, set_lvalue_type, True
 
         with self.msg.filter_errors(filter_deprecated=True):
             get_lvalue_type = self.expr_checker.analyze_ordinary_member_access(
                 lvalue, is_lvalue=False
             )
 
-        # Special case: if the rvalue_type is a subtype of both '__get__' and '__set__' types,
-        # and '__get__' type is narrower than '__set__', then we invoke the binder to narrow type
+        # Special case: if the rvalue_type is a subtype of '__get__' type, and
+        # '__get__' type is narrower than '__set__', then we invoke the binder to narrow type
         # by this assignment. Technically, this is not safe, but in practice this is
         # what a user expects.
-        rvalue_type, _ = self.check_simple_assignment(attribute_type, rvalue, context)
-        infer = is_subtype(rvalue_type, get_lvalue_type) and is_subtype(
-            get_lvalue_type, attribute_type
-        )
-        return rvalue_type if infer else attribute_type, attribute_type, infer
+        rvalue_type, _ = self.check_simple_assignment(set_lvalue_type, rvalue, context)
+        rvalue_type = rvalue_type if is_subtype(rvalue_type, get_lvalue_type) else get_lvalue_type
+        return rvalue_type, set_lvalue_type, is_subtype(get_lvalue_type, set_lvalue_type)
 
     def check_indexed_assignment(
         self, lvalue: IndexExpr, rvalue: Expression, context: Context
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 3778c5276576..0443bc845907 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2593,3 +2593,22 @@ def baz(item: Base) -> None:
         reveal_type(item)  # N: Revealed type is "__main__."
         item.bar()
 [builtins fixtures/isinstance.pyi]
+
+[case testCustomSetterNarrowingReWidened]
+class B: ...
+class C(B): ...
+class C1(B): ...
+class D(C): ...
+
+class Test:
+    @property
+    def foo(self) -> C: ...
+    @foo.setter
+    def foo(self, val: B) -> None: ...
+
+t: Test
+t.foo = D()
+reveal_type(t.foo)  # N: Revealed type is "__main__.D"
+t.foo = C1()
+reveal_type(t.foo)  # N: Revealed type is "__main__.C"
+[builtins fixtures/property.pyi]

From ffb692884f9e84ac2d2ec141996ba07755219090 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Fri, 20 Jun 2025 11:44:22 -0400
Subject: [PATCH 1396/1617] [mypyc] feat(docs): detail issue with
 inspect.iscoroutinefunction (#19309)

Silly little PR, but this note could have saved me some debugging time.

I also opened [an issue](https://github.com/mypyc/mypyc/issues/1110) and
will make an attempt to solve the problem sometime in Q3.
---
 mypyc/doc/differences_from_python.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst
index 65ad709677af..5a230bd984c2 100644
--- a/mypyc/doc/differences_from_python.rst
+++ b/mypyc/doc/differences_from_python.rst
@@ -317,6 +317,7 @@ non-exhaustive list of what won't work:
 - Frames of compiled functions can't be inspected using ``inspect``
 - Compiled methods aren't considered methods by ``inspect.ismethod``
 - ``inspect.signature`` chokes on compiled functions
+- ``inspect.iscoroutinefunction`` and ``asyncio.iscoroutinefunction`` will always return False for compiled functions, even those defined with `async def`
 
 Profiling hooks and tracing
 ***************************

From 4322d4f443ef7568b1c0f4fec8df9e12e90fd8b9 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Fri, 20 Jun 2025 17:48:30 +0200
Subject: [PATCH 1397/1617] Improve the handling of "iteration dependent"
 errors and notes in finally clauses. (#19270)

Fixes #19269

This PR refactors the logic implemented in #19118 (which only targeted
repeatedly checked loops) and applies it to repeatedly checked finally
clauses.

I moved nearly all relevant code to the class `LoopErrorWatcher`, which
now has the more general name `IterationErrorWatcher`, to avoid code
duplication. However, one duplication is left, which concerns error
reporting. It would be nice and easy to move this functionality to
`IterationErrorWatcher`, too, but this would result in import cycles,
and I am unsure if working with `TYPE_CHECKING` and postponed importing
is acceptable in such cases (both for Mypy and Mypyc).

After the refactoring, it should not be much effort to apply the logic
to other cases where code sections are analysed iteratively. However,
the only thing that comes to my mind is the repeated checking of
functions with arguments that contain constrained type variables. I will
check it. If anyone finds a similar case and the solution is as simple
as expected, we could add the fix to this PR, of course.
---
 mypy/checker.py                              | 67 ++++++--------
 mypy/errors.py                               | 96 ++++++++++++++++----
 test-data/unit/check-narrowing.test          | 19 ++++
 test-data/unit/check-redefine2.test          |  3 +-
 test-data/unit/check-union-error-syntax.test | 21 +++++
 5 files changed, 148 insertions(+), 58 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index bfacf7f882e0..e05523a1aa05 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -25,7 +25,14 @@
 from mypy.constraints import SUPERTYPE_OF
 from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values
 from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode
-from mypy.errors import ErrorInfo, Errors, ErrorWatcher, LoopErrorWatcher, report_internal_error
+from mypy.errors import (
+    ErrorInfo,
+    Errors,
+    ErrorWatcher,
+    IterationDependentErrors,
+    IterationErrorWatcher,
+    report_internal_error,
+)
 from mypy.expandtype import expand_type
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash
 from mypy.maptype import map_instance_to_supertype
@@ -598,26 +605,15 @@ def accept_loop(
             # on without bound otherwise)
             widened_old = len(self.widened_vars)
 
-            # one set of `unreachable`, `redundant-expr`, and `redundant-casts` errors
-            # per iteration step:
-            uselessness_errors = []
-            # one set of unreachable line numbers per iteration step:
-            unreachable_lines = []
-            # one set of revealed types per line where `reveal_type` is used (each
-            # created set can grow during the iteration):
-            revealed_types = defaultdict(set)
+            iter_errors = IterationDependentErrors()
             iter = 1
             while True:
                 with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1):
                     if on_enter_body is not None:
                         on_enter_body()
 
-                    with LoopErrorWatcher(self.msg.errors) as watcher:
+                    with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
                         self.accept(body)
-                    uselessness_errors.append(watcher.uselessness_errors)
-                    unreachable_lines.append(watcher.unreachable_lines)
-                    for key, values in watcher.revealed_types.items():
-                        revealed_types[key].update(values)
 
                 partials_new = sum(len(pts.map) for pts in self.partial_types)
                 widened_new = len(self.widened_vars)
@@ -639,29 +635,10 @@ def accept_loop(
                 if iter == 20:
                     raise RuntimeError("Too many iterations when checking a loop")
 
-            # Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
-            # errors that could not be ruled out in any iteration step:
-            persistent_uselessness_errors = set()
-            for candidate in set(itertools.chain(*uselessness_errors)):
-                if all(
-                    (candidate in errors) or (candidate[2] in lines)
-                    for errors, lines in zip(uselessness_errors, unreachable_lines)
-                ):
-                    persistent_uselessness_errors.add(candidate)
-            for error_info in persistent_uselessness_errors:
-                context = Context(line=error_info[2], column=error_info[3])
-                context.end_line = error_info[4]
-                context.end_column = error_info[5]
-                self.msg.fail(error_info[1], context, code=error_info[0])
-
-            #  Report all types revealed in at least one iteration step:
-            for note_info, types in revealed_types.items():
-                sorted_ = sorted(types, key=lambda typ: typ.lower())
-                revealed = sorted_[0] if len(types) == 1 else f"Union[{', '.join(sorted_)}]"
-                context = Context(line=note_info[1], column=note_info[2])
-                context.end_line = note_info[3]
-                context.end_column = note_info[4]
-                self.note(f'Revealed type is "{revealed}"', context)
+            for error_info in watcher.yield_error_infos():
+                self.msg.fail(*error_info[:2], code=error_info[2])
+            for note_info in watcher.yield_note_infos(self.options):
+                self.note(*note_info)
 
             # If exit_condition is set, assume it must be False on exit from the loop:
             if exit_condition:
@@ -4960,6 +4937,9 @@ def type_check_raise(self, e: Expression, s: RaiseStmt, optional: bool = False)
 
     def visit_try_stmt(self, s: TryStmt) -> None:
         """Type check a try statement."""
+
+        iter_errors = None
+
         # Our enclosing frame will get the result if the try/except falls through.
         # This one gets all possible states after the try block exited abnormally
         # (by exception, return, break, etc.)
@@ -4974,7 +4954,9 @@ def visit_try_stmt(self, s: TryStmt) -> None:
             self.visit_try_without_finally(s, try_frame=bool(s.finally_body))
             if s.finally_body:
                 # First we check finally_body is type safe on all abnormal exit paths
-                self.accept(s.finally_body)
+                iter_errors = IterationDependentErrors()
+                with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
+                    self.accept(s.finally_body)
 
         if s.finally_body:
             # Then we try again for the more restricted set of options
@@ -4988,8 +4970,15 @@ def visit_try_stmt(self, s: TryStmt) -> None:
             # type checks in both contexts, but only the resulting types
             # from the latter context affect the type state in the code
             # that follows the try statement.)
+            assert iter_errors is not None
             if not self.binder.is_unreachable():
-                self.accept(s.finally_body)
+                with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
+                    self.accept(s.finally_body)
+
+            for error_info in watcher.yield_error_infos():
+                self.msg.fail(*error_info[:2], code=error_info[2])
+            for note_info in watcher.yield_note_infos(self.options):
+                self.msg.note(*note_info)
 
     def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None:
         """Type check a try statement, ignoring the finally block.
diff --git a/mypy/errors.py b/mypy/errors.py
index 7a173f16d196..41a4de639236 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -4,13 +4,15 @@
 import sys
 import traceback
 from collections import defaultdict
-from collections.abc import Iterable
+from collections.abc import Iterable, Iterator
+from itertools import chain
 from typing import Callable, Final, NoReturn, Optional, TextIO, TypeVar
 from typing_extensions import Literal, Self, TypeAlias as _TypeAlias
 
 from mypy import errorcodes as codes
 from mypy.error_formatter import ErrorFormatter
 from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes
+from mypy.nodes import Context
 from mypy.options import Options
 from mypy.scope import Scope
 from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file
@@ -219,23 +221,43 @@ def filtered_errors(self) -> list[ErrorInfo]:
         return self._filtered
 
 
-class LoopErrorWatcher(ErrorWatcher):
-    """Error watcher that filters and separately collects `unreachable` errors,
-    `redundant-expr` and `redundant-casts` errors, and revealed types when analysing
-    loops iteratively to help avoid making too-hasty reports."""
+class IterationDependentErrors:
+    """An `IterationDependentErrors` instance serves to collect the `unreachable`,
+    `redundant-expr`, and `redundant-casts` errors, as well as the revealed types,
+    handled by the individual `IterationErrorWatcher` instances sequentially applied to
+    the same code section."""
 
-    # Meaning of the tuple items: ErrorCode, message, line, column, end_line, end_column:
-    uselessness_errors: set[tuple[ErrorCode, str, int, int, int, int]]
+    # One set of `unreachable`, `redundant-expr`, and `redundant-casts` errors per
+    # iteration step.  Meaning of the tuple items: ErrorCode, message, line, column,
+    # end_line, end_column.
+    uselessness_errors: list[set[tuple[ErrorCode, str, int, int, int, int]]]
 
-    # Meaning of the tuple items: function_or_member, line, column, end_line, end_column:
+    # One set of unreachable line numbers per iteration step.  Not only the lines where
+    # the error report occurs but really all unreachable lines.
+    unreachable_lines: list[set[int]]
+
+    # One set of revealed types for each `reveal_type` statement.  Each created set can
+    # grow during the iteration.  Meaning of the tuple items: function_or_member, line,
+    # column, end_line, end_column:
     revealed_types: dict[tuple[str | None, int, int, int, int], set[str]]
 
-    # Not only the lines where the error report occurs but really all unreachable lines:
-    unreachable_lines: set[int]
+    def __init__(self) -> None:
+        self.uselessness_errors = []
+        self.unreachable_lines = []
+        self.revealed_types = defaultdict(set)
+
+
+class IterationErrorWatcher(ErrorWatcher):
+    """Error watcher that filters and separately collects `unreachable` errors,
+    `redundant-expr` and `redundant-casts` errors, and revealed types when analysing
+    code sections iteratively to help avoid making too-hasty reports."""
+
+    iteration_dependent_errors: IterationDependentErrors
 
     def __init__(
         self,
         errors: Errors,
+        iteration_dependent_errors: IterationDependentErrors,
         *,
         filter_errors: bool | Callable[[str, ErrorInfo], bool] = False,
         save_filtered_errors: bool = False,
@@ -247,31 +269,71 @@ def __init__(
             save_filtered_errors=save_filtered_errors,
             filter_deprecated=filter_deprecated,
         )
-        self.uselessness_errors = set()
-        self.unreachable_lines = set()
-        self.revealed_types = defaultdict(set)
+        self.iteration_dependent_errors = iteration_dependent_errors
+        iteration_dependent_errors.uselessness_errors.append(set())
+        iteration_dependent_errors.unreachable_lines.append(set())
 
     def on_error(self, file: str, info: ErrorInfo) -> bool:
+        """Filter out the "iteration-dependent" errors and notes and store their
+        information to handle them after iteration is completed."""
+
+        iter_errors = self.iteration_dependent_errors
 
         if info.code in (codes.UNREACHABLE, codes.REDUNDANT_EXPR, codes.REDUNDANT_CAST):
-            self.uselessness_errors.add(
+            iter_errors.uselessness_errors[-1].add(
                 (info.code, info.message, info.line, info.column, info.end_line, info.end_column)
             )
             if info.code == codes.UNREACHABLE:
-                self.unreachable_lines.update(range(info.line, info.end_line + 1))
+                iter_errors.unreachable_lines[-1].update(range(info.line, info.end_line + 1))
             return True
 
         if info.code == codes.MISC and info.message.startswith("Revealed type is "):
             key = info.function_or_member, info.line, info.column, info.end_line, info.end_column
             types = info.message.split('"')[1]
             if types.startswith("Union["):
-                self.revealed_types[key].update(types[6:-1].split(", "))
+                iter_errors.revealed_types[key].update(types[6:-1].split(", "))
             else:
-                self.revealed_types[key].add(types)
+                iter_errors.revealed_types[key].add(types)
             return True
 
         return super().on_error(file, info)
 
+    def yield_error_infos(self) -> Iterator[tuple[str, Context, ErrorCode]]:
+        """Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
+        errors that could not be ruled out in any iteration step."""
+
+        persistent_uselessness_errors = set()
+        iter_errors = self.iteration_dependent_errors
+        for candidate in set(chain(*iter_errors.uselessness_errors)):
+            if all(
+                (candidate in errors) or (candidate[2] in lines)
+                for errors, lines in zip(
+                    iter_errors.uselessness_errors, iter_errors.unreachable_lines
+                )
+            ):
+                persistent_uselessness_errors.add(candidate)
+        for error_info in persistent_uselessness_errors:
+            context = Context(line=error_info[2], column=error_info[3])
+            context.end_line = error_info[4]
+            context.end_column = error_info[5]
+            yield error_info[1], context, error_info[0]
+
+    def yield_note_infos(self, options: Options) -> Iterator[tuple[str, Context]]:
+        """Yield all types revealed in at least one iteration step."""
+
+        for note_info, types in self.iteration_dependent_errors.revealed_types.items():
+            sorted_ = sorted(types, key=lambda typ: typ.lower())
+            if len(types) == 1:
+                revealed = sorted_[0]
+            elif options.use_or_syntax():
+                revealed = " | ".join(sorted_)
+            else:
+                revealed = f"Union[{', '.join(sorted_)}]"
+            context = Context(line=note_info[1], column=note_info[2])
+            context.end_line = note_info[3]
+            context.end_column = note_info[4]
+            yield f'Revealed type is "{revealed}"', context
+
 
 class Errors:
     """Container for compile errors.
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 0443bc845907..7a053e1c5cab 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2446,6 +2446,25 @@ while x is not None and b():
     x = f()
 [builtins fixtures/primitives.pyi]
 
+[case testAvoidFalseUnreachableInFinally]
+# flags: --allow-redefinition-new --local-partial-types --warn-unreachable
+def f() -> None:
+    try:
+        x = 1
+        if int():
+            x = ""
+            return
+        if int():
+            x = None
+            return
+    finally:
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, None]"
+        if isinstance(x, str):
+            reveal_type(x)  # N: Revealed type is "builtins.str"
+        reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]"
+
+[builtins fixtures/isinstancelist.pyi]
+
 [case testNarrowingTypeVarMultiple]
 from typing import TypeVar
 
diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test
index 1062be6976c0..924e66584669 100644
--- a/test-data/unit/check-redefine2.test
+++ b/test-data/unit/check-redefine2.test
@@ -791,8 +791,7 @@ def f3() -> None:
             x = ""
             return
     finally:
-        reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" \
-            # N: Revealed type is "builtins.int"
+        reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
     reveal_type(x) # N: Revealed type is "builtins.int"
 
 def f4() -> None:
diff --git a/test-data/unit/check-union-error-syntax.test b/test-data/unit/check-union-error-syntax.test
index 3c541173a891..d41281b774e1 100644
--- a/test-data/unit/check-union-error-syntax.test
+++ b/test-data/unit/check-union-error-syntax.test
@@ -55,3 +55,24 @@ from typing import Literal, Union
 x : Union[Literal[1], None]
 x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Optional[Literal[1]]")
 [builtins fixtures/tuple.pyi]
+
+[case testUnionSyntaxRecombined]
+# flags: --python-version 3.10 --force-union-syntax --allow-redefinition-new --local-partial-types
+# The following revealed type is recombined because the finally body is visited twice.
+try:
+    x = 1
+    x = ""
+finally:
+    reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+[builtins fixtures/isinstancelist.pyi]
+
+[case testOrSyntaxRecombined]
+# flags: --python-version 3.10 --no-force-union-syntax --allow-redefinition-new --local-partial-types
+# The following revealed type is recombined because the finally body is visited twice.
+# ToDo: Improve this recombination logic, especially (but not only) for the "or syntax".
+try:
+    x = 1
+    x = ""
+finally:
+    reveal_type(x)  # N: Revealed type is "builtins.int | builtins.str | builtins.str"
+[builtins fixtures/isinstancelist.pyi]

From b18d3f82a300b28273a030e619f2fc2a8eb7a9a0 Mon Sep 17 00:00:00 2001
From: Robsdedude 
Date: Fri, 20 Jun 2025 17:44:48 +0000
Subject: [PATCH 1398/1617] Fix metaclass resolution algorithm (#17713)

This PR fixes the algorithm for determining a classes metaclass.

Fixes #14033
---
 mypy/checker.py                    | 23 +++++++--------------
 mypy/nodes.py                      | 28 +++++++++++++++++--------
 test-data/unit/check-abstract.test |  8 ++++++--
 test-data/unit/check-classes.test  | 33 +++++++++++++++++++++++++++++-
 4 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index e05523a1aa05..70d3add74fd9 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2933,23 +2933,14 @@ def check_metaclass_compatibility(self, typ: TypeInfo) -> None:
         ):
             return  # Reasonable exceptions from this check
 
-        metaclasses = [
-            entry.metaclass_type
-            for entry in typ.mro[1:-1]
-            if entry.metaclass_type
-            and not is_named_instance(entry.metaclass_type, "builtins.type")
-        ]
-        if not metaclasses:
-            return
-        if typ.metaclass_type is not None and all(
-            is_subtype(typ.metaclass_type, meta) for meta in metaclasses
+        if typ.metaclass_type is None and any(
+            base.type.metaclass_type is not None for base in typ.bases
         ):
-            return
-        self.fail(
-            "Metaclass conflict: the metaclass of a derived class must be "
-            "a (non-strict) subclass of the metaclasses of all its bases",
-            typ,
-        )
+            self.fail(
+                "Metaclass conflict: the metaclass of a derived class must be "
+                "a (non-strict) subclass of the metaclasses of all its bases",
+                typ,
+            )
 
     def visit_import_from(self, node: ImportFrom) -> None:
         for name, _ in node.names:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 1b6884f04bf5..d69ff10346c3 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3382,15 +3382,25 @@ def calculate_metaclass_type(self) -> mypy.types.Instance | None:
             return declared
         if self._fullname == "builtins.type":
             return mypy.types.Instance(self, [])
-        candidates = [
-            s.declared_metaclass
-            for s in self.mro
-            if s.declared_metaclass is not None and s.declared_metaclass.type is not None
-        ]
-        for c in candidates:
-            if all(other.type in c.type.mro for other in candidates):
-                return c
-        return None
+
+        winner = declared
+        for super_class in self.mro[1:]:
+            super_meta = super_class.declared_metaclass
+            if super_meta is None or super_meta.type is None:
+                continue
+            if winner is None:
+                winner = super_meta
+                continue
+            if winner.type.has_base(super_meta.type.fullname):
+                continue
+            if super_meta.type.has_base(winner.type.fullname):
+                winner = super_meta
+                continue
+            # metaclass conflict
+            winner = None
+            break
+
+        return winner
 
     def is_metaclass(self, *, precise: bool = False) -> bool:
         return (
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
index 2fed3425c8d4..7507a31d115a 100644
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -571,8 +571,12 @@ from abc import abstractmethod, ABCMeta
 import typing
 
 class A(metaclass=ABCMeta): pass
-class B(object, A): pass \
-      # E: Cannot determine consistent method resolution order (MRO) for "B"
+class B(object, A, metaclass=ABCMeta): # E: Cannot determine consistent method resolution order (MRO) for "B"
+    pass
+
+class C(object, A):  # E: Cannot determine consistent method resolution order (MRO) for "C" \
+                     # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+    pass
 
 [case testOverloadedAbstractMethod]
 from foo import *
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 1b3de53567d1..7d2032ef25f0 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -7292,7 +7292,7 @@ class Conflict1(A1, B, E): ...  # E: Metaclass conflict: the metaclass of a deri
 class Conflict2(A, B): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
 class Conflict3(B, A): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
 
-class ChildOfConflict1(Conflict3): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class ChildOfConflict1(Conflict3): ...
 class ChildOfConflict2(Conflict3, metaclass=CorrectMeta): ...
 
 class ConflictingMeta(MyMeta1, MyMeta3): ...
@@ -7301,6 +7301,37 @@ class Conflict4(A1, B, E, metaclass=ConflictingMeta): ...  # E: Metaclass confli
 class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta):  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
     ...
 
+[case testMetaClassConflictIssue14033]
+class M1(type): pass
+class M2(type): pass
+class Mx(M1, M2): pass
+
+class A1(metaclass=M1): pass
+class A2(A1): pass
+
+class B1(metaclass=M2): pass
+
+class C1(metaclass=Mx): pass
+
+class TestABC(A2, B1, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class TestBAC(B1, A2, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+
+# should not warn again for children
+class ChildOfTestABC(TestABC): pass
+
+# no metaclass is assumed if super class has a metaclass conflict
+class ChildOfTestABCMetaMx(TestABC, metaclass=Mx): pass
+class ChildOfTestABCMetaM1(TestABC, metaclass=M1): pass
+
+class TestABCMx(A2, B1, C1, metaclass=Mx): pass
+class TestBACMx(B1, A2, C1, metaclass=Mx): pass
+
+class TestACB(A2, C1, B1): pass
+class TestBCA(B1, C1, A2): pass
+
+class TestCAB(C1, A2, B1): pass
+class TestCBA(C1, B1, A2): pass
+
 [case testGenericOverride]
 from typing import Generic, TypeVar, Any
 

From f97a56e76d8dfcffea740babddc6bb9e060c0be2 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 21 Jun 2025 01:27:43 +0200
Subject: [PATCH 1399/1617] Support running stubtest in non-UTF8 terminals
 (#19085)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes #19071. I looked through the `open()` calls in the codebase, and
only `reports.py` raises some concerns. Stubtest crashes due to this
`print` call with incompatible encoding.

I tested this on Linux with `LC_CTYPE=ru_RU.CP1251` (random non-utf8
locale I found in `/usr/share/i18n/SUPPORTED`) and confirmed that
`stubtest` crashes without the patch and passes with it.

Using a simple MRE (empty stub file and `A = "╙"` in a file, this symbol
is `$'\u2559'`), I got this:

```
error: package.A is not present in stub
Stub: in file /tmp/tmp.Cs4RioNSuR/demo/stub/package/__init__.pyi
MISSING
Runtime:
'?'

Found 1 error (checked 1 module)
```

Without the patch I get a crash - same as in the linked issue.
---
 mypy/stubtest.py | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index f9e6f7d337be..8ea9d786be22 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -2062,7 +2062,7 @@ def warning_callback(msg: str) -> None:
             if args.generate_allowlist:
                 generated_allowlist.add(error.object_desc)
                 continue
-            print(error.get_description(concise=args.concise))
+            safe_print(error.get_description(concise=args.concise))
             error_count += 1
 
     # Print unused allowlist entries
@@ -2102,6 +2102,19 @@ def warning_callback(msg: str) -> None:
     return exit_code
 
 
+def safe_print(text: str) -> None:
+    """Print a text replacing chars not representable in stdout encoding."""
+    # If `sys.stdout` encoding is not the same as out (usually UTF8) string,
+    # if may cause painful crashes. I don't want to reconfigure `sys.stdout`
+    # to do `errors = "replace"` as that sounds scary.
+    out_encoding = sys.stdout.encoding
+    if out_encoding is not None:
+        # Can be None if stdout is replaced (including our own tests). This should be
+        # safe to omit if the actual stream doesn't care about encoding.
+        text = text.encode(out_encoding, errors="replace").decode(out_encoding, errors="replace")
+    print(text)
+
+
 def parse_options(args: list[str]) -> _Arguments:
     parser = argparse.ArgumentParser(
         description="Compares stubs to objects introspected from the runtime."

From 0c26253fb16ca4fae89618e893a1ad5a2e553ed5 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 21 Jun 2025 01:35:17 +0200
Subject: [PATCH 1400/1617] Ignore overload impl when checking __OP__ and
 __rOP__ compatibility (#18502)

Fixes #18498
---
 mypy/checker.py                   | 23 +++++++++++--
 test-data/unit/check-classes.test | 54 +++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 70d3add74fd9..dbf2160d6988 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -107,6 +107,7 @@
     OperatorAssignmentStmt,
     OpExpr,
     OverloadedFuncDef,
+    OverloadPart,
     PassStmt,
     PromoteExpr,
     RaiseStmt,
@@ -407,6 +408,11 @@ def __init__(
         # argument through various `checker` and `checkmember` functions.
         self._is_final_def = False
 
+        # Track when we enter an overload implementation. Some checks should not be applied
+        # to the implementation signature when specific overloads are available.
+        # Use `enter_overload_impl` to modify.
+        self.overload_impl_stack: list[OverloadPart] = []
+
         # This flag is set when we run type-check or attribute access check for the purpose
         # of giving a note on possibly missing "await". It is used to avoid infinite recursion.
         self.checking_missing_await = False
@@ -709,7 +715,8 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
         if num_abstract not in (0, len(defn.items)):
             self.fail(message_registry.INCONSISTENT_ABSTRACT_OVERLOAD, defn)
         if defn.impl:
-            defn.impl.accept(self)
+            with self.enter_overload_impl(defn.impl):
+                defn.impl.accept(self)
         if not defn.is_property:
             self.check_overlapping_overloads(defn)
             if defn.type is None:
@@ -752,6 +759,14 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
             self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl)
             self.check_inplace_operator_method(defn)
 
+    @contextmanager
+    def enter_overload_impl(self, impl: OverloadPart) -> Iterator[None]:
+        self.overload_impl_stack.append(impl)
+        try:
+            yield
+        finally:
+            assert self.overload_impl_stack.pop() == impl
+
     def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> CallableType | None:
         """Get type as seen by an overload item caller."""
         inner_type = get_proper_type(inner_type)
@@ -1278,7 +1293,11 @@ def check_func_def(
                     )
 
                 if name:  # Special method names
-                    if defn.info and self.is_reverse_op_method(name):
+                    if (
+                        defn.info
+                        and self.is_reverse_op_method(name)
+                        and defn not in self.overload_impl_stack
+                    ):
                         self.check_reverse_op_method(item, typ, name, defn)
                     elif name in ("__getattr__", "__getattribute__"):
                         self.check_getattr_method(typ, defn, name)
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 7d2032ef25f0..bf6c51e86446 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -2487,6 +2487,60 @@ reveal_type(Num3() + Num1())  # N: Revealed type is "__main__.Num3"
 reveal_type(Num2() + Num3())  # N: Revealed type is "__main__.Num2"
 reveal_type(Num3() + Num2())  # N: Revealed type is "__main__.Num3"
 
+[case testReverseOperatorWithOverloads3]
+from typing import Union, overload
+
+class A:
+    def __mul__(self, value: A, /) -> A: ...
+    def __rmul__(self, value: A, /) -> A: ...
+
+class B:
+    @overload
+    def __mul__(self, other: B, /) -> B: ...
+    @overload
+    def __mul__(self, other: A, /) -> str: ...
+    def __mul__(self, other: Union[B, A], /) -> Union[B, str]: pass
+
+    @overload
+    def __rmul__(self, other: B, /) -> B: ...
+    @overload
+    def __rmul__(self, other: A, /) -> str: ...
+    def __rmul__(self, other: Union[B, A], /) -> Union[B, str]: pass
+
+[case testReverseOperatorWithOverloadsNested]
+from typing import Union, overload
+
+class A:
+    def __mul__(self, value: A, /) -> A: ...
+    def __rmul__(self, value: A, /) -> A: ...
+
+class B:
+    @overload
+    def __mul__(self, other: B, /) -> B: ...
+    @overload
+    def __mul__(self, other: A, /) -> str: ...
+    def __mul__(self, other: Union[B, A], /) -> Union[B, str]: pass
+
+    @overload
+    def __rmul__(self, other: B, /) -> B: ...
+    @overload
+    def __rmul__(self, other: A, /) -> str: ...
+    def __rmul__(self, other: Union[B, A], /) -> Union[B, str]:
+        class A1:
+            def __add__(self, other: C1) -> int: ...
+
+        class B1:
+            def __add__(self, other: C1) -> int: ...
+
+        class C1:
+            @overload
+            def __radd__(self, other: A1) -> str: ...   # E: Signatures of "__radd__" of "C1" and "__add__" of "A1" are unsafely overlapping
+            @overload
+            def __radd__(self, other: B1) -> str: ...   # E: Signatures of "__radd__" of "C1" and "__add__" of "B1" are unsafely overlapping
+            def __radd__(self, other): pass
+
+        return ""
+
 [case testDivReverseOperator]
 # No error: __div__ has no special meaning in Python 3
 class A1:

From 0755a61b9528beca20c468e15e7c49e7b82671c8 Mon Sep 17 00:00:00 2001
From: Charlie Denton 
Date: Sat, 21 Jun 2025 01:08:46 +0100
Subject: [PATCH 1401/1617] Type ignore comments erroneously marked as unused
 by dmypy (#15043)

There is currently a misbehaviour where "type: ignore" comments are
erroneously marked as unused in re-runs of dmypy. There are also cases
where errors disappear on the re-run.

As far as I can tell, this only happens in modules which contain an
import that we don't know how to type (such as a module which does not
exist), and a submodule which is unused.

There was a lot of commenting and investigation on this PR, but I hope
that the committed tests and fixes illustrate and address the issue.

Related to https://github.com/python/mypy/issues/9655

---------

Co-authored-by: David Seddon 
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: Ivan Levkivskyi 
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypy/errors.py                   |   4 +
 mypy/server/update.py            |   2 +
 test-data/unit/daemon.test       | 137 +++++++++++++++++++++++++++++++
 test-data/unit/fine-grained.test |  24 ++++++
 4 files changed, 167 insertions(+)

diff --git a/mypy/errors.py b/mypy/errors.py
index 41a4de639236..5dd411c39e95 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -848,6 +848,8 @@ def generate_unused_ignore_errors(self, file: str) -> None:
                 code=codes.UNUSED_IGNORE,
                 blocker=False,
                 only_once=False,
+                origin=(self.file, [line]),
+                target=self.target_module,
             )
             self._add_error_info(file, info)
 
@@ -899,6 +901,8 @@ def generate_ignore_without_code_errors(
                 code=codes.IGNORE_WITHOUT_CODE,
                 blocker=False,
                 only_once=False,
+                origin=(self.file, [line]),
+                target=self.target_module,
             )
             self._add_error_info(file, info)
 
diff --git a/mypy/server/update.py b/mypy/server/update.py
index 9891e2417b94..ea336154ae56 100644
--- a/mypy/server/update.py
+++ b/mypy/server/update.py
@@ -668,6 +668,8 @@ def restore(ids: list[str]) -> None:
     state.type_check_first_pass()
     state.type_check_second_pass()
     state.detect_possibly_undefined_vars()
+    state.generate_unused_ignore_notes()
+    state.generate_ignore_without_code_notes()
     t2 = time.time()
     state.finish_passes()
     t3 = time.time()
diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test
index ad3b51b27dfb..295eb4000d81 100644
--- a/test-data/unit/daemon.test
+++ b/test-data/unit/daemon.test
@@ -648,6 +648,143 @@ from demo.test import a
 [file demo/test.py]
 a: int
 
+[case testUnusedTypeIgnorePreservedOnRerun]
+-- Regression test for https://github.com/python/mypy/issues/9655
+$ dmypy start -- --warn-unused-ignores --no-error-summary --hide-error-codes
+Daemon started
+$ dmypy check -- bar.py
+bar.py:2: error: Unused "type: ignore" comment
+== Return code: 1
+$ dmypy check -- bar.py
+bar.py:2: error: Unused "type: ignore" comment
+== Return code: 1
+
+[file foo/__init__.py]
+[file foo/empty.py]
+[file bar.py]
+from foo.empty import *
+a = 1  # type: ignore
+
+[case testTypeIgnoreWithoutCodePreservedOnRerun]
+-- Regression test for https://github.com/python/mypy/issues/9655
+$ dmypy start -- --enable-error-code ignore-without-code --no-error-summary
+Daemon started
+$ dmypy check -- bar.py
+bar.py:2: error: "type: ignore" comment without error code  [ignore-without-code]
+== Return code: 1
+$ dmypy check -- bar.py
+bar.py:2: error: "type: ignore" comment without error code  [ignore-without-code]
+== Return code: 1
+
+[file foo/__init__.py]
+[file foo/empty.py]
+[file bar.py]
+from foo.empty import *
+a = 1  # type: ignore
+
+[case testPossiblyUndefinedVarsPreservedAfterRerun]
+-- Regression test for https://github.com/python/mypy/issues/9655
+$ dmypy start -- --enable-error-code possibly-undefined --no-error-summary
+Daemon started
+$ dmypy check -- bar.py
+bar.py:4: error: Name "a" may be undefined  [possibly-undefined]
+== Return code: 1
+$ dmypy check -- bar.py
+bar.py:4: error: Name "a" may be undefined  [possibly-undefined]
+== Return code: 1
+
+[file foo/__init__.py]
+[file foo/empty.py]
+[file bar.py]
+from foo.empty import *
+if False:
+    a = 1
+a
+
+[case testUnusedTypeIgnorePreservedOnRerunWithIgnoredMissingImports]
+$ dmypy start -- --no-error-summary --ignore-missing-imports --warn-unused-ignores
+Daemon started
+$ dmypy check foo
+foo/main.py:3: error: Unused "type: ignore" comment  [unused-ignore]
+== Return code: 1
+$ dmypy check foo
+foo/main.py:3: error: Unused "type: ignore" comment  [unused-ignore]
+== Return code: 1
+
+[file unused/__init__.py]
+[file unused/submodule.py]
+[file foo/empty.py]
+[file foo/__init__.py]
+from foo.main import *
+from unused.submodule import *
+[file foo/main.py]
+from foo import empty
+from foo.does_not_exist import *
+a = 1  # type: ignore
+
+[case testModuleDoesNotExistPreservedOnRerun]
+$ dmypy start -- --no-error-summary --ignore-missing-imports
+Daemon started
+$ dmypy check foo
+foo/main.py:1: error: Module "foo" has no attribute "does_not_exist"  [attr-defined]
+== Return code: 1
+$ dmypy check foo
+foo/main.py:1: error: Module "foo" has no attribute "does_not_exist"  [attr-defined]
+== Return code: 1
+
+[file unused/__init__.py]
+[file unused/submodule.py]
+[file foo/__init__.py]
+from foo.main import *
+[file foo/main.py]
+from foo import does_not_exist
+from unused.submodule import *
+
+[case testReturnTypeIgnoreAfterUnknownImport]
+-- Return type ignores after unknown imports and unused modules are respected on the second pass.
+$ dmypy start -- --warn-unused-ignores --no-error-summary
+Daemon started
+$ dmypy check -- foo.py
+foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist"  [import-not-found]
+foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+== Return code: 1
+$ dmypy check -- foo.py
+foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist"  [import-not-found]
+foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+== Return code: 1
+
+[file unused/__init__.py]
+[file unused/empty.py]
+[file foo.py]
+from unused.empty import *
+import a_module_which_does_not_exist
+def is_foo() -> str:
+    return True  # type: ignore
+
+[case testAttrsTypeIgnoreAfterUnknownImport]
+$ dmypy start -- --warn-unused-ignores --no-error-summary
+Daemon started
+$ dmypy check -- foo.py
+foo.py:3: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist"  [import-not-found]
+foo.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+== Return code: 1
+$ dmypy check -- foo.py
+foo.py:3: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist"  [import-not-found]
+foo.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+== Return code: 1
+
+[file unused/__init__.py]
+[file unused/empty.py]
+[file foo.py]
+import attr
+from unused.empty import *
+import a_module_which_does_not_exist
+
+@attr.frozen
+class A:
+    def __init__(self) -> None:
+        self.__attrs_init__()  # type: ignore[attr-defined]
+
 [case testDaemonImportAncestors]
 $ dmypy run test.py
 Daemon started
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 7e34a2352dd6..222e38ea0280 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -10540,6 +10540,30 @@ from pkg.sub import modb
 [out]
 ==
 
+[case testUnusedTypeIgnorePreservedAfterChange]
+# flags: --warn-unused-ignores --no-error-summary
+[file main.py]
+a = 1  # type: ignore
+[file main.py.2]
+a = 1  # type: ignore
+# Comment to trigger reload.
+[out]
+main.py:1: error: Unused "type: ignore" comment
+==
+main.py:1: error: Unused "type: ignore" comment
+
+[case testTypeIgnoreWithoutCodePreservedAfterChange]
+# flags: --enable-error-code ignore-without-code --no-error-summary
+[file main.py]
+a = 1  # type: ignore
+[file main.py.2]
+a = 1  # type: ignore
+# Comment to trigger reload.
+[out]
+main.py:1: error: "type: ignore" comment without error code
+==
+main.py:1: error: "type: ignore" comment without error code
+
 [case testFineGrainedFunctoolsPartial]
 import m
 

From fabe37f31e5bd7396e6a1183e3ed56b6e767561b Mon Sep 17 00:00:00 2001
From: Daniel Hnyk 
Date: Sat, 21 Jun 2025 08:43:28 +0200
Subject: [PATCH 1402/1617] Fix nit in documentation example

---
 docs/source/generics.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/source/generics.rst b/docs/source/generics.rst
index 5d787d32b005..4755c4f17ec8 100644
--- a/docs/source/generics.rst
+++ b/docs/source/generics.rst
@@ -630,7 +630,7 @@ Let us illustrate this by few simple examples:
 
      my_circles: list[Circle] = []
      add_one(my_circles)     # This may appear safe, but...
-     my_circles[-1].rotate()  # ...this will fail, since my_circles[0] is now a Shape, not a Circle
+     my_circles[0].rotate()  # ...this will fail, since my_circles[0] is now a Shape, not a Circle
 
   Another example of invariant type is ``dict``. Most mutable containers
   are invariant.

From 34949c82274285b03423bde4def9beacd91c7d52 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sat, 21 Jun 2025 14:12:35 +0100
Subject: [PATCH 1403/1617] Tweaks to perf_compare.py script to reduce RAM
 usage (#19321)

Reduce default parallelism, since it could require many GBs of RAM. Add
--multi-file flag which reduces RAM usage further, but performance might
be slightly worse.
---
 misc/perf_compare.py | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/misc/perf_compare.py b/misc/perf_compare.py
index 025d4065561e..589912dd9826 100644
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -35,11 +35,13 @@ def heading(s: str) -> None:
     print()
 
 
-def build_mypy(target_dir: str) -> None:
+def build_mypy(target_dir: str, multi_file: bool) -> None:
     env = os.environ.copy()
     env["CC"] = "clang"
     env["MYPYC_OPT_LEVEL"] = "2"
     env["PYTHONHASHSEED"] = "1"
+    if multi_file:
+        env["MYPYC_MULTI_FILE"] = "1"
     cmd = [sys.executable, "setup.py", "--use-mypyc", "build_ext", "--inplace"]
     subprocess.run(cmd, env=env, check=True, cwd=target_dir)
 
@@ -110,6 +112,12 @@ def main() -> None:
         action="store_true",
         help="measure incremental run (fully cached)",
     )
+    parser.add_argument(
+        "--multi-file",
+        default=False,
+        action="store_true",
+        help="compile each mypy module to a separate C file (reduces RAM use)",
+    )
     parser.add_argument(
         "--dont-setup",
         default=False,
@@ -127,9 +135,9 @@ def main() -> None:
     parser.add_argument(
         "-j",
         metavar="N",
-        default=8,
+        default=4,
         type=int,
-        help="set maximum number of parallel builds (default=8)",
+        help="set maximum number of parallel builds (default=4) -- high numbers require a lot of RAM!",
     )
     parser.add_argument(
         "-r",
@@ -155,6 +163,7 @@ def main() -> None:
     args = parser.parse_args()
     incremental: bool = args.incremental
     dont_setup: bool = args.dont_setup
+    multi_file: bool = args.multi_file
     commits = args.commit
     num_runs: int = args.num_runs + 1
     max_workers: int = args.j
@@ -185,7 +194,9 @@ def main() -> None:
         print("(This will take a while...)")
 
         with ThreadPoolExecutor(max_workers=max_workers) as executor:
-            futures = [executor.submit(build_mypy, target_dir) for target_dir in target_dirs]
+            futures = [
+                executor.submit(build_mypy, target_dir, multi_file) for target_dir in target_dirs
+            ]
             for future in as_completed(futures):
                 future.result()
 

From 6886b7a17f586e3da881be1b23c41e48916c57e4 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sat, 21 Jun 2025 14:57:46 -0400
Subject: [PATCH 1404/1617] Fix `TypeIs` negative narrowing of union of
 generics (#18193)

Fixes #18009, fixes #19282, fixes #17181

Modelling the runtime behavior of `isinstance` (which erases generic
type arguments) isn't applicable to `TypeIs`. This PR adds a flag so
that we can skip that logic deep inside
`conditional_types_with_intersection`.

---------

Co-authored-by: Shantanu Jain 
---
 mypy/checker.py                  | 44 ++++++++++++--
 mypy/subtypes.py                 | 23 ++++++--
 test-data/unit/check-typeis.test | 99 ++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+), 12 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index dbf2160d6988..596564c98a40 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6185,6 +6185,7 @@ def find_isinstance_check_helper(
                                     self.lookup_type(expr),
                                     [TypeRange(node.callee.type_is, is_upper_bound=False)],
                                     expr,
+                                    consider_runtime_isinstance=False,
                                 ),
                             )
         elif isinstance(node, ComparisonExpr):
@@ -7612,11 +7613,19 @@ def conditional_types_with_intersection(
         type_ranges: list[TypeRange] | None,
         ctx: Context,
         default: None = None,
+        *,
+        consider_runtime_isinstance: bool = True,
     ) -> tuple[Type | None, Type | None]: ...
 
     @overload
     def conditional_types_with_intersection(
-        self, expr_type: Type, type_ranges: list[TypeRange] | None, ctx: Context, default: Type
+        self,
+        expr_type: Type,
+        type_ranges: list[TypeRange] | None,
+        ctx: Context,
+        default: Type,
+        *,
+        consider_runtime_isinstance: bool = True,
     ) -> tuple[Type, Type]: ...
 
     def conditional_types_with_intersection(
@@ -7625,8 +7634,15 @@ def conditional_types_with_intersection(
         type_ranges: list[TypeRange] | None,
         ctx: Context,
         default: Type | None = None,
+        *,
+        consider_runtime_isinstance: bool = True,
     ) -> tuple[Type | None, Type | None]:
-        initial_types = conditional_types(expr_type, type_ranges, default)
+        initial_types = conditional_types(
+            expr_type,
+            type_ranges,
+            default,
+            consider_runtime_isinstance=consider_runtime_isinstance,
+        )
         # For some reason, doing "yes_map, no_map = conditional_types_to_typemaps(...)"
         # doesn't work: mypyc will decide that 'yes_map' is of type None if we try.
         yes_type: Type | None = initial_types[0]
@@ -7938,18 +7954,30 @@ def visit_type_var(self, t: TypeVarType) -> None:
 
 @overload
 def conditional_types(
-    current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: None = None
+    current_type: Type,
+    proposed_type_ranges: list[TypeRange] | None,
+    default: None = None,
+    *,
+    consider_runtime_isinstance: bool = True,
 ) -> tuple[Type | None, Type | None]: ...
 
 
 @overload
 def conditional_types(
-    current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: Type
+    current_type: Type,
+    proposed_type_ranges: list[TypeRange] | None,
+    default: Type,
+    *,
+    consider_runtime_isinstance: bool = True,
 ) -> tuple[Type, Type]: ...
 
 
 def conditional_types(
-    current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: Type | None = None
+    current_type: Type,
+    proposed_type_ranges: list[TypeRange] | None,
+    default: Type | None = None,
+    *,
+    consider_runtime_isinstance: bool = True,
 ) -> tuple[Type | None, Type | None]:
     """Takes in the current type and a proposed type of an expression.
 
@@ -7991,7 +8019,11 @@ def conditional_types(
                     if not type_range.is_upper_bound
                 ]
             )
-            remaining_type = restrict_subtype_away(current_type, proposed_precise_type)
+            remaining_type = restrict_subtype_away(
+                current_type,
+                proposed_precise_type,
+                consider_runtime_isinstance=consider_runtime_isinstance,
+            )
             return proposed_type, remaining_type
     else:
         # An isinstance check, but we don't understand the type
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index a5e6938615e7..143d6783f43e 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -2073,7 +2073,7 @@ def try_restrict_literal_union(t: UnionType, s: Type) -> list[Type] | None:
     return new_items
 
 
-def restrict_subtype_away(t: Type, s: Type) -> Type:
+def restrict_subtype_away(t: Type, s: Type, *, consider_runtime_isinstance: bool = True) -> Type:
     """Return t minus s for runtime type assertions.
 
     If we can't determine a precise result, return a supertype of the
@@ -2087,16 +2087,27 @@ def restrict_subtype_away(t: Type, s: Type) -> Type:
         new_items = try_restrict_literal_union(p_t, s)
         if new_items is None:
             new_items = [
-                restrict_subtype_away(item, s)
+                restrict_subtype_away(
+                    item, s, consider_runtime_isinstance=consider_runtime_isinstance
+                )
                 for item in p_t.relevant_items()
-                if (isinstance(get_proper_type(item), AnyType) or not covers_at_runtime(item, s))
             ]
-        return UnionType.make_union(new_items)
+        return UnionType.make_union(
+            [item for item in new_items if not isinstance(get_proper_type(item), UninhabitedType)]
+        )
     elif isinstance(p_t, TypeVarType):
         return p_t.copy_modified(upper_bound=restrict_subtype_away(p_t.upper_bound, s))
-    elif covers_at_runtime(t, s):
-        return UninhabitedType()
+
+    if consider_runtime_isinstance:
+        if covers_at_runtime(t, s):
+            return UninhabitedType()
+        else:
+            return t
     else:
+        if is_proper_subtype(t, s, ignore_promotions=True):
+            return UninhabitedType()
+        if is_proper_subtype(t, s, ignore_promotions=True, erase_instances=True):
+            return UninhabitedType()
         return t
 
 
diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
index bb8beac72c3a..997cc7474b91 100644
--- a/test-data/unit/check-typeis.test
+++ b/test-data/unit/check-typeis.test
@@ -134,6 +134,91 @@ def main(a: object) -> None:
         reveal_type(a)  # N: Revealed type is "Union[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
 
+[case testTypeIsUnionWithGeneric]
+from typing import Any, List, Sequence, Union
+from typing_extensions import TypeIs
+
+def is_int_list(a: object) -> TypeIs[List[int]]: pass
+def is_int_seq(a: object) -> TypeIs[Sequence[int]]: pass
+def is_seq(a: object) -> TypeIs[Sequence[Any]]: pass
+
+def f1(a: Union[List[int], List[str]]) -> None:
+    if is_int_list(a):
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.int]"
+    else:
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.str]"
+    reveal_type(a)  # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]"
+
+def f2(a: Union[List[int], int]) -> None:
+    if is_int_list(a):
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.int]"
+    else:
+        reveal_type(a)  # N: Revealed type is "builtins.int"
+    reveal_type(a)  # N: Revealed type is "Union[builtins.list[builtins.int], builtins.int]"
+
+def f3(a: Union[List[bool], List[str]]) -> None:
+    if is_int_seq(a):
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.bool]"
+    else:
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.str]"
+    reveal_type(a)  # N: Revealed type is "Union[builtins.list[builtins.bool], builtins.list[builtins.str]]"
+
+def f4(a: Union[List[int], int]) -> None:
+    if is_seq(a):
+        reveal_type(a)  # N: Revealed type is "builtins.list[builtins.int]"
+    else:
+        reveal_type(a)  # N: Revealed type is "builtins.int"
+    reveal_type(a)  # N: Revealed type is "Union[builtins.list[builtins.int], builtins.int]"
+[builtins fixtures/tuple.pyi]
+
+[case testTypeIsTupleGeneric]
+# flags: --warn-unreachable
+from __future__ import annotations
+from typing_extensions import TypeIs, Unpack
+
+class A: ...
+class B: ...
+
+def is_tuple_of_B(v: tuple[A | B, ...]) -> TypeIs[tuple[B, ...]]: ...
+
+def test1(t: tuple[A]) -> None:
+    if is_tuple_of_B(t):
+        reveal_type(t)  # E: Statement is unreachable
+    else:
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.A]"
+
+def test2(t: tuple[B, A]) -> None:
+    if is_tuple_of_B(t):
+        reveal_type(t)  # E: Statement is unreachable
+    else:
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.B, __main__.A]"
+
+def test3(t: tuple[A | B]) -> None:
+    if is_tuple_of_B(t):
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.B]"
+    else:
+        reveal_type(t)  # N: Revealed type is "tuple[Union[__main__.A, __main__.B]]"
+
+def test4(t: tuple[A | B, A | B]) -> None:
+    if is_tuple_of_B(t):
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.B, __main__.B]"
+    else:
+        reveal_type(t)  # N: Revealed type is "tuple[Union[__main__.A, __main__.B], Union[__main__.A, __main__.B]]"
+
+def test5(t: tuple[A | B, ...]) -> None:
+    if is_tuple_of_B(t):
+        reveal_type(t)  # N: Revealed type is "builtins.tuple[__main__.B, ...]"
+    else:
+        reveal_type(t)  # N: Revealed type is "builtins.tuple[Union[__main__.A, __main__.B], ...]"
+
+def test6(t: tuple[B, Unpack[tuple[A | B, ...]], B]) -> None:
+    if is_tuple_of_B(t):
+        # Should this be tuple[B, *tuple[B, ...], B]
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.B, Never, __main__.B]"
+    else:
+        reveal_type(t)  # N: Revealed type is "tuple[__main__.B, Unpack[builtins.tuple[Union[__main__.A, __main__.B], ...]], __main__.B]"
+[builtins fixtures/tuple.pyi]
+
 [case testTypeIsNonzeroFloat]
 from typing_extensions import TypeIs
 def is_nonzero(a: object) -> TypeIs[float]: pass
@@ -834,3 +919,17 @@ def handle(model: Model) -> None:
     if is_model_a(model):
         reveal_type(model)  # N: Revealed type is "Literal[__main__.Model.A]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeIsAwaitableAny]
+from __future__ import annotations
+from typing import Any, Awaitable, Callable
+from typing_extensions import TypeIs
+
+def is_async_callable(obj: Any) -> TypeIs[Callable[..., Awaitable[Any]]]: ...
+
+def main(f: Callable[[], int | Awaitable[int]]) -> None:
+    if is_async_callable(f):
+        reveal_type(f)  # N: Revealed type is "def (*Any, **Any) -> typing.Awaitable[Any]"
+    else:
+        reveal_type(f)  # N: Revealed type is "def () -> Union[builtins.int, typing.Awaitable[builtins.int]]"
+[builtins fixtures/tuple.pyi]

From be11ab84a2c03677c0e5484b8342b4b7a026c310 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sat, 21 Jun 2025 19:58:44 +0100
Subject: [PATCH 1405/1617] Speed up type checking by caching argument
 inference context (#19323)

This speeds up self check by about 1.5% on my computer, according to
`perf_compare.py`:
```
...
=== Results ===

master                    4.264s (0.0%) | stdev 0.024s
HEAD                      4.201s (-1.5%) | stdev 0.030s
```
I noticed this bottleneck when I was looking at a CPU profile generated
using the script from #19322.
---
 mypy/checkexpr.py | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 26cb2a35794b..603acbe84f0f 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -318,6 +318,8 @@ class ExpressionChecker(ExpressionVisitor[Type], ExpressionCheckerSharedApi):
     strfrm_checker: StringFormatterChecker
     plugin: Plugin
 
+    _arg_infer_context_cache: ArgumentInferContext | None
+
     def __init__(
         self,
         chk: mypy.checker.TypeChecker,
@@ -352,6 +354,8 @@ def __init__(
         self.is_callee = False
         type_state.infer_polymorphic = not self.chk.options.old_type_inference
 
+        self._arg_infer_context_cache = None
+
     def reset(self) -> None:
         self.resolved_type = {}
 
@@ -2277,9 +2281,11 @@ def infer_function_type_arguments_pass2(
         return callee_type, inferred_args
 
     def argument_infer_context(self) -> ArgumentInferContext:
-        return ArgumentInferContext(
-            self.chk.named_type("typing.Mapping"), self.chk.named_type("typing.Iterable")
-        )
+        if self._arg_infer_context_cache is None:
+            self._arg_infer_context_cache = ArgumentInferContext(
+                self.chk.named_type("typing.Mapping"), self.chk.named_type("typing.Iterable")
+            )
+        return self._arg_infer_context_cache
 
     def get_arg_infer_passes(
         self,

From b678d9ff5b1b411a18ac6edc400f7f59961d2546 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sat, 21 Jun 2025 15:04:56 -0400
Subject: [PATCH 1406/1617] Fix incorrect signature suggestion from `dmypy
 suggest` when type name matches imported module name (#18937)

Fixes #18935
---
 mypy/suggestions.py                      |  2 +-
 test-data/unit/fine-grained-suggest.test | 30 ++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index cfd7413860ec..81eb20bd0ac3 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -852,7 +852,7 @@ def visit_instance(self, t: Instance) -> str:
         if self.module:
             parts = obj.split(".")  # need to split the object part if it is a nested class
             tree = self.graph[self.module].tree
-            if tree and parts[0] in tree.names:
+            if tree and parts[0] in tree.names and mod not in tree.names:
                 mod = self.module
 
         if (mod, obj) == ("builtins", "tuple"):
diff --git a/test-data/unit/fine-grained-suggest.test b/test-data/unit/fine-grained-suggest.test
index 3a696ce19c63..c2e544baf38b 100644
--- a/test-data/unit/fine-grained-suggest.test
+++ b/test-data/unit/fine-grained-suggest.test
@@ -207,6 +207,36 @@ foo(B())
 (baz.B) -> Tuple[foo.A, foo:A.C]
 ==
 
+[case testSuggestReexportNamingNameMatchesModule1]
+# suggest: foo.foo
+[file foo.py]
+import bar
+def foo():
+    return bar.bar()
+
+[file bar.py]
+class bar: ...  # name matches module name
+
+[out]
+() -> bar.bar
+==
+
+[case testSuggestReexportNamingNameMatchesModule2]
+# suggest: foo.foo
+[file foo.py]
+import bar
+import qux
+def foo():
+    return qux.bar()
+
+[file bar.py]
+[file qux.py]
+class bar: ...  # name matches another module name
+
+[out]
+() -> qux.bar
+==
+
 [case testSuggestInferInit]
 # suggest: foo.Foo.__init__
 [file foo.py]

From b17027e1b3c034e0a39451d4bcbc4514d0a8429c Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sun, 22 Jun 2025 01:26:15 -0400
Subject: [PATCH 1407/1617] Fix `TypeGuard`/`TypeIs` being forgotten when
 semanal defers (#19325)

Fixes #19318

Don't re-analyze `type_is`/`type_guard` if the callable's type was
successfully analyzed on a previous semanal pass. After the first
successful pass, the return type will have been replaced by
`builtins.bool`, so subsequent analysis can't detect `Type{Guard,Is}`.
---
 mypy/typeanal.py                    |  4 ++--
 test-data/unit/check-typeguard.test | 17 +++++++++++++++++
 test-data/unit/check-typeis.test    | 17 +++++++++++++++++
 3 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index b0d11759303c..204d3061c734 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -1109,8 +1109,8 @@ def visit_callable_type(
                 variables = t.variables
             else:
                 variables, _ = self.bind_function_type_variables(t, t)
-            type_guard = self.anal_type_guard(t.ret_type)
-            type_is = self.anal_type_is(t.ret_type)
+            type_guard = self.anal_type_guard(t.ret_type) if t.type_guard is None else t.type_guard
+            type_is = self.anal_type_is(t.ret_type) if t.type_is is None else t.type_is
 
             arg_kinds = t.arg_kinds
             arg_types = []
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index c43eead67876..fdcfcc969adc 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -813,3 +813,20 @@ raw_target: object
 if isinstance(raw_target, type) and is_xlike(raw_target):
     reveal_type(raw_target)  # N: Revealed type is "type[__main__.X]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeGuardWithDefer]
+from typing import Union
+from typing_extensions import TypeGuard
+
+class A: ...
+class B: ...
+
+def is_a(x: object) -> TypeGuard[A]:
+    return defer_not_defined()  # E: Name "defer_not_defined" is not defined
+
+def main(x: Union[A, B]) -> None:
+    if is_a(x):
+        reveal_type(x)  # N: Revealed type is "__main__.A"
+    else:
+        reveal_type(x)  # N: Revealed type is "Union[__main__.A, __main__.B]"
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
index 997cc7474b91..2f54ac5bf5db 100644
--- a/test-data/unit/check-typeis.test
+++ b/test-data/unit/check-typeis.test
@@ -933,3 +933,20 @@ def main(f: Callable[[], int | Awaitable[int]]) -> None:
     else:
         reveal_type(f)  # N: Revealed type is "def () -> Union[builtins.int, typing.Awaitable[builtins.int]]"
 [builtins fixtures/tuple.pyi]
+
+[case testTypeIsWithDefer]
+from typing import Union
+from typing_extensions import TypeIs
+
+class A: ...
+class B: ...
+
+def is_a(x: object) -> TypeIs[A]:
+    return defer_not_defined()  # E: Name "defer_not_defined" is not defined
+
+def main(x: Union[A, B]) -> None:
+    if is_a(x):
+        reveal_type(x)  # N: Revealed type is "__main__.A"
+    else:
+        reveal_type(x)  # N: Revealed type is "__main__.B"
+[builtins fixtures/tuple.pyi]

From 24b831ab43db3671d75dd2fba4868735cadc8b9a Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Sat, 21 Jun 2025 23:23:47 -0700
Subject: [PATCH 1408/1617] Avoid erasing type objects when checking runtime
 cover (#19320)

In particular, avoid erasing CallableType that represent type objects

Helps with #19159
---
 mypy/subtypes.py                    |  3 ++-
 test-data/unit/check-python310.test | 22 ++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 143d6783f43e..9219bf1c544b 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -2117,7 +2117,8 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool:
     supertype = get_proper_type(supertype)
 
     # Since runtime type checks will ignore type arguments, erase the types.
-    supertype = erase_type(supertype)
+    if not (isinstance(supertype, CallableType) and supertype.is_type_obj()):
+        supertype = erase_type(supertype)
     if is_proper_subtype(
         erase_type(item), supertype, ignore_promotions=True, erase_instances=True
     ):
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 0695bd0380cb..bb8f038eb1eb 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -2601,6 +2601,28 @@ def f(t: T) -> None:
             reveal_type(k)  # N: Revealed type is "tuple[builtins.int, fallback=__main__.K]"
 [builtins fixtures/tuple.pyi]
 
+[case testMatchTypeObjectTypeVar]
+# flags: --warn-unreachable
+from typing import TypeVar
+import b
+
+T_Choice = TypeVar("T_Choice", bound=b.One | b.Two)
+
+def switch(choice: type[T_Choice]) -> None:
+    match choice:
+        case b.One:
+            reveal_type(choice)  # N: Revealed type is "def () -> b.One"
+        case b.Two:
+            reveal_type(choice)  # N: Revealed type is "def () -> b.Two"
+        case _:
+            reveal_type(choice)  # N: Revealed type is "type[T_Choice`-1]"
+
+[file b.py]
+class One: ...
+class Two: ...
+
+[builtins fixtures/tuple.pyi]
+
 [case testNewRedefineMatchBasics]
 # flags: --allow-redefinition-new --local-partial-types
 

From dc031c9634e0fabaa1f7ff0a1c682b2e8ca1f863 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sun, 22 Jun 2025 11:13:44 +0100
Subject: [PATCH 1409/1617] Add script for profiling self check (Linux only)
 (#19322)

The script compiles mypy and profiles self check using the 'perf'
profiler.

Example of how to use this:
```
$ python misc/profile_self_check.py
... [will take several minutes]

CPU profile collected. You can now analyze the profile:
  perf report -i mypy.profile.tmpdir/perf.data
```
---
 misc/perf_compare.py       |   2 +-
 misc/profile_self_check.py | 129 +++++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+), 1 deletion(-)
 create mode 100644 misc/profile_self_check.py

diff --git a/misc/perf_compare.py b/misc/perf_compare.py
index 589912dd9826..50543550b7ce 100644
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -35,7 +35,7 @@ def heading(s: str) -> None:
     print()
 
 
-def build_mypy(target_dir: str, multi_file: bool) -> None:
+def build_mypy(target_dir: str, multi_file: bool, *, cflags: str | None = None) -> None:
     env = os.environ.copy()
     env["CC"] = "clang"
     env["MYPYC_OPT_LEVEL"] = "2"
diff --git a/misc/profile_self_check.py b/misc/profile_self_check.py
new file mode 100644
index 000000000000..eb853641d6d6
--- /dev/null
+++ b/misc/profile_self_check.py
@@ -0,0 +1,129 @@
+"""Compile mypy using mypyc and profile self-check using perf.
+
+Notes:
+ - Only Linux is supported for now (TODO: add support for other profilers)
+ - The profile is collected at C level
+   - It includes C functions compiled by mypyc and CPython runtime functions
+ - The names of mypy functions are mangled to C names, but usually it's clear what they mean
+   - Generally CPyDef_ prefix for native functions and CPyPy_ prefix for wrapper functions
+ - It's important to compile CPython using special flags (see below) to get good results
+ - Generally use the latest Python feature release (or the most recent beta if supported by mypyc)
+ - The tool prints a command that can be used to analyze the profile afterwards
+
+You may need to adjust kernel parameters temporarily, e.g. this (note that this has security
+implications):
+
+  sudo sysctl kernel.perf_event_paranoid=-1
+
+This is the recommended way to configure CPython for profiling:
+
+  ./configure \
+      --enable-optimizations \
+      --with-lto \
+      CFLAGS="-O2 -g -fno-omit-frame-pointer"
+"""
+
+import argparse
+import glob
+import os
+import shutil
+import subprocess
+import sys
+import time
+
+from perf_compare import build_mypy, clone
+
+# Use these C compiler flags when compiling mypy (important). Note that it's strongly recommended
+# to also compile CPython using similar flags, but we don't enforce it in this script.
+CFLAGS = "-O2 -fno-omit-frame-pointer -g"
+
+# Generated files, including binaries, go under this directory to avoid overwriting user state.
+TARGET_DIR = "mypy.profile.tmpdir"
+
+
+def _profile_self_check(target_dir: str) -> None:
+    cache_dir = os.path.join(target_dir, ".mypy_cache")
+    if os.path.exists(cache_dir):
+        shutil.rmtree(cache_dir)
+    files = []
+    for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py":
+        files.extend(glob.glob(pat))
+    self_check_cmd = ["python", "-m", "mypy", "--config-file", "mypy_self_check.ini"] + files
+    cmdline = ["perf", "record", "-g"] + self_check_cmd
+    t0 = time.time()
+    subprocess.run(cmdline, cwd=target_dir, check=True)
+    elapsed = time.time() - t0
+    print(f"{elapsed:.2f}s elapsed")
+
+
+def profile_self_check(target_dir: str) -> None:
+    try:
+        _profile_self_check(target_dir)
+    except subprocess.CalledProcessError:
+        print("\nProfiling failed! You may missing some permissions.")
+        print("\nThis may help (note that it has security implications):")
+        print("  sudo sysctl kernel.perf_event_paranoid=-1")
+        sys.exit(1)
+
+
+def check_requirements() -> None:
+    if sys.platform != "linux":
+        # TODO: How to make this work on other platforms?
+        sys.exit("error: Only Linux is supported")
+
+    try:
+        subprocess.run(["perf", "-h"], capture_output=True)
+    except (subprocess.CalledProcessError, FileNotFoundError):
+        print("error: The 'perf' profiler is not installed")
+        sys.exit(1)
+
+    try:
+        subprocess.run(["clang", "--version"], capture_output=True)
+    except (subprocess.CalledProcessError, FileNotFoundError):
+        print("error: The clang compiler is not installed")
+        sys.exit(1)
+
+    if not os.path.isfile("mypy_self_check.ini"):
+        print("error: Run this in the mypy repository root")
+        sys.exit(1)
+
+
+def main() -> None:
+    check_requirements()
+
+    parser = argparse.ArgumentParser(
+        description="Compile mypy and profile self checking using 'perf'."
+    )
+    parser.add_argument(
+        "--multi-file",
+        action="store_true",
+        help="compile mypy into one C file per module (to reduce RAM use during compilation)",
+    )
+    parser.add_argument(
+        "--skip-compile", action="store_true", help="use compiled mypy from previous run"
+    )
+    args = parser.parse_args()
+    multi_file: bool = args.multi_file
+    skip_compile: bool = args.skip_compile
+
+    target_dir = TARGET_DIR
+
+    if not skip_compile:
+        clone(target_dir, "HEAD")
+
+        print(f"Building mypy in {target_dir}...")
+        build_mypy(target_dir, multi_file, cflags=CFLAGS)
+    elif not os.path.isdir(target_dir):
+        sys.exit("error: Can't find compile mypy from previous run -- can't use --skip-compile")
+
+    profile_self_check(target_dir)
+
+    print()
+    print('NOTE: Compile CPython using CFLAGS="-O2 -g -fno-omit-frame-pointer" for good results')
+    print()
+    print("CPU profile collected. You can now analyze the profile:")
+    print(f"  perf report -i {target_dir}/perf.data ")
+
+
+if __name__ == "__main__":
+    main()

From 16e99de5376464beaa2cf086c1cd3dc5d26a791a Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sun, 22 Jun 2025 12:34:46 +0100
Subject: [PATCH 1410/1617] Fix C compiler flags in the profile self check
 script (#19326)

This was mistakenly omitted in the original PR.
---
 misc/perf_compare.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/misc/perf_compare.py b/misc/perf_compare.py
index 50543550b7ce..39dd22b31339 100644
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -42,6 +42,8 @@ def build_mypy(target_dir: str, multi_file: bool, *, cflags: str | None = None)
     env["PYTHONHASHSEED"] = "1"
     if multi_file:
         env["MYPYC_MULTI_FILE"] = "1"
+    if cflags is not None:
+        env["CFLAGS"] = cflags
     cmd = [sys.executable, "setup.py", "--use-mypyc", "build_ext", "--inplace"]
     subprocess.run(cmd, env=env, check=True, cwd=target_dir)
 

From 5e9d657e397cf3e4d43c491525d70144be35d0d8 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Wed, 25 Jun 2025 12:50:45 -0700
Subject: [PATCH 1411/1617] Fix for overloaded type object erasure (#19338)

https://github.com/python/mypy/pull/19320#discussion_r2165179417
---
 mypy/subtypes.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 9219bf1c544b..86935d0613a2 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -2117,7 +2117,7 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool:
     supertype = get_proper_type(supertype)
 
     # Since runtime type checks will ignore type arguments, erase the types.
-    if not (isinstance(supertype, CallableType) and supertype.is_type_obj()):
+    if not (isinstance(supertype, FunctionLike) and supertype.is_type_obj()):
         supertype = erase_type(supertype)
     if is_proper_subtype(
         erase_type(item), supertype, ignore_promotions=True, erase_instances=True

From bca959f2815275a65f569ef078be028722032777 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 29 Jun 2025 21:25:57 +0100
Subject: [PATCH 1412/1617] Remove unnecessary workarounds from bind_self()
 (#19356)

In my original PR I erroneously concluded that attribute access on
`TypeVar` would result in `PyObject` attribute access after mypy is
compiled, but this is actually no the case. I therefore remove some
workarounds (and a bit of unused code).
---
 mypy/checkmember.py | 17 ++++++-----------
 mypy/typeops.py     |  3 ---
 2 files changed, 6 insertions(+), 14 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 5b5580a648a8..ef38cc3a0dcf 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1477,23 +1477,18 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F:
         items = [bind_self_fast(c, original_type) for c in method.items]
         return cast(F, Overloaded(items))
     assert isinstance(method, CallableType)
-    func: CallableType = method
-    if not func.arg_types:
+    if not method.arg_types:
         # Invalid method, return something.
         return method
-    if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
+    if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2):
         # See typeops.py for details.
         return method
-    original_type = get_proper_type(original_type)
-    if isinstance(original_type, CallableType) and original_type.is_type_obj():
-        original_type = TypeType.make_normalized(original_type.ret_type)
-    res = func.copy_modified(
-        arg_types=func.arg_types[1:],
-        arg_kinds=func.arg_kinds[1:],
-        arg_names=func.arg_names[1:],
+    return method.copy_modified(
+        arg_types=method.arg_types[1:],
+        arg_kinds=method.arg_kinds[1:],
+        arg_names=method.arg_names[1:],
         is_bound=True,
     )
-    return cast(F, res)
 
 
 def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool:
diff --git a/mypy/typeops.py b/mypy/typeops.py
index e84be19465cc..9aa08b40a991 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -472,9 +472,6 @@ class B(A): pass
     else:
         variables = func.variables
 
-    original_type = get_proper_type(original_type)
-    if isinstance(original_type, CallableType) and original_type.is_type_obj():
-        original_type = TypeType.make_normalized(original_type.ret_type)
     res = func.copy_modified(
         arg_types=func.arg_types[1:],
         arg_kinds=func.arg_kinds[1:],

From 9934278ec88b39057c774ae1181acc9bfeb9a00e Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 30 Jun 2025 03:00:50 +0200
Subject: [PATCH 1413/1617] Do not show protocol compatibility note against
 unpacked sequence or mapping (#19358)

This was discovered in #19294 where an unrelated change produced a weird
notice that should not be shown. Current behavior of the added testcase:

```
main.py:10: error: Argument 1 to "foo" has incompatible type "*list[object]"; expected "P"  [arg-type]
main.py:10: note: "list" is missing following "P" protocol member:
main.py:10: note:     arg
main.py:11: error: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P"  [arg-type]
main.py:11: note: "dict" is missing following "P" protocol member:
main.py:11: note:     arg
```


https://mypy-play.net/?mypy=master&python=3.12&flags=strict&gist=d0228ba7d2802db8ac4457f374ccc148
---
 mypy/checkexpr.py                   |  9 ++++++---
 test-data/unit/check-functions.test | 16 ++++++++++++++++
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 603acbe84f0f..8223ccfe4ca0 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2684,9 +2684,12 @@ def check_arg(
                 context=context,
                 outer_context=outer_context,
             )
-            self.msg.incompatible_argument_note(
-                original_caller_type, callee_type, context, parent_error=error
-            )
+            if not caller_kind.is_star():
+                # For *args and **kwargs this note would be incorrect - we're comparing
+                # iterable/mapping type with union of relevant arg types.
+                self.msg.incompatible_argument_note(
+                    original_caller_type, callee_type, context, parent_error=error
+                )
             if not self.msg.prefer_simple_messages():
                 self.chk.check_possible_missing_await(
                     caller_type, callee_type, context, error.code
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index ceb7af433dce..07cfd09b2529 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -3694,3 +3694,19 @@ def defer() -> int: ...
 [out]
 main: note: In function "a":
 main:6: error: Unsupported operand types for + ("int" and "str")
+
+[case testNoExtraNoteForUnpacking]
+from typing import Protocol
+
+class P(Protocol):
+    arg: int
+    # Something that list and dict also have
+    def __contains__(self, item: object) -> bool: ...
+
+def foo(x: P, y: P) -> None: ...
+
+args: list[object]
+foo(*args)  # E: Argument 1 to "foo" has incompatible type "*list[object]"; expected "P"
+kwargs: dict[str, object]
+foo(**kwargs)  # E: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P"
+[builtins fixtures/dict.pyi]

From 657154b6748793f44be7b8238b7265c4e84c2e16 Mon Sep 17 00:00:00 2001
From: Robsdedude 
Date: Tue, 1 Jul 2025 01:36:43 +0000
Subject: [PATCH 1414/1617] Metaclass conflict check improvements (#17682)

This PR fixes some points of #14033:
 * Give metaclass errors to their own error code (I chose `metaclass`).
* Document shortcomings of and workarounds for mypy's metaclass
handling.

I didn't attempt to fix that mypy follows the logic for determining the
metaclass as documented whereas it should follow what the interpreter is
actually doing
(https://github.com/python/mypy/issues/14033#issuecomment-1314025562). I
think such a change is better kept as a separate PR, which is why I
don't want to close the issue with this PR.

Fixes: https://github.com/python/mypy/issues/14033

---------

Co-authored-by: hauntsaninja 
---
 docs/source/error_code_list.rst      | 29 ++++++++++++++++++++
 docs/source/metaclasses.rst          | 25 +++++++++++++++++
 mypy/checker.py                      |  4 +++
 mypy/errorcodes.py                   |  1 +
 mypy/nodes.py                        | 37 +++++++++++++++++++++++++
 mypy/semanal.py                      | 17 +++++++++---
 test-data/unit/check-classes.test    | 38 ++++++++++++++++----------
 test-data/unit/check-errorcodes.test | 41 ++++++++++++++++++++++++++++
 test-data/unit/fine-grained.test     | 10 +++++--
 9 files changed, 182 insertions(+), 20 deletions(-)

diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst
index 49cb8a0c06c1..6deed549c2f1 100644
--- a/docs/source/error_code_list.rst
+++ b/docs/source/error_code_list.rst
@@ -215,6 +215,35 @@ You can use :py:class:`~collections.abc.Callable` as the type for callable objec
         for x in objs:
             f(x)
 
+.. _code-metaclass:
+
+Check the validity of a class's metaclass [metaclass]
+-----------------------------------------------------
+
+Mypy checks whether the metaclass of a class is valid. The metaclass
+must be a subclass of ``type``. Further, the class hierarchy must yield
+a consistent metaclass. For more details, see the
+`Python documentation `_
+
+Note that mypy's metaclass checking is limited and may produce false-positives.
+See also :ref:`limitations`.
+
+Example with an error:
+
+.. code-block:: python
+
+    class GoodMeta(type):
+        pass
+
+    class BadMeta:
+        pass
+
+    class A1(metaclass=GoodMeta):  # OK
+        pass
+
+    class A2(metaclass=BadMeta):  # Error:  Metaclasses not inheriting from "type" are not supported  [metaclass]
+        pass
+
 .. _code-var-annotated:
 
 Require annotation if variable type is unclear [var-annotated]
diff --git a/docs/source/metaclasses.rst b/docs/source/metaclasses.rst
index dd77a2f90ed8..e30dfe80f9f9 100644
--- a/docs/source/metaclasses.rst
+++ b/docs/source/metaclasses.rst
@@ -90,3 +90,28 @@ so it's better not to combine metaclasses and class hierarchies:
 * ``Self`` is not allowed as annotation in metaclasses as per `PEP 673`_.
 
 .. _PEP 673: https://peps.python.org/pep-0673/#valid-locations-for-self
+
+For some builtin types, mypy may think their metaclass is :py:class:`abc.ABCMeta`
+even if it is :py:class:`type` at runtime. In those cases, you can either:
+
+* use :py:class:`abc.ABCMeta` instead of :py:class:`type` as the
+  superclass of your metaclass if that works in your use-case
+* mute the error with ``# type: ignore[metaclass]``
+
+.. code-block:: python
+
+    import abc
+
+    assert type(tuple) is type  # metaclass of tuple is type at runtime
+
+    # The problem:
+    class M0(type): pass
+    class A0(tuple, metaclass=M0): pass  # Mypy Error: metaclass conflict
+
+    # Option 1: use ABCMeta instead of type
+    class M1(abc.ABCMeta): pass
+    class A1(tuple, metaclass=M1): pass
+
+    # Option 2: mute the error
+    class M2(type): pass
+    class A2(tuple, metaclass=M2): pass  # type: ignore[metaclass]
diff --git a/mypy/checker.py b/mypy/checker.py
index 596564c98a40..7859934c1ef7 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2959,7 +2959,11 @@ def check_metaclass_compatibility(self, typ: TypeInfo) -> None:
                 "Metaclass conflict: the metaclass of a derived class must be "
                 "a (non-strict) subclass of the metaclasses of all its bases",
                 typ,
+                code=codes.METACLASS,
             )
+            explanation = typ.explain_metaclass_conflict()
+            if explanation:
+                self.note(explanation, typ, code=codes.METACLASS)
 
     def visit_import_from(self, node: ImportFrom) -> None:
         for name, _ in node.names:
diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py
index c22308e4a754..8f85a6f6351a 100644
--- a/mypy/errorcodes.py
+++ b/mypy/errorcodes.py
@@ -270,6 +270,7 @@ def __hash__(self) -> int:
     "General",
     default_enabled=False,
 )
+METACLASS: Final[ErrorCode] = ErrorCode("metaclass", "Ensure that metaclass is valid", "General")
 
 # Syntax errors are often blocking.
 SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General")
diff --git a/mypy/nodes.py b/mypy/nodes.py
index d69ff10346c3..fc2656ce2130 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3402,6 +3402,43 @@ def calculate_metaclass_type(self) -> mypy.types.Instance | None:
 
         return winner
 
+    def explain_metaclass_conflict(self) -> str | None:
+        # Compare to logic in calculate_metaclass_type
+        declared = self.declared_metaclass
+        if declared is not None and not declared.type.has_base("builtins.type"):
+            return None
+        if self._fullname == "builtins.type":
+            return None
+
+        winner = declared
+        if declared is None:
+            resolution_steps = []
+        else:
+            resolution_steps = [f'"{declared.type.fullname}" (metaclass of "{self.fullname}")']
+        for super_class in self.mro[1:]:
+            super_meta = super_class.declared_metaclass
+            if super_meta is None or super_meta.type is None:
+                continue
+            if winner is None:
+                winner = super_meta
+                resolution_steps.append(
+                    f'"{winner.type.fullname}" (metaclass of "{super_class.fullname}")'
+                )
+                continue
+            if winner.type.has_base(super_meta.type.fullname):
+                continue
+            if super_meta.type.has_base(winner.type.fullname):
+                winner = super_meta
+                resolution_steps.append(
+                    f'"{winner.type.fullname}" (metaclass of "{super_class.fullname}")'
+                )
+                continue
+            # metaclass conflict
+            conflict = f'"{super_meta.type.fullname}" (metaclass of "{super_class.fullname}")'
+            return f"{' > '.join(resolution_steps)} conflicts with {conflict}"
+
+        return None
+
     def is_metaclass(self, *, precise: bool = False) -> bool:
         return (
             self.has_base("builtins.type")
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 87aef2595caf..431c5ec04d3c 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2702,7 +2702,7 @@ def infer_metaclass_and_bases_from_compat_helpers(self, defn: ClassDef) -> None:
         if len(metas) == 0:
             return
         if len(metas) > 1:
-            self.fail("Multiple metaclass definitions", defn)
+            self.fail("Multiple metaclass definitions", defn, code=codes.METACLASS)
             return
         defn.metaclass = metas.pop()
 
@@ -2758,7 +2758,11 @@ def get_declared_metaclass(
             elif isinstance(metaclass_expr, MemberExpr):
                 metaclass_name = get_member_expr_fullname(metaclass_expr)
             if metaclass_name is None:
-                self.fail(f'Dynamic metaclass not supported for "{name}"', metaclass_expr)
+                self.fail(
+                    f'Dynamic metaclass not supported for "{name}"',
+                    metaclass_expr,
+                    code=codes.METACLASS,
+                )
                 return None, False, True
             sym = self.lookup_qualified(metaclass_name, metaclass_expr)
             if sym is None:
@@ -2769,6 +2773,7 @@ def get_declared_metaclass(
                     self.fail(
                         f'Class cannot use "{sym.node.name}" as a metaclass (has type "Any")',
                         metaclass_expr,
+                        code=codes.METACLASS,
                     )
                 return None, False, True
             if isinstance(sym.node, PlaceholderNode):
@@ -2786,11 +2791,15 @@ def get_declared_metaclass(
                     metaclass_info = target.type
 
             if not isinstance(metaclass_info, TypeInfo) or metaclass_info.tuple_type is not None:
-                self.fail(f'Invalid metaclass "{metaclass_name}"', metaclass_expr)
+                self.fail(
+                    f'Invalid metaclass "{metaclass_name}"', metaclass_expr, code=codes.METACLASS
+                )
                 return None, False, False
             if not metaclass_info.is_metaclass():
                 self.fail(
-                    'Metaclasses not inheriting from "type" are not supported', metaclass_expr
+                    'Metaclasses not inheriting from "type" are not supported',
+                    metaclass_expr,
+                    code=codes.METACLASS,
                 )
                 return None, False, False
             inst = fill_typevars(metaclass_info)
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index bf6c51e86446..173657620304 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -4757,8 +4757,8 @@ class C(B):
 class X(type): pass
 class Y(type): pass
 class A(metaclass=X): pass
-class B(A, metaclass=Y): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-
+class B(A, metaclass=Y): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                               # N: "__main__.Y" (metaclass of "__main__.B") conflicts with "__main__.X" (metaclass of "__main__.A")
 [case testMetaclassNoTypeReveal]
 class M:
     x = 0  # type: int
@@ -5737,8 +5737,8 @@ def f() -> type: return M
 class C1(six.with_metaclass(M), object): pass  # E: Unsupported dynamic base class "six.with_metaclass"
 class C2(C1, six.with_metaclass(M)): pass  # E: Unsupported dynamic base class "six.with_metaclass"
 class C3(six.with_metaclass(A)): pass  # E: Metaclasses not inheriting from "type" are not supported
-@six.add_metaclass(A)  # E: Metaclasses not inheriting from "type" are not supported  \
-    # E: Argument 1 to "add_metaclass" has incompatible type "type[A]"; expected "type[type]"
+@six.add_metaclass(A)  # E: Metaclasses not inheriting from "type" are not supported \
+                       # E: Argument 1 to "add_metaclass" has incompatible type "type[A]"; expected "type[type]"
 
 class D3(A): pass
 class C4(six.with_metaclass(M), metaclass=M): pass  # E: Multiple metaclass definitions
@@ -5754,8 +5754,10 @@ class CD(six.with_metaclass(M)): pass  # E: Multiple metaclass definitions
 class M1(type): pass
 class Q1(metaclass=M1): pass
 @six.add_metaclass(M)
-class CQA(Q1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-class CQW(six.with_metaclass(M, Q1)): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class CQA(Q1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                     # N: "__main__.M" (metaclass of "__main__.CQA") conflicts with "__main__.M1" (metaclass of "__main__.Q1")
+class CQW(six.with_metaclass(M, Q1)): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                            # N: "__main__.M" (metaclass of "__main__.CQW") conflicts with "__main__.M1" (metaclass of "__main__.Q1")
 [builtins fixtures/tuple.pyi]
 
 [case testSixMetaclassAny]
@@ -5873,7 +5875,8 @@ class C5(future.utils.with_metaclass(f())): pass  # E: Dynamic metaclass not sup
 
 class M1(type): pass
 class Q1(metaclass=M1): pass
-class CQW(future.utils.with_metaclass(M, Q1)): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class CQW(future.utils.with_metaclass(M, Q1)): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                                     # N: "__main__.M" (metaclass of "__main__.CQW") conflicts with "__main__.M1" (metaclass of "__main__.Q1")
 [builtins fixtures/tuple.pyi]
 
 [case testFutureMetaclassAny]
@@ -7342,17 +7345,22 @@ class ChildOfCorrectSubclass1(CorrectSubclass1): ...
 class CorrectWithType1(C, A1): ...
 class CorrectWithType2(B, C): ...
 
-class Conflict1(A1, B, E): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-class Conflict2(A, B): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-class Conflict3(B, A): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class Conflict1(A1, B, E): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                # N: "__main__.MyMeta1" (metaclass of "__main__.A") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B")
+class Conflict2(A, B): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                            # N: "__main__.MyMeta1" (metaclass of "__main__.A") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B")
+class Conflict3(B, A): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                            # N: "__main__.MyMeta2" (metaclass of "__main__.B") conflicts with "__main__.MyMeta1" (metaclass of "__main__.A")
 
 class ChildOfConflict1(Conflict3): ...
 class ChildOfConflict2(Conflict3, metaclass=CorrectMeta): ...
 
 class ConflictingMeta(MyMeta1, MyMeta3): ...
-class Conflict4(A1, B, E, metaclass=ConflictingMeta): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class Conflict4(A1, B, E, metaclass=ConflictingMeta): ...  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                                           # N: "__main__.ConflictingMeta" (metaclass of "__main__.Conflict4") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B")
 
-class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta):  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta):  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                                                                # N: "__main__.ConflictingMeta" (metaclass of "__main__.ChildOfCorrectButWrongMeta") conflicts with "__main__.CorrectMeta" (metaclass of "__main__.CorrectSubclass1")
     ...
 
 [case testMetaClassConflictIssue14033]
@@ -7367,8 +7375,10 @@ class B1(metaclass=M2): pass
 
 class C1(metaclass=Mx): pass
 
-class TestABC(A2, B1, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-class TestBAC(B1, A2, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+class TestABC(A2, B1, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                 # N: "__main__.M1" (metaclass of "__main__.A1") conflicts with "__main__.M2" (metaclass of "__main__.B1")
+class TestBAC(B1, A2, C1): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \
+                                 # N: "__main__.M2" (metaclass of "__main__.B1") conflicts with "__main__.M1" (metaclass of "__main__.A1")
 
 # should not warn again for children
 class ChildOfTestABC(TestABC): pass
diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test
index d6e3366401dd..bb5f658ebb50 100644
--- a/test-data/unit/check-errorcodes.test
+++ b/test-data/unit/check-errorcodes.test
@@ -1239,6 +1239,47 @@ def f(x: str) -> TypeIs[int]:  # E: Narrowed type "int" is not a subtype of inpu
 
 [builtins fixtures/tuple.pyi]
 
+[case testDynamicMetaclass]
+class A(metaclass=type(tuple)): pass  # E: Dynamic metaclass not supported for "A"  [metaclass]
+[builtins fixtures/tuple.pyi]
+
+[case testMetaclassOfTypeAny]
+# mypy: disallow-subclassing-any=True
+from typing import Any
+foo: Any = ...
+class A(metaclass=foo): pass  # E: Class cannot use "foo" as a metaclass (has type "Any")  [metaclass]
+
+[case testMetaclassOfWrongType]
+class Foo:
+    bar = 1
+class A2(metaclass=Foo.bar): pass  # E: Invalid metaclass "Foo.bar"  [metaclass]
+
+[case testMetaclassNotTypeSubclass]
+class M: pass
+class A(metaclass=M): pass  # E: Metaclasses not inheriting from "type" are not supported  [metaclass]
+
+[case testMultipleMetaclasses]
+import six
+class M1(type): pass
+
+@six.add_metaclass(M1)
+class A1(metaclass=M1): pass  # E: Multiple metaclass definitions  [metaclass]
+
+class A2(six.with_metaclass(M1), metaclass=M1): pass  # E: Multiple metaclass definitions  [metaclass]
+
+@six.add_metaclass(M1)
+class A3(six.with_metaclass(M1)): pass  # E: Multiple metaclass definitions  [metaclass]
+[builtins fixtures/tuple.pyi]
+
+[case testInvalidMetaclassStructure]
+class X(type): pass
+class Y(type): pass
+class A(metaclass=X): pass
+class B(A, metaclass=Y): pass  # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases  [metaclass] \
+                               # N: "__main__.Y" (metaclass of "__main__.B") conflicts with "__main__.X" (metaclass of "__main__.A")
+
+
+
 
 [case testOverloadedFunctionSignature]
 from typing import overload, Union
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 222e38ea0280..503135d901f8 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -2936,10 +2936,12 @@ a.py:6: error: Argument 1 to "f" has incompatible type "type[B]"; expected "M"
 
 [case testFineMetaclassRecalculation]
 import a
+
 [file a.py]
 from b import B
 class M2(type): pass
 class D(B, metaclass=M2): pass
+
 [file b.py]
 import c
 class B: pass
@@ -2949,27 +2951,31 @@ import c
 class B(metaclass=c.M): pass
 
 [file c.py]
-class M(type):
-    pass
+class M(type): pass
 [out]
 ==
 a.py:3: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+a.py:3: note: "a.M2" (metaclass of "a.D") conflicts with "c.M" (metaclass of "b.B")
 
 [case testFineMetaclassDeclaredUpdate]
 import a
+
 [file a.py]
 import b
 class B(metaclass=b.M): pass
 class D(B, metaclass=b.M2): pass
+
 [file b.py]
 class M(type): pass
 class M2(M): pass
+
 [file b.py.2]
 class M(type): pass
 class M2(type): pass
 [out]
 ==
 a.py:3: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
+a.py:3: note: "b.M2" (metaclass of "a.D") conflicts with "b.M" (metaclass of "a.B")
 
 [case testFineMetaclassRemoveFromClass]
 import a

From 4cefd4643e25c14bc4748db365e2162a55fa3786 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 30 Jun 2025 18:37:15 -0700
Subject: [PATCH 1415/1617] Sync typeshed (#19364)

Source commit:

https://github.com/python/typeshed/commit/3f727b0cd6620b7fca45318dd34542b1e1c7dbfb
---
 mypy/typeshed/stdlib/VERSIONS                 |   1 +
 mypy/typeshed/stdlib/_csv.pyi                 |   2 +
 mypy/typeshed/stdlib/_zstd.pyi                |   1 +
 mypy/typeshed/stdlib/ast.pyi                  |  97 +++++------
 mypy/typeshed/stdlib/asyncio/tools.pyi        |  41 +++++
 .../stdlib/concurrent/futures/thread.pyi      |  12 +-
 mypy/typeshed/stdlib/os/__init__.pyi          |   2 +-
 mypy/typeshed/stdlib/sys/__init__.pyi         |  11 +-
 mypy/typeshed/stdlib/tkinter/commondialog.pyi |  14 +-
 mypy/typeshed/stdlib/tkinter/filedialog.pyi   |  42 ++---
 mypy/typeshed/stdlib/tkinter/messagebox.pyi   |  83 ++++++++--
 mypy/typeshed/stdlib/token.pyi                | 151 +++++++++---------
 mypy/typeshed/stdlib/tokenize.pyi             |   7 +-
 mypy/typeshed/stdlib/types.pyi                |   2 +-
 14 files changed, 292 insertions(+), 174 deletions(-)
 create mode 100644 mypy/typeshed/stdlib/asyncio/tools.pyi

diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS
index c86bbb314667..8baf207ad7b8 100644
--- a/mypy/typeshed/stdlib/VERSIONS
+++ b/mypy/typeshed/stdlib/VERSIONS
@@ -95,6 +95,7 @@ asyncio.staggered: 3.8-
 asyncio.taskgroups: 3.11-
 asyncio.threads: 3.9-
 asyncio.timeouts: 3.11-
+asyncio.tools: 3.14-
 asyncio.trsock: 3.8-
 asyncore: 3.0-3.11
 atexit: 3.0-
diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi
index ecea4878907c..efe9ad69bd31 100644
--- a/mypy/typeshed/stdlib/_csv.pyi
+++ b/mypy/typeshed/stdlib/_csv.pyi
@@ -90,6 +90,7 @@ else:
 
 def writer(
     csvfile: SupportsWrite[str],
+    /,
     dialect: _DialectLike = "excel",
     *,
     delimiter: str = ",",
@@ -103,6 +104,7 @@ def writer(
 ) -> _writer: ...
 def reader(
     csvfile: Iterable[str],
+    /,
     dialect: _DialectLike = "excel",
     *,
     delimiter: str = ",",
diff --git a/mypy/typeshed/stdlib/_zstd.pyi b/mypy/typeshed/stdlib/_zstd.pyi
index 0648d898448b..2730232528fc 100644
--- a/mypy/typeshed/stdlib/_zstd.pyi
+++ b/mypy/typeshed/stdlib/_zstd.pyi
@@ -52,6 +52,7 @@ class ZstdCompressor:
         self, /, data: ReadableBuffer, mode: _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 0
     ) -> bytes: ...
     def flush(self, /, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 2) -> bytes: ...
+    def set_pledged_input_size(self, size: int | None, /) -> None: ...
     @property
     def last_mode(self) -> _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame: ...
 
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index af9d20d086b3..613940f5da6a 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -1,3 +1,4 @@
+import ast
 import builtins
 import os
 import sys
@@ -623,21 +624,6 @@ class AsyncWith(stmt):
             **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
-if sys.version_info >= (3, 10):
-    class Match(stmt):
-        __match_args__ = ("subject", "cases")
-        subject: expr
-        cases: list[match_case]
-        if sys.version_info >= (3, 13):
-            def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
-        else:
-            def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ...
-
-        if sys.version_info >= (3, 14):
-            def __replace__(
-                self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]
-            ) -> Self: ...
-
 class Raise(stmt):
     if sys.version_info >= (3, 10):
         __match_args__ = ("exc", "cause")
@@ -1076,13 +1062,13 @@ if sys.version_info >= (3, 14):
         value: expr
         str: builtins.str
         conversion: int
-        format_spec: builtins.str | None = None
+        format_spec: expr | None = None
         def __init__(
             self,
             value: expr = ...,
             str: builtins.str = ...,
             conversion: int = ...,
-            format_spec: builtins.str | None = ...,
+            format_spec: expr | None = ...,
             **kwargs: Unpack[_Attributes],
         ) -> None: ...
         def __replace__(
@@ -1091,7 +1077,7 @@ if sys.version_info >= (3, 14):
             value: expr = ...,
             str: builtins.str = ...,
             conversion: int = ...,
-            format_spec: builtins.str | None = ...,
+            format_spec: expr | None = ...,
             **kwargs: Unpack[_Attributes],
         ) -> Self: ...
 
@@ -1135,13 +1121,13 @@ class Subscript(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("value", "slice", "ctx")
     value: expr
-    slice: _Slice
+    slice: expr
     ctx: expr_context  # Not present in Python < 3.13 if not passed to `__init__`
-    def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+    def __init__(self, value: expr, slice: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(
-            self, *, value: expr = ..., slice: _Slice = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]
+            self, *, value: expr = ..., slice: expr = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
 class Starred(expr):
@@ -1194,36 +1180,28 @@ class Tuple(expr):
 @deprecated("Deprecated since Python 3.9.")
 class slice(AST): ...
 
-_Slice: typing_extensions.TypeAlias = expr
-_SliceAttributes: typing_extensions.TypeAlias = _Attributes
-
-class Slice(_Slice):
+class Slice(expr):
     if sys.version_info >= (3, 10):
         __match_args__ = ("lower", "upper", "step")
     lower: expr | None
     upper: expr | None
     step: expr | None
     def __init__(
-        self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes]
+        self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_Attributes]
     ) -> None: ...
 
     if sys.version_info >= (3, 14):
         def __replace__(
-            self,
-            *,
-            lower: expr | None = ...,
-            upper: expr | None = ...,
-            step: expr | None = ...,
-            **kwargs: Unpack[_SliceAttributes],
+            self, *, lower: expr | None = ..., upper: expr | None = ..., step: expr | None = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
 @deprecated("Deprecated since Python 3.9. Use ast.Tuple instead.")
 class ExtSlice(slice):
-    def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_SliceAttributes]) -> Tuple: ...  # type: ignore[misc]
+    def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_Attributes]) -> Tuple: ...  # type: ignore[misc]
 
 @deprecated("Deprecated since Python 3.9. Use the index value directly instead.")
 class Index(slice):
-    def __new__(cls, value: expr, **kwargs: Unpack[_SliceAttributes]) -> expr: ...  # type: ignore[misc]
+    def __new__(cls, value: expr, **kwargs: Unpack[_Attributes]) -> expr: ...  # type: ignore[misc]
 
 class expr_context(AST): ...
 
@@ -1465,37 +1443,48 @@ class withitem(AST):
         def __replace__(self, *, context_expr: expr = ..., optional_vars: expr | None = ...) -> Self: ...
 
 if sys.version_info >= (3, 10):
+    class pattern(AST):
+        lineno: int
+        col_offset: int
+        end_lineno: int
+        end_col_offset: int
+        def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+
+        if sys.version_info >= (3, 14):
+            def __replace__(
+                self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ...
+            ) -> Self: ...
+
     class match_case(AST):
         __match_args__ = ("pattern", "guard", "body")
-        pattern: _Pattern
+        pattern: ast.pattern
         guard: expr | None
         body: list[stmt]
         if sys.version_info >= (3, 13):
-            def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ...
-        else:
+            def __init__(self, pattern: ast.pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ...
+        elif sys.version_info >= (3, 10):
             @overload
-            def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ...
+            def __init__(self, pattern: ast.pattern, guard: expr | None, body: list[stmt]) -> None: ...
             @overload
-            def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ...
+            def __init__(self, pattern: ast.pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ...
 
         if sys.version_info >= (3, 14):
-            def __replace__(self, *, pattern: _Pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ...
+            def __replace__(self, *, pattern: ast.pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ...
 
-    class pattern(AST):
-        lineno: int
-        col_offset: int
-        end_lineno: int
-        end_col_offset: int
-        def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+    class Match(stmt):
+        __match_args__ = ("subject", "cases")
+        subject: expr
+        cases: list[match_case]
+        if sys.version_info >= (3, 13):
+            def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ...
+        else:
+            def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
-                self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ...
+                self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]
             ) -> Self: ...
 
-    # Without the alias, Pyright complains variables named pattern are recursively defined
-    _Pattern: typing_extensions.TypeAlias = pattern
-
     class MatchValue(pattern):
         __match_args__ = ("value",)
         value: expr
@@ -1590,22 +1579,22 @@ if sys.version_info >= (3, 10):
     class MatchStar(pattern):
         __match_args__ = ("name",)
         name: str | None
-        def __init__(self, name: str | None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+        def __init__(self, name: str | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(self, *, name: str | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ...
 
     class MatchAs(pattern):
         __match_args__ = ("pattern", "name")
-        pattern: _Pattern | None
+        pattern: ast.pattern | None
         name: str | None
         def __init__(
-            self, pattern: _Pattern | None = None, name: str | None = None, **kwargs: Unpack[_Attributes[int]]
+            self, pattern: ast.pattern | None = None, name: str | None = None, **kwargs: Unpack[_Attributes[int]]
         ) -> None: ...
 
         if sys.version_info >= (3, 14):
             def __replace__(
-                self, *, pattern: _Pattern | None = ..., name: str | None = ..., **kwargs: Unpack[_Attributes[int]]
+                self, *, pattern: ast.pattern | None = ..., name: str | None = ..., **kwargs: Unpack[_Attributes[int]]
             ) -> Self: ...
 
     class MatchOr(pattern):
diff --git a/mypy/typeshed/stdlib/asyncio/tools.pyi b/mypy/typeshed/stdlib/asyncio/tools.pyi
new file mode 100644
index 000000000000..65c7f27e0b85
--- /dev/null
+++ b/mypy/typeshed/stdlib/asyncio/tools.pyi
@@ -0,0 +1,41 @@
+from collections.abc import Iterable
+from enum import Enum
+from typing import NamedTuple, SupportsIndex, type_check_only
+
+@type_check_only
+class _AwaitedInfo(NamedTuple):  # AwaitedInfo_Type from _remote_debugging
+    thread_id: int
+    awaited_by: list[_TaskInfo]
+
+@type_check_only
+class _TaskInfo(NamedTuple):  # TaskInfo_Type from _remote_debugging
+    task_id: int
+    task_name: str
+    coroutine_stack: list[_CoroInfo]
+    awaited_by: list[_CoroInfo]
+
+@type_check_only
+class _CoroInfo(NamedTuple):  # CoroInfo_Type from _remote_debugging
+    call_stack: list[_FrameInfo]
+    task_name: int | str
+
+@type_check_only
+class _FrameInfo(NamedTuple):  # FrameInfo_Type from _remote_debugging
+    filename: str
+    lineno: int
+    funcname: str
+
+class NodeType(Enum):
+    COROUTINE = 1
+    TASK = 2
+
+class CycleFoundException(Exception):
+    cycles: list[list[int]]
+    id2name: dict[int, str]
+    def __init__(self, cycles: list[list[int]], id2name: dict[int, str]) -> None: ...
+
+def get_all_awaited_by(pid: SupportsIndex) -> list[_AwaitedInfo]: ...
+def build_async_tree(result: Iterable[_AwaitedInfo], task_emoji: str = "(T)", cor_emoji: str = "") -> list[list[str]]: ...
+def build_task_table(result: Iterable[_AwaitedInfo]) -> list[list[int | str]]: ...
+def display_awaited_by_tasks_table(pid: SupportsIndex) -> None: ...
+def display_awaited_by_tasks_tree(pid: SupportsIndex) -> None: ...
diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
index 22df0dca5a3f..50a6a9c6f43e 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi
@@ -91,8 +91,12 @@ class ThreadPoolExecutor(Executor):
     _shutdown: bool
     _shutdown_lock: Lock
     _thread_name_prefix: str | None
-    _initializer: Callable[..., None] | None
-    _initargs: tuple[Any, ...]
+    if sys.version_info >= (3, 14):
+        _create_worker_context: Callable[[], WorkerContext]
+        _resolve_work_item_task: _ResolveTaskFunc
+    else:
+        _initializer: Callable[..., None] | None
+        _initargs: tuple[Any, ...]
     _work_queue: queue.SimpleQueue[_WorkItem[Any]]
 
     if sys.version_info >= (3, 14):
@@ -100,12 +104,12 @@ class ThreadPoolExecutor(Executor):
         @classmethod
         def prepare_context(
             cls, initializer: Callable[[], object], initargs: tuple[()]
-        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+        ) -> tuple[Callable[[], WorkerContext], _ResolveTaskFunc]: ...
         @overload
         @classmethod
         def prepare_context(
             cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]
-        ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ...
+        ) -> tuple[Callable[[], WorkerContext], _ResolveTaskFunc]: ...
 
     @overload
     def __init__(
diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi
index 5286c76d1b06..dd4479f9030a 100644
--- a/mypy/typeshed/stdlib/os/__init__.pyi
+++ b/mypy/typeshed/stdlib/os/__init__.pyi
@@ -683,7 +683,7 @@ else:
 extsep: str
 pathsep: str
 defpath: str
-linesep: str
+linesep: Literal["\n", "\r\n"]
 devnull: str
 name: str
 
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index ce06551f975a..054fe91b17c6 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -6,7 +6,7 @@ from collections.abc import AsyncGenerator, Callable, Sequence
 from io import TextIOWrapper
 from types import FrameType, ModuleType, TracebackType
 from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final, type_check_only
-from typing_extensions import LiteralString, TypeAlias
+from typing_extensions import LiteralString, TypeAlias, deprecated
 
 _T = TypeVar("_T")
 
@@ -335,7 +335,14 @@ class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel
 version_info: _version_info
 
 def call_tracing(func: Callable[..., _T], args: Any, /) -> _T: ...
-def _clear_type_cache() -> None: ...
+
+if sys.version_info >= (3, 13):
+    @deprecated("Deprecated in Python 3.13; use _clear_internal_caches() instead.")
+    def _clear_type_cache() -> None: ...
+
+else:
+    def _clear_type_cache() -> None: ...
+
 def _current_frames() -> dict[int, FrameType]: ...
 def _getframe(depth: int = 0, /) -> FrameType: ...
 def _debugmallocstats() -> None: ...
diff --git a/mypy/typeshed/stdlib/tkinter/commondialog.pyi b/mypy/typeshed/stdlib/tkinter/commondialog.pyi
index d5fc2f05ceec..6dba6bd60928 100644
--- a/mypy/typeshed/stdlib/tkinter/commondialog.pyi
+++ b/mypy/typeshed/stdlib/tkinter/commondialog.pyi
@@ -1,12 +1,14 @@
-from _typeshed import Incomplete
 from collections.abc import Mapping
-from typing import ClassVar
+from tkinter import Misc
+from typing import Any, ClassVar
 
 __all__ = ["Dialog"]
 
 class Dialog:
     command: ClassVar[str | None]
-    master: Incomplete | None
-    options: Mapping[str, Incomplete]
-    def __init__(self, master=None, **options) -> None: ...
-    def show(self, **options): ...
+    master: Misc | None
+    # Types of options are very dynamic. They depend on the command and are
+    # sometimes changed to a different type.
+    options: Mapping[str, Any]
+    def __init__(self, master: Misc | None = None, **options: Any) -> None: ...
+    def show(self, **options: Any) -> Any: ...
diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi
index af033dae97c3..b6ef8f45d035 100644
--- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi
+++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi
@@ -1,6 +1,6 @@
-from _typeshed import Incomplete, StrOrBytesPath
-from collections.abc import Iterable
-from tkinter import Button, Entry, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog
+from _typeshed import Incomplete, StrOrBytesPath, StrPath
+from collections.abc import Hashable, Iterable
+from tkinter import Button, Entry, Event, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog
 from typing import IO, ClassVar, Literal
 
 __all__ = [
@@ -19,12 +19,12 @@ __all__ = [
     "askdirectory",
 ]
 
-dialogstates: dict[Incomplete, tuple[Incomplete, Incomplete]]
+dialogstates: dict[Hashable, tuple[str, str]]
 
 class FileDialog:
     title: str
-    master: Incomplete
-    directory: Incomplete | None
+    master: Misc
+    directory: str | None
     top: Toplevel
     botframe: Frame
     selection: Entry
@@ -38,23 +38,23 @@ class FileDialog:
     filter_button: Button
     cancel_button: Button
     def __init__(
-        self, master, title=None
+        self, master: Misc, title: str | None = None
     ) -> None: ...  # title is usually a str or None, but e.g. int doesn't raise en exception either
-    how: Incomplete | None
-    def go(self, dir_or_file=".", pattern: str = "*", default: str = "", key=None): ...
-    def quit(self, how=None) -> None: ...
-    def dirs_double_event(self, event) -> None: ...
-    def dirs_select_event(self, event) -> None: ...
-    def files_double_event(self, event) -> None: ...
-    def files_select_event(self, event) -> None: ...
-    def ok_event(self, event) -> None: ...
+    how: str | None
+    def go(self, dir_or_file: StrPath = ".", pattern: StrPath = "*", default: StrPath = "", key: Hashable | None = None): ...
+    def quit(self, how: str | None = None) -> None: ...
+    def dirs_double_event(self, event: Event) -> None: ...
+    def dirs_select_event(self, event: Event) -> None: ...
+    def files_double_event(self, event: Event) -> None: ...
+    def files_select_event(self, event: Event) -> None: ...
+    def ok_event(self, event: Event) -> None: ...
     def ok_command(self) -> None: ...
-    def filter_command(self, event=None) -> None: ...
-    def get_filter(self): ...
-    def get_selection(self): ...
-    def cancel_command(self, event=None) -> None: ...
-    def set_filter(self, dir, pat) -> None: ...
-    def set_selection(self, file) -> None: ...
+    def filter_command(self, event: Event | None = None) -> None: ...
+    def get_filter(self) -> tuple[str, str]: ...
+    def get_selection(self) -> str: ...
+    def cancel_command(self, event: Event | None = None) -> None: ...
+    def set_filter(self, dir: StrPath, pat: StrPath) -> None: ...
+    def set_selection(self, file: StrPath) -> None: ...
 
 class LoadFileDialog(FileDialog):
     title: str
diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
index 902fab62ac05..8e5a88f92ea1 100644
--- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi
+++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
@@ -1,5 +1,6 @@
+from tkinter import Misc
 from tkinter.commondialog import Dialog
-from typing import ClassVar, Final
+from typing import ClassVar, Final, Literal
 
 __all__ = ["showinfo", "showwarning", "showerror", "askquestion", "askokcancel", "askyesno", "askyesnocancel", "askretrycancel"]
 
@@ -23,11 +24,75 @@ NO: Final = "no"
 class Message(Dialog):
     command: ClassVar[str]
 
-def showinfo(title: str | None = None, message: str | None = None, **options) -> str: ...
-def showwarning(title: str | None = None, message: str | None = None, **options) -> str: ...
-def showerror(title: str | None = None, message: str | None = None, **options) -> str: ...
-def askquestion(title: str | None = None, message: str | None = None, **options) -> str: ...
-def askokcancel(title: str | None = None, message: str | None = None, **options) -> bool: ...
-def askyesno(title: str | None = None, message: str | None = None, **options) -> bool: ...
-def askyesnocancel(title: str | None = None, message: str | None = None, **options) -> bool | None: ...
-def askretrycancel(title: str | None = None, message: str | None = None, **options) -> bool: ...
+def showinfo(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["ok"] = ...,
+    parent: Misc = ...,
+) -> str: ...
+def showwarning(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["ok"] = ...,
+    parent: Misc = ...,
+) -> str: ...
+def showerror(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["ok"] = ...,
+    parent: Misc = ...,
+) -> str: ...
+def askquestion(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["yes", "no"] = ...,
+    parent: Misc = ...,
+) -> str: ...
+def askokcancel(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["ok", "cancel"] = ...,
+    parent: Misc = ...,
+) -> bool: ...
+def askyesno(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["yes", "no"] = ...,
+    parent: Misc = ...,
+) -> bool: ...
+def askyesnocancel(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["cancel", "yes", "no"] = ...,
+    parent: Misc = ...,
+) -> bool | None: ...
+def askretrycancel(
+    title: str | None = None,
+    message: str | None = None,
+    *,
+    detail: str = ...,
+    icon: Literal["error", "info", "question", "warning"] = ...,
+    default: Literal["retry", "cancel"] = ...,
+    parent: Misc = ...,
+) -> bool: ...
diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi
index 7c13b15d95b7..fd1b10da1d12 100644
--- a/mypy/typeshed/stdlib/token.pyi
+++ b/mypy/typeshed/stdlib/token.pyi
@@ -1,4 +1,5 @@
 import sys
+from typing import Final
 
 __all__ = [
     "AMPER",
@@ -81,87 +82,87 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 14):
     __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"]
 
-ENDMARKER: int
-NAME: int
-NUMBER: int
-STRING: int
-NEWLINE: int
-INDENT: int
-DEDENT: int
-LPAR: int
-RPAR: int
-LSQB: int
-RSQB: int
-COLON: int
-COMMA: int
-SEMI: int
-PLUS: int
-MINUS: int
-STAR: int
-SLASH: int
-VBAR: int
-AMPER: int
-LESS: int
-GREATER: int
-EQUAL: int
-DOT: int
-PERCENT: int
-LBRACE: int
-RBRACE: int
-EQEQUAL: int
-NOTEQUAL: int
-LESSEQUAL: int
-GREATEREQUAL: int
-TILDE: int
-CIRCUMFLEX: int
-LEFTSHIFT: int
-RIGHTSHIFT: int
-DOUBLESTAR: int
-PLUSEQUAL: int
-MINEQUAL: int
-STAREQUAL: int
-SLASHEQUAL: int
-PERCENTEQUAL: int
-AMPEREQUAL: int
-VBAREQUAL: int
-CIRCUMFLEXEQUAL: int
-LEFTSHIFTEQUAL: int
-RIGHTSHIFTEQUAL: int
-DOUBLESTAREQUAL: int
-DOUBLESLASH: int
-DOUBLESLASHEQUAL: int
-AT: int
-RARROW: int
-ELLIPSIS: int
-ATEQUAL: int
+ENDMARKER: Final[int]
+NAME: Final[int]
+NUMBER: Final[int]
+STRING: Final[int]
+NEWLINE: Final[int]
+INDENT: Final[int]
+DEDENT: Final[int]
+LPAR: Final[int]
+RPAR: Final[int]
+LSQB: Final[int]
+RSQB: Final[int]
+COLON: Final[int]
+COMMA: Final[int]
+SEMI: Final[int]
+PLUS: Final[int]
+MINUS: Final[int]
+STAR: Final[int]
+SLASH: Final[int]
+VBAR: Final[int]
+AMPER: Final[int]
+LESS: Final[int]
+GREATER: Final[int]
+EQUAL: Final[int]
+DOT: Final[int]
+PERCENT: Final[int]
+LBRACE: Final[int]
+RBRACE: Final[int]
+EQEQUAL: Final[int]
+NOTEQUAL: Final[int]
+LESSEQUAL: Final[int]
+GREATEREQUAL: Final[int]
+TILDE: Final[int]
+CIRCUMFLEX: Final[int]
+LEFTSHIFT: Final[int]
+RIGHTSHIFT: Final[int]
+DOUBLESTAR: Final[int]
+PLUSEQUAL: Final[int]
+MINEQUAL: Final[int]
+STAREQUAL: Final[int]
+SLASHEQUAL: Final[int]
+PERCENTEQUAL: Final[int]
+AMPEREQUAL: Final[int]
+VBAREQUAL: Final[int]
+CIRCUMFLEXEQUAL: Final[int]
+LEFTSHIFTEQUAL: Final[int]
+RIGHTSHIFTEQUAL: Final[int]
+DOUBLESTAREQUAL: Final[int]
+DOUBLESLASH: Final[int]
+DOUBLESLASHEQUAL: Final[int]
+AT: Final[int]
+RARROW: Final[int]
+ELLIPSIS: Final[int]
+ATEQUAL: Final[int]
 if sys.version_info < (3, 13):
-    AWAIT: int
-    ASYNC: int
-OP: int
-ERRORTOKEN: int
-N_TOKENS: int
-NT_OFFSET: int
-tok_name: dict[int, str]
-COMMENT: int
-NL: int
-ENCODING: int
-TYPE_COMMENT: int
-TYPE_IGNORE: int
-COLONEQUAL: int
-EXACT_TOKEN_TYPES: dict[str, int]
+    AWAIT: Final[int]
+    ASYNC: Final[int]
+OP: Final[int]
+ERRORTOKEN: Final[int]
+N_TOKENS: Final[int]
+NT_OFFSET: Final[int]
+tok_name: Final[dict[int, str]]
+COMMENT: Final[int]
+NL: Final[int]
+ENCODING: Final[int]
+TYPE_COMMENT: Final[int]
+TYPE_IGNORE: Final[int]
+COLONEQUAL: Final[int]
+EXACT_TOKEN_TYPES: Final[dict[str, int]]
 if sys.version_info >= (3, 10):
-    SOFT_KEYWORD: int
+    SOFT_KEYWORD: Final[int]
 
 if sys.version_info >= (3, 12):
-    EXCLAMATION: int
-    FSTRING_END: int
-    FSTRING_MIDDLE: int
-    FSTRING_START: int
+    EXCLAMATION: Final[int]
+    FSTRING_END: Final[int]
+    FSTRING_MIDDLE: Final[int]
+    FSTRING_START: Final[int]
 
 if sys.version_info >= (3, 14):
-    TSTRING_START: int
-    TSTRING_MIDDLE: int
-    TSTRING_END: int
+    TSTRING_START: Final[int]
+    TSTRING_MIDDLE: Final[int]
+    TSTRING_END: Final[int]
 
 def ISTERMINAL(x: int) -> bool: ...
 def ISNONTERMINAL(x: int) -> bool: ...
diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi
index b658740a1ad7..1a3a80937f22 100644
--- a/mypy/typeshed/stdlib/tokenize.pyi
+++ b/mypy/typeshed/stdlib/tokenize.pyi
@@ -3,10 +3,15 @@ from _typeshed import FileDescriptorOrPath
 from collections.abc import Callable, Generator, Iterable, Sequence
 from re import Pattern
 from token import *
-from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES
 from typing import Any, NamedTuple, TextIO, type_check_only
 from typing_extensions import TypeAlias
 
+if sys.version_info < (3, 12):
+    # Avoid double assignment to Final name by imports, which pyright objects to.
+    # EXACT_TOKEN_TYPES is already defined by 'from token import *' above
+    # in Python 3.12+.
+    from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES
+
 __all__ = [
     "AMPER",
     "AMPEREQUAL",
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index d9f8e8756833..582cb653422f 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -388,7 +388,7 @@ class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]):
     @property
     def gi_running(self) -> bool: ...
     @property
-    def gi_yieldfrom(self) -> GeneratorType[_YieldT_co, _SendT_contra, Any] | None: ...
+    def gi_yieldfrom(self) -> Iterator[_YieldT_co] | None: ...
     if sys.version_info >= (3, 11):
         @property
         def gi_suspended(self) -> bool: ...

From 5b0ac327717ffc02e836d5456fb5785b75bba82a Mon Sep 17 00:00:00 2001
From: Jahongir Qurbonov <109198731+Jahongir-Qurbonov@users.noreply.github.com>
Date: Tue, 1 Jul 2025 18:02:23 +0500
Subject: [PATCH 1416/1617] [mypyc] Implement `list.clear()` primitive (#19344)

Add primitive for `list.clear`. Issue:
[#1093](https://github.com/mypyc/mypyc/issues/1093)
---
 mypyc/lib-rt/CPy.h                 |  1 +
 mypyc/lib-rt/list_ops.c            | 17 +++++++++++++++++
 mypyc/primitives/list_ops.py       |  9 +++++++++
 mypyc/test-data/fixtures/ir.py     |  1 +
 mypyc/test-data/irbuild-lists.test | 12 ++++++++++++
 mypyc/test-data/run-lists.test     | 26 ++++++++++++++++++++++++++
 6 files changed, 66 insertions(+)

diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 1f0cf4dd63d6..b8c39772dfae 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -667,6 +667,7 @@ PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size);
 PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq);
 PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size);
 PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
+char CPyList_Clear(PyObject *list);
 PyObject *CPyList_Copy(PyObject *list);
 int CPySequence_Check(PyObject *obj);
 
diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index 4dddb2249f06..03af8a769c0e 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -29,6 +29,23 @@ PyObject *CPyList_Build(Py_ssize_t len, ...) {
     return res;
 }
 
+char CPyList_Clear(PyObject *list) {
+    if (PyList_CheckExact(list)) {
+        PyList_Clear(list);
+    } else {
+        _Py_IDENTIFIER(clear);
+        PyObject *name = _PyUnicode_FromId(&PyId_clear);
+        if (name == NULL) {
+            return 0;
+        }
+        PyObject *res = PyObject_CallMethodNoArgs(list, name);
+        if (res == NULL) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
 PyObject *CPyList_Copy(PyObject *list) {
     if(PyList_CheckExact(list)) {
         return PyList_GetSlice(list, 0, PyList_GET_SIZE(list));
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 99df6fe0dc9c..d0e0af9f987f 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -271,6 +271,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# list.clear()
+method_op(
+    name="clear",
+    arg_types=[list_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="CPyList_Clear",
+    error_kind=ERR_FALSE,
+)
+
 # list.copy()
 method_op(
     name="copy",
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 1b92590a5fd4..532cbbc06177 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -245,6 +245,7 @@ def sort(self) -> None: pass
     def reverse(self) -> None: pass
     def remove(self, o: _T) -> None: pass
     def index(self, o: _T) -> int: pass
+    def clear(self) -> None: pass
     def copy(self) -> List[_T]: pass
 
 class dict(Mapping[_K, _V]):
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index 2435b5aee350..72caa5fad8d8 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -220,6 +220,18 @@ L0:
     r1 = r0 << 1
     return r1
 
+[case testListClear]
+from typing import List
+def f(l: List[int]) -> None:
+    return l.clear()
+[out]
+def f(l):
+    l :: list
+    r0 :: bit
+L0:
+    r0 = CPyList_Clear(l)
+    return 1
+
 [case testListCopy]
 from typing import List
 from typing import Any
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 07c6d7735f10..85e0926027c5 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -51,6 +51,32 @@ print(2, a)
 1 [-1, 5]
 2 [340282366920938463463374607431768211461, -170141183460469231731687303715884105736]
 
+[case testListClear]
+from typing import List, Any
+from copysubclass import subc
+
+def test_list_clear() -> None:
+    l1 = [1, 2, 3, -4, 5]
+    l1.clear()
+    assert l1 == []
+    l1.clear()
+    assert l1 == []
+    l2: List[Any] = []
+    l2.clear()
+    assert l2 == []
+    l3 = [1, 2, 3, "abcdef"]
+    l3.clear()
+    assert l3 == []
+    # subclass testing
+    l4: subc = subc([1, 2, 3])
+    l4.clear()
+    assert l4 == []
+
+[file copysubclass.py]
+from typing import Any
+class subc(list[Any]):
+    pass
+
 [case testListCopy]
 from typing import List
 from copysubclass import subc

From 3df3d796ee7821f04ee0443ec5011687c6af7f45 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Tue, 1 Jul 2025 11:03:27 -0400
Subject: [PATCH 1417/1617] feat: new mypyc primitives for str.count (#19264)

This PR adds new mypyc primitives for all variations of `str.count`

fixes https://github.com/mypyc/mypyc/issues/1096
---
 mypyc/lib-rt/CPy.h               |  2 +
 mypyc/lib-rt/str_ops.c           | 24 +++++++++
 mypyc/primitives/str_ops.py      | 28 ++++++++++
 mypyc/test-data/irbuild-str.test | 58 ++++++++++++++++++++
 mypyc/test-data/run-strings.test | 91 ++++++++++++++++++++++++++++++++
 5 files changed, 203 insertions(+)

diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index b8c39772dfae..bdf3e0130a4c 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -753,6 +753,8 @@ bool CPyStr_IsTrue(PyObject *obj);
 Py_ssize_t CPyStr_Size_size_t(PyObject *str);
 PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors);
 PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors);
+Py_ssize_t CPyStr_Count(PyObject *unicode, PyObject *substring, CPyTagged start);
+Py_ssize_t CPyStr_CountFull(PyObject *unicode, PyObject *substring, CPyTagged start, CPyTagged end);
 CPyTagged CPyStr_Ord(PyObject *obj);
 
 
diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c
index 49fcbb8c6876..210172c57497 100644
--- a/mypyc/lib-rt/str_ops.c
+++ b/mypyc/lib-rt/str_ops.c
@@ -511,6 +511,30 @@ PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) {
     }
 }
 
+Py_ssize_t CPyStr_Count(PyObject *unicode, PyObject *substring, CPyTagged start) {
+    Py_ssize_t temp_start = CPyTagged_AsSsize_t(start);
+    if (temp_start == -1 && PyErr_Occurred()) {
+        PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
+        return -1;
+    }
+    Py_ssize_t end = PyUnicode_GET_LENGTH(unicode);
+    return PyUnicode_Count(unicode, substring, temp_start, end);
+}
+
+Py_ssize_t CPyStr_CountFull(PyObject *unicode, PyObject *substring, CPyTagged start, CPyTagged end) {
+    Py_ssize_t temp_start = CPyTagged_AsSsize_t(start);
+    if (temp_start == -1 && PyErr_Occurred()) {
+        PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
+        return -1;
+    }
+    Py_ssize_t temp_end = CPyTagged_AsSsize_t(end);
+    if (temp_end == -1 && PyErr_Occurred()) {
+        PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
+        return -1;
+    }
+    return PyUnicode_Count(unicode, substring, temp_start, temp_end);
+}
+
 
 CPyTagged CPyStr_Ord(PyObject *obj) {
     Py_ssize_t s = PyUnicode_GET_LENGTH(obj);
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index ded339b9672c..9d46da9c3514 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -277,6 +277,34 @@
     error_kind=ERR_MAGIC,
 )
 
+# str.count(substring)
+method_op(
+    name="count",
+    arg_types=[str_rprimitive, str_rprimitive],
+    return_type=c_pyssize_t_rprimitive,
+    c_function_name="CPyStr_Count",
+    error_kind=ERR_NEG_INT,
+    extra_int_constants=[(0, c_pyssize_t_rprimitive)],
+)
+
+# str.count(substring, start)
+method_op(
+    name="count",
+    arg_types=[str_rprimitive, str_rprimitive, int_rprimitive],
+    return_type=c_pyssize_t_rprimitive,
+    c_function_name="CPyStr_Count",
+    error_kind=ERR_NEG_INT,
+)
+
+# str.count(substring, start, end)
+method_op(
+    name="count",
+    arg_types=[str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive],
+    return_type=c_pyssize_t_rprimitive,
+    c_function_name="CPyStr_CountFull",
+    error_kind=ERR_NEG_INT,
+)
+
 # str.replace(old, new)
 method_op(
     name="replace",
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index ad495dddcb15..2bf77a6cb556 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -504,3 +504,61 @@ L0:
     r7 = CPyStr_Strip(s, 0)
     r8 = CPyStr_RStrip(s, 0)
     return 1
+
+[case testCountAll]
+def do_count(s: str) -> int:
+    return s.count("x")  # type: ignore [attr-defined]
+[out]
+def do_count(s):
+    s, r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4 :: int
+L0:
+    r0 = 'x'
+    r1 = CPyStr_Count(s, r0, 0)
+    r2 = r1 >= 0 :: signed
+    r3 = box(native_int, r1)
+    r4 = unbox(int, r3)
+    return r4
+
+[case testCountStart]
+def do_count(s: str, start: int) -> int:
+    return s.count("x", start)  # type: ignore [attr-defined]
+[out]
+def do_count(s, start):
+    s :: str
+    start :: int
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4 :: int
+L0:
+    r0 = 'x'
+    r1 = CPyStr_Count(s, r0, start)
+    r2 = r1 >= 0 :: signed
+    r3 = box(native_int, r1)
+    r4 = unbox(int, r3)
+    return r4
+
+[case testCountStartEnd]
+def do_count(s: str, start: int, end: int) -> int:
+    return s.count("x", start, end)  # type: ignore [attr-defined]
+[out]
+def do_count(s, start, end):
+    s :: str
+    start, end :: int
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4 :: int
+L0:
+    r0 = 'x'
+    r1 = CPyStr_CountFull(s, r0, start, end)
+    r2 = r1 >= 0 :: signed
+    r3 = box(native_int, r1)
+    r4 = unbox(int, r3)
+    return r4
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index 9183b45b036a..074e56f9068a 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -815,3 +815,94 @@ def test_unicode_range() -> None:
     assert "\u2029 \U0010AAAA\U00104444B\u205F ".strip() == "\U0010AAAA\U00104444B"
     assert " \u3000\u205F ".strip() == ""
     assert "\u2029 \U00102865\u205F ".rstrip() == "\u2029 \U00102865"
+
+[case testCount]
+# mypy: disable-error-code="attr-defined"
+def test_count() -> None:
+    string = "abcbcb"
+    assert string.count("a") == 1
+    assert string.count("b") == 3
+    assert string.count("c") == 2
+def test_count_start() -> None:
+    string = "abcbcb"
+    assert string.count("a", 2) == string.count("a", -4) == 0, (string.count("a", 2), string.count("a", -4))
+    assert string.count("b", 2) == string.count("b", -4) == 2, (string.count("b", 2), string.count("b", -4))
+    assert string.count("c", 2) == string.count("c", -4) == 2, (string.count("c", 2), string.count("c", -4))
+    # out of bounds
+    assert string.count("a", 8) == 0
+    assert string.count("a", -8) == 1
+    assert string.count("b", 8) == 0
+    assert string.count("b", -8) == 3
+    assert string.count("c", 8) == 0
+    assert string.count("c", -8) == 2
+def test_count_start_end() -> None:
+    string = "abcbcb"
+    assert string.count("a", 0, 4) == 1, string.count("a", 0, 4)
+    assert string.count("b", 0, 4) == 2, string.count("b", 0, 4)
+    assert string.count("c", 0, 4) == 1, string.count("c", 0, 4)
+def test_count_multi() -> None:
+    string = "aaabbbcccbbbcccbbb"
+    assert string.count("aaa") == 1, string.count("aaa")
+    assert string.count("bbb") == 3, string.count("bbb")
+    assert string.count("ccc") == 2, string.count("ccc")
+def test_count_multi_start() -> None:
+    string = "aaabbbcccbbbcccbbb"
+    assert string.count("aaa", 6) == string.count("aaa", -12) == 0, (string.count("aaa", 6), string.count("aaa", -12))
+    assert string.count("bbb", 6) == string.count("bbb", -12) == 2, (string.count("bbb", 6), string.count("bbb", -12))
+    assert string.count("ccc", 6) == string.count("ccc", -12) == 2, (string.count("ccc", 6), string.count("ccc", -12))
+    # out of bounds
+    assert string.count("aaa", 20) == 0
+    assert string.count("aaa", -20) == 1
+    assert string.count("bbb", 20) == 0
+    assert string.count("bbb", -20) == 3
+    assert string.count("ccc", 20) == 0
+    assert string.count("ccc", -20) == 2
+def test_count_multi_start_end() -> None:
+    string = "aaabbbcccbbbcccbbb"
+    assert string.count("aaa", 0, 12) == 1, string.count("aaa", 0, 12)
+    assert string.count("bbb", 0, 12) == 2, string.count("bbb", 0, 12)
+    assert string.count("ccc", 0, 12) == 1, string.count("ccc", 0, 12)
+def test_count_emoji() -> None:
+    string = "😴🚀ñ🚀ñ🚀"
+    assert string.count("😴") == 1, string.count("😴")
+    assert string.count("🚀") == 3, string.count("🚀")
+    assert string.count("ñ") == 2, string.count("ñ")
+def test_count_start_emoji() -> None:
+    string = "😴🚀ñ🚀ñ🚀"
+    assert string.count("😴", 2) == string.count("😴", -4) == 0, (string.count("😴", 2), string.count("😴", -4))
+    assert string.count("🚀", 2) == string.count("🚀", -4) == 2, (string.count("🚀", 2), string.count("🚀", -4))
+    assert string.count("ñ", 2) == string.count("ñ", -4) == 2, (string.count("ñ", 2), string.count("ñ", -4))
+    # Out of bounds
+    assert string.count("😴", 8) == 0, string.count("😴", 8)
+    assert string.count("😴", -8) == 1, string.count("😴", -8)
+    assert string.count("🚀", 8) == 0, string.count("🚀", 8)
+    assert string.count("🚀", -8) == 3, string.count("🚀", -8)
+    assert string.count("ñ", 8) == 0, string.count("ñ", 8)
+    assert string.count("ñ", -8) == 2, string.count("ñ", -8)
+def test_count_start_end_emoji() -> None:
+    string = "😴🚀ñ🚀ñ🚀"
+    assert string.count("😴", 0, 4) == 1, string.count("😴", 0, 4)
+    assert string.count("🚀", 0, 4) == 2, string.count("🚀", 0, 4)
+    assert string.count("ñ", 0, 4) == 1, string.count("ñ", 0, 4)
+def test_count_multi_emoji() -> None:
+    string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀"
+    assert string.count("😴😴😴") == 1, string.count("😴😴😴")
+    assert string.count("🚀🚀🚀") == 3, string.count("🚀🚀🚀")
+    assert string.count("ñññ") == 2, string.count("ñññ")
+def test_count_multi_start_emoji() -> None:
+    string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀"
+    assert string.count("😴😴😴", 6) == string.count("😴😴😴", -12) == 0, (string.count("😴😴😴", 6), string.count("😴😴😴", -12))
+    assert string.count("🚀🚀🚀", 6) == string.count("🚀🚀🚀", -12) == 2, (string.count("🚀🚀🚀", 6), string.count("🚀🚀🚀", -12))
+    assert string.count("ñññ", 6) == string.count("ñññ", -12) == 2, (string.count("ñññ", 6), string.count("ñññ", -12))
+    # Out of bounds
+    assert string.count("😴😴😴", 20) == 0, string.count("😴😴😴", 20)
+    assert string.count("😴😴😴", -20) == 1, string.count("😴😴😴", -20)
+    assert string.count("🚀🚀🚀", 20) == 0, string.count("🚀🚀🚀", 20)
+    assert string.count("🚀🚀🚀", -20) == 3, string.count("🚀🚀🚀", -20)
+    assert string.count("ñññ", 20) == 0, string.count("ñññ", 20)
+    assert string.count("ñññ", -20) == 2, string.count("ñññ", -20)
+def test_count_multi_start_end_emoji() -> None:
+    string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀"
+    assert string.count("😴😴😴", 0, 12) == 1, string.count("😴😴😴", 0, 12)
+    assert string.count("🚀🚀🚀", 0, 12) == 2, string.count("🚀🚀🚀", 0, 12)
+    assert string.count("ñññ", 0, 12) == 1, string.count("ñññ", 0, 12)

From cbe28b23240f0091614594b05ef79708d01b5454 Mon Sep 17 00:00:00 2001
From: Chainfire 
Date: Wed, 2 Jul 2025 13:38:18 +0200
Subject: [PATCH 1418/1617] [mypyc] Fix AttributeError in async try/finally
 with mixed return paths (#19361)

Async functions with try/finally blocks were raising AttributeError
when:

* Some paths in the try block return while others don't
* The non-return path is executed at runtime
* No further await calls are needed

This occurred because mypyc's IR requires all control flow paths to
assign
to spill targets. The non-return path assigns NULL to maintain this
invariant, but reading NULL attributes raises AttributeError in Python.

Modified the GetAttr IR operation to support reading NULL attributes
without raising AttributeError through a new allow_null parameter. This
parameter is used specifically in try/finally resolution when reading
spill targets.

* Added allow_null: bool = False parameter to GetAttr.init in
mypyc/ir/ops.py
* When allow_null=True, sets error_kind=ERR_NEVER to prevent
AttributeError
* Modified read_nullable_attr in IRBuilder to create GetAttr with
allow_null=True
* Modified try_finally_resolve_control in statement.py to use
read_nullable_attr
  only for spill targets (attributes starting with 'mypyc_temp')
* Updated C code generation in emitfunc.py:
* visit_get_attr checks for allow_null and delegates to
get_attr_with_allow_null
* get_attr_with_allow_null reads attributes without NULL checks and only
    increments reference count if not NULL

Design decisions:

* Targeted fix: Only applied to spill targets in try/finally resolution,
not a general replacement for GetAttr. This minimizes risk and maintains
  existing behavior for all other attribute access.

* No initialization changes: Initially tried initializing spill targets
to Py_None instead of NULL, but this would incorrectly make try/finally
  blocks return None instead of falling through to subsequent code.

Added two test cases to mypyc/test-data/run-async.test:

* testAsyncTryFinallyMixedReturn: Tests the basic issue with async
  try/finally blocks containing mixed return/non-return paths.

* testAsyncWithMixedReturn: Tests async with statements (which use
  try/finally under the hood) to ensure the fix works for this common
  pattern as well.

Both tests verify that the AttributeError no longer occurs when taking
the non-return path through the try block.

See https://github.com/mypyc/mypyc/issues/1115
---
 mypyc/codegen/emitfunc.py      |  21 +++
 mypyc/ir/ops.py                |   9 +-
 mypyc/irbuild/builder.py       |   9 +
 mypyc/irbuild/statement.py     |  10 +-
 mypyc/test-data/run-async.test | 303 +++++++++++++++++++++++++++++++++
 5 files changed, 348 insertions(+), 4 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index c854516825af..00c7fd56b899 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -358,6 +358,9 @@ def get_attr_expr(self, obj: str, op: GetAttr | SetAttr, decl_cl: ClassIR) -> st
             return f"({cast}{obj})->{self.emitter.attr(op.attr)}"
 
     def visit_get_attr(self, op: GetAttr) -> None:
+        if op.allow_null:
+            self.get_attr_with_allow_null(op)
+            return
         dest = self.reg(op)
         obj = self.reg(op.obj)
         rtype = op.class_type
@@ -426,6 +429,24 @@ def visit_get_attr(self, op: GetAttr) -> None:
             elif not always_defined:
                 self.emitter.emit_line("}")
 
+    def get_attr_with_allow_null(self, op: GetAttr) -> None:
+        """Handle GetAttr with allow_null=True which allows NULL without raising AttributeError."""
+        dest = self.reg(op)
+        obj = self.reg(op.obj)
+        rtype = op.class_type
+        cl = rtype.class_ir
+        attr_rtype, decl_cl = cl.attr_details(op.attr)
+
+        # Direct struct access without NULL check
+        attr_expr = self.get_attr_expr(obj, op, decl_cl)
+        self.emitter.emit_line(f"{dest} = {attr_expr};")
+
+        # Only emit inc_ref if not NULL
+        if attr_rtype.is_refcounted and not op.is_borrowed:
+            self.emitter.emit_line(f"if ({dest} != NULL) {{")
+            self.emitter.emit_inc_ref(dest, attr_rtype)
+            self.emitter.emit_line("}")
+
     def next_branch(self) -> Branch | None:
         if self.op_index + 1 < len(self.ops):
             next_op = self.ops[self.op_index + 1]
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index eec9c34a965e..9dde658231d8 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -777,15 +777,20 @@ class GetAttr(RegisterOp):
 
     error_kind = ERR_MAGIC
 
-    def __init__(self, obj: Value, attr: str, line: int, *, borrow: bool = False) -> None:
+    def __init__(
+        self, obj: Value, attr: str, line: int, *, borrow: bool = False, allow_null: bool = False
+    ) -> None:
         super().__init__(line)
         self.obj = obj
         self.attr = attr
+        self.allow_null = allow_null
         assert isinstance(obj.type, RInstance), "Attribute access not supported: %s" % obj.type
         self.class_type = obj.type
         attr_type = obj.type.attr_type(attr)
         self.type = attr_type
-        if attr_type.error_overlap:
+        if allow_null:
+            self.error_kind = ERR_NEVER
+        elif attr_type.error_overlap:
             self.error_kind = ERR_MAGIC_OVERLAPPING
         self.is_borrowed = borrow and attr_type.is_refcounted
 
diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 75e059a5b570..878c5e76df3d 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -708,6 +708,15 @@ def read(
 
         assert False, "Unsupported lvalue: %r" % target
 
+    def read_nullable_attr(self, obj: Value, attr: str, line: int = -1) -> Value:
+        """Read an attribute that might be NULL without raising AttributeError.
+
+        This is used for reading spill targets in try/finally blocks where NULL
+        indicates the non-return path was taken.
+        """
+        assert isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class
+        return self.add(GetAttr(obj, attr, line, allow_null=True))
+
     def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: int) -> None:
         if isinstance(target, Register):
             self.add(Assign(target, self.coerce_rvalue(rvalue_reg, target.type, line)))
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index 16a0483a8729..5c32d8f1a50c 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -46,6 +46,7 @@
     YieldExpr,
     YieldFromExpr,
 )
+from mypyc.common import TEMP_ATTR_NAME
 from mypyc.ir.ops import (
     NAMESPACE_MODULE,
     NO_TRACEBACK_LINE_NO,
@@ -653,10 +654,15 @@ def try_finally_resolve_control(
     if ret_reg:
         builder.activate_block(rest)
         return_block, rest = BasicBlock(), BasicBlock()
-        builder.add(Branch(builder.read(ret_reg), rest, return_block, Branch.IS_ERROR))
+        # For spill targets in try/finally, use nullable read to avoid AttributeError
+        if isinstance(ret_reg, AssignmentTargetAttr) and ret_reg.attr.startswith(TEMP_ATTR_NAME):
+            ret_val = builder.read_nullable_attr(ret_reg.obj, ret_reg.attr, -1)
+        else:
+            ret_val = builder.read(ret_reg)
+        builder.add(Branch(ret_val, rest, return_block, Branch.IS_ERROR))
 
         builder.activate_block(return_block)
-        builder.nonlocal_control[-1].gen_return(builder, builder.read(ret_reg), -1)
+        builder.nonlocal_control[-1].gen_return(builder, ret_val, -1)
 
     # TODO: handle break/continue
     builder.activate_block(rest)
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 11ce67077270..2dad720f99cd 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -643,3 +643,306 @@ def test_async_def_contains_two_nested_functions() -> None:
 
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
+
+[case testAsyncTryFinallyMixedReturn]
+# This used to raise an AttributeError, when:
+# - the try block contains multiple paths
+# - at least one of those explicitly returns
+# - at least one of those does not explicitly return
+# - the non-returning path is taken at runtime
+
+import asyncio
+
+
+async def test_mixed_return(b: bool) -> bool:
+  try:
+      if b:
+          return b
+  finally:
+      pass
+  return b
+
+
+async def test_run() -> None:
+  # Test return path
+  result1 = await test_mixed_return(True)
+  assert result1 == True
+
+  # Test non-return path
+  result2 = await test_mixed_return(False)
+  assert result2 == False
+
+
+def test_async_try_finally_mixed_return() -> None:
+  asyncio.run(test_run())
+
+[file driver.py]
+from native import test_async_try_finally_mixed_return
+test_async_try_finally_mixed_return()
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...
+
+[case testAsyncWithMixedReturn]
+# This used to raise an AttributeError, related to
+# testAsyncTryFinallyMixedReturn, this is essentially
+# a far more extensive version of that test surfacing
+# more edge cases
+
+import asyncio
+from typing import Optional, Type, Literal
+
+
+class AsyncContextManager:
+    async def __aenter__(self) -> "AsyncContextManager":
+        return self
+
+    async def __aexit__(
+        self,
+        t: Optional[Type[BaseException]],
+        v: Optional[BaseException],
+        tb: object,
+    ) -> Literal[False]:
+        return False
+
+
+# Simple async functions (generator class)
+async def test_gen_1(b: bool) -> bool:
+    async with AsyncContextManager():
+        if b:
+            return b
+    return b
+
+
+async def test_gen_2(b: bool) -> bool:
+    async with AsyncContextManager():
+        if b:
+            return b
+        else:
+            return b
+
+
+async def test_gen_3(b: bool) -> bool:
+    async with AsyncContextManager():
+        if b:
+            return b
+        else:
+            pass
+    return b
+
+
+async def test_gen_4(b: bool) -> bool:
+    ret: bool
+    async with AsyncContextManager():
+        if b:
+            ret = b
+        else:
+            ret = b
+    return ret
+
+
+async def test_gen_5(i: int) -> int:
+    async with AsyncContextManager():
+        if i == 1:
+            return i
+        elif i == 2:
+            pass
+        elif i == 3:
+            return i
+    return i
+
+
+async def test_gen_6(i: int) -> int:
+    async with AsyncContextManager():
+        if i == 1:
+            return i
+        elif i == 2:
+            return i
+        elif i == 3:
+            return i
+    return i
+
+
+async def test_gen_7(i: int) -> int:
+    async with AsyncContextManager():
+        if i == 1:
+            return i
+        elif i == 2:
+            return i
+        elif i == 3:
+            return i
+        else:
+            return i
+
+
+# Async functions with nested functions (environment class)
+async def test_env_1(b: bool) -> bool:
+    def helper() -> bool:
+        return True
+
+    async with AsyncContextManager():
+        if b:
+            return helper()
+    return b
+
+
+async def test_env_2(b: bool) -> bool:
+    def helper() -> bool:
+        return True
+
+    async with AsyncContextManager():
+        if b:
+            return helper()
+        else:
+            return b
+
+
+async def test_env_3(b: bool) -> bool:
+    def helper() -> bool:
+        return True
+
+    async with AsyncContextManager():
+        if b:
+            return helper()
+        else:
+            pass
+    return b
+
+
+async def test_env_4(b: bool) -> bool:
+    def helper() -> bool:
+        return True
+
+    ret: bool
+    async with AsyncContextManager():
+        if b:
+            ret = helper()
+        else:
+            ret = b
+    return ret
+
+
+async def test_env_5(i: int) -> int:
+    def helper() -> int:
+        return 1
+
+    async with AsyncContextManager():
+        if i == 1:
+            return helper()
+        elif i == 2:
+            pass
+        elif i == 3:
+            return i
+    return i
+
+
+async def test_env_6(i: int) -> int:
+    def helper() -> int:
+        return 1
+
+    async with AsyncContextManager():
+        if i == 1:
+            return helper()
+        elif i == 2:
+            return i
+        elif i == 3:
+            return i
+    return i
+
+
+async def test_env_7(i: int) -> int:
+    def helper() -> int:
+        return 1
+
+    async with AsyncContextManager():
+        if i == 1:
+            return helper()
+        elif i == 2:
+            return i
+        elif i == 3:
+            return i
+        else:
+            return i
+
+
+async def run_all_tests() -> None:
+    # Test simple async functions (generator class)
+    # test_env_1: mixed return/no-return
+    assert await test_gen_1(True) is True
+    assert await test_gen_1(False) is False
+
+    # test_gen_2: all branches return
+    assert await test_gen_2(True) is True
+    assert await test_gen_2(False) is False
+
+    # test_gen_3: mixed return/pass
+    assert await test_gen_3(True) is True
+    assert await test_gen_3(False) is False
+
+    # test_gen_4: no returns in async with
+    assert await test_gen_4(True) is True
+    assert await test_gen_4(False) is False
+
+    # test_gen_5: multiple branches, some return
+    assert await test_gen_5(0) == 0
+    assert await test_gen_5(1) == 1
+    assert await test_gen_5(2) == 2
+    assert await test_gen_5(3) == 3
+
+    # test_gen_6: all explicit branches return, implicit fallthrough
+    assert await test_gen_6(0) == 0
+    assert await test_gen_6(1) == 1
+    assert await test_gen_6(2) == 2
+    assert await test_gen_6(3) == 3
+
+    # test_gen_7: all branches return including else
+    assert await test_gen_7(0) == 0
+    assert await test_gen_7(1) == 1
+    assert await test_gen_7(2) == 2
+    assert await test_gen_7(3) == 3
+
+    # Test async functions with nested functions (environment class)
+    # test_env_1: mixed return/no-return
+    assert await test_env_1(True) is True
+    assert await test_env_1(False) is False
+
+    # test_env_2: all branches return
+    assert await test_env_2(True) is True
+    assert await test_env_2(False) is False
+
+    # test_env_3: mixed return/pass
+    assert await test_env_3(True) is True
+    assert await test_env_3(False) is False
+
+    # test_env_4: no returns in async with
+    assert await test_env_4(True) is True
+    assert await test_env_4(False) is False
+
+    # test_env_5: multiple branches, some return
+    assert await test_env_5(0) == 0
+    assert await test_env_5(1) == 1
+    assert await test_env_5(2) == 2
+    assert await test_env_5(3) == 3
+
+    # test_env_6: all explicit branches return, implicit fallthrough
+    assert await test_env_6(0) == 0
+    assert await test_env_6(1) == 1
+    assert await test_env_6(2) == 2
+    assert await test_env_6(3) == 3
+
+    # test_env_7: all branches return including else
+    assert await test_env_7(0) == 0
+    assert await test_env_7(1) == 1
+    assert await test_env_7(2) == 2
+    assert await test_env_7(3) == 3
+
+
+def test_async_with_mixed_return() -> None:
+    asyncio.run(run_all_tests())
+
+[file driver.py]
+from native import test_async_with_mixed_return
+test_async_with_mixed_return()
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...

From eba2336467cd8fa9cff77dbf5821616691cfd76e Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 3 Jul 2025 11:57:29 +0100
Subject: [PATCH 1419/1617] [mypyc] Speed up generator allocation by using a
 per-type freelist (#19316)

Add support for per-type free "lists" that can cache up to one instance
for quick allocation.

If a free list is empty, fall back to regular object allocation.

The per-type free list can be enabled for a class by setting a flag in
ClassIR. Currently there is no way for users to control this, and these
must be enabled based on heuristics.

Use this free list for generator objects and coroutines, since they are
often short-lived.

Use a thread local variable for the free list so that each thread in a
free threaded build has a separate free list. This way we need less
synchronization, and the free list hit rate is higher for multithreaded
workloads.

This speeds up a microbenchmark that performs non-blocking calls of
async functions in a loop by about 20%. The impact will become
significantly bigger after some follow-up optimizations that I'm working
on.

This trades off memory use for performance, which is often good. This
could use a lot of memory if many threads are calling async functions,
but generally async functions are run on a single thread, so this case
seems unlikely right now. Also, in my experience with large code bases
only a small fraction of functions are async functions or generators, so
the overall memory use impact shouldn't be too bad.

We can later look into making this profile guided, so that only
functions that are called frequently get the free list. Also we could
add a compile-time flag to optimize for memory use, and it would turn
this off.
---
 mypyc/codegen/emit.py               |  25 ++++++
 mypyc/codegen/emitclass.py          |  98 +++++++++++++++++---
 mypyc/codegen/emitmodule.py         |   4 +-
 mypyc/ir/class_ir.py                |   8 ++
 mypyc/irbuild/generator.py          |   1 +
 mypyc/lib-rt/mypyc_util.h           |  25 ++++++
 mypyc/test-data/run-generators.test | 134 +++++++++++++++++++++++++++-
 7 files changed, 281 insertions(+), 14 deletions(-)

diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py
index d7d7d9c7abda..ba8b8307e1fd 100644
--- a/mypyc/codegen/emit.py
+++ b/mypyc/codegen/emit.py
@@ -1115,6 +1115,31 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None:
         else:
             assert False, "emit_gc_clear() not implemented for %s" % repr(rtype)
 
+    def emit_reuse_clear(self, target: str, rtype: RType) -> None:
+        """Emit attribute clear before object is added into freelist.
+
+        Assume that 'target' represents a C expression that refers to a
+        struct member, such as 'self->x'.
+
+        Unlike emit_gc_clear(), initialize attribute value to match a freshly
+        allocated object.
+        """
+        if isinstance(rtype, RTuple):
+            for i, item_type in enumerate(rtype.types):
+                self.emit_reuse_clear(f"{target}.f{i}", item_type)
+        elif not rtype.is_refcounted:
+            self.emit_line(f"{target} = {rtype.c_undefined};")
+        elif isinstance(rtype, RPrimitive) and rtype.name == "builtins.int":
+            self.emit_line(f"if (CPyTagged_CheckLong({target})) {{")
+            self.emit_line(f"CPyTagged __tmp = {target};")
+            self.emit_line(f"{target} = {self.c_undefined_value(rtype)};")
+            self.emit_line("Py_XDECREF(CPyTagged_LongAsObject(__tmp));")
+            self.emit_line("} else {")
+            self.emit_line(f"{target} = {self.c_undefined_value(rtype)};")
+            self.emit_line("}")
+        else:
+            self.emit_gc_clear(target, rtype)
+
     def emit_traceback(
         self, source_path: str, module_name: str, traceback_entry: tuple[str, int]
     ) -> None:
diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index da3d14f9dafe..576787424cbf 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -186,6 +186,29 @@ def generate_class_type_decl(
         )
 
 
+def generate_class_reuse(
+    cl: ClassIR, c_emitter: Emitter, external_emitter: Emitter, emitter: Emitter
+) -> None:
+    """Generate a definition of a single-object per-class free "list".
+
+    This speeds up object allocation and freeing when there are many short-lived
+    objects.
+
+    TODO: Generalize to support a free list with up to N objects.
+    """
+    assert cl.reuse_freed_instance
+
+    # The free list implementation doesn't support class hierarchies
+    assert cl.is_final_class or cl.children == []
+
+    context = c_emitter.context
+    name = cl.name_prefix(c_emitter.names) + "_free_instance"
+    struct_name = cl.struct_name(c_emitter.names)
+    context.declarations[name] = HeaderDeclaration(
+        f"CPyThreadLocal {struct_name} *{name};", needs_export=True
+    )
+
+
 def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
     """Generate C code for a class.
 
@@ -557,7 +580,22 @@ def generate_setup_for_class(
     emitter.emit_line("static PyObject *")
     emitter.emit_line(f"{func_name}(PyTypeObject *type)")
     emitter.emit_line("{")
-    emitter.emit_line(f"{cl.struct_name(emitter.names)} *self;")
+    struct_name = cl.struct_name(emitter.names)
+    emitter.emit_line(f"{struct_name} *self;")
+
+    prefix = cl.name_prefix(emitter.names)
+    if cl.reuse_freed_instance:
+        # Attempt to use a per-type free list first (a free "list" with up to one object only).
+        emitter.emit_line(f"if ({prefix}_free_instance != NULL) {{")
+        emitter.emit_line(f"self = {prefix}_free_instance;")
+        emitter.emit_line(f"{prefix}_free_instance = NULL;")
+        emitter.emit_line("Py_SET_REFCNT(self, 1);")
+        emitter.emit_line("PyObject_GC_Track(self);")
+        if defaults_fn is not None:
+            emit_attr_defaults_func_call(defaults_fn, "self", emitter)
+        emitter.emit_line("return (PyObject *)self;")
+        emitter.emit_line("}")
+
     emitter.emit_line(f"self = ({cl.struct_name(emitter.names)} *)type->tp_alloc(type, 0);")
     emitter.emit_line("if (self == NULL)")
     emitter.emit_line("    return NULL;")
@@ -571,9 +609,7 @@ def generate_setup_for_class(
     else:
         emitter.emit_line(f"self->vtable = {vtable_name};")
 
-    for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS):
-        field = emitter.bitmap_field(i)
-        emitter.emit_line(f"self->{field} = 0;")
+    emit_clear_bitmaps(cl, emitter)
 
     if cl.has_method("__call__"):
         name = cl.method_decl("__call__").cname(emitter.names)
@@ -590,19 +626,34 @@ def generate_setup_for_class(
 
     # Initialize attributes to default values, if necessary
     if defaults_fn is not None:
-        emitter.emit_lines(
-            "if ({}{}((PyObject *)self) == 0) {{".format(
-                NATIVE_PREFIX, defaults_fn.cname(emitter.names)
-            ),
-            "Py_DECREF(self);",
-            "return NULL;",
-            "}",
-        )
+        emit_attr_defaults_func_call(defaults_fn, "self", emitter)
 
     emitter.emit_line("return (PyObject *)self;")
     emitter.emit_line("}")
 
 
+def emit_clear_bitmaps(cl: ClassIR, emitter: Emitter) -> None:
+    """Emit C code to clear bitmaps that track if attributes have an assigned value."""
+    for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS):
+        field = emitter.bitmap_field(i)
+        emitter.emit_line(f"self->{field} = 0;")
+
+
+def emit_attr_defaults_func_call(defaults_fn: FuncIR, self_name: str, emitter: Emitter) -> None:
+    """Emit C code to initialize attribute defaults by calling defaults_fn.
+
+    The code returns NULL on a raised exception.
+    """
+    emitter.emit_lines(
+        "if ({}{}((PyObject *){}) == 0) {{".format(
+            NATIVE_PREFIX, defaults_fn.cname(emitter.names), self_name
+        ),
+        "Py_DECREF(self);",
+        "return NULL;",
+        "}",
+    )
+
+
 def generate_constructor_for_class(
     cl: ClassIR,
     fn: FuncDecl,
@@ -787,6 +838,8 @@ def generate_dealloc_for_class(
         emitter.emit_line("Py_TYPE(self)->tp_finalize((PyObject *)self);")
         emitter.emit_line("}")
     emitter.emit_line("PyObject_GC_UnTrack(self);")
+    if cl.reuse_freed_instance:
+        emit_reuse_dealloc(cl, emitter)
     # The trashcan is needed to handle deep recursive deallocations
     emitter.emit_line(f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})")
     emitter.emit_line(f"{clear_func_name}(self);")
@@ -795,6 +848,27 @@ def generate_dealloc_for_class(
     emitter.emit_line("}")
 
 
+def emit_reuse_dealloc(cl: ClassIR, emitter: Emitter) -> None:
+    """Emit code to deallocate object by putting it to per-type free list.
+
+    The free "list" currently can have up to one object.
+    """
+    prefix = cl.name_prefix(emitter.names)
+    emitter.emit_line(f"if ({prefix}_free_instance == NULL) {{")
+    emitter.emit_line(f"{prefix}_free_instance = self;")
+
+    # Clear attributes and free referenced objects.
+
+    emit_clear_bitmaps(cl, emitter)
+
+    for base in reversed(cl.base_mro):
+        for attr, rtype in base.attributes.items():
+            emitter.emit_reuse_clear(f"self->{emitter.attr(attr)}", rtype)
+
+    emitter.emit_line("return;")
+    emitter.emit_line("}")
+
+
 def generate_finalize_for_class(
     del_method: FuncIR, finalize_func_name: str, emitter: Emitter
 ) -> None:
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index f914bfd6345d..e1b6a7857294 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -29,7 +29,7 @@
 from mypy.util import hash_digest, json_dumps
 from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import Emitter, EmitterContext, HeaderDeclaration, c_array_initializer
-from mypyc.codegen.emitclass import generate_class, generate_class_type_decl
+from mypyc.codegen.emitclass import generate_class, generate_class_reuse, generate_class_type_decl
 from mypyc.codegen.emitfunc import generate_native_function, native_function_header
 from mypyc.codegen.emitwrapper import (
     generate_legacy_wrapper_function,
@@ -609,6 +609,8 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
             self.declare_finals(module_name, module.final_names, declarations)
             for cl in module.classes:
                 generate_class_type_decl(cl, emitter, ext_declarations, declarations)
+                if cl.reuse_freed_instance:
+                    generate_class_reuse(cl, emitter, ext_declarations, declarations)
             self.declare_type_vars(module_name, module.type_var_names, declarations)
             for fn in module.functions:
                 generate_function_declaration(fn, declarations)
diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py
index c88b9b0c7afc..561dc9d438c4 100644
--- a/mypyc/ir/class_ir.py
+++ b/mypyc/ir/class_ir.py
@@ -204,6 +204,12 @@ def __init__(
         # If this is a generator environment class, what is the actual method for it
         self.env_user_function: FuncIR | None = None
 
+        # If True, keep one freed, cleared instance available for immediate reuse to
+        # speed up allocations. This helps if many objects are freed quickly, before
+        # other instances of the same class are allocated. This is effectively a
+        # per-type free "list" of up to length 1.
+        self.reuse_freed_instance = False
+
     def __repr__(self) -> str:
         return (
             "ClassIR("
@@ -403,6 +409,7 @@ def serialize(self) -> JsonDict:
             "_sometimes_initialized_attrs": sorted(self._sometimes_initialized_attrs),
             "init_self_leak": self.init_self_leak,
             "env_user_function": self.env_user_function.id if self.env_user_function else None,
+            "reuse_freed_instance": self.reuse_freed_instance,
         }
 
     @classmethod
@@ -458,6 +465,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR:
         ir.env_user_function = (
             ctx.functions[data["env_user_function"]] if data["env_user_function"] else None
         )
+        ir.reuse_freed_instance = data["reuse_freed_instance"]
 
         return ir
 
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 782cb4319757..0e4b0e3e184a 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -156,6 +156,7 @@ def setup_generator_class(builder: IRBuilder) -> ClassIR:
     name = f"{builder.fn_info.namespaced_name()}_gen"
 
     generator_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True)
+    generator_class_ir.reuse_freed_instance = True
     if builder.fn_info.can_merge_generator_and_env_classes():
         builder.fn_info.env_class = generator_class_ir
     else:
diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h
index 27a11ab9f581..3d4eba3a3cdb 100644
--- a/mypyc/lib-rt/mypyc_util.h
+++ b/mypyc/lib-rt/mypyc_util.h
@@ -23,6 +23,31 @@
 #define CPy_NOINLINE
 #endif
 
+#ifndef Py_GIL_DISABLED
+
+// Everything is running in the same thread, so no need for thread locals
+#define CPyThreadLocal
+
+#else
+
+// 1. Use C11 standard thread_local storage, if available
+#if defined(__STDC_VERSION__)  && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
+#define CPyThreadLocal _Thread_local
+
+// 2. Microsoft Visual Studio fallback
+#elif defined(_MSC_VER)
+#define CPyThreadLocal __declspec(thread)
+
+// 3. GNU thread local storage for GCC/Clang targets that still need it
+#elif defined(__GNUC__) || defined(__clang__)
+#define CPyThreadLocal __thread
+
+#else
+#error "Can't define CPyThreadLocal for this compiler/target (consider using a non-free-threaded Python build)"
+#endif
+
+#endif // Py_GIL_DISABLED
+
 // INCREF and DECREF that assert the pointer is not NULL.
 // asserts are disabled in release builds so there shouldn't be a perf hit.
 // I'm honestly kind of surprised that this isn't done by default.
diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test
index 2e55ded76f74..9c0b51d58e79 100644
--- a/mypyc/test-data/run-generators.test
+++ b/mypyc/test-data/run-generators.test
@@ -681,7 +681,6 @@ def test_basic() -> None:
         assert context.x == 1
     assert context.x == 0
 
-
 [case testYieldSpill]
 from typing import Generator
 from testutil import run_generator
@@ -697,3 +696,136 @@ def test_basic() -> None:
     yields, val = x
     assert yields == ('foo',)
     assert val == 3, val
+
+[case testGeneratorReuse]
+from typing import Iterator, Any
+
+def gen(x: list[int]) -> Iterator[list[int]]:
+    y = [9]
+    for z in x:
+        yield y + [z]
+    yield y
+
+def gen_range(n: int) -> Iterator[int]:
+    for x in range(n):
+        yield x
+
+def test_use_generator_multiple_times_one_at_a_time() -> None:
+    for i in range(100):
+        a = []
+        for x in gen([2, i]):
+            a.append(x)
+        assert a == [[9, 2], [9, i], [9]]
+
+def test_use_multiple_generator_instances_at_same_time() -> None:
+    a = []
+    for x in gen([2]):
+        a.append(x)
+        for y in gen([3, 4]):
+            a.append(y)
+    assert a == [[9, 2], [9, 3], [9, 4], [9], [9], [9, 3], [9, 4], [9]]
+
+def test_use_multiple_generator_instances_at_same_time_2() -> None:
+    a = []
+    for x in gen_range(2):
+        a.append(x)
+        b = []
+        for y in gen_range(3):
+            b.append(y)
+            c = []
+            for z in gen_range(4):
+                c.append(z)
+            assert c == [0, 1, 2, 3]
+        assert b == [0, 1, 2]
+    assert a == [0, 1]
+    assert list(gen_range(5)) == list(range(5))
+
+def gen_a(x: int) -> Iterator[int]:
+    yield x + 1
+
+def gen_b(x: int) -> Iterator[int]:
+    yield x + 2
+
+def test_generator_identities() -> None:
+    # Sanity check: two distinct live objects can't reuse the same memory location
+    g1 = gen_a(1)
+    g2 = gen_a(1)
+    assert g1 is not g2
+
+    # If two generators have non-overlapping lifetimes, they should reuse a memory location
+    g3 = gen_b(1)
+    id1 = id(g3)
+    g3 = gen_b(1)
+    assert id(g3) == id1
+
+    # More complex case of reuse: allocate other objects in between
+    g4: Any = gen_a(1)
+    id2 = id(g4)
+    g4 = gen_b(1)
+    g4 = [gen_b(n) for n in range(100)]
+    g4 = gen_a(1)
+    assert id(g4) == id2
+
+[case testGeneratorReuseWithGilDisabled]
+import sys
+import threading
+from typing import Iterator
+
+def gen() -> Iterator[int]:
+    yield 1
+
+def is_gil_disabled() -> bool:
+    return hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled()
+
+def test_each_thread_gets_separate_instance() -> None:
+    if not is_gil_disabled():
+        # This only makes sense if GIL is disabled
+        return
+
+    g = gen()
+    id1 = id(g)
+
+    id2 = 0
+
+    def run() -> None:
+        nonlocal id2
+        g = gen()
+        id2 = id(g)
+
+    t = threading.Thread(target=run)
+    t.start()
+    t.join()
+
+    # Each thread should get a separate reused instance
+    assert id1 != id2
+
+[case testGeneratorWithUndefinedLocalInEnvironment]
+from typing import Iterator
+
+from testutil import assertRaises
+
+def gen(set: bool) -> Iterator[float]:
+    if set:
+        y = float("-113.0")
+    yield 1.0
+    yield y
+
+def test_bitmap_is_cleared_when_object_is_reused() -> None:
+    # This updates the bitmap of the shared instance.
+    list(gen(True))
+
+    # Ensure bitmap has been cleared.
+    with assertRaises(AttributeError):  # TODO: Should be UnboundLocalError
+      list(gen(False))
+
+def gen2(set: bool) -> Iterator[int]:
+    if set:
+        y = int("5")
+    yield 1
+    yield y
+
+def test_undefined_int_in_environment() -> None:
+    list(gen2(True))
+
+    with assertRaises(AttributeError):  # TODO: Should be UnboundLocalError
+      list(gen2(False))

From 1bf186cbf752401e8e85153f72c24514797d1be4 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 3 Jul 2025 11:59:20 +0100
Subject: [PATCH 1420/1617] [mypyc] Document some of our inheritance
 conventions (#19370)

Our policy wasn't clear from just reading the code.
---
 mypyc/ir/ops.py    | 65 +++++++++++++++++++++++++++++++++++++++++++---
 mypyc/ir/rtypes.py | 24 ++++++++++++++++-
 2 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 9dde658231d8..1cb3df916ac9 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -3,17 +3,31 @@
 Opcodes operate on abstract values (Value) in a register machine. Each
 value has a type (RType). A value can hold various things, such as:
 
-- local variables (Register)
+- local variables or temporaries (Register)
 - intermediate values of expressions (RegisterOp subclasses)
 - condition flags (true/false)
 - literals (integer literals, True, False, etc.)
+
+NOTE: As a convention, we don't create subclasses of concrete Value/Op
+      subclasses (e.g. you shouldn't define a subclass of Integer, which
+      is a concrete class).
+
+      If you want to introduce a variant of an existing class, you'd
+      typically add an attribute (e.g. a flag) to an existing concrete
+      class to enable the new behavior. Sometimes adding a new abstract
+      base class is also an option, or just creating a new subclass
+      without any inheritance relationship (some duplication of code
+      is preferred over introducing complex implementation inheritance).
+
+      This makes it possible to use isinstance(x, ) checks without worrying about potential subclasses.
 """
 
 from __future__ import annotations
 
 from abc import abstractmethod
 from collections.abc import Sequence
-from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union
+from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union, final
 
 from mypy_extensions import trait
 
@@ -47,6 +61,7 @@
 T = TypeVar("T")
 
 
+@final
 class BasicBlock:
     """IR basic block.
 
@@ -142,6 +157,7 @@ def is_void(self) -> bool:
         return isinstance(self.type, RVoid)
 
 
+@final
 class Register(Value):
     """A Register holds a value of a specific type, and it can be read and mutated.
 
@@ -168,6 +184,7 @@ def __repr__(self) -> str:
         return f""
 
 
+@final
 class Integer(Value):
     """Short integer literal.
 
@@ -198,6 +215,7 @@ def numeric_value(self) -> int:
         return self.value
 
 
+@final
 class Float(Value):
     """Float literal.
 
@@ -257,13 +275,14 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 
 
 class BaseAssign(Op):
-    """Base class for ops that assign to a register."""
+    """Abstract base class for ops that assign to a register."""
 
     def __init__(self, dest: Register, line: int = -1) -> None:
         super().__init__(line)
         self.dest = dest
 
 
+@final
 class Assign(BaseAssign):
     """Assign a value to a Register (dest = src)."""
 
@@ -286,6 +305,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_assign(self)
 
 
+@final
 class AssignMulti(BaseAssign):
     """Assign multiple values to a Register (dest = src1, src2, ...).
 
@@ -320,7 +340,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 
 
 class ControlOp(Op):
-    """Control flow operation."""
+    """Abstract base class for control flow operations."""
 
     def targets(self) -> Sequence[BasicBlock]:
         """Get all basic block targets of the control operation."""
@@ -331,6 +351,7 @@ def set_target(self, i: int, new: BasicBlock) -> None:
         raise AssertionError(f"Invalid set_target({self}, {i})")
 
 
+@final
 class Goto(ControlOp):
     """Unconditional jump."""
 
@@ -360,6 +381,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_goto(self)
 
 
+@final
 class Branch(ControlOp):
     """Branch based on a value.
 
@@ -426,6 +448,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_branch(self)
 
 
+@final
 class Return(ControlOp):
     """Return a value from a function."""
 
@@ -455,6 +478,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_return(self)
 
 
+@final
 class Unreachable(ControlOp):
     """Mark the end of basic block as unreachable.
 
@@ -511,6 +535,7 @@ def can_raise(self) -> bool:
         return self.error_kind != ERR_NEVER
 
 
+@final
 class IncRef(RegisterOp):
     """Increase reference count (inc_ref src)."""
 
@@ -531,6 +556,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_inc_ref(self)
 
 
+@final
 class DecRef(RegisterOp):
     """Decrease reference count and free object if zero (dec_ref src).
 
@@ -559,6 +585,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_dec_ref(self)
 
 
+@final
 class Call(RegisterOp):
     """Native call f(arg, ...).
 
@@ -587,6 +614,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_call(self)
 
 
+@final
 class MethodCall(RegisterOp):
     """Native method call obj.method(arg, ...)"""
 
@@ -618,6 +646,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_method_call(self)
 
 
+@final
 class PrimitiveDescription:
     """Description of a primitive op.
 
@@ -670,6 +699,7 @@ def __repr__(self) -> str:
         return f""
 
 
+@final
 class PrimitiveOp(RegisterOp):
     """A higher-level primitive operation.
 
@@ -707,6 +737,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_primitive_op(self)
 
 
+@final
 class LoadErrorValue(RegisterOp):
     """Load an error value.
 
@@ -737,6 +768,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_error_value(self)
 
 
+@final
 class LoadLiteral(RegisterOp):
     """Load a Python literal object (dest = 'foo' / b'foo' / ...).
 
@@ -772,6 +804,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_literal(self)
 
 
+@final
 class GetAttr(RegisterOp):
     """obj.attr (for a native object)"""
 
@@ -804,6 +837,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_get_attr(self)
 
 
+@final
 class SetAttr(RegisterOp):
     """obj.attr = src (for a native object)
 
@@ -855,6 +889,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 NAMESPACE_TYPE_VAR: Final = "typevar"
 
 
+@final
 class LoadStatic(RegisterOp):
     """Load a static name (name :: static).
 
@@ -895,6 +930,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_static(self)
 
 
+@final
 class InitStatic(RegisterOp):
     """static = value :: static
 
@@ -927,6 +963,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_init_static(self)
 
 
+@final
 class TupleSet(RegisterOp):
     """dest = (reg, ...) (for fixed-length tuple)"""
 
@@ -959,6 +996,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_tuple_set(self)
 
 
+@final
 class TupleGet(RegisterOp):
     """Get item of a fixed-length tuple (src[index])."""
 
@@ -983,6 +1021,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_tuple_get(self)
 
 
+@final
 class Cast(RegisterOp):
     """cast(type, src)
 
@@ -1014,6 +1053,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_cast(self)
 
 
+@final
 class Box(RegisterOp):
     """box(type, src)
 
@@ -1048,6 +1088,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_box(self)
 
 
+@final
 class Unbox(RegisterOp):
     """unbox(type, src)
 
@@ -1074,6 +1115,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_unbox(self)
 
 
+@final
 class RaiseStandardError(RegisterOp):
     """Raise built-in exception with an optional error string.
 
@@ -1113,6 +1155,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 StealsDescription = Union[bool, list[bool]]
 
 
+@final
 class CallC(RegisterOp):
     """result = function(arg0, arg1, ...)
 
@@ -1167,6 +1210,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_call_c(self)
 
 
+@final
 class Truncate(RegisterOp):
     """result = truncate src from src_type to dst_type
 
@@ -1197,6 +1241,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_truncate(self)
 
 
+@final
 class Extend(RegisterOp):
     """result = extend src from src_type to dst_type
 
@@ -1231,6 +1276,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_extend(self)
 
 
+@final
 class LoadGlobal(RegisterOp):
     """Load a low-level global variable/pointer.
 
@@ -1258,6 +1304,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_global(self)
 
 
+@final
 class IntOp(RegisterOp):
     """Binary arithmetic or bitwise op on integer operands (e.g., r1 = r2 + r3).
 
@@ -1322,6 +1369,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 int_op_to_id: Final = {op: op_id for op_id, op in IntOp.op_str.items()}
 
 
+@final
 class ComparisonOp(RegisterOp):
     """Low-level comparison op for integers and pointers.
 
@@ -1383,6 +1431,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_comparison_op(self)
 
 
+@final
 class FloatOp(RegisterOp):
     """Binary float arithmetic op (e.g., r1 = r2 + r3).
 
@@ -1424,6 +1473,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 float_op_to_id: Final = {op: op_id for op_id, op in FloatOp.op_str.items()}
 
 
+@final
 class FloatNeg(RegisterOp):
     """Float negation op (r1 = -r2)."""
 
@@ -1444,6 +1494,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_float_neg(self)
 
 
+@final
 class FloatComparisonOp(RegisterOp):
     """Low-level comparison op for floats."""
 
@@ -1480,6 +1531,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
 float_comparison_op_to_id: Final = {op: op_id for op_id, op in FloatComparisonOp.op_str.items()}
 
 
+@final
 class LoadMem(RegisterOp):
     """Read a memory location: result = *(type *)src.
 
@@ -1509,6 +1561,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_mem(self)
 
 
+@final
 class SetMem(Op):
     """Write to a memory location: *(type *)dest = src
 
@@ -1540,6 +1593,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_set_mem(self)
 
 
+@final
 class GetElementPtr(RegisterOp):
     """Get the address of a struct element.
 
@@ -1566,6 +1620,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_get_element_ptr(self)
 
 
+@final
 class LoadAddress(RegisterOp):
     """Get the address of a value: result = (type)&src
 
@@ -1600,6 +1655,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_load_address(self)
 
 
+@final
 class KeepAlive(RegisterOp):
     """A no-op operation that ensures source values aren't freed.
 
@@ -1647,6 +1703,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_keep_alive(self)
 
 
+@final
 class Unborrow(RegisterOp):
     """A no-op op to create a regular reference from a borrowed one.
 
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 60a56065006f..61aadce9b9d4 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -18,12 +18,27 @@
 
 mypyc.irbuild.mapper.Mapper.type_to_rtype converts mypy Types to mypyc
 RTypes.
+
+NOTE: As a convention, we don't create subclasses of concrete RType
+      subclasses (e.g. you shouldn't define a subclass of RTuple, which
+      is a concrete class). We prefer a flat class hierarchy.
+
+      If you want to introduce a variant of an existing class, you'd
+      typically add an attribute (e.g. a flag) to an existing concrete
+      class to enable the new behavior. In rare cases, adding a new
+      abstract base class could also be an option. Adding a completely
+      separate class and sharing some functionality using module-level
+      helper functions may also be reasonable.
+
+      This makes it possible to use isinstance(x, ) checks without worrying about potential subclasses
+      and avoids most trouble caused by implementation inheritance.
 """
 
 from __future__ import annotations
 
 from abc import abstractmethod
-from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar
+from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar, final
 from typing_extensions import TypeGuard
 
 from mypyc.common import HAVE_IMMORTAL, IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name
@@ -155,6 +170,7 @@ def visit_rvoid(self, typ: RVoid, /) -> T:
         raise NotImplementedError
 
 
+@final
 class RVoid(RType):
     """The void type (no value).
 
@@ -187,6 +203,7 @@ def __hash__(self) -> int:
 void_rtype: Final = RVoid()
 
 
+@final
 class RPrimitive(RType):
     """Primitive type such as 'object' or 'int'.
 
@@ -650,6 +667,7 @@ def visit_rvoid(self, t: RVoid) -> str:
         assert False, "rvoid in tuple?"
 
 
+@final
 class RTuple(RType):
     """Fixed-length unboxed tuple (represented as a C struct).
 
@@ -791,6 +809,7 @@ def compute_aligned_offsets_and_size(types: list[RType]) -> tuple[list[int], int
     return offsets, final_size
 
 
+@final
 class RStruct(RType):
     """C struct type"""
 
@@ -844,6 +863,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> RStruct:
         assert False
 
 
+@final
 class RInstance(RType):
     """Instance of user-defined class (compiled to C extension class).
 
@@ -904,6 +924,7 @@ def serialize(self) -> str:
         return self.name
 
 
+@final
 class RUnion(RType):
     """union[x, ..., y]"""
 
@@ -994,6 +1015,7 @@ def is_optional_type(rtype: RType) -> bool:
     return optional_value_type(rtype) is not None
 
 
+@final
 class RArray(RType):
     """Fixed-length C array type (for example, int[5]).
 

From 1a99ce22832ada034c8ca61337fd8e87678ab758 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Fri, 4 Jul 2025 12:33:18 +0200
Subject: [PATCH 1421/1617] [mypyc] Fix comparison of tuples with different
 lengths (#19372)

When comparing tuples, their lengths are now compared and their contents
are only compared if their lengths are equal. Otherwise, when they have
different lengths, the comparison always evaluates to `False`.
---
 mypyc/irbuild/ll_builder.py     |  4 ++++
 mypyc/test-data/run-tuples.test | 16 ++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 6bc1eb9d0493..36b7c9241c71 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -1507,6 +1507,10 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
         assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple)
         equal = True if op == "==" else False
         result = Register(bool_rprimitive)
+        # tuples of different lengths
+        if len(lhs.type.types) != len(rhs.type.types):
+            self.add(Assign(result, self.false() if equal else self.true(), line))
+            return result
         # empty tuples
         if len(lhs.type.types) == 0 and len(rhs.type.types) == 0:
             self.add(Assign(result, self.true() if equal else self.false(), line))
diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index 1437eaef2aa5..fe9a8dff08c6 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -203,6 +203,22 @@ def f7(x: List[Tuple[int, int]]) -> int:
 def test_unbox_tuple() -> None:
     assert f7([(5, 6)]) == 11
 
+def test_comparison() -> None:
+    assert ('x','y') == ('x','y')
+    assert not(('x','y') != ('x','y'))
+
+    assert ('x','y') != ('x','y',1)
+    assert not(('x','y') == ('x','y',1))
+
+    assert ('x','y',1) != ('x','y')
+    assert not(('x','y',1) == ('x','y'))
+
+    assert ('x','y') != ()
+    assert not(('x','y') == ())
+
+    assert () != ('x','y')
+    assert not(() == ('x','y'))
+
 # Test that order is irrelevant to unions. Really I only care that this builds.
 
 class A:

From 2de3f7766d972c6c0b2c4f9b5900b40f9244aab8 Mon Sep 17 00:00:00 2001
From: esarp <11684270+esarp@users.noreply.github.com>
Date: Fri, 4 Jul 2025 11:12:53 -0500
Subject: [PATCH 1422/1617] Bump version to 1.18.0+dev (#19371)

Release branch for 1.17.0:
https://github.com/python/mypy/tree/release-1.17

Increase the dev version
---
 mypy/version.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/version.py b/mypy/version.py
index 21d23758c6dc..bb6a9582e74e 100644
--- a/mypy/version.py
+++ b/mypy/version.py
@@ -8,7 +8,7 @@
 # - Release versions have the form "1.2.3".
 # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440).
 # - Before 1.0 we had the form "0.NNN".
-__version__ = "1.17.0+dev"
+__version__ = "1.18.0+dev"
 base_version = __version__
 
 mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

From 4980ae5b80e6e9b214d979a849e349dff6cad0ab Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sat, 5 Jul 2025 12:16:54 +0100
Subject: [PATCH 1423/1617] Support type checking code fragment in profile
 script (#19379)

Previously only self check was supported. Also rename the script since
the old name was no longer suitable.
---
 ...profile_self_check.py => profile_check.py} | 38 +++++++++++++------
 1 file changed, 27 insertions(+), 11 deletions(-)
 rename misc/{profile_self_check.py => profile_check.py} (78%)

diff --git a/misc/profile_self_check.py b/misc/profile_check.py
similarity index 78%
rename from misc/profile_self_check.py
rename to misc/profile_check.py
index eb853641d6d6..b29535020f0a 100644
--- a/misc/profile_self_check.py
+++ b/misc/profile_check.py
@@ -1,4 +1,6 @@
-"""Compile mypy using mypyc and profile self-check using perf.
+"""Compile mypy using mypyc and profile type checking using perf.
+
+By default does a self check.
 
 Notes:
  - Only Linux is supported for now (TODO: add support for other profilers)
@@ -23,6 +25,8 @@
       CFLAGS="-O2 -g -fno-omit-frame-pointer"
 """
 
+from __future__ import annotations
+
 import argparse
 import glob
 import os
@@ -41,24 +45,28 @@
 TARGET_DIR = "mypy.profile.tmpdir"
 
 
-def _profile_self_check(target_dir: str) -> None:
+def _profile_type_check(target_dir: str, code: str | None) -> None:
     cache_dir = os.path.join(target_dir, ".mypy_cache")
     if os.path.exists(cache_dir):
         shutil.rmtree(cache_dir)
-    files = []
-    for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py":
-        files.extend(glob.glob(pat))
-    self_check_cmd = ["python", "-m", "mypy", "--config-file", "mypy_self_check.ini"] + files
-    cmdline = ["perf", "record", "-g"] + self_check_cmd
+    args = []
+    if code is None:
+        args.extend(["--config-file", "mypy_self_check.ini"])
+        for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py":
+            args.extend(glob.glob(pat))
+    else:
+        args.extend(["-c", code])
+    check_cmd = ["python", "-m", "mypy"] + args
+    cmdline = ["perf", "record", "-g"] + check_cmd
     t0 = time.time()
     subprocess.run(cmdline, cwd=target_dir, check=True)
     elapsed = time.time() - t0
     print(f"{elapsed:.2f}s elapsed")
 
 
-def profile_self_check(target_dir: str) -> None:
+def profile_type_check(target_dir: str, code: str | None) -> None:
     try:
-        _profile_self_check(target_dir)
+        _profile_type_check(target_dir, code)
     except subprocess.CalledProcessError:
         print("\nProfiling failed! You may missing some permissions.")
         print("\nThis may help (note that it has security implications):")
@@ -92,7 +100,7 @@ def main() -> None:
     check_requirements()
 
     parser = argparse.ArgumentParser(
-        description="Compile mypy and profile self checking using 'perf'."
+        description="Compile mypy and profile type checking using 'perf' (by default, self check)."
     )
     parser.add_argument(
         "--multi-file",
@@ -102,9 +110,17 @@ def main() -> None:
     parser.add_argument(
         "--skip-compile", action="store_true", help="use compiled mypy from previous run"
     )
+    parser.add_argument(
+        "-c",
+        metavar="CODE",
+        default=None,
+        type=str,
+        help="profile type checking Python code fragment instead of mypy self-check",
+    )
     args = parser.parse_args()
     multi_file: bool = args.multi_file
     skip_compile: bool = args.skip_compile
+    code: str | None = args.c
 
     target_dir = TARGET_DIR
 
@@ -116,7 +132,7 @@ def main() -> None:
     elif not os.path.isdir(target_dir):
         sys.exit("error: Can't find compile mypy from previous run -- can't use --skip-compile")
 
-    profile_self_check(target_dir)
+    profile_type_check(target_dir, code)
 
     print()
     print('NOTE: Compile CPython using CFLAGS="-O2 -g -fno-omit-frame-pointer" for good results')

From de23d08ff1784c763bdb2423d7caed0d421aa8d1 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sun, 6 Jul 2025 17:57:40 +0100
Subject: [PATCH 1424/1617] Subtype checking micro-optimization (#19384)

---
 mypy/subtypes.py | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 86935d0613a2..05f34aaec8f1 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -155,15 +155,13 @@ def is_subtype(
             options=options,
         )
     else:
-        assert not any(
-            {
-                ignore_type_params,
-                ignore_pos_arg_names,
-                ignore_declared_variance,
-                always_covariant,
-                ignore_promotions,
-                options,
-            }
+        assert (
+            not ignore_type_params
+            and not ignore_pos_arg_names
+            and not ignore_declared_variance
+            and not always_covariant
+            and not ignore_promotions
+            and options is None
         ), "Don't pass both context and individual flags"
     if type_state.is_assumed_subtype(left, right):
         return True

From 8df94d2b30383c230cdd51d449b4c46fdabd7be7 Mon Sep 17 00:00:00 2001
From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com>
Date: Mon, 7 Jul 2025 03:07:35 -0500
Subject: [PATCH 1425/1617] Lessen dmypy suggest path limitations for Windows
 machines (#19337)

In this pull request, we allow dmypy suggest absolute paths to contain
the drive letter colon for Windows machines. Fixes #19335.

This is done by changing how `find_node` works slightly, allowing there
to be at most two colon (`:`) characters in the passed key for windows
machines instead of just one like on all other platforms, and then using
`rsplit` and a split limit of 1 instead of just `split` like prior.

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypy/suggestions.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/mypy/suggestions.py b/mypy/suggestions.py
index 81eb20bd0ac3..45aa5ade47a4 100644
--- a/mypy/suggestions.py
+++ b/mypy/suggestions.py
@@ -27,6 +27,7 @@
 import itertools
 import json
 import os
+import sys
 from collections.abc import Iterator
 from contextlib import contextmanager
 from typing import Callable, NamedTuple, TypedDict, TypeVar, cast
@@ -549,12 +550,17 @@ def find_node(self, key: str) -> tuple[str, str, FuncDef]:
         # TODO: Also return OverloadedFuncDef -- currently these are ignored.
         node: SymbolNode | None = None
         if ":" in key:
-            if key.count(":") > 1:
+            # A colon might be part of a drive name on Windows (like `C:/foo/bar`)
+            # and is also used as a delimiter between file path and lineno.
+            # If a colon is there for any of those reasons, it must be a file+line
+            # reference.
+            platform_key_count = 2 if sys.platform == "win32" else 1
+            if key.count(":") > platform_key_count:
                 raise SuggestionFailure(
                     "Malformed location for function: {}. Must be either"
                     " package.module.Class.method or path/to/file.py:line".format(key)
                 )
-            file, line = key.split(":")
+            file, line = key.rsplit(":", 1)
             if not line.isdigit():
                 raise SuggestionFailure(f"Line number must be a number. Got {line}")
             line_number = int(line)

From 3557e221f27b0bd15c62fc8162f54874598bbf0f Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 7 Jul 2025 10:09:29 +0100
Subject: [PATCH 1426/1617] Speed up the default plugin (#19385)

Fix two kinds of inefficiency in the default plugin:
* Pre-calculate various set objects, since set construction is pretty
slow
* Nested imports are pretty slow, so avoid doing them in the fast path

I also had to refactor things a little in order to optimize the nested
imports.

This speeds up self check by about 2.2%. The default plugin is called a
lot.
---
 mypy/plugins/constants.py      | 20 +++++++++
 mypy/plugins/default.py        | 78 ++++++++++++++++++++++------------
 mypy/plugins/enums.py          | 10 +----
 mypy/plugins/singledispatch.py | 19 ++-------
 4 files changed, 76 insertions(+), 51 deletions(-)
 create mode 100644 mypy/plugins/constants.py

diff --git a/mypy/plugins/constants.py b/mypy/plugins/constants.py
new file mode 100644
index 000000000000..9a09e89202de
--- /dev/null
+++ b/mypy/plugins/constants.py
@@ -0,0 +1,20 @@
+"""Constant definitions for plugins kept here to help with import cycles."""
+
+from typing import Final
+
+from mypy.semanal_enum import ENUM_BASES
+
+SINGLEDISPATCH_TYPE: Final = "functools._SingleDispatchCallable"
+SINGLEDISPATCH_REGISTER_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.register"
+SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.__call__"
+SINGLEDISPATCH_REGISTER_RETURN_CLASS: Final = "_SingleDispatchRegisterCallable"
+SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD: Final = (
+    f"functools.{SINGLEDISPATCH_REGISTER_RETURN_CLASS}.__call__"
+)
+
+ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | {
+    f"{prefix}._name_" for prefix in ENUM_BASES
+}
+ENUM_VALUE_ACCESS: Final = {f"{prefix}.value" for prefix in ENUM_BASES} | {
+    f"{prefix}._value_" for prefix in ENUM_BASES
+}
diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py
index 2002a4f06093..09f9795b593e 100644
--- a/mypy/plugins/default.py
+++ b/mypy/plugins/default.py
@@ -15,6 +15,7 @@
     MethodSigContext,
     Plugin,
 )
+from mypy.plugins import constants
 from mypy.plugins.common import try_getting_str_literals
 from mypy.subtypes import is_subtype
 from mypy.typeops import is_literal_type_like, make_simplified_union
@@ -36,22 +37,36 @@
     get_proper_types,
 )
 
+TD_SETDEFAULT_NAMES: Final = {n + ".setdefault" for n in TPDICT_FB_NAMES}
+TD_POP_NAMES: Final = {n + ".pop" for n in TPDICT_FB_NAMES}
+
+TD_UPDATE_METHOD_NAMES: Final = (
+    {n + ".update" for n in TPDICT_FB_NAMES}
+    | {n + ".__or__" for n in TPDICT_FB_NAMES}
+    | {n + ".__ror__" for n in TPDICT_FB_NAMES}
+    | {n + ".__ior__" for n in TPDICT_FB_NAMES}
+)
+
 
 class DefaultPlugin(Plugin):
     """Type checker plugin that is enabled by default."""
 
     def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None:
-        from mypy.plugins import ctypes, enums, singledispatch
-
         if fullname == "_ctypes.Array":
+            from mypy.plugins import ctypes
+
             return ctypes.array_constructor_callback
         elif fullname == "functools.singledispatch":
+            from mypy.plugins import singledispatch
+
             return singledispatch.create_singledispatch_function_callback
         elif fullname == "functools.partial":
             import mypy.plugins.functools
 
             return mypy.plugins.functools.partial_new_callback
         elif fullname == "enum.member":
+            from mypy.plugins import enums
+
             return enums.enum_member_callback
 
         return None
@@ -59,47 +74,42 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type]
     def get_function_signature_hook(
         self, fullname: str
     ) -> Callable[[FunctionSigContext], FunctionLike] | None:
-        from mypy.plugins import attrs, dataclasses
-
         if fullname in ("attr.evolve", "attrs.evolve", "attr.assoc", "attrs.assoc"):
+            from mypy.plugins import attrs
+
             return attrs.evolve_function_sig_callback
         elif fullname in ("attr.fields", "attrs.fields"):
+            from mypy.plugins import attrs
+
             return attrs.fields_function_sig_callback
         elif fullname == "dataclasses.replace":
+            from mypy.plugins import dataclasses
+
             return dataclasses.replace_function_sig_callback
         return None
 
     def get_method_signature_hook(
         self, fullname: str
     ) -> Callable[[MethodSigContext], FunctionLike] | None:
-        from mypy.plugins import ctypes, singledispatch
-
         if fullname == "typing.Mapping.get":
             return typed_dict_get_signature_callback
-        elif fullname in {n + ".setdefault" for n in TPDICT_FB_NAMES}:
+        elif fullname in TD_SETDEFAULT_NAMES:
             return typed_dict_setdefault_signature_callback
-        elif fullname in {n + ".pop" for n in TPDICT_FB_NAMES}:
+        elif fullname in TD_POP_NAMES:
             return typed_dict_pop_signature_callback
         elif fullname == "_ctypes.Array.__setitem__":
-            return ctypes.array_setitem_callback
-        elif fullname == singledispatch.SINGLEDISPATCH_CALLABLE_CALL_METHOD:
-            return singledispatch.call_singledispatch_function_callback
+            from mypy.plugins import ctypes
 
-        typed_dict_updates = set()
-        for n in TPDICT_FB_NAMES:
-            typed_dict_updates.add(n + ".update")
-            typed_dict_updates.add(n + ".__or__")
-            typed_dict_updates.add(n + ".__ror__")
-            typed_dict_updates.add(n + ".__ior__")
+            return ctypes.array_setitem_callback
+        elif fullname == constants.SINGLEDISPATCH_CALLABLE_CALL_METHOD:
+            from mypy.plugins import singledispatch
 
-        if fullname in typed_dict_updates:
+            return singledispatch.call_singledispatch_function_callback
+        elif fullname in TD_UPDATE_METHOD_NAMES:
             return typed_dict_update_signature_callback
-
         return None
 
     def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None:
-        from mypy.plugins import ctypes, singledispatch
-
         if fullname == "typing.Mapping.get":
             return typed_dict_get_callback
         elif fullname == "builtins.int.__pow__":
@@ -117,12 +127,20 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No
         elif fullname in {n + ".__delitem__" for n in TPDICT_FB_NAMES}:
             return typed_dict_delitem_callback
         elif fullname == "_ctypes.Array.__getitem__":
+            from mypy.plugins import ctypes
+
             return ctypes.array_getitem_callback
         elif fullname == "_ctypes.Array.__iter__":
+            from mypy.plugins import ctypes
+
             return ctypes.array_iter_callback
-        elif fullname == singledispatch.SINGLEDISPATCH_REGISTER_METHOD:
+        elif fullname == constants.SINGLEDISPATCH_REGISTER_METHOD:
+            from mypy.plugins import singledispatch
+
             return singledispatch.singledispatch_register_callback
-        elif fullname == singledispatch.REGISTER_CALLABLE_CALL_METHOD:
+        elif fullname == constants.SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD:
+            from mypy.plugins import singledispatch
+
             return singledispatch.call_singledispatch_function_after_register_argument
         elif fullname == "functools.partial.__call__":
             import mypy.plugins.functools
@@ -131,15 +149,21 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No
         return None
 
     def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
-        from mypy.plugins import ctypes, enums
-
         if fullname == "_ctypes.Array.value":
+            from mypy.plugins import ctypes
+
             return ctypes.array_value_callback
         elif fullname == "_ctypes.Array.raw":
+            from mypy.plugins import ctypes
+
             return ctypes.array_raw_callback
-        elif fullname in enums.ENUM_NAME_ACCESS:
+        elif fullname in constants.ENUM_NAME_ACCESS:
+            from mypy.plugins import enums
+
             return enums.enum_name_callback
-        elif fullname in enums.ENUM_VALUE_ACCESS:
+        elif fullname in constants.ENUM_VALUE_ACCESS:
+            from mypy.plugins import enums
+
             return enums.enum_value_callback
         return None
 
diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py
index 8b7c5df6f51f..dc58fc8110a5 100644
--- a/mypy/plugins/enums.py
+++ b/mypy/plugins/enums.py
@@ -14,11 +14,10 @@
 from __future__ import annotations
 
 from collections.abc import Iterable, Sequence
-from typing import Final, TypeVar, cast
+from typing import TypeVar, cast
 
 import mypy.plugin  # To avoid circular imports.
 from mypy.nodes import TypeInfo
-from mypy.semanal_enum import ENUM_BASES
 from mypy.subtypes import is_equivalent
 from mypy.typeops import fixup_partial_type, make_simplified_union
 from mypy.types import (
@@ -31,13 +30,6 @@
     is_named_instance,
 )
 
-ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | {
-    f"{prefix}._name_" for prefix in ENUM_BASES
-}
-ENUM_VALUE_ACCESS: Final = {f"{prefix}.value" for prefix in ENUM_BASES} | {
-    f"{prefix}._value_" for prefix in ENUM_BASES
-}
-
 
 def enum_name_callback(ctx: mypy.plugin.AttributeContext) -> Type:
     """This plugin refines the 'name' attribute in enums to act as if
diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py
index be4b405ce610..eb2bbe133bf0 100644
--- a/mypy/plugins/singledispatch.py
+++ b/mypy/plugins/singledispatch.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from collections.abc import Sequence
-from typing import Final, NamedTuple, TypeVar, Union
+from typing import NamedTuple, TypeVar, Union
 from typing_extensions import TypeAlias as _TypeAlias
 
 from mypy.messages import format_type
@@ -9,6 +9,7 @@
 from mypy.options import Options
 from mypy.plugin import CheckerPluginInterface, FunctionContext, MethodContext, MethodSigContext
 from mypy.plugins.common import add_method_to_class
+from mypy.plugins.constants import SINGLEDISPATCH_REGISTER_RETURN_CLASS
 from mypy.subtypes import is_subtype
 from mypy.types import (
     AnyType,
@@ -33,13 +34,6 @@ class RegisterCallableInfo(NamedTuple):
     singledispatch_obj: Instance
 
 
-SINGLEDISPATCH_TYPE: Final = "functools._SingleDispatchCallable"
-
-SINGLEDISPATCH_REGISTER_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.register"
-
-SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.__call__"
-
-
 def get_singledispatch_info(typ: Instance) -> SingledispatchTypeVars | None:
     if len(typ.args) == 2:
         return SingledispatchTypeVars(*typ.args)  # type: ignore[arg-type]
@@ -56,16 +50,11 @@ def get_first_arg(args: list[list[T]]) -> T | None:
     return None
 
 
-REGISTER_RETURN_CLASS: Final = "_SingleDispatchRegisterCallable"
-
-REGISTER_CALLABLE_CALL_METHOD: Final = f"functools.{REGISTER_RETURN_CLASS}.__call__"
-
-
 def make_fake_register_class_instance(
     api: CheckerPluginInterface, type_args: Sequence[Type]
 ) -> Instance:
-    defn = ClassDef(REGISTER_RETURN_CLASS, Block([]))
-    defn.fullname = f"functools.{REGISTER_RETURN_CLASS}"
+    defn = ClassDef(SINGLEDISPATCH_REGISTER_RETURN_CLASS, Block([]))
+    defn.fullname = f"functools.{SINGLEDISPATCH_REGISTER_RETURN_CLASS}"
     info = TypeInfo(SymbolTable(), defn, "functools")
     obj_type = api.named_generic_type("builtins.object", []).type
     info.bases = [Instance(obj_type, [])]

From 1fde1438f6c47963ecd6e10a8c410ac41ba05103 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Mon, 7 Jul 2025 11:24:42 +0200
Subject: [PATCH 1427/1617] Combine the revealed types of multiple iteration
 steps in a more robust manner. (#19324)

This PR fixes a regression introduced in #19118 and discussed in #19270.
The combination of the revealed types of individual iteration steps now
relies on collecting the original type objects instead of parts of
preliminary `revealed_type` notes. As @JukkaL suspected, this approach
is much more straightforward than introducing a sufficiently complete
`revealed_type` note parser.

Please note that I appended a commit that refactors already existing
code. It is mainly code-moving, so I hope it does not complicate the
review of this PR.
---
 mypy/checker.py                              |  19 ++--
 mypy/errors.py                               | 103 +++++++++----------
 mypy/messages.py                             |  34 +++++-
 test-data/unit/check-inference.test          |   4 +-
 test-data/unit/check-narrowing.test          |   2 +-
 test-data/unit/check-redefine2.test          |   4 +-
 test-data/unit/check-typevar-tuple.test      |   2 +-
 test-data/unit/check-union-error-syntax.test |   7 +-
 8 files changed, 95 insertions(+), 80 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 7859934c1ef7..225a50c7e646 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -618,7 +618,7 @@ def accept_loop(
                     if on_enter_body is not None:
                         on_enter_body()
 
-                    with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
+                    with IterationErrorWatcher(self.msg.errors, iter_errors):
                         self.accept(body)
 
                 partials_new = sum(len(pts.map) for pts in self.partial_types)
@@ -641,10 +641,7 @@ def accept_loop(
                 if iter == 20:
                     raise RuntimeError("Too many iterations when checking a loop")
 
-            for error_info in watcher.yield_error_infos():
-                self.msg.fail(*error_info[:2], code=error_info[2])
-            for note_info in watcher.yield_note_infos(self.options):
-                self.note(*note_info)
+            self.msg.iteration_dependent_errors(iter_errors)
 
             # If exit_condition is set, assume it must be False on exit from the loop:
             if exit_condition:
@@ -3041,7 +3038,7 @@ def is_noop_for_reachability(self, s: Statement) -> bool:
             if isinstance(s.expr, EllipsisExpr):
                 return True
             elif isinstance(s.expr, CallExpr):
-                with self.expr_checker.msg.filter_errors():
+                with self.expr_checker.msg.filter_errors(filter_revealed_type=True):
                     typ = get_proper_type(
                         self.expr_checker.accept(
                             s.expr, allow_none_return=True, always_allow_any=True
@@ -4969,7 +4966,7 @@ def visit_try_stmt(self, s: TryStmt) -> None:
             if s.finally_body:
                 # First we check finally_body is type safe on all abnormal exit paths
                 iter_errors = IterationDependentErrors()
-                with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
+                with IterationErrorWatcher(self.msg.errors, iter_errors):
                     self.accept(s.finally_body)
 
         if s.finally_body:
@@ -4986,13 +4983,9 @@ def visit_try_stmt(self, s: TryStmt) -> None:
             # that follows the try statement.)
             assert iter_errors is not None
             if not self.binder.is_unreachable():
-                with IterationErrorWatcher(self.msg.errors, iter_errors) as watcher:
+                with IterationErrorWatcher(self.msg.errors, iter_errors):
                     self.accept(s.finally_body)
-
-            for error_info in watcher.yield_error_infos():
-                self.msg.fail(*error_info[:2], code=error_info[2])
-            for note_info in watcher.yield_note_infos(self.options):
-                self.msg.note(*note_info)
+            self.msg.iteration_dependent_errors(iter_errors)
 
     def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None:
         """Type check a try statement, ignoring the finally block.
diff --git a/mypy/errors.py b/mypy/errors.py
index 5dd411c39e95..5c135146bcb7 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -15,6 +15,7 @@
 from mypy.nodes import Context
 from mypy.options import Options
 from mypy.scope import Scope
+from mypy.types import Type
 from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file
 from mypy.version import __version__ as mypy_version
 
@@ -166,6 +167,10 @@ class ErrorWatcher:
     out by one of the ErrorWatcher instances.
     """
 
+    # public attribute for the special treatment of `reveal_type` by
+    # `MessageBuilder.reveal_type`:
+    filter_revealed_type: bool
+
     def __init__(
         self,
         errors: Errors,
@@ -173,11 +178,13 @@ def __init__(
         filter_errors: bool | Callable[[str, ErrorInfo], bool] = False,
         save_filtered_errors: bool = False,
         filter_deprecated: bool = False,
+        filter_revealed_type: bool = False,
     ) -> None:
         self.errors = errors
         self._has_new_errors = False
         self._filter = filter_errors
         self._filter_deprecated = filter_deprecated
+        self.filter_revealed_type = filter_revealed_type
         self._filtered: list[ErrorInfo] | None = [] if save_filtered_errors else None
 
     def __enter__(self) -> Self:
@@ -236,15 +243,41 @@ class IterationDependentErrors:
     # the error report occurs but really all unreachable lines.
     unreachable_lines: list[set[int]]
 
-    # One set of revealed types for each `reveal_type` statement.  Each created set can
-    # grow during the iteration.  Meaning of the tuple items: function_or_member, line,
-    # column, end_line, end_column:
-    revealed_types: dict[tuple[str | None, int, int, int, int], set[str]]
+    # One list of revealed types for each `reveal_type` statement.  Each created list
+    # can grow during the iteration.  Meaning of the tuple items: line, column,
+    # end_line, end_column:
+    revealed_types: dict[tuple[int, int, int | None, int | None], list[Type]]
 
     def __init__(self) -> None:
         self.uselessness_errors = []
         self.unreachable_lines = []
-        self.revealed_types = defaultdict(set)
+        self.revealed_types = defaultdict(list)
+
+    def yield_uselessness_error_infos(self) -> Iterator[tuple[str, Context, ErrorCode]]:
+        """Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
+        errors that could not be ruled out in any iteration step."""
+
+        persistent_uselessness_errors = set()
+        for candidate in set(chain(*self.uselessness_errors)):
+            if all(
+                (candidate in errors) or (candidate[2] in lines)
+                for errors, lines in zip(self.uselessness_errors, self.unreachable_lines)
+            ):
+                persistent_uselessness_errors.add(candidate)
+        for error_info in persistent_uselessness_errors:
+            context = Context(line=error_info[2], column=error_info[3])
+            context.end_line = error_info[4]
+            context.end_column = error_info[5]
+            yield error_info[1], context, error_info[0]
+
+    def yield_revealed_type_infos(self) -> Iterator[tuple[list[Type], Context]]:
+        """Yield all types revealed in at least one iteration step."""
+
+        for note_info, types in self.revealed_types.items():
+            context = Context(line=note_info[0], column=note_info[1])
+            context.end_line = note_info[2]
+            context.end_column = note_info[3]
+            yield types, context
 
 
 class IterationErrorWatcher(ErrorWatcher):
@@ -287,53 +320,8 @@ def on_error(self, file: str, info: ErrorInfo) -> bool:
                 iter_errors.unreachable_lines[-1].update(range(info.line, info.end_line + 1))
             return True
 
-        if info.code == codes.MISC and info.message.startswith("Revealed type is "):
-            key = info.function_or_member, info.line, info.column, info.end_line, info.end_column
-            types = info.message.split('"')[1]
-            if types.startswith("Union["):
-                iter_errors.revealed_types[key].update(types[6:-1].split(", "))
-            else:
-                iter_errors.revealed_types[key].add(types)
-            return True
-
         return super().on_error(file, info)
 
-    def yield_error_infos(self) -> Iterator[tuple[str, Context, ErrorCode]]:
-        """Report only those `unreachable`, `redundant-expr`, and `redundant-casts`
-        errors that could not be ruled out in any iteration step."""
-
-        persistent_uselessness_errors = set()
-        iter_errors = self.iteration_dependent_errors
-        for candidate in set(chain(*iter_errors.uselessness_errors)):
-            if all(
-                (candidate in errors) or (candidate[2] in lines)
-                for errors, lines in zip(
-                    iter_errors.uselessness_errors, iter_errors.unreachable_lines
-                )
-            ):
-                persistent_uselessness_errors.add(candidate)
-        for error_info in persistent_uselessness_errors:
-            context = Context(line=error_info[2], column=error_info[3])
-            context.end_line = error_info[4]
-            context.end_column = error_info[5]
-            yield error_info[1], context, error_info[0]
-
-    def yield_note_infos(self, options: Options) -> Iterator[tuple[str, Context]]:
-        """Yield all types revealed in at least one iteration step."""
-
-        for note_info, types in self.iteration_dependent_errors.revealed_types.items():
-            sorted_ = sorted(types, key=lambda typ: typ.lower())
-            if len(types) == 1:
-                revealed = sorted_[0]
-            elif options.use_or_syntax():
-                revealed = " | ".join(sorted_)
-            else:
-                revealed = f"Union[{', '.join(sorted_)}]"
-            context = Context(line=note_info[1], column=note_info[2])
-            context.end_line = note_info[3]
-            context.end_column = note_info[4]
-            yield f'Revealed type is "{revealed}"', context
-
 
 class Errors:
     """Container for compile errors.
@@ -596,18 +584,19 @@ def _add_error_info(self, file: str, info: ErrorInfo) -> None:
         if info.code in (IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND):
             self.seen_import_error = True
 
+    def get_watchers(self) -> Iterator[ErrorWatcher]:
+        """Yield the `ErrorWatcher` stack from top to bottom."""
+        i = len(self._watchers)
+        while i > 0:
+            i -= 1
+            yield self._watchers[i]
+
     def _filter_error(self, file: str, info: ErrorInfo) -> bool:
         """
         process ErrorWatcher stack from top to bottom,
         stopping early if error needs to be filtered out
         """
-        i = len(self._watchers)
-        while i > 0:
-            i -= 1
-            w = self._watchers[i]
-            if w.on_error(file, info):
-                return True
-        return False
+        return any(w.on_error(file, info) for w in self.get_watchers())
 
     def add_error_info(self, info: ErrorInfo) -> None:
         file, lines = info.origin
diff --git a/mypy/messages.py b/mypy/messages.py
index 13a4facc82b0..44ed25a19517 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -23,7 +23,13 @@
 from mypy import errorcodes as codes, message_registry
 from mypy.erasetype import erase_type
 from mypy.errorcodes import ErrorCode
-from mypy.errors import ErrorInfo, Errors, ErrorWatcher
+from mypy.errors import (
+    ErrorInfo,
+    Errors,
+    ErrorWatcher,
+    IterationDependentErrors,
+    IterationErrorWatcher,
+)
 from mypy.nodes import (
     ARG_NAMED,
     ARG_NAMED_OPT,
@@ -188,12 +194,14 @@ def filter_errors(
         filter_errors: bool | Callable[[str, ErrorInfo], bool] = True,
         save_filtered_errors: bool = False,
         filter_deprecated: bool = False,
+        filter_revealed_type: bool = False,
     ) -> ErrorWatcher:
         return ErrorWatcher(
             self.errors,
             filter_errors=filter_errors,
             save_filtered_errors=save_filtered_errors,
             filter_deprecated=filter_deprecated,
+            filter_revealed_type=filter_revealed_type,
         )
 
     def add_errors(self, errors: list[ErrorInfo]) -> None:
@@ -1738,6 +1746,24 @@ def invalid_signature_for_special_method(
         )
 
     def reveal_type(self, typ: Type, context: Context) -> None:
+
+        # Search for an error watcher that modifies the "normal" behaviour (we do not
+        # rely on the normal `ErrorWatcher` filtering approach because we might need to
+        # collect the original types for a later unionised response):
+        for watcher in self.errors.get_watchers():
+            # The `reveal_type` statement should be ignored:
+            if watcher.filter_revealed_type:
+                return
+            # The `reveal_type` statement might be visited iteratively due to being
+            # placed in a loop or so. Hence, we collect the respective types of
+            # individual iterations so that we can report them all in one step later:
+            if isinstance(watcher, IterationErrorWatcher):
+                watcher.iteration_dependent_errors.revealed_types[
+                    (context.line, context.column, context.end_line, context.end_column)
+                ].append(typ)
+                return
+
+        # Nothing special here; just create the note:
         visitor = TypeStrVisitor(options=self.options)
         self.note(f'Revealed type is "{typ.accept(visitor)}"', context)
 
@@ -2481,6 +2507,12 @@ def match_statement_inexhaustive_match(self, typ: Type, context: Context) -> Non
             code=codes.EXHAUSTIVE_MATCH,
         )
 
+    def iteration_dependent_errors(self, iter_errors: IterationDependentErrors) -> None:
+        for error_info in iter_errors.yield_uselessness_error_infos():
+            self.fail(*error_info[:2], code=error_info[2])
+        for types, context in iter_errors.yield_revealed_type_infos():
+            self.reveal_type(mypy.typeops.make_simplified_union(types), context)
+
 
 def quote_type_string(type_string: str) -> str:
     """Quotes a type representation for use in messages."""
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 90cb7d3799cf..6564fb3192d0 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -343,7 +343,7 @@ for var2 in [g, h, i, j, k, l]:
     reveal_type(var2)  # N: Revealed type is "Union[builtins.int, builtins.str]"
 
 for var3 in [m, n, o, p, q, r]:
-    reveal_type(var3)  # N: Revealed type is "Union[Any, builtins.int]"
+    reveal_type(var3)  # N: Revealed type is "Union[builtins.int, Any]"
 
 T = TypeVar("T", bound=Type[Foo])
 
@@ -1247,7 +1247,7 @@ class X(TypedDict):
 
 x: X
 for a in ("hourly", "daily"):
-    reveal_type(a)  # N: Revealed type is "Union[Literal['daily']?, Literal['hourly']?]"
+    reveal_type(a)  # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]"
     reveal_type(x[a])  # N: Revealed type is "builtins.int"
     reveal_type(a.upper())  # N: Revealed type is "builtins.str"
     c = a
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 7a053e1c5cab..e322bd7a37b8 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2346,7 +2346,7 @@ def f() -> bool: ...
 
 y = None
 while f():
-    reveal_type(y)  # N: Revealed type is "Union[builtins.int, None]"
+    reveal_type(y)  # N: Revealed type is "Union[None, builtins.int]"
     y = 1
 reveal_type(y)  # N: Revealed type is "Union[builtins.int, None]"
 
diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test
index 924e66584669..3523772611aa 100644
--- a/test-data/unit/check-redefine2.test
+++ b/test-data/unit/check-redefine2.test
@@ -628,7 +628,7 @@ def f1() -> None:
 def f2() -> None:
     x = None
     while int():
-        reveal_type(x) # N: Revealed type is "Union[builtins.str, None]"
+        reveal_type(x) # N: Revealed type is "Union[None, builtins.str]"
         if int():
             x = ""
     reveal_type(x) # N: Revealed type is "Union[None, builtins.str]"
@@ -923,7 +923,7 @@ class X(TypedDict):
 
 x: X
 for a in ("hourly", "daily"):
-    reveal_type(a)  # N: Revealed type is "Union[Literal['daily']?, Literal['hourly']?]"
+    reveal_type(a)  # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]"
     reveal_type(x[a])  # N: Revealed type is "builtins.int"
     reveal_type(a.upper())  # N: Revealed type is "builtins.str"
     c = a
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index f44758f7b51b..db0e26ba2b36 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -989,7 +989,7 @@ from typing_extensions import Unpack
 
 def pipeline(*xs: Unpack[Tuple[int, Unpack[Tuple[float, ...]], bool]]) -> None:
     for x in xs:
-        reveal_type(x)  # N: Revealed type is "Union[builtins.float, builtins.int]"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.float]"
 [builtins fixtures/tuple.pyi]
 
 [case testFixedUnpackItemInInstanceArguments]
diff --git a/test-data/unit/check-union-error-syntax.test b/test-data/unit/check-union-error-syntax.test
index d41281b774e1..e938598aaefe 100644
--- a/test-data/unit/check-union-error-syntax.test
+++ b/test-data/unit/check-union-error-syntax.test
@@ -62,17 +62,18 @@ x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", v
 try:
     x = 1
     x = ""
+    x = {1: ""}
 finally:
-    reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+    reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.dict[builtins.int, builtins.str]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testOrSyntaxRecombined]
 # flags: --python-version 3.10 --no-force-union-syntax --allow-redefinition-new --local-partial-types
 # The following revealed type is recombined because the finally body is visited twice.
-# ToDo: Improve this recombination logic, especially (but not only) for the "or syntax".
 try:
     x = 1
     x = ""
+    x = {1: ""}
 finally:
-    reveal_type(x)  # N: Revealed type is "builtins.int | builtins.str | builtins.str"
+    reveal_type(x)  # N: Revealed type is "builtins.int | builtins.str | builtins.dict[builtins.int, builtins.str]"
 [builtins fixtures/isinstancelist.pyi]

From b4d52e1fdb8709f5cfa8bc9e61ea3be386564df7 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 7 Jul 2025 13:00:22 +0100
Subject: [PATCH 1428/1617] Remove all nested imports from default plugin
 (#19388)

It looks like these nested imports are not needed anymore.
---
 mypy/plugins/default.py | 173 ++++++++++++++++++++--------------------
 1 file changed, 86 insertions(+), 87 deletions(-)

diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py
index 09f9795b593e..3d27ca99302f 100644
--- a/mypy/plugins/default.py
+++ b/mypy/plugins/default.py
@@ -15,8 +15,51 @@
     MethodSigContext,
     Plugin,
 )
-from mypy.plugins import constants
+from mypy.plugins.attrs import (
+    attr_class_maker_callback,
+    attr_class_makers,
+    attr_dataclass_makers,
+    attr_define_makers,
+    attr_frozen_makers,
+    attr_tag_callback,
+    evolve_function_sig_callback,
+    fields_function_sig_callback,
+)
 from mypy.plugins.common import try_getting_str_literals
+from mypy.plugins.constants import (
+    ENUM_NAME_ACCESS,
+    ENUM_VALUE_ACCESS,
+    SINGLEDISPATCH_CALLABLE_CALL_METHOD,
+    SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD,
+    SINGLEDISPATCH_REGISTER_METHOD,
+)
+from mypy.plugins.ctypes import (
+    array_constructor_callback,
+    array_getitem_callback,
+    array_iter_callback,
+    array_raw_callback,
+    array_setitem_callback,
+    array_value_callback,
+)
+from mypy.plugins.dataclasses import (
+    dataclass_class_maker_callback,
+    dataclass_makers,
+    dataclass_tag_callback,
+    replace_function_sig_callback,
+)
+from mypy.plugins.enums import enum_member_callback, enum_name_callback, enum_value_callback
+from mypy.plugins.functools import (
+    functools_total_ordering_maker_callback,
+    functools_total_ordering_makers,
+    partial_call_callback,
+    partial_new_callback,
+)
+from mypy.plugins.singledispatch import (
+    call_singledispatch_function_after_register_argument,
+    call_singledispatch_function_callback,
+    create_singledispatch_function_callback,
+    singledispatch_register_callback,
+)
 from mypy.subtypes import is_subtype
 from mypy.typeops import is_literal_type_like, make_simplified_union
 from mypy.types import (
@@ -53,39 +96,24 @@ class DefaultPlugin(Plugin):
 
     def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None:
         if fullname == "_ctypes.Array":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_constructor_callback
+            return array_constructor_callback
         elif fullname == "functools.singledispatch":
-            from mypy.plugins import singledispatch
-
-            return singledispatch.create_singledispatch_function_callback
+            return create_singledispatch_function_callback
         elif fullname == "functools.partial":
-            import mypy.plugins.functools
-
-            return mypy.plugins.functools.partial_new_callback
+            return partial_new_callback
         elif fullname == "enum.member":
-            from mypy.plugins import enums
-
-            return enums.enum_member_callback
-
+            return enum_member_callback
         return None
 
     def get_function_signature_hook(
         self, fullname: str
     ) -> Callable[[FunctionSigContext], FunctionLike] | None:
         if fullname in ("attr.evolve", "attrs.evolve", "attr.assoc", "attrs.assoc"):
-            from mypy.plugins import attrs
-
-            return attrs.evolve_function_sig_callback
+            return evolve_function_sig_callback
         elif fullname in ("attr.fields", "attrs.fields"):
-            from mypy.plugins import attrs
-
-            return attrs.fields_function_sig_callback
+            return fields_function_sig_callback
         elif fullname == "dataclasses.replace":
-            from mypy.plugins import dataclasses
-
-            return dataclasses.replace_function_sig_callback
+            return replace_function_sig_callback
         return None
 
     def get_method_signature_hook(
@@ -98,13 +126,9 @@ def get_method_signature_hook(
         elif fullname in TD_POP_NAMES:
             return typed_dict_pop_signature_callback
         elif fullname == "_ctypes.Array.__setitem__":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_setitem_callback
-        elif fullname == constants.SINGLEDISPATCH_CALLABLE_CALL_METHOD:
-            from mypy.plugins import singledispatch
-
-            return singledispatch.call_singledispatch_function_callback
+            return array_setitem_callback
+        elif fullname == SINGLEDISPATCH_CALLABLE_CALL_METHOD:
+            return call_singledispatch_function_callback
         elif fullname in TD_UPDATE_METHOD_NAMES:
             return typed_dict_update_signature_callback
         return None
@@ -127,88 +151,63 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No
         elif fullname in {n + ".__delitem__" for n in TPDICT_FB_NAMES}:
             return typed_dict_delitem_callback
         elif fullname == "_ctypes.Array.__getitem__":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_getitem_callback
+            return array_getitem_callback
         elif fullname == "_ctypes.Array.__iter__":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_iter_callback
-        elif fullname == constants.SINGLEDISPATCH_REGISTER_METHOD:
-            from mypy.plugins import singledispatch
-
-            return singledispatch.singledispatch_register_callback
-        elif fullname == constants.SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD:
-            from mypy.plugins import singledispatch
-
-            return singledispatch.call_singledispatch_function_after_register_argument
+            return array_iter_callback
+        elif fullname == SINGLEDISPATCH_REGISTER_METHOD:
+            return singledispatch_register_callback
+        elif fullname == SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD:
+            return call_singledispatch_function_after_register_argument
         elif fullname == "functools.partial.__call__":
-            import mypy.plugins.functools
-
-            return mypy.plugins.functools.partial_call_callback
+            return partial_call_callback
         return None
 
     def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
         if fullname == "_ctypes.Array.value":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_value_callback
+            return array_value_callback
         elif fullname == "_ctypes.Array.raw":
-            from mypy.plugins import ctypes
-
-            return ctypes.array_raw_callback
-        elif fullname in constants.ENUM_NAME_ACCESS:
-            from mypy.plugins import enums
-
-            return enums.enum_name_callback
-        elif fullname in constants.ENUM_VALUE_ACCESS:
-            from mypy.plugins import enums
-
-            return enums.enum_value_callback
+            return array_raw_callback
+        elif fullname in ENUM_NAME_ACCESS:
+            return enum_name_callback
+        elif fullname in ENUM_VALUE_ACCESS:
+            return enum_value_callback
         return None
 
     def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
-        from mypy.plugins import attrs, dataclasses
-
         # These dataclass and attrs hooks run in the main semantic analysis pass
         # and only tag known dataclasses/attrs classes, so that the second
         # hooks (in get_class_decorator_hook_2) can detect dataclasses/attrs classes
         # in the MRO.
-        if fullname in dataclasses.dataclass_makers:
-            return dataclasses.dataclass_tag_callback
+        if fullname in dataclass_makers:
+            return dataclass_tag_callback
         if (
-            fullname in attrs.attr_class_makers
-            or fullname in attrs.attr_dataclass_makers
-            or fullname in attrs.attr_frozen_makers
-            or fullname in attrs.attr_define_makers
+            fullname in attr_class_makers
+            or fullname in attr_dataclass_makers
+            or fullname in attr_frozen_makers
+            or fullname in attr_define_makers
         ):
-            return attrs.attr_tag_callback
-
+            return attr_tag_callback
         return None
 
     def get_class_decorator_hook_2(
         self, fullname: str
     ) -> Callable[[ClassDefContext], bool] | None:
-        import mypy.plugins.functools
-        from mypy.plugins import attrs, dataclasses
-
-        if fullname in dataclasses.dataclass_makers:
-            return dataclasses.dataclass_class_maker_callback
-        elif fullname in mypy.plugins.functools.functools_total_ordering_makers:
-            return mypy.plugins.functools.functools_total_ordering_maker_callback
-        elif fullname in attrs.attr_class_makers:
-            return attrs.attr_class_maker_callback
-        elif fullname in attrs.attr_dataclass_makers:
-            return partial(attrs.attr_class_maker_callback, auto_attribs_default=True)
-        elif fullname in attrs.attr_frozen_makers:
+        if fullname in dataclass_makers:
+            return dataclass_class_maker_callback
+        elif fullname in functools_total_ordering_makers:
+            return functools_total_ordering_maker_callback
+        elif fullname in attr_class_makers:
+            return attr_class_maker_callback
+        elif fullname in attr_dataclass_makers:
+            return partial(attr_class_maker_callback, auto_attribs_default=True)
+        elif fullname in attr_frozen_makers:
             return partial(
-                attrs.attr_class_maker_callback, auto_attribs_default=None, frozen_default=True
+                attr_class_maker_callback, auto_attribs_default=None, frozen_default=True
             )
-        elif fullname in attrs.attr_define_makers:
+        elif fullname in attr_define_makers:
             return partial(
-                attrs.attr_class_maker_callback, auto_attribs_default=None, slots_default=True
+                attr_class_maker_callback, auto_attribs_default=None, slots_default=True
             )
-
         return None
 
 

From 0b7afdaf281f23fd2982b79d736fa2ab054bc76a Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 7 Jul 2025 14:29:38 +0200
Subject: [PATCH 1429/1617] Check property decorators stricter (#19313)

Fixes #19312. Fixes #18327.

Only accept `@current_prop_name.{setter,deleter}` as property-related
decorators.
---
 mypy/semanal.py                     | 31 ++++++++---
 test-data/unit/check-classes.test   | 80 +++++++++++++++++++++++++++--
 test-data/unit/check-functions.test |  4 +-
 test-data/unit/semanal-errors.test  |  2 +-
 4 files changed, 103 insertions(+), 14 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 431c5ec04d3c..435c1e682e35 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1527,33 +1527,33 @@ def analyze_property_with_multi_part_definition(
         assert isinstance(first_item, Decorator)
         deleted_items = []
         bare_setter_type = None
+        func_name = first_item.func.name
         for i, item in enumerate(items[1:]):
             if isinstance(item, Decorator):
                 item.func.accept(self)
                 if item.decorators:
                     first_node = item.decorators[0]
-                    if isinstance(first_node, MemberExpr):
+                    if self._is_valid_property_decorator(first_node, func_name):
+                        # Get abstractness from the original definition.
+                        item.func.abstract_status = first_item.func.abstract_status
                         if first_node.name == "setter":
                             # The first item represents the entire property.
                             first_item.var.is_settable_property = True
-                            # Get abstractness from the original definition.
-                            item.func.abstract_status = first_item.func.abstract_status
                             setter_func_type = function_type(
                                 item.func, self.named_type("builtins.function")
                             )
                             assert isinstance(setter_func_type, CallableType)
                             bare_setter_type = setter_func_type
                             defn.setter_index = i + 1
-                        if first_node.name == "deleter":
-                            item.func.abstract_status = first_item.func.abstract_status
                         for other_node in item.decorators[1:]:
                             other_node.accept(self)
                     else:
                         self.fail(
-                            f"Only supported top decorator is @{first_item.func.name}.setter", item
+                            f'Only supported top decorators are "@{func_name}.setter" and "@{func_name}.deleter"',
+                            first_node,
                         )
             else:
-                self.fail(f'Unexpected definition for property "{first_item.func.name}"', item)
+                self.fail(f'Unexpected definition for property "{func_name}"', item)
                 deleted_items.append(i + 1)
         for i in reversed(deleted_items):
             del items[i]
@@ -1567,6 +1567,23 @@ def analyze_property_with_multi_part_definition(
                         )
         return bare_setter_type
 
+    def _is_valid_property_decorator(
+        self, deco: Expression, property_name: str
+    ) -> TypeGuard[MemberExpr]:
+        if not isinstance(deco, MemberExpr):
+            return False
+        if not isinstance(deco.expr, NameExpr) or deco.expr.name != property_name:
+            return False
+        if deco.name not in {"setter", "deleter"}:
+            # This intentionally excludes getter. While `@prop.getter` is valid at
+            # runtime, that would mean replacing the already processed getter type.
+            # Such usage is almost definitely a mistake (except for overrides in
+            # subclasses but we don't support them anyway) and might be a typo
+            # (only one letter away from `setter`), it's likely almost never used,
+            # so supporting it properly won't pay off.
+            return False
+        return True
+
     def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> None:
         if self.is_class_scope():
             assert self.type is not None
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 173657620304..ae91815d1e9e 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -772,7 +772,7 @@ class A:
 class B(A):
     @property
     def f(self) -> Callable[[object], None]: pass
-    @func.setter
+    @f.setter
     def f(self, x: object) -> None: pass
 [builtins fixtures/property.pyi]
 
@@ -786,7 +786,7 @@ class A:
 class B(A):
     @property
     def f(self) -> Callable[[object], None]: pass
-    @func.setter
+    @f.setter
     def f(self, x: object) -> None: pass
 [builtins fixtures/property.pyi]
 
@@ -1622,7 +1622,81 @@ class A:
         self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
         return ''
 [builtins fixtures/property.pyi]
+
+[case testPropertyNameIsChecked]
+class A:
+    @property
+    def f(self) -> str: ...
+    @not_f.setter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+
+a = A()
+reveal_type(a.f)  # N: Revealed type is "builtins.str"
+a.f = ''  # E: Property "f" defined in "A" is read-only
+
+class B:
+    @property
+    def f(self) -> str: ...
+    @not_f.deleter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self) -> None: ...
+
+class C:
+    @property
+    def f(self) -> str: ...
+    @not_f.setter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+    @not_f.deleter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self) -> None: ...
+[builtins fixtures/property.pyi]
+
+[case testPropertyAttributeIsChecked]
+class A:
+    @property
+    def f(self) -> str: ...
+    @f.unknown  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+    @f.bad.setter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+    @f  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+    @int  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+[builtins fixtures/property.pyi]
+
+[case testPropertyNameAndAttributeIsCheckedPretty]
+# flags: --pretty
+class A:
+    @property
+    def f(self) -> str: ...
+    @not_f.setter
+    def f(self, val: str) -> None: ...
+    @not_f.deleter
+    def f(self) -> None: ...
+
+class B:
+    @property
+    def f(self) -> str: ...
+    @f.unknown
+    def f(self, val: str) -> None: ...
+[builtins fixtures/property.pyi]
 [out]
+main:5: error: Only supported top decorators are "@f.setter" and "@f.deleter"
+        @not_f.setter
+         ^~~~~~~~~~~~
+main:7: error: Only supported top decorators are "@f.setter" and "@f.deleter"
+        @not_f.deleter
+         ^~~~~~~~~~~~~
+main:13: error: Only supported top decorators are "@f.setter" and "@f.deleter"
+        @f.unknown
+         ^~~~~~~~~
+
+[case testPropertyGetterDecoratorIsRejected]
+class A:
+    @property
+    def f(self) -> str: ...
+    @f.getter  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
+    def f(self, val: str) -> None: ...
+[builtins fixtures/property.pyi]
 
 [case testDynamicallyTypedProperty]
 import typing
@@ -7739,7 +7813,7 @@ class A:
     def y(self) -> int: ...
     @y.setter
     def y(self, value: int) -> None: ...
-    @dec  # E: Only supported top decorator is @y.setter
+    @dec  # E: Only supported top decorators are "@y.setter" and "@y.deleter"
     def y(self) -> None: ...
 
 reveal_type(A().y)  # N: Revealed type is "builtins.int"
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
index 07cfd09b2529..7fa34a398ea0 100644
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -2752,7 +2752,7 @@ class B:
     @property
     @dec
     def f(self) -> int: pass
-    @dec # E: Only supported top decorator is @f.setter
+    @dec # E: Only supported top decorators are "@f.setter" and "@f.deleter"
     @f.setter
     def f(self, v: int) -> None: pass
 
@@ -2764,7 +2764,6 @@ class C:
     @dec
     def f(self, v: int) -> None: pass
 [builtins fixtures/property.pyi]
-[out]
 
 [case testInvalidArgCountForProperty]
 from typing import Callable, TypeVar
@@ -2783,7 +2782,6 @@ class A:
     @property
     def h(self, *args, **kwargs) -> int: pass   # OK
 [builtins fixtures/property.pyi]
-[out]
 
 [case testSubtypingUnionGenericBounds]
 from typing import Callable, TypeVar, Union, Sequence
diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test
index 1e760799828a..2d381644629b 100644
--- a/test-data/unit/semanal-errors.test
+++ b/test-data/unit/semanal-errors.test
@@ -1236,7 +1236,7 @@ class A:
     @overload  # E: Decorators on top of @property are not supported
     @property
     def f(self) -> int: pass
-    @property  # E: Only supported top decorator is @f.setter
+    @property  # E: Only supported top decorators are "@f.setter" and "@f.deleter"
     @overload
     def f(self) -> int: pass
 [builtins fixtures/property.pyi]

From fa5d94284600a8e6e48dcc37f1aa050e7a593617 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 7 Jul 2025 14:16:08 +0100
Subject: [PATCH 1430/1617] [mypyc] Fix error value check for GetAttr that
 allows nullable values (#19378)

Also rename `allow_null` to `allow_error_value` to make it clear that
this works with non-pointer types such as tuples.
---
 mypyc/codegen/emitfunc.py   | 33 ++++++++++++++++++++-------------
 mypyc/ir/ops.py             | 12 +++++++++---
 mypyc/irbuild/builder.py    |  8 ++------
 mypyc/test/test_emitfunc.py | 12 ++++++++++++
 4 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 00c7fd56b899..e81b119b24d6 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -209,6 +209,16 @@ def visit_goto(self, op: Goto) -> None:
         if op.label is not self.next_block:
             self.emit_line("goto %s;" % self.label(op.label))
 
+    def error_value_check(self, value: Value, compare: str) -> str:
+        typ = value.type
+        if isinstance(typ, RTuple):
+            # TODO: What about empty tuple?
+            return self.emitter.tuple_undefined_check_cond(
+                typ, self.reg(value), self.c_error_value, compare
+            )
+        else:
+            return f"{self.reg(value)} {compare} {self.c_error_value(typ)}"
+
     def visit_branch(self, op: Branch) -> None:
         true, false = op.true, op.false
         negated = op.negated
@@ -225,15 +235,8 @@ def visit_branch(self, op: Branch) -> None:
             expr_result = self.reg(op.value)
             cond = f"{neg}{expr_result}"
         elif op.op == Branch.IS_ERROR:
-            typ = op.value.type
             compare = "!=" if negated else "=="
-            if isinstance(typ, RTuple):
-                # TODO: What about empty tuple?
-                cond = self.emitter.tuple_undefined_check_cond(
-                    typ, self.reg(op.value), self.c_error_value, compare
-                )
-            else:
-                cond = f"{self.reg(op.value)} {compare} {self.c_error_value(typ)}"
+            cond = self.error_value_check(op.value, compare)
         else:
             assert False, "Invalid branch"
 
@@ -358,8 +361,8 @@ def get_attr_expr(self, obj: str, op: GetAttr | SetAttr, decl_cl: ClassIR) -> st
             return f"({cast}{obj})->{self.emitter.attr(op.attr)}"
 
     def visit_get_attr(self, op: GetAttr) -> None:
-        if op.allow_null:
-            self.get_attr_with_allow_null(op)
+        if op.allow_error_value:
+            self.get_attr_with_allow_error_value(op)
             return
         dest = self.reg(op)
         obj = self.reg(op.obj)
@@ -429,8 +432,11 @@ def visit_get_attr(self, op: GetAttr) -> None:
             elif not always_defined:
                 self.emitter.emit_line("}")
 
-    def get_attr_with_allow_null(self, op: GetAttr) -> None:
-        """Handle GetAttr with allow_null=True which allows NULL without raising AttributeError."""
+    def get_attr_with_allow_error_value(self, op: GetAttr) -> None:
+        """Handle GetAttr with allow_error_value=True.
+
+        This allows NULL or other error value without raising AttributeError.
+        """
         dest = self.reg(op)
         obj = self.reg(op.obj)
         rtype = op.class_type
@@ -443,7 +449,8 @@ def get_attr_with_allow_null(self, op: GetAttr) -> None:
 
         # Only emit inc_ref if not NULL
         if attr_rtype.is_refcounted and not op.is_borrowed:
-            self.emitter.emit_line(f"if ({dest} != NULL) {{")
+            check = self.error_value_check(op, "!=")
+            self.emitter.emit_line(f"if ({check}) {{")
             self.emitter.emit_inc_ref(dest, attr_rtype)
             self.emitter.emit_line("}")
 
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 1cb3df916ac9..668e3097ce30 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -811,17 +811,23 @@ class GetAttr(RegisterOp):
     error_kind = ERR_MAGIC
 
     def __init__(
-        self, obj: Value, attr: str, line: int, *, borrow: bool = False, allow_null: bool = False
+        self,
+        obj: Value,
+        attr: str,
+        line: int,
+        *,
+        borrow: bool = False,
+        allow_error_value: bool = False,
     ) -> None:
         super().__init__(line)
         self.obj = obj
         self.attr = attr
-        self.allow_null = allow_null
+        self.allow_error_value = allow_error_value
         assert isinstance(obj.type, RInstance), "Attribute access not supported: %s" % obj.type
         self.class_type = obj.type
         attr_type = obj.type.attr_type(attr)
         self.type = attr_type
-        if allow_null:
+        if allow_error_value:
             self.error_kind = ERR_NEVER
         elif attr_type.error_overlap:
             self.error_kind = ERR_MAGIC_OVERLAPPING
diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 878c5e76df3d..323450f7c340 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -709,13 +709,9 @@ def read(
         assert False, "Unsupported lvalue: %r" % target
 
     def read_nullable_attr(self, obj: Value, attr: str, line: int = -1) -> Value:
-        """Read an attribute that might be NULL without raising AttributeError.
-
-        This is used for reading spill targets in try/finally blocks where NULL
-        indicates the non-return path was taken.
-        """
+        """Read an attribute that might have an error value without raising AttributeError."""
         assert isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class
-        return self.add(GetAttr(obj, attr, line, allow_null=True))
+        return self.add(GetAttr(obj, attr, line, allow_error_value=True))
 
     def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: int) -> None:
         if isinstance(target, Register):
diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py
index 275e8c383a4b..0a696e9e3380 100644
--- a/mypyc/test/test_emitfunc.py
+++ b/mypyc/test/test_emitfunc.py
@@ -111,6 +111,7 @@ def add_local(name: str, rtype: RType) -> Register:
             "y": int_rprimitive,
             "i1": int64_rprimitive,
             "i2": int32_rprimitive,
+            "t": RTuple([object_rprimitive, object_rprimitive]),
         }
         ir.bitmap_attrs = ["i1", "i2"]
         compute_vtable(ir)
@@ -418,6 +419,17 @@ def test_get_attr_with_bitmap(self) -> None:
             """,
         )
 
+    def test_get_attr_nullable_with_tuple(self) -> None:
+        self.assert_emit(
+            GetAttr(self.r, "t", 1, allow_error_value=True),
+            """cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_t;
+               if (cpy_r_r0.f0 != NULL) {
+                   CPy_INCREF(cpy_r_r0.f0);
+                   CPy_INCREF(cpy_r_r0.f1);
+               }
+            """,
+        )
+
     def test_set_attr(self) -> None:
         self.assert_emit(
             SetAttr(self.r, "y", self.m, 1),

From b8dd6f3556f361ae2cd312b9edcc604eb6b1f465 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 7 Jul 2025 14:16:42 +0100
Subject: [PATCH 1431/1617] [mypyc] Add support for C string literals in the IR
 (#19383)

Previously only Python str and bytes literals were supported, but
sometimes we want zero-terminated C string literals instead. They
don't need to be allocated from the heap and are usually stored in a
read-only data section, so they are more efficient in some use cases.

These will be useful for a feature I'm working on.
---
 mypyc/codegen/emitfunc.py   | 30 ++++++++++++++++++++++++++++++
 mypyc/ir/ops.py             | 15 +++++++++++++++
 mypyc/ir/pprint.py          |  3 +++
 mypyc/ir/rtypes.py          | 10 ++++++----
 mypyc/test/test_emitfunc.py | 26 ++++++++++++++++++++++++++
 5 files changed, 80 insertions(+), 4 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index e81b119b24d6..625ec0643df0 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -33,6 +33,7 @@
     Cast,
     ComparisonOp,
     ControlOp,
+    CString,
     DecRef,
     Extend,
     Float,
@@ -850,6 +851,8 @@ def reg(self, reg: Value) -> str:
             elif r == "nan":
                 return "NAN"
             return r
+        elif isinstance(reg, CString):
+            return '"' + encode_c_string_literal(reg.value) + '"'
         else:
             return self.emitter.reg(reg)
 
@@ -911,3 +914,30 @@ def emit_unsigned_int_cast(self, type: RType) -> str:
             return "(uint64_t)"
         else:
             return ""
+
+
+_translation_table: Final[dict[int, str]] = {}
+
+
+def encode_c_string_literal(b: bytes) -> str:
+    """Convert bytestring to the C string literal syntax (with necessary escaping).
+
+    For example, b'foo\n' gets converted to 'foo\\n' (note that double quotes are not added).
+    """
+    if not _translation_table:
+        # Initialize the translation table on the first call.
+        d = {
+            ord("\n"): "\\n",
+            ord("\r"): "\\r",
+            ord("\t"): "\\t",
+            ord('"'): '\\"',
+            ord("\\"): "\\\\",
+        }
+        for i in range(256):
+            if i not in d:
+                if i < 32 or i >= 127:
+                    d[i] = "\\x%.2x" % i
+                else:
+                    d[i] = chr(i)
+        _translation_table.update(str.maketrans(d))
+    return b.decode("latin1").translate(_translation_table)
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 668e3097ce30..e15d494c2c57 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -39,6 +39,7 @@ class to enable the new behavior. Sometimes adding a new abstract
     RVoid,
     bit_rprimitive,
     bool_rprimitive,
+    cstring_rprimitive,
     float_rprimitive,
     int_rprimitive,
     is_bit_rprimitive,
@@ -230,6 +231,20 @@ def __init__(self, value: float, line: int = -1) -> None:
         self.line = line
 
 
+@final
+class CString(Value):
+    """C string literal (zero-terminated).
+
+    You can also include zero values in the value, but then you'll need to track
+    the length of the string separately.
+    """
+
+    def __init__(self, value: bytes, line: int = -1) -> None:
+        self.value = value
+        self.type = cstring_rprimitive
+        self.line = line
+
+
 class Op(Value):
     """Abstract base class for all IR operations.
 
diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py
index 6c96a21e473b..5bb11cc231cc 100644
--- a/mypyc/ir/pprint.py
+++ b/mypyc/ir/pprint.py
@@ -21,6 +21,7 @@
     Cast,
     ComparisonOp,
     ControlOp,
+    CString,
     DecRef,
     Extend,
     Float,
@@ -327,6 +328,8 @@ def format(self, fmt: str, *args: Any) -> str:
                         result.append(str(arg.value))
                     elif isinstance(arg, Float):
                         result.append(repr(arg.value))
+                    elif isinstance(arg, CString):
+                        result.append(f"CString({arg.value!r})")
                     else:
                         result.append(self.names[arg])
                 elif typespec == "d":
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 61aadce9b9d4..8dc7d5c9c949 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -254,13 +254,11 @@ def __init__(
         elif ctype == "CPyPtr":
             # TODO: Invent an overlapping error value?
             self.c_undefined = "0"
-        elif ctype == "PyObject *":
-            # Boxed types use the null pointer as the error value.
+        elif ctype.endswith("*"):
+            # Boxed and pointer types use the null pointer as the error value.
             self.c_undefined = "NULL"
         elif ctype == "char":
             self.c_undefined = "2"
-        elif ctype in ("PyObject **", "void *"):
-            self.c_undefined = "NULL"
         elif ctype == "double":
             self.c_undefined = "-113.0"
         elif ctype in ("uint8_t", "uint16_t", "uint32_t", "uint64_t"):
@@ -445,6 +443,10 @@ def __hash__(self) -> int:
     "c_ptr", is_unboxed=False, is_refcounted=False, ctype="void *"
 )
 
+cstring_rprimitive: Final = RPrimitive(
+    "cstring", is_unboxed=True, is_refcounted=False, ctype="const char *"
+)
+
 # The type corresponding to mypyc.common.BITMAP_TYPE
 bitmap_rprimitive: Final = uint32_rprimitive
 
diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py
index 0a696e9e3380..6be4875dafa1 100644
--- a/mypyc/test/test_emitfunc.py
+++ b/mypyc/test/test_emitfunc.py
@@ -19,6 +19,7 @@
     CallC,
     Cast,
     ComparisonOp,
+    CString,
     DecRef,
     Extend,
     GetAttr,
@@ -49,6 +50,7 @@
     RType,
     bool_rprimitive,
     c_int_rprimitive,
+    cstring_rprimitive,
     dict_rprimitive,
     int32_rprimitive,
     int64_rprimitive,
@@ -836,6 +838,30 @@ def test_inc_ref_int_literal(self) -> None:
             b = LoadLiteral(x, object_rprimitive)
             self.assert_emit([b, IncRef(b)], "CPy_INCREF(cpy_r_r0);")
 
+    def test_c_string(self) -> None:
+        s = Register(cstring_rprimitive, "s")
+        self.assert_emit(Assign(s, CString(b"foo")), """cpy_r_s = "foo";""")
+        self.assert_emit(Assign(s, CString(b'foo "o')), r"""cpy_r_s = "foo \"o";""")
+        self.assert_emit(Assign(s, CString(b"\x00")), r"""cpy_r_s = "\x00";""")
+        self.assert_emit(Assign(s, CString(b"\\")), r"""cpy_r_s = "\\";""")
+        for i in range(256):
+            b = bytes([i])
+            if b == b"\n":
+                target = "\\n"
+            elif b == b"\r":
+                target = "\\r"
+            elif b == b"\t":
+                target = "\\t"
+            elif b == b'"':
+                target = '\\"'
+            elif b == b"\\":
+                target = "\\\\"
+            elif i < 32 or i >= 127:
+                target = "\\x%.2x" % i
+            else:
+                target = b.decode("ascii")
+            self.assert_emit(Assign(s, CString(b)), f'cpy_r_s = "{target}";')
+
     def assert_emit(
         self,
         op: Op | list[Op],

From 526fec3f8a4b2b575a7beb8b9d4a87720f57686e Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Mon, 7 Jul 2025 09:24:33 -0400
Subject: [PATCH 1432/1617] [mypyc] Generate introspection signatures for
 compiled functions (#19307)

Refs https://github.com/mypyc/mypyc/issues/838

This PR populates `__text_signature__` for compiled functions, making
runtime signature introspection possible (i.e.
`inspect.signature(func)`).

While `__text_signature__` is an undocumented CPython implementation
detail, other extension module generators are using it in practice. For
example, PyO3 and Cython both support it. I think it would be reasonable
for mypyc to support it too.

Some function signatures can't be represented by `__text_signature__`
(namely, those with complex default arguments). In those cases, no
signatures are generated (same as the current behavior).
---
 mypyc/codegen/emitclass.py            |  29 +++-
 mypyc/codegen/emitfunc.py             |  18 ++-
 mypyc/codegen/emitmodule.py           |  13 +-
 mypyc/doc/differences_from_python.rst |   3 +-
 mypyc/ir/func_ir.py                   |  96 +++++++++++-
 mypyc/test-data/run-signatures.test   | 207 ++++++++++++++++++++++++++
 mypyc/test/test_run.py                |   1 +
 7 files changed, 358 insertions(+), 9 deletions(-)
 create mode 100644 mypyc/test-data/run-signatures.test

diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index 576787424cbf..0c2d470104d0 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -5,8 +5,9 @@
 from collections.abc import Mapping
 from typing import Callable
 
+from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import Emitter, HeaderDeclaration, ReturnHandler
-from mypyc.codegen.emitfunc import native_function_header
+from mypyc.codegen.emitfunc import native_function_doc_initializer, native_function_header
 from mypyc.codegen.emitwrapper import (
     generate_bin_op_wrapper,
     generate_bool_wrapper,
@@ -21,7 +22,13 @@
 )
 from mypyc.common import BITMAP_BITS, BITMAP_TYPE, NATIVE_PREFIX, PREFIX, REG_PREFIX
 from mypyc.ir.class_ir import ClassIR, VTableEntries
-from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR
+from mypyc.ir.func_ir import (
+    FUNC_CLASSMETHOD,
+    FUNC_STATICMETHOD,
+    FuncDecl,
+    FuncIR,
+    get_text_signature,
+)
 from mypyc.ir.rtypes import RTuple, RType, object_rprimitive
 from mypyc.namegen import NameGenerator
 from mypyc.sametype import is_same_type
@@ -368,6 +375,8 @@ def emit_line() -> None:
         flags.append("Py_TPFLAGS_MANAGED_DICT")
     fields["tp_flags"] = " | ".join(flags)
 
+    fields["tp_doc"] = native_class_doc_initializer(cl)
+
     emitter.emit_line(f"static PyTypeObject {emitter.type_struct_name(cl)}_template_ = {{")
     emitter.emit_line("PyVarObject_HEAD_INIT(NULL, 0)")
     for field, value in fields.items():
@@ -915,7 +924,8 @@ def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None:
         elif fn.decl.kind == FUNC_CLASSMETHOD:
             flags.append("METH_CLASS")
 
-        emitter.emit_line(" {}, NULL}},".format(" | ".join(flags)))
+        doc = native_function_doc_initializer(fn)
+        emitter.emit_line(" {}, {}}},".format(" | ".join(flags), doc))
 
     # Provide a default __getstate__ and __setstate__
     if not cl.has_method("__setstate__") and not cl.has_method("__getstate__"):
@@ -1173,3 +1183,16 @@ def has_managed_dict(cl: ClassIR, emitter: Emitter) -> bool:
         and cl.has_dict
         and cl.builtin_base != "PyBaseExceptionObject"
     )
+
+
+def native_class_doc_initializer(cl: ClassIR) -> str:
+    init_fn = cl.get_method("__init__")
+    if init_fn is not None:
+        text_sig = get_text_signature(init_fn, bound=True)
+        if text_sig is None:
+            return "NULL"
+        text_sig = text_sig.replace("__init__", cl.name, 1)
+    else:
+        text_sig = f"{cl.name}()"
+    docstring = f"{text_sig}\n--\n\n"
+    return c_string_initializer(docstring.encode("ascii", errors="backslashreplace"))
diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 625ec0643df0..4b618f3c67db 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -5,6 +5,7 @@
 from typing import Final
 
 from mypyc.analysis.blockfreq import frequently_executed_blocks
+from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer
 from mypyc.common import (
     HAVE_IMMORTAL,
@@ -16,7 +17,14 @@
     TYPE_VAR_PREFIX,
 )
 from mypyc.ir.class_ir import ClassIR
-from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values
+from mypyc.ir.func_ir import (
+    FUNC_CLASSMETHOD,
+    FUNC_STATICMETHOD,
+    FuncDecl,
+    FuncIR,
+    all_values,
+    get_text_signature,
+)
 from mypyc.ir.ops import (
     ERR_FALSE,
     NAMESPACE_MODULE,
@@ -106,6 +114,14 @@ def native_function_header(fn: FuncDecl, emitter: Emitter) -> str:
     )
 
 
+def native_function_doc_initializer(func: FuncIR) -> str:
+    text_sig = get_text_signature(func)
+    if text_sig is None:
+        return "NULL"
+    docstring = f"{text_sig}\n--\n\n"
+    return c_string_initializer(docstring.encode("ascii", errors="backslashreplace"))
+
+
 def generate_native_function(
     fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
 ) -> None:
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index e1b6a7857294..2a6f17cea5e2 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -30,7 +30,11 @@
 from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import Emitter, EmitterContext, HeaderDeclaration, c_array_initializer
 from mypyc.codegen.emitclass import generate_class, generate_class_reuse, generate_class_type_decl
-from mypyc.codegen.emitfunc import generate_native_function, native_function_header
+from mypyc.codegen.emitfunc import (
+    generate_native_function,
+    native_function_doc_initializer,
+    native_function_header,
+)
 from mypyc.codegen.emitwrapper import (
     generate_legacy_wrapper_function,
     generate_wrapper_function,
@@ -917,11 +921,14 @@ def emit_module_methods(
                 flag = "METH_FASTCALL"
             else:
                 flag = "METH_VARARGS"
+            doc = native_function_doc_initializer(fn)
             emitter.emit_line(
                 (
                     '{{"{name}", (PyCFunction){prefix}{cname}, {flag} | METH_KEYWORDS, '
-                    "NULL /* docstring */}},"
-                ).format(name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag)
+                    "{doc} /* docstring */}},"
+                ).format(
+                    name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag, doc=doc
+                )
             )
         emitter.emit_line("{NULL, NULL, 0, NULL}")
         emitter.emit_line("};")
diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst
index 5a230bd984c2..b910e3b3c929 100644
--- a/mypyc/doc/differences_from_python.rst
+++ b/mypyc/doc/differences_from_python.rst
@@ -316,7 +316,8 @@ non-exhaustive list of what won't work:
 - Instance ``__annotations__`` is usually not kept
 - Frames of compiled functions can't be inspected using ``inspect``
 - Compiled methods aren't considered methods by ``inspect.ismethod``
-- ``inspect.signature`` chokes on compiled functions
+- ``inspect.signature`` chokes on compiled functions with default arguments that
+  are not simple literals
 - ``inspect.iscoroutinefunction`` and ``asyncio.iscoroutinefunction`` will always return False for compiled functions, even those defined with `async def`
 
 Profiling hooks and tracing
diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py
index beef8def7f43..881ac5939c27 100644
--- a/mypyc/ir/func_ir.py
+++ b/mypyc/ir/func_ir.py
@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import inspect
 from collections.abc import Sequence
 from typing import Final
 
@@ -11,13 +12,24 @@
     Assign,
     AssignMulti,
     BasicBlock,
+    Box,
     ControlOp,
     DeserMaps,
+    Float,
+    Integer,
     LoadAddress,
+    LoadLiteral,
     Register,
+    TupleSet,
     Value,
 )
-from mypyc.ir.rtypes import RType, bitmap_rprimitive, deserialize_type
+from mypyc.ir.rtypes import (
+    RType,
+    bitmap_rprimitive,
+    deserialize_type,
+    is_bool_rprimitive,
+    is_none_rprimitive,
+)
 from mypyc.namegen import NameGenerator
 
 
@@ -379,3 +391,85 @@ def all_values_full(args: list[Register], blocks: list[BasicBlock]) -> list[Valu
                     values.append(op)
 
     return values
+
+
+_ARG_KIND_TO_INSPECT: Final = {
+    ArgKind.ARG_POS: inspect.Parameter.POSITIONAL_OR_KEYWORD,
+    ArgKind.ARG_OPT: inspect.Parameter.POSITIONAL_OR_KEYWORD,
+    ArgKind.ARG_STAR: inspect.Parameter.VAR_POSITIONAL,
+    ArgKind.ARG_NAMED: inspect.Parameter.KEYWORD_ONLY,
+    ArgKind.ARG_STAR2: inspect.Parameter.VAR_KEYWORD,
+    ArgKind.ARG_NAMED_OPT: inspect.Parameter.KEYWORD_ONLY,
+}
+
+# Sentinel indicating a value that cannot be represented in a text signature.
+_NOT_REPRESENTABLE = object()
+
+
+def get_text_signature(fn: FuncIR, *, bound: bool = False) -> str | None:
+    """Return a text signature in CPython's internal doc format, or None
+    if the function's signature cannot be represented.
+    """
+    parameters = []
+    mark_self = (fn.class_name is not None) and (fn.decl.kind != FUNC_STATICMETHOD) and not bound
+    sig = fn.decl.bound_sig if bound and fn.decl.bound_sig is not None else fn.decl.sig
+    # Pre-scan for end of positional-only parameters.
+    # This is needed to handle signatures like 'def foo(self, __x)', where mypy
+    # currently sees 'self' as being positional-or-keyword and '__x' as positional-only.
+    pos_only_idx = -1
+    for idx, arg in enumerate(sig.args):
+        if arg.pos_only and arg.kind in (ArgKind.ARG_POS, ArgKind.ARG_OPT):
+            pos_only_idx = idx
+    for idx, arg in enumerate(sig.args):
+        if arg.name.startswith(("__bitmap", "__mypyc")):
+            continue
+        kind = (
+            inspect.Parameter.POSITIONAL_ONLY
+            if idx <= pos_only_idx
+            else _ARG_KIND_TO_INSPECT[arg.kind]
+        )
+        default: object = inspect.Parameter.empty
+        if arg.optional:
+            default = _find_default_argument(arg.name, fn.blocks)
+            if default is _NOT_REPRESENTABLE:
+                # This default argument cannot be represented in a __text_signature__
+                return None
+
+        curr_param = inspect.Parameter(arg.name, kind, default=default)
+        parameters.append(curr_param)
+        if mark_self:
+            # Parameter.__init__/Parameter.replace do not accept $
+            curr_param._name = f"${arg.name}"  # type: ignore[attr-defined]
+            mark_self = False
+    return f"{fn.name}{inspect.Signature(parameters)}"
+
+
+def _find_default_argument(name: str, blocks: list[BasicBlock]) -> object:
+    # Find assignment inserted by gen_arg_defaults. Assumed to be the first assignment.
+    for block in blocks:
+        for op in block.ops:
+            if isinstance(op, Assign) and op.dest.name == name:
+                return _extract_python_literal(op.src)
+    return _NOT_REPRESENTABLE
+
+
+def _extract_python_literal(value: Value) -> object:
+    if isinstance(value, Integer):
+        if is_none_rprimitive(value.type):
+            return None
+        val = value.numeric_value()
+        if is_bool_rprimitive(value.type):
+            return bool(val)
+        return val
+    elif isinstance(value, Float):
+        return value.value
+    elif isinstance(value, LoadLiteral):
+        return value.value
+    elif isinstance(value, Box):
+        return _extract_python_literal(value.src)
+    elif isinstance(value, TupleSet):
+        items = tuple(_extract_python_literal(item) for item in value.items)
+        if any(itm is _NOT_REPRESENTABLE for itm in items):
+            return _NOT_REPRESENTABLE
+        return items
+    return _NOT_REPRESENTABLE
diff --git a/mypyc/test-data/run-signatures.test b/mypyc/test-data/run-signatures.test
new file mode 100644
index 000000000000..a2de7076f5ef
--- /dev/null
+++ b/mypyc/test-data/run-signatures.test
@@ -0,0 +1,207 @@
+[case testSignaturesBasic]
+def f1(): pass
+def f2(x): pass
+def f3(x, /): pass
+def f4(*, x): pass
+def f5(*x): pass
+def f6(**x): pass
+def f7(x=None): pass
+def f8(x=None, /): pass
+def f9(*, x=None): pass
+def f10(a, /, b, c=None, *args, d=None, **h): pass
+
+[file driver.py]
+import inspect
+from native import *
+
+assert str(inspect.signature(f1)) == "()"
+assert str(inspect.signature(f2)) == "(x)"
+assert str(inspect.signature(f3)) == "(x, /)"
+assert str(inspect.signature(f4)) == "(*, x)"
+assert str(inspect.signature(f5)) == "(*x)"
+assert str(inspect.signature(f6)) == "(**x)"
+assert str(inspect.signature(f7)) == "(x=None)"
+assert str(inspect.signature(f8)) == "(x=None, /)"
+assert str(inspect.signature(f9)) == "(*, x=None)"
+assert str(inspect.signature(f10)) == "(a, /, b, c=None, *args, d=None, **h)"
+
+for fn in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]:
+    assert getattr(fn, "__doc__") is None
+
+[case testSignaturesValidDefaults]
+from typing import Final
+A: Final = 1
+
+def default_int(x=1): pass
+def default_str(x="a"): pass
+def default_float(x=1.0): pass
+def default_true(x=True): pass
+def default_false(x=False): pass
+def default_none(x=None): pass
+def default_tuple_empty(x=()): pass
+def default_tuple_literals(x=(1, "a", 1.0, False, True, None, (), (1,2,(3,4)))): pass
+def default_tuple_singleton(x=(1,)): pass
+def default_named_constant(x=A): pass
+
+[file driver.py]
+import inspect
+from native import *
+
+assert str(inspect.signature(default_int)) == "(x=1)"
+assert str(inspect.signature(default_str)) == "(x='a')"
+assert str(inspect.signature(default_float)) == "(x=1.0)"
+assert str(inspect.signature(default_true)) == "(x=True)"
+assert str(inspect.signature(default_false)) == "(x=False)"
+assert str(inspect.signature(default_none)) == "(x=None)"
+assert str(inspect.signature(default_tuple_empty)) == "(x=())"
+assert str(inspect.signature(default_tuple_literals)) == "(x=(1, 'a', 1.0, False, True, None, (), (1, 2, (3, 4))))"
+assert str(inspect.signature(default_named_constant)) == "(x=1)"
+
+# Check __text_signature__ directly since inspect.signature produces
+# an incorrect signature for 1-tuple default arguments prior to
+# Python 3.12 (cpython#102379).
+# assert str(inspect.signature(default_tuple_singleton)) == "(x=(1,))"
+assert getattr(default_tuple_singleton, "__text_signature__") == "(x=(1,))"
+
+[case testSignaturesStringDefaults]
+def f1(x="'foo"): pass
+def f2(x='"foo'): pass
+def f3(x=""""Isn\'t," they said."""): pass
+def f4(x="\\ \a \b \f \n \r \t \v \x00"): pass
+def f5(x="\N{BANANA}sv"): pass
+
+[file driver.py]
+import inspect
+from native import *
+
+assert str(inspect.signature(f1)) == """(x="'foo")"""
+assert str(inspect.signature(f2)) == """(x='"foo')"""
+assert str(inspect.signature(f3)) == r"""(x='"Isn\'t," they said.')"""
+assert str(inspect.signature(f4)) == r"""(x='\\ \x07 \x08 \x0c \n \r \t \x0b \x00')"""
+assert str(inspect.signature(f5)) == """(x='\N{BANANA}sv')"""
+
+[case testSignaturesIrrepresentableDefaults]
+import enum
+class Color(enum.Enum):
+    RED = 1
+misc = object()
+
+# Default arguments that cannot be represented in a __text_signature__
+def bad_object(x=misc): pass
+def bad_list_nonliteral(x=[misc]): pass
+def bad_dict_nonliteral(x={'a': misc}): pass
+def bad_set_nonliteral(x={misc}): pass
+def bad_set_empty(x=set()): pass  # supported by ast.literal_eval, but not by inspect._signature_fromstr
+def bad_nan(x=float("nan")): pass
+def bad_enum(x=Color.RED): pass
+
+# TODO: Default arguments that could potentially be represented in a
+# __text_signature__, but which are not currently supported.
+# See 'inspect._signature_fromstr' for what default values are supported at runtime.
+def bad_complex(x=1+2j): pass
+def bad_list_empty(x=[]): pass
+def bad_list_literals(x=[1, 2, 3]): pass
+def bad_dict_empty(x={}): pass
+def bad_dict_literals(x={'a': 1}): pass
+def bad_set_literals(x={1, 2, 3}): pass
+def bad_tuple_literals(x=([1, 2, 3], {'a': 1}, {1, 2, 3})): pass
+def bad_ellipsis(x=...): pass
+def bad_literal_fold(x=1+2): pass
+
+[file driver.py]
+import inspect
+from testutil import assertRaises
+import native
+
+all_bad = [fn for name, fn in vars(native).items() if name.startswith("bad_")]
+assert all_bad
+
+for bad in all_bad:
+    assert bad.__text_signature__ is None, f"{bad.__name__} has unexpected __text_signature__"
+    with assertRaises(ValueError, "no signature found for builtin"):
+        inspect.signature(bad)
+
+[case testSignaturesMethods]
+class Foo:
+    def f1(self, x): pass
+    @classmethod
+    def f2(cls, x): pass
+    @staticmethod
+    def f3(x): pass
+    def __eq__(self, x: object): pass
+
+[file driver.py]
+import inspect
+from native import *
+
+assert str(inspect.signature(Foo.f1)) == "(self, /, x)"
+assert str(inspect.signature(Foo().f1)) == "(x)"
+
+assert str(inspect.signature(Foo.f2)) == "(x)"
+assert str(inspect.signature(Foo().f2)) == "(x)"
+
+assert str(inspect.signature(Foo.f3)) == "(x)"
+assert str(inspect.signature(Foo().f3)) == "(x)"
+
+assert str(inspect.signature(Foo.__eq__)) == "(self, value, /)"
+assert str(inspect.signature(Foo().__eq__)) == "(value, /)"
+
+[case testSignaturesConstructors]
+class Empty: pass
+
+class HasInit:
+    def __init__(self, x) -> None: pass
+
+class InheritedInit(HasInit): pass
+
+class HasInitBad:
+    def __init__(self, x=[]) -> None: pass
+
+[file driver.py]
+import inspect
+from testutil import assertRaises
+from native import *
+
+assert str(inspect.signature(Empty)) == "()"
+assert str(inspect.signature(Empty.__init__)) == "(self, /, *args, **kwargs)"
+
+assert str(inspect.signature(HasInit)) == "(x)"
+assert str(inspect.signature(HasInit.__init__)) == "(self, /, *args, **kwargs)"
+
+assert str(inspect.signature(InheritedInit)) == "(x)"
+assert str(inspect.signature(InheritedInit.__init__)) == "(self, /, *args, **kwargs)"
+
+assert getattr(HasInitBad, "__text_signature__") is None
+with assertRaises(ValueError, "no signature found for builtin"):
+    inspect.signature(HasInitBad)
+
+# CPython detail note: type objects whose tp_doc contains only a text signature behave
+# differently from method objects whose ml_doc contains only a test signature: type
+# objects will have __doc__="" whereas method objects will have __doc__=None. This
+# difference stems from the former using _PyType_GetDocFromInternalDoc(...) and the
+# latter using PyUnicode_FromString(_PyType_DocWithoutSignature(...)).
+for cls in [Empty, HasInit, InheritedInit]:
+    assert getattr(cls, "__doc__") == ""
+assert getattr(HasInitBad, "__doc__") is None
+
+[case testSignaturesHistoricalPositionalOnly]
+import inspect
+
+def f1(__x): pass
+def f2(__x, y): pass
+def f3(*, __y): pass
+def f4(x, *, __y): pass
+def f5(__x, *, __y): pass
+
+class A:
+    def func(self, __x): pass
+
+def test_historical_positional_only() -> None:
+    assert str(inspect.signature(f1)) == "(__x, /)"
+    assert str(inspect.signature(f2)) == "(__x, /, y)"
+    assert str(inspect.signature(f3)) == "(*, __y)"
+    assert str(inspect.signature(f4)) == "(x, *, __y)"
+    assert str(inspect.signature(f5)) == "(__x, /, *, __y)"
+
+    assert str(inspect.signature(A.func)) == "(self, __x, /)"
+    assert str(inspect.signature(A().func)) == "(__x, /)"
diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py
index b96c4241f30d..fcc24403df8e 100644
--- a/mypyc/test/test_run.py
+++ b/mypyc/test/test_run.py
@@ -69,6 +69,7 @@
     "run-dunders-special.test",
     "run-singledispatch.test",
     "run-attrs.test",
+    "run-signatures.test",
     "run-python37.test",
     "run-python38.test",
 ]

From ada0d2a3bb227a23ef8955f2590c581f97cbcfaf Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 7 Jul 2025 15:43:02 +0100
Subject: [PATCH 1433/1617] [mypyc] Call generator helper method directly in
 await expression (#19376)

Previously calls like `await foo()` were compiled to code that included
code like this (in Python-like pseudocode):
```
a = foo()
...
b = get_coro(a)
...
c = next(b)
```
In the above code, `get_coro(a)` just returns `a` if `foo` is a native
async function, so we now optimize this call away. Also `next(b)` calls
`b.__next__()`, which calls the generated generator helper method
`__mypyc_generator_helper__`. Now we call the helper method directly,
which saves some unnecessary calls.

More importantly, in a follow-up PR I can easily change the way
`__mypyc_generator_helper__` is called, since we now call it directly.
This makes it possible to avoid raising a `StopIteration` exception in
many await expressions. The goal of this PR is to prepare for the latter
optimization. This PR doesn't help performance significantly by itself.

In order to call the helper method directly, I had to generate the
declaration of this method and the generated generator class before the
main irbuild pass, since otherwise a call site could be processed before
we have processed the called generator.

I also improved test coverage of related functionality. We don't have an
IR test for async calls, since the IR is very verbose. I manually
inspected the generated IR to verify that the new code path works both
when calling a top-level function and when calling a method. I'll later
add a mypyc benchmark to ensure that we will notice if the performance
of async calls is degraded.
---
 mypyc/irbuild/function.py           |  2 +-
 mypyc/irbuild/generator.py          | 64 ++++++--------------
 mypyc/irbuild/main.py               | 17 +++++-
 mypyc/irbuild/mapper.py             | 11 +++-
 mypyc/irbuild/prepare.py            | 47 ++++++++++++++-
 mypyc/irbuild/statement.py          | 39 ++++++++++--
 mypyc/test-data/run-async.test      | 93 ++++++++++++++++++++++++++++-
 mypyc/test-data/run-generators.test | 48 ++++++++++++---
 8 files changed, 251 insertions(+), 70 deletions(-)

diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index dbebc350bb6c..dcc5a306bcde 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -261,7 +261,7 @@ def c() -> None:
         )
 
         # Re-enter the FuncItem and visit the body of the function this time.
-        gen_generator_func_body(builder, fn_info, sig, func_reg)
+        gen_generator_func_body(builder, fn_info, func_reg)
     else:
         func_ir, func_reg = gen_func_body(builder, sig, cdef, is_singledispatch)
 
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 0e4b0e3e184a..eec27e1cfb84 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -13,9 +13,9 @@
 from typing import Callable
 
 from mypy.nodes import ARG_OPT, FuncDef, Var
-from mypyc.common import ENV_ATTR_NAME, NEXT_LABEL_ATTR_NAME, SELF_NAME
+from mypyc.common import ENV_ATTR_NAME, NEXT_LABEL_ATTR_NAME
 from mypyc.ir.class_ir import ClassIR
-from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg
+from mypyc.ir.func_ir import FuncDecl, FuncIR
 from mypyc.ir.ops import (
     NO_TRACEBACK_LINE_NO,
     BasicBlock,
@@ -78,9 +78,7 @@ def gen_generator_func(
     return func_ir, func_reg
 
 
-def gen_generator_func_body(
-    builder: IRBuilder, fn_info: FuncInfo, sig: FuncSignature, func_reg: Value | None
-) -> None:
+def gen_generator_func_body(builder: IRBuilder, fn_info: FuncInfo, func_reg: Value | None) -> None:
     """Generate IR based on the body of a generator function.
 
     Add "__next__", "__iter__" and other generator methods to the generator
@@ -88,7 +86,7 @@ class that implements the function (each function gets a separate class).
 
     Return the symbol table for the body.
     """
-    builder.enter(fn_info, ret_type=sig.ret_type)
+    builder.enter(fn_info, ret_type=object_rprimitive)
     setup_env_for_generator_class(builder)
 
     load_outer_envs(builder, builder.fn_info.generator_class)
@@ -117,7 +115,7 @@ class that implements the function (each function gets a separate class).
 
     args, _, blocks, ret_type, fn_info = builder.leave()
 
-    add_methods_to_generator_class(builder, fn_info, sig, args, blocks, fitem.is_coroutine)
+    add_methods_to_generator_class(builder, fn_info, args, blocks, fitem.is_coroutine)
 
     # Evaluate argument defaults in the surrounding scope, since we
     # calculate them *once* when the function definition is evaluated.
@@ -153,10 +151,9 @@ def instantiate_generator_class(builder: IRBuilder) -> Value:
 
 
 def setup_generator_class(builder: IRBuilder) -> ClassIR:
-    name = f"{builder.fn_info.namespaced_name()}_gen"
-
-    generator_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True)
-    generator_class_ir.reuse_freed_instance = True
+    mapper = builder.mapper
+    assert isinstance(builder.fn_info.fitem, FuncDef)
+    generator_class_ir = mapper.fdef_to_generator[builder.fn_info.fitem]
     if builder.fn_info.can_merge_generator_and_env_classes():
         builder.fn_info.env_class = generator_class_ir
     else:
@@ -216,46 +213,25 @@ def add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int)
 def add_methods_to_generator_class(
     builder: IRBuilder,
     fn_info: FuncInfo,
-    sig: FuncSignature,
     arg_regs: list[Register],
     blocks: list[BasicBlock],
     is_coroutine: bool,
 ) -> None:
-    helper_fn_decl = add_helper_to_generator_class(builder, arg_regs, blocks, sig, fn_info)
-    add_next_to_generator_class(builder, fn_info, helper_fn_decl, sig)
-    add_send_to_generator_class(builder, fn_info, helper_fn_decl, sig)
+    helper_fn_decl = add_helper_to_generator_class(builder, arg_regs, blocks, fn_info)
+    add_next_to_generator_class(builder, fn_info, helper_fn_decl)
+    add_send_to_generator_class(builder, fn_info, helper_fn_decl)
     add_iter_to_generator_class(builder, fn_info)
-    add_throw_to_generator_class(builder, fn_info, helper_fn_decl, sig)
+    add_throw_to_generator_class(builder, fn_info, helper_fn_decl)
     add_close_to_generator_class(builder, fn_info)
     if is_coroutine:
         add_await_to_generator_class(builder, fn_info)
 
 
 def add_helper_to_generator_class(
-    builder: IRBuilder,
-    arg_regs: list[Register],
-    blocks: list[BasicBlock],
-    sig: FuncSignature,
-    fn_info: FuncInfo,
+    builder: IRBuilder, arg_regs: list[Register], blocks: list[BasicBlock], fn_info: FuncInfo
 ) -> FuncDecl:
     """Generates a helper method for a generator class, called by '__next__' and 'throw'."""
-    sig = FuncSignature(
-        (
-            RuntimeArg(SELF_NAME, object_rprimitive),
-            RuntimeArg("type", object_rprimitive),
-            RuntimeArg("value", object_rprimitive),
-            RuntimeArg("traceback", object_rprimitive),
-            RuntimeArg("arg", object_rprimitive),
-        ),
-        sig.ret_type,
-    )
-    helper_fn_decl = FuncDecl(
-        "__mypyc_generator_helper__",
-        fn_info.generator_class.ir.name,
-        builder.module_name,
-        sig,
-        internal=True,
-    )
+    helper_fn_decl = fn_info.generator_class.ir.method_decls["__mypyc_generator_helper__"]
     helper_fn_ir = FuncIR(
         helper_fn_decl, arg_regs, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name
     )
@@ -272,9 +248,7 @@ def add_iter_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
         builder.add(Return(builder.self()))
 
 
-def add_next_to_generator_class(
-    builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature
-) -> None:
+def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None:
     """Generates the '__next__' method for a generator class."""
     with builder.enter_method(fn_info.generator_class.ir, "__next__", object_rprimitive, fn_info):
         none_reg = builder.none_object()
@@ -289,9 +263,7 @@ def add_next_to_generator_class(
         builder.add(Return(result))
 
 
-def add_send_to_generator_class(
-    builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature
-) -> None:
+def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None:
     """Generates the 'send' method for a generator class."""
     with builder.enter_method(fn_info.generator_class.ir, "send", object_rprimitive, fn_info):
         arg = builder.add_argument("arg", object_rprimitive)
@@ -307,9 +279,7 @@ def add_send_to_generator_class(
         builder.add(Return(result))
 
 
-def add_throw_to_generator_class(
-    builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature
-) -> None:
+def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None:
     """Generates the 'throw' method for a generator class."""
     with builder.enter_method(fn_info.generator_class.ir, "throw", object_rprimitive, fn_info):
         typ = builder.add_argument("type", object_rprimitive)
diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py
index 7cdc6b686778..894e8f277723 100644
--- a/mypyc/irbuild/main.py
+++ b/mypyc/irbuild/main.py
@@ -25,7 +25,7 @@ def f(x: int) -> int:
 from typing import Any, Callable, TypeVar, cast
 
 from mypy.build import Graph
-from mypy.nodes import ClassDef, Expression, MypyFile
+from mypy.nodes import ClassDef, Expression, FuncDef, MypyFile
 from mypy.state import state
 from mypy.types import Type
 from mypyc.analysis.attrdefined import analyze_always_defined_attrs
@@ -37,7 +37,11 @@ def f(x: int) -> int:
 from mypyc.irbuild.builder import IRBuilder
 from mypyc.irbuild.mapper import Mapper
 from mypyc.irbuild.prebuildvisitor import PreBuildVisitor
-from mypyc.irbuild.prepare import build_type_map, find_singledispatch_register_impls
+from mypyc.irbuild.prepare import (
+    build_type_map,
+    create_generator_class_if_needed,
+    find_singledispatch_register_impls,
+)
 from mypyc.irbuild.visitor import IRBuilderVisitor
 from mypyc.irbuild.vtable import compute_vtable
 from mypyc.options import CompilerOptions
@@ -76,6 +80,15 @@ def build_ir(
         pbv = PreBuildVisitor(errors, module, singledispatch_info.decorators_to_remove, types)
         module.accept(pbv)
 
+        # Declare generator classes for nested async functions and generators.
+        for fdef in pbv.nested_funcs:
+            if isinstance(fdef, FuncDef):
+                # Make generator class name sufficiently unique.
+                suffix = f"___{fdef.line}"
+                create_generator_class_if_needed(
+                    module.fullname, None, fdef, mapper, name_suffix=suffix
+                )
+
         # Construct and configure builder objects (cyclic runtime dependency).
         visitor = IRBuilderVisitor()
         builder = IRBuilder(
diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py
index 7c6e03d0037c..4a01255e2d5d 100644
--- a/mypyc/irbuild/mapper.py
+++ b/mypyc/irbuild/mapper.py
@@ -64,6 +64,8 @@ def __init__(self, group_map: dict[str, str | None]) -> None:
         self.type_to_ir: dict[TypeInfo, ClassIR] = {}
         self.func_to_decl: dict[SymbolNode, FuncDecl] = {}
         self.symbol_fullnames: set[str] = set()
+        # The corresponding generator class that implements a generator/async function
+        self.fdef_to_generator: dict[FuncDef, ClassIR] = {}
 
     def type_to_rtype(self, typ: Type | None) -> RType:
         if typ is None:
@@ -171,7 +173,14 @@ def fdef_to_sig(self, fdef: FuncDef, strict_dunders_typing: bool) -> FuncSignatu
                 for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds)
             ]
             arg_pos_onlys = [name is None for name in fdef.type.arg_names]
-            ret = self.type_to_rtype(fdef.type.ret_type)
+            # TODO: We could probably support decorators sometimes (static and class method?)
+            if (fdef.is_coroutine or fdef.is_generator) and not fdef.is_decorated:
+                # Give a more precise type for generators, so that we can optimize
+                # code that uses them. They return a generator object, which has a
+                # specific class. Without this, the type would have to be 'object'.
+                ret: RType = RInstance(self.fdef_to_generator[fdef])
+            else:
+                ret = self.type_to_rtype(fdef.type.ret_type)
         else:
             # Handle unannotated functions
             arg_types = [object_rprimitive for _ in fdef.arguments]
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 65951999dcf9..147392585b25 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -38,7 +38,7 @@
 from mypy.semanal import refers_to_fullname
 from mypy.traverser import TraverserVisitor
 from mypy.types import Instance, Type, get_proper_type
-from mypyc.common import PROPSET_PREFIX, get_id_from_name
+from mypyc.common import PROPSET_PREFIX, SELF_NAME, get_id_from_name
 from mypyc.crash import catch_errors
 from mypyc.errors import Errors
 from mypyc.ir.class_ir import ClassIR
@@ -51,7 +51,14 @@
     RuntimeArg,
 )
 from mypyc.ir.ops import DeserMaps
-from mypyc.ir.rtypes import RInstance, RType, dict_rprimitive, none_rprimitive, tuple_rprimitive
+from mypyc.ir.rtypes import (
+    RInstance,
+    RType,
+    dict_rprimitive,
+    none_rprimitive,
+    object_rprimitive,
+    tuple_rprimitive,
+)
 from mypyc.irbuild.mapper import Mapper
 from mypyc.irbuild.util import (
     get_func_def,
@@ -115,7 +122,7 @@ def build_type_map(
 
     # Collect all the functions also. We collect from the symbol table
     # so that we can easily pick out the right copy of a function that
-    # is conditionally defined.
+    # is conditionally defined. This doesn't include nested functions!
     for module in modules:
         for func in get_module_func_defs(module):
             prepare_func_def(module.fullname, None, func, mapper, options)
@@ -179,6 +186,8 @@ def prepare_func_def(
     mapper: Mapper,
     options: CompilerOptions,
 ) -> FuncDecl:
+    create_generator_class_if_needed(module_name, class_name, fdef, mapper)
+
     kind = (
         FUNC_STATICMETHOD
         if fdef.is_static
@@ -190,6 +199,38 @@ def prepare_func_def(
     return decl
 
 
+def create_generator_class_if_needed(
+    module_name: str, class_name: str | None, fdef: FuncDef, mapper: Mapper, name_suffix: str = ""
+) -> None:
+    """If function is a generator/async function, declare a generator class.
+
+    Each generator and async function gets a dedicated class that implements the
+    generator protocol with generated methods.
+    """
+    if fdef.is_coroutine or fdef.is_generator:
+        name = "_".join(x for x in [fdef.name, class_name] if x) + "_gen" + name_suffix
+        cir = ClassIR(name, module_name, is_generated=True, is_final_class=True)
+        cir.reuse_freed_instance = True
+        mapper.fdef_to_generator[fdef] = cir
+
+        helper_sig = FuncSignature(
+            (
+                RuntimeArg(SELF_NAME, object_rprimitive),
+                RuntimeArg("type", object_rprimitive),
+                RuntimeArg("value", object_rprimitive),
+                RuntimeArg("traceback", object_rprimitive),
+                RuntimeArg("arg", object_rprimitive),
+            ),
+            object_rprimitive,
+        )
+
+        # The implementation of most generator functionality is behind this magic method.
+        helper_fn_decl = FuncDecl(
+            "__mypyc_generator_helper__", name, module_name, helper_sig, internal=True
+        )
+        cir.method_decls[helper_fn_decl.name] = helper_fn_decl
+
+
 def prepare_method_def(
     ir: ClassIR,
     module_name: str,
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index 5c32d8f1a50c..9c7ffb6a3adf 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -48,11 +48,13 @@
 )
 from mypyc.common import TEMP_ATTR_NAME
 from mypyc.ir.ops import (
+    ERR_NEVER,
     NAMESPACE_MODULE,
     NO_TRACEBACK_LINE_NO,
     Assign,
     BasicBlock,
     Branch,
+    Call,
     InitStatic,
     Integer,
     LoadAddress,
@@ -930,16 +932,41 @@ def emit_yield_from_or_await(
     to_yield_reg = Register(object_rprimitive)
     received_reg = Register(object_rprimitive)
 
-    get_op = coro_op if is_await else iter_op
-    if isinstance(get_op, PrimitiveDescription):
-        iter_val = builder.primitive_op(get_op, [val], line)
+    helper_method = "__mypyc_generator_helper__"
+    if (
+        isinstance(val, (Call, MethodCall))
+        and isinstance(val.type, RInstance)
+        and val.type.class_ir.has_method(helper_method)
+    ):
+        # This is a generated native generator class, and we can use a fast path.
+        # This allows two optimizations:
+        # 1) No need to call CPy_GetCoro() or iter() since for native generators
+        #    it just returns the generator object (implemented here).
+        # 2) Instead of calling next(), call generator helper method directly,
+        #    since next() just calls __next__ which calls the helper method.
+        iter_val: Value = val
     else:
-        iter_val = builder.call_c(get_op, [val], line)
+        get_op = coro_op if is_await else iter_op
+        if isinstance(get_op, PrimitiveDescription):
+            iter_val = builder.primitive_op(get_op, [val], line)
+        else:
+            iter_val = builder.call_c(get_op, [val], line)
 
     iter_reg = builder.maybe_spill_assignable(iter_val)
 
     stop_block, main_block, done_block = BasicBlock(), BasicBlock(), BasicBlock()
-    _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], line)
+
+    if isinstance(iter_reg.type, RInstance) and iter_reg.type.class_ir.has_method(helper_method):
+        # Second fast path optimization: call helper directly (see also comment above).
+        obj = builder.read(iter_reg)
+        nn = builder.none_object()
+        m = MethodCall(obj, helper_method, [nn, nn, nn, nn], line)
+        # Generators have custom error handling, so disable normal error handling.
+        m.error_kind = ERR_NEVER
+        _y_init = builder.add(m)
+    else:
+        _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], line)
+
     builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR))
 
     # Try extracting a return value from a StopIteration and return it.
@@ -948,7 +975,7 @@ def emit_yield_from_or_await(
     builder.assign(result, builder.call_c(check_stop_op, [], line), line)
     # Clear the spilled iterator/coroutine so that it will be freed.
     # Otherwise, the freeing of the spilled register would likely be delayed.
-    err = builder.add(LoadErrorValue(object_rprimitive))
+    err = builder.add(LoadErrorValue(iter_reg.type))
     builder.assign(iter_reg, err, line)
     builder.goto(done_block)
 
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index 2dad720f99cd..b8c4c22daf71 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -2,6 +2,7 @@
 
 [case testRunAsyncBasics]
 import asyncio
+from typing import Callable, Awaitable
 
 from testutil import assertRaises
 
@@ -72,6 +73,63 @@ def test_exception() -> None:
     assert asyncio.run(exc5()) == 3
     assert asyncio.run(exc6()) == 3
 
+async def indirect_call(x: int, c: Callable[[int], Awaitable[int]]) -> int:
+    return await c(x)
+
+async def indirect_call_2(a: Awaitable[None]) -> None:
+    await a
+
+async def indirect_call_3(a: Awaitable[float]) -> float:
+    return (await a) + 1.0
+
+async def inc(x: int) -> int:
+    await asyncio.sleep(0)
+    return x + 1
+
+async def ident(x: float, err: bool = False) -> float:
+    await asyncio.sleep(0.0)
+    if err:
+        raise MyError()
+    return x + float("0.0")
+
+def test_indirect_call() -> None:
+    assert asyncio.run(indirect_call(3, inc)) == 4
+
+    with assertRaises(MyError):
+        asyncio.run(indirect_call_2(exc1()))
+
+    assert asyncio.run(indirect_call_3(ident(2.0))) == 3.0
+    assert asyncio.run(indirect_call_3(ident(-113.0))) == -112.0
+    assert asyncio.run(indirect_call_3(ident(-114.0))) == -113.0
+
+    with assertRaises(MyError):
+        asyncio.run(indirect_call_3(ident(1.0, True)))
+    with assertRaises(MyError):
+        asyncio.run(indirect_call_3(ident(-113.0, True)))
+
+class C:
+    def __init__(self, n: int) -> None:
+        self.n = n
+
+    async def add(self, x: int, err: bool = False) -> int:
+        await asyncio.sleep(0)
+        if err:
+            raise MyError()
+        return x + self.n
+
+async def method_call(x: int) -> int:
+    c = C(5)
+    return await c.add(x)
+
+async def method_call_exception() -> int:
+    c = C(5)
+    return await c.add(3, err=True)
+
+def test_async_method_call() -> None:
+    assert asyncio.run(method_call(3)) == 8
+    with assertRaises(MyError):
+        asyncio.run(method_call_exception())
+
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
 # eh, we could use the real type but it doesn't seem important
@@ -563,8 +621,10 @@ def test_bool() -> None:
 def run(x: object) -> object: ...
 
 [case testRunAsyncNestedFunctions]
+from __future__ import annotations
+
 import asyncio
-from typing import cast, Iterator
+from typing import cast, Iterator, overload, Awaitable, Any, TypeVar
 
 from testutil import assertRaises
 
@@ -641,6 +701,37 @@ def test_async_def_contains_two_nested_functions() -> None:
         (5 + 3 + 1), (7 + 4 + 10 + 1)
     )
 
+async def async_def_contains_overloaded_async_def(n: int) -> int:
+    @overload
+    async def f(x: int) -> int: ...
+
+    @overload
+    async def f(x: str) -> str: ...
+
+    async def f(x: int | str) -> Any:
+        return x
+
+    return (await f(n)) + 1
+
+
+def test_async_def_contains_overloaded_async_def() -> None:
+    assert asyncio.run(async_def_contains_overloaded_async_def(5)) == 6
+
+T = TypeVar("T")
+
+def deco(f: T) -> T:
+    return f
+
+async def async_def_contains_decorated_async_def(n: int) -> int:
+    @deco
+    async def f(x: int) -> int:
+        return x + 2
+
+    return (await f(n)) + 1
+
+
+def test_async_def_contains_decorated_async_def() -> None:
+    assert asyncio.run(async_def_contains_decorated_async_def(7)) == 10
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
 
diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test
index 9c0b51d58e79..a43aff27dd45 100644
--- a/mypyc/test-data/run-generators.test
+++ b/mypyc/test-data/run-generators.test
@@ -190,7 +190,9 @@ exit! a exception!
 ((1,), 'exception!')
 
 [case testYieldNested]
-from typing import Callable, Generator
+from typing import Callable, Generator, Iterator, TypeVar, overload
+
+from testutil import run_generator
 
 def normal(a: int, b: float) -> Callable:
     def generator(x: int, y: str) -> Generator:
@@ -235,15 +237,43 @@ def outer() -> Generator:
             yield i
     return recursive(10)
 
-[file driver.py]
-from native import normal, generator, triple, another_triple, outer
-from testutil import run_generator
+def test_return_nested_generator() -> None:
+    assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None)
+    assert run_generator(generator(1)) == ((1, 2, 3), None)
+    assert run_generator(triple()()) == ((1, 2, 3), None)
+    assert run_generator(another_triple()()) == ((1,), None)
+    assert run_generator(outer()) == ((0, 1, 2, 3, 4), None)
+
+def call_nested(x: int) -> list[int]:
+    def generator() -> Iterator[int]:
+        n = int() + 2
+        yield x
+        yield n * x
+
+    a = []
+    for x in generator():
+        a.append(x)
+    return a
+
+T = TypeVar("T")
+
+def deco(f: T) -> T:
+    return f
+
+def call_nested_decorated(x: int) -> list[int]:
+    @deco
+    def generator() -> Iterator[int]:
+        n = int() + 3
+        yield x
+        yield n * x
+
+    a = []
+    for x in generator():
+        a.append(x)
+    return a
 
-assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None)
-assert run_generator(generator(1)) == ((1, 2, 3), None)
-assert run_generator(triple()()) == ((1, 2, 3), None)
-assert run_generator(another_triple()()) == ((1,), None)
-assert run_generator(outer()) == ((0, 1, 2, 3, 4), None)
+def test_call_nested_generator_in_function() -> None:
+    assert call_nested_decorated(5) == [5, 15]
 
 [case testYieldThrow]
 from typing import Generator, Iterable, Any, Union

From 6600073b0abb76d9dfb3cbcee01667c3c0a54b31 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 7 Jul 2025 17:12:48 +0100
Subject: [PATCH 1434/1617] [mypyc] Use per-type free "lists" for nested
 functions (#19390)

Often at most one instance is allocated at any given time, so this
improves performance quite significantly in common cases. Uses the
freelist feature introduced in #19316.

This speeds up self check by about 0.5% (measured 100 samples). Speeds
up this microbenchmark that gets close to the maximal benefit by about
90%:

```
def f(x: int) -> int:
    def inc(y: int) -> int:
        return y + 1
    return inc(x)

def bench(n: int) -> None:
    for x in range(n):
        f(x)

from time import time
bench(1000)
t0 = time()
bench(50 * 1000 * 1000)
print(time() - t0)
```
---
 mypyc/irbuild/callable_class.py | 1 +
 mypyc/irbuild/env_class.py      | 1 +
 2 files changed, 2 insertions(+)

diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py
index c7c3c7677cda..bbd1b909afb6 100644
--- a/mypyc/irbuild/callable_class.py
+++ b/mypyc/irbuild/callable_class.py
@@ -56,6 +56,7 @@ class for the nested function.
     # environment to point at the previously defined environment
     # class.
     callable_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True)
+    callable_class_ir.reuse_freed_instance = True
 
     # The functools @wraps decorator attempts to call setattr on
     # nested functions, so we create a dict for these nested
diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py
index 9e72f7efcf94..51c854a4a2b2 100644
--- a/mypyc/irbuild/env_class.py
+++ b/mypyc/irbuild/env_class.py
@@ -48,6 +48,7 @@ class is generated, the function environment has not yet been
         is_generated=True,
         is_final_class=True,
     )
+    env_class.reuse_freed_instance = True
     env_class.attributes[SELF_NAME] = RInstance(env_class)
     if builder.fn_info.is_nested:
         # If the function is nested, its environment class must contain an environment

From cb3bddd41149405e2dfaa615cfc4c6f109eb80f3 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Mon, 7 Jul 2025 21:42:26 +0200
Subject: [PATCH 1435/1617] Improve the support for promotions inside unions.
 (#19245)

Fixes #14987

I was puzzled as to why my previous attempts to avoid false
`unreachable` warnings for loops failed for issue #14987. After some
debugging, I realised that the underlying problem is that type narrowing
does not work with promotions if both the declared type and the
constraining type are unions:

```python
x: float | None
y: int | None
x = y
reveal_type(x)  #  None !!!
```

The fix seems straightforward (but let's see what the Mypy primer says)
and is checked by the test cases `testNarrowPromotionsInsideUnions1` and
`testNarrowPromotionsInsideUnions2`.

---------

Co-authored-by: Ivan Levkivskyi 
---
 mypy/meet.py                        | 18 ++++++++++++----
 test-data/unit/check-narrowing.test | 33 +++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/mypy/meet.py b/mypy/meet.py
index 7a44feabc10c..2e238be7765e 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -128,18 +128,28 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
     if declared == narrowed:
         return original_declared
     if isinstance(declared, UnionType):
+        declared_items = declared.relevant_items()
+        if isinstance(narrowed, UnionType):
+            narrowed_items = narrowed.relevant_items()
+        else:
+            narrowed_items = [narrowed]
         return make_simplified_union(
             [
-                narrow_declared_type(x, narrowed)
-                for x in declared.relevant_items()
+                narrow_declared_type(d, n)
+                for d in declared_items
+                for n in narrowed_items
                 # This (ugly) special-casing is needed to support checking
                 # branches like this:
                 # x: Union[float, complex]
                 # if isinstance(x, int):
                 #     ...
+                # And assignments like this:
+                # x: float | None
+                # y: int | None
+                # x = y
                 if (
-                    is_overlapping_types(x, narrowed, ignore_promotions=True)
-                    or is_subtype(narrowed, x, ignore_promotions=False)
+                    is_overlapping_types(d, n, ignore_promotions=True)
+                    or is_subtype(n, d, ignore_promotions=False)
                 )
             ]
         )
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index e322bd7a37b8..7fffd3ce94e5 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -2446,6 +2446,39 @@ while x is not None and b():
     x = f()
 [builtins fixtures/primitives.pyi]
 
+[case testNarrowPromotionsInsideUnions1]
+
+from typing import Union
+
+x: Union[str, float, None]
+y: Union[int, str]
+x = y
+reveal_type(x)  # N: Revealed type is "Union[builtins.str, builtins.int]"
+z: Union[complex, str]
+z = x
+reveal_type(z)  # N: Revealed type is "Union[builtins.int, builtins.str]"
+
+[builtins fixtures/primitives.pyi]
+
+[case testNarrowPromotionsInsideUnions2]
+# flags: --warn-unreachable
+
+from typing import Optional
+
+def b() -> bool: ...
+def i() -> int: ...
+x: Optional[float]
+
+while b():
+    x = None
+    while b():
+        reveal_type(x)  # N: Revealed type is "Union[None, builtins.int]"
+        if x is None or b():
+            x = i()
+            reveal_type(x)  # N: Revealed type is "builtins.int"
+
+[builtins fixtures/bool.pyi]
+
 [case testAvoidFalseUnreachableInFinally]
 # flags: --allow-redefinition-new --local-partial-types --warn-unreachable
 def f() -> None:

From afd5a382dc95532da61e6ebc7002323fbca75548 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 8 Jul 2025 01:53:05 +0200
Subject: [PATCH 1436/1617] perf: add `__slots__` to `SubtypeVisitor` (#19394)

We construct quite a lot of them. This made a selfcheck benchmark
0.4-0.7% faster when run on my local PC.
---
 mypy/subtypes.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 05f34aaec8f1..428e6dec6749 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -390,6 +390,15 @@ def check_type_parameter(
 
 
 class SubtypeVisitor(TypeVisitor[bool]):
+    __slots__ = (
+        "right",
+        "orig_right",
+        "proper_subtype",
+        "subtype_context",
+        "options",
+        "_subtype_kind",
+    )
+
     def __init__(self, right: Type, subtype_context: SubtypeContext, proper_subtype: bool) -> None:
         self.right = get_proper_type(right)
         self.orig_right = right

From f9fe33147a9137b2579b6695dbd81980b1cca7f5 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Tue, 8 Jul 2025 12:02:54 +0200
Subject: [PATCH 1437/1617] [mypyc] Simplify comparison of tuple elements
 (#19396)

Got rid of unnecessary operations when comparing tuple elements returns
a bit primitive.

Example code:
```
def f(x: tuple[float], y: tuple[float]) -> bool:
    return x == y
```
IR before:
```
def f(x, y):
    x, y :: tuple[float]
    r0, r1 :: float
    r2 :: bit
    r3 :: object
    r4 :: i32
    r5 :: bit
    r6, r7, r8 :: bool
L0:
    r0 = x[0]
    r1 = y[0]
    r2 = r0 == r1
    r3 = box(bit, r2)
    r4 = PyObject_IsTrue(r3)
    r5 = r4 >= 0 :: signed
    if not r5 goto L5 (error at f:2) else goto L1 :: bool
L1:
    r6 = truncate r4: i32 to builtins.bool
    if not r6 goto L2 else goto L3 :: bool
L2:
    r7 = 0
    goto L4
L3:
    r7 = 1
L4:
    return r7
L5:
    r8 =  :: bool
    return r8
```
IR after:
```
def f(x, y):
    x, y :: tuple[float]
    r0, r1 :: float
    r2 :: bit
    r3 :: bool
L0:
    r0 = x[0]
    r1 = y[0]
    r2 = r0 == r1
    if not r2 goto L1 else goto L2 :: bool
L1:
    r3 = 0
    goto L3
L2:
    r3 = 1
L3:
    return r3
```

Tested using the following benchmark:
```
def f(x: tuple[float,float], y: tuple[float,float]) -> bool:
    return x == y

def bench(n: int) -> None:
    for x in range(n):
        lhs = (float(x), float(x * 2))
        rhs = (float(x), float(x * 3))
        f(lhs, rhs)

from time import time
bench(1000)
t0 = time()
bench(50 * 1000 * 1000)
print(time() - t0)
```
Execution time goes from ~315ms to ~150ms on my machine.
---
 mypyc/irbuild/ll_builder.py        |  2 +-
 mypyc/test-data/irbuild-tuple.test | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 36b7c9241c71..be4178a4a71a 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -1534,7 +1534,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
             compare = self.binary_op(lhs_item, rhs_item, op, line)
             # Cast to bool if necessary since most types uses comparison returning a object type
             # See generic_ops.py for more information
-            if not is_bool_rprimitive(compare.type):
+            if not (is_bool_rprimitive(compare.type) or is_bit_rprimitive(compare.type)):
                 compare = self.primitive_op(bool_op, [compare], line)
             if i < len(lhs.type.types) - 1:
                 branch = Branch(compare, early_stop, check_blocks[i + 1], Branch.BOOL)
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index 582391ff6f98..222021751080 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -453,3 +453,26 @@ L0:
     r0 = CPySequence_Multiply(a, 4)
     b = r0
     return 1
+
+[case testTupleFloatElementComparison]
+def f(x: tuple[float], y: tuple[float]) -> bool:
+    return x == y
+
+[out]
+def f(x, y):
+    x, y :: tuple[float]
+    r0, r1 :: float
+    r2 :: bit
+    r3 :: bool
+L0:
+    r0 = x[0]
+    r1 = y[0]
+    r2 = r0 == r1
+    if not r2 goto L1 else goto L2 :: bool
+L1:
+    r3 = 0
+    goto L3
+L2:
+    r3 = 1
+L3:
+    return r3

From d503edfb906814a815bf048437280ad74cd46c92 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Tue, 8 Jul 2025 12:05:25 +0200
Subject: [PATCH 1438/1617] [mypyc] Raise NameError on undefined names (#19395)

Fixes https://github.com/mypyc/mypyc/issues/879

Changed the type of exception that is raised on undefined names from
`RuntimeError` to `NameError`.

Moved the runtime error that refers to unexpected execution of
unreachable code to `shortcircuit_expr` to keep existing behavior and
generate the exception in blocks that are unreachable due to
short-circuiting.

In the future this could probably be optimized to not generate any code
at all and just constant-fold the boolean expression.
---
 mypyc/irbuild/builder.py                 | 18 +++++++++++++-----
 mypyc/irbuild/expression.py              |  4 +---
 mypyc/test-data/irbuild-basic.test       | 15 +++++++++++++++
 mypyc/test-data/irbuild-unreachable.test | 15 +++------------
 4 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 323450f7c340..daed97cb896d 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -1141,12 +1141,20 @@ def call_refexpr_with_args(
         )
 
     def shortcircuit_expr(self, expr: OpExpr) -> Value:
+        def handle_right() -> Value:
+            if expr.right_unreachable:
+                self.builder.add(
+                    RaiseStandardError(
+                        RaiseStandardError.RUNTIME_ERROR,
+                        "mypyc internal error: should be unreachable",
+                        expr.right.line,
+                    )
+                )
+                return self.builder.none()
+            return self.accept(expr.right)
+
         return self.builder.shortcircuit_helper(
-            expr.op,
-            self.node_type(expr),
-            lambda: self.accept(expr.left),
-            lambda: self.accept(expr.right),
-            expr.line,
+            expr.op, self.node_type(expr), lambda: self.accept(expr.left), handle_right, expr.line
         )
 
     # Basic helpers
diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index c8c67cae309b..c4a3f5f38ce3 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -117,9 +117,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
     if expr.node is None:
         builder.add(
             RaiseStandardError(
-                RaiseStandardError.RUNTIME_ERROR,
-                "mypyc internal error: should be unreachable",
-                expr.line,
+                RaiseStandardError.NAME_ERROR, f'name "{expr.name}" is not defined', expr.line
             )
         )
         return builder.none()
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index 6e5267fc34dd..d652cb9c9a14 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -3556,3 +3556,18 @@ L3:
     s = arg
     r3 = CPyObject_Size(s)
     return r3
+
+[case testUndefinedFunction]
+def f():
+    non_existent_function()
+
+[out]
+def f():
+    r0 :: bool
+    r1, r2, r3 :: object
+L0:
+    r0 = raise NameError('name "non_existent_function" is not defined')
+    r1 = box(None, 1)
+    r2 = PyObject_Vectorcall(r1, 0, 0, 0)
+    r3 = box(None, 1)
+    return r3
diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test
index 7209c00ce75d..cebd4582923b 100644
--- a/mypyc/test-data/irbuild-unreachable.test
+++ b/mypyc/test-data/irbuild-unreachable.test
@@ -17,11 +17,7 @@ def f():
     r8, r9, r10 :: bit
     r11, r12 :: bool
     r13 :: object
-    r14 :: str
-    r15 :: object
-    r16 :: tuple[int, int]
-    r17, r18 :: object
-    r19, y :: bool
+    r14, y :: bool
 L0:
     r0 = sys :: module
     r1 = 'platform'
@@ -46,13 +42,8 @@ L4:
 L5:
     r12 = raise RuntimeError('mypyc internal error: should be unreachable')
     r13 = box(None, 1)
-    r14 = 'version_info'
-    r15 = CPyObject_GetAttr(r13, r14)
-    r16 = (6, 10)
-    r17 = box(tuple[int, int], r16)
-    r18 = PyObject_RichCompare(r15, r17, 4)
-    r19 = unbox(bool, r18)
-    r11 = r19
+    r14 = unbox(bool, r13)
+    r11 = r14
 L6:
     y = r11
     return 1

From 503f5bdd780c1895467c872d3f27f74d756cddec Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 8 Jul 2025 14:00:25 +0100
Subject: [PATCH 1439/1617] [mypyc] Add tests for string equality (#19401)

This is in preparation for adding a faster str equality primitive.
---
 mypyc/test-data/run-strings.test | 58 ++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index 074e56f9068a..c726c4c70896 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -92,6 +92,64 @@ assert remove_prefix_suffix('', '') == ('', '')
 assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc')
 assert remove_prefix_suffix('abc', 'c') == ('abc', 'ab')
 
+[case testStringEquality]
+def eq(a: str, b: str) -> bool:
+    return a == b
+def ne(a: str, b: str) -> bool:
+    return a != b
+
+def test_basic() -> None:
+    xy = "xy"
+    xy2 = str().join(["x", "y"])
+    xx = "xx"
+    yy = "yy"
+    xxx = "xxx"
+
+    assert eq("", str())
+    assert not ne("", str())
+
+    assert eq("x", "x" + str())
+    assert ne("x", "y")
+
+    assert eq(xy, xy)
+    assert eq(xy, xy2)
+    assert not eq(xy, yy)
+    assert ne(xy, xx)
+    assert not ne(xy, xy)
+    assert not ne(xy, xy2)
+
+    assert ne(xx, xxx)
+    assert ne(xxx, xx)
+    assert ne("x", "")
+    assert ne("", "x")
+
+    assert ne("XX", xx)
+    assert ne(yy, xy)
+
+def test_unicode() -> None:
+    assert eq(chr(200), chr(200) + str())
+    assert ne(chr(200), chr(201))
+
+    assert eq(chr(1234), chr(1234) + str())
+    assert ne(chr(1234), chr(1235))
+
+    assert eq("\U0001f4a9", "\U0001f4a9" + str())
+    assert eq("\U0001f4a9", "\U0001F4A9" + str())
+    assert ne("\U0001f4a9", "\U0002f4a9" + str())
+    assert ne("\U0001f4a9", "\U0001f5a9" + str())
+    assert ne("\U0001f4a9", "\U0001f4a8" + str())
+
+    assert eq("foobar\u1234", "foobar\u1234" + str())
+    assert eq("\u1234foobar", "\u1234foobar" + str())
+    assert ne("foobar\uf234", "foobar\uf235")
+    assert ne("foobar\uf234", "foobar\uf334")
+    assert ne("foobar\u1234", "Foobar\u1234" + str())
+
+    assert eq("foo\U0001f4a9", "foo\U0001f4a9" + str())
+    assert eq("\U0001f4a9foo", "\U0001f4a9foo" + str())
+    assert ne("foo\U0001f4a9", "foo\U0001f4a8" + str())
+    assert ne("\U0001f4a9foo", "\U0001f4a8foo" + str())
+
 [case testStringOps]
 from typing import List, Optional, Tuple
 from testutil import assertRaises

From 4a427e9f1ac8a007d5c9bea946cfeb9e163cfa89 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 8 Jul 2025 17:46:00 +0100
Subject: [PATCH 1440/1617] [mypyc] Speed up native-to-native calls using await
 (#19398)

When calling a native async function using `await`, e.g. `await foo()`,
avoid raising `StopIteration` to pass the return value, since this is
expensive. Instead, pass an extra `PyObject **` argument to the
generator helper method and use that to return the return value. This is
mostly helpful when there are many calls using await that don't block
(e.g. there is a fast path that is usually taken that doesn't block).
When awaiting from non-compiled code, the slow path is still taken.

This builds on top of #19376.

This PR makes this microbenchmark about 3x faster, which is about the
ideal scenario for this optimization:
```
import asyncio
from time import time

async def inc(x: int) -> int:
    return x + 1


async def bench(n: int) -> int:
    x = 0
    for i in range(n):
        x = await inc(x)
    return x

asyncio.run(bench(1000))

t0 = time()
asyncio.run(bench(1000 * 1000 * 200))
print(time() - t0)
```
---
 mypyc/irbuild/context.py         |  5 ++++
 mypyc/irbuild/generator.py       | 41 ++++++++++++++++++++++++++++----
 mypyc/irbuild/nonlocalcontrol.py | 19 +++++++++++++++
 mypyc/irbuild/prepare.py         |  3 +++
 mypyc/irbuild/statement.py       | 22 +++++++++++++----
 mypyc/lower/misc_ops.py          | 10 ++++++--
 mypyc/primitives/exc_ops.py      | 12 +++++++++-
 7 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/mypyc/irbuild/context.py b/mypyc/irbuild/context.py
index 8d35c0ce2599..8d2e55ed96fb 100644
--- a/mypyc/irbuild/context.py
+++ b/mypyc/irbuild/context.py
@@ -167,6 +167,11 @@ def __init__(self, ir: ClassIR) -> None:
         # Holds the arg passed to send
         self.send_arg_reg: Value | None = None
 
+        # Holds the PyObject ** pointer through which return value can be passed
+        # instead of raising StopIteration(ret_value) (only if not NULL). This
+        # is used for faster native-to-native calls.
+        self.stop_iter_value_reg: Value | None = None
+
         # The switch block is used to decide which instruction to go using the value held in the
         # next-label register.
         self.switch_block = BasicBlock()
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index eec27e1cfb84..ae45aed2fc67 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -32,7 +32,12 @@
     Unreachable,
     Value,
 )
-from mypyc.ir.rtypes import RInstance, int32_rprimitive, object_rprimitive
+from mypyc.ir.rtypes import (
+    RInstance,
+    int32_rprimitive,
+    object_pointer_rprimitive,
+    object_rprimitive,
+)
 from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults
 from mypyc.irbuild.context import FuncInfo, GeneratorClass
 from mypyc.irbuild.env_class import (
@@ -256,7 +261,14 @@ def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl:
         result = builder.add(
             Call(
                 fn_decl,
-                [builder.self(), none_reg, none_reg, none_reg, none_reg],
+                [
+                    builder.self(),
+                    none_reg,
+                    none_reg,
+                    none_reg,
+                    none_reg,
+                    Integer(0, object_pointer_rprimitive),
+                ],
                 fn_info.fitem.line,
             )
         )
@@ -272,7 +284,14 @@ def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl:
         result = builder.add(
             Call(
                 fn_decl,
-                [builder.self(), none_reg, none_reg, none_reg, builder.read(arg)],
+                [
+                    builder.self(),
+                    none_reg,
+                    none_reg,
+                    none_reg,
+                    builder.read(arg),
+                    Integer(0, object_pointer_rprimitive),
+                ],
                 fn_info.fitem.line,
             )
         )
@@ -297,7 +316,14 @@ def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl:
         result = builder.add(
             Call(
                 fn_decl,
-                [builder.self(), builder.read(typ), builder.read(val), builder.read(tb), none_reg],
+                [
+                    builder.self(),
+                    builder.read(typ),
+                    builder.read(val),
+                    builder.read(tb),
+                    none_reg,
+                    Integer(0, object_pointer_rprimitive),
+                ],
                 fn_info.fitem.line,
             )
         )
@@ -377,8 +403,15 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None:
     # TODO: Use the right type here instead of object?
     exc_arg = builder.add_local(Var("arg"), object_rprimitive, is_arg=True)
 
+    # Parameter that can used to pass a pointer which can used instead of
+    # raising StopIteration(value). If the value is NULL, this won't be used.
+    stop_iter_value_arg = builder.add_local(
+        Var("stop_iter_ptr"), object_pointer_rprimitive, is_arg=True
+    )
+
     cls.exc_regs = (exc_type, exc_val, exc_tb)
     cls.send_arg_reg = exc_arg
+    cls.stop_iter_value_reg = stop_iter_value_arg
 
     cls.self_reg = builder.read(self_target, fitem.line)
     if builder.fn_info.can_merge_generator_and_env_classes():
diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py
index 0ac9bd3cee31..887f6786718d 100644
--- a/mypyc/irbuild/nonlocalcontrol.py
+++ b/mypyc/irbuild/nonlocalcontrol.py
@@ -16,9 +16,11 @@
     Integer,
     Register,
     Return,
+    SetMem,
     Unreachable,
     Value,
 )
+from mypyc.ir.rtypes import object_rprimitive
 from mypyc.irbuild.targets import AssignmentTarget
 from mypyc.primitives.exc_ops import restore_exc_info_op, set_stop_iteration_value
 
@@ -108,10 +110,27 @@ def gen_return(self, builder: IRBuilder, value: Value, line: int) -> None:
         # StopIteration instead of using RaiseStandardError because
         # the obvious thing doesn't work if the value is a tuple
         # (???).
+
+        true, false = BasicBlock(), BasicBlock()
+        stop_iter_reg = builder.fn_info.generator_class.stop_iter_value_reg
+        assert stop_iter_reg is not None
+
+        builder.add(Branch(stop_iter_reg, true, false, Branch.IS_ERROR))
+
+        builder.activate_block(true)
+        # The default/slow path is to raise a StopIteration exception with
+        # return value.
         builder.call_c(set_stop_iteration_value, [value], NO_TRACEBACK_LINE_NO)
         builder.add(Unreachable())
         builder.builder.pop_error_handler()
 
+        builder.activate_block(false)
+        # The fast path is to store return value via caller-provided pointer
+        # instead of raising an exception. This can only be used when the
+        # caller is a native function.
+        builder.add(SetMem(object_rprimitive, stop_iter_reg, value))
+        builder.add(Return(Integer(0, object_rprimitive)))
+
 
 class CleanupNonlocalControl(NonlocalControl):
     """Abstract nonlocal control that runs some cleanup code."""
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 147392585b25..d4ec814372cd 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -56,6 +56,7 @@
     RType,
     dict_rprimitive,
     none_rprimitive,
+    object_pointer_rprimitive,
     object_rprimitive,
     tuple_rprimitive,
 )
@@ -220,6 +221,8 @@ def create_generator_class_if_needed(
                 RuntimeArg("value", object_rprimitive),
                 RuntimeArg("traceback", object_rprimitive),
                 RuntimeArg("arg", object_rprimitive),
+                # If non-NULL, used to store return value instead of raising StopIteration(retv)
+                RuntimeArg("stop_iter_ptr", object_pointer_rprimitive),
             ),
             object_rprimitive,
         )
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index 9c7ffb6a3adf..5f75a60d8d0a 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -103,6 +103,7 @@
     get_exc_info_op,
     get_exc_value_op,
     keep_propagating_op,
+    propagate_if_error_op,
     raise_exception_op,
     reraise_exception_op,
     restore_exc_info_op,
@@ -958,21 +959,34 @@ def emit_yield_from_or_await(
 
     if isinstance(iter_reg.type, RInstance) and iter_reg.type.class_ir.has_method(helper_method):
         # Second fast path optimization: call helper directly (see also comment above).
+        #
+        # Calling a generated generator, so avoid raising StopIteration by passing
+        # an extra PyObject ** argument to helper where the stop iteration value is stored.
+        fast_path = True
         obj = builder.read(iter_reg)
         nn = builder.none_object()
-        m = MethodCall(obj, helper_method, [nn, nn, nn, nn], line)
+        stop_iter_val = Register(object_rprimitive)
+        err = builder.add(LoadErrorValue(object_rprimitive, undefines=True))
+        builder.assign(stop_iter_val, err, line)
+        ptr = builder.add(LoadAddress(object_pointer_rprimitive, stop_iter_val))
+        m = MethodCall(obj, helper_method, [nn, nn, nn, nn, ptr], line)
         # Generators have custom error handling, so disable normal error handling.
         m.error_kind = ERR_NEVER
         _y_init = builder.add(m)
     else:
+        fast_path = False
         _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], line)
 
     builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR))
 
-    # Try extracting a return value from a StopIteration and return it.
-    # If it wasn't, this reraises the exception.
     builder.activate_block(stop_block)
-    builder.assign(result, builder.call_c(check_stop_op, [], line), line)
+    if fast_path:
+        builder.primitive_op(propagate_if_error_op, [stop_iter_val], line)
+        builder.assign(result, stop_iter_val, line)
+    else:
+        # Try extracting a return value from a StopIteration and return it.
+        # If it wasn't, this reraises the exception.
+        builder.assign(result, builder.call_c(check_stop_op, [], line), line)
     # Clear the spilled iterator/coroutine so that it will be freed.
     # Otherwise, the freeing of the spilled register would likely be delayed.
     err = builder.add(LoadErrorValue(iter_reg.type))
diff --git a/mypyc/lower/misc_ops.py b/mypyc/lower/misc_ops.py
index 1effcd4f42ac..3c42257c0dbe 100644
--- a/mypyc/lower/misc_ops.py
+++ b/mypyc/lower/misc_ops.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
-from mypyc.ir.ops import GetElementPtr, LoadMem, Value
-from mypyc.ir.rtypes import PyVarObject, c_pyssize_t_rprimitive
+from mypyc.ir.ops import ComparisonOp, GetElementPtr, Integer, LoadMem, Value
+from mypyc.ir.rtypes import PyVarObject, c_pyssize_t_rprimitive, object_rprimitive
 from mypyc.irbuild.ll_builder import LowLevelIRBuilder
 from mypyc.lower.registry import lower_primitive_op
 
@@ -10,3 +10,9 @@
 def var_object_size(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value:
     elem_address = builder.add(GetElementPtr(args[0], PyVarObject, "ob_size"))
     return builder.add(LoadMem(c_pyssize_t_rprimitive, elem_address))
+
+
+@lower_primitive_op("propagate_if_error")
+def propagate_if_error_op(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value:
+    # Return False on NULL. The primitive uses ERR_FALSE, so this is an error.
+    return builder.add(ComparisonOp(args[0], Integer(0, object_rprimitive), ComparisonOp.NEQ))
diff --git a/mypyc/primitives/exc_ops.py b/mypyc/primitives/exc_ops.py
index 9a5f6392a917..e1234f807afa 100644
--- a/mypyc/primitives/exc_ops.py
+++ b/mypyc/primitives/exc_ops.py
@@ -4,7 +4,7 @@
 
 from mypyc.ir.ops import ERR_ALWAYS, ERR_FALSE, ERR_NEVER
 from mypyc.ir.rtypes import bit_rprimitive, exc_rtuple, object_rprimitive, void_rtype
-from mypyc.primitives.registry import custom_op
+from mypyc.primitives.registry import custom_op, custom_primitive_op
 
 # If the argument is a class, raise an instance of the class. Otherwise, assume
 # that the argument is an exception object, and raise it.
@@ -62,6 +62,16 @@
     error_kind=ERR_FALSE,
 )
 
+# If argument is NULL, propagate currently raised exception (in this case
+# an exception must have been raised). If this can be used, it's faster
+# than using PyErr_Occurred().
+propagate_if_error_op = custom_primitive_op(
+    "propagate_if_error",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    error_kind=ERR_FALSE,
+)
+
 # Catches a propagating exception and makes it the "currently
 # handled exception" (by sticking it into sys.exc_info()). Returns the
 # exception that was previously being handled, which must be restored

From 10f95e666d12c0060a638b39838df4c6474648f2 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 9 Jul 2025 10:25:52 +0100
Subject: [PATCH 1441/1617] [mypyc] Add faster primitive for string equality
 (#19402)

This speeds up self check by ~1.4%.  String equality is one of the top
five most common primitive function calls in self check.

We previously used a string comparison primitive that calculated the
relative order of two strings. Usually we only care about equality,
which we can do quicker since we can fast path using a length check,
for example.

I checked the CPython implementation of string equality in 3.9 (lowest
supported Python version) and 3.13, and both of them had a fast path
based on string object kind, and equality checks overall had the same
semantics.

Current CPython implementation:
https://github.com/python/cpython/blob/main/Objects/stringlib/eq.h

Tests for this were added in #19401.
---
 mypyc/irbuild/ll_builder.py              | 12 +++-
 mypyc/lib-rt/CPy.h                       |  1 +
 mypyc/lib-rt/str_ops.c                   | 16 +++++
 mypyc/primitives/str_ops.py              | 10 ++++
 mypyc/test-data/irbuild-dict.test        | 31 +++-------
 mypyc/test-data/irbuild-str.test         | 38 +++---------
 mypyc/test-data/irbuild-unreachable.test | 76 ++++++++----------------
 7 files changed, 78 insertions(+), 106 deletions(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index be4178a4a71a..e25079c1146b 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -175,7 +175,12 @@
     unary_ops,
 )
 from mypyc.primitives.set_ops import new_set_op
-from mypyc.primitives.str_ops import str_check_if_true, str_ssize_t_size_op, unicode_compare
+from mypyc.primitives.str_ops import (
+    str_check_if_true,
+    str_eq,
+    str_ssize_t_size_op,
+    unicode_compare,
+)
 from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op
 from mypyc.rt_subtype import is_runtime_subtype
 from mypyc.sametype import is_same_type
@@ -1471,6 +1476,11 @@ def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) -
 
     def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value:
         """Compare two strings"""
+        if op == "==":
+            return self.primitive_op(str_eq, [lhs, rhs], line)
+        elif op == "!=":
+            eq = self.primitive_op(str_eq, [lhs, rhs], line)
+            return self.add(ComparisonOp(eq, self.false(), ComparisonOp.EQ, line))
         compare_result = self.call_c(unicode_compare, [lhs, rhs], line)
         error_constant = Integer(-1, c_int_rprimitive, line)
         compare_error_check = self.add(
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index bdf3e0130a4c..a0f1b06cc0d5 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -726,6 +726,7 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) {
 #define RIGHTSTRIP 1
 #define BOTHSTRIP  2
 
+char CPyStr_Equal(PyObject *str1, PyObject *str2);
 PyObject *CPyStr_Build(Py_ssize_t len, ...);
 PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index);
 CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction);
diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c
index 210172c57497..5fd376f21cfa 100644
--- a/mypyc/lib-rt/str_ops.c
+++ b/mypyc/lib-rt/str_ops.c
@@ -64,6 +64,22 @@ make_bloom_mask(int kind, const void* ptr, Py_ssize_t len)
 #undef BLOOM_UPDATE
 }
 
+// Adapted from CPython 3.13.1 (_PyUnicode_Equal)
+char CPyStr_Equal(PyObject *str1, PyObject *str2) {
+    if (str1 == str2) {
+        return 1;
+    }
+    Py_ssize_t len = PyUnicode_GET_LENGTH(str1);
+    if (PyUnicode_GET_LENGTH(str2) != len)
+        return 0;
+    int kind = PyUnicode_KIND(str1);
+    if (PyUnicode_KIND(str2) != kind)
+        return 0;
+    const void *data1 = PyUnicode_DATA(str1);
+    const void *data2 = PyUnicode_DATA(str2);
+    return memcmp(data1, data2, len * kind) == 0;
+}
+
 PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) {
     if (PyUnicode_READY(str) != -1) {
         if (CPyTagged_CheckShort(index)) {
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index 9d46da9c3514..37dbdf21bb5d 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -21,6 +21,7 @@
     ERR_NEG_INT,
     binary_op,
     custom_op,
+    custom_primitive_op,
     function_op,
     load_address_op,
     method_op,
@@ -69,6 +70,15 @@
     steals=[True, False],
 )
 
+# str1 == str2 (very common operation, so we provide our own)
+str_eq = custom_primitive_op(
+    name="str_eq",
+    c_function_name="CPyStr_Equal",
+    arg_types=[str_rprimitive, str_rprimitive],
+    return_type=bool_rprimitive,
+    error_kind=ERR_NEVER,
+)
+
 unicode_compare = custom_op(
     arg_types=[str_rprimitive, str_rprimitive],
     return_type=c_int_rprimitive,
diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test
index a71f5aa2d8a2..cacb14dae273 100644
--- a/mypyc/test-data/irbuild-dict.test
+++ b/mypyc/test-data/irbuild-dict.test
@@ -399,12 +399,9 @@ def typeddict(d):
     r9, k :: str
     v :: object
     r10 :: str
-    r11 :: i32
-    r12 :: bit
-    r13 :: object
-    r14, r15, r16 :: bit
+    r11 :: bool
     name :: object
-    r17, r18 :: bit
+    r12, r13 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d)
@@ -415,7 +412,7 @@ L1:
     r5 = r4[1]
     r0 = r5
     r6 = r4[0]
-    if r6 goto L2 else goto L9 :: bool
+    if r6 goto L2 else goto L6 :: bool
 L2:
     r7 = r4[2]
     r8 = r4[3]
@@ -423,27 +420,17 @@ L2:
     k = r9
     v = r8
     r10 = 'name'
-    r11 = PyUnicode_Compare(k, r10)
-    r12 = r11 == -1
-    if r12 goto L3 else goto L5 :: bool
+    r11 = CPyStr_Equal(k, r10)
+    if r11 goto L3 else goto L4 :: bool
 L3:
-    r13 = PyErr_Occurred()
-    r14 = r13 != 0
-    if r14 goto L4 else goto L5 :: bool
+    name = v
 L4:
-    r15 = CPy_KeepPropagating()
 L5:
-    r16 = r11 == 0
-    if r16 goto L6 else goto L7 :: bool
+    r12 = CPyDict_CheckSize(d, r2)
+    goto L1
 L6:
-    name = v
+    r13 = CPy_NoErrOccurred()
 L7:
-L8:
-    r17 = CPyDict_CheckSize(d, r2)
-    goto L1
-L9:
-    r18 = CPy_NoErrOccurred()
-L10:
     return 1
 
 [case testDictLoadAddress]
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index 2bf77a6cb556..4a4992d41a5d 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -65,42 +65,18 @@ def neq(x: str, y: str) -> bool:
 [out]
 def eq(x, y):
     x, y :: str
-    r0 :: i32
-    r1 :: bit
-    r2 :: object
-    r3, r4, r5 :: bit
+    r0 :: bool
 L0:
-    r0 = PyUnicode_Compare(x, y)
-    r1 = r0 == -1
-    if r1 goto L1 else goto L3 :: bool
-L1:
-    r2 = PyErr_Occurred()
-    r3 = r2 != 0
-    if r3 goto L2 else goto L3 :: bool
-L2:
-    r4 = CPy_KeepPropagating()
-L3:
-    r5 = r0 == 0
-    return r5
+    r0 = CPyStr_Equal(x, y)
+    return r0
 def neq(x, y):
     x, y :: str
-    r0 :: i32
+    r0 :: bool
     r1 :: bit
-    r2 :: object
-    r3, r4, r5 :: bit
 L0:
-    r0 = PyUnicode_Compare(x, y)
-    r1 = r0 == -1
-    if r1 goto L1 else goto L3 :: bool
-L1:
-    r2 = PyErr_Occurred()
-    r3 = r2 != 0
-    if r3 goto L2 else goto L3 :: bool
-L2:
-    r4 = CPy_KeepPropagating()
-L3:
-    r5 = r0 != 0
-    return r5
+    r0 = CPyStr_Equal(x, y)
+    r1 = r0 == 0
+    return r1
 
 [case testStrReplace]
 from typing import Optional
diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test
index cebd4582923b..a4f1ef8c7dba 100644
--- a/mypyc/test-data/irbuild-unreachable.test
+++ b/mypyc/test-data/irbuild-unreachable.test
@@ -11,41 +11,27 @@ def f():
     r1 :: str
     r2 :: object
     r3, r4 :: str
-    r5 :: i32
-    r6 :: bit
-    r7 :: object
-    r8, r9, r10 :: bit
-    r11, r12 :: bool
-    r13 :: object
-    r14, y :: bool
+    r5, r6, r7 :: bool
+    r8 :: object
+    r9, y :: bool
 L0:
     r0 = sys :: module
     r1 = 'platform'
     r2 = CPyObject_GetAttr(r0, r1)
     r3 = cast(str, r2)
     r4 = 'x'
-    r5 = PyUnicode_Compare(r3, r4)
-    r6 = r5 == -1
-    if r6 goto L1 else goto L3 :: bool
+    r5 = CPyStr_Equal(r3, r4)
+    if r5 goto L2 else goto L1 :: bool
 L1:
-    r7 = PyErr_Occurred()
-    r8 = r7 != 0
-    if r8 goto L2 else goto L3 :: bool
+    r6 = r5
+    goto L3
 L2:
-    r9 = CPy_KeepPropagating()
+    r7 = raise RuntimeError('mypyc internal error: should be unreachable')
+    r8 = box(None, 1)
+    r9 = unbox(bool, r8)
+    r6 = r9
 L3:
-    r10 = r5 == 0
-    if r10 goto L5 else goto L4 :: bool
-L4:
-    r11 = r10
-    goto L6
-L5:
-    r12 = raise RuntimeError('mypyc internal error: should be unreachable')
-    r13 = box(None, 1)
-    r14 = unbox(bool, r13)
-    r11 = r14
-L6:
-    y = r11
+    y = r6
     return 1
 
 [case testUnreachableNameExpr]
@@ -59,41 +45,27 @@ def f():
     r1 :: str
     r2 :: object
     r3, r4 :: str
-    r5 :: i32
-    r6 :: bit
-    r7 :: object
-    r8, r9, r10 :: bit
-    r11, r12 :: bool
-    r13 :: object
-    r14, y :: bool
+    r5, r6, r7 :: bool
+    r8 :: object
+    r9, y :: bool
 L0:
     r0 = sys :: module
     r1 = 'platform'
     r2 = CPyObject_GetAttr(r0, r1)
     r3 = cast(str, r2)
     r4 = 'x'
-    r5 = PyUnicode_Compare(r3, r4)
-    r6 = r5 == -1
-    if r6 goto L1 else goto L3 :: bool
+    r5 = CPyStr_Equal(r3, r4)
+    if r5 goto L2 else goto L1 :: bool
 L1:
-    r7 = PyErr_Occurred()
-    r8 = r7 != 0
-    if r8 goto L2 else goto L3 :: bool
+    r6 = r5
+    goto L3
 L2:
-    r9 = CPy_KeepPropagating()
+    r7 = raise RuntimeError('mypyc internal error: should be unreachable')
+    r8 = box(None, 1)
+    r9 = unbox(bool, r8)
+    r6 = r9
 L3:
-    r10 = r5 == 0
-    if r10 goto L5 else goto L4 :: bool
-L4:
-    r11 = r10
-    goto L6
-L5:
-    r12 = raise RuntimeError('mypyc internal error: should be unreachable')
-    r13 = box(None, 1)
-    r14 = unbox(bool, r13)
-    r11 = r14
-L6:
-    y = r11
+    y = r6
     return 1
 
 [case testUnreachableStatementAfterReturn]

From 930a379f05a74745302eebdbcdd9670f8f323373 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Wed, 9 Jul 2025 11:28:47 +0200
Subject: [PATCH 1442/1617] [mypyc] Add is_bool_or_bit_rprimitive (#19406)

Added a wrapper to check if a type is either a bool or bit primitive as
these two checks are often done together.

The wrapper should help in preventing suboptimal code generation if one
forgets to check for the bit primitive in cases when it can be trivially
expanded to bool. One such case was in translation of binary ops, which
is fixed in this PR.

Example code:
```
 def f(a: float, b: float, c: float) -> bool:
     return (a == b) & (a == c)
```

IR before:
```
def f(a, b, c):
    a, b, c :: float
    r0, r1 :: bit
    r2 :: bool
    r3 :: int
    r4 :: bool
    r5, r6 :: int
    r7 :: object
    r8, r9 :: bool
L0:
    r0 = a == b
    r1 = a == c
    r2 = r0 << 1
    r3 = extend r2: builtins.bool to builtins.int
    r4 = r1 << 1
    r5 = extend r4: builtins.bool to builtins.int
    r6 = CPyTagged_And(r3, r5)
    dec_ref r3 :: int
    dec_ref r5 :: int
    r7 = box(int, r6)
    r8 = unbox(bool, r7)
    dec_ref r7
    if is_error(r8) goto L2 (error at f:2) else goto L1
L1:
    return r8
L2:
    r9 =  :: bool
    return r9
```

IR after:
```
def f(a, b, c):
    a, b, c :: float
    r0, r1 :: bit
    r2 :: bool
L0:
    r0 = a == b
    r1 = a == c
    r2 = r0 & r1
    return r2
```
---
 mypyc/codegen/emit.py             | 12 +++-----
 mypyc/ir/ops.py                   |  9 ++----
 mypyc/ir/rtypes.py                |  4 +++
 mypyc/irbuild/ll_builder.py       | 33 ++++++++++----------
 mypyc/test-data/irbuild-bool.test | 51 +++++++++++++++++++++++++++++++
 5 files changed, 78 insertions(+), 31 deletions(-)

diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py
index ba8b8307e1fd..f27a8668142d 100644
--- a/mypyc/codegen/emit.py
+++ b/mypyc/codegen/emit.py
@@ -28,8 +28,7 @@
     RType,
     RUnion,
     int_rprimitive,
-    is_bit_rprimitive,
-    is_bool_rprimitive,
+    is_bool_or_bit_rprimitive,
     is_bytes_rprimitive,
     is_dict_rprimitive,
     is_fixed_width_rtype,
@@ -615,8 +614,7 @@ def emit_cast(
             or is_range_rprimitive(typ)
             or is_float_rprimitive(typ)
             or is_int_rprimitive(typ)
-            or is_bool_rprimitive(typ)
-            or is_bit_rprimitive(typ)
+            or is_bool_or_bit_rprimitive(typ)
             or is_fixed_width_rtype(typ)
         ):
             if declare_dest:
@@ -638,7 +636,7 @@ def emit_cast(
             elif is_int_rprimitive(typ) or is_fixed_width_rtype(typ):
                 # TODO: Range check for fixed-width types?
                 prefix = "PyLong"
-            elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
+            elif is_bool_or_bit_rprimitive(typ):
                 prefix = "PyBool"
             else:
                 assert False, f"unexpected primitive type: {typ}"
@@ -889,7 +887,7 @@ def emit_unbox(
             self.emit_line("else {")
             self.emit_line(failure)
             self.emit_line("}")
-        elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
+        elif is_bool_or_bit_rprimitive(typ):
             # Whether we are borrowing or not makes no difference.
             if declare_dest:
                 self.emit_line(f"char {dest};")
@@ -1015,7 +1013,7 @@ def emit_box(
         if is_int_rprimitive(typ) or is_short_int_rprimitive(typ):
             # Steal the existing reference if it exists.
             self.emit_line(f"{declaration}{dest} = CPyTagged_StealAsObject({src});")
-        elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
+        elif is_bool_or_bit_rprimitive(typ):
             # N.B: bool is special cased to produce a borrowed value
             # after boxing, so we don't need to increment the refcount
             # when this comes directly from a Box op.
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index e15d494c2c57..f362b0cca197 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -42,8 +42,7 @@ class to enable the new behavior. Sometimes adding a new abstract
     cstring_rprimitive,
     float_rprimitive,
     int_rprimitive,
-    is_bit_rprimitive,
-    is_bool_rprimitive,
+    is_bool_or_bit_rprimitive,
     is_int_rprimitive,
     is_none_rprimitive,
     is_pointer_rprimitive,
@@ -1089,11 +1088,7 @@ def __init__(self, src: Value, line: int = -1) -> None:
         self.src = src
         self.type = object_rprimitive
         # When we box None and bool values, we produce a borrowed result
-        if (
-            is_none_rprimitive(self.src.type)
-            or is_bool_rprimitive(self.src.type)
-            or is_bit_rprimitive(self.src.type)
-        ):
+        if is_none_rprimitive(self.src.type) or is_bool_or_bit_rprimitive(self.src.type):
             self.is_borrowed = True
 
     def sources(self) -> list[Value]:
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 8dc7d5c9c949..c0871bba258c 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -582,6 +582,10 @@ def is_bit_rprimitive(rtype: RType) -> bool:
     return isinstance(rtype, RPrimitive) and rtype.name == "bit"
 
 
+def is_bool_or_bit_rprimitive(rtype: RType) -> bool:
+    return is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype)
+
+
 def is_object_rprimitive(rtype: RType) -> bool:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.object"
 
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index e25079c1146b..a97bc52bc7e4 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -93,8 +93,7 @@
     dict_rprimitive,
     float_rprimitive,
     int_rprimitive,
-    is_bit_rprimitive,
-    is_bool_rprimitive,
+    is_bool_or_bit_rprimitive,
     is_bytes_rprimitive,
     is_dict_rprimitive,
     is_fixed_width_rtype,
@@ -381,16 +380,12 @@ def coerce(
             ):
                 # Equivalent types
                 return src
-            elif (is_bool_rprimitive(src_type) or is_bit_rprimitive(src_type)) and is_tagged(
-                target_type
-            ):
+            elif is_bool_or_bit_rprimitive(src_type) and is_tagged(target_type):
                 shifted = self.int_op(
                     bool_rprimitive, src, Integer(1, bool_rprimitive), IntOp.LEFT_SHIFT
                 )
                 return self.add(Extend(shifted, target_type, signed=False))
-            elif (
-                is_bool_rprimitive(src_type) or is_bit_rprimitive(src_type)
-            ) and is_fixed_width_rtype(target_type):
+            elif is_bool_or_bit_rprimitive(src_type) and is_fixed_width_rtype(target_type):
                 return self.add(Extend(src, target_type, signed=False))
             elif isinstance(src, Integer) and is_float_rprimitive(target_type):
                 if is_tagged(src_type):
@@ -1341,7 +1336,11 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
             return self.compare_strings(lreg, rreg, op, line)
         if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype) and op in ("==", "!="):
             return self.compare_bytes(lreg, rreg, op, line)
-        if is_bool_rprimitive(ltype) and is_bool_rprimitive(rtype) and op in BOOL_BINARY_OPS:
+        if (
+            is_bool_or_bit_rprimitive(ltype)
+            and is_bool_or_bit_rprimitive(rtype)
+            and op in BOOL_BINARY_OPS
+        ):
             if op in ComparisonOp.signed_ops:
                 return self.bool_comparison_op(lreg, rreg, op, line)
             else:
@@ -1355,7 +1354,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
                     op_id = int_op_to_id[op]
                 else:
                     op_id = IntOp.DIV
-                if is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype):
+                if is_bool_or_bit_rprimitive(rtype):
                     rreg = self.coerce(rreg, ltype, line)
                     rtype = ltype
                 if is_fixed_width_rtype(rtype) or is_tagged(rtype):
@@ -1367,7 +1366,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
             elif op in ComparisonOp.signed_ops:
                 if is_int_rprimitive(rtype):
                     rreg = self.coerce_int_to_fixed_width(rreg, ltype, line)
-                elif is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype):
+                elif is_bool_or_bit_rprimitive(rtype):
                     rreg = self.coerce(rreg, ltype, line)
                 op_id = ComparisonOp.signed_ops[op]
                 if is_fixed_width_rtype(rreg.type):
@@ -1387,13 +1386,13 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
                     )
                 if is_tagged(ltype):
                     return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line)
-                if is_bool_rprimitive(ltype) or is_bit_rprimitive(ltype):
+                if is_bool_or_bit_rprimitive(ltype):
                     lreg = self.coerce(lreg, rtype, line)
                     return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line)
             elif op in ComparisonOp.signed_ops:
                 if is_int_rprimitive(ltype):
                     lreg = self.coerce_int_to_fixed_width(lreg, rtype, line)
-                elif is_bool_rprimitive(ltype) or is_bit_rprimitive(ltype):
+                elif is_bool_or_bit_rprimitive(ltype):
                     lreg = self.coerce(lreg, rtype, line)
                 op_id = ComparisonOp.signed_ops[op]
                 if isinstance(lreg, Integer):
@@ -1544,7 +1543,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
             compare = self.binary_op(lhs_item, rhs_item, op, line)
             # Cast to bool if necessary since most types uses comparison returning a object type
             # See generic_ops.py for more information
-            if not (is_bool_rprimitive(compare.type) or is_bit_rprimitive(compare.type)):
+            if not is_bool_or_bit_rprimitive(compare.type):
                 compare = self.primitive_op(bool_op, [compare], line)
             if i < len(lhs.type.types) - 1:
                 branch = Branch(compare, early_stop, check_blocks[i + 1], Branch.BOOL)
@@ -1563,7 +1562,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
 
     def translate_instance_contains(self, inst: Value, item: Value, op: str, line: int) -> Value:
         res = self.gen_method_call(inst, "__contains__", [item], None, line)
-        if not is_bool_rprimitive(res.type):
+        if not is_bool_or_bit_rprimitive(res.type):
             res = self.primitive_op(bool_op, [res], line)
         if op == "not in":
             res = self.bool_bitwise_op(res, Integer(1, rtype=bool_rprimitive), "^", line)
@@ -1590,7 +1589,7 @@ def unary_not(self, value: Value, line: int) -> Value:
 
     def unary_op(self, value: Value, expr_op: str, line: int) -> Value:
         typ = value.type
-        if is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
+        if is_bool_or_bit_rprimitive(typ):
             if expr_op == "not":
                 return self.unary_not(value, line)
             if expr_op == "+":
@@ -1748,7 +1747,7 @@ def bool_value(self, value: Value) -> Value:
 
         The result type can be bit_rprimitive or bool_rprimitive.
         """
-        if is_bool_rprimitive(value.type) or is_bit_rprimitive(value.type):
+        if is_bool_or_bit_rprimitive(value.type):
             result = value
         elif is_runtime_subtype(value.type, int_rprimitive):
             zero = Integer(0, short_int_rprimitive)
diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test
index 128266e6b1d7..9810daf487fa 100644
--- a/mypyc/test-data/irbuild-bool.test
+++ b/mypyc/test-data/irbuild-bool.test
@@ -422,3 +422,54 @@ L0:
     r1 = extend r0: builtins.bool to builtins.int
     x = r1
     return x
+
+[case testBitToBoolPromotion]
+def bitand(x: float, y: float, z: float) -> bool:
+    b = (x == y) & (x == z)
+    return b
+def bitor(x: float, y: float, z: float) -> bool:
+    b = (x == y) | (x == z)
+    return b
+def bitxor(x: float, y: float, z: float) -> bool:
+    b = (x == y) ^ (x == z)
+    return b
+def invert(x: float, y: float) -> bool:
+    return not(x == y)
+[out]
+def bitand(x, y, z):
+    x, y, z :: float
+    r0, r1 :: bit
+    r2, b :: bool
+L0:
+    r0 = x == y
+    r1 = x == z
+    r2 = r0 & r1
+    b = r2
+    return b
+def bitor(x, y, z):
+    x, y, z :: float
+    r0, r1 :: bit
+    r2, b :: bool
+L0:
+    r0 = x == y
+    r1 = x == z
+    r2 = r0 | r1
+    b = r2
+    return b
+def bitxor(x, y, z):
+    x, y, z :: float
+    r0, r1 :: bit
+    r2, b :: bool
+L0:
+    r0 = x == y
+    r1 = x == z
+    r2 = r0 ^ r1
+    b = r2
+    return b
+def invert(x, y):
+    x, y :: float
+    r0, r1 :: bit
+L0:
+    r0 = x == y
+    r1 = r0 ^ 1
+    return r1

From 35d8c697f7021a1dc5677fe9d507962f4f00ec84 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Wed, 9 Jul 2025 11:30:10 +0200
Subject: [PATCH 1443/1617] [mypyc] Report error on reserved method name
 (#19407)

Changed mypyc to report an error if a method of a compiled class is
called `__mypyc_generator_helper__`. The name should be reserved because
it might clash with an auto-generated method.
---
 mypyc/irbuild/classdef.py            |  9 +++++++++
 mypyc/irbuild/generator.py           |  5 +++--
 mypyc/irbuild/prepare.py             |  4 +++-
 mypyc/irbuild/statement.py           |  3 ++-
 mypyc/test-data/irbuild-classes.test | 25 +++++++++++++++++++++++++
 5 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 13121707773a..30020aabb310 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -64,6 +64,7 @@
     handle_non_ext_method,
     load_type,
 )
+from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
 from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator
 from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op
 from mypyc.primitives.generic_ops import (
@@ -135,6 +136,14 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
         cls_builder = NonExtClassBuilder(builder, cdef)
 
     for stmt in cdef.defs.body:
+        if (
+            isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef))
+            and stmt.name == GENERATOR_HELPER_NAME
+        ):
+            builder.error(
+                f'Method name "{stmt.name}" is reserved for mypyc internal use', stmt.line
+            )
+
         if isinstance(stmt, OverloadedFuncDef) and stmt.is_property:
             if isinstance(cls_builder, NonExtClassBuilder):
                 # properties with both getters and setters in non_extension
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index ae45aed2fc67..545a9daf956b 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -50,6 +50,7 @@
     setup_func_for_recursive_call,
 )
 from mypyc.irbuild.nonlocalcontrol import ExceptNonlocalControl
+from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
 from mypyc.primitives.exc_ops import (
     error_catch_op,
     exc_matches_op,
@@ -236,11 +237,11 @@ def add_helper_to_generator_class(
     builder: IRBuilder, arg_regs: list[Register], blocks: list[BasicBlock], fn_info: FuncInfo
 ) -> FuncDecl:
     """Generates a helper method for a generator class, called by '__next__' and 'throw'."""
-    helper_fn_decl = fn_info.generator_class.ir.method_decls["__mypyc_generator_helper__"]
+    helper_fn_decl = fn_info.generator_class.ir.method_decls[GENERATOR_HELPER_NAME]
     helper_fn_ir = FuncIR(
         helper_fn_decl, arg_regs, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name
     )
-    fn_info.generator_class.ir.methods["__mypyc_generator_helper__"] = helper_fn_ir
+    fn_info.generator_class.ir.methods[GENERATOR_HELPER_NAME] = helper_fn_ir
     builder.functions.append(helper_fn_ir)
     fn_info.env_class.env_user_function = helper_fn_ir
 
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index d4ec814372cd..c22101ac193d 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -71,6 +71,8 @@
 from mypyc.options import CompilerOptions
 from mypyc.sametype import is_same_type
 
+GENERATOR_HELPER_NAME = "__mypyc_generator_helper__"
+
 
 def build_type_map(
     mapper: Mapper,
@@ -229,7 +231,7 @@ def create_generator_class_if_needed(
 
         # The implementation of most generator functionality is behind this magic method.
         helper_fn_decl = FuncDecl(
-            "__mypyc_generator_helper__", name, module_name, helper_sig, internal=True
+            GENERATOR_HELPER_NAME, name, module_name, helper_sig, internal=True
         )
         cir.method_decls[helper_fn_decl.name] = helper_fn_decl
 
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index 5f75a60d8d0a..c8091c0313d0 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -90,6 +90,7 @@
     FinallyNonlocalControl,
     TryFinallyNonlocalControl,
 )
+from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
 from mypyc.irbuild.targets import (
     AssignmentTarget,
     AssignmentTargetAttr,
@@ -933,7 +934,7 @@ def emit_yield_from_or_await(
     to_yield_reg = Register(object_rprimitive)
     received_reg = Register(object_rprimitive)
 
-    helper_method = "__mypyc_generator_helper__"
+    helper_method = GENERATOR_HELPER_NAME
     if (
         isinstance(val, (Call, MethodCall))
         and isinstance(val.type, RInstance)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index fa4708f02e0b..1543568fccad 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1383,3 +1383,28 @@ class M(type):  # E: Inheriting from most builtin types is unimplemented \
 @mypyc_attr(native_class=True)
 class A(metaclass=M):  # E: Class is marked as native_class=True but it can't be a native class. Classes with a metaclass other than ABCMeta, TypingMeta or GenericMeta can't be native classes.
     pass
+
+[case testReservedName]
+from typing import Any, overload
+
+def decorator(cls):
+    return cls
+
+class TestMethod:
+    def __mypyc_generator_helper__(self) -> None:  # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use
+        pass
+
+class TestDecorator:
+    @decorator  # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use
+    def __mypyc_generator_helper__(self) -> None:
+        pass
+
+class TestOverload:
+    @overload # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use
+    def __mypyc_generator_helper__(self, x: int) -> int: ...
+
+    @overload
+    def __mypyc_generator_helper__(self, x: str) -> str: ...
+
+    def __mypyc_generator_helper__(self, x: Any) -> Any:
+        return x

From a79e85e3958a1ee751a2287a6a8aaa4e77845679 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Wed, 9 Jul 2025 11:27:01 -0400
Subject: [PATCH 1444/1617] feat: add helpful info to internal AssertionError
 excs (#19404)

I added some contextual information to various assert statements
throughout the repo.

This information is helpful for me while debugging [Issue
1116](https://github.com/mypyc/mypyc/issues/1116) and should be
similarly useful in many other debugging contexts.
---
 mypy/checker.py                  |  4 ++--
 mypy/checkpattern.py             |  4 ++--
 mypy/plugins/attrs.py            |  2 +-
 mypy/plugins/dataclasses.py      |  2 +-
 mypy/semanal.py                  |  2 +-
 mypy/semanal_main.py             |  2 +-
 mypyc/analysis/attrdefined.py    |  4 ++--
 mypyc/analysis/selfleaks.py      |  2 +-
 mypyc/codegen/emit.py            |  4 ++--
 mypyc/codegen/emitfunc.py        |  8 ++++----
 mypyc/irbuild/builder.py         | 10 +++++-----
 mypyc/irbuild/classdef.py        |  8 ++++----
 mypyc/irbuild/expression.py      |  4 ++--
 mypyc/irbuild/for_helpers.py     |  2 +-
 mypyc/irbuild/function.py        | 19 ++++++++-----------
 mypyc/irbuild/generator.py       |  2 +-
 mypyc/irbuild/ll_builder.py      |  9 +++++----
 mypyc/irbuild/match.py           |  4 ++--
 mypyc/irbuild/nonlocalcontrol.py |  2 +-
 mypyc/irbuild/statement.py       |  2 +-
 mypyc/lower/list_ops.py          |  2 +-
 mypyc/transform/ir_transform.py  |  2 +-
 mypyc/transform/refcount.py      |  2 +-
 23 files changed, 50 insertions(+), 52 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 225a50c7e646..edd9519da4f8 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -7273,7 +7273,7 @@ def named_type(self, name: str) -> Instance:
         if isinstance(node, TypeAlias):
             assert isinstance(node.target, Instance)  # type: ignore[misc]
             node = node.target.type
-        assert isinstance(node, TypeInfo)
+        assert isinstance(node, TypeInfo), node
         any_type = AnyType(TypeOfAny.from_omitted_generics)
         return Instance(node, [any_type] * len(node.defn.type_vars))
 
@@ -7292,7 +7292,7 @@ def lookup_typeinfo(self, fullname: str) -> TypeInfo:
         # Assume that the name refers to a class.
         sym = self.lookup_qualified(fullname)
         node = sym.node
-        assert isinstance(node, TypeInfo)
+        assert isinstance(node, TypeInfo), node
         return node
 
     def type_type(self) -> Instance:
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 4cf7c1ca7862..48840466f0d8 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -796,9 +796,9 @@ def get_var(expr: Expression) -> Var:
     Warning: this in only true for expressions captured by a match statement.
     Don't call it from anywhere else
     """
-    assert isinstance(expr, NameExpr)
+    assert isinstance(expr, NameExpr), expr
     node = expr.node
-    assert isinstance(node, Var)
+    assert isinstance(node, Var), node
     return node
 
 
diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py
index b7b3821576ea..47c6ad9f305a 100644
--- a/mypy/plugins/attrs.py
+++ b/mypy/plugins/attrs.py
@@ -458,7 +458,7 @@ def _analyze_class(
             if isinstance(node, PlaceholderNode):
                 # This node is not ready yet.
                 continue
-            assert isinstance(node, Var)
+            assert isinstance(node, Var), node
             node.is_initialized_in_class = False
 
     # Traverse the MRO and collect attributes from the parents.
diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index 99d4ef56a540..ee6f8889b894 100644
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -610,7 +610,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
                 # We will issue an error later.
                 continue
 
-            assert isinstance(node, Var)
+            assert isinstance(node, Var), node
 
             # x: ClassVar[int] is ignored by dataclasses.
             if node.is_classvar:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 435c1e682e35..01b7f4989d80 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -6630,7 +6630,7 @@ def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
         sym = self.lookup_fully_qualified(fullname)
         assert sym, "Internal error: attempted to construct unknown type"
         node = sym.node
-        assert isinstance(node, TypeInfo)
+        assert isinstance(node, TypeInfo), node
         if args:
             # TODO: assert len(args) == len(node.defn.type_vars)
             return Instance(node, args)
diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py
index 00d795c64e44..7301e9f9b9b3 100644
--- a/mypy/semanal_main.py
+++ b/mypy/semanal_main.py
@@ -290,7 +290,7 @@ def process_functions(graph: Graph, scc: list[str], patches: Patches) -> None:
 
     for module, target, node, active_type in order_by_subclassing(all_targets):
         analyzer = graph[module].manager.semantic_analyzer
-        assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator))
+        assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)), node
         process_top_level_function(
             analyzer, graph[module], module, target, node, active_type, patches
         )
diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py
index 896527bdcf14..4fd0017257a0 100644
--- a/mypyc/analysis/attrdefined.py
+++ b/mypyc/analysis/attrdefined.py
@@ -285,7 +285,7 @@ def mark_attr_initialization_ops(
 def attributes_initialized_by_init_call(op: Call) -> set[str]:
     """Calculate attributes that are always initialized by a super().__init__ call."""
     self_type = op.fn.sig.args[0].type
-    assert isinstance(self_type, RInstance)
+    assert isinstance(self_type, RInstance), self_type
     cl = self_type.class_ir
     return {a for base in cl.mro for a in base.attributes if base.is_always_defined(a)}
 
@@ -293,7 +293,7 @@ def attributes_initialized_by_init_call(op: Call) -> set[str]:
 def attributes_maybe_initialized_by_init_call(op: Call) -> set[str]:
     """Calculate attributes that may be initialized by a super().__init__ call."""
     self_type = op.fn.sig.args[0].type
-    assert isinstance(self_type, RInstance)
+    assert isinstance(self_type, RInstance), self_type
     cl = self_type.class_ir
     return attributes_initialized_by_init_call(op) | cl._sometimes_initialized_attrs
 
diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py
index 4d3a7c87c5d1..9f7e00db78d2 100644
--- a/mypyc/analysis/selfleaks.py
+++ b/mypyc/analysis/selfleaks.py
@@ -92,7 +92,7 @@ def visit_call(self, op: Call) -> GenAndKill:
         fn = op.fn
         if fn.class_name and fn.name == "__init__":
             self_type = op.fn.sig.args[0].type
-            assert isinstance(self_type, RInstance)
+            assert isinstance(self_type, RInstance), self_type
             cl = self_type.class_ir
             if not cl.init_self_leak:
                 return CLEAN
diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py
index f27a8668142d..8c4a69cfa3cb 100644
--- a/mypyc/codegen/emit.py
+++ b/mypyc/codegen/emit.py
@@ -742,7 +742,7 @@ def emit_cast_error_handler(
             self.emit_traceback(error.source_path, error.module_name, error.traceback_entry)
             self.emit_line("goto %s;" % error.label)
         else:
-            assert isinstance(error, ReturnHandler)
+            assert isinstance(error, ReturnHandler), error
             self.emit_line("return %s;" % error.value)
 
     def emit_union_cast(
@@ -871,7 +871,7 @@ def emit_unbox(
         elif isinstance(error, GotoHandler):
             failure = "goto %s;" % error.label
         else:
-            assert isinstance(error, ReturnHandler)
+            assert isinstance(error, ReturnHandler), error
             failure = "return %s;" % error.value
         if raise_exception:
             raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); '
diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 4b618f3c67db..3fdd08037d1a 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -160,7 +160,7 @@ def generate_native_function(
     # eliminated during code generation.
     for block in fn.blocks:
         terminator = block.terminator
-        assert isinstance(terminator, ControlOp)
+        assert isinstance(terminator, ControlOp), terminator
 
         for target in terminator.targets():
             is_next_block = target.label == block.label + 1
@@ -309,7 +309,7 @@ def visit_assign(self, op: Assign) -> None:
 
     def visit_assign_multi(self, op: AssignMulti) -> None:
         typ = op.dest.type
-        assert isinstance(typ, RArray)
+        assert isinstance(typ, RArray), typ
         dest = self.reg(op.dest)
         # RArray values can only be assigned to once, so we can always
         # declare them on initialization.
@@ -586,7 +586,7 @@ def visit_method_call(self, op: MethodCall) -> None:
     def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Value]) -> None:
         obj = self.reg(op_obj)
         rtype = op_obj.type
-        assert isinstance(rtype, RInstance)
+        assert isinstance(rtype, RInstance), rtype
         class_ir = rtype.class_ir
         method = rtype.class_ir.get_method(name)
         assert method is not None
@@ -805,7 +805,7 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None:
         dest = self.reg(op)
         src = self.reg(op.src)
         # TODO: support tuple type
-        assert isinstance(op.src_type, RStruct)
+        assert isinstance(op.src_type, RStruct), op.src_type
         assert op.field in op.src_type.names, "Invalid field name."
         self.emit_line(
             "{} = ({})&(({} *){})->{};".format(
diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index daed97cb896d..28ebcf2075fb 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -551,8 +551,8 @@ def init_final_static(
         *,
         type_override: RType | None = None,
     ) -> None:
-        assert isinstance(lvalue, NameExpr)
-        assert isinstance(lvalue.node, Var)
+        assert isinstance(lvalue, NameExpr), lvalue
+        assert isinstance(lvalue.node, Var), lvalue.node
         if lvalue.node.final_value is None:
             if class_name is None:
                 name = lvalue.name
@@ -1271,7 +1271,7 @@ def add_local(self, symbol: SymbolNode, typ: RType, is_arg: bool = False) -> Reg
         Args:
             is_arg: is this a function argument
         """
-        assert isinstance(symbol, SymbolNode)
+        assert isinstance(symbol, SymbolNode), symbol
         reg = Register(
             typ, remangle_redefinition_name(symbol.name), is_arg=is_arg, line=symbol.line
         )
@@ -1286,7 +1286,7 @@ def add_local_reg(
         """Like add_local, but return an assignment target instead of value."""
         self.add_local(symbol, typ, is_arg)
         target = self.symtables[-1][symbol]
-        assert isinstance(target, AssignmentTargetRegister)
+        assert isinstance(target, AssignmentTargetRegister), target
         return target
 
     def add_self_to_env(self, cls: ClassIR) -> AssignmentTargetRegister:
@@ -1446,7 +1446,7 @@ def get_default() -> Value:
                         GetAttr(builder.fn_info.callable_class.self_reg, name, arg.line)
                     )
 
-            assert isinstance(target, AssignmentTargetRegister)
+            assert isinstance(target, AssignmentTargetRegister), target
             reg = target.register
             if not reg.type.error_overlap:
                 builder.assign_if_null(target.register, get_default, arg.initializer.line)
diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 30020aabb310..6b59750c7dec 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -424,7 +424,7 @@ def get_type_annotation(self, stmt: AssignmentStmt) -> TypeInfo | None:
                 type_name = stmt.rvalue.args[index]
                 if isinstance(type_name, NameExpr) and isinstance(type_name.node, TypeInfo):
                     lvalue = stmt.lvalues[0]
-                    assert isinstance(lvalue, NameExpr)
+                    assert isinstance(lvalue, NameExpr), lvalue
                     return type_name.node
         return None
 
@@ -765,7 +765,7 @@ def generate_attr_defaults_init(
         self_var = builder.self()
         for stmt in default_assignments:
             lvalue = stmt.lvalues[0]
-            assert isinstance(lvalue, NameExpr)
+            assert isinstance(lvalue, NameExpr), lvalue
             if not stmt.is_final_def and not is_constant(stmt.rvalue):
                 builder.warning("Unsupported default attribute value", stmt.rvalue.line)
 
@@ -871,7 +871,7 @@ def load_decorated_class(builder: IRBuilder, cdef: ClassDef, type_obj: Value) ->
     dec_class = type_obj
     for d in reversed(decorators):
         decorator = d.accept(builder.visitor)
-        assert isinstance(decorator, Value)
+        assert isinstance(decorator, Value), decorator
         dec_class = builder.py_call(decorator, [dec_class], dec_class.line)
     return dec_class
 
@@ -882,7 +882,7 @@ def cache_class_attrs(
     """Add class attributes to be cached to the global cache."""
     typ = builder.load_native_type_object(cdef.info.fullname)
     for lval, rtype in attrs_to_cache:
-        assert isinstance(lval, NameExpr)
+        assert isinstance(lval, NameExpr), lval
         rval = builder.py_get_attr(typ, lval.name, cdef.line)
         builder.init_final_static(lval, rval, cdef.name, type_override=rtype)
 
diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index c4a3f5f38ce3..990c904dc447 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -359,7 +359,7 @@ def translate_method_call(builder: IRBuilder, expr: CallExpr, callee: MemberExpr
         and all(kind in (ARG_POS, ARG_NAMED) for kind in expr.arg_kinds)
     ):
         # Call a method via the *class*
-        assert isinstance(callee.expr.node, TypeInfo)
+        assert isinstance(callee.expr.node, TypeInfo), callee.expr.node
         ir = builder.mapper.type_to_ir[callee.expr.node]
         return call_classmethod(builder, ir, expr, callee)
     elif builder.is_module_member_expr(callee):
@@ -722,7 +722,7 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value:
             mypy_file = builder.graph["builtins"].tree
             assert mypy_file is not None
             info = mypy_file.names["bool"].node
-            assert isinstance(info, TypeInfo)
+            assert isinstance(info, TypeInfo), info
             bool_type = Instance(info, [])
             exprs = []
             for item in items:
diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index c5b1d1273bef..6066aa615e1b 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -902,7 +902,7 @@ def begin_body(self) -> None:
         value = builder.add(TupleGet(self.next_tuple, 3, line))
 
         # Coerce just in case e.g. key is itself a tuple to be unpacked.
-        assert isinstance(self.target_type, RTuple)
+        assert isinstance(self.target_type, RTuple), self.target_type
         key = builder.coerce(key, self.target_type.types[0], line)
         value = builder.coerce(value, self.target_type.types[1], line)
 
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index dcc5a306bcde..90506adde672 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -136,7 +136,7 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
 
 def transform_lambda_expr(builder: IRBuilder, expr: LambdaExpr) -> Value:
     typ = get_proper_type(builder.types[expr])
-    assert isinstance(typ, CallableType)
+    assert isinstance(typ, CallableType), typ
 
     runtime_args = []
     for arg, arg_type in zip(expr.arguments, typ.arg_types):
@@ -269,7 +269,7 @@ def c() -> None:
         # add the generated main singledispatch function
         builder.functions.append(func_ir)
         # create the dispatch function
-        assert isinstance(fitem, FuncDef)
+        assert isinstance(fitem, FuncDef), fitem
         return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig)
 
     return func_ir, func_reg
@@ -336,8 +336,9 @@ def gen_func_ir(
         add_get_to_callable_class(builder, fn_info)
         func_reg = instantiate_callable_class(builder, fn_info)
     else:
-        assert isinstance(fn_info.fitem, FuncDef)
-        func_decl = builder.mapper.func_to_decl[fn_info.fitem]
+        fitem = fn_info.fitem
+        assert isinstance(fitem, FuncDef), fitem
+        func_decl = builder.mapper.func_to_decl[fitem]
         if fn_info.is_decorated or is_singledispatch_main_func:
             class_name = None if cdef is None else cdef.name
             func_decl = FuncDecl(
@@ -349,13 +350,9 @@ def gen_func_ir(
                 func_decl.is_prop_getter,
                 func_decl.is_prop_setter,
             )
-            func_ir = FuncIR(
-                func_decl, args, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name
-            )
+            func_ir = FuncIR(func_decl, args, blocks, fitem.line, traceback_name=fitem.name)
         else:
-            func_ir = FuncIR(
-                func_decl, args, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name
-            )
+            func_ir = FuncIR(func_decl, args, blocks, fitem.line, traceback_name=fitem.name)
     return (func_ir, func_reg)
 
 
@@ -483,7 +480,7 @@ def load_decorated_func(builder: IRBuilder, fdef: FuncDef, orig_func_reg: Value)
     func_reg = orig_func_reg
     for d in reversed(decorators):
         decorator = d.accept(builder.visitor)
-        assert isinstance(decorator, Value)
+        assert isinstance(decorator, Value), decorator
         func_reg = builder.py_call(decorator, [func_reg], func_reg.line)
     return func_reg
 
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index 545a9daf956b..c858946f33c4 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -158,7 +158,7 @@ def instantiate_generator_class(builder: IRBuilder) -> Value:
 
 def setup_generator_class(builder: IRBuilder) -> ClassIR:
     mapper = builder.mapper
-    assert isinstance(builder.fn_info.fitem, FuncDef)
+    assert isinstance(builder.fn_info.fitem, FuncDef), builder.fn_info.fitem
     generator_class_ir = mapper.fdef_to_generator[builder.fn_info.fitem]
     if builder.fn_info.can_merge_generator_and_env_classes():
         builder.fn_info.env_class = generator_class_ir
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index a97bc52bc7e4..40f1d40b478b 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -432,7 +432,7 @@ def coerce(
 
     def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) -> Value:
         assert is_fixed_width_rtype(target_type), target_type
-        assert isinstance(target_type, RPrimitive)
+        assert isinstance(target_type, RPrimitive), target_type
 
         res = Register(target_type)
 
@@ -538,10 +538,11 @@ def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value:
                 line,
             )
 
-        assert is_fixed_width_rtype(src.type)
-        assert isinstance(src.type, RPrimitive)
         src_type = src.type
 
+        assert is_fixed_width_rtype(src_type), src_type
+        assert isinstance(src_type, RPrimitive), src_type
+
         res = Register(int_rprimitive)
 
         fast, fast2, slow, end = BasicBlock(), BasicBlock(), BasicBlock(), BasicBlock()
@@ -1513,7 +1514,7 @@ def compare_bytes(self, lhs: Value, rhs: Value, op: str, line: int) -> Value:
     def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Value:
         """Compare two tuples item by item"""
         # type cast to pass mypy check
-        assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple)
+        assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple), (lhs.type, rhs.type)
         equal = True if op == "==" else False
         result = Register(bool_rprimitive)
         # tuples of different lengths
diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py
index d7bf9e0b94de..c2ca9cfd32ff 100644
--- a/mypyc/irbuild/match.py
+++ b/mypyc/irbuild/match.py
@@ -151,7 +151,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None:
                 return
 
             node = pattern.class_ref.node
-            assert isinstance(node, TypeInfo)
+            assert isinstance(node, TypeInfo), node
             match_args = extract_dunder_match_args_names(node)
 
             for i, expr in enumerate(pattern.positionals):
@@ -345,7 +345,7 @@ def extract_dunder_match_args_names(info: TypeInfo) -> list[str]:
     ty = info.names.get("__match_args__")
     assert ty
     match_args_type = get_proper_type(ty.type)
-    assert isinstance(match_args_type, TupleType)
+    assert isinstance(match_args_type, TupleType), match_args_type
 
     match_args: list[str] = []
     for item in match_args_type.items:
diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py
index 887f6786718d..4a7136fbd18d 100644
--- a/mypyc/irbuild/nonlocalcontrol.py
+++ b/mypyc/irbuild/nonlocalcontrol.py
@@ -175,7 +175,7 @@ def gen_return(self, builder: IRBuilder, value: Value, line: int) -> None:
                 self.ret_reg = Register(builder.ret_types[-1])
         # assert needed because of apparent mypy bug... it loses track of the union
         # and infers the type as object
-        assert isinstance(self.ret_reg, (Register, AssignmentTarget))
+        assert isinstance(self.ret_reg, (Register, AssignmentTarget)), self.ret_reg
         builder.assign(self.ret_reg, value, line)
 
         builder.add(Goto(self.target))
diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index c8091c0313d0..4362f42b41d2 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -785,7 +785,7 @@ def maybe_natively_call_exit(exc_info: bool) -> Value:
             args = [none, none, none]
 
         if is_native:
-            assert isinstance(mgr_v.type, RInstance)
+            assert isinstance(mgr_v.type, RInstance), mgr_v.type
             exit_val = builder.gen_method_call(
                 builder.read(mgr),
                 f"__{al}exit__",
diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py
index f719a9fcd23d..63a1ecca8d11 100644
--- a/mypyc/lower/list_ops.py
+++ b/mypyc/lower/list_ops.py
@@ -22,7 +22,7 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V
     base = args[0]
     index_value = args[1]
     value = args[2]
-    assert isinstance(index_value, Integer)
+    assert isinstance(index_value, Integer), index_value
     index = index_value.numeric_value()
     if index == 0:
         ptr = base
diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py
index 326a5baca1e7..7834fed39465 100644
--- a/mypyc/transform/ir_transform.py
+++ b/mypyc/transform/ir_transform.py
@@ -357,7 +357,7 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None:
     def visit_load_address(self, op: LoadAddress) -> None:
         if isinstance(op.src, LoadStatic):
             new = self.fix_op(op.src)
-            assert isinstance(new, LoadStatic)
+            assert isinstance(new, LoadStatic), new
             op.src = new
 
     def visit_keep_alive(self, op: KeepAlive) -> None:
diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py
index b2ca03d44630..c589918986f0 100644
--- a/mypyc/transform/refcount.py
+++ b/mypyc/transform/refcount.py
@@ -127,7 +127,7 @@ def transform_block(
                 # For assignments to registers that were already live,
                 # decref the old value.
                 if dest not in pre_borrow[key] and dest in pre_live[key]:
-                    assert isinstance(op, Assign)
+                    assert isinstance(op, Assign), op
                     maybe_append_dec_ref(ops, dest, post_must_defined, key)
 
         # Strip KeepAlive. Its only purpose is to help with this transform.

From 6f23e476b83eafa9dd6f63b02cc1467bbce0ca5b Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 9 Jul 2025 17:04:38 +0100
Subject: [PATCH 1445/1617] [mypyc] Speed up for loop over native generator
 (#19415)

Call the generator helper method directly instead of calling
`PyIter_Next` when calling a native generator from a native function.
This way we can avoid raising StopIteration when the generator is
exhausted. The approach is similar to what I used to speed up calls
using await in #19398. Refer to that PR for a more detailed explanation.

This helps mostly when a generator produces a small number of values,
which is quite common.

This PR improves the performance of this microbenchmark, which is a
close to the ideal use case, by about 2.6x (now 5.7x faster than
interpreted):
```
from typing import Iterator

def foo(x: int) -> Iterator[int]:
    for a in range(x):
        yield a

def bench(n: int) -> None:
    for i in range(n):
        for a in foo(1):
            pass

from time import time
bench(1000 * 1000)
t0 = time()
bench(50 * 1000 * 1000)
print(time() - t0)
```
---
 mypyc/irbuild/for_helpers.py | 76 +++++++++++++++++++++++++++++++++++-
 mypyc/irbuild/prepare.py     |  4 +-
 2 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index 6066aa615e1b..ab90a8c86b28 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -24,12 +24,15 @@
     TypeAlias,
 )
 from mypyc.ir.ops import (
+    ERR_NEVER,
     BasicBlock,
     Branch,
     Integer,
     IntOp,
     LoadAddress,
+    LoadErrorValue,
     LoadMem,
+    MethodCall,
     RaiseStandardError,
     Register,
     TupleGet,
@@ -37,6 +40,7 @@
     Value,
 )
 from mypyc.ir.rtypes import (
+    RInstance,
     RTuple,
     RType,
     bool_rprimitive,
@@ -48,10 +52,13 @@
     is_short_int_rprimitive,
     is_str_rprimitive,
     is_tuple_rprimitive,
+    object_pointer_rprimitive,
+    object_rprimitive,
     pointer_rprimitive,
     short_int_rprimitive,
 )
 from mypyc.irbuild.builder import IRBuilder
+from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
 from mypyc.irbuild.targets import AssignmentTarget, AssignmentTargetTuple
 from mypyc.primitives.dict_ops import (
     dict_check_size_op,
@@ -62,7 +69,7 @@
     dict_next_value_op,
     dict_value_iter_op,
 )
-from mypyc.primitives.exc_ops import no_err_occurred_op
+from mypyc.primitives.exc_ops import no_err_occurred_op, propagate_if_error_op
 from mypyc.primitives.generic_ops import aiter_op, anext_op, iter_op, next_op
 from mypyc.primitives.list_ops import list_append_op, list_get_item_unsafe_op, new_list_set_item_op
 from mypyc.primitives.misc_ops import stop_async_iteration_op
@@ -511,7 +518,15 @@ def make_for_loop_generator(
     # Default to a generic for loop.
     if iterable_expr_reg is None:
         iterable_expr_reg = builder.accept(expr)
-    for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
+
+    it = iterable_expr_reg.type
+    for_obj: ForNativeGenerator | ForIterable
+    if isinstance(it, RInstance) and it.class_ir.has_method(GENERATOR_HELPER_NAME):
+        # Directly call generator object methods if iterating over a native generator.
+        for_obj = ForNativeGenerator(builder, index, body_block, loop_exit, line, nested)
+    else:
+        # Generic implementation that works of arbitrary iterables.
+        for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
     item_type = builder._analyze_iterable_item_type(expr)
     item_rtype = builder.type_to_rtype(item_type)
     for_obj.init(iterable_expr_reg, item_rtype)
@@ -623,6 +638,63 @@ def gen_cleanup(self) -> None:
         self.builder.call_c(no_err_occurred_op, [], self.line)
 
 
+class ForNativeGenerator(ForGenerator):
+    """Generate IR for a for loop over a native generator."""
+
+    def need_cleanup(self) -> bool:
+        # Create a new cleanup block for when the loop is finished.
+        return True
+
+    def init(self, expr_reg: Value, target_type: RType) -> None:
+        # Define target to contains the generator expression. It's also the iterator.
+        # If we are inside a generator function, spill these into the environment class.
+        builder = self.builder
+        self.iter_target = builder.maybe_spill(expr_reg)
+        self.target_type = target_type
+
+    def gen_condition(self) -> None:
+        builder = self.builder
+        line = self.line
+        self.return_value = Register(object_rprimitive)
+        err = builder.add(LoadErrorValue(object_rprimitive, undefines=True))
+        builder.assign(self.return_value, err, line)
+
+        # Call generated generator helper method, passing a PyObject ** as the final
+        # argument that will be used to store the return value in the return value
+        # register. We ignore the return value but the presence of a return value
+        # indicates that the generator has finished. This is faster than raising
+        # and catching StopIteration, which is the non-native way of doing this.
+        ptr = builder.add(LoadAddress(object_pointer_rprimitive, self.return_value))
+        nn = builder.none_object()
+        helper_call = MethodCall(
+            builder.read(self.iter_target), GENERATOR_HELPER_NAME, [nn, nn, nn, nn, ptr], line
+        )
+        # We provide custom handling for error values.
+        helper_call.error_kind = ERR_NEVER
+
+        self.next_reg = builder.add(helper_call)
+        builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR))
+
+    def begin_body(self) -> None:
+        # Assign the value obtained from the generator helper method to the
+        # lvalue so that it can be referenced by code in the body of the loop.
+        builder = self.builder
+        line = self.line
+        # We unbox here so that iterating with tuple unpacking generates a tuple based
+        # unpack instead of an iterator based one.
+        next_reg = builder.coerce(self.next_reg, self.target_type, line)
+        builder.assign(builder.get_assignment_target(self.index), next_reg, line)
+
+    def gen_step(self) -> None:
+        # Nothing to do here, since we get the next item as part of gen_condition().
+        pass
+
+    def gen_cleanup(self) -> None:
+        # If return value is NULL (it wasn't assigned to by the generator helper method),
+        # an exception was raised that we need to propagate.
+        self.builder.primitive_op(propagate_if_error_op, [self.return_value], self.line)
+
+
 class ForAsyncIterable(ForGenerator):
     """Generate IR for an async for loop."""
 
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index c22101ac193d..4eff90f90b7d 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -15,7 +15,7 @@
 
 from collections import defaultdict
 from collections.abc import Iterable
-from typing import NamedTuple
+from typing import Final, NamedTuple
 
 from mypy.build import Graph
 from mypy.nodes import (
@@ -71,7 +71,7 @@
 from mypyc.options import CompilerOptions
 from mypyc.sametype import is_same_type
 
-GENERATOR_HELPER_NAME = "__mypyc_generator_helper__"
+GENERATOR_HELPER_NAME: Final = "__mypyc_generator_helper__"
 
 
 def build_type_map(

From 40277a14740ac020373a9afc25dcd4c1e9325ab7 Mon Sep 17 00:00:00 2001
From: Randolf Scholz 
Date: Wed, 9 Jul 2025 18:38:21 +0200
Subject: [PATCH 1446/1617] More shards for `mypy-primer` (#19405)

Increases the number of shards by 1 which helps with
https://github.com/python/mypy/issues/19403.
---
 .github/workflows/mypy_primer.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml
index ee868484751e..1ff984247fb6 100644
--- a/.github/workflows/mypy_primer.yml
+++ b/.github/workflows/mypy_primer.yml
@@ -28,7 +28,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        shard-index: [0, 1, 2, 3, 4]
+        shard-index: [0, 1, 2, 3, 4, 5]
       fail-fast: false
     timeout-minutes: 60
     steps:
@@ -63,7 +63,7 @@ jobs:
             mypy_primer \
             --repo mypy_to_test \
             --new $GITHUB_SHA --old base_commit \
-            --num-shards 5 --shard-index ${{ matrix.shard-index }} \
+            --num-shards 6 --shard-index ${{ matrix.shard-index }} \
             --debug \
             --additional-flags="--debug-serialize" \
             --output concise \

From 5d59e4340446a5bd187ae9388bb1cb6c706bbb0a Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 9 Jul 2025 18:52:27 +0200
Subject: [PATCH 1447/1617] Support `_value_` as a fallback for ellipsis Enum
 members (#19352)

Fixes #19334.

This does not affect enums with explicit values different from ellipsis
and is limited to enums defined in stub files.
---
 mypy/plugins/enums.py            |  22 +++++-
 mypy/stubtest.py                 |   2 +-
 mypy/types.py                    |   4 +-
 test-data/unit/check-enum.test   | 122 +++++++++++++++++++++++++++----
 test-data/unit/lib-stub/enum.pyi |   5 ++
 5 files changed, 139 insertions(+), 16 deletions(-)

diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py
index dc58fc8110a5..860c56c63570 100644
--- a/mypy/plugins/enums.py
+++ b/mypy/plugins/enums.py
@@ -17,10 +17,12 @@
 from typing import TypeVar, cast
 
 import mypy.plugin  # To avoid circular imports.
-from mypy.nodes import TypeInfo
+from mypy.checker import TypeChecker
+from mypy.nodes import TypeInfo, Var
 from mypy.subtypes import is_equivalent
 from mypy.typeops import fixup_partial_type, make_simplified_union
 from mypy.types import (
+    ELLIPSIS_TYPE_NAMES,
     CallableType,
     Instance,
     LiteralType,
@@ -79,6 +81,19 @@ def _infer_value_type_with_auto_fallback(
     if proper_type is None:
         return None
     proper_type = get_proper_type(fixup_partial_type(proper_type))
+    # Enums in stubs may have ... instead of actual values. If `_value_` is annotated
+    # (manually or inherited from IntEnum, for example), it is a more reasonable guess
+    # than literal ellipsis type.
+    if (
+        _is_defined_in_stub(ctx)
+        and isinstance(proper_type, Instance)
+        and proper_type.type.fullname in ELLIPSIS_TYPE_NAMES
+        and isinstance(ctx.type, Instance)
+    ):
+        value_type = ctx.type.type.get("_value_")
+        if value_type is not None and isinstance(var := value_type.node, Var):
+            return var.type
+        return proper_type
     if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"):
         if is_named_instance(proper_type, "enum.member") and proper_type.args:
             return proper_type.args[0]
@@ -106,6 +121,11 @@ def _infer_value_type_with_auto_fallback(
     return ctx.default_attr_type
 
 
+def _is_defined_in_stub(ctx: mypy.plugin.AttributeContext) -> bool:
+    assert isinstance(ctx.api, TypeChecker)
+    return isinstance(ctx.type, Instance) and ctx.api.modules[ctx.type.type.module_name].is_stub
+
+
 def _implements_new(info: TypeInfo) -> bool:
     """Check whether __new__ comes from enum.Enum or was implemented in a
     subclass. In the latter case, we must infer Any as long as mypy can't infer
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 8ea9d786be22..d16e491fb1ab 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -1149,7 +1149,7 @@ def verify_var(
             proper_type = mypy.types.get_proper_type(stub.type)
             if (
                 isinstance(proper_type, mypy.types.Instance)
-                and proper_type.type.fullname == "builtins.ellipsis"
+                and proper_type.type.fullname in mypy.types.ELLIPSIS_TYPE_NAMES
             ):
                 should_error = False
 
diff --git a/mypy/types.py b/mypy/types.py
index 8ecd2ccf52d9..05b02acc68c0 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -181,6 +181,8 @@
 # Supported @override decorator names.
 OVERRIDE_DECORATOR_NAMES: Final = ("typing.override", "typing_extensions.override")
 
+ELLIPSIS_TYPE_NAMES: Final = ("builtins.ellipsis", "types.EllipsisType")
+
 # A placeholder used for Bogus[...] parameters
 _dummy: Final[Any] = object()
 
@@ -1574,7 +1576,7 @@ def is_singleton_type(self) -> bool:
         return (
             self.type.is_enum
             and len(self.type.enum_members) == 1
-            or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"}
+            or self.type.fullname in ELLIPSIS_TYPE_NAMES
         )
 
 
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index 1ab8109eda75..d034fe1a6f5f 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -618,9 +618,8 @@ reveal_type(B.a)        # N: Revealed type is "Literal[__main__.B.a]?"
 reveal_type(A.x.name)   # N: Revealed type is "Literal['x']?"
 reveal_type(B.a.name)   # N: Revealed type is "Literal['a']?"
 
-# TODO: The revealed type should be 'int' here
-reveal_type(A.x.value)  # N: Revealed type is "Any"
-reveal_type(B.a.value)  # N: Revealed type is "Any"
+reveal_type(A.x.value)  # N: Revealed type is "builtins.int"
+reveal_type(B.a.value)  # N: Revealed type is "builtins.int"
 [builtins fixtures/enum.pyi]
 
 [case testAnonymousFunctionalEnum]
@@ -755,12 +754,10 @@ class B2(IntEnum):
 class B3(IntEnum):
     x = 1
 
-# TODO: getting B1.x._value_ and B2.x._value_ to have type 'int' requires a typeshed change
-
 is_x(reveal_type(B1.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(B1.x._name_))  # N: Revealed type is "Literal['x']"
 reveal_type(B1.x.value)         # N: Revealed type is "builtins.int"
-reveal_type(B1.x._value_)       # N: Revealed type is "Any"
+reveal_type(B1.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(B2.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(B2.x._name_))  # N: Revealed type is "Literal['x']"
 reveal_type(B2.x.value)         # N: Revealed type is "builtins.int"
@@ -770,9 +767,6 @@ is_x(reveal_type(B3.x._name_))  # N: Revealed type is "Literal['x']"
 reveal_type(B3.x.value)         # N: Revealed type is "Literal[1]?"
 reveal_type(B3.x._value_)       # N: Revealed type is "Literal[1]?"
 
-# TODO: C1.x.value and C2.x.value should also be of type 'int'
-# This requires either a typeshed change or a plugin refinement
-
 C1 = IntFlag('C1', 'x')
 class C2(IntFlag):
     x = auto()
@@ -781,8 +775,8 @@ class C3(IntFlag):
 
 is_x(reveal_type(C1.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(C1.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(C1.x.value)         # N: Revealed type is "Any"
-reveal_type(C1.x._value_)       # N: Revealed type is "Any"
+reveal_type(C1.x.value)         # N: Revealed type is "builtins.int"
+reveal_type(C1.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(C2.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(C2.x._name_))  # N: Revealed type is "Literal['x']"
 reveal_type(C2.x.value)         # N: Revealed type is "builtins.int"
@@ -800,8 +794,8 @@ class D3(Flag):
 
 is_x(reveal_type(D1.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(D1.x._name_))  # N: Revealed type is "Literal['x']"
-reveal_type(D1.x.value)         # N: Revealed type is "Any"
-reveal_type(D1.x._value_)       # N: Revealed type is "Any"
+reveal_type(D1.x.value)         # N: Revealed type is "builtins.int"
+reveal_type(D1.x._value_)       # N: Revealed type is "builtins.int"
 is_x(reveal_type(D2.x.name))    # N: Revealed type is "Literal['x']"
 is_x(reveal_type(D2.x._name_))  # N: Revealed type is "Literal['x']"
 reveal_type(D2.x.value)         # N: Revealed type is "builtins.int"
@@ -2539,3 +2533,105 @@ def check(thing: Things) -> None:
         return None
     return None  # E: Statement is unreachable
 [builtins fixtures/enum.pyi]
+
+[case testSunderValueTypeEllipsis]
+from foo.bar import (
+    Basic, FromStub, InheritedInt, InheritedStr, InheritedFlag,
+    InheritedIntFlag, Wrapper
+)
+
+reveal_type(Basic.FOO)  # N: Revealed type is "Literal[foo.bar.Basic.FOO]?"
+reveal_type(Basic.FOO.value)  # N: Revealed type is "Literal[1]?"
+reveal_type(Basic.FOO._value_)  # N: Revealed type is "builtins.int"
+
+reveal_type(FromStub.FOO)  # N: Revealed type is "Literal[foo.bar.FromStub.FOO]?"
+reveal_type(FromStub.FOO.value)  # N: Revealed type is "builtins.int"
+reveal_type(FromStub.FOO._value_)  # N: Revealed type is "builtins.int"
+
+reveal_type(Wrapper.Nested.FOO)  # N: Revealed type is "Literal[foo.bar.Wrapper.Nested.FOO]?"
+reveal_type(Wrapper.Nested.FOO.value)  # N: Revealed type is "builtins.int"
+reveal_type(Wrapper.Nested.FOO._value_)  # N: Revealed type is "builtins.int"
+
+reveal_type(InheritedInt.FOO)  # N: Revealed type is "Literal[foo.bar.InheritedInt.FOO]?"
+reveal_type(InheritedInt.FOO.value)  # N: Revealed type is "builtins.int"
+reveal_type(InheritedInt.FOO._value_)  # N: Revealed type is "builtins.int"
+
+reveal_type(InheritedStr.FOO)  # N: Revealed type is "Literal[foo.bar.InheritedStr.FOO]?"
+reveal_type(InheritedStr.FOO.value)  # N: Revealed type is "builtins.str"
+reveal_type(InheritedStr.FOO._value_)  # N: Revealed type is "builtins.str"
+
+reveal_type(InheritedFlag.FOO)  # N: Revealed type is "Literal[foo.bar.InheritedFlag.FOO]?"
+reveal_type(InheritedFlag.FOO.value)  # N: Revealed type is "builtins.int"
+reveal_type(InheritedFlag.FOO._value_)  # N: Revealed type is "builtins.int"
+
+reveal_type(InheritedIntFlag.FOO)  # N: Revealed type is "Literal[foo.bar.InheritedIntFlag.FOO]?"
+reveal_type(InheritedIntFlag.FOO.value)  # N: Revealed type is "builtins.int"
+reveal_type(InheritedIntFlag.FOO._value_)  # N: Revealed type is "builtins.int"
+
+[file foo/__init__.pyi]
+[file foo/bar/__init__.pyi]
+from enum import Enum, IntEnum, StrEnum, Flag, IntFlag
+
+class Basic(Enum):
+    _value_: int
+    FOO = 1
+
+class FromStub(Enum):
+    _value_: int
+    FOO = ...
+
+class Wrapper:
+    class Nested(Enum):
+        _value_: int
+        FOO = ...
+
+class InheritedInt(IntEnum):
+    FOO = ...
+
+class InheritedStr(StrEnum):
+    FOO = ...
+
+class InheritedFlag(Flag):
+    FOO = ...
+
+class InheritedIntFlag(IntFlag):
+    FOO = ...
+[builtins fixtures/enum.pyi]
+
+[case testSunderValueTypeEllipsisNonStub]
+from enum import Enum, StrEnum
+
+class Basic(Enum):
+    _value_: int
+    FOO = 1
+
+reveal_type(Basic.FOO)  # N: Revealed type is "Literal[__main__.Basic.FOO]?"
+reveal_type(Basic.FOO.value)  # N: Revealed type is "Literal[1]?"
+reveal_type(Basic.FOO._value_)  # N: Revealed type is "builtins.int"
+
+# TODO: this and below should produce diagnostics, Ellipsis is not assignable to int
+# Now we do not check members against _value_ at all.
+
+class FromStub(Enum):
+    _value_: int
+    FOO = ...
+
+reveal_type(FromStub.FOO)  # N: Revealed type is "Literal[__main__.FromStub.FOO]?"
+reveal_type(FromStub.FOO.value)  # N: Revealed type is "builtins.ellipsis"
+reveal_type(FromStub.FOO._value_)  # N: Revealed type is "builtins.int"
+
+class InheritedStr(StrEnum):
+    FOO = ...
+
+reveal_type(InheritedStr.FOO)  # N: Revealed type is "Literal[__main__.InheritedStr.FOO]?"
+reveal_type(InheritedStr.FOO.value)  # N: Revealed type is "builtins.ellipsis"
+reveal_type(InheritedStr.FOO._value_)  # N: Revealed type is "builtins.ellipsis"
+
+class Wrapper:
+    class Nested(StrEnum):
+        FOO = ...
+
+reveal_type(Wrapper.Nested.FOO)  # N: Revealed type is "Literal[__main__.Wrapper.Nested.FOO]?"
+reveal_type(Wrapper.Nested.FOO.value)  # N: Revealed type is "builtins.ellipsis"
+reveal_type(Wrapper.Nested.FOO._value_)  # N: Revealed type is "builtins.ellipsis"
+[builtins fixtures/enum.pyi]
diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi
index ccb3818b9d25..5047f7083804 100644
--- a/test-data/unit/lib-stub/enum.pyi
+++ b/test-data/unit/lib-stub/enum.pyi
@@ -29,6 +29,7 @@ class Enum(metaclass=EnumMeta):
 
 class IntEnum(int, Enum):
     value: int
+    _value_: int
     def __new__(cls: Type[_T], value: Union[int, _T]) -> _T: ...
 
 def unique(enumeration: _T) -> _T: pass
@@ -36,6 +37,8 @@ def unique(enumeration: _T) -> _T: pass
 # In reality Flag and IntFlag are 3.6 only
 
 class Flag(Enum):
+    value: int
+    _value_: int
     def __or__(self: _T, other: Union[int, _T]) -> _T: pass
 
 
@@ -49,6 +52,8 @@ class auto(IntFlag):
 
 # It is python-3.11+ only:
 class StrEnum(str, Enum):
+    _value_: str
+    value: str
     def __new__(cls: Type[_T], value: str | _T) -> _T: ...
 
 # It is python-3.11+ only:

From 1091321d4ae4e0316928d5832de9eb97b60f496b Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 9 Jul 2025 18:54:28 +0200
Subject: [PATCH 1448/1617] Infer empty list without annotation for `__slots__`
 and module `__all__` (#19348)

Fixes #10870, fixes #10103.

This adds a fake `Iterable[str]` context when checking the following:

* `__all__ = []` at top level
* `__slots__ = []` at class level (also works for sets but not for
dicts)

Additionally, this fixes a bug with `__slots__` being mistakenly checked
in other contexts (at top level or in function bodies), so e.g. the
following is now accepted:

```python
def foo() -> None:
    __slots__ = 1
```
---
 mypy/checker.py                   |  8 +++++++-
 mypy/checker_shared.py            |  4 ++++
 test-data/unit/check-modules.test | 30 +++++++++++++++++++++++++++++-
 test-data/unit/check-slots.test   | 23 +++++++++++++++++++++++
 4 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index edd9519da4f8..64bcc0871c38 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -3138,7 +3138,7 @@ def check_assignment(
                         else:
                             self.check_getattr_method(signature, lvalue, name)
 
-                if name == "__slots__":
+                if name == "__slots__" and self.scope.active_class() is not None:
                     typ = lvalue_type or self.expr_checker.accept(rvalue)
                     self.check_slots_definition(typ, lvalue)
                 if name == "__match_args__" and inferred is not None:
@@ -3317,6 +3317,12 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
                     type_contexts.append(base_type)
         # Use most derived supertype as type context if available.
         if not type_contexts:
+            if inferred.name == "__slots__" and self.scope.active_class() is not None:
+                str_type = self.named_type("builtins.str")
+                return self.named_generic_type("typing.Iterable", [str_type])
+            if inferred.name == "__all__" and self.scope.is_top_level():
+                str_type = self.named_type("builtins.str")
+                return self.named_generic_type("typing.Sequence", [str_type])
             return None
         candidate = type_contexts[0]
         for other in type_contexts:
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index 2ab4548edfaf..a9cbae643dca 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -334,6 +334,10 @@ def current_self_type(self) -> Instance | TupleType | None:
                 return fill_typevars(item)
         return None
 
+    def is_top_level(self) -> bool:
+        """Is current scope top-level (no classes or functions)?"""
+        return len(self.stack) == 1
+
     @contextmanager
     def push_function(self, item: FuncItem) -> Iterator[None]:
         self.stack.append(item)
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
index 858024e7daf2..862cd8ea3905 100644
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -423,7 +423,35 @@ import typing
 __all__ = [1, 2, 3]
 [builtins fixtures/module_all.pyi]
 [out]
-main:2: error: Type of __all__ must be "Sequence[str]", not "list[int]"
+main:2: error: List item 0 has incompatible type "int"; expected "str"
+main:2: error: List item 1 has incompatible type "int"; expected "str"
+main:2: error: List item 2 has incompatible type "int"; expected "str"
+
+[case testAllMustBeSequenceStr2]
+import typing
+__all__ = 1  # E: Type of __all__ must be "Sequence[str]", not "int"
+reveal_type(__all__)  # N: Revealed type is "builtins.int"
+[builtins fixtures/module_all.pyi]
+
+[case testAllMustBeSequenceStr3]
+import typing
+__all__ = set()  # E: Need type annotation for "__all__" (hint: "__all__: set[] = ...") \
+                 # E: Type of __all__ must be "Sequence[str]", not "set[Any]"
+reveal_type(__all__)  # N: Revealed type is "builtins.set[Any]"
+[builtins fixtures/set.pyi]
+
+[case testModuleAllEmptyList]
+__all__ = []
+reveal_type(__all__)  # N: Revealed type is "builtins.list[builtins.str]"
+[builtins fixtures/module_all.pyi]
+
+[case testDunderAllNotGlobal]
+class A:
+    __all__ = 1
+
+def foo() -> None:
+    __all__ = 1
+[builtins fixtures/module_all.pyi]
 
 [case testUnderscoreExportedValuesInImportAll]
 import typing
diff --git a/test-data/unit/check-slots.test b/test-data/unit/check-slots.test
index b7ce5e596101..e924ac9e5f57 100644
--- a/test-data/unit/check-slots.test
+++ b/test-data/unit/check-slots.test
@@ -496,6 +496,29 @@ class A:
         self.missing = 3
 [builtins fixtures/dict.pyi]
 
+[case testSlotsNotInClass]
+# Shouldn't be triggered
+__slots__ = [1, 2]
+reveal_type(__slots__)  # N: Revealed type is "builtins.list[builtins.int]"
+
+def foo() -> None:
+    __slots__ = 1
+    reveal_type(__slots__)  # N: Revealed type is "builtins.int"
+
+[case testSlotsEmptyList]
+class A:
+    __slots__ = []
+    reveal_type(__slots__)  # N: Revealed type is "builtins.list[builtins.str]"
+
+reveal_type(A.__slots__)  # N: Revealed type is "builtins.list[builtins.str]"
+
+[case testSlotsEmptySet]
+class A:
+    __slots__ = set()
+    reveal_type(__slots__)  # N: Revealed type is "builtins.set[builtins.str]"
+
+reveal_type(A.__slots__)  # N: Revealed type is "builtins.set[builtins.str]"
+[builtins fixtures/set.pyi]
 
 [case testSlotsWithAny]
 from typing import Any

From 02c97661281443483a73f6ca9247c7faf63213e6 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Wed, 9 Jul 2025 19:13:14 +0200
Subject: [PATCH 1449/1617] [mypyc] Use PyList_Check for isinstance(obj, list)
 (#19416)

Added a primitive to translate `isinstance(obj, list)` to
`PyList_Check(obj)` instead of `PyObject_IsInstance(obj, type)`, as the
former is faster. Similar primitives can be added for other built-in
types, which I plan to do in next PRs.

```
def f(x) -> bool:
    return isinstance(x, list)

def bench(n: int) -> None:
    for x in range(n):
        if x % 2 == 0:
            f(x)
        else:
            f([x])

from time import time
bench(1000)
t0 = time()
bench(50 * 1000 * 1000)
print(time() - t0)
```

Using the above benchmark, execution time goes from ~1.4s to ~0.97s on
my machine.
---
 mypyc/irbuild/specialize.py        | 25 +++++++++++++------
 mypyc/primitives/list_ops.py       |  9 +++++++
 mypyc/test-data/irbuild-lists.test | 32 ++++++++++--------------
 mypyc/test-data/run-lists.test     | 40 ++++++++++++++++++++++++------
 4 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py
index f652449f5289..b490c2a52e57 100644
--- a/mypyc/irbuild/specialize.py
+++ b/mypyc/irbuild/specialize.py
@@ -14,7 +14,7 @@
 
 from __future__ import annotations
 
-from typing import Callable, Optional
+from typing import Callable, Final, Optional
 
 from mypy.nodes import (
     ARG_NAMED,
@@ -89,7 +89,7 @@
     dict_setdefault_spec_init_op,
     dict_values_op,
 )
-from mypyc.primitives.list_ops import new_list_set_item_op
+from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op
 from mypyc.primitives.str_ops import (
     str_encode_ascii_strict,
     str_encode_latin1_strict,
@@ -546,6 +546,9 @@ def gen_inner_stmts() -> None:
     return retval
 
 
+isinstance_primitives: Final = {"builtins.list": isinstance_list}
+
+
 @specialize_function("builtins.isinstance")
 def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
     """Special case for builtins.isinstance.
@@ -554,11 +557,10 @@ def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ->
     there is no need to coerce something to a new type before checking
     what type it is, and the coercion could lead to bugs.
     """
-    if (
-        len(expr.args) == 2
-        and expr.arg_kinds == [ARG_POS, ARG_POS]
-        and isinstance(expr.args[1], (RefExpr, TupleExpr))
-    ):
+    if not (len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]):
+        return None
+
+    if isinstance(expr.args[1], (RefExpr, TupleExpr)):
         builder.types[expr.args[0]] = AnyType(TypeOfAny.from_error)
 
         irs = builder.flatten_classes(expr.args[1])
@@ -569,6 +571,15 @@ def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ->
             )
             obj = builder.accept(expr.args[0], can_borrow=can_borrow)
             return builder.builder.isinstance_helper(obj, irs, expr.line)
+
+    if isinstance(expr.args[1], RefExpr):
+        node = expr.args[1].node
+        if node:
+            desc = isinstance_primitives.get(node.fullname)
+            if desc:
+                obj = builder.accept(expr.args[0])
+                return builder.primitive_op(desc, [obj], expr.line)
+
     return None
 
 
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index d0e0af9f987f..7442e31c9118 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -55,6 +55,15 @@
     extra_int_constants=[(0, int_rprimitive)],
 )
 
+# isinstance(obj, list)
+isinstance_list = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyList_Check",
+    error_kind=ERR_NEVER,
+)
+
 new_list_op = custom_op(
     arg_types=[c_pyssize_t_rprimitive],
     return_type=list_rprimitive,
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index 72caa5fad8d8..efd38870974d 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -498,29 +498,23 @@ def nested_union(a: Union[List[str], List[Optional[str]]]) -> None:
 [out]
 def narrow(a):
     a :: union[list, int]
-    r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
-    r4 :: list
-    r5 :: native_int
-    r6 :: short_int
-    r7 :: int
+    r0 :: bit
+    r1 :: list
+    r2 :: native_int
+    r3 :: short_int
+    r4 :: int
 L0:
-    r0 = load_address PyList_Type
-    r1 = PyObject_IsInstance(a, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    if r3 goto L1 else goto L2 :: bool
+    r0 = PyList_Check(a)
+    if r0 goto L1 else goto L2 :: bool
 L1:
-    r4 = borrow cast(list, a)
-    r5 = var_object_size r4
-    r6 = r5 << 1
+    r1 = borrow cast(list, a)
+    r2 = var_object_size r1
+    r3 = r2 << 1
     keep_alive a
-    return r6
+    return r3
 L2:
-    r7 = unbox(int, a)
-    return r7
+    r4 = unbox(int, a)
+    return r4
 def loop(a):
     a :: list
     r0 :: short_int
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 85e0926027c5..ee1bd27e6352 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -466,7 +466,7 @@ assert not list_in_mixed(object)
 assert list_in_mixed(type)
 
 [case testListBuiltFromGenerator]
-def test() -> None:
+def test_from_gen() -> None:
     source_a = ["a", "b", "c"]
     a = list(x + "f2" for x in source_a)
     assert a == ["af2", "bf2", "cf2"]
@@ -486,12 +486,6 @@ def test() -> None:
     f = list("str:" + x for x in source_str)
     assert f == ["str:a", "str:b", "str:c", "str:d"]
 
-[case testNextBug]
-from typing import List, Optional
-
-def test(x: List[int]) -> None:
-    res = next((i for i in x), None)
-
 [case testListGetItemWithBorrow]
 from typing import List
 
@@ -537,3 +531,35 @@ def test_sorted() -> None:
     assert sorted((2, 1, 3)) == res
     assert sorted({2, 1, 3}) == res
     assert sorted({2: "", 1: "", 3: ""}) == res
+
+[case testIsInstance]
+from copysubclass import subc
+def test_built_in() -> None:
+    assert isinstance([], list)
+    assert isinstance([1,2,3], list)
+    assert isinstance(['a','b'], list)
+    assert isinstance(subc(), list)
+    assert isinstance(subc([1,2,3]), list)
+    assert isinstance(subc(['a','b']), list)
+
+    assert not isinstance({}, list)
+    assert not isinstance((), list)
+    assert not isinstance((1,2,3), list)
+    assert not isinstance(('a','b'), list)
+    assert not isinstance(1, list)
+    assert not isinstance('a', list)
+
+def test_user_defined() -> None:
+    from userdefinedlist import list
+
+    assert isinstance(list(), list)
+    assert not isinstance([list()], list)
+
+[file copysubclass.py]
+from typing import Any
+class subc(list[Any]):
+    pass
+
+[file userdefinedlist.py]
+class list:
+    pass

From 7ea925d38657f8c2a81975f8cfc71eb8455ade61 Mon Sep 17 00:00:00 2001
From: Chainfire 
Date: Thu, 10 Jul 2025 11:58:57 +0200
Subject: [PATCH 1450/1617] [mypyc] Fix exception swallowing in async
 try/finally blocks with await (#19353)

When a try/finally block in an async function contains an await statement
in the finally block, exceptions raised in the try block are silently
swallowed if a context switch occurs. This happens because mypyc stores
exception information in registers that don't survive across await points.

The Problem:

- mypyc's transform_try_finally_stmt uses error_catch_op to save exceptions
- to a register, then reraise_exception_op to restore from that register
- When await causes a context switch, register values are lost
- The exception information is gone, causing silent exception swallowing

The Solution:

- Add new transform_try_finally_stmt_async for async-aware exception handling
- Use sys.exc_info() to preserve exceptions across context switches instead
- of registers
- Check error indicator first to handle new exceptions raised in finally
- Route to async version when finally block contains await expressions

Implementation Details:

- transform_try_finally_stmt_async uses get_exc_info_op/restore_exc_info_op
- which work with sys.exc_info() that survives context switches
- Proper exception priority: new exceptions in finally replace originals
- Added has_await_in_block helper to detect await expressions

Test Coverage:

Added comprehensive async exception handling tests:

- testAsyncTryExceptFinallyAwait: 8 test cases covering various scenarios
    - Simple try/finally with exception and await
    - Exception caught but not re-raised
    - Exception caught and re-raised
    - Different exception raised in except
    - Try/except inside finally block
    - Try/finally inside finally block
    - Control case without await
    - Normal flow without exceptions
- testAsyncContextManagerExceptionHandling: Verifies async with still works
    - Basic exception propagation
    - Exception in **aexit** replacing original

See mypyc/mypyc#1114.
---
 mypyc/irbuild/statement.py     | 137 ++++++++++++++++++++-
 mypyc/test-data/run-async.test | 211 +++++++++++++++++++++++++++++++++
 2 files changed, 346 insertions(+), 2 deletions(-)

diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py
index 4362f42b41d2..eeeb40ac672f 100644
--- a/mypyc/irbuild/statement.py
+++ b/mypyc/irbuild/statement.py
@@ -12,6 +12,7 @@
 from collections.abc import Sequence
 from typing import Callable
 
+import mypy.nodes
 from mypy.nodes import (
     ARG_NAMED,
     ARG_POS,
@@ -104,6 +105,7 @@
     get_exc_info_op,
     get_exc_value_op,
     keep_propagating_op,
+    no_err_occurred_op,
     propagate_if_error_op,
     raise_exception_op,
     reraise_exception_op,
@@ -683,7 +685,7 @@ def try_finally_resolve_control(
 
 
 def transform_try_finally_stmt(
-    builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc
+    builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1
 ) -> None:
     """Generalized try/finally handling that takes functions to gen the bodies.
 
@@ -719,6 +721,118 @@ def transform_try_finally_stmt(
     builder.activate_block(out_block)
 
 
+def transform_try_finally_stmt_async(
+    builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1
+) -> None:
+    """Async-aware try/finally handling for when finally contains await.
+
+    This version uses a modified approach that preserves exceptions across await."""
+
+    # We need to handle returns properly, so we'll use TryFinallyNonlocalControl
+    # to track return values, similar to the regular try/finally implementation
+
+    err_handler, main_entry, return_entry, finally_entry = (
+        BasicBlock(),
+        BasicBlock(),
+        BasicBlock(),
+        BasicBlock(),
+    )
+
+    # Track if we're returning from the try block
+    control = TryFinallyNonlocalControl(return_entry)
+    builder.builder.push_error_handler(err_handler)
+    builder.nonlocal_control.append(control)
+    builder.goto_and_activate(BasicBlock())
+    try_body()
+    builder.goto(main_entry)
+    builder.nonlocal_control.pop()
+    builder.builder.pop_error_handler()
+    ret_reg = control.ret_reg
+
+    # Normal case - no exception or return
+    builder.activate_block(main_entry)
+    builder.goto(finally_entry)
+
+    # Return case
+    builder.activate_block(return_entry)
+    builder.goto(finally_entry)
+
+    # Exception case - need to catch to clear the error indicator
+    builder.activate_block(err_handler)
+    # Catch the error to clear Python's error indicator
+    builder.call_c(error_catch_op, [], line)
+    # We're not going to use old_exc since it won't survive await
+    # The exception is now in sys.exc_info()
+    builder.goto(finally_entry)
+
+    # Finally block
+    builder.activate_block(finally_entry)
+
+    # Execute finally body
+    finally_body()
+
+    # After finally, we need to handle exceptions carefully:
+    # 1. If finally raised a new exception, it's in the error indicator - let it propagate
+    # 2. If finally didn't raise, check if we need to reraise the original from sys.exc_info()
+    # 3. If there was a return, return that value
+    # 4. Otherwise, normal exit
+
+    # First, check if there's a current exception in the error indicator
+    # (this would be from the finally block)
+    no_current_exc = builder.call_c(no_err_occurred_op, [], line)
+    finally_raised = BasicBlock()
+    check_original = BasicBlock()
+    builder.add(Branch(no_current_exc, check_original, finally_raised, Branch.BOOL))
+
+    # Finally raised an exception - let it propagate naturally
+    builder.activate_block(finally_raised)
+    builder.call_c(keep_propagating_op, [], NO_TRACEBACK_LINE_NO)
+    builder.add(Unreachable())
+
+    # No exception from finally, check if we need to handle return or original exception
+    builder.activate_block(check_original)
+
+    # Check if we have a return value
+    if ret_reg:
+        return_block, check_old_exc = BasicBlock(), BasicBlock()
+        builder.add(Branch(builder.read(ret_reg), check_old_exc, return_block, Branch.IS_ERROR))
+
+        builder.activate_block(return_block)
+        builder.nonlocal_control[-1].gen_return(builder, builder.read(ret_reg), -1)
+
+        builder.activate_block(check_old_exc)
+
+    # Check if we need to reraise the original exception from sys.exc_info
+    exc_info = builder.call_c(get_exc_info_op, [], line)
+    exc_type = builder.add(TupleGet(exc_info, 0, line))
+
+    # Check if exc_type is None
+    none_obj = builder.none_object()
+    has_exc = builder.binary_op(exc_type, none_obj, "is not", line)
+
+    reraise_block, exit_block = BasicBlock(), BasicBlock()
+    builder.add(Branch(has_exc, reraise_block, exit_block, Branch.BOOL))
+
+    # Reraise the original exception
+    builder.activate_block(reraise_block)
+    builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
+    builder.add(Unreachable())
+
+    # Normal exit
+    builder.activate_block(exit_block)
+
+
+# A simple visitor to detect await expressions
+class AwaitDetector(mypy.traverser.TraverserVisitor):
+    def __init__(self) -> None:
+        super().__init__()
+        self.has_await = False
+
+    def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> None:
+        self.has_await = True
+        super().visit_await_expr(o)
+
+
 def transform_try_stmt(builder: IRBuilder, t: TryStmt) -> None:
     # Our compilation strategy for try/except/else/finally is to
     # treat try/except/else and try/finally as separate language
@@ -727,6 +841,17 @@ def transform_try_stmt(builder: IRBuilder, t: TryStmt) -> None:
     # body of a try/finally block.
     if t.is_star:
         builder.error("Exception groups and except* cannot be compiled yet", t.line)
+
+    # Check if we're in an async function with a finally block that contains await
+    use_async_version = False
+    if t.finally_body and builder.fn_info.is_coroutine:
+        detector = AwaitDetector()
+        t.finally_body.accept(detector)
+
+        if detector.has_await:
+            # Use the async version that handles exceptions correctly
+            use_async_version = True
+
     if t.finally_body:
 
         def transform_try_body() -> None:
@@ -737,7 +862,14 @@ def transform_try_body() -> None:
 
         body = t.finally_body
 
-        transform_try_finally_stmt(builder, transform_try_body, lambda: builder.accept(body))
+        if use_async_version:
+            transform_try_finally_stmt_async(
+                builder, transform_try_body, lambda: builder.accept(body), t.line
+            )
+        else:
+            transform_try_finally_stmt(
+                builder, transform_try_body, lambda: builder.accept(body), t.line
+            )
     else:
         transform_try_except_stmt(builder, t)
 
@@ -828,6 +960,7 @@ def finally_body() -> None:
         builder,
         lambda: transform_try_except(builder, try_body, [(None, None, except_body)], None, line),
         finally_body,
+        line,
     )
 
 
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index b8c4c22daf71..f1ec7e8f85e0 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -1037,3 +1037,214 @@ test_async_with_mixed_return()
 
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
+
+[case testAsyncTryExceptFinallyAwait]
+import asyncio
+from testutil import assertRaises
+
+class TestError(Exception):
+    pass
+
+# Test 0: Simplest case - just try/finally with raise and await
+async def simple_try_finally_await() -> None:
+    try:
+        raise ValueError("simple error")
+    finally:
+        await asyncio.sleep(0)
+
+# Test 1: Raise inside try, catch in except, don't re-raise
+async def async_try_except_no_reraise() -> int:
+    try:
+        raise ValueError("test error")
+        return 1  # Never reached
+    except ValueError:
+        return 2  # Should return this
+    finally:
+        await asyncio.sleep(0)
+    return 3  # Should not reach this
+
+# Test 2: Raise inside try, catch in except, re-raise
+async def async_try_except_reraise() -> int:
+    try:
+        raise ValueError("test error")
+        return 1  # Never reached
+    except ValueError:
+        raise  # Re-raise the exception
+    finally:
+        await asyncio.sleep(0)
+    return 2  # Should not reach this
+
+# Test 3: Raise inside try, catch in except, raise different error
+async def async_try_except_raise_different() -> int:
+    try:
+        raise ValueError("original error")
+        return 1  # Never reached
+    except ValueError:
+        raise RuntimeError("different error")
+    finally:
+        await asyncio.sleep(0)
+    return 2  # Should not reach this
+
+# Test 4: Another try/except block inside finally
+async def async_try_except_inside_finally() -> int:
+    try:
+        raise ValueError("outer error")
+        return 1  # Never reached
+    finally:
+        await asyncio.sleep(0)
+        try:
+            raise RuntimeError("inner error")
+        except RuntimeError:
+            pass  # Catch inner error
+    return 2  # What happens after finally with inner exception handled?
+
+# Test 5: Another try/finally block inside finally
+async def async_try_finally_inside_finally() -> int:
+    try:
+        raise ValueError("outer error")
+        return 1  # Never reached
+    finally:
+        await asyncio.sleep(0)
+        try:
+            raise RuntimeError("inner error")
+        finally:
+            await asyncio.sleep(0)
+    return 2  # Should not reach this
+
+# Control case: No await in finally - should work correctly
+async def async_exception_no_await_in_finally() -> None:
+    """Control case: This works correctly - exception propagates"""
+    try:
+        raise TestError("This exception will propagate!")
+    finally:
+        pass  # No await here
+
+# Test function with no exception to check normal flow
+async def async_no_exception_with_await_in_finally() -> int:
+    try:
+        return 1  # Normal return
+    finally:
+        await asyncio.sleep(0)
+    return 2  # Should not reach this
+
+def test_async_try_except_finally_await() -> None:
+    # Test 0: Simplest case - just try/finally with exception
+    # Expected: ValueError propagates
+    with assertRaises(ValueError):
+        asyncio.run(simple_try_finally_await())
+
+    # Test 1: Exception caught, not re-raised
+    # Expected: return 2 (from except block)
+    result = asyncio.run(async_try_except_no_reraise())
+    assert result == 2, f"Expected 2, got {result}"
+
+    # Test 2: Exception caught and re-raised
+    # Expected: ValueError propagates
+    with assertRaises(ValueError):
+        asyncio.run(async_try_except_reraise())
+
+    # Test 3: Exception caught, different exception raised
+    # Expected: RuntimeError propagates
+    with assertRaises(RuntimeError):
+        asyncio.run(async_try_except_raise_different())
+
+    # Test 4: Try/except inside finally
+    # Expected: ValueError propagates (outer exception)
+    with assertRaises(ValueError):
+        asyncio.run(async_try_except_inside_finally())
+
+    # Test 5: Try/finally inside finally
+    # Expected: RuntimeError propagates (inner error)
+    with assertRaises(RuntimeError):
+        asyncio.run(async_try_finally_inside_finally())
+
+    # Control case: No await in finally (should work correctly)
+    with assertRaises(TestError):
+        asyncio.run(async_exception_no_await_in_finally())
+
+    # Test normal flow (no exception)
+    # Expected: return 1
+    result = asyncio.run(async_no_exception_with_await_in_finally())
+    assert result == 1, f"Expected 1, got {result}"
+
+[file asyncio/__init__.pyi]
+async def sleep(t: float) -> None: ...
+def run(x: object) -> object: ...
+
+[case testAsyncContextManagerExceptionHandling]
+import asyncio
+from typing import Optional, Type
+from testutil import assertRaises
+
+# Test 1: Basic async context manager that doesn't suppress exceptions
+class AsyncContextManager:
+    async def __aenter__(self) -> 'AsyncContextManager':
+        return self
+
+    async def __aexit__(self, exc_type: Optional[Type[BaseException]],
+                       exc_val: Optional[BaseException],
+                       exc_tb: object) -> None:
+        # This await in __aexit__ is like await in finally
+        await asyncio.sleep(0)
+        # Don't suppress the exception (return None/False)
+
+async def func_with_async_context_manager() -> str:
+    async with AsyncContextManager():
+        raise ValueError("Exception inside async with")
+        return "should not reach"  # Never reached
+    return "should not reach either"  # Never reached
+
+async def test_basic_exception() -> str:
+    try:
+        await func_with_async_context_manager()
+        return "func_a returned normally - bug!"
+    except ValueError:
+        return "caught ValueError - correct!"
+    except Exception as e:
+        return f"caught different exception: {type(e).__name__}"
+
+# Test 2: Async context manager that raises a different exception in __aexit__
+class AsyncContextManagerRaisesInExit:
+    async def __aenter__(self) -> 'AsyncContextManagerRaisesInExit':
+        return self
+
+    async def __aexit__(self, exc_type: Optional[Type[BaseException]],
+                       exc_val: Optional[BaseException],
+                       exc_tb: object) -> None:
+        # This await in __aexit__ is like await in finally
+        await asyncio.sleep(0)
+        # Raise a different exception - this should replace the original exception
+        raise RuntimeError("Exception in __aexit__")
+
+async def func_with_raising_context_manager() -> str:
+    async with AsyncContextManagerRaisesInExit():
+        raise ValueError("Original exception")
+        return "should not reach"  # Never reached
+    return "should not reach either"  # Never reached
+
+async def test_exception_in_aexit() -> str:
+    try:
+        await func_with_raising_context_manager()
+        return "func returned normally - unexpected!"
+    except RuntimeError:
+        return "caught RuntimeError - correct!"
+    except ValueError:
+        return "caught ValueError - original exception not replaced!"
+    except Exception as e:
+        return f"caught different exception: {type(e).__name__}"
+
+def test_async_context_manager_exception_handling() -> None:
+    # Test 1: Basic exception propagation
+    result = asyncio.run(test_basic_exception())
+    # Expected: "caught ValueError - correct!"
+    assert result == "caught ValueError - correct!", f"Expected exception to propagate, got: {result}"
+
+    # Test 2: Exception raised in __aexit__ replaces original exception
+    result = asyncio.run(test_exception_in_aexit())
+    # Expected: "caught RuntimeError - correct!"
+    # (The RuntimeError from __aexit__ should replace the ValueError)
+    assert result == "caught RuntimeError - correct!", f"Expected RuntimeError from __aexit__, got: {result}"
+
+[file asyncio/__init__.pyi]
+async def sleep(t: float) -> None: ...
+def run(x: object) -> object: ...

From a794ae3eebfb1c36efd1fa5207cf072a1725b8d0 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 10 Jul 2025 12:10:06 +0200
Subject: [PATCH 1451/1617] Move `is_defined_in_stub` to shared checker API to
 break import cycle (#19417)

Follow-up after #19352 as requested by @JukkaL
---
 mypy/checker.py        | 3 +++
 mypy/checker_shared.py | 4 ++++
 mypy/plugins/enums.py  | 6 +++---
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 64bcc0871c38..159569849061 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -7889,6 +7889,9 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool:
     def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type:
         return self.expr_checker.accept(node, type_context=type_context)
 
+    def is_defined_in_stub(self, typ: Instance, /) -> bool:
+        return self.modules[typ.type.module_name].is_stub
+
     def check_deprecated(self, node: Node | None, context: Context) -> None:
         """Warn if deprecated and not directly imported with a `from` statement."""
         if isinstance(node, Decorator):
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index a9cbae643dca..65cec41d5202 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -277,6 +277,10 @@ def checking_await_set(self) -> Iterator[None]:
     def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None:
         raise NotImplementedError
 
+    @abstractmethod
+    def is_defined_in_stub(self, typ: Instance, /) -> bool:
+        raise NotImplementedError
+
 
 class CheckerScope:
     # We keep two stacks combined, to maintain the relative order
diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py
index 860c56c63570..d21b21fb39f8 100644
--- a/mypy/plugins/enums.py
+++ b/mypy/plugins/enums.py
@@ -17,7 +17,7 @@
 from typing import TypeVar, cast
 
 import mypy.plugin  # To avoid circular imports.
-from mypy.checker import TypeChecker
+from mypy.checker_shared import TypeCheckerSharedApi
 from mypy.nodes import TypeInfo, Var
 from mypy.subtypes import is_equivalent
 from mypy.typeops import fixup_partial_type, make_simplified_union
@@ -122,8 +122,8 @@ def _infer_value_type_with_auto_fallback(
 
 
 def _is_defined_in_stub(ctx: mypy.plugin.AttributeContext) -> bool:
-    assert isinstance(ctx.api, TypeChecker)
-    return isinstance(ctx.type, Instance) and ctx.api.modules[ctx.type.type.module_name].is_stub
+    assert isinstance(ctx.api, TypeCheckerSharedApi)
+    return isinstance(ctx.type, Instance) and ctx.api.is_defined_in_stub(ctx.type)
 
 
 def _implements_new(info: TypeInfo) -> bool:

From 77ce6464c52db1201fd0f17c5b34952da8121ca5 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Fri, 11 Jul 2025 11:35:31 +0200
Subject: [PATCH 1452/1617] [mypyc] Fail run test if default driver does not
 find test cases (#19420)

If a `run-` test doesn't define a custom `driver.py`, the default one
looks for functions prefixed `test_` and calls them. If the prefix is
missing or misspelled, or there is some other issue that causes the test
to have no test cases, the test succeeds without running the cases.

To try to prevent this, the default driver will now fail if it doesn't
find any test cases. Existing tests are changed to conform to this
requirement in this PR.

For most tests that meant simply moving the statements from top level to
a function, but some were revealed to have been broken and weren't
actually run, for example because `[typing fixtures/typing-full.pyi]`
was at the top of the test instead of at the bottom, which made the test
setup code ignore all of the test case.
---
 mypyc/test-data/driver/driver.py         |  4 ++
 mypyc/test-data/fixtures/ir.py           |  7 ++-
 mypyc/test-data/run-bools.test           |  5 +-
 mypyc/test-data/run-bytes.test           |  2 +-
 mypyc/test-data/run-classes.test         | 79 +++++++++++++-----------
 mypyc/test-data/run-dunders-special.test |  3 +-
 mypyc/test-data/run-functions.test       | 17 ++---
 mypyc/test-data/run-generators.test      | 16 ++++-
 mypyc/test-data/run-generics.test        | 32 +++++-----
 mypyc/test-data/run-imports.test         | 32 +++++-----
 mypyc/test-data/run-misc.test            | 53 +++++++++-------
 mypyc/test-data/run-python38.test        | 11 ++--
 mypyc/test-data/run-singledispatch.test  | 22 ++++---
 mypyc/test-data/run-strings.test         |  2 +-
 14 files changed, 167 insertions(+), 118 deletions(-)

diff --git a/mypyc/test-data/driver/driver.py b/mypyc/test-data/driver/driver.py
index c9d179224a30..1ec1c48dfb75 100644
--- a/mypyc/test-data/driver/driver.py
+++ b/mypyc/test-data/driver/driver.py
@@ -11,10 +11,12 @@
 import native
 
 failures = []
+tests_run = 0
 
 for name in dir(native):
     if name.startswith('test_'):
         test_func = getattr(native, name)
+        tests_run += 1
         try:
             test_func()
         except Exception as e:
@@ -46,3 +48,5 @@ def extract_line(tb):
     print(f'<< {failures[-1][0]} >>')
     sys.stdout.flush()
     raise failures[-1][1][1]
+
+assert tests_run > 0, 'Default test driver did not find any functions prefixed "test_" to run.'
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 532cbbc06177..3776a3dcc79a 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -350,7 +350,12 @@ class GeneratorExit(BaseException): pass
 
 def any(i: Iterable[_T]) -> bool: pass
 def all(i: Iterable[_T]) -> bool: pass
-def sum(i: Iterable[_T]) -> int: pass
+@overload
+def sum(i: Iterable[bool]) -> int: pass
+@overload
+def sum(i: Iterable[_T]) -> _T: pass
+@overload
+def sum(i: Iterable[_T], start: _T) -> _T: pass
 def reversed(object: Sequence[_T]) -> Iterator[_T]: ...
 def id(o: object) -> int: pass
 # This type is obviously wrong but the test stubs don't have Sized anymore
diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test
index a0b8ea31ebc0..3409665bfb37 100644
--- a/mypyc/test-data/run-bools.test
+++ b/mypyc/test-data/run-bools.test
@@ -223,7 +223,8 @@ def test_mixed_comparisons_i64() -> None:
             assert gt_mixed_i64(n, x) == (n > int(x))
 
 [case testBoolMixInt]
-y = False
-print((y or 0) and True)
+def test_mix() -> None:
+    y = False
+    print((y or 0) and True)
 [out]
 0
diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test
index fa63c46a6798..bee6b6fe9f76 100644
--- a/mypyc/test-data/run-bytes.test
+++ b/mypyc/test-data/run-bytes.test
@@ -277,7 +277,6 @@ class bytes_subclass(bytes):
         return b'spook'
 
 [case testBytesFormatting]
-[typing fixtures/typing-full.pyi]
 from testutil import assertRaises
 
 # https://www.python.org/dev/peps/pep-0461/
@@ -314,6 +313,7 @@ def test_bytes_formatting_2() -> None:
     aa = b'\xe4\xbd\xa0\xe5\xa5\xbd%b' % b'\xe4\xbd\xa0\xe5\xa5\xbd'
     assert aa == b'\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd'
     assert aa.decode() == '你好你好'
+[typing fixtures/typing-full.pyi]
 
 
 class A:
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index fd486980ef16..54f5343bc7bb 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -1278,9 +1278,10 @@ class Bar(Foo):
     def f(self, *args: int, **kwargs: int) -> None:
         print("stuff", args, kwargs)
 
-z: Foo = Bar()
-z.f(1, z=50)
-z.f()
+def test_override() -> None:
+    z: Foo = Bar()
+    z.f(1, z=50)
+    z.f()
 
 [out]
 stuff (1,) {'z': 50}
@@ -1300,18 +1301,19 @@ class Foo:
 def baz_f(self: Any, *args: int, **kwargs: int) -> None:
     print("Baz", args, kwargs)
 
-# Make an "interpreted" subtype of Foo
-type2: Any = type
-Bar = type2('Bar', (Foo,), {})
-Baz = type2('Baz', (Foo,), {'f': baz_f})
+def test_override() -> None:
+    # Make an "interpreted" subtype of Foo
+    type2: Any = type
+    Bar = type2('Bar', (Foo,), {})
+    Baz = type2('Baz', (Foo,), {'f': baz_f})
 
-y: Foo = Bar()
-y.f(1, z=2)
-y.f()
+    y: Foo = Bar()
+    y.f(1, z=2)
+    y.f()
 
-z: Foo = Baz()
-z.f(1, z=2)
-z.f()
+    z: Foo = Baz()
+    z.f(1, z=2)
+    z.f()
 
 [out]
 Foo 1 2
@@ -1330,9 +1332,10 @@ class Bar(Foo):
     def f(self, x: Optional[int]=None) -> None:
         print(x)
 
-z: Foo = Bar()
-z.f(1)
-z.f()
+def test_override() -> None:
+    z: Foo = Bar()
+    z.f(1)
+    z.f()
 
 [out]
 1
@@ -1349,10 +1352,11 @@ class Bar(Foo):
     def f(self, *args: int, **kwargs: int) -> None:
         print("Bar", args, kwargs)
 
-z: Foo = Bar()
-z.f(1, z=2)
-z.f(1, 2, 3)
-# z.f(x=5)  # Not tested because we (knowingly) do the wrong thing and pass it as positional
+def test_override() -> None:
+    z: Foo = Bar()
+    z.f(1, z=2)
+    z.f(1, 2, 3)
+    # z.f(x=5)  # Not tested because we (knowingly) do the wrong thing and pass it as positional
 
 [out]
 Bar (1,) {'z': 2}
@@ -1370,10 +1374,11 @@ class Bar(Foo):
     def f(self, x: int = 10, *args: int, **kwargs: int) -> None:
         print("Bar", x, args, kwargs)
 
-z: Foo = Bar()
-z.f(1, z=2)
-z.f(1, 2, 3)
-z.f()
+def test_override() -> None:
+    z: Foo = Bar()
+    z.f(1, z=2)
+    z.f(1, 2, 3)
+    z.f()
 
 [out]
 Bar 1 () {'z': 2}
@@ -1397,18 +1402,19 @@ class Foo:
 def baz_f(self, a: int=30, y: int=50) -> None:
     print("Baz", a, y)
 
-# Make an "interpreted" subtype of Foo
-type2: Any = type
-Baz = type2('Baz', (Foo,), {'f': baz_f})
+def test_override() -> None:
+    # Make an "interpreted" subtype of Foo
+    type2: Any = type
+    Baz = type2('Baz', (Foo,), {'f': baz_f})
 
-z: Foo = Baz()
-z.f()
-z.f(y=1)
-z.f(1, 2)
-# Not tested because we don't (and probably won't) match cpython here
-# from testutil import assertRaises
-# with assertRaises(TypeError):
-#     z.f(x=7)
+    z: Foo = Baz()
+    z.f()
+    z.f(y=1)
+    z.f(1, 2)
+    # Not tested because we don't (and probably won't) match cpython here
+    # from testutil import assertRaises
+    # with assertRaises(TypeError):
+    #     z.f(x=7)
 
 [out]
 Baz 30 50
@@ -2591,7 +2597,8 @@ class Base:
 class Derived(Base):
     pass
 
-assert Derived()() == 1
+def test_inherited() -> None:
+    assert Derived()() == 1
 
 [case testClassWithFinalAttribute]
 from typing import Final
diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test
index 30c618374f88..2672434e10ef 100644
--- a/mypyc/test-data/run-dunders-special.test
+++ b/mypyc/test-data/run-dunders-special.test
@@ -6,4 +6,5 @@ class UsesNotImplemented:
     def __eq__(self, b: object) -> bool:
         return NotImplemented
 
-assert UsesNotImplemented() != object()
+def test_not_implemented() -> None:
+    assert UsesNotImplemented() != object()
diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test
index 46f343fa3798..3d7f1f3cc747 100644
--- a/mypyc/test-data/run-functions.test
+++ b/mypyc/test-data/run-functions.test
@@ -1235,13 +1235,11 @@ from contextlib import contextmanager
 def f() -> Iterator[None]:
     yield
 
-def g() -> None:
+def test_special_case() -> None:
     a = ['']
     with f():
         a.pop()
 
-g()
-
 [case testUnpackKwargsCompiled]
 from typing import TypedDict
 from typing_extensions import Unpack
@@ -1253,8 +1251,9 @@ class Person(TypedDict):
 def foo(**kwargs: Unpack[Person]) -> None:
     print(kwargs["name"])
 
-# This is not really supported yet, just test that we behave reasonably.
-foo(name='Jennifer', age=38)
+def test_unpack() -> None:
+    # This is not really supported yet, just test that we behave reasonably.
+    foo(name='Jennifer', age=38)
 [typing fixtures/typing-full.pyi]
 [out]
 Jennifer
@@ -1269,8 +1268,9 @@ def foo() -> None:
     print(inner.__dict__)  # type: ignore[attr-defined]
     print(inner.x)  # type: ignore[attr-defined]
 
-if sys.version_info >= (3, 12):  # type: ignore
-    foo()
+def test_nested() -> None:
+    if sys.version_info >= (3, 12):  # type: ignore
+        foo()
 [out]
 [out version>=3.12]
 {}
@@ -1285,7 +1285,8 @@ def bar() -> None:
     functools.update_wrapper(inner, bar)  # type: ignore
     print(inner.__dict__)  # type: ignore
 
-bar()
+def test_update() -> None:
+    bar()
 [typing fixtures/typing-full.pyi]
 [out]
 {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': }
diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test
index a43aff27dd45..3b4581f849e9 100644
--- a/mypyc/test-data/run-generators.test
+++ b/mypyc/test-data/run-generators.test
@@ -617,16 +617,22 @@ else:
 from typing import Iterator
 
 class Foo:
-    flag: bool
+    flag = False
 
 class C:
-    foo: Foo
+    foo = Foo()
 
     def genf(self) -> Iterator[None]:
         self.foo.flag = True
         yield
         self.foo.flag = False
 
+def test_near_yield() -> None:
+    c = C()
+    for x in c.genf():
+        pass
+    assert c.foo.flag == False
+
 [case testGeneratorEarlyReturnWithBorrows]
 from typing import Iterator
 class Bar:
@@ -639,6 +645,12 @@ class Foo:
             return
         yield 0
 
+def test_early_return() -> None:
+    foo = Foo()
+    for x in foo.f():
+        pass
+    assert foo.bar.bar == 1
+
 [case testBorrowingInGeneratorInTupleAssignment]
 from typing import Iterator
 
diff --git a/mypyc/test-data/run-generics.test b/mypyc/test-data/run-generics.test
index bc78a3b8ab86..55e5adbbb4f9 100644
--- a/mypyc/test-data/run-generics.test
+++ b/mypyc/test-data/run-generics.test
@@ -27,22 +27,23 @@ def fn_typeddict(t: T) -> None:
     print([x for x in t.keys()])
     print({k: v for k, v in t.items()})
 
-fn_mapping({})
-print("=====")
-fn_mapping({"a": 1, "b": 2})
-print("=====")
+def test_mapping() -> None:
+    fn_mapping({})
+    print("=====")
+    fn_mapping({"a": 1, "b": 2})
+    print("=====")
 
-fn_union({"a": 1, "b": 2})
-print("=====")
-fn_union({"a": "1", "b": "2"})
-print("=====")
+    fn_union({"a": 1, "b": 2})
+    print("=====")
+    fn_union({"a": "1", "b": "2"})
+    print("=====")
 
-orig: Union[Dict[str, int], Dict[str, str]] = {"a": 1, "b": 2}
-fn_union(orig)
-print("=====")
+    orig: Union[Dict[str, int], Dict[str, str]] = {"a": 1, "b": 2}
+    fn_union(orig)
+    print("=====")
 
-td: TD = {"foo": 1}
-fn_typeddict(td)
+    td: TD = {"foo": 1}
+    fn_typeddict(td)
 [typing fixtures/typing-full.pyi]
 [out]
 \[]
@@ -96,8 +97,9 @@ def deco(func: Callable[P, int]) -> Callable[P, int]:
 def f(x: int, y: str) -> int:
     return x
 
-assert f(1, 'a') == 1
-assert f(2, y='b') == 2
+def test_usable() -> None:
+    assert f(1, 'a') == 1
+    assert f(2, y='b') == 2
 [out]
 \[1, 'a']
 {}
diff --git a/mypyc/test-data/run-imports.test b/mypyc/test-data/run-imports.test
index c5839d57820e..ce83a882e2de 100644
--- a/mypyc/test-data/run-imports.test
+++ b/mypyc/test-data/run-imports.test
@@ -212,9 +212,10 @@ import shared
 def do_import() -> None:
     import a
 
-assert shared.counter == 0
-do_import()
-assert shared.counter == 1
+def test_lazy() -> None:
+    assert shared.counter == 0
+    do_import()
+    assert shared.counter == 1
 
 [file a.py]
 import shared
@@ -224,9 +225,10 @@ shared.counter += 1
 counter = 0
 
 [case testDelayedImport]
-import a
-print("inbetween")
-import b
+def test_delayed() -> None:
+    import a
+    print("inbetween")
+    import b
 
 [file a.py]
 print("first")
@@ -240,19 +242,21 @@ inbetween
 last
 
 [case testImportErrorLineNumber]
-try:
-    import enum
-    import dataclasses, missing  # type: ignore[import]
-except ImportError as e:
-    line = e.__traceback__.tb_lineno # type: ignore[attr-defined]
-    assert line == 3, f"traceback's line number is {line}, expected 3"
+def test_error() -> None:
+    try:
+        import enum
+        import dataclasses, missing  # type: ignore[import]
+    except ImportError as e:
+        line = e.__traceback__.tb_lineno # type: ignore[attr-defined]
+        assert line == 4, f"traceback's line number is {line}, expected 4"
 
 [case testImportGroupIsolation]
 def func() -> None:
     import second
 
-import first
-func()
+def test_isolation() -> None:
+    import first
+    func()
 
 [file first.py]
 print("first")
diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test
index f6a1c744cade..129946a4c330 100644
--- a/mypyc/test-data/run-misc.test
+++ b/mypyc/test-data/run-misc.test
@@ -109,10 +109,11 @@ def gen(b: bool) -> Generator[Any, None, None]:
         y = None
     yield y
 
-assert f(False) == ((1, None), (None, 1))
-assert f(True) == ((None, 1), (1, None))
-assert next(gen(False)) is None
-assert next(gen(True)) == 1
+def test_inferred() -> None:
+    assert f(False) == ((1, None), (None, 1))
+    assert f(True) == ((None, 1), (1, None))
+    assert next(gen(False)) is None
+    assert next(gen(True)) == 1
 
 [case testWith]
 from typing import Any
@@ -829,23 +830,23 @@ assert call_any_nested([[1, 1, 1], [1, 1], []]) == 1
 assert call_any_nested([[1, 1, 1], [0, 1], []]) == 0
 
 [case testSum]
-[typing fixtures/typing-full.pyi]
-from typing import Any, List
+from typing import List
 
+empty: List[int] = []
 def test_sum_of_numbers() -> None:
     assert sum(x for x in [1, 2, 3]) == 6
-    assert sum(x for x in [0.0, 1.2, 2]) == 6.2
+    assert sum(x for x in [0.0, 1.2, 2]) == 3.2
     assert sum(x for x in [1, 1j]) == 1 + 1j
 
 def test_sum_callables() -> None:
-    assert sum((lambda x: x == 0)(x) for x in []) == 0
+    assert sum((lambda x: x == 0)(x) for x in empty) == 0
     assert sum((lambda x: x == 0)(x) for x in [0]) == 1
     assert sum((lambda x: x == 0)(x) for x in [0, 0, 0]) == 3
     assert sum((lambda x: x == 0)(x) for x in [0, 1, 0]) == 2
     assert sum((lambda x: x % 2 == 0)(x) for x in range(2**10)) == 2**9
 
 def test_sum_comparisons() -> None:
-    assert sum(x == 0 for x in []) == 0
+    assert sum(x == 0 for x in empty) == 0
     assert sum(x == 0 for x in [0]) == 1
     assert sum(x == 0 for x in [0, 0, 0]) == 3
     assert sum(x == 0 for x in [0, 1, 0]) == 2
@@ -865,13 +866,14 @@ def test_sum_misc() -> None:
 def test_sum_start_given() -> None:
     a = 1
     assert sum((x == 0 for x in [0, 1]), a) == 2
-    assert sum(((lambda x: x == 0)(x) for x in []), 1) == 1
+    assert sum(((lambda x: x == 0)(x) for x in empty), 1) == 1
     assert sum(((lambda x: x == 0)(x) for x in [0]), 1) == 2
     assert sum(((lambda x: x == 0)(x) for x in [0, 0, 0]), 1) == 4
     assert sum(((lambda x: x == 0)(x) for x in [0, 1, 0]), 1) == 3
     assert sum(((lambda x: x % 2 == 0)(x) for x in range(2**10)), 1) == 2**9 + 1
     assert sum((x for x in [1, 1j]), 2j) == 1 + 3j
     assert sum((c == 'd' for c in 'abcdd'), 1) == 3
+[typing fixtures/typing-full.pyi]
 
 [case testNoneStuff]
 from typing import Optional
@@ -1090,19 +1092,20 @@ def test_complex() -> None:
 from typing import cast
 import sys
 
-A = sys.platform == 'x' and foobar
-B = sys.platform == 'x' and sys.foobar
-C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)]
-C = sys.platform == 'x' and cast(a, b[c])
-C = sys.platform == 'x' and (lambda x: y + x)
-C = sys.platform == 'x' and (x for y in z)
-C = sys.platform == 'x' and [x for y in z]
-C = sys.platform == 'x' and {x: x for y in z}
-C = sys.platform == 'x' and {x for y in z}
-
-assert not A
-assert not B
-assert not C
+def test_unreachable() -> None:
+    A = sys.platform == 'x' and foobar
+    B = sys.platform == 'x' and sys.foobar
+    C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)]
+    C = sys.platform == 'x' and cast(a, b[c])
+    C = sys.platform == 'x' and (lambda x: y + x)
+    C = sys.platform == 'x' and (x for y in z)
+    C = sys.platform == 'x' and [x for y in z]
+    C = sys.platform == 'x' and {x: x for y in z}
+    C = sys.platform == 'x' and {x for y in z}
+
+    assert not A
+    assert not B
+    assert not C
 
 [case testDoesntSegfaultWhenTopLevelFails]
 # make the initial import fail
@@ -1126,6 +1129,10 @@ class B(A):
     def _(arg): pass
     def _(arg): pass
 
+def test_underscore() -> None:
+    A()
+    B()
+
 [case testGlobalRedefinition_toplevel]
 # mypy: allow-redefinition
 i = 0
diff --git a/mypyc/test-data/run-python38.test b/mypyc/test-data/run-python38.test
index 7de43907cb86..cf7c7d7dea52 100644
--- a/mypyc/test-data/run-python38.test
+++ b/mypyc/test-data/run-python38.test
@@ -75,11 +75,12 @@ class Bar(Foo):
     def f(self, *args: int, **kwargs: int) -> None:
         print("stuff", args, kwargs)
 
-z: Foo = Bar()
-z.f(1, z=50)
-z.f()
-z.f(1)
-z.f(z=50)
+def test_pos_only() -> None:
+    z: Foo = Bar()
+    z.f(1, z=50)
+    z.f()
+    z.f(1)
+    z.f(z=50)
 
 [out]
 stuff (1,) {'z': 50}
diff --git a/mypyc/test-data/run-singledispatch.test b/mypyc/test-data/run-singledispatch.test
index 61e4897c96d6..a119c325984a 100644
--- a/mypyc/test-data/run-singledispatch.test
+++ b/mypyc/test-data/run-singledispatch.test
@@ -152,12 +152,14 @@ from functools import singledispatch
 def fun(arg) -> bool:
     return False
 
-try:
-    @fun.register
-    def fun_specialized(arg: None) -> bool:
-        return True
-except TypeError:
-    pass
+def test_argument() -> None:
+    try:
+        @fun.register
+        def fun_specialized(arg: None) -> bool:
+            return True
+        assert False, "expected to raise an exception"
+    except TypeError:
+        pass
 
 [case testRegisteringTheSameFunctionSeveralTimes]
 from functools import singledispatch
@@ -598,9 +600,11 @@ assert f(1) == 'default'
 def _(arg: B) -> str:
     return 'b'
 
-assert f(A()) == 'a'
-assert f(B()) == 'b'
-assert f(1) == 'default'
+# TODO: Move whole testcase to a function when mypyc#1118 is fixed.
+def test_final() -> None:
+    assert f(A()) == 'a'
+    assert f(B()) == 'b'
+    assert f(1) == 'default'
 
 
 [case testDynamicallyRegisteringFunctionFromInterpretedCode]
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index c726c4c70896..6551d9c352df 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -363,7 +363,6 @@ def test_str_min_max() -> None:
     assert max(x, z) == 'aaa'
 
 [case testStringFormattingCStyle]
-[typing fixtures/typing-full.pyi]
 from typing import Tuple
 
 var = 'mypyc'
@@ -408,6 +407,7 @@ def test_basics() -> None:
     inf_num = float('inf')
     assert '%s, %s' % (nan_num, inf_num) == 'nan, inf'
     assert '%f, %f' % (nan_num, inf_num) == 'nan, inf'
+[typing fixtures/typing-full.pyi]
 
 [case testFStrings]
 import decimal

From 82e0eb6bc516f32aaf056d10b5d50e567b706996 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Fri, 11 Jul 2025 11:36:06 +0200
Subject: [PATCH 1453/1617] [mypyc] Add back test for next with list iterator
 (#19419)

Adding back a test for `next` with a list iterator as argument.
Previously this test only checked if compilation succeeded so I have
removed it in #19416, now it calls the test function and checks the
result.
---
 mypyc/test-data/run-lists.test | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index ee1bd27e6352..03d5741b9eca 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -486,6 +486,17 @@ def test_from_gen() -> None:
     f = list("str:" + x for x in source_str)
     assert f == ["str:a", "str:b", "str:c", "str:d"]
 
+[case testNext]
+from typing import List
+
+def get_next(x: List[int]) -> int:
+    return next((i for i in x), -1)
+
+def test_next() -> None:
+    assert get_next([]) == -1
+    assert get_next([1]) == 1
+    assert get_next([3,2,1]) == 3
+
 [case testListGetItemWithBorrow]
 from typing import List
 

From 095df178f7417f43d87e952a58bc90290a226f8f Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 11 Jul 2025 13:27:26 +0200
Subject: [PATCH 1454/1617] Allow adjacent conditionally-defined overloads
 (#19042)

Fixes #19015. Fixes #17521.
---
 mypy/fastparse.py                     | 15 ++++++++----
 test-data/unit/check-overloading.test | 34 +++++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index e2af2198cdfd..bb71242182f1 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -631,7 +631,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
         ret: list[Statement] = []
         current_overload: list[OverloadPart] = []
         current_overload_name: str | None = None
-        seen_unconditional_func_def = False
+        last_unconditional_func_def: str | None = None
         last_if_stmt: IfStmt | None = None
         last_if_overload: Decorator | FuncDef | OverloadedFuncDef | None = None
         last_if_stmt_overload_name: str | None = None
@@ -641,7 +641,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
             if_overload_name: str | None = None
             if_block_with_overload: Block | None = None
             if_unknown_truth_value: IfStmt | None = None
-            if isinstance(stmt, IfStmt) and seen_unconditional_func_def is False:
+            if isinstance(stmt, IfStmt):
                 # Check IfStmt block to determine if function overloads can be merged
                 if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name)
                 if if_overload_name is not None:
@@ -669,11 +669,18 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
                     last_if_unknown_truth_value = None
                 current_overload.append(stmt)
                 if isinstance(stmt, FuncDef):
-                    seen_unconditional_func_def = True
+                    # This is, strictly speaking, wrong: there might be a decorated
+                    # implementation. However, it only affects the error message we show:
+                    # ideally it's "already defined", but "implementation must come last"
+                    # is also reasonable.
+                    # TODO: can we get rid of this completely and just always emit
+                    # "implementation must come last" instead?
+                    last_unconditional_func_def = stmt.name
             elif (
                 current_overload_name is not None
                 and isinstance(stmt, IfStmt)
                 and if_overload_name == current_overload_name
+                and last_unconditional_func_def != current_overload_name
             ):
                 # IfStmt only contains stmts relevant to current_overload.
                 # Check if stmts are reachable and add them to current_overload,
@@ -729,7 +736,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
                 # most of mypy/mypyc assumes that all the functions in an OverloadedFuncDef are
                 # related, but multiple underscore functions next to each other aren't necessarily
                 # related
-                seen_unconditional_func_def = False
+                last_unconditional_func_def = None
                 if isinstance(stmt, Decorator) and not unnamed_function(stmt.name):
                     current_overload = [stmt]
                     current_overload_name = stmt.name
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index e427d5b21d40..0f0fc8747223 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -6310,6 +6310,40 @@ reveal_type(f12(A()))  # N: Revealed type is "__main__.A"
 
 [typing fixtures/typing-medium.pyi]
 
+[case testAdjacentConditionalOverloads]
+# flags: --always-true true_alias
+from typing import overload
+
+true_alias = True
+
+if true_alias:
+    @overload
+    def ham(v: str) -> list[str]: ...
+
+    @overload
+    def ham(v: int) -> list[int]: ...
+
+def ham(v: "int | str") -> "list[str] | list[int]":
+    return []
+
+if true_alias:
+    @overload
+    def spam(v: str) -> str: ...
+
+    @overload
+    def spam(v: int) -> int: ...
+
+def spam(v: "int | str") -> "str | int":
+    return ""
+
+reveal_type(ham)  # N: Revealed type is "Overload(def (v: builtins.str) -> builtins.list[builtins.str], def (v: builtins.int) -> builtins.list[builtins.int])"
+reveal_type(spam)  # N: Revealed type is "Overload(def (v: builtins.str) -> builtins.str, def (v: builtins.int) -> builtins.int)"
+
+reveal_type(ham(""))  # N: Revealed type is "builtins.list[builtins.str]"
+reveal_type(ham(0))  # N: Revealed type is "builtins.list[builtins.int]"
+reveal_type(spam(""))  # N: Revealed type is "builtins.str"
+reveal_type(spam(0))  # N: Revealed type is "builtins.int"
+
 [case testOverloadIfUnconditionalFuncDef]
 # flags: --always-true True --always-false False
 from typing import overload

From d0962dfa50341bb2702880ca7f9530ab292503fe Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 11 Jul 2025 13:37:31 +0100
Subject: [PATCH 1455/1617] [mypyc] Add a few native int helper irbuilder
 methods (#19423)

I will use these in a follow-up PR. These simplify IR construction a
bit. Only the most common operations have helpers for now, but we can
add more later if it seems useful.
---
 mypyc/irbuild/ll_builder.py | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 40f1d40b478b..c3ea0725cfd4 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -2104,6 +2104,33 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value:
     def compare_floats(self, lhs: Value, rhs: Value, op: int, line: int) -> Value:
         return self.add(FloatComparisonOp(lhs, rhs, op, line))
 
+    def int_add(self, lhs: Value, rhs: Value | int) -> Value:
+        """Helper to add two native integers.
+
+        The result has the type of lhs.
+        """
+        if isinstance(rhs, int):
+            rhs = Integer(rhs, lhs.type)
+        return self.int_op(lhs.type, lhs, rhs, IntOp.ADD, line=-1)
+
+    def int_sub(self, lhs: Value, rhs: Value | int) -> Value:
+        """Helper to subtract a native integer from another one.
+
+        The result has the type of lhs.
+        """
+        if isinstance(rhs, int):
+            rhs = Integer(rhs, lhs.type)
+        return self.int_op(lhs.type, lhs, rhs, IntOp.SUB, line=-1)
+
+    def int_mul(self, lhs: Value, rhs: Value | int) -> Value:
+        """Helper to multiply two native integers.
+
+        The result has the type of lhs.
+        """
+        if isinstance(rhs, int):
+            rhs = Integer(rhs, lhs.type)
+        return self.int_op(lhs.type, lhs, rhs, IntOp.MUL, line=-1)
+
     def fixed_width_int_op(
         self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int
     ) -> Value:

From 2f7ba4e52685cb81abdac81dc71a2ffe13e4dae8 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 11 Jul 2025 13:39:36 +0100
Subject: [PATCH 1456/1617] [mypyc] Remove the unused CPyList_GetItemUnsafe
 primitive (#19424)

We now generate low-level IR directly instead of calling a primitive
function.
---
 mypyc/lib-rt/CPy.h      | 1 -
 mypyc/lib-rt/list_ops.c | 8 --------
 2 files changed, 9 deletions(-)

diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index a0f1b06cc0d5..29370ab2d5d7 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -646,7 +646,6 @@ PyObject *CPyObject_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
 
 PyObject *CPyList_Build(Py_ssize_t len, ...);
 PyObject *CPyList_GetItem(PyObject *list, CPyTagged index);
-PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index);
 PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index);
 PyObject *CPyList_GetItemBorrow(PyObject *list, CPyTagged index);
 PyObject *CPyList_GetItemShortBorrow(PyObject *list, CPyTagged index);
diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index 03af8a769c0e..e141b99c091e 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -59,14 +59,6 @@ PyObject *CPyList_Copy(PyObject *list) {
     return PyObject_CallMethodNoArgs(list, name);
 }
 
-
-PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) {
-    Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
-    PyObject *result = PyList_GET_ITEM(list, n);
-    Py_INCREF(result);
-    return result;
-}
-
 PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) {
     Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
     Py_ssize_t size = PyList_GET_SIZE(list);

From db6788868b07fd13bcc8606a165f2ca90a68c76c Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 11 Jul 2025 15:45:25 +0100
Subject: [PATCH 1457/1617] [mypyc] Use native integers for some sequence
 indexing operations (#19426)

For example, when iterating over a list, now we use a native integer
for the index (which is not exposed to the user). Previously we used
tagged integers, but in these use cases they provide no real benefit.

This simplifies the IR and should slightly improve performance, as fewer
tagged int to native int conversions are needed.

Multiple ops have to be migrated in one go, as these interact with
each other, and by only changing a subset of them would actually
generate more verbose IR, as a bunch of extra coercions would be
needed.

List of impacted statements:
 * For loop over sequence
 * Assignment like `x, y = a` for tuple/list rvalue
 * Dict iteration
 * List comprehension

For example, consider this example:
```
def foo(a: list[int]) -> None:
    for x in a:
        pass
```

Old generated IR was like this:
```
def foo(a):
    a :: list
    r0 :: short_int
    r1 :: ptr
    r2 :: native_int
    r3 :: short_int
    r4 :: bit
    r5 :: native_int
    r6, r7 :: ptr
    r8 :: native_int
    r9 :: ptr
    r10 :: object
    r11 :: int
    r12 :: short_int
    r13 :: None
L0:
    r0 = 0
L1:
    r1 = get_element_ptr a ob_size :: PyVarObject
    r2 = load_mem r1 :: native_int*
    r3 = r2 << 1
    r4 = r0 < r3 :: signed
    if r4 goto L2 else goto L5 :: bool
L2:
    r5 = r0 >> 1
    r6 = get_element_ptr a ob_item :: PyListObject
    r7 = load_mem r6 :: ptr*
    r8 = r5 * 8
    r9 = r7 + r8
    r10 = load_mem r9 :: builtins.object*
    inc_ref r10
    r11 = unbox(int, r10)
    dec_ref r10
    if is_error(r11) goto L6 (error at foo:2) else goto L3
L3:
    dec_ref r11 :: int
L4:
    r12 = r0 + 2
    r0 = r12
    goto L1
L5:
    return 1
L6:
    r13 =  :: None
    return r13
```

Now the generated IR is simpler:
```
def foo(a):
    a :: list
    r0 :: native_int
    r1 :: ptr
    r2 :: native_int
    r3 :: bit
    r4, r5 :: ptr
    r6 :: native_int
    r7 :: ptr
    r8 :: object
    r9 :: int
    r10 :: native_int
    r11 :: None
L0:
    r0 = 0
L1:
    r1 = get_element_ptr a ob_size :: PyVarObject
    r2 = load_mem r1 :: native_int*
    r3 = r0 < r2 :: signed
    if r3 goto L2 else goto L5 :: bool
L2:
    r4 = get_element_ptr a ob_item :: PyListObject
    r5 = load_mem r4 :: ptr*
    r6 = r0 * 8
    r7 = r5 + r6
    r8 = load_mem r7 :: builtins.object*
    inc_ref r8
    r9 = unbox(int, r8)
    dec_ref r8
    if is_error(r9) goto L6 (error at foo:2) else goto L3
L3:
    dec_ref r9 :: int
L4:
    r10 = r0 + 1
    r0 = r10
    goto L1
L5:
    return 1
L6:
    r11 =  :: None
    return r11
```
---
 mypyc/irbuild/builder.py                |   8 +-
 mypyc/irbuild/for_helpers.py            |  22 +-
 mypyc/lib-rt/CPy.h                      |  10 +-
 mypyc/lib-rt/list_ops.c                 |  11 +-
 mypyc/lib-rt/tuple_ops.c                |  19 +-
 mypyc/primitives/dict_ops.py            |   2 +-
 mypyc/primitives/list_ops.py            |   9 +-
 mypyc/primitives/tuple_ops.py           |  19 +-
 mypyc/test-data/irbuild-basic.test      | 210 +++----
 mypyc/test-data/irbuild-dict.test       | 306 +++++----
 mypyc/test-data/irbuild-generics.test   | 790 ++++++++++++------------
 mypyc/test-data/irbuild-lists.test      | 181 +++---
 mypyc/test-data/irbuild-set.test        | 204 +++---
 mypyc/test-data/irbuild-statements.test | 294 +++++----
 mypyc/test-data/irbuild-tuple.test      | 148 +++--
 mypyc/test-data/lowering-int.test       |  54 +-
 mypyc/test-data/refcount.test           |  58 +-
 17 files changed, 1128 insertions(+), 1217 deletions(-)

diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 28ebcf2075fb..7e63d482c786 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -129,6 +129,7 @@
 from mypyc.primitives.list_ops import list_get_item_unsafe_op, list_pop_last, to_list
 from mypyc.primitives.misc_ops import check_unpack_count_op, get_module_dict_op, import_op
 from mypyc.primitives.registry import CFunctionDescription, function_ops
+from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op
 
 # These int binary operations can borrow their operands safely, since the
 # primitives take this into consideration.
@@ -772,10 +773,15 @@ def process_sequence_assignment(
         values = []
         for i in range(len(target.items)):
             item = target.items[i]
-            index = self.builder.load_int(i)
+            index: Value
             if is_list_rprimitive(rvalue.type):
+                index = Integer(i, c_pyssize_t_rprimitive)
                 item_value = self.primitive_op(list_get_item_unsafe_op, [rvalue, index], line)
+            elif is_tuple_rprimitive(rvalue.type):
+                index = Integer(i, c_pyssize_t_rprimitive)
+                item_value = self.call_c(tuple_get_item_unsafe_op, [rvalue, index], line)
             else:
+                index = self.builder.load_int(i)
                 item_value = self.builder.gen_method_call(
                     rvalue, "__getitem__", [index], item.type, line
                 )
diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index ab90a8c86b28..358f7cb76ba8 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -44,6 +44,7 @@
     RTuple,
     RType,
     bool_rprimitive,
+    c_pyssize_t_rprimitive,
     int_rprimitive,
     is_dict_rprimitive,
     is_fixed_width_rtype,
@@ -75,6 +76,7 @@
 from mypyc.primitives.misc_ops import stop_async_iteration_op
 from mypyc.primitives.registry import CFunctionDescription
 from mypyc.primitives.set_ops import set_add_op
+from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op
 
 GenFunc = Callable[[], None]
 
@@ -586,7 +588,9 @@ def gen_cleanup(self) -> None:
 
     def load_len(self, expr: Value | AssignmentTarget) -> Value:
         """A helper to get collection length, used by several subclasses."""
-        return self.builder.builder.builtin_len(self.builder.read(expr, self.line), self.line)
+        return self.builder.builder.builtin_len(
+            self.builder.read(expr, self.line), self.line, use_pyssize_t=True
+        )
 
 
 class ForIterable(ForGenerator):
@@ -766,6 +770,8 @@ def unsafe_index(builder: IRBuilder, target: Value, index: Value, line: int) ->
     # so we just check manually.
     if is_list_rprimitive(target.type):
         return builder.primitive_op(list_get_item_unsafe_op, [target, index], line)
+    elif is_tuple_rprimitive(target.type):
+        return builder.call_c(tuple_get_item_unsafe_op, [target, index], line)
     else:
         return builder.gen_method_call(target, "__getitem__", [index], None, line)
 
@@ -784,11 +790,9 @@ def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
         # environment class.
         self.expr_target = builder.maybe_spill(expr_reg)
         if not reverse:
-            index_reg: Value = Integer(0)
+            index_reg: Value = Integer(0, c_pyssize_t_rprimitive)
         else:
-            index_reg = builder.binary_op(
-                self.load_len(self.expr_target), Integer(1), "-", self.line
-            )
+            index_reg = builder.builder.int_sub(self.load_len(self.expr_target), 1)
         self.index_target = builder.maybe_spill_assignable(index_reg)
         self.target_type = target_type
 
@@ -838,13 +842,7 @@ def gen_step(self) -> None:
         builder = self.builder
         line = self.line
         step = 1 if not self.reverse else -1
-        add = builder.int_op(
-            short_int_rprimitive,
-            builder.read(self.index_target, line),
-            Integer(step),
-            IntOp.ADD,
-            line,
-        )
+        add = builder.builder.int_add(builder.read(self.index_target, line), step)
         builder.assign(self.index_target, add, line)
 
 
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 29370ab2d5d7..dba84d44f363 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -652,7 +652,7 @@ PyObject *CPyList_GetItemShortBorrow(PyObject *list, CPyTagged index);
 PyObject *CPyList_GetItemInt64(PyObject *list, int64_t index);
 PyObject *CPyList_GetItemInt64Borrow(PyObject *list, int64_t index);
 bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value);
-bool CPyList_SetItemUnsafe(PyObject *list, CPyTagged index, PyObject *value);
+void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value);
 bool CPyList_SetItemInt64(PyObject *list, int64_t index, PyObject *value);
 PyObject *CPyList_PopLast(PyObject *obj);
 PyObject *CPyList_Pop(PyObject *obj, CPyTagged index);
@@ -703,14 +703,13 @@ tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset);
 int CPyMapping_Check(PyObject *obj);
 
 // Check that dictionary didn't change size during iteration.
-static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) {
+static inline char CPyDict_CheckSize(PyObject *dict, Py_ssize_t size) {
     if (!PyDict_CheckExact(dict)) {
         // Dict subclasses will be checked by Python runtime.
         return 1;
     }
-    Py_ssize_t py_size = CPyTagged_AsSsize_t(size);
     Py_ssize_t dict_size = PyDict_Size(dict);
-    if (py_size != dict_size) {
+    if (size != dict_size) {
         PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration");
         return 0;
     }
@@ -783,7 +782,8 @@ bool CPySet_Remove(PyObject *set, PyObject *key);
 
 PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index);
 PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
-bool CPySequenceTuple_SetItemUnsafe(PyObject *tuple, CPyTagged index, PyObject *value);
+PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index);
+void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value);
 
 
 // Exception operations
diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index e141b99c091e..31a0d5cec7d5 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -231,15 +231,8 @@ bool CPyList_SetItemInt64(PyObject *list, int64_t index, PyObject *value) {
 }
 
 // This function should only be used to fill in brand new lists.
-bool CPyList_SetItemUnsafe(PyObject *list, CPyTagged index, PyObject *value) {
-    if (CPyTagged_CheckShort(index)) {
-        Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
-        PyList_SET_ITEM(list, n, value);
-        return true;
-    } else {
-        PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
-        return false;
-    }
+void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value) {
+    PyList_SET_ITEM(list, index, value);
 }
 
 PyObject *CPyList_PopLast(PyObject *obj)
diff --git a/mypyc/lib-rt/tuple_ops.c b/mypyc/lib-rt/tuple_ops.c
index 64418974666f..1df73f1907e2 100644
--- a/mypyc/lib-rt/tuple_ops.c
+++ b/mypyc/lib-rt/tuple_ops.c
@@ -46,16 +46,17 @@ PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged en
     return CPyObject_GetSlice(obj, start, end);
 }
 
+// No error checking
+PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index)
+{
+    PyObject *result = PyTuple_GET_ITEM(tuple, index);
+    Py_INCREF(result);
+    return result;
+}
+
 // PyTuple_SET_ITEM does no error checking,
 // and should only be used to fill in brand new tuples.
-bool CPySequenceTuple_SetItemUnsafe(PyObject *tuple, CPyTagged index, PyObject *value)
+void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value)
 {
-    if (CPyTagged_CheckShort(index)) {
-        Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
-        PyTuple_SET_ITEM(tuple, n, value);
-        return true;
-    } else {
-        PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
-        return false;
-    }
+    PyTuple_SET_ITEM(tuple, index, value);
 }
diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py
index ce7b9bb8d70e..3f289c3c6f08 100644
--- a/mypyc/primitives/dict_ops.py
+++ b/mypyc/primitives/dict_ops.py
@@ -289,7 +289,7 @@
 
 # check that len(dict) == const during iteration
 dict_check_size_op = custom_op(
-    arg_types=[dict_rprimitive, int_rprimitive],
+    arg_types=[dict_rprimitive, c_pyssize_t_rprimitive],
     return_type=bit_rprimitive,
     c_function_name="CPyDict_CheckSize",
     error_kind=ERR_FALSE,
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 7442e31c9118..57cb541fdbb8 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -13,6 +13,7 @@
     object_rprimitive,
     pointer_rprimitive,
     short_int_rprimitive,
+    void_rtype,
 )
 from mypyc.primitives.registry import (
     ERR_NEG_INT,
@@ -154,7 +155,7 @@
 # that is in-bounds for the list.
 list_get_item_unsafe_op = custom_primitive_op(
     name="list_get_item_unsafe",
-    arg_types=[list_rprimitive, short_int_rprimitive],
+    arg_types=[list_rprimitive, c_pyssize_t_rprimitive],
     return_type=object_rprimitive,
     error_kind=ERR_NEVER,
 )
@@ -183,10 +184,10 @@
 # PyList_SET_ITEM does no error checking,
 # and should only be used to fill in brand new lists.
 new_list_set_item_op = custom_op(
-    arg_types=[list_rprimitive, int_rprimitive, object_rprimitive],
-    return_type=bit_rprimitive,
+    arg_types=[list_rprimitive, c_pyssize_t_rprimitive, object_rprimitive],
+    return_type=void_rtype,
     c_function_name="CPyList_SetItemUnsafe",
-    error_kind=ERR_FALSE,
+    error_kind=ERR_NEVER,
     steals=[False, False, True],
 )
 
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index a9bbaa80fb5c..e680b6943d84 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -6,14 +6,14 @@
 
 from __future__ import annotations
 
-from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC
+from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
 from mypyc.ir.rtypes import (
-    bit_rprimitive,
     c_pyssize_t_rprimitive,
     int_rprimitive,
     list_rprimitive,
     object_rprimitive,
     tuple_rprimitive,
+    void_rtype,
 )
 from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op
 
@@ -29,6 +29,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# This is unsafe because it assumes that the index is a non-negative integer
+# that is in-bounds for the tuple.
+tuple_get_item_unsafe_op = custom_op(
+    arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="CPySequenceTuple_GetItemUnsafe",
+    error_kind=ERR_NEVER,
+)
+
 # Construct a boxed tuple from items: (item1, item2, ...)
 new_tuple_op = custom_op(
     arg_types=[c_pyssize_t_rprimitive],
@@ -48,10 +57,10 @@
 # PyTuple_SET_ITEM does no error checking,
 # and should only be used to fill in brand new tuples.
 new_tuple_set_item_op = custom_op(
-    arg_types=[tuple_rprimitive, int_rprimitive, object_rprimitive],
-    return_type=bit_rprimitive,
+    arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive, object_rprimitive],
+    return_type=void_rtype,
     c_function_name="CPySequenceTuple_SetItemUnsafe",
-    error_kind=ERR_FALSE,
+    error_kind=ERR_NEVER,
     steals=[False, False, True],
 )
 
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index d652cb9c9a14..ea1b3d06869a 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1895,18 +1895,16 @@ def f():
     r0, r1 :: list
     r2, r3, r4 :: object
     r5 :: ptr
-    r6 :: short_int
-    r7 :: native_int
-    r8 :: short_int
-    r9 :: bit
-    r10 :: object
-    r11, x :: int
-    r12, r13 :: bit
-    r14 :: int
-    r15 :: object
-    r16 :: i32
-    r17 :: bit
-    r18 :: short_int
+    r6, r7 :: native_int
+    r8 :: bit
+    r9 :: object
+    r10, x :: int
+    r11, r12 :: bit
+    r13 :: int
+    r14 :: object
+    r15 :: i32
+    r16 :: bit
+    r17 :: native_int
 L0:
     r0 = PyList_New(0)
     r1 = PyList_New(3)
@@ -1921,30 +1919,29 @@ L0:
     r6 = 0
 L1:
     r7 = var_object_size r1
-    r8 = r7 << 1
-    r9 = int_lt r6, r8
-    if r9 goto L2 else goto L8 :: bool
+    r8 = r6 < r7 :: signed
+    if r8 goto L2 else goto L8 :: bool
 L2:
-    r10 = list_get_item_unsafe r1, r6
-    r11 = unbox(int, r10)
-    x = r11
-    r12 = int_ne x, 4
-    if r12 goto L4 else goto L3 :: bool
+    r9 = list_get_item_unsafe r1, r6
+    r10 = unbox(int, r9)
+    x = r10
+    r11 = int_ne x, 4
+    if r11 goto L4 else goto L3 :: bool
 L3:
     goto L7
 L4:
-    r13 = int_ne x, 6
-    if r13 goto L6 else goto L5 :: bool
+    r12 = int_ne x, 6
+    if r12 goto L6 else goto L5 :: bool
 L5:
     goto L7
 L6:
-    r14 = CPyTagged_Multiply(x, x)
-    r15 = box(int, r14)
-    r16 = PyList_Append(r0, r15)
-    r17 = r16 >= 0 :: signed
+    r13 = CPyTagged_Multiply(x, x)
+    r14 = box(int, r13)
+    r15 = PyList_Append(r0, r14)
+    r16 = r15 >= 0 :: signed
 L7:
-    r18 = r6 + 2
-    r6 = r18
+    r17 = r6 + 1
+    r6 = r17
     goto L1
 L8:
     return r0
@@ -1959,18 +1956,16 @@ def f():
     r1 :: list
     r2, r3, r4 :: object
     r5 :: ptr
-    r6 :: short_int
-    r7 :: native_int
-    r8 :: short_int
-    r9 :: bit
-    r10 :: object
-    r11, x :: int
-    r12, r13 :: bit
-    r14 :: int
-    r15, r16 :: object
-    r17 :: i32
-    r18 :: bit
-    r19 :: short_int
+    r6, r7 :: native_int
+    r8 :: bit
+    r9 :: object
+    r10, x :: int
+    r11, r12 :: bit
+    r13 :: int
+    r14, r15 :: object
+    r16 :: i32
+    r17 :: bit
+    r18 :: native_int
 L0:
     r0 = PyDict_New()
     r1 = PyList_New(3)
@@ -1985,31 +1980,30 @@ L0:
     r6 = 0
 L1:
     r7 = var_object_size r1
-    r8 = r7 << 1
-    r9 = int_lt r6, r8
-    if r9 goto L2 else goto L8 :: bool
+    r8 = r6 < r7 :: signed
+    if r8 goto L2 else goto L8 :: bool
 L2:
-    r10 = list_get_item_unsafe r1, r6
-    r11 = unbox(int, r10)
-    x = r11
-    r12 = int_ne x, 4
-    if r12 goto L4 else goto L3 :: bool
+    r9 = list_get_item_unsafe r1, r6
+    r10 = unbox(int, r9)
+    x = r10
+    r11 = int_ne x, 4
+    if r11 goto L4 else goto L3 :: bool
 L3:
     goto L7
 L4:
-    r13 = int_ne x, 6
-    if r13 goto L6 else goto L5 :: bool
+    r12 = int_ne x, 6
+    if r12 goto L6 else goto L5 :: bool
 L5:
     goto L7
 L6:
-    r14 = CPyTagged_Multiply(x, x)
-    r15 = box(int, x)
-    r16 = box(int, r14)
-    r17 = CPyDict_SetItem(r0, r15, r16)
-    r18 = r17 >= 0 :: signed
+    r13 = CPyTagged_Multiply(x, x)
+    r14 = box(int, x)
+    r15 = box(int, r13)
+    r16 = CPyDict_SetItem(r0, r14, r15)
+    r17 = r16 >= 0 :: signed
 L7:
-    r19 = r6 + 2
-    r6 = r19
+    r18 = r6 + 1
+    r6 = r18
     goto L1
 L8:
     return r0
@@ -2023,74 +2017,66 @@ def f(l: List[Tuple[int, int, int]]) -> List[int]:
 [out]
 def f(l):
     l :: list
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5 :: tuple[int, int, int]
-    r6, x, r7, y, r8, z :: int
-    r9 :: short_int
-    r10 :: native_int
-    r11 :: list
-    r12 :: short_int
-    r13 :: native_int
-    r14 :: short_int
-    r15 :: bit
-    r16 :: object
-    r17 :: tuple[int, int, int]
-    r18, x_2, r19, y_2, r20, z_2, r21, r22 :: int
-    r23 :: object
-    r24 :: bit
-    r25 :: short_int
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4 :: tuple[int, int, int]
+    r5, x, r6, y, r7, z :: int
+    r8, r9 :: native_int
+    r10 :: list
+    r11, r12 :: native_int
+    r13 :: bit
+    r14 :: object
+    r15 :: tuple[int, int, int]
+    r16, x_2, r17, y_2, r18, z_2, r19, r20 :: int
+    r21 :: object
+    r22 :: native_int
 L0:
     r0 = 0
 L1:
     r1 = var_object_size l
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = list_get_item_unsafe l, r0
-    r5 = unbox(tuple[int, int, int], r4)
-    r6 = r5[0]
-    x = r6
-    r7 = r5[1]
-    y = r7
-    r8 = r5[2]
-    z = r8
+    r3 = list_get_item_unsafe l, r0
+    r4 = unbox(tuple[int, int, int], r3)
+    r5 = r4[0]
+    x = r5
+    r6 = r4[1]
+    y = r6
+    r7 = r4[2]
+    z = r7
 L3:
-    r9 = r0 + 2
-    r0 = r9
+    r8 = r0 + 1
+    r0 = r8
     goto L1
 L4:
-    r10 = var_object_size l
-    r11 = PyList_New(r10)
-    r12 = 0
+    r9 = var_object_size l
+    r10 = PyList_New(r9)
+    r11 = 0
 L5:
-    r13 = var_object_size l
-    r14 = r13 << 1
-    r15 = int_lt r12, r14
-    if r15 goto L6 else goto L8 :: bool
+    r12 = var_object_size l
+    r13 = r11 < r12 :: signed
+    if r13 goto L6 else goto L8 :: bool
 L6:
-    r16 = list_get_item_unsafe l, r12
-    r17 = unbox(tuple[int, int, int], r16)
-    r18 = r17[0]
-    x_2 = r18
-    r19 = r17[1]
-    y_2 = r19
-    r20 = r17[2]
-    z_2 = r20
-    r21 = CPyTagged_Add(x_2, y_2)
-    r22 = CPyTagged_Add(r21, z_2)
-    r23 = box(int, r22)
-    r24 = CPyList_SetItemUnsafe(r11, r12, r23)
+    r14 = list_get_item_unsafe l, r11
+    r15 = unbox(tuple[int, int, int], r14)
+    r16 = r15[0]
+    x_2 = r16
+    r17 = r15[1]
+    y_2 = r17
+    r18 = r15[2]
+    z_2 = r18
+    r19 = CPyTagged_Add(x_2, y_2)
+    r20 = CPyTagged_Add(r19, z_2)
+    r21 = box(int, r20)
+    CPyList_SetItemUnsafe(r10, r11, r21)
 L7:
-    r25 = r12 + 2
-    r12 = r25
+    r22 = r11 + 1
+    r11 = r22
     goto L5
 L8:
-    return r11
+    return r10
 
 [case testProperty]
 class PropertyHolder:
diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test
index cacb14dae273..e0c014f07813 100644
--- a/mypyc/test-data/irbuild-dict.test
+++ b/mypyc/test-data/irbuild-dict.test
@@ -152,41 +152,39 @@ def increment(d):
     d :: dict
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object]
-    r5 :: short_int
-    r6 :: bool
-    r7 :: object
-    r8, k :: str
-    r9, r10, r11 :: object
-    r12 :: i32
-    r13, r14, r15 :: bit
+    r2 :: object
+    r3 :: tuple[bool, short_int, object]
+    r4 :: short_int
+    r5 :: bool
+    r6 :: object
+    r7, k :: str
+    r8, r9, r10 :: object
+    r11 :: i32
+    r12, r13, r14 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d)
-    r2 = r1 << 1
-    r3 = CPyDict_GetKeysIter(d)
+    r2 = CPyDict_GetKeysIter(d)
 L1:
-    r4 = CPyDict_NextKey(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L4 :: bool
+    r3 = CPyDict_NextKey(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L4 :: bool
 L2:
-    r7 = r4[2]
-    r8 = cast(str, r7)
-    k = r8
-    r9 = CPyDict_GetItem(d, k)
-    r10 = object 1
-    r11 = PyNumber_InPlaceAdd(r9, r10)
-    r12 = CPyDict_SetItem(d, k, r11)
-    r13 = r12 >= 0 :: signed
+    r6 = r3[2]
+    r7 = cast(str, r6)
+    k = r7
+    r8 = CPyDict_GetItem(d, k)
+    r9 = object 1
+    r10 = PyNumber_InPlaceAdd(r8, r9)
+    r11 = CPyDict_SetItem(d, k, r10)
+    r12 = r11 >= 0 :: signed
 L3:
-    r14 = CPyDict_CheckSize(d, r2)
+    r13 = CPyDict_CheckSize(d, r1)
     goto L1
 L4:
-    r15 = CPy_NoErrOccurred()
+    r14 = CPy_NoErrOccurred()
 L5:
     return d
 
@@ -244,192 +242,184 @@ def print_dict_methods(d1, d2):
     d1, d2 :: dict
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object]
-    r5 :: short_int
-    r6 :: bool
-    r7 :: object
-    r8, v :: int
-    r9 :: object
-    r10 :: i32
-    r11 :: bit
-    r12 :: bool
-    r13, r14 :: bit
-    r15 :: short_int
-    r16 :: native_int
-    r17 :: short_int
-    r18 :: object
-    r19 :: tuple[bool, short_int, object, object]
-    r20 :: short_int
-    r21 :: bool
-    r22, r23 :: object
-    r24, r25, k :: int
-    r26, r27, r28, r29, r30 :: object
-    r31 :: i32
-    r32, r33, r34 :: bit
+    r2 :: object
+    r3 :: tuple[bool, short_int, object]
+    r4 :: short_int
+    r5 :: bool
+    r6 :: object
+    r7, v :: int
+    r8 :: object
+    r9 :: i32
+    r10 :: bit
+    r11 :: bool
+    r12, r13 :: bit
+    r14 :: short_int
+    r15 :: native_int
+    r16 :: object
+    r17 :: tuple[bool, short_int, object, object]
+    r18 :: short_int
+    r19 :: bool
+    r20, r21 :: object
+    r22, r23, k :: int
+    r24, r25, r26, r27, r28 :: object
+    r29 :: i32
+    r30, r31, r32 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d1)
-    r2 = r1 << 1
-    r3 = CPyDict_GetValuesIter(d1)
+    r2 = CPyDict_GetValuesIter(d1)
 L1:
-    r4 = CPyDict_NextValue(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L6 :: bool
+    r3 = CPyDict_NextValue(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L6 :: bool
 L2:
-    r7 = r4[2]
-    r8 = unbox(int, r7)
-    v = r8
-    r9 = box(int, v)
-    r10 = PyDict_Contains(d2, r9)
-    r11 = r10 >= 0 :: signed
-    r12 = truncate r10: i32 to builtins.bool
-    if r12 goto L3 else goto L4 :: bool
+    r6 = r3[2]
+    r7 = unbox(int, r6)
+    v = r7
+    r8 = box(int, v)
+    r9 = PyDict_Contains(d2, r8)
+    r10 = r9 >= 0 :: signed
+    r11 = truncate r9: i32 to builtins.bool
+    if r11 goto L3 else goto L4 :: bool
 L3:
     return 1
 L4:
 L5:
-    r13 = CPyDict_CheckSize(d1, r2)
+    r12 = CPyDict_CheckSize(d1, r1)
     goto L1
 L6:
-    r14 = CPy_NoErrOccurred()
+    r13 = CPy_NoErrOccurred()
 L7:
-    r15 = 0
-    r16 = PyDict_Size(d2)
-    r17 = r16 << 1
-    r18 = CPyDict_GetItemsIter(d2)
+    r14 = 0
+    r15 = PyDict_Size(d2)
+    r16 = CPyDict_GetItemsIter(d2)
 L8:
-    r19 = CPyDict_NextItem(r18, r15)
-    r20 = r19[1]
-    r15 = r20
-    r21 = r19[0]
-    if r21 goto L9 else goto L11 :: bool
+    r17 = CPyDict_NextItem(r16, r14)
+    r18 = r17[1]
+    r14 = r18
+    r19 = r17[0]
+    if r19 goto L9 else goto L11 :: bool
 L9:
-    r22 = r19[2]
-    r23 = r19[3]
-    r24 = unbox(int, r22)
-    r25 = unbox(int, r23)
-    k = r24
-    v = r25
-    r26 = box(int, k)
-    r27 = CPyDict_GetItem(d2, r26)
-    r28 = box(int, v)
-    r29 = PyNumber_InPlaceAdd(r27, r28)
-    r30 = box(int, k)
-    r31 = CPyDict_SetItem(d2, r30, r29)
-    r32 = r31 >= 0 :: signed
+    r20 = r17[2]
+    r21 = r17[3]
+    r22 = unbox(int, r20)
+    r23 = unbox(int, r21)
+    k = r22
+    v = r23
+    r24 = box(int, k)
+    r25 = CPyDict_GetItem(d2, r24)
+    r26 = box(int, v)
+    r27 = PyNumber_InPlaceAdd(r25, r26)
+    r28 = box(int, k)
+    r29 = CPyDict_SetItem(d2, r28, r27)
+    r30 = r29 >= 0 :: signed
 L10:
-    r33 = CPyDict_CheckSize(d2, r17)
+    r31 = CPyDict_CheckSize(d2, r15)
     goto L8
 L11:
-    r34 = CPy_NoErrOccurred()
+    r32 = CPy_NoErrOccurred()
 L12:
     return 1
 def union_of_dicts(d):
     d, r0, new :: dict
     r1 :: short_int
     r2 :: native_int
-    r3 :: short_int
-    r4 :: object
-    r5 :: tuple[bool, short_int, object, object]
-    r6 :: short_int
-    r7 :: bool
-    r8, r9 :: object
-    r10 :: str
-    r11 :: union[int, str]
+    r3 :: object
+    r4 :: tuple[bool, short_int, object, object]
+    r5 :: short_int
+    r6 :: bool
+    r7, r8 :: object
+    r9 :: str
+    r10 :: union[int, str]
     k :: str
     v :: union[int, str]
-    r12 :: object
-    r13 :: object[1]
-    r14 :: object_ptr
-    r15 :: object
-    r16 :: int
-    r17 :: object
-    r18 :: i32
-    r19, r20, r21 :: bit
+    r11 :: object
+    r12 :: object[1]
+    r13 :: object_ptr
+    r14 :: object
+    r15 :: int
+    r16 :: object
+    r17 :: i32
+    r18, r19, r20 :: bit
 L0:
     r0 = PyDict_New()
     new = r0
     r1 = 0
     r2 = PyDict_Size(d)
-    r3 = r2 << 1
-    r4 = CPyDict_GetItemsIter(d)
+    r3 = CPyDict_GetItemsIter(d)
 L1:
-    r5 = CPyDict_NextItem(r4, r1)
-    r6 = r5[1]
-    r1 = r6
-    r7 = r5[0]
-    if r7 goto L2 else goto L4 :: bool
+    r4 = CPyDict_NextItem(r3, r1)
+    r5 = r4[1]
+    r1 = r5
+    r6 = r4[0]
+    if r6 goto L2 else goto L4 :: bool
 L2:
-    r8 = r5[2]
-    r9 = r5[3]
-    r10 = cast(str, r8)
-    r11 = cast(union[int, str], r9)
-    k = r10
-    v = r11
-    r12 = load_address PyLong_Type
-    r13 = [v]
-    r14 = load_address r13
-    r15 = PyObject_Vectorcall(r12, r14, 1, 0)
+    r7 = r4[2]
+    r8 = r4[3]
+    r9 = cast(str, r7)
+    r10 = cast(union[int, str], r8)
+    k = r9
+    v = r10
+    r11 = load_address PyLong_Type
+    r12 = [v]
+    r13 = load_address r12
+    r14 = PyObject_Vectorcall(r11, r13, 1, 0)
     keep_alive v
-    r16 = unbox(int, r15)
-    r17 = box(int, r16)
-    r18 = CPyDict_SetItem(new, k, r17)
-    r19 = r18 >= 0 :: signed
+    r15 = unbox(int, r14)
+    r16 = box(int, r15)
+    r17 = CPyDict_SetItem(new, k, r16)
+    r18 = r17 >= 0 :: signed
 L3:
-    r20 = CPyDict_CheckSize(d, r3)
+    r19 = CPyDict_CheckSize(d, r2)
     goto L1
 L4:
-    r21 = CPy_NoErrOccurred()
+    r20 = CPy_NoErrOccurred()
 L5:
     return 1
 def typeddict(d):
     d :: dict
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object, object]
-    r5 :: short_int
-    r6 :: bool
-    r7, r8 :: object
-    r9, k :: str
+    r2 :: object
+    r3 :: tuple[bool, short_int, object, object]
+    r4 :: short_int
+    r5 :: bool
+    r6, r7 :: object
+    r8, k :: str
     v :: object
-    r10 :: str
-    r11 :: bool
+    r9 :: str
+    r10 :: bool
     name :: object
-    r12, r13 :: bit
+    r11, r12 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d)
-    r2 = r1 << 1
-    r3 = CPyDict_GetItemsIter(d)
+    r2 = CPyDict_GetItemsIter(d)
 L1:
-    r4 = CPyDict_NextItem(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L6 :: bool
+    r3 = CPyDict_NextItem(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L6 :: bool
 L2:
-    r7 = r4[2]
-    r8 = r4[3]
-    r9 = cast(str, r7)
-    k = r9
-    v = r8
-    r10 = 'name'
-    r11 = CPyStr_Equal(k, r10)
-    if r11 goto L3 else goto L4 :: bool
+    r6 = r3[2]
+    r7 = r3[3]
+    r8 = cast(str, r6)
+    k = r8
+    v = r7
+    r9 = 'name'
+    r10 = CPyStr_Equal(k, r9)
+    if r10 goto L3 else goto L4 :: bool
 L3:
     name = v
 L4:
 L5:
-    r12 = CPyDict_CheckSize(d, r2)
+    r11 = CPyDict_CheckSize(d, r1)
     goto L1
 L6:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L7:
     return 1
 
diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test
index feb7b9db20fb..d39d47e397a1 100644
--- a/mypyc/test-data/irbuild-generics.test
+++ b/mypyc/test-data/irbuild-generics.test
@@ -226,153 +226,145 @@ def fn_mapping(m):
     r0 :: list
     r1 :: short_int
     r2 :: native_int
-    r3 :: short_int
-    r4 :: object
-    r5 :: tuple[bool, short_int, object]
-    r6 :: short_int
-    r7 :: bool
-    r8 :: object
-    r9, x :: str
-    r10 :: i32
-    r11, r12, r13 :: bit
-    r14 :: list
-    r15 :: short_int
-    r16 :: native_int
-    r17 :: short_int
-    r18 :: object
-    r19 :: tuple[bool, short_int, object]
-    r20 :: short_int
-    r21 :: bool
+    r3 :: object
+    r4 :: tuple[bool, short_int, object]
+    r5 :: short_int
+    r6 :: bool
+    r7 :: object
+    r8, x :: str
+    r9 :: i32
+    r10, r11, r12 :: bit
+    r13 :: list
+    r14 :: short_int
+    r15 :: native_int
+    r16 :: object
+    r17 :: tuple[bool, short_int, object]
+    r18 :: short_int
+    r19 :: bool
+    r20 :: object
+    r21, x_2 :: int
     r22 :: object
-    r23, x_2 :: int
-    r24 :: object
-    r25 :: i32
-    r26, r27, r28 :: bit
-    r29 :: set
-    r30 :: short_int
-    r31 :: native_int
+    r23 :: i32
+    r24, r25, r26 :: bit
+    r27 :: set
+    r28 :: short_int
+    r29 :: native_int
+    r30 :: object
+    r31 :: tuple[bool, short_int, object]
     r32 :: short_int
-    r33 :: object
-    r34 :: tuple[bool, short_int, object]
-    r35 :: short_int
-    r36 :: bool
-    r37 :: object
-    r38, x_3 :: str
-    r39 :: i32
-    r40, r41, r42 :: bit
-    r43 :: dict
-    r44 :: short_int
-    r45 :: native_int
-    r46 :: short_int
-    r47 :: object
-    r48 :: tuple[bool, short_int, object, object]
-    r49 :: short_int
-    r50 :: bool
-    r51, r52 :: object
-    r53 :: str
-    r54 :: int
+    r33 :: bool
+    r34 :: object
+    r35, x_3 :: str
+    r36 :: i32
+    r37, r38, r39 :: bit
+    r40 :: dict
+    r41 :: short_int
+    r42 :: native_int
+    r43 :: object
+    r44 :: tuple[bool, short_int, object, object]
+    r45 :: short_int
+    r46 :: bool
+    r47, r48 :: object
+    r49 :: str
+    r50 :: int
     k :: str
     v :: int
-    r55 :: object
-    r56 :: i32
-    r57, r58, r59 :: bit
+    r51 :: object
+    r52 :: i32
+    r53, r54, r55 :: bit
 L0:
     r0 = PyList_New(0)
     r1 = 0
     r2 = PyDict_Size(m)
-    r3 = r2 << 1
-    r4 = CPyDict_GetKeysIter(m)
+    r3 = CPyDict_GetKeysIter(m)
 L1:
-    r5 = CPyDict_NextKey(r4, r1)
-    r6 = r5[1]
-    r1 = r6
-    r7 = r5[0]
-    if r7 goto L2 else goto L4 :: bool
+    r4 = CPyDict_NextKey(r3, r1)
+    r5 = r4[1]
+    r1 = r5
+    r6 = r4[0]
+    if r6 goto L2 else goto L4 :: bool
 L2:
-    r8 = r5[2]
-    r9 = cast(str, r8)
-    x = r9
-    r10 = PyList_Append(r0, x)
-    r11 = r10 >= 0 :: signed
+    r7 = r4[2]
+    r8 = cast(str, r7)
+    x = r8
+    r9 = PyList_Append(r0, x)
+    r10 = r9 >= 0 :: signed
 L3:
-    r12 = CPyDict_CheckSize(m, r3)
+    r11 = CPyDict_CheckSize(m, r2)
     goto L1
 L4:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L5:
-    r14 = PyList_New(0)
-    r15 = 0
-    r16 = PyDict_Size(m)
-    r17 = r16 << 1
-    r18 = CPyDict_GetValuesIter(m)
+    r13 = PyList_New(0)
+    r14 = 0
+    r15 = PyDict_Size(m)
+    r16 = CPyDict_GetValuesIter(m)
 L6:
-    r19 = CPyDict_NextValue(r18, r15)
-    r20 = r19[1]
-    r15 = r20
-    r21 = r19[0]
-    if r21 goto L7 else goto L9 :: bool
+    r17 = CPyDict_NextValue(r16, r14)
+    r18 = r17[1]
+    r14 = r18
+    r19 = r17[0]
+    if r19 goto L7 else goto L9 :: bool
 L7:
-    r22 = r19[2]
-    r23 = unbox(int, r22)
-    x_2 = r23
-    r24 = box(int, x_2)
-    r25 = PyList_Append(r14, r24)
-    r26 = r25 >= 0 :: signed
+    r20 = r17[2]
+    r21 = unbox(int, r20)
+    x_2 = r21
+    r22 = box(int, x_2)
+    r23 = PyList_Append(r13, r22)
+    r24 = r23 >= 0 :: signed
 L8:
-    r27 = CPyDict_CheckSize(m, r17)
+    r25 = CPyDict_CheckSize(m, r15)
     goto L6
 L9:
-    r28 = CPy_NoErrOccurred()
+    r26 = CPy_NoErrOccurred()
 L10:
-    r29 = PySet_New(0)
-    r30 = 0
-    r31 = PyDict_Size(m)
-    r32 = r31 << 1
-    r33 = CPyDict_GetKeysIter(m)
+    r27 = PySet_New(0)
+    r28 = 0
+    r29 = PyDict_Size(m)
+    r30 = CPyDict_GetKeysIter(m)
 L11:
-    r34 = CPyDict_NextKey(r33, r30)
-    r35 = r34[1]
-    r30 = r35
-    r36 = r34[0]
-    if r36 goto L12 else goto L14 :: bool
+    r31 = CPyDict_NextKey(r30, r28)
+    r32 = r31[1]
+    r28 = r32
+    r33 = r31[0]
+    if r33 goto L12 else goto L14 :: bool
 L12:
-    r37 = r34[2]
-    r38 = cast(str, r37)
-    x_3 = r38
-    r39 = PySet_Add(r29, x_3)
-    r40 = r39 >= 0 :: signed
+    r34 = r31[2]
+    r35 = cast(str, r34)
+    x_3 = r35
+    r36 = PySet_Add(r27, x_3)
+    r37 = r36 >= 0 :: signed
 L13:
-    r41 = CPyDict_CheckSize(m, r32)
+    r38 = CPyDict_CheckSize(m, r29)
     goto L11
 L14:
-    r42 = CPy_NoErrOccurred()
+    r39 = CPy_NoErrOccurred()
 L15:
-    r43 = PyDict_New()
-    r44 = 0
-    r45 = PyDict_Size(m)
-    r46 = r45 << 1
-    r47 = CPyDict_GetItemsIter(m)
+    r40 = PyDict_New()
+    r41 = 0
+    r42 = PyDict_Size(m)
+    r43 = CPyDict_GetItemsIter(m)
 L16:
-    r48 = CPyDict_NextItem(r47, r44)
-    r49 = r48[1]
-    r44 = r49
-    r50 = r48[0]
-    if r50 goto L17 else goto L19 :: bool
+    r44 = CPyDict_NextItem(r43, r41)
+    r45 = r44[1]
+    r41 = r45
+    r46 = r44[0]
+    if r46 goto L17 else goto L19 :: bool
 L17:
-    r51 = r48[2]
-    r52 = r48[3]
-    r53 = cast(str, r51)
-    r54 = unbox(int, r52)
-    k = r53
-    v = r54
-    r55 = box(int, v)
-    r56 = CPyDict_SetItem(r43, k, r55)
-    r57 = r56 >= 0 :: signed
+    r47 = r44[2]
+    r48 = r44[3]
+    r49 = cast(str, r47)
+    r50 = unbox(int, r48)
+    k = r49
+    v = r50
+    r51 = box(int, v)
+    r52 = CPyDict_SetItem(r40, k, r51)
+    r53 = r52 >= 0 :: signed
 L18:
-    r58 = CPyDict_CheckSize(m, r46)
+    r54 = CPyDict_CheckSize(m, r42)
     goto L16
 L19:
-    r59 = CPy_NoErrOccurred()
+    r55 = CPy_NoErrOccurred()
 L20:
     return 1
 def fn_union(m):
@@ -380,149 +372,141 @@ def fn_union(m):
     r0 :: list
     r1 :: short_int
     r2 :: native_int
-    r3 :: short_int
-    r4 :: object
-    r5 :: tuple[bool, short_int, object]
-    r6 :: short_int
-    r7 :: bool
-    r8 :: object
-    r9, x :: str
-    r10 :: i32
-    r11, r12, r13 :: bit
-    r14 :: list
-    r15 :: short_int
-    r16 :: native_int
-    r17 :: short_int
-    r18 :: object
-    r19 :: tuple[bool, short_int, object]
-    r20 :: short_int
-    r21 :: bool
-    r22 :: object
-    r23, x_2 :: union[int, str]
-    r24 :: i32
-    r25, r26, r27 :: bit
-    r28 :: set
-    r29 :: short_int
-    r30 :: native_int
+    r3 :: object
+    r4 :: tuple[bool, short_int, object]
+    r5 :: short_int
+    r6 :: bool
+    r7 :: object
+    r8, x :: str
+    r9 :: i32
+    r10, r11, r12 :: bit
+    r13 :: list
+    r14 :: short_int
+    r15 :: native_int
+    r16 :: object
+    r17 :: tuple[bool, short_int, object]
+    r18 :: short_int
+    r19 :: bool
+    r20 :: object
+    r21, x_2 :: union[int, str]
+    r22 :: i32
+    r23, r24, r25 :: bit
+    r26 :: set
+    r27 :: short_int
+    r28 :: native_int
+    r29 :: object
+    r30 :: tuple[bool, short_int, object]
     r31 :: short_int
-    r32 :: object
-    r33 :: tuple[bool, short_int, object]
-    r34 :: short_int
-    r35 :: bool
-    r36 :: object
-    r37, x_3 :: str
-    r38 :: i32
-    r39, r40, r41 :: bit
-    r42 :: dict
-    r43 :: short_int
-    r44 :: native_int
-    r45 :: short_int
-    r46 :: object
-    r47 :: tuple[bool, short_int, object, object]
-    r48 :: short_int
-    r49 :: bool
-    r50, r51 :: object
-    r52 :: str
-    r53 :: union[int, str]
+    r32 :: bool
+    r33 :: object
+    r34, x_3 :: str
+    r35 :: i32
+    r36, r37, r38 :: bit
+    r39 :: dict
+    r40 :: short_int
+    r41 :: native_int
+    r42 :: object
+    r43 :: tuple[bool, short_int, object, object]
+    r44 :: short_int
+    r45 :: bool
+    r46, r47 :: object
+    r48 :: str
+    r49 :: union[int, str]
     k :: str
     v :: union[int, str]
-    r54 :: i32
-    r55, r56, r57 :: bit
+    r50 :: i32
+    r51, r52, r53 :: bit
 L0:
     r0 = PyList_New(0)
     r1 = 0
     r2 = PyDict_Size(m)
-    r3 = r2 << 1
-    r4 = CPyDict_GetKeysIter(m)
+    r3 = CPyDict_GetKeysIter(m)
 L1:
-    r5 = CPyDict_NextKey(r4, r1)
-    r6 = r5[1]
-    r1 = r6
-    r7 = r5[0]
-    if r7 goto L2 else goto L4 :: bool
+    r4 = CPyDict_NextKey(r3, r1)
+    r5 = r4[1]
+    r1 = r5
+    r6 = r4[0]
+    if r6 goto L2 else goto L4 :: bool
 L2:
-    r8 = r5[2]
-    r9 = cast(str, r8)
-    x = r9
-    r10 = PyList_Append(r0, x)
-    r11 = r10 >= 0 :: signed
+    r7 = r4[2]
+    r8 = cast(str, r7)
+    x = r8
+    r9 = PyList_Append(r0, x)
+    r10 = r9 >= 0 :: signed
 L3:
-    r12 = CPyDict_CheckSize(m, r3)
+    r11 = CPyDict_CheckSize(m, r2)
     goto L1
 L4:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L5:
-    r14 = PyList_New(0)
-    r15 = 0
-    r16 = PyDict_Size(m)
-    r17 = r16 << 1
-    r18 = CPyDict_GetValuesIter(m)
+    r13 = PyList_New(0)
+    r14 = 0
+    r15 = PyDict_Size(m)
+    r16 = CPyDict_GetValuesIter(m)
 L6:
-    r19 = CPyDict_NextValue(r18, r15)
-    r20 = r19[1]
-    r15 = r20
-    r21 = r19[0]
-    if r21 goto L7 else goto L9 :: bool
+    r17 = CPyDict_NextValue(r16, r14)
+    r18 = r17[1]
+    r14 = r18
+    r19 = r17[0]
+    if r19 goto L7 else goto L9 :: bool
 L7:
-    r22 = r19[2]
-    r23 = cast(union[int, str], r22)
-    x_2 = r23
-    r24 = PyList_Append(r14, x_2)
-    r25 = r24 >= 0 :: signed
+    r20 = r17[2]
+    r21 = cast(union[int, str], r20)
+    x_2 = r21
+    r22 = PyList_Append(r13, x_2)
+    r23 = r22 >= 0 :: signed
 L8:
-    r26 = CPyDict_CheckSize(m, r17)
+    r24 = CPyDict_CheckSize(m, r15)
     goto L6
 L9:
-    r27 = CPy_NoErrOccurred()
+    r25 = CPy_NoErrOccurred()
 L10:
-    r28 = PySet_New(0)
-    r29 = 0
-    r30 = PyDict_Size(m)
-    r31 = r30 << 1
-    r32 = CPyDict_GetKeysIter(m)
+    r26 = PySet_New(0)
+    r27 = 0
+    r28 = PyDict_Size(m)
+    r29 = CPyDict_GetKeysIter(m)
 L11:
-    r33 = CPyDict_NextKey(r32, r29)
-    r34 = r33[1]
-    r29 = r34
-    r35 = r33[0]
-    if r35 goto L12 else goto L14 :: bool
+    r30 = CPyDict_NextKey(r29, r27)
+    r31 = r30[1]
+    r27 = r31
+    r32 = r30[0]
+    if r32 goto L12 else goto L14 :: bool
 L12:
-    r36 = r33[2]
-    r37 = cast(str, r36)
-    x_3 = r37
-    r38 = PySet_Add(r28, x_3)
-    r39 = r38 >= 0 :: signed
+    r33 = r30[2]
+    r34 = cast(str, r33)
+    x_3 = r34
+    r35 = PySet_Add(r26, x_3)
+    r36 = r35 >= 0 :: signed
 L13:
-    r40 = CPyDict_CheckSize(m, r31)
+    r37 = CPyDict_CheckSize(m, r28)
     goto L11
 L14:
-    r41 = CPy_NoErrOccurred()
+    r38 = CPy_NoErrOccurred()
 L15:
-    r42 = PyDict_New()
-    r43 = 0
-    r44 = PyDict_Size(m)
-    r45 = r44 << 1
-    r46 = CPyDict_GetItemsIter(m)
+    r39 = PyDict_New()
+    r40 = 0
+    r41 = PyDict_Size(m)
+    r42 = CPyDict_GetItemsIter(m)
 L16:
-    r47 = CPyDict_NextItem(r46, r43)
-    r48 = r47[1]
-    r43 = r48
-    r49 = r47[0]
-    if r49 goto L17 else goto L19 :: bool
+    r43 = CPyDict_NextItem(r42, r40)
+    r44 = r43[1]
+    r40 = r44
+    r45 = r43[0]
+    if r45 goto L17 else goto L19 :: bool
 L17:
-    r50 = r47[2]
-    r51 = r47[3]
-    r52 = cast(str, r50)
-    r53 = cast(union[int, str], r51)
-    k = r52
-    v = r53
-    r54 = CPyDict_SetItem(r42, k, v)
-    r55 = r54 >= 0 :: signed
+    r46 = r43[2]
+    r47 = r43[3]
+    r48 = cast(str, r46)
+    r49 = cast(union[int, str], r47)
+    k = r48
+    v = r49
+    r50 = CPyDict_SetItem(r39, k, v)
+    r51 = r50 >= 0 :: signed
 L18:
-    r56 = CPyDict_CheckSize(m, r45)
+    r52 = CPyDict_CheckSize(m, r41)
     goto L16
 L19:
-    r57 = CPy_NoErrOccurred()
+    r53 = CPy_NoErrOccurred()
 L20:
     return 1
 def fn_typeddict(t):
@@ -530,144 +514,136 @@ def fn_typeddict(t):
     r0 :: list
     r1 :: short_int
     r2 :: native_int
-    r3 :: short_int
-    r4 :: object
-    r5 :: tuple[bool, short_int, object]
-    r6 :: short_int
-    r7 :: bool
-    r8 :: object
-    r9, x :: str
-    r10 :: i32
-    r11, r12, r13 :: bit
-    r14 :: list
-    r15 :: short_int
-    r16 :: native_int
-    r17 :: short_int
-    r18 :: object
-    r19 :: tuple[bool, short_int, object]
-    r20 :: short_int
-    r21 :: bool
-    r22, x_2 :: object
-    r23 :: i32
-    r24, r25, r26 :: bit
-    r27 :: set
-    r28 :: short_int
-    r29 :: native_int
+    r3 :: object
+    r4 :: tuple[bool, short_int, object]
+    r5 :: short_int
+    r6 :: bool
+    r7 :: object
+    r8, x :: str
+    r9 :: i32
+    r10, r11, r12 :: bit
+    r13 :: list
+    r14 :: short_int
+    r15 :: native_int
+    r16 :: object
+    r17 :: tuple[bool, short_int, object]
+    r18 :: short_int
+    r19 :: bool
+    r20, x_2 :: object
+    r21 :: i32
+    r22, r23, r24 :: bit
+    r25 :: set
+    r26 :: short_int
+    r27 :: native_int
+    r28 :: object
+    r29 :: tuple[bool, short_int, object]
     r30 :: short_int
-    r31 :: object
-    r32 :: tuple[bool, short_int, object]
-    r33 :: short_int
-    r34 :: bool
-    r35 :: object
-    r36, x_3 :: str
-    r37 :: i32
-    r38, r39, r40 :: bit
-    r41 :: dict
-    r42 :: short_int
-    r43 :: native_int
-    r44 :: short_int
-    r45 :: object
-    r46 :: tuple[bool, short_int, object, object]
-    r47 :: short_int
-    r48 :: bool
-    r49, r50 :: object
-    r51, k :: str
+    r31 :: bool
+    r32 :: object
+    r33, x_3 :: str
+    r34 :: i32
+    r35, r36, r37 :: bit
+    r38 :: dict
+    r39 :: short_int
+    r40 :: native_int
+    r41 :: object
+    r42 :: tuple[bool, short_int, object, object]
+    r43 :: short_int
+    r44 :: bool
+    r45, r46 :: object
+    r47, k :: str
     v :: object
-    r52 :: i32
-    r53, r54, r55 :: bit
+    r48 :: i32
+    r49, r50, r51 :: bit
 L0:
     r0 = PyList_New(0)
     r1 = 0
     r2 = PyDict_Size(t)
-    r3 = r2 << 1
-    r4 = CPyDict_GetKeysIter(t)
+    r3 = CPyDict_GetKeysIter(t)
 L1:
-    r5 = CPyDict_NextKey(r4, r1)
-    r6 = r5[1]
-    r1 = r6
-    r7 = r5[0]
-    if r7 goto L2 else goto L4 :: bool
+    r4 = CPyDict_NextKey(r3, r1)
+    r5 = r4[1]
+    r1 = r5
+    r6 = r4[0]
+    if r6 goto L2 else goto L4 :: bool
 L2:
-    r8 = r5[2]
-    r9 = cast(str, r8)
-    x = r9
-    r10 = PyList_Append(r0, x)
-    r11 = r10 >= 0 :: signed
+    r7 = r4[2]
+    r8 = cast(str, r7)
+    x = r8
+    r9 = PyList_Append(r0, x)
+    r10 = r9 >= 0 :: signed
 L3:
-    r12 = CPyDict_CheckSize(t, r3)
+    r11 = CPyDict_CheckSize(t, r2)
     goto L1
 L4:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L5:
-    r14 = PyList_New(0)
-    r15 = 0
-    r16 = PyDict_Size(t)
-    r17 = r16 << 1
-    r18 = CPyDict_GetValuesIter(t)
+    r13 = PyList_New(0)
+    r14 = 0
+    r15 = PyDict_Size(t)
+    r16 = CPyDict_GetValuesIter(t)
 L6:
-    r19 = CPyDict_NextValue(r18, r15)
-    r20 = r19[1]
-    r15 = r20
-    r21 = r19[0]
-    if r21 goto L7 else goto L9 :: bool
+    r17 = CPyDict_NextValue(r16, r14)
+    r18 = r17[1]
+    r14 = r18
+    r19 = r17[0]
+    if r19 goto L7 else goto L9 :: bool
 L7:
-    r22 = r19[2]
-    x_2 = r22
-    r23 = PyList_Append(r14, x_2)
-    r24 = r23 >= 0 :: signed
+    r20 = r17[2]
+    x_2 = r20
+    r21 = PyList_Append(r13, x_2)
+    r22 = r21 >= 0 :: signed
 L8:
-    r25 = CPyDict_CheckSize(t, r17)
+    r23 = CPyDict_CheckSize(t, r15)
     goto L6
 L9:
-    r26 = CPy_NoErrOccurred()
+    r24 = CPy_NoErrOccurred()
 L10:
-    r27 = PySet_New(0)
-    r28 = 0
-    r29 = PyDict_Size(t)
-    r30 = r29 << 1
-    r31 = CPyDict_GetKeysIter(t)
+    r25 = PySet_New(0)
+    r26 = 0
+    r27 = PyDict_Size(t)
+    r28 = CPyDict_GetKeysIter(t)
 L11:
-    r32 = CPyDict_NextKey(r31, r28)
-    r33 = r32[1]
-    r28 = r33
-    r34 = r32[0]
-    if r34 goto L12 else goto L14 :: bool
+    r29 = CPyDict_NextKey(r28, r26)
+    r30 = r29[1]
+    r26 = r30
+    r31 = r29[0]
+    if r31 goto L12 else goto L14 :: bool
 L12:
-    r35 = r32[2]
-    r36 = cast(str, r35)
-    x_3 = r36
-    r37 = PySet_Add(r27, x_3)
-    r38 = r37 >= 0 :: signed
+    r32 = r29[2]
+    r33 = cast(str, r32)
+    x_3 = r33
+    r34 = PySet_Add(r25, x_3)
+    r35 = r34 >= 0 :: signed
 L13:
-    r39 = CPyDict_CheckSize(t, r30)
+    r36 = CPyDict_CheckSize(t, r27)
     goto L11
 L14:
-    r40 = CPy_NoErrOccurred()
+    r37 = CPy_NoErrOccurred()
 L15:
-    r41 = PyDict_New()
-    r42 = 0
-    r43 = PyDict_Size(t)
-    r44 = r43 << 1
-    r45 = CPyDict_GetItemsIter(t)
+    r38 = PyDict_New()
+    r39 = 0
+    r40 = PyDict_Size(t)
+    r41 = CPyDict_GetItemsIter(t)
 L16:
-    r46 = CPyDict_NextItem(r45, r42)
-    r47 = r46[1]
-    r42 = r47
-    r48 = r46[0]
-    if r48 goto L17 else goto L19 :: bool
+    r42 = CPyDict_NextItem(r41, r39)
+    r43 = r42[1]
+    r39 = r43
+    r44 = r42[0]
+    if r44 goto L17 else goto L19 :: bool
 L17:
-    r49 = r46[2]
-    r50 = r46[3]
-    r51 = cast(str, r49)
-    k = r51
-    v = r50
-    r52 = CPyDict_SetItem(r41, k, v)
-    r53 = r52 >= 0 :: signed
+    r45 = r42[2]
+    r46 = r42[3]
+    r47 = cast(str, r45)
+    k = r47
+    v = r46
+    r48 = CPyDict_SetItem(r38, k, v)
+    r49 = r48 >= 0 :: signed
 L18:
-    r54 = CPyDict_CheckSize(t, r44)
+    r50 = CPyDict_CheckSize(t, r40)
     goto L16
 L19:
-    r55 = CPy_NoErrOccurred()
+    r51 = CPy_NoErrOccurred()
 L20:
     return 1
 
@@ -713,38 +689,34 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs):
     r0 :: __main__.deco_env
     r1 :: native_int
     r2 :: list
-    r3 :: short_int
-    r4 :: native_int
-    r5 :: short_int
-    r6 :: bit
-    r7, x :: object
-    r8 :: bit
-    r9 :: short_int
+    r3, r4 :: native_int
+    r5 :: bit
+    r6, x :: object
+    r7 :: native_int
     can_listcomp :: list
-    r10 :: dict
-    r11 :: short_int
-    r12 :: native_int
+    r8 :: dict
+    r9 :: short_int
+    r10 :: native_int
+    r11 :: object
+    r12 :: tuple[bool, short_int, object, object]
     r13 :: short_int
-    r14 :: object
-    r15 :: tuple[bool, short_int, object, object]
-    r16 :: short_int
-    r17 :: bool
-    r18, r19 :: object
-    r20, k :: str
+    r14 :: bool
+    r15, r16 :: object
+    r17, k :: str
     v :: object
-    r21 :: i32
-    r22, r23, r24 :: bit
+    r18 :: i32
+    r19, r20, r21 :: bit
     can_dictcomp :: dict
-    r25, can_iter, r26, can_use_keys, r27, can_use_values :: list
-    r28 :: object
-    r29 :: list
-    r30 :: object
-    r31 :: dict
-    r32 :: i32
-    r33 :: bit
-    r34 :: tuple
-    r35 :: object
-    r36 :: int
+    r22, can_iter, r23, can_use_keys, r24, can_use_values :: list
+    r25 :: object
+    r26 :: list
+    r27 :: object
+    r28 :: dict
+    r29 :: i32
+    r30 :: bit
+    r31 :: tuple
+    r32 :: object
+    r33 :: int
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = var_object_size args
@@ -752,61 +724,59 @@ L0:
     r3 = 0
 L1:
     r4 = var_object_size args
-    r5 = r4 << 1
-    r6 = int_lt r3, r5
-    if r6 goto L2 else goto L4 :: bool
+    r5 = r3 < r4 :: signed
+    if r5 goto L2 else goto L4 :: bool
 L2:
-    r7 = CPySequenceTuple_GetItem(args, r3)
-    x = r7
-    r8 = CPyList_SetItemUnsafe(r2, r3, x)
+    r6 = CPySequenceTuple_GetItemUnsafe(args, r3)
+    x = r6
+    CPyList_SetItemUnsafe(r2, r3, x)
 L3:
-    r9 = r3 + 2
-    r3 = r9
+    r7 = r3 + 1
+    r3 = r7
     goto L1
 L4:
     can_listcomp = r2
-    r10 = PyDict_New()
-    r11 = 0
-    r12 = PyDict_Size(kwargs)
-    r13 = r12 << 1
-    r14 = CPyDict_GetItemsIter(kwargs)
+    r8 = PyDict_New()
+    r9 = 0
+    r10 = PyDict_Size(kwargs)
+    r11 = CPyDict_GetItemsIter(kwargs)
 L5:
-    r15 = CPyDict_NextItem(r14, r11)
-    r16 = r15[1]
-    r11 = r16
-    r17 = r15[0]
-    if r17 goto L6 else goto L8 :: bool
+    r12 = CPyDict_NextItem(r11, r9)
+    r13 = r12[1]
+    r9 = r13
+    r14 = r12[0]
+    if r14 goto L6 else goto L8 :: bool
 L6:
-    r18 = r15[2]
-    r19 = r15[3]
-    r20 = cast(str, r18)
-    k = r20
-    v = r19
-    r21 = CPyDict_SetItem(r10, k, v)
-    r22 = r21 >= 0 :: signed
+    r15 = r12[2]
+    r16 = r12[3]
+    r17 = cast(str, r15)
+    k = r17
+    v = r16
+    r18 = CPyDict_SetItem(r8, k, v)
+    r19 = r18 >= 0 :: signed
 L7:
-    r23 = CPyDict_CheckSize(kwargs, r13)
+    r20 = CPyDict_CheckSize(kwargs, r10)
     goto L5
 L8:
-    r24 = CPy_NoErrOccurred()
+    r21 = CPy_NoErrOccurred()
 L9:
-    can_dictcomp = r10
-    r25 = PySequence_List(kwargs)
-    can_iter = r25
-    r26 = CPyDict_Keys(kwargs)
-    can_use_keys = r26
-    r27 = CPyDict_Values(kwargs)
-    can_use_values = r27
-    r28 = r0.func
-    r29 = PyList_New(0)
-    r30 = CPyList_Extend(r29, args)
-    r31 = PyDict_New()
-    r32 = CPyDict_UpdateInDisplay(r31, kwargs)
-    r33 = r32 >= 0 :: signed
-    r34 = PyList_AsTuple(r29)
-    r35 = PyObject_Call(r28, r34, r31)
-    r36 = unbox(int, r35)
-    return r36
+    can_dictcomp = r8
+    r22 = PySequence_List(kwargs)
+    can_iter = r22
+    r23 = CPyDict_Keys(kwargs)
+    can_use_keys = r23
+    r24 = CPyDict_Values(kwargs)
+    can_use_values = r24
+    r25 = r0.func
+    r26 = PyList_New(0)
+    r27 = CPyList_Extend(r26, args)
+    r28 = PyDict_New()
+    r29 = CPyDict_UpdateInDisplay(r28, kwargs)
+    r30 = r29 >= 0 :: signed
+    r31 = PyList_AsTuple(r26)
+    r32 = PyObject_Call(r25, r31, r28)
+    r33 = unbox(int, r32)
+    return r33
 def deco(func):
     func :: object
     r0 :: __main__.deco_env
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index efd38870974d..06120e077af9 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -371,27 +371,21 @@ def f(source):
     source :: list
     r0 :: native_int
     r1 :: list
-    r2 :: short_int
-    r3 :: native_int
-    r4 :: short_int
-    r5 :: bit
-    r6 :: object
-    r7, x, r8 :: int
-    r9 :: object
-    r10 :: bit
-    r11 :: short_int
+    r2, r3 :: native_int
+    r4 :: bit
+    r5 :: object
+    r6, x, r7 :: int
+    r8 :: object
+    r9 :: native_int
     a :: list
-    r12 :: native_int
-    r13 :: list
-    r14 :: short_int
-    r15 :: native_int
-    r16 :: short_int
-    r17 :: bit
+    r10 :: native_int
+    r11 :: list
+    r12, r13 :: native_int
+    r14 :: bit
+    r15 :: object
+    r16, x_2, r17 :: int
     r18 :: object
-    r19, x_2, r20 :: int
-    r21 :: object
-    r22 :: bit
-    r23 :: short_int
+    r19 :: native_int
     b :: list
 L0:
     r0 = var_object_size source
@@ -399,43 +393,41 @@ L0:
     r2 = 0
 L1:
     r3 = var_object_size source
-    r4 = r3 << 1
-    r5 = int_lt r2, r4
-    if r5 goto L2 else goto L4 :: bool
+    r4 = r2 < r3 :: signed
+    if r4 goto L2 else goto L4 :: bool
 L2:
-    r6 = list_get_item_unsafe source, r2
-    r7 = unbox(int, r6)
-    x = r7
-    r8 = CPyTagged_Add(x, 2)
-    r9 = box(int, r8)
-    r10 = CPyList_SetItemUnsafe(r1, r2, r9)
+    r5 = list_get_item_unsafe source, r2
+    r6 = unbox(int, r5)
+    x = r6
+    r7 = CPyTagged_Add(x, 2)
+    r8 = box(int, r7)
+    CPyList_SetItemUnsafe(r1, r2, r8)
 L3:
-    r11 = r2 + 2
-    r2 = r11
+    r9 = r2 + 1
+    r2 = r9
     goto L1
 L4:
     a = r1
-    r12 = var_object_size source
-    r13 = PyList_New(r12)
-    r14 = 0
+    r10 = var_object_size source
+    r11 = PyList_New(r10)
+    r12 = 0
 L5:
-    r15 = var_object_size source
-    r16 = r15 << 1
-    r17 = int_lt r14, r16
-    if r17 goto L6 else goto L8 :: bool
+    r13 = var_object_size source
+    r14 = r12 < r13 :: signed
+    if r14 goto L6 else goto L8 :: bool
 L6:
-    r18 = list_get_item_unsafe source, r14
-    r19 = unbox(int, r18)
-    x_2 = r19
-    r20 = CPyTagged_Add(x_2, 2)
-    r21 = box(int, r20)
-    r22 = CPyList_SetItemUnsafe(r13, r14, r21)
+    r15 = list_get_item_unsafe source, r12
+    r16 = unbox(int, r15)
+    x_2 = r16
+    r17 = CPyTagged_Add(x_2, 2)
+    r18 = box(int, r17)
+    CPyList_SetItemUnsafe(r11, r12, r18)
 L7:
-    r23 = r14 + 2
-    r14 = r23
+    r19 = r12 + 1
+    r12 = r19
     goto L5
 L8:
-    b = r13
+    b = r11
     return 1
 
 [case testGeneratorNext]
@@ -446,40 +438,37 @@ def test(x: List[int]) -> None:
 [out]
 def test(x):
     x :: list
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5, i :: int
-    r6 :: object
-    r7 :: union[int, None]
-    r8 :: short_int
-    r9 :: object
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4, i :: int
+    r5 :: object
+    r6 :: union[int, None]
+    r7 :: native_int
+    r8 :: object
     res :: union[int, None]
 L0:
     r0 = 0
 L1:
     r1 = var_object_size x
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = list_get_item_unsafe x, r0
-    r5 = unbox(int, r4)
-    i = r5
-    r6 = box(int, i)
-    r7 = r6
+    r3 = list_get_item_unsafe x, r0
+    r4 = unbox(int, r3)
+    i = r4
+    r5 = box(int, i)
+    r6 = r5
     goto L5
 L3:
-    r8 = r0 + 2
-    r0 = r8
+    r7 = r0 + 1
+    r0 = r7
     goto L1
 L4:
-    r9 = box(None, 1)
-    r7 = r9
+    r8 = box(None, 1)
+    r6 = r8
 L5:
-    res = r7
+    res = r6
     return 1
 
 [case testSimplifyListUnion]
@@ -517,53 +506,47 @@ L2:
     return r4
 def loop(a):
     a :: list
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5, x :: union[str, bytes]
-    r6 :: short_int
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4, x :: union[str, bytes]
+    r5 :: native_int
 L0:
     r0 = 0
 L1:
     r1 = var_object_size a
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = list_get_item_unsafe a, r0
-    r5 = cast(union[str, bytes], r4)
-    x = r5
+    r3 = list_get_item_unsafe a, r0
+    r4 = cast(union[str, bytes], r3)
+    x = r4
 L3:
-    r6 = r0 + 2
-    r0 = r6
+    r5 = r0 + 1
+    r0 = r5
     goto L1
 L4:
     return 1
 def nested_union(a):
     a :: list
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5, x :: union[str, None]
-    r6 :: short_int
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4, x :: union[str, None]
+    r5 :: native_int
 L0:
     r0 = 0
 L1:
     r1 = var_object_size a
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = list_get_item_unsafe a, r0
-    r5 = cast(union[str, None], r4)
-    x = r5
+    r3 = list_get_item_unsafe a, r0
+    r4 = cast(union[str, None], r3)
+    x = r4
 L3:
-    r6 = r0 + 2
-    r0 = r6
+    r5 = r0 + 1
+    r0 = r5
     goto L1
 L4:
     return 1
diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test
index c42a1fa74a75..5586a2bf4cfb 100644
--- a/mypyc/test-data/irbuild-set.test
+++ b/mypyc/test-data/irbuild-set.test
@@ -85,16 +85,14 @@ def test1():
     r4 :: ptr
     tmp_list :: list
     r5 :: set
-    r6 :: short_int
-    r7 :: native_int
-    r8 :: short_int
-    r9 :: bit
-    r10 :: object
-    r11, x, r12 :: int
-    r13 :: object
-    r14 :: i32
-    r15 :: bit
-    r16 :: short_int
+    r6, r7 :: native_int
+    r8 :: bit
+    r9 :: object
+    r10, x, r11 :: int
+    r12 :: object
+    r13 :: i32
+    r14 :: bit
+    r15 :: native_int
     a :: set
 L0:
     r0 = PyList_New(3)
@@ -111,20 +109,19 @@ L0:
     r6 = 0
 L1:
     r7 = var_object_size tmp_list
-    r8 = r7 << 1
-    r9 = int_lt r6, r8
-    if r9 goto L2 else goto L4 :: bool
+    r8 = r6 < r7 :: signed
+    if r8 goto L2 else goto L4 :: bool
 L2:
-    r10 = list_get_item_unsafe tmp_list, r6
-    r11 = unbox(int, r10)
-    x = r11
-    r12 = f(x)
-    r13 = box(int, r12)
-    r14 = PySet_Add(r5, r13)
-    r15 = r14 >= 0 :: signed
+    r9 = list_get_item_unsafe tmp_list, r6
+    r10 = unbox(int, r9)
+    x = r10
+    r11 = f(x)
+    r12 = box(int, r11)
+    r13 = PySet_Add(r5, r12)
+    r14 = r13 >= 0 :: signed
 L3:
-    r16 = r6 + 2
-    r6 = r16
+    r15 = r6 + 1
+    r6 = r15
     goto L1
 L4:
     a = r5
@@ -168,16 +165,15 @@ def test3():
     r7 :: set
     r8 :: short_int
     r9 :: native_int
-    r10 :: short_int
-    r11 :: object
-    r12 :: tuple[bool, short_int, object]
-    r13 :: short_int
-    r14 :: bool
-    r15 :: object
-    r16, x, r17 :: int
-    r18 :: object
-    r19 :: i32
-    r20, r21, r22 :: bit
+    r10 :: object
+    r11 :: tuple[bool, short_int, object]
+    r12 :: short_int
+    r13 :: bool
+    r14 :: object
+    r15, x, r16 :: int
+    r17 :: object
+    r18 :: i32
+    r19, r20, r21 :: bit
     c :: set
 L0:
     r0 = '1'
@@ -191,27 +187,26 @@ L0:
     r7 = PySet_New(0)
     r8 = 0
     r9 = PyDict_Size(tmp_dict)
-    r10 = r9 << 1
-    r11 = CPyDict_GetKeysIter(tmp_dict)
+    r10 = CPyDict_GetKeysIter(tmp_dict)
 L1:
-    r12 = CPyDict_NextKey(r11, r8)
-    r13 = r12[1]
-    r8 = r13
-    r14 = r12[0]
-    if r14 goto L2 else goto L4 :: bool
+    r11 = CPyDict_NextKey(r10, r8)
+    r12 = r11[1]
+    r8 = r12
+    r13 = r11[0]
+    if r13 goto L2 else goto L4 :: bool
 L2:
-    r15 = r12[2]
-    r16 = unbox(int, r15)
-    x = r16
-    r17 = f(x)
-    r18 = box(int, r17)
-    r19 = PySet_Add(r7, r18)
-    r20 = r19 >= 0 :: signed
+    r14 = r11[2]
+    r15 = unbox(int, r14)
+    x = r15
+    r16 = f(x)
+    r17 = box(int, r16)
+    r18 = PySet_Add(r7, r17)
+    r19 = r18 >= 0 :: signed
 L3:
-    r21 = CPyDict_CheckSize(tmp_dict, r10)
+    r20 = CPyDict_CheckSize(tmp_dict, r9)
     goto L1
 L4:
-    r22 = CPy_NoErrOccurred()
+    r21 = CPy_NoErrOccurred()
 L5:
     c = r7
     return 1
@@ -313,28 +308,26 @@ def test():
     tmp_list :: list
     r7 :: set
     r8, r9 :: list
-    r10 :: short_int
-    r11 :: native_int
-    r12 :: short_int
-    r13 :: bit
-    r14 :: object
-    r15, z :: int
-    r16 :: bit
-    r17 :: int
-    r18 :: object
-    r19 :: i32
-    r20 :: bit
-    r21 :: short_int
-    r22, r23, r24 :: object
-    r25, y, r26 :: int
-    r27 :: object
-    r28 :: i32
-    r29, r30 :: bit
-    r31, r32, r33 :: object
-    r34, x, r35 :: int
-    r36 :: object
-    r37 :: i32
-    r38, r39 :: bit
+    r10, r11 :: native_int
+    r12 :: bit
+    r13 :: object
+    r14, z :: int
+    r15 :: bit
+    r16 :: int
+    r17 :: object
+    r18 :: i32
+    r19 :: bit
+    r20 :: native_int
+    r21, r22, r23 :: object
+    r24, y, r25 :: int
+    r26 :: object
+    r27 :: i32
+    r28, r29 :: bit
+    r30, r31, r32 :: object
+    r33, x, r34 :: int
+    r35 :: object
+    r36 :: i32
+    r37, r38 :: bit
     a :: set
 L0:
     r0 = PyList_New(5)
@@ -357,60 +350,59 @@ L0:
     r10 = 0
 L1:
     r11 = var_object_size tmp_list
-    r12 = r11 << 1
-    r13 = int_lt r10, r12
-    if r13 goto L2 else goto L6 :: bool
+    r12 = r10 < r11 :: signed
+    if r12 goto L2 else goto L6 :: bool
 L2:
-    r14 = list_get_item_unsafe tmp_list, r10
-    r15 = unbox(int, r14)
-    z = r15
-    r16 = int_lt z, 8
-    if r16 goto L4 else goto L3 :: bool
+    r13 = list_get_item_unsafe tmp_list, r10
+    r14 = unbox(int, r13)
+    z = r14
+    r15 = int_lt z, 8
+    if r15 goto L4 else goto L3 :: bool
 L3:
     goto L5
 L4:
-    r17 = f1(z)
-    r18 = box(int, r17)
-    r19 = PyList_Append(r9, r18)
-    r20 = r19 >= 0 :: signed
+    r16 = f1(z)
+    r17 = box(int, r16)
+    r18 = PyList_Append(r9, r17)
+    r19 = r18 >= 0 :: signed
 L5:
-    r21 = r10 + 2
-    r10 = r21
+    r20 = r10 + 1
+    r10 = r20
     goto L1
 L6:
-    r22 = PyObject_GetIter(r9)
-    r23 = PyObject_GetIter(r22)
+    r21 = PyObject_GetIter(r9)
+    r22 = PyObject_GetIter(r21)
 L7:
-    r24 = PyIter_Next(r23)
-    if is_error(r24) goto L10 else goto L8
+    r23 = PyIter_Next(r22)
+    if is_error(r23) goto L10 else goto L8
 L8:
-    r25 = unbox(int, r24)
-    y = r25
-    r26 = f2(y)
-    r27 = box(int, r26)
-    r28 = PyList_Append(r8, r27)
-    r29 = r28 >= 0 :: signed
+    r24 = unbox(int, r23)
+    y = r24
+    r25 = f2(y)
+    r26 = box(int, r25)
+    r27 = PyList_Append(r8, r26)
+    r28 = r27 >= 0 :: signed
 L9:
     goto L7
 L10:
-    r30 = CPy_NoErrOccurred()
+    r29 = CPy_NoErrOccurred()
 L11:
-    r31 = PyObject_GetIter(r8)
-    r32 = PyObject_GetIter(r31)
+    r30 = PyObject_GetIter(r8)
+    r31 = PyObject_GetIter(r30)
 L12:
-    r33 = PyIter_Next(r32)
-    if is_error(r33) goto L15 else goto L13
+    r32 = PyIter_Next(r31)
+    if is_error(r32) goto L15 else goto L13
 L13:
-    r34 = unbox(int, r33)
-    x = r34
-    r35 = f3(x)
-    r36 = box(int, r35)
-    r37 = PySet_Add(r7, r36)
-    r38 = r37 >= 0 :: signed
+    r33 = unbox(int, r32)
+    x = r33
+    r34 = f3(x)
+    r35 = box(int, r34)
+    r36 = PySet_Add(r7, r35)
+    r37 = r36 >= 0 :: signed
 L14:
     goto L12
 L15:
-    r39 = CPy_NoErrOccurred()
+    r38 = CPy_NoErrOccurred()
 L16:
     a = r7
     return 1
diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test
index 1f9336d32140..48b8e0e318b8 100644
--- a/mypyc/test-data/irbuild-statements.test
+++ b/mypyc/test-data/irbuild-statements.test
@@ -230,30 +230,27 @@ def f(ls: List[int]) -> int:
 def f(ls):
     ls :: list
     y :: int
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5, x, r6 :: int
-    r7 :: short_int
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4, x, r5 :: int
+    r6 :: native_int
 L0:
     y = 0
     r0 = 0
 L1:
     r1 = var_object_size ls
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = list_get_item_unsafe ls, r0
-    r5 = unbox(int, r4)
-    x = r5
-    r6 = CPyTagged_Add(y, x)
-    y = r6
+    r3 = list_get_item_unsafe ls, r0
+    r4 = unbox(int, r3)
+    x = r4
+    r5 = CPyTagged_Add(y, x)
+    y = r5
 L3:
-    r7 = r0 + 2
-    r0 = r7
+    r6 = r0 + 1
+    r0 = r6
     goto L1
 L4:
     return y
@@ -269,39 +266,37 @@ def f(d):
     d :: dict
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object]
-    r5 :: short_int
-    r6 :: bool
-    r7 :: object
-    r8, key :: int
-    r9, r10 :: object
-    r11 :: int
-    r12, r13 :: bit
+    r2 :: object
+    r3 :: tuple[bool, short_int, object]
+    r4 :: short_int
+    r5 :: bool
+    r6 :: object
+    r7, key :: int
+    r8, r9 :: object
+    r10 :: int
+    r11, r12 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d)
-    r2 = r1 << 1
-    r3 = CPyDict_GetKeysIter(d)
+    r2 = CPyDict_GetKeysIter(d)
 L1:
-    r4 = CPyDict_NextKey(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L4 :: bool
+    r3 = CPyDict_NextKey(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L4 :: bool
 L2:
-    r7 = r4[2]
-    r8 = unbox(int, r7)
-    key = r8
-    r9 = box(int, key)
-    r10 = CPyDict_GetItem(d, r9)
-    r11 = unbox(int, r10)
+    r6 = r3[2]
+    r7 = unbox(int, r6)
+    key = r7
+    r8 = box(int, key)
+    r9 = CPyDict_GetItem(d, r8)
+    r10 = unbox(int, r9)
 L3:
-    r12 = CPyDict_CheckSize(d, r2)
+    r11 = CPyDict_CheckSize(d, r1)
     goto L1
 L4:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L5:
     return 1
 
@@ -321,54 +316,52 @@ def sum_over_even_values(d):
     s :: int
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object]
-    r5 :: short_int
-    r6 :: bool
-    r7 :: object
-    r8, key :: int
-    r9, r10 :: object
-    r11, r12 :: int
-    r13 :: bit
-    r14, r15 :: object
-    r16, r17 :: int
-    r18, r19 :: bit
+    r2 :: object
+    r3 :: tuple[bool, short_int, object]
+    r4 :: short_int
+    r5 :: bool
+    r6 :: object
+    r7, key :: int
+    r8, r9 :: object
+    r10, r11 :: int
+    r12 :: bit
+    r13, r14 :: object
+    r15, r16 :: int
+    r17, r18 :: bit
 L0:
     s = 0
     r0 = 0
     r1 = PyDict_Size(d)
-    r2 = r1 << 1
-    r3 = CPyDict_GetKeysIter(d)
+    r2 = CPyDict_GetKeysIter(d)
 L1:
-    r4 = CPyDict_NextKey(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L6 :: bool
+    r3 = CPyDict_NextKey(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L6 :: bool
 L2:
-    r7 = r4[2]
-    r8 = unbox(int, r7)
-    key = r8
-    r9 = box(int, key)
-    r10 = CPyDict_GetItem(d, r9)
-    r11 = unbox(int, r10)
-    r12 = CPyTagged_Remainder(r11, 4)
-    r13 = r12 != 0
-    if r13 goto L3 else goto L4 :: bool
+    r6 = r3[2]
+    r7 = unbox(int, r6)
+    key = r7
+    r8 = box(int, key)
+    r9 = CPyDict_GetItem(d, r8)
+    r10 = unbox(int, r9)
+    r11 = CPyTagged_Remainder(r10, 4)
+    r12 = r11 != 0
+    if r12 goto L3 else goto L4 :: bool
 L3:
     goto L5
 L4:
-    r14 = box(int, key)
-    r15 = CPyDict_GetItem(d, r14)
-    r16 = unbox(int, r15)
-    r17 = CPyTagged_Add(s, r16)
-    s = r17
+    r13 = box(int, key)
+    r14 = CPyDict_GetItem(d, r13)
+    r15 = unbox(int, r14)
+    r16 = CPyTagged_Add(s, r15)
+    s = r16
 L5:
-    r18 = CPyDict_CheckSize(d, r2)
+    r17 = CPyDict_CheckSize(d, r1)
     goto L1
 L6:
-    r19 = CPy_NoErrOccurred()
+    r18 = CPy_NoErrOccurred()
 L7:
     return s
 
@@ -597,16 +590,16 @@ L0:
     r0 = CPySequence_CheckUnpackCount(l, 2)
     r1 = r0 >= 0 :: signed
     r2 = list_get_item_unsafe l, 0
-    r3 = list_get_item_unsafe l, 2
+    r3 = list_get_item_unsafe l, 1
     x = r2
     r4 = unbox(int, r3)
     y = r4
     r5 = CPySequence_CheckUnpackCount(t, 2)
     r6 = r5 >= 0 :: signed
-    r7 = CPySequenceTuple_GetItem(t, 0)
-    r8 = CPySequenceTuple_GetItem(t, 2)
-    r9 = unbox(int, r8)
+    r7 = CPySequenceTuple_GetItemUnsafe(t, 0)
+    r8 = CPySequenceTuple_GetItemUnsafe(t, 1)
     x = r7
+    r9 = unbox(int, r8)
     y = r9
     return 1
 
@@ -872,33 +865,32 @@ def g(x: Iterable[int]) -> None:
 [out]
 def f(a):
     a :: list
-    r0, r1 :: short_int
-    r2 :: native_int
-    r3 :: short_int
-    r4 :: bit
+    r0 :: short_int
+    r1, r2 :: native_int
+    r3 :: bit
     i :: int
-    r5 :: object
-    r6, x, r7 :: int
-    r8, r9 :: short_int
+    r4 :: object
+    r5, x, r6 :: int
+    r7 :: short_int
+    r8 :: native_int
 L0:
     r0 = 0
     r1 = 0
 L1:
     r2 = var_object_size a
-    r3 = r2 << 1
-    r4 = int_lt r1, r3
-    if r4 goto L2 else goto L4 :: bool
+    r3 = r1 < r2 :: signed
+    if r3 goto L2 else goto L4 :: bool
 L2:
     i = r0
-    r5 = list_get_item_unsafe a, r1
-    r6 = unbox(int, r5)
-    x = r6
-    r7 = CPyTagged_Add(i, x)
+    r4 = list_get_item_unsafe a, r1
+    r5 = unbox(int, r4)
+    x = r5
+    r6 = CPyTagged_Add(i, x)
 L3:
-    r8 = r0 + 2
-    r0 = r8
-    r9 = r1 + 2
-    r1 = r9
+    r7 = r0 + 2
+    r0 = r7
+    r8 = r1 + 1
+    r1 = r8
     goto L1
 L4:
 L5:
@@ -944,66 +936,65 @@ def g(a: Iterable[bool], b: List[int]) -> None:
 def f(a, b):
     a :: list
     b :: object
-    r0 :: short_int
+    r0 :: native_int
     r1 :: object
     r2 :: native_int
-    r3 :: short_int
-    r4 :: bit
-    r5, r6 :: object
-    r7, x :: int
-    r8, y :: bool
-    r9 :: i32
-    r10 :: bit
-    r11 :: bool
-    r12 :: short_int
-    r13 :: bit
+    r3 :: bit
+    r4, r5 :: object
+    r6, x :: int
+    r7, y :: bool
+    r8 :: i32
+    r9 :: bit
+    r10 :: bool
+    r11 :: native_int
+    r12 :: bit
 L0:
     r0 = 0
     r1 = PyObject_GetIter(b)
 L1:
     r2 = var_object_size a
-    r3 = r2 << 1
-    r4 = int_lt r0, r3
-    if r4 goto L2 else goto L7 :: bool
+    r3 = r0 < r2 :: signed
+    if r3 goto L2 else goto L7 :: bool
 L2:
-    r5 = PyIter_Next(r1)
-    if is_error(r5) goto L7 else goto L3
+    r4 = PyIter_Next(r1)
+    if is_error(r4) goto L7 else goto L3
 L3:
-    r6 = list_get_item_unsafe a, r0
-    r7 = unbox(int, r6)
-    x = r7
-    r8 = unbox(bool, r5)
-    y = r8
-    r9 = PyObject_IsTrue(b)
-    r10 = r9 >= 0 :: signed
-    r11 = truncate r9: i32 to builtins.bool
-    if r11 goto L4 else goto L5 :: bool
+    r5 = list_get_item_unsafe a, r0
+    r6 = unbox(int, r5)
+    x = r6
+    r7 = unbox(bool, r4)
+    y = r7
+    r8 = PyObject_IsTrue(b)
+    r9 = r8 >= 0 :: signed
+    r10 = truncate r8: i32 to builtins.bool
+    if r10 goto L4 else goto L5 :: bool
 L4:
     x = 2
 L5:
 L6:
-    r12 = r0 + 2
-    r0 = r12
+    r11 = r0 + 1
+    r0 = r11
     goto L1
 L7:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L8:
     return 1
 def g(a, b):
     a :: object
     b :: list
     r0 :: object
-    r1, r2 :: short_int
+    r1 :: native_int
+    r2 :: short_int
     z :: int
     r3 :: object
     r4 :: native_int
-    r5 :: short_int
-    r6, r7 :: bit
-    r8, x :: bool
-    r9 :: object
-    r10, y :: int
-    r11, r12 :: short_int
-    r13 :: bit
+    r5, r6 :: bit
+    r7, x :: bool
+    r8 :: object
+    r9, y :: int
+    r10 :: native_int
+    r11 :: short_int
+    r12 :: bit
 L0:
     r0 = PyObject_GetIter(a)
     r1 = 0
@@ -1014,28 +1005,27 @@ L1:
     if is_error(r3) goto L6 else goto L2
 L2:
     r4 = var_object_size b
-    r5 = r4 << 1
-    r6 = int_lt r1, r5
-    if r6 goto L3 else goto L6 :: bool
+    r5 = r1 < r4 :: signed
+    if r5 goto L3 else goto L6 :: bool
 L3:
-    r7 = int_lt r2, 10
-    if r7 goto L4 else goto L6 :: bool
+    r6 = int_lt r2, 10
+    if r6 goto L4 else goto L6 :: bool
 L4:
-    r8 = unbox(bool, r3)
-    x = r8
-    r9 = list_get_item_unsafe b, r1
-    r10 = unbox(int, r9)
-    y = r10
+    r7 = unbox(bool, r3)
+    x = r7
+    r8 = list_get_item_unsafe b, r1
+    r9 = unbox(int, r8)
+    y = r9
     x = 0
 L5:
-    r11 = r1 + 2
-    r1 = r11
-    r12 = r2 + 2
-    r2 = r12
-    z = r12
+    r10 = r1 + 1
+    r1 = r10
+    r11 = r2 + 2
+    r2 = r11
+    z = r11
     goto L1
 L6:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L7:
     return 1
 
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index 222021751080..c39968fc139e 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -127,27 +127,24 @@ def f(xs: Tuple[str, ...]) -> None:
 [out]
 def f(xs):
     xs :: tuple
-    r0 :: short_int
-    r1 :: native_int
-    r2 :: short_int
-    r3 :: bit
-    r4 :: object
-    r5, x :: str
-    r6 :: short_int
+    r0, r1 :: native_int
+    r2 :: bit
+    r3 :: object
+    r4, x :: str
+    r5 :: native_int
 L0:
     r0 = 0
 L1:
     r1 = var_object_size xs
-    r2 = r1 << 1
-    r3 = int_lt r0, r2
-    if r3 goto L2 else goto L4 :: bool
+    r2 = r0 < r1 :: signed
+    if r2 goto L2 else goto L4 :: bool
 L2:
-    r4 = CPySequenceTuple_GetItem(xs, r0)
-    r5 = cast(str, r4)
-    x = r5
+    r3 = CPySequenceTuple_GetItemUnsafe(xs, r0)
+    r4 = cast(str, r3)
+    x = r4
 L3:
-    r6 = r0 + 2
-    r0 = r6
+    r5 = r0 + 1
+    r0 = r5
     goto L1
 L4:
     return 1
@@ -234,16 +231,13 @@ def test():
     source :: list
     r5 :: native_int
     r6 :: tuple
-    r7 :: short_int
-    r8 :: native_int
-    r9 :: short_int
-    r10 :: bit
-    r11 :: object
-    r12, x :: int
-    r13 :: bool
-    r14 :: object
-    r15 :: bit
-    r16 :: short_int
+    r7, r8 :: native_int
+    r9 :: bit
+    r10 :: object
+    r11, x :: int
+    r12 :: bool
+    r13 :: object
+    r14 :: native_int
     a :: tuple
 L0:
     r0 = PyList_New(3)
@@ -261,25 +255,24 @@ L0:
     r7 = 0
 L1:
     r8 = var_object_size source
-    r9 = r8 << 1
-    r10 = int_lt r7, r9
-    if r10 goto L2 else goto L4 :: bool
+    r9 = r7 < r8 :: signed
+    if r9 goto L2 else goto L4 :: bool
 L2:
-    r11 = list_get_item_unsafe source, r7
-    r12 = unbox(int, r11)
-    x = r12
-    r13 = f(x)
-    r14 = box(bool, r13)
-    r15 = CPySequenceTuple_SetItemUnsafe(r6, r7, r14)
+    r10 = list_get_item_unsafe source, r7
+    r11 = unbox(int, r10)
+    x = r11
+    r12 = f(x)
+    r13 = box(bool, r12)
+    CPySequenceTuple_SetItemUnsafe(r6, r7, r13)
 L3:
-    r16 = r7 + 2
-    r7 = r16
+    r14 = r7 + 1
+    r7 = r14
     goto L1
 L4:
     a = r6
     return 1
 
-[case testTupleBuiltFromStr]
+[case testTupleBuiltFromStr_64bit]
 def f2(val: str) -> str:
     return val + "f2"
 
@@ -298,14 +291,11 @@ def test():
     r1 :: native_int
     r2 :: bit
     r3 :: tuple
-    r4 :: short_int
-    r5 :: native_int
-    r6 :: bit
-    r7 :: short_int
-    r8 :: bit
-    r9, x, r10 :: str
-    r11 :: bit
-    r12 :: short_int
+    r4, r5 :: native_int
+    r6, r7, r8, r9 :: bit
+    r10, r11, r12 :: int
+    r13, x, r14 :: str
+    r15 :: native_int
     a :: tuple
 L0:
     r0 = 'abc'
@@ -317,19 +307,31 @@ L0:
 L1:
     r5 = CPyStr_Size_size_t(source)
     r6 = r5 >= 0 :: signed
-    r7 = r5 << 1
-    r8 = int_lt r4, r7
-    if r8 goto L2 else goto L4 :: bool
+    r7 = r4 < r5 :: signed
+    if r7 goto L2 else goto L8 :: bool
 L2:
-    r9 = CPyStr_GetItem(source, r4)
-    x = r9
-    r10 = f2(x)
-    r11 = CPySequenceTuple_SetItemUnsafe(r3, r4, r10)
+    r8 = r4 <= 4611686018427387903 :: signed
+    if r8 goto L3 else goto L4 :: bool
 L3:
-    r12 = r4 + 2
-    r4 = r12
-    goto L1
+    r9 = r4 >= -4611686018427387904 :: signed
+    if r9 goto L5 else goto L4 :: bool
 L4:
+    r10 = CPyTagged_FromInt64(r4)
+    r11 = r10
+    goto L6
+L5:
+    r12 = r4 << 1
+    r11 = r12
+L6:
+    r13 = CPyStr_GetItem(source, r11)
+    x = r13
+    r14 = f2(x)
+    CPySequenceTuple_SetItemUnsafe(r3, r4, r14)
+L7:
+    r15 = r4 + 1
+    r4 = r15
+    goto L1
+L8:
     a = r3
     return 1
 
@@ -351,15 +353,12 @@ def test(source):
     source :: tuple
     r0 :: native_int
     r1 :: tuple
-    r2 :: short_int
-    r3 :: native_int
-    r4 :: short_int
-    r5 :: bit
-    r6 :: object
-    r7, x, r8 :: bool
-    r9 :: object
-    r10 :: bit
-    r11 :: short_int
+    r2, r3 :: native_int
+    r4 :: bit
+    r5 :: object
+    r6, x, r7 :: bool
+    r8 :: object
+    r9 :: native_int
     a :: tuple
 L0:
     r0 = var_object_size source
@@ -367,19 +366,18 @@ L0:
     r2 = 0
 L1:
     r3 = var_object_size source
-    r4 = r3 << 1
-    r5 = int_lt r2, r4
-    if r5 goto L2 else goto L4 :: bool
+    r4 = r2 < r3 :: signed
+    if r4 goto L2 else goto L4 :: bool
 L2:
-    r6 = CPySequenceTuple_GetItem(source, r2)
-    r7 = unbox(bool, r6)
-    x = r7
-    r8 = f(x)
-    r9 = box(bool, r8)
-    r10 = CPySequenceTuple_SetItemUnsafe(r1, r2, r9)
+    r5 = CPySequenceTuple_GetItemUnsafe(source, r2)
+    r6 = unbox(bool, r5)
+    x = r6
+    r7 = f(x)
+    r8 = box(bool, r7)
+    CPySequenceTuple_SetItemUnsafe(r1, r2, r8)
 L3:
-    r11 = r2 + 2
-    r2 = r11
+    r9 = r2 + 1
+    r2 = r9
     goto L1
 L4:
     a = r1
diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test
index ad561c561872..b4fe14db59c4 100644
--- a/mypyc/test-data/lowering-int.test
+++ b/mypyc/test-data/lowering-int.test
@@ -341,47 +341,43 @@ def f(l: list[int]) -> None:
 [out]
 def f(l):
     l :: list
-    r0 :: short_int
+    r0 :: native_int
     r1 :: ptr
     r2 :: native_int
-    r3 :: short_int
-    r4 :: bit
-    r5 :: native_int
-    r6, r7 :: ptr
-    r8 :: native_int
-    r9 :: ptr
-    r10 :: object
-    r11, x :: int
-    r12 :: short_int
-    r13 :: None
+    r3 :: bit
+    r4, r5 :: ptr
+    r6 :: native_int
+    r7 :: ptr
+    r8 :: object
+    r9, x :: int
+    r10 :: native_int
+    r11 :: None
 L0:
     r0 = 0
 L1:
     r1 = get_element_ptr l ob_size :: PyVarObject
     r2 = load_mem r1 :: native_int*
-    r3 = r2 << 1
-    r4 = r0 < r3 :: signed
-    if r4 goto L2 else goto L5 :: bool
+    r3 = r0 < r2 :: signed
+    if r3 goto L2 else goto L5 :: bool
 L2:
-    r5 = r0 >> 1
-    r6 = get_element_ptr l ob_item :: PyListObject
-    r7 = load_mem r6 :: ptr*
-    r8 = r5 * 8
-    r9 = r7 + r8
-    r10 = load_mem r9 :: builtins.object*
-    inc_ref r10
-    r11 = unbox(int, r10)
-    dec_ref r10
-    if is_error(r11) goto L6 (error at f:4) else goto L3
+    r4 = get_element_ptr l ob_item :: PyListObject
+    r5 = load_mem r4 :: ptr*
+    r6 = r0 * 8
+    r7 = r5 + r6
+    r8 = load_mem r7 :: builtins.object*
+    inc_ref r8
+    r9 = unbox(int, r8)
+    dec_ref r8
+    if is_error(r9) goto L6 (error at f:4) else goto L3
 L3:
-    x = r11
+    x = r9
     dec_ref x :: int
 L4:
-    r12 = r0 + 2
-    r0 = r12
+    r10 = r0 + 1
+    r0 = r10
     goto L1
 L5:
     return 1
 L6:
-    r13 =  :: None
-    return r13
+    r11 =  :: None
+    return r11
diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test
index 22153cff5a91..a831d9baf86e 100644
--- a/mypyc/test-data/refcount.test
+++ b/mypyc/test-data/refcount.test
@@ -730,49 +730,47 @@ def f(d):
     d :: dict
     r0 :: short_int
     r1 :: native_int
-    r2 :: short_int
-    r3 :: object
-    r4 :: tuple[bool, short_int, object]
-    r5 :: short_int
-    r6 :: bool
-    r7 :: object
-    r8, key :: int
-    r9, r10 :: object
-    r11 :: int
-    r12, r13 :: bit
+    r2 :: object
+    r3 :: tuple[bool, short_int, object]
+    r4 :: short_int
+    r5 :: bool
+    r6 :: object
+    r7, key :: int
+    r8, r9 :: object
+    r10 :: int
+    r11, r12 :: bit
 L0:
     r0 = 0
     r1 = PyDict_Size(d)
-    r2 = r1 << 1
-    r3 = CPyDict_GetKeysIter(d)
+    r2 = CPyDict_GetKeysIter(d)
 L1:
-    r4 = CPyDict_NextKey(r3, r0)
-    r5 = r4[1]
-    r0 = r5
-    r6 = r4[0]
-    if r6 goto L2 else goto L6 :: bool
+    r3 = CPyDict_NextKey(r2, r0)
+    r4 = r3[1]
+    r0 = r4
+    r5 = r3[0]
+    if r5 goto L2 else goto L6 :: bool
 L2:
-    r7 = r4[2]
-    dec_ref r4
-    r8 = unbox(int, r7)
-    dec_ref r7
-    key = r8
-    r9 = box(int, key)
-    r10 = CPyDict_GetItem(d, r9)
+    r6 = r3[2]
+    dec_ref r3
+    r7 = unbox(int, r6)
+    dec_ref r6
+    key = r7
+    r8 = box(int, key)
+    r9 = CPyDict_GetItem(d, r8)
+    dec_ref r8
+    r10 = unbox(int, r9)
     dec_ref r9
-    r11 = unbox(int, r10)
-    dec_ref r10
-    dec_ref r11 :: int
+    dec_ref r10 :: int
 L3:
-    r12 = CPyDict_CheckSize(d, r2)
+    r11 = CPyDict_CheckSize(d, r1)
     goto L1
 L4:
-    r13 = CPy_NoErrOccurred()
+    r12 = CPy_NoErrOccurred()
 L5:
     return 1
 L6:
+    dec_ref r2
     dec_ref r3
-    dec_ref r4
     goto L4
 
 [case testBorrowRefs]

From f49a88f55fb84eb02b0b0b1db369b9ee0f138e3b Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 14 Jul 2025 13:24:58 +0100
Subject: [PATCH 1458/1617] [mypyc] Simplify IR generated for "for" loops over
 strings (#19434)

Add unsafe list get item primitive. The new primitive just calls the
primary get item primitive, but we could later provide an optimized
primitive if this turns out to be a performance bottleneck.
---
 mypyc/irbuild/for_helpers.py       |  3 +++
 mypyc/lib-rt/CPy.h                 |  1 +
 mypyc/lib-rt/str_ops.c             |  5 ++++
 mypyc/primitives/str_ops.py        |  9 +++++++
 mypyc/test-data/irbuild-tuple.test | 38 ++++++++++--------------------
 5 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index 358f7cb76ba8..a7ed97ac8eab 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -76,6 +76,7 @@
 from mypyc.primitives.misc_ops import stop_async_iteration_op
 from mypyc.primitives.registry import CFunctionDescription
 from mypyc.primitives.set_ops import set_add_op
+from mypyc.primitives.str_ops import str_get_item_unsafe_op
 from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op
 
 GenFunc = Callable[[], None]
@@ -772,6 +773,8 @@ def unsafe_index(builder: IRBuilder, target: Value, index: Value, line: int) ->
         return builder.primitive_op(list_get_item_unsafe_op, [target, index], line)
     elif is_tuple_rprimitive(target.type):
         return builder.call_c(tuple_get_item_unsafe_op, [target, index], line)
+    elif is_str_rprimitive(target.type):
+        return builder.call_c(str_get_item_unsafe_op, [target, index], line)
     else:
         return builder.gen_method_call(target, "__getitem__", [index], None, line)
 
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index dba84d44f363..698e65155da4 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -727,6 +727,7 @@ static inline char CPyDict_CheckSize(PyObject *dict, Py_ssize_t size) {
 char CPyStr_Equal(PyObject *str1, PyObject *str2);
 PyObject *CPyStr_Build(Py_ssize_t len, ...);
 PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index);
+PyObject *CPyStr_GetItemUnsafe(PyObject *str, Py_ssize_t index);
 CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction);
 CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction);
 PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split);
diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c
index 5fd376f21cfa..a2d10aacea46 100644
--- a/mypyc/lib-rt/str_ops.c
+++ b/mypyc/lib-rt/str_ops.c
@@ -117,6 +117,11 @@ PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) {
     }
 }
 
+PyObject *CPyStr_GetItemUnsafe(PyObject *str, Py_ssize_t index) {
+    // This is unsafe since we don't check for overflow when doing <<.
+    return CPyStr_GetItem(str, index << 1);
+}
+
 // A simplification of _PyUnicode_JoinArray() from CPython 3.9.6
 PyObject *CPyStr_Build(Py_ssize_t len, ...) {
     Py_ssize_t i;
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index 37dbdf21bb5d..e3f0b9dbbc2a 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -95,6 +95,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# This is unsafe since it assumes that the index is within reasonable bounds.
+# In the future this might do no bounds checking at all.
+str_get_item_unsafe_op = custom_op(
+    arg_types=[str_rprimitive, c_pyssize_t_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="CPyStr_GetItemUnsafe",
+    error_kind=ERR_MAGIC,
+)
+
 # str[begin:end]
 str_slice_op = custom_op(
     arg_types=[str_rprimitive, int_rprimitive, int_rprimitive],
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index c39968fc139e..5c5ec27b1882 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -272,7 +272,7 @@ L4:
     a = r6
     return 1
 
-[case testTupleBuiltFromStr_64bit]
+[case testTupleBuiltFromStr]
 def f2(val: str) -> str:
     return val + "f2"
 
@@ -292,10 +292,9 @@ def test():
     r2 :: bit
     r3 :: tuple
     r4, r5 :: native_int
-    r6, r7, r8, r9 :: bit
-    r10, r11, r12 :: int
-    r13, x, r14 :: str
-    r15 :: native_int
+    r6, r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
     a :: tuple
 L0:
     r0 = 'abc'
@@ -308,30 +307,17 @@ L1:
     r5 = CPyStr_Size_size_t(source)
     r6 = r5 >= 0 :: signed
     r7 = r4 < r5 :: signed
-    if r7 goto L2 else goto L8 :: bool
+    if r7 goto L2 else goto L4 :: bool
 L2:
-    r8 = r4 <= 4611686018427387903 :: signed
-    if r8 goto L3 else goto L4 :: bool
+    r8 = CPyStr_GetItemUnsafe(source, r4)
+    x = r8
+    r9 = f2(x)
+    CPySequenceTuple_SetItemUnsafe(r3, r4, r9)
 L3:
-    r9 = r4 >= -4611686018427387904 :: signed
-    if r9 goto L5 else goto L4 :: bool
-L4:
-    r10 = CPyTagged_FromInt64(r4)
-    r11 = r10
-    goto L6
-L5:
-    r12 = r4 << 1
-    r11 = r12
-L6:
-    r13 = CPyStr_GetItem(source, r11)
-    x = r13
-    r14 = f2(x)
-    CPySequenceTuple_SetItemUnsafe(r3, r4, r14)
-L7:
-    r15 = r4 + 1
-    r4 = r15
+    r10 = r4 + 1
+    r4 = r10
     goto L1
-L8:
+L4:
     a = r3
     return 1
 

From 9bc098505d8c363f4ff03b165517414872ca7869 Mon Sep 17 00:00:00 2001
From: esarp <11684270+esarp@users.noreply.github.com>
Date: Mon, 14 Jul 2025 10:34:40 -0500
Subject: [PATCH 1459/1617] Initial changelog for 1.17 release (#19427)

---
 CHANGELOG.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1470b7d50c3..e4f148fe6382 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,16 @@
 
 ## Next Release
 
+## Mypy 1.17 (Unreleased)
+
+We’ve just uploaded mypy 1.17 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
+Mypy is a static type checker for Python. This release includes new features and bug fixes.
+You can install it as follows:
+
+    python3 -m pip install -U mypy
+
+You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io).
+
 ### Remove Support for targeting Python 3.8
 
 Mypy now requires `--python-version 3.9` or greater. Support for only Python 3.8 is
@@ -29,6 +39,119 @@ Mypy only supports Python 3.9+. The \--force-uppercase-builtins flag is now depr
 
 Contributed by Marc Mueller (PR [19176](https://github.com/python/mypy/pull/19176))
 
+### Mypyc Fixes and Improvements
+
+* Fix exception swallowing in async try/finally blocks with await (Chainfire, PR [19353](https://github.com/python/mypy/pull/19353))
+* Fix AttributeError in async try/finally with mixed return paths (Chainfire, PR [19361](https://github.com/python/mypy/pull/19361))
+* Derive .c file name from full module name if using multi_file (Jukka Lehtosalo, PR [19278](https://github.com/python/mypy/pull/19278))
+* Support overriding the group name used in output files (Jukka Lehtosalo, PR [19272](https://github.com/python/mypy/pull/19272))
+* Make generated generator helper method internal (Jukka Lehtosalo, PR [19268](https://github.com/python/mypy/pull/19268))
+* Add note about using non-native class to subclass built-in types (Jukka Lehtosalo, PR [19236](https://github.com/python/mypy/pull/19236))
+* Make some generated classes implicitly final (Jukka Lehtosalo, PR [19235](https://github.com/python/mypy/pull/19235))
+* Free coroutine after await encounters StopIteration (Jukka Lehtosalo, PR [19231](https://github.com/python/mypy/pull/19231))
+* Use non-tagged integer for generator label (Jukka Lehtosalo, PR [19218](https://github.com/python/mypy/pull/19218))
+* Merge generator and environment classes in simple cases (Jukka Lehtosalo, PR [19207](https://github.com/python/mypy/pull/19207))
+* Don't simplify module prefixes if using separate compilation (Jukka Lehtosalo, PR [19206](https://github.com/python/mypy/pull/19206))
+* Test function nesting with async functions (Jukka Lehtosalo, PR [19203](https://github.com/python/mypy/pull/19203))
+* Enable partial, unsafe support for free-threading (Jukka Lehtosalo, PR [19167](https://github.com/python/mypy/pull/19167))
+* Add comment about incref/decref and free-threaded builds (Jukka Lehtosalo, PR [19155](https://github.com/python/mypy/pull/19155))
+* Refactor extension module C generation and generated C (Jukka Lehtosalo, PR [19126](https://github.com/python/mypy/pull/19126))
+* Fix incref/decref on free-threaded builds (Jukka Lehtosalo, PR [19127](https://github.com/python/mypy/pull/19127))
+* Remove last unreachable block from mypyc code (Stanislav Terliakov, PR [19086](https://github.com/python/mypy/pull/19086))
+
+### Stubgen Improvements
+
+* stubgen: add test case for handling `Incomplete` return types (Alexey Makridenko, PR [19253](https://github.com/python/mypy/pull/19253))
+* stubgen: add import for `types` in `__exit__` method signature (Alexey Makridenko, PR [19120](https://github.com/python/mypy/pull/19120))
+* stubgenc: add support for including class and property docstrings (Chad Dombrova, PR [17964](https://github.com/python/mypy/pull/17964))
+* stubgen: Don't generate `Incomplete | None = None` argument annotation (Sebastian Rittau, PR [19097](https://github.com/python/mypy/pull/19097))
+* Support several more constructs in stubgen's AliasPrinter (Stanislav Terliakov, PR [18888](https://github.com/python/mypy/pull/18888))
+
+### Stubtest Improvements
+
+* Syntax error messages capitalization (Charulata, PR [19114](https://github.com/python/mypy/pull/19114))
+
+### Miscellaneous Fixes and Improvements
+
+* Combine the revealed types of multiple iteration steps in a more robust manner (Christoph Tyralla, PR [19324](https://github.com/python/mypy/pull/19324))
+* Improve the handling of "iteration dependent" errors and notes in finally clauses (Christoph Tyralla, PR [19270](https://github.com/python/mypy/pull/19270))
+* Lessen dmypy suggest path limitations for Windows machines (CoolCat467, PR [19337](https://github.com/python/mypy/pull/19337))
+* Type ignore comments erroneously marked as unused by dmypy (Charlie Denton, PR [15043](https://github.com/python/mypy/pull/15043))
+* Handle corner case: protocol vs classvar vs descriptor (Ivan Levkivskyi, PR [19277](https://github.com/python/mypy/pull/19277))
+* Fix `exhaustive-match` error code in title (johnthagen, PR [19276](https://github.com/python/mypy/pull/19276))
+* Fix couple inconsistencies in protocols vs TypeType (Ivan Levkivskyi, PR [19267](https://github.com/python/mypy/pull/19267))
+* Fix missing error context for unpacking assignment involving star expression (Brian Schubert, PR [19258](https://github.com/python/mypy/pull/19258))
+* Fix and simplify error de-duplication (Ivan Levkivskyi, PR [19247](https://github.com/python/mypy/pull/19247))
+* Add regression test for narrowing union of mixins (Shantanu, PR [19266](https://github.com/python/mypy/pull/19266))
+* Disallow `ClassVar` in type aliases (Brian Schubert, PR [19263](https://github.com/python/mypy/pull/19263))
+* Refactor/unify access to static attributes (Ivan Levkivskyi, PR [19254](https://github.com/python/mypy/pull/19254))
+* Clean-up and move operator access to checkmember.py (Ivan Levkivskyi, PR [19250](https://github.com/python/mypy/pull/19250))
+* Add script that prints compiled files when self compiling (Jukka Lehtosalo, PR [19260](https://github.com/python/mypy/pull/19260))
+* Fix help message url for "None and Optional handling" section (Guy Wilson, PR [19252](https://github.com/python/mypy/pull/19252))
+* Display FQN for imported base classes in errors about incompatible overrides (Mikhail Golubev, PR [19115](https://github.com/python/mypy/pull/19115))
+* Fix a minor merge conflict caused by #19118 (Christoph Tyralla, PR [19246](https://github.com/python/mypy/pull/19246))
+* Avoid false `unreachable`, `redundant-expr`, and `redundant-casts` warnings in loops more robustly and efficiently, and avoid multiple `revealed type` notes for the same line (Christoph Tyralla, PR [19118](https://github.com/python/mypy/pull/19118))
+* Fix type extraction from `isinstance` checks (Stanislav Terliakov, PR [19223](https://github.com/python/mypy/pull/19223))
+* Erase stray typevars in functools.partial generic (Stanislav Terliakov, PR [18954](https://github.com/python/mypy/pull/18954))
+* Make infer_condition_value recognize the whole truth table (Stanislav Terliakov, PR [18944](https://github.com/python/mypy/pull/18944))
+* Support type aliases, `NamedTuple` and `TypedDict` in constrained TypeVar defaults (Stanislav Terliakov, PR [18884](https://github.com/python/mypy/pull/18884))
+* Move dataclass kw_only fields to the end of the signature (Stanislav Terliakov, PR [19018](https://github.com/python/mypy/pull/19018))
+* Deprecated --force-uppercase-builtins flag (Marc Mueller, PR [19176](https://github.com/python/mypy/pull/19176))
+* Provide a better fallback value for the python_version option (Marc Mueller, PR [19162](https://github.com/python/mypy/pull/19162))
+* Avoid spurious non-overlapping eq error with metaclass with `__eq__` (Michael J. Sullivan, PR [19220](https://github.com/python/mypy/pull/19220))
+* Remove --show-speed-regression in primer (Shantanu, PR [19226](https://github.com/python/mypy/pull/19226))
+* Add flag to raise error if match statement does not match exaustively (Donal Burns, PR [19144](https://github.com/python/mypy/pull/19144))
+* Narrow type variable bounds in binder (Ivan Levkivskyi, PR [19183](https://github.com/python/mypy/pull/19183))
+* Add regression test for dataclass typeguard (Shantanu, PR [19214](https://github.com/python/mypy/pull/19214))
+* Add classifier for Python 3.14 (Marc Mueller, PR [19199](https://github.com/python/mypy/pull/19199))
+* Further cleanup after dropping Python 3.8 (Marc Mueller, PR [19197](https://github.com/python/mypy/pull/19197))
+* Fix nondeterministic type checking by making join with explicit Protocol and type promotion commute (Shantanu, PR [18402](https://github.com/python/mypy/pull/18402))
+* Infer constraints eagerly if actual is Any (Ivan Levkivskyi, PR [19190](https://github.com/python/mypy/pull/19190))
+* Include walrus assignments in conditional inference (Stanislav Terliakov, PR [19038](https://github.com/python/mypy/pull/19038))
+* Use PEP 604 syntax for TypeStrVisitor (Marc Mueller, PR [19179](https://github.com/python/mypy/pull/19179))
+* Use checkmember.py to check protocol subtyping (Ivan Levkivskyi, PR [18943](https://github.com/python/mypy/pull/18943))
+* Update test requirements (Marc Mueller, PR [19163](https://github.com/python/mypy/pull/19163))
+* Use more lower case builtins in error messages (Marc Mueller, PR [19177](https://github.com/python/mypy/pull/19177))
+* Remove force_uppercase_builtins default from test helpers (Marc Mueller, PR [19173](https://github.com/python/mypy/pull/19173))
+* Start testing Python 3.14 (Marc Mueller, PR [19164](https://github.com/python/mypy/pull/19164))
+* Fix example to use correct method of Stack (Łukasz Kwieciński, PR [19123](https://github.com/python/mypy/pull/19123))
+* Fix nondeterministic type checking caused by nonassociative of None joins (Shantanu, PR [19158](https://github.com/python/mypy/pull/19158))
+* Drop support for --python-version 3.8 (Marc Mueller, PR [19157](https://github.com/python/mypy/pull/19157))
+* Fix nondeterministic type checking caused by nonassociativity of joins (Shantanu, PR [19147](https://github.com/python/mypy/pull/19147))
+* Fix nondeterministic type checking by making join between TypeType and TypeVar commute (Shantanu, PR [19149](https://github.com/python/mypy/pull/19149))
+* Forbid `.pop` of `Readonly` `NotRequired` TypedDict items (Stanislav Terliakov, PR [19133](https://github.com/python/mypy/pull/19133))
+* Emit a friendlier warning on invalid exclude regex, instead of a stacktrace (wyattscarpenter, PR [19102](https://github.com/python/mypy/pull/19102))
+* Update dmypy/client.py:  Enable ANSI color codes for windows cmd (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088))
+* Extend special case for context-based typevar inference to typevar unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976))
+
+### Acknowledgements
+
+Thanks to all mypy contributors who contributed to this release:
+
+* Alexey Makridenko
+* Brian Schubert
+* Chad Dombrova
+* Chainfire
+* Charlie Denton
+* Charulata
+* Christoph Tyralla
+* CoolCat467
+* Donal Burns
+* Guy Wilson
+* Ivan Levkivskyi
+* johnthagen
+* Jukka Lehtosalo
+* Łukasz Kwieciński
+* Marc Mueller
+* Michael J. Sullivan
+* Mikhail Golubev
+* Sebastian Rittau
+* Shantanu
+* Stanislav Terliakov
+* wyattscarpenter
+
+I’d also like to thank my employer, Dropbox, for supporting mypy development.
+
 ## Mypy 1.16
 
 We’ve just uploaded mypy 1.16 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).

From c66417d11a33eb4c9d1e34dcb75dd5b826cb7420 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 14 Jul 2025 16:51:20 +0100
Subject: [PATCH 1460/1617] Updates to 1.17 changelog (#19436)

Add a few sections and do some editing.
---
 CHANGELOG.md | 168 ++++++++++++++++++++++++++++++++-------------------
 1 file changed, 107 insertions(+), 61 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4f148fe6382..a74fb46aba6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ## Next Release
 
-## Mypy 1.17 (Unreleased)
+## Mypy 1.17
 
 We’ve just uploaded mypy 1.17 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
 Mypy is a static type checker for Python. This release includes new features and bug fixes.
@@ -12,11 +12,60 @@ You can install it as follows:
 
 You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io).
 
-### Remove Support for targeting Python 3.8
+### Optionally Check That Match Is Exhaustive
 
-Mypy now requires `--python-version 3.9` or greater. Support for only Python 3.8 is
-fully removed now. Given an unsupported version, mypy will default to the oldest
-supported one, currently 3.9.
+Mypy can now optionally generate an error if a match statement does not
+match exhaustively, without having to use `assert_never(...)`. Enable
+this by using `--enable-error-code exhaustive-match`.
+
+Example:
+
+```python
+# mypy: enable-error-code=exhaustive-match
+
+import enum
+
+class Color(enum.Enum):
+    RED = 1
+    BLUE = 2
+
+def show_color(val: Color) -> None:
+    # error: Unhandled case for values of type "Literal[Color.BLUE]"
+    match val:
+        case Color.RED:
+            print("red")
+```
+
+This feature was contributed by Donal Burns (PR [19144](https://github.com/python/mypy/pull/19144)).
+
+### Further Improvements to Attribute Resolution
+
+This release includes additional improvements to how attribute types
+and kinds are resolved. These fix many bugs and overall improve consistency.
+
+* Handle corner case: protocol/class variable/descriptor (Ivan Levkivskyi, PR [19277](https://github.com/python/mypy/pull/19277))
+* Fix a few inconsistencies in protocol/type object interactions (Ivan Levkivskyi, PR [19267](https://github.com/python/mypy/pull/19267))
+* Refactor/unify access to static attributes (Ivan Levkivskyi, PR [19254](https://github.com/python/mypy/pull/19254))
+* Remove inconsistencies in operator handling (Ivan Levkivskyi, PR [19250](https://github.com/python/mypy/pull/19250))
+* Make protocol subtyping more consistent (Ivan Levkivskyi, PR [18943](https://github.com/python/mypy/pull/18943))
+
+### Fixes to Nondeterministic Type Checking
+
+Previous mypy versions could infer different types for certain expressions
+across different runs (typically depending on which order certain types
+were processed, and this order was nondeterministic). This release includes
+fixes to several such issues.
+
+* Fix nondeterministic type checking by making join with explicit Protocol and type promotion commute (Shantanu, PR [18402](https://github.com/python/mypy/pull/18402))
+* Fix nondeterministic type checking caused by nonassociative of None joins (Shantanu, PR [19158](https://github.com/python/mypy/pull/19158))
+* Fix nondeterministic type checking caused by nonassociativity of joins (Shantanu, PR [19147](https://github.com/python/mypy/pull/19147))
+* Fix nondeterministic type checking by making join between `type` and TypeVar commute (Shantanu, PR [19149](https://github.com/python/mypy/pull/19149))
+
+### Remove Support for Targeting Python 3.8
+
+Mypy now requires `--python-version 3.9` or greater. Support for targeting Python 3.8 is
+fully removed now. Since 3.8 is an unsupported version, mypy will default to the oldest
+supported version (currently 3.9) if you still try to target 3.8.
 
 This change is necessary because typeshed stopped supporting Python 3.8 after it
 reached its End of Life in October 2024.
@@ -27,102 +76,99 @@ Contributed by Marc Mueller
 ### Initial Support for Python 3.14
 
 Mypy is now tested on 3.14 and mypyc works with 3.14.0b3 and later.
-Mypyc compiled wheels of mypy itself will be available for new versions after 3.14.0rc1 is released.
+Binary wheels compiled with mypyc for mypy itself will be available for 3.14
+some time after 3.14.0rc1 has been released.
 
-Note that not all new features might be supported just yet.
+Note that not all features are supported just yet.
 
 Contributed by Marc Mueller (PR [19164](https://github.com/python/mypy/pull/19164))
 
-### Deprecated Flag: \--force-uppercase-builtins
+### 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 only supports Python 3.9+. The `--force-uppercase-builtins` flag is now
+deprecated as unnecessary, 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))
 
-### Mypyc Fixes and Improvements
+### Mypyc: Improvements to Generators and Async Functions
+
+This release includes both performance improvements and bug fixes related
+to generators and async functions (these share many implementation details).
 
 * Fix exception swallowing in async try/finally blocks with await (Chainfire, PR [19353](https://github.com/python/mypy/pull/19353))
 * Fix AttributeError in async try/finally with mixed return paths (Chainfire, PR [19361](https://github.com/python/mypy/pull/19361))
-* Derive .c file name from full module name if using multi_file (Jukka Lehtosalo, PR [19278](https://github.com/python/mypy/pull/19278))
-* Support overriding the group name used in output files (Jukka Lehtosalo, PR [19272](https://github.com/python/mypy/pull/19272))
 * Make generated generator helper method internal (Jukka Lehtosalo, PR [19268](https://github.com/python/mypy/pull/19268))
-* Add note about using non-native class to subclass built-in types (Jukka Lehtosalo, PR [19236](https://github.com/python/mypy/pull/19236))
-* Make some generated classes implicitly final (Jukka Lehtosalo, PR [19235](https://github.com/python/mypy/pull/19235))
 * Free coroutine after await encounters StopIteration (Jukka Lehtosalo, PR [19231](https://github.com/python/mypy/pull/19231))
 * Use non-tagged integer for generator label (Jukka Lehtosalo, PR [19218](https://github.com/python/mypy/pull/19218))
 * Merge generator and environment classes in simple cases (Jukka Lehtosalo, PR [19207](https://github.com/python/mypy/pull/19207))
-* Don't simplify module prefixes if using separate compilation (Jukka Lehtosalo, PR [19206](https://github.com/python/mypy/pull/19206))
-* Test function nesting with async functions (Jukka Lehtosalo, PR [19203](https://github.com/python/mypy/pull/19203))
+
+### Mypyc: Partial, Unsafe Support for Free Threading
+
+Mypyc has minimal, quite memory-unsafe support for the free threaded
+builds of 3.14. It is also only lightly tested. Bug reports and experience
+reports are welcome!
+
+Here are some of the major limitations:
+* Free threading only works when compiling a single module at a time.
+* If there is concurrent access to an object while another thread is mutating the same
+  object, it's possible to encounter segfaults and memory corruption.
+* There are no efficient native primitives for thread synthronization, though the
+  regular `threading` module can be used.
+* Some workloads don't scale well to multiple threads for no clear reason.
+
+Related PRs:
+
 * Enable partial, unsafe support for free-threading (Jukka Lehtosalo, PR [19167](https://github.com/python/mypy/pull/19167))
-* Add comment about incref/decref and free-threaded builds (Jukka Lehtosalo, PR [19155](https://github.com/python/mypy/pull/19155))
-* Refactor extension module C generation and generated C (Jukka Lehtosalo, PR [19126](https://github.com/python/mypy/pull/19126))
 * Fix incref/decref on free-threaded builds (Jukka Lehtosalo, PR [19127](https://github.com/python/mypy/pull/19127))
-* Remove last unreachable block from mypyc code (Stanislav Terliakov, PR [19086](https://github.com/python/mypy/pull/19086))
 
-### Stubgen Improvements
+### Other Mypyc Fixes and Improvements
 
-* stubgen: add test case for handling `Incomplete` return types (Alexey Makridenko, PR [19253](https://github.com/python/mypy/pull/19253))
-* stubgen: add import for `types` in `__exit__` method signature (Alexey Makridenko, PR [19120](https://github.com/python/mypy/pull/19120))
-* stubgenc: add support for including class and property docstrings (Chad Dombrova, PR [17964](https://github.com/python/mypy/pull/17964))
-* stubgen: Don't generate `Incomplete | None = None` argument annotation (Sebastian Rittau, PR [19097](https://github.com/python/mypy/pull/19097))
-* Support several more constructs in stubgen's AliasPrinter (Stanislav Terliakov, PR [18888](https://github.com/python/mypy/pull/18888))
+* Derive .c file name from full module name if using multi_file (Jukka Lehtosalo, PR [19278](https://github.com/python/mypy/pull/19278))
+* Support overriding the group name used in output files (Jukka Lehtosalo, PR [19272](https://github.com/python/mypy/pull/19272))
+* Add note about using non-native class to subclass built-in types (Jukka Lehtosalo, PR [19236](https://github.com/python/mypy/pull/19236))
+* Make some generated classes implicitly final (Jukka Lehtosalo, PR [19235](https://github.com/python/mypy/pull/19235))
+* Don't simplify module prefixes if using separate compilation (Jukka Lehtosalo, PR [19206](https://github.com/python/mypy/pull/19206))
 
-### Stubtest Improvements
+### Stubgen Improvements
 
-* Syntax error messages capitalization (Charulata, PR [19114](https://github.com/python/mypy/pull/19114))
+* Add import for `types` in `__exit__` method signature (Alexey Makridenko, PR [19120](https://github.com/python/mypy/pull/19120))
+* Add support for including class and property docstrings (Chad Dombrova, PR [17964](https://github.com/python/mypy/pull/17964))
+* Don't generate `Incomplete | None = None` argument annotation (Sebastian Rittau, PR [19097](https://github.com/python/mypy/pull/19097))
+* Support several more constructs in stubgen's alias printer (Stanislav Terliakov, PR [18888](https://github.com/python/mypy/pull/18888))
 
 ### Miscellaneous Fixes and Improvements
 
 * Combine the revealed types of multiple iteration steps in a more robust manner (Christoph Tyralla, PR [19324](https://github.com/python/mypy/pull/19324))
 * Improve the handling of "iteration dependent" errors and notes in finally clauses (Christoph Tyralla, PR [19270](https://github.com/python/mypy/pull/19270))
 * Lessen dmypy suggest path limitations for Windows machines (CoolCat467, PR [19337](https://github.com/python/mypy/pull/19337))
-* Type ignore comments erroneously marked as unused by dmypy (Charlie Denton, PR [15043](https://github.com/python/mypy/pull/15043))
-* Handle corner case: protocol vs classvar vs descriptor (Ivan Levkivskyi, PR [19277](https://github.com/python/mypy/pull/19277))
-* Fix `exhaustive-match` error code in title (johnthagen, PR [19276](https://github.com/python/mypy/pull/19276))
-* Fix couple inconsistencies in protocols vs TypeType (Ivan Levkivskyi, PR [19267](https://github.com/python/mypy/pull/19267))
+* Fix type ignore comments erroneously marked as unused by dmypy (Charlie Denton, PR [15043](https://github.com/python/mypy/pull/15043))
+* Fix misspelled `exhaustive-match` error code (johnthagen, PR [19276](https://github.com/python/mypy/pull/19276))
 * Fix missing error context for unpacking assignment involving star expression (Brian Schubert, PR [19258](https://github.com/python/mypy/pull/19258))
 * Fix and simplify error de-duplication (Ivan Levkivskyi, PR [19247](https://github.com/python/mypy/pull/19247))
-* Add regression test for narrowing union of mixins (Shantanu, PR [19266](https://github.com/python/mypy/pull/19266))
 * Disallow `ClassVar` in type aliases (Brian Schubert, PR [19263](https://github.com/python/mypy/pull/19263))
-* Refactor/unify access to static attributes (Ivan Levkivskyi, PR [19254](https://github.com/python/mypy/pull/19254))
-* Clean-up and move operator access to checkmember.py (Ivan Levkivskyi, PR [19250](https://github.com/python/mypy/pull/19250))
-* Add script that prints compiled files when self compiling (Jukka Lehtosalo, PR [19260](https://github.com/python/mypy/pull/19260))
+* Add script that prints list of compiled files when compiling mypy (Jukka Lehtosalo, PR [19260](https://github.com/python/mypy/pull/19260))
 * Fix help message url for "None and Optional handling" section (Guy Wilson, PR [19252](https://github.com/python/mypy/pull/19252))
-* Display FQN for imported base classes in errors about incompatible overrides (Mikhail Golubev, PR [19115](https://github.com/python/mypy/pull/19115))
-* Fix a minor merge conflict caused by #19118 (Christoph Tyralla, PR [19246](https://github.com/python/mypy/pull/19246))
+* Display fully qualified name of imported base classes in errors about incompatible overrides (Mikhail Golubev, PR [19115](https://github.com/python/mypy/pull/19115))
 * Avoid false `unreachable`, `redundant-expr`, and `redundant-casts` warnings in loops more robustly and efficiently, and avoid multiple `revealed type` notes for the same line (Christoph Tyralla, PR [19118](https://github.com/python/mypy/pull/19118))
 * Fix type extraction from `isinstance` checks (Stanislav Terliakov, PR [19223](https://github.com/python/mypy/pull/19223))
-* Erase stray typevars in functools.partial generic (Stanislav Terliakov, PR [18954](https://github.com/python/mypy/pull/18954))
-* Make infer_condition_value recognize the whole truth table (Stanislav Terliakov, PR [18944](https://github.com/python/mypy/pull/18944))
+* Erase stray type variables in `functools.partial` (Stanislav Terliakov, PR [18954](https://github.com/python/mypy/pull/18954))
+* Make inferring condition value recognize the whole truth table (Stanislav Terliakov, PR [18944](https://github.com/python/mypy/pull/18944))
 * Support type aliases, `NamedTuple` and `TypedDict` in constrained TypeVar defaults (Stanislav Terliakov, PR [18884](https://github.com/python/mypy/pull/18884))
-* Move dataclass kw_only fields to the end of the signature (Stanislav Terliakov, PR [19018](https://github.com/python/mypy/pull/19018))
-* Deprecated --force-uppercase-builtins flag (Marc Mueller, PR [19176](https://github.com/python/mypy/pull/19176))
-* Provide a better fallback value for the python_version option (Marc Mueller, PR [19162](https://github.com/python/mypy/pull/19162))
-* Avoid spurious non-overlapping eq error with metaclass with `__eq__` (Michael J. Sullivan, PR [19220](https://github.com/python/mypy/pull/19220))
-* Remove --show-speed-regression in primer (Shantanu, PR [19226](https://github.com/python/mypy/pull/19226))
-* Add flag to raise error if match statement does not match exaustively (Donal Burns, PR [19144](https://github.com/python/mypy/pull/19144))
-* Narrow type variable bounds in binder (Ivan Levkivskyi, PR [19183](https://github.com/python/mypy/pull/19183))
-* Add regression test for dataclass typeguard (Shantanu, PR [19214](https://github.com/python/mypy/pull/19214))
+* Move dataclass `kw_only` fields to the end of the signature (Stanislav Terliakov, PR [19018](https://github.com/python/mypy/pull/19018))
+* Provide a better fallback value for the `python_version` option (Marc Mueller, PR [19162](https://github.com/python/mypy/pull/19162))
+* Avoid spurious non-overlapping equality error with metaclass with `__eq__` (Michael J. Sullivan, PR [19220](https://github.com/python/mypy/pull/19220))
+* Narrow type variable bounds (Ivan Levkivskyi, PR [19183](https://github.com/python/mypy/pull/19183))
 * Add classifier for Python 3.14 (Marc Mueller, PR [19199](https://github.com/python/mypy/pull/19199))
-* Further cleanup after dropping Python 3.8 (Marc Mueller, PR [19197](https://github.com/python/mypy/pull/19197))
-* Fix nondeterministic type checking by making join with explicit Protocol and type promotion commute (Shantanu, PR [18402](https://github.com/python/mypy/pull/18402))
+* Capitalize syntax error messages (Charulata, PR [19114](https://github.com/python/mypy/pull/19114))
 * Infer constraints eagerly if actual is Any (Ivan Levkivskyi, PR [19190](https://github.com/python/mypy/pull/19190))
 * Include walrus assignments in conditional inference (Stanislav Terliakov, PR [19038](https://github.com/python/mypy/pull/19038))
-* Use PEP 604 syntax for TypeStrVisitor (Marc Mueller, PR [19179](https://github.com/python/mypy/pull/19179))
-* Use checkmember.py to check protocol subtyping (Ivan Levkivskyi, PR [18943](https://github.com/python/mypy/pull/18943))
-* Update test requirements (Marc Mueller, PR [19163](https://github.com/python/mypy/pull/19163))
-* Use more lower case builtins in error messages (Marc Mueller, PR [19177](https://github.com/python/mypy/pull/19177))
-* Remove force_uppercase_builtins default from test helpers (Marc Mueller, PR [19173](https://github.com/python/mypy/pull/19173))
-* Start testing Python 3.14 (Marc Mueller, PR [19164](https://github.com/python/mypy/pull/19164))
+* Use PEP 604 syntax when converting types to strings (Marc Mueller, PR [19179](https://github.com/python/mypy/pull/19179))
+* Use more lower-case builtin types in error messages (Marc Mueller, PR [19177](https://github.com/python/mypy/pull/19177))
 * Fix example to use correct method of Stack (Łukasz Kwieciński, PR [19123](https://github.com/python/mypy/pull/19123))
-* Fix nondeterministic type checking caused by nonassociative of None joins (Shantanu, PR [19158](https://github.com/python/mypy/pull/19158))
-* Drop support for --python-version 3.8 (Marc Mueller, PR [19157](https://github.com/python/mypy/pull/19157))
-* Fix nondeterministic type checking caused by nonassociativity of joins (Shantanu, PR [19147](https://github.com/python/mypy/pull/19147))
-* Fix nondeterministic type checking by making join between TypeType and TypeVar commute (Shantanu, PR [19149](https://github.com/python/mypy/pull/19149))
 * Forbid `.pop` of `Readonly` `NotRequired` TypedDict items (Stanislav Terliakov, PR [19133](https://github.com/python/mypy/pull/19133))
 * Emit a friendlier warning on invalid exclude regex, instead of a stacktrace (wyattscarpenter, PR [19102](https://github.com/python/mypy/pull/19102))
-* Update dmypy/client.py:  Enable ANSI color codes for windows cmd (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088))
-* Extend special case for context-based typevar inference to typevar unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976))
+* Enable ANSI color codes for dmypy client in Windows (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088))
+* Extend special case for context-based type variable inference to unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976))
 
 ### Acknowledgements
 

From 32f57e4b09d60b3d492e7c4e9e3682272fe9c566 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 15 Jul 2025 12:30:51 +0100
Subject: [PATCH 1461/1617] [mypyc] Add SetElement op for initializing struct
 values (#19437)

Also add Undef value type that can currently only used as the
operand for SetElement to signify that we are creating a new
value instead of modifying an existing value.

A new struct value can be created by starting with Undef and
setting each element sequentially. Each operation produces a
new struct value, but the temporaries will be optimized away
in the later passes (currently by the C compiler, but we could
do something more clever here in the future).

This is needed to support packed arrays, which are represented
as structs. I extracted this from my packed array branch, and
it's currently unused outside tests.
---
 mypyc/analysis/dataflow.py      |  7 +++-
 mypyc/analysis/ircheck.py       |  8 ++++-
 mypyc/analysis/selfleaks.py     |  4 +++
 mypyc/codegen/emitfunc.py       | 27 +++++++++++++++
 mypyc/ir/ops.py                 | 58 +++++++++++++++++++++++++++++++++
 mypyc/ir/pprint.py              |  9 ++++-
 mypyc/test/test_emitfunc.py     | 18 ++++++++++
 mypyc/transform/ir_transform.py |  7 ++++
 mypyc/transform/refcount.py     |  3 +-
 9 files changed, 137 insertions(+), 4 deletions(-)

diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py
index db62ef1700fa..827c70a0eb4d 100644
--- a/mypyc/analysis/dataflow.py
+++ b/mypyc/analysis/dataflow.py
@@ -45,12 +45,14 @@
     RegisterOp,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
     TupleSet,
     Unborrow,
     Unbox,
+    Undef,
     Unreachable,
     Value,
 )
@@ -272,6 +274,9 @@ def visit_load_mem(self, op: LoadMem) -> GenAndKill[T]:
     def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill[T]:
         return self.visit_register_op(op)
 
+    def visit_set_element(self, op: SetElement) -> GenAndKill[T]:
+        return self.visit_register_op(op)
+
     def visit_load_address(self, op: LoadAddress) -> GenAndKill[T]:
         return self.visit_register_op(op)
 
@@ -444,7 +449,7 @@ def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]:
 def non_trivial_sources(op: Op) -> set[Value]:
     result = set()
     for source in op.sources():
-        if not isinstance(source, (Integer, Float)):
+        if not isinstance(source, (Integer, Float, Undef)):
             result.add(source)
     return result
 
diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py
index 88737ac208de..4ad2a52c1036 100644
--- a/mypyc/analysis/ircheck.py
+++ b/mypyc/analysis/ircheck.py
@@ -17,6 +17,7 @@
     ControlOp,
     DecRef,
     Extend,
+    Float,
     FloatComparisonOp,
     FloatNeg,
     FloatOp,
@@ -42,12 +43,14 @@
     Register,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
     TupleSet,
     Unborrow,
     Unbox,
+    Undef,
     Unreachable,
     Value,
 )
@@ -148,7 +151,7 @@ def check_op_sources_valid(fn: FuncIR) -> list[FnError]:
     for block in fn.blocks:
         for op in block.ops:
             for source in op.sources():
-                if isinstance(source, Integer):
+                if isinstance(source, (Integer, Float, Undef)):
                     pass
                 elif isinstance(source, Op):
                     if source not in valid_ops:
@@ -423,6 +426,9 @@ def visit_set_mem(self, op: SetMem) -> None:
     def visit_get_element_ptr(self, op: GetElementPtr) -> None:
         pass
 
+    def visit_set_element(self, op: SetElement) -> None:
+        pass
+
     def visit_load_address(self, op: LoadAddress) -> None:
         pass
 
diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py
index 9f7e00db78d2..8f46cbe3312b 100644
--- a/mypyc/analysis/selfleaks.py
+++ b/mypyc/analysis/selfleaks.py
@@ -35,6 +35,7 @@
     RegisterOp,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
@@ -181,6 +182,9 @@ def visit_load_mem(self, op: LoadMem) -> GenAndKill:
     def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill:
         return CLEAN
 
+    def visit_set_element(self, op: SetElement) -> GenAndKill:
+        return CLEAN
+
     def visit_load_address(self, op: LoadAddress) -> GenAndKill:
         return CLEAN
 
diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 3fdd08037d1a..9012f072f96b 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -70,12 +70,14 @@
     Register,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
     TupleSet,
     Unborrow,
     Unbox,
+    Undef,
     Unreachable,
     Value,
 )
@@ -813,6 +815,31 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None:
             )
         )
 
+    def visit_set_element(self, op: SetElement) -> None:
+        dest = self.reg(op)
+        item = self.reg(op.item)
+        field = op.field
+        if isinstance(op.src, Undef):
+            # First assignment to an undefined struct is trivial.
+            self.emit_line(f"{dest}.{field} = {item};")
+        else:
+            # In the general case create a copy of the struct with a single
+            # item modified.
+            #
+            # TODO: Can we do better if only a subset of fields are initialized?
+            # TODO: Make this less verbose in the common case
+            # TODO: Support tuples (or use RStruct for tuples)?
+            src = self.reg(op.src)
+            src_type = op.src.type
+            assert isinstance(src_type, RStruct), src_type
+            init_items = []
+            for n in src_type.names:
+                if n != field:
+                    init_items.append(f"{src}.{n}")
+                else:
+                    init_items.append(item)
+            self.emit_line(f"{dest} = ({self.ctype(src_type)}) {{ {', '.join(init_items)} }};")
+
     def visit_load_address(self, op: LoadAddress) -> None:
         typ = op.type
         dest = self.reg(op)
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index f362b0cca197..4829dd6a903d 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -34,6 +34,7 @@ class to enable the new behavior. Sometimes adding a new abstract
 from mypyc.ir.rtypes import (
     RArray,
     RInstance,
+    RStruct,
     RTuple,
     RType,
     RVoid,
@@ -244,6 +245,26 @@ def __init__(self, value: bytes, line: int = -1) -> None:
         self.line = line
 
 
+@final
+class Undef(Value):
+    """An undefined value.
+
+    Use Undef() as the initial value followed by one or more SetElement
+    ops to initialize a struct. Pseudocode example:
+
+      r0 = set_element undef MyStruct, "field1", f1
+      r1 = set_element r0, "field2", f2
+      # r1 now has new struct value with two fields set
+
+    Warning: Always initialize undefined values before using them,
+    as otherwise the values are garbage. You shouldn't expect that
+    undefined values are zeroed, in particular.
+    """
+
+    def __init__(self, rtype: RType) -> None:
+        self.type = rtype
+
+
 class Op(Value):
     """Abstract base class for all IR operations.
 
@@ -1636,6 +1657,39 @@ def accept(self, visitor: OpVisitor[T]) -> T:
         return visitor.visit_get_element_ptr(self)
 
 
+@final
+class SetElement(RegisterOp):
+    """Set the value of a struct element.
+
+    This evaluates to a new struct with the changed value.
+
+    Use together with Undef to initialize a fresh struct value
+    (see Undef for more details).
+    """
+
+    error_kind = ERR_NEVER
+
+    def __init__(self, src: Value, field: str, item: Value, line: int = -1) -> None:
+        super().__init__(line)
+        assert isinstance(src.type, RStruct), src.type
+        self.type = src.type
+        self.src = src
+        self.item = item
+        self.field = field
+
+    def sources(self) -> list[Value]:
+        return [self.src]
+
+    def set_sources(self, new: list[Value]) -> None:
+        (self.src,) = new
+
+    def stolen(self) -> list[Value]:
+        return [self.src]
+
+    def accept(self, visitor: OpVisitor[T]) -> T:
+        return visitor.visit_set_element(self)
+
+
 @final
 class LoadAddress(RegisterOp):
     """Get the address of a value: result = (type)&src
@@ -1908,6 +1962,10 @@ def visit_set_mem(self, op: SetMem) -> T:
     def visit_get_element_ptr(self, op: GetElementPtr) -> T:
         raise NotImplementedError
 
+    @abstractmethod
+    def visit_set_element(self, op: SetElement) -> T:
+        raise NotImplementedError
+
     @abstractmethod
     def visit_load_address(self, op: LoadAddress) -> T:
         raise NotImplementedError
diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py
index 5bb11cc231cc..2a239a0b4d9d 100644
--- a/mypyc/ir/pprint.py
+++ b/mypyc/ir/pprint.py
@@ -50,12 +50,14 @@
     Register,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
     TupleSet,
     Unborrow,
     Unbox,
+    Undef,
     Unreachable,
     Value,
 )
@@ -273,6 +275,9 @@ def visit_set_mem(self, op: SetMem) -> str:
     def visit_get_element_ptr(self, op: GetElementPtr) -> str:
         return self.format("%r = get_element_ptr %r %s :: %t", op, op.src, op.field, op.src_type)
 
+    def visit_set_element(self, op: SetElement) -> str:
+        return self.format("%r = set_element %r, %s, %r", op, op.src, op.field, op.item)
+
     def visit_load_address(self, op: LoadAddress) -> str:
         if isinstance(op.src, Register):
             return self.format("%r = load_address %r", op, op.src)
@@ -330,6 +335,8 @@ def format(self, fmt: str, *args: Any) -> str:
                         result.append(repr(arg.value))
                     elif isinstance(arg, CString):
                         result.append(f"CString({arg.value!r})")
+                    elif isinstance(arg, Undef):
+                        result.append(f"undef {arg.type.name}")
                     else:
                         result.append(self.names[arg])
                 elif typespec == "d":
@@ -486,7 +493,7 @@ def generate_names_for_ir(args: list[Register], blocks: list[BasicBlock]) -> dic
                     continue
                 if isinstance(value, Register) and value.name:
                     name = value.name
-                elif isinstance(value, (Integer, Float)):
+                elif isinstance(value, (Integer, Float, Undef)):
                     continue
                 else:
                     name = "r%d" % temp_index
diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py
index 6be4875dafa1..6382271cfe94 100644
--- a/mypyc/test/test_emitfunc.py
+++ b/mypyc/test/test_emitfunc.py
@@ -35,9 +35,11 @@
     Register,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     TupleGet,
     Unbox,
+    Undef,
     Unreachable,
     Value,
 )
@@ -121,6 +123,11 @@ def add_local(name: str, rtype: RType) -> Register:
         self.r = add_local("r", RInstance(ir))
         self.none = add_local("none", none_rprimitive)
 
+        self.struct_type = RStruct(
+            "Foo", ["b", "x", "y"], [bool_rprimitive, int32_rprimitive, int64_rprimitive]
+        )
+        self.st = add_local("st", self.struct_type)
+
         self.context = EmitterContext(NameGenerator([["mod"]]))
 
     def test_goto(self) -> None:
@@ -674,6 +681,17 @@ def test_get_element_ptr(self) -> None:
             GetElementPtr(self.o, r, "i64"), """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i64;"""
         )
 
+    def test_set_element(self) -> None:
+        # Use compact syntax when setting the initial element of an undefined value
+        self.assert_emit(
+            SetElement(Undef(self.struct_type), "b", self.b), """cpy_r_r0.b = cpy_r_b;"""
+        )
+        # We propagate the unchanged values in subsequent assignments
+        self.assert_emit(
+            SetElement(self.st, "x", self.i32),
+            """cpy_r_r0 = (Foo) { cpy_r_st.b, cpy_r_i32, cpy_r_st.y };""",
+        )
+
     def test_load_address(self) -> None:
         self.assert_emit(
             LoadAddress(object_rprimitive, "PyDict_Type"),
diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py
index 7834fed39465..bcb6db9b0daf 100644
--- a/mypyc/transform/ir_transform.py
+++ b/mypyc/transform/ir_transform.py
@@ -39,6 +39,7 @@
     RaiseStandardError,
     Return,
     SetAttr,
+    SetElement,
     SetMem,
     Truncate,
     TupleGet,
@@ -214,6 +215,9 @@ def visit_set_mem(self, op: SetMem) -> Value | None:
     def visit_get_element_ptr(self, op: GetElementPtr) -> Value | None:
         return self.add(op)
 
+    def visit_set_element(self, op: SetElement) -> Value | None:
+        return self.add(op)
+
     def visit_load_address(self, op: LoadAddress) -> Value | None:
         return self.add(op)
 
@@ -354,6 +358,9 @@ def visit_set_mem(self, op: SetMem) -> None:
     def visit_get_element_ptr(self, op: GetElementPtr) -> None:
         op.src = self.fix_op(op.src)
 
+    def visit_set_element(self, op: SetElement) -> None:
+        op.src = self.fix_op(op.src)
+
     def visit_load_address(self, op: LoadAddress) -> None:
         if isinstance(op.src, LoadStatic):
             new = self.fix_op(op.src)
diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py
index c589918986f0..60daebc415fd 100644
--- a/mypyc/transform/refcount.py
+++ b/mypyc/transform/refcount.py
@@ -43,6 +43,7 @@
     Op,
     Register,
     RegisterOp,
+    Undef,
     Value,
 )
 
@@ -94,7 +95,7 @@ def is_maybe_undefined(post_must_defined: set[Value], src: Value) -> bool:
 def maybe_append_dec_ref(
     ops: list[Op], dest: Value, defined: AnalysisDict[Value], key: tuple[BasicBlock, int]
 ) -> None:
-    if dest.type.is_refcounted and not isinstance(dest, Integer):
+    if dest.type.is_refcounted and not isinstance(dest, (Integer, Undef)):
         ops.append(DecRef(dest, is_xdec=is_maybe_undefined(defined[key], dest)))
 
 

From 38cdacfd142291eae3f30c1a4a83f4042a159975 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Tue, 15 Jul 2025 14:08:01 +0200
Subject: [PATCH 1462/1617] [mypyc] Add primitives for isinstance of built-in
 types (#19435)

Follow-up to #19416 adding primitives for `isinstance(obj, type)` where
type is built-in.
---
 mypyc/irbuild/specialize.py             |  23 +++-
 mypyc/primitives/bytes_ops.py           |  21 ++-
 mypyc/primitives/dict_ops.py            |   9 ++
 mypyc/primitives/float_ops.py           |  10 ++
 mypyc/primitives/int_ops.py             |   9 ++
 mypyc/primitives/list_ops.py            |   2 +-
 mypyc/primitives/misc_ops.py            |   9 ++
 mypyc/primitives/set_ops.py             |  20 ++-
 mypyc/primitives/str_ops.py             |   9 ++
 mypyc/primitives/tuple_ops.py           |  10 ++
 mypyc/test-data/irbuild-basic.test      |  37 ++----
 mypyc/test-data/irbuild-i64.test        |  28 ++--
 mypyc/test-data/irbuild-isinstance.test | 162 ++++++++++++++++++------
 mypyc/test-data/irbuild-optional.test   |  30 ++---
 mypyc/test-data/run-bools.test          |  26 ++++
 mypyc/test-data/run-bytes.test          |  51 ++++++++
 mypyc/test-data/run-dicts.test          |  32 +++++
 mypyc/test-data/run-floats.test         |  31 +++++
 mypyc/test-data/run-integers.test       |  34 +++++
 mypyc/test-data/run-sets.test           |  52 ++++++++
 mypyc/test-data/run-strings.test        |  35 +++++
 mypyc/test-data/run-tuples.test         |  32 +++++
 22 files changed, 568 insertions(+), 104 deletions(-)

diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py
index b490c2a52e57..3015640fb3fd 100644
--- a/mypyc/irbuild/specialize.py
+++ b/mypyc/irbuild/specialize.py
@@ -83,19 +83,26 @@
     join_formatted_strings,
     tokenizer_format_call,
 )
+from mypyc.primitives.bytes_ops import isinstance_bytearray, isinstance_bytes
 from mypyc.primitives.dict_ops import (
     dict_items_op,
     dict_keys_op,
     dict_setdefault_spec_init_op,
     dict_values_op,
+    isinstance_dict,
 )
+from mypyc.primitives.float_ops import isinstance_float
+from mypyc.primitives.int_ops import isinstance_int
 from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op
+from mypyc.primitives.misc_ops import isinstance_bool
+from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set
 from mypyc.primitives.str_ops import (
+    isinstance_str,
     str_encode_ascii_strict,
     str_encode_latin1_strict,
     str_encode_utf8_strict,
 )
-from mypyc.primitives.tuple_ops import new_tuple_set_item_op
+from mypyc.primitives.tuple_ops import isinstance_tuple, new_tuple_set_item_op
 
 # Specializers are attempted before compiling the arguments to the
 # function.  Specializers can return None to indicate that they failed
@@ -546,7 +553,19 @@ def gen_inner_stmts() -> None:
     return retval
 
 
-isinstance_primitives: Final = {"builtins.list": isinstance_list}
+isinstance_primitives: Final = {
+    "builtins.bool": isinstance_bool,
+    "builtins.bytearray": isinstance_bytearray,
+    "builtins.bytes": isinstance_bytes,
+    "builtins.dict": isinstance_dict,
+    "builtins.float": isinstance_float,
+    "builtins.frozenset": isinstance_frozenset,
+    "builtins.int": isinstance_int,
+    "builtins.list": isinstance_list,
+    "builtins.set": isinstance_set,
+    "builtins.str": isinstance_str,
+    "builtins.tuple": isinstance_tuple,
+}
 
 
 @specialize_function("builtins.isinstance")
diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py
index 1afd196cff84..c88e89d1a2ba 100644
--- a/mypyc/primitives/bytes_ops.py
+++ b/mypyc/primitives/bytes_ops.py
@@ -2,9 +2,10 @@
 
 from __future__ import annotations
 
-from mypyc.ir.ops import ERR_MAGIC
+from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
 from mypyc.ir.rtypes import (
     RUnion,
+    bit_rprimitive,
     bytes_rprimitive,
     c_int_rprimitive,
     c_pyssize_t_rprimitive,
@@ -35,6 +36,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, bytes)
+isinstance_bytes = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyBytes_Check",
+    error_kind=ERR_NEVER,
+)
+
 # bytearray(obj)
 function_op(
     name="builtins.bytearray",
@@ -44,6 +54,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, bytearray)
+isinstance_bytearray = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyByteArray_Check",
+    error_kind=ERR_NEVER,
+)
+
 # bytes ==/!= (return -1/0/1)
 bytes_compare = custom_op(
     arg_types=[bytes_rprimitive, bytes_rprimitive],
diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py
index 3f289c3c6f08..ac928bb0eb50 100644
--- a/mypyc/primitives/dict_ops.py
+++ b/mypyc/primitives/dict_ops.py
@@ -71,6 +71,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, dict)
+isinstance_dict = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyDict_Check",
+    error_kind=ERR_NEVER,
+)
+
 # dict[key]
 dict_get_item_op = method_op(
     name="__getitem__",
diff --git a/mypyc/primitives/float_ops.py b/mypyc/primitives/float_ops.py
index 14e8d4caf09c..542192add542 100644
--- a/mypyc/primitives/float_ops.py
+++ b/mypyc/primitives/float_ops.py
@@ -4,6 +4,7 @@
 
 from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
 from mypyc.ir.rtypes import (
+    bit_rprimitive,
     bool_rprimitive,
     float_rprimitive,
     int_rprimitive,
@@ -166,3 +167,12 @@
     c_function_name="CPyFloat_IsNaN",
     error_kind=ERR_NEVER,
 )
+
+# translate isinstance(obj, float)
+isinstance_float = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyFloat_Check",
+    error_kind=ERR_NEVER,
+)
diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py
index 9b8b48da602d..d723c9b63a86 100644
--- a/mypyc/primitives/int_ops.py
+++ b/mypyc/primitives/int_ops.py
@@ -296,3 +296,12 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription:
     c_function_name="CPyUInt8_Overflow",
     error_kind=ERR_ALWAYS,
 )
+
+# translate isinstance(obj, int)
+isinstance_int = function_op(
+    name="builtints.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyLong_Check",
+    error_kind=ERR_NEVER,
+)
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 57cb541fdbb8..516d9e1a4e02 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -56,7 +56,7 @@
     extra_int_constants=[(0, int_rprimitive)],
 )
 
-# isinstance(obj, list)
+# translate isinstance(obj, list)
 isinstance_list = function_op(
     name="builtins.isinstance",
     arg_types=[object_rprimitive],
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index 7494b46790ce..114a5f0a9823 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -191,6 +191,15 @@
     truncated_type=bool_rprimitive,
 )
 
+# isinstance(obj, bool)
+isinstance_bool = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyBool_Check",
+    error_kind=ERR_NEVER,
+)
+
 # slice(start, stop, step)
 new_slice_op = function_op(
     name="builtins.slice",
diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py
index eb7c9b46609d..786de008746d 100644
--- a/mypyc/primitives/set_ops.py
+++ b/mypyc/primitives/set_ops.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC
+from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
 from mypyc.ir.rtypes import (
     bit_rprimitive,
     bool_rprimitive,
@@ -64,6 +64,24 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, set)
+isinstance_set = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PySet_Check",
+    error_kind=ERR_NEVER,
+)
+
+# translate isinstance(obj, frozenset)
+isinstance_frozenset = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyFrozenSet_Check",
+    error_kind=ERR_NEVER,
+)
+
 # item in set
 set_in_op = binary_op(
     name="in",
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index e3f0b9dbbc2a..f07081c6aaa5 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -48,6 +48,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, str)
+isinstance_str = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyUnicode_Check",
+    error_kind=ERR_NEVER,
+)
+
 # str1 + str2
 binary_op(
     name="+",
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index e680b6943d84..d95161acf853 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -8,6 +8,7 @@
 
 from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
 from mypyc.ir.rtypes import (
+    bit_rprimitive,
     c_pyssize_t_rprimitive,
     int_rprimitive,
     list_rprimitive,
@@ -83,6 +84,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# translate isinstance(obj, tuple)
+isinstance_tuple = function_op(
+    name="builtins.isinstance",
+    arg_types=[object_rprimitive],
+    return_type=bit_rprimitive,
+    c_function_name="PyTuple_Check",
+    error_kind=ERR_NEVER,
+)
+
 # tuple + tuple
 binary_op(
     name="+",
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index ea1b3d06869a..4a7d315ec836 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1581,24 +1581,18 @@ def main() -> None:
 [out]
 def foo(x):
     x :: union[int, str]
-    r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
-    r4 :: __main__.B
-    r5 :: __main__.A
+    r0 :: bit
+    r1 :: __main__.B
+    r2 :: __main__.A
 L0:
-    r0 = load_address PyLong_Type
-    r1 = PyObject_IsInstance(x, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    if r3 goto L1 else goto L2 :: bool
+    r0 = PyLong_Check(x)
+    if r0 goto L1 else goto L2 :: bool
 L1:
-    r4 = B()
-    return r4
+    r1 = B()
+    return r1
 L2:
-    r5 = A()
-    return r5
+    r2 = A()
+    return r2
 def main():
     r0 :: object
     r1 :: __main__.A
@@ -3389,16 +3383,11 @@ def f(x: object) -> bool:
     return isinstance(x, bool)
 [out]
 def f(x):
-    x, r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
+    x :: object
+    r0 :: bit
 L0:
-    r0 = load_address PyBool_Type
-    r1 = PyObject_IsInstance(x, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    return r3
+    r0 = PyBool_Check(x)
+    return r0
 
 [case testRangeObject]
 def range_object() -> None:
diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test
index c59e306b09df..e55c3bfe2acc 100644
--- a/mypyc/test-data/irbuild-i64.test
+++ b/mypyc/test-data/irbuild-i64.test
@@ -2046,27 +2046,21 @@ L2:
     return r6
 def narrow2(x):
     x :: union[__main__.C, i64]
-    r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
-    r4 :: i64
-    r5 :: __main__.C
-    r6 :: i64
+    r0 :: bit
+    r1 :: i64
+    r2 :: __main__.C
+    r3 :: i64
 L0:
-    r0 = load_address PyLong_Type
-    r1 = PyObject_IsInstance(x, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    if r3 goto L1 else goto L2 :: bool
+    r0 = PyLong_Check(x)
+    if r0 goto L1 else goto L2 :: bool
 L1:
-    r4 = unbox(i64, x)
-    return r4
+    r1 = unbox(i64, x)
+    return r1
 L2:
-    r5 = borrow cast(__main__.C, x)
-    r6 = r5.a
+    r2 = borrow cast(__main__.C, x)
+    r3 = r2.a
     keep_alive x
-    return r6
+    return r3
 
 [case testI64ConvertBetweenTuples_64bit]
 from __future__ import annotations
diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test
index 78da2e9c1e19..30adfe61e384 100644
--- a/mypyc/test-data/irbuild-isinstance.test
+++ b/mypyc/test-data/irbuild-isinstance.test
@@ -4,16 +4,11 @@ def is_int(value: object) -> bool:
 
 [out]
 def is_int(value):
-    value, r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
+    value :: object
+    r0 :: bit
 L0:
-    r0 = load_address PyLong_Type
-    r1 = PyObject_IsInstance(value, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    return r3
+    r0 = PyLong_Check(value)
+    return r0
 
 [case testIsinstanceNotBool1]
 def is_not_bool(value: object) -> bool:
@@ -21,17 +16,12 @@ def is_not_bool(value: object) -> bool:
 
 [out]
 def is_not_bool(value):
-    value, r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3, r4 :: bool
+    value :: object
+    r0, r1 :: bit
 L0:
-    r0 = load_address PyBool_Type
-    r1 = PyObject_IsInstance(value, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    r4 = r3 ^ 1
-    return r4
+    r0 = PyBool_Check(value)
+    r1 = r0 ^ 1
+    return r1
 
 [case testIsinstanceIntAndNotBool]
 # This test is to ensure that 'value' doesn't get coerced to int when we are
@@ -41,32 +31,22 @@ def is_not_bool_and_is_int(value: object) -> bool:
 
 [out]
 def is_not_bool_and_is_int(value):
-    value, r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3, r4 :: bool
-    r5 :: object
-    r6 :: i32
-    r7 :: bit
-    r8, r9 :: bool
+    value :: object
+    r0 :: bit
+    r1 :: bool
+    r2, r3 :: bit
 L0:
-    r0 = load_address PyLong_Type
-    r1 = PyObject_IsInstance(value, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    if r3 goto L2 else goto L1 :: bool
+    r0 = PyLong_Check(value)
+    if r0 goto L2 else goto L1 :: bool
 L1:
-    r4 = r3
+    r1 = r0
     goto L3
 L2:
-    r5 = load_address PyBool_Type
-    r6 = PyObject_IsInstance(value, r5)
-    r7 = r6 >= 0 :: signed
-    r8 = truncate r6: i32 to builtins.bool
-    r9 = r8 ^ 1
-    r4 = r9
+    r2 = PyBool_Check(value)
+    r3 = r2 ^ 1
+    r1 = r3
 L3:
-    return r4
+    return r1
 
 [case testBorrowSpecialCaseWithIsinstance]
 class C:
@@ -107,3 +87,105 @@ L1:
     keep_alive x
 L2:
     return 1
+
+[case testBytes]
+from typing import Any
+
+def is_bytes(x: Any) -> bool:
+    return isinstance(x, bytes)
+
+def is_bytearray(x: Any) -> bool:
+    return isinstance(x, bytearray)
+
+[out]
+def is_bytes(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyBytes_Check(x)
+    return r0
+def is_bytearray(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyByteArray_Check(x)
+    return r0
+
+[case testDict]
+from typing import Any
+
+def is_dict(x: Any) -> bool:
+    return isinstance(x, dict)
+
+[out]
+def is_dict(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyDict_Check(x)
+    return r0
+
+[case testFloat]
+from typing import Any
+
+def is_float(x: Any) -> bool:
+    return isinstance(x, float)
+
+[out]
+def is_float(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyFloat_Check(x)
+    return r0
+
+[case testSet]
+from typing import Any
+
+def is_set(x: Any) -> bool:
+    return isinstance(x, set)
+
+def is_frozenset(x: Any) -> bool:
+    return isinstance(x, frozenset)
+
+[out]
+def is_set(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PySet_Check(x)
+    return r0
+def is_frozenset(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyFrozenSet_Check(x)
+    return r0
+
+[case testStr]
+from typing import Any
+
+def is_str(x: Any) -> bool:
+    return isinstance(x, str)
+
+[out]
+def is_str(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyUnicode_Check(x)
+    return r0
+
+[case testTuple]
+from typing import Any
+
+def is_tuple(x: Any) -> bool:
+    return isinstance(x, tuple)
+
+[out]
+def is_tuple(x):
+    x :: object
+    r0 :: bit
+L0:
+    r0 = PyTuple_Check(x)
+    return r0
diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test
index 75c008586999..b81465d362ba 100644
--- a/mypyc/test-data/irbuild-optional.test
+++ b/mypyc/test-data/irbuild-optional.test
@@ -251,28 +251,22 @@ def f(x: Union[int, A]) -> int:
 [out]
 def f(x):
     x :: union[int, __main__.A]
-    r0 :: object
-    r1 :: i32
-    r2 :: bit
-    r3 :: bool
-    r4, r5 :: int
-    r6 :: __main__.A
-    r7 :: int
+    r0 :: bit
+    r1, r2 :: int
+    r3 :: __main__.A
+    r4 :: int
 L0:
-    r0 = load_address PyLong_Type
-    r1 = PyObject_IsInstance(x, r0)
-    r2 = r1 >= 0 :: signed
-    r3 = truncate r1: i32 to builtins.bool
-    if r3 goto L1 else goto L2 :: bool
+    r0 = PyLong_Check(x)
+    if r0 goto L1 else goto L2 :: bool
 L1:
-    r4 = unbox(int, x)
-    r5 = CPyTagged_Add(r4, 2)
-    return r5
+    r1 = unbox(int, x)
+    r2 = CPyTagged_Add(r1, 2)
+    return r2
 L2:
-    r6 = borrow cast(__main__.A, x)
-    r7 = r6.a
+    r3 = borrow cast(__main__.A, x)
+    r4 = r3.a
     keep_alive x
-    return r7
+    return r4
 L3:
     unreachable
 
diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test
index 3409665bfb37..b34fedebaa9f 100644
--- a/mypyc/test-data/run-bools.test
+++ b/mypyc/test-data/run-bools.test
@@ -228,3 +228,29 @@ def test_mix() -> None:
     print((y or 0) and True)
 [out]
 0
+
+[case testIsInstance]
+from typing import Any
+def test_built_in() -> None:
+    true: Any = True
+    false: Any = False
+    assert isinstance(true, bool)
+    assert isinstance(false, bool)
+
+    assert not isinstance(set(), bool)
+    assert not isinstance((), bool)
+    assert not isinstance((True, False), bool)
+    assert not isinstance({False, True}, bool)
+    assert not isinstance(int() + 1, bool)
+    assert not isinstance(str() + 'False', bool)
+
+def test_user_defined() -> None:
+    from userdefinedbool import bool
+
+    b: Any = True
+    assert isinstance(bool(), bool)
+    assert not isinstance(b, bool)
+
+[file userdefinedbool.py]
+class bool:
+    pass
diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test
index bee6b6fe9f76..5a285320c849 100644
--- a/mypyc/test-data/run-bytes.test
+++ b/mypyc/test-data/run-bytes.test
@@ -323,3 +323,54 @@ class A:
 def test_bytes_dunder() -> None:
     assert b'%b' % A() == b'aaa'
     assert b'%s' % A() == b'aaa'
+
+[case testIsInstance]
+from copysubclass import subbytes, subbytearray
+from typing import Any
+def test_bytes() -> None:
+    b: Any = b''
+    assert isinstance(b, bytes)
+    assert isinstance(b + b'123', bytes)
+    assert isinstance(b + b'\xff', bytes)
+    assert isinstance(subbytes(), bytes)
+    assert isinstance(subbytes(b + b'123'), bytes)
+    assert isinstance(subbytes(b + b'\xff'), bytes)
+
+    assert not isinstance(set(), bytes)
+    assert not isinstance((), bytes)
+    assert not isinstance((b'1',b'2',b'3'), bytes)
+    assert not isinstance({b'a',b'b'}, bytes)
+    assert not isinstance(int() + 1, bytes)
+    assert not isinstance(str() + 'a', bytes)
+
+def test_user_defined_bytes() -> None:
+    from userdefinedbytes import bytes
+
+    assert isinstance(bytes(), bytes)
+    assert not isinstance(b'\x7f', bytes)
+
+def test_bytearray() -> None:
+    assert isinstance(bytearray(), bytearray)
+    assert isinstance(bytearray(b'123'), bytearray)
+    assert isinstance(bytearray(b'\xff'), bytearray)
+    assert isinstance(subbytearray(), bytearray)
+    assert isinstance(subbytearray(bytearray(b'123')), bytearray)
+    assert isinstance(subbytearray(bytearray(b'\xff')), bytearray)
+
+    assert not isinstance(set(), bytearray)
+    assert not isinstance((), bytearray)
+    assert not isinstance((bytearray(b'1'),bytearray(b'2'),bytearray(b'3')), bytearray)
+    assert not isinstance([bytearray(b'a'),bytearray(b'b')], bytearray)
+    assert not isinstance(int() + 1, bytearray)
+    assert not isinstance(str() + 'a', bytearray)
+
+[file copysubclass.py]
+class subbytes(bytes):
+    pass
+
+class subbytearray(bytearray):
+    pass
+
+[file userdefinedbytes.py]
+class bytes:
+    pass
diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test
index 2a3be188ad00..2b75b32c906e 100644
--- a/mypyc/test-data/run-dicts.test
+++ b/mypyc/test-data/run-dicts.test
@@ -336,3 +336,35 @@ def test_dict_to_bool() -> None:
     for x in tmp_list:
         assert is_true(x)
         assert not is_false(x)
+
+[case testIsInstance]
+from copysubclass import subc
+def test_built_in() -> None:
+    assert isinstance({}, dict)
+    assert isinstance({'one': 1, 'two': 2}, dict)
+    assert isinstance({1: 1, 'two': 2}, dict)
+    assert isinstance(subc(), dict)
+    assert isinstance(subc({'a': 1, 'b': 2}), dict)
+    assert isinstance(subc({1: 'a', 2: 'b'}), dict)
+
+    assert not isinstance(set(), dict)
+    assert not isinstance((), dict)
+    assert not isinstance((1,2,3), dict)
+    assert not isinstance({'a','b'}, dict)
+    assert not isinstance(int() + 1, dict)
+    assert not isinstance(str() + 'a', dict)
+
+def test_user_defined() -> None:
+    from userdefineddict import dict
+
+    assert isinstance(dict(), dict)
+    assert not isinstance({1: dict()}, dict)
+
+[file copysubclass.py]
+from typing import Any
+class subc(dict[Any, Any]):
+    pass
+
+[file userdefineddict.py]
+class dict:
+    pass
diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test
index 49620f6448c7..424d52cdb0d5 100644
--- a/mypyc/test-data/run-floats.test
+++ b/mypyc/test-data/run-floats.test
@@ -512,3 +512,34 @@ def test_implement_trait_attribute() -> None:
     a.y = 8.0
     assert a.x == 7
     assert a.y == 8
+
+[case testIsInstance]
+from copysubclass import subc
+from testutil import float_vals
+from typing import Any
+def test_built_in() -> None:
+    for f in float_vals:
+        assert isinstance(float(0) + f, float)
+        assert isinstance(subc(f), float)
+
+    assert not isinstance(set(), float)
+    assert not isinstance((), float)
+    assert not isinstance((1.0, 2.0), float)
+    assert not isinstance({3.14}, float)
+    assert not isinstance(int() + 1, float)
+    assert not isinstance(str() + '4.2', float)
+
+def test_user_defined() -> None:
+    from userdefinedfloat import float
+
+    f: Any = 3.14
+    assert isinstance(float(), float)
+    assert not isinstance(f, float)
+
+[file copysubclass.py]
+class subc(float):
+    pass
+
+[file userdefinedfloat.py]
+class float:
+    pass
diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test
index d575e141b567..1163c9d942f7 100644
--- a/mypyc/test-data/run-integers.test
+++ b/mypyc/test-data/run-integers.test
@@ -538,3 +538,37 @@ def test_int_bool_min_max() -> None:
     assert min(u, z) == -10
     assert max(u, y) == False
     assert max(u, z) == True
+
+[case testIsInstance]
+from copysubclass import subc
+from typing import Any
+def test_built_in() -> None:
+    i: Any = 0
+    assert isinstance(i + 0, int)
+    assert isinstance(i + 9223372036854775808, int)
+    assert isinstance(i + -9223372036854775808, int)
+    assert isinstance(subc(), int)
+    assert isinstance(subc(9223372036854775808), int)
+    assert isinstance(subc(-9223372036854775808), int)
+
+    assert not isinstance(set(), int)
+    assert not isinstance((), int)
+    assert not isinstance((1,2,3), int)
+    assert not isinstance({1,2}, int)
+    assert not isinstance(float(0) + 1.0, int)
+    assert not isinstance(str() + '1', int)
+
+def test_user_defined() -> None:
+    from userdefinedint import int
+
+    i: Any = 42
+    assert isinstance(int(), int)
+    assert not isinstance(i, int)
+
+[file copysubclass.py]
+class subc(int):
+    pass
+
+[file userdefinedint.py]
+class int:
+    pass
diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test
index 68edd1e6b77d..2668d63bcdac 100644
--- a/mypyc/test-data/run-sets.test
+++ b/mypyc/test-data/run-sets.test
@@ -265,3 +265,55 @@ def test_in_set() -> None:
 
 def test_for_set() -> None:
     assert not s ^ {None, False, 1, 2.0, "3", b"4", 5j, (6,), CONST}, s
+
+[case testIsInstance]
+from copysubclass import subset, subfrozenset
+def test_built_in_set() -> None:
+    assert isinstance(set(), set)
+    assert isinstance({'one', 'two'}, set)
+    assert isinstance({'a', 1}, set)
+    assert isinstance(subset(), set)
+    assert isinstance(subset({'one', 'two'}), set)
+    assert isinstance(subset({'a', 1}), set)
+
+    assert not isinstance(frozenset(), set)
+    assert not isinstance({}, set)
+    assert not isinstance([], set)
+    assert not isinstance((1,2,3), set)
+    assert not isinstance({1:'a', 2:'b'}, set)
+    assert not isinstance(int() + 1, set)
+    assert not isinstance(str() + 'a', set)
+
+def test_user_defined_set() -> None:
+    from userdefinedset import set
+
+    assert isinstance(set(), set)
+    assert not isinstance({set()}, set)
+
+def test_built_in_frozenset() -> None:
+    assert isinstance(frozenset(), frozenset)
+    assert isinstance(frozenset({'one', 'two'}), frozenset)
+    assert isinstance(frozenset({'a', 1}), frozenset)
+    assert isinstance(subfrozenset(), frozenset)
+    assert isinstance(subfrozenset({'one', 'two'}), frozenset)
+    assert isinstance(subfrozenset({'a', 1}), frozenset)
+
+    assert not isinstance(set(), frozenset)
+    assert not isinstance({}, frozenset)
+    assert not isinstance([], frozenset)
+    assert not isinstance((1,2,3), frozenset)
+    assert not isinstance({1:'a', 2:'b'}, frozenset)
+    assert not isinstance(int() + 1, frozenset)
+    assert not isinstance(str() + 'a', frozenset)
+
+[file copysubclass.py]
+from typing import Any
+class subset(set[Any]):
+    pass
+
+class subfrozenset(frozenset[Any]):
+    pass
+
+[file userdefinedset.py]
+class set:
+    pass
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index 6551d9c352df..8a914c08bfb2 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -964,3 +964,38 @@ def test_count_multi_start_end_emoji() -> None:
     assert string.count("😴😴😴", 0, 12) == 1, string.count("😴😴😴", 0, 12)
     assert string.count("🚀🚀🚀", 0, 12) == 2, string.count("🚀🚀🚀", 0, 12)
     assert string.count("ñññ", 0, 12) == 1, string.count("ñññ", 0, 12)
+
+[case testIsInstance]
+from copysubclass import subc
+from typing import Any
+def test_built_in() -> None:
+    s: Any = str()
+    assert isinstance(s, str)
+    assert isinstance(s + "test", str)
+    assert isinstance(s + "ñññ", str)
+    assert isinstance(subc(), str)
+    assert isinstance(subc("test"), str)
+    assert isinstance(subc("ñññ"), str)
+
+    assert not isinstance(set(), str)
+    assert not isinstance((), str)
+    assert not isinstance(('a','b'), str)
+    assert not isinstance({'a','b'}, str)
+    assert not isinstance(int() + 1, str)
+    assert not isinstance(['a','b'], str)
+
+def test_user_defined() -> None:
+    from userdefinedstr import str
+
+    s: Any = "str"
+    assert isinstance(str(), str)
+    assert not isinstance(s, str)
+
+[file copysubclass.py]
+from typing import Any
+class subc(str):
+    pass
+
+[file userdefinedstr.py]
+class str:
+    pass
diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index fe9a8dff08c6..ea0a1cb8d852 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -294,3 +294,35 @@ def test_multiply() -> None:
     assert (1,) * 3 == res
     assert 3 * (1,) == res
     assert multiply((1,), 3) == res
+
+[case testIsInstance]
+from copysubclass import subc
+def test_built_in() -> None:
+    assert isinstance((), tuple)
+    assert isinstance((1, 2), tuple)
+    assert isinstance(('a', 'b', 'c'), tuple)
+    assert isinstance(subc(()), tuple)
+    assert isinstance(subc((1, 2)), tuple)
+    assert isinstance(subc(('a', 'b', 'c')), tuple)
+
+    assert not isinstance(set(), tuple)
+    assert not isinstance({}, tuple)
+    assert not isinstance([1,2,3], tuple)
+    assert not isinstance({'a','b'}, tuple)
+    assert not isinstance(int() + 1, tuple)
+    assert not isinstance(str() + 'a', tuple)
+
+def test_user_defined() -> None:
+    from userdefinedtuple import tuple
+
+    assert isinstance(tuple(), tuple)
+    assert not isinstance((1, tuple()), tuple)
+
+[file copysubclass.py]
+from typing import Any
+class subc(tuple[Any]):
+    pass
+
+[file userdefinedtuple.py]
+class tuple:
+    pass

From 70d78812b7190ccc6b272d745cc442412ba0bbb3 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 15 Jul 2025 13:31:35 +0100
Subject: [PATCH 1463/1617] [mypyc] Refactor: make LoadMem not borrow by
 default (#19445)

Borrowing is a dangerous default. It can only be used in very specific
circumstances, so it shouldn't be the default. This is also arguably
more consistent with other read ops which don't borrow by default.
---
 mypyc/codegen/emitfunc.py                   |  2 ++
 mypyc/ir/ops.py                             |  7 +++----
 mypyc/ir/pprint.py                          |  4 +++-
 mypyc/irbuild/for_helpers.py                |  2 +-
 mypyc/irbuild/ll_builder.py                 |  7 +++++--
 mypyc/lower/list_ops.py                     |  8 +++-----
 mypyc/test-data/irbuild-classes.test        | 14 +++++++-------
 mypyc/test-data/irbuild-isinstance.test     |  2 +-
 mypyc/test-data/irbuild-optional.test       | 10 +++++-----
 mypyc/test-data/irbuild-singledispatch.test |  8 ++++----
 mypyc/test-data/lowering-int.test           |  1 -
 mypyc/test-data/refcount.test               |  2 +-
 12 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 9012f072f96b..086be293d5b3 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -793,6 +793,8 @@ def visit_load_mem(self, op: LoadMem) -> None:
         # TODO: we shouldn't dereference to type that are pointer type so far
         type = self.ctype(op.type)
         self.emit_line(f"{dest} = *({type} *){src};")
+        if not op.is_borrowed:
+            self.emit_inc_ref(dest, op.type)
 
     def visit_set_mem(self, op: SetMem) -> None:
         dest = self.reg(op.dest)
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 4829dd6a903d..62ac9b8d48e4 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -1579,14 +1579,13 @@ class LoadMem(RegisterOp):
 
     error_kind = ERR_NEVER
 
-    def __init__(self, type: RType, src: Value, line: int = -1) -> None:
+    def __init__(self, type: RType, src: Value, line: int = -1, *, borrow: bool = False) -> None:
         super().__init__(line)
         self.type = type
-        # TODO: for now we enforce that the src memory address should be Py_ssize_t
-        #       later we should also support same width unsigned int
+        # TODO: Support other native integer types
         assert is_pointer_rprimitive(src.type)
         self.src = src
-        self.is_borrowed = True
+        self.is_borrowed = borrow and type.is_refcounted
 
     def sources(self) -> list[Value]:
         return [self.src]
diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py
index 2a239a0b4d9d..b0de041e1eae 100644
--- a/mypyc/ir/pprint.py
+++ b/mypyc/ir/pprint.py
@@ -267,7 +267,9 @@ def visit_float_comparison_op(self, op: FloatComparisonOp) -> str:
         return self.format("%r = %r %s %r", op, op.lhs, op.op_str[op.op], op.rhs)
 
     def visit_load_mem(self, op: LoadMem) -> str:
-        return self.format("%r = load_mem %r :: %t*", op, op.src, op.type)
+        return self.format(
+            "%r = %sload_mem %r :: %t*", op, self.borrow_prefix(op), op.src, op.type
+        )
 
     def visit_set_mem(self, op: SetMem) -> str:
         return self.format("set_mem %r, %r :: %t*", op.dest, op.src, op.dest_type)
diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index a7ed97ac8eab..5cf89f579ec4 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -733,7 +733,7 @@ def gen_condition(self) -> None:
 
         def except_match() -> Value:
             addr = builder.add(LoadAddress(pointer_rprimitive, stop_async_iteration_op.src, line))
-            return builder.add(LoadMem(stop_async_iteration_op.type, addr))
+            return builder.add(LoadMem(stop_async_iteration_op.type, addr, borrow=True))
 
         def try_body() -> None:
             awaitable = builder.call_c(anext_op, [builder.read(self.iter_target)], line)
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index c3ea0725cfd4..79ad4cc62822 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -286,6 +286,9 @@ def goto_and_activate(self, block: BasicBlock) -> None:
     def keep_alive(self, values: list[Value], *, steal: bool = False) -> None:
         self.add(KeepAlive(values, steal=steal))
 
+    def load_mem(self, ptr: Value, value_type: RType, *, borrow: bool = False) -> Value:
+        return self.add(LoadMem(value_type, ptr, borrow=borrow))
+
     def push_error_handler(self, handler: BasicBlock | None) -> None:
         self.error_handlers.append(handler)
 
@@ -660,7 +663,7 @@ def other() -> Value:
 
     def get_type_of_obj(self, obj: Value, line: int) -> Value:
         ob_type_address = self.add(GetElementPtr(obj, PyObject, "ob_type", line))
-        ob_type = self.add(LoadMem(object_rprimitive, ob_type_address))
+        ob_type = self.load_mem(ob_type_address, object_rprimitive, borrow=True)
         self.add(KeepAlive([obj]))
         return ob_type
 
@@ -2261,7 +2264,7 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val
             size_value = self.primitive_op(var_object_size, [val], line)
         elif is_set_rprimitive(typ) or is_frozenset_rprimitive(typ):
             elem_address = self.add(GetElementPtr(val, PySetObject, "used"))
-            size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address))
+            size_value = self.load_mem(elem_address, c_pyssize_t_rprimitive)
             self.add(KeepAlive([val]))
         elif is_dict_rprimitive(typ):
             size_value = self.call_c(dict_ssize_t_size_op, [val], line)
diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py
index 63a1ecca8d11..631008db5db6 100644
--- a/mypyc/lower/list_ops.py
+++ b/mypyc/lower/list_ops.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from mypyc.common import PLATFORM_SIZE
-from mypyc.ir.ops import GetElementPtr, IncRef, Integer, IntOp, LoadMem, SetMem, Value
+from mypyc.ir.ops import GetElementPtr, Integer, IntOp, SetMem, Value
 from mypyc.ir.rtypes import (
     PyListObject,
     c_pyssize_t_rprimitive,
@@ -42,7 +42,7 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V
 @lower_primitive_op("list_items")
 def list_items(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value:
     ob_item_ptr = builder.add(GetElementPtr(args[0], PyListObject, "ob_item", line))
-    return builder.add(LoadMem(pointer_rprimitive, ob_item_ptr, line))
+    return builder.load_mem(ob_item_ptr, pointer_rprimitive)
 
 
 def list_item_ptr(builder: LowLevelIRBuilder, obj: Value, index: Value, line: int) -> Value:
@@ -68,6 +68,4 @@ def list_item_ptr(builder: LowLevelIRBuilder, obj: Value, index: Value, line: in
 def list_get_item_unsafe(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value:
     index = builder.coerce(args[1], c_pyssize_t_rprimitive, line)
     item_ptr = list_item_ptr(builder, args[0], index, line)
-    value = builder.add(LoadMem(object_rprimitive, item_ptr, line))
-    builder.add(IncRef(value))
-    return value
+    return builder.load_mem(item_ptr, object_rprimitive)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 1543568fccad..1a2c237cc3c9 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -363,7 +363,7 @@ def f(x):
 L0:
     r0 = __main__.B :: type
     r1 = get_element_ptr x ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive x
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -402,7 +402,7 @@ def f(x):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr x ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive x
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -412,7 +412,7 @@ L1:
 L2:
     r5 = __main__.B :: type
     r6 = get_element_ptr x ob_type :: PyObject
-    r7 = load_mem r6 :: builtins.object*
+    r7 = borrow load_mem r6 :: builtins.object*
     keep_alive x
     r8 = r7 == r5
     r4 = r8
@@ -449,7 +449,7 @@ def f(x):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr x ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive x
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -459,7 +459,7 @@ L1:
 L2:
     r5 = __main__.R :: type
     r6 = get_element_ptr x ob_type :: PyObject
-    r7 = load_mem r6 :: builtins.object*
+    r7 = borrow load_mem r6 :: builtins.object*
     keep_alive x
     r8 = r7 == r5
     r4 = r8
@@ -500,7 +500,7 @@ def f(x):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr x ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive x
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -510,7 +510,7 @@ L1:
 L2:
     r5 = __main__.C :: type
     r6 = get_element_ptr x ob_type :: PyObject
-    r7 = load_mem r6 :: builtins.object*
+    r7 = borrow load_mem r6 :: builtins.object*
     keep_alive x
     r8 = r7 == r5
     r4 = r8
diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test
index 30adfe61e384..0df9448b819f 100644
--- a/mypyc/test-data/irbuild-isinstance.test
+++ b/mypyc/test-data/irbuild-isinstance.test
@@ -77,7 +77,7 @@ L0:
     x = r0
     r1 = __main__.C :: type
     r2 = get_element_ptr x ob_type :: PyObject
-    r3 = load_mem r2 :: builtins.object*
+    r3 = borrow load_mem r2 :: builtins.object*
     keep_alive x
     r4 = r3 == r1
     if r4 goto L1 else goto L2 :: bool
diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test
index b81465d362ba..fbf7cb148b08 100644
--- a/mypyc/test-data/irbuild-optional.test
+++ b/mypyc/test-data/irbuild-optional.test
@@ -311,7 +311,7 @@ def get(o):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr o ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive o
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -390,7 +390,7 @@ def g(o):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr o ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive o
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -403,7 +403,7 @@ L1:
 L2:
     r8 = __main__.B :: type
     r9 = get_element_ptr o ob_type :: PyObject
-    r10 = load_mem r9 :: builtins.object*
+    r10 = borrow load_mem r9 :: builtins.object*
     keep_alive o
     r11 = r10 == r8
     if r11 goto L3 else goto L4 :: bool
@@ -456,7 +456,7 @@ def f(o):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr o ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive o
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
@@ -488,7 +488,7 @@ def g(o):
 L0:
     r0 = __main__.A :: type
     r1 = get_element_ptr o ob_type :: PyObject
-    r2 = load_mem r1 :: builtins.object*
+    r2 = borrow load_mem r1 :: builtins.object*
     keep_alive o
     r3 = r2 == r0
     if r3 goto L1 else goto L2 :: bool
diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test
index c95e832cc5df..981208cb52ee 100644
--- a/mypyc/test-data/irbuild-singledispatch.test
+++ b/mypyc/test-data/irbuild-singledispatch.test
@@ -57,7 +57,7 @@ def f_obj.__call__(__mypyc_self__, arg):
     r27 :: bool
 L0:
     r0 = get_element_ptr arg ob_type :: PyObject
-    r1 = load_mem r0 :: builtins.object*
+    r1 = borrow load_mem r0 :: builtins.object*
     keep_alive arg
     r2 = __mypyc_self__.dispatch_cache
     r3 = CPyDict_GetWithNone(r2, r1)
@@ -82,7 +82,7 @@ L2:
 L3:
     r16 = load_address PyLong_Type
     r17 = get_element_ptr r6 ob_type :: PyObject
-    r18 = load_mem r17 :: builtins.object*
+    r18 = borrow load_mem r17 :: builtins.object*
     keep_alive r6
     r19 = r18 == r16
     if r19 goto L4 else goto L7 :: bool
@@ -195,7 +195,7 @@ def f_obj.__call__(__mypyc_self__, x):
     r24 :: None
 L0:
     r0 = get_element_ptr x ob_type :: PyObject
-    r1 = load_mem r0 :: builtins.object*
+    r1 = borrow load_mem r0 :: builtins.object*
     keep_alive x
     r2 = __mypyc_self__.dispatch_cache
     r3 = CPyDict_GetWithNone(r2, r1)
@@ -220,7 +220,7 @@ L2:
 L3:
     r16 = load_address PyLong_Type
     r17 = get_element_ptr r6 ob_type :: PyObject
-    r18 = load_mem r17 :: builtins.object*
+    r18 = borrow load_mem r17 :: builtins.object*
     keep_alive r6
     r19 = r18 == r16
     if r19 goto L4 else goto L5 :: bool
diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test
index b4fe14db59c4..c2bcba54e444 100644
--- a/mypyc/test-data/lowering-int.test
+++ b/mypyc/test-data/lowering-int.test
@@ -365,7 +365,6 @@ L2:
     r6 = r0 * 8
     r7 = r5 + r6
     r8 = load_mem r7 :: builtins.object*
-    inc_ref r8
     r9 = unbox(int, r8)
     dec_ref r8
     if is_error(r9) goto L6 (error at f:4) else goto L3
diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test
index a831d9baf86e..a71c53041cf7 100644
--- a/mypyc/test-data/refcount.test
+++ b/mypyc/test-data/refcount.test
@@ -1117,7 +1117,7 @@ L0:
     r0 = borrow x.a
     r1 = __main__.D :: type
     r2 = get_element_ptr r0 ob_type :: PyObject
-    r3 = load_mem r2 :: builtins.object*
+    r3 = borrow load_mem r2 :: builtins.object*
     r4 = r3 == r1
     if r4 goto L1 else goto L2 :: bool
 L1:

From 640375293f140f573bda1c7dfc5e1bde851198fd Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 16 Jul 2025 03:10:02 +0200
Subject: [PATCH 1464/1617] Prevent a crash when InitVar is redefined with a
 method in a subclass (#19453)

Fixes #19443. This case is too niche (and should be trivially
avoidable), so just not crashing should be good enough. The value is
indeed redefined, and trying to massage the plugin to move the
`X-redefinition` back to `X` in names is not worth the effort IMO.
---
 mypy/checker.py                       |  9 ++++++++-
 test-data/unit/check-dataclasses.test | 16 ++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 159569849061..7579c36a97d0 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2188,7 +2188,14 @@ def check_method_override_for_base_with_name(
         else:
             override_class_or_static = defn.func.is_class or defn.func.is_static
         typ, _ = self.node_type_from_base(defn.name, defn.info, defn)
-        assert typ is not None
+        if typ is None:
+            # This may only happen if we're checking `x-redefinition` member
+            # and `x` itself is for some reason gone. Normally the node should
+            # be reachable from the containing class by its name.
+            # The redefinition is never removed, use this as a sanity check to verify
+            # the reasoning above.
+            assert f"{defn.name}-redefinition" in defn.info.names
+            return False
 
         original_node = base_attr.node
         # `original_type` can be partial if (e.g.) it is originally an
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index 2ead202bd6af..a6ac30e20c36 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -2666,3 +2666,19 @@ class PersonBad(TypedDict):
 class JobBad:
     person: PersonBad = field(default_factory=PersonBad)  # E: Argument "default_factory" to "field" has incompatible type "type[PersonBad]"; expected "Callable[[], PersonBad]"
 [builtins fixtures/dict.pyi]
+
+[case testDataclassInitVarRedefinitionNoCrash]
+# https://github.com/python/mypy/issues/19443
+from dataclasses import InitVar, dataclass
+
+class ClassA:
+    def value(self) -> int:
+        return 0
+
+@dataclass
+class ClassB(ClassA):
+    value: InitVar[int]
+
+    def value(self) -> int:  # E: Name "value" already defined on line 10
+        return 0
+[builtins fixtures/dict.pyi]

From b546953a9beb145b582f691308ed1878e3599ef5 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 16 Jul 2025 10:50:29 +0200
Subject: [PATCH 1465/1617] Sync typeshed (#19446)

Source commit:

https://github.com/python/typeshed/commit/84e41f2853d7af3d651d620f093031cba849bd1d
---
 mypy/typeshed/stdlib/_interpreters.pyi        |  21 ++-
 mypy/typeshed/stdlib/_json.pyi                |   4 +-
 mypy/typeshed/stdlib/_markupbase.pyi          |   4 +-
 mypy/typeshed/stdlib/ast.pyi                  |  38 +++---
 mypy/typeshed/stdlib/asyncio/__init__.pyi     |   2 +-
 mypy/typeshed/stdlib/builtins.pyi             |   2 +-
 mypy/typeshed/stdlib/collections/__init__.pyi |   4 +
 .../compression/{bz2/__init__.pyi => bz2.pyi} |   0
 .../{gzip/__init__.pyi => gzip.pyi}           |   0
 .../{lzma/__init__.pyi => lzma.pyi}           |   0
 .../{zlib/__init__.pyi => zlib.pyi}           |   0
 .../stdlib/compression/zstd/_zstdfile.pyi     |   6 +-
 mypy/typeshed/stdlib/ctypes/__init__.pyi      |   2 +-
 mypy/typeshed/stdlib/html/parser.pyi          |   2 +-
 .../stdlib/importlib/metadata/__init__.pyi    |   2 +
 mypy/typeshed/stdlib/string/templatelib.pyi   |   2 +-
 mypy/typeshed/stdlib/sys/__init__.pyi         |   4 +
 mypy/typeshed/stdlib/tarfile.pyi              | 121 +++++++++++++++++-
 mypy/typeshed/stdlib/types.pyi                |   4 +-
 mypy/typeshed/stdlib/typing.pyi               |   5 +-
 mypy/typeshed/stdlib/urllib/error.pyi         |   5 +
 mypy/typeshed/stdlib/weakref.pyi              |   4 +
 test-data/unit/pythoneval.test                |   3 +-
 23 files changed, 193 insertions(+), 42 deletions(-)
 rename mypy/typeshed/stdlib/compression/{bz2/__init__.pyi => bz2.pyi} (100%)
 rename mypy/typeshed/stdlib/compression/{gzip/__init__.pyi => gzip.pyi} (100%)
 rename mypy/typeshed/stdlib/compression/{lzma/__init__.pyi => lzma.pyi} (100%)
 rename mypy/typeshed/stdlib/compression/{zlib/__init__.pyi => zlib.pyi} (100%)

diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi
index caa1115e9d3d..ad8eccbe3328 100644
--- a/mypy/typeshed/stdlib/_interpreters.pyi
+++ b/mypy/typeshed/stdlib/_interpreters.pyi
@@ -1,9 +1,10 @@
 import types
-from collections.abc import Callable, Mapping
-from typing import Final, Literal, SupportsIndex
+from collections.abc import Callable
+from typing import Any, Final, Literal, SupportsIndex
 from typing_extensions import TypeAlias
 
 _Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""]
+_SharedDict: TypeAlias = dict[str, Any]  # many objects can be shared
 
 class InterpreterError(Exception): ...
 class InterpreterNotFoundError(InterpreterError): ...
@@ -22,7 +23,11 @@ def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ...
 def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ...
 def whence(id: SupportsIndex) -> int: ...
 def exec(
-    id: SupportsIndex, code: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False
+    id: SupportsIndex,
+    code: str | types.CodeType | Callable[[], object],
+    shared: _SharedDict | None = None,
+    *,
+    restrict: bool = False,
 ) -> None | types.SimpleNamespace: ...
 def call(
     id: SupportsIndex,
@@ -33,12 +38,16 @@ def call(
     restrict: bool = False,
 ) -> object: ...
 def run_string(
-    id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False
+    id: SupportsIndex,
+    script: str | types.CodeType | Callable[[], object],
+    shared: _SharedDict | None = None,
+    *,
+    restrict: bool = False,
 ) -> None: ...
 def run_func(
-    id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False
+    id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: _SharedDict | None = None, *, restrict: bool = False
 ) -> None: ...
-def set___main___attrs(id: SupportsIndex, updates: Mapping[str, object], *, restrict: bool = False) -> None: ...
+def set___main___attrs(id: SupportsIndex, updates: _SharedDict, *, restrict: bool = False) -> None: ...
 def incref(id: SupportsIndex, *, implieslink: bool = False, restrict: bool = False) -> None: ...
 def decref(id: SupportsIndex, *, restrict: bool = False) -> None: ...
 def is_shareable(obj: object) -> bool: ...
diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi
index 5296b8e62a02..cc59146ed982 100644
--- a/mypy/typeshed/stdlib/_json.pyi
+++ b/mypy/typeshed/stdlib/_json.pyi
@@ -11,7 +11,7 @@ class make_encoder:
     @property
     def key_separator(self) -> str: ...
     @property
-    def indent(self) -> int | None: ...
+    def indent(self) -> str | None: ...
     @property
     def markers(self) -> dict[int, Any] | None: ...
     @property
@@ -25,7 +25,7 @@ class make_encoder:
         markers: dict[int, Any] | None,
         default: Callable[[Any], Any],
         encoder: Callable[[str], str],
-        indent: int | None,
+        indent: str | None,
         key_separator: str,
         item_separator: str,
         sort_keys: bool,
diff --git a/mypy/typeshed/stdlib/_markupbase.pyi b/mypy/typeshed/stdlib/_markupbase.pyi
index 62bad25e5ccc..597bd09b700b 100644
--- a/mypy/typeshed/stdlib/_markupbase.pyi
+++ b/mypy/typeshed/stdlib/_markupbase.pyi
@@ -5,9 +5,9 @@ class ParserBase:
     def reset(self) -> None: ...
     def getpos(self) -> tuple[int, int]: ...
     def unknown_decl(self, data: str) -> None: ...
-    def parse_comment(self, i: int, report: int = 1) -> int: ...  # undocumented
+    def parse_comment(self, i: int, report: bool = True) -> int: ...  # undocumented
     def parse_declaration(self, i: int) -> int: ...  # undocumented
-    def parse_marked_section(self, i: int, report: int = 1) -> int: ...  # undocumented
+    def parse_marked_section(self, i: int, report: bool = True) -> int: ...  # undocumented
     def updatepos(self, i: int, j: int) -> int: ...  # undocumented
     if sys.version_info < (3, 10):
         # Removed from ParserBase: https://bugs.python.org/issue31844
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index 613940f5da6a..fcd6e8b01e74 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -1096,8 +1096,16 @@ class Constant(expr):
     kind: str | None
     if sys.version_info < (3, 14):
         # Aliases for value, for backwards compatibility
-        s: _ConstantValue
-        n: _ConstantValue
+        @deprecated("Will be removed in Python 3.14; use value instead")
+        @property
+        def n(self) -> _ConstantValue: ...
+        @n.setter
+        def n(self, value: _ConstantValue) -> None: ...
+        @deprecated("Will be removed in Python 3.14; use value instead")
+        @property
+        def s(self) -> _ConstantValue: ...
+        @s.setter
+        def s(self, value: _ConstantValue) -> None: ...
 
     def __init__(self, value: _ConstantValue, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
 
@@ -1495,11 +1503,11 @@ if sys.version_info >= (3, 10):
 
     class MatchSingleton(pattern):
         __match_args__ = ("value",)
-        value: Literal[True, False] | None
-        def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
+        value: bool | None
+        def __init__(self, value: bool | None, **kwargs: Unpack[_Attributes[int]]) -> None: ...
 
         if sys.version_info >= (3, 14):
-            def __replace__(self, *, value: Literal[True, False] | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ...
+            def __replace__(self, *, value: bool | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ...
 
     class MatchSequence(pattern):
         __match_args__ = ("patterns",)
@@ -1696,25 +1704,23 @@ class _ABC(type):
 if sys.version_info < (3, 14):
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
     class Num(Constant, metaclass=_ABC):
-        value: int | float | complex
+        def __new__(cls, n: complex, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
     class Str(Constant, metaclass=_ABC):
-        value: str
-        # Aliases for value, for backwards compatibility
-        s: str
+        def __new__(cls, s: str, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
     class Bytes(Constant, metaclass=_ABC):
-        value: bytes
-        # Aliases for value, for backwards compatibility
-        s: bytes
+        def __new__(cls, s: bytes, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
-    class NameConstant(Constant, metaclass=_ABC): ...
+    class NameConstant(Constant, metaclass=_ABC):
+        def __new__(cls, value: _ConstantValue, kind: str | None, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
-    class Ellipsis(Constant, metaclass=_ABC): ...
+    class Ellipsis(Constant, metaclass=_ABC):
+        def __new__(cls, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
 # everything below here is defined in ast.py
 
@@ -1797,7 +1803,7 @@ if sys.version_info >= (3, 13):
         type_comments: bool = False,
         feature_version: None | int | tuple[int, int] = None,
         optimize: Literal[-1, 0, 1, 2] = -1,
-    ) -> AST: ...
+    ) -> mod: ...
 
 else:
     @overload
@@ -1868,7 +1874,7 @@ else:
         *,
         type_comments: bool = False,
         feature_version: None | int | tuple[int, int] = None,
-    ) -> AST: ...
+    ) -> mod: ...
 
 def literal_eval(node_or_string: str | AST) -> Any: ...
 
diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi
index 68e44a88face..58739816a67e 100644
--- a/mypy/typeshed/stdlib/asyncio/__init__.pyi
+++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi
@@ -1,4 +1,4 @@
-# ruff: noqa: PLR5501 # This condition is so big, it's clearer to keep to platform condition in two blocks
+# This condition is so big, it's clearer to keep to platform condition in two blocks
 # Can't NOQA on a specific line: https://github.com/plinss/flake8-noqa/issues/22
 import sys
 from collections.abc import Awaitable, Coroutine, Generator
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index 6e983ef9ef29..b853330b18fb 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -609,7 +609,7 @@ class bytes(Sequence[int]):
     def strip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ...
     def swapcase(self) -> bytes: ...
     def title(self) -> bytes: ...
-    def translate(self, table: ReadableBuffer | None, /, delete: bytes = b"") -> bytes: ...
+    def translate(self, table: ReadableBuffer | None, /, delete: ReadableBuffer = b"") -> bytes: ...
     def upper(self) -> bytes: ...
     def zfill(self, width: SupportsIndex, /) -> bytes: ...
     @classmethod
diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi
index b9e4f84ec0b6..bc33d91caa1d 100644
--- a/mypy/typeshed/stdlib/collections/__init__.pyi
+++ b/mypy/typeshed/stdlib/collections/__init__.pyi
@@ -108,6 +108,8 @@ class UserDict(MutableMapping[_KT, _VT]):
         @overload
         def get(self, key: _KT, default: None = None) -> _VT | None: ...
         @overload
+        def get(self, key: _KT, default: _VT) -> _VT: ...
+        @overload
         def get(self, key: _KT, default: _T) -> _VT | _T: ...
 
 class UserList(MutableSequence[_T]):
@@ -452,6 +454,8 @@ class ChainMap(MutableMapping[_KT, _VT]):
     @overload
     def get(self, key: _KT, default: None = None) -> _VT | None: ...
     @overload
+    def get(self, key: _KT, default: _VT) -> _VT: ...
+    @overload
     def get(self, key: _KT, default: _T) -> _VT | _T: ...
     def __missing__(self, key: _KT) -> _VT: ...  # undocumented
     def __bool__(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/compression/bz2/__init__.pyi b/mypy/typeshed/stdlib/compression/bz2.pyi
similarity index 100%
rename from mypy/typeshed/stdlib/compression/bz2/__init__.pyi
rename to mypy/typeshed/stdlib/compression/bz2.pyi
diff --git a/mypy/typeshed/stdlib/compression/gzip/__init__.pyi b/mypy/typeshed/stdlib/compression/gzip.pyi
similarity index 100%
rename from mypy/typeshed/stdlib/compression/gzip/__init__.pyi
rename to mypy/typeshed/stdlib/compression/gzip.pyi
diff --git a/mypy/typeshed/stdlib/compression/lzma/__init__.pyi b/mypy/typeshed/stdlib/compression/lzma.pyi
similarity index 100%
rename from mypy/typeshed/stdlib/compression/lzma/__init__.pyi
rename to mypy/typeshed/stdlib/compression/lzma.pyi
diff --git a/mypy/typeshed/stdlib/compression/zlib/__init__.pyi b/mypy/typeshed/stdlib/compression/zlib.pyi
similarity index 100%
rename from mypy/typeshed/stdlib/compression/zlib/__init__.pyi
rename to mypy/typeshed/stdlib/compression/zlib.pyi
diff --git a/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi
index 045b2d35acfe..e67b3d992f2f 100644
--- a/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi
+++ b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi
@@ -3,7 +3,7 @@ from collections.abc import Mapping
 from compression._common import _streams
 from compression.zstd import ZstdDict
 from io import TextIOWrapper, _WrappedBuffer
-from typing import Literal, overload, type_check_only
+from typing import Literal, Protocol, overload, type_check_only
 from typing_extensions import TypeAlias
 
 from _zstd import ZstdCompressor, _ZstdCompressorFlushBlock, _ZstdCompressorFlushFrame
@@ -16,11 +16,11 @@ _ReadTextMode: TypeAlias = Literal["rt"]
 _WriteTextMode: TypeAlias = Literal["wt", "xt", "at"]
 
 @type_check_only
-class _FileBinaryRead(_streams._Reader):
+class _FileBinaryRead(_streams._Reader, Protocol):
     def close(self) -> None: ...
 
 @type_check_only
-class _FileBinaryWrite(SupportsWrite[bytes]):
+class _FileBinaryWrite(SupportsWrite[bytes], Protocol):
     def close(self) -> None: ...
 
 class ZstdFile(_streams.BaseStream):
diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi
index 0b14bd856784..52288d011e98 100644
--- a/mypy/typeshed/stdlib/ctypes/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi
@@ -32,7 +32,7 @@ if sys.platform == "win32":
     from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error
 
     if sys.version_info >= (3, 14):
-        from _ctypes import COMError as COMError
+        from _ctypes import COMError as COMError, CopyComPointer as CopyComPointer
 
 if sys.version_info >= (3, 11):
     from ctypes._endian import BigEndianUnion as BigEndianUnion, LittleEndianUnion as LittleEndianUnion
diff --git a/mypy/typeshed/stdlib/html/parser.pyi b/mypy/typeshed/stdlib/html/parser.pyi
index d322ade965d9..5d38c9c0d800 100644
--- a/mypy/typeshed/stdlib/html/parser.pyi
+++ b/mypy/typeshed/stdlib/html/parser.pyi
@@ -21,7 +21,7 @@ class HTMLParser(ParserBase):
     def check_for_whole_start_tag(self, i: int) -> int: ...  # undocumented
     def clear_cdata_mode(self) -> None: ...  # undocumented
     def goahead(self, end: bool) -> None: ...  # undocumented
-    def parse_bogus_comment(self, i: int, report: bool = ...) -> int: ...  # undocumented
+    def parse_bogus_comment(self, i: int, report: bool = True) -> int: ...  # undocumented
     def parse_endtag(self, i: int) -> int: ...  # undocumented
     def parse_html_declaration(self, i: int) -> int: ...  # undocumented
     def parse_pi(self, i: int) -> int: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
index 15d8b50b09d2..789878382ceb 100644
--- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
@@ -140,6 +140,8 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12):
         @overload
         def get(self, name: _KT, default: None = None) -> _VT | None: ...
         @overload
+        def get(self, name: _KT, default: _VT) -> _VT: ...
+        @overload
         def get(self, name: _KT, default: _T) -> _VT | _T: ...
         def __iter__(self) -> Iterator[_KT]: ...
         def __contains__(self, *args: object) -> bool: ...
diff --git a/mypy/typeshed/stdlib/string/templatelib.pyi b/mypy/typeshed/stdlib/string/templatelib.pyi
index 324447f5f34c..3f460006a796 100644
--- a/mypy/typeshed/stdlib/string/templatelib.pyi
+++ b/mypy/typeshed/stdlib/string/templatelib.pyi
@@ -26,6 +26,6 @@ class Interpolation:
     __match_args__ = ("value", "expression", "conversion", "format_spec")
 
     def __new__(
-        cls, value: Any, expression: str, conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
+        cls, value: Any, expression: str = "", conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
     ) -> Interpolation: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index 054fe91b17c6..0ca30396a878 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -345,6 +345,10 @@ else:
 
 def _current_frames() -> dict[int, FrameType]: ...
 def _getframe(depth: int = 0, /) -> FrameType: ...
+
+if sys.version_info >= (3, 12):
+    def _getframemodulename(depth: int = 0) -> str | None: ...
+
 def _debugmallocstats() -> None: ...
 def __displayhook__(object: object, /) -> None: ...
 def __excepthook__(exctype: type[BaseException], value: BaseException, traceback: TracebackType | None, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi
index a18ef0b823f9..dba250f2d353 100644
--- a/mypy/typeshed/stdlib/tarfile.pyi
+++ b/mypy/typeshed/stdlib/tarfile.pyi
@@ -9,6 +9,9 @@ from types import TracebackType
 from typing import IO, ClassVar, Literal, Protocol, overload
 from typing_extensions import Self, TypeAlias, deprecated
 
+if sys.version_info >= (3, 14):
+    from compression.zstd import ZstdDict
+
 __all__ = [
     "TarFile",
     "TarInfo",
@@ -186,6 +189,30 @@ class TarFile:
         debug: int | None = ...,
         errorlevel: int | None = ...,
     ) -> Self: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        @classmethod
+        def open(
+            cls,
+            name: StrOrBytesPath | None,
+            mode: Literal["r:zst"],
+            fileobj: _Fileobj | None = None,
+            bufsize: int = 10240,
+            *,
+            format: int | None = ...,
+            tarinfo: type[TarInfo] | None = ...,
+            dereference: bool | None = ...,
+            ignore_zeros: bool | None = ...,
+            encoding: str | None = ...,
+            errors: str = ...,
+            pax_headers: Mapping[str, str] | None = ...,
+            debug: int | None = ...,
+            errorlevel: int | None = ...,
+            level: None = None,
+            options: Mapping[int, int] | None = None,
+            zstd_dict: ZstdDict | None = None,
+        ) -> Self: ...
+
     @overload
     @classmethod
     def open(
@@ -304,12 +331,56 @@ class TarFile:
         errorlevel: int | None = ...,
         preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ...,
     ) -> Self: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        @classmethod
+        def open(
+            cls,
+            name: StrOrBytesPath | None,
+            mode: Literal["x:zst", "w:zst"],
+            fileobj: _Fileobj | None = None,
+            bufsize: int = 10240,
+            *,
+            format: int | None = ...,
+            tarinfo: type[TarInfo] | None = ...,
+            dereference: bool | None = ...,
+            ignore_zeros: bool | None = ...,
+            encoding: str | None = ...,
+            errors: str = ...,
+            pax_headers: Mapping[str, str] | None = ...,
+            debug: int | None = ...,
+            errorlevel: int | None = ...,
+            options: Mapping[int, int] | None = None,
+            zstd_dict: ZstdDict | None = None,
+        ) -> Self: ...
+        @overload
+        @classmethod
+        def open(
+            cls,
+            name: StrOrBytesPath | None = None,
+            *,
+            mode: Literal["x:zst", "w:zst"],
+            fileobj: _Fileobj | None = None,
+            bufsize: int = 10240,
+            format: int | None = ...,
+            tarinfo: type[TarInfo] | None = ...,
+            dereference: bool | None = ...,
+            ignore_zeros: bool | None = ...,
+            encoding: str | None = ...,
+            errors: str = ...,
+            pax_headers: Mapping[str, str] | None = ...,
+            debug: int | None = ...,
+            errorlevel: int | None = ...,
+            options: Mapping[int, int] | None = None,
+            zstd_dict: ZstdDict | None = None,
+        ) -> Self: ...
+
     @overload
     @classmethod
     def open(
         cls,
         name: StrOrBytesPath | ReadableBuffer | None,
-        mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"],
+        mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz", "r|zst"],
         fileobj: _Fileobj | None = None,
         bufsize: int = 10240,
         *,
@@ -329,7 +400,7 @@ class TarFile:
         cls,
         name: StrOrBytesPath | ReadableBuffer | None = None,
         *,
-        mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz"],
+        mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz", "r|zst"],
         fileobj: _Fileobj | None = None,
         bufsize: int = 10240,
         format: int | None = ...,
@@ -347,7 +418,7 @@ class TarFile:
     def open(
         cls,
         name: StrOrBytesPath | WriteableBuffer | None,
-        mode: Literal["w|", "w|xz"],
+        mode: Literal["w|", "w|xz", "w|zst"],
         fileobj: _Fileobj | None = None,
         bufsize: int = 10240,
         *,
@@ -367,7 +438,7 @@ class TarFile:
         cls,
         name: StrOrBytesPath | WriteableBuffer | None = None,
         *,
-        mode: Literal["w|", "w|xz"],
+        mode: Literal["w|", "w|xz", "w|zst"],
         fileobj: _Fileobj | None = None,
         bufsize: int = 10240,
         format: int | None = ...,
@@ -526,6 +597,48 @@ class TarFile:
         debug: int | None = ...,
         errorlevel: int | None = ...,
     ) -> Self: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        @classmethod
+        def zstopen(
+            cls,
+            name: StrOrBytesPath | None,
+            mode: Literal["r"] = "r",
+            fileobj: IO[bytes] | None = None,
+            level: None = None,
+            options: Mapping[int, int] | None = None,
+            zstd_dict: ZstdDict | None = None,
+            *,
+            format: int | None = ...,
+            tarinfo: type[TarInfo] | None = ...,
+            dereference: bool | None = ...,
+            ignore_zeros: bool | None = ...,
+            encoding: str | None = ...,
+            pax_headers: Mapping[str, str] | None = ...,
+            debug: int | None = ...,
+            errorlevel: int | None = ...,
+        ) -> Self: ...
+        @overload
+        @classmethod
+        def zstopen(
+            cls,
+            name: StrOrBytesPath | None,
+            mode: Literal["w", "x"],
+            fileobj: IO[bytes] | None = None,
+            level: int | None = None,
+            options: Mapping[int, int] | None = None,
+            zstd_dict: ZstdDict | None = None,
+            *,
+            format: int | None = ...,
+            tarinfo: type[TarInfo] | None = ...,
+            dereference: bool | None = ...,
+            ignore_zeros: bool | None = ...,
+            encoding: str | None = ...,
+            pax_headers: Mapping[str, str] | None = ...,
+            debug: int | None = ...,
+            errorlevel: int | None = ...,
+        ) -> Self: ...
+
     def getmember(self, name: str) -> TarInfo: ...
     def getmembers(self) -> _list[TarInfo]: ...
     def getnames(self) -> _list[str]: ...
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index 582cb653422f..44bd3eeb3f53 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -323,7 +323,9 @@ class MappingProxyType(Mapping[_KT, _VT_co]):
     @overload
     def get(self, key: _KT, /) -> _VT_co | None: ...
     @overload
-    def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ...
+    def get(self, key: _KT, default: _VT_co, /) -> _VT_co: ...  # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter
+    @overload
+    def get(self, key: _KT, default: _T2, /) -> _VT_co | _T2: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
     def __reversed__(self) -> Iterator[_KT]: ...
     def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index 79ab9eee924f..d296c8d92149 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -1,5 +1,4 @@
 # Since this module defines "overload" it is not recognized by Ruff as typing.overload
-# ruff: noqa: F811
 # TODO: The collections import is required, otherwise mypy crashes.
 # https://github.com/python/mypy/issues/16744
 import collections  # noqa: F401  # pyright: ignore[reportUnusedImport]
@@ -746,7 +745,9 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]):
     @overload
     def get(self, key: _KT, /) -> _VT_co | None: ...
     @overload
-    def get(self, key: _KT, /, default: _VT_co | _T) -> _VT_co | _T: ...
+    def get(self, key: _KT, /, default: _VT_co) -> _VT_co: ...  # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter
+    @overload
+    def get(self, key: _KT, /, default: _T) -> _VT_co | _T: ...
     def items(self) -> ItemsView[_KT, _VT_co]: ...
     def keys(self) -> KeysView[_KT]: ...
     def values(self) -> ValuesView[_VT_co]: ...
diff --git a/mypy/typeshed/stdlib/urllib/error.pyi b/mypy/typeshed/stdlib/urllib/error.pyi
index 89cec9bf289c..2173d7e6efaa 100644
--- a/mypy/typeshed/stdlib/urllib/error.pyi
+++ b/mypy/typeshed/stdlib/urllib/error.pyi
@@ -6,6 +6,8 @@ __all__ = ["URLError", "HTTPError", "ContentTooShortError"]
 
 class URLError(OSError):
     reason: str | BaseException
+    # The `filename` attribute only exists if it was provided to `__init__` and wasn't `None`.
+    filename: str
     def __init__(self, reason: str | BaseException, filename: str | None = None) -> None: ...
 
 class HTTPError(URLError, addinfourl):
@@ -16,6 +18,9 @@ class HTTPError(URLError, addinfourl):
     @property
     def reason(self) -> str: ...  # type: ignore[override]
     code: int
+    msg: str
+    hdrs: Message
+    fp: IO[bytes]
     def __init__(self, url: str, code: int, msg: str, hdrs: Message, fp: IO[bytes] | None) -> None: ...
 
 class ContentTooShortError(URLError):
diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi
index 593eb4615c8f..334fab7e7468 100644
--- a/mypy/typeshed/stdlib/weakref.pyi
+++ b/mypy/typeshed/stdlib/weakref.pyi
@@ -99,6 +99,8 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]):
     @overload
     def get(self, key: _KT, default: None = None) -> _VT | None: ...
     @overload
+    def get(self, key: _KT, default: _VT) -> _VT: ...
+    @overload
     def get(self, key: _KT, default: _T) -> _VT | _T: ...
     # These are incompatible with Mapping
     def keys(self) -> Iterator[_KT]: ...  # type: ignore[override]
@@ -149,6 +151,8 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]):
     @overload
     def get(self, key: _KT, default: None = None) -> _VT | None: ...
     @overload
+    def get(self, key: _KT, default: _VT) -> _VT: ...
+    @overload
     def get(self, key: _KT, default: _T) -> _VT | _T: ...
     # These are incompatible with Mapping
     def keys(self) -> Iterator[_KT]: ...  # type: ignore[override]
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 3cd509d44290..72c00a3b9b1c 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1050,7 +1050,8 @@ _testTypedDictGet.py:8: note: Revealed type is "builtins.object"
 _testTypedDictGet.py:9: error: All overload variants of "get" of "Mapping" require at least one argument
 _testTypedDictGet.py:9: note: Possible overload variants:
 _testTypedDictGet.py:9: note:     def get(self, str, /) -> object
-_testTypedDictGet.py:9: note:     def [_T] get(self, str, /, default: object) -> object
+_testTypedDictGet.py:9: note:     def get(self, str, /, default: object) -> object
+_testTypedDictGet.py:9: note:     def [_T] get(self, str, /, default: _T) -> object
 _testTypedDictGet.py:11: note: Revealed type is "builtins.object"
 
 [case testTypedDictMappingMethods]

From eb07c060cd334ea2e6d9049c214153c5236d741e Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Wed, 16 Jul 2025 11:14:49 +0200
Subject: [PATCH 1466/1617] [mypyc] Report error when registering a nested
 function (#19450)

Fixes https://github.com/mypyc/mypyc/issues/1118

Added a check for nested `@singledispatch` functions and nested
functions registered to `@singledispatch` functions to report an error
in mypyc. Currently either of those cases causes mypyc to crash because
of the different handling of nested and top-level functions.

Changed to abort the compilation early when these errors are found so
that in the transform code we can assume that the singledispatch
functions are valid. This means that mypyc might not report as many
errors until it quits as before, so I have split the error output test
in `commandline.test` into two.
---
 mypyc/irbuild/main.py                       |  2 +
 mypyc/irbuild/prepare.py                    | 16 ++++
 mypyc/test-data/commandline.test            | 99 +++++++++++++--------
 mypyc/test-data/irbuild-singledispatch.test | 55 ++++++++++++
 4 files changed, 133 insertions(+), 39 deletions(-)

diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py
index 894e8f277723..d2c8924a7298 100644
--- a/mypyc/irbuild/main.py
+++ b/mypyc/irbuild/main.py
@@ -71,6 +71,8 @@ def build_ir(
     singledispatch_info = find_singledispatch_register_impls(modules, errors)
 
     result: ModuleIRs = {}
+    if errors.num_errors > 0:
+        return result
 
     # Generate IR for all modules.
     class_irs = []
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 4eff90f90b7d..1d6117ab7b1e 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -616,6 +616,12 @@ def __init__(self, errors: Errors) -> None:
         self.decorators_to_remove: dict[FuncDef, list[int]] = {}
 
         self.errors: Errors = errors
+        self.func_stack_depth = 0
+
+    def visit_func_def(self, o: FuncDef) -> None:
+        self.func_stack_depth += 1
+        super().visit_func_def(o)
+        self.func_stack_depth -= 1
 
     def visit_decorator(self, dec: Decorator) -> None:
         if dec.decorators:
@@ -627,6 +633,10 @@ def visit_decorator(self, dec: Decorator) -> None:
             for i, d in enumerate(decorators_to_store):
                 impl = get_singledispatch_register_call_info(d, dec.func)
                 if impl is not None:
+                    if self.func_stack_depth > 0:
+                        self.errors.error(
+                            "Registering nested functions not supported", self.current_path, d.line
+                        )
                     self.singledispatch_impls[impl.singledispatch_func].append(
                         (impl.dispatch_type, dec.func)
                     )
@@ -643,6 +653,12 @@ def visit_decorator(self, dec: Decorator) -> None:
                         )
                 else:
                     if refers_to_fullname(d, "functools.singledispatch"):
+                        if self.func_stack_depth > 0:
+                            self.errors.error(
+                                "Nested singledispatch functions not supported",
+                                self.current_path,
+                                d.line,
+                            )
                         decorators_to_remove.append(i)
                         # make sure that we still treat the function as a singledispatch function
                         # even if we don't find any registered implementations (which might happen
diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test
index 77c2e08bcf34..392ad3620790 100644
--- a/mypyc/test-data/commandline.test
+++ b/mypyc/test-data/commandline.test
@@ -101,13 +101,71 @@ assert a.f(10) == 100
 def f(x: int) -> int:
     return x*x
 
-[case testErrorOutput]
+[case testErrorOutput1]
+# cmd: test.py
+
+[file test.py]
+from functools import singledispatch
+from mypy_extensions import trait
+from typing import Any
+
+def decorator(x: Any) -> Any:
+    return x
+
+class NeverMetaclass(type):  # E: Inheriting from most builtin types is unimplemented \
+                             # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \
+                             # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes
+    pass
+
+class Concrete1:
+    pass
+
+@trait
+class Trait1:
+    pass
+
+class Concrete2:
+    pass
+
+@decorator
+class NonExt(Concrete1):  # E: Non-extension classes may not inherit from extension classes
+    pass
+
+class NopeMultipleInheritanceAndBadOrder3(Trait1, Concrete1, Concrete2):  # E: Non-trait base must appear first in parent list
+    pass
+
+class NopeBadOrder(Trait1, Concrete2):  # E: Non-trait base must appear first in parent list
+    pass
+
+class Foo:
+    pass
+
+@singledispatch
+def a(arg) -> None:
+    pass
+
+@decorator # E: Calling decorator after registering function not supported
+@a.register
+def g(arg: int) -> None:
+    pass
+
+@a.register
+@decorator
+def h(arg: str) -> None:
+    pass
+
+@decorator
+@decorator # E: Calling decorator after registering function not supported
+@a.register
+def i(arg: Foo) -> None:
+    pass
+
+[case testErrorOutput2]
 # cmd: test.py
 
 [file test.py]
 from typing import Final, List, Any, AsyncIterable
 from mypy_extensions import trait, mypyc_attr
-from functools import singledispatch
 
 def busted(b: bool) -> None:
     for i in range(1, 10, 0):  # E: range() step can't be zero
@@ -138,11 +196,6 @@ Foo.lol = 50  # E: Only class variables defined as ClassVar can be assigned to
 def decorator(x: Any) -> Any:
     return x
 
-class NeverMetaclass(type):  # E: Inheriting from most builtin types is unimplemented \
-                             # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \
-                             # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes
-    pass
-
 class Concrete1:
     pass
 
@@ -161,11 +214,6 @@ class Concrete2:
 class Trait2(Concrete2):
     pass
 
-@decorator
-class NonExt(Concrete1):  # E: Non-extension classes may not inherit from extension classes
-    pass
-
-
 class NopeMultipleInheritance(Concrete1, Concrete2):  # E: Multiple inheritance is not supported (except for traits)
     pass
 
@@ -175,13 +223,6 @@ class NopeMultipleInheritanceAndBadOrder(Concrete1, Trait1, Concrete2):  # E: Mu
 class NopeMultipleInheritanceAndBadOrder2(Concrete1, Concrete2, Trait1):  # E: Multiple inheritance is not supported (except for traits)
     pass
 
-class NopeMultipleInheritanceAndBadOrder3(Trait1, Concrete1, Concrete2):  # E: Non-trait base must appear first in parent list # E: Multiple inheritance is not supported (except for traits)
-    pass
-
-class NopeBadOrder(Trait1, Concrete2):  # E: Non-trait base must appear first in parent list
-    pass
-
-
 @decorator
 class NonExt2:
     @property  # E: Property setters not supported in non-extension classes
@@ -219,26 +260,6 @@ class AllowInterp2(PureTrait):  # E: Base class "test.PureTrait" does not allow
 async def async_generators() -> AsyncIterable[int]:
     yield 1  # E: async generators are unimplemented
 
-@singledispatch
-def a(arg) -> None:
-    pass
-
-@decorator # E: Calling decorator after registering function not supported
-@a.register
-def g(arg: int) -> None:
-    pass
-
-@a.register
-@decorator
-def h(arg: str) -> None:
-    pass
-
-@decorator
-@decorator # E: Calling decorator after registering function not supported
-@a.register
-def i(arg: Foo) -> None:
-    pass
-
 [case testOnlyWarningOutput]
 # cmd: test.py
 
diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test
index 981208cb52ee..ef11ae04dc64 100644
--- a/mypyc/test-data/irbuild-singledispatch.test
+++ b/mypyc/test-data/irbuild-singledispatch.test
@@ -274,3 +274,58 @@ L0:
     r1 = f(r0)
     r2 = box(None, 1)
     return r2
+
+[case registerNestedFunctionError]
+from functools import singledispatch
+from typing import Any, overload
+
+def dec(x: Any) -> Any:
+    return x
+
+def f() -> None:
+    @singledispatch  # E: Nested singledispatch functions not supported
+    def singledispatch_in_func(x: Any) -> None:
+        pass
+
+@dec
+def g() -> None:
+    @singledispatch  # E: Nested singledispatch functions not supported
+    def singledispatch_in_decorated(x: Any) -> None:
+        pass
+
+@overload
+def h(x: int) -> None:
+    pass
+@overload
+def h(x: str) -> None:
+    pass
+def h(x: Any) -> None:
+    @singledispatch  # E: Nested singledispatch functions not supported
+    def singledispatch_in_overload(x: Any) -> None:
+        pass
+
+@singledispatch
+def outside(x: Any) -> None:
+    pass
+
+def i() -> None:
+    @outside.register  # E: Registering nested functions not supported
+    def register_in_func(x: int) -> None:
+        pass
+
+@dec
+def j() -> None:
+    @outside.register  # E: Registering nested functions not supported
+    def register_in_decorated(x: int) -> None:
+        pass
+
+@overload
+def k(x: int) -> None:
+    pass
+@overload
+def k(x: str) -> None:
+    pass
+def k(x: Any) -> None:
+    @outside.register  # E: Registering nested functions not supported
+    def register_in_overload(x: int) -> None:
+        pass

From 02a472a0dbb7d21810ef2c56626eba2113e5a049 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 16 Jul 2025 13:43:40 +0100
Subject: [PATCH 1467/1617] [mypyc] Optionally log a sampled operation trace to
 a file (#19457)

Logging executed ops is useful for performance analysis. For example, we
can look for functions which perform many slow operations and try to
optimize them. I've already used this successfully to implement several
optimizations. A typical optimization that this helps with is replacing
a generic Python function call operation with a native call. This has
also helped me identify inefficient code generated by mypyc.

Compile using `MYPYC_LOG_TRACE=1 mypyc ...` to enable trace logging. The
log will be written to `mypyc_trace.txt`. Roughly 1/1000 of ops of
certain kinds (e.g. primitive calls) are logged.

This can also be enabled by passing `log_trace=True` to `mypycify`.

Compared to profiling, this logging data is frequency-oriented instead
of CPU time oriented, and it's mostly helpful for micro-optimizations.
It also needs some understanding of mypyc internals to be useful. It's
not generally possible to reconstruct call stacks from the event data
(but we could improve this). However, there is very little noise in the
data and even small improvements can be visible.

Logging isn't impacted by C compiler optimizations, so for a faster
iteration loop, optimizations can be disabled.

In the future this could possibly be used for profile-guided
optimizations, but we'd probably need to adjust the data collection a
bit for this use case.

This is currently not documented and mostly intended for mypy or mypyc
maintainers for now. Also no tests yet, since this is not a user-evel
feature and it's disabled by default.

Random example of log entries from mypy self check:
```
mypy.typeops.TypeVarExtractor._merge:1146:call_c:CPyList_Extend
mypy.semanal.SemanticAnalyzer.lookup::primitive_op:list_get_item_unsafe
mypy.expandtype.ExpandTypeVisitor.visit_type_var__TypeVisitor_glue:239:call:mypy.expandtype.ExpandTypeVisitor.visit_type_var
mypy.applytype.apply_generic_arguments:111:call_c:CPy_NoErrOccurred
mypy.indirection.TypeIndirectionVisitor.visit_callable_type__TypeVisitor_glue:118:call:mypy.indirection.TypeIndirectionVisitor.visit_callable_type
mypy.fastparse.ASTConverter.visit_Call::primitive_op:buf_init_item
mypy.semanal.SemanticAnalyzer.is_func_scope::primitive_op:int_eq
mypy.meet.is_overlapping_types:397:call:mypy.meet._is_subtype_is_overlapping_types_obj
mypy.types.CallableType.serialize:2287:call_c:CPyList_SetItemUnsafe
mypy.checkexpr.ExpressionChecker.check_argument_types:2576:call_c:CPyList_SetItemUnsafe
```

For example, let's look at this line:
```
mypy.typeops.TypeVarExtractor._merge:1146:call_c:CPyList_Extend
```
In method `TypeVarExtractor._merge`, on line 1146 of `mypy/typeops.py`,
the C primitive CPyList_Extend was called (corresponds to
`list.extend`).

I'll later add some documentation to the wiki or other developer docs
and give examples of using and analyzing the data.
---
 mypyc/__main__.py            | 20 +++++++--
 mypyc/build.py               | 10 +++++
 mypyc/codegen/emitmodule.py  |  4 ++
 mypyc/lib-rt/CPy.h           |  1 +
 mypyc/lib-rt/misc_ops.c      | 28 ++++++++++++
 mypyc/options.py             |  5 +++
 mypyc/primitives/misc_ops.py | 11 +++++
 mypyc/transform/log_trace.py | 83 ++++++++++++++++++++++++++++++++++++
 8 files changed, 159 insertions(+), 3 deletions(-)
 create mode 100644 mypyc/transform/log_trace.py

diff --git a/mypyc/__main__.py b/mypyc/__main__.py
index 653199e0fb55..9b3973710efa 100644
--- a/mypyc/__main__.py
+++ b/mypyc/__main__.py
@@ -23,8 +23,15 @@
 from setuptools import setup
 from mypyc.build import mypycify
 
-setup(name='mypyc_output',
-      ext_modules=mypycify({}, opt_level="{}", debug_level="{}", strict_dunder_typing={}),
+setup(
+    name='mypyc_output',
+    ext_modules=mypycify(
+        {},
+        opt_level="{}",
+        debug_level="{}",
+        strict_dunder_typing={},
+        log_trace={},
+    ),
 )
 """
 
@@ -39,10 +46,17 @@ def main() -> None:
     opt_level = os.getenv("MYPYC_OPT_LEVEL", "3")
     debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1")
     strict_dunder_typing = bool(int(os.getenv("MYPYC_STRICT_DUNDER_TYPING", "0")))
+    # If enabled, compiled code writes a sampled log of executed ops (or events) to
+    # mypyc_trace.txt.
+    log_trace = bool(int(os.getenv("MYPYC_LOG_TRACE", "0")))
 
     setup_file = os.path.join(build_dir, "setup.py")
     with open(setup_file, "w") as f:
-        f.write(setup_format.format(sys.argv[1:], opt_level, debug_level, strict_dunder_typing))
+        f.write(
+            setup_format.format(
+                sys.argv[1:], opt_level, debug_level, strict_dunder_typing, log_trace
+            )
+        )
 
     # We don't use run_setup (like we do in the test suite) because it throws
     # away the error code from distutils, and we don't care about the slight
diff --git a/mypyc/build.py b/mypyc/build.py
index ab7ba5393614..8ddbf4d22a27 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -485,6 +485,7 @@ def mypycify(
     include_runtime_files: bool | None = None,
     strict_dunder_typing: bool = False,
     group_name: str | None = None,
+    log_trace: bool = False,
 ) -> list[Extension]:
     """Main entry point to building using mypyc.
 
@@ -531,6 +532,10 @@ def mypycify(
                     the hash of module names. This is used for the names of the
                     output C files and the shared library. This is only supported
                     if there is a single group. [Experimental]
+        log_trace: If True, compiled code writes a trace log of events in
+                   mypyc_trace.txt (derived from executed operations). This is
+                   useful for performance analysis, such as analyzing which
+                   primitive ops are used the most and on which lines.
     """
 
     # Figure out our configuration
@@ -543,6 +548,7 @@ def mypycify(
         include_runtime_files=include_runtime_files,
         strict_dunder_typing=strict_dunder_typing,
         group_name=group_name,
+        log_trace=log_trace,
     )
 
     # Generate all the actual important C code
@@ -583,6 +589,8 @@ def mypycify(
             # See https://github.com/mypyc/mypyc/issues/956
             "-Wno-cpp",
         ]
+        if log_trace:
+            cflags.append("-DMYPYC_LOG_TRACE")
     elif compiler.compiler_type == "msvc":
         # msvc doesn't have levels, '/O2' is full and '/Od' is disable
         if opt_level == "0":
@@ -607,6 +615,8 @@ def mypycify(
             # that we actually get the compilation speed and memory
             # use wins that multi-file mode is intended for.
             cflags += ["/GL-", "/wd9025"]  # warning about overriding /GL
+        if log_trace:
+            cflags.append("/DMYPYC_LOG_TRACE")
 
     # If configured to (defaults to yes in multi-file mode), copy the
     # runtime library in. Otherwise it just gets #included to save on
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 2a6f17cea5e2..7037409ff40b 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -65,6 +65,7 @@
 from mypyc.transform.copy_propagation import do_copy_propagation
 from mypyc.transform.exceptions import insert_exception_handling
 from mypyc.transform.flag_elimination import do_flag_elimination
+from mypyc.transform.log_trace import insert_event_trace_logging
 from mypyc.transform.lower import lower_ir
 from mypyc.transform.refcount import insert_ref_count_opcodes
 from mypyc.transform.spill import insert_spills
@@ -253,6 +254,9 @@ def compile_scc_to_ir(
             if fn in env_user_functions:
                 insert_spills(fn, env_user_functions[fn])
 
+            if compiler_options.log_trace:
+                insert_event_trace_logging(fn, compiler_options)
+
             # Switch to lower abstraction level IR.
             lower_ir(fn, compiler_options)
             # Perform optimizations.
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 698e65155da4..e7a7f9a07626 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -929,6 +929,7 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, PyOb
 PyObject *CPy_GetAIter(PyObject *obj);
 PyObject *CPy_GetANext(PyObject *aiter);
 void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value);
+void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details);
 
 #ifdef __cplusplus
 }
diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c
index d234138b2ff7..8aa25cc11e02 100644
--- a/mypyc/lib-rt/misc_ops.c
+++ b/mypyc/lib-rt/misc_ops.c
@@ -1030,6 +1030,34 @@ PyObject *CPy_GetANext(PyObject *aiter)
     return NULL;
 }
 
+#ifdef MYPYC_LOG_TRACE
+
+// This is only compiled in if trace logging is enabled by user
+
+static int TraceCounter = 0;
+static const int TRACE_EVERY_NTH = 1009;  // Should be a prime number
+#define TRACE_LOG_FILE_NAME "mypyc_trace.txt"
+static FILE *TraceLogFile = NULL;
+
+// Log a tracing event on every Nth call
+void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details) {
+    if (TraceLogFile == NULL) {
+        if ((TraceLogFile = fopen(TRACE_LOG_FILE_NAME, "w")) == NULL) {
+            fprintf(stderr, "error: Could not open trace file %s\n", TRACE_LOG_FILE_NAME);
+            abort();
+        }
+    }
+    if (TraceCounter == 0) {
+        fprintf(TraceLogFile, "%s:%s:%s:%s\n", location, line, op, details);
+    }
+    TraceCounter++;
+    if (TraceCounter == TRACE_EVERY_NTH) {
+        TraceCounter = 0;
+    }
+}
+
+#endif
+
 #ifdef CPY_3_12_FEATURES
 
 // Copied from Python 3.12.3, since this struct is internal to CPython. It defines
diff --git a/mypyc/options.py b/mypyc/options.py
index 51114926f6b2..50c76d3c0656 100644
--- a/mypyc/options.py
+++ b/mypyc/options.py
@@ -16,6 +16,7 @@ def __init__(
         python_version: tuple[int, int] | None = None,
         strict_dunder_typing: bool = False,
         group_name: str | None = None,
+        log_trace: bool = False,
     ) -> None:
         self.strip_asserts = strip_asserts
         self.multi_file = multi_file
@@ -45,3 +46,7 @@ def __init__(
         # library is generated (with shims). This can be used to make the output
         # file names more predictable.
         self.group_name = group_name
+        # If enabled, write a trace log of events based on executed operations to
+        # mypyc_trace.txt when compiled module is executed. This is useful for
+        # performance analysis.
+        self.log_trace = log_trace
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index 114a5f0a9823..e2a1aea1a8d6 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -9,6 +9,7 @@
     c_int_rprimitive,
     c_pointer_rprimitive,
     c_pyssize_t_rprimitive,
+    cstring_rprimitive,
     dict_rprimitive,
     int_rprimitive,
     object_pointer_rprimitive,
@@ -300,3 +301,13 @@
     return_type=void_rtype,
     error_kind=ERR_NEVER,
 )
+
+# Log an event to a trace log, which is written to a file during execution.
+log_trace_event = custom_primitive_op(
+    name="log_trace_event",
+    c_function_name="CPyTrace_LogEvent",
+    # (fullname of function/location, line number, operation name, operation details)
+    arg_types=[cstring_rprimitive, cstring_rprimitive, cstring_rprimitive, cstring_rprimitive],
+    return_type=void_rtype,
+    error_kind=ERR_NEVER,
+)
diff --git a/mypyc/transform/log_trace.py b/mypyc/transform/log_trace.py
new file mode 100644
index 000000000000..5b20940c66bb
--- /dev/null
+++ b/mypyc/transform/log_trace.py
@@ -0,0 +1,83 @@
+"""This optional pass adds logging of various executed operations.
+
+Some subset of the executed operations are logged to the mypyc_trace.txt file.
+
+This is useful for performance analysis. For example, it's possible
+to identify how frequently various primitive functions are called,
+and in which code locations they are called.
+"""
+
+from __future__ import annotations
+
+from mypyc.ir.func_ir import FuncIR
+from mypyc.ir.ops import Call, CallC, CString, LoadLiteral, LoadStatic, Op, PrimitiveOp, Value
+from mypyc.irbuild.ll_builder import LowLevelIRBuilder
+from mypyc.options import CompilerOptions
+from mypyc.primitives.misc_ops import log_trace_event
+from mypyc.transform.ir_transform import IRTransform
+
+
+def insert_event_trace_logging(fn: FuncIR, options: CompilerOptions) -> None:
+    builder = LowLevelIRBuilder(None, options)
+    transform = LogTraceEventTransform(builder, fn.decl.fullname)
+    transform.transform_blocks(fn.blocks)
+    fn.blocks = builder.blocks
+
+
+def get_load_global_name(op: CallC) -> str | None:
+    name = op.function_name
+    if name == "CPyDict_GetItem":
+        arg = op.args[0]
+        if (
+            isinstance(arg, LoadStatic)
+            and arg.namespace == "static"
+            and arg.identifier == "globals"
+            and isinstance(op.args[1], LoadLiteral)
+        ):
+            return str(op.args[1].value)
+    return None
+
+
+class LogTraceEventTransform(IRTransform):
+    def __init__(self, builder: LowLevelIRBuilder, fullname: str) -> None:
+        super().__init__(builder)
+        self.fullname = fullname.encode("utf-8")
+
+    def visit_call(self, op: Call) -> Value:
+        # TODO: Use different op name when constructing an instance
+        return self.log(op, "call", op.fn.fullname)
+
+    def visit_primitive_op(self, op: PrimitiveOp) -> Value:
+        return self.log(op, "primitive_op", op.desc.name)
+
+    def visit_call_c(self, op: CallC) -> Value:
+        if global_name := get_load_global_name(op):
+            return self.log(op, "globals_dict_get_item", global_name)
+
+        func_name = op.function_name
+        if func_name == "PyObject_Vectorcall" and isinstance(op.args[0], CallC):
+            if global_name := get_load_global_name(op.args[0]):
+                return self.log(op, "python_call_global", global_name)
+        elif func_name == "CPyObject_GetAttr" and isinstance(op.args[1], LoadLiteral):
+            return self.log(op, "python_get_attr", str(op.args[1].value))
+        elif func_name == "PyObject_VectorcallMethod" and isinstance(op.args[0], LoadLiteral):
+            return self.log(op, "python_call_method", str(op.args[0].value))
+
+        return self.log(op, "call_c", func_name)
+
+    def log(self, op: Op, name: str, details: str) -> Value:
+        if op.line >= 0:
+            line_str = str(op.line)
+        else:
+            line_str = ""
+        self.builder.primitive_op(
+            log_trace_event,
+            [
+                CString(self.fullname),
+                CString(line_str.encode("ascii")),
+                CString(name.encode("utf-8")),
+                CString(details.encode("utf-8")),
+            ],
+            op.line,
+        )
+        return self.add(op)

From edd491f30aa65d1d8798d774a88bc2f6741360f3 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 16 Jul 2025 16:19:38 +0200
Subject: [PATCH 1468/1617] perf: return from `is_subtype` early (#19400)

Our equality implementation is conservative: if two types compare equal,
we know for sure they do indeed refer to the same type, and any type is
subtype of itself. This saved 0.9% in my local benchmark run.
---
 misc/perf_compare.py                              | 2 ++
 mypy/subtypes.py                                  | 4 ++++
 mypy/types.py                                     | 2 ++
 test-data/unit/check-generics.test                | 4 ++--
 test-data/unit/check-parameter-specification.test | 8 ++++----
 5 files changed, 14 insertions(+), 6 deletions(-)
 mode change 100644 => 100755 misc/perf_compare.py

diff --git a/misc/perf_compare.py b/misc/perf_compare.py
old mode 100644
new mode 100755
index 39dd22b31339..7d22f43d1c45
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -1,3 +1,5 @@
+#! /usr/bin/env python
+
 """Compare performance of mypyc-compiled mypy between one or more commits/branches.
 
 Simple usage:
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 428e6dec6749..1aa8543505ec 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -145,6 +145,8 @@ def is_subtype(
     between the type arguments (e.g., A and B), taking the variance of the
     type var into account.
     """
+    if left == right:
+        return True
     if subtype_context is None:
         subtype_context = SubtypeContext(
             ignore_type_params=ignore_type_params,
@@ -206,6 +208,8 @@ def is_proper_subtype(
     (this is useful for runtime isinstance() checks). If keep_erased_types is True,
     do not consider ErasedType a subtype of all types (used by type inference against unions).
     """
+    if left == right:
+        return True
     if subtype_context is None:
         subtype_context = SubtypeContext(
             ignore_promotions=ignore_promotions,
diff --git a/mypy/types.py b/mypy/types.py
index 05b02acc68c0..e9d299dbc8fc 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -2272,6 +2272,8 @@ def __eq__(self, other: object) -> bool:
                 and self.name == other.name
                 and self.is_type_obj() == other.is_type_obj()
                 and self.is_ellipsis_args == other.is_ellipsis_args
+                and self.type_guard == other.type_guard
+                and self.type_is == other.type_is
                 and self.fallback == other.fallback
             )
         else:
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 0be9d918c69f..78680684f69b 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -2929,8 +2929,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]:
 def id(__x: U) -> U:
     ...
 fs = [id, id, id]
-reveal_type(mix(fs))  # N: Revealed type is "def [S] (S`11) -> builtins.list[S`11]"
-reveal_type(mix([id, id, id]))  # N: Revealed type is "def [S] (S`13) -> builtins.list[S`13]"
+reveal_type(mix(fs))  # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]"
+reveal_type(mix([id, id, id]))  # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCurry]
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
index e53c45b5b512..0835ba7ac57d 100644
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -921,8 +921,8 @@ class A:
     def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R:
         ...
 
-reveal_type(A.func)  # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`6, *_P.args, **_P.kwargs) -> _R`6"
-reveal_type(A().func)  # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`10, *_P.args, **_P.kwargs) -> _R`10"
+reveal_type(A.func)  # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`4, *_P.args, **_P.kwargs) -> _R`4"
+reveal_type(A().func)  # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`8, *_P.args, **_P.kwargs) -> _R`8"
 
 def f(x: int) -> int:
     ...
@@ -953,8 +953,8 @@ class A:
     def func(self, action: Job[_P, None]) -> Job[_P, None]:
         ...
 
-reveal_type(A.func)  # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`4, None]) -> __main__.Job[_P`4, None]"
-reveal_type(A().func)  # N: Revealed type is "def [_P] (action: __main__.Job[_P`6, None]) -> __main__.Job[_P`6, None]"
+reveal_type(A.func)  # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]"
+reveal_type(A().func)  # N: Revealed type is "def [_P] (action: __main__.Job[_P`5, None]) -> __main__.Job[_P`5, None]"
 reveal_type(A().func(Job(lambda x: x)))  # N: Revealed type is "__main__.Job[[x: Any], None]"
 
 def f(x: int, y: int) -> None: ...

From 6f7d716c108dc8a8385c3bd7f7015b04c0c374b8 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 16 Jul 2025 18:17:47 +0100
Subject: [PATCH 1469/1617] Speed up the default plugin (#19462)

Use precalculated set objects in more places.

This is similar to #19385. Some cases were still unoptimized.

I used trace logging (#19457) to identify functions where we were
creating many set objects, and I noticed that these were unnecessary.

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/plugins/default.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py
index 3d27ca99302f..e492b8dd7335 100644
--- a/mypy/plugins/default.py
+++ b/mypy/plugins/default.py
@@ -82,6 +82,7 @@
 
 TD_SETDEFAULT_NAMES: Final = {n + ".setdefault" for n in TPDICT_FB_NAMES}
 TD_POP_NAMES: Final = {n + ".pop" for n in TPDICT_FB_NAMES}
+TD_DELITEM_NAMES: Final = {n + ".__delitem__" for n in TPDICT_FB_NAMES}
 
 TD_UPDATE_METHOD_NAMES: Final = (
     {n + ".update" for n in TPDICT_FB_NAMES}
@@ -144,11 +145,11 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No
             return int_pos_callback
         elif fullname in ("builtins.tuple.__mul__", "builtins.tuple.__rmul__"):
             return tuple_mul_callback
-        elif fullname in {n + ".setdefault" for n in TPDICT_FB_NAMES}:
+        elif fullname in TD_SETDEFAULT_NAMES:
             return typed_dict_setdefault_callback
-        elif fullname in {n + ".pop" for n in TPDICT_FB_NAMES}:
+        elif fullname in TD_POP_NAMES:
             return typed_dict_pop_callback
-        elif fullname in {n + ".__delitem__" for n in TPDICT_FB_NAMES}:
+        elif fullname in TD_DELITEM_NAMES:
             return typed_dict_delitem_callback
         elif fullname == "_ctypes.Array.__getitem__":
             return array_getitem_callback

From ca738e5d43be9d8fe6cdafcfe08a3cdf0fd435ea Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 16 Jul 2025 18:18:23 +0100
Subject: [PATCH 1470/1617] Micro-optimization: Avoid temporary set creation in
 is_proper_subtype (#19463)

This was suggested by @sterliakov in
https://github.com/python/mypy/pull/19384#issuecomment-3044364827

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/subtypes.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mypy/subtypes.py b/mypy/subtypes.py
index 1aa8543505ec..7da258a827f3 100644
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -217,8 +217,8 @@ def is_proper_subtype(
             keep_erased_types=keep_erased_types,
         )
     else:
-        assert not any(
-            {ignore_promotions, erase_instances, keep_erased_types}
+        assert (
+            not ignore_promotions and not erase_instances and not keep_erased_types
         ), "Don't pass both context and individual flags"
     if type_state.is_assumed_proper_subtype(left, right):
         return True

From 5f5871dc7646d9a7791c7b0e5a872f63eb24dd41 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 17 Jul 2025 10:10:36 +0100
Subject: [PATCH 1471/1617] Micro-optimize ExpandTypeVisitor (#19461)

Specialize a hot for loop for the concrete `tuple` and `list` types.
Also add a fast path for empty type arguments.

The approach is similar to what I used in #19459.

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/expandtype.py | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/mypy/expandtype.py b/mypy/expandtype.py
index d27105f48ed3..f704df3b010e 100644
--- a/mypy/expandtype.py
+++ b/mypy/expandtype.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-from collections.abc import Iterable, Mapping, Sequence
+from collections.abc import Iterable, Mapping
 from typing import Final, TypeVar, cast, overload
 
 from mypy.nodes import ARG_STAR, FakeInfo, Var
@@ -209,7 +209,11 @@ def visit_erased_type(self, t: ErasedType) -> Type:
         return t
 
     def visit_instance(self, t: Instance) -> Type:
-        args = self.expand_types_with_unpack(list(t.args))
+        if len(t.args) == 0:
+            # TODO: Why do we need to create a copy here?
+            return t.copy_modified()
+
+        args = self.expand_type_tuple_with_unpack(t.args)
 
         if isinstance(t.type, FakeInfo):
             # The type checker expands function definitions and bodies
@@ -431,7 +435,7 @@ def visit_overloaded(self, t: Overloaded) -> Type:
             items.append(new_item)
         return Overloaded(items)
 
-    def expand_types_with_unpack(self, typs: Sequence[Type]) -> list[Type]:
+    def expand_type_list_with_unpack(self, typs: list[Type]) -> list[Type]:
         """Expands a list of types that has an unpack."""
         items: list[Type] = []
         for item in typs:
@@ -441,8 +445,19 @@ def expand_types_with_unpack(self, typs: Sequence[Type]) -> list[Type]:
                 items.append(item.accept(self))
         return items
 
+    def expand_type_tuple_with_unpack(self, typs: tuple[Type, ...]) -> list[Type]:
+        """Expands a tuple of types that has an unpack."""
+        # Micro-optimization: Specialized variant of expand_type_list_with_unpack
+        items: list[Type] = []
+        for item in typs:
+            if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType):
+                items.extend(self.expand_unpack(item))
+            else:
+                items.append(item.accept(self))
+        return items
+
     def visit_tuple_type(self, t: TupleType) -> Type:
-        items = self.expand_types_with_unpack(t.items)
+        items = self.expand_type_list_with_unpack(t.items)
         if len(items) == 1:
             # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...]
             item = items[0]
@@ -510,7 +525,7 @@ def visit_type_type(self, t: TypeType) -> Type:
     def visit_type_alias_type(self, t: TypeAliasType) -> Type:
         # Target of the type alias cannot contain type variables (not bound by the type
         # alias itself), so we just expand the arguments.
-        args = self.expand_types_with_unpack(t.args)
+        args = self.expand_type_list_with_unpack(t.args)
         # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]?
         return t.copy_modified(args=args)
 

From a82948b87609eb422492db6733f18c3bbddc2920 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 17 Jul 2025 10:11:59 +0100
Subject: [PATCH 1472/1617] Micro-optimize chained plugin (#19464)

Avoid using lambdas in the most expensive hooks, since they are slower
than direct method calls. Also use `if hook is None` checks instead of
`if hook`, since the prior is more efficient when compiled.

I used trace logging to look for generic/unoptimized function calls, and
it was clear that ChainedPlugin was doing many unoptimized calls that
were easy to avoid.

This duplicates some code, but I think it's fine since this code is
updated very rarely but the code paths are very hot.

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/plugin.py | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/mypy/plugin.py b/mypy/plugin.py
index 831721eb193c..9019e3c2256f 100644
--- a/mypy/plugin.py
+++ b/mypy/plugin.py
@@ -846,12 +846,22 @@ def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]:
         return deps
 
     def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None:
-        return self._find_hook(lambda plugin: plugin.get_type_analyze_hook(fullname))
+        # Micro-optimization: Inline iteration over plugins
+        for plugin in self._plugins:
+            hook = plugin.get_type_analyze_hook(fullname)
+            if hook is not None:
+                return hook
+        return None
 
     def get_function_signature_hook(
         self, fullname: str
     ) -> Callable[[FunctionSigContext], FunctionLike] | None:
-        return self._find_hook(lambda plugin: plugin.get_function_signature_hook(fullname))
+        # Micro-optimization: Inline iteration over plugins
+        for plugin in self._plugins:
+            hook = plugin.get_function_signature_hook(fullname)
+            if hook is not None:
+                return hook
+        return None
 
     def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None:
         return self._find_hook(lambda plugin: plugin.get_function_hook(fullname))
@@ -859,13 +869,28 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type]
     def get_method_signature_hook(
         self, fullname: str
     ) -> Callable[[MethodSigContext], FunctionLike] | None:
-        return self._find_hook(lambda plugin: plugin.get_method_signature_hook(fullname))
+        # Micro-optimization: Inline iteration over plugins
+        for plugin in self._plugins:
+            hook = plugin.get_method_signature_hook(fullname)
+            if hook is not None:
+                return hook
+        return None
 
     def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None:
-        return self._find_hook(lambda plugin: plugin.get_method_hook(fullname))
+        # Micro-optimization: Inline iteration over plugins
+        for plugin in self._plugins:
+            hook = plugin.get_method_hook(fullname)
+            if hook is not None:
+                return hook
+        return None
 
     def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
-        return self._find_hook(lambda plugin: plugin.get_attribute_hook(fullname))
+        # Micro-optimization: Inline iteration over plugins
+        for plugin in self._plugins:
+            hook = plugin.get_attribute_hook(fullname)
+            if hook is not None:
+                return hook
+        return None
 
     def get_class_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
         return self._find_hook(lambda plugin: plugin.get_class_attribute_hook(fullname))
@@ -897,6 +922,6 @@ def get_dynamic_class_hook(
     def _find_hook(self, lookup: Callable[[Plugin], T]) -> T | None:
         for plugin in self._plugins:
             hook = lookup(plugin)
-            if hook:
+            if hook is not None:
                 return hook
         return None

From 8bd159b755529e067cf67e489b7f345a0b442468 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 17 Jul 2025 10:14:48 +0100
Subject: [PATCH 1473/1617] Micro-optimize TypeTraverserVisitor (#19459)

Specialize for concrete sequence types (`list` and `tuple`) for faster
iteration, since the traversal code is very hot.

I used trace logging (#19457) to identify functions where
`PyObject_GetIter` was called frequently.

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/typetraverser.py | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py
index cc6d4b637d2e..047c5caf6dae 100644
--- a/mypy/typetraverser.py
+++ b/mypy/typetraverser.py
@@ -67,7 +67,7 @@ def visit_param_spec(self, t: ParamSpecType, /) -> None:
         t.default.accept(self)
 
     def visit_parameters(self, t: Parameters, /) -> None:
-        self.traverse_types(t.arg_types)
+        self.traverse_type_list(t.arg_types)
 
     def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> None:
         t.default.accept(self)
@@ -78,11 +78,11 @@ def visit_literal_type(self, t: LiteralType, /) -> None:
     # Composite types
 
     def visit_instance(self, t: Instance, /) -> None:
-        self.traverse_types(t.args)
+        self.traverse_type_tuple(t.args)
 
     def visit_callable_type(self, t: CallableType, /) -> None:
         # FIX generics
-        self.traverse_types(t.arg_types)
+        self.traverse_type_list(t.arg_types)
         t.ret_type.accept(self)
         t.fallback.accept(self)
 
@@ -93,7 +93,7 @@ def visit_callable_type(self, t: CallableType, /) -> None:
             t.type_is.accept(self)
 
     def visit_tuple_type(self, t: TupleType, /) -> None:
-        self.traverse_types(t.items)
+        self.traverse_type_list(t.items)
         t.partial_fallback.accept(self)
 
     def visit_typeddict_type(self, t: TypedDictType, /) -> None:
@@ -101,7 +101,7 @@ def visit_typeddict_type(self, t: TypedDictType, /) -> None:
         t.fallback.accept(self)
 
     def visit_union_type(self, t: UnionType, /) -> None:
-        self.traverse_types(t.items)
+        self.traverse_type_list(t.items)
 
     def visit_overloaded(self, t: Overloaded, /) -> None:
         self.traverse_types(t.items)
@@ -115,16 +115,16 @@ def visit_callable_argument(self, t: CallableArgument, /) -> None:
         t.typ.accept(self)
 
     def visit_unbound_type(self, t: UnboundType, /) -> None:
-        self.traverse_types(t.args)
+        self.traverse_type_tuple(t.args)
 
     def visit_type_list(self, t: TypeList, /) -> None:
-        self.traverse_types(t.items)
+        self.traverse_type_list(t.items)
 
     def visit_ellipsis_type(self, t: EllipsisType, /) -> None:
         pass
 
     def visit_placeholder_type(self, t: PlaceholderType, /) -> None:
-        self.traverse_types(t.args)
+        self.traverse_type_list(t.args)
 
     def visit_partial_type(self, t: PartialType, /) -> None:
         pass
@@ -136,7 +136,7 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> None:
         # TODO: sometimes we want to traverse target as well
         # We need to find a way to indicate explicitly the intent,
         # maybe make this method abstract (like for TypeTranslator)?
-        self.traverse_types(t.args)
+        self.traverse_type_list(t.args)
 
     def visit_unpack_type(self, t: UnpackType, /) -> None:
         t.type.accept(self)
@@ -146,3 +146,13 @@ def visit_unpack_type(self, t: UnpackType, /) -> None:
     def traverse_types(self, types: Iterable[Type], /) -> None:
         for typ in types:
             typ.accept(self)
+
+    def traverse_type_list(self, types: list[Type], /) -> None:
+        # Micro-optimization: Specialized for lists
+        for typ in types:
+            typ.accept(self)
+
+    def traverse_type_tuple(self, types: tuple[Type, ...], /) -> None:
+        # Micro-optimization: Specialized for tuples
+        for typ in types:
+            typ.accept(self)

From 32752ebc58011bc3067dba239e50c91bc6304bd4 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 17 Jul 2025 17:23:53 +0100
Subject: [PATCH 1474/1617] [mypyc] Add script to compile mypy with trace
 logging and run mypy (#19475)

Trace logging helps when analyzing low-level performance issues in mypy.
---
 misc/log_trace_check.py | 85 +++++++++++++++++++++++++++++++++++++++++
 misc/perf_compare.py    | 13 ++++++-
 setup.py                |  2 +
 3 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 misc/log_trace_check.py

diff --git a/misc/log_trace_check.py b/misc/log_trace_check.py
new file mode 100644
index 000000000000..677c164fe992
--- /dev/null
+++ b/misc/log_trace_check.py
@@ -0,0 +1,85 @@
+"""Compile mypy using mypyc with trace logging enabled, and collect a trace.
+
+The trace log can be used to analyze low-level performance bottlenecks.
+
+By default does a self check as the workload.
+
+This works on all supported platforms, unlike some of our other performance tools.
+"""
+
+from __future__ import annotations
+
+import argparse
+import glob
+import os
+import shutil
+import subprocess
+import sys
+import time
+
+from perf_compare import build_mypy, clone
+
+# Generated files, including binaries, go under this directory to avoid overwriting user state.
+TARGET_DIR = "mypy.log_trace.tmpdir"
+
+
+def perform_type_check(target_dir: str, code: str | None) -> None:
+    cache_dir = os.path.join(target_dir, ".mypy_cache")
+    if os.path.exists(cache_dir):
+        shutil.rmtree(cache_dir)
+    args = []
+    if code is None:
+        args.extend(["--config-file", "mypy_self_check.ini"])
+        for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py":
+            args.extend(glob.glob(pat))
+    else:
+        args.extend(["-c", code])
+    check_cmd = ["python", "-m", "mypy"] + args
+    t0 = time.time()
+    subprocess.run(check_cmd, cwd=target_dir, check=True)
+    elapsed = time.time() - t0
+    print(f"{elapsed:.2f}s elapsed")
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(
+        description="Compile mypy and collect a trace log while type checking (by default, self check)."
+    )
+    parser.add_argument(
+        "--multi-file",
+        action="store_true",
+        help="compile mypy into one C file per module (to reduce RAM use during compilation)",
+    )
+    parser.add_argument(
+        "--skip-compile", action="store_true", help="use compiled mypy from previous run"
+    )
+    parser.add_argument(
+        "-c",
+        metavar="CODE",
+        default=None,
+        type=str,
+        help="type check Python code fragment instead of mypy self-check",
+    )
+    args = parser.parse_args()
+    multi_file: bool = args.multi_file
+    skip_compile: bool = args.skip_compile
+    code: str | None = args.c
+
+    target_dir = TARGET_DIR
+
+    if not skip_compile:
+        clone(target_dir, "HEAD")
+
+        print(f"Building mypy in {target_dir} with trace logging enabled...")
+        build_mypy(target_dir, multi_file, log_trace=True, opt_level="0")
+    elif not os.path.isdir(target_dir):
+        sys.exit("error: Can't find compile mypy from previous run -- can't use --skip-compile")
+
+    perform_type_check(target_dir, code)
+
+    trace_fnam = os.path.join(target_dir, "mypyc_trace.txt")
+    print(f"Generated event trace log in {trace_fnam}")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/misc/perf_compare.py b/misc/perf_compare.py
index 7d22f43d1c45..aa05270a8c00 100755
--- a/misc/perf_compare.py
+++ b/misc/perf_compare.py
@@ -37,13 +37,22 @@ def heading(s: str) -> None:
     print()
 
 
-def build_mypy(target_dir: str, multi_file: bool, *, cflags: str | None = None) -> None:
+def build_mypy(
+    target_dir: str,
+    multi_file: bool,
+    *,
+    cflags: str | None = None,
+    log_trace: bool = False,
+    opt_level: str = "2",
+) -> None:
     env = os.environ.copy()
     env["CC"] = "clang"
-    env["MYPYC_OPT_LEVEL"] = "2"
+    env["MYPYC_OPT_LEVEL"] = opt_level
     env["PYTHONHASHSEED"] = "1"
     if multi_file:
         env["MYPYC_MULTI_FILE"] = "1"
+    if log_trace:
+        env["MYPYC_LOG_TRACE"] = "1"
     if cflags is not None:
         env["CFLAGS"] = cflags
     cmd = [sys.executable, "setup.py", "--use-mypyc", "build_ext", "--inplace"]
diff --git a/setup.py b/setup.py
index 12cc1aad4d72..e085b0be3846 100644
--- a/setup.py
+++ b/setup.py
@@ -145,6 +145,7 @@ def run(self) -> None:
     opt_level = os.getenv("MYPYC_OPT_LEVEL", "3")
     debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1")
     force_multifile = os.getenv("MYPYC_MULTI_FILE", "") == "1"
+    log_trace = bool(int(os.getenv("MYPYC_LOG_TRACE", "0")))
     ext_modules = mypycify(
         mypyc_targets + ["--config-file=mypy_bootstrap.ini"],
         opt_level=opt_level,
@@ -152,6 +153,7 @@ def run(self) -> None:
         # Use multi-file compilation mode on windows because without it
         # our Appveyor builds run out of memory sometimes.
         multi_file=sys.platform == "win32" or force_multifile,
+        log_trace=log_trace,
     )
 
 else:

From 23e6072cb969aab81b6b07b096f7e6d07d522a46 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 18 Jul 2025 09:28:05 +0100
Subject: [PATCH 1475/1617] Micro-optimize type indirection visitor (#19460)

Specialize iteration for the concrete types `list` and `tuple`, since
this is faster than iterating over an abstract iterable in compiled
code. Also avoid constructing many temporary lists.

Duplicate some very hot code in the hopes further improving performance
slightly, through inlining and possibly also better branch prediction.

The approach is similar to what I used in #19459.

This is a part of a set of micro-optimizations that improve self check
performance by ~5.5%.
---
 mypy/indirection.py | 42 ++++++++++++++++++++++++++++++------------
 1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/mypy/indirection.py b/mypy/indirection.py
index 4f455d2c1dc9..06a158818fbe 100644
--- a/mypy/indirection.py
+++ b/mypy/indirection.py
@@ -32,11 +32,29 @@ def find_modules(self, typs: Iterable[types.Type]) -> set[str]:
         self.modules = set()
         self.seen_fullnames = set()
         self.seen_aliases = set()
-        self._visit(typs)
+        for typ in typs:
+            self._visit(typ)
         return self.modules
 
-    def _visit(self, typ_or_typs: types.Type | Iterable[types.Type]) -> None:
-        typs = [typ_or_typs] if isinstance(typ_or_typs, types.Type) else typ_or_typs
+    def _visit(self, typ: types.Type) -> None:
+        if isinstance(typ, types.TypeAliasType):
+            # Avoid infinite recursion for recursive type aliases.
+            if typ not in self.seen_aliases:
+                self.seen_aliases.add(typ)
+        typ.accept(self)
+
+    def _visit_type_tuple(self, typs: tuple[types.Type, ...]) -> None:
+        # Micro-optimization: Specialized version of _visit for lists
+        for typ in typs:
+            if isinstance(typ, types.TypeAliasType):
+                # Avoid infinite recursion for recursive type aliases.
+                if typ in self.seen_aliases:
+                    continue
+                self.seen_aliases.add(typ)
+            typ.accept(self)
+
+    def _visit_type_list(self, typs: list[types.Type]) -> None:
+        # Micro-optimization: Specialized version of _visit for tuples
         for typ in typs:
             if isinstance(typ, types.TypeAliasType):
                 # Avoid infinite recursion for recursive type aliases.
@@ -50,7 +68,7 @@ def _visit_module_name(self, module_name: str) -> None:
             self.modules.update(split_module_names(module_name))
 
     def visit_unbound_type(self, t: types.UnboundType) -> None:
-        self._visit(t.args)
+        self._visit_type_tuple(t.args)
 
     def visit_any(self, t: types.AnyType) -> None:
         pass
@@ -68,7 +86,7 @@ def visit_deleted_type(self, t: types.DeletedType) -> None:
         pass
 
     def visit_type_var(self, t: types.TypeVarType) -> None:
-        self._visit(t.values)
+        self._visit_type_list(t.values)
         self._visit(t.upper_bound)
         self._visit(t.default)
 
@@ -84,10 +102,10 @@ def visit_unpack_type(self, t: types.UnpackType) -> None:
         t.type.accept(self)
 
     def visit_parameters(self, t: types.Parameters) -> None:
-        self._visit(t.arg_types)
+        self._visit_type_list(t.arg_types)
 
     def visit_instance(self, t: types.Instance) -> None:
-        self._visit(t.args)
+        self._visit_type_tuple(t.args)
         if t.type:
             # Uses of a class depend on everything in the MRO,
             # as changes to classes in the MRO can add types to methods,
@@ -98,7 +116,7 @@ def visit_instance(self, t: types.Instance) -> None:
                 self._visit_module_name(t.type.metaclass_type.type.module_name)
 
     def visit_callable_type(self, t: types.CallableType) -> None:
-        self._visit(t.arg_types)
+        self._visit_type_list(t.arg_types)
         self._visit(t.ret_type)
         if t.definition is not None:
             fullname = t.definition.fullname
@@ -107,22 +125,22 @@ def visit_callable_type(self, t: types.CallableType) -> None:
                 self.seen_fullnames.add(fullname)
 
     def visit_overloaded(self, t: types.Overloaded) -> None:
-        self._visit(t.items)
+        self._visit_type_list(list(t.items))
         self._visit(t.fallback)
 
     def visit_tuple_type(self, t: types.TupleType) -> None:
-        self._visit(t.items)
+        self._visit_type_list(t.items)
         self._visit(t.partial_fallback)
 
     def visit_typeddict_type(self, t: types.TypedDictType) -> None:
-        self._visit(t.items.values())
+        self._visit_type_list(list(t.items.values()))
         self._visit(t.fallback)
 
     def visit_literal_type(self, t: types.LiteralType) -> None:
         self._visit(t.fallback)
 
     def visit_union_type(self, t: types.UnionType) -> None:
-        self._visit(t.items)
+        self._visit_type_list(t.items)
 
     def visit_partial_type(self, t: types.PartialType) -> None:
         pass

From abb1cc787455501e43f344c8af2de7d0d262e5ef Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 18 Jul 2025 15:34:19 +0100
Subject: [PATCH 1476/1617] Uninhabited should have all attributes (#19300)

Although this can hide some mypy bugs, TBH this always bothered me as
something conceptually wrong. The work on `checkmember` stuff inspired
me to actually try it.
---
 mypy/checkexpr.py                          |  4 ++++
 mypy/checkmember.py                        |  5 +++++
 test-data/unit/check-unreachable-code.test | 16 ++++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 8223ccfe4ca0..24f0c8c85d61 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1671,6 +1671,10 @@ def check_call(
                 object_type,
                 original_type=callee,
             )
+        elif isinstance(callee, UninhabitedType):
+            ret = UninhabitedType()
+            ret.ambiguous = callee.ambiguous
+            return callee, ret
         else:
             return self.msg.not_callable(callee, context), AnyType(TypeOfAny.from_error)
 
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index ef38cc3a0dcf..7ce7e69e21d8 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -69,6 +69,7 @@
     TypeVarLikeType,
     TypeVarTupleType,
     TypeVarType,
+    UninhabitedType,
     UnionType,
     get_proper_type,
 )
@@ -268,6 +269,10 @@ def _analyze_member_access(
         if not mx.suppress_errors:
             mx.msg.deleted_as_rvalue(typ, mx.context)
         return AnyType(TypeOfAny.from_error)
+    elif isinstance(typ, UninhabitedType):
+        attr_type = UninhabitedType()
+        attr_type.ambiguous = typ.ambiguous
+        return attr_type
     return report_missing_attribute(mx.original_type, typ, name, mx)
 
 
diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test
index 368431127b76..f425410a9774 100644
--- a/test-data/unit/check-unreachable-code.test
+++ b/test-data/unit/check-unreachable-code.test
@@ -1587,3 +1587,19 @@ x = 0  # not unreachable
 
 f2: Callable[[], NoReturn] = lambda: foo()
 x = 0  # not unreachable
+
+[case testAttributeNoReturn]
+# flags: --warn-unreachable
+from typing import Optional, NoReturn, TypeVar
+
+def foo() -> NoReturn:
+    raise
+
+T = TypeVar("T")
+def bar(x: Optional[list[T]] = None) -> T:
+    ...
+
+reveal_type(bar().attr)  # N: Revealed type is "Never"
+1  # not unreachable
+reveal_type(foo().attr)  # N: Revealed type is "Never"
+1  # E: Statement is unreachable

From a0665e1645ec7ff646d08e78f39113f2a5e4387d Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 18 Jul 2025 16:41:41 +0200
Subject: [PATCH 1477/1617] Fix "ignored exception in `hasattr`" in dmypy
 (#19428)

Fixes #19425. That property has no setter so it should safe to exclude.
It can raise in inconsistent state that arises when it is not copied
last?
---
 mypy/server/astmerge.py | 15 ++++++++++-----
 pyproject.toml          |  8 ++++++++
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py
index 8cd574628bb8..33e2d2b799cb 100644
--- a/mypy/server/astmerge.py
+++ b/mypy/server/astmerge.py
@@ -345,13 +345,11 @@ def visit_type_alias(self, node: TypeAlias) -> None:
     def fixup(self, node: SN) -> SN:
         if node in self.replacements:
             new = self.replacements[node]
-            skip_slots: tuple[str, ...] = ()
             if isinstance(node, TypeInfo) and isinstance(new, TypeInfo):
                 # Special case: special_alias is not exposed in symbol tables, but may appear
                 # in external types (e.g. named tuples), so we need to update it manually.
-                skip_slots = ("special_alias",)
                 replace_object_state(new.special_alias, node.special_alias)
-            replace_object_state(new, node, skip_slots=skip_slots)
+            replace_object_state(new, node, skip_slots=_get_ignored_slots(new))
             return cast(SN, new)
         return node
 
@@ -556,9 +554,16 @@ def replace_nodes_in_symbol_table(
             if node.node in replacements:
                 new = replacements[node.node]
                 old = node.node
-                # Needed for TypeInfo, see comment in fixup() above.
-                replace_object_state(new, old, skip_slots=("special_alias",))
+                replace_object_state(new, old, skip_slots=_get_ignored_slots(new))
                 node.node = new
             if isinstance(node.node, (Var, TypeAlias)):
                 # Handle them here just in case these aren't exposed through the AST.
                 node.node.accept(NodeReplaceVisitor(replacements))
+
+
+def _get_ignored_slots(node: SymbolNode) -> tuple[str, ...]:
+    if isinstance(node, OverloadedFuncDef):
+        return ("setter",)
+    if isinstance(node, TypeInfo):
+        return ("special_alias",)
+    return ()
diff --git a/pyproject.toml b/pyproject.toml
index 1870e0931407..032bfcb609e7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -222,6 +222,14 @@ addopts = "-nauto --strict-markers --strict-config"
 # treat xpasses as test failures so they get converted to regular tests as soon as possible
 xfail_strict = true
 
+# Force warnings as errors
+filterwarnings = [
+  "error",
+  # Some testcases may contain code that emits SyntaxWarnings, and they are not yet
+  # handled consistently in 3.14 (PEP 765)
+  "default::SyntaxWarning",
+]
+
 [tool.coverage.run]
 branch = true
 source = ["mypy"]

From f42c93685521cb276fd59acdeaedc1d1b1148204 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 21 Jul 2025 20:30:59 +0200
Subject: [PATCH 1478/1617] Retain `None` as constraints bottom if no bottoms
 were provided (#19485)

Current version replaces `None` (which indicates "no items") with an
empty union (=`Uninhabited`). This breaks inference in some cases.

Fixes #19483.
---
 mypy/solve.py                             |  6 ++++--
 test-data/unit/check-recursive-types.test |  4 ++--
 test-data/unit/check-typeddict.test       | 18 ++++++++++++++++++
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/mypy/solve.py b/mypy/solve.py
index 098d926bc789..fbbcac2520ad 100644
--- a/mypy/solve.py
+++ b/mypy/solve.py
@@ -270,6 +270,7 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
     uppers = new_uppers
 
     # ...unless this is the only information we have, then we just pass it on.
+    lowers = list(lowers)
     if not uppers and not lowers:
         candidate = UninhabitedType()
         candidate.ambiguous = True
@@ -281,10 +282,11 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
     # Process each bound separately, and calculate the lower and upper
     # bounds based on constraints. Note that we assume that the constraint
     # targets do not have constraint references.
-    if type_state.infer_unions:
+    if type_state.infer_unions and lowers:
         # This deviates from the general mypy semantics because
         # recursive types are union-heavy in 95% of cases.
-        bottom = UnionType.make_union(list(lowers))
+        # Retain `None` when no bottoms were provided to avoid bogus `Never` inference.
+        bottom = UnionType.make_union(lowers)
     else:
         # The order of lowers is non-deterministic.
         # We attempt to sort lowers because joins are non-associative. For instance:
diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test
index 7ed5ea53c27e..86e9f02b5263 100644
--- a/test-data/unit/check-recursive-types.test
+++ b/test-data/unit/check-recursive-types.test
@@ -54,7 +54,7 @@ reveal_type(flatten([1, [2, [3]]]))  # N: Revealed type is "builtins.list[builti
 
 class Bad: ...
 x: Nested[int] = [1, [2, [3]]]
-x = [1, [Bad()]]  # E: List item 1 has incompatible type "list[Bad]"; expected "Union[int, Nested[int]]"
+x = [1, [Bad()]]  # E: List item 0 has incompatible type "Bad"; expected "Union[int, Nested[int]]"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testRecursiveAliasGenericInferenceNested]
@@ -605,7 +605,7 @@ class NT(NamedTuple, Generic[T]):
 class A: ...
 class B(A): ...
 
-nti: NT[int] = NT(key=0, value=NT(key=1, value=A()))  # E: Argument "value" to "NT" has incompatible type "NT[A]"; expected "Union[int, NT[int]]"
+nti: NT[int] = NT(key=0, value=NT(key=1, value=A()))  # E: Argument "value" to "NT" has incompatible type "A"; expected "Union[int, NT[int]]"
 reveal_type(nti)  # N: Revealed type is "tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]"
 
 nta: NT[A]
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index a068a63274ca..be5a6c655d8c 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -4271,3 +4271,21 @@ reveal_type(dicts.TF)  # N: Revealed type is "def (*, user_id: builtins.int =) -
 reveal_type(dicts.TotalFalse)  # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
+
+[case testRecursiveNestedTypedDictInference]
+from typing import TypedDict, Sequence
+from typing_extensions import NotRequired
+
+class Component(TypedDict):
+    type: str
+    components: NotRequired[Sequence['Component']]
+
+inputs: Sequence[Component] = [{
+    'type': 'tuple',
+    'components': [
+        {'type': 'uint256'},
+        {'type': 'address'},
+    ]
+}]
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]

From d5287070fc60c9a30b305372bd12ce3c1985bf28 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 21 Jul 2025 19:31:50 +0100
Subject: [PATCH 1479/1617] Fix decorated methods with self-types in protocols
 (#19484)

Fixes https://github.com/python/mypy/issues/19482

The regression is caused by overlap of three problems (directly or
indirectly):
* Using `mx.original_type` instead of `mx.self_type` for expanding (not
otherwise bound) self-types.
* Having a weird special case for `...` vs self-types in constraint
inference.
* Not refreshing type variable ids during protocol subtype checks (hack
needed for technical reasons).

I fix the first one, and limit the blast radius of the second one.
---
 mypy/checkmember.py                 |  2 +-
 mypy/constraints.py                 |  5 ++++-
 test-data/unit/check-protocols.test | 14 ++++++++++++++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 7ce7e69e21d8..7eedab2e399a 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -956,7 +956,7 @@ def expand_and_bind_callable(
 ) -> Type:
     if not mx.preserve_type_var_ids:
         functype = freshen_all_functions_type_vars(functype)
-    typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
+    typ = get_proper_type(expand_self_type(var, functype, mx.self_type))
     assert isinstance(typ, FunctionLike)
     if is_trivial_self:
         typ = bind_self_fast(typ, mx.self_type)
diff --git a/mypy/constraints.py b/mypy/constraints.py
index 9eeea3cb2c26..6416791fa74a 100644
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -1110,7 +1110,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]:
                     # like U -> U, should be Callable[..., Any], but if U is a self-type, we can
                     # allow it to leak, to be later bound to self. A bunch of existing code
                     # depends on this old behaviour.
-                    and not any(tv.id.is_self() for tv in cactual.variables)
+                    and not (
+                        any(tv.id.is_self() for tv in cactual.variables)
+                        and template.is_ellipsis_args
+                    )
                 ):
                     # If the actual callable is generic, infer constraints in the opposite
                     # direction, and indicate to the solver there are extra type variables
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
index 79207c9aad56..0f19b404082e 100644
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -4646,3 +4646,17 @@ reveal_type(t.bar)  # N: Revealed type is "def () -> builtins.int"
 tt: Type[P] = C
 reveal_type(tt.foo)  # N: Revealed type is "def (builtins.object) -> builtins.int"
 reveal_type(tt.bar)  # N: Revealed type is "def (builtins.object) -> builtins.int"
+
+[case testProtocolDecoratedSelfBound]
+from abc import abstractmethod
+from typing import Protocol, Self
+
+class Proto(Protocol):
+    @abstractmethod
+    def foo(self, x: Self) -> None: ...
+
+class Impl:
+    def foo(self, x: Self) -> None:
+        pass
+
+x: Proto = Impl()

From 2e5d7eecd7673f9098f54505e68444762855d812 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Wed, 23 Jul 2025 01:55:40 +0200
Subject: [PATCH 1480/1617] Use normalized tuples for fallback calculation
 (#19111)

Fixes #19105. I haven't checked this with namedtuples magic, nor am I
certain that this is the best way.
---
 mypy/semanal_shared.py                  |  3 ++-
 mypy/semanal_typeargs.py                |  6 ++++++
 test-data/unit/check-typevar-tuple.test | 27 +++++++++++++++++++++++++
 3 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py
index bdd01ef6a6f3..e94604b66381 100644
--- a/mypy/semanal_shared.py
+++ b/mypy/semanal_shared.py
@@ -46,6 +46,7 @@
     TypeVarLikeType,
     TypeVarTupleType,
     UnpackType,
+    flatten_nested_tuples,
     get_proper_type,
 )
 
@@ -290,7 +291,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None:
     fallback = typ.partial_fallback
     assert fallback.type.fullname == "builtins.tuple"
     items = []
-    for item in typ.items:
+    for item in flatten_nested_tuples(typ.items):
         # TODO: this duplicates some logic in typeops.tuple_fallback().
         if isinstance(item, UnpackType):
             unpacked_type = get_proper_type(item.type)
diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py
index 435abb78ca43..be39a8259c2e 100644
--- a/mypy/semanal_typeargs.py
+++ b/mypy/semanal_typeargs.py
@@ -102,6 +102,8 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None:
             # If there was already an error for the alias itself, there is no point in checking
             # the expansion, most likely it will result in the same kind of error.
             get_proper_type(t).accept(self)
+            if t.alias is not None:
+                t.alias.accept(self)
 
     def visit_tuple_type(self, t: TupleType) -> None:
         t.items = flatten_nested_tuples(t.items)
@@ -254,6 +256,10 @@ def visit_unpack_type(self, typ: UnpackType) -> None:
     def check_type_var_values(
         self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context
     ) -> bool:
+        if self.in_type_alias_expr:
+            # See testValidTypeAliasValues - we do not enforce typevar compatibility
+            # at the definition site. We check instantiation validity later.
+            return False
         is_error = False
         for actual in get_proper_types(actuals):
             # We skip UnboundType here, since they may appear in defn.bases,
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index db0e26ba2b36..e931c0c01aee 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -2666,3 +2666,30 @@ def identity(smth: _FT) -> _FT:
 class S(tuple[Unpack[Ts]], Generic[T, Unpack[Ts]]):
     def f(self, x: T, /) -> T: ...
 [builtins fixtures/tuple.pyi]
+
+[case testNoCrashSubclassingTupleWithTrivialUnpack]
+# https://github.com/python/mypy/issues/19105
+from typing import Unpack
+
+class A(tuple[Unpack[tuple[int]]]): ...
+class B(tuple[Unpack[tuple[()]]]): ...
+
+a: A
+reveal_type(tuple(a))  # N: Revealed type is "builtins.tuple[builtins.int, ...]"
+(x,) = a
+
+b: B
+(_,) = b  # E: Need more than 0 values to unpack (1 expected)
+[builtins fixtures/tuple.pyi]
+
+[case testNoCrashSubclassingTupleWithVariadicUnpack]
+# https://github.com/python/mypy/issues/19105
+from typing import Unpack
+
+class A(tuple[Unpack[tuple[int, ...]]]): ...
+
+a: A
+tuple(a)
+(x,) = a
+(_,) = a
+[builtins fixtures/tuple.pyi]

From a56adc82f7dd747f400bb9267ec32b73d22de64e Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 25 Jul 2025 09:33:18 +0100
Subject: [PATCH 1481/1617] Optimize generic inference passes (#19501)

This gives around 5% perf boost for interpreted mypy (on self-check),
but only 1.5-2% for compiled one. Note this is _not_ a no-op, this is a
faster (and IMO actually more correct) inference passes logic.

Btw, for more generics-rich code the savings may be bigger, e.g. `mypy
-c "import colours"` becomes 30% faster (compiled).
---
 mypy/checkexpr.py | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 24f0c8c85d61..86f8d9410476 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1755,14 +1755,6 @@ def check_callable_call(
                         return AnyType(TypeOfAny.from_error), callee
                     seen_unpack = True
 
-        formal_to_actual = map_actuals_to_formals(
-            arg_kinds,
-            arg_names,
-            callee.arg_kinds,
-            callee.arg_names,
-            lambda i: self.accept(args[i]),
-        )
-
         # This is tricky: return type may contain its own type variables, like in
         # def [S] (S) -> def [T] (T) -> tuple[S, T], so we need to update their ids
         # to avoid possible id clashes if this call itself appears in a generic
@@ -1773,27 +1765,29 @@ def check_callable_call(
             freeze_all_type_vars(fresh_ret_type)
             callee = callee.copy_modified(ret_type=fresh_ret_type)
 
+        if callee.is_generic():
+            callee = freshen_function_type_vars(callee)
+            callee = self.infer_function_type_arguments_using_context(callee, context)
+
+        formal_to_actual = map_actuals_to_formals(
+            arg_kinds,
+            arg_names,
+            callee.arg_kinds,
+            callee.arg_names,
+            lambda i: self.accept(args[i]),
+        )
+
         if callee.is_generic():
             need_refresh = any(
                 isinstance(v, (ParamSpecType, TypeVarTupleType)) for v in callee.variables
             )
-            callee = freshen_function_type_vars(callee)
-            callee = self.infer_function_type_arguments_using_context(callee, context)
-            if need_refresh:
-                # Argument kinds etc. may have changed due to
-                # ParamSpec or TypeVarTuple variables being replaced with an arbitrary
-                # number of arguments; recalculate actual-to-formal map
-                formal_to_actual = map_actuals_to_formals(
-                    arg_kinds,
-                    arg_names,
-                    callee.arg_kinds,
-                    callee.arg_names,
-                    lambda i: self.accept(args[i]),
-                )
             callee = self.infer_function_type_arguments(
                 callee, args, arg_kinds, arg_names, formal_to_actual, need_refresh, context
             )
             if need_refresh:
+                # Argument kinds etc. may have changed due to
+                # ParamSpec or TypeVarTuple variables being replaced with an arbitrary
+                # number of arguments; recalculate actual-to-formal map
                 formal_to_actual = map_actuals_to_formals(
                     arg_kinds,
                     arg_names,
@@ -2258,6 +2252,11 @@ def infer_function_type_arguments_pass2(
             if isinstance(arg, (NoneType, UninhabitedType)) or has_erased_component(arg):
                 inferred_args[i] = None
         callee_type = self.apply_generic_arguments(callee_type, inferred_args, context)
+
+        if not callee_type.is_generic():
+            # Fast path, second pass can't give new information.
+            return callee_type, []
+
         if need_refresh:
             formal_to_actual = map_actuals_to_formals(
                 arg_kinds,

From afcf6b227d96cdc2b8d07f62108cacc20ca42ece Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 26 Jul 2025 03:21:13 +0200
Subject: [PATCH 1482/1617] docs: update cython and setuptools RTD URLs
 (#19512)

Fixes docs build failure discovered in #19510.

Updated setuptools URL too because previous CI runs say that

> intersphinx inventory has moved:
https://setuptools.readthedocs.io/en/latest/objects.inv ->
https://setuptools.pypa.io/en/latest/objects.inv
---
 docs/source/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/source/conf.py b/docs/source/conf.py
index 79a5c0619615..02caa44dce11 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -278,9 +278,9 @@
 intersphinx_mapping = {
     "python": ("https://docs.python.org/3", None),
     "attrs": ("https://www.attrs.org/en/stable/", None),
-    "cython": ("https://docs.cython.org/en/latest", None),
+    "cython": ("https://cython.readthedocs.io/en/stable", None),
     "monkeytype": ("https://monkeytype.readthedocs.io/en/latest", None),
-    "setuptools": ("https://setuptools.readthedocs.io/en/latest", None),
+    "setuptools": ("https://setuptools.pypa.io/en/latest", None),
 }
 
 

From 31adfb416e42ec4de65e823cce2774595b1f70e9 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 27 Jul 2025 00:29:55 +0100
Subject: [PATCH 1483/1617] Avoid duplicate visit in check_boolean_op()
 (#19515)

Surprisingly, this simple change gives 0.5-1% perf boost on self-check
(for some reasons this is quite hot function).
---
 mypy/checkexpr.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 86f8d9410476..a75ccf05612b 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -3468,7 +3468,7 @@ def visit_op_expr(self, e: OpExpr) -> Type:
             # It's actually a type expression X | Y.
             return self.accept(e.analyzed)
         if e.op == "and" or e.op == "or":
-            return self.check_boolean_op(e, e)
+            return self.check_boolean_op(e)
         if e.op == "*" and isinstance(e.left, ListExpr):
             # Expressions of form [...] * e get special type inference.
             return self.check_list_multiply(e)
@@ -4255,20 +4255,18 @@ def check_op(
                 context=context,
             )
 
-    def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
+    def check_boolean_op(self, e: OpExpr) -> Type:
         """Type check a boolean operation ('and' or 'or')."""
 
         # A boolean operation can evaluate to either of the operands.
 
-        # We use the current type context to guide the type inference of of
+        # We use the current type context to guide the type inference of
         # the left operand. We also use the left operand type to guide the type
         # inference of the right operand so that expressions such as
         # '[1] or []' are inferred correctly.
         ctx = self.type_context[-1]
         left_type = self.accept(e.left, ctx)
-        expanded_left_type = try_expanding_sum_type_to_union(
-            self.accept(e.left, ctx), "builtins.bool"
-        )
+        expanded_left_type = try_expanding_sum_type_to_union(left_type, "builtins.bool")
 
         assert e.op in ("and", "or")  # Checked by visit_op_expr
 

From c6b40df63ce0fecab6bced800ce778d99587c9b8 Mon Sep 17 00:00:00 2001
From: Omer Hadari 
Date: Sun, 27 Jul 2025 07:15:42 +0300
Subject: [PATCH 1484/1617] Prevent final reassignment in match case (#19496)

Fixes #19507

This PR adds a check to prevent reassignment of final variables in a
match statement.

Currently, the following passes without an error:
```Python
from typing import Final
FOO: Final = 8

a = 10

match a:
    case FOO:
        pass
print(FOO)  # FOO is reassigned, prints 10
```

MyPy already checks that the type of FOO isn't changed if used like
this. I added a check in the same place that makes sure it's not `Final`
either.

Since this tests the match syntax, I put the test where I did. If it's
not the appropriate place for it I will move it as instructed :)
---
 mypy/checker.py                     |  2 ++
 test-data/unit/check-python310.test | 10 ++++++++++
 2 files changed, 12 insertions(+)

diff --git a/mypy/checker.py b/mypy/checker.py
index 7579c36a97d0..e4ee0bf4d14c 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -5577,6 +5577,8 @@ def infer_variable_types_from_type_maps(
                 previous_type, _, _ = self.check_lvalue(expr)
                 if previous_type is not None:
                     already_exists = True
+                    if isinstance(expr.node, Var) and expr.node.is_final:
+                        self.msg.cant_assign_to_final(expr.name, False, expr)
                     if self.check_subtype(
                         typ,
                         previous_type,
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index bb8f038eb1eb..80fd64fa3569 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -2839,3 +2839,13 @@ match value_type:
     case _:
         assert_never(value_type)
 [builtins fixtures/tuple.pyi]
+
+[case testAssignmentToFinalInMatchCaseNotAllowed]
+from typing import Final
+
+FOO: Final[int] = 10
+
+val: int = 8
+match val:
+    case FOO:  # E: Cannot assign to final name "FOO"
+        pass

From a8d2f1334b48dfbee5fe04d6bd1f0b145a13d9d9 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 27 Jul 2025 22:49:16 +0100
Subject: [PATCH 1485/1617] Cache type_object_type() (#19514)

This gives almost 4% performance boost (Python 3.12, compiled). Note
there is an old bug in `type_object_type()`, we treat not ready types as
`Any` without deferring, I disable caching in this case.

Unfortunately, using this in fine-grained mode is tricky, essentially I
have three options:
* Use some horrible hacks to invalidate cache when needed
* Add (expensive) class target dependency from `__init__`/`__new__`
* Only allow constructor caching during initial load, but disable it in
fine-grained increments

I decided to choose the last option. I think it has the best balance
complexity/benefits.
---
 mypy/checker.py        |  7 ++++++-
 mypy/checker_shared.py |  1 +
 mypy/nodes.py          |  6 ++++++
 mypy/semanal_infer.py  |  3 +++
 mypy/server/update.py  |  6 ++++--
 mypy/typeops.py        | 40 +++++++++++++++++++++++++++++++++++-----
 6 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index e4ee0bf4d14c..24076fca6710 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -397,6 +397,7 @@ def __init__(
         self.is_stub = tree.is_stub
         self.is_typeshed_stub = tree.is_typeshed_file(options)
         self.inferred_attribute_types = None
+        self.allow_constructor_cache = True
 
         # If True, process function definitions. If False, don't. This is used
         # for processing module top levels in fine-grained incremental mode.
@@ -500,12 +501,16 @@ def check_first_pass(self) -> None:
                         )
 
     def check_second_pass(
-        self, todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None
+        self,
+        todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None,
+        *,
+        allow_constructor_cache: bool = True,
     ) -> bool:
         """Run second or following pass of type checking.
 
         This goes through deferred nodes, returning True if there were any.
         """
+        self.allow_constructor_cache = allow_constructor_cache
         self.recurse_into_functions = True
         with state.strict_optional_set(self.options.strict_optional), checker_state.set(self):
             if not todo and not self.deferred_nodes:
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index 65cec41d5202..7a5e9cb52c70 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -137,6 +137,7 @@ class TypeCheckerSharedApi(CheckerPluginInterface):
     module_refs: set[str]
     scope: CheckerScope
     checking_missing_await: bool
+    allow_constructor_cache: bool
 
     @property
     @abstractmethod
diff --git a/mypy/nodes.py b/mypy/nodes.py
index fc2656ce2130..921620866a06 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3022,6 +3022,7 @@ class is generic then it will be a type constructor of higher kind.
         "dataclass_transform_spec",
         "is_type_check_only",
         "deprecated",
+        "type_object_type",
     )
 
     _fullname: str  # Fully qualified name
@@ -3178,6 +3179,10 @@ class is generic then it will be a type constructor of higher kind.
     # The type's deprecation message (in case it is deprecated)
     deprecated: str | None
 
+    # Cached value of class constructor type, i.e. the type of class object when it
+    # appears in runtime context.
+    type_object_type: mypy.types.FunctionLike | None
+
     FLAGS: Final = [
         "is_abstract",
         "is_enum",
@@ -3236,6 +3241,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None
         self.dataclass_transform_spec = None
         self.is_type_check_only = False
         self.deprecated = None
+        self.type_object_type = None
 
     def add_type_vars(self) -> None:
         self.has_type_var_tuple_type = False
diff --git a/mypy/semanal_infer.py b/mypy/semanal_infer.py
index a146b56dc2d3..89a073cdad47 100644
--- a/mypy/semanal_infer.py
+++ b/mypy/semanal_infer.py
@@ -31,6 +31,7 @@ def infer_decorator_signature_if_simple(
     """
     if dec.var.is_property:
         # Decorators are expected to have a callable type (it's a little odd).
+        # TODO: this may result in wrong type if @property is applied to decorated method.
         if dec.func.type is None:
             dec.var.type = CallableType(
                 [AnyType(TypeOfAny.special_form)],
@@ -47,6 +48,8 @@ def infer_decorator_signature_if_simple(
     for expr in dec.decorators:
         preserve_type = False
         if isinstance(expr, RefExpr) and isinstance(expr.node, FuncDef):
+            if expr.fullname == "typing.no_type_check":
+                return
             if expr.node.type and is_identity_signature(expr.node.type):
                 preserve_type = True
         if not preserve_type:
diff --git a/mypy/server/update.py b/mypy/server/update.py
index ea336154ae56..839090ca45ac 100644
--- a/mypy/server/update.py
+++ b/mypy/server/update.py
@@ -1025,10 +1025,12 @@ def key(node: FineGrainedDeferredNode) -> int:
     # We seem to need additional passes in fine-grained incremental mode.
     checker.pass_num = 0
     checker.last_pass = 3
-    more = checker.check_second_pass(nodes)
+    # It is tricky to reliably invalidate constructor cache in fine-grained increments.
+    # See PR 19514 description for details.
+    more = checker.check_second_pass(nodes, allow_constructor_cache=False)
     while more:
         more = False
-        if graph[module_id].type_checker().check_second_pass():
+        if graph[module_id].type_checker().check_second_pass(allow_constructor_cache=False):
             more = True
 
     if manager.options.export_types:
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 9aa08b40a991..1c22a1711944 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -11,6 +11,7 @@
 from collections.abc import Iterable, Sequence
 from typing import Any, Callable, TypeVar, cast
 
+from mypy.checker_state import checker_state
 from mypy.copytype import copy_type
 from mypy.expandtype import expand_type, expand_type_by_instance
 from mypy.maptype import map_instance_to_supertype
@@ -145,6 +146,15 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
     where ... are argument types for the __init__/__new__ method (without the self
     argument). Also, the fallback type will be 'type' instead of 'function'.
     """
+    allow_cache = (
+        checker_state.type_checker is not None
+        and checker_state.type_checker.allow_constructor_cache
+    )
+
+    if info.type_object_type is not None:
+        if allow_cache:
+            return info.type_object_type
+        info.type_object_type = None
 
     # We take the type from whichever of __init__ and __new__ is first
     # in the MRO, preferring __init__ if there is a tie.
@@ -167,7 +177,15 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
     init_index = info.mro.index(init_method.node.info)
     new_index = info.mro.index(new_method.node.info)
 
-    fallback = info.metaclass_type or named_type("builtins.type")
+    if info.metaclass_type is not None:
+        fallback = info.metaclass_type
+    elif checker_state.type_checker:
+        # Prefer direct call when it is available. It is faster, and,
+        # unfortunately, some callers provide bogus callback.
+        fallback = checker_state.type_checker.named_type("builtins.type")
+    else:
+        fallback = named_type("builtins.type")
+
     if init_index < new_index:
         method: FuncBase | Decorator = init_method.node
         is_new = False
@@ -189,7 +207,10 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
                     is_bound=True,
                     fallback=named_type("builtins.function"),
                 )
-                return class_callable(sig, info, fallback, None, is_new=False)
+                result: FunctionLike = class_callable(sig, info, fallback, None, is_new=False)
+                if allow_cache:
+                    info.type_object_type = result
+                return result
 
         # Otherwise prefer __init__ in a tie. It isn't clear that this
         # is the right thing, but __new__ caused problems with
@@ -199,12 +220,19 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
     # Construct callable type based on signature of __init__. Adjust
     # return type and insert type arguments.
     if isinstance(method, FuncBase):
+        if isinstance(method, OverloadedFuncDef) and not method.type:
+            # Do not cache if the type is not ready. Same logic for decorators is
+            # achieved in early return above because is_valid_constructor() is False.
+            allow_cache = False
         t = function_type(method, fallback)
     else:
         assert isinstance(method.type, ProperType)
         assert isinstance(method.type, FunctionLike)  # is_valid_constructor() ensures this
         t = method.type
-    return type_object_type_from_function(t, info, method.info, fallback, is_new)
+    result = type_object_type_from_function(t, info, method.info, fallback, is_new)
+    if allow_cache:
+        info.type_object_type = result
+    return result
 
 
 def is_valid_constructor(n: SymbolNode | None) -> bool:
@@ -865,8 +893,8 @@ def function_type(func: FuncBase, fallback: Instance) -> FunctionLike:
         if isinstance(func, FuncItem):
             return callable_type(func, fallback)
         else:
-            # Broken overloads can have self.type set to None.
-            # TODO: should we instead always set the type in semantic analyzer?
+            # Either a broken overload, or decorated overload type is not ready.
+            # TODO: make sure the caller defers if possible.
             assert isinstance(func, OverloadedFuncDef)
             any_type = AnyType(TypeOfAny.from_error)
             dummy = CallableType(
@@ -1254,6 +1282,8 @@ def get_protocol_member(
     if member == "__call__" and class_obj:
         # Special case: class objects always have __call__ that is just the constructor.
 
+        # TODO: this is wrong, it creates callables that are not recognized as type objects.
+        # Long-term, we should probably get rid of this callback argument altogether.
         def named_type(fullname: str) -> Instance:
             return Instance(left.type.mro[-1], [])
 

From 0c1f1044fc5e7c6b36f9c0d16169837a3e5a2dba Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 28 Jul 2025 10:24:12 +0100
Subject: [PATCH 1486/1617] Various minor docstring and comment updates
 (#19519)

Mostly grammar improvements.
---
 mypy/build.py               | 14 +++++++-------
 mypyc/build.py              | 10 +++++-----
 mypyc/codegen/emitmodule.py | 29 ++++++++++++++---------------
 3 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/mypy/build.py b/mypy/build.py
index 355ba861385e..71575de9d877 100644
--- a/mypy/build.py
+++ b/mypy/build.py
@@ -194,7 +194,7 @@ def default_flush_errors(
         result.errors = messages
         return result
     except CompileError as e:
-        # CompileErrors raised from an errors object carry all of the
+        # CompileErrors raised from an errors object carry all the
         # messages that have not been reported out by error streaming.
         # Patch it up to contain either none or all none of the messages,
         # depending on whether we are flushing errors.
@@ -802,11 +802,11 @@ def correct_rel_imp(imp: ImportFrom | ImportAll) -> str:
                             res.append((pri, sub_id, imp.line))
                         else:
                             all_are_submodules = False
-                    # Add cur_id as a dependency, even if all of the
+                    # Add cur_id as a dependency, even if all the
                     # imports are submodules. Processing import from will try
                     # to look through cur_id, so we should depend on it.
-                    # As a workaround for for some bugs in cycle handling (#4498),
-                    # if all of the imports are submodules, do the import at a lower
+                    # As a workaround for some bugs in cycle handling (#4498),
+                    # if all the imports are submodules, do the import at a lower
                     # priority.
                     pri = import_priority(imp, PRI_HIGH if not all_are_submodules else PRI_LOW)
                     res.append((pri, cur_id, imp.line))
@@ -929,7 +929,7 @@ def write_deps_cache(
 ) -> None:
     """Write cache files for fine-grained dependencies.
 
-    Serialize fine-grained dependencies map for fine grained mode.
+    Serialize fine-grained dependencies map for fine-grained mode.
 
     Dependencies on some module 'm' is stored in the dependency cache
     file m.deps.json.  This entails some spooky action at a distance:
@@ -943,7 +943,7 @@ def write_deps_cache(
     fine-grained dependencies in a global cache file:
      * We take a snapshot of current sources to later check consistency
        between the fine-grained dependency cache and module cache metadata
-     * We store the mtime of all of the dependency files to verify they
+     * We store the mtime of all the dependency files to verify they
        haven't changed
     """
     metastore = manager.metastore
@@ -1111,7 +1111,7 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta]
     if deps_meta is None:
         return None
     meta_snapshot = deps_meta["snapshot"]
-    # Take a snapshot of the source hashes from all of the metas we found.
+    # Take a snapshot of the source hashes from all the metas we found.
     # (Including the ones we rejected because they were out of date.)
     # We use this to verify that they match up with the proto_deps.
     current_meta_snapshot = {
diff --git a/mypyc/build.py b/mypyc/build.py
index 8ddbf4d22a27..b7d3e1b25366 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -270,12 +270,12 @@ def build_using_shared_lib(
 ) -> list[Extension]:
     """Produce the list of extension modules when a shared library is needed.
 
-    This creates one shared library extension module that all of the
-    others import and then one shim extension module for each
-    module in the build, that simply calls an initialization function
+    This creates one shared library extension module that all the
+    others import, and one shim extension module for each
+    module in the build. Each shim simply calls an initialization function
     in the shared library.
 
-    The shared library (which lib_name is the name of) is a python
+    The shared library (which lib_name is the name of) is a Python
     extension module that exports the real initialization functions in
     Capsules stored in module attributes.
     """
@@ -511,7 +511,7 @@ def mypycify(
         separate: Should compiled modules be placed in separate extension modules.
                   If False, all modules are placed in a single shared library.
                   If True, every module is placed in its own library.
-                  Otherwise separate should be a list of
+                  Otherwise, separate should be a list of
                   (file name list, optional shared library name) pairs specifying
                   groups of files that should be placed in the same shared library
                   (while all other modules will be placed in its own library).
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 7037409ff40b..13a3727cd188 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -1,7 +1,7 @@
 """Generate C code for a Python C extension module from Python source code."""
 
 # FIXME: Basically nothing in this file operates on the level of a
-# single module and it should be renamed.
+#        single module and it should be renamed.
 
 from __future__ import annotations
 
@@ -71,7 +71,7 @@
 from mypyc.transform.spill import insert_spills
 from mypyc.transform.uninit import insert_uninit_checks
 
-# All of the modules being compiled are divided into "groups". A group
+# All the modules being compiled are divided into "groups". A group
 # is a set of modules that are placed into the same shared library.
 # Two common configurations are that every module is placed in a group
 # by itself (fully separate compilation) and that every module is
@@ -164,7 +164,7 @@ def report_config_data(self, ctx: ReportConfigContext) -> tuple[str | None, list
         if hash_digest(meta_json) != ir_data["meta_hash"]:
             return None
 
-        # Check that all of the source files are present and as
+        # Check that all the source files are present and as
         # expected. The main situation where this would come up is the
         # user deleting the build directory without deleting
         # .mypy_cache, which we should handle gracefully.
@@ -215,8 +215,8 @@ def compile_scc_to_ir(
 ) -> ModuleIRs:
     """Compile an SCC into ModuleIRs.
 
-    Any modules that this SCC depends on must have either compiled or
-    loaded from a cache into mapper.
+    Any modules that this SCC depends on must have either been compiled,
+    type checked, or loaded from a cache into mapper.
 
     Arguments:
         scc: The list of MypyFiles to compile
@@ -244,11 +244,11 @@ def compile_scc_to_ir(
 
     for module in modules.values():
         for fn in module.functions:
-            # Insert uninit checks.
+            # Insert checks for uninitialized values.
             insert_uninit_checks(fn)
             # Insert exception handling.
             insert_exception_handling(fn)
-            # Insert refcount handling.
+            # Insert reference count handling.
             insert_ref_count_opcodes(fn)
 
             if fn in env_user_functions:
@@ -369,7 +369,7 @@ def write_cache(
         cache are in sync and refer to the same version of the code.
         This is particularly important if mypyc crashes/errors/is
         stopped after mypy has written its cache but before mypyc has.
-      * The hashes of all of the source file outputs for the group
+      * The hashes of all the source file outputs for the group
         the module is in. This is so that the module will be
         recompiled if the source outputs are missing.
     """
@@ -429,7 +429,7 @@ def compile_modules_to_c(
     Each shared library module provides, for each module in its group,
     a PyCapsule containing an initialization function.
     Additionally, it provides a capsule containing an export table of
-    pointers to all of the group's functions and static variables.
+    pointers to all the group's functions and static variables.
 
     Arguments:
         result: The BuildResult from the mypy front-end
@@ -504,7 +504,7 @@ def __init__(
 
         The code for a compilation group contains an internal and an
         external .h file, and then one .c if not in multi_file mode or
-        one .c file per module if in multi_file mode.)
+        one .c file per module if in multi_file mode.
 
         Arguments:
             modules: (name, ir) pairs for each module in the group
@@ -512,8 +512,7 @@ def __init__(
             group_name: The name of the group (or None if this is single-module compilation)
             group_map: A map of modules to their group names
             names: The name generator for the compilation
-            multi_file: Whether to put each module in its own source file regardless
-                        of group structure.
+            compiler_options: Mypyc specific options, including multi_file mode
         """
         self.modules = modules
         self.source_paths = source_paths
@@ -642,7 +641,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
             decls = ext_declarations if declaration.is_type else declarations
             if not declaration.is_type:
                 decls.emit_lines(f"extern {declaration.decl[0]}", *declaration.decl[1:])
-                # If there is a definition, emit it. Otherwise repeat the declaration
+                # If there is a definition, emit it. Otherwise, repeat the declaration
                 # (without an extern).
                 if declaration.defn:
                     emitter.emit_lines(*declaration.defn)
@@ -770,13 +769,13 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) ->
     def generate_shared_lib_init(self, emitter: Emitter) -> None:
         """Generate the init function for a shared library.
 
-        A shared library contains all of the actual code for a
+        A shared library contains all the actual code for a
         compilation group.
 
         The init function is responsible for creating Capsules that
         wrap pointers to the initialization function of all the real
         init functions for modules in this shared library as well as
-        the export table containing all of the exported functions and
+        the export table containing all the exported functions and
         values from all the modules.
 
         These capsules are stored in attributes of the shared library.

From 3a2b7888a7eb0543d572761464ab5295c585d0a9 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 28 Jul 2025 14:18:14 +0100
Subject: [PATCH 1487/1617] [mypyc] Only generate an export table if using
 separate compilation (#19521)

When not using separate compilation, the export table is not used. Also,
there's actually no simple and safe way to use it without separate
compilation, since we'd have to first ensure that the structure of the
export table is compatible, as otherwise the order of fields or the
types of the fields could be incompatible and cause segfaults and other
fun stuff.
---
 mypyc/codegen/emitmodule.py | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 13a3727cd188..047309ec71e3 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -651,7 +651,8 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
                 decls.emit_lines(*declaration.decl)
 
         if self.group_name:
-            self.generate_export_table(ext_declarations, emitter)
+            if self.compiler_options.separate:
+                self.generate_export_table(ext_declarations, emitter)
 
             self.generate_shared_lib_init(emitter)
 
@@ -808,20 +809,21 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None:
             "",
         )
 
-        emitter.emit_lines(
-            'capsule = PyCapsule_New(&exports, "{}.exports", NULL);'.format(
-                shared_lib_name(self.group_name)
-            ),
-            "if (!capsule) {",
-            "goto fail;",
-            "}",
-            'res = PyObject_SetAttrString(module, "exports", capsule);',
-            "Py_DECREF(capsule);",
-            "if (res < 0) {",
-            "goto fail;",
-            "}",
-            "",
-        )
+        if self.compiler_options.separate:
+            emitter.emit_lines(
+                'capsule = PyCapsule_New(&exports, "{}.exports", NULL);'.format(
+                    shared_lib_name(self.group_name)
+                ),
+                "if (!capsule) {",
+                "goto fail;",
+                "}",
+                'res = PyObject_SetAttrString(module, "exports", capsule);',
+                "Py_DECREF(capsule);",
+                "if (res < 0) {",
+                "goto fail;",
+                "}",
+                "",
+            )
 
         for mod in self.modules:
             name = exported_name(mod)

From e40c36c014710d76a13c90b5e40a828b92296f39 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 28 Jul 2025 17:17:48 +0100
Subject: [PATCH 1488/1617] [mypyc] Fix list.pop primitive on free-threaded
 builds (#19522)

The old implementation caused segfaults on free-threaded builds. Provide
a slower but working implementation for free-threaded builds. Also
improve test coverage, since it used to be spotty.

Tested on a manually compiled Python 3.14.0b4 free-threaded build on
macOS.
---
 mypyc/lib-rt/list_ops.c        | 36 ++++++++++++++++++++++++++++++++--
 mypyc/primitives/list_ops.py   |  2 +-
 mypyc/test-data/run-lists.test | 29 +++++++++++++++++++++------
 3 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c
index 31a0d5cec7d5..c611907fb601 100644
--- a/mypyc/lib-rt/list_ops.c
+++ b/mypyc/lib-rt/list_ops.c
@@ -235,19 +235,51 @@ void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value) {
     PyList_SET_ITEM(list, index, value);
 }
 
-PyObject *CPyList_PopLast(PyObject *obj)
+#ifdef Py_GIL_DISABLED
+// The original optimized list.pop implementation doesn't work on free-threaded
+// builds, so provide an alternative that is a bit slower but works.
+//
+// Note that this implementation isn't intended to be atomic.
+static inline PyObject *list_pop_index(PyObject *list, Py_ssize_t index) {
+    PyObject *item = PyList_GetItemRef(list, index);
+    if (item == NULL) {
+        return NULL;
+    }
+    if (PySequence_DelItem(list, index) < 0) {
+        Py_DECREF(item);
+        return NULL;
+    }
+    return item;
+}
+#endif
+
+PyObject *CPyList_PopLast(PyObject *list)
 {
+#ifdef Py_GIL_DISABLED
+    // The other implementation causes segfaults on a free-threaded Python 3.14b4 build.
+    Py_ssize_t index = PyList_GET_SIZE(list) - 1;
+    return list_pop_index(list, index);
+#else
     // I tried a specalized version of pop_impl for just removing the
     // last element and it wasn't any faster in microbenchmarks than
     // the generic one so I ditched it.
-    return list_pop_impl((PyListObject *)obj, -1);
+    return list_pop_impl((PyListObject *)list, -1);
+#endif
 }
 
 PyObject *CPyList_Pop(PyObject *obj, CPyTagged index)
 {
     if (CPyTagged_CheckShort(index)) {
         Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
+#ifdef Py_GIL_DISABLED
+        // We must use a slower implementation on free-threaded builds.
+        if (n < 0) {
+            n += PyList_GET_SIZE(obj);
+        }
+        return list_pop_index(obj, n);
+#else
         return list_pop_impl((PyListObject *)obj, n);
+#endif
     } else {
         PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG);
         return NULL;
diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py
index 516d9e1a4e02..b9d20a25bea3 100644
--- a/mypyc/primitives/list_ops.py
+++ b/mypyc/primitives/list_ops.py
@@ -219,7 +219,7 @@
 )
 
 # list.pop(index)
-list_pop = method_op(
+method_op(
     name="pop",
     arg_types=[list_rprimitive, int_rprimitive],
     return_type=object_rprimitive,
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 03d5741b9eca..54bcc0384604 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -150,7 +150,9 @@ print(primes(13))
 \[0, 0, 1, 1]
 \[0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1]
 
-[case testListBuild]
+[case testListPrimitives]
+from testutil import assertRaises
+
 def test_list_build() -> None:
     # Currently LIST_BUILDING_EXPANSION_THRESHOLD equals to 10
     # long list built by list_build_op
@@ -169,9 +171,6 @@ def test_list_build() -> None:
     l3.append('a')
     assert l3 == ['a']
 
-[case testListPrims]
-from typing import List
-
 def test_append() -> None:
     l = [1, 2]
     l.append(10)
@@ -189,10 +188,28 @@ def test_pop_last() -> None:
 
 def test_pop_index() -> None:
     l = [1, 2, 10, 3]
-    l.pop(2)
+    assert l.pop(2) == 10
     assert l == [1, 2, 3]
-    l.pop(-2)
+    assert l.pop(-2) == 2
     assert l == [1, 3]
+    assert l.pop(-2) == 1
+    assert l.pop(0) == 3
+    assert l == []
+    l = [int() + 1000, int() + 1001, int() + 1002]
+    assert l.pop(0) == 1000
+    assert l.pop(-1) == 1002
+    assert l == [1001]
+
+def test_pop_index_errors() -> None:
+    l = [int() + 1000]
+    with assertRaises(IndexError):
+        l.pop(1)
+    with assertRaises(IndexError):
+        l.pop(-2)
+    with assertRaises(OverflowError):
+        l.pop(1 << 100)
+    with assertRaises(OverflowError):
+        l.pop(-(1 << 100))
 
 def test_count() -> None:
     l = [1, 3]

From bd94bcbb0a548ec480b78bcf71a955a45dc9d77c Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 28 Jul 2025 18:17:58 +0100
Subject: [PATCH 1489/1617] Try simple-minded call expression cache (#19505)

This gives a modest 1% improvement on self-check (compiled), but it
gives almost 40% on `mypy -c "import colour"`. Some comments:
* I only cache `CallExpr`, `ListExpr`, and `TupleExpr`, this is not very
principled, I found this as a best balance between rare cases like
`colour`, and more common cases like self-check.
* Caching is fragile within lambdas, so I simply disable it, it rarely
matters anyway.
* I cache both messages and the type map, surprisingly the latter only
affects couple test cases, but I still do this generally for peace of
mind.
* It looks like there are only three things that require cache
invalidation: binder, partial types, and deferrals.

In general, this is a bit scary (as this a major change), but also perf
improvements for slow libraries are very tempting.
---
 mypy/binder.py                             |  6 +++
 mypy/checker.py                            |  7 +++-
 mypy/checkexpr.py                          | 45 +++++++++++++++++++++-
 mypy/errors.py                             |  6 ++-
 test-data/unit/check-overloading.test      | 23 +++++++++++
 test-data/unit/fixtures/isinstancelist.pyi |  2 +
 6 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/mypy/binder.py b/mypy/binder.py
index d3482d1dad4f..2ae58dad1fe0 100644
--- a/mypy/binder.py
+++ b/mypy/binder.py
@@ -138,6 +138,10 @@ def __init__(self, options: Options) -> None:
         # flexible inference of variable types (--allow-redefinition-new).
         self.bind_all = options.allow_redefinition_new
 
+        # This tracks any externally visible changes in binder to invalidate
+        # expression caches when needed.
+        self.version = 0
+
     def _get_id(self) -> int:
         self.next_id += 1
         return self.next_id
@@ -158,6 +162,7 @@ def push_frame(self, conditional_frame: bool = False) -> Frame:
         return f
 
     def _put(self, key: Key, type: Type, from_assignment: bool, index: int = -1) -> None:
+        self.version += 1
         self.frames[index].types[key] = CurrentType(type, from_assignment)
 
     def _get(self, key: Key, index: int = -1) -> CurrentType | None:
@@ -185,6 +190,7 @@ def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> N
         self._put(key, typ, from_assignment)
 
     def unreachable(self) -> None:
+        self.version += 1
         self.frames[-1].unreachable = True
 
     def suppress_unreachable_warnings(self) -> None:
diff --git a/mypy/checker.py b/mypy/checker.py
index 24076fca6710..97c1e10259c9 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -449,7 +449,6 @@ def reset(self) -> None:
         self.binder = ConditionalTypeBinder(self.options)
         self._type_maps[1:] = []
         self._type_maps[0].clear()
-        self.temp_type_map = None
         self.expr_checker.reset()
         self.deferred_nodes = []
         self.partial_types = []
@@ -3024,6 +3023,8 @@ def visit_block(self, b: Block) -> None:
                     break
             else:
                 self.accept(s)
+                # Clear expression cache after each statement to avoid unlimited growth.
+                self.expr_checker.expr_cache.clear()
 
     def should_report_unreachable_issues(self) -> bool:
         return (
@@ -4005,7 +4006,7 @@ def check_multi_assignment_from_union(
                 for t, lv in zip(transposed, self.flatten_lvalues(lvalues)):
                     # We can access _type_maps directly since temporary type maps are
                     # only created within expressions.
-                    t.append(self._type_maps[0].pop(lv, AnyType(TypeOfAny.special_form)))
+                    t.append(self._type_maps[-1].pop(lv, AnyType(TypeOfAny.special_form)))
         union_types = tuple(make_simplified_union(col) for col in transposed)
         for expr, items in assignments.items():
             # Bind a union of types collected in 'assignments' to every expression.
@@ -4664,6 +4665,8 @@ def replace_partial_type(
     ) -> None:
         """Replace the partial type of var with a non-partial type."""
         var.type = new_type
+        # Updating a partial type should invalidate expression caches.
+        self.binder.version += 1
         del partial_types[var]
         if self.options.allow_redefinition_new:
             # When using --allow-redefinition-new, binder tracks all types of
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index a75ccf05612b..a8afebbd9923 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -19,7 +19,7 @@
 from mypy.checkmember import analyze_member_access, has_operator
 from mypy.checkstrformat import StringFormatterChecker
 from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
-from mypy.errors import ErrorWatcher, report_internal_error
+from mypy.errors import ErrorInfo, ErrorWatcher, report_internal_error
 from mypy.expandtype import (
     expand_type,
     expand_type_by_instance,
@@ -355,9 +355,15 @@ def __init__(
         type_state.infer_polymorphic = not self.chk.options.old_type_inference
 
         self._arg_infer_context_cache = None
+        self.expr_cache: dict[
+            tuple[Expression, Type | None],
+            tuple[int, Type, list[ErrorInfo], dict[Expression, Type]],
+        ] = {}
+        self.in_lambda_expr = False
 
     def reset(self) -> None:
         self.resolved_type = {}
+        self.expr_cache.clear()
 
     def visit_name_expr(self, e: NameExpr) -> Type:
         """Type check a name expression.
@@ -5402,6 +5408,8 @@ def find_typeddict_context(
 
     def visit_lambda_expr(self, e: LambdaExpr) -> Type:
         """Type check lambda expression."""
+        old_in_lambda = self.in_lambda_expr
+        self.in_lambda_expr = True
         self.chk.check_default_args(e, body_is_trivial=False)
         inferred_type, type_override = self.infer_lambda_type_using_context(e)
         if not inferred_type:
@@ -5422,6 +5430,7 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
                 ret_type = self.accept(e.expr(), allow_none_return=True)
             fallback = self.named_type("builtins.function")
             self.chk.return_types.pop()
+            self.in_lambda_expr = old_in_lambda
             return callable_type(e, fallback, ret_type)
         else:
             # Type context available.
@@ -5434,6 +5443,7 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
                     self.accept(e.expr(), allow_none_return=True)
             ret_type = self.chk.lookup_type(e.expr())
             self.chk.return_types.pop()
+            self.in_lambda_expr = old_in_lambda
             return replace_callable_return_type(inferred_type, ret_type)
 
     def infer_lambda_type_using_context(
@@ -5978,6 +5988,24 @@ def accept(
                 typ = self.visit_conditional_expr(node, allow_none_return=True)
             elif allow_none_return and isinstance(node, AwaitExpr):
                 typ = self.visit_await_expr(node, allow_none_return=True)
+            # Deeply nested generic calls can deteriorate performance dramatically.
+            # Although in most cases caching makes little difference, in worst case
+            # it avoids exponential complexity.
+            # We cannot use cache inside lambdas, because they skip immediate type
+            # context, and use enclosing one, see infer_lambda_type_using_context().
+            # TODO: consider using cache for more expression kinds.
+            elif isinstance(node, (CallExpr, ListExpr, TupleExpr)) and not (
+                self.in_lambda_expr or self.chk.current_node_deferred
+            ):
+                if (node, type_context) in self.expr_cache:
+                    binder_version, typ, messages, type_map = self.expr_cache[(node, type_context)]
+                    if binder_version == self.chk.binder.version:
+                        self.chk.store_types(type_map)
+                        self.msg.add_errors(messages)
+                    else:
+                        typ = self.accept_maybe_cache(node, type_context=type_context)
+                else:
+                    typ = self.accept_maybe_cache(node, type_context=type_context)
             else:
                 typ = node.accept(self)
         except Exception as err:
@@ -6008,6 +6036,21 @@ def accept(
             self.in_expression = False
         return result
 
+    def accept_maybe_cache(self, node: Expression, type_context: Type | None = None) -> Type:
+        binder_version = self.chk.binder.version
+        # Micro-optimization: inline local_type_map() as it is somewhat slow in mypyc.
+        type_map: dict[Expression, Type] = {}
+        self.chk._type_maps.append(type_map)
+        with self.msg.filter_errors(filter_errors=True, save_filtered_errors=True) as msg:
+            typ = node.accept(self)
+        messages = msg.filtered_errors()
+        if binder_version == self.chk.binder.version and not self.chk.current_node_deferred:
+            self.expr_cache[(node, type_context)] = (binder_version, typ, messages, type_map)
+        self.chk._type_maps.pop()
+        self.chk.store_types(type_map)
+        self.msg.add_errors(messages)
+        return typ
+
     def named_type(self, name: str) -> Instance:
         """Return an instance type with type given by the name and no type
         arguments. Alias for TypeChecker.named_type.
diff --git a/mypy/errors.py b/mypy/errors.py
index 5c135146bcb7..d75c1c62a1ed 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -390,7 +390,7 @@ class Errors:
     # in some cases to avoid reporting huge numbers of errors.
     seen_import_error = False
 
-    _watchers: list[ErrorWatcher] = []
+    _watchers: list[ErrorWatcher]
 
     def __init__(
         self,
@@ -421,6 +421,7 @@ def initialize(self) -> None:
         self.scope = None
         self.target_module = None
         self.seen_import_error = False
+        self._watchers = []
 
     def reset(self) -> None:
         self.initialize()
@@ -931,7 +932,8 @@ def prefer_simple_messages(self) -> bool:
         if self.file in self.ignored_files:
             # Errors ignored, so no point generating fancy messages
             return True
-        for _watcher in self._watchers:
+        if self._watchers:
+            _watcher = self._watchers[-1]
             if _watcher._filter is True and _watcher._filtered is None:
                 # Errors are filtered
                 return True
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index 0f0fc8747223..22221416f151 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -6801,3 +6801,26 @@ class D(Generic[T]):
 a: D[str]  # E: Type argument "str" of "D" must be a subtype of "C"
 reveal_type(a.f(1))  # N: Revealed type is "builtins.int"
 reveal_type(a.f("x"))  # N: Revealed type is "builtins.str"
+
+[case testMultiAssignFromUnionInOverloadCached]
+from typing import Iterable, overload, Union, Optional
+
+@overload
+def always_bytes(str_or_bytes: None) -> None: ...
+@overload
+def always_bytes(str_or_bytes: Union[str, bytes]) -> bytes: ...
+def always_bytes(str_or_bytes: Union[None, str, bytes]) -> Optional[bytes]:
+    pass
+
+class Headers:
+    def __init__(self, iter: Iterable[tuple[bytes, bytes]]) -> None: ...
+
+headers: Union[Headers, dict[Union[str, bytes], Union[str, bytes]], Iterable[tuple[bytes, bytes]]]
+
+if isinstance(headers, dict):
+    headers = Headers(
+        (always_bytes(k), always_bytes(v)) for k, v in headers.items()
+    )
+
+reveal_type(headers)  # N: Revealed type is "Union[__main__.Headers, typing.Iterable[tuple[builtins.bytes, builtins.bytes]]]"
+[builtins fixtures/isinstancelist.pyi]
diff --git a/test-data/unit/fixtures/isinstancelist.pyi b/test-data/unit/fixtures/isinstancelist.pyi
index 0ee5258ff74b..2a43606f361a 100644
--- a/test-data/unit/fixtures/isinstancelist.pyi
+++ b/test-data/unit/fixtures/isinstancelist.pyi
@@ -26,6 +26,7 @@ class bool(int): pass
 class str:
     def __add__(self, x: str) -> str: pass
     def __getitem__(self, x: int) -> str: pass
+class bytes: pass
 
 T = TypeVar('T')
 KT = TypeVar('KT')
@@ -52,6 +53,7 @@ class dict(Mapping[KT, VT]):
     def __setitem__(self, k: KT, v: VT) -> None: pass
     def __iter__(self) -> Iterator[KT]: pass
     def update(self, a: Mapping[KT, VT]) -> None: pass
+    def items(self) -> Iterable[Tuple[KT, VT]]: pass
 
 class set(Generic[T]):
     def __iter__(self) -> Iterator[T]: pass

From ad76e1628e2d4fe52d89a80d51890bf84afad38f Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 28 Jul 2025 20:44:07 +0200
Subject: [PATCH 1490/1617] Move self argument checks to a later phase - after
 decorator application, if any (#19490)

Fixes #19392.
Fixes #18989.
Fixes #18720.
Fixes #13434 (correct support for `staticmethod` wrappers, but not for
equivalent `classmethod`s reported in #18968).

Deferring this check in presence of decorators allows decorators that
perform non-trivial transformations (such as making methods from
non-methods and vice versa).
---
 mypy/checker.py                    | 104 ++++++++-----
 mypy/semanal.py                    |   7 +-
 test-data/unit/check-classes.test  | 230 ++++++++++++++++++++++++++++-
 test-data/unit/fine-grained.test   |   2 +-
 test-data/unit/semanal-errors.test |  16 --
 5 files changed, 300 insertions(+), 59 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 97c1e10259c9..f201a767a860 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1373,49 +1373,19 @@ def check_func_def(
                         )
 
                 # Store argument types.
+                found_self = False
+                if isinstance(defn, FuncDef) and not defn.is_decorated:
+                    found_self = self.require_correct_self_argument(typ, defn)
                 for i in range(len(typ.arg_types)):
                     arg_type = typ.arg_types[i]
-                    if (
-                        isinstance(defn, FuncDef)
-                        and ref_type is not None
-                        and i == 0
-                        and defn.has_self_or_cls_argument
-                        and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]
-                    ):
-                        if defn.is_class or defn.name == "__new__":
-                            ref_type = mypy.types.TypeType.make_normalized(ref_type)
-                        if not is_same_type(arg_type, ref_type):
-                            # This level of erasure matches the one in checkmember.check_self_arg(),
-                            # better keep these two checks consistent.
-                            erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
-                            if not is_subtype(ref_type, erased, ignore_type_params=True):
-                                if (
-                                    isinstance(erased, Instance)
-                                    and erased.type.is_protocol
-                                    or isinstance(erased, TypeType)
-                                    and isinstance(erased.item, Instance)
-                                    and erased.item.type.is_protocol
-                                ):
-                                    # We allow the explicit self-type to be not a supertype of
-                                    # the current class if it is a protocol. For such cases
-                                    # the consistency check will be performed at call sites.
-                                    msg = None
-                                elif typ.arg_names[i] in {"self", "cls"}:
-                                    msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
-                                        erased.str_with_options(self.options),
-                                        ref_type.str_with_options(self.options),
-                                    )
-                                else:
-                                    msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
-                                if msg:
-                                    self.fail(msg, defn)
-                    elif isinstance(arg_type, TypeVarType):
+                    if isinstance(arg_type, TypeVarType):
                         # Refuse covariant parameter type variables
                         # TODO: check recursively for inner type variables
                         if (
                             arg_type.variance == COVARIANT
                             and defn.name not in ("__init__", "__new__", "__post_init__")
                             and not is_private(defn.name)  # private methods are not inherited
+                            and (i != 0 or not found_self)
                         ):
                             ctx: Context = arg_type
                             if ctx.line < 0:
@@ -1565,6 +1535,69 @@ def check_func_def(
 
             self.binder = old_binder
 
+    def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool:
+        func = get_proper_type(func)
+        if not isinstance(func, CallableType):
+            return False
+
+        # Do not report errors for untyped methods in classes nested in untyped funcs.
+        if not (
+            self.options.check_untyped_defs
+            or len(self.dynamic_funcs) < 2
+            or not self.dynamic_funcs[-2]
+            or not defn.is_dynamic()
+        ):
+            return bool(func.arg_types)
+
+        with self.scope.push_function(defn):
+            # We temporary push the definition to get the self type as
+            # visible from *inside* of this function/method.
+            ref_type: Type | None = self.scope.active_self_type()
+            if ref_type is None:
+                return False
+
+        if not defn.has_self_or_cls_argument or (
+            func.arg_kinds and func.arg_kinds[0] in [nodes.ARG_STAR, nodes.ARG_STAR2]
+        ):
+            return False
+
+        if not func.arg_types:
+            self.fail(
+                'Method must have at least one argument. Did you forget the "self" argument?', defn
+            )
+            return False
+
+        arg_type = func.arg_types[0]
+        if defn.is_class or defn.name == "__new__":
+            ref_type = mypy.types.TypeType.make_normalized(ref_type)
+        if is_same_type(arg_type, ref_type):
+            return True
+
+        # This level of erasure matches the one in checkmember.check_self_arg(),
+        # better keep these two checks consistent.
+        erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
+        if not is_subtype(ref_type, erased, ignore_type_params=True):
+            if (
+                isinstance(erased, Instance)
+                and erased.type.is_protocol
+                or isinstance(erased, TypeType)
+                and isinstance(erased.item, Instance)
+                and erased.item.type.is_protocol
+            ):
+                # We allow the explicit self-type to be not a supertype of
+                # the current class if it is a protocol. For such cases
+                # the consistency check will be performed at call sites.
+                msg = None
+            elif func.arg_names[0] in {"self", "cls"}:
+                msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
+                    erased.str_with_options(self.options), ref_type.str_with_options(self.options)
+                )
+            else:
+                msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
+            if msg:
+                self.fail(msg, defn)
+        return True
+
     def is_var_redefined_in_outer_context(self, v: Var, after_line: int) -> bool:
         """Can the variable be assigned to at module top level or outer function?
 
@@ -5306,6 +5339,7 @@ def visit_decorator_inner(
             )
         if non_trivial_decorator:
             self.check_untyped_after_decorator(sig, e.func)
+        self.require_correct_self_argument(sig, e.func)
         sig = set_callable_name(sig, e.func)
         e.var.type = sig
         e.var.is_ready = True
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 01b7f4989d80..1840e606af37 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1072,12 +1072,7 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type:
         if func.has_self_or_cls_argument:
             if func.name in ["__init_subclass__", "__class_getitem__"]:
                 func.is_class = True
-            if not func.arguments:
-                self.fail(
-                    'Method must have at least one argument. Did you forget the "self" argument?',
-                    func,
-                )
-            elif isinstance(functype, CallableType):
+            if func.arguments and isinstance(functype, CallableType):
                 self_type = get_proper_type(functype.arg_types[0])
                 if isinstance(self_type, AnyType):
                     if has_self_type:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index ae91815d1e9e..f713fe69bcd2 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -3261,7 +3261,10 @@ b.bad = 'a'  # E: Incompatible types in assignment (expression has type "str", v
 from typing import Any
 
 class Test:
-    def __setattr__() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument? # E: Invalid signature "Callable[[], None]" for "__setattr__"
+    def __setattr__() -> None: ... \
+        # E: Invalid signature "Callable[[], None]" for "__setattr__" \
+        # E: Method must have at least one argument. Did you forget the "self" argument?
+
 t = Test()
 t.crash = 'test'  # E: Attribute function "__setattr__" with type "Callable[[], None]" does not accept self argument \
                   # E: "Test" has no attribute "crash"
@@ -7742,6 +7745,231 @@ class Foo:
     def bad():  # E: Method must have at least one argument. Did you forget the "self" argument?
         self.x = 0  # E: Name "self" is not defined
 
+[case testMethodSelfArgumentChecks]
+from typing import Callable, ParamSpec, TypeVar
+
+T = TypeVar("T")
+P = ParamSpec("P")
+
+def to_number_1(fn: Callable[[], int]) -> int:
+    return 0
+
+def to_number_2(fn: Callable[[int], int]) -> int:
+    return 0
+
+def to_same_callable(fn: Callable[P, T]) -> Callable[P, T]:
+    return fn
+
+class A:
+    def undecorated() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+
+    def undecorated_not_self(x: int) -> None: ...  # E: Self argument missing for a non-static method (or an invalid type for self)
+
+    def undecorated_not_self_2(self: int) -> None: ...  # E: The erased type of self "builtins.int" is not a supertype of its class "__main__.A"
+
+    @to_number_1
+    def fn1() -> int:
+        return 0
+
+    @to_number_1  # E: Argument 1 to "to_number_1" has incompatible type "Callable[[int], int]"; expected "Callable[[], int]"
+    def fn2(_x: int) -> int:
+        return 0
+
+    @to_number_2  # E: Argument 1 to "to_number_2" has incompatible type "Callable[[], int]"; expected "Callable[[int], int]"
+    def fn3() -> int:
+        return 0
+
+    @to_number_2
+    def fn4(_x: int) -> int:
+        return 0
+
+    @to_number_2  # E: Argument 1 to "to_number_2" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]"
+    def fn5(_x: str) -> int:
+        return 0
+
+    @to_same_callable
+    def g1() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+
+    @to_same_callable
+    def g2(x: int) -> None: ...  # E: Self argument missing for a non-static method (or an invalid type for self)
+
+    @to_same_callable
+    def g3(self: int) -> None: ...  # E: The erased type of self "builtins.int" is not a supertype of its class "__main__.A"
+
+reveal_type(A().fn1)  # N: Revealed type is "builtins.int"
+reveal_type(A().fn2)  # N: Revealed type is "builtins.int"
+reveal_type(A().fn3)  # N: Revealed type is "builtins.int"
+reveal_type(A().fn4)  # N: Revealed type is "builtins.int"
+reveal_type(A().fn5)  # N: Revealed type is "builtins.int"
+
+reveal_type(A().g1)  # E: Attribute function "g1" with type "Callable[[], None]" does not accept self argument \
+                     # N: Revealed type is "def ()"
+reveal_type(A().g2)  # E: Invalid self argument "A" to attribute function "g2" with type "Callable[[int], None]" \
+                     # N: Revealed type is "def ()"
+reveal_type(A().g3)  # E: Invalid self argument "A" to attribute function "g3" with type "Callable[[int], None]" \
+                     # N: Revealed type is "def ()"
+[builtins fixtures/tuple.pyi]
+
+[case testMethodSelfArgumentChecksConcatenate]
+from typing import Callable, ParamSpec, TypeVar
+from typing_extensions import Concatenate
+
+T = TypeVar("T")
+P = ParamSpec("P")
+R = TypeVar("R")
+
+def to_same_callable(fn: Callable[Concatenate[T, P], R]) -> Callable[Concatenate[T, P], R]:
+    return fn
+
+def remove_first(fn: Callable[Concatenate[T, P], R]) -> Callable[P, R]:
+    ...
+
+def add_correct_first(fn: Callable[P, R]) -> Callable[Concatenate["C", P], R]:
+    ...
+
+def add_wrong_first(fn: Callable[P, R]) -> Callable[Concatenate[int, P], R]:
+    ...
+
+class A:
+    @to_same_callable  # E: Argument 1 to "to_same_callable" has incompatible type "Callable[[], int]"; expected "Callable[[T], int]"
+    def fn1() -> int:
+        return 0
+
+    @to_same_callable
+    def fn2(_x: int) -> int:  # E: Self argument missing for a non-static method (or an invalid type for self)
+        return 0
+
+    @to_same_callable
+    def fn3(self, _x: int) -> int:
+        return 0
+
+reveal_type(A().fn1)  # N: Revealed type is "def () -> builtins.int"
+reveal_type(A().fn2)  # E: Invalid self argument "A" to attribute function "fn2" with type "Callable[[int], int]" \
+                      # N: Revealed type is "def () -> builtins.int"
+reveal_type(A().fn3)  # N: Revealed type is "def (_x: builtins.int) -> builtins.int"
+
+class B:
+    @remove_first  # E: Argument 1 to "remove_first" has incompatible type "Callable[[], int]"; expected "Callable[[T], int]"
+    def fn1() -> int:  # E: Method must have at least one argument. Did you forget the "self" argument?
+        return 0
+
+    @remove_first
+    def fn2(_x: int) -> int:  # E: Method must have at least one argument. Did you forget the "self" argument?
+        return 0
+
+    @remove_first
+    def fn3(self, _x: int) -> int:  # E: Self argument missing for a non-static method (or an invalid type for self)
+        return 0
+
+    @remove_first
+    def fn4(self, new_self: 'B') -> int:
+        return 0
+
+reveal_type(B().fn1)  # E: Attribute function "fn1" with type "Callable[[], int]" does not accept self argument \
+                      # N: Revealed type is "def () -> builtins.int"
+reveal_type(B().fn2)  # E: Attribute function "fn2" with type "Callable[[], int]" does not accept self argument \
+                      # N: Revealed type is "def () -> builtins.int"
+reveal_type(B().fn3)  # E: Invalid self argument "B" to attribute function "fn3" with type "Callable[[int], int]" \
+                      # N: Revealed type is "def () -> builtins.int"
+reveal_type(B().fn4)  # N: Revealed type is "def () -> builtins.int"
+
+class C:
+    @add_correct_first
+    def fn1() -> int:
+        return 0
+
+    @add_correct_first
+    def fn2(_x: int) -> int:
+        return 0
+
+    @add_correct_first
+    def fn3(self, _x: int) -> int:
+        return 0
+
+reveal_type(C().fn1)  # N: Revealed type is "def () -> builtins.int"
+reveal_type(C().fn2)  # N: Revealed type is "def (_x: builtins.int) -> builtins.int"
+reveal_type(C().fn3)  # N: Revealed type is "def (self: __main__.C, _x: builtins.int) -> builtins.int"
+
+class D:
+    @add_wrong_first
+    def fn1() -> int:  # E: Self argument missing for a non-static method (or an invalid type for self)
+        return 0
+
+    @add_wrong_first
+    def fn2(_x: int) -> int:  # E: Self argument missing for a non-static method (or an invalid type for self)
+        return 0
+
+    @add_wrong_first
+    def fn3(self, _x: int) -> int:  # E: Self argument missing for a non-static method (or an invalid type for self)
+        return 0
+
+reveal_type(D().fn1)  # E: Invalid self argument "D" to attribute function "fn1" with type "Callable[[int], int]" \
+                      # N: Revealed type is "def () -> builtins.int"
+reveal_type(D().fn2)  # E: Invalid self argument "D" to attribute function "fn2" with type "Callable[[int, int], int]" \
+                      # N: Revealed type is "def (_x: builtins.int) -> builtins.int"
+reveal_type(D().fn3)  # E: Invalid self argument "D" to attribute function "fn3" with type "Callable[[int, D, int], int]" \
+                      # N: Revealed type is "def (self: __main__.D, _x: builtins.int) -> builtins.int"
+[builtins fixtures/tuple.pyi]
+
+[case testMethodSelfArgumentChecksInUntyped]
+from typing import Callable, ParamSpec, TypeVar
+
+T = TypeVar("T")
+P = ParamSpec("P")
+
+def to_same_callable(fn: Callable[P, T]) -> Callable[P, T]:
+    return fn
+
+def unchecked():
+    class Bad:
+        def fn() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+        def fn2(x: int) -> None: ...  # E: Self argument missing for a non-static method (or an invalid type for self)
+
+        # TODO: would be nice to make this error, but now we see the func
+        # being decorated as Any, not as a callable
+        @to_same_callable
+        def gaaa() -> None: ...
+        @to_same_callable
+        def gaaa2(x: int) -> None: ...
+
+    class Ok:
+        def fn(): ...
+        def fn2(x): ...
+
+        @to_same_callable
+        def g(): ...
+        @to_same_callable
+        def g2(x): ...
+
+def checked() -> None:
+    class Bad:
+        def fn() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+        def fn2(x: int) -> None: ...  # E: Self argument missing for a non-static method (or an invalid type for self)
+
+        @to_same_callable
+        def g() -> None: ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+        @to_same_callable
+        def g2(x: int) -> None: ...  # E: Self argument missing for a non-static method (or an invalid type for self)
+
+    class AlsoBad:
+        def fn(): ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+        def fn2(x): ...
+
+        @to_same_callable
+        def g(): ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+        @to_same_callable
+        def g2(x): ...
+
+class Ok:
+    def fn(): ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+    def fn2(x): ...
+
+    @to_same_callable
+    def g(): ...  # E: Method must have at least one argument. Did you forget the "self" argument?
+    @to_same_callable
+    def g2(x): ...
+[builtins fixtures/tuple.pyi]
+
 [case testTypeAfterAttributeAccessWithDisallowAnyExpr]
 # flags: --disallow-any-expr
 
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 503135d901f8..c25ed79e7356 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -1720,7 +1720,7 @@ from typing import Iterator, Callable, List, Optional
 from a import f
 import a
 
-def dec(f: Callable[['A'], Optional[Iterator[int]]]) -> Callable[[int], int]: pass
+def dec(f: Callable[['A'], Optional[Iterator[int]]]) -> Callable[['A', int], int]: pass
 
 class A:
     @dec
diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test
index 2d381644629b..8053b33b94fd 100644
--- a/test-data/unit/semanal-errors.test
+++ b/test-data/unit/semanal-errors.test
@@ -537,13 +537,6 @@ def f(y: t): x = t
 main:4: error: "t" is a type variable and only valid in type context
 main:5: error: "t" is a type variable and only valid in type context
 
-[case testMissingSelf]
-import typing
-class A:
-  def f(): pass
-[out]
-main:3: error: Method must have at least one argument. Did you forget the "self" argument?
-
 [case testInvalidBaseClass]
 import typing
 class A(B): pass
@@ -558,15 +551,6 @@ def f() -> None: super().y
 main:2: error: "super" used outside class
 main:3: error: "super" used outside class
 
-[case testMissingSelfInMethod]
-import typing
-class A:
-  def f() -> None: pass
-  def g(): pass
-[out]
-main:3: error: Method must have at least one argument. Did you forget the "self" argument?
-main:4: error: Method must have at least one argument. Did you forget the "self" argument?
-
 [case testMultipleMethodDefinition]
 import typing
 class A:

From 703bee951e51083e01406f82e4835deda28e1672 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 29 Jul 2025 03:00:16 +0200
Subject: [PATCH 1491/1617] perf: deduplicate `fast_container_type` and
 `fast_dict_type` items before joining (#19409)

Vastly improves #14718. Some type joins are really heavy - especially
joins between overloads. This does not fully remove the problem, a
collection of different pairwise equivalent overloads differing only in
their names is still slow to check, but at least we will not join every
such monster callable with itself multiple times.
---
 mypy/checkexpr.py                   | 73 ++++++++++++++++++++++++-----
 test-data/unit/check-generics.test  | 10 ++--
 test-data/unit/check-redefine2.test |  2 +-
 test-data/unit/check-selftype.test  |  2 +-
 4 files changed, 68 insertions(+), 19 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index a8afebbd9923..d6982a6bed43 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -5069,31 +5069,56 @@ def fast_container_type(
         module-level constant definitions.
 
         Limitations:
+
          - no active type context
+         - at least one item
          - no star expressions
-         - the joined type of all entries must be an Instance or Tuple type
+         - not after deferral
+         - either exactly one distinct type inside,
+           or the joined type of all entries is an Instance or Tuple type,
         """
         ctx = self.type_context[-1]
-        if ctx:
+        if ctx or not e.items:
+            return None
+        if self.chk.current_node_deferred:
+            # Guarantees that all items will be Any, we'll reject it anyway.
             return None
         rt = self.resolved_type.get(e, None)
         if rt is not None:
             return rt if isinstance(rt, Instance) else None
         values: list[Type] = []
+        # Preserve join order while avoiding O(n) lookups at every iteration
+        values_set: set[Type] = set()
         for item in e.items:
             if isinstance(item, StarExpr):
                 # fallback to slow path
                 self.resolved_type[e] = NoneType()
                 return None
-            values.append(self.accept(item))
-        vt = join.join_type_list(values)
-        if not allow_fast_container_literal(vt):
+
+            typ = self.accept(item)
+            if typ not in values_set:
+                values.append(typ)
+                values_set.add(typ)
+
+        vt = self._first_or_join_fast_item(values)
+        if vt is None:
             self.resolved_type[e] = NoneType()
             return None
         ct = self.chk.named_generic_type(container_fullname, [vt])
         self.resolved_type[e] = ct
         return ct
 
+    def _first_or_join_fast_item(self, items: list[Type]) -> Type | None:
+        if len(items) == 1 and not self.chk.current_node_deferred:
+            return items[0]
+        typ = join.join_type_list(items)
+        if not allow_fast_container_literal(typ):
+            # TODO: This is overly strict, many other types can be joined safely here.
+            # However, our join implementation isn't bug-free, and some joins may produce
+            # undesired `Any`s or even more surprising results.
+            return None
+        return typ
+
     def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: str) -> Type:
         # fast path
         t = self.fast_container_type(e, fullname)
@@ -5254,18 +5279,30 @@ def fast_dict_type(self, e: DictExpr) -> Type | None:
         module-level constant definitions.
 
         Limitations:
+
          - no active type context
+         - at least one item
          - only supported star expressions are other dict instances
-         - the joined types of all keys and values must be Instance or Tuple types
+         - either exactly one distinct type (keys and values separately) inside,
+           or the joined type of all entries is an Instance or Tuple type
         """
         ctx = self.type_context[-1]
-        if ctx:
+        if ctx or not e.items:
             return None
+
+        if self.chk.current_node_deferred:
+            # Guarantees that all items will be Any, we'll reject it anyway.
+            return None
+
         rt = self.resolved_type.get(e, None)
         if rt is not None:
             return rt if isinstance(rt, Instance) else None
+
         keys: list[Type] = []
         values: list[Type] = []
+        # Preserve join order while avoiding O(n) lookups at every iteration
+        keys_set: set[Type] = set()
+        values_set: set[Type] = set()
         stargs: tuple[Type, Type] | None = None
         for key, value in e.items:
             if key is None:
@@ -5280,13 +5317,25 @@ def fast_dict_type(self, e: DictExpr) -> Type | None:
                     self.resolved_type[e] = NoneType()
                     return None
             else:
-                keys.append(self.accept(key))
-                values.append(self.accept(value))
-        kt = join.join_type_list(keys)
-        vt = join.join_type_list(values)
-        if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)):
+                key_t = self.accept(key)
+                if key_t not in keys_set:
+                    keys.append(key_t)
+                    keys_set.add(key_t)
+                value_t = self.accept(value)
+                if value_t not in values_set:
+                    values.append(value_t)
+                    values_set.add(value_t)
+
+        kt = self._first_or_join_fast_item(keys)
+        if kt is None:
             self.resolved_type[e] = NoneType()
             return None
+
+        vt = self._first_or_join_fast_item(values)
+        if vt is None:
+            self.resolved_type[e] = NoneType()
+            return None
+
         if stargs and (stargs[0] != kt or stargs[1] != vt):
             self.resolved_type[e] = NoneType()
             return None
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index 78680684f69b..abeb5face26f 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -2929,8 +2929,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]:
 def id(__x: U) -> U:
     ...
 fs = [id, id, id]
-reveal_type(mix(fs))  # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]"
-reveal_type(mix([id, id, id]))  # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]"
+reveal_type(mix(fs))  # N: Revealed type is "def [S] (S`2) -> builtins.list[S`2]"
+reveal_type(mix([id, id, id]))  # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCurry]
@@ -3118,11 +3118,11 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]:
 reveal_type(dec1(lambda x: x))  # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]"
 reveal_type(dec2(lambda x: x))  # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]"
 reveal_type(dec3(lambda x: x[0]))  # N: Revealed type is "def [S] (S`8) -> S`8"
-reveal_type(dec4(lambda x: [x]))  # N: Revealed type is "def [S] (S`12) -> S`12"
+reveal_type(dec4(lambda x: [x]))  # N: Revealed type is "def [S] (S`11) -> S`11"
 reveal_type(dec1(lambda x: 1))  # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
 reveal_type(dec5(lambda x: x))  # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
-reveal_type(dec3(lambda x: x))  # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]"
-reveal_type(dec4(lambda x: x))  # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24"
+reveal_type(dec3(lambda x: x))  # N: Revealed type is "def [S] (S`19) -> builtins.list[S`19]"
+reveal_type(dec4(lambda x: x))  # N: Revealed type is "def [T] (builtins.list[T`23]) -> T`23"
 dec4_bound(lambda x: x)  # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]"
 [builtins fixtures/list.pyi]
 
diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test
index 3523772611aa..1abe957240b5 100644
--- a/test-data/unit/check-redefine2.test
+++ b/test-data/unit/check-redefine2.test
@@ -1073,7 +1073,7 @@ def f() -> None:
         while int():
             x = [x]
 
-    reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]]]]]"
+    reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any]]"
 
 [case testNewRedefinePartialNoneEmptyList]
 # flags: --allow-redefinition-new --local-partial-types
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 88ca53c8ed66..05c34eb70796 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2018,7 +2018,7 @@ class Ben(Object):
     }
     @classmethod
     def doit(cls) -> Foo:
-        reveal_type(cls.MY_MAP)  # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]"
+        reveal_type(cls.MY_MAP)  # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`1) -> Self`1]"
         foo_method = cls.MY_MAP["foo"]
         return foo_method(Foo())
 [builtins fixtures/isinstancelist.pyi]

From c53367f87efeebd9781042f0d0790fd11fe5031f Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 29 Jul 2025 02:12:20 +0100
Subject: [PATCH 1492/1617] Use cache for OpExpr (#19523)

Fixes https://github.com/python/mypy/issues/14978

This is irrelevant for self-check (and for most code likely), but it is
important for numerical code, where it avoids exponential slowdown in
formulas involving arrays etc (see e.g. the original issue).

Unless there will be fallout in `mypy_primer` and/or there are
objections, I am going to go ahead and merge it.
---
 mypy/checkexpr.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index d6982a6bed43..e954bbd671e6 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6043,7 +6043,7 @@ def accept(
             # We cannot use cache inside lambdas, because they skip immediate type
             # context, and use enclosing one, see infer_lambda_type_using_context().
             # TODO: consider using cache for more expression kinds.
-            elif isinstance(node, (CallExpr, ListExpr, TupleExpr)) and not (
+            elif isinstance(node, (CallExpr, ListExpr, TupleExpr, OpExpr)) and not (
                 self.in_lambda_expr or self.chk.current_node_deferred
             ):
                 if (node, type_context) in self.expr_cache:

From 0f78f9c578bfbc6fbe4a20888353c20e9691178a Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Wed, 30 Jul 2025 10:12:29 +0100
Subject: [PATCH 1493/1617] Use cache for DictExpr as well (#19536)

Fixes https://github.com/python/mypy/issues/14271
Fixes https://github.com/python/mypy/issues/14636

TBH examples in those issues are already sufficiently fast (probably
because of combined effect of fast dict literals, and the fact that
there are some lists and/or function calls in that examples, so some
caching already kicks in). But this PR will probably make them even
faster.

This has ~0 effect on self-check.
---
 mypy/checkexpr.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index e954bbd671e6..ecae451299d7 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6043,7 +6043,7 @@ def accept(
             # We cannot use cache inside lambdas, because they skip immediate type
             # context, and use enclosing one, see infer_lambda_type_using_context().
             # TODO: consider using cache for more expression kinds.
-            elif isinstance(node, (CallExpr, ListExpr, TupleExpr, OpExpr)) and not (
+            elif isinstance(node, (CallExpr, ListExpr, TupleExpr, DictExpr, OpExpr)) and not (
                 self.in_lambda_expr or self.chk.current_node_deferred
             ):
                 if (node, type_context) in self.expr_cache:

From 5750690e625606a8c6875cff07a0ed299703646c Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 30 Jul 2025 13:36:06 +0100
Subject: [PATCH 1494/1617] [mypyc] Make type objects immortal if using free
 threading (#19538)

If they are not immortal, concurrent construction of objects by multiple
threads can cause serious contention due to reference count updates.

Making them immortal is similar to how both user-defined normal Python
classes and built-in types in free-threaded builds are immortal.

Fix the issue for both native and non-native classes. Dataclasses still
have contention, and they may be harder to fix (this may require a fix
in CPython).

This speeds up a few micro-benchmarks that construct instances of
classes in multiple threads by a big factor (5x+).
---
 mypyc/irbuild/builder.py     |  8 ++++++++
 mypyc/irbuild/classdef.py    |  8 ++++++++
 mypyc/irbuild/ll_builder.py  |  8 ++++++++
 mypyc/lib-rt/CPy.h           |  4 ++++
 mypyc/lib-rt/misc_ops.c      | 12 +++++++++++-
 mypyc/lib-rt/mypyc_util.h    |  3 ++-
 mypyc/primitives/misc_ops.py | 15 +++++++++++++++
 7 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 7e63d482c786..ec3c1b1b1f3c 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -424,6 +424,10 @@ def new_tuple(self, items: list[Value], line: int) -> Value:
     def debug_print(self, toprint: str | Value) -> None:
         return self.builder.debug_print(toprint)
 
+    def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
+        """Make an object immortal on free-threaded builds (to avoid contention)."""
+        self.builder.set_immortal_if_free_threaded(v, line)
+
     # Helpers for IR building
 
     def add_to_non_ext_dict(
@@ -433,6 +437,10 @@ def add_to_non_ext_dict(
         key_unicode = self.load_str(key)
         self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line)
 
+        # It's important that accessing class dictionary items from multiple threads
+        # doesn't cause contention.
+        self.builder.set_immortal_if_free_threaded(val, line)
+
     def gen_import(self, id: str, line: int) -> None:
         self.imports[id] = None
 
diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 6b59750c7dec..3282e836ac9e 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -262,6 +262,9 @@ def finalize(self, ir: ClassIR) -> None:
         non_ext_class = load_non_ext_class(self.builder, ir, self.non_ext, self.cdef.line)
         non_ext_class = load_decorated_class(self.builder, self.cdef, non_ext_class)
 
+        # Try to avoid contention when using free threading.
+        self.builder.set_immortal_if_free_threaded(non_ext_class, self.cdef.line)
+
         # Save the decorated class
         self.builder.add(
             InitStatic(non_ext_class, self.cdef.name, self.builder.module_name, NAMESPACE_TYPE)
@@ -449,6 +452,11 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
     )
     # Create the class
     tp = builder.call_c(pytype_from_template_op, [template, tp_bases, modname], cdef.line)
+
+    # Set type object to be immortal if free threaded, as otherwise reference count contention
+    # can cause a big performance hit.
+    builder.set_immortal_if_free_threaded(tp, cdef.line)
+
     # Immediately fix up the trait vtables, before doing anything with the class.
     ir = builder.mapper.type_to_ir[cdef.info]
     if not ir.is_trait and not ir.builtin_base:
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 79ad4cc62822..a5e28268efed 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -6,6 +6,7 @@
 
 from __future__ import annotations
 
+import sys
 from collections.abc import Sequence
 from typing import Callable, Final, Optional
 
@@ -16,6 +17,7 @@
 from mypyc.common import (
     BITMAP_BITS,
     FAST_ISINSTANCE_MAX_SUBCLASSES,
+    IS_FREE_THREADED,
     MAX_LITERAL_SHORT_INT,
     MAX_SHORT_INT,
     MIN_LITERAL_SHORT_INT,
@@ -164,6 +166,7 @@
     fast_isinstance_op,
     none_object_op,
     not_implemented_op,
+    set_immortal_op,
     var_object_size,
 )
 from mypyc.primitives.registry import (
@@ -2322,6 +2325,11 @@ def new_tuple_with_length(self, length: Value, line: int) -> Value:
     def int_to_float(self, n: Value, line: int) -> Value:
         return self.primitive_op(int_to_float_op, [n], line)
 
+    def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
+        """Make an object immortal on free-threaded builds (to avoid contention)."""
+        if IS_FREE_THREADED and sys.version_info >= (3, 14):
+            self.primitive_op(set_immortal_op, [v], line)
+
     # Internal helpers
 
     def decompose_union_helper(
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index e7a7f9a07626..1881aa97f308 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -931,6 +931,10 @@ PyObject *CPy_GetANext(PyObject *aiter);
 void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value);
 void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details);
 
+#if CPY_3_14_FEATURES
+void CPy_SetImmortal(PyObject *obj);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c
index 8aa25cc11e02..3787ea553037 100644
--- a/mypyc/lib-rt/misc_ops.c
+++ b/mypyc/lib-rt/misc_ops.c
@@ -1058,7 +1058,7 @@ void CPyTrace_LogEvent(const char *location, const char *line, const char *op, c
 
 #endif
 
-#ifdef CPY_3_12_FEATURES
+#if CPY_3_12_FEATURES
 
 // Copied from Python 3.12.3, since this struct is internal to CPython. It defines
 // the structure of typing.TypeAliasType objects. We need it since compute_value is
@@ -1088,3 +1088,13 @@ void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_valu
 }
 
 #endif
+
+#if CPY_3_14_FEATURES
+
+#include "internal/pycore_object.h"
+
+void CPy_SetImmortal(PyObject *obj) {
+    _Py_SetImmortal(obj);
+}
+
+#endif
diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h
index 3d4eba3a3cdb..f200d4f90def 100644
--- a/mypyc/lib-rt/mypyc_util.h
+++ b/mypyc/lib-rt/mypyc_util.h
@@ -139,8 +139,9 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) {
     return x << 1;
 }
 
-// Are we targeting Python 3.12 or newer?
+// Are we targeting Python 3.X or newer?
 #define CPY_3_12_FEATURES (PY_VERSION_HEX >= 0x030c0000)
+#define CPY_3_14_FEATURES (PY_VERSION_HEX >= 0x030e0000)
 
 #if CPY_3_12_FEATURES
 
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index e2a1aea1a8d6..e3d59f53ed76 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -311,3 +311,18 @@
     return_type=void_rtype,
     error_kind=ERR_NEVER,
 )
+
+# Mark object as immortal -- it won't be freed via reference counting, as
+# the reference count won't be updated any longer. Immortal objects support
+# fast concurrent read-only access from multiple threads when using free
+# threading, since this eliminates contention from concurrent reference count
+# updates.
+#
+# Needs at least Python 3.14.
+set_immortal_op = custom_primitive_op(
+    name="set_immmortal",
+    c_function_name="CPy_SetImmortal",
+    arg_types=[object_rprimitive],
+    return_type=void_rtype,
+    error_kind=ERR_NEVER,
+)

From 43364c1be9d0e53d4e715ad02b7b71628d40db79 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 30 Jul 2025 17:20:01 +0100
Subject: [PATCH 1495/1617] [mypyc] Enable free threading when compiling
 multiple modules (#19541)

Previously we used multi-phase initialization to enable free threading
on Python builds that support it, but only if a single module was
compiled in a group.

Implements it also for multiple modules in a group. Add support for
multi-phase initialization in module shims and the shared library. It's
still only used on free-threaded builds, and we fall back to the old
approach on other Python versions/builds.

This enables compiling mypy and mypyc on free-threaded Python builds. At
least almost all mypy and mypyc tests now pass when compiled and on
3.14.0b4 with free threading (only tested on macOS so far).
---
 mypyc/build.py                                |  10 +-
 mypyc/codegen/emitmodule.py                   | 134 +++++++++++++-----
 .../lib-rt/module_shim_no_gil_multiphase.tmpl |  41 ++++++
 3 files changed, 146 insertions(+), 39 deletions(-)
 create mode 100644 mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl

diff --git a/mypyc/build.py b/mypyc/build.py
index b7d3e1b25366..4a2d703b9f10 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -36,7 +36,7 @@
 from mypy.util import write_junit_xml
 from mypyc.annotate import generate_annotated_html
 from mypyc.codegen import emitmodule
-from mypyc.common import RUNTIME_C_FILES, shared_lib_name
+from mypyc.common import IS_FREE_THREADED, RUNTIME_C_FILES, shared_lib_name
 from mypyc.errors import Errors
 from mypyc.ir.pprint import format_modules
 from mypyc.namegen import exported_name
@@ -176,9 +176,15 @@ def generate_c_extension_shim(
     cname = "%s.c" % full_module_name.replace(".", os.sep)
     cpath = os.path.join(dir_name, cname)
 
+    if IS_FREE_THREADED:
+        # We use multi-phase init in free-threaded builds to enable free threading.
+        shim_name = "module_shim_no_gil_multiphase.tmpl"
+    else:
+        shim_name = "module_shim.tmpl"
+
     # We load the C extension shim template from a file.
     # (So that the file could be reused as a bazel template also.)
-    with open(os.path.join(include_dir(), "module_shim.tmpl")) as f:
+    with open(os.path.join(include_dir(), shim_name)) as f:
         shim_template = f.read()
 
     write_file(
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 047309ec71e3..de34ed9fc7da 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -784,28 +784,15 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None:
         assert self.group_name is not None
 
         emitter.emit_line()
+
+        short_name = shared_lib_name(self.group_name).split(".")[-1]
+
         emitter.emit_lines(
-            "PyMODINIT_FUNC PyInit_{}(void)".format(
-                shared_lib_name(self.group_name).split(".")[-1]
-            ),
+            f"static int exec_{short_name}(PyObject *module)",
             "{",
-            (
-                'static PyModuleDef def = {{ PyModuleDef_HEAD_INIT, "{}", NULL, -1, NULL, NULL }};'.format(
-                    shared_lib_name(self.group_name)
-                )
-            ),
             "int res;",
             "PyObject *capsule;",
             "PyObject *tmp;",
-            "static PyObject *module;",
-            "if (module) {",
-            "Py_INCREF(module);",
-            "return module;",
-            "}",
-            "module = PyModule_Create(&def);",
-            "if (!module) {",
-            "goto fail;",
-            "}",
             "",
         )
 
@@ -827,15 +814,26 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None:
 
         for mod in self.modules:
             name = exported_name(mod)
+            if self.multi_phase_init:
+                capsule_func_prefix = "CPyExec_"
+                capsule_name_prefix = "exec_"
+                emitter.emit_line(f"extern int CPyExec_{name}(PyObject *);")
+            else:
+                capsule_func_prefix = "CPyInit_"
+                capsule_name_prefix = "init_"
+                emitter.emit_line(f"extern PyObject *CPyInit_{name}(void);")
             emitter.emit_lines(
-                f"extern PyObject *CPyInit_{name}(void);",
-                'capsule = PyCapsule_New((void *)CPyInit_{}, "{}.init_{}", NULL);'.format(
-                    name, shared_lib_name(self.group_name), name
+                'capsule = PyCapsule_New((void *){}{}, "{}.{}{}", NULL);'.format(
+                    capsule_func_prefix,
+                    name,
+                    shared_lib_name(self.group_name),
+                    capsule_name_prefix,
+                    name,
                 ),
                 "if (!capsule) {",
                 "goto fail;",
                 "}",
-                f'res = PyObject_SetAttrString(module, "init_{name}", capsule);',
+                f'res = PyObject_SetAttrString(module, "{capsule_name_prefix}{name}", capsule);',
                 "Py_DECREF(capsule);",
                 "if (res < 0) {",
                 "goto fail;",
@@ -861,7 +859,56 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None:
                 "",
             )
 
-        emitter.emit_lines("return module;", "fail:", "Py_XDECREF(module);", "return NULL;", "}")
+        emitter.emit_lines("return 0;", "fail:", "return -1;", "}")
+
+        if self.multi_phase_init:
+            emitter.emit_lines(
+                f"static PyModuleDef_Slot slots_{short_name}[] = {{",
+                f"{{Py_mod_exec, exec_{short_name}}},",
+                "{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},",
+                "{Py_mod_gil, Py_MOD_GIL_NOT_USED},",
+                "{0, NULL},",
+                "};",
+            )
+
+        size = 0 if self.multi_phase_init else -1
+        emitter.emit_lines(
+            f"static PyModuleDef module_def_{short_name} = {{",
+            "PyModuleDef_HEAD_INIT,",
+            f'.m_name = "{shared_lib_name(self.group_name)}",',
+            ".m_doc = NULL,",
+            f".m_size = {size},",
+            ".m_methods = NULL,",
+        )
+        if self.multi_phase_init:
+            emitter.emit_line(f".m_slots = slots_{short_name},")
+        emitter.emit_line("};")
+
+        if self.multi_phase_init:
+            emitter.emit_lines(
+                f"PyMODINIT_FUNC PyInit_{short_name}(void) {{",
+                f"return PyModuleDef_Init(&module_def_{short_name});",
+                "}",
+            )
+        else:
+            emitter.emit_lines(
+                f"PyMODINIT_FUNC PyInit_{short_name}(void) {{",
+                "static PyObject *module = NULL;",
+                "if (module) {",
+                "Py_INCREF(module);",
+                "return module;",
+                "}",
+                f"module = PyModule_Create(&module_def_{short_name});",
+                "if (!module) {",
+                "return NULL;",
+                "}",
+                f"if (exec_{short_name}(module) < 0) {{",
+                "Py_DECREF(module);",
+                "return NULL;",
+                "}",
+                "return module;",
+                "}",
+            )
 
     def generate_globals_init(self, emitter: Emitter) -> None:
         emitter.emit_lines(
@@ -887,16 +934,22 @@ def generate_globals_init(self, emitter: Emitter) -> None:
     def generate_module_def(self, emitter: Emitter, module_name: str, module: ModuleIR) -> None:
         """Emit the PyModuleDef struct for a module and the module init function."""
         module_prefix = emitter.names.private_name(module_name)
-        self.emit_module_exec_func(emitter, module_name, module_prefix, module)
-        if self.multi_phase_init:
-            self.emit_module_def_slots(emitter, module_prefix)
         self.emit_module_methods(emitter, module_name, module_prefix, module)
-        self.emit_module_def_struct(emitter, module_name, module_prefix)
-        self.emit_module_init_func(emitter, module_name, module_prefix)
+        self.emit_module_exec_func(emitter, module_name, module_prefix, module)
 
-    def emit_module_def_slots(self, emitter: Emitter, module_prefix: str) -> None:
+        # If using multi-phase init and a shared lib, parts of module definition
+        # will happen in the shim modules, so we skip some steps here.
+        if not (self.multi_phase_init and self.use_shared_lib):
+            if self.multi_phase_init:
+                self.emit_module_def_slots(emitter, module_prefix, module_name)
+            self.emit_module_def_struct(emitter, module_name, module_prefix)
+            self.emit_module_init_func(emitter, module_name, module_prefix)
+
+    def emit_module_def_slots(
+        self, emitter: Emitter, module_prefix: str, module_name: str
+    ) -> None:
         name = f"{module_prefix}_slots"
-        exec_name = f"{module_prefix}_exec"
+        exec_name = f"CPyExec_{exported_name(module_name)}"
 
         emitter.emit_line(f"static PyModuleDef_Slot {name}[] = {{")
         emitter.emit_line(f"{{Py_mod_exec, {exec_name}}},")
@@ -951,7 +1004,7 @@ def emit_module_def_struct(
             "0,       /* size of per-interpreter state of the module */",
             f"{module_prefix}module_methods,",
         )
-        if self.multi_phase_init:
+        if self.multi_phase_init and not self.use_shared_lib:
             slots_name = f"{module_prefix}_slots"
             emitter.emit_line(f"{slots_name}, /* m_slots */")
         else:
@@ -962,15 +1015,16 @@ def emit_module_def_struct(
     def emit_module_exec_func(
         self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
     ) -> None:
-        """Emit the module init function.
+        """Emit the module exec function.
 
-        If we are compiling just one module, this will be the C API init
-        function. If we are compiling 2+ modules, we generate a shared
+        If we are compiling just one module, this will be the normal C API
+        exec function. If we are compiling 2+ modules, we generate a shared
         library for the modules and shims that call into the shared
-        library, and in this case we use an internal module initialized
-        function that will be called by the shim.
+        library, and in this case the shared module defines an internal
+        exec function for each module and these will be called by the shims
+        via Capsules.
         """
-        declaration = f"static int {module_prefix}_exec(PyObject *module)"
+        declaration = f"int CPyExec_{exported_name(module_name)}(PyObject *module)"
         module_static = self.module_internal_static_name(module_name, emitter)
         emitter.emit_lines(declaration, "{")
         emitter.emit_line("PyObject* modname = NULL;")
@@ -987,6 +1041,12 @@ def emit_module_exec_func(
             "    goto fail;",
         )
 
+        if self.multi_phase_init:
+            emitter.emit_lines(
+                f"if (PyModule_AddFunctions(module, {module_prefix}module_methods) < 0)",
+                "    goto fail;",
+            )
+
         # HACK: Manually instantiate generated classes here
         type_structs: list[str] = []
         for cl in module.classes:
@@ -1038,7 +1098,7 @@ def emit_module_init_func(
             emitter.emit_line("}")
             return
 
-        exec_func = f"{module_prefix}_exec"
+        exec_func = f"CPyExec_{exported_name(module_name)}"
 
         # Store the module reference in a static and return it when necessary.
         # This is separate from the *global* reference to the module that will
diff --git a/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl
new file mode 100644
index 000000000000..b9bfe9c91962
--- /dev/null
+++ b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl
@@ -0,0 +1,41 @@
+#include 
+
+static int {modname}_exec(PyObject *module)
+{{
+    PyObject *tmp;
+    if (!(tmp = PyImport_ImportModule("{libname}"))) return -1;
+    PyObject *capsule = PyObject_GetAttrString(tmp, "exec_{full_modname}");
+    Py_DECREF(tmp);
+    if (capsule == NULL) return -1;
+    void *exec_func = PyCapsule_GetPointer(capsule, "{libname}.exec_{full_modname}");
+    Py_DECREF(capsule);
+    if (!exec_func) return -1;
+    if (((int (*)(PyObject *))exec_func)(module) != 0) return -1;
+    return 0;
+}}
+
+static PyModuleDef_Slot {modname}_slots[] = {{
+    {{Py_mod_exec, {modname}_exec}},
+    {{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}},
+    {{Py_mod_gil, Py_MOD_GIL_NOT_USED}},
+    {{0, NULL}},
+}};
+
+static struct PyModuleDef {modname}_module = {{
+    PyModuleDef_HEAD_INIT,
+    .m_name = "{modname}",
+    .m_doc = NULL,
+    .m_methods = NULL,
+    .m_size = 0,
+    .m_slots = {modname}_slots,
+}};
+
+PyMODINIT_FUNC
+PyInit_{modname}(void)
+{{
+    return PyModuleDef_Init(&{modname}_module);
+}}
+
+// distutils sometimes spuriously tells cl to export CPyInit___init__,
+// so provide that so it chills out
+PyMODINIT_FUNC PyInit___init__(void) {{ return PyInit_{modname}(); }}

From e727ea69d7ff2b2e1a758b80983f5962404b4e9d Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Thu, 31 Jul 2025 01:24:08 +0200
Subject: [PATCH 1496/1617] Update test requirements (#19539)

---
 test-requirements.txt | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/test-requirements.txt b/test-requirements.txt
index bcdf02319306..11ac675eca15 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -8,9 +8,9 @@ attrs==25.3.0
     # via -r test-requirements.in
 cfgv==3.4.0
     # via pre-commit
-coverage==7.8.2
+coverage==7.10.1
     # via pytest-cov
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 execnet==2.1.1
     # via pytest-xdist
@@ -22,7 +22,7 @@ identify==2.6.12
     # via pre-commit
 iniconfig==2.1.0
     # via pytest
-lxml==5.4.0 ; python_version < "3.14"
+lxml==6.0.0 ; python_version < "3.14"
     # via -r test-requirements.in
 mypy-extensions==1.1.0
     # via -r mypy-requirements.txt
@@ -35,31 +35,35 @@ pathspec==0.12.1
 platformdirs==4.3.8
     # via virtualenv
 pluggy==1.6.0
-    # via pytest
+    # via
+    #   pytest
+    #   pytest-cov
 pre-commit==4.2.0
     # via -r test-requirements.in
 psutil==7.0.0
     # via -r test-requirements.in
-pytest==8.3.5
+pygments==2.19.2
+    # via pytest
+pytest==8.4.1
     # via
     #   -r test-requirements.in
     #   pytest-cov
     #   pytest-xdist
-pytest-cov==6.1.1
+pytest-cov==6.2.1
     # via -r test-requirements.in
-pytest-xdist==3.7.0
+pytest-xdist==3.8.0
     # via -r test-requirements.in
 pyyaml==6.0.2
     # via pre-commit
 tomli==2.2.1
     # via -r test-requirements.in
-types-psutil==7.0.0.20250516
+types-psutil==7.0.0.20250601
     # via -r build-requirements.txt
-types-setuptools==80.8.0.20250521
+types-setuptools==80.9.0.20250529
     # via -r build-requirements.txt
-typing-extensions==4.13.2
+typing-extensions==4.14.1
     # via -r mypy-requirements.txt
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via pre-commit
 
 # The following packages are considered to be unsafe in a requirements file:

From 270142f90c76f9137cbd2c2ac57568d1f2e17a58 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Wed, 30 Jul 2025 23:55:04 -0700
Subject: [PATCH 1497/1617] Update changelog for 1.17.1 (#19550)

---
 CHANGELOG.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a74fb46aba6b..5bdb888ff9d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -170,6 +170,11 @@ Related PRs:
 * Enable ANSI color codes for dmypy client in Windows (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088))
 * Extend special case for context-based type variable inference to unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976))
 
+### Mypy 1.17.1
+* Retain `None` as constraints bottom if no bottoms were provided (Stanislav Terliakov, PR [19485](https://github.com/python/mypy/pull/19485))
+* Fix "ignored exception in `hasattr`" in dmypy (Stanislav Terliakov, PR [19428](https://github.com/python/mypy/pull/19428))
+* Prevent a crash when InitVar is redefined with a method in a subclass (Stanislav Terliakov, PR [19453](https://github.com/python/mypy/pull/19453))
+
 ### Acknowledgements
 
 Thanks to all mypy contributors who contributed to this release:

From 7534898319cb7f16738c11e4bc1bdcef0eb13c38 Mon Sep 17 00:00:00 2001
From: Emily 
Date: Thu, 31 Jul 2025 08:57:41 +0100
Subject: [PATCH 1498/1617] =?UTF-8?q?Explicitly=20check=20case=E2=80=90sen?=
 =?UTF-8?q?sitivity=20of=20file=20system=20for=20tests=20(#19540)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Both macOS and Windows support using case‐sensitive file systems; this
fixes the test suite in those situations.
---
 mypy/test/testcheck.py   | 11 +++++++----
 mypy/test/testfscache.py |  3 +--
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py
index fb2eb3a75b9b..206eab726a21 100644
--- a/mypy/test/testcheck.py
+++ b/mypy/test/testcheck.py
@@ -5,6 +5,8 @@
 import os
 import re
 import sys
+import tempfile
+from pathlib import Path
 
 from mypy import build
 from mypy.build import Graph
@@ -46,15 +48,16 @@
 if sys.version_info < (3, 13):
     typecheck_files.remove("check-python313.test")
 
-# Special tests for platforms with case-insensitive filesystems.
-if sys.platform not in ("darwin", "win32"):
-    typecheck_files.remove("check-modules-case.test")
-
 
 class TypeCheckSuite(DataSuite):
     files = typecheck_files
 
     def run_case(self, testcase: DataDrivenTestCase) -> None:
+        if os.path.basename(testcase.file) == "check-modules-case.test":
+            with tempfile.NamedTemporaryFile(prefix="test", dir=".") as temp_file:
+                temp_path = Path(temp_file.name)
+                if not temp_path.with_name(temp_path.name.upper()).exists():
+                    pytest.skip("File system is not case‐insensitive")
         if lxml is None and os.path.basename(testcase.file) == "check-reports.test":
             pytest.skip("Cannot import lxml. Is it installed?")
         incremental = (
diff --git a/mypy/test/testfscache.py b/mypy/test/testfscache.py
index 44b0d32f5797..529402dade96 100644
--- a/mypy/test/testfscache.py
+++ b/mypy/test/testfscache.py
@@ -4,7 +4,6 @@
 
 import os
 import shutil
-import sys
 import tempfile
 import unittest
 
@@ -83,7 +82,7 @@ def test_isfile_case_other_directory(self) -> None:
             assert self.isfile_case(os.path.join(other, "other_dir.py"))
             assert not self.isfile_case(os.path.join(other, "Other_Dir.py"))
             assert not self.isfile_case(os.path.join(other, "bar.py"))
-            if sys.platform in ("win32", "darwin"):
+            if os.path.exists(os.path.join(other, "PKG/other_dir.py")):
                 # We only check case for directories under our prefix, and since
                 # this path is not under the prefix, case difference is fine.
                 assert self.isfile_case(os.path.join(other, "PKG/other_dir.py"))

From 07d4a1bf9c73f39d6c05d6665c1fafc8f12c3e77 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Thu, 31 Jul 2025 14:21:01 -0400
Subject: [PATCH 1499/1617] feat: new mypyc primitive for weakref.ref (#19099)

This PR adds a new mypyc primitive for `weakref.ref`

I wasn't able to figure out what name mypyc expects for `weakref.proxy`,
so I took that out and will keep that for a separate PR later on. ref is
more commonly used than proxy anyway.

for later, I tried:
- weakref.proxy
- weakref.ProxyType
- weakref.weakproxy

no luck with those

also for later, I'll need to finish #19145 to add a primitive for
`weakref.ref.__call__`
---
 mypyc/primitives/registry.py         |  3 +-
 mypyc/primitives/weakref_ops.py      | 22 ++++++++++++
 mypyc/test-data/irbuild-weakref.test | 51 ++++++++++++++++++++++++++++
 mypyc/test-data/run-weakref.test     | 30 ++++++++++++++++
 mypyc/test/test_irbuild.py           |  1 +
 mypyc/test/test_run.py               |  1 +
 test-data/unit/lib-stub/weakref.pyi  | 11 ++++++
 7 files changed, 118 insertions(+), 1 deletion(-)
 create mode 100644 mypyc/primitives/weakref_ops.py
 create mode 100644 mypyc/test-data/irbuild-weakref.test
 create mode 100644 mypyc/test-data/run-weakref.test
 create mode 100644 test-data/unit/lib-stub/weakref.pyi

diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py
index 5e7ecb70f55d..07546663d08e 100644
--- a/mypyc/primitives/registry.py
+++ b/mypyc/primitives/registry.py
@@ -371,4 +371,5 @@ def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription:
 import mypyc.primitives.list_ops
 import mypyc.primitives.misc_ops
 import mypyc.primitives.str_ops
-import mypyc.primitives.tuple_ops  # noqa: F401
+import mypyc.primitives.tuple_ops
+import mypyc.primitives.weakref_ops  # noqa: F401
diff --git a/mypyc/primitives/weakref_ops.py b/mypyc/primitives/weakref_ops.py
new file mode 100644
index 000000000000..a7ac035b22a4
--- /dev/null
+++ b/mypyc/primitives/weakref_ops.py
@@ -0,0 +1,22 @@
+from mypyc.ir.ops import ERR_MAGIC
+from mypyc.ir.rtypes import object_rprimitive, pointer_rprimitive
+from mypyc.primitives.registry import function_op
+
+# Weakref operations
+
+new_ref_op = function_op(
+    name="weakref.ReferenceType",
+    arg_types=[object_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="PyWeakref_NewRef",
+    extra_int_constants=[(0, pointer_rprimitive)],
+    error_kind=ERR_MAGIC,
+)
+
+new_ref__with_callback_op = function_op(
+    name="weakref.ReferenceType",
+    arg_types=[object_rprimitive, object_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="PyWeakref_NewRef",
+    error_kind=ERR_MAGIC,
+)
diff --git a/mypyc/test-data/irbuild-weakref.test b/mypyc/test-data/irbuild-weakref.test
new file mode 100644
index 000000000000..58ac6417d297
--- /dev/null
+++ b/mypyc/test-data/irbuild-weakref.test
@@ -0,0 +1,51 @@
+[case testWeakrefRef]
+import weakref
+from typing import Any, Callable
+def f(x: object) -> object:
+    return weakref.ref(x)
+
+[out]
+def f(x):
+    x, r0 :: object
+L0:
+    r0 = PyWeakref_NewRef(x, 0)
+    return r0
+
+[case testWeakrefRefCallback]
+import weakref
+from typing import Any, Callable
+def f(x: object, cb: Callable[[object], Any]) -> object:
+    return weakref.ref(x, cb)
+
+[out]
+def f(x, cb):
+    x, cb, r0 :: object
+L0:
+    r0 = PyWeakref_NewRef(x, cb)
+    return r0
+
+[case testFromWeakrefRef]
+from typing import Any, Callable
+from weakref import ref
+def f(x: object) -> object:
+    return ref(x)
+
+[out]
+def f(x):
+    x, r0 :: object
+L0:
+    r0 = PyWeakref_NewRef(x, 0)
+    return r0
+
+[case testFromWeakrefRefCallback]
+from typing import Any, Callable
+from weakref import ref
+def f(x: object, cb: Callable[[object], Any]) -> object:
+    return ref(x, cb)
+
+[out]
+def f(x, cb):
+    x, cb, r0 :: object
+L0:
+    r0 = PyWeakref_NewRef(x, cb)
+    return r0
diff --git a/mypyc/test-data/run-weakref.test b/mypyc/test-data/run-weakref.test
new file mode 100644
index 000000000000..902c9e407ff4
--- /dev/null
+++ b/mypyc/test-data/run-weakref.test
@@ -0,0 +1,30 @@
+# Test cases for weakrefs (compile and run)
+
+[case testWeakrefRef]
+from weakref import ref
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class Object:
+    """some random weakreffable object"""
+    pass
+
+def test_weakref_ref():
+    obj = Object()
+    r = ref(obj)
+    assert r() is obj
+    obj = None
+    assert r() is None, r()
+
+def test_weakref_ref_with_callback():
+    obj = Object()
+    r = ref(obj, lambda x: x)
+    assert r() is obj
+    obj = None
+    assert r() is None, r()
+
+[file driver.py]
+from native import test_weakref_ref, test_weakref_ref_with_callback
+
+test_weakref_ref()
+test_weakref_ref_with_callback()
diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py
index 9c0ad06416a7..d8f974ef201b 100644
--- a/mypyc/test/test_irbuild.py
+++ b/mypyc/test/test_irbuild.py
@@ -53,6 +53,7 @@
     "irbuild-constant-fold.test",
     "irbuild-glue-methods.test",
     "irbuild-math.test",
+    "irbuild-weakref.test",
 ]
 
 if sys.version_info >= (3, 10):
diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py
index fcc24403df8e..5078426b977f 100644
--- a/mypyc/test/test_run.py
+++ b/mypyc/test/test_run.py
@@ -70,6 +70,7 @@
     "run-singledispatch.test",
     "run-attrs.test",
     "run-signatures.test",
+    "run-weakref.test",
     "run-python37.test",
     "run-python38.test",
 ]
diff --git a/test-data/unit/lib-stub/weakref.pyi b/test-data/unit/lib-stub/weakref.pyi
new file mode 100644
index 000000000000..34e01f4d48f1
--- /dev/null
+++ b/test-data/unit/lib-stub/weakref.pyi
@@ -0,0 +1,11 @@
+from collections.abc import Callable
+from typing import Any, Generic, TypeVar
+from typing_extensions import Self
+
+_T = TypeVar("_T")
+
+class ReferenceType(Generic[_T]):  # "weakref"
+    __callback__: Callable[[Self], Any]
+    def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ...
+
+ref = ReferenceType

From 7c4ec520f4b03d95d4bf26801f8541022728c43c Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 1 Aug 2025 18:15:42 +0100
Subject: [PATCH 1500/1617] Keep trivial instances and aliases during expansion
 (#19543)

This weirdly looking change consistently shows 1% performance
improvement on my machine (Python 3.12, compiled). The key here is to
_not_ create new objects for trivial instances and aliases (i.e. those
with no `.args`). The problem however is that some callers modify
expanded type aliases _in place_ (for better error locations). I updated
couple places discovered by tests, but there may be more.

I think we should go ahead, and then fix bugs exposed by this using
following strategy:
* Always use original aliases (not theirs expansions) as error locations
(see change in `semanal.py`).
* If above is not possible (see unpacked tuple change), then call
`t.copy_modified()` followed by `t.set_line()` manually.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypy/expandtype.py            |  5 ++--
 mypy/meet.py                  | 11 ++++----
 mypy/plugins/proper_plugin.py |  1 +
 mypy/semanal.py               |  8 +++---
 mypy/types.py                 | 51 +++++++++++++++--------------------
 5 files changed, 35 insertions(+), 41 deletions(-)

diff --git a/mypy/expandtype.py b/mypy/expandtype.py
index f704df3b010e..8433708eda44 100644
--- a/mypy/expandtype.py
+++ b/mypy/expandtype.py
@@ -210,8 +210,7 @@ def visit_erased_type(self, t: ErasedType) -> Type:
 
     def visit_instance(self, t: Instance) -> Type:
         if len(t.args) == 0:
-            # TODO: Why do we need to create a copy here?
-            return t.copy_modified()
+            return t
 
         args = self.expand_type_tuple_with_unpack(t.args)
 
@@ -525,6 +524,8 @@ def visit_type_type(self, t: TypeType) -> Type:
     def visit_type_alias_type(self, t: TypeAliasType) -> Type:
         # Target of the type alias cannot contain type variables (not bound by the type
         # alias itself), so we just expand the arguments.
+        if len(t.args) == 0:
+            return t
         args = self.expand_type_list_with_unpack(t.args)
         # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]?
         return t.copy_modified(args=args)
diff --git a/mypy/meet.py b/mypy/meet.py
index 2e238be7765e..fb35bce438ab 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -116,8 +116,11 @@ def meet_types(s: Type, t: Type) -> ProperType:
 def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
     """Return the declared type narrowed down to another type."""
     # TODO: check infinite recursion for aliases here.
-    if isinstance(narrowed, TypeGuardedType):  # type: ignore[misc]
-        # A type guard forces the new type even if it doesn't overlap the old.
+    if isinstance(narrowed, TypeGuardedType):
+        # A type guard forces the new type even if it doesn't overlap the old...
+        if is_proper_subtype(declared, narrowed.type_guard, ignore_promotions=True):
+            # ...unless it is a proper supertype of declared type.
+            return declared
         return narrowed.type_guard
 
     original_declared = declared
@@ -308,9 +311,7 @@ def is_overlapping_types(
     positives), for example: None only overlaps with explicitly optional types, Any
     doesn't overlap with anything except object, we don't ignore positional argument names.
     """
-    if isinstance(left, TypeGuardedType) or isinstance(  # type: ignore[misc]
-        right, TypeGuardedType
-    ):
+    if isinstance(left, TypeGuardedType) or isinstance(right, TypeGuardedType):
         # A type guard forces the new type even if it doesn't overlap the old.
         return True
 
diff --git a/mypy/plugins/proper_plugin.py b/mypy/plugins/proper_plugin.py
index f51685c80afa..0189bfbd22fc 100644
--- a/mypy/plugins/proper_plugin.py
+++ b/mypy/plugins/proper_plugin.py
@@ -107,6 +107,7 @@ def is_special_target(right: ProperType) -> bool:
             "mypy.types.DeletedType",
             "mypy.types.RequiredType",
             "mypy.types.ReadOnlyType",
+            "mypy.types.TypeGuardedType",
         ):
             # Special case: these are not valid targets for a type alias and thus safe.
             # TODO: introduce a SyntheticType base to simplify this?
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 1840e606af37..7cca406b661b 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1046,12 +1046,12 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType
         last_type = typ.arg_types[-1]
         if not isinstance(last_type, UnpackType):
             return typ
-        last_type = get_proper_type(last_type.type)
-        if not isinstance(last_type, TypedDictType):
+        p_last_type = get_proper_type(last_type.type)
+        if not isinstance(p_last_type, TypedDictType):
             self.fail("Unpack item in ** argument must be a TypedDict", last_type)
             new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)]
             return typ.copy_modified(arg_types=new_arg_types)
-        overlap = set(typ.arg_names) & set(last_type.items)
+        overlap = set(typ.arg_names) & set(p_last_type.items)
         # It is OK for TypedDict to have a key named 'kwargs'.
         overlap.discard(typ.arg_names[-1])
         if overlap:
@@ -1060,7 +1060,7 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType
             new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)]
             return typ.copy_modified(arg_types=new_arg_types)
         # OK, everything looks right now, mark the callable type as using unpack.
-        new_arg_types = typ.arg_types[:-1] + [last_type]
+        new_arg_types = typ.arg_types[:-1] + [p_last_type]
         return typ.copy_modified(arg_types=new_arg_types, unpack_kwargs=True)
 
     def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type: bool) -> None:
diff --git a/mypy/types.py b/mypy/types.py
index e9d299dbc8fc..4b5ef332ccf9 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -355,11 +355,7 @@ def _expand_once(self) -> Type:
             ):
                 mapping[tvar.id] = sub
 
-        new_tp = self.alias.target.accept(InstantiateAliasVisitor(mapping))
-        new_tp.accept(LocationSetter(self.line, self.column))
-        new_tp.line = self.line
-        new_tp.column = self.column
-        return new_tp
+        return self.alias.target.accept(InstantiateAliasVisitor(mapping))
 
     def _partial_expansion(self, nothing_args: bool = False) -> tuple[ProperType, bool]:
         # Private method mostly for debugging and testing.
@@ -3214,7 +3210,8 @@ def get_proper_type(typ: Type | None) -> ProperType | None:
     """
     if typ is None:
         return None
-    if isinstance(typ, TypeGuardedType):  # type: ignore[misc]
+    # TODO: this is an ugly hack, remove.
+    if isinstance(typ, TypeGuardedType):
         typ = typ.type_guard
     while isinstance(typ, TypeAliasType):
         typ = typ._expand_once()
@@ -3238,9 +3235,7 @@ def get_proper_types(
     if isinstance(types, list):
         typelist = types
         # Optimize for the common case so that we don't need to allocate anything
-        if not any(
-            isinstance(t, (TypeAliasType, TypeGuardedType)) for t in typelist  # type: ignore[misc]
-        ):
+        if not any(isinstance(t, (TypeAliasType, TypeGuardedType)) for t in typelist):
             return cast("list[ProperType]", typelist)
         return [get_proper_type(t) for t in typelist]
     else:
@@ -3260,7 +3255,6 @@ def get_proper_types(
     TypeTranslator as TypeTranslator,
     TypeVisitor as TypeVisitor,
 )
-from mypy.typetraverser import TypeTraverserVisitor
 
 
 class TypeStrVisitor(SyntheticTypeVisitor[str]):
@@ -3598,23 +3592,6 @@ def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[In
     return isinstance(t, Instance) and t.type.fullname in fullnames
 
 
-class LocationSetter(TypeTraverserVisitor):
-    # TODO: Should we update locations of other Type subclasses?
-    def __init__(self, line: int, column: int) -> None:
-        self.line = line
-        self.column = column
-
-    def visit_instance(self, typ: Instance) -> None:
-        typ.line = self.line
-        typ.column = self.column
-        super().visit_instance(typ)
-
-    def visit_type_alias_type(self, typ: TypeAliasType) -> None:
-        typ.line = self.line
-        typ.column = self.column
-        super().visit_type_alias_type(typ)
-
-
 class HasTypeVars(BoolTypeQuery):
     """Visitor for querying whether a type has a type variable component."""
 
@@ -3709,8 +3686,8 @@ def flatten_nested_unions(
 
     flat_items: list[Type] = []
     for t in typelist:
-        if handle_type_alias_type:
-            if not handle_recursive and isinstance(t, TypeAliasType) and t.is_recursive:
+        if handle_type_alias_type and isinstance(t, TypeAliasType):
+            if not handle_recursive and t.is_recursive:
                 tp: Type = t
             else:
                 tp = get_proper_type(t)
@@ -3757,7 +3734,21 @@ def flatten_nested_tuples(types: Iterable[Type]) -> list[Type]:
         if not isinstance(p_type, TupleType):
             res.append(typ)
             continue
-        res.extend(flatten_nested_tuples(p_type.items))
+        if isinstance(typ.type, TypeAliasType):
+            items = []
+            for item in p_type.items:
+                if (
+                    isinstance(item, ProperType)
+                    and isinstance(item, Instance)
+                    or isinstance(item, TypeAliasType)
+                ):
+                    if len(item.args) == 0:
+                        item = item.copy_modified()
+                        item.set_line(typ)
+                items.append(item)
+        else:
+            items = p_type.items
+        res.extend(flatten_nested_tuples(items))
     return res
 
 

From 45ee5a34a025e7b10be458bb6b6271e4b03c1138 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 2 Aug 2025 02:27:01 +0200
Subject: [PATCH 1501/1617] fix: prevent crash on dataclass with PEP695
 TypeVarTuple on py3.13 (#19565)

Fixes #19559.

Replaces ad-hoc Instance creation with regular `fill_typevars`.

Huge thanks to @A5rocks for the pointer!
---
 mypy/plugins/dataclasses.py         |  3 +--
 test-data/unit/check-python312.test | 11 +++++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index ee6f8889b894..e916ded01dd2 100644
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -410,13 +410,12 @@ def _add_dunder_replace(self, attributes: list[DataclassAttribute]) -> None:
             for attr in attributes
             if attr.is_in_init
         ]
-        type_vars = [tv for tv in self._cls.type_vars]
         add_method_to_class(
             self._api,
             self._cls,
             "__replace__",
             args=args,
-            return_type=Instance(self._cls.info, type_vars),
+            return_type=fill_typevars(self._cls.info),
         )
 
     def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> None:
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index bfd6334b5077..d275503dc411 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2084,3 +2084,14 @@ reveal_type(A1().x)  # N: Revealed type is "builtins.int"
 reveal_type(A2().x)  # N: Revealed type is "builtins.int"
 reveal_type(A3().x)  # N: Revealed type is "builtins.list[builtins.int]"
 [builtins fixtures/tuple.pyi]
+
+[case testDataclassWithTypeVarTuple]
+# flags: --python-version 3.13
+# https://github.com/python/mypy/issues/19559
+from typing import Callable
+from dataclasses import dataclass
+
+@dataclass
+class Test[*Ts, R]:
+    a: Callable[[*Ts], R]
+[builtins fixtures/dict.pyi]

From 8e02657be8217146142fd374a7229f239452f871 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 2 Aug 2025 15:53:34 +0100
Subject: [PATCH 1502/1617] Optimize bind_self() and deprecation checks
 (#19556)

This contains two related optimizations:
* Simplify deprecation check by removing several `bind_self()` calls and
instead restoring callable type definitions in `fixup.py`, and using
them during overload item matching.
* Consequently, best effort filtering in `bind_self()` should be not
needed anymore, since all non-trivial calls to `bind_self()` are now
after `check_self_arg()` calls.

There are also few micro-optimizations I noticed when looking at
relevant code. In total this gives around 1% on self-check.

Note: this may be not a no-op in some corner cases. If so, I will adjust
overload filtering in `check_self_arg()`.
---
 mypy/checker.py                     | 30 ++----------
 mypy/checker_shared.py              |  6 ---
 mypy/checkexpr.py                   | 27 +++--------
 mypy/checkmember.py                 | 74 +++++++++++++++++------------
 mypy/erasetype.py                   | 19 +++++---
 mypy/fixup.py                       |  2 +
 mypy/meet.py                        | 24 +++++-----
 mypy/nodes.py                       |  6 ++-
 mypy/typeops.py                     | 62 ++----------------------
 test-data/unit/check-selftype.test  | 27 +++++++++++
 test-data/unit/check-serialize.test |  1 +
 11 files changed, 114 insertions(+), 164 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index f201a767a860..35a67d188311 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -726,6 +726,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     assert isinstance(item, Decorator)
                     item_type = self.extract_callable_type(item.var.type, item)
                     if item_type is not None:
+                        item_type.definition = item
                         item_types.append(item_type)
                 if item_types:
                     defn.type = Overloaded(item_types)
@@ -4927,17 +4928,7 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None:
         inplace, method = infer_operator_assignment_method(lvalue_type, s.op)
         if inplace:
             # There is __ifoo__, treat as x = x.__ifoo__(y)
-            rvalue_type, method_type = self.expr_checker.check_op(method, lvalue_type, s.rvalue, s)
-            if isinstance(inst := get_proper_type(lvalue_type), Instance) and isinstance(
-                defn := inst.type.get_method(method), OverloadedFuncDef
-            ):
-                for item in defn.items:
-                    if (
-                        isinstance(item, Decorator)
-                        and isinstance(typ := item.func.type, CallableType)
-                        and (bind_self(typ) == method_type)
-                    ):
-                        self.warn_deprecated(item.func, s)
+            rvalue_type, _ = self.expr_checker.check_op(method, lvalue_type, s.rvalue, s)
             if not is_subtype(rvalue_type, lvalue_type):
                 self.msg.incompatible_operator_assignment(s.op, s)
         else:
@@ -7962,7 +7953,7 @@ def warn_deprecated(self, node: Node | None, context: Context) -> None:
             node = node.func
         if (
             isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo))
-            and ((deprecated := node.deprecated) is not None)
+            and (deprecated := node.deprecated) is not None
             and not self.is_typeshed_stub
             and not any(
                 node.fullname == p or node.fullname.startswith(f"{p}.")
@@ -7972,21 +7963,6 @@ def warn_deprecated(self, node: Node | None, context: Context) -> None:
             warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail
             warn(deprecated, context, code=codes.DEPRECATED)
 
-    def warn_deprecated_overload_item(
-        self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None
-    ) -> None:
-        """Warn if the overload item corresponding to the given callable is deprecated."""
-        target = get_proper_type(target)
-        if isinstance(node, OverloadedFuncDef) and isinstance(target, CallableType):
-            for item in node.items:
-                if isinstance(item, Decorator) and isinstance(
-                    candidate := item.func.type, CallableType
-                ):
-                    if selftype is not None and not node.is_static:
-                        candidate = bind_self(candidate, selftype)
-                    if candidate == target:
-                        self.warn_deprecated(item.func, context)
-
     # leafs
 
     def visit_pass_stmt(self, o: PassStmt, /) -> None:
diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index 7a5e9cb52c70..2a8fbdb0c9f1 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -253,12 +253,6 @@ def check_deprecated(self, node: Node | None, context: Context) -> None:
     def warn_deprecated(self, node: Node | None, context: Context) -> None:
         raise NotImplementedError
 
-    @abstractmethod
-    def warn_deprecated_overload_item(
-        self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None
-    ) -> None:
-        raise NotImplementedError
-
     @abstractmethod
     def type_is_iterable(self, type: Type) -> bool:
         raise NotImplementedError
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index ecae451299d7..bec34eef4983 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -129,7 +129,6 @@
     validate_instance,
 )
 from mypy.typeops import (
-    bind_self,
     callable_type,
     custom_special_method,
     erase_to_union_or_bound,
@@ -1517,15 +1516,6 @@ def check_call_expr_with_callee_type(
             object_type=object_type,
         )
         proper_callee = get_proper_type(callee_type)
-        if isinstance(e.callee, (NameExpr, MemberExpr)):
-            node = e.callee.node
-            if node is None and member is not None and isinstance(object_type, Instance):
-                if (symbol := object_type.type.get(member)) is not None:
-                    node = symbol.node
-            self.chk.check_deprecated(node, e)
-            self.chk.warn_deprecated_overload_item(
-                node, e, target=callee_type, selftype=object_type
-            )
         if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType):
             # Cache it for find_isinstance_check()
             if proper_callee.type_guard is not None:
@@ -2943,6 +2933,8 @@ def infer_overload_return_type(
                 # check for ambiguity due to 'Any' below.
                 if not args_contain_any:
                     self.chk.store_types(m)
+                    if isinstance(infer_type, ProperType) and isinstance(infer_type, CallableType):
+                        self.chk.check_deprecated(infer_type.definition, context)
                     return ret_type, infer_type
                 p_infer_type = get_proper_type(infer_type)
                 if isinstance(p_infer_type, CallableType):
@@ -2979,6 +2971,11 @@ def infer_overload_return_type(
         else:
             # Success! No ambiguity; return the first match.
             self.chk.store_types(type_maps[0])
+            inferred_callable = inferred_types[0]
+            if isinstance(inferred_callable, ProperType) and isinstance(
+                inferred_callable, CallableType
+            ):
+                self.chk.check_deprecated(inferred_callable.definition, context)
             return return_types[0], inferred_types[0]
 
     def overload_erased_call_targets(
@@ -4103,16 +4100,6 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
                 errors.append(local_errors.filtered_errors())
                 results.append(result)
             else:
-                if isinstance(obj, Instance) and isinstance(
-                    defn := obj.type.get_method(name), OverloadedFuncDef
-                ):
-                    for item in defn.items:
-                        if (
-                            isinstance(item, Decorator)
-                            and isinstance(typ := item.func.type, CallableType)
-                            and bind_self(typ) == result[1]
-                        ):
-                            self.chk.check_deprecated(item.func, context)
                 return result
 
         # We finish invoking above operators and no early return happens. Therefore,
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 7eedab2e399a..8447dfc7fe64 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -5,7 +5,7 @@
 from collections.abc import Sequence
 from typing import Callable, TypeVar, cast
 
-from mypy import message_registry, state, subtypes
+from mypy import message_registry, state
 from mypy.checker_shared import TypeCheckerSharedApi
 from mypy.erasetype import erase_typevars
 from mypy.expandtype import (
@@ -14,6 +14,7 @@
     freshen_all_functions_type_vars,
 )
 from mypy.maptype import map_instance_to_supertype
+from mypy.meet import is_overlapping_types
 from mypy.messages import MessageBuilder
 from mypy.nodes import (
     ARG_POS,
@@ -39,6 +40,7 @@
     is_final_node,
 )
 from mypy.plugin import AttributeContext
+from mypy.subtypes import is_subtype
 from mypy.typeops import (
     bind_self,
     erase_to_bound,
@@ -745,10 +747,8 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
         callable_name=callable_name,
     )
 
-    mx.chk.check_deprecated(dunder_get, mx.context)
-    mx.chk.warn_deprecated_overload_item(
-        dunder_get, mx.context, target=inferred_dunder_get_type, selftype=descriptor_type
-    )
+    # Search for possible deprecations:
+    mx.chk.warn_deprecated(dunder_get, mx.context)
 
     inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type)
     if isinstance(inferred_dunder_get_type, AnyType):
@@ -825,10 +825,7 @@ def analyze_descriptor_assign(descriptor_type: Instance, mx: MemberContext) -> T
     )
 
     # Search for possible deprecations:
-    mx.chk.check_deprecated(dunder_set, mx.context)
-    mx.chk.warn_deprecated_overload_item(
-        dunder_set, mx.context, target=inferred_dunder_set_type, selftype=descriptor_type
-    )
+    mx.chk.warn_deprecated(dunder_set, mx.context)
 
     # In the following cases, a message already will have been recorded in check_call.
     if (not isinstance(inferred_dunder_set_type, CallableType)) or (
@@ -1053,6 +1050,7 @@ def f(self: S) -> T: ...
     new_items = []
     if is_classmethod:
         dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type)
+    p_dispatched_arg_type = get_proper_type(dispatched_arg_type)
 
     for item in items:
         if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
@@ -1061,28 +1059,42 @@ def f(self: S) -> T: ...
             # This is pretty bad, so just return the original signature if
             # there is at least one such error.
             return functype
-        else:
-            selfarg = get_proper_type(item.arg_types[0])
-            # This matches similar special-casing in bind_self(), see more details there.
-            self_callable = name == "__call__" and isinstance(selfarg, CallableType)
-            if self_callable or subtypes.is_subtype(
-                dispatched_arg_type,
-                # This level of erasure matches the one in checker.check_func_def(),
-                # better keep these two checks consistent.
-                erase_typevars(erase_to_bound(selfarg)),
-                # This is to work around the fact that erased ParamSpec and TypeVarTuple
-                # callables are not always compatible with non-erased ones both ways.
-                always_covariant=any(
-                    not isinstance(tv, TypeVarType) for tv in get_all_type_vars(selfarg)
-                ),
-                ignore_pos_arg_names=True,
-            ):
-                new_items.append(item)
-            elif isinstance(selfarg, ParamSpecType):
-                # TODO: This is not always right. What's the most reasonable thing to do here?
-                new_items.append(item)
-            elif isinstance(selfarg, TypeVarTupleType):
-                raise NotImplementedError
+        selfarg = get_proper_type(item.arg_types[0])
+        if isinstance(selfarg, Instance) and isinstance(p_dispatched_arg_type, Instance):
+            if selfarg.type is p_dispatched_arg_type.type and selfarg.args:
+                if not is_overlapping_types(p_dispatched_arg_type, selfarg):
+                    # This special casing is needed since `actual <: erased(template)`
+                    # logic below doesn't always work, and a more correct approach may
+                    # be tricky.
+                    continue
+        new_items.append(item)
+
+    if new_items:
+        items = new_items
+        new_items = []
+
+    for item in items:
+        selfarg = get_proper_type(item.arg_types[0])
+        # This matches similar special-casing in bind_self(), see more details there.
+        self_callable = name == "__call__" and isinstance(selfarg, CallableType)
+        if self_callable or is_subtype(
+            dispatched_arg_type,
+            # This level of erasure matches the one in checker.check_func_def(),
+            # better keep these two checks consistent.
+            erase_typevars(erase_to_bound(selfarg)),
+            # This is to work around the fact that erased ParamSpec and TypeVarTuple
+            # callables are not always compatible with non-erased ones both ways.
+            always_covariant=any(
+                not isinstance(tv, TypeVarType) for tv in get_all_type_vars(selfarg)
+            ),
+            ignore_pos_arg_names=True,
+        ):
+            new_items.append(item)
+        elif isinstance(selfarg, ParamSpecType):
+            # TODO: This is not always right. What's the most reasonable thing to do here?
+            new_items.append(item)
+        elif isinstance(selfarg, TypeVarTupleType):
+            raise NotImplementedError
     if not new_items:
         # Choose first item for the message (it may be not very helpful for overloads).
         msg.incompatible_self_argument(
diff --git a/mypy/erasetype.py b/mypy/erasetype.py
index 6c47670d6687..3f33ea1648f0 100644
--- a/mypy/erasetype.py
+++ b/mypy/erasetype.py
@@ -145,29 +145,34 @@ def erase_typevars(t: Type, ids_to_erase: Container[TypeVarId] | None = None) ->
     or just the ones in the provided collection.
     """
 
+    if ids_to_erase is None:
+        return t.accept(TypeVarEraser(None, AnyType(TypeOfAny.special_form)))
+
     def erase_id(id: TypeVarId) -> bool:
-        if ids_to_erase is None:
-            return True
         return id in ids_to_erase
 
     return t.accept(TypeVarEraser(erase_id, AnyType(TypeOfAny.special_form)))
 
 
+def erase_meta_id(id: TypeVarId) -> bool:
+    return id.is_meta_var()
+
+
 def replace_meta_vars(t: Type, target_type: Type) -> Type:
     """Replace unification variables in a type with the target type."""
-    return t.accept(TypeVarEraser(lambda id: id.is_meta_var(), target_type))
+    return t.accept(TypeVarEraser(erase_meta_id, target_type))
 
 
 class TypeVarEraser(TypeTranslator):
     """Implementation of type erasure"""
 
-    def __init__(self, erase_id: Callable[[TypeVarId], bool], replacement: Type) -> None:
+    def __init__(self, erase_id: Callable[[TypeVarId], bool] | None, replacement: Type) -> None:
         super().__init__()
         self.erase_id = erase_id
         self.replacement = replacement
 
     def visit_type_var(self, t: TypeVarType) -> Type:
-        if self.erase_id(t.id):
+        if self.erase_id is None or self.erase_id(t.id):
             return self.replacement
         return t
 
@@ -212,12 +217,12 @@ def visit_callable_type(self, t: CallableType) -> Type:
         return result
 
     def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type:
-        if self.erase_id(t.id):
+        if self.erase_id is None or self.erase_id(t.id):
             return t.tuple_fallback.copy_modified(args=[self.replacement])
         return t
 
     def visit_param_spec(self, t: ParamSpecType) -> Type:
-        if self.erase_id(t.id):
+        if self.erase_id is None or self.erase_id(t.id):
             return self.replacement
         return t
 
diff --git a/mypy/fixup.py b/mypy/fixup.py
index 0e9c186fd42a..c0f8e401777c 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -165,6 +165,8 @@ def visit_func_def(self, func: FuncDef) -> None:
             func.info = self.current_info
         if func.type is not None:
             func.type.accept(self.type_fixer)
+            if isinstance(func.type, CallableType):
+                func.type.definition = func
 
     def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
         if self.current_info is not None:
diff --git a/mypy/meet.py b/mypy/meet.py
index fb35bce438ab..349c15e668c3 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -294,6 +294,18 @@ def is_object(t: ProperType) -> bool:
     return isinstance(t, Instance) and t.type.fullname == "builtins.object"
 
 
+def is_none_typevarlike_overlap(t1: ProperType, t2: ProperType) -> bool:
+    return isinstance(t1, NoneType) and isinstance(t2, TypeVarLikeType)
+
+
+def is_none_object_overlap(t1: ProperType, t2: ProperType) -> bool:
+    return (
+        isinstance(t1, NoneType)
+        and isinstance(t2, Instance)
+        and t2.type.fullname == "builtins.object"
+    )
+
+
 def is_overlapping_types(
     left: Type,
     right: Type,
@@ -383,14 +395,6 @@ def _is_overlapping_types(left: Type, right: Type) -> bool:
     ):
         return True
 
-    def is_none_object_overlap(t1: Type, t2: Type) -> bool:
-        t1, t2 = get_proper_types((t1, t2))
-        return (
-            isinstance(t1, NoneType)
-            and isinstance(t2, Instance)
-            and t2.type.fullname == "builtins.object"
-        )
-
     if overlap_for_overloads:
         if is_none_object_overlap(left, right) or is_none_object_overlap(right, left):
             return False
@@ -420,10 +424,6 @@ def _is_subtype(left: Type, right: Type) -> bool:
     # If both types are singleton variants (and are not TypeVarLikes), we've hit the base case:
     # we skip these checks to avoid infinitely recursing.
 
-    def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool:
-        t1, t2 = get_proper_types((t1, t2))
-        return isinstance(t1, NoneType) and isinstance(t2, TypeVarLikeType)
-
     if prohibit_none_typevar_overlap:
         if is_none_typevarlike_overlap(left, right) or is_none_typevarlike_overlap(right, left):
             return False
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 921620866a06..011e4e703a0c 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -595,12 +595,14 @@ def is_trivial_self(self) -> bool:
         """
         if self._is_trivial_self is not None:
             return self._is_trivial_self
-        for item in self.items:
+        for i, item in enumerate(self.items):
+            # Note: bare @property is removed in visit_decorator().
+            trivial = 1 if i > 0 or not self.is_property else 0
             if isinstance(item, FuncDef):
                 if not item.is_trivial_self:
                     self._is_trivial_self = False
                     return False
-            elif item.decorators or not item.func.is_trivial_self:
+            elif len(item.decorators) > trivial or not item.func.is_trivial_self:
                 self._is_trivial_self = False
                 return False
         self._is_trivial_self = True
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 1c22a1711944..75213bd93674 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -16,7 +16,6 @@
 from mypy.expandtype import expand_type, expand_type_by_instance
 from mypy.maptype import map_instance_to_supertype
 from mypy.nodes import (
-    ARG_OPT,
     ARG_POS,
     ARG_STAR,
     ARG_STAR2,
@@ -421,27 +420,9 @@ class B(A): pass
 
     """
     if isinstance(method, Overloaded):
-        items = []
-        original_type = get_proper_type(original_type)
-        for c in method.items:
-            if isinstance(original_type, Instance):
-                # Filter based on whether declared self type can match actual object type.
-                # For example, if self has type C[int] and method is accessed on a C[str] value,
-                # omit this item. This is best effort since bind_self can be called in many
-                # contexts, and doing complete validation might trigger infinite recursion.
-                #
-                # Note that overload item filtering normally happens elsewhere. This is needed
-                # at least during constraint inference.
-                keep = is_valid_self_type_best_effort(c, original_type)
-            else:
-                keep = True
-            if keep:
-                items.append(bind_self(c, original_type, is_classmethod, ignore_instances))
-        if len(items) == 0:
-            # If no item matches, returning all items helps avoid some spurious errors
-            items = [
-                bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items
-            ]
+        items = [
+            bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items
+        ]
         return cast(F, Overloaded(items))
     assert isinstance(method, CallableType)
     func: CallableType = method
@@ -510,43 +491,6 @@ class B(A): pass
     return cast(F, res)
 
 
-def is_valid_self_type_best_effort(c: CallableType, self_type: Instance) -> bool:
-    """Quickly check if self_type might match the self in a callable.
-
-    Avoid performing any complex type operations. This is performance-critical.
-
-    Default to returning True if we don't know (or it would be too expensive).
-    """
-    if (
-        self_type.args
-        and c.arg_types
-        and isinstance((arg_type := get_proper_type(c.arg_types[0])), Instance)
-        and c.arg_kinds[0] in (ARG_POS, ARG_OPT)
-        and arg_type.args
-        and self_type.type.fullname != "functools._SingleDispatchCallable"
-    ):
-        if self_type.type is not arg_type.type:
-            # We can't map to supertype, since it could trigger expensive checks for
-            # protocol types, so we consevatively assume this is fine.
-            return True
-
-        # Fast path: no explicit annotation on self
-        if all(
-            (
-                type(arg) is TypeVarType
-                and type(arg.upper_bound) is Instance
-                and arg.upper_bound.type.fullname == "builtins.object"
-            )
-            for arg in arg_type.args
-        ):
-            return True
-
-        from mypy.meet import is_overlapping_types
-
-        return is_overlapping_types(self_type, c.arg_types[0])
-    return True
-
-
 def erase_to_bound(t: Type) -> Type:
     # TODO: use value restrictions to produce a union?
     t = get_proper_type(t)
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index 05c34eb70796..d99c5ee354a4 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2282,3 +2282,30 @@ class Check:
 reveal_type(Check.foo())  # N: Revealed type is "def () -> __main__.Check"
 reveal_type(Check().foo())  # N: Revealed type is "__main__.Check"
 [builtins fixtures/tuple.pyi]
+
+[case testSelfTypeUpperBoundFiler]
+from typing import Generic, TypeVar, overload, Sequence
+
+class B: ...
+class C(B): ...
+
+TB = TypeVar("TB", bound=B)
+TC = TypeVar("TC", bound=C)
+
+class G(Generic[TB]):
+    @overload
+    def test(self: G[TC]) -> list[TC]: ...
+    @overload
+    def test(self: G[TB]) -> Sequence[TB]: ...
+    def test(self):
+        ...
+
+class D1(B): ...
+class D2(C): ...
+
+gb: G[D1]
+gc: G[D2]
+
+reveal_type(gb.test())  # N: Revealed type is "typing.Sequence[__main__.D1]"
+reveal_type(gc.test())  # N: Revealed type is "builtins.list[__main__.D2]"
+[builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test
index 63d9ccfc80cb..5265832f5f27 100644
--- a/test-data/unit/check-serialize.test
+++ b/test-data/unit/check-serialize.test
@@ -158,6 +158,7 @@ def f(__x: int) -> None: pass
 [out2]
 tmp/a.py:3: error: Argument 1 to "f" has incompatible type "str"; expected "int"
 tmp/a.py:4: error: Unexpected keyword argument "__x" for "f"
+tmp/b.py: note: "f" defined here
 
 [case testSerializeArgumentKindsErrors]
 import a

From 2ce57952f7f0bb63d24dff5eab3f3933a7fcf28f Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sat, 2 Aug 2025 11:04:56 -0400
Subject: [PATCH 1503/1617] Update crash issue template to use syntax
 highlighting in code blocks (#19527)

People often forget or don't know how to add syntax highlighting to
markdown code blocks (especially for tracebacks!). Let's help them out
by pre-filling the template with code blocks that have the right
language hints.
---
 .github/ISSUE_TEMPLATE/crash.md | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md
index fed16a8d28ac..ea82a9a28642 100644
--- a/.github/ISSUE_TEMPLATE/crash.md
+++ b/.github/ISSUE_TEMPLATE/crash.md
@@ -15,7 +15,7 @@ labels: "crash"
 
 **Traceback**
 
-```
+```python-traceback
 (Insert traceback and other messages from mypy here -- use `--show-traceback`.)
 ```
 
@@ -25,6 +25,11 @@ labels: "crash"
 appreciated.  We also very much appreciate it if you try to narrow the
 source down to a small stand-alone example.)
 
+```python
+# Ideally, a small sample program that demonstrates the problem.
+# Or even better, a reproducible playground link https://mypy-play.net/ (use the "Gist" button)
+```
+
 **Your Environment**
 
 

From ac3e240029d74ee0975f4b64131e79e48f3c0d07 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 2 Aug 2025 17:47:38 +0100
Subject: [PATCH 1504/1617] Add internal flag to disable expression cache
 (#19569)

---
 mypy/checkexpr.py | 6 ++++--
 mypy/main.py      | 7 ++++---
 mypy/options.py   | 2 ++
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index bec34eef4983..740efb0d2ee4 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6030,8 +6030,10 @@ def accept(
             # We cannot use cache inside lambdas, because they skip immediate type
             # context, and use enclosing one, see infer_lambda_type_using_context().
             # TODO: consider using cache for more expression kinds.
-            elif isinstance(node, (CallExpr, ListExpr, TupleExpr, DictExpr, OpExpr)) and not (
-                self.in_lambda_expr or self.chk.current_node_deferred
+            elif (
+                isinstance(node, (CallExpr, ListExpr, TupleExpr, DictExpr, OpExpr))
+                and not (self.in_lambda_expr or self.chk.current_node_deferred)
+                and not self.chk.options.disable_expression_cache
             ):
                 if (node, type_context) in self.expr_cache:
                     binder_version, typ, messages, type_map = self.expr_cache[(node, type_context)]
diff --git a/mypy/main.py b/mypy/main.py
index a407a88d3ac1..6e307ab25c48 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1090,14 +1090,15 @@ def add_invertible_flag(
         help="Use a custom typing module",
     )
     internals_group.add_argument(
-        "--old-type-inference",
-        action="store_true",
-        help="Disable new experimental type inference algorithm",
+        "--old-type-inference", action="store_true", help=argparse.SUPPRESS
     )
     # Deprecated reverse variant of the above.
     internals_group.add_argument(
         "--new-type-inference", action="store_true", help=argparse.SUPPRESS
     )
+    internals_group.add_argument(
+        "--disable-expression-cache", action="store_true", help=argparse.SUPPRESS
+    )
     parser.add_argument(
         "--enable-incomplete-feature",
         action="append",
diff --git a/mypy/options.py b/mypy/options.py
index 4a89ef529c07..be61059be5c6 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -395,6 +395,8 @@ def __init__(self) -> None:
         self.old_type_inference = False
         # Deprecated reverse version of the above, do not use.
         self.new_type_inference = False
+        # Disable expression cache (for debugging).
+        self.disable_expression_cache = False
         # Export line-level, limited, fine-grained dependency information in cache data
         # (undocumented feature).
         self.export_ref_info = False

From c5a4efe008f943e379c5b3ee7334e28d9475eb43 Mon Sep 17 00:00:00 2001
From: Ali Hamdan 
Date: Sat, 2 Aug 2025 19:05:46 +0200
Subject: [PATCH 1505/1617] Add stubtest test for property aliases (#19567)

Closes #19509

Bisect show that the issue was fixed by #19297
---
 mypy/test/teststubtest.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index 7925f2a6bd3e..3b19063f08c8 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -850,11 +850,13 @@ def test_property(self) -> Iterator[Case]:
             class Good:
                 @property
                 def read_only_attr(self) -> int: ...
+                read_only_attr_alias = read_only_attr
             """,
             runtime="""
             class Good:
                 @property
                 def read_only_attr(self): return 1
+                read_only_attr_alias = read_only_attr
             """,
             error=None,
         )
@@ -916,6 +918,7 @@ class Z:
                 def read_write_attr(self) -> int: ...
                 @read_write_attr.setter
                 def read_write_attr(self, val: int) -> None: ...
+                read_write_attr_alias = read_write_attr
             """,
             runtime="""
             class Z:
@@ -923,6 +926,7 @@ class Z:
                 def read_write_attr(self): return self._val
                 @read_write_attr.setter
                 def read_write_attr(self, val): self._val = val
+                read_write_attr_alias = read_write_attr
             """,
             error=None,
         )

From e8147f2bdcd8899109fe1a9cffc770c436de95dc Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sat, 2 Aug 2025 13:10:22 -0400
Subject: [PATCH 1506/1617] [stubtest] Allow runtime-existing aliases of types
 marked as `@type_check_only` (#19568)

In typeshed, there's a few cases of stubs like this:
```python
class _DoesNotExist: ...  # does not exist at runtime

if sys.version_info >= (3, X):
    Exists = _DoesNotExist
```
Ideally, it would be nice to mark `_DoesNotExit` as `@type_check_only`
to make it clear that this type isn't available at runtime. However,
this currently can't be done, because doing so will make stubtest think
that `Exists` is also `@type_check_only`, which sets off alarm bells due
to `Exists` being available at runtime.

This PR makes it so stubtest doesn't consider `@type_check_only`-status
when checking type alias targets, making it possible to mark types like
the above as `@type_check_only`.
---
 mypy/stubtest.py          | 10 +++++++---
 mypy/test/teststubtest.py | 11 +++++++++++
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index d16e491fb1ab..ef8c8dc318e1 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -506,9 +506,13 @@ def _verify_metaclass(
 
 @verify.register(nodes.TypeInfo)
 def verify_typeinfo(
-    stub: nodes.TypeInfo, runtime: MaybeMissing[type[Any]], object_path: list[str]
+    stub: nodes.TypeInfo,
+    runtime: MaybeMissing[type[Any]],
+    object_path: list[str],
+    *,
+    is_alias_target: bool = False,
 ) -> Iterator[Error]:
-    if stub.is_type_check_only:
+    if stub.is_type_check_only and not is_alias_target:
         # This type only exists in stubs, we only check that the runtime part
         # is missing. Other checks are not required.
         if not isinstance(runtime, Missing):
@@ -1449,7 +1453,7 @@ def verify_typealias(
         # Okay, either we couldn't construct a fullname
         # or the fullname of the stub didn't match the fullname of the runtime.
         # Fallback to a full structural check of the runtime vis-a-vis the stub.
-        yield from verify(stub_origin, runtime_origin, object_path)
+        yield from verify_typeinfo(stub_origin, runtime_origin, object_path, is_alias_target=True)
         return
     if isinstance(stub_target, mypy.types.UnionType):
         # complain if runtime is not a Union or UnionType
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index 3b19063f08c8..b071c0ee8ab6 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -2472,6 +2472,17 @@ def func2() -> None: ...
             runtime="def func2() -> None: ...",
             error="func2",
         )
+        # A type that exists at runtime is allowed to alias a type marked
+        # as '@type_check_only' in the stubs.
+        yield Case(
+            stub="""
+            @type_check_only
+            class _X1: ...
+            X2 = _X1
+            """,
+            runtime="class X2: ...",
+            error=None,
+        )
 
 
 def remove_color_code(s: str) -> str:

From 82ad62cfed5293e377984c1783c8367aed9fd4ed Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 2 Aug 2025 14:43:36 -0700
Subject: [PATCH 1507/1617] [docs] Include a real listing of the flags strict
 enables in the online documentation (#19062)

Fixes #19061

Currently, https://mypy.readthedocs.io/en/stable/command_line.html just
says

> You can see the list of flags enabled by strict mode in the full [mypy
--help](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-h)
output.

Which makes cross-referencing the documentation difficult. Instead,
there should be the same list there as appears when running `mypy
--help`: eg

> --warn-unused-configs, --disallow-any-generics,
--disallow-subclassing-any, --disallow-untyped-
                            calls, --disallow-untyped-defs, --disallow-
incomplete-defs, --check-untyped-defs, --disallow-
                            untyped-decorators, --warn-redundant-casts,
--warn-unused-ignores, --warn-return-any, --no-
implicit-reexport, --strict-equality, --extra-
                            checks

Ideally this section would be automatically generated from the code.
---
 docs/source/command_line.rst |  8 ++++++
 docs/source/html_builder.py  | 22 ++++++++++++++
 mypy/main.py                 | 56 ++++++++++++++++++++++++------------
 3 files changed, 67 insertions(+), 19 deletions(-)

diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index 697e0fb69eed..db2407e17df8 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -812,6 +812,14 @@ of the above sections.
     Note: the exact list of flags enabled by running :option:`--strict` may change
     over time.
 
+    .. include:: strict_list.rst
+    ..
+        The above file is autogenerated and included during html generation.
+        (That's an include directive, and this is a comment.)
+        It would be fine to generate it at some other time instead,
+        theoretically, but we already had a convenient hook during html gen.
+
+
 .. option:: --disable-error-code
 
     This flag allows disabling one or multiple error codes globally.
diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py
index ea3594e0617b..387f7f13b4c2 100644
--- a/docs/source/html_builder.py
+++ b/docs/source/html_builder.py
@@ -11,16 +11,37 @@
 from sphinx.builders.html import StandaloneHTMLBuilder
 from sphinx.environment import BuildEnvironment
 
+from mypy.main import define_options
+
 
 class MypyHTMLBuilder(StandaloneHTMLBuilder):
+    strict_file: Path
+
     def __init__(self, app: Sphinx, env: BuildEnvironment) -> None:
         super().__init__(app, env)
         self._ref_to_doc = {}
+        self.strict_file = Path(self.srcdir) / "strict_list.rst"
+        self._add_strict_list()
 
     def write_doc(self, docname: str, doctree: document) -> None:
         super().write_doc(docname, doctree)
         self._ref_to_doc.update({_id: docname for _id in doctree.ids})
 
+    def _add_strict_list(self) -> None:
+        strict_flags: list[str]
+        _, strict_flags, _ = define_options()
+        strict_part = ", ".join(f":option:`{s} `" for s in strict_flags)
+        if (
+            not strict_part
+            or strict_part.isspace()
+            or len(strict_part) < 20
+            or len(strict_part) > 2000
+        ):
+            raise ValueError(f"{strict_part=}, which doesn't look right (by a simple heuristic).")
+        self.strict_file.write_text(
+            "For this version of mypy, the list of flags enabled by strict is: " + strict_part
+        )
+
     def _verify_error_codes(self) -> None:
         from mypy.errorcodes import error_codes
 
@@ -55,6 +76,7 @@ def _write_ref_redirector(self) -> None:
     def finish(self) -> None:
         super().finish()
         self._write_ref_redirector()
+        self.strict_file.unlink()
 
 
 def setup(app: Sphinx) -> dict[str, Any]:
diff --git a/mypy/main.py b/mypy/main.py
index 6e307ab25c48..2fbb9671e721 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -462,24 +462,18 @@ def __call__(
         parser.exit()
 
 
-def process_options(
-    args: list[str],
-    stdout: TextIO | None = None,
-    stderr: TextIO | None = None,
-    require_targets: bool = True,
-    server_options: bool = False,
-    fscache: FileSystemCache | None = None,
+def define_options(
     program: str = "mypy",
     header: str = HEADER,
-) -> tuple[list[BuildSource], Options]:
-    """Parse command line arguments.
-
-    If a FileSystemCache is passed in, and package_root options are given,
-    call fscache.set_package_root() to set the cache's package root.
-    """
-    stdout = stdout or sys.stdout
-    stderr = stderr or sys.stderr
-
+    stdout: TextIO = sys.stdout,
+    stderr: TextIO = sys.stderr,
+    server_options: bool = False,
+) -> tuple[CapturableArgumentParser, list[str], list[tuple[str, bool]]]:
+    """Define the options in the parser (by calling a bunch of methods that express/build our desired command-line flags).
+    Returns a tuple of:
+      a parser object, that can parse command line arguments to mypy (expected consumer: main's process_options),
+      a list of what flags are strict (expected consumer: docs' html_builder's _add_strict_list),
+      strict_flag_assignments (expected consumer: main's process_options)."""
     parser = CapturableArgumentParser(
         prog=program,
         usage=header,
@@ -1342,6 +1336,32 @@ def add_invertible_flag(
         dest="special-opts:files",
         help="Type-check given files or directories",
     )
+    return parser, strict_flag_names, strict_flag_assignments
+
+
+def process_options(
+    args: list[str],
+    stdout: TextIO | None = None,
+    stderr: TextIO | None = None,
+    require_targets: bool = True,
+    server_options: bool = False,
+    fscache: FileSystemCache | None = None,
+    program: str = "mypy",
+    header: str = HEADER,
+) -> tuple[list[BuildSource], Options]:
+    """Parse command line arguments.
+
+    If a FileSystemCache is passed in, and package_root options are given,
+    call fscache.set_package_root() to set the cache's package root.
+
+    Returns a tuple of: a list of source files, an Options collected from flags.
+    """
+    stdout = stdout if stdout is not None else sys.stdout
+    stderr = stderr if stderr is not None else sys.stderr
+
+    parser, _, strict_flag_assignments = define_options(
+        program, header, stdout, stderr, server_options
+    )
 
     # Parse arguments once into a dummy namespace so we can get the
     # filename for the config file and know if the user requested all strict options.
@@ -1526,11 +1546,9 @@ def set_strict_flags() -> None:
             targets.extend(p_targets)
         for m in special_opts.modules:
             targets.append(BuildSource(None, m, None))
-        return targets, options
     elif special_opts.command:
         options.build_type = BuildType.PROGRAM_TEXT
         targets = [BuildSource(None, None, "\n".join(special_opts.command))]
-        return targets, options
     else:
         try:
             targets = create_source_list(special_opts.files, options, fscache)
@@ -1539,7 +1557,7 @@ def set_strict_flags() -> None:
         # exceptions of different types.
         except InvalidSourceList as e2:
             fail(str(e2), stderr, options)
-        return targets, options
+    return targets, options
 
 
 def process_package_roots(

From b4102f2e8e2aa0634c4249161081eaba078dcb1b Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 2 Aug 2025 14:47:22 -0700
Subject: [PATCH 1508/1617] [docs] Update common_issues.rst: mention orjson in
 the Mypy slow section (#19058)

This mostly just copies the verbiage from the release blog post.
---
 docs/source/common_issues.rst | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst
index 96d73e5f0399..266d0c5b2c80 100644
--- a/docs/source/common_issues.rst
+++ b/docs/source/common_issues.rst
@@ -218,6 +218,14 @@ daemon `, which can speed up incremental mypy runtimes by
 a factor of 10 or more. :ref:`Remote caching ` can
 make cold mypy runs several times faster.
 
+Furthermore: as of `mypy 1.13 `_,
+mypy allows use of the orjson library for handling the cache instead of the stdlib json, for
+improved performance. You can ensure the presence of orjson using the faster-cache extra:
+
+    python3 -m pip install -U mypy[faster-cache]
+
+Mypy may depend on orjson by default in the future.
+
 Types of empty collections
 --------------------------
 

From c962993db73fc90081c5a06381b6032acf7c0e66 Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 2 Aug 2025 14:56:00 -0700
Subject: [PATCH 1509/1617] Mention in the Any documentation how object is
 preferable (#19103)

This implements a suggestion in
https://github.com/python/mypy/issues/9153#issuecomment-1837446187,
which I thought was a good idea.

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
---
 docs/source/dynamic_typing.rst | 3 ++-
 docs/source/kinds_of_types.rst | 5 +++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst
index 304e25c085a8..da40142d377d 100644
--- a/docs/source/dynamic_typing.rst
+++ b/docs/source/dynamic_typing.rst
@@ -1,6 +1,5 @@
 .. _dynamic-typing:
 
-
 Dynamically typed code
 ======================
 
@@ -94,6 +93,8 @@ third party libraries that mypy does not know about. This is particularly the ca
 when using the :option:`--ignore-missing-imports `
 flag. See :ref:`fix-missing-imports` for more information about this.
 
+.. _any-vs-object:
+
 Any vs. object
 --------------
 
diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst
index 54693cddf953..8e721c0fb321 100644
--- a/docs/source/kinds_of_types.rst
+++ b/docs/source/kinds_of_types.rst
@@ -41,6 +41,11 @@ operations are permitted on the value, and the operations are only checked
 at runtime. You can use ``Any`` as an "escape hatch" when you can't use
 a more precise type for some reason.
 
+This should not be confused with the
+:py:class:`object` type, which represents the set of all values.
+Unlike ``object``, ``Any`` introduces type unsafety — see
+:ref:`any-vs-object` for more.
+
 ``Any`` is compatible with every other type, and vice versa. You can freely
 assign a value of type ``Any`` to a variable with a more precise type:
 

From 813b4d11152e77bf3c96e7f762df2dcd041667db Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 2 Aug 2025 15:01:46 -0700
Subject: [PATCH 1510/1617] Fix a bug where inline configurations of error
 codes would lose their values if accompanied by another inline configuration.
 (#19075)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The following code produces a name-defined code error, despite our
instructions. How can it be?!

```py3
# mypy: disable-error-code=name-defined
# mypy: strict-equality
a
```

The answer is, there was a bug that caused all inline configurations,
even those that didn't specify any enable/disables, to overwrite the
lists of enable/disables. I have now fixed that. I've also added some
tests.

Closes #12342 — I discovered this problem while investigating the last
foible of issue #12342 (itself of tangential interest to something else
I was doing), which can now be closed.

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypy/config_parser.py                   |  23 +++-
 mypy/errorcodes.py                      |   4 +
 mypy/options.py                         |   1 -
 test-data/unit/check-inline-config.test | 150 +++++++++++++++++++++++-
 4 files changed, 171 insertions(+), 7 deletions(-)

diff --git a/mypy/config_parser.py b/mypy/config_parser.py
index e5c0dc893c76..208c12adafbe 100644
--- a/mypy/config_parser.py
+++ b/mypy/config_parser.py
@@ -650,9 +650,8 @@ def parse_mypy_comments(
     Returns a dictionary of options to be applied and a list of error messages
     generated.
     """
-
     errors: list[tuple[int, str]] = []
-    sections = {}
+    sections: dict[str, object] = {"enable_error_code": [], "disable_error_code": []}
 
     for lineno, line in args:
         # In order to easily match the behavior for bools, we abuse configparser.
@@ -660,7 +659,6 @@ def parse_mypy_comments(
         # method is to create a config parser.
         parser = configparser.RawConfigParser()
         options, parse_errors = mypy_comments_to_config_map(line, template)
-
         if "python_version" in options:
             errors.append((lineno, "python_version not supported in inline configuration"))
             del options["python_version"]
@@ -690,9 +688,24 @@ def set_strict_flags() -> None:
                     '(see "mypy -h" for the list of flags enabled in strict mode)',
                 )
             )
-
+        # Because this is currently special-cased
+        # (the new_sections for an inline config *always* includes 'disable_error_code' and
+        # 'enable_error_code' fields, usually empty, which overwrite the old ones),
+        # we have to manipulate them specially.
+        # This could use a refactor, but so could the whole subsystem.
+        if (
+            "enable_error_code" in new_sections
+            and isinstance(neec := new_sections["enable_error_code"], list)
+            and isinstance(eec := sections.get("enable_error_code", []), list)
+        ):
+            new_sections["enable_error_code"] = sorted(set(neec + eec))
+        if (
+            "disable_error_code" in new_sections
+            and isinstance(ndec := new_sections["disable_error_code"], list)
+            and isinstance(dec := sections.get("disable_error_code", []), list)
+        ):
+            new_sections["disable_error_code"] = sorted(set(ndec + dec))
         sections.update(new_sections)
-
     return sections, errors
 
 
diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py
index 8f85a6f6351a..bcfdbf6edc2b 100644
--- a/mypy/errorcodes.py
+++ b/mypy/errorcodes.py
@@ -37,6 +37,10 @@ def __init__(
     def __str__(self) -> str:
         return f""
 
+    def __repr__(self) -> str:
+        """This doesn't fulfill the goals of repr but it's better than the default view."""
+        return f""
+
     def __eq__(self, other: object) -> bool:
         if not isinstance(other, ErrorCode):
             return False
diff --git a/mypy/options.py b/mypy/options.py
index be61059be5c6..573be14c4b1b 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -507,7 +507,6 @@ def apply_changes(self, changes: dict[str, object]) -> Options:
             code = error_codes[code_str]
             new_options.enabled_error_codes.add(code)
             new_options.disabled_error_codes.discard(code)
-
         return new_options
 
     def compare_stable(self, other_snapshot: dict[str, object]) -> bool:
diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test
index 8a306b1dfac0..37d59a84c873 100644
--- a/test-data/unit/check-inline-config.test
+++ b/test-data/unit/check-inline-config.test
@@ -211,6 +211,99 @@ enable_error_code = ignore-without-code, truthy-bool
 \[mypy-tests.*]
 disable_error_code = ignore-without-code
 
+[case testInlineErrorCodesOverrideConfigSmall]
+# flags: --config-file tmp/mypy.ini
+import tests.baz
+[file tests/__init__.py]
+[file tests/baz.py]
+42 + "no"  # type: ignore
+
+[file mypy.ini]
+\[mypy]
+enable_error_code = ignore-without-code, truthy-bool
+
+\[mypy-tests.*]
+disable_error_code = ignore-without-code
+
+[case testInlineErrorCodesOverrideConfigSmall2]
+# flags: --config-file tmp/mypy.ini
+import tests.bar
+import tests.baz
+[file tests/__init__.py]
+[file tests/baz.py]
+42 + "no"  # type: ignore
+[file tests/bar.py]
+# mypy: enable-error-code="ignore-without-code"
+
+def foo() -> int: ...
+if foo: ...  # E: Function "foo" could always be true in boolean context
+42 + "no"  # type: ignore  # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead)
+
+[file mypy.ini]
+\[mypy]
+enable_error_code = ignore-without-code, truthy-bool
+
+\[mypy-tests.*]
+disable_error_code = ignore-without-code
+
+
+[case testInlineErrorCodesOverrideConfigSmallBackward]
+# flags: --config-file tmp/mypy.ini
+import tests.bar
+import tests.baz
+[file tests/__init__.py]
+[file tests/baz.py]
+42 + "no"  # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead)
+[file tests/bar.py]
+# mypy: disable-error-code="ignore-without-code"
+42 + "no"  # type: ignore
+
+[file mypy.ini]
+\[mypy]
+enable_error_code = ignore-without-code, truthy-bool
+
+\[mypy-tests.*]
+enable_error_code = ignore-without-code
+
+[case testInlineOverrideConfig]
+# flags: --config-file tmp/mypy.ini
+import foo
+import tests.bar
+import tests.baz
+[file foo.py]
+# mypy: disable-error-code="truthy-bool"
+class Foo:
+    pass
+
+foo = Foo()
+if foo: ...
+42 # type: ignore  # E: Unused "type: ignore" comment
+
+[file tests/__init__.py]
+[file tests/bar.py]
+# mypy: warn_unused_ignores
+
+def foo() -> int: ...
+if foo: ...  # E: Function "foo" could always be true in boolean context
+42 # type: ignore  # E: Unused "type: ignore" comment
+
+[file tests/baz.py]
+# mypy: disable-error-code="truthy-bool"
+class Foo:
+    pass
+
+foo = Foo()
+if foo: ...
+42 # type: ignore
+
+[file mypy.ini]
+\[mypy]
+warn_unused_ignores = True
+
+\[mypy-tests.*]
+warn_unused_ignores = False
+
+
 [case testIgnoreErrorsSimple]
 # mypy: ignore-errors=True
 
@@ -324,6 +417,61 @@ foo = Foo()
 if foo: ...
 42 + "no"  # type: ignore
 
-
 [case testInlinePythonVersion]
 # mypy: python-version=3.10  # E: python_version not supported in inline configuration
+
+[case testInlineErrorCodesArentRuinedByOthersBaseCase]
+# mypy: disable-error-code=name-defined
+a
+
+[case testInlineErrorCodesArentRuinedByOthersInvalid]
+# mypy: disable-error-code=name-defined
+# mypy: AMONGUS
+a
+[out]
+main:2: error: Unrecognized option: amongus = True
+
+[case testInlineErrorCodesArentRuinedByOthersInvalidBefore]
+# mypy: AMONGUS
+# mypy: disable-error-code=name-defined
+a
+[out]
+main:1: error: Unrecognized option: amongus = True
+
+[case testInlineErrorCodesArentRuinedByOthersSe]
+# mypy: disable-error-code=name-defined
+# mypy: strict-equality
+def is_magic(x: bytes) -> bool:
+    y
+    return x == 'magic' # E: Unsupported left operand type for == ("bytes")
+
+[case testInlineConfigErrorCodesOffAndOn]
+# mypy: disable-error-code=name-defined
+# mypy: enable-error-code=name-defined
+a # E: Name "a" is not defined
+
+[case testInlineConfigErrorCodesOnAndOff]
+# mypy: enable-error-code=name-defined
+# mypy: disable-error-code=name-defined
+a # E: Name "a" is not defined
+
+[case testConfigFileErrorCodesOnAndOff]
+# flags: --config-file tmp/mypy.ini
+import foo
+[file foo.py]
+42 + "no"  # type: ignore  # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead)
+[file mypy.ini]
+\[mypy]
+enable_error_code = ignore-without-code
+disable_error_code = ignore-without-code
+
+[case testInlineConfigBaseCaseWui]
+# mypy: warn_unused_ignores
+x = 1 # type: ignore # E: Unused "type: ignore" comment
+
+[case testInlineConfigIsntRuinedByOthersInvalidWui]
+# mypy: warn_unused_ignores
+# mypy: AMONGUS
+x = 1 # type: ignore # E: Unused "type: ignore" comment
+[out]
+main:2: error: Unrecognized option: amongus = True

From c213db074d319a222464ec82846c908fcadc2bc8 Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Sat, 2 Aug 2025 15:12:54 -0700
Subject: [PATCH 1511/1617] [docs] update information about reveal type &
 locals in common_issues (#19059)

Previously it was impossible to have these in at runtime (more or
less), but now you can just import one of them.

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
---
 docs/source/common_issues.rst | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst
index 266d0c5b2c80..aa325dd3b05c 100644
--- a/docs/source/common_issues.rst
+++ b/docs/source/common_issues.rst
@@ -513,11 +513,15 @@ to see the types of all local variables at once. Example:
    #     b: builtins.str
 .. note::
 
-   ``reveal_type`` and ``reveal_locals`` are only understood by mypy and
-   don't exist in Python. If you try to run your program, you'll have to
-   remove any ``reveal_type`` and ``reveal_locals`` calls before you can
-   run your code. Both are always available and you don't need to import
-   them.
+    ``reveal_type`` and ``reveal_locals`` are handled specially by mypy during
+    type checking, and don't have to be defined or imported.
+
+    However, if you want to run your code,
+    you'll have to remove any ``reveal_type`` and ``reveal_locals``
+    calls from your program or else Python will give you an error at runtime.
+
+    Alternatively, you can import ``reveal_type`` from ``typing_extensions``
+    or ``typing`` (on Python 3.11 and newer)
 
 .. _silencing-linters:
 

From 5b28b62d9f87ac72e8dd5b65cf2cb9fa84fa4be5 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sun, 3 Aug 2025 02:35:06 +0200
Subject: [PATCH 1512/1617] Check slots assignments on self types (#19332)

Fixes #19331.
---
 mypy/checker.py                 |  3 +++
 test-data/unit/check-slots.test | 16 ++++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/mypy/checker.py b/mypy/checker.py
index 35a67d188311..dfbfa753d5f2 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -3752,6 +3752,9 @@ def check_assignment_to_slots(self, lvalue: Lvalue) -> None:
             return
 
         inst = get_proper_type(self.expr_checker.accept(lvalue.expr))
+        if isinstance(inst, TypeVarType) and inst.id.is_self():
+            # Unwrap self type
+            inst = get_proper_type(inst.upper_bound)
         if not isinstance(inst, Instance):
             return
         if inst.type.slots is None:
diff --git a/test-data/unit/check-slots.test b/test-data/unit/check-slots.test
index e924ac9e5f57..10b664bffb11 100644
--- a/test-data/unit/check-slots.test
+++ b/test-data/unit/check-slots.test
@@ -544,3 +544,19 @@ x = X()
 X.a  # E: "a" in __slots__ conflicts with class variable access
 x.a
 [builtins fixtures/tuple.pyi]
+
+[case testSlotsOnSelfType]
+from typing_extensions import Self
+
+class X:
+    __slots__ = ("foo",)
+    foo: int
+
+    def method1(self: Self) -> Self:
+        self.bar = 0  # E: Trying to assign name "bar" that is not in "__slots__" of type "__main__.X"
+        return self
+
+    def method2(self) -> Self:
+        self.bar = 0  # E: Trying to assign name "bar" that is not in "__slots__" of type "__main__.X"
+        return self
+[builtins fixtures/tuple.pyi]

From 06e28f8707baf8eab69eac84a2e4202060420e04 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sun, 3 Aug 2025 15:01:59 +0200
Subject: [PATCH 1513/1617] Follow-up after #19025: test and cleanup (#19294)

As requested by @ilevkivskyi in #19025

---------

Co-authored-by: Ivan Levkivskyi 
---
 mypy/checkmember.py                |  5 +---
 test-data/unit/check-selftype.test | 37 ++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 8447dfc7fe64..d261b3156a0b 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1249,9 +1249,6 @@ def analyze_class_attribute_access(
             or isinstance(node.node, Var)
             and node.node.is_classmethod
         )
-        is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
-            isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
-        )
         t = get_proper_type(t)
         is_trivial_self = False
         if isinstance(node.node, Decorator):
@@ -1274,7 +1271,7 @@ def analyze_class_attribute_access(
             original_vars=original_vars,
             is_trivial_self=is_trivial_self,
         )
-        if is_decorated and not is_staticmethod:
+        if is_decorated:
             t = expand_self_type_if_needed(
                 t, mx, cast(Decorator, node.node).var, itype, is_class=is_classmethod
             )
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index d99c5ee354a4..6481a1766944 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2283,6 +2283,43 @@ reveal_type(Check.foo())  # N: Revealed type is "def () -> __main__.Check"
 reveal_type(Check().foo())  # N: Revealed type is "__main__.Check"
 [builtins fixtures/tuple.pyi]
 
+[case testSelfInClassmethodWithOtherSelfMethod]
+from typing import Any, Callable, Self, TypeVar
+
+_C = TypeVar("_C", bound=Callable[..., Any])
+
+def identity(func: _C, /) -> _C:
+    return func
+
+class A:
+    def meth(self) -> Self: ...
+
+    @classmethod
+    def other_meth(cls) -> Self:
+        reveal_type(cls.meth)  # N: Revealed type is "def [Self <: __main__.A] (self: Self`1) -> Self`1"
+        reveal_type(A.meth)  # N: Revealed type is "def [Self <: __main__.A] (self: Self`2) -> Self`2"
+        return cls().meth()
+
+class B:
+    @identity
+    def meth(self) -> Self: ...
+
+    @classmethod
+    def other_meth(cls) -> Self:
+        reveal_type(cls.meth)  # N: Revealed type is "def [Self <: __main__.B] (self: Self`5) -> Self`5"
+        reveal_type(B.meth)  # N: Revealed type is "def [Self <: __main__.B] (self: Self`6) -> Self`6"
+        return cls().meth()
+
+class C:
+    @classmethod
+    def other_meth(cls) -> Self: ...
+
+    def meth(self) -> Self:
+        reveal_type(self.other_meth)  # N: Revealed type is "def () -> Self`0"
+        reveal_type(type(self).other_meth)  # N: Revealed type is "def () -> Self`0"
+        return self.other_meth()
+[builtins fixtures/tuple.pyi]
+
 [case testSelfTypeUpperBoundFiler]
 from typing import Generic, TypeVar, overload, Sequence
 

From 9d3a0524b32a54858d8efd52d06157908acec348 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sun, 3 Aug 2025 18:47:36 +0200
Subject: [PATCH 1514/1617] Support attribute access on enum members correctly
 (#19422)

Fixes #11368 (apparently canonical).
Fixes #10910.
Fixes #12107.
Fixes #13841.
Fixes #15186.
Fixes #15454.
Fixes #19418.

`mypy` now understands attribute access on enum members - the
"recursive" behaviour of supporting access of almost-all enum members
from members. "Almost", because `.name` and `.value` take precedence
even if a member of the same name exists.

```python
from enum import Enum

class E(Enum):
    FOO = 1
    BAR = 1

# The following is still a `E.BAR` instance:
E.FOO.FOO.BAR.BAR
```

Looks like this is a much wanted feature.
---
 mypy/checkmember.py                   | 12 ++++++
 mypy/plugins/enums.py                 |  2 +-
 test-data/unit/check-enum.test        | 56 ++++++++++++++++++++++++---
 test-data/unit/check-incremental.test |  2 +
 4 files changed, 66 insertions(+), 6 deletions(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index d261b3156a0b..da67591a4553 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -921,6 +921,18 @@ def analyze_var(
         result = AnyType(TypeOfAny.special_form)
     fullname = f"{var.info.fullname}.{name}"
     hook = mx.chk.plugin.get_attribute_hook(fullname)
+
+    if var.info.is_enum and not mx.is_lvalue:
+        if name in var.info.enum_members and name not in {"name", "value"}:
+            enum_literal = LiteralType(name, fallback=itype)
+            result = itype.copy_modified(last_known_value=enum_literal)
+        elif (
+            isinstance(p_result := get_proper_type(result), Instance)
+            and p_result.type.fullname == "enum.nonmember"
+            and p_result.args
+        ):
+            # Unwrap nonmember similar to class-level access
+            result = p_result.args[0]
     if result and not (implicit or var.info.is_protocol and is_instance_var(var)):
         result = analyze_descriptor_access(result, mx)
     if hook:
diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py
index d21b21fb39f8..0be2e083b6dd 100644
--- a/mypy/plugins/enums.py
+++ b/mypy/plugins/enums.py
@@ -144,7 +144,7 @@ def _implements_new(info: TypeInfo) -> bool:
 def enum_member_callback(ctx: mypy.plugin.FunctionContext) -> Type:
     """By default `member(1)` will be inferred as `member[int]`,
     we want to improve the inference to be `Literal[1]` here."""
-    if ctx.arg_types or ctx.arg_types[0]:
+    if ctx.arg_types and ctx.arg_types[0]:
         arg = get_proper_type(ctx.arg_types[0][0])
         proper_return = get_proper_type(ctx.default_return_type)
         if (
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
index d034fe1a6f5f..3bcf9745a801 100644
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -1953,7 +1953,8 @@ class A(Enum):
     x: int
     def method(self) -> int: pass
 class B(A):
-    x = 1  # E: Cannot override writable attribute "x" with a final one
+    x = 1  # E: Cannot override writable attribute "x" with a final one \
+           # E: Incompatible types in assignment (expression has type "B", base class "A" defined the type as "int")
 
 class A1(Enum):
     x: int = 1  # E: Enum members must be left unannotated \
@@ -1971,8 +1972,8 @@ class B2(A2):  # E: Cannot extend enum with existing members: "A2"
 class A3(Enum):
     x: Final[int]  # type: ignore
 class B3(A3):
-    x = 1  # E: Cannot override final attribute "x" (previously declared in base class "A3")
-
+    x = 1  # E: Cannot override final attribute "x" (previously declared in base class "A3") \
+           # E: Incompatible types in assignment (expression has type "B3", base class "A3" defined the type as "int")
 [builtins fixtures/bool.pyi]
 
 [case testEnumNotFinalWithMethodsAndUninitializedValuesStub]
@@ -1984,14 +1985,16 @@ class A(Enum):  # E: Detected enum "lib.A" in a type stub with zero members. The
                 # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members
     x: int
 class B(A):
-    x = 1    # E: Cannot override writable attribute "x" with a final one
+    x = 1    # E: Cannot override writable attribute "x" with a final one \
+             # E: Incompatible types in assignment (expression has type "B", base class "A" defined the type as "int")
 
 class C(Enum):
     x = 1
 class D(C):  # E: Cannot extend enum with existing members: "C" \
              # E: Detected enum "lib.D" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \
              # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members
-    x: int   # E: Cannot assign to final name "x"
+    x: int   # E: Incompatible types in assignment (expression has type "int", base class "C" defined the type as "C") \
+             # E: Cannot assign to final name "x"
 [builtins fixtures/bool.pyi]
 
 [case testEnumNotFinalWithMethodsAndUninitializedValuesStubMember]
@@ -2419,6 +2422,49 @@ def some_a(a: A):
         reveal_type(a)  # N: Revealed type is "Literal[__main__.A.x]"
 [builtins fixtures/dict.pyi]
 
+[case testEnumAccessFromInstance]
+# flags: --python-version 3.11 --warn-unreachable
+# This was added in 3.11
+from enum import Enum, member, nonmember
+
+class A(Enum):
+    x = 1
+    y = member(2)
+    z = nonmember(3)
+
+reveal_type(A.x)  # N: Revealed type is "Literal[__main__.A.x]?"
+reveal_type(A.y)  # N: Revealed type is "Literal[__main__.A.y]?"
+reveal_type(A.z)  # N: Revealed type is "builtins.int"
+
+reveal_type(A.x.x)  # N: Revealed type is "Literal[__main__.A.x]?"
+reveal_type(A.x.x.x)  # N: Revealed type is "Literal[__main__.A.x]?"
+reveal_type(A.x.y)  # N: Revealed type is "Literal[__main__.A.y]?"
+reveal_type(A.x.y.y)  # N: Revealed type is "Literal[__main__.A.y]?"
+reveal_type(A.x.z)  # N: Revealed type is "builtins.int"
+
+reveal_type(A.y.x)  # N: Revealed type is "Literal[__main__.A.x]?"
+reveal_type(A.y.y)  # N: Revealed type is "Literal[__main__.A.y]?"
+reveal_type(A.y.z)  # N: Revealed type is "builtins.int"
+
+A.z.x  # E: "int" has no attribute "x"
+
+class B(Enum):
+    x = 1
+    value = 2
+
+reveal_type(B.x)  # N: Revealed type is "Literal[__main__.B.x]?"
+reveal_type(B.x.value)  # N: Revealed type is "Literal[2]?"
+reveal_type(B.x.x.value)  # N: Revealed type is "Literal[2]?"
+B.x.value.value  # E: "int" has no attribute "value"
+B.x.value.value.value  # E: "int" has no attribute "value"
+reveal_type(B.value)  # N: Revealed type is "Literal[__main__.B.value]?"
+reveal_type(B.value.x)  # N: Revealed type is "Literal[__main__.B.x]?"
+reveal_type(B.value.x.x)  # N: Revealed type is "Literal[__main__.B.x]?"
+reveal_type(B.value.x.value)  # N: Revealed type is "Literal[2]?"
+B.value.x.value.value  # E: "int" has no attribute "value"
+B.value.value.value  # E: "int" has no attribute "value"
+[builtins fixtures/dict.pyi]
+
 
 [case testErrorOnAnnotatedMember]
 from enum import Enum
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 4c170ec4753f..5f2194114d5e 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -5675,10 +5675,12 @@ class FinalEnum(Enum):
 [builtins fixtures/isinstance.pyi]
 [out]
 main:3: error: Cannot override writable attribute "x" with a final one
+main:3: error: Incompatible types in assignment (expression has type "Ok", base class "RegularEnum" defined the type as "int")
 main:4: error: Cannot extend enum with existing members: "FinalEnum"
 main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum")
 [out2]
 main:3: error: Cannot override writable attribute "x" with a final one
+main:3: error: Incompatible types in assignment (expression has type "Ok", base class "RegularEnum" defined the type as "int")
 main:4: error: Cannot extend enum with existing members: "FinalEnum"
 main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum")
 

From 68657d2431714394e81c6420e41d6c3bc8a2cdf5 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 3 Aug 2025 18:47:35 +0100
Subject: [PATCH 1515/1617] Interpret bare ClassVar as inferred, not Any
 (#19573)

Fixes https://github.com/python/mypy/issues/5587

Apparently, it is part of _the spec_ now.
---
 mypy/semanal.py                       |  7 +++++++
 test-data/unit/check-classvar.test    | 20 ++++++++++++++++++++
 test-data/unit/check-incremental.test |  2 +-
 test-data/unit/semanal-classvar.test  |  2 +-
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 7cca406b661b..ab9075cd06ce 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5094,6 +5094,7 @@ def check_classvar(self, s: AssignmentStmt) -> None:
             return
         if not s.type or not self.is_classvar(s.type):
             return
+        assert isinstance(s.type, UnboundType)
         if self.is_class_scope() and isinstance(lvalue, NameExpr):
             node = lvalue.node
             if isinstance(node, Var):
@@ -5110,6 +5111,12 @@ def check_classvar(self, s: AssignmentStmt) -> None:
             # In case of member access, report error only when assigning to self
             # Other kinds of member assignments should be already reported
             self.fail_invalid_classvar(lvalue)
+        if not s.type.args:
+            if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs:
+                if self.options.disallow_any_generics:
+                    self.fail("ClassVar without type argument becomes Any", s, code=codes.TYPE_ARG)
+                return
+            s.type = None
 
     def is_classvar(self, typ: Type) -> bool:
         if not isinstance(typ, UnboundType):
diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test
index 8384e5624793..7918ccded2fe 100644
--- a/test-data/unit/check-classvar.test
+++ b/test-data/unit/check-classvar.test
@@ -360,3 +360,23 @@ reveal_type(C.x)  # E: Access to generic instance variables via class is ambiguo
                   # N: Revealed type is "Any"
 reveal_type(C.y)  # E: Access to generic class variables is ambiguous \
                   # N: Revealed type is "Any"
+
+[case testClassVarBareAnnotation]
+from typing import ClassVar
+
+class C:
+    x: ClassVar = 1
+    y: ClassVar
+
+reveal_type(C.x)  # N: Revealed type is "builtins.int"
+reveal_type(C().x)  # N: Revealed type is "builtins.int"
+reveal_type(C.y)  # N: Revealed type is "Any"
+reveal_type(C().y)  # N: Revealed type is "Any"
+
+[case testClassVarBareAnnotationDisabled]
+# flags: --disallow-any-generics
+from typing import ClassVar
+
+class C:
+    x: ClassVar = 1
+    y: ClassVar  # E: ClassVar without type argument becomes Any
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 5f2194114d5e..7d791319537f 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -2051,7 +2051,7 @@ warn_no_return = True
 [case testIncrementalClassVar]
 from typing import ClassVar
 class A:
-    x = None  # type: ClassVar
+    x: ClassVar
 A().x = 0
 [out1]
 main:4: error: Cannot assign to class variable "x" via instance
diff --git a/test-data/unit/semanal-classvar.test b/test-data/unit/semanal-classvar.test
index 8add559bdd27..62151666c011 100644
--- a/test-data/unit/semanal-classvar.test
+++ b/test-data/unit/semanal-classvar.test
@@ -52,7 +52,7 @@ MypyFile:1(
     AssignmentStmt:3(
       NameExpr(x [m])
       IntExpr(1)
-      Any)))
+      builtins.int)))
 
 [case testClassVarWithTypeVar]
 

From 268c837ce30ca46b82b52be21a6edc96a8a8a8b7 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Sun, 3 Aug 2025 18:40:25 -0400
Subject: [PATCH 1516/1617] [mypyc] feat: unwrap NewType types to their base
 types for optimized code paths (#19497)

This PR adds special case logic for unwrapping NewType types to their
actual type. This logic is currently working. a `NewType("name", str)`
now generates the same code as a `str`.

I wasn't entirely sure of the best way to test this, so I just tweaked
the str tests to use a union of str and newtype str and validated that
the IR still uses `str` and not `object`

Almost all of my tests are running fine, but I get a strange mypy error
in the str.count tests saying that `str` objects do not have a .count
method? Of course a str object has a .count method. Do you think this
might related to the typeshed stuff again?

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypyc/irbuild/mapper.py          |   4 +
 mypyc/test-data/fixtures/ir.py   |   1 +
 mypyc/test-data/irbuild-str.test | 149 ++++++++++++++++++++++---------
 3 files changed, 113 insertions(+), 41 deletions(-)

diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py
index 4a01255e2d5d..815688d90fb6 100644
--- a/mypyc/irbuild/mapper.py
+++ b/mypyc/irbuild/mapper.py
@@ -73,6 +73,10 @@ def type_to_rtype(self, typ: Type | None) -> RType:
 
         typ = get_proper_type(typ)
         if isinstance(typ, Instance):
+            if typ.type.is_newtype:
+                # Unwrap NewType to its base type for rprimitive mapping
+                assert len(typ.type.bases) == 1, typ.type.bases
+                return self.type_to_rtype(typ.type.bases[0])
             if typ.type.fullname == "builtins.int":
                 return int_rprimitive
             elif typ.type.fullname == "builtins.float":
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 3776a3dcc79a..76afc1ea58cc 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -122,6 +122,7 @@ def rpartition(self, sep: str, /) -> Tuple[str, str, str]: ...
     def removeprefix(self, prefix: str, /) -> str: ...
     def removesuffix(self, suffix: str, /) -> str: ...
     def islower(self) -> bool: ...
+    def count(self, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: pass
 
 class float:
     def __init__(self, x: object) -> None: pass
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index 4a4992d41a5d..3e69325a454b 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -1,13 +1,15 @@
 [case testStrSplit]
-from typing import Optional, List
+from typing import NewType, Optional, List, Union
+NewStr = NewType("NewStr", str)
 
-def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]:
+def do_split(s: Union[str, NewStr], sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]:
     if sep is not None:
         if max_split is not None:
             return s.split(sep, max_split)
         else:
             return s.split(sep)
     return s.split()
+[typing fixtures/typing-full.pyi]
 [out]
 def do_split(s, sep, max_split):
     s :: str
@@ -56,12 +58,15 @@ L9:
 
 
 [case testStrEquality]
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
 def eq(x: str, y: str) -> bool:
     return x == y
 
-def neq(x: str, y: str) -> bool:
+def neq(x: str, y: Union[str, NewStr]) -> bool:
     return x != y
 
+[typing fixtures/typing-full.pyi]
 [out]
 def eq(x, y):
     x, y :: str
@@ -79,13 +84,14 @@ L0:
     return r1
 
 [case testStrReplace]
-from typing import Optional
-
-def do_replace(s: str, old_substr: str, new_substr: str, max_count: Optional[int] = None) -> str:
+from typing import NewType, Optional, Union
+NewStr = NewType("NewStr", str)
+def do_replace(s: Union[str, NewStr], old_substr: str, new_substr: str, max_count: Optional[int] = None) -> str:
     if max_count is not None:
         return s.replace(old_substr, new_substr, max_count)
     else:
         return s.replace(old_substr, new_substr)
+[typing fixtures/typing-full.pyi]
 [out]
 def do_replace(s, old_substr, new_substr, max_count):
     s, old_substr, new_substr :: str
@@ -114,17 +120,19 @@ L5:
     unreachable
 
 [case testStrStartswithEndswithTuple]
-from typing import Tuple
+from typing import NewType, Tuple, Union
+NewStr = NewType("NewStr", str)
 
-def do_startswith(s1: str, s2: Tuple[str, ...]) -> bool:
+def do_startswith(s1: Union[str, NewStr], s2: Tuple[str, ...]) -> bool:
     return s1.startswith(s2)
 
-def do_endswith(s1: str, s2: Tuple[str, ...]) -> bool:
+def do_endswith(s1: Union[str, NewStr], s2: Tuple[str, ...]) -> bool:
     return s1.endswith(s2)
 
-def do_tuple_literal_args(s1: str) -> None:
+def do_tuple_literal_args(s1: Union[str, NewStr]) -> None:
     x = s1.startswith(("a", "b"))
     y = s1.endswith(("a", "b"))
+[typing fixtures/typing-full.pyi]
 [out]
 def do_startswith(s1, s2):
     s1 :: str
@@ -165,11 +173,14 @@ L0:
     return 1
 
 [case testStrToBool]
-def is_true(x: str) -> bool:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def is_true(x: Union[str, NewStr]) -> bool:
     if x:
         return True
     else:
         return False
+[typing fixtures/typing-full.pyi]
 [out]
 def is_true(x):
     x :: str
@@ -185,11 +196,14 @@ L3:
     unreachable
 
 [case testStringFormatMethod]
-def f(s: str, num: int) -> None:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def f(s: Union[str, NewStr], num: int) -> None:
     s1 = "Hi! I'm {}, and I'm {} years old.".format(s, num)
     s2 = ''.format()
     s3 = 'abc'.format()
     s4 = '}}{}{{{}}}{{{}'.format(num, num, num)
+[typing fixtures/typing-full.pyi]
 [out]
 def f(s, num):
     s :: str
@@ -217,11 +231,14 @@ L0:
     return 1
 
 [case testFStrings_64bit]
-def f(var: str, num: int) -> None:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def f(var: Union[str, NewStr], num: int) -> None:
     s1 = f"Hi! I'm {var}. I am {num} years old."
     s2 = f'Hello {var:>{num}}'
     s3 = f''
     s4 = f'abc'
+[typing fixtures/typing-full.pyi]
 [out]
 def f(var, num):
     var :: str
@@ -267,7 +284,9 @@ L0:
     return 1
 
 [case testStringFormattingCStyle]
-def f(var: str, num: int) -> None:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def f(var: Union[str, NewStr], num: int) -> None:
     s1 = "Hi! I'm %s." % var
     s2 = "I am %d years old." % num
     s3 = "Hi! I'm %s. I am %d years old." % (var, num)
@@ -322,7 +341,9 @@ L0:
     return 1
 
 [case testEncode_64bit]
-def f(s: str) -> None:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def f(s: Union[str, NewStr]) -> None:
     s.encode()
     s.encode('utf-8')
     s.encode('utf8', 'strict')
@@ -340,6 +361,7 @@ def f(s: str) -> None:
     s.encode(encoding=encoding, errors=errors)
     s.encode('latin2')
 
+[typing fixtures/typing-full.pyi]
 [out]
 def f(s):
     s :: str
@@ -410,7 +432,9 @@ L0:
     return 1
 
 [case testOrd]
-def str_ord(x: str) -> int:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def str_ord(x: Union[str, NewStr]) -> int:
     return ord(x)
 def str_ord_literal() -> int:
     return ord("a")
@@ -420,6 +444,7 @@ def bytes_ord_literal() -> int:
     return ord(b"a")
 def any_ord(x) -> int:
     return ord(x)
+[typing fixtures/typing-full.pyi]
 [out]
 def str_ord(x):
     x :: str
@@ -459,13 +484,16 @@ L0:
     return r6
 
 [case testStrip]
-def do_strip(s: str) -> None:
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
+def do_strip(s: Union[str, NewStr]) -> None:
     s.lstrip("x")
     s.strip("y")
     s.rstrip("z")
     s.lstrip()
     s.strip()
     s.rstrip()
+[typing fixtures/typing-full.pyi]
 [out]
 def do_strip(s):
     s, r0, r1, r2, r3, r4, r5, r6, r7, r8 :: str
@@ -481,60 +509,99 @@ L0:
     r8 = CPyStr_RStrip(s, 0)
     return 1
 
-[case testCountAll]
+[case testCountAll_64bit]
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
 def do_count(s: str) -> int:
-    return s.count("x")  # type: ignore [attr-defined]
+    return s.count("x")
+[typing fixtures/typing-full.pyi]
 [out]
 def do_count(s):
     s, r0 :: str
     r1 :: native_int
-    r2 :: bit
-    r3 :: object
-    r4 :: int
+    r2, r3, r4 :: bit
+    r5, r6, r7 :: int
 L0:
     r0 = 'x'
     r1 = CPyStr_Count(s, r0, 0)
     r2 = r1 >= 0 :: signed
-    r3 = box(native_int, r1)
-    r4 = unbox(int, r3)
-    return r4
+    r3 = r1 <= 4611686018427387903 :: signed
+    if r3 goto L1 else goto L2 :: bool
+L1:
+    r4 = r1 >= -4611686018427387904 :: signed
+    if r4 goto L3 else goto L2 :: bool
+L2:
+    r5 = CPyTagged_FromInt64(r1)
+    r6 = r5
+    goto L4
+L3:
+    r7 = r1 << 1
+    r6 = r7
+L4:
+    return r6
 
-[case testCountStart]
+[case testCountStart_64bit]
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
 def do_count(s: str, start: int) -> int:
-    return s.count("x", start)  # type: ignore [attr-defined]
+    return s.count("x", start)
+[typing fixtures/typing-full.pyi]
 [out]
 def do_count(s, start):
     s :: str
     start :: int
     r0 :: str
     r1 :: native_int
-    r2 :: bit
-    r3 :: object
-    r4 :: int
+    r2, r3, r4 :: bit
+    r5, r6, r7 :: int
 L0:
     r0 = 'x'
     r1 = CPyStr_Count(s, r0, start)
     r2 = r1 >= 0 :: signed
-    r3 = box(native_int, r1)
-    r4 = unbox(int, r3)
-    return r4
+    r3 = r1 <= 4611686018427387903 :: signed
+    if r3 goto L1 else goto L2 :: bool
+L1:
+    r4 = r1 >= -4611686018427387904 :: signed
+    if r4 goto L3 else goto L2 :: bool
+L2:
+    r5 = CPyTagged_FromInt64(r1)
+    r6 = r5
+    goto L4
+L3:
+    r7 = r1 << 1
+    r6 = r7
+L4:
+    return r6
 
-[case testCountStartEnd]
+[case testCountStartEnd_64bit]
+from typing import NewType, Union
+NewStr = NewType("NewStr", str)
 def do_count(s: str, start: int, end: int) -> int:
-    return s.count("x", start, end)  # type: ignore [attr-defined]
+    return s.count("x", start, end)
+[typing fixtures/typing-full.pyi]
 [out]
 def do_count(s, start, end):
     s :: str
     start, end :: int
     r0 :: str
     r1 :: native_int
-    r2 :: bit
-    r3 :: object
-    r4 :: int
+    r2, r3, r4 :: bit
+    r5, r6, r7 :: int
 L0:
     r0 = 'x'
     r1 = CPyStr_CountFull(s, r0, start, end)
     r2 = r1 >= 0 :: signed
-    r3 = box(native_int, r1)
-    r4 = unbox(int, r3)
-    return r4
+    r3 = r1 <= 4611686018427387903 :: signed
+    if r3 goto L1 else goto L2 :: bool
+L1:
+    r4 = r1 >= -4611686018427387904 :: signed
+    if r4 goto L3 else goto L2 :: bool
+L2:
+    r5 = CPyTagged_FromInt64(r1)
+    r6 = r5
+    goto L4
+L3:
+    r7 = r1 << 1
+    r6 = r7
+L4:
+    return r6

From 555edf36a8483042013a7b3c4e858c3f2c0aadcb Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Mon, 4 Aug 2025 00:42:53 +0200
Subject: [PATCH 1517/1617] Introduce temporary named expressions for `match`
 subjects (#18446)

Fixes #18440. Fixes #17230. Fixes #16650. Improves behavior in #14731
(but still thinks that match is non-exhaustive, "missing return" false
positive remains).

#16503 did this specifically for `CallExpr`, but that isn't the only
kind of such statements. I propose to expand this for more general
expressions and believe that a blacklist is more reasonable here: we do
**not** want to introduce a temporary name only for certain expressions
that either are already named or can be used to infer contained
variables (inline tuple/list/dict/set literals).

Writing logic to generate a name for every other kind of expression
would be quite cumbersome - I circumvent this by using a simple counter
to generate unique names on demand.

---------

Co-authored-by: Ivan Levkivskyi 
---
 mypy/binder.py                      |  20 +++++-
 mypy/checker.py                     |  54 ++++++++++----
 test-data/unit/check-python310.test | 108 +++++++++++++++++++++++++++-
 3 files changed, 165 insertions(+), 17 deletions(-)

diff --git a/mypy/binder.py b/mypy/binder.py
index 2ae58dad1fe0..c95481329a57 100644
--- a/mypy/binder.py
+++ b/mypy/binder.py
@@ -8,7 +8,16 @@
 
 from mypy.erasetype import remove_instance_last_known_values
 from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash, subkeys
-from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var
+from mypy.nodes import (
+    LITERAL_NO,
+    Expression,
+    IndexExpr,
+    MemberExpr,
+    NameExpr,
+    RefExpr,
+    TypeInfo,
+    Var,
+)
 from mypy.options import Options
 from mypy.subtypes import is_same_type, is_subtype
 from mypy.typeops import make_simplified_union
@@ -173,6 +182,15 @@ def _get(self, key: Key, index: int = -1) -> CurrentType | None:
                 return self.frames[i].types[key]
         return None
 
+    @classmethod
+    def can_put_directly(cls, expr: Expression) -> bool:
+        """Will `.put()` on this expression be successful?
+
+        This is inlined in `.put()` because the logic is rather hot and must be kept
+        in sync.
+        """
+        return isinstance(expr, (IndexExpr, MemberExpr, NameExpr)) and literal(expr) > LITERAL_NO
+
     def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> None:
         """Directly set the narrowed type of expression (if it supports it).
 
diff --git a/mypy/checker.py b/mypy/checker.py
index dfbfa753d5f2..c1ff29aa33d9 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -358,6 +358,9 @@ class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi):
     # functions such as open(), etc.
     plugin: Plugin
 
+    # A helper state to produce unique temporary names on demand.
+    _unique_id: int
+
     def __init__(
         self,
         errors: Errors,
@@ -428,6 +431,7 @@ def __init__(
             self, self.msg, self.plugin, per_line_checking_time_ns
         )
         self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options)
+        self._unique_id = 0
 
     @property
     def expr_checker(self) -> mypy.checkexpr.ExpressionChecker:
@@ -5476,21 +5480,10 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None:
         return
 
     def visit_match_stmt(self, s: MatchStmt) -> None:
-        named_subject: Expression
-        if isinstance(s.subject, CallExpr):
-            # Create a dummy subject expression to handle cases where a match statement's subject
-            # is not a literal value. This lets us correctly narrow types and check exhaustivity
-            # This is hack!
-            if s.subject_dummy is None:
-                id = s.subject.callee.fullname if isinstance(s.subject.callee, RefExpr) else ""
-                name = "dummy-match-" + id
-                v = Var(name)
-                s.subject_dummy = NameExpr(name)
-                s.subject_dummy.node = v
-            named_subject = s.subject_dummy
-        else:
-            named_subject = s.subject
-
+        named_subject = self._make_named_statement_for_match(s)
+        # In sync with similar actions elsewhere, narrow the target if
+        # we are matching an AssignmentExpr
+        unwrapped_subject = collapse_walrus(s.subject)
         with self.binder.frame_context(can_skip=False, fall_through=0):
             subject_type = get_proper_type(self.expr_checker.accept(s.subject))
 
@@ -5523,6 +5516,12 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
                         pattern_map, else_map = conditional_types_to_typemaps(
                             named_subject, pattern_type.type, pattern_type.rest_type
                         )
+                        # Maybe the subject type can be inferred from constraints on
+                        # its attribute/item?
+                        if pattern_map and named_subject in pattern_map:
+                            pattern_map[unwrapped_subject] = pattern_map[named_subject]
+                        if else_map and named_subject in else_map:
+                            else_map[unwrapped_subject] = else_map[named_subject]
                         pattern_map = self.propagate_up_typemap_info(pattern_map)
                         else_map = self.propagate_up_typemap_info(else_map)
                         self.remove_capture_conflicts(pattern_type.captures, inferred_types)
@@ -5575,6 +5574,25 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
             with self.binder.frame_context(can_skip=False, fall_through=2):
                 pass
 
+    def _make_named_statement_for_match(self, s: MatchStmt) -> Expression:
+        """Construct a fake NameExpr for inference if a match clause is complex."""
+        subject = s.subject
+        if self.binder.can_put_directly(subject):
+            # Already named - we should infer type of it as given
+            return subject
+        elif s.subject_dummy is not None:
+            return s.subject_dummy
+        else:
+            # Create a dummy subject expression to handle cases where a match statement's subject
+            # is not a literal value. This lets us correctly narrow types and check exhaustivity
+            # This is hack!
+            name = self.new_unique_dummy_name("match")
+            v = Var(name)
+            named_subject = NameExpr(name)
+            named_subject.node = v
+            s.subject_dummy = named_subject
+            return named_subject
+
     def _get_recursive_sub_patterns_map(
         self, expr: Expression, typ: Type
     ) -> dict[Expression, Type]:
@@ -7966,6 +7984,12 @@ def warn_deprecated(self, node: Node | None, context: Context) -> None:
             warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail
             warn(deprecated, context, code=codes.DEPRECATED)
 
+    def new_unique_dummy_name(self, namespace: str) -> str:
+        """Generate a name that is guaranteed to be unique for this TypeChecker instance."""
+        name = f"dummy-{namespace}-{self._unique_id}"
+        self._unique_id += 1
+        return name
+
     # leafs
 
     def visit_pass_stmt(self, o: PassStmt, /) -> None:
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 80fd64fa3569..f264167cb067 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1302,7 +1302,7 @@ def main() -> None:
         case a:
             reveal_type(a)  # N: Revealed type is "builtins.int"
 
-[case testMatchCapturePatternFromAsyncFunctionReturningUnion-xfail]
+[case testMatchCapturePatternFromAsyncFunctionReturningUnion]
 async def func1(arg: bool) -> str | int: ...
 async def func2(arg: bool) -> bytes | int: ...
 
@@ -2179,9 +2179,11 @@ def f() -> None:
     match x := returns_a_or_none():
         case A():
             reveal_type(x.a)  # N: Revealed type is "builtins.int"
+    reveal_type(x)  # N: Revealed type is "Union[__main__.A, None]"
     match x := returns_a():
         case A():
             reveal_type(x.a)  # N: Revealed type is "builtins.int"
+    reveal_type(x)  # N: Revealed type is "__main__.A"
     y = returns_a_or_none()
     match y:
         case A():
@@ -2586,6 +2588,110 @@ def fn2(x: Some | int | str) -> None:
             pass
 [builtins fixtures/dict.pyi]
 
+[case testMatchFunctionCall]
+# flags: --warn-unreachable
+
+def fn() -> int | str: ...
+
+match fn():
+    case str(s):
+        reveal_type(s)  # N: Revealed type is "builtins.str"
+    case int(i):
+        reveal_type(i)  # N: Revealed type is "builtins.int"
+    case other:
+        other  # E: Statement is unreachable
+
+[case testMatchAttribute]
+# flags: --warn-unreachable
+
+class A:
+    foo: int | str
+
+match A().foo:
+    case str(s):
+        reveal_type(s)  # N: Revealed type is "builtins.str"
+    case int(i):
+        reveal_type(i)  # N: Revealed type is "builtins.int"
+    case other:
+        other  # E: Statement is unreachable
+
+[case testMatchLiteral]
+# flags: --warn-unreachable
+
+def int_literal() -> None:
+    match 12:
+        case 1 as s:
+            reveal_type(s)  # N: Revealed type is "Literal[1]"
+        case int(i):
+            reveal_type(i)  # N: Revealed type is "Literal[12]?"
+        case other:
+            other  # E: Statement is unreachable
+
+def str_literal() -> None:
+    match 'foo':
+        case 'a' as s:
+            reveal_type(s)  # N: Revealed type is "Literal['a']"
+        case str(i):
+            reveal_type(i)  # N: Revealed type is "Literal['foo']?"
+        case other:
+            other  # E: Statement is unreachable
+
+[case testMatchOperations]
+# flags: --warn-unreachable
+
+x: int
+match -x:
+    case -1 as s:
+        reveal_type(s)  # N: Revealed type is "Literal[-1]"
+    case int(s):
+        reveal_type(s)  # N: Revealed type is "builtins.int"
+    case other:
+        other  # E: Statement is unreachable
+
+match 1 + 2:
+    case 3 as s:
+        reveal_type(s)  # N: Revealed type is "Literal[3]"
+    case int(s):
+        reveal_type(s)  # N: Revealed type is "builtins.int"
+    case other:
+        other  # E: Statement is unreachable
+
+match 1 > 2:
+    case True as s:
+        reveal_type(s)  # N: Revealed type is "Literal[True]"
+    case False as s:
+        reveal_type(s)  # N: Revealed type is "Literal[False]"
+    case other:
+        other  # E: Statement is unreachable
+[builtins fixtures/ops.pyi]
+
+[case testMatchDictItem]
+# flags: --warn-unreachable
+
+m: dict[str, int | str]
+k: str
+
+match m[k]:
+    case str(s):
+        reveal_type(s)  # N: Revealed type is "builtins.str"
+    case int(i):
+        reveal_type(i)  # N: Revealed type is "builtins.int"
+    case other:
+        other  # E: Statement is unreachable
+
+[builtins fixtures/dict.pyi]
+
+[case testMatchLiteralValuePathological]
+# flags: --warn-unreachable
+
+match 0:
+    case 0 as i:
+        reveal_type(i)  # N: Revealed type is "Literal[0]?"
+    case int(i):
+        i  # E: Statement is unreachable
+    case other:
+        other  # E: Statement is unreachable
+
 [case testMatchNamedTupleSequence]
 from typing import Any, NamedTuple
 

From 0c922ce8c0fe28e9ea713e1787b2d9e044c75cd0 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sun, 3 Aug 2025 22:33:45 -0400
Subject: [PATCH 1518/1617] Recognize buffer protocol special methods (#19581)

The special methods `__buffer__` and `__release_buffer__` were [added in
Python
3.12](https://docs.python.org/3/reference/datamodel.html#emulating-buffer-types).
Both are [implemented with slot
descriptors](https://docs.python.org/3/c-api/typeobj.html#:~:text=__buffer__),
and so should be treated as being implicitly positional-only like other
slot special methods.
---
 mypy/sharedparse.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py
index ef2e4f720664..71d1dee8f7d6 100644
--- a/mypy/sharedparse.py
+++ b/mypy/sharedparse.py
@@ -10,6 +10,7 @@
     "__call__",
     "__complex__",
     "__contains__",
+    "__buffer__",
     "__del__",
     "__delattr__",
     "__delitem__",
@@ -31,6 +32,7 @@
     "__new__",
     "__oct__",
     "__pos__",
+    "__release_buffer__",
     "__repr__",
     "__reversed__",
     "__setattr__",

From 5b03024e829940cf3c3e3d99fc6625f569d02728 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 4 Aug 2025 04:09:40 +0100
Subject: [PATCH 1519/1617] Remove --new-type-inference flag (#19570)

It was on deprecated almost two years ago (it is on by default).
---
 mypy/main.py                                  | 10 ------
 mypy/options.py                               |  4 +--
 test-data/unit/check-generics.test            | 35 -------------------
 test-data/unit/check-inference-context.test   |  1 -
 test-data/unit/check-inference.test           |  2 --
 test-data/unit/check-overloading.test         |  1 -
 .../unit/check-parameter-specification.test   |  4 ---
 test-data/unit/check-plugin-attrs.test        |  1 -
 test-data/unit/check-typevar-tuple.test       |  1 -
 test-data/unit/cmdline.test                   |  8 -----
 test-data/unit/pythoneval.test                | 14 +++-----
 11 files changed, 6 insertions(+), 75 deletions(-)

diff --git a/mypy/main.py b/mypy/main.py
index 2fbb9671e721..fd50c7677a11 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1086,10 +1086,6 @@ def add_invertible_flag(
     internals_group.add_argument(
         "--old-type-inference", action="store_true", help=argparse.SUPPRESS
     )
-    # Deprecated reverse variant of the above.
-    internals_group.add_argument(
-        "--new-type-inference", action="store_true", help=argparse.SUPPRESS
-    )
     internals_group.add_argument(
         "--disable-expression-cache", action="store_true", help=argparse.SUPPRESS
     )
@@ -1507,12 +1503,6 @@ def set_strict_flags() -> None:
     if options.logical_deps:
         options.cache_fine_grained = True
 
-    if options.new_type_inference:
-        print(
-            "Warning: --new-type-inference flag is deprecated;"
-            " new type inference algorithm is already enabled by default"
-        )
-
     if options.strict_concatenate and not strict_option_set:
         print("Warning: --strict-concatenate is deprecated; use --extra-checks instead")
 
diff --git a/mypy/options.py b/mypy/options.py
index 573be14c4b1b..6d7eca772888 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -391,10 +391,8 @@ def __init__(self) -> None:
         # skip most errors after this many messages have been reported.
         # -1 means unlimited.
         self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD
-        # Disable new experimental type inference algorithm.
+        # Disable new type inference algorithm.
         self.old_type_inference = False
-        # Deprecated reverse version of the above, do not use.
-        self.new_type_inference = False
         # Disable expression cache (for debugging).
         self.disable_expression_cache = False
         # Export line-level, limited, fine-grained dependency information in cache data
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
index abeb5face26f..3b535ab4a1c0 100644
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -2750,7 +2750,6 @@ reveal_type(func(1))  # N: Revealed type is "builtins.int"
 [builtins fixtures/tuple.pyi]
 
 [case testGenericLambdaGenericMethodNoCrash]
-# flags: --new-type-inference
 from typing import TypeVar, Union, Callable, Generic
 
 S = TypeVar("S")
@@ -2789,7 +2788,6 @@ reveal_type(dict2)  # N: Revealed type is "builtins.dict[Any, __main__.B]"
 -- ------------------------------------------------------------------
 
 [case testInferenceAgainstGenericCallable]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 X = TypeVar('X')
@@ -2807,7 +2805,6 @@ reveal_type(bar(id))  # N: Revealed type is "builtins.list[builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableNoLeak]
-# flags: --new-type-inference
 from typing import TypeVar, Callable
 
 T = TypeVar('T')
@@ -2823,7 +2820,6 @@ reveal_type(f(tpl))  # N: Revealed type is "Any"
 [out]
 
 [case testInferenceAgainstGenericCallableChain]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 X = TypeVar('X')
@@ -2836,7 +2832,6 @@ reveal_type(chain(id, id))  # N: Revealed type is "def (builtins.int) -> builtin
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGeneric]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2857,7 +2852,6 @@ reveal_type(same(42))  # N: Revealed type is "builtins.list[builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericReverse]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2878,7 +2872,6 @@ reveal_type(same([42]))  # N: Revealed type is "builtins.int"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericArg]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2899,7 +2892,6 @@ reveal_type(single(42))  # N: Revealed type is "builtins.list[builtins.int]"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericChain]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2913,7 +2905,6 @@ reveal_type(comb(id, id))  # N: Revealed type is "def [T] (T`1) -> T`1"
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericNonLinear]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2934,7 +2925,6 @@ reveal_type(mix([id, id, id]))  # N: Revealed type is "def [S] (S`4) -> builtins
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCurry]
-# flags: --new-type-inference
 from typing import Callable, List, TypeVar
 
 S = TypeVar("S")
@@ -2953,7 +2943,6 @@ reveal_type(dec2(test2))  # N: Revealed type is "def [T] (T`3) -> def (T`3) -> T
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableNewVariable]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2968,7 +2957,6 @@ reveal_type(dec(test))  # N: Revealed type is "def [U] (builtins.list[U`-1]) ->
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericAlias]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -2986,7 +2974,6 @@ reveal_type(dec(id))  # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1]
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericCallableGenericProtocol]
-# flags: --new-type-inference
 from typing import TypeVar, Protocol, Generic, Optional
 
 T = TypeVar('T')
@@ -3002,7 +2989,6 @@ reveal_type(lift(g))  # N: Revealed type is "def [T] (Union[T`1, None]) -> Union
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericSplitOrder]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -3017,7 +3003,6 @@ reveal_type(dec(id, id))  # N: Revealed type is "def (builtins.int) -> builtins.
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericSplitOrderGeneric]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Tuple
 
 S = TypeVar('S')
@@ -3048,7 +3033,6 @@ reveal_type(id)  # N: Revealed type is "def (builtins.int) -> builtins.int"
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericEllipsisSelfSpecialCase]
-# flags: --new-type-inference
 from typing import Self, Callable, TypeVar
 
 T = TypeVar("T")
@@ -3062,7 +3046,6 @@ c: C
 reveal_type(c.test())  # N: Revealed type is "__main__.C"
 
 [case testInferenceAgainstGenericBoundsAndValues]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 class B: ...
@@ -3090,7 +3073,6 @@ reveal_type(dec2(id2))  # N: Revealed type is "def (Never) -> builtins.list[Neve
                         # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[Never], Never]"
 
 [case testInferenceAgainstGenericLambdas]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List
 
 S = TypeVar('S')
@@ -3127,7 +3109,6 @@ dec4_bound(lambda x: x)  # E: Value of type variable "I" of "dec4_bound" cannot
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecBasicInList]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import ParamSpec
 
@@ -3146,7 +3127,6 @@ reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) ->
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecBasicDeList]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import ParamSpec
 
@@ -3163,7 +3143,6 @@ reveal_type(dec(either))  # N: Revealed type is "def [T] (x: builtins.list[T`5],
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecPopOff]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3184,7 +3163,6 @@ reveal_type(dec(dec))  # N: Revealed type is "def () -> def [T, P, S] (def (T`-1
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecPopOn]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3206,7 +3184,6 @@ reveal_type(dec(dec))  # N: Revealed type is "def [T, S] (T`13, f: def () -> def
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecVsParamSpec]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple, Generic
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3227,7 +3204,6 @@ reveal_type(dec(h))  # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwarg
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecVsParamSpecConcatenate]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple, Generic
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3245,7 +3221,6 @@ reveal_type(dec(h))  # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwarg
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecSecondary]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple, Generic
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3263,7 +3238,6 @@ reveal_type(dec(g))  # N: Revealed type is "def (builtins.int) -> __main__.Foo[[
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericParamSpecSecondOrder]
-# flags: --new-type-inference
 from typing import TypeVar, Callable
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3285,7 +3259,6 @@ reveal_type(transform(dec2))  # N: Revealed type is "def [W, T] (def (builtins.i
 [builtins fixtures/tuple.pyi]
 
 [case testNoAccidentalVariableClashInNestedGeneric]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Generic, Tuple
 
 T = TypeVar('T')
@@ -3302,7 +3275,6 @@ def apply(a: S, b: T) -> None:
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericParamSpecSpuriousBoundsNotUsed]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Generic
 from typing_extensions import ParamSpec, Concatenate
 
@@ -3321,7 +3293,6 @@ reveal_type(test)  # N: Revealed type is "def () -> def [Q] (__main__.Foo[Q`-1])
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericVariadicBasicInList]
-# flags: --new-type-inference
 from typing import Tuple, TypeVar, List, Callable
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3341,7 +3312,6 @@ reveal_type(dec(pair))  # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builti
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericVariadicBasicDeList]
-# flags: --new-type-inference
 from typing import Tuple, TypeVar, List, Callable
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3359,7 +3329,6 @@ reveal_type(dec(either))  # N: Revealed type is "def [T] (builtins.list[T`5], bu
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericVariadicPopOff]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3381,7 +3350,6 @@ reveal_type(dec(dec))  # N: Revealed type is "def () -> def [T, Ts, S] (def (T`-
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericVariadicPopOn]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Tuple
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3404,7 +3372,6 @@ reveal_type(dec(dec))  # N: Revealed type is "def [T, S] (T`13, def () -> def (T
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericVariadicVsVariadic]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, List, Generic
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3424,7 +3391,6 @@ reveal_type(dec(g))  # N: Revealed type is "def [Ts] (*Unpack[Ts`4]) -> builtins
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericVariadicVsVariadicConcatenate]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Generic
 from typing_extensions import Unpack, TypeVarTuple
 
@@ -3443,7 +3409,6 @@ reveal_type(dec(h))  # N: Revealed type is "def [T, Us] (T`-1, *Unpack[Us`-2]) -
 [builtins fixtures/list.pyi]
 
 [case testInferenceAgainstGenericVariadicSecondary]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Generic
 from typing_extensions import Unpack, TypeVarTuple
 
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index ff726530cf9f..5a674cca09da 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -693,7 +693,6 @@ f(lambda: None)
 g(lambda: None)
 
 [case testIsinstanceInInferredLambda]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Optional
 T = TypeVar('T')
 S = TypeVar('S')
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 6564fb3192d0..53efcc0d22e3 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -1405,14 +1405,12 @@ class B: pass
 [builtins fixtures/list.pyi]
 
 [case testUninferableLambda]
-# flags: --new-type-inference
 from typing import TypeVar, Callable
 X = TypeVar('X')
 def f(x: Callable[[X], X]) -> X: pass
 y = f(lambda x: x)  # E: Need type annotation for "y"
 
 [case testUninferableLambdaWithTypeError]
-# flags: --new-type-inference
 from typing import TypeVar, Callable
 X = TypeVar('X')
 def f(x: Callable[[X], X], y: str) -> X: pass
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index 22221416f151..e7f6ff04c13e 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -6637,7 +6637,6 @@ reveal_type(Snafu.snafu('123'))  # N: Revealed type is "builtins.str"
 [builtins fixtures/staticmethod.pyi]
 
 [case testOverloadedWithInternalTypeVars]
-# flags: --new-type-inference
 import m
 
 [file m.pyi]
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
index 0835ba7ac57d..2b4f92c7c819 100644
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -1121,7 +1121,6 @@ reveal_type(jf(1))  # N: Revealed type is "None"
 [builtins fixtures/paramspec.pyi]
 
 [case testGenericsInInferredParamspecReturn]
-# flags: --new-type-inference
 from typing import Callable, TypeVar, Generic
 from typing_extensions import ParamSpec
 
@@ -1646,7 +1645,6 @@ def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ...
 [builtins fixtures/paramspec.pyi]
 
 [case testParamSpecDecoratorAppliedToGeneric]
-# flags: --new-type-inference
 from typing import Callable, List, TypeVar
 from typing_extensions import ParamSpec
 
@@ -1684,7 +1682,6 @@ reveal_type(test(*ints))  # N: Revealed type is "__main__.Foo[[*builtins.int]]"
 [builtins fixtures/paramspec.pyi]
 
 [case testParamSpecArgumentParamInferenceGeneric]
-# flags: --new-type-inference
 from typing import Callable, TypeVar
 from typing_extensions import ParamSpec
 
@@ -1702,7 +1699,6 @@ y: int = call(identity, 2)
 [builtins fixtures/paramspec.pyi]
 
 [case testParamSpecNestedApplyNoCrash]
-# flags: --new-type-inference
 from typing import Callable, TypeVar
 from typing_extensions import ParamSpec
 
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index 00bec13ab16d..42f21e945ef0 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -1201,7 +1201,6 @@ class A:
 [builtins fixtures/bool.pyi]
 
 [case testAttrsFactoryBadReturn]
-# flags: --new-type-inference
 import attr
 def my_factory() -> int:
     return 7
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index e931c0c01aee..2de2e45f0a96 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -886,7 +886,6 @@ reveal_type(z)  # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple
 [builtins fixtures/tuple.pyi]
 
 [case testInferenceAgainstGenericVariadicWithBadType]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Generic
 from typing_extensions import Unpack, TypeVarTuple
 
diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test
index aa0c8916ba0f..ff60c24b72a5 100644
--- a/test-data/unit/cmdline.test
+++ b/test-data/unit/cmdline.test
@@ -1249,14 +1249,6 @@ note: A user-defined top-level module with name "typing" is not supported
 Failed to find builtin module mypy_extensions, perhaps typeshed is broken?
 == Return code: 2
 
-[case testNewTypeInferenceFlagDeprecated]
-# cmd: mypy --new-type-inference a.py
-[file a.py]
-pass
-[out]
-Warning: --new-type-inference flag is deprecated; new type inference algorithm is already enabled by default
-== Return code: 0
-
 [case testCustomTypeshedDirFilePassedExplicitly]
 # cmd: mypy --custom-typeshed-dir dir m.py dir/stdlib/foo.pyi
 [file m.py]
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 72c00a3b9b1c..4bd94dfce03e 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -797,7 +797,6 @@ _program.py:3: error: Dict entry 1 has incompatible type "str": "str"; expected
 _program.py:5: error: "dict[str, int]" has no attribute "xyz"
 
 [case testDefaultDict]
-# flags: --new-type-inference
 import typing as t
 from collections import defaultdict
 
@@ -823,11 +822,11 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]):
 MyDDict(dict)['0']
 MyDDict(dict)[0]
 [out]
-_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "type[list[_T]]"; expected "Optional[Callable[[], str]]"
-_program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int"
-_program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str")
-_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, list[Never]]"; expected "defaultdict[int, list[Never]]"
-_program.py:24: error: Invalid index type "str" for "MyDDict[dict[Never, Never]]"; expected type "int"
+_program.py:6: error: Argument 1 to "defaultdict" has incompatible type "type[list[_T]]"; expected "Optional[Callable[[], str]]"
+_program.py:9: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int"
+_program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str")
+_program.py:19: error: Argument 1 to "tst" has incompatible type "defaultdict[str, list[Never]]"; expected "defaultdict[int, list[Never]]"
+_program.py:23: error: Invalid index type "str" for "MyDDict[dict[Never, Never]]"; expected type "int"
 
 [case testCollectionsAliases]
 import typing as t
@@ -1977,7 +1976,6 @@ _testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompa
 _testDataclassReplace.py:12: error: Unexpected keyword argument "q" for "replace" of "A"
 
 [case testGenericInferenceWithTuple]
-# flags: --new-type-inference
 from typing import TypeVar, Callable, Tuple
 
 T = TypeVar("T")
@@ -1989,7 +1987,6 @@ x: Tuple[str, ...] = f(tuple)
 [out]
 
 [case testGenericInferenceWithDataclass]
-# flags: --new-type-inference
 from typing import Any, Collection, List
 from dataclasses import dataclass, field
 
@@ -2002,7 +1999,6 @@ class A:
 [out]
 
 [case testGenericInferenceWithItertools]
-# flags: --new-type-inference
 from typing import TypeVar, Tuple
 from itertools import groupby
 K = TypeVar("K")

From 5051a4831e174b2a42c8e9fcbd1e6954a86542ae Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 4 Aug 2025 13:25:32 +0100
Subject: [PATCH 1520/1617] Do not use dictionary in CallableType (#19580)

This is mypyc-specific optimization: `dict` creation is quite slow, so
we should not create a dict in the `CallableType` constructor. Instead,
I store the required info on the relevant `FuncDef` object (and restore
the link to definition in `fixup.py`). Quite surprisingly, this gives 2%
speedup on my desktop.
---
 mypy/checker.py                     |  2 +-
 mypy/checkexpr.py                   |  4 ++--
 mypy/fixup.py                       |  8 +++++++
 mypy/messages.py                    | 10 +++++++--
 mypy/nodes.py                       |  9 ++++++++
 mypy/types.py                       | 34 +----------------------------
 test-data/unit/check-serialize.test |  1 +
 7 files changed, 30 insertions(+), 38 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index c1ff29aa33d9..4d8221415754 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -730,7 +730,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     assert isinstance(item, Decorator)
                     item_type = self.extract_callable_type(item.var.type, item)
                     if item_type is not None:
-                        item_type.definition = item
+                        item_type.definition = item.func
                         item_types.append(item_type)
                 if item_types:
                     defn.type = Overloaded(item_types)
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 740efb0d2ee4..1b10370b08cb 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2934,7 +2934,7 @@ def infer_overload_return_type(
                 if not args_contain_any:
                     self.chk.store_types(m)
                     if isinstance(infer_type, ProperType) and isinstance(infer_type, CallableType):
-                        self.chk.check_deprecated(infer_type.definition, context)
+                        self.chk.warn_deprecated(infer_type.definition, context)
                     return ret_type, infer_type
                 p_infer_type = get_proper_type(infer_type)
                 if isinstance(p_infer_type, CallableType):
@@ -2975,7 +2975,7 @@ def infer_overload_return_type(
             if isinstance(inferred_callable, ProperType) and isinstance(
                 inferred_callable, CallableType
             ):
-                self.chk.check_deprecated(inferred_callable.definition, context)
+                self.chk.warn_deprecated(inferred_callable.definition, context)
             return return_types[0], inferred_types[0]
 
     def overload_erased_call_targets(
diff --git a/mypy/fixup.py b/mypy/fixup.py
index c0f8e401777c..0007fe8faabf 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -29,6 +29,7 @@
     Overloaded,
     Parameters,
     ParamSpecType,
+    ProperType,
     TupleType,
     TypeAliasType,
     TypedDictType,
@@ -177,6 +178,11 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
             item.accept(self)
         if o.impl:
             o.impl.accept(self)
+        if isinstance(o.type, Overloaded):
+            # For error messages we link the original definition for each item.
+            for typ, item in zip(o.type.items, o.items):
+                if isinstance(item, Decorator):
+                    typ.definition = item.func
 
     def visit_decorator(self, d: Decorator) -> None:
         if self.current_info is not None:
@@ -187,6 +193,8 @@ def visit_decorator(self, d: Decorator) -> None:
             d.var.accept(self)
         for node in d.decorators:
             node.accept(self)
+        if isinstance(d.var.type, ProperType) and isinstance(d.var.type, CallableType):
+            d.var.type.definition = d.func
 
     def visit_class_def(self, c: ClassDef) -> None:
         for v in c.type_vars:
diff --git a/mypy/messages.py b/mypy/messages.py
index 44ed25a19517..6b55da59d183 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -1003,7 +1003,7 @@ def maybe_note_about_special_args(self, callee: CallableType, context: Context)
         if self.prefer_simple_messages():
             return
         # https://github.com/python/mypy/issues/11309
-        first_arg = callee.def_extras.get("first_arg")
+        first_arg = get_first_arg(callee)
         if first_arg and first_arg not in {"self", "cls", "mcs"}:
             self.note(
                 "Looks like the first special argument in a method "
@@ -3007,7 +3007,7 @@ def [T <: int] f(self, x: int, y: T) -> None
             s = definition_arg_names[0] + s
         s = f"{tp.definition.name}({s})"
     elif tp.name:
-        first_arg = tp.def_extras.get("first_arg")
+        first_arg = get_first_arg(tp)
         if first_arg:
             if s:
                 s = ", " + s
@@ -3050,6 +3050,12 @@ def [T <: int] f(self, x: int, y: T) -> None
     return f"def {s}"
 
 
+def get_first_arg(tp: CallableType) -> str | None:
+    if not isinstance(tp.definition, FuncDef) or not tp.definition.info or tp.definition.is_static:
+        return None
+    return tp.definition.original_first_arg
+
+
 def variance_string(variance: int) -> str:
     if variance == COVARIANT:
         return "covariant"
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 011e4e703a0c..6ffe579efe71 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -823,6 +823,7 @@ class FuncDef(FuncItem, SymbolNode, Statement):
         "dataclass_transform_spec",
         "docstring",
         "deprecated",
+        "original_first_arg",
     )
 
     __match_args__ = ("name", "arguments", "type", "body")
@@ -855,6 +856,12 @@ def __init__(
         # the majority). In cases where self is not annotated and there are no Self
         # in the signature we can simply drop the first argument.
         self.is_trivial_self = False
+        # This is needed because for positional-only arguments the name is set to None,
+        # but we sometimes still want to show it in error messages.
+        if arguments:
+            self.original_first_arg: str | None = arguments[0].variable.name
+        else:
+            self.original_first_arg = None
 
     @property
     def name(self) -> str:
@@ -886,6 +893,7 @@ def serialize(self) -> JsonDict:
                 else self.dataclass_transform_spec.serialize()
             ),
             "deprecated": self.deprecated,
+            "original_first_arg": self.original_first_arg,
         }
 
     @classmethod
@@ -906,6 +914,7 @@ def deserialize(cls, data: JsonDict) -> FuncDef:
         set_flags(ret, data["flags"])
         # NOTE: ret.info is set in the fixup phase.
         ret.arg_names = data["arg_names"]
+        ret.original_first_arg = data.get("original_first_arg")
         ret.arg_kinds = [ArgKind(x) for x in data["arg_kinds"]]
         ret.abstract_status = data["abstract_status"]
         ret.dataclass_transform_spec = (
diff --git a/mypy/types.py b/mypy/types.py
index 4b5ef332ccf9..029477c1d5c4 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -21,16 +21,7 @@
 
 import mypy.nodes
 from mypy.bogus_type import Bogus
-from mypy.nodes import (
-    ARG_POS,
-    ARG_STAR,
-    ARG_STAR2,
-    INVARIANT,
-    ArgKind,
-    FakeInfo,
-    FuncDef,
-    SymbolNode,
-)
+from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, FakeInfo, SymbolNode
 from mypy.options import Options
 from mypy.state import state
 from mypy.util import IdMapper
@@ -1841,8 +1832,6 @@ class CallableType(FunctionLike):
         "from_type_type",  # Was this callable generated by analyzing Type[...]
         # instantiation?
         "is_bound",  # Is this a bound method?
-        "def_extras",  # Information about original definition we want to serialize.
-        # This is used for more detailed error messages.
         "type_guard",  # T, if -> TypeGuard[T] (ret_type is bool in this case).
         "type_is",  # T, if -> TypeIs[T] (ret_type is bool in this case).
         "from_concatenate",  # whether this callable is from a concatenate object
@@ -1869,7 +1858,6 @@ def __init__(
         special_sig: str | None = None,
         from_type_type: bool = False,
         is_bound: bool = False,
-        def_extras: dict[str, Any] | None = None,
         type_guard: Type | None = None,
         type_is: Type | None = None,
         from_concatenate: bool = False,
@@ -1902,22 +1890,6 @@ def __init__(
         self.from_concatenate = from_concatenate
         self.imprecise_arg_kinds = imprecise_arg_kinds
         self.is_bound = is_bound
-        if def_extras:
-            self.def_extras = def_extras
-        elif isinstance(definition, FuncDef):
-            # This information would be lost if we don't have definition
-            # after serialization, but it is useful in error messages.
-            # TODO: decide how to add more info here (file, line, column)
-            # without changing interface hash.
-            first_arg: str | None = None
-            if definition.arg_names and definition.info and not definition.is_static:
-                if getattr(definition, "arguments", None):
-                    first_arg = definition.arguments[0].variable.name
-                else:
-                    first_arg = definition.arg_names[0]
-            self.def_extras = {"first_arg": first_arg}
-        else:
-            self.def_extras = {}
         self.type_guard = type_guard
         self.type_is = type_is
         self.unpack_kwargs = unpack_kwargs
@@ -1939,7 +1911,6 @@ def copy_modified(
         special_sig: Bogus[str | None] = _dummy,
         from_type_type: Bogus[bool] = _dummy,
         is_bound: Bogus[bool] = _dummy,
-        def_extras: Bogus[dict[str, Any]] = _dummy,
         type_guard: Bogus[Type | None] = _dummy,
         type_is: Bogus[Type | None] = _dummy,
         from_concatenate: Bogus[bool] = _dummy,
@@ -1964,7 +1935,6 @@ def copy_modified(
             special_sig=special_sig if special_sig is not _dummy else self.special_sig,
             from_type_type=from_type_type if from_type_type is not _dummy else self.from_type_type,
             is_bound=is_bound if is_bound is not _dummy else self.is_bound,
-            def_extras=def_extras if def_extras is not _dummy else dict(self.def_extras),
             type_guard=type_guard if type_guard is not _dummy else self.type_guard,
             type_is=type_is if type_is is not _dummy else self.type_is,
             from_concatenate=(
@@ -2291,7 +2261,6 @@ def serialize(self) -> JsonDict:
             "is_ellipsis_args": self.is_ellipsis_args,
             "implicit": self.implicit,
             "is_bound": self.is_bound,
-            "def_extras": dict(self.def_extras),
             "type_guard": self.type_guard.serialize() if self.type_guard is not None else None,
             "type_is": (self.type_is.serialize() if self.type_is is not None else None),
             "from_concatenate": self.from_concatenate,
@@ -2314,7 +2283,6 @@ def deserialize(cls, data: JsonDict) -> CallableType:
             is_ellipsis_args=data["is_ellipsis_args"],
             implicit=data["implicit"],
             is_bound=data["is_bound"],
-            def_extras=data["def_extras"],
             type_guard=(
                 deserialize_type(data["type_guard"]) if data["type_guard"] is not None else None
             ),
diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test
index 5265832f5f27..03c185a5694b 100644
--- a/test-data/unit/check-serialize.test
+++ b/test-data/unit/check-serialize.test
@@ -224,6 +224,7 @@ def f(x: int) -> int: pass
 [out2]
 tmp/a.py:2: note: Revealed type is "builtins.str"
 tmp/a.py:3: error: Unexpected keyword argument "x" for "f"
+tmp/b.py: note: "f" defined here
 
 [case testSerializeTypeGuardFunction]
 import a

From 1c48286eb22551d8fe2dc557e387595bcf8335b5 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Mon, 4 Aug 2025 16:23:45 +0200
Subject: [PATCH 1521/1617] Sync typeshed (#19585)

Source commit:

https://github.com/python/typeshed/commit/e16c23d3768c84433b62e0461085f79c740ade57

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: AlexWaygood 
---
 ...ially-revert-Clean-up-argparse-hacks.patch |  10 +-
 ...redundant-inheritances-from-Iterator.patch |  30 +-
 mypy/typeshed/stdlib/VERSIONS                 |   1 +
 mypy/typeshed/stdlib/_compression.pyi         |   3 +-
 mypy/typeshed/stdlib/_ctypes.pyi              |   5 +-
 mypy/typeshed/stdlib/_gdbm.pyi                |   3 +-
 mypy/typeshed/stdlib/_heapq.pyi               |   9 +-
 mypy/typeshed/stdlib/_interpreters.pyi        |  11 +-
 mypy/typeshed/stdlib/_io.pyi                  |   1 +
 mypy/typeshed/stdlib/_json.pyi                |   2 +-
 mypy/typeshed/stdlib/_msi.pyi                 |   5 +
 mypy/typeshed/stdlib/_operator.pyi            |   9 +-
 mypy/typeshed/stdlib/_pickle.pyi              |   1 +
 mypy/typeshed/stdlib/_sqlite3.pyi             | 357 ++++++------
 mypy/typeshed/stdlib/_ssl.pyi                 |   4 +-
 mypy/typeshed/stdlib/_thread.pyi              |   2 +-
 mypy/typeshed/stdlib/_typeshed/__init__.pyi   |  18 +-
 mypy/typeshed/stdlib/_winapi.pyi              |   3 +
 mypy/typeshed/stdlib/argparse.pyi             |  67 ++-
 mypy/typeshed/stdlib/ast.pyi                  |  13 +-
 mypy/typeshed/stdlib/asyncio/__init__.pyi     |   7 -
 mypy/typeshed/stdlib/asyncio/base_futures.pyi |   8 +-
 mypy/typeshed/stdlib/asyncio/events.pyi       |   9 +-
 .../stdlib/asyncio/format_helpers.pyi         |   3 +-
 mypy/typeshed/stdlib/asyncio/futures.pyi      |   8 +-
 mypy/typeshed/stdlib/asyncio/queues.pyi       |   3 +-
 mypy/typeshed/stdlib/asyncio/streams.pyi      |   3 +-
 mypy/typeshed/stdlib/asyncio/tasks.pyi        |   5 +-
 mypy/typeshed/stdlib/asyncio/unix_events.pyi  |   2 +-
 mypy/typeshed/stdlib/bdb.pyi                  |   4 +-
 mypy/typeshed/stdlib/builtins.pyi             |  18 +-
 mypy/typeshed/stdlib/bz2.pyi                  |   4 +-
 mypy/typeshed/stdlib/cgi.pyi                  |   4 +-
 mypy/typeshed/stdlib/codecs.pyi               |  12 +-
 mypy/typeshed/stdlib/collections/__init__.pyi |   5 +-
 mypy/typeshed/stdlib/compileall.pyi           |   3 +-
 .../stdlib/concurrent/futures/__init__.pyi    |   4 +-
 .../stdlib/concurrent/futures/_base.pyi       |   3 +-
 .../concurrent/interpreters/__init__.pyi      |  68 +++
 .../concurrent/interpreters/_crossinterp.pyi  |  29 +
 .../concurrent/interpreters/_queues.pyi       |  58 ++
 mypy/typeshed/stdlib/configparser.pyi         |  16 +-
 mypy/typeshed/stdlib/contextlib.pyi           |   5 +-
 mypy/typeshed/stdlib/copy.pyi                 |   3 +-
 mypy/typeshed/stdlib/ctypes/__init__.pyi      |   3 +-
 mypy/typeshed/stdlib/dataclasses.pyi          |   1 +
 mypy/typeshed/stdlib/datetime.pyi             |  48 +-
 mypy/typeshed/stdlib/dbm/__init__.pyi         |   1 +
 mypy/typeshed/stdlib/difflib.pyi              |   9 +-
 mypy/typeshed/stdlib/email/headerregistry.pyi |   3 +-
 mypy/typeshed/stdlib/email/message.pyi        |   4 +-
 mypy/typeshed/stdlib/encodings/__init__.pyi   |   4 +
 mypy/typeshed/stdlib/enum.pyi                 |   5 +
 mypy/typeshed/stdlib/fileinput.pyi            |   3 +-
 mypy/typeshed/stdlib/fractions.pyi            |   3 +-
 mypy/typeshed/stdlib/functools.pyi            |   6 +-
 mypy/typeshed/stdlib/gettext.pyi              |   3 +-
 mypy/typeshed/stdlib/gzip.pyi                 |   4 +-
 mypy/typeshed/stdlib/hashlib.pyi              |   4 +-
 mypy/typeshed/stdlib/html/parser.pyi          |  12 +-
 mypy/typeshed/stdlib/imghdr.pyi               |   3 +-
 mypy/typeshed/stdlib/imp.pyi                  |   3 +-
 .../stdlib/importlib/resources/abc.pyi        |  24 +-
 mypy/typeshed/stdlib/inspect.pyi              |   5 +-
 mypy/typeshed/stdlib/ipaddress.pyi            |   4 +-
 mypy/typeshed/stdlib/logging/__init__.pyi     |   4 +-
 mypy/typeshed/stdlib/logging/handlers.pyi     |   3 +-
 mypy/typeshed/stdlib/mailbox.pyi              |   5 +-
 mypy/typeshed/stdlib/math.pyi                 |   7 +-
 mypy/typeshed/stdlib/mmap.pyi                 | 105 ++--
 mypy/typeshed/stdlib/multiprocessing/heap.pyi |   3 +-
 .../stdlib/multiprocessing/sharedctypes.pyi   |   4 +-
 mypy/typeshed/stdlib/nt.pyi                   |   3 +
 mypy/typeshed/stdlib/numbers.pyi              |   5 +-
 mypy/typeshed/stdlib/optparse.pyi             |   3 +-
 mypy/typeshed/stdlib/os/__init__.pyi          |  12 +-
 mypy/typeshed/stdlib/pathlib/__init__.pyi     |  10 +-
 mypy/typeshed/stdlib/platform.pyi             |  23 +-
 mypy/typeshed/stdlib/pprint.pyi               |  69 ++-
 mypy/typeshed/stdlib/pydoc.pyi                |   3 +-
 mypy/typeshed/stdlib/queue.pyi                |   5 +-
 mypy/typeshed/stdlib/quopri.pyi               |   3 +-
 mypy/typeshed/stdlib/re.pyi                   |   4 +-
 mypy/typeshed/stdlib/shutil.pyi               |   3 +-
 mypy/typeshed/stdlib/signal.pyi               |  98 ++--
 mypy/typeshed/stdlib/smtplib.pyi              |   4 +-
 mypy/typeshed/stdlib/socket.pyi               |   3 +-
 mypy/typeshed/stdlib/sqlite3/__init__.pyi     |  32 +-
 mypy/typeshed/stdlib/sqlite3/dbapi2.pyi       |   3 +-
 mypy/typeshed/stdlib/ssl.pyi                  |  97 ++--
 mypy/typeshed/stdlib/string/templatelib.pyi   |  11 +-
 mypy/typeshed/stdlib/sys/__init__.pyi         |  10 +-
 mypy/typeshed/stdlib/sys/_monitoring.pyi      |  64 ++-
 mypy/typeshed/stdlib/tarfile.pyi              |  96 ++--
 mypy/typeshed/stdlib/tempfile.pyi             |   3 +-
 mypy/typeshed/stdlib/termios.pyi              | 533 +++++++++---------
 mypy/typeshed/stdlib/threading.pyi            |   2 +-
 mypy/typeshed/stdlib/time.pyi                 |   3 +-
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |   5 +
 mypy/typeshed/stdlib/tkinter/dnd.pyi          |   3 +-
 mypy/typeshed/stdlib/tkinter/font.pyi         |   4 +-
 mypy/typeshed/stdlib/tkinter/ttk.pyi          |   6 +-
 mypy/typeshed/stdlib/tty.pyi                  |  14 +-
 mypy/typeshed/stdlib/turtle.pyi               |  14 +-
 mypy/typeshed/stdlib/typing.pyi               |   2 +
 mypy/typeshed/stdlib/typing_extensions.pyi    |  12 +-
 mypy/typeshed/stdlib/unittest/case.pyi        |  17 +-
 mypy/typeshed/stdlib/unittest/main.pyi        |   3 +-
 mypy/typeshed/stdlib/unittest/mock.pyi        |  43 +-
 mypy/typeshed/stdlib/unittest/runner.pyi      |   4 +-
 mypy/typeshed/stdlib/urllib/request.pyi       |   3 +-
 mypy/typeshed/stdlib/uuid.pyi                 |   2 +-
 mypy/typeshed/stdlib/xml/dom/minidom.pyi      |   4 +-
 .../stdlib/xml/etree/ElementInclude.pyi       |   3 +-
 .../typeshed/stdlib/xml/etree/ElementTree.pyi |   1 +
 mypy/typeshed/stdlib/xml/sax/__init__.pyi     |   3 +-
 mypy/typeshed/stdlib/xmlrpc/client.pyi        |   3 +-
 mypy/typeshed/stdlib/xmlrpc/server.pyi        |   8 +-
 mypy/typeshed/stdlib/zipfile/__init__.pyi     |  13 +-
 mypy/typeshed/stdlib/zoneinfo/_common.pyi     |   3 +-
 120 files changed, 1419 insertions(+), 974 deletions(-)
 create mode 100644 mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi
 create mode 100644 mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
 create mode 100644 mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi

diff --git a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
index f76818d10cba..5c31569711e5 100644
--- a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
+++ b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch
@@ -1,4 +1,4 @@
-From 05f351f6a37fe8b73c698c348bf6aa5108363049 Mon Sep 17 00:00:00 2001
+From 84a9d586544a0408d4654f57f83a93cb048070fb Mon Sep 17 00:00:00 2001
 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
 Date: Sat, 15 Feb 2025 20:11:06 +0100
 Subject: [PATCH] Partially revert Clean up argparse hacks
@@ -8,15 +8,15 @@ Subject: [PATCH] Partially revert Clean up argparse hacks
  1 file changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
-index 95ad6c7da..79e6cfde1 100644
+index b9fa31139..3c3ba116a 100644
 --- a/mypy/typeshed/stdlib/argparse.pyi
 +++ b/mypy/typeshed/stdlib/argparse.pyi
 @@ -2,7 +2,7 @@ import sys
  from _typeshed import SupportsWrite, sentinel
  from collections.abc import Callable, Generator, Iterable, Sequence
  from re import Pattern
--from typing import IO, Any, ClassVar, Final, Generic, NoReturn, Protocol, TypeVar, overload
-+from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload
+-from typing import IO, Any, ClassVar, Final, Generic, NoReturn, Protocol, TypeVar, overload, type_check_only
++from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload, type_check_only
  from typing_extensions import Self, TypeAlias, deprecated
  
  __all__ = [
@@ -41,5 +41,5 @@ index 95ad6c7da..79e6cfde1 100644
          default: Any = ...,
          type: _ActionType = ...,
 -- 
-2.49.0
+2.50.1
 
diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
index 5b30a63f1318..d3f49a4eef3e 100644
--- a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
+++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
@@ -1,4 +1,4 @@
-From 363d69b366695fea117631d30c348e36b9a5a99d Mon Sep 17 00:00:00 2001
+From c217544146d36899d50e828d627652a0d8f63bb7 Mon Sep 17 00:00:00 2001
 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
 Date: Sat, 21 Dec 2024 22:36:38 +0100
 Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
@@ -15,7 +15,7 @@ Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
  7 files changed, 34 insertions(+), 34 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
-index 4544680cc..19a2d12d8 100644
+index ed56f33af..5253e967e 100644
 --- a/mypy/typeshed/stdlib/_asyncio.pyi
 +++ b/mypy/typeshed/stdlib/_asyncio.pyi
 @@ -1,6 +1,6 @@
@@ -36,10 +36,10 @@ index 4544680cc..19a2d12d8 100644
      @property
      def _exception(self) -> BaseException | None: ...
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index ea77a730f..900c4c93f 100644
+index 0575be3c8..d9be595fe 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
-@@ -1170,7 +1170,7 @@ class frozenset(AbstractSet[_T_co]):
+@@ -1186,7 +1186,7 @@ class frozenset(AbstractSet[_T_co]):
      def __hash__(self) -> int: ...
      def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
  
@@ -48,7 +48,7 @@ index ea77a730f..900c4c93f 100644
      def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> tuple[int, _T]: ...
-@@ -1366,7 +1366,7 @@ else:
+@@ -1380,7 +1380,7 @@ else:
  
  exit: _sitebuiltins.Quitter
  
@@ -57,7 +57,7 @@ index ea77a730f..900c4c93f 100644
      @overload
      def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ...
      @overload
-@@ -1431,7 +1431,7 @@ license: _sitebuiltins._Printer
+@@ -1444,7 +1444,7 @@ license: _sitebuiltins._Printer
  
  def locals() -> dict[str, Any]: ...
  
@@ -66,7 +66,7 @@ index ea77a730f..900c4c93f 100644
      # 3.14 adds `strict` argument.
      if sys.version_info >= (3, 14):
          @overload
-@@ -1734,7 +1734,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
+@@ -1750,7 +1750,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
  
  quit: _sitebuiltins.Quitter
  
@@ -75,7 +75,7 @@ index ea77a730f..900c4c93f 100644
      @overload
      def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ...  # type: ignore[misc]
      @overload
-@@ -1795,7 +1795,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
+@@ -1814,7 +1814,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
  @overload
  def vars(object: Any = ..., /) -> dict[str, Any]: ...
  
@@ -107,7 +107,7 @@ index 2c8e7109c..4ed0ab1d8 100644
      restkey: _T | None
      restval: str | Any | None
 diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi
-index 948b39ea1..1d5f9cf00 100644
+index 910d63814..eb942bc55 100644
 --- a/mypy/typeshed/stdlib/fileinput.pyi
 +++ b/mypy/typeshed/stdlib/fileinput.pyi
 @@ -1,8 +1,8 @@
@@ -116,12 +116,12 @@ index 948b39ea1..1d5f9cf00 100644
 -from collections.abc import Callable, Iterable
 +from collections.abc import Callable, Iterable, Iterator
  from types import GenericAlias, TracebackType
--from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload
-+from typing import IO, Any, AnyStr, Literal, Protocol, overload
+-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload, type_check_only
++from typing import IO, Any, AnyStr, Literal, Protocol, overload, type_check_only
  from typing_extensions import Self, TypeAlias
  
  __all__ = [
-@@ -104,7 +104,7 @@ def fileno() -> int: ...
+@@ -105,7 +105,7 @@ def fileno() -> int: ...
  def isfirstline() -> bool: ...
  def isstdin() -> bool: ...
  
@@ -307,10 +307,10 @@ index b79f9e773..f276372d0 100644
      def __iter__(self) -> Self: ...
      def next(self, timeout: float | None = None) -> _T: ...
 diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-index 5d3c2330b..ab783dbde 100644
+index bcfea3a13..5a659deac 100644
 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
 +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-@@ -399,7 +399,7 @@ class Connection:
+@@ -405,7 +405,7 @@ class Connection:
          self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, /
      ) -> Literal[False]: ...
  
@@ -320,5 +320,5 @@ index 5d3c2330b..ab783dbde 100644
      @property
      def connection(self) -> Connection: ...
 -- 
-2.49.0
+2.50.1
 
diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS
index 8baf207ad7b8..6fcf0161790d 100644
--- a/mypy/typeshed/stdlib/VERSIONS
+++ b/mypy/typeshed/stdlib/VERSIONS
@@ -124,6 +124,7 @@ compileall: 3.0-
 compression: 3.14-
 concurrent: 3.2-
 concurrent.futures.interpreter: 3.14-
+concurrent.interpreters: 3.14-
 configparser: 3.0-
 contextlib: 3.0-
 contextvars: 3.7-
diff --git a/mypy/typeshed/stdlib/_compression.pyi b/mypy/typeshed/stdlib/_compression.pyi
index 80d38b4db824..aa67df2ab478 100644
--- a/mypy/typeshed/stdlib/_compression.pyi
+++ b/mypy/typeshed/stdlib/_compression.pyi
@@ -3,10 +3,11 @@
 from _typeshed import Incomplete, WriteableBuffer
 from collections.abc import Callable
 from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
-from typing import Any, Protocol
+from typing import Any, Protocol, type_check_only
 
 BUFFER_SIZE = DEFAULT_BUFFER_SIZE
 
+@type_check_only
 class _Reader(Protocol):
     def read(self, n: int, /) -> bytes: ...
     def seekable(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi
index e134066f0bcf..bfd0f910f482 100644
--- a/mypy/typeshed/stdlib/_ctypes.pyi
+++ b/mypy/typeshed/stdlib/_ctypes.pyi
@@ -103,7 +103,10 @@ class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType):
     def __init__(self, value: _T = ...) -> None: ...  # pyright: ignore[reportInvalidTypeVarUse]
     def __ctypes_from_outparam__(self, /) -> _T: ...  # type: ignore[override]
 
+@type_check_only
 class _CanCastTo(_CData): ...
+
+@type_check_only
 class _PointerLike(_CanCastTo): ...
 
 # This type is not exposed. It calls itself _ctypes.PyCPointerType.
@@ -114,7 +117,7 @@ class _PyCPointerType(_CTypeBaseType):
     def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
     def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
     def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
-    def set_type(self, type: Any, /) -> None: ...
+    def set_type(self, type: _CTypeBaseType, /) -> None: ...
     if sys.version_info < (3, 13):
         # Inherited from CType_Type starting on 3.13
         def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ...  # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
diff --git a/mypy/typeshed/stdlib/_gdbm.pyi b/mypy/typeshed/stdlib/_gdbm.pyi
index 1d1d541f5477..2cb5fba29dfa 100644
--- a/mypy/typeshed/stdlib/_gdbm.pyi
+++ b/mypy/typeshed/stdlib/_gdbm.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadOnlyBuffer, StrOrBytesPath
 from types import TracebackType
-from typing import TypeVar, overload
+from typing import TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 if sys.platform != "win32":
@@ -13,6 +13,7 @@ if sys.platform != "win32":
 
     class error(OSError): ...
     # Actual typename gdbm, not exposed by the implementation
+    @type_check_only
     class _gdbm:
         def firstkey(self) -> bytes | None: ...
         def nextkey(self, key: _KeyType) -> bytes | None: ...
diff --git a/mypy/typeshed/stdlib/_heapq.pyi b/mypy/typeshed/stdlib/_heapq.pyi
index 3363fbcd7e74..4d7d6aba3241 100644
--- a/mypy/typeshed/stdlib/_heapq.pyi
+++ b/mypy/typeshed/stdlib/_heapq.pyi
@@ -1,18 +1,17 @@
 import sys
-from typing import Any, Final, TypeVar
-
-_T = TypeVar("_T")  # list items must be comparable
+from _typeshed import SupportsRichComparisonT as _T  # All type variable use in this module requires comparability.
+from typing import Final
 
 __about__: Final[str]
 
-def heapify(heap: list[Any], /) -> None: ...  # list items must be comparable
+def heapify(heap: list[_T], /) -> None: ...
 def heappop(heap: list[_T], /) -> _T: ...
 def heappush(heap: list[_T], item: _T, /) -> None: ...
 def heappushpop(heap: list[_T], item: _T, /) -> _T: ...
 def heapreplace(heap: list[_T], item: _T, /) -> _T: ...
 
 if sys.version_info >= (3, 14):
-    def heapify_max(heap: list[Any], /) -> None: ...  # list items must be comparable
+    def heapify_max(heap: list[_T], /) -> None: ...
     def heappop_max(heap: list[_T], /) -> _T: ...
     def heappush_max(heap: list[_T], item: _T, /) -> None: ...
     def heappushpop_max(heap: list[_T], item: _T, /) -> _T: ...
diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi
index ad8eccbe3328..54fc0e39d239 100644
--- a/mypy/typeshed/stdlib/_interpreters.pyi
+++ b/mypy/typeshed/stdlib/_interpreters.pyi
@@ -1,8 +1,10 @@
 import types
 from collections.abc import Callable
-from typing import Any, Final, Literal, SupportsIndex
+from typing import Any, Final, Literal, SupportsIndex, TypeVar
 from typing_extensions import TypeAlias
 
+_R = TypeVar("_R")
+
 _Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""]
 _SharedDict: TypeAlias = dict[str, Any]  # many objects can be shared
 
@@ -21,7 +23,7 @@ def get_current() -> tuple[int, int]: ...
 def get_main() -> tuple[int, int]: ...
 def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ...
 def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ...
-def whence(id: SupportsIndex) -> int: ...
+def whence(id: SupportsIndex) -> _Whence: ...
 def exec(
     id: SupportsIndex,
     code: str | types.CodeType | Callable[[], object],
@@ -31,12 +33,12 @@ def exec(
 ) -> None | types.SimpleNamespace: ...
 def call(
     id: SupportsIndex,
-    callable: Callable[..., object],
+    callable: Callable[..., _R],
     args: tuple[object, ...] | None = None,
     kwargs: dict[str, object] | None = None,
     *,
     restrict: bool = False,
-) -> object: ...
+) -> tuple[_R, types.SimpleNamespace]: ...
 def run_string(
     id: SupportsIndex,
     script: str | types.CodeType | Callable[[], object],
@@ -53,6 +55,7 @@ def decref(id: SupportsIndex, *, restrict: bool = False) -> None: ...
 def is_shareable(obj: object) -> bool: ...
 def capture_exception(exc: BaseException | None = None) -> types.SimpleNamespace: ...
 
+_Whence: TypeAlias = Literal[0, 1, 2, 3, 4, 5]
 WHENCE_UNKNOWN: Final = 0
 WHENCE_RUNTIME: Final = 1
 WHENCE_LEGACY_CAPI: Final = 2
diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi
index c77d75287c25..e368ddef7f4e 100644
--- a/mypy/typeshed/stdlib/_io.pyi
+++ b/mypy/typeshed/stdlib/_io.pyi
@@ -88,6 +88,7 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]
     def readlines(self, size: int | None = None, /) -> list[bytes]: ...
     def seek(self, pos: int, whence: int = 0, /) -> int: ...
 
+@type_check_only
 class _BufferedReaderStream(Protocol):
     def read(self, n: int = ..., /) -> bytes: ...
     # Optional: def readall(self) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi
index cc59146ed982..4a77e5be594a 100644
--- a/mypy/typeshed/stdlib/_json.pyi
+++ b/mypy/typeshed/stdlib/_json.pyi
@@ -48,4 +48,4 @@ class make_scanner:
 
 def encode_basestring(s: str, /) -> str: ...
 def encode_basestring_ascii(s: str, /) -> str: ...
-def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ...
+def scanstring(string: str, end: int, strict: bool = True) -> tuple[str, int]: ...
diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi
index 779fda3b67fe..ef45ff6dc3c8 100644
--- a/mypy/typeshed/stdlib/_msi.pyi
+++ b/mypy/typeshed/stdlib/_msi.pyi
@@ -1,8 +1,10 @@
 import sys
+from typing import type_check_only
 
 if sys.platform == "win32":
     class MSIError(Exception): ...
     # Actual typename View, not exposed by the implementation
+    @type_check_only
     class _View:
         def Execute(self, params: _Record | None = ...) -> None: ...
         def GetColumnInfo(self, kind: int) -> _Record: ...
@@ -14,6 +16,7 @@ if sys.platform == "win32":
         __init__: None  # type: ignore[assignment]
 
     # Actual typename SummaryInformation, not exposed by the implementation
+    @type_check_only
     class _SummaryInformation:
         def GetProperty(self, field: int) -> int | bytes | None: ...
         def GetPropertyCount(self) -> int: ...
@@ -24,6 +27,7 @@ if sys.platform == "win32":
         __init__: None  # type: ignore[assignment]
 
     # Actual typename Database, not exposed by the implementation
+    @type_check_only
     class _Database:
         def OpenView(self, sql: str) -> _View: ...
         def Commit(self) -> None: ...
@@ -34,6 +38,7 @@ if sys.platform == "win32":
         __init__: None  # type: ignore[assignment]
 
     # Actual typename Record, not exposed by the implementation
+    @type_check_only
     class _Record:
         def GetFieldCount(self) -> int: ...
         def GetInteger(self, field: int) -> int: ...
diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi
index 967215d8fa21..cb1c1bcfc4aa 100644
--- a/mypy/typeshed/stdlib/_operator.pyi
+++ b/mypy/typeshed/stdlib/_operator.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import SupportsGetItem
 from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence
 from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller
-from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload
+from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload, type_check_only
 from typing_extensions import ParamSpec, TypeAlias, TypeIs
 
 _R = TypeVar("_R")
@@ -16,26 +16,33 @@ _P = ParamSpec("_P")
 # operators can be overloaded to return an arbitrary object. For example,
 # the numpy.array comparison dunders return another numpy.array.
 
+@type_check_only
 class _SupportsDunderLT(Protocol):
     def __lt__(self, other: Any, /) -> Any: ...
 
+@type_check_only
 class _SupportsDunderGT(Protocol):
     def __gt__(self, other: Any, /) -> Any: ...
 
+@type_check_only
 class _SupportsDunderLE(Protocol):
     def __le__(self, other: Any, /) -> Any: ...
 
+@type_check_only
 class _SupportsDunderGE(Protocol):
     def __ge__(self, other: Any, /) -> Any: ...
 
 _SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT
 
+@type_check_only
 class _SupportsInversion(Protocol[_T_co]):
     def __invert__(self) -> _T_co: ...
 
+@type_check_only
 class _SupportsNeg(Protocol[_T_co]):
     def __neg__(self) -> _T_co: ...
 
+@type_check_only
 class _SupportsPos(Protocol[_T_co]):
     def __pos__(self) -> _T_co: ...
 
diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi
index 8e8afb600efa..03051bb09d3c 100644
--- a/mypy/typeshed/stdlib/_pickle.pyi
+++ b/mypy/typeshed/stdlib/_pickle.pyi
@@ -4,6 +4,7 @@ from pickle import PickleBuffer as PickleBuffer
 from typing import Any, Protocol, type_check_only
 from typing_extensions import TypeAlias
 
+@type_check_only
 class _ReadableFileobj(Protocol):
     def read(self, n: int, /) -> bytes: ...
     def readline(self) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/_sqlite3.pyi b/mypy/typeshed/stdlib/_sqlite3.pyi
index 6f06542c1ba7..50006dcf4032 100644
--- a/mypy/typeshed/stdlib/_sqlite3.pyi
+++ b/mypy/typeshed/stdlib/_sqlite3.pyi
@@ -16,6 +16,7 @@ from sqlite3 import (
     ProgrammingError as ProgrammingError,
     Row as Row,
     Warning as Warning,
+    _IsolationLevel,
 )
 from typing import Any, Final, Literal, TypeVar, overload
 from typing_extensions import TypeAlias
@@ -29,45 +30,45 @@ _SqliteData: TypeAlias = str | ReadableBuffer | int | float | None
 _Adapter: TypeAlias = Callable[[_T], _SqliteData]
 _Converter: TypeAlias = Callable[[bytes], Any]
 
-PARSE_COLNAMES: Final[int]
-PARSE_DECLTYPES: Final[int]
-SQLITE_ALTER_TABLE: Final[int]
-SQLITE_ANALYZE: Final[int]
-SQLITE_ATTACH: Final[int]
-SQLITE_CREATE_INDEX: Final[int]
-SQLITE_CREATE_TABLE: Final[int]
-SQLITE_CREATE_TEMP_INDEX: Final[int]
-SQLITE_CREATE_TEMP_TABLE: Final[int]
-SQLITE_CREATE_TEMP_TRIGGER: Final[int]
-SQLITE_CREATE_TEMP_VIEW: Final[int]
-SQLITE_CREATE_TRIGGER: Final[int]
-SQLITE_CREATE_VIEW: Final[int]
-SQLITE_CREATE_VTABLE: Final[int]
-SQLITE_DELETE: Final[int]
-SQLITE_DENY: Final[int]
-SQLITE_DETACH: Final[int]
-SQLITE_DONE: Final[int]
-SQLITE_DROP_INDEX: Final[int]
-SQLITE_DROP_TABLE: Final[int]
-SQLITE_DROP_TEMP_INDEX: Final[int]
-SQLITE_DROP_TEMP_TABLE: Final[int]
-SQLITE_DROP_TEMP_TRIGGER: Final[int]
-SQLITE_DROP_TEMP_VIEW: Final[int]
-SQLITE_DROP_TRIGGER: Final[int]
-SQLITE_DROP_VIEW: Final[int]
-SQLITE_DROP_VTABLE: Final[int]
-SQLITE_FUNCTION: Final[int]
-SQLITE_IGNORE: Final[int]
-SQLITE_INSERT: Final[int]
-SQLITE_OK: Final[int]
-SQLITE_PRAGMA: Final[int]
-SQLITE_READ: Final[int]
-SQLITE_RECURSIVE: Final[int]
-SQLITE_REINDEX: Final[int]
-SQLITE_SAVEPOINT: Final[int]
-SQLITE_SELECT: Final[int]
-SQLITE_TRANSACTION: Final[int]
-SQLITE_UPDATE: Final[int]
+PARSE_COLNAMES: Final = 2
+PARSE_DECLTYPES: Final = 1
+SQLITE_ALTER_TABLE: Final = 26
+SQLITE_ANALYZE: Final = 28
+SQLITE_ATTACH: Final = 24
+SQLITE_CREATE_INDEX: Final = 1
+SQLITE_CREATE_TABLE: Final = 2
+SQLITE_CREATE_TEMP_INDEX: Final = 3
+SQLITE_CREATE_TEMP_TABLE: Final = 4
+SQLITE_CREATE_TEMP_TRIGGER: Final = 5
+SQLITE_CREATE_TEMP_VIEW: Final = 6
+SQLITE_CREATE_TRIGGER: Final = 7
+SQLITE_CREATE_VIEW: Final = 8
+SQLITE_CREATE_VTABLE: Final = 29
+SQLITE_DELETE: Final = 9
+SQLITE_DENY: Final = 1
+SQLITE_DETACH: Final = 25
+SQLITE_DONE: Final = 101
+SQLITE_DROP_INDEX: Final = 10
+SQLITE_DROP_TABLE: Final = 11
+SQLITE_DROP_TEMP_INDEX: Final = 12
+SQLITE_DROP_TEMP_TABLE: Final = 13
+SQLITE_DROP_TEMP_TRIGGER: Final = 14
+SQLITE_DROP_TEMP_VIEW: Final = 15
+SQLITE_DROP_TRIGGER: Final = 16
+SQLITE_DROP_VIEW: Final = 17
+SQLITE_DROP_VTABLE: Final = 30
+SQLITE_FUNCTION: Final = 31
+SQLITE_IGNORE: Final = 2
+SQLITE_INSERT: Final = 18
+SQLITE_OK: Final = 0
+SQLITE_PRAGMA: Final = 19
+SQLITE_READ: Final = 20
+SQLITE_RECURSIVE: Final = 33
+SQLITE_REINDEX: Final = 27
+SQLITE_SAVEPOINT: Final = 32
+SQLITE_SELECT: Final = 21
+SQLITE_TRANSACTION: Final = 22
+SQLITE_UPDATE: Final = 23
 adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]]
 converters: dict[str, _Converter]
 sqlite_version: str
@@ -76,141 +77,141 @@ if sys.version_info < (3, 12):
     version: str
 
 if sys.version_info >= (3, 12):
-    LEGACY_TRANSACTION_CONTROL: Final[int]
-    SQLITE_DBCONFIG_DEFENSIVE: Final[int]
-    SQLITE_DBCONFIG_DQS_DDL: Final[int]
-    SQLITE_DBCONFIG_DQS_DML: Final[int]
-    SQLITE_DBCONFIG_ENABLE_FKEY: Final[int]
-    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int]
-    SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int]
-    SQLITE_DBCONFIG_ENABLE_QPSG: Final[int]
-    SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int]
-    SQLITE_DBCONFIG_ENABLE_VIEW: Final[int]
-    SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int]
-    SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int]
-    SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int]
-    SQLITE_DBCONFIG_RESET_DATABASE: Final[int]
-    SQLITE_DBCONFIG_TRIGGER_EQP: Final[int]
-    SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int]
-    SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int]
+    LEGACY_TRANSACTION_CONTROL: Final = -1
+    SQLITE_DBCONFIG_DEFENSIVE: Final = 1010
+    SQLITE_DBCONFIG_DQS_DDL: Final = 1014
+    SQLITE_DBCONFIG_DQS_DML: Final = 1013
+    SQLITE_DBCONFIG_ENABLE_FKEY: Final = 1002
+    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final = 1004
+    SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final = 1005
+    SQLITE_DBCONFIG_ENABLE_QPSG: Final = 1007
+    SQLITE_DBCONFIG_ENABLE_TRIGGER: Final = 1003
+    SQLITE_DBCONFIG_ENABLE_VIEW: Final = 1015
+    SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final = 1012
+    SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final = 1016
+    SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final = 1006
+    SQLITE_DBCONFIG_RESET_DATABASE: Final = 1009
+    SQLITE_DBCONFIG_TRIGGER_EQP: Final = 1008
+    SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final = 1017
+    SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final = 1011
 
 if sys.version_info >= (3, 11):
-    SQLITE_ABORT: Final[int]
-    SQLITE_ABORT_ROLLBACK: Final[int]
-    SQLITE_AUTH: Final[int]
-    SQLITE_AUTH_USER: Final[int]
-    SQLITE_BUSY: Final[int]
-    SQLITE_BUSY_RECOVERY: Final[int]
-    SQLITE_BUSY_SNAPSHOT: Final[int]
-    SQLITE_BUSY_TIMEOUT: Final[int]
-    SQLITE_CANTOPEN: Final[int]
-    SQLITE_CANTOPEN_CONVPATH: Final[int]
-    SQLITE_CANTOPEN_DIRTYWAL: Final[int]
-    SQLITE_CANTOPEN_FULLPATH: Final[int]
-    SQLITE_CANTOPEN_ISDIR: Final[int]
-    SQLITE_CANTOPEN_NOTEMPDIR: Final[int]
-    SQLITE_CANTOPEN_SYMLINK: Final[int]
-    SQLITE_CONSTRAINT: Final[int]
-    SQLITE_CONSTRAINT_CHECK: Final[int]
-    SQLITE_CONSTRAINT_COMMITHOOK: Final[int]
-    SQLITE_CONSTRAINT_FOREIGNKEY: Final[int]
-    SQLITE_CONSTRAINT_FUNCTION: Final[int]
-    SQLITE_CONSTRAINT_NOTNULL: Final[int]
-    SQLITE_CONSTRAINT_PINNED: Final[int]
-    SQLITE_CONSTRAINT_PRIMARYKEY: Final[int]
-    SQLITE_CONSTRAINT_ROWID: Final[int]
-    SQLITE_CONSTRAINT_TRIGGER: Final[int]
-    SQLITE_CONSTRAINT_UNIQUE: Final[int]
-    SQLITE_CONSTRAINT_VTAB: Final[int]
-    SQLITE_CORRUPT: Final[int]
-    SQLITE_CORRUPT_INDEX: Final[int]
-    SQLITE_CORRUPT_SEQUENCE: Final[int]
-    SQLITE_CORRUPT_VTAB: Final[int]
-    SQLITE_EMPTY: Final[int]
-    SQLITE_ERROR: Final[int]
-    SQLITE_ERROR_MISSING_COLLSEQ: Final[int]
-    SQLITE_ERROR_RETRY: Final[int]
-    SQLITE_ERROR_SNAPSHOT: Final[int]
-    SQLITE_FORMAT: Final[int]
-    SQLITE_FULL: Final[int]
-    SQLITE_INTERNAL: Final[int]
-    SQLITE_INTERRUPT: Final[int]
-    SQLITE_IOERR: Final[int]
-    SQLITE_IOERR_ACCESS: Final[int]
-    SQLITE_IOERR_AUTH: Final[int]
-    SQLITE_IOERR_BEGIN_ATOMIC: Final[int]
-    SQLITE_IOERR_BLOCKED: Final[int]
-    SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int]
-    SQLITE_IOERR_CLOSE: Final[int]
-    SQLITE_IOERR_COMMIT_ATOMIC: Final[int]
-    SQLITE_IOERR_CONVPATH: Final[int]
-    SQLITE_IOERR_CORRUPTFS: Final[int]
-    SQLITE_IOERR_DATA: Final[int]
-    SQLITE_IOERR_DELETE: Final[int]
-    SQLITE_IOERR_DELETE_NOENT: Final[int]
-    SQLITE_IOERR_DIR_CLOSE: Final[int]
-    SQLITE_IOERR_DIR_FSYNC: Final[int]
-    SQLITE_IOERR_FSTAT: Final[int]
-    SQLITE_IOERR_FSYNC: Final[int]
-    SQLITE_IOERR_GETTEMPPATH: Final[int]
-    SQLITE_IOERR_LOCK: Final[int]
-    SQLITE_IOERR_MMAP: Final[int]
-    SQLITE_IOERR_NOMEM: Final[int]
-    SQLITE_IOERR_RDLOCK: Final[int]
-    SQLITE_IOERR_READ: Final[int]
-    SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int]
-    SQLITE_IOERR_SEEK: Final[int]
-    SQLITE_IOERR_SHMLOCK: Final[int]
-    SQLITE_IOERR_SHMMAP: Final[int]
-    SQLITE_IOERR_SHMOPEN: Final[int]
-    SQLITE_IOERR_SHMSIZE: Final[int]
-    SQLITE_IOERR_SHORT_READ: Final[int]
-    SQLITE_IOERR_TRUNCATE: Final[int]
-    SQLITE_IOERR_UNLOCK: Final[int]
-    SQLITE_IOERR_VNODE: Final[int]
-    SQLITE_IOERR_WRITE: Final[int]
-    SQLITE_LIMIT_ATTACHED: Final[int]
-    SQLITE_LIMIT_COLUMN: Final[int]
-    SQLITE_LIMIT_COMPOUND_SELECT: Final[int]
-    SQLITE_LIMIT_EXPR_DEPTH: Final[int]
-    SQLITE_LIMIT_FUNCTION_ARG: Final[int]
-    SQLITE_LIMIT_LENGTH: Final[int]
-    SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int]
-    SQLITE_LIMIT_SQL_LENGTH: Final[int]
-    SQLITE_LIMIT_TRIGGER_DEPTH: Final[int]
-    SQLITE_LIMIT_VARIABLE_NUMBER: Final[int]
-    SQLITE_LIMIT_VDBE_OP: Final[int]
-    SQLITE_LIMIT_WORKER_THREADS: Final[int]
-    SQLITE_LOCKED: Final[int]
-    SQLITE_LOCKED_SHAREDCACHE: Final[int]
-    SQLITE_LOCKED_VTAB: Final[int]
-    SQLITE_MISMATCH: Final[int]
-    SQLITE_MISUSE: Final[int]
-    SQLITE_NOLFS: Final[int]
-    SQLITE_NOMEM: Final[int]
-    SQLITE_NOTADB: Final[int]
-    SQLITE_NOTFOUND: Final[int]
-    SQLITE_NOTICE: Final[int]
-    SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int]
-    SQLITE_NOTICE_RECOVER_WAL: Final[int]
-    SQLITE_OK_LOAD_PERMANENTLY: Final[int]
-    SQLITE_OK_SYMLINK: Final[int]
-    SQLITE_PERM: Final[int]
-    SQLITE_PROTOCOL: Final[int]
-    SQLITE_RANGE: Final[int]
-    SQLITE_READONLY: Final[int]
-    SQLITE_READONLY_CANTINIT: Final[int]
-    SQLITE_READONLY_CANTLOCK: Final[int]
-    SQLITE_READONLY_DBMOVED: Final[int]
-    SQLITE_READONLY_DIRECTORY: Final[int]
-    SQLITE_READONLY_RECOVERY: Final[int]
-    SQLITE_READONLY_ROLLBACK: Final[int]
-    SQLITE_ROW: Final[int]
-    SQLITE_SCHEMA: Final[int]
-    SQLITE_TOOBIG: Final[int]
-    SQLITE_WARNING: Final[int]
-    SQLITE_WARNING_AUTOINDEX: Final[int]
-    threadsafety: Final[int]
+    SQLITE_ABORT: Final = 4
+    SQLITE_ABORT_ROLLBACK: Final = 516
+    SQLITE_AUTH: Final = 23
+    SQLITE_AUTH_USER: Final = 279
+    SQLITE_BUSY: Final = 5
+    SQLITE_BUSY_RECOVERY: Final = 261
+    SQLITE_BUSY_SNAPSHOT: Final = 517
+    SQLITE_BUSY_TIMEOUT: Final = 773
+    SQLITE_CANTOPEN: Final = 14
+    SQLITE_CANTOPEN_CONVPATH: Final = 1038
+    SQLITE_CANTOPEN_DIRTYWAL: Final = 1294
+    SQLITE_CANTOPEN_FULLPATH: Final = 782
+    SQLITE_CANTOPEN_ISDIR: Final = 526
+    SQLITE_CANTOPEN_NOTEMPDIR: Final = 270
+    SQLITE_CANTOPEN_SYMLINK: Final = 1550
+    SQLITE_CONSTRAINT: Final = 19
+    SQLITE_CONSTRAINT_CHECK: Final = 275
+    SQLITE_CONSTRAINT_COMMITHOOK: Final = 531
+    SQLITE_CONSTRAINT_FOREIGNKEY: Final = 787
+    SQLITE_CONSTRAINT_FUNCTION: Final = 1043
+    SQLITE_CONSTRAINT_NOTNULL: Final = 1299
+    SQLITE_CONSTRAINT_PINNED: Final = 2835
+    SQLITE_CONSTRAINT_PRIMARYKEY: Final = 1555
+    SQLITE_CONSTRAINT_ROWID: Final = 2579
+    SQLITE_CONSTRAINT_TRIGGER: Final = 1811
+    SQLITE_CONSTRAINT_UNIQUE: Final = 2067
+    SQLITE_CONSTRAINT_VTAB: Final = 2323
+    SQLITE_CORRUPT: Final = 11
+    SQLITE_CORRUPT_INDEX: Final = 779
+    SQLITE_CORRUPT_SEQUENCE: Final = 523
+    SQLITE_CORRUPT_VTAB: Final = 267
+    SQLITE_EMPTY: Final = 16
+    SQLITE_ERROR: Final = 1
+    SQLITE_ERROR_MISSING_COLLSEQ: Final = 257
+    SQLITE_ERROR_RETRY: Final = 513
+    SQLITE_ERROR_SNAPSHOT: Final = 769
+    SQLITE_FORMAT: Final = 24
+    SQLITE_FULL: Final = 13
+    SQLITE_INTERNAL: Final = 2
+    SQLITE_INTERRUPT: Final = 9
+    SQLITE_IOERR: Final = 10
+    SQLITE_IOERR_ACCESS: Final = 3338
+    SQLITE_IOERR_AUTH: Final = 7178
+    SQLITE_IOERR_BEGIN_ATOMIC: Final = 7434
+    SQLITE_IOERR_BLOCKED: Final = 2826
+    SQLITE_IOERR_CHECKRESERVEDLOCK: Final = 3594
+    SQLITE_IOERR_CLOSE: Final = 4106
+    SQLITE_IOERR_COMMIT_ATOMIC: Final = 7690
+    SQLITE_IOERR_CONVPATH: Final = 6666
+    SQLITE_IOERR_CORRUPTFS: Final = 8458
+    SQLITE_IOERR_DATA: Final = 8202
+    SQLITE_IOERR_DELETE: Final = 2570
+    SQLITE_IOERR_DELETE_NOENT: Final = 5898
+    SQLITE_IOERR_DIR_CLOSE: Final = 4362
+    SQLITE_IOERR_DIR_FSYNC: Final = 1290
+    SQLITE_IOERR_FSTAT: Final = 1802
+    SQLITE_IOERR_FSYNC: Final = 1034
+    SQLITE_IOERR_GETTEMPPATH: Final = 6410
+    SQLITE_IOERR_LOCK: Final = 3850
+    SQLITE_IOERR_MMAP: Final = 6154
+    SQLITE_IOERR_NOMEM: Final = 3082
+    SQLITE_IOERR_RDLOCK: Final = 2314
+    SQLITE_IOERR_READ: Final = 266
+    SQLITE_IOERR_ROLLBACK_ATOMIC: Final = 7946
+    SQLITE_IOERR_SEEK: Final = 5642
+    SQLITE_IOERR_SHMLOCK: Final = 5130
+    SQLITE_IOERR_SHMMAP: Final = 5386
+    SQLITE_IOERR_SHMOPEN: Final = 4618
+    SQLITE_IOERR_SHMSIZE: Final = 4874
+    SQLITE_IOERR_SHORT_READ: Final = 522
+    SQLITE_IOERR_TRUNCATE: Final = 1546
+    SQLITE_IOERR_UNLOCK: Final = 2058
+    SQLITE_IOERR_VNODE: Final = 6922
+    SQLITE_IOERR_WRITE: Final = 778
+    SQLITE_LIMIT_ATTACHED: Final = 7
+    SQLITE_LIMIT_COLUMN: Final = 22
+    SQLITE_LIMIT_COMPOUND_SELECT: Final = 4
+    SQLITE_LIMIT_EXPR_DEPTH: Final = 3
+    SQLITE_LIMIT_FUNCTION_ARG: Final = 6
+    SQLITE_LIMIT_LENGTH: Final = 0
+    SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final = 8
+    SQLITE_LIMIT_SQL_LENGTH: Final = 1
+    SQLITE_LIMIT_TRIGGER_DEPTH: Final = 10
+    SQLITE_LIMIT_VARIABLE_NUMBER: Final = 9
+    SQLITE_LIMIT_VDBE_OP: Final = 5
+    SQLITE_LIMIT_WORKER_THREADS: Final = 11
+    SQLITE_LOCKED: Final = 6
+    SQLITE_LOCKED_SHAREDCACHE: Final = 262
+    SQLITE_LOCKED_VTAB: Final = 518
+    SQLITE_MISMATCH: Final = 20
+    SQLITE_MISUSE: Final = 21
+    SQLITE_NOLFS: Final = 22
+    SQLITE_NOMEM: Final = 7
+    SQLITE_NOTADB: Final = 26
+    SQLITE_NOTFOUND: Final = 12
+    SQLITE_NOTICE: Final = 27
+    SQLITE_NOTICE_RECOVER_ROLLBACK: Final = 539
+    SQLITE_NOTICE_RECOVER_WAL: Final = 283
+    SQLITE_OK_LOAD_PERMANENTLY: Final = 256
+    SQLITE_OK_SYMLINK: Final = 512
+    SQLITE_PERM: Final = 3
+    SQLITE_PROTOCOL: Final = 15
+    SQLITE_RANGE: Final = 25
+    SQLITE_READONLY: Final = 8
+    SQLITE_READONLY_CANTINIT: Final = 1288
+    SQLITE_READONLY_CANTLOCK: Final = 520
+    SQLITE_READONLY_DBMOVED: Final = 1032
+    SQLITE_READONLY_DIRECTORY: Final = 1544
+    SQLITE_READONLY_RECOVERY: Final = 264
+    SQLITE_READONLY_ROLLBACK: Final = 776
+    SQLITE_ROW: Final = 100
+    SQLITE_SCHEMA: Final = 17
+    SQLITE_TOOBIG: Final = 18
+    SQLITE_WARNING: Final = 28
+    SQLITE_WARNING_AUTOINDEX: Final = 284
+    threadsafety: Literal[0, 1, 3]
 
 # Can take or return anything depending on what's in the registry.
 @overload
@@ -225,7 +226,7 @@ if sys.version_info >= (3, 12):
         database: StrOrBytesPath,
         timeout: float = 5.0,
         detect_types: int = 0,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
+        isolation_level: _IsolationLevel = "DEFERRED",
         check_same_thread: bool = True,
         cached_statements: int = 128,
         uri: bool = False,
@@ -237,7 +238,7 @@ if sys.version_info >= (3, 12):
         database: StrOrBytesPath,
         timeout: float,
         detect_types: int,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None,
+        isolation_level: _IsolationLevel,
         check_same_thread: bool,
         factory: type[_ConnectionT],
         cached_statements: int = 128,
@@ -250,7 +251,7 @@ if sys.version_info >= (3, 12):
         database: StrOrBytesPath,
         timeout: float = 5.0,
         detect_types: int = 0,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
+        isolation_level: _IsolationLevel = "DEFERRED",
         check_same_thread: bool = True,
         *,
         factory: type[_ConnectionT],
@@ -265,7 +266,7 @@ else:
         database: StrOrBytesPath,
         timeout: float = 5.0,
         detect_types: int = 0,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
+        isolation_level: _IsolationLevel = "DEFERRED",
         check_same_thread: bool = True,
         cached_statements: int = 128,
         uri: bool = False,
@@ -275,7 +276,7 @@ else:
         database: StrOrBytesPath,
         timeout: float,
         detect_types: int,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None,
+        isolation_level: _IsolationLevel,
         check_same_thread: bool,
         factory: type[_ConnectionT],
         cached_statements: int = 128,
@@ -286,7 +287,7 @@ else:
         database: StrOrBytesPath,
         timeout: float = 5.0,
         detect_types: int = 0,
-        isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
+        isolation_level: _IsolationLevel = "DEFERRED",
         check_same_thread: bool = True,
         *,
         factory: type[_ConnectionT],
diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi
index 7ab880e4def7..88dd06780904 100644
--- a/mypy/typeshed/stdlib/_ssl.pyi
+++ b/mypy/typeshed/stdlib/_ssl.pyi
@@ -12,7 +12,7 @@ from ssl import (
     SSLWantWriteError as SSLWantWriteError,
     SSLZeroReturnError as SSLZeroReturnError,
 )
-from typing import Any, ClassVar, Literal, TypedDict, final, overload
+from typing import Any, ClassVar, Literal, TypedDict, final, overload, type_check_only
 from typing_extensions import NotRequired, Self, TypeAlias
 
 _PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray
@@ -20,6 +20,7 @@ _PCTRTT: TypeAlias = tuple[tuple[str, str], ...]
 _PCTRTTT: TypeAlias = tuple[_PCTRTT, ...]
 _PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT]
 
+@type_check_only
 class _Cipher(TypedDict):
     aead: bool
     alg_bits: int
@@ -33,6 +34,7 @@ class _Cipher(TypedDict):
     strength_bits: int
     symmetric: str
 
+@type_check_only
 class _CertInfo(TypedDict):
     subject: tuple[tuple[tuple[str, str], ...], ...]
     issuer: tuple[tuple[tuple[str, str], ...], ...]
diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi
index 9cfbe55b4fe3..970130dfb09c 100644
--- a/mypy/typeshed/stdlib/_thread.pyi
+++ b/mypy/typeshed/stdlib/_thread.pyi
@@ -73,7 +73,7 @@ def start_new(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]
 def start_new(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ...
 
 if sys.version_info >= (3, 10):
-    def interrupt_main(signum: signal.Signals = ..., /) -> None: ...
+    def interrupt_main(signum: signal.Signals = signal.SIGINT, /) -> None: ...
 
 else:
     def interrupt_main() -> None: ...
diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
index f322244016dd..98a369dfc589 100644
--- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi
+++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
@@ -65,10 +65,10 @@ MaybeNone: TypeAlias = Any  # stable
 # In cases where the sentinel object is exported and can be used by user code,
 # a construct like this is better:
 #
-# _SentinelType = NewType("_SentinelType", object)
-# sentinel: _SentinelType
+# _SentinelType = NewType("_SentinelType", object)  # does not exist at runtime
+# sentinel: Final[_SentinelType]
 # def foo(x: int | None | _SentinelType = ...) -> None: ...
-sentinel: Any
+sentinel: Any  # stable
 
 # stable
 class IdentityFunction(Protocol):
@@ -82,19 +82,21 @@ class SupportsNext(Protocol[_T_co]):
 class SupportsAnext(Protocol[_T_co]):
     def __anext__(self) -> Awaitable[_T_co]: ...
 
-# Comparison protocols
+class SupportsBool(Protocol):
+    def __bool__(self) -> bool: ...
 
+# Comparison protocols
 class SupportsDunderLT(Protocol[_T_contra]):
-    def __lt__(self, other: _T_contra, /) -> bool: ...
+    def __lt__(self, other: _T_contra, /) -> SupportsBool: ...
 
 class SupportsDunderGT(Protocol[_T_contra]):
-    def __gt__(self, other: _T_contra, /) -> bool: ...
+    def __gt__(self, other: _T_contra, /) -> SupportsBool: ...
 
 class SupportsDunderLE(Protocol[_T_contra]):
-    def __le__(self, other: _T_contra, /) -> bool: ...
+    def __le__(self, other: _T_contra, /) -> SupportsBool: ...
 
 class SupportsDunderGE(Protocol[_T_contra]):
-    def __ge__(self, other: _T_contra, /) -> bool: ...
+    def __ge__(self, other: _T_contra, /) -> SupportsBool: ...
 
 class SupportsAllComparisons(
     SupportsDunderLT[Any], SupportsDunderGT[Any], SupportsDunderLE[Any], SupportsDunderGE[Any], Protocol
diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi
index 0f71a0687748..6083ea4ae57a 100644
--- a/mypy/typeshed/stdlib/_winapi.pyi
+++ b/mypy/typeshed/stdlib/_winapi.pyi
@@ -172,6 +172,9 @@ if sys.platform == "win32":
         ERROR_ACCESS_DENIED: Final = 5
         ERROR_PRIVILEGE_NOT_HELD: Final = 1314
 
+    if sys.version_info >= (3, 14):
+        COPY_FILE_DIRECTORY: Final = 0x00000080
+
     def CloseHandle(handle: int, /) -> None: ...
     @overload
     def ConnectNamedPipe(handle: int, overlapped: Literal[True]) -> Overlapped: ...
diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
index c22777e45436..3c3ba116a692 100644
--- a/mypy/typeshed/stdlib/argparse.pyi
+++ b/mypy/typeshed/stdlib/argparse.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import SupportsWrite, sentinel
 from collections.abc import Callable, Generator, Iterable, Sequence
 from re import Pattern
-from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload
+from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, deprecated
 
 __all__ = [
@@ -114,6 +114,7 @@ class _ActionsContainer:
     def _handle_conflict_error(self, action: Action, conflicting_actions: Iterable[tuple[str, Action]]) -> NoReturn: ...
     def _handle_conflict_resolve(self, action: Action, conflicting_actions: Iterable[tuple[str, Action]]) -> None: ...
 
+@type_check_only
 class _FormatterClass(Protocol):
     def __call__(self, *, prog: str) -> HelpFormatter: ...
 
@@ -283,7 +284,7 @@ class HelpFormatter:
 
     if sys.version_info >= (3, 14):
         def __init__(
-            self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, color: bool = False
+            self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, color: bool = True
         ) -> None: ...
     else:
         def __init__(
@@ -497,16 +498,40 @@ else:
 class _ArgumentGroup(_ActionsContainer):
     title: str | None
     _group_actions: list[Action]
-    def __init__(
-        self,
-        container: _ActionsContainer,
-        title: str | None = None,
-        description: str | None = None,
-        *,
-        prefix_chars: str = ...,
-        argument_default: Any = ...,
-        conflict_handler: str = ...,
-    ) -> None: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        def __init__(
+            self,
+            container: _ActionsContainer,
+            title: str | None = None,
+            description: str | None = None,
+            *,
+            argument_default: Any = ...,
+            conflict_handler: str = ...,
+        ) -> None: ...
+        @overload
+        @deprecated("Undocumented `prefix_chars` parameter is deprecated since Python 3.14.")
+        def __init__(
+            self,
+            container: _ActionsContainer,
+            title: str | None = None,
+            description: str | None = None,
+            *,
+            prefix_chars: str,
+            argument_default: Any = ...,
+            conflict_handler: str = ...,
+        ) -> None: ...
+    else:
+        def __init__(
+            self,
+            container: _ActionsContainer,
+            title: str | None = None,
+            description: str | None = None,
+            *,
+            prefix_chars: str = ...,
+            argument_default: Any = ...,
+            conflict_handler: str = ...,
+        ) -> None: ...
 
 # undocumented
 class _MutuallyExclusiveGroup(_ArgumentGroup):
@@ -740,9 +765,9 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
             fromfile_prefix_chars: str | None = ...,
             argument_default: Any = ...,
             conflict_handler: str = ...,
-            add_help: bool = ...,
-            allow_abbrev: bool = ...,
-            exit_on_error: bool = ...,
+            add_help: bool = True,
+            allow_abbrev: bool = True,
+            exit_on_error: bool = True,
             suggest_on_error: bool = False,
             color: bool = False,
             **kwargs: Any,  # Accepting any additional kwargs for custom parser classes
@@ -766,9 +791,9 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
             fromfile_prefix_chars: str | None = ...,
             argument_default: Any = ...,
             conflict_handler: str = ...,
-            add_help: bool = ...,
-            allow_abbrev: bool = ...,
-            exit_on_error: bool = ...,
+            add_help: bool = True,
+            allow_abbrev: bool = True,
+            exit_on_error: bool = True,
             **kwargs: Any,  # Accepting any additional kwargs for custom parser classes
         ) -> _ArgumentParserT: ...
     else:
@@ -789,9 +814,9 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]):
             fromfile_prefix_chars: str | None = ...,
             argument_default: Any = ...,
             conflict_handler: str = ...,
-            add_help: bool = ...,
-            allow_abbrev: bool = ...,
-            exit_on_error: bool = ...,
+            add_help: bool = True,
+            allow_abbrev: bool = True,
+            exit_on_error: bool = True,
             **kwargs: Any,  # Accepting any additional kwargs for custom parser classes
         ) -> _ArgumentParserT: ...
 
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index fcd6e8b01e74..3ba56f55932a 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -10,7 +10,7 @@ from _ast import (
 )
 from _typeshed import ReadableBuffer, Unused
 from collections.abc import Iterable, Iterator, Sequence
-from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload
+from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload, type_check_only
 from typing_extensions import Self, Unpack, deprecated
 
 if sys.version_info >= (3, 13):
@@ -20,6 +20,7 @@ if sys.version_info >= (3, 13):
 _EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None)
 
 # Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes
+@type_check_only
 class _Attributes(TypedDict, Generic[_EndPositionT], total=False):
     lineno: int
     col_offset: int
@@ -1698,8 +1699,14 @@ if sys.version_info >= (3, 12):
                 self, *, name: str = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]]
             ) -> Self: ...
 
-class _ABC(type):
-    def __init__(cls, *args: Unused) -> None: ...
+if sys.version_info >= (3, 14):
+    @type_check_only
+    class _ABC(type):
+        def __init__(cls, *args: Unused) -> None: ...
+
+else:
+    class _ABC(type):
+        def __init__(cls, *args: Unused) -> None: ...
 
 if sys.version_info < (3, 14):
     @deprecated("Replaced by ast.Constant; removed in Python 3.14")
diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi
index 58739816a67e..23cf57aaac33 100644
--- a/mypy/typeshed/stdlib/asyncio/__init__.pyi
+++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi
@@ -41,14 +41,11 @@ if sys.platform == "win32":
             "Server",  # from base_events
             "iscoroutinefunction",  # from coroutines
             "iscoroutine",  # from coroutines
-            "_AbstractEventLoopPolicy",  # from events
             "AbstractEventLoop",  # from events
             "AbstractServer",  # from events
             "Handle",  # from events
             "TimerHandle",  # from events
-            "_get_event_loop_policy",  # from events
             "get_event_loop_policy",  # from events
-            "_set_event_loop_policy",  # from events
             "set_event_loop_policy",  # from events
             "get_event_loop",  # from events
             "set_event_loop",  # from events
@@ -517,14 +514,11 @@ else:
             "Server",  # from base_events
             "iscoroutinefunction",  # from coroutines
             "iscoroutine",  # from coroutines
-            "_AbstractEventLoopPolicy",  # from events
             "AbstractEventLoop",  # from events
             "AbstractServer",  # from events
             "Handle",  # from events
             "TimerHandle",  # from events
-            "_get_event_loop_policy",  # from events
             "get_event_loop_policy",  # from events
-            "_set_event_loop_policy",  # from events
             "set_event_loop_policy",  # from events
             "get_event_loop",  # from events
             "set_event_loop",  # from events
@@ -610,7 +604,6 @@ else:
             "DatagramTransport",  # from transports
             "SubprocessTransport",  # from transports
             "SelectorEventLoop",  # from unix_events
-            "_DefaultEventLoopPolicy",  # from unix_events
             "EventLoop",  # from unix_events
         )
     elif sys.version_info >= (3, 13):
diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi
index 55d2fbdbdb62..2cd0f2e3a7e4 100644
--- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi
+++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi
@@ -1,19 +1,17 @@
+from _asyncio import Future
 from collections.abc import Callable, Sequence
 from contextvars import Context
 from typing import Any, Final
+from typing_extensions import TypeIs
 
 from . import futures
 
 __all__ = ()
 
-# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py
-# but it leads to circular import error in pytype tool.
-# That's why the import order is reversed.
-from .futures import isfuture as isfuture
-
 _PENDING: Final = "PENDING"  # undocumented
 _CANCELLED: Final = "CANCELLED"  # undocumented
 _FINISHED: Final = "FINISHED"  # undocumented
 
+def isfuture(obj: object) -> TypeIs[Future[Any]]: ...
 def _format_callbacks(cb: Sequence[tuple[Callable[[futures.Future[Any]], None], Context]]) -> str: ...  # undocumented
 def _future_repr_info(future: futures.Future[Any]) -> list[str]: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi
index 688ef3ed0879..a37f6f697b9a 100644
--- a/mypy/typeshed/stdlib/asyncio/events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/events.pyi
@@ -12,7 +12,7 @@ from collections.abc import Callable, Sequence
 from concurrent.futures import Executor
 from contextvars import Context
 from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket
-from typing import IO, Any, Literal, Protocol, TypeVar, overload
+from typing import IO, Any, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack, deprecated
 
 from . import _AwaitableLike, _CoroutineLike
@@ -28,14 +28,11 @@ if sys.version_info < (3, 14):
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.version_info >= (3, 14):
     __all__ = (
-        "_AbstractEventLoopPolicy",
         "AbstractEventLoop",
         "AbstractServer",
         "Handle",
         "TimerHandle",
-        "_get_event_loop_policy",
         "get_event_loop_policy",
-        "_set_event_loop_policy",
         "set_event_loop_policy",
         "get_event_loop",
         "set_event_loop",
@@ -71,6 +68,7 @@ _ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], object]
 _ProtocolFactory: TypeAlias = Callable[[], BaseProtocol]
 _SSLContext: TypeAlias = bool | None | ssl.SSLContext
 
+@type_check_only
 class _TaskFactory(Protocol):
     def __call__(self, loop: AbstractEventLoop, factory: _CoroutineLike[_T], /) -> Future[_T]: ...
 
@@ -602,6 +600,9 @@ class AbstractEventLoop:
     @abstractmethod
     async def shutdown_default_executor(self) -> None: ...
 
+# This class does not exist at runtime, but stubtest complains if it's marked as
+# @type_check_only because it has an alias that does exist at runtime. See mypy#19568.
+# @type_check_only
 class _AbstractEventLoopPolicy:
     @abstractmethod
     def get_event_loop(self) -> AbstractEventLoop: ...
diff --git a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi
index 41505b14cd08..597eb9e56e1a 100644
--- a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi
+++ b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi
@@ -3,9 +3,10 @@ import sys
 import traceback
 from collections.abc import Iterable
 from types import FrameType, FunctionType
-from typing import Any, overload
+from typing import Any, overload, type_check_only
 from typing_extensions import TypeAlias
 
+@type_check_only
 class _HasWrapper:
     __wrapper__: _HasWrapper | FunctionType
 
diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi
index 644d2d0e94ca..c907c7036b04 100644
--- a/mypy/typeshed/stdlib/asyncio/futures.pyi
+++ b/mypy/typeshed/stdlib/asyncio/futures.pyi
@@ -1,9 +1,9 @@
 import sys
 from _asyncio import Future as Future
 from concurrent.futures._base import Future as _ConcurrentFuture
-from typing import Any, TypeVar
-from typing_extensions import TypeIs
+from typing import TypeVar
 
+from .base_futures import isfuture as isfuture
 from .events import AbstractEventLoop
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
@@ -16,8 +16,4 @@ else:
 
 _T = TypeVar("_T")
 
-# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py
-# but it leads to circular import error in pytype tool.
-# That's why the import order is reversed.
-def isfuture(obj: object) -> TypeIs[Future[Any]]: ...
 def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ...
diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi
index 63cd98f53da3..2fa2226d0e6a 100644
--- a/mypy/typeshed/stdlib/asyncio/queues.pyi
+++ b/mypy/typeshed/stdlib/asyncio/queues.pyi
@@ -1,4 +1,5 @@
 import sys
+from _typeshed import SupportsRichComparisonT
 from asyncio.events import AbstractEventLoop
 from types import GenericAlias
 from typing import Any, Generic, TypeVar
@@ -50,5 +51,5 @@ class Queue(Generic[_T], _LoopBoundMixin):  # noqa: Y059
     if sys.version_info >= (3, 13):
         def shutdown(self, immediate: bool = False) -> None: ...
 
-class PriorityQueue(Queue[_T]): ...
+class PriorityQueue(Queue[SupportsRichComparisonT]): ...
 class LifoQueue(Queue[_T]): ...
diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi
index 43df5ae2d0c8..bf8db0246ee2 100644
--- a/mypy/typeshed/stdlib/asyncio/streams.pyi
+++ b/mypy/typeshed/stdlib/asyncio/streams.pyi
@@ -3,7 +3,7 @@ import sys
 from _typeshed import ReadableBuffer, StrPath
 from collections.abc import Awaitable, Callable, Iterable, Sequence, Sized
 from types import ModuleType
-from typing import Any, Protocol, SupportsIndex
+from typing import Any, Protocol, SupportsIndex, type_check_only
 from typing_extensions import Self, TypeAlias
 
 from . import events, protocols, transports
@@ -25,6 +25,7 @@ else:
 
 _ClientConnectedCallback: TypeAlias = Callable[[StreamReader, StreamWriter], Awaitable[None] | None]
 
+@type_check_only
 class _ReaduntilBuffer(ReadableBuffer, Sized, Protocol): ...
 
 if sys.version_info >= (3, 10):
diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi
index a088e95af653..4104b3ecfeee 100644
--- a/mypy/typeshed/stdlib/asyncio/tasks.pyi
+++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi
@@ -8,7 +8,7 @@ from _asyncio import (
     _unregister_task as _unregister_task,
 )
 from collections.abc import AsyncIterator, Awaitable, Coroutine, Generator, Iterable, Iterator
-from typing import Any, Literal, Protocol, TypeVar, overload
+from typing import Any, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias
 
 from . import _CoroutineLike
@@ -87,6 +87,7 @@ FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION
 ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
 
 if sys.version_info >= (3, 13):
+    @type_check_only
     class _SyncAndAsyncIterator(Iterator[_T_co], AsyncIterator[_T_co], Protocol[_T_co]): ...
 
     def as_completed(fs: Iterable[_FutureLike[_T]], *, timeout: float | None = None) -> _SyncAndAsyncIterator[Future[_T]]: ...
@@ -445,6 +446,7 @@ elif sys.version_info >= (3, 12):
 if sys.version_info >= (3, 12):
     _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True)
 
+    @type_check_only
     class _CustomTaskConstructor(Protocol[_TaskT_co]):
         def __call__(
             self,
@@ -457,6 +459,7 @@ if sys.version_info >= (3, 12):
             eager_start: bool,
         ) -> _TaskT_co: ...
 
+    @type_check_only
     class _EagerTaskFactoryType(Protocol[_TaskT_co]):
         def __call__(
             self,
diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
index 49f200dcdcae..b2bf22a27677 100644
--- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
@@ -16,7 +16,7 @@ _Ts = TypeVarTuple("_Ts")
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.platform != "win32":
     if sys.version_info >= (3, 14):
-        __all__ = ("SelectorEventLoop", "_DefaultEventLoopPolicy", "EventLoop")
+        __all__ = ("SelectorEventLoop", "EventLoop")
     elif sys.version_info >= (3, 13):
         # Adds EventLoop
         __all__ = (
diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi
index b73f894093ce..b6be2210ffe2 100644
--- a/mypy/typeshed/stdlib/bdb.pyi
+++ b/mypy/typeshed/stdlib/bdb.pyi
@@ -81,8 +81,8 @@ class Bdb:
     def get_bpbynumber(self, arg: SupportsInt) -> Breakpoint: ...
     def get_break(self, filename: str, lineno: int) -> bool: ...
     def get_breaks(self, filename: str, lineno: int) -> list[Breakpoint]: ...
-    def get_file_breaks(self, filename: str) -> list[Breakpoint]: ...
-    def get_all_breaks(self) -> list[Breakpoint]: ...
+    def get_file_breaks(self, filename: str) -> list[int]: ...
+    def get_all_breaks(self) -> dict[str, list[int]]: ...
     def get_stack(self, f: FrameType | None, t: TracebackType | None) -> tuple[list[tuple[FrameType, int]], int]: ...
     def format_stack_entry(self, frame_lineno: tuple[FrameType, int], lprefix: str = ": ") -> str: ...
     def run(
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index b853330b18fb..baf399e7bb77 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -451,9 +451,11 @@ class complex:
         @classmethod
         def from_number(cls, number: complex | SupportsComplex | SupportsFloat | SupportsIndex, /) -> Self: ...
 
+@type_check_only
 class _FormatMapMapping(Protocol):
     def __getitem__(self, key: str, /) -> Any: ...
 
+@type_check_only
 class _TranslateTable(Protocol):
     def __getitem__(self, key: int, /) -> str | int | None: ...
 
@@ -1240,6 +1242,9 @@ class property:
     def __set__(self, instance: Any, value: Any, /) -> None: ...
     def __delete__(self, instance: Any, /) -> None: ...
 
+# This class does not exist at runtime, but stubtest complains if it's marked as
+# @type_check_only because it has an alias that does exist at runtime. See mypy#19568.
+# @type_check_only
 @final
 class _NotImplementedType(Any):
     __call__: None
@@ -1257,7 +1262,7 @@ def chr(i: int | SupportsIndex, /) -> str: ...
 
 if sys.version_info >= (3, 10):
     def aiter(async_iterable: SupportsAiter[_SupportsAnextT_co], /) -> _SupportsAnextT_co: ...
-
+    @type_check_only
     class _SupportsSynchronousAnext(Protocol[_AwaitableT_co]):
         def __anext__(self) -> _AwaitableT_co: ...
 
@@ -1413,7 +1418,7 @@ help: _sitebuiltins._Helper
 def hex(number: int | SupportsIndex, /) -> str: ...
 def id(obj: object, /) -> int: ...
 def input(prompt: object = "", /) -> str: ...
-
+@type_check_only
 class _GetItemIterable(Protocol[_T_co]):
     def __getitem__(self, i: int, /) -> _T_co: ...
 
@@ -1426,7 +1431,6 @@ def iter(object: Callable[[], _T | None], sentinel: None, /) -> Iterator[_T]: ..
 @overload
 def iter(object: Callable[[], _T], sentinel: object, /) -> Iterator[_T]: ...
 
-# Keep this alias in sync with unittest.case._ClassInfo
 if sys.version_info >= (3, 10):
     _ClassInfo: TypeAlias = type | types.UnionType | tuple[_ClassInfo, ...]
 else:
@@ -1669,7 +1673,7 @@ def open(
     opener: _Opener | None = None,
 ) -> IO[Any]: ...
 def ord(c: str | bytes | bytearray, /) -> int: ...
-
+@type_check_only
 class _SupportsWriteAndFlush(SupportsWrite[_T_contra], SupportsFlush, Protocol[_T_contra]): ...
 
 @overload
@@ -1688,12 +1692,15 @@ def print(
 _E_contra = TypeVar("_E_contra", contravariant=True)
 _M_contra = TypeVar("_M_contra", contravariant=True)
 
+@type_check_only
 class _SupportsPow2(Protocol[_E_contra, _T_co]):
     def __pow__(self, other: _E_contra, /) -> _T_co: ...
 
+@type_check_only
 class _SupportsPow3NoneOnly(Protocol[_E_contra, _T_co]):
     def __pow__(self, other: _E_contra, modulo: None = None, /) -> _T_co: ...
 
+@type_check_only
 class _SupportsPow3(Protocol[_E_contra, _M_contra, _T_co]):
     def __pow__(self, other: _E_contra, modulo: _M_contra, /) -> _T_co: ...
 
@@ -1758,9 +1765,11 @@ def repr(obj: object, /) -> str: ...
 # and https://github.com/python/typeshed/pull/9151
 # on why we don't use `SupportsRound` from `typing.pyi`
 
+@type_check_only
 class _SupportsRound1(Protocol[_T_co]):
     def __round__(self) -> _T_co: ...
 
+@type_check_only
 class _SupportsRound2(Protocol[_T_co]):
     def __round__(self, ndigits: int, /) -> _T_co: ...
 
@@ -1782,6 +1791,7 @@ def sorted(iterable: Iterable[_T], /, *, key: Callable[[_T], SupportsRichCompari
 _AddableT1 = TypeVar("_AddableT1", bound=SupportsAdd[Any, Any])
 _AddableT2 = TypeVar("_AddableT2", bound=SupportsAdd[Any, Any])
 
+@type_check_only
 class _SupportsSumWithNoDefaultGiven(SupportsAdd[Any, Any], SupportsRAdd[int, Any], Protocol): ...
 
 _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWithNoDefaultGiven)
diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi
index dce6187a2da1..7bd829d040cb 100644
--- a/mypy/typeshed/stdlib/bz2.pyi
+++ b/mypy/typeshed/stdlib/bz2.pyi
@@ -3,7 +3,7 @@ from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompres
 from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from collections.abc import Iterable
 from io import TextIOWrapper
-from typing import IO, Literal, Protocol, SupportsIndex, overload
+from typing import IO, Literal, Protocol, SupportsIndex, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 if sys.version_info >= (3, 14):
@@ -16,8 +16,10 @@ __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "d
 # The following attributes and methods are optional:
 # def fileno(self) -> int: ...
 # def close(self) -> object: ...
+@type_check_only
 class _ReadableFileobj(_Reader, Protocol): ...
 
+@type_check_only
 class _WritableFileobj(Protocol):
     def write(self, b: bytes, /) -> object: ...
     # The following attributes and methods are optional:
diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi
index 3a2e2a91b241..a7a95a139330 100644
--- a/mypy/typeshed/stdlib/cgi.pyi
+++ b/mypy/typeshed/stdlib/cgi.pyi
@@ -3,7 +3,7 @@ from builtins import list as _list, type as _type
 from collections.abc import Iterable, Iterator, Mapping
 from email.message import Message
 from types import TracebackType
-from typing import IO, Any, Protocol
+from typing import IO, Any, Protocol, type_check_only
 from typing_extensions import Self
 
 __all__ = [
@@ -31,7 +31,7 @@ def parse(
 def parse_multipart(
     fp: IO[Any], pdict: SupportsGetItem[str, bytes], encoding: str = "utf-8", errors: str = "replace", separator: str = "&"
 ) -> dict[str, list[Any]]: ...
-
+@type_check_only
 class _Environ(Protocol):
     def __getitem__(self, k: str, /) -> str: ...
     def keys(self) -> Iterable[str]: ...
diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi
index 579d09c66a1b..15e184fc1038 100644
--- a/mypy/typeshed/stdlib/codecs.pyi
+++ b/mypy/typeshed/stdlib/codecs.pyi
@@ -3,7 +3,7 @@ from _codecs import *
 from _typeshed import ReadableBuffer
 from abc import abstractmethod
 from collections.abc import Callable, Generator, Iterable
-from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO, overload
+from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -73,16 +73,19 @@ _BufferedEncoding: TypeAlias = Literal[
     "utf-8-sig",
 ]
 
+@type_check_only
 class _WritableStream(Protocol):
     def write(self, data: bytes, /) -> object: ...
     def seek(self, offset: int, whence: int, /) -> object: ...
     def close(self) -> object: ...
 
+@type_check_only
 class _ReadableStream(Protocol):
     def read(self, size: int = ..., /) -> bytes: ...
     def seek(self, offset: int, whence: int, /) -> object: ...
     def close(self) -> object: ...
 
+@type_check_only
 class _Stream(_WritableStream, _ReadableStream, Protocol): ...
 
 # TODO: this only satisfies the most common interface, where
@@ -91,24 +94,31 @@ class _Stream(_WritableStream, _ReadableStream, Protocol): ...
 # There *are* bytes->bytes and str->str encodings in the standard library.
 # They were much more common in Python 2 than in Python 3.
 
+@type_check_only
 class _Encoder(Protocol):
     def __call__(self, input: str, errors: str = ..., /) -> tuple[bytes, int]: ...  # signature of Codec().encode
 
+@type_check_only
 class _Decoder(Protocol):
     def __call__(self, input: ReadableBuffer, errors: str = ..., /) -> tuple[str, int]: ...  # signature of Codec().decode
 
+@type_check_only
 class _StreamReader(Protocol):
     def __call__(self, stream: _ReadableStream, errors: str = ..., /) -> StreamReader: ...
 
+@type_check_only
 class _StreamWriter(Protocol):
     def __call__(self, stream: _WritableStream, errors: str = ..., /) -> StreamWriter: ...
 
+@type_check_only
 class _IncrementalEncoder(Protocol):
     def __call__(self, errors: str = ...) -> IncrementalEncoder: ...
 
+@type_check_only
 class _IncrementalDecoder(Protocol):
     def __call__(self, errors: str = ...) -> IncrementalDecoder: ...
 
+@type_check_only
 class _BufferedIncrementalDecoder(Protocol):
     def __call__(self, errors: str = ...) -> BufferedIncrementalDecoder: ...
 
diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi
index bc33d91caa1d..df9449ef4c9b 100644
--- a/mypy/typeshed/stdlib/collections/__init__.pyi
+++ b/mypy/typeshed/stdlib/collections/__init__.pyi
@@ -2,7 +2,7 @@ import sys
 from _collections_abc import dict_items, dict_keys, dict_values
 from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT
 from types import GenericAlias
-from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload
+from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only
 from typing_extensions import Self
 
 if sys.version_info >= (3, 10):
@@ -342,14 +342,17 @@ class _OrderedDictValuesView(ValuesView[_VT_co]):
 # but they are not exposed anywhere)
 # pyright doesn't have a specific error code for subclassing error!
 @final
+@type_check_only
 class _odict_keys(dict_keys[_KT_co, _VT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
     def __reversed__(self) -> Iterator[_KT_co]: ...
 
 @final
+@type_check_only
 class _odict_items(dict_items[_KT_co, _VT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
     def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ...
 
 @final
+@type_check_only
 class _odict_values(dict_values[_KT_co, _VT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
     def __reversed__(self) -> Iterator[_VT_co]: ...
 
diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi
index a599b1b23540..8972d50a4a63 100644
--- a/mypy/typeshed/stdlib/compileall.pyi
+++ b/mypy/typeshed/stdlib/compileall.pyi
@@ -1,10 +1,11 @@
 import sys
 from _typeshed import StrPath
 from py_compile import PycInvalidationMode
-from typing import Any, Protocol
+from typing import Any, Protocol, type_check_only
 
 __all__ = ["compile_dir", "compile_file", "compile_path"]
 
+@type_check_only
 class _SupportsSearch(Protocol):
     def search(self, string: str, /) -> Any: ...
 
diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
index dd1f6da80c4d..ad4d20ea5445 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi
@@ -19,7 +19,7 @@ from .thread import ThreadPoolExecutor as ThreadPoolExecutor
 if sys.version_info >= (3, 14):
     from .interpreter import InterpreterPoolExecutor as InterpreterPoolExecutor
 
-    __all__ = (
+    __all__ = [
         "FIRST_COMPLETED",
         "FIRST_EXCEPTION",
         "ALL_COMPLETED",
@@ -34,7 +34,7 @@ if sys.version_info >= (3, 14):
         "ProcessPoolExecutor",
         "ThreadPoolExecutor",
         "InterpreterPoolExecutor",
-    )
+    ]
 
 elif sys.version_info >= (3, 13):
     __all__ = (
diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
index fbf07a3fc78f..4063027f3eed 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
@@ -4,7 +4,7 @@ from _typeshed import Unused
 from collections.abc import Callable, Iterable, Iterator
 from logging import Logger
 from types import GenericAlias, TracebackType
-from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar
+from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar, type_check_only
 from typing_extensions import ParamSpec, Self
 
 FIRST_COMPLETED: Final = "FIRST_COMPLETED"
@@ -74,6 +74,7 @@ class Executor:
         self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
     ) -> bool | None: ...
 
+@type_check_only
 class _AsCompletedFuture(Protocol[_T_co]):
     # as_completed only mutates non-generic aspects of passed Futures and does not do any nominal
     # checks. Therefore, we can use a Protocol here to allow as_completed to act covariantly.
diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi
new file mode 100644
index 000000000000..3839e6bef09b
--- /dev/null
+++ b/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi
@@ -0,0 +1,68 @@
+import sys
+import threading
+import types
+from collections.abc import Callable
+from typing import Any, Literal, TypeVar
+from typing_extensions import ParamSpec, Self
+
+if sys.version_info >= (3, 13):  # needed to satisfy pyright checks for Python <3.13
+    from _interpreters import (
+        InterpreterError as InterpreterError,
+        InterpreterNotFoundError as InterpreterNotFoundError,
+        NotShareableError as NotShareableError,
+        _SharedDict,
+        _Whence,
+        is_shareable as is_shareable,
+    )
+
+    from ._queues import Queue as Queue, QueueEmpty as QueueEmpty, QueueFull as QueueFull, create as create_queue
+
+    __all__ = [
+        "ExecutionFailed",
+        "Interpreter",
+        "InterpreterError",
+        "InterpreterNotFoundError",
+        "NotShareableError",
+        "Queue",
+        "QueueEmpty",
+        "QueueFull",
+        "create",
+        "create_queue",
+        "get_current",
+        "get_main",
+        "is_shareable",
+        "list_all",
+    ]
+
+    _R = TypeVar("_R")
+    _P = ParamSpec("_P")
+
+    class ExecutionFailed(InterpreterError):
+        excinfo: types.SimpleNamespace
+
+        def __init__(self, excinfo: types.SimpleNamespace) -> None: ...
+
+    def create() -> Interpreter: ...
+    def list_all() -> list[Interpreter]: ...
+    def get_current() -> Interpreter: ...
+    def get_main() -> Interpreter: ...
+
+    class Interpreter:
+        def __new__(cls, id: int, /, _whence: _Whence | None = None, _ownsref: bool | None = None) -> Self: ...
+        def __reduce__(self) -> tuple[type[Self], int]: ...
+        def __hash__(self) -> int: ...
+        def __del__(self) -> None: ...
+        @property
+        def id(self) -> int: ...
+        @property
+        def whence(
+            self,
+        ) -> Literal["unknown", "runtime init", "legacy C-API", "C-API", "cross-interpreter C-API", "_interpreters module"]: ...
+        def is_running(self) -> bool: ...
+        def close(self) -> None: ...
+        def prepare_main(
+            self, ns: _SharedDict | None = None, /, **kwargs: Any
+        ) -> None: ...  # kwargs has same value restrictions as _SharedDict
+        def exec(self, code: str | types.CodeType | Callable[[], object], /) -> None: ...
+        def call(self, callable: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...
+        def call_in_thread(self, callable: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> threading.Thread: ...
diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
new file mode 100644
index 000000000000..b073aefa7ca7
--- /dev/null
+++ b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
@@ -0,0 +1,29 @@
+import sys
+from collections.abc import Callable
+from typing import Final, NewType
+from typing_extensions import Never, Self, TypeAlias
+
+if sys.version_info >= (3, 13):  # needed to satisfy pyright checks for Python <3.13
+    from _interpqueues import _UnboundOp
+
+    class ItemInterpreterDestroyed(Exception): ...
+    # Actually a descriptor that behaves similarly to classmethod but prevents
+    # access from instances.
+    classonly = classmethod
+
+    class UnboundItem:
+        def __new__(cls) -> Never: ...
+        @classonly
+        def singleton(cls, kind: str, module: str, name: str = "UNBOUND") -> Self: ...
+
+    # Sentinel types and alias that don't exist at runtime.
+    _UnboundErrorType = NewType("_UnboundErrorType", object)
+    _UnboundRemoveType = NewType("_UnboundRemoveType", object)
+    _AnyUnbound: TypeAlias = _UnboundErrorType | _UnboundRemoveType | UnboundItem
+
+    UNBOUND_ERROR: Final[_UnboundErrorType]
+    UNBOUND_REMOVE: Final[_UnboundRemoveType]
+    UNBOUND: Final[UnboundItem]  # analogous to UNBOUND_REPLACE in C
+
+    def serialize_unbound(unbound: _AnyUnbound) -> tuple[_UnboundOp]: ...
+    def resolve_unbound(flag: _UnboundOp, exctype_destroyed: Callable[[str], BaseException]) -> UnboundItem: ...
diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi
new file mode 100644
index 000000000000..39a057ee9a7b
--- /dev/null
+++ b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi
@@ -0,0 +1,58 @@
+import queue
+import sys
+from typing import Final, SupportsIndex
+from typing_extensions import Self
+
+if sys.version_info >= (3, 13):  # needed to satisfy pyright checks for Python <3.13
+    from _interpqueues import QueueError as QueueError, QueueNotFoundError as QueueNotFoundError
+
+    from . import _crossinterp
+    from ._crossinterp import UNBOUND_ERROR as UNBOUND_ERROR, UNBOUND_REMOVE as UNBOUND_REMOVE, UnboundItem, _AnyUnbound
+
+    __all__ = [
+        "UNBOUND",
+        "UNBOUND_ERROR",
+        "UNBOUND_REMOVE",
+        "ItemInterpreterDestroyed",
+        "Queue",
+        "QueueEmpty",
+        "QueueError",
+        "QueueFull",
+        "QueueNotFoundError",
+        "create",
+        "list_all",
+    ]
+
+    class QueueEmpty(QueueError, queue.Empty): ...
+    class QueueFull(QueueError, queue.Full): ...
+    class ItemInterpreterDestroyed(QueueError, _crossinterp.ItemInterpreterDestroyed): ...
+    UNBOUND: Final[UnboundItem]
+
+    def create(maxsize: int = 0, *, unbounditems: _AnyUnbound = ...) -> Queue: ...
+    def list_all() -> list[Queue]: ...
+
+    class Queue:
+        def __new__(cls, id: int, /) -> Self: ...
+        def __del__(self) -> None: ...
+        def __hash__(self) -> int: ...
+        def __reduce__(self) -> tuple[type[Self], int]: ...
+        @property
+        def id(self) -> int: ...
+        @property
+        def unbounditems(self) -> _AnyUnbound: ...
+        @property
+        def maxsize(self) -> int: ...
+        def empty(self) -> bool: ...
+        def full(self) -> bool: ...
+        def qsize(self) -> int: ...
+        def put(
+            self,
+            obj: object,
+            timeout: SupportsIndex | None = None,
+            *,
+            unbounditems: _AnyUnbound | None = None,
+            _delay: float = ...,
+        ) -> None: ...
+        def put_nowait(self, obj: object, *, unbounditems: _AnyUnbound | None = None) -> None: ...
+        def get(self, timeout: SupportsIndex | None = None, *, _delay: float = ...) -> object: ...
+        def get_nowait(self) -> object: ...
diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi
index 15c564c02589..fb02701e3711 100644
--- a/mypy/typeshed/stdlib/configparser.pyi
+++ b/mypy/typeshed/stdlib/configparser.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite
 from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence
 from re import Pattern
-from typing import Any, ClassVar, Final, Literal, TypeVar, overload
+from typing import Any, ClassVar, Final, Literal, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias
 
 if sys.version_info >= (3, 14):
@@ -104,7 +104,9 @@ else:
     ]
 
 if sys.version_info >= (3, 13):
+    @type_check_only
     class _UNNAMED_SECTION: ...
+
     UNNAMED_SECTION: _UNNAMED_SECTION
 
     _SectionName: TypeAlias = str | _UNNAMED_SECTION
@@ -369,17 +371,17 @@ class SectionProxy(MutableMapping[str, str]):
     # These are partially-applied version of the methods with the same names in
     # RawConfigParser; the stubs should be kept updated together
     @overload
-    def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int | None: ...
+    def getint(self, option: str, *, raw: bool = False, vars: _Section | None = None) -> int | None: ...
     @overload
-    def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ...
+    def getint(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> int | _T: ...
     @overload
-    def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float | None: ...
+    def getfloat(self, option: str, *, raw: bool = False, vars: _Section | None = None) -> float | None: ...
     @overload
-    def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ...
+    def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> float | _T: ...
     @overload
-    def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool | None: ...
+    def getboolean(self, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool | None: ...
     @overload
-    def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ...
+    def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> bool | _T: ...
     # SectionProxy can have arbitrary attributes when custom converters are used
     def __getattr__(self, key: str) -> Callable[..., Any]: ...
 
diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi
index 4663b448c79c..c616c1f5bf19 100644
--- a/mypy/typeshed/stdlib/contextlib.pyi
+++ b/mypy/typeshed/stdlib/contextlib.pyi
@@ -4,7 +4,7 @@ from _typeshed import FileDescriptorOrPath, Unused
 from abc import ABC, abstractmethod
 from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator
 from types import TracebackType
-from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable
+from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias
 
 __all__ = [
@@ -112,7 +112,7 @@ else:
         ) -> bool | None: ...
 
 def asynccontextmanager(func: Callable[_P, AsyncIterator[_T_co]]) -> Callable[_P, _AsyncGeneratorContextManager[_T_co]]: ...
-
+@type_check_only
 class _SupportsClose(Protocol):
     def close(self) -> object: ...
 
@@ -123,6 +123,7 @@ class closing(AbstractContextManager[_SupportsCloseT, None]):
     def __exit__(self, *exc_info: Unused) -> None: ...
 
 if sys.version_info >= (3, 10):
+    @type_check_only
     class _SupportsAclose(Protocol):
         def aclose(self) -> Awaitable[object]: ...
 
diff --git a/mypy/typeshed/stdlib/copy.pyi b/mypy/typeshed/stdlib/copy.pyi
index 2cceec6a2250..10d2f0ae3710 100644
--- a/mypy/typeshed/stdlib/copy.pyi
+++ b/mypy/typeshed/stdlib/copy.pyi
@@ -1,5 +1,5 @@
 import sys
-from typing import Any, Protocol, TypeVar
+from typing import Any, Protocol, TypeVar, type_check_only
 from typing_extensions import Self
 
 __all__ = ["Error", "copy", "deepcopy"]
@@ -7,6 +7,7 @@ __all__ = ["Error", "copy", "deepcopy"]
 _T = TypeVar("_T")
 _SR = TypeVar("_SR", bound=_SupportsReplace)
 
+@type_check_only
 class _SupportsReplace(Protocol):
     # In reality doesn't support args, but there's no other great way to express this.
     def __replace__(self, *args: Any, **kwargs: Any) -> Self: ...
diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi
index 52288d011e98..15649da9ff73 100644
--- a/mypy/typeshed/stdlib/ctypes/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi
@@ -11,6 +11,7 @@ from _ctypes import (
     _CData as _CData,
     _CDataType as _CDataType,
     _CField as _CField,
+    _CTypeBaseType,
     _Pointer as _Pointer,
     _PointerLike as _PointerLike,
     _SimpleCData as _SimpleCData,
@@ -162,7 +163,7 @@ c_buffer = create_string_buffer
 
 def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ...
 @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-def SetPointerType(pointer: type[_Pointer[Any]], cls: Any) -> None: ...
+def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ...
 def ARRAY(typ: _CT, len: int) -> Array[_CT]: ...  # Soft Deprecated, no plans to remove
 
 if sys.platform == "win32":
diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi
index c76b0b0e61e2..b3183f57ebd2 100644
--- a/mypy/typeshed/stdlib/dataclasses.pyi
+++ b/mypy/typeshed/stdlib/dataclasses.pyi
@@ -165,6 +165,7 @@ else:
     ) -> Callable[[type[_T]], type[_T]]: ...
 
 # See https://github.com/python/mypy/issues/10750
+@type_check_only
 class _DefaultFactory(Protocol[_T_co]):
     def __call__(self) -> _T_co: ...
 
diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi
index 37d6a06dfff9..c54de6159b51 100644
--- a/mypy/typeshed/stdlib/datetime.pyi
+++ b/mypy/typeshed/stdlib/datetime.pyi
@@ -118,13 +118,13 @@ class time:
     resolution: ClassVar[timedelta]
     def __new__(
         cls,
-        hour: SupportsIndex = ...,
-        minute: SupportsIndex = ...,
-        second: SupportsIndex = ...,
-        microsecond: SupportsIndex = ...,
-        tzinfo: _TzInfo | None = ...,
+        hour: SupportsIndex = 0,
+        minute: SupportsIndex = 0,
+        second: SupportsIndex = 0,
+        microsecond: SupportsIndex = 0,
+        tzinfo: _TzInfo | None = None,
         *,
-        fold: int = ...,
+        fold: int = 0,
     ) -> Self: ...
     @property
     def hour(self) -> int: ...
@@ -144,7 +144,7 @@ class time:
     def __gt__(self, value: time, /) -> bool: ...
     def __eq__(self, value: object, /) -> bool: ...
     def __hash__(self) -> int: ...
-    def isoformat(self, timespec: str = ...) -> str: ...
+    def isoformat(self, timespec: str = "auto") -> str: ...
     @classmethod
     def fromisoformat(cls, time_string: str, /) -> Self: ...
 
@@ -197,13 +197,13 @@ class timedelta:
     resolution: ClassVar[timedelta]
     def __new__(
         cls,
-        days: float = ...,
-        seconds: float = ...,
-        microseconds: float = ...,
-        milliseconds: float = ...,
-        minutes: float = ...,
-        hours: float = ...,
-        weeks: float = ...,
+        days: float = 0,
+        seconds: float = 0,
+        microseconds: float = 0,
+        milliseconds: float = 0,
+        minutes: float = 0,
+        hours: float = 0,
+        weeks: float = 0,
     ) -> Self: ...
     @property
     def days(self) -> int: ...
@@ -247,13 +247,13 @@ class datetime(date):
         year: SupportsIndex,
         month: SupportsIndex,
         day: SupportsIndex,
-        hour: SupportsIndex = ...,
-        minute: SupportsIndex = ...,
-        second: SupportsIndex = ...,
-        microsecond: SupportsIndex = ...,
-        tzinfo: _TzInfo | None = ...,
+        hour: SupportsIndex = 0,
+        minute: SupportsIndex = 0,
+        second: SupportsIndex = 0,
+        microsecond: SupportsIndex = 0,
+        tzinfo: _TzInfo | None = None,
         *,
-        fold: int = ...,
+        fold: int = 0,
     ) -> Self: ...
     @property
     def hour(self) -> int: ...
@@ -272,10 +272,10 @@ class datetime(date):
     # meaning it is only *safe* to pass it as a keyword argument on 3.12+
     if sys.version_info >= (3, 12):
         @classmethod
-        def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = ...) -> Self: ...
+        def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = None) -> Self: ...
     else:
         @classmethod
-        def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ...
+        def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = None) -> Self: ...
 
     @classmethod
     @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.timezone.utc)")
@@ -321,8 +321,8 @@ class datetime(date):
         *,
         fold: int = ...,
     ) -> Self: ...
-    def astimezone(self, tz: _TzInfo | None = ...) -> Self: ...
-    def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ...
+    def astimezone(self, tz: _TzInfo | None = None) -> Self: ...
+    def isoformat(self, sep: str = "T", timespec: str = "auto") -> str: ...
     @classmethod
     def strptime(cls, date_string: str, format: str, /) -> Self: ...
     def utcoffset(self) -> timedelta | None: ...
diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi
index 7f344060f9ab..7cbb63cf2f06 100644
--- a/mypy/typeshed/stdlib/dbm/__init__.pyi
+++ b/mypy/typeshed/stdlib/dbm/__init__.pyi
@@ -76,6 +76,7 @@ _TFlags: TypeAlias = Literal[
     "nusf",
 ]
 
+@type_check_only
 class _Database(MutableMapping[_KeyType, bytes]):
     def close(self) -> None: ...
     def __getitem__(self, key: _KeyType) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi
index 18583a3acfe9..6efe68322bb6 100644
--- a/mypy/typeshed/stdlib/difflib.pyi
+++ b/mypy/typeshed/stdlib/difflib.pyi
@@ -1,3 +1,5 @@
+import re
+import sys
 from collections.abc import Callable, Iterable, Iterator, Sequence
 from types import GenericAlias
 from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload
@@ -60,7 +62,12 @@ class Differ:
     def __init__(self, linejunk: Callable[[str], bool] | None = None, charjunk: Callable[[str], bool] | None = None) -> None: ...
     def compare(self, a: Sequence[str], b: Sequence[str]) -> Iterator[str]: ...
 
-def IS_LINE_JUNK(line: str, pat: Any = ...) -> bool: ...  # pat is undocumented
+if sys.version_info >= (3, 14):
+    def IS_LINE_JUNK(line: str, pat: Callable[[str], re.Match[str] | None] | None = None) -> bool: ...
+
+else:
+    def IS_LINE_JUNK(line: str, pat: Callable[[str], re.Match[str] | None] = ...) -> bool: ...
+
 def IS_CHARACTER_JUNK(ch: str, ws: str = " \t") -> bool: ...  # ws is undocumented
 def unified_diff(
     a: Sequence[str],
diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi
index dc641c8c952b..dff9593b731f 100644
--- a/mypy/typeshed/stdlib/email/headerregistry.pyi
+++ b/mypy/typeshed/stdlib/email/headerregistry.pyi
@@ -13,7 +13,7 @@ from email._header_value_parser import (
 )
 from email.errors import MessageDefect
 from email.policy import Policy
-from typing import Any, ClassVar, Literal, Protocol
+from typing import Any, ClassVar, Literal, Protocol, type_check_only
 from typing_extensions import Self
 
 class BaseHeader(str):
@@ -137,6 +137,7 @@ class MessageIDHeader:
     @staticmethod
     def value_parser(value: str) -> MessageID: ...
 
+@type_check_only
 class _HeaderParser(Protocol):
     max_count: ClassVar[Literal[1] | None]
     @staticmethod
diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi
index e4d14992168a..794882b140e6 100644
--- a/mypy/typeshed/stdlib/email/message.pyi
+++ b/mypy/typeshed/stdlib/email/message.pyi
@@ -5,7 +5,7 @@ from email.charset import Charset
 from email.contentmanager import ContentManager
 from email.errors import MessageDefect
 from email.policy import Policy
-from typing import Any, Generic, Literal, Protocol, TypeVar, overload
+from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = ["Message", "EmailMessage"]
@@ -24,9 +24,11 @@ _EncodedPayloadType: TypeAlias = Message | bytes
 _MultipartPayloadType: TypeAlias = list[_PayloadType]
 _CharsetType: TypeAlias = Charset | str | None
 
+@type_check_only
 class _SupportsEncodeToPayload(Protocol):
     def encode(self, encoding: str, /) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ...
 
+@type_check_only
 class _SupportsDecodeToPayload(Protocol):
     def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ...
 
diff --git a/mypy/typeshed/stdlib/encodings/__init__.pyi b/mypy/typeshed/stdlib/encodings/__init__.pyi
index 12ec6792d49b..61f86d243c72 100644
--- a/mypy/typeshed/stdlib/encodings/__init__.pyi
+++ b/mypy/typeshed/stdlib/encodings/__init__.pyi
@@ -1,3 +1,4 @@
+import sys
 from codecs import CodecInfo
 
 class CodecRegistryError(LookupError, SystemError): ...
@@ -5,5 +6,8 @@ class CodecRegistryError(LookupError, SystemError): ...
 def normalize_encoding(encoding: str | bytes) -> str: ...
 def search_function(encoding: str) -> CodecInfo | None: ...
 
+if sys.version_info >= (3, 14) and sys.platform == "win32":
+    def win32_code_page_search_function(encoding: str) -> CodecInfo | None: ...
+
 # Needed for submodules
 def __getattr__(name: str): ...  # incomplete module
diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi
index 327b135459a0..eb7d2e3819fd 100644
--- a/mypy/typeshed/stdlib/enum.pyi
+++ b/mypy/typeshed/stdlib/enum.pyi
@@ -219,6 +219,11 @@ class Enum(metaclass=EnumMeta):
     if sys.version_info >= (3, 12) and sys.version_info < (3, 14):
         @classmethod
         def __signature__(cls) -> str: ...
+    if sys.version_info >= (3, 13):
+        # Value may be any type, even in special enums. Enabling Enum parsing from
+        # multiple value types
+        def _add_value_alias_(self, value: Any) -> None: ...
+        def _add_alias_(self, name: str) -> None: ...
 
 if sys.version_info >= (3, 11):
     class ReprEnum(Enum): ...
diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi
index 1d5f9cf00f36..eb942bc55177 100644
--- a/mypy/typeshed/stdlib/fileinput.pyi
+++ b/mypy/typeshed/stdlib/fileinput.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import AnyStr_co, StrOrBytesPath
 from collections.abc import Callable, Iterable, Iterator
 from types import GenericAlias, TracebackType
-from typing import IO, Any, AnyStr, Literal, Protocol, overload
+from typing import IO, Any, AnyStr, Literal, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -25,6 +25,7 @@ if sys.version_info >= (3, 11):
 else:
     _TextMode: TypeAlias = Literal["r", "rU", "U"]
 
+@type_check_only
 class _HasReadlineAndFileno(Protocol[AnyStr_co]):
     def readline(self) -> AnyStr_co: ...
     def fileno(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi
index 16259fcfadc7..e81fbaf5dad7 100644
--- a/mypy/typeshed/stdlib/fractions.pyi
+++ b/mypy/typeshed/stdlib/fractions.pyi
@@ -2,13 +2,14 @@ import sys
 from collections.abc import Callable
 from decimal import Decimal
 from numbers import Rational, Real
-from typing import Any, Literal, Protocol, SupportsIndex, overload
+from typing import Any, Literal, Protocol, SupportsIndex, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 _ComparableNum: TypeAlias = int | float | Decimal | Real
 
 __all__ = ["Fraction"]
 
+@type_check_only
 class _ConvertibleToIntegerRatio(Protocol):
     def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ...
 
diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi
index e31399fb8705..6e17ba7d35dc 100644
--- a/mypy/typeshed/stdlib/functools.pyi
+++ b/mypy/typeshed/stdlib/functools.pyi
@@ -3,7 +3,7 @@ import types
 from _typeshed import SupportsAllComparisons, SupportsItems
 from collections.abc import Callable, Hashable, Iterable, Sized
 from types import GenericAlias
-from typing import Any, Final, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload
+from typing import Any, Final, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias
 
 __all__ = [
@@ -48,6 +48,7 @@ class _CacheInfo(NamedTuple):
     maxsize: int | None
     currsize: int
 
+@type_check_only
 class _CacheParameters(TypedDict):
     maxsize: int
     typed: bool
@@ -96,6 +97,7 @@ else:
 
 WRAPPER_UPDATES: tuple[Literal["__dict__"]]
 
+@type_check_only
 class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]):
     __wrapped__: Callable[_PWrapped, _RWrapped]
     def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ...
@@ -103,6 +105,7 @@ class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]):
     __name__: str
     __qualname__: str
 
+@type_check_only
 class _Wrapper(Generic[_PWrapped, _RWrapped]):
     def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ...
 
@@ -180,6 +183,7 @@ if sys.version_info >= (3, 11):
 else:
     _RegType: TypeAlias = type[Any]
 
+@type_check_only
 class _SingleDispatchCallable(Generic[_T]):
     registry: types.MappingProxyType[Any, Callable[..., _T]]
     def dispatch(self, cls: Any) -> Callable[..., _T]: ...
diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi
index d8fd92a00e13..5ff98b052cdb 100644
--- a/mypy/typeshed/stdlib/gettext.pyi
+++ b/mypy/typeshed/stdlib/gettext.pyi
@@ -2,7 +2,7 @@ import io
 import sys
 from _typeshed import StrPath
 from collections.abc import Callable, Container, Iterable, Sequence
-from typing import Any, Final, Literal, Protocol, TypeVar, overload
+from typing import Any, Final, Literal, Protocol, TypeVar, overload, type_check_only
 
 __all__ = [
     "NullTranslations",
@@ -26,6 +26,7 @@ __all__ = [
 if sys.version_info < (3, 11):
     __all__ += ["bind_textdomain_codeset", "ldgettext", "ldngettext", "lgettext", "lngettext"]
 
+@type_check_only
 class _TranslationsReader(Protocol):
     def read(self) -> bytes: ...
     # optional:
diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi
index 34ae92b4d8ed..06f5e2880bd5 100644
--- a/mypy/typeshed/stdlib/gzip.pyi
+++ b/mypy/typeshed/stdlib/gzip.pyi
@@ -2,7 +2,7 @@ import sys
 import zlib
 from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath, WriteableBuffer
 from io import FileIO, TextIOWrapper
-from typing import Final, Literal, Protocol, overload
+from typing import Final, Literal, Protocol, overload, type_check_only
 from typing_extensions import TypeAlias
 
 if sys.version_info >= (3, 14):
@@ -25,6 +25,7 @@ FEXTRA: Final[int]  # actually Literal[4] # undocumented
 FNAME: Final[int]  # actually Literal[8] # undocumented
 FCOMMENT: Final[int]  # actually Literal[16] # undocumented
 
+@type_check_only
 class _ReadableFileobj(Protocol):
     def read(self, n: int, /) -> bytes: ...
     def seek(self, n: int, /) -> object: ...
@@ -33,6 +34,7 @@ class _ReadableFileobj(Protocol):
     # mode: str
     # def fileno() -> int: ...
 
+@type_check_only
 class _WritableFileobj(Protocol):
     def write(self, b: bytes, /) -> object: ...
     def flush(self) -> object: ...
diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi
index b32c0e992574..924136301b21 100644
--- a/mypy/typeshed/stdlib/hashlib.pyi
+++ b/mypy/typeshed/stdlib/hashlib.pyi
@@ -20,7 +20,7 @@ from _hashlib import (
 )
 from _typeshed import ReadableBuffer
 from collections.abc import Callable, Set as AbstractSet
-from typing import Protocol
+from typing import Protocol, type_check_only
 
 if sys.version_info >= (3, 11):
     __all__ = (
@@ -72,9 +72,11 @@ algorithms_guaranteed: AbstractSet[str]
 algorithms_available: AbstractSet[str]
 
 if sys.version_info >= (3, 11):
+    @type_check_only
     class _BytesIOLike(Protocol):
         def getbuffer(self) -> ReadableBuffer: ...
 
+    @type_check_only
     class _FileDigestFileObj(Protocol):
         def readinto(self, buf: bytearray, /) -> int: ...
         def readable(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/html/parser.pyi b/mypy/typeshed/stdlib/html/parser.pyi
index 5d38c9c0d800..45336f03aaa7 100644
--- a/mypy/typeshed/stdlib/html/parser.pyi
+++ b/mypy/typeshed/stdlib/html/parser.pyi
@@ -1,9 +1,15 @@
+import sys
 from _markupbase import ParserBase
 from re import Pattern
+from typing import Final
 
 __all__ = ["HTMLParser"]
 
 class HTMLParser(ParserBase):
+    CDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]]
+    if sys.version_info >= (3, 14):
+        RCDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]]
+
     def __init__(self, *, convert_charrefs: bool = True) -> None: ...
     def feed(self, data: str) -> None: ...
     def close(self) -> None: ...
@@ -17,7 +23,6 @@ class HTMLParser(ParserBase):
     def handle_comment(self, data: str) -> None: ...
     def handle_decl(self, decl: str) -> None: ...
     def handle_pi(self, data: str) -> None: ...
-    CDATA_CONTENT_ELEMENTS: tuple[str, ...]
     def check_for_whole_start_tag(self, i: int) -> int: ...  # undocumented
     def clear_cdata_mode(self) -> None: ...  # undocumented
     def goahead(self, end: bool) -> None: ...  # undocumented
@@ -26,7 +31,10 @@ class HTMLParser(ParserBase):
     def parse_html_declaration(self, i: int) -> int: ...  # undocumented
     def parse_pi(self, i: int) -> int: ...  # undocumented
     def parse_starttag(self, i: int) -> int: ...  # undocumented
-    def set_cdata_mode(self, elem: str) -> None: ...  # undocumented
+    if sys.version_info >= (3, 14):
+        def set_cdata_mode(self, elem: str, *, escapable: bool = False) -> None: ...  # undocumented
+    else:
+        def set_cdata_mode(self, elem: str) -> None: ...  # undocumented
     rawdata: str  # undocumented
     cdata_elem: str | None  # undocumented
     convert_charrefs: bool  # undocumented
diff --git a/mypy/typeshed/stdlib/imghdr.pyi b/mypy/typeshed/stdlib/imghdr.pyi
index 6e1b858b8f32..e45ca3eb5bdb 100644
--- a/mypy/typeshed/stdlib/imghdr.pyi
+++ b/mypy/typeshed/stdlib/imghdr.pyi
@@ -1,9 +1,10 @@
 from _typeshed import StrPath
 from collections.abc import Callable
-from typing import Any, BinaryIO, Protocol, overload
+from typing import Any, BinaryIO, Protocol, overload, type_check_only
 
 __all__ = ["what"]
 
+@type_check_only
 class _ReadableBinary(Protocol):
     def tell(self) -> int: ...
     def read(self, size: int, /) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/imp.pyi b/mypy/typeshed/stdlib/imp.pyi
index ee5a0cd7bc72..f045fd969b27 100644
--- a/mypy/typeshed/stdlib/imp.pyi
+++ b/mypy/typeshed/stdlib/imp.pyi
@@ -13,7 +13,7 @@ from _imp import (
 from _typeshed import StrPath
 from os import PathLike
 from types import TracebackType
-from typing import IO, Any, Protocol
+from typing import IO, Any, Protocol, type_check_only
 
 SEARCH_ERROR: int
 PY_SOURCE: int
@@ -39,6 +39,7 @@ class NullImporter:
 
 # Technically, a text file has to support a slightly different set of operations than a binary file,
 # but we ignore that here.
+@type_check_only
 class _FileLike(Protocol):
     closed: bool
     mode: str
diff --git a/mypy/typeshed/stdlib/importlib/resources/abc.pyi b/mypy/typeshed/stdlib/importlib/resources/abc.pyi
index fe0fe64dba0d..80d92a608604 100644
--- a/mypy/typeshed/stdlib/importlib/resources/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/resources/abc.pyi
@@ -10,13 +10,8 @@ if sys.version_info >= (3, 11):
         def open_resource(self, resource: str) -> IO[bytes]: ...
         @abstractmethod
         def resource_path(self, resource: str) -> str: ...
-        if sys.version_info >= (3, 10):
-            @abstractmethod
-            def is_resource(self, path: str) -> bool: ...
-        else:
-            @abstractmethod
-            def is_resource(self, name: str) -> bool: ...
-
+        @abstractmethod
+        def is_resource(self, path: str) -> bool: ...
         @abstractmethod
         def contents(self) -> Iterator[str]: ...
 
@@ -28,12 +23,8 @@ if sys.version_info >= (3, 11):
         def is_file(self) -> bool: ...
         @abstractmethod
         def iterdir(self) -> Iterator[Traversable]: ...
-        if sys.version_info >= (3, 11):
-            @abstractmethod
-            def joinpath(self, *descendants: str) -> Traversable: ...
-        else:
-            @abstractmethod
-            def joinpath(self, child: str, /) -> Traversable: ...
+        @abstractmethod
+        def joinpath(self, *descendants: str) -> Traversable: ...
 
         # The documentation and runtime protocol allows *args, **kwargs arguments,
         # but this would mean that all implementers would have to support them,
@@ -47,12 +38,7 @@ if sys.version_info >= (3, 11):
         @property
         @abstractmethod
         def name(self) -> str: ...
-        if sys.version_info >= (3, 10):
-            def __truediv__(self, child: str, /) -> Traversable: ...
-        else:
-            @abstractmethod
-            def __truediv__(self, child: str, /) -> Traversable: ...
-
+        def __truediv__(self, child: str, /) -> Traversable: ...
         @abstractmethod
         def read_bytes(self) -> bytes: ...
         @abstractmethod
diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi
index e19c2a634aa0..e73f9e75838d 100644
--- a/mypy/typeshed/stdlib/inspect.pyi
+++ b/mypy/typeshed/stdlib/inspect.pyi
@@ -25,7 +25,7 @@ from types import (
     TracebackType,
     WrapperDescriptorType,
 )
-from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload
+from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs
 
 if sys.version_info >= (3, 14):
@@ -240,10 +240,11 @@ def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ..
 def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ...
 @overload
 def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ...
-
+@type_check_only
 class _SupportsSet(Protocol[_T_contra, _V_contra]):
     def __set__(self, instance: _T_contra, value: _V_contra, /) -> None: ...
 
+@type_check_only
 class _SupportsDelete(Protocol[_T_contra]):
     def __delete__(self, instance: _T_contra, /) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi
index 9df6bab7c167..6d49eb8bd94a 100644
--- a/mypy/typeshed/stdlib/ipaddress.pyi
+++ b/mypy/typeshed/stdlib/ipaddress.pyi
@@ -137,7 +137,7 @@ class IPv4Address(_BaseV4, _BaseAddress):
         def ipv6_mapped(self) -> IPv6Address: ...
 
 class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]):
-    def __init__(self, address: object, strict: bool = ...) -> None: ...
+    def __init__(self, address: object, strict: bool = True) -> None: ...
 
 class IPv4Interface(IPv4Address):
     netmask: IPv4Address
@@ -197,7 +197,7 @@ class IPv6Address(_BaseV6, _BaseAddress):
     def __eq__(self, other: object) -> bool: ...
 
 class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]):
-    def __init__(self, address: object, strict: bool = ...) -> None: ...
+    def __init__(self, address: object, strict: bool = True) -> None: ...
     @property
     def is_site_local(self) -> bool: ...
 
diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi
index 24529bd48d6a..03c79cc3e265 100644
--- a/mypy/typeshed/stdlib/logging/__init__.pyi
+++ b/mypy/typeshed/stdlib/logging/__init__.pyi
@@ -7,7 +7,7 @@ from re import Pattern
 from string import Template
 from time import struct_time
 from types import FrameType, GenericAlias, TracebackType
-from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload
+from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, deprecated
 
 __all__ = [
@@ -67,11 +67,13 @@ _Level: TypeAlias = int | str
 _FormatStyle: TypeAlias = Literal["%", "{", "$"]
 
 if sys.version_info >= (3, 12):
+    @type_check_only
     class _SupportsFilter(Protocol):
         def filter(self, record: LogRecord, /) -> bool | LogRecord: ...
 
     _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool | LogRecord] | _SupportsFilter
 else:
+    @type_check_only
     class _SupportsFilter(Protocol):
         def filter(self, record: LogRecord, /) -> bool: ...
 
diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi
index 9636b81dc4f3..e231d1de3fb5 100644
--- a/mypy/typeshed/stdlib/logging/handlers.pyi
+++ b/mypy/typeshed/stdlib/logging/handlers.pyi
@@ -9,7 +9,7 @@ from re import Pattern
 from socket import SocketKind, socket
 from threading import Thread
 from types import TracebackType
-from typing import Any, ClassVar, Final, Protocol, TypeVar
+from typing import Any, ClassVar, Final, Protocol, TypeVar, type_check_only
 from typing_extensions import Self
 
 _T = TypeVar("_T")
@@ -225,6 +225,7 @@ class HTTPHandler(Handler):
     def mapLogRecord(self, record: LogRecord) -> dict[str, Any]: ...
     def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ...  # undocumented
 
+@type_check_only
 class _QueueLike(Protocol[_T]):
     def get(self) -> _T: ...
     def put_nowait(self, item: _T, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi
index ff605c0661fb..89bd998b4dfe 100644
--- a/mypy/typeshed/stdlib/mailbox.pyi
+++ b/mypy/typeshed/stdlib/mailbox.pyi
@@ -6,7 +6,7 @@ from abc import ABCMeta, abstractmethod
 from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
 from email._policybase import _MessageT
 from types import GenericAlias, TracebackType
-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload
+from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -31,13 +31,16 @@ __all__ = [
 
 _T = TypeVar("_T")
 
+@type_check_only
 class _SupportsReadAndReadline(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ...
 
 _MessageData: TypeAlias = email.message.Message | bytes | str | io.StringIO | _SupportsReadAndReadline
 
+@type_check_only
 class _HasIteritems(Protocol):
     def iteritems(self) -> Iterator[tuple[str, _MessageData]]: ...
 
+@type_check_only
 class _HasItems(Protocol):
     def items(self) -> Iterator[tuple[str, _MessageData]]: ...
 
diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi
index 9e77f0cd7e06..1903d488f7bb 100644
--- a/mypy/typeshed/stdlib/math.pyi
+++ b/mypy/typeshed/stdlib/math.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import SupportsMul, SupportsRMul
 from collections.abc import Iterable
-from typing import Any, Final, Literal, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload
+from typing import Any, Final, Literal, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias
 
 _T = TypeVar("_T")
@@ -26,6 +26,7 @@ def atanh(x: _SupportsFloatOrIndex, /) -> float: ...
 if sys.version_info >= (3, 11):
     def cbrt(x: _SupportsFloatOrIndex, /) -> float: ...
 
+@type_check_only
 class _SupportsCeil(Protocol[_T_co]):
     def __ceil__(self) -> _T_co: ...
 
@@ -49,7 +50,7 @@ if sys.version_info >= (3, 11):
 def expm1(x: _SupportsFloatOrIndex, /) -> float: ...
 def fabs(x: _SupportsFloatOrIndex, /) -> float: ...
 def factorial(x: SupportsIndex, /) -> int: ...
-
+@type_check_only
 class _SupportsFloor(Protocol[_T_co]):
     def __floor__(self) -> _T_co: ...
 
@@ -99,6 +100,7 @@ _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0]  # noqa: Y026
 _MultiplicableT1 = TypeVar("_MultiplicableT1", bound=SupportsMul[Any, Any])
 _MultiplicableT2 = TypeVar("_MultiplicableT2", bound=SupportsMul[Any, Any])
 
+@type_check_only
 class _SupportsProdWithNoDefaultGiven(SupportsMul[Any, Any], SupportsRMul[int, Any], Protocol): ...
 
 _SupportsProdNoDefaultT = TypeVar("_SupportsProdNoDefaultT", bound=_SupportsProdWithNoDefaultGiven)
@@ -127,6 +129,7 @@ def tan(x: _SupportsFloatOrIndex, /) -> float: ...
 def tanh(x: _SupportsFloatOrIndex, /) -> float: ...
 
 # Is different from `_typeshed.SupportsTrunc`, which is not generic
+@type_check_only
 class _SupportsTrunc(Protocol[_T_co]):
     def __trunc__(self) -> _T_co: ...
 
diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi
index c9b8358cde6c..261a2bfdfc44 100644
--- a/mypy/typeshed/stdlib/mmap.pyi
+++ b/mypy/typeshed/stdlib/mmap.pyi
@@ -1,38 +1,39 @@
+import os
 import sys
 from _typeshed import ReadableBuffer, Unused
 from collections.abc import Iterator
 from typing import Final, Literal, NoReturn, overload
 from typing_extensions import Self
 
-ACCESS_DEFAULT: int
-ACCESS_READ: int
-ACCESS_WRITE: int
-ACCESS_COPY: int
+ACCESS_DEFAULT: Final = 0
+ACCESS_READ: Final = 1
+ACCESS_WRITE: Final = 2
+ACCESS_COPY: Final = 3
 
-ALLOCATIONGRANULARITY: int
+ALLOCATIONGRANULARITY: Final[int]
 
 if sys.platform == "linux":
-    MAP_DENYWRITE: int
-    MAP_EXECUTABLE: int
+    MAP_DENYWRITE: Final[int]
+    MAP_EXECUTABLE: Final[int]
     if sys.version_info >= (3, 10):
-        MAP_POPULATE: int
+        MAP_POPULATE: Final[int]
 if sys.version_info >= (3, 11) and sys.platform != "win32" and sys.platform != "darwin":
-    MAP_STACK: int
+    MAP_STACK: Final[int]
 
 if sys.platform != "win32":
-    MAP_ANON: int
-    MAP_ANONYMOUS: int
-    MAP_PRIVATE: int
-    MAP_SHARED: int
-    PROT_EXEC: int
-    PROT_READ: int
-    PROT_WRITE: int
+    MAP_ANON: Final[int]
+    MAP_ANONYMOUS: Final[int]
+    MAP_PRIVATE: Final[int]
+    MAP_SHARED: Final[int]
+    PROT_EXEC: Final[int]
+    PROT_READ: Final[int]
+    PROT_WRITE: Final[int]
 
-PAGESIZE: int
+PAGESIZE: Final[int]
 
 class mmap:
     if sys.platform == "win32":
-        def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ...
+        def __init__(self, fileno: int, length: int, tagname: str | None = None, access: int = 0, offset: int = 0) -> None: ...
     else:
         if sys.version_info >= (3, 13):
             def __new__(
@@ -41,34 +42,38 @@ class mmap:
                 length: int,
                 flags: int = ...,
                 prot: int = ...,
-                access: int = ...,
-                offset: int = ...,
+                access: int = 0,
+                offset: int = 0,
                 *,
                 trackfd: bool = True,
             ) -> Self: ...
         else:
             def __new__(
-                cls, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ...
+                cls, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = 0, offset: int = 0
             ) -> Self: ...
 
     def close(self) -> None: ...
-    def flush(self, offset: int = ..., size: int = ...) -> None: ...
+    def flush(self, offset: int = 0, size: int = ...) -> None: ...
     def move(self, dest: int, src: int, count: int) -> None: ...
     def read_byte(self) -> int: ...
     def readline(self) -> bytes: ...
     def resize(self, newsize: int) -> None: ...
-    def seek(self, pos: int, whence: int = ...) -> None: ...
+    if sys.platform != "win32":
+        def seek(self, pos: int, whence: Literal[0, 1, 2, 3, 4] = os.SEEK_SET) -> None: ...
+    else:
+        def seek(self, pos: int, whence: Literal[0, 1, 2] = os.SEEK_SET) -> None: ...
+
     def size(self) -> int: ...
     def tell(self) -> int: ...
     def write_byte(self, byte: int) -> None: ...
     def __len__(self) -> int: ...
     closed: bool
     if sys.platform != "win32":
-        def madvise(self, option: int, start: int = ..., length: int = ...) -> None: ...
+        def madvise(self, option: int, start: int = 0, length: int = ...) -> None: ...
 
     def find(self, sub: ReadableBuffer, start: int = ..., stop: int = ...) -> int: ...
     def rfind(self, sub: ReadableBuffer, start: int = ..., stop: int = ...) -> int: ...
-    def read(self, n: int | None = ...) -> bytes: ...
+    def read(self, n: int | None = None) -> bytes: ...
     def write(self, bytes: ReadableBuffer) -> int: ...
     @overload
     def __getitem__(self, key: int, /) -> int: ...
@@ -93,42 +98,42 @@ class mmap:
         def seekable(self) -> Literal[True]: ...
 
 if sys.platform != "win32":
-    MADV_NORMAL: int
-    MADV_RANDOM: int
-    MADV_SEQUENTIAL: int
-    MADV_WILLNEED: int
-    MADV_DONTNEED: int
-    MADV_FREE: int
+    MADV_NORMAL: Final[int]
+    MADV_RANDOM: Final[int]
+    MADV_SEQUENTIAL: Final[int]
+    MADV_WILLNEED: Final[int]
+    MADV_DONTNEED: Final[int]
+    MADV_FREE: Final[int]
 
 if sys.platform == "linux":
-    MADV_REMOVE: int
-    MADV_DONTFORK: int
-    MADV_DOFORK: int
-    MADV_HWPOISON: int
-    MADV_MERGEABLE: int
-    MADV_UNMERGEABLE: int
+    MADV_REMOVE: Final[int]
+    MADV_DONTFORK: Final[int]
+    MADV_DOFORK: Final[int]
+    MADV_HWPOISON: Final[int]
+    MADV_MERGEABLE: Final[int]
+    MADV_UNMERGEABLE: Final[int]
     # Seems like this constant is not defined in glibc.
     # See https://github.com/python/typeshed/pull/5360 for details
-    # MADV_SOFT_OFFLINE: int
-    MADV_HUGEPAGE: int
-    MADV_NOHUGEPAGE: int
-    MADV_DONTDUMP: int
-    MADV_DODUMP: int
+    # MADV_SOFT_OFFLINE: Final[int]
+    MADV_HUGEPAGE: Final[int]
+    MADV_NOHUGEPAGE: Final[int]
+    MADV_DONTDUMP: Final[int]
+    MADV_DODUMP: Final[int]
 
 # This Values are defined for FreeBSD but type checkers do not support conditions for these
 if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32":
-    MADV_NOSYNC: int
-    MADV_AUTOSYNC: int
-    MADV_NOCORE: int
-    MADV_CORE: int
-    MADV_PROTECT: int
+    MADV_NOSYNC: Final[int]
+    MADV_AUTOSYNC: Final[int]
+    MADV_NOCORE: Final[int]
+    MADV_CORE: Final[int]
+    MADV_PROTECT: Final[int]
 
 if sys.version_info >= (3, 10) and sys.platform == "darwin":
-    MADV_FREE_REUSABLE: int
-    MADV_FREE_REUSE: int
+    MADV_FREE_REUSABLE: Final[int]
+    MADV_FREE_REUSE: Final[int]
 
 if sys.version_info >= (3, 13) and sys.platform != "win32":
-    MAP_32BIT: Final = 32768
+    MAP_32BIT: Final[int]
 
 if sys.version_info >= (3, 13) and sys.platform == "darwin":
     MAP_NORESERVE: Final = 64
diff --git a/mypy/typeshed/stdlib/multiprocessing/heap.pyi b/mypy/typeshed/stdlib/multiprocessing/heap.pyi
index b5e2ced5e8ee..38191a099f1e 100644
--- a/mypy/typeshed/stdlib/multiprocessing/heap.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/heap.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import Incomplete
 from collections.abc import Callable
 from mmap import mmap
-from typing import Protocol
+from typing import Protocol, type_check_only
 from typing_extensions import TypeAlias
 
 __all__ = ["BufferWrapper"]
@@ -20,6 +20,7 @@ class Arena:
 _Block: TypeAlias = tuple[Arena, int, int]
 
 if sys.platform != "win32":
+    @type_check_only
     class _SupportsDetach(Protocol):
         def detach(self) -> int: ...
 
diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi
index 5283445d8545..e2ec15f05ea2 100644
--- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi
@@ -5,7 +5,7 @@ from ctypes import _SimpleCData, c_char
 from multiprocessing.context import BaseContext
 from multiprocessing.synchronize import _LockLike
 from types import TracebackType
-from typing import Any, Generic, Literal, Protocol, TypeVar, overload
+from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only
 
 __all__ = ["RawValue", "RawArray", "Value", "Array", "copy", "synchronized"]
 
@@ -81,7 +81,7 @@ def synchronized(
 ) -> SynchronizedArray[_T]: ...
 @overload
 def synchronized(obj: _CT, lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedBase[_CT]: ...
-
+@type_check_only
 class _AcquireFunc(Protocol):
     def __call__(self, block: bool = ..., timeout: float | None = ..., /) -> bool: ...
 
diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi
index 3ed8f8af379b..0c87444d18f4 100644
--- a/mypy/typeshed/stdlib/nt.pyi
+++ b/mypy/typeshed/stdlib/nt.pyi
@@ -110,4 +110,7 @@ if sys.platform == "win32":
     if sys.version_info >= (3, 13):
         from os import fchmod as fchmod, lchmod as lchmod
 
+    if sys.version_info >= (3, 14):
+        from os import readinto as readinto
+
     environ: dict[str, str]
diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi
index 02d469ce0ee5..b24591719cff 100644
--- a/mypy/typeshed/stdlib/numbers.pyi
+++ b/mypy/typeshed/stdlib/numbers.pyi
@@ -8,7 +8,7 @@
 # nor `float` as a subtype of `numbers.Real`, etc.)
 
 from abc import ABCMeta, abstractmethod
-from typing import ClassVar, Literal, Protocol, overload
+from typing import ClassVar, Literal, Protocol, overload, type_check_only
 
 __all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
 
@@ -22,6 +22,7 @@ __all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
 # NOTE: We can't include `__complex__` here,
 # as we want `int` to be seen as a subtype of `_ComplexLike`,
 # and `int.__complex__` does not exist :(
+@type_check_only
 class _ComplexLike(Protocol):
     def __neg__(self) -> _ComplexLike: ...
     def __pos__(self) -> _ComplexLike: ...
@@ -29,6 +30,7 @@ class _ComplexLike(Protocol):
 
 # _RealLike is a structural-typing approximation
 # of the `Real` ABC, which is not (and cannot be) a protocol
+@type_check_only
 class _RealLike(_ComplexLike, Protocol):
     def __trunc__(self) -> _IntegralLike: ...
     def __floor__(self) -> _IntegralLike: ...
@@ -41,6 +43,7 @@ class _RealLike(_ComplexLike, Protocol):
 
 # _IntegralLike is a structural-typing approximation
 # of the `Integral` ABC, which is not (and cannot be) a protocol
+@type_check_only
 class _IntegralLike(_RealLike, Protocol):
     def __invert__(self) -> _IntegralLike: ...
     def __int__(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi
index 8b7fcd82e5a5..c52291799280 100644
--- a/mypy/typeshed/stdlib/optparse.pyi
+++ b/mypy/typeshed/stdlib/optparse.pyi
@@ -24,8 +24,7 @@ __all__ = [
     "BadOptionError",
     "check_choice",
 ]
-# pytype is not happy with `NO_DEFAULT: Final = ("NO", "DEFAULT")`
-NO_DEFAULT: Final[tuple[Literal["NO"], Literal["DEFAULT"]]]
+NO_DEFAULT: Final = ("NO", "DEFAULT")
 SUPPRESS_HELP: Final = "SUPPRESSHELP"
 SUPPRESS_USAGE: Final = "SUPPRESSUSAGE"
 
diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi
index dd4479f9030a..4047bb0f1c4d 100644
--- a/mypy/typeshed/stdlib/os/__init__.pyi
+++ b/mypy/typeshed/stdlib/os/__init__.pyi
@@ -39,6 +39,7 @@ from typing import (
     final,
     overload,
     runtime_checkable,
+    type_check_only,
 )
 from typing_extensions import Self, TypeAlias, Unpack, deprecated
 
@@ -597,12 +598,12 @@ if sys.platform == "darwin" and sys.version_info >= (3, 12):
     PRIO_DARWIN_PROCESS: int
     PRIO_DARWIN_THREAD: int
 
-SEEK_SET: int
-SEEK_CUR: int
-SEEK_END: int
+SEEK_SET: Final = 0
+SEEK_CUR: Final = 1
+SEEK_END: Final = 2
 if sys.platform != "win32":
-    SEEK_DATA: int
-    SEEK_HOLE: int
+    SEEK_DATA: Final = 3
+    SEEK_HOLE: Final = 4
 
 O_RDONLY: int
 O_WRONLY: int
@@ -1241,6 +1242,7 @@ def replace(
 ) -> None: ...
 def rmdir(path: StrOrBytesPath, *, dir_fd: int | None = None) -> None: ...
 @final
+@type_check_only
 class _ScandirIterator(Generic[AnyStr]):
     def __del__(self) -> None: ...
     def __iter__(self) -> Self: ...
diff --git a/mypy/typeshed/stdlib/pathlib/__init__.pyi b/mypy/typeshed/stdlib/pathlib/__init__.pyi
index b84fc69313a1..774478bb2ff4 100644
--- a/mypy/typeshed/stdlib/pathlib/__init__.pyi
+++ b/mypy/typeshed/stdlib/pathlib/__init__.pyi
@@ -67,7 +67,14 @@ class PurePath(PathLike[str]):
     def as_posix(self) -> str: ...
     def as_uri(self) -> str: ...
     def is_absolute(self) -> bool: ...
-    def is_reserved(self) -> bool: ...
+    if sys.version_info >= (3, 13):
+        @deprecated(
+            "Deprecated since Python 3.13; will be removed in Python 3.15. "
+            "Use `os.path.isreserved()` to detect reserved paths on Windows."
+        )
+        def is_reserved(self) -> bool: ...
+    else:
+        def is_reserved(self) -> bool: ...
     if sys.version_info >= (3, 14):
         def is_relative_to(self, other: StrPath) -> bool: ...
     elif sys.version_info >= (3, 12):
@@ -163,7 +170,6 @@ class Path(PurePath):
     def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ...
 
     if sys.version_info >= (3, 14):
-
         @property
         def info(self) -> PathInfo: ...
         @overload
diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi
index fbc73c6c9177..c6125bd3a56f 100644
--- a/mypy/typeshed/stdlib/platform.pyi
+++ b/mypy/typeshed/stdlib/platform.pyi
@@ -1,6 +1,6 @@
 import sys
 from typing import NamedTuple, type_check_only
-from typing_extensions import Self
+from typing_extensions import Self, deprecated
 
 def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ...
 def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ...
@@ -9,9 +9,24 @@ def win32_is_iot() -> bool: ...
 def mac_ver(
     release: str = "", versioninfo: tuple[str, str, str] = ("", "", ""), machine: str = ""
 ) -> tuple[str, tuple[str, str, str], str]: ...
-def java_ver(
-    release: str = "", vendor: str = "", vminfo: tuple[str, str, str] = ("", "", ""), osinfo: tuple[str, str, str] = ("", "", "")
-) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ...
+
+if sys.version_info >= (3, 13):
+    @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+    def java_ver(
+        release: str = "",
+        vendor: str = "",
+        vminfo: tuple[str, str, str] = ("", "", ""),
+        osinfo: tuple[str, str, str] = ("", "", ""),
+    ) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ...
+
+else:
+    def java_ver(
+        release: str = "",
+        vendor: str = "",
+        vminfo: tuple[str, str, str] = ("", "", ""),
+        osinfo: tuple[str, str, str] = ("", "", ""),
+    ) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ...
+
 def system_alias(system: str, release: str, version: str) -> tuple[str, str, str]: ...
 def architecture(executable: str = sys.executable, bits: str = "", linkage: str = "") -> tuple[str, str]: ...
 
diff --git a/mypy/typeshed/stdlib/pprint.pyi b/mypy/typeshed/stdlib/pprint.pyi
index 171878f4165d..1e80462e2565 100644
--- a/mypy/typeshed/stdlib/pprint.pyi
+++ b/mypy/typeshed/stdlib/pprint.pyi
@@ -1,4 +1,6 @@
 import sys
+from _typeshed import SupportsWrite
+from collections import deque
 from typing import IO
 
 __all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter", "pp"]
@@ -29,25 +31,25 @@ else:
 if sys.version_info >= (3, 10):
     def pp(
         object: object,
-        stream: IO[str] | None = ...,
-        indent: int = ...,
-        width: int = ...,
-        depth: int | None = ...,
+        stream: IO[str] | None = None,
+        indent: int = 1,
+        width: int = 80,
+        depth: int | None = None,
         *,
-        compact: bool = ...,
+        compact: bool = False,
         sort_dicts: bool = False,
-        underscore_numbers: bool = ...,
+        underscore_numbers: bool = False,
     ) -> None: ...
 
 else:
     def pp(
         object: object,
-        stream: IO[str] | None = ...,
-        indent: int = ...,
-        width: int = ...,
-        depth: int | None = ...,
+        stream: IO[str] | None = None,
+        indent: int = 1,
+        width: int = 80,
+        depth: int | None = None,
         *,
-        compact: bool = ...,
+        compact: bool = False,
         sort_dicts: bool = False,
     ) -> None: ...
 
@@ -110,3 +112,48 @@ class PrettyPrinter:
     def isreadable(self, object: object) -> bool: ...
     def isrecursive(self, object: object) -> bool: ...
     def format(self, object: object, context: dict[int, int], maxlevels: int, level: int) -> tuple[str, bool, bool]: ...
+    def _format(
+        self, object: object, stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int
+    ) -> None: ...
+    def _pprint_dict(
+        self,
+        object: dict[object, object],
+        stream: SupportsWrite[str],
+        indent: int,
+        allowance: int,
+        context: dict[int, int],
+        level: int,
+    ) -> None: ...
+    def _pprint_list(
+        self, object: list[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int
+    ) -> None: ...
+    def _pprint_tuple(
+        self,
+        object: tuple[object, ...],
+        stream: SupportsWrite[str],
+        indent: int,
+        allowance: int,
+        context: dict[int, int],
+        level: int,
+    ) -> None: ...
+    def _pprint_set(
+        self, object: set[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int
+    ) -> None: ...
+    def _pprint_deque(
+        self, object: deque[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int
+    ) -> None: ...
+    def _format_dict_items(
+        self,
+        items: list[tuple[object, object]],
+        stream: SupportsWrite[str],
+        indent: int,
+        allowance: int,
+        context: dict[int, int],
+        level: int,
+    ) -> None: ...
+    def _format_items(
+        self, items: list[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int
+    ) -> None: ...
+    def _repr(self, object: object, context: dict[int, int], level: int) -> str: ...
+    if sys.version_info >= (3, 10):
+        def _safe_repr(self, object: object, context: dict[int, int], maxlevels: int, level: int) -> tuple[str, bool, bool]: ...
diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi
index f14b9d1bb699..3c78f9d2de8e 100644
--- a/mypy/typeshed/stdlib/pydoc.pyi
+++ b/mypy/typeshed/stdlib/pydoc.pyi
@@ -5,7 +5,7 @@ from builtins import list as _list  # "list" conflicts with method name
 from collections.abc import Callable, Container, Mapping, MutableMapping
 from reprlib import Repr
 from types import MethodType, ModuleType, TracebackType
-from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar
+from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar, type_check_only
 from typing_extensions import TypeGuard, deprecated
 
 __all__ = ["help"]
@@ -17,6 +17,7 @@ __date__: Final[str]
 __version__: Final[str]
 __credits__: Final[str]
 
+@type_check_only
 class _Pager(Protocol):
     def __call__(self, text: str, title: str = "") -> None: ...
 
diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi
index f5d9179e079d..65e2ac1559ad 100644
--- a/mypy/typeshed/stdlib/queue.pyi
+++ b/mypy/typeshed/stdlib/queue.pyi
@@ -1,5 +1,6 @@
 import sys
 from _queue import Empty as Empty, SimpleQueue as SimpleQueue
+from _typeshed import SupportsRichComparisonT
 from threading import Condition, Lock
 from types import GenericAlias
 from typing import Any, Generic, TypeVar
@@ -47,8 +48,8 @@ class Queue(Generic[_T]):
     def task_done(self) -> None: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
-class PriorityQueue(Queue[_T]):
-    queue: list[_T]
+class PriorityQueue(Queue[SupportsRichComparisonT]):
+    queue: list[SupportsRichComparisonT]
 
 class LifoQueue(Queue[_T]):
     queue: list[_T]
diff --git a/mypy/typeshed/stdlib/quopri.pyi b/mypy/typeshed/stdlib/quopri.pyi
index b652e139bd0e..be6892fcbcd7 100644
--- a/mypy/typeshed/stdlib/quopri.pyi
+++ b/mypy/typeshed/stdlib/quopri.pyi
@@ -1,8 +1,9 @@
 from _typeshed import ReadableBuffer, SupportsNoArgReadline, SupportsRead, SupportsWrite
-from typing import Protocol
+from typing import Protocol, type_check_only
 
 __all__ = ["encode", "decode", "encodestring", "decodestring"]
 
+@type_check_only
 class _Input(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ...
 
 def encode(input: _Input, output: SupportsWrite[bytes], quotetabs: int, header: bool = False) -> None: ...
diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi
index f25a0a376704..b080626c5802 100644
--- a/mypy/typeshed/stdlib/re.pyi
+++ b/mypy/typeshed/stdlib/re.pyi
@@ -239,9 +239,7 @@ if sys.version_info < (3, 13):
     T: Final = RegexFlag.T
     TEMPLATE: Final = RegexFlag.TEMPLATE
 if sys.version_info >= (3, 11):
-    # pytype chokes on `NOFLAG: Final = RegexFlag.NOFLAG` with `LiteralValueError`
-    # mypy chokes on `NOFLAG: Final[Literal[RegexFlag.NOFLAG]]` with `Literal[...] is invalid`
-    NOFLAG = RegexFlag.NOFLAG
+    NOFLAG: Final = RegexFlag.NOFLAG
 _FlagsType: TypeAlias = int | RegexFlag
 
 # Type-wise the compile() overloads are unnecessary, they could also be modeled using
diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi
index c66d8fa128be..cc26cfc556a0 100644
--- a/mypy/typeshed/stdlib/shutil.pyi
+++ b/mypy/typeshed/stdlib/shutil.pyi
@@ -3,7 +3,7 @@ import sys
 from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, MaybeNone, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite
 from collections.abc import Callable, Iterable, Sequence
 from tarfile import _TarfileFilter
-from typing import Any, AnyStr, NamedTuple, NoReturn, Protocol, TypeVar, overload
+from typing import Any, AnyStr, NamedTuple, NoReturn, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias, deprecated
 
 __all__ = [
@@ -79,6 +79,7 @@ def copytree(
 _OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, ExcInfo], object]
 _OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object]
 
+@type_check_only
 class _RmtreeType(Protocol):
     avoids_symlink_attacks: bool
     if sys.version_info >= (3, 12):
diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi
index d50565d1c8ac..c2668bd8b32d 100644
--- a/mypy/typeshed/stdlib/signal.pyi
+++ b/mypy/typeshed/stdlib/signal.pyi
@@ -3,7 +3,7 @@ from _typeshed import structseq
 from collections.abc import Callable, Iterable
 from enum import IntEnum
 from types import FrameType
-from typing import Any, Final, Literal, final
+from typing import Any, Final, final
 from typing_extensions import Never, TypeAlias
 
 NSIG: int
@@ -61,8 +61,8 @@ class Handlers(IntEnum):
     SIG_DFL = 0
     SIG_IGN = 1
 
-SIG_DFL: Literal[Handlers.SIG_DFL]
-SIG_IGN: Literal[Handlers.SIG_IGN]
+SIG_DFL: Final = Handlers.SIG_DFL
+SIG_IGN: Final = Handlers.SIG_IGN
 
 _SIGNUM: TypeAlias = int | Signals
 _HANDLER: TypeAlias = Callable[[int, FrameType | None], Any] | int | Handlers | None
@@ -77,45 +77,45 @@ else:
     def getsignal(signalnum: _SIGNUM, /) -> _HANDLER: ...
     def signal(signalnum: _SIGNUM, handler: _HANDLER, /) -> _HANDLER: ...
 
-SIGABRT: Literal[Signals.SIGABRT]
-SIGFPE: Literal[Signals.SIGFPE]
-SIGILL: Literal[Signals.SIGILL]
-SIGINT: Literal[Signals.SIGINT]
-SIGSEGV: Literal[Signals.SIGSEGV]
-SIGTERM: Literal[Signals.SIGTERM]
+SIGABRT: Final = Signals.SIGABRT
+SIGFPE: Final = Signals.SIGFPE
+SIGILL: Final = Signals.SIGILL
+SIGINT: Final = Signals.SIGINT
+SIGSEGV: Final = Signals.SIGSEGV
+SIGTERM: Final = Signals.SIGTERM
 
 if sys.platform == "win32":
-    SIGBREAK: Literal[Signals.SIGBREAK]
-    CTRL_C_EVENT: Literal[Signals.CTRL_C_EVENT]
-    CTRL_BREAK_EVENT: Literal[Signals.CTRL_BREAK_EVENT]
+    SIGBREAK: Final = Signals.SIGBREAK
+    CTRL_C_EVENT: Final = Signals.CTRL_C_EVENT
+    CTRL_BREAK_EVENT: Final = Signals.CTRL_BREAK_EVENT
 else:
     if sys.platform != "linux":
-        SIGINFO: Literal[Signals.SIGINFO]
-        SIGEMT: Literal[Signals.SIGEMT]
-    SIGALRM: Literal[Signals.SIGALRM]
-    SIGBUS: Literal[Signals.SIGBUS]
-    SIGCHLD: Literal[Signals.SIGCHLD]
-    SIGCONT: Literal[Signals.SIGCONT]
-    SIGHUP: Literal[Signals.SIGHUP]
-    SIGIO: Literal[Signals.SIGIO]
-    SIGIOT: Literal[Signals.SIGABRT]  # alias
-    SIGKILL: Literal[Signals.SIGKILL]
-    SIGPIPE: Literal[Signals.SIGPIPE]
-    SIGPROF: Literal[Signals.SIGPROF]
-    SIGQUIT: Literal[Signals.SIGQUIT]
-    SIGSTOP: Literal[Signals.SIGSTOP]
-    SIGSYS: Literal[Signals.SIGSYS]
-    SIGTRAP: Literal[Signals.SIGTRAP]
-    SIGTSTP: Literal[Signals.SIGTSTP]
-    SIGTTIN: Literal[Signals.SIGTTIN]
-    SIGTTOU: Literal[Signals.SIGTTOU]
-    SIGURG: Literal[Signals.SIGURG]
-    SIGUSR1: Literal[Signals.SIGUSR1]
-    SIGUSR2: Literal[Signals.SIGUSR2]
-    SIGVTALRM: Literal[Signals.SIGVTALRM]
-    SIGWINCH: Literal[Signals.SIGWINCH]
-    SIGXCPU: Literal[Signals.SIGXCPU]
-    SIGXFSZ: Literal[Signals.SIGXFSZ]
+        SIGINFO: Final = Signals.SIGINFO
+        SIGEMT: Final = Signals.SIGEMT
+    SIGALRM: Final = Signals.SIGALRM
+    SIGBUS: Final = Signals.SIGBUS
+    SIGCHLD: Final = Signals.SIGCHLD
+    SIGCONT: Final = Signals.SIGCONT
+    SIGHUP: Final = Signals.SIGHUP
+    SIGIO: Final = Signals.SIGIO
+    SIGIOT: Final = Signals.SIGABRT  # alias
+    SIGKILL: Final = Signals.SIGKILL
+    SIGPIPE: Final = Signals.SIGPIPE
+    SIGPROF: Final = Signals.SIGPROF
+    SIGQUIT: Final = Signals.SIGQUIT
+    SIGSTOP: Final = Signals.SIGSTOP
+    SIGSYS: Final = Signals.SIGSYS
+    SIGTRAP: Final = Signals.SIGTRAP
+    SIGTSTP: Final = Signals.SIGTSTP
+    SIGTTIN: Final = Signals.SIGTTIN
+    SIGTTOU: Final = Signals.SIGTTOU
+    SIGURG: Final = Signals.SIGURG
+    SIGUSR1: Final = Signals.SIGUSR1
+    SIGUSR2: Final = Signals.SIGUSR2
+    SIGVTALRM: Final = Signals.SIGVTALRM
+    SIGWINCH: Final = Signals.SIGWINCH
+    SIGXCPU: Final = Signals.SIGXCPU
+    SIGXFSZ: Final = Signals.SIGXFSZ
 
     class ItimerError(OSError): ...
     ITIMER_PROF: int
@@ -127,9 +127,9 @@ else:
         SIG_UNBLOCK = 1
         SIG_SETMASK = 2
 
-    SIG_BLOCK: Literal[Sigmasks.SIG_BLOCK]
-    SIG_UNBLOCK: Literal[Sigmasks.SIG_UNBLOCK]
-    SIG_SETMASK: Literal[Sigmasks.SIG_SETMASK]
+    SIG_BLOCK: Final = Sigmasks.SIG_BLOCK
+    SIG_UNBLOCK: Final = Sigmasks.SIG_UNBLOCK
+    SIG_SETMASK: Final = Sigmasks.SIG_SETMASK
     def alarm(seconds: int, /) -> int: ...
     def getitimer(which: int, /) -> tuple[float, float]: ...
     def pause() -> None: ...
@@ -147,13 +147,13 @@ else:
     else:
         def sigwait(sigset: Iterable[int], /) -> _SIGNUM: ...
     if sys.platform != "darwin":
-        SIGCLD: Literal[Signals.SIGCHLD]  # alias
-        SIGPOLL: Literal[Signals.SIGIO]  # alias
-        SIGPWR: Literal[Signals.SIGPWR]
-        SIGRTMAX: Literal[Signals.SIGRTMAX]
-        SIGRTMIN: Literal[Signals.SIGRTMIN]
+        SIGCLD: Final = Signals.SIGCHLD  # alias
+        SIGPOLL: Final = Signals.SIGIO  # alias
+        SIGPWR: Final = Signals.SIGPWR
+        SIGRTMAX: Final = Signals.SIGRTMAX
+        SIGRTMIN: Final = Signals.SIGRTMIN
         if sys.version_info >= (3, 11):
-            SIGSTKFLT: Literal[Signals.SIGSTKFLT]
+            SIGSTKFLT: Final = Signals.SIGSTKFLT
 
         @final
         class struct_siginfo(structseq[int], tuple[int, int, int, int, int, int, int]):
@@ -181,7 +181,7 @@ else:
 def strsignal(signalnum: _SIGNUM, /) -> str | None: ...
 def valid_signals() -> set[Signals]: ...
 def raise_signal(signalnum: _SIGNUM, /) -> None: ...
-def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = ...) -> int: ...
+def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = True) -> int: ...
 
 if sys.platform == "linux":
-    def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = ..., /) -> None: ...
+    def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = 0, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi
index 609b3e6426c4..3d392c047993 100644
--- a/mypy/typeshed/stdlib/smtplib.pyi
+++ b/mypy/typeshed/stdlib/smtplib.pyi
@@ -7,7 +7,7 @@ from re import Pattern
 from socket import socket
 from ssl import SSLContext
 from types import TracebackType
-from typing import Any, Protocol, overload
+from typing import Any, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -65,7 +65,7 @@ class SMTPAuthenticationError(SMTPResponseException): ...
 
 def quoteaddr(addrstring: str) -> str: ...
 def quotedata(data: str) -> str: ...
-
+@type_check_only
 class _AuthObject(Protocol):
     @overload
     def __call__(self, challenge: None = None, /) -> str | None: ...
diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi
index b4fa4381a72c..491551dd52b1 100644
--- a/mypy/typeshed/stdlib/socket.pyi
+++ b/mypy/typeshed/stdlib/socket.pyi
@@ -136,7 +136,7 @@ from _typeshed import ReadableBuffer, Unused, WriteableBuffer
 from collections.abc import Iterable
 from enum import IntEnum, IntFlag
 from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase, TextIOWrapper
-from typing import Any, Literal, Protocol, SupportsIndex, overload
+from typing import Any, Literal, Protocol, SupportsIndex, overload, type_check_only
 from typing_extensions import Self
 
 __all__ = [
@@ -1290,6 +1290,7 @@ if sys.platform != "win32" and sys.platform != "linux":
 if sys.platform == "win32":
     errorTab: dict[int, str]  # undocumented
 
+@type_check_only
 class _SendableFile(Protocol):
     def read(self, size: int, /) -> bytes: ...
     def seek(self, offset: int, /) -> object: ...
diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
index ab783dbde121..5a659deaccf6 100644
--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
+++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
@@ -220,23 +220,29 @@ _SqliteData: TypeAlias = str | ReadableBuffer | int | float | None
 _AdaptedInputData: TypeAlias = _SqliteData | Any
 # The Mapping must really be a dict, but making it invariant is too annoying.
 _Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData]
+# Controls the legacy transaction handling mode of sqlite3.
+_IsolationLevel: TypeAlias = Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None
 
+@type_check_only
 class _AnyParamWindowAggregateClass(Protocol):
     def step(self, *args: Any) -> object: ...
     def inverse(self, *args: Any) -> object: ...
     def value(self) -> _SqliteData: ...
     def finalize(self) -> _SqliteData: ...
 
+@type_check_only
 class _WindowAggregateClass(Protocol):
     step: Callable[..., object]
     inverse: Callable[..., object]
     def value(self) -> _SqliteData: ...
     def finalize(self) -> _SqliteData: ...
 
+@type_check_only
 class _AggregateProtocol(Protocol):
     def step(self, value: int, /) -> object: ...
     def finalize(self) -> int: ...
 
+@type_check_only
 class _SingleParamWindowAggregateClass(Protocol):
     def step(self, param: Any, /) -> object: ...
     def inverse(self, param: Any, /) -> object: ...
@@ -285,7 +291,7 @@ class Connection:
     def Warning(self) -> type[Warning]: ...
     @property
     def in_transaction(self) -> bool: ...
-    isolation_level: str | None  # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE'
+    isolation_level: _IsolationLevel
     @property
     def total_changes(self) -> int: ...
     if sys.version_info >= (3, 12):
@@ -299,26 +305,26 @@ class Connection:
         def __init__(
             self,
             database: StrOrBytesPath,
-            timeout: float = ...,
-            detect_types: int = ...,
-            isolation_level: str | None = ...,
-            check_same_thread: bool = ...,
+            timeout: float = 5.0,
+            detect_types: int = 0,
+            isolation_level: _IsolationLevel = "DEFERRED",
+            check_same_thread: bool = True,
             factory: type[Connection] | None = ...,
-            cached_statements: int = ...,
-            uri: bool = ...,
+            cached_statements: int = 128,
+            uri: bool = False,
             autocommit: bool = ...,
         ) -> None: ...
     else:
         def __init__(
             self,
             database: StrOrBytesPath,
-            timeout: float = ...,
-            detect_types: int = ...,
-            isolation_level: str | None = ...,
-            check_same_thread: bool = ...,
+            timeout: float = 5.0,
+            detect_types: int = 0,
+            isolation_level: _IsolationLevel = "DEFERRED",
+            check_same_thread: bool = True,
             factory: type[Connection] | None = ...,
-            cached_statements: int = ...,
-            uri: bool = ...,
+            cached_statements: int = 128,
+            uri: bool = False,
         ) -> None: ...
 
     def close(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
index d3ea3ef0e896..d37a0d391ec6 100644
--- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
+++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
@@ -66,6 +66,7 @@ from sqlite3 import (
     Row as Row,
     Warning as Warning,
 )
+from typing import Literal
 
 if sys.version_info >= (3, 12):
     from _sqlite3 import (
@@ -223,7 +224,7 @@ if sys.version_info < (3, 10):
     from _sqlite3 import OptimizedUnicode as OptimizedUnicode
 
 paramstyle: str
-threadsafety: int
+threadsafety: Literal[0, 1, 3]
 apilevel: str
 Date = date
 Time = time
diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi
index 9fbf5e8dfa84..1b8631d3fb12 100644
--- a/mypy/typeshed/stdlib/ssl.pyi
+++ b/mypy/typeshed/stdlib/ssl.pyi
@@ -50,6 +50,7 @@ _SrvnmeCbType: TypeAlias = Callable[[SSLSocket | SSLObject, str | None, SSLSocke
 
 socket_error = OSError
 
+@type_check_only
 class _Cipher(TypedDict):
     aead: bool
     alg_bits: int
@@ -80,6 +81,7 @@ class SSLCertVerificationError(SSLError, ValueError):
 CertificateError = SSLCertVerificationError
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.7. Removed in Python 3.12. Use `SSLContext.wrap_socket()` instead.")
     def wrap_socket(
         sock: socket.socket,
         keyfile: StrOrBytesPath | None = None,
@@ -92,46 +94,7 @@ if sys.version_info < (3, 12):
         suppress_ragged_eofs: bool = True,
         ciphers: str | None = None,
     ) -> SSLSocket: ...
-
-def create_default_context(
-    purpose: Purpose = ...,
-    *,
-    cafile: StrOrBytesPath | None = None,
-    capath: StrOrBytesPath | None = None,
-    cadata: str | ReadableBuffer | None = None,
-) -> SSLContext: ...
-
-if sys.version_info >= (3, 10):
-    def _create_unverified_context(
-        protocol: int | None = None,
-        *,
-        cert_reqs: int = ...,
-        check_hostname: bool = False,
-        purpose: Purpose = ...,
-        certfile: StrOrBytesPath | None = None,
-        keyfile: StrOrBytesPath | None = None,
-        cafile: StrOrBytesPath | None = None,
-        capath: StrOrBytesPath | None = None,
-        cadata: str | ReadableBuffer | None = None,
-    ) -> SSLContext: ...
-
-else:
-    def _create_unverified_context(
-        protocol: int = ...,
-        *,
-        cert_reqs: int = ...,
-        check_hostname: bool = False,
-        purpose: Purpose = ...,
-        certfile: StrOrBytesPath | None = None,
-        keyfile: StrOrBytesPath | None = None,
-        cafile: StrOrBytesPath | None = None,
-        capath: StrOrBytesPath | None = None,
-        cadata: str | ReadableBuffer | None = None,
-    ) -> SSLContext: ...
-
-_create_default_https_context: Callable[..., SSLContext]
-
-if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.7. Removed in Python 3.12.")
     def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ...
 
 def cert_time_to_seconds(cert_time: str) -> int: ...
@@ -370,7 +333,7 @@ class SSLSocket(socket.socket):
     def get_channel_binding(self, cb_type: str = "tls-unique") -> bytes | None: ...
     def selected_alpn_protocol(self) -> str | None: ...
     if sys.version_info >= (3, 10):
-        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        @deprecated("Deprecated since Python 3.10. Use ALPN instead.")
         def selected_npn_protocol(self) -> str | None: ...
     else:
         def selected_npn_protocol(self) -> str | None: ...
@@ -416,13 +379,15 @@ class SSLContext(_SSLContext):
     if sys.version_info >= (3, 10):
         security_level: int
     if sys.version_info >= (3, 10):
-        # Using the default (None) for the `protocol` parameter is deprecated,
-        # but there isn't a good way of marking that in the stub unless/until PEP 702 is accepted
-        def __new__(cls, protocol: int | None = None, *args: Any, **kwargs: Any) -> Self: ...
+        @overload
+        def __new__(cls, protocol: int, *args: Any, **kwargs: Any) -> Self: ...
+        @overload
+        @deprecated("Deprecated since Python 3.10. Use a specific version of the SSL protocol.")
+        def __new__(cls, protocol: None = None, *args: Any, **kwargs: Any) -> Self: ...
     else:
         def __new__(cls, protocol: int = ..., *args: Any, **kwargs: Any) -> Self: ...
 
-    def load_default_certs(self, purpose: Purpose = ...) -> None: ...
+    def load_default_certs(self, purpose: Purpose = Purpose.SERVER_AUTH) -> None: ...
     def load_verify_locations(
         self,
         cafile: StrOrBytesPath | None = None,
@@ -440,7 +405,7 @@ class SSLContext(_SSLContext):
     def set_ciphers(self, cipherlist: str, /) -> None: ...
     def set_alpn_protocols(self, alpn_protocols: Iterable[str]) -> None: ...
     if sys.version_info >= (3, 10):
-        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        @deprecated("Deprecated since Python 3.10. Use ALPN instead.")
         def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ...
     else:
         def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ...
@@ -466,6 +431,44 @@ class SSLContext(_SSLContext):
         session: SSLSession | None = None,
     ) -> SSLObject: ...
 
+def create_default_context(
+    purpose: Purpose = Purpose.SERVER_AUTH,
+    *,
+    cafile: StrOrBytesPath | None = None,
+    capath: StrOrBytesPath | None = None,
+    cadata: str | ReadableBuffer | None = None,
+) -> SSLContext: ...
+
+if sys.version_info >= (3, 10):
+    def _create_unverified_context(
+        protocol: int | None = None,
+        *,
+        cert_reqs: int = ...,
+        check_hostname: bool = False,
+        purpose: Purpose = Purpose.SERVER_AUTH,
+        certfile: StrOrBytesPath | None = None,
+        keyfile: StrOrBytesPath | None = None,
+        cafile: StrOrBytesPath | None = None,
+        capath: StrOrBytesPath | None = None,
+        cadata: str | ReadableBuffer | None = None,
+    ) -> SSLContext: ...
+
+else:
+    def _create_unverified_context(
+        protocol: int = ...,
+        *,
+        cert_reqs: int = ...,
+        check_hostname: bool = False,
+        purpose: Purpose = Purpose.SERVER_AUTH,
+        certfile: StrOrBytesPath | None = None,
+        keyfile: StrOrBytesPath | None = None,
+        cafile: StrOrBytesPath | None = None,
+        capath: StrOrBytesPath | None = None,
+        cadata: str | ReadableBuffer | None = None,
+    ) -> SSLContext: ...
+
+_create_default_https_context = create_default_context
+
 class SSLObject:
     context: SSLContext
     @property
@@ -486,7 +489,7 @@ class SSLObject:
     def getpeercert(self, binary_form: bool) -> _PeerCertRetType: ...
     def selected_alpn_protocol(self) -> str | None: ...
     if sys.version_info >= (3, 10):
-        @deprecated("Deprecated in 3.10. Use ALPN instead.")
+        @deprecated("Deprecated since Python 3.10. Use ALPN instead.")
         def selected_npn_protocol(self) -> str | None: ...
     else:
         def selected_npn_protocol(self) -> str | None: ...
diff --git a/mypy/typeshed/stdlib/string/templatelib.pyi b/mypy/typeshed/stdlib/string/templatelib.pyi
index 3f460006a796..9906d31c6391 100644
--- a/mypy/typeshed/stdlib/string/templatelib.pyi
+++ b/mypy/typeshed/stdlib/string/templatelib.pyi
@@ -1,8 +1,8 @@
 from collections.abc import Iterator
 from types import GenericAlias
-from typing import Any, Literal, final
+from typing import Any, Literal, TypeVar, final, overload
 
-__all__ = ["Interpolation", "Template"]
+_T = TypeVar("_T")
 
 @final
 class Template:  # TODO: consider making `Template` generic on `TypeVarTuple`
@@ -11,7 +11,7 @@ class Template:  # TODO: consider making `Template` generic on `TypeVarTuple`
 
     def __new__(cls, *args: str | Interpolation) -> Template: ...
     def __iter__(self) -> Iterator[str | Interpolation]: ...
-    def __add__(self, other: Template | str) -> Template: ...
+    def __add__(self, other: Template, /) -> Template: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
     @property
     def values(self) -> tuple[Any, ...]: ...  # Tuple of interpolation values, which can have any type
@@ -29,3 +29,8 @@ class Interpolation:
         cls, value: Any, expression: str = "", conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
     ) -> Interpolation: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
+
+@overload
+def convert(obj: _T, /, conversion: None) -> _T: ...
+@overload
+def convert(obj: object, /, conversion: Literal["r", "s", "a"]) -> str: ...
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index 0ca30396a878..149f374d6e17 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -378,6 +378,7 @@ def settrace(function: TraceFunction | None, /) -> None: ...
 if sys.platform == "win32":
     # A tuple of length 5, even though it has more than 5 attributes.
     @final
+    @type_check_only
     class _WinVersion(_UninstantiableStructseq, tuple[int, int, int, int, str]):
         @property
         def major(self) -> int: ...
@@ -454,7 +455,14 @@ def get_asyncgen_hooks() -> _asyncgen_hooks: ...
 def set_asyncgen_hooks(firstiter: _AsyncgenHook = ..., finalizer: _AsyncgenHook = ...) -> None: ...
 
 if sys.platform == "win32":
-    def _enablelegacywindowsfsencoding() -> None: ...
+    if sys.version_info >= (3, 13):
+        @deprecated(
+            "Deprecated since Python 3.13; will be removed in Python 3.16. "
+            "Use the `PYTHONLEGACYWINDOWSFSENCODING` environment variable instead."
+        )
+        def _enablelegacywindowsfsencoding() -> None: ...
+    else:
+        def _enablelegacywindowsfsencoding() -> None: ...
 
 def get_coroutine_origin_tracking_depth() -> int: ...
 def set_coroutine_origin_tracking_depth(depth: int) -> None: ...
diff --git a/mypy/typeshed/stdlib/sys/_monitoring.pyi b/mypy/typeshed/stdlib/sys/_monitoring.pyi
index 0507eeedc26d..3a8292ea0df4 100644
--- a/mypy/typeshed/stdlib/sys/_monitoring.pyi
+++ b/mypy/typeshed/stdlib/sys/_monitoring.pyi
@@ -5,40 +5,52 @@
 # of being a `types.ModuleType` instance that cannot be directly imported,
 # and exists in the `sys`-module namespace despite `sys` not being a package.
 
+import sys
 from collections.abc import Callable
 from types import CodeType
-from typing import Any
+from typing import Any, Final, type_check_only
+from typing_extensions import deprecated
 
-DEBUGGER_ID: int
-COVERAGE_ID: int
-PROFILER_ID: int
-OPTIMIZER_ID: int
+DEBUGGER_ID: Final[int]
+COVERAGE_ID: Final[int]
+PROFILER_ID: Final[int]
+OPTIMIZER_ID: Final[int]
 
 def use_tool_id(tool_id: int, name: str, /) -> None: ...
 def free_tool_id(tool_id: int, /) -> None: ...
 def get_tool(tool_id: int, /) -> str | None: ...
 
-events: _events
+events: Final[_events]
 
+@type_check_only
 class _events:
-    BRANCH: int
-    CALL: int
-    C_RAISE: int
-    C_RETURN: int
-    EXCEPTION_HANDLED: int
-    INSTRUCTION: int
-    JUMP: int
-    LINE: int
-    NO_EVENTS: int
-    PY_RESUME: int
-    PY_RETURN: int
-    PY_START: int
-    PY_THROW: int
-    PY_UNWIND: int
-    PY_YIELD: int
-    RAISE: int
-    RERAISE: int
-    STOP_ITERATION: int
+    CALL: Final[int]
+    C_RAISE: Final[int]
+    C_RETURN: Final[int]
+    EXCEPTION_HANDLED: Final[int]
+    INSTRUCTION: Final[int]
+    JUMP: Final[int]
+    LINE: Final[int]
+    NO_EVENTS: Final[int]
+    PY_RESUME: Final[int]
+    PY_RETURN: Final[int]
+    PY_START: Final[int]
+    PY_THROW: Final[int]
+    PY_UNWIND: Final[int]
+    PY_YIELD: Final[int]
+    RAISE: Final[int]
+    RERAISE: Final[int]
+    STOP_ITERATION: Final[int]
+    if sys.version_info >= (3, 14):
+        BRANCH_LEFT: Final[int]
+        BRANCH_TAKEN: Final[int]
+
+        @property
+        @deprecated("BRANCH is deprecated; use BRANCH_LEFT or BRANCH_TAKEN instead")
+        def BRANCH(self) -> int: ...
+
+    else:
+        BRANCH: Final[int]
 
 def get_events(tool_id: int, /) -> int: ...
 def set_events(tool_id: int, event_set: int, /) -> None: ...
@@ -46,7 +58,7 @@ def get_local_events(tool_id: int, code: CodeType, /) -> int: ...
 def set_local_events(tool_id: int, code: CodeType, event_set: int, /) -> int: ...
 def restart_events() -> None: ...
 
-DISABLE: object
-MISSING: object
+DISABLE: Final[object]
+MISSING: Final[object]
 
 def register_callback(tool_id: int, event: int, func: Callable[..., Any] | None, /) -> Callable[..., Any] | None: ...
diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi
index dba250f2d353..4e394409bbe0 100644
--- a/mypy/typeshed/stdlib/tarfile.pyi
+++ b/mypy/typeshed/stdlib/tarfile.pyi
@@ -6,7 +6,7 @@ from builtins import list as _list  # aliases to avoid name clashes with fields
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj
 from types import TracebackType
-from typing import IO, ClassVar, Literal, Protocol, overload
+from typing import IO, ClassVar, Final, Literal, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias, deprecated
 
 if sys.version_info >= (3, 14):
@@ -47,6 +47,7 @@ if sys.version_info >= (3, 13):
 _FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None]
 _TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction
 
+@type_check_only
 class _Fileobj(Protocol):
     def read(self, size: int, /) -> bytes: ...
     def write(self, b: bytes, /) -> object: ...
@@ -57,58 +58,61 @@ class _Fileobj(Protocol):
     # name: str | bytes
     # mode: Literal["rb", "r+b", "wb", "xb"]
 
+@type_check_only
 class _Bz2ReadableFileobj(bz2._ReadableFileobj):
     def close(self) -> object: ...
 
+@type_check_only
 class _Bz2WritableFileobj(bz2._WritableFileobj):
     def close(self) -> object: ...
 
 # tar constants
-NUL: bytes
-BLOCKSIZE: int
-RECORDSIZE: int
-GNU_MAGIC: bytes
-POSIX_MAGIC: bytes
-
-LENGTH_NAME: int
-LENGTH_LINK: int
-LENGTH_PREFIX: int
-
-REGTYPE: bytes
-AREGTYPE: bytes
-LNKTYPE: bytes
-SYMTYPE: bytes
-CONTTYPE: bytes
-BLKTYPE: bytes
-DIRTYPE: bytes
-FIFOTYPE: bytes
-CHRTYPE: bytes
-
-GNUTYPE_LONGNAME: bytes
-GNUTYPE_LONGLINK: bytes
-GNUTYPE_SPARSE: bytes
-
-XHDTYPE: bytes
-XGLTYPE: bytes
-SOLARIS_XHDTYPE: bytes
-
-USTAR_FORMAT: int
-GNU_FORMAT: int
-PAX_FORMAT: int
-DEFAULT_FORMAT: int
+NUL: Final = b"\0"
+BLOCKSIZE: Final = 512
+RECORDSIZE: Final = 10240
+GNU_MAGIC: Final = b"ustar  \0"
+POSIX_MAGIC: Final = b"ustar\x0000"
+
+LENGTH_NAME: Final = 100
+LENGTH_LINK: Final = 100
+LENGTH_PREFIX: Final = 155
+
+REGTYPE: Final = b"0"
+AREGTYPE: Final = b"\0"
+LNKTYPE: Final = b"1"
+SYMTYPE: Final = b"2"
+CHRTYPE: Final = b"3"
+BLKTYPE: Final = b"4"
+DIRTYPE: Final = b"5"
+FIFOTYPE: Final = b"6"
+CONTTYPE: Final = b"7"
+
+GNUTYPE_LONGNAME: Final = b"L"
+GNUTYPE_LONGLINK: Final = b"K"
+GNUTYPE_SPARSE: Final = b"S"
+
+XHDTYPE: Final = b"x"
+XGLTYPE: Final = b"g"
+SOLARIS_XHDTYPE: Final = b"X"
+
+_TarFormat: TypeAlias = Literal[0, 1, 2]  # does not exist at runtime
+USTAR_FORMAT: Final = 0
+GNU_FORMAT: Final = 1
+PAX_FORMAT: Final = 2
+DEFAULT_FORMAT: Final = PAX_FORMAT
 
 # tarfile constants
 
-SUPPORTED_TYPES: tuple[bytes, ...]
-REGULAR_TYPES: tuple[bytes, ...]
-GNU_TYPES: tuple[bytes, ...]
-PAX_FIELDS: tuple[str, ...]
-PAX_NUMBER_FIELDS: dict[str, type]
-PAX_NAME_FIELDS: set[str]
+SUPPORTED_TYPES: Final[tuple[bytes, ...]]
+REGULAR_TYPES: Final[tuple[bytes, ...]]
+GNU_TYPES: Final[tuple[bytes, ...]]
+PAX_FIELDS: Final[tuple[str, ...]]
+PAX_NUMBER_FIELDS: Final[dict[str, type]]
+PAX_NAME_FIELDS: Final[set[str]]
 
-ENCODING: str
+ENCODING: Final[str]
 
-class ExFileObject(io.BufferedReader):
+class ExFileObject(io.BufferedReader):  # undocumented
     def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ...
 
 class TarFile:
@@ -116,13 +120,13 @@ class TarFile:
     name: StrOrBytesPath | None
     mode: Literal["r", "a", "w", "x"]
     fileobj: _Fileobj | None
-    format: int | None
+    format: _TarFormat | None
     tarinfo: type[TarInfo]
     dereference: bool | None
     ignore_zeros: bool | None
     encoding: str | None
     errors: str
-    fileobject: type[ExFileObject]
+    fileobject: type[ExFileObject]  # undocumented
     pax_headers: Mapping[str, str] | None
     debug: int | None
     errorlevel: int | None
@@ -642,7 +646,7 @@ class TarFile:
     def getmember(self, name: str) -> TarInfo: ...
     def getmembers(self) -> _list[TarInfo]: ...
     def getnames(self) -> _list[str]: ...
-    def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ...
+    def list(self, verbose: bool = True, *, members: Iterable[TarInfo] | None = None) -> None: ...
     def next(self) -> TarInfo | None: ...
     # Calling this method without `filter` is deprecated, but it may be set either on the class or in an
     # individual call, so we can't mark it as @deprecated here.
@@ -751,7 +755,7 @@ class TarInfo:
     offset_data: int
     sparse: bytes | None
     mode: int
-    type: bytes
+    type: bytes  # usually one of the TYPE constants, but could be an arbitrary byte
     linkname: str
     uid: int
     gid: int
@@ -791,7 +795,7 @@ class TarInfo:
         deep: bool = True,
     ) -> Self: ...
     def get_info(self) -> Mapping[str, str | int | bytes | Mapping[str, str]]: ...
-    def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ...
+    def tobuf(self, format: _TarFormat | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ...
     def create_ustar_header(
         self, info: Mapping[str, str | int | bytes | Mapping[str, str]], encoding: str, errors: str
     ) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi
index ea6e057e410d..6b2abe4398d2 100644
--- a/mypy/typeshed/stdlib/tempfile.pyi
+++ b/mypy/typeshed/stdlib/tempfile.pyi
@@ -15,7 +15,7 @@ from _typeshed import (
 from collections.abc import Iterable, Iterator
 from types import GenericAlias, TracebackType
 from typing import IO, Any, AnyStr, Generic, Literal, overload
-from typing_extensions import Self
+from typing_extensions import Self, deprecated
 
 __all__ = [
     "NamedTemporaryFile",
@@ -471,6 +471,7 @@ def mkstemp(
 def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ...
 @overload
 def mkdtemp(suffix: bytes | None = None, prefix: bytes | None = None, dir: BytesPath | None = None) -> bytes: ...
+@deprecated("Deprecated since Python 2.3. Use `mkstemp()` or `NamedTemporaryFile(delete=False)` instead.")
 def mktemp(suffix: str = "", prefix: str = "tmp", dir: StrPath | None = None) -> str: ...
 def gettempdirb() -> bytes: ...
 def gettempprefixb() -> bytes: ...
diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi
index 5a5a1f53be3c..a35be5dfe740 100644
--- a/mypy/typeshed/stdlib/termios.pyi
+++ b/mypy/typeshed/stdlib/termios.pyi
@@ -1,6 +1,6 @@
 import sys
 from _typeshed import FileDescriptorLike
-from typing import Any
+from typing import Any, Final
 from typing_extensions import TypeAlias
 
 # Must be a list of length 7, containing 6 ints and a list of NCCS 1-character bytes or ints.
@@ -9,286 +9,287 @@ _Attr: TypeAlias = list[int | list[bytes | int]] | list[int | list[bytes]] | lis
 _AttrReturn: TypeAlias = list[Any]
 
 if sys.platform != "win32":
-    B0: int
-    B110: int
-    B115200: int
-    B1200: int
-    B134: int
-    B150: int
-    B1800: int
-    B19200: int
-    B200: int
-    B230400: int
-    B2400: int
-    B300: int
-    B38400: int
-    B4800: int
-    B50: int
-    B57600: int
-    B600: int
-    B75: int
-    B9600: int
-    BRKINT: int
-    BS0: int
-    BS1: int
-    BSDLY: int
-    CDSUSP: int
-    CEOF: int
-    CEOL: int
-    CEOT: int
-    CERASE: int
-    CFLUSH: int
-    CINTR: int
-    CKILL: int
-    CLNEXT: int
-    CLOCAL: int
-    CQUIT: int
-    CR0: int
-    CR1: int
-    CR2: int
-    CR3: int
-    CRDLY: int
-    CREAD: int
-    CRPRNT: int
-    CRTSCTS: int
-    CS5: int
-    CS6: int
-    CS7: int
-    CS8: int
-    CSIZE: int
-    CSTART: int
-    CSTOP: int
-    CSTOPB: int
-    CSUSP: int
-    CWERASE: int
-    ECHO: int
-    ECHOCTL: int
-    ECHOE: int
-    ECHOK: int
-    ECHOKE: int
-    ECHONL: int
-    ECHOPRT: int
-    EXTA: int
-    EXTB: int
-    FF0: int
-    FF1: int
-    FFDLY: int
-    FIOASYNC: int
-    FIOCLEX: int
-    FIONBIO: int
-    FIONCLEX: int
-    FIONREAD: int
-    FLUSHO: int
-    HUPCL: int
-    ICANON: int
-    ICRNL: int
-    IEXTEN: int
-    IGNBRK: int
-    IGNCR: int
-    IGNPAR: int
-    IMAXBEL: int
-    INLCR: int
-    INPCK: int
-    ISIG: int
-    ISTRIP: int
-    IXANY: int
-    IXOFF: int
-    IXON: int
-    NCCS: int
-    NL0: int
-    NL1: int
-    NLDLY: int
-    NOFLSH: int
-    OCRNL: int
-    OFDEL: int
-    OFILL: int
-    ONLCR: int
-    ONLRET: int
-    ONOCR: int
-    OPOST: int
-    PARENB: int
-    PARMRK: int
-    PARODD: int
-    PENDIN: int
-    TAB0: int
-    TAB1: int
-    TAB2: int
-    TAB3: int
-    TABDLY: int
-    TCIFLUSH: int
-    TCIOFF: int
-    TCIOFLUSH: int
-    TCION: int
-    TCOFLUSH: int
-    TCOOFF: int
-    TCOON: int
-    TCSADRAIN: int
-    TCSAFLUSH: int
-    TCSANOW: int
-    TIOCCONS: int
-    TIOCEXCL: int
-    TIOCGETD: int
-    TIOCGPGRP: int
-    TIOCGWINSZ: int
-    TIOCM_CAR: int
-    TIOCM_CD: int
-    TIOCM_CTS: int
-    TIOCM_DSR: int
-    TIOCM_DTR: int
-    TIOCM_LE: int
-    TIOCM_RI: int
-    TIOCM_RNG: int
-    TIOCM_RTS: int
-    TIOCM_SR: int
-    TIOCM_ST: int
-    TIOCMBIC: int
-    TIOCMBIS: int
-    TIOCMGET: int
-    TIOCMSET: int
-    TIOCNOTTY: int
-    TIOCNXCL: int
-    TIOCOUTQ: int
-    TIOCPKT_DATA: int
-    TIOCPKT_DOSTOP: int
-    TIOCPKT_FLUSHREAD: int
-    TIOCPKT_FLUSHWRITE: int
-    TIOCPKT_NOSTOP: int
-    TIOCPKT_START: int
-    TIOCPKT_STOP: int
-    TIOCPKT: int
-    TIOCSCTTY: int
-    TIOCSETD: int
-    TIOCSPGRP: int
-    TIOCSTI: int
-    TIOCSWINSZ: int
-    TOSTOP: int
-    VDISCARD: int
-    VEOF: int
-    VEOL: int
-    VEOL2: int
-    VERASE: int
-    VINTR: int
-    VKILL: int
-    VLNEXT: int
-    VMIN: int
-    VQUIT: int
-    VREPRINT: int
-    VSTART: int
-    VSTOP: int
-    VSUSP: int
-    VT0: int
-    VT1: int
-    VTDLY: int
-    VTIME: int
-    VWERASE: int
+    # Values depends on the platform
+    B0: Final[int]
+    B110: Final[int]
+    B115200: Final[int]
+    B1200: Final[int]
+    B134: Final[int]
+    B150: Final[int]
+    B1800: Final[int]
+    B19200: Final[int]
+    B200: Final[int]
+    B230400: Final[int]
+    B2400: Final[int]
+    B300: Final[int]
+    B38400: Final[int]
+    B4800: Final[int]
+    B50: Final[int]
+    B57600: Final[int]
+    B600: Final[int]
+    B75: Final[int]
+    B9600: Final[int]
+    BRKINT: Final[int]
+    BS0: Final[int]
+    BS1: Final[int]
+    BSDLY: Final[int]
+    CDSUSP: Final[int]
+    CEOF: Final[int]
+    CEOL: Final[int]
+    CEOT: Final[int]
+    CERASE: Final[int]
+    CFLUSH: Final[int]
+    CINTR: Final[int]
+    CKILL: Final[int]
+    CLNEXT: Final[int]
+    CLOCAL: Final[int]
+    CQUIT: Final[int]
+    CR0: Final[int]
+    CR1: Final[int]
+    CR2: Final[int]
+    CR3: Final[int]
+    CRDLY: Final[int]
+    CREAD: Final[int]
+    CRPRNT: Final[int]
+    CRTSCTS: Final[int]
+    CS5: Final[int]
+    CS6: Final[int]
+    CS7: Final[int]
+    CS8: Final[int]
+    CSIZE: Final[int]
+    CSTART: Final[int]
+    CSTOP: Final[int]
+    CSTOPB: Final[int]
+    CSUSP: Final[int]
+    CWERASE: Final[int]
+    ECHO: Final[int]
+    ECHOCTL: Final[int]
+    ECHOE: Final[int]
+    ECHOK: Final[int]
+    ECHOKE: Final[int]
+    ECHONL: Final[int]
+    ECHOPRT: Final[int]
+    EXTA: Final[int]
+    EXTB: Final[int]
+    FF0: Final[int]
+    FF1: Final[int]
+    FFDLY: Final[int]
+    FIOASYNC: Final[int]
+    FIOCLEX: Final[int]
+    FIONBIO: Final[int]
+    FIONCLEX: Final[int]
+    FIONREAD: Final[int]
+    FLUSHO: Final[int]
+    HUPCL: Final[int]
+    ICANON: Final[int]
+    ICRNL: Final[int]
+    IEXTEN: Final[int]
+    IGNBRK: Final[int]
+    IGNCR: Final[int]
+    IGNPAR: Final[int]
+    IMAXBEL: Final[int]
+    INLCR: Final[int]
+    INPCK: Final[int]
+    ISIG: Final[int]
+    ISTRIP: Final[int]
+    IXANY: Final[int]
+    IXOFF: Final[int]
+    IXON: Final[int]
+    NCCS: Final[int]
+    NL0: Final[int]
+    NL1: Final[int]
+    NLDLY: Final[int]
+    NOFLSH: Final[int]
+    OCRNL: Final[int]
+    OFDEL: Final[int]
+    OFILL: Final[int]
+    ONLCR: Final[int]
+    ONLRET: Final[int]
+    ONOCR: Final[int]
+    OPOST: Final[int]
+    PARENB: Final[int]
+    PARMRK: Final[int]
+    PARODD: Final[int]
+    PENDIN: Final[int]
+    TAB0: Final[int]
+    TAB1: Final[int]
+    TAB2: Final[int]
+    TAB3: Final[int]
+    TABDLY: Final[int]
+    TCIFLUSH: Final[int]
+    TCIOFF: Final[int]
+    TCIOFLUSH: Final[int]
+    TCION: Final[int]
+    TCOFLUSH: Final[int]
+    TCOOFF: Final[int]
+    TCOON: Final[int]
+    TCSADRAIN: Final[int]
+    TCSAFLUSH: Final[int]
+    TCSANOW: Final[int]
+    TIOCCONS: Final[int]
+    TIOCEXCL: Final[int]
+    TIOCGETD: Final[int]
+    TIOCGPGRP: Final[int]
+    TIOCGWINSZ: Final[int]
+    TIOCM_CAR: Final[int]
+    TIOCM_CD: Final[int]
+    TIOCM_CTS: Final[int]
+    TIOCM_DSR: Final[int]
+    TIOCM_DTR: Final[int]
+    TIOCM_LE: Final[int]
+    TIOCM_RI: Final[int]
+    TIOCM_RNG: Final[int]
+    TIOCM_RTS: Final[int]
+    TIOCM_SR: Final[int]
+    TIOCM_ST: Final[int]
+    TIOCMBIC: Final[int]
+    TIOCMBIS: Final[int]
+    TIOCMGET: Final[int]
+    TIOCMSET: Final[int]
+    TIOCNOTTY: Final[int]
+    TIOCNXCL: Final[int]
+    TIOCOUTQ: Final[int]
+    TIOCPKT_DATA: Final[int]
+    TIOCPKT_DOSTOP: Final[int]
+    TIOCPKT_FLUSHREAD: Final[int]
+    TIOCPKT_FLUSHWRITE: Final[int]
+    TIOCPKT_NOSTOP: Final[int]
+    TIOCPKT_START: Final[int]
+    TIOCPKT_STOP: Final[int]
+    TIOCPKT: Final[int]
+    TIOCSCTTY: Final[int]
+    TIOCSETD: Final[int]
+    TIOCSPGRP: Final[int]
+    TIOCSTI: Final[int]
+    TIOCSWINSZ: Final[int]
+    TOSTOP: Final[int]
+    VDISCARD: Final[int]
+    VEOF: Final[int]
+    VEOL: Final[int]
+    VEOL2: Final[int]
+    VERASE: Final[int]
+    VINTR: Final[int]
+    VKILL: Final[int]
+    VLNEXT: Final[int]
+    VMIN: Final[int]
+    VQUIT: Final[int]
+    VREPRINT: Final[int]
+    VSTART: Final[int]
+    VSTOP: Final[int]
+    VSUSP: Final[int]
+    VT0: Final[int]
+    VT1: Final[int]
+    VTDLY: Final[int]
+    VTIME: Final[int]
+    VWERASE: Final[int]
 
     if sys.version_info >= (3, 13):
-        EXTPROC: int
-        IUTF8: int
+        EXTPROC: Final[int]
+        IUTF8: Final[int]
 
     if sys.platform == "darwin" and sys.version_info >= (3, 13):
-        ALTWERASE: int
-        B14400: int
-        B28800: int
-        B7200: int
-        B76800: int
-        CCAR_OFLOW: int
-        CCTS_OFLOW: int
-        CDSR_OFLOW: int
-        CDTR_IFLOW: int
-        CIGNORE: int
-        CRTS_IFLOW: int
-        MDMBUF: int
-        NL2: int
-        NL3: int
-        NOKERNINFO: int
-        ONOEOT: int
-        OXTABS: int
-        VDSUSP: int
-        VSTATUS: int
+        ALTWERASE: Final[int]
+        B14400: Final[int]
+        B28800: Final[int]
+        B7200: Final[int]
+        B76800: Final[int]
+        CCAR_OFLOW: Final[int]
+        CCTS_OFLOW: Final[int]
+        CDSR_OFLOW: Final[int]
+        CDTR_IFLOW: Final[int]
+        CIGNORE: Final[int]
+        CRTS_IFLOW: Final[int]
+        MDMBUF: Final[int]
+        NL2: Final[int]
+        NL3: Final[int]
+        NOKERNINFO: Final[int]
+        ONOEOT: Final[int]
+        OXTABS: Final[int]
+        VDSUSP: Final[int]
+        VSTATUS: Final[int]
 
     if sys.platform == "darwin" and sys.version_info >= (3, 11):
-        TIOCGSIZE: int
-        TIOCSSIZE: int
+        TIOCGSIZE: Final[int]
+        TIOCSSIZE: Final[int]
 
     if sys.platform == "linux":
-        B1152000: int
-        B576000: int
-        CBAUD: int
-        CBAUDEX: int
-        CIBAUD: int
-        IOCSIZE_MASK: int
-        IOCSIZE_SHIFT: int
-        IUCLC: int
-        N_MOUSE: int
-        N_PPP: int
-        N_SLIP: int
-        N_STRIP: int
-        N_TTY: int
-        NCC: int
-        OLCUC: int
-        TCFLSH: int
-        TCGETA: int
-        TCGETS: int
-        TCSBRK: int
-        TCSBRKP: int
-        TCSETA: int
-        TCSETAF: int
-        TCSETAW: int
-        TCSETS: int
-        TCSETSF: int
-        TCSETSW: int
-        TCXONC: int
-        TIOCGICOUNT: int
-        TIOCGLCKTRMIOS: int
-        TIOCGSERIAL: int
-        TIOCGSOFTCAR: int
-        TIOCINQ: int
-        TIOCLINUX: int
-        TIOCMIWAIT: int
-        TIOCTTYGSTRUCT: int
-        TIOCSER_TEMT: int
-        TIOCSERCONFIG: int
-        TIOCSERGETLSR: int
-        TIOCSERGETMULTI: int
-        TIOCSERGSTRUCT: int
-        TIOCSERGWILD: int
-        TIOCSERSETMULTI: int
-        TIOCSERSWILD: int
-        TIOCSLCKTRMIOS: int
-        TIOCSSERIAL: int
-        TIOCSSOFTCAR: int
-        VSWTC: int
-        VSWTCH: int
-        XCASE: int
-        XTABS: int
+        B1152000: Final[int]
+        B576000: Final[int]
+        CBAUD: Final[int]
+        CBAUDEX: Final[int]
+        CIBAUD: Final[int]
+        IOCSIZE_MASK: Final[int]
+        IOCSIZE_SHIFT: Final[int]
+        IUCLC: Final[int]
+        N_MOUSE: Final[int]
+        N_PPP: Final[int]
+        N_SLIP: Final[int]
+        N_STRIP: Final[int]
+        N_TTY: Final[int]
+        NCC: Final[int]
+        OLCUC: Final[int]
+        TCFLSH: Final[int]
+        TCGETA: Final[int]
+        TCGETS: Final[int]
+        TCSBRK: Final[int]
+        TCSBRKP: Final[int]
+        TCSETA: Final[int]
+        TCSETAF: Final[int]
+        TCSETAW: Final[int]
+        TCSETS: Final[int]
+        TCSETSF: Final[int]
+        TCSETSW: Final[int]
+        TCXONC: Final[int]
+        TIOCGICOUNT: Final[int]
+        TIOCGLCKTRMIOS: Final[int]
+        TIOCGSERIAL: Final[int]
+        TIOCGSOFTCAR: Final[int]
+        TIOCINQ: Final[int]
+        TIOCLINUX: Final[int]
+        TIOCMIWAIT: Final[int]
+        TIOCTTYGSTRUCT: Final[int]
+        TIOCSER_TEMT: Final[int]
+        TIOCSERCONFIG: Final[int]
+        TIOCSERGETLSR: Final[int]
+        TIOCSERGETMULTI: Final[int]
+        TIOCSERGSTRUCT: Final[int]
+        TIOCSERGWILD: Final[int]
+        TIOCSERSETMULTI: Final[int]
+        TIOCSERSWILD: Final[int]
+        TIOCSLCKTRMIOS: Final[int]
+        TIOCSSERIAL: Final[int]
+        TIOCSSOFTCAR: Final[int]
+        VSWTC: Final[int]
+        VSWTCH: Final[int]
+        XCASE: Final[int]
+        XTABS: Final[int]
 
     if sys.platform != "darwin":
-        B1000000: int
-        B1500000: int
-        B2000000: int
-        B2500000: int
-        B3000000: int
-        B3500000: int
-        B4000000: int
-        B460800: int
-        B500000: int
-        B921600: int
+        B1000000: Final[int]
+        B1500000: Final[int]
+        B2000000: Final[int]
+        B2500000: Final[int]
+        B3000000: Final[int]
+        B3500000: Final[int]
+        B4000000: Final[int]
+        B460800: Final[int]
+        B500000: Final[int]
+        B921600: Final[int]
 
     if sys.platform != "linux":
-        TCSASOFT: int
+        TCSASOFT: Final[int]
 
     if sys.platform != "darwin" and sys.platform != "linux":
         # not available on FreeBSD either.
-        CDEL: int
-        CEOL2: int
-        CESC: int
-        CNUL: int
-        COMMON: int
-        CSWTCH: int
-        IBSHIFT: int
-        INIT_C_CC: int
-        NSWTCH: int
+        CDEL: Final[int]
+        CEOL2: Final[int]
+        CESC: Final[int]
+        CNUL: Final[int]
+        COMMON: Final[int]
+        CSWTCH: Final[int]
+        IBSHIFT: Final[int]
+        INIT_C_CC: Final[int]
+        NSWTCH: Final[int]
 
     def tcgetattr(fd: FileDescriptorLike, /) -> _AttrReturn: ...
     def tcsetattr(fd: FileDescriptorLike, when: int, attributes: _Attr, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi
index d31351754d05..033cad3931f5 100644
--- a/mypy/typeshed/stdlib/threading.pyi
+++ b/mypy/typeshed/stdlib/threading.pyi
@@ -142,7 +142,7 @@ class Condition:
     def __exit__(
         self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
     ) -> None: ...
-    def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
+    def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ...
     def release(self) -> None: ...
     def wait(self, timeout: float | None = None) -> bool: ...
     def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ...
diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi
index 6d2538ea7e3e..a921722b62c5 100644
--- a/mypy/typeshed/stdlib/time.pyi
+++ b/mypy/typeshed/stdlib/time.pyi
@@ -1,6 +1,6 @@
 import sys
 from _typeshed import structseq
-from typing import Any, Final, Literal, Protocol, final
+from typing import Any, Final, Literal, Protocol, final, type_check_only
 from typing_extensions import TypeAlias
 
 _TimeTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int]
@@ -80,6 +80,7 @@ def time() -> float: ...
 if sys.platform != "win32":
     def tzset() -> None: ...  # Unix only
 
+@type_check_only
 class _ClockInfo(Protocol):
     adjustable: bool
     implementation: str
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index db0e34d737a6..b802d5e97c84 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -366,12 +366,14 @@ def getboolean(s): ...
 
 _Ts = TypeVarTuple("_Ts")
 
+@type_check_only
 class _GridIndexInfo(TypedDict, total=False):
     minsize: _ScreenUnits
     pad: _ScreenUnits
     uniform: str | None
     weight: int
 
+@type_check_only
 class _BusyInfo(TypedDict):
     cursor: _Cursor
 
@@ -1039,6 +1041,7 @@ def Tcl(screenName: str | None = None, baseName: str | None = None, className: s
 _InMiscTotal = TypedDict("_InMiscTotal", {"in": Misc})
 _InMiscNonTotal = TypedDict("_InMiscNonTotal", {"in": Misc}, total=False)
 
+@type_check_only
 class _PackInfo(_InMiscTotal):
     # 'before' and 'after' never appear in _PackInfo
     anchor: _Anchor
@@ -1080,6 +1083,7 @@ class Pack:
     forget = pack_forget
     propagate = Misc.pack_propagate
 
+@type_check_only
 class _PlaceInfo(_InMiscNonTotal):  # empty dict if widget hasn't been placed
     anchor: _Anchor
     bordermode: Literal["inside", "outside", "ignore"]
@@ -1116,6 +1120,7 @@ class Place:
     place = place_configure
     info = place_info
 
+@type_check_only
 class _GridInfo(_InMiscNonTotal):  # empty dict if widget hasn't been gridded
     column: int
     columnspan: int
diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi
index fe2961701c61..521f451a9b2c 100644
--- a/mypy/typeshed/stdlib/tkinter/dnd.pyi
+++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi
@@ -1,8 +1,9 @@
 from tkinter import Event, Misc, Tk, Widget
-from typing import ClassVar, Protocol
+from typing import ClassVar, Protocol, type_check_only
 
 __all__ = ["dnd_start", "DndHandler"]
 
+@type_check_only
 class _DndSource(Protocol):
     def dnd_end(self, target: Widget | None, event: Event[Misc] | None, /) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi
index cab97490be34..327ba7a2432e 100644
--- a/mypy/typeshed/stdlib/tkinter/font.pyi
+++ b/mypy/typeshed/stdlib/tkinter/font.pyi
@@ -2,7 +2,7 @@ import _tkinter
 import itertools
 import sys
 import tkinter
-from typing import Any, ClassVar, Final, Literal, TypedDict, overload
+from typing import Any, ClassVar, Final, Literal, TypedDict, overload, type_check_only
 from typing_extensions import TypeAlias, Unpack
 
 __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"]
@@ -23,6 +23,7 @@ _FontDescription: TypeAlias = (
     | _tkinter.Tcl_Obj  # A font object constructed in Tcl
 )
 
+@type_check_only
 class _FontDict(TypedDict):
     family: str
     size: int
@@ -31,6 +32,7 @@ class _FontDict(TypedDict):
     underline: bool
     overstrike: bool
 
+@type_check_only
 class _MetricsDict(TypedDict):
     ascent: int
     descent: int
diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi
index 50b9cd8f9bcd..c46239df81eb 100644
--- a/mypy/typeshed/stdlib/tkinter/ttk.pyi
+++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi
@@ -3,7 +3,7 @@ import tkinter
 from _typeshed import Incomplete, MaybeNone
 from collections.abc import Callable
 from tkinter.font import _FontDescription
-from typing import Any, Literal, TypedDict, overload
+from typing import Any, Literal, TypedDict, overload, type_check_only
 from typing_extensions import TypeAlias
 
 __all__ = [
@@ -928,6 +928,7 @@ class Spinbox(Entry):
     config = configure  # type: ignore[assignment]
     def set(self, value: Any) -> None: ...
 
+@type_check_only
 class _TreeviewItemDict(TypedDict):
     text: str
     image: list[str] | Literal[""]  # no idea why it's wrapped in list
@@ -935,6 +936,7 @@ class _TreeviewItemDict(TypedDict):
     open: bool  # actually 0 or 1
     tags: list[str] | Literal[""]
 
+@type_check_only
 class _TreeviewTagDict(TypedDict):
     # There is also 'text' and 'anchor', but they don't seem to do anything, using them is likely a bug
     foreground: str
@@ -942,6 +944,7 @@ class _TreeviewTagDict(TypedDict):
     font: _FontDescription
     image: str  # not wrapped in list :D
 
+@type_check_only
 class _TreeviewHeaderDict(TypedDict):
     text: str
     image: list[str] | Literal[""]
@@ -949,6 +952,7 @@ class _TreeviewHeaderDict(TypedDict):
     command: str
     state: str  # Doesn't seem to appear anywhere else than in these dicts
 
+@type_check_only
 class _TreeviewColumnDict(TypedDict):
     width: int
     minwidth: int
diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi
index 0611879cf1b2..ca3f0013b20e 100644
--- a/mypy/typeshed/stdlib/tty.pyi
+++ b/mypy/typeshed/stdlib/tty.pyi
@@ -15,13 +15,13 @@ if sys.platform != "win32":
     _FD: TypeAlias = int | IO[str]
 
     # XXX: Undocumented integer constants
-    IFLAG: Final[int]
-    OFLAG: Final[int]
-    CFLAG: Final[int]
-    LFLAG: Final[int]
-    ISPEED: Final[int]
-    OSPEED: Final[int]
-    CC: Final[int]
+    IFLAG: Final = 0
+    OFLAG: Final = 1
+    CFLAG: Final = 2
+    LFLAG: Final = 3
+    ISPEED: Final = 4
+    OSPEED: Final = 5
+    CC: Final = 6
     def setraw(fd: _FD, when: int = 2) -> _ModeSetterReturn: ...
     def setcbreak(fd: _FD, when: int = 2) -> _ModeSetterReturn: ...
 
diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi
index 9c62c64e718a..7d39026b8041 100644
--- a/mypy/typeshed/stdlib/turtle.pyi
+++ b/mypy/typeshed/stdlib/turtle.pyi
@@ -3,7 +3,7 @@ from _typeshed import StrPath
 from collections.abc import Callable, Generator, Sequence
 from contextlib import contextmanager
 from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar
-from typing import Any, ClassVar, Literal, TypedDict, overload
+from typing import Any, ClassVar, Literal, TypedDict, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -146,6 +146,7 @@ if sys.version_info < (3, 13):
 _Color: TypeAlias = str | tuple[float, float, float]
 _AnyColor: TypeAlias = Any
 
+@type_check_only
 class _PenState(TypedDict):
     shown: bool
     pendown: bool
@@ -487,19 +488,8 @@ Pen = Turtle
 
 def write_docstringdict(filename: str = "turtle_docstringdict") -> None: ...
 
-# Note: it's somewhat unfortunate that we have to copy the function signatures.
-# It would be nice if we could partially reduce the redundancy by doing something
-# like the following:
-#
-#     _screen: Screen
-#     clear = _screen.clear
-#
-# However, it seems pytype does not support this type of syntax in pyi files.
-
 # Functions copied from TurtleScreenBase:
 
-# Note: mainloop() was always present in the global scope, but was added to
-# TurtleScreenBase in Python 3.0
 def mainloop() -> None: ...
 def textinput(title: str, prompt: str) -> str | None: ...
 def numinput(
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index d296c8d92149..a85aa2e2dc83 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -419,6 +419,7 @@ def type_check_only(func_or_cls: _FT) -> _FT: ...
 
 # Type aliases and type constructors
 
+@type_check_only
 class _Alias:
     # Class for defining generic aliases for library types.
     def __getitem__(self, typeargs: Any) -> Any: ...
@@ -1125,6 +1126,7 @@ if sys.version_info >= (3, 13):
     def is_protocol(tp: type, /) -> bool: ...
     def get_protocol_members(tp: type, /) -> frozenset[str]: ...
     @final
+    @type_check_only
     class _NoDefaultType: ...
 
     NoDefault: _NoDefaultType
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index 3f7c25712081..22b6ada8ffb7 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -59,6 +59,7 @@ from typing import (  # noqa: Y022,Y037,Y038,Y039,UP035
     TypeVar as _TypeVar,
     Union as Union,
     _Alias,
+    _SpecialForm,
     cast as cast,
     no_type_check as no_type_check,
     no_type_check_decorator as no_type_check_decorator,
@@ -204,15 +205,6 @@ _TC = _TypeVar("_TC", bound=type[object])
 _T_co = _TypeVar("_T_co", covariant=True)  # Any type covariant containers.
 _T_contra = _TypeVar("_T_contra", contravariant=True)
 
-class _Final: ...  # This should be imported from typing but that breaks pytype
-
-# unfortunately we have to duplicate this class definition from typing.pyi or we break pytype
-class _SpecialForm(_Final):
-    def __getitem__(self, parameters: Any) -> object: ...
-    if sys.version_info >= (3, 10):
-        def __or__(self, other: Any) -> _SpecialForm: ...
-        def __ror__(self, other: Any) -> _SpecialForm: ...
-
 # Do not import (and re-export) Protocol or runtime_checkable from
 # typing module because type checkers need to be able to distinguish
 # typing.Protocol and typing_extensions.Protocol so they can properly
@@ -480,6 +472,7 @@ else:
     def is_protocol(tp: type, /) -> bool: ...
     def get_protocol_members(tp: type, /) -> frozenset[str]: ...
     @final
+    @type_check_only
     class _NoDefaultType: ...
 
     NoDefault: _NoDefaultType
@@ -611,6 +604,7 @@ class Doc:
     def __eq__(self, other: object) -> bool: ...
 
 # PEP 728
+@type_check_only
 class _NoExtraItemsType: ...
 
 NoExtraItems: _NoExtraItemsType
diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi
index 89bcabf104c2..a602196e73c6 100644
--- a/mypy/typeshed/stdlib/unittest/case.pyi
+++ b/mypy/typeshed/stdlib/unittest/case.pyi
@@ -2,18 +2,16 @@ import logging
 import sys
 import unittest.result
 from _typeshed import SupportsDunderGE, SupportsDunderGT, SupportsDunderLE, SupportsDunderLT, SupportsRSub, SupportsSub
+from builtins import _ClassInfo
 from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Set as AbstractSet
 from contextlib import AbstractContextManager
 from re import Pattern
 from types import GenericAlias, TracebackType
-from typing import Any, AnyStr, Final, Generic, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload
-from typing_extensions import Never, ParamSpec, Self, TypeAlias
+from typing import Any, AnyStr, Final, Generic, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload, type_check_only
+from typing_extensions import Never, ParamSpec, Self
 from unittest._log import _AssertLogsContext, _LoggingWatcher
 from warnings import WarningMessage
 
-if sys.version_info >= (3, 10):
-    from types import UnionType
-
 _T = TypeVar("_T")
 _S = TypeVar("_S", bound=SupportsSub[Any, Any])
 _E = TypeVar("_E", bound=BaseException)
@@ -58,16 +56,9 @@ def skipUnless(condition: object, reason: str) -> Callable[[_FT], _FT]: ...
 class SkipTest(Exception):
     def __init__(self, reason: str) -> None: ...
 
+@type_check_only
 class _SupportsAbsAndDunderGE(SupportsDunderGE[Any], SupportsAbs[Any], Protocol): ...
 
-# Keep this alias in sync with builtins._ClassInfo
-# We can't import it from builtins or pytype crashes,
-# due to the fact that pytype uses a custom builtins stub rather than typeshed's builtins stub
-if sys.version_info >= (3, 10):
-    _ClassInfo: TypeAlias = type | UnionType | tuple[_ClassInfo, ...]
-else:
-    _ClassInfo: TypeAlias = type | tuple[_ClassInfo, ...]
-
 class TestCase:
     failureException: type[BaseException]
     longMessage: bool
diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi
index 22f2ec10634d..152e9c33209c 100644
--- a/mypy/typeshed/stdlib/unittest/main.pyi
+++ b/mypy/typeshed/stdlib/unittest/main.pyi
@@ -5,12 +5,13 @@ import unittest.result
 import unittest.suite
 from collections.abc import Iterable
 from types import ModuleType
-from typing import Any, Final, Protocol
+from typing import Any, Final, Protocol, type_check_only
 from typing_extensions import deprecated
 
 MAIN_EXAMPLES: Final[str]
 MODULE_EXAMPLES: Final[str]
 
+@type_check_only
 class _TestRunner(Protocol):
     def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase, /) -> unittest.result.TestResult: ...
 
diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi
index 9e353900f2d7..6b0941a91719 100644
--- a/mypy/typeshed/stdlib/unittest/mock.pyi
+++ b/mypy/typeshed/stdlib/unittest/mock.pyi
@@ -3,7 +3,7 @@ from _typeshed import MaybeNone
 from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, Sequence
 from contextlib import _GeneratorContextManager
 from types import TracebackType
-from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload
+from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias
 
 _T = TypeVar("_T")
@@ -262,7 +262,8 @@ class _patch(Generic[_T]):
 # This class does not exist at runtime, it's a hack to make this work:
 #     @patch("foo")
 #     def bar(..., mock: MagicMock) -> None: ...
-class _patch_default_new(_patch[MagicMock | AsyncMock]):
+@type_check_only
+class _patch_pass_arg(_patch[_T]):
     @overload
     def __call__(self, func: _TT) -> _TT: ...
     # Can't use the following as ParamSpec is only allowed as last parameter:
@@ -288,6 +289,7 @@ class _patch_dict:
 
 # This class does not exist at runtime, it's a hack to add methods to the
 # patch() function.
+@type_check_only
 class _patcher:
     TEST_PREFIX: str
     dict: type[_patch_dict]
@@ -303,7 +305,7 @@ class _patcher:
         create: bool = ...,
         spec_set: Any | None = ...,
         autospec: Any | None = ...,
-        new_callable: Any | None = ...,
+        new_callable: Callable[..., Any] | None = ...,
         **kwargs: Any,
     ) -> _patch[_T]: ...
     @overload
@@ -315,9 +317,21 @@ class _patcher:
         create: bool = ...,
         spec_set: Any | None = ...,
         autospec: Any | None = ...,
-        new_callable: Any | None = ...,
+        new_callable: Callable[..., _T],
         **kwargs: Any,
-    ) -> _patch_default_new: ...
+    ) -> _patch_pass_arg[_T]: ...
+    @overload
+    def __call__(
+        self,
+        target: str,
+        *,
+        spec: Any | None = ...,
+        create: bool = ...,
+        spec_set: Any | None = ...,
+        autospec: Any | None = ...,
+        new_callable: None = ...,
+        **kwargs: Any,
+    ) -> _patch_pass_arg[MagicMock | AsyncMock]: ...
     @overload
     @staticmethod
     def object(
@@ -328,7 +342,7 @@ class _patcher:
         create: bool = ...,
         spec_set: Any | None = ...,
         autospec: Any | None = ...,
-        new_callable: Any | None = ...,
+        new_callable: Callable[..., Any] | None = ...,
         **kwargs: Any,
     ) -> _patch[_T]: ...
     @overload
@@ -341,9 +355,22 @@ class _patcher:
         create: bool = ...,
         spec_set: Any | None = ...,
         autospec: Any | None = ...,
-        new_callable: Any | None = ...,
+        new_callable: Callable[..., _T],
+        **kwargs: Any,
+    ) -> _patch_pass_arg[_T]: ...
+    @overload
+    @staticmethod
+    def object(
+        target: Any,
+        attribute: str,
+        *,
+        spec: Any | None = ...,
+        create: bool = ...,
+        spec_set: Any | None = ...,
+        autospec: Any | None = ...,
+        new_callable: None = ...,
         **kwargs: Any,
-    ) -> _patch[MagicMock | AsyncMock]: ...
+    ) -> _patch_pass_arg[MagicMock | AsyncMock]: ...
     @staticmethod
     def multiple(
         target: Any,
diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi
index 783764464a53..f76771f55e13 100644
--- a/mypy/typeshed/stdlib/unittest/runner.pyi
+++ b/mypy/typeshed/stdlib/unittest/runner.pyi
@@ -4,15 +4,17 @@ import unittest.result
 import unittest.suite
 from _typeshed import SupportsFlush, SupportsWrite
 from collections.abc import Callable, Iterable
-from typing import Any, Generic, Protocol, TypeVar
+from typing import Any, Generic, Protocol, TypeVar, type_check_only
 from typing_extensions import Never, TypeAlias
 from warnings import _ActionKind
 
 _ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult[Any]]
 
+@type_check_only
 class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ...
 
 # All methods used by unittest.runner.TextTestResult's stream
+@type_check_only
 class _TextTestStream(_SupportsWriteAndFlush, Protocol):
     def writeln(self, arg: str | None = None, /) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi
index d8fc5e0d8f48..b99577c1cf71 100644
--- a/mypy/typeshed/stdlib/urllib/request.pyi
+++ b/mypy/typeshed/stdlib/urllib/request.pyi
@@ -6,7 +6,7 @@ from email.message import Message
 from http.client import HTTPConnection, HTTPMessage, HTTPResponse
 from http.cookiejar import CookieJar
 from re import Pattern
-from typing import IO, Any, ClassVar, NoReturn, Protocol, TypeVar, overload
+from typing import IO, Any, ClassVar, NoReturn, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias, deprecated
 from urllib.error import HTTPError as HTTPError
 from urllib.response import addclosehook, addinfourl
@@ -237,6 +237,7 @@ class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
     auth_header: ClassVar[str]  # undocumented
     def http_error_407(self, req: Request, fp: IO[bytes], code: int, msg: str, headers: HTTPMessage) -> _UrlopenRet | None: ...
 
+@type_check_only
 class _HTTPConnectionProtocol(Protocol):
     def __call__(
         self,
diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi
index 99ac6eb223ef..0aa2f76d40cc 100644
--- a/mypy/typeshed/stdlib/uuid.pyi
+++ b/mypy/typeshed/stdlib/uuid.pyi
@@ -21,7 +21,7 @@ class UUID:
         int: builtins.int | None = None,
         version: builtins.int | None = None,
         *,
-        is_safe: SafeUUID = ...,
+        is_safe: SafeUUID = SafeUUID.unknown,
     ) -> None: ...
     @property
     def is_safe(self) -> SafeUUID: ...
diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
index ab2ef87e38a8..b9da9f3558ff 100644
--- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
@@ -3,7 +3,7 @@ from _collections_abc import dict_keys, dict_values
 from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite
 from collections.abc import Iterable, Sequence
 from types import TracebackType
-from typing import Any, ClassVar, Generic, Literal, NoReturn, Protocol, TypeVar, overload
+from typing import Any, ClassVar, Generic, Literal, NoReturn, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 from xml.dom.minicompat import EmptyNodeList, NodeList
 from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS
@@ -40,9 +40,11 @@ _ImportableNodeVar = TypeVar(
     | Notation,
 )
 
+@type_check_only
 class _DOMErrorHandler(Protocol):
     def handleError(self, error: Exception) -> bool: ...
 
+@type_check_only
 class _UserDataHandler(Protocol):
     def handle(self, operation: int, key: str, data: Any, src: Node, dst: Node) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
index 8f20ee15a14e..fd829fdaa5ff 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
@@ -1,7 +1,8 @@
 from _typeshed import FileDescriptorOrPath
-from typing import Final, Literal, Protocol, overload
+from typing import Final, Literal, Protocol, overload, type_check_only
 from xml.etree.ElementTree import Element
 
+@type_check_only
 class _Loader(Protocol):
     @overload
     def __call__(self, href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ...
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
index 4c55a1a7452e..1d7e1725dd8e 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
@@ -335,6 +335,7 @@ class C14NWriterTarget:
 # The target type is tricky, because the implementation doesn't
 # require any particular attribute to be present. This documents the attributes
 # that can be present, but uncommenting any of them would require them.
+@type_check_only
 class _Target(Protocol):
     # start: Callable[str, dict[str, str], Any] | None
     # end: Callable[[str], Any] | None
diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
index ebe92d28c74d..5a82b48c1e19 100644
--- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co
 from collections.abc import Iterable
-from typing import Protocol
+from typing import Protocol, type_check_only
 from typing_extensions import TypeAlias
 from xml.sax._exceptions import (
     SAXException as SAXException,
@@ -13,6 +13,7 @@ from xml.sax._exceptions import (
 from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler
 from xml.sax.xmlreader import InputSource as InputSource, XMLReader
 
+@type_check_only
 class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]):
     def close(self) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi
index 6cc4361f4a09..42420ee85848 100644
--- a/mypy/typeshed/stdlib/xmlrpc/client.pyi
+++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi
@@ -6,9 +6,10 @@ from collections.abc import Callable, Iterable, Mapping
 from datetime import datetime
 from io import BytesIO
 from types import TracebackType
-from typing import Any, ClassVar, Final, Literal, Protocol, overload
+from typing import Any, ClassVar, Final, Literal, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
+@type_check_only
 class _SupportsTimeTuple(Protocol):
     def timetuple(self) -> time.struct_time: ...
 
diff --git a/mypy/typeshed/stdlib/xmlrpc/server.pyi b/mypy/typeshed/stdlib/xmlrpc/server.pyi
index 5f497aa7190e..286aaf980fbf 100644
--- a/mypy/typeshed/stdlib/xmlrpc/server.pyi
+++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi
@@ -4,28 +4,34 @@ import socketserver
 from _typeshed import ReadableBuffer
 from collections.abc import Callable, Iterable, Mapping
 from re import Pattern
-from typing import Any, ClassVar, Protocol
+from typing import Any, ClassVar, Protocol, type_check_only
 from typing_extensions import TypeAlias
 from xmlrpc.client import Fault, _Marshallable
 
 # The dispatch accepts anywhere from 0 to N arguments, no easy way to allow this in mypy
+@type_check_only
 class _DispatchArity0(Protocol):
     def __call__(self) -> _Marshallable: ...
 
+@type_check_only
 class _DispatchArity1(Protocol):
     def __call__(self, arg1: _Marshallable, /) -> _Marshallable: ...
 
+@type_check_only
 class _DispatchArity2(Protocol):
     def __call__(self, arg1: _Marshallable, arg2: _Marshallable, /) -> _Marshallable: ...
 
+@type_check_only
 class _DispatchArity3(Protocol):
     def __call__(self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, /) -> _Marshallable: ...
 
+@type_check_only
 class _DispatchArity4(Protocol):
     def __call__(
         self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, arg4: _Marshallable, /
     ) -> _Marshallable: ...
 
+@type_check_only
 class _DispatchArityN(Protocol):
     def __call__(self, *args: _Marshallable) -> _Marshallable: ...
 
diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi
index 27c1ef0246c7..73e3a92fd0e2 100644
--- a/mypy/typeshed/stdlib/zipfile/__init__.pyi
+++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi
@@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable, Iterator
 from io import TextIOWrapper
 from os import PathLike
 from types import TracebackType
-from typing import IO, Final, Literal, Protocol, overload
+from typing import IO, Final, Literal, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -41,6 +41,7 @@ error = BadZipfile
 
 class LargeZipFile(Exception): ...
 
+@type_check_only
 class _ZipStream(Protocol):
     def read(self, n: int, /) -> bytes: ...
     # The following methods are optional:
@@ -49,11 +50,13 @@ class _ZipStream(Protocol):
     # def seek(self, n: int, /) -> object: ...
 
 # Stream shape as required by _EndRecData() and _EndRecData64().
+@type_check_only
 class _SupportsReadSeekTell(Protocol):
     def read(self, n: int = ..., /) -> bytes: ...
     def seek(self, cookie: int, whence: int, /) -> object: ...
     def tell(self) -> int: ...
 
+@type_check_only
 class _ClosableZipStream(_ZipStream, Protocol):
     def close(self) -> object: ...
 
@@ -93,18 +96,23 @@ class ZipExtFile(io.BufferedIOBase):
     def read1(self, n: int | None) -> bytes: ...  # type: ignore[override]
     def seek(self, offset: int, whence: int = 0) -> int: ...
 
+@type_check_only
 class _Writer(Protocol):
     def write(self, s: str, /) -> object: ...
 
+@type_check_only
 class _ZipReadable(Protocol):
     def seek(self, offset: int, whence: int = 0, /) -> int: ...
     def read(self, n: int = -1, /) -> bytes: ...
 
+@type_check_only
 class _ZipTellable(Protocol):
     def tell(self) -> int: ...
 
+@type_check_only
 class _ZipReadableTellable(_ZipReadable, _ZipTellable, Protocol): ...
 
+@type_check_only
 class _ZipWritable(Protocol):
     def flush(self) -> None: ...
     def close(self) -> None: ...
@@ -254,9 +262,6 @@ class ZipFile:
     ) -> None: ...
     if sys.version_info >= (3, 11):
         def mkdir(self, zinfo_or_directory_name: str | ZipInfo, mode: int = 0o777) -> None: ...
-    if sys.version_info >= (3, 14):
-        @property
-        def data_offset(self) -> int | None: ...
 
     def __del__(self) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/zoneinfo/_common.pyi b/mypy/typeshed/stdlib/zoneinfo/_common.pyi
index a2f29f2d14f0..e6d2d83caac1 100644
--- a/mypy/typeshed/stdlib/zoneinfo/_common.pyi
+++ b/mypy/typeshed/stdlib/zoneinfo/_common.pyi
@@ -1,6 +1,7 @@
 import io
-from typing import Any, Protocol
+from typing import Any, Protocol, type_check_only
 
+@type_check_only
 class _IOBytes(Protocol):
     def read(self, size: int, /) -> bytes: ...
     def seek(self, size: int, whence: int = ..., /) -> Any: ...

From 8f48f1baf5dcdd0c08f2a3de2235ad587a677ef5 Mon Sep 17 00:00:00 2001
From: Emma Smith 
Date: Mon, 4 Aug 2025 08:34:53 -0700
Subject: [PATCH 1522/1617] Fix `--package-root` tests for Windows and Python
 3.13+ (#19583)

This PR should fix the rest of the test failures in
https://github.com/python/mypy/pull/19545.

A change to `os.path.relpath` in 3.13 seems to have broken the handling
of Windows paths beginning with `\\`. To resolve this issue, we don't
split the drive letter off of the path and instead verify the path is on
the current drive. If it isn't it will never resolve to the package root
because that must be on the same drive as the CWD:

https://github.com/python/mypy/blob/5b03024e829940cf3c3e3d99fc6625f569d02728/mypy/main.py#L1571-L1572

Keeping the drive letter allows relpath to properly generate a relative
path and make the tests pass.

I need to investigate if the relpath change is a regression in CPython.
---
 mypy/fscache.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/mypy/fscache.py b/mypy/fscache.py
index 8251f4bd9488..240370159fff 100644
--- a/mypy/fscache.py
+++ b/mypy/fscache.py
@@ -117,7 +117,12 @@ def init_under_package_root(self, path: str) -> bool:
             if not stat.S_ISDIR(st.st_mode):
                 return False
         ok = False
-        drive, path = os.path.splitdrive(path)  # Ignore Windows drive name
+
+        # skip if on a different drive
+        current_drive, _ = os.path.splitdrive(os.getcwd())
+        drive, _ = os.path.splitdrive(path)
+        if drive != current_drive:
+            return False
         if os.path.isabs(path):
             path = os.path.relpath(path)
         path = os.path.normpath(path)

From 64dff4273d7b7140d66fe947b598005be3fd0a9c Mon Sep 17 00:00:00 2001
From: Emma Smith 
Date: Mon, 4 Aug 2025 08:40:29 -0700
Subject: [PATCH 1523/1617] [mypyc] Fix async mypyc tests on Windows (#19578)

This is part of fixing the failed tests in
https://github.com/python/mypy/pull/19545

The async tests previously used many invocations of `asyncio.run`, which
likely caused issues with event loop management. The documentation for
`asyncio.run` states:
> This function cannot be called when another asyncio event loop is
running in the same thread. ...
> This function should be used as a main entry point for asyncio
programs, and should ideally only be called once.

Calling `asyncio.run` multiple times could cause the test processes to
hang for strange event loop reasons. This commit converts most test
cases to be run in a single event loop managed by the default driver,
which is now async aware.

Not all tests could be converted, e.g. the test that runs an async
function in a sync context. However, the test suite does succeed with
these changes, and these tests can be further modified if needed.
---
 mypyc/test-data/driver/driver.py |   9 +-
 mypyc/test-data/run-async.test   | 394 ++++++++++++++-----------------
 2 files changed, 185 insertions(+), 218 deletions(-)

diff --git a/mypyc/test-data/driver/driver.py b/mypyc/test-data/driver/driver.py
index 1ec1c48dfb75..395be6e1630e 100644
--- a/mypyc/test-data/driver/driver.py
+++ b/mypyc/test-data/driver/driver.py
@@ -9,6 +9,10 @@
 
 import sys
 import native
+import asyncio
+import inspect
+
+evloop = asyncio.new_event_loop()
 
 failures = []
 tests_run = 0
@@ -18,7 +22,10 @@
         test_func = getattr(native, name)
         tests_run += 1
         try:
-            test_func()
+            if inspect.iscoroutinefunction(test_func):
+                evloop.run_until_complete(test_func)
+            else:
+                test_func()
         except Exception as e:
             failures.append((name, sys.exc_info()))
 
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index f1ec7e8f85e0..a1112e964671 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -22,12 +22,12 @@ async def f2() -> int:
         x += i + await f() + await g()
     return x
 
-def test_simple_call() -> None:
-    result = asyncio.run(f())
+async def test_simple_call() -> None:
+    result = await f()
     assert result == 3
 
-def test_multiple_awaits_in_expression() -> None:
-    result = asyncio.run(f2())
+async def test_multiple_awaits_in_expression() -> None:
+    result = await f2()
     assert result == 9
 
 class MyError(Exception):
@@ -61,17 +61,17 @@ async def exc6() -> int:
         return 3
     return 4
 
-def test_exception() -> None:
+async def test_exception() -> None:
     with assertRaises(MyError):
-        asyncio.run(exc1())
+        await exc1()
     with assertRaises(MyError):
-        asyncio.run(exc2())
+        await exc2()
     with assertRaises(MyError):
-        asyncio.run(exc3())
+        await exc3()
     with assertRaises(MyError):
-        asyncio.run(exc4())
-    assert asyncio.run(exc5()) == 3
-    assert asyncio.run(exc6()) == 3
+        await exc4()
+    assert await exc5() == 3
+    assert await exc6() == 3
 
 async def indirect_call(x: int, c: Callable[[int], Awaitable[int]]) -> int:
     return await c(x)
@@ -92,20 +92,20 @@ async def ident(x: float, err: bool = False) -> float:
         raise MyError()
     return x + float("0.0")
 
-def test_indirect_call() -> None:
-    assert asyncio.run(indirect_call(3, inc)) == 4
+async def test_indirect_call() -> None:
+    assert await indirect_call(3, inc) == 4
 
     with assertRaises(MyError):
-        asyncio.run(indirect_call_2(exc1()))
+        await indirect_call_2(exc1())
 
-    assert asyncio.run(indirect_call_3(ident(2.0))) == 3.0
-    assert asyncio.run(indirect_call_3(ident(-113.0))) == -112.0
-    assert asyncio.run(indirect_call_3(ident(-114.0))) == -113.0
+    assert await indirect_call_3(ident(2.0)) == 3.0
+    assert await indirect_call_3(ident(-113.0)) == -112.0
+    assert await indirect_call_3(ident(-114.0)) == -113.0
 
     with assertRaises(MyError):
-        asyncio.run(indirect_call_3(ident(1.0, True)))
+        await indirect_call_3(ident(1.0, True))
     with assertRaises(MyError):
-        asyncio.run(indirect_call_3(ident(-113.0, True)))
+        await indirect_call_3(ident(-113.0, True))
 
 class C:
     def __init__(self, n: int) -> None:
@@ -125,15 +125,13 @@ async def method_call_exception() -> int:
     c = C(5)
     return await c.add(3, err=True)
 
-def test_async_method_call() -> None:
-    assert asyncio.run(method_call(3)) == 8
+async def test_async_method_call() -> None:
+    assert await method_call(3) == 8
     with assertRaises(MyError):
-        asyncio.run(method_call_exception())
+        await method_call_exception()
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
-# eh, we could use the real type but it doesn't seem important
-def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
 
@@ -159,16 +157,16 @@ async def branch_await_not() -> int:
         return 3
     return 2
 
-def test_branch() -> None:
-    assert asyncio.run(branch_await()) == 3
-    assert asyncio.run(branch_await_not()) == 2
+async def test_branch() -> None:
+    assert await branch_await() == 3
+    assert await branch_await_not() == 2
 
 async def assign_multi() -> int:
     _, x = int(), await one()
     return x + 1
 
-def test_assign_multi() -> None:
-    assert asyncio.run(assign_multi()) == 2
+async def test_assign_multi() -> None:
+    assert await assign_multi() == 2
 
 class C:
     def __init__(self, s: str) -> None:
@@ -188,8 +186,8 @@ async def concat(s: str, t: str) -> str:
 async def set_attr(s: str) -> None:
     (await make_c("xyz")).s = await concat(s, "!")
 
-def test_set_attr() -> None:
-    asyncio.run(set_attr("foo"))  # Just check that it compiles and runs
+async def test_set_attr() -> None:
+    await set_attr("foo")  # Just check that it compiles and runs
 
 def concat2(x: str, y: str) -> str:
     return x + y
@@ -200,15 +198,15 @@ async def call1(s: str) -> str:
 async def call2(s: str) -> str:
     return await concat(str(int()), await concat(s, "b"))
 
-def test_call() -> None:
-    assert asyncio.run(call1("foo")) == "0fooa"
-    assert asyncio.run(call2("foo")) == "0foob"
+async def test_call() -> None:
+    assert await call1("foo") == "0fooa"
+    assert await call2("foo") == "0foob"
 
 async def method_call(s: str) -> str:
     return C("<").concat(await concat(s, ">"))
 
-def test_method_call() -> None:
-    assert asyncio.run(method_call("foo")) == ""
+async def test_method_call() -> None:
+    assert await method_call("foo") == ""
 
 class D:
     def __init__(self, a: str, b: str) -> None:
@@ -219,13 +217,11 @@ async def construct(s: str) -> str:
     c = D(await concat(s, "!"), await concat(s, "?"))
     return c.a + c.b
 
-def test_construct() -> None:
-    assert asyncio.run(construct("foo")) == "foo!foo?"
+async def test_construct() -> None:
+    assert await construct("foo") == "foo!foo?"
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
-# eh, we could use the real type but it doesn't seem important
-def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
 
@@ -361,7 +357,7 @@ class ConManB:
     async def __aexit__(self, *exc: object):
         pass
 
-async def x() -> None:
+async def test_x() -> None:
     value = 2
     async with ConMan() as f:
         value += f
@@ -370,12 +366,6 @@ async def x() -> None:
         value += f
     assert value == 5, value
 
-[typing fixtures/typing-full.pyi]
-[file driver.py]
-import asyncio
-import native
-asyncio.run(native.x())
-
 [case testRunAsyncSpecialCases]
 import asyncio
 
@@ -385,8 +375,8 @@ async def t() -> tuple[int, str, str]:
 async def f() -> tuple[int, str, str]:
     return await t()
 
-def test_tuple_return() -> None:
-    result = asyncio.run(f())
+async def test_tuple_return() -> None:
+    result = await f()
     assert result == (1, "x", "y")
 
 async def e() -> ValueError:
@@ -395,14 +385,12 @@ async def e() -> ValueError:
 async def g() -> ValueError:
     return await e()
 
-def test_exception_return() -> None:
-    result = asyncio.run(g())
+async def test_exception_return() -> None:
+    result = await g()
     assert isinstance(result, ValueError)
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
-# eh, we could use the real type but it doesn't seem important
-def run(x: object) -> object: ...
 
 [typing fixtures/typing-full.pyi]
 
@@ -410,15 +398,15 @@ def run(x: object) -> object: ...
 import asyncio
 import gc
 
-def assert_no_leaks(fn, max_new):
+async def assert_no_leaks(fn, max_new):
     # Warm-up, in case asyncio allocates something on first use
-    asyncio.run(fn())
+    await fn()
 
     gc.collect()
     old_objs = gc.get_objects()
 
     for i in range(10):
-        asyncio.run(fn())
+        await fn()
 
     gc.collect()
     new_objs = gc.get_objects()
@@ -438,8 +426,8 @@ async def foo(n: int) -> str:
         s = await concat_one(s)
     return s
 
-def test_trivial() -> None:
-    assert_no_leaks(lambda: foo(1000), 5)
+async def test_trivial() -> None:
+    await assert_no_leaks(lambda: foo(1000), 5)
 
 async def make_list(a: list[int]) -> list[int]:
     await concat_one("foobar")
@@ -456,8 +444,8 @@ async def bar(n: int) -> None:
     for i in range(n):
         await spill()
 
-def test_spilled() -> None:
-    assert_no_leaks(lambda: bar(40), 2)
+async def test_spilled() -> None:
+    await assert_no_leaks(lambda: bar(80), 2)
 
 async def raise_deep(n: int) -> str:
     if n == 0:
@@ -484,8 +472,8 @@ async def exc(n: int) -> list[str]:
             a.append(str(int() + 5))
     return a
 
-def test_exception() -> None:
-    assert_no_leaks(lambda: exc(50), 2)
+async def test_exception() -> None:
+    await assert_no_leaks(lambda: exc(50), 2)
 
 class C:
     def __init__(self, s: str) -> None:
@@ -507,11 +495,10 @@ async def stolen(n: int) -> int:
         assert s == str(i + 2) + "1"
     return n
 
-def test_stolen() -> None:
-    assert_no_leaks(lambda: stolen(100), 2)
+async def test_stolen() -> None:
+    await assert_no_leaks(lambda: stolen(200), 2)
 
 [file asyncio/__init__.pyi]
-def run(x: object) -> object: ...
 async def sleep(t: float) -> None: ...
 
 [case testRunAsyncMiscTypesInEnvironment]
@@ -559,8 +546,8 @@ async def float_ops(x: float) -> float:
     n = float("0.5") + await inc_float(n)
     return n
 
-def test_float() -> None:
-    assert asyncio.run(float_ops(2.5)) == 5.0
+async def test_float() -> None:
+    assert await float_ops(2.5) == 5.0
 
 async def i64_ops(x: i64) -> i64:
     n = x
@@ -568,8 +555,8 @@ async def i64_ops(x: i64) -> i64:
     n = i64("1") + await inc_i64(n)
     return n
 
-def test_i64() -> None:
-    assert asyncio.run(i64_ops(2)) == 5
+async def test_i64() -> None:
+    assert await i64_ops(2) == 5
 
 async def i32_ops(x: i32) -> i32:
     n = x
@@ -577,8 +564,8 @@ async def i32_ops(x: i32) -> i32:
     n = i32("1") + await inc_i32(n)
     return n
 
-def test_i32() -> None:
-    assert asyncio.run(i32_ops(3)) == 6
+async def test_i32() -> None:
+    assert await i32_ops(3) == 6
 
 async def i16_ops(x: i16) -> i16:
     n = x
@@ -586,8 +573,8 @@ async def i16_ops(x: i16) -> i16:
     n = i16("1") + await inc_i16(n)
     return n
 
-def test_i16() -> None:
-    assert asyncio.run(i16_ops(4)) == 7
+async def test_i16() -> None:
+    assert await i16_ops(4) == 7
 
 async def u8_ops(x: u8) -> u8:
     n = x
@@ -595,8 +582,8 @@ async def u8_ops(x: u8) -> u8:
     n = u8("1") + await inc_u8(n)
     return n
 
-def test_u8() -> None:
-    assert asyncio.run(u8_ops(5)) == 8
+async def test_u8() -> None:
+    assert await u8_ops(5) == 8
 
 async def tuple_ops(x: tuple[i64, float]) -> tuple[i64, float]:
     n = x
@@ -604,8 +591,8 @@ async def tuple_ops(x: tuple[i64, float]) -> tuple[i64, float]:
     m = ((i64("1"), float("0.5")), await inc_tuple(n))
     return m[1]
 
-def test_tuple() -> None:
-    assert asyncio.run(tuple_ops((1, 2.5))) == (3, 5.5)
+async def test_tuple() -> None:
+    assert await tuple_ops((1, 2.5)) == (3, 5.5)
 
 async def bool_ops(x: bool) -> bool:
     n = x
@@ -613,9 +600,9 @@ async def bool_ops(x: bool) -> bool:
     m = (bool("1"), await neg_bool(n))
     return m[0] and m[1]
 
-def test_bool() -> None:
-    assert asyncio.run(bool_ops(True)) is True
-    assert asyncio.run(bool_ops(False)) is False
+async def test_bool() -> None:
+    assert await bool_ops(True) is True
+    assert await bool_ops(False) is False
 
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
@@ -648,8 +635,8 @@ async def async_def_contains_normal(x: int) -> int:
     a += nested((await inc(3)), (await inc(4)))
     return a
 
-def test_async_def_contains_normal() -> None:
-    assert normal_contains_async_def(2) == (2 + 2 + 4 + 5)
+async def test_async_def_contains_normal() -> None:
+    assert await async_def_contains_normal(2) == (2 + 2 + 4 + 5)
 
 async def async_def_contains_async_def(x: int) -> int:
     async def f(y: int) -> int:
@@ -657,8 +644,8 @@ async def async_def_contains_async_def(x: int) -> int:
 
     return (await f(1)) + (await f(2))
 
-def test_async_def_contains_async_def() -> None:
-    assert asyncio.run(async_def_contains_async_def(3)) == (3 + 1 + 1 + 1) + (3 + 1 + 2 + 1)
+async def test_async_def_contains_async_def() -> None:
+    assert await async_def_contains_async_def(3) == (3 + 1 + 1 + 1) + (3 + 1 + 2 + 1)
 
 async def async_def_contains_generator(x: int) -> tuple[int, int, int]:
     def gen(y: int) -> Iterator[int]:
@@ -673,8 +660,8 @@ async def async_def_contains_generator(x: int) -> tuple[int, int, int]:
 
     return res
 
-def test_async_def_contains_generator() -> None:
-    assert asyncio.run(async_def_contains_generator(3)) == (13, 4, 7)
+async def test_async_def_contains_generator() -> None:
+    assert await async_def_contains_generator(3) == (13, 4, 7)
 
 def generator_contains_async_def(x: int) -> Iterator[int]:
     async def f(y: int) -> int:
@@ -696,8 +683,8 @@ async def async_def_contains_two_nested_functions(x: int, y: int) -> tuple[int,
 
     return (await inc(f(3))), (await inc(g(4, 10)))
 
-def test_async_def_contains_two_nested_functions() -> None:
-    assert asyncio.run(async_def_contains_two_nested_functions(5, 7)) == (
+async def test_async_def_contains_two_nested_functions() -> None:
+    assert await async_def_contains_two_nested_functions(5, 7) == (
         (5 + 3 + 1), (7 + 4 + 10 + 1)
     )
 
@@ -714,8 +701,8 @@ async def async_def_contains_overloaded_async_def(n: int) -> int:
     return (await f(n)) + 1
 
 
-def test_async_def_contains_overloaded_async_def() -> None:
-    assert asyncio.run(async_def_contains_overloaded_async_def(5)) == 6
+async def test_async_def_contains_overloaded_async_def() -> None:
+    assert await async_def_contains_overloaded_async_def(5) == 6
 
 T = TypeVar("T")
 
@@ -730,8 +717,9 @@ async def async_def_contains_decorated_async_def(n: int) -> int:
     return (await f(n)) + 1
 
 
-def test_async_def_contains_decorated_async_def() -> None:
-    assert asyncio.run(async_def_contains_decorated_async_def(7)) == 10
+async def test_async_def_contains_decorated_async_def() -> None:
+    assert await async_def_contains_decorated_async_def(7) == 10
+
 [file asyncio/__init__.pyi]
 def run(x: object) -> object: ...
 
@@ -742,10 +730,7 @@ def run(x: object) -> object: ...
 # - at least one of those does not explicitly return
 # - the non-returning path is taken at runtime
 
-import asyncio
-
-
-async def test_mixed_return(b: bool) -> bool:
+async def mixed_return(b: bool) -> bool:
   try:
       if b:
           return b
@@ -754,33 +739,21 @@ async def test_mixed_return(b: bool) -> bool:
   return b
 
 
-async def test_run() -> None:
+async def test_async_try_finally_mixed_return() -> None:
   # Test return path
-  result1 = await test_mixed_return(True)
+  result1 = await mixed_return(True)
   assert result1 == True
 
   # Test non-return path
-  result2 = await test_mixed_return(False)
+  result2 = await mixed_return(False)
   assert result2 == False
 
-
-def test_async_try_finally_mixed_return() -> None:
-  asyncio.run(test_run())
-
-[file driver.py]
-from native import test_async_try_finally_mixed_return
-test_async_try_finally_mixed_return()
-
-[file asyncio/__init__.pyi]
-def run(x: object) -> object: ...
-
 [case testAsyncWithMixedReturn]
 # This used to raise an AttributeError, related to
 # testAsyncTryFinallyMixedReturn, this is essentially
 # a far more extensive version of that test surfacing
 # more edge cases
 
-import asyncio
 from typing import Optional, Type, Literal
 
 
@@ -798,14 +771,14 @@ class AsyncContextManager:
 
 
 # Simple async functions (generator class)
-async def test_gen_1(b: bool) -> bool:
+async def gen_1(b: bool) -> bool:
     async with AsyncContextManager():
         if b:
             return b
     return b
 
 
-async def test_gen_2(b: bool) -> bool:
+async def gen_2(b: bool) -> bool:
     async with AsyncContextManager():
         if b:
             return b
@@ -813,7 +786,7 @@ async def test_gen_2(b: bool) -> bool:
             return b
 
 
-async def test_gen_3(b: bool) -> bool:
+async def gen_3(b: bool) -> bool:
     async with AsyncContextManager():
         if b:
             return b
@@ -822,7 +795,7 @@ async def test_gen_3(b: bool) -> bool:
     return b
 
 
-async def test_gen_4(b: bool) -> bool:
+async def gen_4(b: bool) -> bool:
     ret: bool
     async with AsyncContextManager():
         if b:
@@ -832,7 +805,7 @@ async def test_gen_4(b: bool) -> bool:
     return ret
 
 
-async def test_gen_5(i: int) -> int:
+async def gen_5(i: int) -> int:
     async with AsyncContextManager():
         if i == 1:
             return i
@@ -843,7 +816,7 @@ async def test_gen_5(i: int) -> int:
     return i
 
 
-async def test_gen_6(i: int) -> int:
+async def gen_6(i: int) -> int:
     async with AsyncContextManager():
         if i == 1:
             return i
@@ -854,7 +827,7 @@ async def test_gen_6(i: int) -> int:
     return i
 
 
-async def test_gen_7(i: int) -> int:
+async def gen_7(i: int) -> int:
     async with AsyncContextManager():
         if i == 1:
             return i
@@ -867,7 +840,7 @@ async def test_gen_7(i: int) -> int:
 
 
 # Async functions with nested functions (environment class)
-async def test_env_1(b: bool) -> bool:
+async def env_1(b: bool) -> bool:
     def helper() -> bool:
         return True
 
@@ -877,7 +850,7 @@ async def test_env_1(b: bool) -> bool:
     return b
 
 
-async def test_env_2(b: bool) -> bool:
+async def env_2(b: bool) -> bool:
     def helper() -> bool:
         return True
 
@@ -888,7 +861,7 @@ async def test_env_2(b: bool) -> bool:
             return b
 
 
-async def test_env_3(b: bool) -> bool:
+async def env_3(b: bool) -> bool:
     def helper() -> bool:
         return True
 
@@ -900,7 +873,7 @@ async def test_env_3(b: bool) -> bool:
     return b
 
 
-async def test_env_4(b: bool) -> bool:
+async def env_4(b: bool) -> bool:
     def helper() -> bool:
         return True
 
@@ -913,7 +886,7 @@ async def test_env_4(b: bool) -> bool:
     return ret
 
 
-async def test_env_5(i: int) -> int:
+async def env_5(i: int) -> int:
     def helper() -> int:
         return 1
 
@@ -927,7 +900,7 @@ async def test_env_5(i: int) -> int:
     return i
 
 
-async def test_env_6(i: int) -> int:
+async def env_6(i: int) -> int:
     def helper() -> int:
         return 1
 
@@ -941,7 +914,7 @@ async def test_env_6(i: int) -> int:
     return i
 
 
-async def test_env_7(i: int) -> int:
+async def env_7(i: int) -> int:
     def helper() -> int:
         return 1
 
@@ -956,87 +929,76 @@ async def test_env_7(i: int) -> int:
             return i
 
 
-async def run_all_tests() -> None:
+async def test_async_with_mixed_return() -> None:
     # Test simple async functions (generator class)
-    # test_env_1: mixed return/no-return
-    assert await test_gen_1(True) is True
-    assert await test_gen_1(False) is False
-
-    # test_gen_2: all branches return
-    assert await test_gen_2(True) is True
-    assert await test_gen_2(False) is False
-
-    # test_gen_3: mixed return/pass
-    assert await test_gen_3(True) is True
-    assert await test_gen_3(False) is False
-
-    # test_gen_4: no returns in async with
-    assert await test_gen_4(True) is True
-    assert await test_gen_4(False) is False
-
-    # test_gen_5: multiple branches, some return
-    assert await test_gen_5(0) == 0
-    assert await test_gen_5(1) == 1
-    assert await test_gen_5(2) == 2
-    assert await test_gen_5(3) == 3
-
-    # test_gen_6: all explicit branches return, implicit fallthrough
-    assert await test_gen_6(0) == 0
-    assert await test_gen_6(1) == 1
-    assert await test_gen_6(2) == 2
-    assert await test_gen_6(3) == 3
-
-    # test_gen_7: all branches return including else
-    assert await test_gen_7(0) == 0
-    assert await test_gen_7(1) == 1
-    assert await test_gen_7(2) == 2
-    assert await test_gen_7(3) == 3
+    # env_1: mixed return/no-return
+    assert await gen_1(True) is True
+    assert await gen_1(False) is False
+
+    # gen_2: all branches return
+    assert await gen_2(True) is True
+    assert await gen_2(False) is False
+
+    # gen_3: mixed return/pass
+    assert await gen_3(True) is True
+    assert await gen_3(False) is False
+
+    # gen_4: no returns in async with
+    assert await gen_4(True) is True
+    assert await gen_4(False) is False
+
+    # gen_5: multiple branches, some return
+    assert await gen_5(0) == 0
+    assert await gen_5(1) == 1
+    assert await gen_5(2) == 2
+    assert await gen_5(3) == 3
+
+    # gen_6: all explicit branches return, implicit fallthrough
+    assert await gen_6(0) == 0
+    assert await gen_6(1) == 1
+    assert await gen_6(2) == 2
+    assert await gen_6(3) == 3
+
+    # gen_7: all branches return including else
+    assert await gen_7(0) == 0
+    assert await gen_7(1) == 1
+    assert await gen_7(2) == 2
+    assert await gen_7(3) == 3
 
     # Test async functions with nested functions (environment class)
-    # test_env_1: mixed return/no-return
-    assert await test_env_1(True) is True
-    assert await test_env_1(False) is False
-
-    # test_env_2: all branches return
-    assert await test_env_2(True) is True
-    assert await test_env_2(False) is False
-
-    # test_env_3: mixed return/pass
-    assert await test_env_3(True) is True
-    assert await test_env_3(False) is False
-
-    # test_env_4: no returns in async with
-    assert await test_env_4(True) is True
-    assert await test_env_4(False) is False
-
-    # test_env_5: multiple branches, some return
-    assert await test_env_5(0) == 0
-    assert await test_env_5(1) == 1
-    assert await test_env_5(2) == 2
-    assert await test_env_5(3) == 3
-
-    # test_env_6: all explicit branches return, implicit fallthrough
-    assert await test_env_6(0) == 0
-    assert await test_env_6(1) == 1
-    assert await test_env_6(2) == 2
-    assert await test_env_6(3) == 3
-
-    # test_env_7: all branches return including else
-    assert await test_env_7(0) == 0
-    assert await test_env_7(1) == 1
-    assert await test_env_7(2) == 2
-    assert await test_env_7(3) == 3
-
-
-def test_async_with_mixed_return() -> None:
-    asyncio.run(run_all_tests())
-
-[file driver.py]
-from native import test_async_with_mixed_return
-test_async_with_mixed_return()
-
-[file asyncio/__init__.pyi]
-def run(x: object) -> object: ...
+    # env_1: mixed return/no-return
+    assert await env_1(True) is True
+    assert await env_1(False) is False
+
+    # env_2: all branches return
+    assert await env_2(True) is True
+    assert await env_2(False) is False
+
+    # env_3: mixed return/pass
+    assert await env_3(True) is True
+    assert await env_3(False) is False
+
+    # env_4: no returns in async with
+    assert await env_4(True) is True
+    assert await env_4(False) is False
+
+    # env_5: multiple branches, some return
+    assert await env_5(0) == 0
+    assert await env_5(1) == 1
+    assert await env_5(2) == 2
+    assert await env_5(3) == 3
+
+    # env_6: all explicit branches return, implicit fallthrough
+    assert await env_6(0) == 0
+    assert await env_6(1) == 1
+    assert await env_6(2) == 2
+    assert await env_6(3) == 3
+
+    # env_7: all branches return including else
+    assert await env_7(0) == 0
+    assert await env_7(1) == 1
+    assert await env_7(2) == 2
+    assert await env_7(3) == 3
 
 [case testAsyncTryExceptFinallyAwait]
 import asyncio
@@ -1127,49 +1089,48 @@ async def async_no_exception_with_await_in_finally() -> int:
         await asyncio.sleep(0)
     return 2  # Should not reach this
 
-def test_async_try_except_finally_await() -> None:
+async def test_async_try_except_finally_await() -> None:
     # Test 0: Simplest case - just try/finally with exception
     # Expected: ValueError propagates
     with assertRaises(ValueError):
-        asyncio.run(simple_try_finally_await())
+        await simple_try_finally_await()
 
     # Test 1: Exception caught, not re-raised
     # Expected: return 2 (from except block)
-    result = asyncio.run(async_try_except_no_reraise())
+    result = await async_try_except_no_reraise()
     assert result == 2, f"Expected 2, got {result}"
 
     # Test 2: Exception caught and re-raised
     # Expected: ValueError propagates
     with assertRaises(ValueError):
-        asyncio.run(async_try_except_reraise())
+        await async_try_except_reraise()
 
     # Test 3: Exception caught, different exception raised
     # Expected: RuntimeError propagates
     with assertRaises(RuntimeError):
-        asyncio.run(async_try_except_raise_different())
+        await async_try_except_raise_different()
 
     # Test 4: Try/except inside finally
     # Expected: ValueError propagates (outer exception)
     with assertRaises(ValueError):
-        asyncio.run(async_try_except_inside_finally())
+        await async_try_except_inside_finally()
 
     # Test 5: Try/finally inside finally
     # Expected: RuntimeError propagates (inner error)
     with assertRaises(RuntimeError):
-        asyncio.run(async_try_finally_inside_finally())
+        await async_try_finally_inside_finally()
 
     # Control case: No await in finally (should work correctly)
     with assertRaises(TestError):
-        asyncio.run(async_exception_no_await_in_finally())
+        await async_exception_no_await_in_finally()
 
     # Test normal flow (no exception)
     # Expected: return 1
-    result = asyncio.run(async_no_exception_with_await_in_finally())
+    result = await async_no_exception_with_await_in_finally()
     assert result == 1, f"Expected 1, got {result}"
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
-def run(x: object) -> object: ...
 
 [case testAsyncContextManagerExceptionHandling]
 import asyncio
@@ -1233,18 +1194,17 @@ async def test_exception_in_aexit() -> str:
     except Exception as e:
         return f"caught different exception: {type(e).__name__}"
 
-def test_async_context_manager_exception_handling() -> None:
+async def test_async_context_manager_exception_handling() -> None:
     # Test 1: Basic exception propagation
-    result = asyncio.run(test_basic_exception())
+    result = await test_basic_exception()
     # Expected: "caught ValueError - correct!"
     assert result == "caught ValueError - correct!", f"Expected exception to propagate, got: {result}"
 
     # Test 2: Exception raised in __aexit__ replaces original exception
-    result = asyncio.run(test_exception_in_aexit())
+    result = await test_exception_in_aexit()
     # Expected: "caught RuntimeError - correct!"
     # (The RuntimeError from __aexit__ should replace the ValueError)
     assert result == "caught RuntimeError - correct!", f"Expected RuntimeError from __aexit__, got: {result}"
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
-def run(x: object) -> object: ...

From 3387d6fd9761cb39c952abe5251ee538bcdea598 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Tue, 5 Aug 2025 02:23:45 +0200
Subject: [PATCH 1524/1617] Fix dict assignment to a wider context containing
 an incompatible typeddict of the same shape (#19592)

Fixes #19590.
Fixes #14991 (oops, I forgot I have already reported this...).

When a typeddict context does not cover all available options, proceed
with checking as usual against the whole context if none of the items
matches in full despite being structurally compatible.
---
 mypy/checkexpr.py                   | 24 ++++++++++++++++--------
 test-data/unit/check-typeddict.test | 18 ++++++++++++++++++
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 1b10370b08cb..6e0915179f90 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -5350,9 +5350,9 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
         # an error, but returns the TypedDict type that matches the literal it found
         # that would cause a second error when that TypedDict type is returned upstream
         # to avoid the second error, we always return TypedDict type that was requested
-        typeddict_contexts = self.find_typeddict_context(self.type_context[-1], e)
+        typeddict_contexts, exhaustive = self.find_typeddict_context(self.type_context[-1], e)
         if typeddict_contexts:
-            if len(typeddict_contexts) == 1:
+            if len(typeddict_contexts) == 1 and exhaustive:
                 return self.check_typeddict_literal_in_context(e, typeddict_contexts[0])
             # Multiple items union, check if at least one of them matches cleanly.
             for typeddict_context in typeddict_contexts:
@@ -5363,7 +5363,8 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
                 self.chk.store_types(tmap)
                 return ret_type
             # No item matched without an error, so we can't unambiguously choose the item.
-            self.msg.typeddict_context_ambiguous(typeddict_contexts, e)
+            if exhaustive:
+                self.msg.typeddict_context_ambiguous(typeddict_contexts, e)
 
         # fast path attempt
         dt = self.fast_dict_type(e)
@@ -5425,22 +5426,29 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
 
     def find_typeddict_context(
         self, context: Type | None, dict_expr: DictExpr
-    ) -> list[TypedDictType]:
+    ) -> tuple[list[TypedDictType], bool]:
+        """Extract `TypedDict` members of the enclosing context.
+
+        Returns:
+            a 2-tuple, (found_candidates, is_exhaustive)
+        """
         context = get_proper_type(context)
         if isinstance(context, TypedDictType):
-            return [context]
+            return [context], True
         elif isinstance(context, UnionType):
             items = []
+            exhaustive = True
             for item in context.items:
-                item_contexts = self.find_typeddict_context(item, dict_expr)
+                item_contexts, item_exhaustive = self.find_typeddict_context(item, dict_expr)
                 for item_context in item_contexts:
                     if self.match_typeddict_call_with_dict(
                         item_context, dict_expr.items, dict_expr
                     ):
                         items.append(item_context)
-            return items
+                exhaustive = exhaustive and item_exhaustive
+            return items, exhaustive
         # No TypedDict type in context.
-        return []
+        return [], False
 
     def visit_lambda_expr(self, e: LambdaExpr) -> Type:
         """Type check lambda expression."""
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
index be5a6c655d8c..34cae74d795b 100644
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -4289,3 +4289,21 @@ inputs: Sequence[Component] = [{
 }]
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]
+
+[case testTypedDictAssignableToWiderContext]
+from typing import TypedDict, Union
+
+class TD(TypedDict):
+    x: int
+
+x: Union[TD, dict[str, str]] = {"x": "foo"}
+y: Union[TD, dict[str, int]] = {"x": "foo"}  # E: Dict entry 0 has incompatible type "str": "str"; expected "str": "int"
+
+def ok(d: Union[TD, dict[str, str]]) -> None: ...
+ok({"x": "foo"})
+
+def bad(d: Union[TD, dict[str, int]]) -> None: ...
+bad({"x": "foo"})  # E: Dict entry 0 has incompatible type "str": "str"; expected "str": "int"
+
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-typeddict.pyi]

From 83fed5ab1f307016e1fbb00527e2b6f88e9522b5 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 5 Aug 2025 14:26:17 +0100
Subject: [PATCH 1525/1617] Skip more method bodies in third-party libraries
 (#19586)

A while ago we started stripping function bodies when checking
third-party libraries. This PR pushes this idea further:
* Tighten the check in `fastparse.py` to only consider `foo.bar` as
possible self attribute definition.
* Do not type-check bodies where we didn't find any `self` attribute
_definitions_ during semantic analysis.
* Skip method override checks in third-party libraries.

In total this makes e.g. `mypy -c 'import torch'` ~10% faster.
Surprisingly, this also has some visible impact on self-check.
---
 mypy/checker.py   | 23 ++++++++++++++++++++---
 mypy/fastparse.py |  2 +-
 mypy/nodes.py     |  3 +++
 mypy/semanal.py   |  3 +++
 4 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 4d8221415754..68f9bd4c1383 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -832,8 +832,10 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
         # At this point we should have set the impl already, and all remaining
         # items are decorators
 
-        if self.msg.errors.file in self.msg.errors.ignored_files or (
-            self.is_typeshed_stub and self.options.test_env
+        if (
+            self.options.ignore_errors
+            or self.msg.errors.file in self.msg.errors.ignored_files
+            or (self.is_typeshed_stub and self.options.test_env)
         ):
             # This is a little hacky, however, the quadratic check here is really expensive, this
             # method has no side effects, so we should skip it if we aren't going to report
@@ -1444,7 +1446,19 @@ def check_func_def(
                     # TODO: Find a way of working around this limitation
                     if _is_empty_generator_function(item) or len(expanded) >= 2:
                         self.binder.suppress_unreachable_warnings()
-                    self.accept(item.body)
+                    # When checking a third-party library, we can skip function body,
+                    # if during semantic analysis we found that there are no attributes
+                    # defined via self here.
+                    if (
+                        not (
+                            self.options.ignore_errors
+                            or self.msg.errors.file in self.msg.errors.ignored_files
+                        )
+                        or self.options.preserve_asts
+                        or not isinstance(defn, FuncDef)
+                        or defn.has_self_attr_def
+                    ):
+                        self.accept(item.body)
                 unreachable = self.binder.is_unreachable()
                 if new_frame is not None:
                     self.binder.pop_frame(True, 0)
@@ -2127,6 +2141,9 @@ def check_method_override(
 
         Return a list of base classes which contain an attribute with the method name.
         """
+        if self.options.ignore_errors or self.msg.errors.file in self.msg.errors.ignored_files:
+            # Method override checks may be expensive, so skip them in third-party libraries.
+            return None
         # Check against definitions in base classes.
         check_override_compatibility = (
             defn.name not in ("__init__", "__new__", "__init_subclass__", "__post_init__")
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index bb71242182f1..0e1b66f0db59 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -2234,7 +2234,7 @@ def visit_index_expr(self, e: IndexExpr) -> None:
         pass
 
     def visit_member_expr(self, e: MemberExpr) -> None:
-        if self.lvalue:
+        if self.lvalue and isinstance(e.expr, NameExpr):
             self.found = True
 
 
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 6ffe579efe71..d5fa4a79699e 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -818,6 +818,7 @@ class FuncDef(FuncItem, SymbolNode, Statement):
         "original_def",
         "is_trivial_body",
         "is_trivial_self",
+        "has_self_attr_def",
         "is_mypy_only",
         # Present only when a function is decorated with @typing.dataclass_transform or similar
         "dataclass_transform_spec",
@@ -856,6 +857,8 @@ def __init__(
         # the majority). In cases where self is not annotated and there are no Self
         # in the signature we can simply drop the first argument.
         self.is_trivial_self = False
+        # Keep track of functions where self attributes are defined.
+        self.has_self_attr_def = False
         # This is needed because for positional-only arguments the name is set to None,
         # but we sometimes still want to show it in error messages.
         if arguments:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index ab9075cd06ce..0aace6b1165a 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -4570,6 +4570,9 @@ def analyze_member_lvalue(
                     lval.node = v
                     # TODO: should we also set lval.kind = MDEF?
                     self.type.names[lval.name] = SymbolTableNode(MDEF, v, implicit=True)
+                    for func in self.scope.functions:
+                        if isinstance(func, FuncDef):
+                            func.has_self_attr_def = True
         self.check_lvalue_validity(lval.node, lval)
 
     def is_self_member_ref(self, memberexpr: MemberExpr) -> bool:

From d7753ef05393eaa9f7902ac5cb53ced30a0b7b2e Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 5 Aug 2025 14:27:43 +0100
Subject: [PATCH 1526/1617] Move Windows tests from oldest to newest version
 (#19545)

This is mostly to speed-up CI, but also as we discussed with Jukka this
may make more sense as we want to test new features on more platforms.
---
 .github/workflows/test.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 97fb7755563b..47f725170bd8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -37,11 +37,6 @@ jobs:
           toxenv: py
           tox_extra_args: "-n 4"
           test_mypyc: true
-        - name: Test suite with py39-windows-64
-          python: '3.9'
-          os: windows-latest
-          toxenv: py39
-          tox_extra_args: "-n 4"
         - name: Test suite with py310-ubuntu
           python: '3.10'
           os: ubuntu-24.04-arm
@@ -64,6 +59,11 @@ jobs:
           toxenv: py
           tox_extra_args: "-n 4"
           test_mypyc: true
+        - name: Test suite with py313-windows-64
+          python: '3.13'
+          os: windows-latest
+          toxenv: py
+          tox_extra_args: "-n 4"
 
         - name: Test suite with py314-dev-ubuntu
           python: '3.14-dev'

From db67fac952a390cf2cb533beb9bcce1cf15ce918 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 5 Aug 2025 17:05:54 +0100
Subject: [PATCH 1527/1617] Assorted niche optimizations (#19587)

These are few random micro-optimizations (plus few correctness fixes I
noticed in the process):
* De-serializing large callables/overloads is very slaw because of
`Enum.__call__()`
* Large unions _without_ literals were forced through
`make_simplified_union()` in code path that supposed to handle literals
* Now that `SomeType.__eq__()` is called more often and we preserve more
original types in `expand_type()` I added couple fast paths after
gathering call stats there.

In total this gives ~0.5% on self-check, but makes loading some numeric
libraries from cache up to 10% faster.
---
 mypy/nodes.py   |  8 +++++++-
 mypy/semanal.py |  2 ++
 mypy/typeops.py | 14 ++++++++------
 mypy/types.py   | 25 +++++++++++++------------
 4 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/mypy/nodes.py b/mypy/nodes.py
index d5fa4a79699e..9d5867c5371d 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -918,7 +918,7 @@ def deserialize(cls, data: JsonDict) -> FuncDef:
         # NOTE: ret.info is set in the fixup phase.
         ret.arg_names = data["arg_names"]
         ret.original_first_arg = data.get("original_first_arg")
-        ret.arg_kinds = [ArgKind(x) for x in data["arg_kinds"]]
+        ret.arg_kinds = [ARG_KINDS[x] for x in data["arg_kinds"]]
         ret.abstract_status = data["abstract_status"]
         ret.dataclass_transform_spec = (
             DataclassTransformSpec.deserialize(data["dataclass_transform_spec"])
@@ -2016,6 +2016,8 @@ def is_star(self) -> bool:
 ARG_STAR2: Final = ArgKind.ARG_STAR2
 ARG_NAMED_OPT: Final = ArgKind.ARG_NAMED_OPT
 
+ARG_KINDS: Final = (ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2, ARG_NAMED_OPT)
+
 
 class CallExpr(Expression):
     """Call expression.
@@ -3491,6 +3493,8 @@ def update_tuple_type(self, typ: mypy.types.TupleType) -> None:
             self.special_alias = alias
         else:
             self.special_alias.target = alias.target
+            # Invalidate recursive status cache in case it was previously set.
+            self.special_alias._is_recursive = None
 
     def update_typeddict_type(self, typ: mypy.types.TypedDictType) -> None:
         """Update typeddict_type and special_alias as needed."""
@@ -3500,6 +3504,8 @@ def update_typeddict_type(self, typ: mypy.types.TypedDictType) -> None:
             self.special_alias = alias
         else:
             self.special_alias.target = alias.target
+            # Invalidate recursive status cache in case it was previously set.
+            self.special_alias._is_recursive = None
 
     def __str__(self) -> str:
         """Return a string representation of the type.
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 0aace6b1165a..99d1eb36e788 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5636,6 +5636,8 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
                         existing.node.target = res
                         existing.node.alias_tvars = alias_tvars
                         updated = True
+                        # Invalidate recursive status cache in case it was previously set.
+                        existing.node._is_recursive = None
                 else:
                     # Otherwise just replace existing placeholder with type alias.
                     existing.node = alias_node
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 75213bd93674..13082225e5fd 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -63,6 +63,7 @@
     flatten_nested_unions,
     get_proper_type,
     get_proper_types,
+    remove_dups,
 )
 from mypy.typetraverser import TypeTraverserVisitor
 from mypy.typevars import fill_typevars
@@ -995,7 +996,7 @@ def is_singleton_type(typ: Type) -> bool:
     return typ.is_singleton_type()
 
 
-def try_expanding_sum_type_to_union(typ: Type, target_fullname: str) -> ProperType:
+def try_expanding_sum_type_to_union(typ: Type, target_fullname: str) -> Type:
     """Attempts to recursively expand any enum Instances with the given target_fullname
     into a Union of all of its component LiteralTypes.
 
@@ -1017,21 +1018,22 @@ class Status(Enum):
     typ = get_proper_type(typ)
 
     if isinstance(typ, UnionType):
+        # Non-empty enums cannot subclass each other so simply removing duplicates is enough.
         items = [
-            try_expanding_sum_type_to_union(item, target_fullname) for item in typ.relevant_items()
+            try_expanding_sum_type_to_union(item, target_fullname)
+            for item in remove_dups(flatten_nested_unions(typ.relevant_items()))
         ]
-        return make_simplified_union(items, contract_literals=False)
+        return UnionType.make_union(items)
 
     if isinstance(typ, Instance) and typ.type.fullname == target_fullname:
         if typ.type.fullname == "builtins.bool":
-            items = [LiteralType(True, typ), LiteralType(False, typ)]
-            return make_simplified_union(items, contract_literals=False)
+            return UnionType([LiteralType(True, typ), LiteralType(False, typ)])
 
         if typ.type.is_enum:
             items = [LiteralType(name, typ) for name in typ.type.enum_members]
             if not items:
                 return typ
-            return make_simplified_union(items, contract_literals=False)
+            return UnionType.make_union(items)
 
     return typ
 
diff --git a/mypy/types.py b/mypy/types.py
index 029477c1d5c4..b4771b15f77a 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -21,7 +21,7 @@
 
 import mypy.nodes
 from mypy.bogus_type import Bogus
-from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, FakeInfo, SymbolNode
+from mypy.nodes import ARG_KINDS, ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, SymbolNode
 from mypy.options import Options
 from mypy.state import state
 from mypy.util import IdMapper
@@ -538,6 +538,10 @@ def __repr__(self) -> str:
         return self.raw_id.__repr__()
 
     def __eq__(self, other: object) -> bool:
+        # Although this call is not expensive (like UnionType or TypedDictType),
+        # most of the time we get the same object here, so add a fast path.
+        if self is other:
+            return True
         return (
             isinstance(other, TypeVarId)
             and self.raw_id == other.raw_id
@@ -1780,7 +1784,9 @@ def deserialize(cls, data: JsonDict) -> Parameters:
         assert data[".class"] == "Parameters"
         return Parameters(
             [deserialize_type(t) for t in data["arg_types"]],
-            [ArgKind(x) for x in data["arg_kinds"]],
+            # This is a micro-optimization until mypyc gets dedicated enum support. Otherwise,
+            # we would spend ~20% of types deserialization time in Enum.__call__().
+            [ARG_KINDS[x] for x in data["arg_kinds"]],
             data["arg_names"],
             variables=[cast(TypeVarLikeType, deserialize_type(v)) for v in data["variables"]],
             imprecise_arg_kinds=data["imprecise_arg_kinds"],
@@ -1797,7 +1803,7 @@ def __hash__(self) -> int:
         )
 
     def __eq__(self, other: object) -> bool:
-        if isinstance(other, (Parameters, CallableType)):
+        if isinstance(other, Parameters):
             return (
                 self.arg_types == other.arg_types
                 and self.arg_names == other.arg_names
@@ -2210,15 +2216,9 @@ def with_normalized_var_args(self) -> Self:
         )
 
     def __hash__(self) -> int:
-        # self.is_type_obj() will fail if self.fallback.type is a FakeInfo
-        if isinstance(self.fallback.type, FakeInfo):
-            is_type_obj = 2
-        else:
-            is_type_obj = self.is_type_obj()
         return hash(
             (
                 self.ret_type,
-                is_type_obj,
                 self.is_ellipsis_args,
                 self.name,
                 tuple(self.arg_types),
@@ -2236,7 +2236,6 @@ def __eq__(self, other: object) -> bool:
                 and self.arg_names == other.arg_names
                 and self.arg_kinds == other.arg_kinds
                 and self.name == other.name
-                and self.is_type_obj() == other.is_type_obj()
                 and self.is_ellipsis_args == other.is_ellipsis_args
                 and self.type_guard == other.type_guard
                 and self.type_is == other.type_is
@@ -2271,10 +2270,10 @@ def serialize(self) -> JsonDict:
     @classmethod
     def deserialize(cls, data: JsonDict) -> CallableType:
         assert data[".class"] == "CallableType"
-        # TODO: Set definition to the containing SymbolNode?
+        # The .definition link is set in fixup.py.
         return CallableType(
             [deserialize_type(t) for t in data["arg_types"]],
-            [ArgKind(x) for x in data["arg_kinds"]],
+            [ARG_KINDS[x] for x in data["arg_kinds"]],
             data["arg_names"],
             deserialize_type(data["ret_type"]),
             Instance.deserialize(data["fallback"]),
@@ -2931,6 +2930,8 @@ def __hash__(self) -> int:
     def __eq__(self, other: object) -> bool:
         if not isinstance(other, UnionType):
             return NotImplemented
+        if self is other:
+            return True
         return frozenset(self.items) == frozenset(other.items)
 
     @overload

From a6bfb2eed74e156f48daa8266945708c0a50366d Mon Sep 17 00:00:00 2001
From: wyattscarpenter 
Date: Thu, 7 Aug 2025 22:05:57 -0700
Subject: [PATCH 1528/1617] Fix crash when using enable_error_code value of
 wrong type in pyproject.toml (#19494)

Fixes #19491

If you give a string in toml, you get Invalid error code(s): whatever.
However, if you give a value that doesn't mean a string in toml, you get
a crash like TypeError: 'int' object is not iterable.

I suspect this would also apply to many other pyproject.toml values if
you set them wrong, because we pass many of them immediately into
try_split, which tries to iterate them.

I have added a (fairly minimal) test for this behavior.

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
---
 mypy/config_parser.py                 | 29 +++++++++++++++++++++------
 test-data/unit/cmdline.pyproject.test | 11 ++++++++++
 2 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/mypy/config_parser.py b/mypy/config_parser.py
index 208c12adafbe..5f08f342241e 100644
--- a/mypy/config_parser.py
+++ b/mypy/config_parser.py
@@ -17,15 +17,15 @@
 
 from collections.abc import Mapping, MutableMapping, Sequence
 from typing import Any, Callable, Final, TextIO, Union
-from typing_extensions import TypeAlias as _TypeAlias
+from typing_extensions import Never, TypeAlias
 
 from mypy import defaults
 from mypy.options import PER_MODULE_OPTIONS, Options
 
-_CONFIG_VALUE_TYPES: _TypeAlias = Union[
+_CONFIG_VALUE_TYPES: TypeAlias = Union[
     str, bool, int, float, dict[str, str], list[str], tuple[int, int]
 ]
-_INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES]
+_INI_PARSER_CALLABLE: TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES]
 
 
 class VersionTypeError(argparse.ArgumentTypeError):
@@ -60,14 +60,31 @@ def parse_version(v: str | float) -> tuple[int, int]:
     return major, minor
 
 
-def try_split(v: str | Sequence[str], split_regex: str = "[,]") -> list[str]:
-    """Split and trim a str or list of str into a list of str"""
+def try_split(v: str | Sequence[str] | object, split_regex: str = ",") -> list[str]:
+    """Split and trim a str or sequence (eg: list) of str into a list of str.
+    If an element of the input is not str, a type error will be raised."""
+
+    def complain(x: object, additional_info: str = "") -> Never:
+        raise argparse.ArgumentTypeError(
+            f"Expected a list or a stringified version thereof, but got: '{x}', of type {type(x).__name__}.{additional_info}"
+        )
+
     if isinstance(v, str):
         items = [p.strip() for p in re.split(split_regex, v)]
         if items and items[-1] == "":
             items.pop(-1)
         return items
-    return [p.strip() for p in v]
+    elif isinstance(v, Sequence):
+        return [
+            (
+                p.strip()
+                if isinstance(p, str)
+                else complain(p, additional_info=" (As an element of the list.)")
+            )
+            for p in v
+        ]
+    else:
+        complain(v)
 
 
 def validate_codes(codes: list[str]) -> list[str]:
diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test
index f9691ba245f9..68dfacb372fb 100644
--- a/test-data/unit/cmdline.pyproject.test
+++ b/test-data/unit/cmdline.pyproject.test
@@ -226,3 +226,14 @@ y: int = 'y'  # E: Incompatible types in assignment (expression has type "str",
 # This should not trigger any errors, because it is not included:
 z: int = 'z'
 [out]
+
+[case testPyprojectTOMLSettingOfWrongType]
+# cmd: mypy a.py
+[file pyproject.toml]
+\[tool.mypy]
+enable_error_code = true
+[file a.py]
+x: int = 1
+[out]
+pyproject.toml: [mypy]: enable_error_code: Expected a list or a stringified version thereof, but got: 'True', of type bool.
+== Return code: 0

From de6e7426ab3e7fac87ca5afc575846dc05763ea7 Mon Sep 17 00:00:00 2001
From: Saul Shanabrook 
Date: Fri, 8 Aug 2025 01:06:52 -0400
Subject: [PATCH 1529/1617] Fix TypeGuard with call on temporary object 
 (#19577)

Fixes #19575 by adding support for TypeGaurd/TypeIs when they are used
on methods off of classes which were not saved to a variable.

Solution adapted from copilot answer here and then refined:
https://github.com/saulshanabrook/mypy/pull/1
---
 mypy/checker.py                     | 43 ++++++++++++++------------
 test-data/unit/check-typeguard.test | 47 +++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 20 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 68f9bd4c1383..3b94b84bb975 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6218,21 +6218,26 @@ def find_isinstance_check_helper(
                 attr = try_getting_str_literals(node.args[1], self.lookup_type(node.args[1]))
                 if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1:
                     return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0])
-            elif isinstance(node.callee, RefExpr):
-                if node.callee.type_guard is not None or node.callee.type_is is not None:
+            else:
+                type_is, type_guard = None, None
+                called_type = self.lookup_type_or_none(node.callee)
+                if called_type is not None:
+                    called_type = get_proper_type(called_type)
+                    # TODO: there are some more cases in check_call() to handle.
+                    # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method.
+                    if isinstance(called_type, Instance):
+                        call = find_member("__call__", called_type, called_type, is_operator=True)
+                        if call is not None:
+                            called_type = get_proper_type(call)
+                    if isinstance(called_type, CallableType):
+                        type_is, type_guard = called_type.type_is, called_type.type_guard
+
+                # If the callee is a RefExpr, extract TypeGuard/TypeIs directly.
+                if isinstance(node.callee, RefExpr):
+                    type_is, type_guard = node.callee.type_is, node.callee.type_guard
+                if type_guard is not None or type_is is not None:
                     # TODO: Follow *args, **kwargs
                     if node.arg_kinds[0] != nodes.ARG_POS:
-                        # the first argument might be used as a kwarg
-                        called_type = get_proper_type(self.lookup_type(node.callee))
-
-                        # TODO: there are some more cases in check_call() to handle.
-                        if isinstance(called_type, Instance):
-                            call = find_member(
-                                "__call__", called_type, called_type, is_operator=True
-                            )
-                            if call is not None:
-                                called_type = get_proper_type(call)
-
                         # *assuming* the overloaded function is correct, there's a couple cases:
                         #  1) The first argument has different names, but is pos-only. We don't
                         #     care about this case, the argument must be passed positionally.
@@ -6245,9 +6250,7 @@ def find_isinstance_check_helper(
                                 # we want the idx-th variable to be narrowed
                                 expr = collapse_walrus(node.args[idx])
                             else:
-                                kind = (
-                                    "guard" if node.callee.type_guard is not None else "narrower"
-                                )
+                                kind = "guard" if type_guard is not None else "narrower"
                                 self.fail(
                                     message_registry.TYPE_GUARD_POS_ARG_REQUIRED.format(kind), node
                                 )
@@ -6258,15 +6261,15 @@ def find_isinstance_check_helper(
                         # considered "always right" (i.e. even if the types are not overlapping).
                         # Also note that a care must be taken to unwrap this back at read places
                         # where we use this to narrow down declared type.
-                        if node.callee.type_guard is not None:
-                            return {expr: TypeGuardedType(node.callee.type_guard)}, {}
+                        if type_guard is not None:
+                            return {expr: TypeGuardedType(type_guard)}, {}
                         else:
-                            assert node.callee.type_is is not None
+                            assert type_is is not None
                             return conditional_types_to_typemaps(
                                 expr,
                                 *self.conditional_types_with_intersection(
                                     self.lookup_type(expr),
-                                    [TypeRange(node.callee.type_is, is_upper_bound=False)],
+                                    [TypeRange(type_is, is_upper_bound=False)],
                                     expr,
                                     consider_runtime_isinstance=False,
                                 ),
diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
index fdcfcc969adc..93e665e4548c 100644
--- a/test-data/unit/check-typeguard.test
+++ b/test-data/unit/check-typeguard.test
@@ -731,6 +731,53 @@ assert a(x=x)
 reveal_type(x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/tuple.pyi]
 
+# https://github.com/python/mypy/issues/19575
+[case testNoCrashOnDunderCallTypeGuardTemporaryObject]
+from typing_extensions import TypeGuard
+class E:
+    def __init__(self) -> None: ...
+    def __call__(self, o: object) -> TypeGuard[int]:
+        return True
+x = object()
+if E()(x):
+    reveal_type(x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
+
+[case testNoCrashOnDunderCallTypeIsTemporaryObject]
+from typing_extensions import TypeIs
+class E:
+    def __init__(self) -> None: ...
+    def __call__(self, o: object) -> TypeIs[int]:
+        return True
+x = object()
+if E()(x):
+    reveal_type(x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
+
+[case testNoCrashOnDunderCallTypeIsTemporaryObjectGeneric]
+from typing import Generic, TypeVar
+from typing_extensions import TypeIs
+T = TypeVar("T")
+class E(Generic[T]):
+    def __init__(self) -> None: ...
+    def __call__(self, o: object) -> TypeIs[T]:
+        return True
+x = object()
+if E[int]()(x):
+    reveal_type(x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
+
+[case testTypeGuardTemporaryObjectWithKeywordArg]
+from typing_extensions import TypeGuard
+class E:
+    def __init__(self) -> None: ...
+    def __call__(self, o: object) -> TypeGuard[int]:
+        return True
+x = object()
+if E()(o=x):
+    reveal_type(x)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
+
 [case testTypeGuardRestrictAwaySingleInvariant]
 from typing import List
 from typing_extensions import TypeGuard

From a3eb219fb9a1c4d700b897ebd5e0ba95d2846c35 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Fri, 8 Aug 2025 06:38:45 -0700
Subject: [PATCH 1530/1617] Fix overload diagnostic when vararg and varkwarg
 can match (#19614)

Fixes #19612
---
 mypy/typeops.py                       |  9 ++++++---
 test-data/unit/check-overloading.test | 14 +++++++++++++-
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/mypy/typeops.py b/mypy/typeops.py
index 13082225e5fd..866eb72ede3e 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -521,15 +521,18 @@ def callable_corresponding_argument(
 
         # def right(a: int = ...) -> None: ...
         # def left(__a: int = ..., *, a: int = ...) -> None: ...
-        from mypy.subtypes import is_equivalent
+        from mypy.subtypes import is_subtype
 
         if (
             not (by_name.required or by_pos.required)
             and by_pos.name is None
             and by_name.pos is None
-            and is_equivalent(by_name.typ, by_pos.typ)
         ):
-            return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False)
+            # We actually want the intersection of by_name.typ and by_pos.typ
+            if is_subtype(by_name.typ, by_pos.typ):
+                return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False)
+            if is_subtype(by_pos.typ, by_name.typ):
+                return FormalArgument(by_name.name, by_pos.pos, by_pos.typ, False)
     return by_name if by_name is not None else by_pos
 
 
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index e7f6ff04c13e..560d4a5c12fc 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -231,9 +231,21 @@ def f(x: 'A') -> Any: # E: Overloaded function implementation does not accept al
 
 reveal_type(f(A())) # N: Revealed type is "__main__.B"
 reveal_type(f(B())) # N: Revealed type is "__main__.A"
-
 [builtins fixtures/isinstance.pyi]
 
+[case testTypeCheckOverloadImplOverlapVarArgsAndKwargs]
+from __future__ import annotations
+from typing import overload
+
+@overload
+def foo(x: int) -> None: ...
+@overload
+def foo(a: str, /) -> None: ...
+
+def foo(*args: int | str, **kw: int) -> None:
+    pass
+[builtins fixtures/tuple.pyi]
+
 [case testTypeCheckOverloadWithImplTooSpecificRetType]
 from typing import overload, Any
 

From 0d791b29b7ba4e5a9b04c0b6bdba11faf4a186a2 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Fri, 8 Aug 2025 15:48:09 +0200
Subject: [PATCH 1531/1617] PEP 702 (@deprecated): consider overloads in
 snapshot descriptions (#19613)

This change is taken from #18682. The tests are unmodified. The code is
simplified [as suggested by
Ivan](https://github.com/python/mypy/pull/18682#issuecomment-3158248336).
---
 mypy/server/astdiff.py           |  11 +-
 test-data/unit/fine-grained.test | 308 +++++++++++++++++++++++++++++++
 2 files changed, 318 insertions(+), 1 deletion(-)

diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py
index 16a0d882a8aa..1df85a163e0f 100644
--- a/mypy/server/astdiff.py
+++ b/mypy/server/astdiff.py
@@ -252,6 +252,15 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb
                 setter_type = snapshot_optional_type(first_item.var.setter_type)
         is_trivial_body = impl.is_trivial_body if impl else False
         dataclass_transform_spec = find_dataclass_transform_spec(node)
+
+        deprecated: str | list[str | None] | None = None
+        if isinstance(node, FuncDef):
+            deprecated = node.deprecated
+        elif isinstance(node, OverloadedFuncDef):
+            deprecated = [node.deprecated] + [
+                i.func.deprecated for i in node.items if isinstance(i, Decorator)
+            ]
+
         return (
             "Func",
             common,
@@ -262,7 +271,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb
             signature,
             is_trivial_body,
             dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None,
-            node.deprecated if isinstance(node, FuncDef) else None,
+            deprecated,
             setter_type,  # multi-part properties are stored as OverloadedFuncDef
         )
     elif isinstance(node, Var):
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index c25ed79e7356..0d10559d0692 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -11033,6 +11033,314 @@ b.py:1: error: class a.C is deprecated: use C2 instead
 b.py:2: error: class a.D is deprecated: use D2 instead
 
 
+[case testDeprecatedAddKeepChangeAndRemoveOverloadedFunctionDeprecation]
+# flags: --enable-error-code=deprecated
+
+from a import f
+f(1)
+f("y")
+import a
+a.f(1)
+a.f("y")
+
+[file a.py]
+from typing import overload, Union
+@overload
+def f(x: int) -> int: ...
+@overload
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.3]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.4]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int, please")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.5]
+from typing import overload, Union
+@overload
+def f(x: int) -> int: ...
+@overload
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please
+==
+
+
+[case testDeprecatedRemoveOverloadedFunctionDeprecation]
+# flags: --enable-error-code=deprecated
+
+from a import f
+f(1)
+f("y")
+import a
+a.f(1)
+a.f("y")
+
+[file a.py]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+@overload
+def f(x: int) -> int: ...
+@overload
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+
+
+[case testDeprecatedKeepOverloadedFunctionDeprecation]
+# flags: --enable-error-code=deprecated
+
+from a import f
+f(1)
+f("y")
+import a
+a.f(1)
+a.f("y")
+
+[file a.py]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+
+
+[case testDeprecatedAddOverloadedFunctionDeprecationIndirectImport]
+# flags: --enable-error-code=deprecated
+
+from b import f
+f(1)
+f("y")
+import b
+b.f(1)
+b.f("y")
+
+[file b.py]
+from a import f
+
+[file a.py]
+from typing import overload, Union
+@overload
+def f(x: int) -> int: ...
+@overload
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+
+
+[case testDeprecatedChangeOverloadedFunctionDeprecationIndirectImport]
+# flags: --enable-error-code=deprecated
+
+from b import f
+f(1)
+f("y")
+import b
+b.f(1)
+b.f("y")
+
+[file b.py]
+from a import f
+
+[file a.py]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int, please")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please
+
+
+[case testDeprecatedRemoveOverloadedFunctionDeprecationIndirectImport]
+# flags: --enable-error-code=deprecated
+
+from b import f
+f(1)
+f("y")
+import b
+b.f(1)
+b.f("y")
+
+[file b.py]
+from a import f
+
+[file a.py]
+from typing import overload, Union
+from typing_extensions import deprecated
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("pass int")
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import overload, Union
+@overload
+def f(x: int) -> int: ...
+@overload
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int
+==
+
+
+[case testDeprecatedOverloadedFunctionAlreadyDecorated]
+# flags: --enable-error-code=deprecated
+
+from b import f
+f(1)
+f("y")
+import b
+b.f(1)
+b.f("y")
+
+[file b.py]
+from a import f
+
+[file a.py]
+from typing import Callable, overload, Union
+
+def d(t: Callable[[str], str]) -> Callable[[str], str]: ...
+
+@overload
+def f(x: int) -> int: ...
+@overload
+@d
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[file a.py.2]
+from typing import Callable, overload, Union
+from typing_extensions import deprecated
+
+def d(t: Callable[[str], str]) -> Callable[[str], str]: ...
+
+@overload
+def f(x: int) -> int: ...
+@overload
+@deprecated("deprecated decorated overload")
+@d
+def f(x: str) -> str: ...
+def f(x: Union[int, str]) -> Union[int, str]: ...
+
+[builtins fixtures/tuple.pyi]
+[out]
+==
+main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: deprecated decorated overload
+main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: deprecated decorated overload
+
+
 [case testDeprecatedChangeClassDeprecationIndirectImport]
 # flags: --enable-error-code=deprecated
 from b import C

From 04f38f5862547a3f695da0dc6a8843de5370574e Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 8 Aug 2025 20:01:59 +0100
Subject: [PATCH 1532/1617] Fix crash on settable property alias (#19615)

Fixes https://github.com/python/mypy/issues/19572

Surprisingly, when working on this I found another inconsistency in how
`.definiton` is set. So after all I decided to do some cleanup, now
`.definiton` should always point do the `Decortor` if the definition is
a decorated function (no matter whether it is a trivial decorator like
`@abstractmethod` or `@overload` or a "real" one).

---------

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypy/checker.py                       | 27 ++++++--
 mypy/checkmember.py                   | 11 ++-
 mypy/fixup.py                         |  8 +--
 mypy/messages.py                      | 22 +++---
 mypy/nodes.py                         |  7 ++
 mypy/semanal.py                       |  2 +
 mypy/types.py                         |  3 +
 test-data/unit/check-classes.test     | 47 +++++++++++++
 test-data/unit/check-dataclasses.test |  2 +-
 test-data/unit/fine-grained.test      | 98 +--------------------------
 test-data/unit/pythoneval.test        |  1 +
 11 files changed, 111 insertions(+), 117 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 3b94b84bb975..6176df84c225 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -130,6 +130,7 @@
     WhileStmt,
     WithStmt,
     YieldExpr,
+    get_func_def,
     is_final_node,
 )
 from mypy.operators import flip_ops, int_op_to_method, neg_ops
@@ -703,6 +704,12 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     # TODO: keep precise type for callables with tricky but valid signatures.
                     setter_type = fallback_setter_type
                 defn.items[0].var.setter_type = setter_type
+                if isinstance(defn.type, Overloaded):
+                    # Update legacy property type for decorated properties.
+                    getter_type = self.extract_callable_type(defn.items[0].var.type, defn)
+                    if getter_type is not None:
+                        getter_type.definition = defn.items[0]
+                        defn.type.items[0] = getter_type
         for i, fdef in enumerate(defn.items):
             assert isinstance(fdef, Decorator)
             if defn.is_property:
@@ -730,7 +737,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     assert isinstance(item, Decorator)
                     item_type = self.extract_callable_type(item.var.type, item)
                     if item_type is not None:
-                        item_type.definition = item.func
+                        item_type.definition = item
                         item_types.append(item_type)
                 if item_types:
                     defn.type = Overloaded(item_types)
@@ -2501,8 +2508,9 @@ def check_override(
 
                 override_ids = override.type_var_ids()
                 type_name = None
-                if isinstance(override.definition, FuncDef):
-                    type_name = override.definition.info.name
+                definition = get_func_def(override)
+                if isinstance(definition, FuncDef):
+                    type_name = definition.info.name
 
                 def erase_override(t: Type) -> Type:
                     return erase_typevars(t, ids_to_erase=override_ids)
@@ -3509,6 +3517,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                     continue
 
                 base_type, base_node = self.node_type_from_base(lvalue_node.name, base, lvalue)
+                # TODO: if the r.h.s. is a descriptor, we should check setter override as well.
                 custom_setter = is_custom_settable_property(base_node)
                 if isinstance(base_type, PartialType):
                     base_type = None
@@ -4494,6 +4503,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
                     if isinstance(p_type, Overloaded):
                         # TODO: in theory we can have a property with a deleter only.
                         var.is_settable_property = True
+                        assert isinstance(definition, Decorator), definition
+                        var.setter_type = definition.var.setter_type
 
     def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         """Store best known type for variable if type inference failed.
@@ -5356,6 +5367,8 @@ def visit_decorator_inner(
             self.check_untyped_after_decorator(sig, e.func)
         self.require_correct_self_argument(sig, e.func)
         sig = set_callable_name(sig, e.func)
+        if isinstance(sig, CallableType):
+            sig.definition = e
         e.var.type = sig
         e.var.is_ready = True
         if e.func.is_property:
@@ -8654,8 +8667,10 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
         return t.copy_modified(args=[a.accept(self) for a in t.args])
 
 
-def is_classmethod_node(node: Node | None) -> bool | None:
+def is_classmethod_node(node: SymbolNode | None) -> bool | None:
     """Find out if a node describes a classmethod."""
+    if isinstance(node, Decorator):
+        node = node.func
     if isinstance(node, FuncDef):
         return node.is_class
     if isinstance(node, Var):
@@ -8663,8 +8678,10 @@ def is_classmethod_node(node: Node | None) -> bool | None:
     return None
 
 
-def is_node_static(node: Node | None) -> bool | None:
+def is_node_static(node: SymbolNode | None) -> bool | None:
     """Find out if a node describes a static function method."""
+    if isinstance(node, Decorator):
+        node = node.func
     if isinstance(node, FuncDef):
         return node.is_static
     if isinstance(node, Var):
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index da67591a4553..2c41f2e273cc 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -976,8 +976,15 @@ def expand_and_bind_callable(
     freeze_all_type_vars(expanded)
     if not var.is_property:
         return expanded
-    # TODO: a decorated property can result in Overloaded here.
-    assert isinstance(expanded, CallableType)
+    if isinstance(expanded, Overloaded):
+        # Legacy way to store settable properties is with overloads. Also in case it is
+        # an actual overloaded property, selecting first item that passed check_self_arg()
+        # is a good approximation, long-term we should use check_call() inference below.
+        if not expanded.items:
+            # A broken overload, error should be already reported.
+            return AnyType(TypeOfAny.from_error)
+        expanded = expanded.items[0]
+    assert isinstance(expanded, CallableType), expanded
     if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
         if expanded.variables:
             type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context)
diff --git a/mypy/fixup.py b/mypy/fixup.py
index 0007fe8faabf..18bdc1c6f497 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -181,8 +181,7 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
         if isinstance(o.type, Overloaded):
             # For error messages we link the original definition for each item.
             for typ, item in zip(o.type.items, o.items):
-                if isinstance(item, Decorator):
-                    typ.definition = item.func
+                typ.definition = item
 
     def visit_decorator(self, d: Decorator) -> None:
         if self.current_info is not None:
@@ -193,8 +192,9 @@ def visit_decorator(self, d: Decorator) -> None:
             d.var.accept(self)
         for node in d.decorators:
             node.accept(self)
-        if isinstance(d.var.type, ProperType) and isinstance(d.var.type, CallableType):
-            d.var.type.definition = d.func
+        typ = d.var.type
+        if isinstance(typ, ProperType) and isinstance(typ, CallableType):
+            typ.definition = d.func
 
     def visit_class_def(self, c: ClassDef) -> None:
         for v in c.type_vars:
diff --git a/mypy/messages.py b/mypy/messages.py
index 6b55da59d183..f626d4c71916 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -55,6 +55,7 @@
     SymbolTable,
     TypeInfo,
     Var,
+    get_func_def,
     reverse_builtin_aliases,
 )
 from mypy.operators import op_methods, op_methods_to_symbols
@@ -2938,10 +2939,11 @@ def format_single(arg: Type) -> str:
 
 def pretty_class_or_static_decorator(tp: CallableType) -> str | None:
     """Return @classmethod or @staticmethod, if any, for the given callable type."""
-    if tp.definition is not None and isinstance(tp.definition, SYMBOL_FUNCBASE_TYPES):
-        if tp.definition.is_class:
+    definition = get_func_def(tp)
+    if definition is not None and isinstance(definition, SYMBOL_FUNCBASE_TYPES):
+        if definition.is_class:
             return "@classmethod"
-        if tp.definition.is_static:
+        if definition.is_static:
             return "@staticmethod"
     return None
 
@@ -2991,12 +2993,13 @@ def [T <: int] f(self, x: int, y: T) -> None
             slash = True
 
     # If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
+    definition = get_func_def(tp)
     if (
-        isinstance(tp.definition, FuncDef)
-        and hasattr(tp.definition, "arguments")
+        isinstance(definition, FuncDef)
+        and hasattr(definition, "arguments")
         and not tp.from_concatenate
     ):
-        definition_arg_names = [arg.variable.name for arg in tp.definition.arguments]
+        definition_arg_names = [arg.variable.name for arg in definition.arguments]
         if (
             len(definition_arg_names) > len(tp.arg_names)
             and definition_arg_names[0]
@@ -3005,7 +3008,7 @@ def [T <: int] f(self, x: int, y: T) -> None
             if s:
                 s = ", " + s
             s = definition_arg_names[0] + s
-        s = f"{tp.definition.name}({s})"
+        s = f"{definition.name}({s})"
     elif tp.name:
         first_arg = get_first_arg(tp)
         if first_arg:
@@ -3051,9 +3054,10 @@ def [T <: int] f(self, x: int, y: T) -> None
 
 
 def get_first_arg(tp: CallableType) -> str | None:
-    if not isinstance(tp.definition, FuncDef) or not tp.definition.info or tp.definition.is_static:
+    definition = get_func_def(tp)
+    if not isinstance(definition, FuncDef) or not definition.info or definition.is_static:
         return None
-    return tp.definition.original_first_arg
+    return definition.original_first_arg
 
 
 def variance_string(variance: int) -> str:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 9d5867c5371d..99b9bf72c948 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -4380,6 +4380,13 @@ def is_final_node(node: SymbolNode | None) -> bool:
     return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final
 
 
+def get_func_def(typ: mypy.types.CallableType) -> SymbolNode | None:
+    definition = typ.definition
+    if isinstance(definition, Decorator):
+        definition = definition.func
+    return definition
+
+
 def local_definitions(
     names: SymbolTable, name_prefix: str, info: TypeInfo | None = None
 ) -> Iterator[Definition]:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 99d1eb36e788..fb66fb5158db 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1243,6 +1243,7 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
             bare_setter_type = self.analyze_property_with_multi_part_definition(defn)
             typ = function_type(first_item.func, self.named_type("builtins.function"))
             assert isinstance(typ, CallableType)
+            typ.definition = first_item
             types = [typ]
         else:
             # This is a normal overload. Find the item signatures, the
@@ -1374,6 +1375,7 @@ def analyze_overload_sigs_and_impl(
             if isinstance(item, Decorator):
                 callable = function_type(item.func, self.named_type("builtins.function"))
                 assert isinstance(callable, CallableType)
+                callable.definition = item
                 if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators):
                     if i == len(defn.items) - 1 and not self.is_stub_file:
                         # Last item outside a stub is impl
diff --git a/mypy/types.py b/mypy/types.py
index b4771b15f77a..a73ac3c3524a 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1887,6 +1887,9 @@ def __init__(
         self.fallback = fallback
         assert not name or " __main__.CGI"
 reveal_type(CGT)  # N: Revealed type is "def [T] () -> __main__.CGT[T`1]"
+
+[case testSettablePropertyAlias]
+from typing import Any, TypeVar
+
+class A:
+    @property
+    def prop(self: Any) -> str: ...
+    @prop.setter
+    def prop(self, val: str) -> None: ...
+
+T = TypeVar("T")
+class AT:
+    @property
+    def prop(self: T) -> T: ...
+    @prop.setter
+    def prop(self: T, val: list[T]) -> None: ...
+
+class B:
+    prop: str
+    prop_t: str
+
+class C(B):
+    prop = A.prop
+    prop_t = AT.prop  # E: Incompatible types in assignment (expression has type "C", base class "B" defined the type as "str")
+
+reveal_type(C().prop)  # N: Revealed type is "builtins.str"
+C().prop = "no"  # E: Invalid self argument "C" to attribute function "prop" with type "Callable[[A, str], None]"
+reveal_type(C().prop_t)  # N: Revealed type is "__main__.C"
+C().prop_t = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "list[C]")
+[builtins fixtures/property.pyi]
+
+[case testClassEqDecoratedAbstractNote]
+from abc import abstractmethod
+
+class C:
+    @abstractmethod
+    def __eq__(self, other: C) -> bool: ...
+[builtins fixtures/plugin_attrs.pyi]
+[out]
+main:5: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object"
+main:5: note: This violates the Liskov substitution principle
+main:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
+main:5: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
+main:5: note:     def __eq__(self, other: object) -> bool:
+main:5: note:         if not isinstance(other, C):
+main:5: note:             return NotImplemented
+main:5: note:         return 
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index a6ac30e20c36..f43c49c200c8 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -2055,7 +2055,7 @@ from dataclasses import dataclass, replace, InitVar
 from typing import ClassVar
 
 @dataclass
-class A:
+class A:  # N: "replace" of "A" defined here
     x: int
     q: InitVar[int]
     q2: InitVar[int] = 0
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index 0d10559d0692..888b7bc7e97f 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -7312,9 +7312,7 @@ class C:
 ==
 mod.py:9: error: Incompatible types in assignment (expression has type "int", variable has type "str")
 
-[case testOverloadedMethodSupertype-only_when_cache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
+[case testOverloadedMethodSupertype]
 from typing import overload, Any
 import b
 class Child(b.Parent):
@@ -7355,49 +7353,6 @@ main:4: note:          def f(self, arg: int) -> int
 main:4: note:          @overload
 main:4: note:          def f(self, arg: str) -> str
 
-[case testOverloadedMethodSupertype2-only_when_nocache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
-from typing import overload, Any
-import b
-class Child(b.Parent):
-    @overload                           # Fail
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> str: ...
-    def f(self, arg: Any) -> Any: ...
-[file b.py]
-from typing import overload, Any
-class C: pass
-class Parent:
-    @overload
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> str: ...
-    def f(self, arg: Any) -> Any: ...
-[file b.py.2]
-from typing import overload, Any
-class C: pass
-class Parent:
-    @overload
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> C: ...
-    def f(self, arg: Any) -> Any: ...
-[out]
-==
-main:4: error: Signature of "f" incompatible with supertype "b.Parent"
-main:4: note:      Superclass:
-main:4: note:          @overload
-main:4: note:          def f(self, arg: int) -> int
-main:4: note:          @overload
-main:4: note:          def f(self, arg: str) -> C
-main:4: note:      Subclass:
-main:4: note:          @overload
-main:4: note:          def f(arg: int) -> int
-main:4: note:          @overload
-main:4: note:          def f(arg: str) -> str
-
 [case testOverloadedInitSupertype]
 import a
 [file a.py]
@@ -8486,9 +8441,7 @@ class D:
 ==
 a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
 
-[case testFinalBodyReprocessedAndStillFinalOverloaded-only_when_cache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
+[case testFinalBodyReprocessedAndStillFinalOverloaded]
 import a
 [file a.py]
 from c import C
@@ -8533,53 +8486,6 @@ a.py:3: note:          def meth(self, x: str) -> str
 a.py:3: note:      Subclass:
 a.py:3: note:          def meth(self) -> None
 
-[case testFinalBodyReprocessedAndStillFinalOverloaded2-only_when_nocache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
-import a
-[file a.py]
-from c import C
-class A:
-    def meth(self) -> None: ...
-
-[file a.py.3]
-from c import C
-class A(C):
-    def meth(self) -> None: ...
-
-[file c.py]
-from typing import final, overload, Union
-from d import D
-
-class C:
-    @overload
-    def meth(self, x: int) -> int: ...
-    @overload
-    def meth(self, x: str) -> str: ...
-    @final
-    def meth(self, x: Union[int, str]) -> Union[int, str]:
-        D(int())
-        return x
-[file d.py]
-class D:
-    def __init__(self, x: int) -> None: ...
-[file d.py.2]
-from typing import Optional
-class D:
-    def __init__(self, x: Optional[int]) -> None: ...
-[out]
-==
-==
-a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
-a.py:3: error: Signature of "meth" incompatible with supertype "c.C"
-a.py:3: note:      Superclass:
-a.py:3: note:          @overload
-a.py:3: note:          def meth(x: int) -> int
-a.py:3: note:          @overload
-a.py:3: note:          def meth(x: str) -> str
-a.py:3: note:      Subclass:
-a.py:3: note:          def meth(self) -> None
-
 [case testIfMypyUnreachableClass]
 from a import x
 
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 4bd94dfce03e..9b5d8a1ac54c 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1970,6 +1970,7 @@ a2 = replace()
 a2 = replace(a, x='spam')
 a2 = replace(a, x=42, q=42)
 [out]
+_testDataclassReplace.py:4: note: "replace" of "A" defined here
 _testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A"
 _testDataclassReplace.py:10: error: Too few arguments for "replace"
 _testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"

From adacbbf563e2768cac40ed2f962caca5619c5518 Mon Sep 17 00:00:00 2001
From: Emma Smith 
Date: Fri, 8 Aug 2025 17:27:32 -0700
Subject: [PATCH 1533/1617] [mypyc] Provide instructions for resolving missing
 test module on Windows (#19579)

Python doesn't come with the test module by default on Windows if installed through pymanager. Since `test.support.EqualToForwardRef` is used as part of the mypyc run tests, we should provide a helpful error message when the test fails due to the missing `test` module.

Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
---
 mypyc/test-data/run-tuples.test | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index ea0a1cb8d852..5d9485288cfb 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -131,10 +131,23 @@ import sys
 from typing import Optional
 from native import ClassIR, FuncIR, Record
 
+HAVE_TEST = False
 if sys.version_info >= (3, 14):
-    from test.support import EqualToForwardRef
-    type_forward_ref = EqualToForwardRef
-else:
+    try:
+        from test.support import EqualToForwardRef
+        type_forward_ref = EqualToForwardRef
+        HAVE_TEST = True
+    except ImportError as e:
+        # catch the case of a pymanager installed Python
+        # without the test module. It is excluded by default
+        # on Windows.
+        msg = 'Missing "test" module.'
+        if sys.platform == "win32":
+            msg += (' Please install a version of Python with the test module.'
+                   ' If you are using pymanager, try running pymanager install --force PythonTest\\')
+        raise ImportError(msg) from e
+
+if not HAVE_TEST:
     from typing import ForwardRef
     type_forward_ref = ForwardRef
 

From 186515f66fc47d8afc0c9d43482265f295c83a08 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Fri, 8 Aug 2025 21:16:01 -0700
Subject: [PATCH 1534/1617] Further fix overload diagnostic for vararg and
 varkwarg (#19619)

#19614 got merged faster than I expected :-)
---
 mypy/typeops.py                       | 10 ++++------
 test-data/unit/check-overloading.test | 17 +++++++++++++++++
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/mypy/typeops.py b/mypy/typeops.py
index 866eb72ede3e..88b3c5da48ce 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -521,18 +521,16 @@ def callable_corresponding_argument(
 
         # def right(a: int = ...) -> None: ...
         # def left(__a: int = ..., *, a: int = ...) -> None: ...
-        from mypy.subtypes import is_subtype
+        from mypy.meet import meet_types
 
         if (
             not (by_name.required or by_pos.required)
             and by_pos.name is None
             and by_name.pos is None
         ):
-            # We actually want the intersection of by_name.typ and by_pos.typ
-            if is_subtype(by_name.typ, by_pos.typ):
-                return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False)
-            if is_subtype(by_pos.typ, by_name.typ):
-                return FormalArgument(by_name.name, by_pos.pos, by_pos.typ, False)
+            return FormalArgument(
+                by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False
+            )
     return by_name if by_name is not None else by_pos
 
 
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
index 560d4a5c12fc..be55a182b87b 100644
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -246,6 +246,23 @@ def foo(*args: int | str, **kw: int) -> None:
     pass
 [builtins fixtures/tuple.pyi]
 
+[case testTypeCheckOverloadImplOverlapVarArgsAndKwargsUnion]
+from __future__ import annotations
+from typing import overload
+
+class Foo: ...
+
+@overload
+def foo(x: int) -> None: ...
+@overload
+def foo(*, x: Foo) -> None: ...
+@overload
+def foo(a: str, /) -> None: ...
+
+def foo(*args: int | str, **kw: int | Foo) -> None:
+    pass
+[builtins fixtures/tuple.pyi]
+
 [case testTypeCheckOverloadWithImplTooSpecificRetType]
 from typing import overload, Any
 

From cc5f1e1b9763d712d5db7b89c40e20d881407361 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 9 Aug 2025 09:09:21 +0100
Subject: [PATCH 1535/1617] Cache common instances (#19621)

These few types account for a significant proportion of all types
created:
* `str` is just everywhere
* `object` and `function` are used as fallbacks in many places
* `int` and `bool` are coming from various literals

This gives around 1.5% performance improvement on my desktop. This is a
bit ugly, but also looks like an easy win. Note that during semantic
analysis I am caching types more conservatively, just in case some
plugins modify them in place (`named_type()` is a part of semantic
analyzer plugin interface).
---
 mypy/checker.py   | 30 ++++++++++++++++++++++++++++++
 mypy/checkexpr.py | 21 ++++++++++++++++-----
 mypy/semanal.py   | 25 ++++++++++++++++++-------
 3 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 6176df84c225..32ef3701df9e 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -431,6 +431,13 @@ def __init__(
         self._expr_checker = mypy.checkexpr.ExpressionChecker(
             self, self.msg, self.plugin, per_line_checking_time_ns
         )
+
+        self._str_type: Instance | None = None
+        self._function_type: Instance | None = None
+        self._int_type: Instance | None = None
+        self._bool_type: Instance | None = None
+        self._object_type: Instance | None = None
+
         self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options)
         self._unique_id = 0
 
@@ -7369,6 +7376,29 @@ def named_type(self, name: str) -> Instance:
 
         For example, named_type('builtins.object') produces the 'object' type.
         """
+        if name == "builtins.str":
+            if self._str_type is None:
+                self._str_type = self._named_type(name)
+            return self._str_type
+        if name == "builtins.function":
+            if self._function_type is None:
+                self._function_type = self._named_type(name)
+            return self._function_type
+        if name == "builtins.int":
+            if self._int_type is None:
+                self._int_type = self._named_type(name)
+            return self._int_type
+        if name == "builtins.bool":
+            if self._bool_type is None:
+                self._bool_type = self._named_type(name)
+            return self._bool_type
+        if name == "builtins.object":
+            if self._object_type is None:
+                self._object_type = self._named_type(name)
+            return self._object_type
+        return self._named_type(name)
+
+    def _named_type(self, name: str) -> Instance:
         # Assume that the name refers to a type.
         sym = self.lookup_qualified(name)
         node = sym.node
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 6e0915179f90..04ea678a2736 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -360,6 +360,9 @@ def __init__(
         ] = {}
         self.in_lambda_expr = False
 
+        self._literal_true: Instance | None = None
+        self._literal_false: Instance | None = None
+
     def reset(self) -> None:
         self.resolved_type = {}
         self.expr_cache.clear()
@@ -3428,11 +3431,19 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty
         if self.is_literal_context():
             return LiteralType(value=value, fallback=typ)
         else:
-            return typ.copy_modified(
-                last_known_value=LiteralType(
-                    value=value, fallback=typ, line=typ.line, column=typ.column
-                )
-            )
+            if value is True:
+                if self._literal_true is None:
+                    self._literal_true = typ.copy_modified(
+                        last_known_value=LiteralType(value=value, fallback=typ)
+                    )
+                return self._literal_true
+            if value is False:
+                if self._literal_false is None:
+                    self._literal_false = typ.copy_modified(
+                        last_known_value=LiteralType(value=value, fallback=typ)
+                    )
+                return self._literal_false
+            return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ))
 
     def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType:
         """Concatenate two fixed length tuples."""
diff --git a/mypy/semanal.py b/mypy/semanal.py
index fb66fb5158db..dfa210234606 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -497,6 +497,10 @@ def __init__(
         # Used to track edge case when return is still inside except* if it enters a loop
         self.return_stmt_inside_except_star_block: bool = False
 
+        self._str_type: Instance | None = None
+        self._function_type: Instance | None = None
+        self._object_type: Instance | None = None
+
     # mypyc doesn't properly handle implementing an abstractproperty
     # with a regular attribute so we make them properties
     @property
@@ -1241,7 +1245,7 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
             # This is a property.
             first_item.func.is_overload = True
             bare_setter_type = self.analyze_property_with_multi_part_definition(defn)
-            typ = function_type(first_item.func, self.named_type("builtins.function"))
+            typ = function_type(first_item.func, self.function_type())
             assert isinstance(typ, CallableType)
             typ.definition = first_item
             types = [typ]
@@ -1373,7 +1377,7 @@ def analyze_overload_sigs_and_impl(
                     item.accept(self)
             # TODO: support decorated overloaded functions properly
             if isinstance(item, Decorator):
-                callable = function_type(item.func, self.named_type("builtins.function"))
+                callable = function_type(item.func, self.function_type())
                 assert isinstance(callable, CallableType)
                 callable.definition = item
                 if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators):
@@ -1536,9 +1540,7 @@ def analyze_property_with_multi_part_definition(
                         if first_node.name == "setter":
                             # The first item represents the entire property.
                             first_item.var.is_settable_property = True
-                            setter_func_type = function_type(
-                                item.func, self.named_type("builtins.function")
-                            )
+                            setter_func_type = function_type(item.func, self.function_type())
                             assert isinstance(setter_func_type, CallableType)
                             bare_setter_type = setter_func_type
                             defn.setter_index = i + 1
@@ -6630,10 +6632,19 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non
             return result
 
     def object_type(self) -> Instance:
-        return self.named_type("builtins.object")
+        if self._object_type is None:
+            self._object_type = self.named_type("builtins.object")
+        return self._object_type
 
     def str_type(self) -> Instance:
-        return self.named_type("builtins.str")
+        if self._str_type is None:
+            self._str_type = self.named_type("builtins.str")
+        return self._str_type
+
+    def function_type(self) -> Instance:
+        if self._function_type is None:
+            self._function_type = self.named_type("builtins.function")
+        return self._function_type
 
     def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
         sym = self.lookup_fully_qualified(fullname)

From 660d911223da3516c496e3a0cfdefa3eaf982290 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 9 Aug 2025 19:47:25 +0100
Subject: [PATCH 1536/1617] Two more micro-optimizations (#19627)

This has two things (totalling 1.5% locally, but see caveat below):
* Do not use `@contextmanger` (that is relatively slow) for
`local_type_map`, since it appears in multiple hot paths.
* Do not show name suggestions for import errors in third party packages
(since those errors are ignored anyway). It calls `difflib` that can be
extremely slow with large modules.

Btw the second will probably not affect self-check, although it did
affect _my_ self-check, since apparently `pytest` depends on `numpy`.
Well, they don't specify it as a package dependency, but
https://github.com/pytest-dev/pytest/blob/main/src/_pytest/python_api.py#L17-L18
```python
if TYPE_CHECKING:
    from numpy import ndarray
```
(and I have numpy installed in all my environments, LOL)
---
 mypy/checker.py   | 48 +++++++++++++++++++++++++++++++++--------------
 mypy/checkexpr.py | 15 ++++++---------
 mypy/semanal.py   |  4 +++-
 3 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 32ef3701df9e..206abae6adec 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6,7 +6,18 @@
 from collections import defaultdict
 from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet
 from contextlib import ExitStack, contextmanager
-from typing import Callable, Final, Generic, NamedTuple, Optional, TypeVar, Union, cast, overload
+from typing import (
+    Callable,
+    Final,
+    Generic,
+    Literal,
+    NamedTuple,
+    Optional,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
 from typing_extensions import TypeAlias as _TypeAlias, TypeGuard
 
 import mypy.checkexpr
@@ -277,6 +288,26 @@ class PartialTypeScope(NamedTuple):
     is_local: bool
 
 
+class LocalTypeMap:
+    """Store inferred types into a temporary type map (returned).
+
+    This can be used to perform type checking "experiments" without
+    affecting exported types (which are used by mypyc).
+    """
+
+    def __init__(self, chk: TypeChecker) -> None:
+        self.chk = chk
+
+    def __enter__(self) -> dict[Expression, Type]:
+        temp_type_map: dict[Expression, Type] = {}
+        self.chk._type_maps.append(temp_type_map)
+        return temp_type_map
+
+    def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> Literal[False]:
+        self.chk._type_maps.pop()
+        return False
+
+
 class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi):
     """Mypy type checker.
 
@@ -402,6 +433,7 @@ def __init__(
         self.is_typeshed_stub = tree.is_typeshed_file(options)
         self.inferred_attribute_types = None
         self.allow_constructor_cache = True
+        self.local_type_map = LocalTypeMap(self)
 
         # If True, process function definitions. If False, don't. This is used
         # for processing module top levels in fine-grained incremental mode.
@@ -4631,7 +4663,7 @@ def check_simple_assignment(
                 # may cause some perf impact, plus we want to partially preserve
                 # the old behavior. This helps with various practical examples, see
                 # e.g. testOptionalTypeNarrowedByGenericCall.
-                with self.msg.filter_errors() as local_errors, self.local_type_map() as type_map:
+                with self.msg.filter_errors() as local_errors, self.local_type_map as type_map:
                     alt_rvalue_type = self.expr_checker.accept(
                         rvalue, None, always_allow_any=always_allow_any
                     )
@@ -7458,18 +7490,6 @@ def lookup_type(self, node: Expression) -> Type:
     def store_types(self, d: dict[Expression, Type]) -> None:
         self._type_maps[-1].update(d)
 
-    @contextmanager
-    def local_type_map(self) -> Iterator[dict[Expression, Type]]:
-        """Store inferred types into a temporary type map (returned).
-
-        This can be used to perform type checking "experiments" without
-        affecting exported types (which are used by mypyc).
-        """
-        temp_type_map: dict[Expression, Type] = {}
-        self._type_maps.append(temp_type_map)
-        yield temp_type_map
-        self._type_maps.pop()
-
     def in_checked_function(self) -> bool:
         """Should we type-check the current function?
 
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 04ea678a2736..63f39b641602 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1075,7 +1075,7 @@ def check_typeddict_call_with_kwargs(
 
         # We don't show any errors, just infer types in a generic TypedDict type,
         # a custom error message will be given below, if there are errors.
-        with self.msg.filter_errors(), self.chk.local_type_map():
+        with self.msg.filter_errors(), self.chk.local_type_map:
             orig_ret_type, _ = self.check_callable_call(
                 infer_callee,
                 # We use first expression for each key to infer type variables of a generic
@@ -1440,7 +1440,7 @@ def is_generic_decorator_overload_call(
             return None
         if not isinstance(get_proper_type(callee_type.ret_type), CallableType):
             return None
-        with self.chk.local_type_map():
+        with self.chk.local_type_map:
             with self.msg.filter_errors():
                 arg_type = get_proper_type(self.accept(args[0], type_context=None))
         if isinstance(arg_type, Overloaded):
@@ -2920,7 +2920,7 @@ def infer_overload_return_type(
         for typ in plausible_targets:
             assert self.msg is self.chk.msg
             with self.msg.filter_errors() as w:
-                with self.chk.local_type_map() as m:
+                with self.chk.local_type_map as m:
                     ret_type, infer_type = self.check_call(
                         callee=typ,
                         args=args,
@@ -5367,7 +5367,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
                 return self.check_typeddict_literal_in_context(e, typeddict_contexts[0])
             # Multiple items union, check if at least one of them matches cleanly.
             for typeddict_context in typeddict_contexts:
-                with self.msg.filter_errors() as err, self.chk.local_type_map() as tmap:
+                with self.msg.filter_errors() as err, self.chk.local_type_map as tmap:
                     ret_type = self.check_typeddict_literal_in_context(e, typeddict_context)
                 if err.has_new_errors():
                     continue
@@ -6095,15 +6095,12 @@ def accept(
 
     def accept_maybe_cache(self, node: Expression, type_context: Type | None = None) -> Type:
         binder_version = self.chk.binder.version
-        # Micro-optimization: inline local_type_map() as it is somewhat slow in mypyc.
-        type_map: dict[Expression, Type] = {}
-        self.chk._type_maps.append(type_map)
         with self.msg.filter_errors(filter_errors=True, save_filtered_errors=True) as msg:
-            typ = node.accept(self)
+            with self.chk.local_type_map as type_map:
+                typ = node.accept(self)
         messages = msg.filtered_errors()
         if binder_version == self.chk.binder.version and not self.chk.current_node_deferred:
             self.expr_cache[(node, type_context)] = (binder_version, typ, messages, type_map)
-        self.chk._type_maps.pop()
         self.chk.store_types(type_map)
         self.msg.add_errors(messages)
         return typ
diff --git a/mypy/semanal.py b/mypy/semanal.py
index dfa210234606..bebabfd3233c 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -3048,7 +3048,9 @@ def report_missing_module_attribute(
                 message = (
                     f'Module "{import_id}" does not explicitly export attribute "{source_id}"'
                 )
-            else:
+            elif not (
+                self.options.ignore_errors or self.cur_mod_node.path in self.errors.ignored_files
+            ):
                 alternatives = set(module.names.keys()).difference({source_id})
                 matches = best_matches(source_id, alternatives, n=3)
                 if matches:

From 94eb6b75388e48f3beb0691f27d3d16cee149a93 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Sun, 10 Aug 2025 11:18:08 -0400
Subject: [PATCH 1537/1617] [mypyc] Fix seg fault due to heap type objects with
 static tp_doc (#19636)

See https://github.com/python/mypy/pull/19634#issuecomment-3172291620

Also took the opportunity to add `PyDoc_STR` to the static docstrings.
AFAIK this isn't strictly necessary, but it's better style and in theory
makes it possible to compile without docstrings if someone wanted to do
that.
---
 mypyc/codegen/emitclass.py          |  4 ++--
 mypyc/codegen/emitmodule.py         |  2 +-
 mypyc/lib-rt/misc_ops.c             | 15 +++++++++++++++
 mypyc/test-data/run-signatures.test | 16 ++++++++++++++++
 4 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index 0c2d470104d0..ecf8c37f83c9 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -375,7 +375,7 @@ def emit_line() -> None:
         flags.append("Py_TPFLAGS_MANAGED_DICT")
     fields["tp_flags"] = " | ".join(flags)
 
-    fields["tp_doc"] = native_class_doc_initializer(cl)
+    fields["tp_doc"] = f"PyDoc_STR({native_class_doc_initializer(cl)})"
 
     emitter.emit_line(f"static PyTypeObject {emitter.type_struct_name(cl)}_template_ = {{")
     emitter.emit_line("PyVarObject_HEAD_INIT(NULL, 0)")
@@ -925,7 +925,7 @@ def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None:
             flags.append("METH_CLASS")
 
         doc = native_function_doc_initializer(fn)
-        emitter.emit_line(" {}, {}}},".format(" | ".join(flags), doc))
+        emitter.emit_line(" {}, PyDoc_STR({})}},".format(" | ".join(flags), doc))
 
     # Provide a default __getstate__ and __setstate__
     if not cl.has_method("__setstate__") and not cl.has_method("__getstate__"):
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index de34ed9fc7da..1e49b1320b26 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -983,7 +983,7 @@ def emit_module_methods(
             emitter.emit_line(
                 (
                     '{{"{name}", (PyCFunction){prefix}{cname}, {flag} | METH_KEYWORDS, '
-                    "{doc} /* docstring */}},"
+                    "PyDoc_STR({doc}) /* docstring */}},"
                 ).format(
                     name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag, doc=doc
                 )
diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c
index 3787ea553037..0c9d7812ac6c 100644
--- a/mypyc/lib-rt/misc_ops.c
+++ b/mypyc/lib-rt/misc_ops.c
@@ -300,6 +300,21 @@ PyObject *CPyType_FromTemplate(PyObject *template,
 
     Py_XDECREF(dummy_class);
 
+    // Unlike the tp_doc slots of most other object, a heap type's tp_doc
+    // must be heap allocated.
+    if (template_->tp_doc) {
+        // Silently truncate the docstring if it contains a null byte
+        Py_ssize_t size = strlen(template_->tp_doc) + 1;
+        char *tp_doc = (char *)PyMem_Malloc(size);
+        if (tp_doc == NULL) {
+            PyErr_NoMemory();
+            goto error;
+        }
+
+        memcpy(tp_doc, template_->tp_doc, size);
+        t->ht_type.tp_doc = tp_doc;
+    }
+
 #if PY_MINOR_VERSION == 11
     // This is a hack. Python 3.11 doesn't include good public APIs to work with managed
     // dicts, which are the default for heap types. So we try to opt-out until Python 3.12.
diff --git a/mypyc/test-data/run-signatures.test b/mypyc/test-data/run-signatures.test
index a2de7076f5ef..0a9ea32f5357 100644
--- a/mypyc/test-data/run-signatures.test
+++ b/mypyc/test-data/run-signatures.test
@@ -184,6 +184,22 @@ for cls in [Empty, HasInit, InheritedInit]:
     assert getattr(cls, "__doc__") == ""
 assert getattr(HasInitBad, "__doc__") is None
 
+[case testSignaturesConstructorsNonExt]
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class NonExt:
+    def __init__(self, x) -> None: pass
+
+[file driver.py]
+import inspect
+from testutil import assertRaises
+from native import *
+
+# TODO: support constructor signatures for non-extension classes
+with assertRaises(ValueError, "no signature found for builtin"):
+    inspect.signature(NonExt)
+
 [case testSignaturesHistoricalPositionalOnly]
 import inspect
 

From a07abb64c46b43700c0f9a58cfeaa5c16cff5d93 Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Sun, 10 Aug 2025 17:19:07 +0200
Subject: [PATCH 1538/1617] PEP 702 (@deprecated): handle "combined" overloads
 (#19626)

This change is taken from #18682. The new code and the tests are
unmodified. I only had to remove two now unnecessary calls of
`warn_deprecated` which were introduced after opening #18682.
---
 mypy/checkexpr.py                    | 30 ++++++-----
 test-data/unit/check-deprecated.test | 76 ++++++++++++++++++++++++++--
 2 files changed, 89 insertions(+), 17 deletions(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 63f39b641602..9752a5e68638 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2722,6 +2722,7 @@ def check_overload_call(
         #         for example, when we have a fallback alternative that accepts an unrestricted
         #         typevar. See https://github.com/python/mypy/issues/4063 for related discussion.
         erased_targets: list[CallableType] | None = None
+        inferred_types: list[Type] | None = None
         unioned_result: tuple[Type, Type] | None = None
 
         # Determine whether we need to encourage union math. This should be generally safe,
@@ -2749,13 +2750,14 @@ def check_overload_call(
                 # Record if we succeeded. Next we need to see if maybe normal procedure
                 # gives a narrower type.
                 if unioned_return:
-                    returns, inferred_types = zip(*unioned_return)
+                    returns = [u[0] for u in unioned_return]
+                    inferred_types = [u[1] for u in unioned_return]
                     # Note that we use `combine_function_signatures` instead of just returning
                     # a union of inferred callables because for example a call
                     # Union[int -> int, str -> str](Union[int, str]) is invalid and
                     # we don't want to introduce internal inconsistencies.
                     unioned_result = (
-                        make_simplified_union(list(returns), context.line, context.column),
+                        make_simplified_union(returns, context.line, context.column),
                         self.combine_function_signatures(get_proper_types(inferred_types)),
                     )
 
@@ -2770,7 +2772,7 @@ def check_overload_call(
             object_type,
             context,
         )
-        # If any of checks succeed, stop early.
+        # If any of checks succeed, perform deprecation tests and stop early.
         if inferred_result is not None and unioned_result is not None:
             # Both unioned and direct checks succeeded, choose the more precise type.
             if (
@@ -2778,11 +2780,18 @@ def check_overload_call(
                 and not isinstance(get_proper_type(inferred_result[0]), AnyType)
                 and not none_type_var_overlap
             ):
-                return inferred_result
-            return unioned_result
-        elif unioned_result is not None:
+                unioned_result = None
+            else:
+                inferred_result = None
+        if unioned_result is not None:
+            if inferred_types is not None:
+                for inferred_type in inferred_types:
+                    if isinstance(c := get_proper_type(inferred_type), CallableType):
+                        self.chk.warn_deprecated(c.definition, context)
             return unioned_result
-        elif inferred_result is not None:
+        if inferred_result is not None:
+            if isinstance(c := get_proper_type(inferred_result[1]), CallableType):
+                self.chk.warn_deprecated(c.definition, context)
             return inferred_result
 
         # Step 4: Failure. At this point, we know there is no match. We fall back to trying
@@ -2936,8 +2945,6 @@ def infer_overload_return_type(
                 # check for ambiguity due to 'Any' below.
                 if not args_contain_any:
                     self.chk.store_types(m)
-                    if isinstance(infer_type, ProperType) and isinstance(infer_type, CallableType):
-                        self.chk.warn_deprecated(infer_type.definition, context)
                     return ret_type, infer_type
                 p_infer_type = get_proper_type(infer_type)
                 if isinstance(p_infer_type, CallableType):
@@ -2974,11 +2981,6 @@ def infer_overload_return_type(
         else:
             # Success! No ambiguity; return the first match.
             self.chk.store_types(type_maps[0])
-            inferred_callable = inferred_types[0]
-            if isinstance(inferred_callable, ProperType) and isinstance(
-                inferred_callable, CallableType
-            ):
-                self.chk.warn_deprecated(inferred_callable.definition, context)
             return return_types[0], inferred_types[0]
 
     def overload_erased_call_targets(
diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test
index e1173ac425ba..607e9d767956 100644
--- a/test-data/unit/check-deprecated.test
+++ b/test-data/unit/check-deprecated.test
@@ -671,9 +671,11 @@ C().g = "x"  # E: function __main__.C.g is deprecated: use g2 instead \
 [case testDeprecatedDescriptor]
 # flags: --enable-error-code=deprecated
 
-from typing import Any, Optional, Union, overload
+from typing import Any, Generic, Optional, overload, TypeVar, Union
 from typing_extensions import deprecated
 
+T = TypeVar("T")
+
 @deprecated("use E1 instead")
 class D1:
     def __get__(self, obj: Optional[C], objtype: Any) -> Union[D1, int]: ...
@@ -701,10 +703,19 @@ class D3:
     def __set__(self, obj: C, value: str) -> None: ...
     def __set__(self, obj: C, value: Union[int, str]) -> None: ...
 
+class D4(Generic[T]):
+    @overload
+    def __get__(self, obj: None, objtype: Any) -> T: ...
+    @overload
+    @deprecated("deprecated instance access")
+    def __get__(self, obj: C, objtype: Any) -> T: ...
+    def __get__(self, obj: Optional[C], objtype: Any) -> T: ...
+
 class C:
     d1 = D1()  # E: class __main__.D1 is deprecated: use E1 instead
     d2 = D2()
     d3 = D3()
+    d4 = D4[int]()
 
 c: C
 C.d1
@@ -719,15 +730,21 @@ C.d3  # E: overload def (self: __main__.D3, obj: None, objtype: Any) -> __main__
 c.d3  # E: overload def (self: __main__.D3, obj: __main__.C, objtype: Any) -> builtins.int of function __main__.D3.__get__ is deprecated: use E3.__get__ instead
 c.d3 = 1
 c.d3 = "x"  # E: overload def (self: __main__.D3, obj: __main__.C, value: builtins.str) of function __main__.D3.__set__ is deprecated: use E3.__set__ instead
+
+C.d4
+c.d4  # E: overload def (self: __main__.D4[T`1], obj: __main__.C, objtype: Any) -> T`1 of function __main__.D4.__get__ is deprecated: deprecated instance access
 [builtins fixtures/property.pyi]
 
 
 [case testDeprecatedOverloadedFunction]
 # flags: --enable-error-code=deprecated
 
-from typing import Union, overload
+from typing import Any, overload, Union
 from typing_extensions import deprecated
 
+int_or_str: Union[int, str]
+any: Any
+
 @overload
 def f(x: int) -> int: ...
 @overload
@@ -738,6 +755,8 @@ def f(x: Union[int, str]) -> Union[int, str]: ...
 f  # E: function __main__.f is deprecated: use f2 instead
 f(1)  # E: function __main__.f is deprecated: use f2 instead
 f("x")  # E: function __main__.f is deprecated: use f2 instead
+f(int_or_str)  # E: function __main__.f is deprecated: use f2 instead
+f(any)  # E: function __main__.f is deprecated: use f2 instead
 f(1.0)  # E: function __main__.f is deprecated: use f2 instead \
         # E: No overload variant of "f" matches argument type "float" \
         # N: Possible overload variants: \
@@ -754,6 +773,8 @@ def g(x: Union[int, str]) -> Union[int, str]: ...
 g
 g(1)  # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead
 g("x")
+g(int_or_str)  # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead
+g(any)
 g(1.0)  # E: No overload variant of "g" matches argument type "float" \
         # N: Possible overload variants: \
         # N:     def g(x: int) -> int \
@@ -769,13 +790,62 @@ def h(x: Union[int, str]) -> Union[int, str]: ...
 h
 h(1)
 h("x")  # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead
+h(int_or_str)  # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead
+h(any)
 h(1.0)  # E: No overload variant of "h" matches argument type "float" \
         # N: Possible overload variants: \
         # N:     def h(x: int) -> int \
         # N:     def h(x: str) -> str
 
-[builtins fixtures/tuple.pyi]
+@overload
+def i(x: int) -> int: ...
+@overload
+@deprecated("work with int instead")
+def i(x: str) -> str: ...
+@overload
+def i(x: Any) -> Any: ...
+def i(x: Union[int, str]) -> Union[int, str]: ...
 
+i
+i(1)
+i("x")  # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead
+i(int_or_str)  # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead
+i(any)
+i(1.0)
+
+@overload
+def j(x: int) -> int: ...
+@overload
+def j(x: str) -> str: ...
+@overload
+@deprecated("work with int or str instead")
+def j(x: Any) -> Any: ...
+def j(x: Union[int, str]) -> Union[int, str]: ...
+
+j
+j(1)
+j("x")
+j(int_or_str)
+j(any)
+j(1.0)  # E: overload def (x: Any) -> Any of function __main__.j is deprecated: work with int or str instead
+
+@overload
+@deprecated("work with str instead")
+def k(x: int) -> int: ...
+@overload
+def k(x: str) -> str: ...
+@overload
+@deprecated("work with str instead")
+def k(x: object) -> Any: ...
+def k(x: object) -> Union[int, str]: ...
+
+k
+k(1)  # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead
+k("x")
+k(int_or_str)  # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead
+k(any)
+k(1.0)  # E: overload def (x: builtins.object) -> Any of function __main__.k is deprecated: work with str instead
+[builtins fixtures/tuple.pyi]
 
 [case testDeprecatedImportedOverloadedFunction]
 # flags: --enable-error-code=deprecated

From 6d0ce5eb7ffa3322bd9a63708682a9805c7996e1 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Mon, 11 Aug 2025 14:32:22 +0200
Subject: [PATCH 1539/1617] [mypyc] Add prefix to attributes of generator
 classes (#19535)

Fixes https://github.com/mypyc/mypyc/issues/1120

`async` functions were recently changed to be represented as generator
classes in the IR. Their parameters are represented as attributes in
those classes.

There are several methods added to every generator class by the
compiler, one of those is called `send`. If there is also a parameter
called `send`, mypyc assumes that the method is a property getter/setter
and inserts method calls in the generated code for `GetAttr` and
`SetAttr` nodes in the IR.

This is incorrect and led to the compilation error in the linked issue
because the `send` method of generator classes takes one more argument
than a property getter would.

The name clash is fixed by adding a prefix
`__mypyc_generator_attribute__` to attribute names derived from
parameters, which should ensure that argument references are not
converted into method calls.
---
 mypyc/codegen/emitfunc.py           |  7 ++--
 mypyc/common.py                     |  1 +
 mypyc/irbuild/builder.py            | 11 +++++--
 mypyc/irbuild/env_class.py          | 51 ++++++++++++++++++++---------
 mypyc/irbuild/generator.py          | 18 ++++++----
 mypyc/test-data/run-async.test      | 26 +++++++++++++++
 mypyc/test-data/run-generators.test | 36 ++++++++++++++++++++
 7 files changed, 122 insertions(+), 28 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 086be293d5b3..f00f2e700217 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -8,6 +8,7 @@
 from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer
 from mypyc.common import (
+    GENERATOR_ATTRIBUTE_PREFIX,
     HAVE_IMMORTAL,
     MODULE_PREFIX,
     NATIVE_PREFIX,
@@ -436,7 +437,9 @@ def visit_get_attr(self, op: GetAttr) -> None:
                     exc_class = "PyExc_AttributeError"
                     self.emitter.emit_line(
                         'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
-                            exc_class, repr(op.attr), repr(cl.name)
+                            exc_class,
+                            repr(op.attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX)),
+                            repr(cl.name),
                         )
                     )
 
@@ -938,7 +941,7 @@ def emit_attribute_error(self, op: Branch, class_name: str, attr: str) -> None:
                 self.source_path.replace("\\", "\\\\"),
                 op.traceback_entry[0],
                 class_name,
-                attr,
+                attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX),
                 op.traceback_entry[1],
                 globals_static,
             )
diff --git a/mypyc/common.py b/mypyc/common.py
index b5506eed89c2..3a77e9e60c35 100644
--- a/mypyc/common.py
+++ b/mypyc/common.py
@@ -22,6 +22,7 @@
 LAMBDA_NAME: Final = "__mypyc_lambda__"
 PROPSET_PREFIX: Final = "__mypyc_setter__"
 SELF_NAME: Final = "__mypyc_self__"
+GENERATOR_ATTRIBUTE_PREFIX: Final = "__mypyc_generator_attribute__"
 
 # Max short int we accept as a literal is based on 32-bit platforms,
 # so that we can just always emit the same code.
diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index ec3c1b1b1f3c..608c524b5d4f 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -59,7 +59,7 @@
 )
 from mypy.util import module_prefix, split_target
 from mypy.visitor import ExpressionVisitor, StatementVisitor
-from mypyc.common import BITMAP_BITS, SELF_NAME, TEMP_ATTR_NAME
+from mypyc.common import BITMAP_BITS, GENERATOR_ATTRIBUTE_PREFIX, SELF_NAME, TEMP_ATTR_NAME
 from mypyc.crash import catch_errors
 from mypyc.errors import Errors
 from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
@@ -651,7 +651,11 @@ def get_assignment_target(
                     # current environment.
                     if self.fn_info.is_generator:
                         return self.add_var_to_env_class(
-                            symbol, reg_type, self.fn_info.generator_class, reassign=False
+                            symbol,
+                            reg_type,
+                            self.fn_info.generator_class,
+                            reassign=False,
+                            prefix=GENERATOR_ATTRIBUTE_PREFIX,
                         )
 
                     # Otherwise define a new local variable.
@@ -1333,10 +1337,11 @@ def add_var_to_env_class(
         base: FuncInfo | ImplicitClass,
         reassign: bool = False,
         always_defined: bool = False,
+        prefix: str = "",
     ) -> AssignmentTarget:
         # First, define the variable name as an attribute of the environment class, and then
         # construct a target for that attribute.
-        name = remangle_redefinition_name(var.name)
+        name = prefix + remangle_redefinition_name(var.name)
         self.fn_info.env_class.attributes[name] = rtype
         if always_defined:
             self.fn_info.env_class.attrs_with_defaults.add(name)
diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py
index 51c854a4a2b2..2334b4370103 100644
--- a/mypyc/irbuild/env_class.py
+++ b/mypyc/irbuild/env_class.py
@@ -18,7 +18,13 @@ def g() -> int:
 from __future__ import annotations
 
 from mypy.nodes import Argument, FuncDef, SymbolNode, Var
-from mypyc.common import BITMAP_BITS, ENV_ATTR_NAME, SELF_NAME, bitmap_name
+from mypyc.common import (
+    BITMAP_BITS,
+    ENV_ATTR_NAME,
+    GENERATOR_ATTRIBUTE_PREFIX,
+    SELF_NAME,
+    bitmap_name,
+)
 from mypyc.ir.class_ir import ClassIR
 from mypyc.ir.ops import Call, GetAttr, SetAttr, Value
 from mypyc.ir.rtypes import RInstance, bitmap_rprimitive, object_rprimitive
@@ -60,7 +66,7 @@ class is generated, the function environment has not yet been
     return env_class
 
 
-def finalize_env_class(builder: IRBuilder) -> None:
+def finalize_env_class(builder: IRBuilder, prefix: str = "") -> None:
     """Generate, instantiate, and set up the environment of an environment class."""
     if not builder.fn_info.can_merge_generator_and_env_classes():
         instantiate_env_class(builder)
@@ -69,9 +75,9 @@ def finalize_env_class(builder: IRBuilder) -> None:
     # that were previously added to the environment with references to the function's
     # environment class.
     if builder.fn_info.is_nested:
-        add_args_to_env(builder, local=False, base=builder.fn_info.callable_class)
+        add_args_to_env(builder, local=False, base=builder.fn_info.callable_class, prefix=prefix)
     else:
-        add_args_to_env(builder, local=False, base=builder.fn_info)
+        add_args_to_env(builder, local=False, base=builder.fn_info, prefix=prefix)
 
 
 def instantiate_env_class(builder: IRBuilder) -> Value:
@@ -96,7 +102,7 @@ def instantiate_env_class(builder: IRBuilder) -> Value:
     return curr_env_reg
 
 
-def load_env_registers(builder: IRBuilder) -> None:
+def load_env_registers(builder: IRBuilder, prefix: str = "") -> None:
     """Load the registers for the current FuncItem being visited.
 
     Adds the arguments of the FuncItem to the environment. If the
@@ -104,7 +110,7 @@ def load_env_registers(builder: IRBuilder) -> None:
     loads all of the outer environments of the FuncItem into registers
     so that they can be used when accessing free variables.
     """
-    add_args_to_env(builder, local=True)
+    add_args_to_env(builder, local=True, prefix=prefix)
 
     fn_info = builder.fn_info
     fitem = fn_info.fitem
@@ -113,7 +119,7 @@ def load_env_registers(builder: IRBuilder) -> None:
         # If this is a FuncDef, then make sure to load the FuncDef into its own environment
         # class so that the function can be called recursively.
         if isinstance(fitem, FuncDef) and fn_info.add_nested_funcs_to_env:
-            setup_func_for_recursive_call(builder, fitem, fn_info.callable_class)
+            setup_func_for_recursive_call(builder, fitem, fn_info.callable_class, prefix=prefix)
 
 
 def load_outer_env(
@@ -134,8 +140,11 @@ def load_outer_env(
     assert isinstance(env.type, RInstance), f"{env} must be of type RInstance"
 
     for symbol, target in outer_env.items():
-        env.type.class_ir.attributes[symbol.name] = target.type
-        symbol_target = AssignmentTargetAttr(env, symbol.name)
+        attr_name = symbol.name
+        if isinstance(target, AssignmentTargetAttr):
+            attr_name = target.attr
+        env.type.class_ir.attributes[attr_name] = target.type
+        symbol_target = AssignmentTargetAttr(env, attr_name)
         builder.add_target(symbol, symbol_target)
 
     return env
@@ -178,6 +187,7 @@ def add_args_to_env(
     local: bool = True,
     base: FuncInfo | ImplicitClass | None = None,
     reassign: bool = True,
+    prefix: str = "",
 ) -> None:
     fn_info = builder.fn_info
     args = fn_info.fitem.arguments
@@ -193,10 +203,12 @@ def add_args_to_env(
             if is_free_variable(builder, arg.variable) or fn_info.is_generator:
                 rtype = builder.type_to_rtype(arg.variable.type)
                 assert base is not None, "base cannot be None for adding nonlocal args"
-                builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign)
+                builder.add_var_to_env_class(
+                    arg.variable, rtype, base, reassign=reassign, prefix=prefix
+                )
 
 
-def add_vars_to_env(builder: IRBuilder) -> None:
+def add_vars_to_env(builder: IRBuilder, prefix: str = "") -> None:
     """Add relevant local variables and nested functions to the environment class.
 
     Add all variables and functions that are declared/defined within current
@@ -216,7 +228,9 @@ def add_vars_to_env(builder: IRBuilder) -> None:
         for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name):
             if isinstance(var, Var):
                 rtype = builder.type_to_rtype(var.type)
-                builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False)
+                builder.add_var_to_env_class(
+                    var, rtype, env_for_func, reassign=False, prefix=prefix
+                )
 
     if builder.fn_info.fitem in builder.encapsulating_funcs:
         for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
@@ -226,12 +240,16 @@ def add_vars_to_env(builder: IRBuilder) -> None:
                 # the same name and signature across conditional blocks
                 # will generate different callable classes, so the callable
                 # class that gets instantiated must be generic.
+                if nested_fn.is_generator:
+                    prefix = GENERATOR_ATTRIBUTE_PREFIX
                 builder.add_var_to_env_class(
-                    nested_fn, object_rprimitive, env_for_func, reassign=False
+                    nested_fn, object_rprimitive, env_for_func, reassign=False, prefix=prefix
                 )
 
 
-def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: ImplicitClass) -> None:
+def setup_func_for_recursive_call(
+    builder: IRBuilder, fdef: FuncDef, base: ImplicitClass, prefix: str = ""
+) -> None:
     """Enable calling a nested function (with a callable class) recursively.
 
     Adds the instance of the callable class representing the given
@@ -241,7 +259,8 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli
     """
     # First, set the attribute of the environment class so that GetAttr can be called on it.
     prev_env = builder.fn_infos[-2].env_class
-    prev_env.attributes[fdef.name] = builder.type_to_rtype(fdef.type)
+    attr_name = prefix + fdef.name
+    prev_env.attributes[attr_name] = builder.type_to_rtype(fdef.type)
 
     if isinstance(base, GeneratorClass):
         # If we are dealing with a generator class, then we need to first get the register
@@ -253,7 +272,7 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli
 
     # Obtain the instance of the callable class representing the FuncDef, and add it to the
     # current environment.
-    val = builder.add(GetAttr(prev_env_reg, fdef.name, -1))
+    val = builder.add(GetAttr(prev_env_reg, attr_name, -1))
     target = builder.add_local_reg(fdef, object_rprimitive)
     builder.assign(target, val, -1)
 
diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py
index c858946f33c4..b3a417ed6a3e 100644
--- a/mypyc/irbuild/generator.py
+++ b/mypyc/irbuild/generator.py
@@ -13,7 +13,7 @@
 from typing import Callable
 
 from mypy.nodes import ARG_OPT, FuncDef, Var
-from mypyc.common import ENV_ATTR_NAME, NEXT_LABEL_ATTR_NAME
+from mypyc.common import ENV_ATTR_NAME, GENERATOR_ATTRIBUTE_PREFIX, NEXT_LABEL_ATTR_NAME
 from mypyc.ir.class_ir import ClassIR
 from mypyc.ir.func_ir import FuncDecl, FuncIR
 from mypyc.ir.ops import (
@@ -68,14 +68,14 @@ def gen_generator_func(
 ) -> tuple[FuncIR, Value | None]:
     """Generate IR for generator function that returns generator object."""
     setup_generator_class(builder)
-    load_env_registers(builder)
+    load_env_registers(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
     gen_arg_defaults(builder)
     if builder.fn_info.can_merge_generator_and_env_classes():
         gen = instantiate_generator_class(builder)
         builder.fn_info._curr_env_reg = gen
-        finalize_env_class(builder)
+        finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
     else:
-        finalize_env_class(builder)
+        finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
         gen = instantiate_generator_class(builder)
     builder.add(Return(gen))
 
@@ -104,11 +104,13 @@ class that implements the function (each function gets a separate class).
         and top_level
         and top_level.add_nested_funcs_to_env
     ):
-        setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class)
+        setup_func_for_recursive_call(
+            builder, fitem, builder.fn_info.generator_class, prefix=GENERATOR_ATTRIBUTE_PREFIX
+        )
     create_switch_for_generator_class(builder)
     add_raise_exception_blocks_to_generator_class(builder, fitem.line)
 
-    add_vars_to_env(builder)
+    add_vars_to_env(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
 
     builder.accept(fitem.body)
     builder.maybe_add_implicit_return()
@@ -429,7 +431,9 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None:
 
     # Add arguments from the original generator function to the
     # environment of the generator class.
-    add_args_to_env(builder, local=False, base=cls, reassign=False)
+    add_args_to_env(
+        builder, local=False, base=cls, reassign=False, prefix=GENERATOR_ATTRIBUTE_PREFIX
+    )
 
     # Set the next label register for the generator class.
     cls.next_label_reg = builder.read(cls.next_label_target, fitem.line)
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
index a1112e964671..55cde4ab44f1 100644
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -1208,3 +1208,29 @@ async def test_async_context_manager_exception_handling() -> None:
 
 [file asyncio/__init__.pyi]
 async def sleep(t: float) -> None: ...
+
+[case testCallableArgWithSameNameAsHelperMethod]
+import asyncio
+from typing import Awaitable, Callable
+
+
+MyCallable = Callable[[int, int], Awaitable[int]]
+
+async def add(a: int, b: int) -> int:
+    return a + b
+
+async def await_send(send: MyCallable) -> int:
+    return await send(1, 2)
+
+async def await_throw(throw: MyCallable) -> int:
+    return await throw(3, 4)
+
+async def tests() -> None:
+    assert await await_send(add) == 3
+    assert await await_throw(add) == 7
+
+def test_callable_arg_same_name_as_helper() -> None:
+    asyncio.run(tests())
+
+[file asyncio/__init__.pyi]
+def run(x: object) -> object: ...
diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test
index 3b4581f849e9..bfbd5b83696b 100644
--- a/mypyc/test-data/run-generators.test
+++ b/mypyc/test-data/run-generators.test
@@ -272,8 +272,17 @@ def call_nested_decorated(x: int) -> list[int]:
         a.append(x)
     return a
 
+def call_nested_recursive(x: int) -> Iterator:
+    def recursive(x: int) -> Iterator:
+        if x > 0:
+            yield from recursive(x - 1)
+        yield x
+
+    yield from recursive(x)
+
 def test_call_nested_generator_in_function() -> None:
     assert call_nested_decorated(5) == [5, 15]
+    assert list(call_nested_recursive(5)) == [0, 1, 2, 3, 4, 5]
 
 [case testYieldThrow]
 from typing import Generator, Iterable, Any, Union
@@ -871,3 +880,30 @@ def test_undefined_int_in_environment() -> None:
 
     with assertRaises(AttributeError):  # TODO: Should be UnboundLocalError
       list(gen2(False))
+
+[case testVariableWithSameNameAsHelperMethod]
+from testutil import assertRaises
+from typing import Iterator
+
+def gen_send() -> Iterator[int]:
+    send = 1
+    yield send + 1
+
+def gen_throw() -> Iterator[int]:
+    throw = 42
+    yield throw * 2
+
+def undefined() -> Iterator[int]:
+    if int():
+        send = 1
+    yield send + 1
+
+def test_same_names() -> None:
+    assert list(gen_send()) == [2]
+    assert list(gen_throw()) == [84]
+
+    with assertRaises(AttributeError, "attribute 'send' of 'undefined_gen' undefined"):
+        # TODO: Should be UnboundLocalError, this test verifies that the attribute name
+        # matches the variable name in the input code, since internally it's generated
+        # with a prefix.
+        list(undefined())

From 5a786075d8c366ee753c62fa36857589023ed561 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 11 Aug 2025 16:24:15 +0100
Subject: [PATCH 1540/1617] Another two micro-optimizations (#19633)

Here are two things:
* Make `FormalArgument` a native class. We create huge amount of these
(as callable subtyping is one of the most common subtype checks), and
named tuples creation is significantly slower than native classes.
* Do not call `re.match()` in a code path of `format_type()`. This is
relatively slow (as it is a `py_call()`) and it is called in almost
every error message. This creates problems for code with many
third-party dependencies where these errors are ignored anyway.

FWIW in total these give ~0.5% together (I didn't measure individually,
but I guess the most benefit for self-check is from the first one).

---------

Co-authored-by: Ali Hamdan 
---
 mypy/messages.py                 |  9 +++-----
 mypy/types.py                    | 37 +++++++++++++++++---------------
 test-data/unit/check-tuples.test |  2 +-
 3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/mypy/messages.py b/mypy/messages.py
index f626d4c71916..571cebb1b174 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -2432,13 +2432,13 @@ 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 "tuple[{}, {}, ... <{} more items>]".format(
+            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),
             )
         else:
-            return format_type_bare(typ, self.options)
+            return format_type(typ, self.options)
 
     def generate_incompatible_tuple_error(
         self,
@@ -2517,15 +2517,12 @@ def iteration_dependent_errors(self, iter_errors: IterationDependentErrors) -> N
 
 def quote_type_string(type_string: str) -> str:
     """Quotes a type representation for use in messages."""
-    no_quote_regex = r"^<(tuple|union): \d+ items>$"
     if (
         type_string in ["Module", "overloaded function", ""]
         or type_string.startswith("Module ")
-        or re.match(no_quote_regex, type_string) is not None
         or type_string.endswith("?")
     ):
-        # Messages are easier to read if these aren't quoted.  We use a
-        # regex to match strings with variable contents.
+        # These messages are easier to read if these aren't quoted.
         return type_string
     return f'"{type_string}"'
 
diff --git a/mypy/types.py b/mypy/types.py
index a73ac3c3524a..d7dd3e1f2dce 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -5,18 +5,7 @@
 import sys
 from abc import abstractmethod
 from collections.abc import Iterable, Sequence
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    ClassVar,
-    Final,
-    NamedTuple,
-    NewType,
-    TypeVar,
-    Union,
-    cast,
-    overload,
-)
+from typing import TYPE_CHECKING, Any, ClassVar, Final, NewType, TypeVar, Union, cast, overload
 from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard
 
 import mypy.nodes
@@ -1607,11 +1596,25 @@ def bound(self) -> bool:
         return bool(self.items) and self.items[0].is_bound
 
 
-class FormalArgument(NamedTuple):
-    name: str | None
-    pos: int | None
-    typ: Type
-    required: bool
+class FormalArgument:
+    def __init__(self, name: str | None, pos: int | None, typ: Type, required: bool) -> None:
+        self.name = name
+        self.pos = pos
+        self.typ = typ
+        self.required = required
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, FormalArgument):
+            return NotImplemented
+        return (
+            self.name == other.name
+            and self.pos == other.pos
+            and self.typ == other.typ
+            and self.required == other.required
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.name, self.pos, self.typ, self.required))
 
 
 class Parameters(ProperType):
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
index 615ba129dad5..cfdd2aacc4d2 100644
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -1612,7 +1612,7 @@ t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3
 t5: Tuple[int, int] = (1, 2, "s", 4)  # E: Incompatible types in assignment (expression has type "tuple[int, int, str, int]", variable has type "tuple[int, int]")
 
 # long initializer assignment with mismatched pairs
-t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type tuple[int, int, ... <15 more items>], variable has type tuple[int, int, ... <10 more items>])
+t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type "tuple[int, int, ... <15 more items>]", variable has type "tuple[int, int, ... <10 more items>]")
 
 [builtins fixtures/tuple.pyi]
 

From 982853dc318c9aaf84001a98f7bd0aa4fc9cdbe1 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 12 Aug 2025 23:47:24 +0100
Subject: [PATCH 1541/1617] Special-case enum method calls (#19634)

Improves https://github.com/mypyc/mypyc/issues/1121, this gives a bit
above 1% on mypy self-check.

This only adds support for regular and overloaded methods without
decorators (class/static methods and properties stay slow). When working
on this I considered (and actually tried) four options:
* Make enums extension classes, then many methods will use fast calls
~automatically (we will just need to set a final flag). This just didn't
work, in the sense no segfaults, but it looks like we don't call
`__prepare__()`, or don't call it at the right moment. Or maybe I just
didn't try hard enough. In general, for some reason this feels risky.
* Use existing `CPyDef`s for (non-extension) enum methods, but since
they have an extra argument, `__mypyc_self__`, we can supply `NULL`
there, since we know it is unused. This is actually easy and it works,
but IMO it is ultra-ugly, so I decided to not do it.
* Write a separate `CPyDef` without `__mypy_self__`, use it for direct
calls, and make existing callable classes `CPyDef`s one-line functions
that simply call the first one. This is possible, but quite complicated,
and I am not sure it is easy to generalize (e.g. on classmethods).
* Finally, the way I do this is to simply generate a second method, that
is almost a copy of the original one. This involves a bit of code
duplication (in C), but the benefit is that it is conceptually simple,
and easily extendable. We can cover more special cases on as-needed
basis.
---
 mypyc/common.py                      |  1 +
 mypyc/ir/class_ir.py                 |  5 +++
 mypyc/irbuild/function.py            | 17 +++++++-
 mypyc/irbuild/ll_builder.py          |  5 ++-
 mypyc/irbuild/prepare.py             | 35 +++++++++++++++-
 mypyc/test-data/irbuild-classes.test | 60 ++++++++++++++++++++++++++++
 mypyc/test-data/run-classes.test     | 50 +++++++++++++++++++++++
 7 files changed, 169 insertions(+), 4 deletions(-)

diff --git a/mypyc/common.py b/mypyc/common.py
index 3a77e9e60c35..2de63c09bb2c 100644
--- a/mypyc/common.py
+++ b/mypyc/common.py
@@ -15,6 +15,7 @@
 MODULE_PREFIX: Final = "CPyModule_"  # Cached modules
 TYPE_VAR_PREFIX: Final = "CPyTypeVar_"  # Type variables when using new-style Python 3.12 syntax
 ATTR_PREFIX: Final = "_"  # Attributes
+FAST_PREFIX: Final = "__mypyc_fast_"  # Optimized methods in non-extension classes
 
 ENV_ATTR_NAME: Final = "__mypyc_env__"
 NEXT_LABEL_ATTR_NAME: Final = "__mypyc_next_label__"
diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py
index 561dc9d438c4..f6015b64dcdd 100644
--- a/mypyc/ir/class_ir.py
+++ b/mypyc/ir/class_ir.py
@@ -210,6 +210,9 @@ def __init__(
         # per-type free "list" of up to length 1.
         self.reuse_freed_instance = False
 
+        # Is this a class inheriting from enum.Enum? Such classes can be special-cased.
+        self.is_enum = False
+
     def __repr__(self) -> str:
         return (
             "ClassIR("
@@ -410,6 +413,7 @@ def serialize(self) -> JsonDict:
             "init_self_leak": self.init_self_leak,
             "env_user_function": self.env_user_function.id if self.env_user_function else None,
             "reuse_freed_instance": self.reuse_freed_instance,
+            "is_enum": self.is_enum,
         }
 
     @classmethod
@@ -466,6 +470,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR:
             ctx.functions[data["env_user_function"]] if data["env_user_function"] else None
         )
         ir.reuse_freed_instance = data["reuse_freed_instance"]
+        ir.is_enum = data["is_enum"]
 
         return ir
 
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index 90506adde672..d70b16475503 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -29,7 +29,7 @@
     Var,
 )
 from mypy.types import CallableType, Type, UnboundType, get_proper_type
-from mypyc.common import LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME
+from mypyc.common import FAST_PREFIX, LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME
 from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
 from mypyc.ir.func_ir import (
     FUNC_CLASSMETHOD,
@@ -166,6 +166,7 @@ def gen_func_item(
     name: str,
     sig: FuncSignature,
     cdef: ClassDef | None = None,
+    make_ext_method: bool = False,
 ) -> tuple[FuncIR, Value | None]:
     """Generate and return the FuncIR for a given FuncDef.
 
@@ -217,7 +218,7 @@ def c() -> None:
     class_name = None
     if cdef:
         ir = builder.mapper.type_to_ir[cdef.info]
-        in_non_ext = not ir.is_ext_class
+        in_non_ext = not ir.is_ext_class and not make_ext_method
         class_name = cdef.name
 
     if is_singledispatch:
@@ -339,6 +340,9 @@ def gen_func_ir(
         fitem = fn_info.fitem
         assert isinstance(fitem, FuncDef), fitem
         func_decl = builder.mapper.func_to_decl[fitem]
+        if cdef and fn_info.name == FAST_PREFIX + func_decl.name:
+            # Special-cased version of a method has a separate FuncDecl, use that one.
+            func_decl = builder.mapper.type_to_ir[cdef.info].method_decls[fn_info.name]
         if fn_info.is_decorated or is_singledispatch_main_func:
             class_name = None if cdef is None else cdef.name
             func_decl = FuncDecl(
@@ -453,6 +457,15 @@ def handle_non_ext_method(
 
     builder.add_to_non_ext_dict(non_ext, name, func_reg, fdef.line)
 
+    # If we identified that this non-extension class method can be special-cased for
+    # direct access during prepare phase, generate a "static" version of it.
+    class_ir = builder.mapper.type_to_ir[cdef.info]
+    name = FAST_PREFIX + fdef.name
+    if name in class_ir.method_decls:
+        func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef, make_ext_method=True)
+        class_ir.methods[name] = func_ir
+        builder.functions.append(func_ir)
+
 
 def gen_func_ns(builder: IRBuilder) -> str:
     """Generate a namespace for a nested function using its outer function names."""
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index a5e28268efed..05d558e0822a 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -17,6 +17,7 @@
 from mypyc.common import (
     BITMAP_BITS,
     FAST_ISINSTANCE_MAX_SUBCLASSES,
+    FAST_PREFIX,
     IS_FREE_THREADED,
     MAX_LITERAL_SHORT_INT,
     MAX_SHORT_INT,
@@ -1171,11 +1172,13 @@ def gen_method_call(
             return self.py_method_call(base, name, arg_values, line, arg_kinds, arg_names)
 
         # If the base type is one of ours, do a MethodCall
+        fast_name = FAST_PREFIX + name
         if (
             isinstance(base.type, RInstance)
-            and base.type.class_ir.is_ext_class
+            and (base.type.class_ir.is_ext_class or base.type.class_ir.has_method(fast_name))
             and not base.type.class_ir.builtin_base
         ):
+            name = name if base.type.class_ir.is_ext_class else fast_name
             if base.type.class_ir.has_method(name):
                 decl = base.type.class_ir.method_decl(name)
                 if arg_kinds is None:
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 1d6117ab7b1e..83ec3f7c1d38 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -38,7 +38,7 @@
 from mypy.semanal import refers_to_fullname
 from mypy.traverser import TraverserVisitor
 from mypy.types import Instance, Type, get_proper_type
-from mypyc.common import PROPSET_PREFIX, SELF_NAME, get_id_from_name
+from mypyc.common import FAST_PREFIX, PROPSET_PREFIX, SELF_NAME, get_id_from_name
 from mypyc.crash import catch_errors
 from mypyc.errors import Errors
 from mypyc.ir.class_ir import ClassIR
@@ -106,6 +106,7 @@ def build_type_map(
             class_ir.children = None
         mapper.type_to_ir[cdef.info] = class_ir
         mapper.symbol_fullnames.add(class_ir.fullname)
+        class_ir.is_enum = cdef.info.is_enum and len(cdef.info.enum_members) > 0
 
     # Populate structural information in class IR for extension classes.
     for module, cdef in classes:
@@ -270,6 +271,36 @@ def prepare_method_def(
             ir.property_types[node.name] = decl.sig.ret_type
 
 
+def prepare_fast_path(
+    ir: ClassIR,
+    module_name: str,
+    cdef: ClassDef,
+    mapper: Mapper,
+    node: SymbolNode | None,
+    options: CompilerOptions,
+) -> None:
+    """Add fast (direct) variants of methods in non-extension classes."""
+    if ir.is_enum:
+        # We check that non-empty enums are implicitly final in mypy, so we
+        # can generate direct calls to enum methods.
+        if isinstance(node, OverloadedFuncDef):
+            if node.is_property:
+                return
+            node = node.impl
+        if not isinstance(node, FuncDef):
+            # TODO: support decorated methods (at least @classmethod and @staticmethod).
+            return
+        # The simplest case is a regular or overloaded method without decorators. In this
+        # case we can generate practically identical IR method body, but with a signature
+        # suitable for direct calls (usual non-extension class methods are converted to
+        # callable classes, and thus have an extra __mypyc_self__ argument).
+        name = FAST_PREFIX + node.name
+        sig = mapper.fdef_to_sig(node, options.strict_dunders_typing)
+        decl = FuncDecl(name, cdef.name, module_name, sig, FUNC_NORMAL)
+        ir.method_decls[name] = decl
+    return
+
+
 def is_valid_multipart_property_def(prop: OverloadedFuncDef) -> bool:
     # Checks to ensure supported property decorator semantics
     if len(prop.items) != 2:
@@ -579,6 +610,8 @@ def prepare_non_ext_class_def(
             else:
                 prepare_method_def(ir, module_name, cdef, mapper, get_func_def(node.node), options)
 
+        prepare_fast_path(ir, module_name, cdef, mapper, node.node, options)
+
     if any(cls in mapper.type_to_ir and mapper.type_to_ir[cls].is_ext_class for cls in info.mro):
         errors.error(
             "Non-extension classes may not inherit from extension classes", path, cdef.line
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 1a2c237cc3c9..f8ea26cd41e8 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1408,3 +1408,63 @@ class TestOverload:
 
     def __mypyc_generator_helper__(self, x: Any) -> Any:
         return x
+
+[case testEnumFastPath]
+from enum import Enum
+
+def test(e: E) -> bool:
+    return e.is_one()
+
+class E(Enum):
+    ONE = 1
+    TWO = 2
+
+    def is_one(self) -> bool:
+        return self == E.ONE
+[out]
+def test(e):
+    e :: __main__.E
+    r0 :: bool
+L0:
+    r0 = e.__mypyc_fast_is_one()
+    return r0
+def is_one_E_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def is_one_E_obj.__call__(__mypyc_self__, self):
+    __mypyc_self__ :: __main__.is_one_E_obj
+    self, r0 :: __main__.E
+    r1 :: bool
+    r2 :: bit
+L0:
+    r0 = __main__.E.ONE :: static
+    if is_error(r0) goto L1 else goto L2
+L1:
+    r1 = raise NameError('value for final name "ONE" was not set')
+    unreachable
+L2:
+    r2 = self == r0
+    return r2
+def E.__mypyc_fast_is_one(self):
+    self, r0 :: __main__.E
+    r1 :: bool
+    r2 :: bit
+L0:
+    r0 = __main__.E.ONE :: static
+    if is_error(r0) goto L1 else goto L2
+L1:
+    r1 = raise NameError('value for final name "ONE" was not set')
+    unreachable
+L2:
+    r2 = self == r0
+    return r2
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 54f5343bc7bb..1481f3e06871 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2710,6 +2710,56 @@ from native import Player
 [out]
 Player.MIN = 
 
+[case testEnumMethodCalls]
+from enum import Enum
+from typing import overload, Optional, Union
+
+class C:
+    def foo(self, x: Test) -> bool:
+        assert Test.ONE.is_one()
+        assert x.next(2) == Test.THREE
+        assert x.prev(2) == Test.ONE
+        assert x.enigma(22)
+        assert x.enigma("22") == 22
+        return x.is_one(inverse=True)
+
+class Test(Enum):
+    ONE = 1
+    TWO = 2
+    THREE = 3
+
+    def is_one(self, *, inverse: bool = False) -> bool:
+        if inverse:
+            return self != Test.ONE
+        return self == Test.ONE
+
+    @classmethod
+    def next(cls, val: int) -> Test:
+        return cls(val + 1)
+
+    @staticmethod
+    def prev(val: int) -> Test:
+        return Test(val - 1)
+
+    @overload
+    def enigma(self, val: int) -> bool: ...
+    @overload
+    def enigma(self, val: Optional[str] = None) -> int: ...
+    def enigma(self, val: Union[int, str, None] = None) -> Union[int, bool]:
+        if isinstance(val, int):
+            return self.is_one()
+        return 22
+[file driver.py]
+from native import Test, C
+
+assert Test.ONE.is_one()
+assert Test.TWO.is_one(inverse=True)
+assert not C().foo(Test.ONE)
+assert Test.next(2) == Test.THREE
+assert Test.prev(2) == Test.ONE
+assert Test.ONE.enigma(22)
+assert Test.ONE.enigma("22") == 22
+
 [case testStaticCallsWithUnpackingArgs]
 from typing import Tuple
 

From a0c5238cb881f93fdfabb375c2b9a6921fefea63 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 13 Aug 2025 11:32:13 +0100
Subject: [PATCH 1542/1617] [mypyc] Fix remaining failing test on free-threaded
 builds (#19646)

Just skip an in irbuild test if running on a free-threaded build, since
the IR looks different if free threading is enabled.

Only this one test was failing for me on Python 3.14.0rc1.
---
 mypyc/test-data/irbuild-classes.test | 2 +-
 mypyc/test/test_irbuild.py           | 5 ++++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index f8ea26cd41e8..8a2cc42fbb0f 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -180,7 +180,7 @@ L0:
     o.x = r1; r2 = is_error
     return o
 
-[case testSubclass_toplevel]
+[case testSubclass_withgil_toplevel]
 from typing import TypeVar, Generic
 from mypy_extensions import trait
 T = TypeVar('T')
diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py
index d8f974ef201b..e79cbec392f4 100644
--- a/mypyc/test/test_irbuild.py
+++ b/mypyc/test/test_irbuild.py
@@ -8,7 +8,7 @@
 from mypy.errors import CompileError
 from mypy.test.config import test_temp_dir
 from mypy.test.data import DataDrivenTestCase
-from mypyc.common import TOP_LEVEL_NAME
+from mypyc.common import IS_FREE_THREADED, TOP_LEVEL_NAME
 from mypyc.ir.pprint import format_func
 from mypyc.test.testutil import (
     ICODE_GEN_BUILTINS,
@@ -71,6 +71,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
         if options is None:
             # Skipped test case
             return
+        if "_withgil" in testcase.name and IS_FREE_THREADED:
+            # Test case should only run on a non-free-threaded build.
+            return
         with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
             expected_output = remove_comment_lines(testcase.output)
             expected_output = replace_word_size(expected_output)

From b3d5021503624cd55b50801091166add22e7d81d Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Wed, 13 Aug 2025 11:32:52 +0100
Subject: [PATCH 1543/1617] [mypyc] Include more operations in the trace log
 (#19647)

Add these operations to the trace log (note that trace logging is
disabled by default):
* Native attribute get/set
* Boxing and unboxing
* Casts
* Incref/decref (including some implicit increfs)

All of these are common operations and can be performance bottlenecks.
There are ways to avoid or speed up most of these (possibly through new
mypyc features). For example, incref/decref can sometimes be avoided by
using borrowing.
---
 mypyc/transform/log_trace.py | 89 +++++++++++++++++++++++++++++++++---
 1 file changed, 82 insertions(+), 7 deletions(-)

diff --git a/mypyc/transform/log_trace.py b/mypyc/transform/log_trace.py
index 5b20940c66bb..cec76b9b4f88 100644
--- a/mypyc/transform/log_trace.py
+++ b/mypyc/transform/log_trace.py
@@ -9,8 +9,27 @@
 
 from __future__ import annotations
 
+from typing import Final
+
 from mypyc.ir.func_ir import FuncIR
-from mypyc.ir.ops import Call, CallC, CString, LoadLiteral, LoadStatic, Op, PrimitiveOp, Value
+from mypyc.ir.ops import (
+    Box,
+    Call,
+    CallC,
+    Cast,
+    CString,
+    DecRef,
+    GetAttr,
+    IncRef,
+    LoadLiteral,
+    LoadStatic,
+    Op,
+    PrimitiveOp,
+    SetAttr,
+    Unbox,
+    Value,
+)
+from mypyc.ir.rtypes import none_rprimitive
 from mypyc.irbuild.ll_builder import LowLevelIRBuilder
 from mypyc.options import CompilerOptions
 from mypyc.primitives.misc_ops import log_trace_event
@@ -38,6 +57,18 @@ def get_load_global_name(op: CallC) -> str | None:
     return None
 
 
+# These primitives perform an implicit IncRef for the return value. Only some of the most common ones
+# are included, and mostly ops that could be switched to use borrowing in some contexts.
+primitives_that_inc_ref: Final = {
+    "list_get_item_unsafe",
+    "CPyList_GetItemShort",
+    "CPyDict_GetWithNone",
+    "CPyList_GetItem",
+    "CPyDict_GetItem",
+    "CPyList_PopLast",
+}
+
+
 class LogTraceEventTransform(IRTransform):
     def __init__(self, builder: LowLevelIRBuilder, fullname: str) -> None:
         super().__init__(builder)
@@ -48,7 +79,10 @@ def visit_call(self, op: Call) -> Value:
         return self.log(op, "call", op.fn.fullname)
 
     def visit_primitive_op(self, op: PrimitiveOp) -> Value:
-        return self.log(op, "primitive_op", op.desc.name)
+        value = self.log(op, "primitive_op", op.desc.name)
+        if op.desc.name in primitives_that_inc_ref:
+            self.log_inc_ref(value)
+        return value
 
     def visit_call_c(self, op: CallC) -> Value:
         if global_name := get_load_global_name(op):
@@ -63,11 +97,53 @@ def visit_call_c(self, op: CallC) -> Value:
         elif func_name == "PyObject_VectorcallMethod" and isinstance(op.args[0], LoadLiteral):
             return self.log(op, "python_call_method", str(op.args[0].value))
 
-        return self.log(op, "call_c", func_name)
+        value = self.log(op, "call_c", func_name)
+        if func_name in primitives_that_inc_ref:
+            self.log_inc_ref(value)
+        return value
+
+    def visit_get_attr(self, op: GetAttr) -> Value:
+        value = self.log(op, "get_attr", f"{op.class_type.name}.{op.attr}")
+        if not op.is_borrowed and op.type.is_refcounted:
+            self.log_inc_ref(op)
+        return value
+
+    def visit_set_attr(self, op: SetAttr) -> Value:
+        name = "set_attr" if not op.is_init else "set_attr_init"
+        return self.log(op, name, f"{op.class_type.name}.{op.attr}")
+
+    def visit_box(self, op: Box) -> Value:
+        if op.src.type is none_rprimitive:
+            # Boxing 'None' is a very quick operation, so we don't log it.
+            return self.add(op)
+        else:
+            return self.log(op, "box", str(op.src.type))
+
+    def visit_unbox(self, op: Unbox) -> Value:
+        return self.log(op, "unbox", str(op.type))
+
+    def visit_cast(self, op: Cast) -> Value | None:
+        value = self.log(op, "cast", str(op.type))
+        if not op.is_borrowed:
+            self.log_inc_ref(value)
+        return value
+
+    def visit_inc_ref(self, op: IncRef) -> Value:
+        return self.log(op, "inc_ref", str(op.src.type))
+
+    def visit_dec_ref(self, op: DecRef) -> Value:
+        return self.log(op, "dec_ref", str(op.src.type))
+
+    def log_inc_ref(self, value: Value) -> None:
+        self.log_event("inc_ref", str(value.type), value.line)
 
     def log(self, op: Op, name: str, details: str) -> Value:
-        if op.line >= 0:
-            line_str = str(op.line)
+        self.log_event(name, details, op.line)
+        return self.add(op)
+
+    def log_event(self, name: str, details: str, line: int) -> None:
+        if line >= 0:
+            line_str = str(line)
         else:
             line_str = ""
         self.builder.primitive_op(
@@ -78,6 +154,5 @@ def log(self, op: Op, name: str, details: str) -> Value:
                 CString(name.encode("utf-8")),
                 CString(details.encode("utf-8")),
             ],
-            op.line,
+            line,
         )
-        return self.add(op)

From fb41108b945c562bb02a9ff30a3530f6a2cc9c70 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Wed, 13 Aug 2025 11:52:02 -0400
Subject: [PATCH 1544/1617] [mypyc] feat: stararg fastpath when calling
 fn(*args) with tuple (#19623)

There are 3 safe cases where we can reuse a tuple when calling a python
function:
fn(*args)
fn(*args, **kwargs)
fn(*args, k=1, k2=2, **kwargs)

This PR covers the first two cases.

The IR diff will probably demonstrate this change better than I can
explain it.
---
 mypyc/irbuild/ll_builder.py           | 16 +++++++-
 mypyc/test-data/irbuild-generics.test | 57 +++++++++++----------------
 2 files changed, 37 insertions(+), 36 deletions(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 05d558e0822a..c5f9503b8c66 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -789,6 +789,18 @@ def _construct_varargs(
         for value, kind, name in args:
             if kind == ARG_STAR:
                 if star_result is None:
+                    # fast path if star expr is a tuple:
+                    # we can pass the immutable tuple straight into the function call.
+                    if is_tuple_rprimitive(value.type):
+                        if len(args) == 1:
+                            # fn(*args)
+                            return value, self._create_dict([], [], line)
+                        elif len(args) == 2 and args[1][1] == ARG_STAR2:
+                            # fn(*args, **kwargs)
+                            star_result = value
+                            continue
+                        # elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
+                    # TODO optimize this case using the length utils - currently in review
                     star_result = self.new_list_op(star_values, line)
                 self.primitive_op(list_extend_op, [star_result, value], line)
             elif kind == ARG_STAR2:
@@ -886,9 +898,11 @@ def _construct_varargs(
             # tuple. Otherwise create the tuple from the list.
             if star_result is None:
                 star_result = self.new_tuple(star_values, line)
-            else:
+            elif not is_tuple_rprimitive(star_result.type):
+                # if star_result is a tuple we took the fast path
                 star_result = self.primitive_op(list_tuple_op, [star_result], line)
         if has_star2 and star2_result is None:
+            # TODO: use dict_copy_op for simple cases of **kwargs
             star2_result = self._create_dict(star2_keys, star2_values, line)
 
         return star_result, star2_result
diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test
index d39d47e397a1..03032a7746c0 100644
--- a/mypyc/test-data/irbuild-generics.test
+++ b/mypyc/test-data/irbuild-generics.test
@@ -166,25 +166,18 @@ execute(f, 1)
 def execute(func, args, kwargs):
     func :: object
     args :: tuple
-    kwargs :: dict
-    r0 :: list
-    r1 :: object
-    r2 :: dict
-    r3 :: i32
-    r4 :: bit
-    r5 :: tuple
-    r6 :: object
-    r7 :: int
+    kwargs, r0 :: dict
+    r1 :: i32
+    r2 :: bit
+    r3 :: object
+    r4 :: int
 L0:
-    r0 = PyList_New(0)
-    r1 = CPyList_Extend(r0, args)
-    r2 = PyDict_New()
-    r3 = CPyDict_UpdateInDisplay(r2, kwargs)
-    r4 = r3 >= 0 :: signed
-    r5 = PyList_AsTuple(r0)
-    r6 = PyObject_Call(func, r5, r2)
-    r7 = unbox(int, r6)
-    return r7
+    r0 = PyDict_New()
+    r1 = CPyDict_UpdateInDisplay(r0, kwargs)
+    r2 = r1 >= 0 :: signed
+    r3 = PyObject_Call(func, args, r0)
+    r4 = unbox(int, r3)
+    return r4
 def f(x):
     x :: int
 L0:
@@ -709,14 +702,11 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs):
     can_dictcomp :: dict
     r22, can_iter, r23, can_use_keys, r24, can_use_values :: list
     r25 :: object
-    r26 :: list
-    r27 :: object
-    r28 :: dict
-    r29 :: i32
-    r30 :: bit
-    r31 :: tuple
-    r32 :: object
-    r33 :: int
+    r26 :: dict
+    r27 :: i32
+    r28 :: bit
+    r29 :: object
+    r30 :: int
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = var_object_size args
@@ -768,15 +758,12 @@ L9:
     r24 = CPyDict_Values(kwargs)
     can_use_values = r24
     r25 = r0.func
-    r26 = PyList_New(0)
-    r27 = CPyList_Extend(r26, args)
-    r28 = PyDict_New()
-    r29 = CPyDict_UpdateInDisplay(r28, kwargs)
-    r30 = r29 >= 0 :: signed
-    r31 = PyList_AsTuple(r26)
-    r32 = PyObject_Call(r25, r31, r28)
-    r33 = unbox(int, r32)
-    return r33
+    r26 = PyDict_New()
+    r27 = CPyDict_UpdateInDisplay(r26, kwargs)
+    r28 = r27 >= 0 :: signed
+    r29 = PyObject_Call(r25, args, r26)
+    r30 = unbox(int, r29)
+    return r30
 def deco(func):
     func :: object
     r0 :: __main__.deco_env

From c95d8ab3d48dbb2192e2179278a60a951b98bcba Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Thu, 14 Aug 2025 11:15:20 -0400
Subject: [PATCH 1545/1617] [mypyc] feat: cache len for iterating over
 immutable types (#19656)

Currently, if a user uses an immutable type as the sequence input for a
for loop, the length is checked once at each iteration which, while
necessary for some container types such as list and dictionaries, is not
necessary for iterating over immutable types tuple, str, and bytes.

This PR modifies the codebase such that the length is only checked at
the first iteration, and reused from there.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypyc/ir/rtypes.py                    |  14 +-
 mypyc/irbuild/builder.py              |   7 +-
 mypyc/irbuild/for_helpers.py          |  35 +-
 mypyc/irbuild/specialize.py           |   2 +-
 mypyc/test-data/fixtures/ir.py        |   1 +
 mypyc/test-data/irbuild-generics.test |  14 +-
 mypyc/test-data/irbuild-lists.test    | 354 ++++++++++++++++++++
 mypyc/test-data/irbuild-tuple.test    | 460 ++++++++++++++++++++++++--
 8 files changed, 846 insertions(+), 41 deletions(-)

diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index c0871bba258c..7a82a884256d 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -628,7 +628,19 @@ def is_range_rprimitive(rtype: RType) -> bool:
 
 def is_sequence_rprimitive(rtype: RType) -> bool:
     return isinstance(rtype, RPrimitive) and (
-        is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype)
+        is_list_rprimitive(rtype)
+        or is_tuple_rprimitive(rtype)
+        or is_str_rprimitive(rtype)
+        or is_bytes_rprimitive(rtype)
+    )
+
+
+def is_immutable_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
+    return (
+        is_str_rprimitive(rtype)
+        or is_bytes_rprimitive(rtype)
+        or is_tuple_rprimitive(rtype)
+        or is_frozenset_rprimitive(rtype)
     )
 
 
diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py
index 608c524b5d4f..4f2f539118d7 100644
--- a/mypyc/irbuild/builder.py
+++ b/mypyc/irbuild/builder.py
@@ -91,6 +91,7 @@
     RType,
     RUnion,
     bitmap_rprimitive,
+    bytes_rprimitive,
     c_pyssize_t_rprimitive,
     dict_rprimitive,
     int_rprimitive,
@@ -962,8 +963,12 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType:
         elif isinstance(target_type, Instance):
             if target_type.type.fullname == "builtins.str":
                 return str_rprimitive
-            else:
+            elif target_type.type.fullname == "builtins.bytes":
+                return bytes_rprimitive
+            try:
                 return self.type_to_rtype(target_type.args[0])
+            except IndexError:
+                raise ValueError(f"{target_type!r} is not a valid sequence.") from None
         # This elif-blocks are needed for iterating over classes derived from NamedTuple.
         elif isinstance(target_type, TypeVarLikeType):
             return self.get_sequence_type_from_type(target_type.upper_bound)
diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py
index 5cf89f579ec4..762b41866a05 100644
--- a/mypyc/irbuild/for_helpers.py
+++ b/mypyc/irbuild/for_helpers.py
@@ -48,6 +48,7 @@
     int_rprimitive,
     is_dict_rprimitive,
     is_fixed_width_rtype,
+    is_immutable_rprimitive,
     is_list_rprimitive,
     is_sequence_rprimitive,
     is_short_int_rprimitive,
@@ -205,9 +206,9 @@ def sequence_from_generator_preallocate_helper(
     there is no condition list in the generator and only one original sequence with
     one index is allowed.
 
-    e.g.  (1) tuple(f(x) for x in a_list/a_tuple)
-          (2) list(f(x) for x in a_list/a_tuple)
-          (3) [f(x) for x in a_list/a_tuple]
+    e.g.  (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes)
+          (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes)
+          (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes]
     RTuple as an original sequence is not supported yet.
 
     Args:
@@ -224,7 +225,7 @@ def sequence_from_generator_preallocate_helper(
     """
     if len(gen.sequences) == 1 and len(gen.indices) == 1 and len(gen.condlists[0]) == 0:
         rtype = builder.node_type(gen.sequences[0])
-        if is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype):
+        if is_sequence_rprimitive(rtype):
             sequence = builder.accept(gen.sequences[0])
             length = builder.builder.builtin_len(sequence, gen.line, use_pyssize_t=True)
             target_op = empty_op_llbuilder(length, gen.line)
@@ -785,17 +786,31 @@ class ForSequence(ForGenerator):
     Supports iterating in both forward and reverse.
     """
 
+    length_reg: Value | AssignmentTarget | None
+
     def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
+        assert is_sequence_rprimitive(expr_reg.type), expr_reg
         builder = self.builder
         self.reverse = reverse
         # Define target to contain the expression, along with the index that will be used
         # for the for-loop. If we are inside of a generator function, spill these into the
         # environment class.
         self.expr_target = builder.maybe_spill(expr_reg)
+        if is_immutable_rprimitive(expr_reg.type):
+            # If the expression is an immutable type, we can load the length just once.
+            self.length_reg = builder.maybe_spill(self.load_len(self.expr_target))
+        else:
+            # Otherwise, even if the length is known, we must recalculate the length
+            # at every iteration for compatibility with python semantics.
+            self.length_reg = None
         if not reverse:
             index_reg: Value = Integer(0, c_pyssize_t_rprimitive)
         else:
-            index_reg = builder.builder.int_sub(self.load_len(self.expr_target), 1)
+            if self.length_reg is not None:
+                len_val = builder.read(self.length_reg)
+            else:
+                len_val = self.load_len(self.expr_target)
+            index_reg = builder.builder.int_sub(len_val, 1)
         self.index_target = builder.maybe_spill_assignable(index_reg)
         self.target_type = target_type
 
@@ -814,9 +829,13 @@ def gen_condition(self) -> None:
             second_check = BasicBlock()
             builder.add_bool_branch(comparison, second_check, self.loop_exit)
             builder.activate_block(second_check)
-        # For compatibility with python semantics we recalculate the length
-        # at every iteration.
-        len_reg = self.load_len(self.expr_target)
+        if self.length_reg is None:
+            # For compatibility with python semantics we recalculate the length
+            # at every iteration.
+            len_reg = self.load_len(self.expr_target)
+        else:
+            # (unless input is immutable type).
+            len_reg = builder.read(self.length_reg, line)
         comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, "<", line)
         builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
 
diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py
index 3015640fb3fd..748cda1256a7 100644
--- a/mypyc/irbuild/specialize.py
+++ b/mypyc/irbuild/specialize.py
@@ -288,7 +288,7 @@ def translate_tuple_from_generator_call(
     """Special case for simplest tuple creation from a generator.
 
     For example:
-        tuple(f(x) for x in some_list/some_tuple/some_str)
+        tuple(f(x) for x in some_list/some_tuple/some_str/some_bytes)
     'translate_safe_generator_call()' would take care of other cases
     if this fails.
     """
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 76afc1ea58cc..661ae50fd5f3 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -172,6 +172,7 @@ def __getitem__(self, i: int) -> int: ...
     def __getitem__(self, i: slice) -> bytes: ...
     def join(self, x: Iterable[object]) -> bytes: ...
     def decode(self, x: str=..., y: str=...) -> str: ...
+    def __iter__(self) -> Iterator[int]: ...
 
 class bytearray:
     @overload
diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test
index 03032a7746c0..4e9391e0d59e 100644
--- a/mypyc/test-data/irbuild-generics.test
+++ b/mypyc/test-data/irbuild-generics.test
@@ -711,18 +711,18 @@ L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = var_object_size args
     r2 = PyList_New(r1)
-    r3 = 0
+    r3 = var_object_size args
+    r4 = 0
 L1:
-    r4 = var_object_size args
-    r5 = r3 < r4 :: signed
+    r5 = r4 < r3 :: signed
     if r5 goto L2 else goto L4 :: bool
 L2:
-    r6 = CPySequenceTuple_GetItemUnsafe(args, r3)
+    r6 = CPySequenceTuple_GetItemUnsafe(args, r4)
     x = r6
-    CPyList_SetItemUnsafe(r2, r3, x)
+    CPyList_SetItemUnsafe(r2, r4, x)
 L3:
-    r7 = r3 + 1
-    r3 = r7
+    r7 = r4 + 1
+    r4 = r7
     goto L1
 L4:
     can_listcomp = r2
diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test
index 06120e077af9..d83fb88390db 100644
--- a/mypyc/test-data/irbuild-lists.test
+++ b/mypyc/test-data/irbuild-lists.test
@@ -572,3 +572,357 @@ def sort_iterable(a):
 L0:
     r0 = CPySequence_Sort(a)
     return 1
+
+[case testListBuiltFromStr]
+def f2(val: str) -> str:
+    return val + "f2"
+
+def test() -> None:
+    source = "abc"
+    a = [f2(x) for x in source]
+[out]
+def f2(val):
+    val, r0, r1 :: str
+L0:
+    r0 = 'f2'
+    r1 = PyUnicode_Concat(val, r0)
+    return r1
+def test():
+    r0, source :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: list
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
+    a :: list
+L0:
+    r0 = 'abc'
+    source = r0
+    r1 = CPyStr_Size_size_t(source)
+    r2 = r1 >= 0 :: signed
+    r3 = PyList_New(r1)
+    r4 = CPyStr_Size_size_t(source)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
+L1:
+    r7 = r6 < r4 :: signed
+    if r7 goto L2 else goto L4 :: bool
+L2:
+    r8 = CPyStr_GetItemUnsafe(source, r6)
+    x = r8
+    r9 = f2(x)
+    CPyList_SetItemUnsafe(r3, r6, r9)
+L3:
+    r10 = r6 + 1
+    r6 = r10
+    goto L1
+L4:
+    a = r3
+    return 1
+
+[case testListBuiltFromStrExpr]
+def f2(val: str) -> str:
+    return val + "f2"
+
+def test() -> None:
+    a = [f2(x) for x in "abc"]
+[out]
+def f2(val):
+    val, r0, r1 :: str
+L0:
+    r0 = 'f2'
+    r1 = PyUnicode_Concat(val, r0)
+    return r1
+def test():
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: list
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
+    a :: list
+L0:
+    r0 = 'abc'
+    r1 = CPyStr_Size_size_t(r0)
+    r2 = r1 >= 0 :: signed
+    r3 = PyList_New(r1)
+    r4 = CPyStr_Size_size_t(r0)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
+L1:
+    r7 = r6 < r4 :: signed
+    if r7 goto L2 else goto L4 :: bool
+L2:
+    r8 = CPyStr_GetItemUnsafe(r0, r6)
+    x = r8
+    r9 = f2(x)
+    CPyList_SetItemUnsafe(r3, r6, r9)
+L3:
+    r10 = r6 + 1
+    r6 = r10
+    goto L1
+L4:
+    a = r3
+    return 1
+
+[case testListBuiltFromFinalStr]
+from typing import Final
+
+source: Final = "abc"
+
+def f2(val: str) -> str:
+    return val + "f2"
+
+def test() -> None:
+    a = [f2(x) for x in source]
+[out]
+def f2(val):
+    val, r0, r1 :: str
+L0:
+    r0 = 'f2'
+    r1 = PyUnicode_Concat(val, r0)
+    return r1
+def test():
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: list
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
+    a :: list
+L0:
+    r0 = 'abc'
+    r1 = CPyStr_Size_size_t(r0)
+    r2 = r1 >= 0 :: signed
+    r3 = PyList_New(r1)
+    r4 = CPyStr_Size_size_t(r0)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
+L1:
+    r7 = r6 < r4 :: signed
+    if r7 goto L2 else goto L4 :: bool
+L2:
+    r8 = CPyStr_GetItemUnsafe(r0, r6)
+    x = r8
+    r9 = f2(x)
+    CPyList_SetItemUnsafe(r3, r6, r9)
+L3:
+    r10 = r6 + 1
+    r6 = r10
+    goto L1
+L4:
+    a = r3
+    return 1
+
+[case testListBuiltFromBytes_64bit]
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    source = b"abc"
+    a = [f2(x) for x in source]
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0, source :: bytes
+    r1 :: native_int
+    r2 :: list
+    r3, r4 :: native_int
+    r5, r6, r7 :: bit
+    r8, r9, r10, r11 :: int
+    r12 :: object
+    r13, x, r14 :: int
+    r15 :: object
+    r16 :: native_int
+    a :: list
+L0:
+    r0 = b'abc'
+    source = r0
+    r1 = var_object_size source
+    r2 = PyList_New(r1)
+    r3 = var_object_size source
+    r4 = 0
+L1:
+    r5 = r4 < r3 :: signed
+    if r5 goto L2 else goto L8 :: bool
+L2:
+    r6 = r4 <= 4611686018427387903 :: signed
+    if r6 goto L3 else goto L4 :: bool
+L3:
+    r7 = r4 >= -4611686018427387904 :: signed
+    if r7 goto L5 else goto L4 :: bool
+L4:
+    r8 = CPyTagged_FromInt64(r4)
+    r9 = r8
+    goto L6
+L5:
+    r10 = r4 << 1
+    r9 = r10
+L6:
+    r11 = CPyBytes_GetItem(source, r9)
+    r12 = box(int, r11)
+    r13 = unbox(int, r12)
+    x = r13
+    r14 = f2(x)
+    r15 = box(int, r14)
+    CPyList_SetItemUnsafe(r2, r4, r15)
+L7:
+    r16 = r4 + 1
+    r4 = r16
+    goto L1
+L8:
+    a = r2
+    return 1
+
+[case testListBuiltFromBytesExpr_64bit]
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    a = [f2(x) for x in b"abc"]
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0 :: bytes
+    r1 :: native_int
+    r2 :: list
+    r3, r4 :: native_int
+    r5, r6, r7 :: bit
+    r8, r9, r10, r11 :: int
+    r12 :: object
+    r13, x, r14 :: int
+    r15 :: object
+    r16 :: native_int
+    a :: list
+L0:
+    r0 = b'abc'
+    r1 = var_object_size r0
+    r2 = PyList_New(r1)
+    r3 = var_object_size r0
+    r4 = 0
+L1:
+    r5 = r4 < r3 :: signed
+    if r5 goto L2 else goto L8 :: bool
+L2:
+    r6 = r4 <= 4611686018427387903 :: signed
+    if r6 goto L3 else goto L4 :: bool
+L3:
+    r7 = r4 >= -4611686018427387904 :: signed
+    if r7 goto L5 else goto L4 :: bool
+L4:
+    r8 = CPyTagged_FromInt64(r4)
+    r9 = r8
+    goto L6
+L5:
+    r10 = r4 << 1
+    r9 = r10
+L6:
+    r11 = CPyBytes_GetItem(r0, r9)
+    r12 = box(int, r11)
+    r13 = unbox(int, r12)
+    x = r13
+    r14 = f2(x)
+    r15 = box(int, r14)
+    CPyList_SetItemUnsafe(r2, r4, r15)
+L7:
+    r16 = r4 + 1
+    r4 = r16
+    goto L1
+L8:
+    a = r2
+    return 1
+
+[case testListBuiltFromFinalBytes_64bit]
+from typing import Final
+
+source: Final = b"abc"
+
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    a = [f2(x) for x in source]
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0 :: bytes
+    r1 :: bool
+    r2 :: native_int
+    r3 :: list
+    r4, r5 :: native_int
+    r6, r7, r8 :: bit
+    r9, r10, r11, r12 :: int
+    r13 :: object
+    r14, x, r15 :: int
+    r16 :: object
+    r17 :: native_int
+    a :: list
+L0:
+    r0 = __main__.source :: static
+    if is_error(r0) goto L1 else goto L2
+L1:
+    r1 = raise NameError('value for final name "source" was not set')
+    unreachable
+L2:
+    r2 = var_object_size r0
+    r3 = PyList_New(r2)
+    r4 = var_object_size r0
+    r5 = 0
+L3:
+    r6 = r5 < r4 :: signed
+    if r6 goto L4 else goto L10 :: bool
+L4:
+    r7 = r5 <= 4611686018427387903 :: signed
+    if r7 goto L5 else goto L6 :: bool
+L5:
+    r8 = r5 >= -4611686018427387904 :: signed
+    if r8 goto L7 else goto L6 :: bool
+L6:
+    r9 = CPyTagged_FromInt64(r5)
+    r10 = r9
+    goto L8
+L7:
+    r11 = r5 << 1
+    r10 = r11
+L8:
+    r12 = CPyBytes_GetItem(r0, r10)
+    r13 = box(int, r12)
+    r14 = unbox(int, r13)
+    x = r14
+    r15 = f2(x)
+    r16 = box(int, r15)
+    CPyList_SetItemUnsafe(r3, r5, r16)
+L9:
+    r17 = r5 + 1
+    r5 = r17
+    goto L3
+L10:
+    a = r3
+    return 1
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index 5c5ec27b1882..0342ec304c25 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -133,18 +133,18 @@ def f(xs):
     r4, x :: str
     r5 :: native_int
 L0:
-    r0 = 0
+    r0 = var_object_size xs
+    r1 = 0
 L1:
-    r1 = var_object_size xs
-    r2 = r0 < r1 :: signed
+    r2 = r1 < r0 :: signed
     if r2 goto L2 else goto L4 :: bool
 L2:
-    r3 = CPySequenceTuple_GetItemUnsafe(xs, r0)
+    r3 = CPySequenceTuple_GetItemUnsafe(xs, r1)
     r4 = cast(str, r3)
     x = r4
 L3:
-    r5 = r0 + 1
-    r0 = r5
+    r5 = r1 + 1
+    r1 = r5
     goto L1
 L4:
     return 1
@@ -291,8 +291,10 @@ def test():
     r1 :: native_int
     r2 :: bit
     r3 :: tuple
-    r4, r5 :: native_int
-    r6, r7 :: bit
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
     r8, x, r9 :: str
     r10 :: native_int
     a :: tuple
@@ -302,25 +304,437 @@ L0:
     r1 = CPyStr_Size_size_t(source)
     r2 = r1 >= 0 :: signed
     r3 = PyTuple_New(r1)
-    r4 = 0
+    r4 = CPyStr_Size_size_t(source)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
 L1:
-    r5 = CPyStr_Size_size_t(source)
-    r6 = r5 >= 0 :: signed
-    r7 = r4 < r5 :: signed
+    r7 = r6 < r4 :: signed
     if r7 goto L2 else goto L4 :: bool
 L2:
-    r8 = CPyStr_GetItemUnsafe(source, r4)
+    r8 = CPyStr_GetItemUnsafe(source, r6)
     x = r8
     r9 = f2(x)
-    CPySequenceTuple_SetItemUnsafe(r3, r4, r9)
+    CPySequenceTuple_SetItemUnsafe(r3, r6, r9)
 L3:
-    r10 = r4 + 1
-    r4 = r10
+    r10 = r6 + 1
+    r6 = r10
     goto L1
 L4:
     a = r3
     return 1
 
+[case testTupleBuiltFromStrExpr]
+def f2(val: str) -> str:
+    return val + "f2"
+
+def test() -> None:
+    a = tuple(f2(x) for x in "abc")
+
+[out]
+def f2(val):
+    val, r0, r1 :: str
+L0:
+    r0 = 'f2'
+    r1 = PyUnicode_Concat(val, r0)
+    return r1
+def test():
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: tuple
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
+    a :: tuple
+L0:
+    r0 = 'abc'
+    r1 = CPyStr_Size_size_t(r0)
+    r2 = r1 >= 0 :: signed
+    r3 = PyTuple_New(r1)
+    r4 = CPyStr_Size_size_t(r0)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
+L1:
+    r7 = r6 < r4 :: signed
+    if r7 goto L2 else goto L4 :: bool
+L2:
+    r8 = CPyStr_GetItemUnsafe(r0, r6)
+    x = r8
+    r9 = f2(x)
+    CPySequenceTuple_SetItemUnsafe(r3, r6, r9)
+L3:
+    r10 = r6 + 1
+    r6 = r10
+    goto L1
+L4:
+    a = r3
+    return 1
+
+[case testTupleBuiltFromFinalStr]
+from typing import Final
+
+source: Final = "abc"
+
+def f2(val: str) -> str:
+    return val + "f2"
+
+def test() -> None:
+    a = tuple(f2(x) for x in source)
+[out]
+def f2(val):
+    val, r0, r1 :: str
+L0:
+    r0 = 'f2'
+    r1 = PyUnicode_Concat(val, r0)
+    return r1
+def test():
+    r0 :: str
+    r1 :: native_int
+    r2 :: bit
+    r3 :: tuple
+    r4 :: native_int
+    r5 :: bit
+    r6 :: native_int
+    r7 :: bit
+    r8, x, r9 :: str
+    r10 :: native_int
+    a :: tuple
+L0:
+    r0 = 'abc'
+    r1 = CPyStr_Size_size_t(r0)
+    r2 = r1 >= 0 :: signed
+    r3 = PyTuple_New(r1)
+    r4 = CPyStr_Size_size_t(r0)
+    r5 = r4 >= 0 :: signed
+    r6 = 0
+L1:
+    r7 = r6 < r4 :: signed
+    if r7 goto L2 else goto L4 :: bool
+L2:
+    r8 = CPyStr_GetItemUnsafe(r0, r6)
+    x = r8
+    r9 = f2(x)
+    CPySequenceTuple_SetItemUnsafe(r3, r6, r9)
+L3:
+    r10 = r6 + 1
+    r6 = r10
+    goto L1
+L4:
+    a = r3
+    return 1
+
+[case testTupleBuiltFromBytes_64bit]
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    source = b"abc"
+    a = tuple(f2(x) for x in source)
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0, source :: bytes
+    r1 :: native_int
+    r2 :: tuple
+    r3, r4 :: native_int
+    r5, r6, r7 :: bit
+    r8, r9, r10, r11 :: int
+    r12 :: object
+    r13, x, r14 :: int
+    r15 :: object
+    r16 :: native_int
+    a :: tuple
+L0:
+    r0 = b'abc'
+    source = r0
+    r1 = var_object_size source
+    r2 = PyTuple_New(r1)
+    r3 = var_object_size source
+    r4 = 0
+L1:
+    r5 = r4 < r3 :: signed
+    if r5 goto L2 else goto L8 :: bool
+L2:
+    r6 = r4 <= 4611686018427387903 :: signed
+    if r6 goto L3 else goto L4 :: bool
+L3:
+    r7 = r4 >= -4611686018427387904 :: signed
+    if r7 goto L5 else goto L4 :: bool
+L4:
+    r8 = CPyTagged_FromInt64(r4)
+    r9 = r8
+    goto L6
+L5:
+    r10 = r4 << 1
+    r9 = r10
+L6:
+    r11 = CPyBytes_GetItem(source, r9)
+    r12 = box(int, r11)
+    r13 = unbox(int, r12)
+    x = r13
+    r14 = f2(x)
+    r15 = box(int, r14)
+    CPySequenceTuple_SetItemUnsafe(r2, r4, r15)
+L7:
+    r16 = r4 + 1
+    r4 = r16
+    goto L1
+L8:
+    a = r2
+    return 1
+
+[case testTupleBuiltFromBytesExpr_64bit]
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    a = tuple(f2(x) for x in b"abc")
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0 :: bytes
+    r1 :: native_int
+    r2 :: tuple
+    r3, r4 :: native_int
+    r5, r6, r7 :: bit
+    r8, r9, r10, r11 :: int
+    r12 :: object
+    r13, x, r14 :: int
+    r15 :: object
+    r16 :: native_int
+    a :: tuple
+L0:
+    r0 = b'abc'
+    r1 = var_object_size r0
+    r2 = PyTuple_New(r1)
+    r3 = var_object_size r0
+    r4 = 0
+L1:
+    r5 = r4 < r3 :: signed
+    if r5 goto L2 else goto L8 :: bool
+L2:
+    r6 = r4 <= 4611686018427387903 :: signed
+    if r6 goto L3 else goto L4 :: bool
+L3:
+    r7 = r4 >= -4611686018427387904 :: signed
+    if r7 goto L5 else goto L4 :: bool
+L4:
+    r8 = CPyTagged_FromInt64(r4)
+    r9 = r8
+    goto L6
+L5:
+    r10 = r4 << 1
+    r9 = r10
+L6:
+    r11 = CPyBytes_GetItem(r0, r9)
+    r12 = box(int, r11)
+    r13 = unbox(int, r12)
+    x = r13
+    r14 = f2(x)
+    r15 = box(int, r14)
+    CPySequenceTuple_SetItemUnsafe(r2, r4, r15)
+L7:
+    r16 = r4 + 1
+    r4 = r16
+    goto L1
+L8:
+    a = r2
+    return 1
+
+[case testTupleBuiltFromFinalBytes_64bit]
+from typing import Final
+
+source: Final = b"abc"
+
+def f2(val: int) -> int:
+    return val + 2
+
+def test() -> None:
+    a = tuple(f2(x) for x in source)
+
+[out]
+def f2(val):
+    val, r0 :: int
+L0:
+    r0 = CPyTagged_Add(val, 4)
+    return r0
+def test():
+    r0 :: bytes
+    r1 :: bool
+    r2 :: native_int
+    r3 :: tuple
+    r4, r5 :: native_int
+    r6, r7, r8 :: bit
+    r9, r10, r11, r12 :: int
+    r13 :: object
+    r14, x, r15 :: int
+    r16 :: object
+    r17 :: native_int
+    a :: tuple
+L0:
+    r0 = __main__.source :: static
+    if is_error(r0) goto L1 else goto L2
+L1:
+    r1 = raise NameError('value for final name "source" was not set')
+    unreachable
+L2:
+    r2 = var_object_size r0
+    r3 = PyTuple_New(r2)
+    r4 = var_object_size r0
+    r5 = 0
+L3:
+    r6 = r5 < r4 :: signed
+    if r6 goto L4 else goto L10 :: bool
+L4:
+    r7 = r5 <= 4611686018427387903 :: signed
+    if r7 goto L5 else goto L6 :: bool
+L5:
+    r8 = r5 >= -4611686018427387904 :: signed
+    if r8 goto L7 else goto L6 :: bool
+L6:
+    r9 = CPyTagged_FromInt64(r5)
+    r10 = r9
+    goto L8
+L7:
+    r11 = r5 << 1
+    r10 = r11
+L8:
+    r12 = CPyBytes_GetItem(r0, r10)
+    r13 = box(int, r12)
+    r14 = unbox(int, r13)
+    x = r14
+    r15 = f2(x)
+    r16 = box(int, r15)
+    CPySequenceTuple_SetItemUnsafe(r3, r5, r16)
+L9:
+    r17 = r5 + 1
+    r5 = r17
+    goto L3
+L10:
+    a = r3
+    return 1
+
+[case testTupleBuiltFromFixedLengthTuple]
+def f(val: int) -> bool:
+    return val % 2 == 0
+
+def test() -> None:
+    source = (1, 2, 3)
+    a = tuple(f(x) for x in source)
+[out]
+def f(val):
+    val, r0 :: int
+    r1 :: bit
+L0:
+    r0 = CPyTagged_Remainder(val, 4)
+    r1 = int_eq r0, 0
+    return r1
+def test():
+    r0, source :: tuple[int, int, int]
+    r1 :: list
+    r2, r3, r4 :: object
+    r5, x :: int
+    r6 :: bool
+    r7 :: object
+    r8 :: i32
+    r9, r10 :: bit
+    r11, a :: tuple
+L0:
+    r0 = (2, 4, 6)
+    source = r0
+    r1 = PyList_New(0)
+    r2 = box(tuple[int, int, int], source)
+    r3 = PyObject_GetIter(r2)
+L1:
+    r4 = PyIter_Next(r3)
+    if is_error(r4) goto L4 else goto L2
+L2:
+    r5 = unbox(int, r4)
+    x = r5
+    r6 = f(x)
+    r7 = box(bool, r6)
+    r8 = PyList_Append(r1, r7)
+    r9 = r8 >= 0 :: signed
+L3:
+    goto L1
+L4:
+    r10 = CPy_NoErrOccurred()
+L5:
+    r11 = PyList_AsTuple(r1)
+    a = r11
+    return 1
+
+[case testTupleBuiltFromFinalFixedLengthTuple]
+from typing import Final
+
+source: Final = (1, 2, 3)
+
+def f(val: int) -> bool:
+    return val % 2 == 0
+
+def test() -> None:
+    a = tuple(f(x) for x in source)
+[out]
+def f(val):
+    val, r0 :: int
+    r1 :: bit
+L0:
+    r0 = CPyTagged_Remainder(val, 4)
+    r1 = int_eq r0, 0
+    return r1
+def test():
+    r0 :: list
+    r1 :: tuple[int, int, int]
+    r2 :: bool
+    r3, r4, r5 :: object
+    r6, x :: int
+    r7 :: bool
+    r8 :: object
+    r9 :: i32
+    r10, r11 :: bit
+    r12, a :: tuple
+L0:
+    r0 = PyList_New(0)
+    r1 = __main__.source :: static
+    if is_error(r1) goto L1 else goto L2
+L1:
+    r2 = raise NameError('value for final name "source" was not set')
+    unreachable
+L2:
+    r3 = box(tuple[int, int, int], r1)
+    r4 = PyObject_GetIter(r3)
+L3:
+    r5 = PyIter_Next(r4)
+    if is_error(r5) goto L6 else goto L4
+L4:
+    r6 = unbox(int, r5)
+    x = r6
+    r7 = f(x)
+    r8 = box(bool, r7)
+    r9 = PyList_Append(r0, r8)
+    r10 = r9 >= 0 :: signed
+L5:
+    goto L3
+L6:
+    r11 = CPy_NoErrOccurred()
+L7:
+    r12 = PyList_AsTuple(r0)
+    a = r12
+    return 1
+
 [case testTupleBuiltFromVariableLengthTuple]
 from typing import Tuple
 
@@ -349,21 +763,21 @@ def test(source):
 L0:
     r0 = var_object_size source
     r1 = PyTuple_New(r0)
-    r2 = 0
+    r2 = var_object_size source
+    r3 = 0
 L1:
-    r3 = var_object_size source
-    r4 = r2 < r3 :: signed
+    r4 = r3 < r2 :: signed
     if r4 goto L2 else goto L4 :: bool
 L2:
-    r5 = CPySequenceTuple_GetItemUnsafe(source, r2)
+    r5 = CPySequenceTuple_GetItemUnsafe(source, r3)
     r6 = unbox(bool, r5)
     x = r6
     r7 = f(x)
     r8 = box(bool, r7)
-    CPySequenceTuple_SetItemUnsafe(r1, r2, r8)
+    CPySequenceTuple_SetItemUnsafe(r1, r3, r8)
 L3:
-    r9 = r2 + 1
-    r2 = r9
+    r9 = r3 + 1
+    r3 = r9
     goto L1
 L4:
     a = r1

From 27b9ba0031a2ffd1661a1c93c606a1013e04f95f Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Thu, 14 Aug 2025 11:17:07 -0400
Subject: [PATCH 1546/1617] chore: homogenize TypeGuard usage in rtypes.py
 (#19655)

---
 mypyc/ir/rtypes.py | 62 +++++++++++++++++++++++-----------------------
 1 file changed, 31 insertions(+), 31 deletions(-)

diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 7a82a884256d..3c2fbfec1035 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -192,7 +192,7 @@ def may_be_immortal(self) -> bool:
     def serialize(self) -> str:
         return "void"
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RVoid]:
         return isinstance(other, RVoid)
 
     def __hash__(self) -> int:
@@ -279,7 +279,7 @@ def serialize(self) -> str:
     def __repr__(self) -> str:
         return "" % self.name
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RPrimitive]:
         return isinstance(other, RPrimitive) and other.name == self.name
 
     def __hash__(self) -> int:
@@ -513,15 +513,15 @@ def __hash__(self) -> int:
 range_rprimitive: Final = RPrimitive("builtins.range", is_unboxed=False, is_refcounted=True)
 
 
-def is_tagged(rtype: RType) -> bool:
+def is_tagged(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is int_rprimitive or rtype is short_int_rprimitive
 
 
-def is_int_rprimitive(rtype: RType) -> bool:
+def is_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is int_rprimitive
 
 
-def is_short_int_rprimitive(rtype: RType) -> bool:
+def is_short_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is short_int_rprimitive
 
 
@@ -535,7 +535,7 @@ def is_int32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     )
 
 
-def is_int64_rprimitive(rtype: RType) -> bool:
+def is_int64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is int64_rprimitive or (
         rtype is c_pyssize_t_rprimitive and rtype._ctype == "int64_t"
     )
@@ -554,79 +554,79 @@ def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is uint8_rprimitive
 
 
-def is_uint32_rprimitive(rtype: RType) -> bool:
+def is_uint32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is uint32_rprimitive
 
 
-def is_uint64_rprimitive(rtype: RType) -> bool:
+def is_uint64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is uint64_rprimitive
 
 
-def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool:
+def is_c_py_ssize_t_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is c_pyssize_t_rprimitive
 
 
-def is_pointer_rprimitive(rtype: RType) -> bool:
+def is_pointer_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is pointer_rprimitive
 
 
-def is_float_rprimitive(rtype: RType) -> bool:
+def is_float_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.float"
 
 
-def is_bool_rprimitive(rtype: RType) -> bool:
+def is_bool_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bool"
 
 
-def is_bit_rprimitive(rtype: RType) -> bool:
+def is_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "bit"
 
 
-def is_bool_or_bit_rprimitive(rtype: RType) -> bool:
+def is_bool_or_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype)
 
 
-def is_object_rprimitive(rtype: RType) -> bool:
+def is_object_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.object"
 
 
-def is_none_rprimitive(rtype: RType) -> bool:
+def is_none_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.None"
 
 
-def is_list_rprimitive(rtype: RType) -> bool:
+def is_list_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.list"
 
 
-def is_dict_rprimitive(rtype: RType) -> bool:
+def is_dict_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.dict"
 
 
-def is_set_rprimitive(rtype: RType) -> bool:
+def is_set_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.set"
 
 
-def is_frozenset_rprimitive(rtype: RType) -> bool:
+def is_frozenset_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.frozenset"
 
 
-def is_str_rprimitive(rtype: RType) -> bool:
+def is_str_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.str"
 
 
-def is_bytes_rprimitive(rtype: RType) -> bool:
+def is_bytes_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bytes"
 
 
-def is_tuple_rprimitive(rtype: RType) -> bool:
+def is_tuple_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.tuple"
 
 
-def is_range_rprimitive(rtype: RType) -> bool:
+def is_range_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and rtype.name == "builtins.range"
 
 
-def is_sequence_rprimitive(rtype: RType) -> bool:
+def is_sequence_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]:
     return isinstance(rtype, RPrimitive) and (
         is_list_rprimitive(rtype)
         or is_tuple_rprimitive(rtype)
@@ -729,7 +729,7 @@ def __str__(self) -> str:
     def __repr__(self) -> str:
         return "" % ", ".join(repr(typ) for typ in self.types)
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RTuple]:
         return isinstance(other, RTuple) and self.types == other.types
 
     def __hash__(self) -> int:
@@ -862,7 +862,7 @@ def __repr__(self) -> str:
             ", ".join(name + ":" + repr(typ) for name, typ in zip(self.names, self.types)),
         )
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RStruct]:
         return (
             isinstance(other, RStruct)
             and self.name == other.name
@@ -932,7 +932,7 @@ def attr_type(self, name: str) -> RType:
     def __repr__(self) -> str:
         return "" % self.name
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RInstance]:
         return isinstance(other, RInstance) and other.name == self.name
 
     def __hash__(self) -> int:
@@ -986,7 +986,7 @@ def __str__(self) -> str:
         return "union[%s]" % ", ".join(str(item) for item in self.items)
 
     # We compare based on the set because order in a union doesn't matter
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RUnion]:
         return isinstance(other, RUnion) and self.items_set == other.items_set
 
     def __hash__(self) -> int:
@@ -1028,7 +1028,7 @@ def optional_value_type(rtype: RType) -> RType | None:
     return None
 
 
-def is_optional_type(rtype: RType) -> bool:
+def is_optional_type(rtype: RType) -> TypeGuard[RUnion]:
     """Is rtype an optional type with exactly two union items?"""
     return optional_value_type(rtype) is not None
 
@@ -1060,7 +1060,7 @@ def __str__(self) -> str:
     def __repr__(self) -> str:
         return f""
 
-    def __eq__(self, other: object) -> bool:
+    def __eq__(self, other: object) -> TypeGuard[RArray]:
         return (
             isinstance(other, RArray)
             and self.item_type == other.item_type

From 766c43c6d66b64a3e8e61cc537a26a86286de968 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Thu, 14 Aug 2025 13:08:23 -0400
Subject: [PATCH 1547/1617] [mypyc] feat: exact_dict_set_item_op (#19657)

This PR implements a `exact_dict_set_item_op` custom_op in preparation
for the addition of exact_dict_rprimitive. We don't actually need to
implement exact_dict_rprimitive before implementing this op as long as
we accept that it will not be used for CallExpr specialization. This is
okay for us now, as it is already addressed in the larger PR.
---
 mypyc/irbuild/classdef.py                   | 14 ++++++++------
 mypyc/irbuild/expression.py                 |  4 ++--
 mypyc/irbuild/function.py                   | 14 +++++++++-----
 mypyc/primitives/dict_ops.py                |  9 +++++++++
 mypyc/test-data/irbuild-basic.test          |  6 +++---
 mypyc/test-data/irbuild-classes.test        |  6 +++---
 mypyc/test-data/irbuild-generics.test       |  8 ++++----
 mypyc/test-data/irbuild-singledispatch.test |  4 ++--
 8 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 3282e836ac9e..72482710208a 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -66,7 +66,7 @@
 )
 from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
 from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator
-from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op
+from mypyc.primitives.dict_ops import dict_new_op, exact_dict_set_item_op
 from mypyc.primitives.generic_ops import (
     iter_op,
     next_op,
@@ -271,8 +271,8 @@ def finalize(self, ir: ClassIR) -> None:
         )
 
         # Add the non-extension class to the dict
-        self.builder.primitive_op(
-            dict_set_item_op,
+        self.builder.call_c(
+            exact_dict_set_item_op,
             [
                 self.builder.load_globals_dict(),
                 self.builder.load_str(self.cdef.name),
@@ -487,8 +487,10 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
     builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE))
 
     # Add it to the dict
-    builder.primitive_op(
-        dict_set_item_op, [builder.load_globals_dict(), builder.load_str(cdef.name), tp], cdef.line
+    builder.call_c(
+        exact_dict_set_item_op,
+        [builder.load_globals_dict(), builder.load_str(cdef.name), tp],
+        cdef.line,
     )
 
     return tp
@@ -672,7 +674,7 @@ def add_non_ext_class_attr_ann(
             typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line))
 
     key = builder.load_str(lvalue.name)
-    builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line)
+    builder.call_c(exact_dict_set_item_op, [non_ext.anns, key, typ], stmt.line)
 
 
 def add_non_ext_class_attr(
diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index 990c904dc447..c3d863fa96de 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -97,7 +97,7 @@
 )
 from mypyc.irbuild.specialize import apply_function_specialization, apply_method_specialization
 from mypyc.primitives.bytes_ops import bytes_slice_op
-from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, dict_set_item_op
+from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op
 from mypyc.primitives.generic_ops import iter_op
 from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op
 from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op
@@ -1030,7 +1030,7 @@ def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehe
     def gen_inner_stmts() -> None:
         k = builder.accept(o.key)
         v = builder.accept(o.value)
-        builder.primitive_op(dict_set_item_op, [builder.read(d), k, v], o.line)
+        builder.call_c(exact_dict_set_item_op, [builder.read(d), k, v], o.line)
 
     comprehension_helper(builder, loop_params, gen_inner_stmts, o.line)
     return builder.read(d)
diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py
index d70b16475503..f0fc424aea54 100644
--- a/mypyc/irbuild/function.py
+++ b/mypyc/irbuild/function.py
@@ -76,7 +76,11 @@
 )
 from mypyc.irbuild.generator import gen_generator_func, gen_generator_func_body
 from mypyc.irbuild.targets import AssignmentTarget
-from mypyc.primitives.dict_ops import dict_get_method_with_none, dict_new_op, dict_set_item_op
+from mypyc.primitives.dict_ops import (
+    dict_get_method_with_none,
+    dict_new_op,
+    exact_dict_set_item_op,
+)
 from mypyc.primitives.generic_ops import py_setattr_op
 from mypyc.primitives.misc_ops import register_function
 from mypyc.primitives.registry import builtin_names
@@ -123,8 +127,8 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
 
     if decorated_func is not None:
         # Set the callable object representing the decorated function as a global.
-        builder.primitive_op(
-            dict_set_item_op,
+        builder.call_c(
+            exact_dict_set_item_op,
             [builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func],
             decorated_func.line,
         )
@@ -826,7 +830,7 @@ def generate_singledispatch_dispatch_function(
     find_impl = builder.load_module_attr_by_fullname("functools._find_impl", line)
     registry = load_singledispatch_registry(builder, dispatch_func_obj, line)
     uncached_impl = builder.py_call(find_impl, [arg_type, registry], line)
-    builder.primitive_op(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line)
+    builder.call_c(exact_dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line)
     builder.assign(impl_to_use, uncached_impl, line)
     builder.goto(call_func)
 
@@ -1003,7 +1007,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None:
         registry = load_singledispatch_registry(builder, dispatch_func_obj, line)
         for typ in types:
             loaded_type = load_type(builder, typ, None, line)
-            builder.primitive_op(dict_set_item_op, [registry, loaded_type, to_insert], line)
+            builder.call_c(exact_dict_set_item_op, [registry, loaded_type, to_insert], line)
         dispatch_cache = builder.builder.get_attr(
             dispatch_func_obj, "dispatch_cache", dict_rprimitive, line
         )
diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py
index ac928bb0eb50..21f8a4badca3 100644
--- a/mypyc/primitives/dict_ops.py
+++ b/mypyc/primitives/dict_ops.py
@@ -98,6 +98,15 @@
     error_kind=ERR_NEG_INT,
 )
 
+# dict[key] = value (exact dict only, no subclasses)
+# NOTE: this is currently for internal use only, and not used for CallExpr specialization
+exact_dict_set_item_op = custom_op(
+    arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive],
+    return_type=c_int_rprimitive,
+    c_function_name="PyDict_SetItem",
+    error_kind=ERR_NEG_INT,
+)
+
 # key in dict
 binary_op(
     name="in",
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index 4a7d315ec836..8d981db2b391 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1993,7 +1993,7 @@ L6:
     r13 = CPyTagged_Multiply(x, x)
     r14 = box(int, x)
     r15 = box(int, r13)
-    r16 = CPyDict_SetItem(r0, r14, r15)
+    r16 = PyDict_SetItem(r0, r14, r15)
     r17 = r16 >= 0 :: signed
 L7:
     r18 = r6 + 1
@@ -2620,7 +2620,7 @@ L0:
     d = r14
     r15 = __main__.globals :: static
     r16 = 'd'
-    r17 = CPyDict_SetItem(r15, r16, r14)
+    r17 = PyDict_SetItem(r15, r16, r14)
     r18 = r17 >= 0 :: signed
     r19 = 'c'
     r20 = builtins :: module
@@ -2693,7 +2693,7 @@ L2:
     keep_alive r17
     r24 = __main__.globals :: static
     r25 = 'c'
-    r26 = CPyDict_SetItem(r24, r25, r23)
+    r26 = PyDict_SetItem(r24, r25, r23)
     r27 = r26 >= 0 :: signed
     return 1
 
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 8a2cc42fbb0f..c7bf5de852a8 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -303,7 +303,7 @@ L2:
     __main__.C = r27 :: type
     r33 = __main__.globals :: static
     r34 = 'C'
-    r35 = CPyDict_SetItem(r33, r34, r27)
+    r35 = PyDict_SetItem(r33, r34, r27)
     r36 = r35 >= 0 :: signed
     r37 =  :: object
     r38 = '__main__'
@@ -316,7 +316,7 @@ L2:
     __main__.S = r40 :: type
     r45 = __main__.globals :: static
     r46 = 'S'
-    r47 = CPyDict_SetItem(r45, r46, r40)
+    r47 = PyDict_SetItem(r45, r46, r40)
     r48 = r47 >= 0 :: signed
     r49 = __main__.C :: type
     r50 = __main__.S :: type
@@ -340,7 +340,7 @@ L2:
     __main__.D = r61 :: type
     r68 = __main__.globals :: static
     r69 = 'D'
-    r70 = CPyDict_SetItem(r68, r69, r61)
+    r70 = PyDict_SetItem(r68, r69, r61)
     r71 = r70 >= 0 :: signed
     return 1
 
diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test
index 4e9391e0d59e..783492e63e47 100644
--- a/mypyc/test-data/irbuild-generics.test
+++ b/mypyc/test-data/irbuild-generics.test
@@ -351,7 +351,7 @@ L17:
     k = r49
     v = r50
     r51 = box(int, v)
-    r52 = CPyDict_SetItem(r40, k, r51)
+    r52 = PyDict_SetItem(r40, k, r51)
     r53 = r52 >= 0 :: signed
 L18:
     r54 = CPyDict_CheckSize(m, r42)
@@ -493,7 +493,7 @@ L17:
     r49 = cast(union[int, str], r47)
     k = r48
     v = r49
-    r50 = CPyDict_SetItem(r39, k, v)
+    r50 = PyDict_SetItem(r39, k, v)
     r51 = r50 >= 0 :: signed
 L18:
     r52 = CPyDict_CheckSize(m, r41)
@@ -630,7 +630,7 @@ L17:
     r47 = cast(str, r45)
     k = r47
     v = r46
-    r48 = CPyDict_SetItem(r38, k, v)
+    r48 = PyDict_SetItem(r38, k, v)
     r49 = r48 >= 0 :: signed
 L18:
     r50 = CPyDict_CheckSize(t, r40)
@@ -742,7 +742,7 @@ L6:
     r17 = cast(str, r15)
     k = r17
     v = r16
-    r18 = CPyDict_SetItem(r8, k, v)
+    r18 = PyDict_SetItem(r8, k, v)
     r19 = r18 >= 0 :: signed
 L7:
     r20 = CPyDict_CheckSize(kwargs, r10)
diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test
index ef11ae04dc64..1060ee63c57d 100644
--- a/mypyc/test-data/irbuild-singledispatch.test
+++ b/mypyc/test-data/irbuild-singledispatch.test
@@ -76,7 +76,7 @@ L2:
     r12 = load_address r11
     r13 = PyObject_Vectorcall(r9, r12, 2, 0)
     keep_alive r1, r10
-    r14 = CPyDict_SetItem(r2, r1, r13)
+    r14 = PyDict_SetItem(r2, r1, r13)
     r15 = r14 >= 0 :: signed
     r6 = r13
 L3:
@@ -214,7 +214,7 @@ L2:
     r12 = load_address r11
     r13 = PyObject_Vectorcall(r9, r12, 2, 0)
     keep_alive r1, r10
-    r14 = CPyDict_SetItem(r2, r1, r13)
+    r14 = PyDict_SetItem(r2, r1, r13)
     r15 = r14 >= 0 :: signed
     r6 = r13
 L3:

From 74927d5cbbcdeaefb2ef3c36c8b37a331050f6e5 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 15 Aug 2025 00:27:09 +0200
Subject: [PATCH 1548/1617] Include `ambiguous` into `UninhabitedType` identity
 (#19648)

Fixes #19641, but also reveals a test that was only passing by
coincidence. This inference has never worked correctly:

```python
from typing import Mapping, Never

d: dict[Never, Never]
def run() -> Mapping[str, int]:
    return d
```

As discussed, I updated the test to expect failure in that case. We
should only special-case inline collection literals for that, everything
else (including locals) can cause undesired false negatives.
---
 mypy/test/testsolve.py                      | 12 +++++++-----
 mypy/test/typefixture.py                    |  2 ++
 mypy/types.py                               |  4 ++--
 test-data/unit/check-generic-subtyping.test | 14 ++++++++++++++
 test-data/unit/check-inference.test         | 10 +++++++++-
 5 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py
index 6566b03ef5e9..d60b2cb3fcc5 100644
--- a/mypy/test/testsolve.py
+++ b/mypy/test/testsolve.py
@@ -64,12 +64,14 @@ def test_multiple_variables(self) -> None:
         )
 
     def test_no_constraints_for_var(self) -> None:
-        self.assert_solve([self.fx.t], [], [self.fx.uninhabited])
-        self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited])
+        self.assert_solve([self.fx.t], [], [self.fx.a_uninhabited])
+        self.assert_solve(
+            [self.fx.t, self.fx.s], [], [self.fx.a_uninhabited, self.fx.a_uninhabited]
+        )
         self.assert_solve(
             [self.fx.t, self.fx.s],
             [self.supc(self.fx.s, self.fx.a)],
-            [self.fx.uninhabited, self.fx.a],
+            [self.fx.a_uninhabited, self.fx.a],
         )
 
     def test_simple_constraints_with_dynamic_type(self) -> None:
@@ -116,7 +118,7 @@ def test_poly_no_constraints(self) -> None:
         self.assert_solve(
             [self.fx.t, self.fx.u],
             [],
-            [self.fx.uninhabited, self.fx.uninhabited],
+            [self.fx.a_uninhabited, self.fx.a_uninhabited],
             allow_polymorphic=True,
         )
 
@@ -152,7 +154,7 @@ def test_poly_free_pair_with_bounds_uninhabited(self) -> None:
         self.assert_solve(
             [self.fx.ub, self.fx.uc],
             [self.subc(self.fx.ub, self.fx.uc)],
-            [self.fx.uninhabited, self.fx.uninhabited],
+            [self.fx.a_uninhabited, self.fx.a_uninhabited],
             [],
             allow_polymorphic=True,
         )
diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py
index d6c904732b17..0defcdaebc99 100644
--- a/mypy/test/typefixture.py
+++ b/mypy/test/typefixture.py
@@ -78,6 +78,8 @@ def make_type_var(
         self.anyt = AnyType(TypeOfAny.special_form)
         self.nonet = NoneType()
         self.uninhabited = UninhabitedType()
+        self.a_uninhabited = UninhabitedType()
+        self.a_uninhabited.ambiguous = True
 
         # Abstract class TypeInfos
 
diff --git a/mypy/types.py b/mypy/types.py
index d7dd3e1f2dce..26c5b474ba6c 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1236,10 +1236,10 @@ def accept(self, visitor: TypeVisitor[T]) -> T:
         return visitor.visit_uninhabited_type(self)
 
     def __hash__(self) -> int:
-        return hash(UninhabitedType)
+        return hash((UninhabitedType, self.ambiguous))
 
     def __eq__(self, other: object) -> bool:
-        return isinstance(other, UninhabitedType)
+        return isinstance(other, UninhabitedType) and other.ambiguous == self.ambiguous
 
     def serialize(self) -> JsonDict:
         return {".class": "UninhabitedType"}
diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test
index f65ef3975852..ee5bcccf8ace 100644
--- a/test-data/unit/check-generic-subtyping.test
+++ b/test-data/unit/check-generic-subtyping.test
@@ -753,6 +753,20 @@ s, s = Nums() # E: Incompatible types in assignment (expression has type "int",
 [builtins fixtures/for.pyi]
 [out]
 
+[case testUninhabitedCacheChecksAmbiguous]
+# https://github.com/python/mypy/issues/19641
+from typing import Mapping, Never, TypeVar
+
+M = TypeVar("M", bound=Mapping[str,object])
+
+def get(arg: M, /) -> M:
+    return arg
+
+get({})
+
+def upcast(d: dict[Never, Never]) -> Mapping[str, object]:
+    return d  # E: Incompatible return value type (got "dict[Never, Never]", expected "Mapping[str, object]")
+[builtins fixtures/dict.pyi]
 
 -- Variance
 -- --------
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 53efcc0d22e3..63278d6c4547 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -3781,10 +3781,18 @@ from typing import Any, Dict, NoReturn, NoReturn, Union
 
 def foo() -> Union[Dict[str, Any], Dict[int, Any]]:
     return {}
+[builtins fixtures/dict.pyi]
+
+[case testExistingEmptyCollectionDoesNotUpcast]
+from typing import Any, Dict, NoReturn, NoReturn, Union
 
 empty: Dict[NoReturn, NoReturn]
+
+def foo() -> Dict[str, Any]:
+    return empty  # E: Incompatible return value type (got "dict[Never, Never]", expected "dict[str, Any]")
+
 def bar() -> Union[Dict[str, Any], Dict[int, Any]]:
-    return empty
+    return empty  # E: Incompatible return value type (got "dict[Never, Never]", expected "Union[dict[str, Any], dict[int, Any]]")
 [builtins fixtures/dict.pyi]
 
 [case testUpperBoundInferenceFallbackNotOverused]

From cb77a9b36775742b726e856af628e2dcc8e714c2 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 15 Aug 2025 00:29:13 +0200
Subject: [PATCH 1549/1617] Reset to previous statement when leaving `return`
 in semanal (#19642)

Unlike other statements, return can be found in lambdas, and so is not
immediately followed by another statement to check or EOF. We need to
restore the previous value to continue checking it.

Fixes #19632
---
 mypy/semanal.py                   |  2 ++
 test-data/unit/check-classes.test | 15 +++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index bebabfd3233c..ebd9515d832d 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5332,6 +5332,7 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None:
         s.expr.accept(self)
 
     def visit_return_stmt(self, s: ReturnStmt) -> None:
+        old = self.statement
         self.statement = s
         if not self.is_func_scope():
             self.fail('"return" outside function', s)
@@ -5339,6 +5340,7 @@ def visit_return_stmt(self, s: ReturnStmt) -> None:
             self.fail('"return" not allowed in except* block', s, serious=True)
         if s.expr:
             s.expr.accept(self)
+        self.statement = old
 
     def visit_raise_stmt(self, s: RaiseStmt) -> None:
         self.statement = s
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 62f538260fff..23dbe2bc07af 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -9258,3 +9258,18 @@ main:5: note:     def __eq__(self, other: object) -> bool:
 main:5: note:         if not isinstance(other, C):
 main:5: note:             return NotImplemented
 main:5: note:         return 
+
+[case testLambdaInAttributeCallValue]
+# https://github.com/python/mypy/issues/19632
+import foo
+
+def nop(fn: object) -> foo.Bar:
+    return foo.Bar()
+
+class Bar:
+    foo: foo.Bar = nop(
+        lambda: 0
+    )
+[file foo.py]
+class Bar:
+    ...

From 8e66cf2f56ea3892e8d56fc6f7f8f97d81734c8e Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Fri, 15 Aug 2025 12:16:01 +0200
Subject: [PATCH 1550/1617] fix: prevent false positive "untyped after
 decorator transformation" after deferral (#19591)

Fixes #19589
---
 mypy/checker.py                 |  2 +-
 test-data/unit/check-flags.test | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 206abae6adec..47c72924bf3c 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -5502,7 +5502,7 @@ def visit_with_stmt(self, s: WithStmt) -> None:
             self.accept(s.body)
 
     def check_untyped_after_decorator(self, typ: Type, func: FuncDef) -> None:
-        if not self.options.disallow_any_decorated or self.is_stub:
+        if not self.options.disallow_any_decorated or self.is_stub or self.current_node_deferred:
             return
 
         if mypy.checkexpr.has_any_type(typ):
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
index bb64bb44d282..8eec979029d0 100644
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -1116,6 +1116,39 @@ def f(x: Any) -> Any:  # E: Function is untyped after decorator transformation
 def h(x):  # E: Function is untyped after decorator transformation
     pass
 [builtins fixtures/list.pyi]
+
+[case testDisallowAnyDecoratedUnannotatedDecoratorDeferred1]
+# flags: --disallow-any-decorated
+from typing import Callable
+
+def d(f: Callable[[int], None]) -> Callable[[int], None]:
+    return f
+
+def wrapper() -> None:
+    if c:
+        @d
+        def h(x):
+            pass
+
+c = [1]
+[builtins fixtures/list.pyi]
+
+[case testDisallowAnyDecoratedUnannotatedDecoratorDeferred2]
+# flags: --disallow-any-decorated
+from typing import Callable
+
+def d(f: Callable[[int], None]) -> Callable[[int], None]:
+    return f
+
+c = 1  # no deferral - check that the previous testcase is valid
+
+def wrapper() -> None:
+    if c:
+        @d
+        def h(x):
+            pass
+[builtins fixtures/list.pyi]
+
 [case testDisallowAnyDecoratedErrorIsReportedOnlyOnce]
 # flags: --disallow-any-decorated
 

From ac646c0c55e790388609065400084fe8e189f86c Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Fri, 15 Aug 2025 08:07:51 -0400
Subject: [PATCH 1551/1617] [mypyc] feat: optimize f-string building from Final
 values (#19611)

We can do some extra constant folding in cases like this:

```python
from typing import Final

BASE_URL: Final = "https://example.com"
PORT: Final = 1234

def get_url(endpoint: str) -> str:
    return f"{BASE_URL}:{PORT}/{endpoint}"
```

which should generate the same C code as

```python
def get_url(endpoint: str) -> str:
    return f"https://example.com:1234/{endpoint}"
```

This PR makes it so.
---
 mypyc/irbuild/format_str_tokenizer.py |  6 ++--
 mypyc/irbuild/specialize.py           | 17 ++++++++++++
 mypyc/test-data/irbuild-str.test      | 40 +++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py
index eaa4027ed768..5a35900006d2 100644
--- a/mypyc/irbuild/format_str_tokenizer.py
+++ b/mypyc/irbuild/format_str_tokenizer.py
@@ -12,7 +12,7 @@
 )
 from mypy.errors import Errors
 from mypy.messages import MessageBuilder
-from mypy.nodes import Context, Expression
+from mypy.nodes import Context, Expression, StrExpr
 from mypy.options import Options
 from mypyc.ir.ops import Integer, Value
 from mypyc.ir.rtypes import (
@@ -143,7 +143,9 @@ def convert_format_expr_to_str(
     for x, format_op in zip(exprs, format_ops):
         node_type = builder.node_type(x)
         if format_op == FormatOp.STR:
-            if is_str_rprimitive(node_type):
+            if is_str_rprimitive(node_type) or isinstance(
+                x, StrExpr
+            ):  # NOTE: why does mypyc think our fake StrExprs are not str rprimitives?
                 var_str = builder.accept(x)
             elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type):
                 var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line)
diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py
index 748cda1256a7..845968222234 100644
--- a/mypyc/irbuild/specialize.py
+++ b/mypyc/irbuild/specialize.py
@@ -31,6 +31,7 @@
     RefExpr,
     StrExpr,
     TupleExpr,
+    Var,
 )
 from mypy.types import AnyType, TypeOfAny
 from mypyc.ir.ops import (
@@ -710,6 +711,22 @@ def translate_fstring(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va
                 format_ops.append(FormatOp.STR)
                 exprs.append(item.args[0])
 
+        def get_literal_str(expr: Expression) -> str | None:
+            if isinstance(expr, StrExpr):
+                return expr.value
+            elif isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_final:
+                return str(expr.node.final_value)
+            return None
+
+        for i in range(len(exprs) - 1):
+            while (
+                len(exprs) >= i + 2
+                and (first := get_literal_str(exprs[i])) is not None
+                and (second := get_literal_str(exprs[i + 1])) is not None
+            ):
+                exprs = [*exprs[:i], StrExpr(first + second), *exprs[i + 2 :]]
+                format_ops = [*format_ops[:i], FormatOp.STR, *format_ops[i + 2 :]]
+
         substitutions = convert_format_expr_to_str(builder, format_ops, exprs, expr.line)
         if substitutions is None:
             return None
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index 3e69325a454b..24807510193d 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -605,3 +605,43 @@ L3:
     r6 = r7
 L4:
     return r6
+
+[case testFStringFromConstants]
+from typing import Final
+string: Final = "abc"
+integer: Final = 123
+floating: Final = 3.14
+boolean: Final = True
+
+def test(x: str) -> str:
+    return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}"
+def test2(x: str) -> str:
+    return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}"
+def test3(x: str) -> str:
+    return f"{x}{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}"
+
+[out]
+def test(x):
+    x, r0, r1, r2, r3 :: str
+L0:
+    r0 = 'abc1233.14True'
+    r1 = 'True3.14123abc'
+    r2 = 'abc1233.14True'
+    r3 = CPyStr_Build(5, r0, x, r1, x, r2)
+    return r3
+def test2(x):
+    x, r0, r1, r2, r3 :: str
+L0:
+    r0 = 'abc1233.14True'
+    r1 = 'True3.14123abc'
+    r2 = 'abc1233.14True'
+    r3 = CPyStr_Build(6, r0, x, r1, x, r2, x)
+    return r3
+def test3(x):
+    x, r0, r1, r2, r3 :: str
+L0:
+    r0 = 'abc1233.14True'
+    r1 = 'True3.14123abc'
+    r2 = 'abc1233.14True'
+    r3 = CPyStr_Build(7, x, r0, x, r1, x, r2, x)
+    return r3

From 206b739489e7448a7a330b385c21053e0ddd7eab Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 15 Aug 2025 15:46:53 +0100
Subject: [PATCH 1552/1617] [mypyc] Remove unreachable code (#19667)

See #19050 for context.
---
 mypyc/test/test_cheader.py | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py
index 7ab055c735ad..ec9e2c4cf450 100644
--- a/mypyc/test/test_cheader.py
+++ b/mypyc/test/test_cheader.py
@@ -7,7 +7,6 @@
 import re
 import unittest
 
-from mypyc.ir.ops import PrimitiveDescription
 from mypyc.primitives import registry
 
 
@@ -32,8 +31,6 @@ def check_name(name: str) -> None:
             registry.function_ops.values(),
         ]:
             for ops in values:
-                if isinstance(ops, PrimitiveDescription):
-                    ops = [ops]
                 for op in ops:
                     if op.c_function_name is not None:
                         check_name(op.c_function_name)

From e30128b1b649bba3c4aab198fe9d35c138c5eeb1 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 15 Aug 2025 17:51:45 +0200
Subject: [PATCH 1553/1617] Sort arguments in TypedDict overlap error message
 (#19666)

Discovered in
https://github.com/python/mypy/pull/19664#issuecomment-3191182315.
Sets are unsorted which could make the primer output differ
unnecessarily.
---
 mypy/semanal.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index ebd9515d832d..eef658d9300b 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1059,7 +1059,7 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType
         # It is OK for TypedDict to have a key named 'kwargs'.
         overlap.discard(typ.arg_names[-1])
         if overlap:
-            overlapped = ", ".join([f'"{name}"' for name in overlap])
+            overlapped = ", ".join([f'"{name}"' for name in sorted(filter(None, overlap))])
             self.fail(f"Overlap between argument names and ** TypedDict items: {overlapped}", defn)
             new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)]
             return typ.copy_modified(arg_types=new_arg_types)

From 3fcfcb8d9e73facab074b6f7dbba76c2fe1212fc Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 16 Aug 2025 11:09:20 +0200
Subject: [PATCH 1554/1617] Sync typeshed (#19664)

Source commit:

https://github.com/python/typeshed/commit/554701e9b6107be8e0bda4955a309d94fefc22ae
---
 ...e-of-LiteralString-in-builtins-13743.patch |   14 +-
 mypy/typeshed/stdlib/_contextvars.pyi         |    2 +-
 mypy/typeshed/stdlib/_frozen_importlib.pyi    |   11 +
 .../stdlib/_frozen_importlib_external.pyi     |   17 +-
 mypy/typeshed/stdlib/_interpchannels.pyi      |   12 +-
 mypy/typeshed/stdlib/_interpreters.pyi        |    4 +-
 mypy/typeshed/stdlib/_socket.pyi              | 1053 ++++++++---------
 mypy/typeshed/stdlib/_ssl.pyi                 |  212 ++--
 mypy/typeshed/stdlib/_tkinter.pyi             |    3 +-
 mypy/typeshed/stdlib/array.pyi                |   29 +-
 mypy/typeshed/stdlib/ast.pyi                  |    6 +-
 mypy/typeshed/stdlib/asyncio/coroutines.pyi   |    3 +-
 mypy/typeshed/stdlib/asyncio/trsock.pyi       |   31 +-
 mypy/typeshed/stdlib/binascii.pyi             |    6 +-
 mypy/typeshed/stdlib/builtins.pyi             |    4 +
 .../stdlib/concurrent/futures/interpreter.pyi |   59 +-
 mypy/typeshed/stdlib/configparser.pyi         |   23 +-
 mypy/typeshed/stdlib/fcntl.pyi                |  166 +--
 mypy/typeshed/stdlib/gettext.pyi              |   19 +-
 mypy/typeshed/stdlib/glob.pyi                 |   12 +-
 mypy/typeshed/stdlib/gzip.pyi                 |    3 +-
 mypy/typeshed/stdlib/html/parser.pyi          |    6 +-
 mypy/typeshed/stdlib/importlib/__init__.pyi   |    2 +
 mypy/typeshed/stdlib/importlib/_abc.pyi       |    5 +
 mypy/typeshed/stdlib/importlib/abc.pyi        |    4 +
 .../stdlib/importlib/metadata/__init__.pyi    |    5 +-
 mypy/typeshed/stdlib/importlib/util.pyi       |   14 +-
 mypy/typeshed/stdlib/inspect.pyi              |    7 +-
 mypy/typeshed/stdlib/locale.pyi               |    8 +-
 mypy/typeshed/stdlib/pathlib/__init__.pyi     |   10 +-
 mypy/typeshed/stdlib/pkgutil.pyi              |   14 +-
 mypy/typeshed/stdlib/pty.pyi                  |   12 +-
 mypy/typeshed/stdlib/re.pyi                   |    8 +-
 mypy/typeshed/stdlib/smtpd.pyi                |    3 +-
 mypy/typeshed/stdlib/socket.pyi               |    4 +-
 mypy/typeshed/stdlib/ssl.pyi                  |  160 +--
 mypy/typeshed/stdlib/sys/__init__.pyi         |    8 +
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |    1 +
 mypy/typeshed/stdlib/turtle.pyi               |    4 +-
 mypy/typeshed/stdlib/typing.pyi               |   32 +-
 mypy/typeshed/stdlib/typing_extensions.pyi    |    4 +-
 mypy/typeshed/stdlib/urllib/request.pyi       |    4 +-
 mypy/typeshed/stdlib/winreg.pyi               |    2 +-
 mypy/typeshed/stdlib/zipimport.pyi            |   16 +-
 mypy/typeshed/stdlib/zlib.pyi                 |   12 +-
 45 files changed, 1104 insertions(+), 930 deletions(-)

diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
index 9d0cb5271e7d..a47d5db3cd22 100644
--- a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
+++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch
@@ -1,4 +1,4 @@
-From e6995c91231e1915eba43a29a22dd4cbfaf9e08e Mon Sep 17 00:00:00 2001
+From 805d7fc06a8bee350959512e0908a18a87b7f8c2 Mon Sep 17 00:00:00 2001
 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
 Date: Mon, 26 Sep 2022 12:55:07 -0700
 Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
@@ -8,7 +8,7 @@ Subject: [PATCH] Remove use of LiteralString in builtins (#13743)
  1 file changed, 1 insertion(+), 99 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index 00728f42d..ea77a730f 100644
+index c7ab95482..3e93da36e 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
 @@ -63,7 +63,6 @@ from typing import (  # noqa: Y022,UP035
@@ -19,7 +19,7 @@ index 00728f42d..ea77a730f 100644
      ParamSpec,
      Self,
      TypeAlias,
-@@ -453,31 +452,16 @@ class str(Sequence[str]):
+@@ -468,31 +467,16 @@ class str(Sequence[str]):
      def __new__(cls, object: object = ...) -> Self: ...
      @overload
      def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
@@ -51,7 +51,7 @@ index 00728f42d..ea77a730f 100644
      def format(self, *args: object, **kwargs: object) -> str: ...
      def format_map(self, mapping: _FormatMapMapping, /) -> str: ...
      def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-@@ -493,98 +477,34 @@ class str(Sequence[str]):
+@@ -508,98 +492,34 @@ class str(Sequence[str]):
      def isspace(self) -> bool: ...
      def istitle(self) -> bool: ...
      def isupper(self) -> bool: ...
@@ -150,7 +150,7 @@ index 00728f42d..ea77a730f 100644
      def zfill(self, width: SupportsIndex, /) -> str: ...  # type: ignore[misc]
      @staticmethod
      @overload
-@@ -595,39 +515,21 @@ class str(Sequence[str]):
+@@ -610,39 +530,21 @@ class str(Sequence[str]):
      @staticmethod
      @overload
      def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...
@@ -190,7 +190,7 @@ index 00728f42d..ea77a730f 100644
 -    @overload
      def __rmul__(self, value: SupportsIndex, /) -> str: ...  # type: ignore[misc]
      def __getnewargs__(self) -> tuple[str]: ...
- 
+     def __format__(self, format_spec: str, /) -> str: ...
 -- 
-2.49.0
+2.50.1
 
diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi
index e2e2e4df9d08..0ddeca7882cd 100644
--- a/mypy/typeshed/stdlib/_contextvars.pyi
+++ b/mypy/typeshed/stdlib/_contextvars.pyi
@@ -39,7 +39,7 @@ class Token(Generic[_T]):
     if sys.version_info >= (3, 14):
         def __enter__(self) -> Self: ...
         def __exit__(
-            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
+            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /
         ) -> None: ...
 
 def copy_context() -> Context: ...
diff --git a/mypy/typeshed/stdlib/_frozen_importlib.pyi b/mypy/typeshed/stdlib/_frozen_importlib.pyi
index 3dbc8c6b52f0..93aaed82e2e1 100644
--- a/mypy/typeshed/stdlib/_frozen_importlib.pyi
+++ b/mypy/typeshed/stdlib/_frozen_importlib.pyi
@@ -6,6 +6,7 @@ from _typeshed.importlib import LoaderProtocol
 from collections.abc import Mapping, Sequence
 from types import ModuleType
 from typing import Any, ClassVar
+from typing_extensions import deprecated
 
 # Signature of `builtins.__import__` should be kept identical to `importlib.__import__`
 def __import__(
@@ -49,6 +50,7 @@ class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader)
     # MetaPathFinder
     if sys.version_info < (3, 12):
         @classmethod
+        @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
         def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ...
 
     @classmethod
@@ -67,6 +69,10 @@ class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader)
     # Loader
     if sys.version_info < (3, 12):
         @staticmethod
+        @deprecated(
+            "Deprecated since Python 3.4; removed in Python 3.12. "
+            "The module spec is now used by the import machinery to generate a module repr."
+        )
         def module_repr(module: types.ModuleType) -> str: ...
     if sys.version_info >= (3, 10):
         @staticmethod
@@ -83,6 +89,7 @@ class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader):
     # MetaPathFinder
     if sys.version_info < (3, 12):
         @classmethod
+        @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
         def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ...
 
     @classmethod
@@ -101,6 +108,10 @@ class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader):
     # Loader
     if sys.version_info < (3, 12):
         @staticmethod
+        @deprecated(
+            "Deprecated since Python 3.4; removed in Python 3.12. "
+            "The module spec is now used by the import machinery to generate a module repr."
+        )
         def module_repr(m: types.ModuleType) -> str: ...
     if sys.version_info >= (3, 10):
         @staticmethod
diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
index edad50a8d858..80eebe45a7d4 100644
--- a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
+++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
@@ -43,6 +43,7 @@ def spec_from_file_location(
 class WindowsRegistryFinder(importlib.abc.MetaPathFinder):
     if sys.version_info < (3, 12):
         @classmethod
+        @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
         def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ...
 
     @classmethod
@@ -70,6 +71,7 @@ class PathFinder(importlib.abc.MetaPathFinder):
     ) -> ModuleSpec | None: ...
     if sys.version_info < (3, 12):
         @classmethod
+        @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
         def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ...
 
 SOURCE_SUFFIXES: list[str]
@@ -158,7 +160,10 @@ if sys.version_info >= (3, 11):
         def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ...
         if sys.version_info < (3, 12):
             @staticmethod
-            @deprecated("module_repr() is deprecated, and has been removed in Python 3.12")
+            @deprecated(
+                "Deprecated since Python 3.4; removed in Python 3.12. "
+                "The module spec is now used by the import machinery to generate a module repr."
+            )
             def module_repr(module: types.ModuleType) -> str: ...
 
     _NamespaceLoader = NamespaceLoader
@@ -176,12 +181,18 @@ else:
         def load_module(self, fullname: str) -> types.ModuleType: ...
         if sys.version_info >= (3, 10):
             @staticmethod
-            @deprecated("module_repr() is deprecated, and has been removed in Python 3.12")
+            @deprecated(
+                "Deprecated since Python 3.4; removed in Python 3.12. "
+                "The module spec is now used by the import machinery to generate a module repr."
+            )
             def module_repr(module: types.ModuleType) -> str: ...
             def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ...
         else:
             @classmethod
-            @deprecated("module_repr() is deprecated, and has been removed in Python 3.12")
+            @deprecated(
+                "Deprecated since Python 3.4; removed in Python 3.12. "
+                "The module spec is now used by the import machinery to generate a module repr."
+            )
             def module_repr(cls, module: types.ModuleType) -> str: ...
 
 if sys.version_info >= (3, 13):
diff --git a/mypy/typeshed/stdlib/_interpchannels.pyi b/mypy/typeshed/stdlib/_interpchannels.pyi
index c03496044df0..a631a6f16616 100644
--- a/mypy/typeshed/stdlib/_interpchannels.pyi
+++ b/mypy/typeshed/stdlib/_interpchannels.pyi
@@ -17,15 +17,15 @@ class ChannelID:
     def send(self) -> Self: ...
     @property
     def recv(self) -> Self: ...
-    def __eq__(self, other: object) -> bool: ...
-    def __ge__(self, other: ChannelID) -> bool: ...
-    def __gt__(self, other: ChannelID) -> bool: ...
+    def __eq__(self, other: object, /) -> bool: ...
+    def __ge__(self, other: ChannelID, /) -> bool: ...
+    def __gt__(self, other: ChannelID, /) -> bool: ...
     def __hash__(self) -> int: ...
     def __index__(self) -> int: ...
     def __int__(self) -> int: ...
-    def __le__(self, other: ChannelID) -> bool: ...
-    def __lt__(self, other: ChannelID) -> bool: ...
-    def __ne__(self, other: object) -> bool: ...
+    def __le__(self, other: ChannelID, /) -> bool: ...
+    def __lt__(self, other: ChannelID, /) -> bool: ...
+    def __ne__(self, other: object, /) -> bool: ...
 
 @final
 class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, int]):
diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi
index 54fc0e39d239..f89a24e7d85c 100644
--- a/mypy/typeshed/stdlib/_interpreters.pyi
+++ b/mypy/typeshed/stdlib/_interpreters.pyi
@@ -34,8 +34,8 @@ def exec(
 def call(
     id: SupportsIndex,
     callable: Callable[..., _R],
-    args: tuple[object, ...] | None = None,
-    kwargs: dict[str, object] | None = None,
+    args: tuple[Any, ...] | None = None,
+    kwargs: dict[str, Any] | None = None,
     *,
     restrict: bool = False,
 ) -> tuple[_R, types.SimpleNamespace]: ...
diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi
index 41fdce87ec14..9c153a3a6ba0 100644
--- a/mypy/typeshed/stdlib/_socket.pyi
+++ b/mypy/typeshed/stdlib/_socket.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import ReadableBuffer, WriteableBuffer
 from collections.abc import Iterable
 from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout
-from typing import Any, SupportsIndex, overload
+from typing import Any, Final, SupportsIndex, overload
 from typing_extensions import CapsuleType, TypeAlias
 
 _CMSG: TypeAlias = tuple[int, int, bytes]
@@ -19,23 +19,23 @@ _RetAddress: TypeAlias = Any
 # https://docs.python.org/3/library/socket.html#constants
 
 if sys.platform != "win32":
-    AF_UNIX: int
+    AF_UNIX: Final[int]
 
-AF_INET: int
-AF_INET6: int
+AF_INET: Final[int]
+AF_INET6: Final[int]
 
-AF_UNSPEC: int
+AF_UNSPEC: Final[int]
 
-SOCK_STREAM: int
-SOCK_DGRAM: int
-SOCK_RAW: int
-SOCK_RDM: int
-SOCK_SEQPACKET: int
+SOCK_STREAM: Final[int]
+SOCK_DGRAM: Final[int]
+SOCK_RAW: Final[int]
+SOCK_RDM: Final[int]
+SOCK_SEQPACKET: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.27
-    SOCK_CLOEXEC: int
-    SOCK_NONBLOCK: int
+    SOCK_CLOEXEC: Final[int]
+    SOCK_NONBLOCK: Final[int]
 
 # --------------------
 # Many constants of these forms, documented in the Unix documentation on
@@ -56,329 +56,329 @@ if sys.platform == "linux":
 # TCP_*
 # --------------------
 
-SO_ACCEPTCONN: int
-SO_BROADCAST: int
-SO_DEBUG: int
-SO_DONTROUTE: int
-SO_ERROR: int
-SO_KEEPALIVE: int
-SO_LINGER: int
-SO_OOBINLINE: int
-SO_RCVBUF: int
-SO_RCVLOWAT: int
-SO_RCVTIMEO: int
-SO_REUSEADDR: int
-SO_SNDBUF: int
-SO_SNDLOWAT: int
-SO_SNDTIMEO: int
-SO_TYPE: int
+SO_ACCEPTCONN: Final[int]
+SO_BROADCAST: Final[int]
+SO_DEBUG: Final[int]
+SO_DONTROUTE: Final[int]
+SO_ERROR: Final[int]
+SO_KEEPALIVE: Final[int]
+SO_LINGER: Final[int]
+SO_OOBINLINE: Final[int]
+SO_RCVBUF: Final[int]
+SO_RCVLOWAT: Final[int]
+SO_RCVTIMEO: Final[int]
+SO_REUSEADDR: Final[int]
+SO_SNDBUF: Final[int]
+SO_SNDLOWAT: Final[int]
+SO_SNDTIMEO: Final[int]
+SO_TYPE: Final[int]
 if sys.platform != "linux":
-    SO_USELOOPBACK: int
+    SO_USELOOPBACK: Final[int]
 if sys.platform == "win32":
-    SO_EXCLUSIVEADDRUSE: int
+    SO_EXCLUSIVEADDRUSE: Final[int]
 if sys.platform != "win32":
-    SO_REUSEPORT: int
+    SO_REUSEPORT: Final[int]
     if sys.platform != "darwin" or sys.version_info >= (3, 13):
-        SO_BINDTODEVICE: int
+        SO_BINDTODEVICE: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    SO_DOMAIN: int
-    SO_MARK: int
-    SO_PASSCRED: int
-    SO_PASSSEC: int
-    SO_PEERCRED: int
-    SO_PEERSEC: int
-    SO_PRIORITY: int
-    SO_PROTOCOL: int
+    SO_DOMAIN: Final[int]
+    SO_MARK: Final[int]
+    SO_PASSCRED: Final[int]
+    SO_PASSSEC: Final[int]
+    SO_PEERCRED: Final[int]
+    SO_PEERSEC: Final[int]
+    SO_PRIORITY: Final[int]
+    SO_PROTOCOL: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
-    SO_SETFIB: int
+    SO_SETFIB: Final[int]
 if sys.platform == "linux" and sys.version_info >= (3, 13):
-    SO_BINDTOIFINDEX: int
+    SO_BINDTOIFINDEX: Final[int]
 
-SOMAXCONN: int
+SOMAXCONN: Final[int]
 
-MSG_CTRUNC: int
-MSG_DONTROUTE: int
-MSG_OOB: int
-MSG_PEEK: int
-MSG_TRUNC: int
-MSG_WAITALL: int
+MSG_CTRUNC: Final[int]
+MSG_DONTROUTE: Final[int]
+MSG_OOB: Final[int]
+MSG_PEEK: Final[int]
+MSG_TRUNC: Final[int]
+MSG_WAITALL: Final[int]
 if sys.platform != "win32":
-    MSG_DONTWAIT: int
-    MSG_EOR: int
-    MSG_NOSIGNAL: int  # Sometimes this exists on darwin, sometimes not
+    MSG_DONTWAIT: Final[int]
+    MSG_EOR: Final[int]
+    MSG_NOSIGNAL: Final[int]  # Sometimes this exists on darwin, sometimes not
 if sys.platform != "darwin":
-    MSG_ERRQUEUE: int
+    MSG_ERRQUEUE: Final[int]
 if sys.platform == "win32":
-    MSG_BCAST: int
-    MSG_MCAST: int
+    MSG_BCAST: Final[int]
+    MSG_MCAST: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin":
-    MSG_CMSG_CLOEXEC: int
-    MSG_CONFIRM: int
-    MSG_FASTOPEN: int
-    MSG_MORE: int
+    MSG_CMSG_CLOEXEC: Final[int]
+    MSG_CONFIRM: Final[int]
+    MSG_FASTOPEN: Final[int]
+    MSG_MORE: Final[int]
 if sys.platform != "win32" and sys.platform != "linux":
-    MSG_EOF: int
+    MSG_EOF: Final[int]
 if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin":
-    MSG_NOTIFICATION: int
-    MSG_BTAG: int  # Not FreeBSD either
-    MSG_ETAG: int  # Not FreeBSD either
-
-SOL_IP: int
-SOL_SOCKET: int
-SOL_TCP: int
-SOL_UDP: int
+    MSG_NOTIFICATION: Final[int]
+    MSG_BTAG: Final[int]  # Not FreeBSD either
+    MSG_ETAG: Final[int]  # Not FreeBSD either
+
+SOL_IP: Final[int]
+SOL_SOCKET: Final[int]
+SOL_TCP: Final[int]
+SOL_UDP: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin":
     # Defined in socket.h for Linux, but these aren't always present for
     # some reason.
-    SOL_ATALK: int
-    SOL_AX25: int
-    SOL_HCI: int
-    SOL_IPX: int
-    SOL_NETROM: int
-    SOL_ROSE: int
+    SOL_ATALK: Final[int]
+    SOL_AX25: Final[int]
+    SOL_HCI: Final[int]
+    SOL_IPX: Final[int]
+    SOL_NETROM: Final[int]
+    SOL_ROSE: Final[int]
 
 if sys.platform != "win32":
-    SCM_RIGHTS: int
+    SCM_RIGHTS: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin":
-    SCM_CREDENTIALS: int
+    SCM_CREDENTIALS: Final[int]
 if sys.platform != "win32" and sys.platform != "linux":
-    SCM_CREDS: int
-
-IPPROTO_ICMP: int
-IPPROTO_IP: int
-IPPROTO_RAW: int
-IPPROTO_TCP: int
-IPPROTO_UDP: int
-IPPROTO_AH: int
-IPPROTO_DSTOPTS: int
-IPPROTO_EGP: int
-IPPROTO_ESP: int
-IPPROTO_FRAGMENT: int
-IPPROTO_HOPOPTS: int
-IPPROTO_ICMPV6: int
-IPPROTO_IDP: int
-IPPROTO_IGMP: int
-IPPROTO_IPV6: int
-IPPROTO_NONE: int
-IPPROTO_PIM: int
-IPPROTO_PUP: int
-IPPROTO_ROUTING: int
-IPPROTO_SCTP: int
+    SCM_CREDS: Final[int]
+
+IPPROTO_ICMP: Final[int]
+IPPROTO_IP: Final[int]
+IPPROTO_RAW: Final[int]
+IPPROTO_TCP: Final[int]
+IPPROTO_UDP: Final[int]
+IPPROTO_AH: Final[int]
+IPPROTO_DSTOPTS: Final[int]
+IPPROTO_EGP: Final[int]
+IPPROTO_ESP: Final[int]
+IPPROTO_FRAGMENT: Final[int]
+IPPROTO_HOPOPTS: Final[int]
+IPPROTO_ICMPV6: Final[int]
+IPPROTO_IDP: Final[int]
+IPPROTO_IGMP: Final[int]
+IPPROTO_IPV6: Final[int]
+IPPROTO_NONE: Final[int]
+IPPROTO_PIM: Final[int]
+IPPROTO_PUP: Final[int]
+IPPROTO_ROUTING: Final[int]
+IPPROTO_SCTP: Final[int]
 if sys.platform != "linux":
-    IPPROTO_GGP: int
-    IPPROTO_IPV4: int
-    IPPROTO_MAX: int
-    IPPROTO_ND: int
+    IPPROTO_GGP: Final[int]
+    IPPROTO_IPV4: Final[int]
+    IPPROTO_MAX: Final[int]
+    IPPROTO_ND: Final[int]
 if sys.platform == "win32":
-    IPPROTO_CBT: int
-    IPPROTO_ICLFXBM: int
-    IPPROTO_IGP: int
-    IPPROTO_L2TP: int
-    IPPROTO_PGM: int
-    IPPROTO_RDP: int
-    IPPROTO_ST: int
+    IPPROTO_CBT: Final[int]
+    IPPROTO_ICLFXBM: Final[int]
+    IPPROTO_IGP: Final[int]
+    IPPROTO_L2TP: Final[int]
+    IPPROTO_PGM: Final[int]
+    IPPROTO_RDP: Final[int]
+    IPPROTO_ST: Final[int]
 if sys.platform != "win32":
-    IPPROTO_GRE: int
-    IPPROTO_IPIP: int
-    IPPROTO_RSVP: int
-    IPPROTO_TP: int
+    IPPROTO_GRE: Final[int]
+    IPPROTO_IPIP: Final[int]
+    IPPROTO_RSVP: Final[int]
+    IPPROTO_TP: Final[int]
 if sys.platform != "win32" and sys.platform != "linux":
-    IPPROTO_EON: int
-    IPPROTO_HELLO: int
-    IPPROTO_IPCOMP: int
-    IPPROTO_XTP: int
+    IPPROTO_EON: Final[int]
+    IPPROTO_HELLO: Final[int]
+    IPPROTO_IPCOMP: Final[int]
+    IPPROTO_XTP: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
-    IPPROTO_BIP: int  # Not FreeBSD either
-    IPPROTO_MOBILE: int  # Not FreeBSD either
-    IPPROTO_VRRP: int  # Not FreeBSD either
+    IPPROTO_BIP: Final[int]  # Not FreeBSD either
+    IPPROTO_MOBILE: Final[int]  # Not FreeBSD either
+    IPPROTO_VRRP: Final[int]  # Not FreeBSD either
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.20, FreeBSD >= 10.1
-    IPPROTO_UDPLITE: int
+    IPPROTO_UDPLITE: Final[int]
 if sys.version_info >= (3, 10) and sys.platform == "linux":
-    IPPROTO_MPTCP: int
-
-IPPORT_RESERVED: int
-IPPORT_USERRESERVED: int
-
-INADDR_ALLHOSTS_GROUP: int
-INADDR_ANY: int
-INADDR_BROADCAST: int
-INADDR_LOOPBACK: int
-INADDR_MAX_LOCAL_GROUP: int
-INADDR_NONE: int
-INADDR_UNSPEC_GROUP: int
-
-IP_ADD_MEMBERSHIP: int
-IP_DROP_MEMBERSHIP: int
-IP_HDRINCL: int
-IP_MULTICAST_IF: int
-IP_MULTICAST_LOOP: int
-IP_MULTICAST_TTL: int
-IP_OPTIONS: int
+    IPPROTO_MPTCP: Final[int]
+
+IPPORT_RESERVED: Final[int]
+IPPORT_USERRESERVED: Final[int]
+
+INADDR_ALLHOSTS_GROUP: Final[int]
+INADDR_ANY: Final[int]
+INADDR_BROADCAST: Final[int]
+INADDR_LOOPBACK: Final[int]
+INADDR_MAX_LOCAL_GROUP: Final[int]
+INADDR_NONE: Final[int]
+INADDR_UNSPEC_GROUP: Final[int]
+
+IP_ADD_MEMBERSHIP: Final[int]
+IP_DROP_MEMBERSHIP: Final[int]
+IP_HDRINCL: Final[int]
+IP_MULTICAST_IF: Final[int]
+IP_MULTICAST_LOOP: Final[int]
+IP_MULTICAST_TTL: Final[int]
+IP_OPTIONS: Final[int]
 if sys.platform != "linux":
-    IP_RECVDSTADDR: int
+    IP_RECVDSTADDR: Final[int]
 if sys.version_info >= (3, 10):
-    IP_RECVTOS: int
-IP_TOS: int
-IP_TTL: int
+    IP_RECVTOS: Final[int]
+IP_TOS: Final[int]
+IP_TTL: Final[int]
 if sys.platform != "win32":
-    IP_DEFAULT_MULTICAST_LOOP: int
-    IP_DEFAULT_MULTICAST_TTL: int
-    IP_MAX_MEMBERSHIPS: int
-    IP_RECVOPTS: int
-    IP_RECVRETOPTS: int
-    IP_RETOPTS: int
+    IP_DEFAULT_MULTICAST_LOOP: Final[int]
+    IP_DEFAULT_MULTICAST_TTL: Final[int]
+    IP_MAX_MEMBERSHIPS: Final[int]
+    IP_RECVOPTS: Final[int]
+    IP_RECVRETOPTS: Final[int]
+    IP_RETOPTS: Final[int]
 if sys.version_info >= (3, 13) and sys.platform == "linux":
-    CAN_RAW_ERR_FILTER: int
+    CAN_RAW_ERR_FILTER: Final[int]
 if sys.version_info >= (3, 14):
-    IP_RECVTTL: int
+    IP_RECVTTL: Final[int]
 
     if sys.platform == "win32" or sys.platform == "linux":
-        IPV6_RECVERR: int
-        IP_RECVERR: int
-        SO_ORIGINAL_DST: int
+        IPV6_RECVERR: Final[int]
+        IP_RECVERR: Final[int]
+        SO_ORIGINAL_DST: Final[int]
 
     if sys.platform == "win32":
-        SOL_RFCOMM: int
-        SO_BTH_ENCRYPT: int
-        SO_BTH_MTU: int
-        SO_BTH_MTU_MAX: int
-        SO_BTH_MTU_MIN: int
-        TCP_QUICKACK: int
+        SOL_RFCOMM: Final[int]
+        SO_BTH_ENCRYPT: Final[int]
+        SO_BTH_MTU: Final[int]
+        SO_BTH_MTU_MAX: Final[int]
+        SO_BTH_MTU_MIN: Final[int]
+        TCP_QUICKACK: Final[int]
 
     if sys.platform == "linux":
-        IP_FREEBIND: int
-        IP_RECVORIGDSTADDR: int
-        VMADDR_CID_LOCAL: int
+        IP_FREEBIND: Final[int]
+        IP_RECVORIGDSTADDR: Final[int]
+        VMADDR_CID_LOCAL: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    IP_TRANSPARENT: int
+    IP_TRANSPARENT: Final[int]
 if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11):
-    IP_BIND_ADDRESS_NO_PORT: int
+    IP_BIND_ADDRESS_NO_PORT: Final[int]
 if sys.version_info >= (3, 12):
-    IP_ADD_SOURCE_MEMBERSHIP: int
-    IP_BLOCK_SOURCE: int
-    IP_DROP_SOURCE_MEMBERSHIP: int
-    IP_PKTINFO: int
-    IP_UNBLOCK_SOURCE: int
-
-IPV6_CHECKSUM: int
-IPV6_JOIN_GROUP: int
-IPV6_LEAVE_GROUP: int
-IPV6_MULTICAST_HOPS: int
-IPV6_MULTICAST_IF: int
-IPV6_MULTICAST_LOOP: int
-IPV6_RECVTCLASS: int
-IPV6_TCLASS: int
-IPV6_UNICAST_HOPS: int
-IPV6_V6ONLY: int
-IPV6_DONTFRAG: int
-IPV6_HOPLIMIT: int
-IPV6_HOPOPTS: int
-IPV6_PKTINFO: int
-IPV6_RECVRTHDR: int
-IPV6_RTHDR: int
+    IP_ADD_SOURCE_MEMBERSHIP: Final[int]
+    IP_BLOCK_SOURCE: Final[int]
+    IP_DROP_SOURCE_MEMBERSHIP: Final[int]
+    IP_PKTINFO: Final[int]
+    IP_UNBLOCK_SOURCE: Final[int]
+
+IPV6_CHECKSUM: Final[int]
+IPV6_JOIN_GROUP: Final[int]
+IPV6_LEAVE_GROUP: Final[int]
+IPV6_MULTICAST_HOPS: Final[int]
+IPV6_MULTICAST_IF: Final[int]
+IPV6_MULTICAST_LOOP: Final[int]
+IPV6_RECVTCLASS: Final[int]
+IPV6_TCLASS: Final[int]
+IPV6_UNICAST_HOPS: Final[int]
+IPV6_V6ONLY: Final[int]
+IPV6_DONTFRAG: Final[int]
+IPV6_HOPLIMIT: Final[int]
+IPV6_HOPOPTS: Final[int]
+IPV6_PKTINFO: Final[int]
+IPV6_RECVRTHDR: Final[int]
+IPV6_RTHDR: Final[int]
 if sys.platform != "win32":
-    IPV6_RTHDR_TYPE_0: int
-    IPV6_DSTOPTS: int
-    IPV6_NEXTHOP: int
-    IPV6_PATHMTU: int
-    IPV6_RECVDSTOPTS: int
-    IPV6_RECVHOPLIMIT: int
-    IPV6_RECVHOPOPTS: int
-    IPV6_RECVPATHMTU: int
-    IPV6_RECVPKTINFO: int
-    IPV6_RTHDRDSTOPTS: int
+    IPV6_RTHDR_TYPE_0: Final[int]
+    IPV6_DSTOPTS: Final[int]
+    IPV6_NEXTHOP: Final[int]
+    IPV6_PATHMTU: Final[int]
+    IPV6_RECVDSTOPTS: Final[int]
+    IPV6_RECVHOPLIMIT: Final[int]
+    IPV6_RECVHOPOPTS: Final[int]
+    IPV6_RECVPATHMTU: Final[int]
+    IPV6_RECVPKTINFO: Final[int]
+    IPV6_RTHDRDSTOPTS: Final[int]
 
 if sys.platform != "win32" and sys.platform != "linux":
-    IPV6_USE_MIN_MTU: int
-
-EAI_AGAIN: int
-EAI_BADFLAGS: int
-EAI_FAIL: int
-EAI_FAMILY: int
-EAI_MEMORY: int
-EAI_NODATA: int
-EAI_NONAME: int
-EAI_SERVICE: int
-EAI_SOCKTYPE: int
+    IPV6_USE_MIN_MTU: Final[int]
+
+EAI_AGAIN: Final[int]
+EAI_BADFLAGS: Final[int]
+EAI_FAIL: Final[int]
+EAI_FAMILY: Final[int]
+EAI_MEMORY: Final[int]
+EAI_NODATA: Final[int]
+EAI_NONAME: Final[int]
+EAI_SERVICE: Final[int]
+EAI_SOCKTYPE: Final[int]
 if sys.platform != "win32":
-    EAI_ADDRFAMILY: int
-    EAI_OVERFLOW: int
-    EAI_SYSTEM: int
+    EAI_ADDRFAMILY: Final[int]
+    EAI_OVERFLOW: Final[int]
+    EAI_SYSTEM: Final[int]
 if sys.platform != "win32" and sys.platform != "linux":
-    EAI_BADHINTS: int
-    EAI_MAX: int
-    EAI_PROTOCOL: int
-
-AI_ADDRCONFIG: int
-AI_ALL: int
-AI_CANONNAME: int
-AI_NUMERICHOST: int
-AI_NUMERICSERV: int
-AI_PASSIVE: int
-AI_V4MAPPED: int
+    EAI_BADHINTS: Final[int]
+    EAI_MAX: Final[int]
+    EAI_PROTOCOL: Final[int]
+
+AI_ADDRCONFIG: Final[int]
+AI_ALL: Final[int]
+AI_CANONNAME: Final[int]
+AI_NUMERICHOST: Final[int]
+AI_NUMERICSERV: Final[int]
+AI_PASSIVE: Final[int]
+AI_V4MAPPED: Final[int]
 if sys.platform != "win32" and sys.platform != "linux":
-    AI_DEFAULT: int
-    AI_MASK: int
-    AI_V4MAPPED_CFG: int
-
-NI_DGRAM: int
-NI_MAXHOST: int
-NI_MAXSERV: int
-NI_NAMEREQD: int
-NI_NOFQDN: int
-NI_NUMERICHOST: int
-NI_NUMERICSERV: int
+    AI_DEFAULT: Final[int]
+    AI_MASK: Final[int]
+    AI_V4MAPPED_CFG: Final[int]
+
+NI_DGRAM: Final[int]
+NI_MAXHOST: Final[int]
+NI_MAXSERV: Final[int]
+NI_NAMEREQD: Final[int]
+NI_NOFQDN: Final[int]
+NI_NUMERICHOST: Final[int]
+NI_NUMERICSERV: Final[int]
 if sys.platform == "linux" and sys.version_info >= (3, 13):
-    NI_IDN: int
+    NI_IDN: Final[int]
 
-TCP_FASTOPEN: int
-TCP_KEEPCNT: int
-TCP_KEEPINTVL: int
-TCP_MAXSEG: int
-TCP_NODELAY: int
+TCP_FASTOPEN: Final[int]
+TCP_KEEPCNT: Final[int]
+TCP_KEEPINTVL: Final[int]
+TCP_MAXSEG: Final[int]
+TCP_NODELAY: Final[int]
 if sys.platform != "win32":
-    TCP_NOTSENT_LOWAT: int
+    TCP_NOTSENT_LOWAT: Final[int]
 if sys.platform != "darwin":
-    TCP_KEEPIDLE: int
+    TCP_KEEPIDLE: Final[int]
 if sys.version_info >= (3, 10) and sys.platform == "darwin":
-    TCP_KEEPALIVE: int
+    TCP_KEEPALIVE: Final[int]
 if sys.version_info >= (3, 11) and sys.platform == "darwin":
-    TCP_CONNECTION_INFO: int
+    TCP_CONNECTION_INFO: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    TCP_CONGESTION: int
-    TCP_CORK: int
-    TCP_DEFER_ACCEPT: int
-    TCP_INFO: int
-    TCP_LINGER2: int
-    TCP_QUICKACK: int
-    TCP_SYNCNT: int
-    TCP_USER_TIMEOUT: int
-    TCP_WINDOW_CLAMP: int
+    TCP_CONGESTION: Final[int]
+    TCP_CORK: Final[int]
+    TCP_DEFER_ACCEPT: Final[int]
+    TCP_INFO: Final[int]
+    TCP_LINGER2: Final[int]
+    TCP_QUICKACK: Final[int]
+    TCP_SYNCNT: Final[int]
+    TCP_USER_TIMEOUT: Final[int]
+    TCP_WINDOW_CLAMP: Final[int]
 if sys.platform == "linux" and sys.version_info >= (3, 12):
-    TCP_CC_INFO: int
-    TCP_FASTOPEN_CONNECT: int
-    TCP_FASTOPEN_KEY: int
-    TCP_FASTOPEN_NO_COOKIE: int
-    TCP_INQ: int
-    TCP_MD5SIG: int
-    TCP_MD5SIG_EXT: int
-    TCP_QUEUE_SEQ: int
-    TCP_REPAIR: int
-    TCP_REPAIR_OPTIONS: int
-    TCP_REPAIR_QUEUE: int
-    TCP_REPAIR_WINDOW: int
-    TCP_SAVED_SYN: int
-    TCP_SAVE_SYN: int
-    TCP_THIN_DUPACK: int
-    TCP_THIN_LINEAR_TIMEOUTS: int
-    TCP_TIMESTAMP: int
-    TCP_TX_DELAY: int
-    TCP_ULP: int
-    TCP_ZEROCOPY_RECEIVE: int
+    TCP_CC_INFO: Final[int]
+    TCP_FASTOPEN_CONNECT: Final[int]
+    TCP_FASTOPEN_KEY: Final[int]
+    TCP_FASTOPEN_NO_COOKIE: Final[int]
+    TCP_INQ: Final[int]
+    TCP_MD5SIG: Final[int]
+    TCP_MD5SIG_EXT: Final[int]
+    TCP_QUEUE_SEQ: Final[int]
+    TCP_REPAIR: Final[int]
+    TCP_REPAIR_OPTIONS: Final[int]
+    TCP_REPAIR_QUEUE: Final[int]
+    TCP_REPAIR_WINDOW: Final[int]
+    TCP_SAVED_SYN: Final[int]
+    TCP_SAVE_SYN: Final[int]
+    TCP_THIN_DUPACK: Final[int]
+    TCP_THIN_LINEAR_TIMEOUTS: Final[int]
+    TCP_TIMESTAMP: Final[int]
+    TCP_TX_DELAY: Final[int]
+    TCP_ULP: Final[int]
+    TCP_ZEROCOPY_RECEIVE: Final[int]
 
 # --------------------
 # Specifically documented constants
@@ -386,250 +386,250 @@ if sys.platform == "linux" and sys.version_info >= (3, 12):
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.25, NetBSD >= 8
-    AF_CAN: int
-    PF_CAN: int
-    SOL_CAN_BASE: int
-    SOL_CAN_RAW: int
-    CAN_EFF_FLAG: int
-    CAN_EFF_MASK: int
-    CAN_ERR_FLAG: int
-    CAN_ERR_MASK: int
-    CAN_RAW: int
-    CAN_RAW_FILTER: int
-    CAN_RAW_LOOPBACK: int
-    CAN_RAW_RECV_OWN_MSGS: int
-    CAN_RTR_FLAG: int
-    CAN_SFF_MASK: int
+    AF_CAN: Final[int]
+    PF_CAN: Final[int]
+    SOL_CAN_BASE: Final[int]
+    SOL_CAN_RAW: Final[int]
+    CAN_EFF_FLAG: Final[int]
+    CAN_EFF_MASK: Final[int]
+    CAN_ERR_FLAG: Final[int]
+    CAN_ERR_MASK: Final[int]
+    CAN_RAW: Final[int]
+    CAN_RAW_FILTER: Final[int]
+    CAN_RAW_LOOPBACK: Final[int]
+    CAN_RAW_RECV_OWN_MSGS: Final[int]
+    CAN_RTR_FLAG: Final[int]
+    CAN_SFF_MASK: Final[int]
     if sys.version_info < (3, 11):
-        CAN_RAW_ERR_FILTER: int
+        CAN_RAW_ERR_FILTER: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.25
-    CAN_BCM: int
-    CAN_BCM_TX_SETUP: int
-    CAN_BCM_TX_DELETE: int
-    CAN_BCM_TX_READ: int
-    CAN_BCM_TX_SEND: int
-    CAN_BCM_RX_SETUP: int
-    CAN_BCM_RX_DELETE: int
-    CAN_BCM_RX_READ: int
-    CAN_BCM_TX_STATUS: int
-    CAN_BCM_TX_EXPIRED: int
-    CAN_BCM_RX_STATUS: int
-    CAN_BCM_RX_TIMEOUT: int
-    CAN_BCM_RX_CHANGED: int
-    CAN_BCM_SETTIMER: int
-    CAN_BCM_STARTTIMER: int
-    CAN_BCM_TX_COUNTEVT: int
-    CAN_BCM_TX_ANNOUNCE: int
-    CAN_BCM_TX_CP_CAN_ID: int
-    CAN_BCM_RX_FILTER_ID: int
-    CAN_BCM_RX_CHECK_DLC: int
-    CAN_BCM_RX_NO_AUTOTIMER: int
-    CAN_BCM_RX_ANNOUNCE_RESUME: int
-    CAN_BCM_TX_RESET_MULTI_IDX: int
-    CAN_BCM_RX_RTR_FRAME: int
-    CAN_BCM_CAN_FD_FRAME: int
+    CAN_BCM: Final[int]
+    CAN_BCM_TX_SETUP: Final[int]
+    CAN_BCM_TX_DELETE: Final[int]
+    CAN_BCM_TX_READ: Final[int]
+    CAN_BCM_TX_SEND: Final[int]
+    CAN_BCM_RX_SETUP: Final[int]
+    CAN_BCM_RX_DELETE: Final[int]
+    CAN_BCM_RX_READ: Final[int]
+    CAN_BCM_TX_STATUS: Final[int]
+    CAN_BCM_TX_EXPIRED: Final[int]
+    CAN_BCM_RX_STATUS: Final[int]
+    CAN_BCM_RX_TIMEOUT: Final[int]
+    CAN_BCM_RX_CHANGED: Final[int]
+    CAN_BCM_SETTIMER: Final[int]
+    CAN_BCM_STARTTIMER: Final[int]
+    CAN_BCM_TX_COUNTEVT: Final[int]
+    CAN_BCM_TX_ANNOUNCE: Final[int]
+    CAN_BCM_TX_CP_CAN_ID: Final[int]
+    CAN_BCM_RX_FILTER_ID: Final[int]
+    CAN_BCM_RX_CHECK_DLC: Final[int]
+    CAN_BCM_RX_NO_AUTOTIMER: Final[int]
+    CAN_BCM_RX_ANNOUNCE_RESUME: Final[int]
+    CAN_BCM_TX_RESET_MULTI_IDX: Final[int]
+    CAN_BCM_RX_RTR_FRAME: Final[int]
+    CAN_BCM_CAN_FD_FRAME: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 3.6
-    CAN_RAW_FD_FRAMES: int
+    CAN_RAW_FD_FRAMES: Final[int]
     # Availability: Linux >= 4.1
-    CAN_RAW_JOIN_FILTERS: int
+    CAN_RAW_JOIN_FILTERS: Final[int]
     # Availability: Linux >= 2.6.25
-    CAN_ISOTP: int
+    CAN_ISOTP: Final[int]
     # Availability: Linux >= 5.4
-    CAN_J1939: int
-
-    J1939_MAX_UNICAST_ADDR: int
-    J1939_IDLE_ADDR: int
-    J1939_NO_ADDR: int
-    J1939_NO_NAME: int
-    J1939_PGN_REQUEST: int
-    J1939_PGN_ADDRESS_CLAIMED: int
-    J1939_PGN_ADDRESS_COMMANDED: int
-    J1939_PGN_PDU1_MAX: int
-    J1939_PGN_MAX: int
-    J1939_NO_PGN: int
-
-    SO_J1939_FILTER: int
-    SO_J1939_PROMISC: int
-    SO_J1939_SEND_PRIO: int
-    SO_J1939_ERRQUEUE: int
-
-    SCM_J1939_DEST_ADDR: int
-    SCM_J1939_DEST_NAME: int
-    SCM_J1939_PRIO: int
-    SCM_J1939_ERRQUEUE: int
-
-    J1939_NLA_PAD: int
-    J1939_NLA_BYTES_ACKED: int
-    J1939_EE_INFO_NONE: int
-    J1939_EE_INFO_TX_ABORT: int
-    J1939_FILTER_MAX: int
+    CAN_J1939: Final[int]
+
+    J1939_MAX_UNICAST_ADDR: Final[int]
+    J1939_IDLE_ADDR: Final[int]
+    J1939_NO_ADDR: Final[int]
+    J1939_NO_NAME: Final[int]
+    J1939_PGN_REQUEST: Final[int]
+    J1939_PGN_ADDRESS_CLAIMED: Final[int]
+    J1939_PGN_ADDRESS_COMMANDED: Final[int]
+    J1939_PGN_PDU1_MAX: Final[int]
+    J1939_PGN_MAX: Final[int]
+    J1939_NO_PGN: Final[int]
+
+    SO_J1939_FILTER: Final[int]
+    SO_J1939_PROMISC: Final[int]
+    SO_J1939_SEND_PRIO: Final[int]
+    SO_J1939_ERRQUEUE: Final[int]
+
+    SCM_J1939_DEST_ADDR: Final[int]
+    SCM_J1939_DEST_NAME: Final[int]
+    SCM_J1939_PRIO: Final[int]
+    SCM_J1939_ERRQUEUE: Final[int]
+
+    J1939_NLA_PAD: Final[int]
+    J1939_NLA_BYTES_ACKED: Final[int]
+    J1939_EE_INFO_NONE: Final[int]
+    J1939_EE_INFO_TX_ABORT: Final[int]
+    J1939_FILTER_MAX: Final[int]
 
 if sys.version_info >= (3, 12) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin":
     # Availability: FreeBSD >= 14.0
-    AF_DIVERT: int
-    PF_DIVERT: int
+    AF_DIVERT: Final[int]
+    PF_DIVERT: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.2
-    AF_PACKET: int
-    PF_PACKET: int
-    PACKET_BROADCAST: int
-    PACKET_FASTROUTE: int
-    PACKET_HOST: int
-    PACKET_LOOPBACK: int
-    PACKET_MULTICAST: int
-    PACKET_OTHERHOST: int
-    PACKET_OUTGOING: int
+    AF_PACKET: Final[int]
+    PF_PACKET: Final[int]
+    PACKET_BROADCAST: Final[int]
+    PACKET_FASTROUTE: Final[int]
+    PACKET_HOST: Final[int]
+    PACKET_LOOPBACK: Final[int]
+    PACKET_MULTICAST: Final[int]
+    PACKET_OTHERHOST: Final[int]
+    PACKET_OUTGOING: Final[int]
 
 if sys.version_info >= (3, 12) and sys.platform == "linux":
-    ETH_P_ALL: int
+    ETH_P_ALL: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.30
-    AF_RDS: int
-    PF_RDS: int
-    SOL_RDS: int
+    AF_RDS: Final[int]
+    PF_RDS: Final[int]
+    SOL_RDS: Final[int]
     # These are present in include/linux/rds.h but don't always show up
     # here.
-    RDS_CANCEL_SENT_TO: int
-    RDS_CMSG_RDMA_ARGS: int
-    RDS_CMSG_RDMA_DEST: int
-    RDS_CMSG_RDMA_MAP: int
-    RDS_CMSG_RDMA_STATUS: int
-    RDS_CONG_MONITOR: int
-    RDS_FREE_MR: int
-    RDS_GET_MR: int
-    RDS_GET_MR_FOR_DEST: int
-    RDS_RDMA_DONTWAIT: int
-    RDS_RDMA_FENCE: int
-    RDS_RDMA_INVALIDATE: int
-    RDS_RDMA_NOTIFY_ME: int
-    RDS_RDMA_READWRITE: int
-    RDS_RDMA_SILENT: int
-    RDS_RDMA_USE_ONCE: int
-    RDS_RECVERR: int
+    RDS_CANCEL_SENT_TO: Final[int]
+    RDS_CMSG_RDMA_ARGS: Final[int]
+    RDS_CMSG_RDMA_DEST: Final[int]
+    RDS_CMSG_RDMA_MAP: Final[int]
+    RDS_CMSG_RDMA_STATUS: Final[int]
+    RDS_CONG_MONITOR: Final[int]
+    RDS_FREE_MR: Final[int]
+    RDS_GET_MR: Final[int]
+    RDS_GET_MR_FOR_DEST: Final[int]
+    RDS_RDMA_DONTWAIT: Final[int]
+    RDS_RDMA_FENCE: Final[int]
+    RDS_RDMA_INVALIDATE: Final[int]
+    RDS_RDMA_NOTIFY_ME: Final[int]
+    RDS_RDMA_READWRITE: Final[int]
+    RDS_RDMA_SILENT: Final[int]
+    RDS_RDMA_USE_ONCE: Final[int]
+    RDS_RECVERR: Final[int]
 
     # This is supported by CPython but doesn't seem to be a real thing.
     # The closest existing constant in rds.h is RDS_CMSG_CONG_UPDATE
-    # RDS_CMSG_RDMA_UPDATE: int
+    # RDS_CMSG_RDMA_UPDATE: Final[int]
 
 if sys.platform == "win32":
-    SIO_RCVALL: int
-    SIO_KEEPALIVE_VALS: int
-    SIO_LOOPBACK_FAST_PATH: int
-    RCVALL_MAX: int
-    RCVALL_OFF: int
-    RCVALL_ON: int
-    RCVALL_SOCKETLEVELONLY: int
+    SIO_RCVALL: Final[int]
+    SIO_KEEPALIVE_VALS: Final[int]
+    SIO_LOOPBACK_FAST_PATH: Final[int]
+    RCVALL_MAX: Final[int]
+    RCVALL_OFF: Final[int]
+    RCVALL_ON: Final[int]
+    RCVALL_SOCKETLEVELONLY: Final[int]
 
 if sys.platform == "linux":
-    AF_TIPC: int
-    SOL_TIPC: int
-    TIPC_ADDR_ID: int
-    TIPC_ADDR_NAME: int
-    TIPC_ADDR_NAMESEQ: int
-    TIPC_CFG_SRV: int
-    TIPC_CLUSTER_SCOPE: int
-    TIPC_CONN_TIMEOUT: int
-    TIPC_CRITICAL_IMPORTANCE: int
-    TIPC_DEST_DROPPABLE: int
-    TIPC_HIGH_IMPORTANCE: int
-    TIPC_IMPORTANCE: int
-    TIPC_LOW_IMPORTANCE: int
-    TIPC_MEDIUM_IMPORTANCE: int
-    TIPC_NODE_SCOPE: int
-    TIPC_PUBLISHED: int
-    TIPC_SRC_DROPPABLE: int
-    TIPC_SUBSCR_TIMEOUT: int
-    TIPC_SUB_CANCEL: int
-    TIPC_SUB_PORTS: int
-    TIPC_SUB_SERVICE: int
-    TIPC_TOP_SRV: int
-    TIPC_WAIT_FOREVER: int
-    TIPC_WITHDRAWN: int
-    TIPC_ZONE_SCOPE: int
+    AF_TIPC: Final[int]
+    SOL_TIPC: Final[int]
+    TIPC_ADDR_ID: Final[int]
+    TIPC_ADDR_NAME: Final[int]
+    TIPC_ADDR_NAMESEQ: Final[int]
+    TIPC_CFG_SRV: Final[int]
+    TIPC_CLUSTER_SCOPE: Final[int]
+    TIPC_CONN_TIMEOUT: Final[int]
+    TIPC_CRITICAL_IMPORTANCE: Final[int]
+    TIPC_DEST_DROPPABLE: Final[int]
+    TIPC_HIGH_IMPORTANCE: Final[int]
+    TIPC_IMPORTANCE: Final[int]
+    TIPC_LOW_IMPORTANCE: Final[int]
+    TIPC_MEDIUM_IMPORTANCE: Final[int]
+    TIPC_NODE_SCOPE: Final[int]
+    TIPC_PUBLISHED: Final[int]
+    TIPC_SRC_DROPPABLE: Final[int]
+    TIPC_SUBSCR_TIMEOUT: Final[int]
+    TIPC_SUB_CANCEL: Final[int]
+    TIPC_SUB_PORTS: Final[int]
+    TIPC_SUB_SERVICE: Final[int]
+    TIPC_TOP_SRV: Final[int]
+    TIPC_WAIT_FOREVER: Final[int]
+    TIPC_WITHDRAWN: Final[int]
+    TIPC_ZONE_SCOPE: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 2.6.38
-    AF_ALG: int
-    SOL_ALG: int
-    ALG_OP_DECRYPT: int
-    ALG_OP_ENCRYPT: int
-    ALG_OP_SIGN: int
-    ALG_OP_VERIFY: int
-    ALG_SET_AEAD_ASSOCLEN: int
-    ALG_SET_AEAD_AUTHSIZE: int
-    ALG_SET_IV: int
-    ALG_SET_KEY: int
-    ALG_SET_OP: int
-    ALG_SET_PUBKEY: int
+    AF_ALG: Final[int]
+    SOL_ALG: Final[int]
+    ALG_OP_DECRYPT: Final[int]
+    ALG_OP_ENCRYPT: Final[int]
+    ALG_OP_SIGN: Final[int]
+    ALG_OP_VERIFY: Final[int]
+    ALG_SET_AEAD_ASSOCLEN: Final[int]
+    ALG_SET_AEAD_AUTHSIZE: Final[int]
+    ALG_SET_IV: Final[int]
+    ALG_SET_KEY: Final[int]
+    ALG_SET_OP: Final[int]
+    ALG_SET_PUBKEY: Final[int]
 
 if sys.platform == "linux":
     # Availability: Linux >= 4.8 (or maybe 3.9, CPython docs are confusing)
-    AF_VSOCK: int
-    IOCTL_VM_SOCKETS_GET_LOCAL_CID: int
-    VMADDR_CID_ANY: int
-    VMADDR_CID_HOST: int
-    VMADDR_PORT_ANY: int
-    SO_VM_SOCKETS_BUFFER_MAX_SIZE: int
-    SO_VM_SOCKETS_BUFFER_SIZE: int
-    SO_VM_SOCKETS_BUFFER_MIN_SIZE: int
-    VM_SOCKETS_INVALID_VERSION: int  # undocumented
+    AF_VSOCK: Final[int]
+    IOCTL_VM_SOCKETS_GET_LOCAL_CID: Final = 0x7B9
+    VMADDR_CID_ANY: Final = 0xFFFFFFFF
+    VMADDR_CID_HOST: Final = 2
+    VMADDR_PORT_ANY: Final = 0xFFFFFFFF
+    SO_VM_SOCKETS_BUFFER_MAX_SIZE: Final = 2
+    SO_VM_SOCKETS_BUFFER_SIZE: Final = 0
+    SO_VM_SOCKETS_BUFFER_MIN_SIZE: Final = 1
+    VM_SOCKETS_INVALID_VERSION: Final = 0xFFFFFFFF  # undocumented
 
 # Documented as only available on BSD, macOS, but empirically sometimes
 # available on Windows
 if sys.platform != "linux":
-    AF_LINK: int
+    AF_LINK: Final[int]
 
 has_ipv6: bool
 
 if sys.platform != "darwin" and sys.platform != "linux":
-    BDADDR_ANY: str
-    BDADDR_LOCAL: str
+    BDADDR_ANY: Final = "00:00:00:00:00:00"
+    BDADDR_LOCAL: Final = "00:00:00:FF:FF:FF"
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
-    HCI_FILTER: int  # not in NetBSD or DragonFlyBSD
-    HCI_TIME_STAMP: int  # not in FreeBSD, NetBSD, or DragonFlyBSD
-    HCI_DATA_DIR: int  # not in FreeBSD, NetBSD, or DragonFlyBSD
+    HCI_FILTER: Final[int]  # not in NetBSD or DragonFlyBSD
+    HCI_TIME_STAMP: Final[int]  # not in FreeBSD, NetBSD, or DragonFlyBSD
+    HCI_DATA_DIR: Final[int]  # not in FreeBSD, NetBSD, or DragonFlyBSD
 
 if sys.platform == "linux":
-    AF_QIPCRTR: int  # Availability: Linux >= 4.7
+    AF_QIPCRTR: Final[int]  # Availability: Linux >= 4.7
 
 if sys.version_info >= (3, 11) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin":
     # FreeBSD
-    SCM_CREDS2: int
-    LOCAL_CREDS: int
-    LOCAL_CREDS_PERSISTENT: int
+    SCM_CREDS2: Final[int]
+    LOCAL_CREDS: Final[int]
+    LOCAL_CREDS_PERSISTENT: Final[int]
 
 if sys.version_info >= (3, 11) and sys.platform == "linux":
-    SO_INCOMING_CPU: int  # Availability: Linux >= 3.9
+    SO_INCOMING_CPU: Final[int]  # Availability: Linux >= 3.9
 
 if sys.version_info >= (3, 12) and sys.platform == "win32":
     # Availability: Windows
-    AF_HYPERV: int
-    HV_PROTOCOL_RAW: int
-    HVSOCKET_CONNECT_TIMEOUT: int
-    HVSOCKET_CONNECT_TIMEOUT_MAX: int
-    HVSOCKET_CONNECTED_SUSPEND: int
-    HVSOCKET_ADDRESS_FLAG_PASSTHRU: int
-    HV_GUID_ZERO: str
-    HV_GUID_WILDCARD: str
-    HV_GUID_BROADCAST: str
-    HV_GUID_CHILDREN: str
-    HV_GUID_LOOPBACK: str
-    HV_GUID_PARENT: str
+    AF_HYPERV: Final[int]
+    HV_PROTOCOL_RAW: Final[int]
+    HVSOCKET_CONNECT_TIMEOUT: Final[int]
+    HVSOCKET_CONNECT_TIMEOUT_MAX: Final[int]
+    HVSOCKET_CONNECTED_SUSPEND: Final[int]
+    HVSOCKET_ADDRESS_FLAG_PASSTHRU: Final[int]
+    HV_GUID_ZERO: Final = "00000000-0000-0000-0000-000000000000"
+    HV_GUID_WILDCARD: Final = "00000000-0000-0000-0000-000000000000"
+    HV_GUID_BROADCAST: Final = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
+    HV_GUID_CHILDREN: Final = "90DB8B89-0D35-4F79-8CE9-49EA0AC8B7CD"
+    HV_GUID_LOOPBACK: Final = "E0E16197-DD56-4A10-9195-5EE7A155A838"
+    HV_GUID_PARENT: Final = "A42E7CDA-D03F-480C-9CC2-A4DE20ABB878"
 
 if sys.version_info >= (3, 12):
     if sys.platform != "win32":
         # Availability: Linux, FreeBSD, macOS
-        ETHERTYPE_ARP: int
-        ETHERTYPE_IP: int
-        ETHERTYPE_IPV6: int
-        ETHERTYPE_VLAN: int
+        ETHERTYPE_ARP: Final[int]
+        ETHERTYPE_IP: Final[int]
+        ETHERTYPE_IPV6: Final[int]
+        ETHERTYPE_VLAN: Final[int]
 
 # --------------------
 # Semi-documented constants
@@ -639,95 +639,95 @@ if sys.version_info >= (3, 12):
 
 if sys.platform == "linux":
     # Netlink is defined by Linux
-    AF_NETLINK: int
-    NETLINK_CRYPTO: int
-    NETLINK_DNRTMSG: int
-    NETLINK_FIREWALL: int
-    NETLINK_IP6_FW: int
-    NETLINK_NFLOG: int
-    NETLINK_ROUTE: int
-    NETLINK_USERSOCK: int
-    NETLINK_XFRM: int
+    AF_NETLINK: Final[int]
+    NETLINK_CRYPTO: Final[int]
+    NETLINK_DNRTMSG: Final[int]
+    NETLINK_FIREWALL: Final[int]
+    NETLINK_IP6_FW: Final[int]
+    NETLINK_NFLOG: Final[int]
+    NETLINK_ROUTE: Final[int]
+    NETLINK_USERSOCK: Final[int]
+    NETLINK_XFRM: Final[int]
     # Technically still supported by CPython
-    # NETLINK_ARPD: int  # linux 2.0 to 2.6.12 (EOL August 2005)
-    # NETLINK_ROUTE6: int  # linux 2.2 to 2.6.12 (EOL August 2005)
-    # NETLINK_SKIP: int  # linux 2.0 to 2.6.12 (EOL August 2005)
-    # NETLINK_TAPBASE: int  # linux 2.2 to 2.6.12 (EOL August 2005)
-    # NETLINK_TCPDIAG: int  # linux 2.6.0 to 2.6.13 (EOL December 2005)
-    # NETLINK_W1: int  # linux 2.6.13 to 2.6.17 (EOL October 2006)
+    # NETLINK_ARPD: Final[int]  # linux 2.0 to 2.6.12 (EOL August 2005)
+    # NETLINK_ROUTE6: Final[int]  # linux 2.2 to 2.6.12 (EOL August 2005)
+    # NETLINK_SKIP: Final[int]  # linux 2.0 to 2.6.12 (EOL August 2005)
+    # NETLINK_TAPBASE: Final[int]  # linux 2.2 to 2.6.12 (EOL August 2005)
+    # NETLINK_TCPDIAG: Final[int]  # linux 2.6.0 to 2.6.13 (EOL December 2005)
+    # NETLINK_W1: Final[int]  # linux 2.6.13 to 2.6.17 (EOL October 2006)
 
 if sys.platform == "darwin":
-    PF_SYSTEM: int
-    SYSPROTO_CONTROL: int
+    PF_SYSTEM: Final[int]
+    SYSPROTO_CONTROL: Final[int]
 
 if sys.platform != "darwin" and sys.platform != "linux":
-    AF_BLUETOOTH: int
+    AF_BLUETOOTH: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
     # Linux and some BSD support is explicit in the docs
     # Windows and macOS do not support in practice
-    BTPROTO_HCI: int
-    BTPROTO_L2CAP: int
-    BTPROTO_SCO: int  # not in FreeBSD
+    BTPROTO_HCI: Final[int]
+    BTPROTO_L2CAP: Final[int]
+    BTPROTO_SCO: Final[int]  # not in FreeBSD
 if sys.platform != "darwin" and sys.platform != "linux":
-    BTPROTO_RFCOMM: int
+    BTPROTO_RFCOMM: Final[int]
 
 if sys.platform == "linux":
-    UDPLITE_RECV_CSCOV: int
-    UDPLITE_SEND_CSCOV: int
+    UDPLITE_RECV_CSCOV: Final[int]
+    UDPLITE_SEND_CSCOV: Final[int]
 
 # --------------------
 # Documented under socket.shutdown
 # --------------------
-SHUT_RD: int
-SHUT_RDWR: int
-SHUT_WR: int
+SHUT_RD: Final[int]
+SHUT_RDWR: Final[int]
+SHUT_WR: Final[int]
 
 # --------------------
 # Undocumented constants
 # --------------------
 
 # Undocumented address families
-AF_APPLETALK: int
-AF_DECnet: int
-AF_IPX: int
-AF_SNA: int
+AF_APPLETALK: Final[int]
+AF_DECnet: Final[int]
+AF_IPX: Final[int]
+AF_SNA: Final[int]
 
 if sys.platform != "win32":
-    AF_ROUTE: int
+    AF_ROUTE: Final[int]
 
 if sys.platform == "darwin":
-    AF_SYSTEM: int
+    AF_SYSTEM: Final[int]
 
 if sys.platform != "darwin":
-    AF_IRDA: int
+    AF_IRDA: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    AF_ASH: int
-    AF_ATMPVC: int
-    AF_ATMSVC: int
-    AF_AX25: int
-    AF_BRIDGE: int
-    AF_ECONET: int
-    AF_KEY: int
-    AF_LLC: int
-    AF_NETBEUI: int
-    AF_NETROM: int
-    AF_PPPOX: int
-    AF_ROSE: int
-    AF_SECURITY: int
-    AF_WANPIPE: int
-    AF_X25: int
+    AF_ASH: Final[int]
+    AF_ATMPVC: Final[int]
+    AF_ATMSVC: Final[int]
+    AF_AX25: Final[int]
+    AF_BRIDGE: Final[int]
+    AF_ECONET: Final[int]
+    AF_KEY: Final[int]
+    AF_LLC: Final[int]
+    AF_NETBEUI: Final[int]
+    AF_NETROM: Final[int]
+    AF_PPPOX: Final[int]
+    AF_ROSE: Final[int]
+    AF_SECURITY: Final[int]
+    AF_WANPIPE: Final[int]
+    AF_X25: Final[int]
 
 # Miscellaneous undocumented
 
 if sys.platform != "win32" and sys.platform != "linux":
-    LOCAL_PEERCRED: int
+    LOCAL_PEERCRED: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
     # Defined in linux socket.h, but this isn't always present for
     # some reason.
-    IPX_TYPE: int
+    IPX_TYPE: Final[int]
 
 # ===== Classes =====
 
@@ -743,10 +743,10 @@ class socket:
     def timeout(self) -> float | None: ...  # noqa: F811
     if sys.platform == "win32":
         def __init__(
-            self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ...
+            self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = None
         ) -> None: ...
     else:
-        def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = ...) -> None: ...
+        def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = None) -> None: ...
 
     def bind(self, address: _Address, /) -> None: ...
     def close(self) -> None: ...
@@ -766,18 +766,18 @@ class socket:
         def ioctl(self, control: int, option: int | tuple[int, int, int] | bool, /) -> None: ...
 
     def listen(self, backlog: int = ..., /) -> None: ...
-    def recv(self, bufsize: int, flags: int = ..., /) -> bytes: ...
-    def recvfrom(self, bufsize: int, flags: int = ..., /) -> tuple[bytes, _RetAddress]: ...
+    def recv(self, bufsize: int, flags: int = 0, /) -> bytes: ...
+    def recvfrom(self, bufsize: int, flags: int = 0, /) -> tuple[bytes, _RetAddress]: ...
     if sys.platform != "win32":
-        def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ...
+        def recvmsg(self, bufsize: int, ancbufsize: int = 0, flags: int = 0, /) -> tuple[bytes, list[_CMSG], int, Any]: ...
         def recvmsg_into(
-            self, buffers: Iterable[WriteableBuffer], ancbufsize: int = ..., flags: int = ..., /
+            self, buffers: Iterable[WriteableBuffer], ancbufsize: int = 0, flags: int = 0, /
         ) -> tuple[int, list[_CMSG], int, Any]: ...
 
-    def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ...
-    def recv_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> int: ...
-    def send(self, data: ReadableBuffer, flags: int = ..., /) -> int: ...
-    def sendall(self, data: ReadableBuffer, flags: int = ..., /) -> None: ...
+    def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = 0, flags: int = 0) -> tuple[int, _RetAddress]: ...
+    def recv_into(self, buffer: WriteableBuffer, nbytes: int = 0, flags: int = 0) -> int: ...
+    def send(self, data: ReadableBuffer, flags: int = 0, /) -> int: ...
+    def sendall(self, data: ReadableBuffer, flags: int = 0, /) -> None: ...
     @overload
     def sendto(self, data: ReadableBuffer, address: _Address, /) -> int: ...
     @overload
@@ -787,13 +787,13 @@ class socket:
             self,
             buffers: Iterable[ReadableBuffer],
             ancdata: Iterable[_CMSGArg] = ...,
-            flags: int = ...,
-            address: _Address | None = ...,
+            flags: int = 0,
+            address: _Address | None = None,
             /,
         ) -> int: ...
     if sys.platform == "linux":
         def sendmsg_afalg(
-            self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
+            self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0
         ) -> int: ...
 
     def setblocking(self, flag: bool, /) -> None: ...
@@ -816,12 +816,7 @@ def dup(fd: SupportsIndex, /) -> int: ...
 
 # the 5th tuple item is an address
 def getaddrinfo(
-    host: bytes | str | None,
-    port: bytes | str | int | None,
-    family: int = ...,
-    type: int = ...,
-    proto: int = ...,
-    flags: int = ...,
+    host: bytes | str | None, port: bytes | str | int | None, family: int = ..., type: int = 0, proto: int = 0, flags: int = 0
 ) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]: ...
 def gethostbyname(hostname: str, /) -> str: ...
 def gethostbyname_ex(hostname: str, /) -> tuple[str, list[str], list[str]]: ...
@@ -848,7 +843,7 @@ if sys.platform != "win32":
     def sethostname(name: str, /) -> None: ...
     def CMSG_LEN(length: int, /) -> int: ...
     def CMSG_SPACE(length: int, /) -> int: ...
-    def socketpair(family: int = ..., type: int = ..., proto: int = ..., /) -> tuple[socket, socket]: ...
+    def socketpair(family: int = ..., type: int = ..., proto: int = 0, /) -> tuple[socket, socket]: ...
 
 def if_nameindex() -> list[tuple[int, str]]: ...
 def if_nametoindex(oname: str, /) -> int: ...
diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi
index 88dd06780904..8afa3e5297bd 100644
--- a/mypy/typeshed/stdlib/_ssl.pyi
+++ b/mypy/typeshed/stdlib/_ssl.pyi
@@ -12,8 +12,8 @@ from ssl import (
     SSLWantWriteError as SSLWantWriteError,
     SSLZeroReturnError as SSLZeroReturnError,
 )
-from typing import Any, ClassVar, Literal, TypedDict, final, overload, type_check_only
-from typing_extensions import NotRequired, Self, TypeAlias
+from typing import Any, ClassVar, Final, Literal, TypedDict, final, overload, type_check_only
+from typing_extensions import NotRequired, Self, TypeAlias, deprecated
 
 _PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray
 _PCTRTT: TypeAlias = tuple[tuple[str, str], ...]
@@ -51,6 +51,7 @@ def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ...
 def RAND_bytes(n: int, /) -> bytes: ...
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.6; removed in Python 3.12. Use `ssl.RAND_bytes()` instead.")
     def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ...
 
 if sys.version_info < (3, 10):
@@ -161,135 +162,134 @@ if sys.version_info < (3, 12):
     err_names_to_codes: dict[str, tuple[int, int]]
     lib_codes_to_names: dict[int, str]
 
-_DEFAULT_CIPHERS: str
+_DEFAULT_CIPHERS: Final[str]
 
 # SSL error numbers
-SSL_ERROR_ZERO_RETURN: int
-SSL_ERROR_WANT_READ: int
-SSL_ERROR_WANT_WRITE: int
-SSL_ERROR_WANT_X509_LOOKUP: int
-SSL_ERROR_SYSCALL: int
-SSL_ERROR_SSL: int
-SSL_ERROR_WANT_CONNECT: int
-SSL_ERROR_EOF: int
-SSL_ERROR_INVALID_ERROR_CODE: int
+SSL_ERROR_ZERO_RETURN: Final = 6
+SSL_ERROR_WANT_READ: Final = 2
+SSL_ERROR_WANT_WRITE: Final = 3
+SSL_ERROR_WANT_X509_LOOKUP: Final = 4
+SSL_ERROR_SYSCALL: Final = 5
+SSL_ERROR_SSL: Final = 1
+SSL_ERROR_WANT_CONNECT: Final = 7
+SSL_ERROR_EOF: Final = 8
+SSL_ERROR_INVALID_ERROR_CODE: Final = 10
 
 # verify modes
-CERT_NONE: int
-CERT_OPTIONAL: int
-CERT_REQUIRED: int
+CERT_NONE: Final = 0
+CERT_OPTIONAL: Final = 1
+CERT_REQUIRED: Final = 2
 
 # verify flags
-VERIFY_DEFAULT: int
-VERIFY_CRL_CHECK_LEAF: int
-VERIFY_CRL_CHECK_CHAIN: int
-VERIFY_X509_STRICT: int
-VERIFY_X509_TRUSTED_FIRST: int
+VERIFY_DEFAULT: Final = 0
+VERIFY_CRL_CHECK_LEAF: Final = 0x4
+VERIFY_CRL_CHECK_CHAIN: Final = 0x8
+VERIFY_X509_STRICT: Final = 0x20
+VERIFY_X509_TRUSTED_FIRST: Final = 0x8000
 if sys.version_info >= (3, 10):
-    VERIFY_ALLOW_PROXY_CERTS: int
-    VERIFY_X509_PARTIAL_CHAIN: int
+    VERIFY_ALLOW_PROXY_CERTS: Final = 0x40
+    VERIFY_X509_PARTIAL_CHAIN: Final = 0x80000
 
 # alert descriptions
-ALERT_DESCRIPTION_CLOSE_NOTIFY: int
-ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: int
-ALERT_DESCRIPTION_BAD_RECORD_MAC: int
-ALERT_DESCRIPTION_RECORD_OVERFLOW: int
-ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: int
-ALERT_DESCRIPTION_HANDSHAKE_FAILURE: int
-ALERT_DESCRIPTION_BAD_CERTIFICATE: int
-ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: int
-ALERT_DESCRIPTION_CERTIFICATE_REVOKED: int
-ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: int
-ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: int
-ALERT_DESCRIPTION_ILLEGAL_PARAMETER: int
-ALERT_DESCRIPTION_UNKNOWN_CA: int
-ALERT_DESCRIPTION_ACCESS_DENIED: int
-ALERT_DESCRIPTION_DECODE_ERROR: int
-ALERT_DESCRIPTION_DECRYPT_ERROR: int
-ALERT_DESCRIPTION_PROTOCOL_VERSION: int
-ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: int
-ALERT_DESCRIPTION_INTERNAL_ERROR: int
-ALERT_DESCRIPTION_USER_CANCELLED: int
-ALERT_DESCRIPTION_NO_RENEGOTIATION: int
-ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: int
-ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: int
-ALERT_DESCRIPTION_UNRECOGNIZED_NAME: int
-ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: int
-ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: int
-ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: int
+ALERT_DESCRIPTION_CLOSE_NOTIFY: Final = 0
+ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: Final = 10
+ALERT_DESCRIPTION_BAD_RECORD_MAC: Final = 20
+ALERT_DESCRIPTION_RECORD_OVERFLOW: Final = 22
+ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: Final = 30
+ALERT_DESCRIPTION_HANDSHAKE_FAILURE: Final = 40
+ALERT_DESCRIPTION_BAD_CERTIFICATE: Final = 42
+ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: Final = 43
+ALERT_DESCRIPTION_CERTIFICATE_REVOKED: Final = 44
+ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: Final = 45
+ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: Final = 46
+ALERT_DESCRIPTION_ILLEGAL_PARAMETER: Final = 47
+ALERT_DESCRIPTION_UNKNOWN_CA: Final = 48
+ALERT_DESCRIPTION_ACCESS_DENIED: Final = 49
+ALERT_DESCRIPTION_DECODE_ERROR: Final = 50
+ALERT_DESCRIPTION_DECRYPT_ERROR: Final = 51
+ALERT_DESCRIPTION_PROTOCOL_VERSION: Final = 70
+ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: Final = 71
+ALERT_DESCRIPTION_INTERNAL_ERROR: Final = 80
+ALERT_DESCRIPTION_USER_CANCELLED: Final = 90
+ALERT_DESCRIPTION_NO_RENEGOTIATION: Final = 100
+ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: Final = 110
+ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: Final = 111
+ALERT_DESCRIPTION_UNRECOGNIZED_NAME: Final = 112
+ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: Final = 113
+ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: Final = 114
+ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: Final = 115
 
 # protocol versions
-PROTOCOL_SSLv23: int
-PROTOCOL_TLS: int
-PROTOCOL_TLS_CLIENT: int
-PROTOCOL_TLS_SERVER: int
-PROTOCOL_TLSv1: int
-PROTOCOL_TLSv1_1: int
-PROTOCOL_TLSv1_2: int
+PROTOCOL_SSLv23: Final = 2
+PROTOCOL_TLS: Final = 2
+PROTOCOL_TLS_CLIENT: Final = 16
+PROTOCOL_TLS_SERVER: Final = 17
+PROTOCOL_TLSv1: Final = 3
+PROTOCOL_TLSv1_1: Final = 4
+PROTOCOL_TLSv1_2: Final = 5
 
 # protocol options
-OP_ALL: int
-OP_NO_SSLv2: int
-OP_NO_SSLv3: int
-OP_NO_TLSv1: int
-OP_NO_TLSv1_1: int
-OP_NO_TLSv1_2: int
-OP_NO_TLSv1_3: int
-OP_CIPHER_SERVER_PREFERENCE: int
-OP_SINGLE_DH_USE: int
-OP_NO_TICKET: int
-OP_SINGLE_ECDH_USE: int
-OP_NO_COMPRESSION: int
-OP_ENABLE_MIDDLEBOX_COMPAT: int
-OP_NO_RENEGOTIATION: int
+OP_ALL: Final = 0x80000050
+OP_NO_SSLv2: Final = 0x0
+OP_NO_SSLv3: Final = 0x2000000
+OP_NO_TLSv1: Final = 0x4000000
+OP_NO_TLSv1_1: Final = 0x10000000
+OP_NO_TLSv1_2: Final = 0x8000000
+OP_NO_TLSv1_3: Final = 0x20000000
+OP_CIPHER_SERVER_PREFERENCE: Final = 0x400000
+OP_SINGLE_DH_USE: Final = 0x0
+OP_NO_TICKET: Final = 0x4000
+OP_SINGLE_ECDH_USE: Final = 0x0
+OP_NO_COMPRESSION: Final = 0x20000
+OP_ENABLE_MIDDLEBOX_COMPAT: Final = 0x100000
+OP_NO_RENEGOTIATION: Final = 0x40000000
 if sys.version_info >= (3, 11) or sys.platform == "linux":
-    OP_IGNORE_UNEXPECTED_EOF: int
+    OP_IGNORE_UNEXPECTED_EOF: Final = 0x80
 if sys.version_info >= (3, 12):
-    OP_LEGACY_SERVER_CONNECT: int
-    OP_ENABLE_KTLS: int
+    OP_LEGACY_SERVER_CONNECT: Final = 0x4
+    OP_ENABLE_KTLS: Final = 0x8
 
 # host flags
-HOSTFLAG_ALWAYS_CHECK_SUBJECT: int
-HOSTFLAG_NEVER_CHECK_SUBJECT: int
-HOSTFLAG_NO_WILDCARDS: int
-HOSTFLAG_NO_PARTIAL_WILDCARDS: int
-HOSTFLAG_MULTI_LABEL_WILDCARDS: int
-HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: int
+HOSTFLAG_ALWAYS_CHECK_SUBJECT: Final = 0x1
+HOSTFLAG_NEVER_CHECK_SUBJECT: Final = 0x20
+HOSTFLAG_NO_WILDCARDS: Final = 0x2
+HOSTFLAG_NO_PARTIAL_WILDCARDS: Final = 0x4
+HOSTFLAG_MULTI_LABEL_WILDCARDS: Final = 0x8
+HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: Final = 0x10
 
 if sys.version_info >= (3, 10):
     # certificate file types
-    # Typed as Literal so the overload on Certificate.public_bytes can work properly.
-    ENCODING_PEM: Literal[1]
-    ENCODING_DER: Literal[2]
+    ENCODING_PEM: Final = 1
+    ENCODING_DER: Final = 2
 
 # protocol versions
-PROTO_MINIMUM_SUPPORTED: int
-PROTO_MAXIMUM_SUPPORTED: int
-PROTO_SSLv3: int
-PROTO_TLSv1: int
-PROTO_TLSv1_1: int
-PROTO_TLSv1_2: int
-PROTO_TLSv1_3: int
+PROTO_MINIMUM_SUPPORTED: Final = -2
+PROTO_MAXIMUM_SUPPORTED: Final = -1
+PROTO_SSLv3: Final[int]
+PROTO_TLSv1: Final[int]
+PROTO_TLSv1_1: Final[int]
+PROTO_TLSv1_2: Final[int]
+PROTO_TLSv1_3: Final[int]
 
 # feature support
-HAS_SNI: bool
-HAS_TLS_UNIQUE: bool
-HAS_ECDH: bool
-HAS_NPN: bool
+HAS_SNI: Final[bool]
+HAS_TLS_UNIQUE: Final[bool]
+HAS_ECDH: Final[bool]
+HAS_NPN: Final[bool]
 if sys.version_info >= (3, 13):
-    HAS_PSK: bool
-HAS_ALPN: bool
-HAS_SSLv2: bool
-HAS_SSLv3: bool
-HAS_TLSv1: bool
-HAS_TLSv1_1: bool
-HAS_TLSv1_2: bool
-HAS_TLSv1_3: bool
+    HAS_PSK: Final[bool]
+HAS_ALPN: Final[bool]
+HAS_SSLv2: Final[bool]
+HAS_SSLv3: Final[bool]
+HAS_TLSv1: Final[bool]
+HAS_TLSv1_1: Final[bool]
+HAS_TLSv1_2: Final[bool]
+HAS_TLSv1_3: Final[bool]
 if sys.version_info >= (3, 14):
-    HAS_PHA: bool
+    HAS_PHA: Final[bool]
 
 # version info
-OPENSSL_VERSION_NUMBER: int
-OPENSSL_VERSION_INFO: tuple[int, int, int, int, int]
-OPENSSL_VERSION: str
-_OPENSSL_API_VERSION: tuple[int, int, int, int, int]
+OPENSSL_VERSION_NUMBER: Final[int]
+OPENSSL_VERSION_INFO: Final[tuple[int, int, int, int, int]]
+OPENSSL_VERSION: Final[str]
+_OPENSSL_API_VERSION: Final[tuple[int, int, int, int, int]]
diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi
index 08eb00ca442b..46366ccc1740 100644
--- a/mypy/typeshed/stdlib/_tkinter.pyi
+++ b/mypy/typeshed/stdlib/_tkinter.pyi
@@ -1,7 +1,7 @@
 import sys
 from collections.abc import Callable
 from typing import Any, ClassVar, Final, final
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 # _tkinter is meant to be only used internally by tkinter, but some tkinter
 # functions e.g. return _tkinter.Tcl_Obj objects. Tcl_Obj represents a Tcl
@@ -84,6 +84,7 @@ class TkappType:
     def record(self, script, /): ...
     def setvar(self, *ags, **kwargs): ...
     if sys.version_info < (3, 11):
+        @deprecated("Deprecated since Python 3.9; removed in Python 3.11. Use `splitlist()` instead.")
         def split(self, arg, /): ...
 
     def splitlist(self, arg, /): ...
diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi
index bd96c9bc2d31..ccb7f1b98ef3 100644
--- a/mypy/typeshed/stdlib/array.pyi
+++ b/mypy/typeshed/stdlib/array.pyi
@@ -3,11 +3,14 @@ from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite
 from collections.abc import Iterable, MutableSequence
 from types import GenericAlias
 from typing import Any, ClassVar, Literal, SupportsIndex, TypeVar, overload
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, deprecated
 
 _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"]
 _FloatTypeCode: TypeAlias = Literal["f", "d"]
-_UnicodeTypeCode: TypeAlias = Literal["u"]
+if sys.version_info >= (3, 13):
+    _UnicodeTypeCode: TypeAlias = Literal["u", "w"]
+else:
+    _UnicodeTypeCode: TypeAlias = Literal["u"]
 _TypeCode: TypeAlias = _IntTypeCode | _FloatTypeCode | _UnicodeTypeCode
 
 _T = TypeVar("_T", int, float, str)
@@ -27,10 +30,23 @@ class array(MutableSequence[_T]):
     def __new__(
         cls: type[array[float]], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., /
     ) -> array[float]: ...
-    @overload
-    def __new__(
-        cls: type[array[str]], typecode: _UnicodeTypeCode, initializer: bytes | bytearray | Iterable[str] = ..., /
-    ) -> array[str]: ...
+    if sys.version_info >= (3, 13):
+        @overload
+        def __new__(
+            cls: type[array[str]], typecode: Literal["w"], initializer: bytes | bytearray | Iterable[str] = ..., /
+        ) -> array[str]: ...
+        @overload
+        @deprecated("Deprecated since Python 3.3; will be removed in Python 3.16. Use 'w' typecode instead.")
+        def __new__(
+            cls: type[array[str]], typecode: Literal["u"], initializer: bytes | bytearray | Iterable[str] = ..., /
+        ) -> array[str]: ...
+    else:
+        @overload
+        @deprecated("Deprecated since Python 3.3; will be removed in Python 3.16.")
+        def __new__(
+            cls: type[array[str]], typecode: Literal["u"], initializer: bytes | bytearray | Iterable[str] = ..., /
+        ) -> array[str]: ...
+
     @overload
     def __new__(cls, typecode: str, initializer: Iterable[_T], /) -> Self: ...
     @overload
@@ -58,6 +74,7 @@ class array(MutableSequence[_T]):
     def tounicode(self) -> str: ...
 
     __hash__: ClassVar[None]  # type: ignore[assignment]
+    def __contains__(self, value: object, /) -> bool: ...
     def __len__(self) -> int: ...
     @overload
     def __getitem__(self, key: SupportsIndex, /) -> _T: ...
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index 3ba56f55932a..8ee867116301 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -1097,15 +1097,17 @@ class Constant(expr):
     kind: str | None
     if sys.version_info < (3, 14):
         # Aliases for value, for backwards compatibility
-        @deprecated("Will be removed in Python 3.14; use value instead")
         @property
+        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
         def n(self) -> _ConstantValue: ...
         @n.setter
+        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
         def n(self, value: _ConstantValue) -> None: ...
-        @deprecated("Will be removed in Python 3.14; use value instead")
         @property
+        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
         def s(self) -> _ConstantValue: ...
         @s.setter
+        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
         def s(self, value: _ConstantValue) -> None: ...
 
     def __init__(self, value: _ConstantValue, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/coroutines.pyi b/mypy/typeshed/stdlib/asyncio/coroutines.pyi
index 8ef30b3d3198..59212f4ec398 100644
--- a/mypy/typeshed/stdlib/asyncio/coroutines.pyi
+++ b/mypy/typeshed/stdlib/asyncio/coroutines.pyi
@@ -1,7 +1,7 @@
 import sys
 from collections.abc import Awaitable, Callable, Coroutine
 from typing import Any, TypeVar, overload
-from typing_extensions import ParamSpec, TypeGuard, TypeIs
+from typing_extensions import ParamSpec, TypeGuard, TypeIs, deprecated
 
 # Keep asyncio.__all__ updated with any changes to __all__ here
 if sys.version_info >= (3, 11):
@@ -14,6 +14,7 @@ _FunctionT = TypeVar("_FunctionT", bound=Callable[..., Any])
 _P = ParamSpec("_P")
 
 if sys.version_info < (3, 11):
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `async def` instead.")
     def coroutine(func: _FunctionT) -> _FunctionT: ...
 
 @overload
diff --git a/mypy/typeshed/stdlib/asyncio/trsock.pyi b/mypy/typeshed/stdlib/asyncio/trsock.pyi
index e74cf6fd4e05..4dacbbd49399 100644
--- a/mypy/typeshed/stdlib/asyncio/trsock.pyi
+++ b/mypy/typeshed/stdlib/asyncio/trsock.pyi
@@ -5,7 +5,7 @@ from builtins import type as Type  # alias to avoid name clashes with property n
 from collections.abc import Iterable
 from types import TracebackType
 from typing import Any, BinaryIO, NoReturn, overload
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 # These are based in socket, maybe move them out into _typeshed.pyi or such
 _Address: TypeAlias = socket._Address
@@ -42,53 +42,82 @@ class TransportSocket:
     def setblocking(self, flag: bool) -> None: ...
     if sys.version_info < (3, 11):
         def _na(self, what: str) -> None: ...
+        @deprecated("Removed in Python 3.11")
         def accept(self) -> tuple[socket.socket, _RetAddress]: ...
+        @deprecated("Removed in Python 3.11")
         def connect(self, address: _Address) -> None: ...
+        @deprecated("Removed in Python 3.11")
         def connect_ex(self, address: _Address) -> int: ...
+        @deprecated("Removed in Python 3.11")
         def bind(self, address: _Address) -> None: ...
         if sys.platform == "win32":
+            @deprecated("Removed in Python 3.11")
             def ioctl(self, control: int, option: int | tuple[int, int, int] | bool) -> None: ...
         else:
+            @deprecated("Removed in Python 3.11")
             def ioctl(self, control: int, option: int | tuple[int, int, int] | bool) -> NoReturn: ...
 
+        @deprecated("Removed in Python 3.11")
         def listen(self, backlog: int = ..., /) -> None: ...
+        @deprecated("Removed in Python 3.11")
         def makefile(self) -> BinaryIO: ...
+        @deprecated("Rmoved in Python 3.11")
         def sendfile(self, file: BinaryIO, offset: int = ..., count: int | None = ...) -> int: ...
+        @deprecated("Removed in Python 3.11")
         def close(self) -> None: ...
+        @deprecated("Removed in Python 3.11")
         def detach(self) -> int: ...
         if sys.platform == "linux":
+            @deprecated("Removed in Python 3.11")
             def sendmsg_afalg(
                 self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
             ) -> int: ...
         else:
+            @deprecated("Removed in Python 3.11.")
             def sendmsg_afalg(
                 self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
             ) -> NoReturn: ...
 
+        @deprecated("Removed in Python 3.11.")
         def sendmsg(
             self, buffers: Iterable[ReadableBuffer], ancdata: Iterable[_CMSG] = ..., flags: int = ..., address: _Address = ..., /
         ) -> int: ...
         @overload
+        @deprecated("Removed in Python 3.11.")
         def sendto(self, data: ReadableBuffer, address: _Address) -> int: ...
         @overload
+        @deprecated("Removed in Python 3.11.")
         def sendto(self, data: ReadableBuffer, flags: int, address: _Address) -> int: ...
+        @deprecated("Removed in Python 3.11.")
         def send(self, data: ReadableBuffer, flags: int = ...) -> int: ...
+        @deprecated("Removed in Python 3.11.")
         def sendall(self, data: ReadableBuffer, flags: int = ...) -> None: ...
+        @deprecated("Removed in Python 3.11.")
         def set_inheritable(self, inheritable: bool) -> None: ...
         if sys.platform == "win32":
+            @deprecated("Removed in Python 3.11.")
             def share(self, process_id: int) -> bytes: ...
         else:
+            @deprecated("Removed in Python 3.11.")
             def share(self, process_id: int) -> NoReturn: ...
 
+        @deprecated("Removed in Python 3.11.")
         def recv_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> int: ...
+        @deprecated("Removed in Python 3.11.")
         def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ...
+        @deprecated("Removed in Python 3.11.")
         def recvmsg_into(
             self, buffers: Iterable[_WriteBuffer], ancbufsize: int = ..., flags: int = ..., /
         ) -> tuple[int, list[_CMSG], int, Any]: ...
+        @deprecated("Removed in Python 3.11.")
         def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ...
+        @deprecated("Removed in Python 3.11.")
         def recvfrom(self, bufsize: int, flags: int = ...) -> tuple[bytes, _RetAddress]: ...
+        @deprecated("Removed in Python 3.11.")
         def recv(self, bufsize: int, flags: int = ...) -> bytes: ...
+        @deprecated("Removed in Python 3.11.")
         def __enter__(self) -> socket.socket: ...
+        @deprecated("Removed in Python 3.11.")
         def __exit__(
             self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
         ) -> None: ...
diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi
index 32e018c653cb..e09d335596fc 100644
--- a/mypy/typeshed/stdlib/binascii.pyi
+++ b/mypy/typeshed/stdlib/binascii.pyi
@@ -1,6 +1,6 @@
 import sys
 from _typeshed import ReadableBuffer
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 # Many functions in binascii accept buffer objects
 # or ASCII-only strings.
@@ -20,9 +20,13 @@ def a2b_qp(data: _AsciiBuffer, header: bool = False) -> bytes: ...
 def b2a_qp(data: ReadableBuffer, quotetabs: bool = False, istext: bool = True, header: bool = False) -> bytes: ...
 
 if sys.version_info < (3, 11):
+    @deprecated("Deprecated since Python 3.9; removed in Python 3.11.")
     def a2b_hqx(data: _AsciiBuffer, /) -> bytes: ...
+    @deprecated("Deprecated since Python 3.9; removed in Python 3.11.")
     def rledecode_hqx(data: ReadableBuffer, /) -> bytes: ...
+    @deprecated("Deprecated since Python 3.9; removed in Python 3.11.")
     def rlecode_hqx(data: ReadableBuffer, /) -> bytes: ...
+    @deprecated("Deprecated since Python 3.9; removed in Python 3.11.")
     def b2a_hqx(data: ReadableBuffer, /) -> bytes: ...
 
 def crc_hqx(data: ReadableBuffer, crc: int, /) -> int: ...
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index baf399e7bb77..d7c0fe27c1ee 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -348,6 +348,7 @@ class int:
     def __hash__(self) -> int: ...
     def __bool__(self) -> bool: ...
     def __index__(self) -> int: ...
+    def __format__(self, format_spec: str, /) -> str: ...
 
 class float:
     def __new__(cls, x: ConvertibleToFloat = ..., /) -> Self: ...
@@ -409,6 +410,7 @@ class float:
     def __abs__(self) -> float: ...
     def __hash__(self) -> int: ...
     def __bool__(self) -> bool: ...
+    def __format__(self, format_spec: str, /) -> str: ...
     if sys.version_info >= (3, 14):
         @classmethod
         def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ...
@@ -445,6 +447,7 @@ class complex:
     def __abs__(self) -> float: ...
     def __hash__(self) -> int: ...
     def __bool__(self) -> bool: ...
+    def __format__(self, format_spec: str, /) -> str: ...
     if sys.version_info >= (3, 11):
         def __complex__(self) -> complex: ...
     if sys.version_info >= (3, 14):
@@ -544,6 +547,7 @@ class str(Sequence[str]):
     def __ne__(self, value: object, /) -> bool: ...
     def __rmul__(self, value: SupportsIndex, /) -> str: ...  # type: ignore[misc]
     def __getnewargs__(self) -> tuple[str]: ...
+    def __format__(self, format_spec: str, /) -> str: ...
 
 class bytes(Sequence[int]):
     @overload
diff --git a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
index 9c1078983d8c..e101022babcb 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi
@@ -1,10 +1,13 @@
 import sys
-from collections.abc import Callable, Mapping
+from collections.abc import Callable
 from concurrent.futures import ThreadPoolExecutor
-from typing import Literal, Protocol, overload, type_check_only
+from typing import Any, Literal, Protocol, overload, type_check_only
 from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack
 
 _Task: TypeAlias = tuple[bytes, Literal["function", "script"]]
+_Ts = TypeVarTuple("_Ts")
+_P = ParamSpec("_P")
+_R = TypeVar("_R")
 
 @type_check_only
 class _TaskFunc(Protocol):
@@ -13,62 +16,41 @@ class _TaskFunc(Protocol):
     @overload
     def __call__(self, fn: str) -> tuple[bytes, Literal["script"]]: ...
 
-_Ts = TypeVarTuple("_Ts")
-_P = ParamSpec("_P")
-_R = TypeVar("_R")
-
-# A `type.simplenamespace` with `__name__` attribute.
-@type_check_only
-class _HasName(Protocol):
-    __name__: str
-
-# `_interpreters.exec` technically gives us a simple namespace.
-@type_check_only
-class _ExcInfo(Protocol):
-    formatted: str
-    msg: str
-    type: _HasName
-
 if sys.version_info >= (3, 14):
     from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext
+    from concurrent.interpreters import Interpreter, Queue
 
-    from _interpreters import InterpreterError
-
-    class ExecutionFailed(InterpreterError):
-        def __init__(self, excinfo: _ExcInfo) -> None: ...  #  type: ignore[override]
+    def do_call(results: Queue, func: Callable[..., _R], args: tuple[Any, ...], kwargs: dict[str, Any]) -> _R: ...
 
     class WorkerContext(ThreadWorkerContext):
-        # Parent class doesn't have `shared` argument,
-        @overload  #  type: ignore[override]
+        interp: Interpreter | None
+        results: Queue | None
+        @overload  # type: ignore[override]
         @classmethod
         def prepare(
-            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object]
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]
         ) -> tuple[Callable[[], Self], _TaskFunc]: ...
-        @overload  #  type: ignore[override]
+        @overload
         @classmethod
-        def prepare(
-            cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object]
-        ) -> tuple[Callable[[], Self], _TaskFunc]: ...
-        def __init__(
-            self, initdata: tuple[bytes, Literal["function", "script"]], shared: Mapping[str, object] | None = None
-        ) -> None: ...  #  type: ignore[override]
+        def prepare(cls, initializer: Callable[[], object], initargs: tuple[()]) -> tuple[Callable[[], Self], _TaskFunc]: ...
+        def __init__(self, initdata: _Task) -> None: ...
         def __del__(self) -> None: ...
-        def run(self, task: _Task) -> None: ...  #  type: ignore[override]
+        def run(self, task: _Task) -> None: ...  # type: ignore[override]
 
     class BrokenInterpreterPool(BrokenThreadPool): ...
 
     class InterpreterPoolExecutor(ThreadPoolExecutor):
         BROKEN: type[BrokenInterpreterPool]
 
-        @overload  #  type: ignore[override]
+        @overload  # type: ignore[override]
         @classmethod
         def prepare_context(
-            cls, initializer: Callable[[], object], initargs: tuple[()], shared: Mapping[str, object]
+            cls, initializer: Callable[[], object], initargs: tuple[()]
         ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ...
-        @overload  #  type: ignore[override]
+        @overload
         @classmethod
         def prepare_context(
-            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], shared: Mapping[str, object]
+            cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]
         ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ...
         @overload
         def __init__(
@@ -77,7 +59,6 @@ if sys.version_info >= (3, 14):
             thread_name_prefix: str = "",
             initializer: Callable[[], object] | None = None,
             initargs: tuple[()] = (),
-            shared: Mapping[str, object] | None = None,
         ) -> None: ...
         @overload
         def __init__(
@@ -87,7 +68,6 @@ if sys.version_info >= (3, 14):
             *,
             initializer: Callable[[Unpack[_Ts]], object],
             initargs: tuple[Unpack[_Ts]],
-            shared: Mapping[str, object] | None = None,
         ) -> None: ...
         @overload
         def __init__(
@@ -96,5 +76,4 @@ if sys.version_info >= (3, 14):
             thread_name_prefix: str,
             initializer: Callable[[Unpack[_Ts]], object],
             initargs: tuple[Unpack[_Ts]],
-            shared: Mapping[str, object] | None = None,
         ) -> None: ...
diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi
index fb02701e3711..b3a421003026 100644
--- a/mypy/typeshed/stdlib/configparser.pyi
+++ b/mypy/typeshed/stdlib/configparser.pyi
@@ -3,7 +3,7 @@ from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite
 from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence
 from re import Pattern
 from typing import Any, ClassVar, Final, Literal, TypeVar, overload, type_check_only
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 if sys.version_info >= (3, 14):
     __all__ = (
@@ -137,6 +137,9 @@ class BasicInterpolation(Interpolation): ...
 class ExtendedInterpolation(Interpolation): ...
 
 if sys.version_info < (3, 13):
+    @deprecated(
+        "Deprecated since Python 3.2; removed in Python 3.13. Use `BasicInterpolation` or `ExtendedInterpolation` instead."
+    )
     class LegacyInterpolation(Interpolation):
         def before_get(self, parser: _Parser, section: _SectionName, option: str, value: str, vars: _Section) -> str: ...
 
@@ -271,6 +274,7 @@ class RawConfigParser(_Parser):
     def read_string(self, string: str, source: str = "") -> None: ...
     def read_dict(self, dictionary: Mapping[str, Mapping[str, Any]], source: str = "") -> None: ...
     if sys.version_info < (3, 12):
+        @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `parser.read_file()` instead.")
         def readfp(self, fp: Iterable[str], filename: str | None = None) -> None: ...
     # These get* methods are partially applied (with the same names) in
     # SectionProxy; the stubs should be kept updated together
@@ -331,7 +335,8 @@ class ConfigParser(RawConfigParser):
     ) -> str | _T: ...
 
 if sys.version_info < (3, 12):
-    class SafeConfigParser(ConfigParser): ...  # deprecated alias
+    @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `ConfigParser` instead.")
+    class SafeConfigParser(ConfigParser): ...
 
 class SectionProxy(MutableMapping[str, str]):
     def __init__(self, parser: RawConfigParser, name: str) -> None: ...
@@ -443,10 +448,22 @@ class ParsingError(Error):
     elif sys.version_info >= (3, 12):
         def __init__(self, source: str) -> None: ...
     else:
-        def __init__(self, source: str | None = None, filename: str | None = None) -> None: ...
+        @overload
+        def __init__(self, source: str, filename: None = None) -> None: ...
+        @overload
+        @deprecated("The `filename` parameter removed in Python 3.12. Use `source` instead.")
+        def __init__(self, source: None = None, filename: str = ...) -> None: ...
 
     def append(self, lineno: int, line: str) -> None: ...
 
+    if sys.version_info < (3, 12):
+        @property
+        @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `source` instead.")
+        def filename(self) -> str: ...
+        @filename.setter
+        @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `source` instead.")
+        def filename(self, value: str) -> None: ...
+
 class MissingSectionHeaderError(ParsingError):
     lineno: int
     line: str
diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi
index 2fe64eb53201..5a3e89b0c676 100644
--- a/mypy/typeshed/stdlib/fcntl.pyi
+++ b/mypy/typeshed/stdlib/fcntl.pyi
@@ -4,107 +4,107 @@ from typing import Any, Final, Literal, overload
 from typing_extensions import Buffer
 
 if sys.platform != "win32":
-    FASYNC: int
-    FD_CLOEXEC: int
-    F_DUPFD: int
-    F_DUPFD_CLOEXEC: int
-    F_GETFD: int
-    F_GETFL: int
-    F_GETLK: int
-    F_GETOWN: int
-    F_RDLCK: int
-    F_SETFD: int
-    F_SETFL: int
-    F_SETLK: int
-    F_SETLKW: int
-    F_SETOWN: int
-    F_UNLCK: int
-    F_WRLCK: int
+    FASYNC: Final[int]
+    FD_CLOEXEC: Final[int]
+    F_DUPFD: Final[int]
+    F_DUPFD_CLOEXEC: Final[int]
+    F_GETFD: Final[int]
+    F_GETFL: Final[int]
+    F_GETLK: Final[int]
+    F_GETOWN: Final[int]
+    F_RDLCK: Final[int]
+    F_SETFD: Final[int]
+    F_SETFL: Final[int]
+    F_SETLK: Final[int]
+    F_SETLKW: Final[int]
+    F_SETOWN: Final[int]
+    F_UNLCK: Final[int]
+    F_WRLCK: Final[int]
 
-    F_GETLEASE: int
-    F_SETLEASE: int
+    F_GETLEASE: Final[int]
+    F_SETLEASE: Final[int]
     if sys.platform == "darwin":
-        F_FULLFSYNC: int
-        F_NOCACHE: int
-        F_GETPATH: int
+        F_FULLFSYNC: Final[int]
+        F_NOCACHE: Final[int]
+        F_GETPATH: Final[int]
     if sys.platform == "linux":
-        F_SETLKW64: int
-        F_SETSIG: int
-        F_SHLCK: int
-        F_SETLK64: int
-        F_GETSIG: int
-        F_NOTIFY: int
-        F_EXLCK: int
-        F_GETLK64: int
-        F_ADD_SEALS: int
-        F_GET_SEALS: int
-        F_SEAL_GROW: int
-        F_SEAL_SEAL: int
-        F_SEAL_SHRINK: int
-        F_SEAL_WRITE: int
+        F_SETLKW64: Final[int]
+        F_SETSIG: Final[int]
+        F_SHLCK: Final[int]
+        F_SETLK64: Final[int]
+        F_GETSIG: Final[int]
+        F_NOTIFY: Final[int]
+        F_EXLCK: Final[int]
+        F_GETLK64: Final[int]
+        F_ADD_SEALS: Final[int]
+        F_GET_SEALS: Final[int]
+        F_SEAL_GROW: Final[int]
+        F_SEAL_SEAL: Final[int]
+        F_SEAL_SHRINK: Final[int]
+        F_SEAL_WRITE: Final[int]
         F_OFD_GETLK: Final[int]
         F_OFD_SETLK: Final[int]
         F_OFD_SETLKW: Final[int]
 
         if sys.version_info >= (3, 10):
-            F_GETPIPE_SZ: int
-            F_SETPIPE_SZ: int
+            F_GETPIPE_SZ: Final[int]
+            F_SETPIPE_SZ: Final[int]
 
-        DN_ACCESS: int
-        DN_ATTRIB: int
-        DN_CREATE: int
-        DN_DELETE: int
-        DN_MODIFY: int
-        DN_MULTISHOT: int
-        DN_RENAME: int
+        DN_ACCESS: Final[int]
+        DN_ATTRIB: Final[int]
+        DN_CREATE: Final[int]
+        DN_DELETE: Final[int]
+        DN_MODIFY: Final[int]
+        DN_MULTISHOT: Final[int]
+        DN_RENAME: Final[int]
 
-    LOCK_EX: int
-    LOCK_NB: int
-    LOCK_SH: int
-    LOCK_UN: int
+    LOCK_EX: Final[int]
+    LOCK_NB: Final[int]
+    LOCK_SH: Final[int]
+    LOCK_UN: Final[int]
     if sys.platform == "linux":
-        LOCK_MAND: int
-        LOCK_READ: int
-        LOCK_RW: int
-        LOCK_WRITE: int
+        LOCK_MAND: Final[int]
+        LOCK_READ: Final[int]
+        LOCK_RW: Final[int]
+        LOCK_WRITE: Final[int]
 
     if sys.platform == "linux":
         # Constants for the POSIX STREAMS interface. Present in glibc until 2.29 (released February 2019).
         # Never implemented on BSD, and considered "obsolescent" starting in POSIX 2008.
         # Probably still used on Solaris.
-        I_ATMARK: int
-        I_CANPUT: int
-        I_CKBAND: int
-        I_FDINSERT: int
-        I_FIND: int
-        I_FLUSH: int
-        I_FLUSHBAND: int
-        I_GETBAND: int
-        I_GETCLTIME: int
-        I_GETSIG: int
-        I_GRDOPT: int
-        I_GWROPT: int
-        I_LINK: int
-        I_LIST: int
-        I_LOOK: int
-        I_NREAD: int
-        I_PEEK: int
-        I_PLINK: int
-        I_POP: int
-        I_PUNLINK: int
-        I_PUSH: int
-        I_RECVFD: int
-        I_SENDFD: int
-        I_SETCLTIME: int
-        I_SETSIG: int
-        I_SRDOPT: int
-        I_STR: int
-        I_SWROPT: int
-        I_UNLINK: int
+        I_ATMARK: Final[int]
+        I_CANPUT: Final[int]
+        I_CKBAND: Final[int]
+        I_FDINSERT: Final[int]
+        I_FIND: Final[int]
+        I_FLUSH: Final[int]
+        I_FLUSHBAND: Final[int]
+        I_GETBAND: Final[int]
+        I_GETCLTIME: Final[int]
+        I_GETSIG: Final[int]
+        I_GRDOPT: Final[int]
+        I_GWROPT: Final[int]
+        I_LINK: Final[int]
+        I_LIST: Final[int]
+        I_LOOK: Final[int]
+        I_NREAD: Final[int]
+        I_PEEK: Final[int]
+        I_PLINK: Final[int]
+        I_POP: Final[int]
+        I_PUNLINK: Final[int]
+        I_PUSH: Final[int]
+        I_RECVFD: Final[int]
+        I_SENDFD: Final[int]
+        I_SETCLTIME: Final[int]
+        I_SETSIG: Final[int]
+        I_SRDOPT: Final[int]
+        I_STR: Final[int]
+        I_SWROPT: Final[int]
+        I_UNLINK: Final[int]
 
     if sys.version_info >= (3, 12) and sys.platform == "linux":
-        FICLONE: int
-        FICLONERANGE: int
+        FICLONE: Final[int]
+        FICLONERANGE: Final[int]
 
     if sys.version_info >= (3, 13) and sys.platform == "linux":
         F_OWNER_TID: Final = 0
diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi
index 5ff98b052cdb..937aece03437 100644
--- a/mypy/typeshed/stdlib/gettext.pyi
+++ b/mypy/typeshed/stdlib/gettext.pyi
@@ -3,6 +3,7 @@ import sys
 from _typeshed import StrPath
 from collections.abc import Callable, Container, Iterable, Sequence
 from typing import Any, Final, Literal, Protocol, TypeVar, overload, type_check_only
+from typing_extensions import deprecated
 
 __all__ = [
     "NullTranslations",
@@ -43,9 +44,13 @@ class NullTranslations:
     def info(self) -> dict[str, str]: ...
     def charset(self) -> str | None: ...
     if sys.version_info < (3, 11):
+        @deprecated("Deprecated since Python 3.8; removed in Python 3.11.")
         def output_charset(self) -> str | None: ...
+        @deprecated("Deprecated since Python 3.8; removed in Python 3.11.")
         def set_output_charset(self, charset: str) -> None: ...
+        @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `gettext()` instead.")
         def lgettext(self, message: str) -> str: ...
+        @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `ngettext()` instead.")
         def lngettext(self, msgid1: str, msgid2: str, n: int) -> str: ...
 
     def install(self, names: Container[str] | None = None) -> None: ...
@@ -145,9 +150,16 @@ else:
         fallback: bool = False,
         codeset: str | None = None,
     ) -> NullTranslations: ...
+    @overload
     def install(
-        domain: str, localedir: StrPath | None = None, codeset: str | None = None, names: Container[str] | None = None
+        domain: str, localedir: StrPath | None = None, codeset: None = None, names: Container[str] | None = None
     ) -> None: ...
+    @overload
+    @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.")
+    def install(domain: str, localedir: StrPath | None, codeset: str, /, names: Container[str] | None = None) -> None: ...
+    @overload
+    @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.")
+    def install(domain: str, localedir: StrPath | None = None, *, codeset: str, names: Container[str] | None = None) -> None: ...
 
 def textdomain(domain: str | None = None) -> str: ...
 def bindtextdomain(domain: str, localedir: StrPath | None = None) -> str: ...
@@ -161,10 +173,15 @@ def npgettext(context: str, msgid1: str, msgid2: str, n: int) -> str: ...
 def dnpgettext(domain: str, context: str, msgid1: str, msgid2: str, n: int) -> str: ...
 
 if sys.version_info < (3, 11):
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `gettext()` instead.")
     def lgettext(message: str) -> str: ...
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `dgettext()` instead.")
     def ldgettext(domain: str, message: str) -> str: ...
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `ngettext()` instead.")
     def lngettext(msgid1: str, msgid2: str, n: int) -> str: ...
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `dngettext()` instead.")
     def ldngettext(domain: str, msgid1: str, msgid2: str, n: int) -> str: ...
+    @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `bindtextdomain()` instead.")
     def bind_textdomain_codeset(domain: str, codeset: str | None = None) -> str: ...
 
 Catalog = translation
diff --git a/mypy/typeshed/stdlib/glob.pyi b/mypy/typeshed/stdlib/glob.pyi
index 03cb5418e256..63069d8009c8 100644
--- a/mypy/typeshed/stdlib/glob.pyi
+++ b/mypy/typeshed/stdlib/glob.pyi
@@ -2,14 +2,22 @@ import sys
 from _typeshed import StrOrBytesPath
 from collections.abc import Iterator, Sequence
 from typing import AnyStr
+from typing_extensions import deprecated
 
 __all__ = ["escape", "glob", "iglob"]
 
 if sys.version_info >= (3, 13):
     __all__ += ["translate"]
 
-def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
-def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
+if sys.version_info >= (3, 10):
+    @deprecated("Will be removed in Python 3.15; Use `glob.glob` and pass *root_dir* argument instead.")
+    def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
+    @deprecated("Will be removed in Python 3.15; Use `glob.glob` and pass *root_dir* argument instead.")
+    def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
+
+else:
+    def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
+    def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
 
 if sys.version_info >= (3, 11):
     def glob(
diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi
index 06f5e2880bd5..b18f76f06e3e 100644
--- a/mypy/typeshed/stdlib/gzip.pyi
+++ b/mypy/typeshed/stdlib/gzip.pyi
@@ -3,7 +3,7 @@ import zlib
 from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath, WriteableBuffer
 from io import FileIO, TextIOWrapper
 from typing import Final, Literal, Protocol, overload, type_check_only
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 if sys.version_info >= (3, 14):
     from compression._common._streams import BaseStream, DecompressReader
@@ -143,6 +143,7 @@ class GzipFile(BaseStream):
     ) -> None: ...
     if sys.version_info < (3, 12):
         @property
+        @deprecated("Deprecated since Python 2.6; removed in Python 3.12. Use `name` attribute instead.")
         def filename(self) -> str: ...
 
     @property
diff --git a/mypy/typeshed/stdlib/html/parser.pyi b/mypy/typeshed/stdlib/html/parser.pyi
index 45336f03aaa7..8b3fce0010b7 100644
--- a/mypy/typeshed/stdlib/html/parser.pyi
+++ b/mypy/typeshed/stdlib/html/parser.pyi
@@ -7,7 +7,8 @@ __all__ = ["HTMLParser"]
 
 class HTMLParser(ParserBase):
     CDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]]
-    if sys.version_info >= (3, 14):
+    if sys.version_info >= (3, 13):
+        # Added in 3.13.6
         RCDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]]
 
     def __init__(self, *, convert_charrefs: bool = True) -> None: ...
@@ -31,7 +32,8 @@ class HTMLParser(ParserBase):
     def parse_html_declaration(self, i: int) -> int: ...  # undocumented
     def parse_pi(self, i: int) -> int: ...  # undocumented
     def parse_starttag(self, i: int) -> int: ...  # undocumented
-    if sys.version_info >= (3, 14):
+    if sys.version_info >= (3, 13):
+        # `escapable` parameter added in 3.13.6
         def set_cdata_mode(self, elem: str, *, escapable: bool = False) -> None: ...  # undocumented
     else:
         def set_cdata_mode(self, elem: str) -> None: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi
index cab81512e92f..d60f90adee19 100644
--- a/mypy/typeshed/stdlib/importlib/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/__init__.pyi
@@ -2,6 +2,7 @@ import sys
 from importlib._bootstrap import __import__ as __import__
 from importlib.abc import Loader
 from types import ModuleType
+from typing_extensions import deprecated
 
 __all__ = ["__import__", "import_module", "invalidate_caches", "reload"]
 
@@ -9,6 +10,7 @@ __all__ = ["__import__", "import_module", "invalidate_caches", "reload"]
 def import_module(name: str, package: str | None = None) -> ModuleType: ...
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `importlib.util.find_spec()` instead.")
     def find_loader(name: str, path: str | None = None) -> Loader | None: ...
 
 def invalidate_caches() -> None: ...
diff --git a/mypy/typeshed/stdlib/importlib/_abc.pyi b/mypy/typeshed/stdlib/importlib/_abc.pyi
index 1a21b9a72cd8..90ab34021917 100644
--- a/mypy/typeshed/stdlib/importlib/_abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/_abc.pyi
@@ -2,11 +2,16 @@ import sys
 import types
 from abc import ABCMeta
 from importlib.machinery import ModuleSpec
+from typing_extensions import deprecated
 
 if sys.version_info >= (3, 10):
     class Loader(metaclass=ABCMeta):
         def load_module(self, fullname: str) -> types.ModuleType: ...
         if sys.version_info < (3, 12):
+            @deprecated(
+                "Deprecated since Python 3.4; removed in Python 3.12. "
+                "The module spec is now used by the import machinery to generate a module repr."
+            )
             def module_repr(self, module: types.ModuleType) -> str: ...
 
         def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ...
diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi
index cf0fd0807b7b..ef87663cb72d 100644
--- a/mypy/typeshed/stdlib/importlib/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/abc.pyi
@@ -37,6 +37,7 @@ else:
         def exec_module(self, module: types.ModuleType) -> None: ...
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use `MetaPathFinder` or `PathEntryFinder` instead.")
     class Finder(metaclass=ABCMeta): ...
 
 @deprecated("Deprecated as of Python 3.7: Use importlib.resources.abc.TraversableResources instead.")
@@ -71,6 +72,7 @@ if sys.version_info >= (3, 10):
     # Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol
     class MetaPathFinder(metaclass=ABCMeta):
         if sys.version_info < (3, 12):
+            @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `MetaPathFinder.find_spec()` instead.")
             def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ...
 
         def invalidate_caches(self) -> None: ...
@@ -81,7 +83,9 @@ if sys.version_info >= (3, 10):
 
     class PathEntryFinder(metaclass=ABCMeta):
         if sys.version_info < (3, 12):
+            @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `PathEntryFinder.find_spec()` instead.")
             def find_module(self, fullname: str) -> Loader | None: ...
+            @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
             def find_loader(self, fullname: str) -> tuple[Loader | None, Sequence[str]]: ...
 
         def invalidate_caches(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
index 789878382ceb..d1315b2eb2f1 100644
--- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
@@ -11,7 +11,7 @@ from os import PathLike
 from pathlib import Path
 from re import Pattern
 from typing import Any, ClassVar, Generic, NamedTuple, TypeVar, overload
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, deprecated
 
 _T = TypeVar("_T")
 _KT = TypeVar("_KT")
@@ -89,8 +89,8 @@ class EntryPoint(_EntryPointBase):
         ) -> bool: ...  # undocumented
 
     def __hash__(self) -> int: ...
-    def __eq__(self, other: object) -> bool: ...
     if sys.version_info >= (3, 11):
+        def __eq__(self, other: object) -> bool: ...
         def __lt__(self, other: object) -> bool: ...
     if sys.version_info < (3, 12):
         def __iter__(self) -> Iterator[Any]: ...  # result of iter((str, Self)), really
@@ -148,6 +148,7 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12):
         def keys(self) -> dict_keys[_KT, _VT]: ...
         def values(self) -> dict_values[_KT, _VT]: ...
 
+    @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `select` instead.")
     class SelectableGroups(Deprecated[str, EntryPoints], dict[str, EntryPoints]):  # use as dict is deprecated since 3.10
         @classmethod
         def load(cls, eps: Iterable[EntryPoint]) -> Self: ...
diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi
index 370a08623842..05c4d0d1edb3 100644
--- a/mypy/typeshed/stdlib/importlib/util.pyi
+++ b/mypy/typeshed/stdlib/importlib/util.pyi
@@ -12,13 +12,25 @@ from importlib._bootstrap_external import (
     spec_from_file_location as spec_from_file_location,
 )
 from importlib.abc import Loader
-from typing_extensions import ParamSpec
+from typing_extensions import ParamSpec, deprecated
 
 _P = ParamSpec("_P")
 
 if sys.version_info < (3, 12):
+    @deprecated(
+        "Deprecated since Python 3.4; removed in Python 3.12. "
+        "`__name__`, `__package__` and `__loader__` are now set automatically."
+    )
     def module_for_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...
+    @deprecated(
+        "Deprecated since Python 3.4; removed in Python 3.12. "
+        "`__name__`, `__package__` and `__loader__` are now set automatically."
+    )
     def set_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...
+    @deprecated(
+        "Deprecated since Python 3.4; removed in Python 3.12. "
+        "`__name__`, `__package__` and `__loader__` are now set automatically."
+    )
     def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...
 
 def resolve_name(name: str, package: str | None) -> str: ...
diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi
index e73f9e75838d..f8ec6cad0160 100644
--- a/mypy/typeshed/stdlib/inspect.pyi
+++ b/mypy/typeshed/stdlib/inspect.pyi
@@ -26,7 +26,7 @@ from types import (
     WrapperDescriptorType,
 )
 from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only
-from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs
+from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs, deprecated
 
 if sys.version_info >= (3, 14):
     from annotationlib import Format
@@ -476,12 +476,14 @@ class Arguments(NamedTuple):
 def getargs(co: CodeType) -> Arguments: ...
 
 if sys.version_info < (3, 11):
+    @deprecated("Deprecated since Python 3.0; removed in Python 3.11.")
     class ArgSpec(NamedTuple):
         args: list[str]
         varargs: str | None
         keywords: str | None
         defaults: tuple[Any, ...]
 
+    @deprecated("Deprecated since Python 3.0; removed in Python 3.11. Use `inspect.signature()` instead.")
     def getargspec(func: object) -> ArgSpec: ...
 
 class FullArgSpec(NamedTuple):
@@ -512,6 +514,9 @@ else:
 def formatannotationrelativeto(object: object) -> Callable[[object], str]: ...
 
 if sys.version_info < (3, 11):
+    @deprecated(
+        "Deprecated since Python 3.5; removed in Python 3.11. Use `inspect.signature()` and the `Signature` class instead."
+    )
     def formatargspec(
         args: list[str],
         varargs: str | None = None,
diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi
index 58de65449572..fae9f849b637 100644
--- a/mypy/typeshed/stdlib/locale.pyi
+++ b/mypy/typeshed/stdlib/locale.pyi
@@ -18,6 +18,7 @@ from builtins import str as _str
 from collections.abc import Callable, Iterable
 from decimal import Decimal
 from typing import Any
+from typing_extensions import deprecated
 
 if sys.version_info >= (3, 11):
     from _locale import getencoding as getencoding
@@ -137,9 +138,14 @@ def getpreferredencoding(do_setlocale: bool = True) -> _str: ...
 def normalize(localename: _str) -> _str: ...
 
 if sys.version_info < (3, 13):
-    def resetlocale(category: int = ...) -> None: ...
+    if sys.version_info >= (3, 11):
+        @deprecated("Deprecated since Python 3.11; removed in Python 3.13. Use `locale.setlocale(locale.LC_ALL, '')` instead.")
+        def resetlocale(category: int = ...) -> None: ...
+    else:
+        def resetlocale(category: int = ...) -> None: ...
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.7; removed in Python 3.12. Use `locale.format_string()` instead.")
     def format(
         percent: _str, value: float | Decimal, grouping: bool = False, monetary: bool = False, *additional: Any
     ) -> _str: ...
diff --git a/mypy/typeshed/stdlib/pathlib/__init__.pyi b/mypy/typeshed/stdlib/pathlib/__init__.pyi
index 774478bb2ff4..4858f8db1ed0 100644
--- a/mypy/typeshed/stdlib/pathlib/__init__.pyi
+++ b/mypy/typeshed/stdlib/pathlib/__init__.pyi
@@ -245,9 +245,13 @@ class Path(PurePath):
         self, mode: str, buffering: int = -1, encoding: str | None = None, errors: str | None = None, newline: str | None = None
     ) -> IO[Any]: ...
 
-    # These methods do "exist" on Windows on <3.13, but they always raise NotImplementedError.
+    # These methods do "exist" on Windows, but they always raise NotImplementedError.
     if sys.platform == "win32":
-        if sys.version_info < (3, 13):
+        if sys.version_info >= (3, 13):
+            # raises UnsupportedOperation:
+            def owner(self: Never, *, follow_symlinks: bool = True) -> str: ...  # type: ignore[misc]
+            def group(self: Never, *, follow_symlinks: bool = True) -> str: ...  # type: ignore[misc]
+        else:
             def owner(self: Never) -> str: ...  # type: ignore[misc]
             def group(self: Never) -> str: ...  # type: ignore[misc]
     else:
@@ -297,7 +301,7 @@ class Path(PurePath):
         def write_text(self, data: str, encoding: str | None = None, errors: str | None = None) -> int: ...
     if sys.version_info < (3, 12):
         if sys.version_info >= (3, 10):
-            @deprecated("Deprecated as of Python 3.10 and removed in Python 3.12. Use hardlink_to() instead.")
+            @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `hardlink_to()` instead.")
             def link_to(self, target: StrOrBytesPath) -> None: ...
         else:
             def link_to(self, target: StrOrBytesPath) -> None: ...
diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi
index e764d08e79f8..7c70dcc4c5ab 100644
--- a/mypy/typeshed/stdlib/pkgutil.pyi
+++ b/mypy/typeshed/stdlib/pkgutil.pyi
@@ -30,17 +30,23 @@ class ModuleInfo(NamedTuple):
 def extend_path(path: _PathT, name: str) -> _PathT: ...
 
 if sys.version_info < (3, 12):
+    @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use the `importlib` module instead.")
     class ImpImporter:
         def __init__(self, path: StrOrBytesPath | None = None) -> None: ...
 
+    @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use the `importlib` module instead.")
     class ImpLoader:
         def __init__(self, fullname: str, file: IO[str], filename: StrOrBytesPath, etc: tuple[str, str, int]) -> None: ...
 
 if sys.version_info < (3, 14):
-    @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
-    def find_loader(fullname: str) -> LoaderProtocol | None: ...
-    @deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
-    def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
+    if sys.version_info >= (3, 12):
+        @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `importlib.util.find_spec()` instead.")
+        def find_loader(fullname: str) -> LoaderProtocol | None: ...
+        @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `importlib.util.find_spec()` instead.")
+        def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
+    else:
+        def find_loader(fullname: str) -> LoaderProtocol | None: ...
+        def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
 
 def get_importer(path_item: StrOrBytesPath) -> PathEntryFinderProtocol | None: ...
 def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ...
diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi
index 941915179c4a..d1c78f9e3dd6 100644
--- a/mypy/typeshed/stdlib/pty.pyi
+++ b/mypy/typeshed/stdlib/pty.pyi
@@ -15,10 +15,14 @@ if sys.platform != "win32":
     def openpty() -> tuple[int, int]: ...
 
     if sys.version_info < (3, 14):
-        @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead")
-        def master_open() -> tuple[int, str]: ...
-        @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead")
-        def slave_open(tty_name: str) -> int: ...
+        if sys.version_info >= (3, 12):
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `openpty()` instead.")
+            def master_open() -> tuple[int, str]: ...
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `openpty()` instead.")
+            def slave_open(tty_name: str) -> int: ...
+        else:
+            def master_open() -> tuple[int, str]: ...
+            def slave_open(tty_name: str) -> int: ...
 
     def fork() -> tuple[int, int]: ...
     def spawn(argv: str | Iterable[str], master_read: _Reader = ..., stdin_read: _Reader = ...) -> int: ...
diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi
index b080626c5802..fb2a06d5e4c8 100644
--- a/mypy/typeshed/stdlib/re.pyi
+++ b/mypy/typeshed/stdlib/re.pyi
@@ -6,7 +6,7 @@ from _typeshed import MaybeNone, ReadableBuffer
 from collections.abc import Callable, Iterator, Mapping
 from types import GenericAlias
 from typing import Any, AnyStr, Final, Generic, Literal, TypeVar, final, overload
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 __all__ = [
     "match",
@@ -307,4 +307,8 @@ def escape(pattern: AnyStr) -> AnyStr: ...
 def purge() -> None: ...
 
 if sys.version_info < (3, 13):
-    def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ...
+    if sys.version_info >= (3, 11):
+        @deprecated("Deprecated since Python 3.11; removed in Python 3.13. Use `re.compile()` instead.")
+        def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ...  # undocumented
+    else:
+        def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/smtpd.pyi b/mypy/typeshed/stdlib/smtpd.pyi
index 7392bd51627d..dee7e949f42f 100644
--- a/mypy/typeshed/stdlib/smtpd.pyi
+++ b/mypy/typeshed/stdlib/smtpd.pyi
@@ -4,7 +4,7 @@ import socket
 import sys
 from collections import defaultdict
 from typing import Any
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, deprecated
 
 if sys.version_info >= (3, 11):
     __all__ = ["SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy"]
@@ -87,5 +87,6 @@ class PureProxy(SMTPServer):
     def process_message(self, peer: _Address, mailfrom: str, rcpttos: list[str], data: bytes | str) -> str | None: ...  # type: ignore[override]
 
 if sys.version_info < (3, 11):
+    @deprecated("Deprecated since Python 3.9; removed in Python 3.11.")
     class MailmanProxy(PureProxy):
         def process_message(self, peer: _Address, mailfrom: str, rcpttos: list[str], data: bytes | str) -> str | None: ...  # type: ignore[override]
diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi
index 491551dd52b1..d62f4c228151 100644
--- a/mypy/typeshed/stdlib/socket.pyi
+++ b/mypy/typeshed/stdlib/socket.pyi
@@ -1051,14 +1051,12 @@ if sys.version_info >= (3, 14):
 
     if sys.platform == "linux":
         from _socket import (
-            CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER,
             IP_FREEBIND as IP_FREEBIND,
             IP_RECVORIGDSTADDR as IP_RECVORIGDSTADDR,
-            SO_ORIGINAL_DST as SO_ORIGINAL_DST,
             VMADDR_CID_LOCAL as VMADDR_CID_LOCAL,
         )
 
-        __all__ += ["CAN_RAW_ERR_FILTER", "IP_FREEBIND", "IP_RECVORIGDSTADDR", "VMADDR_CID_LOCAL"]
+        __all__ += ["IP_FREEBIND", "IP_RECVORIGDSTADDR", "VMADDR_CID_LOCAL"]
 
 # Re-exported from errno
 EBADF: int
diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi
index 1b8631d3fb12..f1893ec3194f 100644
--- a/mypy/typeshed/stdlib/ssl.pyi
+++ b/mypy/typeshed/stdlib/ssl.pyi
@@ -27,7 +27,7 @@ from _ssl import (
 )
 from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
 from collections.abc import Callable, Iterable
-from typing import Any, Literal, NamedTuple, TypedDict, overload, type_check_only
+from typing import Any, Final, Literal, NamedTuple, TypedDict, overload, type_check_only
 from typing_extensions import Never, Self, TypeAlias, deprecated
 
 if sys.version_info >= (3, 13):
@@ -125,9 +125,9 @@ class VerifyMode(enum.IntEnum):
     CERT_OPTIONAL = 1
     CERT_REQUIRED = 2
 
-CERT_NONE: VerifyMode
-CERT_OPTIONAL: VerifyMode
-CERT_REQUIRED: VerifyMode
+CERT_NONE: Final = VerifyMode.CERT_NONE
+CERT_OPTIONAL: Final = VerifyMode.CERT_OPTIONAL
+CERT_REQUIRED: Final = VerifyMode.CERT_REQUIRED
 
 class VerifyFlags(enum.IntFlag):
     VERIFY_DEFAULT = 0
@@ -139,15 +139,15 @@ class VerifyFlags(enum.IntFlag):
         VERIFY_ALLOW_PROXY_CERTS = 64
         VERIFY_X509_PARTIAL_CHAIN = 524288
 
-VERIFY_DEFAULT: VerifyFlags
-VERIFY_CRL_CHECK_LEAF: VerifyFlags
-VERIFY_CRL_CHECK_CHAIN: VerifyFlags
-VERIFY_X509_STRICT: VerifyFlags
-VERIFY_X509_TRUSTED_FIRST: VerifyFlags
+VERIFY_DEFAULT: Final = VerifyFlags.VERIFY_DEFAULT
+VERIFY_CRL_CHECK_LEAF: Final = VerifyFlags.VERIFY_CRL_CHECK_LEAF
+VERIFY_CRL_CHECK_CHAIN: Final = VerifyFlags.VERIFY_CRL_CHECK_CHAIN
+VERIFY_X509_STRICT: Final = VerifyFlags.VERIFY_X509_STRICT
+VERIFY_X509_TRUSTED_FIRST: Final = VerifyFlags.VERIFY_X509_TRUSTED_FIRST
 
 if sys.version_info >= (3, 10):
-    VERIFY_ALLOW_PROXY_CERTS: VerifyFlags
-    VERIFY_X509_PARTIAL_CHAIN: VerifyFlags
+    VERIFY_ALLOW_PROXY_CERTS: Final = VerifyFlags.VERIFY_ALLOW_PROXY_CERTS
+    VERIFY_X509_PARTIAL_CHAIN: Final = VerifyFlags.VERIFY_X509_PARTIAL_CHAIN
 
 class _SSLMethod(enum.IntEnum):
     PROTOCOL_SSLv23 = 2
@@ -160,15 +160,15 @@ class _SSLMethod(enum.IntEnum):
     PROTOCOL_TLS_CLIENT = 16
     PROTOCOL_TLS_SERVER = 17
 
-PROTOCOL_SSLv23: _SSLMethod
-PROTOCOL_SSLv2: _SSLMethod
-PROTOCOL_SSLv3: _SSLMethod
-PROTOCOL_TLSv1: _SSLMethod
-PROTOCOL_TLSv1_1: _SSLMethod
-PROTOCOL_TLSv1_2: _SSLMethod
-PROTOCOL_TLS: _SSLMethod
-PROTOCOL_TLS_CLIENT: _SSLMethod
-PROTOCOL_TLS_SERVER: _SSLMethod
+PROTOCOL_SSLv23: Final = _SSLMethod.PROTOCOL_SSLv23
+PROTOCOL_SSLv2: Final = _SSLMethod.PROTOCOL_SSLv2
+PROTOCOL_SSLv3: Final = _SSLMethod.PROTOCOL_SSLv3
+PROTOCOL_TLSv1: Final = _SSLMethod.PROTOCOL_TLSv1
+PROTOCOL_TLSv1_1: Final = _SSLMethod.PROTOCOL_TLSv1_1
+PROTOCOL_TLSv1_2: Final = _SSLMethod.PROTOCOL_TLSv1_2
+PROTOCOL_TLS: Final = _SSLMethod.PROTOCOL_TLS
+PROTOCOL_TLS_CLIENT: Final = _SSLMethod.PROTOCOL_TLS_CLIENT
+PROTOCOL_TLS_SERVER: Final = _SSLMethod.PROTOCOL_TLS_SERVER
 
 class Options(enum.IntFlag):
     OP_ALL = 2147483728
@@ -191,29 +191,29 @@ class Options(enum.IntFlag):
     if sys.version_info >= (3, 11) or sys.platform == "linux":
         OP_IGNORE_UNEXPECTED_EOF = 128
 
-OP_ALL: Options
-OP_NO_SSLv2: Options
-OP_NO_SSLv3: Options
-OP_NO_TLSv1: Options
-OP_NO_TLSv1_1: Options
-OP_NO_TLSv1_2: Options
-OP_NO_TLSv1_3: Options
-OP_CIPHER_SERVER_PREFERENCE: Options
-OP_SINGLE_DH_USE: Options
-OP_SINGLE_ECDH_USE: Options
-OP_NO_COMPRESSION: Options
-OP_NO_TICKET: Options
-OP_NO_RENEGOTIATION: Options
-OP_ENABLE_MIDDLEBOX_COMPAT: Options
+OP_ALL: Final = Options.OP_ALL
+OP_NO_SSLv2: Final = Options.OP_NO_SSLv2
+OP_NO_SSLv3: Final = Options.OP_NO_SSLv3
+OP_NO_TLSv1: Final = Options.OP_NO_TLSv1
+OP_NO_TLSv1_1: Final = Options.OP_NO_TLSv1_1
+OP_NO_TLSv1_2: Final = Options.OP_NO_TLSv1_2
+OP_NO_TLSv1_3: Final = Options.OP_NO_TLSv1_3
+OP_CIPHER_SERVER_PREFERENCE: Final = Options.OP_CIPHER_SERVER_PREFERENCE
+OP_SINGLE_DH_USE: Final = Options.OP_SINGLE_DH_USE
+OP_SINGLE_ECDH_USE: Final = Options.OP_SINGLE_ECDH_USE
+OP_NO_COMPRESSION: Final = Options.OP_NO_COMPRESSION
+OP_NO_TICKET: Final = Options.OP_NO_TICKET
+OP_NO_RENEGOTIATION: Final = Options.OP_NO_RENEGOTIATION
+OP_ENABLE_MIDDLEBOX_COMPAT: Final = Options.OP_ENABLE_MIDDLEBOX_COMPAT
 if sys.version_info >= (3, 12):
-    OP_LEGACY_SERVER_CONNECT: Options
-    OP_ENABLE_KTLS: Options
+    OP_LEGACY_SERVER_CONNECT: Final = Options.OP_LEGACY_SERVER_CONNECT
+    OP_ENABLE_KTLS: Final = Options.OP_ENABLE_KTLS
 if sys.version_info >= (3, 11) or sys.platform == "linux":
-    OP_IGNORE_UNEXPECTED_EOF: Options
+    OP_IGNORE_UNEXPECTED_EOF: Final = Options.OP_IGNORE_UNEXPECTED_EOF
 
-HAS_NEVER_CHECK_COMMON_NAME: bool
+HAS_NEVER_CHECK_COMMON_NAME: Final[bool]
 
-CHANNEL_BINDING_TYPES: list[str]
+CHANNEL_BINDING_TYPES: Final[list[str]]
 
 class AlertDescription(enum.IntEnum):
     ALERT_DESCRIPTION_ACCESS_DENIED = 49
@@ -244,33 +244,33 @@ class AlertDescription(enum.IntEnum):
     ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION = 110
     ALERT_DESCRIPTION_USER_CANCELLED = 90
 
-ALERT_DESCRIPTION_HANDSHAKE_FAILURE: AlertDescription
-ALERT_DESCRIPTION_INTERNAL_ERROR: AlertDescription
-ALERT_DESCRIPTION_ACCESS_DENIED: AlertDescription
-ALERT_DESCRIPTION_BAD_CERTIFICATE: AlertDescription
-ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: AlertDescription
-ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: AlertDescription
-ALERT_DESCRIPTION_BAD_RECORD_MAC: AlertDescription
-ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: AlertDescription
-ALERT_DESCRIPTION_CERTIFICATE_REVOKED: AlertDescription
-ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: AlertDescription
-ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: AlertDescription
-ALERT_DESCRIPTION_CLOSE_NOTIFY: AlertDescription
-ALERT_DESCRIPTION_DECODE_ERROR: AlertDescription
-ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: AlertDescription
-ALERT_DESCRIPTION_DECRYPT_ERROR: AlertDescription
-ALERT_DESCRIPTION_ILLEGAL_PARAMETER: AlertDescription
-ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: AlertDescription
-ALERT_DESCRIPTION_NO_RENEGOTIATION: AlertDescription
-ALERT_DESCRIPTION_PROTOCOL_VERSION: AlertDescription
-ALERT_DESCRIPTION_RECORD_OVERFLOW: AlertDescription
-ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: AlertDescription
-ALERT_DESCRIPTION_UNKNOWN_CA: AlertDescription
-ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: AlertDescription
-ALERT_DESCRIPTION_UNRECOGNIZED_NAME: AlertDescription
-ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: AlertDescription
-ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: AlertDescription
-ALERT_DESCRIPTION_USER_CANCELLED: AlertDescription
+ALERT_DESCRIPTION_HANDSHAKE_FAILURE: Final = AlertDescription.ALERT_DESCRIPTION_HANDSHAKE_FAILURE
+ALERT_DESCRIPTION_INTERNAL_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_INTERNAL_ERROR
+ALERT_DESCRIPTION_ACCESS_DENIED: Final = AlertDescription.ALERT_DESCRIPTION_ACCESS_DENIED
+ALERT_DESCRIPTION_BAD_CERTIFICATE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE
+ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE
+ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE
+ALERT_DESCRIPTION_BAD_RECORD_MAC: Final = AlertDescription.ALERT_DESCRIPTION_BAD_RECORD_MAC
+ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_EXPIRED
+ALERT_DESCRIPTION_CERTIFICATE_REVOKED: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_REVOKED
+ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN
+ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE
+ALERT_DESCRIPTION_CLOSE_NOTIFY: Final = AlertDescription.ALERT_DESCRIPTION_CLOSE_NOTIFY
+ALERT_DESCRIPTION_DECODE_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_DECODE_ERROR
+ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: Final = AlertDescription.ALERT_DESCRIPTION_DECOMPRESSION_FAILURE
+ALERT_DESCRIPTION_DECRYPT_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_DECRYPT_ERROR
+ALERT_DESCRIPTION_ILLEGAL_PARAMETER: Final = AlertDescription.ALERT_DESCRIPTION_ILLEGAL_PARAMETER
+ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: Final = AlertDescription.ALERT_DESCRIPTION_INSUFFICIENT_SECURITY
+ALERT_DESCRIPTION_NO_RENEGOTIATION: Final = AlertDescription.ALERT_DESCRIPTION_NO_RENEGOTIATION
+ALERT_DESCRIPTION_PROTOCOL_VERSION: Final = AlertDescription.ALERT_DESCRIPTION_PROTOCOL_VERSION
+ALERT_DESCRIPTION_RECORD_OVERFLOW: Final = AlertDescription.ALERT_DESCRIPTION_RECORD_OVERFLOW
+ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: Final = AlertDescription.ALERT_DESCRIPTION_UNEXPECTED_MESSAGE
+ALERT_DESCRIPTION_UNKNOWN_CA: Final = AlertDescription.ALERT_DESCRIPTION_UNKNOWN_CA
+ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: Final = AlertDescription.ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
+ALERT_DESCRIPTION_UNRECOGNIZED_NAME: Final = AlertDescription.ALERT_DESCRIPTION_UNRECOGNIZED_NAME
+ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: Final = AlertDescription.ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE
+ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: Final = AlertDescription.ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION
+ALERT_DESCRIPTION_USER_CANCELLED: Final = AlertDescription.ALERT_DESCRIPTION_USER_CANCELLED
 
 # This class is not exposed. It calls itself ssl._ASN1Object.
 @type_check_only
@@ -518,20 +518,20 @@ class SSLErrorNumber(enum.IntEnum):
     SSL_ERROR_WANT_X509_LOOKUP = 4
     SSL_ERROR_ZERO_RETURN = 6
 
-SSL_ERROR_EOF: SSLErrorNumber  # undocumented
-SSL_ERROR_INVALID_ERROR_CODE: SSLErrorNumber  # undocumented
-SSL_ERROR_SSL: SSLErrorNumber  # undocumented
-SSL_ERROR_SYSCALL: SSLErrorNumber  # undocumented
-SSL_ERROR_WANT_CONNECT: SSLErrorNumber  # undocumented
-SSL_ERROR_WANT_READ: SSLErrorNumber  # undocumented
-SSL_ERROR_WANT_WRITE: SSLErrorNumber  # undocumented
-SSL_ERROR_WANT_X509_LOOKUP: SSLErrorNumber  # undocumented
-SSL_ERROR_ZERO_RETURN: SSLErrorNumber  # undocumented
+SSL_ERROR_EOF: Final = SSLErrorNumber.SSL_ERROR_EOF  # undocumented
+SSL_ERROR_INVALID_ERROR_CODE: Final = SSLErrorNumber.SSL_ERROR_INVALID_ERROR_CODE  # undocumented
+SSL_ERROR_SSL: Final = SSLErrorNumber.SSL_ERROR_SSL  # undocumented
+SSL_ERROR_SYSCALL: Final = SSLErrorNumber.SSL_ERROR_SYSCALL  # undocumented
+SSL_ERROR_WANT_CONNECT: Final = SSLErrorNumber.SSL_ERROR_WANT_CONNECT  # undocumented
+SSL_ERROR_WANT_READ: Final = SSLErrorNumber.SSL_ERROR_WANT_READ  # undocumented
+SSL_ERROR_WANT_WRITE: Final = SSLErrorNumber.SSL_ERROR_WANT_WRITE  # undocumented
+SSL_ERROR_WANT_X509_LOOKUP: Final = SSLErrorNumber.SSL_ERROR_WANT_X509_LOOKUP  # undocumented
+SSL_ERROR_ZERO_RETURN: Final = SSLErrorNumber.SSL_ERROR_ZERO_RETURN  # undocumented
 
 def get_protocol_name(protocol_code: int) -> str: ...
 
-PEM_FOOTER: str
-PEM_HEADER: str
-SOCK_STREAM: int
-SOL_SOCKET: int
-SO_TYPE: int
+PEM_FOOTER: Final[str]
+PEM_HEADER: Final[str]
+SOCK_STREAM: Final = socket.SOCK_STREAM
+SOL_SOCKET: Final = socket.SOL_SOCKET
+SO_TYPE: Final = socket.SO_TYPE
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index 149f374d6e17..b16e7c0abd05 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -181,6 +181,14 @@ class _flags(_UninstantiableStructseq, tuple[int, ...]):
     if sys.version_info >= (3, 11):
         @property
         def safe_path(self) -> bool: ...
+    if sys.version_info >= (3, 13):
+        @property
+        def gil(self) -> Literal[0, 1]: ...
+    if sys.version_info >= (3, 14):
+        @property
+        def thread_inherit_context(self) -> Literal[0, 1]: ...
+        @property
+        def context_aware_warnings(self) -> Literal[0, 1]: ...
     # Whether or not this exists on lower versions of Python
     # may depend on which patch release you're using
     # (it was backported to all Python versions on 3.8+ as a security fix)
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index b802d5e97c84..76b2ddcf17df 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -1029,6 +1029,7 @@ class Tk(Misc, Wm):
     def loadtk(self) -> None: ...
     def record(self, script, /): ...
     if sys.version_info < (3, 11):
+        @deprecated("Deprecated since Python 3.9; removed in Python 3.11. Use `splitlist()` instead.")
         def split(self, arg, /): ...
 
     def splitlist(self, arg, /): ...
diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi
index 7d39026b8041..e41476b73b8c 100644
--- a/mypy/typeshed/stdlib/turtle.pyi
+++ b/mypy/typeshed/stdlib/turtle.pyi
@@ -4,7 +4,7 @@ from collections.abc import Callable, Generator, Sequence
 from contextlib import contextmanager
 from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar
 from typing import Any, ClassVar, Literal, TypedDict, overload, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, deprecated
 
 __all__ = [
     "ScrolledCanvas",
@@ -426,6 +426,7 @@ class RawTurtle(TPen, TNavigator):  # type: ignore[misc]  # Conflicting methods
     def get_shapepoly(self) -> _PolygonCoords | None: ...
 
     if sys.version_info < (3, 13):
+        @deprecated("Deprecated since Python 3.1; removed in Python 3.13. Use `tiltangle()` instead.")
         def settiltangle(self, angle: float) -> None: ...
 
     @overload
@@ -707,6 +708,7 @@ def shapetransform(
 def get_shapepoly() -> _PolygonCoords | None: ...
 
 if sys.version_info < (3, 13):
+    @deprecated("Deprecated since Python 3.1; removed in Python 3.13. Use `tiltangle()` instead.")
     def settiltangle(angle: float) -> None: ...
 
 @overload
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index a85aa2e2dc83..fd9da29addbf 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -207,12 +207,12 @@ class TypeVar:
             contravariant: bool = False,
         ) -> None: ...
     if sys.version_info >= (3, 10):
-        def __or__(self, right: Any) -> _SpecialForm: ...  # AnnotationForm
-        def __ror__(self, left: Any) -> _SpecialForm: ...  # AnnotationForm
+        def __or__(self, right: Any, /) -> _SpecialForm: ...  # AnnotationForm
+        def __ror__(self, left: Any, /) -> _SpecialForm: ...  # AnnotationForm
     if sys.version_info >= (3, 11):
-        def __typing_subst__(self, arg: Any) -> Any: ...
+        def __typing_subst__(self, arg: Any, /) -> Any: ...
     if sys.version_info >= (3, 13):
-        def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
+        def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ...
         def has_default(self) -> bool: ...
     if sys.version_info >= (3, 14):
         @property
@@ -273,8 +273,8 @@ if sys.version_info >= (3, 11):
             def __init__(self, name: str) -> None: ...
 
         def __iter__(self) -> Any: ...
-        def __typing_subst__(self, arg: Never) -> Never: ...
-        def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
+        def __typing_subst__(self, arg: Never, /) -> Never: ...
+        def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ...
         if sys.version_info >= (3, 14):
             @property
             def evaluate_default(self) -> EvaluateFunc | None: ...
@@ -289,7 +289,7 @@ if sys.version_info >= (3, 10):
         else:
             def __init__(self, origin: ParamSpec) -> None: ...
 
-        def __eq__(self, other: object) -> bool: ...
+        def __eq__(self, other: object, /) -> bool: ...
         __hash__: ClassVar[None]  # type: ignore[assignment]
 
     @final
@@ -301,7 +301,7 @@ if sys.version_info >= (3, 10):
         else:
             def __init__(self, origin: ParamSpec) -> None: ...
 
-        def __eq__(self, other: object) -> bool: ...
+        def __eq__(self, other: object, /) -> bool: ...
         __hash__: ClassVar[None]  # type: ignore[assignment]
 
     @final
@@ -365,11 +365,11 @@ if sys.version_info >= (3, 10):
         @property
         def kwargs(self) -> ParamSpecKwargs: ...
         if sys.version_info >= (3, 11):
-            def __typing_subst__(self, arg: Any) -> Any: ...
-            def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ...
+            def __typing_subst__(self, arg: Any, /) -> Any: ...
+            def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ...
 
-        def __or__(self, right: Any) -> _SpecialForm: ...
-        def __ror__(self, left: Any) -> _SpecialForm: ...
+        def __or__(self, right: Any, /) -> _SpecialForm: ...
+        def __ror__(self, left: Any, /) -> _SpecialForm: ...
         if sys.version_info >= (3, 13):
             def has_default(self) -> bool: ...
         if sys.version_info >= (3, 14):
@@ -710,7 +710,7 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co,
     def __init__(self, mapping: Mapping[_KT_co, _VT_co]) -> None: ...  # undocumented
     def __and__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ...
     def __rand__(self, other: Iterable[_T]) -> set[_T]: ...
-    def __contains__(self, item: object) -> bool: ...
+    def __contains__(self, item: tuple[object, object]) -> bool: ...  # type: ignore[override]
     def __iter__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ...
     def __or__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ...
     def __ror__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ...
@@ -1115,9 +1115,9 @@ if sys.version_info >= (3, 12):
         # It's writable on types, but not on instances of TypeAliasType.
         @property
         def __module__(self) -> str | None: ...  # type: ignore[override]
-        def __getitem__(self, parameters: Any) -> GenericAlias: ...  # AnnotationForm
-        def __or__(self, right: Any) -> _SpecialForm: ...
-        def __ror__(self, left: Any) -> _SpecialForm: ...
+        def __getitem__(self, parameters: Any, /) -> GenericAlias: ...  # AnnotationForm
+        def __or__(self, right: Any, /) -> _SpecialForm: ...
+        def __ror__(self, left: Any, /) -> _SpecialForm: ...
         if sys.version_info >= (3, 14):
             @property
             def evaluate_value(self) -> EvaluateFunc: ...
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index 22b6ada8ffb7..71bf3d87d499 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -593,8 +593,8 @@ else:
         def __getitem__(self, parameters: Incomplete | tuple[Incomplete, ...]) -> AnnotationForm: ...
         def __init_subclass__(cls, *args: Unused, **kwargs: Unused) -> NoReturn: ...
         if sys.version_info >= (3, 10):
-            def __or__(self, right: Any) -> _SpecialForm: ...
-            def __ror__(self, left: Any) -> _SpecialForm: ...
+            def __or__(self, right: Any, /) -> _SpecialForm: ...
+            def __ror__(self, left: Any, /) -> _SpecialForm: ...
 
 # PEP 727
 class Doc:
diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi
index b99577c1cf71..876b9d3f165c 100644
--- a/mypy/typeshed/stdlib/urllib/request.pyi
+++ b/mypy/typeshed/stdlib/urllib/request.pyi
@@ -325,7 +325,7 @@ def urlretrieve(
 def urlcleanup() -> None: ...
 
 if sys.version_info < (3, 14):
-    @deprecated("Deprecated since Python 3.3; Removed in 3.14; Use newer urlopen functions and methods.")
+    @deprecated("Deprecated since Python 3.3; removed in Python 3.14. Use newer `urlopen` functions and methods.")
     class URLopener:
         version: ClassVar[str]
         def __init__(self, proxies: dict[str, str] | None = None, **x509: str) -> None: ...
@@ -356,7 +356,7 @@ if sys.version_info < (3, 14):
         def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ...  # undocumented
         def __del__(self) -> None: ...
 
-    @deprecated("Deprecated since Python 3.3; Removed in 3.14; Use newer urlopen functions and methods.")
+    @deprecated("Deprecated since Python 3.3; removed in Python 3.14. Use newer `urlopen` functions and methods.")
     class FancyURLopener(URLopener):
         def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ...
         def get_user_passwd(self, host: str, realm: str, clear_cache: int = 0) -> tuple[str, str]: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi
index d4d04817d7e0..0a22bb23d8f6 100644
--- a/mypy/typeshed/stdlib/winreg.pyi
+++ b/mypy/typeshed/stdlib/winreg.pyi
@@ -123,7 +123,7 @@ if sys.platform == "win32":
         def __int__(self) -> int: ...
         def __enter__(self) -> Self: ...
         def __exit__(
-            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
+            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /
         ) -> bool | None: ...
         def Close(self) -> None: ...
         def Detach(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi
index 4aab318e7c71..22af3c272759 100644
--- a/mypy/typeshed/stdlib/zipimport.pyi
+++ b/mypy/typeshed/stdlib/zipimport.pyi
@@ -27,8 +27,14 @@ class zipimporter(_LoaderBasics):
         def __init__(self, path: StrOrBytesPath) -> None: ...
 
     if sys.version_info < (3, 12):
-        def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ...  # undocumented
-        def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ...
+        if sys.version_info >= (3, 10):
+            @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `find_spec()` instead.")
+            def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ...
+            @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `find_spec()` instead.")
+            def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ...
+        else:
+            def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ...
+            def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ...
 
     def get_code(self, fullname: str) -> CodeType: ...
     def get_data(self, pathname: str) -> bytes: ...
@@ -42,10 +48,12 @@ class zipimporter(_LoaderBasics):
 
     def get_source(self, fullname: str) -> str | None: ...
     def is_package(self, fullname: str) -> bool: ...
-    @deprecated("Deprecated since 3.10; use exec_module() instead")
-    def load_module(self, fullname: str) -> ModuleType: ...
     if sys.version_info >= (3, 10):
+        @deprecated("Deprecated since Python 3.10; removed in Python 3.15. Use `exec_module()` instead.")
+        def load_module(self, fullname: str) -> ModuleType: ...
         def exec_module(self, module: ModuleType) -> None: ...
         def create_module(self, spec: ModuleSpec) -> None: ...
         def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ...
         def invalidate_caches(self) -> None: ...
+    else:
+        def load_module(self, fullname: str) -> ModuleType: ...
diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi
index 7cafb44b34a7..4e410fdd18ad 100644
--- a/mypy/typeshed/stdlib/zlib.pyi
+++ b/mypy/typeshed/stdlib/zlib.pyi
@@ -4,11 +4,11 @@ from typing import Any, Final, final, type_check_only
 from typing_extensions import Self
 
 DEFLATED: Final = 8
-DEF_MEM_LEVEL: int  # can change
+DEF_MEM_LEVEL: Final[int]
 DEF_BUF_SIZE: Final = 16384
-MAX_WBITS: int
-ZLIB_VERSION: str  # can change
-ZLIB_RUNTIME_VERSION: str  # can change
+MAX_WBITS: Final[int]
+ZLIB_VERSION: Final[str]
+ZLIB_RUNTIME_VERSION: Final[str]
 Z_NO_COMPRESSION: Final = 0
 Z_PARTIAL_FLUSH: Final = 1
 Z_BEST_COMPRESSION: Final = 9
@@ -26,6 +26,10 @@ Z_RLE: Final = 3
 Z_SYNC_FLUSH: Final = 2
 Z_TREES: Final = 6
 
+if sys.version_info >= (3, 14) and sys.platform == "win32":
+    # Available when zlib was built with zlib-ng, usually only on Windows
+    ZLIBNG_VERSION: Final[str]
+
 class error(Exception): ...
 
 # This class is not exposed at runtime. It calls itself zlib.Compress.

From abf61fbd49ff244e86867d60de0945162328faf9 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 18 Aug 2025 13:30:51 +0100
Subject: [PATCH 1555/1617] [mypyc] Refactor building IR for "in" against a
 tuple (#19679)

This covers `x in (a, b)` and `x in [a, b]`.

Also add an irbuild test for a use case that is currently inefficient.

This is in preparation for improving IR building for "in" against tuple.
---
 mypyc/irbuild/expression.py        | 128 +++++++++++++++--------------
 mypyc/test-data/irbuild-tuple.test |  30 +++++++
 2 files changed, 95 insertions(+), 63 deletions(-)

diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index c3d863fa96de..a600afff4bc9 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -701,24 +701,70 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value:
     # x in (...)/[...]
     # x not in (...)/[...]
     first_op = e.operators[0]
-    if (
-        first_op in ["in", "not in"]
-        and len(e.operators) == 1
-        and isinstance(e.operands[1], (TupleExpr, ListExpr))
-    ):
-        items = e.operands[1].items
+    if first_op in ["in", "not in"] and len(e.operators) == 1:
+        result = try_specialize_in_expr(builder, first_op, e.operands[0], e.operands[1], e.line)
+        if result is not None:
+            return result
+
+    if len(e.operators) == 1:
+        # Special some common simple cases
+        if first_op in ("is", "is not"):
+            right_expr = e.operands[1]
+            if isinstance(right_expr, NameExpr) and right_expr.fullname == "builtins.None":
+                # Special case 'is None' / 'is not None'.
+                return translate_is_none(builder, e.operands[0], negated=first_op != "is")
+        left_expr = e.operands[0]
+        if is_int_rprimitive(builder.node_type(left_expr)):
+            right_expr = e.operands[1]
+            if is_int_rprimitive(builder.node_type(right_expr)):
+                if first_op in int_borrow_friendly_op:
+                    borrow_left = is_borrow_friendly_expr(builder, right_expr)
+                    left = builder.accept(left_expr, can_borrow=borrow_left)
+                    right = builder.accept(right_expr, can_borrow=True)
+                    return builder.binary_op(left, right, first_op, e.line)
+
+    # TODO: Don't produce an expression when used in conditional context
+    # All of the trickiness here is due to support for chained conditionals
+    # (`e1 < e2 > e3`, etc). `e1 < e2 > e3` is approximately equivalent to
+    # `e1 < e2 and e2 > e3` except that `e2` is only evaluated once.
+    expr_type = builder.node_type(e)
+
+    # go(i, prev) generates code for `ei opi e{i+1} op{i+1} ... en`,
+    # assuming that prev contains the value of `ei`.
+    def go(i: int, prev: Value) -> Value:
+        if i == len(e.operators) - 1:
+            return transform_basic_comparison(
+                builder, e.operators[i], prev, builder.accept(e.operands[i + 1]), e.line
+            )
+
+        next = builder.accept(e.operands[i + 1])
+        return builder.builder.shortcircuit_helper(
+            "and",
+            expr_type,
+            lambda: transform_basic_comparison(builder, e.operators[i], prev, next, e.line),
+            lambda: go(i + 1, next),
+            e.line,
+        )
+
+    return go(0, builder.accept(e.operands[0]))
+
+
+def try_specialize_in_expr(
+    builder: IRBuilder, op: str, lhs: Expression, rhs: Expression, line: int
+) -> Value | None:
+    if isinstance(rhs, (TupleExpr, ListExpr)):
+        items = rhs.items
         n_items = len(items)
         # x in y -> x == y[0] or ... or x == y[n]
         # x not in y -> x != y[0] and ... and x != y[n]
         # 16 is arbitrarily chosen to limit code size
         if 1 < n_items < 16:
-            if e.operators[0] == "in":
+            if op == "in":
                 bin_op = "or"
                 cmp_op = "=="
             else:
                 bin_op = "and"
                 cmp_op = "!="
-            lhs = e.operands[0]
             mypy_file = builder.graph["builtins"].tree
             assert mypy_file is not None
             info = mypy_file.names["bool"].node
@@ -738,78 +784,34 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value:
         # x in [y]/(y) -> x == y
         # x not in [y]/(y) -> x != y
         elif n_items == 1:
-            if e.operators[0] == "in":
+            if op == "in":
                 cmp_op = "=="
             else:
                 cmp_op = "!="
-            e.operators = [cmp_op]
-            e.operands[1] = items[0]
+            left = builder.accept(lhs)
+            right = builder.accept(items[0])
+            return transform_basic_comparison(builder, cmp_op, left, right, line)
         # x in []/() -> False
         # x not in []/() -> True
         elif n_items == 0:
-            if e.operators[0] == "in":
+            if op == "in":
                 return builder.false()
             else:
                 return builder.true()
 
     # x in {...}
     # x not in {...}
-    if (
-        first_op in ("in", "not in")
-        and len(e.operators) == 1
-        and isinstance(e.operands[1], SetExpr)
-    ):
-        set_literal = precompute_set_literal(builder, e.operands[1])
+    if isinstance(rhs, SetExpr):
+        set_literal = precompute_set_literal(builder, rhs)
         if set_literal is not None:
-            lhs = e.operands[0]
             result = builder.builder.primitive_op(
-                set_in_op, [builder.accept(lhs), set_literal], e.line, bool_rprimitive
+                set_in_op, [builder.accept(lhs), set_literal], line, bool_rprimitive
             )
-            if first_op == "not in":
-                return builder.unary_op(result, "not", e.line)
+            if op == "not in":
+                return builder.unary_op(result, "not", line)
             return result
 
-    if len(e.operators) == 1:
-        # Special some common simple cases
-        if first_op in ("is", "is not"):
-            right_expr = e.operands[1]
-            if isinstance(right_expr, NameExpr) and right_expr.fullname == "builtins.None":
-                # Special case 'is None' / 'is not None'.
-                return translate_is_none(builder, e.operands[0], negated=first_op != "is")
-        left_expr = e.operands[0]
-        if is_int_rprimitive(builder.node_type(left_expr)):
-            right_expr = e.operands[1]
-            if is_int_rprimitive(builder.node_type(right_expr)):
-                if first_op in int_borrow_friendly_op:
-                    borrow_left = is_borrow_friendly_expr(builder, right_expr)
-                    left = builder.accept(left_expr, can_borrow=borrow_left)
-                    right = builder.accept(right_expr, can_borrow=True)
-                    return builder.binary_op(left, right, first_op, e.line)
-
-    # TODO: Don't produce an expression when used in conditional context
-    # All of the trickiness here is due to support for chained conditionals
-    # (`e1 < e2 > e3`, etc). `e1 < e2 > e3` is approximately equivalent to
-    # `e1 < e2 and e2 > e3` except that `e2` is only evaluated once.
-    expr_type = builder.node_type(e)
-
-    # go(i, prev) generates code for `ei opi e{i+1} op{i+1} ... en`,
-    # assuming that prev contains the value of `ei`.
-    def go(i: int, prev: Value) -> Value:
-        if i == len(e.operators) - 1:
-            return transform_basic_comparison(
-                builder, e.operators[i], prev, builder.accept(e.operands[i + 1]), e.line
-            )
-
-        next = builder.accept(e.operands[i + 1])
-        return builder.builder.shortcircuit_helper(
-            "and",
-            expr_type,
-            lambda: transform_basic_comparison(builder, e.operators[i], prev, next, e.line),
-            lambda: go(i + 1, next),
-            e.line,
-        )
-
-    return go(0, builder.accept(e.operands[0]))
+    return None
 
 
 def translate_is_none(builder: IRBuilder, expr: Expression, negated: bool) -> Value:
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index 0342ec304c25..712b9c26355a 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -209,6 +209,36 @@ L5:
 L6:
     return r3
 
+[case testTupleOperatorInFinalTuple]
+from typing import Final
+
+tt: Final = (1, 2)
+
+def f(x: int) -> bool:
+    return x in tt
+[out]
+def f(x):
+    x :: int
+    r0 :: tuple[int, int]
+    r1 :: bool
+    r2, r3 :: object
+    r4 :: i32
+    r5 :: bit
+    r6 :: bool
+L0:
+    r0 = __main__.tt :: static
+    if is_error(r0) goto L1 else goto L2
+L1:
+    r1 = raise NameError('value for final name "tt" was not set')
+    unreachable
+L2:
+    r2 = box(int, x)
+    r3 = box(tuple[int, int], r0)
+    r4 = PySequence_Contains(r3, r2)
+    r5 = r4 >= 0 :: signed
+    r6 = truncate r4: i32 to builtins.bool
+    return r6
+
 [case testTupleBuiltFromList]
 def f(val: int) -> bool:
     return val % 2 == 0

From 1f9505c13657c6028553e2628b8194b8e8d247d2 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 18 Aug 2025 15:11:24 +0100
Subject: [PATCH 1556/1617] [mypyc] Speed up "in" against final fixed-length
 tuple (#19682)

Previously the `in` operation here boxed the tuple:
```
TUP: Final = ('x', 'y')
...
    if s in TUP: ...
```
Now we don't box the tuple and inline the comparisons against each tuple
item instead,
which is more efficient.

Also make the semantics closer to Python and add tests.
---
 mypyc/irbuild/expression.py        |  59 ++++++++-----
 mypyc/test-data/irbuild-tuple.test |  88 ++++++++++++++------
 mypyc/test-data/run-lists.test     | 128 ++++++++++++++---------------
 mypyc/test-data/run-tuples.test    |  83 +++++++++++++++++++
 4 files changed, 247 insertions(+), 111 deletions(-)

diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index a600afff4bc9..2df47680d27c 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -752,35 +752,51 @@ def go(i: int, prev: Value) -> Value:
 def try_specialize_in_expr(
     builder: IRBuilder, op: str, lhs: Expression, rhs: Expression, line: int
 ) -> Value | None:
+    left: Value | None = None
+    items: list[Value] | None = None
+
     if isinstance(rhs, (TupleExpr, ListExpr)):
-        items = rhs.items
+        left = builder.accept(lhs)
+        items = [builder.accept(item) for item in rhs.items]
+    elif isinstance(builder.node_type(rhs), RTuple):
+        left = builder.accept(lhs)
+        tuple_val = builder.accept(rhs)
+        assert isinstance(tuple_val.type, RTuple)
+        items = [builder.add(TupleGet(tuple_val, i)) for i in range(len(tuple_val.type.types))]
+
+    if items is not None:
+        assert left is not None
         n_items = len(items)
         # x in y -> x == y[0] or ... or x == y[n]
         # x not in y -> x != y[0] and ... and x != y[n]
-        # 16 is arbitrarily chosen to limit code size
-        if 1 < n_items < 16:
+        if n_items > 1:
             if op == "in":
-                bin_op = "or"
                 cmp_op = "=="
             else:
-                bin_op = "and"
                 cmp_op = "!="
-            mypy_file = builder.graph["builtins"].tree
-            assert mypy_file is not None
-            info = mypy_file.names["bool"].node
-            assert isinstance(info, TypeInfo), info
-            bool_type = Instance(info, [])
-            exprs = []
+            out = BasicBlock()
             for item in items:
-                expr = ComparisonExpr([cmp_op], [lhs, item])
-                builder.types[expr] = bool_type
-                exprs.append(expr)
-
-            or_expr: Expression = exprs.pop(0)
-            for expr in exprs:
-                or_expr = OpExpr(bin_op, or_expr, expr)
-                builder.types[or_expr] = bool_type
-            return builder.accept(or_expr)
+                cmp = transform_basic_comparison(builder, cmp_op, left, item, line)
+                bool_val = builder.builder.bool_value(cmp)
+                next_block = BasicBlock()
+                if op == "in":
+                    builder.add_bool_branch(bool_val, out, next_block)
+                else:
+                    builder.add_bool_branch(bool_val, next_block, out)
+                builder.activate_block(next_block)
+            result_reg = Register(bool_rprimitive)
+            end = BasicBlock()
+            if op == "in":
+                values = builder.false(), builder.true()
+            else:
+                values = builder.true(), builder.false()
+            builder.assign(result_reg, values[0], line)
+            builder.goto(end)
+            builder.activate_block(out)
+            builder.assign(result_reg, values[1], line)
+            builder.goto(end)
+            builder.activate_block(end)
+            return result_reg
         # x in [y]/(y) -> x == y
         # x not in [y]/(y) -> x != y
         elif n_items == 1:
@@ -788,8 +804,7 @@ def try_specialize_in_expr(
                 cmp_op = "=="
             else:
                 cmp_op = "!="
-            left = builder.accept(lhs)
-            right = builder.accept(items[0])
+            right = items[0]
             return transform_basic_comparison(builder, cmp_op, left, right, line)
         # x in []/() -> False
         # x not in []/() -> True
diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test
index 712b9c26355a..00ea7f074a5d 100644
--- a/mypyc/test-data/irbuild-tuple.test
+++ b/mypyc/test-data/irbuild-tuple.test
@@ -184,31 +184,66 @@ def f(i: int) -> bool:
 [out]
 def f(i):
     i :: int
-    r0 :: bit
-    r1 :: bool
-    r2 :: bit
+    r0, r1, r2 :: bit
     r3 :: bool
-    r4 :: bit
 L0:
     r0 = int_eq i, 2
-    if r0 goto L1 else goto L2 :: bool
+    if r0 goto L4 else goto L1 :: bool
 L1:
-    r1 = r0
-    goto L3
+    r1 = int_eq i, 4
+    if r1 goto L4 else goto L2 :: bool
 L2:
-    r2 = int_eq i, 4
-    r1 = r2
+    r2 = int_eq i, 6
+    if r2 goto L4 else goto L3 :: bool
 L3:
-    if r1 goto L4 else goto L5 :: bool
+    r3 = 0
+    goto L5
 L4:
-    r3 = r1
-    goto L6
+    r3 = 1
 L5:
-    r4 = int_eq i, 6
-    r3 = r4
-L6:
     return r3
 
+[case testTupleOperatorNotIn]
+def x() -> int:
+    return 1
+def y() -> int:
+    return 2
+def z() -> int:
+    return 3
+
+def f() -> bool:
+    return z() not in (x(), y())
+[out]
+def x():
+L0:
+    return 2
+def y():
+L0:
+    return 4
+def z():
+L0:
+    return 6
+def f():
+    r0, r1, r2 :: int
+    r3, r4 :: bit
+    r5 :: bool
+L0:
+    r0 = z()
+    r1 = x()
+    r2 = y()
+    r3 = int_ne r0, r1
+    if r3 goto L1 else goto L3 :: bool
+L1:
+    r4 = int_ne r0, r2
+    if r4 goto L2 else goto L3 :: bool
+L2:
+    r5 = 1
+    goto L4
+L3:
+    r5 = 0
+L4:
+    return r5
+
 [case testTupleOperatorInFinalTuple]
 from typing import Final
 
@@ -221,9 +256,8 @@ def f(x):
     x :: int
     r0 :: tuple[int, int]
     r1 :: bool
-    r2, r3 :: object
-    r4 :: i32
-    r5 :: bit
+    r2, r3 :: int
+    r4, r5 :: bit
     r6 :: bool
 L0:
     r0 = __main__.tt :: static
@@ -232,11 +266,19 @@ L1:
     r1 = raise NameError('value for final name "tt" was not set')
     unreachable
 L2:
-    r2 = box(int, x)
-    r3 = box(tuple[int, int], r0)
-    r4 = PySequence_Contains(r3, r2)
-    r5 = r4 >= 0 :: signed
-    r6 = truncate r4: i32 to builtins.bool
+    r2 = r0[0]
+    r3 = r0[1]
+    r4 = int_eq x, r2
+    if r4 goto L5 else goto L3 :: bool
+L3:
+    r5 = int_eq x, r3
+    if r5 goto L5 else goto L4 :: bool
+L4:
+    r6 = 0
+    goto L6
+L5:
+    r6 = 1
+L6:
     return r6
 
 [case testTupleBuiltFromList]
diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test
index 54bcc0384604..1569579c1156 100644
--- a/mypyc/test-data/run-lists.test
+++ b/mypyc/test-data/run-lists.test
@@ -364,7 +364,6 @@ def test_multiply() -> None:
     assert l1 == [1, 1, 1]
 
 [case testOperatorInExpression]
-
 def tuple_in_int0(i: int) -> bool:
     return i in []
 
@@ -416,71 +415,68 @@ def list_not_in_str(s: "str") -> bool:
 def list_in_mixed(i: object):
     return i in [[], (), "", 0, 0.0, False, 0j, {}, set(), type]
 
-[file driver.py]
-
-from native import *
-
-assert not tuple_in_int0(0)
-assert not tuple_in_int1(0)
-assert tuple_in_int1(1)
-assert not tuple_in_int3(0)
-assert tuple_in_int3(1)
-assert tuple_in_int3(2)
-assert tuple_in_int3(3)
-assert not tuple_in_int3(4)
-
-assert tuple_not_in_int0(0)
-assert tuple_not_in_int1(0)
-assert not tuple_not_in_int1(1)
-assert tuple_not_in_int3(0)
-assert not tuple_not_in_int3(1)
-assert not tuple_not_in_int3(2)
-assert not tuple_not_in_int3(3)
-assert tuple_not_in_int3(4)
-
-assert tuple_in_str("foo")
-assert tuple_in_str("bar")
-assert tuple_in_str("baz")
-assert not tuple_in_str("apple")
-assert not tuple_in_str("pie")
-assert not tuple_in_str("\0")
-assert not tuple_in_str("")
-
-assert not list_in_int0(0)
-assert not list_in_int1(0)
-assert list_in_int1(1)
-assert not list_in_int3(0)
-assert list_in_int3(1)
-assert list_in_int3(2)
-assert list_in_int3(3)
-assert not list_in_int3(4)
-
-assert list_not_in_int0(0)
-assert list_not_in_int1(0)
-assert not list_not_in_int1(1)
-assert list_not_in_int3(0)
-assert not list_not_in_int3(1)
-assert not list_not_in_int3(2)
-assert not list_not_in_int3(3)
-assert list_not_in_int3(4)
-
-assert list_in_str("foo")
-assert list_in_str("bar")
-assert list_in_str("baz")
-assert not list_in_str("apple")
-assert not list_in_str("pie")
-assert not list_in_str("\0")
-assert not list_in_str("")
-
-assert list_in_mixed(0)
-assert list_in_mixed([])
-assert list_in_mixed({})
-assert list_in_mixed(())
-assert list_in_mixed(False)
-assert list_in_mixed(0.0)
-assert not list_in_mixed([1])
-assert not list_in_mixed(object)
-assert list_in_mixed(type)
+def test_in_operator_various_cases() -> None:
+    assert not tuple_in_int0(0)
+    assert not tuple_in_int1(0)
+    assert tuple_in_int1(1)
+    assert not tuple_in_int3(0)
+    assert tuple_in_int3(1)
+    assert tuple_in_int3(2)
+    assert tuple_in_int3(3)
+    assert not tuple_in_int3(4)
+
+    assert tuple_not_in_int0(0)
+    assert tuple_not_in_int1(0)
+    assert not tuple_not_in_int1(1)
+    assert tuple_not_in_int3(0)
+    assert not tuple_not_in_int3(1)
+    assert not tuple_not_in_int3(2)
+    assert not tuple_not_in_int3(3)
+    assert tuple_not_in_int3(4)
+
+    assert tuple_in_str("foo")
+    assert tuple_in_str("bar")
+    assert tuple_in_str("baz")
+    assert not tuple_in_str("apple")
+    assert not tuple_in_str("pie")
+    assert not tuple_in_str("\0")
+    assert not tuple_in_str("")
+
+    assert not list_in_int0(0)
+    assert not list_in_int1(0)
+    assert list_in_int1(1)
+    assert not list_in_int3(0)
+    assert list_in_int3(1)
+    assert list_in_int3(2)
+    assert list_in_int3(3)
+    assert not list_in_int3(4)
+
+    assert list_not_in_int0(0)
+    assert list_not_in_int1(0)
+    assert not list_not_in_int1(1)
+    assert list_not_in_int3(0)
+    assert not list_not_in_int3(1)
+    assert not list_not_in_int3(2)
+    assert not list_not_in_int3(3)
+    assert list_not_in_int3(4)
+
+    assert list_in_str("foo")
+    assert list_in_str("bar")
+    assert list_in_str("baz")
+    assert not list_in_str("apple")
+    assert not list_in_str("pie")
+    assert not list_in_str("\0")
+    assert not list_in_str("")
+
+    assert list_in_mixed(0)
+    assert list_in_mixed([])
+    assert list_in_mixed({})
+    assert list_in_mixed(())
+    assert list_in_mixed(False)
+    assert list_in_mixed(0.0)
+    assert not list_in_mixed([1])
+    assert not list_in_mixed(object)
+    assert list_in_mixed(type)
 
 [case testListBuiltFromGenerator]
 def test_from_gen() -> None:
diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test
index 5d9485288cfb..f5e1733d429b 100644
--- a/mypyc/test-data/run-tuples.test
+++ b/mypyc/test-data/run-tuples.test
@@ -292,6 +292,89 @@ TUPLE: Final[Tuple[str, ...]] = ('x', 'y')
 def test_final_boxed_tuple() -> None:
     t = TUPLE
     assert t == ('x', 'y')
+    assert 'x' in TUPLE
+    assert 'y' in TUPLE
+    b: object = 'z' in TUPLE
+    assert not b
+    assert 'z' not in TUPLE
+    b2: object = 'x' not in TUPLE
+    assert not b2
+    b3: object = 'y' not in TUPLE
+    assert not b3
+
+TUP2: Final = ('x', 'y')
+TUP1: Final = ('x',)
+TUP0: Final = ()
+
+def test_final_tuple_in() -> None:
+    assert 'x' + str() in TUP2
+    assert 'y' + str() in TUP2
+    b: object = 'z' + str() in TUP2
+    assert not b
+
+    assert 'x' + str() in TUP1
+    b2: object = 'y' in TUP1
+    assert not b2
+
+    b3: object = 'x' in TUP0
+    assert not b3
+
+def test_final_tuple_not_in() -> None:
+    assert 'z' + str() not in TUP2
+    b: object = 'x' + str() not in TUP2
+    assert not b
+    b2: object = 'y' + str() not in TUP2
+    assert not b2
+
+    assert 'y' + str() not in TUP1
+    b3: object = 'x' not in TUP1
+    assert not b2
+
+    assert 'x' not in TUP0
+
+log = []
+
+def f_a() -> str:
+    log.append('f_a')
+    return 'a'
+
+def f_a2() -> str:
+    log.append('f_a2')
+    return 'a'
+
+def f_b() -> str:
+    log.append('f_b')
+    return 'b'
+
+def f_c() -> str:
+    log.append('f_c')
+    return 'c'
+
+def test_tuple_in_order_of_evaluation() -> None:
+    log.clear()
+    assert f_a() in (f_b(), f_a2())
+    assert log ==["f_a", "f_b", "f_a2"]
+
+    log.clear()
+    assert f_a() not in (f_b(), f_c())
+    assert log ==["f_a", "f_b", "f_c"]
+
+    log.clear()
+    assert f_a() in (f_b(), f_a2(), f_c())
+    assert log ==["f_a", "f_b", "f_a2", "f_c"]
+
+def f_t() -> tuple[str, ...]:
+    log.append('f_t')
+    return ('x', 'a')
+
+def test_tuple_in_non_specialized() -> None:
+    log.clear()
+    assert f_a() in f_t()
+    assert log == ["f_a", "f_t"]
+
+    log.clear()
+    assert f_b() not in f_t()
+    assert log == ["f_b", "f_t"]
 
 def test_add() -> None:
     res = (1, 2, 3, 4)

From 38eeff85a8c290371648477a07dd38fbb4508018 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 18 Aug 2025 16:43:31 +0100
Subject: [PATCH 1557/1617] [mypyc] Add primitive for .__name__ (#19683)

This seems quite common in real-world code, including in
performance-critical functions. Python 3.11 added a C API function for
this, which we use here.

The primitive works for arbtirary objects, but only type objects have a
specialized code path. Other use cases of `__name__` seem typically less
performance-sensitive.

This PR makes this micro-benchmark 2.0x faster on Python 3.13:
```
from typing import Iterator

class FooBar: pass

def foo(x: type[object], n: int) -> str:
    for a in range(n):
        s = x.__name__
    return s

def bench(n: int) -> None:
    for i in range(n):
        foo(FooBar, 1000)

from time import time
bench(50 * 1000)
t0 = time()
bench(50 * 1000)
print(time() - t0)
```
---
 mypyc/irbuild/expression.py          | 10 +++++-
 mypyc/lib-rt/CPy.h                   |  4 +++
 mypyc/lib-rt/misc_ops.c              | 14 ++++++++
 mypyc/lib-rt/mypyc_util.h            |  1 +
 mypyc/primitives/generic_ops.py      | 10 ++++++
 mypyc/test-data/irbuild-classes.test | 48 ++++++++++++++++++++++++++++
 mypyc/test-data/run-classes.test     | 48 ++++++++++++++++++++++++++++
 7 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index 2df47680d27c..312ba98e3c5d 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -76,6 +76,7 @@
     is_int_rprimitive,
     is_list_rprimitive,
     is_none_rprimitive,
+    is_object_rprimitive,
     object_rprimitive,
     set_rprimitive,
 )
@@ -98,7 +99,7 @@
 from mypyc.irbuild.specialize import apply_function_specialization, apply_method_specialization
 from mypyc.primitives.bytes_ops import bytes_slice_op
 from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op
-from mypyc.primitives.generic_ops import iter_op
+from mypyc.primitives.generic_ops import iter_op, name_op
 from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op
 from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op
 from mypyc.primitives.registry import builtin_names
@@ -218,6 +219,13 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value:
     obj = builder.accept(expr.expr, can_borrow=can_borrow)
     rtype = builder.node_type(expr)
 
+    if (
+        is_object_rprimitive(obj.type)
+        and expr.name == "__name__"
+        and builder.options.capi_version >= (3, 11)
+    ):
+        return builder.primitive_op(name_op, [obj], expr.line)
+
     # Special case: for named tuples transform attribute access to faster index access.
     typ = get_proper_type(builder.types.get(expr.expr))
     if isinstance(typ, TupleType) and typ.partial_fallback.type.is_named_tuple:
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 1881aa97f308..ffa4f6a363d2 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -931,6 +931,10 @@ PyObject *CPy_GetANext(PyObject *aiter);
 void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value);
 void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details);
 
+#if CPY_3_11_FEATURES
+PyObject *CPy_GetName(PyObject *obj);
+#endif
+
 #if CPY_3_14_FEATURES
 void CPy_SetImmortal(PyObject *obj);
 #endif
diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c
index 0c9d7812ac6c..b7593491a6e6 100644
--- a/mypyc/lib-rt/misc_ops.c
+++ b/mypyc/lib-rt/misc_ops.c
@@ -1045,6 +1045,20 @@ PyObject *CPy_GetANext(PyObject *aiter)
     return NULL;
 }
 
+#if CPY_3_11_FEATURES
+
+// Return obj.__name__ (specialized to type objects, which are the most common target).
+PyObject *CPy_GetName(PyObject *obj) {
+    if (PyType_Check(obj)) {
+        return PyType_GetName((PyTypeObject *)obj);
+    }
+    _Py_IDENTIFIER(__name__);
+    PyObject *name = _PyUnicode_FromId(&PyId___name__); /* borrowed */
+    return PyObject_GetAttr(obj, name);
+}
+
+#endif
+
 #ifdef MYPYC_LOG_TRACE
 
 // This is only compiled in if trace logging is enabled by user
diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h
index f200d4f90def..4168d3c53ee2 100644
--- a/mypyc/lib-rt/mypyc_util.h
+++ b/mypyc/lib-rt/mypyc_util.h
@@ -140,6 +140,7 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) {
 }
 
 // Are we targeting Python 3.X or newer?
+#define CPY_3_11_FEATURES (PY_VERSION_HEX >= 0x030b0000)
 #define CPY_3_12_FEATURES (PY_VERSION_HEX >= 0x030c0000)
 #define CPY_3_14_FEATURES (PY_VERSION_HEX >= 0x030e0000)
 
diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py
index 54510d99cf87..4a95be4e5d4e 100644
--- a/mypyc/primitives/generic_ops.py
+++ b/mypyc/primitives/generic_ops.py
@@ -26,6 +26,7 @@
     ERR_NEG_INT,
     binary_op,
     custom_op,
+    custom_primitive_op,
     function_op,
     method_op,
     unary_op,
@@ -382,3 +383,12 @@
     c_function_name="CPy_GetANext",
     error_kind=ERR_MAGIC,
 )
+
+# x.__name__ (requires Python 3.11+)
+name_op = custom_primitive_op(
+    name="__name__",
+    arg_types=[object_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="CPy_GetName",
+    error_kind=ERR_MAGIC,
+)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index c7bf5de852a8..2bdbb42b8d23 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1468,3 +1468,51 @@ L1:
 L2:
     r2 = self == r0
     return r2
+
+[case testTypeObjectName_python3_11]
+from typing import Any
+
+class C: pass
+class D(C): pass
+
+def n1(t: type[object]) -> str:
+    return t.__name__
+
+def n2(t: Any) -> str:
+    return t.__name__
+
+def n3() -> str:
+    return C.__name__
+
+def n4(t: type[C]) -> str:
+    return t.__name__
+[out]
+def n1(t):
+    t, r0 :: object
+    r1 :: str
+L0:
+    r0 = CPy_GetName(t)
+    r1 = cast(str, r0)
+    return r1
+def n2(t):
+    t, r0 :: object
+    r1 :: str
+L0:
+    r0 = CPy_GetName(t)
+    r1 = cast(str, r0)
+    return r1
+def n3():
+    r0, r1 :: object
+    r2 :: str
+L0:
+    r0 = __main__.C :: type
+    r1 = CPy_GetName(r0)
+    r2 = cast(str, r1)
+    return r2
+def n4(t):
+    t, r0 :: object
+    r1 :: str
+L0:
+    r0 = CPy_GetName(t)
+    r1 = cast(str, r0)
+    return r1
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 1481f3e06871..9582eec07b1a 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -3115,3 +3115,51 @@ f(C(1, "yes"))
 [out]
 B 1
 C yes
+
+[case testTypeObjectName]
+from typing import Any
+import re
+
+from dynamic import E, foo, Thing
+
+class C: pass
+class D(C): pass
+
+def type_name(t: type[object]) -> str:
+    return t.__name__
+
+def any_name(x: Any) -> str:
+    return x.__name__
+
+def assert_type_name(x: Any) -> None:
+    assert type_name(x) == getattr(x, "__name__")
+    assert any_name(x) == getattr(x, "__name__")
+
+def assert_any_name(x: Any) -> None:
+    assert any_name(x) == getattr(x, "__name__")
+
+def test_type_name() -> None:
+    assert_type_name(C)
+    assert_type_name(D)
+    assert_type_name(int)
+    assert_type_name(E)
+    assert_type_name(re.Pattern)
+
+def test_module_name() -> None:
+    assert_any_name(re)
+
+def test_function_name() -> None:
+    assert_any_name(any_name)
+    assert_any_name(foo)
+
+def test_obj_name() -> None:
+    assert_any_name(Thing())
+
+[file dynamic.py]
+class E: pass
+
+def foo(): pass
+
+class Thing:
+    def __init__(self):
+        self.__name__ = "xyz"

From 91487cbb6ba81954aac315932615afde7f05171d Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Mon, 18 Aug 2025 12:29:23 -0400
Subject: [PATCH 1558/1617] [mypyc] feat: extend stararg fastpath from #19623
 to handle lists and generic sequences (#19629)

This PR extends #19623 with additional logic for handling non-tuple star
inputs

Now, we can use the fast path for any arbitrary sequence, in addition to
tuples.

I opted to separate this PR from 19623 to keep them smaller and easier
to review, and to declutter the changes in the IR.
---
 mypyc/irbuild/ll_builder.py        |  34 +++-
 mypyc/primitives/tuple_ops.py      |   2 +-
 mypyc/test-data/irbuild-basic.test | 281 +++++++++++++++++++++++++++--
 3 files changed, 294 insertions(+), 23 deletions(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index c5f9503b8c66..f244e2f05e05 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -184,7 +184,12 @@
     str_ssize_t_size_op,
     unicode_compare,
 )
-from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op
+from mypyc.primitives.tuple_ops import (
+    list_tuple_op,
+    new_tuple_op,
+    new_tuple_with_length_op,
+    sequence_tuple_op,
+)
 from mypyc.rt_subtype import is_runtime_subtype
 from mypyc.sametype import is_same_type
 from mypyc.subtype import is_subtype
@@ -789,16 +794,25 @@ def _construct_varargs(
         for value, kind, name in args:
             if kind == ARG_STAR:
                 if star_result is None:
-                    # fast path if star expr is a tuple:
-                    # we can pass the immutable tuple straight into the function call.
-                    if is_tuple_rprimitive(value.type):
-                        if len(args) == 1:
-                            # fn(*args)
-                            return value, self._create_dict([], [], line)
-                        elif len(args) == 2 and args[1][1] == ARG_STAR2:
-                            # fn(*args, **kwargs)
+                    # star args fastpath
+                    if len(args) == 1:
+                        # fn(*args)
+                        if is_list_rprimitive(value.type):
+                            value = self.primitive_op(list_tuple_op, [value], line)
+                        elif not is_tuple_rprimitive(value.type) and not isinstance(
+                            value.type, RTuple
+                        ):
+                            value = self.primitive_op(sequence_tuple_op, [value], line)
+                        return value, self._create_dict([], [], line)
+                    elif len(args) == 2 and args[1][1] == ARG_STAR2:
+                        # fn(*args, **kwargs)
+                        if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple):
                             star_result = value
-                            continue
+                        elif is_list_rprimitive(value.type):
+                            star_result = self.primitive_op(list_tuple_op, [value], line)
+                        else:
+                            star_result = self.primitive_op(sequence_tuple_op, [value], line)
+                        continue
                         # elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
                     # TODO optimize this case using the length utils - currently in review
                     star_result = self.new_list_op(star_values, line)
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index d95161acf853..f262dec8b05a 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -76,7 +76,7 @@
 )
 
 # Construct tuple from an arbitrary (iterable) object.
-function_op(
+sequence_tuple_op = function_op(
     name="builtins.tuple",
     arg_types=[object_rprimitive],
     return_type=tuple_rprimitive,
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index 8d981db2b391..f52e1af03b52 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1675,25 +1675,19 @@ def g():
     r1 :: dict
     r2 :: str
     r3 :: object
-    r4 :: list
+    r4 :: dict
     r5, r6 :: object
-    r7 :: tuple
-    r8 :: dict
-    r9 :: object
-    r10 :: tuple[int, int, int]
+    r7 :: tuple[int, int, int]
 L0:
     r0 = (2, 4, 6)
     r1 = __main__.globals :: static
     r2 = 'f'
     r3 = CPyDict_GetItem(r1, r2)
-    r4 = PyList_New(0)
+    r4 = PyDict_New()
     r5 = box(tuple[int, int, int], r0)
-    r6 = CPyList_Extend(r4, r5)
-    r7 = PyList_AsTuple(r4)
-    r8 = PyDict_New()
-    r9 = PyObject_Call(r3, r7, r8)
-    r10 = unbox(tuple[int, int, int], r9)
-    return r10
+    r6 = PyObject_Call(r3, r5, r4)
+    r7 = unbox(tuple[int, int, int], r6)
+    return r7
 def h():
     r0 :: tuple[int, int]
     r1 :: dict
@@ -3546,3 +3540,266 @@ L0:
     r2 = PyObject_Vectorcall(r1, 0, 0, 0)
     r3 = box(None, 1)
     return r3
+
+[case testStarArgFastPathTuple]
+from typing import Any, Callable
+def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
+    def wrapper(*args: Any) -> Any:
+        return fn(*args)
+    return wrapper
+
+[out]
+def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def wrapper_deco_obj.__call__(__mypyc_self__, args):
+    __mypyc_self__ :: __main__.wrapper_deco_obj
+    args :: tuple
+    r0 :: __main__.deco_env
+    r1 :: object
+    r2 :: dict
+    r3 :: object
+L0:
+    r0 = __mypyc_self__.__mypyc_env__
+    r1 = r0.fn
+    r2 = PyDict_New()
+    r3 = PyObject_Call(r1, args, r2)
+    return r3
+def deco(fn):
+    fn :: object
+    r0 :: __main__.deco_env
+    r1 :: bool
+    r2 :: __main__.wrapper_deco_obj
+    r3 :: bool
+    wrapper :: object
+L0:
+    r0 = deco_env()
+    r0.fn = fn; r1 = is_error
+    r2 = wrapper_deco_obj()
+    r2.__mypyc_env__ = r0; r3 = is_error
+    wrapper = r2
+    return wrapper
+
+[case testStarArgFastPathList]
+from typing import Any, Callable
+def deco(fn: Callable[..., Any]) -> Callable[[list[Any]], Any]:
+    def wrapper(args: list[Any]) -> Any:
+        return fn(*args)
+    return wrapper
+
+[out]
+def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def wrapper_deco_obj.__call__(__mypyc_self__, args):
+    __mypyc_self__ :: __main__.wrapper_deco_obj
+    args :: list
+    r0 :: __main__.deco_env
+    r1 :: object
+    r2 :: tuple
+    r3 :: dict
+    r4 :: object
+L0:
+    r0 = __mypyc_self__.__mypyc_env__
+    r1 = r0.fn
+    r2 = PyList_AsTuple(args)
+    r3 = PyDict_New()
+    r4 = PyObject_Call(r1, r2, r3)
+    return r4
+def deco(fn):
+    fn :: object
+    r0 :: __main__.deco_env
+    r1 :: bool
+    r2 :: __main__.wrapper_deco_obj
+    r3 :: bool
+    wrapper :: object
+L0:
+    r0 = deco_env()
+    r0.fn = fn; r1 = is_error
+    r2 = wrapper_deco_obj()
+    r2.__mypyc_env__ = r0; r3 = is_error
+    wrapper = r2
+    return wrapper
+
+[case testStarArgFastPathListWithKwargs]
+from typing import Any, Callable
+def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
+    def wrapper(lst: list[Any], kwargs: dict[str, Any]) -> Any:
+        return fn(*lst, **kwargs)
+    return wrapper
+
+[out]
+def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs):
+    __mypyc_self__ :: __main__.wrapper_deco_obj
+    lst :: list
+    kwargs :: dict
+    r0 :: __main__.deco_env
+    r1 :: object
+    r2 :: tuple
+    r3 :: dict
+    r4 :: i32
+    r5 :: bit
+    r6 :: object
+L0:
+    r0 = __mypyc_self__.__mypyc_env__
+    r1 = r0.fn
+    r2 = PyList_AsTuple(lst)
+    r3 = PyDict_New()
+    r4 = CPyDict_UpdateInDisplay(r3, kwargs)
+    r5 = r4 >= 0 :: signed
+    r6 = PyObject_Call(r1, r2, r3)
+    return r6
+def deco(fn):
+    fn :: object
+    r0 :: __main__.deco_env
+    r1 :: bool
+    r2 :: __main__.wrapper_deco_obj
+    r3 :: bool
+    wrapper :: object
+L0:
+    r0 = deco_env()
+    r0.fn = fn; r1 = is_error
+    r2 = wrapper_deco_obj()
+    r2.__mypyc_env__ = r0; r3 = is_error
+    wrapper = r2
+    return wrapper
+
+[case testStarArgFastPathSequence]
+from typing import Any, Callable
+def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]:
+    def wrapper(args: Any) -> Any:
+        return fn(*args)
+    return wrapper
+
+[out]
+def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def wrapper_deco_obj.__call__(__mypyc_self__, args):
+    __mypyc_self__ :: __main__.wrapper_deco_obj
+    args :: object
+    r0 :: __main__.deco_env
+    r1 :: object
+    r2 :: tuple
+    r3 :: dict
+    r4 :: object
+L0:
+    r0 = __mypyc_self__.__mypyc_env__
+    r1 = r0.fn
+    r2 = PySequence_Tuple(args)
+    r3 = PyDict_New()
+    r4 = PyObject_Call(r1, r2, r3)
+    return r4
+def deco(fn):
+    fn :: object
+    r0 :: __main__.deco_env
+    r1 :: bool
+    r2 :: __main__.wrapper_deco_obj
+    r3 :: bool
+    wrapper :: object
+L0:
+    r0 = deco_env()
+    r0.fn = fn; r1 = is_error
+    r2 = wrapper_deco_obj()
+    r2.__mypyc_env__ = r0; r3 = is_error
+    wrapper = r2
+    return wrapper
+
+[case testStarArgFastPathSequenceWithKwargs]
+from typing import Any, Callable
+def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]:
+    def wrapper(args: Any, **kwargs: Any) -> Any:
+        return fn(*args, **kwargs)
+    return wrapper
+
+[out]
+def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
+    __mypyc_self__, instance, owner, r0 :: object
+    r1 :: bit
+    r2 :: object
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = instance == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    return __mypyc_self__
+L2:
+    r2 = PyMethod_New(__mypyc_self__, instance)
+    return r2
+def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs):
+    __mypyc_self__ :: __main__.wrapper_deco_obj
+    args :: object
+    kwargs :: dict
+    r0 :: __main__.deco_env
+    r1 :: object
+    r2 :: tuple
+    r3 :: dict
+    r4 :: i32
+    r5 :: bit
+    r6 :: object
+L0:
+    r0 = __mypyc_self__.__mypyc_env__
+    r1 = r0.fn
+    r2 = PySequence_Tuple(args)
+    r3 = PyDict_New()
+    r4 = CPyDict_UpdateInDisplay(r3, kwargs)
+    r5 = r4 >= 0 :: signed
+    r6 = PyObject_Call(r1, r2, r3)
+    return r6
+def deco(fn):
+    fn :: object
+    r0 :: __main__.deco_env
+    r1 :: bool
+    r2 :: __main__.wrapper_deco_obj
+    r3 :: bool
+    wrapper :: object
+L0:
+    r0 = deco_env()
+    r0.fn = fn; r1 = is_error
+    r2 = wrapper_deco_obj()
+    r2.__mypyc_env__ = r0; r3 = is_error
+    wrapper = r2
+    return wrapper

From 6c5b13ccbad7bcdb4b45b12d9ae1d0af8cb9a0e5 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 19 Aug 2025 11:48:51 +0100
Subject: [PATCH 1559/1617] Force all deserialized objects to the oldest GC
 generation (#19681)

This is a hack, but it gives ~30% perf win for `mypy -c 'import torch'`
on a warm run. This should not increase memory consumption too much,
since we shouldn't create any cyclic garbage during deserialization (we
do create some cyclic references, like `TypeInfo` -> `SymbolTable` ->
`Instance` -> `TypeInfo`, but those are genuine long-living objects).
---
 mypy/build.py | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/mypy/build.py b/mypy/build.py
index 71575de9d877..883ae1f22f19 100644
--- a/mypy/build.py
+++ b/mypy/build.py
@@ -116,6 +116,8 @@
     "abc",
 }
 
+# We are careful now, we can increase this in future if safe/useful.
+MAX_GC_FREEZE_CYCLES = 1
 
 Graph: _TypeAlias = dict[str, "State"]
 
@@ -707,6 +709,8 @@ def __init__(
         # new file can be processed O(n**2) times. This cache
         # avoids most of this redundant work.
         self.ast_cache: dict[str, tuple[MypyFile, list[ErrorInfo]]] = {}
+        # Number of times we used GC optimization hack for fresh SCCs.
+        self.gc_freeze_cycles = 0
 
     def dump_stats(self) -> None:
         if self.options.dump_build_stats:
@@ -3326,8 +3330,29 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
                 #
                 # TODO: see if it's possible to determine if we need to process only a
                 # _subset_ of the past SCCs instead of having to process them all.
+                if (
+                    platform.python_implementation() == "CPython"
+                    and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
+                ):
+                    # When deserializing cache we create huge amount of new objects, so even
+                    # with our generous GC thresholds, GC is still doing a lot of pointless
+                    # work searching for garbage. So, we temporarily disable it when
+                    # processing fresh SCCs, and then move all the new objects to the oldest
+                    # generation with the freeze()/unfreeze() trick below. This is arguably
+                    # a hack, but it gives huge performance wins for large third-party
+                    # libraries, like torch.
+                    gc.collect()
+                    gc.disable()
                 for prev_scc in fresh_scc_queue:
                     process_fresh_modules(graph, prev_scc, manager)
+                if (
+                    platform.python_implementation() == "CPython"
+                    and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
+                ):
+                    manager.gc_freeze_cycles += 1
+                    gc.freeze()
+                    gc.unfreeze()
+                    gc.enable()
                 fresh_scc_queue = []
             size = len(scc)
             if size == 1:

From 68809c0056c3bc6446481ec7429d48f6fcc1f5c2 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 19 Aug 2025 14:32:50 +0100
Subject: [PATCH 1560/1617] [mypyc] Specialize bytes.decode calls with common
 encodings (#19688)

This is similar to #18232, which specialized `encode`.

A micro-benchmark that calls `decode` repeatedly was up to 45% faster.
---
 mypyc/irbuild/specialize.py      | 65 ++++++++++++++++++++++++++++
 mypyc/lib-rt/CPy.h               |  3 ++
 mypyc/lib-rt/str_ops.c           | 39 +++++++++++++++++
 mypyc/primitives/str_ops.py      | 26 ++++++++++-
 mypyc/test-data/fixtures/ir.py   |  2 +-
 mypyc/test-data/irbuild-str.test | 38 +++++++++++++---
 mypyc/test-data/run-strings.test | 74 +++++++++++++++++++++++++++++---
 7 files changed, 233 insertions(+), 14 deletions(-)

diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py
index 845968222234..0880c62bc7a5 100644
--- a/mypyc/irbuild/specialize.py
+++ b/mypyc/irbuild/specialize.py
@@ -50,6 +50,7 @@
     RTuple,
     RType,
     bool_rprimitive,
+    bytes_rprimitive,
     c_int_rprimitive,
     dict_rprimitive,
     int16_rprimitive,
@@ -98,6 +99,9 @@
 from mypyc.primitives.misc_ops import isinstance_bool
 from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set
 from mypyc.primitives.str_ops import (
+    bytes_decode_ascii_strict,
+    bytes_decode_latin1_strict,
+    bytes_decode_utf8_strict,
     isinstance_str,
     str_encode_ascii_strict,
     str_encode_latin1_strict,
@@ -787,6 +791,67 @@ def str_encode_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ->
     return None
 
 
+@specialize_function("decode", bytes_rprimitive)
+def bytes_decode_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
+    """Specialize common cases of obj.decode for most used encodings and strict errors."""
+
+    if not isinstance(callee, MemberExpr):
+        return None
+
+    # We can only specialize if we have string literals as args
+    if len(expr.arg_kinds) > 0 and not isinstance(expr.args[0], StrExpr):
+        return None
+    if len(expr.arg_kinds) > 1 and not isinstance(expr.args[1], StrExpr):
+        return None
+
+    encoding = "utf8"
+    errors = "strict"
+    if len(expr.arg_kinds) > 0 and isinstance(expr.args[0], StrExpr):
+        if expr.arg_kinds[0] == ARG_NAMED:
+            if expr.arg_names[0] == "encoding":
+                encoding = expr.args[0].value
+            elif expr.arg_names[0] == "errors":
+                errors = expr.args[0].value
+        elif expr.arg_kinds[0] == ARG_POS:
+            encoding = expr.args[0].value
+        else:
+            return None
+    if len(expr.arg_kinds) > 1 and isinstance(expr.args[1], StrExpr):
+        if expr.arg_kinds[1] == ARG_NAMED:
+            if expr.arg_names[1] == "encoding":
+                encoding = expr.args[1].value
+            elif expr.arg_names[1] == "errors":
+                errors = expr.args[1].value
+        elif expr.arg_kinds[1] == ARG_POS:
+            errors = expr.args[1].value
+        else:
+            return None
+
+    if errors != "strict":
+        # We can only specialize strict errors
+        return None
+
+    encoding = encoding.lower().replace("_", "-")  # normalize
+    # Specialized encodings and their accepted aliases
+    if encoding in ["u8", "utf", "utf8", "utf-8", "cp65001"]:
+        return builder.call_c(bytes_decode_utf8_strict, [builder.accept(callee.expr)], expr.line)
+    elif encoding in ["646", "ascii", "usascii", "us-ascii"]:
+        return builder.call_c(bytes_decode_ascii_strict, [builder.accept(callee.expr)], expr.line)
+    elif encoding in [
+        "iso8859-1",
+        "iso-8859-1",
+        "8859",
+        "cp819",
+        "latin",
+        "latin1",
+        "latin-1",
+        "l1",
+    ]:
+        return builder.call_c(bytes_decode_latin1_strict, [builder.accept(callee.expr)], expr.line)
+
+    return None
+
+
 @specialize_function("mypy_extensions.i64")
 def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
     if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS:
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index ffa4f6a363d2..aea5db25f29f 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -752,6 +752,9 @@ PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix);
 bool CPyStr_IsTrue(PyObject *obj);
 Py_ssize_t CPyStr_Size_size_t(PyObject *str);
 PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors);
+PyObject *CPy_DecodeUTF8(PyObject *bytes);
+PyObject *CPy_DecodeASCII(PyObject *bytes);
+PyObject *CPy_DecodeLatin1(PyObject *bytes);
 PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors);
 Py_ssize_t CPyStr_Count(PyObject *unicode, PyObject *substring, CPyTagged start);
 Py_ssize_t CPyStr_CountFull(PyObject *unicode, PyObject *substring, CPyTagged start, CPyTagged end);
diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c
index a2d10aacea46..337ef14fc955 100644
--- a/mypyc/lib-rt/str_ops.c
+++ b/mypyc/lib-rt/str_ops.c
@@ -513,6 +513,45 @@ PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors) {
     }
 }
 
+PyObject *CPy_DecodeUTF8(PyObject *bytes) {
+    if (PyBytes_CheckExact(bytes)) {
+        char *buffer = PyBytes_AsString(bytes);   // Borrowed reference
+        if (buffer == NULL) {
+            return NULL;
+        }
+        Py_ssize_t size = PyBytes_Size(bytes);
+        return PyUnicode_DecodeUTF8(buffer, size, "strict");
+    } else {
+        return PyUnicode_FromEncodedObject(bytes, "utf-8", "strict");
+    }
+}
+
+PyObject *CPy_DecodeASCII(PyObject *bytes) {
+    if (PyBytes_CheckExact(bytes)) {
+        char *buffer = PyBytes_AsString(bytes);   // Borrowed reference
+        if (buffer == NULL) {
+            return NULL;
+        }
+        Py_ssize_t size = PyBytes_Size(bytes);
+        return PyUnicode_DecodeASCII(buffer, size, "strict");;
+    } else {
+        return PyUnicode_FromEncodedObject(bytes, "ascii", "strict");
+    }
+}
+
+PyObject *CPy_DecodeLatin1(PyObject *bytes) {
+    if (PyBytes_CheckExact(bytes)) {
+        char *buffer = PyBytes_AsString(bytes);   // Borrowed reference
+        if (buffer == NULL) {
+            return NULL;
+        }
+        Py_ssize_t size = PyBytes_Size(bytes);
+        return PyUnicode_DecodeLatin1(buffer, size, "strict");
+    } else {
+        return PyUnicode_FromEncodedObject(bytes, "latin1", "strict");
+    }
+}
+
 PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) {
     const char *enc = NULL;
     const char *err = NULL;
diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py
index f07081c6aaa5..a8f4e4df74c2 100644
--- a/mypyc/primitives/str_ops.py
+++ b/mypyc/primitives/str_ops.py
@@ -387,7 +387,7 @@
     extra_int_constants=[(0, pointer_rprimitive)],
 )
 
-# obj.decode(encoding, errors)
+# bytes.decode(encoding, errors)
 method_op(
     name="decode",
     arg_types=[bytes_rprimitive, str_rprimitive, str_rprimitive],
@@ -396,6 +396,30 @@
     error_kind=ERR_MAGIC,
 )
 
+# bytes.decode(encoding) - utf8 strict specialization
+bytes_decode_utf8_strict = custom_op(
+    arg_types=[bytes_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="CPy_DecodeUTF8",
+    error_kind=ERR_MAGIC,
+)
+
+# bytes.decode(encoding) - ascii strict specialization
+bytes_decode_ascii_strict = custom_op(
+    arg_types=[bytes_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="CPy_DecodeASCII",
+    error_kind=ERR_MAGIC,
+)
+
+# bytes.decode(encoding) - latin1 strict specialization
+bytes_decode_latin1_strict = custom_op(
+    arg_types=[bytes_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="CPy_DecodeLatin1",
+    error_kind=ERR_MAGIC,
+)
+
 # str.encode()
 method_op(
     name="encode",
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 661ae50fd5f3..28c9244b4b27 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -171,7 +171,7 @@ def __getitem__(self, i: int) -> int: ...
     @overload
     def __getitem__(self, i: slice) -> bytes: ...
     def join(self, x: Iterable[object]) -> bytes: ...
-    def decode(self, x: str=..., y: str=...) -> str: ...
+    def decode(self, encoding: str=..., errors: str=...) -> str: ...
     def __iter__(self) -> Iterator[int]: ...
 
 class bytearray:
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index 24807510193d..245acf7402a1 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -325,19 +325,43 @@ L0:
 [case testDecode]
 def f(b: bytes) -> None:
     b.decode()
+    b.decode('Utf_8')
     b.decode('utf-8')
+    b.decode('UTF8')
+    b.decode('latin1')
+    b.decode('Latin-1')
+    b.decode('ascii')
+    encoding = 'utf-8'
+    b.decode(encoding)
     b.decode('utf-8', 'backslashreplace')
+def variants(b: bytes) -> None:
+    b.decode(encoding="UTF_8")
+    b.decode("ascii", errors="strict")
 [out]
 def f(b):
     b :: bytes
-    r0, r1, r2, r3, r4, r5 :: str
+    r0, r1, r2, r3, r4, r5, r6, r7, encoding, r8, r9, r10, r11 :: str
 L0:
-    r0 = CPy_Decode(b, 0, 0)
-    r1 = 'utf-8'
-    r2 = CPy_Decode(b, r1, 0)
-    r3 = 'utf-8'
-    r4 = 'backslashreplace'
-    r5 = CPy_Decode(b, r3, r4)
+    r0 = CPy_DecodeUTF8(b)
+    r1 = CPy_DecodeUTF8(b)
+    r2 = CPy_DecodeUTF8(b)
+    r3 = CPy_DecodeUTF8(b)
+    r4 = CPy_DecodeLatin1(b)
+    r5 = CPy_DecodeLatin1(b)
+    r6 = CPy_DecodeASCII(b)
+    r7 = 'utf-8'
+    encoding = r7
+    r8 = CPy_Decode(b, encoding, 0)
+    r9 = 'utf-8'
+    r10 = 'backslashreplace'
+    r11 = CPy_Decode(b, r9, r10)
+    return 1
+def variants(b):
+    b :: bytes
+    r0, r1 :: str
+L0:
+    r0 = CPy_DecodeUTF8(b)
+    r1 = CPy_DecodeASCII(b)
     return 1
 
 [case testEncode_64bit]
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index 8a914c08bfb2..b4f3ebd66910 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -792,14 +792,23 @@ def test_ord() -> None:
         ord('')
 
 [case testDecode]
+from testutil import assertRaises
+
 def test_decode() -> None:
     assert "\N{GREEK CAPITAL LETTER DELTA}" == '\u0394'
     assert "\u0394" == "\u0394"
     assert "\U00000394" == '\u0394'
     assert b'\x80abc'.decode('utf-8', 'replace') == '\ufffdabc'
     assert b'\x80abc'.decode('utf-8', 'backslashreplace') == '\\x80abc'
+    assert b''.decode() == ''
+    assert b'a'.decode() == 'a'
     assert b'abc'.decode() == 'abc'
     assert b'abc'.decode('utf-8') == 'abc'
+    assert b'abc'.decode('utf-8' + str()) == 'abc'
+    assert b'abc\x00\xce'.decode('latin-1') == 'abc\x00\xce'
+    assert b'abc\x00\xce'.decode('latin-1' + str()) == 'abc\x00\xce'
+    assert b'abc\x00\x7f'.decode('ascii') == 'abc\x00\x7f'
+    assert b'abc\x00\x7f'.decode('ascii' + str()) == 'abc\x00\x7f'
     assert b'\x80abc'.decode('utf-8', 'ignore') == 'abc'
     assert b'\x80abc'.decode('UTF-8', 'ignore') == 'abc'
     assert b'\x80abc'.decode('Utf-8', 'ignore') == 'abc'
@@ -808,16 +817,71 @@ def test_decode() -> None:
     assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('gbk', 'ignore') == '一二三'
     assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('latin1', 'ignore') == 'Ò»¶þÈý'
     assert b'Z\xc3\xbcrich'.decode("utf-8") == 'Zürich'
-    try:
-        b'Z\xc3\xbcrich'.decode('ascii')
-        assert False
-    except UnicodeDecodeError:
-        pass
+    assert b'Z\xc3\xbcrich'.decode("utf-8" + str()) == 'Zürich'
+
     assert bytearray(range(5)).decode() == '\x00\x01\x02\x03\x04'
     b = bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd')
     assert b.decode() == '你好'
     assert b.decode('gbk') == '浣犲ソ'
     assert b.decode('latin1') == 'ä½\xa0好'
+    assert b.decode('latin1' + str()) == 'ä½\xa0好'
+
+def test_decode_error() -> None:
+    try:
+        b'Z\xc3\xbcrich'.decode('ascii')
+        assert False
+    except UnicodeDecodeError:
+        pass
+    try:
+        b'Z\xc3\xbcrich'.decode('ascii' + str())
+        assert False
+    except UnicodeDecodeError:
+        pass
+    try:
+        b'Z\xc3y'.decode('utf8')
+        assert False
+    except UnicodeDecodeError:
+        pass
+    try:
+        b'Z\xc3y'.decode('utf8' + str())
+        assert False
+    except UnicodeDecodeError:
+        pass
+
+def test_decode_bytearray() -> None:
+    b: bytes = bytearray(b'foo\x00bar')
+    assert b.decode() == 'foo\x00bar'
+    assert b.decode('utf-8') == 'foo\x00bar'
+    assert b.decode('latin-1') == 'foo\x00bar'
+    assert b.decode('ascii') == 'foo\x00bar'
+    assert b.decode('utf-8' + str()) == 'foo\x00bar'
+    assert b.decode('latin-1' + str()) == 'foo\x00bar'
+    assert b.decode('ascii' + str()) == 'foo\x00bar'
+    b2: bytes = bytearray(b'foo\x00bar\xbe')
+    assert b2.decode('latin-1') == 'foo\x00bar\xbe'
+    with assertRaises(UnicodeDecodeError):
+        b2.decode('ascii')
+    with assertRaises(UnicodeDecodeError):
+        b2.decode('ascii' + str())
+    with assertRaises(UnicodeDecodeError):
+        b2.decode('utf-8')
+    with assertRaises(UnicodeDecodeError):
+        b2.decode('utf-8' + str())
+    b3: bytes = bytearray(b'Z\xc3\xbcrich')
+    assert b3.decode("utf-8") == 'Zürich'
+
+def test_invalid_encoding() -> None:
+    try:
+        b"foo".decode("ut-f-8")
+        assert False
+    except Exception as e:
+        assert repr(e).startswith("LookupError")
+    try:
+        encoding = "ut-f-8"
+        b"foo".decode(encoding)
+        assert False
+    except Exception as e:
+        assert repr(e).startswith("LookupError")
 
 [case testEncode]
 from testutil import assertRaises

From 722f4dd92d368e059f616a143def40b6d2eaa8d0 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Tue, 19 Aug 2025 16:32:57 +0100
Subject: [PATCH 1561/1617] [mypyc] Optimize type(x) and x.__class__ (#19691)

Using `Py_TYPE` avoids a function call. Also specialize `x.__class__`
for instances of native classes -- it's treated as equivalent to
`type(x)`. This is not generally possible, so only do it for native
instances where we can make more assumptions.
---
 mypyc/irbuild/expression.py          |  6 ++++
 mypyc/lib-rt/CPy.h                   |  6 ++++
 mypyc/primitives/misc_ops.py         |  2 +-
 mypyc/test-data/fixtures/ir.py       |  1 +
 mypyc/test-data/irbuild-classes.test | 42 +++++++++++++++++++++++++
 mypyc/test-data/irbuild-try.test     |  2 +-
 mypyc/test-data/run-classes.test     | 46 ++++++++++++++++++++++++++++
 7 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index 312ba98e3c5d..e82203021ae3 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -69,6 +69,7 @@
     Value,
 )
 from mypyc.ir.rtypes import (
+    RInstance,
     RTuple,
     bool_rprimitive,
     int_rprimitive,
@@ -226,6 +227,11 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value:
     ):
         return builder.primitive_op(name_op, [obj], expr.line)
 
+    if isinstance(obj.type, RInstance) and expr.name == "__class__":
+        # A non-native class could override "__class__" using "__getattribute__", so
+        # only apply to RInstance types.
+        return builder.primitive_op(type_op, [obj], expr.line)
+
     # Special case: for named tuples transform attribute access to faster index access.
     typ = get_proper_type(builder.types.get(expr.expr))
     if isinstance(typ, TupleType) and typ.partial_fallback.type.is_named_tuple:
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index aea5db25f29f..8cd141545bbb 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -877,6 +877,12 @@ static inline bool CPy_TypeCheck(PyObject *o, PyObject *type) {
     return PyObject_TypeCheck(o, (PyTypeObject *)type);
 }
 
+static inline PyObject *CPy_TYPE(PyObject *obj) {
+    PyObject *result = (PyObject *)Py_TYPE(obj);
+    Py_INCREF(result);
+    return result;
+}
+
 PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o);
 PyObject *CPy_GetCoro(PyObject *obj);
 PyObject *CPyIter_Send(PyObject *iter, PyObject *val);
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index e3d59f53ed76..a13f87cc94e9 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -214,7 +214,7 @@
 type_op = function_op(
     name="builtins.type",
     arg_types=[object_rprimitive],
-    c_function_name="PyObject_Type",
+    c_function_name="CPy_TYPE",
     return_type=object_rprimitive,
     error_kind=ERR_NEVER,
 )
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index 28c9244b4b27..c041c661741c 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -39,6 +39,7 @@ def __pow__(self, other: T_contra, modulo: _M) -> T_co: ...
 ]
 
 class object:
+    __class__: type
     def __init__(self) -> None: pass
     def __eq__(self, x: object) -> bool: pass
     def __ne__(self, x: object) -> bool: pass
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 2bdbb42b8d23..4bb20ee9c65c 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1516,3 +1516,45 @@ L0:
     r0 = CPy_GetName(t)
     r1 = cast(str, r0)
     return r1
+
+[case testTypeOfObject]
+class C: pass
+class D(C): pass
+
+def generic_type(x: object) -> type[object]:
+    return type(x)
+
+def generic_class(x: object) -> type[object]:
+    return x.__class__
+
+def native_type(x: C) -> type[object]:
+    return type(x)
+
+def native_class(x: C) -> type[object]:
+    return x.__class__
+[out]
+def generic_type(x):
+    x, r0 :: object
+L0:
+    r0 = CPy_TYPE(x)
+    return r0
+def generic_class(x):
+    x :: object
+    r0 :: str
+    r1 :: object
+L0:
+    r0 = '__class__'
+    r1 = CPyObject_GetAttr(x, r0)
+    return r1
+def native_type(x):
+    x :: __main__.C
+    r0 :: object
+L0:
+    r0 = CPy_TYPE(x)
+    return r0
+def native_class(x):
+    x :: __main__.C
+    r0 :: object
+L0:
+    r0 = CPy_TYPE(x)
+    return r0
diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test
index ad1aa78c0554..ec470eae1e88 100644
--- a/mypyc/test-data/irbuild-try.test
+++ b/mypyc/test-data/irbuild-try.test
@@ -412,7 +412,7 @@ def foo(x):
     r36 :: bit
 L0:
     r0 = PyObject_Vectorcall(x, 0, 0, 0)
-    r1 = PyObject_Type(r0)
+    r1 = CPy_TYPE(r0)
     r2 = '__exit__'
     r3 = CPyObject_GetAttr(r1, r2)
     r4 = '__enter__'
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 9582eec07b1a..fed5cfb65870 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -3163,3 +3163,49 @@ def foo(): pass
 class Thing:
     def __init__(self):
         self.__name__ = "xyz"
+
+[case testTypeOfObject]
+from typing import Any
+
+from dynamic import Dyn
+
+class Foo: pass
+class Bar(Foo): pass
+
+def generic_type(x) -> type[object]:
+    return x.__class__
+
+def test_built_in_type() -> None:
+    i: Any = int
+    l: Any = list
+    assert type(i()) is i().__class__
+    assert type(i()) is int
+    assert type(l()) is list
+    n = 5
+    assert n.__class__ is i
+
+def test_native_class() -> None:
+    f_any: Any = Foo()
+    b_any: Any = Bar()
+    f: Foo = f_any
+    b: Foo = b_any
+    if int("1"):  # use int("1") to avoid constant folding
+        assert type(f) is Foo
+        assert type(b) is Bar
+    if int("2"):
+        assert f.__class__ is Foo
+        assert b.__class__ is Bar
+    if int("3"):
+        assert f_any.__class__ is Foo
+        assert b_any.__class__ is Bar
+    if int("4"):
+        assert type(f_any) is Foo
+        assert type(b_any) is Bar
+
+def test_python_class() -> None:
+    d = Dyn()
+    assert type(d) is Dyn
+    assert d.__class__ is Dyn
+
+[file dynamic.py]
+class Dyn: pass

From 0d23c61f067c56ef51d3c00b902c7582af1d9263 Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra 
Date: Tue, 19 Aug 2025 08:43:49 -0700
Subject: [PATCH 1562/1617] Implement PEP 800 (@disjoint_base) (#19678)

https://peps.python.org/pep-0800/

- Recognize the @disjoint_base decorator
- Error if a class definition has incompatible disjoint bases
- Recognize that classes with incompatible disjoint bases cannot exist
- Check in stubtest that @disjoint_base is correctly applied
- The self check found a line of dead code in mypy itself, due to
classes that are disjoint bases from `__slots__`.
---
 mypy/checker.py                               |  7 ++
 mypy/checkmember.py                           |  8 +--
 mypy/message_registry.py                      |  3 +
 mypy/nodes.py                                 |  4 ++
 mypy/semanal.py                               |  3 +
 mypy/stubtest.py                              | 63 ++++++++++++++++++
 mypy/test/teststubtest.py                     | 65 ++++++++++++++++++-
 mypy/typeops.py                               | 51 +++++++++++++++
 mypy/types.py                                 |  5 +-
 test-data/unit/check-classes.test             |  2 +-
 test-data/unit/check-final.test               | 64 ++++++++++++++++++
 test-data/unit/check-incremental.test         |  6 +-
 test-data/unit/check-isinstance.test          | 27 ++++++++
 test-data/unit/check-slots.test               |  1 +
 test-data/unit/lib-stub/typing_extensions.pyi |  1 +
 15 files changed, 296 insertions(+), 14 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 47c72924bf3c..0fe77e953d06 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -171,6 +171,7 @@
 from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type, make_optional_type
 from mypy.typeops import (
     bind_self,
+    can_have_shared_disjoint_base,
     coerce_to_literal,
     custom_special_method,
     erase_def_to_union_or_bound,
@@ -2658,6 +2659,8 @@ def visit_class_def(self, defn: ClassDef) -> None:
         for base in typ.mro[1:]:
             if base.is_final:
                 self.fail(message_registry.CANNOT_INHERIT_FROM_FINAL.format(base.name), defn)
+        if not can_have_shared_disjoint_base(typ.bases):
+            self.fail(message_registry.INCOMPATIBLE_DISJOINT_BASES.format(typ.name), defn)
         with self.tscope.class_scope(defn.info), self.enter_partial_types(is_class=True):
             old_binder = self.binder
             self.binder = ConditionalTypeBinder(self.options)
@@ -5826,6 +5829,10 @@ def _make_fake_typeinfo_and_full_name(
             format_type_distinctly(*base_classes, options=self.options, bare=True), "and"
         )
 
+        if not can_have_shared_disjoint_base(base_classes):
+            errors.append((pretty_names_list, "have distinct disjoint bases"))
+            return None
+
         new_errors = []
         for base in base_classes:
             if base.type.is_final:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 2c41f2e273cc..e7de1b7a304f 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1484,13 +1484,7 @@ def analyze_decorator_or_funcbase_access(
     if isinstance(defn, Decorator):
         return analyze_var(name, defn.var, itype, mx)
     typ = function_type(defn, mx.chk.named_type("builtins.function"))
-    is_trivial_self = False
-    if isinstance(defn, Decorator):
-        # Use fast path if there are trivial decorators like @classmethod or @property
-        is_trivial_self = defn.func.is_trivial_self and not defn.decorators
-    elif isinstance(defn, (FuncDef, OverloadedFuncDef)):
-        is_trivial_self = defn.is_trivial_self
-    if is_trivial_self:
+    if isinstance(defn, (FuncDef, OverloadedFuncDef)) and defn.is_trivial_self:
         return bind_self_fast(typ, mx.self_type)
     typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg)
     return bind_self(typ, original_type=mx.self_type, is_classmethod=defn.is_class)
diff --git a/mypy/message_registry.py b/mypy/message_registry.py
index 381aedfca059..09004322aee9 100644
--- a/mypy/message_registry.py
+++ b/mypy/message_registry.py
@@ -239,6 +239,9 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
 )
 CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final")
 
+# Disjoint bases
+INCOMPATIBLE_DISJOINT_BASES: Final = ErrorMessage('Class "{}" has incompatible disjoint bases')
+
 # Enum
 ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDDEN: Final = ErrorMessage(
     'Assigned "__members__" will be overridden by "Enum" internally'
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 99b9bf72c948..8c2110b156f1 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -3004,6 +3004,7 @@ class is generic then it will be a type constructor of higher kind.
         "_mro_refs",
         "bad_mro",
         "is_final",
+        "is_disjoint_base",
         "declared_metaclass",
         "metaclass_type",
         "names",
@@ -3055,6 +3056,7 @@ class is generic then it will be a type constructor of higher kind.
     _mro_refs: list[str] | None
     bad_mro: bool  # Could not construct full MRO
     is_final: bool
+    is_disjoint_base: bool
 
     declared_metaclass: mypy.types.Instance | None
     metaclass_type: mypy.types.Instance | None
@@ -3209,6 +3211,7 @@ class is generic then it will be a type constructor of higher kind.
         "is_protocol",
         "runtime_protocol",
         "is_final",
+        "is_disjoint_base",
         "is_intersection",
     ]
 
@@ -3241,6 +3244,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None
         self.type_var_tuple_suffix: int | None = None
         self.add_type_vars()
         self.is_final = False
+        self.is_disjoint_base = False
         self.is_enum = False
         self.fallback_to_any = False
         self.meta_fallback_to_any = False
diff --git a/mypy/semanal.py b/mypy/semanal.py
index eef658d9300b..e8426a4e4885 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -254,6 +254,7 @@
     ASSERT_TYPE_NAMES,
     DATACLASS_TRANSFORM_NAMES,
     DEPRECATED_TYPE_NAMES,
+    DISJOINT_BASE_DECORATOR_NAMES,
     FINAL_DECORATOR_NAMES,
     FINAL_TYPE_NAMES,
     IMPORTED_REVEAL_TYPE_NAMES,
@@ -2188,6 +2189,8 @@ def analyze_class_decorator_common(
         """
         if refers_to_fullname(decorator, FINAL_DECORATOR_NAMES):
             info.is_final = True
+        elif refers_to_fullname(decorator, DISJOINT_BASE_DECORATOR_NAMES):
+            info.is_disjoint_base = True
         elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES):
             info.is_type_check_only = True
         elif (deprecated := self.get_deprecated(decorator)) is not None:
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index ef8c8dc318e1..43da0518b3f9 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -17,6 +17,7 @@
 import os
 import pkgutil
 import re
+import struct
 import symtable
 import sys
 import traceback
@@ -466,6 +467,67 @@ class SubClass(runtime):  # type: ignore[misc]
         )
 
 
+SIZEOF_PYOBJECT = struct.calcsize("P")
+
+
+def _shape_differs(t1: type[object], t2: type[object]) -> bool:
+    """Check whether two types differ in shape.
+
+    Mirrors the shape_differs() function in typeobject.c in CPython."""
+    if sys.version_info >= (3, 12):
+        return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__
+    else:
+        # CPython had more complicated logic before 3.12:
+        # https://github.com/python/cpython/blob/f3c6f882cddc8dc30320d2e73edf019e201394fc/Objects/typeobject.c#L2224
+        # We attempt to mirror it here well enough to support the most common cases.
+        if t1.__itemsize__ or t2.__itemsize__:
+            return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__
+        t_size = t1.__basicsize__
+        if not t2.__weakrefoffset__ and t1.__weakrefoffset__ + SIZEOF_PYOBJECT == t_size:
+            t_size -= SIZEOF_PYOBJECT
+        if not t2.__dictoffset__ and t1.__dictoffset__ + SIZEOF_PYOBJECT == t_size:
+            t_size -= SIZEOF_PYOBJECT
+        if not t2.__weakrefoffset__ and t2.__weakrefoffset__ == t_size:
+            t_size -= SIZEOF_PYOBJECT
+        return t_size != t2.__basicsize__
+
+
+def _is_disjoint_base(typ: type[object]) -> bool:
+    """Return whether a type is a disjoint base at runtime, mirroring CPython's logic in typeobject.c.
+
+    See PEP 800."""
+    if typ is object:
+        return True
+    base = typ.__base__
+    assert base is not None, f"Type {typ} has no base"
+    return _shape_differs(typ, base)
+
+
+def _verify_disjoint_base(
+    stub: nodes.TypeInfo, runtime: type[object], object_path: list[str]
+) -> Iterator[Error]:
+    # If it's final, doesn't matter whether it's a disjoint base or not
+    if stub.is_final:
+        return
+    is_disjoint_runtime = _is_disjoint_base(runtime)
+    if is_disjoint_runtime and not stub.is_disjoint_base:
+        yield Error(
+            object_path,
+            "is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub",
+            stub,
+            runtime,
+            stub_desc=repr(stub),
+        )
+    elif not is_disjoint_runtime and stub.is_disjoint_base:
+        yield Error(
+            object_path,
+            "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime",
+            stub,
+            runtime,
+            stub_desc=repr(stub),
+        )
+
+
 def _verify_metaclass(
     stub: nodes.TypeInfo, runtime: type[Any], object_path: list[str], *, is_runtime_typeddict: bool
 ) -> Iterator[Error]:
@@ -534,6 +596,7 @@ def verify_typeinfo(
         return
 
     yield from _verify_final(stub, runtime, object_path)
+    yield from _verify_disjoint_base(stub, runtime, object_path)
     is_runtime_typeddict = stub.typeddict_type is not None and is_typeddict(runtime)
     yield from _verify_metaclass(
         stub, runtime, object_path, is_runtime_typeddict=is_runtime_typeddict
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index b071c0ee8ab6..69e2abe62f85 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -1405,9 +1405,16 @@ def spam(x=Flags4(0)): pass
         )
         yield Case(
             stub="""
+            import sys
             from typing import Final, Literal
-            class BytesEnum(bytes, enum.Enum):
-                a = b'foo'
+            from typing_extensions import disjoint_base
+            if sys.version_info >= (3, 12):
+                class BytesEnum(bytes, enum.Enum):
+                    a = b'foo'
+            else:
+                @disjoint_base
+                class BytesEnum(bytes, enum.Enum):
+                    a = b'foo'
             FOO: Literal[BytesEnum.a]
             BAR: Final = BytesEnum.a
             BAZ: BytesEnum
@@ -1613,6 +1620,60 @@ def test_not_subclassable(self) -> Iterator[Case]:
             error="CannotBeSubclassed",
         )
 
+    @collect_cases
+    def test_disjoint_base(self) -> Iterator[Case]:
+        yield Case(
+            stub="""
+            class A: pass
+            """,
+            runtime="""
+            class A: pass
+            """,
+            error=None,
+        )
+        yield Case(
+            stub="""
+            from typing_extensions import disjoint_base
+
+            @disjoint_base
+            class B: pass
+            """,
+            runtime="""
+            class B: pass
+            """,
+            error="test_module.B",
+        )
+        yield Case(
+            stub="""
+            from typing_extensions import Self
+
+            class mytakewhile:
+                def __new__(cls, predicate: object, iterable: object, /) -> Self: ...
+                def __iter__(self) -> Self: ...
+                def __next__(self) -> object: ...
+            """,
+            runtime="""
+            from itertools import takewhile as mytakewhile
+            """,
+            # Should have @disjoint_base
+            error="test_module.mytakewhile",
+        )
+        yield Case(
+            stub="""
+            from typing_extensions import disjoint_base, Self
+
+            @disjoint_base
+            class mycorrecttakewhile:
+                def __new__(cls, predicate: object, iterable: object, /) -> Self: ...
+                def __iter__(self) -> Self: ...
+                def __next__(self) -> object: ...
+            """,
+            runtime="""
+            from itertools import takewhile as mycorrecttakewhile
+            """,
+            error=None,
+        )
+
     @collect_cases
     def test_has_runtime_final_decorator(self) -> Iterator[Case]:
         yield Case(
diff --git a/mypy/typeops.py b/mypy/typeops.py
index 88b3c5da48ce..0cb6018d01fd 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -1253,3 +1253,54 @@ def named_type(fullname: str) -> Instance:
             )
         )
     return subtype
+
+
+def _is_disjoint_base(info: TypeInfo) -> bool:
+    # It either has the @disjoint_base decorator or defines nonempty __slots__.
+    if info.is_disjoint_base:
+        return True
+    if not info.slots:
+        return False
+    own_slots = {
+        slot
+        for slot in info.slots
+        if not any(
+            base_info.type.slots is not None and slot in base_info.type.slots
+            for base_info in info.bases
+        )
+    }
+    return bool(own_slots)
+
+
+def _get_disjoint_base_of(instance: Instance) -> TypeInfo | None:
+    """Returns the disjoint base of the given instance, if it exists."""
+    if _is_disjoint_base(instance.type):
+        return instance.type
+    for base in instance.type.mro:
+        if _is_disjoint_base(base):
+            return base
+    return None
+
+
+def can_have_shared_disjoint_base(instances: list[Instance]) -> bool:
+    """Returns whether the given instances can share a disjoint base.
+
+    This means that a child class of these classes can exist at runtime.
+    """
+    # Ignore None disjoint bases (which are `object`).
+    disjoint_bases = [
+        base for instance in instances if (base := _get_disjoint_base_of(instance)) is not None
+    ]
+    if not disjoint_bases:
+        # All are `object`.
+        return True
+
+    candidate = disjoint_bases[0]
+    for base in disjoint_bases[1:]:
+        if candidate.has_base(base.fullname):
+            continue
+        elif base.has_base(candidate.fullname):
+            candidate = base
+        else:
+            return False
+    return True
diff --git a/mypy/types.py b/mypy/types.py
index 26c5b474ba6c..4fa8b8e64703 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -119,9 +119,12 @@
 # Supported Unpack type names.
 UNPACK_TYPE_NAMES: Final = ("typing.Unpack", "typing_extensions.Unpack")
 
-# Supported @deprecated type names
+# Supported @deprecated decorator names
 DEPRECATED_TYPE_NAMES: Final = ("warnings.deprecated", "typing_extensions.deprecated")
 
+# Supported @disjoint_base decorator names
+DISJOINT_BASE_DECORATOR_NAMES: Final = ("typing.disjoint_base", "typing_extensions.disjoint_base")
+
 # We use this constant in various places when checking `tuple` subtyping:
 TUPLE_LIKE_INSTANCE_NAMES: Final = (
     "builtins.tuple",
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 23dbe2bc07af..5cc4910fb265 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -6084,7 +6084,7 @@ class B(A):
     __slots__ = ('a', 'b')
 class C:
     __slots__ = ('x',)
-class D(B, C):
+class D(B, C):  # E: Class "D" has incompatible disjoint bases
     __slots__ = ('aa', 'bb', 'cc')
 [builtins fixtures/tuple.pyi]
 
diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test
index d23199dc8b33..e3fc4614fc06 100644
--- a/test-data/unit/check-final.test
+++ b/test-data/unit/check-final.test
@@ -1272,3 +1272,67 @@ if FOO is not None:
 
     def func() -> int:
         return FOO
+
+[case testDisjointBase]
+from typing_extensions import disjoint_base
+
+@disjoint_base
+class Disjoint1: pass
+
+@disjoint_base
+class Disjoint2: pass
+
+@disjoint_base
+class DisjointChild(Disjoint1): pass
+
+class C1: pass
+class C2(Disjoint1, C1): pass
+class C3(DisjointChild, Disjoint1): pass
+
+class C4(Disjoint1, Disjoint2):  # E: Class "C4" has incompatible disjoint bases
+    pass
+
+class C5(Disjoint2, Disjoint1):  # E: Class "C5" has incompatible disjoint bases
+    pass
+
+class C6(Disjoint2, DisjointChild):  # E: Class "C6" has incompatible disjoint bases
+    pass
+
+class C7(DisjointChild, Disjoint2):  # E: Class "C7" has incompatible disjoint bases
+    pass
+
+class C8(DisjointChild, Disjoint1, Disjoint2):  # E: Class "C8" has incompatible disjoint bases
+    pass
+
+class C9(C2, Disjoint2):  # E: Class "C9" has incompatible disjoint bases
+    pass
+
+class C10(C3, Disjoint2):  # E: Class "C10" has incompatible disjoint bases
+    pass
+
+[builtins fixtures/tuple.pyi]
+[case testDisjointBaseSlots]
+class S1:
+    __slots__ = ("a",)
+
+class S2:
+    __slots__ = ("b",)
+
+class S3:
+    __slots__ = ()
+
+class S4(S1):
+    __slots__ = ("c",)
+
+class S5(S1, S2):  # E: Class "S5" has incompatible disjoint bases
+    pass
+
+class S6(S1, S3): pass  # OK
+class S7(S3, S1): pass  # OK
+
+class S8(S4, S1): pass  # OK
+
+class S9(S2, S4):  # E: Class "S9" has incompatible disjoint bases
+    pass
+
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 7d791319537f..c3fe98e69d95 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -3170,13 +3170,13 @@ C(5, 'foo', True)
 
 [file a.py]
 import attrs
-@attrs.define
+@attrs.define(slots=False)
 class A:
     a: int
 
 [file b.py]
 import attrs
-@attrs.define
+@attrs.define(slots=False)
 class B:
     b: str
 
@@ -3184,7 +3184,7 @@ class B:
 from a import A
 from b import B
 import attrs
-@attrs.define
+@attrs.define(slots=False)
 class C(A, B):
     c: bool
 
diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test
index 640fc10915d1..5043d5422108 100644
--- a/test-data/unit/check-isinstance.test
+++ b/test-data/unit/check-isinstance.test
@@ -2551,6 +2551,33 @@ def f2(x: T2) -> T2:
         return C()
 [builtins fixtures/isinstance.pyi]
 
+[case testIsInstanceDisjointBase]
+# flags: --warn-unreachable
+from typing_extensions import disjoint_base
+
+@disjoint_base
+class Disjoint1: pass
+@disjoint_base
+class Disjoint2: pass
+@disjoint_base
+class Disjoint3(Disjoint1): pass
+class Child(Disjoint1): pass
+class Unrelated: pass
+
+def f(d1: Disjoint1, u: Unrelated, c: Child) -> object:
+    if isinstance(d1, Disjoint2): # E: Subclass of "Disjoint1" and "Disjoint2" cannot exist: have distinct disjoint bases
+        return u  # E: Statement is unreachable
+    if isinstance(u, Disjoint1):  # OK
+        return d1
+    if isinstance(c, Disjoint3):  # OK
+        return c
+    if isinstance(c, Disjoint2):  # E: Subclass of "Child" and "Disjoint2" cannot exist: have distinct disjoint bases
+        return c  # E: Statement is unreachable
+    return d1
+
+[builtins fixtures/isinstance.pyi]
+
+
 [case testIsInstanceAdHocIntersectionUsage]
 # flags: --warn-unreachable
 class A: pass
diff --git a/test-data/unit/check-slots.test b/test-data/unit/check-slots.test
index 10b664bffb11..25dd630e1cbe 100644
--- a/test-data/unit/check-slots.test
+++ b/test-data/unit/check-slots.test
@@ -180,6 +180,7 @@ b.m = 2
 b.b = 2
 b._two = 2
 [out]
+main:5: error: Class "B" has incompatible disjoint bases
 main:11: error: Trying to assign name "_one" that is not in "__slots__" of type "__main__.B"
 main:16: error: "B" has no attribute "b"
 main:17: error: "B" has no attribute "_two"
diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi
index cb054b0e6b4f..6158a0c9ebbc 100644
--- a/test-data/unit/lib-stub/typing_extensions.pyi
+++ b/test-data/unit/lib-stub/typing_extensions.pyi
@@ -93,5 +93,6 @@ def dataclass_transform(
 
 def override(__arg: _T) -> _T: ...
 def deprecated(__msg: str) -> Callable[[_T], _T]: ...
+def disjoint_base(__arg: _T) -> _T: ...
 
 _FutureFeatureFixture = 0

From 657bdd84cff780a4b96c04525e2693c074a3fbf3 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 19 Aug 2025 20:33:09 +0100
Subject: [PATCH 1563/1617] More efficient (fixed-format) serialization
 (#19668)

This makes deserialization ~2.5x faster compared to `orjson`. This is
fully functional, but still PoC in terms of distribution logic. Some
comments:
* In you want to try this in compiled mode, simply install mypy using
`MYPY_USE_MYPYC=1`, this will install the extension automatically (for
now)
* If you want to just play with the extension, or use it in interpreted
mode, use `pip install mypyc/lib-rt`
* I translated (de-)serialization logic from JSON methods almost
verbatim (including comments)
* This may be still not the most efficient way to do this, but I wanted
to write something simple, that probably still gets us 90% there in
terms of performance. I am still open to suggestions however
* Please forgive me if the PR looks not very polished, I feel tired, but
needed some kind of closure on this :-)

Some technical notes:
* The huge `try/except` import blob in `mypy/cache.py` is temporary, it
is needed for now to be able to run tests without installing mypy itself
(only with `test-requirements.txt`).
* There is certain asymmetry with read/write for literals, this is
intentional because we allow `complex` and/or `None` in some cases, but
not in other cases.
* General convention is that during deserialization the type/symbol
marker is consumer by the caller (except for `MypyFile`, which is
special). There is no convention for few classes that are not
types/symbols.
* I add new primitive type for `native_internal.Buffer` (and possible
more type in future from `native`) for better/automatic method call
specializations. If this feels wrong/risky, I can convert this to a more
ad-hoc logic in `transform_call_expr()`

Related issue: #3456
---
 mypy/build.py                                 |  49 +-
 mypy/cache.py                                 | 153 ++++++
 mypy/fixup.py                                 |   2 +
 mypy/main.py                                  |   3 +
 mypy/modulefinder.py                          |   7 +-
 mypy/nodes.py                                 | 520 +++++++++++++++++-
 mypy/options.py                               |   2 +
 mypy/types.py                                 | 465 +++++++++++++++-
 mypy/typeshed/stubs/mypy-native/METADATA.toml |   1 +
 .../stubs/mypy-native/native_internal.pyi     |  12 +
 mypyc/analysis/ircheck.py                     |   3 +-
 mypyc/build.py                                |  25 +
 mypyc/codegen/emit.py                         |   3 +-
 mypyc/codegen/emitmodule.py                   |   8 +-
 mypyc/ir/rtypes.py                            |   9 +
 mypyc/irbuild/mapper.py                       |   3 +
 mypyc/lib-rt/native_internal.c                | 510 +++++++++++++++++
 mypyc/lib-rt/native_internal.h                |  52 ++
 mypyc/lib-rt/setup.py                         |  96 ++--
 mypyc/options.py                              |   5 +
 mypyc/primitives/misc_ops.py                  |  97 ++++
 mypyc/test-data/irbuild-classes.test          |  49 ++
 mypyc/test-data/run-classes.test              |  68 +++
 mypyc/test/test_external.py                   |   1 +
 mypyc/test/test_run.py                        |   7 +-
 setup.py                                      |   4 +
 test-data/unit/lib-stub/native_internal.pyi   |  12 +
 27 files changed, 2099 insertions(+), 67 deletions(-)
 create mode 100644 mypy/cache.py
 create mode 100644 mypy/typeshed/stubs/mypy-native/METADATA.toml
 create mode 100644 mypy/typeshed/stubs/mypy-native/native_internal.pyi
 create mode 100644 mypyc/lib-rt/native_internal.c
 create mode 100644 mypyc/lib-rt/native_internal.h
 create mode 100644 test-data/unit/lib-stub/native_internal.pyi

diff --git a/mypy/build.py b/mypy/build.py
index 883ae1f22f19..4f22e0703d97 100644
--- a/mypy/build.py
+++ b/mypy/build.py
@@ -40,6 +40,7 @@
 from typing_extensions import TypeAlias as _TypeAlias
 
 import mypy.semanal_main
+from mypy.cache import Buffer
 from mypy.checker import TypeChecker
 from mypy.error_formatter import OUTPUT_CHOICES, ErrorFormatter
 from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error
@@ -1143,6 +1144,17 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta]
     return module_deps_metas
 
 
+def _load_ff_file(file: str, manager: BuildManager, log_error: str) -> bytes | None:
+    t0 = time.time()
+    try:
+        data = manager.metastore.read(file)
+    except OSError:
+        manager.log(log_error + file)
+        return None
+    manager.add_stats(metastore_read_time=time.time() - t0)
+    return data
+
+
 def _load_json_file(
     file: str, manager: BuildManager, log_success: str, log_error: str
 ) -> dict[str, Any] | None:
@@ -1263,7 +1275,11 @@ def get_cache_names(id: str, path: str, options: Options) -> tuple[str, str, str
     deps_json = None
     if options.cache_fine_grained:
         deps_json = prefix + ".deps.json"
-    return (prefix + ".meta.json", prefix + ".data.json", deps_json)
+    if options.fixed_format_cache:
+        data_suffix = ".data.ff"
+    else:
+        data_suffix = ".data.json"
+    return (prefix + ".meta.json", prefix + data_suffix, deps_json)
 
 
 def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | None:
@@ -1563,8 +1579,13 @@ def write_cache(
         tree.path = path
 
     # Serialize data and analyze interface
-    data = tree.serialize()
-    data_bytes = json_dumps(data, manager.options.debug_cache)
+    if manager.options.fixed_format_cache:
+        data_io = Buffer()
+        tree.write(data_io)
+        data_bytes = data_io.getvalue()
+    else:
+        data = tree.serialize()
+        data_bytes = json_dumps(data, manager.options.debug_cache)
     interface_hash = hash_digest(data_bytes)
 
     plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False))
@@ -2089,15 +2110,23 @@ def load_tree(self, temporary: bool = False) -> None:
             self.meta is not None
         ), "Internal error: this method must be called only for cached modules"
 
-        data = _load_json_file(
-            self.meta.data_json, self.manager, "Load tree ", "Could not load tree: "
-        )
+        data: bytes | dict[str, Any] | None
+        if self.options.fixed_format_cache:
+            data = _load_ff_file(self.meta.data_json, self.manager, "Could not load tree: ")
+        else:
+            data = _load_json_file(
+                self.meta.data_json, self.manager, "Load tree ", "Could not load tree: "
+            )
         if data is None:
             return
 
         t0 = time.time()
         # TODO: Assert data file wasn't changed.
-        self.tree = MypyFile.deserialize(data)
+        if isinstance(data, bytes):
+            data_io = Buffer(data)
+            self.tree = MypyFile.read(data_io)
+        else:
+            self.tree = MypyFile.deserialize(data)
         t1 = time.time()
         self.manager.add_stats(deserialize_time=t1 - t0)
         if not temporary:
@@ -2485,7 +2514,11 @@ def write_cache(self) -> None:
         ):
             if self.options.debug_serialize:
                 try:
-                    self.tree.serialize()
+                    if self.manager.options.fixed_format_cache:
+                        data = Buffer()
+                        self.tree.write(data)
+                    else:
+                        self.tree.serialize()
                 except Exception:
                     print(f"Error serializing {self.id}", file=self.manager.stdout)
                     raise  # Propagate to display traceback
diff --git a/mypy/cache.py b/mypy/cache.py
new file mode 100644
index 000000000000..49f568c1f3c1
--- /dev/null
+++ b/mypy/cache.py
@@ -0,0 +1,153 @@
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Final
+
+try:
+    from native_internal import (
+        Buffer as Buffer,
+        read_bool as read_bool,
+        read_float as read_float,
+        read_int as read_int,
+        read_str as read_str,
+        write_bool as write_bool,
+        write_float as write_float,
+        write_int as write_int,
+        write_str as write_str,
+    )
+except ImportError:
+    # TODO: temporary, remove this after we publish mypy-native on PyPI.
+    if not TYPE_CHECKING:
+
+        class Buffer:
+            def __init__(self, source: bytes = b"") -> None:
+                raise NotImplementedError
+
+            def getvalue(self) -> bytes:
+                raise NotImplementedError
+
+        def read_int(data: Buffer) -> int:
+            raise NotImplementedError
+
+        def write_int(data: Buffer, value: int) -> None:
+            raise NotImplementedError
+
+        def read_str(data: Buffer) -> str:
+            raise NotImplementedError
+
+        def write_str(data: Buffer, value: str) -> None:
+            raise NotImplementedError
+
+        def read_bool(data: Buffer) -> bool:
+            raise NotImplementedError
+
+        def write_bool(data: Buffer, value: bool) -> None:
+            raise NotImplementedError
+
+        def read_float(data: Buffer) -> float:
+            raise NotImplementedError
+
+        def write_float(data: Buffer, value: float) -> None:
+            raise NotImplementedError
+
+
+LITERAL_INT: Final = 1
+LITERAL_STR: Final = 2
+LITERAL_BOOL: Final = 3
+LITERAL_FLOAT: Final = 4
+LITERAL_COMPLEX: Final = 5
+LITERAL_NONE: Final = 6
+
+
+def read_literal(data: Buffer, marker: int) -> int | str | bool | float:
+    if marker == LITERAL_INT:
+        return read_int(data)
+    elif marker == LITERAL_STR:
+        return read_str(data)
+    elif marker == LITERAL_BOOL:
+        return read_bool(data)
+    elif marker == LITERAL_FLOAT:
+        return read_float(data)
+    assert False, f"Unknown literal marker {marker}"
+
+
+def write_literal(data: Buffer, value: int | str | bool | float | complex | None) -> None:
+    if isinstance(value, bool):
+        write_int(data, LITERAL_BOOL)
+        write_bool(data, value)
+    elif isinstance(value, int):
+        write_int(data, LITERAL_INT)
+        write_int(data, value)
+    elif isinstance(value, str):
+        write_int(data, LITERAL_STR)
+        write_str(data, value)
+    elif isinstance(value, float):
+        write_int(data, LITERAL_FLOAT)
+        write_float(data, value)
+    elif isinstance(value, complex):
+        write_int(data, LITERAL_COMPLEX)
+        write_float(data, value.real)
+        write_float(data, value.imag)
+    else:
+        write_int(data, LITERAL_NONE)
+
+
+def read_int_opt(data: Buffer) -> int | None:
+    if read_bool(data):
+        return read_int(data)
+    return None
+
+
+def write_int_opt(data: Buffer, value: int | None) -> None:
+    if value is not None:
+        write_bool(data, True)
+        write_int(data, value)
+    else:
+        write_bool(data, False)
+
+
+def read_str_opt(data: Buffer) -> str | None:
+    if read_bool(data):
+        return read_str(data)
+    return None
+
+
+def write_str_opt(data: Buffer, value: str | None) -> None:
+    if value is not None:
+        write_bool(data, True)
+        write_str(data, value)
+    else:
+        write_bool(data, False)
+
+
+def read_int_list(data: Buffer) -> list[int]:
+    size = read_int(data)
+    return [read_int(data) for _ in range(size)]
+
+
+def write_int_list(data: Buffer, value: list[int]) -> None:
+    write_int(data, len(value))
+    for item in value:
+        write_int(data, item)
+
+
+def read_str_list(data: Buffer) -> list[str]:
+    size = read_int(data)
+    return [read_str(data) for _ in range(size)]
+
+
+def write_str_list(data: Buffer, value: Sequence[str]) -> None:
+    write_int(data, len(value))
+    for item in value:
+        write_str(data, item)
+
+
+def read_str_opt_list(data: Buffer) -> list[str | None]:
+    size = read_int(data)
+    return [read_str_opt(data) for _ in range(size)]
+
+
+def write_str_opt_list(data: Buffer, value: list[str | None]) -> None:
+    write_int(data, len(value))
+    for item in value:
+        write_str_opt(data, item)
diff --git a/mypy/fixup.py b/mypy/fixup.py
index 18bdc1c6f497..bec5929ad4b1 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -97,6 +97,8 @@ def visit_type_info(self, info: TypeInfo) -> None:
                 info.declared_metaclass.accept(self.type_fixer)
             if info.metaclass_type:
                 info.metaclass_type.accept(self.type_fixer)
+            if info.self_type:
+                info.self_type.accept(self.type_fixer)
             if info.alt_promote:
                 info.alt_promote.accept(self.type_fixer)
                 instance = Instance(info, [])
diff --git a/mypy/main.py b/mypy/main.py
index fd50c7677a11..0f70eb41bb14 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1056,6 +1056,9 @@ def add_invertible_flag(
         action="store_true",
         help="Include fine-grained dependency information in the cache for the mypy daemon",
     )
+    incremental_group.add_argument(
+        "--fixed-format-cache", action="store_true", help=argparse.SUPPRESS
+    )
     incremental_group.add_argument(
         "--skip-version-check",
         action="store_true",
diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
index d159736078eb..d61c9ee3ec3f 100644
--- a/mypy/modulefinder.py
+++ b/mypy/modulefinder.py
@@ -796,6 +796,7 @@ def default_lib_path(
         custom_typeshed_dir = os.path.abspath(custom_typeshed_dir)
         typeshed_dir = os.path.join(custom_typeshed_dir, "stdlib")
         mypy_extensions_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-extensions")
+        mypy_native_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-native")
         versions_file = os.path.join(typeshed_dir, "VERSIONS")
         if not os.path.isdir(typeshed_dir) or not os.path.isfile(versions_file):
             print(
@@ -811,11 +812,13 @@ def default_lib_path(
             data_dir = auto
         typeshed_dir = os.path.join(data_dir, "typeshed", "stdlib")
         mypy_extensions_dir = os.path.join(data_dir, "typeshed", "stubs", "mypy-extensions")
+        mypy_native_dir = os.path.join(data_dir, "typeshed", "stubs", "mypy-native")
     path.append(typeshed_dir)
 
-    # Get mypy-extensions stubs from typeshed, since we treat it as an
-    # "internal" library, similar to typing and typing-extensions.
+    # Get mypy-extensions and mypy-native stubs from typeshed, since we treat them as
+    # "internal" libraries, similar to typing and typing-extensions.
     path.append(mypy_extensions_dir)
+    path.append(mypy_native_dir)
 
     # Add fallback path that can be used if we have a broken installation.
     if sys.platform != "win32":
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 8c2110b156f1..b9c08f02f316 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import json
 import os
 from abc import abstractmethod
 from collections import defaultdict
@@ -13,6 +14,30 @@
 from mypy_extensions import trait
 
 import mypy.strconv
+from mypy.cache import (
+    LITERAL_COMPLEX,
+    LITERAL_NONE,
+    Buffer,
+    read_bool,
+    read_float,
+    read_int,
+    read_int_list,
+    read_int_opt,
+    read_literal,
+    read_str,
+    read_str_list,
+    read_str_opt,
+    read_str_opt_list,
+    write_bool,
+    write_int,
+    write_int_list,
+    write_int_opt,
+    write_literal,
+    write_str,
+    write_str_list,
+    write_str_opt,
+    write_str_opt_list,
+)
 from mypy.options import Options
 from mypy.util import is_sunder, is_typeshed_file, short_type
 from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor
@@ -240,6 +265,13 @@ def deserialize(cls, data: JsonDict) -> SymbolNode:
             return method(data)
         raise NotImplementedError(f"unexpected .class {classname}")
 
+    def write(self, data: Buffer) -> None:
+        raise NotImplementedError(f"Cannot serialize {self.__class__.__name__} instance")
+
+    @classmethod
+    def read(cls, data: Buffer) -> SymbolNode:
+        raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance")
+
 
 # Items: fullname, related symbol table node, surrounding type (if any)
 Definition: _TypeAlias = tuple[str, "SymbolTableNode", Optional["TypeInfo"]]
@@ -368,7 +400,7 @@ def serialize(self) -> JsonDict:
             "is_stub": self.is_stub,
             "path": self.path,
             "is_partial_stub_package": self.is_partial_stub_package,
-            "future_import_flags": list(self.future_import_flags),
+            "future_import_flags": sorted(self.future_import_flags),
         }
 
     @classmethod
@@ -384,6 +416,28 @@ def deserialize(cls, data: JsonDict) -> MypyFile:
         tree.future_import_flags = set(data["future_import_flags"])
         return tree
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, MYPY_FILE)
+        write_str(data, self._fullname)
+        self.names.write(data, self._fullname)
+        write_bool(data, self.is_stub)
+        write_str(data, self.path)
+        write_bool(data, self.is_partial_stub_package)
+        write_str_list(data, sorted(self.future_import_flags))
+
+    @classmethod
+    def read(cls, data: Buffer) -> MypyFile:
+        assert read_int(data) == MYPY_FILE
+        tree = MypyFile([], [])
+        tree._fullname = read_str(data)
+        tree.names = SymbolTable.read(data)
+        tree.is_stub = read_bool(data)
+        tree.path = read_str(data)
+        tree.is_partial_stub_package = read_bool(data)
+        tree.future_import_flags = set(read_str_list(data))
+        tree.is_cache_skeleton = True
+        return tree
+
 
 class ImportBase(Statement):
     """Base class for all import statements."""
@@ -656,6 +710,41 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef:
         # NOTE: res.info will be set in the fixup phase.
         return res
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, OVERLOADED_FUNC_DEF)
+        write_int(data, len(self.items))
+        for item in self.items:
+            item.write(data)
+        mypy.types.write_type_opt(data, self.type)
+        write_str(data, self._fullname)
+        if self.impl is None:
+            write_bool(data, False)
+        else:
+            write_bool(data, True)
+            self.impl.write(data)
+        write_flags(data, self, FUNCBASE_FLAGS)
+        write_str_opt(data, self.deprecated)
+        write_int_opt(data, self.setter_index)
+
+    @classmethod
+    def read(cls, data: Buffer) -> OverloadedFuncDef:
+        res = OverloadedFuncDef([read_overload_part(data) for _ in range(read_int(data))])
+        typ = mypy.types.read_type_opt(data)
+        if typ is not None:
+            assert isinstance(typ, mypy.types.ProperType)
+            res.type = typ
+        res._fullname = read_str(data)
+        if read_bool(data):
+            res.impl = read_overload_part(data)
+            # set line for empty overload items, as not set in __init__
+            if len(res.items) > 0:
+                res.set_line(res.impl.line)
+        read_flags(data, res, FUNCBASE_FLAGS)
+        res.deprecated = read_str_opt(data)
+        res.setter_index = read_int_opt(data)
+        # NOTE: res.info will be set in the fixup phase.
+        return res
+
     def is_dynamic(self) -> bool:
         return all(item.is_dynamic() for item in self.items)
 
@@ -932,6 +1021,46 @@ def deserialize(cls, data: JsonDict) -> FuncDef:
         del ret.min_args
         return ret
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, FUNC_DEF)
+        write_str(data, self._name)
+        mypy.types.write_type_opt(data, self.type)
+        write_str(data, self._fullname)
+        write_flags(data, self, FUNCDEF_FLAGS)
+        write_str_opt_list(data, self.arg_names)
+        write_int_list(data, [int(ak.value) for ak in self.arg_kinds])
+        write_int(data, self.abstract_status)
+        if self.dataclass_transform_spec is None:
+            write_bool(data, False)
+        else:
+            write_bool(data, True)
+            self.dataclass_transform_spec.write(data)
+        write_str_opt(data, self.deprecated)
+        write_str_opt(data, self.original_first_arg)
+
+    @classmethod
+    def read(cls, data: Buffer) -> FuncDef:
+        name = read_str(data)
+        typ: mypy.types.FunctionLike | None = None
+        if read_bool(data):
+            typ = mypy.types.read_function_like(data)
+        ret = FuncDef(name, [], Block([]), typ)
+        ret._fullname = read_str(data)
+        read_flags(data, ret, FUNCDEF_FLAGS)
+        # NOTE: ret.info is set in the fixup phase.
+        ret.arg_names = read_str_opt_list(data)
+        ret.arg_kinds = [ARG_KINDS[ak] for ak in read_int_list(data)]
+        ret.abstract_status = read_int(data)
+        if read_bool(data):
+            ret.dataclass_transform_spec = DataclassTransformSpec.read(data)
+        ret.deprecated = read_str_opt(data)
+        ret.original_first_arg = read_str_opt(data)
+        # Leave these uninitialized so that future uses will trigger an error
+        del ret.arguments
+        del ret.max_pos
+        del ret.min_args
+        return ret
+
 
 # All types that are both SymbolNodes and FuncBases. See the FuncBase
 # docstring for the rationale.
@@ -1004,6 +1133,22 @@ def deserialize(cls, data: JsonDict) -> Decorator:
         dec.is_overload = data["is_overload"]
         return dec
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, DECORATOR)
+        self.func.write(data)
+        self.var.write(data)
+        write_bool(data, self.is_overload)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Decorator:
+        assert read_int(data) == FUNC_DEF
+        func = FuncDef.read(data)
+        assert read_int(data) == VAR
+        var = Var.read(data)
+        dec = Decorator(func, [], var)
+        dec.is_overload = read_bool(data)
+        return dec
+
     def is_dynamic(self) -> bool:
         return self.func.is_dynamic()
 
@@ -1180,6 +1325,35 @@ def deserialize(cls, data: JsonDict) -> Var:
         v.final_value = data.get("final_value")
         return v
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, VAR)
+        write_str(data, self._name)
+        mypy.types.write_type_opt(data, self.type)
+        mypy.types.write_type_opt(data, self.setter_type)
+        write_str(data, self._fullname)
+        write_flags(data, self, VAR_FLAGS)
+        write_literal(data, self.final_value)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Var:
+        name = read_str(data)
+        typ = mypy.types.read_type_opt(data)
+        v = Var(name, typ)
+        setter_type: mypy.types.CallableType | None = None
+        if read_bool(data):
+            assert read_int(data) == mypy.types.CALLABLE_TYPE
+            setter_type = mypy.types.CallableType.read(data)
+        v.setter_type = setter_type
+        v.is_ready = False  # Override True default set in __init__
+        v._fullname = read_str(data)
+        read_flags(data, v, VAR_FLAGS)
+        marker = read_int(data)
+        if marker == LITERAL_COMPLEX:
+            v.final_value = complex(read_float(data), read_float(data))
+        elif marker != LITERAL_NONE:
+            v.final_value = read_literal(data, marker)
+        return v
+
 
 class ClassDef(Statement):
     """Class definition"""
@@ -1290,6 +1464,22 @@ def deserialize(cls, data: JsonDict) -> ClassDef:
         res.fullname = data["fullname"]
         return res
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, CLASS_DEF)
+        write_str(data, self.name)
+        mypy.types.write_type_list(data, self.type_vars)
+        write_str(data, self.fullname)
+
+    @classmethod
+    def read(cls, data: Buffer) -> ClassDef:
+        res = ClassDef(
+            read_str(data),
+            Block([]),
+            [mypy.types.read_type_var_like(data) for _ in range(read_int(data))],
+        )
+        res.fullname = read_str(data)
+        return res
+
 
 class GlobalDecl(Statement):
     """Declaration global x, y, ..."""
@@ -2707,6 +2897,26 @@ def deserialize(cls, data: JsonDict) -> TypeVarExpr:
             data["variance"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_VAR_EXPR)
+        write_str(data, self._name)
+        write_str(data, self._fullname)
+        mypy.types.write_type_list(data, self.values)
+        self.upper_bound.write(data)
+        self.default.write(data)
+        write_int(data, self.variance)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeVarExpr:
+        return TypeVarExpr(
+            read_str(data),
+            read_str(data),
+            mypy.types.read_type_list(data),
+            mypy.types.read_type(data),
+            mypy.types.read_type(data),
+            read_int(data),
+        )
+
 
 class ParamSpecExpr(TypeVarLikeExpr):
     __slots__ = ()
@@ -2737,6 +2947,24 @@ def deserialize(cls, data: JsonDict) -> ParamSpecExpr:
             data["variance"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, PARAM_SPEC_EXPR)
+        write_str(data, self._name)
+        write_str(data, self._fullname)
+        self.upper_bound.write(data)
+        self.default.write(data)
+        write_int(data, self.variance)
+
+    @classmethod
+    def read(cls, data: Buffer) -> ParamSpecExpr:
+        return ParamSpecExpr(
+            read_str(data),
+            read_str(data),
+            mypy.types.read_type(data),
+            mypy.types.read_type(data),
+            read_int(data),
+        )
+
 
 class TypeVarTupleExpr(TypeVarLikeExpr):
     """Type variable tuple expression TypeVarTuple(...)."""
@@ -2787,6 +3015,28 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleExpr:
             data["variance"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_VAR_TUPLE_EXPR)
+        self.tuple_fallback.write(data)
+        write_str(data, self._name)
+        write_str(data, self._fullname)
+        self.upper_bound.write(data)
+        self.default.write(data)
+        write_int(data, self.variance)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeVarTupleExpr:
+        assert read_int(data) == mypy.types.INSTANCE
+        fallback = mypy.types.Instance.read(data)
+        return TypeVarTupleExpr(
+            read_str(data),
+            read_str(data),
+            mypy.types.read_type(data),
+            fallback,
+            mypy.types.read_type(data),
+            read_int(data),
+        )
+
 
 class TypeAliasExpr(Expression):
     """Type alias expression (rvalue)."""
@@ -3598,7 +3848,6 @@ def deserialize(cls, data: JsonDict) -> TypeInfo:
         module_name = data["module_name"]
         ti = TypeInfo(names, defn, module_name)
         ti._fullname = data["fullname"]
-        # TODO: Is there a reason to reconstruct ti.subtypes?
         ti.abstract_attributes = [(attr[0], attr[1]) for attr in data["abstract_attributes"]]
         ti.type_vars = data["type_vars"]
         ti.has_param_spec_type = data["has_param_spec_type"]
@@ -3658,6 +3907,100 @@ def deserialize(cls, data: JsonDict) -> TypeInfo:
         ti.deprecated = data.get("deprecated")
         return ti
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_INFO)
+        self.names.write(data, self.fullname)
+        self.defn.write(data)
+        write_str(data, self.module_name)
+        write_str(data, self.fullname)
+        write_str_list(data, [a for a, _ in self.abstract_attributes])
+        write_int_list(data, [s for _, s in self.abstract_attributes])
+        write_str_list(data, self.type_vars)
+        write_bool(data, self.has_param_spec_type)
+        mypy.types.write_type_list(data, self.bases)
+        write_str_list(data, [c.fullname for c in self.mro])
+        mypy.types.write_type_list(data, self._promote)
+        mypy.types.write_type_opt(data, self.alt_promote)
+        mypy.types.write_type_opt(data, self.declared_metaclass)
+        mypy.types.write_type_opt(data, self.metaclass_type)
+        mypy.types.write_type_opt(data, self.tuple_type)
+        mypy.types.write_type_opt(data, self.typeddict_type)
+        write_flags(data, self, TypeInfo.FLAGS)
+        write_str(data, json.dumps(self.metadata))
+        if self.slots is None:
+            write_bool(data, False)
+        else:
+            write_bool(data, True)
+            write_str_list(data, sorted(self.slots))
+        write_str_list(data, self.deletable_attributes)
+        mypy.types.write_type_opt(data, self.self_type)
+        if self.dataclass_transform_spec is None:
+            write_bool(data, False)
+        else:
+            write_bool(data, True)
+            self.dataclass_transform_spec.write(data)
+        write_str_opt(data, self.deprecated)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeInfo:
+        names = SymbolTable.read(data)
+        assert read_int(data) == CLASS_DEF
+        defn = ClassDef.read(data)
+        module_name = read_str(data)
+        ti = TypeInfo(names, defn, module_name)
+        ti._fullname = read_str(data)
+        attrs = read_str_list(data)
+        statuses = read_int_list(data)
+        ti.abstract_attributes = list(zip(attrs, statuses))
+        ti.type_vars = read_str_list(data)
+        ti.has_param_spec_type = read_bool(data)
+        num_bases = read_int(data)
+        ti.bases = []
+        for _ in range(num_bases):
+            assert read_int(data) == mypy.types.INSTANCE
+            ti.bases.append(mypy.types.Instance.read(data))
+        # NOTE: ti.mro will be set in the fixup phase based on these
+        # names.  The reason we need to store the mro instead of just
+        # recomputing it from base classes has to do with a subtle
+        # point about fine-grained incremental: the cache files might
+        # not be loaded until after a class in the mro has changed its
+        # bases, which causes the mro to change. If we recomputed our
+        # mro, we would compute the *new* mro, which leaves us with no
+        # way to detect that the mro has changed! Thus, we need to make
+        # sure to load the original mro so that once the class is
+        # rechecked, it can tell that the mro has changed.
+        ti._mro_refs = read_str_list(data)
+        ti._promote = cast(list[mypy.types.ProperType], mypy.types.read_type_list(data))
+        if read_bool(data):
+            assert read_int(data) == mypy.types.INSTANCE
+            ti.alt_promote = mypy.types.Instance.read(data)
+        if read_bool(data):
+            assert read_int(data) == mypy.types.INSTANCE
+            ti.declared_metaclass = mypy.types.Instance.read(data)
+        if read_bool(data):
+            assert read_int(data) == mypy.types.INSTANCE
+            ti.metaclass_type = mypy.types.Instance.read(data)
+        if read_bool(data):
+            assert read_int(data) == mypy.types.TUPLE_TYPE
+            ti.tuple_type = mypy.types.TupleType.read(data)
+        if read_bool(data):
+            assert read_int(data) == mypy.types.TYPED_DICT_TYPE
+            ti.typeddict_type = mypy.types.TypedDictType.read(data)
+        read_flags(data, ti, TypeInfo.FLAGS)
+        metadata = read_str(data)
+        if metadata != "{}":
+            ti.metadata = json.loads(metadata)
+        if read_bool(data):
+            ti.slots = set(read_str_list(data))
+        ti.deletable_attributes = read_str_list(data)
+        if read_bool(data):
+            assert read_int(data) == mypy.types.TYPE_VAR_TYPE
+            ti.self_type = mypy.types.TypeVarType.read(data)
+        if read_bool(data):
+            ti.dataclass_transform_spec = DataclassTransformSpec.read(data)
+        ti.deprecated = read_str_opt(data)
+        return ti
+
 
 class FakeInfo(TypeInfo):
     __slots__ = ("msg",)
@@ -3886,6 +4229,9 @@ def fullname(self) -> str:
     def has_param_spec_type(self) -> bool:
         return any(isinstance(v, mypy.types.ParamSpecType) for v in self.alias_tvars)
 
+    def accept(self, visitor: NodeVisitor[T]) -> T:
+        return visitor.visit_type_alias(self)
+
     def serialize(self) -> JsonDict:
         data: JsonDict = {
             ".class": "TypeAlias",
@@ -3900,9 +4246,6 @@ def serialize(self) -> JsonDict:
         }
         return data
 
-    def accept(self, visitor: NodeVisitor[T]) -> T:
-        return visitor.visit_type_alias(self)
-
     @classmethod
     def deserialize(cls, data: JsonDict) -> TypeAlias:
         assert data[".class"] == "TypeAlias"
@@ -3926,6 +4269,33 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
             python_3_12_type_alias=python_3_12_type_alias,
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_ALIAS)
+        write_str(data, self._fullname)
+        self.target.write(data)
+        mypy.types.write_type_list(data, self.alias_tvars)
+        write_int(data, self.line)
+        write_int(data, self.column)
+        write_bool(data, self.no_args)
+        write_bool(data, self.normalized)
+        write_bool(data, self.python_3_12_type_alias)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeAlias:
+        fullname = read_str(data)
+        target = mypy.types.read_type(data)
+        alias_tvars = [mypy.types.read_type_var_like(data) for _ in range(read_int(data))]
+        return TypeAlias(
+            target,
+            fullname,
+            read_int(data),
+            read_int(data),
+            alias_tvars=alias_tvars,
+            no_args=read_bool(data),
+            normalized=read_bool(data),
+            python_3_12_type_alias=read_bool(data),
+        )
+
 
 class PlaceholderNode(SymbolNode):
     """Temporary symbol node that will later become a real SymbolNode.
@@ -4184,6 +4554,49 @@ def deserialize(cls, data: JsonDict) -> SymbolTableNode:
             stnode.plugin_generated = data["plugin_generated"]
         return stnode
 
+    def write(self, data: Buffer, prefix: str, name: str) -> None:
+        write_int(data, self.kind)
+        write_bool(data, self.module_hidden)
+        write_bool(data, self.module_public)
+        write_bool(data, self.implicit)
+        write_bool(data, self.plugin_generated)
+
+        cross_ref = None
+        if isinstance(self.node, MypyFile):
+            cross_ref = self.node.fullname
+        else:
+            assert self.node is not None, f"{prefix}:{name}"
+            if prefix is not None:
+                fullname = self.node.fullname
+                if (
+                    "." in fullname
+                    and fullname != prefix + "." + name
+                    and not (isinstance(self.node, Var) and self.node.from_module_getattr)
+                ):
+                    assert not isinstance(
+                        self.node, PlaceholderNode
+                    ), f"Definition of {fullname} is unexpectedly incomplete"
+                    cross_ref = fullname
+
+        write_str_opt(data, cross_ref)
+        if cross_ref is None:
+            assert self.node is not None
+            self.node.write(data)
+
+    @classmethod
+    def read(cls, data: Buffer) -> SymbolTableNode:
+        sym = SymbolTableNode(read_int(data), None)
+        sym.module_hidden = read_bool(data)
+        sym.module_public = read_bool(data)
+        sym.implicit = read_bool(data)
+        sym.plugin_generated = read_bool(data)
+        cross_ref = read_str_opt(data)
+        if cross_ref is None:
+            sym.node = read_symbol(data)
+        else:
+            sym.cross_ref = cross_ref
+        return sym
+
 
 class SymbolTable(dict[str, SymbolTableNode]):
     """Static representation of a namespace dictionary.
@@ -4235,6 +4648,29 @@ def deserialize(cls, data: JsonDict) -> SymbolTable:
                 st[key] = SymbolTableNode.deserialize(value)
         return st
 
+    def write(self, data: Buffer, fullname: str) -> None:
+        size = 0
+        for key, value in self.items():
+            # Skip __builtins__: it's a reference to the builtins
+            # module that gets added to every module by
+            # SemanticAnalyzerPass2.visit_file(), but it shouldn't be
+            # accessed by users of the module.
+            if key == "__builtins__" or value.no_serialize:
+                continue
+            size += 1
+        write_int(data, size)
+        for key in sorted(self):
+            value = self[key]
+            if key == "__builtins__" or value.no_serialize:
+                continue
+            write_str(data, key)
+            value.write(data, fullname, key)
+
+    @classmethod
+    def read(cls, data: Buffer) -> SymbolTable:
+        size = read_int(data)
+        return SymbolTable([(read_str(data), SymbolTableNode.read(data)) for _ in range(size)])
+
 
 class DataclassTransformSpec:
     """Specifies how a dataclass-like transform should be applied. The fields here are based on the
@@ -4285,6 +4721,23 @@ def deserialize(cls, data: JsonDict) -> DataclassTransformSpec:
             field_specifiers=tuple(data.get("field_specifiers", [])),
         )
 
+    def write(self, data: Buffer) -> None:
+        write_bool(data, self.eq_default)
+        write_bool(data, self.order_default)
+        write_bool(data, self.kw_only_default)
+        write_bool(data, self.frozen_default)
+        write_str_list(data, self.field_specifiers)
+
+    @classmethod
+    def read(cls, data: Buffer) -> DataclassTransformSpec:
+        return DataclassTransformSpec(
+            eq_default=read_bool(data),
+            order_default=read_bool(data),
+            kw_only_default=read_bool(data),
+            frozen_default=read_bool(data),
+            field_specifiers=tuple(read_str_list(data)),
+        )
+
 
 def get_flags(node: Node, names: list[str]) -> list[str]:
     return [name for name in names if getattr(node, name)]
@@ -4295,6 +4748,17 @@ def set_flags(node: Node, flags: list[str]) -> None:
         setattr(node, name, True)
 
 
+def write_flags(data: Buffer, node: SymbolNode, flags: list[str]) -> None:
+    for flag in flags:
+        write_bool(data, getattr(node, flag))
+
+
+def read_flags(data: Buffer, node: SymbolNode, flags: list[str]) -> None:
+    for flag in flags:
+        if read_bool(data):
+            setattr(node, flag, True)
+
+
 def get_member_expr_fullname(expr: MemberExpr) -> str | None:
     """Return the qualified name representation of a member expression.
 
@@ -4410,3 +4874,49 @@ def local_definitions(
             yield fullname, symnode, info
             if isinstance(node, TypeInfo):
                 yield from local_definitions(node.names, fullname, node)
+
+
+MYPY_FILE: Final = 0
+OVERLOADED_FUNC_DEF: Final = 1
+FUNC_DEF: Final = 2
+DECORATOR: Final = 3
+VAR: Final = 4
+TYPE_VAR_EXPR: Final = 5
+PARAM_SPEC_EXPR: Final = 6
+TYPE_VAR_TUPLE_EXPR: Final = 7
+TYPE_INFO: Final = 8
+TYPE_ALIAS: Final = 9
+CLASS_DEF: Final = 10
+
+
+def read_symbol(data: Buffer) -> mypy.nodes.SymbolNode:
+    marker = read_int(data)
+    # The branches here are ordered manually by type "popularity".
+    if marker == VAR:
+        return mypy.nodes.Var.read(data)
+    if marker == FUNC_DEF:
+        return mypy.nodes.FuncDef.read(data)
+    if marker == DECORATOR:
+        return mypy.nodes.Decorator.read(data)
+    if marker == TYPE_INFO:
+        return mypy.nodes.TypeInfo.read(data)
+    if marker == OVERLOADED_FUNC_DEF:
+        return mypy.nodes.OverloadedFuncDef.read(data)
+    if marker == TYPE_VAR_EXPR:
+        return mypy.nodes.TypeVarExpr.read(data)
+    if marker == TYPE_ALIAS:
+        return mypy.nodes.TypeAlias.read(data)
+    if marker == PARAM_SPEC_EXPR:
+        return mypy.nodes.ParamSpecExpr.read(data)
+    if marker == TYPE_VAR_TUPLE_EXPR:
+        return mypy.nodes.TypeVarTupleExpr.read(data)
+    assert False, f"Unknown symbol marker {marker}"
+
+
+def read_overload_part(data: Buffer) -> OverloadPart:
+    marker = read_int(data)
+    if marker == DECORATOR:
+        return Decorator.read(data)
+    if marker == FUNC_DEF:
+        return FuncDef.read(data)
+    assert False, f"Invalid marker for an OverloadPart {marker}"
diff --git a/mypy/options.py b/mypy/options.py
index 6d7eca772888..ad4b26cca095 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -72,6 +72,7 @@ class BuildType:
         "disable_bytearray_promotion",
         "disable_memoryview_promotion",
         "strict_bytes",
+        "fixed_format_cache",
     }
 ) - {"debug_cache"}
 
@@ -286,6 +287,7 @@ def __init__(self) -> None:
         self.incremental = True
         self.cache_dir = defaults.CACHE_DIR
         self.sqlite_cache = False
+        self.fixed_format_cache = False
         self.debug_cache = False
         self.skip_version_check = False
         self.skip_cache_mtime_checks = False
diff --git a/mypy/types.py b/mypy/types.py
index 4fa8b8e64703..b48e0ef4d985 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -10,6 +10,25 @@
 
 import mypy.nodes
 from mypy.bogus_type import Bogus
+from mypy.cache import (
+    Buffer,
+    read_bool,
+    read_int,
+    read_int_list,
+    read_literal,
+    read_str,
+    read_str_list,
+    read_str_opt,
+    read_str_opt_list,
+    write_bool,
+    write_int,
+    write_int_list,
+    write_literal,
+    write_str,
+    write_str_list,
+    write_str_opt,
+    write_str_opt_list,
+)
 from mypy.nodes import ARG_KINDS, ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, SymbolNode
 from mypy.options import Options
 from mypy.state import state
@@ -276,6 +295,13 @@ def serialize(self) -> JsonDict | str:
     def deserialize(cls, data: JsonDict) -> Type:
         raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance")
 
+    def write(self, data: Buffer) -> None:
+        raise NotImplementedError(f"Cannot serialize {self.__class__.__name__} instance")
+
+    @classmethod
+    def read(cls, data: Buffer) -> Type:
+        raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance")
+
     def is_singleton_type(self) -> bool:
         return False
 
@@ -391,6 +417,11 @@ def can_be_false_default(self) -> bool:
             return self.alias.target.can_be_false
         return super().can_be_false_default()
 
+    def copy_modified(self, *, args: list[Type] | None = None) -> TypeAliasType:
+        return TypeAliasType(
+            self.alias, args if args is not None else self.args.copy(), self.line, self.column
+        )
+
     def accept(self, visitor: TypeVisitor[T]) -> T:
         return visitor.visit_type_alias_type(self)
 
@@ -424,10 +455,17 @@ def deserialize(cls, data: JsonDict) -> TypeAliasType:
         alias.type_ref = data["type_ref"]
         return alias
 
-    def copy_modified(self, *, args: list[Type] | None = None) -> TypeAliasType:
-        return TypeAliasType(
-            self.alias, args if args is not None else self.args.copy(), self.line, self.column
-        )
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_ALIAS_TYPE)
+        write_type_list(data, self.args)
+        assert self.alias is not None
+        write_str(data, self.alias.fullname)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeAliasType:
+        alias = TypeAliasType(None, read_type_list(data))
+        alias.type_ref = read_str(data)
+        return alias
 
 
 class TypeGuardedType(Type):
@@ -696,6 +734,29 @@ def deserialize(cls, data: JsonDict) -> TypeVarType:
             variance=data["variance"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_VAR_TYPE)
+        write_str(data, self.name)
+        write_str(data, self.fullname)
+        write_int(data, self.id.raw_id)
+        write_str(data, self.id.namespace)
+        write_type_list(data, self.values)
+        self.upper_bound.write(data)
+        self.default.write(data)
+        write_int(data, self.variance)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeVarType:
+        return TypeVarType(
+            read_str(data),
+            read_str(data),
+            TypeVarId(read_int(data), namespace=read_str(data)),
+            read_type_list(data),
+            read_type(data),
+            read_type(data),
+            read_int(data),
+        )
+
 
 class ParamSpecFlavor:
     # Simple ParamSpec reference such as "P"
@@ -825,6 +886,31 @@ def deserialize(cls, data: JsonDict) -> ParamSpecType:
             prefix=Parameters.deserialize(data["prefix"]),
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, PARAM_SPEC_TYPE)
+        self.prefix.write(data)
+        write_str(data, self.name)
+        write_str(data, self.fullname)
+        write_int(data, self.id.raw_id)
+        write_str(data, self.id.namespace)
+        write_int(data, self.flavor)
+        self.upper_bound.write(data)
+        self.default.write(data)
+
+    @classmethod
+    def read(cls, data: Buffer) -> ParamSpecType:
+        assert read_int(data) == PARAMETERS
+        prefix = Parameters.read(data)
+        return ParamSpecType(
+            read_str(data),
+            read_str(data),
+            TypeVarId(read_int(data), namespace=read_str(data)),
+            read_int(data),
+            read_type(data),
+            read_type(data),
+            prefix=prefix,
+        )
+
 
 class TypeVarTupleType(TypeVarLikeType):
     """Type that refers to a TypeVarTuple.
@@ -880,6 +966,31 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType:
             min_len=data["min_len"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_VAR_TUPLE_TYPE)
+        self.tuple_fallback.write(data)
+        write_str(data, self.name)
+        write_str(data, self.fullname)
+        write_int(data, self.id.raw_id)
+        write_str(data, self.id.namespace)
+        self.upper_bound.write(data)
+        self.default.write(data)
+        write_int(data, self.min_len)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypeVarTupleType:
+        assert read_int(data) == INSTANCE
+        fallback = Instance.read(data)
+        return TypeVarTupleType(
+            read_str(data),
+            read_str(data),
+            TypeVarId(read_int(data), namespace=read_str(data)),
+            read_type(data),
+            fallback,
+            read_type(data),
+            min_len=read_int(data),
+        )
+
     def accept(self, visitor: TypeVisitor[T]) -> T:
         return visitor.visit_type_var_tuple(self)
 
@@ -1011,6 +1122,22 @@ def deserialize(cls, data: JsonDict) -> UnboundType:
             original_str_fallback=data["expr_fallback"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, UNBOUND_TYPE)
+        write_str(data, self.name)
+        write_type_list(data, self.args)
+        write_str_opt(data, self.original_str_expr)
+        write_str_opt(data, self.original_str_fallback)
+
+    @classmethod
+    def read(cls, data: Buffer) -> UnboundType:
+        return UnboundType(
+            read_str(data),
+            read_type_list(data),
+            original_str_expr=read_str_opt(data),
+            original_str_fallback=read_str_opt(data),
+        )
+
 
 class CallableArgument(ProperType):
     """Represents a Arg(type, 'name') inside a Callable's type list.
@@ -1105,6 +1232,14 @@ def accept(self, visitor: TypeVisitor[T]) -> T:
     def serialize(self) -> JsonDict:
         return {".class": "UnpackType", "type": self.type.serialize()}
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, UNPACK_TYPE)
+        self.type.write(data)
+
+    @classmethod
+    def read(cls, data: Buffer) -> UnpackType:
+        return UnpackType(read_type(data))
+
     @classmethod
     def deserialize(cls, data: JsonDict) -> UnpackType:
         assert data[".class"] == "UnpackType"
@@ -1206,6 +1341,21 @@ def deserialize(cls, data: JsonDict) -> AnyType:
             data["missing_import_name"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, ANY_TYPE)
+        write_type_opt(data, self.source_any)
+        write_int(data, self.type_of_any)
+        write_str_opt(data, self.missing_import_name)
+
+    @classmethod
+    def read(cls, data: Buffer) -> AnyType:
+        if read_bool(data):
+            assert read_int(data) == ANY_TYPE
+            source_any = AnyType.read(data)
+        else:
+            source_any = None
+        return AnyType(read_int(data), source_any, read_str_opt(data))
+
 
 class UninhabitedType(ProperType):
     """This type has no members.
@@ -1252,6 +1402,13 @@ def deserialize(cls, data: JsonDict) -> UninhabitedType:
         assert data[".class"] == "UninhabitedType"
         return UninhabitedType()
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, UNINHABITED_TYPE)
+
+    @classmethod
+    def read(cls, data: Buffer) -> UninhabitedType:
+        return UninhabitedType()
+
 
 class NoneType(ProperType):
     """The type of 'None'.
@@ -1284,6 +1441,13 @@ def deserialize(cls, data: JsonDict) -> NoneType:
         assert data[".class"] == "NoneType"
         return NoneType()
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, NONE_TYPE)
+
+    @classmethod
+    def read(cls, data: Buffer) -> NoneType:
+        return NoneType()
+
     def is_singleton_type(self) -> bool:
         return True
 
@@ -1331,6 +1495,14 @@ def deserialize(cls, data: JsonDict) -> DeletedType:
         assert data[".class"] == "DeletedType"
         return DeletedType(data["source"])
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, DELETED_TYPE)
+        write_str_opt(data, self.source)
+
+    @classmethod
+    def read(cls, data: Buffer) -> DeletedType:
+        return DeletedType(read_str_opt(data))
+
 
 # Fake TypeInfo to be used as a placeholder during Instance de-serialization.
 NOT_READY: Final = mypy.nodes.FakeInfo("De-serialization failure: TypeInfo not fixed")
@@ -1373,7 +1545,7 @@ def serialize(self) -> JsonDict:
         return {
             ".class": "ExtraAttrs",
             "attrs": {k: v.serialize() for k, v in self.attrs.items()},
-            "immutable": list(self.immutable),
+            "immutable": sorted(self.immutable),
             "mod_name": self.mod_name,
         }
 
@@ -1386,6 +1558,15 @@ def deserialize(cls, data: JsonDict) -> ExtraAttrs:
             data["mod_name"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_type_map(data, self.attrs)
+        write_str_list(data, sorted(self.immutable))
+        write_str_opt(data, self.mod_name)
+
+    @classmethod
+    def read(cls, data: Buffer) -> ExtraAttrs:
+        return ExtraAttrs(read_type_map(data), set(read_str_list(data)), read_str_opt(data))
+
 
 class Instance(ProperType):
     """An instance type of form C[T1, ..., Tn].
@@ -1522,6 +1703,29 @@ def deserialize(cls, data: JsonDict | str) -> Instance:
             inst.extra_attrs = ExtraAttrs.deserialize(data["extra_attrs"])
         return inst
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, INSTANCE)
+        write_str(data, self.type.fullname)
+        write_type_list(data, self.args)
+        write_type_opt(data, self.last_known_value)
+        if self.extra_attrs is None:
+            write_bool(data, False)
+        else:
+            write_bool(data, True)
+            self.extra_attrs.write(data)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Instance:
+        type_ref = read_str(data)
+        inst = Instance(NOT_READY, read_type_list(data))
+        inst.type_ref = type_ref
+        if read_bool(data):
+            assert read_int(data) == LITERAL_TYPE
+            inst.last_known_value = LiteralType.read(data)
+        if read_bool(data):
+            inst.extra_attrs = ExtraAttrs.read(data)
+        return inst
+
     def copy_modified(
         self,
         *,
@@ -1798,6 +2002,26 @@ def deserialize(cls, data: JsonDict) -> Parameters:
             imprecise_arg_kinds=data["imprecise_arg_kinds"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, PARAMETERS)
+        write_type_list(data, self.arg_types)
+        write_int_list(data, [int(x.value) for x in self.arg_kinds])
+        write_str_opt_list(data, self.arg_names)
+        write_type_list(data, self.variables)
+        write_bool(data, self.imprecise_arg_kinds)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Parameters:
+        return Parameters(
+            read_type_list(data),
+            # This is a micro-optimization until mypyc gets dedicated enum support. Otherwise,
+            # we would spend ~20% of types deserialization time in Enum.__call__().
+            [ARG_KINDS[ak] for ak in read_int_list(data)],
+            read_str_opt_list(data),
+            variables=[read_type_var_like(data) for _ in range(read_int(data))],
+            imprecise_arg_kinds=read_bool(data),
+        )
+
     def __hash__(self) -> int:
         return hash(
             (
@@ -2300,6 +2524,46 @@ def deserialize(cls, data: JsonDict) -> CallableType:
             unpack_kwargs=data["unpack_kwargs"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, CALLABLE_TYPE)
+        self.fallback.write(data)
+        write_type_list(data, self.arg_types)
+        write_int_list(data, [int(x.value) for x in self.arg_kinds])
+        write_str_opt_list(data, self.arg_names)
+        self.ret_type.write(data)
+        write_str_opt(data, self.name)
+        write_type_list(data, self.variables)
+        write_bool(data, self.is_ellipsis_args)
+        write_bool(data, self.implicit)
+        write_bool(data, self.is_bound)
+        write_type_opt(data, self.type_guard)
+        write_type_opt(data, self.type_is)
+        write_bool(data, self.from_concatenate)
+        write_bool(data, self.imprecise_arg_kinds)
+        write_bool(data, self.unpack_kwargs)
+
+    @classmethod
+    def read(cls, data: Buffer) -> CallableType:
+        assert read_int(data) == INSTANCE
+        fallback = Instance.read(data)
+        return CallableType(
+            read_type_list(data),
+            [ARG_KINDS[ak] for ak in read_int_list(data)],
+            read_str_opt_list(data),
+            read_type(data),
+            fallback,
+            name=read_str_opt(data),
+            variables=[read_type_var_like(data) for _ in range(read_int(data))],
+            is_ellipsis_args=read_bool(data),
+            implicit=read_bool(data),
+            is_bound=read_bool(data),
+            type_guard=read_type_opt(data),
+            type_is=read_type_opt(data),
+            from_concatenate=read_bool(data),
+            imprecise_arg_kinds=read_bool(data),
+            unpack_kwargs=read_bool(data),
+        )
+
 
 # This is a little safety net to prevent reckless special-casing of callables
 # that can potentially break Unpack[...] with **kwargs.
@@ -2375,6 +2639,19 @@ def deserialize(cls, data: JsonDict) -> Overloaded:
         assert data[".class"] == "Overloaded"
         return Overloaded([CallableType.deserialize(t) for t in data["items"]])
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, OVERLOADED)
+        write_type_list(data, self.items)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Overloaded:
+        items = []
+        num_overloads = read_int(data)
+        for _ in range(num_overloads):
+            assert read_int(data) == CALLABLE_TYPE
+            items.append(CallableType.read(data))
+        return Overloaded(items)
+
 
 class TupleType(ProperType):
     """The tuple type Tuple[T1, ..., Tn] (at least one type argument).
@@ -2471,6 +2748,18 @@ def deserialize(cls, data: JsonDict) -> TupleType:
             implicit=data["implicit"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TUPLE_TYPE)
+        self.partial_fallback.write(data)
+        write_type_list(data, self.items)
+        write_bool(data, self.implicit)
+
+    @classmethod
+    def read(cls, data: Buffer) -> TupleType:
+        assert read_int(data) == INSTANCE
+        fallback = Instance.read(data)
+        return TupleType(read_type_list(data), fallback, implicit=read_bool(data))
+
     def copy_modified(
         self, *, fallback: Instance | None = None, items: list[Type] | None = None
     ) -> TupleType:
@@ -2641,6 +2930,21 @@ def deserialize(cls, data: JsonDict) -> TypedDictType:
             Instance.deserialize(data["fallback"]),
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPED_DICT_TYPE)
+        self.fallback.write(data)
+        write_type_map(data, self.items)
+        write_str_list(data, sorted(self.required_keys))
+        write_str_list(data, sorted(self.readonly_keys))
+
+    @classmethod
+    def read(cls, data: Buffer) -> TypedDictType:
+        assert read_int(data) == INSTANCE
+        fallback = Instance.read(data)
+        return TypedDictType(
+            read_type_map(data), set(read_str_list(data)), set(read_str_list(data)), fallback
+        )
+
     @property
     def is_final(self) -> bool:
         return self.fallback.type.is_final
@@ -2889,6 +3193,18 @@ def deserialize(cls, data: JsonDict) -> LiteralType:
         assert data[".class"] == "LiteralType"
         return LiteralType(value=data["value"], fallback=Instance.deserialize(data["fallback"]))
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, LITERAL_TYPE)
+        self.fallback.write(data)
+        write_literal(data, self.value)
+
+    @classmethod
+    def read(cls, data: Buffer) -> LiteralType:
+        assert read_int(data) == INSTANCE
+        fallback = Instance.read(data)
+        marker = read_int(data)
+        return LiteralType(read_literal(data, marker), fallback)
+
     def is_singleton_type(self) -> bool:
         return self.is_enum_literal() or isinstance(self.value, bool)
 
@@ -2990,6 +3306,15 @@ def deserialize(cls, data: JsonDict) -> UnionType:
             uses_pep604_syntax=data["uses_pep604_syntax"],
         )
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, UNION_TYPE)
+        write_type_list(data, self.items)
+        write_bool(data, self.uses_pep604_syntax)
+
+    @classmethod
+    def read(cls, data: Buffer) -> UnionType:
+        return UnionType(read_type_list(data), uses_pep604_syntax=read_bool(data))
+
 
 class PartialType(ProperType):
     """Type such as List[?] where type arguments are unknown, or partial None type.
@@ -3126,6 +3451,14 @@ def deserialize(cls, data: JsonDict) -> Type:
         assert data[".class"] == "TypeType"
         return TypeType.make_normalized(deserialize_type(data["item"]))
 
+    def write(self, data: Buffer) -> None:
+        write_int(data, TYPE_TYPE)
+        self.item.write(data)
+
+    @classmethod
+    def read(cls, data: Buffer) -> Type:
+        return TypeType.make_normalized(read_type(data))
+
 
 class PlaceholderType(ProperType):
     """Temporary, yet-unknown type during semantic analysis.
@@ -3786,6 +4119,128 @@ def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]:
     return tuple(args)
 
 
+TYPE_ALIAS_TYPE: Final = 1
+TYPE_VAR_TYPE: Final = 2
+PARAM_SPEC_TYPE: Final = 3
+TYPE_VAR_TUPLE_TYPE: Final = 4
+UNBOUND_TYPE: Final = 5
+UNPACK_TYPE: Final = 6
+ANY_TYPE: Final = 7
+UNINHABITED_TYPE: Final = 8
+NONE_TYPE: Final = 9
+DELETED_TYPE: Final = 10
+INSTANCE: Final = 11
+CALLABLE_TYPE: Final = 12
+OVERLOADED: Final = 13
+TUPLE_TYPE: Final = 14
+TYPED_DICT_TYPE: Final = 15
+LITERAL_TYPE: Final = 16
+UNION_TYPE: Final = 17
+TYPE_TYPE: Final = 18
+PARAMETERS: Final = 19
+
+
+def read_type(data: Buffer) -> Type:
+    marker = read_int(data)
+    # The branches here are ordered manually by type "popularity".
+    if marker == INSTANCE:
+        return Instance.read(data)
+    if marker == ANY_TYPE:
+        return AnyType.read(data)
+    if marker == TYPE_VAR_TYPE:
+        return TypeVarType.read(data)
+    if marker == CALLABLE_TYPE:
+        return CallableType.read(data)
+    if marker == NONE_TYPE:
+        return NoneType.read(data)
+    if marker == UNION_TYPE:
+        return UnionType.read(data)
+    if marker == LITERAL_TYPE:
+        return LiteralType.read(data)
+    if marker == TYPE_ALIAS_TYPE:
+        return TypeAliasType.read(data)
+    if marker == TUPLE_TYPE:
+        return TupleType.read(data)
+    if marker == TYPED_DICT_TYPE:
+        return TypedDictType.read(data)
+    if marker == TYPE_TYPE:
+        return TypeType.read(data)
+    if marker == OVERLOADED:
+        return Overloaded.read(data)
+    if marker == PARAM_SPEC_TYPE:
+        return ParamSpecType.read(data)
+    if marker == TYPE_VAR_TUPLE_TYPE:
+        return TypeVarTupleType.read(data)
+    if marker == UNPACK_TYPE:
+        return UnpackType.read(data)
+    if marker == PARAMETERS:
+        return Parameters.read(data)
+    if marker == UNINHABITED_TYPE:
+        return UninhabitedType.read(data)
+    if marker == UNBOUND_TYPE:
+        return UnboundType.read(data)
+    if marker == DELETED_TYPE:
+        return DeletedType.read(data)
+    assert False, f"Unknown type marker {marker}"
+
+
+def read_function_like(data: Buffer) -> FunctionLike:
+    marker = read_int(data)
+    if marker == CALLABLE_TYPE:
+        return CallableType.read(data)
+    if marker == OVERLOADED:
+        return Overloaded.read(data)
+    assert False, f"Invalid type marker for FunctionLike {marker}"
+
+
+def read_type_var_like(data: Buffer) -> TypeVarLikeType:
+    marker = read_int(data)
+    if marker == TYPE_VAR_TYPE:
+        return TypeVarType.read(data)
+    if marker == PARAM_SPEC_TYPE:
+        return ParamSpecType.read(data)
+    if marker == TYPE_VAR_TUPLE_TYPE:
+        return TypeVarTupleType.read(data)
+    assert False, f"Invalid type marker for TypeVarLikeType {marker}"
+
+
+def read_type_opt(data: Buffer) -> Type | None:
+    if read_bool(data):
+        return read_type(data)
+    return None
+
+
+def write_type_opt(data: Buffer, value: Type | None) -> None:
+    if value is not None:
+        write_bool(data, True)
+        value.write(data)
+    else:
+        write_bool(data, False)
+
+
+def read_type_list(data: Buffer) -> list[Type]:
+    size = read_int(data)
+    return [read_type(data) for _ in range(size)]
+
+
+def write_type_list(data: Buffer, value: Sequence[Type]) -> None:
+    write_int(data, len(value))
+    for item in value:
+        item.write(data)
+
+
+def read_type_map(data: Buffer) -> dict[str, Type]:
+    size = read_int(data)
+    return {read_str(data): read_type(data) for _ in range(size)}
+
+
+def write_type_map(data: Buffer, value: dict[str, Type]) -> None:
+    write_int(data, len(value))
+    for key in sorted(value):
+        write_str(data, key)
+        value[key].write(data)
+
+
 # This cyclic import is unfortunate, but to avoid it we would need to move away all uses
 # of get_proper_type() from types.py. Majority of them have been removed, but few remaining
 # are quite tricky to get rid of, but ultimately we want to do it at some point.
diff --git a/mypy/typeshed/stubs/mypy-native/METADATA.toml b/mypy/typeshed/stubs/mypy-native/METADATA.toml
new file mode 100644
index 000000000000..76574b01cb4b
--- /dev/null
+++ b/mypy/typeshed/stubs/mypy-native/METADATA.toml
@@ -0,0 +1 @@
+version = "0.0.*"
diff --git a/mypy/typeshed/stubs/mypy-native/native_internal.pyi b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
new file mode 100644
index 000000000000..bc1f570a8e9c
--- /dev/null
+++ b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
@@ -0,0 +1,12 @@
+class Buffer:
+    def __init__(self, source: bytes = ...) -> None: ...
+    def getvalue(self) -> bytes: ...
+
+def write_bool(data: Buffer, value: bool) -> None: ...
+def read_bool(data: Buffer) -> bool: ...
+def write_str(data: Buffer, value: str) -> None: ...
+def read_str(data: Buffer) -> str: ...
+def write_float(data: Buffer, value: float) -> None: ...
+def read_float(data: Buffer) -> float: ...
+def write_int(data: Buffer, value: int) -> None: ...
+def read_int(data: Buffer) -> int: ...
diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py
index 4ad2a52c1036..6980c9cee419 100644
--- a/mypyc/analysis/ircheck.py
+++ b/mypyc/analysis/ircheck.py
@@ -56,6 +56,7 @@
 )
 from mypyc.ir.pprint import format_func
 from mypyc.ir.rtypes import (
+    KNOWN_NATIVE_TYPES,
     RArray,
     RInstance,
     RPrimitive,
@@ -181,7 +182,7 @@ def check_op_sources_valid(fn: FuncIR) -> list[FnError]:
     set_rprimitive.name,
     tuple_rprimitive.name,
     range_rprimitive.name,
-}
+} | set(KNOWN_NATIVE_TYPES)
 
 
 def can_coerce_to(src: RType, dest: RType) -> bool:
diff --git a/mypyc/build.py b/mypyc/build.py
index 4a2d703b9f10..efbd0dce31db 100644
--- a/mypyc/build.py
+++ b/mypyc/build.py
@@ -492,6 +492,8 @@ def mypycify(
     strict_dunder_typing: bool = False,
     group_name: str | None = None,
     log_trace: bool = False,
+    depends_on_native_internal: bool = False,
+    install_native_libs: bool = False,
 ) -> list[Extension]:
     """Main entry point to building using mypyc.
 
@@ -542,6 +544,11 @@ def mypycify(
                    mypyc_trace.txt (derived from executed operations). This is
                    useful for performance analysis, such as analyzing which
                    primitive ops are used the most and on which lines.
+        depends_on_native_internal: This is True only for mypy itself.
+        install_native_libs: If True, also build the native extension modules. Normally,
+                             those are build and published on PyPI separately, but during
+                             tests, we want to use their development versions (i.e. from
+                             current commit).
     """
 
     # Figure out our configuration
@@ -555,6 +562,7 @@ def mypycify(
         strict_dunder_typing=strict_dunder_typing,
         group_name=group_name,
         log_trace=log_trace,
+        depends_on_native_internal=depends_on_native_internal,
     )
 
     # Generate all the actual important C code
@@ -653,4 +661,21 @@ def mypycify(
                 build_single_module(group_sources, cfilenames + shared_cfilenames, cflags)
             )
 
+    if install_native_libs:
+        for name in ["native_internal.c"] + RUNTIME_C_FILES:
+            rt_file = os.path.join(build_dir, name)
+            with open(os.path.join(include_dir(), name), encoding="utf-8") as f:
+                write_file(rt_file, f.read())
+        extensions.append(
+            get_extension()(
+                "native_internal",
+                sources=[
+                    os.path.join(build_dir, file)
+                    for file in ["native_internal.c"] + RUNTIME_C_FILES
+                ],
+                include_dirs=[include_dir()],
+                extra_compile_args=cflags,
+            )
+        )
+
     return extensions
diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py
index 8c4a69cfa3cb..9ca761bd8ac5 100644
--- a/mypyc/codegen/emit.py
+++ b/mypyc/codegen/emit.py
@@ -39,6 +39,7 @@
     is_int64_rprimitive,
     is_int_rprimitive,
     is_list_rprimitive,
+    is_native_rprimitive,
     is_none_rprimitive,
     is_object_rprimitive,
     is_optional_type,
@@ -704,7 +705,7 @@ def emit_cast(
             self.emit_lines(f"    {dest} = {src};", "else {")
             self.emit_cast_error_handler(error, src, dest, typ, raise_exception)
             self.emit_line("}")
-        elif is_object_rprimitive(typ):
+        elif is_object_rprimitive(typ) or is_native_rprimitive(typ):
             if declare_dest:
                 self.emit_line(f"PyObject *{dest};")
             self.emit_arg_check(src, dest, typ, "", optional)
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index 1e49b1320b26..e31fcf8ea0c9 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -601,6 +601,8 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]:
         ext_declarations.emit_line(f"#define MYPYC_NATIVE{self.group_suffix}_H")
         ext_declarations.emit_line("#include ")
         ext_declarations.emit_line("#include ")
+        if self.compiler_options.depends_on_native_internal:
+            ext_declarations.emit_line("#include ")
 
         declarations = Emitter(self.context)
         declarations.emit_line(f"#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H")
@@ -1027,6 +1029,10 @@ def emit_module_exec_func(
         declaration = f"int CPyExec_{exported_name(module_name)}(PyObject *module)"
         module_static = self.module_internal_static_name(module_name, emitter)
         emitter.emit_lines(declaration, "{")
+        if self.compiler_options.depends_on_native_internal:
+            emitter.emit_line("if (import_native_internal() < 0) {")
+            emitter.emit_line("return -1;")
+            emitter.emit_line("}")
         emitter.emit_line("PyObject* modname = NULL;")
         if self.multi_phase_init:
             emitter.emit_line(f"{module_static} = module;")
@@ -1187,7 +1193,7 @@ def declare_internal_globals(self, module_name: str, emitter: Emitter) -> None:
         self.declare_global("PyObject *", static_name)
 
     def module_internal_static_name(self, module_name: str, emitter: Emitter) -> str:
-        return emitter.static_name(module_name + "_internal", None, prefix=MODULE_PREFIX)
+        return emitter.static_name(module_name + "__internal", None, prefix=MODULE_PREFIX)
 
     def declare_module(self, module_name: str, emitter: Emitter) -> None:
         # We declare two globals for each compiled module:
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 3c2fbfec1035..667ff60b0204 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -512,6 +512,15 @@ def __hash__(self) -> int:
 # Python range object.
 range_rprimitive: Final = RPrimitive("builtins.range", is_unboxed=False, is_refcounted=True)
 
+KNOWN_NATIVE_TYPES: Final = {
+    name: RPrimitive(name, is_unboxed=False, is_refcounted=True)
+    for name in ["native_internal.Buffer"]
+}
+
+
+def is_native_rprimitive(rtype: RType) -> bool:
+    return isinstance(rtype, RPrimitive) and rtype.name in KNOWN_NATIVE_TYPES
+
 
 def is_tagged(rtype: RType) -> TypeGuard[RPrimitive]:
     return rtype is int_rprimitive or rtype is short_int_rprimitive
diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py
index 815688d90fb6..05aa0e45c569 100644
--- a/mypyc/irbuild/mapper.py
+++ b/mypyc/irbuild/mapper.py
@@ -25,6 +25,7 @@
 from mypyc.ir.class_ir import ClassIR
 from mypyc.ir.func_ir import FuncDecl, FuncSignature, RuntimeArg
 from mypyc.ir.rtypes import (
+    KNOWN_NATIVE_TYPES,
     RInstance,
     RTuple,
     RType,
@@ -119,6 +120,8 @@ def type_to_rtype(self, typ: Type | None) -> RType:
                 return int16_rprimitive
             elif typ.type.fullname == "mypy_extensions.u8":
                 return uint8_rprimitive
+            elif typ.type.fullname in KNOWN_NATIVE_TYPES:
+                return KNOWN_NATIVE_TYPES[typ.type.fullname]
             else:
                 return object_rprimitive
         elif isinstance(typ, TupleType):
diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
new file mode 100644
index 000000000000..11a3fafee56f
--- /dev/null
+++ b/mypyc/lib-rt/native_internal.c
@@ -0,0 +1,510 @@
+#define PY_SSIZE_T_CLEAN
+#include 
+#include "CPy.h"
+#define NATIVE_INTERNAL_MODULE
+#include "native_internal.h"
+
+#define START_SIZE 512
+
+typedef struct {
+    PyObject_HEAD
+    Py_ssize_t pos;
+    Py_ssize_t end;
+    Py_ssize_t size;
+    char *buf;
+    PyObject *source;
+} BufferObject;
+
+static PyTypeObject BufferType;
+
+static PyObject*
+Buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    if (type != &BufferType) {
+        PyErr_SetString(PyExc_TypeError, "Buffer should not be subclassed");
+        return NULL;
+    }
+
+    BufferObject *self = (BufferObject *)type->tp_alloc(type, 0);
+    if (self != NULL) {
+        self->pos = 0;
+        self->end = 0;
+        self->size = 0;
+        self->buf = NULL;
+    }
+    return (PyObject *) self;
+}
+
+
+static int
+Buffer_init_internal(BufferObject *self, PyObject *source) {
+    if (source) {
+        if (!PyBytes_Check(source)) {
+            PyErr_SetString(PyExc_TypeError, "source must be a bytes object");
+            return -1;
+        }
+        self->size = PyBytes_GET_SIZE(source);
+        self->end = self->size;
+        // This returns a pointer to internal bytes data, so make our own copy.
+        char *buf = PyBytes_AsString(source);
+        self->buf = PyMem_Malloc(self->size);
+        memcpy(self->buf, buf, self->size);
+    } else {
+        self->buf = PyMem_Malloc(START_SIZE);
+        self->size = START_SIZE;
+    }
+    return 0;
+}
+
+static PyObject*
+Buffer_internal(PyObject *source) {
+    BufferObject *self = (BufferObject *)BufferType.tp_alloc(&BufferType, 0);
+    if (self == NULL)
+        return NULL;
+    self->pos = 0;
+    self->end = 0;
+    self->size = 0;
+    self->buf = NULL;
+    if (Buffer_init_internal(self, source) == -1) {
+        Py_DECREF(self);
+        return NULL;
+    }
+    return (PyObject *)self;
+}
+
+static PyObject*
+Buffer_internal_empty(void) {
+    return Buffer_internal(NULL);
+}
+
+static int
+Buffer_init(BufferObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"source", NULL};
+    PyObject *source = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &source))
+        return -1;
+
+    return Buffer_init_internal(self, source);
+}
+
+static void
+Buffer_dealloc(BufferObject *self)
+{
+    PyMem_Free(self->buf);
+    Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject*
+Buffer_getvalue_internal(PyObject *self)
+{
+    return PyBytes_FromStringAndSize(((BufferObject *)self)->buf, ((BufferObject *)self)->end);
+}
+
+static PyObject*
+Buffer_getvalue(BufferObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return PyBytes_FromStringAndSize(self->buf, self->end);
+}
+
+static PyMethodDef Buffer_methods[] = {
+    {"getvalue", (PyCFunction) Buffer_getvalue, METH_NOARGS,
+     "Return the buffer content as bytes object"
+    },
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject BufferType = {
+    .ob_base = PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "Buffer",
+    .tp_doc = PyDoc_STR("Mypy cache buffer objects"),
+    .tp_basicsize = sizeof(BufferObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_new = Buffer_new,
+    .tp_init = (initproc) Buffer_init,
+    .tp_dealloc = (destructor) Buffer_dealloc,
+    .tp_methods = Buffer_methods,
+};
+
+static inline char
+_check_buffer(PyObject *data) {
+    if (Py_TYPE(data) != &BufferType) {
+        PyErr_Format(
+            PyExc_TypeError, "data must be a Buffer object, got %s", Py_TYPE(data)->tp_name
+        );
+        return 2;
+    }
+    return 1;
+}
+
+static inline char
+_check_size(BufferObject *data, Py_ssize_t need) {
+    Py_ssize_t target = data->pos + need;
+    if (target <= data->size)
+        return 1;
+    do
+        data->size *= 2;
+    while (target >= data->size);
+    data->buf = PyMem_Realloc(data->buf, data->size);
+    if (!data->buf) {
+        PyErr_NoMemory();
+        return 2;
+    }
+    return 1;
+}
+
+static inline char
+_check_read(BufferObject *data, Py_ssize_t need) {
+    if (data->pos + need > data->end) {
+        PyErr_SetString(PyExc_ValueError, "reading past the buffer end");
+        return 2;
+    }
+    return 1;
+}
+
+static char
+read_bool_internal(PyObject *data) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    if (_check_read((BufferObject *)data, 1) == 2)
+        return 2;
+    char *buf = ((BufferObject *)data)->buf;
+    char res = buf[((BufferObject *)data)->pos];
+    ((BufferObject *)data)->pos += 1;
+    return res;
+}
+
+static PyObject*
+read_bool(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", NULL};
+    PyObject *data = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+        return NULL;
+    char res = read_bool_internal(data);
+    if (res == 2)
+        return NULL;
+    PyObject *retval = res ? Py_True : Py_False;
+    Py_INCREF(retval);
+    return retval;
+}
+
+static char
+write_bool_internal(PyObject *data, char value) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    if (_check_size((BufferObject *)data, 1) == 2)
+        return 2;
+    char *buf = ((BufferObject *)data)->buf;
+    buf[((BufferObject *)data)->pos] = value;
+    ((BufferObject *)data)->pos += 1;
+    ((BufferObject *)data)->end += 1;
+    return 1;
+}
+
+static PyObject*
+write_bool(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", "value", NULL};
+    PyObject *data = NULL;
+    PyObject *value = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+        return NULL;
+    if (!PyBool_Check(value)) {
+        PyErr_SetString(PyExc_TypeError, "value must be a bool");
+        return NULL;
+    }
+    if (write_bool_internal(data, value == Py_True) == 2) {
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject*
+read_str_internal(PyObject *data) {
+    if (_check_buffer(data) == 2)
+        return NULL;
+
+    if (_check_read((BufferObject *)data, sizeof(Py_ssize_t)) == 2)
+        return NULL;
+    char *buf = ((BufferObject *)data)->buf;
+    // Read string length.
+    Py_ssize_t size = *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+    if (_check_read((BufferObject *)data, size) == 2)
+        return NULL;
+    // Read string content.
+    PyObject *res = PyUnicode_FromStringAndSize(
+        buf + ((BufferObject *)data)->pos, (Py_ssize_t)size
+    );
+    if (!res)
+        return NULL;
+    ((BufferObject *)data)->pos += size;
+    return res;
+}
+
+static PyObject*
+read_str(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", NULL};
+    PyObject *data = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+        return NULL;
+    return read_str_internal(data);
+}
+
+static char
+write_str_internal(PyObject *data, PyObject *value) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    Py_ssize_t size;
+    const char *chunk = PyUnicode_AsUTF8AndSize(value, &size);
+    if (!chunk)
+        return 2;
+    Py_ssize_t need = size + sizeof(Py_ssize_t);
+    if (_check_size((BufferObject *)data, need) == 2)
+        return 2;
+
+    char *buf = ((BufferObject *)data)->buf;
+    // Write string length.
+    *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos) = size;
+    ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+    // Write string content.
+    memcpy(buf + ((BufferObject *)data)->pos, chunk, size);
+    ((BufferObject *)data)->pos += size;
+    ((BufferObject *)data)->end += need;
+    return 1;
+}
+
+static PyObject*
+write_str(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", "value", NULL};
+    PyObject *data = NULL;
+    PyObject *value = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+        return NULL;
+    if (!PyUnicode_Check(value)) {
+        PyErr_SetString(PyExc_TypeError, "value must be a str");
+        return NULL;
+    }
+    if (write_str_internal(data, value) == 2) {
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static double
+read_float_internal(PyObject *data) {
+    if (_check_buffer(data) == 2)
+        return CPY_FLOAT_ERROR;
+
+    if (_check_read((BufferObject *)data, sizeof(double)) == 2)
+        return CPY_FLOAT_ERROR;
+    char *buf = ((BufferObject *)data)->buf;
+    double res = *(double *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += sizeof(double);
+    return res;
+}
+
+static PyObject*
+read_float(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", NULL};
+    PyObject *data = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+        return NULL;
+    double retval = read_float_internal(data);
+    if (retval == CPY_FLOAT_ERROR && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PyFloat_FromDouble(retval);
+}
+
+static char
+write_float_internal(PyObject *data, double value) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    if (_check_size((BufferObject *)data, sizeof(double)) == 2)
+        return 2;
+    char *buf = ((BufferObject *)data)->buf;
+    *(double *)(buf + ((BufferObject *)data)->pos) = value;
+    ((BufferObject *)data)->pos += sizeof(double);
+    ((BufferObject *)data)->end += sizeof(double);
+    return 1;
+}
+
+static PyObject*
+write_float(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", "value", NULL};
+    PyObject *data = NULL;
+    PyObject *value = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+        return NULL;
+    if (!PyFloat_Check(value)) {
+        PyErr_SetString(PyExc_TypeError, "value must be a float");
+        return NULL;
+    }
+    if (write_float_internal(data, PyFloat_AsDouble(value)) == 2) {
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static CPyTagged
+read_int_internal(PyObject *data) {
+    if (_check_buffer(data) == 2)
+        return CPY_INT_TAG;
+
+    if (_check_read((BufferObject *)data, sizeof(CPyTagged)) == 2)
+        return CPY_INT_TAG;
+    char *buf = ((BufferObject *)data)->buf;
+
+    CPyTagged ret = *(CPyTagged *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += sizeof(CPyTagged);
+    if ((ret & CPY_INT_TAG) == 0)
+        return ret;
+    // People who have literal ints not fitting in size_t should be punished :-)
+    PyObject *str_ret = read_str_internal(data);
+    if (str_ret == NULL)
+        return CPY_INT_TAG;
+    PyObject* ret_long = PyLong_FromUnicodeObject(str_ret, 10);
+    Py_DECREF(str_ret);
+    return ((CPyTagged)ret_long) | CPY_INT_TAG;
+}
+
+static PyObject*
+read_int(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", NULL};
+    PyObject *data = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+        return NULL;
+    CPyTagged retval = read_int_internal(data);
+    if (retval == CPY_INT_TAG) {
+        return NULL;
+    }
+    return CPyTagged_StealAsObject(retval);
+}
+
+static char
+write_int_internal(PyObject *data, CPyTagged value) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    if (_check_size((BufferObject *)data, sizeof(CPyTagged)) == 2)
+        return 2;
+    char *buf = ((BufferObject *)data)->buf;
+    if ((value & CPY_INT_TAG) == 0) {
+        *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = value;
+    } else {
+        *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = CPY_INT_TAG;
+    }
+    ((BufferObject *)data)->pos += sizeof(CPyTagged);
+    ((BufferObject *)data)->end += sizeof(CPyTagged);
+    if ((value & CPY_INT_TAG) != 0) {
+        PyObject *str_value = PyObject_Str(CPyTagged_LongAsObject(value));
+        if (str_value == NULL)
+            return 2;
+        char res = write_str_internal(data, str_value);
+        Py_DECREF(str_value);
+        if (res == 2)
+            return 2;
+    }
+    return 1;
+}
+
+static PyObject*
+write_int(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", "value", NULL};
+    PyObject *data = NULL;
+    PyObject *value = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+        return NULL;
+    if (!PyLong_Check(value)) {
+        PyErr_SetString(PyExc_TypeError, "value must be an int");
+        return NULL;
+    }
+    CPyTagged tagged_value = CPyTagged_BorrowFromObject(value);
+    if (write_int_internal(data, tagged_value) == 2) {
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef native_internal_module_methods[] = {
+    // TODO: switch public wrappers to METH_FASTCALL.
+    {"write_bool", (PyCFunction)write_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a bool")},
+    {"read_bool", (PyCFunction)read_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a bool")},
+    {"write_str", (PyCFunction)write_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a string")},
+    {"read_str", (PyCFunction)read_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a string")},
+    {"write_float", (PyCFunction)write_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a float")},
+    {"read_float", (PyCFunction)read_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a float")},
+    {"write_int", (PyCFunction)write_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write an int")},
+    {"read_int", (PyCFunction)read_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read an int")},
+    {NULL, NULL, 0, NULL}
+};
+
+static int
+NativeInternal_ABI_Version(void) {
+    return NATIVE_INTERNAL_ABI_VERSION;
+}
+
+static int
+native_internal_module_exec(PyObject *m)
+{
+    if (PyType_Ready(&BufferType) < 0) {
+        return -1;
+    }
+    if (PyModule_AddObjectRef(m, "Buffer", (PyObject *) &BufferType) < 0) {
+        return -1;
+    }
+
+    // Export mypy internal C API, be careful with the order!
+    static void *NativeInternal_API[12] = {
+        (void *)Buffer_internal,
+        (void *)Buffer_internal_empty,
+        (void *)Buffer_getvalue_internal,
+        (void *)write_bool_internal,
+        (void *)read_bool_internal,
+        (void *)write_str_internal,
+        (void *)read_str_internal,
+        (void *)write_float_internal,
+        (void *)read_float_internal,
+        (void *)write_int_internal,
+        (void *)read_int_internal,
+        (void *)NativeInternal_ABI_Version,
+    };
+    PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "native_internal._C_API", NULL);
+    if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static PyModuleDef_Slot native_internal_module_slots[] = {
+    {Py_mod_exec, native_internal_module_exec},
+#ifdef Py_MOD_GIL_NOT_USED
+    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+    {0, NULL}
+};
+
+static PyModuleDef native_internal_module = {
+    .m_base = PyModuleDef_HEAD_INIT,
+    .m_name = "native_internal",
+    .m_doc = "Mypy cache serialization utils",
+    .m_size = 0,
+    .m_methods = native_internal_module_methods,
+    .m_slots = native_internal_module_slots,
+};
+
+PyMODINIT_FUNC
+PyInit_native_internal(void)
+{
+    return PyModuleDef_Init(&native_internal_module);
+}
diff --git a/mypyc/lib-rt/native_internal.h b/mypyc/lib-rt/native_internal.h
new file mode 100644
index 000000000000..3bd3dd1bbb33
--- /dev/null
+++ b/mypyc/lib-rt/native_internal.h
@@ -0,0 +1,52 @@
+#ifndef NATIVE_INTERNAL_H
+#define NATIVE_INTERNAL_H
+
+#define NATIVE_INTERNAL_ABI_VERSION 0
+
+#ifdef NATIVE_INTERNAL_MODULE
+
+static PyObject *Buffer_internal(PyObject *source);
+static PyObject *Buffer_internal_empty(void);
+static PyObject *Buffer_getvalue_internal(PyObject *self);
+static char write_bool_internal(PyObject *data, char value);
+static char read_bool_internal(PyObject *data);
+static char write_str_internal(PyObject *data, PyObject *value);
+static PyObject *read_str_internal(PyObject *data);
+static char write_float_internal(PyObject *data, double value);
+static double read_float_internal(PyObject *data);
+static char write_int_internal(PyObject *data, CPyTagged value);
+static CPyTagged read_int_internal(PyObject *data);
+static int NativeInternal_ABI_Version(void);
+
+#else
+
+static void **NativeInternal_API;
+
+#define Buffer_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[0])
+#define Buffer_internal_empty (*(PyObject* (*)(void)) NativeInternal_API[1])
+#define Buffer_getvalue_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[2])
+#define write_bool_internal (*(char (*)(PyObject *source, char value)) NativeInternal_API[3])
+#define read_bool_internal (*(char (*)(PyObject *source)) NativeInternal_API[4])
+#define write_str_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[5])
+#define read_str_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[6])
+#define write_float_internal (*(char (*)(PyObject *source, double value)) NativeInternal_API[7])
+#define read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8])
+#define write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9])
+#define read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10])
+#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[11])
+
+static int
+import_native_internal(void)
+{
+    NativeInternal_API = (void **)PyCapsule_Import("native_internal._C_API", 0);
+    if (NativeInternal_API == NULL)
+        return -1;
+    if (NativeInternal_ABI_Version() != NATIVE_INTERNAL_ABI_VERSION) {
+        PyErr_SetString(PyExc_ValueError, "ABI version conflict for native_internal");
+        return -1;
+    }
+    return 0;
+}
+
+#endif
+#endif  // NATIVE_INTERNAL_H
diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py
index 1faacc8fc136..5b7a2919c0fd 100644
--- a/mypyc/lib-rt/setup.py
+++ b/mypyc/lib-rt/setup.py
@@ -12,60 +12,74 @@
 from distutils.core import Extension, setup
 from typing import Any
 
-kwargs: dict[str, Any]
-if sys.platform == "darwin":
-    kwargs = {"language": "c++"}
-    compile_args = []
-else:
-    kwargs = {}
-    compile_args = ["--std=c++11"]
+C_APIS_TO_TEST = [
+    "init.c",
+    "int_ops.c",
+    "float_ops.c",
+    "list_ops.c",
+    "exc_ops.c",
+    "generic_ops.c",
+    "pythonsupport.c",
+]
 
 
-class build_ext_custom(build_ext):  # noqa: N801
-    def get_library_names(self):
+class BuildExtGtest(build_ext):
+    def get_library_names(self) -> list[str]:
         return ["gtest"]
 
-    def run(self):
+    def run(self) -> None:
+        # Build Google Test, the C++ framework we use for testing C code.
+        # The source code for Google Test is copied to this repository.
         gtest_dir = os.path.abspath(
             os.path.join(os.path.dirname(__file__), "..", "external", "googletest")
         )
-
         os.makedirs(self.build_temp, exist_ok=True)
-
-        # Build Google Test, the C++ framework we use for testing C code.
-        # The source code for Google Test is copied to this repository.
         subprocess.check_call(
             ["make", "-f", os.path.join(gtest_dir, "make", "Makefile"), f"GTEST_DIR={gtest_dir}"],
             cwd=self.build_temp,
         )
-
         self.library_dirs = [self.build_temp]
-
         return build_ext.run(self)
 
 
-setup(
-    name="test_capi",
-    version="0.1",
-    ext_modules=[
-        Extension(
-            "test_capi",
-            [
-                "test_capi.cc",
-                "init.c",
-                "int_ops.c",
-                "float_ops.c",
-                "list_ops.c",
-                "exc_ops.c",
-                "generic_ops.c",
-                "pythonsupport.c",
-            ],
-            depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"],
-            extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args,
-            libraries=["gtest"],
-            include_dirs=["../external/googletest", "../external/googletest/include"],
-            **kwargs,
-        )
-    ],
-    cmdclass={"build_ext": build_ext_custom},
-)
+if "--run-capi-tests" in sys.argv:
+    sys.argv.pop()
+
+    kwargs: dict[str, Any]
+    if sys.platform == "darwin":
+        kwargs = {"language": "c++"}
+        compile_args = []
+    else:
+        kwargs = {}
+        compile_args = ["--std=c++11"]
+
+    setup(
+        name="test_capi",
+        version="0.1",
+        ext_modules=[
+            Extension(
+                "test_capi",
+                ["test_capi.cc"] + C_APIS_TO_TEST,
+                depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"],
+                extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args,
+                libraries=["gtest"],
+                include_dirs=["../external/googletest", "../external/googletest/include"],
+                **kwargs,
+            )
+        ],
+        cmdclass={"build_ext": BuildExtGtest},
+    )
+else:
+    # TODO: we need a way to share our preferred C flags and get_extension() logic with
+    # mypyc/build.py without code duplication.
+    setup(
+        name="mypy-native",
+        version="0.0.1",
+        ext_modules=[
+            Extension(
+                "native_internal",
+                ["native_internal.c", "init.c", "int_ops.c", "exc_ops.c", "pythonsupport.c"],
+                include_dirs=["."],
+            )
+        ],
+    )
diff --git a/mypyc/options.py b/mypyc/options.py
index 50c76d3c0656..c009d3c6a7a4 100644
--- a/mypyc/options.py
+++ b/mypyc/options.py
@@ -17,6 +17,7 @@ def __init__(
         strict_dunder_typing: bool = False,
         group_name: str | None = None,
         log_trace: bool = False,
+        depends_on_native_internal: bool = False,
     ) -> None:
         self.strip_asserts = strip_asserts
         self.multi_file = multi_file
@@ -50,3 +51,7 @@ def __init__(
         # mypyc_trace.txt when compiled module is executed. This is useful for
         # performance analysis.
         self.log_trace = log_trace
+        # If enabled, add capsule imports of native_internal API. This should be used
+        # only for mypy itself, third-party code compiled with mypyc should not use
+        # native_internal.
+        self.depends_on_native_internal = depends_on_native_internal
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index a13f87cc94e9..8738255081e2 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -4,14 +4,18 @@
 
 from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
 from mypyc.ir.rtypes import (
+    KNOWN_NATIVE_TYPES,
     bit_rprimitive,
     bool_rprimitive,
+    bytes_rprimitive,
     c_int_rprimitive,
     c_pointer_rprimitive,
     c_pyssize_t_rprimitive,
     cstring_rprimitive,
     dict_rprimitive,
+    float_rprimitive,
     int_rprimitive,
+    none_rprimitive,
     object_pointer_rprimitive,
     object_rprimitive,
     pointer_rprimitive,
@@ -24,6 +28,7 @@
     custom_primitive_op,
     function_op,
     load_address_op,
+    method_op,
 )
 
 # Get the 'bool' type object.
@@ -326,3 +331,95 @@
     return_type=void_rtype,
     error_kind=ERR_NEVER,
 )
+
+buffer_rprimitive = KNOWN_NATIVE_TYPES["native_internal.Buffer"]
+
+# Buffer(source)
+function_op(
+    name="native_internal.Buffer",
+    arg_types=[bytes_rprimitive],
+    return_type=buffer_rprimitive,
+    c_function_name="Buffer_internal",
+    error_kind=ERR_MAGIC,
+)
+
+# Buffer()
+function_op(
+    name="native_internal.Buffer",
+    arg_types=[],
+    return_type=buffer_rprimitive,
+    c_function_name="Buffer_internal_empty",
+    error_kind=ERR_MAGIC,
+)
+
+method_op(
+    name="getvalue",
+    arg_types=[buffer_rprimitive],
+    return_type=bytes_rprimitive,
+    c_function_name="Buffer_getvalue_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.write_bool",
+    arg_types=[object_rprimitive, bool_rprimitive],
+    return_type=none_rprimitive,
+    c_function_name="write_bool_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.read_bool",
+    arg_types=[object_rprimitive],
+    return_type=bool_rprimitive,
+    c_function_name="read_bool_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.write_str",
+    arg_types=[object_rprimitive, str_rprimitive],
+    return_type=none_rprimitive,
+    c_function_name="write_str_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.read_str",
+    arg_types=[object_rprimitive],
+    return_type=str_rprimitive,
+    c_function_name="read_str_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.write_float",
+    arg_types=[object_rprimitive, float_rprimitive],
+    return_type=none_rprimitive,
+    c_function_name="write_float_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.read_float",
+    arg_types=[object_rprimitive],
+    return_type=float_rprimitive,
+    c_function_name="read_float_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.write_int",
+    arg_types=[object_rprimitive, int_rprimitive],
+    return_type=none_rprimitive,
+    c_function_name="write_int_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.read_int",
+    arg_types=[object_rprimitive],
+    return_type=int_rprimitive,
+    c_function_name="read_int_internal",
+    error_kind=ERR_MAGIC,
+)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 4bb20ee9c65c..68bc18c7bdeb 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1409,6 +1409,55 @@ class TestOverload:
     def __mypyc_generator_helper__(self, x: Any) -> Any:
         return x
 
+[case testNativeBufferFastPath]
+from native_internal import (
+    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float, write_int, read_int
+)
+
+def foo() -> None:
+    b = Buffer()
+    write_str(b, "foo")
+    write_bool(b, True)
+    write_float(b, 0.1)
+    write_int(b, 1)
+
+    b = Buffer(b.getvalue())
+    x = read_str(b)
+    y = read_bool(b)
+    z = read_float(b)
+    t = read_int(b)
+[out]
+def foo():
+    r0, b :: native_internal.Buffer
+    r1 :: str
+    r2, r3, r4, r5 :: None
+    r6 :: bytes
+    r7 :: native_internal.Buffer
+    r8, x :: str
+    r9, y :: bool
+    r10, z :: float
+    r11, t :: int
+L0:
+    r0 = Buffer_internal_empty()
+    b = r0
+    r1 = 'foo'
+    r2 = write_str_internal(b, r1)
+    r3 = write_bool_internal(b, 1)
+    r4 = write_float_internal(b, 0.1)
+    r5 = write_int_internal(b, 2)
+    r6 = Buffer_getvalue_internal(b)
+    r7 = Buffer_internal(r6)
+    b = r7
+    r8 = read_str_internal(b)
+    x = r8
+    r9 = read_bool_internal(b)
+    y = r9
+    r10 = read_float_internal(b)
+    z = r10
+    r11 = read_int_internal(b)
+    t = r11
+    return 1
+
 [case testEnumFastPath]
 from enum import Enum
 
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index fed5cfb65870..6f1217bd36e6 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2710,6 +2710,74 @@ from native import Player
 [out]
 Player.MIN = 
 
+[case testBufferRoundTrip_native_libs]
+from native_internal import (
+    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float, write_int, read_int
+)
+
+def test_buffer_basic() -> None:
+    b = Buffer(b"foo")
+    assert b.getvalue() == b"foo"
+
+def test_buffer_roundtrip() -> None:
+    b = Buffer()
+    write_str(b, "foo")
+    write_bool(b, True)
+    write_str(b, "bar" * 1000)
+    write_bool(b, False)
+    write_float(b, 0.1)
+    write_int(b, 0)
+    write_int(b, 1)
+    write_int(b, 2)
+    write_int(b, 2 ** 85)
+
+    b = Buffer(b.getvalue())
+    assert read_str(b) == "foo"
+    assert read_bool(b) is True
+    assert read_str(b) == "bar" * 1000
+    assert read_bool(b) is False
+    assert read_float(b) == 0.1
+    assert read_int(b) == 0
+    assert read_int(b) == 1
+    assert read_int(b) == 2
+    assert read_int(b) == 2 ** 85
+
+[file driver.py]
+from native import *
+
+test_buffer_basic()
+test_buffer_roundtrip()
+
+def test_buffer_basic_interpreted() -> None:
+    b = Buffer(b"foo")
+    assert b.getvalue() == b"foo"
+
+def test_buffer_roundtrip_interpreted() -> None:
+    b = Buffer()
+    write_str(b, "foo")
+    write_bool(b, True)
+    write_str(b, "bar" * 1000)
+    write_bool(b, False)
+    write_float(b, 0.1)
+    write_int(b, 0)
+    write_int(b, 1)
+    write_int(b, 2)
+    write_int(b, 2 ** 85)
+
+    b = Buffer(b.getvalue())
+    assert read_str(b) == "foo"
+    assert read_bool(b) is True
+    assert read_str(b) == "bar" * 1000
+    assert read_bool(b) is False
+    assert read_float(b) == 0.1
+    assert read_int(b) == 0
+    assert read_int(b) == 1
+    assert read_int(b) == 2
+    assert read_int(b) == 2 ** 85
+
+test_buffer_basic_interpreted()
+test_buffer_roundtrip_interpreted()
+
 [case testEnumMethodCalls]
 from enum import Enum
 from typing import overload, Optional, Union
diff --git a/mypyc/test/test_external.py b/mypyc/test/test_external.py
index 010c74dee42e..a416cf2ee130 100644
--- a/mypyc/test/test_external.py
+++ b/mypyc/test/test_external.py
@@ -34,6 +34,7 @@ def test_c_unit_test(self) -> None:
                     "build_ext",
                     f"--build-lib={tmpdir}",
                     f"--build-temp={tmpdir}",
+                    "--run-capi-tests",
                 ],
                 env=env,
                 cwd=os.path.join(base_dir, "mypyc", "lib-rt"),
diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py
index 5078426b977f..172a1016dd91 100644
--- a/mypyc/test/test_run.py
+++ b/mypyc/test/test_run.py
@@ -86,7 +86,7 @@
 
 setup(name='test_run_output',
       ext_modules=mypycify({}, separate={}, skip_cgen_input={!r}, strip_asserts=False,
-                           multi_file={}, opt_level='{}'),
+                           multi_file={}, opt_level='{}', install_native_libs={}),
 )
 """
 
@@ -239,11 +239,13 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
 
         groups = construct_groups(sources, separate, len(module_names) > 1, None)
 
+        native_libs = "_native_libs" in testcase.name
         try:
             compiler_options = CompilerOptions(
                 multi_file=self.multi_file,
                 separate=self.separate,
                 strict_dunder_typing=self.strict_dunder_typing,
+                depends_on_native_internal=native_libs,
             )
             result = emitmodule.parse_and_typecheck(
                 sources=sources,
@@ -270,14 +272,13 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
             check_serialization_roundtrip(ir)
 
         opt_level = int(os.environ.get("MYPYC_OPT_LEVEL", 0))
-        debug_level = int(os.environ.get("MYPYC_DEBUG_LEVEL", 0))
 
         setup_file = os.path.abspath(os.path.join(WORKDIR, "setup.py"))
         # We pass the C file information to the build script via setup.py unfortunately
         with open(setup_file, "w", encoding="utf-8") as f:
             f.write(
                 setup_format.format(
-                    module_paths, separate, cfiles, self.multi_file, opt_level, debug_level
+                    module_paths, separate, cfiles, self.multi_file, opt_level, native_libs
                 )
             )
 
diff --git a/setup.py b/setup.py
index e085b0be3846..798ff4f6c710 100644
--- a/setup.py
+++ b/setup.py
@@ -154,6 +154,10 @@ def run(self) -> None:
         # our Appveyor builds run out of memory sometimes.
         multi_file=sys.platform == "win32" or force_multifile,
         log_trace=log_trace,
+        # Mypy itself is allowed to use native_internal extension.
+        depends_on_native_internal=True,
+        # TODO: temporary, remove this after we publish mypy-native on PyPI.
+        install_native_libs=True,
     )
 
 else:
diff --git a/test-data/unit/lib-stub/native_internal.pyi b/test-data/unit/lib-stub/native_internal.pyi
new file mode 100644
index 000000000000..bc1f570a8e9c
--- /dev/null
+++ b/test-data/unit/lib-stub/native_internal.pyi
@@ -0,0 +1,12 @@
+class Buffer:
+    def __init__(self, source: bytes = ...) -> None: ...
+    def getvalue(self) -> bytes: ...
+
+def write_bool(data: Buffer, value: bool) -> None: ...
+def read_bool(data: Buffer) -> bool: ...
+def write_str(data: Buffer, value: str) -> None: ...
+def read_str(data: Buffer) -> str: ...
+def write_float(data: Buffer, value: float) -> None: ...
+def read_float(data: Buffer) -> float: ...
+def write_int(data: Buffer, value: int) -> None: ...
+def read_int(data: Buffer) -> int: ...

From d1c69046a9b54fff1219f14832116810456b29bb Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Wed, 20 Aug 2025 16:33:51 -0400
Subject: [PATCH 1564/1617] Don't expand PEP 695 aliases when checking node
 fullnames (#19699)

Fixes #19698
---
 mypy/semanal.py                     |  2 +-
 test-data/unit/check-python312.test | 16 ++++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index e8426a4e4885..77e6b0c005e2 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -7678,7 +7678,7 @@ def refers_to_fullname(node: Expression, fullnames: str | tuple[str, ...]) -> bo
         return False
     if node.fullname in fullnames:
         return True
-    if isinstance(node.node, TypeAlias):
+    if isinstance(node.node, TypeAlias) and not node.node.python_3_12_type_alias:
         return is_named_instance(node.node.target, fullnames)
     return False
 
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index d275503dc411..817184dc561c 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2095,3 +2095,19 @@ from dataclasses import dataclass
 class Test[*Ts, R]:
     a: Callable[[*Ts], R]
 [builtins fixtures/dict.pyi]
+
+[case testPEP695AliasDoesNotReferToFullname]
+# https://github.com/python/mypy/issues/19698
+from typing import TypeAliasType
+type D = dict
+type T = type
+type TA = TypeAliasType
+
+D()          # E: "TypeAliasType" not callable
+X = TA("Y")  # E: "TypeAliasType" not callable
+
+x: object
+if T(x) is str:     # E: "TypeAliasType" not callable
+    reveal_type(x)  # N: Revealed type is "builtins.object"
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-full.pyi]

From b99948bc13832666515bc11b1b8410890badebd0 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Wed, 20 Aug 2025 22:07:01 -0400
Subject: [PATCH 1565/1617] Fail gracefully on unsupported template strings
 (PEP 750) (#19700)

Refs #19329

Stopgap until proper support is added
---
 mypy/fastparse.py                   | 22 ++++++++++++++++++++++
 mypy/test/testcheck.py              |  2 ++
 mypy/test/testparse.py              |  4 ++++
 test-data/unit/check-python314.test |  3 +++
 test-data/unit/parse-python314.test |  5 +++++
 5 files changed, 36 insertions(+)
 create mode 100644 test-data/unit/check-python314.test
 create mode 100644 test-data/unit/parse-python314.test

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index 0e1b66f0db59..99d5c48c92d7 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -188,6 +188,13 @@ def ast3_parse(
     ast_TypeVar = Any
     ast_TypeVarTuple = Any
 
+if sys.version_info >= (3, 14):
+    ast_TemplateStr = ast3.TemplateStr
+    ast_Interpolation = ast3.Interpolation
+else:
+    ast_TemplateStr = Any
+    ast_Interpolation = Any
+
 N = TypeVar("N", bound=Node)
 
 # There is no way to create reasonable fallbacks at this stage,
@@ -1705,6 +1712,21 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression:
         )
         return self.set_line(result_expression, n)
 
+    # TemplateStr(expr* values)
+    def visit_TemplateStr(self, n: ast_TemplateStr) -> Expression:
+        self.fail(
+            ErrorMessage("PEP 750 template strings are not yet supported"),
+            n.lineno,
+            n.col_offset,
+            blocker=False,
+        )
+        e = TempNode(AnyType(TypeOfAny.from_error))
+        return self.set_line(e, n)
+
+    # Interpolation(expr value, constant str, int conversion, expr? format_spec)
+    def visit_Interpolation(self, n: ast_Interpolation) -> Expression:
+        assert False, "Unreachable"
+
     # Attribute(expr value, identifier attr, expr_context ctx)
     def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr:
         value = n.value
diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py
index 206eab726a21..04ef5370d381 100644
--- a/mypy/test/testcheck.py
+++ b/mypy/test/testcheck.py
@@ -47,6 +47,8 @@
     typecheck_files.remove("check-python312.test")
 if sys.version_info < (3, 13):
     typecheck_files.remove("check-python313.test")
+if sys.version_info < (3, 14):
+    typecheck_files.remove("check-python314.test")
 
 
 class TypeCheckSuite(DataSuite):
diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py
index 027ca4dd2887..c8bcb5c0d807 100644
--- a/mypy/test/testparse.py
+++ b/mypy/test/testparse.py
@@ -27,6 +27,8 @@ class ParserSuite(DataSuite):
         files.remove("parse-python312.test")
     if sys.version_info < (3, 13):
         files.remove("parse-python313.test")
+    if sys.version_info < (3, 14):
+        files.remove("parse-python314.test")
 
     def run_case(self, testcase: DataDrivenTestCase) -> None:
         test_parser(testcase)
@@ -46,6 +48,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None:
         options.python_version = (3, 12)
     elif testcase.file.endswith("python313.test"):
         options.python_version = (3, 13)
+    elif testcase.file.endswith("python314.test"):
+        options.python_version = (3, 14)
     else:
         options.python_version = defaults.PYTHON3_VERSION
 
diff --git a/test-data/unit/check-python314.test b/test-data/unit/check-python314.test
new file mode 100644
index 000000000000..f1043aab860a
--- /dev/null
+++ b/test-data/unit/check-python314.test
@@ -0,0 +1,3 @@
+[case testTemplateString]
+reveal_type(t"mypy")  # E: PEP 750 template strings are not yet supported \
+                      # N: Revealed type is "Any"
diff --git a/test-data/unit/parse-python314.test b/test-data/unit/parse-python314.test
new file mode 100644
index 000000000000..34fe753084f6
--- /dev/null
+++ b/test-data/unit/parse-python314.test
@@ -0,0 +1,5 @@
+[case testTemplateString]
+x = 'mypy'
+t'Hello {x}'
+[out]
+main:2: error: PEP 750 template strings are not yet supported

From 8da097d1758bc403553d82b4838a6c9b76c6a627 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Thu, 21 Aug 2025 05:36:17 -0400
Subject: [PATCH 1566/1617] [mypyc] feat: extend stararg fastpath from #19629
 with star2 fastpath (#19630)

This PR further extends the stararg fastpath PRs (#19623 , #19629) with
fastpath logic for star2

All 3 PRs were kept separate in order to make them easier to review and
to make the changes in the IR more obvious in a diff.
---
 mypyc/irbuild/ll_builder.py           | 25 ++++++++++++++-
 mypyc/primitives/dict_ops.py          |  2 +-
 mypyc/test-data/irbuild-basic.test    | 46 ++++++++++-----------------
 mypyc/test-data/irbuild-generics.test | 32 +++++++------------
 mypyc/test-data/run-functions.test    | 26 +++++++++++++++
 5 files changed, 80 insertions(+), 51 deletions(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index f244e2f05e05..116a1bb4bae0 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -128,6 +128,8 @@
 from mypyc.primitives.bytes_ops import bytes_compare
 from mypyc.primitives.dict_ops import (
     dict_build_op,
+    dict_copy,
+    dict_copy_op,
     dict_new_op,
     dict_ssize_t_size_op,
     dict_update_in_display_op,
@@ -806,19 +808,40 @@ def _construct_varargs(
                         return value, self._create_dict([], [], line)
                     elif len(args) == 2 and args[1][1] == ARG_STAR2:
                         # fn(*args, **kwargs)
+                        # TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
                         if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple):
                             star_result = value
                         elif is_list_rprimitive(value.type):
                             star_result = self.primitive_op(list_tuple_op, [value], line)
                         else:
                             star_result = self.primitive_op(sequence_tuple_op, [value], line)
-                        continue
+
+                        star2_arg = args[1]
+                        star2_value = star2_arg[0]
+                        if is_dict_rprimitive(star2_value.type):
+                            star2_fastpath_op = dict_copy_op
+                        else:
+                            star2_fastpath_op = dict_copy
+                        return star_result, self.primitive_op(
+                            star2_fastpath_op, [star2_value], line
+                        )
                         # elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
                     # TODO optimize this case using the length utils - currently in review
                     star_result = self.new_list_op(star_values, line)
                 self.primitive_op(list_extend_op, [star_result, value], line)
             elif kind == ARG_STAR2:
                 if star2_result is None:
+                    if len(args) == 1:
+                        # early exit with fastpath if the only arg is ARG_STAR2
+                        # TODO: can we maintain an empty tuple in memory and just reuse it again and again?
+                        if is_dict_rprimitive(value.type):
+                            star2_fastpath_op = dict_copy_op
+                        else:
+                            star2_fastpath_op = dict_copy
+                        return self.new_tuple([], line), self.primitive_op(
+                            star2_fastpath_op, [value], line
+                        )
+
                     star2_result = self._create_dict(star2_keys, star2_values, line)
 
                 self.call_c(dict_update_in_display_op, [star2_result, value], line=line)
diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py
index 21f8a4badca3..f98bcc8ac2ec 100644
--- a/mypyc/primitives/dict_ops.py
+++ b/mypyc/primitives/dict_ops.py
@@ -53,7 +53,7 @@
 )
 
 # Construct a dictionary from another dictionary.
-function_op(
+dict_copy_op = function_op(
     name="builtins.dict",
     arg_types=[dict_rprimitive],
     return_type=dict_rprimitive,
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index f52e1af03b52..63e4ef55d3fc 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1740,12 +1740,10 @@ def g():
     r6, r7 :: dict
     r8 :: str
     r9 :: object
-    r10 :: dict
-    r11 :: i32
-    r12 :: bit
-    r13 :: tuple
-    r14 :: object
-    r15 :: tuple[int, int, int]
+    r10 :: tuple
+    r11 :: dict
+    r12 :: object
+    r13 :: tuple[int, int, int]
 L0:
     r0 = 'a'
     r1 = 'b'
@@ -1757,13 +1755,11 @@ L0:
     r7 = __main__.globals :: static
     r8 = 'f'
     r9 = CPyDict_GetItem(r7, r8)
-    r10 = PyDict_New()
-    r11 = CPyDict_UpdateInDisplay(r10, r6)
-    r12 = r11 >= 0 :: signed
-    r13 = PyTuple_Pack(0)
-    r14 = PyObject_Call(r9, r13, r10)
-    r15 = unbox(tuple[int, int, int], r14)
-    return r15
+    r10 = PyTuple_Pack(0)
+    r11 = PyDict_Copy(r6)
+    r12 = PyObject_Call(r9, r10, r11)
+    r13 = unbox(tuple[int, int, int], r12)
+    return r13
 def h():
     r0, r1 :: str
     r2, r3 :: object
@@ -3670,18 +3666,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs):
     r1 :: object
     r2 :: tuple
     r3 :: dict
-    r4 :: i32
-    r5 :: bit
-    r6 :: object
+    r4 :: object
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = r0.fn
     r2 = PyList_AsTuple(lst)
-    r3 = PyDict_New()
-    r4 = CPyDict_UpdateInDisplay(r3, kwargs)
-    r5 = r4 >= 0 :: signed
-    r6 = PyObject_Call(r1, r2, r3)
-    return r6
+    r3 = PyDict_Copy(kwargs)
+    r4 = PyObject_Call(r1, r2, r3)
+    return r4
 def deco(fn):
     fn :: object
     r0 :: __main__.deco_env
@@ -3777,18 +3769,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs):
     r1 :: object
     r2 :: tuple
     r3 :: dict
-    r4 :: i32
-    r5 :: bit
-    r6 :: object
+    r4 :: object
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = r0.fn
     r2 = PySequence_Tuple(args)
-    r3 = PyDict_New()
-    r4 = CPyDict_UpdateInDisplay(r3, kwargs)
-    r5 = r4 >= 0 :: signed
-    r6 = PyObject_Call(r1, r2, r3)
-    return r6
+    r3 = PyDict_Copy(kwargs)
+    r4 = PyObject_Call(r1, r2, r3)
+    return r4
 def deco(fn):
     fn :: object
     r0 :: __main__.deco_env
diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test
index 783492e63e47..96437a0079c9 100644
--- a/mypyc/test-data/irbuild-generics.test
+++ b/mypyc/test-data/irbuild-generics.test
@@ -167,17 +167,13 @@ def execute(func, args, kwargs):
     func :: object
     args :: tuple
     kwargs, r0 :: dict
-    r1 :: i32
-    r2 :: bit
-    r3 :: object
-    r4 :: int
+    r1 :: object
+    r2 :: int
 L0:
-    r0 = PyDict_New()
-    r1 = CPyDict_UpdateInDisplay(r0, kwargs)
-    r2 = r1 >= 0 :: signed
-    r3 = PyObject_Call(func, args, r0)
-    r4 = unbox(int, r3)
-    return r4
+    r0 = PyDict_Copy(kwargs)
+    r1 = PyObject_Call(func, args, r0)
+    r2 = unbox(int, r1)
+    return r2
 def f(x):
     x :: int
 L0:
@@ -703,10 +699,8 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs):
     r22, can_iter, r23, can_use_keys, r24, can_use_values :: list
     r25 :: object
     r26 :: dict
-    r27 :: i32
-    r28 :: bit
-    r29 :: object
-    r30 :: int
+    r27 :: object
+    r28 :: int
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = var_object_size args
@@ -758,12 +752,10 @@ L9:
     r24 = CPyDict_Values(kwargs)
     can_use_values = r24
     r25 = r0.func
-    r26 = PyDict_New()
-    r27 = CPyDict_UpdateInDisplay(r26, kwargs)
-    r28 = r27 >= 0 :: signed
-    r29 = PyObject_Call(r25, args, r26)
-    r30 = unbox(int, r29)
-    return r30
+    r26 = PyDict_Copy(kwargs)
+    r27 = PyObject_Call(r25, args, r26)
+    r28 = unbox(int, r27)
+    return r28
 def deco(func):
     func :: object
     r0 :: __main__.deco_env
diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test
index 3d7f1f3cc747..9bc5bb05c8d6 100644
--- a/mypyc/test-data/run-functions.test
+++ b/mypyc/test-data/run-functions.test
@@ -1312,3 +1312,29 @@ from native import f
 print(f(1))
 [out]
 2
+
+[case testStarArgFastPaths]
+from typing import Any, Mapping
+def fn(x: str, y: int) -> str:
+    return x * y
+def star_tuple(*args: Any) -> str:
+    return fn(*args)
+def star_list(args: list[Any]) -> str:
+    return fn(*args)
+def star_generic(args: dict[Any, Any]) -> str:
+    return fn(*args)
+def star2(**kwargs: Any) -> str:
+    return fn(**kwargs)
+def star2_generic(kwargs: Mapping[Any, Any]) -> str:
+    return fn(**kwargs)
+
+def test_star_fastpath_tuple() -> None:
+    assert star_tuple("a", 3) == "aaa"
+def test_star_fastpath_list() -> None:
+    assert star_list(["a", 3]) == "aaa"
+def test_star_fastpath_generic() -> None:
+    assert star_generic({"a": None, 3: None}) == "aaa"
+def test_star2_fastpath() -> None:
+    assert star2(x="a", y=3) == "aaa"
+def test_star2_fastpath_generic() -> None:
+    assert star2_generic({"x": "a", "y": 3}) == "aaa"

From 30a52639c7ac32e8d64906f3ba96d819f4785b3e Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra 
Date: Thu, 21 Aug 2025 06:51:08 -0700
Subject: [PATCH 1567/1617] stubtest: do not require @disjoint_base if there
 are __slots__ (#19701)

---
 mypy/stubtest.py          |  4 +++-
 mypy/test/teststubtest.py | 25 +++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 43da0518b3f9..f560049f2ec8 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -510,7 +510,9 @@ def _verify_disjoint_base(
     if stub.is_final:
         return
     is_disjoint_runtime = _is_disjoint_base(runtime)
-    if is_disjoint_runtime and not stub.is_disjoint_base:
+    # Don't complain about missing @disjoint_base if there are __slots__, because
+    # in that case we can infer that it's a disjoint base.
+    if is_disjoint_runtime and not stub.is_disjoint_base and not runtime.__dict__.get("__slots__"):
         yield Error(
             object_path,
             "is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub",
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index 69e2abe62f85..e6bc2c818164 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -1430,6 +1430,31 @@ class BytesEnum(bytes, enum.Enum):
             """,
             error=None,
         )
+        yield Case(
+            stub="""
+            class HasSlotsAndNothingElse:
+                __slots__ = ("x",)
+                x: int
+
+            class HasInheritedSlots(HasSlotsAndNothingElse):
+                pass
+
+            class HasEmptySlots:
+                __slots__ = ()
+            """,
+            runtime="""
+            class HasSlotsAndNothingElse:
+                __slots__ = ("x",)
+                x: int
+
+            class HasInheritedSlots(HasSlotsAndNothingElse):
+                pass
+
+            class HasEmptySlots:
+                __slots__ = ()
+            """,
+            error=None,
+        )
 
     @collect_cases
     def test_decorator(self) -> Iterator[Case]:

From 13fa6c3066612fcb38672c1002809dd5b08ac8e9 Mon Sep 17 00:00:00 2001
From: Stephen Morton 
Date: Thu, 21 Aug 2025 08:52:55 -0700
Subject: [PATCH 1568/1617] stubtest: get better signatures for `__init__` of C
 classes (#18259)

When an `__init__` method has the generic C-class signature, check the
underlying class for a better signature.

I was looking at `asyncio.futures.Future.__init__` and wanted to take
advantage of the better `__text_signature__` on
`asyncio.futures.Future`.

The upside is that this clears several allowlist entries and surfaced
several other cases where typeshed is currently incorrect, with no false
positives.
---
 mypy/stubtest.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index f560049f2ec8..296550f94967 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -938,6 +938,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
                     or arg.pos_only
                     or assume_positional_only
                     or arg.variable.name.strip("_") == "self"
+                    or (index == 0 and arg.variable.name.strip("_") == "cls")
                     else arg.variable.name
                 )
                 all_args.setdefault(name, []).append((arg, index))
@@ -1008,6 +1009,7 @@ def _verify_signature(
             and not stub_arg.pos_only
             and not stub_arg.variable.name.startswith("__")
             and stub_arg.variable.name.strip("_") != "self"
+            and stub_arg.variable.name.strip("_") != "cls"
             and not is_dunder(function_name, exclude_special=True)  # noisy for dunder methods
         ):
             yield (
@@ -1019,6 +1021,7 @@ def _verify_signature(
             and (stub_arg.pos_only or stub_arg.variable.name.startswith("__"))
             and not runtime_arg.name.startswith("__")
             and stub_arg.variable.name.strip("_") != "self"
+            and stub_arg.variable.name.strip("_") != "cls"
             and not is_dunder(function_name, exclude_special=True)  # noisy for dunder methods
         ):
             yield (
@@ -1662,6 +1665,71 @@ def is_read_only_property(runtime: object) -> bool:
 
 
 def safe_inspect_signature(runtime: Any) -> inspect.Signature | None:
+    if (
+        hasattr(runtime, "__name__")
+        and runtime.__name__ == "__init__"
+        and hasattr(runtime, "__text_signature__")
+        and runtime.__text_signature__ == "($self, /, *args, **kwargs)"
+        and hasattr(runtime, "__objclass__")
+        and hasattr(runtime.__objclass__, "__text_signature__")
+        and runtime.__objclass__.__text_signature__ is not None
+    ):
+        # This is an __init__ method with the generic C-class signature.
+        # In this case, the underlying class often has a better signature,
+        # which we can convert into an __init__ signature by adding in the
+        # self parameter.
+        try:
+            s = inspect.signature(runtime.__objclass__)
+
+            parameter_kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD
+            if s.parameters:
+                first_parameter = next(iter(s.parameters.values()))
+                if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
+                    parameter_kind = inspect.Parameter.POSITIONAL_ONLY
+            return s.replace(
+                parameters=[inspect.Parameter("self", parameter_kind), *s.parameters.values()]
+            )
+        except Exception:
+            pass
+
+    if (
+        hasattr(runtime, "__name__")
+        and runtime.__name__ == "__new__"
+        and hasattr(runtime, "__text_signature__")
+        and runtime.__text_signature__ == "($type, *args, **kwargs)"
+        and hasattr(runtime, "__self__")
+        and hasattr(runtime.__self__, "__text_signature__")
+        and runtime.__self__.__text_signature__ is not None
+    ):
+        # This is a __new__ method with the generic C-class signature.
+        # In this case, the underlying class often has a better signature,
+        # which we can convert into a __new__ signature by adding in the
+        # cls parameter.
+
+        # If the attached class has a valid __init__, skip recovering a
+        # signature for this __new__ method.
+        has_init = False
+        if (
+            hasattr(runtime.__self__, "__init__")
+            and hasattr(runtime.__self__.__init__, "__objclass__")
+            and runtime.__self__.__init__.__objclass__ is runtime.__self__
+        ):
+            has_init = True
+
+        if not has_init:
+            try:
+                s = inspect.signature(runtime.__self__)
+                parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
+                if s.parameters:
+                    first_parameter = next(iter(s.parameters.values()))
+                    if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
+                        parameter_kind = inspect.Parameter.POSITIONAL_ONLY
+                return s.replace(
+                    parameters=[inspect.Parameter("cls", parameter_kind), *s.parameters.values()]
+                )
+            except Exception:
+                pass
+
     try:
         try:
             return inspect.signature(runtime)

From 35a15b1302f3ca97492d5eda1c53b5034c0ba2f4 Mon Sep 17 00:00:00 2001
From: PrinceNaroliya 
Date: Fri, 22 Aug 2025 09:34:02 +0530
Subject: [PATCH 1569/1617] =?UTF-8?q?stubtest:=20correct=20"argument"=20?=
 =?UTF-8?q?=E2=86=92=20"parameter"=20terminology=20in=20error=20messages?=
 =?UTF-8?q?=20(#19707)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

## Description
This PR fixes the incorrect usage of the word **"argument"** in
`stubtest` error messages and replaces it with the correct term
**"parameter"**, as requested in issue #16508.

According to convention:
- **Parameter** → part of the function signature
- **Argument** → actual value passed when calling the function

Since `stubtest` deals only with *parameters*, this correction improves
the accuracy of the error messages.

## Related Issue
Fixes #16508
---
 mypy/stubtest.py          | 52 +++++++++++++++++++--------------------
 mypy/test/teststubtest.py |  8 +++---
 2 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 296550f94967..db902bae08c9 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -747,8 +747,8 @@ def names_approx_match(a: str, b: str) -> bool:
     if stub_arg.variable.name == "_self":
         return
     yield (
-        f'stub argument "{stub_arg.variable.name}" '
-        f'differs from runtime argument "{runtime_arg.name}"'
+        f'stub parameter "{stub_arg.variable.name}" '
+        f'differs from runtime parameter "{runtime_arg.name}"'
     )
 
 
@@ -759,8 +759,8 @@ def _verify_arg_default_value(
     if runtime_arg.default is not inspect.Parameter.empty:
         if stub_arg.kind.is_required():
             yield (
-                f'runtime argument "{runtime_arg.name}" '
-                "has a default value but stub argument does not"
+                f'runtime parameter "{runtime_arg.name}" '
+                "has a default value but stub parameter does not"
             )
         else:
             runtime_type = get_mypy_type_of_runtime_value(runtime_arg.default)
@@ -781,9 +781,9 @@ def _verify_arg_default_value(
                 and not is_subtype_helper(runtime_type, stub_type)
             ):
                 yield (
-                    f'runtime argument "{runtime_arg.name}" '
+                    f'runtime parameter "{runtime_arg.name}" '
                     f"has a default value of type {runtime_type}, "
-                    f"which is incompatible with stub argument type {stub_type}"
+                    f"which is incompatible with stub parameter type {stub_type}"
                 )
             if stub_arg.initializer is not None:
                 stub_default = evaluate_expression(stub_arg.initializer)
@@ -807,15 +807,15 @@ def _verify_arg_default_value(
                             defaults_match = False
                     if not defaults_match:
                         yield (
-                            f'runtime argument "{runtime_arg.name}" '
+                            f'runtime parameter "{runtime_arg.name}" '
                             f"has a default value of {runtime_arg.default!r}, "
-                            f"which is different from stub argument default {stub_default!r}"
+                            f"which is different from stub parameter default {stub_default!r}"
                         )
     else:
         if stub_arg.kind.is_optional():
             yield (
-                f'stub argument "{stub_arg.variable.name}" has a default value '
-                f"but runtime argument does not"
+                f'stub parameter "{stub_arg.variable.name}" has a default value '
+                f"but runtime parameter does not"
             )
 
 
@@ -1013,7 +1013,7 @@ def _verify_signature(
             and not is_dunder(function_name, exclude_special=True)  # noisy for dunder methods
         ):
             yield (
-                f'stub argument "{stub_arg.variable.name}" should be positional-only '
+                f'stub parameter "{stub_arg.variable.name}" should be positional-only '
                 f'(add "/", e.g. "{runtime_arg.name}, /")'
             )
         if (
@@ -1025,7 +1025,7 @@ def _verify_signature(
             and not is_dunder(function_name, exclude_special=True)  # noisy for dunder methods
         ):
             yield (
-                f'stub argument "{stub_arg.variable.name}" should be positional or keyword '
+                f'stub parameter "{stub_arg.variable.name}" should be positional or keyword '
                 '(remove "/")'
             )
 
@@ -1040,28 +1040,28 @@ def _verify_signature(
                 # If the variable is in runtime.kwonly, it's just mislabelled as not a
                 # keyword-only argument
                 if stub_arg.variable.name not in runtime.kwonly:
-                    msg = f'runtime does not have argument "{stub_arg.variable.name}"'
+                    msg = f'runtime does not have parameter "{stub_arg.variable.name}"'
                     if runtime.varkw is not None:
                         msg += ". Maybe you forgot to make it keyword-only in the stub?"
                     yield msg
                 else:
-                    yield f'stub argument "{stub_arg.variable.name}" is not keyword-only'
+                    yield f'stub parameter "{stub_arg.variable.name}" is not keyword-only'
             if stub.varpos is not None:
-                yield f'runtime does not have *args argument "{stub.varpos.variable.name}"'
+                yield f'runtime does not have *args parameter "{stub.varpos.variable.name}"'
     elif len(stub.pos) < len(runtime.pos):
         for runtime_arg in runtime.pos[len(stub.pos) :]:
             if runtime_arg.name not in stub.kwonly:
                 if not _is_private_parameter(runtime_arg):
-                    yield f'stub does not have argument "{runtime_arg.name}"'
+                    yield f'stub does not have parameter "{runtime_arg.name}"'
             else:
-                yield f'runtime argument "{runtime_arg.name}" is not keyword-only'
+                yield f'runtime parameter "{runtime_arg.name}" is not keyword-only'
 
     # Checks involving *args
     if len(stub.pos) <= len(runtime.pos) or runtime.varpos is None:
         if stub.varpos is None and runtime.varpos is not None:
-            yield f'stub does not have *args argument "{runtime.varpos.name}"'
+            yield f'stub does not have *args parameter "{runtime.varpos.name}"'
         if stub.varpos is not None and runtime.varpos is None:
-            yield f'runtime does not have *args argument "{stub.varpos.variable.name}"'
+            yield f'runtime does not have *args parameter "{stub.varpos.variable.name}"'
 
     # Check keyword-only args
     for arg in sorted(set(stub.kwonly) & set(runtime.kwonly)):
@@ -1080,9 +1080,9 @@ def _verify_signature(
             if arg in {runtime_arg.name for runtime_arg in runtime.pos}:
                 # Don't report this if we've reported it before
                 if arg not in {runtime_arg.name for runtime_arg in runtime.pos[len(stub.pos) :]}:
-                    yield f'runtime argument "{arg}" is not keyword-only'
+                    yield f'runtime parameter "{arg}" is not keyword-only'
             else:
-                yield f'runtime does not have argument "{arg}"'
+                yield f'runtime does not have parameter "{arg}"'
     for arg in sorted(set(runtime.kwonly) - set(stub.kwonly)):
         if arg in {stub_arg.variable.name for stub_arg in stub.pos}:
             # Don't report this if we've reported it before
@@ -1090,10 +1090,10 @@ def _verify_signature(
                 runtime.varpos is None
                 and arg in {stub_arg.variable.name for stub_arg in stub.pos[len(runtime.pos) :]}
             ):
-                yield f'stub argument "{arg}" is not keyword-only'
+                yield f'stub parameter "{arg}" is not keyword-only'
         else:
             if not _is_private_parameter(runtime.kwonly[arg]):
-                yield f'stub does not have argument "{arg}"'
+                yield f'stub does not have parameter "{arg}"'
 
     # Checks involving **kwargs
     if stub.varkw is None and runtime.varkw is not None:
@@ -1103,9 +1103,9 @@ def _verify_signature(
         stub_pos_names = {stub_arg.variable.name for stub_arg in stub.pos}
         # Ideally we'd do a strict subset check, but in practice the errors from that aren't useful
         if not set(runtime.kwonly).issubset(set(stub.kwonly) | stub_pos_names):
-            yield f'stub does not have **kwargs argument "{runtime.varkw.name}"'
+            yield f'stub does not have **kwargs parameter "{runtime.varkw.name}"'
     if stub.varkw is not None and runtime.varkw is None:
-        yield f'runtime does not have **kwargs argument "{stub.varkw.variable.name}"'
+        yield f'runtime does not have **kwargs parameter "{stub.varkw.variable.name}"'
 
 
 def _is_private_parameter(arg: inspect.Parameter) -> bool:
@@ -1425,7 +1425,7 @@ def apply_decorator_to_funcitem(
         if decorator.fullname == "builtins.classmethod":
             if func.arguments[0].variable.name not in ("cls", "mcs", "metacls"):
                 raise StubtestFailure(
-                    f"unexpected class argument name {func.arguments[0].variable.name!r} "
+                    f"unexpected class parameter name {func.arguments[0].variable.name!r} "
                     f"in {dec.fullname}"
                 )
             # FuncItem is written so that copy.copy() actually works, even when compiled
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index e6bc2c818164..c9404e206e4f 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -2583,8 +2583,8 @@ def test_output(self) -> None:
             options=[],
         )
         expected = (
-            f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub argument "number" differs '
-            'from runtime argument "num"\n'
+            f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub parameter "number" differs '
+            'from runtime parameter "num"\n'
             f"Stub: in file {TEST_MODULE_NAME}.pyi:1\n"
             "def (number: builtins.int, text: builtins.str)\n"
             f"Runtime: in file {TEST_MODULE_NAME}.py:1\ndef (num, text)\n\n"
@@ -2599,7 +2599,9 @@ def test_output(self) -> None:
         )
         expected = (
             "{}.bad is inconsistent, "
-            'stub argument "number" differs from runtime argument "num"\n'.format(TEST_MODULE_NAME)
+            'stub parameter "number" differs from runtime parameter "num"\n'.format(
+                TEST_MODULE_NAME
+            )
         )
         assert output == expected
 

From fa7fa7f7a6a632ff9fe37b3e7e659b97bb8a5e7b Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra 
Date: Fri, 22 Aug 2025 12:28:43 -0700
Subject: [PATCH 1570/1617] stubtest: flag redundant @disjoint_base decorators
 (#19715)

Co-authored-by: Alex Waygood 
---
 mypy/stubtest.py          | 53 +++++++++++++++++++++++++++--------
 mypy/test/teststubtest.py | 58 +++++++++++++++++++++++++++++++++------
 2 files changed, 91 insertions(+), 20 deletions(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index db902bae08c9..482a14984950 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -506,13 +506,16 @@ def _is_disjoint_base(typ: type[object]) -> bool:
 def _verify_disjoint_base(
     stub: nodes.TypeInfo, runtime: type[object], object_path: list[str]
 ) -> Iterator[Error]:
-    # If it's final, doesn't matter whether it's a disjoint base or not
-    if stub.is_final:
-        return
     is_disjoint_runtime = _is_disjoint_base(runtime)
     # Don't complain about missing @disjoint_base if there are __slots__, because
     # in that case we can infer that it's a disjoint base.
-    if is_disjoint_runtime and not stub.is_disjoint_base and not runtime.__dict__.get("__slots__"):
+    if (
+        is_disjoint_runtime
+        and not stub.is_disjoint_base
+        and not runtime.__dict__.get("__slots__")
+        and not stub.is_final
+        and not (stub.is_enum and stub.enum_members)
+    ):
         yield Error(
             object_path,
             "is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub",
@@ -520,14 +523,40 @@ def _verify_disjoint_base(
             runtime,
             stub_desc=repr(stub),
         )
-    elif not is_disjoint_runtime and stub.is_disjoint_base:
-        yield Error(
-            object_path,
-            "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime",
-            stub,
-            runtime,
-            stub_desc=repr(stub),
-        )
+    elif stub.is_disjoint_base:
+        if not is_disjoint_runtime:
+            yield Error(
+                object_path,
+                "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime",
+                stub,
+                runtime,
+                stub_desc=repr(stub),
+            )
+        if runtime.__dict__.get("__slots__"):
+            yield Error(
+                object_path,
+                "is marked as @disjoint_base, but also has slots; add __slots__ instead",
+                stub,
+                runtime,
+                stub_desc=repr(stub),
+            )
+        elif stub.is_final:
+            yield Error(
+                object_path,
+                "is marked as @disjoint_base, but also marked as @final; remove @disjoint_base",
+                stub,
+                runtime,
+                stub_desc=repr(stub),
+            )
+        elif stub.is_enum and stub.enum_members:
+            yield Error(
+                object_path,
+                "is marked as @disjoint_base, but is an enum with members, which is implicitly final; "
+                "remove @disjoint_base",
+                stub,
+                runtime,
+                stub_desc=repr(stub),
+            )
 
 
 def _verify_metaclass(
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index c9404e206e4f..2bf071d34d48 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -1407,14 +1407,9 @@ def spam(x=Flags4(0)): pass
             stub="""
             import sys
             from typing import Final, Literal
-            from typing_extensions import disjoint_base
-            if sys.version_info >= (3, 12):
-                class BytesEnum(bytes, enum.Enum):
-                    a = b'foo'
-            else:
-                @disjoint_base
-                class BytesEnum(bytes, enum.Enum):
-                    a = b'foo'
+            class BytesEnum(bytes, enum.Enum):
+                a = b'foo'
+
             FOO: Literal[BytesEnum.a]
             BAR: Final = BytesEnum.a
             BAZ: BytesEnum
@@ -1698,6 +1693,53 @@ def __next__(self) -> object: ...
             """,
             error=None,
         )
+        yield Case(
+            runtime="""
+            class IsDisjointBaseBecauseItHasSlots:
+                __slots__ = ("a",)
+                a: int
+            """,
+            stub="""
+            from typing_extensions import disjoint_base
+
+            @disjoint_base
+            class IsDisjointBaseBecauseItHasSlots:
+                a: int
+            """,
+            error="test_module.IsDisjointBaseBecauseItHasSlots",
+        )
+        yield Case(
+            runtime="""
+            class IsFinalSoDisjointBaseIsRedundant: ...
+            """,
+            stub="""
+            from typing_extensions import disjoint_base, final
+
+            @final
+            @disjoint_base
+            class IsFinalSoDisjointBaseIsRedundant: ...
+            """,
+            error="test_module.IsFinalSoDisjointBaseIsRedundant",
+        )
+        yield Case(
+            runtime="""
+            import enum
+
+            class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum):
+                A = 1
+                B = 2
+            """,
+            stub="""
+            from typing_extensions import disjoint_base
+            import enum
+
+            @disjoint_base
+            class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum):
+                A = 1
+                B = 2
+            """,
+            error="test_module.IsEnumWithMembersSoDisjointBaseIsRedundant",
+        )
 
     @collect_cases
     def test_has_runtime_final_decorator(self) -> Iterator[Case]:

From 01e2a8c4a6851e3e5eff77b96f0f4a5d0b41079d Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Fri, 22 Aug 2025 22:55:44 +0200
Subject: [PATCH 1571/1617] Use union for captured vars in or pattern (#19710)

Mypy creates a union type for the pattern subject in an or pattern
already. Use union for captured variables as well.
---
 mypy/checkpattern.py                | 2 +-
 test-data/unit/check-python310.test | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 48840466f0d8..2b9e823c55b7 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -192,7 +192,7 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType:
         for capture_list in capture_types.values():
             typ = UninhabitedType()
             for _, other in capture_list:
-                typ = join_types(typ, other)
+                typ = make_simplified_union([typ, other])
 
             captures[capture_list[0][0]] = typ
 
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index f264167cb067..a4d6188136c6 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1204,13 +1204,13 @@ match m:
     case 1 | "foo":
         reveal_type(m)  # N: Revealed type is "Union[Literal[1], Literal['foo']]"
 
-[case testMatchOrPatterCapturesMissing]
+[case testMatchOrPatternCapturesMissing]
 from typing import List
 m: List[int]
 
 match m:
     case [x, y] | list(x):  # E: Alternative patterns bind different names
-        reveal_type(x)  # N: Revealed type is "builtins.object"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.list[builtins.int]]"
         reveal_type(y)  # N: Revealed type is "builtins.int"
 [builtins fixtures/list.pyi]
 
@@ -1219,7 +1219,7 @@ m: object
 
 match m:
     case list(x) | dict(x):
-        reveal_type(x)  # N: Revealed type is "typing.Iterable[Any]"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.list[Any], builtins.dict[Any, Any]]"
 [builtins fixtures/dict.pyi]
 
 -- Interactions --
@@ -1405,7 +1405,7 @@ m: Union[str, bytes, int]
 
 match m:
     case str(a) | bytes(a):
-        reveal_type(a)  # N: Revealed type is "builtins.object"
+        reveal_type(a)  # N: Revealed type is "Union[builtins.str, builtins.bytes]"
         reveal_type(m)  # N: Revealed type is "Union[builtins.str, builtins.bytes]"
     case b:
         reveal_type(b)  # N: Revealed type is "builtins.int"

From ac4cacbba3c8511bb591fd11f813de66e79a7aa7 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Fri, 22 Aug 2025 17:29:26 -0700
Subject: [PATCH 1572/1617] Somewhat better support for isinstance on old-style
 unions (#19714)

Partially fixes #17680 , remainder of the issue should probably be fixed
in the isinstance stub
---
 mypy/checkexpr.py                |  5 ++++-
 test-data/unit/check-unions.test | 27 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 9752a5e68638..88b3005b1376 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -582,7 +582,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
                     and not node.node.no_args
                     and not (
                         isinstance(union_target := get_proper_type(node.node.target), UnionType)
-                        and union_target.uses_pep604_syntax
+                        and (
+                            union_target.uses_pep604_syntax
+                            or self.chk.options.python_version >= (3, 10)
+                        )
                     )
                 ):
                     self.msg.type_arguments_not_allowed(e)
diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test
index f8c894a7957b..398b007ce57d 100644
--- a/test-data/unit/check-unions.test
+++ b/test-data/unit/check-unions.test
@@ -1345,3 +1345,30 @@ x: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11]
 y: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, None]
 x = y  # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, <6 more items>, None]", variable has type "Union[C1, C2, C3, C4, C5, <6 more items>]") \
        # N: Item in the first union not in the second: "None"
+
+[case testTypeAliasWithOldUnionIsInstance]
+# flags: --python-version 3.10
+from typing import Union
+SimpleAlias = Union[int, str]
+
+def foo(x: Union[int, str, tuple]):
+    # TODO: fix the typeshed stub for isinstance
+    if isinstance(x, SimpleAlias):  # E: Argument 2 to "isinstance" has incompatible type ""; expected "type"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
+    else:
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
+[builtins fixtures/tuple.pyi]
+
+
+[case testTypeAliasWithOldUnionIsInstancePython39]
+# flags: --python-version 3.9
+from typing import Union
+SimpleAlias = Union[int, str]
+
+def foo(x: Union[int, str, tuple]):
+    if isinstance(x, SimpleAlias):  # E: Parameterized generics cannot be used with class or instance checks \
+                                    # E: Argument 2 to "isinstance" has incompatible type ""; expected "type"
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
+    else:
+        reveal_type(x)  # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
+[builtins fixtures/tuple.pyi]

From f51b6995353626a56b8ae7cf078ef28119ae5eb5 Mon Sep 17 00:00:00 2001
From: Stephen Morton 
Date: Fri, 22 Aug 2025 18:12:26 -0700
Subject: [PATCH 1573/1617] stubtest: handle overloads with mixed pos-only
 params (#18287)

Fixes #17023

Stubtest should only mangle positional-only parameter names if they're
positional-only in all branches of the overload. The signatures get
really ugly and wrong otherwise.

I'm not sure if I did the new `test_overload_signature` in the best way.
I couldn't figure out a way to get covert a string into a
`nodes.OverloadedFuncDef` object with any of the techniques in existing
tests in `teststubtest.py`. Maybe the new test case is sufficient, but I
wanted to test the signature generation directly.
---
 mypy/stubtest.py          | 30 +++++++++++++++++++--------
 mypy/test/teststubtest.py | 43 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 482a14984950..884a442d15fb 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -954,22 +954,36 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
         # For most dunder methods, just assume all args are positional-only
         assume_positional_only = is_dunder(stub.name, exclude_special=True)
 
-        all_args: dict[str, list[tuple[nodes.Argument, int]]] = {}
+        is_arg_pos_only: defaultdict[str, set[bool]] = defaultdict(set)
         for func in map(_resolve_funcitem_from_decorator, stub.items):
             assert func is not None, "Failed to resolve decorated overload"
             args = maybe_strip_cls(stub.name, func.arguments)
             for index, arg in enumerate(args):
-                # For positional-only args, we allow overloads to have different names for the same
-                # argument. To accomplish this, we just make up a fake index-based name.
-                name = (
-                    f"__{index}"
-                    if arg.variable.name.startswith("__")
+                if (
+                    arg.variable.name.startswith("__")
                     or arg.pos_only
                     or assume_positional_only
                     or arg.variable.name.strip("_") == "self"
                     or (index == 0 and arg.variable.name.strip("_") == "cls")
-                    else arg.variable.name
-                )
+                ):
+                    is_arg_pos_only[arg.variable.name].add(True)
+                else:
+                    is_arg_pos_only[arg.variable.name].add(False)
+
+        all_args: dict[str, list[tuple[nodes.Argument, int]]] = {}
+        for func in map(_resolve_funcitem_from_decorator, stub.items):
+            assert func is not None, "Failed to resolve decorated overload"
+            args = maybe_strip_cls(stub.name, func.arguments)
+            for index, arg in enumerate(args):
+                # For positional-only args, we allow overloads to have different names for the same
+                # argument. To accomplish this, we just make up a fake index-based name.
+                # We can only use the index-based name if the argument is always
+                # positional only. Sometimes overloads have an arg as positional-only
+                # in some but not all branches of the overload.
+                name = arg.variable.name
+                if is_arg_pos_only[name] == {True}:
+                    name = f"__{index}"
+
                 all_args.setdefault(name, []).append((arg, index))
 
         def get_position(arg_name: str) -> int:
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index 2bf071d34d48..ee69d2077f0f 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -13,7 +13,11 @@
 from typing import Any, Callable
 
 import mypy.stubtest
+from mypy import build, nodes
+from mypy.modulefinder import BuildSource
+from mypy.options import Options
 from mypy.stubtest import parse_options, test_stubs
+from mypy.test.config import test_temp_dir
 from mypy.test.data import root_dir
 
 
@@ -158,6 +162,14 @@ def __invert__(self: _T) -> _T: pass
 """
 
 
+def build_helper(source: str) -> build.BuildResult:
+    return build.build(
+        sources=[BuildSource("main.pyi", None, textwrap.dedent(source))],
+        options=Options(),
+        alt_lib_path=test_temp_dir,
+    )
+
+
 def run_stubtest_with_stderr(
     stub: str, runtime: str, options: list[str], config_file: str | None = None
 ) -> tuple[str, str]:
@@ -842,6 +854,18 @@ def f2(self, *a) -> int: ...
             """,
             error=None,
         )
+        yield Case(
+            stub="""
+            @overload
+            def f(a: int) -> int: ...
+            @overload
+            def f(a: int, b: str, /) -> str: ...
+            """,
+            runtime="""
+            def f(a, *args): ...
+            """,
+            error=None,
+        )
 
     @collect_cases
     def test_property(self) -> Iterator[Case]:
@@ -2790,6 +2814,25 @@ def test_builtin_signature_with_unrepresentable_default(self) -> None:
             == "def (self, sep = ..., bytes_per_sep = ...)"
         )
 
+    def test_overload_signature(self) -> None:
+        # The same argument as both positional-only and pos-or-kw in
+        # different overloads previously produced incorrect signatures
+        source = """
+        from typing import overload
+        @overload
+        def myfunction(arg: int) -> None: ...
+        @overload
+        def myfunction(arg: str, /) -> None: ...
+        """
+        result = build_helper(source)
+        stub = result.files["__main__"].names["myfunction"].node
+        assert isinstance(stub, nodes.OverloadedFuncDef)
+        sig = mypy.stubtest.Signature.from_overloadedfuncdef(stub)
+        if sys.version_info >= (3, 10):
+            assert str(sig) == "def (arg: builtins.int | builtins.str)"
+        else:
+            assert str(sig) == "def (arg: Union[builtins.int, builtins.str])"
+
     def test_config_file(self) -> None:
         runtime = "temp = 5\n"
         stub = "from decimal import Decimal\ntemp: Decimal\n"

From 15b8ca967cc6187effcab23e6613da2db4546584 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Sat, 23 Aug 2025 04:17:58 +0200
Subject: [PATCH 1574/1617] Remove unnecessary error message for match class
 patterns (#19708)

Remove `Cannot determine type of ...` error for class patterns if the
class to match cannot be resolved. In these cases a `Name ... is not
defined` error is already emitted. The captured variable should simply
be inferred as `Any`.

Previously, this was especially an issue for class matches to a class
from an untyped library together with a MemberExpr. An example from
pylint / astroid which shouldn't emit any errors:
```py
from typing import Any
from astroid import nodes

def func(var: Any) -> None:
    match var:
        case nodes.Assign(targets=t):
            reveal_type(t)  # Any
```
---
 mypy/checkpattern.py                | 8 ++++----
 test-data/unit/check-python310.test | 9 +++++----
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 2b9e823c55b7..fc00e9f20291 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -539,12 +539,12 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
         #
         type_info = o.class_ref.node
         if type_info is None:
-            return PatternType(AnyType(TypeOfAny.from_error), AnyType(TypeOfAny.from_error), {})
-        if isinstance(type_info, TypeAlias) and not type_info.no_args:
+            typ: Type = AnyType(TypeOfAny.from_error)
+        elif isinstance(type_info, TypeAlias) and not type_info.no_args:
             self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o)
             return self.early_non_match()
-        if isinstance(type_info, TypeInfo):
-            typ: Type = fill_typevars_with_any(type_info)
+        elif isinstance(type_info, TypeInfo):
+            typ = fill_typevars_with_any(type_info)
         elif isinstance(type_info, TypeAlias):
             typ = type_info.target
         elif (
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index a4d6188136c6..b49bb13d520f 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -721,13 +721,14 @@ m: object
 match m:
     case xyz(y):  # E: Name "xyz" is not defined
         reveal_type(m)  # N: Revealed type is "Any"
-        reveal_type(y)  # E: Cannot determine type of "y" \
-                        # N: Revealed type is "Any"
+        reveal_type(y)  # N: Revealed type is "Any"
 
 match m:
     case xyz(z=x):  # E: Name "xyz" is not defined
-        reveal_type(x)  # E: Cannot determine type of "x" \
-                        # N: Revealed type is "Any"
+        reveal_type(x)  # N: Revealed type is "Any"
+    case (xyz1() as n) | (xyz2(attr=n)):  # E: Name "xyz1" is not defined \
+                                          # E: Name "xyz2" is not defined
+        reveal_type(n)  # N: Revealed type is "Any"
 
 [case testMatchClassPatternCaptureDataclass]
 from dataclasses import dataclass

From 116b92bae7b5dbf5e6bd36fd9b0c6804973e5554 Mon Sep 17 00:00:00 2001
From: Stephen Morton 
Date: Sat, 23 Aug 2025 06:11:52 -0700
Subject: [PATCH 1575/1617] More detailed checking of type objects in stubtest
 (#18251)

This uses `checkmember.type_object_type` and context to produce better
types of type objects.
---
 mypy/stubtest.py          | 69 +++++++++++++++++++++++++++++++++------
 mypy/test/teststubtest.py | 25 ++++++++++++++
 2 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 884a442d15fb..ee15fed81b4b 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -34,6 +34,9 @@
 from typing_extensions import get_origin, is_typeddict
 
 import mypy.build
+import mypy.checkexpr
+import mypy.checkmember
+import mypy.erasetype
 import mypy.modulefinder
 import mypy.nodes
 import mypy.state
@@ -792,7 +795,11 @@ def _verify_arg_default_value(
                 "has a default value but stub parameter does not"
             )
         else:
-            runtime_type = get_mypy_type_of_runtime_value(runtime_arg.default)
+            type_context = stub_arg.variable.type
+            runtime_type = get_mypy_type_of_runtime_value(
+                runtime_arg.default, type_context=type_context
+            )
+
             # Fallback to the type annotation type if var type is missing. The type annotation
             # is an UnboundType, but I don't know enough to know what the pros and cons here are.
             # UnboundTypes have ugly question marks following them, so default to var type.
@@ -1247,7 +1254,7 @@ def verify_var(
     ):
         yield Error(object_path, "is read-only at runtime but not in the stub", stub, runtime)
 
-    runtime_type = get_mypy_type_of_runtime_value(runtime)
+    runtime_type = get_mypy_type_of_runtime_value(runtime, type_context=stub.type)
     if (
         runtime_type is not None
         and stub.type is not None
@@ -1832,7 +1839,18 @@ def is_subtype_helper(left: mypy.types.Type, right: mypy.types.Type) -> bool:
         return mypy.subtypes.is_subtype(left, right)
 
 
-def get_mypy_type_of_runtime_value(runtime: Any) -> mypy.types.Type | None:
+def get_mypy_node_for_name(module: str, type_name: str) -> mypy.nodes.SymbolNode | None:
+    stub = get_stub(module)
+    if stub is None:
+        return None
+    if type_name not in stub.names:
+        return None
+    return stub.names[type_name].node
+
+
+def get_mypy_type_of_runtime_value(
+    runtime: Any, type_context: mypy.types.Type | None = None
+) -> mypy.types.Type | None:
     """Returns a mypy type object representing the type of ``runtime``.
 
     Returns None if we can't find something that works.
@@ -1893,14 +1911,45 @@ def anytype() -> mypy.types.AnyType:
             is_ellipsis_args=True,
         )
 
-    # Try and look up a stub for the runtime object
-    stub = get_stub(type(runtime).__module__)
-    if stub is None:
-        return None
-    type_name = type(runtime).__name__
-    if type_name not in stub.names:
+    skip_type_object_type = False
+    if type_context:
+        # Don't attempt to process the type object when context is generic
+        # This is related to issue #3737
+        type_context = mypy.types.get_proper_type(type_context)
+        # Callable types with a generic return value
+        if isinstance(type_context, mypy.types.CallableType):
+            if isinstance(type_context.ret_type, mypy.types.TypeVarType):
+                skip_type_object_type = True
+        # Type[x] where x is generic
+        if isinstance(type_context, mypy.types.TypeType):
+            if isinstance(type_context.item, mypy.types.TypeVarType):
+                skip_type_object_type = True
+
+    if isinstance(runtime, type) and not skip_type_object_type:
+
+        def _named_type(name: str) -> mypy.types.Instance:
+            parts = name.rsplit(".", maxsplit=1)
+            node = get_mypy_node_for_name(parts[0], parts[1])
+            assert isinstance(node, nodes.TypeInfo)
+            any_type = mypy.types.AnyType(mypy.types.TypeOfAny.special_form)
+            return mypy.types.Instance(node, [any_type] * len(node.defn.type_vars))
+
+        # Try and look up a stub for the runtime object itself
+        # The logic here is similar to ExpressionChecker.analyze_ref_expr
+        type_info = get_mypy_node_for_name(runtime.__module__, runtime.__name__)
+        if isinstance(type_info, nodes.TypeInfo):
+            result: mypy.types.Type | None = None
+            result = mypy.typeops.type_object_type(type_info, _named_type)
+            if mypy.checkexpr.is_type_type_context(type_context):
+                # This is the type in a type[] expression, so substitute type
+                # variables with Any.
+                result = mypy.erasetype.erase_typevars(result)
+            return result
+
+    # Try and look up a stub for the runtime object's type
+    type_info = get_mypy_node_for_name(type(runtime).__module__, type(runtime).__name__)
+    if type_info is None:
         return None
-    type_info = stub.names[type_name].node
     if isinstance(type_info, nodes.Var):
         return type_info.type
     if not isinstance(type_info, nodes.TypeInfo):
diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py
index ee69d2077f0f..28263e20099d 100644
--- a/mypy/test/teststubtest.py
+++ b/mypy/test/teststubtest.py
@@ -2636,6 +2636,31 @@ class _X1: ...
             error=None,
         )
 
+    @collect_cases
+    def test_type_default_protocol(self) -> Iterator[Case]:
+        yield Case(
+            stub="""
+            from typing import Protocol
+
+            class _FormatterClass(Protocol):
+                def __call__(self, *, prog: str) -> HelpFormatter: ...
+
+            class ArgumentParser:
+                def __init__(self, formatter_class: _FormatterClass = ...) -> None: ...
+
+            class HelpFormatter:
+                def __init__(self, prog: str, indent_increment: int = 2) -> None: ...
+            """,
+            runtime="""
+            class HelpFormatter:
+                def __init__(self, prog, indent_increment=2) -> None: ...
+
+            class ArgumentParser:
+                def __init__(self, formatter_class=HelpFormatter): ...
+            """,
+            error=None,
+        )
+
 
 def remove_color_code(s: str) -> str:
     return re.sub("\\x1b.*?m", "", s)  # this works!

From 1f89b1a04ddf39c6a346591535a080f719399e2e Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Mon, 25 Aug 2025 15:56:29 -0400
Subject: [PATCH 1576/1617] Include base mypy requirements in docs requirements
 (#19727)

Attempt to fix #19726

After #19062, the docs build script attempts to import `mypy.main`, so
building the docs now requires all of mypy's base requirements to be
present as well.
---
 docs/requirements-docs.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
index 747f376a8f5a..09062a635e63 100644
--- a/docs/requirements-docs.txt
+++ b/docs/requirements-docs.txt
@@ -1,3 +1,4 @@
+-r ../mypy-requirements.txt
 sphinx>=8.1.0
 furo>=2022.3.4
 myst-parser>=4.0.0

From ffe2db864e3ea4a079b48892cd98ba2eabeda9a3 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Mon, 25 Aug 2025 21:57:46 +0200
Subject: [PATCH 1577/1617] Update dependencies (#19720)

lxml 6.0.1 has wheels for Python 3.14 now.
https://pypi.org/project/lxml/6.0.1/#files
---
 test-requirements.in  |  2 +-
 test-requirements.txt | 18 +++++++++---------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/test-requirements.in b/test-requirements.in
index 666dd9fc082c..df074965a1e8 100644
--- a/test-requirements.in
+++ b/test-requirements.in
@@ -5,7 +5,7 @@
 -r build-requirements.txt
 attrs>=18.0
 filelock>=3.3.0
-lxml>=5.3.0; python_version<'3.14'
+lxml>=5.3.0; python_version<'3.15'
 psutil>=4.0
 pytest>=8.1.0
 pytest-xdist>=1.34.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 11ac675eca15..521208c5aa27 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,5 +1,5 @@
 #
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.13
 # by the following command:
 #
 #    pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in
@@ -8,21 +8,21 @@ attrs==25.3.0
     # via -r test-requirements.in
 cfgv==3.4.0
     # via pre-commit
-coverage==7.10.1
+coverage==7.10.5
     # via pytest-cov
 distlib==0.4.0
     # via virtualenv
 execnet==2.1.1
     # via pytest-xdist
-filelock==3.18.0
+filelock==3.19.1
     # via
     #   -r test-requirements.in
     #   virtualenv
-identify==2.6.12
+identify==2.6.13
     # via pre-commit
 iniconfig==2.1.0
     # via pytest
-lxml==6.0.0 ; python_version < "3.14"
+lxml==6.0.1 ; python_version < "3.15"
     # via -r test-requirements.in
 mypy-extensions==1.1.0
     # via -r mypy-requirements.txt
@@ -38,7 +38,7 @@ pluggy==1.6.0
     # via
     #   pytest
     #   pytest-cov
-pre-commit==4.2.0
+pre-commit==4.3.0
     # via -r test-requirements.in
 psutil==7.0.0
     # via -r test-requirements.in
@@ -57,13 +57,13 @@ pyyaml==6.0.2
     # via pre-commit
 tomli==2.2.1
     # via -r test-requirements.in
-types-psutil==7.0.0.20250601
+types-psutil==7.0.0.20250822
     # via -r build-requirements.txt
-types-setuptools==80.9.0.20250529
+types-setuptools==80.9.0.20250822
     # via -r build-requirements.txt
 typing-extensions==4.14.1
     # via -r mypy-requirements.txt
-virtualenv==20.32.0
+virtualenv==20.34.0
     # via pre-commit
 
 # The following packages are considered to be unsafe in a requirements file:

From 881a35a6761b3b9a916ca159768ec68c3e745a05 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 26 Aug 2025 01:17:15 +0200
Subject: [PATCH 1578/1617] Revert "Enable colored output for argparse help in
 Python 3.14 (#19021)" (#19721)

Reverts #19021

After the PR was merged, `color` was changed to `true` for Python 3.14.
Setting it manually is no longer necessary.
https://github.com/python/cpython/pull/136809
https://docs.python.org/3.14/library/argparse.html#color
---
 mypy/dmypy/client.py | 3 ---
 mypy/main.py         | 2 --
 mypy/stubgen.py      | 2 --
 mypy/stubtest.py     | 2 --
 4 files changed, 9 deletions(-)

diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
index b34e9bf8ced2..3db47f80d01b 100644
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -36,9 +36,6 @@ def __init__(self, prog: str, **kwargs: Any) -> None:
 parser = argparse.ArgumentParser(
     prog="dmypy", description="Client for mypy daemon mode", fromfile_prefix_chars="@"
 )
-if sys.version_info >= (3, 14):
-    parser.color = True  # Set as init arg in 3.14
-
 parser.set_defaults(action=None)
 parser.add_argument(
     "--status-file", default=DEFAULT_STATUS_FILE, help="status file to retrieve daemon details"
diff --git a/mypy/main.py b/mypy/main.py
index 0f70eb41bb14..ad257bab6996 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -485,8 +485,6 @@ def define_options(
         stdout=stdout,
         stderr=stderr,
     )
-    if sys.version_info >= (3, 14):
-        parser.color = True  # Set as init arg in 3.14
 
     strict_flag_names: list[str] = []
     strict_flag_assignments: list[tuple[str, bool]] = []
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index ece22ba235bf..60fbd7f43c0f 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -1899,8 +1899,6 @@ def parse_options(args: list[str]) -> Options:
     parser = argparse.ArgumentParser(
         prog="stubgen", usage=HEADER, description=DESCRIPTION, fromfile_prefix_chars="@"
     )
-    if sys.version_info >= (3, 14):
-        parser.color = True  # Set as init arg in 3.14
 
     parser.add_argument(
         "--ignore-errors",
diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index ee15fed81b4b..31b3fd20b002 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -2348,8 +2348,6 @@ def parse_options(args: list[str]) -> _Arguments:
     parser = argparse.ArgumentParser(
         description="Compares stubs to objects introspected from the runtime."
     )
-    if sys.version_info >= (3, 14):
-        parser.color = True  # Set as init arg in 3.14
     parser.add_argument("modules", nargs="*", help="Modules to test")
     parser.add_argument(
         "--concise",

From 50fc847a976484d02cf6132cec9dbce2d0e545d2 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Tue, 26 Aug 2025 02:37:56 +0200
Subject: [PATCH 1579/1617] Omit errors for class pattern matches against
 object (#19709)

Since the class pattern matches any subclass, it can also be used to
check whether the matched object has a specific attribute. Mypy should
not emit an error for it.
```py
match m:
    case object(foo=_):
        m.foo
```

Using `object` for it is recommended in [PEP
635](https://peps.python.org/pep-0635/#history-and-context) and more
prominently in the precursor [PEP
622](https://peps.python.org/pep-0622/#class-patterns).
---
 mypy/checker_shared.py              |  4 ++++
 mypy/checkpattern.py                | 15 +++++++++------
 test-data/unit/check-python310.test |  9 +++++++++
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py
index 2a8fbdb0c9f1..0014d2c6fc88 100644
--- a/mypy/checker_shared.py
+++ b/mypy/checker_shared.py
@@ -272,6 +272,10 @@ def checking_await_set(self) -> Iterator[None]:
     def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None:
         raise NotImplementedError
 
+    @abstractmethod
+    def add_any_attribute_to_type(self, typ: Type, name: str) -> Type:
+        raise NotImplementedError
+
     @abstractmethod
     def is_defined_in_stub(self, typ: Instance, /) -> bool:
         raise NotImplementedError
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index fc00e9f20291..f81684d2f44a 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -671,12 +671,15 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
                 has_local_errors = local_errors.has_new_errors()
             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.str_with_options(self.options), keyword
-                    ),
-                    pattern,
-                )
+                if not (type_info and type_info.fullname == "builtins.object"):
+                    self.msg.fail(
+                        message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(
+                            typ.str_with_options(self.options), keyword
+                        ),
+                        pattern,
+                    )
+                elif keyword is not None:
+                    new_type = self.chk.add_any_attribute_to_type(new_type, keyword)
 
             inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type)
             if is_uninhabited(inner_type):
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index b49bb13d520f..24bf2fdb8fb4 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1003,15 +1003,24 @@ match m:
 [builtins fixtures/tuple.pyi]
 
 [case testMatchClassPatternNonexistentKeyword]
+from typing import Any
 class A: ...
 
 m: object
+n: Any
 
 match m:
     case A(a=j):  # E: Class "__main__.A" has no attribute "a"
         reveal_type(m)  # N: Revealed type is "__main__.A"
         reveal_type(j)  # N: Revealed type is "Any"
 
+match n:
+    # Matching against object should not emit an error for non-existing keys
+    case object(a=k):
+        reveal_type(n)  # N: Revealed type is "builtins.object"
+        reveal_type(n.a)  # N: Revealed type is "Any"
+        reveal_type(k)  # N: Revealed type is "Any"
+
 [case testMatchClassPatternDuplicateKeyword]
 class A:
     a: str

From d1dffe275b9ef0babb2df6ddf0a329b4304cb114 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Tue, 26 Aug 2025 13:36:23 -0400
Subject: [PATCH 1580/1617] [mypyc] feat: PyObject_CallObject op for fn(*args)
 fastpath (#19631)

This PR adds a new custom_op for PyObject_CallObject which is more
efficient than PyObject_Call in cases where there are no kwargs.
posarg-only use cases are already optimized but this is helpful for
patterns such as `fn(*args)` or `fn(a1, a2, *args)`

This PR extends #19623 and #19629 , as this change will not be helpful
until those PRs are merged.
---
 mypyc/annotate.py                  |  1 +
 mypyc/irbuild/ll_builder.py        | 15 +++++----
 mypyc/primitives/generic_ops.py    |  9 ++++++
 mypyc/test-data/irbuild-basic.test | 52 ++++++++++++------------------
 4 files changed, 39 insertions(+), 38 deletions(-)

diff --git a/mypyc/annotate.py b/mypyc/annotate.py
index 6736ca63c9e8..bc282fc3ea6c 100644
--- a/mypyc/annotate.py
+++ b/mypyc/annotate.py
@@ -77,6 +77,7 @@ def __init__(self, message: str, priority: int = 1) -> None:
     "PyNumber_Rshift": Annotation('Generic ">>" operation.'),
     "PyNumber_Invert": Annotation('Generic "~" operation.'),
     "PyObject_Call": Annotation("Generic call operation."),
+    "PyObject_CallObject": Annotation("Generic call operation."),
     "PyObject_RichCompare": Annotation("Generic comparison operation."),
     "PyObject_GetItem": Annotation("Generic indexing operation."),
     "PyObject_SetItem": Annotation("Generic indexed assignment."),
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 116a1bb4bae0..ba8ef94b00bd 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -141,6 +141,7 @@
     generic_ssize_t_len_op,
     py_call_op,
     py_call_with_kwargs_op,
+    py_call_with_posargs_op,
     py_getattr_op,
     py_method_call_op,
     py_vectorcall_method_op,
@@ -805,7 +806,7 @@ def _construct_varargs(
                             value.type, RTuple
                         ):
                             value = self.primitive_op(sequence_tuple_op, [value], line)
-                        return value, self._create_dict([], [], line)
+                        return value, None
                     elif len(args) == 2 and args[1][1] == ARG_STAR2:
                         # fn(*args, **kwargs)
                         # TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
@@ -938,7 +939,7 @@ def _construct_varargs(
             elif not is_tuple_rprimitive(star_result.type):
                 # if star_result is a tuple we took the fast path
                 star_result = self.primitive_op(list_tuple_op, [star_result], line)
-        if has_star2 and star2_result is None:
+        if has_star2 and star2_result is None and len(star2_keys) > 0:
             # TODO: use dict_copy_op for simple cases of **kwargs
             star2_result = self._create_dict(star2_keys, star2_values, line)
 
@@ -964,13 +965,16 @@ def py_call(
         if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds):
             return self.call_c(py_call_op, [function] + arg_values, line)
 
-        # Otherwise fallback to py_call_with_kwargs_op.
+        # Otherwise fallback to py_call_with_posargs_op or py_call_with_kwargs_op.
         assert arg_names is not None
 
         pos_args_tuple, kw_args_dict = self._construct_varargs(
             list(zip(arg_values, arg_kinds, arg_names)), line, has_star=True, has_star2=True
         )
-        assert pos_args_tuple and kw_args_dict
+        assert pos_args_tuple
+
+        if kw_args_dict is None:
+            return self.call_c(py_call_with_posargs_op, [function, pos_args_tuple], line)
 
         return self.call_c(py_call_with_kwargs_op, [function, pos_args_tuple, kw_args_dict], line)
 
@@ -1169,8 +1173,7 @@ def native_args_to_positional(
                 assert star_arg
                 output_arg = star_arg
             elif arg.kind == ARG_STAR2:
-                assert star2_arg
-                output_arg = star2_arg
+                output_arg = star2_arg or self._create_dict([], [], line)
             elif not lst:
                 if is_fixed_width_rtype(arg.type):
                     output_arg = Integer(0, arg.type)
diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py
index 4a95be4e5d4e..8a4ddc370280 100644
--- a/mypyc/primitives/generic_ops.py
+++ b/mypyc/primitives/generic_ops.py
@@ -308,6 +308,15 @@
     error_kind=ERR_MAGIC,
 )
 
+# Call callable object with positional args only: func(*args)
+# Arguments are (func, *args tuple).
+py_call_with_posargs_op = custom_op(
+    arg_types=[object_rprimitive, object_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="PyObject_CallObject",
+    error_kind=ERR_MAGIC,
+)
+
 # Call method with positional arguments: obj.method(arg1, ...)
 # Arguments are (object, attribute name, arg1, ...).
 py_method_call_op = custom_op(
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index 63e4ef55d3fc..4eeeca04719c 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1674,20 +1674,17 @@ def g():
     r0 :: tuple[int, int, int]
     r1 :: dict
     r2 :: str
-    r3 :: object
-    r4 :: dict
-    r5, r6 :: object
-    r7 :: tuple[int, int, int]
+    r3, r4, r5 :: object
+    r6 :: tuple[int, int, int]
 L0:
     r0 = (2, 4, 6)
     r1 = __main__.globals :: static
     r2 = 'f'
     r3 = CPyDict_GetItem(r1, r2)
-    r4 = PyDict_New()
-    r5 = box(tuple[int, int, int], r0)
-    r6 = PyObject_Call(r3, r5, r4)
-    r7 = unbox(tuple[int, int, int], r6)
-    return r7
+    r4 = box(tuple[int, int, int], r0)
+    r5 = PyObject_CallObject(r3, r4)
+    r6 = unbox(tuple[int, int, int], r5)
+    return r6
 def h():
     r0 :: tuple[int, int]
     r1 :: dict
@@ -1698,9 +1695,8 @@ def h():
     r6 :: ptr
     r7, r8 :: object
     r9 :: tuple
-    r10 :: dict
-    r11 :: object
-    r12 :: tuple[int, int, int]
+    r10 :: object
+    r11 :: tuple[int, int, int]
 L0:
     r0 = (4, 6)
     r1 = __main__.globals :: static
@@ -1714,10 +1710,9 @@ L0:
     r7 = box(tuple[int, int], r0)
     r8 = CPyList_Extend(r4, r7)
     r9 = PyList_AsTuple(r4)
-    r10 = PyDict_New()
-    r11 = PyObject_Call(r3, r9, r10)
-    r12 = unbox(tuple[int, int, int], r11)
-    return r12
+    r10 = PyObject_CallObject(r3, r9)
+    r11 = unbox(tuple[int, int, int], r10)
+    return r11
 
 [case testStar2Args]
 from typing import Tuple
@@ -3562,15 +3557,12 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
     __mypyc_self__ :: __main__.wrapper_deco_obj
     args :: tuple
     r0 :: __main__.deco_env
-    r1 :: object
-    r2 :: dict
-    r3 :: object
+    r1, r2 :: object
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = r0.fn
-    r2 = PyDict_New()
-    r3 = PyObject_Call(r1, args, r2)
-    return r3
+    r2 = PyObject_CallObject(r1, args)
+    return r2
 def deco(fn):
     fn :: object
     r0 :: __main__.deco_env
@@ -3613,15 +3605,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
     r0 :: __main__.deco_env
     r1 :: object
     r2 :: tuple
-    r3 :: dict
-    r4 :: object
+    r3 :: object
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = r0.fn
     r2 = PyList_AsTuple(args)
-    r3 = PyDict_New()
-    r4 = PyObject_Call(r1, r2, r3)
-    return r4
+    r3 = PyObject_CallObject(r1, r2)
+    return r3
 def deco(fn):
     fn :: object
     r0 :: __main__.deco_env
@@ -3716,15 +3706,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
     r0 :: __main__.deco_env
     r1 :: object
     r2 :: tuple
-    r3 :: dict
-    r4 :: object
+    r3 :: object
 L0:
     r0 = __mypyc_self__.__mypyc_env__
     r1 = r0.fn
     r2 = PySequence_Tuple(args)
-    r3 = PyDict_New()
-    r4 = PyObject_Call(r1, r2, r3)
-    return r4
+    r3 = PyObject_CallObject(r1, r2)
+    return r3
 def deco(fn):
     fn :: object
     r0 :: __main__.deco_env

From dcb4d695e0b3ea6fedd90572bc249ec07731b3bc Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 26 Aug 2025 21:10:20 +0100
Subject: [PATCH 1581/1617] Use 1 byte per type/symbol tag (#19735)

This is a small incremental improvement for fixed format cache. I am
adding a dedicated write/read functions for tags (i.e. integers in 0-255
range). I propose to exclusively use these functions for type tags
(hence the name), and still use regular `write_int()`/`read_int()` for
integers that are "accidentally small" (like argument kinds etc). In a
separate PR I will change regular `int` format to be more progressive
(e.g. only use 1 byte if an integer happens to be small). I also change
the terminology from "marker" to "tag", as this is a more common name
for this concept.

Note we can probably use `mypy_extensions.u8` for type tags. If there is
a desire for this, I can switch to it (either in this or a separate PR).
---
 mypy/cache.py                                 |  32 +++--
 mypy/nodes.py                                 |  85 ++++++------
 mypy/types.py                                 | 125 +++++++++---------
 .../stubs/mypy-native/native_internal.pyi     |   2 +
 mypyc/lib-rt/native_internal.c                |  73 +++++++++-
 mypyc/lib-rt/native_internal.h                |   6 +-
 mypyc/primitives/misc_ops.py                  |  16 +++
 mypyc/test-data/irbuild-classes.test          |  44 +++---
 mypyc/test-data/run-classes.test              |  15 ++-
 test-data/unit/lib-stub/native_internal.pyi   |   2 +
 10 files changed, 262 insertions(+), 138 deletions(-)

diff --git a/mypy/cache.py b/mypy/cache.py
index 49f568c1f3c1..a16a36900c7a 100644
--- a/mypy/cache.py
+++ b/mypy/cache.py
@@ -10,10 +10,12 @@
         read_float as read_float,
         read_int as read_int,
         read_str as read_str,
+        read_tag as read_tag,
         write_bool as write_bool,
         write_float as write_float,
         write_int as write_int,
         write_str as write_str,
+        write_tag as write_tag,
     )
 except ImportError:
     # TODO: temporary, remove this after we publish mypy-native on PyPI.
@@ -32,6 +34,12 @@ def read_int(data: Buffer) -> int:
         def write_int(data: Buffer, value: int) -> None:
             raise NotImplementedError
 
+        def read_tag(data: Buffer) -> int:
+            raise NotImplementedError
+
+        def write_tag(data: Buffer, value: int) -> None:
+            raise NotImplementedError
+
         def read_str(data: Buffer) -> str:
             raise NotImplementedError
 
@@ -59,37 +67,37 @@ def write_float(data: Buffer, value: float) -> None:
 LITERAL_NONE: Final = 6
 
 
-def read_literal(data: Buffer, marker: int) -> int | str | bool | float:
-    if marker == LITERAL_INT:
+def read_literal(data: Buffer, tag: int) -> int | str | bool | float:
+    if tag == LITERAL_INT:
         return read_int(data)
-    elif marker == LITERAL_STR:
+    elif tag == LITERAL_STR:
         return read_str(data)
-    elif marker == LITERAL_BOOL:
+    elif tag == LITERAL_BOOL:
         return read_bool(data)
-    elif marker == LITERAL_FLOAT:
+    elif tag == LITERAL_FLOAT:
         return read_float(data)
-    assert False, f"Unknown literal marker {marker}"
+    assert False, f"Unknown literal tag {tag}"
 
 
 def write_literal(data: Buffer, value: int | str | bool | float | complex | None) -> None:
     if isinstance(value, bool):
-        write_int(data, LITERAL_BOOL)
+        write_tag(data, LITERAL_BOOL)
         write_bool(data, value)
     elif isinstance(value, int):
-        write_int(data, LITERAL_INT)
+        write_tag(data, LITERAL_INT)
         write_int(data, value)
     elif isinstance(value, str):
-        write_int(data, LITERAL_STR)
+        write_tag(data, LITERAL_STR)
         write_str(data, value)
     elif isinstance(value, float):
-        write_int(data, LITERAL_FLOAT)
+        write_tag(data, LITERAL_FLOAT)
         write_float(data, value)
     elif isinstance(value, complex):
-        write_int(data, LITERAL_COMPLEX)
+        write_tag(data, LITERAL_COMPLEX)
         write_float(data, value.real)
         write_float(data, value.imag)
     else:
-        write_int(data, LITERAL_NONE)
+        write_tag(data, LITERAL_NONE)
 
 
 def read_int_opt(data: Buffer) -> int | None:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index b9c08f02f316..45e2b60c3e78 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -28,6 +28,7 @@
     read_str_list,
     read_str_opt,
     read_str_opt_list,
+    read_tag,
     write_bool,
     write_int,
     write_int_list,
@@ -37,6 +38,7 @@
     write_str_list,
     write_str_opt,
     write_str_opt_list,
+    write_tag,
 )
 from mypy.options import Options
 from mypy.util import is_sunder, is_typeshed_file, short_type
@@ -417,7 +419,7 @@ def deserialize(cls, data: JsonDict) -> MypyFile:
         return tree
 
     def write(self, data: Buffer) -> None:
-        write_int(data, MYPY_FILE)
+        write_tag(data, MYPY_FILE)
         write_str(data, self._fullname)
         self.names.write(data, self._fullname)
         write_bool(data, self.is_stub)
@@ -427,7 +429,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> MypyFile:
-        assert read_int(data) == MYPY_FILE
+        assert read_tag(data) == MYPY_FILE
         tree = MypyFile([], [])
         tree._fullname = read_str(data)
         tree.names = SymbolTable.read(data)
@@ -711,7 +713,7 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef:
         return res
 
     def write(self, data: Buffer) -> None:
-        write_int(data, OVERLOADED_FUNC_DEF)
+        write_tag(data, OVERLOADED_FUNC_DEF)
         write_int(data, len(self.items))
         for item in self.items:
             item.write(data)
@@ -1022,7 +1024,7 @@ def deserialize(cls, data: JsonDict) -> FuncDef:
         return ret
 
     def write(self, data: Buffer) -> None:
-        write_int(data, FUNC_DEF)
+        write_tag(data, FUNC_DEF)
         write_str(data, self._name)
         mypy.types.write_type_opt(data, self.type)
         write_str(data, self._fullname)
@@ -1134,16 +1136,16 @@ def deserialize(cls, data: JsonDict) -> Decorator:
         return dec
 
     def write(self, data: Buffer) -> None:
-        write_int(data, DECORATOR)
+        write_tag(data, DECORATOR)
         self.func.write(data)
         self.var.write(data)
         write_bool(data, self.is_overload)
 
     @classmethod
     def read(cls, data: Buffer) -> Decorator:
-        assert read_int(data) == FUNC_DEF
+        assert read_tag(data) == FUNC_DEF
         func = FuncDef.read(data)
-        assert read_int(data) == VAR
+        assert read_tag(data) == VAR
         var = Var.read(data)
         dec = Decorator(func, [], var)
         dec.is_overload = read_bool(data)
@@ -1326,7 +1328,7 @@ def deserialize(cls, data: JsonDict) -> Var:
         return v
 
     def write(self, data: Buffer) -> None:
-        write_int(data, VAR)
+        write_tag(data, VAR)
         write_str(data, self._name)
         mypy.types.write_type_opt(data, self.type)
         mypy.types.write_type_opt(data, self.setter_type)
@@ -1341,13 +1343,13 @@ def read(cls, data: Buffer) -> Var:
         v = Var(name, typ)
         setter_type: mypy.types.CallableType | None = None
         if read_bool(data):
-            assert read_int(data) == mypy.types.CALLABLE_TYPE
+            assert read_tag(data) == mypy.types.CALLABLE_TYPE
             setter_type = mypy.types.CallableType.read(data)
         v.setter_type = setter_type
         v.is_ready = False  # Override True default set in __init__
         v._fullname = read_str(data)
         read_flags(data, v, VAR_FLAGS)
-        marker = read_int(data)
+        marker = read_tag(data)
         if marker == LITERAL_COMPLEX:
             v.final_value = complex(read_float(data), read_float(data))
         elif marker != LITERAL_NONE:
@@ -1465,7 +1467,7 @@ def deserialize(cls, data: JsonDict) -> ClassDef:
         return res
 
     def write(self, data: Buffer) -> None:
-        write_int(data, CLASS_DEF)
+        write_tag(data, CLASS_DEF)
         write_str(data, self.name)
         mypy.types.write_type_list(data, self.type_vars)
         write_str(data, self.fullname)
@@ -2898,7 +2900,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarExpr:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_VAR_EXPR)
+        write_tag(data, TYPE_VAR_EXPR)
         write_str(data, self._name)
         write_str(data, self._fullname)
         mypy.types.write_type_list(data, self.values)
@@ -2948,7 +2950,7 @@ def deserialize(cls, data: JsonDict) -> ParamSpecExpr:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, PARAM_SPEC_EXPR)
+        write_tag(data, PARAM_SPEC_EXPR)
         write_str(data, self._name)
         write_str(data, self._fullname)
         self.upper_bound.write(data)
@@ -3016,7 +3018,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleExpr:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_VAR_TUPLE_EXPR)
+        write_tag(data, TYPE_VAR_TUPLE_EXPR)
         self.tuple_fallback.write(data)
         write_str(data, self._name)
         write_str(data, self._fullname)
@@ -3026,7 +3028,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> TypeVarTupleExpr:
-        assert read_int(data) == mypy.types.INSTANCE
+        assert read_tag(data) == mypy.types.INSTANCE
         fallback = mypy.types.Instance.read(data)
         return TypeVarTupleExpr(
             read_str(data),
@@ -3908,7 +3910,7 @@ def deserialize(cls, data: JsonDict) -> TypeInfo:
         return ti
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_INFO)
+        write_tag(data, TYPE_INFO)
         self.names.write(data, self.fullname)
         self.defn.write(data)
         write_str(data, self.module_name)
@@ -3944,7 +3946,7 @@ def write(self, data: Buffer) -> None:
     @classmethod
     def read(cls, data: Buffer) -> TypeInfo:
         names = SymbolTable.read(data)
-        assert read_int(data) == CLASS_DEF
+        assert read_tag(data) == CLASS_DEF
         defn = ClassDef.read(data)
         module_name = read_str(data)
         ti = TypeInfo(names, defn, module_name)
@@ -3954,10 +3956,9 @@ def read(cls, data: Buffer) -> TypeInfo:
         ti.abstract_attributes = list(zip(attrs, statuses))
         ti.type_vars = read_str_list(data)
         ti.has_param_spec_type = read_bool(data)
-        num_bases = read_int(data)
         ti.bases = []
-        for _ in range(num_bases):
-            assert read_int(data) == mypy.types.INSTANCE
+        for _ in range(read_int(data)):
+            assert read_tag(data) == mypy.types.INSTANCE
             ti.bases.append(mypy.types.Instance.read(data))
         # NOTE: ti.mro will be set in the fixup phase based on these
         # names.  The reason we need to store the mro instead of just
@@ -3972,19 +3973,19 @@ def read(cls, data: Buffer) -> TypeInfo:
         ti._mro_refs = read_str_list(data)
         ti._promote = cast(list[mypy.types.ProperType], mypy.types.read_type_list(data))
         if read_bool(data):
-            assert read_int(data) == mypy.types.INSTANCE
+            assert read_tag(data) == mypy.types.INSTANCE
             ti.alt_promote = mypy.types.Instance.read(data)
         if read_bool(data):
-            assert read_int(data) == mypy.types.INSTANCE
+            assert read_tag(data) == mypy.types.INSTANCE
             ti.declared_metaclass = mypy.types.Instance.read(data)
         if read_bool(data):
-            assert read_int(data) == mypy.types.INSTANCE
+            assert read_tag(data) == mypy.types.INSTANCE
             ti.metaclass_type = mypy.types.Instance.read(data)
         if read_bool(data):
-            assert read_int(data) == mypy.types.TUPLE_TYPE
+            assert read_tag(data) == mypy.types.TUPLE_TYPE
             ti.tuple_type = mypy.types.TupleType.read(data)
         if read_bool(data):
-            assert read_int(data) == mypy.types.TYPED_DICT_TYPE
+            assert read_tag(data) == mypy.types.TYPED_DICT_TYPE
             ti.typeddict_type = mypy.types.TypedDictType.read(data)
         read_flags(data, ti, TypeInfo.FLAGS)
         metadata = read_str(data)
@@ -3994,7 +3995,7 @@ def read(cls, data: Buffer) -> TypeInfo:
             ti.slots = set(read_str_list(data))
         ti.deletable_attributes = read_str_list(data)
         if read_bool(data):
-            assert read_int(data) == mypy.types.TYPE_VAR_TYPE
+            assert read_tag(data) == mypy.types.TYPE_VAR_TYPE
             ti.self_type = mypy.types.TypeVarType.read(data)
         if read_bool(data):
             ti.dataclass_transform_spec = DataclassTransformSpec.read(data)
@@ -4270,7 +4271,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_ALIAS)
+        write_tag(data, TYPE_ALIAS)
         write_str(data, self._fullname)
         self.target.write(data)
         mypy.types.write_type_list(data, self.alias_tvars)
@@ -4890,33 +4891,33 @@ def local_definitions(
 
 
 def read_symbol(data: Buffer) -> mypy.nodes.SymbolNode:
-    marker = read_int(data)
+    tag = read_tag(data)
     # The branches here are ordered manually by type "popularity".
-    if marker == VAR:
+    if tag == VAR:
         return mypy.nodes.Var.read(data)
-    if marker == FUNC_DEF:
+    if tag == FUNC_DEF:
         return mypy.nodes.FuncDef.read(data)
-    if marker == DECORATOR:
+    if tag == DECORATOR:
         return mypy.nodes.Decorator.read(data)
-    if marker == TYPE_INFO:
+    if tag == TYPE_INFO:
         return mypy.nodes.TypeInfo.read(data)
-    if marker == OVERLOADED_FUNC_DEF:
+    if tag == OVERLOADED_FUNC_DEF:
         return mypy.nodes.OverloadedFuncDef.read(data)
-    if marker == TYPE_VAR_EXPR:
+    if tag == TYPE_VAR_EXPR:
         return mypy.nodes.TypeVarExpr.read(data)
-    if marker == TYPE_ALIAS:
+    if tag == TYPE_ALIAS:
         return mypy.nodes.TypeAlias.read(data)
-    if marker == PARAM_SPEC_EXPR:
+    if tag == PARAM_SPEC_EXPR:
         return mypy.nodes.ParamSpecExpr.read(data)
-    if marker == TYPE_VAR_TUPLE_EXPR:
+    if tag == TYPE_VAR_TUPLE_EXPR:
         return mypy.nodes.TypeVarTupleExpr.read(data)
-    assert False, f"Unknown symbol marker {marker}"
+    assert False, f"Unknown symbol tag {tag}"
 
 
 def read_overload_part(data: Buffer) -> OverloadPart:
-    marker = read_int(data)
-    if marker == DECORATOR:
+    tag = read_tag(data)
+    if tag == DECORATOR:
         return Decorator.read(data)
-    if marker == FUNC_DEF:
+    if tag == FUNC_DEF:
         return FuncDef.read(data)
-    assert False, f"Invalid marker for an OverloadPart {marker}"
+    assert False, f"Invalid tag for an OverloadPart {tag}"
diff --git a/mypy/types.py b/mypy/types.py
index b48e0ef4d985..43e6dafe298e 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -20,6 +20,7 @@
     read_str_list,
     read_str_opt,
     read_str_opt_list,
+    read_tag,
     write_bool,
     write_int,
     write_int_list,
@@ -28,6 +29,7 @@
     write_str_list,
     write_str_opt,
     write_str_opt_list,
+    write_tag,
 )
 from mypy.nodes import ARG_KINDS, ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, SymbolNode
 from mypy.options import Options
@@ -456,7 +458,7 @@ def deserialize(cls, data: JsonDict) -> TypeAliasType:
         return alias
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_ALIAS_TYPE)
+        write_tag(data, TYPE_ALIAS_TYPE)
         write_type_list(data, self.args)
         assert self.alias is not None
         write_str(data, self.alias.fullname)
@@ -735,7 +737,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_VAR_TYPE)
+        write_tag(data, TYPE_VAR_TYPE)
         write_str(data, self.name)
         write_str(data, self.fullname)
         write_int(data, self.id.raw_id)
@@ -887,7 +889,7 @@ def deserialize(cls, data: JsonDict) -> ParamSpecType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, PARAM_SPEC_TYPE)
+        write_tag(data, PARAM_SPEC_TYPE)
         self.prefix.write(data)
         write_str(data, self.name)
         write_str(data, self.fullname)
@@ -899,7 +901,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> ParamSpecType:
-        assert read_int(data) == PARAMETERS
+        assert read_tag(data) == PARAMETERS
         prefix = Parameters.read(data)
         return ParamSpecType(
             read_str(data),
@@ -967,7 +969,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_VAR_TUPLE_TYPE)
+        write_tag(data, TYPE_VAR_TUPLE_TYPE)
         self.tuple_fallback.write(data)
         write_str(data, self.name)
         write_str(data, self.fullname)
@@ -979,7 +981,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> TypeVarTupleType:
-        assert read_int(data) == INSTANCE
+        assert read_tag(data) == INSTANCE
         fallback = Instance.read(data)
         return TypeVarTupleType(
             read_str(data),
@@ -1123,7 +1125,7 @@ def deserialize(cls, data: JsonDict) -> UnboundType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, UNBOUND_TYPE)
+        write_tag(data, UNBOUND_TYPE)
         write_str(data, self.name)
         write_type_list(data, self.args)
         write_str_opt(data, self.original_str_expr)
@@ -1233,7 +1235,7 @@ def serialize(self) -> JsonDict:
         return {".class": "UnpackType", "type": self.type.serialize()}
 
     def write(self, data: Buffer) -> None:
-        write_int(data, UNPACK_TYPE)
+        write_tag(data, UNPACK_TYPE)
         self.type.write(data)
 
     @classmethod
@@ -1342,7 +1344,7 @@ def deserialize(cls, data: JsonDict) -> AnyType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, ANY_TYPE)
+        write_tag(data, ANY_TYPE)
         write_type_opt(data, self.source_any)
         write_int(data, self.type_of_any)
         write_str_opt(data, self.missing_import_name)
@@ -1350,7 +1352,7 @@ def write(self, data: Buffer) -> None:
     @classmethod
     def read(cls, data: Buffer) -> AnyType:
         if read_bool(data):
-            assert read_int(data) == ANY_TYPE
+            assert read_tag(data) == ANY_TYPE
             source_any = AnyType.read(data)
         else:
             source_any = None
@@ -1403,7 +1405,7 @@ def deserialize(cls, data: JsonDict) -> UninhabitedType:
         return UninhabitedType()
 
     def write(self, data: Buffer) -> None:
-        write_int(data, UNINHABITED_TYPE)
+        write_tag(data, UNINHABITED_TYPE)
 
     @classmethod
     def read(cls, data: Buffer) -> UninhabitedType:
@@ -1442,7 +1444,7 @@ def deserialize(cls, data: JsonDict) -> NoneType:
         return NoneType()
 
     def write(self, data: Buffer) -> None:
-        write_int(data, NONE_TYPE)
+        write_tag(data, NONE_TYPE)
 
     @classmethod
     def read(cls, data: Buffer) -> NoneType:
@@ -1496,7 +1498,7 @@ def deserialize(cls, data: JsonDict) -> DeletedType:
         return DeletedType(data["source"])
 
     def write(self, data: Buffer) -> None:
-        write_int(data, DELETED_TYPE)
+        write_tag(data, DELETED_TYPE)
         write_str_opt(data, self.source)
 
     @classmethod
@@ -1704,7 +1706,7 @@ def deserialize(cls, data: JsonDict | str) -> Instance:
         return inst
 
     def write(self, data: Buffer) -> None:
-        write_int(data, INSTANCE)
+        write_tag(data, INSTANCE)
         write_str(data, self.type.fullname)
         write_type_list(data, self.args)
         write_type_opt(data, self.last_known_value)
@@ -1720,7 +1722,7 @@ def read(cls, data: Buffer) -> Instance:
         inst = Instance(NOT_READY, read_type_list(data))
         inst.type_ref = type_ref
         if read_bool(data):
-            assert read_int(data) == LITERAL_TYPE
+            assert read_tag(data) == LITERAL_TYPE
             inst.last_known_value = LiteralType.read(data)
         if read_bool(data):
             inst.extra_attrs = ExtraAttrs.read(data)
@@ -2003,7 +2005,7 @@ def deserialize(cls, data: JsonDict) -> Parameters:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, PARAMETERS)
+        write_tag(data, PARAMETERS)
         write_type_list(data, self.arg_types)
         write_int_list(data, [int(x.value) for x in self.arg_kinds])
         write_str_opt_list(data, self.arg_names)
@@ -2525,7 +2527,7 @@ def deserialize(cls, data: JsonDict) -> CallableType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, CALLABLE_TYPE)
+        write_tag(data, CALLABLE_TYPE)
         self.fallback.write(data)
         write_type_list(data, self.arg_types)
         write_int_list(data, [int(x.value) for x in self.arg_kinds])
@@ -2544,7 +2546,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> CallableType:
-        assert read_int(data) == INSTANCE
+        assert read_tag(data) == INSTANCE
         fallback = Instance.read(data)
         return CallableType(
             read_type_list(data),
@@ -2640,15 +2642,14 @@ def deserialize(cls, data: JsonDict) -> Overloaded:
         return Overloaded([CallableType.deserialize(t) for t in data["items"]])
 
     def write(self, data: Buffer) -> None:
-        write_int(data, OVERLOADED)
+        write_tag(data, OVERLOADED)
         write_type_list(data, self.items)
 
     @classmethod
     def read(cls, data: Buffer) -> Overloaded:
         items = []
-        num_overloads = read_int(data)
-        for _ in range(num_overloads):
-            assert read_int(data) == CALLABLE_TYPE
+        for _ in range(read_int(data)):
+            assert read_tag(data) == CALLABLE_TYPE
             items.append(CallableType.read(data))
         return Overloaded(items)
 
@@ -2749,14 +2750,14 @@ def deserialize(cls, data: JsonDict) -> TupleType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TUPLE_TYPE)
+        write_tag(data, TUPLE_TYPE)
         self.partial_fallback.write(data)
         write_type_list(data, self.items)
         write_bool(data, self.implicit)
 
     @classmethod
     def read(cls, data: Buffer) -> TupleType:
-        assert read_int(data) == INSTANCE
+        assert read_tag(data) == INSTANCE
         fallback = Instance.read(data)
         return TupleType(read_type_list(data), fallback, implicit=read_bool(data))
 
@@ -2931,7 +2932,7 @@ def deserialize(cls, data: JsonDict) -> TypedDictType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPED_DICT_TYPE)
+        write_tag(data, TYPED_DICT_TYPE)
         self.fallback.write(data)
         write_type_map(data, self.items)
         write_str_list(data, sorted(self.required_keys))
@@ -2939,7 +2940,7 @@ def write(self, data: Buffer) -> None:
 
     @classmethod
     def read(cls, data: Buffer) -> TypedDictType:
-        assert read_int(data) == INSTANCE
+        assert read_tag(data) == INSTANCE
         fallback = Instance.read(data)
         return TypedDictType(
             read_type_map(data), set(read_str_list(data)), set(read_str_list(data)), fallback
@@ -3194,16 +3195,16 @@ def deserialize(cls, data: JsonDict) -> LiteralType:
         return LiteralType(value=data["value"], fallback=Instance.deserialize(data["fallback"]))
 
     def write(self, data: Buffer) -> None:
-        write_int(data, LITERAL_TYPE)
+        write_tag(data, LITERAL_TYPE)
         self.fallback.write(data)
         write_literal(data, self.value)
 
     @classmethod
     def read(cls, data: Buffer) -> LiteralType:
-        assert read_int(data) == INSTANCE
+        assert read_tag(data) == INSTANCE
         fallback = Instance.read(data)
-        marker = read_int(data)
-        return LiteralType(read_literal(data, marker), fallback)
+        tag = read_tag(data)
+        return LiteralType(read_literal(data, tag), fallback)
 
     def is_singleton_type(self) -> bool:
         return self.is_enum_literal() or isinstance(self.value, bool)
@@ -3307,7 +3308,7 @@ def deserialize(cls, data: JsonDict) -> UnionType:
         )
 
     def write(self, data: Buffer) -> None:
-        write_int(data, UNION_TYPE)
+        write_tag(data, UNION_TYPE)
         write_type_list(data, self.items)
         write_bool(data, self.uses_pep604_syntax)
 
@@ -3452,7 +3453,7 @@ def deserialize(cls, data: JsonDict) -> Type:
         return TypeType.make_normalized(deserialize_type(data["item"]))
 
     def write(self, data: Buffer) -> None:
-        write_int(data, TYPE_TYPE)
+        write_tag(data, TYPE_TYPE)
         self.item.write(data)
 
     @classmethod
@@ -4141,67 +4142,67 @@ def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]:
 
 
 def read_type(data: Buffer) -> Type:
-    marker = read_int(data)
+    tag = read_tag(data)
     # The branches here are ordered manually by type "popularity".
-    if marker == INSTANCE:
+    if tag == INSTANCE:
         return Instance.read(data)
-    if marker == ANY_TYPE:
+    if tag == ANY_TYPE:
         return AnyType.read(data)
-    if marker == TYPE_VAR_TYPE:
+    if tag == TYPE_VAR_TYPE:
         return TypeVarType.read(data)
-    if marker == CALLABLE_TYPE:
+    if tag == CALLABLE_TYPE:
         return CallableType.read(data)
-    if marker == NONE_TYPE:
+    if tag == NONE_TYPE:
         return NoneType.read(data)
-    if marker == UNION_TYPE:
+    if tag == UNION_TYPE:
         return UnionType.read(data)
-    if marker == LITERAL_TYPE:
+    if tag == LITERAL_TYPE:
         return LiteralType.read(data)
-    if marker == TYPE_ALIAS_TYPE:
+    if tag == TYPE_ALIAS_TYPE:
         return TypeAliasType.read(data)
-    if marker == TUPLE_TYPE:
+    if tag == TUPLE_TYPE:
         return TupleType.read(data)
-    if marker == TYPED_DICT_TYPE:
+    if tag == TYPED_DICT_TYPE:
         return TypedDictType.read(data)
-    if marker == TYPE_TYPE:
+    if tag == TYPE_TYPE:
         return TypeType.read(data)
-    if marker == OVERLOADED:
+    if tag == OVERLOADED:
         return Overloaded.read(data)
-    if marker == PARAM_SPEC_TYPE:
+    if tag == PARAM_SPEC_TYPE:
         return ParamSpecType.read(data)
-    if marker == TYPE_VAR_TUPLE_TYPE:
+    if tag == TYPE_VAR_TUPLE_TYPE:
         return TypeVarTupleType.read(data)
-    if marker == UNPACK_TYPE:
+    if tag == UNPACK_TYPE:
         return UnpackType.read(data)
-    if marker == PARAMETERS:
+    if tag == PARAMETERS:
         return Parameters.read(data)
-    if marker == UNINHABITED_TYPE:
+    if tag == UNINHABITED_TYPE:
         return UninhabitedType.read(data)
-    if marker == UNBOUND_TYPE:
+    if tag == UNBOUND_TYPE:
         return UnboundType.read(data)
-    if marker == DELETED_TYPE:
+    if tag == DELETED_TYPE:
         return DeletedType.read(data)
-    assert False, f"Unknown type marker {marker}"
+    assert False, f"Unknown type tag {tag}"
 
 
 def read_function_like(data: Buffer) -> FunctionLike:
-    marker = read_int(data)
-    if marker == CALLABLE_TYPE:
+    tag = read_tag(data)
+    if tag == CALLABLE_TYPE:
         return CallableType.read(data)
-    if marker == OVERLOADED:
+    if tag == OVERLOADED:
         return Overloaded.read(data)
-    assert False, f"Invalid type marker for FunctionLike {marker}"
+    assert False, f"Invalid type tag for FunctionLike {tag}"
 
 
 def read_type_var_like(data: Buffer) -> TypeVarLikeType:
-    marker = read_int(data)
-    if marker == TYPE_VAR_TYPE:
+    tag = read_tag(data)
+    if tag == TYPE_VAR_TYPE:
         return TypeVarType.read(data)
-    if marker == PARAM_SPEC_TYPE:
+    if tag == PARAM_SPEC_TYPE:
         return ParamSpecType.read(data)
-    if marker == TYPE_VAR_TUPLE_TYPE:
+    if tag == TYPE_VAR_TUPLE_TYPE:
         return TypeVarTupleType.read(data)
-    assert False, f"Invalid type marker for TypeVarLikeType {marker}"
+    assert False, f"Invalid type tag for TypeVarLikeType {tag}"
 
 
 def read_type_opt(data: Buffer) -> Type | None:
diff --git a/mypy/typeshed/stubs/mypy-native/native_internal.pyi b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
index bc1f570a8e9c..3c6a22c938e3 100644
--- a/mypy/typeshed/stubs/mypy-native/native_internal.pyi
+++ b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
@@ -10,3 +10,5 @@ def write_float(data: Buffer, value: float) -> None: ...
 def read_float(data: Buffer) -> float: ...
 def write_int(data: Buffer, value: int) -> None: ...
 def read_int(data: Buffer) -> int: ...
+def write_tag(data: Buffer, value: int) -> None: ...
+def read_tag(data: Buffer) -> int: ...
diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
index 11a3fafee56f..1c35eab946f8 100644
--- a/mypyc/lib-rt/native_internal.c
+++ b/mypyc/lib-rt/native_internal.c
@@ -1,10 +1,12 @@
 #define PY_SSIZE_T_CLEAN
 #include 
+#include 
 #include "CPy.h"
 #define NATIVE_INTERNAL_MODULE
 #include "native_internal.h"
 
 #define START_SIZE 512
+#define MAX_SHORT_INT_TAGGED (255 << 1)
 
 typedef struct {
     PyObject_HEAD
@@ -436,6 +438,71 @@ write_int(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
+static CPyTagged
+read_tag_internal(PyObject *data) {
+    if (_check_buffer(data) == 2)
+        return CPY_INT_TAG;
+
+    if (_check_read((BufferObject *)data, 1) == 2)
+        return CPY_INT_TAG;
+    char *buf = ((BufferObject *)data)->buf;
+
+    uint8_t ret = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += 1;
+    return ((CPyTagged)ret) << 1;
+}
+
+static PyObject*
+read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", NULL};
+    PyObject *data = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+        return NULL;
+    CPyTagged retval = read_tag_internal(data);
+    if (retval == CPY_INT_TAG) {
+        return NULL;
+    }
+    return CPyTagged_StealAsObject(retval);
+}
+
+static char
+write_tag_internal(PyObject *data, CPyTagged value) {
+    if (_check_buffer(data) == 2)
+        return 2;
+
+    if (value > MAX_SHORT_INT_TAGGED) {
+        PyErr_SetString(PyExc_OverflowError, "value must fit in single byte");
+        return 2;
+    }
+
+    if (_check_size((BufferObject *)data, 1) == 2)
+        return 2;
+    uint8_t *buf = (uint8_t *)((BufferObject *)data)->buf;
+    *(buf + ((BufferObject *)data)->pos) = (uint8_t)(value >> 1);
+    ((BufferObject *)data)->pos += 1;
+    ((BufferObject *)data)->end += 1;
+    return 1;
+}
+
+static PyObject*
+write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
+    static char *kwlist[] = {"data", "value", NULL};
+    PyObject *data = NULL;
+    PyObject *value = NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+        return NULL;
+    if (!PyLong_Check(value)) {
+        PyErr_SetString(PyExc_TypeError, "value must be an int");
+        return NULL;
+    }
+    CPyTagged tagged_value = CPyTagged_BorrowFromObject(value);
+    if (write_tag_internal(data, tagged_value) == 2) {
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 static PyMethodDef native_internal_module_methods[] = {
     // TODO: switch public wrappers to METH_FASTCALL.
     {"write_bool", (PyCFunction)write_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a bool")},
@@ -446,6 +513,8 @@ static PyMethodDef native_internal_module_methods[] = {
     {"read_float", (PyCFunction)read_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a float")},
     {"write_int", (PyCFunction)write_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write an int")},
     {"read_int", (PyCFunction)read_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read an int")},
+    {"write_tag", (PyCFunction)write_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a short int")},
+    {"read_tag", (PyCFunction)read_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a short int")},
     {NULL, NULL, 0, NULL}
 };
 
@@ -465,7 +534,7 @@ native_internal_module_exec(PyObject *m)
     }
 
     // Export mypy internal C API, be careful with the order!
-    static void *NativeInternal_API[12] = {
+    static void *NativeInternal_API[14] = {
         (void *)Buffer_internal,
         (void *)Buffer_internal_empty,
         (void *)Buffer_getvalue_internal,
@@ -477,6 +546,8 @@ native_internal_module_exec(PyObject *m)
         (void *)read_float_internal,
         (void *)write_int_internal,
         (void *)read_int_internal,
+        (void *)write_tag_internal,
+        (void *)read_tag_internal,
         (void *)NativeInternal_ABI_Version,
     };
     PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "native_internal._C_API", NULL);
diff --git a/mypyc/lib-rt/native_internal.h b/mypyc/lib-rt/native_internal.h
index 3bd3dd1bbb33..5a8905f0e6f0 100644
--- a/mypyc/lib-rt/native_internal.h
+++ b/mypyc/lib-rt/native_internal.h
@@ -16,6 +16,8 @@ static char write_float_internal(PyObject *data, double value);
 static double read_float_internal(PyObject *data);
 static char write_int_internal(PyObject *data, CPyTagged value);
 static CPyTagged read_int_internal(PyObject *data);
+static char write_tag_internal(PyObject *data, CPyTagged value);
+static CPyTagged read_tag_internal(PyObject *data);
 static int NativeInternal_ABI_Version(void);
 
 #else
@@ -33,7 +35,9 @@ static void **NativeInternal_API;
 #define read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8])
 #define write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9])
 #define read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10])
-#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[11])
+#define write_tag_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[11])
+#define read_tag_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[12])
+#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13])
 
 static int
 import_native_internal(void)
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index 8738255081e2..5875d5d65e9b 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -423,3 +423,19 @@
     c_function_name="read_int_internal",
     error_kind=ERR_MAGIC,
 )
+
+function_op(
+    name="native_internal.write_tag",
+    arg_types=[object_rprimitive, int_rprimitive],
+    return_type=none_rprimitive,
+    c_function_name="write_tag_internal",
+    error_kind=ERR_MAGIC,
+)
+
+function_op(
+    name="native_internal.read_tag",
+    arg_types=[object_rprimitive],
+    return_type=int_rprimitive,
+    c_function_name="read_tag_internal",
+    error_kind=ERR_MAGIC,
+)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 68bc18c7bdeb..3a9657d49f34 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1411,7 +1411,8 @@ class TestOverload:
 
 [case testNativeBufferFastPath]
 from native_internal import (
-    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float, write_int, read_int
+    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
+    write_int, read_int, write_tag, read_tag
 )
 
 def foo() -> None:
@@ -1420,23 +1421,25 @@ def foo() -> None:
     write_bool(b, True)
     write_float(b, 0.1)
     write_int(b, 1)
+    write_tag(b, 1)
 
     b = Buffer(b.getvalue())
     x = read_str(b)
     y = read_bool(b)
     z = read_float(b)
     t = read_int(b)
+    u = read_tag(b)
 [out]
 def foo():
     r0, b :: native_internal.Buffer
     r1 :: str
-    r2, r3, r4, r5 :: None
-    r6 :: bytes
-    r7 :: native_internal.Buffer
-    r8, x :: str
-    r9, y :: bool
-    r10, z :: float
-    r11, t :: int
+    r2, r3, r4, r5, r6 :: None
+    r7 :: bytes
+    r8 :: native_internal.Buffer
+    r9, x :: str
+    r10, y :: bool
+    r11, z :: float
+    r12, t, r13, u :: int
 L0:
     r0 = Buffer_internal_empty()
     b = r0
@@ -1445,17 +1448,20 @@ L0:
     r3 = write_bool_internal(b, 1)
     r4 = write_float_internal(b, 0.1)
     r5 = write_int_internal(b, 2)
-    r6 = Buffer_getvalue_internal(b)
-    r7 = Buffer_internal(r6)
-    b = r7
-    r8 = read_str_internal(b)
-    x = r8
-    r9 = read_bool_internal(b)
-    y = r9
-    r10 = read_float_internal(b)
-    z = r10
-    r11 = read_int_internal(b)
-    t = r11
+    r6 = write_tag_internal(b, 2)
+    r7 = Buffer_getvalue_internal(b)
+    r8 = Buffer_internal(r7)
+    b = r8
+    r9 = read_str_internal(b)
+    x = r9
+    r10 = read_bool_internal(b)
+    y = r10
+    r11 = read_float_internal(b)
+    z = r11
+    r12 = read_int_internal(b)
+    t = r12
+    r13 = read_tag_internal(b)
+    u = r13
     return 1
 
 [case testEnumFastPath]
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 6f1217bd36e6..dc64680f67c1 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2712,7 +2712,8 @@ Player.MIN = 
 
 [case testBufferRoundTrip_native_libs]
 from native_internal import (
-    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float, write_int, read_int
+    Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
+    write_int, read_int, write_tag, read_tag
 )
 
 def test_buffer_basic() -> None:
@@ -2728,8 +2729,11 @@ def test_buffer_roundtrip() -> None:
     write_float(b, 0.1)
     write_int(b, 0)
     write_int(b, 1)
+    write_tag(b, 33)
+    write_tag(b, 255)
     write_int(b, 2)
     write_int(b, 2 ** 85)
+    write_int(b, -1)
 
     b = Buffer(b.getvalue())
     assert read_str(b) == "foo"
@@ -2739,8 +2743,11 @@ def test_buffer_roundtrip() -> None:
     assert read_float(b) == 0.1
     assert read_int(b) == 0
     assert read_int(b) == 1
+    assert read_tag(b) == 33
+    assert read_tag(b) == 255
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
+    assert read_int(b) == -1
 
 [file driver.py]
 from native import *
@@ -2761,8 +2768,11 @@ def test_buffer_roundtrip_interpreted() -> None:
     write_float(b, 0.1)
     write_int(b, 0)
     write_int(b, 1)
+    write_tag(b, 33)
+    write_tag(b, 255)
     write_int(b, 2)
     write_int(b, 2 ** 85)
+    write_int(b, -1)
 
     b = Buffer(b.getvalue())
     assert read_str(b) == "foo"
@@ -2772,8 +2782,11 @@ def test_buffer_roundtrip_interpreted() -> None:
     assert read_float(b) == 0.1
     assert read_int(b) == 0
     assert read_int(b) == 1
+    assert read_tag(b) == 33
+    assert read_tag(b) == 255
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
+    assert read_int(b) == -1
 
 test_buffer_basic_interpreted()
 test_buffer_roundtrip_interpreted()
diff --git a/test-data/unit/lib-stub/native_internal.pyi b/test-data/unit/lib-stub/native_internal.pyi
index bc1f570a8e9c..3c6a22c938e3 100644
--- a/test-data/unit/lib-stub/native_internal.pyi
+++ b/test-data/unit/lib-stub/native_internal.pyi
@@ -10,3 +10,5 @@ def write_float(data: Buffer, value: float) -> None: ...
 def read_float(data: Buffer) -> float: ...
 def write_int(data: Buffer, value: int) -> None: ...
 def read_int(data: Buffer) -> int: ...
+def write_tag(data: Buffer, value: int) -> None: ...
+def read_tag(data: Buffer) -> int: ...

From de1247d76119c91ab36fa687b98dc9aabb261a6e Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 27 Aug 2025 02:10:35 +0200
Subject: [PATCH 1582/1617] Fix unwrapping assignment expressions in match
 subject (#19742)

The `else_map` from guard clauses can only be applied properly if the
subject expression itself can be put on the binder. Unwrap assignment
expressions so we don't need to fall back to a dummy name and loose the
guard clause inference.
---
 mypy/checker.py                     |  5 ++---
 test-data/unit/check-python310.test | 10 ++++++++++
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index 0fe77e953d06..ae6ae591ed8c 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -5552,10 +5552,10 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None:
         return
 
     def visit_match_stmt(self, s: MatchStmt) -> None:
-        named_subject = self._make_named_statement_for_match(s)
         # In sync with similar actions elsewhere, narrow the target if
         # we are matching an AssignmentExpr
         unwrapped_subject = collapse_walrus(s.subject)
+        named_subject = self._make_named_statement_for_match(s, unwrapped_subject)
         with self.binder.frame_context(can_skip=False, fall_through=0):
             subject_type = get_proper_type(self.expr_checker.accept(s.subject))
 
@@ -5646,9 +5646,8 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
             with self.binder.frame_context(can_skip=False, fall_through=2):
                 pass
 
-    def _make_named_statement_for_match(self, s: MatchStmt) -> Expression:
+    def _make_named_statement_for_match(self, s: MatchStmt, subject: Expression) -> Expression:
         """Construct a fake NameExpr for inference if a match clause is complex."""
-        subject = s.subject
         if self.binder.can_put_directly(subject):
             # Already named - we should infer type of it as given
             return subject
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 24bf2fdb8fb4..5c495d2ed863 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1393,6 +1393,16 @@ match m:
         reveal_type(a)
 [builtins fixtures/isinstancelist.pyi]
 
+[case testMatchSubjectAssignExprWithGuard]
+from typing import Optional
+def func() -> Optional[str]: ...
+
+match m := func():
+    case _ if not m:
+        reveal_type(m)  # N: Revealed type is "Union[Literal[''], None]"
+    case _:
+        reveal_type(m)  # N: Revealed type is "builtins.str"
+
 -- Exhaustiveness --
 
 [case testMatchUnionNegativeNarrowing]

From e852829e06aeeef90b12d389eeba775cdfabfd46 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 27 Aug 2025 02:15:15 +0200
Subject: [PATCH 1583/1617] Cleanup old ast classes in fastparse (#19743)

The ast `ExtSlice` and `Index` classes have been deprecated (and unused)
since Python 3.9.
https://docs.python.org/3.9/library/ast.html#ast.AST
---
 mypy/fastparse.py | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/mypy/fastparse.py b/mypy/fastparse.py
index 99d5c48c92d7..6b2eb532003c 100644
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -129,9 +129,7 @@
 PY_MINOR_VERSION: Final = sys.version_info[1]
 
 import ast as ast3
-
-# TODO: Index, ExtSlice are deprecated in 3.9.
-from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UAdd, UnaryOp, USub
+from ast import AST, Attribute, Call, FunctionType, Name, Starred, UAdd, UnaryOp, USub
 
 
 def ast3_parse(
@@ -1779,18 +1777,6 @@ def visit_Slice(self, n: ast3.Slice) -> SliceExpr:
         e = SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step))
         return self.set_line(e, n)
 
-    # ExtSlice(slice* dims)
-    def visit_ExtSlice(self, n: ast3.ExtSlice) -> TupleExpr:
-        # cast for mypyc's benefit on Python 3.9
-        return TupleExpr(self.translate_expr_list(cast(Any, n).dims))
-
-    # Index(expr value)
-    def visit_Index(self, n: Index) -> Node:
-        # cast for mypyc's benefit on Python 3.9
-        value = self.visit(cast(Any, n).value)
-        assert isinstance(value, Node)
-        return value
-
     # Match(expr subject, match_case* cases) # python 3.10 and later
     def visit_Match(self, n: Match) -> MatchStmt:
         node = MatchStmt(

From e0ce3e347bb1f2ec0ed885b914858cffe15a77d5 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Wed, 27 Aug 2025 12:53:21 -0400
Subject: [PATCH 1584/1617] [mypyc] feat: `__mypyc_empty_tuple__` constant
 (#19654)

I realized that any time a user has a kwarg-only call expression like
`fn(abc=123, ...)` in their compiled code, and `func` is not a native
function, a new empty tuple is created every time

This is not really necessary, we can just hold the same empty tuple in
memory as a constant and pass it around. It's immutable, and that's
already what we're already doing, since `tuple() is tuple()` but our
current method involves more steps.

This should slightly improve the speed of kwarg-only python func
calling.
---
 mypyc/codegen/emit.py                | 26 +++++++++++++++-----------
 mypyc/irbuild/ll_builder.py          |  8 ++++++--
 mypyc/lib-rt/CPy.h                   |  9 +++++++++
 mypyc/lib-rt/init.c                  | 11 +++++++++++
 mypyc/primitives/tuple_ops.py        |  7 +++++++
 mypyc/test-data/irbuild-basic.test   |  2 +-
 mypyc/test-data/irbuild-classes.test |  4 ++--
 7 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py
index 9ca761bd8ac5..4ef53296ef0d 100644
--- a/mypyc/codegen/emit.py
+++ b/mypyc/codegen/emit.py
@@ -1036,17 +1036,21 @@ def emit_box(
             self.emit_line(f"{declaration}{dest} = PyFloat_FromDouble({src});")
         elif isinstance(typ, RTuple):
             self.declare_tuple_struct(typ)
-            self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});")
-            self.emit_line(f"if (unlikely({dest} == NULL))")
-            self.emit_line("    CPyError_OutOfMemory();")
-            # TODO: Fail if dest is None
-            for i in range(len(typ.types)):
-                if not typ.is_unboxed:
-                    self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}")
-                else:
-                    inner_name = self.temp_name()
-                    self.emit_box(f"{src}.f{i}", inner_name, typ.types[i], declare_dest=True)
-                    self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {inner_name});")
+            if not typ.types:
+                self.emit_line(f"{declaration}{dest} = CPyTuple_LoadEmptyTupleConstant();")
+            else:
+                self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});")
+                self.emit_line(f"if (unlikely({dest} == NULL))")
+                self.emit_line("    CPyError_OutOfMemory();")
+
+                # TODO: Fail if dest is None
+                for i in range(len(typ.types)):
+                    if not typ.is_unboxed:
+                        self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}")
+                    else:
+                        inner_name = self.temp_name()
+                        self.emit_box(f"{src}.f{i}", inner_name, typ.types[i], declare_dest=True)
+                        self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {inner_name});")
         else:
             assert not typ.is_unboxed
             # Type is boxed -- trivially just assign.
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index ba8ef94b00bd..112bbdbb50ed 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -189,6 +189,7 @@
 )
 from mypyc.primitives.tuple_ops import (
     list_tuple_op,
+    load_empty_tuple_constant_op,
     new_tuple_op,
     new_tuple_with_length_op,
     sequence_tuple_op,
@@ -2362,8 +2363,11 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val
             return self.call_c(generic_len_op, [val], line)
 
     def new_tuple(self, items: list[Value], line: int) -> Value:
-        size: Value = Integer(len(items), c_pyssize_t_rprimitive)
-        return self.call_c(new_tuple_op, [size] + items, line)
+        if items:
+            size: Value = Integer(len(items), c_pyssize_t_rprimitive)
+            return self.call_c(new_tuple_op, [size] + items, line)
+        else:
+            return self.call_c(load_empty_tuple_constant_op, [], line)
 
     def new_tuple_with_length(self, length: Value, line: int) -> Value:
         """This function returns an uninitialized tuple.
diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index 8cd141545bbb..b4d3a0013ae7 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -64,6 +64,15 @@ typedef struct tuple_T4CIOO {
 } tuple_T4CIOO;
 #endif
 
+// System-wide empty tuple constant
+extern PyObject * __mypyc_empty_tuple__;
+
+static inline PyObject *CPyTuple_LoadEmptyTupleConstant() {
+#if !CPY_3_12_FEATURES
+    Py_INCREF(__mypyc_empty_tuple__);
+#endif
+    return __mypyc_empty_tuple__;
+}
 
 // Native object operations
 
diff --git a/mypyc/lib-rt/init.c b/mypyc/lib-rt/init.c
index 01b133233489..9215c2d59019 100644
--- a/mypyc/lib-rt/init.c
+++ b/mypyc/lib-rt/init.c
@@ -4,10 +4,21 @@
 struct ExcDummyStruct _CPy_ExcDummyStruct = { PyObject_HEAD_INIT(NULL) };
 PyObject *_CPy_ExcDummy = (PyObject *)&_CPy_ExcDummyStruct;
 
+// System-wide empty tuple constant
+PyObject * __mypyc_empty_tuple__ = NULL;
+
 // Because its dynamic linker is more restricted than linux/OS X,
 // Windows doesn't allow initializing globals with values from
 // other dynamic libraries. This means we need to initialize
 // things at load time.
 void CPy_Init(void) {
     _CPy_ExcDummyStruct.ob_base.ob_type = &PyBaseObject_Type;
+
+    // Initialize system-wide empty tuple constant
+    if (__mypyc_empty_tuple__ == NULL) {
+        __mypyc_empty_tuple__ = PyTuple_New(0);
+        if (!__mypyc_empty_tuple__) {
+            CPyError_OutOfMemory();
+        }
+    }
 }
diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py
index f262dec8b05a..ab23f8c441f5 100644
--- a/mypyc/primitives/tuple_ops.py
+++ b/mypyc/primitives/tuple_ops.py
@@ -55,6 +55,13 @@
     error_kind=ERR_MAGIC,
 )
 
+load_empty_tuple_constant_op = custom_op(
+    arg_types=[],
+    return_type=tuple_rprimitive,
+    c_function_name="CPyTuple_LoadEmptyTupleConstant",
+    error_kind=ERR_NEVER,
+)
+
 # PyTuple_SET_ITEM does no error checking,
 # and should only be used to fill in brand new tuples.
 new_tuple_set_item_op = custom_op(
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index 4eeeca04719c..feb7b36a2b52 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -1750,7 +1750,7 @@ L0:
     r7 = __main__.globals :: static
     r8 = 'f'
     r9 = CPyDict_GetItem(r7, r8)
-    r10 = PyTuple_Pack(0)
+    r10 = CPyTuple_LoadEmptyTupleConstant()
     r11 = PyDict_Copy(r6)
     r12 = PyObject_Call(r9, r10, r11)
     r13 = unbox(tuple[int, int, int], r12)
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index 3a9657d49f34..bfd32a523437 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -297,7 +297,7 @@ L2:
     r27 = CPyType_FromTemplate(r26, r24, r25)
     r28 = C_trait_vtable_setup()
     r29 = '__mypyc_attrs__'
-    r30 = PyTuple_Pack(0)
+    r30 = CPyTuple_LoadEmptyTupleConstant()
     r31 = PyObject_SetAttr(r27, r29, r30)
     r32 = r31 >= 0 :: signed
     __main__.C = r27 :: type
@@ -310,7 +310,7 @@ L2:
     r39 = __main__.S_template :: type
     r40 = CPyType_FromTemplate(r39, r37, r38)
     r41 = '__mypyc_attrs__'
-    r42 = PyTuple_Pack(0)
+    r42 = CPyTuple_LoadEmptyTupleConstant()
     r43 = PyObject_SetAttr(r40, r41, r42)
     r44 = r43 >= 0 :: signed
     __main__.S = r40 :: type

From abd9424039fd7b1f7da6b5c143a27c5b682371ee Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 28 Aug 2025 00:28:18 +0100
Subject: [PATCH 1585/1617] Use u8 for type/symbol tags (#19741)

While trying this I found that this is a bit fragile, in the sense that
`write_tag(data, 1)` (with a literal `1`) will not get specialized and
will go through slow path (btw @JukkaL is this a bug, should type of a
literal in fixed int type context be inferred as fixed int?) OTOH this
is probably not a big deal since no-one will use `write_tag()` with
literals, it will always be something like `write_tag(data, FUNC_DEF)`.
---
 mypy/cache.py                                 | 23 ++++++-----
 mypy/nodes.py                                 | 23 +++++------
 mypy/types.py                                 | 39 ++++++++++---------
 .../stubs/mypy-native/native_internal.pyi     |  6 ++-
 mypyc/lib-rt/native_internal.c                | 31 +++++++--------
 mypyc/lib-rt/native_internal.h                |  8 ++--
 mypyc/primitives/misc_ops.py                  | 11 +++---
 mypyc/test-data/irbuild-classes.test          | 12 ++++--
 mypyc/test-data/run-classes.test              | 19 +++++++--
 test-data/unit/lib-stub/native_internal.pyi   |  6 ++-
 10 files changed, 101 insertions(+), 77 deletions(-)

diff --git a/mypy/cache.py b/mypy/cache.py
index a16a36900c7a..08e3b05d1a75 100644
--- a/mypy/cache.py
+++ b/mypy/cache.py
@@ -3,6 +3,8 @@
 from collections.abc import Sequence
 from typing import TYPE_CHECKING, Final
 
+from mypy_extensions import u8
+
 try:
     from native_internal import (
         Buffer as Buffer,
@@ -34,10 +36,10 @@ def read_int(data: Buffer) -> int:
         def write_int(data: Buffer, value: int) -> None:
             raise NotImplementedError
 
-        def read_tag(data: Buffer) -> int:
+        def read_tag(data: Buffer) -> u8:
             raise NotImplementedError
 
-        def write_tag(data: Buffer, value: int) -> None:
+        def write_tag(data: Buffer, value: u8) -> None:
             raise NotImplementedError
 
         def read_str(data: Buffer) -> str:
@@ -59,15 +61,18 @@ def write_float(data: Buffer, value: float) -> None:
             raise NotImplementedError
 
 
-LITERAL_INT: Final = 1
-LITERAL_STR: Final = 2
-LITERAL_BOOL: Final = 3
-LITERAL_FLOAT: Final = 4
-LITERAL_COMPLEX: Final = 5
-LITERAL_NONE: Final = 6
+# Always use this type alias to refer to type tags.
+Tag = u8
+
+LITERAL_INT: Final[Tag] = 1
+LITERAL_STR: Final[Tag] = 2
+LITERAL_BOOL: Final[Tag] = 3
+LITERAL_FLOAT: Final[Tag] = 4
+LITERAL_COMPLEX: Final[Tag] = 5
+LITERAL_NONE: Final[Tag] = 6
 
 
-def read_literal(data: Buffer, tag: int) -> int | str | bool | float:
+def read_literal(data: Buffer, tag: Tag) -> int | str | bool | float:
     if tag == LITERAL_INT:
         return read_int(data)
     elif tag == LITERAL_STR:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 45e2b60c3e78..9cfc61c80b3e 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -18,6 +18,7 @@
     LITERAL_COMPLEX,
     LITERAL_NONE,
     Buffer,
+    Tag,
     read_bool,
     read_float,
     read_int,
@@ -4877,17 +4878,17 @@ def local_definitions(
                 yield from local_definitions(node.names, fullname, node)
 
 
-MYPY_FILE: Final = 0
-OVERLOADED_FUNC_DEF: Final = 1
-FUNC_DEF: Final = 2
-DECORATOR: Final = 3
-VAR: Final = 4
-TYPE_VAR_EXPR: Final = 5
-PARAM_SPEC_EXPR: Final = 6
-TYPE_VAR_TUPLE_EXPR: Final = 7
-TYPE_INFO: Final = 8
-TYPE_ALIAS: Final = 9
-CLASS_DEF: Final = 10
+MYPY_FILE: Final[Tag] = 0
+OVERLOADED_FUNC_DEF: Final[Tag] = 1
+FUNC_DEF: Final[Tag] = 2
+DECORATOR: Final[Tag] = 3
+VAR: Final[Tag] = 4
+TYPE_VAR_EXPR: Final[Tag] = 5
+PARAM_SPEC_EXPR: Final[Tag] = 6
+TYPE_VAR_TUPLE_EXPR: Final[Tag] = 7
+TYPE_INFO: Final[Tag] = 8
+TYPE_ALIAS: Final[Tag] = 9
+CLASS_DEF: Final[Tag] = 10
 
 
 def read_symbol(data: Buffer) -> mypy.nodes.SymbolNode:
diff --git a/mypy/types.py b/mypy/types.py
index 43e6dafe298e..8d5648ae0bda 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -12,6 +12,7 @@
 from mypy.bogus_type import Bogus
 from mypy.cache import (
     Buffer,
+    Tag,
     read_bool,
     read_int,
     read_int_list,
@@ -4120,25 +4121,25 @@ def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]:
     return tuple(args)
 
 
-TYPE_ALIAS_TYPE: Final = 1
-TYPE_VAR_TYPE: Final = 2
-PARAM_SPEC_TYPE: Final = 3
-TYPE_VAR_TUPLE_TYPE: Final = 4
-UNBOUND_TYPE: Final = 5
-UNPACK_TYPE: Final = 6
-ANY_TYPE: Final = 7
-UNINHABITED_TYPE: Final = 8
-NONE_TYPE: Final = 9
-DELETED_TYPE: Final = 10
-INSTANCE: Final = 11
-CALLABLE_TYPE: Final = 12
-OVERLOADED: Final = 13
-TUPLE_TYPE: Final = 14
-TYPED_DICT_TYPE: Final = 15
-LITERAL_TYPE: Final = 16
-UNION_TYPE: Final = 17
-TYPE_TYPE: Final = 18
-PARAMETERS: Final = 19
+TYPE_ALIAS_TYPE: Final[Tag] = 1
+TYPE_VAR_TYPE: Final[Tag] = 2
+PARAM_SPEC_TYPE: Final[Tag] = 3
+TYPE_VAR_TUPLE_TYPE: Final[Tag] = 4
+UNBOUND_TYPE: Final[Tag] = 5
+UNPACK_TYPE: Final[Tag] = 6
+ANY_TYPE: Final[Tag] = 7
+UNINHABITED_TYPE: Final[Tag] = 8
+NONE_TYPE: Final[Tag] = 9
+DELETED_TYPE: Final[Tag] = 10
+INSTANCE: Final[Tag] = 11
+CALLABLE_TYPE: Final[Tag] = 12
+OVERLOADED: Final[Tag] = 13
+TUPLE_TYPE: Final[Tag] = 14
+TYPED_DICT_TYPE: Final[Tag] = 15
+LITERAL_TYPE: Final[Tag] = 16
+UNION_TYPE: Final[Tag] = 17
+TYPE_TYPE: Final[Tag] = 18
+PARAMETERS: Final[Tag] = 19
 
 
 def read_type(data: Buffer) -> Type:
diff --git a/mypy/typeshed/stubs/mypy-native/native_internal.pyi b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
index 3c6a22c938e3..a47a4849fe20 100644
--- a/mypy/typeshed/stubs/mypy-native/native_internal.pyi
+++ b/mypy/typeshed/stubs/mypy-native/native_internal.pyi
@@ -1,3 +1,5 @@
+from mypy_extensions import u8
+
 class Buffer:
     def __init__(self, source: bytes = ...) -> None: ...
     def getvalue(self) -> bytes: ...
@@ -10,5 +12,5 @@ def write_float(data: Buffer, value: float) -> None: ...
 def read_float(data: Buffer) -> float: ...
 def write_int(data: Buffer, value: int) -> None: ...
 def read_int(data: Buffer) -> int: ...
-def write_tag(data: Buffer, value: int) -> None: ...
-def read_tag(data: Buffer) -> int: ...
+def write_tag(data: Buffer, value: u8) -> None: ...
+def read_tag(data: Buffer) -> u8: ...
diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
index 1c35eab946f8..3228f0330793 100644
--- a/mypyc/lib-rt/native_internal.c
+++ b/mypyc/lib-rt/native_internal.c
@@ -438,18 +438,18 @@ write_int(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
-static CPyTagged
+static uint8_t
 read_tag_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
-        return CPY_INT_TAG;
+        return CPY_LL_UINT_ERROR;
 
     if (_check_read((BufferObject *)data, 1) == 2)
-        return CPY_INT_TAG;
+        return CPY_LL_UINT_ERROR;
     char *buf = ((BufferObject *)data)->buf;
 
     uint8_t ret = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
     ((BufferObject *)data)->pos += 1;
-    return ((CPyTagged)ret) << 1;
+    return ret;
 }
 
 static PyObject*
@@ -458,27 +458,22 @@ read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
     PyObject *data = NULL;
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
         return NULL;
-    CPyTagged retval = read_tag_internal(data);
-    if (retval == CPY_INT_TAG) {
+    uint8_t retval = read_tag_internal(data);
+    if (retval == CPY_LL_UINT_ERROR && PyErr_Occurred()) {
         return NULL;
     }
-    return CPyTagged_StealAsObject(retval);
+    return PyLong_FromLong(retval);
 }
 
 static char
-write_tag_internal(PyObject *data, CPyTagged value) {
+write_tag_internal(PyObject *data, uint8_t value) {
     if (_check_buffer(data) == 2)
         return 2;
 
-    if (value > MAX_SHORT_INT_TAGGED) {
-        PyErr_SetString(PyExc_OverflowError, "value must fit in single byte");
-        return 2;
-    }
-
     if (_check_size((BufferObject *)data, 1) == 2)
         return 2;
     uint8_t *buf = (uint8_t *)((BufferObject *)data)->buf;
-    *(buf + ((BufferObject *)data)->pos) = (uint8_t)(value >> 1);
+    *(buf + ((BufferObject *)data)->pos) = value;
     ((BufferObject *)data)->pos += 1;
     ((BufferObject *)data)->end += 1;
     return 1;
@@ -491,12 +486,12 @@ write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
     PyObject *value = NULL;
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
         return NULL;
-    if (!PyLong_Check(value)) {
-        PyErr_SetString(PyExc_TypeError, "value must be an int");
+    uint8_t unboxed = CPyLong_AsUInt8(value);
+    if (unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred()) {
+        CPy_TypeError("u8", value);
         return NULL;
     }
-    CPyTagged tagged_value = CPyTagged_BorrowFromObject(value);
-    if (write_tag_internal(data, tagged_value) == 2) {
+    if (write_tag_internal(data, unboxed) == 2) {
         return NULL;
     }
     Py_INCREF(Py_None);
diff --git a/mypyc/lib-rt/native_internal.h b/mypyc/lib-rt/native_internal.h
index 5a8905f0e6f0..63e902a6e1bf 100644
--- a/mypyc/lib-rt/native_internal.h
+++ b/mypyc/lib-rt/native_internal.h
@@ -16,8 +16,8 @@ static char write_float_internal(PyObject *data, double value);
 static double read_float_internal(PyObject *data);
 static char write_int_internal(PyObject *data, CPyTagged value);
 static CPyTagged read_int_internal(PyObject *data);
-static char write_tag_internal(PyObject *data, CPyTagged value);
-static CPyTagged read_tag_internal(PyObject *data);
+static char write_tag_internal(PyObject *data, uint8_t value);
+static uint8_t read_tag_internal(PyObject *data);
 static int NativeInternal_ABI_Version(void);
 
 #else
@@ -35,8 +35,8 @@ static void **NativeInternal_API;
 #define read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8])
 #define write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9])
 #define read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10])
-#define write_tag_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[11])
-#define read_tag_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[12])
+#define write_tag_internal (*(char (*)(PyObject *source, uint8_t value)) NativeInternal_API[11])
+#define read_tag_internal (*(uint8_t (*)(PyObject *source)) NativeInternal_API[12])
 #define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13])
 
 static int
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index 5875d5d65e9b..943f6fc04b72 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER
+from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER
 from mypyc.ir.rtypes import (
     KNOWN_NATIVE_TYPES,
     bit_rprimitive,
@@ -20,6 +20,7 @@
     object_rprimitive,
     pointer_rprimitive,
     str_rprimitive,
+    uint8_rprimitive,
     void_rtype,
 )
 from mypyc.primitives.registry import (
@@ -426,16 +427,16 @@
 
 function_op(
     name="native_internal.write_tag",
-    arg_types=[object_rprimitive, int_rprimitive],
+    arg_types=[object_rprimitive, uint8_rprimitive],
     return_type=none_rprimitive,
     c_function_name="write_tag_internal",
-    error_kind=ERR_MAGIC,
+    error_kind=ERR_MAGIC_OVERLAPPING,
 )
 
 function_op(
     name="native_internal.read_tag",
     arg_types=[object_rprimitive],
-    return_type=int_rprimitive,
+    return_type=uint8_rprimitive,
     c_function_name="read_tag_internal",
-    error_kind=ERR_MAGIC,
+    error_kind=ERR_MAGIC_OVERLAPPING,
 )
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index bfd32a523437..b49b20e13a43 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1410,18 +1410,23 @@ class TestOverload:
         return x
 
 [case testNativeBufferFastPath]
+from typing import Final
+from mypy_extensions import u8
 from native_internal import (
     Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
     write_int, read_int, write_tag, read_tag
 )
 
+Tag = u8
+TAG: Final[Tag] = 1
+
 def foo() -> None:
     b = Buffer()
     write_str(b, "foo")
     write_bool(b, True)
     write_float(b, 0.1)
     write_int(b, 1)
-    write_tag(b, 1)
+    write_tag(b, TAG)
 
     b = Buffer(b.getvalue())
     x = read_str(b)
@@ -1439,7 +1444,8 @@ def foo():
     r9, x :: str
     r10, y :: bool
     r11, z :: float
-    r12, t, r13, u :: int
+    r12, t :: int
+    r13, u :: u8
 L0:
     r0 = Buffer_internal_empty()
     b = r0
@@ -1448,7 +1454,7 @@ L0:
     r3 = write_bool_internal(b, 1)
     r4 = write_float_internal(b, 0.1)
     r5 = write_int_internal(b, 2)
-    r6 = write_tag_internal(b, 2)
+    r6 = write_tag_internal(b, 1)
     r7 = Buffer_getvalue_internal(b)
     r8 = Buffer_internal(r7)
     b = r8
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index dc64680f67c1..edc989ea641c 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2711,11 +2711,18 @@ from native import Player
 Player.MIN = 
 
 [case testBufferRoundTrip_native_libs]
+from typing import Final
+from mypy_extensions import u8
 from native_internal import (
     Buffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
     write_int, read_int, write_tag, read_tag
 )
 
+Tag = u8
+TAG_A: Final[Tag] = 33
+TAG_B: Final[Tag] = 255
+TAG_SPECIAL: Final[Tag] = 239
+
 def test_buffer_basic() -> None:
     b = Buffer(b"foo")
     assert b.getvalue() == b"foo"
@@ -2729,8 +2736,9 @@ def test_buffer_roundtrip() -> None:
     write_float(b, 0.1)
     write_int(b, 0)
     write_int(b, 1)
-    write_tag(b, 33)
-    write_tag(b, 255)
+    write_tag(b, TAG_A)
+    write_tag(b, TAG_SPECIAL)
+    write_tag(b, TAG_B)
     write_int(b, 2)
     write_int(b, 2 ** 85)
     write_int(b, -1)
@@ -2743,8 +2751,9 @@ def test_buffer_roundtrip() -> None:
     assert read_float(b) == 0.1
     assert read_int(b) == 0
     assert read_int(b) == 1
-    assert read_tag(b) == 33
-    assert read_tag(b) == 255
+    assert read_tag(b) == TAG_A
+    assert read_tag(b) == TAG_SPECIAL
+    assert read_tag(b) == TAG_B
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
     assert read_int(b) == -1
@@ -2769,6 +2778,7 @@ def test_buffer_roundtrip_interpreted() -> None:
     write_int(b, 0)
     write_int(b, 1)
     write_tag(b, 33)
+    write_tag(b, 239)
     write_tag(b, 255)
     write_int(b, 2)
     write_int(b, 2 ** 85)
@@ -2783,6 +2793,7 @@ def test_buffer_roundtrip_interpreted() -> None:
     assert read_int(b) == 0
     assert read_int(b) == 1
     assert read_tag(b) == 33
+    assert read_tag(b) == 239
     assert read_tag(b) == 255
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
diff --git a/test-data/unit/lib-stub/native_internal.pyi b/test-data/unit/lib-stub/native_internal.pyi
index 3c6a22c938e3..a47a4849fe20 100644
--- a/test-data/unit/lib-stub/native_internal.pyi
+++ b/test-data/unit/lib-stub/native_internal.pyi
@@ -1,3 +1,5 @@
+from mypy_extensions import u8
+
 class Buffer:
     def __init__(self, source: bytes = ...) -> None: ...
     def getvalue(self) -> bytes: ...
@@ -10,5 +12,5 @@ def write_float(data: Buffer, value: float) -> None: ...
 def read_float(data: Buffer) -> float: ...
 def write_int(data: Buffer, value: int) -> None: ...
 def read_int(data: Buffer) -> int: ...
-def write_tag(data: Buffer, value: int) -> None: ...
-def read_tag(data: Buffer) -> int: ...
+def write_tag(data: Buffer, value: u8) -> None: ...
+def read_tag(data: Buffer) -> u8: ...

From e633140ba672d97b9300a44d0cae9fd38db28b2c Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Thu, 28 Aug 2025 14:26:21 +0200
Subject: [PATCH 1586/1617] Do not use outer context for `or` expr inference if
 the LHS has Any (#19748)

Fixes #19492.

In #19695 I tried to add another set of heuristics to `any_constraints`,
but can't get that working: trying to join/meet all similar constraints
together breaks inference in other cases, and only doing that for Any
would be somewhat non-trivial. This PR reverts behaviour introduced in
#19492 when LHS of the expression contains `Any`. Outer context is still
included in all other cases, and this seems to strike a good balance.
---
 mypy/checkexpr.py                           |  4 ++++
 test-data/unit/check-inference-context.test | 10 ++++++++++
 2 files changed, 14 insertions(+)

diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 88b3005b1376..fd83b6359ddc 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6004,6 +6004,10 @@ def analyze_cond_branch(
     def _combined_context(self, ty: Type | None) -> Type | None:
         ctx_items = []
         if ty is not None:
+            if has_any_type(ty):
+                # HACK: Any should be contagious, `dict[str, Any] or ` should still
+                # infer Any in x.
+                return ty
             ctx_items.append(ty)
         if self.type_context and self.type_context[-1] is not None:
             ctx_items.append(self.type_context[-1])
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index 5a674cca09da..cd44fb5b85cd 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -1530,3 +1530,13 @@ def check3(use: bool, val: str) -> "str | Literal[False]":
 def check4(use: bool, val: str) -> "str | bool":
     return use and identity(val)
 [builtins fixtures/tuple.pyi]
+
+[case testDictAnyOrLiteralInContext]
+from typing import Union, Optional, Any
+
+def f(x: dict[str, Union[str, None, int]]) -> None:
+    pass
+
+def g(x: Optional[dict[str, Any]], s: Optional[str]) -> None:
+    f(x or {'x': s})
+[builtins fixtures/dict.pyi]

From ae87821dd6fe2cf29402a5bb009d72474a97c835 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Thu, 28 Aug 2025 14:59:24 +0100
Subject: [PATCH 1587/1617] Don't write constructor cache without strict
 optional (#19752)

Fixes https://github.com/python/mypy/issues/19751

Fix is straightforward: don't write cache when it is not safe to.
---
 mypy/typeops.py                   |  6 ++++--
 test-data/unit/check-classes.test | 16 ++++++++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/mypy/typeops.py b/mypy/typeops.py
index 0cb6018d01fd..87a4d8cefd13 100644
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -208,7 +208,7 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
                     fallback=named_type("builtins.function"),
                 )
                 result: FunctionLike = class_callable(sig, info, fallback, None, is_new=False)
-                if allow_cache:
+                if allow_cache and state.strict_optional:
                     info.type_object_type = result
                 return result
 
@@ -230,7 +230,9 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P
         assert isinstance(method.type, FunctionLike)  # is_valid_constructor() ensures this
         t = method.type
     result = type_object_type_from_function(t, info, method.info, fallback, is_new)
-    if allow_cache:
+    # Only write cached result is strict_optional=True, otherwise we may get
+    # inconsistent behaviour because of union simplification.
+    if allow_cache and state.strict_optional:
         info.type_object_type = result
     return result
 
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index 5cc4910fb265..498a2c12b6e8 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -9273,3 +9273,19 @@ class Bar:
 [file foo.py]
 class Bar:
     ...
+
+[case testConstructorWithoutStrictOptionalNoCache]
+import mod
+a = mod.NT(x=None)  # OK
+
+[file typ.py]
+from typing import NamedTuple, Optional
+NT = NamedTuple("NT", [("x", Optional[str])])
+
+[file mod.py]
+# mypy: no-strict-optional
+from typ import NT
+
+def f() -> NT:
+    return NT(x='')
+[builtins fixtures/tuple.pyi]

From dcc76ea2ef7bc56ab185ab5832583be998ad6a12 Mon Sep 17 00:00:00 2001
From: Randolf Scholz 
Date: Thu, 28 Aug 2025 23:56:12 +0200
Subject: [PATCH 1588/1617] Fix crash with variadic tuple arguments to generic
 type (#19705)

Fixes #19704
---
 mypy/semanal.py                         |  5 ++++-
 mypy/typeanal.py                        |  2 +-
 test-data/unit/check-python312.test     | 11 ++++++-----
 test-data/unit/check-typevar-tuple.test | 18 +++++++++---------
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index 77e6b0c005e2..fa5d9fdc82c4 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -295,6 +295,7 @@
     UnboundType,
     UnionType,
     UnpackType,
+    flatten_nested_tuples,
     get_proper_type,
     get_proper_types,
     has_type_vars,
@@ -6093,7 +6094,9 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
             types.append(analyzed)
 
         if allow_unpack:
-            types = self.type_analyzer().check_unpacks_in_list(types)
+            # need to flatten away harmless unpacks like Unpack[tuple[int]]
+            flattened_items = flatten_nested_tuples(types)
+            types = self.type_analyzer().check_unpacks_in_list(flattened_items)
         if has_param_spec and num_args == 1 and types:
             first_arg = get_proper_type(types[0])
             single_any = len(types) == 1 and isinstance(first_arg, AnyType)
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index 204d3061c734..d44b13880cbb 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -1978,7 +1978,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]:
 
         if num_unpacks > 1:
             assert final_unpack is not None
-            self.fail("More than one Unpack in a type is not allowed", final_unpack.type)
+            self.fail("More than one variadic Unpack in a type is not allowed", final_unpack.type)
         return new_items
 
     def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType:
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 817184dc561c..01364bdfa32a 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2038,12 +2038,13 @@ class Z: ...  # E: Name "Z" already defined on line 2
 # https://github.com/python/mypy/issues/18856
 class A[*Ts]: ...
 
-A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-a: A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ...  # E: More than one Unpack in a type is not allowed
+A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+A[*tuple[*tuple[int, ...]], *tuple[*tuple[int, ...]]]  # E: More than one variadic Unpack in a type is not allowed
+a: A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ...  # E: More than one variadic Unpack in a type is not allowed
 
-tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 2de2e45f0a96..c668f14eaa50 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -123,7 +123,7 @@ reveal_type(empty)  # N: Revealed type is "__main__.Variadic[()]"
 omitted: Variadic
 reveal_type(omitted)  # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[Any, ...]]]"
 
-bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]]  # E: More than one Unpack in a type is not allowed
+bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad)  # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[builtins.int, ...]], builtins.str]"
 
 bad2: Unpack[Tuple[int, ...]]  # E: Unpack is only valid in a variadic position
@@ -353,12 +353,12 @@ expect_variadic_array_2(u)
 Ts = TypeVarTuple("Ts")
 Ts2 = TypeVarTuple("Ts2")
 
-def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one Unpack in a type is not allowed
+def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one variadic Unpack in a type is not allowed
 
     ...
 reveal_type(bad)  # N: Revealed type is "def [Ts, Ts2] (x: tuple[builtins.int, Unpack[Ts`-1], builtins.str])"
 
-def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None:  # E: More than one Unpack in a type is not allowed
+def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None:  # E: More than one variadic Unpack in a type is not allowed
     ...
 reveal_type(bad2)  # N: Revealed type is "def (x: tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])"
 [builtins fixtures/tuple.pyi]
@@ -571,7 +571,7 @@ from typing_extensions import Unpack, TypeVarTuple
 
 Ts = TypeVarTuple("Ts")
 Us = TypeVarTuple("Us")
-a: Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one Unpack in a type is not allowed
+a: Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(a)  # N: Revealed type is "def [Ts, Us] (*Unpack[Ts`-1]) -> builtins.int"
 b: Callable[[Unpack], int]  # E: Unpack[...] requires exactly one type argument
 reveal_type(b)  # N: Revealed type is "def (*Any) -> builtins.int"
@@ -725,15 +725,15 @@ Ts = TypeVarTuple("Ts")
 Us = TypeVarTuple("Us")
 class G(Generic[Unpack[Ts]]): ...
 
-A = Tuple[Unpack[Ts], Unpack[Us]]  # E: More than one Unpack in a type is not allowed
+A = Tuple[Unpack[Ts], Unpack[Us]]  # E: More than one variadic Unpack in a type is not allowed
 x: A[int, str]
 reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
-B = Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one Unpack in a type is not allowed
+B = Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one variadic Unpack in a type is not allowed
 y: B[int, str]
 reveal_type(y)  # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int"
 
-C = G[Unpack[Ts], Unpack[Us]]  # E: More than one Unpack in a type is not allowed
+C = G[Unpack[Ts], Unpack[Us]]  # E: More than one variadic Unpack in a type is not allowed
 z: C[int, str]
 reveal_type(z)  # N: Revealed type is "__main__.G[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
@@ -2223,12 +2223,12 @@ cb2(1, 2, 3, a="a", b="b")
 cb2(1, a="a", b="b")  # E: Too few arguments
 cb2(1, 2, 3, a="a")  # E: Missing named argument "b"
 
-bad1: Callable[[Unpack[Ints], Unpack[Ints]], None]  # E: More than one Unpack in a type is not allowed
+bad1: Callable[[Unpack[Ints], Unpack[Ints]], None]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad1)  # N: Revealed type is "def (*builtins.int)"
 bad2: Callable[[Unpack[Keywords], Unpack[Keywords]], None]  # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple)
 reveal_type(bad2)  # N: Revealed type is "def (*Any, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])"
 bad3: Callable[[Unpack[Keywords], Unpack[Ints]], None]  # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) \
-                                                        # E: More than one Unpack in a type is not allowed
+                                                        # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad3)  # N: Revealed type is "def (*Any)"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]

From 6a88c2135cf35875c4bcdc61c0bddd613d72281c Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 29 Aug 2025 10:53:51 +0100
Subject: [PATCH 1589/1617] Use more compact cache representation for int and
 str (#19750)

After looking more at some real data I found that:
* More than 99.9% of all `int`s are between -10 and 117. Values are a
bit arbitrary TBH, the idea is that we should include small negative
values (for `TypeVarId`s) and still be able to fit them in 1 byte.
* More than 99.9% of strings are shorter that 128 bytes (again the idea
is to fit the length into a single byte)

Note there are very few integers that would fit in two bytes currently.
This is because we only store line for type alias nodes, and type
aliases are usually defined at the top of a module. We can add special
case for two bytes later when needed.

We could probably save another byte for long strings and medium
integers, but I don't want to have anything fancy that would only affect
less than 0.1% cases.

Finally you may notice I add a small correctness change I noticed
accidentally when working on this, it is not really related, but it is
so minor that it doesn't deserve a separate PR.
---
 mypy/types.py                    |   3 +-
 mypyc/lib-rt/native_internal.c   | 133 +++++++++++++++++++++++++------
 mypyc/primitives/misc_ops.py     |   2 +-
 mypyc/test-data/run-classes.test |  64 +++++++++++++++
 4 files changed, 174 insertions(+), 28 deletions(-)

diff --git a/mypy/types.py b/mypy/types.py
index 8d5648ae0bda..e0265e601e0c 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1674,7 +1674,7 @@ def __eq__(self, other: object) -> bool:
     def serialize(self) -> JsonDict | str:
         assert self.type is not None
         type_ref = self.type.fullname
-        if not self.args and not self.last_known_value:
+        if not self.args and not self.last_known_value and not self.extra_attrs:
             return type_ref
         data: JsonDict = {
             ".class": "Instance",
@@ -1745,7 +1745,6 @@ def copy_modified(
             ),
             extra_attrs=self.extra_attrs,
         )
-        # We intentionally don't copy the extra_attrs here, so they will be erased.
         new.can_be_true = self.can_be_true
         new.can_be_false = self.can_be_false
         return new
diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
index 3228f0330793..1c211464b19c 100644
--- a/mypyc/lib-rt/native_internal.c
+++ b/mypyc/lib-rt/native_internal.c
@@ -8,6 +8,14 @@
 #define START_SIZE 512
 #define MAX_SHORT_INT_TAGGED (255 << 1)
 
+#define MAX_SHORT_LEN 127
+#define LONG_STR_TAG 1
+
+#define MIN_SHORT_INT -10
+#define MAX_SHORT_INT 117
+#define MEDIUM_INT_TAG 1
+#define LONG_INT_TAG 3
+
 typedef struct {
     PyObject_HEAD
     Py_ssize_t pos;
@@ -166,6 +174,12 @@ _check_read(BufferObject *data, Py_ssize_t need) {
     return 1;
 }
 
+/*
+bool format: single byte
+    \x00 - False
+    \x01 - True
+*/
+
 static char
 read_bool_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
@@ -225,20 +239,34 @@ write_bool(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
+/*
+str format: size followed by UTF-8 bytes
+    short strings (len <= 127): single byte for size as `(uint8_t)size << 1`
+    long strings: \x01 followed by size as Py_ssize_t
+*/
+
 static PyObject*
 read_str_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
         return NULL;
 
-    if (_check_read((BufferObject *)data, sizeof(Py_ssize_t)) == 2)
-        return NULL;
+    Py_ssize_t size;
     char *buf = ((BufferObject *)data)->buf;
     // Read string length.
-    Py_ssize_t size = *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
-    if (_check_read((BufferObject *)data, size) == 2)
+    if (_check_read((BufferObject *)data, 1) == 2)
         return NULL;
+    uint8_t first = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += 1;
+    if (first != LONG_STR_TAG) {
+        // Common case: short string (len <= 127).
+        size = (Py_ssize_t)(first >> 1);
+    } else {
+        size = *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos);
+        ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+    }
     // Read string content.
+    if (_check_read((BufferObject *)data, size) == 2)
+        return NULL;
     PyObject *res = PyUnicode_FromStringAndSize(
         buf + ((BufferObject *)data)->pos, (Py_ssize_t)size
     );
@@ -266,14 +294,28 @@ write_str_internal(PyObject *data, PyObject *value) {
     const char *chunk = PyUnicode_AsUTF8AndSize(value, &size);
     if (!chunk)
         return 2;
-    Py_ssize_t need = size + sizeof(Py_ssize_t);
-    if (_check_size((BufferObject *)data, need) == 2)
-        return 2;
 
-    char *buf = ((BufferObject *)data)->buf;
+    Py_ssize_t need;
+    char *buf;
     // Write string length.
-    *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos) = size;
-    ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+    if (size <= MAX_SHORT_LEN) {
+        // Common case: short string (len <= 127) store as single byte.
+        need = size + 1;
+        if (_check_size((BufferObject *)data, need) == 2)
+            return 2;
+        buf = ((BufferObject *)data)->buf;
+        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = (uint8_t)size << 1;
+        ((BufferObject *)data)->pos += 1;
+    } else {
+        need = size + sizeof(Py_ssize_t) + 1;
+        if (_check_size((BufferObject *)data, need) == 2)
+            return 2;
+        buf = ((BufferObject *)data)->buf;
+        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = LONG_STR_TAG;
+        ((BufferObject *)data)->pos += 1;
+        *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos) = size;
+        ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+    }
     // Write string content.
     memcpy(buf + ((BufferObject *)data)->pos, chunk, size);
     ((BufferObject *)data)->pos += size;
@@ -299,6 +341,11 @@ write_str(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
+/*
+float format:
+    stored as a C double
+*/
+
 static double
 read_float_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
@@ -357,19 +404,33 @@ write_float(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
+/*
+int format:
+    most common values (-10 <= value <= 117): single byte as `(uint8_t)(value + 10) << 1`
+    medium values (fit in CPyTagged): \x01 followed by CPyTagged value
+    long values (very rare): \x03 followed by decimal string (see str format)
+*/
+
 static CPyTagged
 read_int_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
         return CPY_INT_TAG;
 
-    if (_check_read((BufferObject *)data, sizeof(CPyTagged)) == 2)
-        return CPY_INT_TAG;
     char *buf = ((BufferObject *)data)->buf;
+    if (_check_read((BufferObject *)data, 1) == 2)
+        return CPY_INT_TAG;
 
-    CPyTagged ret = *(CPyTagged *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += sizeof(CPyTagged);
-    if ((ret & CPY_INT_TAG) == 0)
+    uint8_t first = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
+    ((BufferObject *)data)->pos += 1;
+    if ((first & MEDIUM_INT_TAG) == 0) {
+       // Most common case: int that is small in absolute value.
+       return ((Py_ssize_t)(first >> 1) + MIN_SHORT_INT) << 1;
+    }
+    if (first == MEDIUM_INT_TAG) {
+        CPyTagged ret = *(CPyTagged *)(buf + ((BufferObject *)data)->pos);
+        ((BufferObject *)data)->pos += sizeof(CPyTagged);
         return ret;
+    }
     // People who have literal ints not fitting in size_t should be punished :-)
     PyObject *str_ret = read_str_internal(data);
     if (str_ret == NULL)
@@ -397,17 +458,34 @@ write_int_internal(PyObject *data, CPyTagged value) {
     if (_check_buffer(data) == 2)
         return 2;
 
-    if (_check_size((BufferObject *)data, sizeof(CPyTagged)) == 2)
-        return 2;
-    char *buf = ((BufferObject *)data)->buf;
+    char *buf;
     if ((value & CPY_INT_TAG) == 0) {
-        *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = value;
+        Py_ssize_t real_value = CPyTagged_ShortAsSsize_t(value);
+        if (real_value >= MIN_SHORT_INT && real_value <= MAX_SHORT_INT) {
+            // Most common case: int that is small in absolute value.
+            if (_check_size((BufferObject *)data, 1) == 2)
+                return 2;
+            buf = ((BufferObject *)data)->buf;
+            *(uint8_t *)(buf + ((BufferObject *)data)->pos) = (uint8_t)(real_value - MIN_SHORT_INT) << 1;
+            ((BufferObject *)data)->pos += 1;
+            ((BufferObject *)data)->end += 1;
+        } else {
+            if (_check_size((BufferObject *)data, sizeof(CPyTagged) + 1) == 2)
+                return 2;
+            buf = ((BufferObject *)data)->buf;
+            *(uint8_t *)(buf + ((BufferObject *)data)->pos) = MEDIUM_INT_TAG;
+            ((BufferObject *)data)->pos += 1;
+            *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = value;
+            ((BufferObject *)data)->pos += sizeof(CPyTagged);
+            ((BufferObject *)data)->end += sizeof(CPyTagged) + 1;
+        }
     } else {
-        *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = CPY_INT_TAG;
-    }
-    ((BufferObject *)data)->pos += sizeof(CPyTagged);
-    ((BufferObject *)data)->end += sizeof(CPyTagged);
-    if ((value & CPY_INT_TAG) != 0) {
+        if (_check_size((BufferObject *)data, 1) == 2)
+            return 2;
+        buf = ((BufferObject *)data)->buf;
+        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = LONG_INT_TAG;
+        ((BufferObject *)data)->pos += 1;
+        ((BufferObject *)data)->end += 1;
         PyObject *str_value = PyObject_Str(CPyTagged_LongAsObject(value));
         if (str_value == NULL)
             return 2;
@@ -438,6 +516,11 @@ write_int(PyObject *self, PyObject *args, PyObject *kwds) {
     return Py_None;
 }
 
+/*
+integer tag format (0 <= t <= 255):
+    stored as a uint8_t
+*/
+
 static uint8_t
 read_tag_internal(PyObject *data) {
     if (_check_buffer(data) == 2)
diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py
index 943f6fc04b72..8e6e450c64dc 100644
--- a/mypyc/primitives/misc_ops.py
+++ b/mypyc/primitives/misc_ops.py
@@ -430,7 +430,7 @@
     arg_types=[object_rprimitive, uint8_rprimitive],
     return_type=none_rprimitive,
     c_function_name="write_tag_internal",
-    error_kind=ERR_MAGIC_OVERLAPPING,
+    error_kind=ERR_MAGIC,
 )
 
 function_op(
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index edc989ea641c..2e55ee70687e 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -2741,7 +2741,11 @@ def test_buffer_roundtrip() -> None:
     write_tag(b, TAG_B)
     write_int(b, 2)
     write_int(b, 2 ** 85)
+    write_int(b, 255)
     write_int(b, -1)
+    write_int(b, -255)
+    write_int(b, 1234512344)
+    write_int(b, 1234512345)
 
     b = Buffer(b.getvalue())
     assert read_str(b) == "foo"
@@ -2756,13 +2760,41 @@ def test_buffer_roundtrip() -> None:
     assert read_tag(b) == TAG_B
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
+    assert read_int(b) == 255
     assert read_int(b) == -1
+    assert read_int(b) == -255
+    assert read_int(b) == 1234512344
+    assert read_int(b) == 1234512345
+
+def test_buffer_int_size() -> None:
+    for i in (-10, -9, 0, 116, 117):
+        b = Buffer()
+        write_int(b, i)
+        assert len(b.getvalue()) == 1
+        b = Buffer(b.getvalue())
+        assert read_int(b) == i
+    for i in (-12345, -12344, -11, 118, 12344, 12345):
+        b = Buffer()
+        write_int(b, i)
+        assert len(b.getvalue()) <= 9  # sizeof(size_t) + 1
+        b = Buffer(b.getvalue())
+        assert read_int(b) == i
+
+def test_buffer_str_size() -> None:
+    for s in ("", "a", "a" * 127):
+        b = Buffer()
+        write_str(b, s)
+        assert len(b.getvalue()) == len(s) + 1
+        b = Buffer(b.getvalue())
+        assert read_str(b) == s
 
 [file driver.py]
 from native import *
 
 test_buffer_basic()
 test_buffer_roundtrip()
+test_buffer_int_size()
+test_buffer_str_size()
 
 def test_buffer_basic_interpreted() -> None:
     b = Buffer(b"foo")
@@ -2782,7 +2814,11 @@ def test_buffer_roundtrip_interpreted() -> None:
     write_tag(b, 255)
     write_int(b, 2)
     write_int(b, 2 ** 85)
+    write_int(b, 255)
     write_int(b, -1)
+    write_int(b, -255)
+    write_int(b, 1234512344)
+    write_int(b, 1234512345)
 
     b = Buffer(b.getvalue())
     assert read_str(b) == "foo"
@@ -2797,10 +2833,38 @@ def test_buffer_roundtrip_interpreted() -> None:
     assert read_tag(b) == 255
     assert read_int(b) == 2
     assert read_int(b) == 2 ** 85
+    assert read_int(b) == 255
     assert read_int(b) == -1
+    assert read_int(b) == -255
+    assert read_int(b) == 1234512344
+    assert read_int(b) == 1234512345
+
+def test_buffer_int_size_interpreted() -> None:
+    for i in (-10, -9, 0, 116, 117):
+        b = Buffer()
+        write_int(b, i)
+        assert len(b.getvalue()) == 1
+        b = Buffer(b.getvalue())
+        assert read_int(b) == i
+    for i in (-12345, -12344, -11, 118, 12344, 12345):
+        b = Buffer()
+        write_int(b, i)
+        assert len(b.getvalue()) <= 9  # sizeof(size_t) + 1
+        b = Buffer(b.getvalue())
+        assert read_int(b) == i
+
+def test_buffer_str_size_interpreted() -> None:
+    for s in ("", "a", "a" * 127):
+        b = Buffer()
+        write_str(b, s)
+        assert len(b.getvalue()) == len(s) + 1
+        b = Buffer(b.getvalue())
+        assert read_str(b) == s
 
 test_buffer_basic_interpreted()
 test_buffer_roundtrip_interpreted()
+test_buffer_int_size_interpreted()
+test_buffer_str_size_interpreted()
 
 [case testEnumMethodCalls]
 from enum import Enum

From d9c77cbe827123be16dd4487bf7cd8248c643100 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 29 Aug 2025 12:56:51 +0100
Subject: [PATCH 1590/1617] [mypyc] Refactor IR build of equality and unary
 operators (#19756)

Make the code cleaner. This will also make it easier to implement
additional optimizations.

This probably fixes a few bugs as well.
---
 mypyc/irbuild/ll_builder.py | 125 ++++++++++++++++++++++--------------
 1 file changed, 76 insertions(+), 49 deletions(-)

diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 112bbdbb50ed..f82be9fe706f 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -1395,12 +1395,6 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
         # Special case various ops
         if op in ("is", "is not"):
             return self.translate_is_op(lreg, rreg, op, line)
-        # TODO: modify 'str' to use same interface as 'compare_bytes' as it avoids
-        # call to PyErr_Occurred()
-        if is_str_rprimitive(ltype) and is_str_rprimitive(rtype) and op in ("==", "!="):
-            return self.compare_strings(lreg, rreg, op, line)
-        if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype) and op in ("==", "!="):
-            return self.compare_bytes(lreg, rreg, op, line)
         if (
             is_bool_or_bit_rprimitive(ltype)
             and is_bool_or_bit_rprimitive(rtype)
@@ -1496,6 +1490,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value:
     def dunder_op(self, lreg: Value, rreg: Value | None, op: str, line: int) -> Value | None:
         """
         Dispatch a dunder method if applicable.
+
         For example for `a + b` it will use `a.__add__(b)` which can lead to higher performance
         due to the fact that the method could be already compiled and optimized instead of going
         all the way through `PyNumber_Add(a, b)` python api (making a jump into the python DL).
@@ -1545,6 +1540,10 @@ def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value:
         elif op == "!=":
             eq = self.primitive_op(str_eq, [lhs, rhs], line)
             return self.add(ComparisonOp(eq, self.false(), ComparisonOp.EQ, line))
+
+        # TODO: modify 'str' to use same interface as 'compare_bytes' as it would avoid
+        # call to PyErr_Occurred() below
+
         compare_result = self.call_c(unicode_compare, [lhs, rhs], line)
         error_constant = Integer(-1, c_int_rprimitive, line)
         compare_error_check = self.add(
@@ -1648,55 +1647,75 @@ def bool_comparison_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Va
         op_id = ComparisonOp.signed_ops[op]
         return self.comparison_op(lreg, rreg, op_id, line)
 
-    def unary_not(self, value: Value, line: int) -> Value:
-        mask = Integer(1, value.type, line)
-        return self.int_op(value.type, value, mask, IntOp.XOR, line)
+    def _non_specialized_unary_op(self, value: Value, op: str, line: int) -> Value:
+        if isinstance(value.type, RInstance):
+            result = self.dunder_op(value, None, op, line)
+            if result is not None:
+                return result
+        primitive_ops_candidates = unary_ops.get(op, [])
+        target = self.matching_primitive_op(primitive_ops_candidates, [value], line)
+        assert target, "Unsupported unary operation: %s" % op
+        return target
 
-    def unary_op(self, value: Value, expr_op: str, line: int) -> Value:
+    def unary_not(self, value: Value, line: int) -> Value:
+        """Perform unary 'not'."""
         typ = value.type
         if is_bool_or_bit_rprimitive(typ):
-            if expr_op == "not":
-                return self.unary_not(value, line)
-            if expr_op == "+":
-                return value
-        if is_fixed_width_rtype(typ):
-            if expr_op == "-":
-                # Translate to '0 - x'
-                return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line)
-            elif expr_op == "~":
-                if typ.is_signed:
-                    # Translate to 'x ^ -1'
-                    return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line)
-                else:
-                    # Translate to 'x ^ 0xff...'
-                    mask = (1 << (typ.size * 8)) - 1
-                    return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line)
-            elif expr_op == "+":
-                return value
-        if is_float_rprimitive(typ):
-            if expr_op == "-":
-                return self.add(FloatNeg(value, line))
-            elif expr_op == "+":
-                return value
+            mask = Integer(1, typ, line)
+            return self.int_op(typ, value, mask, IntOp.XOR, line)
+        return self._non_specialized_unary_op(value, "not", line)
 
+    def unary_minus(self, value: Value, line: int) -> Value:
+        """Perform unary '-'."""
+        typ = value.type
         if isinstance(value, Integer):
             # TODO: Overflow? Unsigned?
-            num = value.value
-            if is_short_int_rprimitive(typ):
-                num >>= 1
-            return Integer(-num, typ, value.line)
-        if is_tagged(typ) and expr_op == "+":
+            return Integer(-value.numeric_value(), typ, line)
+        elif isinstance(value, Float):
+            return Float(-value.value, line)
+        elif is_fixed_width_rtype(typ):
+            # Translate to '0 - x'
+            return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line)
+        elif is_float_rprimitive(typ):
+            return self.add(FloatNeg(value, line))
+        return self._non_specialized_unary_op(value, "-", line)
+
+    def unary_plus(self, value: Value, line: int) -> Value:
+        """Perform unary '+'."""
+        typ = value.type
+        if (
+            is_tagged(typ)
+            or is_float_rprimitive(typ)
+            or is_bool_or_bit_rprimitive(typ)
+            or is_fixed_width_rtype(typ)
+        ):
             return value
-        if isinstance(value, Float):
-            return Float(-value.value, value.line)
-        if isinstance(typ, RInstance):
-            result = self.dunder_op(value, None, expr_op, line)
-            if result is not None:
-                return result
-        primitive_ops_candidates = unary_ops.get(expr_op, [])
-        target = self.matching_primitive_op(primitive_ops_candidates, [value], line)
-        assert target, "Unsupported unary operation: %s" % expr_op
-        return target
+        return self._non_specialized_unary_op(value, "+", line)
+
+    def unary_invert(self, value: Value, line: int) -> Value:
+        """Perform unary '~'."""
+        typ = value.type
+        if is_fixed_width_rtype(typ):
+            if typ.is_signed:
+                # Translate to 'x ^ -1'
+                return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line)
+            else:
+                # Translate to 'x ^ 0xff...'
+                mask = (1 << (typ.size * 8)) - 1
+                return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line)
+        return self._non_specialized_unary_op(value, "~", line)
+
+    def unary_op(self, value: Value, op: str, line: int) -> Value:
+        """Perform a unary operation."""
+        if op == "not":
+            return self.unary_not(value, line)
+        elif op == "-":
+            return self.unary_minus(value, line)
+        elif op == "+":
+            return self.unary_plus(value, line)
+        elif op == "~":
+            return self.unary_invert(value, line)
+        raise RuntimeError("Unsupported unary operation: %s" % op)
 
     def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value:
         result: Value | None = None
@@ -2480,13 +2499,21 @@ def translate_special_method_call(
         return primitive_op
 
     def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value | None:
-        """Add a equality comparison operation.
+        """Add an equality comparison operation.
+
+        Note that this doesn't cover all possible types.
 
         Args:
             expr_op: either '==' or '!='
         """
         ltype = lreg.type
         rtype = rreg.type
+
+        if is_str_rprimitive(ltype) and is_str_rprimitive(rtype):
+            return self.compare_strings(lreg, rreg, expr_op, line)
+        if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype):
+            return self.compare_bytes(lreg, rreg, expr_op, line)
+
         if not (isinstance(ltype, RInstance) and ltype == rtype):
             return None
 

From 6a97dc63b4291633ecda87c74f5e8d98d987b8b7 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 29 Aug 2025 14:55:49 +0100
Subject: [PATCH 1591/1617] [mypyc] Speed up equality with optional str/bytes
 types (#19758)

Specialize most equality (`==` and `!=`) operations when one of the
operands is `str | None` or `bytes | None`. First check if the value is
`None`, and based on that branch into fast path operations. Previously
we used a generic C API primitive for such comparisons, which was quite
slow.

This could be generalized to other optional types, but let's start with
`str | None` since it's a very common type and it's often used in
equality tests. `bytes | None` is also covered, since it's very similar
to the `str` case.

Also add support for unchecked `Cast` operations in the IR. These don't
perform a runtime type check -- they can be used to narrow the static
type when it can be known statically that the cast is always safe.
---
 mypyc/codegen/emitfunc.py          |  3 +
 mypyc/ir/ops.py                    | 10 +++-
 mypyc/ir/pprint.py                 |  8 ++-
 mypyc/ir/rtypes.py                 |  2 +-
 mypyc/irbuild/ll_builder.py        | 96 +++++++++++++++++++++++++++++-
 mypyc/test-data/irbuild-bytes.test | 32 ++++++++++
 mypyc/test-data/irbuild-str.test   | 71 ++++++++++++++++++++++
 mypyc/test-data/run-bytes.test     | 27 +++++++++
 mypyc/test-data/run-strings.test   | 31 ++++++++++
 9 files changed, 275 insertions(+), 5 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index f00f2e700217..7ae49a0d9730 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -657,6 +657,9 @@ def visit_box(self, op: Box) -> None:
         self.emitter.emit_box(self.reg(op.src), self.reg(op), op.src.type, can_borrow=True)
 
     def visit_cast(self, op: Cast) -> None:
+        if op.is_unchecked and op.is_borrowed:
+            self.emit_line(f"{self.reg(op)} = {self.reg(op.src)};")
+            return
         branch = self.next_branch()
         handler = None
         if branch is not None:
diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py
index 62ac9b8d48e4..4b3b5eb3c8ca 100644
--- a/mypyc/ir/ops.py
+++ b/mypyc/ir/ops.py
@@ -1073,11 +1073,19 @@ class Cast(RegisterOp):
 
     error_kind = ERR_MAGIC
 
-    def __init__(self, src: Value, typ: RType, line: int, *, borrow: bool = False) -> None:
+    def __init__(
+        self, src: Value, typ: RType, line: int, *, borrow: bool = False, unchecked: bool = False
+    ) -> None:
         super().__init__(line)
         self.src = src
         self.type = typ
+        # If true, don't incref the result.
         self.is_borrowed = borrow
+        # If true, don't perform a runtime type check (only changes the static type of
+        # the operand). Used when we know that the cast will always succeed.
+        self.is_unchecked = unchecked
+        if unchecked:
+            self.error_kind = ERR_NEVER
 
     def sources(self) -> list[Value]:
         return [self.src]
diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py
index b0de041e1eae..efefd76d15f0 100644
--- a/mypyc/ir/pprint.py
+++ b/mypyc/ir/pprint.py
@@ -196,7 +196,13 @@ def visit_method_call(self, op: MethodCall) -> str:
         return s
 
     def visit_cast(self, op: Cast) -> str:
-        return self.format("%r = %scast(%s, %r)", op, self.borrow_prefix(op), op.type, op.src)
+        if op.is_unchecked:
+            prefix = "unchecked "
+        else:
+            prefix = ""
+        return self.format(
+            "%r = %s%scast(%s, %r)", op, prefix, self.borrow_prefix(op), op.type, op.src
+        )
 
     def visit_box(self, op: Box) -> str:
         return self.format("%r = box(%s, %r)", op, op.src.type, op.src)
diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py
index 667ff60b0204..34824a59cd5c 100644
--- a/mypyc/ir/rtypes.py
+++ b/mypyc/ir/rtypes.py
@@ -1027,7 +1027,7 @@ def flatten_nested_unions(types: list[RType]) -> list[RType]:
 def optional_value_type(rtype: RType) -> RType | None:
     """If rtype is the union of none_rprimitive and another type X, return X.
 
-    Otherwise return None.
+    Otherwise, return None.
     """
     if isinstance(rtype, RUnion) and len(rtype.items) == 2:
         if rtype.items[0] == none_rprimitive:
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index f82be9fe706f..8876003645dd 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -336,14 +336,20 @@ def box(self, src: Value) -> Value:
             return src
 
     def unbox_or_cast(
-        self, src: Value, target_type: RType, line: int, *, can_borrow: bool = False
+        self,
+        src: Value,
+        target_type: RType,
+        line: int,
+        *,
+        can_borrow: bool = False,
+        unchecked: bool = False,
     ) -> Value:
         if target_type.is_unboxed:
             return self.add(Unbox(src, target_type, line))
         else:
             if can_borrow:
                 self.keep_alives.append(src)
-            return self.add(Cast(src, target_type, line, borrow=can_borrow))
+            return self.add(Cast(src, target_type, line, borrow=can_borrow, unchecked=unchecked))
 
     def coerce(
         self,
@@ -2514,6 +2520,22 @@ def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) ->
         if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype):
             return self.compare_bytes(lreg, rreg, expr_op, line)
 
+        lopt = optional_value_type(ltype)
+        ropt = optional_value_type(rtype)
+
+        # Can we do a quick comparison of two optional types (special case None values)?
+        fast_opt_eq = False
+        if lopt is not None:
+            if ropt is not None and is_same_type(lopt, ropt) and self._never_equal_to_none(lopt):
+                fast_opt_eq = True
+            if is_same_type(lopt, rtype) and self._never_equal_to_none(lopt):
+                fast_opt_eq = True
+        elif ropt is not None:
+            if is_same_type(ropt, ltype) and self._never_equal_to_none(ropt):
+                fast_opt_eq = True
+        if fast_opt_eq:
+            return self._translate_fast_optional_eq_cmp(lreg, rreg, expr_op, line)
+
         if not (isinstance(ltype, RInstance) and ltype == rtype):
             return None
 
@@ -2540,6 +2562,76 @@ def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) ->
 
         return self.gen_method_call(lreg, op_methods[expr_op], [rreg], ltype, line)
 
+    def _never_equal_to_none(self, typ: RType) -> bool:
+        """Are the values of type never equal to None?"""
+        # TODO: Support RInstance with no custom __eq__/__ne__ and other primitive types.
+        return is_str_rprimitive(typ) or is_bytes_rprimitive(typ)
+
+    def _translate_fast_optional_eq_cmp(
+        self, lreg: Value, rreg: Value, expr_op: str, line: int
+    ) -> Value:
+        """Generate eq/ne fast path between 'X | None' and ('X | None' or X).
+
+        Assume 'X' never compares equal to None.
+        """
+        if not isinstance(lreg.type, RUnion):
+            lreg, rreg = rreg, lreg
+        value_typ = optional_value_type(lreg.type)
+        assert value_typ
+        res = Register(bool_rprimitive)
+
+        # Fast path: left value is None?
+        cmp = self.add(ComparisonOp(lreg, self.none_object(), ComparisonOp.EQ, line))
+        l_none = BasicBlock()
+        l_not_none = BasicBlock()
+        out = BasicBlock()
+        self.add(Branch(cmp, l_none, l_not_none, Branch.BOOL))
+        self.activate_block(l_none)
+        if not isinstance(rreg.type, RUnion):
+            val = self.false() if expr_op == "==" else self.true()
+            self.add(Assign(res, val))
+        else:
+            op = ComparisonOp.EQ if expr_op == "==" else ComparisonOp.NEQ
+            cmp = self.add(ComparisonOp(rreg, self.none_object(), op, line))
+            self.add(Assign(res, cmp))
+        self.goto(out)
+
+        self.activate_block(l_not_none)
+        if not isinstance(rreg.type, RUnion):
+            # Both operands are known to be not None, perform specialized comparison
+            eq = self.translate_eq_cmp(
+                self.unbox_or_cast(lreg, value_typ, line, can_borrow=True, unchecked=True),
+                rreg,
+                expr_op,
+                line,
+            )
+            assert eq is not None
+            self.add(Assign(res, eq))
+        else:
+            r_none = BasicBlock()
+            r_not_none = BasicBlock()
+            # Fast path: right value is None?
+            cmp = self.add(ComparisonOp(rreg, self.none_object(), ComparisonOp.EQ, line))
+            self.add(Branch(cmp, r_none, r_not_none, Branch.BOOL))
+            self.activate_block(r_none)
+            # None vs not-None
+            val = self.false() if expr_op == "==" else self.true()
+            self.add(Assign(res, val))
+            self.goto(out)
+            self.activate_block(r_not_none)
+            # Both operands are known to be not None, perform specialized comparison
+            eq = self.translate_eq_cmp(
+                self.unbox_or_cast(lreg, value_typ, line, can_borrow=True, unchecked=True),
+                self.unbox_or_cast(rreg, value_typ, line, can_borrow=True, unchecked=True),
+                expr_op,
+                line,
+            )
+            assert eq is not None
+            self.add(Assign(res, eq))
+        self.goto(out)
+        self.activate_block(out)
+        return res
+
     def translate_is_op(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value:
         """Create equality comparison operation between object identities
 
diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test
index 476c5ac59f48..8cfefe03ae22 100644
--- a/mypyc/test-data/irbuild-bytes.test
+++ b/mypyc/test-data/irbuild-bytes.test
@@ -185,3 +185,35 @@ L0:
     r10 = CPyBytes_Build(2, var, r9)
     b4 = r10
     return 1
+
+[case testOptionalBytesEquality]
+from typing import Optional
+
+def non_opt_opt(x: bytes, y: Optional[bytes]) -> bool:
+    return x != y
+[out]
+def non_opt_opt(x, y):
+    x :: bytes
+    y :: union[bytes, None]
+    r0 :: object
+    r1 :: bit
+    r2 :: bool
+    r3 :: bytes
+    r4 :: i32
+    r5, r6 :: bit
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = y == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    r2 = 1
+    goto L3
+L2:
+    r3 = unchecked borrow cast(bytes, y)
+    r4 = CPyBytes_Compare(r3, x)
+    r5 = r4 >= 0 :: signed
+    r6 = r4 != 1
+    r2 = r6
+L3:
+    keep_alive y
+    return r2
diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test
index 245acf7402a1..3fa39819498d 100644
--- a/mypyc/test-data/irbuild-str.test
+++ b/mypyc/test-data/irbuild-str.test
@@ -669,3 +669,74 @@ L0:
     r2 = 'abc1233.14True'
     r3 = CPyStr_Build(7, x, r0, x, r1, x, r2, x)
     return r3
+
+[case testOptionalStrEquality1]
+from typing import Optional
+
+def opt_opt(x: Optional[str], y: Optional[str]) -> bool:
+    return x == y
+[out]
+def opt_opt(x, y):
+    x, y :: union[str, None]
+    r0 :: object
+    r1 :: bit
+    r2 :: object
+    r3 :: bit
+    r4 :: bool
+    r5 :: object
+    r6 :: bit
+    r7, r8 :: str
+    r9 :: bool
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = x == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    r2 = load_address _Py_NoneStruct
+    r3 = y == r2
+    r4 = r3
+    goto L5
+L2:
+    r5 = load_address _Py_NoneStruct
+    r6 = y == r5
+    if r6 goto L3 else goto L4 :: bool
+L3:
+    r4 = 0
+    goto L5
+L4:
+    r7 = unchecked borrow cast(str, x)
+    r8 = unchecked borrow cast(str, y)
+    r9 = CPyStr_Equal(r7, r8)
+    r4 = r9
+L5:
+    keep_alive x, y
+    return r4
+
+[case testOptionalStrEquality2]
+from typing import Optional
+
+def opt_non_opt(x: Optional[str], y: str) -> bool:
+    return x == y
+[out]
+def opt_non_opt(x, y):
+    x :: union[str, None]
+    y :: str
+    r0 :: object
+    r1 :: bit
+    r2 :: bool
+    r3 :: str
+    r4 :: bool
+L0:
+    r0 = load_address _Py_NoneStruct
+    r1 = x == r0
+    if r1 goto L1 else goto L2 :: bool
+L1:
+    r2 = 0
+    goto L3
+L2:
+    r3 = unchecked borrow cast(str, x)
+    r4 = CPyStr_Equal(r3, y)
+    r2 = r4
+L3:
+    keep_alive x
+    return r2
diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test
index 5a285320c849..df5cb209b902 100644
--- a/mypyc/test-data/run-bytes.test
+++ b/mypyc/test-data/run-bytes.test
@@ -374,3 +374,30 @@ class subbytearray(bytearray):
 [file userdefinedbytes.py]
 class bytes:
     pass
+
+[case testBytesOptionalEquality]
+from __future__ import annotations
+
+def eq_b_opt_b(x: bytes | None, y: bytes) -> bool:
+    return x == y
+
+def ne_b_b_opt(x: bytes, y: bytes | None) -> bool:
+    return x != y
+
+def test_optional_eq() -> None:
+    b = b'x'
+    assert eq_b_opt_b(b, b)
+    assert eq_b_opt_b(b + bytes([int()]), b + bytes([int()]))
+
+    assert not eq_b_opt_b(b'x', b'y')
+    assert not eq_b_opt_b(b'y', b'x')
+    assert not eq_b_opt_b(None, b'x')
+
+def test_optional_ne() -> None:
+    b = b'x'
+    assert not ne_b_b_opt(b, b)
+    assert not ne_b_b_opt(b + b'y', b + bytes() + b'y')
+
+    assert ne_b_b_opt(b'x', b'y')
+    assert ne_b_b_opt(b'y', b'x')
+    assert ne_b_b_opt(b'x', None)
diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test
index b4f3ebd66910..6960b0a04303 100644
--- a/mypyc/test-data/run-strings.test
+++ b/mypyc/test-data/run-strings.test
@@ -1063,3 +1063,34 @@ class subc(str):
 [file userdefinedstr.py]
 class str:
     pass
+
+[case testStrOptionalEquality]
+from __future__ import annotations
+
+def eq_s_opt_s_opt(x: str | None, y: str | None) -> bool:
+    return x == y
+
+def ne_s_opt_s_opt(x: str | None, y: str | None) -> bool:
+    return x != y
+
+def test_optional_eq() -> None:
+    s = 'x'
+    assert eq_s_opt_s_opt(s, s)
+    assert eq_s_opt_s_opt(s + str(int()), s + str(int()))
+    assert eq_s_opt_s_opt(None, None)
+
+    assert not eq_s_opt_s_opt('x', 'y')
+    assert not eq_s_opt_s_opt('y', 'x')
+    assert not eq_s_opt_s_opt(None, 'x')
+    assert not eq_s_opt_s_opt('x', None)
+
+def test_optional_ne() -> None:
+    s = 'x'
+    assert not ne_s_opt_s_opt(s, s)
+    assert not ne_s_opt_s_opt(s + str(int()), s+ str(int()))
+    assert not ne_s_opt_s_opt(None, None)
+
+    assert ne_s_opt_s_opt('x', 'y')
+    assert ne_s_opt_s_opt('y', 'x')
+    assert ne_s_opt_s_opt(None, 'x')
+    assert ne_s_opt_s_opt('x', None)

From f83ec9fffa838978a6ee6411a515a313979c61fa Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Fri, 29 Aug 2025 15:53:55 +0100
Subject: [PATCH 1592/1617] [mypyc] Speed up implicit __ne__ method  (#19759)

Avoid the use of `PyObject_Not` if `__eq__` returns a boolean (which is
common). Also skip incref on the bool/bit result on 3.12+, since the
object is immortal.

This speeds up a microbenchmark that repeatedly does `!=` operations by
20%.
---
 mypyc/codegen/emitfunc.py                |  4 +-
 mypyc/irbuild/classdef.py                |  4 +-
 mypyc/irbuild/ll_builder.py              | 41 ++++++++++++-
 mypyc/test-data/irbuild-basic.test       | 38 +++++++++---
 mypyc/test-data/irbuild-classes.test     | 76 ++++++++++++++++++------
 mypyc/test-data/run-dunders-special.test |  2 +
 mypyc/test-data/run-dunders.test         | 22 +++++++
 7 files changed, 155 insertions(+), 32 deletions(-)

diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py
index 7ae49a0d9730..a1e18353693e 100644
--- a/mypyc/codegen/emitfunc.py
+++ b/mypyc/codegen/emitfunc.py
@@ -89,7 +89,7 @@
     RStruct,
     RTuple,
     RType,
-    is_bool_rprimitive,
+    is_bool_or_bit_rprimitive,
     is_int32_rprimitive,
     is_int64_rprimitive,
     is_int_rprimitive,
@@ -633,7 +633,7 @@ def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Va
     def visit_inc_ref(self, op: IncRef) -> None:
         if (
             isinstance(op.src, Box)
-            and (is_none_rprimitive(op.src.src.type) or is_bool_rprimitive(op.src.src.type))
+            and (is_none_rprimitive(op.src.src.type) or is_bool_or_bit_rprimitive(op.src.src.type))
             and HAVE_IMMORTAL
         ):
             # On Python 3.12+, None/True/False are immortal, and we can skip inc ref
diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
index 72482710208a..324b44b95dc4 100644
--- a/mypyc/irbuild/classdef.py
+++ b/mypyc/irbuild/classdef.py
@@ -845,7 +845,9 @@ def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None:
             )
             builder.activate_block(regular_block)
             rettype = bool_rprimitive if return_bool and strict_typing else object_rprimitive
-            retval = builder.coerce(builder.unary_op(eqval, "not", line), rettype, line)
+            retval = builder.coerce(
+                builder.builder.unary_not(eqval, line, likely_bool=True), rettype, line
+            )
             builder.add(Return(retval))
             builder.activate_block(not_implemented_block)
             builder.add(Return(not_implemented))
diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py
index 8876003645dd..475d490a48f2 100644
--- a/mypyc/irbuild/ll_builder.py
+++ b/mypyc/irbuild/ll_builder.py
@@ -56,6 +56,7 @@
     KeepAlive,
     LoadAddress,
     LoadErrorValue,
+    LoadGlobal,
     LoadLiteral,
     LoadMem,
     LoadStatic,
@@ -108,6 +109,7 @@
     is_int_rprimitive,
     is_list_rprimitive,
     is_none_rprimitive,
+    is_object_rprimitive,
     is_set_rprimitive,
     is_short_int_rprimitive,
     is_str_rprimitive,
@@ -1318,6 +1320,14 @@ def none_object(self) -> Value:
         """Load Python None value (type: object_rprimitive)."""
         return self.add(LoadAddress(none_object_op.type, none_object_op.src, line=-1))
 
+    def true_object(self) -> Value:
+        """Load Python True object (type: object_rprimitive)."""
+        return self.add(LoadGlobal(object_rprimitive, "Py_True"))
+
+    def false_object(self) -> Value:
+        """Load Python False object (type: object_rprimitive)."""
+        return self.add(LoadGlobal(object_rprimitive, "Py_False"))
+
     def load_int(self, value: int) -> Value:
         """Load a tagged (Python) integer literal value."""
         if value > MAX_LITERAL_SHORT_INT or value < MIN_LITERAL_SHORT_INT:
@@ -1663,12 +1673,39 @@ def _non_specialized_unary_op(self, value: Value, op: str, line: int) -> Value:
         assert target, "Unsupported unary operation: %s" % op
         return target
 
-    def unary_not(self, value: Value, line: int) -> Value:
-        """Perform unary 'not'."""
+    def unary_not(self, value: Value, line: int, *, likely_bool: bool = False) -> Value:
+        """Perform unary 'not'.
+
+        Args:
+            likely_bool: The operand is likely a bool value, even if the type is something
+                more general, so specialize for bool values
+        """
         typ = value.type
         if is_bool_or_bit_rprimitive(typ):
             mask = Integer(1, typ, line)
             return self.int_op(typ, value, mask, IntOp.XOR, line)
+        if likely_bool and is_object_rprimitive(typ):
+            # First quickly check if it's a bool, and otherwise fall back to generic op.
+            res = Register(bit_rprimitive)
+            false, not_false, true, other = BasicBlock(), BasicBlock(), BasicBlock(), BasicBlock()
+            out = BasicBlock()
+            cmp = self.add(ComparisonOp(value, self.true_object(), ComparisonOp.EQ, line))
+            self.add(Branch(cmp, false, not_false, Branch.BOOL))
+            self.activate_block(false)
+            self.add(Assign(res, self.false()))
+            self.goto(out)
+            self.activate_block(not_false)
+            cmp = self.add(ComparisonOp(value, self.false_object(), ComparisonOp.EQ, line))
+            self.add(Branch(cmp, true, other, Branch.BOOL))
+            self.activate_block(true)
+            self.add(Assign(res, self.true()))
+            self.goto(out)
+            self.activate_block(other)
+            val = self._non_specialized_unary_op(value, "not", line)
+            self.add(Assign(res, val))
+            self.goto(out)
+            self.activate_block(out)
+            return res
         return self._non_specialized_unary_op(value, "not", line)
 
     def unary_minus(self, value: Value, line: int) -> Value:
diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test
index feb7b36a2b52..612f3266fd79 100644
--- a/mypyc/test-data/irbuild-basic.test
+++ b/mypyc/test-data/irbuild-basic.test
@@ -2347,22 +2347,42 @@ def A.__ne__(__mypyc_self__, rhs):
     __mypyc_self__ :: __main__.A
     rhs, r0, r1 :: object
     r2 :: bit
-    r3 :: i32
-    r4 :: bit
-    r5 :: bool
+    r3 :: object
+    r4, r5 :: bit
     r6 :: object
+    r7 :: bit
+    r8 :: i32
+    r9 :: bit
+    r10 :: bool
+    r11 :: object
 L0:
     r0 = __mypyc_self__.__eq__(rhs)
     r1 = load_address _Py_NotImplementedStruct
     r2 = r0 == r1
-    if r2 goto L2 else goto L1 :: bool
+    if r2 goto L7 else goto L1 :: bool
 L1:
-    r3 = PyObject_Not(r0)
-    r4 = r3 >= 0 :: signed
-    r5 = truncate r3: i32 to builtins.bool
-    r6 = box(bool, r5)
-    return r6
+    r3 = load_global Py_True :: static
+    r4 = r0 == r3
+    if r4 goto L2 else goto L3 :: bool
 L2:
+    r5 = 0
+    goto L6
+L3:
+    r6 = load_global Py_False :: static
+    r7 = r0 == r6
+    if r7 goto L4 else goto L5 :: bool
+L4:
+    r5 = 1
+    goto L6
+L5:
+    r8 = PyObject_Not(r0)
+    r9 = r8 >= 0 :: signed
+    r10 = truncate r8: i32 to builtins.bool
+    r5 = r10
+L6:
+    r11 = box(bit, r5)
+    return r11
+L7:
     return r1
 
 [case testDecorators_toplevel]
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index b49b20e13a43..a573bb8e0668 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -854,22 +854,42 @@ def Base.__ne__(__mypyc_self__, rhs):
     __mypyc_self__ :: __main__.Base
     rhs, r0, r1 :: object
     r2 :: bit
-    r3 :: i32
-    r4 :: bit
-    r5 :: bool
+    r3 :: object
+    r4, r5 :: bit
     r6 :: object
+    r7 :: bit
+    r8 :: i32
+    r9 :: bit
+    r10 :: bool
+    r11 :: object
 L0:
     r0 = __mypyc_self__.__eq__(rhs)
     r1 = load_address _Py_NotImplementedStruct
     r2 = r0 == r1
-    if r2 goto L2 else goto L1 :: bool
+    if r2 goto L7 else goto L1 :: bool
 L1:
-    r3 = PyObject_Not(r0)
-    r4 = r3 >= 0 :: signed
-    r5 = truncate r3: i32 to builtins.bool
-    r6 = box(bool, r5)
-    return r6
+    r3 = load_global Py_True :: static
+    r4 = r0 == r3
+    if r4 goto L2 else goto L3 :: bool
 L2:
+    r5 = 0
+    goto L6
+L3:
+    r6 = load_global Py_False :: static
+    r7 = r0 == r6
+    if r7 goto L4 else goto L5 :: bool
+L4:
+    r5 = 1
+    goto L6
+L5:
+    r8 = PyObject_Not(r0)
+    r9 = r8 >= 0 :: signed
+    r10 = truncate r8: i32 to builtins.bool
+    r5 = r10
+L6:
+    r11 = box(bit, r5)
+    return r11
+L7:
     return r1
 def Derived.__eq__(self, other):
     self :: __main__.Derived
@@ -979,22 +999,42 @@ def Derived.__ne__(__mypyc_self__, rhs):
     __mypyc_self__ :: __main__.Derived
     rhs, r0, r1 :: object
     r2 :: bit
-    r3 :: i32
-    r4 :: bit
-    r5 :: bool
+    r3 :: object
+    r4, r5 :: bit
     r6 :: object
+    r7 :: bit
+    r8 :: i32
+    r9 :: bit
+    r10 :: bool
+    r11 :: object
 L0:
     r0 = __mypyc_self__.__eq__(rhs)
     r1 = load_address _Py_NotImplementedStruct
     r2 = r0 == r1
-    if r2 goto L2 else goto L1 :: bool
+    if r2 goto L7 else goto L1 :: bool
 L1:
-    r3 = PyObject_Not(r0)
-    r4 = r3 >= 0 :: signed
-    r5 = truncate r3: i32 to builtins.bool
-    r6 = box(bool, r5)
-    return r6
+    r3 = load_global Py_True :: static
+    r4 = r0 == r3
+    if r4 goto L2 else goto L3 :: bool
 L2:
+    r5 = 0
+    goto L6
+L3:
+    r6 = load_global Py_False :: static
+    r7 = r0 == r6
+    if r7 goto L4 else goto L5 :: bool
+L4:
+    r5 = 1
+    goto L6
+L5:
+    r8 = PyObject_Not(r0)
+    r9 = r8 >= 0 :: signed
+    r10 = truncate r8: i32 to builtins.bool
+    r5 = r10
+L6:
+    r11 = box(bit, r5)
+    return r11
+L7:
     return r1
 
 [case testDefaultVars]
diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test
index 2672434e10ef..4817435b1e7c 100644
--- a/mypyc/test-data/run-dunders-special.test
+++ b/mypyc/test-data/run-dunders-special.test
@@ -8,3 +8,5 @@ class UsesNotImplemented:
 
 def test_not_implemented() -> None:
     assert UsesNotImplemented() != object()
+    x = UsesNotImplemented() == object()
+    assert not x
diff --git a/mypyc/test-data/run-dunders.test b/mypyc/test-data/run-dunders.test
index b8fb13c9dcec..ec992afbfbd1 100644
--- a/mypyc/test-data/run-dunders.test
+++ b/mypyc/test-data/run-dunders.test
@@ -965,3 +965,25 @@ def test_final() -> None:
     assert b + 3 == 9
     assert (a < A(5)) is False
     assert (b < A(5)) is True
+
+[case testDundersEq]
+class Eq:
+    def __init__(self, x: int) -> None:
+        self.x = x
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Eq):
+            return NotImplemented
+        return self.x == other.x
+
+def eq(x: Eq, y: Eq) -> bool:
+    return x == y
+
+def ne(x: Eq, y: Eq) -> bool:
+    return x != y
+
+def test_equality_with_implicit_ne() -> None:
+    assert eq(Eq(1), Eq(1))
+    assert not eq(Eq(1), Eq(2))
+    assert ne(Eq(1), Eq(2))
+    assert not ne(Eq(1), Eq(1))

From c32b0a50f2a7329109d049bd439954fdd913e7f2 Mon Sep 17 00:00:00 2001
From: Piotr Sawicki 
Date: Fri, 29 Aug 2025 18:50:05 +0200
Subject: [PATCH 1593/1617] [mypyc] Use defined __new__ method in tp_new and
 constructor (#19739)

Fixes #16012

mypyc ignored custom implementations of `__new__` because, even though a
C function representing the method was generated, it was called neither
in the type constructor nor in the method assigned to the `tp_new`
pointer.

Now if there's a `__new__` method defined for a type, the corresponding
function is called in place of the setup function which is responsible
for allocating memory for new objects and initializing their attributes
to default values.

The setup function is still called when creating instances of the type
as calls resolving to `object.__new__()` are transformed to call the
setup function. This way, `__new__` can return instances of other types
and instances of the type of the class where `__new__` is defined are
setup correctly.

There are a couple of limitations:
- Programs with `super().__new__()` calls in `__new__` methods of
non-native classes are rejected because it's more difficult to resolve
the setup function for non-native classes but this could probably be
supported in the future.
- Similarly, programs are rejected when a class inherits from a
non-compiled class. In this case calling the `tp_new` method of the
parent type results in an error because cpython expects the sub type to
use a wrapper for `tp_new` which compiled classes don't. Allowing this
would require compiled types to be initialized more closely to the way
cpython does it which might need a lot of work.
- Lastly, when `__new__` is annotated with `@classmethod`, calling it
without the type parameter works in compiled code but raises an error in
interpreted. I'm not sure of the reason and it's difficult to make it a
compiler error because it's outside of what mypyc sees.

---------

Co-authored-by: Brian Schubert 
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 mypyc/analysis/attrdefined.py            |   1 +
 mypyc/codegen/emitclass.py               |  90 +++--
 mypyc/codegen/emitmodule.py              |   4 +-
 mypyc/codegen/emitwrapper.py             |   2 +-
 mypyc/ir/class_ir.py                     |  14 +-
 mypyc/irbuild/expression.py              |  42 ++-
 mypyc/irbuild/prepare.py                 |  50 ++-
 mypyc/lib-rt/misc_ops.c                  |  11 +
 mypyc/test-data/fixtures/ir.py           |   4 +-
 mypyc/test-data/fixtures/typing-full.pyi |   1 +
 mypyc/test-data/irbuild-classes.test     |  75 ++++
 mypyc/test-data/run-classes.test         | 429 +++++++++++++++++++++++
 12 files changed, 677 insertions(+), 46 deletions(-)

diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py
index 4fd0017257a0..5be57d767e35 100644
--- a/mypyc/analysis/attrdefined.py
+++ b/mypyc/analysis/attrdefined.py
@@ -138,6 +138,7 @@ def analyze_always_defined_attrs_in_class(cl: ClassIR, seen: set[ClassIR]) -> No
         or cl.builtin_base is not None
         or cl.children is None
         or cl.is_serializable()
+        or cl.has_method("__new__")
     ):
         # Give up -- we can't enforce that attributes are always defined.
         return
diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py
index ecf8c37f83c9..0931c849131d 100644
--- a/mypyc/codegen/emitclass.py
+++ b/mypyc/codegen/emitclass.py
@@ -5,6 +5,7 @@
 from collections.abc import Mapping
 from typing import Callable
 
+from mypy.nodes import ARG_STAR, ARG_STAR2
 from mypyc.codegen.cstring import c_string_initializer
 from mypyc.codegen.emit import Emitter, HeaderDeclaration, ReturnHandler
 from mypyc.codegen.emitfunc import native_function_doc_initializer, native_function_header
@@ -224,7 +225,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
     name = cl.name
     name_prefix = cl.name_prefix(emitter.names)
 
-    setup_name = f"{name_prefix}_setup"
+    setup_name = emitter.native_function_name(cl.setup)
     new_name = f"{name_prefix}_new"
     finalize_name = f"{name_prefix}_finalize"
     members_name = f"{name_prefix}_members"
@@ -317,10 +318,8 @@ def emit_line() -> None:
         fields["tp_basicsize"] = base_size
 
     if generate_full:
-        # Declare setup method that allocates and initializes an object. type is the
-        # type of the class being initialized, which could be another class if there
-        # is an interpreted subclass.
-        emitter.emit_line(f"static PyObject *{setup_name}(PyTypeObject *type);")
+        assert cl.setup is not None
+        emitter.emit_line(native_function_header(cl.setup, emitter) + ";")
         assert cl.ctor is not None
         emitter.emit_line(native_function_header(cl.ctor, emitter) + ";")
 
@@ -390,9 +389,7 @@ def emit_line() -> None:
 
     emitter.emit_line()
     if generate_full:
-        generate_setup_for_class(
-            cl, setup_name, defaults_fn, vtable_name, shadow_vtable_name, emitter
-        )
+        generate_setup_for_class(cl, defaults_fn, vtable_name, shadow_vtable_name, emitter)
         emitter.emit_line()
         generate_constructor_for_class(cl, cl.ctor, init_fn, setup_name, vtable_name, emitter)
         emitter.emit_line()
@@ -579,16 +576,16 @@ def generate_vtable(
 
 def generate_setup_for_class(
     cl: ClassIR,
-    func_name: str,
     defaults_fn: FuncIR | None,
     vtable_name: str,
     shadow_vtable_name: str | None,
     emitter: Emitter,
 ) -> None:
     """Generate a native function that allocates an instance of a class."""
-    emitter.emit_line("static PyObject *")
-    emitter.emit_line(f"{func_name}(PyTypeObject *type)")
+    emitter.emit_line(native_function_header(cl.setup, emitter))
     emitter.emit_line("{")
+    type_arg_name = REG_PREFIX + cl.setup.sig.args[0].name
+    emitter.emit_line(f"PyTypeObject *type = (PyTypeObject*){type_arg_name};")
     struct_name = cl.struct_name(emitter.names)
     emitter.emit_line(f"{struct_name} *self;")
 
@@ -663,6 +660,35 @@ def emit_attr_defaults_func_call(defaults_fn: FuncIR, self_name: str, emitter: E
     )
 
 
+def emit_setup_or_dunder_new_call(
+    cl: ClassIR,
+    setup_name: str,
+    type_arg: str,
+    native_prefix: bool,
+    new_args: str,
+    emitter: Emitter,
+) -> None:
+    def emit_null_check() -> None:
+        emitter.emit_line("if (self == NULL)")
+        emitter.emit_line("    return NULL;")
+
+    new_fn = cl.get_method("__new__")
+    if not new_fn:
+        emitter.emit_line(f"PyObject *self = {setup_name}({type_arg});")
+        emit_null_check()
+        return
+    prefix = emitter.get_group_prefix(new_fn.decl) + NATIVE_PREFIX if native_prefix else PREFIX
+    all_args = type_arg
+    if new_args != "":
+        all_args += ", " + new_args
+    emitter.emit_line(f"PyObject *self = {prefix}{new_fn.cname(emitter.names)}({all_args});")
+    emit_null_check()
+
+    # skip __init__ if __new__ returns some other type
+    emitter.emit_line(f"if (Py_TYPE(self) != {emitter.type_struct_name(cl)})")
+    emitter.emit_line("    return self;")
+
+
 def generate_constructor_for_class(
     cl: ClassIR,
     fn: FuncDecl,
@@ -674,17 +700,30 @@ def generate_constructor_for_class(
     """Generate a native function that allocates and initializes an instance of a class."""
     emitter.emit_line(f"{native_function_header(fn, emitter)}")
     emitter.emit_line("{")
-    emitter.emit_line(f"PyObject *self = {setup_name}({emitter.type_struct_name(cl)});")
-    emitter.emit_line("if (self == NULL)")
-    emitter.emit_line("    return NULL;")
-    args = ", ".join(["self"] + [REG_PREFIX + arg.name for arg in fn.sig.args])
+
+    fn_args = [REG_PREFIX + arg.name for arg in fn.sig.args]
+    type_arg = "(PyObject *)" + emitter.type_struct_name(cl)
+    new_args = ", ".join(fn_args)
+
+    use_wrapper = (
+        cl.has_method("__new__")
+        and len(fn.sig.args) == 2
+        and fn.sig.args[0].kind == ARG_STAR
+        and fn.sig.args[1].kind == ARG_STAR2
+    )
+    emit_setup_or_dunder_new_call(cl, setup_name, type_arg, not use_wrapper, new_args, emitter)
+
+    args = ", ".join(["self"] + fn_args)
     if init_fn is not None:
+        prefix = PREFIX if use_wrapper else NATIVE_PREFIX
+        cast = "!= NULL ? 0 : -1" if use_wrapper else ""
         emitter.emit_line(
-            "char res = {}{}{}({});".format(
+            "char res = {}{}{}({}){};".format(
                 emitter.get_group_prefix(init_fn.decl),
-                NATIVE_PREFIX,
+                prefix,
                 init_fn.cname(emitter.names),
                 args,
+                cast,
             )
         )
         emitter.emit_line("if (res == 2) {")
@@ -717,7 +756,7 @@ def generate_init_for_class(cl: ClassIR, init_fn: FuncIR, emitter: Emitter) -> s
     emitter.emit_line("static int")
     emitter.emit_line(f"{func_name}(PyObject *self, PyObject *args, PyObject *kwds)")
     emitter.emit_line("{")
-    if cl.allow_interpreted_subclasses or cl.builtin_base:
+    if cl.allow_interpreted_subclasses or cl.builtin_base or cl.has_method("__new__"):
         emitter.emit_line(
             "return {}{}(self, args, kwds) != NULL ? 0 : -1;".format(
                 PREFIX, init_fn.cname(emitter.names)
@@ -750,15 +789,22 @@ def generate_new_for_class(
         emitter.emit_line("return NULL;")
         emitter.emit_line("}")
 
-    if not init_fn or cl.allow_interpreted_subclasses or cl.builtin_base or cl.is_serializable():
+    type_arg = "(PyObject*)type"
+    new_args = "args, kwds"
+    emit_setup_or_dunder_new_call(cl, setup_name, type_arg, False, new_args, emitter)
+    if (
+        not init_fn
+        or cl.allow_interpreted_subclasses
+        or cl.builtin_base
+        or cl.is_serializable()
+        or cl.has_method("__new__")
+    ):
         # Match Python semantics -- __new__ doesn't call __init__.
-        emitter.emit_line(f"return {setup_name}(type);")
+        emitter.emit_line("return self;")
     else:
         # __new__ of a native class implicitly calls __init__ so that we
         # can enforce that instances are always properly initialized. This
         # is needed to support always defined attributes.
-        emitter.emit_line(f"PyObject *self = {setup_name}(type);")
-        emitter.emit_lines("if (self == NULL)", "    return NULL;")
         emitter.emit_line(
             f"PyObject *ret = {PREFIX}{init_fn.cname(emitter.names)}(self, args, kwds);"
         )
diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py
index e31fcf8ea0c9..ca5db52ab7da 100644
--- a/mypyc/codegen/emitmodule.py
+++ b/mypyc/codegen/emitmodule.py
@@ -1274,8 +1274,8 @@ def is_fastcall_supported(fn: FuncIR, capi_version: tuple[int, int]) -> bool:
         if fn.name == "__call__":
             # We can use vectorcalls (PEP 590) when supported
             return True
-        # TODO: Support fastcall for __init__.
-        return fn.name != "__init__"
+        # TODO: Support fastcall for __init__ and __new__.
+        return fn.name != "__init__" and fn.name != "__new__"
     return True
 
 
diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py
index cd1684255855..2e5d7efa4e98 100644
--- a/mypyc/codegen/emitwrapper.py
+++ b/mypyc/codegen/emitwrapper.py
@@ -238,7 +238,7 @@ def generate_legacy_wrapper_function(
     real_args = list(fn.args)
     if fn.sig.num_bitmap_args:
         real_args = real_args[: -fn.sig.num_bitmap_args]
-    if fn.class_name and fn.decl.kind != FUNC_STATICMETHOD:
+    if fn.class_name and (fn.decl.name == "__new__" or fn.decl.kind != FUNC_STATICMETHOD):
         arg = real_args.pop(0)
         emitter.emit_line(f"PyObject *obj_{arg.name} = self;")
 
diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py
index f6015b64dcdd..0a56aaf5d101 100644
--- a/mypyc/ir/class_ir.py
+++ b/mypyc/ir/class_ir.py
@@ -5,9 +5,9 @@
 from typing import NamedTuple
 
 from mypyc.common import PROPSET_PREFIX, JsonDict
-from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature
+from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg
 from mypyc.ir.ops import DeserMaps, Value
-from mypyc.ir.rtypes import RInstance, RType, deserialize_type
+from mypyc.ir.rtypes import RInstance, RType, deserialize_type, object_rprimitive
 from mypyc.namegen import NameGenerator, exported_name
 
 # Some notes on the vtable layout: Each concrete class has a vtable
@@ -133,6 +133,16 @@ def __init__(
         self.builtin_base: str | None = None
         # Default empty constructor
         self.ctor = FuncDecl(name, None, module_name, FuncSignature([], RInstance(self)))
+        # Declare setup method that allocates and initializes an object. type is the
+        # type of the class being initialized, which could be another class if there
+        # is an interpreted subclass.
+        # TODO: Make it a regular method and generate its body in IR
+        self.setup = FuncDecl(
+            "__mypyc__" + name + "_setup",
+            None,
+            module_name,
+            FuncSignature([RuntimeArg("type", object_rprimitive)], RInstance(self)),
+        )
         # Attributes defined in the class (not inherited)
         self.attributes: dict[str, RType] = {}
         # Deletable attributes
diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py
index e82203021ae3..4409b1acff26 100644
--- a/mypyc/irbuild/expression.py
+++ b/mypyc/irbuild/expression.py
@@ -57,6 +57,7 @@
 from mypyc.ir.ops import (
     Assign,
     BasicBlock,
+    Call,
     ComparisonOp,
     Integer,
     LoadAddress,
@@ -472,23 +473,42 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe
         if callee.name in base.method_decls:
             break
     else:
-        if (
-            ir.is_ext_class
-            and ir.builtin_base is None
-            and not ir.inherits_python
-            and callee.name == "__init__"
-            and len(expr.args) == 0
-        ):
-            # Call translates to object.__init__(self), which is a
-            # no-op, so omit the call.
-            return builder.none()
+        if ir.is_ext_class and ir.builtin_base is None and not ir.inherits_python:
+            if callee.name == "__init__" and len(expr.args) == 0:
+                # Call translates to object.__init__(self), which is a
+                # no-op, so omit the call.
+                return builder.none()
+            elif callee.name == "__new__":
+                # object.__new__(cls)
+                assert (
+                    len(expr.args) == 1
+                ), f"Expected object.__new__() call to have exactly 1 argument, got {len(expr.args)}"
+                typ_arg = expr.args[0]
+                method_args = builder.fn_info.fitem.arg_names
+                if (
+                    isinstance(typ_arg, NameExpr)
+                    and len(method_args) > 0
+                    and method_args[0] == typ_arg.name
+                ):
+                    subtype = builder.accept(expr.args[0])
+                    return builder.add(Call(ir.setup, [subtype], expr.line))
+
+        if callee.name == "__new__":
+            call = "super().__new__()"
+            if not ir.is_ext_class:
+                builder.error(f"{call} not supported for non-extension classes", expr.line)
+            if ir.inherits_python:
+                builder.error(
+                    f"{call} not supported for classes inheriting from non-native classes",
+                    expr.line,
+                )
         return translate_call(builder, expr, callee)
 
     decl = base.method_decl(callee.name)
     arg_values = [builder.accept(arg) for arg in expr.args]
     arg_kinds, arg_names = expr.arg_kinds.copy(), expr.arg_names.copy()
 
-    if decl.kind != FUNC_STATICMETHOD:
+    if decl.kind != FUNC_STATICMETHOD and decl.name != "__new__":
         # Grab first argument
         vself: Value = builder.self()
         if decl.kind == FUNC_CLASSMETHOD:
diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py
index 83ec3f7c1d38..95c8c448d642 100644
--- a/mypyc/irbuild/prepare.py
+++ b/mypyc/irbuild/prepare.py
@@ -193,9 +193,9 @@ def prepare_func_def(
     create_generator_class_if_needed(module_name, class_name, fdef, mapper)
 
     kind = (
-        FUNC_STATICMETHOD
-        if fdef.is_static
-        else (FUNC_CLASSMETHOD if fdef.is_class else FUNC_NORMAL)
+        FUNC_CLASSMETHOD
+        if fdef.is_class
+        else (FUNC_STATICMETHOD if fdef.is_static else FUNC_NORMAL)
     )
     sig = mapper.fdef_to_sig(fdef, options.strict_dunders_typing)
     decl = FuncDecl(fdef.name, class_name, module_name, sig, kind)
@@ -555,21 +555,57 @@ def add_setter_declaration(
     ir.method_decls[setter_name] = decl
 
 
+def check_matching_args(init_sig: FuncSignature, new_sig: FuncSignature) -> bool:
+    num_init_args = len(init_sig.args) - init_sig.num_bitmap_args
+    num_new_args = len(new_sig.args) - new_sig.num_bitmap_args
+    if num_init_args != num_new_args:
+        return False
+
+    for idx in range(1, num_init_args):
+        init_arg = init_sig.args[idx]
+        new_arg = new_sig.args[idx]
+        if init_arg.type != new_arg.type:
+            return False
+
+        if init_arg.kind != new_arg.kind:
+            return False
+
+    return True
+
+
 def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None:
     # Set up a constructor decl
     init_node = cdef.info["__init__"].node
+
+    new_node: SymbolNode | None = None
+    new_symbol = cdef.info.get("__new__")
+    # We are only interested in __new__ method defined in a user-defined class,
+    # so we ignore it if it comes from a builtin type. It's usually builtins.object
+    # but could also be builtins.type for metaclasses so we detect the prefix which
+    # matches both.
+    if new_symbol and new_symbol.fullname and not new_symbol.fullname.startswith("builtins."):
+        new_node = new_symbol.node
+    if isinstance(new_node, (Decorator, OverloadedFuncDef)):
+        new_node = get_func_def(new_node)
     if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef):
         init_sig = mapper.fdef_to_sig(init_node, True)
+        args_match = True
+        if isinstance(new_node, FuncDef):
+            new_sig = mapper.fdef_to_sig(new_node, True)
+            args_match = check_matching_args(init_sig, new_sig)
 
         defining_ir = mapper.type_to_ir.get(init_node.info)
         # If there is a nontrivial __init__ that wasn't defined in an
         # extension class, we need to make the constructor take *args,
         # **kwargs so it can call tp_init.
         if (
-            defining_ir is None
-            or not defining_ir.is_ext_class
-            or cdef.info["__init__"].plugin_generated
-        ) and init_node.info.fullname != "builtins.object":
+            (
+                defining_ir is None
+                or not defining_ir.is_ext_class
+                or cdef.info["__init__"].plugin_generated
+            )
+            and init_node.info.fullname != "builtins.object"
+        ) or not args_match:
             init_sig = FuncSignature(
                 [
                     init_sig.args[0],
diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c
index b7593491a6e6..ca09c347b4ff 100644
--- a/mypyc/lib-rt/misc_ops.c
+++ b/mypyc/lib-rt/misc_ops.c
@@ -227,6 +227,17 @@ PyObject *CPyType_FromTemplate(PyObject *template,
     if (!name)
         goto error;
 
+    if (template_->tp_doc) {
+        // cpython expects tp_doc to be heap-allocated so convert it here to
+        // avoid segfaults on deallocation.
+        Py_ssize_t size = strlen(template_->tp_doc) + 1;
+        char *doc = (char *)PyMem_Malloc(size);
+        if (!doc)
+            goto error;
+        memcpy(doc, template_->tp_doc, size);
+        template_->tp_doc = doc;
+    }
+
     // Allocate the type and then copy the main stuff in.
     t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0);
     if (!t)
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index c041c661741c..fb5512b77279 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -3,7 +3,7 @@
 
 import _typeshed
 from typing import (
-    TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set,
+    Self, TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set,
     overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol
 )
 
@@ -40,9 +40,11 @@ def __pow__(self, other: T_contra, modulo: _M) -> T_co: ...
 
 class object:
     __class__: type
+    def __new__(cls) -> Self: pass
     def __init__(self) -> None: pass
     def __eq__(self, x: object) -> bool: pass
     def __ne__(self, x: object) -> bool: pass
+    def __str__(self) -> str: pass
 
 class type:
     def __init__(self, o: object) -> None: ...
diff --git a/mypyc/test-data/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi
index d37129bc2e0b..8d89e4f93bc9 100644
--- a/mypyc/test-data/fixtures/typing-full.pyi
+++ b/mypyc/test-data/fixtures/typing-full.pyi
@@ -32,6 +32,7 @@ Final = 0
 TypedDict = 0
 NoReturn = 0
 NewType = 0
+Self = 0
 Callable: _SpecialForm
 Union: _SpecialForm
 Literal: _SpecialForm
diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test
index a573bb8e0668..bb55958dc6dc 100644
--- a/mypyc/test-data/irbuild-classes.test
+++ b/mypyc/test-data/irbuild-classes.test
@@ -1659,3 +1659,78 @@ def native_class(x):
 L0:
     r0 = CPy_TYPE(x)
     return r0
+
+[case testDunderNew]
+from __future__ import annotations
+
+class Test:
+    val: int
+
+    def __new__(cls, val: int) -> Test:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+def fn() -> Test:
+    return Test.__new__(Test, 42)
+
+class NewClassMethod:
+    val: int
+
+    @classmethod
+    def __new__(cls, val: int) -> NewClassMethod:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+def fn2() -> NewClassMethod:
+    return NewClassMethod.__new__(42)
+
+[out]
+def Test.__new__(cls, val):
+    cls :: object
+    val :: int
+    r0, obj :: __main__.Test
+    r1 :: bool
+L0:
+    r0 = __mypyc__Test_setup(cls)
+    obj = r0
+    obj.val = val; r1 = is_error
+    return obj
+def fn():
+    r0 :: object
+    r1 :: __main__.Test
+L0:
+    r0 = __main__.Test :: type
+    r1 = Test.__new__(r0, 84)
+    return r1
+def NewClassMethod.__new__(cls, val):
+    cls :: object
+    val :: int
+    r0, obj :: __main__.NewClassMethod
+    r1 :: bool
+L0:
+    r0 = __mypyc__NewClassMethod_setup(cls)
+    obj = r0
+    obj.val = val; r1 = is_error
+    return obj
+def fn2():
+    r0 :: object
+    r1 :: __main__.NewClassMethod
+L0:
+    r0 = __main__.NewClassMethod :: type
+    r1 = NewClassMethod.__new__(r0, 84)
+    return r1
+
+[case testUnsupportedDunderNew]
+from __future__ import annotations
+from mypy_extensions import mypyc_attr
+
+@mypyc_attr(native_class=False)
+class NonNative:
+    def __new__(cls) -> NonNative:
+        return super().__new__(cls)  # E: super().__new__() not supported for non-extension classes
+
+class InheritsPython(dict):
+    def __new__(cls) -> InheritsPython:
+        return super().__new__(cls)  # E: super().__new__() not supported for classes inheriting from non-native classes
diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test
index 2e55ee70687e..b25dc9458fd1 100644
--- a/mypyc/test-data/run-classes.test
+++ b/mypyc/test-data/run-classes.test
@@ -3365,3 +3365,432 @@ def test_python_class() -> None:
 
 [file dynamic.py]
 class Dyn: pass
+
+[case testDunderNew]
+from __future__ import annotations
+from typing import Any, Union
+
+from testutil import assertRaises
+
+class Add:
+    l: IntLike
+    r: IntLike
+
+    def __new__(cls, l: IntLike, r: IntLike) -> Any:
+        return (
+            l if r == 0 else
+            r if l == 0 else
+            super().__new__(cls)
+        )
+
+    def __init__(self, l: IntLike, r: IntLike):
+        self.l = l
+        self.r = r
+
+IntLike = Union[int, Add]
+
+class RaisesException:
+    def __new__(cls, val: int) -> RaisesException:
+        if val == 0:
+            raise RuntimeError("Invalid value!")
+        return super().__new__(cls)
+
+    def __init__(self, val: int) -> None:
+        self.val = val
+
+class ClsArgNotPassed:
+    def __new__(cls) -> Any:
+        return super().__new__(str)
+
+def test_dunder_new() -> None:
+    add_instance: Any = Add(1, 5)
+    assert type(add_instance) == Add
+    assert add_instance.l == 1
+    assert add_instance.r == 5
+
+    # TODO: explicit types should not be needed but mypy does not use
+    # the return type of __new__ which makes mypyc add casts to Add.
+    right_int: Any = Add(0, 5)
+    assert type(right_int) == int
+    assert right_int == 5
+
+    left_int: Any = Add(1, 0)
+    assert type(left_int) == int
+    assert left_int == 1
+
+    with assertRaises(RuntimeError, "Invalid value!"):
+        raised = RaisesException(0)
+
+    not_raised = RaisesException(1)
+    assert not_raised.val == 1
+
+    with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
+        str_as_cls = ClsArgNotPassed()
+
+
+[case testDunderNewInInterpreted]
+from __future__ import annotations
+from typing import Any, Union
+
+class Add:
+    l: IntLike
+    r: IntLike
+
+    def __new__(cls, l: IntLike, r: IntLike) -> Any:
+        print(f'running __new__ with {l} and {r}')
+
+        return (
+            l if r == 0 else
+            r if l == 0 else
+            super().__new__(cls)
+        )
+
+    def __init__(self, l: IntLike, r: IntLike):
+        self.l = l
+        self.r = r
+
+    def __repr__(self) -> str:
+        return f'({self.l} + {self.r})'
+
+IntLike = Union[int, Add]
+
+class RaisesException:
+    def __new__(cls, val: int) -> RaisesException:
+        if val == 0:
+            raise RuntimeError("Invalid value!")
+        return super().__new__(cls)
+
+    def __init__(self, val: int) -> None:
+        self.val = val
+
+class ClsArgNotPassed:
+    def __new__(cls) -> Any:
+        return super().__new__(str)
+
+[file driver.py]
+from native import Add, ClsArgNotPassed, RaisesException
+
+from testutil import assertRaises
+
+print(f'{Add(1, 5)=}')
+print(f'{Add(0, 5)=}')
+print(f'{Add(1, 0)=}')
+
+with assertRaises(RuntimeError, "Invalid value!"):
+    raised = RaisesException(0)
+
+not_raised = RaisesException(1)
+assert not_raised.val == 1
+
+with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
+    str_as_cls = ClsArgNotPassed()
+
+[out]
+running __new__ with 1 and 5
+Add(1, 5)=(1 + 5)
+running __new__ with 0 and 5
+Add(0, 5)=5
+running __new__ with 1 and 0
+Add(1, 0)=1
+
+[case testInheritedDunderNew]
+from __future__ import annotations
+from mypy_extensions import mypyc_attr
+from typing_extensions import Self
+
+from m import interpreted_subclass
+
+@mypyc_attr(allow_interpreted_subclasses=True)
+class Base:
+    val: int
+
+    def __new__(cls, val: int) -> Self:
+        obj = super().__new__(cls)
+        obj.val = val + 1
+        return obj
+
+    def __init__(self, val: int) -> None:
+        self.init_val = val
+
+class Sub(Base):
+    def __new__(cls, val: int) -> Self:
+        return super().__new__(cls, val + 1)
+
+    def __init__(self, val: int) -> None:
+        super().__init__(val)
+        self.init_val = self.init_val * 2
+
+class SubWithoutNew(Base):
+    def __init__(self, val: int) -> None:
+        super().__init__(val)
+        self.init_val = self.init_val * 2
+
+class BaseWithoutInterpretedSubclasses:
+    val: int
+
+    def __new__(cls, val: int) -> Self:
+        obj = super().__new__(cls)
+        obj.val = val + 1
+        return obj
+
+    def __init__(self, val: int) -> None:
+        self.init_val = val
+
+class SubNoInterpreted(BaseWithoutInterpretedSubclasses):
+    def __new__(cls, val: int) -> Self:
+        return super().__new__(cls, val + 1)
+
+    def __init__(self, val: int) -> None:
+        super().__init__(val)
+        self.init_val = self.init_val * 2
+
+class SubNoInterpretedWithoutNew(BaseWithoutInterpretedSubclasses):
+    def __init__(self, val: int) -> None:
+        super().__init__(val)
+        self.init_val = self.init_val * 2
+
+def test_inherited_dunder_new() -> None:
+    b = Base(42)
+    assert type(b) == Base
+    assert b.val == 43
+    assert b.init_val == 42
+
+    s = Sub(42)
+    assert type(s) == Sub
+    assert s.val == 44
+    assert s.init_val == 84
+
+    s2 = SubWithoutNew(42)
+    assert type(s2) == SubWithoutNew
+    assert s2.val == 43
+    assert s2.init_val == 84
+
+def test_inherited_dunder_new_without_interpreted_subclasses() -> None:
+    b = BaseWithoutInterpretedSubclasses(42)
+    assert type(b) == BaseWithoutInterpretedSubclasses
+    assert b.val == 43
+    assert b.init_val == 42
+
+    s = SubNoInterpreted(42)
+    assert type(s) == SubNoInterpreted
+    assert s.val == 44
+    assert s.init_val == 84
+
+    s2 = SubNoInterpretedWithoutNew(42)
+    assert type(s2) == SubNoInterpretedWithoutNew
+    assert s2.val == 43
+    assert s2.init_val == 84
+
+def test_interpreted_subclass() -> None:
+    interpreted_subclass(Base)
+
+[file m.py]
+from __future__ import annotations
+from typing_extensions import Self
+
+def interpreted_subclass(base) -> None:
+    b = base(42)
+    assert type(b) == base
+    assert b.val == 43
+    assert b.init_val == 42
+
+    class InterpretedSub(base):
+        def __new__(cls, val: int) -> Self:
+            return super().__new__(cls, val + 1)
+
+        def __init__(self, val: int) -> None:
+            super().__init__(val)
+            self.init_val : int = self.init_val * 2
+
+    s = InterpretedSub(42)
+    assert type(s) == InterpretedSub
+    assert s.val == 44
+    assert s.init_val == 84
+
+    class InterpretedSubWithoutNew(base):
+        def __init__(self, val: int) -> None:
+            super().__init__(val)
+            self.init_val : int = self.init_val * 2
+
+    s2 = InterpretedSubWithoutNew(42)
+    assert type(s2) == InterpretedSubWithoutNew
+    assert s2.val == 43
+    assert s2.init_val == 84
+
+[typing fixtures/typing-full.pyi]
+
+[case testDunderNewInitArgMismatch]
+from __future__ import annotations
+from testutil import assertRaises
+
+class Test0:
+    @classmethod
+    def __new__(cls, val: int = 42) -> Test0:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+    def __init__(self) -> None:
+        self.val = 0
+
+class Test1:
+    def __new__(cls, val: int) -> Test1:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+    def __init__(self) -> None:
+        self.val = 0
+
+class Test2:
+    def __new__(cls) -> Test2:
+        obj = super().__new__(cls)
+        return obj
+
+    def __init__(self, val: int) -> None:
+        self.val = val
+
+def test_arg_mismatch() -> None:
+    t0 = Test0()
+    assert t0.val == 0
+    t0 = Test0.__new__(1)
+    assert t0.val == 1
+    with assertRaises(TypeError, "__new__() missing required argument 'val'"):
+        t1 = Test1()
+    t1 = Test1.__new__(Test1, 2)
+    assert t1.val == 2
+    with assertRaises(TypeError, "__new__() takes at most 0 arguments"):
+        t2 = Test2(42)
+    t2 = Test2.__new__(Test2)
+    with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"):
+        print(t2.val)
+
+[case testDunderNewInitArgMismatchInInterpreted]
+from __future__ import annotations
+
+class Test0:
+    # TODO: It should be possible to annotate '@classmethod' here
+    # but when it's added calling __new__ in interpreted code
+    # without the explicit type param results in a TypeError.
+    def __new__(cls, val: int = 42) -> Test0:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+    def __init__(self) -> None:
+        self.val = 0
+
+class Test1:
+    def __new__(cls, val: int) -> Test1:
+        obj = super().__new__(cls)
+        obj.val = val
+        return obj
+
+    def __init__(self) -> None:
+        self.val = 0
+
+class Test2:
+    def __new__(cls) -> Test2:
+        obj = super().__new__(cls)
+        return obj
+
+    def __init__(self, val: int) -> None:
+        self.val = val
+
+[file driver.py]
+from native import Test0, Test1, Test2
+from testutil import assertRaises
+
+t0 = Test0()
+assert t0.val == 0
+t0 = Test0.__new__(Test0, 1)
+assert t0.val == 1
+with assertRaises(TypeError, "__new__() missing required argument 'val'"):
+    t1 = Test1()
+t1 = Test1.__new__(Test1, 2)
+assert t1.val == 2
+with assertRaises(TypeError, "__new__() takes at most 0 arguments"):
+    t2 = Test2(42)
+t2 = Test2.__new__(Test2)
+with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"):
+    print(t2.val)
+
+[case testDunderNewAttributeAccess]
+from __future__ import annotations
+
+from mypy_extensions import u8
+from testutil import assertRaises
+
+class Test:
+    native: int
+    generic: object
+    bitfield: u8
+    default: int = 5
+
+    def __new__(cls, native: int, generic: object, bitfield: u8) -> Test:
+        obj = super().__new__(cls)
+
+        with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"):
+            print(obj.native)
+        with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"):
+            print(obj.generic)
+        with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"):
+            print(obj.bitfield)
+
+        obj.native = native
+        obj.generic = generic
+        obj.bitfield = bitfield
+
+        obj.native = obj.native + 1
+        obj.generic = obj.generic.__str__()
+        obj.bitfield = obj.bitfield & 0x0F
+        obj.default = obj.default * 2
+        return obj
+
+def test_attribute_access() -> None:
+    t = Test(42, {}, 0xCC)
+    assert t.native == 43
+    assert t.generic == "{}"
+    assert t.bitfield == 0x0C
+    assert t.default == 10
+
+[case testDunderNewAttributeAccessInInterpreted]
+from __future__ import annotations
+
+from mypy_extensions import u8
+from testutil import assertRaises
+
+class Test:
+    native: int
+    generic: object
+    bitfield: u8
+    default: int = 5
+
+    def __new__(cls, native: int, generic: object, bitfield: u8) -> Test:
+        obj = super().__new__(cls)
+
+        with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"):
+            print(obj.native)
+        with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"):
+            print(obj.generic)
+        with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"):
+            print(obj.bitfield)
+
+        obj.native = native
+        obj.generic = generic
+        obj.bitfield = bitfield
+
+        obj.native = obj.native + 1
+        obj.generic = obj.generic.__str__()
+        obj.bitfield = obj.bitfield & 0x0F
+        obj.default = obj.default * 2
+        return obj
+
+[file driver.py]
+from native import Test
+
+t = Test(42, {}, 0xCC)
+assert t.native == 43
+assert t.generic == "{}"
+assert t.bitfield == 0x0C
+assert t.default == 10

From 4847ec8fcd98b041125a0a08f72b2f02a9e5be71 Mon Sep 17 00:00:00 2001
From: Brian Schubert 
Date: Fri, 29 Aug 2025 19:38:04 -0400
Subject: [PATCH 1594/1617] Fix forward references in type parameters of
 overparameterized PEP 695 type aliases (#19725)

Fixes https://github.com/python/mypy/issues/19734

Make semanal defer for placeholders in the upper bounds / constraints,
even when the type variable doesn't appear in the target.


https://discuss.python.org/t/mypy-raises-exception-for-certain-type-alias-definitions-with-parameters-bounded-by-a-not-yet-defined-type/103305
---
 mypy/semanal.py                     | 3 ++-
 test-data/unit/check-python312.test | 9 +++++++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/mypy/semanal.py b/mypy/semanal.py
index fa5d9fdc82c4..213c8d04122d 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -5598,7 +5598,8 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
             else:
                 incomplete_target = has_placeholder(res)
 
-            if self.found_incomplete_ref(tag) or incomplete_target:
+            incomplete_tv = any(has_placeholder(tv) for tv in alias_tvars)
+            if self.found_incomplete_ref(tag) or incomplete_target or incomplete_tv:
                 # Since we have got here, we know this must be a type alias (incomplete refs
                 # may appear in nested positions), therefore use becomes_typeinfo=True.
                 self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True)
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 01364bdfa32a..382822ced861 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2112,3 +2112,12 @@ if T(x) is str:     # E: "TypeAliasType" not callable
     reveal_type(x)  # N: Revealed type is "builtins.object"
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
+
+[case testPEP695TypeAliasForwardReferenceInUnusedTypeVar]
+# https://discuss.python.org/t/103305
+type Alias1[T: "A"] = int
+type Alias2[T: ("A", int)] = int
+class A: ...
+
+x1: Alias1[A]  # ok
+x2: Alias2[A]  # ok

From 06f97b256e760d68b067510d6213db7af372ab73 Mon Sep 17 00:00:00 2001
From: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com>
Date: Sat, 30 Aug 2025 11:23:25 +0200
Subject: [PATCH 1595/1617] Do not display import-related errors after
 module-level always false assert (#19347)

Fixes #19346
---
 mypy/semanal_pass1.py                      |  3 +++
 test-data/unit/check-incremental.test      |  9 +++++++++
 test-data/unit/check-unreachable-code.test | 16 ++++++++++++++++
 3 files changed, 28 insertions(+)

diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py
index aaa01969217a..266fd236a01f 100644
--- a/mypy/semanal_pass1.py
+++ b/mypy/semanal_pass1.py
@@ -74,6 +74,9 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -
                     if last.end_line is not None:
                         # We are on a Python version recent enough to support end lines.
                         self.skipped_lines |= set(range(next_def.line, last.end_line + 1))
+                file.imports = [
+                    i for i in file.imports if (i.line, i.column) <= (defn.line, defn.column)
+                ]
                 del file.defs[i + 1 :]
                 break
         file.skipped_lines = self.skipped_lines
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index c3fe98e69d95..defe7402730f 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -6888,3 +6888,12 @@ class A:
 [out]
 [out2]
 main:3: error: Too few arguments
+
+[case testUnreachableAfterToplevelAssertImportThirdParty]
+# flags: --platform unknown
+import sys
+assert sys.platform == 'linux'
+import does_not_exist
+[builtins fixtures/ops.pyi]
+[out]
+[out2]
diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test
index f425410a9774..645f81e89ca1 100644
--- a/test-data/unit/check-unreachable-code.test
+++ b/test-data/unit/check-unreachable-code.test
@@ -778,6 +778,22 @@ assert sys.platform == 'lol'
 def bar() -> None: pass
 [builtins fixtures/ops.pyi]
 
+[case testUnreachableAfterToplevelAssertImportThirdParty]
+# flags: --platform unknown
+import sys
+assert sys.platform == 'linux'
+import does_not_exist
+[builtins fixtures/ops.pyi]
+
+[case testUnreachableAfterToplevelAssertImportThirdParty2]
+# flags: --platform unknown
+import sys
+import bad; assert sys.platform == 'linux'; import does_not_exist
+[builtins fixtures/ops.pyi]
+[out]
+main:3: error: Cannot find implementation or library stub for module named "bad"
+main:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+
 [case testUnreachableAfterToplevelAssertNotInsideIf]
 import sys
 if sys.version_info[0] >= 2:

From 618cf55d4b454dc0b64b06ed387a0f5c70bc0cfe Mon Sep 17 00:00:00 2001
From: Christoph Tyralla 
Date: Sat, 30 Aug 2025 11:25:16 +0200
Subject: [PATCH 1596/1617] `--strict-equality` for `None` (#19718)

Fixes #18386 (at least partly)

(edited)

In a first test run, in which I included checks against `None` in
`--strict-equality`, the Mypy primer gave hundreds of new
`comparison-overlap` reports. Many of them seem really helpful
(including those for the Mypy source code itself), because it is often
hard to tell if non-overlapping `None` checks are just remnants of
incomplete refactorings or can handle cases with corrupted data or
similar issues. As it was only a little effort, I decided to add the
option `--strict-equality-for-none` to Mypy, which is disabled even in
`--strict` mode. Other libraries could adjust to this new behaviour if
and when they want. If many of them do so, we could eventually enable
`--strict-equality-for-none` in `--strict` mode or even merge it with
`--strict-equality` later.

The remaining new true positives revealed by the Mypy primer are the
result of no longer excluding types with custom `__eq__` methods for
identity checks (which, in my opinion, makes sense even in case
`--strict-equality-for-none` would be rejected).

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 docs/source/command_line.rst          | 15 +++++++++++++-
 docs/source/config_file.rst           | 10 ++++++++-
 docs/source/error_code_list2.rst      | 23 +++++++++++++++++++++
 mypy/checkexpr.py                     | 16 ++++++++++-----
 mypy/main.py                          | 11 +++++++++-
 mypy/options.py                       |  4 ++++
 test-data/unit/check-expressions.test | 29 +++++++++++++++++++++++++++
 7 files changed, 100 insertions(+), 8 deletions(-)

diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst
index db2407e17df8..c1b757a00ef2 100644
--- a/docs/source/command_line.rst
+++ b/docs/source/command_line.rst
@@ -728,9 +728,22 @@ of the above sections.
        if text != b'other bytes':  # Error: non-overlapping equality check!
            ...
 
-       assert text is not None  # OK, check against None is allowed as a special case.
+       assert text is not None  # OK, check against None is allowed
 
 
+.. option:: --strict-equality-for-none
+
+    This flag extends :option:`--strict-equality ` for checks
+    against ``None``:
+
+    .. code-block:: python
+
+       text: str
+       assert text is not None  # Error: non-overlapping identity check!
+
+    Note that :option:`--strict-equality-for-none `
+    only works in combination with :option:`--strict-equality `.
+
 .. option:: --strict-bytes
 
     By default, mypy treats ``bytearray`` and ``memoryview`` as subtypes of ``bytes`` which
diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst
index b4f134f26cb1..934e465a7c23 100644
--- a/docs/source/config_file.rst
+++ b/docs/source/config_file.rst
@@ -834,7 +834,15 @@ section of the command line docs.
    :default: False
 
    Prohibit equality checks, identity checks, and container checks between
-   non-overlapping types.
+   non-overlapping types (except ``None``).
+
+.. confval:: strict_equality_for_none
+
+   :type: boolean
+   :default: False
+
+   Include ``None`` in strict equality checks (requires :confval:`strict_equality`
+   to be activated).
 
 .. confval:: strict_bytes
 
diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst
index 784c2ad72819..125671bc2bef 100644
--- a/docs/source/error_code_list2.rst
+++ b/docs/source/error_code_list2.rst
@@ -145,6 +145,29 @@ literal:
     def is_magic(x: bytes) -> bool:
         return x == b'magic'  # OK
 
+:option:`--strict-equality ` does not include comparisons with
+``None``:
+
+.. code-block:: python
+
+    # mypy: strict-equality
+
+    def is_none(x: str) -> bool:
+        return x is None  # OK
+
+If you want such checks, you must also activate
+:option:`--strict-equality-for-none ` (we might merge
+these two options later).
+
+.. code-block:: python
+
+    # mypy: strict-equality strict-equality-for-none
+
+    def is_none(x: str) -> bool:
+        # Error: Non-overlapping identity check
+        #        (left operand type: "str", right operand type: "None")
+        return x is None
+
 .. _code-no-untyped-call:
 
 Check that no untyped functions are called [no-untyped-call]
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index fd83b6359ddc..794af875867a 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -3721,7 +3721,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
             elif operator == "is" or operator == "is not":
                 right_type = self.accept(right)  # validate the right operand
                 sub_result = self.bool_type()
-                if self.dangerous_comparison(left_type, right_type):
+                if self.dangerous_comparison(left_type, right_type, identity_check=True):
                     # Show the most specific literal types possible
                     left_type = try_getting_literal(left_type)
                     right_type = try_getting_literal(right_type)
@@ -3763,6 +3763,7 @@ def dangerous_comparison(
         original_container: Type | None = None,
         seen_types: set[tuple[Type, Type]] | None = None,
         prefer_literal: bool = True,
+        identity_check: bool = False,
     ) -> bool:
         """Check for dangerous non-overlapping comparisons like 42 == 'no'.
 
@@ -3790,10 +3791,12 @@ def dangerous_comparison(
 
         left, right = get_proper_types((left, right))
 
-        # We suppress the error if there is a custom __eq__() method on either
-        # side. User defined (or even standard library) classes can define this
+        # We suppress the error for equality and container checks if there is a custom __eq__()
+        # method on either side. User defined (or even standard library) classes can define this
         # to return True for comparisons between non-overlapping types.
-        if custom_special_method(left, "__eq__") or custom_special_method(right, "__eq__"):
+        if (
+            custom_special_method(left, "__eq__") or custom_special_method(right, "__eq__")
+        ) and not identity_check:
             return False
 
         if prefer_literal:
@@ -3817,7 +3820,10 @@ def dangerous_comparison(
             #
             # TODO: find a way of disabling the check only for types resulted from the expansion.
             return False
-        if isinstance(left, NoneType) or isinstance(right, NoneType):
+        if self.chk.options.strict_equality_for_none:
+            if isinstance(left, NoneType) and isinstance(right, NoneType):
+                return False
+        elif isinstance(left, NoneType) or isinstance(right, NoneType):
             return False
         if isinstance(left, UnionType) and isinstance(right, UnionType):
             left = remove_optional(left)
diff --git a/mypy/main.py b/mypy/main.py
index ad257bab6996..706d1daef680 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -903,7 +903,16 @@ def add_invertible_flag(
         "--strict-equality",
         default=False,
         strict_flag=True,
-        help="Prohibit equality, identity, and container checks for non-overlapping types",
+        help="Prohibit equality, identity, and container checks for non-overlapping types "
+        "(except `None`)",
+        group=strictness_group,
+    )
+
+    add_invertible_flag(
+        "--strict-equality-for-none",
+        default=False,
+        strict_flag=False,
+        help="Extend `--strict-equality` for `None` checks",
         group=strictness_group,
     )
 
diff --git a/mypy/options.py b/mypy/options.py
index ad4b26cca095..b3dc9639a41d 100644
--- a/mypy/options.py
+++ b/mypy/options.py
@@ -55,6 +55,7 @@ class BuildType:
     "mypyc",
     "strict_concatenate",
     "strict_equality",
+    "strict_equality_for_none",
     "strict_optional",
     "warn_no_return",
     "warn_return_any",
@@ -230,6 +231,9 @@ def __init__(self) -> None:
         # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors.
         self.strict_equality = False
 
+        # Extend the logic of `scrict_equality` for comparisons with `None`.
+        self.strict_equality_for_none = False
+
         # Disable treating bytearray and memoryview as subtypes of bytes
         self.strict_bytes = False
 
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index 33271a3cc04c..ea6eac9a39b3 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -2419,6 +2419,35 @@ assert a == c
 [builtins fixtures/list.pyi]
 [typing fixtures/typing-full.pyi]
 
+[case testStrictEqualityForNone]
+# flags: --strict-equality --strict-equality-for-none
+
+class A: ...
+
+def a1(x: A) -> None:
+    assert x is None  # E: Non-overlapping identity check (left operand type: "A", right operand type: "None")
+def a2(x: A) -> None:
+    x is not None  # E: Non-overlapping identity check (left operand type: "A", right operand type: "None")
+def a3(x: A) -> None:
+    None == x  # E: Non-overlapping equality check (left operand type: "None", right operand type: "A")
+def a4(x: list[A]) -> None:
+    None in x  # E: Non-overlapping container check (element type: "None", container item type: "A")
+
+class B:
+    def __eq__(self, x: object) -> bool: ...
+
+def b1(x: B) -> None:
+    assert x is None  # E: Non-overlapping identity check (left operand type: "B", right operand type: "None")
+def b2(x: B) -> None:
+    x is not None  # E: Non-overlapping identity check (left operand type: "B", right operand type: "None")
+def b3(x: B) -> None:
+    x == None
+def b4(x: list[B]) -> None:
+    None in x
+
+[builtins fixtures/list.pyi]
+[typing fixtures/typing-full.pyi]
+
 [case testUnimportedHintAny]
 def f(x: Any) -> None:  # E: Name "Any" is not defined \
                         # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any")

From 03aa0f857373780f91e1c8b0a7a47a1414a75f28 Mon Sep 17 00:00:00 2001
From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com>
Date: Sat, 30 Aug 2025 08:02:13 -0500
Subject: [PATCH 1597/1617] Add idlemypyextension to IDE integrations section
 (#18615)

In this pull request, we add idlemypyextension
(https://github.com/CoolCat467/idlemypyextension) for the IDLE text
editor to the Popular IDE integrations section in the README.
---
 README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.md b/README.md
index 45b71c8a4824..8040566b18ef 100644
--- a/README.md
+++ b/README.md
@@ -142,6 +142,7 @@ Mypy can be integrated into popular IDEs:
 - Emacs: using [Flycheck](https://github.com/flycheck/)
 - Sublime Text: [SublimeLinter-contrib-mypy](https://github.com/fredcallaway/SublimeLinter-contrib-mypy)
 - PyCharm: [mypy plugin](https://github.com/dropbox/mypy-PyCharm-plugin)
+- IDLE: [idlemypyextension](https://github.com/CoolCat467/idlemypyextension)
 - pre-commit: use [pre-commit mirrors-mypy](https://github.com/pre-commit/mirrors-mypy), although
   note by default this will limit mypy's ability to analyse your third party dependencies.
 

From dcaca0e7dca053f3dff5c5c3ad3b1a65df572cce Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 30 Aug 2025 16:24:20 +0100
Subject: [PATCH 1598/1617] Use some macros in native_internal (#19757)

This should be a no-op from semantics/format point if view. Essentially
three things here:
* Add some macros to hide boilerplate for common steps
* Make use of some magic constants more obvious (most notably `2`)
* Add some `likely()/unlikely()` directives (this actually seems to have
some small positive effect).
---
 mypyc/lib-rt/native_internal.c | 269 ++++++++++++++-------------------
 1 file changed, 113 insertions(+), 156 deletions(-)

diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
index 1c211464b19c..b9bf899588fb 100644
--- a/mypyc/lib-rt/native_internal.c
+++ b/mypyc/lib-rt/native_internal.c
@@ -16,6 +16,23 @@
 #define MEDIUM_INT_TAG 1
 #define LONG_INT_TAG 3
 
+#define CPY_BOOL_ERROR 2
+#define CPY_NONE_ERROR 2
+#define CPY_NONE 1
+
+#define _CHECK_BUFFER(data, err)      if (unlikely(_check_buffer(data) == CPY_NONE_ERROR)) \
+                                          return err;
+#define _CHECK_SIZE(data, need)       if (unlikely(_check_size((BufferObject *)data, need) == CPY_NONE_ERROR)) \
+                                          return CPY_NONE_ERROR;
+#define _CHECK_READ(data, size, err)  if (unlikely(_check_read((BufferObject *)data, size) == CPY_NONE_ERROR)) \
+                                          return err;
+
+#define _READ(data, type)  *(type *)(((BufferObject *)data)->buf + ((BufferObject *)data)->pos); \
+                           ((BufferObject *)data)->pos += sizeof(type);
+
+#define _WRITE(data, type, v)  *(type *)(((BufferObject *)data)->buf + ((BufferObject *)data)->pos) = v; \
+                               ((BufferObject *)data)->pos += sizeof(type);
+
 typedef struct {
     PyObject_HEAD
     Py_ssize_t pos;
@@ -140,38 +157,38 @@ static PyTypeObject BufferType = {
 
 static inline char
 _check_buffer(PyObject *data) {
-    if (Py_TYPE(data) != &BufferType) {
+    if (unlikely(Py_TYPE(data) != &BufferType)) {
         PyErr_Format(
             PyExc_TypeError, "data must be a Buffer object, got %s", Py_TYPE(data)->tp_name
         );
-        return 2;
+        return CPY_NONE_ERROR;
     }
-    return 1;
+    return CPY_NONE;
 }
 
 static inline char
 _check_size(BufferObject *data, Py_ssize_t need) {
     Py_ssize_t target = data->pos + need;
     if (target <= data->size)
-        return 1;
+        return CPY_NONE;
     do
         data->size *= 2;
     while (target >= data->size);
     data->buf = PyMem_Realloc(data->buf, data->size);
-    if (!data->buf) {
+    if (unlikely(data->buf == NULL)) {
         PyErr_NoMemory();
-        return 2;
+        return CPY_NONE_ERROR;
     }
-    return 1;
+    return CPY_NONE;
 }
 
 static inline char
 _check_read(BufferObject *data, Py_ssize_t need) {
-    if (data->pos + need > data->end) {
+    if (unlikely(data->pos + need > data->end)) {
         PyErr_SetString(PyExc_ValueError, "reading past the buffer end");
-        return 2;
+        return CPY_NONE_ERROR;
     }
-    return 1;
+    return CPY_NONE;
 }
 
 /*
@@ -182,14 +199,9 @@ bool format: single byte
 
 static char
 read_bool_internal(PyObject *data) {
-    if (_check_buffer(data) == 2)
-        return 2;
-
-    if (_check_read((BufferObject *)data, 1) == 2)
-        return 2;
-    char *buf = ((BufferObject *)data)->buf;
-    char res = buf[((BufferObject *)data)->pos];
-    ((BufferObject *)data)->pos += 1;
+    _CHECK_BUFFER(data, CPY_BOOL_ERROR)
+    _CHECK_READ(data, 1, CPY_BOOL_ERROR)
+    char res = _READ(data, char)
     return res;
 }
 
@@ -197,10 +209,10 @@ static PyObject*
 read_bool(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", NULL};
     PyObject *data = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
         return NULL;
     char res = read_bool_internal(data);
-    if (res == 2)
+    if (unlikely(res == CPY_BOOL_ERROR))
         return NULL;
     PyObject *retval = res ? Py_True : Py_False;
     Py_INCREF(retval);
@@ -209,16 +221,11 @@ read_bool(PyObject *self, PyObject *args, PyObject *kwds) {
 
 static char
 write_bool_internal(PyObject *data, char value) {
-    if (_check_buffer(data) == 2)
-        return 2;
-
-    if (_check_size((BufferObject *)data, 1) == 2)
-        return 2;
-    char *buf = ((BufferObject *)data)->buf;
-    buf[((BufferObject *)data)->pos] = value;
-    ((BufferObject *)data)->pos += 1;
+    _CHECK_BUFFER(data, CPY_NONE_ERROR)
+    _CHECK_SIZE(data, 1)
+    _WRITE(data, char, value)
     ((BufferObject *)data)->end += 1;
-    return 1;
+    return CPY_NONE;
 }
 
 static PyObject*
@@ -226,13 +233,13 @@ write_bool(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", "value", NULL};
     PyObject *data = NULL;
     PyObject *value = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
         return NULL;
-    if (!PyBool_Check(value)) {
+    if (unlikely(!PyBool_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a bool");
         return NULL;
     }
-    if (write_bool_internal(data, value == Py_True) == 2) {
+    if (unlikely(write_bool_internal(data, value == Py_True) == CPY_NONE_ERROR)) {
         return NULL;
     }
     Py_INCREF(Py_None);
@@ -247,30 +254,26 @@ str format: size followed by UTF-8 bytes
 
 static PyObject*
 read_str_internal(PyObject *data) {
-    if (_check_buffer(data) == 2)
-        return NULL;
+    _CHECK_BUFFER(data, NULL)
 
-    Py_ssize_t size;
-    char *buf = ((BufferObject *)data)->buf;
     // Read string length.
-    if (_check_read((BufferObject *)data, 1) == 2)
-        return NULL;
-    uint8_t first = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += 1;
-    if (first != LONG_STR_TAG) {
+    Py_ssize_t size;
+    _CHECK_READ(data, 1, NULL)
+    uint8_t first = _READ(data, uint8_t)
+    if (likely(first != LONG_STR_TAG)) {
         // Common case: short string (len <= 127).
         size = (Py_ssize_t)(first >> 1);
     } else {
-        size = *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos);
-        ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+        _CHECK_READ(data, sizeof(CPyTagged), NULL)
+        size = _READ(data, Py_ssize_t)
     }
     // Read string content.
-    if (_check_read((BufferObject *)data, size) == 2)
-        return NULL;
+    char *buf = ((BufferObject *)data)->buf;
+    _CHECK_READ(data, size, NULL)
     PyObject *res = PyUnicode_FromStringAndSize(
         buf + ((BufferObject *)data)->pos, (Py_ssize_t)size
     );
-    if (!res)
+    if (unlikely(res == NULL))
         return NULL;
     ((BufferObject *)data)->pos += size;
     return res;
@@ -280,47 +283,39 @@ static PyObject*
 read_str(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", NULL};
     PyObject *data = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
         return NULL;
     return read_str_internal(data);
 }
 
 static char
 write_str_internal(PyObject *data, PyObject *value) {
-    if (_check_buffer(data) == 2)
-        return 2;
+    _CHECK_BUFFER(data, CPY_NONE_ERROR)
 
     Py_ssize_t size;
     const char *chunk = PyUnicode_AsUTF8AndSize(value, &size);
-    if (!chunk)
-        return 2;
+    if (unlikely(chunk == NULL))
+        return CPY_NONE_ERROR;
 
     Py_ssize_t need;
-    char *buf;
     // Write string length.
-    if (size <= MAX_SHORT_LEN) {
+    if (likely(size <= MAX_SHORT_LEN)) {
         // Common case: short string (len <= 127) store as single byte.
         need = size + 1;
-        if (_check_size((BufferObject *)data, need) == 2)
-            return 2;
-        buf = ((BufferObject *)data)->buf;
-        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = (uint8_t)size << 1;
-        ((BufferObject *)data)->pos += 1;
+        _CHECK_SIZE(data, need)
+        _WRITE(data, uint8_t, (uint8_t)size << 1)
     } else {
         need = size + sizeof(Py_ssize_t) + 1;
-        if (_check_size((BufferObject *)data, need) == 2)
-            return 2;
-        buf = ((BufferObject *)data)->buf;
-        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = LONG_STR_TAG;
-        ((BufferObject *)data)->pos += 1;
-        *(Py_ssize_t *)(buf + ((BufferObject *)data)->pos) = size;
-        ((BufferObject *)data)->pos += sizeof(Py_ssize_t);
+        _CHECK_SIZE(data, need)
+        _WRITE(data, uint8_t, LONG_STR_TAG)
+        _WRITE(data, Py_ssize_t, size)
     }
     // Write string content.
+    char *buf = ((BufferObject *)data)->buf;
     memcpy(buf + ((BufferObject *)data)->pos, chunk, size);
     ((BufferObject *)data)->pos += size;
     ((BufferObject *)data)->end += need;
-    return 1;
+    return CPY_NONE;
 }
 
 static PyObject*
@@ -328,13 +323,13 @@ write_str(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", "value", NULL};
     PyObject *data = NULL;
     PyObject *value = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
         return NULL;
-    if (!PyUnicode_Check(value)) {
+    if (unlikely(!PyUnicode_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a str");
         return NULL;
     }
-    if (write_str_internal(data, value) == 2) {
+    if (unlikely(write_str_internal(data, value) == CPY_NONE_ERROR)) {
         return NULL;
     }
     Py_INCREF(Py_None);
@@ -348,14 +343,9 @@ float format:
 
 static double
 read_float_internal(PyObject *data) {
-    if (_check_buffer(data) == 2)
-        return CPY_FLOAT_ERROR;
-
-    if (_check_read((BufferObject *)data, sizeof(double)) == 2)
-        return CPY_FLOAT_ERROR;
-    char *buf = ((BufferObject *)data)->buf;
-    double res = *(double *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += sizeof(double);
+    _CHECK_BUFFER(data, CPY_FLOAT_ERROR)
+    _CHECK_READ(data, sizeof(double), CPY_FLOAT_ERROR)
+    double res = _READ(data, double);
     return res;
 }
 
@@ -363,10 +353,10 @@ static PyObject*
 read_float(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", NULL};
     PyObject *data = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
         return NULL;
     double retval = read_float_internal(data);
-    if (retval == CPY_FLOAT_ERROR && PyErr_Occurred()) {
+    if (unlikely(retval == CPY_FLOAT_ERROR && PyErr_Occurred())) {
         return NULL;
     }
     return PyFloat_FromDouble(retval);
@@ -374,16 +364,11 @@ read_float(PyObject *self, PyObject *args, PyObject *kwds) {
 
 static char
 write_float_internal(PyObject *data, double value) {
-    if (_check_buffer(data) == 2)
-        return 2;
-
-    if (_check_size((BufferObject *)data, sizeof(double)) == 2)
-        return 2;
-    char *buf = ((BufferObject *)data)->buf;
-    *(double *)(buf + ((BufferObject *)data)->pos) = value;
-    ((BufferObject *)data)->pos += sizeof(double);
+    _CHECK_BUFFER(data, CPY_NONE_ERROR)
+    _CHECK_SIZE(data, sizeof(double))
+    _WRITE(data, double, value)
     ((BufferObject *)data)->end += sizeof(double);
-    return 1;
+    return CPY_NONE;
 }
 
 static PyObject*
@@ -391,13 +376,13 @@ write_float(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", "value", NULL};
     PyObject *data = NULL;
     PyObject *value = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
         return NULL;
-    if (!PyFloat_Check(value)) {
+    if (unlikely(!PyFloat_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a float");
         return NULL;
     }
-    if (write_float_internal(data, PyFloat_AsDouble(value)) == 2) {
+    if (unlikely(write_float_internal(data, PyFloat_AsDouble(value)) == CPY_NONE_ERROR)) {
         return NULL;
     }
     Py_INCREF(Py_None);
@@ -413,27 +398,22 @@ int format:
 
 static CPyTagged
 read_int_internal(PyObject *data) {
-    if (_check_buffer(data) == 2)
-        return CPY_INT_TAG;
+    _CHECK_BUFFER(data, CPY_INT_TAG)
+    _CHECK_READ(data, 1, CPY_INT_TAG)
 
-    char *buf = ((BufferObject *)data)->buf;
-    if (_check_read((BufferObject *)data, 1) == 2)
-        return CPY_INT_TAG;
-
-    uint8_t first = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += 1;
+    uint8_t first = _READ(data, uint8_t)
     if ((first & MEDIUM_INT_TAG) == 0) {
        // Most common case: int that is small in absolute value.
        return ((Py_ssize_t)(first >> 1) + MIN_SHORT_INT) << 1;
     }
     if (first == MEDIUM_INT_TAG) {
-        CPyTagged ret = *(CPyTagged *)(buf + ((BufferObject *)data)->pos);
-        ((BufferObject *)data)->pos += sizeof(CPyTagged);
+        _CHECK_READ(data, sizeof(CPyTagged), CPY_INT_TAG)
+        CPyTagged ret = _READ(data, CPyTagged)
         return ret;
     }
     // People who have literal ints not fitting in size_t should be punished :-)
     PyObject *str_ret = read_str_internal(data);
-    if (str_ret == NULL)
+    if (unlikely(str_ret == NULL))
         return CPY_INT_TAG;
     PyObject* ret_long = PyLong_FromUnicodeObject(str_ret, 10);
     Py_DECREF(str_ret);
@@ -444,10 +424,10 @@ static PyObject*
 read_int(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", NULL};
     PyObject *data = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
         return NULL;
     CPyTagged retval = read_int_internal(data);
-    if (retval == CPY_INT_TAG) {
+    if (unlikely(retval == CPY_INT_TAG)) {
         return NULL;
     }
     return CPyTagged_StealAsObject(retval);
@@ -455,46 +435,34 @@ read_int(PyObject *self, PyObject *args, PyObject *kwds) {
 
 static char
 write_int_internal(PyObject *data, CPyTagged value) {
-    if (_check_buffer(data) == 2)
-        return 2;
+    _CHECK_BUFFER(data, CPY_NONE_ERROR)
 
-    char *buf;
-    if ((value & CPY_INT_TAG) == 0) {
+    if (likely((value & CPY_INT_TAG) == 0)) {
         Py_ssize_t real_value = CPyTagged_ShortAsSsize_t(value);
         if (real_value >= MIN_SHORT_INT && real_value <= MAX_SHORT_INT) {
             // Most common case: int that is small in absolute value.
-            if (_check_size((BufferObject *)data, 1) == 2)
-                return 2;
-            buf = ((BufferObject *)data)->buf;
-            *(uint8_t *)(buf + ((BufferObject *)data)->pos) = (uint8_t)(real_value - MIN_SHORT_INT) << 1;
-            ((BufferObject *)data)->pos += 1;
+            _CHECK_SIZE(data, 1)
+            _WRITE(data, uint8_t, (uint8_t)(real_value - MIN_SHORT_INT) << 1)
             ((BufferObject *)data)->end += 1;
         } else {
-            if (_check_size((BufferObject *)data, sizeof(CPyTagged) + 1) == 2)
-                return 2;
-            buf = ((BufferObject *)data)->buf;
-            *(uint8_t *)(buf + ((BufferObject *)data)->pos) = MEDIUM_INT_TAG;
-            ((BufferObject *)data)->pos += 1;
-            *(CPyTagged *)(buf + ((BufferObject *)data)->pos) = value;
-            ((BufferObject *)data)->pos += sizeof(CPyTagged);
+            _CHECK_SIZE(data, sizeof(CPyTagged) + 1)
+            _WRITE(data, uint8_t, MEDIUM_INT_TAG)
+            _WRITE(data, CPyTagged, value)
             ((BufferObject *)data)->end += sizeof(CPyTagged) + 1;
         }
     } else {
-        if (_check_size((BufferObject *)data, 1) == 2)
-            return 2;
-        buf = ((BufferObject *)data)->buf;
-        *(uint8_t *)(buf + ((BufferObject *)data)->pos) = LONG_INT_TAG;
-        ((BufferObject *)data)->pos += 1;
+        _CHECK_SIZE(data, 1)
+        _WRITE(data, uint8_t, LONG_INT_TAG)
         ((BufferObject *)data)->end += 1;
         PyObject *str_value = PyObject_Str(CPyTagged_LongAsObject(value));
-        if (str_value == NULL)
-            return 2;
+        if (unlikely(str_value == NULL))
+            return CPY_NONE_ERROR;
         char res = write_str_internal(data, str_value);
         Py_DECREF(str_value);
-        if (res == 2)
-            return 2;
+        if (unlikely(res == CPY_NONE_ERROR))
+            return CPY_NONE_ERROR;
     }
-    return 1;
+    return CPY_NONE;
 }
 
 static PyObject*
@@ -502,14 +470,14 @@ write_int(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", "value", NULL};
     PyObject *data = NULL;
     PyObject *value = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
         return NULL;
-    if (!PyLong_Check(value)) {
+    if (unlikely(!PyLong_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be an int");
         return NULL;
     }
     CPyTagged tagged_value = CPyTagged_BorrowFromObject(value);
-    if (write_int_internal(data, tagged_value) == 2) {
+    if (unlikely(write_int_internal(data, tagged_value) == CPY_NONE_ERROR)) {
         return NULL;
     }
     Py_INCREF(Py_None);
@@ -523,15 +491,9 @@ integer tag format (0 <= t <= 255):
 
 static uint8_t
 read_tag_internal(PyObject *data) {
-    if (_check_buffer(data) == 2)
-        return CPY_LL_UINT_ERROR;
-
-    if (_check_read((BufferObject *)data, 1) == 2)
-        return CPY_LL_UINT_ERROR;
-    char *buf = ((BufferObject *)data)->buf;
-
-    uint8_t ret = *(uint8_t *)(buf + ((BufferObject *)data)->pos);
-    ((BufferObject *)data)->pos += 1;
+    _CHECK_BUFFER(data, CPY_LL_UINT_ERROR)
+    _CHECK_READ(data, 1, CPY_LL_UINT_ERROR)
+    uint8_t ret = _READ(data, uint8_t)
     return ret;
 }
 
@@ -539,10 +501,10 @@ static PyObject*
 read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", NULL};
     PyObject *data = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
         return NULL;
     uint8_t retval = read_tag_internal(data);
-    if (retval == CPY_LL_UINT_ERROR && PyErr_Occurred()) {
+    if (unlikely(retval == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
         return NULL;
     }
     return PyLong_FromLong(retval);
@@ -550,16 +512,11 @@ read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
 
 static char
 write_tag_internal(PyObject *data, uint8_t value) {
-    if (_check_buffer(data) == 2)
-        return 2;
-
-    if (_check_size((BufferObject *)data, 1) == 2)
-        return 2;
-    uint8_t *buf = (uint8_t *)((BufferObject *)data)->buf;
-    *(buf + ((BufferObject *)data)->pos) = value;
-    ((BufferObject *)data)->pos += 1;
+    _CHECK_BUFFER(data, CPY_NONE_ERROR)
+    _CHECK_SIZE(data, 1)
+    _WRITE(data, uint8_t, value)
     ((BufferObject *)data)->end += 1;
-    return 1;
+    return CPY_NONE;
 }
 
 static PyObject*
@@ -567,14 +524,14 @@ write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
     static char *kwlist[] = {"data", "value", NULL};
     PyObject *data = NULL;
     PyObject *value = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value))
+    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
         return NULL;
     uint8_t unboxed = CPyLong_AsUInt8(value);
-    if (unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred()) {
+    if (unlikely(unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
         CPy_TypeError("u8", value);
         return NULL;
     }
-    if (write_tag_internal(data, unboxed) == 2) {
+    if (unlikely(write_tag_internal(data, unboxed) == CPY_NONE_ERROR)) {
         return NULL;
     }
     Py_INCREF(Py_None);

From 1660a392a79a8e36b82e2f572d2af3af468d7874 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sat, 30 Aug 2025 23:44:35 +0100
Subject: [PATCH 1599/1617] Try fixing test times after GC hack (#19766)

I am not sure what happens, but for some reason after GC
`freeze()`/`unfreeze()` hack https://github.com/python/mypy/pull/19681
was merged, compiled tests are running twice slower (on GH runner, but I
also see much smaller but visible slow-down locally). I have two
theories:
* The constant overhead we add outweighs the savings when running
thousands of tiny builds.
* The 8% of extra memory we use goes over the limit in the runner
because we were already very close to it.

In any case, I propose to try disabling this hack in most tests and see
if it helps.
---
 mypy/build.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/mypy/build.py b/mypy/build.py
index 4f22e0703d97..39199b39b6ad 100644
--- a/mypy/build.py
+++ b/mypy/build.py
@@ -3364,7 +3364,8 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
                 # TODO: see if it's possible to determine if we need to process only a
                 # _subset_ of the past SCCs instead of having to process them all.
                 if (
-                    platform.python_implementation() == "CPython"
+                    not manager.options.test_env
+                    and platform.python_implementation() == "CPython"
                     and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
                 ):
                     # When deserializing cache we create huge amount of new objects, so even
@@ -3379,7 +3380,8 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
                 for prev_scc in fresh_scc_queue:
                     process_fresh_modules(graph, prev_scc, manager)
                 if (
-                    platform.python_implementation() == "CPython"
+                    not manager.options.test_env
+                    and platform.python_implementation() == "CPython"
                     and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
                 ):
                     manager.gc_freeze_cycles += 1

From bf70dab2d09b61048a8b720e33efae18281a3313 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Sun, 31 Aug 2025 12:06:39 +0100
Subject: [PATCH 1600/1617] Use fast Python wrappers in native_internal
 (#19765)

This makes fixed format cache significantly faster in _interpreted_ mypy
(which is important e.g. for tests). In fact, `load_tree_time` with this
PR is 15% faster than when using `orjson`.
---
 mypyc/lib-rt/native_internal.c | 131 +++++++++++++++++++--------------
 mypyc/lib-rt/setup.py          |   9 ++-
 2 files changed, 83 insertions(+), 57 deletions(-)

diff --git a/mypyc/lib-rt/native_internal.c b/mypyc/lib-rt/native_internal.c
index b9bf899588fb..a6511a1caf25 100644
--- a/mypyc/lib-rt/native_internal.c
+++ b/mypyc/lib-rt/native_internal.c
@@ -206,11 +206,13 @@ read_bool_internal(PyObject *data) {
 }
 
 static PyObject*
-read_bool(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", NULL};
-    PyObject *data = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
+read_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", 0};
+    static CPyArg_Parser parser = {"O:read_bool", kwlist, 0};
+    PyObject *data;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
         return NULL;
+    }
     char res = read_bool_internal(data);
     if (unlikely(res == CPY_BOOL_ERROR))
         return NULL;
@@ -229,12 +231,14 @@ write_bool_internal(PyObject *data, char value) {
 }
 
 static PyObject*
-write_bool(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", "value", NULL};
-    PyObject *data = NULL;
-    PyObject *value = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
+write_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", "value", 0};
+    static CPyArg_Parser parser = {"OO:write_bool", kwlist, 0};
+    PyObject *data;
+    PyObject *value;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
         return NULL;
+    }
     if (unlikely(!PyBool_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a bool");
         return NULL;
@@ -280,11 +284,13 @@ read_str_internal(PyObject *data) {
 }
 
 static PyObject*
-read_str(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", NULL};
-    PyObject *data = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
+read_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", 0};
+    static CPyArg_Parser parser = {"O:read_str", kwlist, 0};
+    PyObject *data;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
         return NULL;
+    }
     return read_str_internal(data);
 }
 
@@ -319,12 +325,14 @@ write_str_internal(PyObject *data, PyObject *value) {
 }
 
 static PyObject*
-write_str(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", "value", NULL};
-    PyObject *data = NULL;
-    PyObject *value = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
+write_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", "value", 0};
+    static CPyArg_Parser parser = {"OO:write_str", kwlist, 0};
+    PyObject *data;
+    PyObject *value;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
         return NULL;
+    }
     if (unlikely(!PyUnicode_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a str");
         return NULL;
@@ -350,11 +358,13 @@ read_float_internal(PyObject *data) {
 }
 
 static PyObject*
-read_float(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", NULL};
-    PyObject *data = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
+read_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", 0};
+    static CPyArg_Parser parser = {"O:read_float", kwlist, 0};
+    PyObject *data;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
         return NULL;
+    }
     double retval = read_float_internal(data);
     if (unlikely(retval == CPY_FLOAT_ERROR && PyErr_Occurred())) {
         return NULL;
@@ -372,12 +382,14 @@ write_float_internal(PyObject *data, double value) {
 }
 
 static PyObject*
-write_float(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", "value", NULL};
-    PyObject *data = NULL;
-    PyObject *value = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
+write_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", "value", 0};
+    static CPyArg_Parser parser = {"OO:write_float", kwlist, 0};
+    PyObject *data;
+    PyObject *value;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
         return NULL;
+    }
     if (unlikely(!PyFloat_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be a float");
         return NULL;
@@ -421,11 +433,13 @@ read_int_internal(PyObject *data) {
 }
 
 static PyObject*
-read_int(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", NULL};
-    PyObject *data = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
+read_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", 0};
+    static CPyArg_Parser parser = {"O:read_int", kwlist, 0};
+    PyObject *data;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
         return NULL;
+    }
     CPyTagged retval = read_int_internal(data);
     if (unlikely(retval == CPY_INT_TAG)) {
         return NULL;
@@ -466,12 +480,14 @@ write_int_internal(PyObject *data, CPyTagged value) {
 }
 
 static PyObject*
-write_int(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", "value", NULL};
-    PyObject *data = NULL;
-    PyObject *value = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
+write_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", "value", 0};
+    static CPyArg_Parser parser = {"OO:write_int", kwlist, 0};
+    PyObject *data;
+    PyObject *value;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
         return NULL;
+    }
     if (unlikely(!PyLong_Check(value))) {
         PyErr_SetString(PyExc_TypeError, "value must be an int");
         return NULL;
@@ -498,11 +514,13 @@ read_tag_internal(PyObject *data) {
 }
 
 static PyObject*
-read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", NULL};
-    PyObject *data = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
+read_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", 0};
+    static CPyArg_Parser parser = {"O:read_tag", kwlist, 0};
+    PyObject *data;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
         return NULL;
+    }
     uint8_t retval = read_tag_internal(data);
     if (unlikely(retval == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
         return NULL;
@@ -520,12 +538,14 @@ write_tag_internal(PyObject *data, uint8_t value) {
 }
 
 static PyObject*
-write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
-    static char *kwlist[] = {"data", "value", NULL};
-    PyObject *data = NULL;
-    PyObject *value = NULL;
-    if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
+write_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
+    static const char * const kwlist[] = {"data", "value", 0};
+    static CPyArg_Parser parser = {"OO:write_tag", kwlist, 0};
+    PyObject *data;
+    PyObject *value;
+    if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
         return NULL;
+    }
     uint8_t unboxed = CPyLong_AsUInt8(value);
     if (unlikely(unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
         CPy_TypeError("u8", value);
@@ -539,17 +559,16 @@ write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
 }
 
 static PyMethodDef native_internal_module_methods[] = {
-    // TODO: switch public wrappers to METH_FASTCALL.
-    {"write_bool", (PyCFunction)write_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a bool")},
-    {"read_bool", (PyCFunction)read_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a bool")},
-    {"write_str", (PyCFunction)write_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a string")},
-    {"read_str", (PyCFunction)read_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a string")},
-    {"write_float", (PyCFunction)write_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a float")},
-    {"read_float", (PyCFunction)read_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a float")},
-    {"write_int", (PyCFunction)write_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write an int")},
-    {"read_int", (PyCFunction)read_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read an int")},
-    {"write_tag", (PyCFunction)write_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a short int")},
-    {"read_tag", (PyCFunction)read_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a short int")},
+    {"write_bool", (PyCFunction)write_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a bool")},
+    {"read_bool", (PyCFunction)read_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a bool")},
+    {"write_str", (PyCFunction)write_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a string")},
+    {"read_str", (PyCFunction)read_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a string")},
+    {"write_float", (PyCFunction)write_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a float")},
+    {"read_float", (PyCFunction)read_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a float")},
+    {"write_int", (PyCFunction)write_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write an int")},
+    {"read_int", (PyCFunction)read_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read an int")},
+    {"write_tag", (PyCFunction)write_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a short int")},
+    {"read_tag", (PyCFunction)read_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a short int")},
     {NULL, NULL, 0, NULL}
 };
 
diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py
index 5b7a2919c0fd..3a5976cf88b2 100644
--- a/mypyc/lib-rt/setup.py
+++ b/mypyc/lib-rt/setup.py
@@ -78,7 +78,14 @@ def run(self) -> None:
         ext_modules=[
             Extension(
                 "native_internal",
-                ["native_internal.c", "init.c", "int_ops.c", "exc_ops.c", "pythonsupport.c"],
+                [
+                    "native_internal.c",
+                    "init.c",
+                    "int_ops.c",
+                    "exc_ops.c",
+                    "pythonsupport.c",
+                    "getargsfast.c",
+                ],
                 include_dirs=["."],
             )
         ],

From 87f4dc8e7021a420bc561d491ff756d421a938ab Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sun, 31 Aug 2025 23:36:00 +0100
Subject: [PATCH 1601/1617] Mypy micro-optimizations (batch 1/3) (#19768)

Several mypy micro-optimizations. Together with batches 2 and 3 these
improve self check performance by 1.8%.
---
 mypy/applytype.py          |  2 +-
 mypy/binder.py             |  8 +++++---
 mypy/plugins/common.py     |  2 +-
 mypy/semanal.py            |  2 +-
 mypy/semanal_namedtuple.py |  2 +-
 mypy/server/aststrip.py    |  2 +-
 mypy/type_visitor.py       | 15 +++++++++------
 mypy/typeanal.py           |  6 +++---
 mypy/types.py              | 12 +++++++-----
 9 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/mypy/applytype.py b/mypy/applytype.py
index e87bf939c81a..dfeaf7752d21 100644
--- a/mypy/applytype.py
+++ b/mypy/applytype.py
@@ -243,7 +243,7 @@ def visit_callable_type(self, t: CallableType) -> Type:
         self.bound_tvars -= set(found_vars)
 
         assert isinstance(result, ProperType) and isinstance(result, CallableType)
-        result.variables = list(result.variables) + found_vars
+        result.variables = result.variables + tuple(found_vars)
         return result
 
     def visit_type_var(self, t: TypeVarType) -> Type:
diff --git a/mypy/binder.py b/mypy/binder.py
index c95481329a57..a83e65276ff4 100644
--- a/mypy/binder.py
+++ b/mypy/binder.py
@@ -250,10 +250,12 @@ def update_from_options(self, frames: list[Frame]) -> bool:
         options are the same.
         """
         all_reachable = all(not f.unreachable for f in frames)
-        frames = [f for f in frames if not f.unreachable]
+        if not all_reachable:
+            frames = [f for f in frames if not f.unreachable]
         changed = False
-        keys = {key for f in frames for key in f.types}
-
+        keys = [key for f in frames for key in f.types]
+        if len(keys) > 1:
+            keys = list(set(keys))
         for key in keys:
             current_value = self._get(key)
             resulting_values = [f.types.get(key, current_value) for f in frames]
diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py
index ac00171a037c..ed2a91d102f4 100644
--- a/mypy/plugins/common.py
+++ b/mypy/plugins/common.py
@@ -360,7 +360,7 @@ def _add_method_by_spec(
 
     signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type)
     if tvar_defs:
-        signature.variables = tvar_defs
+        signature.variables = tuple(tvar_defs)
 
     func = FuncDef(name, args, Block([PassStmt()]))
     func.info = info
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 213c8d04122d..50ee3b532463 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -995,7 +995,7 @@ def analyze_func_def(self, defn: FuncDef) -> None:
                     if has_self_type and self.type is not None:
                         info = self.type
                         if info.self_type is not None:
-                            result.variables = [info.self_type] + list(result.variables)
+                            result.variables = (info.self_type,) + result.variables
                 defn.type = result
                 self.add_type_alias_deps(analyzer.aliases_used)
                 self.check_function_signature(defn)
diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py
index b67747d16887..37a650f1b664 100644
--- a/mypy/semanal_namedtuple.py
+++ b/mypy/semanal_namedtuple.py
@@ -606,7 +606,7 @@ def add_method(
             arg_kinds = [arg.kind for arg in args]
             assert None not in types
             signature = CallableType(cast(list[Type], types), arg_kinds, items, ret, function_type)
-            signature.variables = [self_type]
+            signature.variables = (self_type,)
             func = FuncDef(funcname, args, Block([]))
             func.info = info
             func.is_class = is_classmethod
diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py
index a70dfc30deb5..27c1c4a0eedb 100644
--- a/mypy/server/aststrip.py
+++ b/mypy/server/aststrip.py
@@ -165,7 +165,7 @@ def visit_func_def(self, node: FuncDef) -> None:
             # in order to get the state exactly as it was before semantic analysis.
             # See also #4814.
             assert isinstance(node.type, CallableType)
-            node.type.variables = []
+            node.type.variables = ()
         with self.enter_method(node.info) if node.info else nullcontext():
             super().visit_func_def(node)
 
diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py
index ab1ec8b46fdd..65051ddbab67 100644
--- a/mypy/type_visitor.py
+++ b/mypy/type_visitor.py
@@ -228,7 +228,7 @@ def visit_instance(self, t: Instance, /) -> Type:
             last_known_value = raw_last_known_value
         return Instance(
             typ=t.type,
-            args=self.translate_types(t.args),
+            args=self.translate_type_tuple(t.args),
             line=t.line,
             column=t.column,
             last_known_value=last_known_value,
@@ -242,7 +242,7 @@ def visit_param_spec(self, t: ParamSpecType, /) -> Type:
         return t
 
     def visit_parameters(self, t: Parameters, /) -> Type:
-        return t.copy_modified(arg_types=self.translate_types(t.arg_types))
+        return t.copy_modified(arg_types=self.translate_type_list(t.arg_types))
 
     def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> Type:
         return t
@@ -255,14 +255,14 @@ def visit_unpack_type(self, t: UnpackType, /) -> Type:
 
     def visit_callable_type(self, t: CallableType, /) -> Type:
         return t.copy_modified(
-            arg_types=self.translate_types(t.arg_types),
+            arg_types=self.translate_type_list(t.arg_types),
             ret_type=t.ret_type.accept(self),
             variables=self.translate_variables(t.variables),
         )
 
     def visit_tuple_type(self, t: TupleType, /) -> Type:
         return TupleType(
-            self.translate_types(t.items),
+            self.translate_type_list(t.items),
             # TODO: This appears to be unsafe.
             cast(Any, t.partial_fallback.accept(self)),
             t.line,
@@ -299,7 +299,7 @@ def visit_union_type(self, t: UnionType, /) -> Type:
             return cached
 
         result = UnionType(
-            self.translate_types(t.items),
+            self.translate_type_list(t.items),
             t.line,
             t.column,
             uses_pep604_syntax=t.uses_pep604_syntax,
@@ -308,9 +308,12 @@ def visit_union_type(self, t: UnionType, /) -> Type:
             self.set_cached(t, result)
         return result
 
-    def translate_types(self, types: Iterable[Type]) -> list[Type]:
+    def translate_type_list(self, types: list[Type]) -> list[Type]:
         return [t.accept(self) for t in types]
 
+    def translate_type_tuple(self, types: tuple[Type, ...]) -> tuple[Type, ...]:
+        return tuple(t.accept(self) for t in types)
+
     def translate_variables(
         self, variables: Sequence[TypeVarLikeType]
     ) -> Sequence[TypeVarLikeType]:
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index d44b13880cbb..af70c52180aa 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -1820,7 +1820,7 @@ def infer_type_variables(
 
     def bind_function_type_variables(
         self, fun_type: CallableType, defn: Context
-    ) -> tuple[Sequence[TypeVarLikeType], bool]:
+    ) -> tuple[tuple[TypeVarLikeType, ...], bool]:
         """Find the type variables of the function type and bind them in our tvar_scope"""
         has_self_type = False
         if fun_type.variables:
@@ -1835,7 +1835,7 @@ def bind_function_type_variables(
                 assert isinstance(var_expr, TypeVarLikeExpr)
                 binding = self.tvar_scope.bind_new(var.name, var_expr)
                 defs.append(binding)
-            return defs, has_self_type
+            return tuple(defs), has_self_type
         typevars, has_self_type = self.infer_type_variables(fun_type)
         # Do not define a new type variable if already defined in scope.
         typevars = [
@@ -1849,7 +1849,7 @@ def bind_function_type_variables(
             binding = self.tvar_scope.bind_new(name, tvar)
             defs.append(binding)
 
-        return defs, has_self_type
+        return tuple(defs), has_self_type
 
     def is_defined_type_var(self, tvar: str, context: Context) -> bool:
         tvar_node = self.lookup_qualified(tvar, context)
diff --git a/mypy/types.py b/mypy/types.py
index e0265e601e0c..388a1906285e 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -2104,14 +2104,12 @@ def __init__(
     ) -> None:
         super().__init__(line, column)
         assert len(arg_types) == len(arg_kinds) == len(arg_names)
-        for t, k in zip(arg_types, arg_kinds):
+        self.arg_types = list(arg_types)
+        for t in self.arg_types:
             if isinstance(t, ParamSpecType):
                 assert not t.prefix.arg_types
                 # TODO: should we assert that only ARG_STAR contain ParamSpecType?
                 # See testParamSpecJoin, that relies on passing e.g `P.args` as plain argument.
-        if variables is None:
-            variables = []
-        self.arg_types = list(arg_types)
         self.arg_kinds = arg_kinds
         self.arg_names = list(arg_names)
         self.min_args = arg_kinds.count(ARG_POS)
@@ -2123,7 +2121,11 @@ def __init__(
         #   * If it is a non-decorated function, FuncDef is the definition
         #   * If it is a decorated function, enclosing Decorator is the definition
         self.definition = definition
-        self.variables = variables
+        self.variables: tuple[TypeVarLikeType, ...]
+        if variables is None:
+            self.variables = ()
+        else:
+            self.variables = tuple(variables)
         self.is_ellipsis_args = is_ellipsis_args
         self.implicit = implicit
         self.special_sig = special_sig

From a009879d540bf38bc46803f6043f564c782a0531 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Sun, 31 Aug 2025 23:37:08 +0100
Subject: [PATCH 1602/1617] Mypy micro-optimizations (batch 2/3) (#19769)

Several mypy micro-optimizations. Together with batches 1 and 3 these
improve self check performance by 1.8%.
---
 mypy/argmap.py     |  4 +++-
 mypy/checkexpr.py  |  8 +++-----
 mypy/expandtype.py |  4 +++-
 mypy/messages.py   | 31 +++++++++++++++----------------
 mypy/types.py      |  4 ++--
 5 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/mypy/argmap.py b/mypy/argmap.py
index 28fad1f093dd..a3e8f7fc8c2e 100644
--- a/mypy/argmap.py
+++ b/mypy/argmap.py
@@ -167,7 +167,7 @@ def __init__(self, context: ArgumentInferContext) -> None:
         # Next tuple *args index to use.
         self.tuple_index = 0
         # Keyword arguments in TypedDict **kwargs used.
-        self.kwargs_used: set[str] = set()
+        self.kwargs_used: set[str] | None = None
         # Type context for `*` and `**` arg kinds.
         self.context = context
 
@@ -241,6 +241,8 @@ def expand_actual_type(
             from mypy.subtypes import is_subtype
 
             if isinstance(actual_type, TypedDictType):
+                if self.kwargs_used is None:
+                    self.kwargs_used = set()
                 if formal_kind != nodes.ARG_STAR2 and formal_name in actual_type.items:
                     # Lookup type based on keyword argument name.
                     assert formal_name is not None
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 794af875867a..250decd7567e 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -248,12 +248,11 @@ def allow_fast_container_literal(t: Type) -> bool:
     )
 
 
-def extract_refexpr_names(expr: RefExpr) -> set[str]:
+def extract_refexpr_names(expr: RefExpr, output: set[str]) -> None:
     """Recursively extracts all module references from a reference expression.
 
     Note that currently, the only two subclasses of RefExpr are NameExpr and
     MemberExpr."""
-    output: set[str] = set()
     while isinstance(expr.node, MypyFile) or expr.fullname:
         if isinstance(expr.node, MypyFile) and expr.fullname:
             # If it's None, something's wrong (perhaps due to an
@@ -277,7 +276,6 @@ def extract_refexpr_names(expr: RefExpr) -> set[str]:
                 break
         else:
             raise AssertionError(f"Unknown RefExpr subclass: {type(expr)}")
-    return output
 
 
 class Finished(Exception):
@@ -372,7 +370,7 @@ def visit_name_expr(self, e: NameExpr) -> Type:
 
         It can be of any kind: local, member or global.
         """
-        self.chk.module_refs.update(extract_refexpr_names(e))
+        extract_refexpr_names(e, self.chk.module_refs)
         result = self.analyze_ref_expr(e)
         narrowed = self.narrow_type_from_binder(e, result)
         self.chk.check_deprecated(e.node, e)
@@ -3345,7 +3343,7 @@ def check_union_call(
 
     def visit_member_expr(self, e: MemberExpr, is_lvalue: bool = False) -> Type:
         """Visit member expression (of form e.id)."""
-        self.chk.module_refs.update(extract_refexpr_names(e))
+        extract_refexpr_names(e, self.chk.module_refs)
         result = self.analyze_ordinary_member_access(e, is_lvalue)
         narrowed = self.narrow_type_from_binder(e, result)
         self.chk.warn_deprecated(e.node, e)
diff --git a/mypy/expandtype.py b/mypy/expandtype.py
index 8433708eda44..e2a42317141f 100644
--- a/mypy/expandtype.py
+++ b/mypy/expandtype.py
@@ -182,7 +182,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator):
     def __init__(self, variables: Mapping[TypeVarId, Type]) -> None:
         super().__init__()
         self.variables = variables
-        self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {}
+        self.recursive_tvar_guard: dict[TypeVarId, Type | None] | None = None
 
     def visit_unbound_type(self, t: UnboundType) -> Type:
         return t
@@ -246,6 +246,8 @@ def visit_type_var(self, t: TypeVarType) -> Type:
             # If I try to remove this special-casing ~40 tests fail on reveal_type().
             return repl.copy_modified(last_known_value=None)
         if isinstance(repl, TypeVarType) and repl.has_default():
+            if self.recursive_tvar_guard is None:
+                self.recursive_tvar_guard = {}
             if (tvar_id := repl.id) in self.recursive_tvar_guard:
                 return self.recursive_tvar_guard[tvar_id] or repl
             self.recursive_tvar_guard[tvar_id] = None
diff --git a/mypy/messages.py b/mypy/messages.py
index 571cebb1b174..6329cad687f6 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -649,26 +649,11 @@ def incompatible_argument(
             else:
                 base = extract_type(name)
 
-            for method, op in op_methods_to_symbols.items():
-                for variant in method, "__r" + method[2:]:
-                    # FIX: do not rely on textual formatting
-                    if name.startswith(f'"{variant}" of'):
-                        if op == "in" or variant != method:
-                            # Reversed order of base/argument.
-                            return self.unsupported_operand_types(
-                                op, arg_type, base, context, code=codes.OPERATOR
-                            )
-                        else:
-                            return self.unsupported_operand_types(
-                                op, base, arg_type, context, code=codes.OPERATOR
-                            )
-
             if name.startswith('"__getitem__" of'):
                 return self.invalid_index_type(
                     arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX
                 )
-
-            if name.startswith('"__setitem__" of'):
+            elif name.startswith('"__setitem__" of'):
                 if n == 1:
                     return self.invalid_index_type(
                         arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX
@@ -684,6 +669,20 @@ def incompatible_argument(
                         message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT.with_additional_msg(info)
                     )
                     return self.fail(error_msg.value, context, code=error_msg.code)
+            elif name.startswith('"__'):
+                for method, op in op_methods_to_symbols.items():
+                    for variant in method, "__r" + method[2:]:
+                        # FIX: do not rely on textual formatting
+                        if name.startswith(f'"{variant}" of'):
+                            if op == "in" or variant != method:
+                                # Reversed order of base/argument.
+                                return self.unsupported_operand_types(
+                                    op, arg_type, base, context, code=codes.OPERATOR
+                                )
+                            else:
+                                return self.unsupported_operand_types(
+                                    op, base, arg_type, context, code=codes.OPERATOR
+                                )
 
             target = f"to {name} "
 
diff --git a/mypy/types.py b/mypy/types.py
index 388a1906285e..c23997d069d4 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -543,7 +543,7 @@ class TypeVarId:
     # function type variables.
 
     # Metavariables are allocated unique ids starting from 1.
-    raw_id: int
+    raw_id: Final[int]
 
     # Level of the variable in type inference. Currently either 0 for
     # declared types, or 1 for type inference metavariables.
@@ -586,7 +586,7 @@ def __ne__(self, other: object) -> bool:
         return not (self == other)
 
     def __hash__(self) -> int:
-        return hash((self.raw_id, self.meta_level, self.namespace))
+        return self.raw_id ^ (self.meta_level << 8) ^ hash(self.namespace)
 
     def is_meta_var(self) -> bool:
         return self.meta_level > 0

From 70d0521a8a61ceaa07a2dadcd916829fde8072b1 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Sun, 31 Aug 2025 16:44:44 -0700
Subject: [PATCH 1603/1617] Update stubinfo for latest typeshed (#19771)

---
 misc/update-stubinfo.py   |  2 +-
 mypy/stubinfo.py          | 39 ++++++++++++++++++++++++++++-----------
 mypy/test/teststubinfo.py |  3 ++-
 3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/misc/update-stubinfo.py b/misc/update-stubinfo.py
index beaed34a8a47..4a5b9a40c408 100644
--- a/misc/update-stubinfo.py
+++ b/misc/update-stubinfo.py
@@ -58,7 +58,7 @@ def main() -> None:
     print("Consider removing the following packages no longer in typeshed:")
     print("=" * 40)
     for p in sorted(mypy_p - typeshed_p_to_d.keys()):
-        if p in {"lxml", "pandas"}:  # never in typeshed
+        if p in {"lxml", "pandas", "scipy"}:  # never in typeshed
             continue
         print(p)
 
diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py
index 33064c9d3067..42e53ba21c84 100644
--- a/mypy/stubinfo.py
+++ b/mypy/stubinfo.py
@@ -45,7 +45,6 @@ def stub_distribution_name(module: str) -> str | None:
     "first": "types-first",
     "markdown": "types-Markdown",
     "mock": "types-mock",
-    "OpenSSL": "types-pyOpenSSL",
     "paramiko": "types-paramiko",
     "polib": "types-polib",
     "pycurl": "types-pycurl",
@@ -75,35 +74,37 @@ def stub_distribution_name(module: str) -> str | None:
 #               but is a non-typeshed stubs package.
 non_bundled_packages_flat: dict[str, str] = {
     "_cffi_backend": "types-cffi",
+    "_jsonnet": "types-jsonnet",
     "_win32typing": "types-pywin32",
     "antlr4": "types-antlr4-python3-runtime",
     "assertpy": "types-assertpy",
-    "atheris": "types-atheris",
+    "auth0": "types-auth0-python",
     "authlib": "types-Authlib",
     "aws_xray_sdk": "types-aws-xray-sdk",
+    "binaryornot": "types-binaryornot",
     "boltons": "types-boltons",
     "braintree": "types-braintree",
-    "bs4": "types-beautifulsoup4",
     "bugbear": "types-flake8-bugbear",
-    "caldav": "types-caldav",
     "capturer": "types-capturer",
     "cffi": "types-cffi",
+    "channels": "types-channels",
     "chevron": "types-chevron",
     "click_default_group": "types-click-default-group",
     "click_log": "types-click-log",
+    "click_shell": "types-click-shell",
     "click_web": "types-click-web",
     "colorama": "types-colorama",
     "commctrl": "types-pywin32",
-    "commonmark": "types-commonmark",
     "consolemenu": "types-console-menu",
-    "corus": "types-corus",  # codespell:ignore corus
+    "convertdate": "types-convertdate",
     "cronlog": "types-python-crontab",
     "crontab": "types-python-crontab",
     "crontabs": "types-python-crontab",
-    "datemath": "types-python-datemath",
     "dateparser_data": "types-dateparser",
     "dde": "types-pywin32",
     "defusedxml": "types-defusedxml",
+    "dirhash": "types-dirhash",
+    "django_filters": "types-django-filter",
     "docker": "types-docker",
     "dockerfile_parse": "types-dockerfile-parse",
     "editdistance": "types-editdistance",
@@ -122,17 +123,22 @@ def stub_distribution_name(module: str) -> str | None:
     "flask_socketio": "types-Flask-SocketIO",
     "fpdf": "types-fpdf2",
     "gdb": "types-gdb",
+    "geopandas": "types-geopandas",
     "gevent": "types-gevent",
     "greenlet": "types-greenlet",
+    "grpc_channelz": "types-grpcio-channelz",
+    "grpc_health": "types-grpcio-health-checking",
+    "grpc_reflection": "types-grpcio-reflection",
+    "grpc_status": "types-grpcio-status",
+    "grpc": "types-grpcio",
     "hdbcli": "types-hdbcli",
+    "hnswlib": "types-hnswlib",
     "html5lib": "types-html5lib",
     "httplib2": "types-httplib2",
-    "humanfriendly": "types-humanfriendly",
     "hvac": "types-hvac",
     "ibm_db": "types-ibm-db",
     "icalendar": "types-icalendar",
     "import_export": "types-django-import-export",
-    "influxdb_client": "types-influxdb-client",
     "inifile": "types-inifile",
     "isapi": "types-pywin32",
     "jack": "types-JACK-Client",
@@ -145,9 +151,11 @@ def stub_distribution_name(module: str) -> str | None:
     "jwcrypto": "types-jwcrypto",
     "keyboard": "types-keyboard",
     "ldap3": "types-ldap3",
+    "lunardate": "types-lunardate",
     "lupa": "types-lupa",
     "lzstring": "types-lzstring",
     "m3u8": "types-m3u8",
+    "management": "types-django-import-export",
     "mmapfile": "types-pywin32",
     "mmsystem": "types-pywin32",
     "mypy_extensions": "types-mypy-extensions",
@@ -173,6 +181,7 @@ def stub_distribution_name(module: str) -> str | None:
     "perfmon": "types-pywin32",
     "pexpect": "types-pexpect",
     "playhouse": "types-peewee",
+    "pony": "types-pony",
     "portpicker": "types-portpicker",
     "psutil": "types-psutil",
     "psycopg2": "types-psycopg2",
@@ -181,11 +190,13 @@ def stub_distribution_name(module: str) -> str | None:
     "pyautogui": "types-PyAutoGUI",
     "pycocotools": "types-pycocotools",
     "pyflakes": "types-pyflakes",
-    "pygit2": "types-pygit2",
     "pygments": "types-Pygments",
     "pyi_splash": "types-pyinstaller",
     "PyInstaller": "types-pyinstaller",
+    "pyluach": "types-pyluach",
+    "pymeeus": "types-PyMeeus",
     "pynput": "types-pynput",
+    "pyperclip": "types-pyperclip",
     "pyscreeze": "types-PyScreeze",
     "pysftp": "types-pysftp",
     "pytest_lazyfixture": "types-pytest-lazy-fixture",
@@ -195,10 +206,12 @@ def stub_distribution_name(module: str) -> str | None:
     "pywintypes": "types-pywin32",
     "qrbill": "types-qrbill",
     "qrcode": "types-qrcode",
+    "ratelimit": "types-ratelimit",
     "regex": "types-regex",
     "regutil": "types-pywin32",
     "reportlab": "types-reportlab",
     "requests_oauthlib": "types-requests-oauthlib",
+    "rfc3339_validator": "types-rfc3339-validator",
     "RPi": "types-RPi.GPIO",
     "s2clientprotocol": "types-s2clientprotocol",
     "sass": "types-libsass",
@@ -210,6 +223,8 @@ def stub_distribution_name(module: str) -> str | None:
     "setuptools": "types-setuptools",
     "shapely": "types-shapely",
     "slumber": "types-slumber",
+    "socks": "types-PySocks",
+    "sockshandler": "types-PySocks",
     "sspicon": "types-pywin32",
     "str2bool": "types-str2bool",
     "tensorflow": "types-tensorflow",
@@ -218,7 +233,6 @@ def stub_distribution_name(module: str) -> str | None:
     "toposort": "types-toposort",
     "tqdm": "types-tqdm",
     "translationstring": "types-translationstring",
-    "tree_sitter_languages": "types-tree-sitter-languages",
     "ttkthemes": "types-ttkthemes",
     "unidiff": "types-unidiff",
     "untangle": "types-untangle",
@@ -226,6 +240,7 @@ def stub_distribution_name(module: str) -> str | None:
     "uwsgi": "types-uWSGI",
     "uwsgidecorators": "types-uWSGI",
     "vobject": "types-vobject",
+    "watchpoints": "types-watchpoints",
     "webob": "types-WebOb",
     "whatthepatch": "types-whatthepatch",
     "win2kras": "types-pywin32",
@@ -282,7 +297,9 @@ def stub_distribution_name(module: str) -> str | None:
     "xdg": "types-pyxdg",
     "xdgenvpy": "types-xdgenvpy",
     "Xlib": "types-python-xlib",
+    "xlrd": "types-xlrd",
     "xmltodict": "types-xmltodict",
+    "yt_dlp": "types-yt-dlp",
     "zstd": "types-zstd",
     "zxcvbn": "types-zxcvbn",
     # Stub packages that are not from typeshed
diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py
index e90c72335bf8..ae34e78f98c6 100644
--- a/mypy/test/teststubinfo.py
+++ b/mypy/test/teststubinfo.py
@@ -20,7 +20,8 @@ def test_is_legacy_bundled_packages(self) -> None:
     def test_stub_distribution_name(self) -> None:
         assert stub_distribution_name("foobar_asdf") is None
         assert stub_distribution_name("pycurl") == "types-pycurl"
-        assert stub_distribution_name("bs4") == "types-beautifulsoup4"
+        assert stub_distribution_name("psutil") == "types-psutil"
+        assert stub_distribution_name("sassutils") == "types-libsass"
         assert stub_distribution_name("google.cloud.ndb") == "types-google-cloud-ndb"
         assert stub_distribution_name("google.cloud.ndb.submodule") == "types-google-cloud-ndb"
         assert stub_distribution_name("google.cloud.unknown") is None

From a856e559756ea0e8b836e94d226a3cecf2cb9166 Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Mon, 1 Sep 2025 01:19:19 +0100
Subject: [PATCH 1604/1617] Use empty context as fallback for return statements
 (#19767)

Fixes https://github.com/python/mypy/issues/16924
Fixes https://github.com/python/mypy/issues/15886

Mypy uses external type context first, this can cause bad type inference
in return statements (see example in test case added), usually we
recommend a workaround to users like replacing:
```python
    return foo(x)
```
with
```python
    y = foo(x)
    return y
```
But this is a bit ugly, and more importantly we can essentially
automatically try this workaround. This is what this PR adds. I checked
performance impact, and don't see any (but for some reason noise level
on my desktop is much higher now).
---
 mypy/checker.py                             | 53 +++++++++++++++++++--
 mypy/errors.py                              |  3 +-
 test-data/unit/check-inference-context.test | 42 ++++++++++++++++
 test-data/unit/pythoneval.test              | 16 +++++++
 4 files changed, 109 insertions(+), 5 deletions(-)

diff --git a/mypy/checker.py b/mypy/checker.py
index ae6ae591ed8c..12a86fe6fba1 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -90,6 +90,7 @@
     ContinueStmt,
     Decorator,
     DelStmt,
+    DictExpr,
     EllipsisExpr,
     Expression,
     ExpressionStmt,
@@ -124,6 +125,7 @@
     RaiseStmt,
     RefExpr,
     ReturnStmt,
+    SetExpr,
     StarExpr,
     Statement,
     StrExpr,
@@ -4859,6 +4861,42 @@ def visit_return_stmt(self, s: ReturnStmt) -> None:
         self.check_return_stmt(s)
         self.binder.unreachable()
 
+    def infer_context_dependent(
+        self, expr: Expression, type_ctx: Type, allow_none_func_call: bool
+    ) -> ProperType:
+        """Infer type of an expression with fallback to empty type context."""
+        with self.msg.filter_errors(
+            filter_errors=True, filter_deprecated=True, save_filtered_errors=True
+        ) as msg:
+            with self.local_type_map as type_map:
+                typ = get_proper_type(
+                    self.expr_checker.accept(
+                        expr, type_ctx, allow_none_return=allow_none_func_call
+                    )
+                )
+        if not msg.has_new_errors():
+            self.store_types(type_map)
+            return typ
+
+        # If there are errors with the original type context, try re-inferring in empty context.
+        original_messages = msg.filtered_errors()
+        original_type_map = type_map
+        with self.msg.filter_errors(
+            filter_errors=True, filter_deprecated=True, save_filtered_errors=True
+        ) as msg:
+            with self.local_type_map as type_map:
+                alt_typ = get_proper_type(
+                    self.expr_checker.accept(expr, None, allow_none_return=allow_none_func_call)
+                )
+        if not msg.has_new_errors() and is_subtype(alt_typ, type_ctx):
+            self.store_types(type_map)
+            return alt_typ
+
+        # If empty fallback didn't work, use results from the original type context.
+        self.msg.add_errors(original_messages)
+        self.store_types(original_type_map)
+        return typ
+
     def check_return_stmt(self, s: ReturnStmt) -> None:
         defn = self.scope.current_function()
         if defn is not None:
@@ -4891,11 +4929,18 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
                 allow_none_func_call = is_lambda or declared_none_return or declared_any_return
 
                 # Return with a value.
-                typ = get_proper_type(
-                    self.expr_checker.accept(
-                        s.expr, return_type, allow_none_return=allow_none_func_call
+                if isinstance(s.expr, (CallExpr, ListExpr, TupleExpr, DictExpr, SetExpr, OpExpr)):
+                    # For expressions that (strongly) depend on type context (i.e. those that
+                    # are handled like a function call), we allow fallback to empty type context
+                    # in case of errors, this improves user experience in some cases,
+                    # see e.g. testReturnFallbackInference.
+                    typ = self.infer_context_dependent(s.expr, return_type, allow_none_func_call)
+                else:
+                    typ = get_proper_type(
+                        self.expr_checker.accept(
+                            s.expr, return_type, allow_none_return=allow_none_func_call
+                        )
                     )
-                )
                 # Treat NotImplemented as having type Any, consistent with its
                 # definition in typeshed prior to python/typeshed#4222.
                 if (
diff --git a/mypy/errors.py b/mypy/errors.py
index d75c1c62a1ed..f1b2faf67401 100644
--- a/mypy/errors.py
+++ b/mypy/errors.py
@@ -206,7 +206,8 @@ def on_error(self, file: str, info: ErrorInfo) -> bool:
         """
         if info.code == codes.DEPRECATED:
             # Deprecated is not a type error, so it is handled on opt-in basis here.
-            return self._filter_deprecated
+            if not self._filter_deprecated:
+                return False
 
         self._has_new_errors = True
         if isinstance(self._filter, bool):
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
index cd44fb5b85cd..7dbbd68c4215 100644
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -1540,3 +1540,45 @@ def f(x: dict[str, Union[str, None, int]]) -> None:
 def g(x: Optional[dict[str, Any]], s: Optional[str]) -> None:
     f(x or {'x': s})
 [builtins fixtures/dict.pyi]
+
+[case testReturnFallbackInferenceTuple]
+from typing import TypeVar, Union
+
+T = TypeVar("T")
+def foo(x: list[T]) -> tuple[T, ...]: ...
+
+def bar(x: list[int]) -> tuple[Union[str, int], ...]:
+    return foo(x)
+
+def bar2(x: list[int]) -> tuple[Union[str, int], ...]:
+    y = foo(x)
+    return y
+[builtins fixtures/tuple.pyi]
+
+[case testReturnFallbackInferenceUnion]
+from typing import Generic, TypeVar, Union
+
+T = TypeVar("T")
+
+class Cls(Generic[T]):
+    pass
+
+def inner(c: Cls[T]) -> Union[T, int]:
+    return 1
+
+def outer(c: Cls[T]) -> Union[T, int]:
+    return inner(c)
+
+[case testReturnFallbackInferenceAsync]
+from typing import Generic, TypeVar, Optional
+
+T = TypeVar("T")
+
+class Cls(Generic[T]):
+    pass
+
+async def inner(c: Cls[T]) -> Optional[T]:
+    return None
+
+async def outer(c: Cls[T]) -> Optional[T]:
+    return await inner(c)
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 9b5d8a1ac54c..2910e59b9173 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -2188,3 +2188,19 @@ reveal_type([*map(str, x)])
 [out]
 _testUnpackIteratorBuiltins.py:4: note: Revealed type is "builtins.list[builtins.int]"
 _testUnpackIteratorBuiltins.py:5: note: Revealed type is "builtins.list[builtins.str]"
+
+[case testReturnFallbackInferenceDict]
+# Requires full dict stubs.
+from typing import Dict, Mapping, TypeVar, Union
+
+K = TypeVar("K")
+V = TypeVar("V")
+K2 = TypeVar("K2")
+V2 = TypeVar("V2")
+
+def func(one: Dict[K, V], two: Mapping[K2, V2]) -> Dict[Union[K, K2], Union[V, V2]]:
+    ...
+
+def caller(arg1: Mapping[K, V], arg2: Mapping[K2, V2]) -> Dict[Union[K, K2], Union[V, V2]]:
+    _arg1 = arg1 if isinstance(arg1, dict) else dict(arg1)
+    return func(_arg1, arg2)

From b1b8b0ccb1adeab7801bd41e3d688ecdeded6ed8 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Mon, 1 Sep 2025 05:26:36 +0200
Subject: [PATCH 1605/1617] Sync typeshed (#19772)

Source commit:

https://github.com/python/typeshed/commit/2480d7e7c74493a024eaf254c5d2c6f452c80ee2
---
 ...redundant-inheritances-from-Iterator.patch |  96 ++--
 mypy/typeshed/stdlib/_ast.pyi                 |  10 +-
 mypy/typeshed/stdlib/_asyncio.pyi             |  12 +-
 mypy/typeshed/stdlib/_blake2.pyi              | 133 +++--
 mypy/typeshed/stdlib/_collections_abc.pyi     |   1 +
 mypy/typeshed/stdlib/_compat_pickle.pyi       |  18 +-
 mypy/typeshed/stdlib/_csv.pyi                 |   7 +-
 mypy/typeshed/stdlib/_ctypes.pyi              |  22 +-
 mypy/typeshed/stdlib/_curses.pyi              | 540 +++++++++---------
 mypy/typeshed/stdlib/_curses_panel.pyi        |   6 +-
 mypy/typeshed/stdlib/_dbm.pyi                 |   6 +-
 mypy/typeshed/stdlib/_decimal.pyi             |  16 +-
 .../stdlib/_frozen_importlib_external.pyi     |  23 +-
 mypy/typeshed/stdlib/_hashlib.pyi             |   3 +-
 mypy/typeshed/stdlib/_interpreters.pyi        |  35 +-
 mypy/typeshed/stdlib/_io.pyi                  | 122 ++--
 mypy/typeshed/stdlib/_lsprof.pyi              |   2 +
 mypy/typeshed/stdlib/_lzma.pyi                |  12 +-
 mypy/typeshed/stdlib/_msi.pyi                 |  78 +--
 mypy/typeshed/stdlib/_multibytecodec.pyi      |   5 +
 mypy/typeshed/stdlib/_pickle.pyi              |   4 +-
 mypy/typeshed/stdlib/_queue.pyi               |   2 +
 mypy/typeshed/stdlib/_random.pyi              |  10 +-
 mypy/typeshed/stdlib/_socket.pyi              |   3 +-
 mypy/typeshed/stdlib/_ssl.pyi                 |   4 +-
 mypy/typeshed/stdlib/_struct.pyi              |   3 +-
 mypy/typeshed/stdlib/_thread.pyi              |   5 +-
 mypy/typeshed/stdlib/_threading_local.pyi     |   2 +
 mypy/typeshed/stdlib/_typeshed/__init__.pyi   |  15 +-
 mypy/typeshed/stdlib/_warnings.pyi            |  14 +-
 mypy/typeshed/stdlib/_winapi.pyi              |  28 +-
 mypy/typeshed/stdlib/_zstd.pyi                |  10 +-
 mypy/typeshed/stdlib/abc.pyi                  |   6 +-
 mypy/typeshed/stdlib/annotationlib.pyi        |  16 +-
 mypy/typeshed/stdlib/argparse.pyi             |   4 +-
 mypy/typeshed/stdlib/array.pyi                |   3 +-
 mypy/typeshed/stdlib/ast.pyi                  |  56 +-
 mypy/typeshed/stdlib/asyncio/base_events.pyi  |  26 +-
 mypy/typeshed/stdlib/asyncio/events.pyi       |  42 +-
 mypy/typeshed/stdlib/asyncio/graph.pyi        |  36 +-
 mypy/typeshed/stdlib/asyncio/protocols.pyi    |   6 +
 mypy/typeshed/stdlib/asyncio/runners.pyi      |   2 +-
 mypy/typeshed/stdlib/asyncio/streams.pyi      |   8 +-
 mypy/typeshed/stdlib/asyncio/subprocess.pyi   |  12 +-
 mypy/typeshed/stdlib/asyncio/tasks.pyi        |   8 +-
 mypy/typeshed/stdlib/asyncio/transports.pyi   |   9 +-
 mypy/typeshed/stdlib/asyncio/trsock.pyi       |  30 +-
 mypy/typeshed/stdlib/asyncio/unix_events.pyi  |  12 +-
 .../stdlib/asyncio/windows_events.pyi         |   2 +-
 .../typeshed/stdlib/asyncio/windows_utils.pyi |  10 +-
 mypy/typeshed/stdlib/binascii.pyi             |   4 +-
 mypy/typeshed/stdlib/builtins.pyi             |  52 +-
 mypy/typeshed/stdlib/calendar.pyi             |  38 +-
 mypy/typeshed/stdlib/cgi.pyi                  |   9 +-
 mypy/typeshed/stdlib/codecs.pyi               |  88 ++-
 mypy/typeshed/stdlib/collections/__init__.pyi |  17 +-
 mypy/typeshed/stdlib/colorsys.pyi             |   8 +-
 .../stdlib/compression/zstd/__init__.pyi      |   1 +
 .../stdlib/concurrent/futures/_base.pyi       |   3 +-
 .../stdlib/concurrent/futures/process.pyi     |   6 +-
 .../concurrent/interpreters/_crossinterp.pyi  |   1 +
 .../concurrent/interpreters/_queues.pyi       |   4 +-
 mypy/typeshed/stdlib/configparser.pyi         |   7 +-
 mypy/typeshed/stdlib/contextlib.pyi           |   2 +
 mypy/typeshed/stdlib/crypt.pyi                |   8 +-
 mypy/typeshed/stdlib/ctypes/__init__.pyi      |  14 +-
 mypy/typeshed/stdlib/ctypes/_endian.pyi       |   8 +-
 .../stdlib/ctypes/macholib/__init__.pyi       |   4 +-
 mypy/typeshed/stdlib/ctypes/wintypes.pyi      |   4 +-
 mypy/typeshed/stdlib/curses/__init__.pyi      |   8 +-
 mypy/typeshed/stdlib/curses/ascii.pyi         |  76 +--
 mypy/typeshed/stdlib/dataclasses.pyi          |  36 +-
 mypy/typeshed/stdlib/datetime.pyi             |   6 +-
 mypy/typeshed/stdlib/decimal.pyi              |   4 +-
 mypy/typeshed/stdlib/dis.pyi                  |  76 +--
 mypy/typeshed/stdlib/distutils/file_util.pyi  |   4 +-
 mypy/typeshed/stdlib/doctest.pyi              |  34 +-
 .../stdlib/email/_header_value_parser.pyi     |   2 +-
 mypy/typeshed/stdlib/email/charset.pyi        |  13 +-
 mypy/typeshed/stdlib/enum.pyi                 |  59 +-
 mypy/typeshed/stdlib/errno.pyi                | 405 ++++++-------
 mypy/typeshed/stdlib/filecmp.pyi              |   2 +-
 mypy/typeshed/stdlib/fractions.pyi            |   1 +
 mypy/typeshed/stdlib/functools.pyi            |  21 +-
 mypy/typeshed/stdlib/gettext.pyi              |  18 +-
 mypy/typeshed/stdlib/glob.pyi                 |   8 +-
 mypy/typeshed/stdlib/hmac.pyi                 |   1 +
 mypy/typeshed/stdlib/html/entities.pyi        |  10 +-
 mypy/typeshed/stdlib/http/client.pyi          | 138 ++---
 mypy/typeshed/stdlib/http/server.pyi          |  30 +-
 mypy/typeshed/stdlib/imp.pyi                  |  22 +-
 mypy/typeshed/stdlib/importlib/abc.pyi        |   4 +-
 .../stdlib/importlib/metadata/__init__.pyi    |  65 ++-
 mypy/typeshed/stdlib/inspect.pyi              |  88 ++-
 mypy/typeshed/stdlib/io.pyi                   |   2 +
 mypy/typeshed/stdlib/ipaddress.pyi            |   8 +-
 mypy/typeshed/stdlib/itertools.pyi            |  22 +-
 .../stdlib/lib2to3/fixes/fix_tuple_params.pyi |   3 +-
 mypy/typeshed/stdlib/logging/__init__.pyi     |   8 +-
 mypy/typeshed/stdlib/logging/config.pyi       |  27 +-
 mypy/typeshed/stdlib/logging/handlers.pyi     |  12 +-
 mypy/typeshed/stdlib/mmap.pyi                 |   5 +-
 mypy/typeshed/stdlib/msilib/__init__.pyi      |  28 +-
 mypy/typeshed/stdlib/msilib/schema.pyi        |   3 +-
 mypy/typeshed/stdlib/msilib/sequence.pyi      |  13 +-
 mypy/typeshed/stdlib/msilib/text.pyi          |   7 +-
 mypy/typeshed/stdlib/msvcrt.pyi               |   8 +-
 .../stdlib/multiprocessing/managers.pyi       |   1 +
 mypy/typeshed/stdlib/multiprocessing/util.pyi |   2 +-
 mypy/typeshed/stdlib/nturl2path.pyi           |   4 +-
 mypy/typeshed/stdlib/numbers.pyi              |   5 +
 mypy/typeshed/stdlib/opcode.pyi               |  32 +-
 mypy/typeshed/stdlib/os/__init__.pyi          | 373 ++++++------
 mypy/typeshed/stdlib/ossaudiodev.pyi          | 227 ++++----
 mypy/typeshed/stdlib/pathlib/__init__.pyi     |  48 +-
 mypy/typeshed/stdlib/pdb.pyi                  |   8 +-
 mypy/typeshed/stdlib/pickle.pyi               | 146 ++---
 mypy/typeshed/stdlib/pickletools.pyi          |  15 +-
 mypy/typeshed/stdlib/platform.pyi             |  24 +-
 mypy/typeshed/stdlib/plistlib.pyi             |   6 +-
 mypy/typeshed/stdlib/poplib.pyi               |   2 +-
 mypy/typeshed/stdlib/pydoc.pyi                |   6 +-
 mypy/typeshed/stdlib/pydoc_data/topics.pyi    |   4 +-
 mypy/typeshed/stdlib/random.pyi               |   5 +
 mypy/typeshed/stdlib/resource.pyi             |  41 +-
 mypy/typeshed/stdlib/select.pyi               | 136 ++---
 mypy/typeshed/stdlib/selectors.pyi            |   6 +-
 mypy/typeshed/stdlib/smtplib.pyi              |  14 +-
 mypy/typeshed/stdlib/socket.pyi               | 155 ++---
 mypy/typeshed/stdlib/sqlite3/__init__.pyi     |   5 +-
 mypy/typeshed/stdlib/sqlite3/dbapi2.pyi       |  17 +-
 mypy/typeshed/stdlib/sre_compile.pyi          |   4 +-
 mypy/typeshed/stdlib/sre_constants.pyi        |  15 +-
 mypy/typeshed/stdlib/sre_parse.pyi            |  30 +-
 mypy/typeshed/stdlib/ssl.pyi                  |   4 +-
 mypy/typeshed/stdlib/statistics.pyi           |   1 +
 mypy/typeshed/stdlib/string/__init__.pyi      |  20 +-
 mypy/typeshed/stdlib/stringprep.pyi           |  16 +-
 mypy/typeshed/stdlib/subprocess.pyi           | 126 ++--
 mypy/typeshed/stdlib/sunau.pyi                |  30 +-
 mypy/typeshed/stdlib/symbol.pyi               | 188 +++---
 mypy/typeshed/stdlib/symtable.pyi             |   7 +-
 mypy/typeshed/stdlib/sys/__init__.pyi         |   2 +-
 mypy/typeshed/stdlib/sys/_monitoring.pyi      |  10 +-
 mypy/typeshed/stdlib/tarfile.pyi              |  30 +-
 mypy/typeshed/stdlib/telnetlib.pyi            | 150 ++---
 mypy/typeshed/stdlib/tempfile.pyi             |   4 +-
 mypy/typeshed/stdlib/threading.pyi            |  20 +-
 mypy/typeshed/stdlib/time.pyi                 |  26 +-
 mypy/typeshed/stdlib/tkinter/__init__.pyi     |  49 +-
 mypy/typeshed/stdlib/tkinter/messagebox.pyi   |   6 +-
 mypy/typeshed/stdlib/tkinter/ttk.pyi          | 165 +++++-
 mypy/typeshed/stdlib/tokenize.pyi             |  89 +--
 mypy/typeshed/stdlib/tomllib.pyi              |   2 +-
 mypy/typeshed/stdlib/traceback.pyi            |  19 +-
 mypy/typeshed/stdlib/tracemalloc.pyi          |   5 +
 mypy/typeshed/stdlib/turtle.pyi               |  42 +-
 mypy/typeshed/stdlib/types.pyi                |  44 +-
 mypy/typeshed/stdlib/typing.pyi               |  56 +-
 mypy/typeshed/stdlib/typing_extensions.pyi    |  15 +-
 mypy/typeshed/stdlib/unicodedata.pyi          |   4 +-
 mypy/typeshed/stdlib/unittest/loader.pyi      |  53 +-
 mypy/typeshed/stdlib/unittest/main.pyi        |   7 +-
 mypy/typeshed/stdlib/unittest/mock.pyi        | 213 ++++---
 mypy/typeshed/stdlib/unittest/util.pyi        |  12 +-
 mypy/typeshed/stdlib/urllib/parse.pyi         |  28 +-
 mypy/typeshed/stdlib/uuid.pyi                 |   1 +
 mypy/typeshed/stdlib/venv/__init__.pyi        |   3 +-
 mypy/typeshed/stdlib/wave.pyi                 |  34 +-
 mypy/typeshed/stdlib/weakref.pyi              |   6 +-
 mypy/typeshed/stdlib/webbrowser.pyi           |  14 +-
 mypy/typeshed/stdlib/winreg.pyi               |  14 +-
 mypy/typeshed/stdlib/wsgiref/headers.pyi      |   4 +-
 .../typeshed/stdlib/wsgiref/simple_server.pyi |   8 +-
 mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi   |  34 +-
 mypy/typeshed/stdlib/xml/dom/__init__.pyi     |  33 +-
 mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi |  21 +-
 mypy/typeshed/stdlib/xml/dom/minicompat.pyi   |   2 +
 mypy/typeshed/stdlib/xml/dom/minidom.pyi      |  26 +
 mypy/typeshed/stdlib/xml/dom/pulldom.pyi      |   2 +-
 mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi   |  24 +-
 .../stdlib/xml/etree/ElementInclude.pyi       |   7 +-
 .../typeshed/stdlib/xml/etree/ElementPath.pyi |   6 +-
 .../typeshed/stdlib/xml/etree/ElementTree.pyi |  17 +-
 mypy/typeshed/stdlib/xml/sax/__init__.pyi     |   4 +-
 mypy/typeshed/stdlib/xml/sax/expatreader.pyi  |   4 +-
 mypy/typeshed/stdlib/xml/sax/handler.pyi      |  32 +-
 mypy/typeshed/stdlib/zipfile/__init__.pyi     |  31 +-
 mypy/typeshed/stdlib/zipfile/_path/glob.pyi   |   6 +-
 mypy/typeshed/stdlib/zoneinfo/__init__.pyi    |   3 +-
 test-data/unit/pythoneval.test                |   4 +-
 191 files changed, 3640 insertions(+), 2564 deletions(-)

diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
index d3f49a4eef3e..fdcc14cec3c6 100644
--- a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
+++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch
@@ -1,4 +1,4 @@
-From c217544146d36899d50e828d627652a0d8f63bb7 Mon Sep 17 00:00:00 2001
+From 438dbb1300b77331940d7db8f010e97305745116 Mon Sep 17 00:00:00 2001
 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
 Date: Sat, 21 Dec 2024 22:36:38 +0100
 Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
@@ -15,7 +15,7 @@ Subject: [PATCH] Revert Remove redundant inheritances from Iterator in
  7 files changed, 34 insertions(+), 34 deletions(-)
 
 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
-index ed56f33af..5253e967e 100644
+index d663f5d93..f43178e4d 100644
 --- a/mypy/typeshed/stdlib/_asyncio.pyi
 +++ b/mypy/typeshed/stdlib/_asyncio.pyi
 @@ -1,6 +1,6 @@
@@ -26,59 +26,59 @@ index ed56f33af..5253e967e 100644
  from contextvars import Context
  from types import FrameType, GenericAlias
  from typing import Any, Literal, TextIO, TypeVar
-@@ -10,7 +10,7 @@ _T = TypeVar("_T")
- _T_co = TypeVar("_T_co", covariant=True)
+@@ -11,7 +11,7 @@ _T_co = TypeVar("_T_co", covariant=True)
  _TaskYieldType: TypeAlias = Future[object] | None
  
+ @disjoint_base
 -class Future(Awaitable[_T]):
 +class Future(Awaitable[_T], Iterable[_T]):
      _state: str
      @property
      def _exception(self) -> BaseException | None: ...
 diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
-index 0575be3c8..d9be595fe 100644
+index f2dd00079..784ee7eac 100644
 --- a/mypy/typeshed/stdlib/builtins.pyi
 +++ b/mypy/typeshed/stdlib/builtins.pyi
-@@ -1186,7 +1186,7 @@ class frozenset(AbstractSet[_T_co]):
-     def __hash__(self) -> int: ...
+@@ -1209,7 +1209,7 @@ class frozenset(AbstractSet[_T_co]):
      def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
  
+ @disjoint_base
 -class enumerate(Generic[_T]):
 +class enumerate(Iterator[tuple[int, _T]]):
      def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> tuple[int, _T]: ...
-@@ -1380,7 +1380,7 @@ else:
- 
+@@ -1405,7 +1405,7 @@ else:
  exit: _sitebuiltins.Quitter
  
+ @disjoint_base
 -class filter(Generic[_T]):
 +class filter(Iterator[_T]):
      @overload
      def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ...
      @overload
-@@ -1444,7 +1444,7 @@ license: _sitebuiltins._Printer
+@@ -1469,7 +1469,7 @@ license: _sitebuiltins._Printer
  
  def locals() -> dict[str, Any]: ...
- 
+ @disjoint_base
 -class map(Generic[_S]):
 +class map(Iterator[_S]):
      # 3.14 adds `strict` argument.
      if sys.version_info >= (3, 14):
          @overload
-@@ -1750,7 +1750,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
- 
+@@ -1776,7 +1776,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
  quit: _sitebuiltins.Quitter
  
+ @disjoint_base
 -class reversed(Generic[_T]):
 +class reversed(Iterator[_T]):
      @overload
      def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ...  # type: ignore[misc]
      @overload
-@@ -1814,7 +1814,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
+@@ -1840,7 +1840,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
  @overload
  def vars(object: Any = ..., /) -> dict[str, Any]: ...
- 
+ @disjoint_base
 -class zip(Generic[_T_co]):
 +class zip(Iterator[_T_co]):
      if sys.version_info >= (3, 10):
@@ -131,97 +131,102 @@ index 910d63814..eb942bc55 100644
          # encoding and errors are added
          @overload
 diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi
-index d0085dd72..7d05b1318 100644
+index fe4ccbdf8..73745fe92 100644
 --- a/mypy/typeshed/stdlib/itertools.pyi
 +++ b/mypy/typeshed/stdlib/itertools.pyi
-@@ -27,7 +27,7 @@ _Predicate: TypeAlias = Callable[[_T], object]
- 
+@@ -28,7 +28,7 @@ _Predicate: TypeAlias = Callable[[_T], object]
  # Technically count can take anything that implements a number protocol and has an add method
  # but we can't enforce the add method
+ @disjoint_base
 -class count(Generic[_N]):
 +class count(Iterator[_N]):
      @overload
      def __new__(cls) -> count[int]: ...
      @overload
-@@ -37,12 +37,12 @@ class count(Generic[_N]):
-     def __next__(self) -> _N: ...
+@@ -39,13 +39,13 @@ class count(Generic[_N]):
      def __iter__(self) -> Self: ...
  
+ @disjoint_base
 -class cycle(Generic[_T]):
 +class cycle(Iterator[_T]):
      def __new__(cls, iterable: Iterable[_T], /) -> Self: ...
      def __next__(self) -> _T: ...
      def __iter__(self) -> Self: ...
  
+ @disjoint_base
 -class repeat(Generic[_T]):
 +class repeat(Iterator[_T]):
      @overload
      def __new__(cls, object: _T) -> Self: ...
      @overload
-@@ -51,7 +51,7 @@ class repeat(Generic[_T]):
-     def __iter__(self) -> Self: ...
+@@ -55,7 +55,7 @@ class repeat(Generic[_T]):
      def __length_hint__(self) -> int: ...
  
+ @disjoint_base
 -class accumulate(Generic[_T]):
 +class accumulate(Iterator[_T]):
      @overload
      def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ...
      @overload
-@@ -59,7 +59,7 @@ class accumulate(Generic[_T]):
-     def __iter__(self) -> Self: ...
+@@ -64,7 +64,7 @@ class accumulate(Generic[_T]):
      def __next__(self) -> _T: ...
  
+ @disjoint_base
 -class chain(Generic[_T]):
 +class chain(Iterator[_T]):
      def __new__(cls, *iterables: Iterable[_T]) -> Self: ...
      def __next__(self) -> _T: ...
      def __iter__(self) -> Self: ...
-@@ -68,22 +68,22 @@ class chain(Generic[_T]):
-     def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ...
+@@ -74,25 +74,25 @@ class chain(Generic[_T]):
      def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
  
+ @disjoint_base
 -class compress(Generic[_T]):
 +class compress(Iterator[_T]):
      def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
  
+ @disjoint_base
 -class dropwhile(Generic[_T]):
 +class dropwhile(Iterator[_T]):
      def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
  
+ @disjoint_base
 -class filterfalse(Generic[_T]):
 +class filterfalse(Iterator[_T]):
      def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T: ...
  
+ @disjoint_base
 -class groupby(Generic[_T_co, _S_co]):
 +class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ...
      @overload
-@@ -91,7 +91,7 @@ class groupby(Generic[_T_co, _S_co]):
-     def __iter__(self) -> Self: ...
+@@ -101,7 +101,7 @@ class groupby(Generic[_T_co, _S_co]):
      def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ...
  
+ @disjoint_base
 -class islice(Generic[_T]):
 +class islice(Iterator[_T]):
      @overload
      def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ...
      @overload
-@@ -99,19 +99,19 @@ class islice(Generic[_T]):
-     def __iter__(self) -> Self: ...
+@@ -110,20 +110,20 @@ class islice(Generic[_T]):
      def __next__(self) -> _T: ...
  
+ @disjoint_base
 -class starmap(Generic[_T_co]):
 +class starmap(Iterator[_T_co]):
      def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ...
      def __iter__(self) -> Self: ...
      def __next__(self) -> _T_co: ...
  
+ @disjoint_base
 -class takewhile(Generic[_T]):
 +class takewhile(Iterator[_T]):
      def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
@@ -229,52 +234,52 @@ index d0085dd72..7d05b1318 100644
      def __next__(self) -> _T: ...
  
  def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ...
- 
+ @disjoint_base
 -class zip_longest(Generic[_T_co]):
 +class zip_longest(Iterator[_T_co]):
      # one iterable (fillvalue doesn't matter)
      @overload
      def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ...
-@@ -189,7 +189,7 @@ class zip_longest(Generic[_T_co]):
-     def __iter__(self) -> Self: ...
+@@ -202,7 +202,7 @@ class zip_longest(Generic[_T_co]):
      def __next__(self) -> _T_co: ...
  
+ @disjoint_base
 -class product(Generic[_T_co]):
 +class product(Iterator[_T_co]):
      @overload
      def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ...
      @overload
-@@ -274,7 +274,7 @@ class product(Generic[_T_co]):
-     def __iter__(self) -> Self: ...
+@@ -288,7 +288,7 @@ class product(Generic[_T_co]):
      def __next__(self) -> _T_co: ...
  
+ @disjoint_base
 -class permutations(Generic[_T_co]):
 +class permutations(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ...
      @overload
-@@ -288,7 +288,7 @@ class permutations(Generic[_T_co]):
-     def __iter__(self) -> Self: ...
+@@ -303,7 +303,7 @@ class permutations(Generic[_T_co]):
      def __next__(self) -> _T_co: ...
  
+ @disjoint_base
 -class combinations(Generic[_T_co]):
 +class combinations(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ...
      @overload
-@@ -302,7 +302,7 @@ class combinations(Generic[_T_co]):
-     def __iter__(self) -> Self: ...
+@@ -318,7 +318,7 @@ class combinations(Generic[_T_co]):
      def __next__(self) -> _T_co: ...
  
+ @disjoint_base
 -class combinations_with_replacement(Generic[_T_co]):
 +class combinations_with_replacement(Iterator[_T_co]):
      @overload
      def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ...
      @overload
-@@ -317,13 +317,13 @@ class combinations_with_replacement(Generic[_T_co]):
-     def __next__(self) -> _T_co: ...
+@@ -334,14 +334,14 @@ class combinations_with_replacement(Generic[_T_co]):
  
  if sys.version_info >= (3, 10):
+     @disjoint_base
 -    class pairwise(Generic[_T_co]):
 +    class pairwise(Iterator[_T_co]):
          def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ...
@@ -282,6 +287,7 @@ index d0085dd72..7d05b1318 100644
          def __next__(self) -> _T_co: ...
  
  if sys.version_info >= (3, 12):
+     @disjoint_base
 -    class batched(Generic[_T_co]):
 +    class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]):
          if sys.version_info >= (3, 13):
@@ -307,18 +313,18 @@ index b79f9e773..f276372d0 100644
      def __iter__(self) -> Self: ...
      def next(self, timeout: float | None = None) -> _T: ...
 diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-index bcfea3a13..5a659deac 100644
+index 6b0f1ba94..882cd143c 100644
 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
 +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
-@@ -405,7 +405,7 @@ class Connection:
-         self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, /
+@@ -407,7 +407,7 @@ class Connection:
      ) -> Literal[False]: ...
  
+ @disjoint_base
 -class Cursor:
 +class Cursor(Iterator[Any]):
      arraysize: int
      @property
      def connection(self) -> Connection: ...
 -- 
-2.50.1
+2.51.0
 
diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi
index 00c6b357f7d8..d8d5a1829991 100644
--- a/mypy/typeshed/stdlib/_ast.pyi
+++ b/mypy/typeshed/stdlib/_ast.pyi
@@ -108,7 +108,7 @@ from ast import (
     unaryop as unaryop,
     withitem as withitem,
 )
-from typing import Literal
+from typing import Final
 
 if sys.version_info >= (3, 12):
     from ast import (
@@ -137,9 +137,9 @@ if sys.version_info >= (3, 10):
         pattern as pattern,
     )
 
-PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192]
-PyCF_ONLY_AST: Literal[1024]
-PyCF_TYPE_COMMENTS: Literal[4096]
+PyCF_ALLOW_TOP_LEVEL_AWAIT: Final = 8192
+PyCF_ONLY_AST: Final = 1024
+PyCF_TYPE_COMMENTS: Final = 4096
 
 if sys.version_info >= (3, 13):
-    PyCF_OPTIMIZED_AST: Literal[33792]
+    PyCF_OPTIMIZED_AST: Final = 33792
diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi
index 5253e967e5a3..f43178e4d725 100644
--- a/mypy/typeshed/stdlib/_asyncio.pyi
+++ b/mypy/typeshed/stdlib/_asyncio.pyi
@@ -4,12 +4,13 @@ from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable
 from contextvars import Context
 from types import FrameType, GenericAlias
 from typing import Any, Literal, TextIO, TypeVar
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 _T = TypeVar("_T")
 _T_co = TypeVar("_T_co", covariant=True)
 _TaskYieldType: TypeAlias = Future[object] | None
 
+@disjoint_base
 class Future(Awaitable[_T], Iterable[_T]):
     _state: str
     @property
@@ -20,7 +21,7 @@ class Future(Awaitable[_T], Iterable[_T]):
     @_log_traceback.setter
     def _log_traceback(self, val: Literal[False]) -> None: ...
     _asyncio_future_blocking: bool  # is a part of duck-typing contract for `Future`
-    def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ...
+    def __init__(self, *, loop: AbstractEventLoop | None = None) -> None: ...
     def __del__(self) -> None: ...
     def get_loop(self) -> AbstractEventLoop: ...
     @property
@@ -49,6 +50,7 @@ else:
 # While this is true in general, here it's sort-of okay to have a covariant subclass,
 # since the only reason why `asyncio.Future` is invariant is the `set_result()` method,
 # and `asyncio.Task.set_result()` always raises.
+@disjoint_base
 class Task(Future[_T_co]):  # type: ignore[type-var]  # pyright: ignore[reportInvalidTypeArguments]
     if sys.version_info >= (3, 12):
         def __init__(
@@ -56,7 +58,7 @@ class Task(Future[_T_co]):  # type: ignore[type-var]  # pyright: ignore[reportIn
             coro: _TaskCompatibleCoro[_T_co],
             *,
             loop: AbstractEventLoop | None = None,
-            name: str | None = ...,
+            name: str | None = None,
             context: Context | None = None,
             eager_start: bool = False,
         ) -> None: ...
@@ -66,12 +68,12 @@ class Task(Future[_T_co]):  # type: ignore[type-var]  # pyright: ignore[reportIn
             coro: _TaskCompatibleCoro[_T_co],
             *,
             loop: AbstractEventLoop | None = None,
-            name: str | None = ...,
+            name: str | None = None,
             context: Context | None = None,
         ) -> None: ...
     else:
         def __init__(
-            self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop | None = None, name: str | None = ...
+            self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop | None = None, name: str | None = None
         ) -> None: ...
 
     if sys.version_info >= (3, 12):
diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi
index d578df55c2fa..a6c3869fb851 100644
--- a/mypy/typeshed/stdlib/_blake2.pyi
+++ b/mypy/typeshed/stdlib/_blake2.pyi
@@ -1,15 +1,16 @@
+import sys
 from _typeshed import ReadableBuffer
-from typing import ClassVar, final
+from typing import ClassVar, Final, final
 from typing_extensions import Self
 
-BLAKE2B_MAX_DIGEST_SIZE: int = 64
-BLAKE2B_MAX_KEY_SIZE: int = 64
-BLAKE2B_PERSON_SIZE: int = 16
-BLAKE2B_SALT_SIZE: int = 16
-BLAKE2S_MAX_DIGEST_SIZE: int = 32
-BLAKE2S_MAX_KEY_SIZE: int = 32
-BLAKE2S_PERSON_SIZE: int = 8
-BLAKE2S_SALT_SIZE: int = 8
+BLAKE2B_MAX_DIGEST_SIZE: Final = 64
+BLAKE2B_MAX_KEY_SIZE: Final = 64
+BLAKE2B_PERSON_SIZE: Final = 16
+BLAKE2B_SALT_SIZE: Final = 16
+BLAKE2S_MAX_DIGEST_SIZE: Final = 32
+BLAKE2S_MAX_KEY_SIZE: Final = 32
+BLAKE2S_PERSON_SIZE: Final = 8
+BLAKE2S_SALT_SIZE: Final = 8
 
 @final
 class blake2b:
@@ -20,24 +21,45 @@ class blake2b:
     block_size: int
     digest_size: int
     name: str
-    def __new__(
-        cls,
-        data: ReadableBuffer = b"",
-        /,
-        *,
-        digest_size: int = 64,
-        key: ReadableBuffer = b"",
-        salt: ReadableBuffer = b"",
-        person: ReadableBuffer = b"",
-        fanout: int = 1,
-        depth: int = 1,
-        leaf_size: int = 0,
-        node_offset: int = 0,
-        node_depth: int = 0,
-        inner_size: int = 0,
-        last_node: bool = False,
-        usedforsecurity: bool = True,
-    ) -> Self: ...
+    if sys.version_info >= (3, 13):
+        def __new__(
+            cls,
+            data: ReadableBuffer = b"",
+            *,
+            digest_size: int = 64,
+            key: ReadableBuffer = b"",
+            salt: ReadableBuffer = b"",
+            person: ReadableBuffer = b"",
+            fanout: int = 1,
+            depth: int = 1,
+            leaf_size: int = 0,
+            node_offset: int = 0,
+            node_depth: int = 0,
+            inner_size: int = 0,
+            last_node: bool = False,
+            usedforsecurity: bool = True,
+            string: ReadableBuffer | None = None,
+        ) -> Self: ...
+    else:
+        def __new__(
+            cls,
+            data: ReadableBuffer = b"",
+            /,
+            *,
+            digest_size: int = 64,
+            key: ReadableBuffer = b"",
+            salt: ReadableBuffer = b"",
+            person: ReadableBuffer = b"",
+            fanout: int = 1,
+            depth: int = 1,
+            leaf_size: int = 0,
+            node_offset: int = 0,
+            node_depth: int = 0,
+            inner_size: int = 0,
+            last_node: bool = False,
+            usedforsecurity: bool = True,
+        ) -> Self: ...
+
     def copy(self) -> Self: ...
     def digest(self) -> bytes: ...
     def hexdigest(self) -> str: ...
@@ -52,24 +74,45 @@ class blake2s:
     block_size: int
     digest_size: int
     name: str
-    def __new__(
-        cls,
-        data: ReadableBuffer = b"",
-        /,
-        *,
-        digest_size: int = 32,
-        key: ReadableBuffer = b"",
-        salt: ReadableBuffer = b"",
-        person: ReadableBuffer = b"",
-        fanout: int = 1,
-        depth: int = 1,
-        leaf_size: int = 0,
-        node_offset: int = 0,
-        node_depth: int = 0,
-        inner_size: int = 0,
-        last_node: bool = False,
-        usedforsecurity: bool = True,
-    ) -> Self: ...
+    if sys.version_info >= (3, 13):
+        def __new__(
+            cls,
+            data: ReadableBuffer = b"",
+            *,
+            digest_size: int = 32,
+            key: ReadableBuffer = b"",
+            salt: ReadableBuffer = b"",
+            person: ReadableBuffer = b"",
+            fanout: int = 1,
+            depth: int = 1,
+            leaf_size: int = 0,
+            node_offset: int = 0,
+            node_depth: int = 0,
+            inner_size: int = 0,
+            last_node: bool = False,
+            usedforsecurity: bool = True,
+            string: ReadableBuffer | None = None,
+        ) -> Self: ...
+    else:
+        def __new__(
+            cls,
+            data: ReadableBuffer = b"",
+            /,
+            *,
+            digest_size: int = 32,
+            key: ReadableBuffer = b"",
+            salt: ReadableBuffer = b"",
+            person: ReadableBuffer = b"",
+            fanout: int = 1,
+            depth: int = 1,
+            leaf_size: int = 0,
+            node_offset: int = 0,
+            node_depth: int = 0,
+            inner_size: int = 0,
+            last_node: bool = False,
+            usedforsecurity: bool = True,
+        ) -> Self: ...
+
     def copy(self) -> Self: ...
     def digest(self) -> bytes: ...
     def hexdigest(self) -> str: ...
diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi
index b099bdd98f3c..c63606a13ca9 100644
--- a/mypy/typeshed/stdlib/_collections_abc.pyi
+++ b/mypy/typeshed/stdlib/_collections_abc.pyi
@@ -103,5 +103,6 @@ class dict_items(ItemsView[_KT_co, _VT_co]):  # undocumented
 if sys.version_info >= (3, 12):
     @runtime_checkable
     class Buffer(Protocol):
+        __slots__ = ()
         @abstractmethod
         def __buffer__(self, flags: int, /) -> memoryview: ...
diff --git a/mypy/typeshed/stdlib/_compat_pickle.pyi b/mypy/typeshed/stdlib/_compat_pickle.pyi
index 50fb22442cc9..32c0b542d991 100644
--- a/mypy/typeshed/stdlib/_compat_pickle.pyi
+++ b/mypy/typeshed/stdlib/_compat_pickle.pyi
@@ -1,8 +1,10 @@
-IMPORT_MAPPING: dict[str, str]
-NAME_MAPPING: dict[tuple[str, str], tuple[str, str]]
-PYTHON2_EXCEPTIONS: tuple[str, ...]
-MULTIPROCESSING_EXCEPTIONS: tuple[str, ...]
-REVERSE_IMPORT_MAPPING: dict[str, str]
-REVERSE_NAME_MAPPING: dict[tuple[str, str], tuple[str, str]]
-PYTHON3_OSERROR_EXCEPTIONS: tuple[str, ...]
-PYTHON3_IMPORTERROR_EXCEPTIONS: tuple[str, ...]
+from typing import Final
+
+IMPORT_MAPPING: Final[dict[str, str]]
+NAME_MAPPING: Final[dict[tuple[str, str], tuple[str, str]]]
+PYTHON2_EXCEPTIONS: Final[tuple[str, ...]]
+MULTIPROCESSING_EXCEPTIONS: Final[tuple[str, ...]]
+REVERSE_IMPORT_MAPPING: Final[dict[str, str]]
+REVERSE_NAME_MAPPING: Final[dict[tuple[str, str], tuple[str, str]]]
+PYTHON3_OSERROR_EXCEPTIONS: Final[tuple[str, ...]]
+PYTHON3_IMPORTERROR_EXCEPTIONS: Final[tuple[str, ...]]
diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi
index efe9ad69bd31..4128178c18b3 100644
--- a/mypy/typeshed/stdlib/_csv.pyi
+++ b/mypy/typeshed/stdlib/_csv.pyi
@@ -3,7 +3,7 @@ import sys
 from _typeshed import SupportsWrite
 from collections.abc import Iterable
 from typing import Any, Final, Literal, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 __version__: Final[str]
 
@@ -24,6 +24,7 @@ class Error(Exception): ...
 
 _DialectLike: TypeAlias = str | Dialect | csv.Dialect | type[Dialect | csv.Dialect]
 
+@disjoint_base
 class Dialect:
     delimiter: str
     quotechar: str | None
@@ -35,7 +36,7 @@ class Dialect:
     strict: bool
     def __new__(
         cls,
-        dialect: _DialectLike | None = ...,
+        dialect: _DialectLike | None = None,
         delimiter: str = ",",
         doublequote: bool = True,
         escapechar: str | None = None,
@@ -48,6 +49,7 @@ class Dialect:
 
 if sys.version_info >= (3, 10):
     # This class calls itself _csv.reader.
+    @disjoint_base
     class Reader:
         @property
         def dialect(self) -> Dialect: ...
@@ -56,6 +58,7 @@ if sys.version_info >= (3, 10):
         def __next__(self) -> list[str]: ...
 
     # This class calls itself _csv.writer.
+    @disjoint_base
     class Writer:
         @property
         def dialect(self) -> Dialect: ...
diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi
index bfd0f910f482..082a31f70562 100644
--- a/mypy/typeshed/stdlib/_ctypes.pyi
+++ b/mypy/typeshed/stdlib/_ctypes.pyi
@@ -5,24 +5,24 @@ from abc import abstractmethod
 from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
 from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
 from types import GenericAlias
-from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
+from typing import Any, ClassVar, Final, Generic, TypeVar, final, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 _T = TypeVar("_T")
 _CT = TypeVar("_CT", bound=_CData)
 
-FUNCFLAG_CDECL: int
-FUNCFLAG_PYTHONAPI: int
-FUNCFLAG_USE_ERRNO: int
-FUNCFLAG_USE_LASTERROR: int
-RTLD_GLOBAL: int
-RTLD_LOCAL: int
+FUNCFLAG_CDECL: Final = 0x1
+FUNCFLAG_PYTHONAPI: Final = 0x4
+FUNCFLAG_USE_ERRNO: Final = 0x8
+FUNCFLAG_USE_LASTERROR: Final = 0x10
+RTLD_GLOBAL: Final[int]
+RTLD_LOCAL: Final[int]
 
 if sys.version_info >= (3, 11):
-    CTYPES_MAX_ARGCOUNT: int
+    CTYPES_MAX_ARGCOUNT: Final[int]
 
 if sys.version_info >= (3, 12):
-    SIZEOF_TIME_T: int
+    SIZEOF_TIME_T: Final[int]
 
 if sys.platform == "win32":
     # Description, Source, HelpFile, HelpContext, scode
@@ -37,8 +37,8 @@ if sys.platform == "win32":
 
     def CopyComPointer(src: _PointerLike, dst: _PointerLike | _CArgObject) -> int: ...
 
-    FUNCFLAG_HRESULT: int
-    FUNCFLAG_STDCALL: int
+    FUNCFLAG_HRESULT: Final = 0x2
+    FUNCFLAG_STDCALL: Final = 0x0
 
     def FormatError(code: int = ...) -> str: ...
     def get_last_error() -> int: ...
diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi
index f21a9ca60270..d4e4d48f4e20 100644
--- a/mypy/typeshed/stdlib/_curses.pyi
+++ b/mypy/typeshed/stdlib/_curses.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadOnlyBuffer, SupportsRead, SupportsWrite
 from curses import _ncurses_version
-from typing import Any, final, overload
+from typing import Any, Final, final, overload
 from typing_extensions import TypeAlias
 
 # NOTE: This module is ordinarily only available on Unix, but the windows-curses
@@ -11,270 +11,270 @@ from typing_extensions import TypeAlias
 _ChType: TypeAlias = str | bytes | int
 
 # ACS codes are only initialized after initscr is called
-ACS_BBSS: int
-ACS_BLOCK: int
-ACS_BOARD: int
-ACS_BSBS: int
-ACS_BSSB: int
-ACS_BSSS: int
-ACS_BTEE: int
-ACS_BULLET: int
-ACS_CKBOARD: int
-ACS_DARROW: int
-ACS_DEGREE: int
-ACS_DIAMOND: int
-ACS_GEQUAL: int
-ACS_HLINE: int
-ACS_LANTERN: int
-ACS_LARROW: int
-ACS_LEQUAL: int
-ACS_LLCORNER: int
-ACS_LRCORNER: int
-ACS_LTEE: int
-ACS_NEQUAL: int
-ACS_PI: int
-ACS_PLMINUS: int
-ACS_PLUS: int
-ACS_RARROW: int
-ACS_RTEE: int
-ACS_S1: int
-ACS_S3: int
-ACS_S7: int
-ACS_S9: int
-ACS_SBBS: int
-ACS_SBSB: int
-ACS_SBSS: int
-ACS_SSBB: int
-ACS_SSBS: int
-ACS_SSSB: int
-ACS_SSSS: int
-ACS_STERLING: int
-ACS_TTEE: int
-ACS_UARROW: int
-ACS_ULCORNER: int
-ACS_URCORNER: int
-ACS_VLINE: int
-ALL_MOUSE_EVENTS: int
-A_ALTCHARSET: int
-A_ATTRIBUTES: int
-A_BLINK: int
-A_BOLD: int
-A_CHARTEXT: int
-A_COLOR: int
-A_DIM: int
-A_HORIZONTAL: int
-A_INVIS: int
-A_ITALIC: int
-A_LEFT: int
-A_LOW: int
-A_NORMAL: int
-A_PROTECT: int
-A_REVERSE: int
-A_RIGHT: int
-A_STANDOUT: int
-A_TOP: int
-A_UNDERLINE: int
-A_VERTICAL: int
-BUTTON1_CLICKED: int
-BUTTON1_DOUBLE_CLICKED: int
-BUTTON1_PRESSED: int
-BUTTON1_RELEASED: int
-BUTTON1_TRIPLE_CLICKED: int
-BUTTON2_CLICKED: int
-BUTTON2_DOUBLE_CLICKED: int
-BUTTON2_PRESSED: int
-BUTTON2_RELEASED: int
-BUTTON2_TRIPLE_CLICKED: int
-BUTTON3_CLICKED: int
-BUTTON3_DOUBLE_CLICKED: int
-BUTTON3_PRESSED: int
-BUTTON3_RELEASED: int
-BUTTON3_TRIPLE_CLICKED: int
-BUTTON4_CLICKED: int
-BUTTON4_DOUBLE_CLICKED: int
-BUTTON4_PRESSED: int
-BUTTON4_RELEASED: int
-BUTTON4_TRIPLE_CLICKED: int
+ACS_BBSS: Final[int]
+ACS_BLOCK: Final[int]
+ACS_BOARD: Final[int]
+ACS_BSBS: Final[int]
+ACS_BSSB: Final[int]
+ACS_BSSS: Final[int]
+ACS_BTEE: Final[int]
+ACS_BULLET: Final[int]
+ACS_CKBOARD: Final[int]
+ACS_DARROW: Final[int]
+ACS_DEGREE: Final[int]
+ACS_DIAMOND: Final[int]
+ACS_GEQUAL: Final[int]
+ACS_HLINE: Final[int]
+ACS_LANTERN: Final[int]
+ACS_LARROW: Final[int]
+ACS_LEQUAL: Final[int]
+ACS_LLCORNER: Final[int]
+ACS_LRCORNER: Final[int]
+ACS_LTEE: Final[int]
+ACS_NEQUAL: Final[int]
+ACS_PI: Final[int]
+ACS_PLMINUS: Final[int]
+ACS_PLUS: Final[int]
+ACS_RARROW: Final[int]
+ACS_RTEE: Final[int]
+ACS_S1: Final[int]
+ACS_S3: Final[int]
+ACS_S7: Final[int]
+ACS_S9: Final[int]
+ACS_SBBS: Final[int]
+ACS_SBSB: Final[int]
+ACS_SBSS: Final[int]
+ACS_SSBB: Final[int]
+ACS_SSBS: Final[int]
+ACS_SSSB: Final[int]
+ACS_SSSS: Final[int]
+ACS_STERLING: Final[int]
+ACS_TTEE: Final[int]
+ACS_UARROW: Final[int]
+ACS_ULCORNER: Final[int]
+ACS_URCORNER: Final[int]
+ACS_VLINE: Final[int]
+ALL_MOUSE_EVENTS: Final[int]
+A_ALTCHARSET: Final[int]
+A_ATTRIBUTES: Final[int]
+A_BLINK: Final[int]
+A_BOLD: Final[int]
+A_CHARTEXT: Final[int]
+A_COLOR: Final[int]
+A_DIM: Final[int]
+A_HORIZONTAL: Final[int]
+A_INVIS: Final[int]
+A_ITALIC: Final[int]
+A_LEFT: Final[int]
+A_LOW: Final[int]
+A_NORMAL: Final[int]
+A_PROTECT: Final[int]
+A_REVERSE: Final[int]
+A_RIGHT: Final[int]
+A_STANDOUT: Final[int]
+A_TOP: Final[int]
+A_UNDERLINE: Final[int]
+A_VERTICAL: Final[int]
+BUTTON1_CLICKED: Final[int]
+BUTTON1_DOUBLE_CLICKED: Final[int]
+BUTTON1_PRESSED: Final[int]
+BUTTON1_RELEASED: Final[int]
+BUTTON1_TRIPLE_CLICKED: Final[int]
+BUTTON2_CLICKED: Final[int]
+BUTTON2_DOUBLE_CLICKED: Final[int]
+BUTTON2_PRESSED: Final[int]
+BUTTON2_RELEASED: Final[int]
+BUTTON2_TRIPLE_CLICKED: Final[int]
+BUTTON3_CLICKED: Final[int]
+BUTTON3_DOUBLE_CLICKED: Final[int]
+BUTTON3_PRESSED: Final[int]
+BUTTON3_RELEASED: Final[int]
+BUTTON3_TRIPLE_CLICKED: Final[int]
+BUTTON4_CLICKED: Final[int]
+BUTTON4_DOUBLE_CLICKED: Final[int]
+BUTTON4_PRESSED: Final[int]
+BUTTON4_RELEASED: Final[int]
+BUTTON4_TRIPLE_CLICKED: Final[int]
 # Darwin ncurses doesn't provide BUTTON5_* constants prior to 3.12.10 and 3.13.3
 if sys.version_info >= (3, 10):
     if sys.version_info >= (3, 12) or sys.platform != "darwin":
-        BUTTON5_PRESSED: int
-        BUTTON5_RELEASED: int
-        BUTTON5_CLICKED: int
-        BUTTON5_DOUBLE_CLICKED: int
-        BUTTON5_TRIPLE_CLICKED: int
-BUTTON_ALT: int
-BUTTON_CTRL: int
-BUTTON_SHIFT: int
-COLOR_BLACK: int
-COLOR_BLUE: int
-COLOR_CYAN: int
-COLOR_GREEN: int
-COLOR_MAGENTA: int
-COLOR_RED: int
-COLOR_WHITE: int
-COLOR_YELLOW: int
-ERR: int
-KEY_A1: int
-KEY_A3: int
-KEY_B2: int
-KEY_BACKSPACE: int
-KEY_BEG: int
-KEY_BREAK: int
-KEY_BTAB: int
-KEY_C1: int
-KEY_C3: int
-KEY_CANCEL: int
-KEY_CATAB: int
-KEY_CLEAR: int
-KEY_CLOSE: int
-KEY_COMMAND: int
-KEY_COPY: int
-KEY_CREATE: int
-KEY_CTAB: int
-KEY_DC: int
-KEY_DL: int
-KEY_DOWN: int
-KEY_EIC: int
-KEY_END: int
-KEY_ENTER: int
-KEY_EOL: int
-KEY_EOS: int
-KEY_EXIT: int
-KEY_F0: int
-KEY_F1: int
-KEY_F10: int
-KEY_F11: int
-KEY_F12: int
-KEY_F13: int
-KEY_F14: int
-KEY_F15: int
-KEY_F16: int
-KEY_F17: int
-KEY_F18: int
-KEY_F19: int
-KEY_F2: int
-KEY_F20: int
-KEY_F21: int
-KEY_F22: int
-KEY_F23: int
-KEY_F24: int
-KEY_F25: int
-KEY_F26: int
-KEY_F27: int
-KEY_F28: int
-KEY_F29: int
-KEY_F3: int
-KEY_F30: int
-KEY_F31: int
-KEY_F32: int
-KEY_F33: int
-KEY_F34: int
-KEY_F35: int
-KEY_F36: int
-KEY_F37: int
-KEY_F38: int
-KEY_F39: int
-KEY_F4: int
-KEY_F40: int
-KEY_F41: int
-KEY_F42: int
-KEY_F43: int
-KEY_F44: int
-KEY_F45: int
-KEY_F46: int
-KEY_F47: int
-KEY_F48: int
-KEY_F49: int
-KEY_F5: int
-KEY_F50: int
-KEY_F51: int
-KEY_F52: int
-KEY_F53: int
-KEY_F54: int
-KEY_F55: int
-KEY_F56: int
-KEY_F57: int
-KEY_F58: int
-KEY_F59: int
-KEY_F6: int
-KEY_F60: int
-KEY_F61: int
-KEY_F62: int
-KEY_F63: int
-KEY_F7: int
-KEY_F8: int
-KEY_F9: int
-KEY_FIND: int
-KEY_HELP: int
-KEY_HOME: int
-KEY_IC: int
-KEY_IL: int
-KEY_LEFT: int
-KEY_LL: int
-KEY_MARK: int
-KEY_MAX: int
-KEY_MESSAGE: int
-KEY_MIN: int
-KEY_MOUSE: int
-KEY_MOVE: int
-KEY_NEXT: int
-KEY_NPAGE: int
-KEY_OPEN: int
-KEY_OPTIONS: int
-KEY_PPAGE: int
-KEY_PREVIOUS: int
-KEY_PRINT: int
-KEY_REDO: int
-KEY_REFERENCE: int
-KEY_REFRESH: int
-KEY_REPLACE: int
-KEY_RESET: int
-KEY_RESIZE: int
-KEY_RESTART: int
-KEY_RESUME: int
-KEY_RIGHT: int
-KEY_SAVE: int
-KEY_SBEG: int
-KEY_SCANCEL: int
-KEY_SCOMMAND: int
-KEY_SCOPY: int
-KEY_SCREATE: int
-KEY_SDC: int
-KEY_SDL: int
-KEY_SELECT: int
-KEY_SEND: int
-KEY_SEOL: int
-KEY_SEXIT: int
-KEY_SF: int
-KEY_SFIND: int
-KEY_SHELP: int
-KEY_SHOME: int
-KEY_SIC: int
-KEY_SLEFT: int
-KEY_SMESSAGE: int
-KEY_SMOVE: int
-KEY_SNEXT: int
-KEY_SOPTIONS: int
-KEY_SPREVIOUS: int
-KEY_SPRINT: int
-KEY_SR: int
-KEY_SREDO: int
-KEY_SREPLACE: int
-KEY_SRESET: int
-KEY_SRIGHT: int
-KEY_SRSUME: int
-KEY_SSAVE: int
-KEY_SSUSPEND: int
-KEY_STAB: int
-KEY_SUNDO: int
-KEY_SUSPEND: int
-KEY_UNDO: int
-KEY_UP: int
-OK: int
-REPORT_MOUSE_POSITION: int
+        BUTTON5_PRESSED: Final[int]
+        BUTTON5_RELEASED: Final[int]
+        BUTTON5_CLICKED: Final[int]
+        BUTTON5_DOUBLE_CLICKED: Final[int]
+        BUTTON5_TRIPLE_CLICKED: Final[int]
+BUTTON_ALT: Final[int]
+BUTTON_CTRL: Final[int]
+BUTTON_SHIFT: Final[int]
+COLOR_BLACK: Final[int]
+COLOR_BLUE: Final[int]
+COLOR_CYAN: Final[int]
+COLOR_GREEN: Final[int]
+COLOR_MAGENTA: Final[int]
+COLOR_RED: Final[int]
+COLOR_WHITE: Final[int]
+COLOR_YELLOW: Final[int]
+ERR: Final[int]
+KEY_A1: Final[int]
+KEY_A3: Final[int]
+KEY_B2: Final[int]
+KEY_BACKSPACE: Final[int]
+KEY_BEG: Final[int]
+KEY_BREAK: Final[int]
+KEY_BTAB: Final[int]
+KEY_C1: Final[int]
+KEY_C3: Final[int]
+KEY_CANCEL: Final[int]
+KEY_CATAB: Final[int]
+KEY_CLEAR: Final[int]
+KEY_CLOSE: Final[int]
+KEY_COMMAND: Final[int]
+KEY_COPY: Final[int]
+KEY_CREATE: Final[int]
+KEY_CTAB: Final[int]
+KEY_DC: Final[int]
+KEY_DL: Final[int]
+KEY_DOWN: Final[int]
+KEY_EIC: Final[int]
+KEY_END: Final[int]
+KEY_ENTER: Final[int]
+KEY_EOL: Final[int]
+KEY_EOS: Final[int]
+KEY_EXIT: Final[int]
+KEY_F0: Final[int]
+KEY_F1: Final[int]
+KEY_F10: Final[int]
+KEY_F11: Final[int]
+KEY_F12: Final[int]
+KEY_F13: Final[int]
+KEY_F14: Final[int]
+KEY_F15: Final[int]
+KEY_F16: Final[int]
+KEY_F17: Final[int]
+KEY_F18: Final[int]
+KEY_F19: Final[int]
+KEY_F2: Final[int]
+KEY_F20: Final[int]
+KEY_F21: Final[int]
+KEY_F22: Final[int]
+KEY_F23: Final[int]
+KEY_F24: Final[int]
+KEY_F25: Final[int]
+KEY_F26: Final[int]
+KEY_F27: Final[int]
+KEY_F28: Final[int]
+KEY_F29: Final[int]
+KEY_F3: Final[int]
+KEY_F30: Final[int]
+KEY_F31: Final[int]
+KEY_F32: Final[int]
+KEY_F33: Final[int]
+KEY_F34: Final[int]
+KEY_F35: Final[int]
+KEY_F36: Final[int]
+KEY_F37: Final[int]
+KEY_F38: Final[int]
+KEY_F39: Final[int]
+KEY_F4: Final[int]
+KEY_F40: Final[int]
+KEY_F41: Final[int]
+KEY_F42: Final[int]
+KEY_F43: Final[int]
+KEY_F44: Final[int]
+KEY_F45: Final[int]
+KEY_F46: Final[int]
+KEY_F47: Final[int]
+KEY_F48: Final[int]
+KEY_F49: Final[int]
+KEY_F5: Final[int]
+KEY_F50: Final[int]
+KEY_F51: Final[int]
+KEY_F52: Final[int]
+KEY_F53: Final[int]
+KEY_F54: Final[int]
+KEY_F55: Final[int]
+KEY_F56: Final[int]
+KEY_F57: Final[int]
+KEY_F58: Final[int]
+KEY_F59: Final[int]
+KEY_F6: Final[int]
+KEY_F60: Final[int]
+KEY_F61: Final[int]
+KEY_F62: Final[int]
+KEY_F63: Final[int]
+KEY_F7: Final[int]
+KEY_F8: Final[int]
+KEY_F9: Final[int]
+KEY_FIND: Final[int]
+KEY_HELP: Final[int]
+KEY_HOME: Final[int]
+KEY_IC: Final[int]
+KEY_IL: Final[int]
+KEY_LEFT: Final[int]
+KEY_LL: Final[int]
+KEY_MARK: Final[int]
+KEY_MAX: Final[int]
+KEY_MESSAGE: Final[int]
+KEY_MIN: Final[int]
+KEY_MOUSE: Final[int]
+KEY_MOVE: Final[int]
+KEY_NEXT: Final[int]
+KEY_NPAGE: Final[int]
+KEY_OPEN: Final[int]
+KEY_OPTIONS: Final[int]
+KEY_PPAGE: Final[int]
+KEY_PREVIOUS: Final[int]
+KEY_PRINT: Final[int]
+KEY_REDO: Final[int]
+KEY_REFERENCE: Final[int]
+KEY_REFRESH: Final[int]
+KEY_REPLACE: Final[int]
+KEY_RESET: Final[int]
+KEY_RESIZE: Final[int]
+KEY_RESTART: Final[int]
+KEY_RESUME: Final[int]
+KEY_RIGHT: Final[int]
+KEY_SAVE: Final[int]
+KEY_SBEG: Final[int]
+KEY_SCANCEL: Final[int]
+KEY_SCOMMAND: Final[int]
+KEY_SCOPY: Final[int]
+KEY_SCREATE: Final[int]
+KEY_SDC: Final[int]
+KEY_SDL: Final[int]
+KEY_SELECT: Final[int]
+KEY_SEND: Final[int]
+KEY_SEOL: Final[int]
+KEY_SEXIT: Final[int]
+KEY_SF: Final[int]
+KEY_SFIND: Final[int]
+KEY_SHELP: Final[int]
+KEY_SHOME: Final[int]
+KEY_SIC: Final[int]
+KEY_SLEFT: Final[int]
+KEY_SMESSAGE: Final[int]
+KEY_SMOVE: Final[int]
+KEY_SNEXT: Final[int]
+KEY_SOPTIONS: Final[int]
+KEY_SPREVIOUS: Final[int]
+KEY_SPRINT: Final[int]
+KEY_SR: Final[int]
+KEY_SREDO: Final[int]
+KEY_SREPLACE: Final[int]
+KEY_SRESET: Final[int]
+KEY_SRIGHT: Final[int]
+KEY_SRSUME: Final[int]
+KEY_SSAVE: Final[int]
+KEY_SSUSPEND: Final[int]
+KEY_STAB: Final[int]
+KEY_SUNDO: Final[int]
+KEY_SUSPEND: Final[int]
+KEY_UNDO: Final[int]
+KEY_UP: Final[int]
+OK: Final[int]
+REPORT_MOUSE_POSITION: Final[int]
 _C_API: Any
-version: bytes
+version: Final[bytes]
 
 def baudrate() -> int: ...
 def beep() -> None: ...
@@ -324,7 +324,7 @@ def mouseinterval(interval: int, /) -> None: ...
 def mousemask(newmask: int, /) -> tuple[int, int]: ...
 def napms(ms: int, /) -> int: ...
 def newpad(nlines: int, ncols: int, /) -> window: ...
-def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> window: ...
+def newwin(nlines: int, ncols: int, begin_y: int = 0, begin_x: int = 0, /) -> window: ...
 def nl(flag: bool = True, /) -> None: ...
 def nocbreak() -> None: ...
 def noecho() -> None: ...
@@ -394,8 +394,8 @@ class window:  # undocumented
     def attroff(self, attr: int, /) -> None: ...
     def attron(self, attr: int, /) -> None: ...
     def attrset(self, attr: int, /) -> None: ...
-    def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ...
-    def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ...
+    def bkgd(self, ch: _ChType, attr: int = 0, /) -> None: ...
+    def bkgdset(self, ch: _ChType, attr: int = 0, /) -> None: ...
     def border(
         self,
         ls: _ChType = ...,
@@ -410,7 +410,7 @@ class window:  # undocumented
     @overload
     def box(self) -> None: ...
     @overload
-    def box(self, vertch: _ChType = ..., horch: _ChType = ...) -> None: ...
+    def box(self, vertch: _ChType = 0, horch: _ChType = 0) -> None: ...
     @overload
     def chgat(self, attr: int) -> None: ...
     @overload
@@ -433,7 +433,7 @@ class window:  # undocumented
     def derwin(self, begin_y: int, begin_x: int) -> window: ...
     @overload
     def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ...
-    def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ...
+    def echochar(self, ch: _ChType, attr: int = 0, /) -> None: ...
     def enclose(self, y: int, x: int, /) -> bool: ...
     def erase(self) -> None: ...
     def getbegyx(self) -> tuple[int, int]: ...
@@ -487,9 +487,9 @@ class window:  # undocumented
     @overload
     def insstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ...
     @overload
-    def instr(self, n: int = ...) -> bytes: ...
+    def instr(self, n: int = 2047) -> bytes: ...
     @overload
-    def instr(self, y: int, x: int, n: int = ...) -> bytes: ...
+    def instr(self, y: int, x: int, n: int = 2047) -> bytes: ...
     def is_linetouched(self, line: int, /) -> bool: ...
     def is_wintouched(self) -> bool: ...
     def keypad(self, yes: bool, /) -> None: ...
@@ -523,7 +523,7 @@ class window:  # undocumented
     @overload
     def refresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ...
     def resize(self, nlines: int, ncols: int) -> None: ...
-    def scroll(self, lines: int = ...) -> None: ...
+    def scroll(self, lines: int = 1) -> None: ...
     def scrollok(self, flag: bool) -> None: ...
     def setscrreg(self, top: int, bottom: int, /) -> None: ...
     def standend(self) -> None: ...
@@ -540,7 +540,7 @@ class window:  # undocumented
     def syncok(self, flag: bool) -> None: ...
     def syncup(self) -> None: ...
     def timeout(self, delay: int) -> None: ...
-    def touchline(self, start: int, count: int, changed: bool = ...) -> None: ...
+    def touchline(self, start: int, count: int, changed: bool = True) -> None: ...
     def touchwin(self) -> None: ...
     def untouchwin(self) -> None: ...
     @overload
diff --git a/mypy/typeshed/stdlib/_curses_panel.pyi b/mypy/typeshed/stdlib/_curses_panel.pyi
index ddec22236b96..a552a151ddf1 100644
--- a/mypy/typeshed/stdlib/_curses_panel.pyi
+++ b/mypy/typeshed/stdlib/_curses_panel.pyi
@@ -1,8 +1,8 @@
 from _curses import window
-from typing import final
+from typing import Final, final
 
-__version__: str
-version: str
+__version__: Final[str]
+version: Final[str]
 
 class error(Exception): ...
 
diff --git a/mypy/typeshed/stdlib/_dbm.pyi b/mypy/typeshed/stdlib/_dbm.pyi
index 7e53cca3c704..222c3ffcb246 100644
--- a/mypy/typeshed/stdlib/_dbm.pyi
+++ b/mypy/typeshed/stdlib/_dbm.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadOnlyBuffer, StrOrBytesPath
 from types import TracebackType
-from typing import TypeVar, final, overload, type_check_only
+from typing import Final, TypeVar, final, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 if sys.platform != "win32":
@@ -10,7 +10,7 @@ if sys.platform != "win32":
     _ValueType: TypeAlias = str | ReadOnlyBuffer
 
     class error(OSError): ...
-    library: str
+    library: Final[str]
 
     # Actual typename dbm, not exposed by the implementation
     @final
@@ -33,7 +33,7 @@ if sys.platform != "win32":
         @overload
         def get(self, k: _KeyType, default: _T, /) -> bytes | _T: ...
         def keys(self) -> list[bytes]: ...
-        def setdefault(self, k: _KeyType, default: _ValueType = ..., /) -> bytes: ...
+        def setdefault(self, k: _KeyType, default: _ValueType = b"", /) -> bytes: ...
         # This isn't true, but the class can't be instantiated. See #13024
         __new__: None  # type: ignore[assignment]
         __init__: None  # type: ignore[assignment]
diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi
index fd0e6e6ac091..3cfe8944dfaf 100644
--- a/mypy/typeshed/stdlib/_decimal.pyi
+++ b/mypy/typeshed/stdlib/_decimal.pyi
@@ -51,14 +51,14 @@ if sys.version_info >= (3, 11):
     def localcontext(
         ctx: Context | None = None,
         *,
-        prec: int | None = ...,
-        rounding: str | None = ...,
-        Emin: int | None = ...,
-        Emax: int | None = ...,
-        capitals: int | None = ...,
-        clamp: int | None = ...,
-        traps: dict[_TrapType, bool] | None = ...,
-        flags: dict[_TrapType, bool] | None = ...,
+        prec: int | None = None,
+        rounding: str | None = None,
+        Emin: int | None = None,
+        Emax: int | None = None,
+        capitals: int | None = None,
+        clamp: int | None = None,
+        traps: dict[_TrapType, bool] | None = None,
+        flags: dict[_TrapType, bool] | None = None,
     ) -> _ContextManager: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
index 80eebe45a7d4..71642c65dc07 100644
--- a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
+++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi
@@ -9,7 +9,7 @@ from _typeshed.importlib import LoaderProtocol
 from collections.abc import Callable, Iterable, Iterator, Mapping, MutableSequence, Sequence
 from importlib.machinery import ModuleSpec
 from importlib.metadata import DistributionFinder, PathDistribution
-from typing import Any, Literal
+from typing import Any, Final, Literal
 from typing_extensions import Self, deprecated
 
 if sys.version_info >= (3, 10):
@@ -24,7 +24,7 @@ else:
     path_sep: Literal["/"]
     path_sep_tuple: tuple[Literal["/"]]
 
-MAGIC_NUMBER: bytes
+MAGIC_NUMBER: Final[bytes]
 
 def cache_from_source(path: StrPath, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ...
 def source_from_cache(path: StrPath) -> str: ...
@@ -37,7 +37,7 @@ def spec_from_file_location(
     submodule_search_locations: list[str] | None = ...,
 ) -> importlib.machinery.ModuleSpec | None: ...
 @deprecated(
-    "Deprecated as of Python 3.6: Use site configuration instead. "
+    "Deprecated since Python 3.6. Use site configuration instead. "
     "Future versions of Python may not enable this finder by default."
 )
 class WindowsRegistryFinder(importlib.abc.MetaPathFinder):
@@ -74,11 +74,11 @@ class PathFinder(importlib.abc.MetaPathFinder):
         @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.")
         def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ...
 
-SOURCE_SUFFIXES: list[str]
-DEBUG_BYTECODE_SUFFIXES: list[str]
-OPTIMIZED_BYTECODE_SUFFIXES: list[str]
-BYTECODE_SUFFIXES: list[str]
-EXTENSION_SUFFIXES: list[str]
+SOURCE_SUFFIXES: Final[list[str]]
+DEBUG_BYTECODE_SUFFIXES: Final = [".pyc"]
+OPTIMIZED_BYTECODE_SUFFIXES: Final = [".pyc"]
+BYTECODE_SUFFIXES: Final = [".pyc"]
+EXTENSION_SUFFIXES: Final[list[str]]
 
 class FileFinder(importlib.abc.PathEntryFinder):
     path: str
@@ -155,7 +155,7 @@ if sys.version_info >= (3, 11):
         def get_code(self, fullname: str) -> types.CodeType: ...
         def create_module(self, spec: ModuleSpec) -> None: ...
         def exec_module(self, module: types.ModuleType) -> None: ...
-        @deprecated("load_module() is deprecated; use exec_module() instead")
+        @deprecated("Deprecated since Python 3.10; will be removed in Python 3.15. Use `exec_module()` instead.")
         def load_module(self, fullname: str) -> types.ModuleType: ...
         def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ...
         if sys.version_info < (3, 12):
@@ -177,9 +177,9 @@ else:
         def get_code(self, fullname: str) -> types.CodeType: ...
         def create_module(self, spec: ModuleSpec) -> None: ...
         def exec_module(self, module: types.ModuleType) -> None: ...
-        @deprecated("load_module() is deprecated; use exec_module() instead")
-        def load_module(self, fullname: str) -> types.ModuleType: ...
         if sys.version_info >= (3, 10):
+            @deprecated("Deprecated since Python 3.10; will be removed in Python 3.15. Use `exec_module()` instead.")
+            def load_module(self, fullname: str) -> types.ModuleType: ...
             @staticmethod
             @deprecated(
                 "Deprecated since Python 3.4; removed in Python 3.12. "
@@ -188,6 +188,7 @@ else:
             def module_repr(module: types.ModuleType) -> str: ...
             def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ...
         else:
+            def load_module(self, fullname: str) -> types.ModuleType: ...
             @classmethod
             @deprecated(
                 "Deprecated since Python 3.4; removed in Python 3.12. "
diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi
index 8b7ef52cdffd..03c1eef3be3f 100644
--- a/mypy/typeshed/stdlib/_hashlib.pyi
+++ b/mypy/typeshed/stdlib/_hashlib.pyi
@@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer
 from collections.abc import Callable
 from types import ModuleType
 from typing import AnyStr, Protocol, final, overload, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 _DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType | None
 
@@ -22,6 +22,7 @@ class _HashObject(Protocol):
     def hexdigest(self) -> str: ...
     def update(self, obj: ReadableBuffer, /) -> None: ...
 
+@disjoint_base
 class HASH:
     @property
     def digest_size(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi
index f89a24e7d85c..8e097efad618 100644
--- a/mypy/typeshed/stdlib/_interpreters.pyi
+++ b/mypy/typeshed/stdlib/_interpreters.pyi
@@ -1,7 +1,7 @@
 import types
 from collections.abc import Callable
-from typing import Any, Final, Literal, SupportsIndex, TypeVar
-from typing_extensions import TypeAlias
+from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload
+from typing_extensions import TypeAlias, disjoint_base
 
 _R = TypeVar("_R")
 
@@ -12,48 +12,45 @@ class InterpreterError(Exception): ...
 class InterpreterNotFoundError(InterpreterError): ...
 class NotShareableError(ValueError): ...
 
+@disjoint_base
 class CrossInterpreterBufferView:
     def __buffer__(self, flags: int, /) -> memoryview: ...
 
 def new_config(name: _Configs = "isolated", /, **overides: object) -> types.SimpleNamespace: ...
 def create(config: types.SimpleNamespace | _Configs | None = "isolated", *, reqrefs: bool = False) -> int: ...
 def destroy(id: SupportsIndex, *, restrict: bool = False) -> None: ...
-def list_all(*, require_ready: bool) -> list[tuple[int, int]]: ...
-def get_current() -> tuple[int, int]: ...
-def get_main() -> tuple[int, int]: ...
+def list_all(*, require_ready: bool = False) -> list[tuple[int, _Whence]]: ...
+def get_current() -> tuple[int, _Whence]: ...
+def get_main() -> tuple[int, _Whence]: ...
 def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ...
 def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ...
 def whence(id: SupportsIndex) -> _Whence: ...
 def exec(
-    id: SupportsIndex,
-    code: str | types.CodeType | Callable[[], object],
-    shared: _SharedDict | None = None,
-    *,
-    restrict: bool = False,
+    id: SupportsIndex, code: str | types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False
 ) -> None | types.SimpleNamespace: ...
 def call(
     id: SupportsIndex,
     callable: Callable[..., _R],
-    args: tuple[Any, ...] | None = None,
-    kwargs: dict[str, Any] | None = None,
+    args: tuple[Any, ...] = (),
+    kwargs: dict[str, Any] = {},
     *,
+    preserve_exc: bool = False,
     restrict: bool = False,
 ) -> tuple[_R, types.SimpleNamespace]: ...
 def run_string(
-    id: SupportsIndex,
-    script: str | types.CodeType | Callable[[], object],
-    shared: _SharedDict | None = None,
-    *,
-    restrict: bool = False,
+    id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False
 ) -> None: ...
 def run_func(
-    id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: _SharedDict | None = None, *, restrict: bool = False
+    id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False
 ) -> None: ...
 def set___main___attrs(id: SupportsIndex, updates: _SharedDict, *, restrict: bool = False) -> None: ...
 def incref(id: SupportsIndex, *, implieslink: bool = False, restrict: bool = False) -> None: ...
 def decref(id: SupportsIndex, *, restrict: bool = False) -> None: ...
 def is_shareable(obj: object) -> bool: ...
-def capture_exception(exc: BaseException | None = None) -> types.SimpleNamespace: ...
+@overload
+def capture_exception(exc: BaseException) -> types.SimpleNamespace: ...
+@overload
+def capture_exception(exc: None = None) -> types.SimpleNamespace | None: ...
 
 _Whence: TypeAlias = Literal[0, 1, 2, 3, 4, 5]
 WHENCE_UNKNOWN: Final = 0
diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi
index e368ddef7f4e..2d2a60e4dddf 100644
--- a/mypy/typeshed/stdlib/_io.pyi
+++ b/mypy/typeshed/stdlib/_io.pyi
@@ -7,11 +7,14 @@ from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as Un
 from os import _Opener
 from types import TracebackType
 from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
-from typing_extensions import Self
+from typing_extensions import Self, disjoint_base
 
 _T = TypeVar("_T")
 
-DEFAULT_BUFFER_SIZE: Final = 8192
+if sys.version_info >= (3, 14):
+    DEFAULT_BUFFER_SIZE: Final = 131072
+else:
+    DEFAULT_BUFFER_SIZE: Final = 8192
 
 open = builtins.open
 
@@ -19,32 +22,62 @@ def open_code(path: str) -> IO[bytes]: ...
 
 BlockingIOError = builtins.BlockingIOError
 
-class _IOBase:
-    def __iter__(self) -> Iterator[bytes]: ...
-    def __next__(self) -> bytes: ...
-    def __enter__(self) -> Self: ...
-    def __exit__(
-        self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
-    ) -> None: ...
-    def close(self) -> None: ...
-    def fileno(self) -> int: ...
-    def flush(self) -> None: ...
-    def isatty(self) -> bool: ...
-    def readable(self) -> bool: ...
-    read: Callable[..., Any]
-    def readlines(self, hint: int = -1, /) -> list[bytes]: ...
-    def seek(self, offset: int, whence: int = 0, /) -> int: ...
-    def seekable(self) -> bool: ...
-    def tell(self) -> int: ...
-    def truncate(self, size: int | None = None, /) -> int: ...
-    def writable(self) -> bool: ...
-    write: Callable[..., Any]
-    def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ...
-    def readline(self, size: int | None = -1, /) -> bytes: ...
-    def __del__(self) -> None: ...
-    @property
-    def closed(self) -> bool: ...
-    def _checkClosed(self) -> None: ...  # undocumented
+if sys.version_info >= (3, 12):
+    @disjoint_base
+    class _IOBase:
+        def __iter__(self) -> Iterator[bytes]: ...
+        def __next__(self) -> bytes: ...
+        def __enter__(self) -> Self: ...
+        def __exit__(
+            self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
+        ) -> None: ...
+        def close(self) -> None: ...
+        def fileno(self) -> int: ...
+        def flush(self) -> None: ...
+        def isatty(self) -> bool: ...
+        def readable(self) -> bool: ...
+        read: Callable[..., Any]
+        def readlines(self, hint: int = -1, /) -> list[bytes]: ...
+        def seek(self, offset: int, whence: int = 0, /) -> int: ...
+        def seekable(self) -> bool: ...
+        def tell(self) -> int: ...
+        def truncate(self, size: int | None = None, /) -> int: ...
+        def writable(self) -> bool: ...
+        write: Callable[..., Any]
+        def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ...
+        def readline(self, size: int | None = -1, /) -> bytes: ...
+        def __del__(self) -> None: ...
+        @property
+        def closed(self) -> bool: ...
+        def _checkClosed(self) -> None: ...  # undocumented
+
+else:
+    class _IOBase:
+        def __iter__(self) -> Iterator[bytes]: ...
+        def __next__(self) -> bytes: ...
+        def __enter__(self) -> Self: ...
+        def __exit__(
+            self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
+        ) -> None: ...
+        def close(self) -> None: ...
+        def fileno(self) -> int: ...
+        def flush(self) -> None: ...
+        def isatty(self) -> bool: ...
+        def readable(self) -> bool: ...
+        read: Callable[..., Any]
+        def readlines(self, hint: int = -1, /) -> list[bytes]: ...
+        def seek(self, offset: int, whence: int = 0, /) -> int: ...
+        def seekable(self) -> bool: ...
+        def tell(self) -> int: ...
+        def truncate(self, size: int | None = None, /) -> int: ...
+        def writable(self) -> bool: ...
+        write: Callable[..., Any]
+        def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ...
+        def readline(self, size: int | None = -1, /) -> bytes: ...
+        def __del__(self) -> None: ...
+        @property
+        def closed(self) -> bool: ...
+        def _checkClosed(self) -> None: ...  # undocumented
 
 class _RawIOBase(_IOBase):
     def readall(self) -> bytes: ...
@@ -62,6 +95,7 @@ class _BufferedIOBase(_IOBase):
     def read(self, size: int | None = -1, /) -> bytes: ...
     def read1(self, size: int = -1, /) -> bytes: ...
 
+@disjoint_base
 class FileIO(RawIOBase, _RawIOBase, BinaryIO):  # type: ignore[misc]  # incompatible definitions of writelines in the base classes
     mode: str
     # The type of "name" equals the argument passed in to the constructor,
@@ -76,6 +110,7 @@ class FileIO(RawIOBase, _RawIOBase, BinaryIO):  # type: ignore[misc]  # incompat
     def seek(self, pos: int, whence: int = 0, /) -> int: ...
     def read(self, size: int | None = -1, /) -> bytes | MaybeNone: ...
 
+@disjoint_base
 class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]  # incompatible definitions of methods in the base classes
     def __init__(self, initial_bytes: ReadableBuffer = b"") -> None: ...
     # BytesIO does not contain a "name" field. This workaround is necessary
@@ -116,31 +151,51 @@ class _BufferedReaderStream(Protocol):
 
 _BufferedReaderStreamT = TypeVar("_BufferedReaderStreamT", bound=_BufferedReaderStream, default=_BufferedReaderStream)
 
+@disjoint_base
 class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedReaderStreamT]):  # type: ignore[misc]  # incompatible definitions of methods in the base classes
     raw: _BufferedReaderStreamT
-    def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 8192) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 131072) -> None: ...
+    else:
+        def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 8192) -> None: ...
+
     def peek(self, size: int = 0, /) -> bytes: ...
     def seek(self, target: int, whence: int = 0, /) -> int: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
+@disjoint_base
 class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]  # incompatible definitions of writelines in the base classes
     raw: RawIOBase
-    def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(self, raw: RawIOBase, buffer_size: int = 131072) -> None: ...
+    else:
+        def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
+
     def write(self, buffer: ReadableBuffer, /) -> int: ...
     def seek(self, target: int, whence: int = 0, /) -> int: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
+@disjoint_base
 class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO):  # type: ignore[misc]  # incompatible definitions of methods in the base classes
     mode: str
     name: Any
     raw: RawIOBase
-    def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(self, raw: RawIOBase, buffer_size: int = 131072) -> None: ...
+    else:
+        def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
+
     def seek(self, target: int, whence: int = 0, /) -> int: ...  # stubtest needs this
     def peek(self, size: int = 0, /) -> bytes: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
+@disjoint_base
 class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT]):
-    def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
+    if sys.version_info >= (3, 14):
+        def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 131072, /) -> None: ...
+    else:
+        def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
+
     def peek(self, size: int = 0, /) -> bytes: ...
 
 class _TextIOBase(_IOBase):
@@ -181,6 +236,7 @@ class _WrappedBuffer(Protocol):
 
 _BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True)
 
+@disjoint_base
 class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]):  # type: ignore[misc]  # incompatible definitions of write in the base classes
     def __init__(
         self,
@@ -215,6 +271,7 @@ class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]):  # t
     def seek(self, cookie: int, whence: int = 0, /) -> int: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
+@disjoint_base
 class StringIO(TextIOBase, _TextIOBase, TextIO):  # type: ignore[misc]  # incompatible definitions of write in the base classes
     def __init__(self, initial_value: str | None = "", newline: str | None = "\n") -> None: ...
     # StringIO does not contain a "name" field. This workaround is necessary
@@ -227,6 +284,7 @@ class StringIO(TextIOBase, _TextIOBase, TextIO):  # type: ignore[misc]  # incomp
     def seek(self, pos: int, whence: int = 0, /) -> int: ...
     def truncate(self, pos: int | None = None, /) -> int: ...
 
+@disjoint_base
 class IncrementalNewlineDecoder:
     def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = "strict") -> None: ...
     def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ...
diff --git a/mypy/typeshed/stdlib/_lsprof.pyi b/mypy/typeshed/stdlib/_lsprof.pyi
index 8a6934162c92..4f6d98b8ffb6 100644
--- a/mypy/typeshed/stdlib/_lsprof.pyi
+++ b/mypy/typeshed/stdlib/_lsprof.pyi
@@ -3,7 +3,9 @@ from _typeshed import structseq
 from collections.abc import Callable
 from types import CodeType
 from typing import Any, Final, final
+from typing_extensions import disjoint_base
 
+@disjoint_base
 class Profiler:
     def __init__(
         self, timer: Callable[[], float] | None = None, timeunit: float = 0.0, subcalls: bool = True, builtins: bool = True
diff --git a/mypy/typeshed/stdlib/_lzma.pyi b/mypy/typeshed/stdlib/_lzma.pyi
index 1a27c7428e8e..b38dce9faded 100644
--- a/mypy/typeshed/stdlib/_lzma.pyi
+++ b/mypy/typeshed/stdlib/_lzma.pyi
@@ -16,7 +16,7 @@ CHECK_CRC64: Final = 4
 CHECK_SHA256: Final = 10
 CHECK_ID_MAX: Final = 15
 CHECK_UNKNOWN: Final = 16
-FILTER_LZMA1: int  # v big number
+FILTER_LZMA1: Final[int]  # v big number
 FILTER_LZMA2: Final = 33
 FILTER_DELTA: Final = 3
 FILTER_X86: Final = 4
@@ -33,14 +33,14 @@ MF_BT4: Final = 20
 MODE_FAST: Final = 1
 MODE_NORMAL: Final = 2
 PRESET_DEFAULT: Final = 6
-PRESET_EXTREME: int  # v big number
+PRESET_EXTREME: Final[int]  # v big number
 
 @final
 class LZMADecompressor:
     if sys.version_info >= (3, 12):
-        def __new__(cls, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> Self: ...
+        def __new__(cls, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None) -> Self: ...
     else:
-        def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ...
+        def __init__(self, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None) -> None: ...
 
     def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ...
     @property
@@ -56,11 +56,11 @@ class LZMADecompressor:
 class LZMACompressor:
     if sys.version_info >= (3, 12):
         def __new__(
-            cls, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ...
+            cls, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = None
         ) -> Self: ...
     else:
         def __init__(
-            self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ...
+            self, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = None
         ) -> None: ...
 
     def compress(self, data: ReadableBuffer, /) -> bytes: ...
diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi
index ef45ff6dc3c8..edceed51bf9d 100644
--- a/mypy/typeshed/stdlib/_msi.pyi
+++ b/mypy/typeshed/stdlib/_msi.pyi
@@ -1,5 +1,5 @@
 import sys
-from typing import type_check_only
+from typing import Final, type_check_only
 
 if sys.platform == "win32":
     class MSIError(Exception): ...
@@ -56,42 +56,42 @@ if sys.platform == "win32":
     def OpenDatabase(path: str, persist: int, /) -> _Database: ...
     def CreateRecord(count: int, /) -> _Record: ...
 
-    MSICOLINFO_NAMES: int
-    MSICOLINFO_TYPES: int
-    MSIDBOPEN_CREATE: int
-    MSIDBOPEN_CREATEDIRECT: int
-    MSIDBOPEN_DIRECT: int
-    MSIDBOPEN_PATCHFILE: int
-    MSIDBOPEN_READONLY: int
-    MSIDBOPEN_TRANSACT: int
-    MSIMODIFY_ASSIGN: int
-    MSIMODIFY_DELETE: int
-    MSIMODIFY_INSERT: int
-    MSIMODIFY_INSERT_TEMPORARY: int
-    MSIMODIFY_MERGE: int
-    MSIMODIFY_REFRESH: int
-    MSIMODIFY_REPLACE: int
-    MSIMODIFY_SEEK: int
-    MSIMODIFY_UPDATE: int
-    MSIMODIFY_VALIDATE: int
-    MSIMODIFY_VALIDATE_DELETE: int
-    MSIMODIFY_VALIDATE_FIELD: int
-    MSIMODIFY_VALIDATE_NEW: int
+    MSICOLINFO_NAMES: Final[int]
+    MSICOLINFO_TYPES: Final[int]
+    MSIDBOPEN_CREATE: Final[int]
+    MSIDBOPEN_CREATEDIRECT: Final[int]
+    MSIDBOPEN_DIRECT: Final[int]
+    MSIDBOPEN_PATCHFILE: Final[int]
+    MSIDBOPEN_READONLY: Final[int]
+    MSIDBOPEN_TRANSACT: Final[int]
+    MSIMODIFY_ASSIGN: Final[int]
+    MSIMODIFY_DELETE: Final[int]
+    MSIMODIFY_INSERT: Final[int]
+    MSIMODIFY_INSERT_TEMPORARY: Final[int]
+    MSIMODIFY_MERGE: Final[int]
+    MSIMODIFY_REFRESH: Final[int]
+    MSIMODIFY_REPLACE: Final[int]
+    MSIMODIFY_SEEK: Final[int]
+    MSIMODIFY_UPDATE: Final[int]
+    MSIMODIFY_VALIDATE: Final[int]
+    MSIMODIFY_VALIDATE_DELETE: Final[int]
+    MSIMODIFY_VALIDATE_FIELD: Final[int]
+    MSIMODIFY_VALIDATE_NEW: Final[int]
 
-    PID_APPNAME: int
-    PID_AUTHOR: int
-    PID_CHARCOUNT: int
-    PID_CODEPAGE: int
-    PID_COMMENTS: int
-    PID_CREATE_DTM: int
-    PID_KEYWORDS: int
-    PID_LASTAUTHOR: int
-    PID_LASTPRINTED: int
-    PID_LASTSAVE_DTM: int
-    PID_PAGECOUNT: int
-    PID_REVNUMBER: int
-    PID_SECURITY: int
-    PID_SUBJECT: int
-    PID_TEMPLATE: int
-    PID_TITLE: int
-    PID_WORDCOUNT: int
+    PID_APPNAME: Final[int]
+    PID_AUTHOR: Final[int]
+    PID_CHARCOUNT: Final[int]
+    PID_CODEPAGE: Final[int]
+    PID_COMMENTS: Final[int]
+    PID_CREATE_DTM: Final[int]
+    PID_KEYWORDS: Final[int]
+    PID_LASTAUTHOR: Final[int]
+    PID_LASTPRINTED: Final[int]
+    PID_LASTSAVE_DTM: Final[int]
+    PID_PAGECOUNT: Final[int]
+    PID_REVNUMBER: Final[int]
+    PID_SECURITY: Final[int]
+    PID_SUBJECT: Final[int]
+    PID_TEMPLATE: Final[int]
+    PID_TITLE: Final[int]
+    PID_WORDCOUNT: Final[int]
diff --git a/mypy/typeshed/stdlib/_multibytecodec.pyi b/mypy/typeshed/stdlib/_multibytecodec.pyi
index 7e408f2aa30e..abe58cb64f31 100644
--- a/mypy/typeshed/stdlib/_multibytecodec.pyi
+++ b/mypy/typeshed/stdlib/_multibytecodec.pyi
@@ -2,6 +2,7 @@ from _typeshed import ReadableBuffer
 from codecs import _ReadableStream, _WritableStream
 from collections.abc import Iterable
 from typing import final, type_check_only
+from typing_extensions import disjoint_base
 
 # This class is not exposed. It calls itself _multibytecodec.MultibyteCodec.
 @final
@@ -10,6 +11,7 @@ class _MultibyteCodec:
     def decode(self, input: ReadableBuffer, errors: str | None = None) -> str: ...
     def encode(self, input: str, errors: str | None = None) -> bytes: ...
 
+@disjoint_base
 class MultibyteIncrementalDecoder:
     errors: str
     def __init__(self, errors: str = "strict") -> None: ...
@@ -18,6 +20,7 @@ class MultibyteIncrementalDecoder:
     def reset(self) -> None: ...
     def setstate(self, state: tuple[bytes, int], /) -> None: ...
 
+@disjoint_base
 class MultibyteIncrementalEncoder:
     errors: str
     def __init__(self, errors: str = "strict") -> None: ...
@@ -26,6 +29,7 @@ class MultibyteIncrementalEncoder:
     def reset(self) -> None: ...
     def setstate(self, state: int, /) -> None: ...
 
+@disjoint_base
 class MultibyteStreamReader:
     errors: str
     stream: _ReadableStream
@@ -35,6 +39,7 @@ class MultibyteStreamReader:
     def readlines(self, sizehintobj: int | None = None, /) -> list[str]: ...
     def reset(self) -> None: ...
 
+@disjoint_base
 class MultibyteStreamWriter:
     errors: str
     stream: _WritableStream
diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi
index 03051bb09d3c..544f787172d6 100644
--- a/mypy/typeshed/stdlib/_pickle.pyi
+++ b/mypy/typeshed/stdlib/_pickle.pyi
@@ -2,7 +2,7 @@ from _typeshed import ReadableBuffer, SupportsWrite
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from pickle import PickleBuffer as PickleBuffer
 from typing import Any, Protocol, type_check_only
-from typing_extensions import TypeAlias
+from typing_extensions import TypeAlias, disjoint_base
 
 @type_check_only
 class _ReadableFileobj(Protocol):
@@ -57,6 +57,7 @@ class PicklerMemoProxy:
     def clear(self, /) -> None: ...
     def copy(self, /) -> dict[int, tuple[int, Any]]: ...
 
+@disjoint_base
 class Pickler:
     fast: bool
     dispatch_table: Mapping[type, Callable[[Any], _ReducedType]]
@@ -84,6 +85,7 @@ class UnpicklerMemoProxy:
     def clear(self, /) -> None: ...
     def copy(self, /) -> dict[int, tuple[int, Any]]: ...
 
+@disjoint_base
 class Unpickler:
     def __init__(
         self,
diff --git a/mypy/typeshed/stdlib/_queue.pyi b/mypy/typeshed/stdlib/_queue.pyi
index f98397b132ab..edd484a9a71a 100644
--- a/mypy/typeshed/stdlib/_queue.pyi
+++ b/mypy/typeshed/stdlib/_queue.pyi
@@ -1,10 +1,12 @@
 from types import GenericAlias
 from typing import Any, Generic, TypeVar
+from typing_extensions import disjoint_base
 
 _T = TypeVar("_T")
 
 class Empty(Exception): ...
 
+@disjoint_base
 class SimpleQueue(Generic[_T]):
     def __init__(self) -> None: ...
     def empty(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/_random.pyi b/mypy/typeshed/stdlib/_random.pyi
index 4082344ade8e..ac00fdfb7272 100644
--- a/mypy/typeshed/stdlib/_random.pyi
+++ b/mypy/typeshed/stdlib/_random.pyi
@@ -1,10 +1,16 @@
-from typing_extensions import TypeAlias
+import sys
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 # Actually Tuple[(int,) * 625]
 _State: TypeAlias = tuple[int, ...]
 
+@disjoint_base
 class Random:
-    def __init__(self, seed: object = ...) -> None: ...
+    if sys.version_info >= (3, 10):
+        def __init__(self, seed: object = ..., /) -> None: ...
+    else:
+        def __new__(self, seed: object = ..., /) -> Self: ...
+
     def seed(self, n: object = None, /) -> None: ...
     def getstate(self) -> _State: ...
     def setstate(self, state: _State, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi
index 9c153a3a6ba0..cdad886b3415 100644
--- a/mypy/typeshed/stdlib/_socket.pyi
+++ b/mypy/typeshed/stdlib/_socket.pyi
@@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, WriteableBuffer
 from collections.abc import Iterable
 from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout
 from typing import Any, Final, SupportsIndex, overload
-from typing_extensions import CapsuleType, TypeAlias
+from typing_extensions import CapsuleType, TypeAlias, disjoint_base
 
 _CMSG: TypeAlias = tuple[int, int, bytes]
 _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer]
@@ -731,6 +731,7 @@ if sys.platform != "win32" and sys.platform != "darwin":
 
 # ===== Classes =====
 
+@disjoint_base
 class socket:
     @property
     def family(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi
index 8afa3e5297bd..73a43f29c8c5 100644
--- a/mypy/typeshed/stdlib/_ssl.pyi
+++ b/mypy/typeshed/stdlib/_ssl.pyi
@@ -13,7 +13,7 @@ from ssl import (
     SSLZeroReturnError as SSLZeroReturnError,
 )
 from typing import Any, ClassVar, Final, Literal, TypedDict, final, overload, type_check_only
-from typing_extensions import NotRequired, Self, TypeAlias, deprecated
+from typing_extensions import NotRequired, Self, TypeAlias, deprecated, disjoint_base
 
 _PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray
 _PCTRTT: TypeAlias = tuple[tuple[str, str], ...]
@@ -67,7 +67,7 @@ if sys.platform == "win32":
 
 def txt2obj(txt: str, name: bool = False) -> tuple[int, str, str, str]: ...
 def nid2obj(nid: int, /) -> tuple[int, str, str, str]: ...
-
+@disjoint_base
 class _SSLContext:
     check_hostname: bool
     keylog_filename: str | None
diff --git a/mypy/typeshed/stdlib/_struct.pyi b/mypy/typeshed/stdlib/_struct.pyi
index 662170e869f3..a8fac2aea1b0 100644
--- a/mypy/typeshed/stdlib/_struct.pyi
+++ b/mypy/typeshed/stdlib/_struct.pyi
@@ -1,6 +1,7 @@
 from _typeshed import ReadableBuffer, WriteableBuffer
 from collections.abc import Iterator
 from typing import Any
+from typing_extensions import disjoint_base
 
 def pack(fmt: str | bytes, /, *v: Any) -> bytes: ...
 def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ...
@@ -8,7 +9,7 @@ def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: .
 def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ...
 def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ...
 def calcsize(format: str | bytes, /) -> int: ...
-
+@disjoint_base
 class Struct:
     @property
     def format(self) -> str: ...
diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi
index 970130dfb09c..6969ae48cae7 100644
--- a/mypy/typeshed/stdlib/_thread.pyi
+++ b/mypy/typeshed/stdlib/_thread.pyi
@@ -5,7 +5,7 @@ from collections.abc import Callable
 from threading import Thread
 from types import TracebackType
 from typing import Any, Final, NoReturn, final, overload
-from typing_extensions import TypeVarTuple, Unpack
+from typing_extensions import TypeVarTuple, Unpack, disjoint_base
 
 _Ts = TypeVarTuple("_Ts")
 
@@ -85,7 +85,7 @@ def allocate() -> LockType: ...  # Obsolete synonym for allocate_lock()
 def get_ident() -> int: ...
 def stack_size(size: int = 0, /) -> int: ...
 
-TIMEOUT_MAX: float
+TIMEOUT_MAX: Final[float]
 
 def get_native_id() -> int: ...  # only available on some platforms
 @final
@@ -110,6 +110,7 @@ if sys.version_info >= (3, 12):
 if sys.version_info >= (3, 14):
     def set_name(name: str) -> None: ...
 
+@disjoint_base
 class _local:
     def __getattribute__(self, name: str, /) -> Any: ...
     def __setattr__(self, name: str, value: Any, /) -> None: ...
diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi
index 07a825f0d816..5f6acaf840aa 100644
--- a/mypy/typeshed/stdlib/_threading_local.pyi
+++ b/mypy/typeshed/stdlib/_threading_local.pyi
@@ -7,6 +7,7 @@ __all__ = ["local"]
 _LocalDict: TypeAlias = dict[Any, Any]
 
 class _localimpl:
+    __slots__ = ("key", "dicts", "localargs", "locallock", "__weakref__")
     key: str
     dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]]
     # Keep localargs in sync with the *args, **kwargs annotation on local.__new__
@@ -16,6 +17,7 @@ class _localimpl:
     def create_dict(self) -> _LocalDict: ...
 
 class local:
+    __slots__ = ("_local__impl", "__dict__")
     def __new__(cls, /, *args: Any, **kw: Any) -> Self: ...
     def __getattribute__(self, name: str) -> Any: ...
     def __setattr__(self, name: str, value: Any) -> None: ...
diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
index 98a369dfc589..25054b601a4f 100644
--- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi
+++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi
@@ -3,7 +3,7 @@
 # See the README.md file in this directory for more information.
 
 import sys
-from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized
+from collections.abc import Awaitable, Callable, Iterable, Iterator, Sequence, Set as AbstractSet, Sized
 from dataclasses import Field
 from os import PathLike
 from types import FrameType, TracebackType
@@ -54,7 +54,8 @@ Unused: TypeAlias = object  # stable
 
 # Marker for return types that include None, but where forcing the user to
 # check for None can be detrimental. Sometimes called "the Any trick". See
-# CONTRIBUTING.md for more information.
+# https://typing.python.org/en/latest/guides/writing_stubs.html#the-any-trick
+# for more information.
 MaybeNone: TypeAlias = Any  # stable
 
 # Used to mark arguments that default to a sentinel value. This prevents
@@ -275,6 +276,16 @@ class SupportsWrite(Protocol[_T_contra]):
 class SupportsFlush(Protocol):
     def flush(self) -> object: ...
 
+# Suitable for dictionary view objects
+class Viewable(Protocol[_T_co]):
+    def __len__(self) -> int: ...
+    def __iter__(self) -> Iterator[_T_co]: ...
+
+class SupportsGetItemViewable(Protocol[_KT, _VT_co]):
+    def __len__(self) -> int: ...
+    def __iter__(self) -> Iterator[_KT]: ...
+    def __getitem__(self, key: _KT, /) -> _VT_co: ...
+
 # Unfortunately PEP 688 does not allow us to distinguish read-only
 # from writable buffers. We use these aliases for readability for now.
 # Perhaps a future extension of the buffer protocol will allow us to
diff --git a/mypy/typeshed/stdlib/_warnings.pyi b/mypy/typeshed/stdlib/_warnings.pyi
index 2e571e676c97..2dbc7b855281 100644
--- a/mypy/typeshed/stdlib/_warnings.pyi
+++ b/mypy/typeshed/stdlib/_warnings.pyi
@@ -38,9 +38,9 @@ def warn_explicit(
     filename: str,
     lineno: int,
     module: str | None = ...,
-    registry: dict[str | tuple[str, type[Warning], int], int] | None = ...,
-    module_globals: dict[str, Any] | None = ...,
-    source: Any | None = ...,
+    registry: dict[str | tuple[str, type[Warning], int], int] | None = None,
+    module_globals: dict[str, Any] | None = None,
+    source: Any | None = None,
 ) -> None: ...
 @overload
 def warn_explicit(
@@ -48,8 +48,8 @@ def warn_explicit(
     category: Any,
     filename: str,
     lineno: int,
-    module: str | None = ...,
-    registry: dict[str | tuple[str, type[Warning], int], int] | None = ...,
-    module_globals: dict[str, Any] | None = ...,
-    source: Any | None = ...,
+    module: str | None = None,
+    registry: dict[str | tuple[str, type[Warning], int], int] | None = None,
+    module_globals: dict[str, Any] | None = None,
+    source: Any | None = None,
 ) -> None: ...
diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi
index 6083ea4ae57a..d9e2c377b115 100644
--- a/mypy/typeshed/stdlib/_winapi.pyi
+++ b/mypy/typeshed/stdlib/_winapi.pyi
@@ -128,21 +128,21 @@ if sys.platform == "win32":
     WAIT_TIMEOUT: Final = 258
 
     if sys.version_info >= (3, 10):
-        LOCALE_NAME_INVARIANT: str
-        LOCALE_NAME_MAX_LENGTH: int
-        LOCALE_NAME_SYSTEM_DEFAULT: str
-        LOCALE_NAME_USER_DEFAULT: str | None
+        LOCALE_NAME_INVARIANT: Final[str]
+        LOCALE_NAME_MAX_LENGTH: Final[int]
+        LOCALE_NAME_SYSTEM_DEFAULT: Final[str]
+        LOCALE_NAME_USER_DEFAULT: Final[str | None]
 
-        LCMAP_FULLWIDTH: int
-        LCMAP_HALFWIDTH: int
-        LCMAP_HIRAGANA: int
-        LCMAP_KATAKANA: int
-        LCMAP_LINGUISTIC_CASING: int
-        LCMAP_LOWERCASE: int
-        LCMAP_SIMPLIFIED_CHINESE: int
-        LCMAP_TITLECASE: int
-        LCMAP_TRADITIONAL_CHINESE: int
-        LCMAP_UPPERCASE: int
+        LCMAP_FULLWIDTH: Final[int]
+        LCMAP_HALFWIDTH: Final[int]
+        LCMAP_HIRAGANA: Final[int]
+        LCMAP_KATAKANA: Final[int]
+        LCMAP_LINGUISTIC_CASING: Final[int]
+        LCMAP_LOWERCASE: Final[int]
+        LCMAP_SIMPLIFIED_CHINESE: Final[int]
+        LCMAP_TITLECASE: Final[int]
+        LCMAP_TRADITIONAL_CHINESE: Final[int]
+        LCMAP_UPPERCASE: Final[int]
 
     if sys.version_info >= (3, 12):
         COPYFILE2_CALLBACK_CHUNK_STARTED: Final = 1
diff --git a/mypy/typeshed/stdlib/_zstd.pyi b/mypy/typeshed/stdlib/_zstd.pyi
index 2730232528fc..f5e98ef88bb9 100644
--- a/mypy/typeshed/stdlib/_zstd.pyi
+++ b/mypy/typeshed/stdlib/_zstd.pyi
@@ -45,9 +45,9 @@ class ZstdCompressor:
     CONTINUE: Final = 0
     FLUSH_BLOCK: Final = 1
     FLUSH_FRAME: Final = 2
-    def __init__(
-        self, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
-    ) -> None: ...
+    def __new__(
+        cls, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
+    ) -> Self: ...
     def compress(
         self, /, data: ReadableBuffer, mode: _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 0
     ) -> bytes: ...
@@ -58,7 +58,7 @@ class ZstdCompressor:
 
 @final
 class ZstdDecompressor:
-    def __init__(self, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> None: ...
+    def __new__(cls, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> Self: ...
     def decompress(self, /, data: ReadableBuffer, max_length: int = -1) -> bytes: ...
     @property
     def eof(self) -> bool: ...
@@ -69,7 +69,7 @@ class ZstdDecompressor:
 
 @final
 class ZstdDict:
-    def __init__(self, dict_content: bytes, /, *, is_raw: bool = False) -> None: ...
+    def __new__(cls, dict_content: bytes, /, *, is_raw: bool = False) -> Self: ...
     def __len__(self, /) -> int: ...
     @property
     def as_digested_dict(self) -> tuple[Self, int]: ...
diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi
index fdca48ac7aaf..c8cd549e30ec 100644
--- a/mypy/typeshed/stdlib/abc.pyi
+++ b/mypy/typeshed/stdlib/abc.pyi
@@ -28,17 +28,17 @@ class ABCMeta(type):
     def register(cls: ABCMeta, subclass: type[_T]) -> type[_T]: ...
 
 def abstractmethod(funcobj: _FuncT) -> _FuncT: ...
-@deprecated("Use 'classmethod' with 'abstractmethod' instead")
+@deprecated("Deprecated since Python 3.3. Use `@classmethod` stacked on top of `@abstractmethod` instead.")
 class abstractclassmethod(classmethod[_T, _P, _R_co]):
     __isabstractmethod__: Literal[True]
     def __init__(self, callable: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ...
 
-@deprecated("Use 'staticmethod' with 'abstractmethod' instead")
+@deprecated("Deprecated since Python 3.3. Use `@staticmethod` stacked on top of `@abstractmethod` instead.")
 class abstractstaticmethod(staticmethod[_P, _R_co]):
     __isabstractmethod__: Literal[True]
     def __init__(self, callable: Callable[_P, _R_co]) -> None: ...
 
-@deprecated("Use 'property' with 'abstractmethod' instead")
+@deprecated("Deprecated since Python 3.3. Use `@property` stacked on top of `@abstractmethod` instead.")
 class abstractproperty(property):
     __isabstractmethod__: Literal[True]
 
diff --git a/mypy/typeshed/stdlib/annotationlib.pyi b/mypy/typeshed/stdlib/annotationlib.pyi
index 7590c632d785..3679dc29daaa 100644
--- a/mypy/typeshed/stdlib/annotationlib.pyi
+++ b/mypy/typeshed/stdlib/annotationlib.pyi
@@ -28,6 +28,20 @@ if sys.version_info >= (3, 14):
 
     @final
     class ForwardRef:
+        __slots__ = (
+            "__forward_is_argument__",
+            "__forward_is_class__",
+            "__forward_module__",
+            "__weakref__",
+            "__arg__",
+            "__globals__",
+            "__extra_names__",
+            "__code__",
+            "__ast_node__",
+            "__cell__",
+            "__owner__",
+            "__stringifier_dict__",
+        )
         __forward_is_argument__: bool
         __forward_is_class__: bool
         __forward_module__: str | None
@@ -64,7 +78,7 @@ if sys.version_info >= (3, 14):
             owner: object = None,
             format: Format = Format.VALUE,  # noqa: Y011
         ) -> AnnotationForm: ...
-        @deprecated("Use ForwardRef.evaluate() or typing.evaluate_forward_ref() instead.")
+        @deprecated("Use `ForwardRef.evaluate()` or `typing.evaluate_forward_ref()` instead.")
         def _evaluate(
             self,
             globalns: dict[str, Any] | None,
diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi
index 3c3ba116a692..f4b3aac09aa9 100644
--- a/mypy/typeshed/stdlib/argparse.pyi
+++ b/mypy/typeshed/stdlib/argparse.pyi
@@ -156,7 +156,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
             exit_on_error: bool = True,
             *,
             suggest_on_error: bool = False,
-            color: bool = False,
+            color: bool = True,
         ) -> None: ...
     else:
         def __init__(
@@ -470,7 +470,7 @@ class Namespace(_AttributeHolder):
     __hash__: ClassVar[None]  # type: ignore[assignment]
 
 if sys.version_info >= (3, 14):
-    @deprecated("Deprecated in Python 3.14; Simply open files after parsing arguments")
+    @deprecated("Deprecated since Python 3.14. Open files after parsing arguments instead.")
     class FileType:
         # undocumented
         _mode: str
diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi
index ccb7f1b98ef3..a6b0344a1e2e 100644
--- a/mypy/typeshed/stdlib/array.pyi
+++ b/mypy/typeshed/stdlib/array.pyi
@@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite
 from collections.abc import Iterable, MutableSequence
 from types import GenericAlias
 from typing import Any, ClassVar, Literal, SupportsIndex, TypeVar, overload
-from typing_extensions import Self, TypeAlias, deprecated
+from typing_extensions import Self, TypeAlias, deprecated, disjoint_base
 
 _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"]
 _FloatTypeCode: TypeAlias = Literal["f", "d"]
@@ -17,6 +17,7 @@ _T = TypeVar("_T", int, float, str)
 
 typecodes: str
 
+@disjoint_base
 class array(MutableSequence[_T]):
     @property
     def typecode(self) -> _TypeCode: ...
diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi
index 8ee867116301..d360c2ed60e5 100644
--- a/mypy/typeshed/stdlib/ast.pyi
+++ b/mypy/typeshed/stdlib/ast.pyi
@@ -11,7 +11,7 @@ from _ast import (
 from _typeshed import ReadableBuffer, Unused
 from collections.abc import Iterable, Iterator, Sequence
 from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload, type_check_only
-from typing_extensions import Self, Unpack, deprecated
+from typing_extensions import Self, Unpack, deprecated, disjoint_base
 
 if sys.version_info >= (3, 13):
     from _ast import PyCF_OPTIMIZED_AST as PyCF_OPTIMIZED_AST
@@ -30,16 +30,24 @@ class _Attributes(TypedDict, Generic[_EndPositionT], total=False):
 # The various AST classes are implemented in C, and imported from _ast at runtime,
 # but they consider themselves to live in the ast module,
 # so we'll define the stubs in this file.
-class AST:
-    if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 12):
+    @disjoint_base
+    class AST:
         __match_args__ = ()
-    _attributes: ClassVar[tuple[str, ...]]
-    _fields: ClassVar[tuple[str, ...]]
-    if sys.version_info >= (3, 13):
-        _field_types: ClassVar[dict[str, Any]]
+        _attributes: ClassVar[tuple[str, ...]]
+        _fields: ClassVar[tuple[str, ...]]
+        if sys.version_info >= (3, 13):
+            _field_types: ClassVar[dict[str, Any]]
 
-    if sys.version_info >= (3, 14):
-        def __replace__(self) -> Self: ...
+        if sys.version_info >= (3, 14):
+            def __replace__(self) -> Self: ...
+
+else:
+    class AST:
+        if sys.version_info >= (3, 10):
+            __match_args__ = ()
+        _attributes: ClassVar[tuple[str, ...]]
+        _fields: ClassVar[tuple[str, ...]]
 
 class mod(AST): ...
 
@@ -1098,16 +1106,16 @@ class Constant(expr):
     if sys.version_info < (3, 14):
         # Aliases for value, for backwards compatibility
         @property
-        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
+        @deprecated("Removed in Python 3.14. Use `value` instead.")
         def n(self) -> _ConstantValue: ...
         @n.setter
-        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
+        @deprecated("Removed in Python 3.14. Use `value` instead.")
         def n(self, value: _ConstantValue) -> None: ...
         @property
-        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
+        @deprecated("Removed in Python 3.14. Use `value` instead.")
         def s(self) -> _ConstantValue: ...
         @s.setter
-        @deprecated("Will be removed in Python 3.14. Use `value` instead.")
+        @deprecated("Removed in Python 3.14. Use `value` instead.")
         def s(self, value: _ConstantValue) -> None: ...
 
     def __init__(self, value: _ConstantValue, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ...
@@ -1206,7 +1214,7 @@ class Slice(expr):
             self, *, lower: expr | None = ..., upper: expr | None = ..., step: expr | None = ..., **kwargs: Unpack[_Attributes]
         ) -> Self: ...
 
-@deprecated("Deprecated since Python 3.9. Use ast.Tuple instead.")
+@deprecated("Deprecated since Python 3.9. Use `ast.Tuple` instead.")
 class ExtSlice(slice):
     def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_Attributes]) -> Tuple: ...  # type: ignore[misc]
 
@@ -1711,23 +1719,23 @@ else:
         def __init__(cls, *args: Unused) -> None: ...
 
 if sys.version_info < (3, 14):
-    @deprecated("Replaced by ast.Constant; removed in Python 3.14")
+    @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.")
     class Num(Constant, metaclass=_ABC):
         def __new__(cls, n: complex, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
-    @deprecated("Replaced by ast.Constant; removed in Python 3.14")
+    @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.")
     class Str(Constant, metaclass=_ABC):
         def __new__(cls, s: str, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
-    @deprecated("Replaced by ast.Constant; removed in Python 3.14")
+    @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.")
     class Bytes(Constant, metaclass=_ABC):
         def __new__(cls, s: bytes, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
-    @deprecated("Replaced by ast.Constant; removed in Python 3.14")
+    @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.")
     class NameConstant(Constant, metaclass=_ABC):
         def __new__(cls, value: _ConstantValue, kind: str | None, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
-    @deprecated("Replaced by ast.Constant; removed in Python 3.14")
+    @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.")
     class Ellipsis(Constant, metaclass=_ABC):
         def __new__(cls, **kwargs: Unpack[_Attributes]) -> Constant: ...  # type: ignore[misc]  # pyright: ignore[reportInconsistentConstructor]
 
@@ -2046,15 +2054,15 @@ class NodeVisitor:
     def visit_Param(self, node: Param) -> Any: ...
 
     if sys.version_info < (3, 14):
-        @deprecated("Replaced by visit_Constant; removed in Python 3.14")
+        @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.")
         def visit_Num(self, node: Num) -> Any: ...  # type: ignore[deprecated]
-        @deprecated("Replaced by visit_Constant; removed in Python 3.14")
+        @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.")
         def visit_Str(self, node: Str) -> Any: ...  # type: ignore[deprecated]
-        @deprecated("Replaced by visit_Constant; removed in Python 3.14")
+        @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.")
         def visit_Bytes(self, node: Bytes) -> Any: ...  # type: ignore[deprecated]
-        @deprecated("Replaced by visit_Constant; removed in Python 3.14")
+        @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.")
         def visit_NameConstant(self, node: NameConstant) -> Any: ...  # type: ignore[deprecated]
-        @deprecated("Replaced by visit_Constant; removed in Python 3.14")
+        @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.")
         def visit_Ellipsis(self, node: Ellipsis) -> Any: ...  # type: ignore[deprecated]
 
 class NodeTransformer(NodeVisitor):
diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi
index cad7dde40b01..1f493210d665 100644
--- a/mypy/typeshed/stdlib/asyncio/base_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi
@@ -10,7 +10,7 @@ from asyncio.transports import BaseTransport, DatagramTransport, ReadTransport,
 from collections.abc import Callable, Iterable, Sequence
 from concurrent.futures import Executor, ThreadPoolExecutor
 from contextvars import Context
-from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket
+from socket import AddressFamily, AddressInfo, SocketKind, _Address, _RetAddress, socket
 from typing import IO, Any, Literal, TypeVar, overload
 from typing_extensions import TypeAlias, TypeVarTuple, Unpack
 
@@ -235,8 +235,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = 0,
+            flags: int = 1,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -254,8 +254,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = 0,
+            flags: int = 1,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -274,8 +274,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -292,8 +292,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -311,8 +311,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -328,8 +328,8 @@ class BaseEventLoop(AbstractEventLoop):
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi
index a37f6f697b9a..14c4c0bf3d5a 100644
--- a/mypy/typeshed/stdlib/asyncio/events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/events.pyi
@@ -11,7 +11,7 @@ from abc import ABCMeta, abstractmethod
 from collections.abc import Callable, Sequence
 from concurrent.futures import Executor
 from contextvars import Context
-from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket
+from socket import AddressFamily, AddressInfo, SocketKind, _Address, _RetAddress, socket
 from typing import IO, Any, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack, deprecated
 
@@ -73,6 +73,7 @@ class _TaskFactory(Protocol):
     def __call__(self, loop: AbstractEventLoop, factory: _CoroutineLike[_T], /) -> Future[_T]: ...
 
 class Handle:
+    __slots__ = ("_callback", "_args", "_cancelled", "_loop", "_source_traceback", "_repr", "__weakref__", "_context")
     _cancelled: bool
     _args: Sequence[Any]
     def __init__(
@@ -85,6 +86,7 @@ class Handle:
         def get_context(self) -> Context: ...
 
 class TimerHandle(Handle):
+    __slots__ = ["_scheduled", "_when"]
     def __init__(
         self,
         when: float,
@@ -287,8 +289,8 @@ class AbstractEventLoop:
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -307,8 +309,8 @@ class AbstractEventLoop:
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -328,8 +330,8 @@ class AbstractEventLoop:
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -347,8 +349,8 @@ class AbstractEventLoop:
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -367,8 +369,8 @@ class AbstractEventLoop:
             host: str | Sequence[str] | None = None,
             port: int = ...,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: None = None,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -385,8 +387,8 @@ class AbstractEventLoop:
             host: None = None,
             port: None = None,
             *,
-            family: int = ...,
-            flags: int = ...,
+            family: int = AddressFamily.AF_UNSPEC,
+            flags: int = AddressInfo.AI_PASSIVE,
             sock: socket = ...,
             backlog: int = 100,
             ssl: _SSLContext = None,
@@ -534,7 +536,7 @@ class AbstractEventLoop:
         bufsize: Literal[0] = 0,
         encoding: None = None,
         errors: None = None,
-        text: Literal[False] | None = ...,
+        text: Literal[False] | None = None,
         **kwargs: Any,
     ) -> tuple[SubprocessTransport, _ProtocolT]: ...
     @abstractmethod
@@ -614,10 +616,10 @@ class _AbstractEventLoopPolicy:
     if sys.version_info < (3, 14):
         if sys.version_info >= (3, 12):
             @abstractmethod
-            @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
             def get_child_watcher(self) -> AbstractChildWatcher: ...
             @abstractmethod
-            @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
             def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ...
         else:
             @abstractmethod
@@ -643,9 +645,9 @@ else:
 if sys.version_info >= (3, 14):
     def _get_event_loop_policy() -> _AbstractEventLoopPolicy: ...
     def _set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ...
-    @deprecated("Deprecated as of Python 3.14; will be removed in Python 3.16")
+    @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.")
     def get_event_loop_policy() -> _AbstractEventLoopPolicy: ...
-    @deprecated("Deprecated as of Python 3.14; will be removed in Python 3.16")
+    @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.")
     def set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ...
 
 else:
@@ -657,9 +659,9 @@ def new_event_loop() -> AbstractEventLoop: ...
 
 if sys.version_info < (3, 14):
     if sys.version_info >= (3, 12):
-        @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+        @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
         def get_child_watcher() -> AbstractChildWatcher: ...
-        @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+        @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
         def set_child_watcher(watcher: AbstractChildWatcher) -> None: ...
 
     else:
diff --git a/mypy/typeshed/stdlib/asyncio/graph.pyi b/mypy/typeshed/stdlib/asyncio/graph.pyi
index cb2cf0174995..18a8a6457d75 100644
--- a/mypy/typeshed/stdlib/asyncio/graph.pyi
+++ b/mypy/typeshed/stdlib/asyncio/graph.pyi
@@ -1,26 +1,28 @@
+import sys
 from _typeshed import SupportsWrite
 from asyncio import Future
 from dataclasses import dataclass
 from types import FrameType
 from typing import Any, overload
 
-__all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph")
+if sys.version_info >= (3, 14):
+    __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph")
 
-@dataclass(frozen=True)
-class FrameCallGraphEntry:
-    frame: FrameType
+    @dataclass(frozen=True, slots=True)
+    class FrameCallGraphEntry:
+        frame: FrameType
 
-@dataclass(frozen=True)
-class FutureCallGraph:
-    future: Future[Any]
-    call_stack: tuple[FrameCallGraphEntry, ...]
-    awaited_by: tuple[FutureCallGraph, ...]
+    @dataclass(frozen=True, slots=True)
+    class FutureCallGraph:
+        future: Future[Any]
+        call_stack: tuple[FrameCallGraphEntry, ...]
+        awaited_by: tuple[FutureCallGraph, ...]
 
-@overload
-def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
-@overload
-def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
-def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ...
-def print_call_graph(
-    future: Future[Any] | None = None, /, *, file: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None
-) -> None: ...
+    @overload
+    def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
+    @overload
+    def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ...
+    def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ...
+    def print_call_graph(
+        future: Future[Any] | None = None, /, *, file: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None
+    ) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/protocols.pyi b/mypy/typeshed/stdlib/asyncio/protocols.pyi
index 5425336c49a8..2c52ad4be410 100644
--- a/mypy/typeshed/stdlib/asyncio/protocols.pyi
+++ b/mypy/typeshed/stdlib/asyncio/protocols.pyi
@@ -6,21 +6,26 @@ from typing import Any
 __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol")
 
 class BaseProtocol:
+    __slots__ = ()
     def connection_made(self, transport: transports.BaseTransport) -> None: ...
     def connection_lost(self, exc: Exception | None) -> None: ...
     def pause_writing(self) -> None: ...
     def resume_writing(self) -> None: ...
 
 class Protocol(BaseProtocol):
+    # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class'
+    __slots__: tuple[()] = ()
     def data_received(self, data: bytes) -> None: ...
     def eof_received(self) -> bool | None: ...
 
 class BufferedProtocol(BaseProtocol):
+    __slots__ = ()
     def get_buffer(self, sizehint: int) -> ReadableBuffer: ...
     def buffer_updated(self, nbytes: int) -> None: ...
     def eof_received(self) -> bool | None: ...
 
 class DatagramProtocol(BaseProtocol):
+    __slots__ = ()
     def connection_made(self, transport: transports.DatagramTransport) -> None: ...  # type: ignore[override]
     # addr can be a tuple[int, int] for some unusual protocols like socket.AF_NETLINK.
     # Use tuple[str | Any, int] to not cause typechecking issues on most usual cases.
@@ -30,6 +35,7 @@ class DatagramProtocol(BaseProtocol):
     def error_received(self, exc: Exception) -> None: ...
 
 class SubprocessProtocol(BaseProtocol):
+    __slots__: tuple[()] = ()
     def pipe_data_received(self, fd: int, data: bytes) -> None: ...
     def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ...
     def process_exited(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/runners.pyi b/mypy/typeshed/stdlib/asyncio/runners.pyi
index caf5e4996cf4..919e6521f8a1 100644
--- a/mypy/typeshed/stdlib/asyncio/runners.pyi
+++ b/mypy/typeshed/stdlib/asyncio/runners.pyi
@@ -26,7 +26,7 @@ if sys.version_info >= (3, 11):
 
 if sys.version_info >= (3, 12):
     def run(
-        main: Coroutine[Any, Any, _T], *, debug: bool | None = ..., loop_factory: Callable[[], AbstractEventLoop] | None = ...
+        main: Coroutine[Any, Any, _T], *, debug: bool | None = None, loop_factory: Callable[[], AbstractEventLoop] | None = None
     ) -> _T: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi
index bf8db0246ee2..33cffb11ed78 100644
--- a/mypy/typeshed/stdlib/asyncio/streams.pyi
+++ b/mypy/typeshed/stdlib/asyncio/streams.pyi
@@ -34,7 +34,7 @@ if sys.version_info >= (3, 10):
         port: int | str | None = None,
         *,
         limit: int = 65536,
-        ssl_handshake_timeout: float | None = ...,
+        ssl_handshake_timeout: float | None = None,
         **kwds: Any,
     ) -> tuple[StreamReader, StreamWriter]: ...
     async def start_server(
@@ -43,7 +43,7 @@ if sys.version_info >= (3, 10):
         port: int | str | None = None,
         *,
         limit: int = 65536,
-        ssl_handshake_timeout: float | None = ...,
+        ssl_handshake_timeout: float | None = None,
         **kwds: Any,
     ) -> Server: ...
 
@@ -54,7 +54,7 @@ else:
         *,
         loop: events.AbstractEventLoop | None = None,
         limit: int = 65536,
-        ssl_handshake_timeout: float | None = ...,
+        ssl_handshake_timeout: float | None = None,
         **kwds: Any,
     ) -> tuple[StreamReader, StreamWriter]: ...
     async def start_server(
@@ -64,7 +64,7 @@ else:
         *,
         loop: events.AbstractEventLoop | None = None,
         limit: int = 65536,
-        ssl_handshake_timeout: float | None = ...,
+        ssl_handshake_timeout: float | None = None,
         **kwds: Any,
     ) -> Server: ...
 
diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi
index 50d75391f36d..ceee2b5b90a0 100644
--- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi
+++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi
@@ -60,7 +60,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
@@ -92,7 +92,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
@@ -126,7 +126,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
@@ -157,7 +157,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
@@ -191,7 +191,7 @@ else:  # >= 3.9
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
@@ -222,7 +222,7 @@ else:  # >= 3.9
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         group: None | str | int = None,
         extra_groups: None | Collection[str | int] = None,
         user: None | str | int = None,
diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi
index 4104b3ecfeee..1442f7400a9c 100644
--- a/mypy/typeshed/stdlib/asyncio/tasks.pyi
+++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi
@@ -8,7 +8,7 @@ from _asyncio import (
     _unregister_task as _unregister_task,
 )
 from collections.abc import AsyncIterator, Awaitable, Coroutine, Generator, Iterable, Iterator
-from typing import Any, Literal, Protocol, TypeVar, overload, type_check_only
+from typing import Any, Final, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias
 
 from . import _CoroutineLike
@@ -82,9 +82,9 @@ else:
 
 _TaskYieldType: TypeAlias = Future[object] | None
 
-FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
-FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION
-ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
+FIRST_COMPLETED: Final = concurrent.futures.FIRST_COMPLETED
+FIRST_EXCEPTION: Final = concurrent.futures.FIRST_EXCEPTION
+ALL_COMPLETED: Final = concurrent.futures.ALL_COMPLETED
 
 if sys.version_info >= (3, 13):
     @type_check_only
diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi
index bce54897f18f..cc870d5e0b9a 100644
--- a/mypy/typeshed/stdlib/asyncio/transports.pyi
+++ b/mypy/typeshed/stdlib/asyncio/transports.pyi
@@ -8,6 +8,7 @@ from typing import Any
 __all__ = ("BaseTransport", "ReadTransport", "WriteTransport", "Transport", "DatagramTransport", "SubprocessTransport")
 
 class BaseTransport:
+    __slots__ = ("_extra",)
     def __init__(self, extra: Mapping[str, Any] | None = None) -> None: ...
     def get_extra_info(self, name: str, default: Any = None) -> Any: ...
     def is_closing(self) -> bool: ...
@@ -16,11 +17,13 @@ class BaseTransport:
     def get_protocol(self) -> BaseProtocol: ...
 
 class ReadTransport(BaseTransport):
+    __slots__ = ()
     def is_reading(self) -> bool: ...
     def pause_reading(self) -> None: ...
     def resume_reading(self) -> None: ...
 
 class WriteTransport(BaseTransport):
+    __slots__ = ()
     def set_write_buffer_limits(self, high: int | None = None, low: int | None = None) -> None: ...
     def get_write_buffer_size(self) -> int: ...
     def get_write_buffer_limits(self) -> tuple[int, int]: ...
@@ -32,13 +35,16 @@ class WriteTransport(BaseTransport):
     def can_write_eof(self) -> bool: ...
     def abort(self) -> None: ...
 
-class Transport(ReadTransport, WriteTransport): ...
+class Transport(ReadTransport, WriteTransport):
+    __slots__ = ()
 
 class DatagramTransport(BaseTransport):
+    __slots__ = ()
     def sendto(self, data: bytes | bytearray | memoryview, addr: _Address | None = None) -> None: ...
     def abort(self) -> None: ...
 
 class SubprocessTransport(BaseTransport):
+    __slots__ = ()
     def get_pid(self) -> int: ...
     def get_returncode(self) -> int | None: ...
     def get_pipe_transport(self, fd: int) -> BaseTransport | None: ...
@@ -47,4 +53,5 @@ class SubprocessTransport(BaseTransport):
     def kill(self) -> None: ...
 
 class _FlowControlMixin(Transport):
+    __slots__ = ("_loop", "_protocol_paused", "_high_water", "_low_water")
     def __init__(self, extra: Mapping[str, Any] | None = None, loop: AbstractEventLoop | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/trsock.pyi b/mypy/typeshed/stdlib/asyncio/trsock.pyi
index 4dacbbd49399..492f1e42adf2 100644
--- a/mypy/typeshed/stdlib/asyncio/trsock.pyi
+++ b/mypy/typeshed/stdlib/asyncio/trsock.pyi
@@ -14,6 +14,7 @@ _WriteBuffer: TypeAlias = bytearray | memoryview
 _CMSG: TypeAlias = tuple[int, int, bytes]
 
 class TransportSocket:
+    __slots__ = ("_sock",)
     def __init__(self, sock: socket.socket) -> None: ...
     @property
     def family(self) -> int: ...
@@ -62,7 +63,7 @@ class TransportSocket:
         @deprecated("Removed in Python 3.11")
         def makefile(self) -> BinaryIO: ...
         @deprecated("Rmoved in Python 3.11")
-        def sendfile(self, file: BinaryIO, offset: int = ..., count: int | None = ...) -> int: ...
+        def sendfile(self, file: BinaryIO, offset: int = 0, count: int | None = None) -> int: ...
         @deprecated("Removed in Python 3.11")
         def close(self) -> None: ...
         @deprecated("Removed in Python 3.11")
@@ -70,17 +71,22 @@ class TransportSocket:
         if sys.platform == "linux":
             @deprecated("Removed in Python 3.11")
             def sendmsg_afalg(
-                self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
+                self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0
             ) -> int: ...
         else:
             @deprecated("Removed in Python 3.11.")
             def sendmsg_afalg(
-                self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
+                self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0
             ) -> NoReturn: ...
 
         @deprecated("Removed in Python 3.11.")
         def sendmsg(
-            self, buffers: Iterable[ReadableBuffer], ancdata: Iterable[_CMSG] = ..., flags: int = ..., address: _Address = ..., /
+            self,
+            buffers: Iterable[ReadableBuffer],
+            ancdata: Iterable[_CMSG] = ...,
+            flags: int = 0,
+            address: _Address | None = None,
+            /,
         ) -> int: ...
         @overload
         @deprecated("Removed in Python 3.11.")
@@ -89,9 +95,9 @@ class TransportSocket:
         @deprecated("Removed in Python 3.11.")
         def sendto(self, data: ReadableBuffer, flags: int, address: _Address) -> int: ...
         @deprecated("Removed in Python 3.11.")
-        def send(self, data: ReadableBuffer, flags: int = ...) -> int: ...
+        def send(self, data: ReadableBuffer, flags: int = 0) -> int: ...
         @deprecated("Removed in Python 3.11.")
-        def sendall(self, data: ReadableBuffer, flags: int = ...) -> None: ...
+        def sendall(self, data: ReadableBuffer, flags: int = 0) -> None: ...
         @deprecated("Removed in Python 3.11.")
         def set_inheritable(self, inheritable: bool) -> None: ...
         if sys.platform == "win32":
@@ -102,19 +108,19 @@ class TransportSocket:
             def share(self, process_id: int) -> NoReturn: ...
 
         @deprecated("Removed in Python 3.11.")
-        def recv_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> int: ...
+        def recv_into(self, buffer: _WriteBuffer, nbytes: int = 0, flags: int = 0) -> int: ...
         @deprecated("Removed in Python 3.11.")
-        def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ...
+        def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = 0, flags: int = 0) -> tuple[int, _RetAddress]: ...
         @deprecated("Removed in Python 3.11.")
         def recvmsg_into(
-            self, buffers: Iterable[_WriteBuffer], ancbufsize: int = ..., flags: int = ..., /
+            self, buffers: Iterable[_WriteBuffer], ancbufsize: int = 0, flags: int = 0, /
         ) -> tuple[int, list[_CMSG], int, Any]: ...
         @deprecated("Removed in Python 3.11.")
-        def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ...
+        def recvmsg(self, bufsize: int, ancbufsize: int = 0, flags: int = 0, /) -> tuple[bytes, list[_CMSG], int, Any]: ...
         @deprecated("Removed in Python 3.11.")
-        def recvfrom(self, bufsize: int, flags: int = ...) -> tuple[bytes, _RetAddress]: ...
+        def recvfrom(self, bufsize: int, flags: int = 0) -> tuple[bytes, _RetAddress]: ...
         @deprecated("Removed in Python 3.11.")
-        def recv(self, bufsize: int, flags: int = ...) -> bytes: ...
+        def recv(self, bufsize: int, flags: int = 0) -> bytes: ...
         @deprecated("Removed in Python 3.11.")
         def __enter__(self) -> socket.socket: ...
         @deprecated("Removed in Python 3.11.")
diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
index b2bf22a27677..9071ee9a2fa7 100644
--- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi
@@ -48,7 +48,7 @@ if sys.platform != "win32":
 # So, it is special cased.
 if sys.version_info < (3, 14):
     if sys.version_info >= (3, 12):
-        @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+        @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
         class AbstractChildWatcher:
             @abstractmethod
             def add_child_handler(
@@ -100,7 +100,7 @@ if sys.platform != "win32":
                 def is_active(self) -> bool: ...
                 def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ...
 
-            @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
             class SafeChildWatcher(BaseChildWatcher):
                 def __enter__(self) -> Self: ...
                 def __exit__(
@@ -111,7 +111,7 @@ if sys.platform != "win32":
                 ) -> None: ...
                 def remove_child_handler(self, pid: int) -> bool: ...
 
-            @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
             class FastChildWatcher(BaseChildWatcher):
                 def __enter__(self) -> Self: ...
                 def __exit__(
@@ -171,9 +171,9 @@ if sys.platform != "win32":
     else:
         class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
             if sys.version_info >= (3, 12):
-                @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+                @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
                 def get_child_watcher(self) -> AbstractChildWatcher: ...
-                @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+                @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
                 def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ...
             else:
                 def get_child_watcher(self) -> AbstractChildWatcher: ...
@@ -191,7 +191,7 @@ if sys.platform != "win32":
 
     if sys.version_info < (3, 14):
         if sys.version_info >= (3, 12):
-            @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14")
+            @deprecated("Deprecated since Python 3.12; removed in Python 3.14.")
             class MultiLoopChildWatcher(AbstractChildWatcher):
                 def is_active(self) -> bool: ...
                 def close(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi
index b454aca1f262..a32381bfb3e6 100644
--- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi
+++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi
@@ -116,6 +116,6 @@ if sys.platform == "win32":
     if sys.version_info >= (3, 14):
         _DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy
     else:
-        DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy
+        DefaultEventLoopPolicy = WindowsProactorEventLoopPolicy
     if sys.version_info >= (3, 13):
         EventLoop = ProactorEventLoop
diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi
index 4fa014532376..5cedd61b5f4a 100644
--- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi
+++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi
@@ -9,8 +9,8 @@ if sys.platform == "win32":
     __all__ = ("pipe", "Popen", "PIPE", "PipeHandle")
 
     BUFSIZE: Final = 8192
-    PIPE = subprocess.PIPE
-    STDOUT = subprocess.STDOUT
+    PIPE: Final = subprocess.PIPE
+    STDOUT: Final = subprocess.STDOUT
     def pipe(*, duplex: bool = False, overlapped: tuple[bool, bool] = (True, True), bufsize: int = 8192) -> tuple[int, int]: ...
 
     class PipeHandle:
@@ -34,9 +34,9 @@ if sys.platform == "win32":
         def __new__(
             cls,
             args: subprocess._CMD,
-            stdin: subprocess._FILE | None = ...,
-            stdout: subprocess._FILE | None = ...,
-            stderr: subprocess._FILE | None = ...,
+            stdin: subprocess._FILE | None = None,
+            stdout: subprocess._FILE | None = None,
+            stderr: subprocess._FILE | None = None,
             **kwds: Any,
         ) -> Self: ...
         def __init__(
diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi
index e09d335596fc..5606d5cdf74d 100644
--- a/mypy/typeshed/stdlib/binascii.pyi
+++ b/mypy/typeshed/stdlib/binascii.pyi
@@ -31,8 +31,8 @@ if sys.version_info < (3, 11):
 
 def crc_hqx(data: ReadableBuffer, crc: int, /) -> int: ...
 def crc32(data: ReadableBuffer, crc: int = 0, /) -> int: ...
-def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ...
-def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ...
+def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = 1) -> bytes: ...
+def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = 1) -> bytes: ...
 def a2b_hex(hexstr: _AsciiBuffer, /) -> bytes: ...
 def unhexlify(hexstr: _AsciiBuffer, /) -> bytes: ...
 
diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi
index d7c0fe27c1ee..ca8d56cb4297 100644
--- a/mypy/typeshed/stdlib/builtins.pyi
+++ b/mypy/typeshed/stdlib/builtins.pyi
@@ -70,6 +70,7 @@ from typing_extensions import (  # noqa: Y023
     TypeIs,
     TypeVarTuple,
     deprecated,
+    disjoint_base,
 )
 
 if sys.version_info >= (3, 14):
@@ -102,6 +103,7 @@ _StopT_co = TypeVar("_StopT_co", covariant=True, default=_StartT_co)  #  slice[A
 # FIXME: https://github.com/python/typing/issues/213 (replace step=start|stop with step=start&stop)
 _StepT_co = TypeVar("_StepT_co", covariant=True, default=_StartT_co | _StopT_co)  #  slice[A,B] -> slice[A, B, A|B]
 
+@disjoint_base
 class object:
     __doc__: str | None
     __dict__: dict[str, Any]
@@ -137,6 +139,7 @@ class object:
     @classmethod
     def __subclasshook__(cls, subclass: type, /) -> bool: ...
 
+@disjoint_base
 class staticmethod(Generic[_P, _R_co]):
     @property
     def __func__(self) -> Callable[_P, _R_co]: ...
@@ -157,6 +160,7 @@ class staticmethod(Generic[_P, _R_co]):
         def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
         __annotate__: AnnotateFunc | None
 
+@disjoint_base
 class classmethod(Generic[_T, _P, _R_co]):
     @property
     def __func__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ...
@@ -176,6 +180,7 @@ class classmethod(Generic[_T, _P, _R_co]):
         def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
         __annotate__: AnnotateFunc | None
 
+@disjoint_base
 class type:
     # object.__base__ is None. Otherwise, it would be a type.
     @property
@@ -228,6 +233,7 @@ class type:
     if sys.version_info >= (3, 14):
         __annotate__: AnnotateFunc | None
 
+@disjoint_base
 class super:
     @overload
     def __init__(self, t: Any, obj: Any, /) -> None: ...
@@ -240,6 +246,7 @@ _PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
 _NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20]
 _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0]  # noqa: Y026  # TODO: Use TypeAlias once mypy bugs are fixed
 
+@disjoint_base
 class int:
     @overload
     def __new__(cls, x: ConvertibleToInt = ..., /) -> Self: ...
@@ -350,6 +357,7 @@ class int:
     def __index__(self) -> int: ...
     def __format__(self, format_spec: str, /) -> str: ...
 
+@disjoint_base
 class float:
     def __new__(cls, x: ConvertibleToFloat = ..., /) -> Self: ...
     def as_integer_ratio(self) -> tuple[int, int]: ...
@@ -415,6 +423,7 @@ class float:
         @classmethod
         def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ...
 
+@disjoint_base
 class complex:
     # Python doesn't currently accept SupportsComplex for the second argument
     @overload
@@ -462,6 +471,7 @@ class _FormatMapMapping(Protocol):
 class _TranslateTable(Protocol):
     def __getitem__(self, key: int, /) -> str | int | None: ...
 
+@disjoint_base
 class str(Sequence[str]):
     @overload
     def __new__(cls, object: object = ...) -> Self: ...
@@ -549,6 +559,7 @@ class str(Sequence[str]):
     def __getnewargs__(self) -> tuple[str]: ...
     def __format__(self, format_spec: str, /) -> str: ...
 
+@disjoint_base
 class bytes(Sequence[int]):
     @overload
     def __new__(cls, o: Iterable[SupportsIndex] | SupportsIndex | SupportsBytes | ReadableBuffer, /) -> Self: ...
@@ -647,6 +658,7 @@ class bytes(Sequence[int]):
 
     def __buffer__(self, flags: int, /) -> memoryview: ...
 
+@disjoint_base
 class bytearray(MutableSequence[int]):
     @overload
     def __init__(self) -> None: ...
@@ -911,6 +923,8 @@ class slice(Generic[_StartT_co, _StopT_co, _StepT_co]):
 
     def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ...
 
+# Making this a disjoint_base upsets pyright
+# @disjoint_base
 class tuple(Sequence[_T_co]):
     def __new__(cls, iterable: Iterable[_T_co] = ..., /) -> Self: ...
     def __len__(self) -> int: ...
@@ -987,6 +1001,7 @@ class function:
     # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any.
     def __get__(self, instance: object, owner: type | None = None, /) -> Any: ...
 
+@disjoint_base
 class list(MutableSequence[_T]):
     @overload
     def __init__(self) -> None: ...
@@ -1041,6 +1056,7 @@ class list(MutableSequence[_T]):
     def __eq__(self, value: object, /) -> bool: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
+@disjoint_base
 class dict(MutableMapping[_KT, _VT]):
     # __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics
     # Also multiprocessing.managers.SyncManager.dict()
@@ -1123,6 +1139,7 @@ class dict(MutableMapping[_KT, _VT]):
     @overload
     def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
 
+@disjoint_base
 class set(MutableSet[_T]):
     @overload
     def __init__(self) -> None: ...
@@ -1162,6 +1179,7 @@ class set(MutableSet[_T]):
     __hash__: ClassVar[None]  # type: ignore[assignment]
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
+@disjoint_base
 class frozenset(AbstractSet[_T_co]):
     @overload
     def __new__(cls) -> Self: ...
@@ -1190,6 +1208,7 @@ class frozenset(AbstractSet[_T_co]):
     def __hash__(self) -> int: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
+@disjoint_base
 class enumerate(Iterator[tuple[int, _T]]):
     def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
     def __iter__(self) -> Self: ...
@@ -1221,6 +1240,7 @@ class range(Sequence[int]):
     def __getitem__(self, key: slice, /) -> range: ...
     def __reversed__(self) -> Iterator[int]: ...
 
+@disjoint_base
 class property:
     fget: Callable[[Any], Any] | None
     fset: Callable[[Any, Any], None] | None
@@ -1384,6 +1404,7 @@ else:
 
 exit: _sitebuiltins.Quitter
 
+@disjoint_base
 class filter(Iterator[_T]):
     @overload
     def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ...
@@ -1447,7 +1468,7 @@ def len(obj: Sized, /) -> int: ...
 license: _sitebuiltins._Printer
 
 def locals() -> dict[str, Any]: ...
-
+@disjoint_base
 class map(Iterator[_S]):
     # 3.14 adds `strict` argument.
     if sys.version_info >= (3, 14):
@@ -1754,6 +1775,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
 
 quit: _sitebuiltins.Quitter
 
+@disjoint_base
 class reversed(Iterator[_T]):
     @overload
     def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ...  # type: ignore[misc]
@@ -1817,7 +1839,7 @@ def sum(iterable: Iterable[_AddableT1], /, start: _AddableT2) -> _AddableT1 | _A
 def vars(object: type, /) -> types.MappingProxyType[str, Any]: ...
 @overload
 def vars(object: Any = ..., /) -> dict[str, Any]: ...
-
+@disjoint_base
 class zip(Iterator[_T_co]):
     if sys.version_info >= (3, 10):
         @overload
@@ -1921,6 +1943,7 @@ else:
 
     Ellipsis: ellipsis
 
+@disjoint_base
 class BaseException:
     args: tuple[Any, ...]
     __cause__: BaseException | None
@@ -1939,14 +1962,17 @@ class BaseException:
 class GeneratorExit(BaseException): ...
 class KeyboardInterrupt(BaseException): ...
 
+@disjoint_base
 class SystemExit(BaseException):
     code: sys._ExitCode
 
 class Exception(BaseException): ...
 
+@disjoint_base
 class StopIteration(Exception):
     value: Any
 
+@disjoint_base
 class OSError(Exception):
     errno: int | None
     strerror: str | None
@@ -1964,15 +1990,20 @@ if sys.platform == "win32":
 class ArithmeticError(Exception): ...
 class AssertionError(Exception): ...
 
-class AttributeError(Exception):
-    if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 10):
+    @disjoint_base
+    class AttributeError(Exception):
         def __init__(self, *args: object, name: str | None = ..., obj: object = ...) -> None: ...
         name: str
         obj: object
 
+else:
+    class AttributeError(Exception): ...
+
 class BufferError(Exception): ...
 class EOFError(Exception): ...
 
+@disjoint_base
 class ImportError(Exception):
     def __init__(self, *args: object, name: str | None = ..., path: str | None = ...) -> None: ...
     name: str | None
@@ -1984,15 +2015,20 @@ class ImportError(Exception):
 class LookupError(Exception): ...
 class MemoryError(Exception): ...
 
-class NameError(Exception):
-    if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 10):
+    @disjoint_base
+    class NameError(Exception):
         def __init__(self, *args: object, name: str | None = ...) -> None: ...
         name: str
 
+else:
+    class NameError(Exception): ...
+
 class ReferenceError(Exception): ...
 class RuntimeError(Exception): ...
 class StopAsyncIteration(Exception): ...
 
+@disjoint_base
 class SyntaxError(Exception):
     msg: str
     filename: str | None
@@ -2056,6 +2092,7 @@ class IndentationError(SyntaxError): ...
 class TabError(IndentationError): ...
 class UnicodeError(ValueError): ...
 
+@disjoint_base
 class UnicodeDecodeError(UnicodeError):
     encoding: str
     object: bytes
@@ -2064,6 +2101,7 @@ class UnicodeDecodeError(UnicodeError):
     reason: str
     def __init__(self, encoding: str, object: ReadableBuffer, start: int, end: int, reason: str, /) -> None: ...
 
+@disjoint_base
 class UnicodeEncodeError(UnicodeError):
     encoding: str
     object: str
@@ -2072,6 +2110,7 @@ class UnicodeEncodeError(UnicodeError):
     reason: str
     def __init__(self, encoding: str, object: str, start: int, end: int, reason: str, /) -> None: ...
 
+@disjoint_base
 class UnicodeTranslateError(UnicodeError):
     encoding: None
     object: str
@@ -2102,6 +2141,7 @@ if sys.version_info >= (3, 11):
     _ExceptionT = TypeVar("_ExceptionT", bound=Exception)
 
     # See `check_exception_group.py` for use-cases and comments.
+    @disjoint_base
     class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]):
         def __new__(cls, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> Self: ...
         def __init__(self, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> None: ...
diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi
index cabf3b881c30..d00f0d5d2bce 100644
--- a/mypy/typeshed/stdlib/calendar.pyi
+++ b/mypy/typeshed/stdlib/calendar.pyi
@@ -167,18 +167,18 @@ if sys.version_info >= (3, 12):
         NOVEMBER = 11
         DECEMBER = 12
 
-    JANUARY = Month.JANUARY
-    FEBRUARY = Month.FEBRUARY
-    MARCH = Month.MARCH
-    APRIL = Month.APRIL
-    MAY = Month.MAY
-    JUNE = Month.JUNE
-    JULY = Month.JULY
-    AUGUST = Month.AUGUST
-    SEPTEMBER = Month.SEPTEMBER
-    OCTOBER = Month.OCTOBER
-    NOVEMBER = Month.NOVEMBER
-    DECEMBER = Month.DECEMBER
+    JANUARY: Final = Month.JANUARY
+    FEBRUARY: Final = Month.FEBRUARY
+    MARCH: Final = Month.MARCH
+    APRIL: Final = Month.APRIL
+    MAY: Final = Month.MAY
+    JUNE: Final = Month.JUNE
+    JULY: Final = Month.JULY
+    AUGUST: Final = Month.AUGUST
+    SEPTEMBER: Final = Month.SEPTEMBER
+    OCTOBER: Final = Month.OCTOBER
+    NOVEMBER: Final = Month.NOVEMBER
+    DECEMBER: Final = Month.DECEMBER
 
     class Day(enum.IntEnum):
         MONDAY = 0
@@ -189,13 +189,13 @@ if sys.version_info >= (3, 12):
         SATURDAY = 5
         SUNDAY = 6
 
-    MONDAY = Day.MONDAY
-    TUESDAY = Day.TUESDAY
-    WEDNESDAY = Day.WEDNESDAY
-    THURSDAY = Day.THURSDAY
-    FRIDAY = Day.FRIDAY
-    SATURDAY = Day.SATURDAY
-    SUNDAY = Day.SUNDAY
+    MONDAY: Final = Day.MONDAY
+    TUESDAY: Final = Day.TUESDAY
+    WEDNESDAY: Final = Day.WEDNESDAY
+    THURSDAY: Final = Day.THURSDAY
+    FRIDAY: Final = Day.FRIDAY
+    SATURDAY: Final = Day.SATURDAY
+    SUNDAY: Final = Day.SUNDAY
 else:
     MONDAY: Final = 0
     TUESDAY: Final = 1
diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi
index a7a95a139330..0f9d4343b630 100644
--- a/mypy/typeshed/stdlib/cgi.pyi
+++ b/mypy/typeshed/stdlib/cgi.pyi
@@ -1,3 +1,4 @@
+import os
 from _typeshed import SupportsContainsAndGetItem, SupportsGetItem, SupportsItemAccess, Unused
 from builtins import list as _list, type as _type
 from collections.abc import Iterable, Iterator, Mapping
@@ -23,7 +24,7 @@ __all__ = [
 
 def parse(
     fp: IO[Any] | None = None,
-    environ: SupportsItemAccess[str, str] = ...,
+    environ: SupportsItemAccess[str, str] = os.environ,
     keep_blank_values: bool = ...,
     strict_parsing: bool = ...,
     separator: str = "&",
@@ -37,8 +38,8 @@ class _Environ(Protocol):
     def keys(self) -> Iterable[str]: ...
 
 def parse_header(line: str) -> tuple[str, dict[str, str]]: ...
-def test(environ: _Environ = ...) -> None: ...
-def print_environ(environ: _Environ = ...) -> None: ...
+def test(environ: _Environ = os.environ) -> None: ...
+def print_environ(environ: _Environ = os.environ) -> None: ...
 def print_form(form: dict[str, Any]) -> None: ...
 def print_directory() -> None: ...
 def print_environ_usage() -> None: ...
@@ -85,7 +86,7 @@ class FieldStorage:
         fp: IO[Any] | None = None,
         headers: Mapping[str, str] | Message | None = None,
         outerboundary: bytes = b"",
-        environ: SupportsContainsAndGetItem[str, str] = ...,
+        environ: SupportsContainsAndGetItem[str, str] = os.environ,
         keep_blank_values: int = 0,
         strict_parsing: int = 0,
         limit: int | None = None,
diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi
index 15e184fc1038..fa4d4fd4ba92 100644
--- a/mypy/typeshed/stdlib/codecs.pyi
+++ b/mypy/typeshed/stdlib/codecs.pyi
@@ -1,10 +1,11 @@
+import sys
 import types
 from _codecs import *
 from _typeshed import ReadableBuffer
 from abc import abstractmethod
 from collections.abc import Callable, Generator, Iterable
 from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO, overload, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 __all__ = [
     "register",
@@ -122,33 +123,64 @@ class _IncrementalDecoder(Protocol):
 class _BufferedIncrementalDecoder(Protocol):
     def __call__(self, errors: str = ...) -> BufferedIncrementalDecoder: ...
 
-class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]):
-    _is_text_encoding: bool
-    @property
-    def encode(self) -> _Encoder: ...
-    @property
-    def decode(self) -> _Decoder: ...
-    @property
-    def streamreader(self) -> _StreamReader: ...
-    @property
-    def streamwriter(self) -> _StreamWriter: ...
-    @property
-    def incrementalencoder(self) -> _IncrementalEncoder: ...
-    @property
-    def incrementaldecoder(self) -> _IncrementalDecoder: ...
-    name: str
-    def __new__(
-        cls,
-        encode: _Encoder,
-        decode: _Decoder,
-        streamreader: _StreamReader | None = None,
-        streamwriter: _StreamWriter | None = None,
-        incrementalencoder: _IncrementalEncoder | None = None,
-        incrementaldecoder: _IncrementalDecoder | None = None,
-        name: str | None = None,
-        *,
-        _is_text_encoding: bool | None = None,
-    ) -> Self: ...
+if sys.version_info >= (3, 12):
+    class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]):
+        _is_text_encoding: bool
+        @property
+        def encode(self) -> _Encoder: ...
+        @property
+        def decode(self) -> _Decoder: ...
+        @property
+        def streamreader(self) -> _StreamReader: ...
+        @property
+        def streamwriter(self) -> _StreamWriter: ...
+        @property
+        def incrementalencoder(self) -> _IncrementalEncoder: ...
+        @property
+        def incrementaldecoder(self) -> _IncrementalDecoder: ...
+        name: str
+        def __new__(
+            cls,
+            encode: _Encoder,
+            decode: _Decoder,
+            streamreader: _StreamReader | None = None,
+            streamwriter: _StreamWriter | None = None,
+            incrementalencoder: _IncrementalEncoder | None = None,
+            incrementaldecoder: _IncrementalDecoder | None = None,
+            name: str | None = None,
+            *,
+            _is_text_encoding: bool | None = None,
+        ) -> Self: ...
+
+else:
+    @disjoint_base
+    class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]):
+        _is_text_encoding: bool
+        @property
+        def encode(self) -> _Encoder: ...
+        @property
+        def decode(self) -> _Decoder: ...
+        @property
+        def streamreader(self) -> _StreamReader: ...
+        @property
+        def streamwriter(self) -> _StreamWriter: ...
+        @property
+        def incrementalencoder(self) -> _IncrementalEncoder: ...
+        @property
+        def incrementaldecoder(self) -> _IncrementalDecoder: ...
+        name: str
+        def __new__(
+            cls,
+            encode: _Encoder,
+            decode: _Decoder,
+            streamreader: _StreamReader | None = None,
+            streamwriter: _StreamWriter | None = None,
+            incrementalencoder: _IncrementalEncoder | None = None,
+            incrementaldecoder: _IncrementalDecoder | None = None,
+            name: str | None = None,
+            *,
+            _is_text_encoding: bool | None = None,
+        ) -> Self: ...
 
 def getencoder(encoding: str) -> _Encoder: ...
 def getdecoder(encoding: str) -> _Decoder: ...
diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi
index df9449ef4c9b..8636e6cdbdc3 100644
--- a/mypy/typeshed/stdlib/collections/__init__.pyi
+++ b/mypy/typeshed/stdlib/collections/__init__.pyi
@@ -3,7 +3,7 @@ from _collections_abc import dict_items, dict_keys, dict_values
 from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT
 from types import GenericAlias
 from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only
-from typing_extensions import Self
+from typing_extensions import Self, disjoint_base
 
 if sys.version_info >= (3, 10):
     from collections.abc import (
@@ -231,6 +231,7 @@ class UserString(Sequence[UserString]):
     def upper(self) -> Self: ...
     def zfill(self, width: int) -> Self: ...
 
+@disjoint_base
 class deque(MutableSequence[_T]):
     @property
     def maxlen(self) -> int | None: ...
@@ -356,6 +357,7 @@ class _odict_items(dict_items[_KT_co, _VT_co]):  # type: ignore[misc]  # pyright
 class _odict_values(dict_values[_KT_co, _VT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
     def __reversed__(self) -> Iterator[_VT_co]: ...
 
+@disjoint_base
 class OrderedDict(dict[_KT, _VT]):
     def popitem(self, last: bool = True) -> tuple[_KT, _VT]: ...
     def move_to_end(self, key: _KT, last: bool = True) -> None: ...
@@ -395,6 +397,7 @@ class OrderedDict(dict[_KT, _VT]):
     @overload
     def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ...  # type: ignore[misc]
 
+@disjoint_base
 class defaultdict(dict[_KT, _VT]):
     default_factory: Callable[[], _VT] | None
     @overload
@@ -477,9 +480,15 @@ class ChainMap(MutableMapping[_KT, _VT]):
     __copy__ = copy
     # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime,
     # so the signature should be kept in line with `dict.fromkeys`.
-    @classmethod
-    @overload
-    def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ...
+    if sys.version_info >= (3, 13):
+        @classmethod
+        @overload
+        def fromkeys(cls, iterable: Iterable[_T], /) -> ChainMap[_T, Any | None]: ...
+    else:
+        @classmethod
+        @overload
+        def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ...
+
     @classmethod
     @overload
     # Special-case None: the user probably wants to add non-None values later.
diff --git a/mypy/typeshed/stdlib/colorsys.pyi b/mypy/typeshed/stdlib/colorsys.pyi
index 7842f80284ef..4afcb5392b58 100644
--- a/mypy/typeshed/stdlib/colorsys.pyi
+++ b/mypy/typeshed/stdlib/colorsys.pyi
@@ -1,3 +1,5 @@
+from typing import Final
+
 __all__ = ["rgb_to_yiq", "yiq_to_rgb", "rgb_to_hls", "hls_to_rgb", "rgb_to_hsv", "hsv_to_rgb"]
 
 def rgb_to_yiq(r: float, g: float, b: float) -> tuple[float, float, float]: ...
@@ -8,6 +10,6 @@ def rgb_to_hsv(r: float, g: float, b: float) -> tuple[float, float, float]: ...
 def hsv_to_rgb(h: float, s: float, v: float) -> tuple[float, float, float]: ...
 
 # TODO: undocumented
-ONE_SIXTH: float
-ONE_THIRD: float
-TWO_THIRD: float
+ONE_SIXTH: Final[float]
+ONE_THIRD: Final[float]
+TWO_THIRD: Final[float]
diff --git a/mypy/typeshed/stdlib/compression/zstd/__init__.pyi b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi
index 24a9633c488e..d5da4be03612 100644
--- a/mypy/typeshed/stdlib/compression/zstd/__init__.pyi
+++ b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi
@@ -35,6 +35,7 @@ zstd_version_info: Final[tuple[int, int, int]]
 COMPRESSION_LEVEL_DEFAULT: Final = _zstd.ZSTD_CLEVEL_DEFAULT
 
 class FrameInfo:
+    __slots__ = ("decompressed_size", "dictionary_id")
     decompressed_size: int
     dictionary_id: int
     def __init__(self, decompressed_size: int, dictionary_id: int) -> None: ...
diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
index 4063027f3eed..be48a6e4289c 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi
@@ -15,8 +15,7 @@ RUNNING: Final = "RUNNING"
 CANCELLED: Final = "CANCELLED"
 CANCELLED_AND_NOTIFIED: Final = "CANCELLED_AND_NOTIFIED"
 FINISHED: Final = "FINISHED"
-_FUTURE_STATES: list[str]
-_STATE_TO_DESCRIPTION_MAP: dict[str, str]
+_STATE_TO_DESCRIPTION_MAP: Final[dict[str, str]]
 LOGGER: Logger
 
 class Error(Exception): ...
diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi
index 607990100369..071b3aba5d33 100644
--- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi
+++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi
@@ -5,7 +5,7 @@ from multiprocessing.context import BaseContext, Process
 from multiprocessing.queues import Queue, SimpleQueue
 from threading import Lock, Semaphore, Thread
 from types import TracebackType
-from typing import Any, Generic, TypeVar, overload
+from typing import Any, Final, Generic, TypeVar, overload
 from typing_extensions import TypeVarTuple, Unpack
 from weakref import ref
 
@@ -28,9 +28,9 @@ class _ThreadWakeup:
 
 def _python_exit() -> None: ...
 
-EXTRA_QUEUED_CALLS: int
+EXTRA_QUEUED_CALLS: Final = 1
 
-_MAX_WINDOWS_WORKERS: int
+_MAX_WINDOWS_WORKERS: Final = 61
 
 class _RemoteTraceback(Exception):
     tb: str
diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
index b073aefa7ca7..7cf1ea34786e 100644
--- a/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
+++ b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi
@@ -12,6 +12,7 @@ if sys.version_info >= (3, 13):  # needed to satisfy pyright checks for Python <
     classonly = classmethod
 
     class UnboundItem:
+        __slots__ = ()
         def __new__(cls) -> Never: ...
         @classonly
         def singleton(cls, kind: str, module: str, name: str = "UNBOUND") -> Self: ...
diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi
index 39a057ee9a7b..7493f87809c8 100644
--- a/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi
+++ b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi
@@ -51,8 +51,8 @@ if sys.version_info >= (3, 13):  # needed to satisfy pyright checks for Python <
             timeout: SupportsIndex | None = None,
             *,
             unbounditems: _AnyUnbound | None = None,
-            _delay: float = ...,
+            _delay: float = 0.01,
         ) -> None: ...
         def put_nowait(self, obj: object, *, unbounditems: _AnyUnbound | None = None) -> None: ...
-        def get(self, timeout: SupportsIndex | None = None, *, _delay: float = ...) -> object: ...
+        def get(self, timeout: SupportsIndex | None = None, *, _delay: float = 0.01) -> object: ...
         def get_nowait(self) -> object: ...
diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi
index b3a421003026..764a8a965ea2 100644
--- a/mypy/typeshed/stdlib/configparser.pyi
+++ b/mypy/typeshed/stdlib/configparser.pyi
@@ -449,10 +449,13 @@ class ParsingError(Error):
         def __init__(self, source: str) -> None: ...
     else:
         @overload
-        def __init__(self, source: str, filename: None = None) -> None: ...
+        def __init__(self, source: str) -> None: ...
+        @overload
+        @deprecated("The `filename` parameter removed in Python 3.12. Use `source` instead.")
+        def __init__(self, source: None, filename: str | None) -> None: ...
         @overload
         @deprecated("The `filename` parameter removed in Python 3.12. Use `source` instead.")
-        def __init__(self, source: None = None, filename: str = ...) -> None: ...
+        def __init__(self, source: None = None, *, filename: str | None) -> None: ...
 
     def append(self, lineno: int, line: str) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi
index c616c1f5bf19..383a1b7f334b 100644
--- a/mypy/typeshed/stdlib/contextlib.pyi
+++ b/mypy/typeshed/stdlib/contextlib.pyi
@@ -47,6 +47,7 @@ _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc)
 # allowlist for use as a Protocol.
 @runtime_checkable
 class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
+    __slots__ = ()
     def __enter__(self) -> _T_co: ...
     @abstractmethod
     def __exit__(
@@ -58,6 +59,7 @@ class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]):  # type: ignore[m
 # allowlist for use as a Protocol.
 @runtime_checkable
 class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
+    __slots__ = ()
     async def __aenter__(self) -> _T_co: ...
     @abstractmethod
     async def __aexit__(
diff --git a/mypy/typeshed/stdlib/crypt.pyi b/mypy/typeshed/stdlib/crypt.pyi
index bd22b5f8daba..f92632196989 100644
--- a/mypy/typeshed/stdlib/crypt.pyi
+++ b/mypy/typeshed/stdlib/crypt.pyi
@@ -1,5 +1,6 @@
 import sys
 from typing import Final, NamedTuple, type_check_only
+from typing_extensions import disjoint_base
 
 if sys.platform != "win32":
     @type_check_only
@@ -9,7 +10,12 @@ if sys.platform != "win32":
         salt_chars: int
         total_size: int
 
-    class _Method(_MethodBase): ...
+    if sys.version_info >= (3, 12):
+        class _Method(_MethodBase): ...
+    else:
+        @disjoint_base
+        class _Method(_MethodBase): ...
+
     METHOD_CRYPT: Final[_Method]
     METHOD_MD5: Final[_Method]
     METHOD_SHA256: Final[_Method]
diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi
index 15649da9ff73..9da972240abb 100644
--- a/mypy/typeshed/stdlib/ctypes/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi
@@ -26,7 +26,7 @@ from _ctypes import (
 from _typeshed import StrPath
 from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure
 from types import GenericAlias
-from typing import Any, ClassVar, Generic, Literal, TypeVar, overload, type_check_only
+from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload, type_check_only
 from typing_extensions import Self, TypeAlias, deprecated
 
 if sys.platform == "win32":
@@ -55,7 +55,7 @@ if sys.version_info >= (3, 14):
 else:
     from _ctypes import POINTER as POINTER, pointer as pointer
 
-DEFAULT_MODE: int
+DEFAULT_MODE: Final[int]
 
 class ArgumentError(Exception): ...
 
@@ -162,8 +162,14 @@ def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_
 c_buffer = create_string_buffer
 
 def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ...
-@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ...
+
+if sys.version_info >= (3, 13):
+    @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+    def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ...
+
+else:
+    def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ...
+
 def ARRAY(typ: _CT, len: int) -> Array[_CT]: ...  # Soft Deprecated, no plans to remove
 
 if sys.platform == "win32":
diff --git a/mypy/typeshed/stdlib/ctypes/_endian.pyi b/mypy/typeshed/stdlib/ctypes/_endian.pyi
index 144f5ba5dd40..97852f67aa6e 100644
--- a/mypy/typeshed/stdlib/ctypes/_endian.pyi
+++ b/mypy/typeshed/stdlib/ctypes/_endian.pyi
@@ -3,10 +3,14 @@ from ctypes import Structure, Union
 
 # At runtime, the native endianness is an alias for Structure,
 # while the other is a subclass with a metaclass added in.
-class BigEndianStructure(Structure): ...
+class BigEndianStructure(Structure):
+    __slots__ = ()
+
 class LittleEndianStructure(Structure): ...
 
 # Same thing for these: one is an alias of Union at runtime
 if sys.version_info >= (3, 11):
-    class BigEndianUnion(Union): ...
+    class BigEndianUnion(Union):
+        __slots__ = ()
+
     class LittleEndianUnion(Union): ...
diff --git a/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi
index bda5b5a7f4cc..c5dd95466063 100644
--- a/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi
+++ b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi
@@ -1 +1,3 @@
-__version__: str
+from typing import Final
+
+__version__: Final[str]
diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
index e9ed0df24dd1..0f0d61a396d5 100644
--- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi
+++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi
@@ -21,7 +21,7 @@ from ctypes import (
     c_wchar,
     c_wchar_p,
 )
-from typing import Any, TypeVar
+from typing import Any, Final, TypeVar
 from typing_extensions import Self, TypeAlias
 
 if sys.version_info >= (3, 12):
@@ -177,7 +177,7 @@ class MSG(Structure):
     pt: _CField[POINT, POINT, POINT]
 
 tagMSG = MSG
-MAX_PATH: int
+MAX_PATH: Final = 260
 
 class WIN32_FIND_DATAA(Structure):
     dwFileAttributes: _CIntLikeField[DWORD]
diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi
index 5c157fd7c2f6..2c0231c13087 100644
--- a/mypy/typeshed/stdlib/curses/__init__.pyi
+++ b/mypy/typeshed/stdlib/curses/__init__.pyi
@@ -14,12 +14,12 @@ _T = TypeVar("_T")
 _P = ParamSpec("_P")
 
 # available after calling `curses.initscr()`
-LINES: int
-COLS: int
+LINES: Final[int]
+COLS: Final[int]
 
 # available after calling `curses.start_color()`
-COLORS: int
-COLOR_PAIRS: int
+COLORS: Final[int]
+COLOR_PAIRS: Final[int]
 
 def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ...
 
diff --git a/mypy/typeshed/stdlib/curses/ascii.pyi b/mypy/typeshed/stdlib/curses/ascii.pyi
index 66efbe36a7df..0234434b8c3d 100644
--- a/mypy/typeshed/stdlib/curses/ascii.pyi
+++ b/mypy/typeshed/stdlib/curses/ascii.pyi
@@ -1,45 +1,45 @@
-from typing import TypeVar
+from typing import Final, TypeVar
 
 _CharT = TypeVar("_CharT", str, int)
 
-NUL: int
-SOH: int
-STX: int
-ETX: int
-EOT: int
-ENQ: int
-ACK: int
-BEL: int
-BS: int
-TAB: int
-HT: int
-LF: int
-NL: int
-VT: int
-FF: int
-CR: int
-SO: int
-SI: int
-DLE: int
-DC1: int
-DC2: int
-DC3: int
-DC4: int
-NAK: int
-SYN: int
-ETB: int
-CAN: int
-EM: int
-SUB: int
-ESC: int
-FS: int
-GS: int
-RS: int
-US: int
-SP: int
-DEL: int
+NUL: Final = 0x00
+SOH: Final = 0x01
+STX: Final = 0x02
+ETX: Final = 0x03
+EOT: Final = 0x04
+ENQ: Final = 0x05
+ACK: Final = 0x06
+BEL: Final = 0x07
+BS: Final = 0x08
+TAB: Final = 0x09
+HT: Final = 0x09
+LF: Final = 0x0A
+NL: Final = 0x0A
+VT: Final = 0x0B
+FF: Final = 0x0C
+CR: Final = 0x0D
+SO: Final = 0x0E
+SI: Final = 0x0F
+DLE: Final = 0x10
+DC1: Final = 0x11
+DC2: Final = 0x12
+DC3: Final = 0x13
+DC4: Final = 0x14
+NAK: Final = 0x15
+SYN: Final = 0x16
+ETB: Final = 0x17
+CAN: Final = 0x18
+EM: Final = 0x19
+SUB: Final = 0x1A
+ESC: Final = 0x1B
+FS: Final = 0x1C
+GS: Final = 0x1D
+RS: Final = 0x1E
+US: Final = 0x1F
+SP: Final = 0x20
+DEL: Final = 0x7F
 
-controlnames: list[int]
+controlnames: Final[list[int]]
 
 def isalnum(c: str | int) -> bool: ...
 def isalpha(c: str | int) -> bool: ...
diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi
index b3183f57ebd2..3a1c8cb5d62d 100644
--- a/mypy/typeshed/stdlib/dataclasses.pyi
+++ b/mypy/typeshed/stdlib/dataclasses.pyi
@@ -5,7 +5,7 @@ from _typeshed import DataclassInstance
 from builtins import type as Type  # alias to avoid name clashes with fields named "type"
 from collections.abc import Callable, Iterable, Mapping
 from types import GenericAlias
-from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only
+from typing import Any, Final, Generic, Literal, Protocol, TypeVar, overload, type_check_only
 from typing_extensions import Never, TypeIs
 
 _T = TypeVar("_T")
@@ -58,7 +58,7 @@ class _DataclassFactory(Protocol):
 class _MISSING_TYPE(enum.Enum):
     MISSING = enum.auto()
 
-MISSING = _MISSING_TYPE.MISSING
+MISSING: Final = _MISSING_TYPE.MISSING
 
 if sys.version_info >= (3, 10):
     class KW_ONLY: ...
@@ -170,6 +170,37 @@ class _DefaultFactory(Protocol[_T_co]):
     def __call__(self) -> _T_co: ...
 
 class Field(Generic[_T]):
+    if sys.version_info >= (3, 14):
+        __slots__ = (
+            "name",
+            "type",
+            "default",
+            "default_factory",
+            "repr",
+            "hash",
+            "init",
+            "compare",
+            "metadata",
+            "kw_only",
+            "doc",
+            "_field_type",
+        )
+    elif sys.version_info >= (3, 10):
+        __slots__ = (
+            "name",
+            "type",
+            "default",
+            "default_factory",
+            "repr",
+            "hash",
+            "init",
+            "compare",
+            "metadata",
+            "kw_only",
+            "_field_type",
+        )
+    else:
+        __slots__ = ("name", "type", "default", "default_factory", "repr", "hash", "init", "compare", "metadata", "_field_type")
     name: str
     type: Type[_T] | str | Any
     default: _T | Literal[_MISSING_TYPE.MISSING]
@@ -355,6 +386,7 @@ def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstan
 class FrozenInstanceError(AttributeError): ...
 
 class InitVar(Generic[_T]):
+    __slots__ = ("type",)
     type: Type[_T]
     def __init__(self, type: Type[_T]) -> None: ...
     @overload
diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi
index c54de6159b51..8a0536c006d5 100644
--- a/mypy/typeshed/stdlib/datetime.pyi
+++ b/mypy/typeshed/stdlib/datetime.pyi
@@ -2,7 +2,7 @@ import sys
 from abc import abstractmethod
 from time import struct_time
 from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only
-from typing_extensions import CapsuleType, Self, TypeAlias, deprecated
+from typing_extensions import CapsuleType, Self, TypeAlias, deprecated, disjoint_base
 
 if sys.version_info >= (3, 11):
     __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC")
@@ -51,6 +51,7 @@ class _IsoCalendarDate(tuple[int, int, int]):
     @property
     def weekday(self) -> int: ...
 
+@disjoint_base
 class date:
     min: ClassVar[date]
     max: ClassVar[date]
@@ -112,6 +113,7 @@ class date:
     def isoweekday(self) -> int: ...
     def isocalendar(self) -> _IsoCalendarDate: ...
 
+@disjoint_base
 class time:
     min: ClassVar[time]
     max: ClassVar[time]
@@ -191,6 +193,7 @@ class time:
 _Date: TypeAlias = date
 _Time: TypeAlias = time
 
+@disjoint_base
 class timedelta:
     min: ClassVar[timedelta]
     max: ClassVar[timedelta]
@@ -239,6 +242,7 @@ class timedelta:
     def __bool__(self) -> bool: ...
     def __hash__(self) -> int: ...
 
+@disjoint_base
 class datetime(date):
     min: ClassVar[datetime]
     max: ClassVar[datetime]
diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi
index b85c00080092..2e06c2d1b724 100644
--- a/mypy/typeshed/stdlib/decimal.pyi
+++ b/mypy/typeshed/stdlib/decimal.pyi
@@ -27,7 +27,7 @@ from _decimal import (
 from collections.abc import Container, Sequence
 from types import TracebackType
 from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 if sys.version_info >= (3, 14):
     from _decimal import IEEE_CONTEXT_MAX_BITS as IEEE_CONTEXT_MAX_BITS, IEEEContext as IEEEContext
@@ -68,6 +68,7 @@ class Overflow(Inexact, Rounded): ...
 class Underflow(Inexact, Rounded, Subnormal): ...
 class FloatOperation(DecimalException, TypeError): ...
 
+@disjoint_base
 class Decimal:
     def __new__(cls, value: _DecimalNew = "0", context: Context | None = None) -> Self: ...
     if sys.version_info >= (3, 14):
@@ -173,6 +174,7 @@ class Decimal:
     def __deepcopy__(self, memo: Any, /) -> Self: ...
     def __format__(self, specifier: str, context: Context | None = None, /) -> str: ...
 
+@disjoint_base
 class Context:
     # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime,
     # even settable attributes like `prec` and `rounding`,
diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi
index 86b6d01e3120..896b50fa9384 100644
--- a/mypy/typeshed/stdlib/dis.pyi
+++ b/mypy/typeshed/stdlib/dis.pyi
@@ -2,8 +2,8 @@ import sys
 import types
 from collections.abc import Callable, Iterator
 from opcode import *  # `dis` re-exports it as a part of public API
-from typing import IO, Any, NamedTuple
-from typing_extensions import Self, TypeAlias
+from typing import IO, Any, Final, NamedTuple
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 __all__ = [
     "code_info",
@@ -88,39 +88,45 @@ else:
         starts_line: int | None
         is_jump_target: bool
 
-class Instruction(_Instruction):
-    if sys.version_info < (3, 13):
+if sys.version_info >= (3, 12):
+    class Instruction(_Instruction):
+        if sys.version_info < (3, 13):
+            def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ...
+        if sys.version_info >= (3, 13):
+            @property
+            def oparg(self) -> int: ...
+            @property
+            def baseopcode(self) -> int: ...
+            @property
+            def baseopname(self) -> str: ...
+            @property
+            def cache_offset(self) -> int: ...
+            @property
+            def end_offset(self) -> int: ...
+            @property
+            def jump_target(self) -> int: ...
+            @property
+            def is_jump_target(self) -> bool: ...
+        if sys.version_info >= (3, 14):
+            @staticmethod
+            def make(
+                opname: str,
+                arg: int | None,
+                argval: Any,
+                argrepr: str,
+                offset: int,
+                start_offset: int,
+                starts_line: bool,
+                line_number: int | None,
+                label: int | None = None,
+                positions: Positions | None = None,
+                cache_info: list[tuple[str, int, Any]] | None = None,
+            ) -> Instruction: ...
+
+else:
+    @disjoint_base
+    class Instruction(_Instruction):
         def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ...
-    if sys.version_info >= (3, 13):
-        @property
-        def oparg(self) -> int: ...
-        @property
-        def baseopcode(self) -> int: ...
-        @property
-        def baseopname(self) -> str: ...
-        @property
-        def cache_offset(self) -> int: ...
-        @property
-        def end_offset(self) -> int: ...
-        @property
-        def jump_target(self) -> int: ...
-        @property
-        def is_jump_target(self) -> bool: ...
-    if sys.version_info >= (3, 14):
-        @staticmethod
-        def make(
-            opname: str,
-            arg: int | None,
-            argval: Any,
-            argrepr: str,
-            offset: int,
-            start_offset: int,
-            starts_line: bool,
-            line_number: int | None,
-            label: int | None = None,
-            positions: Positions | None = None,
-            cache_info: list[tuple[str, int, Any]] | None = None,
-        ) -> Instruction: ...
 
 class Bytecode:
     codeobj: types.CodeType
@@ -178,7 +184,7 @@ class Bytecode:
     def info(self) -> str: ...
     def dis(self) -> str: ...
 
-COMPILER_FLAG_NAMES: dict[int, str]
+COMPILER_FLAG_NAMES: Final[dict[int, str]]
 
 def findlabels(code: _HaveCodeType) -> list[int]: ...
 def findlinestarts(code: _HaveCodeType) -> Iterator[tuple[int, int]]: ...
diff --git a/mypy/typeshed/stdlib/distutils/file_util.pyi b/mypy/typeshed/stdlib/distutils/file_util.pyi
index 873d23ea7e50..c763f91a958d 100644
--- a/mypy/typeshed/stdlib/distutils/file_util.pyi
+++ b/mypy/typeshed/stdlib/distutils/file_util.pyi
@@ -29,10 +29,10 @@ def copy_file(
 ) -> tuple[_BytesPathT | bytes, bool]: ...
 @overload
 def move_file(
-    src: StrPath, dst: _StrPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0
+    src: StrPath, dst: _StrPathT, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0
 ) -> _StrPathT | str: ...
 @overload
 def move_file(
-    src: BytesPath, dst: _BytesPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0
+    src: BytesPath, dst: _BytesPathT, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0
 ) -> _BytesPathT | bytes: ...
 def write_file(filename: StrOrBytesPath, contents: Iterable[str]) -> None: ...
diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi
index 562b5a5bdac9..1bb96e1a7786 100644
--- a/mypy/typeshed/stdlib/doctest.pyi
+++ b/mypy/typeshed/stdlib/doctest.pyi
@@ -3,7 +3,7 @@ import types
 import unittest
 from _typeshed import ExcInfo
 from collections.abc import Callable
-from typing import Any, NamedTuple, type_check_only
+from typing import Any, Final, NamedTuple, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -57,29 +57,29 @@ else:
         failed: int
         attempted: int
 
-OPTIONFLAGS_BY_NAME: dict[str, int]
+OPTIONFLAGS_BY_NAME: Final[dict[str, int]]
 
 def register_optionflag(name: str) -> int: ...
 
-DONT_ACCEPT_TRUE_FOR_1: int
-DONT_ACCEPT_BLANKLINE: int
-NORMALIZE_WHITESPACE: int
-ELLIPSIS: int
-SKIP: int
-IGNORE_EXCEPTION_DETAIL: int
+DONT_ACCEPT_TRUE_FOR_1: Final = 1
+DONT_ACCEPT_BLANKLINE: Final = 2
+NORMALIZE_WHITESPACE: Final = 4
+ELLIPSIS: Final = 8
+SKIP: Final = 16
+IGNORE_EXCEPTION_DETAIL: Final = 32
 
-COMPARISON_FLAGS: int
+COMPARISON_FLAGS: Final = 63
 
-REPORT_UDIFF: int
-REPORT_CDIFF: int
-REPORT_NDIFF: int
-REPORT_ONLY_FIRST_FAILURE: int
-FAIL_FAST: int
+REPORT_UDIFF: Final = 64
+REPORT_CDIFF: Final = 128
+REPORT_NDIFF: Final = 256
+REPORT_ONLY_FIRST_FAILURE: Final = 512
+FAIL_FAST: Final = 1024
 
-REPORTING_FLAGS: int
+REPORTING_FLAGS: Final = 1984
 
-BLANKLINE_MARKER: str
-ELLIPSIS_MARKER: str
+BLANKLINE_MARKER: Final = ""
+ELLIPSIS_MARKER: Final = "..."
 
 class Example:
     source: str
diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
index 95ada186c4ec..dededd006e5b 100644
--- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi
+++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi
@@ -25,7 +25,7 @@ SPECIALSNL: Final[set[str]]
 def make_quoted_pairs(value: Any) -> str: ...
 def quote_string(value: Any) -> str: ...
 
-rfc2047_matcher: Pattern[str]
+rfc2047_matcher: Final[Pattern[str]]
 
 class TokenList(list[TokenList | Terminal]):
     token_type: str | None
diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi
index 683daa468cf3..e1930835bbd1 100644
--- a/mypy/typeshed/stdlib/email/charset.pyi
+++ b/mypy/typeshed/stdlib/email/charset.pyi
@@ -4,9 +4,16 @@ from typing import ClassVar, Final, overload
 
 __all__ = ["Charset", "add_alias", "add_charset", "add_codec"]
 
-QP: Final[int]  # undocumented
-BASE64: Final[int]  # undocumented
-SHORTEST: Final[int]  # undocumented
+QP: Final = 1  # undocumented
+BASE64: Final = 2  # undocumented
+SHORTEST: Final = 3  # undocumented
+RFC2047_CHROME_LEN: Final = 7  # undocumented
+DEFAULT_CHARSET: Final = "us-ascii"  # undocumented
+UNKNOWN8BIT: Final = "unknown-8bit"  # undocumented
+EMPTYSTRING: Final = ""  # undocumented
+CHARSETS: Final[dict[str, tuple[int | None, int | None, str | None]]]
+ALIASES: Final[dict[str, str]]
+CODEC_MAP: Final[dict[str, str | None]]  # undocumented
 
 class Charset:
     input_charset: str
diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi
index eb7d2e3819fd..4ac860f5e611 100644
--- a/mypy/typeshed/stdlib/enum.pyi
+++ b/mypy/typeshed/stdlib/enum.pyi
@@ -4,8 +4,8 @@ import types
 from _typeshed import SupportsKeysAndGetItem, Unused
 from builtins import property as _builtins_property
 from collections.abc import Callable, Iterable, Iterator, Mapping
-from typing import Any, Generic, Literal, TypeVar, overload
-from typing_extensions import Self, TypeAlias
+from typing import Any, Final, Generic, Literal, TypeVar, overload
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 __all__ = ["EnumMeta", "Enum", "IntEnum", "Flag", "IntFlag", "auto", "unique"]
 
@@ -228,16 +228,25 @@ class Enum(metaclass=EnumMeta):
 if sys.version_info >= (3, 11):
     class ReprEnum(Enum): ...
 
-if sys.version_info >= (3, 11):
-    _IntEnumBase = ReprEnum
+if sys.version_info >= (3, 12):
+    class IntEnum(int, ReprEnum):
+        _value_: int
+        @_magic_enum_attr
+        def value(self) -> int: ...
+        def __new__(cls, value: int) -> Self: ...
+
 else:
-    _IntEnumBase = Enum
+    if sys.version_info >= (3, 11):
+        _IntEnumBase = ReprEnum
+    else:
+        _IntEnumBase = Enum
 
-class IntEnum(int, _IntEnumBase):
-    _value_: int
-    @_magic_enum_attr
-    def value(self) -> int: ...
-    def __new__(cls, value: int) -> Self: ...
+    @disjoint_base
+    class IntEnum(int, _IntEnumBase):
+        _value_: int
+        @_magic_enum_attr
+        def value(self) -> int: ...
+        def __new__(cls, value: int) -> Self: ...
 
 def unique(enumeration: _EnumerationT) -> _EnumerationT: ...
 
@@ -277,9 +286,9 @@ if sys.version_info >= (3, 11):
         NAMED_FLAGS = "multi-flag aliases may not contain unnamed flags"
         UNIQUE = "one name per value"
 
-    CONTINUOUS = EnumCheck.CONTINUOUS
-    NAMED_FLAGS = EnumCheck.NAMED_FLAGS
-    UNIQUE = EnumCheck.UNIQUE
+    CONTINUOUS: Final = EnumCheck.CONTINUOUS
+    NAMED_FLAGS: Final = EnumCheck.NAMED_FLAGS
+    UNIQUE: Final = EnumCheck.UNIQUE
 
     class verify:
         def __init__(self, *checks: EnumCheck) -> None: ...
@@ -291,18 +300,31 @@ if sys.version_info >= (3, 11):
         EJECT = "eject"
         KEEP = "keep"
 
-    STRICT = FlagBoundary.STRICT
-    CONFORM = FlagBoundary.CONFORM
-    EJECT = FlagBoundary.EJECT
-    KEEP = FlagBoundary.KEEP
+    STRICT: Final = FlagBoundary.STRICT
+    CONFORM: Final = FlagBoundary.CONFORM
+    EJECT: Final = FlagBoundary.EJECT
+    KEEP: Final = FlagBoundary.KEEP
 
     def global_str(self: Enum) -> str: ...
     def global_enum(cls: _EnumerationT, update_str: bool = False) -> _EnumerationT: ...
     def global_enum_repr(self: Enum) -> str: ...
     def global_flag_repr(self: Flag) -> str: ...
 
-if sys.version_info >= (3, 11):
+if sys.version_info >= (3, 12):
+    # The body of the class is the same, but the base classes are different.
+    class IntFlag(int, ReprEnum, Flag, boundary=KEEP):  # type: ignore[misc]  # complaints about incompatible bases
+        def __new__(cls, value: int) -> Self: ...
+        def __or__(self, other: int) -> Self: ...
+        def __and__(self, other: int) -> Self: ...
+        def __xor__(self, other: int) -> Self: ...
+        def __invert__(self) -> Self: ...
+        __ror__ = __or__
+        __rand__ = __and__
+        __rxor__ = __xor__
+
+elif sys.version_info >= (3, 11):
     # The body of the class is the same, but the base classes are different.
+    @disjoint_base
     class IntFlag(int, ReprEnum, Flag, boundary=KEEP):  # type: ignore[misc]  # complaints about incompatible bases
         def __new__(cls, value: int) -> Self: ...
         def __or__(self, other: int) -> Self: ...
@@ -314,6 +336,7 @@ if sys.version_info >= (3, 11):
         __rxor__ = __xor__
 
 else:
+    @disjoint_base
     class IntFlag(int, Flag):  # type: ignore[misc]  # complaints about incompatible bases
         def __new__(cls, value: int) -> Self: ...
         def __or__(self, other: int) -> Self: ...
diff --git a/mypy/typeshed/stdlib/errno.pyi b/mypy/typeshed/stdlib/errno.pyi
index 3ba8b66d2865..4f19b5aee87e 100644
--- a/mypy/typeshed/stdlib/errno.pyi
+++ b/mypy/typeshed/stdlib/errno.pyi
@@ -1,225 +1,226 @@
 import sys
 from collections.abc import Mapping
+from typing import Final
 
 errorcode: Mapping[int, str]
 
-EPERM: int
-ENOENT: int
-ESRCH: int
-EINTR: int
-EIO: int
-ENXIO: int
-E2BIG: int
-ENOEXEC: int
-EBADF: int
-ECHILD: int
-EAGAIN: int
-ENOMEM: int
-EACCES: int
-EFAULT: int
-EBUSY: int
-EEXIST: int
-EXDEV: int
-ENODEV: int
-ENOTDIR: int
-EISDIR: int
-EINVAL: int
-ENFILE: int
-EMFILE: int
-ENOTTY: int
-ETXTBSY: int
-EFBIG: int
-ENOSPC: int
-ESPIPE: int
-EROFS: int
-EMLINK: int
-EPIPE: int
-EDOM: int
-ERANGE: int
-EDEADLK: int
-ENAMETOOLONG: int
-ENOLCK: int
-ENOSYS: int
-ENOTEMPTY: int
-ELOOP: int
-EWOULDBLOCK: int
-ENOMSG: int
-EIDRM: int
-ENOSTR: int
-ENODATA: int
-ETIME: int
-ENOSR: int
-EREMOTE: int
-ENOLINK: int
-EPROTO: int
-EBADMSG: int
-EOVERFLOW: int
-EILSEQ: int
-EUSERS: int
-ENOTSOCK: int
-EDESTADDRREQ: int
-EMSGSIZE: int
-EPROTOTYPE: int
-ENOPROTOOPT: int
-EPROTONOSUPPORT: int
-ESOCKTNOSUPPORT: int
-ENOTSUP: int
-EOPNOTSUPP: int
-EPFNOSUPPORT: int
-EAFNOSUPPORT: int
-EADDRINUSE: int
-EADDRNOTAVAIL: int
-ENETDOWN: int
-ENETUNREACH: int
-ENETRESET: int
-ECONNABORTED: int
-ECONNRESET: int
-ENOBUFS: int
-EISCONN: int
-ENOTCONN: int
-ESHUTDOWN: int
-ETOOMANYREFS: int
-ETIMEDOUT: int
-ECONNREFUSED: int
-EHOSTDOWN: int
-EHOSTUNREACH: int
-EALREADY: int
-EINPROGRESS: int
-ESTALE: int
-EDQUOT: int
-ECANCELED: int  # undocumented
-ENOTRECOVERABLE: int  # undocumented
-EOWNERDEAD: int  # undocumented
+EPERM: Final[int]
+ENOENT: Final[int]
+ESRCH: Final[int]
+EINTR: Final[int]
+EIO: Final[int]
+ENXIO: Final[int]
+E2BIG: Final[int]
+ENOEXEC: Final[int]
+EBADF: Final[int]
+ECHILD: Final[int]
+EAGAIN: Final[int]
+ENOMEM: Final[int]
+EACCES: Final[int]
+EFAULT: Final[int]
+EBUSY: Final[int]
+EEXIST: Final[int]
+EXDEV: Final[int]
+ENODEV: Final[int]
+ENOTDIR: Final[int]
+EISDIR: Final[int]
+EINVAL: Final[int]
+ENFILE: Final[int]
+EMFILE: Final[int]
+ENOTTY: Final[int]
+ETXTBSY: Final[int]
+EFBIG: Final[int]
+ENOSPC: Final[int]
+ESPIPE: Final[int]
+EROFS: Final[int]
+EMLINK: Final[int]
+EPIPE: Final[int]
+EDOM: Final[int]
+ERANGE: Final[int]
+EDEADLK: Final[int]
+ENAMETOOLONG: Final[int]
+ENOLCK: Final[int]
+ENOSYS: Final[int]
+ENOTEMPTY: Final[int]
+ELOOP: Final[int]
+EWOULDBLOCK: Final[int]
+ENOMSG: Final[int]
+EIDRM: Final[int]
+ENOSTR: Final[int]
+ENODATA: Final[int]
+ETIME: Final[int]
+ENOSR: Final[int]
+EREMOTE: Final[int]
+ENOLINK: Final[int]
+EPROTO: Final[int]
+EBADMSG: Final[int]
+EOVERFLOW: Final[int]
+EILSEQ: Final[int]
+EUSERS: Final[int]
+ENOTSOCK: Final[int]
+EDESTADDRREQ: Final[int]
+EMSGSIZE: Final[int]
+EPROTOTYPE: Final[int]
+ENOPROTOOPT: Final[int]
+EPROTONOSUPPORT: Final[int]
+ESOCKTNOSUPPORT: Final[int]
+ENOTSUP: Final[int]
+EOPNOTSUPP: Final[int]
+EPFNOSUPPORT: Final[int]
+EAFNOSUPPORT: Final[int]
+EADDRINUSE: Final[int]
+EADDRNOTAVAIL: Final[int]
+ENETDOWN: Final[int]
+ENETUNREACH: Final[int]
+ENETRESET: Final[int]
+ECONNABORTED: Final[int]
+ECONNRESET: Final[int]
+ENOBUFS: Final[int]
+EISCONN: Final[int]
+ENOTCONN: Final[int]
+ESHUTDOWN: Final[int]
+ETOOMANYREFS: Final[int]
+ETIMEDOUT: Final[int]
+ECONNREFUSED: Final[int]
+EHOSTDOWN: Final[int]
+EHOSTUNREACH: Final[int]
+EALREADY: Final[int]
+EINPROGRESS: Final[int]
+ESTALE: Final[int]
+EDQUOT: Final[int]
+ECANCELED: Final[int]  # undocumented
+ENOTRECOVERABLE: Final[int]  # undocumented
+EOWNERDEAD: Final[int]  # undocumented
 
 if sys.platform == "sunos5" or sys.platform == "solaris":  # noqa: Y008
-    ELOCKUNMAPPED: int
-    ENOTACTIVE: int
+    ELOCKUNMAPPED: Final[int]
+    ENOTACTIVE: Final[int]
 
 if sys.platform != "win32":
-    ENOTBLK: int
-    EMULTIHOP: int
+    ENOTBLK: Final[int]
+    EMULTIHOP: Final[int]
 
 if sys.platform == "darwin":
     # All of the below are undocumented
-    EAUTH: int
-    EBADARCH: int
-    EBADEXEC: int
-    EBADMACHO: int
-    EBADRPC: int
-    EDEVERR: int
-    EFTYPE: int
-    ENEEDAUTH: int
-    ENOATTR: int
-    ENOPOLICY: int
-    EPROCLIM: int
-    EPROCUNAVAIL: int
-    EPROGMISMATCH: int
-    EPROGUNAVAIL: int
-    EPWROFF: int
-    ERPCMISMATCH: int
-    ESHLIBVERS: int
+    EAUTH: Final[int]
+    EBADARCH: Final[int]
+    EBADEXEC: Final[int]
+    EBADMACHO: Final[int]
+    EBADRPC: Final[int]
+    EDEVERR: Final[int]
+    EFTYPE: Final[int]
+    ENEEDAUTH: Final[int]
+    ENOATTR: Final[int]
+    ENOPOLICY: Final[int]
+    EPROCLIM: Final[int]
+    EPROCUNAVAIL: Final[int]
+    EPROGMISMATCH: Final[int]
+    EPROGUNAVAIL: Final[int]
+    EPWROFF: Final[int]
+    ERPCMISMATCH: Final[int]
+    ESHLIBVERS: Final[int]
     if sys.version_info >= (3, 11):
-        EQFULL: int
+        EQFULL: Final[int]
 
 if sys.platform != "darwin":
-    EDEADLOCK: int
+    EDEADLOCK: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    ECHRNG: int
-    EL2NSYNC: int
-    EL3HLT: int
-    EL3RST: int
-    ELNRNG: int
-    EUNATCH: int
-    ENOCSI: int
-    EL2HLT: int
-    EBADE: int
-    EBADR: int
-    EXFULL: int
-    ENOANO: int
-    EBADRQC: int
-    EBADSLT: int
-    EBFONT: int
-    ENONET: int
-    ENOPKG: int
-    EADV: int
-    ESRMNT: int
-    ECOMM: int
-    EDOTDOT: int
-    ENOTUNIQ: int
-    EBADFD: int
-    EREMCHG: int
-    ELIBACC: int
-    ELIBBAD: int
-    ELIBSCN: int
-    ELIBMAX: int
-    ELIBEXEC: int
-    ERESTART: int
-    ESTRPIPE: int
-    EUCLEAN: int
-    ENOTNAM: int
-    ENAVAIL: int
-    EISNAM: int
-    EREMOTEIO: int
+    ECHRNG: Final[int]
+    EL2NSYNC: Final[int]
+    EL3HLT: Final[int]
+    EL3RST: Final[int]
+    ELNRNG: Final[int]
+    EUNATCH: Final[int]
+    ENOCSI: Final[int]
+    EL2HLT: Final[int]
+    EBADE: Final[int]
+    EBADR: Final[int]
+    EXFULL: Final[int]
+    ENOANO: Final[int]
+    EBADRQC: Final[int]
+    EBADSLT: Final[int]
+    EBFONT: Final[int]
+    ENONET: Final[int]
+    ENOPKG: Final[int]
+    EADV: Final[int]
+    ESRMNT: Final[int]
+    ECOMM: Final[int]
+    EDOTDOT: Final[int]
+    ENOTUNIQ: Final[int]
+    EBADFD: Final[int]
+    EREMCHG: Final[int]
+    ELIBACC: Final[int]
+    ELIBBAD: Final[int]
+    ELIBSCN: Final[int]
+    ELIBMAX: Final[int]
+    ELIBEXEC: Final[int]
+    ERESTART: Final[int]
+    ESTRPIPE: Final[int]
+    EUCLEAN: Final[int]
+    ENOTNAM: Final[int]
+    ENAVAIL: Final[int]
+    EISNAM: Final[int]
+    EREMOTEIO: Final[int]
     # All of the below are undocumented
-    EKEYEXPIRED: int
-    EKEYREJECTED: int
-    EKEYREVOKED: int
-    EMEDIUMTYPE: int
-    ENOKEY: int
-    ENOMEDIUM: int
-    ERFKILL: int
+    EKEYEXPIRED: Final[int]
+    EKEYREJECTED: Final[int]
+    EKEYREVOKED: Final[int]
+    EMEDIUMTYPE: Final[int]
+    ENOKEY: Final[int]
+    ENOMEDIUM: Final[int]
+    ERFKILL: Final[int]
 
     if sys.version_info >= (3, 14):
-        EHWPOISON: int
+        EHWPOISON: Final[int]
 
 if sys.platform == "win32":
     # All of these are undocumented
-    WSABASEERR: int
-    WSAEACCES: int
-    WSAEADDRINUSE: int
-    WSAEADDRNOTAVAIL: int
-    WSAEAFNOSUPPORT: int
-    WSAEALREADY: int
-    WSAEBADF: int
-    WSAECONNABORTED: int
-    WSAECONNREFUSED: int
-    WSAECONNRESET: int
-    WSAEDESTADDRREQ: int
-    WSAEDISCON: int
-    WSAEDQUOT: int
-    WSAEFAULT: int
-    WSAEHOSTDOWN: int
-    WSAEHOSTUNREACH: int
-    WSAEINPROGRESS: int
-    WSAEINTR: int
-    WSAEINVAL: int
-    WSAEISCONN: int
-    WSAELOOP: int
-    WSAEMFILE: int
-    WSAEMSGSIZE: int
-    WSAENAMETOOLONG: int
-    WSAENETDOWN: int
-    WSAENETRESET: int
-    WSAENETUNREACH: int
-    WSAENOBUFS: int
-    WSAENOPROTOOPT: int
-    WSAENOTCONN: int
-    WSAENOTEMPTY: int
-    WSAENOTSOCK: int
-    WSAEOPNOTSUPP: int
-    WSAEPFNOSUPPORT: int
-    WSAEPROCLIM: int
-    WSAEPROTONOSUPPORT: int
-    WSAEPROTOTYPE: int
-    WSAEREMOTE: int
-    WSAESHUTDOWN: int
-    WSAESOCKTNOSUPPORT: int
-    WSAESTALE: int
-    WSAETIMEDOUT: int
-    WSAETOOMANYREFS: int
-    WSAEUSERS: int
-    WSAEWOULDBLOCK: int
-    WSANOTINITIALISED: int
-    WSASYSNOTREADY: int
-    WSAVERNOTSUPPORTED: int
+    WSABASEERR: Final[int]
+    WSAEACCES: Final[int]
+    WSAEADDRINUSE: Final[int]
+    WSAEADDRNOTAVAIL: Final[int]
+    WSAEAFNOSUPPORT: Final[int]
+    WSAEALREADY: Final[int]
+    WSAEBADF: Final[int]
+    WSAECONNABORTED: Final[int]
+    WSAECONNREFUSED: Final[int]
+    WSAECONNRESET: Final[int]
+    WSAEDESTADDRREQ: Final[int]
+    WSAEDISCON: Final[int]
+    WSAEDQUOT: Final[int]
+    WSAEFAULT: Final[int]
+    WSAEHOSTDOWN: Final[int]
+    WSAEHOSTUNREACH: Final[int]
+    WSAEINPROGRESS: Final[int]
+    WSAEINTR: Final[int]
+    WSAEINVAL: Final[int]
+    WSAEISCONN: Final[int]
+    WSAELOOP: Final[int]
+    WSAEMFILE: Final[int]
+    WSAEMSGSIZE: Final[int]
+    WSAENAMETOOLONG: Final[int]
+    WSAENETDOWN: Final[int]
+    WSAENETRESET: Final[int]
+    WSAENETUNREACH: Final[int]
+    WSAENOBUFS: Final[int]
+    WSAENOPROTOOPT: Final[int]
+    WSAENOTCONN: Final[int]
+    WSAENOTEMPTY: Final[int]
+    WSAENOTSOCK: Final[int]
+    WSAEOPNOTSUPP: Final[int]
+    WSAEPFNOSUPPORT: Final[int]
+    WSAEPROCLIM: Final[int]
+    WSAEPROTONOSUPPORT: Final[int]
+    WSAEPROTOTYPE: Final[int]
+    WSAEREMOTE: Final[int]
+    WSAESHUTDOWN: Final[int]
+    WSAESOCKTNOSUPPORT: Final[int]
+    WSAESTALE: Final[int]
+    WSAETIMEDOUT: Final[int]
+    WSAETOOMANYREFS: Final[int]
+    WSAEUSERS: Final[int]
+    WSAEWOULDBLOCK: Final[int]
+    WSANOTINITIALISED: Final[int]
+    WSASYSNOTREADY: Final[int]
+    WSAVERNOTSUPPORTED: Final[int]
diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi
index a2a2b235fdad..620cc177a415 100644
--- a/mypy/typeshed/stdlib/filecmp.pyi
+++ b/mypy/typeshed/stdlib/filecmp.pyi
@@ -6,7 +6,7 @@ from typing import Any, AnyStr, Final, Generic, Literal
 
 __all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"]
 
-DEFAULT_IGNORES: list[str]
+DEFAULT_IGNORES: Final[list[str]]
 BUFSIZE: Final = 8192
 
 def cmp(f1: StrOrBytesPath, f2: StrOrBytesPath, shallow: bool | Literal[0, 1] = True) -> bool: ...
diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi
index e81fbaf5dad7..ef4066aa65b5 100644
--- a/mypy/typeshed/stdlib/fractions.pyi
+++ b/mypy/typeshed/stdlib/fractions.pyi
@@ -14,6 +14,7 @@ class _ConvertibleToIntegerRatio(Protocol):
     def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ...
 
 class Fraction(Rational):
+    __slots__ = ("_numerator", "_denominator")
     @overload
     def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ...
     @overload
diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi
index 6e17ba7d35dc..47baf917294d 100644
--- a/mypy/typeshed/stdlib/functools.pyi
+++ b/mypy/typeshed/stdlib/functools.pyi
@@ -4,7 +4,7 @@ from _typeshed import SupportsAllComparisons, SupportsItems
 from collections.abc import Callable, Hashable, Iterable, Sized
 from types import GenericAlias
 from typing import Any, Final, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload, type_check_only
-from typing_extensions import ParamSpec, Self, TypeAlias
+from typing_extensions import ParamSpec, Self, TypeAlias, disjoint_base
 
 __all__ = [
     "update_wrapper",
@@ -95,7 +95,7 @@ else:
         tuple[Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"]]
     ]
 
-WRAPPER_UPDATES: tuple[Literal["__dict__"]]
+WRAPPER_UPDATES: Final[tuple[Literal["__dict__"]]]
 
 @type_check_only
 class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]):
@@ -150,7 +150,7 @@ else:
 
 def total_ordering(cls: type[_T]) -> type[_T]: ...
 def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ...
-
+@disjoint_base
 class partial(Generic[_T]):
     @property
     def func(self) -> Callable[..., _T]: ...
@@ -169,10 +169,17 @@ class partialmethod(Generic[_T]):
     func: Callable[..., _T] | _Descriptor
     args: tuple[Any, ...]
     keywords: dict[str, Any]
-    @overload
-    def __init__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> None: ...
-    @overload
-    def __init__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> None: ...
+    if sys.version_info >= (3, 14):
+        @overload
+        def __new__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> Self: ...
+        @overload
+        def __new__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> Self: ...
+    else:
+        @overload
+        def __init__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> None: ...
+        @overload
+        def __init__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> None: ...
+
     def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ...
     @property
     def __isabstractmethod__(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi
index 937aece03437..e9ffd7a4a4a4 100644
--- a/mypy/typeshed/stdlib/gettext.pyi
+++ b/mypy/typeshed/stdlib/gettext.pyi
@@ -120,7 +120,7 @@ else:
         languages: Iterable[str] | None = None,
         class_: None = None,
         fallback: Literal[False] = False,
-        codeset: str | None = None,
+        codeset: str | None = ...,
     ) -> GNUTranslations: ...
     @overload
     def translation(
@@ -130,7 +130,7 @@ else:
         *,
         class_: Callable[[io.BufferedReader], _NullTranslationsT],
         fallback: Literal[False] = False,
-        codeset: str | None = None,
+        codeset: str | None = ...,
     ) -> _NullTranslationsT: ...
     @overload
     def translation(
@@ -139,7 +139,7 @@ else:
         languages: Iterable[str] | None,
         class_: Callable[[io.BufferedReader], _NullTranslationsT],
         fallback: Literal[False] = False,
-        codeset: str | None = None,
+        codeset: str | None = ...,
     ) -> _NullTranslationsT: ...
     @overload
     def translation(
@@ -148,18 +148,18 @@ else:
         languages: Iterable[str] | None = None,
         class_: Callable[[io.BufferedReader], NullTranslations] | None = None,
         fallback: bool = False,
-        codeset: str | None = None,
+        codeset: str | None = ...,
     ) -> NullTranslations: ...
     @overload
-    def install(
-        domain: str, localedir: StrPath | None = None, codeset: None = None, names: Container[str] | None = None
-    ) -> None: ...
+    def install(domain: str, localedir: StrPath | None = None, names: Container[str] | None = None) -> None: ...
     @overload
     @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.")
-    def install(domain: str, localedir: StrPath | None, codeset: str, /, names: Container[str] | None = None) -> None: ...
+    def install(domain: str, localedir: StrPath | None, codeset: str | None, /, names: Container[str] | None = None) -> None: ...
     @overload
     @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.")
-    def install(domain: str, localedir: StrPath | None = None, *, codeset: str, names: Container[str] | None = None) -> None: ...
+    def install(
+        domain: str, localedir: StrPath | None = None, *, codeset: str | None, names: Container[str] | None = None
+    ) -> None: ...
 
 def textdomain(domain: str | None = None) -> str: ...
 def bindtextdomain(domain: str, localedir: StrPath | None = None) -> str: ...
diff --git a/mypy/typeshed/stdlib/glob.pyi b/mypy/typeshed/stdlib/glob.pyi
index 63069d8009c8..942fd7396196 100644
--- a/mypy/typeshed/stdlib/glob.pyi
+++ b/mypy/typeshed/stdlib/glob.pyi
@@ -10,9 +10,13 @@ if sys.version_info >= (3, 13):
     __all__ += ["translate"]
 
 if sys.version_info >= (3, 10):
-    @deprecated("Will be removed in Python 3.15; Use `glob.glob` and pass *root_dir* argument instead.")
+    @deprecated(
+        "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead."
+    )
     def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
-    @deprecated("Will be removed in Python 3.15; Use `glob.glob` and pass *root_dir* argument instead.")
+    @deprecated(
+        "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead."
+    )
     def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi
index 300ed9eb26d8..070c59b1c166 100644
--- a/mypy/typeshed/stdlib/hmac.pyi
+++ b/mypy/typeshed/stdlib/hmac.pyi
@@ -20,6 +20,7 @@ def new(key: bytes | bytearray, msg: ReadableBuffer | None, digestmod: _DigestMo
 def new(key: bytes | bytearray, *, digestmod: _DigestMod) -> HMAC: ...
 
 class HMAC:
+    __slots__ = ("_hmac", "_inner", "_outer", "block_size", "digest_size")
     digest_size: int
     block_size: int
     @property
diff --git a/mypy/typeshed/stdlib/html/entities.pyi b/mypy/typeshed/stdlib/html/entities.pyi
index be83fd1135be..e5890d1ecfbd 100644
--- a/mypy/typeshed/stdlib/html/entities.pyi
+++ b/mypy/typeshed/stdlib/html/entities.pyi
@@ -1,6 +1,8 @@
+from typing import Final
+
 __all__ = ["html5", "name2codepoint", "codepoint2name", "entitydefs"]
 
-name2codepoint: dict[str, int]
-html5: dict[str, str]
-codepoint2name: dict[int, str]
-entitydefs: dict[str, str]
+name2codepoint: Final[dict[str, int]]
+html5: Final[dict[str, str]]
+codepoint2name: Final[dict[int, str]]
+entitydefs: Final[dict[str, str]]
diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi
index 5c35dff28d43..d259e84e6f2a 100644
--- a/mypy/typeshed/stdlib/http/client.pyi
+++ b/mypy/typeshed/stdlib/http/client.pyi
@@ -7,7 +7,7 @@ from _typeshed import MaybeNone, ReadableBuffer, SupportsRead, SupportsReadline,
 from collections.abc import Callable, Iterable, Iterator, Mapping
 from email._policybase import _MessageT
 from socket import socket
-from typing import BinaryIO, Literal, TypeVar, overload
+from typing import BinaryIO, Final, TypeVar, overload
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -36,85 +36,85 @@ _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | Readable
 _T = TypeVar("_T")
 _HeaderValue: TypeAlias = ReadableBuffer | str | int
 
-HTTP_PORT: int
-HTTPS_PORT: int
+HTTP_PORT: Final = 80
+HTTPS_PORT: Final = 443
 
 # Keep these global constants in sync with http.HTTPStatus (http/__init__.pyi).
 # They are present for backward compatibility reasons.
-CONTINUE: Literal[100]
-SWITCHING_PROTOCOLS: Literal[101]
-PROCESSING: Literal[102]
-EARLY_HINTS: Literal[103]
+CONTINUE: Final = 100
+SWITCHING_PROTOCOLS: Final = 101
+PROCESSING: Final = 102
+EARLY_HINTS: Final = 103
 
-OK: Literal[200]
-CREATED: Literal[201]
-ACCEPTED: Literal[202]
-NON_AUTHORITATIVE_INFORMATION: Literal[203]
-NO_CONTENT: Literal[204]
-RESET_CONTENT: Literal[205]
-PARTIAL_CONTENT: Literal[206]
-MULTI_STATUS: Literal[207]
-ALREADY_REPORTED: Literal[208]
-IM_USED: Literal[226]
+OK: Final = 200
+CREATED: Final = 201
+ACCEPTED: Final = 202
+NON_AUTHORITATIVE_INFORMATION: Final = 203
+NO_CONTENT: Final = 204
+RESET_CONTENT: Final = 205
+PARTIAL_CONTENT: Final = 206
+MULTI_STATUS: Final = 207
+ALREADY_REPORTED: Final = 208
+IM_USED: Final = 226
 
-MULTIPLE_CHOICES: Literal[300]
-MOVED_PERMANENTLY: Literal[301]
-FOUND: Literal[302]
-SEE_OTHER: Literal[303]
-NOT_MODIFIED: Literal[304]
-USE_PROXY: Literal[305]
-TEMPORARY_REDIRECT: Literal[307]
-PERMANENT_REDIRECT: Literal[308]
+MULTIPLE_CHOICES: Final = 300
+MOVED_PERMANENTLY: Final = 301
+FOUND: Final = 302
+SEE_OTHER: Final = 303
+NOT_MODIFIED: Final = 304
+USE_PROXY: Final = 305
+TEMPORARY_REDIRECT: Final = 307
+PERMANENT_REDIRECT: Final = 308
 
-BAD_REQUEST: Literal[400]
-UNAUTHORIZED: Literal[401]
-PAYMENT_REQUIRED: Literal[402]
-FORBIDDEN: Literal[403]
-NOT_FOUND: Literal[404]
-METHOD_NOT_ALLOWED: Literal[405]
-NOT_ACCEPTABLE: Literal[406]
-PROXY_AUTHENTICATION_REQUIRED: Literal[407]
-REQUEST_TIMEOUT: Literal[408]
-CONFLICT: Literal[409]
-GONE: Literal[410]
-LENGTH_REQUIRED: Literal[411]
-PRECONDITION_FAILED: Literal[412]
+BAD_REQUEST: Final = 400
+UNAUTHORIZED: Final = 401
+PAYMENT_REQUIRED: Final = 402
+FORBIDDEN: Final = 403
+NOT_FOUND: Final = 404
+METHOD_NOT_ALLOWED: Final = 405
+NOT_ACCEPTABLE: Final = 406
+PROXY_AUTHENTICATION_REQUIRED: Final = 407
+REQUEST_TIMEOUT: Final = 408
+CONFLICT: Final = 409
+GONE: Final = 410
+LENGTH_REQUIRED: Final = 411
+PRECONDITION_FAILED: Final = 412
 if sys.version_info >= (3, 13):
-    CONTENT_TOO_LARGE: Literal[413]
-REQUEST_ENTITY_TOO_LARGE: Literal[413]
+    CONTENT_TOO_LARGE: Final = 413
+REQUEST_ENTITY_TOO_LARGE: Final = 413
 if sys.version_info >= (3, 13):
-    URI_TOO_LONG: Literal[414]
-REQUEST_URI_TOO_LONG: Literal[414]
-UNSUPPORTED_MEDIA_TYPE: Literal[415]
+    URI_TOO_LONG: Final = 414
+REQUEST_URI_TOO_LONG: Final = 414
+UNSUPPORTED_MEDIA_TYPE: Final = 415
 if sys.version_info >= (3, 13):
-    RANGE_NOT_SATISFIABLE: Literal[416]
-REQUESTED_RANGE_NOT_SATISFIABLE: Literal[416]
-EXPECTATION_FAILED: Literal[417]
-IM_A_TEAPOT: Literal[418]
-MISDIRECTED_REQUEST: Literal[421]
+    RANGE_NOT_SATISFIABLE: Final = 416
+REQUESTED_RANGE_NOT_SATISFIABLE: Final = 416
+EXPECTATION_FAILED: Final = 417
+IM_A_TEAPOT: Final = 418
+MISDIRECTED_REQUEST: Final = 421
 if sys.version_info >= (3, 13):
-    UNPROCESSABLE_CONTENT: Literal[422]
-UNPROCESSABLE_ENTITY: Literal[422]
-LOCKED: Literal[423]
-FAILED_DEPENDENCY: Literal[424]
-TOO_EARLY: Literal[425]
-UPGRADE_REQUIRED: Literal[426]
-PRECONDITION_REQUIRED: Literal[428]
-TOO_MANY_REQUESTS: Literal[429]
-REQUEST_HEADER_FIELDS_TOO_LARGE: Literal[431]
-UNAVAILABLE_FOR_LEGAL_REASONS: Literal[451]
+    UNPROCESSABLE_CONTENT: Final = 422
+UNPROCESSABLE_ENTITY: Final = 422
+LOCKED: Final = 423
+FAILED_DEPENDENCY: Final = 424
+TOO_EARLY: Final = 425
+UPGRADE_REQUIRED: Final = 426
+PRECONDITION_REQUIRED: Final = 428
+TOO_MANY_REQUESTS: Final = 429
+REQUEST_HEADER_FIELDS_TOO_LARGE: Final = 431
+UNAVAILABLE_FOR_LEGAL_REASONS: Final = 451
 
-INTERNAL_SERVER_ERROR: Literal[500]
-NOT_IMPLEMENTED: Literal[501]
-BAD_GATEWAY: Literal[502]
-SERVICE_UNAVAILABLE: Literal[503]
-GATEWAY_TIMEOUT: Literal[504]
-HTTP_VERSION_NOT_SUPPORTED: Literal[505]
-VARIANT_ALSO_NEGOTIATES: Literal[506]
-INSUFFICIENT_STORAGE: Literal[507]
-LOOP_DETECTED: Literal[508]
-NOT_EXTENDED: Literal[510]
-NETWORK_AUTHENTICATION_REQUIRED: Literal[511]
+INTERNAL_SERVER_ERROR: Final = 500
+NOT_IMPLEMENTED: Final = 501
+BAD_GATEWAY: Final = 502
+SERVICE_UNAVAILABLE: Final = 503
+GATEWAY_TIMEOUT: Final = 504
+HTTP_VERSION_NOT_SUPPORTED: Final = 505
+VARIANT_ALSO_NEGOTIATES: Final = 506
+INSUFFICIENT_STORAGE: Final = 507
+LOOP_DETECTED: Final = 508
+NOT_EXTENDED: Final = 510
+NETWORK_AUTHENTICATION_REQUIRED: Final = 511
 
 responses: dict[int, str]
 
diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi
index 429bb65bb0ef..2c1a374331bc 100644
--- a/mypy/typeshed/stdlib/http/server.pyi
+++ b/mypy/typeshed/stdlib/http/server.pyi
@@ -119,12 +119,24 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
     def guess_type(self, path: StrPath) -> str: ...  # undocumented
 
 def executable(path: StrPath) -> bool: ...  # undocumented
-@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
-    cgi_directories: list[str]
-    have_fork: bool  # undocumented
-    def do_POST(self) -> None: ...
-    def is_cgi(self) -> bool: ...  # undocumented
-    def is_executable(self, path: StrPath) -> bool: ...  # undocumented
-    def is_python(self, path: StrPath) -> bool: ...  # undocumented
-    def run_cgi(self) -> None: ...  # undocumented
+
+if sys.version_info >= (3, 13):
+    @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+    class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
+        cgi_directories: list[str]
+        have_fork: bool  # undocumented
+        def do_POST(self) -> None: ...
+        def is_cgi(self) -> bool: ...  # undocumented
+        def is_executable(self, path: StrPath) -> bool: ...  # undocumented
+        def is_python(self, path: StrPath) -> bool: ...  # undocumented
+        def run_cgi(self) -> None: ...  # undocumented
+
+else:
+    class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
+        cgi_directories: list[str]
+        have_fork: bool  # undocumented
+        def do_POST(self) -> None: ...
+        def is_cgi(self) -> bool: ...  # undocumented
+        def is_executable(self, path: StrPath) -> bool: ...  # undocumented
+        def is_python(self, path: StrPath) -> bool: ...  # undocumented
+        def run_cgi(self) -> None: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/imp.pyi b/mypy/typeshed/stdlib/imp.pyi
index f045fd969b27..b5b4223aa58e 100644
--- a/mypy/typeshed/stdlib/imp.pyi
+++ b/mypy/typeshed/stdlib/imp.pyi
@@ -13,18 +13,18 @@ from _imp import (
 from _typeshed import StrPath
 from os import PathLike
 from types import TracebackType
-from typing import IO, Any, Protocol, type_check_only
+from typing import IO, Any, Final, Protocol, type_check_only
 
-SEARCH_ERROR: int
-PY_SOURCE: int
-PY_COMPILED: int
-C_EXTENSION: int
-PY_RESOURCE: int
-PKG_DIRECTORY: int
-C_BUILTIN: int
-PY_FROZEN: int
-PY_CODERESOURCE: int
-IMP_HOOK: int
+SEARCH_ERROR: Final = 0
+PY_SOURCE: Final = 1
+PY_COMPILED: Final = 2
+C_EXTENSION: Final = 3
+PY_RESOURCE: Final = 4
+PKG_DIRECTORY: Final = 5
+C_BUILTIN: Final = 6
+PY_FROZEN: Final = 7
+PY_CODERESOURCE: Final = 8
+IMP_HOOK: Final = 9
 
 def new_module(name: str) -> types.ModuleType: ...
 def get_magic() -> bytes: ...
diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi
index ef87663cb72d..72031e0e3bd2 100644
--- a/mypy/typeshed/stdlib/importlib/abc.pyi
+++ b/mypy/typeshed/stdlib/importlib/abc.pyi
@@ -40,7 +40,7 @@ if sys.version_info < (3, 12):
     @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use `MetaPathFinder` or `PathEntryFinder` instead.")
     class Finder(metaclass=ABCMeta): ...
 
-@deprecated("Deprecated as of Python 3.7: Use importlib.resources.abc.TraversableResources instead.")
+@deprecated("Deprecated since Python 3.7. Use `importlib.resources.abc.TraversableResources` instead.")
 class ResourceLoader(Loader):
     @abstractmethod
     def get_data(self, path: str) -> bytes: ...
@@ -61,7 +61,7 @@ class ExecutionLoader(InspectLoader):
     def get_filename(self, fullname: str) -> str: ...
 
 class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta):  # type: ignore[misc]  # incompatible definitions of source_to_code in the base classes
-    @deprecated("Deprecated as of Python 3.3: Use importlib.resources.abc.SourceLoader.path_stats instead.")
+    @deprecated("Deprecated since Python 3.3. Use `importlib.resources.abc.SourceLoader.path_stats` instead.")
     def path_mtime(self, path: str) -> float: ...
     def set_data(self, path: str, data: bytes) -> None: ...
     def get_source(self, fullname: str) -> str | None: ...
diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
index d1315b2eb2f1..9286e92331c8 100644
--- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
+++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi
@@ -11,7 +11,7 @@ from os import PathLike
 from pathlib import Path
 from re import Pattern
 from typing import Any, ClassVar, Generic, NamedTuple, TypeVar, overload
-from typing_extensions import Self, TypeAlias, deprecated
+from typing_extensions import Self, TypeAlias, deprecated, disjoint_base
 
 _T = TypeVar("_T")
 _KT = TypeVar("_KT")
@@ -59,23 +59,21 @@ else:
         value: str
         group: str
 
-class EntryPoint(_EntryPointBase):
-    pattern: ClassVar[Pattern[str]]
-    if sys.version_info >= (3, 11):
+if sys.version_info >= (3, 11):
+    class EntryPoint(_EntryPointBase):
+        pattern: ClassVar[Pattern[str]]
         name: str
         value: str
         group: str
 
         def __init__(self, name: str, value: str, group: str) -> None: ...
-
-    def load(self) -> Any: ...  # Callable[[], Any] or an importable module
-    @property
-    def extras(self) -> list[str]: ...
-    @property
-    def module(self) -> str: ...
-    @property
-    def attr(self) -> str: ...
-    if sys.version_info >= (3, 10):
+        def load(self) -> Any: ...  # Callable[[], Any] or an importable module
+        @property
+        def extras(self) -> list[str]: ...
+        @property
+        def module(self) -> str: ...
+        @property
+        def attr(self) -> str: ...
         dist: ClassVar[Distribution | None]
         def matches(
             self,
@@ -87,16 +85,43 @@ class EntryPoint(_EntryPointBase):
             attr: str = ...,
             extras: list[str] = ...,
         ) -> bool: ...  # undocumented
-
-    def __hash__(self) -> int: ...
-    if sys.version_info >= (3, 11):
+        def __hash__(self) -> int: ...
         def __eq__(self, other: object) -> bool: ...
         def __lt__(self, other: object) -> bool: ...
-    if sys.version_info < (3, 12):
+        if sys.version_info < (3, 12):
+            def __iter__(self) -> Iterator[Any]: ...  # result of iter((str, Self)), really
+
+else:
+    @disjoint_base
+    class EntryPoint(_EntryPointBase):
+        pattern: ClassVar[Pattern[str]]
+
+        def load(self) -> Any: ...  # Callable[[], Any] or an importable module
+        @property
+        def extras(self) -> list[str]: ...
+        @property
+        def module(self) -> str: ...
+        @property
+        def attr(self) -> str: ...
+        if sys.version_info >= (3, 10):
+            dist: ClassVar[Distribution | None]
+            def matches(
+                self,
+                *,
+                name: str = ...,
+                value: str = ...,
+                group: str = ...,
+                module: str = ...,
+                attr: str = ...,
+                extras: list[str] = ...,
+            ) -> bool: ...  # undocumented
+
+        def __hash__(self) -> int: ...
         def __iter__(self) -> Iterator[Any]: ...  # result of iter((str, Self)), really
 
 if sys.version_info >= (3, 12):
     class EntryPoints(tuple[EntryPoint, ...]):
+        __slots__ = ()
         def __getitem__(self, name: str) -> EntryPoint: ...  # type: ignore[override]
         def select(
             self,
@@ -114,10 +139,12 @@ if sys.version_info >= (3, 12):
         def groups(self) -> set[str]: ...
 
 elif sys.version_info >= (3, 10):
-    class DeprecatedList(list[_T]): ...
+    class DeprecatedList(list[_T]):
+        __slots__ = ()
 
     class EntryPoints(DeprecatedList[EntryPoint]):  # use as list is deprecated since 3.10
         # int argument is deprecated since 3.10
+        __slots__ = ()
         def __getitem__(self, name: int | str) -> EntryPoint: ...  # type: ignore[override]
         def select(
             self,
@@ -230,7 +257,7 @@ class Distribution(_distribution_parent):
         def name(self) -> str: ...
     if sys.version_info >= (3, 13):
         @property
-        def origin(self) -> types.SimpleNamespace: ...
+        def origin(self) -> types.SimpleNamespace | None: ...
 
 class DistributionFinder(MetaPathFinder):
     class Context:
diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi
index f8ec6cad0160..55ae61617af7 100644
--- a/mypy/typeshed/stdlib/inspect.pyi
+++ b/mypy/typeshed/stdlib/inspect.pyi
@@ -26,7 +26,7 @@ from types import (
     WrapperDescriptorType,
 )
 from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only
-from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs, deprecated
+from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs, deprecated, disjoint_base
 
 if sys.version_info >= (3, 14):
     from annotationlib import Format
@@ -336,6 +336,7 @@ class _void: ...
 class _empty: ...
 
 class Signature:
+    __slots__ = ("_return_annotation", "_parameters")
     def __init__(
         self, parameters: Sequence[Parameter] | None = None, *, return_annotation: Any = ..., __validate_parameters__: bool = True
     ) -> None: ...
@@ -416,6 +417,7 @@ if sys.version_info >= (3, 12):
     def getasyncgenlocals(agen: AsyncGeneratorType[Any, Any]) -> dict[str, Any]: ...
 
 class Parameter:
+    __slots__ = ("_name", "_kind", "_default", "_annotation")
     def __init__(self, name: str, kind: _ParameterKind, *, default: Any = ..., annotation: Any = ...) -> None: ...
     empty = _empty
 
@@ -447,6 +449,7 @@ class Parameter:
     def __hash__(self) -> int: ...
 
 class BoundArguments:
+    __slots__ = ("arguments", "_signature", "__weakref__")
     arguments: OrderedDict[str, Any]
     @property
     def args(self) -> tuple[Any, ...]: ...
@@ -567,19 +570,6 @@ if sys.version_info >= (3, 11):
         code_context: list[str] | None
         index: int | None  # type: ignore[assignment]
 
-    class Traceback(_Traceback):
-        positions: dis.Positions | None
-        def __new__(
-            cls,
-            filename: str,
-            lineno: int,
-            function: str,
-            code_context: list[str] | None,
-            index: int | None,
-            *,
-            positions: dis.Positions | None = None,
-        ) -> Self: ...
-
     class _FrameInfo(NamedTuple):
         frame: FrameType
         filename: str
@@ -588,19 +578,63 @@ if sys.version_info >= (3, 11):
         code_context: list[str] | None
         index: int | None  # type: ignore[assignment]
 
-    class FrameInfo(_FrameInfo):
-        positions: dis.Positions | None
-        def __new__(
-            cls,
-            frame: FrameType,
-            filename: str,
-            lineno: int,
-            function: str,
-            code_context: list[str] | None,
-            index: int | None,
-            *,
-            positions: dis.Positions | None = None,
-        ) -> Self: ...
+    if sys.version_info >= (3, 12):
+        class Traceback(_Traceback):
+            positions: dis.Positions | None
+            def __new__(
+                cls,
+                filename: str,
+                lineno: int,
+                function: str,
+                code_context: list[str] | None,
+                index: int | None,
+                *,
+                positions: dis.Positions | None = None,
+            ) -> Self: ...
+
+        class FrameInfo(_FrameInfo):
+            positions: dis.Positions | None
+            def __new__(
+                cls,
+                frame: FrameType,
+                filename: str,
+                lineno: int,
+                function: str,
+                code_context: list[str] | None,
+                index: int | None,
+                *,
+                positions: dis.Positions | None = None,
+            ) -> Self: ...
+
+    else:
+        @disjoint_base
+        class Traceback(_Traceback):
+            positions: dis.Positions | None
+            def __new__(
+                cls,
+                filename: str,
+                lineno: int,
+                function: str,
+                code_context: list[str] | None,
+                index: int | None,
+                *,
+                positions: dis.Positions | None = None,
+            ) -> Self: ...
+
+        @disjoint_base
+        class FrameInfo(_FrameInfo):
+            positions: dis.Positions | None
+            def __new__(
+                cls,
+                frame: FrameType,
+                filename: str,
+                lineno: int,
+                function: str,
+                code_context: list[str] | None,
+                index: int | None,
+                *,
+                positions: dis.Positions | None = None,
+            ) -> Self: ...
 
 else:
     class Traceback(NamedTuple):
diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi
index 1313df183d36..d301d700e9d0 100644
--- a/mypy/typeshed/stdlib/io.pyi
+++ b/mypy/typeshed/stdlib/io.pyi
@@ -67,7 +67,9 @@ class TextIOBase(_TextIOBase, IOBase): ...
 
 if sys.version_info >= (3, 14):
     class Reader(Protocol[_T_co]):
+        __slots__ = ()
         def read(self, size: int = ..., /) -> _T_co: ...
 
     class Writer(Protocol[_T_contra]):
+        __slots__ = ()
         def write(self, data: _T_contra, /) -> int: ...
diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi
index 6d49eb8bd94a..e2f3defa2dea 100644
--- a/mypy/typeshed/stdlib/ipaddress.pyi
+++ b/mypy/typeshed/stdlib/ipaddress.pyi
@@ -22,6 +22,7 @@ def ip_interface(
 ) -> IPv4Interface | IPv6Interface: ...
 
 class _IPAddressBase:
+    __slots__ = ()
     @property
     def compressed(self) -> str: ...
     @property
@@ -33,6 +34,7 @@ class _IPAddressBase:
         def version(self) -> int: ...
 
 class _BaseAddress(_IPAddressBase):
+    __slots__ = ()
     def __add__(self, other: int) -> Self: ...
     def __hash__(self) -> int: ...
     def __int__(self) -> int: ...
@@ -71,7 +73,7 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]):
     @property
     def broadcast_address(self) -> _A: ...
     def compare_networks(self, other: Self) -> int: ...
-    def hosts(self) -> Iterator[_A]: ...
+    def hosts(self) -> Iterator[_A] | list[_A]: ...
     @property
     def is_global(self) -> bool: ...
     @property
@@ -105,6 +107,7 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]):
     def hostmask(self) -> _A: ...
 
 class _BaseV4:
+    __slots__ = ()
     if sys.version_info >= (3, 14):
         version: Final = 4
         max_prefixlen: Final = 32
@@ -115,6 +118,7 @@ class _BaseV4:
         def max_prefixlen(self) -> Literal[32]: ...
 
 class IPv4Address(_BaseV4, _BaseAddress):
+    __slots__ = ("_ip", "__weakref__")
     def __init__(self, address: object) -> None: ...
     @property
     def is_global(self) -> bool: ...
@@ -156,6 +160,7 @@ class IPv4Interface(IPv4Address):
     def with_prefixlen(self) -> str: ...
 
 class _BaseV6:
+    __slots__ = ()
     if sys.version_info >= (3, 14):
         version: Final = 6
         max_prefixlen: Final = 128
@@ -166,6 +171,7 @@ class _BaseV6:
         def max_prefixlen(self) -> Literal[128]: ...
 
 class IPv6Address(_BaseV6, _BaseAddress):
+    __slots__ = ("_ip", "_scope_id", "__weakref__")
     def __init__(self, address: object) -> None: ...
     @property
     def is_global(self) -> bool: ...
diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi
index 7d05b1318680..73745fe92d9e 100644
--- a/mypy/typeshed/stdlib/itertools.pyi
+++ b/mypy/typeshed/stdlib/itertools.pyi
@@ -3,7 +3,7 @@ from _typeshed import MaybeNone
 from collections.abc import Callable, Iterable, Iterator
 from types import GenericAlias
 from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 _T = TypeVar("_T")
 _S = TypeVar("_S")
@@ -27,6 +27,7 @@ _Predicate: TypeAlias = Callable[[_T], object]
 
 # Technically count can take anything that implements a number protocol and has an add method
 # but we can't enforce the add method
+@disjoint_base
 class count(Iterator[_N]):
     @overload
     def __new__(cls) -> count[int]: ...
@@ -37,11 +38,13 @@ class count(Iterator[_N]):
     def __next__(self) -> _N: ...
     def __iter__(self) -> Self: ...
 
+@disjoint_base
 class cycle(Iterator[_T]):
     def __new__(cls, iterable: Iterable[_T], /) -> Self: ...
     def __next__(self) -> _T: ...
     def __iter__(self) -> Self: ...
 
+@disjoint_base
 class repeat(Iterator[_T]):
     @overload
     def __new__(cls, object: _T) -> Self: ...
@@ -51,6 +54,7 @@ class repeat(Iterator[_T]):
     def __iter__(self) -> Self: ...
     def __length_hint__(self) -> int: ...
 
+@disjoint_base
 class accumulate(Iterator[_T]):
     @overload
     def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ...
@@ -59,6 +63,7 @@ class accumulate(Iterator[_T]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
+@disjoint_base
 class chain(Iterator[_T]):
     def __new__(cls, *iterables: Iterable[_T]) -> Self: ...
     def __next__(self) -> _T: ...
@@ -68,21 +73,25 @@ class chain(Iterator[_T]):
     def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
+@disjoint_base
 class compress(Iterator[_T]):
     def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
+@disjoint_base
 class dropwhile(Iterator[_T]):
     def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
+@disjoint_base
 class filterfalse(Iterator[_T]):
     def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
+@disjoint_base
 class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]):
     @overload
     def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ...
@@ -91,6 +100,7 @@ class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ...
 
+@disjoint_base
 class islice(Iterator[_T]):
     @overload
     def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ...
@@ -99,18 +109,20 @@ class islice(Iterator[_T]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
+@disjoint_base
 class starmap(Iterator[_T_co]):
     def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T_co: ...
 
+@disjoint_base
 class takewhile(Iterator[_T]):
     def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ...
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T: ...
 
 def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ...
-
+@disjoint_base
 class zip_longest(Iterator[_T_co]):
     # one iterable (fillvalue doesn't matter)
     @overload
@@ -189,6 +201,7 @@ class zip_longest(Iterator[_T_co]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T_co: ...
 
+@disjoint_base
 class product(Iterator[_T_co]):
     @overload
     def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ...
@@ -274,6 +287,7 @@ class product(Iterator[_T_co]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T_co: ...
 
+@disjoint_base
 class permutations(Iterator[_T_co]):
     @overload
     def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ...
@@ -288,6 +302,7 @@ class permutations(Iterator[_T_co]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T_co: ...
 
+@disjoint_base
 class combinations(Iterator[_T_co]):
     @overload
     def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ...
@@ -302,6 +317,7 @@ class combinations(Iterator[_T_co]):
     def __iter__(self) -> Self: ...
     def __next__(self) -> _T_co: ...
 
+@disjoint_base
 class combinations_with_replacement(Iterator[_T_co]):
     @overload
     def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ...
@@ -317,12 +333,14 @@ class combinations_with_replacement(Iterator[_T_co]):
     def __next__(self) -> _T_co: ...
 
 if sys.version_info >= (3, 10):
+    @disjoint_base
     class pairwise(Iterator[_T_co]):
         def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ...
         def __iter__(self) -> Self: ...
         def __next__(self) -> _T_co: ...
 
 if sys.version_info >= (3, 12):
+    @disjoint_base
     class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]):
         if sys.version_info >= (3, 13):
             def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ...
diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi
index bfaa9970c996..7f4f7f4e8656 100644
--- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi
+++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi
@@ -1,4 +1,3 @@
-from _typeshed import Incomplete
 from typing import ClassVar, Literal
 
 from .. import fixer_base
@@ -13,5 +12,5 @@ class FixTupleParams(fixer_base.BaseFix):
 
 def simplify_args(node): ...
 def find_params(node): ...
-def map_to_index(param_list, prefix=..., d: Incomplete | None = ...): ...
+def map_to_index(param_list, prefix=[], d=None): ...
 def tuple_name(param_list): ...
diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi
index 03c79cc3e265..8248f82ea87a 100644
--- a/mypy/typeshed/stdlib/logging/__init__.pyi
+++ b/mypy/typeshed/stdlib/logging/__init__.pyi
@@ -155,7 +155,7 @@ class Logger(Filterer):
         stacklevel: int = 1,
         extra: Mapping[str, object] | None = None,
     ) -> None: ...
-    @deprecated("Deprecated; use warning() instead.")
+    @deprecated("Deprecated since Python 3.3. Use `Logger.warning()` instead.")
     def warn(
         self,
         msg: object,
@@ -409,7 +409,7 @@ class LoggerAdapter(Generic[_L]):
         extra: Mapping[str, object] | None = None,
         **kwargs: object,
     ) -> None: ...
-    @deprecated("Deprecated; use warning() instead.")
+    @deprecated("Deprecated since Python 3.3. Use `LoggerAdapter.warning()` instead.")
     def warn(
         self,
         msg: object,
@@ -519,7 +519,7 @@ def warning(
     stacklevel: int = 1,
     extra: Mapping[str, object] | None = None,
 ) -> None: ...
-@deprecated("Deprecated; use warning() instead.")
+@deprecated("Deprecated since Python 3.3. Use `warning()` instead.")
 def warn(
     msg: object,
     *args: object,
@@ -659,4 +659,4 @@ class StringTemplateStyle(PercentStyle):  # undocumented
 
 _STYLES: Final[dict[str, tuple[PercentStyle, str]]]
 
-BASIC_FORMAT: Final[str]
+BASIC_FORMAT: Final = "%(levelname)s:%(name)s:%(message)s"
diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi
index 000ba1ebb06e..72412ddc2cea 100644
--- a/mypy/typeshed/stdlib/logging/config.pyi
+++ b/mypy/typeshed/stdlib/logging/config.pyi
@@ -5,11 +5,11 @@ from configparser import RawConfigParser
 from re import Pattern
 from threading import Thread
 from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload, type_check_only
-from typing_extensions import Required, TypeAlias
+from typing_extensions import Required, TypeAlias, disjoint_base
 
 from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level
 
-DEFAULT_LOGGING_CONFIG_PORT: int
+DEFAULT_LOGGING_CONFIG_PORT: Final = 9030
 RESET_ERROR: Final[int]  # undocumented
 IDENTIFIER: Final[Pattern[str]]  # undocumented
 
@@ -100,13 +100,22 @@ class ConvertingList(list[Any], ConvertingMixin):  # undocumented
     def __getitem__(self, key: slice) -> Any: ...
     def pop(self, idx: SupportsIndex = -1) -> Any: ...
 
-class ConvertingTuple(tuple[Any, ...], ConvertingMixin):  # undocumented
-    @overload
-    def __getitem__(self, key: SupportsIndex) -> Any: ...
-    @overload
-    def __getitem__(self, key: slice) -> Any: ...
+if sys.version_info >= (3, 12):
+    class ConvertingTuple(tuple[Any, ...], ConvertingMixin):  # undocumented
+        @overload
+        def __getitem__(self, key: SupportsIndex) -> Any: ...
+        @overload
+        def __getitem__(self, key: slice) -> Any: ...
 
-class BaseConfigurator:  # undocumented
+else:
+    @disjoint_base
+    class ConvertingTuple(tuple[Any, ...], ConvertingMixin):  # undocumented
+        @overload
+        def __getitem__(self, key: SupportsIndex) -> Any: ...
+        @overload
+        def __getitem__(self, key: slice) -> Any: ...
+
+class BaseConfigurator:
     CONVERT_PATTERN: Pattern[str]
     WORD_PATTERN: Pattern[str]
     DOT_PATTERN: Pattern[str]
@@ -115,6 +124,8 @@ class BaseConfigurator:  # undocumented
     value_converters: dict[str, str]
     importer: Callable[..., Any]
 
+    config: dict[str, Any]  # undocumented
+
     def __init__(self, config: _DictConfigArgs | dict[str, Any]) -> None: ...
     def resolve(self, s: str) -> Any: ...
     def ext_convert(self, value: str) -> Any: ...
diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi
index e231d1de3fb5..535f1c685183 100644
--- a/mypy/typeshed/stdlib/logging/handlers.pyi
+++ b/mypy/typeshed/stdlib/logging/handlers.pyi
@@ -14,12 +14,12 @@ from typing_extensions import Self
 
 _T = TypeVar("_T")
 
-DEFAULT_TCP_LOGGING_PORT: Final[int]
-DEFAULT_UDP_LOGGING_PORT: Final[int]
-DEFAULT_HTTP_LOGGING_PORT: Final[int]
-DEFAULT_SOAP_LOGGING_PORT: Final[int]
-SYSLOG_UDP_PORT: Final[int]
-SYSLOG_TCP_PORT: Final[int]
+DEFAULT_TCP_LOGGING_PORT: Final = 9020
+DEFAULT_UDP_LOGGING_PORT: Final = 9021
+DEFAULT_HTTP_LOGGING_PORT: Final = 9022
+DEFAULT_SOAP_LOGGING_PORT: Final = 9023
+SYSLOG_UDP_PORT: Final = 514
+SYSLOG_TCP_PORT: Final = 514
 
 class WatchedFileHandler(FileHandler):
     dev: int  # undocumented
diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi
index 261a2bfdfc44..8a5baba62914 100644
--- a/mypy/typeshed/stdlib/mmap.pyi
+++ b/mypy/typeshed/stdlib/mmap.pyi
@@ -3,7 +3,7 @@ import sys
 from _typeshed import ReadableBuffer, Unused
 from collections.abc import Iterator
 from typing import Final, Literal, NoReturn, overload
-from typing_extensions import Self
+from typing_extensions import Self, disjoint_base
 
 ACCESS_DEFAULT: Final = 0
 ACCESS_READ: Final = 1
@@ -31,9 +31,10 @@ if sys.platform != "win32":
 
 PAGESIZE: Final[int]
 
+@disjoint_base
 class mmap:
     if sys.platform == "win32":
-        def __init__(self, fileno: int, length: int, tagname: str | None = None, access: int = 0, offset: int = 0) -> None: ...
+        def __new__(self, fileno: int, length: int, tagname: str | None = None, access: int = 0, offset: int = 0) -> Self: ...
     else:
         if sys.version_info >= (3, 13):
             def __new__(
diff --git a/mypy/typeshed/stdlib/msilib/__init__.pyi b/mypy/typeshed/stdlib/msilib/__init__.pyi
index 3e43cbc44f52..622f585f5bee 100644
--- a/mypy/typeshed/stdlib/msilib/__init__.pyi
+++ b/mypy/typeshed/stdlib/msilib/__init__.pyi
@@ -1,26 +1,26 @@
 import sys
 from collections.abc import Container, Iterable, Sequence
 from types import ModuleType
-from typing import Any, Literal
+from typing import Any, Final
 
 if sys.platform == "win32":
     from _msi import *
     from _msi import _Database
 
-    AMD64: bool
-    Win64: bool
+    AMD64: Final[bool]
+    Win64: Final[bool]
 
-    datasizemask: Literal[0x00FF]
-    type_valid: Literal[0x0100]
-    type_localizable: Literal[0x0200]
-    typemask: Literal[0x0C00]
-    type_long: Literal[0x0000]
-    type_short: Literal[0x0400]
-    type_string: Literal[0x0C00]
-    type_binary: Literal[0x0800]
-    type_nullable: Literal[0x1000]
-    type_key: Literal[0x2000]
-    knownbits: Literal[0x3FFF]
+    datasizemask: Final = 0x00FF
+    type_valid: Final = 0x0100
+    type_localizable: Final = 0x0200
+    typemask: Final = 0x0C00
+    type_long: Final = 0x0000
+    type_short: Final = 0x0400
+    type_string: Final = 0x0C00
+    type_binary: Final = 0x0800
+    type_nullable: Final = 0x1000
+    type_key: Final = 0x2000
+    knownbits: Final = 0x3FFF
 
     class Table:
         name: str
diff --git a/mypy/typeshed/stdlib/msilib/schema.pyi b/mypy/typeshed/stdlib/msilib/schema.pyi
index 4ad9a1783fcd..3bbdc41a1e8e 100644
--- a/mypy/typeshed/stdlib/msilib/schema.pyi
+++ b/mypy/typeshed/stdlib/msilib/schema.pyi
@@ -1,4 +1,5 @@
 import sys
+from typing import Final
 
 if sys.platform == "win32":
     from . import Table
@@ -89,6 +90,6 @@ if sys.platform == "win32":
     Upgrade: Table
     Verb: Table
 
-    tables: list[Table]
+    tables: Final[list[Table]]
 
     _Validation_records: list[tuple[str, str, str, int | None, int | None, str | None, int | None, str | None, str | None, str]]
diff --git a/mypy/typeshed/stdlib/msilib/sequence.pyi b/mypy/typeshed/stdlib/msilib/sequence.pyi
index b8af09f46e65..a9f5c24717bd 100644
--- a/mypy/typeshed/stdlib/msilib/sequence.pyi
+++ b/mypy/typeshed/stdlib/msilib/sequence.pyi
@@ -1,13 +1,14 @@
 import sys
+from typing import Final
 from typing_extensions import TypeAlias
 
 if sys.platform == "win32":
     _SequenceType: TypeAlias = list[tuple[str, str | None, int]]
 
-    AdminExecuteSequence: _SequenceType
-    AdminUISequence: _SequenceType
-    AdvtExecuteSequence: _SequenceType
-    InstallExecuteSequence: _SequenceType
-    InstallUISequence: _SequenceType
+    AdminExecuteSequence: Final[_SequenceType]
+    AdminUISequence: Final[_SequenceType]
+    AdvtExecuteSequence: Final[_SequenceType]
+    InstallExecuteSequence: Final[_SequenceType]
+    InstallUISequence: Final[_SequenceType]
 
-    tables: list[str]
+    tables: Final[list[str]]
diff --git a/mypy/typeshed/stdlib/msilib/text.pyi b/mypy/typeshed/stdlib/msilib/text.pyi
index 441c843ca6cf..da3c5fd0fb7a 100644
--- a/mypy/typeshed/stdlib/msilib/text.pyi
+++ b/mypy/typeshed/stdlib/msilib/text.pyi
@@ -1,7 +1,8 @@
 import sys
+from typing import Final
 
 if sys.platform == "win32":
-    ActionText: list[tuple[str, str, str | None]]
-    UIText: list[tuple[str, str | None]]
+    ActionText: Final[list[tuple[str, str, str | None]]]
+    UIText: Final[list[tuple[str, str | None]]]
     dirname: str
-    tables: list[str]
+    tables: Final[list[str]]
diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi
index 403a5d933522..5feca8eab5c1 100644
--- a/mypy/typeshed/stdlib/msvcrt.pyi
+++ b/mypy/typeshed/stdlib/msvcrt.pyi
@@ -9,10 +9,10 @@ if sys.platform == "win32":
     LK_NBLCK: Final = 2
     LK_RLCK: Final = 3
     LK_NBRLCK: Final = 4
-    SEM_FAILCRITICALERRORS: int
-    SEM_NOALIGNMENTFAULTEXCEPT: int
-    SEM_NOGPFAULTERRORBOX: int
-    SEM_NOOPENFILEERRORBOX: int
+    SEM_FAILCRITICALERRORS: Final = 0x0001
+    SEM_NOALIGNMENTFAULTEXCEPT: Final = 0x0004
+    SEM_NOGPFAULTERRORBOX: Final = 0x0002
+    SEM_NOOPENFILEERRORBOX: Final = 0x8000
     def locking(fd: int, mode: int, nbytes: int, /) -> None: ...
     def setmode(fd: int, mode: int, /) -> int: ...
     def open_osfhandle(handle: int, flags: int, /) -> int: ...
diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
index b0ccac41b925..5efe69a97377 100644
--- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi
@@ -38,6 +38,7 @@ class Namespace:
 _Namespace: TypeAlias = Namespace
 
 class Token:
+    __slots__ = ("typeid", "address", "id")
     typeid: str | bytes | None
     address: _Address | None
     id: str | bytes | int | None
diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi
index ecb4a7ddec7d..3583194c77e2 100644
--- a/mypy/typeshed/stdlib/multiprocessing/util.pyi
+++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi
@@ -52,7 +52,7 @@ def get_logger() -> Logger: ...
 def log_to_stderr(level: _LoggingLevel | None = None) -> Logger: ...
 def is_abstract_socket_namespace(address: str | bytes | None) -> bool: ...
 
-abstract_sockets_supported: bool
+abstract_sockets_supported: Final[bool]
 
 def get_temp_dir() -> str: ...
 def register_after_fork(obj: _T, func: Callable[[_T], object]) -> None: ...
diff --git a/mypy/typeshed/stdlib/nturl2path.pyi b/mypy/typeshed/stdlib/nturl2path.pyi
index c38a359469d2..014af8a0fd2e 100644
--- a/mypy/typeshed/stdlib/nturl2path.pyi
+++ b/mypy/typeshed/stdlib/nturl2path.pyi
@@ -2,9 +2,9 @@ import sys
 from typing_extensions import deprecated
 
 if sys.version_info >= (3, 14):
-    @deprecated("nturl2path module was deprecated since Python 3.14")
+    @deprecated("The `nturl2path` module is deprecated since Python 3.14.")
     def url2pathname(url: str) -> str: ...
-    @deprecated("nturl2path module was deprecated since Python 3.14")
+    @deprecated("The `nturl2path` module is deprecated since Python 3.14.")
     def pathname2url(p: str) -> str: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi
index b24591719cff..64fb16581e95 100644
--- a/mypy/typeshed/stdlib/numbers.pyi
+++ b/mypy/typeshed/stdlib/numbers.pyi
@@ -61,12 +61,14 @@ class _IntegralLike(_RealLike, Protocol):
 #################
 
 class Number(metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __hash__(self) -> int: ...
 
 # See comment at the top of the file
 # for why some of these return types are purposefully vague
 class Complex(Number, _ComplexLike):
+    __slots__ = ()
     @abstractmethod
     def __complex__(self) -> complex: ...
     def __bool__(self) -> bool: ...
@@ -109,6 +111,7 @@ class Complex(Number, _ComplexLike):
 # See comment at the top of the file
 # for why some of these return types are purposefully vague
 class Real(Complex, _RealLike):
+    __slots__ = ()
     @abstractmethod
     def __float__(self) -> float: ...
     @abstractmethod
@@ -153,6 +156,7 @@ class Real(Complex, _RealLike):
 # See comment at the top of the file
 # for why some of these return types are purposefully vague
 class Rational(Real):
+    __slots__ = ()
     @property
     @abstractmethod
     def numerator(self) -> _IntegralLike: ...
@@ -164,6 +168,7 @@ class Rational(Real):
 # See comment at the top of the file
 # for why some of these return types are purposefully vague
 class Integral(Rational, _IntegralLike):
+    __slots__ = ()
     @abstractmethod
     def __int__(self) -> int: ...
     def __index__(self) -> int: ...
diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi
index a5a3a79c323b..ed0e96ef1cb9 100644
--- a/mypy/typeshed/stdlib/opcode.pyi
+++ b/mypy/typeshed/stdlib/opcode.pyi
@@ -1,5 +1,5 @@
 import sys
-from typing import Literal
+from typing import Final, Literal
 
 __all__ = [
     "cmp_op",
@@ -24,24 +24,24 @@ if sys.version_info >= (3, 13):
     __all__ += ["hasjump"]
 
 cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]]
-hasconst: list[int]
-hasname: list[int]
-hasjrel: list[int]
-hasjabs: list[int]
-haslocal: list[int]
-hascompare: list[int]
-hasfree: list[int]
+hasconst: Final[list[int]]
+hasname: Final[list[int]]
+hasjrel: Final[list[int]]
+hasjabs: Final[list[int]]
+haslocal: Final[list[int]]
+hascompare: Final[list[int]]
+hasfree: Final[list[int]]
 if sys.version_info >= (3, 12):
-    hasarg: list[int]
-    hasexc: list[int]
+    hasarg: Final[list[int]]
+    hasexc: Final[list[int]]
 else:
-    hasnargs: list[int]
+    hasnargs: Final[list[int]]
 if sys.version_info >= (3, 13):
-    hasjump: list[int]
-opname: list[str]
+    hasjump: Final[list[int]]
+opname: Final[list[str]]
 
-opmap: dict[str, int]
-HAVE_ARGUMENT: int
-EXTENDED_ARG: int
+opmap: Final[dict[str, int]]
+HAVE_ARGUMENT: Final = 43
+EXTENDED_ARG: Final = 69
 
 def stack_effect(opcode: int, oparg: int | None = None, /, *, jump: bool | None = None) -> int: ...
diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi
index 4047bb0f1c4d..71c79dfac399 100644
--- a/mypy/typeshed/stdlib/os/__init__.pyi
+++ b/mypy/typeshed/stdlib/os/__init__.pyi
@@ -509,22 +509,22 @@ supports_follow_symlinks: set[Callable[..., Any]]
 
 if sys.platform != "win32":
     # Unix only
-    PRIO_PROCESS: int
-    PRIO_PGRP: int
-    PRIO_USER: int
+    PRIO_PROCESS: Final[int]
+    PRIO_PGRP: Final[int]
+    PRIO_USER: Final[int]
 
-    F_LOCK: int
-    F_TLOCK: int
-    F_ULOCK: int
-    F_TEST: int
+    F_LOCK: Final[int]
+    F_TLOCK: Final[int]
+    F_ULOCK: Final[int]
+    F_TEST: Final[int]
 
     if sys.platform != "darwin":
-        POSIX_FADV_NORMAL: int
-        POSIX_FADV_SEQUENTIAL: int
-        POSIX_FADV_RANDOM: int
-        POSIX_FADV_NOREUSE: int
-        POSIX_FADV_WILLNEED: int
-        POSIX_FADV_DONTNEED: int
+        POSIX_FADV_NORMAL: Final[int]
+        POSIX_FADV_SEQUENTIAL: Final[int]
+        POSIX_FADV_RANDOM: Final[int]
+        POSIX_FADV_NOREUSE: Final[int]
+        POSIX_FADV_WILLNEED: Final[int]
+        POSIX_FADV_DONTNEED: Final[int]
 
     if sys.platform != "linux" and sys.platform != "darwin":
         # In the os-module docs, these are marked as being available
@@ -534,69 +534,69 @@ if sys.platform != "win32":
         # so the sys-module docs recommend doing `if sys.platform.startswith('freebsd')`
         # to detect FreeBSD builds. Unfortunately that would be too dynamic
         # for type checkers, however.
-        SF_NODISKIO: int
-        SF_MNOWAIT: int
-        SF_SYNC: int
+        SF_NODISKIO: Final[int]
+        SF_MNOWAIT: Final[int]
+        SF_SYNC: Final[int]
 
         if sys.version_info >= (3, 11):
-            SF_NOCACHE: int
+            SF_NOCACHE: Final[int]
 
     if sys.platform == "linux":
-        XATTR_SIZE_MAX: int
-        XATTR_CREATE: int
-        XATTR_REPLACE: int
+        XATTR_SIZE_MAX: Final[int]
+        XATTR_CREATE: Final[int]
+        XATTR_REPLACE: Final[int]
 
-    P_PID: int
-    P_PGID: int
-    P_ALL: int
+    P_PID: Final[int]
+    P_PGID: Final[int]
+    P_ALL: Final[int]
 
     if sys.platform == "linux":
-        P_PIDFD: int
-
-    WEXITED: int
-    WSTOPPED: int
-    WNOWAIT: int
-
-    CLD_EXITED: int
-    CLD_DUMPED: int
-    CLD_TRAPPED: int
-    CLD_CONTINUED: int
-    CLD_KILLED: int
-    CLD_STOPPED: int
-
-    SCHED_OTHER: int
-    SCHED_FIFO: int
-    SCHED_RR: int
+        P_PIDFD: Final[int]
+
+    WEXITED: Final[int]
+    WSTOPPED: Final[int]
+    WNOWAIT: Final[int]
+
+    CLD_EXITED: Final[int]
+    CLD_DUMPED: Final[int]
+    CLD_TRAPPED: Final[int]
+    CLD_CONTINUED: Final[int]
+    CLD_KILLED: Final[int]
+    CLD_STOPPED: Final[int]
+
+    SCHED_OTHER: Final[int]
+    SCHED_FIFO: Final[int]
+    SCHED_RR: Final[int]
     if sys.platform != "darwin" and sys.platform != "linux":
-        SCHED_SPORADIC: int
+        SCHED_SPORADIC: Final[int]
 
 if sys.platform == "linux":
-    SCHED_BATCH: int
-    SCHED_IDLE: int
-    SCHED_RESET_ON_FORK: int
+    SCHED_BATCH: Final[int]
+    SCHED_IDLE: Final[int]
+    SCHED_RESET_ON_FORK: Final[int]
 
 if sys.version_info >= (3, 14) and sys.platform == "linux":
-    SCHED_DEADLINE: int
-    SCHED_NORMAL: int
+    SCHED_DEADLINE: Final[int]
+    SCHED_NORMAL: Final[int]
 
 if sys.platform != "win32":
-    RTLD_LAZY: int
-    RTLD_NOW: int
-    RTLD_GLOBAL: int
-    RTLD_LOCAL: int
-    RTLD_NODELETE: int
-    RTLD_NOLOAD: int
+    RTLD_LAZY: Final[int]
+    RTLD_NOW: Final[int]
+    RTLD_GLOBAL: Final[int]
+    RTLD_LOCAL: Final[int]
+    RTLD_NODELETE: Final[int]
+    RTLD_NOLOAD: Final[int]
 
 if sys.platform == "linux":
-    RTLD_DEEPBIND: int
-    GRND_NONBLOCK: int
-    GRND_RANDOM: int
+    RTLD_DEEPBIND: Final[int]
+    GRND_NONBLOCK: Final[int]
+    GRND_RANDOM: Final[int]
 
 if sys.platform == "darwin" and sys.version_info >= (3, 12):
-    PRIO_DARWIN_BG: int
-    PRIO_DARWIN_NONUI: int
-    PRIO_DARWIN_PROCESS: int
-    PRIO_DARWIN_THREAD: int
+    PRIO_DARWIN_BG: Final[int]
+    PRIO_DARWIN_NONUI: Final[int]
+    PRIO_DARWIN_PROCESS: Final[int]
+    PRIO_DARWIN_THREAD: Final[int]
 
 SEEK_SET: Final = 0
 SEEK_CUR: Final = 1
@@ -605,74 +605,74 @@ if sys.platform != "win32":
     SEEK_DATA: Final = 3
     SEEK_HOLE: Final = 4
 
-O_RDONLY: int
-O_WRONLY: int
-O_RDWR: int
-O_APPEND: int
-O_CREAT: int
-O_EXCL: int
-O_TRUNC: int
+O_RDONLY: Final[int]
+O_WRONLY: Final[int]
+O_RDWR: Final[int]
+O_APPEND: Final[int]
+O_CREAT: Final[int]
+O_EXCL: Final[int]
+O_TRUNC: Final[int]
 if sys.platform == "win32":
-    O_BINARY: int
-    O_NOINHERIT: int
-    O_SHORT_LIVED: int
-    O_TEMPORARY: int
-    O_RANDOM: int
-    O_SEQUENTIAL: int
-    O_TEXT: int
+    O_BINARY: Final[int]
+    O_NOINHERIT: Final[int]
+    O_SHORT_LIVED: Final[int]
+    O_TEMPORARY: Final[int]
+    O_RANDOM: Final[int]
+    O_SEQUENTIAL: Final[int]
+    O_TEXT: Final[int]
 
 if sys.platform != "win32":
-    O_DSYNC: int
-    O_SYNC: int
-    O_NDELAY: int
-    O_NONBLOCK: int
-    O_NOCTTY: int
-    O_CLOEXEC: int
-    O_ASYNC: int  # Gnu extension if in C library
-    O_DIRECTORY: int  # Gnu extension if in C library
-    O_NOFOLLOW: int  # Gnu extension if in C library
-    O_ACCMODE: int  # TODO: when does this exist?
+    O_DSYNC: Final[int]
+    O_SYNC: Final[int]
+    O_NDELAY: Final[int]
+    O_NONBLOCK: Final[int]
+    O_NOCTTY: Final[int]
+    O_CLOEXEC: Final[int]
+    O_ASYNC: Final[int]  # Gnu extension if in C library
+    O_DIRECTORY: Final[int]  # Gnu extension if in C library
+    O_NOFOLLOW: Final[int]  # Gnu extension if in C library
+    O_ACCMODE: Final[int]  # TODO: when does this exist?
 
 if sys.platform == "linux":
-    O_RSYNC: int
-    O_DIRECT: int  # Gnu extension if in C library
-    O_NOATIME: int  # Gnu extension if in C library
-    O_PATH: int  # Gnu extension if in C library
-    O_TMPFILE: int  # Gnu extension if in C library
-    O_LARGEFILE: int  # Gnu extension if in C library
+    O_RSYNC: Final[int]
+    O_DIRECT: Final[int]  # Gnu extension if in C library
+    O_NOATIME: Final[int]  # Gnu extension if in C library
+    O_PATH: Final[int]  # Gnu extension if in C library
+    O_TMPFILE: Final[int]  # Gnu extension if in C library
+    O_LARGEFILE: Final[int]  # Gnu extension if in C library
 
 if sys.platform != "linux" and sys.platform != "win32":
-    O_SHLOCK: int
-    O_EXLOCK: int
+    O_SHLOCK: Final[int]
+    O_EXLOCK: Final[int]
 
 if sys.platform == "darwin" and sys.version_info >= (3, 10):
-    O_EVTONLY: int
-    O_NOFOLLOW_ANY: int
-    O_SYMLINK: int
+    O_EVTONLY: Final[int]
+    O_NOFOLLOW_ANY: Final[int]
+    O_SYMLINK: Final[int]
 
 if sys.platform != "win32" and sys.version_info >= (3, 10):
-    O_FSYNC: int
+    O_FSYNC: Final[int]
 
 if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13):
-    O_EXEC: int
-    O_SEARCH: int
+    O_EXEC: Final[int]
+    O_SEARCH: Final[int]
 
 if sys.platform != "win32" and sys.platform != "darwin":
     # posix, but apparently missing on macos
-    ST_APPEND: int
-    ST_MANDLOCK: int
-    ST_NOATIME: int
-    ST_NODEV: int
-    ST_NODIRATIME: int
-    ST_NOEXEC: int
-    ST_RELATIME: int
-    ST_SYNCHRONOUS: int
-    ST_WRITE: int
+    ST_APPEND: Final[int]
+    ST_MANDLOCK: Final[int]
+    ST_NOATIME: Final[int]
+    ST_NODEV: Final[int]
+    ST_NODIRATIME: Final[int]
+    ST_NOEXEC: Final[int]
+    ST_RELATIME: Final[int]
+    ST_SYNCHRONOUS: Final[int]
+    ST_WRITE: Final[int]
 
 if sys.platform != "win32":
-    NGROUPS_MAX: int
-    ST_NOSUID: int
-    ST_RDONLY: int
+    NGROUPS_MAX: Final[int]
+    ST_NOSUID: Final[int]
+    ST_RDONLY: Final[int]
 
 curdir: str
 pardir: str
@@ -688,10 +688,10 @@ linesep: Literal["\n", "\r\n"]
 devnull: str
 name: str
 
-F_OK: int
-R_OK: int
-W_OK: int
-X_OK: int
+F_OK: Final = 0
+R_OK: Final = 4
+W_OK: Final = 2
+X_OK: Final = 1
 
 _EnvironCodeFunc: TypeAlias = Callable[[AnyStr], AnyStr]
 
@@ -730,47 +730,47 @@ if sys.platform != "win32":
     environb: _Environ[bytes]
 
 if sys.version_info >= (3, 11) or sys.platform != "win32":
-    EX_OK: int
+    EX_OK: Final[int]
 
 if sys.platform != "win32":
     confstr_names: dict[str, int]
     pathconf_names: dict[str, int]
     sysconf_names: dict[str, int]
 
-    EX_USAGE: int
-    EX_DATAERR: int
-    EX_NOINPUT: int
-    EX_NOUSER: int
-    EX_NOHOST: int
-    EX_UNAVAILABLE: int
-    EX_SOFTWARE: int
-    EX_OSERR: int
-    EX_OSFILE: int
-    EX_CANTCREAT: int
-    EX_IOERR: int
-    EX_TEMPFAIL: int
-    EX_PROTOCOL: int
-    EX_NOPERM: int
-    EX_CONFIG: int
+    EX_USAGE: Final[int]
+    EX_DATAERR: Final[int]
+    EX_NOINPUT: Final[int]
+    EX_NOUSER: Final[int]
+    EX_NOHOST: Final[int]
+    EX_UNAVAILABLE: Final[int]
+    EX_SOFTWARE: Final[int]
+    EX_OSERR: Final[int]
+    EX_OSFILE: Final[int]
+    EX_CANTCREAT: Final[int]
+    EX_IOERR: Final[int]
+    EX_TEMPFAIL: Final[int]
+    EX_PROTOCOL: Final[int]
+    EX_NOPERM: Final[int]
+    EX_CONFIG: Final[int]
 
 # Exists on some Unix platforms, e.g. Solaris.
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
-    EX_NOTFOUND: int
+    EX_NOTFOUND: Final[int]
 
-P_NOWAIT: int
-P_NOWAITO: int
-P_WAIT: int
+P_NOWAIT: Final[int]
+P_NOWAITO: Final[int]
+P_WAIT: Final[int]
 if sys.platform == "win32":
-    P_DETACH: int
-    P_OVERLAY: int
+    P_DETACH: Final[int]
+    P_OVERLAY: Final[int]
 
 # wait()/waitpid() options
 if sys.platform != "win32":
-    WNOHANG: int  # Unix only
-    WCONTINUED: int  # some Unix systems
-    WUNTRACED: int  # Unix only
+    WNOHANG: Final[int]  # Unix only
+    WCONTINUED: Final[int]  # some Unix systems
+    WUNTRACED: Final[int]  # Unix only
 
-TMP_MAX: int  # Undocumented, but used by tempfile
+TMP_MAX: Final[int]  # Undocumented, but used by tempfile
 
 # ----- os classes (structures) -----
 @final
@@ -862,6 +862,7 @@ In the future, this property will contain the last metadata change time."""
 # on the allowlist for use as a Protocol starting in 3.14.
 @runtime_checkable
 class PathLike(ABC, Protocol[AnyStr_co]):  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
+    __slots__ = ()
     @abstractmethod
     def __fspath__(self) -> AnyStr_co: ...
 
@@ -1136,11 +1137,11 @@ if sys.platform != "win32":
     def pwritev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], offset: int, flags: int = 0, /) -> int: ...
     if sys.platform != "darwin":
         if sys.version_info >= (3, 10):
-            RWF_APPEND: int  # docs say available on 3.7+, stubtest says otherwise
-        RWF_DSYNC: int
-        RWF_SYNC: int
-        RWF_HIPRI: int
-        RWF_NOWAIT: int
+            RWF_APPEND: Final[int]  # docs say available on 3.7+, stubtest says otherwise
+        RWF_DSYNC: Final[int]
+        RWF_SYNC: Final[int]
+        RWF_HIPRI: Final[int]
+        RWF_NOWAIT: Final[int]
 
     if sys.platform == "linux":
         def sendfile(out_fd: FileDescriptor, in_fd: FileDescriptor, offset: int | None, count: int) -> int: ...
@@ -1150,8 +1151,8 @@ if sys.platform != "win32":
             in_fd: FileDescriptor,
             offset: int,
             count: int,
-            headers: Sequence[ReadableBuffer] = ...,
-            trailers: Sequence[ReadableBuffer] = ...,
+            headers: Sequence[ReadableBuffer] = (),
+            trailers: Sequence[ReadableBuffer] = (),
             flags: int = 0,
         ) -> int: ...  # FreeBSD and Mac OS X only
 
@@ -1196,7 +1197,7 @@ if sys.platform != "win32":
 
 def getcwd() -> str: ...
 def getcwdb() -> bytes: ...
-def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = ...) -> None: ...
+def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = True) -> None: ...
 
 if sys.platform != "win32" and sys.platform != "linux":
     def chflags(path: StrOrBytesPath, flags: int, follow_symlinks: bool = True) -> None: ...  # some flavors of Unix
@@ -1499,9 +1500,9 @@ else:
         setsigdef: Iterable[int] = ...,
         scheduler: tuple[Any, sched_param] | None = ...,
     ) -> int: ...
-    POSIX_SPAWN_OPEN: int
-    POSIX_SPAWN_CLOSE: int
-    POSIX_SPAWN_DUP2: int
+    POSIX_SPAWN_OPEN: Final = 0
+    POSIX_SPAWN_CLOSE: Final = 1
+    POSIX_SPAWN_DUP2: Final = 2
 
 if sys.platform != "win32":
     @final
@@ -1565,23 +1566,23 @@ if sys.platform == "win32":
     def add_dll_directory(path: str) -> _AddedDllDirectory: ...
 
 if sys.platform == "linux":
-    MFD_CLOEXEC: int
-    MFD_ALLOW_SEALING: int
-    MFD_HUGETLB: int
-    MFD_HUGE_SHIFT: int
-    MFD_HUGE_MASK: int
-    MFD_HUGE_64KB: int
-    MFD_HUGE_512KB: int
-    MFD_HUGE_1MB: int
-    MFD_HUGE_2MB: int
-    MFD_HUGE_8MB: int
-    MFD_HUGE_16MB: int
-    MFD_HUGE_32MB: int
-    MFD_HUGE_256MB: int
-    MFD_HUGE_512MB: int
-    MFD_HUGE_1GB: int
-    MFD_HUGE_2GB: int
-    MFD_HUGE_16GB: int
+    MFD_CLOEXEC: Final[int]
+    MFD_ALLOW_SEALING: Final[int]
+    MFD_HUGETLB: Final[int]
+    MFD_HUGE_SHIFT: Final[int]
+    MFD_HUGE_MASK: Final[int]
+    MFD_HUGE_64KB: Final[int]
+    MFD_HUGE_512KB: Final[int]
+    MFD_HUGE_1MB: Final[int]
+    MFD_HUGE_2MB: Final[int]
+    MFD_HUGE_8MB: Final[int]
+    MFD_HUGE_16MB: Final[int]
+    MFD_HUGE_32MB: Final[int]
+    MFD_HUGE_256MB: Final[int]
+    MFD_HUGE_512MB: Final[int]
+    MFD_HUGE_1GB: Final[int]
+    MFD_HUGE_2GB: Final[int]
+    MFD_HUGE_16GB: Final[int]
     def memfd_create(name: str, flags: int = ...) -> int: ...
     def copy_file_range(src: int, dst: int, count: int, offset_src: int | None = ..., offset_dst: int | None = ...) -> int: ...
 
@@ -1599,12 +1600,12 @@ if sys.version_info >= (3, 12) and sys.platform == "win32":
     def listvolumes() -> list[str]: ...
 
 if sys.version_info >= (3, 10) and sys.platform == "linux":
-    EFD_CLOEXEC: int
-    EFD_NONBLOCK: int
-    EFD_SEMAPHORE: int
-    SPLICE_F_MORE: int
-    SPLICE_F_MOVE: int
-    SPLICE_F_NONBLOCK: int
+    EFD_CLOEXEC: Final[int]
+    EFD_NONBLOCK: Final[int]
+    EFD_SEMAPHORE: Final[int]
+    SPLICE_F_MORE: Final[int]
+    SPLICE_F_MOVE: Final[int]
+    SPLICE_F_NONBLOCK: Final[int]
     def eventfd(initval: int, flags: int = 524288) -> FileDescriptor: ...
     def eventfd_read(fd: FileDescriptor) -> int: ...
     def eventfd_write(fd: FileDescriptor, value: int) -> None: ...
@@ -1618,20 +1619,20 @@ if sys.version_info >= (3, 10) and sys.platform == "linux":
     ) -> int: ...
 
 if sys.version_info >= (3, 12) and sys.platform == "linux":
-    CLONE_FILES: int
-    CLONE_FS: int
-    CLONE_NEWCGROUP: int  # Linux 4.6+
-    CLONE_NEWIPC: int  # Linux 2.6.19+
-    CLONE_NEWNET: int  # Linux 2.6.24+
-    CLONE_NEWNS: int
-    CLONE_NEWPID: int  # Linux 3.8+
-    CLONE_NEWTIME: int  # Linux 5.6+
-    CLONE_NEWUSER: int  # Linux 3.8+
-    CLONE_NEWUTS: int  # Linux 2.6.19+
-    CLONE_SIGHAND: int
-    CLONE_SYSVSEM: int  # Linux 2.6.26+
-    CLONE_THREAD: int
-    CLONE_VM: int
+    CLONE_FILES: Final[int]
+    CLONE_FS: Final[int]
+    CLONE_NEWCGROUP: Final[int]  # Linux 4.6+
+    CLONE_NEWIPC: Final[int]  # Linux 2.6.19+
+    CLONE_NEWNET: Final[int]  # Linux 2.6.24+
+    CLONE_NEWNS: Final[int]
+    CLONE_NEWPID: Final[int]  # Linux 3.8+
+    CLONE_NEWTIME: Final[int]  # Linux 5.6+
+    CLONE_NEWUSER: Final[int]  # Linux 3.8+
+    CLONE_NEWUTS: Final[int]  # Linux 2.6.19+
+    CLONE_SIGHAND: Final[int]
+    CLONE_SYSVSEM: Final[int]  # Linux 2.6.26+
+    CLONE_THREAD: Final[int]
+    CLONE_VM: Final[int]
     def unshare(flags: int) -> None: ...
     def setns(fd: FileDescriptorLike, nstype: int = 0) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/ossaudiodev.pyi b/mypy/typeshed/stdlib/ossaudiodev.pyi
index b9ee3edab033..f8230b4f0212 100644
--- a/mypy/typeshed/stdlib/ossaudiodev.pyi
+++ b/mypy/typeshed/stdlib/ossaudiodev.pyi
@@ -1,119 +1,120 @@
 import sys
-from typing import Any, Literal, overload
+from typing import Any, Final, Literal, overload
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    AFMT_AC3: int
-    AFMT_A_LAW: int
-    AFMT_IMA_ADPCM: int
-    AFMT_MPEG: int
-    AFMT_MU_LAW: int
-    AFMT_QUERY: int
-    AFMT_S16_BE: int
-    AFMT_S16_LE: int
-    AFMT_S16_NE: int
-    AFMT_S8: int
-    AFMT_U16_BE: int
-    AFMT_U16_LE: int
-    AFMT_U8: int
-    SNDCTL_COPR_HALT: int
-    SNDCTL_COPR_LOAD: int
-    SNDCTL_COPR_RCODE: int
-    SNDCTL_COPR_RCVMSG: int
-    SNDCTL_COPR_RDATA: int
-    SNDCTL_COPR_RESET: int
-    SNDCTL_COPR_RUN: int
-    SNDCTL_COPR_SENDMSG: int
-    SNDCTL_COPR_WCODE: int
-    SNDCTL_COPR_WDATA: int
-    SNDCTL_DSP_BIND_CHANNEL: int
-    SNDCTL_DSP_CHANNELS: int
-    SNDCTL_DSP_GETBLKSIZE: int
-    SNDCTL_DSP_GETCAPS: int
-    SNDCTL_DSP_GETCHANNELMASK: int
-    SNDCTL_DSP_GETFMTS: int
-    SNDCTL_DSP_GETIPTR: int
-    SNDCTL_DSP_GETISPACE: int
-    SNDCTL_DSP_GETODELAY: int
-    SNDCTL_DSP_GETOPTR: int
-    SNDCTL_DSP_GETOSPACE: int
-    SNDCTL_DSP_GETSPDIF: int
-    SNDCTL_DSP_GETTRIGGER: int
-    SNDCTL_DSP_MAPINBUF: int
-    SNDCTL_DSP_MAPOUTBUF: int
-    SNDCTL_DSP_NONBLOCK: int
-    SNDCTL_DSP_POST: int
-    SNDCTL_DSP_PROFILE: int
-    SNDCTL_DSP_RESET: int
-    SNDCTL_DSP_SAMPLESIZE: int
-    SNDCTL_DSP_SETDUPLEX: int
-    SNDCTL_DSP_SETFMT: int
-    SNDCTL_DSP_SETFRAGMENT: int
-    SNDCTL_DSP_SETSPDIF: int
-    SNDCTL_DSP_SETSYNCRO: int
-    SNDCTL_DSP_SETTRIGGER: int
-    SNDCTL_DSP_SPEED: int
-    SNDCTL_DSP_STEREO: int
-    SNDCTL_DSP_SUBDIVIDE: int
-    SNDCTL_DSP_SYNC: int
-    SNDCTL_FM_4OP_ENABLE: int
-    SNDCTL_FM_LOAD_INSTR: int
-    SNDCTL_MIDI_INFO: int
-    SNDCTL_MIDI_MPUCMD: int
-    SNDCTL_MIDI_MPUMODE: int
-    SNDCTL_MIDI_PRETIME: int
-    SNDCTL_SEQ_CTRLRATE: int
-    SNDCTL_SEQ_GETINCOUNT: int
-    SNDCTL_SEQ_GETOUTCOUNT: int
-    SNDCTL_SEQ_GETTIME: int
-    SNDCTL_SEQ_NRMIDIS: int
-    SNDCTL_SEQ_NRSYNTHS: int
-    SNDCTL_SEQ_OUTOFBAND: int
-    SNDCTL_SEQ_PANIC: int
-    SNDCTL_SEQ_PERCMODE: int
-    SNDCTL_SEQ_RESET: int
-    SNDCTL_SEQ_RESETSAMPLES: int
-    SNDCTL_SEQ_SYNC: int
-    SNDCTL_SEQ_TESTMIDI: int
-    SNDCTL_SEQ_THRESHOLD: int
-    SNDCTL_SYNTH_CONTROL: int
-    SNDCTL_SYNTH_ID: int
-    SNDCTL_SYNTH_INFO: int
-    SNDCTL_SYNTH_MEMAVL: int
-    SNDCTL_SYNTH_REMOVESAMPLE: int
-    SNDCTL_TMR_CONTINUE: int
-    SNDCTL_TMR_METRONOME: int
-    SNDCTL_TMR_SELECT: int
-    SNDCTL_TMR_SOURCE: int
-    SNDCTL_TMR_START: int
-    SNDCTL_TMR_STOP: int
-    SNDCTL_TMR_TEMPO: int
-    SNDCTL_TMR_TIMEBASE: int
-    SOUND_MIXER_ALTPCM: int
-    SOUND_MIXER_BASS: int
-    SOUND_MIXER_CD: int
-    SOUND_MIXER_DIGITAL1: int
-    SOUND_MIXER_DIGITAL2: int
-    SOUND_MIXER_DIGITAL3: int
-    SOUND_MIXER_IGAIN: int
-    SOUND_MIXER_IMIX: int
-    SOUND_MIXER_LINE: int
-    SOUND_MIXER_LINE1: int
-    SOUND_MIXER_LINE2: int
-    SOUND_MIXER_LINE3: int
-    SOUND_MIXER_MIC: int
-    SOUND_MIXER_MONITOR: int
-    SOUND_MIXER_NRDEVICES: int
-    SOUND_MIXER_OGAIN: int
-    SOUND_MIXER_PCM: int
-    SOUND_MIXER_PHONEIN: int
-    SOUND_MIXER_PHONEOUT: int
-    SOUND_MIXER_RADIO: int
-    SOUND_MIXER_RECLEV: int
-    SOUND_MIXER_SPEAKER: int
-    SOUND_MIXER_SYNTH: int
-    SOUND_MIXER_TREBLE: int
-    SOUND_MIXER_VIDEO: int
-    SOUND_MIXER_VOLUME: int
+    # Depends on soundcard.h
+    AFMT_AC3: Final[int]
+    AFMT_A_LAW: Final[int]
+    AFMT_IMA_ADPCM: Final[int]
+    AFMT_MPEG: Final[int]
+    AFMT_MU_LAW: Final[int]
+    AFMT_QUERY: Final[int]
+    AFMT_S16_BE: Final[int]
+    AFMT_S16_LE: Final[int]
+    AFMT_S16_NE: Final[int]
+    AFMT_S8: Final[int]
+    AFMT_U16_BE: Final[int]
+    AFMT_U16_LE: Final[int]
+    AFMT_U8: Final[int]
+    SNDCTL_COPR_HALT: Final[int]
+    SNDCTL_COPR_LOAD: Final[int]
+    SNDCTL_COPR_RCODE: Final[int]
+    SNDCTL_COPR_RCVMSG: Final[int]
+    SNDCTL_COPR_RDATA: Final[int]
+    SNDCTL_COPR_RESET: Final[int]
+    SNDCTL_COPR_RUN: Final[int]
+    SNDCTL_COPR_SENDMSG: Final[int]
+    SNDCTL_COPR_WCODE: Final[int]
+    SNDCTL_COPR_WDATA: Final[int]
+    SNDCTL_DSP_BIND_CHANNEL: Final[int]
+    SNDCTL_DSP_CHANNELS: Final[int]
+    SNDCTL_DSP_GETBLKSIZE: Final[int]
+    SNDCTL_DSP_GETCAPS: Final[int]
+    SNDCTL_DSP_GETCHANNELMASK: Final[int]
+    SNDCTL_DSP_GETFMTS: Final[int]
+    SNDCTL_DSP_GETIPTR: Final[int]
+    SNDCTL_DSP_GETISPACE: Final[int]
+    SNDCTL_DSP_GETODELAY: Final[int]
+    SNDCTL_DSP_GETOPTR: Final[int]
+    SNDCTL_DSP_GETOSPACE: Final[int]
+    SNDCTL_DSP_GETSPDIF: Final[int]
+    SNDCTL_DSP_GETTRIGGER: Final[int]
+    SNDCTL_DSP_MAPINBUF: Final[int]
+    SNDCTL_DSP_MAPOUTBUF: Final[int]
+    SNDCTL_DSP_NONBLOCK: Final[int]
+    SNDCTL_DSP_POST: Final[int]
+    SNDCTL_DSP_PROFILE: Final[int]
+    SNDCTL_DSP_RESET: Final[int]
+    SNDCTL_DSP_SAMPLESIZE: Final[int]
+    SNDCTL_DSP_SETDUPLEX: Final[int]
+    SNDCTL_DSP_SETFMT: Final[int]
+    SNDCTL_DSP_SETFRAGMENT: Final[int]
+    SNDCTL_DSP_SETSPDIF: Final[int]
+    SNDCTL_DSP_SETSYNCRO: Final[int]
+    SNDCTL_DSP_SETTRIGGER: Final[int]
+    SNDCTL_DSP_SPEED: Final[int]
+    SNDCTL_DSP_STEREO: Final[int]
+    SNDCTL_DSP_SUBDIVIDE: Final[int]
+    SNDCTL_DSP_SYNC: Final[int]
+    SNDCTL_FM_4OP_ENABLE: Final[int]
+    SNDCTL_FM_LOAD_INSTR: Final[int]
+    SNDCTL_MIDI_INFO: Final[int]
+    SNDCTL_MIDI_MPUCMD: Final[int]
+    SNDCTL_MIDI_MPUMODE: Final[int]
+    SNDCTL_MIDI_PRETIME: Final[int]
+    SNDCTL_SEQ_CTRLRATE: Final[int]
+    SNDCTL_SEQ_GETINCOUNT: Final[int]
+    SNDCTL_SEQ_GETOUTCOUNT: Final[int]
+    SNDCTL_SEQ_GETTIME: Final[int]
+    SNDCTL_SEQ_NRMIDIS: Final[int]
+    SNDCTL_SEQ_NRSYNTHS: Final[int]
+    SNDCTL_SEQ_OUTOFBAND: Final[int]
+    SNDCTL_SEQ_PANIC: Final[int]
+    SNDCTL_SEQ_PERCMODE: Final[int]
+    SNDCTL_SEQ_RESET: Final[int]
+    SNDCTL_SEQ_RESETSAMPLES: Final[int]
+    SNDCTL_SEQ_SYNC: Final[int]
+    SNDCTL_SEQ_TESTMIDI: Final[int]
+    SNDCTL_SEQ_THRESHOLD: Final[int]
+    SNDCTL_SYNTH_CONTROL: Final[int]
+    SNDCTL_SYNTH_ID: Final[int]
+    SNDCTL_SYNTH_INFO: Final[int]
+    SNDCTL_SYNTH_MEMAVL: Final[int]
+    SNDCTL_SYNTH_REMOVESAMPLE: Final[int]
+    SNDCTL_TMR_CONTINUE: Final[int]
+    SNDCTL_TMR_METRONOME: Final[int]
+    SNDCTL_TMR_SELECT: Final[int]
+    SNDCTL_TMR_SOURCE: Final[int]
+    SNDCTL_TMR_START: Final[int]
+    SNDCTL_TMR_STOP: Final[int]
+    SNDCTL_TMR_TEMPO: Final[int]
+    SNDCTL_TMR_TIMEBASE: Final[int]
+    SOUND_MIXER_ALTPCM: Final[int]
+    SOUND_MIXER_BASS: Final[int]
+    SOUND_MIXER_CD: Final[int]
+    SOUND_MIXER_DIGITAL1: Final[int]
+    SOUND_MIXER_DIGITAL2: Final[int]
+    SOUND_MIXER_DIGITAL3: Final[int]
+    SOUND_MIXER_IGAIN: Final[int]
+    SOUND_MIXER_IMIX: Final[int]
+    SOUND_MIXER_LINE: Final[int]
+    SOUND_MIXER_LINE1: Final[int]
+    SOUND_MIXER_LINE2: Final[int]
+    SOUND_MIXER_LINE3: Final[int]
+    SOUND_MIXER_MIC: Final[int]
+    SOUND_MIXER_MONITOR: Final[int]
+    SOUND_MIXER_NRDEVICES: Final[int]
+    SOUND_MIXER_OGAIN: Final[int]
+    SOUND_MIXER_PCM: Final[int]
+    SOUND_MIXER_PHONEIN: Final[int]
+    SOUND_MIXER_PHONEOUT: Final[int]
+    SOUND_MIXER_RADIO: Final[int]
+    SOUND_MIXER_RECLEV: Final[int]
+    SOUND_MIXER_SPEAKER: Final[int]
+    SOUND_MIXER_SYNTH: Final[int]
+    SOUND_MIXER_TREBLE: Final[int]
+    SOUND_MIXER_VIDEO: Final[int]
+    SOUND_MIXER_VOLUME: Final[int]
 
     control_labels: list[str]
     control_names: list[str]
diff --git a/mypy/typeshed/stdlib/pathlib/__init__.pyi b/mypy/typeshed/stdlib/pathlib/__init__.pyi
index 4858f8db1ed0..fa5143f20292 100644
--- a/mypy/typeshed/stdlib/pathlib/__init__.pyi
+++ b/mypy/typeshed/stdlib/pathlib/__init__.pyi
@@ -29,6 +29,31 @@ if sys.version_info >= (3, 13):
     __all__ += ["UnsupportedOperation"]
 
 class PurePath(PathLike[str]):
+    if sys.version_info >= (3, 13):
+        __slots__ = (
+            "_raw_paths",
+            "_drv",
+            "_root",
+            "_tail_cached",
+            "_str",
+            "_str_normcase_cached",
+            "_parts_normcase_cached",
+            "_hash",
+        )
+    elif sys.version_info >= (3, 12):
+        __slots__ = (
+            "_raw_paths",
+            "_drv",
+            "_root",
+            "_tail_cached",
+            "_str",
+            "_str_normcase_cached",
+            "_parts_normcase_cached",
+            "_lines_cached",
+            "_hash",
+        )
+    else:
+        __slots__ = ("_drv", "_root", "_parts", "_str", "_hash", "_pparts", "_cached_cparts")
     if sys.version_info >= (3, 13):
         parser: ClassVar[types.ModuleType]
         def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ...
@@ -108,10 +133,20 @@ class PurePath(PathLike[str]):
     if sys.version_info >= (3, 12):
         def with_segments(self, *args: StrPath) -> Self: ...
 
-class PurePosixPath(PurePath): ...
-class PureWindowsPath(PurePath): ...
+class PurePosixPath(PurePath):
+    __slots__ = ()
+
+class PureWindowsPath(PurePath):
+    __slots__ = ()
 
 class Path(PurePath):
+    if sys.version_info >= (3, 14):
+        __slots__ = ("_info",)
+    elif sys.version_info >= (3, 10):
+        __slots__ = ()
+    else:
+        __slots__ = ("_accessor",)
+
     if sys.version_info >= (3, 12):
         def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ...  # pyright: ignore[reportInconsistentConstructor]
     else:
@@ -307,11 +342,14 @@ class Path(PurePath):
             def link_to(self, target: StrOrBytesPath) -> None: ...
     if sys.version_info >= (3, 12):
         def walk(
-            self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ...
+            self, top_down: bool = True, on_error: Callable[[OSError], object] | None = None, follow_symlinks: bool = False
         ) -> Iterator[tuple[Self, list[str], list[str]]]: ...
 
-class PosixPath(Path, PurePosixPath): ...
-class WindowsPath(Path, PureWindowsPath): ...
+class PosixPath(Path, PurePosixPath):
+    __slots__ = ()
+
+class WindowsPath(Path, PureWindowsPath):
+    __slots__ = ()
 
 if sys.version_info >= (3, 13):
     class UnsupportedOperation(NotImplementedError): ...
diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi
index ad69fcab16de..0c16f48e2e22 100644
--- a/mypy/typeshed/stdlib/pdb.pyi
+++ b/mypy/typeshed/stdlib/pdb.pyi
@@ -17,7 +17,7 @@ _T = TypeVar("_T")
 _P = ParamSpec("_P")
 _Mode: TypeAlias = Literal["inline", "cli"]
 
-line_prefix: str  # undocumented
+line_prefix: Final[str]  # undocumented
 
 class Restart(Exception): ...
 
@@ -131,7 +131,11 @@ class Pdb(Bdb, Cmd):
         def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ...
 
     def do_commands(self, arg: str) -> bool | None: ...
-    def do_break(self, arg: str, temporary: bool = ...) -> bool | None: ...
+    if sys.version_info >= (3, 14):
+        def do_break(self, arg: str, temporary: bool = False) -> bool | None: ...
+    else:
+        def do_break(self, arg: str, temporary: bool | Literal[0, 1] = 0) -> bool | None: ...
+
     def do_tbreak(self, arg: str) -> bool | None: ...
     def do_enable(self, arg: str) -> bool | None: ...
     def do_disable(self, arg: str) -> bool | None: ...
diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi
index 2d80d61645e0..d94fe208f446 100644
--- a/mypy/typeshed/stdlib/pickle.pyi
+++ b/mypy/typeshed/stdlib/pickle.pyi
@@ -14,7 +14,7 @@ from _pickle import (
 )
 from _typeshed import ReadableBuffer, SupportsWrite
 from collections.abc import Callable, Iterable, Mapping
-from typing import Any, ClassVar, SupportsBytes, SupportsIndex, final
+from typing import Any, ClassVar, Final, SupportsBytes, SupportsIndex, final
 from typing_extensions import Self
 
 __all__ = [
@@ -102,8 +102,8 @@ __all__ = [
     "UNICODE",
 ]
 
-HIGHEST_PROTOCOL: int
-DEFAULT_PROTOCOL: int
+HIGHEST_PROTOCOL: Final = 5
+DEFAULT_PROTOCOL: Final = 5
 
 bytes_types: tuple[type[Any], ...]  # undocumented
 
@@ -115,85 +115,85 @@ class PickleBuffer:
     def __buffer__(self, flags: int, /) -> memoryview: ...
     def __release_buffer__(self, buffer: memoryview, /) -> None: ...
 
-MARK: bytes
-STOP: bytes
-POP: bytes
-POP_MARK: bytes
-DUP: bytes
-FLOAT: bytes
-INT: bytes
-BININT: bytes
-BININT1: bytes
-LONG: bytes
-BININT2: bytes
-NONE: bytes
-PERSID: bytes
-BINPERSID: bytes
-REDUCE: bytes
-STRING: bytes
-BINSTRING: bytes
-SHORT_BINSTRING: bytes
-UNICODE: bytes
-BINUNICODE: bytes
-APPEND: bytes
-BUILD: bytes
-GLOBAL: bytes
-DICT: bytes
-EMPTY_DICT: bytes
-APPENDS: bytes
-GET: bytes
-BINGET: bytes
-INST: bytes
-LONG_BINGET: bytes
-LIST: bytes
-EMPTY_LIST: bytes
-OBJ: bytes
-PUT: bytes
-BINPUT: bytes
-LONG_BINPUT: bytes
-SETITEM: bytes
-TUPLE: bytes
-EMPTY_TUPLE: bytes
-SETITEMS: bytes
-BINFLOAT: bytes
+MARK: Final = b"("
+STOP: Final = b"."
+POP: Final = b"0"
+POP_MARK: Final = b"1"
+DUP: Final = b"2"
+FLOAT: Final = b"F"
+INT: Final = b"I"
+BININT: Final = b"J"
+BININT1: Final = b"K"
+LONG: Final = b"L"
+BININT2: Final = b"M"
+NONE: Final = b"N"
+PERSID: Final = b"P"
+BINPERSID: Final = b"Q"
+REDUCE: Final = b"R"
+STRING: Final = b"S"
+BINSTRING: Final = b"T"
+SHORT_BINSTRING: Final = b"U"
+UNICODE: Final = b"V"
+BINUNICODE: Final = b"X"
+APPEND: Final = b"a"
+BUILD: Final = b"b"
+GLOBAL: Final = b"c"
+DICT: Final = b"d"
+EMPTY_DICT: Final = b"}"
+APPENDS: Final = b"e"
+GET: Final = b"g"
+BINGET: Final = b"h"
+INST: Final = b"i"
+LONG_BINGET: Final = b"j"
+LIST: Final = b"l"
+EMPTY_LIST: Final = b"]"
+OBJ: Final = b"o"
+PUT: Final = b"p"
+BINPUT: Final = b"q"
+LONG_BINPUT: Final = b"r"
+SETITEM: Final = b"s"
+TUPLE: Final = b"t"
+EMPTY_TUPLE: Final = b")"
+SETITEMS: Final = b"u"
+BINFLOAT: Final = b"G"
 
-TRUE: bytes
-FALSE: bytes
+TRUE: Final = b"I01\n"
+FALSE: Final = b"I00\n"
 
 # protocol 2
-PROTO: bytes
-NEWOBJ: bytes
-EXT1: bytes
-EXT2: bytes
-EXT4: bytes
-TUPLE1: bytes
-TUPLE2: bytes
-TUPLE3: bytes
-NEWTRUE: bytes
-NEWFALSE: bytes
-LONG1: bytes
-LONG4: bytes
+PROTO: Final = b"\x80"
+NEWOBJ: Final = b"\x81"
+EXT1: Final = b"\x82"
+EXT2: Final = b"\x83"
+EXT4: Final = b"\x84"
+TUPLE1: Final = b"\x85"
+TUPLE2: Final = b"\x86"
+TUPLE3: Final = b"\x87"
+NEWTRUE: Final = b"\x88"
+NEWFALSE: Final = b"\x89"
+LONG1: Final = b"\x8a"
+LONG4: Final = b"\x8b"
 
 # protocol 3
-BINBYTES: bytes
-SHORT_BINBYTES: bytes
+BINBYTES: Final = b"B"
+SHORT_BINBYTES: Final = b"C"
 
 # protocol 4
-SHORT_BINUNICODE: bytes
-BINUNICODE8: bytes
-BINBYTES8: bytes
-EMPTY_SET: bytes
-ADDITEMS: bytes
-FROZENSET: bytes
-NEWOBJ_EX: bytes
-STACK_GLOBAL: bytes
-MEMOIZE: bytes
-FRAME: bytes
+SHORT_BINUNICODE: Final = b"\x8c"
+BINUNICODE8: Final = b"\x8d"
+BINBYTES8: Final = b"\x8e"
+EMPTY_SET: Final = b"\x8f"
+ADDITEMS: Final = b"\x90"
+FROZENSET: Final = b"\x91"
+NEWOBJ_EX: Final = b"\x92"
+STACK_GLOBAL: Final = b"\x93"
+MEMOIZE: Final = b"\x94"
+FRAME: Final = b"\x95"
 
 # protocol 5
-BYTEARRAY8: bytes
-NEXT_BUFFER: bytes
-READONLY_BUFFER: bytes
+BYTEARRAY8: Final = b"\x96"
+NEXT_BUFFER: Final = b"\x97"
+READONLY_BUFFER: Final = b"\x98"
 
 def encode_long(x: int) -> bytes: ...  # undocumented
 def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ...  # undocumented
diff --git a/mypy/typeshed/stdlib/pickletools.pyi b/mypy/typeshed/stdlib/pickletools.pyi
index cdade08d39a8..8bbfaba31b67 100644
--- a/mypy/typeshed/stdlib/pickletools.pyi
+++ b/mypy/typeshed/stdlib/pickletools.pyi
@@ -1,6 +1,6 @@
 import sys
 from collections.abc import Callable, Iterator, MutableMapping
-from typing import IO, Any
+from typing import IO, Any, Final
 from typing_extensions import TypeAlias
 
 __all__ = ["dis", "genops", "optimize"]
@@ -8,13 +8,14 @@ __all__ = ["dis", "genops", "optimize"]
 _Reader: TypeAlias = Callable[[IO[bytes]], Any]
 bytes_types: tuple[type[Any], ...]
 
-UP_TO_NEWLINE: int
-TAKEN_FROM_ARGUMENT1: int
-TAKEN_FROM_ARGUMENT4: int
-TAKEN_FROM_ARGUMENT4U: int
-TAKEN_FROM_ARGUMENT8U: int
+UP_TO_NEWLINE: Final = -1
+TAKEN_FROM_ARGUMENT1: Final = -2
+TAKEN_FROM_ARGUMENT4: Final = -3
+TAKEN_FROM_ARGUMENT4U: Final = -4
+TAKEN_FROM_ARGUMENT8U: Final = -5
 
 class ArgumentDescriptor:
+    __slots__ = ("name", "n", "reader", "doc")
     name: str
     n: int
     reader: _Reader
@@ -118,6 +119,7 @@ def read_long4(f: IO[bytes]) -> int: ...
 long4: ArgumentDescriptor
 
 class StackObject:
+    __slots__ = ("name", "obtype", "doc")
     name: str
     obtype: type[Any] | tuple[type[Any], ...]
     doc: str
@@ -143,6 +145,7 @@ markobject: StackObject
 stackslice: StackObject
 
 class OpcodeInfo:
+    __slots__ = ("name", "code", "arg", "stack_before", "stack_after", "proto", "doc")
     name: str
     code: str
     arg: ArgumentDescriptor | None
diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi
index c6125bd3a56f..69d702bb155c 100644
--- a/mypy/typeshed/stdlib/platform.pyi
+++ b/mypy/typeshed/stdlib/platform.pyi
@@ -1,6 +1,6 @@
 import sys
 from typing import NamedTuple, type_check_only
-from typing_extensions import Self, deprecated
+from typing_extensions import Self, deprecated, disjoint_base
 
 def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ...
 def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ...
@@ -46,13 +46,23 @@ class _uname_result_base(NamedTuple):
 
 # uname_result emulates a 6-field named tuple, but the processor field
 # is lazily evaluated rather than being passed in to the constructor.
-class uname_result(_uname_result_base):
-    if sys.version_info >= (3, 10):
+if sys.version_info >= (3, 12):
+    class uname_result(_uname_result_base):
         __match_args__ = ("system", "node", "release", "version", "machine")  # pyright: ignore[reportAssignmentType]
 
-    def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ...
-    @property
-    def processor(self) -> str: ...
+        def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ...
+        @property
+        def processor(self) -> str: ...
+
+else:
+    @disjoint_base
+    class uname_result(_uname_result_base):
+        if sys.version_info >= (3, 10):
+            __match_args__ = ("system", "node", "release", "version", "machine")  # pyright: ignore[reportAssignmentType]
+
+        def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ...
+        @property
+        def processor(self) -> str: ...
 
 def uname() -> uname_result: ...
 def system() -> str: ...
@@ -68,7 +78,7 @@ def python_branch() -> str: ...
 def python_revision() -> str: ...
 def python_build() -> tuple[str, str]: ...
 def python_compiler() -> str: ...
-def platform(aliased: bool = ..., terse: bool = ...) -> str: ...
+def platform(aliased: bool = False, terse: bool = False) -> str: ...
 
 if sys.version_info >= (3, 10):
     def freedesktop_os_release() -> dict[str, str]: ...
diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi
index 8b39b4217eae..dc3247ee47fb 100644
--- a/mypy/typeshed/stdlib/plistlib.pyi
+++ b/mypy/typeshed/stdlib/plistlib.pyi
@@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer
 from collections.abc import Mapping, MutableMapping
 from datetime import datetime
 from enum import Enum
-from typing import IO, Any
+from typing import IO, Any, Final
 from typing_extensions import Self
 
 __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"]
@@ -12,8 +12,8 @@ class PlistFormat(Enum):
     FMT_XML = 1
     FMT_BINARY = 2
 
-FMT_XML = PlistFormat.FMT_XML
-FMT_BINARY = PlistFormat.FMT_BINARY
+FMT_XML: Final = PlistFormat.FMT_XML
+FMT_BINARY: Final = PlistFormat.FMT_BINARY
 if sys.version_info >= (3, 13):
     def load(
         fp: IO[bytes],
diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi
index a1e41be86a7f..9ff2b764aeb6 100644
--- a/mypy/typeshed/stdlib/poplib.pyi
+++ b/mypy/typeshed/stdlib/poplib.pyi
@@ -17,7 +17,7 @@ POP3_SSL_PORT: Final = 995
 CR: Final = b"\r"
 LF: Final = b"\n"
 CRLF: Final = b"\r\n"
-HAVE_SSL: bool
+HAVE_SSL: Final[bool]
 
 class POP3:
     encoding: str
diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi
index 3c78f9d2de8e..935f9420f88c 100644
--- a/mypy/typeshed/stdlib/pydoc.pyi
+++ b/mypy/typeshed/stdlib/pydoc.pyi
@@ -34,11 +34,11 @@ def visiblename(name: str, all: Container[str] | None = None, obj: object = None
 def classify_class_attrs(object: object) -> list[tuple[str, str, type, str]]: ...
 
 if sys.version_info >= (3, 13):
-    @deprecated("Deprecated in Python 3.13.")
-    def ispackage(path: str) -> bool: ...
+    @deprecated("Deprecated since Python 3.13.")
+    def ispackage(path: str) -> bool: ...  # undocumented
 
 else:
-    def ispackage(path: str) -> bool: ...
+    def ispackage(path: str) -> bool: ...  # undocumented
 
 def source_synopsis(file: IO[AnyStr]) -> AnyStr | None: ...
 def synopsis(filename: str, cache: MutableMapping[str, tuple[int, str]] = {}) -> str | None: ...
diff --git a/mypy/typeshed/stdlib/pydoc_data/topics.pyi b/mypy/typeshed/stdlib/pydoc_data/topics.pyi
index 091d34300106..ce907a41c005 100644
--- a/mypy/typeshed/stdlib/pydoc_data/topics.pyi
+++ b/mypy/typeshed/stdlib/pydoc_data/topics.pyi
@@ -1 +1,3 @@
-topics: dict[str, str]
+from typing import Final
+
+topics: Final[dict[str, str]]
diff --git a/mypy/typeshed/stdlib/random.pyi b/mypy/typeshed/stdlib/random.pyi
index 83e37113a941..a797794b8050 100644
--- a/mypy/typeshed/stdlib/random.pyi
+++ b/mypy/typeshed/stdlib/random.pyi
@@ -4,6 +4,7 @@ from _typeshed import SupportsLenAndGetItem
 from collections.abc import Callable, Iterable, MutableSequence, Sequence, Set as AbstractSet
 from fractions import Fraction
 from typing import Any, ClassVar, NoReturn, TypeVar
+from typing_extensions import Self
 
 __all__ = [
     "Random",
@@ -44,6 +45,10 @@ class Random(_random.Random):
     # Using other `seed` types is deprecated since 3.9 and removed in 3.11
     # Ignore Y041, since random.seed doesn't treat int like a float subtype. Having an explicit
     # int better documents conventional usage of random.seed.
+    if sys.version_info < (3, 10):
+        # this is a workaround for pyright correctly flagging an inconsistent inherited constructor, see #14624
+        def __new__(cls, x: int | float | str | bytes | bytearray | None = None) -> Self: ...  # noqa: Y041
+
     def seed(self, a: int | float | str | bytes | bytearray | None = None, version: int = 2) -> None: ...  # type: ignore[override]  # noqa: Y041
     def getstate(self) -> tuple[Any, ...]: ...
     def setstate(self, state: tuple[Any, ...]) -> None: ...
diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi
index 5e468c2cead5..f99cd5b08805 100644
--- a/mypy/typeshed/stdlib/resource.pyi
+++ b/mypy/typeshed/stdlib/resource.pyi
@@ -3,27 +3,28 @@ from _typeshed import structseq
 from typing import Final, final
 
 if sys.platform != "win32":
-    RLIMIT_AS: int
-    RLIMIT_CORE: int
-    RLIMIT_CPU: int
-    RLIMIT_DATA: int
-    RLIMIT_FSIZE: int
-    RLIMIT_MEMLOCK: int
-    RLIMIT_NOFILE: int
-    RLIMIT_NPROC: int
-    RLIMIT_RSS: int
-    RLIMIT_STACK: int
-    RLIM_INFINITY: int
-    RUSAGE_CHILDREN: int
-    RUSAGE_SELF: int
+    # Depends on resource.h
+    RLIMIT_AS: Final[int]
+    RLIMIT_CORE: Final[int]
+    RLIMIT_CPU: Final[int]
+    RLIMIT_DATA: Final[int]
+    RLIMIT_FSIZE: Final[int]
+    RLIMIT_MEMLOCK: Final[int]
+    RLIMIT_NOFILE: Final[int]
+    RLIMIT_NPROC: Final[int]
+    RLIMIT_RSS: Final[int]
+    RLIMIT_STACK: Final[int]
+    RLIM_INFINITY: Final[int]
+    RUSAGE_CHILDREN: Final[int]
+    RUSAGE_SELF: Final[int]
     if sys.platform == "linux":
-        RLIMIT_MSGQUEUE: int
-        RLIMIT_NICE: int
-        RLIMIT_OFILE: int
-        RLIMIT_RTPRIO: int
-        RLIMIT_RTTIME: int
-        RLIMIT_SIGPENDING: int
-        RUSAGE_THREAD: int
+        RLIMIT_MSGQUEUE: Final[int]
+        RLIMIT_NICE: Final[int]
+        RLIMIT_OFILE: Final[int]
+        RLIMIT_RTPRIO: Final[int]
+        RLIMIT_RTTIME: Final[int]
+        RLIMIT_SIGPENDING: Final[int]
+        RUSAGE_THREAD: Final[int]
 
     @final
     class struct_rusage(
diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi
index 023547390273..587bc75376ef 100644
--- a/mypy/typeshed/stdlib/select.pyi
+++ b/mypy/typeshed/stdlib/select.pyi
@@ -2,25 +2,25 @@ import sys
 from _typeshed import FileDescriptorLike
 from collections.abc import Iterable
 from types import TracebackType
-from typing import Any, ClassVar, final
+from typing import Any, ClassVar, Final, final
 from typing_extensions import Self
 
 if sys.platform != "win32":
-    PIPE_BUF: int
-    POLLERR: int
-    POLLHUP: int
-    POLLIN: int
+    PIPE_BUF: Final[int]
+    POLLERR: Final[int]
+    POLLHUP: Final[int]
+    POLLIN: Final[int]
     if sys.platform == "linux":
-        POLLMSG: int
-    POLLNVAL: int
-    POLLOUT: int
-    POLLPRI: int
-    POLLRDBAND: int
+        POLLMSG: Final[int]
+    POLLNVAL: Final[int]
+    POLLOUT: Final[int]
+    POLLPRI: Final[int]
+    POLLRDBAND: Final[int]
     if sys.platform == "linux":
-        POLLRDHUP: int
-    POLLRDNORM: int
-    POLLWRBAND: int
-    POLLWRNORM: int
+        POLLRDHUP: Final[int]
+    POLLRDNORM: Final[int]
+    POLLWRBAND: Final[int]
+    POLLWRNORM: Final[int]
 
     # This is actually a function that returns an instance of a class.
     # The class is not accessible directly, and also calls itself select.poll.
@@ -71,50 +71,50 @@ if sys.platform != "linux" and sys.platform != "win32":
         @classmethod
         def fromfd(cls, fd: FileDescriptorLike, /) -> kqueue: ...
 
-    KQ_EV_ADD: int
-    KQ_EV_CLEAR: int
-    KQ_EV_DELETE: int
-    KQ_EV_DISABLE: int
-    KQ_EV_ENABLE: int
-    KQ_EV_EOF: int
-    KQ_EV_ERROR: int
-    KQ_EV_FLAG1: int
-    KQ_EV_ONESHOT: int
-    KQ_EV_SYSFLAGS: int
-    KQ_FILTER_AIO: int
+    KQ_EV_ADD: Final[int]
+    KQ_EV_CLEAR: Final[int]
+    KQ_EV_DELETE: Final[int]
+    KQ_EV_DISABLE: Final[int]
+    KQ_EV_ENABLE: Final[int]
+    KQ_EV_EOF: Final[int]
+    KQ_EV_ERROR: Final[int]
+    KQ_EV_FLAG1: Final[int]
+    KQ_EV_ONESHOT: Final[int]
+    KQ_EV_SYSFLAGS: Final[int]
+    KQ_FILTER_AIO: Final[int]
     if sys.platform != "darwin":
-        KQ_FILTER_NETDEV: int
-    KQ_FILTER_PROC: int
-    KQ_FILTER_READ: int
-    KQ_FILTER_SIGNAL: int
-    KQ_FILTER_TIMER: int
-    KQ_FILTER_VNODE: int
-    KQ_FILTER_WRITE: int
-    KQ_NOTE_ATTRIB: int
-    KQ_NOTE_CHILD: int
-    KQ_NOTE_DELETE: int
-    KQ_NOTE_EXEC: int
-    KQ_NOTE_EXIT: int
-    KQ_NOTE_EXTEND: int
-    KQ_NOTE_FORK: int
-    KQ_NOTE_LINK: int
+        KQ_FILTER_NETDEV: Final[int]
+    KQ_FILTER_PROC: Final[int]
+    KQ_FILTER_READ: Final[int]
+    KQ_FILTER_SIGNAL: Final[int]
+    KQ_FILTER_TIMER: Final[int]
+    KQ_FILTER_VNODE: Final[int]
+    KQ_FILTER_WRITE: Final[int]
+    KQ_NOTE_ATTRIB: Final[int]
+    KQ_NOTE_CHILD: Final[int]
+    KQ_NOTE_DELETE: Final[int]
+    KQ_NOTE_EXEC: Final[int]
+    KQ_NOTE_EXIT: Final[int]
+    KQ_NOTE_EXTEND: Final[int]
+    KQ_NOTE_FORK: Final[int]
+    KQ_NOTE_LINK: Final[int]
     if sys.platform != "darwin":
-        KQ_NOTE_LINKDOWN: int
-        KQ_NOTE_LINKINV: int
-        KQ_NOTE_LINKUP: int
-    KQ_NOTE_LOWAT: int
-    KQ_NOTE_PCTRLMASK: int
-    KQ_NOTE_PDATAMASK: int
-    KQ_NOTE_RENAME: int
-    KQ_NOTE_REVOKE: int
-    KQ_NOTE_TRACK: int
-    KQ_NOTE_TRACKERR: int
-    KQ_NOTE_WRITE: int
+        KQ_NOTE_LINKDOWN: Final[int]
+        KQ_NOTE_LINKINV: Final[int]
+        KQ_NOTE_LINKUP: Final[int]
+    KQ_NOTE_LOWAT: Final[int]
+    KQ_NOTE_PCTRLMASK: Final[int]
+    KQ_NOTE_PDATAMASK: Final[int]
+    KQ_NOTE_RENAME: Final[int]
+    KQ_NOTE_REVOKE: Final[int]
+    KQ_NOTE_TRACK: Final[int]
+    KQ_NOTE_TRACKERR: Final[int]
+    KQ_NOTE_WRITE: Final[int]
 
 if sys.platform == "linux":
     @final
     class epoll:
-        def __init__(self, sizehint: int = ..., flags: int = ...) -> None: ...
+        def __new__(self, sizehint: int = ..., flags: int = ...) -> Self: ...
         def __enter__(self) -> Self: ...
         def __exit__(
             self,
@@ -133,23 +133,23 @@ if sys.platform == "linux":
         @classmethod
         def fromfd(cls, fd: FileDescriptorLike, /) -> epoll: ...
 
-    EPOLLERR: int
-    EPOLLEXCLUSIVE: int
-    EPOLLET: int
-    EPOLLHUP: int
-    EPOLLIN: int
-    EPOLLMSG: int
-    EPOLLONESHOT: int
-    EPOLLOUT: int
-    EPOLLPRI: int
-    EPOLLRDBAND: int
-    EPOLLRDHUP: int
-    EPOLLRDNORM: int
-    EPOLLWRBAND: int
-    EPOLLWRNORM: int
-    EPOLL_CLOEXEC: int
+    EPOLLERR: Final[int]
+    EPOLLEXCLUSIVE: Final[int]
+    EPOLLET: Final[int]
+    EPOLLHUP: Final[int]
+    EPOLLIN: Final[int]
+    EPOLLMSG: Final[int]
+    EPOLLONESHOT: Final[int]
+    EPOLLOUT: Final[int]
+    EPOLLPRI: Final[int]
+    EPOLLRDBAND: Final[int]
+    EPOLLRDHUP: Final[int]
+    EPOLLRDNORM: Final[int]
+    EPOLLWRBAND: Final[int]
+    EPOLLWRNORM: Final[int]
+    EPOLL_CLOEXEC: Final[int]
     if sys.version_info >= (3, 14):
-        EPOLLWAKEUP: int
+        EPOLLWAKEUP: Final[int]
 
 if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32":
     # Solaris only
diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi
index 0ba843a403d8..bcca4e341b9a 100644
--- a/mypy/typeshed/stdlib/selectors.pyi
+++ b/mypy/typeshed/stdlib/selectors.pyi
@@ -2,13 +2,13 @@ import sys
 from _typeshed import FileDescriptor, FileDescriptorLike, Unused
 from abc import ABCMeta, abstractmethod
 from collections.abc import Mapping
-from typing import Any, NamedTuple
+from typing import Any, Final, NamedTuple
 from typing_extensions import Self, TypeAlias
 
 _EventMask: TypeAlias = int
 
-EVENT_READ: _EventMask
-EVENT_WRITE: _EventMask
+EVENT_READ: Final = 1
+EVENT_WRITE: Final = 2
 
 class SelectorKey(NamedTuple):
     fileobj: FileDescriptorLike
diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi
index 3d392c047993..6a8467689367 100644
--- a/mypy/typeshed/stdlib/smtplib.pyi
+++ b/mypy/typeshed/stdlib/smtplib.pyi
@@ -7,7 +7,7 @@ from re import Pattern
 from socket import socket
 from ssl import SSLContext
 from types import TracebackType
-from typing import Any, Protocol, overload, type_check_only
+from typing import Any, Final, Protocol, overload, type_check_only
 from typing_extensions import Self, TypeAlias
 
 __all__ = [
@@ -30,12 +30,12 @@ __all__ = [
 _Reply: TypeAlias = tuple[int, bytes]
 _SendErrs: TypeAlias = dict[str, _Reply]
 
-SMTP_PORT: int
-SMTP_SSL_PORT: int
-CRLF: str
-bCRLF: bytes
+SMTP_PORT: Final = 25
+SMTP_SSL_PORT: Final = 465
+CRLF: Final[str]
+bCRLF: Final[bytes]
 
-OLDSTYLE_AUTH: Pattern[str]
+OLDSTYLE_AUTH: Final[Pattern[str]]
 
 class SMTPException(OSError): ...
 class SMTPNotSupportedError(SMTPException): ...
@@ -182,7 +182,7 @@ class SMTP_SSL(SMTP):
             context: SSLContext | None = None,
         ) -> None: ...
 
-LMTP_PORT: int
+LMTP_PORT: Final = 2003
 
 class LMTP(SMTP):
     def __init__(
diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi
index d62f4c228151..b10b3560b91f 100644
--- a/mypy/typeshed/stdlib/socket.pyi
+++ b/mypy/typeshed/stdlib/socket.pyi
@@ -136,7 +136,7 @@ from _typeshed import ReadableBuffer, Unused, WriteableBuffer
 from collections.abc import Iterable
 from enum import IntEnum, IntFlag
 from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase, TextIOWrapper
-from typing import Any, Literal, Protocol, SupportsIndex, overload, type_check_only
+from typing import Any, Final, Literal, Protocol, SupportsIndex, overload, type_check_only
 from typing_extensions import Self
 
 __all__ = [
@@ -1059,9 +1059,9 @@ if sys.version_info >= (3, 14):
         __all__ += ["IP_FREEBIND", "IP_RECVORIGDSTADDR", "VMADDR_CID_LOCAL"]
 
 # Re-exported from errno
-EBADF: int
-EAGAIN: int
-EWOULDBLOCK: int
+EBADF: Final[int]
+EAGAIN: Final[int]
+EWOULDBLOCK: Final[int]
 
 # These errors are implemented in _socket at runtime
 # but they consider themselves to live in socket so we'll put them here.
@@ -1124,60 +1124,60 @@ class AddressFamily(IntEnum):
         # FreeBSD >= 14.0
         AF_DIVERT = 44
 
-AF_INET = AddressFamily.AF_INET
-AF_INET6 = AddressFamily.AF_INET6
-AF_APPLETALK = AddressFamily.AF_APPLETALK
-AF_DECnet: Literal[12]
-AF_IPX = AddressFamily.AF_IPX
-AF_SNA = AddressFamily.AF_SNA
-AF_UNSPEC = AddressFamily.AF_UNSPEC
+AF_INET: Final = AddressFamily.AF_INET
+AF_INET6: Final = AddressFamily.AF_INET6
+AF_APPLETALK: Final = AddressFamily.AF_APPLETALK
+AF_DECnet: Final = 12
+AF_IPX: Final = AddressFamily.AF_IPX
+AF_SNA: Final = AddressFamily.AF_SNA
+AF_UNSPEC: Final = AddressFamily.AF_UNSPEC
 
 if sys.platform != "darwin":
-    AF_IRDA = AddressFamily.AF_IRDA
+    AF_IRDA: Final = AddressFamily.AF_IRDA
 
 if sys.platform != "win32":
-    AF_ROUTE = AddressFamily.AF_ROUTE
-    AF_UNIX = AddressFamily.AF_UNIX
+    AF_ROUTE: Final = AddressFamily.AF_ROUTE
+    AF_UNIX: Final = AddressFamily.AF_UNIX
 
 if sys.platform == "darwin":
-    AF_SYSTEM = AddressFamily.AF_SYSTEM
+    AF_SYSTEM: Final = AddressFamily.AF_SYSTEM
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    AF_ASH = AddressFamily.AF_ASH
-    AF_ATMPVC = AddressFamily.AF_ATMPVC
-    AF_ATMSVC = AddressFamily.AF_ATMSVC
-    AF_AX25 = AddressFamily.AF_AX25
-    AF_BRIDGE = AddressFamily.AF_BRIDGE
-    AF_ECONET = AddressFamily.AF_ECONET
-    AF_KEY = AddressFamily.AF_KEY
-    AF_LLC = AddressFamily.AF_LLC
-    AF_NETBEUI = AddressFamily.AF_NETBEUI
-    AF_NETROM = AddressFamily.AF_NETROM
-    AF_PPPOX = AddressFamily.AF_PPPOX
-    AF_ROSE = AddressFamily.AF_ROSE
-    AF_SECURITY = AddressFamily.AF_SECURITY
-    AF_WANPIPE = AddressFamily.AF_WANPIPE
-    AF_X25 = AddressFamily.AF_X25
+    AF_ASH: Final = AddressFamily.AF_ASH
+    AF_ATMPVC: Final = AddressFamily.AF_ATMPVC
+    AF_ATMSVC: Final = AddressFamily.AF_ATMSVC
+    AF_AX25: Final = AddressFamily.AF_AX25
+    AF_BRIDGE: Final = AddressFamily.AF_BRIDGE
+    AF_ECONET: Final = AddressFamily.AF_ECONET
+    AF_KEY: Final = AddressFamily.AF_KEY
+    AF_LLC: Final = AddressFamily.AF_LLC
+    AF_NETBEUI: Final = AddressFamily.AF_NETBEUI
+    AF_NETROM: Final = AddressFamily.AF_NETROM
+    AF_PPPOX: Final = AddressFamily.AF_PPPOX
+    AF_ROSE: Final = AddressFamily.AF_ROSE
+    AF_SECURITY: Final = AddressFamily.AF_SECURITY
+    AF_WANPIPE: Final = AddressFamily.AF_WANPIPE
+    AF_X25: Final = AddressFamily.AF_X25
 
 if sys.platform == "linux":
-    AF_CAN = AddressFamily.AF_CAN
-    AF_PACKET = AddressFamily.AF_PACKET
-    AF_RDS = AddressFamily.AF_RDS
-    AF_TIPC = AddressFamily.AF_TIPC
-    AF_ALG = AddressFamily.AF_ALG
-    AF_NETLINK = AddressFamily.AF_NETLINK
-    AF_VSOCK = AddressFamily.AF_VSOCK
-    AF_QIPCRTR = AddressFamily.AF_QIPCRTR
+    AF_CAN: Final = AddressFamily.AF_CAN
+    AF_PACKET: Final = AddressFamily.AF_PACKET
+    AF_RDS: Final = AddressFamily.AF_RDS
+    AF_TIPC: Final = AddressFamily.AF_TIPC
+    AF_ALG: Final = AddressFamily.AF_ALG
+    AF_NETLINK: Final = AddressFamily.AF_NETLINK
+    AF_VSOCK: Final = AddressFamily.AF_VSOCK
+    AF_QIPCRTR: Final = AddressFamily.AF_QIPCRTR
 
 if sys.platform != "linux":
-    AF_LINK = AddressFamily.AF_LINK
+    AF_LINK: Final = AddressFamily.AF_LINK
 if sys.platform != "darwin" and sys.platform != "linux":
-    AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH
+    AF_BLUETOOTH: Final = AddressFamily.AF_BLUETOOTH
 if sys.platform == "win32" and sys.version_info >= (3, 12):
-    AF_HYPERV = AddressFamily.AF_HYPERV
+    AF_HYPERV: Final = AddressFamily.AF_HYPERV
 if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12):
     # FreeBSD >= 14.0
-    AF_DIVERT = AddressFamily.AF_DIVERT
+    AF_DIVERT: Final = AddressFamily.AF_DIVERT
 
 class SocketKind(IntEnum):
     SOCK_STREAM = 1
@@ -1189,14 +1189,14 @@ class SocketKind(IntEnum):
         SOCK_CLOEXEC = 524288
         SOCK_NONBLOCK = 2048
 
-SOCK_STREAM = SocketKind.SOCK_STREAM
-SOCK_DGRAM = SocketKind.SOCK_DGRAM
-SOCK_RAW = SocketKind.SOCK_RAW
-SOCK_RDM = SocketKind.SOCK_RDM
-SOCK_SEQPACKET = SocketKind.SOCK_SEQPACKET
+SOCK_STREAM: Final = SocketKind.SOCK_STREAM
+SOCK_DGRAM: Final = SocketKind.SOCK_DGRAM
+SOCK_RAW: Final = SocketKind.SOCK_RAW
+SOCK_RDM: Final = SocketKind.SOCK_RDM
+SOCK_SEQPACKET: Final = SocketKind.SOCK_SEQPACKET
 if sys.platform == "linux":
-    SOCK_CLOEXEC = SocketKind.SOCK_CLOEXEC
-    SOCK_NONBLOCK = SocketKind.SOCK_NONBLOCK
+    SOCK_CLOEXEC: Final = SocketKind.SOCK_CLOEXEC
+    SOCK_NONBLOCK: Final = SocketKind.SOCK_NONBLOCK
 
 class MsgFlag(IntFlag):
     MSG_CTRUNC = 8
@@ -1228,36 +1228,36 @@ class MsgFlag(IntFlag):
     if sys.platform != "win32" and sys.platform != "linux":
         MSG_EOF = 256
 
-MSG_CTRUNC = MsgFlag.MSG_CTRUNC
-MSG_DONTROUTE = MsgFlag.MSG_DONTROUTE
-MSG_OOB = MsgFlag.MSG_OOB
-MSG_PEEK = MsgFlag.MSG_PEEK
-MSG_TRUNC = MsgFlag.MSG_TRUNC
-MSG_WAITALL = MsgFlag.MSG_WAITALL
+MSG_CTRUNC: Final = MsgFlag.MSG_CTRUNC
+MSG_DONTROUTE: Final = MsgFlag.MSG_DONTROUTE
+MSG_OOB: Final = MsgFlag.MSG_OOB
+MSG_PEEK: Final = MsgFlag.MSG_PEEK
+MSG_TRUNC: Final = MsgFlag.MSG_TRUNC
+MSG_WAITALL: Final = MsgFlag.MSG_WAITALL
 
 if sys.platform == "win32":
-    MSG_BCAST = MsgFlag.MSG_BCAST
-    MSG_MCAST = MsgFlag.MSG_MCAST
+    MSG_BCAST: Final = MsgFlag.MSG_BCAST
+    MSG_MCAST: Final = MsgFlag.MSG_MCAST
 
 if sys.platform != "darwin":
-    MSG_ERRQUEUE = MsgFlag.MSG_ERRQUEUE
+    MSG_ERRQUEUE: Final = MsgFlag.MSG_ERRQUEUE
 
 if sys.platform != "win32":
-    MSG_DONTWAIT = MsgFlag.MSG_DONTWAIT
-    MSG_EOR = MsgFlag.MSG_EOR
-    MSG_NOSIGNAL = MsgFlag.MSG_NOSIGNAL  # Sometimes this exists on darwin, sometimes not
+    MSG_DONTWAIT: Final = MsgFlag.MSG_DONTWAIT
+    MSG_EOR: Final = MsgFlag.MSG_EOR
+    MSG_NOSIGNAL: Final = MsgFlag.MSG_NOSIGNAL  # Sometimes this exists on darwin, sometimes not
 
 if sys.platform != "win32" and sys.platform != "darwin":
-    MSG_CMSG_CLOEXEC = MsgFlag.MSG_CMSG_CLOEXEC
-    MSG_CONFIRM = MsgFlag.MSG_CONFIRM
-    MSG_FASTOPEN = MsgFlag.MSG_FASTOPEN
-    MSG_MORE = MsgFlag.MSG_MORE
+    MSG_CMSG_CLOEXEC: Final = MsgFlag.MSG_CMSG_CLOEXEC
+    MSG_CONFIRM: Final = MsgFlag.MSG_CONFIRM
+    MSG_FASTOPEN: Final = MsgFlag.MSG_FASTOPEN
+    MSG_MORE: Final = MsgFlag.MSG_MORE
 
 if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
-    MSG_NOTIFICATION = MsgFlag.MSG_NOTIFICATION
+    MSG_NOTIFICATION: Final = MsgFlag.MSG_NOTIFICATION
 
 if sys.platform != "win32" and sys.platform != "linux":
-    MSG_EOF = MsgFlag.MSG_EOF
+    MSG_EOF: Final = MsgFlag.MSG_EOF
 
 class AddressInfo(IntFlag):
     AI_ADDRCONFIG = 32
@@ -1272,18 +1272,18 @@ class AddressInfo(IntFlag):
         AI_MASK = 5127
         AI_V4MAPPED_CFG = 512
 
-AI_ADDRCONFIG = AddressInfo.AI_ADDRCONFIG
-AI_ALL = AddressInfo.AI_ALL
-AI_CANONNAME = AddressInfo.AI_CANONNAME
-AI_NUMERICHOST = AddressInfo.AI_NUMERICHOST
-AI_NUMERICSERV = AddressInfo.AI_NUMERICSERV
-AI_PASSIVE = AddressInfo.AI_PASSIVE
-AI_V4MAPPED = AddressInfo.AI_V4MAPPED
+AI_ADDRCONFIG: Final = AddressInfo.AI_ADDRCONFIG
+AI_ALL: Final = AddressInfo.AI_ALL
+AI_CANONNAME: Final = AddressInfo.AI_CANONNAME
+AI_NUMERICHOST: Final = AddressInfo.AI_NUMERICHOST
+AI_NUMERICSERV: Final = AddressInfo.AI_NUMERICSERV
+AI_PASSIVE: Final = AddressInfo.AI_PASSIVE
+AI_V4MAPPED: Final = AddressInfo.AI_V4MAPPED
 
 if sys.platform != "win32" and sys.platform != "linux":
-    AI_DEFAULT = AddressInfo.AI_DEFAULT
-    AI_MASK = AddressInfo.AI_MASK
-    AI_V4MAPPED_CFG = AddressInfo.AI_V4MAPPED_CFG
+    AI_DEFAULT: Final = AddressInfo.AI_DEFAULT
+    AI_MASK: Final = AddressInfo.AI_MASK
+    AI_V4MAPPED_CFG: Final = AddressInfo.AI_V4MAPPED_CFG
 
 if sys.platform == "win32":
     errorTab: dict[int, str]  # undocumented
@@ -1300,6 +1300,7 @@ class _SendableFile(Protocol):
     # def fileno(self) -> int: ...
 
 class socket(_socket.socket):
+    __slots__ = ["__weakref__", "_io_refs", "_closed"]
     def __init__(
         self, family: AddressFamily | int = -1, type: SocketKind | int = -1, proto: int = -1, fileno: int | None = None
     ) -> None: ...
diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
index 5a659deaccf6..882cd143c29c 100644
--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi
+++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi
@@ -63,7 +63,7 @@ from sqlite3.dbapi2 import (
 )
 from types import TracebackType
 from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload, type_check_only
-from typing_extensions import Self, TypeAlias
+from typing_extensions import Self, TypeAlias, disjoint_base
 
 if sys.version_info < (3, 14):
     from sqlite3.dbapi2 import version_info as version_info
@@ -268,6 +268,7 @@ class OperationalError(DatabaseError): ...
 class ProgrammingError(DatabaseError): ...
 class Warning(Exception): ...
 
+@disjoint_base
 class Connection:
     @property
     def DataError(self) -> type[DataError]: ...
@@ -405,6 +406,7 @@ class Connection:
         self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, /
     ) -> Literal[False]: ...
 
+@disjoint_base
 class Cursor(Iterator[Any]):
     arraysize: int
     @property
@@ -436,6 +438,7 @@ class Cursor(Iterator[Any]):
 class PrepareProtocol:
     def __init__(self, *args: object, **kwargs: object) -> None: ...
 
+@disjoint_base
 class Row(Sequence[Any]):
     def __new__(cls, cursor: Cursor, data: tuple[Any, ...], /) -> Self: ...
     def keys(self) -> list[str]: ...
diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
index d37a0d391ec6..9e170a81243d 100644
--- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
+++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi
@@ -66,7 +66,8 @@ from sqlite3 import (
     Row as Row,
     Warning as Warning,
 )
-from typing import Literal
+from typing import Final, Literal
+from typing_extensions import deprecated
 
 if sys.version_info >= (3, 12):
     from _sqlite3 import (
@@ -211,11 +212,15 @@ if sys.version_info >= (3, 11):
 
 if sys.version_info < (3, 14):
     # Deprecated and removed from _sqlite3 in 3.12, but removed from here in 3.14.
-    version: str
+    version: Final[str]
 
 if sys.version_info < (3, 12):
     if sys.version_info >= (3, 10):
         # deprecation wrapper that has a different name for the argument...
+        @deprecated(
+            "Deprecated since Python 3.10; removed in Python 3.12. "
+            "Open database in URI mode using `cache=shared` parameter instead."
+        )
         def enable_shared_cache(enable: int) -> None: ...
     else:
         from _sqlite3 import enable_shared_cache as enable_shared_cache
@@ -223,9 +228,9 @@ if sys.version_info < (3, 12):
 if sys.version_info < (3, 10):
     from _sqlite3 import OptimizedUnicode as OptimizedUnicode
 
-paramstyle: str
+paramstyle: Final = "qmark"
 threadsafety: Literal[0, 1, 3]
-apilevel: str
+apilevel: Final[str]
 Date = date
 Time = time
 Timestamp = datetime
@@ -236,7 +241,7 @@ def TimestampFromTicks(ticks: float) -> Timestamp: ...
 
 if sys.version_info < (3, 14):
     # Deprecated in 3.12, removed in 3.14.
-    version_info: tuple[int, int, int]
+    version_info: Final[tuple[int, int, int]]
 
-sqlite_version_info: tuple[int, int, int]
+sqlite_version_info: Final[tuple[int, int, int]]
 Binary = memoryview
diff --git a/mypy/typeshed/stdlib/sre_compile.pyi b/mypy/typeshed/stdlib/sre_compile.pyi
index 2d04a886c931..d8f0b7937e99 100644
--- a/mypy/typeshed/stdlib/sre_compile.pyi
+++ b/mypy/typeshed/stdlib/sre_compile.pyi
@@ -2,9 +2,9 @@ from re import Pattern
 from sre_constants import *
 from sre_constants import _NamedIntConstant
 from sre_parse import SubPattern
-from typing import Any
+from typing import Any, Final
 
-MAXCODE: int
+MAXCODE: Final[int]
 
 def dis(code: list[_NamedIntConstant]) -> None: ...
 def isstring(obj: Any) -> bool: ...
diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi
index a3921aa0fc3b..9a1da4ac89e7 100644
--- a/mypy/typeshed/stdlib/sre_constants.pyi
+++ b/mypy/typeshed/stdlib/sre_constants.pyi
@@ -1,15 +1,22 @@
 import sys
 from re import error as error
 from typing import Final
-from typing_extensions import Self
+from typing_extensions import Self, disjoint_base
 
 MAXGROUPS: Final[int]
 
 MAGIC: Final[int]
 
-class _NamedIntConstant(int):
-    name: str
-    def __new__(cls, value: int, name: str) -> Self: ...
+if sys.version_info >= (3, 12):
+    class _NamedIntConstant(int):
+        name: str
+        def __new__(cls, value: int, name: str) -> Self: ...
+
+else:
+    @disjoint_base
+    class _NamedIntConstant(int):
+        name: str
+        def __new__(cls, value: int, name: str) -> Self: ...
 
 MAXREPEAT: Final[_NamedIntConstant]
 OPCODES: list[_NamedIntConstant]
diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi
index c242bd2a065f..eaacbff312a9 100644
--- a/mypy/typeshed/stdlib/sre_parse.pyi
+++ b/mypy/typeshed/stdlib/sre_parse.pyi
@@ -3,24 +3,24 @@ from collections.abc import Iterable
 from re import Match, Pattern as _Pattern
 from sre_constants import *
 from sre_constants import _NamedIntConstant as _NIC, error as _Error
-from typing import Any, overload
+from typing import Any, Final, overload
 from typing_extensions import TypeAlias
 
-SPECIAL_CHARS: str
-REPEAT_CHARS: str
-DIGITS: frozenset[str]
-OCTDIGITS: frozenset[str]
-HEXDIGITS: frozenset[str]
-ASCIILETTERS: frozenset[str]
-WHITESPACE: frozenset[str]
-ESCAPES: dict[str, tuple[_NIC, int]]
-CATEGORIES: dict[str, tuple[_NIC, _NIC] | tuple[_NIC, list[tuple[_NIC, _NIC]]]]
-FLAGS: dict[str, int]
-TYPE_FLAGS: int
-GLOBAL_FLAGS: int
+SPECIAL_CHARS: Final = ".\\[{()*+?^$|"
+REPEAT_CHARS: Final = "*+?{"
+DIGITS: Final[frozenset[str]]
+OCTDIGITS: Final[frozenset[str]]
+HEXDIGITS: Final[frozenset[str]]
+ASCIILETTERS: Final[frozenset[str]]
+WHITESPACE: Final[frozenset[str]]
+ESCAPES: Final[dict[str, tuple[_NIC, int]]]
+CATEGORIES: Final[dict[str, tuple[_NIC, _NIC] | tuple[_NIC, list[tuple[_NIC, _NIC]]]]]
+FLAGS: Final[dict[str, int]]
+TYPE_FLAGS: Final[int]
+GLOBAL_FLAGS: Final[int]
 
 if sys.version_info >= (3, 11):
-    MAXWIDTH: int
+    MAXWIDTH: Final[int]
 
 if sys.version_info < (3, 11):
     class Verbose(Exception): ...
@@ -39,7 +39,7 @@ class State:
     lookbehindgroups: int | None
     @property
     def groups(self) -> int: ...
-    def opengroup(self, name: str | None = ...) -> int: ...
+    def opengroup(self, name: str | None = None) -> int: ...
     def closegroup(self, gid: int, p: SubPattern) -> None: ...
     def checkgroup(self, gid: int) -> bool: ...
     def checklookbehindgroup(self, gid: int, source: Tokenizer) -> None: ...
diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi
index f1893ec3194f..faa98cb39920 100644
--- a/mypy/typeshed/stdlib/ssl.pyi
+++ b/mypy/typeshed/stdlib/ssl.pyi
@@ -81,7 +81,7 @@ class SSLCertVerificationError(SSLError, ValueError):
 CertificateError = SSLCertVerificationError
 
 if sys.version_info < (3, 12):
-    @deprecated("Deprecated since Python 3.7. Removed in Python 3.12. Use `SSLContext.wrap_socket()` instead.")
+    @deprecated("Deprecated since Python 3.7; removed in Python 3.12. Use `SSLContext.wrap_socket()` instead.")
     def wrap_socket(
         sock: socket.socket,
         keyfile: StrOrBytesPath | None = None,
@@ -94,7 +94,7 @@ if sys.version_info < (3, 12):
         suppress_ragged_eofs: bool = True,
         ciphers: str | None = None,
     ) -> SSLSocket: ...
-    @deprecated("Deprecated since Python 3.7. Removed in Python 3.12.")
+    @deprecated("Deprecated since Python 3.7; removed in Python 3.12.")
     def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ...
 
 def cert_time_to_seconds(cert_time: str) -> int: ...
diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi
index 6d7d3fbb4956..ba9e5f1b6b71 100644
--- a/mypy/typeshed/stdlib/statistics.pyi
+++ b/mypy/typeshed/stdlib/statistics.pyi
@@ -79,6 +79,7 @@ def stdev(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: .
 def variance(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: ...
 
 class NormalDist:
+    __slots__ = {"_mu": "Arithmetic mean of a normal distribution", "_sigma": "Standard deviation of a normal distribution"}
     def __init__(self, mu: float = 0.0, sigma: float = 1.0) -> None: ...
     @property
     def mean(self) -> float: ...
diff --git a/mypy/typeshed/stdlib/string/__init__.pyi b/mypy/typeshed/stdlib/string/__init__.pyi
index 29fe27f39b80..c8b32a98e26d 100644
--- a/mypy/typeshed/stdlib/string/__init__.pyi
+++ b/mypy/typeshed/stdlib/string/__init__.pyi
@@ -2,7 +2,7 @@ import sys
 from _typeshed import StrOrLiteralStr
 from collections.abc import Iterable, Mapping, Sequence
 from re import Pattern, RegexFlag
-from typing import Any, ClassVar, overload
+from typing import Any, ClassVar, Final, overload
 from typing_extensions import LiteralString
 
 __all__ = [
@@ -20,15 +20,15 @@ __all__ = [
     "Template",
 ]
 
-ascii_letters: LiteralString
-ascii_lowercase: LiteralString
-ascii_uppercase: LiteralString
-digits: LiteralString
-hexdigits: LiteralString
-octdigits: LiteralString
-punctuation: LiteralString
-printable: LiteralString
-whitespace: LiteralString
+whitespace: Final = " \t\n\r\v\f"
+ascii_lowercase: Final = "abcdefghijklmnopqrstuvwxyz"
+ascii_uppercase: Final = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ascii_letters: Final[LiteralString]  # string too long
+digits: Final = "0123456789"
+hexdigits: Final = "0123456789abcdefABCDEF"
+octdigits: Final = "01234567"
+punctuation: Final = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
+printable: Final[LiteralString]  # string too long
 
 def capwords(s: StrOrLiteralStr, sep: StrOrLiteralStr | None = None) -> StrOrLiteralStr: ...
 
diff --git a/mypy/typeshed/stdlib/stringprep.pyi b/mypy/typeshed/stdlib/stringprep.pyi
index fc28c027ca9b..d67955e499c8 100644
--- a/mypy/typeshed/stdlib/stringprep.pyi
+++ b/mypy/typeshed/stdlib/stringprep.pyi
@@ -1,10 +1,12 @@
-b1_set: set[int]
-b3_exceptions: dict[int, str]
-c22_specials: set[int]
-c6_set: set[int]
-c7_set: set[int]
-c8_set: set[int]
-c9_set: set[int]
+from typing import Final
+
+b1_set: Final[set[int]]
+b3_exceptions: Final[dict[int, str]]
+c22_specials: Final[set[int]]
+c6_set: Final[set[int]]
+c7_set: Final[set[int]]
+c8_set: Final[set[int]]
+c9_set: Final[set[int]]
 
 def in_table_a1(code: str) -> bool: ...
 def in_table_b1(code: str) -> bool: ...
diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi
index 8b72e2ec7ae2..e1e25bcb50cb 100644
--- a/mypy/typeshed/stdlib/subprocess.pyi
+++ b/mypy/typeshed/stdlib/subprocess.pyi
@@ -106,7 +106,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -140,7 +140,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -174,7 +174,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -209,7 +209,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the *real* keyword only args start
         capture_output: bool = False,
         check: bool = False,
@@ -243,7 +243,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -277,7 +277,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -314,7 +314,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -347,7 +347,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -380,7 +380,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -414,7 +414,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the *real* keyword only args start
         capture_output: bool = False,
         check: bool = False,
@@ -447,7 +447,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -480,7 +480,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -516,7 +516,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -548,7 +548,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -580,7 +580,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -613,7 +613,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the *real* keyword only args start
         capture_output: bool = False,
         check: bool = False,
@@ -645,7 +645,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -677,7 +677,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         capture_output: bool = False,
         check: bool = False,
@@ -712,7 +712,7 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         encoding: str | None = None,
         timeout: float | None = None,
@@ -744,7 +744,7 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         encoding: str | None = None,
         timeout: float | None = None,
@@ -774,7 +774,7 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         encoding: str | None = None,
         timeout: float | None = None,
@@ -805,8 +805,8 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        timeout: float | None = ...,
+        pass_fds: Collection[int] = (),
+        timeout: float | None = None,
         *,
         encoding: str | None = None,
         text: bool | None = None,
@@ -837,8 +837,8 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        timeout: float | None = ...,
+        pass_fds: Collection[int] = (),
+        timeout: float | None = None,
         *,
         encoding: str | None = None,
         text: bool | None = None,
@@ -867,8 +867,8 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
-        timeout: float | None = ...,
+        pass_fds: Collection[int] = (),
+        timeout: float | None = None,
         *,
         encoding: str | None = None,
         text: bool | None = None,
@@ -897,10 +897,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: Literal[True],
@@ -928,10 +928,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str,
         errors: str | None = None,
         text: bool | None = None,
@@ -959,10 +959,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str,
         text: bool | None = None,
@@ -991,10 +991,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the real keyword only ones start
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
@@ -1022,10 +1022,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: None = None,
         errors: None = None,
         text: Literal[False] | None = None,
@@ -1053,10 +1053,10 @@ if sys.version_info >= (3, 11):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
@@ -1087,10 +1087,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: Literal[True],
@@ -1117,10 +1117,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str,
         errors: str | None = None,
         text: bool | None = None,
@@ -1147,10 +1147,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str,
         text: bool | None = None,
@@ -1178,10 +1178,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the real keyword only ones start
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
@@ -1208,10 +1208,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: None = None,
         errors: None = None,
         text: Literal[False] | None = None,
@@ -1238,10 +1238,10 @@ elif sys.version_info >= (3, 10):
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
@@ -1270,10 +1270,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: Literal[True],
@@ -1299,10 +1299,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str,
         errors: str | None = None,
         text: bool | None = None,
@@ -1328,10 +1328,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str,
         text: bool | None = None,
@@ -1358,10 +1358,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         # where the real keyword only ones start
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
@@ -1387,10 +1387,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: None = None,
         errors: None = None,
         text: Literal[False] | None = None,
@@ -1416,10 +1416,10 @@ else:
         creationflags: int = 0,
         restore_signals: bool = True,
         start_new_session: bool = False,
-        pass_fds: Collection[int] = ...,
+        pass_fds: Collection[int] = (),
         *,
         timeout: float | None = None,
-        input: _InputString | None = ...,
+        input: _InputString | None = None,
         encoding: str | None = None,
         errors: str | None = None,
         text: bool | None = None,
diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi
index d81645cb5687..f83a0a4c520e 100644
--- a/mypy/typeshed/stdlib/sunau.pyi
+++ b/mypy/typeshed/stdlib/sunau.pyi
@@ -1,25 +1,25 @@
 from _typeshed import Unused
-from typing import IO, Any, Literal, NamedTuple, NoReturn, overload
+from typing import IO, Any, Final, Literal, NamedTuple, NoReturn, overload
 from typing_extensions import Self, TypeAlias
 
 _File: TypeAlias = str | IO[bytes]
 
 class Error(Exception): ...
 
-AUDIO_FILE_MAGIC: int
-AUDIO_FILE_ENCODING_MULAW_8: int
-AUDIO_FILE_ENCODING_LINEAR_8: int
-AUDIO_FILE_ENCODING_LINEAR_16: int
-AUDIO_FILE_ENCODING_LINEAR_24: int
-AUDIO_FILE_ENCODING_LINEAR_32: int
-AUDIO_FILE_ENCODING_FLOAT: int
-AUDIO_FILE_ENCODING_DOUBLE: int
-AUDIO_FILE_ENCODING_ADPCM_G721: int
-AUDIO_FILE_ENCODING_ADPCM_G722: int
-AUDIO_FILE_ENCODING_ADPCM_G723_3: int
-AUDIO_FILE_ENCODING_ADPCM_G723_5: int
-AUDIO_FILE_ENCODING_ALAW_8: int
-AUDIO_UNKNOWN_SIZE: int
+AUDIO_FILE_MAGIC: Final = 0x2E736E64
+AUDIO_FILE_ENCODING_MULAW_8: Final = 1
+AUDIO_FILE_ENCODING_LINEAR_8: Final = 2
+AUDIO_FILE_ENCODING_LINEAR_16: Final = 3
+AUDIO_FILE_ENCODING_LINEAR_24: Final = 4
+AUDIO_FILE_ENCODING_LINEAR_32: Final = 5
+AUDIO_FILE_ENCODING_FLOAT: Final = 6
+AUDIO_FILE_ENCODING_DOUBLE: Final = 7
+AUDIO_FILE_ENCODING_ADPCM_G721: Final = 23
+AUDIO_FILE_ENCODING_ADPCM_G722: Final = 24
+AUDIO_FILE_ENCODING_ADPCM_G723_3: Final = 25
+AUDIO_FILE_ENCODING_ADPCM_G723_5: Final = 26
+AUDIO_FILE_ENCODING_ALAW_8: Final = 27
+AUDIO_UNKNOWN_SIZE: Final = 0xFFFFFFFF
 
 class _sunau_params(NamedTuple):
     nchannels: int
diff --git a/mypy/typeshed/stdlib/symbol.pyi b/mypy/typeshed/stdlib/symbol.pyi
index 48ae3567a1a5..5344ce504c6c 100644
--- a/mypy/typeshed/stdlib/symbol.pyi
+++ b/mypy/typeshed/stdlib/symbol.pyi
@@ -1,93 +1,95 @@
-single_input: int
-file_input: int
-eval_input: int
-decorator: int
-decorators: int
-decorated: int
-async_funcdef: int
-funcdef: int
-parameters: int
-typedargslist: int
-tfpdef: int
-varargslist: int
-vfpdef: int
-stmt: int
-simple_stmt: int
-small_stmt: int
-expr_stmt: int
-annassign: int
-testlist_star_expr: int
-augassign: int
-del_stmt: int
-pass_stmt: int
-flow_stmt: int
-break_stmt: int
-continue_stmt: int
-return_stmt: int
-yield_stmt: int
-raise_stmt: int
-import_stmt: int
-import_name: int
-import_from: int
-import_as_name: int
-dotted_as_name: int
-import_as_names: int
-dotted_as_names: int
-dotted_name: int
-global_stmt: int
-nonlocal_stmt: int
-assert_stmt: int
-compound_stmt: int
-async_stmt: int
-if_stmt: int
-while_stmt: int
-for_stmt: int
-try_stmt: int
-with_stmt: int
-with_item: int
-except_clause: int
-suite: int
-test: int
-test_nocond: int
-lambdef: int
-lambdef_nocond: int
-or_test: int
-and_test: int
-not_test: int
-comparison: int
-comp_op: int
-star_expr: int
-expr: int
-xor_expr: int
-and_expr: int
-shift_expr: int
-arith_expr: int
-term: int
-factor: int
-power: int
-atom_expr: int
-atom: int
-testlist_comp: int
-trailer: int
-subscriptlist: int
-subscript: int
-sliceop: int
-exprlist: int
-testlist: int
-dictorsetmaker: int
-classdef: int
-arglist: int
-argument: int
-comp_iter: int
-comp_for: int
-comp_if: int
-encoding_decl: int
-yield_expr: int
-yield_arg: int
-sync_comp_for: int
-func_body_suite: int
-func_type: int
-func_type_input: int
-namedexpr_test: int
-typelist: int
-sym_name: dict[int, str]
+from typing import Final
+
+single_input: Final[int]
+file_input: Final[int]
+eval_input: Final[int]
+decorator: Final[int]
+decorators: Final[int]
+decorated: Final[int]
+async_funcdef: Final[int]
+funcdef: Final[int]
+parameters: Final[int]
+typedargslist: Final[int]
+tfpdef: Final[int]
+varargslist: Final[int]
+vfpdef: Final[int]
+stmt: Final[int]
+simple_stmt: Final[int]
+small_stmt: Final[int]
+expr_stmt: Final[int]
+annassign: Final[int]
+testlist_star_expr: Final[int]
+augassign: Final[int]
+del_stmt: Final[int]
+pass_stmt: Final[int]
+flow_stmt: Final[int]
+break_stmt: Final[int]
+continue_stmt: Final[int]
+return_stmt: Final[int]
+yield_stmt: Final[int]
+raise_stmt: Final[int]
+import_stmt: Final[int]
+import_name: Final[int]
+import_from: Final[int]
+import_as_name: Final[int]
+dotted_as_name: Final[int]
+import_as_names: Final[int]
+dotted_as_names: Final[int]
+dotted_name: Final[int]
+global_stmt: Final[int]
+nonlocal_stmt: Final[int]
+assert_stmt: Final[int]
+compound_stmt: Final[int]
+async_stmt: Final[int]
+if_stmt: Final[int]
+while_stmt: Final[int]
+for_stmt: Final[int]
+try_stmt: Final[int]
+with_stmt: Final[int]
+with_item: Final[int]
+except_clause: Final[int]
+suite: Final[int]
+test: Final[int]
+test_nocond: Final[int]
+lambdef: Final[int]
+lambdef_nocond: Final[int]
+or_test: Final[int]
+and_test: Final[int]
+not_test: Final[int]
+comparison: Final[int]
+comp_op: Final[int]
+star_expr: Final[int]
+expr: Final[int]
+xor_expr: Final[int]
+and_expr: Final[int]
+shift_expr: Final[int]
+arith_expr: Final[int]
+term: Final[int]
+factor: Final[int]
+power: Final[int]
+atom_expr: Final[int]
+atom: Final[int]
+testlist_comp: Final[int]
+trailer: Final[int]
+subscriptlist: Final[int]
+subscript: Final[int]
+sliceop: Final[int]
+exprlist: Final[int]
+testlist: Final[int]
+dictorsetmaker: Final[int]
+classdef: Final[int]
+arglist: Final[int]
+argument: Final[int]
+comp_iter: Final[int]
+comp_for: Final[int]
+comp_if: Final[int]
+encoding_decl: Final[int]
+yield_expr: Final[int]
+yield_arg: Final[int]
+sync_comp_for: Final[int]
+func_body_suite: Final[int]
+func_type: Final[int]
+func_type_input: Final[int]
+namedexpr_test: Final[int]
+typelist: Final[int]
+sym_name: Final[dict[int, str]]
diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi
index d5f2be04b600..a727b878688e 100644
--- a/mypy/typeshed/stdlib/symtable.pyi
+++ b/mypy/typeshed/stdlib/symtable.pyi
@@ -49,8 +49,11 @@ class Function(SymbolTable):
     def get_nonlocals(self) -> tuple[str, ...]: ...
 
 class Class(SymbolTable):
-    @deprecated("deprecated in Python 3.14, will be removed in Python 3.16")
-    def get_methods(self) -> tuple[str, ...]: ...
+    if sys.version_info >= (3, 14):
+        @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.")
+        def get_methods(self) -> tuple[str, ...]: ...
+    else:
+        def get_methods(self) -> tuple[str, ...]: ...
 
 class Symbol:
     def __init__(
diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi
index b16e7c0abd05..7807b0eab01f 100644
--- a/mypy/typeshed/stdlib/sys/__init__.pyi
+++ b/mypy/typeshed/stdlib/sys/__init__.pyi
@@ -345,7 +345,7 @@ version_info: _version_info
 def call_tracing(func: Callable[..., _T], args: Any, /) -> _T: ...
 
 if sys.version_info >= (3, 13):
-    @deprecated("Deprecated in Python 3.13; use _clear_internal_caches() instead.")
+    @deprecated("Deprecated since Python 3.13. Use `_clear_internal_caches()` instead.")
     def _clear_type_cache() -> None: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/sys/_monitoring.pyi b/mypy/typeshed/stdlib/sys/_monitoring.pyi
index 3a8292ea0df4..5d231c7a93b3 100644
--- a/mypy/typeshed/stdlib/sys/_monitoring.pyi
+++ b/mypy/typeshed/stdlib/sys/_monitoring.pyi
@@ -11,10 +11,10 @@ from types import CodeType
 from typing import Any, Final, type_check_only
 from typing_extensions import deprecated
 
-DEBUGGER_ID: Final[int]
-COVERAGE_ID: Final[int]
-PROFILER_ID: Final[int]
-OPTIMIZER_ID: Final[int]
+DEBUGGER_ID: Final = 0
+COVERAGE_ID: Final = 1
+PROFILER_ID: Final = 2
+OPTIMIZER_ID: Final = 5
 
 def use_tool_id(tool_id: int, name: str, /) -> None: ...
 def free_tool_id(tool_id: int, /) -> None: ...
@@ -46,7 +46,7 @@ class _events:
         BRANCH_TAKEN: Final[int]
 
         @property
-        @deprecated("BRANCH is deprecated; use BRANCH_LEFT or BRANCH_TAKEN instead")
+        @deprecated("Deprecated since Python 3.14. Use `BRANCH_LEFT` or `BRANCH_TAKEN` instead.")
         def BRANCH(self) -> int: ...
 
     else:
diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi
index 4e394409bbe0..f6623ea9929d 100644
--- a/mypy/typeshed/stdlib/tarfile.pyi
+++ b/mypy/typeshed/stdlib/tarfile.pyi
@@ -656,7 +656,7 @@ class TarFile:
         members: Iterable[TarInfo] | None = None,
         *,
         numeric_owner: bool = False,
-        filter: _TarfileFilter | None = ...,
+        filter: _TarfileFilter | None = None,
     ) -> None: ...
     # Same situation as for `extractall`.
     def extract(
@@ -666,7 +666,7 @@ class TarFile:
         set_attrs: bool = True,
         *,
         numeric_owner: bool = False,
-        filter: _TarfileFilter | None = ...,
+        filter: _TarfileFilter | None = None,
     ) -> None: ...
     def _extract_member(
         self,
@@ -744,6 +744,28 @@ def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
 def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
 
 class TarInfo:
+    __slots__ = (
+        "name",
+        "mode",
+        "uid",
+        "gid",
+        "size",
+        "mtime",
+        "chksum",
+        "type",
+        "linkname",
+        "uname",
+        "gname",
+        "devmajor",
+        "devminor",
+        "offset",
+        "offset_data",
+        "pax_headers",
+        "sparse",
+        "_tarfile",
+        "_sparse_structs",
+        "_link_target",
+    )
     name: str
     path: str
     size: int
@@ -765,10 +787,10 @@ class TarInfo:
     def __init__(self, name: str = "") -> None: ...
     if sys.version_info >= (3, 13):
         @property
-        @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.16")
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.16.")
         def tarfile(self) -> TarFile | None: ...
         @tarfile.setter
-        @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.16")
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.16.")
         def tarfile(self, tarfile: TarFile | None) -> None: ...
     else:
         tarfile: TarFile | None
diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi
index 6b599256d17b..88aa43d24899 100644
--- a/mypy/typeshed/stdlib/telnetlib.pyi
+++ b/mypy/typeshed/stdlib/telnetlib.pyi
@@ -2,89 +2,89 @@ import socket
 from collections.abc import Callable, MutableSequence, Sequence
 from re import Match, Pattern
 from types import TracebackType
-from typing import Any
+from typing import Any, Final
 from typing_extensions import Self
 
 __all__ = ["Telnet"]
 
-DEBUGLEVEL: int
-TELNET_PORT: int
+DEBUGLEVEL: Final = 0
+TELNET_PORT: Final = 23
 
-IAC: bytes
-DONT: bytes
-DO: bytes
-WONT: bytes
-WILL: bytes
-theNULL: bytes
+IAC: Final = b"\xff"
+DONT: Final = b"\xfe"
+DO: Final = b"\xfd"
+WONT: Final = b"\xfc"
+WILL: Final = b"\xfb"
+theNULL: Final = b"\x00"
 
-SE: bytes
-NOP: bytes
-DM: bytes
-BRK: bytes
-IP: bytes
-AO: bytes
-AYT: bytes
-EC: bytes
-EL: bytes
-GA: bytes
-SB: bytes
+SE: Final = b"\xf0"
+NOP: Final = b"\xf1"
+DM: Final = b"\xf2"
+BRK: Final = b"\xf3"
+IP: Final = b"\xf4"
+AO: Final = b"\xf5"
+AYT: Final = b"\xf6"
+EC: Final = b"\xf7"
+EL: Final = b"\xf8"
+GA: Final = b"\xf9"
+SB: Final = b"\xfa"
 
-BINARY: bytes
-ECHO: bytes
-RCP: bytes
-SGA: bytes
-NAMS: bytes
-STATUS: bytes
-TM: bytes
-RCTE: bytes
-NAOL: bytes
-NAOP: bytes
-NAOCRD: bytes
-NAOHTS: bytes
-NAOHTD: bytes
-NAOFFD: bytes
-NAOVTS: bytes
-NAOVTD: bytes
-NAOLFD: bytes
-XASCII: bytes
-LOGOUT: bytes
-BM: bytes
-DET: bytes
-SUPDUP: bytes
-SUPDUPOUTPUT: bytes
-SNDLOC: bytes
-TTYPE: bytes
-EOR: bytes
-TUID: bytes
-OUTMRK: bytes
-TTYLOC: bytes
-VT3270REGIME: bytes
-X3PAD: bytes
-NAWS: bytes
-TSPEED: bytes
-LFLOW: bytes
-LINEMODE: bytes
-XDISPLOC: bytes
-OLD_ENVIRON: bytes
-AUTHENTICATION: bytes
-ENCRYPT: bytes
-NEW_ENVIRON: bytes
+BINARY: Final = b"\x00"
+ECHO: Final = b"\x01"
+RCP: Final = b"\x02"
+SGA: Final = b"\x03"
+NAMS: Final = b"\x04"
+STATUS: Final = b"\x05"
+TM: Final = b"\x06"
+RCTE: Final = b"\x07"
+NAOL: Final = b"\x08"
+NAOP: Final = b"\t"
+NAOCRD: Final = b"\n"
+NAOHTS: Final = b"\x0b"
+NAOHTD: Final = b"\x0c"
+NAOFFD: Final = b"\r"
+NAOVTS: Final = b"\x0e"
+NAOVTD: Final = b"\x0f"
+NAOLFD: Final = b"\x10"
+XASCII: Final = b"\x11"
+LOGOUT: Final = b"\x12"
+BM: Final = b"\x13"
+DET: Final = b"\x14"
+SUPDUP: Final = b"\x15"
+SUPDUPOUTPUT: Final = b"\x16"
+SNDLOC: Final = b"\x17"
+TTYPE: Final = b"\x18"
+EOR: Final = b"\x19"
+TUID: Final = b"\x1a"
+OUTMRK: Final = b"\x1b"
+TTYLOC: Final = b"\x1c"
+VT3270REGIME: Final = b"\x1d"
+X3PAD: Final = b"\x1e"
+NAWS: Final = b"\x1f"
+TSPEED: Final = b" "
+LFLOW: Final = b"!"
+LINEMODE: Final = b'"'
+XDISPLOC: Final = b"#"
+OLD_ENVIRON: Final = b"$"
+AUTHENTICATION: Final = b"%"
+ENCRYPT: Final = b"&"
+NEW_ENVIRON: Final = b"'"
 
-TN3270E: bytes
-XAUTH: bytes
-CHARSET: bytes
-RSP: bytes
-COM_PORT_OPTION: bytes
-SUPPRESS_LOCAL_ECHO: bytes
-TLS: bytes
-KERMIT: bytes
-SEND_URL: bytes
-FORWARD_X: bytes
-PRAGMA_LOGON: bytes
-SSPI_LOGON: bytes
-PRAGMA_HEARTBEAT: bytes
-EXOPL: bytes
-NOOPT: bytes
+TN3270E: Final = b"("
+XAUTH: Final = b")"
+CHARSET: Final = b"*"
+RSP: Final = b"+"
+COM_PORT_OPTION: Final = b","
+SUPPRESS_LOCAL_ECHO: Final = b"-"
+TLS: Final = b"."
+KERMIT: Final = b"/"
+SEND_URL: Final = b"0"
+FORWARD_X: Final = b"1"
+PRAGMA_LOGON: Final = b"\x8a"
+SSPI_LOGON: Final = b"\x8b"
+PRAGMA_HEARTBEAT: Final = b"\x8c"
+EXOPL: Final = b"\xff"
+NOOPT: Final = b"\x00"
 
 class Telnet:
     host: str | None  # undocumented
diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi
index 6b2abe4398d2..26491074ff71 100644
--- a/mypy/typeshed/stdlib/tempfile.pyi
+++ b/mypy/typeshed/stdlib/tempfile.pyi
@@ -14,7 +14,7 @@ from _typeshed import (
 )
 from collections.abc import Iterable, Iterator
 from types import GenericAlias, TracebackType
-from typing import IO, Any, AnyStr, Generic, Literal, overload
+from typing import IO, Any, AnyStr, Final, Generic, Literal, overload
 from typing_extensions import Self, deprecated
 
 __all__ = [
@@ -34,7 +34,7 @@ __all__ = [
 ]
 
 # global variables
-TMP_MAX: int
+TMP_MAX: Final[int]
 tempdir: str | None
 template: str
 
diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi
index 033cad3931f5..28fa5267a997 100644
--- a/mypy/typeshed/stdlib/threading.pyi
+++ b/mypy/typeshed/stdlib/threading.pyi
@@ -5,7 +5,7 @@ from _typeshed import ProfileFunction, TraceFunction
 from collections.abc import Callable, Iterable, Mapping
 from contextvars import ContextVar
 from types import TracebackType
-from typing import Any, TypeVar, final
+from typing import Any, Final, TypeVar, final
 from typing_extensions import deprecated
 
 _T = TypeVar("_T")
@@ -46,10 +46,10 @@ if sys.version_info >= (3, 12):
 _profile_hook: ProfileFunction | None
 
 def active_count() -> int: ...
-@deprecated("Use active_count() instead")
+@deprecated("Deprecated since Python 3.10. Use `active_count()` instead.")
 def activeCount() -> int: ...
 def current_thread() -> Thread: ...
-@deprecated("Use current_thread() instead")
+@deprecated("Deprecated since Python 3.10. Use `current_thread()` instead.")
 def currentThread() -> Thread: ...
 def get_ident() -> int: ...
 def enumerate() -> list[Thread]: ...
@@ -67,7 +67,7 @@ if sys.version_info >= (3, 10):
 
 def stack_size(size: int = 0, /) -> int: ...
 
-TIMEOUT_MAX: float
+TIMEOUT_MAX: Final[float]
 
 ThreadError = _thread.error
 local = _thread._local
@@ -107,13 +107,13 @@ class Thread:
     @property
     def native_id(self) -> int | None: ...  # only available on some platforms
     def is_alive(self) -> bool: ...
-    @deprecated("Get the daemon attribute instead")
+    @deprecated("Deprecated since Python 3.10. Read the `daemon` attribute instead.")
     def isDaemon(self) -> bool: ...
-    @deprecated("Set the daemon attribute instead")
+    @deprecated("Deprecated since Python 3.10. Set the `daemon` attribute instead.")
     def setDaemon(self, daemonic: bool) -> None: ...
-    @deprecated("Use the name attribute instead")
+    @deprecated("Deprecated since Python 3.10. Read the `name` attribute instead.")
     def getName(self) -> str: ...
-    @deprecated("Use the name attribute instead")
+    @deprecated("Deprecated since Python 3.10. Set the `name` attribute instead.")
     def setName(self, name: str) -> None: ...
 
 class _DummyThread(Thread):
@@ -148,7 +148,7 @@ class Condition:
     def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ...
     def notify(self, n: int = 1) -> None: ...
     def notify_all(self) -> None: ...
-    @deprecated("Use notify_all() instead")
+    @deprecated("Deprecated since Python 3.10. Use `notify_all()` instead.")
     def notifyAll(self) -> None: ...
 
 class Semaphore:
@@ -163,7 +163,7 @@ class BoundedSemaphore(Semaphore): ...
 
 class Event:
     def is_set(self) -> bool: ...
-    @deprecated("Use is_set() instead")
+    @deprecated("Deprecated since Python 3.10. Use `is_set()` instead.")
     def isSet(self) -> bool: ...
     def set(self) -> None: ...
     def clear(self) -> None: ...
diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi
index a921722b62c5..5665efbba69d 100644
--- a/mypy/typeshed/stdlib/time.pyi
+++ b/mypy/typeshed/stdlib/time.pyi
@@ -11,28 +11,28 @@ timezone: int
 tzname: tuple[str, str]
 
 if sys.platform == "linux":
-    CLOCK_BOOTTIME: int
+    CLOCK_BOOTTIME: Final[int]
 if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin":
-    CLOCK_PROF: int  # FreeBSD, NetBSD, OpenBSD
-    CLOCK_UPTIME: int  # FreeBSD, OpenBSD
+    CLOCK_PROF: Final[int]  # FreeBSD, NetBSD, OpenBSD
+    CLOCK_UPTIME: Final[int]  # FreeBSD, OpenBSD
 
 if sys.platform != "win32":
-    CLOCK_MONOTONIC: int
-    CLOCK_MONOTONIC_RAW: int
-    CLOCK_PROCESS_CPUTIME_ID: int
-    CLOCK_REALTIME: int
-    CLOCK_THREAD_CPUTIME_ID: int
+    CLOCK_MONOTONIC: Final[int]
+    CLOCK_MONOTONIC_RAW: Final[int]
+    CLOCK_PROCESS_CPUTIME_ID: Final[int]
+    CLOCK_REALTIME: Final[int]
+    CLOCK_THREAD_CPUTIME_ID: Final[int]
     if sys.platform != "linux" and sys.platform != "darwin":
-        CLOCK_HIGHRES: int  # Solaris only
+        CLOCK_HIGHRES: Final[int]  # Solaris only
 
 if sys.platform == "darwin":
-    CLOCK_UPTIME_RAW: int
+    CLOCK_UPTIME_RAW: Final[int]
     if sys.version_info >= (3, 13):
-        CLOCK_UPTIME_RAW_APPROX: int
-        CLOCK_MONOTONIC_RAW_APPROX: int
+        CLOCK_UPTIME_RAW_APPROX: Final[int]
+        CLOCK_MONOTONIC_RAW_APPROX: Final[int]
 
 if sys.platform == "linux":
-    CLOCK_TAI: int
+    CLOCK_TAI: Final[int]
 
 # Constructor takes an iterable of any type, of length between 9 and 11 elements.
 # However, it always *behaves* like a tuple of 9 elements,
diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi
index 76b2ddcf17df..54dd70baf199 100644
--- a/mypy/typeshed/stdlib/tkinter/__init__.pyi
+++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi
@@ -5,8 +5,8 @@ from collections.abc import Callable, Iterable, Mapping, Sequence
 from tkinter.constants import *
 from tkinter.font import _FontDescription
 from types import GenericAlias, TracebackType
-from typing import Any, ClassVar, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only
-from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated
+from typing import Any, ClassVar, Final, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only
+from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated, disjoint_base
 
 if sys.version_info >= (3, 11):
     from enum import StrEnum
@@ -153,11 +153,11 @@ __all__ = [
 
 TclError = _tkinter.TclError
 wantobjects: int
-TkVersion: float
-TclVersion: float
-READABLE = _tkinter.READABLE
-WRITABLE = _tkinter.WRITABLE
-EXCEPTION = _tkinter.EXCEPTION
+TkVersion: Final[float]
+TclVersion: Final[float]
+READABLE: Final = _tkinter.READABLE
+WRITABLE: Final = _tkinter.WRITABLE
+EXCEPTION: Final = _tkinter.EXCEPTION
 
 # Quick guide for figuring out which widget class to choose:
 #   - Misc: any widget (don't use BaseWidget because Tk doesn't inherit from BaseWidget)
@@ -198,7 +198,11 @@ if sys.version_info >= (3, 11):
         releaselevel: str
         serial: int
 
-    class _VersionInfoType(_VersionInfoTypeBase): ...
+    if sys.version_info >= (3, 12):
+        class _VersionInfoType(_VersionInfoTypeBase): ...
+    else:
+        @disjoint_base
+        class _VersionInfoType(_VersionInfoTypeBase): ...
 
 if sys.version_info >= (3, 11):
     class EventType(StrEnum):
@@ -321,14 +325,21 @@ class Variable:
     def trace_add(self, mode: Literal["array", "read", "write", "unset"], callback: Callable[[str, str, str], object]) -> str: ...
     def trace_remove(self, mode: Literal["array", "read", "write", "unset"], cbname: str) -> None: ...
     def trace_info(self) -> list[tuple[tuple[Literal["array", "read", "write", "unset"], ...], str]]: ...
-    @deprecated("use trace_add() instead of trace()")
-    def trace(self, mode, callback): ...
-    @deprecated("use trace_add() instead of trace_variable()")
-    def trace_variable(self, mode, callback): ...
-    @deprecated("use trace_remove() instead of trace_vdelete()")
-    def trace_vdelete(self, mode, cbname) -> None: ...
-    @deprecated("use trace_info() instead of trace_vinfo()")
-    def trace_vinfo(self): ...
+    if sys.version_info >= (3, 14):
+        @deprecated("Deprecated since Python 3.14. Use `trace_add()` instead.")
+        def trace(self, mode, callback) -> str: ...
+        @deprecated("Deprecated since Python 3.14. Use `trace_add()` instead.")
+        def trace_variable(self, mode, callback) -> str: ...
+        @deprecated("Deprecated since Python 3.14. Use `trace_remove()` instead.")
+        def trace_vdelete(self, mode, cbname) -> None: ...
+        @deprecated("Deprecated since Python 3.14. Use `trace_info()` instead.")
+        def trace_vinfo(self): ...
+    else:
+        def trace(self, mode, callback) -> str: ...
+        def trace_variable(self, mode, callback) -> str: ...
+        def trace_vdelete(self, mode, cbname) -> None: ...
+        def trace_vinfo(self): ...
+
     def __eq__(self, other: object) -> bool: ...
     def __del__(self) -> None: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
@@ -359,8 +370,8 @@ class BooleanVar(Variable):
 
 def mainloop(n: int = 0) -> None: ...
 
-getint: Incomplete
-getdouble: Incomplete
+getint = int
+getdouble = float
 
 def getboolean(s): ...
 
@@ -3468,7 +3479,7 @@ class Text(Widget, XView, YView):
     def image_configure(
         self,
         index: _TextIndex,
-        cnf: dict[str, Any] | None = {},
+        cnf: dict[str, Any] | None = None,
         *,
         align: Literal["baseline", "bottom", "center", "top"] = ...,
         image: _ImageSpec = ...,
diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
index 8e5a88f92ea1..cd95f0de5f80 100644
--- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi
+++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi
@@ -30,7 +30,7 @@ def showinfo(
     *,
     detail: str = ...,
     icon: Literal["error", "info", "question", "warning"] = ...,
-    default: Literal["ok"] = ...,
+    default: Literal["ok"] = "ok",
     parent: Misc = ...,
 ) -> str: ...
 def showwarning(
@@ -39,7 +39,7 @@ def showwarning(
     *,
     detail: str = ...,
     icon: Literal["error", "info", "question", "warning"] = ...,
-    default: Literal["ok"] = ...,
+    default: Literal["ok"] = "ok",
     parent: Misc = ...,
 ) -> str: ...
 def showerror(
@@ -48,7 +48,7 @@ def showerror(
     *,
     detail: str = ...,
     icon: Literal["error", "info", "question", "warning"] = ...,
-    default: Literal["ok"] = ...,
+    default: Literal["ok"] = "ok",
     parent: Misc = ...,
 ) -> str: ...
 def askquestion(
diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi
index c46239df81eb..86c55eba7006 100644
--- a/mypy/typeshed/stdlib/tkinter/ttk.pyi
+++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi
@@ -1,10 +1,11 @@
 import _tkinter
+import sys
 import tkinter
-from _typeshed import Incomplete, MaybeNone
-from collections.abc import Callable
+from _typeshed import MaybeNone
+from collections.abc import Callable, Iterable
 from tkinter.font import _FontDescription
 from typing import Any, Literal, TypedDict, overload, type_check_only
-from typing_extensions import TypeAlias
+from typing_extensions import Never, TypeAlias, Unpack
 
 __all__ = [
     "Button",
@@ -35,7 +36,7 @@ __all__ = [
 ]
 
 def tclobjs_to_py(adict: dict[Any, Any]) -> dict[Any, Any]: ...
-def setup_master(master=None): ...
+def setup_master(master: tkinter.Misc | None = None): ...
 
 _Padding: TypeAlias = (
     tkinter._ScreenUnits
@@ -48,19 +49,153 @@ _Padding: TypeAlias = (
 # from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound
 _TtkCompound: TypeAlias = Literal["", "text", "image", tkinter._Compound]
 
+# Last item (option value to apply) varies between different options so use Any.
+# It could also be any iterable with items matching the tuple, but that case
+# hasn't been added here for consistency with _Padding above.
+_Statespec: TypeAlias = tuple[Unpack[tuple[str, ...]], Any]
+_ImageStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], tkinter._ImageSpec]
+_VsapiStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], int]
+
+class _Layout(TypedDict, total=False):
+    side: Literal["left", "right", "top", "bottom"]
+    sticky: str  # consists of letters 'n', 's', 'w', 'e', may contain repeats, may be empty
+    unit: Literal[0, 1] | bool
+    children: _LayoutSpec
+    # Note: there seem to be some other undocumented keys sometimes
+
+# This could be any sequence when passed as a parameter but will always be a list when returned.
+_LayoutSpec: TypeAlias = list[tuple[str, _Layout | None]]
+
+# Keep these in sync with the appropriate methods in Style
+class _ElementCreateImageKwargs(TypedDict, total=False):
+    border: _Padding
+    height: tkinter._ScreenUnits
+    padding: _Padding
+    sticky: str
+    width: tkinter._ScreenUnits
+
+_ElementCreateArgsCrossPlatform: TypeAlias = (
+    # Could be any sequence here but types are not homogenous so just type it as tuple
+    tuple[Literal["image"], tkinter._ImageSpec, Unpack[tuple[_ImageStatespec, ...]], _ElementCreateImageKwargs]
+    | tuple[Literal["from"], str, str]
+    | tuple[Literal["from"], str]  # (fromelement is optional)
+)
+if sys.platform == "win32" and sys.version_info >= (3, 13):
+    class _ElementCreateVsapiKwargsPadding(TypedDict, total=False):
+        padding: _Padding
+
+    class _ElementCreateVsapiKwargsMargin(TypedDict, total=False):
+        padding: _Padding
+
+    class _ElementCreateVsapiKwargsSize(TypedDict):
+        width: tkinter._ScreenUnits
+        height: tkinter._ScreenUnits
+
+    _ElementCreateVsapiKwargsDict: TypeAlias = (
+        _ElementCreateVsapiKwargsPadding | _ElementCreateVsapiKwargsMargin | _ElementCreateVsapiKwargsSize
+    )
+    _ElementCreateArgs: TypeAlias = (  # noqa: Y047  # It doesn't recognise the usage below for whatever reason
+        _ElementCreateArgsCrossPlatform
+        | tuple[Literal["vsapi"], str, int, _ElementCreateVsapiKwargsDict]
+        | tuple[Literal["vsapi"], str, int, _VsapiStatespec, _ElementCreateVsapiKwargsDict]
+    )
+else:
+    _ElementCreateArgs: TypeAlias = _ElementCreateArgsCrossPlatform
+_ThemeSettingsValue = TypedDict(
+    "_ThemeSettingsValue",
+    {
+        "configure": dict[str, Any],
+        "map": dict[str, Iterable[_Statespec]],
+        "layout": _LayoutSpec,
+        "element create": _ElementCreateArgs,
+    },
+    total=False,
+)
+_ThemeSettings: TypeAlias = dict[str, _ThemeSettingsValue]
+
 class Style:
-    master: Incomplete
+    master: tkinter.Misc
     tk: _tkinter.TkappType
     def __init__(self, master: tkinter.Misc | None = None) -> None: ...
-    def configure(self, style, query_opt=None, **kw): ...
-    def map(self, style, query_opt=None, **kw): ...
-    def lookup(self, style, option, state=None, default=None): ...
-    def layout(self, style, layoutspec=None): ...
-    def element_create(self, elementname, etype, *args, **kw) -> None: ...
-    def element_names(self): ...
-    def element_options(self, elementname): ...
-    def theme_create(self, themename, parent=None, settings=None) -> None: ...
-    def theme_settings(self, themename, settings) -> None: ...
+    # For these methods, values given vary between options. Returned values
+    # seem to be str, but this might not always be the case.
+    @overload
+    def configure(self, style: str) -> dict[str, Any] | None: ...  # Returns None if no configuration.
+    @overload
+    def configure(self, style: str, query_opt: str, **kw: Any) -> Any: ...
+    @overload
+    def configure(self, style: str, query_opt: None = None, **kw: Any) -> None: ...
+    @overload
+    def map(self, style: str, query_opt: str) -> _Statespec: ...
+    @overload
+    def map(self, style: str, query_opt: None = None, **kw: Iterable[_Statespec]) -> dict[str, _Statespec]: ...
+    def lookup(self, style: str, option: str, state: Iterable[str] | None = None, default: Any | None = None) -> Any: ...
+    @overload
+    def layout(self, style: str, layoutspec: _LayoutSpec) -> list[Never]: ...  # Always seems to return an empty list
+    @overload
+    def layout(self, style: str, layoutspec: None = None) -> _LayoutSpec: ...
+    @overload
+    def element_create(
+        self,
+        elementname: str,
+        etype: Literal["image"],
+        default_image: tkinter._ImageSpec,
+        /,
+        *imagespec: _ImageStatespec,
+        border: _Padding = ...,
+        height: tkinter._ScreenUnits = ...,
+        padding: _Padding = ...,
+        sticky: str = ...,
+        width: tkinter._ScreenUnits = ...,
+    ) -> None: ...
+    @overload
+    def element_create(self, elementname: str, etype: Literal["from"], themename: str, fromelement: str = ..., /) -> None: ...
+    if sys.platform == "win32" and sys.version_info >= (3, 13):  # and tk version >= 8.6
+        # margin, padding, and (width + height) are mutually exclusive. width
+        # and height must either both be present or not present at all. Note:
+        # There are other undocumented options if you look at ttk's source code.
+        @overload
+        def element_create(
+            self,
+            elementname: str,
+            etype: Literal["vsapi"],
+            class_: str,
+            part: int,
+            vs_statespec: _VsapiStatespec = ...,
+            /,
+            *,
+            padding: _Padding = ...,
+        ) -> None: ...
+        @overload
+        def element_create(
+            self,
+            elementname: str,
+            etype: Literal["vsapi"],
+            class_: str,
+            part: int,
+            vs_statespec: _VsapiStatespec = ...,
+            /,
+            *,
+            margin: _Padding = ...,
+        ) -> None: ...
+        @overload
+        def element_create(
+            self,
+            elementname: str,
+            etype: Literal["vsapi"],
+            class_: str,
+            part: int,
+            vs_statespec: _VsapiStatespec = ...,
+            /,
+            *,
+            width: tkinter._ScreenUnits,
+            height: tkinter._ScreenUnits,
+        ) -> None: ...
+
+    def element_names(self) -> tuple[str, ...]: ...
+    def element_options(self, elementname: str) -> tuple[str, ...]: ...
+    def theme_create(self, themename: str, parent: str | None = None, settings: _ThemeSettings | None = None) -> None: ...
+    def theme_settings(self, themename: str, settings: _ThemeSettings) -> None: ...
     def theme_names(self) -> tuple[str, ...]: ...
     @overload
     def theme_use(self, themename: str) -> None: ...
@@ -615,7 +750,7 @@ class Panedwindow(Widget, tkinter.PanedWindow):
     ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ...
     @overload
     def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ...
-    forget: Incomplete
+    forget = tkinter.PanedWindow.forget
     def insert(self, pos, child, **kw) -> None: ...
     def pane(self, pane, option=None, **kw): ...
     def sashpos(self, index, newpos=None): ...
diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi
index 1a3a80937f22..00a24b4eea07 100644
--- a/mypy/typeshed/stdlib/tokenize.pyi
+++ b/mypy/typeshed/stdlib/tokenize.pyi
@@ -3,8 +3,8 @@ from _typeshed import FileDescriptorOrPath
 from collections.abc import Callable, Generator, Iterable, Sequence
 from re import Pattern
 from token import *
-from typing import Any, NamedTuple, TextIO, type_check_only
-from typing_extensions import TypeAlias
+from typing import Any, Final, NamedTuple, TextIO, type_check_only
+from typing_extensions import TypeAlias, disjoint_base
 
 if sys.version_info < (3, 12):
     # Avoid double assignment to Final name by imports, which pyright objects to.
@@ -101,8 +101,8 @@ if sys.version_info >= (3, 13):
 if sys.version_info >= (3, 14):
     __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"]
 
-cookie_re: Pattern[str]
-blank_re: Pattern[bytes]
+cookie_re: Final[Pattern[str]]
+blank_re: Final[Pattern[bytes]]
 
 _Position: TypeAlias = tuple[int, int]
 
@@ -115,9 +115,16 @@ class _TokenInfo(NamedTuple):
     end: _Position
     line: str
 
-class TokenInfo(_TokenInfo):
-    @property
-    def exact_type(self) -> int: ...
+if sys.version_info >= (3, 12):
+    class TokenInfo(_TokenInfo):
+        @property
+        def exact_type(self) -> int: ...
+
+else:
+    @disjoint_base
+    class TokenInfo(_TokenInfo):
+        @property
+        def exact_type(self) -> int: ...
 
 # Backwards compatible tokens can be sequences of a shorter length too
 _Token: TypeAlias = TokenInfo | Sequence[int | str | _Position]
@@ -151,46 +158,46 @@ def group(*choices: str) -> str: ...  # undocumented
 def any(*choices: str) -> str: ...  # undocumented
 def maybe(*choices: str) -> str: ...  # undocumented
 
-Whitespace: str  # undocumented
-Comment: str  # undocumented
-Ignore: str  # undocumented
-Name: str  # undocumented
-
-Hexnumber: str  # undocumented
-Binnumber: str  # undocumented
-Octnumber: str  # undocumented
-Decnumber: str  # undocumented
-Intnumber: str  # undocumented
-Exponent: str  # undocumented
-Pointfloat: str  # undocumented
-Expfloat: str  # undocumented
-Floatnumber: str  # undocumented
-Imagnumber: str  # undocumented
-Number: str  # undocumented
+Whitespace: Final[str]  # undocumented
+Comment: Final[str]  # undocumented
+Ignore: Final[str]  # undocumented
+Name: Final[str]  # undocumented
+
+Hexnumber: Final[str]  # undocumented
+Binnumber: Final[str]  # undocumented
+Octnumber: Final[str]  # undocumented
+Decnumber: Final[str]  # undocumented
+Intnumber: Final[str]  # undocumented
+Exponent: Final[str]  # undocumented
+Pointfloat: Final[str]  # undocumented
+Expfloat: Final[str]  # undocumented
+Floatnumber: Final[str]  # undocumented
+Imagnumber: Final[str]  # undocumented
+Number: Final[str]  # undocumented
 
 def _all_string_prefixes() -> set[str]: ...  # undocumented
 
-StringPrefix: str  # undocumented
+StringPrefix: Final[str]  # undocumented
 
-Single: str  # undocumented
-Double: str  # undocumented
-Single3: str  # undocumented
-Double3: str  # undocumented
-Triple: str  # undocumented
-String: str  # undocumented
+Single: Final[str]  # undocumented
+Double: Final[str]  # undocumented
+Single3: Final[str]  # undocumented
+Double3: Final[str]  # undocumented
+Triple: Final[str]  # undocumented
+String: Final[str]  # undocumented
 
-Special: str  # undocumented
-Funny: str  # undocumented
+Special: Final[str]  # undocumented
+Funny: Final[str]  # undocumented
 
-PlainToken: str  # undocumented
-Token: str  # undocumented
+PlainToken: Final[str]  # undocumented
+Token: Final[str]  # undocumented
 
-ContStr: str  # undocumented
-PseudoExtras: str  # undocumented
-PseudoToken: str  # undocumented
+ContStr: Final[str]  # undocumented
+PseudoExtras: Final[str]  # undocumented
+PseudoToken: Final[str]  # undocumented
 
-endpats: dict[str, str]  # undocumented
-single_quoted: set[str]  # undocumented
-triple_quoted: set[str]  # undocumented
+endpats: Final[dict[str, str]]  # undocumented
+single_quoted: Final[set[str]]  # undocumented
+triple_quoted: Final[set[str]]  # undocumented
 
-tabsize: int  # undocumented
+tabsize: Final = 8  # undocumented
diff --git a/mypy/typeshed/stdlib/tomllib.pyi b/mypy/typeshed/stdlib/tomllib.pyi
index c160ffc38bfd..4ff4097f8313 100644
--- a/mypy/typeshed/stdlib/tomllib.pyi
+++ b/mypy/typeshed/stdlib/tomllib.pyi
@@ -16,7 +16,7 @@ if sys.version_info >= (3, 14):
         @overload
         def __init__(self, msg: str, doc: str, pos: int) -> None: ...
         @overload
-        @deprecated("Deprecated in Python 3.14; Please set 'msg', 'doc' and 'pos' arguments only.")
+        @deprecated("Deprecated since Python 3.14. Set the 'msg', 'doc' and 'pos' arguments only.")
         def __init__(self, msg: str | type = ..., doc: str | type = ..., pos: int | type = ..., *args: Any) -> None: ...
 
 else:
diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi
index 4553dbd08384..d587295cd1cf 100644
--- a/mypy/typeshed/stdlib/traceback.pyi
+++ b/mypy/typeshed/stdlib/traceback.pyi
@@ -138,7 +138,7 @@ class TracebackException:
         @property
         def exc_type_str(self) -> str: ...
         @property
-        @deprecated("Deprecated in 3.13. Use exc_type_str instead.")
+        @deprecated("Deprecated since Python 3.13. Use `exc_type_str` instead.")
         def exc_type(self) -> type[BaseException] | None: ...
     else:
         exc_type: type[BaseException]
@@ -245,6 +245,23 @@ class TracebackException:
         def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ...
 
 class FrameSummary:
+    if sys.version_info >= (3, 13):
+        __slots__ = (
+            "filename",
+            "lineno",
+            "end_lineno",
+            "colno",
+            "end_colno",
+            "name",
+            "_lines",
+            "_lines_dedented",
+            "locals",
+            "_code",
+        )
+    elif sys.version_info >= (3, 11):
+        __slots__ = ("filename", "lineno", "end_lineno", "colno", "end_colno", "name", "_line", "locals")
+    else:
+        __slots__ = ("filename", "lineno", "name", "_line", "locals")
     if sys.version_info >= (3, 11):
         def __init__(
             self,
diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi
index 05d98ae127d8..31d8f7445639 100644
--- a/mypy/typeshed/stdlib/tracemalloc.pyi
+++ b/mypy/typeshed/stdlib/tracemalloc.pyi
@@ -32,6 +32,7 @@ class Filter(BaseFilter):
     ) -> None: ...
 
 class Statistic:
+    __slots__ = ("traceback", "size", "count")
     count: int
     size: int
     traceback: Traceback
@@ -40,6 +41,7 @@ class Statistic:
     def __hash__(self) -> int: ...
 
 class StatisticDiff:
+    __slots__ = ("traceback", "size", "size_diff", "count", "count_diff")
     count: int
     count_diff: int
     size: int
@@ -52,6 +54,7 @@ class StatisticDiff:
 _FrameTuple: TypeAlias = tuple[str, int]
 
 class Frame:
+    __slots__ = ("_frame",)
     @property
     def filename(self) -> str: ...
     @property
@@ -72,6 +75,7 @@ class Frame:
 _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]]
 
 class Trace:
+    __slots__ = ("_trace",)
     @property
     def domain(self) -> int: ...
     @property
@@ -83,6 +87,7 @@ class Trace:
     def __hash__(self) -> int: ...
 
 class Traceback(Sequence[Frame]):
+    __slots__ = ("_frames", "_total_nframe")
     @property
     def total_nframe(self) -> int | None: ...
     def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi
index e41476b73b8c..0b93429904c5 100644
--- a/mypy/typeshed/stdlib/turtle.pyi
+++ b/mypy/typeshed/stdlib/turtle.pyi
@@ -4,7 +4,7 @@ from collections.abc import Callable, Generator, Sequence
 from contextlib import contextmanager
 from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar
 from typing import Any, ClassVar, Literal, TypedDict, overload, type_check_only
-from typing_extensions import Self, TypeAlias, deprecated
+from typing_extensions import Self, TypeAlias, deprecated, disjoint_base
 
 __all__ = [
     "ScrolledCanvas",
@@ -163,18 +163,34 @@ class _PenState(TypedDict):
 _Speed: TypeAlias = str | float
 _PolygonCoords: TypeAlias = Sequence[tuple[float, float]]
 
-class Vec2D(tuple[float, float]):
-    def __new__(cls, x: float, y: float) -> Self: ...
-    def __add__(self, other: tuple[float, float]) -> Vec2D: ...  # type: ignore[override]
-    @overload  # type: ignore[override]
-    def __mul__(self, other: Vec2D) -> float: ...
-    @overload
-    def __mul__(self, other: float) -> Vec2D: ...
-    def __rmul__(self, other: float) -> Vec2D: ...  # type: ignore[override]
-    def __sub__(self, other: tuple[float, float]) -> Vec2D: ...
-    def __neg__(self) -> Vec2D: ...
-    def __abs__(self) -> float: ...
-    def rotate(self, angle: float) -> Vec2D: ...
+if sys.version_info >= (3, 12):
+    class Vec2D(tuple[float, float]):
+        def __new__(cls, x: float, y: float) -> Self: ...
+        def __add__(self, other: tuple[float, float]) -> Vec2D: ...  # type: ignore[override]
+        @overload  # type: ignore[override]
+        def __mul__(self, other: Vec2D) -> float: ...
+        @overload
+        def __mul__(self, other: float) -> Vec2D: ...
+        def __rmul__(self, other: float) -> Vec2D: ...  # type: ignore[override]
+        def __sub__(self, other: tuple[float, float]) -> Vec2D: ...
+        def __neg__(self) -> Vec2D: ...
+        def __abs__(self) -> float: ...
+        def rotate(self, angle: float) -> Vec2D: ...
+
+else:
+    @disjoint_base
+    class Vec2D(tuple[float, float]):
+        def __new__(cls, x: float, y: float) -> Self: ...
+        def __add__(self, other: tuple[float, float]) -> Vec2D: ...  # type: ignore[override]
+        @overload  # type: ignore[override]
+        def __mul__(self, other: Vec2D) -> float: ...
+        @overload
+        def __mul__(self, other: float) -> Vec2D: ...
+        def __rmul__(self, other: float) -> Vec2D: ...  # type: ignore[override]
+        def __sub__(self, other: tuple[float, float]) -> Vec2D: ...
+        def __neg__(self) -> Vec2D: ...
+        def __abs__(self) -> float: ...
+        def rotate(self, angle: float) -> Vec2D: ...
 
 # Does not actually inherit from Canvas, but dynamically gets all methods of Canvas
 class ScrolledCanvas(Canvas, Frame):  # type: ignore[misc]
diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi
index 44bd3eeb3f53..591d5da2360d 100644
--- a/mypy/typeshed/stdlib/types.pyi
+++ b/mypy/typeshed/stdlib/types.pyi
@@ -17,7 +17,7 @@ from collections.abc import (
 )
 from importlib.machinery import ModuleSpec
 from typing import Any, ClassVar, Literal, TypeVar, final, overload
-from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated
+from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated, disjoint_base
 
 if sys.version_info >= (3, 14):
     from _typeshed import AnnotateFunc
@@ -151,7 +151,7 @@ class CodeType:
     def co_firstlineno(self) -> int: ...
     if sys.version_info >= (3, 10):
         @property
-        @deprecated("Will be removed in Python 3.15. Use the co_lines() method instead.")
+        @deprecated("Deprecated since Python 3.10; will be removed in Python 3.15. Use `CodeType.co_lines()` instead.")
         def co_lnotab(self) -> bytes: ...
     else:
         @property
@@ -331,20 +331,34 @@ class MappingProxyType(Mapping[_KT, _VT_co]):
     def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
     def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ...
 
-class SimpleNamespace:
-    __hash__: ClassVar[None]  # type: ignore[assignment]
-    if sys.version_info >= (3, 13):
-        def __init__(self, mapping_or_iterable: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), /, **kwargs: Any) -> None: ...
-    else:
-        def __init__(self, **kwargs: Any) -> None: ...
+if sys.version_info >= (3, 12):
+    @disjoint_base
+    class SimpleNamespace:
+        __hash__: ClassVar[None]  # type: ignore[assignment]
+        if sys.version_info >= (3, 13):
+            def __init__(
+                self, mapping_or_iterable: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), /, **kwargs: Any
+            ) -> None: ...
+        else:
+            def __init__(self, **kwargs: Any) -> None: ...
 
-    def __eq__(self, value: object, /) -> bool: ...
-    def __getattribute__(self, name: str, /) -> Any: ...
-    def __setattr__(self, name: str, value: Any, /) -> None: ...
-    def __delattr__(self, name: str, /) -> None: ...
-    if sys.version_info >= (3, 13):
-        def __replace__(self, **kwargs: Any) -> Self: ...
+        def __eq__(self, value: object, /) -> bool: ...
+        def __getattribute__(self, name: str, /) -> Any: ...
+        def __setattr__(self, name: str, value: Any, /) -> None: ...
+        def __delattr__(self, name: str, /) -> None: ...
+        if sys.version_info >= (3, 13):
+            def __replace__(self, **kwargs: Any) -> Self: ...
+
+else:
+    class SimpleNamespace:
+        __hash__: ClassVar[None]  # type: ignore[assignment]
+        def __init__(self, **kwargs: Any) -> None: ...
+        def __eq__(self, value: object, /) -> bool: ...
+        def __getattribute__(self, name: str, /) -> Any: ...
+        def __setattr__(self, name: str, value: Any, /) -> None: ...
+        def __delattr__(self, name: str, /) -> None: ...
 
+@disjoint_base
 class ModuleType:
     __name__: str
     __file__: str | None
@@ -661,7 +675,7 @@ _P = ParamSpec("_P")
 def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ...
 @overload
 def coroutine(func: _Fn) -> _Fn: ...
-
+@disjoint_base
 class GenericAlias:
     @property
     def __origin__(self) -> type | TypeAliasType: ...
diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi
index fd9da29addbf..15a5864613d1 100644
--- a/mypy/typeshed/stdlib/typing.pyi
+++ b/mypy/typeshed/stdlib/typing.pyi
@@ -5,7 +5,7 @@ import collections  # noqa: F401  # pyright: ignore[reportUnusedImport]
 import sys
 import typing_extensions
 from _collections_abc import dict_items, dict_keys, dict_values
-from _typeshed import IdentityFunction, ReadableBuffer, SupportsKeysAndGetItem
+from _typeshed import IdentityFunction, ReadableBuffer, SupportsGetItem, SupportsGetItemViewable, SupportsKeysAndGetItem, Viewable
 from abc import ABCMeta, abstractmethod
 from re import Match as Match, Pattern as Pattern
 from types import (
@@ -146,7 +146,9 @@ if sys.version_info >= (3, 13):
 # from _typeshed import AnnotationForm
 
 class Any: ...
-class _Final: ...
+
+class _Final:
+    __slots__ = ("__weakref__",)
 
 def final(f: _T) -> _T: ...
 @final
@@ -229,13 +231,13 @@ _promote = object()
 # N.B. Keep this definition in sync with typing_extensions._SpecialForm
 @final
 class _SpecialForm(_Final):
+    __slots__ = ("_name", "__doc__", "_getitem")
     def __getitem__(self, parameters: Any) -> object: ...
     if sys.version_info >= (3, 10):
         def __or__(self, other: Any) -> _SpecialForm: ...
         def __ror__(self, other: Any) -> _SpecialForm: ...
 
 Union: _SpecialForm
-Generic: _SpecialForm
 Protocol: _SpecialForm
 Callable: _SpecialForm
 Type: _SpecialForm
@@ -440,6 +442,20 @@ Annotated: _SpecialForm
 # Predefined type variables.
 AnyStr = TypeVar("AnyStr", str, bytes)  # noqa: Y001
 
+@type_check_only
+class _Generic:
+    if sys.version_info < (3, 12):
+        __slots__ = ()
+
+    if sys.version_info >= (3, 10):
+        @classmethod
+        def __class_getitem__(cls, args: TypeVar | ParamSpec | tuple[TypeVar | ParamSpec, ...]) -> _Final: ...
+    else:
+        @classmethod
+        def __class_getitem__(cls, args: TypeVar | tuple[TypeVar, ...]) -> _Final: ...
+
+Generic: type[_Generic]
+
 class _ProtocolMeta(ABCMeta):
     if sys.version_info >= (3, 12):
         def __init__(cls, *args: Any, **kwargs: Any) -> None: ...
@@ -449,36 +465,43 @@ class _ProtocolMeta(ABCMeta):
 def runtime_checkable(cls: _TC) -> _TC: ...
 @runtime_checkable
 class SupportsInt(Protocol, metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __int__(self) -> int: ...
 
 @runtime_checkable
 class SupportsFloat(Protocol, metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __float__(self) -> float: ...
 
 @runtime_checkable
 class SupportsComplex(Protocol, metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __complex__(self) -> complex: ...
 
 @runtime_checkable
 class SupportsBytes(Protocol, metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __bytes__(self) -> bytes: ...
 
 @runtime_checkable
 class SupportsIndex(Protocol, metaclass=ABCMeta):
+    __slots__ = ()
     @abstractmethod
     def __index__(self) -> int: ...
 
 @runtime_checkable
 class SupportsAbs(Protocol[_T_co]):
+    __slots__ = ()
     @abstractmethod
     def __abs__(self) -> _T_co: ...
 
 @runtime_checkable
 class SupportsRound(Protocol[_T_co]):
+    __slots__ = ()
     @overload
     @abstractmethod
     def __round__(self) -> int: ...
@@ -703,11 +726,12 @@ class MutableSet(AbstractSet[_T]):
     def __isub__(self, it: AbstractSet[Any]) -> typing_extensions.Self: ...
 
 class MappingView(Sized):
-    def __init__(self, mapping: Mapping[Any, Any]) -> None: ...  # undocumented
+    __slots__ = ("_mapping",)
+    def __init__(self, mapping: Sized) -> None: ...  # undocumented
     def __len__(self) -> int: ...
 
 class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, _VT_co]):
-    def __init__(self, mapping: Mapping[_KT_co, _VT_co]) -> None: ...  # undocumented
+    def __init__(self, mapping: SupportsGetItemViewable[_KT_co, _VT_co]) -> None: ...  # undocumented
     def __and__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ...
     def __rand__(self, other: Iterable[_T]) -> set[_T]: ...
     def __contains__(self, item: tuple[object, object]) -> bool: ...  # type: ignore[override]
@@ -720,7 +744,7 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co,
     def __rxor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ...
 
 class KeysView(MappingView, AbstractSet[_KT_co]):
-    def __init__(self, mapping: Mapping[_KT_co, Any]) -> None: ...  # undocumented
+    def __init__(self, mapping: Viewable[_KT_co]) -> None: ...  # undocumented
     def __and__(self, other: Iterable[Any]) -> set[_KT_co]: ...
     def __rand__(self, other: Iterable[_T]) -> set[_T]: ...
     def __contains__(self, key: object) -> bool: ...
@@ -733,7 +757,7 @@ class KeysView(MappingView, AbstractSet[_KT_co]):
     def __rxor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ...
 
 class ValuesView(MappingView, Collection[_VT_co]):
-    def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ...  # undocumented
+    def __init__(self, mapping: SupportsGetItemViewable[Any, _VT_co]) -> None: ...  # undocumented
     def __contains__(self, value: object) -> bool: ...
     def __iter__(self) -> Iterator[_VT_co]: ...
 
@@ -801,13 +825,13 @@ class MutableMapping(Mapping[_KT, _VT]):
     @overload
     def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ...
     @overload
-    def update(self: Mapping[str, _VT], m: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ...
+    def update(self: SupportsGetItem[str, _VT], m: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ...
     @overload
     def update(self, m: Iterable[tuple[_KT, _VT]], /) -> None: ...
     @overload
-    def update(self: Mapping[str, _VT], m: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ...
+    def update(self: SupportsGetItem[str, _VT], m: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ...
     @overload
-    def update(self: Mapping[str, _VT], **kwargs: _VT) -> None: ...
+    def update(self: SupportsGetItem[str, _VT], **kwargs: _VT) -> None: ...
 
 Text = str
 
@@ -820,6 +844,7 @@ class IO(Generic[AnyStr]):
     # At runtime these are all abstract properties,
     # but making them abstract in the stub is hugely disruptive, for not much gain.
     # See #8726
+    __slots__ = ()
     @property
     def mode(self) -> str: ...
     # Usually str, but may be bytes if a bytes path was passed to open(). See #10737.
@@ -878,11 +903,13 @@ class IO(Generic[AnyStr]):
     ) -> None: ...
 
 class BinaryIO(IO[bytes]):
+    __slots__ = ()
     @abstractmethod
     def __enter__(self) -> BinaryIO: ...
 
 class TextIO(IO[str]):
     # See comment regarding the @properties in the `IO` class
+    __slots__ = ()
     @property
     def buffer(self) -> BinaryIO: ...
     @property
@@ -1045,6 +1072,15 @@ if sys.version_info >= (3, 14):
 else:
     @final
     class ForwardRef(_Final):
+        __slots__ = (
+            "__forward_arg__",
+            "__forward_code__",
+            "__forward_evaluated__",
+            "__forward_value__",
+            "__forward_is_argument__",
+            "__forward_is_class__",
+            "__forward_module__",
+        )
         __forward_arg__: str
         __forward_code__: CodeType
         __forward_evaluated__: bool
diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi
index 71bf3d87d499..f5ea13f67733 100644
--- a/mypy/typeshed/stdlib/typing_extensions.pyi
+++ b/mypy/typeshed/stdlib/typing_extensions.pyi
@@ -120,6 +120,7 @@ __all__ = [
     "clear_overloads",
     "dataclass_transform",
     "deprecated",
+    "disjoint_base",
     "Doc",
     "evaluate_forward_ref",
     "get_overloads",
@@ -150,6 +151,7 @@ __all__ = [
     "TypeGuard",
     "TypeIs",
     "TYPE_CHECKING",
+    "type_repr",
     "Never",
     "NoReturn",
     "ReadOnly",
@@ -219,6 +221,7 @@ runtime = runtime_checkable
 Final: _SpecialForm
 
 def final(f: _F) -> _F: ...
+def disjoint_base(cls: _TC) -> _TC: ...
 
 Literal: _SpecialForm
 
@@ -405,36 +408,43 @@ else:
 
     @runtime_checkable
     class SupportsInt(Protocol, metaclass=abc.ABCMeta):
+        __slots__ = ()
         @abc.abstractmethod
         def __int__(self) -> int: ...
 
     @runtime_checkable
     class SupportsFloat(Protocol, metaclass=abc.ABCMeta):
+        __slots__ = ()
         @abc.abstractmethod
         def __float__(self) -> float: ...
 
     @runtime_checkable
     class SupportsComplex(Protocol, metaclass=abc.ABCMeta):
+        __slots__ = ()
         @abc.abstractmethod
         def __complex__(self) -> complex: ...
 
     @runtime_checkable
     class SupportsBytes(Protocol, metaclass=abc.ABCMeta):
+        __slots__ = ()
         @abc.abstractmethod
         def __bytes__(self) -> bytes: ...
 
     @runtime_checkable
     class SupportsIndex(Protocol, metaclass=abc.ABCMeta):
+        __slots__ = ()
         @abc.abstractmethod
         def __index__(self) -> int: ...
 
     @runtime_checkable
     class SupportsAbs(Protocol[_T_co]):
+        __slots__ = ()
         @abc.abstractmethod
         def __abs__(self) -> _T_co: ...
 
     @runtime_checkable
     class SupportsRound(Protocol[_T_co]):
+        __slots__ = ()
         @overload
         @abc.abstractmethod
         def __round__(self) -> int: ...
@@ -447,11 +457,13 @@ if sys.version_info >= (3, 14):
 else:
     @runtime_checkable
     class Reader(Protocol[_T_co]):
+        __slots__ = ()
         @abc.abstractmethod
         def read(self, size: int = ..., /) -> _T_co: ...
 
     @runtime_checkable
     class Writer(Protocol[_T_contra]):
+        __slots__ = ()
         @abc.abstractmethod
         def write(self, data: _T_contra, /) -> int: ...
 
@@ -616,7 +628,7 @@ TypeForm: _SpecialForm
 if sys.version_info >= (3, 14):
     from typing import evaluate_forward_ref as evaluate_forward_ref
 
-    from annotationlib import Format as Format, get_annotations as get_annotations
+    from annotationlib import Format as Format, get_annotations as get_annotations, type_repr as type_repr
 else:
     class Format(enum.IntEnum):
         VALUE = 1
@@ -684,6 +696,7 @@ else:
         format: Format | None = None,
         _recursive_guard: Container[str] = ...,
     ) -> AnnotationForm: ...
+    def type_repr(value: object) -> str: ...
 
 # PEP 661
 class Sentinel:
diff --git a/mypy/typeshed/stdlib/unicodedata.pyi b/mypy/typeshed/stdlib/unicodedata.pyi
index 77d69edf06af..9fff042f0b96 100644
--- a/mypy/typeshed/stdlib/unicodedata.pyi
+++ b/mypy/typeshed/stdlib/unicodedata.pyi
@@ -1,10 +1,10 @@
 import sys
 from _typeshed import ReadOnlyBuffer
-from typing import Any, Literal, TypeVar, final, overload
+from typing import Any, Final, Literal, TypeVar, final, overload
 from typing_extensions import TypeAlias
 
 ucd_3_2_0: UCD
-unidata_version: str
+unidata_version: Final[str]
 
 if sys.version_info < (3, 10):
     ucnhash_CAPI: Any
diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi
index 598e3cd84a5e..81de40c89849 100644
--- a/mypy/typeshed/stdlib/unittest/loader.pyi
+++ b/mypy/typeshed/stdlib/unittest/loader.pyi
@@ -35,21 +35,38 @@ class TestLoader:
 defaultTestLoader: TestLoader
 
 if sys.version_info < (3, 13):
-    @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13")
-    def getTestCaseNames(
-        testCaseClass: type[unittest.case.TestCase],
-        prefix: str,
-        sortUsing: _SortComparisonMethod = ...,
-        testNamePatterns: list[str] | None = None,
-    ) -> Sequence[str]: ...
-    @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13")
-    def makeSuite(
-        testCaseClass: type[unittest.case.TestCase],
-        prefix: str = "test",
-        sortUsing: _SortComparisonMethod = ...,
-        suiteClass: _SuiteClass = ...,
-    ) -> unittest.suite.TestSuite: ...
-    @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13")
-    def findTestCases(
-        module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ...
-    ) -> unittest.suite.TestSuite: ...
+    if sys.version_info >= (3, 11):
+        @deprecated("Deprecated since Python 3.11; removed in Python 3.13.")
+        def getTestCaseNames(
+            testCaseClass: type[unittest.case.TestCase],
+            prefix: str,
+            sortUsing: _SortComparisonMethod = ...,
+            testNamePatterns: list[str] | None = None,
+        ) -> Sequence[str]: ...
+        @deprecated("Deprecated since Python 3.11; removed in Python 3.13.")
+        def makeSuite(
+            testCaseClass: type[unittest.case.TestCase],
+            prefix: str = "test",
+            sortUsing: _SortComparisonMethod = ...,
+            suiteClass: _SuiteClass = ...,
+        ) -> unittest.suite.TestSuite: ...
+        @deprecated("Deprecated since Python 3.11; removed in Python 3.13.")
+        def findTestCases(
+            module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ...
+        ) -> unittest.suite.TestSuite: ...
+    else:
+        def getTestCaseNames(
+            testCaseClass: type[unittest.case.TestCase],
+            prefix: str,
+            sortUsing: _SortComparisonMethod = ...,
+            testNamePatterns: list[str] | None = None,
+        ) -> Sequence[str]: ...
+        def makeSuite(
+            testCaseClass: type[unittest.case.TestCase],
+            prefix: str = "test",
+            sortUsing: _SortComparisonMethod = ...,
+            suiteClass: _SuiteClass = ...,
+        ) -> unittest.suite.TestSuite: ...
+        def findTestCases(
+            module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ...
+        ) -> unittest.suite.TestSuite: ...
diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi
index 152e9c33209c..23ead1638ecc 100644
--- a/mypy/typeshed/stdlib/unittest/main.pyi
+++ b/mypy/typeshed/stdlib/unittest/main.pyi
@@ -64,8 +64,11 @@ class TestProgram:
         ) -> None: ...
 
     if sys.version_info < (3, 13):
-        @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13")
-        def usageExit(self, msg: Any = None) -> None: ...
+        if sys.version_info >= (3, 11):
+            @deprecated("Deprecated since Python 3.11; removed in Python 3.13.")
+            def usageExit(self, msg: Any = None) -> None: ...
+        else:
+            def usageExit(self, msg: Any = None) -> None: ...
 
     def parseArgs(self, argv: list[str]) -> None: ...
     def createTests(self, from_discovery: bool = False, Loader: unittest.loader.TestLoader | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi
index 6b0941a91719..f4b59e7cab90 100644
--- a/mypy/typeshed/stdlib/unittest/mock.pyi
+++ b/mypy/typeshed/stdlib/unittest/mock.pyi
@@ -4,7 +4,7 @@ from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, S
 from contextlib import _GeneratorContextManager
 from types import TracebackType
 from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload, type_check_only
-from typing_extensions import ParamSpec, Self, TypeAlias
+from typing_extensions import ParamSpec, Self, TypeAlias, disjoint_base
 
 _T = TypeVar("_T")
 _TT = TypeVar("_TT", bound=type[Any])
@@ -52,7 +52,7 @@ else:
         "seal",
     )
 
-FILTER_DIR: Any
+FILTER_DIR: bool  # controls the way mock objects respond to `dir` function
 
 class _SentinelObject:
     name: Any
@@ -61,36 +61,73 @@ class _SentinelObject:
 class _Sentinel:
     def __getattr__(self, name: str) -> Any: ...
 
-sentinel: Any
+sentinel: _Sentinel
 DEFAULT: Any
 
 _ArgsKwargs: TypeAlias = tuple[tuple[Any, ...], Mapping[str, Any]]
 _NameArgsKwargs: TypeAlias = tuple[str, tuple[Any, ...], Mapping[str, Any]]
 _CallValue: TypeAlias = str | tuple[Any, ...] | Mapping[str, Any] | _ArgsKwargs | _NameArgsKwargs
 
-class _Call(tuple[Any, ...]):
-    def __new__(
-        cls, value: _CallValue = (), name: str | None = "", parent: _Call | None = None, two: bool = False, from_kall: bool = True
-    ) -> Self: ...
-    def __init__(
-        self,
-        value: _CallValue = (),
-        name: str | None = None,
-        parent: _Call | None = None,
-        two: bool = False,
-        from_kall: bool = True,
-    ) -> None: ...
-    __hash__: ClassVar[None]  # type: ignore[assignment]
-    def __eq__(self, other: object) -> bool: ...
-    def __ne__(self, value: object, /) -> bool: ...
-    def __call__(self, *args: Any, **kwargs: Any) -> _Call: ...
-    def __getattr__(self, attr: str) -> Any: ...
-    def __getattribute__(self, attr: str) -> Any: ...
-    @property
-    def args(self) -> tuple[Any, ...]: ...
-    @property
-    def kwargs(self) -> Mapping[str, Any]: ...
-    def call_list(self) -> Any: ...
+if sys.version_info >= (3, 12):
+    class _Call(tuple[Any, ...]):
+        def __new__(
+            cls,
+            value: _CallValue = (),
+            name: str | None = "",
+            parent: _Call | None = None,
+            two: bool = False,
+            from_kall: bool = True,
+        ) -> Self: ...
+        def __init__(
+            self,
+            value: _CallValue = (),
+            name: str | None = None,
+            parent: _Call | None = None,
+            two: bool = False,
+            from_kall: bool = True,
+        ) -> None: ...
+        __hash__: ClassVar[None]  # type: ignore[assignment]
+        def __eq__(self, other: object) -> bool: ...
+        def __ne__(self, value: object, /) -> bool: ...
+        def __call__(self, *args: Any, **kwargs: Any) -> _Call: ...
+        def __getattr__(self, attr: str) -> Any: ...
+        def __getattribute__(self, attr: str) -> Any: ...
+        @property
+        def args(self) -> tuple[Any, ...]: ...
+        @property
+        def kwargs(self) -> Mapping[str, Any]: ...
+        def call_list(self) -> Any: ...
+
+else:
+    @disjoint_base
+    class _Call(tuple[Any, ...]):
+        def __new__(
+            cls,
+            value: _CallValue = (),
+            name: str | None = "",
+            parent: _Call | None = None,
+            two: bool = False,
+            from_kall: bool = True,
+        ) -> Self: ...
+        def __init__(
+            self,
+            value: _CallValue = (),
+            name: str | None = None,
+            parent: _Call | None = None,
+            two: bool = False,
+            from_kall: bool = True,
+        ) -> None: ...
+        __hash__: ClassVar[None]  # type: ignore[assignment]
+        def __eq__(self, other: object) -> bool: ...
+        def __ne__(self, value: object, /) -> bool: ...
+        def __call__(self, *args: Any, **kwargs: Any) -> _Call: ...
+        def __getattr__(self, attr: str) -> Any: ...
+        def __getattribute__(self, attr: str) -> Any: ...
+        @property
+        def args(self) -> tuple[Any, ...]: ...
+        @property
+        def kwargs(self) -> Mapping[str, Any]: ...
+        def call_list(self) -> Any: ...
 
 call: _Call
 
@@ -297,27 +334,32 @@ class _patcher:
     # Ideally we'd be able to add an overload for it so that the return type is _patch[MagicMock],
     # but that's impossible with the current type system.
     @overload
-    def __call__(
+    def __call__(  # type: ignore[overload-overlap]
         self,
         target: str,
         new: _T,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
-        new_callable: Callable[..., Any] | None = ...,
-        **kwargs: Any,
+        spec: Literal[False] | None = None,
+        create: bool = False,
+        spec_set: Literal[False] | None = None,
+        autospec: Literal[False] | None = None,
+        new_callable: None = None,
+        *,
+        unsafe: bool = False,
     ) -> _patch[_T]: ...
     @overload
     def __call__(
         self,
         target: str,
         *,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
+        # If not False or None, this is passed to new_callable
+        spec: Any | Literal[False] | None = None,
+        create: bool = False,
+        # If not False or None, this is passed to new_callable
+        spec_set: Any | Literal[False] | None = None,
+        autospec: Literal[False] | None = None,
         new_callable: Callable[..., _T],
+        unsafe: bool = False,
+        # kwargs are passed to new_callable
         **kwargs: Any,
     ) -> _patch_pass_arg[_T]: ...
     @overload
@@ -325,25 +367,31 @@ class _patcher:
         self,
         target: str,
         *,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
-        new_callable: None = ...,
+        spec: Any | bool | None = None,
+        create: bool = False,
+        spec_set: Any | bool | None = None,
+        autospec: Any | bool | None = None,
+        new_callable: None = None,
+        unsafe: bool = False,
+        # kwargs are passed to the MagicMock/AsyncMock constructor
         **kwargs: Any,
     ) -> _patch_pass_arg[MagicMock | AsyncMock]: ...
+    # This overload also covers the case, where new==DEFAULT. In this case, the return type is _patch[Any].
+    # Ideally we'd be able to add an overload for it so that the return type is _patch[MagicMock],
+    # but that's impossible with the current type system.
     @overload
     @staticmethod
     def object(
         target: Any,
         attribute: str,
         new: _T,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
-        new_callable: Callable[..., Any] | None = ...,
-        **kwargs: Any,
+        spec: Literal[False] | None = None,
+        create: bool = False,
+        spec_set: Literal[False] | None = None,
+        autospec: Literal[False] | None = None,
+        new_callable: None = None,
+        *,
+        unsafe: bool = False,
     ) -> _patch[_T]: ...
     @overload
     @staticmethod
@@ -351,11 +399,15 @@ class _patcher:
         target: Any,
         attribute: str,
         *,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
+        # If not False or None, this is passed to new_callable
+        spec: Any | Literal[False] | None = None,
+        create: bool = False,
+        # If not False or None, this is passed to new_callable
+        spec_set: Any | Literal[False] | None = None,
+        autospec: Literal[False] | None = None,
         new_callable: Callable[..., _T],
+        unsafe: bool = False,
+        # kwargs are passed to new_callable
         **kwargs: Any,
     ) -> _patch_pass_arg[_T]: ...
     @overload
@@ -364,21 +416,54 @@ class _patcher:
         target: Any,
         attribute: str,
         *,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
-        new_callable: None = ...,
+        spec: Any | bool | None = None,
+        create: bool = False,
+        spec_set: Any | bool | None = None,
+        autospec: Any | bool | None = None,
+        new_callable: None = None,
+        unsafe: bool = False,
+        # kwargs are passed to the MagicMock/AsyncMock constructor
         **kwargs: Any,
     ) -> _patch_pass_arg[MagicMock | AsyncMock]: ...
+    @overload
     @staticmethod
     def multiple(
-        target: Any,
-        spec: Any | None = ...,
-        create: bool = ...,
-        spec_set: Any | None = ...,
-        autospec: Any | None = ...,
-        new_callable: Any | None = ...,
+        target: Any | str,
+        # If not False or None, this is passed to new_callable
+        spec: Any | Literal[False] | None = None,
+        create: bool = False,
+        # If not False or None, this is passed to new_callable
+        spec_set: Any | Literal[False] | None = None,
+        autospec: Literal[False] | None = None,
+        *,
+        new_callable: Callable[..., _T],
+        # The kwargs must be DEFAULT
+        **kwargs: Any,
+    ) -> _patch_pass_arg[_T]: ...
+    @overload
+    @staticmethod
+    def multiple(
+        target: Any | str,
+        # If not False or None, this is passed to new_callable
+        spec: Any | Literal[False] | None,
+        create: bool,
+        # If not False or None, this is passed to new_callable
+        spec_set: Any | Literal[False] | None,
+        autospec: Literal[False] | None,
+        new_callable: Callable[..., _T],
+        # The kwargs must be DEFAULT
+        **kwargs: Any,
+    ) -> _patch_pass_arg[_T]: ...
+    @overload
+    @staticmethod
+    def multiple(
+        target: Any | str,
+        spec: Any | bool | None = None,
+        create: bool = False,
+        spec_set: Any | bool | None = None,
+        autospec: Any | bool | None = None,
+        new_callable: None = None,
+        # The kwargs are the mock objects or DEFAULT
         **kwargs: Any,
     ) -> _patch[Any]: ...
     @staticmethod
@@ -428,7 +513,7 @@ class _ANY:
     def __ne__(self, other: object) -> Literal[False]: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
 
-ANY: Any
+ANY: _ANY
 
 if sys.version_info >= (3, 10):
     def create_autospec(
diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi
index 945b0cecfed0..31c830e8268a 100644
--- a/mypy/typeshed/stdlib/unittest/util.pyi
+++ b/mypy/typeshed/stdlib/unittest/util.pyi
@@ -5,12 +5,12 @@ from typing_extensions import TypeAlias
 _T = TypeVar("_T")
 _Mismatch: TypeAlias = tuple[_T, _T, int]
 
-_MAX_LENGTH: Final[int]
-_PLACEHOLDER_LEN: Final[int]
-_MIN_BEGIN_LEN: Final[int]
-_MIN_END_LEN: Final[int]
-_MIN_COMMON_LEN: Final[int]
-_MIN_DIFF_LEN: Final[int]
+_MAX_LENGTH: Final = 80
+_PLACEHOLDER_LEN: Final = 12
+_MIN_BEGIN_LEN: Final = 5
+_MIN_END_LEN: Final = 5
+_MIN_COMMON_LEN: Final = 5
+_MIN_DIFF_LEN: Final = 41
 
 def _shorten(s: str, prefixlen: int, suffixlen: int) -> str: ...
 def _common_shorten_repr(*args: str) -> tuple[str, ...]: ...
diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi
index a5ed616d25af..364892ecdf69 100644
--- a/mypy/typeshed/stdlib/urllib/parse.pyi
+++ b/mypy/typeshed/stdlib/urllib/parse.pyi
@@ -1,7 +1,7 @@
 import sys
 from collections.abc import Iterable, Mapping, Sequence
 from types import GenericAlias
-from typing import Any, AnyStr, Generic, Literal, NamedTuple, Protocol, overload, type_check_only
+from typing import Any, AnyStr, Final, Generic, Literal, NamedTuple, Protocol, overload, type_check_only
 from typing_extensions import TypeAlias
 
 __all__ = [
@@ -28,23 +28,26 @@ __all__ = [
     "SplitResultBytes",
 ]
 
-uses_relative: list[str]
-uses_netloc: list[str]
-uses_params: list[str]
-non_hierarchical: list[str]
-uses_query: list[str]
-uses_fragment: list[str]
-scheme_chars: str
+uses_relative: Final[list[str]]
+uses_netloc: Final[list[str]]
+uses_params: Final[list[str]]
+non_hierarchical: Final[list[str]]
+uses_query: Final[list[str]]
+uses_fragment: Final[list[str]]
+scheme_chars: Final[str]
 if sys.version_info < (3, 11):
-    MAX_CACHE_SIZE: int
+    MAX_CACHE_SIZE: Final[int]
 
 class _ResultMixinStr:
+    __slots__ = ()
     def encode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinBytes: ...
 
 class _ResultMixinBytes:
+    __slots__ = ()
     def decode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinStr: ...
 
 class _NetlocResultMixinBase(Generic[AnyStr]):
+    __slots__ = ()
     @property
     def username(self) -> AnyStr | None: ...
     @property
@@ -55,8 +58,11 @@ class _NetlocResultMixinBase(Generic[AnyStr]):
     def port(self) -> int | None: ...
     def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
 
-class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): ...
-class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): ...
+class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr):
+    __slots__ = ()
+
+class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes):
+    __slots__ = ()
 
 class _DefragResultBase(NamedTuple, Generic[AnyStr]):
     url: AnyStr
diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi
index 0aa2f76d40cc..303fb10eaf53 100644
--- a/mypy/typeshed/stdlib/uuid.pyi
+++ b/mypy/typeshed/stdlib/uuid.pyi
@@ -12,6 +12,7 @@ class SafeUUID(Enum):
     unknown = None
 
 class UUID:
+    __slots__ = ("int", "is_safe", "__weakref__")
     def __init__(
         self,
         hex: str | None = None,
diff --git a/mypy/typeshed/stdlib/venv/__init__.pyi b/mypy/typeshed/stdlib/venv/__init__.pyi
index 0f71f0e073f5..14db88523dba 100644
--- a/mypy/typeshed/stdlib/venv/__init__.pyi
+++ b/mypy/typeshed/stdlib/venv/__init__.pyi
@@ -3,10 +3,11 @@ import sys
 from _typeshed import StrOrBytesPath
 from collections.abc import Iterable, Sequence
 from types import SimpleNamespace
+from typing import Final
 
 logger: logging.Logger
 
-CORE_VENV_DEPS: tuple[str, ...]
+CORE_VENV_DEPS: Final[tuple[str, ...]]
 
 class EnvBuilder:
     system_site_packages: bool
diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi
index ddc6f6bd02a5..fd7dbfade884 100644
--- a/mypy/typeshed/stdlib/wave.pyi
+++ b/mypy/typeshed/stdlib/wave.pyi
@@ -1,3 +1,4 @@
+import sys
 from _typeshed import ReadableBuffer, Unused
 from typing import IO, Any, BinaryIO, Final, Literal, NamedTuple, NoReturn, overload
 from typing_extensions import Self, TypeAlias, deprecated
@@ -8,7 +9,7 @@ _File: TypeAlias = str | IO[bytes]
 
 class Error(Exception): ...
 
-WAVE_FORMAT_PCM: Final = 1
+WAVE_FORMAT_PCM: Final = 0x0001
 
 class _wave_params(NamedTuple):
     nchannels: int
@@ -34,10 +35,15 @@ class Wave_read:
     def getcomptype(self) -> str: ...
     def getcompname(self) -> str: ...
     def getparams(self) -> _wave_params: ...
-    @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-    def getmarkers(self) -> None: ...
-    @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-    def getmark(self, id: Any) -> NoReturn: ...
+    if sys.version_info >= (3, 13):
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+        def getmarkers(self) -> None: ...
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+        def getmark(self, id: Any) -> NoReturn: ...
+    else:
+        def getmarkers(self) -> None: ...
+        def getmark(self, id: Any) -> NoReturn: ...
+
     def setpos(self, pos: int) -> None: ...
     def readframes(self, nframes: int) -> bytes: ...
 
@@ -59,12 +65,18 @@ class Wave_write:
     def getcompname(self) -> str: ...
     def setparams(self, params: _wave_params | tuple[int, int, int, int, str, str]) -> None: ...
     def getparams(self) -> _wave_params: ...
-    @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-    def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ...
-    @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-    def getmark(self, id: Any) -> NoReturn: ...
-    @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
-    def getmarkers(self) -> None: ...
+    if sys.version_info >= (3, 13):
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+        def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ...
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+        def getmark(self, id: Any) -> NoReturn: ...
+        @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.")
+        def getmarkers(self) -> None: ...
+    else:
+        def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ...
+        def getmark(self, id: Any) -> NoReturn: ...
+        def getmarkers(self) -> None: ...
+
     def tell(self) -> int: ...
     def writeframesraw(self, data: ReadableBuffer) -> None: ...
     def writeframes(self, data: ReadableBuffer) -> None: ...
diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi
index 334fab7e7468..76ab86b957a1 100644
--- a/mypy/typeshed/stdlib/weakref.pyi
+++ b/mypy/typeshed/stdlib/weakref.pyi
@@ -4,7 +4,7 @@ from _weakrefset import WeakSet as WeakSet
 from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping
 from types import GenericAlias
 from typing import Any, ClassVar, Generic, TypeVar, final, overload
-from typing_extensions import ParamSpec, Self
+from typing_extensions import ParamSpec, Self, disjoint_base
 
 __all__ = [
     "ref",
@@ -52,6 +52,7 @@ class ProxyType(Generic[_T]):  # "weakproxy"
     def __getattr__(self, attr: str) -> Any: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
 
+@disjoint_base
 class ReferenceType(Generic[_T]):  # "weakref"
     __callback__: Callable[[Self], Any]
     def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ...
@@ -65,6 +66,7 @@ ref = ReferenceType
 # everything below here is implemented in weakref.py
 
 class WeakMethod(ref[_CallableT]):
+    __slots__ = ("_func_ref", "_meth_type", "_alive", "__weakref__")
     def __new__(cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None) -> Self: ...
     def __call__(self) -> _CallableT | None: ...
     def __eq__(self, other: object) -> bool: ...
@@ -130,6 +132,7 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]):
     def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
 
 class KeyedRef(ref[_T], Generic[_KT, _T]):
+    __slots__ = ("key",)
     key: _KT
     def __new__(type, ob: _T, callback: Callable[[Self], Any], key: _KT) -> Self: ...
     def __init__(self, ob: _T, callback: Callable[[Self], Any], key: _KT) -> None: ...
@@ -185,6 +188,7 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]):
     def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ...
 
 class finalize(Generic[_P, _T]):
+    __slots__ = ()
     def __init__(self, obj: _T, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ...
     def __call__(self, _: Any = None) -> Any | None: ...
     def detach(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ...
diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi
index 773786c24821..56c30f872727 100644
--- a/mypy/typeshed/stdlib/webbrowser.pyi
+++ b/mypy/typeshed/stdlib/webbrowser.pyi
@@ -64,10 +64,16 @@ if sys.platform == "win32":
 
 if sys.platform == "darwin":
     if sys.version_info < (3, 13):
-        @deprecated("Deprecated in 3.11, to be removed in 3.13.")
-        class MacOSX(BaseBrowser):
-            def __init__(self, name: str) -> None: ...
-            def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ...
+        if sys.version_info >= (3, 11):
+            @deprecated("Deprecated since Python 3.11; removed in Python 3.13.")
+            class MacOSX(BaseBrowser):
+                def __init__(self, name: str) -> None: ...
+                def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ...
+
+        else:
+            class MacOSX(BaseBrowser):
+                def __init__(self, name: str) -> None: ...
+                def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ...
 
     class MacOSXOSAScript(BaseBrowser):  # In runtime this class does not have `name` and `basename`
         if sys.version_info >= (3, 11):
diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi
index 0a22bb23d8f6..53457112ee96 100644
--- a/mypy/typeshed/stdlib/winreg.pyi
+++ b/mypy/typeshed/stdlib/winreg.pyi
@@ -59,13 +59,13 @@ if sys.platform == "win32":
     def EnableReflectionKey(key: _KeyType, /) -> None: ...
     def QueryReflectionKey(key: _KeyType, /) -> bool: ...
 
-    HKEY_CLASSES_ROOT: int
-    HKEY_CURRENT_USER: int
-    HKEY_LOCAL_MACHINE: int
-    HKEY_USERS: int
-    HKEY_PERFORMANCE_DATA: int
-    HKEY_CURRENT_CONFIG: int
-    HKEY_DYN_DATA: int
+    HKEY_CLASSES_ROOT: Final[int]
+    HKEY_CURRENT_USER: Final[int]
+    HKEY_LOCAL_MACHINE: Final[int]
+    HKEY_USERS: Final[int]
+    HKEY_PERFORMANCE_DATA: Final[int]
+    HKEY_CURRENT_CONFIG: Final[int]
+    HKEY_DYN_DATA: Final[int]
 
     KEY_ALL_ACCESS: Final = 983103
     KEY_WRITE: Final = 131078
diff --git a/mypy/typeshed/stdlib/wsgiref/headers.pyi b/mypy/typeshed/stdlib/wsgiref/headers.pyi
index 2654d79bf4e5..9febad4b3277 100644
--- a/mypy/typeshed/stdlib/wsgiref/headers.pyi
+++ b/mypy/typeshed/stdlib/wsgiref/headers.pyi
@@ -1,10 +1,10 @@
 from re import Pattern
-from typing import overload
+from typing import Final, overload
 from typing_extensions import TypeAlias
 
 _HeaderList: TypeAlias = list[tuple[str, str]]
 
-tspecials: Pattern[str]  # undocumented
+tspecials: Final[Pattern[str]]  # undocumented
 
 class Headers:
     def __init__(self, headers: _HeaderList | None = None) -> None: ...
diff --git a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi
index 547f562cc1d4..bdf58719c828 100644
--- a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi
+++ b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi
@@ -1,14 +1,14 @@
 from _typeshed.wsgi import ErrorStream, StartResponse, WSGIApplication, WSGIEnvironment
 from http.server import BaseHTTPRequestHandler, HTTPServer
-from typing import TypeVar, overload
+from typing import Final, TypeVar, overload
 
 from .handlers import SimpleHandler
 
 __all__ = ["WSGIServer", "WSGIRequestHandler", "demo_app", "make_server"]
 
-server_version: str  # undocumented
-sys_version: str  # undocumented
-software_version: str  # undocumented
+server_version: Final[str]  # undocumented
+sys_version: Final[str]  # undocumented
+software_version: Final[str]  # undocumented
 
 class ServerHandler(SimpleHandler):  # undocumented
     server_software: str
diff --git a/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi
index 007df982e06a..7b301373f528 100644
--- a/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi
@@ -1,22 +1,22 @@
-from typing import Literal
+from typing import Final
 from xml.dom.minidom import Node
 
 class NodeFilter:
-    FILTER_ACCEPT: Literal[1]
-    FILTER_REJECT: Literal[2]
-    FILTER_SKIP: Literal[3]
+    FILTER_ACCEPT: Final = 1
+    FILTER_REJECT: Final = 2
+    FILTER_SKIP: Final = 3
 
-    SHOW_ALL: int
-    SHOW_ELEMENT: int
-    SHOW_ATTRIBUTE: int
-    SHOW_TEXT: int
-    SHOW_CDATA_SECTION: int
-    SHOW_ENTITY_REFERENCE: int
-    SHOW_ENTITY: int
-    SHOW_PROCESSING_INSTRUCTION: int
-    SHOW_COMMENT: int
-    SHOW_DOCUMENT: int
-    SHOW_DOCUMENT_TYPE: int
-    SHOW_DOCUMENT_FRAGMENT: int
-    SHOW_NOTATION: int
+    SHOW_ALL: Final = 0xFFFFFFFF
+    SHOW_ELEMENT: Final = 0x00000001
+    SHOW_ATTRIBUTE: Final = 0x00000002
+    SHOW_TEXT: Final = 0x00000004
+    SHOW_CDATA_SECTION: Final = 0x00000008
+    SHOW_ENTITY_REFERENCE: Final = 0x00000010
+    SHOW_ENTITY: Final = 0x00000020
+    SHOW_PROCESSING_INSTRUCTION: Final = 0x00000040
+    SHOW_COMMENT: Final = 0x00000080
+    SHOW_DOCUMENT: Final = 0x00000100
+    SHOW_DOCUMENT_TYPE: Final = 0x00000200
+    SHOW_DOCUMENT_FRAGMENT: Final = 0x00000400
+    SHOW_NOTATION: Final = 0x00000800
     def acceptNode(self, node: Node) -> int: ...
diff --git a/mypy/typeshed/stdlib/xml/dom/__init__.pyi b/mypy/typeshed/stdlib/xml/dom/__init__.pyi
index d9615f9aacfe..5dbb6c536f61 100644
--- a/mypy/typeshed/stdlib/xml/dom/__init__.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/__init__.pyi
@@ -3,18 +3,19 @@ from typing import Any, Final, Literal
 from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation
 
 class Node:
-    ELEMENT_NODE: Literal[1]
-    ATTRIBUTE_NODE: Literal[2]
-    TEXT_NODE: Literal[3]
-    CDATA_SECTION_NODE: Literal[4]
-    ENTITY_REFERENCE_NODE: Literal[5]
-    ENTITY_NODE: Literal[6]
-    PROCESSING_INSTRUCTION_NODE: Literal[7]
-    COMMENT_NODE: Literal[8]
-    DOCUMENT_NODE: Literal[9]
-    DOCUMENT_TYPE_NODE: Literal[10]
-    DOCUMENT_FRAGMENT_NODE: Literal[11]
-    NOTATION_NODE: Literal[12]
+    __slots__ = ()
+    ELEMENT_NODE: Final = 1
+    ATTRIBUTE_NODE: Final = 2
+    TEXT_NODE: Final = 3
+    CDATA_SECTION_NODE: Final = 4
+    ENTITY_REFERENCE_NODE: Final = 5
+    ENTITY_NODE: Final = 6
+    PROCESSING_INSTRUCTION_NODE: Final = 7
+    COMMENT_NODE: Final = 8
+    DOCUMENT_NODE: Final = 9
+    DOCUMENT_TYPE_NODE: Final = 10
+    DOCUMENT_FRAGMENT_NODE: Final = 11
+    NOTATION_NODE: Final = 12
 
 # ExceptionCode
 INDEX_SIZE_ERR: Final = 1
@@ -88,10 +89,10 @@ class ValidationErr(DOMException):
     code: Literal[16]
 
 class UserDataHandler:
-    NODE_CLONED: Literal[1]
-    NODE_IMPORTED: Literal[2]
-    NODE_DELETED: Literal[3]
-    NODE_RENAMED: Literal[4]
+    NODE_CLONED: Final = 1
+    NODE_IMPORTED: Final = 2
+    NODE_DELETED: Final = 3
+    NODE_RENAMED: Final = 4
 
 XML_NAMESPACE: Final = "http://www.w3.org/XML/1998/namespace"
 XMLNS_NAMESPACE: Final = "http://www.w3.org/2000/xmlns/"
diff --git a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi
index 228ad07e15ad..2b9ac8876970 100644
--- a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi
@@ -1,5 +1,5 @@
 from _typeshed import ReadableBuffer, SupportsRead
-from typing import Any, NoReturn
+from typing import Any, Final, NoReturn
 from typing_extensions import TypeAlias
 from xml.dom.minidom import Document, DocumentFragment, DOMImplementation, Element, Node, TypeInfo
 from xml.dom.xmlbuilder import DOMBuilderFilter, Options
@@ -7,16 +7,17 @@ from xml.parsers.expat import XMLParserType
 
 _Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]]  # same as in pyexpat
 
-TEXT_NODE = Node.TEXT_NODE
-CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE
-DOCUMENT_NODE = Node.DOCUMENT_NODE
-FILTER_ACCEPT = DOMBuilderFilter.FILTER_ACCEPT
-FILTER_REJECT = DOMBuilderFilter.FILTER_REJECT
-FILTER_SKIP = DOMBuilderFilter.FILTER_SKIP
-FILTER_INTERRUPT = DOMBuilderFilter.FILTER_INTERRUPT
+TEXT_NODE: Final = Node.TEXT_NODE
+CDATA_SECTION_NODE: Final = Node.CDATA_SECTION_NODE
+DOCUMENT_NODE: Final = Node.DOCUMENT_NODE
+FILTER_ACCEPT: Final = DOMBuilderFilter.FILTER_ACCEPT
+FILTER_REJECT: Final = DOMBuilderFilter.FILTER_REJECT
+FILTER_SKIP: Final = DOMBuilderFilter.FILTER_SKIP
+FILTER_INTERRUPT: Final = DOMBuilderFilter.FILTER_INTERRUPT
 theDOMImplementation: DOMImplementation
 
 class ElementInfo:
+    __slots__ = ("_attr_info", "_model", "tagName")
     tagName: str
     def __init__(self, tagName: str, model: _Model | None = None) -> None: ...
     def getAttributeType(self, aname: str) -> TypeInfo: ...
@@ -66,19 +67,23 @@ class ExpatBuilder:
     def xml_decl_handler(self, version: str, encoding: str | None, standalone: int) -> None: ...
 
 class FilterVisibilityController:
+    __slots__ = ("filter",)
     filter: DOMBuilderFilter
     def __init__(self, filter: DOMBuilderFilter) -> None: ...
     def startContainer(self, node: Node) -> int: ...
     def acceptNode(self, node: Node) -> int: ...
 
 class FilterCrutch:
+    __slots__ = ("_builder", "_level", "_old_start", "_old_end")
     def __init__(self, builder: ExpatBuilder) -> None: ...
 
 class Rejecter(FilterCrutch):
+    __slots__ = ()
     def start_element_handler(self, *args: Any) -> None: ...
     def end_element_handler(self, *args: Any) -> None: ...
 
 class Skipper(FilterCrutch):
+    __slots__ = ()
     def start_element_handler(self, *args: Any) -> None: ...
     def end_element_handler(self, *args: Any) -> None: ...
 
diff --git a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi
index 162f60254a58..6fcaee019dc2 100644
--- a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi
@@ -8,11 +8,13 @@ _T = TypeVar("_T")
 StringTypes: tuple[type[str]]
 
 class NodeList(list[_T]):
+    __slots__ = ()
     @property
     def length(self) -> int: ...
     def item(self, index: int) -> _T | None: ...
 
 class EmptyNodeList(tuple[()]):
+    __slots__ = ()
     @property
     def length(self) -> Literal[0]: ...
     def item(self, index: int) -> None: ...
diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
index b9da9f3558ff..e0431417aa3c 100644
--- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi
@@ -188,6 +188,7 @@ _AttrChildrenVar = TypeVar("_AttrChildrenVar", bound=_AttrChildren)
 _AttrChildrenPlusFragment = TypeVar("_AttrChildrenPlusFragment", bound=_AttrChildren | DocumentFragment)
 
 class Attr(Node):
+    __slots__ = ("_name", "_value", "namespaceURI", "_prefix", "childNodes", "_localName", "ownerDocument", "ownerElement")
     nodeType: ClassVar[Literal[2]]
     nodeName: str  # same as Attr.name
     nodeValue: str  # same as Attr.value
@@ -231,6 +232,7 @@ class Attr(Node):
 # In the DOM, this interface isn't specific to Attr, but our implementation is
 # because that's the only place we use it.
 class NamedNodeMap:
+    __slots__ = ("_attrs", "_attrsNS", "_ownerElement")
     def __init__(self, attrs: dict[str, Attr], attrsNS: dict[_NSName, Attr], ownerElement: Element) -> None: ...
     @property
     def length(self) -> int: ...
@@ -262,6 +264,7 @@ class NamedNodeMap:
 AttributeList = NamedNodeMap
 
 class TypeInfo:
+    __slots__ = ("namespace", "name")
     namespace: str | None
     name: str | None
     def __init__(self, namespace: Incomplete | None, name: str | None) -> None: ...
@@ -270,6 +273,20 @@ _ElementChildrenVar = TypeVar("_ElementChildrenVar", bound=_ElementChildren)
 _ElementChildrenPlusFragment = TypeVar("_ElementChildrenPlusFragment", bound=_ElementChildren | DocumentFragment)
 
 class Element(Node):
+    __slots__ = (
+        "ownerDocument",
+        "parentNode",
+        "tagName",
+        "nodeName",
+        "prefix",
+        "namespaceURI",
+        "_localName",
+        "childNodes",
+        "_attrs",
+        "_attrsNS",
+        "nextSibling",
+        "previousSibling",
+    )
     nodeType: ClassVar[Literal[1]]
     nodeName: str  # same as Element.tagName
     nodeValue: None
@@ -331,6 +348,7 @@ class Element(Node):
     def removeChild(self, oldChild: _ElementChildrenVar) -> _ElementChildrenVar: ...  # type: ignore[override]
 
 class Childless:
+    __slots__ = ()
     attributes: None
     childNodes: EmptyNodeList
     @property
@@ -347,6 +365,7 @@ class Childless:
     def replaceChild(self, newChild: _NodesThatAreChildren | DocumentFragment, oldChild: _NodesThatAreChildren) -> NoReturn: ...
 
 class ProcessingInstruction(Childless, Node):
+    __slots__ = ("target", "data")
     nodeType: ClassVar[Literal[7]]
     nodeName: str  # same as ProcessingInstruction.target
     nodeValue: str  # same as ProcessingInstruction.data
@@ -373,6 +392,7 @@ class ProcessingInstruction(Childless, Node):
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class CharacterData(Childless, Node):
+    __slots__ = ("_data", "ownerDocument", "parentNode", "previousSibling", "nextSibling")
     nodeValue: str
     attributes: None
 
@@ -397,6 +417,7 @@ class CharacterData(Childless, Node):
     def replaceData(self, offset: int, count: int, arg: str) -> None: ...
 
 class Text(CharacterData):
+    __slots__ = ()
     nodeType: ClassVar[Literal[3]]
     nodeName: Literal["#text"]
     nodeValue: str  # same as CharacterData.data, the content of the text node
@@ -448,6 +469,7 @@ class Comment(CharacterData):
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class CDATASection(Text):
+    __slots__ = ()
     nodeType: ClassVar[Literal[4]]  # type: ignore[assignment]
     nodeName: Literal["#cdata-section"]  # type: ignore[assignment]
     nodeValue: str  # same as CharacterData.data, the content of the CDATA Section
@@ -460,6 +482,7 @@ class CDATASection(Text):
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class ReadOnlySequentialNamedNodeMap(Generic[_N]):
+    __slots__ = ("_seq",)
     def __init__(self, seq: Sequence[_N] = ()) -> None: ...
     def __len__(self) -> int: ...
     def getNamedItem(self, name: str) -> _N | None: ...
@@ -474,6 +497,7 @@ class ReadOnlySequentialNamedNodeMap(Generic[_N]):
     def length(self) -> int: ...
 
 class Identified:
+    __slots__ = ("publicId", "systemId")
     publicId: str | None
     systemId: str | None
 
@@ -565,6 +589,7 @@ class DOMImplementation(DOMImplementationLS):
     def getInterface(self, feature: str) -> Self | None: ...
 
 class ElementInfo:
+    __slots__ = ("tagName",)
     tagName: str
     def __init__(self, name: str) -> None: ...
     def getAttributeType(self, aname: str) -> TypeInfo: ...
@@ -577,6 +602,7 @@ class ElementInfo:
 _DocumentChildrenPlusFragment = TypeVar("_DocumentChildrenPlusFragment", bound=_DocumentChildren | DocumentFragment)
 
 class Document(Node, DocumentLS):
+    __slots__ = ("_elem_info", "doctype", "_id_search_stack", "childNodes", "_id_cache")
     nodeType: ClassVar[Literal[9]]
     nodeName: Literal["#document"]
     nodeValue: None
diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi
index d9458654c185..df7a3ad0eddb 100644
--- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi
@@ -99,7 +99,7 @@ class SAX2DOM(PullDOM):
     def ignorableWhitespace(self, chars: str) -> None: ...
     def characters(self, chars: str) -> None: ...
 
-default_bufsize: int
+default_bufsize: Final[int]
 
 def parse(
     stream_or_string: str | _SupportsReadClose[bytes] | _SupportsReadClose[str],
diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi
index 6fb18bbc4eda..f19f7050b08d 100644
--- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi
+++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi
@@ -1,5 +1,5 @@
 from _typeshed import SupportsRead
-from typing import Any, Literal, NoReturn
+from typing import Any, Final, Literal, NoReturn
 from xml.dom.minidom import Document, Node, _DOMErrorHandler
 
 __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"]
@@ -29,10 +29,10 @@ class DOMBuilder:
     entityResolver: DOMEntityResolver | None
     errorHandler: _DOMErrorHandler | None
     filter: DOMBuilderFilter | None
-    ACTION_REPLACE: Literal[1]
-    ACTION_APPEND_AS_CHILDREN: Literal[2]
-    ACTION_INSERT_AFTER: Literal[3]
-    ACTION_INSERT_BEFORE: Literal[4]
+    ACTION_REPLACE: Final = 1
+    ACTION_APPEND_AS_CHILDREN: Final = 2
+    ACTION_INSERT_AFTER: Final = 3
+    ACTION_INSERT_BEFORE: Final = 4
     def __init__(self) -> None: ...
     def setFeature(self, name: str, state: int) -> None: ...
     def supportsFeature(self, name: str) -> bool: ...
@@ -44,9 +44,11 @@ class DOMBuilder:
     def parseWithContext(self, input: DOMInputSource, cnode: Node, action: Literal[1, 2, 3, 4]) -> NoReturn: ...
 
 class DOMEntityResolver:
+    __slots__ = ("_opener",)
     def resolveEntity(self, publicId: str | None, systemId: str) -> DOMInputSource: ...
 
 class DOMInputSource:
+    __slots__ = ("byteStream", "characterStream", "stringData", "encoding", "publicId", "systemId", "baseURI")
     byteStream: SupportsRead[bytes] | None
     characterStream: SupportsRead[str] | None
     stringData: str | None
@@ -56,10 +58,10 @@ class DOMInputSource:
     baseURI: str | None
 
 class DOMBuilderFilter:
-    FILTER_ACCEPT: Literal[1]
-    FILTER_REJECT: Literal[2]
-    FILTER_SKIP: Literal[3]
-    FILTER_INTERRUPT: Literal[4]
+    FILTER_ACCEPT: Final = 1
+    FILTER_REJECT: Final = 2
+    FILTER_SKIP: Final = 3
+    FILTER_INTERRUPT: Final = 4
     whatToShow: int
     def acceptNode(self, element: Node) -> Literal[1, 2, 3, 4]: ...
     def startContainer(self, element: Node) -> Literal[1, 2, 3, 4]: ...
@@ -72,8 +74,8 @@ class DocumentLS:
     def saveXML(self, snode: Node | None) -> str: ...
 
 class DOMImplementationLS:
-    MODE_SYNCHRONOUS: Literal[1]
-    MODE_ASYNCHRONOUS: Literal[2]
+    MODE_SYNCHRONOUS: Final = 1
+    MODE_ASYNCHRONOUS: Final = 2
     def createDOMBuilder(self, mode: Literal[1], schemaType: None) -> DOMBuilder: ...
     def createDOMWriter(self) -> NoReturn: ...
     def createDOMInputSource(self) -> DOMInputSource: ...
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
index fd829fdaa5ff..10784e7d4021 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi
@@ -9,9 +9,10 @@ class _Loader(Protocol):
     @overload
     def __call__(self, href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ...
 
-XINCLUDE: Final[str]
-XINCLUDE_INCLUDE: Final[str]
-XINCLUDE_FALLBACK: Final[str]
+XINCLUDE: Final = "{http://www.w3.org/2001/XInclude}"
+
+XINCLUDE_INCLUDE: Final = "{http://www.w3.org/2001/XInclude}include"
+XINCLUDE_FALLBACK: Final = "{http://www.w3.org/2001/XInclude}fallback"
 
 DEFAULT_MAX_INCLUSION_DEPTH: Final = 6
 
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi
index ebfb4f1ffbb9..80f3c55c1489 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi
@@ -1,10 +1,10 @@
 from collections.abc import Callable, Generator, Iterable
 from re import Pattern
-from typing import Any, Literal, TypeVar, overload
+from typing import Any, Final, Literal, TypeVar, overload
 from typing_extensions import TypeAlias
 from xml.etree.ElementTree import Element
 
-xpath_tokenizer_re: Pattern[str]
+xpath_tokenizer_re: Final[Pattern[str]]
 
 _Token: TypeAlias = tuple[str, str]
 _Next: TypeAlias = Callable[[], _Token]
@@ -20,7 +20,7 @@ def prepare_descendant(next: _Next, token: _Token) -> _Callback | None: ...
 def prepare_parent(next: _Next, token: _Token) -> _Callback: ...
 def prepare_predicate(next: _Next, token: _Token) -> _Callback | None: ...
 
-ops: dict[str, Callable[[_Next, _Token], _Callback | None]]
+ops: Final[dict[str, Callable[[_Next, _Token], _Callback | None]]]
 
 class _SelectorContext:
     parent_map: dict[Element, Element] | None
diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
index 1d7e1725dd8e..e8f737778040 100644
--- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
+++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi
@@ -3,7 +3,7 @@ from _collections_abc import dict_keys
 from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite
 from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence
 from typing import Any, Final, Generic, Literal, Protocol, SupportsIndex, TypeVar, overload, type_check_only
-from typing_extensions import TypeAlias, TypeGuard, deprecated
+from typing_extensions import TypeAlias, TypeGuard, deprecated, disjoint_base
 from xml.parsers.expat import XMLParserType
 
 __all__ = [
@@ -78,14 +78,13 @@ def canonicalize(
 ) -> None: ...
 
 # The tag for Element can be set to the Comment or ProcessingInstruction
-# functions defined in this module. _ElementCallable could be a recursive
-# type, but defining it that way uncovered a bug in pytype.
-_ElementCallable: TypeAlias = Callable[..., Element[Any]]
-_CallableElement: TypeAlias = Element[_ElementCallable]
+# functions defined in this module.
+_ElementCallable: TypeAlias = Callable[..., Element[_ElementCallable]]
 
 _Tag = TypeVar("_Tag", default=str, bound=str | _ElementCallable)
 _OtherTag = TypeVar("_OtherTag", default=str, bound=str | _ElementCallable)
 
+@disjoint_base
 class Element(Generic[_Tag]):
     tag: _Tag
     attrib: dict[str, str]
@@ -138,8 +137,8 @@ class Element(Generic[_Tag]):
     def __bool__(self) -> bool: ...
 
 def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ...
-def Comment(text: str | None = None) -> _CallableElement: ...
-def ProcessingInstruction(target: str, text: str | None = None) -> _CallableElement: ...
+def Comment(text: str | None = None) -> Element[_ElementCallable]: ...
+def ProcessingInstruction(target: str, text: str | None = None) -> Element[_ElementCallable]: ...
 
 PI = ProcessingInstruction
 
@@ -182,7 +181,7 @@ class ElementTree(Generic[_Root]):
     ) -> None: ...
     def write_c14n(self, file: _FileWriteC14N) -> None: ...
 
-HTML_EMPTY: set[str]
+HTML_EMPTY: Final[set[str]]
 
 def register_namespace(prefix: str, uri: str) -> None: ...
 @overload
@@ -288,6 +287,7 @@ def fromstringlist(sequence: Sequence[str | ReadableBuffer], parser: XMLParser |
 # elementfactories.
 _ElementFactory: TypeAlias = Callable[[Any, dict[Any, Any]], Element]
 
+@disjoint_base
 class TreeBuilder:
     # comment_factory can take None because passing None to Comment is not an error
     def __init__(
@@ -353,6 +353,7 @@ _E = TypeVar("_E", default=Element)
 # The default target is TreeBuilder, which returns Element.
 # C14NWriterTarget does not implement a close method, so using it results
 # in a type of XMLParser[None].
+@disjoint_base
 class XMLParser(Generic[_E]):
     parser: XMLParserType
     target: _Target
diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
index 5a82b48c1e19..679466fa34d2 100644
--- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co
 from collections.abc import Iterable
-from typing import Protocol, type_check_only
+from typing import Final, Protocol, type_check_only
 from typing_extensions import TypeAlias
 from xml.sax._exceptions import (
     SAXException as SAXException,
@@ -19,7 +19,7 @@ class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]):
 
 _Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str]
 
-default_parser_list: list[str]
+default_parser_list: Final[list[str]]
 
 def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ...
 def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ...
diff --git a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
index 012d6c03e121..3f9573a25f9a 100644
--- a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi
@@ -1,7 +1,7 @@
 import sys
 from _typeshed import ReadableBuffer
 from collections.abc import Mapping
-from typing import Any, Literal, overload
+from typing import Any, Final, Literal, overload
 from typing_extensions import TypeAlias
 from xml.sax import _Source, xmlreader
 from xml.sax.handler import _ContentHandlerProtocol
@@ -11,7 +11,7 @@ if sys.version_info >= (3, 10):
 
 _BoolType: TypeAlias = Literal[0, 1] | bool
 
-version: str
+version: Final[str]
 AttributesImpl = xmlreader.AttributesImpl
 AttributesNSImpl = xmlreader.AttributesNSImpl
 
diff --git a/mypy/typeshed/stdlib/xml/sax/handler.pyi b/mypy/typeshed/stdlib/xml/sax/handler.pyi
index 550911734596..5ecbfa6f1272 100644
--- a/mypy/typeshed/stdlib/xml/sax/handler.pyi
+++ b/mypy/typeshed/stdlib/xml/sax/handler.pyi
@@ -1,8 +1,8 @@
 import sys
-from typing import Literal, NoReturn, Protocol, type_check_only
+from typing import Final, NoReturn, Protocol, type_check_only
 from xml.sax import xmlreader
 
-version: str
+version: Final[str]
 
 @type_check_only
 class _ErrorHandlerProtocol(Protocol):  # noqa: Y046  # Protocol is not used
@@ -62,20 +62,20 @@ class _EntityResolverProtocol(Protocol):  # noqa: Y046  # Protocol is not used
 class EntityResolver:
     def resolveEntity(self, publicId: str | None, systemId: str) -> str: ...
 
-feature_namespaces: str
-feature_namespace_prefixes: str
-feature_string_interning: str
-feature_validation: str
-feature_external_ges: str
-feature_external_pes: str
-all_features: list[str]
-property_lexical_handler: Literal["http://xml.org/sax/properties/lexical-handler"]
-property_declaration_handler: Literal["http://xml.org/sax/properties/declaration-handler"]
-property_dom_node: Literal["http://xml.org/sax/properties/dom-node"]
-property_xml_string: Literal["http://xml.org/sax/properties/xml-string"]
-property_encoding: Literal["http://www.python.org/sax/properties/encoding"]
-property_interning_dict: Literal["http://www.python.org/sax/properties/interning-dict"]
-all_properties: list[str]
+feature_namespaces: Final = "http://xml.org/sax/features/namespaces"
+feature_namespace_prefixes: Final = "http://xml.org/sax/features/namespace-prefixes"
+feature_string_interning: Final = "http://xml.org/sax/features/string-interning"
+feature_validation: Final = "http://xml.org/sax/features/validation"
+feature_external_ges: Final[str]  # too long string
+feature_external_pes: Final[str]  # too long string
+all_features: Final[list[str]]
+property_lexical_handler: Final = "http://xml.org/sax/properties/lexical-handler"
+property_declaration_handler: Final = "http://xml.org/sax/properties/declaration-handler"
+property_dom_node: Final = "http://xml.org/sax/properties/dom-node"
+property_xml_string: Final = "http://xml.org/sax/properties/xml-string"
+property_encoding: Final = "http://www.python.org/sax/properties/encoding"
+property_interning_dict: Final[str]  # too long string
+all_properties: Final[list[str]]
 
 if sys.version_info >= (3, 10):
     class LexicalHandler:
diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi
index 73e3a92fd0e2..e573d04dba05 100644
--- a/mypy/typeshed/stdlib/zipfile/__init__.pyi
+++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi
@@ -161,7 +161,7 @@ class ZipFile:
         def __init__(
             self,
             file: StrPath | _ZipWritable,
-            mode: Literal["w", "x"] = ...,
+            mode: Literal["w", "x"],
             compression: int = 0,
             allowZip64: bool = True,
             compresslevel: int | None = None,
@@ -173,7 +173,7 @@ class ZipFile:
         def __init__(
             self,
             file: StrPath | _ZipReadableTellable,
-            mode: Literal["a"] = ...,
+            mode: Literal["a"],
             compression: int = 0,
             allowZip64: bool = True,
             compresslevel: int | None = None,
@@ -208,7 +208,7 @@ class ZipFile:
         def __init__(
             self,
             file: StrPath | _ZipWritable,
-            mode: Literal["w", "x"] = ...,
+            mode: Literal["w", "x"],
             compression: int = 0,
             allowZip64: bool = True,
             compresslevel: int | None = None,
@@ -219,7 +219,7 @@ class ZipFile:
         def __init__(
             self,
             file: StrPath | _ZipReadableTellable,
-            mode: Literal["a"] = ...,
+            mode: Literal["a"],
             compression: int = 0,
             allowZip64: bool = True,
             compresslevel: int | None = None,
@@ -272,6 +272,29 @@ class PyZipFile(ZipFile):
     def writepy(self, pathname: str, basename: str = "", filterfunc: Callable[[str], bool] | None = None) -> None: ...
 
 class ZipInfo:
+    __slots__ = (
+        "orig_filename",
+        "filename",
+        "date_time",
+        "compress_type",
+        "compress_level",
+        "comment",
+        "extra",
+        "create_system",
+        "create_version",
+        "extract_version",
+        "reserved",
+        "flag_bits",
+        "volume",
+        "internal_attr",
+        "external_attr",
+        "header_offset",
+        "CRC",
+        "compress_size",
+        "file_size",
+        "_raw_time",
+        "_end_offset",
+    )
     filename: str
     date_time: _DateTuple
     compress_type: int
diff --git a/mypy/typeshed/stdlib/zipfile/_path/glob.pyi b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi
index f25ae71725c0..f6a661be8cdf 100644
--- a/mypy/typeshed/stdlib/zipfile/_path/glob.pyi
+++ b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi
@@ -4,7 +4,11 @@ from re import Match
 
 if sys.version_info >= (3, 13):
     class Translator:
-        def __init__(self, seps: str = ...) -> None: ...
+        if sys.platform == "win32":
+            def __init__(self, seps: str = "\\/") -> None: ...
+        else:
+            def __init__(self, seps: str = "/") -> None: ...
+
         def translate(self, pattern: str) -> str: ...
         def extend(self, pattern: str) -> str: ...
         def match_dirs(self, pattern: str) -> str: ...
diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
index e9f54fbf2a26..b7433f835f83 100644
--- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
+++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi
@@ -1,7 +1,7 @@
 import sys
 from collections.abc import Iterable
 from datetime import datetime, timedelta, tzinfo
-from typing_extensions import Self
+from typing_extensions import Self, disjoint_base
 from zoneinfo._common import ZoneInfoNotFoundError as ZoneInfoNotFoundError, _IOBytes
 from zoneinfo._tzpath import (
     TZPATH as TZPATH,
@@ -12,6 +12,7 @@ from zoneinfo._tzpath import (
 
 __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"]
 
+@disjoint_base
 class ZoneInfo(tzinfo):
     @property
     def key(self) -> str: ...
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 2910e59b9173..93b67bfa813a 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1479,9 +1479,9 @@ y: str
 if isinstance(x, int):
     reveal_type(x)
 [out]
-_testIsInstanceAdHocIntersectionWithStrAndBytes.py:3: error: Subclass of "str" and "bytes" cannot exist: would have incompatible method signatures
+_testIsInstanceAdHocIntersectionWithStrAndBytes.py:3: error: Subclass of "str" and "bytes" cannot exist: have distinct disjoint bases
 _testIsInstanceAdHocIntersectionWithStrAndBytes.py:4: error: Statement is unreachable
-_testIsInstanceAdHocIntersectionWithStrAndBytes.py:6: error: Subclass of "str" and "int" cannot exist: would have incompatible method signatures
+_testIsInstanceAdHocIntersectionWithStrAndBytes.py:6: error: Subclass of "str" and "int" cannot exist: have distinct disjoint bases
 _testIsInstanceAdHocIntersectionWithStrAndBytes.py:7: error: Statement is unreachable
 
 [case testAsyncioFutureWait]

From 0afa33dcdffa770de5fadb73e8210f0d5724611c Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 1 Sep 2025 14:25:20 +0100
Subject: [PATCH 1606/1617] Mypy micro-optimizations (batch 3/3) (#19770)

Several mypy micro-optimizations. Together with batches 1 and 2 these
improve self check performance by 1.8%.

This mostly avoids some allocations of nested function objects.
---
 mypy/meet.py | 98 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 55 insertions(+), 43 deletions(-)

diff --git a/mypy/meet.py b/mypy/meet.py
index 349c15e668c3..353af59367ad 100644
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -306,6 +306,19 @@ def is_none_object_overlap(t1: ProperType, t2: ProperType) -> bool:
     )
 
 
+def are_related_types(
+    left: Type, right: Type, *, proper_subtype: bool, ignore_promotions: bool
+) -> bool:
+    if proper_subtype:
+        return is_proper_subtype(
+            left, right, ignore_promotions=ignore_promotions
+        ) or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)
+    else:
+        return is_subtype(left, right, ignore_promotions=ignore_promotions) or is_subtype(
+            right, left, ignore_promotions=ignore_promotions
+        )
+
+
 def is_overlapping_types(
     left: Type,
     right: Type,
@@ -329,27 +342,13 @@ def is_overlapping_types(
 
     if seen_types is None:
         seen_types = set()
-    if (left, right) in seen_types:
+    elif (left, right) in seen_types:
         return True
     if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType):
         seen_types.add((left, right))
 
     left, right = get_proper_types((left, right))
 
-    def _is_overlapping_types(left: Type, right: Type) -> bool:
-        """Encode the kind of overlapping check to perform.
-
-        This function mostly exists, so we don't have to repeat keyword arguments everywhere.
-        """
-        return is_overlapping_types(
-            left,
-            right,
-            ignore_promotions=ignore_promotions,
-            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
-            overlap_for_overloads=overlap_for_overloads,
-            seen_types=seen_types.copy(),
-        )
-
     # We should never encounter this type.
     if isinstance(left, PartialType) or isinstance(right, PartialType):
         assert False, "Unexpectedly encountered partial type"
@@ -399,13 +398,9 @@ def _is_overlapping_types(left: Type, right: Type) -> bool:
         if is_none_object_overlap(left, right) or is_none_object_overlap(right, left):
             return False
 
-    def _is_subtype(left: Type, right: Type) -> bool:
-        if overlap_for_overloads:
-            return is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
-        else:
-            return is_subtype(left, right, ignore_promotions=ignore_promotions)
-
-    if _is_subtype(left, right) or _is_subtype(right, left):
+    if are_related_types(
+        left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions
+    ):
         return True
 
     # See the docstring for 'get_possible_variants' for more info on what the
@@ -428,6 +423,20 @@ def _is_subtype(left: Type, right: Type) -> bool:
         if is_none_typevarlike_overlap(left, right) or is_none_typevarlike_overlap(right, left):
             return False
 
+    def _is_overlapping_types(left: Type, right: Type) -> bool:
+        """Encode the kind of overlapping check to perform.
+
+        This function mostly exists, so we don't have to repeat keyword arguments everywhere.
+        """
+        return is_overlapping_types(
+            left,
+            right,
+            ignore_promotions=ignore_promotions,
+            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
+            overlap_for_overloads=overlap_for_overloads,
+            seen_types=seen_types.copy(),
+        )
+
     if (
         len(left_possible) > 1
         or len(right_possible) > 1
@@ -483,27 +492,28 @@ def _is_subtype(left: Type, right: Type) -> bool:
     if isinstance(left, TypeType) and isinstance(right, TypeType):
         return _is_overlapping_types(left.item, right.item)
 
-    def _type_object_overlap(left: Type, right: Type) -> bool:
-        """Special cases for type object types overlaps."""
-        # TODO: these checks are a bit in gray area, adjust if they cause problems.
-        left, right = get_proper_types((left, right))
-        # 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object.
-        if isinstance(left, TypeType) and isinstance(right, CallableType):
-            return _is_overlapping_types(left.item, right.ret_type)
-        # 2. Type[C] vs Meta, where Meta is a metaclass for C.
-        if isinstance(left, TypeType) and isinstance(right, Instance):
-            if isinstance(left.item, Instance):
-                left_meta = left.item.type.metaclass_type
-                if left_meta is not None:
-                    return _is_overlapping_types(left_meta, right)
-                # builtins.type (default metaclass) overlaps with all metaclasses
-                return right.type.has_base("builtins.type")
-            elif isinstance(left.item, AnyType):
-                return right.type.has_base("builtins.type")
-        # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
-        return False
-
     if isinstance(left, TypeType) or isinstance(right, TypeType):
+
+        def _type_object_overlap(left: Type, right: Type) -> bool:
+            """Special cases for type object types overlaps."""
+            # TODO: these checks are a bit in gray area, adjust if they cause problems.
+            left, right = get_proper_types((left, right))
+            # 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object.
+            if isinstance(left, TypeType) and isinstance(right, CallableType):
+                return _is_overlapping_types(left.item, right.ret_type)
+            # 2. Type[C] vs Meta, where Meta is a metaclass for C.
+            if isinstance(left, TypeType) and isinstance(right, Instance):
+                if isinstance(left.item, Instance):
+                    left_meta = left.item.type.metaclass_type
+                    if left_meta is not None:
+                        return _is_overlapping_types(left_meta, right)
+                    # builtins.type (default metaclass) overlaps with all metaclasses
+                    return right.type.has_base("builtins.type")
+                elif isinstance(left.item, AnyType):
+                    return right.type.has_base("builtins.type")
+            # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
+            return False
+
         return _type_object_overlap(left, right) or _type_object_overlap(right, left)
 
     if isinstance(left, Parameters) and isinstance(right, Parameters):
@@ -564,7 +574,9 @@ def _type_object_overlap(left: Type, right: Type) -> bool:
     if isinstance(left, Instance) and isinstance(right, Instance):
         # First we need to handle promotions and structural compatibility for instances
         # that came as fallbacks, so simply call is_subtype() to avoid code duplication.
-        if _is_subtype(left, right) or _is_subtype(right, left):
+        if are_related_types(
+            left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions
+        ):
             return True
 
         if right.type.fullname == "builtins.int" and left.type.fullname in MYPYC_NATIVE_INT_NAMES:

From acacc9eba333701efbc1beedfe87245d4743bf94 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Mon, 1 Sep 2025 14:57:59 +0100
Subject: [PATCH 1607/1617] [mypyc] Fix C function signature (#19773)

---
 mypyc/lib-rt/CPy.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h
index b4d3a0013ae7..5dec7509ac7b 100644
--- a/mypyc/lib-rt/CPy.h
+++ b/mypyc/lib-rt/CPy.h
@@ -67,7 +67,7 @@ typedef struct tuple_T4CIOO {
 // System-wide empty tuple constant
 extern PyObject * __mypyc_empty_tuple__;
 
-static inline PyObject *CPyTuple_LoadEmptyTupleConstant() {
+static inline PyObject *CPyTuple_LoadEmptyTupleConstant(void) {
 #if !CPY_3_12_FEATURES
     Py_INCREF(__mypyc_empty_tuple__);
 #endif

From 8638eb40aa7b9d3088f17ab005c538c86c752edd Mon Sep 17 00:00:00 2001
From: Joren Hammudoglu 
Date: Thu, 4 Sep 2025 19:18:10 +0200
Subject: [PATCH 1608/1617] [stubtest] temporary `--ignore-disjoint-bases` flag
 (#19740)

closes #19737
ref: https://github.com/python/mypy/issues/19737#issuecomment-3224801978

---

It's not the prettiest code, but since it will be removed once PEP 800
gets accepted (or rejected), I figured it would be best to keep it
simple, so that we can easily revert it.
---
 mypy/stubtest.py | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/mypy/stubtest.py b/mypy/stubtest.py
index 31b3fd20b002..d4f96a3d9389 100644
--- a/mypy/stubtest.py
+++ b/mypy/stubtest.py
@@ -140,6 +140,11 @@ def is_positional_only_related(self) -> bool:
         # TODO: This is hacky, use error codes or something more resilient
         return "should be positional" in self.message
 
+    def is_disjoint_base_related(self) -> bool:
+        """Whether or not the error is related to @disjoint_base."""
+        # TODO: This is hacky, use error codes or something more resilient
+        return "@disjoint_base" in self.message
+
     def get_description(self, concise: bool = False) -> str:
         """Returns a description of the error.
 
@@ -2181,6 +2186,7 @@ class _Arguments:
     concise: bool
     ignore_missing_stub: bool
     ignore_positional_only: bool
+    ignore_disjoint_bases: bool
     allowlist: list[str]
     generate_allowlist: bool
     ignore_unused_allowlist: bool
@@ -2274,6 +2280,8 @@ def warning_callback(msg: str) -> None:
                 continue
             if args.ignore_positional_only and error.is_positional_only_related():
                 continue
+            if args.ignore_disjoint_bases and error.is_disjoint_base_related():
+                continue
             if error.object_desc in allowlist:
                 allowlist[error.object_desc] = True
                 continue
@@ -2364,6 +2372,12 @@ def parse_options(args: list[str]) -> _Arguments:
         action="store_true",
         help="Ignore errors for whether an argument should or shouldn't be positional-only",
     )
+    # TODO: Remove once PEP 800 is accepted
+    parser.add_argument(
+        "--ignore-disjoint-bases",
+        action="store_true",
+        help="Disable checks for PEP 800 @disjoint_base classes",
+    )
     parser.add_argument(
         "--allowlist",
         "--whitelist",

From 5a323dd2f8927eb958835e51916402c27ec2f31f Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Fri, 5 Sep 2025 00:50:31 +0100
Subject: [PATCH 1609/1617] Make --allow-redefinition-new argument public
 (#19796)

It is time to announce this (as still experimental obviously).

(cherry picked from commit d33c147138f56a78d3fca8e2f7b61be46677b13c)
---
 mypy/main.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/main.py b/mypy/main.py
index 706d1daef680..4ca1bde73d40 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -886,7 +886,7 @@ def add_invertible_flag(
         "--allow-redefinition-new",
         default=False,
         strict_flag=False,
-        help=argparse.SUPPRESS,  # This is still very experimental
+        help="Allow more flexible variable redefinition semantics (experimental)",
         group=strictness_group,
     )
 

From a6b55f061d6a352e7d272ec5bc890650b7187380 Mon Sep 17 00:00:00 2001
From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com>
Date: Fri, 5 Sep 2025 11:15:36 -0400
Subject: [PATCH 1610/1617] feat: new mypyc primitives for weakref.proxy
 (#19217)

This PR adds 2 new weakref primitives for weakref.proxy (1 and 2 arg)

(cherry picked from commit 8f2371a565eb9c29f03922b26da8eab054fbbcf8)
---
 mypyc/primitives/weakref_ops.py      | 18 ++++++++++
 mypyc/test-data/fixtures/ir.py       |  1 +
 mypyc/test-data/irbuild-weakref.test | 52 ++++++++++++++++++++++++++++
 mypyc/test-data/run-weakref.test     | 44 +++++++++++++++++------
 test-data/unit/lib-stub/_weakref.pyi | 11 ++++++
 test-data/unit/lib-stub/weakref.pyi  | 14 +++++++-
 6 files changed, 128 insertions(+), 12 deletions(-)
 create mode 100644 test-data/unit/lib-stub/_weakref.pyi

diff --git a/mypyc/primitives/weakref_ops.py b/mypyc/primitives/weakref_ops.py
index a7ac035b22a4..21379d3b2c82 100644
--- a/mypyc/primitives/weakref_ops.py
+++ b/mypyc/primitives/weakref_ops.py
@@ -20,3 +20,21 @@
     c_function_name="PyWeakref_NewRef",
     error_kind=ERR_MAGIC,
 )
+
+new_proxy_op = function_op(
+    name="_weakref.proxy",
+    arg_types=[object_rprimitive],
+    return_type=object_rprimitive,
+    c_function_name="PyWeakref_NewProxy",
+    extra_int_constants=[(0, pointer_rprimitive)],
+    error_kind=ERR_MAGIC,
+)
+
+new_proxy_with_callback_op = function_op(
+    name="_weakref.proxy",
+    arg_types=[object_rprimitive, object_rprimitive],
+    # steals=[True, False],
+    return_type=object_rprimitive,
+    c_function_name="PyWeakref_NewProxy",
+    error_kind=ERR_MAGIC,
+)
diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py
index fb5512b77279..d3f7bc7ae4f6 100644
--- a/mypyc/test-data/fixtures/ir.py
+++ b/mypyc/test-data/fixtures/ir.py
@@ -343,6 +343,7 @@ class RuntimeError(Exception): pass
 class UnicodeEncodeError(RuntimeError): pass
 class UnicodeDecodeError(RuntimeError): pass
 class NotImplementedError(RuntimeError): pass
+class ReferenceError(Exception): pass
 
 class StopIteration(Exception):
     value: Any
diff --git a/mypyc/test-data/irbuild-weakref.test b/mypyc/test-data/irbuild-weakref.test
index 58ac6417d297..2180b1e747aa 100644
--- a/mypyc/test-data/irbuild-weakref.test
+++ b/mypyc/test-data/irbuild-weakref.test
@@ -49,3 +49,55 @@ def f(x, cb):
 L0:
     r0 = PyWeakref_NewRef(x, cb)
     return r0
+
+[case testWeakrefProxy]
+import weakref
+from typing import Any, Callable
+def f(x: object) -> object:
+    return weakref.proxy(x)
+
+[out]
+def f(x):
+    x, r0 :: object
+L0:
+    r0 = PyWeakref_NewProxy(x, 0)
+    return r0
+
+[case testWeakrefProxyCallback]
+import weakref
+from typing import Any, Callable
+def f(x: object, cb: Callable[[object], Any]) -> object:
+    return weakref.proxy(x, cb)
+
+[out]
+def f(x, cb):
+    x, cb, r0 :: object
+L0:
+    r0 = PyWeakref_NewProxy(x, cb)
+    return r0
+
+[case testFromWeakrefProxy]
+from typing import Any, Callable
+from weakref import proxy
+def f(x: object) -> object:
+    return proxy(x)
+
+[out]
+def f(x):
+    x, r0 :: object
+L0:
+    r0 = PyWeakref_NewProxy(x, 0)
+    return r0
+
+[case testFromWeakrefProxyCallback]
+from typing import Any, Callable
+from weakref import proxy
+def f(x: object, cb: Callable[[object], Any]) -> object:
+    return proxy(x, cb)
+
+[out]
+def f(x, cb):
+    x, cb, r0 :: object
+L0:
+    r0 = PyWeakref_NewProxy(x, cb)
+    return r0
diff --git a/mypyc/test-data/run-weakref.test b/mypyc/test-data/run-weakref.test
index 902c9e407ff4..0a0e180d635d 100644
--- a/mypyc/test-data/run-weakref.test
+++ b/mypyc/test-data/run-weakref.test
@@ -1,30 +1,52 @@
 # Test cases for weakrefs (compile and run)
 
 [case testWeakrefRef]
-from weakref import ref
+# mypy: disable-error-code="union-attr"
+from weakref import proxy, ref
 from mypy_extensions import mypyc_attr
+from testutil import assertRaises
+from typing import Optional
 
 @mypyc_attr(native_class=False)
 class Object:
     """some random weakreffable object"""
-    pass
+    def some_meth(self) -> int:
+        return 1
 
-def test_weakref_ref():
-    obj = Object()
+_callback_called_cache = {"ref": False, "proxy": False}
+
+def test_weakref_ref() -> None:
+    obj: Optional[Object] = Object()
     r = ref(obj)
     assert r() is obj
     obj = None
     assert r() is None, r()
 
-def test_weakref_ref_with_callback():
-    obj = Object()
-    r = ref(obj, lambda x: x)
+def test_weakref_ref_with_callback() -> None:
+    obj: Optional[Object] = Object()
+    r = ref(obj, lambda x: _callback_called_cache.__setitem__("ref", True))
     assert r() is obj
     obj = None
     assert r() is None, r()
+    assert _callback_called_cache["ref"] is True
 
-[file driver.py]
-from native import test_weakref_ref, test_weakref_ref_with_callback
+def test_weakref_proxy() -> None:
+    obj: Optional[Object] = Object()
+    p = proxy(obj)
+    assert obj.some_meth() == 1
+    assert p.some_meth() == 1
+    obj.some_meth()
+    obj = None
+    with assertRaises(ReferenceError):
+        p.some_meth()
 
-test_weakref_ref()
-test_weakref_ref_with_callback()
+def test_weakref_proxy_with_callback() -> None:
+    obj: Optional[Object] = Object()
+    p = proxy(obj, lambda x: _callback_called_cache.__setitem__("proxy", True))
+    assert obj.some_meth() == 1
+    assert p.some_meth() == 1
+    obj.some_meth()
+    obj = None
+    with assertRaises(ReferenceError):
+        p.some_meth()
+    assert _callback_called_cache["proxy"] is True
diff --git a/test-data/unit/lib-stub/_weakref.pyi b/test-data/unit/lib-stub/_weakref.pyi
new file mode 100644
index 000000000000..50c59b65e267
--- /dev/null
+++ b/test-data/unit/lib-stub/_weakref.pyi
@@ -0,0 +1,11 @@
+from typing import Any, Callable, TypeVar, overload
+from weakref import CallableProxyType, ProxyType
+
+_C = TypeVar("_C", bound=Callable[..., Any])
+_T = TypeVar("_T")
+
+# Return CallableProxyType if object is callable, ProxyType otherwise
+@overload
+def proxy(object: _C, callback: Callable[[CallableProxyType[_C]], Any] | None = None, /) -> CallableProxyType[_C]: ...
+@overload
+def proxy(object: _T, callback: Callable[[ProxyType[_T]], Any] | None = None, /) -> ProxyType[_T]: ...
diff --git a/test-data/unit/lib-stub/weakref.pyi b/test-data/unit/lib-stub/weakref.pyi
index 34e01f4d48f1..7d11b65d4548 100644
--- a/test-data/unit/lib-stub/weakref.pyi
+++ b/test-data/unit/lib-stub/weakref.pyi
@@ -1,11 +1,23 @@
+from _weakref import proxy
 from collections.abc import Callable
-from typing import Any, Generic, TypeVar
+from typing import Any, ClassVar, Generic, TypeVar, final
 from typing_extensions import Self
 
+_C = TypeVar("_C", bound=Callable[..., Any])
 _T = TypeVar("_T")
 
 class ReferenceType(Generic[_T]):  # "weakref"
     __callback__: Callable[[Self], Any]
     def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ...
+    def __call__(self) -> _T | None: ...
 
 ref = ReferenceType
+
+@final
+class CallableProxyType(Generic[_C]):  # "weakcallableproxy"
+    def __eq__(self, value: object, /) -> bool: ...
+    def __getattr__(self, attr: str) -> Any: ...
+    __call__: _C
+    __hash__: ClassVar[None]  # type: ignore[assignment]
+
+__all__ = ["proxy"]

From 2ce1bb225f3ced0ad4b2080af8a9a0eb39f8b38e Mon Sep 17 00:00:00 2001
From: Chainfire 
Date: Wed, 3 Sep 2025 17:58:12 +0200
Subject: [PATCH 1611/1617] [mypyc] Fix subclass processing in
 detect_undefined_bitmap (#19787)

Incorrect processing in detect_undefined_bitmap could cause a ValueError
exception in emit_undefined_attr_check.
---
 mypyc/analysis/attrdefined.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypyc/analysis/attrdefined.py b/mypyc/analysis/attrdefined.py
index 5be57d767e35..1dfd33630f1c 100644
--- a/mypyc/analysis/attrdefined.py
+++ b/mypyc/analysis/attrdefined.py
@@ -422,7 +422,7 @@ def detect_undefined_bitmap(cl: ClassIR, seen: set[ClassIR]) -> None:
         return
     seen.add(cl)
     for base in cl.base_mro[1:]:
-        detect_undefined_bitmap(cl, seen)
+        detect_undefined_bitmap(base, seen)
 
     if len(cl.base_mro) > 1:
         cl.bitmap_attrs.extend(cl.base_mro[1].bitmap_attrs)

From 0ca1f2a7e50ed38eed95ecf8c1f2df1057da902d Mon Sep 17 00:00:00 2001
From: Ivan Levkivskyi 
Date: Tue, 9 Sep 2025 21:10:52 +0100
Subject: [PATCH 1612/1617] Expose --fixed-format-cache if compiled (#19815)

(cherry picked from commit fd0526545419028090f064ba4c1fa6e576ccdd6b)
---
 mypy/main.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/mypy/main.py b/mypy/main.py
index 4ca1bde73d40..d5bbca704305 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -1064,7 +1064,13 @@ def add_invertible_flag(
         help="Include fine-grained dependency information in the cache for the mypy daemon",
     )
     incremental_group.add_argument(
-        "--fixed-format-cache", action="store_true", help=argparse.SUPPRESS
+        "--fixed-format-cache",
+        action="store_true",
+        help=(
+            "Use experimental fast and compact fixed format cache"
+            if compilation_status == "yes"
+            else argparse.SUPPRESS
+        ),
     )
     incremental_group.add_argument(
         "--skip-version-check",

From 24eed0bd0bb1107ede163ed314082a0f3e7dbbc4 Mon Sep 17 00:00:00 2001
From: Kevin Kannammalil 
Date: Wed, 10 Sep 2025 13:04:02 -0400
Subject: [PATCH 1613/1617] Initial changelog for release 1.18 (#19818)

(cherry picked from commit 9ae3e9aa160c11b99960f12eef111e4a3197b7d3)
---
 CHANGELOG.md | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 239 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bdb888ff9d6..76643a0b805c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,245 @@
 
 ## Next Release
 
+## Mypy 1.18 (Unreleased)
+
+We’ve just uploaded mypy 1.18 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
+Mypy is a static type checker for Python. This release includes new features and bug fixes.
+You can install it as follows:
+
+    python3 -m pip install -U mypy
+
+You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io).
+
+### `--allow-redefinition-new`
+
+TODO by Jukka
+
+This feature was contributed by Jukka Lehtosalo.
+
+### Fixed‑Format Cache (experimental)
+
+TODO by Jukka
+
+This feature was contributed by Ivan Levkivskyi (PR [19668](https://github.com/python/mypy/pull/19668), [19735](https://github.com/python/mypy/pull/19735), [19750](https://github.com/python/mypy/pull/19750), [19681](https://github.com/python/mypy/pull/19681), [19752](https://github.com/python/mypy/pull/19752), [19815](https://github.com/python/mypy/pull/19815))
+
+### Disjoint Base Classes (@disjoint_base, PEP 800)
+
+Mypy now implements PEP 800 Disjoint bases: it understands the @disjoint_base marker, rejects class definitions that combine incompatible disjoint bases, and exploits the fact that such classes cannot exist in reachability and narrowing logic
+
+This feature was contributed by Jelle Zijlstra (PR [19678](https://github.com/python/mypy/pull/19678)).
+
+### Mypy Performance Improvements
+
+Mypy 1.18 includes numerous performance improvements, resulting in a 38% overall speedup compared to 1.17
+
+- Improve self check performance by 1.8% (Jukka Lehtosalo, PR [19768](https://github.com/python/mypy/pull/19768), [19769](https://github.com/python/mypy/pull/19769), [19770](https://github.com/python/mypy/pull/19770))
+- Use fast Python wrappers in native_internal (Ivan Levkivskyi, PR [19765](https://github.com/python/mypy/pull/19765))
+- Use macros in native_internal hot paths (Ivan Levkivskyi, PR [19757](https://github.com/python/mypy/pull/19757))
+- Special‑case certain Enum method calls for speed (Ivan Levkivskyi, PR [19634](https://github.com/python/mypy/pull/19634))
+- Two additional micro‑optimizations (Ivan Levkivskyi, PR [19627](https://github.com/python/mypy/pull/19627))
+- Another set of micro‑optimizations (Ivan Levkivskyi, PR [19633](https://github.com/python/mypy/pull/19633))
+- Cache common instances (Ivan Levkivskyi, PR [19621](https://github.com/python/mypy/pull/19621))
+- Skip more method bodies in third‑party libraries for speed (Ivan Levkivskyi, PR [19586](https://github.com/python/mypy/pull/19586))
+- Avoid using a dict in CallableType (Ivan Levkivskyi, PR [19580](https://github.com/python/mypy/pull/19580))
+- Use cache for DictExpr (Ivan Levkivskyi, PR [19536](https://github.com/python/mypy/pull/19536))
+- Use cache for OpExpr (Ivan Levkivskyi, PR [19523](https://github.com/python/mypy/pull/19523))
+- Simple call‑expression cache (Ivan Levkivskyi, PR [19505](https://github.com/python/mypy/pull/19505))
+- Cache type_object_type() (Ivan Levkivskyi, PR [19514](https://github.com/python/mypy/pull/19514))
+- Avoid duplicate visits in boolean‑op checking (Ivan Levkivskyi, PR [19515](https://github.com/python/mypy/pull/19515))
+- Optimize generic inference passes (Ivan Levkivskyi, PR [19501](https://github.com/python/mypy/pull/19501))
+- Speed up default plugin (Jukka Lehtosalo, PR [19462](https://github.com/python/mypy/pull/19462))
+- Micro‑optimize ExpandTypeVisitor (Jukka Lehtosalo, PR [19461](https://github.com/python/mypy/pull/19461))
+- Micro‑optimize type indirection visitor (Jukka Lehtosalo, PR [19460](https://github.com/python/mypy/pull/19460))
+- Micro‑optimize chained plugin (Jukka Lehtosalo, PR [19464](https://github.com/python/mypy/pull/19464))
+- Avoid temporary set creation in is_proper_subtype (Jukka Lehtosalo, PR [19463](https://github.com/python/mypy/pull/19463))
+- Subtype checking micro‑optimization (Jukka Lehtosalo, PR [19384](https://github.com/python/mypy/pull/19384))
+- Speed up default plugin (earlier pass) (Jukka Lehtosalo, PR [19385](https://github.com/python/mypy/pull/19385))
+- Remove nested imports from default plugin (Ivan Levkivskyi, PR [19388](https://github.com/python/mypy/pull/19388))
+- is_subtype: return early where possible (Stanislav Terliakov, PR [19400](https://github.com/python/mypy/pull/19400))
+- Deduplicate fast_container_type / fast_dict_type items before join (Stanislav Terliakov, PR [19409](https://github.com/python/mypy/pull/19409))
+- Speed up type checking by caching argument inference context (Jukka Lehtosalo, PR [19323](https://github.com/python/mypy/pull/19323))
+- Optimize bind_self() and deprecation checks (Ivan Levkivskyi, PR [19556](https://github.com/python/mypy/pull/19556))
+- Keep trivial instances/aliases during expansion (Ivan Levkivskyi, PR [19543](https://github.com/python/mypy/pull/19543))
+
+### Stubtest Improvements
+- Add temporary --ignore-disjoint-bases flag to ease PEP 800 migration (Joren Hammudoglu, PR [19740](https://github.com/python/mypy/pull/19740))
+- Flag redundant uses of @disjoint_base (Jelle Zijlstra, PR [19715](https://github.com/python/mypy/pull/19715))
+- Improve signatures for `__init__` of C classes (Stephen Morton, PR [18259](https://github.com/python/mypy/pull/18259))
+- Handle overloads with mixed pos‑only parameters (Stephen Morton, PR [18287](https://github.com/python/mypy/pull/18287))
+- Use “parameter” (not “argument”) in error messages (PrinceNaroliya, PR [19707](https://github.com/python/mypy/pull/19707))
+- Don’t require @disjoint_base when `__slots__` imply finality (Jelle Zijlstra, PR [19701](https://github.com/python/mypy/pull/19701))
+- Allow runtime‑existing aliases of @type_check_only types (Brian Schubert, PR [19568](https://github.com/python/mypy/pull/19568))
+- More detailed checking of type objects in stubtest (Stephen Morton, PR [18251](https://github.com/python/mypy/pull/18251))
+- Support running stubtest in non-UTF8 terminals (Stanislav Terliakovm, PR [19085](https://github.com/python/mypy/pull/19085))
+
+### Mypyc Improvements
+
+- Fix subclass processing in detect_undefined_bitmap (Chainfire, PR [19787](https://github.com/python/mypy/pull/19787))
+- Fix C function signature emission (Jukka Lehtosalo, PR [19773](https://github.com/python/mypy/pull/19773))
+- Use defined `__new__` in tp_new and constructor (Piotr Sawicki, PR [19739](https://github.com/python/mypy/pull/19739))
+- Speed up implicit `__ne__` (Jukka Lehtosalo, PR [19759](https://github.com/python/mypy/pull/19759))
+- Speed up equality with optional str/bytes (Jukka Lehtosalo, PR [19758](https://github.com/python/mypy/pull/19758))
+- Add `__mypyc_empty_tuple__` constant (BobTheBuidler, PR [19654](https://github.com/python/mypy/pull/19654))
+- Add PyObject_CallObject fast‑path op for fn(*args) (BobTheBuidler, PR [19631](https://github.com/python/mypy/pull/19631))
+- Add **kwargs star2 fast‑path (follow‑up to starargs) (BobTheBuidler, PR [19630](https://github.com/python/mypy/pull/19630))
+- Optimize type(x), x.`__class__`, and `.__name__` (Jukka Lehtosalo, PR [19691](https://github.com/python/mypy/pull/19691), [19683](https://github.com/python/mypy/pull/19683))
+- Specialize bytes.decode for common encodings (Jukka Lehtosalo, PR [19688](https://github.com/python/mypy/pull/19688))
+- Speed up in against final fixed‑length tuples (Jukka Lehtosalo, PR [19682](https://github.com/python/mypy/pull/19682))
+- Optimize f‑string building from Final values (BobTheBuidler, PR [19611](https://github.com/python/mypy/pull/19611))
+- Add exact_dict_set_item_op (BobTheBuidler, PR [19657](https://github.com/python/mypy/pull/19657))
+- Cache len() when iterating over immutable types (BobTheBuidler, PR [19656](https://github.com/python/mypy/pull/19656))
+- Add stararg fast‑path for tuple calls fn(*args) (BobTheBuidler, PR [19623](https://github.com/python/mypy/pull/19623))
+- Include more operations in the mypyc trace log (Jukka Lehtosalo, PR [19647](https://github.com/python/mypy/pull/19647))
+- Add prefix to attributes of generator classes (Piotr Sawicki, PR [19535](https://github.com/python/mypy/pull/19535))
+- Fix segfault from heap type objects with static tp_doc (Brian Schubert, PR [19636](https://github.com/python/mypy/pull/19636))
+- Unwrap NewType to its base type for optimized paths (BobTheBuidler, PR [19497](https://github.com/python/mypy/pull/19497))
+- Enable free‑threading when compiling multiple modules (Jukka Lehtosalo, PR [19541](https://github.com/python/mypy/pull/19541))
+- Make type objects immortal under free‑threading (Jukka Lehtosalo, PR [19538](https://github.com/python/mypy/pull/19538))
+- Fix list.pop primitive on free‑threaded builds (Jukka Lehtosalo, PR [19522](https://github.com/python/mypy/pull/19522))
+- Generate an export table only for separate compilation (Jukka Lehtosalo, PR [19521](https://github.com/python/mypy/pull/19521))
+- Add primitives for isinstance of built‑in types (Piotr Sawicki, PR [19435](https://github.com/python/mypy/pull/19435))
+- Add SetElement op to initialize struct values (Jukka Lehtosalo, PR [19437](https://github.com/python/mypy/pull/19437))
+- Simplify IR for for loops over strings (Jukka Lehtosalo, PR [19434](https://github.com/python/mypy/pull/19434))
+- Use native integers for some sequence indexing (Jukka Lehtosalo, PR [19426](https://github.com/python/mypy/pull/19426))
+- Remove unused CPyList_GetItemUnsafe primitive (Jukka Lehtosalo, PR [19424](https://github.com/python/mypy/pull/19424))
+- Add native‑int helper methods in IR builder (Jukka Lehtosalo, PR [19423](https://github.com/python/mypy/pull/19423))
+- Use PyList_Check for isinstance(obj, list) (Piotr Sawicki, PR [19416](https://github.com/python/mypy/pull/19416))
+- Speed up for loops over native generators (Jukka Lehtosalo, PR [19415](https://github.com/python/mypy/pull/19415))
+- Report error on reserved method names (Piotr Sawicki, PR [19407](https://github.com/python/mypy/pull/19407))
+- Add is_bool_or_bit_rprimitive (Piotr Sawicki, PR [19406](https://github.com/python/mypy/pull/19406))
+- Faster string equality primitive (Jukka Lehtosalo, PR [19402](https://github.com/python/mypy/pull/19402))
+- Speed up native‑to‑native calls using await (Jukka Lehtosalo, PR [19398](https://github.com/python/mypy/pull/19398))
+- Raise NameError on undefined names (Piotr Sawicki, PR [19395](https://github.com/python/mypy/pull/19395))
+- Simplify comparison of tuple elements (Piotr Sawicki, PR [19396](https://github.com/python/mypy/pull/19396))
+- Use per‑type freelists for nested functions (Jukka Lehtosalo, PR [19390](https://github.com/python/mypy/pull/19390))
+- Call generator helper directly in await expressions (Jukka Lehtosalo, PR [19376](https://github.com/python/mypy/pull/19376))
+- Generate introspection signatures for compiled functions (Brian Schubert, PR [19307](https://github.com/python/mypy/pull/19307))
+- Support C string literals in IR (Jukka Lehtosalo, PR [19383](https://github.com/python/mypy/pull/19383))
+- Fix error‑value check for GetAttr that allows nullable values (Jukka Lehtosalo, PR [19378](https://github.com/python/mypy/pull/19378))
+- Fix comparison of tuples with different lengths (Piotr Sawicki, PR [19372](https://github.com/python/mypy/pull/19372))
+- Speed up generator allocation with per‑type freelists (Jukka Lehtosalo, PR [19316](https://github.com/python/mypy/pull/19316))
+- Implement list.clear() primitive (Jahongir Qurbonov, PR [19344](https://github.com/python/mypy/pull/19344))
+- New primitives for weakref.proxy (BobTheBuidler, PR [19217](https://github.com/python/mypy/pull/19217))
+- New primitive for weakref.ref (BobTheBuidler, PR [19099](https://github.com/python/mypy/pull/19099))
+- New primitive for str.count (BobTheBuidler, PR [19264](https://github.com/python/mypy/pull/19264))
+- Tracing/tooling: optionally log sampled operation traces (Jukka Lehtosalo, PR [19457](https://github.com/python/mypy/pull/19457))
+- Tracing/tooling: script to compile with trace logging and run mypy (Jukka Lehtosalo, PR [19475](https://github.com/python/mypy/pull/19475))
+
+
+### Documentation Updates
+
+- Add idlemypyextension to IDE integrations (CoolCat467, PR [18615](https://github.com/python/mypy/pull/18615))
+- Document that object is often preferable to Any in APIs (wyattscarpenter, PR [19103](https://github.com/python/mypy/pull/19103))
+- Include a detailed listing of flags enabled by --strict (wyattscarpenter, PR [19062](https://github.com/python/mypy/pull/19062))
+- Update “common issues” (reveal_type/reveal_locals; note on orjson) (wyattscarpenter, PR [19059](https://github.com/python/mypy/pull/19059), [19058](https://github.com/python/mypy/pull/19058))
+
+### Other Notable Improvements
+
+- Remove deprecated --new-type-inference flag (the new algorithm has long been default) (Ivan Levkivskyi, PR [19570](https://github.com/python/mypy/pull/19570))
+- Use empty context as a fallback for return expressions when outer context misleads inference (Ivan Levkivskyi, PR [19767](https://github.com/python/mypy/pull/19767))
+- Support --strict-equality checks involving None (Christoph Tyralla, PR [19718](https://github.com/python/mypy/pull/19718))
+- Don’t show import‑related errors after a module‑level assert False (Stanislav Terliakov, PR [19347](https://github.com/python/mypy/pull/19347))
+- Fix forward refs in type parameters of over‑parameterized PEP 695 aliases (Brian Schubert, PR [19725](https://github.com/python/mypy/pull/19725))
+- Don’t expand PEP 695 aliases when checking node fullnames (Brian Schubert, PR [19699](https://github.com/python/mypy/pull/19699))
+- Don’t use outer context for or expression inference when LHS is Any (Stanislav Terliakov, PR [19748](https://github.com/python/mypy/pull/19748))
+- Interpret bare ClassVar as inferred (not Any) (Ivan Levkivskyi, PR [19573](https://github.com/python/mypy/pull/19573))
+- Recognize buffer protocol special methods (Brian Schubert, PR [19581](https://github.com/python/mypy/pull/19581))
+- Add temporary named expressions for match subjects (Stanislav Terliakov, PR [18446](https://github.com/python/mypy/pull/18446))
+- Support attribute access on enum members correctly (Stanislav Terliakov, PR [19422](https://github.com/python/mypy/pull/19422))
+- Check `__slots__` assignments on self types (Stanislav Terliakov, PR [19332](https://github.com/python/mypy/pull/19332))
+- Move self‑argument checks after decorator application (Stanislav Terliakov, PR [19490](https://github.com/python/mypy/pull/19490))
+- Infer empty list for `__slots__` and module `__all__` (Stanislav Terliakov, PR [19348](https://github.com/python/mypy/pull/19348))
+- Use normalized tuples for fallback calculation (Stanislav Terliakov, PR [19111](https://github.com/python/mypy/pull/19111))
+- Preserve literals when joining Literal with Instance that has matching last_known_value (Stanislav Terliakov, PR [19279](https://github.com/python/mypy/pull/19279))
+- Allow adjacent conditionally‑defined overloads (Stanislav Terliakov, PR [19042](https://github.com/python/mypy/pull/19042))
+- Check property decorators more strictly (Stanislav Terliakov, PR [19313](https://github.com/python/mypy/pull/19313))
+- Support properties with generic setters (Ivan Levkivskyi, PR [19298](https://github.com/python/mypy/pull/19298))
+- Generalize class/static method and property alias support (Ivan Levkivskyi, PR [19297](https://github.com/python/mypy/pull/19297))
+- Re‑widen custom properties after narrowing (Ivan Levkivskyi, PR [19296](https://github.com/python/mypy/pull/19296))
+- Avoid erasing type objects when checking runtime cover (Shantanu, PR [19320](https://github.com/python/mypy/pull/19320))
+- Include tuple fallback in constraints built from tuple types (Stanislav Terliakov, PR [19100](https://github.com/python/mypy/pull/19100))
+- Somewhat better isinstance support on old‑style unions (Shantanu, PR [19714](https://github.com/python/mypy/pull/19714))
+- Improve promotions inside unions (Christoph Tyralla, PR [19245](https://github.com/python/mypy/pull/19245))
+- Uninhabited types should have all attributes (Ivan Levkivskyi, PR [19300](https://github.com/python/mypy/pull/19300))
+- Metaclass conflict checks improved (Robsdedude, PR [17682](https://github.com/python/mypy/pull/17682))
+- Metaclass resolution algorithm fixes (Robsdedude, PR [17713](https://github.com/python/mypy/pull/17713))
+- PEP 702 @deprecated: handle “combined” overloads (Christoph Tyralla, PR [19626](https://github.com/python/mypy/pull/19626))
+- PEP 702 @deprecated: include overloads in snapshot descriptions (Christoph Tyralla, PR [19613](https://github.com/python/mypy/pull/19613))
+- Ignore overload implementation when checking `__OP__` / `__rOP__` compatibility (Stanislav Terliakov, PR [18502](https://github.com/python/mypy/pull/18502))
+- Fix unwrapping of assignment expressions in match subject (Marc Mueller, PR [19742](https://github.com/python/mypy/pull/19742))
+- Omit errors for class patterns against object (Marc Mueller, PR [19709](https://github.com/python/mypy/pull/19709))
+- Remove unnecessary error for certain match class patterns (Marc Mueller, PR [19708](https://github.com/python/mypy/pull/19708))
+- Use union type for captured vars in or pattern (Marc Mueller, PR [19710](https://github.com/python/mypy/pull/19710))
+- Prevent final reassignment inside match case (Omer Hadari, PR [19496](https://github.com/python/mypy/pull/19496))
+- Support _value_ as a fallback for ellipsis Enum members (Stanislav Terliakov, PR [19352](https://github.com/python/mypy/pull/19352))
+- Sort arguments in TypedDict overlap messages (Marc Mueller, PR [19666](https://github.com/python/mypy/pull/19666))
+- Reset to previous statement on leaving return in semanal (Stanislav Terliakov, PR [19642](https://github.com/python/mypy/pull/19642))
+- Add ambiguous to UninhabitedType identity for better messaging (Stanislav Terliakov, PR [19648](https://github.com/python/mypy/pull/19648))
+- Further fix overload diagnostics for varargs/kwargs (Shantanu, PR [19619](https://github.com/python/mypy/pull/19619))
+- Fix overload diagnostics when vararg and varkwarg both match (Shantanu, PR [19614](https://github.com/python/mypy/pull/19614))
+- Show type variable name in “Cannot infer type argument” (Brian Schubert, PR [19290](https://github.com/python/mypy/pull/19290))
+- Fail gracefully on unsupported template strings (PEP 750) (Brian Schubert, PR [19700](https://github.com/python/mypy/pull/19700))
+- Revert colored argparse help for Python 3.14 (Marc Mueller, PR [19721](https://github.com/python/mypy/pull/19721))
+- Support type‑checking a code fragment in the profile script (Jukka Lehtosalo, PR [19379](https://github.com/python/mypy/pull/19379))
+- Fix C compiler flags in the profile self‑check script (Jukka Lehtosalo, PR [19326](https://github.com/python/mypy/pull/19326))
+- Add a script for profiling self‑check (Linux only) (Jukka Lehtosalo, PR [19322](https://github.com/python/mypy/pull/19322))
+- Retry PyPI upload script: skip existing files on retry (Jukka Lehtosalo, PR [19305](https://github.com/python/mypy/pull/19305))
+- Update stubinfo for latest typeshed (Shantanu, PR [19771](https://github.com/python/mypy/pull/19771))
+- Fix crash with variadic tuple arguments to a generic type (Randolf Scholz, PR [19705](https://github.com/python/mypy/pull/19705))
+- Fix crash when enable_error_code in pyproject.toml has wrong type (wyattscarpenter, PR [19494](https://github.com/python/mypy/pull/19494))
+- Fix dict assignment to a wider context when an incompatible same‑shape TypedDict exists (Stanislav Terliakov, PR [19592](https://github.com/python/mypy/pull/19592))
+- Prevent crash for dataclass with PEP 695 TypeVarTuple on Python 3.13+ (Stanislav Terliakov, PR [19565](https://github.com/python/mypy/pull/19565))
+- Fix constructor type for subclasses of Any (Ivan Levkivskyi, PR [19295](https://github.com/python/mypy/pull/19295))
+- Fix TypeGuard/TypeIs being forgotten when semanal defers (Brian Schubert, PR [19325](https://github.com/python/mypy/pull/19325))
+- Fix TypeIs negative narrowing for unions of generics (Brian Schubert, PR [18193](https://github.com/python/mypy/pull/18193))
+- dmypy suggest: fix incorrect signature suggestion when a type matches a module name (Brian Schubert, PR [18937](https://github.com/python/mypy/pull/18937))
+- dmypy suggest: fix interaction with `__new__` (Stanislav Terliakov, PR [18966](https://github.com/python/mypy/pull/18966))
+- dmypy suggest: support Callable / callable Protocols in decorator unwrapping (Anthony Sottile, PR [19072](https://github.com/python/mypy/pull/19072))
+- Fix missing error when redeclaring a type variable in a nested generic class (Brian Schubert, PR [18883](https://github.com/python/mypy/pull/18883))
+- Fix for overloaded type object erasure (Shantanu, PR [19338](https://github.com/python/mypy/pull/19338))
+- Fix TypeGuard with call on temporary object (Saul Shanabrook, PR [19577](https://github.com/python/mypy/pull/19577))
+- Fix crash on settable property alias (Ivan Levkivskyi, PR [19615](https://github.com/python/mypy/pull/19615))
+
+### Typeshed Updates
+
+Please see [git log](https://github.com/python/typeshed/commits/main?after=2480d7e7c74493a024eaf254c5d2c6f452c80ee2+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes.
+
+### Acknowledgements
+
+Thanks to all mypy contributors who contributed to this release:
+
+- Ali Hamdan
+- Anthony Sottile
+- BobTheBuidler
+- Brian Schubert
+- Chainfire
+- Charlie Denton
+- Christoph Tyralla
+- CoolCat467
+- Daniel Hnyk
+- Emily
+- Emma Smith
+- Ethan Sarp
+- Ivan Levkivskyi
+- Jahongir Qurbonov
+- Jelle Zijlstra
+- Joren Hammudoglu
+- Jukka Lehtosalo
+- Marc Mueller
+- Omer Hadari
+- Piotr Sawicki
+- PrinceNaroliya
+- Randolf Scholz
+- Robsdedude
+- Saul Shanabrook
+- Shantanu
+- Stanislav Terliakov
+- Stephen Morton
+- wyattscarpenter
+
+I’d also like to thank my employer, Dropbox, for supporting mypy development.
+
 ## Mypy 1.17
 
 We’ve just uploaded mypy 1.17 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).

From ee61cec0d654463874ddee3a60914f0a6cd08222 Mon Sep 17 00:00:00 2001
From: Jukka Lehtosalo 
Date: Thu, 11 Sep 2025 14:03:58 +0100
Subject: [PATCH 1614/1617] Updates to 1.18 changelog (#19826)

Did various edits, added a few additional sections, and reordered some
sections.

(cherry picked from commit 73fa69ed3d7fe0f80d74874ec0d9c738e8674bd1)
---
 CHANGELOG.md | 335 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 206 insertions(+), 129 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76643a0b805c..5266a86c725e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,154 +5,244 @@
 ## Mypy 1.18 (Unreleased)
 
 We’ve just uploaded mypy 1.18 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
-Mypy is a static type checker for Python. This release includes new features and bug fixes.
-You can install it as follows:
+Mypy is a static type checker for Python. This release includes new features, performance
+improvements and bug fixes. You can install it as follows:
 
     python3 -m pip install -U mypy
 
 You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io).
 
-### `--allow-redefinition-new`
+### Mypy Performance Improvements
+
+Mypy 1.18 includes numerous performance improvements, resulting in about 40% speedup
+compared to 1.17 when type checking mypy itself. In extreme cases, the improvement
+can be 10x or higher. The list below is an overview of the various mypy optimizations.
+Many mypyc improvements (discussed in a separate section below) also improve performance.
 
-TODO by Jukka
+Type caching optimizations have a small risk of causing regressions. When
+reporting issues with unexpected inferred types, please also check if
+`--disable-expression-cache` will work around the issue, as it turns off some of
+these optimizations.
+
+- Improve self check performance by 1.8% (Jukka Lehtosalo, PR [19768](https://github.com/python/mypy/pull/19768), [19769](https://github.com/python/mypy/pull/19769), [19770](https://github.com/python/mypy/pull/19770))
+- Optimize fixed-format deserialization (Ivan Levkivskyi, PR [19765](https://github.com/python/mypy/pull/19765))
+- Use macros to optimize fixed-format deserialization (Ivan Levkivskyi, PR [19757](https://github.com/python/mypy/pull/19757))
+- Two additional micro‑optimizations (Ivan Levkivskyi, PR [19627](https://github.com/python/mypy/pull/19627))
+- Another set of micro‑optimizations (Ivan Levkivskyi, PR [19633](https://github.com/python/mypy/pull/19633))
+- Cache common types (Ivan Levkivskyi, PR [19621](https://github.com/python/mypy/pull/19621))
+- Skip more method bodies in third‑party libraries for speed (Ivan Levkivskyi, PR [19586](https://github.com/python/mypy/pull/19586))
+- Simplify the representation of callable types (Ivan Levkivskyi, PR [19580](https://github.com/python/mypy/pull/19580))
+- Add cache for types of some expressions (Ivan Levkivskyi, PR [19505](https://github.com/python/mypy/pull/19505))
+- Use cache for dictionary expressions (Ivan Levkivskyi, PR [19536](https://github.com/python/mypy/pull/19536))
+- Use cache for binary operations (Ivan Levkivskyi, PR [19523](https://github.com/python/mypy/pull/19523))
+- Cache types of type objects (Ivan Levkivskyi, PR [19514](https://github.com/python/mypy/pull/19514))
+- Avoid duplicate work when checking boolean operations (Ivan Levkivskyi, PR [19515](https://github.com/python/mypy/pull/19515))
+- Optimize generic inference passes (Ivan Levkivskyi, PR [19501](https://github.com/python/mypy/pull/19501))
+- Speed up the default plugin (Jukka Lehtosalo, PRs [19385](https://github.com/python/mypy/pull/19385) and [19462](https://github.com/python/mypy/pull/19462))
+- Remove nested imports from the default plugin (Ivan Levkivskyi, PR [19388](https://github.com/python/mypy/pull/19388))
+- Micro‑optimize type expansion (Jukka Lehtosalo, PR [19461](https://github.com/python/mypy/pull/19461))
+- Micro‑optimize type indirection (Jukka Lehtosalo, PR [19460](https://github.com/python/mypy/pull/19460))
+- Micro‑optimize the plugin framework (Jukka Lehtosalo, PR [19464](https://github.com/python/mypy/pull/19464))
+- Avoid temporary set creation in subtype checking (Jukka Lehtosalo, PR [19463](https://github.com/python/mypy/pull/19463))
+- Subtype checking micro‑optimization (Jukka Lehtosalo, PR [19384](https://github.com/python/mypy/pull/19384))
+- Return early where possible in subtype check (Stanislav Terliakov, PR [19400](https://github.com/python/mypy/pull/19400))
+- Deduplicate some types before joining (Stanislav Terliakov, PR [19409](https://github.com/python/mypy/pull/19409))
+- Speed up type checking by caching argument inference context (Jukka Lehtosalo, PR [19323](https://github.com/python/mypy/pull/19323))
+- Optimize binding method self argument type and deprecation checks (Ivan Levkivskyi, PR [19556](https://github.com/python/mypy/pull/19556))
+- Keep trivial instance types/aliases during expansion (Ivan Levkivskyi, PR [19543](https://github.com/python/mypy/pull/19543))
+
+### Fixed‑Format Cache (Experimental)
+
+Mypy now supports a new cache format used for faster incremental builds. It makes
+incremental builds up to twice as fast. The feature is experimental and
+currently only supported when using a compiled version of mypy. Use `--fixed-format-cache`
+to enable the new format, or `fixed_format_cache = True` in a configuration file.
+
+We plan to enable this by default in a future mypy release, and we'll eventually
+deprecate and remove support for the original JSON-based format.
+
+Unlike the JSON-based cache format, the new binary format is currently
+not easy to parse and inspect by mypy users. We are planning to provide a tool to
+convert fixed-format cache files to JSON, but details of the output JSON may be
+different from the current JSON format. If you rely on being able to inspect
+mypy cache files, we recommend creating a GitHub issue and explaining your use
+case, so that we can more likely provide support for it. (Using
+`MypyFile.read(binary_data)` to inspect cache data may be sufficient to support
+some use cases.)
+
+This feature was contributed by Ivan Levkivskyi (PR [19668](https://github.com/python/mypy/pull/19668), [19735](https://github.com/python/mypy/pull/19735), [19750](https://github.com/python/mypy/pull/19750), [19681](https://github.com/python/mypy/pull/19681), [19752](https://github.com/python/mypy/pull/19752), [19815](https://github.com/python/mypy/pull/19815)).
+
+### Flexible Variable Definitions: Update
+
+Mypy 1.16.0 introduced `--allow-redefinition-new`, which allows redefining variables
+with different types, and inferring union types for variables from multiple assignments.
+The feature is now documented in the `--help` output, but the feature is still experimental.
+
+We are planning to enable this by default in mypy 2.0, and we will also deprecate the
+older `--allow-redefinition` flag. Since the new behavior differs significantly from
+the older flag, we encourage users of `--allow-redefinition` to experiment with
+`--allow-redefinition-new` and create a GitHub issue if the new functionality doesn't
+support some important use cases.
 
 This feature was contributed by Jukka Lehtosalo.
 
-### Fixed‑Format Cache (experimental)
+### Inferred Type for Bare ClassVar
+
+A ClassVar without an explicit type annotation now causes the type of the variable
+to be inferred from the initializer:
+
+
+```python
+from typing import ClassVar
+
+class Item:
+    # Type of 'next_id' is now 'int' (it was 'Any')
+    next_id: ClassVar = 1
 
-TODO by Jukka
+    ...
+```
 
-This feature was contributed by Ivan Levkivskyi (PR [19668](https://github.com/python/mypy/pull/19668), [19735](https://github.com/python/mypy/pull/19735), [19750](https://github.com/python/mypy/pull/19750), [19681](https://github.com/python/mypy/pull/19681), [19752](https://github.com/python/mypy/pull/19752), [19815](https://github.com/python/mypy/pull/19815))
+This feature was contributed by Ivan Levkivskyi (PR [19573](https://github.com/python/mypy/pull/19573)).
 
 ### Disjoint Base Classes (@disjoint_base, PEP 800)
 
-Mypy now implements PEP 800 Disjoint bases: it understands the @disjoint_base marker, rejects class definitions that combine incompatible disjoint bases, and exploits the fact that such classes cannot exist in reachability and narrowing logic
+Mypy now understands disjoint bases (PEP 800): it recognizes the `@disjoint_base`
+decorator, and rejects class definitions that combine mutually incompatible base classes,
+and takes advantage of the fact that such classes cannot exist in reachability and
+narrowing logic.
+
+This class definition will now generate an error:
+
+```python
+# Error: Class "Bad" has incompatible disjoint bases
+class Bad(str, Exception):
+    ...
+```
 
 This feature was contributed by Jelle Zijlstra (PR [19678](https://github.com/python/mypy/pull/19678)).
 
-### Mypy Performance Improvements
+### Miscellaneous New Mypy Features
 
-Mypy 1.18 includes numerous performance improvements, resulting in a 38% overall speedup compared to 1.17
+- Add `--strict-equality-for-none` to flag non-overlapping comparisons involving None (Christoph Tyralla, PR [19718](https://github.com/python/mypy/pull/19718))
+- Don’t show import‑related errors after a module‑level assert such as `assert sys.platform == "linux"` that is always false (Stanislav Terliakov, PR [19347](https://github.com/python/mypy/pull/19347))
 
-- Improve self check performance by 1.8% (Jukka Lehtosalo, PR [19768](https://github.com/python/mypy/pull/19768), [19769](https://github.com/python/mypy/pull/19769), [19770](https://github.com/python/mypy/pull/19770))
-- Use fast Python wrappers in native_internal (Ivan Levkivskyi, PR [19765](https://github.com/python/mypy/pull/19765))
-- Use macros in native_internal hot paths (Ivan Levkivskyi, PR [19757](https://github.com/python/mypy/pull/19757))
-- Special‑case certain Enum method calls for speed (Ivan Levkivskyi, PR [19634](https://github.com/python/mypy/pull/19634))
-- Two additional micro‑optimizations (Ivan Levkivskyi, PR [19627](https://github.com/python/mypy/pull/19627))
-- Another set of micro‑optimizations (Ivan Levkivskyi, PR [19633](https://github.com/python/mypy/pull/19633))
-- Cache common instances (Ivan Levkivskyi, PR [19621](https://github.com/python/mypy/pull/19621))
-- Skip more method bodies in third‑party libraries for speed (Ivan Levkivskyi, PR [19586](https://github.com/python/mypy/pull/19586))
-- Avoid using a dict in CallableType (Ivan Levkivskyi, PR [19580](https://github.com/python/mypy/pull/19580))
-- Use cache for DictExpr (Ivan Levkivskyi, PR [19536](https://github.com/python/mypy/pull/19536))
-- Use cache for OpExpr (Ivan Levkivskyi, PR [19523](https://github.com/python/mypy/pull/19523))
-- Simple call‑expression cache (Ivan Levkivskyi, PR [19505](https://github.com/python/mypy/pull/19505))
-- Cache type_object_type() (Ivan Levkivskyi, PR [19514](https://github.com/python/mypy/pull/19514))
-- Avoid duplicate visits in boolean‑op checking (Ivan Levkivskyi, PR [19515](https://github.com/python/mypy/pull/19515))
-- Optimize generic inference passes (Ivan Levkivskyi, PR [19501](https://github.com/python/mypy/pull/19501))
-- Speed up default plugin (Jukka Lehtosalo, PR [19462](https://github.com/python/mypy/pull/19462))
-- Micro‑optimize ExpandTypeVisitor (Jukka Lehtosalo, PR [19461](https://github.com/python/mypy/pull/19461))
-- Micro‑optimize type indirection visitor (Jukka Lehtosalo, PR [19460](https://github.com/python/mypy/pull/19460))
-- Micro‑optimize chained plugin (Jukka Lehtosalo, PR [19464](https://github.com/python/mypy/pull/19464))
-- Avoid temporary set creation in is_proper_subtype (Jukka Lehtosalo, PR [19463](https://github.com/python/mypy/pull/19463))
-- Subtype checking micro‑optimization (Jukka Lehtosalo, PR [19384](https://github.com/python/mypy/pull/19384))
-- Speed up default plugin (earlier pass) (Jukka Lehtosalo, PR [19385](https://github.com/python/mypy/pull/19385))
-- Remove nested imports from default plugin (Ivan Levkivskyi, PR [19388](https://github.com/python/mypy/pull/19388))
-- is_subtype: return early where possible (Stanislav Terliakov, PR [19400](https://github.com/python/mypy/pull/19400))
-- Deduplicate fast_container_type / fast_dict_type items before join (Stanislav Terliakov, PR [19409](https://github.com/python/mypy/pull/19409))
-- Speed up type checking by caching argument inference context (Jukka Lehtosalo, PR [19323](https://github.com/python/mypy/pull/19323))
-- Optimize bind_self() and deprecation checks (Ivan Levkivskyi, PR [19556](https://github.com/python/mypy/pull/19556))
-- Keep trivial instances/aliases during expansion (Ivan Levkivskyi, PR [19543](https://github.com/python/mypy/pull/19543))
+### Improvements to Match Statements
 
-### Stubtest Improvements
-- Add temporary --ignore-disjoint-bases flag to ease PEP 800 migration (Joren Hammudoglu, PR [19740](https://github.com/python/mypy/pull/19740))
-- Flag redundant uses of @disjoint_base (Jelle Zijlstra, PR [19715](https://github.com/python/mypy/pull/19715))
-- Improve signatures for `__init__` of C classes (Stephen Morton, PR [18259](https://github.com/python/mypy/pull/18259))
-- Handle overloads with mixed pos‑only parameters (Stephen Morton, PR [18287](https://github.com/python/mypy/pull/18287))
-- Use “parameter” (not “argument”) in error messages (PrinceNaroliya, PR [19707](https://github.com/python/mypy/pull/19707))
-- Don’t require @disjoint_base when `__slots__` imply finality (Jelle Zijlstra, PR [19701](https://github.com/python/mypy/pull/19701))
-- Allow runtime‑existing aliases of @type_check_only types (Brian Schubert, PR [19568](https://github.com/python/mypy/pull/19568))
-- More detailed checking of type objects in stubtest (Stephen Morton, PR [18251](https://github.com/python/mypy/pull/18251))
-- Support running stubtest in non-UTF8 terminals (Stanislav Terliakovm, PR [19085](https://github.com/python/mypy/pull/19085))
+- Add temporary named expressions for match subjects (Stanislav Terliakov, PR [18446](https://github.com/python/mypy/pull/18446))
+- Fix unwrapping of assignment expressions in match subject (Marc Mueller, PR [19742](https://github.com/python/mypy/pull/19742))
+- Omit errors for class patterns against object (Marc Mueller, PR [19709](https://github.com/python/mypy/pull/19709))
+- Remove unnecessary error for certain match class patterns (Marc Mueller, PR [19708](https://github.com/python/mypy/pull/19708))
+- Use union type for captured vars in or pattern (Marc Mueller, PR [19710](https://github.com/python/mypy/pull/19710))
+- Prevent final reassignment inside match case (Omer Hadari, PR [19496](https://github.com/python/mypy/pull/19496))
 
-### Mypyc Improvements
+### Fixes to Crashes
 
-- Fix subclass processing in detect_undefined_bitmap (Chainfire, PR [19787](https://github.com/python/mypy/pull/19787))
-- Fix C function signature emission (Jukka Lehtosalo, PR [19773](https://github.com/python/mypy/pull/19773))
-- Use defined `__new__` in tp_new and constructor (Piotr Sawicki, PR [19739](https://github.com/python/mypy/pull/19739))
-- Speed up implicit `__ne__` (Jukka Lehtosalo, PR [19759](https://github.com/python/mypy/pull/19759))
-- Speed up equality with optional str/bytes (Jukka Lehtosalo, PR [19758](https://github.com/python/mypy/pull/19758))
-- Add `__mypyc_empty_tuple__` constant (BobTheBuidler, PR [19654](https://github.com/python/mypy/pull/19654))
-- Add PyObject_CallObject fast‑path op for fn(*args) (BobTheBuidler, PR [19631](https://github.com/python/mypy/pull/19631))
-- Add **kwargs star2 fast‑path (follow‑up to starargs) (BobTheBuidler, PR [19630](https://github.com/python/mypy/pull/19630))
-- Optimize type(x), x.`__class__`, and `.__name__` (Jukka Lehtosalo, PR [19691](https://github.com/python/mypy/pull/19691), [19683](https://github.com/python/mypy/pull/19683))
-- Specialize bytes.decode for common encodings (Jukka Lehtosalo, PR [19688](https://github.com/python/mypy/pull/19688))
-- Speed up in against final fixed‑length tuples (Jukka Lehtosalo, PR [19682](https://github.com/python/mypy/pull/19682))
-- Optimize f‑string building from Final values (BobTheBuidler, PR [19611](https://github.com/python/mypy/pull/19611))
-- Add exact_dict_set_item_op (BobTheBuidler, PR [19657](https://github.com/python/mypy/pull/19657))
-- Cache len() when iterating over immutable types (BobTheBuidler, PR [19656](https://github.com/python/mypy/pull/19656))
-- Add stararg fast‑path for tuple calls fn(*args) (BobTheBuidler, PR [19623](https://github.com/python/mypy/pull/19623))
-- Include more operations in the mypyc trace log (Jukka Lehtosalo, PR [19647](https://github.com/python/mypy/pull/19647))
-- Add prefix to attributes of generator classes (Piotr Sawicki, PR [19535](https://github.com/python/mypy/pull/19535))
-- Fix segfault from heap type objects with static tp_doc (Brian Schubert, PR [19636](https://github.com/python/mypy/pull/19636))
-- Unwrap NewType to its base type for optimized paths (BobTheBuidler, PR [19497](https://github.com/python/mypy/pull/19497))
+- Fix crash with variadic tuple arguments to a generic type (Randolf Scholz, PR [19705](https://github.com/python/mypy/pull/19705))
+- Fix crash when enable_error_code in pyproject.toml has wrong type (wyattscarpenter, PR [19494](https://github.com/python/mypy/pull/19494))
+- Prevent crash for dataclass with PEP 695 TypeVarTuple on Python 3.13+ (Stanislav Terliakov, PR [19565](https://github.com/python/mypy/pull/19565))
+- Fix crash on settable property alias (Ivan Levkivskyi, PR [19615](https://github.com/python/mypy/pull/19615))
+
+### Experimental Free-threading Support for Mypyc
+
+All mypyc tests now pass on free-threading Python 3.14 release candidate builds. The performance
+of various micro-benchmarks scale well across multiple threads.
+
+Free-threading support is still experimental. Note that native attribute access
+(get and set), list item access and certain other operations are still
+unsafe when there are race conditions. This will likely change in the future.
+You can follow the
+[area-free-threading label](https://github.com/mypyc/mypyc/issues?q=is%3Aissue%20state%3Aopen%20label%3Aarea-free-threading)
+in the mypyc issues tracker to follow progress.
+
+Related PRs:
 - Enable free‑threading when compiling multiple modules (Jukka Lehtosalo, PR [19541](https://github.com/python/mypy/pull/19541))
+- Fix `list.pop` on free‑threaded builds (Jukka Lehtosalo, PR [19522](https://github.com/python/mypy/pull/19522))
 - Make type objects immortal under free‑threading (Jukka Lehtosalo, PR [19538](https://github.com/python/mypy/pull/19538))
-- Fix list.pop primitive on free‑threaded builds (Jukka Lehtosalo, PR [19522](https://github.com/python/mypy/pull/19522))
+
+### Mypyc: Support `__new__`
+
+Mypyc now has rudimentary support for user-defined `__new__` methods.
+
+This feature was contributed by Piotr Sawicki (PR [19739](https://github.com/python/mypy/pull/19739)).
+
+### Mypyc: Faster Generators and Async Functions
+
+Generators and calls of async functions are now faster, sometimes by 2x or more.
+
+Related PRs:
+- Speed up for loops over native generators (Jukka Lehtosalo, PR [19415](https://github.com/python/mypy/pull/19415))
+- Speed up native‑to‑native calls using await (Jukka Lehtosalo, PR [19398](https://github.com/python/mypy/pull/19398))
+- Call generator helper directly in await expressions (Jukka Lehtosalo, PR [19376](https://github.com/python/mypy/pull/19376))
+- Speed up generator allocation with per‑type freelists (Jukka Lehtosalo, PR [19316](https://github.com/python/mypy/pull/19316))
+
+### Miscellaneous Mypyc Improvements
+
+- Special‑case certain Enum method calls for speed (Ivan Levkivskyi, PR [19634](https://github.com/python/mypy/pull/19634))
+- Fix issues related to subclassing and undefined attribute tracking (Chainfire, PR [19787](https://github.com/python/mypy/pull/19787))
+- Fix invalid C function signature (Jukka Lehtosalo, PR [19773](https://github.com/python/mypy/pull/19773))
+- Speed up implicit `__ne__` (Jukka Lehtosalo, PR [19759](https://github.com/python/mypy/pull/19759))
+- Speed up equality with optional str/bytes types (Jukka Lehtosalo, PR [19758](https://github.com/python/mypy/pull/19758))
+- Speed up access to empty tuples (BobTheBuidler, PR [19654](https://github.com/python/mypy/pull/19654))
+- Speed up calls with `*args` (BobTheBuidler, PRs [19623](https://github.com/python/mypy/pull/19623) and [19631](https://github.com/python/mypy/pull/19631))
+- Speed up calls with `**kwargs` (BobTheBuidler, PR [19630](https://github.com/python/mypy/pull/19630))
+- Optimize `type(x)`, `x.__class__`, and `.__name__` (Jukka Lehtosalo, PR [19691](https://github.com/python/mypy/pull/19691), [19683](https://github.com/python/mypy/pull/19683))
+- Specialize `bytes.decode` for common encodings (Jukka Lehtosalo, PR [19688](https://github.com/python/mypy/pull/19688))
+- Speed up `in` operations using final fixed‑length tuples (Jukka Lehtosalo, PR [19682](https://github.com/python/mypy/pull/19682))
+- Optimize f‑string building from final values (BobTheBuidler, PR [19611](https://github.com/python/mypy/pull/19611))
+- Add dictionary set item for exact dict instances (BobTheBuidler, PR [19657](https://github.com/python/mypy/pull/19657))
+- Cache length when iterating over immutable types (BobTheBuidler, PR [19656](https://github.com/python/mypy/pull/19656))
+- Fix name conflict related to attributes of generator classes (Piotr Sawicki, PR [19535](https://github.com/python/mypy/pull/19535))
+- Fix segfault from heap type objects with a static docstring (Brian Schubert, PR [19636](https://github.com/python/mypy/pull/19636))
+- Unwrap NewType to its base type for additional optimizations (BobTheBuidler, PR [19497](https://github.com/python/mypy/pull/19497))
 - Generate an export table only for separate compilation (Jukka Lehtosalo, PR [19521](https://github.com/python/mypy/pull/19521))
-- Add primitives for isinstance of built‑in types (Piotr Sawicki, PR [19435](https://github.com/python/mypy/pull/19435))
-- Add SetElement op to initialize struct values (Jukka Lehtosalo, PR [19437](https://github.com/python/mypy/pull/19437))
-- Simplify IR for for loops over strings (Jukka Lehtosalo, PR [19434](https://github.com/python/mypy/pull/19434))
+- Speed up `isinstance` with built‑in types (Piotr Sawicki, PR [19435](https://github.com/python/mypy/pull/19435))
 - Use native integers for some sequence indexing (Jukka Lehtosalo, PR [19426](https://github.com/python/mypy/pull/19426))
-- Remove unused CPyList_GetItemUnsafe primitive (Jukka Lehtosalo, PR [19424](https://github.com/python/mypy/pull/19424))
-- Add native‑int helper methods in IR builder (Jukka Lehtosalo, PR [19423](https://github.com/python/mypy/pull/19423))
-- Use PyList_Check for isinstance(obj, list) (Piotr Sawicki, PR [19416](https://github.com/python/mypy/pull/19416))
-- Speed up for loops over native generators (Jukka Lehtosalo, PR [19415](https://github.com/python/mypy/pull/19415))
+- Speed up `isinstance(obj, list)` (Piotr Sawicki, PR [19416](https://github.com/python/mypy/pull/19416))
 - Report error on reserved method names (Piotr Sawicki, PR [19407](https://github.com/python/mypy/pull/19407))
-- Add is_bool_or_bit_rprimitive (Piotr Sawicki, PR [19406](https://github.com/python/mypy/pull/19406))
-- Faster string equality primitive (Jukka Lehtosalo, PR [19402](https://github.com/python/mypy/pull/19402))
-- Speed up native‑to‑native calls using await (Jukka Lehtosalo, PR [19398](https://github.com/python/mypy/pull/19398))
-- Raise NameError on undefined names (Piotr Sawicki, PR [19395](https://github.com/python/mypy/pull/19395))
-- Simplify comparison of tuple elements (Piotr Sawicki, PR [19396](https://github.com/python/mypy/pull/19396))
+- Speed up string equality (Jukka Lehtosalo, PR [19402](https://github.com/python/mypy/pull/19402))
+- Raise `NameError` on undefined names (Piotr Sawicki, PR [19395](https://github.com/python/mypy/pull/19395))
 - Use per‑type freelists for nested functions (Jukka Lehtosalo, PR [19390](https://github.com/python/mypy/pull/19390))
-- Call generator helper directly in await expressions (Jukka Lehtosalo, PR [19376](https://github.com/python/mypy/pull/19376))
+- Simplify comparison of tuple elements (Piotr Sawicki, PR [19396](https://github.com/python/mypy/pull/19396))
 - Generate introspection signatures for compiled functions (Brian Schubert, PR [19307](https://github.com/python/mypy/pull/19307))
-- Support C string literals in IR (Jukka Lehtosalo, PR [19383](https://github.com/python/mypy/pull/19383))
-- Fix error‑value check for GetAttr that allows nullable values (Jukka Lehtosalo, PR [19378](https://github.com/python/mypy/pull/19378))
+- Fix undefined attribute checking special case (Jukka Lehtosalo, PR [19378](https://github.com/python/mypy/pull/19378))
 - Fix comparison of tuples with different lengths (Piotr Sawicki, PR [19372](https://github.com/python/mypy/pull/19372))
-- Speed up generator allocation with per‑type freelists (Jukka Lehtosalo, PR [19316](https://github.com/python/mypy/pull/19316))
-- Implement list.clear() primitive (Jahongir Qurbonov, PR [19344](https://github.com/python/mypy/pull/19344))
-- New primitives for weakref.proxy (BobTheBuidler, PR [19217](https://github.com/python/mypy/pull/19217))
-- New primitive for weakref.ref (BobTheBuidler, PR [19099](https://github.com/python/mypy/pull/19099))
-- New primitive for str.count (BobTheBuidler, PR [19264](https://github.com/python/mypy/pull/19264))
-- Tracing/tooling: optionally log sampled operation traces (Jukka Lehtosalo, PR [19457](https://github.com/python/mypy/pull/19457))
-- Tracing/tooling: script to compile with trace logging and run mypy (Jukka Lehtosalo, PR [19475](https://github.com/python/mypy/pull/19475))
+- Speed up `list.clear` (Jahongir Qurbonov, PR [19344](https://github.com/python/mypy/pull/19344))
+- Speed up `weakref.proxy` (BobTheBuidler, PR [19217](https://github.com/python/mypy/pull/19217))
+- Speed up `weakref.ref` (BobTheBuidler, PR [19099](https://github.com/python/mypy/pull/19099))
+- Speed up `str.count` (BobTheBuidler, PR [19264](https://github.com/python/mypy/pull/19264))
 
+### Stubtest Improvements
+- Add temporary `--ignore-disjoint-bases` flag to ease PEP 800 migration (Joren Hammudoglu, PR [19740](https://github.com/python/mypy/pull/19740))
+- Flag redundant uses of `@disjoint_base` (Jelle Zijlstra, PR [19715](https://github.com/python/mypy/pull/19715))
+- Improve signatures for `__init__` of C extension classes (Stephen Morton, PR [18259](https://github.com/python/mypy/pull/18259))
+- Handle overloads with mixed positional‑only parameters (Stephen Morton, PR [18287](https://github.com/python/mypy/pull/18287))
+- Use “parameter” (not “argument”) in error messages (PrinceNaroliya, PR [19707](https://github.com/python/mypy/pull/19707))
+- Don’t require `@disjoint_base` when `__slots__` imply finality (Jelle Zijlstra, PR [19701](https://github.com/python/mypy/pull/19701))
+- Allow runtime‑existing aliases of `@type_check_only` types (Brian Schubert, PR [19568](https://github.com/python/mypy/pull/19568))
+- More detailed checking of type objects in stubtest (Stephen Morton, PR [18251](https://github.com/python/mypy/pull/18251))
+- Support running stubtest in non-UTF8 terminals (Stanislav Terliakov, PR [19085](https://github.com/python/mypy/pull/19085))
 
 ### Documentation Updates
 
 - Add idlemypyextension to IDE integrations (CoolCat467, PR [18615](https://github.com/python/mypy/pull/18615))
-- Document that object is often preferable to Any in APIs (wyattscarpenter, PR [19103](https://github.com/python/mypy/pull/19103))
-- Include a detailed listing of flags enabled by --strict (wyattscarpenter, PR [19062](https://github.com/python/mypy/pull/19062))
+- Document that `object` is often preferable to `Any` in APIs (wyattscarpenter, PR [19103](https://github.com/python/mypy/pull/19103))
+- Include a detailed listing of flags enabled by `--strict` (wyattscarpenter, PR [19062](https://github.com/python/mypy/pull/19062))
 - Update “common issues” (reveal_type/reveal_locals; note on orjson) (wyattscarpenter, PR [19059](https://github.com/python/mypy/pull/19059), [19058](https://github.com/python/mypy/pull/19058))
 
-### Other Notable Improvements
+### Other Notable Fixes and Improvements
 
-- Remove deprecated --new-type-inference flag (the new algorithm has long been default) (Ivan Levkivskyi, PR [19570](https://github.com/python/mypy/pull/19570))
+- Remove deprecated `--new-type-inference` flag (the new algorithm has long been default) (Ivan Levkivskyi, PR [19570](https://github.com/python/mypy/pull/19570))
 - Use empty context as a fallback for return expressions when outer context misleads inference (Ivan Levkivskyi, PR [19767](https://github.com/python/mypy/pull/19767))
-- Support --strict-equality checks involving None (Christoph Tyralla, PR [19718](https://github.com/python/mypy/pull/19718))
-- Don’t show import‑related errors after a module‑level assert False (Stanislav Terliakov, PR [19347](https://github.com/python/mypy/pull/19347))
-- Fix forward refs in type parameters of over‑parameterized PEP 695 aliases (Brian Schubert, PR [19725](https://github.com/python/mypy/pull/19725))
+- Fix forward references in type parameters of over‑parameterized PEP 695 aliases (Brian Schubert, PR [19725](https://github.com/python/mypy/pull/19725))
 - Don’t expand PEP 695 aliases when checking node fullnames (Brian Schubert, PR [19699](https://github.com/python/mypy/pull/19699))
-- Don’t use outer context for or expression inference when LHS is Any (Stanislav Terliakov, PR [19748](https://github.com/python/mypy/pull/19748))
-- Interpret bare ClassVar as inferred (not Any) (Ivan Levkivskyi, PR [19573](https://github.com/python/mypy/pull/19573))
+- Don’t use outer context for 'or' expression inference when LHS is Any (Stanislav Terliakov, PR [19748](https://github.com/python/mypy/pull/19748))
 - Recognize buffer protocol special methods (Brian Schubert, PR [19581](https://github.com/python/mypy/pull/19581))
-- Add temporary named expressions for match subjects (Stanislav Terliakov, PR [18446](https://github.com/python/mypy/pull/18446))
 - Support attribute access on enum members correctly (Stanislav Terliakov, PR [19422](https://github.com/python/mypy/pull/19422))
 - Check `__slots__` assignments on self types (Stanislav Terliakov, PR [19332](https://github.com/python/mypy/pull/19332))
 - Move self‑argument checks after decorator application (Stanislav Terliakov, PR [19490](https://github.com/python/mypy/pull/19490))
 - Infer empty list for `__slots__` and module `__all__` (Stanislav Terliakov, PR [19348](https://github.com/python/mypy/pull/19348))
 - Use normalized tuples for fallback calculation (Stanislav Terliakov, PR [19111](https://github.com/python/mypy/pull/19111))
-- Preserve literals when joining Literal with Instance that has matching last_known_value (Stanislav Terliakov, PR [19279](https://github.com/python/mypy/pull/19279))
+- Preserve literals when joining similar types (Stanislav Terliakov, PR [19279](https://github.com/python/mypy/pull/19279))
 - Allow adjacent conditionally‑defined overloads (Stanislav Terliakov, PR [19042](https://github.com/python/mypy/pull/19042))
 - Check property decorators more strictly (Stanislav Terliakov, PR [19313](https://github.com/python/mypy/pull/19313))
 - Support properties with generic setters (Ivan Levkivskyi, PR [19298](https://github.com/python/mypy/pull/19298))
@@ -162,45 +252,32 @@ Mypy 1.18 includes numerous performance improvements, resulting in a 38% overall
 - Include tuple fallback in constraints built from tuple types (Stanislav Terliakov, PR [19100](https://github.com/python/mypy/pull/19100))
 - Somewhat better isinstance support on old‑style unions (Shantanu, PR [19714](https://github.com/python/mypy/pull/19714))
 - Improve promotions inside unions (Christoph Tyralla, PR [19245](https://github.com/python/mypy/pull/19245))
-- Uninhabited types should have all attributes (Ivan Levkivskyi, PR [19300](https://github.com/python/mypy/pull/19300))
-- Metaclass conflict checks improved (Robsdedude, PR [17682](https://github.com/python/mypy/pull/17682))
-- Metaclass resolution algorithm fixes (Robsdedude, PR [17713](https://github.com/python/mypy/pull/17713))
+- Treat uninhabited types as having all attributes (Ivan Levkivskyi, PR [19300](https://github.com/python/mypy/pull/19300))
+- Improve metaclass conflict checks (Robsdedude, PR [17682](https://github.com/python/mypy/pull/17682))
+- Fixes to metaclass resolution algorithm (Robsdedude, PR [17713](https://github.com/python/mypy/pull/17713))
 - PEP 702 @deprecated: handle “combined” overloads (Christoph Tyralla, PR [19626](https://github.com/python/mypy/pull/19626))
 - PEP 702 @deprecated: include overloads in snapshot descriptions (Christoph Tyralla, PR [19613](https://github.com/python/mypy/pull/19613))
 - Ignore overload implementation when checking `__OP__` / `__rOP__` compatibility (Stanislav Terliakov, PR [18502](https://github.com/python/mypy/pull/18502))
-- Fix unwrapping of assignment expressions in match subject (Marc Mueller, PR [19742](https://github.com/python/mypy/pull/19742))
-- Omit errors for class patterns against object (Marc Mueller, PR [19709](https://github.com/python/mypy/pull/19709))
-- Remove unnecessary error for certain match class patterns (Marc Mueller, PR [19708](https://github.com/python/mypy/pull/19708))
-- Use union type for captured vars in or pattern (Marc Mueller, PR [19710](https://github.com/python/mypy/pull/19710))
-- Prevent final reassignment inside match case (Omer Hadari, PR [19496](https://github.com/python/mypy/pull/19496))
-- Support _value_ as a fallback for ellipsis Enum members (Stanislav Terliakov, PR [19352](https://github.com/python/mypy/pull/19352))
+- Support `_value_` as a fallback for ellipsis Enum members (Stanislav Terliakov, PR [19352](https://github.com/python/mypy/pull/19352))
 - Sort arguments in TypedDict overlap messages (Marc Mueller, PR [19666](https://github.com/python/mypy/pull/19666))
-- Reset to previous statement on leaving return in semanal (Stanislav Terliakov, PR [19642](https://github.com/python/mypy/pull/19642))
-- Add ambiguous to UninhabitedType identity for better messaging (Stanislav Terliakov, PR [19648](https://github.com/python/mypy/pull/19648))
-- Further fix overload diagnostics for varargs/kwargs (Shantanu, PR [19619](https://github.com/python/mypy/pull/19619))
-- Fix overload diagnostics when vararg and varkwarg both match (Shantanu, PR [19614](https://github.com/python/mypy/pull/19614))
-- Show type variable name in “Cannot infer type argument” (Brian Schubert, PR [19290](https://github.com/python/mypy/pull/19290))
+- Fix handling of implicit return in lambda (Stanislav Terliakov, PR [19642](https://github.com/python/mypy/pull/19642))
+- Improve behavior of uninhabited types (Stanislav Terliakov, PR [19648](https://github.com/python/mypy/pull/19648))
+- Fix overload diagnostics when `*args` and `**kwargs` both match (Shantanu, PR [19614](https://github.com/python/mypy/pull/19614))
+- Further fix overload diagnostics for `*args`/`**kwargs` (Shantanu, PR [19619](https://github.com/python/mypy/pull/19619))
+- Show type variable name in "Cannot infer type argument" (Brian Schubert, PR [19290](https://github.com/python/mypy/pull/19290))
 - Fail gracefully on unsupported template strings (PEP 750) (Brian Schubert, PR [19700](https://github.com/python/mypy/pull/19700))
 - Revert colored argparse help for Python 3.14 (Marc Mueller, PR [19721](https://github.com/python/mypy/pull/19721))
-- Support type‑checking a code fragment in the profile script (Jukka Lehtosalo, PR [19379](https://github.com/python/mypy/pull/19379))
-- Fix C compiler flags in the profile self‑check script (Jukka Lehtosalo, PR [19326](https://github.com/python/mypy/pull/19326))
-- Add a script for profiling self‑check (Linux only) (Jukka Lehtosalo, PR [19322](https://github.com/python/mypy/pull/19322))
-- Retry PyPI upload script: skip existing files on retry (Jukka Lehtosalo, PR [19305](https://github.com/python/mypy/pull/19305))
 - Update stubinfo for latest typeshed (Shantanu, PR [19771](https://github.com/python/mypy/pull/19771))
-- Fix crash with variadic tuple arguments to a generic type (Randolf Scholz, PR [19705](https://github.com/python/mypy/pull/19705))
-- Fix crash when enable_error_code in pyproject.toml has wrong type (wyattscarpenter, PR [19494](https://github.com/python/mypy/pull/19494))
-- Fix dict assignment to a wider context when an incompatible same‑shape TypedDict exists (Stanislav Terliakov, PR [19592](https://github.com/python/mypy/pull/19592))
-- Prevent crash for dataclass with PEP 695 TypeVarTuple on Python 3.13+ (Stanislav Terliakov, PR [19565](https://github.com/python/mypy/pull/19565))
+- Fix dict assignment when an incompatible same‑shape TypedDict exists (Stanislav Terliakov, PR [19592](https://github.com/python/mypy/pull/19592))
 - Fix constructor type for subclasses of Any (Ivan Levkivskyi, PR [19295](https://github.com/python/mypy/pull/19295))
-- Fix TypeGuard/TypeIs being forgotten when semanal defers (Brian Schubert, PR [19325](https://github.com/python/mypy/pull/19325))
+- Fix TypeGuard/TypeIs being forgotten in some cases (Brian Schubert, PR [19325](https://github.com/python/mypy/pull/19325))
 - Fix TypeIs negative narrowing for unions of generics (Brian Schubert, PR [18193](https://github.com/python/mypy/pull/18193))
-- dmypy suggest: fix incorrect signature suggestion when a type matches a module name (Brian Schubert, PR [18937](https://github.com/python/mypy/pull/18937))
-- dmypy suggest: fix interaction with `__new__` (Stanislav Terliakov, PR [18966](https://github.com/python/mypy/pull/18966))
-- dmypy suggest: support Callable / callable Protocols in decorator unwrapping (Anthony Sottile, PR [19072](https://github.com/python/mypy/pull/19072))
+- dmypy suggest: Fix incorrect signature suggestion when a type matches a module name (Brian Schubert, PR [18937](https://github.com/python/mypy/pull/18937))
+- dmypy suggest: Fix interaction with `__new__` (Stanislav Terliakov, PR [18966](https://github.com/python/mypy/pull/18966))
+- dmypy suggest: Support Callable / callable Protocols in decorator unwrapping (Anthony Sottile, PR [19072](https://github.com/python/mypy/pull/19072))
 - Fix missing error when redeclaring a type variable in a nested generic class (Brian Schubert, PR [18883](https://github.com/python/mypy/pull/18883))
 - Fix for overloaded type object erasure (Shantanu, PR [19338](https://github.com/python/mypy/pull/19338))
 - Fix TypeGuard with call on temporary object (Saul Shanabrook, PR [19577](https://github.com/python/mypy/pull/19577))
-- Fix crash on settable property alias (Ivan Levkivskyi, PR [19615](https://github.com/python/mypy/pull/19615))
 
 ### Typeshed Updates
 

From 7197a99d1aebb1b7a584f82a53c44efb7dddf136 Mon Sep 17 00:00:00 2001
From: Kevin Kannammalil 
Date: Thu, 11 Sep 2025 11:19:13 -0400
Subject: [PATCH 1615/1617] Removed Unreleased in the Changelog for Release
 1.18 (#19827)

Remove Unreleased from section title

(cherry picked from commit f0863a551ad1ee7f0116cf2580cdb19ffbbbf9c3)
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5266a86c725e..3e6f8c2cac38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ## Next Release
 
-## Mypy 1.18 (Unreleased)
+## Mypy 1.18
 
 We’ve just uploaded mypy 1.18 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
 Mypy is a static type checker for Python. This release includes new features, performance

From b44a1fbf0cf9fd90fd29d6bcd9f64c55dd2fd4c8 Mon Sep 17 00:00:00 2001
From: KevinRK29 
Date: Thu, 11 Sep 2025 11:33:46 -0400
Subject: [PATCH 1616/1617] removed +dev from version

---
 mypy/version.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/version.py b/mypy/version.py
index bb6a9582e74e..8d4e14c9172d 100644
--- a/mypy/version.py
+++ b/mypy/version.py
@@ -8,7 +8,7 @@
 # - Release versions have the form "1.2.3".
 # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440).
 # - Before 1.0 we had the form "0.NNN".
-__version__ = "1.18.0+dev"
+__version__ = "1.18.0"
 base_version = __version__
 
 mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

From 03fbaa941bccc3a9f8aea796d586603b67119bf2 Mon Sep 17 00:00:00 2001
From: KevinRK29 
Date: Thu, 11 Sep 2025 16:00:12 -0400
Subject: [PATCH 1617/1617] bump version to 1.18.1 due to wheels failure

---
 mypy/version.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mypy/version.py b/mypy/version.py
index 8d4e14c9172d..be15f5395fbe 100644
--- a/mypy/version.py
+++ b/mypy/version.py
@@ -8,7 +8,7 @@
 # - Release versions have the form "1.2.3".
 # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440).
 # - Before 1.0 we had the form "0.NNN".
-__version__ = "1.18.0"
+__version__ = "1.18.1"
 base_version = __version__
 
 mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))